summaryrefslogtreecommitdiff
path: root/linux
diff options
context:
space:
mode:
Diffstat (limited to 'linux')
-rw-r--r--linux/drivers/media/radio/Kconfig354
-rw-r--r--linux/drivers/media/radio/Makefile22
-rw-r--r--linux/drivers/media/radio/miropcm20-radio.c266
-rw-r--r--linux/drivers/media/radio/miropcm20-rds-core.c211
-rw-r--r--linux/drivers/media/radio/miropcm20-rds-core.h19
-rw-r--r--linux/drivers/media/radio/miropcm20-rds.c134
-rw-r--r--linux/drivers/media/radio/radio-aimslab.c370
-rw-r--r--linux/drivers/media/radio/radio-aztech.c317
-rw-r--r--linux/drivers/media/radio/radio-cadet.c622
-rw-r--r--linux/drivers/media/radio/radio-gemtek-pci.c417
-rw-r--r--linux/drivers/media/radio/radio-gemtek.c306
-rw-r--r--linux/drivers/media/radio/radio-maestro.c383
-rw-r--r--linux/drivers/media/radio/radio-maxiradio.c352
-rw-r--r--linux/drivers/media/radio/radio-rtrack2.c268
-rw-r--r--linux/drivers/media/radio/radio-sf16fmi.c330
-rw-r--r--linux/drivers/media/radio/radio-sf16fmr2.c436
-rw-r--r--linux/drivers/media/radio/radio-terratec.c343
-rw-r--r--linux/drivers/media/radio/radio-trust.c322
-rw-r--r--linux/drivers/media/radio/radio-typhoon.c385
-rw-r--r--linux/drivers/media/radio/radio-zoltrix.c387
-rw-r--r--linux/drivers/media/video/adv7170.c519
-rw-r--r--linux/drivers/media/video/adv7175.c537
-rw-r--r--linux/drivers/media/video/arv.c913
-rw-r--r--linux/drivers/media/video/bt819.c643
-rw-r--r--linux/drivers/media/video/bt856.c426
-rw-r--r--linux/drivers/media/video/bw-qcam.c1029
-rw-r--r--linux/drivers/media/video/bw-qcam.h68
-rw-r--r--linux/drivers/media/video/c-qcam.c858
-rw-r--r--linux/drivers/media/video/cpia.c4073
-rw-r--r--linux/drivers/media/video/cpia.h432
-rw-r--r--linux/drivers/media/video/cpia_pp.c881
-rw-r--r--linux/drivers/media/video/cpia_usb.c649
-rw-r--r--linux/drivers/media/video/cs8420.h50
-rw-r--r--linux/drivers/media/video/dabfirmware.h1408
-rw-r--r--linux/drivers/media/video/dabusb.c874
-rw-r--r--linux/drivers/media/video/dabusb.h85
-rw-r--r--linux/drivers/media/video/dsbr100.c430
-rw-r--r--linux/drivers/media/video/ibmmpeg2.h94
-rw-r--r--linux/drivers/media/video/indycam.c471
-rw-r--r--linux/drivers/media/video/indycam.h108
-rw-r--r--linux/drivers/media/video/meye.c2044
-rw-r--r--linux/drivers/media/video/meye.h320
-rw-r--r--linux/drivers/media/video/ov511.c5932
-rw-r--r--linux/drivers/media/video/ov511.h569
-rw-r--r--linux/drivers/media/video/planb.c2304
-rw-r--r--linux/drivers/media/video/planb.h231
-rw-r--r--linux/drivers/media/video/pms.c1064
-rw-r--r--linux/drivers/media/video/saa5246a.c837
-rw-r--r--linux/drivers/media/video/saa5246a.h364
-rw-r--r--linux/drivers/media/video/saa5249.c720
-rw-r--r--linux/drivers/media/video/saa7110.c604
-rw-r--r--linux/drivers/media/video/saa7111.c614
-rw-r--r--linux/drivers/media/video/saa7114.c1221
-rw-r--r--linux/drivers/media/video/saa7121.h132
-rw-r--r--linux/drivers/media/video/saa7146.h114
-rw-r--r--linux/drivers/media/video/saa7146reg.h283
-rw-r--r--linux/drivers/media/video/saa7185.c508
-rw-r--r--linux/drivers/media/video/saa7191.c808
-rw-r--r--linux/drivers/media/video/saa7191.h255
-rw-r--r--linux/drivers/media/video/saa7196.h117
-rw-r--r--linux/drivers/media/video/se401.c1435
-rw-r--r--linux/drivers/media/video/se401.h235
-rw-r--r--linux/drivers/media/video/stradis.c2213
-rw-r--r--linux/drivers/media/video/stv680.c1509
-rw-r--r--linux/drivers/media/video/stv680.h227
-rw-r--r--linux/drivers/media/video/tuner-3036.c214
-rw-r--r--linux/drivers/media/video/videocodec.c481
-rw-r--r--linux/drivers/media/video/videocodec.h359
-rw-r--r--linux/drivers/media/video/vino.c4680
-rw-r--r--linux/drivers/media/video/vino.h138
-rw-r--r--linux/drivers/media/video/vpx3220.c749
-rw-r--r--linux/drivers/media/video/w9966.c986
-rw-r--r--linux/drivers/media/video/w9968cf.c3691
-rw-r--r--linux/drivers/media/video/w9968cf.h331
-rw-r--r--linux/drivers/media/video/w9968cf_decoder.h86
-rw-r--r--linux/drivers/media/video/w9968cf_vpp.h40
-rw-r--r--linux/drivers/media/video/zoran.h515
-rw-r--r--linux/drivers/media/video/zoran_card.c1578
-rw-r--r--linux/drivers/media/video/zoran_card.h45
-rw-r--r--linux/drivers/media/video/zoran_device.c1786
-rw-r--r--linux/drivers/media/video/zoran_device.h91
-rw-r--r--linux/drivers/media/video/zoran_driver.c4700
-rw-r--r--linux/drivers/media/video/zoran_procfs.c234
-rw-r--r--linux/drivers/media/video/zoran_procfs.h36
-rw-r--r--linux/drivers/media/video/zr36016.c531
-rw-r--r--linux/drivers/media/video/zr36016.h111
-rw-r--r--linux/drivers/media/video/zr36050.c905
-rw-r--r--linux/drivers/media/video/zr36050.h184
-rw-r--r--linux/drivers/media/video/zr36057.h168
-rw-r--r--linux/drivers/media/video/zr36060.c1014
-rw-r--r--linux/drivers/media/video/zr36060.h220
91 files changed, 69325 insertions, 0 deletions
diff --git a/linux/drivers/media/radio/Kconfig b/linux/drivers/media/radio/Kconfig
new file mode 100644
index 000000000..d318be383
--- /dev/null
+++ b/linux/drivers/media/radio/Kconfig
@@ -0,0 +1,354 @@
+#
+# Multimedia Video device configuration
+#
+
+menu "Radio Adapters"
+ depends on VIDEO_DEV!=n
+
+config RADIO_CADET
+ tristate "ADS Cadet AM/FM Tuner"
+ depends on ISA && VIDEO_DEV
+ ---help---
+ Choose Y here if you have one of these AM/FM radio cards, and then
+ fill in the port address below.
+
+ In order to control your radio card, you will need to use programs
+ that are compatible with the Video For Linux API. Information on
+ this API and pointers to "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+ Further documentation on this driver can be found on the WWW at
+ <http://linux.blackhawke.net/cadet/>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-cadet.
+
+config RADIO_RTRACK
+ tristate "AIMSlab RadioTrack (aka RadioReveal) support"
+ depends on ISA && VIDEO_DEV
+ ---help---
+ Choose Y here if you have one of these FM radio cards, and then fill
+ in the port address below.
+
+ Note that newer AIMSlab RadioTrack cards have a different chipset
+ and are not supported by this driver. For these cards, use the
+ RadioTrack II driver below.
+
+ If you have a GemTeks combined (PnP) sound- and radio card you must
+ use this driver as a module and setup the card with isapnptools.
+ You must also pass the module a suitable io parameter, 0x248 has
+ been reported to be used by these cards.
+
+ In order to control your radio card, you will need to use programs
+ that are compatible with the Video For Linux API. Information on
+ this API and pointers to "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>. More information is
+ contained in the file
+ <file:Documentation/video4linux/radiotrack.txt>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-aimslab.
+
+config RADIO_RTRACK_PORT
+ hex "RadioTrack i/o port (0x20f or 0x30f)"
+ depends on RADIO_RTRACK=y
+ default "20f"
+ help
+ Enter either 0x30f or 0x20f here. The card default is 0x30f, if you
+ haven't changed the jumper setting on the card.
+
+config RADIO_RTRACK2
+ tristate "AIMSlab RadioTrack II support"
+ depends on ISA && VIDEO_DEV
+ ---help---
+ Choose Y here if you have this FM radio card, and then fill in the
+ port address below.
+
+ In order to control your radio card, you will need to use programs
+ that are compatible with the Video For Linux API. Information on
+ this API and pointers to "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-rtrack2.
+
+config RADIO_RTRACK2_PORT
+ hex "RadioTrack II i/o port (0x20c or 0x30c)"
+ depends on RADIO_RTRACK2=y
+ default "30c"
+ help
+ Enter either 0x30c or 0x20c here. The card default is 0x30c, if you
+ haven't changed the jumper setting on the card.
+
+config RADIO_AZTECH
+ tristate "Aztech/Packard Bell Radio"
+ depends on ISA && VIDEO_DEV
+ ---help---
+ Choose Y here if you have one of these FM radio cards, and then fill
+ in the port address below.
+
+ In order to control your radio card, you will need to use programs
+ that are compatible with the Video For Linux API. Information on
+ this API and pointers to "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-aztech.
+
+config RADIO_AZTECH_PORT
+ hex "Aztech/Packard Bell I/O port (0x350 or 0x358)"
+ depends on RADIO_AZTECH=y
+ default "350"
+ help
+ Enter either 0x350 or 0x358 here. The card default is 0x350, if you
+ haven't changed the setting of jumper JP3 on the card. Removing the
+ jumper sets the card to 0x358.
+
+config RADIO_GEMTEK
+ tristate "GemTek Radio Card support"
+ depends on ISA && VIDEO_DEV
+ ---help---
+ Choose Y here if you have this FM radio card, and then fill in the
+ port address below.
+
+ In order to control your radio card, you will need to use programs
+ that are compatible with the Video For Linux API. Information on
+ this API and pointers to "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-gemtek.
+
+config RADIO_GEMTEK_PORT
+ hex "GemTek i/o port (0x20c, 0x30c, 0x24c or 0x34c)"
+ depends on RADIO_GEMTEK=y
+ default "34c"
+ help
+ Enter either 0x20c, 0x30c, 0x24c or 0x34c here. The card default is
+ 0x34c, if you haven't changed the jumper setting on the card. On
+ Sound Vision 16 Gold PnP with FM Radio (ESS1869+FM Gemtek), the I/O
+ port is 0x28c.
+
+config RADIO_GEMTEK_PCI
+ tristate "GemTek PCI Radio Card support"
+ depends on VIDEO_DEV && PCI
+ ---help---
+ Choose Y here if you have this PCI FM radio card.
+
+ In order to control your radio card, you will need to use programs
+ that are compatible with the Video for Linux API. Information on
+ this API and pointers to "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-gemtek-pci.
+
+config RADIO_MAXIRADIO
+ tristate "Guillemot MAXI Radio FM 2000 radio"
+ depends on VIDEO_DEV && PCI
+ ---help---
+ Choose Y here if you have this radio card. This card may also be
+ found as Gemtek PCI FM.
+
+ In order to control your radio card, you will need to use programs
+ that are compatible with the Video For Linux API. Information on
+ this API and pointers to "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-maxiradio.
+
+config RADIO_MAESTRO
+ tristate "Maestro on board radio"
+ depends on VIDEO_DEV
+ ---help---
+ Say Y here to directly support the on-board radio tuner on the
+ Maestro 2 or 2E sound card.
+
+ In order to control your radio card, you will need to use programs
+ that are compatible with the Video For Linux API. Information on
+ this API and pointers to "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-maestro.
+
+config RADIO_MIROPCM20
+ tristate "miroSOUND PCM20 radio"
+ depends on ISA && VIDEO_DEV && SOUND_ACI_MIXER
+ ---help---
+ Choose Y here if you have this FM radio card. You also need to say Y
+ to "ACI mixer (miroSOUND PCM1-pro/PCM12/PCM20 radio)" (in "Sound")
+ for this to work.
+
+ In order to control your radio card, you will need to use programs
+ that are compatible with the Video For Linux API. Information on
+ this API and pointers to "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called miropcm20.
+
+config RADIO_MIROPCM20_RDS
+ tristate "miroSOUND PCM20 radio RDS user interface (EXPERIMENTAL)"
+ depends on RADIO_MIROPCM20 && EXPERIMENTAL
+ ---help---
+ Choose Y here if you want to see RDS/RBDS information like
+ RadioText, Programme Service name, Clock Time and date, Programme
+ TYpe and Traffic Announcement/Programme identification. You also
+ need to say Y to "miroSOUND PCM20 radio" and devfs!
+
+ It's not possible to read the raw RDS packets from the device, so
+ the driver cant provide an V4L interface for this. But the
+ availability of RDS is reported over V4L by the basic driver
+ already. Here RDS can be read from files in /dev/v4l/rds.
+
+ To compile this driver as a module, choose M here: the
+ module will be called miropcm20-rds.
+
+config RADIO_SF16FMI
+ tristate "SF16FMI Radio"
+ depends on ISA && VIDEO_DEV
+ ---help---
+ Choose Y here if you have one of these FM radio cards. If you
+ compile the driver into the kernel and your card is not PnP one, you
+ have to add "sf16fm=<io>" to the kernel command line (I/O address is
+ 0x284 or 0x384).
+
+ In order to control your radio card, you will need to use programs
+ that are compatible with the Video For Linux API. Information on
+ this API and pointers to "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-sf16fmi.
+
+config RADIO_SF16FMR2
+ tristate "SF16FMR2 Radio"
+ depends on ISA && VIDEO_DEV
+ ---help---
+ Choose Y here if you have one of these FM radio cards.
+
+ In order to control your radio card, you will need to use programs
+ that are compatible with the Video For Linux API. Information on
+ this API and pointers to "v4l" programs may be found on the WWW at
+ <http://roadrunner.swansea.uk.linux.org/v4l.shtml>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-sf16fmr2.
+
+config RADIO_TERRATEC
+ tristate "TerraTec ActiveRadio ISA Standalone"
+ depends on ISA && VIDEO_DEV
+ ---help---
+ Choose Y here if you have this FM radio card, and then fill in the
+ port address below. (TODO)
+
+ Note: This driver is in its early stages. Right now volume and
+ frequency control and muting works at least for me, but
+ unfortunately I have not found anybody who wants to use this card
+ with Linux. So if it is this what YOU are trying to do right now,
+ PLEASE DROP ME A NOTE!! Rolf Offermanns <rolf@offermanns.de>.
+
+ In order to control your radio card, you will need to use programs
+ that are compatible with the Video For Linux API. Information on
+ this API and pointers to "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-terratec.
+
+config RADIO_TERRATEC_PORT
+ hex "Terratec i/o port (normally 0x590)"
+ depends on RADIO_TERRATEC=y
+ default "590"
+ help
+ Fill in the I/O port of your TerraTec FM radio card. If unsure, go
+ with the default.
+
+config RADIO_TRUST
+ tristate "Trust FM radio card"
+ depends on ISA && VIDEO_DEV
+ help
+ This is a driver for the Trust FM radio cards. Say Y if you have
+ such a card and want to use it under Linux.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-trust.
+
+config RADIO_TRUST_PORT
+ hex "Trust i/o port (usually 0x350 or 0x358)"
+ depends on RADIO_TRUST=y
+ default "350"
+ help
+ Enter the I/O port of your Trust FM radio card. If unsure, try the
+ values "0x350" or "0x358".
+
+config RADIO_TYPHOON
+ tristate "Typhoon Radio (a.k.a. EcoRadio)"
+ depends on ISA && VIDEO_DEV
+ ---help---
+ Choose Y here if you have one of these FM radio cards, and then fill
+ in the port address and the frequency used for muting below.
+
+ In order to control your radio card, you will need to use programs
+ that are compatible with the Video For Linux API. Information on
+ this API and pointers to "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-typhoon.
+
+config RADIO_TYPHOON_PROC_FS
+ bool "Support for /proc/radio-typhoon"
+ depends on PROC_FS && RADIO_TYPHOON
+ help
+ Say Y here if you want the typhoon radio card driver to write
+ status information (frequency, volume, muted, mute frequency,
+ base address) to /proc/radio-typhoon. The file can be viewed with
+ your favorite pager (i.e. use "more /proc/radio-typhoon" or "less
+ /proc/radio-typhoon" or simply "cat /proc/radio-typhoon").
+
+config RADIO_TYPHOON_PORT
+ hex "Typhoon I/O port (0x316 or 0x336)"
+ depends on RADIO_TYPHOON=y
+ default "316"
+ help
+ Enter the I/O port of your Typhoon or EcoRadio radio card.
+
+config RADIO_TYPHOON_MUTEFREQ
+ int "Typhoon frequency set when muting the device (kHz)"
+ depends on RADIO_TYPHOON=y
+ default "87500"
+ help
+ Enter the frequency used for muting the radio. The device is never
+ completely silent. If the volume is just turned down, you can still
+ hear silent voices and music. For that reason, the frequency of the
+ radio device is set to the frequency you can enter here whenever
+ the device is muted. There should be no local radio station at that
+ frequency.
+
+config RADIO_ZOLTRIX
+ tristate "Zoltrix Radio"
+ depends on ISA && VIDEO_DEV
+ ---help---
+ Choose Y here if you have one of these FM radio cards, and then fill
+ in the port address below.
+
+ In order to control your radio card, you will need to use programs
+ that are compatible with the Video For Linux API. Information on
+ this API and pointers to "v4l" programs may be found at
+ <file:Documentation/video4linux/API.html>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-zoltrix.
+
+config RADIO_ZOLTRIX_PORT
+ hex "ZOLTRIX I/O port (0x20c or 0x30c)"
+ depends on RADIO_ZOLTRIX=y
+ default "20c"
+ help
+ Enter the I/O port of your Zoltrix radio card.
+
+endmenu
+
diff --git a/linux/drivers/media/radio/Makefile b/linux/drivers/media/radio/Makefile
new file mode 100644
index 000000000..8b351945d
--- /dev/null
+++ b/linux/drivers/media/radio/Makefile
@@ -0,0 +1,22 @@
+#
+# Makefile for the kernel character device drivers.
+#
+
+miropcm20-objs := miropcm20-rds-core.o miropcm20-radio.o
+
+obj-$(CONFIG_RADIO_AZTECH) += radio-aztech.o
+obj-$(CONFIG_RADIO_RTRACK2) += radio-rtrack2.o
+obj-$(CONFIG_RADIO_SF16FMI) += radio-sf16fmi.o
+obj-$(CONFIG_RADIO_SF16FMR2) += radio-sf16fmr2.o
+obj-$(CONFIG_RADIO_CADET) += radio-cadet.o
+obj-$(CONFIG_RADIO_TYPHOON) += radio-typhoon.o
+obj-$(CONFIG_RADIO_TERRATEC) += radio-terratec.o
+obj-$(CONFIG_RADIO_MAXIRADIO) += radio-maxiradio.o
+obj-$(CONFIG_RADIO_RTRACK) += radio-aimslab.o
+obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o
+obj-$(CONFIG_RADIO_MIROPCM20) += miropcm20.o
+obj-$(CONFIG_RADIO_MIROPCM20_RDS) += miropcm20-rds.o
+obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o
+obj-$(CONFIG_RADIO_GEMTEK_PCI) += radio-gemtek-pci.o
+obj-$(CONFIG_RADIO_TRUST) += radio-trust.o
+obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o
diff --git a/linux/drivers/media/radio/miropcm20-radio.c b/linux/drivers/media/radio/miropcm20-radio.c
new file mode 100644
index 000000000..c305ecbc0
--- /dev/null
+++ b/linux/drivers/media/radio/miropcm20-radio.c
@@ -0,0 +1,266 @@
+/* Miro PCM20 radio driver for Linux radio support
+ * (c) 1998 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
+ * Thanks to Norberto Pellici for the ACI device interface specification
+ * The API part is based on the radiotrack driver by M. Kirkwood
+ * This driver relies on the aci mixer (drivers/sound/aci.c)
+ * Look there for further info...
+ */
+
+/* Revision history:
+ *
+ * 1998 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
+ * 2000-09-05 Robert Siemer <Robert.Siemer@gmx.de>
+ * removed unfinished volume control (maybe adding it later again)
+ * use OSS-mixer; added stereo control
+ */
+
+/* What ever you think about the ACI, version 0x07 is not very well!
+ * I can't get frequency, 'tuner status', 'tuner flags' or mute/mono
+ * conditions... Robert
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include "compat.h"
+#include <linux/videodev.h>
+#include "../../../sound/oss/aci.h"
+#include "miropcm20-rds-core.h"
+
+static int radio_nr = -1;
+module_param(radio_nr, int, 0);
+
+struct pcm20_device {
+ unsigned long freq;
+ int muted;
+ int stereo;
+};
+
+
+static int pcm20_mute(struct pcm20_device *dev, unsigned char mute)
+{
+ dev->muted = mute;
+ return aci_write_cmd(ACI_SET_TUNERMUTE, mute);
+}
+
+static int pcm20_stereo(struct pcm20_device *dev, unsigned char stereo)
+{
+ dev->stereo = stereo;
+ return aci_write_cmd(ACI_SET_TUNERMONO, !stereo);
+}
+
+static int pcm20_setfreq(struct pcm20_device *dev, unsigned long freq)
+{
+ unsigned char freql;
+ unsigned char freqh;
+
+ dev->freq=freq;
+
+ freq /= 160;
+ if (!(aci_version==0x07 || aci_version>=0xb0))
+ freq /= 10; /* I don't know exactly which version
+ * needs this hack */
+ freql = freq & 0xff;
+ freqh = freq >> 8;
+
+ aci_rds_cmd(RDS_RESET, NULL, 0);
+ pcm20_stereo(dev, 1);
+
+ return aci_rw_cmd(ACI_WRITE_TUNE, freql, freqh);
+}
+
+static int pcm20_getflags(struct pcm20_device *dev, __u32 *flags, __u16 *signal)
+{
+ /* okay, check for signal, stereo and rds here... */
+ int i;
+ unsigned char buf;
+
+ if ((i=aci_rw_cmd(ACI_READ_TUNERSTATION, -1, -1))<0)
+ return i;
+ pr_debug("check_sig: 0x%x\n", i);
+ if (i & 0x80) {
+ /* no signal from tuner */
+ *flags=0;
+ *signal=0;
+ return 0;
+ } else
+ *signal=0xffff;
+
+ if ((i=aci_rw_cmd(ACI_READ_TUNERSTEREO, -1, -1))<0)
+ return i;
+ if (i & 0x40) {
+ *flags=0;
+ } else {
+ /* stereo */
+ *flags=VIDEO_TUNER_STEREO_ON;
+ /* I can't see stereo, when forced to mono */
+ dev->stereo=1;
+ }
+
+ if ((i=aci_rds_cmd(RDS_STATUS, &buf, 1))<0)
+ return i;
+ if (buf & 1)
+ /* RDS available */
+ *flags|=VIDEO_TUNER_RDS_ON;
+ else
+ return 0;
+
+ if ((i=aci_rds_cmd(RDS_RXVALUE, &buf, 1))<0)
+ return i;
+ pr_debug("rds-signal: %d\n", buf);
+ if (buf > 15) {
+ printk("miropcm20-radio: RX strengths unexpected high...\n");
+ buf=15;
+ }
+ /* refine signal */
+ if ((*signal=SCALE(15, 0xffff, buf))==0)
+ *signal = 1;
+
+ return 0;
+}
+
+static int pcm20_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct pcm20_device *pcm20 = dev->priv;
+ int i;
+
+ switch(cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *v = arg;
+ memset(v,0,sizeof(*v));
+ v->type=VID_TYPE_TUNER;
+ strcpy(v->name, "Miro PCM20");
+ v->channels=1;
+ v->audios=1;
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner *v = arg;
+ if(v->tuner) /* Only 1 tuner */
+ return -EINVAL;
+ v->rangelow=87*16000;
+ v->rangehigh=108*16000;
+ pcm20_getflags(pcm20, &v->flags, &v->signal);
+ v->flags|=VIDEO_TUNER_LOW;
+ v->mode=VIDEO_MODE_AUTO;
+ strcpy(v->name, "FM");
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner *v = arg;
+ if(v->tuner!=0)
+ return -EINVAL;
+ /* Only 1 tuner so no setting needed ! */
+ return 0;
+ }
+ case VIDIOCGFREQ:
+ {
+ unsigned long *freq = arg;
+ *freq = pcm20->freq;
+ return 0;
+ }
+ case VIDIOCSFREQ:
+ {
+ unsigned long *freq = arg;
+ pcm20->freq = *freq;
+ i=pcm20_setfreq(pcm20, pcm20->freq);
+ pr_debug("First view (setfreq): 0x%x\n", i);
+ return i;
+ }
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio *v = arg;
+ memset(v,0, sizeof(*v));
+ v->flags=VIDEO_AUDIO_MUTABLE;
+ if (pcm20->muted)
+ v->flags|=VIDEO_AUDIO_MUTE;
+ v->mode=VIDEO_SOUND_STEREO;
+ if (pcm20->stereo)
+ v->mode|=VIDEO_SOUND_MONO;
+ /* v->step=2048; */
+ strcpy(v->name, "Radio");
+ return 0;
+ }
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio *v = arg;
+ if(v->audio)
+ return -EINVAL;
+
+ pcm20_mute(pcm20, !!(v->flags&VIDEO_AUDIO_MUTE));
+ if(v->flags&VIDEO_SOUND_MONO)
+ pcm20_stereo(pcm20, 0);
+ if(v->flags&VIDEO_SOUND_STEREO)
+ pcm20_stereo(pcm20, 1);
+
+ return 0;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int pcm20_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, pcm20_do_ioctl);
+}
+
+static struct pcm20_device pcm20_unit = {
+ .freq = 87*16000,
+ .muted = 1,
+};
+
+static struct file_operations pcm20_fops = {
+ .owner = THIS_MODULE,
+ .open = video_exclusive_open,
+ .release = video_exclusive_release,
+ .ioctl = pcm20_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device pcm20_radio = {
+ .owner = THIS_MODULE,
+ .name = "Miro PCM 20 radio",
+ .type = VID_TYPE_TUNER,
+ .hardware = VID_HARDWARE_RTRACK,
+ .fops = &pcm20_fops,
+ .priv = &pcm20_unit
+};
+
+static int __init pcm20_init(void)
+{
+ if(video_register_device(&pcm20_radio, VFL_TYPE_RADIO, radio_nr)==-1)
+ goto video_register_device;
+
+ if(attach_aci_rds()<0)
+ goto attach_aci_rds;
+
+ printk(KERN_INFO "Miro PCM20 radio card driver.\n");
+
+ return 0;
+
+ attach_aci_rds:
+ video_unregister_device(&pcm20_radio);
+ video_register_device:
+ return -EINVAL;
+}
+
+MODULE_AUTHOR("Ruurd Reitsma");
+MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card.");
+MODULE_LICENSE("GPL");
+
+static void __exit pcm20_cleanup(void)
+{
+ unload_aci_rds();
+ video_unregister_device(&pcm20_radio);
+}
+
+module_init(pcm20_init);
+module_exit(pcm20_cleanup);
diff --git a/linux/drivers/media/radio/miropcm20-rds-core.c b/linux/drivers/media/radio/miropcm20-rds-core.c
new file mode 100644
index 000000000..b602c73e2
--- /dev/null
+++ b/linux/drivers/media/radio/miropcm20-rds-core.c
@@ -0,0 +1,211 @@
+/*
+ * Many thanks to Fred Seidel <seidel@metabox.de>, the
+ * designer of the RDS decoder hardware. With his help
+ * I was able to code this driver.
+ * Thanks also to Norberto Pellicci, Dominic Mounteney
+ * <DMounteney@pinnaclesys.com> and www.teleauskunft.de
+ * for good hints on finding Fred. It was somewhat hard
+ * to locate him here in Germany... [:
+ *
+ * Revision history:
+ *
+ * 2000-08-09 Robert Siemer <Robert.Siemer@gmx.de>
+ * RDS support for MiroSound PCM20 radio
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+
+#include <asm/io.h>
+#include "../../../sound/oss/aci.h"
+#include "miropcm20-rds-core.h"
+
+#define DEBUG 0
+
+static struct mutex aci_rds_mutex;
+
+#define RDS_DATASHIFT 2 /* Bit 2 */
+#define RDS_DATAMASK (1 << RDS_DATASHIFT)
+#define RDS_BUSYMASK 0x10 /* Bit 4 */
+#define RDS_CLOCKMASK 0x08 /* Bit 3 */
+
+#define RDS_DATA(x) (((x) >> RDS_DATASHIFT) & 1)
+
+
+#if DEBUG
+static void print_matrix(char array[], unsigned int length)
+{
+ int i, j;
+
+ for (i=0; i<length; i++) {
+ printk(KERN_DEBUG "aci-rds: ");
+ for (j=7; j>=0; j--) {
+ printk("%d", (array[i] >> j) & 0x1);
+ }
+ if (i%8 == 0)
+ printk(" byte-border\n");
+ else
+ printk("\n");
+ }
+}
+#endif /* DEBUG */
+
+static int byte2trans(unsigned char byte, unsigned char sendbuffer[], int size)
+{
+ int i;
+
+ if (size != 8)
+ return -1;
+ for (i = 7; i >= 0; i--)
+ sendbuffer[7-i] = (byte & (1 << i)) ? RDS_DATAMASK : 0;
+ sendbuffer[0] |= RDS_CLOCKMASK;
+
+ return 0;
+}
+
+static int rds_waitread(void)
+{
+ unsigned char byte;
+ int i=2000;
+
+ do {
+ byte=inb(RDS_REGISTER);
+ i--;
+ }
+ while ((byte & RDS_BUSYMASK) && i);
+
+ if (i) {
+ #if DEBUG
+ printk(KERN_DEBUG "rds_waitread()");
+ print_matrix(&byte, 1);
+ #endif
+ return (byte);
+ } else {
+ printk(KERN_WARNING "aci-rds: rds_waitread() timeout...\n");
+ return -1;
+ }
+}
+
+/* don't use any ..._nowait() function if you are not sure what you do... */
+
+static inline void rds_rawwrite_nowait(unsigned char byte)
+{
+ #if DEBUG
+ printk(KERN_DEBUG "rds_rawwrite()");
+ print_matrix(&byte, 1);
+ #endif
+ outb(byte, RDS_REGISTER);
+}
+
+static int rds_rawwrite(unsigned char byte)
+{
+ if (rds_waitread() >= 0) {
+ rds_rawwrite_nowait(byte);
+ return 0;
+ } else
+ return -1;
+}
+
+static int rds_write(unsigned char cmd)
+{
+ unsigned char sendbuffer[8];
+ int i;
+
+ if (byte2trans(cmd, sendbuffer, 8) != 0){
+ return -1;
+ } else {
+ for (i=0; i<8; i++) {
+ rds_rawwrite(sendbuffer[i]);
+ }
+ }
+ return 0;
+}
+
+static int rds_readcycle_nowait(void)
+{
+ rds_rawwrite_nowait(0);
+ return rds_waitread();
+}
+
+static int rds_readcycle(void)
+{
+ if (rds_rawwrite(0) < 0)
+ return -1;
+ return rds_waitread();
+}
+
+static int rds_read(unsigned char databuffer[], int datasize)
+{
+ #define READSIZE (8*datasize)
+
+ int i,j;
+
+ if (datasize < 1) /* nothing to read */
+ return 0;
+
+ /* to be able to use rds_readcycle_nowait()
+ I have to waitread() here */
+ if (rds_waitread() < 0)
+ return -1;
+
+ memset(databuffer, 0, datasize);
+
+ for (i=0; i< READSIZE; i++)
+ if((j=rds_readcycle_nowait()) < 0) {
+ return -1;
+ } else {
+ databuffer[i/8]|=(RDS_DATA(j) << (7-(i%8)));
+ }
+
+ return 0;
+}
+
+static int rds_ack(void)
+{
+ int i=rds_readcycle();
+
+ if (i < 0)
+ return -1;
+ if (i & RDS_DATAMASK) {
+ return 0; /* ACK */
+ } else {
+ printk(KERN_DEBUG "aci-rds: NACK\n");
+ return 1; /* NACK */
+ }
+}
+
+int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasize)
+{
+ int ret;
+
+ if (mutex_lock_interruptible(&aci_rds_mutex))
+ return -EINTR;
+
+ rds_write(cmd);
+
+ /* RDS_RESET doesn't need further processing */
+ if (cmd!=RDS_RESET && (rds_ack() || rds_read(databuffer, datasize)))
+ ret = -1;
+ else
+ ret = 0;
+
+ mutex_unlock(&aci_rds_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(aci_rds_cmd);
+
+int __init attach_aci_rds(void)
+{
+ mutex_init(&aci_rds_mutex);
+ return 0;
+}
+
+void __exit unload_aci_rds(void)
+{
+}
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/radio/miropcm20-rds-core.h b/linux/drivers/media/radio/miropcm20-rds-core.h
new file mode 100644
index 000000000..aeb5761f0
--- /dev/null
+++ b/linux/drivers/media/radio/miropcm20-rds-core.h
@@ -0,0 +1,19 @@
+#ifndef _MIROPCM20_RDS_CORE_H_
+#define _MIROPCM20_RDS_CORE_H_
+
+extern int aci_rds_cmd(unsigned char cmd, unsigned char databuffer[], int datasize);
+
+#define RDS_STATUS 0x01
+#define RDS_STATIONNAME 0x02
+#define RDS_TEXT 0x03
+#define RDS_ALTFREQ 0x04
+#define RDS_TIMEDATE 0x05
+#define RDS_PI_CODE 0x06
+#define RDS_PTYTATP 0x07
+#define RDS_RESET 0x08
+#define RDS_RXVALUE 0x09
+
+extern void __exit unload_aci_rds(void);
+extern int __init attach_aci_rds(void);
+
+#endif /* _MIROPCM20_RDS_CORE_H_ */
diff --git a/linux/drivers/media/radio/miropcm20-rds.c b/linux/drivers/media/radio/miropcm20-rds.c
new file mode 100644
index 000000000..e09214082
--- /dev/null
+++ b/linux/drivers/media/radio/miropcm20-rds.c
@@ -0,0 +1,134 @@
+/* MiroSOUND PCM20 radio rds interface driver
+ * (c) 2001 Robert Siemer <Robert.Siemer@gmx.de>
+ * Thanks to Fred Seidel. See miropcm20-rds-core.c for further information.
+ */
+
+/* Revision history:
+ *
+ * 2001-04-18 Robert Siemer <Robert.Siemer@gmx.de>
+ * separate file for user interface driver
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/sched.h> /* current, TASK_*, schedule_timeout() */
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include "miropcm20-rds-core.h"
+
+static char * text_buffer;
+static int rds_users = 0;
+
+
+static int rds_f_open(struct inode *in, struct file *fi)
+{
+ if (rds_users)
+ return -EBUSY;
+
+ rds_users++;
+ if ((text_buffer=kmalloc(66, GFP_KERNEL)) == 0) {
+ rds_users--;
+ printk(KERN_NOTICE "aci-rds: Out of memory by open()...\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int rds_f_release(struct inode *in, struct file *fi)
+{
+ kfree(text_buffer);
+
+ rds_users--;
+ return 0;
+}
+
+static void print_matrix(char *ch, char out[])
+{
+ int j;
+
+ for (j=7; j>=0; j--) {
+ out[7-j] = ((*ch >> j) & 0x1) + '0';
+ }
+}
+
+static ssize_t rds_f_read(struct file *file, char __user *buffer, size_t length, loff_t *offset)
+{
+// i = sprintf(text_buffer, "length: %d, offset: %d\n", length, *offset);
+
+ char c;
+ char bits[8];
+
+ msleep(2000);
+ aci_rds_cmd(RDS_STATUS, &c, 1);
+ print_matrix(&c, bits);
+ if (copy_to_user(buffer, bits, 8))
+ return -EFAULT;
+
+/* if ((c >> 3) & 1) {
+ aci_rds_cmd(RDS_STATIONNAME, text_buffer+1, 8);
+ text_buffer[0] = ' ' ;
+ text_buffer[9] = '\n';
+ return copy_to_user(buffer+8, text_buffer, 10) ? -EFAULT: 18;
+ }
+*/
+/* if ((c >> 6) & 1) {
+ aci_rds_cmd(RDS_PTYTATP, &c, 1);
+ if ( c & 1)
+ sprintf(text_buffer, " M");
+ else
+ sprintf(text_buffer, " S");
+ if ((c >> 1) & 1)
+ sprintf(text_buffer+2, " TA");
+ else
+ sprintf(text_buffer+2, " --");
+ if ((c >> 7) & 1)
+ sprintf(text_buffer+5, " TP");
+ else
+ sprintf(text_buffer+5, " --");
+ sprintf(text_buffer+8, " %2d\n", (c >> 2) & 0x1f);
+ return copy_to_user(buffer+8, text_buffer, 12) ? -EFAULT: 20;
+ }
+*/
+
+ if ((c >> 4) & 1) {
+ aci_rds_cmd(RDS_TEXT, text_buffer, 65);
+ text_buffer[0] = ' ' ;
+ text_buffer[65] = '\n';
+ return copy_to_user(buffer+8, text_buffer,66) ? -EFAULT : 66+8;
+ } else {
+ put_user('\n', buffer+8);
+ return 9;
+ }
+}
+
+static struct file_operations rds_fops = {
+ .owner = THIS_MODULE,
+ .read = rds_f_read,
+ .open = rds_f_open,
+ .release = rds_f_release
+};
+
+static struct miscdevice rds_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "radiotext",
+ .devfs_name = "v4l/rds/radiotext",
+ .fops = &rds_fops,
+};
+
+static int __init miropcm20_rds_init(void)
+{
+ return misc_register(&rds_miscdev);
+}
+
+static void __exit miropcm20_rds_cleanup(void)
+{
+ misc_deregister(&rds_miscdev);
+}
+
+module_init(miropcm20_rds_init);
+module_exit(miropcm20_rds_cleanup);
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/radio/radio-aimslab.c b/linux/drivers/media/radio/radio-aimslab.c
new file mode 100644
index 000000000..0fa385ef8
--- /dev/null
+++ b/linux/drivers/media/radio/radio-aimslab.c
@@ -0,0 +1,370 @@
+/* radiotrack (radioreveal) driver for Linux radio support
+ * (c) 1997 M. Kirkwood
+ * Converted to new API by Alan Cox <Alan.Cox@linux.org>
+ * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
+ *
+ * History:
+ * 1999-02-24 Russell Kroll <rkroll@exploits.org>
+ * Fine tuning/VIDEO_TUNER_LOW
+ * Frequency range expanded to start at 87 MHz
+ *
+ * TODO: Allow for more than one of these foolish entities :-)
+ *
+ * Notes on the hardware (reverse engineered from other peoples'
+ * reverse engineering of AIMS' code :-)
+ *
+ * Frequency control is done digitally -- ie out(port,encodefreq(95.8));
+ *
+ * The signal strength query is unsurprisingly inaccurate. And it seems
+ * to indicate that (on my card, at least) the frequency setting isn't
+ * too great. (I have to tune up .025MHz from what the freq should be
+ * to get a report that the thing is tuned.)
+ *
+ * Volume control is (ugh) analogue:
+ * out(port, start_increasing_volume);
+ * wait(a_wee_while);
+ * out(port, stop_changing_the_volume);
+ *
+ */
+
+#include <linux/module.h> /* Modules */
+#include <linux/init.h> /* Initdata */
+#include <linux/ioport.h> /* request_region */
+#include <linux/delay.h> /* udelay */
+#include <asm/io.h> /* outb, outb_p */
+#include <asm/uaccess.h> /* copy to/from user */
+#include "compat.h"
+#include <linux/videodev.h> /* kernel radio structs */
+#include <linux/config.h> /* CONFIG_RADIO_RTRACK_PORT */
+#include <asm/semaphore.h> /* Lock for the I/O */
+
+#ifndef CONFIG_RADIO_RTRACK_PORT
+#define CONFIG_RADIO_RTRACK_PORT -1
+#endif
+
+static int io = CONFIG_RADIO_RTRACK_PORT;
+static int radio_nr = -1;
+static struct mutex lock;
+
+struct rt_device
+{
+ int port;
+ int curvol;
+ unsigned long curfreq;
+ int muted;
+};
+
+
+/* local things */
+
+static void sleep_delay(long n)
+{
+ /* Sleep nicely for 'n' uS */
+ int d=n/(1000000/HZ);
+ if(!d)
+ udelay(n);
+ else
+ msleep(jiffies_to_msecs(d));
+}
+
+static void rt_decvol(void)
+{
+ outb(0x58, io); /* volume down + sigstr + on */
+ sleep_delay(100000);
+ outb(0xd8, io); /* volume steady + sigstr + on */
+}
+
+static void rt_incvol(void)
+{
+ outb(0x98, io); /* volume up + sigstr + on */
+ sleep_delay(100000);
+ outb(0xd8, io); /* volume steady + sigstr + on */
+}
+
+static void rt_mute(struct rt_device *dev)
+{
+ dev->muted = 1;
+ mutex_lock(&lock);
+ outb(0xd0, io); /* volume steady, off */
+ mutex_unlock(&lock);
+}
+
+static int rt_setvol(struct rt_device *dev, int vol)
+{
+ int i;
+
+ mutex_lock(&lock);
+
+ if(vol == dev->curvol) { /* requested volume = current */
+ if (dev->muted) { /* user is unmuting the card */
+ dev->muted = 0;
+ outb (0xd8, io); /* enable card */
+ }
+ mutex_unlock(&lock);
+ return 0;
+ }
+
+ if(vol == 0) { /* volume = 0 means mute the card */
+ outb(0x48, io); /* volume down but still "on" */
+ sleep_delay(2000000); /* make sure it's totally down */
+ outb(0xd0, io); /* volume steady, off */
+ dev->curvol = 0; /* track the volume state! */
+ mutex_unlock(&lock);
+ return 0;
+ }
+
+ dev->muted = 0;
+ if(vol > dev->curvol)
+ for(i = dev->curvol; i < vol; i++)
+ rt_incvol();
+ else
+ for(i = dev->curvol; i > vol; i--)
+ rt_decvol();
+
+ dev->curvol = vol;
+ mutex_unlock(&lock);
+ return 0;
+}
+
+/* the 128+64 on these outb's is to keep the volume stable while tuning
+ * without them, the volume _will_ creep up with each frequency change
+ * and bit 4 (+16) is to keep the signal strength meter enabled
+ */
+
+static void send_0_byte(int port, struct rt_device *dev)
+{
+ if ((dev->curvol == 0) || (dev->muted)) {
+ outb_p(128+64+16+ 1, port); /* wr-enable + data low */
+ outb_p(128+64+16+2+1, port); /* clock */
+ }
+ else {
+ outb_p(128+64+16+8+ 1, port); /* on + wr-enable + data low */
+ outb_p(128+64+16+8+2+1, port); /* clock */
+ }
+ sleep_delay(1000);
+}
+
+static void send_1_byte(int port, struct rt_device *dev)
+{
+ if ((dev->curvol == 0) || (dev->muted)) {
+ outb_p(128+64+16+4 +1, port); /* wr-enable+data high */
+ outb_p(128+64+16+4+2+1, port); /* clock */
+ }
+ else {
+ outb_p(128+64+16+8+4 +1, port); /* on+wr-enable+data high */
+ outb_p(128+64+16+8+4+2+1, port); /* clock */
+ }
+
+ sleep_delay(1000);
+}
+
+static int rt_setfreq(struct rt_device *dev, unsigned long freq)
+{
+ int i;
+
+ /* adapted from radio-aztech.c */
+
+ /* now uses VIDEO_TUNER_LOW for fine tuning */
+
+ freq += 171200; /* Add 10.7 MHz IF */
+ freq /= 800; /* Convert to 50 kHz units */
+
+ mutex_lock(&lock); /* Stop other ops interfering */
+
+ send_0_byte (io, dev); /* 0: LSB of frequency */
+
+ for (i = 0; i < 13; i++) /* : frequency bits (1-13) */
+ if (freq & (1 << i))
+ send_1_byte (io, dev);
+ else
+ send_0_byte (io, dev);
+
+ send_0_byte (io, dev); /* 14: test bit - always 0 */
+ send_0_byte (io, dev); /* 15: test bit - always 0 */
+
+ send_0_byte (io, dev); /* 16: band data 0 - always 0 */
+ send_0_byte (io, dev); /* 17: band data 1 - always 0 */
+ send_0_byte (io, dev); /* 18: band data 2 - always 0 */
+ send_0_byte (io, dev); /* 19: time base - always 0 */
+
+ send_0_byte (io, dev); /* 20: spacing (0 = 25 kHz) */
+ send_1_byte (io, dev); /* 21: spacing (1 = 25 kHz) */
+ send_0_byte (io, dev); /* 22: spacing (0 = 25 kHz) */
+ send_1_byte (io, dev); /* 23: AM/FM (FM = 1, always) */
+
+ if ((dev->curvol == 0) || (dev->muted))
+ outb (0xd0, io); /* volume steady + sigstr */
+ else
+ outb (0xd8, io); /* volume steady + sigstr + on */
+
+ mutex_unlock(&lock);
+
+ return 0;
+}
+
+static int rt_getsigstr(struct rt_device *dev)
+{
+ if (inb(io) & 2) /* bit set = no signal present */
+ return 0;
+ return 1; /* signal present */
+}
+
+static int rt_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct rt_device *rt=dev->priv;
+
+ switch(cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *v = arg;
+ memset(v,0,sizeof(*v));
+ v->type=VID_TYPE_TUNER;
+ v->channels=1;
+ v->audios=1;
+ strcpy(v->name, "RadioTrack");
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner *v = arg;
+ if(v->tuner) /* Only 1 tuner */
+ return -EINVAL;
+ v->rangelow=(87*16000);
+ v->rangehigh=(108*16000);
+ v->flags=VIDEO_TUNER_LOW;
+ v->mode=VIDEO_MODE_AUTO;
+ strcpy(v->name, "FM");
+ v->signal=0xFFFF*rt_getsigstr(rt);
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner *v = arg;
+ if(v->tuner!=0)
+ return -EINVAL;
+ /* Only 1 tuner so no setting needed ! */
+ return 0;
+ }
+ case VIDIOCGFREQ:
+ {
+ unsigned long *freq = arg;
+ *freq = rt->curfreq;
+ return 0;
+ }
+ case VIDIOCSFREQ:
+ {
+ unsigned long *freq = arg;
+ rt->curfreq = *freq;
+ rt_setfreq(rt, rt->curfreq);
+ return 0;
+ }
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio *v = arg;
+ memset(v,0, sizeof(*v));
+ v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
+ v->volume=rt->curvol * 6554;
+ v->step=6554;
+ strcpy(v->name, "Radio");
+ return 0;
+ }
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio *v = arg;
+ if(v->audio)
+ return -EINVAL;
+ if(v->flags&VIDEO_AUDIO_MUTE)
+ rt_mute(rt);
+ else
+ rt_setvol(rt,v->volume/6554);
+ return 0;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int rt_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, rt_do_ioctl);
+}
+
+static struct rt_device rtrack_unit;
+
+static struct file_operations rtrack_fops = {
+ .owner = THIS_MODULE,
+ .open = video_exclusive_open,
+ .release = video_exclusive_release,
+ .ioctl = rt_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device rtrack_radio=
+{
+ .owner = THIS_MODULE,
+ .name = "RadioTrack radio",
+ .type = VID_TYPE_TUNER,
+ .hardware = VID_HARDWARE_RTRACK,
+ .fops = &rtrack_fops,
+};
+
+static int __init rtrack_init(void)
+{
+ if(io==-1)
+ {
+ printk(KERN_ERR "You must set an I/O address with io=0x???\n");
+ return -EINVAL;
+ }
+
+ if (!request_region(io, 2, "rtrack"))
+ {
+ printk(KERN_ERR "rtrack: port 0x%x already in use\n", io);
+ return -EBUSY;
+ }
+
+ rtrack_radio.priv=&rtrack_unit;
+
+ if(video_register_device(&rtrack_radio, VFL_TYPE_RADIO, radio_nr)==-1)
+ {
+ release_region(io, 2);
+ return -EINVAL;
+ }
+ printk(KERN_INFO "AIMSlab RadioTrack/RadioReveal card driver.\n");
+
+ /* Set up the I/O locking */
+
+ mutex_init(&lock);
+
+ /* mute card - prevents noisy bootups */
+
+ /* this ensures that the volume is all the way down */
+ outb(0x48, io); /* volume down but still "on" */
+ sleep_delay(2000000); /* make sure it's totally down */
+ outb(0xc0, io); /* steady volume, mute card */
+ rtrack_unit.curvol = 0;
+
+ return 0;
+}
+
+MODULE_AUTHOR("M.Kirkwood");
+MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card.");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20f or 0x30f)");
+module_param(radio_nr, int, 0);
+
+static void __exit cleanup_rtrack_module(void)
+{
+ video_unregister_device(&rtrack_radio);
+ release_region(io,2);
+}
+
+module_init(rtrack_init);
+module_exit(cleanup_rtrack_module);
+
diff --git a/linux/drivers/media/radio/radio-aztech.c b/linux/drivers/media/radio/radio-aztech.c
new file mode 100644
index 000000000..97f88c907
--- /dev/null
+++ b/linux/drivers/media/radio/radio-aztech.c
@@ -0,0 +1,317 @@
+/* radio-aztech.c - Aztech radio card driver for Linux 2.2
+ *
+ * Adapted to support the Video for Linux API by
+ * Russell Kroll <rkroll@exploits.org>. Based on original tuner code by:
+ *
+ * Quay Ly
+ * Donald Song
+ * Jason Lewis (jlewis@twilight.vtc.vsc.edu)
+ * Scott McGrath (smcgrath@twilight.vtc.vsc.edu)
+ * William McGrath (wmcgrath@twilight.vtc.vsc.edu)
+ *
+ * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/
+ * along with more information on the card itself.
+ *
+ * History:
+ * 1999-02-24 Russell Kroll <rkroll@exploits.org>
+ * Fine tuning/VIDEO_TUNER_LOW
+ * Range expanded to 87-108 MHz (from 87.9-107.8)
+ *
+ * Notable changes from the original source:
+ * - includes stripped down to the essentials
+ * - for loops used as delays replaced with udelay()
+ * - #defines removed, changed to static values
+ * - tuning structure changed - no more character arrays, other changes
+*/
+
+#include <linux/module.h> /* Modules */
+#include <linux/init.h> /* Initdata */
+#include <linux/ioport.h> /* request_region */
+#include <linux/delay.h> /* udelay */
+#include <asm/io.h> /* outb, outb_p */
+#include <asm/uaccess.h> /* copy to/from user */
+#include "compat.h"
+#include <linux/videodev.h> /* kernel radio structs */
+#include <linux/config.h> /* CONFIG_RADIO_AZTECH_PORT */
+
+/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
+
+#ifndef CONFIG_RADIO_AZTECH_PORT
+#define CONFIG_RADIO_AZTECH_PORT -1
+#endif
+
+static int io = CONFIG_RADIO_AZTECH_PORT;
+static int radio_nr = -1;
+static int radio_wait_time = 1000;
+static struct mutex lock;
+
+struct az_device
+{
+ int curvol;
+ unsigned long curfreq;
+ int stereo;
+};
+
+static int volconvert(int level)
+{
+ level>>=14; /* Map 16bits down to 2 bit */
+ level&=3;
+
+ /* convert to card-friendly values */
+ switch (level)
+ {
+ case 0:
+ return 0;
+ case 1:
+ return 1;
+ case 2:
+ return 4;
+ case 3:
+ return 5;
+ }
+ return 0; /* Quieten gcc */
+}
+
+static void send_0_byte (struct az_device *dev)
+{
+ udelay(radio_wait_time);
+ outb_p(2+volconvert(dev->curvol), io);
+ outb_p(64+2+volconvert(dev->curvol), io);
+}
+
+static void send_1_byte (struct az_device *dev)
+{
+ udelay (radio_wait_time);
+ outb_p(128+2+volconvert(dev->curvol), io);
+ outb_p(128+64+2+volconvert(dev->curvol), io);
+}
+
+static int az_setvol(struct az_device *dev, int vol)
+{
+ mutex_lock(&lock);
+ outb (volconvert(vol), io);
+ mutex_unlock(&lock);
+ return 0;
+}
+
+/* thanks to Michael Dwyer for giving me a dose of clues in
+ * the signal strength department..
+ *
+ * This card has a stereo bit - bit 0 set = mono, not set = stereo
+ * It also has a "signal" bit - bit 1 set = bad signal, not set = good
+ *
+ */
+
+static int az_getsigstr(struct az_device *dev)
+{
+ if (inb(io) & 2) /* bit set = no signal present */
+ return 0;
+ return 1; /* signal present */
+}
+
+static int az_getstereo(struct az_device *dev)
+{
+ if (inb(io) & 1) /* bit set = mono */
+ return 0;
+ return 1; /* stereo */
+}
+
+static int az_setfreq(struct az_device *dev, unsigned long frequency)
+{
+ int i;
+
+ frequency += 171200; /* Add 10.7 MHz IF */
+ frequency /= 800; /* Convert to 50 kHz units */
+
+ mutex_lock(&lock);
+
+ send_0_byte (dev); /* 0: LSB of frequency */
+
+ for (i = 0; i < 13; i++) /* : frequency bits (1-13) */
+ if (frequency & (1 << i))
+ send_1_byte (dev);
+ else
+ send_0_byte (dev);
+
+ send_0_byte (dev); /* 14: test bit - always 0 */
+ send_0_byte (dev); /* 15: test bit - always 0 */
+ send_0_byte (dev); /* 16: band data 0 - always 0 */
+ if (dev->stereo) /* 17: stereo (1 to enable) */
+ send_1_byte (dev);
+ else
+ send_0_byte (dev);
+
+ send_1_byte (dev); /* 18: band data 1 - unknown */
+ send_0_byte (dev); /* 19: time base - always 0 */
+ send_0_byte (dev); /* 20: spacing (0 = 25 kHz) */
+ send_1_byte (dev); /* 21: spacing (1 = 25 kHz) */
+ send_0_byte (dev); /* 22: spacing (0 = 25 kHz) */
+ send_1_byte (dev); /* 23: AM/FM (FM = 1, always) */
+
+ /* latch frequency */
+
+ udelay (radio_wait_time);
+ outb_p(128+64+volconvert(dev->curvol), io);
+
+ mutex_unlock(&lock);
+
+ return 0;
+}
+
+static int az_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct az_device *az = dev->priv;
+
+ switch(cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *v = arg;
+ memset(v,0,sizeof(*v));
+ v->type=VID_TYPE_TUNER;
+ v->channels=1;
+ v->audios=1;
+ strcpy(v->name, "Aztech Radio");
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner *v = arg;
+ if(v->tuner) /* Only 1 tuner */
+ return -EINVAL;
+ v->rangelow=(87*16000);
+ v->rangehigh=(108*16000);
+ v->flags=VIDEO_TUNER_LOW;
+ v->mode=VIDEO_MODE_AUTO;
+ v->signal=0xFFFF*az_getsigstr(az);
+ if(az_getstereo(az))
+ v->flags|=VIDEO_TUNER_STEREO_ON;
+ strcpy(v->name, "FM");
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner *v = arg;
+ if(v->tuner!=0)
+ return -EINVAL;
+ return 0;
+ }
+ case VIDIOCGFREQ:
+ {
+ unsigned long *freq = arg;
+ *freq = az->curfreq;
+ return 0;
+ }
+ case VIDIOCSFREQ:
+ {
+ unsigned long *freq = arg;
+ az->curfreq = *freq;
+ az_setfreq(az, az->curfreq);
+ return 0;
+ }
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio *v = arg;
+ memset(v,0, sizeof(*v));
+ v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
+ if(az->stereo)
+ v->mode=VIDEO_SOUND_STEREO;
+ else
+ v->mode=VIDEO_SOUND_MONO;
+ v->volume=az->curvol;
+ v->step=16384;
+ strcpy(v->name, "Radio");
+ return 0;
+ }
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio *v = arg;
+ if(v->audio)
+ return -EINVAL;
+ az->curvol=v->volume;
+
+ az->stereo=(v->mode&VIDEO_SOUND_STEREO)?1:0;
+ if(v->flags&VIDEO_AUDIO_MUTE)
+ az_setvol(az,0);
+ else
+ az_setvol(az,az->curvol);
+ return 0;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int az_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, az_do_ioctl);
+}
+
+static struct az_device aztech_unit;
+
+static struct file_operations aztech_fops = {
+ .owner = THIS_MODULE,
+ .open = video_exclusive_open,
+ .release = video_exclusive_release,
+ .ioctl = az_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device aztech_radio=
+{
+ .owner = THIS_MODULE,
+ .name = "Aztech radio",
+ .type = VID_TYPE_TUNER,
+ .hardware = VID_HARDWARE_AZTECH,
+ .fops = &aztech_fops,
+};
+
+static int __init aztech_init(void)
+{
+ if(io==-1)
+ {
+ printk(KERN_ERR "You must set an I/O address with io=0x???\n");
+ return -EINVAL;
+ }
+
+ if (!request_region(io, 2, "aztech"))
+ {
+ printk(KERN_ERR "aztech: port 0x%x already in use\n", io);
+ return -EBUSY;
+ }
+
+ mutex_init(&lock);
+ aztech_radio.priv=&aztech_unit;
+
+ if(video_register_device(&aztech_radio, VFL_TYPE_RADIO, radio_nr)==-1)
+ {
+ release_region(io,2);
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n");
+ /* mute card - prevents noisy bootups */
+ outb (0, io);
+ return 0;
+}
+
+MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
+MODULE_DESCRIPTION("A driver for the Aztech radio card.");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0);
+module_param(radio_nr, int, 0);
+MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)");
+
+static void __exit aztech_cleanup(void)
+{
+ video_unregister_device(&aztech_radio);
+ release_region(io,2);
+}
+
+module_init(aztech_init);
+module_exit(aztech_cleanup);
diff --git a/linux/drivers/media/radio/radio-cadet.c b/linux/drivers/media/radio/radio-cadet.c
new file mode 100644
index 000000000..559be5d00
--- /dev/null
+++ b/linux/drivers/media/radio/radio-cadet.c
@@ -0,0 +1,622 @@
+/* radio-cadet.c - A video4linux driver for the ADS Cadet AM/FM Radio Card
+ *
+ * by Fred Gleason <fredg@wava.com>
+ * Version 0.3.3
+ *
+ * (Loosely) based on code for the Aztech radio card by
+ *
+ * Russell Kroll (rkroll@exploits.org)
+ * Quay Ly
+ * Donald Song
+ * Jason Lewis (jlewis@twilight.vtc.vsc.edu)
+ * Scott McGrath (smcgrath@twilight.vtc.vsc.edu)
+ * William McGrath (wmcgrath@twilight.vtc.vsc.edu)
+ *
+ * History:
+ * 2000-04-29 Russell Kroll <rkroll@exploits.org>
+ * Added ISAPnP detection for Linux 2.3/2.4
+ *
+ * 2001-01-10 Russell Kroll <rkroll@exploits.org>
+ * Removed dead CONFIG_RADIO_CADET_PORT code
+ * PnP detection on load is now default (no args necessary)
+ *
+ * 2002-01-17 Adam Belay <ambx1@neo.rr.com>
+ * Updated to latest pnp code
+ *
+ * 2003-01-31 Alan Cox <alan@redhat.com>
+ * Cleaned up locking, delay code, general odds and ends
+ */
+
+#include <linux/module.h> /* Modules */
+#include <linux/init.h> /* Initdata */
+#include <linux/ioport.h> /* request_region */
+#include <linux/delay.h> /* udelay */
+#include <asm/io.h> /* outb, outb_p */
+#include <asm/uaccess.h> /* copy to/from user */
+#include "compat.h"
+#include <linux/videodev.h> /* kernel radio structs */
+#include <linux/param.h>
+#include <linux/pnp.h>
+
+#define RDS_BUFFER 256
+
+static int io=-1; /* default to isapnp activation */
+static int radio_nr = -1;
+static int users=0;
+static int curtuner=0;
+static int tunestat=0;
+static int sigstrength=0;
+static wait_queue_head_t read_queue;
+static struct timer_list readtimer;
+static __u8 rdsin=0,rdsout=0,rdsstat=0;
+static unsigned char rdsbuf[RDS_BUFFER];
+static spinlock_t cadet_io_lock;
+
+static int cadet_probe(void);
+
+/*
+ * Signal Strength Threshold Values
+ * The V4L API spec does not define any particular unit for the signal
+ * strength value. These values are in microvolts of RF at the tuner's input.
+ */
+static __u16 sigtable[2][4]={{5,10,30,150},{28,40,63,1000}};
+
+static int cadet_getrds(void)
+{
+ int rdsstat=0;
+
+ spin_lock(&cadet_io_lock);
+ outb(3,io); /* Select Decoder Control/Status */
+ outb(inb(io+1)&0x7f,io+1); /* Reset RDS detection */
+ spin_unlock(&cadet_io_lock);
+
+ msleep(100);
+
+ spin_lock(&cadet_io_lock);
+ outb(3,io); /* Select Decoder Control/Status */
+ if((inb(io+1)&0x80)!=0) {
+ rdsstat|=VIDEO_TUNER_RDS_ON;
+ }
+ if((inb(io+1)&0x10)!=0) {
+ rdsstat|=VIDEO_TUNER_MBS_ON;
+ }
+ spin_unlock(&cadet_io_lock);
+ return rdsstat;
+}
+
+static int cadet_getstereo(void)
+{
+ int ret = 0;
+ if(curtuner != 0) /* Only FM has stereo capability! */
+ return 0;
+
+ spin_lock(&cadet_io_lock);
+ outb(7,io); /* Select tuner control */
+ if( (inb(io+1) & 0x40) == 0)
+ ret = 1;
+ spin_unlock(&cadet_io_lock);
+ return ret;
+}
+
+static unsigned cadet_gettune(void)
+{
+ int curvol,i;
+ unsigned fifo=0;
+
+ /*
+ * Prepare for read
+ */
+
+ spin_lock(&cadet_io_lock);
+
+ outb(7,io); /* Select tuner control */
+ curvol=inb(io+1); /* Save current volume/mute setting */
+ outb(0x00,io+1); /* Ensure WRITE-ENABLE is LOW */
+ tunestat=0xffff;
+
+ /*
+ * Read the shift register
+ */
+ for(i=0;i<25;i++) {
+ fifo=(fifo<<1)|((inb(io+1)>>7)&0x01);
+ if(i<24) {
+ outb(0x01,io+1);
+ tunestat&=inb(io+1);
+ outb(0x00,io+1);
+ }
+ }
+
+ /*
+ * Restore volume/mute setting
+ */
+ outb(curvol,io+1);
+ spin_unlock(&cadet_io_lock);
+
+ return fifo;
+}
+
+static unsigned cadet_getfreq(void)
+{
+ int i;
+ unsigned freq=0,test,fifo=0;
+
+ /*
+ * Read current tuning
+ */
+ fifo=cadet_gettune();
+
+ /*
+ * Convert to actual frequency
+ */
+ if(curtuner==0) { /* FM */
+ test=12500;
+ for(i=0;i<14;i++) {
+ if((fifo&0x01)!=0) {
+ freq+=test;
+ }
+ test=test<<1;
+ fifo=fifo>>1;
+ }
+ freq-=10700000; /* IF frequency is 10.7 MHz */
+ freq=(freq*16)/1000000; /* Make it 1/16 MHz */
+ }
+ if(curtuner==1) { /* AM */
+ freq=((fifo&0x7fff)-2010)*16;
+ }
+
+ return freq;
+}
+
+static void cadet_settune(unsigned fifo)
+{
+ int i;
+ unsigned test;
+
+ spin_lock(&cadet_io_lock);
+
+ outb(7,io); /* Select tuner control */
+ /*
+ * Write the shift register
+ */
+ test=0;
+ test=(fifo>>23)&0x02; /* Align data for SDO */
+ test|=0x1c; /* SDM=1, SWE=1, SEN=1, SCK=0 */
+ outb(7,io); /* Select tuner control */
+ outb(test,io+1); /* Initialize for write */
+ for(i=0;i<25;i++) {
+ test|=0x01; /* Toggle SCK High */
+ outb(test,io+1);
+ test&=0xfe; /* Toggle SCK Low */
+ outb(test,io+1);
+ fifo=fifo<<1; /* Prepare the next bit */
+ test=0x1c|((fifo>>23)&0x02);
+ outb(test,io+1);
+ }
+ spin_unlock(&cadet_io_lock);
+}
+
+static void cadet_setfreq(unsigned freq)
+{
+ unsigned fifo;
+ int i,j,test;
+ int curvol;
+
+ /*
+ * Formulate a fifo command
+ */
+ fifo=0;
+ if(curtuner==0) { /* FM */
+ test=102400;
+ freq=(freq*1000)/16; /* Make it kHz */
+ freq+=10700; /* IF is 10700 kHz */
+ for(i=0;i<14;i++) {
+ fifo=fifo<<1;
+ if(freq>=test) {
+ fifo|=0x01;
+ freq-=test;
+ }
+ test=test>>1;
+ }
+ }
+ if(curtuner==1) { /* AM */
+ fifo=(freq/16)+2010; /* Make it kHz */
+ fifo|=0x100000; /* Select AM Band */
+ }
+
+ /*
+ * Save current volume/mute setting
+ */
+
+ spin_lock(&cadet_io_lock);
+ outb(7,io); /* Select tuner control */
+ curvol=inb(io+1);
+ spin_unlock(&cadet_io_lock);
+
+ /*
+ * Tune the card
+ */
+ for(j=3;j>-1;j--) {
+ cadet_settune(fifo|(j<<16));
+
+ spin_lock(&cadet_io_lock);
+ outb(7,io); /* Select tuner control */
+ outb(curvol,io+1);
+ spin_unlock(&cadet_io_lock);
+
+ msleep(100);
+
+ cadet_gettune();
+ if((tunestat & 0x40) == 0) { /* Tuned */
+ sigstrength=sigtable[curtuner][j];
+ return;
+ }
+ }
+ sigstrength=0;
+}
+
+
+static int cadet_getvol(void)
+{
+ int ret = 0;
+
+ spin_lock(&cadet_io_lock);
+
+ outb(7,io); /* Select tuner control */
+ if((inb(io + 1) & 0x20) != 0)
+ ret = 0xffff;
+
+ spin_unlock(&cadet_io_lock);
+ return ret;
+}
+
+
+static void cadet_setvol(int vol)
+{
+ spin_lock(&cadet_io_lock);
+ outb(7,io); /* Select tuner control */
+ if(vol>0)
+ outb(0x20,io+1);
+ else
+ outb(0x00,io+1);
+ spin_unlock(&cadet_io_lock);
+}
+
+static void cadet_handler(unsigned long data)
+{
+ /*
+ * Service the RDS fifo
+ */
+
+ if(spin_trylock(&cadet_io_lock))
+ {
+ outb(0x3,io); /* Select RDS Decoder Control */
+ if((inb(io+1)&0x20)!=0) {
+ printk(KERN_CRIT "cadet: RDS fifo overflow\n");
+ }
+ outb(0x80,io); /* Select RDS fifo */
+ while((inb(io)&0x80)!=0) {
+ rdsbuf[rdsin]=inb(io+1);
+ if(rdsin==rdsout)
+ printk(KERN_WARNING "cadet: RDS buffer overflow\n");
+ else
+ rdsin++;
+ }
+ spin_unlock(&cadet_io_lock);
+ }
+
+ /*
+ * Service pending read
+ */
+ if( rdsin!=rdsout)
+ wake_up_interruptible(&read_queue);
+
+ /*
+ * Clean up and exit
+ */
+ init_timer(&readtimer);
+ readtimer.function=cadet_handler;
+ readtimer.data=(unsigned long)0;
+ readtimer.expires=jiffies+(HZ/20);
+ add_timer(&readtimer);
+}
+
+
+
+static ssize_t cadet_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ int i=0;
+ unsigned char readbuf[RDS_BUFFER];
+
+ if(rdsstat==0) {
+ spin_lock(&cadet_io_lock);
+ rdsstat=1;
+ outb(0x80,io); /* Select RDS fifo */
+ spin_unlock(&cadet_io_lock);
+ init_timer(&readtimer);
+ readtimer.function=cadet_handler;
+ readtimer.data=(unsigned long)0;
+ readtimer.expires=jiffies+(HZ/20);
+ add_timer(&readtimer);
+ }
+ if(rdsin==rdsout) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EWOULDBLOCK;
+ interruptible_sleep_on(&read_queue);
+ }
+ while( i<count && rdsin!=rdsout)
+ readbuf[i++]=rdsbuf[rdsout++];
+
+ if (copy_to_user(data,readbuf,i))
+ return -EFAULT;
+ return i;
+}
+
+
+
+static int cadet_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ switch(cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *v = arg;
+ memset(v,0,sizeof(*v));
+ v->type=VID_TYPE_TUNER;
+ v->channels=2;
+ v->audios=1;
+ strcpy(v->name, "ADS Cadet");
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner *v = arg;
+ if((v->tuner<0)||(v->tuner>1)) {
+ return -EINVAL;
+ }
+ switch(v->tuner) {
+ case 0:
+ strcpy(v->name,"FM");
+ v->rangelow=1400; /* 87.5 MHz */
+ v->rangehigh=1728; /* 108.0 MHz */
+ v->flags=0;
+ v->mode=0;
+ v->mode|=VIDEO_MODE_AUTO;
+ v->signal=sigstrength;
+ if(cadet_getstereo()==1) {
+ v->flags|=VIDEO_TUNER_STEREO_ON;
+ }
+ v->flags|=cadet_getrds();
+ break;
+ case 1:
+ strcpy(v->name,"AM");
+ v->rangelow=8320; /* 520 kHz */
+ v->rangehigh=26400; /* 1650 kHz */
+ v->flags=0;
+ v->flags|=VIDEO_TUNER_LOW;
+ v->mode=0;
+ v->mode|=VIDEO_MODE_AUTO;
+ v->signal=sigstrength;
+ break;
+ }
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner *v = arg;
+ if((v->tuner<0)||(v->tuner>1)) {
+ return -EINVAL;
+ }
+ curtuner=v->tuner;
+ return 0;
+ }
+ case VIDIOCGFREQ:
+ {
+ unsigned long *freq = arg;
+ *freq = cadet_getfreq();
+ return 0;
+ }
+ case VIDIOCSFREQ:
+ {
+ unsigned long *freq = arg;
+ if((curtuner==0)&&((*freq<1400)||(*freq>1728))) {
+ return -EINVAL;
+ }
+ if((curtuner==1)&&((*freq<8320)||(*freq>26400))) {
+ return -EINVAL;
+ }
+ cadet_setfreq(*freq);
+ return 0;
+ }
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio *v = arg;
+ memset(v,0, sizeof(*v));
+ v->flags=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
+ if(cadet_getstereo()==0) {
+ v->mode=VIDEO_SOUND_MONO;
+ } else {
+ v->mode=VIDEO_SOUND_STEREO;
+ }
+ v->volume=cadet_getvol();
+ v->step=0xffff;
+ strcpy(v->name, "Radio");
+ return 0;
+ }
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio *v = arg;
+ if(v->audio)
+ return -EINVAL;
+ cadet_setvol(v->volume);
+ if(v->flags&VIDEO_AUDIO_MUTE)
+ cadet_setvol(0);
+ else
+ cadet_setvol(0xffff);
+ return 0;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int cadet_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, cadet_do_ioctl);
+}
+
+static int cadet_open(struct inode *inode, struct file *file)
+{
+ if(users)
+ return -EBUSY;
+ users++;
+ init_waitqueue_head(&read_queue);
+ return 0;
+}
+
+static int cadet_release(struct inode *inode, struct file *file)
+{
+ del_timer_sync(&readtimer);
+ rdsstat=0;
+ users--;
+ return 0;
+}
+
+
+static struct file_operations cadet_fops = {
+ .owner = THIS_MODULE,
+ .open = cadet_open,
+ .release = cadet_release,
+ .read = cadet_read,
+ .ioctl = cadet_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device cadet_radio=
+{
+ .owner = THIS_MODULE,
+ .name = "Cadet radio",
+ .type = VID_TYPE_TUNER,
+ .hardware = VID_HARDWARE_CADET,
+ .fops = &cadet_fops,
+};
+
+static struct pnp_device_id cadet_pnp_devices[] = {
+ /* ADS Cadet AM/FM Radio Card */
+ {.id = "MSM0c24", .driver_data = 0},
+ {.id = ""}
+};
+
+MODULE_DEVICE_TABLE(pnp, cadet_pnp_devices);
+
+static int cadet_pnp_probe(struct pnp_dev * dev, const struct pnp_device_id *dev_id)
+{
+ if (!dev)
+ return -ENODEV;
+ /* only support one device */
+ if (io > 0)
+ return -EBUSY;
+
+ if (!pnp_port_valid(dev, 0)) {
+ return -ENODEV;
+ }
+
+ io = pnp_port_start(dev, 0);
+
+ printk ("radio-cadet: PnP reports device at %#x\n", io);
+
+ return io;
+}
+
+static struct pnp_driver cadet_pnp_driver = {
+ .name = "radio-cadet",
+ .id_table = cadet_pnp_devices,
+ .probe = cadet_pnp_probe,
+ .remove = NULL,
+};
+
+static int cadet_probe(void)
+{
+ static int iovals[8]={0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e};
+ int i;
+
+ for(i=0;i<8;i++) {
+ io=iovals[i];
+ if (request_region(io, 2, "cadet-probe")) {
+ cadet_setfreq(1410);
+ if(cadet_getfreq()==1410) {
+ release_region(io, 2);
+ return io;
+ }
+ release_region(io, 2);
+ }
+ }
+ return -1;
+}
+
+/*
+ * io should only be set if the user has used something like
+ * isapnp (the userspace program) to initialize this card for us
+ */
+
+static int __init cadet_init(void)
+{
+ spin_lock_init(&cadet_io_lock);
+
+ /*
+ * If a probe was requested then probe ISAPnP first (safest)
+ */
+ if (io < 0)
+ pnp_register_driver(&cadet_pnp_driver);
+ /*
+ * If that fails then probe unsafely if probe is requested
+ */
+ if(io < 0)
+ io = cadet_probe ();
+
+ /*
+ * Else we bail out
+ */
+
+ if(io < 0) {
+#ifdef MODULE
+ printk(KERN_ERR "You must set an I/O address with io=0x???\n");
+#endif
+ goto fail;
+ }
+ if (!request_region(io,2,"cadet"))
+ goto fail;
+ if(video_register_device(&cadet_radio,VFL_TYPE_RADIO,radio_nr)==-1) {
+ release_region(io,2);
+ goto fail;
+ }
+ printk(KERN_INFO "ADS Cadet Radio Card at 0x%x\n",io);
+ return 0;
+fail:
+ pnp_unregister_driver(&cadet_pnp_driver);
+ return -1;
+}
+
+
+
+MODULE_AUTHOR("Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
+MODULE_DESCRIPTION("A driver for the ADS Cadet AM/FM/RDS radio card.");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "I/O address of Cadet card (0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e)");
+module_param(radio_nr, int, 0);
+
+static void __exit cadet_cleanup_module(void)
+{
+ video_unregister_device(&cadet_radio);
+ release_region(io,2);
+ pnp_unregister_driver(&cadet_pnp_driver);
+}
+
+module_init(cadet_init);
+module_exit(cadet_cleanup_module);
+
diff --git a/linux/drivers/media/radio/radio-gemtek-pci.c b/linux/drivers/media/radio/radio-gemtek-pci.c
new file mode 100644
index 000000000..c8777e953
--- /dev/null
+++ b/linux/drivers/media/radio/radio-gemtek-pci.c
@@ -0,0 +1,417 @@
+/*
+ ***************************************************************************
+ *
+ * radio-gemtek-pci.c - Gemtek PCI Radio driver
+ * (C) 2001 Vladimir Shebordaev <vshebordaev@mail.ru>
+ *
+ ***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+ * USA.
+ *
+ ***************************************************************************
+ *
+ * Gemtek Corp still silently refuses to release any specifications
+ * of their multimedia devices, so the protocol still has to be
+ * reverse engineered.
+ *
+ * The v4l code was inspired by Jonas Munsin's Gemtek serial line
+ * radio device driver.
+ *
+ * Please, let me know if this piece of code was useful :)
+ *
+ * TODO: multiple device support and portability were not tested
+ *
+ ***************************************************************************
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include "compat.h"
+#include <linux/videodev.h>
+#include <linux/errno.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#ifndef PCI_VENDOR_ID_GEMTEK
+#define PCI_VENDOR_ID_GEMTEK 0x5046
+#endif
+
+#ifndef PCI_DEVICE_ID_GEMTEK_PR103
+#define PCI_DEVICE_ID_GEMTEK_PR103 0x1001
+#endif
+
+#ifndef GEMTEK_PCI_RANGE_LOW
+#define GEMTEK_PCI_RANGE_LOW (87*16000)
+#endif
+
+#ifndef GEMTEK_PCI_RANGE_HIGH
+#define GEMTEK_PCI_RANGE_HIGH (108*16000)
+#endif
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+struct gemtek_pci_card {
+ struct video_device *videodev;
+
+ u32 iobase;
+ u32 length;
+ u8 chiprev;
+ u16 model;
+
+ u32 current_frequency;
+ u8 mute;
+};
+
+static const char rcsid[] = "$Id: radio-gemtek-pci.c,v 1.1 2001/07/23 08:08:16 ted Exp ted $";
+
+static int nr_radio = -1;
+
+static inline u8 gemtek_pci_out( u16 value, u32 port )
+{
+ outw( value, port );
+
+ return (u8)value;
+}
+
+#define _b0( v ) *((u8 *)&v)
+static void __gemtek_pci_cmd( u16 value, u32 port, u8 *last_byte, int keep )
+{
+ register u8 byte = *last_byte;
+
+ if ( !value ) {
+ if ( !keep )
+ value = (u16)port;
+ byte &= 0xfd;
+ } else
+ byte |= 2;
+
+ _b0( value ) = byte;
+ outw( value, port );
+ byte |= 1;
+ _b0( value ) = byte;
+ outw( value, port );
+ byte &= 0xfe;
+ _b0( value ) = byte;
+ outw( value, port );
+
+ *last_byte = byte;
+}
+
+static inline void gemtek_pci_nil( u32 port, u8 *last_byte )
+{
+ __gemtek_pci_cmd( 0x00, port, last_byte, FALSE );
+}
+
+static inline void gemtek_pci_cmd( u16 cmd, u32 port, u8 *last_byte )
+{
+ __gemtek_pci_cmd( cmd, port, last_byte, TRUE );
+}
+
+static void gemtek_pci_setfrequency( struct gemtek_pci_card *card, unsigned long frequency )
+{
+ register int i;
+ register u32 value = frequency / 200 + 856;
+ register u16 mask = 0x8000;
+ u8 last_byte;
+ u32 port = card->iobase;
+
+ last_byte = gemtek_pci_out( 0x06, port );
+
+ i = 0;
+ do {
+ gemtek_pci_nil( port, &last_byte );
+ i++;
+ } while ( i < 9 );
+
+ i = 0;
+ do {
+ gemtek_pci_cmd( value & mask, port, &last_byte );
+ mask >>= 1;
+ i++;
+ } while ( i < 16 );
+
+ outw( 0x10, port );
+}
+
+
+static inline void gemtek_pci_mute( struct gemtek_pci_card *card )
+{
+ outb( 0x1f, card->iobase );
+ card->mute = TRUE;
+}
+
+static inline void gemtek_pci_unmute( struct gemtek_pci_card *card )
+{
+ if ( card->mute ) {
+ gemtek_pci_setfrequency( card, card->current_frequency );
+ card->mute = FALSE;
+ }
+}
+
+static inline unsigned int gemtek_pci_getsignal( struct gemtek_pci_card *card )
+{
+ return ( inb( card->iobase ) & 0x08 ) ? 0 : 1;
+}
+
+static int gemtek_pci_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct gemtek_pci_card *card = dev->priv;
+
+ switch ( cmd ) {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *c = arg;
+
+ memset(c,0,sizeof(*c));
+ c->type = VID_TYPE_TUNER;
+ c->channels = 1;
+ c->audios = 1;
+ strcpy( c->name, "Gemtek PCI Radio" );
+ return 0;
+ }
+
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner *t = arg;
+
+ if ( t->tuner )
+ return -EINVAL;
+
+ t->rangelow = GEMTEK_PCI_RANGE_LOW;
+ t->rangehigh = GEMTEK_PCI_RANGE_HIGH;
+ t->flags = VIDEO_TUNER_LOW;
+ t->mode = VIDEO_MODE_AUTO;
+ t->signal = 0xFFFF * gemtek_pci_getsignal( card );
+ strcpy( t->name, "FM" );
+ return 0;
+ }
+
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner *t = arg;
+ if ( t->tuner )
+ return -EINVAL;
+ return 0;
+ }
+
+ case VIDIOCGFREQ:
+ {
+ unsigned long *freq = arg;
+ *freq = card->current_frequency;
+ return 0;
+ }
+ case VIDIOCSFREQ:
+ {
+ unsigned long *freq = arg;
+
+ if ( (*freq < GEMTEK_PCI_RANGE_LOW) ||
+ (*freq > GEMTEK_PCI_RANGE_HIGH) )
+ return -EINVAL;
+
+ gemtek_pci_setfrequency( card, *freq );
+ card->current_frequency = *freq;
+ card->mute = FALSE;
+
+ return 0;
+ }
+
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio *a = arg;
+
+ memset( a, 0, sizeof( *a ) );
+ a->flags |= VIDEO_AUDIO_MUTABLE;
+ a->volume = 1;
+ a->step = 65535;
+ strcpy( a->name, "Radio" );
+ return 0;
+ }
+
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio *a = arg;
+
+ if ( a->audio )
+ return -EINVAL;
+
+ if ( a->flags & VIDEO_AUDIO_MUTE )
+ gemtek_pci_mute( card );
+ else
+ gemtek_pci_unmute( card );
+ return 0;
+ }
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int gemtek_pci_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, gemtek_pci_do_ioctl);
+}
+
+enum {
+ GEMTEK_PR103
+};
+
+static char *card_names[] __devinitdata = {
+ "GEMTEK_PR103"
+};
+
+static struct pci_device_id gemtek_pci_id[] =
+{
+ { PCI_VENDOR_ID_GEMTEK, PCI_DEVICE_ID_GEMTEK_PR103,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, GEMTEK_PR103 },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE( pci, gemtek_pci_id );
+
+static int mx = 1;
+
+static struct file_operations gemtek_pci_fops = {
+ .owner = THIS_MODULE,
+ .open = video_exclusive_open,
+ .release = video_exclusive_release,
+ .ioctl = gemtek_pci_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device vdev_template = {
+ .owner = THIS_MODULE,
+ .name = "Gemtek PCI Radio",
+ .type = VID_TYPE_TUNER,
+ .hardware = VID_HARDWARE_GEMTEK,
+ .fops = &gemtek_pci_fops,
+};
+
+static int __devinit gemtek_pci_probe( struct pci_dev *pci_dev, const struct pci_device_id *pci_id )
+{
+ struct gemtek_pci_card *card;
+ struct video_device *devradio;
+
+ if ( (card = kzalloc( sizeof( struct gemtek_pci_card ), GFP_KERNEL )) == NULL ) {
+ printk( KERN_ERR "gemtek_pci: out of memory\n" );
+ return -ENOMEM;
+ }
+
+ if ( pci_enable_device( pci_dev ) )
+ goto err_pci;
+
+ card->iobase = pci_resource_start( pci_dev, 0 );
+ card->length = pci_resource_len( pci_dev, 0 );
+
+ if ( request_region( card->iobase, card->length, card_names[pci_id->driver_data] ) == NULL ) {
+ printk( KERN_ERR "gemtek_pci: i/o port already in use\n" );
+ goto err_pci;
+ }
+
+ pci_read_config_byte( pci_dev, PCI_REVISION_ID, &card->chiprev );
+ pci_read_config_word( pci_dev, PCI_SUBSYSTEM_ID, &card->model );
+
+ pci_set_drvdata( pci_dev, card );
+
+ if ( (devradio = kmalloc( sizeof( struct video_device ), GFP_KERNEL )) == NULL ) {
+ printk( KERN_ERR "gemtek_pci: out of memory\n" );
+ goto err_video;
+ }
+ *devradio = vdev_template;
+
+ if ( video_register_device( devradio, VFL_TYPE_RADIO , nr_radio) == -1 ) {
+ kfree( devradio );
+ goto err_video;
+ }
+
+ card->videodev = devradio;
+ devradio->priv = card;
+ gemtek_pci_mute( card );
+
+ printk( KERN_INFO "Gemtek PCI Radio (rev. %d) found at 0x%04x-0x%04x.\n",
+ card->chiprev, card->iobase, card->iobase + card->length - 1 );
+
+ return 0;
+
+err_video:
+ release_region( card->iobase, card->length );
+
+err_pci:
+ kfree( card );
+ return -ENODEV;
+}
+
+static void __devexit gemtek_pci_remove( struct pci_dev *pci_dev )
+{
+ struct gemtek_pci_card *card = pci_get_drvdata( pci_dev );
+
+ video_unregister_device( card->videodev );
+ kfree( card->videodev );
+
+ release_region( card->iobase, card->length );
+
+ if ( mx )
+ gemtek_pci_mute( card );
+
+ kfree( card );
+
+ pci_set_drvdata( pci_dev, NULL );
+}
+
+static struct pci_driver gemtek_pci_driver =
+{
+ .name = "gemtek_pci",
+ .id_table = gemtek_pci_id,
+ .probe = gemtek_pci_probe,
+ .remove = __devexit_p(gemtek_pci_remove),
+};
+
+static int __init gemtek_pci_init_module( void )
+{
+ return pci_register_driver( &gemtek_pci_driver );
+}
+
+static void __exit gemtek_pci_cleanup_module( void )
+{
+ return pci_unregister_driver( &gemtek_pci_driver );
+}
+
+MODULE_AUTHOR( "Vladimir Shebordaev <vshebordaev@mail.ru>" );
+MODULE_DESCRIPTION( "The video4linux driver for the Gemtek PCI Radio Card" );
+MODULE_LICENSE("GPL");
+
+module_param(mx, bool, 0);
+MODULE_PARM_DESC( mx, "single digit: 1 - turn off the turner upon module exit (default), 0 - do not" );
+module_param(nr_radio, int, 0);
+MODULE_PARM_DESC( nr_radio, "video4linux device number to use");
+
+module_init( gemtek_pci_init_module );
+module_exit( gemtek_pci_cleanup_module );
+
diff --git a/linux/drivers/media/radio/radio-gemtek.c b/linux/drivers/media/radio/radio-gemtek.c
new file mode 100644
index 000000000..199f2df7a
--- /dev/null
+++ b/linux/drivers/media/radio/radio-gemtek.c
@@ -0,0 +1,306 @@
+/* GemTek radio card driver for Linux (C) 1998 Jonas Munsin <jmunsin@iki.fi>
+ *
+ * GemTek hasn't released any specs on the card, so the protocol had to
+ * be reverse engineered with dosemu.
+ *
+ * Besides the protocol changes, this is mostly a copy of:
+ *
+ * RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff
+ *
+ * Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood
+ * Converted to new API by Alan Cox <Alan.Cox@linux.org>
+ * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
+ *
+ * TODO: Allow for more than one of these foolish entities :-)
+ *
+ */
+
+#include <linux/module.h> /* Modules */
+#include <linux/init.h> /* Initdata */
+#include <linux/ioport.h> /* request_region */
+#include <linux/delay.h> /* udelay */
+#include <asm/io.h> /* outb, outb_p */
+#include <asm/uaccess.h> /* copy to/from user */
+#include "compat.h"
+#include <linux/videodev.h> /* kernel radio structs */
+#include <linux/config.h> /* CONFIG_RADIO_GEMTEK_PORT */
+#include <linux/spinlock.h>
+
+#ifndef CONFIG_RADIO_GEMTEK_PORT
+#define CONFIG_RADIO_GEMTEK_PORT -1
+#endif
+
+static int io = CONFIG_RADIO_GEMTEK_PORT;
+static int radio_nr = -1;
+static spinlock_t lock;
+
+struct gemtek_device
+{
+ int port;
+ unsigned long curfreq;
+ int muted;
+};
+
+
+/* local things */
+
+/* the correct way to mute the gemtek may be to write the last written
+ * frequency || 0x10, but just writing 0x10 once seems to do it as well
+ */
+static void gemtek_mute(struct gemtek_device *dev)
+{
+ if(dev->muted)
+ return;
+ spin_lock(&lock);
+ outb(0x10, io);
+ spin_unlock(&lock);
+ dev->muted = 1;
+}
+
+static void gemtek_unmute(struct gemtek_device *dev)
+{
+ if(dev->muted == 0)
+ return;
+ spin_lock(&lock);
+ outb(0x20, io);
+ spin_unlock(&lock);
+ dev->muted = 0;
+}
+
+static void zero(void)
+{
+ outb_p(0x04, io);
+ udelay(5);
+ outb_p(0x05, io);
+ udelay(5);
+}
+
+static void one(void)
+{
+ outb_p(0x06, io);
+ udelay(5);
+ outb_p(0x07, io);
+ udelay(5);
+}
+
+static int gemtek_setfreq(struct gemtek_device *dev, unsigned long freq)
+{
+ int i;
+
+/* freq = 78.25*((float)freq/16000.0 + 10.52); */
+
+ freq /= 16;
+ freq += 10520;
+ freq *= 7825;
+ freq /= 100000;
+
+ spin_lock(&lock);
+
+ /* 2 start bits */
+ outb_p(0x03, io);
+ udelay(5);
+ outb_p(0x07, io);
+ udelay(5);
+
+ /* 28 frequency bits (lsb first) */
+ for (i = 0; i < 14; i++)
+ if (freq & (1 << i))
+ one();
+ else
+ zero();
+ /* 36 unknown bits */
+ for (i = 0; i < 11; i++)
+ zero();
+ one();
+ for (i = 0; i < 4; i++)
+ zero();
+ one();
+ zero();
+
+ /* 2 end bits */
+ outb_p(0x03, io);
+ udelay(5);
+ outb_p(0x07, io);
+ udelay(5);
+
+ spin_unlock(&lock);
+
+ return 0;
+}
+
+static int gemtek_getsigstr(struct gemtek_device *dev)
+{
+ spin_lock(&lock);
+ inb(io);
+ udelay(5);
+ spin_unlock(&lock);
+ if (inb(io) & 8) /* bit set = no signal present */
+ return 0;
+ return 1; /* signal present */
+}
+
+static int gemtek_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct gemtek_device *rt=dev->priv;
+
+ switch(cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *v = arg;
+ memset(v,0,sizeof(*v));
+ v->type=VID_TYPE_TUNER;
+ v->channels=1;
+ v->audios=1;
+ strcpy(v->name, "GemTek");
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner *v = arg;
+ if(v->tuner) /* Only 1 tuner */
+ return -EINVAL;
+ v->rangelow=87*16000;
+ v->rangehigh=108*16000;
+ v->flags=VIDEO_TUNER_LOW;
+ v->mode=VIDEO_MODE_AUTO;
+ v->signal=0xFFFF*gemtek_getsigstr(rt);
+ strcpy(v->name, "FM");
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner *v = arg;
+ if(v->tuner!=0)
+ return -EINVAL;
+ /* Only 1 tuner so no setting needed ! */
+ return 0;
+ }
+ case VIDIOCGFREQ:
+ {
+ unsigned long *freq = arg;
+ *freq = rt->curfreq;
+ return 0;
+ }
+ case VIDIOCSFREQ:
+ {
+ unsigned long *freq = arg;
+ rt->curfreq = *freq;
+ /* needs to be called twice in order for getsigstr to work */
+ gemtek_setfreq(rt, rt->curfreq);
+ gemtek_setfreq(rt, rt->curfreq);
+ return 0;
+ }
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio *v = arg;
+ memset(v,0, sizeof(*v));
+ v->flags|=VIDEO_AUDIO_MUTABLE;
+ v->volume=1;
+ v->step=65535;
+ strcpy(v->name, "Radio");
+ return 0;
+ }
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio *v = arg;
+ if(v->audio)
+ return -EINVAL;
+
+ if(v->flags&VIDEO_AUDIO_MUTE)
+ gemtek_mute(rt);
+ else
+ gemtek_unmute(rt);
+
+ return 0;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int gemtek_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, gemtek_do_ioctl);
+}
+
+static struct gemtek_device gemtek_unit;
+
+static struct file_operations gemtek_fops = {
+ .owner = THIS_MODULE,
+ .open = video_exclusive_open,
+ .release = video_exclusive_release,
+ .ioctl = gemtek_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device gemtek_radio=
+{
+ .owner = THIS_MODULE,
+ .name = "GemTek radio",
+ .type = VID_TYPE_TUNER,
+ .hardware = VID_HARDWARE_GEMTEK,
+ .fops = &gemtek_fops,
+};
+
+static int __init gemtek_init(void)
+{
+ if(io==-1)
+ {
+ printk(KERN_ERR "You must set an I/O address with io=0x20c, io=0x30c, io=0x24c or io=0x34c (io=0x020c or io=0x248 for the combined sound/radiocard)\n");
+ return -EINVAL;
+ }
+
+ if (!request_region(io, 4, "gemtek"))
+ {
+ printk(KERN_ERR "gemtek: port 0x%x already in use\n", io);
+ return -EBUSY;
+ }
+
+ gemtek_radio.priv=&gemtek_unit;
+
+ if(video_register_device(&gemtek_radio, VFL_TYPE_RADIO, radio_nr)==-1)
+ {
+ release_region(io, 4);
+ return -EINVAL;
+ }
+ printk(KERN_INFO "GemTek Radio Card driver.\n");
+
+ spin_lock_init(&lock);
+
+ /* this is _maybe_ unnecessary */
+ outb(0x01, io);
+
+ /* mute card - prevents noisy bootups */
+ gemtek_unit.muted = 0;
+ gemtek_mute(&gemtek_unit);
+
+ return 0;
+}
+
+MODULE_AUTHOR("Jonas Munsin");
+MODULE_DESCRIPTION("A driver for the GemTek Radio Card");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "I/O address of the GemTek card (0x20c, 0x30c, 0x24c or 0x34c (0x20c or 0x248 have been reported to work for the combined sound/radiocard)).");
+module_param(radio_nr, int, 0);
+
+static void __exit gemtek_cleanup(void)
+{
+ video_unregister_device(&gemtek_radio);
+ release_region(io,4);
+}
+
+module_init(gemtek_init);
+module_exit(gemtek_cleanup);
+
+/*
+ Local variables:
+ compile-command: "gcc -c -DMODVERSIONS -D__KERNEL__ -DMODULE -O6 -Wall -Wstrict-prototypes -I /home/blp/tmp/linux-2.1.111-rtrack/include radio-rtrack2.c"
+ End:
+*/
diff --git a/linux/drivers/media/radio/radio-maestro.c b/linux/drivers/media/radio/radio-maestro.c
new file mode 100644
index 000000000..613a84142
--- /dev/null
+++ b/linux/drivers/media/radio/radio-maestro.c
@@ -0,0 +1,383 @@
+/* Maestro PCI sound card radio driver for Linux support
+ * (c) 2000 A. Tlalka, atlka@pg.gda.pl
+ * Notes on the hardware
+ *
+ * + Frequency control is done digitally
+ * + No volume control - only mute/unmute - you have to use Aux line volume
+ * control on Maestro card to set the volume
+ * + Radio status (tuned/not_tuned and stereo/mono) is valid some time after
+ * frequency setting (>100ms) and only when the radio is unmuted.
+ * version 0.02
+ * + io port is automatically detected - only the first radio is used
+ * version 0.03
+ * + thread access locking additions
+ * version 0.04
+ * + code improvements
+ * + VIDEO_TUNER_LOW is permanent
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include "compat.h"
+#include <linux/videodev.h>
+
+
+#define DRIVER_VERSION "0.05"
+
+#define GPIO_DATA 0x60 /* port offset from ESS_IO_BASE */
+
+#define IO_MASK 4 /* mask register offset from GPIO_DATA
+ bits 1=unmask write to given bit */
+#define IO_DIR 8 /* direction register offset from GPIO_DATA
+ bits 0/1=read/write direction */
+
+#define GPIO6 0x0040 /* mask bits for GPIO lines */
+#define GPIO7 0x0080
+#define GPIO8 0x0100
+#define GPIO9 0x0200
+
+#define STR_DATA GPIO6 /* radio TEA5757 pins and GPIO bits */
+#define STR_CLK GPIO7
+#define STR_WREN GPIO8
+#define STR_MOST GPIO9
+
+#define FREQ_LO 50*16000
+#define FREQ_HI 150*16000
+
+#define FREQ_IF 171200 /* 10.7*16000 */
+#define FREQ_STEP 200 /* 12.5*16 */
+
+#define FREQ2BITS(x) ((((unsigned int)(x)+FREQ_IF+(FREQ_STEP<<1))\
+ /(FREQ_STEP<<2))<<2) /* (x==fmhz*16*1000) -> bits */
+
+#define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF)
+
+static int radio_nr = -1;
+module_param(radio_nr, int, 0);
+
+static int radio_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+static int maestro_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
+static void maestro_remove(struct pci_dev *pdev);
+
+static struct pci_device_id maestro_r_pci_tbl[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ESS1968),
+ .class = PCI_CLASS_MULTIMEDIA_AUDIO << 8,
+ .class_mask = 0xffff00 },
+ { PCI_DEVICE(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ESS1978),
+ .class = PCI_CLASS_MULTIMEDIA_AUDIO << 8,
+ .class_mask = 0xffff00 },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, maestro_r_pci_tbl);
+
+static struct pci_driver maestro_r_driver = {
+ .name = "maestro_radio",
+ .id_table = maestro_r_pci_tbl,
+ .probe = maestro_probe,
+ .remove = __devexit_p(maestro_remove),
+};
+
+static struct file_operations maestro_fops = {
+ .owner = THIS_MODULE,
+ .open = video_exclusive_open,
+ .release = video_exclusive_release,
+ .ioctl = radio_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device maestro_radio = {
+ .name = "Maestro radio",
+ .type = VID_TYPE_TUNER,
+ .hardware = VID_HARDWARE_SF16MI,
+ .fops = &maestro_fops,
+};
+
+struct radio_device {
+ u16 io, /* base of Maestro card radio io (GPIO_DATA)*/
+ muted, /* VIDEO_AUDIO_MUTE */
+ stereo, /* VIDEO_TUNER_STEREO_ON */
+ tuned; /* signal strength (0 or 0xffff) */
+ struct mutex lock;
+};
+
+static u32 radio_bits_get(struct radio_device *dev)
+{
+ register u16 io=dev->io, l, rdata;
+ register u32 data=0;
+ u16 omask;
+
+ omask = inw(io + IO_MASK);
+ outw(~(STR_CLK | STR_WREN), io + IO_MASK);
+ outw(0, io);
+ udelay(16);
+
+ for (l=24;l--;) {
+ outw(STR_CLK, io); /* HI state */
+ udelay(2);
+ if(!l)
+ dev->tuned = inw(io) & STR_MOST ? 0 : 0xffff;
+ outw(0, io); /* LO state */
+ udelay(2);
+ data <<= 1; /* shift data */
+ rdata = inw(io);
+ if(!l)
+ dev->stereo = rdata & STR_MOST ?
+ 0 : VIDEO_TUNER_STEREO_ON;
+ else
+ if(rdata & STR_DATA)
+ data++;
+ udelay(2);
+ }
+
+ if(dev->muted)
+ outw(STR_WREN, io);
+
+ udelay(4);
+ outw(omask, io + IO_MASK);
+
+ return data & 0x3ffe;
+}
+
+static void radio_bits_set(struct radio_device *dev, u32 data)
+{
+ register u16 io=dev->io, l, bits;
+ u16 omask, odir;
+
+ omask = inw(io + IO_MASK);
+ odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN);
+ outw(odir | STR_DATA, io + IO_DIR);
+ outw(~(STR_DATA | STR_CLK | STR_WREN), io + IO_MASK);
+ udelay(16);
+ for (l=25;l;l--) {
+ bits = ((data >> 18) & STR_DATA) | STR_WREN ;
+ data <<= 1; /* shift data */
+ outw(bits, io); /* start strobe */
+ udelay(2);
+ outw(bits | STR_CLK, io); /* HI level */
+ udelay(2);
+ outw(bits, io); /* LO level */
+ udelay(4);
+ }
+
+ if(!dev->muted)
+ outw(0, io);
+
+ udelay(4);
+ outw(omask, io + IO_MASK);
+ outw(odir, io + IO_DIR);
+ msleep(125);
+}
+
+static inline int radio_function(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct radio_device *card = video_get_drvdata(dev);
+
+ switch (cmd) {
+ case VIDIOCGCAP: {
+ struct video_capability *v = arg;
+ memset(v, 0, sizeof(*v));
+ strcpy(v->name, "Maestro radio");
+ v->type = VID_TYPE_TUNER;
+ v->channels = v->audios = 1;
+ return 0;
+ } case VIDIOCGTUNER: {
+ struct video_tuner *v = arg;
+ if (v->tuner)
+ return -EINVAL;
+ (void)radio_bits_get(card);
+ v->flags = VIDEO_TUNER_LOW | card->stereo;
+ v->signal = card->tuned;
+ strcpy(v->name, "FM");
+ v->rangelow = FREQ_LO;
+ v->rangehigh = FREQ_HI;
+ v->mode = VIDEO_MODE_AUTO;
+ return 0;
+ } case VIDIOCSTUNER: {
+ struct video_tuner *v = arg;
+ if (v->tuner != 0)
+ return -EINVAL;
+ return 0;
+ } case VIDIOCGFREQ: {
+ unsigned long *freq = arg;
+ *freq = BITS2FREQ(radio_bits_get(card));
+ return 0;
+ } case VIDIOCSFREQ: {
+ unsigned long *freq = arg;
+ if (*freq < FREQ_LO || *freq > FREQ_HI)
+ return -EINVAL;
+ radio_bits_set(card, FREQ2BITS(*freq));
+ return 0;
+ } case VIDIOCGAUDIO: {
+ struct video_audio *v = arg;
+ memset(v, 0, sizeof(*v));
+ strcpy(v->name, "Radio");
+ v->flags = VIDEO_AUDIO_MUTABLE | card->muted;
+ v->mode = VIDEO_SOUND_STEREO;
+ return 0;
+ } case VIDIOCSAUDIO: {
+ struct video_audio *v = arg;
+ if (v->audio)
+ return -EINVAL;
+ {
+ register u16 io = card->io;
+ register u16 omask = inw(io + IO_MASK);
+ outw(~STR_WREN, io + IO_MASK);
+ outw((card->muted = v->flags & VIDEO_AUDIO_MUTE) ?
+ STR_WREN : 0, io);
+ udelay(4);
+ outw(omask, io + IO_MASK);
+ msleep(125);
+ return 0;
+ }
+ } case VIDIOCGUNIT: {
+ struct video_unit *v = arg;
+ v->video = VIDEO_NO_UNIT;
+ v->vbi = VIDEO_NO_UNIT;
+ v->radio = dev->minor;
+ v->audio = 0;
+ v->teletext = VIDEO_NO_UNIT;
+ return 0;
+ } default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int radio_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct radio_device *card = video_get_drvdata(dev);
+ int ret;
+
+ mutex_lock(&card->lock);
+ ret = video_usercopy(inode, file, cmd, arg, radio_function);
+ mutex_unlock(&card->lock);
+
+ return ret;
+}
+
+static u16 __devinit radio_power_on(struct radio_device *dev)
+{
+ register u16 io = dev->io;
+ register u32 ofreq;
+ u16 omask, odir;
+
+ omask = inw(io + IO_MASK);
+ odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN);
+ outw(odir & ~STR_WREN, io + IO_DIR);
+ dev->muted = inw(io) & STR_WREN ? 0 : VIDEO_AUDIO_MUTE;
+ outw(odir, io + IO_DIR);
+ outw(~(STR_WREN | STR_CLK), io + IO_MASK);
+ outw(dev->muted ? 0 : STR_WREN, io);
+ udelay(16);
+ outw(omask, io + IO_MASK);
+ ofreq = radio_bits_get(dev);
+
+ if ((ofreq < FREQ2BITS(FREQ_LO)) || (ofreq > FREQ2BITS(FREQ_HI)))
+ ofreq = FREQ2BITS(FREQ_LO);
+ radio_bits_set(dev, ofreq);
+
+ return (ofreq == radio_bits_get(dev));
+}
+
+static int __devinit maestro_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct radio_device *radio_unit;
+ struct video_device *maestro_radio_inst;
+ int retval;
+
+ retval = pci_enable_device(pdev);
+ if (retval) {
+ dev_err(&pdev->dev, "enabling pci device failed!\n");
+ goto err;
+ }
+
+ retval = -ENOMEM;
+
+ radio_unit = kzalloc(sizeof(*radio_unit), GFP_KERNEL);
+ if (radio_unit == NULL) {
+ dev_err(&pdev->dev, "not enough memory\n");
+ goto err;
+ }
+
+ radio_unit->io = pci_resource_start(pdev, 0) + GPIO_DATA;
+ mutex_init(&radio_unit->lock);
+
+ maestro_radio_inst = video_device_alloc();
+ if (maestro_radio_inst == NULL) {
+ dev_err(&pdev->dev, "not enough memory\n");
+ goto errfr;
+ }
+
+ memcpy(maestro_radio_inst, &maestro_radio, sizeof(maestro_radio));
+ video_set_drvdata(maestro_radio_inst, radio_unit);
+ pci_set_drvdata(pdev, maestro_radio_inst);
+
+ retval = video_register_device(maestro_radio_inst, VFL_TYPE_RADIO,
+ radio_nr);
+ if (retval) {
+ printk(KERN_ERR "can't register video device!\n");
+ goto errfr1;
+ }
+
+ if (!radio_power_on(radio_unit)) {
+ retval = -EIO;
+ goto errunr;
+ }
+
+ dev_info(&pdev->dev, "version " DRIVER_VERSION " time " __TIME__ " "
+ __DATE__ "\n");
+ dev_info(&pdev->dev, "radio chip initialized\n");
+
+ return 0;
+errunr:
+ video_unregister_device(maestro_radio_inst);
+errfr1:
+ kfree(maestro_radio_inst);
+errfr:
+ kfree(radio_unit);
+err:
+ return retval;
+
+}
+
+static void __devexit maestro_remove(struct pci_dev *pdev)
+{
+ struct video_device *vdev = pci_get_drvdata(pdev);
+
+ video_unregister_device(vdev);
+}
+
+static int __init maestro_radio_init(void)
+{
+ int retval = pci_register_driver(&maestro_r_driver);
+
+ if (retval)
+ printk(KERN_ERR "error during registration pci driver\n");
+
+ return retval;
+}
+
+static void __exit maestro_radio_exit(void)
+{
+ pci_unregister_driver(&maestro_r_driver);
+}
+
+module_init(maestro_radio_init);
+module_exit(maestro_radio_exit);
+
+MODULE_AUTHOR("Adam Tlalka, atlka@pg.gda.pl");
+MODULE_DESCRIPTION("Radio driver for the Maestro PCI sound card radio.");
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/radio/radio-maxiradio.c b/linux/drivers/media/radio/radio-maxiradio.c
new file mode 100644
index 000000000..da6b6148e
--- /dev/null
+++ b/linux/drivers/media/radio/radio-maxiradio.c
@@ -0,0 +1,352 @@
+/*
+ * Guillemot Maxi Radio FM 2000 PCI radio card driver for Linux
+ * (C) 2001 Dimitromanolakis Apostolos <apdim@grecian.net>
+ *
+ * Based in the radio Maestro PCI driver. Actually it uses the same chip
+ * for radio but different pci controller.
+ *
+ * I didn't have any specs I reversed engineered the protocol from
+ * the windows driver (radio.dll).
+ *
+ * The card uses the TEA5757 chip that includes a search function but it
+ * is useless as I haven't found any way to read back the frequency. If
+ * anybody does please mail me.
+ *
+ * For the pdf file see:
+ * http://www.semiconductors.philips.com/pip/TEA5757H/V1
+ *
+ *
+ * CHANGES:
+ * 0.75b
+ * - better pci interface thanks to Francois Romieu <romieu@cogenit.fr>
+ *
+ * 0.75
+ * - tiding up
+ * - removed support for multiple devices as it didn't work anyway
+ *
+ * BUGS:
+ * - card unmutes if you change frequency
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/mutex.h>
+
+#include <linux/pci.h>
+#include "compat.h"
+#include <linux/videodev.h>
+
+/* version 0.75 Sun Feb 4 22:51:27 EET 2001 */
+#define DRIVER_VERSION "0.75"
+
+#ifndef PCI_VENDOR_ID_GUILLEMOT
+#define PCI_VENDOR_ID_GUILLEMOT 0x5046
+#endif
+
+#ifndef PCI_DEVICE_ID_GUILLEMOT
+#define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001
+#endif
+
+
+/* TEA5757 pin mappings */
+static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16 ;
+
+static int radio_nr = -1;
+module_param(radio_nr, int, 0);
+
+
+#define FREQ_LO 50*16000
+#define FREQ_HI 150*16000
+
+#define FREQ_IF 171200 /* 10.7*16000 */
+#define FREQ_STEP 200 /* 12.5*16 */
+
+#define FREQ2BITS(x) ((( (unsigned int)(x)+FREQ_IF+(FREQ_STEP<<1))\
+ /(FREQ_STEP<<2))<<2) /* (x==fmhz*16*1000) -> bits */
+
+#define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF)
+
+
+static int radio_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+
+static struct file_operations maxiradio_fops = {
+ .owner = THIS_MODULE,
+ .open = video_exclusive_open,
+ .release = video_exclusive_release,
+ .ioctl = radio_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+static struct video_device maxiradio_radio =
+{
+ .owner = THIS_MODULE,
+ .name = "Maxi Radio FM2000 radio",
+ .type = VID_TYPE_TUNER,
+ .hardware = VID_HARDWARE_SF16MI,
+ .fops = &maxiradio_fops,
+};
+
+static struct radio_device
+{
+ __u16 io, /* base of radio io */
+ muted, /* VIDEO_AUDIO_MUTE */
+ stereo, /* VIDEO_TUNER_STEREO_ON */
+ tuned; /* signal strength (0 or 0xffff) */
+
+ unsigned long freq;
+
+ struct mutex lock;
+} radio_unit = {0, 0, 0, 0, };
+
+
+static void outbit(unsigned long bit, __u16 io)
+{
+ if(bit != 0)
+ {
+ outb( power|wren|data ,io); udelay(4);
+ outb( power|wren|data|clk ,io); udelay(4);
+ outb( power|wren|data ,io); udelay(4);
+ }
+ else
+ {
+ outb( power|wren ,io); udelay(4);
+ outb( power|wren|clk ,io); udelay(4);
+ outb( power|wren ,io); udelay(4);
+ }
+}
+
+static void turn_power(__u16 io, int p)
+{
+ if(p != 0) outb(power, io); else outb(0,io);
+}
+
+
+static void set_freq(__u16 io, __u32 data)
+{
+ unsigned long int si;
+ int bl;
+
+ /* TEA5757 shift register bits (see pdf) */
+
+ outbit(0,io); // 24 search
+ outbit(1,io); // 23 search up/down
+
+ outbit(0,io); // 22 stereo/mono
+
+ outbit(0,io); // 21 band
+ outbit(0,io); // 20 band (only 00=FM works I think)
+
+ outbit(0,io); // 19 port ?
+ outbit(0,io); // 18 port ?
+
+ outbit(0,io); // 17 search level
+ outbit(0,io); // 16 search level
+
+ si = 0x8000;
+ for(bl = 1; bl <= 16 ; bl++) { outbit(data & si,io); si >>=1; }
+
+ outb(power,io);
+}
+
+static int get_stereo(__u16 io)
+{
+ outb(power,io); udelay(4);
+ return !(inb(io) & mo_st);
+}
+
+static int get_tune(__u16 io)
+{
+ outb(power+clk,io); udelay(4);
+ return !(inb(io) & mo_st);
+}
+
+
+static inline int radio_function(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct radio_device *card=dev->priv;
+
+ switch(cmd) {
+ case VIDIOCGCAP: {
+ struct video_capability *v = arg;
+
+ memset(v,0,sizeof(*v));
+ strcpy(v->name, "Maxi Radio FM2000 radio");
+ v->type=VID_TYPE_TUNER;
+ v->channels=v->audios=1;
+ return 0;
+ }
+ case VIDIOCGTUNER: {
+ struct video_tuner *v = arg;
+
+ if(v->tuner)
+ return -EINVAL;
+
+ card->stereo = 0xffff * get_stereo(card->io);
+ card->tuned = 0xffff * get_tune(card->io);
+
+ v->flags = VIDEO_TUNER_LOW | card->stereo;
+ v->signal = card->tuned;
+
+ strcpy(v->name, "FM");
+
+ v->rangelow = FREQ_LO;
+ v->rangehigh = FREQ_HI;
+ v->mode = VIDEO_MODE_AUTO;
+
+ return 0;
+ }
+ case VIDIOCSTUNER: {
+ struct video_tuner *v = arg;
+ if(v->tuner!=0)
+ return -EINVAL;
+ return 0;
+ }
+ case VIDIOCGFREQ: {
+ unsigned long *freq = arg;
+
+ *freq = card->freq;
+ return 0;
+ }
+ case VIDIOCSFREQ: {
+ unsigned long *freq = arg;
+
+ if (*freq < FREQ_LO || *freq > FREQ_HI)
+ return -EINVAL;
+ card->freq = *freq;
+ set_freq(card->io, FREQ2BITS(card->freq));
+ msleep(125);
+ return 0;
+ }
+ case VIDIOCGAUDIO: {
+ struct video_audio *v = arg;
+ memset(v,0,sizeof(*v));
+ strcpy(v->name, "Radio");
+ v->flags=VIDEO_AUDIO_MUTABLE | card->muted;
+ v->mode=VIDEO_SOUND_STEREO;
+ return 0;
+ }
+
+ case VIDIOCSAUDIO: {
+ struct video_audio *v = arg;
+
+ if(v->audio)
+ return -EINVAL;
+ card->muted = v->flags & VIDEO_AUDIO_MUTE;
+ if(card->muted)
+ turn_power(card->io, 0);
+ else
+ set_freq(card->io, FREQ2BITS(card->freq));
+ return 0;
+ }
+ case VIDIOCGUNIT: {
+ struct video_unit *v = arg;
+
+ v->video=VIDEO_NO_UNIT;
+ v->vbi=VIDEO_NO_UNIT;
+ v->radio=dev->minor;
+ v->audio=0;
+ v->teletext=VIDEO_NO_UNIT;
+ return 0;
+ }
+ default: return -ENOIOCTLCMD;
+ }
+}
+
+static int radio_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct radio_device *card=dev->priv;
+ int ret;
+
+ mutex_lock(&card->lock);
+ ret = video_usercopy(inode, file, cmd, arg, radio_function);
+ mutex_unlock(&card->lock);
+ return ret;
+}
+
+MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net");
+MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000 radio.");
+MODULE_LICENSE("GPL");
+
+
+static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ if(!request_region(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0), "Maxi Radio FM 2000")) {
+ printk(KERN_ERR "radio-maxiradio: can't reserve I/O ports\n");
+ goto err_out;
+ }
+
+ if (pci_enable_device(pdev))
+ goto err_out_free_region;
+
+ radio_unit.io = pci_resource_start(pdev, 0);
+ mutex_init(&radio_unit.lock);
+ maxiradio_radio.priv = &radio_unit;
+
+ if(video_register_device(&maxiradio_radio, VFL_TYPE_RADIO, radio_nr)==-1) {
+ printk("radio-maxiradio: can't register device!");
+ goto err_out_free_region;
+ }
+
+ printk(KERN_INFO "radio-maxiradio: version "
+ DRIVER_VERSION
+ " time "
+ __TIME__ " "
+ __DATE__
+ "\n");
+
+ printk(KERN_INFO "radio-maxiradio: found Guillemot MAXI Radio device (io = 0x%x)\n",
+ radio_unit.io);
+ return 0;
+
+err_out_free_region:
+ release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
+err_out:
+ return -ENODEV;
+}
+
+static void __devexit maxiradio_remove_one(struct pci_dev *pdev)
+{
+ video_unregister_device(&maxiradio_radio);
+ release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
+}
+
+static struct pci_device_id maxiradio_pci_tbl[] = {
+ { PCI_VENDOR_ID_GUILLEMOT, PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ { 0,}
+};
+
+MODULE_DEVICE_TABLE(pci, maxiradio_pci_tbl);
+
+static struct pci_driver maxiradio_driver = {
+ .name = "radio-maxiradio",
+ .id_table = maxiradio_pci_tbl,
+ .probe = maxiradio_init_one,
+ .remove = __devexit_p(maxiradio_remove_one),
+};
+
+static int __init maxiradio_radio_init(void)
+{
+ return pci_register_driver(&maxiradio_driver);
+}
+
+static void __exit maxiradio_radio_exit(void)
+{
+ pci_unregister_driver(&maxiradio_driver);
+}
+
+module_init(maxiradio_radio_init);
+module_exit(maxiradio_radio_exit);
diff --git a/linux/drivers/media/radio/radio-rtrack2.c b/linux/drivers/media/radio/radio-rtrack2.c
new file mode 100644
index 000000000..defdc83e4
--- /dev/null
+++ b/linux/drivers/media/radio/radio-rtrack2.c
@@ -0,0 +1,268 @@
+/* RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff
+ *
+ * Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood
+ * Converted to new API by Alan Cox <Alan.Cox@linux.org>
+ * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
+ *
+ * TODO: Allow for more than one of these foolish entities :-)
+ *
+ */
+
+#include <linux/module.h> /* Modules */
+#include <linux/init.h> /* Initdata */
+#include <linux/ioport.h> /* request_region */
+#include <linux/delay.h> /* udelay */
+#include <asm/io.h> /* outb, outb_p */
+#include <asm/uaccess.h> /* copy to/from user */
+#include "compat.h"
+#include <linux/videodev.h> /* kernel radio structs */
+#include <linux/config.h> /* CONFIG_RADIO_RTRACK2_PORT */
+#include <linux/spinlock.h>
+
+#ifndef CONFIG_RADIO_RTRACK2_PORT
+#define CONFIG_RADIO_RTRACK2_PORT -1
+#endif
+
+static int io = CONFIG_RADIO_RTRACK2_PORT;
+static int radio_nr = -1;
+static spinlock_t lock;
+
+struct rt_device
+{
+ int port;
+ unsigned long curfreq;
+ int muted;
+};
+
+
+/* local things */
+
+static void rt_mute(struct rt_device *dev)
+{
+ if(dev->muted)
+ return;
+ spin_lock(&lock);
+ outb(1, io);
+ spin_unlock(&lock);
+ dev->muted = 1;
+}
+
+static void rt_unmute(struct rt_device *dev)
+{
+ if(dev->muted == 0)
+ return;
+ spin_lock(&lock);
+ outb(0, io);
+ spin_unlock(&lock);
+ dev->muted = 0;
+}
+
+static void zero(void)
+{
+ outb_p(1, io);
+ outb_p(3, io);
+ outb_p(1, io);
+}
+
+static void one(void)
+{
+ outb_p(5, io);
+ outb_p(7, io);
+ outb_p(5, io);
+}
+
+static int rt_setfreq(struct rt_device *dev, unsigned long freq)
+{
+ int i;
+
+ freq = freq / 200 + 856;
+
+ spin_lock(&lock);
+
+ outb_p(0xc8, io);
+ outb_p(0xc9, io);
+ outb_p(0xc9, io);
+
+ for (i = 0; i < 10; i++)
+ zero ();
+
+ for (i = 14; i >= 0; i--)
+ if (freq & (1 << i))
+ one ();
+ else
+ zero ();
+
+ outb_p(0xc8, io);
+ if (!dev->muted)
+ outb_p(0, io);
+
+ spin_unlock(&lock);
+ return 0;
+}
+
+static int rt_getsigstr(struct rt_device *dev)
+{
+ if (inb(io) & 2) /* bit set = no signal present */
+ return 0;
+ return 1; /* signal present */
+}
+
+static int rt_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct rt_device *rt=dev->priv;
+
+ switch(cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *v = arg;
+ memset(v,0,sizeof(*v));
+ v->type=VID_TYPE_TUNER;
+ v->channels=1;
+ v->audios=1;
+ strcpy(v->name, "RadioTrack II");
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner *v = arg;
+ if(v->tuner) /* Only 1 tuner */
+ return -EINVAL;
+ v->rangelow=88*16000;
+ v->rangehigh=108*16000;
+ v->flags=VIDEO_TUNER_LOW;
+ v->mode=VIDEO_MODE_AUTO;
+ v->signal=0xFFFF*rt_getsigstr(rt);
+ strcpy(v->name, "FM");
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner *v = arg;
+ if(v->tuner!=0)
+ return -EINVAL;
+ /* Only 1 tuner so no setting needed ! */
+ return 0;
+ }
+ case VIDIOCGFREQ:
+ {
+ unsigned long *freq = arg;
+ *freq = rt->curfreq;
+ return 0;
+ }
+ case VIDIOCSFREQ:
+ {
+ unsigned long *freq = arg;
+ rt->curfreq = *freq;
+ rt_setfreq(rt, rt->curfreq);
+ return 0;
+ }
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio *v = arg;
+ memset(v,0, sizeof(*v));
+ v->flags|=VIDEO_AUDIO_MUTABLE;
+ v->volume=1;
+ v->step=65535;
+ strcpy(v->name, "Radio");
+ return 0;
+ }
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio *v = arg;
+ if(v->audio)
+ return -EINVAL;
+
+ if(v->flags&VIDEO_AUDIO_MUTE)
+ rt_mute(rt);
+ else
+ rt_unmute(rt);
+
+ return 0;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int rt_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, rt_do_ioctl);
+}
+
+static struct rt_device rtrack2_unit;
+
+static struct file_operations rtrack2_fops = {
+ .owner = THIS_MODULE,
+ .open = video_exclusive_open,
+ .release = video_exclusive_release,
+ .ioctl = rt_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device rtrack2_radio=
+{
+ .owner = THIS_MODULE,
+ .name = "RadioTrack II radio",
+ .type = VID_TYPE_TUNER,
+ .hardware = VID_HARDWARE_RTRACK2,
+ .fops = &rtrack2_fops,
+};
+
+static int __init rtrack2_init(void)
+{
+ if(io==-1)
+ {
+ printk(KERN_ERR "You must set an I/O address with io=0x20c or io=0x30c\n");
+ return -EINVAL;
+ }
+ if (!request_region(io, 4, "rtrack2"))
+ {
+ printk(KERN_ERR "rtrack2: port 0x%x already in use\n", io);
+ return -EBUSY;
+ }
+
+ rtrack2_radio.priv=&rtrack2_unit;
+
+ spin_lock_init(&lock);
+ if(video_register_device(&rtrack2_radio, VFL_TYPE_RADIO, radio_nr)==-1)
+ {
+ release_region(io, 4);
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "AIMSlab Radiotrack II card driver.\n");
+
+ /* mute card - prevents noisy bootups */
+ outb(1, io);
+ rtrack2_unit.muted = 1;
+
+ return 0;
+}
+
+MODULE_AUTHOR("Ben Pfaff");
+MODULE_DESCRIPTION("A driver for the RadioTrack II radio card.");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20c or 0x30c)");
+module_param(radio_nr, int, 0);
+
+static void __exit rtrack2_cleanup_module(void)
+{
+ video_unregister_device(&rtrack2_radio);
+ release_region(io,4);
+}
+
+module_init(rtrack2_init);
+module_exit(rtrack2_cleanup_module);
+
+/*
+ Local variables:
+ compile-command: "mmake"
+ End:
+*/
diff --git a/linux/drivers/media/radio/radio-sf16fmi.c b/linux/drivers/media/radio/radio-sf16fmi.c
new file mode 100644
index 000000000..c1d3b5c33
--- /dev/null
+++ b/linux/drivers/media/radio/radio-sf16fmi.c
@@ -0,0 +1,330 @@
+/* SF16FMI radio driver for Linux radio support
+ * heavily based on rtrack driver...
+ * (c) 1997 M. Kirkwood
+ * (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz
+ *
+ * Fitted to new interface by Alan Cox <alan.cox@linux.org>
+ * Made working and cleaned up functions <mikael.hedin@irf.se>
+ * Support for ISAPnP by Ladislav Michl <ladis@psi.cz>
+ *
+ * Notes on the hardware
+ *
+ * Frequency control is done digitally -- ie out(port,encodefreq(95.8));
+ * No volume control - only mute/unmute - you have to use line volume
+ * control on SB-part of SF16FMI
+ *
+ */
+
+#include <linux/kernel.h> /* __setup */
+#include <linux/module.h> /* Modules */
+#include <linux/init.h> /* Initdata */
+#include <linux/ioport.h> /* request_region */
+#include <linux/delay.h> /* udelay */
+#include "compat.h"
+#include <linux/videodev.h> /* kernel radio structs */
+#include <linux/isapnp.h>
+#include <asm/io.h> /* outb, outb_p */
+#include <asm/uaccess.h> /* copy to/from user */
+#include <linux/mutex.h>
+
+struct fmi_device
+{
+ int port;
+ int curvol; /* 1 or 0 */
+ unsigned long curfreq; /* freq in kHz */
+ __u32 flags;
+};
+
+static int io = -1;
+static int radio_nr = -1;
+static struct pnp_dev *dev = NULL;
+static struct mutex lock;
+
+/* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */
+/* It is only useful to give freq in intervall of 800 (=0.05Mhz),
+ * other bits will be truncated, e.g 92.7400016 -> 92.7, but
+ * 92.7400017 -> 92.75
+ */
+#define RSF16_ENCODE(x) ((x)/800+214)
+#define RSF16_MINFREQ 87*16000
+#define RSF16_MAXFREQ 108*16000
+
+static void outbits(int bits, unsigned int data, int port)
+{
+ while(bits--) {
+ if(data & 1) {
+ outb(5, port);
+ udelay(6);
+ outb(7, port);
+ udelay(6);
+ } else {
+ outb(1, port);
+ udelay(6);
+ outb(3, port);
+ udelay(6);
+ }
+ data>>=1;
+ }
+}
+
+static inline void fmi_mute(int port)
+{
+ mutex_lock(&lock);
+ outb(0x00, port);
+ mutex_unlock(&lock);
+}
+
+static inline void fmi_unmute(int port)
+{
+ mutex_lock(&lock);
+ outb(0x08, port);
+ mutex_unlock(&lock);
+}
+
+static inline int fmi_setfreq(struct fmi_device *dev)
+{
+ int myport = dev->port;
+ unsigned long freq = dev->curfreq;
+
+ mutex_lock(&lock);
+
+ outbits(16, RSF16_ENCODE(freq), myport);
+ outbits(8, 0xC0, myport);
+ msleep(143); /* was schedule_timeout(HZ/7) */
+ mutex_unlock(&lock);
+ if (dev->curvol) fmi_unmute(myport);
+ return 0;
+}
+
+static inline int fmi_getsigstr(struct fmi_device *dev)
+{
+ int val;
+ int res;
+ int myport = dev->port;
+
+
+ mutex_lock(&lock);
+ val = dev->curvol ? 0x08 : 0x00; /* unmute/mute */
+ outb(val, myport);
+ outb(val | 0x10, myport);
+ msleep(143); /* was schedule_timeout(HZ/7) */
+ res = (int)inb(myport+1);
+ outb(val, myport);
+
+ mutex_unlock(&lock);
+ return (res & 2) ? 0 : 0xFFFF;
+}
+
+static int fmi_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct fmi_device *fmi=dev->priv;
+
+ switch(cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *v = arg;
+ memset(v,0,sizeof(*v));
+ strcpy(v->name, "SF16-FMx radio");
+ v->type=VID_TYPE_TUNER;
+ v->channels=1;
+ v->audios=1;
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner *v = arg;
+ int mult;
+
+ if(v->tuner) /* Only 1 tuner */
+ return -EINVAL;
+ strcpy(v->name, "FM");
+ mult = (fmi->flags & VIDEO_TUNER_LOW) ? 1 : 1000;
+ v->rangelow = RSF16_MINFREQ/mult;
+ v->rangehigh = RSF16_MAXFREQ/mult;
+ v->flags=fmi->flags;
+ v->mode=VIDEO_MODE_AUTO;
+ v->signal = fmi_getsigstr(fmi);
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner *v = arg;
+ if(v->tuner!=0)
+ return -EINVAL;
+ fmi->flags = v->flags & VIDEO_TUNER_LOW;
+ /* Only 1 tuner so no setting needed ! */
+ return 0;
+ }
+ case VIDIOCGFREQ:
+ {
+ unsigned long *freq = arg;
+ *freq = fmi->curfreq;
+ if (!(fmi->flags & VIDEO_TUNER_LOW))
+ *freq /= 1000;
+ return 0;
+ }
+ case VIDIOCSFREQ:
+ {
+ unsigned long *freq = arg;
+ if (!(fmi->flags & VIDEO_TUNER_LOW))
+ *freq *= 1000;
+ if (*freq < RSF16_MINFREQ || *freq > RSF16_MAXFREQ )
+ return -EINVAL;
+ /*rounding in steps of 800 to match th freq
+ that will be used */
+ fmi->curfreq = (*freq/800)*800;
+ fmi_setfreq(fmi);
+ return 0;
+ }
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio *v = arg;
+ memset(v,0,sizeof(*v));
+ v->flags=( (!fmi->curvol)*VIDEO_AUDIO_MUTE | VIDEO_AUDIO_MUTABLE);
+ strcpy(v->name, "Radio");
+ v->mode=VIDEO_SOUND_STEREO;
+ return 0;
+ }
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio *v = arg;
+ if(v->audio)
+ return -EINVAL;
+ fmi->curvol= v->flags&VIDEO_AUDIO_MUTE ? 0 : 1;
+ fmi->curvol ?
+ fmi_unmute(fmi->port) : fmi_mute(fmi->port);
+ return 0;
+ }
+ case VIDIOCGUNIT:
+ {
+ struct video_unit *v = arg;
+ v->video=VIDEO_NO_UNIT;
+ v->vbi=VIDEO_NO_UNIT;
+ v->radio=dev->minor;
+ v->audio=0; /* How do we find out this??? */
+ v->teletext=VIDEO_NO_UNIT;
+ return 0;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int fmi_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, fmi_do_ioctl);
+}
+
+static struct fmi_device fmi_unit;
+
+static struct file_operations fmi_fops = {
+ .owner = THIS_MODULE,
+ .open = video_exclusive_open,
+ .release = video_exclusive_release,
+ .ioctl = fmi_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device fmi_radio=
+{
+ .owner = THIS_MODULE,
+ .name = "SF16FMx radio",
+ .type = VID_TYPE_TUNER,
+ .hardware = VID_HARDWARE_SF16MI,
+ .fops = &fmi_fops,
+};
+
+/* ladis: this is my card. does any other types exist? */
+static struct isapnp_device_id id_table[] __devinitdata = {
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+ ISAPNP_VENDOR('M','F','R'), ISAPNP_FUNCTION(0xad10), 0},
+ { ISAPNP_CARD_END, },
+};
+
+MODULE_DEVICE_TABLE(isapnp, id_table);
+
+static int isapnp_fmi_probe(void)
+{
+ int i = 0;
+
+ while (id_table[i].card_vendor != 0 && dev == NULL) {
+ dev = pnp_find_dev(NULL, id_table[i].vendor,
+ id_table[i].function, NULL);
+ i++;
+ }
+
+ if (!dev)
+ return -ENODEV;
+ if (pnp_device_attach(dev) < 0)
+ return -EAGAIN;
+ if (pnp_activate_dev(dev) < 0) {
+ printk ("radio-sf16fmi: PnP configure failed (out of resources?)\n");
+ pnp_device_detach(dev);
+ return -ENOMEM;
+ }
+ if (!pnp_port_valid(dev, 0)) {
+ pnp_device_detach(dev);
+ return -ENODEV;
+ }
+
+ i = pnp_port_start(dev, 0);
+ printk ("radio-sf16fmi: PnP reports card at %#x\n", i);
+
+ return i;
+}
+
+static int __init fmi_init(void)
+{
+ if (io < 0)
+ io = isapnp_fmi_probe();
+ if (io < 0) {
+ printk(KERN_ERR "radio-sf16fmi: No PnP card found.\n");
+ return io;
+ }
+ if (!request_region(io, 2, "radio-sf16fmi")) {
+ printk(KERN_ERR "radio-sf16fmi: port 0x%x already in use\n", io);
+ return -EBUSY;
+ }
+
+ fmi_unit.port = io;
+ fmi_unit.curvol = 0;
+ fmi_unit.curfreq = 0;
+ fmi_unit.flags = VIDEO_TUNER_LOW;
+ fmi_radio.priv = &fmi_unit;
+
+ mutex_init(&lock);
+
+ if (video_register_device(&fmi_radio, VFL_TYPE_RADIO, radio_nr) == -1) {
+ release_region(io, 2);
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "SF16FMx radio card driver at 0x%x\n", io);
+ /* mute card - prevents noisy bootups */
+ fmi_mute(io);
+ return 0;
+}
+
+MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood");
+MODULE_DESCRIPTION("A driver for the SF16MI radio.");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "I/O address of the SF16MI card (0x284 or 0x384)");
+module_param(radio_nr, int, 0);
+
+static void __exit fmi_cleanup_module(void)
+{
+ video_unregister_device(&fmi_radio);
+ release_region(io, 2);
+ if (dev)
+ pnp_device_detach(dev);
+}
+
+module_init(fmi_init);
+module_exit(fmi_cleanup_module);
diff --git a/linux/drivers/media/radio/radio-sf16fmr2.c b/linux/drivers/media/radio/radio-sf16fmr2.c
new file mode 100644
index 000000000..d90d87840
--- /dev/null
+++ b/linux/drivers/media/radio/radio-sf16fmr2.c
@@ -0,0 +1,436 @@
+/* SF16FMR2 radio driver for Linux radio support
+ * heavily based on fmi driver...
+ * (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
+ *
+ * Notes on the hardware
+ *
+ * Frequency control is done digitally -- ie out(port,encodefreq(95.8));
+ * No volume control - only mute/unmute - you have to use line volume
+ *
+ * For read stereo/mono you must wait 0.1 sec after set frequency and
+ * card unmuted so I set frequency on unmute
+ * Signal handling seem to work only on autoscanning (not implemented)
+ */
+
+#include <linux/module.h> /* Modules */
+#include <linux/init.h> /* Initdata */
+#include <linux/ioport.h> /* request_region */
+#include <linux/delay.h> /* udelay */
+#include <asm/io.h> /* outb, outb_p */
+#include <asm/uaccess.h> /* copy to/from user */
+#include "compat.h"
+#include <linux/videodev.h> /* kernel radio structs */
+#include <linux/mutex.h>
+
+static struct mutex lock;
+
+#undef DEBUG
+//#define DEBUG 1
+
+#ifdef DEBUG
+# define debug_print(s) printk s
+#else
+# define debug_print(s)
+#endif
+
+/* this should be static vars for module size */
+struct fmr2_device
+{
+ int port;
+ int curvol; /* 0-65535, if not volume 0 or 65535 */
+ int mute;
+ int stereo; /* card is producing stereo audio */
+ unsigned long curfreq; /* freq in kHz */
+ int card_type;
+ __u32 flags;
+};
+
+static int io = 0x384;
+static int radio_nr = -1;
+
+/* hw precision is 12.5 kHz
+ * It is only useful to give freq in intervall of 200 (=0.0125Mhz),
+ * other bits will be truncated
+ */
+#define RSF16_ENCODE(x) ((x)/200+856)
+#define RSF16_MINFREQ 87*16000
+#define RSF16_MAXFREQ 108*16000
+
+static inline void wait(int n,int port)
+{
+ for (;n;--n) inb(port);
+}
+
+static void outbits(int bits, unsigned int data, int nWait, int port)
+{
+ int bit;
+ for(;--bits>=0;) {
+ bit = (data>>bits) & 1;
+ outb(bit,port);
+ wait(nWait,port);
+ outb(bit|2,port);
+ wait(nWait,port);
+ outb(bit,port);
+ wait(nWait,port);
+ }
+}
+
+static inline void fmr2_mute(int port)
+{
+ outb(0x00, port);
+ wait(4,port);
+}
+
+static inline void fmr2_unmute(int port)
+{
+ outb(0x04, port);
+ wait(4,port);
+}
+
+static inline int fmr2_stereo_mode(int port)
+{
+ int n = inb(port);
+ outb(6,port);
+ inb(port);
+ n = ((n>>3)&1)^1;
+ debug_print((KERN_DEBUG "stereo: %d\n", n));
+ return n;
+}
+
+static int fmr2_product_info(struct fmr2_device *dev)
+{
+ int n = inb(dev->port);
+ n &= 0xC1;
+ if (n == 0)
+ {
+ /* this should support volume set */
+ dev->card_type = 12;
+ return 0;
+ }
+ /* not volume (mine is 11) */
+ dev->card_type = (n==128)?11:0;
+ return n;
+}
+
+static inline int fmr2_getsigstr(struct fmr2_device *dev)
+{
+ /* !!! work only if scanning freq */
+ int port = dev->port, res = 0xffff;
+ outb(5,port);
+ wait(4,port);
+ if (!(inb(port)&1)) res = 0;
+ debug_print((KERN_DEBUG "signal: %d\n", res));
+ return res;
+}
+
+/* set frequency and unmute card */
+static int fmr2_setfreq(struct fmr2_device *dev)
+{
+ int port = dev->port;
+ unsigned long freq = dev->curfreq;
+
+ fmr2_mute(port);
+
+ /* 0x42 for mono output
+ * 0x102 forward scanning
+ * 0x182 scansione avanti
+ */
+ outbits(9,0x2,3,port);
+ outbits(16,RSF16_ENCODE(freq),2,port);
+
+ fmr2_unmute(port);
+
+ /* wait 0.11 sec */
+ msleep(110);
+
+ /* NOTE if mute this stop radio
+ you must set freq on unmute */
+ dev->stereo = fmr2_stereo_mode(port);
+ return 0;
+}
+
+/* !!! not tested, in my card this does't work !!! */
+static int fmr2_setvolume(struct fmr2_device *dev)
+{
+ int i,a,n, port = dev->port;
+
+ if (dev->card_type != 11) return 1;
+
+ switch( (dev->curvol+(1<<11)) >> 12 )
+ {
+ case 0: case 1: n = 0x21; break;
+ case 2: n = 0x84; break;
+ case 3: n = 0x90; break;
+ case 4: n = 0x104; break;
+ case 5: n = 0x110; break;
+ case 6: n = 0x204; break;
+ case 7: n = 0x210; break;
+ case 8: n = 0x402; break;
+ case 9: n = 0x404; break;
+ default:
+ case 10: n = 0x408; break;
+ case 11: n = 0x410; break;
+ case 12: n = 0x801; break;
+ case 13: n = 0x802; break;
+ case 14: n = 0x804; break;
+ case 15: n = 0x808; break;
+ case 16: n = 0x810; break;
+ }
+ for(i=12;--i>=0;)
+ {
+ a = ((n >> i) & 1) << 6; /* if (a=0) a= 0; else a= 0x40; */
+ outb(a|4, port);
+ wait(4,port);
+ outb(a|0x24, port);
+ wait(4,port);
+ outb(a|4, port);
+ wait(4,port);
+ }
+ for(i=6;--i>=0;)
+ {
+ a = ((0x18 >> i) & 1) << 6;
+ outb(a|4, port);
+ wait(4,port);
+ outb(a|0x24, port);
+ wait(4,port);
+ outb(a|4, port);
+ wait(4,port);
+ }
+ wait(4,port);
+ outb(0x14, port);
+
+ return 0;
+}
+
+static int fmr2_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct fmr2_device *fmr2 = dev->priv;
+ debug_print((KERN_DEBUG "freq %ld flags %d vol %d mute %d "
+ "stereo %d type %d\n",
+ fmr2->curfreq, fmr2->flags, fmr2->curvol, fmr2->mute,
+ fmr2->stereo, fmr2->card_type));
+
+ switch(cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *v = arg;
+ memset(v,0,sizeof(*v));
+ strcpy(v->name, "SF16-FMR2 radio");
+ v->type=VID_TYPE_TUNER;
+ v->channels=1;
+ v->audios=1;
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner *v = arg;
+ int mult;
+
+ if(v->tuner) /* Only 1 tuner */
+ return -EINVAL;
+ strcpy(v->name, "FM");
+ mult = (fmr2->flags & VIDEO_TUNER_LOW) ? 1 : 1000;
+ v->rangelow = RSF16_MINFREQ/mult;
+ v->rangehigh = RSF16_MAXFREQ/mult;
+ v->flags = fmr2->flags | VIDEO_AUDIO_MUTABLE;
+ if (fmr2->mute)
+ v->flags |= VIDEO_AUDIO_MUTE;
+ v->mode=VIDEO_MODE_AUTO;
+ mutex_lock(&lock);
+ v->signal = fmr2_getsigstr(fmr2);
+ mutex_unlock(&lock);
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner *v = arg;
+ if (v->tuner!=0)
+ return -EINVAL;
+ fmr2->flags = v->flags & VIDEO_TUNER_LOW;
+ return 0;
+ }
+ case VIDIOCGFREQ:
+ {
+ unsigned long *freq = arg;
+ *freq = fmr2->curfreq;
+ if (!(fmr2->flags & VIDEO_TUNER_LOW))
+ *freq /= 1000;
+ return 0;
+ }
+ case VIDIOCSFREQ:
+ {
+ unsigned long *freq = arg;
+ if (!(fmr2->flags & VIDEO_TUNER_LOW))
+ *freq *= 1000;
+ if ( *freq < RSF16_MINFREQ || *freq > RSF16_MAXFREQ )
+ return -EINVAL;
+ /* rounding in steps of 200 to match th freq
+ * that will be used
+ */
+ fmr2->curfreq = (*freq/200)*200;
+
+ /* set card freq (if not muted) */
+ if (fmr2->curvol && !fmr2->mute)
+ {
+ mutex_lock(&lock);
+ fmr2_setfreq(fmr2);
+ mutex_unlock(&lock);
+ }
+ return 0;
+ }
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio *v = arg;
+ memset(v,0,sizeof(*v));
+ /* !!! do not return VIDEO_AUDIO_MUTE */
+ v->flags = VIDEO_AUDIO_MUTABLE;
+ strcpy(v->name, "Radio");
+ /* get current stereo mode */
+ v->mode = fmr2->stereo ? VIDEO_SOUND_STEREO: VIDEO_SOUND_MONO;
+ /* volume supported ? */
+ if (fmr2->card_type == 11)
+ {
+ v->flags |= VIDEO_AUDIO_VOLUME;
+ v->step = 1 << 12;
+ v->volume = fmr2->curvol;
+ }
+ debug_print((KERN_DEBUG "Get flags %d vol %d\n", v->flags, v->volume));
+ return 0;
+ }
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio *v = arg;
+ if(v->audio)
+ return -EINVAL;
+ debug_print((KERN_DEBUG "Set flags %d vol %d\n", v->flags, v->volume));
+ /* set volume */
+ if (v->flags & VIDEO_AUDIO_VOLUME)
+ fmr2->curvol = v->volume; /* !!! set with precision */
+ if (fmr2->card_type != 11) fmr2->curvol = 65535;
+ fmr2->mute = 0;
+ if (v->flags & VIDEO_AUDIO_MUTE)
+ fmr2->mute = 1;
+#ifdef DEBUG
+ if (fmr2->curvol && !fmr2->mute)
+ printk(KERN_DEBUG "unmute\n");
+ else
+ printk(KERN_DEBUG "mute\n");
+#endif
+ mutex_lock(&lock);
+ if (fmr2->curvol && !fmr2->mute)
+ {
+ fmr2_setvolume(fmr2);
+ fmr2_setfreq(fmr2);
+ }
+ else fmr2_mute(fmr2->port);
+ mutex_unlock(&lock);
+ return 0;
+ }
+ case VIDIOCGUNIT:
+ {
+ struct video_unit *v = arg;
+ v->video=VIDEO_NO_UNIT;
+ v->vbi=VIDEO_NO_UNIT;
+ v->radio=dev->minor;
+ v->audio=0; /* How do we find out this??? */
+ v->teletext=VIDEO_NO_UNIT;
+ return 0;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int fmr2_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+ {
+ return video_usercopy(inode, file, cmd, arg, fmr2_do_ioctl);
+}
+
+static struct fmr2_device fmr2_unit;
+
+static struct file_operations fmr2_fops = {
+ .owner = THIS_MODULE,
+ .open = video_exclusive_open,
+ .release = video_exclusive_release,
+ .ioctl = fmr2_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device fmr2_radio=
+{
+ .owner = THIS_MODULE,
+ .name = "SF16FMR2 radio",
+ . type = VID_TYPE_TUNER,
+ .hardware = VID_HARDWARE_SF16FMR2,
+ .fops = &fmr2_fops,
+};
+
+static int __init fmr2_init(void)
+{
+ fmr2_unit.port = io;
+ fmr2_unit.curvol = 0;
+ fmr2_unit.mute = 0;
+ fmr2_unit.curfreq = 0;
+ fmr2_unit.stereo = 1;
+ fmr2_unit.flags = VIDEO_TUNER_LOW;
+ fmr2_unit.card_type = 0;
+ fmr2_radio.priv = &fmr2_unit;
+
+ mutex_init(&lock);
+
+ if (request_region(io, 2, "sf16fmr2"))
+ {
+ printk(KERN_ERR "fmr2: port 0x%x already in use\n", io);
+ return -EBUSY;
+ }
+
+ if(video_register_device(&fmr2_radio, VFL_TYPE_RADIO, radio_nr)==-1)
+ {
+ release_region(io, 2);
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "SF16FMR2 radio card driver at 0x%x.\n", io);
+ debug_print((KERN_DEBUG "Mute %d Low %d\n",VIDEO_AUDIO_MUTE,VIDEO_TUNER_LOW));
+ /* mute card - prevents noisy bootups */
+ mutex_lock(&lock);
+ fmr2_mute(io);
+ fmr2_product_info(&fmr2_unit);
+ mutex_unlock(&lock);
+ debug_print((KERN_DEBUG "card_type %d\n", fmr2_unit.card_type));
+ return 0;
+}
+
+MODULE_AUTHOR("Ziglio Frediano, freddy77@angelfire.com");
+MODULE_DESCRIPTION("A driver for the SF16FMR2 radio.");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "I/O address of the SF16FMR2 card (should be 0x384, if do not work try 0x284)");
+module_param(radio_nr, int, 0);
+
+static void __exit fmr2_cleanup_module(void)
+{
+ video_unregister_device(&fmr2_radio);
+ release_region(io,2);
+}
+
+module_init(fmr2_init);
+module_exit(fmr2_cleanup_module);
+
+#ifndef MODULE
+
+static int __init fmr2_setup_io(char *str)
+{
+ get_option(&str, &io);
+ return 1;
+}
+
+__setup("sf16fmr2=", fmr2_setup_io);
+
+#endif
diff --git a/linux/drivers/media/radio/radio-terratec.c b/linux/drivers/media/radio/radio-terratec.c
new file mode 100644
index 000000000..ee8cc71a0
--- /dev/null
+++ b/linux/drivers/media/radio/radio-terratec.c
@@ -0,0 +1,343 @@
+/* Terratec ActiveRadio ISA Standalone card driver for Linux radio support
+ * (c) 1999 R. Offermanns (rolf@offermanns.de)
+ * based on the aimslab radio driver from M. Kirkwood
+ * many thanks to Michael Becker and Friedhelm Birth (from TerraTec)
+ *
+ *
+ * History:
+ * 1999-05-21 First preview release
+ *
+ * Notes on the hardware:
+ * There are two "main" chips on the card:
+ * - Philips OM5610 (http://www-us.semiconductors.philips.com/acrobat/datasheets/OM5610_2.pdf)
+ * - Philips SAA6588 (http://www-us.semiconductors.philips.com/acrobat/datasheets/SAA6588_1.pdf)
+ * (you can get the datasheet at the above links)
+ *
+ * Frequency control is done digitally -- ie out(port,encodefreq(95.8));
+ * Volume Control is done digitally
+ *
+ * there is a I2C controlled RDS decoder (SAA6588) onboard, which i would like to support someday
+ * (as soon i have understand how to get started :)
+ * If you can help me out with that, please contact me!!
+ *
+ *
+ */
+
+#include <linux/module.h> /* Modules */
+#include <linux/init.h> /* Initdata */
+#include <linux/ioport.h> /* request_region */
+#include <linux/delay.h> /* udelay */
+#include <asm/io.h> /* outb, outb_p */
+#include <asm/uaccess.h> /* copy to/from user */
+#include "compat.h"
+#include <linux/videodev.h> /* kernel radio structs */
+#include <linux/config.h> /* CONFIG_RADIO_TERRATEC_PORT */
+#include <linux/spinlock.h>
+
+#ifndef CONFIG_RADIO_TERRATEC_PORT
+#define CONFIG_RADIO_TERRATEC_PORT 0x590
+#endif
+
+/**************** this ones are for the terratec *******************/
+#define BASEPORT 0x590
+#define VOLPORT 0x591
+#define WRT_DIS 0x00
+#define CLK_OFF 0x00
+#define IIC_DATA 0x01
+#define IIC_CLK 0x02
+#define DATA 0x04
+#define CLK_ON 0x08
+#define WRT_EN 0x10
+/*******************************************************************/
+
+static int io = CONFIG_RADIO_TERRATEC_PORT;
+static int radio_nr = -1;
+static spinlock_t lock;
+
+struct tt_device
+{
+ int port;
+ int curvol;
+ unsigned long curfreq;
+ int muted;
+};
+
+
+/* local things */
+
+static void cardWriteVol(int volume)
+{
+ int i;
+ volume = volume+(volume * 32); // change both channels
+ spin_lock(&lock);
+ for (i=0;i<8;i++)
+ {
+ if (volume & (0x80>>i))
+ outb(0x80, VOLPORT);
+ else outb(0x00, VOLPORT);
+ }
+ spin_unlock(&lock);
+}
+
+
+
+static void tt_mute(struct tt_device *dev)
+{
+ dev->muted = 1;
+ cardWriteVol(0);
+}
+
+static int tt_setvol(struct tt_device *dev, int vol)
+{
+
+// printk(KERN_ERR "setvol called, vol = %d\n", vol);
+
+ if(vol == dev->curvol) { /* requested volume = current */
+ if (dev->muted) { /* user is unmuting the card */
+ dev->muted = 0;
+ cardWriteVol(vol); /* enable card */
+ }
+
+ return 0;
+ }
+
+ if(vol == 0) { /* volume = 0 means mute the card */
+ cardWriteVol(0); /* "turn off card" by setting vol to 0 */
+ dev->curvol = vol; /* track the volume state! */
+ return 0;
+ }
+
+ dev->muted = 0;
+
+ cardWriteVol(vol);
+
+ dev->curvol = vol;
+
+ return 0;
+
+}
+
+
+/* this is the worst part in this driver */
+/* many more or less strange things are going on here, but hey, it works :) */
+
+static int tt_setfreq(struct tt_device *dev, unsigned long freq1)
+{
+ int freq;
+ int i;
+ int p;
+ int temp;
+ long rest;
+
+ unsigned char buffer[25]; /* we have to bit shift 25 registers */
+ freq = freq1/160; /* convert the freq. to a nice to handle value */
+ for(i=24;i>-1;i--)
+ buffer[i]=0;
+
+ rest = freq*10+10700; /* i once had understood what is going on here */
+ /* maybe some wise guy (friedhelm?) can comment this stuff */
+ i=13;
+ p=10;
+ temp=102400;
+ while (rest!=0)
+ {
+ if (rest%temp == rest)
+ buffer[i] = 0;
+ else
+ {
+ buffer[i] = 1;
+ rest = rest-temp;
+ }
+ i--;
+ p--;
+ temp = temp/2;
+ }
+
+ spin_lock(&lock);
+
+ for (i=24;i>-1;i--) /* bit shift the values to the radiocard */
+ {
+ if (buffer[i]==1)
+ {
+ outb(WRT_EN|DATA, BASEPORT);
+ outb(WRT_EN|DATA|CLK_ON , BASEPORT);
+ outb(WRT_EN|DATA, BASEPORT);
+ }
+ else
+ {
+ outb(WRT_EN|0x00, BASEPORT);
+ outb(WRT_EN|0x00|CLK_ON , BASEPORT);
+ }
+ }
+ outb(0x00, BASEPORT);
+
+ spin_unlock(&lock);
+
+ return 0;
+}
+
+static int tt_getsigstr(struct tt_device *dev) /* TODO */
+{
+ if (inb(io) & 2) /* bit set = no signal present */
+ return 0;
+ return 1; /* signal present */
+}
+
+
+/* implement the video4linux api */
+
+static int tt_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct tt_device *tt=dev->priv;
+
+ switch(cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *v = arg;
+ memset(v,0,sizeof(*v));
+ v->type=VID_TYPE_TUNER;
+ v->channels=1;
+ v->audios=1;
+ strcpy(v->name, "ActiveRadio");
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner *v = arg;
+ if(v->tuner) /* Only 1 tuner */
+ return -EINVAL;
+ v->rangelow=(87*16000);
+ v->rangehigh=(108*16000);
+ v->flags=VIDEO_TUNER_LOW;
+ v->mode=VIDEO_MODE_AUTO;
+ strcpy(v->name, "FM");
+ v->signal=0xFFFF*tt_getsigstr(tt);
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner *v = arg;
+ if(v->tuner!=0)
+ return -EINVAL;
+ /* Only 1 tuner so no setting needed ! */
+ return 0;
+ }
+ case VIDIOCGFREQ:
+ {
+ unsigned long *freq = arg;
+ *freq = tt->curfreq;
+ return 0;
+ }
+ case VIDIOCSFREQ:
+ {
+ unsigned long *freq = arg;
+ tt->curfreq = *freq;
+ tt_setfreq(tt, tt->curfreq);
+ return 0;
+ }
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio *v = arg;
+ memset(v,0, sizeof(*v));
+ v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
+ v->volume=tt->curvol * 6554;
+ v->step=6554;
+ strcpy(v->name, "Radio");
+ return 0;
+ }
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio *v = arg;
+ if(v->audio)
+ return -EINVAL;
+ if(v->flags&VIDEO_AUDIO_MUTE)
+ tt_mute(tt);
+ else
+ tt_setvol(tt,v->volume/6554);
+ return 0;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int tt_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, tt_do_ioctl);
+}
+
+static struct tt_device terratec_unit;
+
+static struct file_operations terratec_fops = {
+ .owner = THIS_MODULE,
+ .open = video_exclusive_open,
+ .release = video_exclusive_release,
+ .ioctl = tt_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device terratec_radio=
+{
+ .owner = THIS_MODULE,
+ .name = "TerraTec ActiveRadio",
+ .type = VID_TYPE_TUNER,
+ .hardware = VID_HARDWARE_TERRATEC,
+ .fops = &terratec_fops,
+};
+
+static int __init terratec_init(void)
+{
+ if(io==-1)
+ {
+ printk(KERN_ERR "You must set an I/O address with io=0x???\n");
+ return -EINVAL;
+ }
+ if (!request_region(io, 2, "terratec"))
+ {
+ printk(KERN_ERR "TerraTec: port 0x%x already in use\n", io);
+ return -EBUSY;
+ }
+
+ terratec_radio.priv=&terratec_unit;
+
+ spin_lock_init(&lock);
+
+ if(video_register_device(&terratec_radio, VFL_TYPE_RADIO, radio_nr)==-1)
+ {
+ release_region(io,2);
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "TERRATEC ActivRadio Standalone card driver.\n");
+
+ /* mute card - prevents noisy bootups */
+
+ /* this ensures that the volume is all the way down */
+ cardWriteVol(0);
+ terratec_unit.curvol = 0;
+
+ return 0;
+}
+
+MODULE_AUTHOR("R.OFFERMANNS & others");
+MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card.");
+MODULE_LICENSE("GPL");
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "I/O address of the TerraTec ActiveRadio card (0x590 or 0x591)");
+module_param(radio_nr, int, 0);
+
+static void __exit terratec_cleanup_module(void)
+{
+ video_unregister_device(&terratec_radio);
+ release_region(io,2);
+ printk(KERN_INFO "TERRATEC ActivRadio Standalone card driver unloaded.\n");
+}
+
+module_init(terratec_init);
+module_exit(terratec_cleanup_module);
+
diff --git a/linux/drivers/media/radio/radio-trust.c b/linux/drivers/media/radio/radio-trust.c
new file mode 100644
index 000000000..448725ca9
--- /dev/null
+++ b/linux/drivers/media/radio/radio-trust.c
@@ -0,0 +1,322 @@
+/* radio-trust.c - Trust FM Radio card driver for Linux 2.2
+ * by Eric Lammerts <eric@scintilla.utwente.nl>
+ *
+ * Based on radio-aztech.c. Original notes:
+ *
+ * Adapted to support the Video for Linux API by
+ * Russell Kroll <rkroll@exploits.org>. Based on original tuner code by:
+ *
+ * Quay Ly
+ * Donald Song
+ * Jason Lewis (jlewis@twilight.vtc.vsc.edu)
+ * Scott McGrath (smcgrath@twilight.vtc.vsc.edu)
+ * William McGrath (wmcgrath@twilight.vtc.vsc.edu)
+ *
+ * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/
+ */
+
+#include <stdarg.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include "compat.h"
+#include <linux/videodev.h>
+#include <linux/config.h> /* CONFIG_RADIO_TRUST_PORT */
+
+/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
+
+#ifndef CONFIG_RADIO_TRUST_PORT
+#define CONFIG_RADIO_TRUST_PORT -1
+#endif
+
+static int io = CONFIG_RADIO_TRUST_PORT;
+static int radio_nr = -1;
+static int ioval = 0xf;
+static __u16 curvol;
+static __u16 curbass;
+static __u16 curtreble;
+static unsigned long curfreq;
+static int curstereo;
+static int curmute;
+
+/* i2c addresses */
+#define TDA7318_ADDR 0x88
+#define TSA6060T_ADDR 0xc4
+
+#define TR_DELAY do { inb(io); inb(io); inb(io); } while(0)
+#define TR_SET_SCL outb(ioval |= 2, io)
+#define TR_CLR_SCL outb(ioval &= 0xfd, io)
+#define TR_SET_SDA outb(ioval |= 1, io)
+#define TR_CLR_SDA outb(ioval &= 0xfe, io)
+
+static void write_i2c(int n, ...)
+{
+ unsigned char val, mask;
+ va_list args;
+
+ va_start(args, n);
+
+ /* start condition */
+ TR_SET_SDA;
+ TR_SET_SCL;
+ TR_DELAY;
+ TR_CLR_SDA;
+ TR_CLR_SCL;
+ TR_DELAY;
+
+ for(; n; n--) {
+ val = va_arg(args, unsigned);
+ for(mask = 0x80; mask; mask >>= 1) {
+ if(val & mask)
+ TR_SET_SDA;
+ else
+ TR_CLR_SDA;
+ TR_SET_SCL;
+ TR_DELAY;
+ TR_CLR_SCL;
+ TR_DELAY;
+ }
+ /* acknowledge bit */
+ TR_SET_SDA;
+ TR_SET_SCL;
+ TR_DELAY;
+ TR_CLR_SCL;
+ TR_DELAY;
+ }
+
+ /* stop condition */
+ TR_CLR_SDA;
+ TR_DELAY;
+ TR_SET_SCL;
+ TR_DELAY;
+ TR_SET_SDA;
+ TR_DELAY;
+
+ va_end(args);
+}
+
+static void tr_setvol(__u16 vol)
+{
+ curvol = vol / 2048;
+ write_i2c(2, TDA7318_ADDR, curvol ^ 0x1f);
+}
+
+static int basstreble2chip[15] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8
+};
+
+static void tr_setbass(__u16 bass)
+{
+ curbass = bass / 4370;
+ write_i2c(2, TDA7318_ADDR, 0x60 | basstreble2chip[curbass]);
+}
+
+static void tr_settreble(__u16 treble)
+{
+ curtreble = treble / 4370;
+ write_i2c(2, TDA7318_ADDR, 0x70 | basstreble2chip[curtreble]);
+}
+
+static void tr_setstereo(int stereo)
+{
+ curstereo = !!stereo;
+ ioval = (ioval & 0xfb) | (!curstereo << 2);
+ outb(ioval, io);
+}
+
+static void tr_setmute(int mute)
+{
+ curmute = !!mute;
+ ioval = (ioval & 0xf7) | (curmute << 3);
+ outb(ioval, io);
+}
+
+static int tr_getsigstr(void)
+{
+ int i, v;
+
+ for(i = 0, v = 0; i < 100; i++) v |= inb(io);
+ return (v & 1)? 0 : 0xffff;
+}
+
+static int tr_getstereo(void)
+{
+ /* don't know how to determine it, just return the setting */
+ return curstereo;
+}
+
+static void tr_setfreq(unsigned long f)
+{
+ f /= 160; /* Convert to 10 kHz units */
+ f += 1070; /* Add 10.7 MHz IF */
+
+ write_i2c(5, TSA6060T_ADDR, (f << 1) | 1, f >> 7, 0x60 | ((f >> 15) & 1), 0);
+}
+
+static int tr_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ switch(cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *v = arg;
+
+ memset(v,0,sizeof(*v));
+ v->type=VID_TYPE_TUNER;
+ v->channels=1;
+ v->audios=1;
+ strcpy(v->name, "Trust FM Radio");
+
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner *v = arg;
+
+ if(v->tuner) /* Only 1 tuner */
+ return -EINVAL;
+
+ v->rangelow = 87500 * 16;
+ v->rangehigh = 108000 * 16;
+ v->flags = VIDEO_TUNER_LOW;
+ v->mode = VIDEO_MODE_AUTO;
+
+ v->signal = tr_getsigstr();
+ if(tr_getstereo())
+ v->flags |= VIDEO_TUNER_STEREO_ON;
+
+ strcpy(v->name, "FM");
+
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner *v = arg;
+ if(v->tuner != 0)
+ return -EINVAL;
+ return 0;
+ }
+ case VIDIOCGFREQ:
+ {
+ unsigned long *freq = arg;
+ *freq = curfreq;
+ return 0;
+ }
+ case VIDIOCSFREQ:
+ {
+ unsigned long *freq = arg;
+ tr_setfreq(*freq);
+ return 0;
+ }
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio *v = arg;
+
+ memset(v,0, sizeof(*v));
+ v->flags = VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME |
+ VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE;
+ v->mode = curstereo? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
+ v->volume = curvol * 2048;
+ v->step = 2048;
+ v->bass = curbass * 4370;
+ v->treble = curtreble * 4370;
+
+ strcpy(v->name, "Trust FM Radio");
+ return 0;
+ }
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio *v = arg;
+
+ if(v->audio)
+ return -EINVAL;
+ tr_setvol(v->volume);
+ tr_setbass(v->bass);
+ tr_settreble(v->treble);
+ tr_setstereo(v->mode & VIDEO_SOUND_STEREO);
+ tr_setmute(v->flags & VIDEO_AUDIO_MUTE);
+ return 0;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int tr_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, tr_do_ioctl);
+}
+
+static struct file_operations trust_fops = {
+ .owner = THIS_MODULE,
+ .open = video_exclusive_open,
+ .release = video_exclusive_release,
+ .ioctl = tr_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device trust_radio=
+{
+ .owner = THIS_MODULE,
+ .name = "Trust FM Radio",
+ .type = VID_TYPE_TUNER,
+ .hardware = VID_HARDWARE_TRUST,
+ .fops = &trust_fops,
+};
+
+static int __init trust_init(void)
+{
+ if(io == -1) {
+ printk(KERN_ERR "You must set an I/O address with io=0x???\n");
+ return -EINVAL;
+ }
+ if(!request_region(io, 2, "Trust FM Radio")) {
+ printk(KERN_ERR "trust: port 0x%x already in use\n", io);
+ return -EBUSY;
+ }
+ if(video_register_device(&trust_radio, VFL_TYPE_RADIO, radio_nr)==-1)
+ {
+ release_region(io, 2);
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "Trust FM Radio card driver v1.0.\n");
+
+ write_i2c(2, TDA7318_ADDR, 0x80); /* speaker att. LF = 0 dB */
+ write_i2c(2, TDA7318_ADDR, 0xa0); /* speaker att. RF = 0 dB */
+ write_i2c(2, TDA7318_ADDR, 0xc0); /* speaker att. LR = 0 dB */
+ write_i2c(2, TDA7318_ADDR, 0xe0); /* speaker att. RR = 0 dB */
+ write_i2c(2, TDA7318_ADDR, 0x40); /* stereo 1 input, gain = 18.75 dB */
+
+ tr_setvol(0x8000);
+ tr_setbass(0x8000);
+ tr_settreble(0x8000);
+ tr_setstereo(1);
+
+ /* mute card - prevents noisy bootups */
+ tr_setmute(1);
+
+ return 0;
+}
+
+MODULE_AUTHOR("Eric Lammerts, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
+MODULE_DESCRIPTION("A driver for the Trust FM Radio card.");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "I/O address of the Trust FM Radio card (0x350 or 0x358)");
+module_param(radio_nr, int, 0);
+
+static void __exit cleanup_trust_module(void)
+{
+ video_unregister_device(&trust_radio);
+ release_region(io, 2);
+}
+
+module_init(trust_init);
+module_exit(cleanup_trust_module);
diff --git a/linux/drivers/media/radio/radio-typhoon.c b/linux/drivers/media/radio/radio-typhoon.c
new file mode 100644
index 000000000..8e87d0b42
--- /dev/null
+++ b/linux/drivers/media/radio/radio-typhoon.c
@@ -0,0 +1,385 @@
+/* Typhoon Radio Card driver for radio support
+ * (c) 1999 Dr. Henrik Seidel <Henrik.Seidel@gmx.de>
+ *
+ * Card manufacturer:
+ * http://194.18.155.92/idc/prod2.idc?nr=50753&lang=e
+ *
+ * Notes on the hardware
+ *
+ * This card has two output sockets, one for speakers and one for line.
+ * The speaker output has volume control, but only in four discrete
+ * steps. The line output has neither volume control nor mute.
+ *
+ * The card has auto-stereo according to its manual, although it all
+ * sounds mono to me (even with the Win/DOS drivers). Maybe it's my
+ * antenna - I really don't know for sure.
+ *
+ * Frequency control is done digitally.
+ *
+ * Volume control is done digitally, but there are only four different
+ * possible values. So you should better always turn the volume up and
+ * use line control. I got the best results by connecting line output
+ * to the sound card microphone input. For such a configuration the
+ * volume control has no effect, since volume control only influences
+ * the speaker output.
+ *
+ * There is no explicit mute/unmute. So I set the radio frequency to a
+ * value where I do expect just noise and turn the speaker volume down.
+ * The frequency change is necessary since the card never seems to be
+ * completely silent.
+ */
+
+#include <linux/module.h> /* Modules */
+#include <linux/init.h> /* Initdata */
+#include <linux/ioport.h> /* request_region */
+#include <linux/proc_fs.h> /* radio card status report */
+#include <asm/io.h> /* outb, outb_p */
+#include <asm/uaccess.h> /* copy to/from user */
+#include "compat.h"
+#include <linux/videodev.h> /* kernel radio structs */
+#include <linux/config.h> /* CONFIG_RADIO_TYPHOON_* */
+
+#define BANNER "Typhoon Radio Card driver v0.1\n"
+
+#ifndef CONFIG_RADIO_TYPHOON_PORT
+#define CONFIG_RADIO_TYPHOON_PORT -1
+#endif
+
+#ifndef CONFIG_RADIO_TYPHOON_MUTEFREQ
+#define CONFIG_RADIO_TYPHOON_MUTEFREQ 0
+#endif
+
+#ifndef CONFIG_PROC_FS
+#undef CONFIG_RADIO_TYPHOON_PROC_FS
+#endif
+
+struct typhoon_device {
+ int users;
+ int iobase;
+ int curvol;
+ int muted;
+ unsigned long curfreq;
+ unsigned long mutefreq;
+ struct mutex lock;
+};
+
+static void typhoon_setvol_generic(struct typhoon_device *dev, int vol);
+static int typhoon_setfreq_generic(struct typhoon_device *dev,
+ unsigned long frequency);
+static int typhoon_setfreq(struct typhoon_device *dev, unsigned long frequency);
+static void typhoon_mute(struct typhoon_device *dev);
+static void typhoon_unmute(struct typhoon_device *dev);
+static int typhoon_setvol(struct typhoon_device *dev, int vol);
+static int typhoon_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+#ifdef CONFIG_RADIO_TYPHOON_PROC_FS
+static int typhoon_get_info(char *buf, char **start, off_t offset, int len);
+#endif
+
+static void typhoon_setvol_generic(struct typhoon_device *dev, int vol)
+{
+ mutex_lock(&dev->lock);
+ vol >>= 14; /* Map 16 bit to 2 bit */
+ vol &= 3;
+ outb_p(vol / 2, dev->iobase); /* Set the volume, high bit. */
+ outb_p(vol % 2, dev->iobase + 2); /* Set the volume, low bit. */
+ mutex_unlock(&dev->lock);
+}
+
+static int typhoon_setfreq_generic(struct typhoon_device *dev,
+ unsigned long frequency)
+{
+ unsigned long outval;
+ unsigned long x;
+
+ /*
+ * The frequency transfer curve is not linear. The best fit I could
+ * get is
+ *
+ * outval = -155 + exp((f + 15.55) * 0.057))
+ *
+ * where frequency f is in MHz. Since we don't have exp in the kernel,
+ * I approximate this function by a third order polynomial.
+ *
+ */
+
+ mutex_lock(&dev->lock);
+ x = frequency / 160;
+ outval = (x * x + 2500) / 5000;
+ outval = (outval * x + 5000) / 10000;
+ outval -= (10 * x * x + 10433) / 20866;
+ outval += 4 * x - 11505;
+
+ outb_p((outval >> 8) & 0x01, dev->iobase + 4);
+ outb_p(outval >> 9, dev->iobase + 6);
+ outb_p(outval & 0xff, dev->iobase + 8);
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+static int typhoon_setfreq(struct typhoon_device *dev, unsigned long frequency)
+{
+ typhoon_setfreq_generic(dev, frequency);
+ dev->curfreq = frequency;
+ return 0;
+}
+
+static void typhoon_mute(struct typhoon_device *dev)
+{
+ if (dev->muted == 1)
+ return;
+ typhoon_setvol_generic(dev, 0);
+ typhoon_setfreq_generic(dev, dev->mutefreq);
+ dev->muted = 1;
+}
+
+static void typhoon_unmute(struct typhoon_device *dev)
+{
+ if (dev->muted == 0)
+ return;
+ typhoon_setfreq_generic(dev, dev->curfreq);
+ typhoon_setvol_generic(dev, dev->curvol);
+ dev->muted = 0;
+}
+
+static int typhoon_setvol(struct typhoon_device *dev, int vol)
+{
+ if (dev->muted && vol != 0) { /* user is unmuting the card */
+ dev->curvol = vol;
+ typhoon_unmute(dev);
+ return 0;
+ }
+ if (vol == dev->curvol) /* requested volume == current */
+ return 0;
+
+ if (vol == 0) { /* volume == 0 means mute the card */
+ typhoon_mute(dev);
+ dev->curvol = vol;
+ return 0;
+ }
+ typhoon_setvol_generic(dev, vol);
+ dev->curvol = vol;
+ return 0;
+}
+
+
+static int typhoon_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct typhoon_device *typhoon = dev->priv;
+
+ switch (cmd) {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *v = arg;
+ memset(v,0,sizeof(*v));
+ v->type = VID_TYPE_TUNER;
+ v->channels = 1;
+ v->audios = 1;
+ strcpy(v->name, "Typhoon Radio");
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner *v = arg;
+ if (v->tuner) /* Only 1 tuner */
+ return -EINVAL;
+ v->rangelow = 875 * 1600;
+ v->rangehigh = 1080 * 1600;
+ v->flags = VIDEO_TUNER_LOW;
+ v->mode = VIDEO_MODE_AUTO;
+ v->signal = 0xFFFF; /* We can't get the signal strength */
+ strcpy(v->name, "FM");
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner *v = arg;
+ if (v->tuner != 0)
+ return -EINVAL;
+ /* Only 1 tuner so no setting needed ! */
+ return 0;
+ }
+ case VIDIOCGFREQ:
+ {
+ unsigned long *freq = arg;
+ *freq = typhoon->curfreq;
+ return 0;
+ }
+ case VIDIOCSFREQ:
+ {
+ unsigned long *freq = arg;
+ typhoon->curfreq = *freq;
+ typhoon_setfreq(typhoon, typhoon->curfreq);
+ return 0;
+ }
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio *v = arg;
+ memset(v, 0, sizeof(*v));
+ v->flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME;
+ v->mode |= VIDEO_SOUND_MONO;
+ v->volume = typhoon->curvol;
+ v->step = 1 << 14;
+ strcpy(v->name, "Typhoon Radio");
+ return 0;
+ }
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio *v = arg;
+ if (v->audio)
+ return -EINVAL;
+ if (v->flags & VIDEO_AUDIO_MUTE)
+ typhoon_mute(typhoon);
+ else
+ typhoon_unmute(typhoon);
+ if (v->flags & VIDEO_AUDIO_VOLUME)
+ typhoon_setvol(typhoon, v->volume);
+ return 0;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int typhoon_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, typhoon_do_ioctl);
+}
+
+static struct typhoon_device typhoon_unit =
+{
+ .iobase = CONFIG_RADIO_TYPHOON_PORT,
+ .curfreq = CONFIG_RADIO_TYPHOON_MUTEFREQ,
+ .mutefreq = CONFIG_RADIO_TYPHOON_MUTEFREQ,
+};
+
+static struct file_operations typhoon_fops = {
+ .owner = THIS_MODULE,
+ .open = video_exclusive_open,
+ .release = video_exclusive_release,
+ .ioctl = typhoon_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device typhoon_radio =
+{
+ .owner = THIS_MODULE,
+ .name = "Typhoon Radio",
+ .type = VID_TYPE_TUNER,
+ .hardware = VID_HARDWARE_TYPHOON,
+ .fops = &typhoon_fops,
+};
+
+#ifdef CONFIG_RADIO_TYPHOON_PROC_FS
+
+static int typhoon_get_info(char *buf, char **start, off_t offset, int len)
+{
+ char *out = buf;
+
+ #ifdef MODULE
+ #define MODULEPROCSTRING "Driver loaded as a module"
+ #else
+ #define MODULEPROCSTRING "Driver compiled into kernel"
+ #endif
+
+ /* output must be kept under PAGE_SIZE */
+ out += sprintf(out, BANNER);
+ out += sprintf(out, "Load type: " MODULEPROCSTRING "\n\n");
+ out += sprintf(out, "frequency = %lu kHz\n",
+ typhoon_unit.curfreq >> 4);
+ out += sprintf(out, "volume = %d\n", typhoon_unit.curvol);
+ out += sprintf(out, "mute = %s\n", typhoon_unit.muted ?
+ "on" : "off");
+ out += sprintf(out, "iobase = 0x%x\n", typhoon_unit.iobase);
+ out += sprintf(out, "mute frequency = %lu kHz\n",
+ typhoon_unit.mutefreq >> 4);
+ return out - buf;
+}
+
+#endif /* CONFIG_RADIO_TYPHOON_PROC_FS */
+
+MODULE_AUTHOR("Dr. Henrik Seidel");
+MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio).");
+MODULE_LICENSE("GPL");
+
+static int io = -1;
+static int radio_nr = -1;
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "I/O address of the Typhoon card (0x316 or 0x336)");
+module_param(radio_nr, int, 0);
+
+#ifdef MODULE
+static unsigned long mutefreq = 0;
+module_param(mutefreq, ulong, 0);
+MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)");
+#endif
+
+static int __init typhoon_init(void)
+{
+#ifdef MODULE
+ if (io == -1) {
+ printk(KERN_ERR "radio-typhoon: You must set an I/O address with io=0x316 or io=0x336\n");
+ return -EINVAL;
+ }
+ typhoon_unit.iobase = io;
+
+ if (mutefreq < 87000 || mutefreq > 108500) {
+ printk(KERN_ERR "radio-typhoon: You must set a frequency (in kHz) used when muting the card,\n");
+ printk(KERN_ERR "radio-typhoon: e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108500)\n");
+ return -EINVAL;
+ }
+ typhoon_unit.mutefreq = mutefreq;
+#endif /* MODULE */
+
+ printk(KERN_INFO BANNER);
+ mutex_init(&typhoon_unit.lock);
+ io = typhoon_unit.iobase;
+ if (!request_region(io, 8, "typhoon")) {
+ printk(KERN_ERR "radio-typhoon: port 0x%x already in use\n",
+ typhoon_unit.iobase);
+ return -EBUSY;
+ }
+
+ typhoon_radio.priv = &typhoon_unit;
+ if (video_register_device(&typhoon_radio, VFL_TYPE_RADIO, radio_nr) == -1)
+ {
+ release_region(io, 8);
+ return -EINVAL;
+ }
+ printk(KERN_INFO "radio-typhoon: port 0x%x.\n", typhoon_unit.iobase);
+ printk(KERN_INFO "radio-typhoon: mute frequency is %lu kHz.\n",
+ typhoon_unit.mutefreq);
+ typhoon_unit.mutefreq <<= 4;
+
+ /* mute card - prevents noisy bootups */
+ typhoon_mute(&typhoon_unit);
+
+#ifdef CONFIG_RADIO_TYPHOON_PROC_FS
+ if (!create_proc_info_entry("driver/radio-typhoon", 0, NULL,
+ typhoon_get_info))
+ printk(KERN_ERR "radio-typhoon: registering /proc/driver/radio-typhoon failed\n");
+#endif
+
+ return 0;
+}
+
+static void __exit typhoon_cleanup_module(void)
+{
+
+#ifdef CONFIG_RADIO_TYPHOON_PROC_FS
+ remove_proc_entry("driver/radio-typhoon", NULL);
+#endif
+
+ video_unregister_device(&typhoon_radio);
+ release_region(io, 8);
+}
+
+module_init(typhoon_init);
+module_exit(typhoon_cleanup_module);
+
diff --git a/linux/drivers/media/radio/radio-zoltrix.c b/linux/drivers/media/radio/radio-zoltrix.c
new file mode 100644
index 000000000..7c7760e26
--- /dev/null
+++ b/linux/drivers/media/radio/radio-zoltrix.c
@@ -0,0 +1,387 @@
+/* zoltrix radio plus driver for Linux radio support
+ * (c) 1998 C. van Schaik <carl@leg.uct.ac.za>
+ *
+ * BUGS
+ * Due to the inconsistency in reading from the signal flags
+ * it is difficult to get an accurate tuned signal.
+ *
+ * It seems that the card is not linear to 0 volume. It cuts off
+ * at a low volume, and it is not possible (at least I have not found)
+ * to get fine volume control over the low volume range.
+ *
+ * Some code derived from code by Romolo Manfredini
+ * romolo@bicnet.it
+ *
+ * 1999-05-06 - (C. van Schaik)
+ * - Make signal strength and stereo scans
+ * kinder to cpu while in delay
+ * 1999-01-05 - (C. van Schaik)
+ * - Changed tuning to 1/160Mhz accuracy
+ * - Added stereo support
+ * (card defaults to stereo)
+ * (can explicitly force mono on the card)
+ * (can detect if station is in stereo)
+ * - Added unmute function
+ * - Reworked ioctl functions
+ * 2002-07-15 - Fix Stereo typo
+ */
+
+#include <linux/module.h> /* Modules */
+#include <linux/init.h> /* Initdata */
+#include <linux/ioport.h> /* request_region */
+#include <linux/delay.h> /* udelay, msleep */
+#include <asm/io.h> /* outb, outb_p */
+#include <asm/uaccess.h> /* copy to/from user */
+#include "compat.h"
+#include <linux/videodev.h> /* kernel radio structs */
+#include <linux/config.h> /* CONFIG_RADIO_ZOLTRIX_PORT */
+
+#ifndef CONFIG_RADIO_ZOLTRIX_PORT
+#define CONFIG_RADIO_ZOLTRIX_PORT -1
+#endif
+
+static int io = CONFIG_RADIO_ZOLTRIX_PORT;
+static int radio_nr = -1;
+
+struct zol_device {
+ int port;
+ int curvol;
+ unsigned long curfreq;
+ int muted;
+ unsigned int stereo;
+ struct mutex lock;
+};
+
+static int zol_setvol(struct zol_device *dev, int vol)
+{
+ dev->curvol = vol;
+ if (dev->muted)
+ return 0;
+
+ mutex_lock(&dev->lock);
+ if (vol == 0) {
+ outb(0, io);
+ outb(0, io);
+ inb(io + 3); /* Zoltrix needs to be read to confirm */
+ mutex_unlock(&dev->lock);
+ return 0;
+ }
+
+ outb(dev->curvol-1, io);
+ msleep(10);
+ inb(io + 2);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static void zol_mute(struct zol_device *dev)
+{
+ dev->muted = 1;
+ mutex_lock(&dev->lock);
+ outb(0, io);
+ outb(0, io);
+ inb(io + 3); /* Zoltrix needs to be read to confirm */
+ mutex_unlock(&dev->lock);
+}
+
+static void zol_unmute(struct zol_device *dev)
+{
+ dev->muted = 0;
+ zol_setvol(dev, dev->curvol);
+}
+
+static int zol_setfreq(struct zol_device *dev, unsigned long freq)
+{
+ /* tunes the radio to the desired frequency */
+ unsigned long long bitmask, f, m;
+ unsigned int stereo = dev->stereo;
+ int i;
+
+ if (freq == 0)
+ return 1;
+ m = (freq / 160 - 8800) * 2;
+ f = (unsigned long long) m + 0x4d1c;
+
+ bitmask = 0xc480402c10080000ull;
+ i = 45;
+
+ mutex_lock(&dev->lock);
+
+ outb(0, io);
+ outb(0, io);
+ inb(io + 3); /* Zoltrix needs to be read to confirm */
+
+ outb(0x40, io);
+ outb(0xc0, io);
+
+ bitmask = (bitmask ^ ((f & 0xff) << 47) ^ ((f & 0xff00) << 30) ^ ( stereo << 31));
+ while (i--) {
+ if ((bitmask & 0x8000000000000000ull) != 0) {
+ outb(0x80, io);
+ udelay(50);
+ outb(0x00, io);
+ udelay(50);
+ outb(0x80, io);
+ udelay(50);
+ } else {
+ outb(0xc0, io);
+ udelay(50);
+ outb(0x40, io);
+ udelay(50);
+ outb(0xc0, io);
+ udelay(50);
+ }
+ bitmask *= 2;
+ }
+ /* termination sequence */
+ outb(0x80, io);
+ outb(0xc0, io);
+ outb(0x40, io);
+ udelay(1000);
+ inb(io+2);
+
+ udelay(1000);
+
+ if (dev->muted)
+ {
+ outb(0, io);
+ outb(0, io);
+ inb(io + 3);
+ udelay(1000);
+ }
+
+ mutex_unlock(&dev->lock);
+
+ if(!dev->muted)
+ {
+ zol_setvol(dev, dev->curvol);
+ }
+ return 0;
+}
+
+/* Get signal strength */
+
+static int zol_getsigstr(struct zol_device *dev)
+{
+ int a, b;
+
+ mutex_lock(&dev->lock);
+ outb(0x00, io); /* This stuff I found to do nothing */
+ outb(dev->curvol, io);
+ msleep(20);
+
+ a = inb(io);
+ msleep(10);
+ b = inb(io);
+
+ mutex_unlock(&dev->lock);
+
+ if (a != b)
+ return (0);
+
+ if ((a == 0xcf) || (a == 0xdf) /* I found this out by playing */
+ || (a == 0xef)) /* with a binary scanner on the card io */
+ return (1);
+ return (0);
+}
+
+static int zol_is_stereo (struct zol_device *dev)
+{
+ int x1, x2;
+
+ mutex_lock(&dev->lock);
+
+ outb(0x00, io);
+ outb(dev->curvol, io);
+ msleep(20);
+
+ x1 = inb(io);
+ msleep(10);
+ x2 = inb(io);
+
+ mutex_unlock(&dev->lock);
+
+ if ((x1 == x2) && (x1 == 0xcf))
+ return 1;
+ return 0;
+}
+
+static int zol_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct zol_device *zol = dev->priv;
+
+ switch (cmd) {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *v = arg;
+
+ memset(v,0,sizeof(*v));
+ v->type = VID_TYPE_TUNER;
+ v->channels = 1 + zol->stereo;
+ v->audios = 1;
+ strcpy(v->name, "Zoltrix Radio");
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner *v = arg;
+ if (v->tuner)
+ return -EINVAL;
+ strcpy(v->name, "FM");
+ v->rangelow = (int) (88.0 * 16000);
+ v->rangehigh = (int) (108.0 * 16000);
+ v->flags = zol_is_stereo(zol)
+ ? VIDEO_TUNER_STEREO_ON : 0;
+ v->flags |= VIDEO_TUNER_LOW;
+ v->mode = VIDEO_MODE_AUTO;
+ v->signal = 0xFFFF * zol_getsigstr(zol);
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner *v = arg;
+ if (v->tuner != 0)
+ return -EINVAL;
+ /* Only 1 tuner so no setting needed ! */
+ return 0;
+ }
+ case VIDIOCGFREQ:
+ {
+ unsigned long *freq = arg;
+ *freq = zol->curfreq;
+ return 0;
+ }
+ case VIDIOCSFREQ:
+ {
+ unsigned long *freq = arg;
+ zol->curfreq = *freq;
+ zol_setfreq(zol, zol->curfreq);
+ return 0;
+ }
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio *v = arg;
+ memset(v, 0, sizeof(*v));
+ v->flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME;
+ v->mode |= zol_is_stereo(zol)
+ ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
+ v->volume = zol->curvol * 4096;
+ v->step = 4096;
+ strcpy(v->name, "Zoltrix Radio");
+ return 0;
+ }
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio *v = arg;
+ if (v->audio)
+ return -EINVAL;
+
+ if (v->flags & VIDEO_AUDIO_MUTE)
+ zol_mute(zol);
+ else {
+ zol_unmute(zol);
+ zol_setvol(zol, v->volume / 4096);
+ }
+
+ if (v->mode & VIDEO_SOUND_STEREO) {
+ zol->stereo = 1;
+ zol_setfreq(zol, zol->curfreq);
+ }
+ if (v->mode & VIDEO_SOUND_MONO) {
+ zol->stereo = 0;
+ zol_setfreq(zol, zol->curfreq);
+ }
+ return 0;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int zol_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, zol_do_ioctl);
+}
+
+static struct zol_device zoltrix_unit;
+
+static struct file_operations zoltrix_fops =
+{
+ .owner = THIS_MODULE,
+ .open = video_exclusive_open,
+ .release = video_exclusive_release,
+ .ioctl = zol_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device zoltrix_radio =
+{
+ .owner = THIS_MODULE,
+ .name = "Zoltrix Radio Plus",
+ .type = VID_TYPE_TUNER,
+ .hardware = VID_HARDWARE_ZOLTRIX,
+ .fops = &zoltrix_fops,
+};
+
+static int __init zoltrix_init(void)
+{
+ if (io == -1) {
+ printk(KERN_ERR "You must set an I/O address with io=0x???\n");
+ return -EINVAL;
+ }
+ if ((io != 0x20c) && (io != 0x30c)) {
+ printk(KERN_ERR "zoltrix: invalid port, try 0x20c or 0x30c\n");
+ return -ENXIO;
+ }
+
+ zoltrix_radio.priv = &zoltrix_unit;
+ if (!request_region(io, 2, "zoltrix")) {
+ printk(KERN_ERR "zoltrix: port 0x%x already in use\n", io);
+ return -EBUSY;
+ }
+
+ if (video_register_device(&zoltrix_radio, VFL_TYPE_RADIO, radio_nr) == -1)
+ {
+ release_region(io, 2);
+ return -EINVAL;
+ }
+ printk(KERN_INFO "Zoltrix Radio Plus card driver.\n");
+
+ mutex_init(&zoltrix_unit.lock);
+
+ /* mute card - prevents noisy bootups */
+
+ /* this ensures that the volume is all the way down */
+
+ outb(0, io);
+ outb(0, io);
+ msleep(20);
+ inb(io + 3);
+
+ zoltrix_unit.curvol = 0;
+ zoltrix_unit.stereo = 1;
+
+ return 0;
+}
+
+MODULE_AUTHOR("C.van Schaik");
+MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus.");
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0);
+MODULE_PARM_DESC(io, "I/O address of the Zoltrix Radio Plus (0x20c or 0x30c)");
+module_param(radio_nr, int, 0);
+
+static void __exit zoltrix_cleanup_module(void)
+{
+ video_unregister_device(&zoltrix_radio);
+ release_region(io, 2);
+}
+
+module_init(zoltrix_init);
+module_exit(zoltrix_cleanup_module);
+
diff --git a/linux/drivers/media/video/adv7170.c b/linux/drivers/media/video/adv7170.c
new file mode 100644
index 000000000..48fe2344b
--- /dev/null
+++ b/linux/drivers/media/video/adv7170.c
@@ -0,0 +1,519 @@
+/*
+ * adv7170 - adv7170, adv7171 video encoder driver version 0.0.1
+ *
+ * Copyright (C) 2002 Maxim Yevtyushkin <max@linuxmedialabs.com>
+ *
+ * Based on adv7176 driver by:
+ *
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ * Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net>
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ * - some corrections for Pinnacle Systems Inc. DC10plus card.
+ *
+ * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * - moved over to linux>=2.4.x i2c protocol (1/1/2003)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/signal.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+
+#include "compat.h"
+#include <linux/videodev.h>
+#include <asm/uaccess.h>
+
+MODULE_DESCRIPTION("Analog Devices ADV7170 video encoder driver");
+MODULE_AUTHOR("Maxim Yevtyushkin");
+MODULE_LICENSE("GPL");
+
+#include <linux/i2c.h>
+
+#define I2C_NAME(x) (x)->name
+
+#include <linux/video_encoder.h>
+
+static int debug = 0;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+/* ----------------------------------------------------------------------- */
+
+struct adv7170 {
+ unsigned char reg[128];
+
+ int norm;
+ int input;
+ int enable;
+ int bright;
+ int contrast;
+ int hue;
+ int sat;
+};
+
+#define I2C_ADV7170 0xd4
+#define I2C_ADV7171 0x54
+
+static char adv7170_name[] = "adv7170";
+static char adv7171_name[] = "adv7171";
+
+static char *inputs[] = { "pass_through", "play_back" };
+static char *norms[] = { "PAL", "NTSC" };
+
+/* ----------------------------------------------------------------------- */
+
+static inline int
+adv7170_write (struct i2c_client *client,
+ u8 reg,
+ u8 value)
+{
+ struct adv7170 *encoder = i2c_get_clientdata(client);
+
+ encoder->reg[reg] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static inline int
+adv7170_read (struct i2c_client *client,
+ u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int
+adv7170_write_block (struct i2c_client *client,
+ const u8 *data,
+ unsigned int len)
+{
+ int ret = -1;
+ u8 reg;
+
+ /* the adv7170 has an autoincrement function, use it if
+ * the adapter understands raw I2C */
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ /* do raw I2C, not smbus compatible */
+ struct adv7170 *encoder = i2c_get_clientdata(client);
+ u8 block_data[32];
+ int block_len;
+
+ while (len >= 2) {
+ block_len = 0;
+ block_data[block_len++] = reg = data[0];
+ do {
+ block_data[block_len++] =
+ encoder->reg[reg++] = data[1];
+ len -= 2;
+ data += 2;
+ } while (len >= 2 && data[0] == reg &&
+ block_len < 32);
+ if ((ret = i2c_master_send(client, block_data,
+ block_len)) < 0)
+ break;
+ }
+ } else {
+ /* do some slow I2C emulation kind of thing */
+ while (len >= 2) {
+ reg = *data++;
+ if ((ret = adv7170_write(client, reg,
+ *data++)) < 0)
+ break;
+ len -= 2;
+ }
+ }
+
+ return ret;
+}
+
+/* ----------------------------------------------------------------------- */
+// Output filter: S-Video Composite
+
+#define MR050 0x11 //0x09
+#define MR060 0x14 //0x0c
+
+//---------------------------------------------------------------------------
+
+#define TR0MODE 0x4c
+#define TR0RST 0x80
+
+#define TR1CAPT 0x00
+#define TR1PLAY 0x00
+
+
+static const unsigned char init_NTSC[] = {
+ 0x00, 0x10, // MR0
+ 0x01, 0x20, // MR1
+ 0x02, 0x0e, // MR2 RTC control: bits 2 and 1
+ 0x03, 0x80, // MR3
+ 0x04, 0x30, // MR4
+ 0x05, 0x00, // Reserved
+ 0x06, 0x00, // Reserved
+ 0x07, TR0MODE, // TM0
+ 0x08, TR1CAPT, // TM1
+ 0x09, 0x16, // Fsc0
+ 0x0a, 0x7c, // Fsc1
+ 0x0b, 0xf0, // Fsc2
+ 0x0c, 0x21, // Fsc3
+ 0x0d, 0x00, // Subcarrier Phase
+ 0x0e, 0x00, // Closed Capt. Ext 0
+ 0x0f, 0x00, // Closed Capt. Ext 1
+ 0x10, 0x00, // Closed Capt. 0
+ 0x11, 0x00, // Closed Capt. 1
+ 0x12, 0x00, // Pedestal Ctl 0
+ 0x13, 0x00, // Pedestal Ctl 1
+ 0x14, 0x00, // Pedestal Ctl 2
+ 0x15, 0x00, // Pedestal Ctl 3
+ 0x16, 0x00, // CGMS_WSS_0
+ 0x17, 0x00, // CGMS_WSS_1
+ 0x18, 0x00, // CGMS_WSS_2
+ 0x19, 0x00, // Teletext Ctl
+};
+
+static const unsigned char init_PAL[] = {
+ 0x00, 0x71, // MR0
+ 0x01, 0x20, // MR1
+ 0x02, 0x0e, // MR2 RTC control: bits 2 and 1
+ 0x03, 0x80, // MR3
+ 0x04, 0x30, // MR4
+ 0x05, 0x00, // Reserved
+ 0x06, 0x00, // Reserved
+ 0x07, TR0MODE, // TM0
+ 0x08, TR1CAPT, // TM1
+ 0x09, 0xcb, // Fsc0
+ 0x0a, 0x8a, // Fsc1
+ 0x0b, 0x09, // Fsc2
+ 0x0c, 0x2a, // Fsc3
+ 0x0d, 0x00, // Subcarrier Phase
+ 0x0e, 0x00, // Closed Capt. Ext 0
+ 0x0f, 0x00, // Closed Capt. Ext 1
+ 0x10, 0x00, // Closed Capt. 0
+ 0x11, 0x00, // Closed Capt. 1
+ 0x12, 0x00, // Pedestal Ctl 0
+ 0x13, 0x00, // Pedestal Ctl 1
+ 0x14, 0x00, // Pedestal Ctl 2
+ 0x15, 0x00, // Pedestal Ctl 3
+ 0x16, 0x00, // CGMS_WSS_0
+ 0x17, 0x00, // CGMS_WSS_1
+ 0x18, 0x00, // CGMS_WSS_2
+ 0x19, 0x00, // Teletext Ctl
+};
+
+
+static int
+adv7170_command (struct i2c_client *client,
+ unsigned int cmd,
+ void * arg)
+{
+ struct adv7170 *encoder = i2c_get_clientdata(client);
+
+ switch (cmd) {
+
+ case 0:
+#if 0
+ /* This is just for testing!!! */
+ adv7170_write_block(client, init_common,
+ sizeof(init_common));
+ adv7170_write(client, 0x07, TR0MODE | TR0RST);
+ adv7170_write(client, 0x07, TR0MODE);
+#endif
+ break;
+
+ case ENCODER_GET_CAPABILITIES:
+ {
+ struct video_encoder_capability *cap = arg;
+
+ cap->flags = VIDEO_ENCODER_PAL |
+ VIDEO_ENCODER_NTSC;
+ cap->inputs = 2;
+ cap->outputs = 1;
+ }
+ break;
+
+ case ENCODER_SET_NORM:
+ {
+ int iarg = *(int *) arg;
+
+ dprintk(1, KERN_DEBUG "%s_command: set norm %d",
+ I2C_NAME(client), iarg);
+
+ switch (iarg) {
+
+ case VIDEO_MODE_NTSC:
+ adv7170_write_block(client, init_NTSC,
+ sizeof(init_NTSC));
+ if (encoder->input == 0)
+ adv7170_write(client, 0x02, 0x0e); // Enable genlock
+ adv7170_write(client, 0x07, TR0MODE | TR0RST);
+ adv7170_write(client, 0x07, TR0MODE);
+ break;
+
+ case VIDEO_MODE_PAL:
+ adv7170_write_block(client, init_PAL,
+ sizeof(init_PAL));
+ if (encoder->input == 0)
+ adv7170_write(client, 0x02, 0x0e); // Enable genlock
+ adv7170_write(client, 0x07, TR0MODE | TR0RST);
+ adv7170_write(client, 0x07, TR0MODE);
+ break;
+
+ default:
+ dprintk(1, KERN_ERR "%s: illegal norm: %d\n",
+ I2C_NAME(client), iarg);
+ return -EINVAL;
+
+ }
+ dprintk(1, KERN_DEBUG "%s: switched to %s\n", I2C_NAME(client),
+ norms[iarg]);
+ encoder->norm = iarg;
+ }
+ break;
+
+ case ENCODER_SET_INPUT:
+ {
+ int iarg = *(int *) arg;
+
+ /* RJ: *iarg = 0: input is from decoder
+ *iarg = 1: input is from ZR36060
+ *iarg = 2: color bar */
+
+ dprintk(1, KERN_DEBUG "%s_command: set input from %s\n",
+ I2C_NAME(client),
+ iarg == 0 ? "decoder" : "ZR36060");
+
+ switch (iarg) {
+
+ case 0:
+ adv7170_write(client, 0x01, 0x20);
+ adv7170_write(client, 0x08, TR1CAPT); /* TR1 */
+ adv7170_write(client, 0x02, 0x0e); // Enable genlock
+ adv7170_write(client, 0x07, TR0MODE | TR0RST);
+ adv7170_write(client, 0x07, TR0MODE);
+ //udelay(10);
+ break;
+
+ case 1:
+ adv7170_write(client, 0x01, 0x00);
+ adv7170_write(client, 0x08, TR1PLAY); /* TR1 */
+ adv7170_write(client, 0x02, 0x08);
+ adv7170_write(client, 0x07, TR0MODE | TR0RST);
+ adv7170_write(client, 0x07, TR0MODE);
+ //udelay(10);
+ break;
+
+ default:
+ dprintk(1, KERN_ERR "%s: illegal input: %d\n",
+ I2C_NAME(client), iarg);
+ return -EINVAL;
+
+ }
+ dprintk(1, KERN_DEBUG "%s: switched to %s\n", I2C_NAME(client),
+ inputs[iarg]);
+ encoder->input = iarg;
+ }
+ break;
+
+ case ENCODER_SET_OUTPUT:
+ {
+ int *iarg = arg;
+
+ /* not much choice of outputs */
+ if (*iarg != 0) {
+ return -EINVAL;
+ }
+ }
+ break;
+
+ case ENCODER_ENABLE_OUTPUT:
+ {
+ int *iarg = arg;
+
+ encoder->enable = !!*iarg;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/*
+ * Generic i2c probe
+ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+ */
+static unsigned short normal_i2c[] =
+ { I2C_ADV7170 >> 1, (I2C_ADV7170 >> 1) + 1,
+ I2C_ADV7171 >> 1, (I2C_ADV7171 >> 1) + 1,
+ I2C_CLIENT_END
+};
+
+static unsigned short ignore = I2C_CLIENT_END;
+
+static struct i2c_client_address_data addr_data = {
+ .normal_i2c = normal_i2c,
+ .probe = &ignore,
+ .ignore = &ignore,
+};
+
+static struct i2c_driver i2c_driver_adv7170;
+
+static int
+adv7170_detect_client (struct i2c_adapter *adapter,
+ int address,
+ int kind)
+{
+ int i;
+ struct i2c_client *client;
+ struct adv7170 *encoder;
+ char *dname;
+
+ dprintk(1,
+ KERN_INFO
+ "adv7170.c: detecting adv7170 client on address 0x%x\n",
+ address << 1);
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return 0;
+
+ client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (client == 0)
+ return -ENOMEM;
+ client->addr = address;
+ client->adapter = adapter;
+ client->driver = &i2c_driver_adv7170;
+ if ((client->addr == I2C_ADV7170 >> 1) ||
+ (client->addr == (I2C_ADV7170 >> 1) + 1)) {
+ dname = adv7170_name;
+ } else if ((client->addr == I2C_ADV7171 >> 1) ||
+ (client->addr == (I2C_ADV7171 >> 1) + 1)) {
+ dname = adv7171_name;
+ } else {
+ /* We should never get here!!! */
+ kfree(client);
+ return 0;
+ }
+ strlcpy(I2C_NAME(client), dname, sizeof(I2C_NAME(client)));
+
+ encoder = kzalloc(sizeof(struct adv7170), GFP_KERNEL);
+ if (encoder == NULL) {
+ kfree(client);
+ return -ENOMEM;
+ }
+ encoder->norm = VIDEO_MODE_NTSC;
+ encoder->input = 0;
+ encoder->enable = 1;
+ i2c_set_clientdata(client, encoder);
+
+ i = i2c_attach_client(client);
+ if (i) {
+ kfree(client);
+ kfree(encoder);
+ return i;
+ }
+
+ i = adv7170_write_block(client, init_NTSC, sizeof(init_NTSC));
+ if (i >= 0) {
+ i = adv7170_write(client, 0x07, TR0MODE | TR0RST);
+ i = adv7170_write(client, 0x07, TR0MODE);
+ i = adv7170_read(client, 0x12);
+ dprintk(1, KERN_INFO "%s_attach: rev. %d at 0x%02x\n",
+ I2C_NAME(client), i & 1, client->addr << 1);
+ }
+ if (i < 0) {
+ dprintk(1, KERN_ERR "%s_attach: init error 0x%x\n",
+ I2C_NAME(client), i);
+ }
+
+ return 0;
+}
+
+static int
+adv7170_attach_adapter (struct i2c_adapter *adapter)
+{
+ dprintk(1,
+ KERN_INFO
+ "adv7170.c: starting probe for adapter %s (0x%x)\n",
+ I2C_NAME(adapter), adapter->id);
+ return i2c_probe(adapter, &addr_data, &adv7170_detect_client);
+}
+
+static int
+adv7170_detach_client (struct i2c_client *client)
+{
+ struct adv7170 *encoder = i2c_get_clientdata(client);
+ int err;
+
+ err = i2c_detach_client(client);
+ if (err) {
+ return err;
+ }
+
+ kfree(encoder);
+ kfree(client);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_driver i2c_driver_adv7170 = {
+ .driver = {
+ .name = "adv7170", /* name */
+ },
+
+ .id = I2C_DRIVERID_ADV7170,
+
+ .attach_adapter = adv7170_attach_adapter,
+ .detach_client = adv7170_detach_client,
+ .command = adv7170_command,
+};
+
+static int __init
+adv7170_init (void)
+{
+ return i2c_add_driver(&i2c_driver_adv7170);
+}
+
+static void __exit
+adv7170_exit (void)
+{
+ i2c_del_driver(&i2c_driver_adv7170);
+}
+
+module_init(adv7170_init);
+module_exit(adv7170_exit);
diff --git a/linux/drivers/media/video/adv7175.c b/linux/drivers/media/video/adv7175.c
new file mode 100644
index 000000000..052b54f83
--- /dev/null
+++ b/linux/drivers/media/video/adv7175.c
@@ -0,0 +1,537 @@
+/*
+ * adv7175 - adv7175a video encoder driver version 0.0.3
+ *
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ * Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net>
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ * - some corrections for Pinnacle Systems Inc. DC10plus card.
+ *
+ * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * - moved over to linux>=2.4.x i2c protocol (9/9/2002)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/signal.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+
+#include "compat.h"
+#include <linux/videodev.h>
+#include <asm/uaccess.h>
+
+MODULE_DESCRIPTION("Analog Devices ADV7175 video encoder driver");
+MODULE_AUTHOR("Dave Perks");
+MODULE_LICENSE("GPL");
+
+#include <linux/i2c.h>
+
+#define I2C_NAME(s) (s)->name
+
+#include <linux/video_encoder.h>
+
+static int debug = 0;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+/* ----------------------------------------------------------------------- */
+
+struct adv7175 {
+ int norm;
+ int input;
+ int enable;
+ int bright;
+ int contrast;
+ int hue;
+ int sat;
+};
+
+#define I2C_ADV7175 0xd4
+#define I2C_ADV7176 0x54
+
+static char adv7175_name[] = "adv7175";
+static char adv7176_name[] = "adv7176";
+
+static char *inputs[] = { "pass_through", "play_back", "color_bar" };
+static char *norms[] = { "PAL", "NTSC", "SECAM->PAL (may not work!)" };
+
+/* ----------------------------------------------------------------------- */
+
+static inline int
+adv7175_write (struct i2c_client *client,
+ u8 reg,
+ u8 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static inline int
+adv7175_read (struct i2c_client *client,
+ u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int
+adv7175_write_block (struct i2c_client *client,
+ const u8 *data,
+ unsigned int len)
+{
+ int ret = -1;
+ u8 reg;
+
+ /* the adv7175 has an autoincrement function, use it if
+ * the adapter understands raw I2C */
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ /* do raw I2C, not smbus compatible */
+ u8 block_data[32];
+ int block_len;
+
+ while (len >= 2) {
+ block_len = 0;
+ block_data[block_len++] = reg = data[0];
+ do {
+ block_data[block_len++] = data[1];
+ reg++;
+ len -= 2;
+ data += 2;
+ } while (len >= 2 && data[0] == reg &&
+ block_len < 32);
+ if ((ret = i2c_master_send(client, block_data,
+ block_len)) < 0)
+ break;
+ }
+ } else {
+ /* do some slow I2C emulation kind of thing */
+ while (len >= 2) {
+ reg = *data++;
+ if ((ret = adv7175_write(client, reg,
+ *data++)) < 0)
+ break;
+ len -= 2;
+ }
+ }
+
+ return ret;
+}
+
+static void
+set_subcarrier_freq (struct i2c_client *client,
+ int pass_through)
+{
+ /* for some reason pass_through NTSC needs
+ * a different sub-carrier freq to remain stable. */
+ if(pass_through)
+ adv7175_write(client, 0x02, 0x00);
+ else
+ adv7175_write(client, 0x02, 0x55);
+
+ adv7175_write(client, 0x03, 0x55);
+ adv7175_write(client, 0x04, 0x55);
+ adv7175_write(client, 0x05, 0x25);
+}
+
+/* ----------------------------------------------------------------------- */
+// Output filter: S-Video Composite
+
+#define MR050 0x11 //0x09
+#define MR060 0x14 //0x0c
+
+//---------------------------------------------------------------------------
+
+#define TR0MODE 0x46
+#define TR0RST 0x80
+
+#define TR1CAPT 0x80
+#define TR1PLAY 0x00
+
+static const unsigned char init_common[] = {
+
+ 0x00, MR050, /* MR0, PAL enabled */
+ 0x01, 0x00, /* MR1 */
+ 0x02, 0x0c, /* subc. freq. */
+ 0x03, 0x8c, /* subc. freq. */
+ 0x04, 0x79, /* subc. freq. */
+ 0x05, 0x26, /* subc. freq. */
+ 0x06, 0x40, /* subc. phase */
+
+ 0x07, TR0MODE, /* TR0, 16bit */
+ 0x08, 0x21, /* */
+ 0x09, 0x00, /* */
+ 0x0a, 0x00, /* */
+ 0x0b, 0x00, /* */
+ 0x0c, TR1CAPT, /* TR1 */
+ 0x0d, 0x4f, /* MR2 */
+ 0x0e, 0x00, /* */
+ 0x0f, 0x00, /* */
+ 0x10, 0x00, /* */
+ 0x11, 0x00, /* */
+};
+
+static const unsigned char init_pal[] = {
+ 0x00, MR050, /* MR0, PAL enabled */
+ 0x01, 0x00, /* MR1 */
+ 0x02, 0x0c, /* subc. freq. */
+ 0x03, 0x8c, /* subc. freq. */
+ 0x04, 0x79, /* subc. freq. */
+ 0x05, 0x26, /* subc. freq. */
+ 0x06, 0x40, /* subc. phase */
+};
+
+static const unsigned char init_ntsc[] = {
+ 0x00, MR060, /* MR0, NTSC enabled */
+ 0x01, 0x00, /* MR1 */
+ 0x02, 0x55, /* subc. freq. */
+ 0x03, 0x55, /* subc. freq. */
+ 0x04, 0x55, /* subc. freq. */
+ 0x05, 0x25, /* subc. freq. */
+ 0x06, 0x1a, /* subc. phase */
+};
+
+static int
+adv7175_command (struct i2c_client *client,
+ unsigned int cmd,
+ void *arg)
+{
+ struct adv7175 *encoder = i2c_get_clientdata(client);
+
+ switch (cmd) {
+
+ case 0:
+ /* This is just for testing!!! */
+ adv7175_write_block(client, init_common,
+ sizeof(init_common));
+ adv7175_write(client, 0x07, TR0MODE | TR0RST);
+ adv7175_write(client, 0x07, TR0MODE);
+ break;
+
+ case ENCODER_GET_CAPABILITIES:
+ {
+ struct video_encoder_capability *cap = arg;
+
+ cap->flags = VIDEO_ENCODER_PAL |
+ VIDEO_ENCODER_NTSC |
+ VIDEO_ENCODER_SECAM; /* well, hacky */
+ cap->inputs = 2;
+ cap->outputs = 1;
+ }
+ break;
+
+ case ENCODER_SET_NORM:
+ {
+ int iarg = *(int *) arg;
+
+ switch (iarg) {
+
+ case VIDEO_MODE_NTSC:
+ adv7175_write_block(client, init_ntsc,
+ sizeof(init_ntsc));
+ if (encoder->input == 0)
+ adv7175_write(client, 0x0d, 0x4f); // Enable genlock
+ adv7175_write(client, 0x07, TR0MODE | TR0RST);
+ adv7175_write(client, 0x07, TR0MODE);
+ break;
+
+ case VIDEO_MODE_PAL:
+ adv7175_write_block(client, init_pal,
+ sizeof(init_pal));
+ if (encoder->input == 0)
+ adv7175_write(client, 0x0d, 0x4f); // Enable genlock
+ adv7175_write(client, 0x07, TR0MODE | TR0RST);
+ adv7175_write(client, 0x07, TR0MODE);
+ break;
+
+ case VIDEO_MODE_SECAM: // WARNING! ADV7176 does not support SECAM.
+ /* This is an attempt to convert
+ * SECAM->PAL (typically it does not work
+ * due to genlock: when decoder is in SECAM
+ * and encoder in in PAL the subcarrier can
+ * not be syncronized with horizontal
+ * quency) */
+ adv7175_write_block(client, init_pal,
+ sizeof(init_pal));
+ if (encoder->input == 0)
+ adv7175_write(client, 0x0d, 0x49); // Disable genlock
+ adv7175_write(client, 0x07, TR0MODE | TR0RST);
+ adv7175_write(client, 0x07, TR0MODE);
+ break;
+ default:
+ dprintk(1, KERN_ERR "%s: illegal norm: %d\n",
+ I2C_NAME(client), iarg);
+ return -EINVAL;
+
+ }
+ dprintk(1, KERN_INFO "%s: switched to %s\n", I2C_NAME(client),
+ norms[iarg]);
+ encoder->norm = iarg;
+ }
+ break;
+
+ case ENCODER_SET_INPUT:
+ {
+ int iarg = *(int *) arg;
+
+ /* RJ: *iarg = 0: input is from SAA7110
+ *iarg = 1: input is from ZR36060
+ *iarg = 2: color bar */
+
+ switch (iarg) {
+
+ case 0:
+ adv7175_write(client, 0x01, 0x00);
+
+ if (encoder->norm == VIDEO_MODE_NTSC)
+ set_subcarrier_freq(client, 1);
+
+ adv7175_write(client, 0x0c, TR1CAPT); /* TR1 */
+ if (encoder->norm == VIDEO_MODE_SECAM)
+ adv7175_write(client, 0x0d, 0x49); // Disable genlock
+ else
+ adv7175_write(client, 0x0d, 0x4f); // Enable genlock
+ adv7175_write(client, 0x07, TR0MODE | TR0RST);
+ adv7175_write(client, 0x07, TR0MODE);
+ //udelay(10);
+ break;
+
+ case 1:
+ adv7175_write(client, 0x01, 0x00);
+
+ if (encoder->norm == VIDEO_MODE_NTSC)
+ set_subcarrier_freq(client, 0);
+
+ adv7175_write(client, 0x0c, TR1PLAY); /* TR1 */
+ adv7175_write(client, 0x0d, 0x49);
+ adv7175_write(client, 0x07, TR0MODE | TR0RST);
+ adv7175_write(client, 0x07, TR0MODE);
+ //udelay(10);
+ break;
+
+ case 2:
+ adv7175_write(client, 0x01, 0x80);
+
+ if (encoder->norm == VIDEO_MODE_NTSC)
+ set_subcarrier_freq(client, 0);
+
+ adv7175_write(client, 0x0d, 0x49);
+ adv7175_write(client, 0x07, TR0MODE | TR0RST);
+ adv7175_write(client, 0x07, TR0MODE);
+ //udelay(10);
+ break;
+
+ default:
+ dprintk(1, KERN_ERR "%s: illegal input: %d\n",
+ I2C_NAME(client), iarg);
+ return -EINVAL;
+
+ }
+ dprintk(1, KERN_INFO "%s: switched to %s\n", I2C_NAME(client),
+ inputs[iarg]);
+ encoder->input = iarg;
+ }
+ break;
+
+ case ENCODER_SET_OUTPUT:
+ {
+ int *iarg = arg;
+
+ /* not much choice of outputs */
+ if (*iarg != 0) {
+ return -EINVAL;
+ }
+ }
+ break;
+
+ case ENCODER_ENABLE_OUTPUT:
+ {
+ int *iarg = arg;
+
+ encoder->enable = !!*iarg;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/*
+ * Generic i2c probe
+ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+ */
+static unsigned short normal_i2c[] =
+ { I2C_ADV7175 >> 1, (I2C_ADV7175 >> 1) + 1,
+ I2C_ADV7176 >> 1, (I2C_ADV7176 >> 1) + 1,
+ I2C_CLIENT_END
+};
+
+static unsigned short ignore = I2C_CLIENT_END;
+
+static struct i2c_client_address_data addr_data = {
+ .normal_i2c = normal_i2c,
+ .probe = &ignore,
+ .ignore = &ignore,
+};
+
+static struct i2c_driver i2c_driver_adv7175;
+
+static int
+adv7175_detect_client (struct i2c_adapter *adapter,
+ int address,
+ int kind)
+{
+ int i;
+ struct i2c_client *client;
+ struct adv7175 *encoder;
+ char *dname;
+
+ dprintk(1,
+ KERN_INFO
+ "adv7175.c: detecting adv7175 client on address 0x%x\n",
+ address << 1);
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return 0;
+
+ client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (client == 0)
+ return -ENOMEM;
+ client->addr = address;
+ client->adapter = adapter;
+ client->driver = &i2c_driver_adv7175;
+ if ((client->addr == I2C_ADV7175 >> 1) ||
+ (client->addr == (I2C_ADV7175 >> 1) + 1)) {
+ dname = adv7175_name;
+ } else if ((client->addr == I2C_ADV7176 >> 1) ||
+ (client->addr == (I2C_ADV7176 >> 1) + 1)) {
+ dname = adv7176_name;
+ } else {
+ /* We should never get here!!! */
+ kfree(client);
+ return 0;
+ }
+ strlcpy(I2C_NAME(client), dname, sizeof(I2C_NAME(client)));
+
+ encoder = kzalloc(sizeof(struct adv7175), GFP_KERNEL);
+ if (encoder == NULL) {
+ kfree(client);
+ return -ENOMEM;
+ }
+ encoder->norm = VIDEO_MODE_PAL;
+ encoder->input = 0;
+ encoder->enable = 1;
+ i2c_set_clientdata(client, encoder);
+
+ i = i2c_attach_client(client);
+ if (i) {
+ kfree(client);
+ kfree(encoder);
+ return i;
+ }
+
+ i = adv7175_write_block(client, init_common, sizeof(init_common));
+ if (i >= 0) {
+ i = adv7175_write(client, 0x07, TR0MODE | TR0RST);
+ i = adv7175_write(client, 0x07, TR0MODE);
+ i = adv7175_read(client, 0x12);
+ dprintk(1, KERN_INFO "%s_attach: rev. %d at 0x%x\n",
+ I2C_NAME(client), i & 1, client->addr << 1);
+ }
+ if (i < 0) {
+ dprintk(1, KERN_ERR "%s_attach: init error 0x%x\n",
+ I2C_NAME(client), i);
+ }
+
+ return 0;
+}
+
+static int
+adv7175_attach_adapter (struct i2c_adapter *adapter)
+{
+ dprintk(1,
+ KERN_INFO
+ "adv7175.c: starting probe for adapter %s (0x%x)\n",
+ I2C_NAME(adapter), adapter->id);
+ return i2c_probe(adapter, &addr_data, &adv7175_detect_client);
+}
+
+static int
+adv7175_detach_client (struct i2c_client *client)
+{
+ struct adv7175 *encoder = i2c_get_clientdata(client);
+ int err;
+
+ err = i2c_detach_client(client);
+ if (err) {
+ return err;
+ }
+
+ kfree(encoder);
+ kfree(client);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_driver i2c_driver_adv7175 = {
+ .driver = {
+ .name = "adv7175", /* name */
+ },
+
+ .id = I2C_DRIVERID_ADV7175,
+
+ .attach_adapter = adv7175_attach_adapter,
+ .detach_client = adv7175_detach_client,
+ .command = adv7175_command,
+};
+
+static int __init
+adv7175_init (void)
+{
+ return i2c_add_driver(&i2c_driver_adv7175);
+}
+
+static void __exit
+adv7175_exit (void)
+{
+ i2c_del_driver(&i2c_driver_adv7175);
+}
+
+module_init(adv7175_init);
+module_exit(adv7175_exit);
diff --git a/linux/drivers/media/video/arv.c b/linux/drivers/media/video/arv.c
new file mode 100644
index 000000000..aec57f485
--- /dev/null
+++ b/linux/drivers/media/video/arv.c
@@ -0,0 +1,913 @@
+/*
+ * Colour AR M64278(VGA) driver for Video4Linux
+ *
+ * Copyright (C) 2003 Takeo Takahashi <takahashi.takeo@renesas.com>
+ *
+ * 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.
+ *
+ * Some code is taken from AR driver sample program for M3T-M32700UT.
+ *
+ * AR driver sample (M32R SDK):
+ * Copyright (c) 2003 RENESAS TECHNOROGY CORPORATION
+ * AND RENESAS SOLUTIONS CORPORATION
+ * All Rights Reserved.
+ *
+ * 2003-09-01: Support w3cam by Takeo Takahashi
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include "compat.h"
+#include <linux/videodev.h>
+#include <linux/mutex.h>
+
+#include <asm/uaccess.h>
+#include <asm/m32r.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/byteorder.h>
+
+#if 0
+#define DEBUG(n, args...) printk(args)
+#define CHECK_LOST 1
+#else
+#define DEBUG(n, args...)
+#define CHECK_LOST 0
+#endif
+
+/*
+ * USE_INT is always 0, interrupt mode is not available
+ * on linux due to lack of speed
+ */
+#define USE_INT 0 /* Don't modify */
+
+#define VERSION "0.03"
+
+#define ar_inl(addr) inl((unsigned long)(addr))
+#define ar_outl(val, addr) outl((unsigned long)(val),(unsigned long)(addr))
+
+extern struct cpuinfo_m32r boot_cpu_data;
+
+/*
+ * CCD pixel size
+ * Note that M32700UT does not support CIF mode, but QVGA is
+ * supported by M32700UT hardware using VGA mode of AR LSI.
+ *
+ * Supported: VGA (Normal mode, Interlace mode)
+ * QVGA (Always Interlace mode of VGA)
+ *
+ */
+#define AR_WIDTH_VGA 640
+#define AR_HEIGHT_VGA 480
+#define AR_WIDTH_QVGA 320
+#define AR_HEIGHT_QVGA 240
+#define MIN_AR_WIDTH AR_WIDTH_QVGA
+#define MIN_AR_HEIGHT AR_HEIGHT_QVGA
+#define MAX_AR_WIDTH AR_WIDTH_VGA
+#define MAX_AR_HEIGHT AR_HEIGHT_VGA
+
+/* bits & bytes per pixel */
+#define AR_BITS_PER_PIXEL 16
+#define AR_BYTES_PER_PIXEL (AR_BITS_PER_PIXEL/8)
+
+/* line buffer size */
+#define AR_LINE_BYTES_VGA (AR_WIDTH_VGA * AR_BYTES_PER_PIXEL)
+#define AR_LINE_BYTES_QVGA (AR_WIDTH_QVGA * AR_BYTES_PER_PIXEL)
+#define MAX_AR_LINE_BYTES AR_LINE_BYTES_VGA
+
+/* frame size & type */
+#define AR_FRAME_BYTES_VGA \
+ (AR_WIDTH_VGA * AR_HEIGHT_VGA * AR_BYTES_PER_PIXEL)
+#define AR_FRAME_BYTES_QVGA \
+ (AR_WIDTH_QVGA * AR_HEIGHT_QVGA * AR_BYTES_PER_PIXEL)
+#define MAX_AR_FRAME_BYTES \
+ (MAX_AR_WIDTH * MAX_AR_HEIGHT * AR_BYTES_PER_PIXEL)
+
+#define AR_MAX_FRAME 15
+
+/* capture size */
+#define AR_SIZE_VGA 0
+#define AR_SIZE_QVGA 1
+
+/* capture mode */
+#define AR_MODE_INTERLACE 0
+#define AR_MODE_NORMAL 1
+
+struct ar_device {
+ struct video_device *vdev;
+ unsigned int start_capture; /* duaring capture in INT. mode. */
+#if USE_INT
+ unsigned char *line_buff; /* DMA line buffer */
+#endif
+ unsigned char *frame[MAX_AR_HEIGHT]; /* frame data */
+ short size; /* capture size */
+ short mode; /* capture mode */
+ int width, height;
+ int frame_bytes, line_bytes;
+ wait_queue_head_t wait;
+ struct mutex lock;
+};
+
+static int video_nr = -1; /* video device number (first free) */
+static unsigned char yuv[MAX_AR_FRAME_BYTES];
+
+/* module parameters */
+/* default frequency */
+#define DEFAULT_FREQ 50 /* 50 or 75 (MHz) is available as BCLK */
+static int freq = DEFAULT_FREQ; /* BCLK: available 50 or 70 (MHz) */
+static int vga = 0; /* default mode(0:QVGA mode, other:VGA mode) */
+static int vga_interlace = 0; /* 0 is normal mode for, else interlace mode */
+module_param(freq, int, 0);
+module_param(vga, int, 0);
+module_param(vga_interlace, int, 0);
+
+static int ar_initialize(struct video_device *dev);
+
+static inline void wait_for_vsync(void)
+{
+ while (ar_inl(ARVCR0) & ARVCR0_VDS) /* wait for VSYNC */
+ cpu_relax();
+ while (!(ar_inl(ARVCR0) & ARVCR0_VDS)) /* wait for VSYNC */
+ cpu_relax();
+}
+
+static inline void wait_acknowledge(void)
+{
+ int i;
+
+ for (i = 0; i < 1000; i++)
+ cpu_relax();
+ while (ar_inl(PLDI2CSTS) & PLDI2CSTS_NOACK)
+ cpu_relax();
+}
+
+/*******************************************************************
+ * I2C functions
+ *******************************************************************/
+void iic(int n, unsigned long addr, unsigned long data1, unsigned long data2,
+ unsigned long data3)
+{
+ int i;
+
+ /* Slave Address */
+ ar_outl(addr, PLDI2CDATA);
+ wait_for_vsync();
+
+ /* Start */
+ ar_outl(1, PLDI2CCND);
+ wait_acknowledge();
+
+ /* Transfer data 1 */
+ ar_outl(data1, PLDI2CDATA);
+ wait_for_vsync();
+ ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN);
+ wait_acknowledge();
+
+ /* Transfer data 2 */
+ ar_outl(data2, PLDI2CDATA);
+ wait_for_vsync();
+ ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN);
+ wait_acknowledge();
+
+ if (n == 3) {
+ /* Transfer data 3 */
+ ar_outl(data3, PLDI2CDATA);
+ wait_for_vsync();
+ ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN);
+ wait_acknowledge();
+ }
+
+ /* Stop */
+ for (i = 0; i < 100; i++)
+ cpu_relax();
+ ar_outl(2, PLDI2CCND);
+ ar_outl(2, PLDI2CCND);
+
+ while (ar_inl(PLDI2CSTS) & PLDI2CSTS_BB)
+ cpu_relax();
+}
+
+
+void init_iic(void)
+{
+ DEBUG(1, "init_iic:\n");
+
+ /*
+ * ICU Setting (iic)
+ */
+ /* I2C Setting */
+ ar_outl(0x0, PLDI2CCR); /* I2CCR Disable */
+ ar_outl(0x0300, PLDI2CMOD); /* I2CMOD ACK/8b-data/7b-addr/auto */
+ ar_outl(0x1, PLDI2CACK); /* I2CACK ACK */
+
+ /* I2C CLK */
+ /* 50MH-100k */
+ if (freq == 75) {
+ ar_outl(369, PLDI2CFREQ); /* BCLK = 75MHz */
+ } else if (freq == 50) {
+ ar_outl(244, PLDI2CFREQ); /* BCLK = 50MHz */
+ } else {
+ ar_outl(244, PLDI2CFREQ); /* default: BCLK = 50MHz */
+ }
+ ar_outl(0x1, PLDI2CCR); /* I2CCR Enable */
+}
+
+/**************************************************************************
+ *
+ * Video4Linux Interface functions
+ *
+ **************************************************************************/
+
+static inline void disable_dma(void)
+{
+ ar_outl(0x8000, M32R_DMAEN_PORTL); /* disable DMA0 */
+}
+
+static inline void enable_dma(void)
+{
+ ar_outl(0x8080, M32R_DMAEN_PORTL); /* enable DMA0 */
+}
+
+static inline void clear_dma_status(void)
+{
+ ar_outl(0x8000, M32R_DMAEDET_PORTL); /* clear status */
+}
+
+static inline void wait_for_vertical_sync(int exp_line)
+{
+#if CHECK_LOST
+ int tmout = 10000; /* FIXME */
+ int l;
+
+ /*
+ * check HCOUNT because we cannot check vertical sync.
+ */
+ for (; tmout >= 0; tmout--) {
+ l = ar_inl(ARVHCOUNT);
+ if (l == exp_line)
+ break;
+ }
+ if (tmout < 0)
+ printk("arv: lost %d -> %d\n", exp_line, l);
+#else
+ while (ar_inl(ARVHCOUNT) != exp_line)
+ cpu_relax();
+#endif
+}
+
+static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+ struct video_device *v = video_devdata(file);
+ struct ar_device *ar = v->priv;
+ long ret = ar->frame_bytes; /* return read bytes */
+ unsigned long arvcr1 = 0;
+ unsigned long flags;
+ unsigned char *p;
+ int h, w;
+ unsigned char *py, *pu, *pv;
+#if ! USE_INT
+ int l;
+#endif
+
+ DEBUG(1, "ar_read()\n");
+
+ if (ar->size == AR_SIZE_QVGA)
+ arvcr1 |= ARVCR1_QVGA;
+ if (ar->mode == AR_MODE_NORMAL)
+ arvcr1 |= ARVCR1_NORMAL;
+
+ mutex_lock(&ar->lock);
+
+#if USE_INT
+ local_irq_save(flags);
+ disable_dma();
+ ar_outl(0xa1871300, M32R_DMA0CR0_PORTL);
+ ar_outl(0x01000000, M32R_DMA0CR1_PORTL);
+
+ /* set AR FIFO address as source(BSEL5) */
+ ar_outl(ARDATA32, M32R_DMA0CSA_PORTL);
+ ar_outl(ARDATA32, M32R_DMA0RSA_PORTL);
+ ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); /* destination addr. */
+ ar_outl(ar->line_buff, M32R_DMA0RDA_PORTL); /* reload address */
+ ar_outl(ar->line_bytes, M32R_DMA0CBCUT_PORTL); /* byte count (bytes) */
+ ar_outl(ar->line_bytes, M32R_DMA0RBCUT_PORTL); /* reload count (bytes) */
+
+ /*
+ * Okey , kicks AR LSI to invoke an interrupt
+ */
+ ar->start_capture = 0;
+ ar_outl(arvcr1 | ARVCR1_HIEN, ARVCR1);
+ local_irq_restore(flags);
+ /* .... AR interrupts .... */
+ interruptible_sleep_on(&ar->wait);
+ if (signal_pending(current)) {
+ printk("arv: interrupted while get frame data.\n");
+ ret = -EINTR;
+ goto out_up;
+ }
+#else /* ! USE_INT */
+ /* polling */
+ ar_outl(arvcr1, ARVCR1);
+ disable_dma();
+ ar_outl(0x8000, M32R_DMAEDET_PORTL);
+ ar_outl(0xa0861300, M32R_DMA0CR0_PORTL);
+ ar_outl(0x01000000, M32R_DMA0CR1_PORTL);
+ ar_outl(ARDATA32, M32R_DMA0CSA_PORTL);
+ ar_outl(ARDATA32, M32R_DMA0RSA_PORTL);
+ ar_outl(ar->line_bytes, M32R_DMA0CBCUT_PORTL);
+ ar_outl(ar->line_bytes, M32R_DMA0RBCUT_PORTL);
+
+ local_irq_save(flags);
+ while (ar_inl(ARVHCOUNT) != 0) /* wait for 0 */
+ cpu_relax();
+ if (ar->mode == AR_MODE_INTERLACE && ar->size == AR_SIZE_VGA) {
+ for (h = 0; h < ar->height; h++) {
+ wait_for_vertical_sync(h);
+ if (h < (AR_HEIGHT_VGA/2))
+ l = h << 1;
+ else
+ l = (((h - (AR_HEIGHT_VGA/2)) << 1) + 1);
+ ar_outl(virt_to_phys(ar->frame[l]), M32R_DMA0CDA_PORTL);
+ enable_dma();
+ while (!(ar_inl(M32R_DMAEDET_PORTL) & 0x8000))
+ cpu_relax();
+ disable_dma();
+ clear_dma_status();
+ ar_outl(0xa0861300, M32R_DMA0CR0_PORTL);
+ }
+ } else {
+ for (h = 0; h < ar->height; h++) {
+ wait_for_vertical_sync(h);
+ ar_outl(virt_to_phys(ar->frame[h]), M32R_DMA0CDA_PORTL);
+ enable_dma();
+ while (!(ar_inl(M32R_DMAEDET_PORTL) & 0x8000))
+ cpu_relax();
+ disable_dma();
+ clear_dma_status();
+ ar_outl(0xa0861300, M32R_DMA0CR0_PORTL);
+ }
+ }
+ local_irq_restore(flags);
+#endif /* ! USE_INT */
+
+ /*
+ * convert YUV422 to YUV422P
+ * +--------------------+
+ * | Y0,Y1,... |
+ * | ..............Yn |
+ * +--------------------+
+ * | U0,U1,........Un |
+ * +--------------------+
+ * | V0,V1,........Vn |
+ * +--------------------+
+ */
+ py = yuv;
+ pu = py + (ar->frame_bytes / 2);
+ pv = pu + (ar->frame_bytes / 4);
+ for (h = 0; h < ar->height; h++) {
+ p = ar->frame[h];
+ for (w = 0; w < ar->line_bytes; w += 4) {
+ *py++ = *p++;
+ *pu++ = *p++;
+ *py++ = *p++;
+ *pv++ = *p++;
+ }
+ }
+ if (copy_to_user(buf, yuv, ar->frame_bytes)) {
+ printk("arv: failed while copy_to_user yuv.\n");
+ ret = -EFAULT;
+ goto out_up;
+ }
+ DEBUG(1, "ret = %d\n", ret);
+out_up:
+ mutex_unlock(&ar->lock);
+ return ret;
+}
+
+static int ar_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct ar_device *ar = dev->priv;
+
+ DEBUG(1, "ar_ioctl()\n");
+ switch(cmd) {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *b = arg;
+ DEBUG(1, "VIDIOCGCAP:\n");
+ strcpy(b->name, ar->vdev->name);
+ b->type = VID_TYPE_CAPTURE;
+ b->channels = 0;
+ b->audios = 0;
+ b->maxwidth = MAX_AR_WIDTH;
+ b->maxheight = MAX_AR_HEIGHT;
+ b->minwidth = MIN_AR_WIDTH;
+ b->minheight = MIN_AR_HEIGHT;
+ return 0;
+ }
+ case VIDIOCGCHAN:
+ DEBUG(1, "VIDIOCGCHAN:\n");
+ return 0;
+ case VIDIOCSCHAN:
+ DEBUG(1, "VIDIOCSCHAN:\n");
+ return 0;
+ case VIDIOCGTUNER:
+ DEBUG(1, "VIDIOCGTUNER:\n");
+ return 0;
+ case VIDIOCSTUNER:
+ DEBUG(1, "VIDIOCSTUNER:\n");
+ return 0;
+ case VIDIOCGPICT:
+ DEBUG(1, "VIDIOCGPICT:\n");
+ return 0;
+ case VIDIOCSPICT:
+ DEBUG(1, "VIDIOCSPICT:\n");
+ return 0;
+ case VIDIOCCAPTURE:
+ DEBUG(1, "VIDIOCCAPTURE:\n");
+ return -EINVAL;
+ case VIDIOCGWIN:
+ {
+ struct video_window *w = arg;
+ DEBUG(1, "VIDIOCGWIN:\n");
+ memset(w, 0, sizeof(w));
+ w->width = ar->width;
+ w->height = ar->height;
+ return 0;
+ }
+ case VIDIOCSWIN:
+ {
+ struct video_window *w = arg;
+ DEBUG(1, "VIDIOCSWIN:\n");
+ if ((w->width != AR_WIDTH_VGA || w->height != AR_HEIGHT_VGA) &&
+ (w->width != AR_WIDTH_QVGA || w->height != AR_HEIGHT_QVGA))
+ return -EINVAL;
+
+ mutex_lock(&ar->lock);
+ ar->width = w->width;
+ ar->height = w->height;
+ if (ar->width == AR_WIDTH_VGA) {
+ ar->size = AR_SIZE_VGA;
+ ar->frame_bytes = AR_FRAME_BYTES_VGA;
+ ar->line_bytes = AR_LINE_BYTES_VGA;
+ if (vga_interlace)
+ ar->mode = AR_MODE_INTERLACE;
+ else
+ ar->mode = AR_MODE_NORMAL;
+ } else {
+ ar->size = AR_SIZE_QVGA;
+ ar->frame_bytes = AR_FRAME_BYTES_QVGA;
+ ar->line_bytes = AR_LINE_BYTES_QVGA;
+ ar->mode = AR_MODE_INTERLACE;
+ }
+ mutex_unlock(&ar->lock);
+ return 0;
+ }
+ case VIDIOCGFBUF:
+ DEBUG(1, "VIDIOCGFBUF:\n");
+ return -EINVAL;
+ case VIDIOCSFBUF:
+ DEBUG(1, "VIDIOCSFBUF:\n");
+ return -EINVAL;
+ case VIDIOCKEY:
+ DEBUG(1, "VIDIOCKEY:\n");
+ return 0;
+ case VIDIOCGFREQ:
+ DEBUG(1, "VIDIOCGFREQ:\n");
+ return -EINVAL;
+ case VIDIOCSFREQ:
+ DEBUG(1, "VIDIOCSFREQ:\n");
+ return -EINVAL;
+ case VIDIOCGAUDIO:
+ DEBUG(1, "VIDIOCGAUDIO:\n");
+ return -EINVAL;
+ case VIDIOCSAUDIO:
+ DEBUG(1, "VIDIOCSAUDIO:\n");
+ return -EINVAL;
+ case VIDIOCSYNC:
+ DEBUG(1, "VIDIOCSYNC:\n");
+ return -EINVAL;
+ case VIDIOCMCAPTURE:
+ DEBUG(1, "VIDIOCMCAPTURE:\n");
+ return -EINVAL;
+ case VIDIOCGMBUF:
+ DEBUG(1, "VIDIOCGMBUF:\n");
+ return -EINVAL;
+ case VIDIOCGUNIT:
+ DEBUG(1, "VIDIOCGUNIT:\n");
+ return -EINVAL;
+ case VIDIOCGCAPTURE:
+ DEBUG(1, "VIDIOCGCAPTURE:\n");
+ return -EINVAL;
+ case VIDIOCSCAPTURE:
+ DEBUG(1, "VIDIOCSCAPTURE:\n");
+ return -EINVAL;
+ case VIDIOCSPLAYMODE:
+ DEBUG(1, "VIDIOCSPLAYMODE:\n");
+ return -EINVAL;
+ case VIDIOCSWRITEMODE:
+ DEBUG(1, "VIDIOCSWRITEMODE:\n");
+ return -EINVAL;
+ case VIDIOCGPLAYINFO:
+ DEBUG(1, "VIDIOCGPLAYINFO:\n");
+ return -EINVAL;
+ case VIDIOCSMICROCODE:
+ DEBUG(1, "VIDIOCSMICROCODE:\n");
+ return -EINVAL;
+ case VIDIOCGVBIFMT:
+ DEBUG(1, "VIDIOCGVBIFMT:\n");
+ return -EINVAL;
+ case VIDIOCSVBIFMT:
+ DEBUG(1, "VIDIOCSVBIFMT:\n");
+ return -EINVAL;
+ default:
+ DEBUG(1, "Unknown ioctl(0x%08x)\n", cmd);
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static int ar_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, ar_do_ioctl);
+}
+
+#if USE_INT
+/*
+ * Interrupt handler
+ */
+static void ar_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+ struct ar_device *ar = dev;
+ unsigned int line_count;
+ unsigned int line_number;
+ unsigned int arvcr1;
+
+ line_count = ar_inl(ARVHCOUNT); /* line number */
+ if (ar->mode == AR_MODE_INTERLACE && ar->size == AR_SIZE_VGA) {
+ /* operations for interlace mode */
+ if ( line_count < (AR_HEIGHT_VGA/2) ) /* even line */
+ line_number = (line_count << 1);
+ else /* odd line */
+ line_number =
+ (((line_count - (AR_HEIGHT_VGA/2)) << 1) + 1);
+ } else {
+ line_number = line_count;
+ }
+
+ if (line_number == 0) {
+ /*
+ * It is an interrupt for line 0.
+ * we have to start capture.
+ */
+ disable_dma();
+#if 0
+ ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); /* needless? */
+#endif
+ memcpy(ar->frame[0], ar->line_buff, ar->line_bytes);
+#if 0
+ ar_outl(0xa1861300, M32R_DMA0CR0_PORTL);
+#endif
+ enable_dma();
+ ar->start_capture = 1; /* during capture */
+ return;
+ }
+
+ if (ar->start_capture == 1 && line_number <= (ar->height - 1)) {
+ disable_dma();
+ memcpy(ar->frame[line_number], ar->line_buff, ar->line_bytes);
+
+ /*
+ * if captured all line of a frame, disable AR interrupt
+ * and wake a process up.
+ */
+ if (line_number == (ar->height - 1)) { /* end of line */
+
+ ar->start_capture = 0;
+
+ /* disable AR interrupt request */
+ arvcr1 = ar_inl(ARVCR1);
+ arvcr1 &= ~ARVCR1_HIEN; /* clear int. flag */
+ ar_outl(arvcr1, ARVCR1); /* disable */
+ wake_up_interruptible(&ar->wait);
+ } else {
+#if 0
+ ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL);
+ ar_outl(0xa1861300, M32R_DMA0CR0_PORTL);
+#endif
+ enable_dma();
+ }
+ }
+}
+#endif
+
+/*
+ * ar_initialize()
+ * ar_initialize() is called by video_register_device() and
+ * initializes AR LSI and peripherals.
+ *
+ * -1 is returned in all failures.
+ * 0 is returned in success.
+ *
+ */
+static int ar_initialize(struct video_device *dev)
+{
+ struct ar_device *ar = dev->priv;
+ unsigned long cr = 0;
+ int i,found=0;
+
+ DEBUG(1, "ar_initialize:\n");
+
+ /*
+ * initialize AR LSI
+ */
+ ar_outl(0, ARVCR0); /* assert reset of AR LSI */
+ for (i = 0; i < 0x18; i++) /* wait for over 10 cycles @ 27MHz */
+ cpu_relax();
+ ar_outl(ARVCR0_RST, ARVCR0); /* negate reset of AR LSI (enable) */
+ for (i = 0; i < 0x40d; i++) /* wait for over 420 cycles @ 27MHz */
+ cpu_relax();
+
+ /* AR uses INT3 of CPU as interrupt pin. */
+ ar_outl(ARINTSEL_INT3, ARINTSEL);
+
+ if (ar->size == AR_SIZE_QVGA)
+ cr |= ARVCR1_QVGA;
+ if (ar->mode == AR_MODE_NORMAL)
+ cr |= ARVCR1_NORMAL;
+ ar_outl(cr, ARVCR1);
+
+ /*
+ * Initialize IIC so that CPU can communicate with AR LSI,
+ * and send boot commands to AR LSI.
+ */
+ init_iic();
+
+ for (i = 0; i < 0x100000; i++) { /* > 0xa1d10, 56ms */
+ if ((ar_inl(ARVCR0) & ARVCR0_VDS)) { /* VSYNC */
+ found = 1;
+ break;
+ }
+ }
+
+ if (found == 0)
+ return -ENODEV;
+
+ printk("arv: Initializing ");
+
+ iic(2,0x78,0x11,0x01,0x00); /* start */
+ iic(3,0x78,0x12,0x00,0x06);
+ iic(3,0x78,0x12,0x12,0x30);
+ iic(3,0x78,0x12,0x15,0x58);
+ iic(3,0x78,0x12,0x17,0x30);
+ printk(".");
+ iic(3,0x78,0x12,0x1a,0x97);
+ iic(3,0x78,0x12,0x1b,0xff);
+ iic(3,0x78,0x12,0x1c,0xff);
+ iic(3,0x78,0x12,0x26,0x10);
+ iic(3,0x78,0x12,0x27,0x00);
+ printk(".");
+ iic(2,0x78,0x34,0x02,0x00);
+ iic(2,0x78,0x7a,0x10,0x00);
+ iic(2,0x78,0x80,0x39,0x00);
+ iic(2,0x78,0x81,0xe6,0x00);
+ iic(2,0x78,0x8d,0x00,0x00);
+ printk(".");
+ iic(2,0x78,0x8e,0x0c,0x00);
+ iic(2,0x78,0x8f,0x00,0x00);
+#if 0
+ iic(2,0x78,0x90,0x00,0x00); /* AWB on=1 off=0 */
+#endif
+ iic(2,0x78,0x93,0x01,0x00);
+ iic(2,0x78,0x94,0xcd,0x00);
+ iic(2,0x78,0x95,0x00,0x00);
+ printk(".");
+ iic(2,0x78,0x96,0xa0,0x00);
+ iic(2,0x78,0x97,0x00,0x00);
+ iic(2,0x78,0x98,0x60,0x00);
+ iic(2,0x78,0x99,0x01,0x00);
+ iic(2,0x78,0x9a,0x19,0x00);
+ printk(".");
+ iic(2,0x78,0x9b,0x02,0x00);
+ iic(2,0x78,0x9c,0xe8,0x00);
+ iic(2,0x78,0x9d,0x02,0x00);
+ iic(2,0x78,0x9e,0x2e,0x00);
+ iic(2,0x78,0xb8,0x78,0x00);
+ iic(2,0x78,0xba,0x05,0x00);
+#if 0
+ iic(2,0x78,0x83,0x8c,0x00); /* brightness */
+#endif
+ printk(".");
+
+ /* color correction */
+ iic(3,0x78,0x49,0x00,0x95); /* a */
+ iic(3,0x78,0x49,0x01,0x96); /* b */
+ iic(3,0x78,0x49,0x03,0x85); /* c */
+ iic(3,0x78,0x49,0x04,0x97); /* d */
+ iic(3,0x78,0x49,0x02,0x7e); /* e(Lo) */
+ iic(3,0x78,0x49,0x05,0xa4); /* f(Lo) */
+ iic(3,0x78,0x49,0x06,0x04); /* e(Hi) */
+ iic(3,0x78,0x49,0x07,0x04); /* e(Hi) */
+ iic(2,0x78,0x48,0x01,0x00); /* on=1 off=0 */
+
+ printk(".");
+ iic(2,0x78,0x11,0x00,0x00); /* end */
+ printk(" done\n");
+ return 0;
+}
+
+
+void ar_release(struct video_device *vfd)
+{
+ struct ar_device *ar = vfd->priv;
+ mutex_lock(&ar->lock);
+ video_device_release(vfd);
+}
+
+/****************************************************************************
+ *
+ * Video4Linux Module functions
+ *
+ ****************************************************************************/
+static struct file_operations ar_fops = {
+ .owner = THIS_MODULE,
+ .open = video_exclusive_open,
+ .release = video_exclusive_release,
+ .read = ar_read,
+ .ioctl = ar_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device ar_template = {
+ .owner = THIS_MODULE,
+ .name = "Colour AR VGA",
+ .type = VID_TYPE_CAPTURE,
+ .hardware = VID_HARDWARE_ARV,
+ .fops = &ar_fops,
+ .release = ar_release,
+ .minor = -1,
+};
+
+#define ALIGN4(x) ((((int)(x)) & 0x3) == 0)
+static struct ar_device ardev;
+
+static int __init ar_init(void)
+{
+ struct ar_device *ar;
+ int ret;
+ int i;
+
+ DEBUG(1, "ar_init:\n");
+ ret = -EIO;
+ printk(KERN_INFO "arv: Colour AR VGA driver %s\n", VERSION);
+
+ ar = &ardev;
+ memset(ar, 0, sizeof(struct ar_device));
+
+#if USE_INT
+ /* allocate a DMA buffer for 1 line. */
+ ar->line_buff = kmalloc(MAX_AR_LINE_BYTES, GFP_KERNEL | GFP_DMA);
+ if (ar->line_buff == NULL || ! ALIGN4(ar->line_buff)) {
+ printk("arv: buffer allocation failed for DMA.\n");
+ ret = -ENOMEM;
+ goto out_end;
+ }
+#endif
+ /* allocate buffers for a frame */
+ for (i = 0; i < MAX_AR_HEIGHT; i++) {
+ ar->frame[i] = kmalloc(MAX_AR_LINE_BYTES, GFP_KERNEL);
+ if (ar->frame[i] == NULL || ! ALIGN4(ar->frame[i])) {
+ printk("arv: buffer allocation failed for frame.\n");
+ ret = -ENOMEM;
+ goto out_line_buff;
+ }
+ }
+
+ ar->vdev = video_device_alloc();
+ if (!ar->vdev) {
+ printk(KERN_ERR "arv: video_device_alloc() failed\n");
+ return -ENOMEM;
+ }
+ memcpy(ar->vdev, &ar_template, sizeof(ar_template));
+ ar->vdev->priv = ar;
+
+ if (vga) {
+ ar->width = AR_WIDTH_VGA;
+ ar->height = AR_HEIGHT_VGA;
+ ar->size = AR_SIZE_VGA;
+ ar->frame_bytes = AR_FRAME_BYTES_VGA;
+ ar->line_bytes = AR_LINE_BYTES_VGA;
+ if (vga_interlace)
+ ar->mode = AR_MODE_INTERLACE;
+ else
+ ar->mode = AR_MODE_NORMAL;
+ } else {
+ ar->width = AR_WIDTH_QVGA;
+ ar->height = AR_HEIGHT_QVGA;
+ ar->size = AR_SIZE_QVGA;
+ ar->frame_bytes = AR_FRAME_BYTES_QVGA;
+ ar->line_bytes = AR_LINE_BYTES_QVGA;
+ ar->mode = AR_MODE_INTERLACE;
+ }
+ mutex_init(&ar->lock);
+ init_waitqueue_head(&ar->wait);
+
+#if USE_INT
+ if (request_irq(M32R_IRQ_INT3, ar_interrupt, 0, "arv", ar)) {
+ printk("arv: request_irq(%d) failed.\n", M32R_IRQ_INT3);
+ ret = -EIO;
+ goto out_irq;
+ }
+#endif
+
+ if (ar_initialize(ar->vdev) != 0) {
+ printk("arv: M64278 not found.\n");
+ ret = -ENODEV;
+ goto out_dev;
+ }
+
+ /*
+ * ok, we can initialize h/w according to parameters,
+ * so register video device as a frame grabber type.
+ * device is named "video[0-64]".
+ * video_register_device() initializes h/w using ar_initialize().
+ */
+ if (video_register_device(ar->vdev, VFL_TYPE_GRABBER, video_nr) != 0) {
+ /* return -1, -ENFILE(full) or others */
+ printk("arv: register video (Colour AR) failed.\n");
+ ret = -ENODEV;
+ goto out_dev;
+ }
+
+ printk("video%d: Found M64278 VGA (IRQ %d, Freq %dMHz).\n",
+ ar->vdev->minor, M32R_IRQ_INT3, freq);
+
+ return 0;
+
+out_dev:
+#if USE_INT
+ free_irq(M32R_IRQ_INT3, ar);
+
+out_irq:
+#endif
+ for (i = 0; i < MAX_AR_HEIGHT; i++)
+ kfree(ar->frame[i]);
+
+out_line_buff:
+#if USE_INT
+ kfree(ar->line_buff);
+
+out_end:
+#endif
+ return ret;
+}
+
+
+static int __init ar_init_module(void)
+{
+ freq = (boot_cpu_data.bus_clock / 1000000);
+ printk("arv: Bus clock %d\n", freq);
+ if (freq != 50 && freq != 75)
+ freq = DEFAULT_FREQ;
+ return ar_init();
+}
+
+static void __exit ar_cleanup_module(void)
+{
+ struct ar_device *ar;
+ int i;
+
+ ar = &ardev;
+ video_unregister_device(ar->vdev);
+#if USE_INT
+ free_irq(M32R_IRQ_INT3, ar);
+#endif
+ for (i = 0; i < MAX_AR_HEIGHT; i++)
+ kfree(ar->frame[i]);
+#if USE_INT
+ kfree(ar->line_buff);
+#endif
+}
+
+module_init(ar_init_module);
+module_exit(ar_cleanup_module);
+
+MODULE_AUTHOR("Takeo Takahashi <takahashi.takeo@renesas.com>");
+MODULE_DESCRIPTION("Colour AR M64278(VGA) for Video4Linux");
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/video/bt819.c b/linux/drivers/media/video/bt819.c
new file mode 100644
index 000000000..570d5997c
--- /dev/null
+++ b/linux/drivers/media/video/bt819.c
@@ -0,0 +1,643 @@
+/*
+ * bt819 - BT819A VideoStream Decoder (Rockwell Part)
+ *
+ * Copyright (C) 1999 Mike Bernson <mike@mlb.org>
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ *
+ * Modifications for LML33/DC10plus unified driver
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * - moved over to linux>=2.4.x i2c protocol (9/9/2002)
+ *
+ * This code was modify/ported from the saa7111 driver written
+ * by Dave Perks.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/signal.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+
+#include "compat.h"
+#include <linux/videodev.h>
+#include <asm/uaccess.h>
+
+MODULE_DESCRIPTION("Brooktree-819 video decoder driver");
+MODULE_AUTHOR("Mike Bernson & Dave Perks");
+MODULE_LICENSE("GPL");
+
+#include <linux/i2c.h>
+
+#define I2C_NAME(s) (s)->name
+
+#include <linux/video_decoder.h>
+
+static int debug = 0;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+/* ----------------------------------------------------------------------- */
+
+struct bt819 {
+ unsigned char reg[32];
+
+ int initialized;
+ int norm;
+ int input;
+ int enable;
+ int bright;
+ int contrast;
+ int hue;
+ int sat;
+};
+
+struct timing {
+ int hactive;
+ int hdelay;
+ int vactive;
+ int vdelay;
+ int hscale;
+ int vscale;
+};
+
+/* for values, see the bt819 datasheet */
+static struct timing timing_data[] = {
+ {864 - 24, 20, 625 - 2, 1, 0x0504, 0x0000},
+ {858 - 24, 20, 525 - 2, 1, 0x00f8, 0x0000},
+};
+
+#define I2C_BT819 0x8a
+
+/* ----------------------------------------------------------------------- */
+
+static inline int
+bt819_write (struct i2c_client *client,
+ u8 reg,
+ u8 value)
+{
+ struct bt819 *decoder = i2c_get_clientdata(client);
+
+ decoder->reg[reg] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static inline int
+bt819_setbit (struct i2c_client *client,
+ u8 reg,
+ u8 bit,
+ u8 value)
+{
+ struct bt819 *decoder = i2c_get_clientdata(client);
+
+ return bt819_write(client, reg,
+ (decoder->
+ reg[reg] & ~(1 << bit)) |
+ (value ? (1 << bit) : 0));
+}
+
+static int
+bt819_write_block (struct i2c_client *client,
+ const u8 *data,
+ unsigned int len)
+{
+ int ret = -1;
+ u8 reg;
+
+ /* the bt819 has an autoincrement function, use it if
+ * the adapter understands raw I2C */
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ /* do raw I2C, not smbus compatible */
+ struct bt819 *decoder = i2c_get_clientdata(client);
+ u8 block_data[32];
+ int block_len;
+
+ while (len >= 2) {
+ block_len = 0;
+ block_data[block_len++] = reg = data[0];
+ do {
+ block_data[block_len++] =
+ decoder->reg[reg++] = data[1];
+ len -= 2;
+ data += 2;
+ } while (len >= 2 && data[0] == reg &&
+ block_len < 32);
+ if ((ret = i2c_master_send(client, block_data,
+ block_len)) < 0)
+ break;
+ }
+ } else {
+ /* do some slow I2C emulation kind of thing */
+ while (len >= 2) {
+ reg = *data++;
+ if ((ret = bt819_write(client, reg, *data++)) < 0)
+ break;
+ len -= 2;
+ }
+ }
+
+ return ret;
+}
+
+static inline int
+bt819_read (struct i2c_client *client,
+ u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int
+bt819_init (struct i2c_client *client)
+{
+ struct bt819 *decoder = i2c_get_clientdata(client);
+
+ static unsigned char init[] = {
+ //0x1f, 0x00, /* Reset */
+ 0x01, 0x59, /* 0x01 input format */
+ 0x02, 0x00, /* 0x02 temporal decimation */
+ 0x03, 0x12, /* 0x03 Cropping msb */
+ 0x04, 0x16, /* 0x04 Vertical Delay, lsb */
+ 0x05, 0xe0, /* 0x05 Vertical Active lsb */
+ 0x06, 0x80, /* 0x06 Horizontal Delay lsb */
+ 0x07, 0xd0, /* 0x07 Horizontal Active lsb */
+ 0x08, 0x00, /* 0x08 Horizontal Scaling msb */
+ 0x09, 0xf8, /* 0x09 Horizontal Scaling lsb */
+ 0x0a, 0x00, /* 0x0a Brightness control */
+ 0x0b, 0x30, /* 0x0b Miscellaneous control */
+ 0x0c, 0xd8, /* 0x0c Luma Gain lsb */
+ 0x0d, 0xfe, /* 0x0d Chroma Gain (U) lsb */
+ 0x0e, 0xb4, /* 0x0e Chroma Gain (V) msb */
+ 0x0f, 0x00, /* 0x0f Hue control */
+ 0x12, 0x04, /* 0x12 Output Format */
+ 0x13, 0x20, /* 0x13 Vertial Scaling msb 0x00
+ chroma comb OFF, line drop scaling, interlace scaling
+ BUG? Why does turning the chroma comb on fuck up color?
+ Bug in the bt819 stepping on my board?
+ */
+ 0x14, 0x00, /* 0x14 Vertial Scaling lsb */
+ 0x16, 0x07, /* 0x16 Video Timing Polarity
+ ACTIVE=active low
+ FIELD: high=odd,
+ vreset=active high,
+ hreset=active high */
+ 0x18, 0x68, /* 0x18 AGC Delay */
+ 0x19, 0x5d, /* 0x19 Burst Gate Delay */
+ 0x1a, 0x80, /* 0x1a ADC Interface */
+ };
+
+ struct timing *timing = &timing_data[decoder->norm];
+
+ init[0x03 * 2 - 1] =
+ (((timing->vdelay >> 8) & 0x03) << 6) | (((timing->
+ vactive >> 8) &
+ 0x03) << 4) |
+ (((timing->hdelay >> 8) & 0x03) << 2) | ((timing->
+ hactive >> 8) &
+ 0x03);
+ init[0x04 * 2 - 1] = timing->vdelay & 0xff;
+ init[0x05 * 2 - 1] = timing->vactive & 0xff;
+ init[0x06 * 2 - 1] = timing->hdelay & 0xff;
+ init[0x07 * 2 - 1] = timing->hactive & 0xff;
+ init[0x08 * 2 - 1] = timing->hscale >> 8;
+ init[0x09 * 2 - 1] = timing->hscale & 0xff;
+ /* 0x15 in array is address 0x19 */
+ init[0x15 * 2 - 1] = (decoder->norm == 0) ? 115 : 93; /* Chroma burst delay */
+ /* reset */
+ bt819_write(client, 0x1f, 0x00);
+ mdelay(1);
+
+ /* init */
+ return bt819_write_block(client, init, sizeof(init));
+
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int
+bt819_command (struct i2c_client *client,
+ unsigned int cmd,
+ void *arg)
+{
+ int temp;
+
+ struct bt819 *decoder = i2c_get_clientdata(client);
+
+ if (!decoder->initialized) { // First call to bt819_init could be
+ bt819_init(client); // without #FRST = 0
+ decoder->initialized = 1;
+ }
+
+ switch (cmd) {
+
+ case 0:
+ /* This is just for testing!!! */
+ bt819_init(client);
+ break;
+
+ case DECODER_GET_CAPABILITIES:
+ {
+ struct video_decoder_capability *cap = arg;
+
+ cap->flags = VIDEO_DECODER_PAL |
+ VIDEO_DECODER_NTSC |
+ VIDEO_DECODER_AUTO |
+ VIDEO_DECODER_CCIR;
+ cap->inputs = 8;
+ cap->outputs = 1;
+ }
+ break;
+
+ case DECODER_GET_STATUS:
+ {
+ int *iarg = arg;
+ int status;
+ int res;
+
+ status = bt819_read(client, 0x00);
+ res = 0;
+ if ((status & 0x80)) {
+ res |= DECODER_STATUS_GOOD;
+ }
+ switch (decoder->norm) {
+ case VIDEO_MODE_NTSC:
+ res |= DECODER_STATUS_NTSC;
+ break;
+ case VIDEO_MODE_PAL:
+ res |= DECODER_STATUS_PAL;
+ break;
+ default:
+ case VIDEO_MODE_AUTO:
+ if ((status & 0x10)) {
+ res |= DECODER_STATUS_PAL;
+ } else {
+ res |= DECODER_STATUS_NTSC;
+ }
+ break;
+ }
+ res |= DECODER_STATUS_COLOR;
+ *iarg = res;
+
+ dprintk(1, KERN_INFO "%s: get status %x\n", I2C_NAME(client),
+ *iarg);
+ }
+ break;
+
+ case DECODER_SET_NORM:
+ {
+ int *iarg = arg;
+ struct timing *timing = NULL;
+
+ dprintk(1, KERN_INFO "%s: set norm %x\n", I2C_NAME(client),
+ *iarg);
+
+ switch (*iarg) {
+ case VIDEO_MODE_NTSC:
+ bt819_setbit(client, 0x01, 0, 1);
+ bt819_setbit(client, 0x01, 1, 0);
+ bt819_setbit(client, 0x01, 5, 0);
+ bt819_write(client, 0x18, 0x68);
+ bt819_write(client, 0x19, 0x5d);
+ //bt819_setbit(client, 0x1a, 5, 1);
+ timing = &timing_data[VIDEO_MODE_NTSC];
+ break;
+ case VIDEO_MODE_PAL:
+ bt819_setbit(client, 0x01, 0, 1);
+ bt819_setbit(client, 0x01, 1, 1);
+ bt819_setbit(client, 0x01, 5, 1);
+ bt819_write(client, 0x18, 0x7f);
+ bt819_write(client, 0x19, 0x72);
+ //bt819_setbit(client, 0x1a, 5, 0);
+ timing = &timing_data[VIDEO_MODE_PAL];
+ break;
+ case VIDEO_MODE_AUTO:
+ bt819_setbit(client, 0x01, 0, 0);
+ bt819_setbit(client, 0x01, 1, 0);
+ break;
+ default:
+ dprintk(1,
+ KERN_ERR
+ "%s: unsupported norm %d\n",
+ I2C_NAME(client), *iarg);
+ return -EINVAL;
+ }
+
+ if (timing) {
+ bt819_write(client, 0x03,
+ (((timing->vdelay >> 8) & 0x03) << 6) |
+ (((timing->vactive >> 8) & 0x03) << 4) |
+ (((timing->hdelay >> 8) & 0x03) << 2) |
+ ((timing->hactive >> 8) & 0x03) );
+ bt819_write(client, 0x04, timing->vdelay & 0xff);
+ bt819_write(client, 0x05, timing->vactive & 0xff);
+ bt819_write(client, 0x06, timing->hdelay & 0xff);
+ bt819_write(client, 0x07, timing->hactive & 0xff);
+ bt819_write(client, 0x08, (timing->hscale >> 8) & 0xff);
+ bt819_write(client, 0x09, timing->hscale & 0xff);
+ }
+
+ decoder->norm = *iarg;
+ }
+ break;
+
+ case DECODER_SET_INPUT:
+ {
+ int *iarg = arg;
+
+ dprintk(1, KERN_INFO "%s: set input %x\n", I2C_NAME(client),
+ *iarg);
+
+ if (*iarg < 0 || *iarg > 7) {
+ return -EINVAL;
+ }
+
+ if (decoder->input != *iarg) {
+ decoder->input = *iarg;
+ /* select mode */
+ if (decoder->input == 0) {
+ bt819_setbit(client, 0x0b, 6, 0);
+ bt819_setbit(client, 0x1a, 1, 1);
+ } else {
+ bt819_setbit(client, 0x0b, 6, 1);
+ bt819_setbit(client, 0x1a, 1, 0);
+ }
+ }
+ }
+ break;
+
+ case DECODER_SET_OUTPUT:
+ {
+ int *iarg = arg;
+
+ dprintk(1, KERN_INFO "%s: set output %x\n", I2C_NAME(client),
+ *iarg);
+
+ /* not much choice of outputs */
+ if (*iarg != 0) {
+ return -EINVAL;
+ }
+ }
+ break;
+
+ case DECODER_ENABLE_OUTPUT:
+ {
+ int *iarg = arg;
+ int enable = (*iarg != 0);
+
+ dprintk(1, KERN_INFO "%s: enable output %x\n",
+ I2C_NAME(client), *iarg);
+
+ if (decoder->enable != enable) {
+ decoder->enable = enable;
+
+ if (decoder->enable) {
+ bt819_setbit(client, 0x16, 7, 0);
+ } else {
+ bt819_setbit(client, 0x16, 7, 1);
+ }
+ }
+ }
+ break;
+
+ case DECODER_SET_PICTURE:
+ {
+ struct video_picture *pic = arg;
+
+ dprintk(1,
+ KERN_INFO
+ "%s: set picture brightness %d contrast %d colour %d\n",
+ I2C_NAME(client), pic->brightness, pic->contrast,
+ pic->colour);
+
+
+ if (decoder->bright != pic->brightness) {
+ /* We want -128 to 127 we get 0-65535 */
+ decoder->bright = pic->brightness;
+ bt819_write(client, 0x0a,
+ (decoder->bright >> 8) - 128);
+ }
+
+ if (decoder->contrast != pic->contrast) {
+ /* We want 0 to 511 we get 0-65535 */
+ decoder->contrast = pic->contrast;
+ bt819_write(client, 0x0c,
+ (decoder->contrast >> 7) & 0xff);
+ bt819_setbit(client, 0x0b, 2,
+ ((decoder->contrast >> 15) & 0x01));
+ }
+
+ if (decoder->sat != pic->colour) {
+ /* We want 0 to 511 we get 0-65535 */
+ decoder->sat = pic->colour;
+ bt819_write(client, 0x0d,
+ (decoder->sat >> 7) & 0xff);
+ bt819_setbit(client, 0x0b, 1,
+ ((decoder->sat >> 15) & 0x01));
+
+ temp = (decoder->sat * 201) / 237;
+ bt819_write(client, 0x0e, (temp >> 7) & 0xff);
+ bt819_setbit(client, 0x0b, 0, (temp >> 15) & 0x01);
+ }
+
+ if (decoder->hue != pic->hue) {
+ /* We want -128 to 127 we get 0-65535 */
+ decoder->hue = pic->hue;
+ bt819_write(client, 0x0f,
+ 128 - (decoder->hue >> 8));
+ }
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/*
+ * Generic i2c probe
+ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+ */
+static unsigned short normal_i2c[] = {
+ I2C_BT819 >> 1,
+ I2C_CLIENT_END,
+};
+
+static unsigned short ignore = I2C_CLIENT_END;
+
+static struct i2c_client_address_data addr_data = {
+ .normal_i2c = normal_i2c,
+ .probe = &ignore,
+ .ignore = &ignore,
+};
+
+static struct i2c_driver i2c_driver_bt819;
+
+static int
+bt819_detect_client (struct i2c_adapter *adapter,
+ int address,
+ int kind)
+{
+ int i, id;
+ struct bt819 *decoder;
+ struct i2c_client *client;
+
+ dprintk(1,
+ KERN_INFO
+ "saa7111.c: detecting bt819 client on address 0x%x\n",
+ address << 1);
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return 0;
+
+ client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (client == 0)
+ return -ENOMEM;
+ client->addr = address;
+ client->adapter = adapter;
+ client->driver = &i2c_driver_bt819;
+
+ decoder = kzalloc(sizeof(struct bt819), GFP_KERNEL);
+ if (decoder == NULL) {
+ kfree(client);
+ return -ENOMEM;
+ }
+ decoder->norm = VIDEO_MODE_NTSC;
+ decoder->input = 0;
+ decoder->enable = 1;
+ decoder->bright = 32768;
+ decoder->contrast = 32768;
+ decoder->hue = 32768;
+ decoder->sat = 32768;
+ decoder->initialized = 0;
+ i2c_set_clientdata(client, decoder);
+
+ id = bt819_read(client, 0x17);
+ switch (id & 0xf0) {
+ case 0x70:
+ strlcpy(I2C_NAME(client), "bt819a", sizeof(I2C_NAME(client)));
+ break;
+ case 0x60:
+ strlcpy(I2C_NAME(client), "bt817a", sizeof(I2C_NAME(client)));
+ break;
+ case 0x20:
+ strlcpy(I2C_NAME(client), "bt815a", sizeof(I2C_NAME(client)));
+ break;
+ default:
+ dprintk(1,
+ KERN_ERR
+ "bt819: unknown chip version 0x%x (ver 0x%x)\n",
+ id & 0xf0, id & 0x0f);
+ kfree(decoder);
+ kfree(client);
+ return 0;
+ }
+
+ i = i2c_attach_client(client);
+ if (i) {
+ kfree(client);
+ kfree(decoder);
+ return i;
+ }
+
+ i = bt819_init(client);
+ if (i < 0) {
+ dprintk(1, KERN_ERR "%s_attach: init status %d\n",
+ I2C_NAME(client), i);
+ } else {
+ dprintk(1,
+ KERN_INFO
+ "%s_attach: chip version 0x%x at address 0x%x\n",
+ I2C_NAME(client), id & 0x0f,
+ client->addr << 1);
+ }
+
+ return 0;
+}
+
+static int
+bt819_attach_adapter (struct i2c_adapter *adapter)
+{
+ return i2c_probe(adapter, &addr_data, &bt819_detect_client);
+}
+
+static int
+bt819_detach_client (struct i2c_client *client)
+{
+ struct bt819 *decoder = i2c_get_clientdata(client);
+ int err;
+
+ err = i2c_detach_client(client);
+ if (err) {
+ return err;
+ }
+
+ kfree(decoder);
+ kfree(client);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_driver i2c_driver_bt819 = {
+ .driver = {
+ .name = "bt819",
+ },
+
+ .id = I2C_DRIVERID_BT819,
+
+ .attach_adapter = bt819_attach_adapter,
+ .detach_client = bt819_detach_client,
+ .command = bt819_command,
+};
+
+static int __init
+bt819_init_module (void)
+{
+ return i2c_add_driver(&i2c_driver_bt819);
+}
+
+static void __exit
+bt819_exit (void)
+{
+ i2c_del_driver(&i2c_driver_bt819);
+}
+
+module_init(bt819_init_module);
+module_exit(bt819_exit);
diff --git a/linux/drivers/media/video/bt856.c b/linux/drivers/media/video/bt856.c
new file mode 100644
index 000000000..cb5c2160f
--- /dev/null
+++ b/linux/drivers/media/video/bt856.c
@@ -0,0 +1,426 @@
+/*
+ * bt856 - BT856A Digital Video Encoder (Rockwell Part)
+ *
+ * Copyright (C) 1999 Mike Bernson <mike@mlb.org>
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ *
+ * Modifications for LML33/DC10plus unified driver
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * This code was modify/ported from the saa7111 driver written
+ * by Dave Perks.
+ *
+ * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * - moved over to linux>=2.4.x i2c protocol (9/9/2002)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/signal.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+
+#include "compat.h"
+#include <linux/videodev.h>
+#include <asm/uaccess.h>
+
+MODULE_DESCRIPTION("Brooktree-856A video encoder driver");
+MODULE_AUTHOR("Mike Bernson & Dave Perks");
+MODULE_LICENSE("GPL");
+
+#include <linux/i2c.h>
+
+#define I2C_NAME(s) (s)->name
+
+#include <linux/video_encoder.h>
+
+static int debug = 0;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+/* ----------------------------------------------------------------------- */
+
+#define REG_OFFSET 0xDA
+#define BT856_NR_REG 6
+
+struct bt856 {
+ unsigned char reg[BT856_NR_REG];
+
+ int norm;
+ int enable;
+};
+
+#define I2C_BT856 0x88
+
+/* ----------------------------------------------------------------------- */
+
+static inline int
+bt856_write (struct i2c_client *client,
+ u8 reg,
+ u8 value)
+{
+ struct bt856 *encoder = i2c_get_clientdata(client);
+
+ encoder->reg[reg - REG_OFFSET] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static inline int
+bt856_setbit (struct i2c_client *client,
+ u8 reg,
+ u8 bit,
+ u8 value)
+{
+ struct bt856 *encoder = i2c_get_clientdata(client);
+
+ return bt856_write(client, reg,
+ (encoder->
+ reg[reg - REG_OFFSET] & ~(1 << bit)) |
+ (value ? (1 << bit) : 0));
+}
+
+static void
+bt856_dump (struct i2c_client *client)
+{
+ int i;
+ struct bt856 *encoder = i2c_get_clientdata(client);
+
+ printk(KERN_INFO "%s: register dump:", I2C_NAME(client));
+ for (i = 0; i < BT856_NR_REG; i += 2)
+ printk(" %02x", encoder->reg[i]);
+ printk("\n");
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int
+bt856_command (struct i2c_client *client,
+ unsigned int cmd,
+ void *arg)
+{
+ struct bt856 *encoder = i2c_get_clientdata(client);
+
+ switch (cmd) {
+
+ case 0:
+ /* This is just for testing!!! */
+ dprintk(1, KERN_INFO "bt856: init\n");
+ bt856_write(client, 0xdc, 0x18);
+ bt856_write(client, 0xda, 0);
+ bt856_write(client, 0xde, 0);
+
+ bt856_setbit(client, 0xdc, 3, 1);
+ //bt856_setbit(client, 0xdc, 6, 0);
+ bt856_setbit(client, 0xdc, 4, 1);
+
+ switch (encoder->norm) {
+
+ case VIDEO_MODE_NTSC:
+ bt856_setbit(client, 0xdc, 2, 0);
+ break;
+
+ case VIDEO_MODE_PAL:
+ bt856_setbit(client, 0xdc, 2, 1);
+ break;
+ }
+
+ bt856_setbit(client, 0xdc, 1, 1);
+ bt856_setbit(client, 0xde, 4, 0);
+ bt856_setbit(client, 0xde, 3, 1);
+ if (debug != 0)
+ bt856_dump(client);
+ break;
+
+ case ENCODER_GET_CAPABILITIES:
+ {
+ struct video_encoder_capability *cap = arg;
+
+ dprintk(1, KERN_INFO "%s: get capabilities\n",
+ I2C_NAME(client));
+
+ cap->flags = VIDEO_ENCODER_PAL |
+ VIDEO_ENCODER_NTSC |
+ VIDEO_ENCODER_CCIR;
+ cap->inputs = 2;
+ cap->outputs = 1;
+ }
+ break;
+
+ case ENCODER_SET_NORM:
+ {
+ int *iarg = arg;
+
+ dprintk(1, KERN_INFO "%s: set norm %d\n", I2C_NAME(client),
+ *iarg);
+
+ switch (*iarg) {
+
+ case VIDEO_MODE_NTSC:
+ bt856_setbit(client, 0xdc, 2, 0);
+ break;
+
+ case VIDEO_MODE_PAL:
+ bt856_setbit(client, 0xdc, 2, 1);
+ bt856_setbit(client, 0xda, 0, 0);
+ //bt856_setbit(client, 0xda, 0, 1);
+ break;
+
+ default:
+ return -EINVAL;
+
+ }
+ encoder->norm = *iarg;
+ if (debug != 0)
+ bt856_dump(client);
+ }
+ break;
+
+ case ENCODER_SET_INPUT:
+ {
+ int *iarg = arg;
+
+ dprintk(1, KERN_INFO "%s: set input %d\n", I2C_NAME(client),
+ *iarg);
+
+ /* We only have video bus.
+ * iarg = 0: input is from bt819
+ * iarg = 1: input is from ZR36060 */
+
+ switch (*iarg) {
+
+ case 0:
+ bt856_setbit(client, 0xde, 4, 0);
+ bt856_setbit(client, 0xde, 3, 1);
+ bt856_setbit(client, 0xdc, 3, 1);
+ bt856_setbit(client, 0xdc, 6, 0);
+ break;
+ case 1:
+ bt856_setbit(client, 0xde, 4, 0);
+ bt856_setbit(client, 0xde, 3, 1);
+ bt856_setbit(client, 0xdc, 3, 1);
+ bt856_setbit(client, 0xdc, 6, 1);
+ break;
+ case 2: // Color bar
+ bt856_setbit(client, 0xdc, 3, 0);
+ bt856_setbit(client, 0xde, 4, 1);
+ break;
+ default:
+ return -EINVAL;
+
+ }
+
+ if (debug != 0)
+ bt856_dump(client);
+ }
+ break;
+
+ case ENCODER_SET_OUTPUT:
+ {
+ int *iarg = arg;
+
+ dprintk(1, KERN_INFO "%s: set output %d\n", I2C_NAME(client),
+ *iarg);
+
+ /* not much choice of outputs */
+ if (*iarg != 0) {
+ return -EINVAL;
+ }
+ }
+ break;
+
+ case ENCODER_ENABLE_OUTPUT:
+ {
+ int *iarg = arg;
+
+ encoder->enable = !!*iarg;
+
+ dprintk(1, KERN_INFO "%s: enable output %d\n",
+ I2C_NAME(client), encoder->enable);
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/*
+ * Generic i2c probe
+ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+ */
+static unsigned short normal_i2c[] = { I2C_BT856 >> 1, I2C_CLIENT_END };
+
+static unsigned short ignore = I2C_CLIENT_END;
+
+static struct i2c_client_address_data addr_data = {
+ .normal_i2c = normal_i2c,
+ .probe = &ignore,
+ .ignore = &ignore,
+};
+
+static struct i2c_driver i2c_driver_bt856;
+
+static int
+bt856_detect_client (struct i2c_adapter *adapter,
+ int address,
+ int kind)
+{
+ int i;
+ struct i2c_client *client;
+ struct bt856 *encoder;
+
+ dprintk(1,
+ KERN_INFO
+ "bt856.c: detecting bt856 client on address 0x%x\n",
+ address << 1);
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return 0;
+
+ client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (client == 0)
+ return -ENOMEM;
+ client->addr = address;
+ client->adapter = adapter;
+ client->driver = &i2c_driver_bt856;
+ strlcpy(I2C_NAME(client), "bt856", sizeof(I2C_NAME(client)));
+
+ encoder = kzalloc(sizeof(struct bt856), GFP_KERNEL);
+ if (encoder == NULL) {
+ kfree(client);
+ return -ENOMEM;
+ }
+ encoder->norm = VIDEO_MODE_NTSC;
+ encoder->enable = 1;
+ i2c_set_clientdata(client, encoder);
+
+ i = i2c_attach_client(client);
+ if (i) {
+ kfree(client);
+ kfree(encoder);
+ return i;
+ }
+
+ bt856_write(client, 0xdc, 0x18);
+ bt856_write(client, 0xda, 0);
+ bt856_write(client, 0xde, 0);
+
+ bt856_setbit(client, 0xdc, 3, 1);
+ //bt856_setbit(client, 0xdc, 6, 0);
+ bt856_setbit(client, 0xdc, 4, 1);
+
+ switch (encoder->norm) {
+
+ case VIDEO_MODE_NTSC:
+ bt856_setbit(client, 0xdc, 2, 0);
+ break;
+
+ case VIDEO_MODE_PAL:
+ bt856_setbit(client, 0xdc, 2, 1);
+ break;
+ }
+
+ bt856_setbit(client, 0xdc, 1, 1);
+ bt856_setbit(client, 0xde, 4, 0);
+ bt856_setbit(client, 0xde, 3, 1);
+
+ if (debug != 0)
+ bt856_dump(client);
+
+ dprintk(1, KERN_INFO "%s_attach: at address 0x%x\n", I2C_NAME(client),
+ client->addr << 1);
+
+ return 0;
+}
+
+static int
+bt856_attach_adapter (struct i2c_adapter *adapter)
+{
+ dprintk(1,
+ KERN_INFO
+ "bt856.c: starting probe for adapter %s (0x%x)\n",
+ I2C_NAME(adapter), adapter->id);
+ return i2c_probe(adapter, &addr_data, &bt856_detect_client);
+}
+
+static int
+bt856_detach_client (struct i2c_client *client)
+{
+ struct bt856 *encoder = i2c_get_clientdata(client);
+ int err;
+
+ err = i2c_detach_client(client);
+ if (err) {
+ return err;
+ }
+
+ kfree(encoder);
+ kfree(client);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_driver i2c_driver_bt856 = {
+ .driver = {
+ .name = "bt856",
+ },
+
+ .id = I2C_DRIVERID_BT856,
+
+ .attach_adapter = bt856_attach_adapter,
+ .detach_client = bt856_detach_client,
+ .command = bt856_command,
+};
+
+static int __init
+bt856_init (void)
+{
+ return i2c_add_driver(&i2c_driver_bt856);
+}
+
+static void __exit
+bt856_exit (void)
+{
+ i2c_del_driver(&i2c_driver_bt856);
+}
+
+module_init(bt856_init);
+module_exit(bt856_exit);
diff --git a/linux/drivers/media/video/bw-qcam.c b/linux/drivers/media/video/bw-qcam.c
new file mode 100644
index 000000000..0a276d967
--- /dev/null
+++ b/linux/drivers/media/video/bw-qcam.c
@@ -0,0 +1,1029 @@
+/*
+ * QuickCam Driver For Video4Linux.
+ *
+ * Video4Linux conversion work by Alan Cox.
+ * Parport compatibility by Phil Blundell.
+ * Busy loop avoidance by Mark Cooke.
+ *
+ * Module parameters:
+ *
+ * maxpoll=<1 - 5000>
+ *
+ * When polling the QuickCam for a response, busy-wait for a
+ * maximum of this many loops. The default of 250 gives little
+ * impact on interactive response.
+ *
+ * NOTE: If this parameter is set too high, the processor
+ * will busy wait until this loop times out, and then
+ * slowly poll for a further 5 seconds before failing
+ * the transaction. You have been warned.
+ *
+ * yieldlines=<1 - 250>
+ *
+ * When acquiring a frame from the camera, the data gathering
+ * loop will yield back to the scheduler after completing
+ * this many lines. The default of 4 provides a trade-off
+ * between increased frame acquisition time and impact on
+ * interactive response.
+ */
+
+/* qcam-lib.c -- Library for programming with the Connectix QuickCam.
+ * See the included documentation for usage instructions and details
+ * of the protocol involved. */
+
+
+/* Version 0.5, August 4, 1996 */
+/* Version 0.7, August 27, 1996 */
+/* Version 0.9, November 17, 1996 */
+
+
+/******************************************************************
+
+Copyright (C) 1996 by Scott Laird
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/parport.h>
+#include <linux/sched.h>
+#include "compat.h"
+#include <linux/videodev.h>
+#include <linux/mutex.h>
+#include <asm/uaccess.h>
+
+#include "bw-qcam.h"
+
+static unsigned int maxpoll=250; /* Maximum busy-loop count for qcam I/O */
+static unsigned int yieldlines=4; /* Yield after this many during capture */
+static int video_nr = -1;
+
+module_param(maxpoll, int, 0);
+module_param(yieldlines, int, 0);
+module_param(video_nr, int, 0);
+
+static inline int read_lpstatus(struct qcam_device *q)
+{
+ return parport_read_status(q->pport);
+}
+
+static inline int read_lpdata(struct qcam_device *q)
+{
+ return parport_read_data(q->pport);
+}
+
+static inline void write_lpdata(struct qcam_device *q, int d)
+{
+ parport_write_data(q->pport, d);
+}
+
+static inline void write_lpcontrol(struct qcam_device *q, int d)
+{
+ parport_write_control(q->pport, d);
+}
+
+static int qc_waithand(struct qcam_device *q, int val);
+static int qc_command(struct qcam_device *q, int command);
+static int qc_readparam(struct qcam_device *q);
+static int qc_setscanmode(struct qcam_device *q);
+static int qc_readbytes(struct qcam_device *q, char buffer[]);
+
+static struct video_device qcam_template;
+
+static int qc_calibrate(struct qcam_device *q)
+{
+ /*
+ * Bugfix by Hanno Mueller hmueller@kabel.de, Mai 21 96
+ * The white balance is an individiual value for each
+ * quickcam.
+ */
+
+ int value;
+ int count = 0;
+
+ qc_command(q, 27); /* AutoAdjustOffset */
+ qc_command(q, 0); /* Dummy Parameter, ignored by the camera */
+
+ /* GetOffset (33) will read 255 until autocalibration */
+ /* is finished. After that, a value of 1-254 will be */
+ /* returned. */
+
+ do {
+ qc_command(q, 33);
+ value = qc_readparam(q);
+ mdelay(1);
+ schedule();
+ count++;
+ } while (value == 0xff && count<2048);
+
+ q->whitebal = value;
+ return value;
+}
+
+/* Initialize the QuickCam driver control structure. This is where
+ * defaults are set for people who don't have a config file.*/
+
+static struct qcam_device *qcam_init(struct parport *port)
+{
+ struct qcam_device *q;
+
+ q = kmalloc(sizeof(struct qcam_device), GFP_KERNEL);
+ if(q==NULL)
+ return NULL;
+
+ q->pport = port;
+ q->pdev = parport_register_device(port, "bw-qcam", NULL, NULL,
+ NULL, 0, NULL);
+ if (q->pdev == NULL)
+ {
+ printk(KERN_ERR "bw-qcam: couldn't register for %s.\n",
+ port->name);
+ kfree(q);
+ return NULL;
+ }
+
+ memcpy(&q->vdev, &qcam_template, sizeof(qcam_template));
+
+ mutex_init(&q->lock);
+
+ q->port_mode = (QC_ANY | QC_NOTSET);
+ q->width = 320;
+ q->height = 240;
+ q->bpp = 4;
+ q->transfer_scale = 2;
+ q->contrast = 192;
+ q->brightness = 180;
+ q->whitebal = 105;
+ q->top = 1;
+ q->left = 14;
+ q->mode = -1;
+ q->status = QC_PARAM_CHANGE;
+ return q;
+}
+
+
+/* qc_command is probably a bit of a misnomer -- it's used to send
+ * bytes *to* the camera. Generally, these bytes are either commands
+ * or arguments to commands, so the name fits, but it still bugs me a
+ * bit. See the documentation for a list of commands. */
+
+static int qc_command(struct qcam_device *q, int command)
+{
+ int n1, n2;
+ int cmd;
+
+ write_lpdata(q, command);
+ write_lpcontrol(q, 6);
+
+ n1 = qc_waithand(q, 1);
+
+ write_lpcontrol(q, 0xe);
+ n2 = qc_waithand(q, 0);
+
+ cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
+ return cmd;
+}
+
+static int qc_readparam(struct qcam_device *q)
+{
+ int n1, n2;
+ int cmd;
+
+ write_lpcontrol(q, 6);
+ n1 = qc_waithand(q, 1);
+
+ write_lpcontrol(q, 0xe);
+ n2 = qc_waithand(q, 0);
+
+ cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
+ return cmd;
+}
+
+/* qc_waithand busy-waits for a handshake signal from the QuickCam.
+ * Almost all communication with the camera requires handshaking. */
+
+static int qc_waithand(struct qcam_device *q, int val)
+{
+ int status;
+ int runs=0;
+
+ if (val)
+ {
+ while (!((status = read_lpstatus(q)) & 8))
+ {
+ /* 1000 is enough spins on the I/O for all normal
+ cases, at that point we start to poll slowly
+ until the camera wakes up. However, we are
+ busy blocked until the camera responds, so
+ setting it lower is much better for interactive
+ response. */
+
+ if(runs++>maxpoll)
+ {
+ msleep_interruptible(5);
+ }
+ if(runs>(maxpoll+1000)) /* 5 seconds */
+ return -1;
+ }
+ }
+ else
+ {
+ while (((status = read_lpstatus(q)) & 8))
+ {
+ /* 1000 is enough spins on the I/O for all normal
+ cases, at that point we start to poll slowly
+ until the camera wakes up. However, we are
+ busy blocked until the camera responds, so
+ setting it lower is much better for interactive
+ response. */
+
+ if(runs++>maxpoll)
+ {
+ msleep_interruptible(5);
+ }
+ if(runs++>(maxpoll+1000)) /* 5 seconds */
+ return -1;
+ }
+ }
+
+ return status;
+}
+
+/* Waithand2 is used when the qcam is in bidirectional mode, and the
+ * handshaking signal is CamRdy2 (bit 0 of data reg) instead of CamRdy1
+ * (bit 3 of status register). It also returns the last value read,
+ * since this data is useful. */
+
+static unsigned int qc_waithand2(struct qcam_device *q, int val)
+{
+ unsigned int status;
+ int runs=0;
+
+ do
+ {
+ status = read_lpdata(q);
+ /* 1000 is enough spins on the I/O for all normal
+ cases, at that point we start to poll slowly
+ until the camera wakes up. However, we are
+ busy blocked until the camera responds, so
+ setting it lower is much better for interactive
+ response. */
+
+ if(runs++>maxpoll)
+ {
+ msleep_interruptible(5);
+ }
+ if(runs++>(maxpoll+1000)) /* 5 seconds */
+ return 0;
+ }
+ while ((status & 1) != val);
+
+ return status;
+}
+
+
+/* Try to detect a QuickCam. It appears to flash the upper 4 bits of
+ the status register at 5-10 Hz. This is only used in the autoprobe
+ code. Be aware that this isn't the way Connectix detects the
+ camera (they send a reset and try to handshake), but this should be
+ almost completely safe, while their method screws up my printer if
+ I plug it in before the camera. */
+
+static int qc_detect(struct qcam_device *q)
+{
+ int reg, lastreg;
+ int count = 0;
+ int i;
+
+ lastreg = reg = read_lpstatus(q) & 0xf0;
+
+ for (i = 0; i < 500; i++)
+ {
+ reg = read_lpstatus(q) & 0xf0;
+ if (reg != lastreg)
+ count++;
+ lastreg = reg;
+ mdelay(2);
+ }
+
+
+#if 0
+ /* Force camera detection during testing. Sometimes the camera
+ won't be flashing these bits. Possibly unloading the module
+ in the middle of a grab? Or some timeout condition?
+ I've seen this parameter as low as 19 on my 450Mhz box - mpc */
+ printk("Debugging: QCam detection counter <30-200 counts as detected>: %d\n", count);
+ return 1;
+#endif
+
+ /* Be (even more) liberal in what you accept... */
+
+/* if (count > 30 && count < 200) */
+ if (count > 20 && count < 300)
+ return 1; /* found */
+ else
+ return 0; /* not found */
+}
+
+
+/* Reset the QuickCam. This uses the same sequence the Windows
+ * QuickPic program uses. Someone with a bi-directional port should
+ * check that bi-directional mode is detected right, and then
+ * implement bi-directional mode in qc_readbyte(). */
+
+static void qc_reset(struct qcam_device *q)
+{
+ switch (q->port_mode & QC_FORCE_MASK)
+ {
+ case QC_FORCE_UNIDIR:
+ q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR;
+ break;
+
+ case QC_FORCE_BIDIR:
+ q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR;
+ break;
+
+ case QC_ANY:
+ write_lpcontrol(q, 0x20);
+ write_lpdata(q, 0x75);
+
+ if (read_lpdata(q) != 0x75) {
+ q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR;
+ } else {
+ q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR;
+ }
+ break;
+ }
+
+ write_lpcontrol(q, 0xb);
+ udelay(250);
+ write_lpcontrol(q, 0xe);
+ qc_setscanmode(q); /* in case port_mode changed */
+}
+
+
+/* Decide which scan mode to use. There's no real requirement that
+ * the scanmode match the resolution in q->height and q-> width -- the
+ * camera takes the picture at the resolution specified in the
+ * "scanmode" and then returns the image at the resolution specified
+ * with the resolution commands. If the scan is bigger than the
+ * requested resolution, the upper-left hand corner of the scan is
+ * returned. If the scan is smaller, then the rest of the image
+ * returned contains garbage. */
+
+static int qc_setscanmode(struct qcam_device *q)
+{
+ int old_mode = q->mode;
+
+ switch (q->transfer_scale)
+ {
+ case 1:
+ q->mode = 0;
+ break;
+ case 2:
+ q->mode = 4;
+ break;
+ case 4:
+ q->mode = 8;
+ break;
+ }
+
+ switch (q->bpp)
+ {
+ case 4:
+ break;
+ case 6:
+ q->mode += 2;
+ break;
+ }
+
+ switch (q->port_mode & QC_MODE_MASK)
+ {
+ case QC_BIDIR:
+ q->mode += 1;
+ break;
+ case QC_NOTSET:
+ case QC_UNIDIR:
+ break;
+ }
+
+ if (q->mode != old_mode)
+ q->status |= QC_PARAM_CHANGE;
+
+ return 0;
+}
+
+
+/* Reset the QuickCam and program for brightness, contrast,
+ * white-balance, and resolution. */
+
+static void qc_set(struct qcam_device *q)
+{
+ int val;
+ int val2;
+
+ qc_reset(q);
+
+ /* Set the brightness. Yes, this is repetitive, but it works.
+ * Shorter versions seem to fail subtly. Feel free to try :-). */
+ /* I think the problem was in qc_command, not here -- bls */
+
+ qc_command(q, 0xb);
+ qc_command(q, q->brightness);
+
+ val = q->height / q->transfer_scale;
+ qc_command(q, 0x11);
+ qc_command(q, val);
+ if ((q->port_mode & QC_MODE_MASK) == QC_UNIDIR && q->bpp == 6) {
+ /* The normal "transfers per line" calculation doesn't seem to work
+ as expected here (and yet it works fine in qc_scan). No idea
+ why this case is the odd man out. Fortunately, Laird's original
+ working version gives me a good way to guess at working values.
+ -- bls */
+ val = q->width;
+ val2 = q->transfer_scale * 4;
+ } else {
+ val = q->width * q->bpp;
+ val2 = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) *
+ q->transfer_scale;
+ }
+ val = (val + val2 - 1) / val2;
+ qc_command(q, 0x13);
+ qc_command(q, val);
+
+ /* Setting top and left -- bls */
+ qc_command(q, 0xd);
+ qc_command(q, q->top);
+ qc_command(q, 0xf);
+ qc_command(q, q->left / 2);
+
+ qc_command(q, 0x19);
+ qc_command(q, q->contrast);
+ qc_command(q, 0x1f);
+ qc_command(q, q->whitebal);
+
+ /* Clear flag that we must update the grabbing parameters on the camera
+ before we grab the next frame */
+ q->status &= (~QC_PARAM_CHANGE);
+}
+
+/* Qc_readbytes reads some bytes from the QC and puts them in
+ the supplied buffer. It returns the number of bytes read,
+ or -1 on error. */
+
+static inline int qc_readbytes(struct qcam_device *q, char buffer[])
+{
+ int ret=1;
+ unsigned int hi, lo;
+ unsigned int hi2, lo2;
+ static int state = 0;
+
+ if (buffer == NULL)
+ {
+ state = 0;
+ return 0;
+ }
+
+ switch (q->port_mode & QC_MODE_MASK)
+ {
+ case QC_BIDIR: /* Bi-directional Port */
+ write_lpcontrol(q, 0x26);
+ lo = (qc_waithand2(q, 1) >> 1);
+ hi = (read_lpstatus(q) >> 3) & 0x1f;
+ write_lpcontrol(q, 0x2e);
+ lo2 = (qc_waithand2(q, 0) >> 1);
+ hi2 = (read_lpstatus(q) >> 3) & 0x1f;
+ switch (q->bpp)
+ {
+ case 4:
+ buffer[0] = lo & 0xf;
+ buffer[1] = ((lo & 0x70) >> 4) | ((hi & 1) << 3);
+ buffer[2] = (hi & 0x1e) >> 1;
+ buffer[3] = lo2 & 0xf;
+ buffer[4] = ((lo2 & 0x70) >> 4) | ((hi2 & 1) << 3);
+ buffer[5] = (hi2 & 0x1e) >> 1;
+ ret = 6;
+ break;
+ case 6:
+ buffer[0] = lo & 0x3f;
+ buffer[1] = ((lo & 0x40) >> 6) | (hi << 1);
+ buffer[2] = lo2 & 0x3f;
+ buffer[3] = ((lo2 & 0x40) >> 6) | (hi2 << 1);
+ ret = 4;
+ break;
+ }
+ break;
+
+ case QC_UNIDIR: /* Unidirectional Port */
+ write_lpcontrol(q, 6);
+ lo = (qc_waithand(q, 1) & 0xf0) >> 4;
+ write_lpcontrol(q, 0xe);
+ hi = (qc_waithand(q, 0) & 0xf0) >> 4;
+
+ switch (q->bpp)
+ {
+ case 4:
+ buffer[0] = lo;
+ buffer[1] = hi;
+ ret = 2;
+ break;
+ case 6:
+ switch (state)
+ {
+ case 0:
+ buffer[0] = (lo << 2) | ((hi & 0xc) >> 2);
+ q->saved_bits = (hi & 3) << 4;
+ state = 1;
+ ret = 1;
+ break;
+ case 1:
+ buffer[0] = lo | q->saved_bits;
+ q->saved_bits = hi << 2;
+ state = 2;
+ ret = 1;
+ break;
+ case 2:
+ buffer[0] = ((lo & 0xc) >> 2) | q->saved_bits;
+ buffer[1] = ((lo & 3) << 4) | hi;
+ state = 0;
+ ret = 2;
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ return ret;
+}
+
+/* requests a scan from the camera. It sends the correct instructions
+ * to the camera and then reads back the correct number of bytes. In
+ * previous versions of this routine the return structure contained
+ * the raw output from the camera, and there was a 'qc_convertscan'
+ * function that converted that to a useful format. In version 0.3 I
+ * rolled qc_convertscan into qc_scan and now I only return the
+ * converted scan. The format is just an one-dimensional array of
+ * characters, one for each pixel, with 0=black up to n=white, where
+ * n=2^(bit depth)-1. Ask me for more details if you don't understand
+ * this. */
+
+static long qc_capture(struct qcam_device * q, char __user *buf, unsigned long len)
+{
+ int i, j, k, yield;
+ int bytes;
+ int linestotrans, transperline;
+ int divisor;
+ int pixels_per_line;
+ int pixels_read = 0;
+ int got=0;
+ char buffer[6];
+ int shift=8-q->bpp;
+ char invert;
+
+ if (q->mode == -1)
+ return -ENXIO;
+
+ qc_command(q, 0x7);
+ qc_command(q, q->mode);
+
+ if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR)
+ {
+ write_lpcontrol(q, 0x2e); /* turn port around */
+ write_lpcontrol(q, 0x26);
+ (void) qc_waithand(q, 1);
+ write_lpcontrol(q, 0x2e);
+ (void) qc_waithand(q, 0);
+ }
+
+ /* strange -- should be 15:63 below, but 4bpp is odd */
+ invert = (q->bpp == 4) ? 16 : 63;
+
+ linestotrans = q->height / q->transfer_scale;
+ pixels_per_line = q->width / q->transfer_scale;
+ transperline = q->width * q->bpp;
+ divisor = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) *
+ q->transfer_scale;
+ transperline = (transperline + divisor - 1) / divisor;
+
+ for (i = 0, yield = yieldlines; i < linestotrans; i++)
+ {
+ for (pixels_read = j = 0; j < transperline; j++)
+ {
+ bytes = qc_readbytes(q, buffer);
+ for (k = 0; k < bytes && (pixels_read + k) < pixels_per_line; k++)
+ {
+ int o;
+ if (buffer[k] == 0 && invert == 16)
+ {
+ /* 4bpp is odd (again) -- inverter is 16, not 15, but output
+ must be 0-15 -- bls */
+ buffer[k] = 16;
+ }
+ o=i*pixels_per_line + pixels_read + k;
+ if(o<len)
+ {
+ got++;
+ put_user((invert - buffer[k])<<shift, buf+o);
+ }
+ }
+ pixels_read += bytes;
+ }
+ (void) qc_readbytes(q, NULL); /* reset state machine */
+
+ /* Grabbing an entire frame from the quickcam is a lengthy
+ process. We don't (usually) want to busy-block the
+ processor for the entire frame. yieldlines is a module
+ parameter. If we yield every line, the minimum frame
+ time will be 240 / 200 = 1.2 seconds. The compile-time
+ default is to yield every 4 lines. */
+ if (i >= yield) {
+ msleep_interruptible(5);
+ yield = i + yieldlines;
+ }
+ }
+
+ if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR)
+ {
+ write_lpcontrol(q, 2);
+ write_lpcontrol(q, 6);
+ udelay(3);
+ write_lpcontrol(q, 0xe);
+ }
+ if(got<len)
+ return got;
+ return len;
+}
+
+/*
+ * Video4linux interfacing
+ */
+
+static int qcam_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct qcam_device *qcam=(struct qcam_device *)dev;
+
+ switch(cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *b = arg;
+ strcpy(b->name, "Quickcam");
+ b->type = VID_TYPE_CAPTURE|VID_TYPE_SCALES|VID_TYPE_MONOCHROME;
+ b->channels = 1;
+ b->audios = 0;
+ b->maxwidth = 320;
+ b->maxheight = 240;
+ b->minwidth = 80;
+ b->minheight = 60;
+ return 0;
+ }
+ case VIDIOCGCHAN:
+ {
+ struct video_channel *v = arg;
+ if(v->channel!=0)
+ return -EINVAL;
+ v->flags=0;
+ v->tuners=0;
+ /* Good question.. its composite or SVHS so.. */
+ v->type = VIDEO_TYPE_CAMERA;
+ strcpy(v->name, "Camera");
+ return 0;
+ }
+ case VIDIOCSCHAN:
+ {
+ struct video_channel *v = arg;
+ if(v->channel!=0)
+ return -EINVAL;
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner *v = arg;
+ if(v->tuner)
+ return -EINVAL;
+ strcpy(v->name, "Format");
+ v->rangelow=0;
+ v->rangehigh=0;
+ v->flags= 0;
+ v->mode = VIDEO_MODE_AUTO;
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner *v = arg;
+ if(v->tuner)
+ return -EINVAL;
+ if(v->mode!=VIDEO_MODE_AUTO)
+ return -EINVAL;
+ return 0;
+ }
+ case VIDIOCGPICT:
+ {
+ struct video_picture *p = arg;
+ p->colour=0x8000;
+ p->hue=0x8000;
+ p->brightness=qcam->brightness<<8;
+ p->contrast=qcam->contrast<<8;
+ p->whiteness=qcam->whitebal<<8;
+ p->depth=qcam->bpp;
+ p->palette=VIDEO_PALETTE_GREY;
+ return 0;
+ }
+ case VIDIOCSPICT:
+ {
+ struct video_picture *p = arg;
+ if(p->palette!=VIDEO_PALETTE_GREY)
+ return -EINVAL;
+ if(p->depth!=4 && p->depth!=6)
+ return -EINVAL;
+
+ /*
+ * Now load the camera.
+ */
+
+ qcam->brightness = p->brightness>>8;
+ qcam->contrast = p->contrast>>8;
+ qcam->whitebal = p->whiteness>>8;
+ qcam->bpp = p->depth;
+
+ mutex_lock(&qcam->lock);
+ qc_setscanmode(qcam);
+ mutex_unlock(&qcam->lock);
+ qcam->status |= QC_PARAM_CHANGE;
+
+ return 0;
+ }
+ case VIDIOCSWIN:
+ {
+ struct video_window *vw = arg;
+ if(vw->flags)
+ return -EINVAL;
+ if(vw->clipcount)
+ return -EINVAL;
+ if(vw->height<60||vw->height>240)
+ return -EINVAL;
+ if(vw->width<80||vw->width>320)
+ return -EINVAL;
+
+ qcam->width = 320;
+ qcam->height = 240;
+ qcam->transfer_scale = 4;
+
+ if(vw->width>=160 && vw->height>=120)
+ {
+ qcam->transfer_scale = 2;
+ }
+ if(vw->width>=320 && vw->height>=240)
+ {
+ qcam->width = 320;
+ qcam->height = 240;
+ qcam->transfer_scale = 1;
+ }
+ mutex_lock(&qcam->lock);
+ qc_setscanmode(qcam);
+ mutex_unlock(&qcam->lock);
+
+ /* We must update the camera before we grab. We could
+ just have changed the grab size */
+ qcam->status |= QC_PARAM_CHANGE;
+
+ /* Ok we figured out what to use from our wide choice */
+ return 0;
+ }
+ case VIDIOCGWIN:
+ {
+ struct video_window *vw = arg;
+ memset(vw, 0, sizeof(*vw));
+ vw->width=qcam->width/qcam->transfer_scale;
+ vw->height=qcam->height/qcam->transfer_scale;
+ return 0;
+ }
+ case VIDIOCKEY:
+ return 0;
+ case VIDIOCCAPTURE:
+ case VIDIOCGFBUF:
+ case VIDIOCSFBUF:
+ case VIDIOCGFREQ:
+ case VIDIOCSFREQ:
+ case VIDIOCGAUDIO:
+ case VIDIOCSAUDIO:
+ return -EINVAL;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static int qcam_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, qcam_do_ioctl);
+}
+
+static ssize_t qcam_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct video_device *v = video_devdata(file);
+ struct qcam_device *qcam=(struct qcam_device *)v;
+ int len;
+ parport_claim_or_block(qcam->pdev);
+
+ mutex_lock(&qcam->lock);
+
+ qc_reset(qcam);
+
+ /* Update the camera parameters if we need to */
+ if (qcam->status & QC_PARAM_CHANGE)
+ qc_set(qcam);
+
+ len=qc_capture(qcam, buf,count);
+
+ mutex_unlock(&qcam->lock);
+
+ parport_release(qcam->pdev);
+ return len;
+}
+
+static struct file_operations qcam_fops = {
+ .owner = THIS_MODULE,
+ .open = video_exclusive_open,
+ .release = video_exclusive_release,
+ .ioctl = qcam_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .read = qcam_read,
+ .llseek = no_llseek,
+};
+static struct video_device qcam_template=
+{
+ .owner = THIS_MODULE,
+ .name = "Connectix Quickcam",
+ .type = VID_TYPE_CAPTURE,
+ .hardware = VID_HARDWARE_QCAM_BW,
+ .fops = &qcam_fops,
+};
+
+#define MAX_CAMS 4
+static struct qcam_device *qcams[MAX_CAMS];
+static unsigned int num_cams = 0;
+
+static int init_bwqcam(struct parport *port)
+{
+ struct qcam_device *qcam;
+
+ if (num_cams == MAX_CAMS)
+ {
+ printk(KERN_ERR "Too many Quickcams (max %d)\n", MAX_CAMS);
+ return -ENOSPC;
+ }
+
+ qcam=qcam_init(port);
+ if(qcam==NULL)
+ return -ENODEV;
+
+ parport_claim_or_block(qcam->pdev);
+
+ qc_reset(qcam);
+
+ if(qc_detect(qcam)==0)
+ {
+ parport_release(qcam->pdev);
+ parport_unregister_device(qcam->pdev);
+ kfree(qcam);
+ return -ENODEV;
+ }
+ qc_calibrate(qcam);
+
+ parport_release(qcam->pdev);
+
+ printk(KERN_INFO "Connectix Quickcam on %s\n", qcam->pport->name);
+
+ if(video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr)==-1)
+ {
+ parport_unregister_device(qcam->pdev);
+ kfree(qcam);
+ return -ENODEV;
+ }
+
+ qcams[num_cams++] = qcam;
+
+ return 0;
+}
+
+static void close_bwqcam(struct qcam_device *qcam)
+{
+ video_unregister_device(&qcam->vdev);
+ parport_unregister_device(qcam->pdev);
+ kfree(qcam);
+}
+
+/* The parport parameter controls which parports will be scanned.
+ * Scanning all parports causes some printers to print a garbage page.
+ * -- March 14, 1999 Billy Donahue <billy@escape.com> */
+#ifdef MODULE
+static char *parport[MAX_CAMS] = { NULL, };
+module_param_array(parport, charp, NULL, 0);
+#endif
+
+static int accept_bwqcam(struct parport *port)
+{
+#ifdef MODULE
+ int n;
+
+ if (parport[0] && strncmp(parport[0], "auto", 4) != 0) {
+ /* user gave parport parameters */
+ for(n=0; parport[n] && n<MAX_CAMS; n++){
+ char *ep;
+ unsigned long r;
+ r = simple_strtoul(parport[n], &ep, 0);
+ if (ep == parport[n]) {
+ printk(KERN_ERR
+ "bw-qcam: bad port specifier \"%s\"\n",
+ parport[n]);
+ continue;
+ }
+ if (r == port->number)
+ return 1;
+ }
+ return 0;
+ }
+#endif
+ return 1;
+}
+
+static void bwqcam_attach(struct parport *port)
+{
+ if (accept_bwqcam(port))
+ init_bwqcam(port);
+}
+
+static void bwqcam_detach(struct parport *port)
+{
+ int i;
+ for (i = 0; i < num_cams; i++) {
+ struct qcam_device *qcam = qcams[i];
+ if (qcam && qcam->pdev->port == port) {
+ qcams[i] = NULL;
+ close_bwqcam(qcam);
+ }
+ }
+}
+
+static struct parport_driver bwqcam_driver = {
+ .name = "bw-qcam",
+ .attach = bwqcam_attach,
+ .detach = bwqcam_detach,
+};
+
+static void __exit exit_bw_qcams(void)
+{
+ parport_unregister_driver(&bwqcam_driver);
+}
+
+static int __init init_bw_qcams(void)
+{
+#ifdef MODULE
+ /* Do some sanity checks on the module parameters. */
+ if (maxpoll > 5000) {
+ printk("Connectix Quickcam max-poll was above 5000. Using 5000.\n");
+ maxpoll = 5000;
+ }
+
+ if (yieldlines < 1) {
+ printk("Connectix Quickcam yieldlines was less than 1. Using 1.\n");
+ yieldlines = 1;
+ }
+#endif
+ return parport_register_driver(&bwqcam_driver);
+}
+
+module_init(init_bw_qcams);
+module_exit(exit_bw_qcams);
+
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/video/bw-qcam.h b/linux/drivers/media/video/bw-qcam.h
new file mode 100644
index 000000000..6701dafbc
--- /dev/null
+++ b/linux/drivers/media/video/bw-qcam.h
@@ -0,0 +1,68 @@
+/*
+ * Video4Linux bw-qcam driver
+ *
+ * Derived from code..
+ */
+
+/******************************************************************
+
+Copyright (C) 1996 by Scott Laird
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+/* One from column A... */
+#define QC_NOTSET 0
+#define QC_UNIDIR 1
+#define QC_BIDIR 2
+#define QC_SERIAL 3
+
+/* ... and one from column B */
+#define QC_ANY 0x00
+#define QC_FORCE_UNIDIR 0x10
+#define QC_FORCE_BIDIR 0x20
+#define QC_FORCE_SERIAL 0x30
+/* in the port_mode member */
+
+#define QC_MODE_MASK 0x07
+#define QC_FORCE_MASK 0x70
+
+#define MAX_HEIGHT 243
+#define MAX_WIDTH 336
+
+/* Bit fields for status flags */
+#define QC_PARAM_CHANGE 0x01 /* Camera status change has occurred */
+
+struct qcam_device {
+ struct video_device vdev;
+ struct pardevice *pdev;
+ struct parport *pport;
+ struct mutex lock;
+ int width, height;
+ int bpp;
+ int mode;
+ int contrast, brightness, whitebal;
+ int port_mode;
+ int transfer_scale;
+ int top, left;
+ int status;
+ unsigned int saved_bits;
+};
diff --git a/linux/drivers/media/video/c-qcam.c b/linux/drivers/media/video/c-qcam.c
new file mode 100644
index 000000000..c1957a143
--- /dev/null
+++ b/linux/drivers/media/video/c-qcam.c
@@ -0,0 +1,858 @@
+/*
+ * Video4Linux Colour QuickCam driver
+ * Copyright 1997-2000 Philip Blundell <philb@gnu.org>
+ *
+ * Module parameters:
+ *
+ * parport=auto -- probe all parports (default)
+ * parport=0 -- parport0 becomes qcam1
+ * parport=2,0,1 -- parports 2,0,1 are tried in that order
+ *
+ * probe=0 -- do no probing, assume camera is present
+ * probe=1 -- use IEEE-1284 autoprobe data only (default)
+ * probe=2 -- probe aggressively for cameras
+ *
+ * force_rgb=1 -- force data format to RGB (default is BGR)
+ *
+ * The parport parameter controls which parports will be scanned.
+ * Scanning all parports causes some printers to print a garbage page.
+ * -- March 14, 1999 Billy Donahue <billy@escape.com>
+ *
+ * Fixed data format to BGR, added force_rgb parameter. Added missing
+ * parport_unregister_driver() on module removal.
+ * -- May 28, 2000 Claudio Matsuoka <claudio@conectiva.com>
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/parport.h>
+#include <linux/sched.h>
+#include "compat.h"
+#include <linux/videodev.h>
+#include <linux/mutex.h>
+
+#include <asm/uaccess.h>
+
+struct qcam_device {
+ struct video_device vdev;
+ struct pardevice *pdev;
+ struct parport *pport;
+ int width, height;
+ int ccd_width, ccd_height;
+ int mode;
+ int contrast, brightness, whitebal;
+ int top, left;
+ unsigned int bidirectional;
+ struct mutex lock;
+};
+
+/* cameras maximum */
+#define MAX_CAMS 4
+
+/* The three possible QuickCam modes */
+#define QC_MILLIONS 0x18
+#define QC_BILLIONS 0x10
+#define QC_THOUSANDS 0x08 /* with VIDEC compression (not supported) */
+
+/* The three possible decimations */
+#define QC_DECIMATION_1 0
+#define QC_DECIMATION_2 2
+#define QC_DECIMATION_4 4
+
+#define BANNER "Colour QuickCam for Video4Linux v0.05"
+
+static int parport[MAX_CAMS] = { [1 ... MAX_CAMS-1] = -1 };
+static int probe = 2;
+static int force_rgb = 0;
+static int video_nr = -1;
+
+static inline void qcam_set_ack(struct qcam_device *qcam, unsigned int i)
+{
+ /* note: the QC specs refer to the PCAck pin by voltage, not
+ software level. PC ports have builtin inverters. */
+ parport_frob_control(qcam->pport, 8, i?8:0);
+}
+
+static inline unsigned int qcam_ready1(struct qcam_device *qcam)
+{
+ return (parport_read_status(qcam->pport) & 0x8)?1:0;
+}
+
+static inline unsigned int qcam_ready2(struct qcam_device *qcam)
+{
+ return (parport_read_data(qcam->pport) & 0x1)?1:0;
+}
+
+static unsigned int qcam_await_ready1(struct qcam_device *qcam,
+ int value)
+{
+ unsigned long oldjiffies = jiffies;
+ unsigned int i;
+
+ for (oldjiffies = jiffies; (jiffies - oldjiffies) < (HZ/25); )
+ if (qcam_ready1(qcam) == value)
+ return 0;
+
+ /* If the camera didn't respond within 1/25 second, poll slowly
+ for a while. */
+ for (i = 0; i < 50; i++)
+ {
+ if (qcam_ready1(qcam) == value)
+ return 0;
+ msleep_interruptible(100);
+ }
+
+ /* Probably somebody pulled the plug out. Not much we can do. */
+ printk(KERN_ERR "c-qcam: ready1 timeout (%d) %x %x\n", value,
+ parport_read_status(qcam->pport),
+ parport_read_control(qcam->pport));
+ return 1;
+}
+
+static unsigned int qcam_await_ready2(struct qcam_device *qcam, int value)
+{
+ unsigned long oldjiffies = jiffies;
+ unsigned int i;
+
+ for (oldjiffies = jiffies; (jiffies - oldjiffies) < (HZ/25); )
+ if (qcam_ready2(qcam) == value)
+ return 0;
+
+ /* If the camera didn't respond within 1/25 second, poll slowly
+ for a while. */
+ for (i = 0; i < 50; i++)
+ {
+ if (qcam_ready2(qcam) == value)
+ return 0;
+ msleep_interruptible(100);
+ }
+
+ /* Probably somebody pulled the plug out. Not much we can do. */
+ printk(KERN_ERR "c-qcam: ready2 timeout (%d) %x %x %x\n", value,
+ parport_read_status(qcam->pport),
+ parport_read_control(qcam->pport),
+ parport_read_data(qcam->pport));
+ return 1;
+}
+
+static int qcam_read_data(struct qcam_device *qcam)
+{
+ unsigned int idata;
+ qcam_set_ack(qcam, 0);
+ if (qcam_await_ready1(qcam, 1)) return -1;
+ idata = parport_read_status(qcam->pport) & 0xf0;
+ qcam_set_ack(qcam, 1);
+ if (qcam_await_ready1(qcam, 0)) return -1;
+ idata |= (parport_read_status(qcam->pport) >> 4);
+ return idata;
+}
+
+static int qcam_write_data(struct qcam_device *qcam, unsigned int data)
+{
+ unsigned int idata;
+ parport_write_data(qcam->pport, data);
+ idata = qcam_read_data(qcam);
+ if (data != idata)
+ {
+ printk(KERN_WARNING "cqcam: sent %x but received %x\n", data,
+ idata);
+ return 1;
+ }
+ return 0;
+}
+
+static inline int qcam_set(struct qcam_device *qcam, unsigned int cmd, unsigned int data)
+{
+ if (qcam_write_data(qcam, cmd))
+ return -1;
+ if (qcam_write_data(qcam, data))
+ return -1;
+ return 0;
+}
+
+static inline int qcam_get(struct qcam_device *qcam, unsigned int cmd)
+{
+ if (qcam_write_data(qcam, cmd))
+ return -1;
+ return qcam_read_data(qcam);
+}
+
+static int qc_detect(struct qcam_device *qcam)
+{
+ unsigned int stat, ostat, i, count = 0;
+
+ /* The probe routine below is not very reliable. The IEEE-1284
+ probe takes precedence. */
+ /* XXX Currently parport provides no way to distinguish between
+ "the IEEE probe was not done" and "the probe was done, but
+ no device was found". Fix this one day. */
+ if (qcam->pport->probe_info[0].class == PARPORT_CLASS_MEDIA
+ && qcam->pport->probe_info[0].model
+ && !strcmp(qcam->pdev->port->probe_info[0].model,
+ "Color QuickCam 2.0")) {
+ printk(KERN_DEBUG "QuickCam: Found by IEEE1284 probe.\n");
+ return 1;
+ }
+
+ if (probe < 2)
+ return 0;
+
+ parport_write_control(qcam->pport, 0xc);
+
+ /* look for a heartbeat */
+ ostat = stat = parport_read_status(qcam->pport);
+ for (i=0; i<250; i++)
+ {
+ mdelay(1);
+ stat = parport_read_status(qcam->pport);
+ if (ostat != stat)
+ {
+ if (++count >= 3) return 1;
+ ostat = stat;
+ }
+ }
+
+ /* Reset the camera and try again */
+ parport_write_control(qcam->pport, 0xc);
+ parport_write_control(qcam->pport, 0x8);
+ mdelay(1);
+ parport_write_control(qcam->pport, 0xc);
+ mdelay(1);
+ count = 0;
+
+ ostat = stat = parport_read_status(qcam->pport);
+ for (i=0; i<250; i++)
+ {
+ mdelay(1);
+ stat = parport_read_status(qcam->pport);
+ if (ostat != stat)
+ {
+ if (++count >= 3) return 1;
+ ostat = stat;
+ }
+ }
+
+ /* no (or flatline) camera, give up */
+ return 0;
+}
+
+static void qc_reset(struct qcam_device *qcam)
+{
+ parport_write_control(qcam->pport, 0xc);
+ parport_write_control(qcam->pport, 0x8);
+ mdelay(1);
+ parport_write_control(qcam->pport, 0xc);
+ mdelay(1);
+}
+
+/* Reset the QuickCam and program for brightness, contrast,
+ * white-balance, and resolution. */
+
+static void qc_setup(struct qcam_device *q)
+{
+ qc_reset(q);
+
+ /* Set the brightness. */
+ qcam_set(q, 11, q->brightness);
+
+ /* Set the height and width. These refer to the actual
+ CCD area *before* applying the selected decimation. */
+ qcam_set(q, 17, q->ccd_height);
+ qcam_set(q, 19, q->ccd_width / 2);
+
+ /* Set top and left. */
+ qcam_set(q, 0xd, q->top);
+ qcam_set(q, 0xf, q->left);
+
+ /* Set contrast and white balance. */
+ qcam_set(q, 0x19, q->contrast);
+ qcam_set(q, 0x1f, q->whitebal);
+
+ /* Set the speed. */
+ qcam_set(q, 45, 2);
+}
+
+/* Read some bytes from the camera and put them in the buffer.
+ nbytes should be a multiple of 3, because bidirectional mode gives
+ us three bytes at a time. */
+
+static unsigned int qcam_read_bytes(struct qcam_device *q, unsigned char *buf, unsigned int nbytes)
+{
+ unsigned int bytes = 0;
+
+ qcam_set_ack(q, 0);
+ if (q->bidirectional)
+ {
+ /* It's a bidirectional port */
+ while (bytes < nbytes)
+ {
+ unsigned int lo1, hi1, lo2, hi2;
+ unsigned char r, g, b;
+
+ if (qcam_await_ready2(q, 1)) return bytes;
+ lo1 = parport_read_data(q->pport) >> 1;
+ hi1 = ((parport_read_status(q->pport) >> 3) & 0x1f) ^ 0x10;
+ qcam_set_ack(q, 1);
+ if (qcam_await_ready2(q, 0)) return bytes;
+ lo2 = parport_read_data(q->pport) >> 1;
+ hi2 = ((parport_read_status(q->pport) >> 3) & 0x1f) ^ 0x10;
+ qcam_set_ack(q, 0);
+ r = (lo1 | ((hi1 & 1)<<7));
+ g = ((hi1 & 0x1e)<<3) | ((hi2 & 0x1e)>>1);
+ b = (lo2 | ((hi2 & 1)<<7));
+ if (force_rgb) {
+ buf[bytes++] = r;
+ buf[bytes++] = g;
+ buf[bytes++] = b;
+ } else {
+ buf[bytes++] = b;
+ buf[bytes++] = g;
+ buf[bytes++] = r;
+ }
+ }
+ }
+ else
+ {
+ /* It's a unidirectional port */
+ int i = 0, n = bytes;
+ unsigned char rgb[3];
+
+ while (bytes < nbytes)
+ {
+ unsigned int hi, lo;
+
+ if (qcam_await_ready1(q, 1)) return bytes;
+ hi = (parport_read_status(q->pport) & 0xf0);
+ qcam_set_ack(q, 1);
+ if (qcam_await_ready1(q, 0)) return bytes;
+ lo = (parport_read_status(q->pport) & 0xf0);
+ qcam_set_ack(q, 0);
+ /* flip some bits */
+ rgb[(i = bytes++ % 3)] = (hi | (lo >> 4)) ^ 0x88;
+ if (i >= 2) {
+get_fragment:
+ if (force_rgb) {
+ buf[n++] = rgb[0];
+ buf[n++] = rgb[1];
+ buf[n++] = rgb[2];
+ } else {
+ buf[n++] = rgb[2];
+ buf[n++] = rgb[1];
+ buf[n++] = rgb[0];
+ }
+ }
+ }
+ if (i) {
+ i = 0;
+ goto get_fragment;
+ }
+ }
+ return bytes;
+}
+
+#define BUFSZ 150
+
+static long qc_capture(struct qcam_device *q, char __user *buf, unsigned long len)
+{
+ unsigned lines, pixelsperline, bitsperxfer;
+ unsigned int is_bi_dir = q->bidirectional;
+ size_t wantlen, outptr = 0;
+ char tmpbuf[BUFSZ];
+
+ if (!access_ok(VERIFY_WRITE, buf, len))
+ return -EFAULT;
+
+ /* Wait for camera to become ready */
+ for (;;)
+ {
+ int i = qcam_get(q, 41);
+ if (i == -1) {
+ qc_setup(q);
+ return -EIO;
+ }
+ if ((i & 0x80) == 0)
+ break;
+ else
+ schedule();
+ }
+
+ if (qcam_set(q, 7, (q->mode | (is_bi_dir?1:0)) + 1))
+ return -EIO;
+
+ lines = q->height;
+ pixelsperline = q->width;
+ bitsperxfer = (is_bi_dir) ? 24 : 8;
+
+ if (is_bi_dir)
+ {
+ /* Turn the port around */
+ parport_data_reverse(q->pport);
+ mdelay(3);
+ qcam_set_ack(q, 0);
+ if (qcam_await_ready1(q, 1)) {
+ qc_setup(q);
+ return -EIO;
+ }
+ qcam_set_ack(q, 1);
+ if (qcam_await_ready1(q, 0)) {
+ qc_setup(q);
+ return -EIO;
+ }
+ }
+
+ wantlen = lines * pixelsperline * 24 / 8;
+
+ while (wantlen)
+ {
+ size_t t, s;
+ s = (wantlen > BUFSZ)?BUFSZ:wantlen;
+ t = qcam_read_bytes(q, tmpbuf, s);
+ if (outptr < len)
+ {
+ size_t sz = len - outptr;
+ if (sz > t) sz = t;
+ if (__copy_to_user(buf+outptr, tmpbuf, sz))
+ break;
+ outptr += sz;
+ }
+ wantlen -= t;
+ if (t < s)
+ break;
+ cond_resched();
+ }
+
+ len = outptr;
+
+ if (wantlen)
+ {
+ printk("qcam: short read.\n");
+ if (is_bi_dir)
+ parport_data_forward(q->pport);
+ qc_setup(q);
+ return len;
+ }
+
+ if (is_bi_dir)
+ {
+ int l;
+ do {
+ l = qcam_read_bytes(q, tmpbuf, 3);
+ cond_resched();
+ } while (l && (tmpbuf[0] == 0x7e || tmpbuf[1] == 0x7e || tmpbuf[2] == 0x7e));
+ if (force_rgb) {
+ if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
+ printk("qcam: bad EOF\n");
+ } else {
+ if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe)
+ printk("qcam: bad EOF\n");
+ }
+ qcam_set_ack(q, 0);
+ if (qcam_await_ready1(q, 1))
+ {
+ printk("qcam: no ack after EOF\n");
+ parport_data_forward(q->pport);
+ qc_setup(q);
+ return len;
+ }
+ parport_data_forward(q->pport);
+ mdelay(3);
+ qcam_set_ack(q, 1);
+ if (qcam_await_ready1(q, 0))
+ {
+ printk("qcam: no ack to port turnaround\n");
+ qc_setup(q);
+ return len;
+ }
+ }
+ else
+ {
+ int l;
+ do {
+ l = qcam_read_bytes(q, tmpbuf, 1);
+ cond_resched();
+ } while (l && tmpbuf[0] == 0x7e);
+ l = qcam_read_bytes(q, tmpbuf+1, 2);
+ if (force_rgb) {
+ if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
+ printk("qcam: bad EOF\n");
+ } else {
+ if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe)
+ printk("qcam: bad EOF\n");
+ }
+ }
+
+ qcam_write_data(q, 0);
+ return len;
+}
+
+/*
+ * Video4linux interfacing
+ */
+
+static int qcam_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct qcam_device *qcam=(struct qcam_device *)dev;
+
+ switch(cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *b = arg;
+ strcpy(b->name, "Quickcam");
+ b->type = VID_TYPE_CAPTURE|VID_TYPE_SCALES;
+ b->channels = 1;
+ b->audios = 0;
+ b->maxwidth = 320;
+ b->maxheight = 240;
+ b->minwidth = 80;
+ b->minheight = 60;
+ return 0;
+ }
+ case VIDIOCGCHAN:
+ {
+ struct video_channel *v = arg;
+ if(v->channel!=0)
+ return -EINVAL;
+ v->flags=0;
+ v->tuners=0;
+ /* Good question.. its composite or SVHS so.. */
+ v->type = VIDEO_TYPE_CAMERA;
+ strcpy(v->name, "Camera");
+ return 0;
+ }
+ case VIDIOCSCHAN:
+ {
+ struct video_channel *v = arg;
+ if(v->channel!=0)
+ return -EINVAL;
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner *v = arg;
+ if(v->tuner)
+ return -EINVAL;
+ memset(v,0,sizeof(*v));
+ strcpy(v->name, "Format");
+ v->mode = VIDEO_MODE_AUTO;
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner *v = arg;
+ if(v->tuner)
+ return -EINVAL;
+ if(v->mode!=VIDEO_MODE_AUTO)
+ return -EINVAL;
+ return 0;
+ }
+ case VIDIOCGPICT:
+ {
+ struct video_picture *p = arg;
+ p->colour=0x8000;
+ p->hue=0x8000;
+ p->brightness=qcam->brightness<<8;
+ p->contrast=qcam->contrast<<8;
+ p->whiteness=qcam->whitebal<<8;
+ p->depth=24;
+ p->palette=VIDEO_PALETTE_RGB24;
+ return 0;
+ }
+ case VIDIOCSPICT:
+ {
+ struct video_picture *p = arg;
+
+ /*
+ * Sanity check args
+ */
+ if (p->depth != 24 || p->palette != VIDEO_PALETTE_RGB24)
+ return -EINVAL;
+
+ /*
+ * Now load the camera.
+ */
+ qcam->brightness = p->brightness>>8;
+ qcam->contrast = p->contrast>>8;
+ qcam->whitebal = p->whiteness>>8;
+
+ mutex_lock(&qcam->lock);
+ parport_claim_or_block(qcam->pdev);
+ qc_setup(qcam);
+ parport_release(qcam->pdev);
+ mutex_unlock(&qcam->lock);
+ return 0;
+ }
+ case VIDIOCSWIN:
+ {
+ struct video_window *vw = arg;
+
+ if(vw->flags)
+ return -EINVAL;
+ if(vw->clipcount)
+ return -EINVAL;
+ if(vw->height<60||vw->height>240)
+ return -EINVAL;
+ if(vw->width<80||vw->width>320)
+ return -EINVAL;
+
+ qcam->width = 80;
+ qcam->height = 60;
+ qcam->mode = QC_DECIMATION_4;
+
+ if(vw->width>=160 && vw->height>=120)
+ {
+ qcam->width = 160;
+ qcam->height = 120;
+ qcam->mode = QC_DECIMATION_2;
+ }
+ if(vw->width>=320 && vw->height>=240)
+ {
+ qcam->width = 320;
+ qcam->height = 240;
+ qcam->mode = QC_DECIMATION_1;
+ }
+ qcam->mode |= QC_MILLIONS;
+#if 0
+ if(vw->width>=640 && vw->height>=480)
+ {
+ qcam->width = 640;
+ qcam->height = 480;
+ qcam->mode = QC_BILLIONS | QC_DECIMATION_1;
+ }
+#endif
+ /* Ok we figured out what to use from our
+ wide choice */
+ mutex_lock(&qcam->lock);
+ parport_claim_or_block(qcam->pdev);
+ qc_setup(qcam);
+ parport_release(qcam->pdev);
+ mutex_unlock(&qcam->lock);
+ return 0;
+ }
+ case VIDIOCGWIN:
+ {
+ struct video_window *vw = arg;
+ memset(vw, 0, sizeof(*vw));
+ vw->width=qcam->width;
+ vw->height=qcam->height;
+ return 0;
+ }
+ case VIDIOCKEY:
+ return 0;
+ case VIDIOCCAPTURE:
+ case VIDIOCGFBUF:
+ case VIDIOCSFBUF:
+ case VIDIOCGFREQ:
+ case VIDIOCSFREQ:
+ case VIDIOCGAUDIO:
+ case VIDIOCSAUDIO:
+ return -EINVAL;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static int qcam_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, qcam_do_ioctl);
+}
+
+static ssize_t qcam_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct video_device *v = video_devdata(file);
+ struct qcam_device *qcam=(struct qcam_device *)v;
+ int len;
+
+ mutex_lock(&qcam->lock);
+ parport_claim_or_block(qcam->pdev);
+ /* Probably should have a semaphore against multiple users */
+ len = qc_capture(qcam, buf,count);
+ parport_release(qcam->pdev);
+ mutex_unlock(&qcam->lock);
+ return len;
+}
+
+/* video device template */
+static struct file_operations qcam_fops = {
+ .owner = THIS_MODULE,
+ .open = video_exclusive_open,
+ .release = video_exclusive_release,
+ .ioctl = qcam_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .read = qcam_read,
+ .llseek = no_llseek,
+};
+
+static struct video_device qcam_template=
+{
+ .owner = THIS_MODULE,
+ .name = "Colour QuickCam",
+ .type = VID_TYPE_CAPTURE,
+ .hardware = VID_HARDWARE_QCAM_C,
+ .fops = &qcam_fops,
+};
+
+/* Initialize the QuickCam driver control structure. */
+
+static struct qcam_device *qcam_init(struct parport *port)
+{
+ struct qcam_device *q;
+
+ q = kmalloc(sizeof(struct qcam_device), GFP_KERNEL);
+ if(q==NULL)
+ return NULL;
+
+ q->pport = port;
+ q->pdev = parport_register_device(port, "c-qcam", NULL, NULL,
+ NULL, 0, NULL);
+
+ q->bidirectional = (q->pport->modes & PARPORT_MODE_TRISTATE)?1:0;
+
+ if (q->pdev == NULL)
+ {
+ printk(KERN_ERR "c-qcam: couldn't register for %s.\n",
+ port->name);
+ kfree(q);
+ return NULL;
+ }
+
+ memcpy(&q->vdev, &qcam_template, sizeof(qcam_template));
+
+ mutex_init(&q->lock);
+ q->width = q->ccd_width = 320;
+ q->height = q->ccd_height = 240;
+ q->mode = QC_MILLIONS | QC_DECIMATION_1;
+ q->contrast = 192;
+ q->brightness = 240;
+ q->whitebal = 128;
+ q->top = 1;
+ q->left = 14;
+ return q;
+}
+
+static struct qcam_device *qcams[MAX_CAMS];
+static unsigned int num_cams = 0;
+
+static int init_cqcam(struct parport *port)
+{
+ struct qcam_device *qcam;
+
+ if (parport[0] != -1)
+ {
+ /* The user gave specific instructions */
+ int i, found = 0;
+ for (i = 0; i < MAX_CAMS && parport[i] != -1; i++)
+ {
+ if (parport[0] == port->number)
+ found = 1;
+ }
+ if (!found)
+ return -ENODEV;
+ }
+
+ if (num_cams == MAX_CAMS)
+ return -ENOSPC;
+
+ qcam = qcam_init(port);
+ if (qcam==NULL)
+ return -ENODEV;
+
+ parport_claim_or_block(qcam->pdev);
+
+ qc_reset(qcam);
+
+ if (probe && qc_detect(qcam)==0)
+ {
+ parport_release(qcam->pdev);
+ parport_unregister_device(qcam->pdev);
+ kfree(qcam);
+ return -ENODEV;
+ }
+
+ qc_setup(qcam);
+
+ parport_release(qcam->pdev);
+
+ if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr)==-1)
+ {
+ printk(KERN_ERR "Unable to register Colour QuickCam on %s\n",
+ qcam->pport->name);
+ parport_unregister_device(qcam->pdev);
+ kfree(qcam);
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "video%d: Colour QuickCam found on %s\n",
+ qcam->vdev.minor, qcam->pport->name);
+
+ qcams[num_cams++] = qcam;
+
+ return 0;
+}
+
+static void close_cqcam(struct qcam_device *qcam)
+{
+ video_unregister_device(&qcam->vdev);
+ parport_unregister_device(qcam->pdev);
+ kfree(qcam);
+}
+
+static void cq_attach(struct parport *port)
+{
+ init_cqcam(port);
+}
+
+static void cq_detach(struct parport *port)
+{
+ /* Write this some day. */
+}
+
+static struct parport_driver cqcam_driver = {
+ .name = "cqcam",
+ .attach = cq_attach,
+ .detach = cq_detach,
+};
+
+static int __init cqcam_init (void)
+{
+ printk(BANNER "\n");
+
+ return parport_register_driver(&cqcam_driver);
+}
+
+static void __exit cqcam_cleanup (void)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_cams; i++)
+ close_cqcam(qcams[i]);
+
+ parport_unregister_driver(&cqcam_driver);
+}
+
+MODULE_AUTHOR("Philip Blundell <philb@gnu.org>");
+MODULE_DESCRIPTION(BANNER);
+MODULE_LICENSE("GPL");
+
+/* FIXME: parport=auto would never have worked, surely? --RR */
+MODULE_PARM_DESC(parport ,"parport=<auto|n[,n]...> for port detection method\n\
+probe=<0|1|2> for camera detection method\n\
+force_rgb=<0|1> for RGB data format (default BGR)");
+module_param_array(parport, int, NULL, 0);
+module_param(probe, int, 0);
+module_param(force_rgb, bool, 0);
+module_param(video_nr, int, 0);
+
+module_init(cqcam_init);
+module_exit(cqcam_cleanup);
diff --git a/linux/drivers/media/video/cpia.c b/linux/drivers/media/video/cpia.c
new file mode 100644
index 000000000..85d84e89d
--- /dev/null
+++ b/linux/drivers/media/video/cpia.c
@@ -0,0 +1,4073 @@
+/*
+ * cpia CPiA driver
+ *
+ * Supports CPiA based Video Camera's.
+ *
+ * (C) Copyright 1999-2000 Peter Pregler
+ * (C) Copyright 1999-2000 Scott J. Bertin
+ * (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com>
+ * (C) Copyright 2000 STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */
+/* #define _CPIA_DEBUG_ 1 */
+
+#include <linux/config.h>
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/ctype.h>
+#include <linux/pagemap.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <linux/mutex.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#include "cpia.h"
+
+#ifdef CONFIG_VIDEO_CPIA_PP
+extern int cpia_pp_init(void);
+#endif
+#ifdef CONFIG_VIDEO_CPIA_USB
+extern int cpia_usb_init(void);
+#endif
+
+static int video_nr = -1;
+
+#ifdef MODULE
+module_param(video_nr, int, 0);
+MODULE_AUTHOR("Scott J. Bertin <sbertin@securenym.net> & Peter Pregler <Peter_Pregler@email.com> & Johannes Erdfelt <johannes@erdfelt.com>");
+MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("video");
+#endif
+
+static unsigned short colorspace_conv;
+module_param(colorspace_conv, ushort, 0444);
+MODULE_PARM_DESC(colorspace_conv,
+ " Colorspace conversion:"
+ "\n 0 = disable, 1 = enable"
+ "\n Default value is 0"
+ );
+
+#define ABOUT "V4L-Driver for Vision CPiA based cameras"
+
+#ifndef VID_HARDWARE_CPIA
+#define VID_HARDWARE_CPIA 24 /* FIXME -> from linux/videodev.h */
+#endif
+
+#define CPIA_MODULE_CPIA (0<<5)
+#define CPIA_MODULE_SYSTEM (1<<5)
+#define CPIA_MODULE_VP_CTRL (5<<5)
+#define CPIA_MODULE_CAPTURE (6<<5)
+#define CPIA_MODULE_DEBUG (7<<5)
+
+#define INPUT (DATA_IN << 8)
+#define OUTPUT (DATA_OUT << 8)
+
+#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1)
+#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2)
+#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3)
+#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4)
+#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5)
+#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7)
+#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8)
+#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10)
+
+#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1)
+#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2)
+#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3)
+#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4)
+#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5)
+#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6)
+#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7)
+#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8)
+#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9)
+#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10)
+#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11)
+#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12)
+#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13)
+
+#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1)
+#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2)
+#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3)
+#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4)
+#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6)
+#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7)
+#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8)
+#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9)
+#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10)
+#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11)
+#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16)
+#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17)
+#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18)
+#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19)
+#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25)
+#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30)
+#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31)
+
+#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1)
+#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2)
+#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3)
+#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4)
+#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5)
+#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6)
+#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7)
+#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8)
+#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9)
+#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10)
+#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
+#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12)
+#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
+#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14)
+#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15)
+
+#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1)
+#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4)
+#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5)
+#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6)
+#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8)
+#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9)
+#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10)
+#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11)
+
+enum {
+ FRAME_READY, /* Ready to grab into */
+ FRAME_GRABBING, /* In the process of being grabbed into */
+ FRAME_DONE, /* Finished grabbing, but not been synced yet */
+ FRAME_UNUSED, /* Unused (no MCAPTURE) */
+};
+
+#define COMMAND_NONE 0x0000
+#define COMMAND_SETCOMPRESSION 0x0001
+#define COMMAND_SETCOMPRESSIONTARGET 0x0002
+#define COMMAND_SETCOLOURPARAMS 0x0004
+#define COMMAND_SETFORMAT 0x0008
+#define COMMAND_PAUSE 0x0010
+#define COMMAND_RESUME 0x0020
+#define COMMAND_SETYUVTHRESH 0x0040
+#define COMMAND_SETECPTIMING 0x0080
+#define COMMAND_SETCOMPRESSIONPARAMS 0x0100
+#define COMMAND_SETEXPOSURE 0x0200
+#define COMMAND_SETCOLOURBALANCE 0x0400
+#define COMMAND_SETSENSORFPS 0x0800
+#define COMMAND_SETAPCOR 0x1000
+#define COMMAND_SETFLICKERCTRL 0x2000
+#define COMMAND_SETVLOFFSET 0x4000
+#define COMMAND_SETLIGHTS 0x8000
+
+#define ROUND_UP_EXP_FOR_FLICKER 15
+
+/* Constants for automatic frame rate adjustment */
+#define MAX_EXP 302
+#define MAX_EXP_102 255
+#define LOW_EXP 140
+#define VERY_LOW_EXP 70
+#define TC 94
+#define EXP_ACC_DARK 50
+#define EXP_ACC_LIGHT 90
+#define HIGH_COMP_102 160
+#define MAX_COMP 239
+#define DARK_TIME 3
+#define LIGHT_TIME 3
+
+/* Maximum number of 10ms loops to wait for the stream to become ready */
+#define READY_TIMEOUT 100
+
+/* Developer's Guide Table 5 p 3-34
+ * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
+static u8 flicker_jumps[2][2][4] =
+{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
+ { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
+};
+
+/* forward declaration of local function */
+static void reset_camera_struct(struct cam_data *cam);
+static int find_over_exposure(int brightness);
+static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
+ int on);
+
+
+/**********************************************************************
+ *
+ * Memory management
+ *
+ **********************************************************************/
+static void *rvmalloc(unsigned long size)
+{
+ void *mem;
+ unsigned long adr;
+
+ size = PAGE_ALIGN(size);
+ mem = vmalloc_32(size);
+ if (!mem)
+ return NULL;
+
+ memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+ adr = (unsigned long) mem;
+ while (size > 0) {
+ SetPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ return mem;
+}
+
+static void rvfree(void *mem, unsigned long size)
+{
+ unsigned long adr;
+
+ if (!mem)
+ return;
+
+ adr = (unsigned long) mem;
+ while ((long) size > 0) {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ vfree(mem);
+}
+
+/**********************************************************************
+ *
+ * /proc interface
+ *
+ **********************************************************************/
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry *cpia_proc_root=NULL;
+
+static int cpia_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ char *out = page;
+ int len, tmp;
+ struct cam_data *cam = data;
+ char tmpstr[29];
+
+ /* IMPORTANT: This output MUST be kept under PAGE_SIZE
+ * or we need to get more sophisticated. */
+
+ out += sprintf(out, "read-only\n-----------------------\n");
+ out += sprintf(out, "V4L Driver version: %d.%d.%d\n",
+ CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
+ out += sprintf(out, "CPIA Version: %d.%02d (%d.%d)\n",
+ cam->params.version.firmwareVersion,
+ cam->params.version.firmwareRevision,
+ cam->params.version.vcVersion,
+ cam->params.version.vcRevision);
+ out += sprintf(out, "CPIA PnP-ID: %04x:%04x:%04x\n",
+ cam->params.pnpID.vendor, cam->params.pnpID.product,
+ cam->params.pnpID.deviceRevision);
+ out += sprintf(out, "VP-Version: %d.%d %04x\n",
+ cam->params.vpVersion.vpVersion,
+ cam->params.vpVersion.vpRevision,
+ cam->params.vpVersion.cameraHeadID);
+
+ out += sprintf(out, "system_state: %#04x\n",
+ cam->params.status.systemState);
+ out += sprintf(out, "grab_state: %#04x\n",
+ cam->params.status.grabState);
+ out += sprintf(out, "stream_state: %#04x\n",
+ cam->params.status.streamState);
+ out += sprintf(out, "fatal_error: %#04x\n",
+ cam->params.status.fatalError);
+ out += sprintf(out, "cmd_error: %#04x\n",
+ cam->params.status.cmdError);
+ out += sprintf(out, "debug_flags: %#04x\n",
+ cam->params.status.debugFlags);
+ out += sprintf(out, "vp_status: %#04x\n",
+ cam->params.status.vpStatus);
+ out += sprintf(out, "error_code: %#04x\n",
+ cam->params.status.errorCode);
+ /* QX3 specific entries */
+ if (cam->params.qx3.qx3_detected) {
+ out += sprintf(out, "button: %4d\n",
+ cam->params.qx3.button);
+ out += sprintf(out, "cradled: %4d\n",
+ cam->params.qx3.cradled);
+ }
+ out += sprintf(out, "video_size: %s\n",
+ cam->params.format.videoSize == VIDEOSIZE_CIF ?
+ "CIF " : "QCIF");
+ out += sprintf(out, "roi: (%3d, %3d) to (%3d, %3d)\n",
+ cam->params.roi.colStart*8,
+ cam->params.roi.rowStart*4,
+ cam->params.roi.colEnd*8,
+ cam->params.roi.rowEnd*4);
+ out += sprintf(out, "actual_fps: %3d\n", cam->fps);
+ out += sprintf(out, "transfer_rate: %4dkB/s\n",
+ cam->transfer_rate);
+
+ out += sprintf(out, "\nread-write\n");
+ out += sprintf(out, "----------------------- current min"
+ " max default comment\n");
+ out += sprintf(out, "brightness: %8d %8d %8d %8d\n",
+ cam->params.colourParams.brightness, 0, 100, 50);
+ if (cam->params.version.firmwareVersion == 1 &&
+ cam->params.version.firmwareRevision == 2)
+ /* 1-02 firmware limits contrast to 80 */
+ tmp = 80;
+ else
+ tmp = 96;
+
+ out += sprintf(out, "contrast: %8d %8d %8d %8d"
+ " steps of 8\n",
+ cam->params.colourParams.contrast, 0, tmp, 48);
+ out += sprintf(out, "saturation: %8d %8d %8d %8d\n",
+ cam->params.colourParams.saturation, 0, 100, 50);
+ tmp = (25000+5000*cam->params.sensorFps.baserate)/
+ (1<<cam->params.sensorFps.divisor);
+ out += sprintf(out, "sensor_fps: %4d.%03d %8d %8d %8d\n",
+ tmp/1000, tmp%1000, 3, 30, 15);
+ out += sprintf(out, "stream_start_line: %8d %8d %8d %8d\n",
+ 2*cam->params.streamStartLine, 0,
+ cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144,
+ cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120);
+ out += sprintf(out, "sub_sample: %8s %8s %8s %8s\n",
+ cam->params.format.subSample == SUBSAMPLE_420 ?
+ "420" : "422", "420", "422", "422");
+ out += sprintf(out, "yuv_order: %8s %8s %8s %8s\n",
+ cam->params.format.yuvOrder == YUVORDER_YUYV ?
+ "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV");
+ out += sprintf(out, "ecp_timing: %8s %8s %8s %8s\n",
+ cam->params.ecpTiming ? "slow" : "normal", "slow",
+ "normal", "normal");
+
+ if (cam->params.colourBalance.balanceMode == 2) {
+ sprintf(tmpstr, "auto");
+ } else {
+ sprintf(tmpstr, "manual");
+ }
+ out += sprintf(out, "color_balance_mode: %8s %8s %8s"
+ " %8s\n", tmpstr, "manual", "auto", "auto");
+ out += sprintf(out, "red_gain: %8d %8d %8d %8d\n",
+ cam->params.colourBalance.redGain, 0, 212, 32);
+ out += sprintf(out, "green_gain: %8d %8d %8d %8d\n",
+ cam->params.colourBalance.greenGain, 0, 212, 6);
+ out += sprintf(out, "blue_gain: %8d %8d %8d %8d\n",
+ cam->params.colourBalance.blueGain, 0, 212, 92);
+
+ if (cam->params.version.firmwareVersion == 1 &&
+ cam->params.version.firmwareRevision == 2)
+ /* 1-02 firmware limits gain to 2 */
+ sprintf(tmpstr, "%8d %8d %8d", 1, 2, 2);
+ else
+ sprintf(tmpstr, "%8d %8d %8d", 1, 8, 2);
+
+ if (cam->params.exposure.gainMode == 0)
+ out += sprintf(out, "max_gain: unknown %28s"
+ " powers of 2\n", tmpstr);
+ else
+ out += sprintf(out, "max_gain: %8d %28s"
+ " 1,2,4 or 8 \n",
+ 1<<(cam->params.exposure.gainMode-1), tmpstr);
+
+ switch(cam->params.exposure.expMode) {
+ case 1:
+ case 3:
+ sprintf(tmpstr, "manual");
+ break;
+ case 2:
+ sprintf(tmpstr, "auto");
+ break;
+ default:
+ sprintf(tmpstr, "unknown");
+ break;
+ }
+ out += sprintf(out, "exposure_mode: %8s %8s %8s"
+ " %8s\n", tmpstr, "manual", "auto", "auto");
+ out += sprintf(out, "centre_weight: %8s %8s %8s %8s\n",
+ (2-cam->params.exposure.centreWeight) ? "on" : "off",
+ "off", "on", "on");
+ out += sprintf(out, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n",
+ 1<<cam->params.exposure.gain, 1, 1);
+ if (cam->params.version.firmwareVersion == 1 &&
+ cam->params.version.firmwareRevision == 2)
+ /* 1-02 firmware limits fineExp/2 to 127 */
+ tmp = 254;
+ else
+ tmp = 510;
+
+ out += sprintf(out, "fine_exp: %8d %8d %8d %8d\n",
+ cam->params.exposure.fineExp*2, 0, tmp, 0);
+ if (cam->params.version.firmwareVersion == 1 &&
+ cam->params.version.firmwareRevision == 2)
+ /* 1-02 firmware limits coarseExpHi to 0 */
+ tmp = MAX_EXP_102;
+ else
+ tmp = MAX_EXP;
+
+ out += sprintf(out, "coarse_exp: %8d %8d %8d"
+ " %8d\n", cam->params.exposure.coarseExpLo+
+ 256*cam->params.exposure.coarseExpHi, 0, tmp, 185);
+ out += sprintf(out, "red_comp: %8d %8d %8d %8d\n",
+ cam->params.exposure.redComp, COMP_RED, 255, COMP_RED);
+ out += sprintf(out, "green1_comp: %8d %8d %8d %8d\n",
+ cam->params.exposure.green1Comp, COMP_GREEN1, 255,
+ COMP_GREEN1);
+ out += sprintf(out, "green2_comp: %8d %8d %8d %8d\n",
+ cam->params.exposure.green2Comp, COMP_GREEN2, 255,
+ COMP_GREEN2);
+ out += sprintf(out, "blue_comp: %8d %8d %8d %8d\n",
+ cam->params.exposure.blueComp, COMP_BLUE, 255, COMP_BLUE);
+
+ out += sprintf(out, "apcor_gain1: %#8x %#8x %#8x %#8x\n",
+ cam->params.apcor.gain1, 0, 0xff, 0x1c);
+ out += sprintf(out, "apcor_gain2: %#8x %#8x %#8x %#8x\n",
+ cam->params.apcor.gain2, 0, 0xff, 0x1a);
+ out += sprintf(out, "apcor_gain4: %#8x %#8x %#8x %#8x\n",
+ cam->params.apcor.gain4, 0, 0xff, 0x2d);
+ out += sprintf(out, "apcor_gain8: %#8x %#8x %#8x %#8x\n",
+ cam->params.apcor.gain8, 0, 0xff, 0x2a);
+ out += sprintf(out, "vl_offset_gain1: %8d %8d %8d %8d\n",
+ cam->params.vlOffset.gain1, 0, 255, 24);
+ out += sprintf(out, "vl_offset_gain2: %8d %8d %8d %8d\n",
+ cam->params.vlOffset.gain2, 0, 255, 28);
+ out += sprintf(out, "vl_offset_gain4: %8d %8d %8d %8d\n",
+ cam->params.vlOffset.gain4, 0, 255, 30);
+ out += sprintf(out, "vl_offset_gain8: %8d %8d %8d %8d\n",
+ cam->params.vlOffset.gain8, 0, 255, 30);
+ out += sprintf(out, "flicker_control: %8s %8s %8s %8s\n",
+ cam->params.flickerControl.flickerMode ? "on" : "off",
+ "off", "on", "off");
+ out += sprintf(out, "mains_frequency: %8d %8d %8d %8d"
+ " only 50/60\n",
+ cam->mainsFreq ? 60 : 50, 50, 60, 50);
+ if(cam->params.flickerControl.allowableOverExposure < 0)
+ out += sprintf(out, "allowable_overexposure: %4dauto auto %8d auto\n",
+ -cam->params.flickerControl.allowableOverExposure,
+ 255);
+ else
+ out += sprintf(out, "allowable_overexposure: %8d auto %8d auto\n",
+ cam->params.flickerControl.allowableOverExposure,
+ 255);
+ out += sprintf(out, "compression_mode: ");
+ switch(cam->params.compression.mode) {
+ case CPIA_COMPRESSION_NONE:
+ out += sprintf(out, "%8s", "none");
+ break;
+ case CPIA_COMPRESSION_AUTO:
+ out += sprintf(out, "%8s", "auto");
+ break;
+ case CPIA_COMPRESSION_MANUAL:
+ out += sprintf(out, "%8s", "manual");
+ break;
+ default:
+ out += sprintf(out, "%8s", "unknown");
+ break;
+ }
+ out += sprintf(out, " none,auto,manual auto\n");
+ out += sprintf(out, "decimation_enable: %8s %8s %8s %8s\n",
+ cam->params.compression.decimation ==
+ DECIMATION_ENAB ? "on":"off", "off", "on",
+ "off");
+ out += sprintf(out, "compression_target: %9s %9s %9s %9s\n",
+ cam->params.compressionTarget.frTargeting ==
+ CPIA_COMPRESSION_TARGET_FRAMERATE ?
+ "framerate":"quality",
+ "framerate", "quality", "quality");
+ out += sprintf(out, "target_framerate: %8d %8d %8d %8d\n",
+ cam->params.compressionTarget.targetFR, 1, 30, 15);
+ out += sprintf(out, "target_quality: %8d %8d %8d %8d\n",
+ cam->params.compressionTarget.targetQ, 1, 64, 5);
+ out += sprintf(out, "y_threshold: %8d %8d %8d %8d\n",
+ cam->params.yuvThreshold.yThreshold, 0, 31, 6);
+ out += sprintf(out, "uv_threshold: %8d %8d %8d %8d\n",
+ cam->params.yuvThreshold.uvThreshold, 0, 31, 6);
+ out += sprintf(out, "hysteresis: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.hysteresis, 0, 255, 3);
+ out += sprintf(out, "threshold_max: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.threshMax, 0, 255, 11);
+ out += sprintf(out, "small_step: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.smallStep, 0, 255, 1);
+ out += sprintf(out, "large_step: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.largeStep, 0, 255, 3);
+ out += sprintf(out, "decimation_hysteresis: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.decimationHysteresis,
+ 0, 255, 2);
+ out += sprintf(out, "fr_diff_step_thresh: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.frDiffStepThresh,
+ 0, 255, 5);
+ out += sprintf(out, "q_diff_step_thresh: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.qDiffStepThresh,
+ 0, 255, 3);
+ out += sprintf(out, "decimation_thresh_mod: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.decimationThreshMod,
+ 0, 255, 2);
+ /* QX3 specific entries */
+ if (cam->params.qx3.qx3_detected) {
+ out += sprintf(out, "toplight: %8s %8s %8s %8s\n",
+ cam->params.qx3.toplight ? "on" : "off",
+ "off", "on", "off");
+ out += sprintf(out, "bottomlight: %8s %8s %8s %8s\n",
+ cam->params.qx3.bottomlight ? "on" : "off",
+ "off", "on", "off");
+ }
+
+ len = out - page;
+ len -= off;
+ if (len < count) {
+ *eof = 1;
+ if (len <= 0) return 0;
+ } else
+ len = count;
+
+ *start = page + off;
+ return len;
+}
+
+
+static int match(char *checkstr, char **buffer, unsigned long *count,
+ int *find_colon, int *err)
+{
+ int ret, colon_found = 1;
+ int len = strlen(checkstr);
+ ret = (len <= *count && strncmp(*buffer, checkstr, len) == 0);
+ if (ret) {
+ *buffer += len;
+ *count -= len;
+ if (*find_colon) {
+ colon_found = 0;
+ while (*count && (**buffer == ' ' || **buffer == '\t' ||
+ (!colon_found && **buffer == ':'))) {
+ if (**buffer == ':')
+ colon_found = 1;
+ --*count;
+ ++*buffer;
+ }
+ if (!*count || !colon_found)
+ *err = -EINVAL;
+ *find_colon = 0;
+ }
+ }
+ return ret;
+}
+
+static unsigned long int value(char **buffer, unsigned long *count, int *err)
+{
+ char *p;
+ unsigned long int ret;
+ ret = simple_strtoul(*buffer, &p, 0);
+ if (p == *buffer)
+ *err = -EINVAL;
+ else {
+ *count -= p - *buffer;
+ *buffer = p;
+ }
+ return ret;
+}
+
+static int cpia_write_proc(struct file *file, const char __user *buf,
+ unsigned long count, void *data)
+{
+ struct cam_data *cam = data;
+ struct cam_params new_params;
+ char *page, *buffer;
+ int retval, find_colon;
+ int size = count;
+ unsigned long val = 0;
+ u32 command_flags = 0;
+ u8 new_mains;
+
+ /*
+ * This code to copy from buf to page is shamelessly copied
+ * from the comx driver
+ */
+ if (count > PAGE_SIZE) {
+ printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE);
+ return -ENOSPC;
+ }
+
+ if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM;
+
+ if(copy_from_user(page, buf, count))
+ {
+ retval = -EFAULT;
+ goto out;
+ }
+
+ if (page[count-1] == '\n')
+ page[count-1] = '\0';
+ else if (count < PAGE_SIZE)
+ page[count] = '\0';
+ else if (page[count]) {
+ retval = -EINVAL;
+ goto out;
+ }
+
+ buffer = page;
+
+ if (mutex_lock_interruptible(&cam->param_lock))
+ return -ERESTARTSYS;
+
+ /*
+ * Skip over leading whitespace
+ */
+ while (count && isspace(*buffer)) {
+ --count;
+ ++buffer;
+ }
+
+ memcpy(&new_params, &cam->params, sizeof(struct cam_params));
+ new_mains = cam->mainsFreq;
+
+#define MATCH(x) (match(x, &buffer, &count, &find_colon, &retval))
+#define VALUE (value(&buffer,&count, &retval))
+#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \
+ new_params.version.firmwareRevision == (y))
+
+ retval = 0;
+ while (count && !retval) {
+ find_colon = 1;
+ if (MATCH("brightness")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 100)
+ new_params.colourParams.brightness = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOLOURPARAMS;
+ if(new_params.flickerControl.allowableOverExposure < 0)
+ new_params.flickerControl.allowableOverExposure =
+ -find_over_exposure(new_params.colourParams.brightness);
+ if(new_params.flickerControl.flickerMode != 0)
+ command_flags |= COMMAND_SETFLICKERCTRL;
+
+ } else if (MATCH("contrast")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 100) {
+ /* contrast is in steps of 8, so round*/
+ val = ((val + 3) / 8) * 8;
+ /* 1-02 firmware limits contrast to 80*/
+ if (FIRMWARE_VERSION(1,2) && val > 80)
+ val = 80;
+
+ new_params.colourParams.contrast = val;
+ } else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOLOURPARAMS;
+ } else if (MATCH("saturation")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 100)
+ new_params.colourParams.saturation = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOLOURPARAMS;
+ } else if (MATCH("sensor_fps")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ /* find values so that sensorFPS is minimized,
+ * but >= val */
+ if (val > 30)
+ retval = -EINVAL;
+ else if (val > 25) {
+ new_params.sensorFps.divisor = 0;
+ new_params.sensorFps.baserate = 1;
+ } else if (val > 15) {
+ new_params.sensorFps.divisor = 0;
+ new_params.sensorFps.baserate = 0;
+ } else if (val > 12) {
+ new_params.sensorFps.divisor = 1;
+ new_params.sensorFps.baserate = 1;
+ } else if (val > 7) {
+ new_params.sensorFps.divisor = 1;
+ new_params.sensorFps.baserate = 0;
+ } else if (val > 6) {
+ new_params.sensorFps.divisor = 2;
+ new_params.sensorFps.baserate = 1;
+ } else if (val > 3) {
+ new_params.sensorFps.divisor = 2;
+ new_params.sensorFps.baserate = 0;
+ } else {
+ new_params.sensorFps.divisor = 3;
+ /* Either base rate would work here */
+ new_params.sensorFps.baserate = 1;
+ }
+ new_params.flickerControl.coarseJump =
+ flicker_jumps[new_mains]
+ [new_params.sensorFps.baserate]
+ [new_params.sensorFps.divisor];
+ if (new_params.flickerControl.flickerMode)
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ }
+ command_flags |= COMMAND_SETSENSORFPS;
+ cam->exposure_status = EXPOSURE_NORMAL;
+ } else if (MATCH("stream_start_line")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ int max_line = 288;
+
+ if (new_params.format.videoSize == VIDEOSIZE_QCIF)
+ max_line = 144;
+ if (val <= max_line)
+ new_params.streamStartLine = val/2;
+ else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("sub_sample")) {
+ if (!retval && MATCH("420"))
+ new_params.format.subSample = SUBSAMPLE_420;
+ else if (!retval && MATCH("422"))
+ new_params.format.subSample = SUBSAMPLE_422;
+ else
+ retval = -EINVAL;
+
+ command_flags |= COMMAND_SETFORMAT;
+ } else if (MATCH("yuv_order")) {
+ if (!retval && MATCH("YUYV"))
+ new_params.format.yuvOrder = YUVORDER_YUYV;
+ else if (!retval && MATCH("UYVY"))
+ new_params.format.yuvOrder = YUVORDER_UYVY;
+ else
+ retval = -EINVAL;
+
+ command_flags |= COMMAND_SETFORMAT;
+ } else if (MATCH("ecp_timing")) {
+ if (!retval && MATCH("normal"))
+ new_params.ecpTiming = 0;
+ else if (!retval && MATCH("slow"))
+ new_params.ecpTiming = 1;
+ else
+ retval = -EINVAL;
+
+ command_flags |= COMMAND_SETECPTIMING;
+ } else if (MATCH("color_balance_mode")) {
+ if (!retval && MATCH("manual"))
+ new_params.colourBalance.balanceMode = 3;
+ else if (!retval && MATCH("auto"))
+ new_params.colourBalance.balanceMode = 2;
+ else
+ retval = -EINVAL;
+
+ command_flags |= COMMAND_SETCOLOURBALANCE;
+ } else if (MATCH("red_gain")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 212) {
+ new_params.colourBalance.redGain = val;
+ new_params.colourBalance.balanceMode = 1;
+ } else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOLOURBALANCE;
+ } else if (MATCH("green_gain")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 212) {
+ new_params.colourBalance.greenGain = val;
+ new_params.colourBalance.balanceMode = 1;
+ } else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOLOURBALANCE;
+ } else if (MATCH("blue_gain")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 212) {
+ new_params.colourBalance.blueGain = val;
+ new_params.colourBalance.balanceMode = 1;
+ } else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOLOURBALANCE;
+ } else if (MATCH("max_gain")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ /* 1-02 firmware limits gain to 2 */
+ if (FIRMWARE_VERSION(1,2) && val > 2)
+ val = 2;
+ switch(val) {
+ case 1:
+ new_params.exposure.gainMode = 1;
+ break;
+ case 2:
+ new_params.exposure.gainMode = 2;
+ break;
+ case 4:
+ new_params.exposure.gainMode = 3;
+ break;
+ case 8:
+ new_params.exposure.gainMode = 4;
+ break;
+ default:
+ retval = -EINVAL;
+ break;
+ }
+ }
+ command_flags |= COMMAND_SETEXPOSURE;
+ } else if (MATCH("exposure_mode")) {
+ if (!retval && MATCH("auto"))
+ new_params.exposure.expMode = 2;
+ else if (!retval && MATCH("manual")) {
+ if (new_params.exposure.expMode == 2)
+ new_params.exposure.expMode = 3;
+ if(new_params.flickerControl.flickerMode != 0)
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ new_params.flickerControl.flickerMode = 0;
+ } else
+ retval = -EINVAL;
+
+ command_flags |= COMMAND_SETEXPOSURE;
+ } else if (MATCH("centre_weight")) {
+ if (!retval && MATCH("on"))
+ new_params.exposure.centreWeight = 1;
+ else if (!retval && MATCH("off"))
+ new_params.exposure.centreWeight = 2;
+ else
+ retval = -EINVAL;
+
+ command_flags |= COMMAND_SETEXPOSURE;
+ } else if (MATCH("gain")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ switch(val) {
+ case 1:
+ new_params.exposure.gain = 0;
+ break;
+ case 2:
+ new_params.exposure.gain = 1;
+ break;
+ case 4:
+ new_params.exposure.gain = 2;
+ break;
+ case 8:
+ new_params.exposure.gain = 3;
+ break;
+ default:
+ retval = -EINVAL;
+ break;
+ }
+ new_params.exposure.expMode = 1;
+ if(new_params.flickerControl.flickerMode != 0)
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ new_params.flickerControl.flickerMode = 0;
+ command_flags |= COMMAND_SETEXPOSURE;
+ if (new_params.exposure.gain >
+ new_params.exposure.gainMode-1)
+ retval = -EINVAL;
+ }
+ } else if (MATCH("fine_exp")) {
+ if (!retval)
+ val = VALUE/2;
+
+ if (!retval) {
+ if (val < 256) {
+ /* 1-02 firmware limits fineExp/2 to 127*/
+ if (FIRMWARE_VERSION(1,2) && val > 127)
+ val = 127;
+ new_params.exposure.fineExp = val;
+ new_params.exposure.expMode = 1;
+ command_flags |= COMMAND_SETEXPOSURE;
+ if(new_params.flickerControl.flickerMode != 0)
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ new_params.flickerControl.flickerMode = 0;
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ } else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("coarse_exp")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= MAX_EXP) {
+ if (FIRMWARE_VERSION(1,2) &&
+ val > MAX_EXP_102)
+ val = MAX_EXP_102;
+ new_params.exposure.coarseExpLo =
+ val & 0xff;
+ new_params.exposure.coarseExpHi =
+ val >> 8;
+ new_params.exposure.expMode = 1;
+ command_flags |= COMMAND_SETEXPOSURE;
+ if(new_params.flickerControl.flickerMode != 0)
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ new_params.flickerControl.flickerMode = 0;
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ } else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("red_comp")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val >= COMP_RED && val <= 255) {
+ new_params.exposure.redComp = val;
+ new_params.exposure.compMode = 1;
+ command_flags |= COMMAND_SETEXPOSURE;
+ } else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("green1_comp")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val >= COMP_GREEN1 && val <= 255) {
+ new_params.exposure.green1Comp = val;
+ new_params.exposure.compMode = 1;
+ command_flags |= COMMAND_SETEXPOSURE;
+ } else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("green2_comp")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val >= COMP_GREEN2 && val <= 255) {
+ new_params.exposure.green2Comp = val;
+ new_params.exposure.compMode = 1;
+ command_flags |= COMMAND_SETEXPOSURE;
+ } else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("blue_comp")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val >= COMP_BLUE && val <= 255) {
+ new_params.exposure.blueComp = val;
+ new_params.exposure.compMode = 1;
+ command_flags |= COMMAND_SETEXPOSURE;
+ } else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("apcor_gain1")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ command_flags |= COMMAND_SETAPCOR;
+ if (val <= 0xff)
+ new_params.apcor.gain1 = val;
+ else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("apcor_gain2")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ command_flags |= COMMAND_SETAPCOR;
+ if (val <= 0xff)
+ new_params.apcor.gain2 = val;
+ else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("apcor_gain4")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ command_flags |= COMMAND_SETAPCOR;
+ if (val <= 0xff)
+ new_params.apcor.gain4 = val;
+ else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("apcor_gain8")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ command_flags |= COMMAND_SETAPCOR;
+ if (val <= 0xff)
+ new_params.apcor.gain8 = val;
+ else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("vl_offset_gain1")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.vlOffset.gain1 = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETVLOFFSET;
+ } else if (MATCH("vl_offset_gain2")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.vlOffset.gain2 = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETVLOFFSET;
+ } else if (MATCH("vl_offset_gain4")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.vlOffset.gain4 = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETVLOFFSET;
+ } else if (MATCH("vl_offset_gain8")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.vlOffset.gain8 = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETVLOFFSET;
+ } else if (MATCH("flicker_control")) {
+ if (!retval && MATCH("on")) {
+ set_flicker(&new_params, &command_flags, 1);
+ } else if (!retval && MATCH("off")) {
+ set_flicker(&new_params, &command_flags, 0);
+ } else
+ retval = -EINVAL;
+
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ } else if (MATCH("mains_frequency")) {
+ if (!retval && MATCH("50")) {
+ new_mains = 0;
+ new_params.flickerControl.coarseJump =
+ flicker_jumps[new_mains]
+ [new_params.sensorFps.baserate]
+ [new_params.sensorFps.divisor];
+ if (new_params.flickerControl.flickerMode)
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ } else if (!retval && MATCH("60")) {
+ new_mains = 1;
+ new_params.flickerControl.coarseJump =
+ flicker_jumps[new_mains]
+ [new_params.sensorFps.baserate]
+ [new_params.sensorFps.divisor];
+ if (new_params.flickerControl.flickerMode)
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ } else
+ retval = -EINVAL;
+ } else if (MATCH("allowable_overexposure")) {
+ if (!retval && MATCH("auto")) {
+ new_params.flickerControl.allowableOverExposure =
+ -find_over_exposure(new_params.colourParams.brightness);
+ if(new_params.flickerControl.flickerMode != 0)
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ } else {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff) {
+ new_params.flickerControl.
+ allowableOverExposure = val;
+ if(new_params.flickerControl.flickerMode != 0)
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ } else
+ retval = -EINVAL;
+ }
+ }
+ } else if (MATCH("compression_mode")) {
+ if (!retval && MATCH("none"))
+ new_params.compression.mode =
+ CPIA_COMPRESSION_NONE;
+ else if (!retval && MATCH("auto"))
+ new_params.compression.mode =
+ CPIA_COMPRESSION_AUTO;
+ else if (!retval && MATCH("manual"))
+ new_params.compression.mode =
+ CPIA_COMPRESSION_MANUAL;
+ else
+ retval = -EINVAL;
+
+ command_flags |= COMMAND_SETCOMPRESSION;
+ } else if (MATCH("decimation_enable")) {
+ if (!retval && MATCH("off"))
+ new_params.compression.decimation = 0;
+ else if (!retval && MATCH("on"))
+ new_params.compression.decimation = 1;
+ else
+ retval = -EINVAL;
+
+ command_flags |= COMMAND_SETCOMPRESSION;
+ } else if (MATCH("compression_target")) {
+ if (!retval && MATCH("quality"))
+ new_params.compressionTarget.frTargeting =
+ CPIA_COMPRESSION_TARGET_QUALITY;
+ else if (!retval && MATCH("framerate"))
+ new_params.compressionTarget.frTargeting =
+ CPIA_COMPRESSION_TARGET_FRAMERATE;
+ else
+ retval = -EINVAL;
+
+ command_flags |= COMMAND_SETCOMPRESSIONTARGET;
+ } else if (MATCH("target_framerate")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if(val > 0 && val <= 30)
+ new_params.compressionTarget.targetFR = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONTARGET;
+ } else if (MATCH("target_quality")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if(val > 0 && val <= 64)
+ new_params.compressionTarget.targetQ = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONTARGET;
+ } else if (MATCH("y_threshold")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val < 32)
+ new_params.yuvThreshold.yThreshold = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETYUVTHRESH;
+ } else if (MATCH("uv_threshold")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val < 32)
+ new_params.yuvThreshold.uvThreshold = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETYUVTHRESH;
+ } else if (MATCH("hysteresis")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.compressionParams.hysteresis = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ } else if (MATCH("threshold_max")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.compressionParams.threshMax = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ } else if (MATCH("small_step")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.compressionParams.smallStep = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ } else if (MATCH("large_step")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.compressionParams.largeStep = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ } else if (MATCH("decimation_hysteresis")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.compressionParams.decimationHysteresis = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ } else if (MATCH("fr_diff_step_thresh")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.compressionParams.frDiffStepThresh = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ } else if (MATCH("q_diff_step_thresh")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.compressionParams.qDiffStepThresh = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ } else if (MATCH("decimation_thresh_mod")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.compressionParams.decimationThreshMod = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ } else if (MATCH("toplight")) {
+ if (!retval && MATCH("on"))
+ new_params.qx3.toplight = 1;
+ else if (!retval && MATCH("off"))
+ new_params.qx3.toplight = 0;
+ else
+ retval = -EINVAL;
+ command_flags |= COMMAND_SETLIGHTS;
+ } else if (MATCH("bottomlight")) {
+ if (!retval && MATCH("on"))
+ new_params.qx3.bottomlight = 1;
+ else if (!retval && MATCH("off"))
+ new_params.qx3.bottomlight = 0;
+ else
+ retval = -EINVAL;
+ command_flags |= COMMAND_SETLIGHTS;
+ } else {
+ DBG("No match found\n");
+ retval = -EINVAL;
+ }
+
+ if (!retval) {
+ while (count && isspace(*buffer) && *buffer != '\n') {
+ --count;
+ ++buffer;
+ }
+ if (count) {
+ if (*buffer == '\0' && count != 1)
+ retval = -EINVAL;
+ else if (*buffer != '\n' && *buffer != ';' &&
+ *buffer != '\0')
+ retval = -EINVAL;
+ else {
+ --count;
+ ++buffer;
+ }
+ }
+ }
+ }
+#undef MATCH
+#undef VALUE
+#undef FIRMWARE_VERSION
+ if (!retval) {
+ if (command_flags & COMMAND_SETCOLOURPARAMS) {
+ /* Adjust cam->vp to reflect these changes */
+ cam->vp.brightness =
+ new_params.colourParams.brightness*65535/100;
+ cam->vp.contrast =
+ new_params.colourParams.contrast*65535/100;
+ cam->vp.colour =
+ new_params.colourParams.saturation*65535/100;
+ }
+ if((command_flags & COMMAND_SETEXPOSURE) &&
+ new_params.exposure.expMode == 2)
+ cam->exposure_status = EXPOSURE_NORMAL;
+
+ memcpy(&cam->params, &new_params, sizeof(struct cam_params));
+ cam->mainsFreq = new_mains;
+ cam->cmd_queue |= command_flags;
+ retval = size;
+ } else
+ DBG("error: %d\n", retval);
+
+ mutex_unlock(&cam->param_lock);
+
+out:
+ free_page((unsigned long)page);
+ return retval;
+}
+
+static void create_proc_cpia_cam(struct cam_data *cam)
+{
+ char name[7];
+ struct proc_dir_entry *ent;
+
+ if (!cpia_proc_root || !cam)
+ return;
+
+ sprintf(name, "video%d", cam->vdev.minor);
+
+ ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root);
+ if (!ent)
+ return;
+
+ ent->data = cam;
+ ent->read_proc = cpia_read_proc;
+ ent->write_proc = cpia_write_proc;
+ /*
+ size of the proc entry is 3736 bytes for the standard webcam;
+ the extra features of the QX3 microscope add 189 bytes.
+ (we have not yet probed the camera to see which type it is).
+ */
+ ent->size = 3736 + 189;
+ cam->proc_entry = ent;
+}
+
+static void destroy_proc_cpia_cam(struct cam_data *cam)
+{
+ char name[7];
+
+ if (!cam || !cam->proc_entry)
+ return;
+
+ sprintf(name, "video%d", cam->vdev.minor);
+ remove_proc_entry(name, cpia_proc_root);
+ cam->proc_entry = NULL;
+}
+
+static void proc_cpia_create(void)
+{
+ cpia_proc_root = proc_mkdir("cpia", NULL);
+
+ if (cpia_proc_root)
+ cpia_proc_root->owner = THIS_MODULE;
+ else
+ LOG("Unable to initialise /proc/cpia\n");
+}
+
+static void __exit proc_cpia_destroy(void)
+{
+ remove_proc_entry("cpia", NULL);
+}
+#endif /* CONFIG_PROC_FS */
+
+/* ----------------------- debug functions ---------------------- */
+
+#define printstatus(cam) \
+ DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\
+ cam->params.status.systemState, cam->params.status.grabState, \
+ cam->params.status.streamState, cam->params.status.fatalError, \
+ cam->params.status.cmdError, cam->params.status.debugFlags, \
+ cam->params.status.vpStatus, cam->params.status.errorCode);
+
+/* ----------------------- v4l helpers -------------------------- */
+
+/* supported frame palettes and depths */
+static inline int valid_mode(u16 palette, u16 depth)
+{
+ if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) ||
+ (palette == VIDEO_PALETTE_YUYV && depth == 16))
+ return 1;
+
+ if (colorspace_conv)
+ return (palette == VIDEO_PALETTE_GREY && depth == 8) ||
+ (palette == VIDEO_PALETTE_RGB555 && depth == 16) ||
+ (palette == VIDEO_PALETTE_RGB565 && depth == 16) ||
+ (palette == VIDEO_PALETTE_RGB24 && depth == 24) ||
+ (palette == VIDEO_PALETTE_RGB32 && depth == 32) ||
+ (palette == VIDEO_PALETTE_UYVY && depth == 16);
+
+ return 0;
+}
+
+static int match_videosize( int width, int height )
+{
+ /* return the best match, where 'best' is as always
+ * the largest that is not bigger than what is requested. */
+ if (width>=352 && height>=288)
+ return VIDEOSIZE_352_288; /* CIF */
+
+ if (width>=320 && height>=240)
+ return VIDEOSIZE_320_240; /* SIF */
+
+ if (width>=288 && height>=216)
+ return VIDEOSIZE_288_216;
+
+ if (width>=256 && height>=192)
+ return VIDEOSIZE_256_192;
+
+ if (width>=224 && height>=168)
+ return VIDEOSIZE_224_168;
+
+ if (width>=192 && height>=144)
+ return VIDEOSIZE_192_144;
+
+ if (width>=176 && height>=144)
+ return VIDEOSIZE_176_144; /* QCIF */
+
+ if (width>=160 && height>=120)
+ return VIDEOSIZE_160_120; /* QSIF */
+
+ if (width>=128 && height>=96)
+ return VIDEOSIZE_128_96;
+
+ if (width>=88 && height>=72)
+ return VIDEOSIZE_88_72;
+
+ if (width>=64 && height>=48)
+ return VIDEOSIZE_64_48;
+
+ if (width>=48 && height>=48)
+ return VIDEOSIZE_48_48;
+
+ return -1;
+}
+
+/* these are the capture sizes we support */
+static void set_vw_size(struct cam_data *cam)
+{
+ /* the col/row/start/end values are the result of simple math */
+ /* study the SetROI-command in cpia developers guide p 2-22 */
+ /* streamStartLine is set to the recommended value in the cpia */
+ /* developers guide p 3-37 */
+ switch(cam->video_size) {
+ case VIDEOSIZE_CIF:
+ cam->vw.width = 352;
+ cam->vw.height = 288;
+ cam->params.format.videoSize=VIDEOSIZE_CIF;
+ cam->params.roi.colStart=0;
+ cam->params.roi.rowStart=0;
+ cam->params.streamStartLine = 120;
+ break;
+ case VIDEOSIZE_SIF:
+ cam->vw.width = 320;
+ cam->vw.height = 240;
+ cam->params.format.videoSize=VIDEOSIZE_CIF;
+ cam->params.roi.colStart=2;
+ cam->params.roi.rowStart=6;
+ cam->params.streamStartLine = 120;
+ break;
+ case VIDEOSIZE_288_216:
+ cam->vw.width = 288;
+ cam->vw.height = 216;
+ cam->params.format.videoSize=VIDEOSIZE_CIF;
+ cam->params.roi.colStart=4;
+ cam->params.roi.rowStart=9;
+ cam->params.streamStartLine = 120;
+ break;
+ case VIDEOSIZE_256_192:
+ cam->vw.width = 256;
+ cam->vw.height = 192;
+ cam->params.format.videoSize=VIDEOSIZE_CIF;
+ cam->params.roi.colStart=6;
+ cam->params.roi.rowStart=12;
+ cam->params.streamStartLine = 120;
+ break;
+ case VIDEOSIZE_224_168:
+ cam->vw.width = 224;
+ cam->vw.height = 168;
+ cam->params.format.videoSize=VIDEOSIZE_CIF;
+ cam->params.roi.colStart=8;
+ cam->params.roi.rowStart=15;
+ cam->params.streamStartLine = 120;
+ break;
+ case VIDEOSIZE_192_144:
+ cam->vw.width = 192;
+ cam->vw.height = 144;
+ cam->params.format.videoSize=VIDEOSIZE_CIF;
+ cam->params.roi.colStart=10;
+ cam->params.roi.rowStart=18;
+ cam->params.streamStartLine = 120;
+ break;
+ case VIDEOSIZE_QCIF:
+ cam->vw.width = 176;
+ cam->vw.height = 144;
+ cam->params.format.videoSize=VIDEOSIZE_QCIF;
+ cam->params.roi.colStart=0;
+ cam->params.roi.rowStart=0;
+ cam->params.streamStartLine = 60;
+ break;
+ case VIDEOSIZE_QSIF:
+ cam->vw.width = 160;
+ cam->vw.height = 120;
+ cam->params.format.videoSize=VIDEOSIZE_QCIF;
+ cam->params.roi.colStart=1;
+ cam->params.roi.rowStart=3;
+ cam->params.streamStartLine = 60;
+ break;
+ case VIDEOSIZE_128_96:
+ cam->vw.width = 128;
+ cam->vw.height = 96;
+ cam->params.format.videoSize=VIDEOSIZE_QCIF;
+ cam->params.roi.colStart=3;
+ cam->params.roi.rowStart=6;
+ cam->params.streamStartLine = 60;
+ break;
+ case VIDEOSIZE_88_72:
+ cam->vw.width = 88;
+ cam->vw.height = 72;
+ cam->params.format.videoSize=VIDEOSIZE_QCIF;
+ cam->params.roi.colStart=5;
+ cam->params.roi.rowStart=9;
+ cam->params.streamStartLine = 60;
+ break;
+ case VIDEOSIZE_64_48:
+ cam->vw.width = 64;
+ cam->vw.height = 48;
+ cam->params.format.videoSize=VIDEOSIZE_QCIF;
+ cam->params.roi.colStart=7;
+ cam->params.roi.rowStart=12;
+ cam->params.streamStartLine = 60;
+ break;
+ case VIDEOSIZE_48_48:
+ cam->vw.width = 48;
+ cam->vw.height = 48;
+ cam->params.format.videoSize=VIDEOSIZE_QCIF;
+ cam->params.roi.colStart=8;
+ cam->params.roi.rowStart=6;
+ cam->params.streamStartLine = 60;
+ break;
+ default:
+ LOG("bad videosize value: %d\n", cam->video_size);
+ return;
+ }
+
+ if(cam->vc.width == 0)
+ cam->vc.width = cam->vw.width;
+ if(cam->vc.height == 0)
+ cam->vc.height = cam->vw.height;
+
+ cam->params.roi.colStart += cam->vc.x >> 3;
+ cam->params.roi.colEnd = cam->params.roi.colStart +
+ (cam->vc.width >> 3);
+ cam->params.roi.rowStart += cam->vc.y >> 2;
+ cam->params.roi.rowEnd = cam->params.roi.rowStart +
+ (cam->vc.height >> 2);
+
+ return;
+}
+
+static int allocate_frame_buf(struct cam_data *cam)
+{
+ int i;
+
+ cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE);
+ if (!cam->frame_buf)
+ return -ENOBUFS;
+
+ for (i = 0; i < FRAME_NUM; i++)
+ cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE;
+
+ return 0;
+}
+
+static int free_frame_buf(struct cam_data *cam)
+{
+ int i;
+
+ rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE);
+ cam->frame_buf = NULL;
+ for (i=0; i < FRAME_NUM; i++)
+ cam->frame[i].data = NULL;
+
+ return 0;
+}
+
+
+static inline void free_frames(struct cpia_frame frame[FRAME_NUM])
+{
+ int i;
+
+ for (i=0; i < FRAME_NUM; i++)
+ frame[i].state = FRAME_UNUSED;
+ return;
+}
+
+/**********************************************************************
+ *
+ * General functions
+ *
+ **********************************************************************/
+/* send an arbitrary command to the camera */
+static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d)
+{
+ int retval, datasize;
+ u8 cmd[8], data[8];
+
+ switch(command) {
+ case CPIA_COMMAND_GetCPIAVersion:
+ case CPIA_COMMAND_GetPnPID:
+ case CPIA_COMMAND_GetCameraStatus:
+ case CPIA_COMMAND_GetVPVersion:
+ datasize=8;
+ break;
+ case CPIA_COMMAND_GetColourParams:
+ case CPIA_COMMAND_GetColourBalance:
+ case CPIA_COMMAND_GetExposure:
+ mutex_lock(&cam->param_lock);
+ datasize=8;
+ break;
+ case CPIA_COMMAND_ReadMCPorts:
+ case CPIA_COMMAND_ReadVCRegs:
+ datasize = 4;
+ break;
+ default:
+ datasize=0;
+ break;
+ }
+
+ cmd[0] = command>>8;
+ cmd[1] = command&0xff;
+ cmd[2] = a;
+ cmd[3] = b;
+ cmd[4] = c;
+ cmd[5] = d;
+ cmd[6] = datasize;
+ cmd[7] = 0;
+
+ retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
+ if (retval) {
+ DBG("%x - failed, retval=%d\n", command, retval);
+ if (command == CPIA_COMMAND_GetColourParams ||
+ command == CPIA_COMMAND_GetColourBalance ||
+ command == CPIA_COMMAND_GetExposure)
+ mutex_unlock(&cam->param_lock);
+ } else {
+ switch(command) {
+ case CPIA_COMMAND_GetCPIAVersion:
+ cam->params.version.firmwareVersion = data[0];
+ cam->params.version.firmwareRevision = data[1];
+ cam->params.version.vcVersion = data[2];
+ cam->params.version.vcRevision = data[3];
+ break;
+ case CPIA_COMMAND_GetPnPID:
+ cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8);
+ cam->params.pnpID.product = data[2]+(((u16)data[3])<<8);
+ cam->params.pnpID.deviceRevision =
+ data[4]+(((u16)data[5])<<8);
+ break;
+ case CPIA_COMMAND_GetCameraStatus:
+ cam->params.status.systemState = data[0];
+ cam->params.status.grabState = data[1];
+ cam->params.status.streamState = data[2];
+ cam->params.status.fatalError = data[3];
+ cam->params.status.cmdError = data[4];
+ cam->params.status.debugFlags = data[5];
+ cam->params.status.vpStatus = data[6];
+ cam->params.status.errorCode = data[7];
+ break;
+ case CPIA_COMMAND_GetVPVersion:
+ cam->params.vpVersion.vpVersion = data[0];
+ cam->params.vpVersion.vpRevision = data[1];
+ cam->params.vpVersion.cameraHeadID =
+ data[2]+(((u16)data[3])<<8);
+ break;
+ case CPIA_COMMAND_GetColourParams:
+ cam->params.colourParams.brightness = data[0];
+ cam->params.colourParams.contrast = data[1];
+ cam->params.colourParams.saturation = data[2];
+ mutex_unlock(&cam->param_lock);
+ break;
+ case CPIA_COMMAND_GetColourBalance:
+ cam->params.colourBalance.redGain = data[0];
+ cam->params.colourBalance.greenGain = data[1];
+ cam->params.colourBalance.blueGain = data[2];
+ mutex_unlock(&cam->param_lock);
+ break;
+ case CPIA_COMMAND_GetExposure:
+ cam->params.exposure.gain = data[0];
+ cam->params.exposure.fineExp = data[1];
+ cam->params.exposure.coarseExpLo = data[2];
+ cam->params.exposure.coarseExpHi = data[3];
+ cam->params.exposure.redComp = data[4];
+ cam->params.exposure.green1Comp = data[5];
+ cam->params.exposure.green2Comp = data[6];
+ cam->params.exposure.blueComp = data[7];
+ mutex_unlock(&cam->param_lock);
+ break;
+
+ case CPIA_COMMAND_ReadMCPorts:
+ if (!cam->params.qx3.qx3_detected)
+ break;
+ /* test button press */
+ cam->params.qx3.button = ((data[1] & 0x02) == 0);
+ if (cam->params.qx3.button) {
+ /* button pressed - unlock the latch */
+ do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0);
+ do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0);
+ }
+
+ /* test whether microscope is cradled */
+ cam->params.qx3.cradled = ((data[2] & 0x40) == 0);
+ break;
+
+ default:
+ break;
+ }
+ }
+ return retval;
+}
+
+/* send a command to the camera with an additional data transaction */
+static int do_command_extended(struct cam_data *cam, u16 command,
+ u8 a, u8 b, u8 c, u8 d,
+ u8 e, u8 f, u8 g, u8 h,
+ u8 i, u8 j, u8 k, u8 l)
+{
+ int retval;
+ u8 cmd[8], data[8];
+
+ cmd[0] = command>>8;
+ cmd[1] = command&0xff;
+ cmd[2] = a;
+ cmd[3] = b;
+ cmd[4] = c;
+ cmd[5] = d;
+ cmd[6] = 8;
+ cmd[7] = 0;
+ data[0] = e;
+ data[1] = f;
+ data[2] = g;
+ data[3] = h;
+ data[4] = i;
+ data[5] = j;
+ data[6] = k;
+ data[7] = l;
+
+ retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
+ if (retval)
+ DBG("%x - failed\n", command);
+
+ return retval;
+}
+
+/**********************************************************************
+ *
+ * Colorspace conversion
+ *
+ **********************************************************************/
+#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
+
+static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt,
+ int linesize, int mmap_kludge)
+{
+ int y, u, v, r, g, b, y1;
+
+ /* Odd lines use the same u and v as the previous line.
+ * Because of compression, it is necessary to get this
+ * information from the decoded image. */
+ switch(out_fmt) {
+ case VIDEO_PALETTE_RGB555:
+ y = (*yuv++ - 16) * 76310;
+ y1 = (*yuv - 16) * 76310;
+ r = ((*(rgb+1-linesize)) & 0x7c) << 1;
+ g = ((*(rgb-linesize)) & 0xe0) >> 4 |
+ ((*(rgb+1-linesize)) & 0x03) << 6;
+ b = ((*(rgb-linesize)) & 0x1f) << 3;
+ u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
+ v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
+ r = 104635 * v;
+ g = -25690 * u - 53294 * v;
+ b = 132278 * u;
+ *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
+ *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
+ *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
+ *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
+ return 4;
+ case VIDEO_PALETTE_RGB565:
+ y = (*yuv++ - 16) * 76310;
+ y1 = (*yuv - 16) * 76310;
+ r = (*(rgb+1-linesize)) & 0xf8;
+ g = ((*(rgb-linesize)) & 0xe0) >> 3 |
+ ((*(rgb+1-linesize)) & 0x07) << 5;
+ b = ((*(rgb-linesize)) & 0x1f) << 3;
+ u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
+ v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
+ r = 104635 * v;
+ g = -25690 * u - 53294 * v;
+ b = 132278 * u;
+ *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
+ *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
+ *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
+ *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
+ return 4;
+ break;
+ case VIDEO_PALETTE_RGB24:
+ case VIDEO_PALETTE_RGB32:
+ y = (*yuv++ - 16) * 76310;
+ y1 = (*yuv - 16) * 76310;
+ if (mmap_kludge) {
+ r = *(rgb+2-linesize);
+ g = *(rgb+1-linesize);
+ b = *(rgb-linesize);
+ } else {
+ r = *(rgb-linesize);
+ g = *(rgb+1-linesize);
+ b = *(rgb+2-linesize);
+ }
+ u = (-53294 * r - 104635 * g + 157929 * b) / 5756495;
+ v = (157968 * r - 132278 * g - 25690 * b) / 5366159;
+ r = 104635 * v;
+ g = -25690 * u + -53294 * v;
+ b = 132278 * u;
+ if (mmap_kludge) {
+ *rgb++ = LIMIT(b+y);
+ *rgb++ = LIMIT(g+y);
+ *rgb++ = LIMIT(r+y);
+ if(out_fmt == VIDEO_PALETTE_RGB32)
+ rgb++;
+ *rgb++ = LIMIT(b+y1);
+ *rgb++ = LIMIT(g+y1);
+ *rgb = LIMIT(r+y1);
+ } else {
+ *rgb++ = LIMIT(r+y);
+ *rgb++ = LIMIT(g+y);
+ *rgb++ = LIMIT(b+y);
+ if(out_fmt == VIDEO_PALETTE_RGB32)
+ rgb++;
+ *rgb++ = LIMIT(r+y1);
+ *rgb++ = LIMIT(g+y1);
+ *rgb = LIMIT(b+y1);
+ }
+ if(out_fmt == VIDEO_PALETTE_RGB32)
+ return 8;
+ return 6;
+ case VIDEO_PALETTE_YUV422:
+ case VIDEO_PALETTE_YUYV:
+ y = *yuv++;
+ u = *(rgb+1-linesize);
+ y1 = *yuv;
+ v = *(rgb+3-linesize);
+ *rgb++ = y;
+ *rgb++ = u;
+ *rgb++ = y1;
+ *rgb = v;
+ return 4;
+ case VIDEO_PALETTE_UYVY:
+ u = *(rgb-linesize);
+ y = *yuv++;
+ v = *(rgb+2-linesize);
+ y1 = *yuv;
+ *rgb++ = u;
+ *rgb++ = y;
+ *rgb++ = v;
+ *rgb = y1;
+ return 4;
+ case VIDEO_PALETTE_GREY:
+ *rgb++ = *yuv++;
+ *rgb = *yuv;
+ return 2;
+ default:
+ DBG("Empty: %d\n", out_fmt);
+ return 0;
+ }
+}
+
+
+static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt,
+ int in_uyvy, int mmap_kludge)
+{
+ int y, u, v, r, g, b, y1;
+
+ switch(out_fmt) {
+ case VIDEO_PALETTE_RGB555:
+ case VIDEO_PALETTE_RGB565:
+ case VIDEO_PALETTE_RGB24:
+ case VIDEO_PALETTE_RGB32:
+ if (in_uyvy) {
+ u = *yuv++ - 128;
+ y = (*yuv++ - 16) * 76310;
+ v = *yuv++ - 128;
+ y1 = (*yuv - 16) * 76310;
+ } else {
+ y = (*yuv++ - 16) * 76310;
+ u = *yuv++ - 128;
+ y1 = (*yuv++ - 16) * 76310;
+ v = *yuv - 128;
+ }
+ r = 104635 * v;
+ g = -25690 * u + -53294 * v;
+ b = 132278 * u;
+ break;
+ default:
+ y = *yuv++;
+ u = *yuv++;
+ y1 = *yuv++;
+ v = *yuv;
+ /* Just to avoid compiler warnings */
+ r = 0;
+ g = 0;
+ b = 0;
+ break;
+ }
+ switch(out_fmt) {
+ case VIDEO_PALETTE_RGB555:
+ *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
+ *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
+ *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
+ *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
+ return 4;
+ case VIDEO_PALETTE_RGB565:
+ *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
+ *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
+ *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
+ *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
+ return 4;
+ case VIDEO_PALETTE_RGB24:
+ if (mmap_kludge) {
+ *rgb++ = LIMIT(b+y);
+ *rgb++ = LIMIT(g+y);
+ *rgb++ = LIMIT(r+y);
+ *rgb++ = LIMIT(b+y1);
+ *rgb++ = LIMIT(g+y1);
+ *rgb = LIMIT(r+y1);
+ } else {
+ *rgb++ = LIMIT(r+y);
+ *rgb++ = LIMIT(g+y);
+ *rgb++ = LIMIT(b+y);
+ *rgb++ = LIMIT(r+y1);
+ *rgb++ = LIMIT(g+y1);
+ *rgb = LIMIT(b+y1);
+ }
+ return 6;
+ case VIDEO_PALETTE_RGB32:
+ if (mmap_kludge) {
+ *rgb++ = LIMIT(b+y);
+ *rgb++ = LIMIT(g+y);
+ *rgb++ = LIMIT(r+y);
+ rgb++;
+ *rgb++ = LIMIT(b+y1);
+ *rgb++ = LIMIT(g+y1);
+ *rgb = LIMIT(r+y1);
+ } else {
+ *rgb++ = LIMIT(r+y);
+ *rgb++ = LIMIT(g+y);
+ *rgb++ = LIMIT(b+y);
+ rgb++;
+ *rgb++ = LIMIT(r+y1);
+ *rgb++ = LIMIT(g+y1);
+ *rgb = LIMIT(b+y1);
+ }
+ return 8;
+ case VIDEO_PALETTE_GREY:
+ *rgb++ = y;
+ *rgb = y1;
+ return 2;
+ case VIDEO_PALETTE_YUV422:
+ case VIDEO_PALETTE_YUYV:
+ *rgb++ = y;
+ *rgb++ = u;
+ *rgb++ = y1;
+ *rgb = v;
+ return 4;
+ case VIDEO_PALETTE_UYVY:
+ *rgb++ = u;
+ *rgb++ = y;
+ *rgb++ = v;
+ *rgb = y1;
+ return 4;
+ default:
+ DBG("Empty: %d\n", out_fmt);
+ return 0;
+ }
+}
+
+static int skipcount(int count, int fmt)
+{
+ switch(fmt) {
+ case VIDEO_PALETTE_GREY:
+ return count;
+ case VIDEO_PALETTE_RGB555:
+ case VIDEO_PALETTE_RGB565:
+ case VIDEO_PALETTE_YUV422:
+ case VIDEO_PALETTE_YUYV:
+ case VIDEO_PALETTE_UYVY:
+ return 2*count;
+ case VIDEO_PALETTE_RGB24:
+ return 3*count;
+ case VIDEO_PALETTE_RGB32:
+ return 4*count;
+ default:
+ return 0;
+ }
+}
+
+static int parse_picture(struct cam_data *cam, int size)
+{
+ u8 *obuf, *ibuf, *end_obuf;
+ int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt;
+ int rows, cols, linesize, subsample_422;
+
+ /* make sure params don't change while we are decoding */
+ mutex_lock(&cam->param_lock);
+
+ obuf = cam->decompressed_frame.data;
+ end_obuf = obuf+CPIA_MAX_FRAME_SIZE;
+ ibuf = cam->raw_image;
+ origsize = size;
+ out_fmt = cam->vp.palette;
+
+ if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) {
+ LOG("header not found\n");
+ mutex_unlock(&cam->param_lock);
+ return -1;
+ }
+
+ if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) {
+ LOG("wrong video size\n");
+ mutex_unlock(&cam->param_lock);
+ return -1;
+ }
+
+ if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) {
+ LOG("illegal subtype %d\n",ibuf[17]);
+ mutex_unlock(&cam->param_lock);
+ return -1;
+ }
+ subsample_422 = ibuf[17] == SUBSAMPLE_422;
+
+ if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) {
+ LOG("illegal yuvorder %d\n",ibuf[18]);
+ mutex_unlock(&cam->param_lock);
+ return -1;
+ }
+ in_uyvy = ibuf[18] == YUVORDER_UYVY;
+
+ if ((ibuf[24] != cam->params.roi.colStart) ||
+ (ibuf[25] != cam->params.roi.colEnd) ||
+ (ibuf[26] != cam->params.roi.rowStart) ||
+ (ibuf[27] != cam->params.roi.rowEnd)) {
+ LOG("ROI mismatch\n");
+ mutex_unlock(&cam->param_lock);
+ return -1;
+ }
+ cols = 8*(ibuf[25] - ibuf[24]);
+ rows = 4*(ibuf[27] - ibuf[26]);
+
+
+ if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) {
+ LOG("illegal compression %d\n",ibuf[28]);
+ mutex_unlock(&cam->param_lock);
+ return -1;
+ }
+ compressed = (ibuf[28] == COMPRESSED);
+
+ if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) {
+ LOG("illegal decimation %d\n",ibuf[29]);
+ mutex_unlock(&cam->param_lock);
+ return -1;
+ }
+ decimation = (ibuf[29] == DECIMATION_ENAB);
+
+ cam->params.yuvThreshold.yThreshold = ibuf[30];
+ cam->params.yuvThreshold.uvThreshold = ibuf[31];
+ cam->params.status.systemState = ibuf[32];
+ cam->params.status.grabState = ibuf[33];
+ cam->params.status.streamState = ibuf[34];
+ cam->params.status.fatalError = ibuf[35];
+ cam->params.status.cmdError = ibuf[36];
+ cam->params.status.debugFlags = ibuf[37];
+ cam->params.status.vpStatus = ibuf[38];
+ cam->params.status.errorCode = ibuf[39];
+ cam->fps = ibuf[41];
+ mutex_unlock(&cam->param_lock);
+
+ linesize = skipcount(cols, out_fmt);
+ ibuf += FRAME_HEADER_SIZE;
+ size -= FRAME_HEADER_SIZE;
+ ll = ibuf[0] | (ibuf[1] << 8);
+ ibuf += 2;
+ even_line = 1;
+
+ while (size > 0) {
+ size -= (ll+2);
+ if (size < 0) {
+ LOG("Insufficient data in buffer\n");
+ return -1;
+ }
+
+ while (ll > 1) {
+ if (!compressed || (compressed && !(*ibuf & 1))) {
+ if(subsample_422 || even_line) {
+ obuf += yuvconvert(ibuf, obuf, out_fmt,
+ in_uyvy, cam->mmap_kludge);
+ ibuf += 4;
+ ll -= 4;
+ } else {
+ /* SUBSAMPLE_420 on an odd line */
+ obuf += convert420(ibuf, obuf,
+ out_fmt, linesize,
+ cam->mmap_kludge);
+ ibuf += 2;
+ ll -= 2;
+ }
+ } else {
+ /*skip compressed interval from previous frame*/
+ obuf += skipcount(*ibuf >> 1, out_fmt);
+ if (obuf > end_obuf) {
+ LOG("Insufficient buffer size\n");
+ return -1;
+ }
+ ++ibuf;
+ ll--;
+ }
+ }
+ if (ll == 1) {
+ if (*ibuf != EOL) {
+ DBG("EOL not found giving up after %d/%d"
+ " bytes\n", origsize-size, origsize);
+ return -1;
+ }
+
+ ++ibuf; /* skip over EOL */
+
+ if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) &&
+ (ibuf[2] == EOI) && (ibuf[3] == EOI)) {
+ size -= 4;
+ break;
+ }
+
+ if(decimation) {
+ /* skip the odd lines for now */
+ obuf += linesize;
+ }
+
+ if (size > 1) {
+ ll = ibuf[0] | (ibuf[1] << 8);
+ ibuf += 2; /* skip over line length */
+ }
+ if(!decimation)
+ even_line = !even_line;
+ } else {
+ LOG("line length was not 1 but %d after %d/%d bytes\n",
+ ll, origsize-size, origsize);
+ return -1;
+ }
+ }
+
+ if(decimation) {
+ /* interpolate odd rows */
+ int i, j;
+ u8 *prev, *next;
+ prev = cam->decompressed_frame.data;
+ obuf = prev+linesize;
+ next = obuf+linesize;
+ for(i=1; i<rows-1; i+=2) {
+ for(j=0; j<linesize; ++j) {
+ *obuf++ = ((int)*prev++ + *next++) / 2;
+ }
+ prev += linesize;
+ obuf += linesize;
+ next += linesize;
+ }
+ /* last row is odd, just copy previous row */
+ memcpy(obuf, prev, linesize);
+ }
+
+ cam->decompressed_frame.count = obuf-cam->decompressed_frame.data;
+
+ return cam->decompressed_frame.count;
+}
+
+/* InitStreamCap wrapper to select correct start line */
+static inline int init_stream_cap(struct cam_data *cam)
+{
+ return do_command(cam, CPIA_COMMAND_InitStreamCap,
+ 0, cam->params.streamStartLine, 0, 0);
+}
+
+
+/* find_over_exposure
+ * Finds a suitable value of OverExposure for use with SetFlickerCtrl
+ * Some calculation is required because this value changes with the brightness
+ * set with SetColourParameters
+ *
+ * Parameters: Brightness - last brightness value set with SetColourParameters
+ *
+ * Returns: OverExposure value to use with SetFlickerCtrl
+ */
+#define FLICKER_MAX_EXPOSURE 250
+#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146
+#define FLICKER_BRIGHTNESS_CONSTANT 59
+static int find_over_exposure(int brightness)
+{
+ int MaxAllowableOverExposure, OverExposure;
+
+ MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
+ FLICKER_BRIGHTNESS_CONSTANT;
+
+ if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) {
+ OverExposure = MaxAllowableOverExposure;
+ } else {
+ OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
+ }
+
+ return OverExposure;
+}
+#undef FLICKER_MAX_EXPOSURE
+#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
+#undef FLICKER_BRIGHTNESS_CONSTANT
+
+/* update various camera modes and settings */
+static void dispatch_commands(struct cam_data *cam)
+{
+ mutex_lock(&cam->param_lock);
+ if (cam->cmd_queue==COMMAND_NONE) {
+ mutex_unlock(&cam->param_lock);
+ return;
+ }
+ DEB_BYTE(cam->cmd_queue);
+ DEB_BYTE(cam->cmd_queue>>8);
+ if (cam->cmd_queue & COMMAND_SETFORMAT) {
+ do_command(cam, CPIA_COMMAND_SetFormat,
+ cam->params.format.videoSize,
+ cam->params.format.subSample,
+ cam->params.format.yuvOrder, 0);
+ do_command(cam, CPIA_COMMAND_SetROI,
+ cam->params.roi.colStart, cam->params.roi.colEnd,
+ cam->params.roi.rowStart, cam->params.roi.rowEnd);
+ cam->first_frame = 1;
+ }
+
+ if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS)
+ do_command(cam, CPIA_COMMAND_SetColourParams,
+ cam->params.colourParams.brightness,
+ cam->params.colourParams.contrast,
+ cam->params.colourParams.saturation, 0);
+
+ if (cam->cmd_queue & COMMAND_SETAPCOR)
+ do_command(cam, CPIA_COMMAND_SetApcor,
+ cam->params.apcor.gain1,
+ cam->params.apcor.gain2,
+ cam->params.apcor.gain4,
+ cam->params.apcor.gain8);
+
+ if (cam->cmd_queue & COMMAND_SETVLOFFSET)
+ do_command(cam, CPIA_COMMAND_SetVLOffset,
+ cam->params.vlOffset.gain1,
+ cam->params.vlOffset.gain2,
+ cam->params.vlOffset.gain4,
+ cam->params.vlOffset.gain8);
+
+ if (cam->cmd_queue & COMMAND_SETEXPOSURE) {
+ do_command_extended(cam, CPIA_COMMAND_SetExposure,
+ cam->params.exposure.gainMode,
+ 1,
+ cam->params.exposure.compMode,
+ cam->params.exposure.centreWeight,
+ cam->params.exposure.gain,
+ cam->params.exposure.fineExp,
+ cam->params.exposure.coarseExpLo,
+ cam->params.exposure.coarseExpHi,
+ cam->params.exposure.redComp,
+ cam->params.exposure.green1Comp,
+ cam->params.exposure.green2Comp,
+ cam->params.exposure.blueComp);
+ if(cam->params.exposure.expMode != 1) {
+ do_command_extended(cam, CPIA_COMMAND_SetExposure,
+ 0,
+ cam->params.exposure.expMode,
+ 0, 0,
+ cam->params.exposure.gain,
+ cam->params.exposure.fineExp,
+ cam->params.exposure.coarseExpLo,
+ cam->params.exposure.coarseExpHi,
+ 0, 0, 0, 0);
+ }
+ }
+
+ if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) {
+ if (cam->params.colourBalance.balanceMode == 1) {
+ do_command(cam, CPIA_COMMAND_SetColourBalance,
+ 1,
+ cam->params.colourBalance.redGain,
+ cam->params.colourBalance.greenGain,
+ cam->params.colourBalance.blueGain);
+ do_command(cam, CPIA_COMMAND_SetColourBalance,
+ 3, 0, 0, 0);
+ }
+ if (cam->params.colourBalance.balanceMode == 2) {
+ do_command(cam, CPIA_COMMAND_SetColourBalance,
+ 2, 0, 0, 0);
+ }
+ if (cam->params.colourBalance.balanceMode == 3) {
+ do_command(cam, CPIA_COMMAND_SetColourBalance,
+ 3, 0, 0, 0);
+ }
+ }
+
+ if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET)
+ do_command(cam, CPIA_COMMAND_SetCompressionTarget,
+ cam->params.compressionTarget.frTargeting,
+ cam->params.compressionTarget.targetFR,
+ cam->params.compressionTarget.targetQ, 0);
+
+ if (cam->cmd_queue & COMMAND_SETYUVTHRESH)
+ do_command(cam, CPIA_COMMAND_SetYUVThresh,
+ cam->params.yuvThreshold.yThreshold,
+ cam->params.yuvThreshold.uvThreshold, 0, 0);
+
+ if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS)
+ do_command_extended(cam, CPIA_COMMAND_SetCompressionParams,
+ 0, 0, 0, 0,
+ cam->params.compressionParams.hysteresis,
+ cam->params.compressionParams.threshMax,
+ cam->params.compressionParams.smallStep,
+ cam->params.compressionParams.largeStep,
+ cam->params.compressionParams.decimationHysteresis,
+ cam->params.compressionParams.frDiffStepThresh,
+ cam->params.compressionParams.qDiffStepThresh,
+ cam->params.compressionParams.decimationThreshMod);
+
+ if (cam->cmd_queue & COMMAND_SETCOMPRESSION)
+ do_command(cam, CPIA_COMMAND_SetCompression,
+ cam->params.compression.mode,
+ cam->params.compression.decimation, 0, 0);
+
+ if (cam->cmd_queue & COMMAND_SETSENSORFPS)
+ do_command(cam, CPIA_COMMAND_SetSensorFPS,
+ cam->params.sensorFps.divisor,
+ cam->params.sensorFps.baserate, 0, 0);
+
+ if (cam->cmd_queue & COMMAND_SETFLICKERCTRL)
+ do_command(cam, CPIA_COMMAND_SetFlickerCtrl,
+ cam->params.flickerControl.flickerMode,
+ cam->params.flickerControl.coarseJump,
+ abs(cam->params.flickerControl.allowableOverExposure),
+ 0);
+
+ if (cam->cmd_queue & COMMAND_SETECPTIMING)
+ do_command(cam, CPIA_COMMAND_SetECPTiming,
+ cam->params.ecpTiming, 0, 0, 0);
+
+ if (cam->cmd_queue & COMMAND_PAUSE)
+ do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
+
+ if (cam->cmd_queue & COMMAND_RESUME)
+ init_stream_cap(cam);
+
+ if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected)
+ {
+ int p1 = (cam->params.qx3.bottomlight == 0) << 1;
+ int p2 = (cam->params.qx3.toplight == 0) << 3;
+ do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0);
+ do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0);
+ }
+
+ cam->cmd_queue = COMMAND_NONE;
+ mutex_unlock(&cam->param_lock);
+ return;
+}
+
+
+
+static void set_flicker(struct cam_params *params, volatile u32 *command_flags,
+ int on)
+{
+ /* Everything in here is from the Windows driver */
+#define FIRMWARE_VERSION(x,y) (params->version.firmwareVersion == (x) && \
+ params->version.firmwareRevision == (y))
+/* define for compgain calculation */
+#if 0
+#define COMPGAIN(base, curexp, newexp) \
+ (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
+#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
+ (u16)((float)curexp * (float)(u8)(curcomp + 128) / (float)(u8)(basecomp - 128))
+#else
+ /* equivalent functions without floating point math */
+#define COMPGAIN(base, curexp, newexp) \
+ (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2* newexp)) )
+#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
+ (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
+#endif
+
+
+ int currentexp = params->exposure.coarseExpLo +
+ params->exposure.coarseExpHi*256;
+ int startexp;
+ if (on) {
+ int cj = params->flickerControl.coarseJump;
+ params->flickerControl.flickerMode = 1;
+ params->flickerControl.disabled = 0;
+ if(params->exposure.expMode != 2)
+ *command_flags |= COMMAND_SETEXPOSURE;
+ params->exposure.expMode = 2;
+ currentexp = currentexp << params->exposure.gain;
+ params->exposure.gain = 0;
+ /* round down current exposure to nearest value */
+ startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
+ if(startexp < 1)
+ startexp = 1;
+ startexp = (startexp * cj) - 1;
+ if(FIRMWARE_VERSION(1,2))
+ while(startexp > MAX_EXP_102)
+ startexp -= cj;
+ else
+ while(startexp > MAX_EXP)
+ startexp -= cj;
+ params->exposure.coarseExpLo = startexp & 0xff;
+ params->exposure.coarseExpHi = startexp >> 8;
+ if (currentexp > startexp) {
+ if (currentexp > (2 * startexp))
+ currentexp = 2 * startexp;
+ params->exposure.redComp = COMPGAIN (COMP_RED, currentexp, startexp);
+ params->exposure.green1Comp = COMPGAIN (COMP_GREEN1, currentexp, startexp);
+ params->exposure.green2Comp = COMPGAIN (COMP_GREEN2, currentexp, startexp);
+ params->exposure.blueComp = COMPGAIN (COMP_BLUE, currentexp, startexp);
+ } else {
+ params->exposure.redComp = COMP_RED;
+ params->exposure.green1Comp = COMP_GREEN1;
+ params->exposure.green2Comp = COMP_GREEN2;
+ params->exposure.blueComp = COMP_BLUE;
+ }
+ if(FIRMWARE_VERSION(1,2))
+ params->exposure.compMode = 0;
+ else
+ params->exposure.compMode = 1;
+
+ params->apcor.gain1 = 0x18;
+ params->apcor.gain2 = 0x18;
+ params->apcor.gain4 = 0x16;
+ params->apcor.gain8 = 0x14;
+ *command_flags |= COMMAND_SETAPCOR;
+ } else {
+ params->flickerControl.flickerMode = 0;
+ params->flickerControl.disabled = 1;
+ /* Coarse = average of equivalent coarse for each comp channel */
+ startexp = EXP_FROM_COMP(COMP_RED, params->exposure.redComp, currentexp);
+ startexp += EXP_FROM_COMP(COMP_GREEN1, params->exposure.green1Comp, currentexp);
+ startexp += EXP_FROM_COMP(COMP_GREEN2, params->exposure.green2Comp, currentexp);
+ startexp += EXP_FROM_COMP(COMP_BLUE, params->exposure.blueComp, currentexp);
+ startexp = startexp >> 2;
+ while(startexp > MAX_EXP &&
+ params->exposure.gain < params->exposure.gainMode-1) {
+ startexp = startexp >> 1;
+ ++params->exposure.gain;
+ }
+ if(FIRMWARE_VERSION(1,2) && startexp > MAX_EXP_102)
+ startexp = MAX_EXP_102;
+ if(startexp > MAX_EXP)
+ startexp = MAX_EXP;
+ params->exposure.coarseExpLo = startexp&0xff;
+ params->exposure.coarseExpHi = startexp >> 8;
+ params->exposure.redComp = COMP_RED;
+ params->exposure.green1Comp = COMP_GREEN1;
+ params->exposure.green2Comp = COMP_GREEN2;
+ params->exposure.blueComp = COMP_BLUE;
+ params->exposure.compMode = 1;
+ *command_flags |= COMMAND_SETEXPOSURE;
+ params->apcor.gain1 = 0x18;
+ params->apcor.gain2 = 0x16;
+ params->apcor.gain4 = 0x24;
+ params->apcor.gain8 = 0x34;
+ *command_flags |= COMMAND_SETAPCOR;
+ }
+ params->vlOffset.gain1 = 20;
+ params->vlOffset.gain2 = 24;
+ params->vlOffset.gain4 = 26;
+ params->vlOffset.gain8 = 26;
+ *command_flags |= COMMAND_SETVLOFFSET;
+#undef FIRMWARE_VERSION
+#undef EXP_FROM_COMP
+#undef COMPGAIN
+}
+
+#define FIRMWARE_VERSION(x,y) (cam->params.version.firmwareVersion == (x) && \
+ cam->params.version.firmwareRevision == (y))
+/* monitor the exposure and adjust the sensor frame rate if needed */
+static void monitor_exposure(struct cam_data *cam)
+{
+ u8 exp_acc, bcomp, gain, coarseL, cmd[8], data[8];
+ int retval, light_exp, dark_exp, very_dark_exp;
+ int old_exposure, new_exposure, framerate;
+
+ /* get necessary stats and register settings from camera */
+ /* do_command can't handle this, so do it ourselves */
+ cmd[0] = CPIA_COMMAND_ReadVPRegs>>8;
+ cmd[1] = CPIA_COMMAND_ReadVPRegs&0xff;
+ cmd[2] = 30;
+ cmd[3] = 4;
+ cmd[4] = 9;
+ cmd[5] = 8;
+ cmd[6] = 8;
+ cmd[7] = 0;
+ retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
+ if (retval) {
+ LOG("ReadVPRegs(30,4,9,8) - failed, retval=%d\n",
+ retval);
+ return;
+ }
+ exp_acc = data[0];
+ bcomp = data[1];
+ gain = data[2];
+ coarseL = data[3];
+
+ mutex_lock(&cam->param_lock);
+ light_exp = cam->params.colourParams.brightness +
+ TC - 50 + EXP_ACC_LIGHT;
+ if(light_exp > 255)
+ light_exp = 255;
+ dark_exp = cam->params.colourParams.brightness +
+ TC - 50 - EXP_ACC_DARK;
+ if(dark_exp < 0)
+ dark_exp = 0;
+ very_dark_exp = dark_exp/2;
+
+ old_exposure = cam->params.exposure.coarseExpHi * 256 +
+ cam->params.exposure.coarseExpLo;
+
+ if(!cam->params.flickerControl.disabled) {
+ /* Flicker control on */
+ int max_comp = FIRMWARE_VERSION(1,2) ? MAX_COMP : HIGH_COMP_102;
+ bcomp += 128; /* decode */
+ if(bcomp >= max_comp && exp_acc < dark_exp) {
+ /* dark */
+ if(exp_acc < very_dark_exp) {
+ /* very dark */
+ if(cam->exposure_status == EXPOSURE_VERY_DARK)
+ ++cam->exposure_count;
+ else {
+ cam->exposure_status = EXPOSURE_VERY_DARK;
+ cam->exposure_count = 1;
+ }
+ } else {
+ /* just dark */
+ if(cam->exposure_status == EXPOSURE_DARK)
+ ++cam->exposure_count;
+ else {
+ cam->exposure_status = EXPOSURE_DARK;
+ cam->exposure_count = 1;
+ }
+ }
+ } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
+ /* light */
+ if(old_exposure <= VERY_LOW_EXP) {
+ /* very light */
+ if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
+ ++cam->exposure_count;
+ else {
+ cam->exposure_status = EXPOSURE_VERY_LIGHT;
+ cam->exposure_count = 1;
+ }
+ } else {
+ /* just light */
+ if(cam->exposure_status == EXPOSURE_LIGHT)
+ ++cam->exposure_count;
+ else {
+ cam->exposure_status = EXPOSURE_LIGHT;
+ cam->exposure_count = 1;
+ }
+ }
+ } else {
+ /* not dark or light */
+ cam->exposure_status = EXPOSURE_NORMAL;
+ }
+ } else {
+ /* Flicker control off */
+ if(old_exposure >= MAX_EXP && exp_acc < dark_exp) {
+ /* dark */
+ if(exp_acc < very_dark_exp) {
+ /* very dark */
+ if(cam->exposure_status == EXPOSURE_VERY_DARK)
+ ++cam->exposure_count;
+ else {
+ cam->exposure_status = EXPOSURE_VERY_DARK;
+ cam->exposure_count = 1;
+ }
+ } else {
+ /* just dark */
+ if(cam->exposure_status == EXPOSURE_DARK)
+ ++cam->exposure_count;
+ else {
+ cam->exposure_status = EXPOSURE_DARK;
+ cam->exposure_count = 1;
+ }
+ }
+ } else if(old_exposure <= LOW_EXP || exp_acc > light_exp) {
+ /* light */
+ if(old_exposure <= VERY_LOW_EXP) {
+ /* very light */
+ if(cam->exposure_status == EXPOSURE_VERY_LIGHT)
+ ++cam->exposure_count;
+ else {
+ cam->exposure_status = EXPOSURE_VERY_LIGHT;
+ cam->exposure_count = 1;
+ }
+ } else {
+ /* just light */
+ if(cam->exposure_status == EXPOSURE_LIGHT)
+ ++cam->exposure_count;
+ else {
+ cam->exposure_status = EXPOSURE_LIGHT;
+ cam->exposure_count = 1;
+ }
+ }
+ } else {
+ /* not dark or light */
+ cam->exposure_status = EXPOSURE_NORMAL;
+ }
+ }
+
+ framerate = cam->fps;
+ if(framerate > 30 || framerate < 1)
+ framerate = 1;
+
+ if(!cam->params.flickerControl.disabled) {
+ /* Flicker control on */
+ if((cam->exposure_status == EXPOSURE_VERY_DARK ||
+ cam->exposure_status == EXPOSURE_DARK) &&
+ cam->exposure_count >= DARK_TIME*framerate &&
+ cam->params.sensorFps.divisor < 3) {
+
+ /* dark for too long */
+ ++cam->params.sensorFps.divisor;
+ cam->cmd_queue |= COMMAND_SETSENSORFPS;
+
+ cam->params.flickerControl.coarseJump =
+ flicker_jumps[cam->mainsFreq]
+ [cam->params.sensorFps.baserate]
+ [cam->params.sensorFps.divisor];
+ cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
+
+ new_exposure = cam->params.flickerControl.coarseJump-1;
+ while(new_exposure < old_exposure/2)
+ new_exposure += cam->params.flickerControl.coarseJump;
+ cam->params.exposure.coarseExpLo = new_exposure & 0xff;
+ cam->params.exposure.coarseExpHi = new_exposure >> 8;
+ cam->cmd_queue |= COMMAND_SETEXPOSURE;
+ cam->exposure_status = EXPOSURE_NORMAL;
+ LOG("Automatically decreasing sensor_fps\n");
+
+ } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
+ cam->exposure_status == EXPOSURE_LIGHT) &&
+ cam->exposure_count >= LIGHT_TIME*framerate &&
+ cam->params.sensorFps.divisor > 0) {
+
+ /* light for too long */
+ int max_exp = FIRMWARE_VERSION(1,2) ? MAX_EXP_102 : MAX_EXP ;
+
+ --cam->params.sensorFps.divisor;
+ cam->cmd_queue |= COMMAND_SETSENSORFPS;
+
+ cam->params.flickerControl.coarseJump =
+ flicker_jumps[cam->mainsFreq]
+ [cam->params.sensorFps.baserate]
+ [cam->params.sensorFps.divisor];
+ cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
+
+ new_exposure = cam->params.flickerControl.coarseJump-1;
+ while(new_exposure < 2*old_exposure &&
+ new_exposure+
+ cam->params.flickerControl.coarseJump < max_exp)
+ new_exposure += cam->params.flickerControl.coarseJump;
+ cam->params.exposure.coarseExpLo = new_exposure & 0xff;
+ cam->params.exposure.coarseExpHi = new_exposure >> 8;
+ cam->cmd_queue |= COMMAND_SETEXPOSURE;
+ cam->exposure_status = EXPOSURE_NORMAL;
+ LOG("Automatically increasing sensor_fps\n");
+ }
+ } else {
+ /* Flicker control off */
+ if((cam->exposure_status == EXPOSURE_VERY_DARK ||
+ cam->exposure_status == EXPOSURE_DARK) &&
+ cam->exposure_count >= DARK_TIME*framerate &&
+ cam->params.sensorFps.divisor < 3) {
+
+ /* dark for too long */
+ ++cam->params.sensorFps.divisor;
+ cam->cmd_queue |= COMMAND_SETSENSORFPS;
+
+ if(cam->params.exposure.gain > 0) {
+ --cam->params.exposure.gain;
+ cam->cmd_queue |= COMMAND_SETEXPOSURE;
+ }
+ cam->exposure_status = EXPOSURE_NORMAL;
+ LOG("Automatically decreasing sensor_fps\n");
+
+ } else if((cam->exposure_status == EXPOSURE_VERY_LIGHT ||
+ cam->exposure_status == EXPOSURE_LIGHT) &&
+ cam->exposure_count >= LIGHT_TIME*framerate &&
+ cam->params.sensorFps.divisor > 0) {
+
+ /* light for too long */
+ --cam->params.sensorFps.divisor;
+ cam->cmd_queue |= COMMAND_SETSENSORFPS;
+
+ if(cam->params.exposure.gain <
+ cam->params.exposure.gainMode-1) {
+ ++cam->params.exposure.gain;
+ cam->cmd_queue |= COMMAND_SETEXPOSURE;
+ }
+ cam->exposure_status = EXPOSURE_NORMAL;
+ LOG("Automatically increasing sensor_fps\n");
+ }
+ }
+ mutex_unlock(&cam->param_lock);
+}
+
+/*-----------------------------------------------------------------*/
+/* if flicker is switched off, this function switches it back on.It checks,
+ however, that conditions are suitable before restarting it.
+ This should only be called for firmware version 1.2.
+
+ It also adjust the colour balance when an exposure step is detected - as
+ long as flicker is running
+*/
+static void restart_flicker(struct cam_data *cam)
+{
+ int cam_exposure, old_exp;
+ if(!FIRMWARE_VERSION(1,2))
+ return;
+ mutex_lock(&cam->param_lock);
+ if(cam->params.flickerControl.flickerMode == 0 ||
+ cam->raw_image[39] == 0) {
+ mutex_unlock(&cam->param_lock);
+ return;
+ }
+ cam_exposure = cam->raw_image[39]*2;
+ old_exp = cam->params.exposure.coarseExpLo +
+ cam->params.exposure.coarseExpHi*256;
+ /*
+ see how far away camera exposure is from a valid
+ flicker exposure value
+ */
+ cam_exposure %= cam->params.flickerControl.coarseJump;
+ if(!cam->params.flickerControl.disabled &&
+ cam_exposure <= cam->params.flickerControl.coarseJump - 3) {
+ /* Flicker control auto-disabled */
+ cam->params.flickerControl.disabled = 1;
+ }
+
+ if(cam->params.flickerControl.disabled &&
+ cam->params.flickerControl.flickerMode &&
+ old_exp > cam->params.flickerControl.coarseJump +
+ ROUND_UP_EXP_FOR_FLICKER) {
+ /* exposure is now high enough to switch
+ flicker control back on */
+ set_flicker(&cam->params, &cam->cmd_queue, 1);
+ if((cam->cmd_queue & COMMAND_SETEXPOSURE) &&
+ cam->params.exposure.expMode == 2)
+ cam->exposure_status = EXPOSURE_NORMAL;
+
+ }
+ mutex_unlock(&cam->param_lock);
+}
+#undef FIRMWARE_VERSION
+
+static int clear_stall(struct cam_data *cam)
+{
+ /* FIXME: Does this actually work? */
+ LOG("Clearing stall\n");
+
+ cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0);
+ do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
+ return cam->params.status.streamState != STREAM_PAUSED;
+}
+
+/* kernel thread function to read image from camera */
+static int fetch_frame(void *data)
+{
+ int image_size, retry;
+ struct cam_data *cam = (struct cam_data *)data;
+ unsigned long oldjif, rate, diff;
+
+ /* Allow up to two bad images in a row to be read and
+ * ignored before an error is reported */
+ for (retry = 0; retry < 3; ++retry) {
+ if (retry)
+ DBG("retry=%d\n", retry);
+
+ if (!cam->ops)
+ continue;
+
+ /* load first frame always uncompressed */
+ if (cam->first_frame &&
+ cam->params.compression.mode != CPIA_COMPRESSION_NONE) {
+ do_command(cam, CPIA_COMMAND_SetCompression,
+ CPIA_COMPRESSION_NONE,
+ NO_DECIMATION, 0, 0);
+ /* Trial & error - Discarding a frame prevents the
+ first frame from having an error in the data. */
+ do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
+ }
+
+ /* init camera upload */
+ if (do_command(cam, CPIA_COMMAND_GrabFrame, 0,
+ cam->params.streamStartLine, 0, 0))
+ continue;
+
+ if (cam->ops->wait_for_stream_ready) {
+ /* loop until image ready */
+ int count = 0;
+ do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
+ while (cam->params.status.streamState != STREAM_READY) {
+ if(++count > READY_TIMEOUT)
+ break;
+ if(cam->params.status.streamState ==
+ STREAM_PAUSED) {
+ /* Bad news */
+ if(!clear_stall(cam))
+ return -EIO;
+ }
+
+ cond_resched();
+
+ /* sleep for 10 ms, hopefully ;) */
+ msleep_interruptible(10);
+ if (signal_pending(current))
+ return -EINTR;
+
+ do_command(cam, CPIA_COMMAND_GetCameraStatus,
+ 0, 0, 0, 0);
+ }
+ if(cam->params.status.streamState != STREAM_READY) {
+ continue;
+ }
+ }
+
+ cond_resched();
+
+ /* grab image from camera */
+ oldjif = jiffies;
+ image_size = cam->ops->streamRead(cam->lowlevel_data,
+ cam->raw_image, 0);
+ if (image_size <= 0) {
+ DBG("streamRead failed: %d\n", image_size);
+ continue;
+ }
+
+ rate = image_size * HZ / 1024;
+ diff = jiffies-oldjif;
+ cam->transfer_rate = diff==0 ? rate : rate/diff;
+ /* diff==0 ? unlikely but possible */
+
+ /* Switch flicker control back on if it got turned off */
+ restart_flicker(cam);
+
+ /* If AEC is enabled, monitor the exposure and
+ adjust the sensor frame rate if needed */
+ if(cam->params.exposure.expMode == 2)
+ monitor_exposure(cam);
+
+ /* camera idle now so dispatch queued commands */
+ dispatch_commands(cam);
+
+ /* Update our knowledge of the camera state */
+ do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
+ do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
+ do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
+
+ /* decompress and convert image to by copying it from
+ * raw_image to decompressed_frame
+ */
+
+ cond_resched();
+
+ cam->image_size = parse_picture(cam, image_size);
+ if (cam->image_size <= 0) {
+ DBG("parse_picture failed %d\n", cam->image_size);
+ if(cam->params.compression.mode !=
+ CPIA_COMPRESSION_NONE) {
+ /* Compression may not work right if we
+ had a bad frame, get the next one
+ uncompressed. */
+ cam->first_frame = 1;
+ do_command(cam, CPIA_COMMAND_SetGrabMode,
+ CPIA_GRAB_SINGLE, 0, 0, 0);
+ /* FIXME: Trial & error - need up to 70ms for
+ the grab mode change to complete ? */
+ msleep_interruptible(70);
+ if (signal_pending(current))
+ return -EINTR;
+ }
+ } else
+ break;
+ }
+
+ if (retry < 3) {
+ /* FIXME: this only works for double buffering */
+ if (cam->frame[cam->curframe].state == FRAME_READY) {
+ memcpy(cam->frame[cam->curframe].data,
+ cam->decompressed_frame.data,
+ cam->decompressed_frame.count);
+ cam->frame[cam->curframe].state = FRAME_DONE;
+ } else
+ cam->decompressed_frame.state = FRAME_DONE;
+
+ if (cam->first_frame) {
+ cam->first_frame = 0;
+ do_command(cam, CPIA_COMMAND_SetCompression,
+ cam->params.compression.mode,
+ cam->params.compression.decimation, 0, 0);
+
+ /* Switch from single-grab to continuous grab */
+ do_command(cam, CPIA_COMMAND_SetGrabMode,
+ CPIA_GRAB_CONTINUOUS, 0, 0, 0);
+ }
+ return 0;
+ }
+ return -EIO;
+}
+
+static int capture_frame(struct cam_data *cam, struct video_mmap *vm)
+{
+ if (!cam->frame_buf) {
+ /* we do lazy allocation */
+ int err;
+ if ((err = allocate_frame_buf(cam)))
+ return err;
+ }
+
+ cam->curframe = vm->frame;
+ cam->frame[cam->curframe].state = FRAME_READY;
+ return fetch_frame(cam);
+}
+
+static int goto_high_power(struct cam_data *cam)
+{
+ if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0))
+ return -EIO;
+ msleep_interruptible(40); /* windows driver does it too */
+ if(signal_pending(current))
+ return -EINTR;
+ if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
+ return -EIO;
+ if (cam->params.status.systemState == HI_POWER_STATE) {
+ DBG("camera now in HIGH power state\n");
+ return 0;
+ }
+ printstatus(cam);
+ return -EIO;
+}
+
+static int goto_low_power(struct cam_data *cam)
+{
+ if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0))
+ return -1;
+ if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
+ return -1;
+ if (cam->params.status.systemState == LO_POWER_STATE) {
+ DBG("camera now in LOW power state\n");
+ return 0;
+ }
+ printstatus(cam);
+ return -1;
+}
+
+static void save_camera_state(struct cam_data *cam)
+{
+ if(!(cam->cmd_queue & COMMAND_SETCOLOURBALANCE))
+ do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
+ if(!(cam->cmd_queue & COMMAND_SETEXPOSURE))
+ do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
+
+ DBG("%d/%d/%d/%d/%d/%d/%d/%d\n",
+ cam->params.exposure.gain,
+ cam->params.exposure.fineExp,
+ cam->params.exposure.coarseExpLo,
+ cam->params.exposure.coarseExpHi,
+ cam->params.exposure.redComp,
+ cam->params.exposure.green1Comp,
+ cam->params.exposure.green2Comp,
+ cam->params.exposure.blueComp);
+ DBG("%d/%d/%d\n",
+ cam->params.colourBalance.redGain,
+ cam->params.colourBalance.greenGain,
+ cam->params.colourBalance.blueGain);
+}
+
+static int set_camera_state(struct cam_data *cam)
+{
+ cam->cmd_queue = COMMAND_SETCOMPRESSION |
+ COMMAND_SETCOMPRESSIONTARGET |
+ COMMAND_SETCOLOURPARAMS |
+ COMMAND_SETFORMAT |
+ COMMAND_SETYUVTHRESH |
+ COMMAND_SETECPTIMING |
+ COMMAND_SETCOMPRESSIONPARAMS |
+ COMMAND_SETEXPOSURE |
+ COMMAND_SETCOLOURBALANCE |
+ COMMAND_SETSENSORFPS |
+ COMMAND_SETAPCOR |
+ COMMAND_SETFLICKERCTRL |
+ COMMAND_SETVLOFFSET;
+
+ do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_SINGLE,0,0,0);
+ dispatch_commands(cam);
+
+ /* Wait 6 frames for the sensor to get all settings and
+ AEC/ACB to settle */
+ msleep_interruptible(6*(cam->params.sensorFps.baserate ? 33 : 40) *
+ (1 << cam->params.sensorFps.divisor) + 10);
+
+ if(signal_pending(current))
+ return -EINTR;
+
+ save_camera_state(cam);
+
+ return 0;
+}
+
+static void get_version_information(struct cam_data *cam)
+{
+ /* GetCPIAVersion */
+ do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
+
+ /* GetPnPID */
+ do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
+}
+
+/* initialize camera */
+static int reset_camera(struct cam_data *cam)
+{
+ int err;
+ /* Start the camera in low power mode */
+ if (goto_low_power(cam)) {
+ if (cam->params.status.systemState != WARM_BOOT_STATE)
+ return -ENODEV;
+
+ /* FIXME: this is just dirty trial and error */
+ err = goto_high_power(cam);
+ if(err)
+ return err;
+ do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
+ if (goto_low_power(cam))
+ return -ENODEV;
+ }
+
+ /* procedure described in developer's guide p3-28 */
+
+ /* Check the firmware version. */
+ cam->params.version.firmwareVersion = 0;
+ get_version_information(cam);
+ if (cam->params.version.firmwareVersion != 1)
+ return -ENODEV;
+
+ /* A bug in firmware 1-02 limits gainMode to 2 */
+ if(cam->params.version.firmwareRevision <= 2 &&
+ cam->params.exposure.gainMode > 2) {
+ cam->params.exposure.gainMode = 2;
+ }
+
+ /* set QX3 detected flag */
+ cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 &&
+ cam->params.pnpID.product == 0x0001);
+
+ /* The fatal error checking should be done after
+ * the camera powers up (developer's guide p 3-38) */
+
+ /* Set streamState before transition to high power to avoid bug
+ * in firmware 1-02 */
+ do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0,
+ STREAM_NOT_READY, 0);
+
+ /* GotoHiPower */
+ err = goto_high_power(cam);
+ if (err)
+ return err;
+
+ /* Check the camera status */
+ if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
+ return -EIO;
+
+ if (cam->params.status.fatalError) {
+ DBG("fatal_error: %#04x\n",
+ cam->params.status.fatalError);
+ DBG("vp_status: %#04x\n",
+ cam->params.status.vpStatus);
+ if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) {
+ /* Fatal error in camera */
+ return -EIO;
+ } else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) {
+ /* Firmware 1-02 may do this for parallel port cameras,
+ * just clear the flags (developer's guide p 3-38) */
+ do_command(cam, CPIA_COMMAND_ModifyCameraStatus,
+ FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0);
+ }
+ }
+
+ /* Check the camera status again */
+ if (cam->params.status.fatalError) {
+ if (cam->params.status.fatalError)
+ return -EIO;
+ }
+
+ /* VPVersion can't be retrieved before the camera is in HiPower,
+ * so get it here instead of in get_version_information. */
+ do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
+
+ /* set camera to a known state */
+ return set_camera_state(cam);
+}
+
+static void put_cam(struct cpia_camera_ops* ops)
+{
+ if (ops->owner)
+ module_put(ops->owner);
+}
+
+/* ------------------------- V4L interface --------------------- */
+static int cpia_open(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct cam_data *cam = dev->priv;
+ int err;
+
+ if (!cam) {
+ DBG("Internal error, cam_data not found!\n");
+ return -ENODEV;
+ }
+
+ if (cam->open_count > 0) {
+ DBG("Camera already open\n");
+ return -EBUSY;
+ }
+
+ if (!try_module_get(cam->ops->owner))
+ return -ENODEV;
+
+ mutex_lock(&cam->busy_lock);
+ err = -ENOMEM;
+ if (!cam->raw_image) {
+ cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE);
+ if (!cam->raw_image)
+ goto oops;
+ }
+
+ if (!cam->decompressed_frame.data) {
+ cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE);
+ if (!cam->decompressed_frame.data)
+ goto oops;
+ }
+
+ /* open cpia */
+ err = -ENODEV;
+ if (cam->ops->open(cam->lowlevel_data))
+ goto oops;
+
+ /* reset the camera */
+ if ((err = reset_camera(cam)) != 0) {
+ cam->ops->close(cam->lowlevel_data);
+ goto oops;
+ }
+
+ err = -EINTR;
+ if(signal_pending(current))
+ goto oops;
+
+ /* Set ownership of /proc/cpia/videoX to current user */
+ if(cam->proc_entry)
+ cam->proc_entry->uid = current->uid;
+
+ /* set mark for loading first frame uncompressed */
+ cam->first_frame = 1;
+
+ /* init it to something */
+ cam->mmap_kludge = 0;
+
+ ++cam->open_count;
+ file->private_data = dev;
+ mutex_unlock(&cam->busy_lock);
+ return 0;
+
+ oops:
+ if (cam->decompressed_frame.data) {
+ rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
+ cam->decompressed_frame.data = NULL;
+ }
+ if (cam->raw_image) {
+ rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+ cam->raw_image = NULL;
+ }
+ mutex_unlock(&cam->busy_lock);
+ put_cam(cam->ops);
+ return err;
+}
+
+static int cpia_close(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = file->private_data;
+ struct cam_data *cam = dev->priv;
+
+ if (cam->ops) {
+ /* Return ownership of /proc/cpia/videoX to root */
+ if(cam->proc_entry)
+ cam->proc_entry->uid = 0;
+
+ /* save camera state for later open (developers guide ch 3.5.3) */
+ save_camera_state(cam);
+
+ /* GotoLoPower */
+ goto_low_power(cam);
+
+ /* Update the camera status */
+ do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+
+ /* cleanup internal state stuff */
+ free_frames(cam->frame);
+
+ /* close cpia */
+ cam->ops->close(cam->lowlevel_data);
+
+ put_cam(cam->ops);
+ }
+
+ if (--cam->open_count == 0) {
+ /* clean up capture-buffers */
+ if (cam->raw_image) {
+ rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+ cam->raw_image = NULL;
+ }
+
+ if (cam->decompressed_frame.data) {
+ rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
+ cam->decompressed_frame.data = NULL;
+ }
+
+ if (cam->frame_buf)
+ free_frame_buf(cam);
+
+ if (!cam->ops)
+ kfree(cam);
+ }
+ file->private_data = NULL;
+
+ return 0;
+}
+
+static ssize_t cpia_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct video_device *dev = file->private_data;
+ struct cam_data *cam = dev->priv;
+ int err;
+
+ /* make this _really_ smp and multithread-safe */
+ if (mutex_lock_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ if (!buf) {
+ DBG("buf NULL\n");
+ mutex_unlock(&cam->busy_lock);
+ return -EINVAL;
+ }
+
+ if (!count) {
+ DBG("count 0\n");
+ mutex_unlock(&cam->busy_lock);
+ return 0;
+ }
+
+ if (!cam->ops) {
+ DBG("ops NULL\n");
+ mutex_unlock(&cam->busy_lock);
+ return -ENODEV;
+ }
+
+ /* upload frame */
+ cam->decompressed_frame.state = FRAME_READY;
+ cam->mmap_kludge=0;
+ if((err = fetch_frame(cam)) != 0) {
+ DBG("ERROR from fetch_frame: %d\n", err);
+ mutex_unlock(&cam->busy_lock);
+ return err;
+ }
+ cam->decompressed_frame.state = FRAME_UNUSED;
+
+ /* copy data to user space */
+ if (cam->decompressed_frame.count > count) {
+ DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count,
+ (unsigned long) count);
+ mutex_unlock(&cam->busy_lock);
+ return -EFAULT;
+ }
+ if (copy_to_user(buf, cam->decompressed_frame.data,
+ cam->decompressed_frame.count)) {
+ DBG("copy_to_user failed\n");
+ mutex_unlock(&cam->busy_lock);
+ return -EFAULT;
+ }
+
+ mutex_unlock(&cam->busy_lock);
+ return cam->decompressed_frame.count;
+}
+
+static int cpia_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int ioctlnr, void *arg)
+{
+ struct video_device *dev = file->private_data;
+ struct cam_data *cam = dev->priv;
+ int retval = 0;
+
+ if (!cam || !cam->ops)
+ return -ENODEV;
+
+ /* make this _really_ smp-safe */
+ if (mutex_lock_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ //DBG("cpia_ioctl: %u\n", ioctlnr);
+
+ switch (ioctlnr) {
+ /* query capabilities */
+ case VIDIOCGCAP:
+ {
+ struct video_capability *b = arg;
+
+ DBG("VIDIOCGCAP\n");
+ strcpy(b->name, "CPiA Camera");
+ b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
+ b->channels = 1;
+ b->audios = 0;
+ b->maxwidth = 352; /* VIDEOSIZE_CIF */
+ b->maxheight = 288;
+ b->minwidth = 48; /* VIDEOSIZE_48_48 */
+ b->minheight = 48;
+ break;
+ }
+
+ /* get/set video source - we are a camera and nothing else */
+ case VIDIOCGCHAN:
+ {
+ struct video_channel *v = arg;
+
+ DBG("VIDIOCGCHAN\n");
+ if (v->channel != 0) {
+ retval = -EINVAL;
+ break;
+ }
+
+ v->channel = 0;
+ strcpy(v->name, "Camera");
+ v->tuners = 0;
+ v->flags = 0;
+ v->type = VIDEO_TYPE_CAMERA;
+ v->norm = 0;
+ break;
+ }
+
+ case VIDIOCSCHAN:
+ {
+ struct video_channel *v = arg;
+
+ DBG("VIDIOCSCHAN\n");
+ if (v->channel != 0)
+ retval = -EINVAL;
+ break;
+ }
+
+ /* image properties */
+ case VIDIOCGPICT:
+ {
+ struct video_picture *pic = arg;
+ DBG("VIDIOCGPICT\n");
+ *pic = cam->vp;
+ break;
+ }
+
+ case VIDIOCSPICT:
+ {
+ struct video_picture *vp = arg;
+
+ DBG("VIDIOCSPICT\n");
+
+ /* check validity */
+ DBG("palette: %d\n", vp->palette);
+ DBG("depth: %d\n", vp->depth);
+ if (!valid_mode(vp->palette, vp->depth)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ mutex_lock(&cam->param_lock);
+ /* brightness, colour, contrast need no check 0-65535 */
+ cam->vp = *vp;
+ /* update cam->params.colourParams */
+ cam->params.colourParams.brightness = vp->brightness*100/65535;
+ cam->params.colourParams.contrast = vp->contrast*100/65535;
+ cam->params.colourParams.saturation = vp->colour*100/65535;
+ /* contrast is in steps of 8, so round */
+ cam->params.colourParams.contrast =
+ ((cam->params.colourParams.contrast + 3) / 8) * 8;
+ if (cam->params.version.firmwareVersion == 1 &&
+ cam->params.version.firmwareRevision == 2 &&
+ cam->params.colourParams.contrast > 80) {
+ /* 1-02 firmware limits contrast to 80 */
+ cam->params.colourParams.contrast = 80;
+ }
+
+ /* Adjust flicker control if necessary */
+ if(cam->params.flickerControl.allowableOverExposure < 0)
+ cam->params.flickerControl.allowableOverExposure =
+ -find_over_exposure(cam->params.colourParams.brightness);
+ if(cam->params.flickerControl.flickerMode != 0)
+ cam->cmd_queue |= COMMAND_SETFLICKERCTRL;
+
+
+ /* queue command to update camera */
+ cam->cmd_queue |= COMMAND_SETCOLOURPARAMS;
+ mutex_unlock(&cam->param_lock);
+ DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n",
+ vp->depth, vp->palette, vp->brightness, vp->hue, vp->colour,
+ vp->contrast);
+ break;
+ }
+
+ /* get/set capture window */
+ case VIDIOCGWIN:
+ {
+ struct video_window *vw = arg;
+ DBG("VIDIOCGWIN\n");
+
+ *vw = cam->vw;
+ break;
+ }
+
+ case VIDIOCSWIN:
+ {
+ /* copy_from_user, check validity, copy to internal structure */
+ struct video_window *vw = arg;
+ DBG("VIDIOCSWIN\n");
+
+ if (vw->clipcount != 0) { /* clipping not supported */
+ retval = -EINVAL;
+ break;
+ }
+ if (vw->clips != NULL) { /* clipping not supported */
+ retval = -EINVAL;
+ break;
+ }
+
+ /* we set the video window to something smaller or equal to what
+ * is requested by the user???
+ */
+ mutex_lock(&cam->param_lock);
+ if (vw->width != cam->vw.width || vw->height != cam->vw.height) {
+ int video_size = match_videosize(vw->width, vw->height);
+
+ if (video_size < 0) {
+ retval = -EINVAL;
+ mutex_unlock(&cam->param_lock);
+ break;
+ }
+ cam->video_size = video_size;
+
+ /* video size is changing, reset the subcapture area */
+ memset(&cam->vc, 0, sizeof(cam->vc));
+
+ set_vw_size(cam);
+ DBG("%d / %d\n", cam->vw.width, cam->vw.height);
+ cam->cmd_queue |= COMMAND_SETFORMAT;
+ }
+
+ mutex_unlock(&cam->param_lock);
+
+ /* setformat ignored by camera during streaming,
+ * so stop/dispatch/start */
+ if (cam->cmd_queue & COMMAND_SETFORMAT) {
+ DBG("\n");
+ dispatch_commands(cam);
+ }
+ DBG("%d/%d:%d\n", cam->video_size,
+ cam->vw.width, cam->vw.height);
+ break;
+ }
+
+ /* mmap interface */
+ case VIDIOCGMBUF:
+ {
+ struct video_mbuf *vm = arg;
+ int i;
+
+ DBG("VIDIOCGMBUF\n");
+ memset(vm, 0, sizeof(*vm));
+ vm->size = CPIA_MAX_FRAME_SIZE*FRAME_NUM;
+ vm->frames = FRAME_NUM;
+ for (i = 0; i < FRAME_NUM; i++)
+ vm->offsets[i] = CPIA_MAX_FRAME_SIZE * i;
+ break;
+ }
+
+ case VIDIOCMCAPTURE:
+ {
+ struct video_mmap *vm = arg;
+ int video_size;
+
+ DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm->format, vm->frame,
+ vm->width, vm->height);
+ if (vm->frame<0||vm->frame>=FRAME_NUM) {
+ retval = -EINVAL;
+ break;
+ }
+
+ /* set video format */
+ cam->vp.palette = vm->format;
+ switch(vm->format) {
+ case VIDEO_PALETTE_GREY:
+ cam->vp.depth=8;
+ break;
+ case VIDEO_PALETTE_RGB555:
+ case VIDEO_PALETTE_RGB565:
+ case VIDEO_PALETTE_YUV422:
+ case VIDEO_PALETTE_YUYV:
+ case VIDEO_PALETTE_UYVY:
+ cam->vp.depth = 16;
+ break;
+ case VIDEO_PALETTE_RGB24:
+ cam->vp.depth = 24;
+ break;
+ case VIDEO_PALETTE_RGB32:
+ cam->vp.depth = 32;
+ break;
+ default:
+ retval = -EINVAL;
+ break;
+ }
+ if (retval)
+ break;
+
+ /* set video size */
+ video_size = match_videosize(vm->width, vm->height);
+ if (video_size < 0) {
+ retval = -EINVAL;
+ break;
+ }
+ if (video_size != cam->video_size) {
+ cam->video_size = video_size;
+
+ /* video size is changing, reset the subcapture area */
+ memset(&cam->vc, 0, sizeof(cam->vc));
+
+ set_vw_size(cam);
+ cam->cmd_queue |= COMMAND_SETFORMAT;
+ dispatch_commands(cam);
+ }
+ /* according to v4l-spec we must start streaming here */
+ cam->mmap_kludge = 1;
+ retval = capture_frame(cam, vm);
+
+ break;
+ }
+
+ case VIDIOCSYNC:
+ {
+ int *frame = arg;
+
+ //DBG("VIDIOCSYNC: %d\n", *frame);
+
+ if (*frame<0 || *frame >= FRAME_NUM) {
+ retval = -EINVAL;
+ break;
+ }
+
+ switch (cam->frame[*frame].state) {
+ case FRAME_UNUSED:
+ case FRAME_READY:
+ case FRAME_GRABBING:
+ DBG("sync to unused frame %d\n", *frame);
+ retval = -EINVAL;
+ break;
+
+ case FRAME_DONE:
+ cam->frame[*frame].state = FRAME_UNUSED;
+ //DBG("VIDIOCSYNC: %d synced\n", *frame);
+ break;
+ }
+ if (retval == -EINTR) {
+ /* FIXME - xawtv does not handle this nice */
+ retval = 0;
+ }
+ break;
+ }
+
+ case VIDIOCGCAPTURE:
+ {
+ struct video_capture *vc = arg;
+
+ DBG("VIDIOCGCAPTURE\n");
+
+ *vc = cam->vc;
+
+ break;
+ }
+
+ case VIDIOCSCAPTURE:
+ {
+ struct video_capture *vc = arg;
+
+ DBG("VIDIOCSCAPTURE\n");
+
+ if (vc->decimation != 0) { /* How should this be used? */
+ retval = -EINVAL;
+ break;
+ }
+ if (vc->flags != 0) { /* Even/odd grab not supported */
+ retval = -EINVAL;
+ break;
+ }
+
+ /* Clip to the resolution we can set for the ROI
+ (every 8 columns and 4 rows) */
+ vc->x = vc->x & ~(__u32)7;
+ vc->y = vc->y & ~(__u32)3;
+ vc->width = vc->width & ~(__u32)7;
+ vc->height = vc->height & ~(__u32)3;
+
+ if(vc->width == 0 || vc->height == 0 ||
+ vc->x + vc->width > cam->vw.width ||
+ vc->y + vc->height > cam->vw.height) {
+ retval = -EINVAL;
+ break;
+ }
+
+ DBG("%d,%d/%dx%d\n", vc->x,vc->y,vc->width, vc->height);
+
+ mutex_lock(&cam->param_lock);
+
+ cam->vc.x = vc->x;
+ cam->vc.y = vc->y;
+ cam->vc.width = vc->width;
+ cam->vc.height = vc->height;
+
+ set_vw_size(cam);
+ cam->cmd_queue |= COMMAND_SETFORMAT;
+
+ mutex_unlock(&cam->param_lock);
+
+ /* setformat ignored by camera during streaming,
+ * so stop/dispatch/start */
+ dispatch_commands(cam);
+ break;
+ }
+
+ case VIDIOCGUNIT:
+ {
+ struct video_unit *vu = arg;
+
+ DBG("VIDIOCGUNIT\n");
+
+ vu->video = cam->vdev.minor;
+ vu->vbi = VIDEO_NO_UNIT;
+ vu->radio = VIDEO_NO_UNIT;
+ vu->audio = VIDEO_NO_UNIT;
+ vu->teletext = VIDEO_NO_UNIT;
+
+ break;
+ }
+
+
+ /* pointless to implement overlay with this camera */
+ case VIDIOCCAPTURE:
+ case VIDIOCGFBUF:
+ case VIDIOCSFBUF:
+ case VIDIOCKEY:
+ /* tuner interface - we have none */
+ case VIDIOCGTUNER:
+ case VIDIOCSTUNER:
+ case VIDIOCGFREQ:
+ case VIDIOCSFREQ:
+ /* audio interface - we have none */
+ case VIDIOCGAUDIO:
+ case VIDIOCSAUDIO:
+ retval = -EINVAL;
+ break;
+ default:
+ retval = -ENOIOCTLCMD;
+ break;
+ }
+
+ mutex_unlock(&cam->busy_lock);
+ return retval;
+}
+
+static int cpia_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, cpia_do_ioctl);
+}
+
+
+/* FIXME */
+static int cpia_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *dev = file->private_data;
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ unsigned long page, pos;
+ struct cam_data *cam = dev->priv;
+ int retval;
+
+ if (!cam || !cam->ops)
+ return -ENODEV;
+
+ DBG("cpia_mmap: %ld\n", size);
+
+ if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE)
+ return -EINVAL;
+
+ if (!cam || !cam->ops)
+ return -ENODEV;
+
+ /* make this _really_ smp-safe */
+ if (mutex_lock_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ if (!cam->frame_buf) { /* we do lazy allocation */
+ if ((retval = allocate_frame_buf(cam))) {
+ mutex_unlock(&cam->busy_lock);
+ return retval;
+ }
+ }
+
+ pos = (unsigned long)(cam->frame_buf);
+ while (size > 0) {
+ page = vmalloc_to_pfn((void *)pos);
+ if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
+ mutex_unlock(&cam->busy_lock);
+ return -EAGAIN;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+
+ DBG("cpia_mmap: %ld\n", size);
+ mutex_unlock(&cam->busy_lock);
+
+ return 0;
+}
+
+static struct file_operations cpia_fops = {
+ .owner = THIS_MODULE,
+ .open = cpia_open,
+ .release = cpia_close,
+ .read = cpia_read,
+ .mmap = cpia_mmap,
+ .ioctl = cpia_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device cpia_template = {
+ .owner = THIS_MODULE,
+ .name = "CPiA Camera",
+ .type = VID_TYPE_CAPTURE,
+ .hardware = VID_HARDWARE_CPIA,
+ .fops = &cpia_fops,
+};
+
+/* initialise cam_data structure */
+static void reset_camera_struct(struct cam_data *cam)
+{
+ /* The following parameter values are the defaults from
+ * "Software Developer's Guide for CPiA Cameras". Any changes
+ * to the defaults are noted in comments. */
+ cam->params.colourParams.brightness = 50;
+ cam->params.colourParams.contrast = 48;
+ cam->params.colourParams.saturation = 50;
+ cam->params.exposure.gainMode = 4;
+ cam->params.exposure.expMode = 2; /* AEC */
+ cam->params.exposure.compMode = 1;
+ cam->params.exposure.centreWeight = 1;
+ cam->params.exposure.gain = 0;
+ cam->params.exposure.fineExp = 0;
+ cam->params.exposure.coarseExpLo = 185;
+ cam->params.exposure.coarseExpHi = 0;
+ cam->params.exposure.redComp = COMP_RED;
+ cam->params.exposure.green1Comp = COMP_GREEN1;
+ cam->params.exposure.green2Comp = COMP_GREEN2;
+ cam->params.exposure.blueComp = COMP_BLUE;
+ cam->params.colourBalance.balanceMode = 2; /* ACB */
+ cam->params.colourBalance.redGain = 32;
+ cam->params.colourBalance.greenGain = 6;
+ cam->params.colourBalance.blueGain = 92;
+ cam->params.apcor.gain1 = 0x18;
+ cam->params.apcor.gain2 = 0x16;
+ cam->params.apcor.gain4 = 0x24;
+ cam->params.apcor.gain8 = 0x34;
+ cam->params.flickerControl.flickerMode = 0;
+ cam->params.flickerControl.disabled = 1;
+
+ cam->params.flickerControl.coarseJump =
+ flicker_jumps[cam->mainsFreq]
+ [cam->params.sensorFps.baserate]
+ [cam->params.sensorFps.divisor];
+ cam->params.flickerControl.allowableOverExposure =
+ -find_over_exposure(cam->params.colourParams.brightness);
+ cam->params.vlOffset.gain1 = 20;
+ cam->params.vlOffset.gain2 = 24;
+ cam->params.vlOffset.gain4 = 26;
+ cam->params.vlOffset.gain8 = 26;
+ cam->params.compressionParams.hysteresis = 3;
+ cam->params.compressionParams.threshMax = 11;
+ cam->params.compressionParams.smallStep = 1;
+ cam->params.compressionParams.largeStep = 3;
+ cam->params.compressionParams.decimationHysteresis = 2;
+ cam->params.compressionParams.frDiffStepThresh = 5;
+ cam->params.compressionParams.qDiffStepThresh = 3;
+ cam->params.compressionParams.decimationThreshMod = 2;
+ /* End of default values from Software Developer's Guide */
+
+ cam->transfer_rate = 0;
+ cam->exposure_status = EXPOSURE_NORMAL;
+
+ /* Set Sensor FPS to 15fps. This seems better than 30fps
+ * for indoor lighting. */
+ cam->params.sensorFps.divisor = 1;
+ cam->params.sensorFps.baserate = 1;
+
+ cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */
+ cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */
+
+ cam->params.format.subSample = SUBSAMPLE_422;
+ cam->params.format.yuvOrder = YUVORDER_YUYV;
+
+ cam->params.compression.mode = CPIA_COMPRESSION_AUTO;
+ cam->params.compressionTarget.frTargeting =
+ CPIA_COMPRESSION_TARGET_QUALITY;
+ cam->params.compressionTarget.targetFR = 15; /* From windows driver */
+ cam->params.compressionTarget.targetQ = 5; /* From windows driver */
+
+ cam->params.qx3.qx3_detected = 0;
+ cam->params.qx3.toplight = 0;
+ cam->params.qx3.bottomlight = 0;
+ cam->params.qx3.button = 0;
+ cam->params.qx3.cradled = 0;
+
+ cam->video_size = VIDEOSIZE_CIF;
+
+ cam->vp.colour = 32768; /* 50% */
+ cam->vp.hue = 32768; /* 50% */
+ cam->vp.brightness = 32768; /* 50% */
+ cam->vp.contrast = 32768; /* 50% */
+ cam->vp.whiteness = 0; /* not used -> grayscale only */
+ cam->vp.depth = 24; /* to be set by user */
+ cam->vp.palette = VIDEO_PALETTE_RGB24; /* to be set by user */
+
+ cam->vc.x = 0;
+ cam->vc.y = 0;
+ cam->vc.width = 0;
+ cam->vc.height = 0;
+
+ cam->vw.x = 0;
+ cam->vw.y = 0;
+ set_vw_size(cam);
+ cam->vw.chromakey = 0;
+ cam->vw.flags = 0;
+ cam->vw.clipcount = 0;
+ cam->vw.clips = NULL;
+
+ cam->cmd_queue = COMMAND_NONE;
+ cam->first_frame = 1;
+
+ return;
+}
+
+/* initialize cam_data structure */
+static void init_camera_struct(struct cam_data *cam,
+ struct cpia_camera_ops *ops )
+{
+ int i;
+
+ /* Default everything to 0 */
+ memset(cam, 0, sizeof(struct cam_data));
+
+ cam->ops = ops;
+ mutex_init(&cam->param_lock);
+ mutex_init(&cam->busy_lock);
+
+ reset_camera_struct(cam);
+
+ cam->proc_entry = NULL;
+
+ memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template));
+ cam->vdev.priv = cam;
+
+ cam->curframe = 0;
+ for (i = 0; i < FRAME_NUM; i++) {
+ cam->frame[i].width = 0;
+ cam->frame[i].height = 0;
+ cam->frame[i].state = FRAME_UNUSED;
+ cam->frame[i].data = NULL;
+ }
+ cam->decompressed_frame.width = 0;
+ cam->decompressed_frame.height = 0;
+ cam->decompressed_frame.state = FRAME_UNUSED;
+ cam->decompressed_frame.data = NULL;
+}
+
+struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel)
+{
+ struct cam_data *camera;
+
+ if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL)
+ return NULL;
+
+
+ init_camera_struct( camera, ops );
+ camera->lowlevel_data = lowlevel;
+
+ /* register v4l device */
+ if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
+ kfree(camera);
+ printk(KERN_DEBUG "video_register_device failed\n");
+ return NULL;
+ }
+
+ /* get version information from camera: open/reset/close */
+
+ /* open cpia */
+ if (camera->ops->open(camera->lowlevel_data))
+ return camera;
+
+ /* reset the camera */
+ if (reset_camera(camera) != 0) {
+ camera->ops->close(camera->lowlevel_data);
+ return camera;
+ }
+
+ /* close cpia */
+ camera->ops->close(camera->lowlevel_data);
+
+#ifdef CONFIG_PROC_FS
+ create_proc_cpia_cam(camera);
+#endif
+
+ printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n",
+ camera->params.version.firmwareVersion,
+ camera->params.version.firmwareRevision,
+ camera->params.version.vcVersion,
+ camera->params.version.vcRevision);
+ printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n",
+ camera->params.pnpID.vendor,
+ camera->params.pnpID.product,
+ camera->params.pnpID.deviceRevision);
+ printk(KERN_INFO " VP-Version: %d.%d %04x\n",
+ camera->params.vpVersion.vpVersion,
+ camera->params.vpVersion.vpRevision,
+ camera->params.vpVersion.cameraHeadID);
+
+ return camera;
+}
+
+void cpia_unregister_camera(struct cam_data *cam)
+{
+ DBG("unregistering video\n");
+ video_unregister_device(&cam->vdev);
+ if (cam->open_count) {
+ put_cam(cam->ops);
+ DBG("camera open -- setting ops to NULL\n");
+ cam->ops = NULL;
+ }
+
+#ifdef CONFIG_PROC_FS
+ DBG("destroying /proc/cpia/video%d\n", cam->vdev.minor);
+ destroy_proc_cpia_cam(cam);
+#endif
+ if (!cam->open_count) {
+ DBG("freeing camera\n");
+ kfree(cam);
+ }
+}
+
+static int __init cpia_init(void)
+{
+ printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
+ CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
+
+ printk(KERN_WARNING "Since in-kernel colorspace conversion is not "
+ "allowed, it is disabled by default now. Users should fix the "
+ "applications in case they don't work without conversion "
+ "reenabled by setting the 'colorspace_conv' module "
+ "parameter to 1\n");
+
+#ifdef CONFIG_PROC_FS
+ proc_cpia_create();
+#endif
+
+#ifdef CONFIG_VIDEO_CPIA_PP
+ cpia_pp_init();
+#endif
+#ifdef CONFIG_VIDEO_CPIA_USB
+ cpia_usb_init();
+#endif
+
+ return 0;
+}
+
+static void __exit cpia_exit(void)
+{
+#ifdef CONFIG_PROC_FS
+ proc_cpia_destroy();
+#endif
+}
+
+module_init(cpia_init);
+module_exit(cpia_exit);
+
+/* Exported symbols for modules. */
+
+EXPORT_SYMBOL(cpia_register_camera);
+EXPORT_SYMBOL(cpia_unregister_camera);
diff --git a/linux/drivers/media/video/cpia.h b/linux/drivers/media/video/cpia.h
new file mode 100644
index 000000000..120b66388
--- /dev/null
+++ b/linux/drivers/media/video/cpia.h
@@ -0,0 +1,432 @@
+#ifndef cpia_h
+#define cpia_h
+
+/*
+ * CPiA Parallel Port Video4Linux driver
+ *
+ * Supports CPiA based parallel port Video Camera's.
+ *
+ * (C) Copyright 1999 Bas Huisman,
+ * Peter Pregler,
+ * Scott J. Bertin,
+ * VLSI Vision Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define CPIA_MAJ_VER 1
+#define CPIA_MIN_VER 2
+#define CPIA_PATCH_VER 3
+
+#define CPIA_PP_MAJ_VER CPIA_MAJ_VER
+#define CPIA_PP_MIN_VER CPIA_MIN_VER
+#define CPIA_PP_PATCH_VER CPIA_PATCH_VER
+
+#define CPIA_USB_MAJ_VER CPIA_MAJ_VER
+#define CPIA_USB_MIN_VER CPIA_MIN_VER
+#define CPIA_USB_PATCH_VER CPIA_PATCH_VER
+
+#define CPIA_MAX_FRAME_SIZE_UNALIGNED (352 * 288 * 4) /* CIF at RGB32 */
+#define CPIA_MAX_FRAME_SIZE ((CPIA_MAX_FRAME_SIZE_UNALIGNED + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) /* align above to PAGE_SIZE */
+
+#ifdef __KERNEL__
+
+#include <asm/uaccess.h>
+#include "compat.h"
+#include <linux/videodev.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>
+#include <linux/mutex.h>
+
+struct cpia_camera_ops
+{
+ /* open sets privdata to point to structure for this camera.
+ * Returns negative value on error, otherwise 0.
+ */
+ int (*open)(void *privdata);
+
+ /* Registers callback function cb to be called with cbdata
+ * when an image is ready. If cb is NULL, only single image grabs
+ * should be used. cb should immediately call streamRead to read
+ * the data or data may be lost. Returns negative value on error,
+ * otherwise 0.
+ */
+ int (*registerCallback)(void *privdata, void (*cb)(void *cbdata),
+ void *cbdata);
+
+ /* transferCmd sends commands to the camera. command MUST point to
+ * an 8 byte buffer in kernel space. data can be NULL if no extra
+ * data is needed. The size of the data is given by the last 2
+ * bytes of command. data must also point to memory in kernel space.
+ * Returns negative value on error, otherwise 0.
+ */
+ int (*transferCmd)(void *privdata, u8 *command, u8 *data);
+
+ /* streamStart initiates stream capture mode.
+ * Returns negative value on error, otherwise 0.
+ */
+ int (*streamStart)(void *privdata);
+
+ /* streamStop terminates stream capture mode.
+ * Returns negative value on error, otherwise 0.
+ */
+ int (*streamStop)(void *privdata);
+
+ /* streamRead reads a frame from the camera. buffer points to a
+ * buffer large enough to hold a complete frame in kernel space.
+ * noblock indicates if this should be a non blocking read.
+ * Returns the number of bytes read, or negative value on error.
+ */
+ int (*streamRead)(void *privdata, u8 *buffer, int noblock);
+
+ /* close disables the device until open() is called again.
+ * Returns negative value on error, otherwise 0.
+ */
+ int (*close)(void *privdata);
+
+ /* If wait_for_stream_ready is non-zero, wait until the streamState
+ * is STREAM_READY before calling streamRead.
+ */
+ int wait_for_stream_ready;
+
+ /*
+ * Used to maintain lowlevel module usage counts
+ */
+ struct module *owner;
+};
+
+struct cpia_frame {
+ u8 *data;
+ int count;
+ int width;
+ int height;
+ volatile int state;
+};
+
+struct cam_params {
+ struct {
+ u8 firmwareVersion;
+ u8 firmwareRevision;
+ u8 vcVersion;
+ u8 vcRevision;
+ } version;
+ struct {
+ u16 vendor;
+ u16 product;
+ u16 deviceRevision;
+ } pnpID;
+ struct {
+ u8 vpVersion;
+ u8 vpRevision;
+ u16 cameraHeadID;
+ } vpVersion;
+ struct {
+ u8 systemState;
+ u8 grabState;
+ u8 streamState;
+ u8 fatalError;
+ u8 cmdError;
+ u8 debugFlags;
+ u8 vpStatus;
+ u8 errorCode;
+ } status;
+ struct {
+ u8 brightness;
+ u8 contrast;
+ u8 saturation;
+ } colourParams;
+ struct {
+ u8 gainMode;
+ u8 expMode;
+ u8 compMode;
+ u8 centreWeight;
+ u8 gain;
+ u8 fineExp;
+ u8 coarseExpLo;
+ u8 coarseExpHi;
+ u8 redComp;
+ u8 green1Comp;
+ u8 green2Comp;
+ u8 blueComp;
+ } exposure;
+ struct {
+ u8 balanceMode;
+ u8 redGain;
+ u8 greenGain;
+ u8 blueGain;
+ } colourBalance;
+ struct {
+ u8 divisor;
+ u8 baserate;
+ } sensorFps;
+ struct {
+ u8 gain1;
+ u8 gain2;
+ u8 gain4;
+ u8 gain8;
+ } apcor;
+ struct {
+ u8 disabled;
+ u8 flickerMode;
+ u8 coarseJump;
+ int allowableOverExposure;
+ } flickerControl;
+ struct {
+ u8 gain1;
+ u8 gain2;
+ u8 gain4;
+ u8 gain8;
+ } vlOffset;
+ struct {
+ u8 mode;
+ u8 decimation;
+ } compression;
+ struct {
+ u8 frTargeting;
+ u8 targetFR;
+ u8 targetQ;
+ } compressionTarget;
+ struct {
+ u8 yThreshold;
+ u8 uvThreshold;
+ } yuvThreshold;
+ struct {
+ u8 hysteresis;
+ u8 threshMax;
+ u8 smallStep;
+ u8 largeStep;
+ u8 decimationHysteresis;
+ u8 frDiffStepThresh;
+ u8 qDiffStepThresh;
+ u8 decimationThreshMod;
+ } compressionParams;
+ struct {
+ u8 videoSize; /* CIF/QCIF */
+ u8 subSample;
+ u8 yuvOrder;
+ } format;
+ struct { /* Intel QX3 specific data */
+ u8 qx3_detected; /* a QX3 is present */
+ u8 toplight; /* top light lit , R/W */
+ u8 bottomlight; /* bottom light lit, R/W */
+ u8 button; /* snapshot button pressed (R/O) */
+ u8 cradled; /* microscope is in cradle (R/O) */
+ } qx3;
+ struct {
+ u8 colStart; /* skip first 8*colStart pixels */
+ u8 colEnd; /* finish at 8*colEnd pixels */
+ u8 rowStart; /* skip first 4*rowStart lines */
+ u8 rowEnd; /* finish at 4*rowEnd lines */
+ } roi;
+ u8 ecpTiming;
+ u8 streamStartLine;
+};
+
+enum v4l_camstates {
+ CPIA_V4L_IDLE = 0,
+ CPIA_V4L_ERROR,
+ CPIA_V4L_COMMAND,
+ CPIA_V4L_GRABBING,
+ CPIA_V4L_STREAMING,
+ CPIA_V4L_STREAMING_PAUSED,
+};
+
+#define FRAME_NUM 2 /* double buffering for now */
+
+struct cam_data {
+ struct list_head cam_data_list;
+
+ struct mutex busy_lock; /* guard against SMP multithreading */
+ struct cpia_camera_ops *ops; /* lowlevel driver operations */
+ void *lowlevel_data; /* private data for lowlevel driver */
+ u8 *raw_image; /* buffer for raw image data */
+ struct cpia_frame decompressed_frame;
+ /* buffer to hold decompressed frame */
+ int image_size; /* sizeof last decompressed image */
+ int open_count; /* # of process that have camera open */
+
+ /* camera status */
+ int fps; /* actual fps reported by the camera */
+ int transfer_rate; /* transfer rate from camera in kB/s */
+ u8 mainsFreq; /* for flicker control */
+
+ /* proc interface */
+ struct mutex param_lock; /* params lock for this camera */
+ struct cam_params params; /* camera settings */
+ struct proc_dir_entry *proc_entry; /* /proc/cpia/videoX */
+
+ /* v4l */
+ int video_size; /* VIDEO_SIZE_ */
+ volatile enum v4l_camstates camstate; /* v4l layer status */
+ struct video_device vdev; /* v4l videodev */
+ struct video_picture vp; /* v4l camera settings */
+ struct video_window vw; /* v4l capture area */
+ struct video_capture vc; /* v4l subcapture area */
+
+ /* mmap interface */
+ int curframe; /* the current frame to grab into */
+ u8 *frame_buf; /* frame buffer data */
+ struct cpia_frame frame[FRAME_NUM];
+ /* FRAME_NUM-buffering, so we need a array */
+
+ int first_frame;
+ int mmap_kludge; /* 'wrong' byte order for mmap */
+ volatile u32 cmd_queue; /* queued commands */
+ int exposure_status; /* EXPOSURE_* */
+ int exposure_count; /* number of frames at this status */
+};
+
+/* cpia_register_camera is called by low level driver for each camera.
+ * A unique camera number is returned, or a negative value on error */
+struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel);
+
+/* cpia_unregister_camera is called by low level driver when a camera
+ * is removed. This must not fail. */
+void cpia_unregister_camera(struct cam_data *cam);
+
+/* raw CIF + 64 byte header + (2 bytes line_length + EOL) per line + 4*EOI +
+ * one byte 16bit DMA alignment
+ */
+#define CPIA_MAX_IMAGE_SIZE ((352*288*2)+64+(288*3)+5)
+
+/* constant value's */
+#define MAGIC_0 0x19
+#define MAGIC_1 0x68
+#define DATA_IN 0xC0
+#define DATA_OUT 0x40
+#define VIDEOSIZE_QCIF 0 /* 176x144 */
+#define VIDEOSIZE_CIF 1 /* 352x288 */
+#define VIDEOSIZE_SIF 2 /* 320x240 */
+#define VIDEOSIZE_QSIF 3 /* 160x120 */
+#define VIDEOSIZE_48_48 4 /* where no one has gone before, iconsize! */
+#define VIDEOSIZE_64_48 5
+#define VIDEOSIZE_128_96 6
+#define VIDEOSIZE_160_120 VIDEOSIZE_QSIF
+#define VIDEOSIZE_176_144 VIDEOSIZE_QCIF
+#define VIDEOSIZE_192_144 7
+#define VIDEOSIZE_224_168 8
+#define VIDEOSIZE_256_192 9
+#define VIDEOSIZE_288_216 10
+#define VIDEOSIZE_320_240 VIDEOSIZE_SIF
+#define VIDEOSIZE_352_288 VIDEOSIZE_CIF
+#define VIDEOSIZE_88_72 11 /* quarter CIF */
+#define SUBSAMPLE_420 0
+#define SUBSAMPLE_422 1
+#define YUVORDER_YUYV 0
+#define YUVORDER_UYVY 1
+#define NOT_COMPRESSED 0
+#define COMPRESSED 1
+#define NO_DECIMATION 0
+#define DECIMATION_ENAB 1
+#define EOI 0xff /* End Of Image */
+#define EOL 0xfd /* End Of Line */
+#define FRAME_HEADER_SIZE 64
+
+/* Image grab modes */
+#define CPIA_GRAB_SINGLE 0
+#define CPIA_GRAB_CONTINUOUS 1
+
+/* Compression parameters */
+#define CPIA_COMPRESSION_NONE 0
+#define CPIA_COMPRESSION_AUTO 1
+#define CPIA_COMPRESSION_MANUAL 2
+#define CPIA_COMPRESSION_TARGET_QUALITY 0
+#define CPIA_COMPRESSION_TARGET_FRAMERATE 1
+
+/* Return offsets for GetCameraState */
+#define SYSTEMSTATE 0
+#define GRABSTATE 1
+#define STREAMSTATE 2
+#define FATALERROR 3
+#define CMDERROR 4
+#define DEBUGFLAGS 5
+#define VPSTATUS 6
+#define ERRORCODE 7
+
+/* SystemState */
+#define UNINITIALISED_STATE 0
+#define PASS_THROUGH_STATE 1
+#define LO_POWER_STATE 2
+#define HI_POWER_STATE 3
+#define WARM_BOOT_STATE 4
+
+/* GrabState */
+#define GRAB_IDLE 0
+#define GRAB_ACTIVE 1
+#define GRAB_DONE 2
+
+/* StreamState */
+#define STREAM_NOT_READY 0
+#define STREAM_READY 1
+#define STREAM_OPEN 2
+#define STREAM_PAUSED 3
+#define STREAM_FINISHED 4
+
+/* Fatal Error, CmdError, and DebugFlags */
+#define CPIA_FLAG 1
+#define SYSTEM_FLAG 2
+#define INT_CTRL_FLAG 4
+#define PROCESS_FLAG 8
+#define COM_FLAG 16
+#define VP_CTRL_FLAG 32
+#define CAPTURE_FLAG 64
+#define DEBUG_FLAG 128
+
+/* VPStatus */
+#define VP_STATE_OK 0x00
+
+#define VP_STATE_FAILED_VIDEOINIT 0x01
+#define VP_STATE_FAILED_AECACBINIT 0x02
+#define VP_STATE_AEC_MAX 0x04
+#define VP_STATE_ACB_BMAX 0x08
+
+#define VP_STATE_ACB_RMIN 0x10
+#define VP_STATE_ACB_GMIN 0x20
+#define VP_STATE_ACB_RMAX 0x40
+#define VP_STATE_ACB_GMAX 0x80
+
+/* default (minimum) compensation values */
+#define COMP_RED 220
+#define COMP_GREEN1 214
+#define COMP_GREEN2 COMP_GREEN1
+#define COMP_BLUE 230
+
+/* exposure status */
+#define EXPOSURE_VERY_LIGHT 0
+#define EXPOSURE_LIGHT 1
+#define EXPOSURE_NORMAL 2
+#define EXPOSURE_DARK 3
+#define EXPOSURE_VERY_DARK 4
+
+/* ErrorCode */
+#define ERROR_FLICKER_BELOW_MIN_EXP 0x01 /*flicker exposure got below minimum exposure */
+#define ALOG(fmt,args...) printk(fmt, ##args)
+#define LOG(fmt,args...) ALOG(KERN_INFO __FILE__ ":%s(%d):" fmt, __FUNCTION__ , __LINE__ , ##args)
+
+#ifdef _CPIA_DEBUG_
+#define ADBG(fmt,args...) printk(fmt, jiffies, ##args)
+#define DBG(fmt,args...) ADBG(KERN_DEBUG __FILE__" (%ld):%s(%d):" fmt, __FUNCTION__, __LINE__ , ##args)
+#else
+#define DBG(fmn,args...) do {} while(0)
+#endif
+
+#define DEB_BYTE(p)\
+ DBG("%1d %1d %1d %1d %1d %1d %1d %1d \n",\
+ (p)&0x80?1:0, (p)&0x40?1:0, (p)&0x20?1:0, (p)&0x10?1:0,\
+ (p)&0x08?1:0, (p)&0x04?1:0, (p)&0x02?1:0, (p)&0x01?1:0);
+
+#endif /* __KERNEL__ */
+
+#endif /* cpia_h */
diff --git a/linux/drivers/media/video/cpia_pp.c b/linux/drivers/media/video/cpia_pp.c
new file mode 100644
index 000000000..0b00e6027
--- /dev/null
+++ b/linux/drivers/media/video/cpia_pp.c
@@ -0,0 +1,881 @@
+/*
+ * cpia_pp CPiA Parallel Port driver
+ *
+ * Supports CPiA based parallel port Video Camera's.
+ *
+ * (C) Copyright 1999 Bas Huisman <bhuism@cs.utwente.nl>
+ * (C) Copyright 1999-2000 Scott J. Bertin <sbertin@securenym.net>,
+ * (C) Copyright 1999-2000 Peter Pregler <Peter_Pregler@email.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */
+/* #define _CPIA_DEBUG_ 1 */
+
+#include <linux/config.h>
+
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/kernel.h>
+#include <linux/parport.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/smp_lock.h>
+#include <linux/sched.h>
+
+#include <linux/kmod.h>
+
+/* #define _CPIA_DEBUG_ define for verbose debug output */
+#include "cpia.h"
+
+static int cpia_pp_open(void *privdata);
+static int cpia_pp_registerCallback(void *privdata, void (*cb) (void *cbdata),
+ void *cbdata);
+static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data);
+static int cpia_pp_streamStart(void *privdata);
+static int cpia_pp_streamStop(void *privdata);
+static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock);
+static int cpia_pp_close(void *privdata);
+
+
+#define ABOUT "Parallel port driver for Vision CPiA based cameras"
+
+#define PACKET_LENGTH 8
+
+/* Magic numbers for defining port-device mappings */
+#define PPCPIA_PARPORT_UNSPEC -4
+#define PPCPIA_PARPORT_AUTO -3
+#define PPCPIA_PARPORT_OFF -2
+#define PPCPIA_PARPORT_NONE -1
+
+#ifdef MODULE
+static int parport_nr[PARPORT_MAX] = {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC};
+static char *parport[PARPORT_MAX] = {NULL,};
+
+MODULE_AUTHOR("B. Huisman <bhuism@cs.utwente.nl> & Peter Pregler <Peter_Pregler@email.com>");
+MODULE_DESCRIPTION("Parallel port driver for Vision CPiA based cameras");
+MODULE_LICENSE("GPL");
+
+module_param_array(parport, charp, NULL, 0);
+MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp.");
+#else
+static int parport_nr[PARPORT_MAX] __initdata =
+ {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC};
+static int parport_ptr = 0;
+#endif
+
+struct pp_cam_entry {
+ struct pardevice *pdev;
+ struct parport *port;
+ struct work_struct cb_task;
+ int open_count;
+ wait_queue_head_t wq_stream;
+ /* image state flags */
+ int image_ready; /* we got an interrupt */
+ int image_complete; /* we have seen 4 EOI */
+
+ int streaming; /* we are in streaming mode */
+ int stream_irq;
+};
+
+static struct cpia_camera_ops cpia_pp_ops =
+{
+ cpia_pp_open,
+ cpia_pp_registerCallback,
+ cpia_pp_transferCmd,
+ cpia_pp_streamStart,
+ cpia_pp_streamStop,
+ cpia_pp_streamRead,
+ cpia_pp_close,
+ 1,
+ THIS_MODULE
+};
+
+static LIST_HEAD(cam_list);
+static spinlock_t cam_list_lock_pp;
+
+/* FIXME */
+static void cpia_parport_enable_irq( struct parport *port ) {
+ parport_enable_irq(port);
+ mdelay(10);
+ return;
+}
+
+static void cpia_parport_disable_irq( struct parport *port ) {
+ parport_disable_irq(port);
+ mdelay(10);
+ return;
+}
+
+/* Special CPiA PPC modes: These are invoked by using the 1284 Extensibility
+ * Link Flag during negotiation */
+#define UPLOAD_FLAG 0x08
+#define NIBBLE_TRANSFER 0x01
+#define ECP_TRANSFER 0x03
+
+#define PARPORT_CHUNK_SIZE PAGE_SIZE
+
+
+/****************************************************************************
+ *
+ * CPiA-specific low-level parport functions for nibble uploads
+ *
+ ***************************************************************************/
+/* CPiA nonstandard "Nibble" mode (no nDataAvail signal after each byte). */
+/* The standard kernel parport_ieee1284_read_nibble() fails with the CPiA... */
+
+static size_t cpia_read_nibble (struct parport *port,
+ void *buffer, size_t len,
+ int flags)
+{
+ /* adapted verbatim, with one change, from
+ parport_ieee1284_read_nibble() in drivers/parport/ieee1284-ops.c */
+
+ unsigned char *buf = buffer;
+ int i;
+ unsigned char byte = 0;
+
+ len *= 2; /* in nibbles */
+ for (i=0; i < len; i++) {
+ unsigned char nibble;
+
+ /* The CPiA firmware suppresses the use of nDataAvail (nFault LO)
+ * after every second nibble to signal that more
+ * data is available. (the total number of Bytes that
+ * should be sent is known; if too few are received, an error
+ * will be recorded after a timeout).
+ * This is incompatible with parport_ieee1284_read_nibble(),
+ * which expects to find nFault LO after every second nibble.
+ */
+
+ /* Solution: modify cpia_read_nibble to only check for
+ * nDataAvail before the first nibble is sent.
+ */
+
+ /* Does the error line indicate end of data? */
+ if (((i /*& 1*/) == 0) &&
+ (parport_read_status(port) & PARPORT_STATUS_ERROR)) {
+ DBG("%s: No more nibble data (%d bytes)\n",
+ port->name, i/2);
+ goto end_of_data;
+ }
+
+ /* Event 7: Set nAutoFd low. */
+ parport_frob_control (port,
+ PARPORT_CONTROL_AUTOFD,
+ PARPORT_CONTROL_AUTOFD);
+
+ /* Event 9: nAck goes low. */
+ port->ieee1284.phase = IEEE1284_PH_REV_DATA;
+ if (parport_wait_peripheral (port,
+ PARPORT_STATUS_ACK, 0)) {
+ /* Timeout -- no more data? */
+ DBG("%s: Nibble timeout at event 9 (%d bytes)\n",
+ port->name, i/2);
+ parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
+ break;
+ }
+
+
+ /* Read a nibble. */
+ nibble = parport_read_status (port) >> 3;
+ nibble &= ~8;
+ if ((nibble & 0x10) == 0)
+ nibble |= 8;
+ nibble &= 0xf;
+
+ /* Event 10: Set nAutoFd high. */
+ parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
+
+ /* Event 11: nAck goes high. */
+ if (parport_wait_peripheral (port,
+ PARPORT_STATUS_ACK,
+ PARPORT_STATUS_ACK)) {
+ /* Timeout -- no more data? */
+ DBG("%s: Nibble timeout at event 11\n",
+ port->name);
+ break;
+ }
+
+ if (i & 1) {
+ /* Second nibble */
+ byte |= nibble << 4;
+ *buf++ = byte;
+ } else
+ byte = nibble;
+ }
+
+ if (i == len) {
+ /* Read the last nibble without checking data avail. */
+ if (parport_read_status (port) & PARPORT_STATUS_ERROR) {
+ end_of_data:
+ /* Go to reverse idle phase. */
+ parport_frob_control (port,
+ PARPORT_CONTROL_AUTOFD,
+ PARPORT_CONTROL_AUTOFD);
+ port->physport->ieee1284.phase = IEEE1284_PH_REV_IDLE;
+ }
+ else
+ port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL;
+ }
+
+ return i/2;
+}
+
+/* CPiA nonstandard "Nibble Stream" mode (2 nibbles per cycle, instead of 1)
+ * (See CPiA Data sheet p. 31)
+ *
+ * "Nibble Stream" mode used by CPiA for uploads to non-ECP ports is a
+ * nonstandard variant of nibble mode which allows the same (mediocre)
+ * data flow of 8 bits per cycle as software-enabled ECP by TRISTATE-capable
+ * parallel ports, but works also for non-TRISTATE-capable ports.
+ * (Standard nibble mode only send 4 bits per cycle)
+ *
+ */
+
+static size_t cpia_read_nibble_stream(struct parport *port,
+ void *buffer, size_t len,
+ int flags)
+{
+ int i;
+ unsigned char *buf = buffer;
+ int endseen = 0;
+
+ for (i=0; i < len; i++) {
+ unsigned char nibble[2], byte = 0;
+ int j;
+
+ /* Image Data is complete when 4 consecutive EOI bytes (0xff) are seen */
+ if (endseen > 3 )
+ break;
+
+ /* Event 7: Set nAutoFd low. */
+ parport_frob_control (port,
+ PARPORT_CONTROL_AUTOFD,
+ PARPORT_CONTROL_AUTOFD);
+
+ /* Event 9: nAck goes low. */
+ port->ieee1284.phase = IEEE1284_PH_REV_DATA;
+ if (parport_wait_peripheral (port,
+ PARPORT_STATUS_ACK, 0)) {
+ /* Timeout -- no more data? */
+ DBG("%s: Nibble timeout at event 9 (%d bytes)\n",
+ port->name, i/2);
+ parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
+ break;
+ }
+
+ /* Read lower nibble */
+ nibble[0] = parport_read_status (port) >>3;
+
+ /* Event 10: Set nAutoFd high. */
+ parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
+
+ /* Event 11: nAck goes high. */
+ if (parport_wait_peripheral (port,
+ PARPORT_STATUS_ACK,
+ PARPORT_STATUS_ACK)) {
+ /* Timeout -- no more data? */
+ DBG("%s: Nibble timeout at event 11\n",
+ port->name);
+ break;
+ }
+
+ /* Read upper nibble */
+ nibble[1] = parport_read_status (port) >>3;
+
+ /* reassemble the byte */
+ for (j = 0; j < 2 ; j++ ) {
+ nibble[j] &= ~8;
+ if ((nibble[j] & 0x10) == 0)
+ nibble[j] |= 8;
+ nibble[j] &= 0xf;
+ }
+ byte = (nibble[0] |(nibble[1] << 4));
+ *buf++ = byte;
+
+ if(byte == EOI)
+ endseen++;
+ else
+ endseen = 0;
+ }
+ return i;
+}
+
+/****************************************************************************
+ *
+ * EndTransferMode
+ *
+ ***************************************************************************/
+static void EndTransferMode(struct pp_cam_entry *cam)
+{
+ parport_negotiate(cam->port, IEEE1284_MODE_COMPAT);
+}
+
+/****************************************************************************
+ *
+ * ForwardSetup
+ *
+ ***************************************************************************/
+static int ForwardSetup(struct pp_cam_entry *cam)
+{
+ int retry;
+
+ /* The CPiA uses ECP protocol for Downloads from the Host to the camera.
+ * This will be software-emulated if ECP hardware is not present
+ */
+
+ /* the usual camera maximum response time is 10ms, but after receiving
+ * some commands, it needs up to 40ms. (Data Sheet p. 32)*/
+
+ for(retry = 0; retry < 4; ++retry) {
+ if(!parport_negotiate(cam->port, IEEE1284_MODE_ECP)) {
+ break;
+ }
+ mdelay(10);
+ }
+ if(retry == 4) {
+ DBG("Unable to negotiate IEEE1284 ECP Download mode\n");
+ return -1;
+ }
+ return 0;
+}
+/****************************************************************************
+ *
+ * ReverseSetup
+ *
+ ***************************************************************************/
+static int ReverseSetup(struct pp_cam_entry *cam, int extensibility)
+{
+ int retry;
+ int upload_mode, mode = IEEE1284_MODE_ECP;
+ int transfer_mode = ECP_TRANSFER;
+
+ if (!(cam->port->modes & PARPORT_MODE_ECP) &&
+ !(cam->port->modes & PARPORT_MODE_TRISTATE)) {
+ mode = IEEE1284_MODE_NIBBLE;
+ transfer_mode = NIBBLE_TRANSFER;
+ }
+
+ upload_mode = mode;
+ if(extensibility) mode = UPLOAD_FLAG|transfer_mode|IEEE1284_EXT_LINK;
+
+ /* the usual camera maximum response time is 10ms, but after
+ * receiving some commands, it needs up to 40ms. */
+
+ for(retry = 0; retry < 4; ++retry) {
+ if(!parport_negotiate(cam->port, mode)) {
+ break;
+ }
+ mdelay(10);
+ }
+ if(retry == 4) {
+ if(extensibility)
+ DBG("Unable to negotiate upload extensibility mode\n");
+ else
+ DBG("Unable to negotiate upload mode\n");
+ return -1;
+ }
+ if(extensibility) cam->port->ieee1284.mode = upload_mode;
+ return 0;
+}
+
+/****************************************************************************
+ *
+ * WritePacket
+ *
+ ***************************************************************************/
+static int WritePacket(struct pp_cam_entry *cam, const u8 *packet, size_t size)
+{
+ int retval=0;
+ int size_written;
+
+ if (packet == NULL) {
+ return -EINVAL;
+ }
+ if (ForwardSetup(cam)) {
+ DBG("Write failed in setup\n");
+ return -EIO;
+ }
+ size_written = parport_write(cam->port, packet, size);
+ if(size_written != size) {
+ DBG("Write failed, wrote %d/%d\n", size_written, size);
+ retval = -EIO;
+ }
+ EndTransferMode(cam);
+ return retval;
+}
+
+/****************************************************************************
+ *
+ * ReadPacket
+ *
+ ***************************************************************************/
+static int ReadPacket(struct pp_cam_entry *cam, u8 *packet, size_t size)
+{
+ int retval=0;
+
+ if (packet == NULL) {
+ return -EINVAL;
+ }
+ if (ReverseSetup(cam, 0)) {
+ return -EIO;
+ }
+
+ /* support for CPiA variant nibble reads */
+ if(cam->port->ieee1284.mode == IEEE1284_MODE_NIBBLE) {
+ if(cpia_read_nibble(cam->port, packet, size, 0) != size)
+ retval = -EIO;
+ } else {
+ if(parport_read(cam->port, packet, size) != size)
+ retval = -EIO;
+ }
+ EndTransferMode(cam);
+ return retval;
+}
+
+/****************************************************************************
+ *
+ * cpia_pp_streamStart
+ *
+ ***************************************************************************/
+static int cpia_pp_streamStart(void *privdata)
+{
+ struct pp_cam_entry *cam = privdata;
+ DBG("\n");
+ cam->streaming=1;
+ cam->image_ready=0;
+ //if (ReverseSetup(cam,1)) return -EIO;
+ if(cam->stream_irq) cpia_parport_enable_irq(cam->port);
+ return 0;
+}
+
+/****************************************************************************
+ *
+ * cpia_pp_streamStop
+ *
+ ***************************************************************************/
+static int cpia_pp_streamStop(void *privdata)
+{
+ struct pp_cam_entry *cam = privdata;
+
+ DBG("\n");
+ cam->streaming=0;
+ cpia_parport_disable_irq(cam->port);
+ //EndTransferMode(cam);
+
+ return 0;
+}
+
+/****************************************************************************
+ *
+ * cpia_pp_streamRead
+ *
+ ***************************************************************************/
+static int cpia_pp_read(struct parport *port, u8 *buffer, int len)
+{
+ int bytes_read;
+
+ /* support for CPiA variant "nibble stream" reads */
+ if(port->ieee1284.mode == IEEE1284_MODE_NIBBLE)
+ bytes_read = cpia_read_nibble_stream(port,buffer,len,0);
+ else {
+ int new_bytes;
+ for(bytes_read=0; bytes_read<len; bytes_read += new_bytes) {
+ new_bytes = parport_read(port, buffer+bytes_read,
+ len-bytes_read);
+ if(new_bytes < 0) break;
+ }
+ }
+ return bytes_read;
+}
+
+static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock)
+{
+ struct pp_cam_entry *cam = privdata;
+ int read_bytes = 0;
+ int i, endseen, block_size, new_bytes;
+
+ if(cam == NULL) {
+ DBG("Internal driver error: cam is NULL\n");
+ return -EINVAL;
+ }
+ if(buffer == NULL) {
+ DBG("Internal driver error: buffer is NULL\n");
+ return -EINVAL;
+ }
+ //if(cam->streaming) DBG("%d / %d\n", cam->image_ready, noblock);
+ if( cam->stream_irq ) {
+ DBG("%d\n", cam->image_ready);
+ cam->image_ready--;
+ }
+ cam->image_complete=0;
+ if (0/*cam->streaming*/) {
+ if(!cam->image_ready) {
+ if(noblock) return -EWOULDBLOCK;
+ interruptible_sleep_on(&cam->wq_stream);
+ if( signal_pending(current) ) return -EINTR;
+ DBG("%d\n", cam->image_ready);
+ }
+ } else {
+ if (ReverseSetup(cam, 1)) {
+ DBG("unable to ReverseSetup\n");
+ return -EIO;
+ }
+ }
+ endseen = 0;
+ block_size = PARPORT_CHUNK_SIZE;
+ while( !cam->image_complete ) {
+ cond_resched();
+
+ new_bytes = cpia_pp_read(cam->port, buffer, block_size );
+ if( new_bytes <= 0 ) {
+ break;
+ }
+ i=-1;
+ while(++i<new_bytes && endseen<4) {
+ if(*buffer==EOI) {
+ endseen++;
+ } else {
+ endseen=0;
+ }
+ buffer++;
+ }
+ read_bytes += i;
+ if( endseen==4 ) {
+ cam->image_complete=1;
+ break;
+ }
+ if( CPIA_MAX_IMAGE_SIZE-read_bytes <= PARPORT_CHUNK_SIZE ) {
+ block_size=CPIA_MAX_IMAGE_SIZE-read_bytes;
+ }
+ }
+ EndTransferMode(cam);
+ return cam->image_complete ? read_bytes : -EIO;
+}
+/****************************************************************************
+ *
+ * cpia_pp_transferCmd
+ *
+ ***************************************************************************/
+static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data)
+{
+ int err;
+ int retval=0;
+ int databytes;
+ struct pp_cam_entry *cam = privdata;
+
+ if(cam == NULL) {
+ DBG("Internal driver error: cam is NULL\n");
+ return -EINVAL;
+ }
+ if(command == NULL) {
+ DBG("Internal driver error: command is NULL\n");
+ return -EINVAL;
+ }
+ databytes = (((int)command[7])<<8) | command[6];
+ if ((err = WritePacket(cam, command, PACKET_LENGTH)) < 0) {
+ DBG("Error writing command\n");
+ return err;
+ }
+ if(command[0] == DATA_IN) {
+ u8 buffer[8];
+ if(data == NULL) {
+ DBG("Internal driver error: data is NULL\n");
+ return -EINVAL;
+ }
+ if((err = ReadPacket(cam, buffer, 8)) < 0) {
+ DBG("Error reading command result\n");
+ return err;
+ }
+ memcpy(data, buffer, databytes);
+ } else if(command[0] == DATA_OUT) {
+ if(databytes > 0) {
+ if(data == NULL) {
+ DBG("Internal driver error: data is NULL\n");
+ retval = -EINVAL;
+ } else {
+ if((err=WritePacket(cam, data, databytes)) < 0){
+ DBG("Error writing command data\n");
+ return err;
+ }
+ }
+ }
+ } else {
+ DBG("Unexpected first byte of command: %x\n", command[0]);
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+/****************************************************************************
+ *
+ * cpia_pp_open
+ *
+ ***************************************************************************/
+static int cpia_pp_open(void *privdata)
+{
+ struct pp_cam_entry *cam = (struct pp_cam_entry *)privdata;
+
+ if (cam == NULL)
+ return -EINVAL;
+
+ if(cam->open_count == 0) {
+ if (parport_claim(cam->pdev)) {
+ DBG("failed to claim the port\n");
+ return -EBUSY;
+ }
+ parport_negotiate(cam->port, IEEE1284_MODE_COMPAT);
+ parport_data_forward(cam->port);
+ parport_write_control(cam->port, PARPORT_CONTROL_SELECT);
+ udelay(50);
+ parport_write_control(cam->port,
+ PARPORT_CONTROL_SELECT
+ | PARPORT_CONTROL_INIT);
+ }
+
+ ++cam->open_count;
+
+ return 0;
+}
+
+/****************************************************************************
+ *
+ * cpia_pp_registerCallback
+ *
+ ***************************************************************************/
+static int cpia_pp_registerCallback(void *privdata, void (*cb)(void *cbdata), void *cbdata)
+{
+ struct pp_cam_entry *cam = privdata;
+ int retval = 0;
+
+ if(cam->port->irq != PARPORT_IRQ_NONE) {
+ INIT_WORK(&cam->cb_task, cb, cbdata);
+ } else {
+ retval = -1;
+ }
+ return retval;
+}
+
+/****************************************************************************
+ *
+ * cpia_pp_close
+ *
+ ***************************************************************************/
+static int cpia_pp_close(void *privdata)
+{
+ struct pp_cam_entry *cam = privdata;
+ if (--cam->open_count == 0) {
+ parport_release(cam->pdev);
+ }
+ return 0;
+}
+
+/****************************************************************************
+ *
+ * cpia_pp_register
+ *
+ ***************************************************************************/
+static int cpia_pp_register(struct parport *port)
+{
+ struct pardevice *pdev = NULL;
+ struct pp_cam_entry *cam;
+ struct cam_data *cpia;
+
+ if (!(port->modes & PARPORT_MODE_PCSPP)) {
+ LOG("port is not supported by CPiA driver\n");
+ return -ENXIO;
+ }
+
+ cam = kzalloc(sizeof(struct pp_cam_entry), GFP_KERNEL);
+ if (cam == NULL) {
+ LOG("failed to allocate camera structure\n");
+ return -ENOMEM;
+ }
+
+ pdev = parport_register_device(port, "cpia_pp", NULL, NULL,
+ NULL, 0, cam);
+
+ if (!pdev) {
+ LOG("failed to parport_register_device\n");
+ kfree(cam);
+ return -ENXIO;
+ }
+
+ cam->pdev = pdev;
+ cam->port = port;
+ init_waitqueue_head(&cam->wq_stream);
+
+ cam->streaming = 0;
+ cam->stream_irq = 0;
+
+ if((cpia = cpia_register_camera(&cpia_pp_ops, cam)) == NULL) {
+ LOG("failed to cpia_register_camera\n");
+ parport_unregister_device(pdev);
+ kfree(cam);
+ return -ENXIO;
+ }
+ spin_lock( &cam_list_lock_pp );
+ list_add( &cpia->cam_data_list, &cam_list );
+ spin_unlock( &cam_list_lock_pp );
+
+ return 0;
+}
+
+static void cpia_pp_detach (struct parport *port)
+{
+ struct list_head *tmp;
+ struct cam_data *cpia = NULL;
+ struct pp_cam_entry *cam;
+
+ spin_lock( &cam_list_lock_pp );
+ list_for_each (tmp, &cam_list) {
+ cpia = list_entry(tmp, struct cam_data, cam_data_list);
+ cam = (struct pp_cam_entry *) cpia->lowlevel_data;
+ if (cam && cam->port->number == port->number) {
+ list_del(&cpia->cam_data_list);
+ break;
+ }
+ cpia = NULL;
+ }
+ spin_unlock( &cam_list_lock_pp );
+
+ if (!cpia) {
+ DBG("cpia_pp_detach failed to find cam_data in cam_list\n");
+ return;
+ }
+
+ cam = (struct pp_cam_entry *) cpia->lowlevel_data;
+ cpia_unregister_camera(cpia);
+ if(cam->open_count > 0)
+ cpia_pp_close(cam);
+ parport_unregister_device(cam->pdev);
+ cpia->lowlevel_data = NULL;
+ kfree(cam);
+}
+
+static void cpia_pp_attach (struct parport *port)
+{
+ unsigned int i;
+
+ switch (parport_nr[0])
+ {
+ case PPCPIA_PARPORT_UNSPEC:
+ case PPCPIA_PARPORT_AUTO:
+ if (port->probe_info[0].class != PARPORT_CLASS_MEDIA ||
+ port->probe_info[0].cmdset == NULL ||
+ strncmp(port->probe_info[0].cmdset, "CPIA_1", 6) != 0)
+ return;
+
+ cpia_pp_register(port);
+
+ break;
+
+ default:
+ for (i = 0; i < PARPORT_MAX; ++i) {
+ if (port->number == parport_nr[i]) {
+ cpia_pp_register(port);
+ break;
+ }
+ }
+ break;
+ }
+}
+
+static struct parport_driver cpia_pp_driver = {
+ .name = "cpia_pp",
+ .attach = cpia_pp_attach,
+ .detach = cpia_pp_detach,
+};
+
+int cpia_pp_init(void)
+{
+ printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT,
+ CPIA_PP_MAJ_VER,CPIA_PP_MIN_VER,CPIA_PP_PATCH_VER);
+
+ if(parport_nr[0] == PPCPIA_PARPORT_OFF) {
+ printk(" disabled\n");
+ return 0;
+ }
+
+ spin_lock_init( &cam_list_lock_pp );
+
+ if (parport_register_driver (&cpia_pp_driver)) {
+ LOG ("unable to register with parport\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ if (parport[0]) {
+ /* The user gave some parameters. Let's see what they were. */
+ if (!strncmp(parport[0], "auto", 4)) {
+ parport_nr[0] = PPCPIA_PARPORT_AUTO;
+ } else {
+ int n;
+ for (n = 0; n < PARPORT_MAX && parport[n]; n++) {
+ if (!strncmp(parport[n], "none", 4)) {
+ parport_nr[n] = PPCPIA_PARPORT_NONE;
+ } else {
+ char *ep;
+ unsigned long r = simple_strtoul(parport[n], &ep, 0);
+ if (ep != parport[n]) {
+ parport_nr[n] = r;
+ } else {
+ LOG("bad port specifier `%s'\n", parport[n]);
+ return -ENODEV;
+ }
+ }
+ }
+ }
+ }
+ return cpia_pp_init();
+}
+
+void cleanup_module(void)
+{
+ parport_unregister_driver (&cpia_pp_driver);
+ return;
+}
+
+#else /* !MODULE */
+
+static int __init cpia_pp_setup(char *str)
+{
+ if (!strncmp(str, "parport", 7)) {
+ int n = simple_strtoul(str + 7, NULL, 10);
+ if (parport_ptr < PARPORT_MAX) {
+ parport_nr[parport_ptr++] = n;
+ } else {
+ LOG("too many ports, %s ignored.\n", str);
+ }
+ } else if (!strcmp(str, "auto")) {
+ parport_nr[0] = PPCPIA_PARPORT_AUTO;
+ } else if (!strcmp(str, "none")) {
+ parport_nr[parport_ptr++] = PPCPIA_PARPORT_NONE;
+ }
+
+ return 1;
+}
+
+__setup("cpia_pp=", cpia_pp_setup);
+
+#endif /* !MODULE */
diff --git a/linux/drivers/media/video/cpia_usb.c b/linux/drivers/media/video/cpia_usb.c
new file mode 100644
index 000000000..9c49a4b00
--- /dev/null
+++ b/linux/drivers/media/video/cpia_usb.c
@@ -0,0 +1,649 @@
+/*
+ * cpia_usb CPiA USB driver
+ *
+ * Supports CPiA based parallel port Video Camera's.
+ *
+ * Copyright (C) 1999 Jochen Scharrlach <Jochen.Scharrlach@schwaben.de>
+ * Copyright (C) 1999, 2000 Johannes Erdfelt <johannes@erdfelt.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */
+/* #define _CPIA_DEBUG_ 1 */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/usb.h>
+
+#include "cpia.h"
+
+#define USB_REQ_CPIA_GRAB_FRAME 0xC1
+#define USB_REQ_CPIA_UPLOAD_FRAME 0xC2
+#define WAIT_FOR_NEXT_FRAME 0
+#define FORCE_FRAME_UPLOAD 1
+
+#define FRAMES_PER_DESC 10
+#define FRAME_SIZE_PER_DESC 960 /* Shouldn't be hardcoded */
+#define CPIA_NUMSBUF 2
+#define STREAM_BUF_SIZE (PAGE_SIZE * 4)
+#define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2)
+
+struct cpia_sbuf {
+ char *data;
+ struct urb *urb;
+};
+
+#define FRAMEBUF_LEN (CPIA_MAX_FRAME_SIZE+100)
+enum framebuf_status {
+ FRAME_EMPTY,
+ FRAME_READING,
+ FRAME_READY,
+ FRAME_ERROR,
+};
+
+struct framebuf {
+ int length;
+ enum framebuf_status status;
+ u8 data[FRAMEBUF_LEN];
+ struct framebuf *next;
+};
+
+struct usb_cpia {
+ /* Device structure */
+ struct usb_device *dev;
+
+ unsigned char iface;
+ wait_queue_head_t wq_stream;
+
+ int cursbuf; /* Current receiving sbuf */
+ struct cpia_sbuf sbuf[CPIA_NUMSBUF]; /* Double buffering */
+
+ int streaming;
+ int open;
+ int present;
+ struct framebuf *buffers[3];
+ struct framebuf *curbuff, *workbuff;
+};
+
+static int cpia_usb_open(void *privdata);
+static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata),
+ void *cbdata);
+static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data);
+static int cpia_usb_streamStart(void *privdata);
+static int cpia_usb_streamStop(void *privdata);
+static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock);
+static int cpia_usb_close(void *privdata);
+
+#define ABOUT "USB driver for Vision CPiA based cameras"
+
+static struct cpia_camera_ops cpia_usb_ops = {
+ cpia_usb_open,
+ cpia_usb_registerCallback,
+ cpia_usb_transferCmd,
+ cpia_usb_streamStart,
+ cpia_usb_streamStop,
+ cpia_usb_streamRead,
+ cpia_usb_close,
+ 0,
+ THIS_MODULE
+};
+
+static LIST_HEAD(cam_list);
+static spinlock_t cam_list_lock_usb;
+
+static void cpia_usb_complete(struct urb *urb, struct pt_regs *regs)
+{
+ int i;
+ char *cdata;
+ struct usb_cpia *ucpia;
+
+ if (!urb || !urb->context)
+ return;
+
+ ucpia = (struct usb_cpia *) urb->context;
+
+ if (!ucpia->dev || !ucpia->streaming || !ucpia->present || !ucpia->open)
+ return;
+
+ if (ucpia->workbuff->status == FRAME_EMPTY) {
+ ucpia->workbuff->status = FRAME_READING;
+ ucpia->workbuff->length = 0;
+ }
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int n = urb->iso_frame_desc[i].actual_length;
+ int st = urb->iso_frame_desc[i].status;
+
+ cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+ if (st)
+ printk(KERN_DEBUG "cpia data error: [%d] len=%d, status=%X\n", i, n, st);
+
+ if (FRAMEBUF_LEN < ucpia->workbuff->length + n) {
+ printk(KERN_DEBUG "cpia: scratch buf overflow!scr_len: %d, n: %d\n", ucpia->workbuff->length, n);
+ return;
+ }
+
+ if (n) {
+ if ((ucpia->workbuff->length > 0) ||
+ (0x19 == cdata[0] && 0x68 == cdata[1])) {
+ memcpy(ucpia->workbuff->data + ucpia->workbuff->length, cdata, n);
+ ucpia->workbuff->length += n;
+ } else
+ DBG("Ignoring packet!\n");
+ } else {
+ if (ucpia->workbuff->length > 4 &&
+ 0xff == ucpia->workbuff->data[ucpia->workbuff->length-1] &&
+ 0xff == ucpia->workbuff->data[ucpia->workbuff->length-2] &&
+ 0xff == ucpia->workbuff->data[ucpia->workbuff->length-3] &&
+ 0xff == ucpia->workbuff->data[ucpia->workbuff->length-4]) {
+ ucpia->workbuff->status = FRAME_READY;
+ ucpia->curbuff = ucpia->workbuff;
+ ucpia->workbuff = ucpia->workbuff->next;
+ ucpia->workbuff->status = FRAME_EMPTY;
+ ucpia->workbuff->length = 0;
+
+ if (waitqueue_active(&ucpia->wq_stream))
+ wake_up_interruptible(&ucpia->wq_stream);
+ }
+ }
+ }
+
+ /* resubmit */
+ urb->dev = ucpia->dev;
+ if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0)
+ printk(KERN_ERR "%s: usb_submit_urb ret %d\n", __FUNCTION__, i);
+}
+
+static int cpia_usb_open(void *privdata)
+{
+ struct usb_cpia *ucpia = (struct usb_cpia *) privdata;
+ struct urb *urb;
+ int ret, retval = 0, fx, err;
+
+ if (!ucpia)
+ return -EINVAL;
+
+ ucpia->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
+ if (!ucpia->sbuf[0].data)
+ return -EINVAL;
+
+ ucpia->sbuf[1].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
+ if (!ucpia->sbuf[1].data) {
+ retval = -EINVAL;
+ goto error_0;
+ }
+
+ ret = usb_set_interface(ucpia->dev, ucpia->iface, 3);
+ if (ret < 0) {
+ printk(KERN_ERR "cpia_usb_open: usb_set_interface error (ret = %d)\n", ret);
+ retval = -EBUSY;
+ goto error_1;
+ }
+
+ ucpia->buffers[0]->status = FRAME_EMPTY;
+ ucpia->buffers[0]->length = 0;
+ ucpia->buffers[1]->status = FRAME_EMPTY;
+ ucpia->buffers[1]->length = 0;
+ ucpia->buffers[2]->status = FRAME_EMPTY;
+ ucpia->buffers[2]->length = 0;
+ ucpia->curbuff = ucpia->buffers[0];
+ ucpia->workbuff = ucpia->buffers[1];
+
+ /* We double buffer the Iso lists, and also know the polling
+ * interval is every frame (1 == (1 << (bInterval -1))).
+ */
+ urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL);
+ if (!urb) {
+ printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n");
+ retval = -ENOMEM;
+ goto error_1;
+ }
+
+ ucpia->sbuf[0].urb = urb;
+ urb->dev = ucpia->dev;
+ urb->context = ucpia;
+ urb->pipe = usb_rcvisocpipe(ucpia->dev, 1);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = ucpia->sbuf[0].data;
+ urb->complete = cpia_usb_complete;
+ urb->number_of_packets = FRAMES_PER_DESC;
+ urb->interval = 1;
+ urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
+ for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
+ urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx;
+ urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
+ }
+
+ urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL);
+ if (!urb) {
+ printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 1\n");
+ retval = -ENOMEM;
+ goto error_urb0;
+ }
+
+ ucpia->sbuf[1].urb = urb;
+ urb->dev = ucpia->dev;
+ urb->context = ucpia;
+ urb->pipe = usb_rcvisocpipe(ucpia->dev, 1);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = ucpia->sbuf[1].data;
+ urb->complete = cpia_usb_complete;
+ urb->number_of_packets = FRAMES_PER_DESC;
+ urb->interval = 1;
+ urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
+ for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
+ urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx;
+ urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
+ }
+
+ /* queue the ISO urbs, and resubmit in the completion handler */
+ err = usb_submit_urb(ucpia->sbuf[0].urb, GFP_KERNEL);
+ if (err) {
+ printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 0 ret %d\n",
+ err);
+ goto error_urb1;
+ }
+ err = usb_submit_urb(ucpia->sbuf[1].urb, GFP_KERNEL);
+ if (err) {
+ printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 1 ret %d\n",
+ err);
+ goto error_urb1;
+ }
+
+ ucpia->streaming = 1;
+ ucpia->open = 1;
+
+ return 0;
+
+error_urb1: /* free urb 1 */
+ usb_free_urb(ucpia->sbuf[1].urb);
+ ucpia->sbuf[1].urb = NULL;
+error_urb0: /* free urb 0 */
+ usb_free_urb(ucpia->sbuf[0].urb);
+ ucpia->sbuf[0].urb = NULL;
+error_1:
+ kfree (ucpia->sbuf[1].data);
+ ucpia->sbuf[1].data = NULL;
+error_0:
+ kfree (ucpia->sbuf[0].data);
+ ucpia->sbuf[0].data = NULL;
+
+ return retval;
+}
+
+//
+// convenience functions
+//
+
+/****************************************************************************
+ *
+ * WritePacket
+ *
+ ***************************************************************************/
+static int WritePacket(struct usb_device *udev, const u8 *packet, u8 *buf, size_t size)
+{
+ if (!packet)
+ return -EINVAL;
+
+ return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ packet[1] + (packet[0] << 8),
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ packet[2] + (packet[3] << 8),
+ packet[4] + (packet[5] << 8), buf, size, 1000);
+}
+
+/****************************************************************************
+ *
+ * ReadPacket
+ *
+ ***************************************************************************/
+static int ReadPacket(struct usb_device *udev, u8 *packet, u8 *buf, size_t size)
+{
+ if (!packet || size <= 0)
+ return -EINVAL;
+
+ return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ packet[1] + (packet[0] << 8),
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ packet[2] + (packet[3] << 8),
+ packet[4] + (packet[5] << 8), buf, size, 1000);
+}
+
+static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data)
+{
+ int err = 0;
+ int databytes;
+ struct usb_cpia *ucpia = (struct usb_cpia *)privdata;
+ struct usb_device *udev = ucpia->dev;
+
+ if (!udev) {
+ DBG("Internal driver error: udev is NULL\n");
+ return -EINVAL;
+ }
+
+ if (!command) {
+ DBG("Internal driver error: command is NULL\n");
+ return -EINVAL;
+ }
+
+ databytes = (((int)command[7])<<8) | command[6];
+
+ if (command[0] == DATA_IN) {
+ u8 buffer[8];
+
+ if (!data) {
+ DBG("Internal driver error: data is NULL\n");
+ return -EINVAL;
+ }
+
+ err = ReadPacket(udev, command, buffer, 8);
+ if (err < 0)
+ return err;
+
+ memcpy(data, buffer, databytes);
+ } else if(command[0] == DATA_OUT)
+ WritePacket(udev, command, data, databytes);
+ else {
+ DBG("Unexpected first byte of command: %x\n", command[0]);
+ err = -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata),
+ void *cbdata)
+{
+ return -ENODEV;
+}
+
+static int cpia_usb_streamStart(void *privdata)
+{
+ return -ENODEV;
+}
+
+static int cpia_usb_streamStop(void *privdata)
+{
+ return -ENODEV;
+}
+
+static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock)
+{
+ struct usb_cpia *ucpia = (struct usb_cpia *) privdata;
+ struct framebuf *mybuff;
+
+ if (!ucpia || !ucpia->present)
+ return -1;
+
+ if (ucpia->curbuff->status != FRAME_READY)
+ interruptible_sleep_on(&ucpia->wq_stream);
+ else
+ DBG("Frame already waiting!\n");
+
+ mybuff = ucpia->curbuff;
+
+ if (!mybuff)
+ return -1;
+
+ if (mybuff->status != FRAME_READY || mybuff->length < 4) {
+ DBG("Something went wrong!\n");
+ return -1;
+ }
+
+ memcpy(frame, mybuff->data, mybuff->length);
+ mybuff->status = FRAME_EMPTY;
+
+/* DBG("read done, %d bytes, Header: %x/%x, Footer: %x%x%x%x\n", */
+/* mybuff->length, frame[0], frame[1], */
+/* frame[mybuff->length-4], frame[mybuff->length-3], */
+/* frame[mybuff->length-2], frame[mybuff->length-1]); */
+
+ return mybuff->length;
+}
+
+static void cpia_usb_free_resources(struct usb_cpia *ucpia, int try)
+{
+ if (!ucpia->streaming)
+ return;
+
+ ucpia->streaming = 0;
+
+ /* Set packet size to 0 */
+ if (try) {
+ int ret;
+
+ ret = usb_set_interface(ucpia->dev, ucpia->iface, 0);
+ if (ret < 0) {
+ printk(KERN_ERR "usb_set_interface error (ret = %d)\n", ret);
+ return;
+ }
+ }
+
+ /* Unschedule all of the iso td's */
+ if (ucpia->sbuf[1].urb) {
+ usb_kill_urb(ucpia->sbuf[1].urb);
+ usb_free_urb(ucpia->sbuf[1].urb);
+ ucpia->sbuf[1].urb = NULL;
+ }
+
+ kfree(ucpia->sbuf[1].data);
+ ucpia->sbuf[1].data = NULL;
+
+ if (ucpia->sbuf[0].urb) {
+ usb_kill_urb(ucpia->sbuf[0].urb);
+ usb_free_urb(ucpia->sbuf[0].urb);
+ ucpia->sbuf[0].urb = NULL;
+ }
+
+ kfree(ucpia->sbuf[0].data);
+ ucpia->sbuf[0].data = NULL;
+}
+
+static int cpia_usb_close(void *privdata)
+{
+ struct usb_cpia *ucpia = (struct usb_cpia *) privdata;
+
+ if(!ucpia)
+ return -ENODEV;
+
+ ucpia->open = 0;
+
+ /* ucpia->present = 0 protects against trying to reset the
+ * alt setting if camera is physically disconnected while open */
+ cpia_usb_free_resources(ucpia, ucpia->present);
+
+ return 0;
+}
+
+int cpia_usb_init(void)
+{
+ /* return -ENODEV; */
+ return 0;
+}
+
+/* Probing and initializing */
+
+static int cpia_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct usb_host_interface *interface;
+ struct usb_cpia *ucpia;
+ struct cam_data *cam;
+ int ret;
+
+ /* A multi-config CPiA camera? */
+ if (udev->descriptor.bNumConfigurations != 1)
+ return -ENODEV;
+
+ interface = intf->cur_altsetting;
+
+ printk(KERN_INFO "USB CPiA camera found\n");
+
+ ucpia = kzalloc(sizeof(*ucpia), GFP_KERNEL);
+ if (!ucpia) {
+ printk(KERN_ERR "couldn't kmalloc cpia struct\n");
+ return -ENOMEM;
+ }
+
+ ucpia->dev = udev;
+ ucpia->iface = interface->desc.bInterfaceNumber;
+ init_waitqueue_head(&ucpia->wq_stream);
+
+ ucpia->buffers[0] = vmalloc(sizeof(*ucpia->buffers[0]));
+ if (!ucpia->buffers[0]) {
+ printk(KERN_ERR "couldn't vmalloc frame buffer 0\n");
+ goto fail_alloc_0;
+ }
+
+ ucpia->buffers[1] = vmalloc(sizeof(*ucpia->buffers[1]));
+ if (!ucpia->buffers[1]) {
+ printk(KERN_ERR "couldn't vmalloc frame buffer 1\n");
+ goto fail_alloc_1;
+ }
+
+ ucpia->buffers[2] = vmalloc(sizeof(*ucpia->buffers[2]));
+ if (!ucpia->buffers[2]) {
+ printk(KERN_ERR "couldn't vmalloc frame buffer 2\n");
+ goto fail_alloc_2;
+ }
+
+ ucpia->buffers[0]->next = ucpia->buffers[1];
+ ucpia->buffers[1]->next = ucpia->buffers[2];
+ ucpia->buffers[2]->next = ucpia->buffers[0];
+
+ ret = usb_set_interface(udev, ucpia->iface, 0);
+ if (ret < 0) {
+ printk(KERN_ERR "cpia_probe: usb_set_interface error (ret = %d)\n", ret);
+ /* goto fail_all; */
+ }
+
+ /* Before register_camera, important */
+ ucpia->present = 1;
+
+ cam = cpia_register_camera(&cpia_usb_ops, ucpia);
+ if (!cam) {
+ LOG("failed to cpia_register_camera\n");
+ goto fail_all;
+ }
+
+ spin_lock( &cam_list_lock_usb );
+ list_add( &cam->cam_data_list, &cam_list );
+ spin_unlock( &cam_list_lock_usb );
+
+ usb_set_intfdata(intf, cam);
+ return 0;
+
+fail_all:
+ vfree(ucpia->buffers[2]);
+ ucpia->buffers[2] = NULL;
+fail_alloc_2:
+ vfree(ucpia->buffers[1]);
+ ucpia->buffers[1] = NULL;
+fail_alloc_1:
+ vfree(ucpia->buffers[0]);
+ ucpia->buffers[0] = NULL;
+fail_alloc_0:
+ kfree(ucpia);
+ return -EIO;
+}
+
+static void cpia_disconnect(struct usb_interface *intf);
+
+static struct usb_device_id cpia_id_table [] = {
+ { USB_DEVICE(0x0553, 0x0002) },
+ { USB_DEVICE(0x0813, 0x0001) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, cpia_id_table);
+MODULE_LICENSE("GPL");
+
+
+static struct usb_driver cpia_driver = {
+ .name = "cpia",
+ .probe = cpia_probe,
+ .disconnect = cpia_disconnect,
+ .id_table = cpia_id_table,
+};
+
+static void cpia_disconnect(struct usb_interface *intf)
+{
+ struct cam_data *cam = usb_get_intfdata(intf);
+ struct usb_cpia *ucpia;
+ struct usb_device *udev;
+
+ usb_set_intfdata(intf, NULL);
+ if (!cam)
+ return;
+
+ ucpia = (struct usb_cpia *) cam->lowlevel_data;
+ spin_lock( &cam_list_lock_usb );
+ list_del(&cam->cam_data_list);
+ spin_unlock( &cam_list_lock_usb );
+
+ ucpia->present = 0;
+
+ cpia_unregister_camera(cam);
+ if(ucpia->open)
+ cpia_usb_close(cam->lowlevel_data);
+
+ ucpia->curbuff->status = FRAME_ERROR;
+
+ if (waitqueue_active(&ucpia->wq_stream))
+ wake_up_interruptible(&ucpia->wq_stream);
+
+ udev = interface_to_usbdev(intf);
+
+ ucpia->curbuff = ucpia->workbuff = NULL;
+
+ vfree(ucpia->buffers[2]);
+ ucpia->buffers[2] = NULL;
+
+ vfree(ucpia->buffers[1]);
+ ucpia->buffers[1] = NULL;
+
+ vfree(ucpia->buffers[0]);
+ ucpia->buffers[0] = NULL;
+
+ cam->lowlevel_data = NULL;
+ kfree(ucpia);
+}
+
+static int __init usb_cpia_init(void)
+{
+ printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT,
+ CPIA_USB_MAJ_VER,CPIA_USB_MIN_VER,CPIA_USB_PATCH_VER);
+
+ spin_lock_init(&cam_list_lock_usb);
+ return usb_register(&cpia_driver);
+}
+
+static void __exit usb_cpia_cleanup(void)
+{
+ usb_deregister(&cpia_driver);
+}
+
+
+module_init (usb_cpia_init);
+module_exit (usb_cpia_cleanup);
+
diff --git a/linux/drivers/media/video/cs8420.h b/linux/drivers/media/video/cs8420.h
new file mode 100644
index 000000000..621c0c667
--- /dev/null
+++ b/linux/drivers/media/video/cs8420.h
@@ -0,0 +1,50 @@
+/* cs8420.h - cs8420 initializations
+ Copyright (C) 1999 Nathan Laredo (laredo@gnu.org)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+#ifndef __CS8420_H__
+#define __CS8420_H__
+
+/* Initialization Sequence */
+
+static __u8 init8420[] = {
+ 1, 0x01, 2, 0x02, 3, 0x00, 4, 0x46,
+ 5, 0x24, 6, 0x84, 18, 0x18, 19, 0x13,
+};
+
+#define INIT8420LEN (sizeof(init8420)/2)
+
+static __u8 mode8420pro[] = { /* professional output mode */
+ 32, 0xa1, 33, 0x00, 34, 0x00, 35, 0x00,
+ 36, 0x00, 37, 0x00, 38, 0x00, 39, 0x00,
+ 40, 0x00, 41, 0x00, 42, 0x00, 43, 0x00,
+ 44, 0x00, 45, 0x00, 46, 0x00, 47, 0x00,
+ 48, 0x00, 49, 0x00, 50, 0x00, 51, 0x00,
+ 52, 0x00, 53, 0x00, 54, 0x00, 55, 0x00,
+};
+#define MODE8420LEN (sizeof(mode8420pro)/2)
+
+static __u8 mode8420con[] = { /* consumer output mode */
+ 32, 0x20, 33, 0x00, 34, 0x00, 35, 0x48,
+ 36, 0x00, 37, 0x00, 38, 0x00, 39, 0x00,
+ 40, 0x00, 41, 0x00, 42, 0x00, 43, 0x00,
+ 44, 0x00, 45, 0x00, 46, 0x00, 47, 0x00,
+ 48, 0x00, 49, 0x00, 50, 0x00, 51, 0x00,
+ 52, 0x00, 53, 0x00, 54, 0x00, 55, 0x00,
+};
+
+#endif
diff --git a/linux/drivers/media/video/dabfirmware.h b/linux/drivers/media/video/dabfirmware.h
new file mode 100644
index 000000000..d14d80356
--- /dev/null
+++ b/linux/drivers/media/video/dabfirmware.h
@@ -0,0 +1,1408 @@
+/*
+ * dabdata.h - dab usb firmware and bitstream data
+ */
+
+static INTEL_HEX_RECORD firmware[] = {
+
+{ 2, 0x0000, 0, {0x21,0x57} },
+{ 3, 0x0003, 0, {0x02,0x01,0x66} },
+{ 3, 0x000b, 0, {0x02,0x01,0x66} },
+{ 3, 0x0013, 0, {0x02,0x01,0x66} },
+{ 3, 0x001b, 0, {0x02,0x01,0x66} },
+{ 3, 0x0023, 0, {0x02,0x01,0x66} },
+{ 3, 0x002b, 0, {0x02,0x01,0x66} },
+{ 3, 0x0033, 0, {0x02,0x03,0x0f} },
+{ 3, 0x003b, 0, {0x02,0x01,0x66} },
+{ 3, 0x0043, 0, {0x02,0x01,0x00} },
+{ 3, 0x004b, 0, {0x02,0x01,0x66} },
+{ 3, 0x0053, 0, {0x02,0x01,0x66} },
+{ 3, 0x005b, 0, {0x02,0x04,0xbd} },
+{ 3, 0x0063, 0, {0x02,0x01,0x67} },
+{ 3, 0x0100, 0, {0x02,0x0c,0x5a} },
+{ 3, 0x0104, 0, {0x02,0x01,0xed} },
+{ 3, 0x0108, 0, {0x02,0x02,0x51} },
+{ 3, 0x010c, 0, {0x02,0x02,0x7c} },
+{ 3, 0x0110, 0, {0x02,0x02,0xe4} },
+{ 1, 0x0114, 0, {0x32} },
+{ 1, 0x0118, 0, {0x32} },
+{ 3, 0x011c, 0, {0x02,0x05,0xfd} },
+{ 3, 0x0120, 0, {0x02,0x00,0x00} },
+{ 3, 0x0124, 0, {0x02,0x00,0x00} },
+{ 3, 0x0128, 0, {0x02,0x04,0x3c} },
+{ 3, 0x012c, 0, {0x02,0x04,0x6a} },
+{ 3, 0x0130, 0, {0x02,0x00,0x00} },
+{ 3, 0x0134, 0, {0x02,0x00,0x00} },
+{ 3, 0x0138, 0, {0x02,0x00,0x00} },
+{ 3, 0x013c, 0, {0x02,0x00,0x00} },
+{ 3, 0x0140, 0, {0x02,0x00,0x00} },
+{ 3, 0x0144, 0, {0x02,0x00,0x00} },
+{ 3, 0x0148, 0, {0x02,0x00,0x00} },
+{ 3, 0x014c, 0, {0x02,0x00,0x00} },
+{ 3, 0x0150, 0, {0x02,0x00,0x00} },
+{ 3, 0x0154, 0, {0x02,0x00,0x00} },
+{ 10, 0x0157, 0, {0x75,0x81,0x7f,0xe5,0x82,0x60,0x03,0x02,0x01,0x61} },
+{ 5, 0x0161, 0, {0x12,0x07,0x6f,0x21,0x64} },
+{ 1, 0x0166, 0, {0x32} },
+{ 14, 0x0167, 0, {0xc0,0xd0,0xc0,0x86,0xc0,0x82,0xc0,0x83,0xc0,0xe0,0x90,0x7f,0x97,0xe0} },
+{ 14, 0x0175, 0, {0x44,0x80,0xf0,0x90,0x7f,0x69,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} },
+{ 14, 0x0183, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} },
+{ 14, 0x0191, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0x90,0x7f,0x97,0xe0} },
+{ 3, 0x019f, 0, {0x55,0x7f,0xf0} },
+{ 14, 0x01a2, 0, {0x90,0x7f,0x9a,0xe0,0x30,0xe4,0x23,0x90,0x7f,0x68,0xf0,0xf0,0xf0,0xf0} },
+{ 14, 0x01b0, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} },
+{ 14, 0x01be, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} },
+{ 14, 0x01cc, 0, {0xe5,0xd8,0xc2,0xe3,0xf5,0xd8,0xd0,0xe0,0xd0,0x83,0xd0,0x82,0xd0,0x86} },
+{ 3, 0x01da, 0, {0xd0,0xd0,0x32} },
+{ 8, 0x01dd, 0, {0x75,0x86,0x00,0x90,0xff,0xc3,0x7c,0x05} },
+{ 7, 0x01e5, 0, {0xa3,0xe5,0x82,0x45,0x83,0x70,0xf9} },
+{ 1, 0x01ec, 0, {0x22} },
+{ 14, 0x01ed, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0xd0} },
+{ 14, 0x01fb, 0, {0x75,0xd0,0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91} },
+{ 13, 0x0209, 0, {0x90,0x88,0x00,0xe0,0xf5,0x41,0x90,0x7f,0xab,0x74,0x02,0xf0,0x90} },
+{ 9, 0x0216, 0, {0x7f,0xab,0x74,0x02,0xf0,0xe5,0x32,0x60,0x21} },
+{ 4, 0x021f, 0, {0x7a,0x00,0x7b,0x00} },
+{ 11, 0x0223, 0, {0xc3,0xea,0x94,0x18,0xeb,0x64,0x80,0x94,0x80,0x50,0x12} },
+{ 14, 0x022e, 0, {0x90,0x7f,0x69,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0x0a,0xba,0x00} },
+{ 2, 0x023c, 0, {0x01,0x0b} },
+{ 2, 0x023e, 0, {0x80,0xe3} },
+{ 2, 0x0240, 0, {0xd0,0x86} },
+{ 14, 0x0242, 0, {0xd0,0xd0,0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0} },
+{ 1, 0x0250, 0, {0x32} },
+{ 14, 0x0251, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} },
+{ 14, 0x025f, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xab,0x74} },
+{ 4, 0x026d, 0, {0x04,0xf0,0xd0,0x86} },
+{ 11, 0x0271, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x027c, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} },
+{ 14, 0x028a, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} },
+{ 13, 0x0298, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90} },
+{ 12, 0x02a5, 0, {0x7f,0xab,0x74,0x08,0xf0,0x75,0x6e,0x00,0x75,0x6f,0x02,0x12} },
+{ 6, 0x02b1, 0, {0x11,0x44,0x75,0x70,0x39,0x75} },
+{ 6, 0x02b7, 0, {0x71,0x0c,0x75,0x72,0x02,0x12} },
+{ 12, 0x02bd, 0, {0x11,0x75,0x90,0x7f,0xd6,0xe4,0xf0,0x75,0xd8,0x20,0xd0,0x86} },
+{ 14, 0x02c9, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} },
+{ 13, 0x02d7, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x02e4, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} },
+{ 14, 0x02f2, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xab,0x74} },
+{ 4, 0x0300, 0, {0x10,0xf0,0xd0,0x86} },
+{ 11, 0x0304, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x030f, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} },
+{ 14, 0x031d, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} },
+{ 12, 0x032b, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0x75,0x6e,0x00,0x75,0x6f,0x02} },
+{ 7, 0x0337, 0, {0x12,0x11,0x44,0x75,0x70,0x40,0x75} },
+{ 6, 0x033e, 0, {0x71,0x0c,0x75,0x72,0x02,0x12} },
+{ 14, 0x0344, 0, {0x11,0x75,0x90,0x7f,0xd6,0x74,0x02,0xf0,0x90,0x7f,0xd6,0x74,0x06,0xf0} },
+{ 5, 0x0352, 0, {0x75,0xd8,0x10,0xd0,0x86} },
+{ 14, 0x0357, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} },
+{ 13, 0x0365, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 13, 0x0372, 0, {0x90,0x7f,0xa5,0x74,0x80,0xf0,0x90,0x7f,0xa6,0x74,0x9a,0xf0,0x12} },
+{ 12, 0x037f, 0, {0x10,0x1b,0x90,0x7f,0xa6,0xe5,0x42,0xf0,0x12,0x10,0x1b,0x90} },
+{ 13, 0x038b, 0, {0x7f,0xa6,0xe5,0x43,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa5,0x74,0x40} },
+{ 1, 0x0398, 0, {0xf0} },
+{ 1, 0x0399, 0, {0x22} },
+{ 13, 0x039a, 0, {0x90,0x7f,0xa5,0x74,0x80,0xf0,0x90,0x7f,0xa6,0x74,0x9a,0xf0,0x12} },
+{ 12, 0x03a7, 0, {0x10,0x1b,0x90,0x7f,0xa6,0xe5,0x44,0xf0,0x12,0x10,0x1b,0x90} },
+{ 12, 0x03b3, 0, {0x7f,0xa6,0xe5,0x45,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa6,0xe5} },
+{ 11, 0x03bf, 0, {0x46,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa5,0x74,0x40,0xf0} },
+{ 1, 0x03ca, 0, {0x22} },
+{ 10, 0x03cb, 0, {0x75,0x44,0x02,0x75,0x45,0x00,0x75,0x46,0x00,0x12} },
+{ 9, 0x03d5, 0, {0x03,0x9a,0x75,0x42,0x03,0x75,0x43,0x00,0x12} },
+{ 2, 0x03de, 0, {0x03,0x72} },
+{ 1, 0x03e0, 0, {0x22} },
+{ 12, 0x03e1, 0, {0x90,0x88,0x00,0xe5,0x36,0xf0,0x90,0x88,0x00,0x74,0x10,0x25} },
+{ 9, 0x03ed, 0, {0x36,0xf0,0x12,0x01,0xdd,0x75,0x42,0x01,0x75} },
+{ 9, 0x03f6, 0, {0x43,0x18,0x12,0x03,0x72,0x75,0x44,0x02,0x75} },
+{ 9, 0x03ff, 0,{0x45,0x00,0x75,0x46,0x00,0x12,0x03,0x9a,0x75} },
+{ 8, 0x0408, 0,{0x42,0x03,0x75,0x43,0x44,0x12,0x03,0x72} },
+{ 1, 0x0410, 0,{0x22} },
+{ 14, 0x0411, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} },
+{ 14, 0x041f, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xaa,0x74} },
+{ 4, 0x042d, 0, {0x02,0xf0,0xd0,0x86} },
+{ 11, 0x0431, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x043c, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} },
+{ 14, 0x044a, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xa9,0x74} },
+{ 7, 0x0458, 0, {0x04,0xf0,0x75,0x30,0x01,0xd0,0x86} },
+{ 11, 0x045f, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x046a, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} },
+{ 14, 0x0478, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xaa,0x74} },
+{ 7, 0x0486, 0, {0x04,0xf0,0x75,0x31,0x01,0xd0,0x86} },
+{ 11, 0x048d, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x0498, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} },
+{ 12, 0x04a6, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe5,0xf5,0x91,0xd0,0x86} },
+{ 11, 0x04b2, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x04bd, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} },
+{ 12, 0x04cb, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe7,0xf5,0x91,0xd0,0x86} },
+{ 11, 0x04d7, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 12, 0x04e2, 0, {0x90,0x7f,0xea,0xe0,0xfa,0x8a,0x20,0x90,0x7f,0x96,0xe4,0xf0} },
+{ 1, 0x04ee, 0, {0x22} },
+{ 7, 0x04ef, 0, {0x90,0x7f,0xea,0xe0,0xfa,0x8a,0x21} },
+{ 1, 0x04f6, 0, {0x22} },
+{ 14, 0x04f7, 0, {0x90,0x17,0x13,0xe0,0xfa,0x90,0x17,0x15,0xe0,0xfb,0x74,0x80,0x2a,0xfa} },
+{ 14, 0x0505, 0, {0x74,0x80,0x2b,0xfb,0xea,0x03,0x03,0x54,0x3f,0xfc,0xea,0xc4,0x23,0x54} },
+{ 14, 0x0513, 0, {0x1f,0xfa,0x2c,0xfa,0xeb,0x03,0x03,0x54,0x3f,0xfc,0xeb,0xc4,0x23,0x54} },
+{ 11, 0x0521, 0, {0x1f,0xfb,0x2c,0xfb,0x90,0x17,0x0a,0xe0,0xfc,0x60,0x02} },
+{ 2, 0x052c, 0, {0x7a,0x00} },
+{ 7, 0x052e, 0, {0x90,0x17,0x0c,0xe0,0xfc,0x60,0x02} },
+{ 2, 0x0535, 0, {0x7b,0x00} },
+{ 11, 0x0537, 0, {0xea,0x2b,0xfc,0xc3,0x13,0xf5,0x3a,0x75,0x44,0x02,0x8b} },
+{ 7, 0x0542, 0, {0x45,0x8a,0x46,0x12,0x03,0x9a,0x75} },
+{ 9, 0x0549, 0, {0x6e,0x08,0x75,0x6f,0x00,0x12,0x11,0x44,0x75} },
+{ 4, 0x0552, 0, {0x70,0x47,0x75,0x71} },
+{ 8, 0x0556, 0, {0x0c,0x75,0x72,0x02,0x12,0x11,0x75,0x85} },
+{ 5, 0x055e, 0, {0x3a,0x73,0x12,0x11,0xa0} },
+{ 1, 0x0563, 0, {0x22} },
+{ 14, 0x0564, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x80,0x65,0x02,0xf0,0x90} },
+{ 14, 0x0572, 0, {0x7f,0xeb,0xe0,0xfa,0x90,0x7f,0xea,0xe0,0xfb,0x90,0x7f,0xef,0xe0,0xfc} },
+{ 14, 0x0580, 0, {0x33,0x95,0xe0,0xfd,0x8c,0x05,0x7c,0x00,0x90,0x7f,0xee,0xe0,0xfe,0x33} },
+{ 14, 0x058e, 0, {0x95,0xe0,0xff,0xec,0x2e,0xfc,0xed,0x3f,0xfd,0x90,0x7f,0xe9,0xe0,0xfe} },
+{ 5, 0x059c, 0, {0xbe,0x01,0x02,0x80,0x03} },
+{ 3, 0x05a1, 0, {0x02,0x05,0xf9} },
+{ 6, 0x05a4, 0, {0xbc,0x01,0x21,0xbd,0x00,0x1e} },
+{ 14, 0x05aa, 0, {0xea,0xc4,0x03,0x54,0xf8,0xfc,0xeb,0x25,0xe0,0xfd,0x2c,0x24,0x00,0xfc} },
+{ 14, 0x05b8, 0, {0xe4,0x34,0x17,0xfd,0x90,0x7e,0xc0,0xe0,0xfe,0x8c,0x82,0x8d,0x83,0xf0} },
+{ 2, 0x05c6, 0, {0x80,0x31} },
+{ 14, 0x05c8, 0, {0xea,0xc4,0x03,0x54,0xf8,0xfa,0xeb,0x25,0xe0,0xfb,0x2a,0xfa,0x24,0x00} },
+{ 14, 0x05d6, 0, {0xfb,0xe4,0x34,0x17,0xfc,0x90,0x7e,0xc0,0xe0,0xfd,0x8b,0x82,0x8c,0x83} },
+{ 14, 0x05e4, 0, {0xf0,0x74,0x01,0x2a,0x24,0x00,0xfa,0xe4,0x34,0x17,0xfb,0x90,0x7e,0xc1} },
+{ 7, 0x05f2, 0, {0xe0,0xfc,0x8a,0x82,0x8b,0x83,0xf0} },
+{ 3, 0x05f9, 0, {0x75,0x38,0x01} },
+{ 1, 0x05fc, 0, {0x22} },
+{ 14, 0x05fd, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} },
+{ 14, 0x060b, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} },
+{ 13, 0x0619, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90} },
+{ 13, 0x0626, 0, {0x7f,0xaa,0x74,0x01,0xf0,0x12,0x05,0x64,0x75,0x37,0x00,0xd0,0x86} },
+{ 14, 0x0633, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} },
+{ 13, 0x0641, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 14, 0x064e, 0, {0x90,0x7f,0xeb,0xe0,0xfa,0x90,0x7f,0xea,0xe0,0xfb,0x90,0x7f,0xee,0xe0} },
+{ 14, 0x065c, 0, {0xfc,0x33,0x95,0xe0,0xfd,0x90,0x7f,0x96,0xe0,0xfe,0x90,0x7f,0x96,0x74} },
+{ 14, 0x066a, 0, {0x80,0x65,0x06,0xf0,0x90,0x7f,0x00,0x74,0x01,0xf0,0xea,0xc4,0x03,0x54} },
+{ 14, 0x0678, 0, {0xf8,0xfe,0xeb,0x25,0xe0,0xfb,0x2e,0xfe,0x24,0x00,0xfb,0xe4,0x34,0x17} },
+{ 14, 0x0686, 0, {0xff,0x8b,0x82,0x8f,0x83,0xe0,0xfb,0x74,0x01,0x2e,0x24,0x00,0xfe,0xe4} },
+{ 14, 0x0694, 0, {0x34,0x17,0xff,0x8e,0x82,0x8f,0x83,0xe0,0xfe,0x90,0x7f,0xe9,0xe0,0xff} },
+{ 3, 0x06a2, 0, {0xbf,0x81,0x0a} },
+{ 10, 0x06a5, 0, {0x90,0x7f,0x00,0xeb,0xf0,0x90,0x7f,0x01,0xee,0xf0} },
+{ 8, 0x06af, 0, {0x90,0x7f,0xe9,0xe0,0xfb,0xbb,0x82,0x1a} },
+{ 3, 0x06b7, 0, {0xba,0x01,0x0c} },
+{ 12, 0x06ba, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x80,0x0b} },
+{ 11, 0x06c6, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0x74,0xb5,0xf0} },
+{ 8, 0x06d1, 0, {0x90,0x7f,0xe9,0xe0,0xfb,0xbb,0x83,0x1b} },
+{ 3, 0x06d9, 0, {0xba,0x01,0x0d} },
+{ 13, 0x06dc, 0, {0x90,0x7f,0x00,0x74,0x01,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x80,0x0b} },
+{ 11, 0x06e9, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0x74,0x12,0xf0} },
+{ 8, 0x06f4, 0, {0x90,0x7f,0xe9,0xe0,0xfb,0xbb,0x84,0x1c} },
+{ 3, 0x06fc, 0, {0xba,0x01,0x0d} },
+{ 13, 0x06ff, 0, {0x90,0x7f,0x00,0x74,0x01,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x80,0x0c} },
+{ 12, 0x070c, 0, {0x90,0x7f,0x00,0x74,0x80,0xf0,0x90,0x7f,0x01,0x74,0x01,0xf0} },
+{ 5, 0x0718, 0, {0x90,0x7f,0xb5,0xec,0xf0} },
+{ 1, 0x071d, 0, {0x22} },
+{ 12, 0x071e, 0, {0x75,0x36,0x0d,0x90,0x88,0x00,0x74,0x1d,0xf0,0x75,0x6b,0x80} },
+{ 10, 0x072a, 0, {0x75,0x6c,0x3c,0x12,0x10,0xe2,0x75,0x6b,0x80,0x75} },
+{ 9, 0x0734, 0, {0x6c,0x0f,0x12,0x10,0xe2,0x75,0x6b,0x80,0x75} },
+{ 9, 0x073d, 0, {0x6c,0x06,0x12,0x10,0xe2,0x75,0x6b,0x80,0x75} },
+{ 7, 0x0746, 0, {0x6c,0x01,0x12,0x10,0xe2,0x7a,0x00} },
+{ 3, 0x074d, 0, {0xba,0xff,0x00} },
+{ 2, 0x0750, 0, {0x50,0x0a} },
+{ 10, 0x0752, 0, {0xc0,0x02,0x12,0x01,0xdd,0xd0,0x02,0x0a,0x80,0xf1} },
+{ 10, 0x075c, 0, {0x75,0x6b,0x80,0x75,0x6c,0x3c,0x12,0x10,0xe2,0x75} },
+{ 8, 0x0766, 0, {0x6b,0x80,0x75,0x6c,0x0f,0x12,0x10,0xe2} },
+{ 1, 0x076e, 0, {0x22} },
+{ 14, 0x076f, 0, {0x90,0x7f,0xa1,0xe4,0xf0,0x90,0x7f,0xaf,0x74,0x01,0xf0,0x90,0x7f,0x92} },
+{ 14, 0x077d, 0, {0x74,0x02,0xf0,0x75,0x8e,0x31,0x75,0x89,0x21,0x75,0x88,0x00,0x75,0xc8} },
+{ 14, 0x078b, 0, {0x00,0x75,0x8d,0x40,0x75,0x98,0x40,0x75,0xc0,0x40,0x75,0x87,0x00,0x75} },
+{ 9, 0x0799, 0, {0x20,0x00,0x75,0x21,0x00,0x75,0x22,0x00,0x75} },
+{ 5, 0x07a2, 0, {0x23,0x00,0x75,0x47,0x00} },
+{ 7, 0x07a7, 0, {0xc3,0xe5,0x47,0x94,0x20,0x50,0x11} },
+{ 13, 0x07ae, 0, {0xe5,0x47,0x24,0x00,0xf5,0x82,0xe4,0x34,0x17,0xf5,0x83,0xe4,0xf0} },
+{ 4, 0x07bb, 0, {0x05,0x47,0x80,0xe8} },
+{ 9, 0x07bf, 0, {0xe4,0xf5,0x40,0xf5,0x3f,0xe4,0xf5,0x3c,0xf5} },
+{ 7, 0x07c8, 0, {0x3b,0xe4,0xf5,0x3e,0xf5,0x3d,0x75} },
+{ 11, 0x07cf, 0, {0x32,0x00,0x75,0x37,0x00,0x75,0x39,0x00,0x90,0x7f,0x93} },
+{ 14, 0x07da, 0, {0x74,0x3c,0xf0,0x90,0x7f,0x9c,0x74,0xff,0xf0,0x90,0x7f,0x96,0x74,0x80} },
+{ 14, 0x07e8, 0, {0xf0,0x90,0x7f,0x94,0x74,0x70,0xf0,0x90,0x7f,0x9d,0x74,0x8f,0xf0,0x90} },
+{ 14, 0x07f6, 0, {0x7f,0x97,0xe4,0xf0,0x90,0x7f,0x95,0x74,0xc2,0xf0,0x90,0x7f,0x98,0x74} },
+{ 14, 0x0804, 0, {0x28,0xf0,0x90,0x7f,0x9e,0x74,0x28,0xf0,0x90,0x7f,0xf0,0xe4,0xf0,0x90} },
+{ 14, 0x0812, 0, {0x7f,0xf1,0xe4,0xf0,0x90,0x7f,0xf2,0xe4,0xf0,0x90,0x7f,0xf3,0xe4,0xf0} },
+{ 14, 0x0820, 0, {0x90,0x7f,0xf4,0xe4,0xf0,0x90,0x7f,0xf5,0xe4,0xf0,0x90,0x7f,0xf6,0xe4} },
+{ 14, 0x082e, 0, {0xf0,0x90,0x7f,0xf7,0xe4,0xf0,0x90,0x7f,0xf8,0xe4,0xf0,0x90,0x7f,0xf9} },
+{ 14, 0x083c, 0, {0x74,0x38,0xf0,0x90,0x7f,0xfa,0x74,0xa0,0xf0,0x90,0x7f,0xfb,0x74,0xa0} },
+{ 14, 0x084a, 0, {0xf0,0x90,0x7f,0xfc,0x74,0xa0,0xf0,0x90,0x7f,0xfd,0x74,0xa0,0xf0,0x90} },
+{ 14, 0x0858, 0, {0x7f,0xfe,0x74,0xa0,0xf0,0x90,0x7f,0xff,0x74,0xa0,0xf0,0x90,0x7f,0xe0} },
+{ 14, 0x0866, 0, {0x74,0x03,0xf0,0x90,0x7f,0xe1,0x74,0x01,0xf0,0x90,0x7f,0xdd,0x74,0x80} },
+{ 11, 0x0874, 0, {0xf0,0x12,0x12,0x43,0x12,0x07,0x1e,0x7a,0x00,0x7b,0x00} },
+{ 9, 0x087f, 0, {0xc3,0xea,0x94,0x1e,0xeb,0x94,0x00,0x50,0x17} },
+{ 12, 0x0888, 0, {0x90,0x88,0x00,0xe0,0xf5,0x47,0x90,0x88,0x0b,0xe0,0xf5,0x47} },
+{ 9, 0x0894, 0, {0x90,0x7f,0x68,0xf0,0x0a,0xba,0x00,0x01,0x0b} },
+{ 2, 0x089d, 0, {0x80,0xe0} },
+{ 12, 0x089f, 0, {0x12,0x03,0xe1,0x90,0x7f,0xd6,0xe4,0xf0,0x7a,0x00,0x7b,0x00} },
+{ 13, 0x08ab, 0, {0x8a,0x04,0x8b,0x05,0xc3,0xea,0x94,0xe0,0xeb,0x94,0x2e,0x50,0x1a} },
+{ 14, 0x08b8, 0, {0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0} },
+{ 10, 0x08c6, 0, {0x04,0xd0,0x03,0xd0,0x02,0x0a,0xba,0x00,0x01,0x0b} },
+{ 2, 0x08d0, 0, {0x80,0xd9} },
+{ 13, 0x08d2, 0, {0x90,0x7f,0xd6,0x74,0x02,0xf0,0x90,0x7f,0xd6,0x74,0x06,0xf0,0x90} },
+{ 14, 0x08df, 0, {0x7f,0xde,0x74,0x05,0xf0,0x90,0x7f,0xdf,0x74,0x05,0xf0,0x90,0x7f,0xac} },
+{ 14, 0x08ed, 0, {0xe4,0xf0,0x90,0x7f,0xad,0x74,0x05,0xf0,0x75,0xa8,0x80,0x75,0xf8,0x10} },
+{ 13, 0x08fb, 0, {0x90,0x7f,0xae,0x74,0x0b,0xf0,0x90,0x7f,0xe2,0x74,0x88,0xf0,0x90} },
+{ 12, 0x0908, 0, {0x7f,0xab,0x74,0x08,0xf0,0x75,0xe8,0x11,0x75,0x32,0x01,0x75} },
+{ 12, 0x0914, 0, {0x31,0x00,0x75,0x30,0x00,0xc0,0x04,0xc0,0x05,0x12,0x04,0xf7} },
+{ 10, 0x0920, 0, {0xd0,0x05,0xd0,0x04,0x75,0x34,0x00,0x75,0x35,0x01} },
+{ 13, 0x092a, 0, {0x90,0x7f,0xae,0x74,0x03,0xf0,0x8c,0x02,0xba,0x00,0x02,0x80,0x03} },
+{ 3, 0x0937, 0, {0x02,0x0a,0x3f} },
+{ 12, 0x093a, 0, {0x85,0x33,0x34,0x90,0x7f,0x9d,0x74,0x8f,0xf0,0x90,0x7f,0x97} },
+{ 14, 0x0946, 0, {0x74,0x08,0xf0,0x90,0x7f,0x9d,0x74,0x88,0xf0,0x90,0x7f,0x9a,0xe0,0xfa} },
+{ 12, 0x0954, 0, {0x74,0x05,0x5a,0xf5,0x33,0x90,0x7f,0x9d,0x74,0x8f,0xf0,0x90} },
+{ 13, 0x0960, 0, {0x7f,0x97,0x74,0x02,0xf0,0x90,0x7f,0x9d,0x74,0x82,0xf0,0xe5,0x33} },
+{ 13, 0x096d, 0, {0x25,0xe0,0xfa,0x90,0x7f,0x9a,0xe0,0x54,0x05,0xfb,0x4a,0xf5,0x33} },
+{ 2, 0x097a, 0, {0x60,0x0c} },
+{ 12, 0x097c, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x80,0x4a,0xf0} },
+{ 11, 0x0988, 0, {0x75,0x6e,0x00,0x75,0x6f,0x00,0xc0,0x04,0xc0,0x05,0x12} },
+{ 14, 0x0993, 0, {0x11,0x44,0xd0,0x05,0xd0,0x04,0x90,0x17,0x13,0xe0,0xfa,0x74,0x80,0x2a} },
+{ 6, 0x09a1, 0, {0xfa,0xe5,0x33,0xb4,0x04,0x29} },
+{ 3, 0x09a7, 0, {0xba,0xa0,0x00} },
+{ 2, 0x09aa, 0, {0x50,0x24} },
+{ 13, 0x09ac, 0, {0x90,0x17,0x13,0xe0,0x04,0xfb,0x0b,0x90,0x17,0x13,0xeb,0xf0,0x90} },
+{ 14, 0x09b9, 0, {0x17,0x13,0xe0,0xfb,0x90,0x17,0x15,0xf0,0xc0,0x02,0xc0,0x04,0xc0,0x05} },
+{ 9, 0x09c7, 0, {0x12,0x04,0xf7,0xd0,0x05,0xd0,0x04,0xd0,0x02} },
+{ 5, 0x09d0, 0, {0xe5,0x33,0xb4,0x02,0x26} },
+{ 6, 0x09d5, 0, {0xc3,0x74,0x04,0x9a,0x50,0x20} },
+{ 13, 0x09db, 0, {0x90,0x17,0x13,0xe0,0xfa,0x1a,0x1a,0x90,0x17,0x13,0xea,0xf0,0x90} },
+{ 13, 0x09e8, 0, {0x17,0x13,0xe0,0xfa,0x90,0x17,0x15,0xf0,0xc0,0x04,0xc0,0x05,0x12} },
+{ 6, 0x09f5, 0, {0x04,0xf7,0xd0,0x05,0xd0,0x04} },
+{ 5, 0x09fb, 0, {0xe5,0x33,0xb4,0x08,0x1d} },
+{ 4, 0x0a00, 0, {0xe5,0x34,0x70,0x19} },
+{ 10, 0x0a04, 0, {0x74,0x01,0x25,0x35,0x54,0x0f,0xf5,0x35,0x85,0x35} },
+{ 12, 0x0a0e, 0, {0x75,0x75,0x76,0x00,0xc0,0x04,0xc0,0x05,0x12,0x13,0xfe,0xd0} },
+{ 3, 0x0a1a, 0, {0x05,0xd0,0x04} },
+{ 5, 0x0a1d, 0, {0xe5,0x33,0xb4,0x01,0x1d} },
+{ 4, 0x0a22, 0, {0xe5,0x34,0x70,0x19} },
+{ 10, 0x0a26, 0, {0xe5,0x35,0x24,0xff,0x54,0x0f,0xf5,0x35,0x85,0x35} },
+{ 12, 0x0a30, 0, {0x75,0x75,0x76,0x00,0xc0,0x04,0xc0,0x05,0x12,0x13,0xfe,0xd0} },
+{ 3, 0x0a3c, 0, {0x05,0xd0,0x04} },
+{ 14, 0x0a3f, 0, {0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0,0x04,0x90,0x7f,0x96} },
+{ 14, 0x0a4d, 0, {0xe0,0xfa,0x90,0x7f,0x96,0x74,0x7f,0x5a,0xf0,0x90,0x7f,0x97,0x74,0x08} },
+{ 10, 0x0a5b, 0, {0xf0,0xc3,0xec,0x94,0x00,0xed,0x94,0x02,0x40,0x08} },
+{ 8, 0x0a65, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x20,0xe6,0x08} },
+{ 8, 0x0a6d, 0, {0xc3,0xe4,0x9c,0x74,0x08,0x9d,0x50,0x13} },
+{ 14, 0x0a75, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x40,0x65,0x02,0xf0,0x7c} },
+{ 5, 0x0a83, 0, {0x00,0x7d,0x00,0x80,0x05} },
+{ 5, 0x0a88, 0, {0x0c,0xbc,0x00,0x01,0x0d} },
+{ 5, 0x0a8d, 0, {0xe5,0x38,0xb4,0x01,0x0e} },
+{ 13, 0x0a92, 0, {0xc0,0x04,0xc0,0x05,0x12,0x04,0xf7,0xd0,0x05,0xd0,0x04,0x75,0x38} },
+{ 1, 0x0a9f, 0, {0x00} },
+{ 7, 0x0aa0, 0, {0xe5,0x31,0x70,0x03,0x02,0x09,0x2a} },
+{ 10, 0x0aa7, 0, {0x90,0x7f,0xc9,0xe0,0xfa,0x70,0x03,0x02,0x0c,0x2d} },
+{ 14, 0x0ab1, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x80,0x65,0x02,0xf0,0x90} },
+{ 9, 0x0abf, 0, {0x7d,0xc0,0xe0,0xfa,0xba,0x2c,0x02,0x80,0x03} },
+{ 3, 0x0ac8, 0, {0x02,0x0b,0x36} },
+{ 5, 0x0acb, 0, {0x75,0x32,0x00,0x7b,0x00} },
+{ 3, 0x0ad0, 0, {0xbb,0x64,0x00} },
+{ 2, 0x0ad3, 0, {0x50,0x1c} },
+{ 14, 0x0ad5, 0, {0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0} },
+{ 13, 0x0ae3, 0, {0x04,0xd0,0x03,0xd0,0x02,0x90,0x88,0x0f,0xe0,0xf5,0x47,0x0b,0x80} },
+{ 1, 0x0af0, 0, {0xdf} },
+{ 13, 0x0af1, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x07,0x1e,0x12,0x03,0xe1,0x12} },
+{ 12, 0x0afe, 0, {0x04,0xf7,0xd0,0x05,0xd0,0x04,0xd0,0x02,0x75,0x6e,0x00,0x75} },
+{ 13, 0x0b0a, 0, {0x6f,0x01,0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x11,0x44,0xd0,0x05} },
+{ 9, 0x0b17, 0, {0xd0,0x04,0xd0,0x02,0x75,0x70,0x4d,0x75,0x71} },
+{ 11, 0x0b20, 0, {0x0c,0x75,0x72,0x02,0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12} },
+{ 11, 0x0b2b, 0, {0x11,0x75,0xd0,0x05,0xd0,0x04,0xd0,0x02,0x02,0x0c,0x2d} },
+{ 3, 0x0b36, 0, {0xba,0x2a,0x3b} },
+{ 13, 0x0b39, 0, {0x90,0x7f,0x98,0x74,0x20,0xf0,0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12} },
+{ 14, 0x0b46, 0, {0x01,0xdd,0xd0,0x05,0xd0,0x04,0xd0,0x02,0x90,0x7f,0x98,0x74,0x28,0xf0} },
+{ 2, 0x0b54, 0, {0x7b,0x00} },
+{ 3, 0x0b56, 0, {0xbb,0x0a,0x00} },
+{ 5, 0x0b59, 0, {0x40,0x03,0x02,0x0c,0x2d} },
+{ 14, 0x0b5e, 0, {0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0} },
+{ 8, 0x0b6c, 0, {0x04,0xd0,0x03,0xd0,0x02,0x0b,0x80,0xe2} },
+{ 3, 0x0b74, 0, {0xba,0x2b,0x1a} },
+{ 8, 0x0b77, 0, {0x90,0x7f,0xc9,0xe0,0xfb,0xbb,0x40,0x12} },
+{ 14, 0x0b7f, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x12,0x05,0xd0,0x05,0xd0,0x04,0xd0} },
+{ 4, 0x0b8d, 0, {0x02,0x02,0x0c,0x2d} },
+{ 3, 0x0b91, 0, {0xba,0x10,0x1f} },
+{ 14, 0x0b94, 0, {0x90,0x7f,0x96,0xe0,0xfb,0x90,0x7f,0x96,0x74,0x80,0x65,0x03,0xf0,0xc0} },
+{ 14, 0x0ba2, 0, {0x02,0xc0,0x04,0xc0,0x05,0x12,0x10,0x3d,0xd0,0x05,0xd0,0x04,0xd0,0x02} },
+{ 3, 0x0bb0, 0, {0x02,0x0c,0x2d} },
+{ 3, 0x0bb3, 0, {0xba,0x11,0x12} },
+{ 14, 0x0bb6, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x10,0x6a,0xd0,0x05,0xd0,0x04,0xd0} },
+{ 4, 0x0bc4, 0, {0x02,0x02,0x0c,0x2d} },
+{ 3, 0x0bc8, 0, {0xba,0x12,0x12} },
+{ 14, 0x0bcb, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x10,0x8f,0xd0,0x05,0xd0,0x04,0xd0} },
+{ 4, 0x0bd9, 0, {0x02,0x02,0x0c,0x2d} },
+{ 3, 0x0bdd, 0, {0xba,0x13,0x0b} },
+{ 11, 0x0be0, 0, {0x90,0x7d,0xc1,0xe0,0xfb,0x90,0x88,0x00,0xf0,0x80,0x42} },
+{ 3, 0x0beb, 0, {0xba,0x14,0x11} },
+{ 14, 0x0bee, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x11,0xdd,0xd0,0x05,0xd0,0x04,0xd0} },
+{ 3, 0x0bfc, 0, {0x02,0x80,0x2e} },
+{ 3, 0x0bff, 0, {0xba,0x15,0x1d} },
+{ 12, 0x0c02, 0, {0x90,0x7d,0xc1,0xe0,0xf5,0x75,0x90,0x7d,0xc2,0xe0,0xf5,0x76} },
+{ 14, 0x0c0e, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x13,0xfe,0xd0,0x05,0xd0,0x04,0xd0} },
+{ 3, 0x0c1c, 0, {0x02,0x80,0x0e} },
+{ 3, 0x0c1f, 0, {0xba,0x16,0x0b} },
+{ 11, 0x0c22, 0, {0xc0,0x04,0xc0,0x05,0x12,0x13,0xa3,0xd0,0x05,0xd0,0x04} },
+{ 11, 0x0c2d, 0, {0x90,0x7f,0xc9,0xe4,0xf0,0x75,0x31,0x00,0x02,0x09,0x2a} },
+{ 1, 0x0c38, 0, {0x22} },
+{ 7, 0x0c39, 0, {0x53,0x55,0x50,0x45,0x4e,0x44,0x00} },
+{ 7, 0x0c40, 0, {0x52,0x45,0x53,0x55,0x4d,0x45,0x00} },
+{ 6, 0x0c47, 0, {0x20,0x56,0x6f,0x6c,0x20,0x00} },
+{ 13, 0x0c4d, 0, {0x44,0x41,0x42,0x55,0x53,0x42,0x20,0x76,0x31,0x2e,0x30,0x30,0x00} },
+{ 14, 0x0c5a, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} },
+{ 14, 0x0c68, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} },
+{ 13, 0x0c76, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90} },
+{ 14, 0x0c83, 0, {0x7f,0xab,0x74,0x01,0xf0,0x90,0x7f,0xe8,0xe0,0xfa,0x90,0x7f,0xe9,0xe0} },
+{ 6, 0x0c91, 0, {0xfb,0xbb,0x00,0x02,0x80,0x03} },
+{ 3, 0x0c97, 0, {0x02,0x0d,0x38} },
+{ 3, 0x0c9a, 0, {0xba,0x80,0x14} },
+{ 14, 0x0c9d, 0, {0x90,0x7f,0x00,0x74,0x01,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x90,0x7f,0xb5} },
+{ 6, 0x0cab, 0, {0x74,0x02,0xf0,0x02,0x0e,0xcd} },
+{ 5, 0x0cb1, 0, {0xba,0x82,0x02,0x80,0x03} },
+{ 3, 0x0cb6, 0, {0x02,0x0d,0x1d} },
+{ 8, 0x0cb9, 0, {0x90,0x7f,0xec,0xe0,0xfc,0xbc,0x01,0x00} },
+{ 2, 0x0cc1, 0, {0x40,0x21} },
+{ 6, 0x0cc3, 0, {0xc3,0x74,0x07,0x9c,0x40,0x1b} },
+{ 14, 0x0cc9, 0, {0xec,0x24,0xff,0x25,0xe0,0xfd,0x24,0xc6,0xf5,0x82,0xe4,0x34,0x7f,0xf5} },
+{ 13, 0x0cd7, 0, {0x83,0xe0,0xfd,0x53,0x05,0x01,0x90,0x7f,0x00,0xed,0xf0,0x80,0x2b} },
+{ 3, 0x0ce4, 0, {0xbc,0x81,0x00} },
+{ 2, 0x0ce7, 0, {0x40,0x21} },
+{ 6, 0x0ce9, 0, {0xc3,0x74,0x87,0x9c,0x40,0x1b} },
+{ 14, 0x0cef, 0, {0xec,0x24,0x7f,0x25,0xe0,0xfc,0x24,0xb6,0xf5,0x82,0xe4,0x34,0x7f,0xf5} },
+{ 13, 0x0cfd, 0, {0x83,0xe0,0xfc,0x53,0x04,0x01,0x90,0x7f,0x00,0xec,0xf0,0x80,0x05} },
+{ 5, 0x0d0a, 0, {0x90,0x7f,0x00,0xe4,0xf0} },
+{ 14, 0x0d0f, 0, {0x90,0x7f,0x01,0xe4,0xf0,0x90,0x7f,0xb5,0x74,0x02,0xf0,0x02,0x0e,0xcd} },
+{ 5, 0x0d1d, 0, {0xba,0x81,0x02,0x80,0x03} },
+{ 3, 0x0d22, 0, {0x02,0x0e,0xc5} },
+{ 14, 0x0d25, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x90,0x7f,0xb5,0x74} },
+{ 5, 0x0d33, 0, {0x02,0xf0,0x02,0x0e,0xcd} },
+{ 3, 0x0d38, 0, {0xbb,0x01,0x2d} },
+{ 6, 0x0d3b, 0, {0xba,0x00,0x03,0x02,0x0e,0xcd} },
+{ 3, 0x0d41, 0, {0xba,0x02,0x11} },
+{ 13, 0x0d44, 0, {0x75,0x59,0x00,0xc0,0x02,0xc0,0x03,0x12,0x0e,0xf0,0xd0,0x03,0xd0} },
+{ 4, 0x0d51, 0, {0x02,0x02,0x0e,0xcd} },
+{ 5, 0x0d55, 0, {0xba,0x21,0x02,0x80,0x03} },
+{ 3, 0x0d5a, 0, {0x02,0x0e,0xcd} },
+{ 11, 0x0d5d, 0, {0x75,0x37,0x01,0x90,0x7f,0xc5,0xe4,0xf0,0x02,0x0e,0xcd} },
+{ 3, 0x0d68, 0, {0xbb,0x03,0x1f} },
+{ 6, 0x0d6b, 0, {0xba,0x00,0x03,0x02,0x0e,0xcd} },
+{ 5, 0x0d71, 0, {0xba,0x02,0x02,0x80,0x03} },
+{ 3, 0x0d76, 0, {0x02,0x0e,0xcd} },
+{ 13, 0x0d79, 0, {0x75,0x59,0x01,0xc0,0x02,0xc0,0x03,0x12,0x0e,0xf0,0xd0,0x03,0xd0} },
+{ 4, 0x0d86, 0, {0x02,0x02,0x0e,0xcd} },
+{ 3, 0x0d8a, 0, {0xbb,0x06,0x54} },
+{ 5, 0x0d8d, 0, {0xba,0x80,0x02,0x80,0x03} },
+{ 3, 0x0d92, 0, {0x02,0x0e,0xc5} },
+{ 8, 0x0d95, 0, {0x90,0x7f,0xeb,0xe0,0xfc,0xbc,0x01,0x15} },
+{ 12, 0x0d9d, 0, {0x7c,0xfb,0x7d,0x0f,0x8d,0x06,0x7f,0x00,0x90,0x7f,0xd4,0xee} },
+{ 9, 0x0da9, 0, {0xf0,0x90,0x7f,0xd5,0xec,0xf0,0x02,0x0e,0xcd} },
+{ 10, 0x0db2, 0, {0x90,0x7f,0xeb,0xe0,0xfc,0xbc,0x02,0x02,0x80,0x03} },
+{ 3, 0x0dbc, 0, {0x02,0x0e,0xc5} },
+{ 10, 0x0dbf, 0, {0x90,0x7f,0xea,0xe0,0xfc,0xbc,0x00,0x02,0x80,0x03} },
+{ 3, 0x0dc9, 0, {0x02,0x0e,0xc5} },
+{ 12, 0x0dcc, 0, {0x7c,0x3b,0x7d,0x0f,0x8d,0x06,0x7f,0x00,0x90,0x7f,0xd4,0xee} },
+{ 9, 0x0dd8, 0, {0xf0,0x90,0x7f,0xd5,0xec,0xf0,0x02,0x0e,0xcd} },
+{ 6, 0x0de1, 0, {0xbb,0x07,0x03,0x02,0x0e,0xc5} },
+{ 3, 0x0de7, 0, {0xbb,0x08,0x10} },
+{ 13, 0x0dea, 0, {0xac,0x48,0x90,0x7f,0x00,0xec,0xf0,0x90,0x7f,0xb5,0x74,0x01,0xf0} },
+{ 3, 0x0df7, 0, {0x02,0x0e,0xcd} },
+{ 3, 0x0dfa, 0, {0xbb,0x09,0x31} },
+{ 5, 0x0dfd, 0, {0xba,0x00,0x02,0x80,0x03} },
+{ 3, 0x0e02, 0, {0x02,0x0e,0xc5} },
+{ 14, 0x0e05, 0, {0x90,0x7f,0xea,0xe0,0xfc,0xc3,0x74,0x01,0x9c,0x50,0x03,0x02,0x0e,0xc5} },
+{ 8, 0x0e13, 0, {0x90,0x7f,0xea,0xe0,0xfc,0xbc,0x00,0x0a} },
+{ 10, 0x0e1b, 0, {0x90,0x17,0x21,0xe4,0xf0,0x90,0x17,0x22,0xe4,0xf0} },
+{ 9, 0x0e25, 0, {0x90,0x7f,0xea,0xe0,0xf5,0x48,0x02,0x0e,0xcd} },
+{ 3, 0x0e2e, 0, {0xbb,0x0a,0x27} },
+{ 5, 0x0e31, 0, {0xba,0x81,0x02,0x80,0x03} },
+{ 3, 0x0e36, 0, {0x02,0x0e,0xc5} },
+{ 14, 0x0e39, 0, {0x90,0x7f,0xec,0xe0,0xfa,0x24,0x20,0xfa,0xe4,0x34,0x17,0xfc,0x8a,0x82} },
+{ 14, 0x0e47, 0, {0x8c,0x83,0xe0,0xfa,0x90,0x7f,0x00,0xf0,0x90,0x7f,0xb5,0x74,0x01,0xf0} },
+{ 3, 0x0e55, 0, {0x02,0x0e,0xcd} },
+{ 5, 0x0e58, 0, {0xbb,0x0b,0x02,0x80,0x03} },
+{ 3, 0x0e5d, 0, {0x02,0x0e,0xa9} },
+{ 13, 0x0e60, 0, {0x90,0x17,0x20,0xe4,0xf0,0x90,0x7f,0xec,0xe0,0xfa,0xba,0x01,0x1a} },
+{ 8, 0x0e6d, 0, {0x90,0x7f,0xed,0xe0,0xfa,0xba,0x00,0x12} },
+{ 14, 0x0e75, 0, {0x90,0x7f,0xea,0xe0,0xfa,0x90,0x17,0x21,0xf0,0xc0,0x03,0x12,0x04,0xe2} },
+{ 4, 0x0e83, 0, {0xd0,0x03,0x80,0x46} },
+{ 8, 0x0e87, 0, {0x90,0x7f,0xec,0xe0,0xfa,0xba,0x02,0x3e} },
+{ 8, 0x0e8f, 0, {0x90,0x7f,0xed,0xe0,0xfa,0xba,0x00,0x36} },
+{ 13, 0x0e97, 0, {0xc0,0x03,0x12,0x04,0xef,0xd0,0x03,0x90,0x7f,0xea,0xe0,0xfa,0x90} },
+{ 5, 0x0ea4, 0, {0x17,0x22,0xf0,0x80,0x24} },
+{ 5, 0x0ea9, 0, {0xbb,0x12,0x02,0x80,0x17} },
+{ 5, 0x0eae, 0, {0xbb,0x81,0x02,0x80,0x0d} },
+{ 5, 0x0eb3, 0, {0xbb,0x83,0x02,0x80,0x08} },
+{ 5, 0x0eb8, 0, {0xbb,0x82,0x02,0x80,0x03} },
+{ 3, 0x0ebd, 0, {0xbb,0x84,0x05} },
+{ 5, 0x0ec0, 0, {0x12,0x06,0x4e,0x80,0x08} },
+{ 8, 0x0ec5, 0, {0x90,0x7f,0xb4,0x74,0x03,0xf0,0x80,0x06} },
+{ 6, 0x0ecd, 0, {0x90,0x7f,0xb4,0x74,0x02,0xf0} },
+{ 2, 0x0ed3, 0, {0xd0,0x86} },
+{ 14, 0x0ed5, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} },
+{ 13, 0x0ee3, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} },
+{ 11, 0x0ef0, 0, {0x90,0x7f,0xec,0xe0,0xf5,0x5a,0xc3,0x94,0x01,0x40,0x1d} },
+{ 7, 0x0efb, 0, {0xc3,0x74,0x07,0x95,0x5a,0x40,0x16} },
+{ 13, 0x0f02, 0, {0xe5,0x5a,0x24,0xff,0x25,0xe0,0xfa,0x24,0xc6,0xf5,0x82,0xe4,0x34} },
+{ 9, 0x0f0f, 0, {0x7f,0xf5,0x83,0xaa,0x59,0xea,0xf0,0x80,0x22} },
+{ 7, 0x0f18, 0, {0xc3,0xe5,0x5a,0x94,0x81,0x40,0x1b} },
+{ 7, 0x0f1f, 0, {0xc3,0x74,0x87,0x95,0x5a,0x40,0x14} },
+{ 13, 0x0f26, 0, {0xe5,0x5a,0x24,0xff,0x25,0xe0,0xfa,0x24,0xb6,0xf5,0x82,0xe4,0x34} },
+{ 7, 0x0f33, 0, {0x7f,0xf5,0x83,0xaa,0x59,0xea,0xf0} },
+{ 1, 0x0f3a, 0, {0x22} },
+{ 14, 0x0f3b, 0, {0x09,0x02,0xba,0x00,0x03,0x01,0x00,0x40,0x00,0x09,0x04,0x00,0x00,0x00} },
+{ 14, 0x0f49, 0, {0x01,0x01,0x00,0x00,0x09,0x24,0x01,0x00,0x01,0x3d,0x00,0x01,0x01,0x0c} },
+{ 14, 0x0f57, 0, {0x24,0x02,0x01,0x10,0x07,0x00,0x02,0x03,0x00,0x00,0x00,0x0d,0x24,0x06} },
+{ 14, 0x0f65, 0, {0x03,0x01,0x02,0x15,0x00,0x03,0x00,0x03,0x00,0x00,0x09,0x24,0x03,0x02} },
+{ 14, 0x0f73, 0, {0x01,0x01,0x00,0x01,0x00,0x09,0x24,0x03,0x04,0x02,0x03,0x00,0x03,0x00} },
+{ 14, 0x0f81, 0, {0x09,0x24,0x03,0x05,0x03,0x06,0x00,0x01,0x00,0x09,0x04,0x01,0x00,0x00} },
+{ 14, 0x0f8f, 0, {0x01,0x02,0x00,0x00,0x09,0x04,0x01,0x01,0x01,0x01,0x02,0x00,0x00,0x07} },
+{ 14, 0x0f9d, 0, {0x24,0x01,0x02,0x01,0x01,0x00,0x0b,0x24,0x02,0x01,0x02,0x02,0x10,0x01} },
+{ 14, 0x0fab, 0, {0x80,0xbb,0x00,0x09,0x05,0x88,0x05,0x00,0x01,0x01,0x00,0x00,0x07,0x25} },
+{ 14, 0x0fb9, 0, {0x01,0x00,0x00,0x00,0x00,0x09,0x04,0x02,0x00,0x02,0x00,0x00,0x00,0x00} },
+{ 14, 0x0fc7, 0, {0x07,0x05,0x82,0x02,0x40,0x00,0x00,0x07,0x05,0x02,0x02,0x40,0x00,0x00} },
+{ 14, 0x0fd5, 0, {0x09,0x04,0x02,0x01,0x03,0x00,0x00,0x00,0x00,0x07,0x05,0x82,0x02,0x40} },
+{ 14, 0x0fe3, 0, {0x00,0x00,0x07,0x05,0x02,0x02,0x40,0x00,0x00,0x09,0x05,0x89,0x05,0xa0} },
+{ 10, 0x0ff1, 0, {0x01,0x01,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00} },
+{ 14, 0x0ffb, 0, {0x12,0x01,0x00,0x01,0x00,0x00,0x00,0x40,0x47,0x05,0x99,0x99,0x00,0x01} },
+{ 14, 0x1009, 0, {0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x02,0xba} },
+{ 4, 0x1017, 0, {0x00,0x03,0x01,0x00} },
+{ 2, 0x101b, 0, {0x7a,0x00} },
+{ 3, 0x101d, 0, {0xba,0x05,0x00} },
+{ 2, 0x1020, 0, {0x50,0x17} },
+{ 8, 0x1022, 0, {0x90,0x7f,0xa5,0xe0,0xfb,0x30,0xe0,0x05} },
+{ 5, 0x102a, 0, {0x90,0x00,0x01,0x80,0x0d} },
+{ 10, 0x102f, 0, {0xc0,0x02,0x12,0x01,0xdd,0xd0,0x02,0x0a,0x80,0xe4} },
+{ 3, 0x1039, 0, {0x90,0x00,0x01} },
+{ 1, 0x103c, 0, {0x22} },
+{ 14, 0x103d, 0, {0x90,0x7d,0xc1,0xe0,0xf9,0xa3,0xe0,0xfa,0xa3,0xe0,0xfb,0x7c,0x00,0x7d} },
+{ 4, 0x104b, 0, {0x7e,0xeb,0x60,0x12} },
+{ 14, 0x104f, 0, {0x89,0x82,0x8a,0x83,0xe0,0xa3,0xa9,0x82,0xaa,0x83,0x8c,0x82,0x8d,0x83} },
+{ 4, 0x105d, 0, {0xf0,0x0c,0xdb,0xee} },
+{ 8, 0x1061, 0, {0x90,0x7d,0xc3,0xe0,0x90,0x7f,0xb9,0xf0} },
+{ 1, 0x1069, 0, {0x22} },
+{ 14, 0x106a, 0, {0x90,0x7d,0xc1,0xe0,0xf9,0xa3,0xe0,0xfa,0xa3,0xe0,0xfb,0x7c,0xc4,0x7d} },
+{ 4, 0x1078, 0, {0x7d,0xeb,0x60,0xe5} },
+{ 14, 0x107c, 0, {0x8c,0x82,0x8d,0x83,0xe0,0x0c,0x89,0x82,0x8a,0x83,0xf0,0xa3,0xa9,0x82} },
+{ 4, 0x108a, 0, {0xaa,0x83,0xdb,0xee} },
+{ 1, 0x108e, 0, {0x22} },
+{ 14, 0x108f, 0, {0x90,0x7f,0xa5,0x74,0x80,0xf0,0x05,0x86,0x90,0x7d,0xc1,0xe0,0x05,0x86} },
+{ 14, 0x109d, 0, {0xa3,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa6,0x05,0x86,0xa3,0xa3,0xe0,0xf9} },
+{ 5, 0x10ab, 0, {0x60,0x16,0xa3,0x05,0x86} },
+{ 13, 0x10b0, 0, {0x90,0x7f,0xa6,0x05,0x86,0xe0,0xa3,0x05,0x86,0xf0,0xc0,0x01,0x12} },
+{ 6, 0x10bd, 0, {0x10,0x1b,0xd0,0x01,0xd9,0xed} },
+{ 6, 0x10c3, 0, {0x90,0x7f,0xa5,0x74,0x40,0xf0} },
+{ 1, 0x10c9, 0, {0x22} },
+{ 8, 0x10ca, 0, {0x90,0x88,0x02,0x74,0x01,0xf0,0x7a,0x00} },
+{ 3, 0x10d2, 0, {0xba,0xff,0x00} },
+{ 2, 0x10d5, 0, {0x50,0x0a} },
+{ 10, 0x10d7, 0, {0xc0,0x02,0x12,0x01,0xdd,0xd0,0x02,0x0a,0x80,0xf1} },
+{ 1, 0x10e1, 0, {0x22} },
+{ 5, 0x10e2, 0, {0xe5,0x6b,0xb4,0xc0,0x08} },
+{ 8, 0x10e7, 0, {0x90,0x88,0x03,0xe5,0x6c,0xf0,0x80,0x06} },
+{ 6, 0x10ef, 0, {0x90,0x88,0x02,0xe5,0x6c,0xf0} },
+{ 4, 0x10f5, 0, {0x7a,0x00,0x7b,0x00} },
+{ 11, 0x10f9, 0, {0xc3,0xea,0x94,0x32,0xeb,0x64,0x80,0x94,0x80,0x50,0x07} },
+{ 5, 0x1104, 0, {0x0a,0xba,0x00,0x01,0x0b} },
+{ 2, 0x1109, 0, {0x80,0xee} },
+{ 1, 0x110b, 0, {0x22} },
+{ 10, 0x110c, 0, {0x90,0x88,0x03,0xe5,0x6d,0xf0,0x05,0x39,0x7a,0x00} },
+{ 3, 0x1116, 0, {0xba,0x28,0x00} },
+{ 2, 0x1119, 0, {0x50,0x03} },
+{ 3, 0x111b, 0, {0x0a,0x80,0xf8} },
+{ 5, 0x111e, 0, {0xe5,0x39,0xb4,0x10,0x08} },
+{ 8, 0x1123, 0, {0x90,0x88,0x02,0x74,0xc0,0xf0,0x80,0x0e} },
+{ 5, 0x112b, 0, {0xe5,0x39,0xb4,0x20,0x09} },
+{ 9, 0x1130, 0, {0x90,0x88,0x02,0x74,0x80,0xf0,0x75,0x39,0x00} },
+{ 2, 0x1139, 0, {0x7a,0x00} },
+{ 3, 0x113b, 0, {0xba,0x28,0x00} },
+{ 2, 0x113e, 0, {0x50,0x03} },
+{ 3, 0x1140, 0, {0x0a,0x80,0xf8} },
+{ 1, 0x1143, 0, {0x22} },
+{ 4, 0x1144, 0, {0xe5,0x6f,0x60,0x02} },
+{ 2, 0x1148, 0, {0x80,0x07} },
+{ 7, 0x114a, 0, {0x7a,0x00,0x75,0x39,0x00,0x80,0x05} },
+{ 5, 0x1151, 0, {0x7a,0x40,0x75,0x39,0x10} },
+{ 9, 0x1156, 0, {0xe5,0x6e,0x2a,0xfa,0xe5,0x6e,0x25,0x39,0xf5} },
+{ 10, 0x115f, 0, {0x39,0x90,0x88,0x02,0x74,0x80,0x2a,0xf0,0x7a,0x00} },
+{ 8, 0x1169, 0, {0xc3,0xea,0x64,0x80,0x94,0xa8,0x50,0x03} },
+{ 3, 0x1171, 0, {0x0a,0x80,0xf5} },
+{ 1, 0x1174, 0, {0x22} },
+{ 6, 0x1175, 0, {0xaa,0x70,0xab,0x71,0xac,0x72} },
+{ 12, 0x117b, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x12,0x14,0xee,0xfd,0x60,0x18} },
+{ 13, 0x1187, 0, {0x8d,0x6d,0xc0,0x02,0xc0,0x03,0xc0,0x04,0x12,0x11,0x0c,0xd0,0x04} },
+{ 9, 0x1194, 0, {0xd0,0x03,0xd0,0x02,0x0a,0xba,0x00,0x01,0x0b} },
+{ 2, 0x119d, 0, {0x80,0xdc} },
+{ 1, 0x119f, 0, {0x22} },
+{ 13, 0x11a0, 0, {0xe5,0x73,0xc4,0x54,0x0f,0xfa,0x53,0x02,0x0f,0xc3,0x74,0x09,0x9a} },
+{ 2, 0x11ad, 0, {0x50,0x06} },
+{ 6, 0x11af, 0, {0x74,0x37,0x2a,0xfb,0x80,0x04} },
+{ 4, 0x11b5, 0, {0x74,0x30,0x2a,0xfb} },
+{ 12, 0x11b9, 0, {0x8b,0x6d,0xc0,0x03,0x12,0x11,0x0c,0xd0,0x03,0xaa,0x73,0x53} },
+{ 8, 0x11c5, 0, {0x02,0x0f,0xc3,0x74,0x09,0x9a,0x50,0x06} },
+{ 6, 0x11cd, 0, {0x74,0x37,0x2a,0xfb,0x80,0x04} },
+{ 4, 0x11d3, 0, {0x74,0x30,0x2a,0xfb} },
+{ 5, 0x11d7, 0, {0x8b,0x6d,0x12,0x11,0x0c} },
+{ 1, 0x11dc, 0, {0x22} },
+{ 7, 0x11dd, 0, {0x90,0x7d,0xc3,0xe0,0xfa,0x60,0x0f} },
+{ 12, 0x11e4, 0, {0x90,0x7d,0xc1,0xe0,0xf5,0x6e,0x90,0x7d,0xc2,0xe0,0xf5,0x6f} },
+{ 3, 0x11f0, 0, {0x12,0x11,0x44} },
+{ 12, 0x11f3, 0, {0x90,0x7d,0xff,0xe4,0xf0,0x75,0x70,0xc4,0x75,0x71,0x7d,0x75} },
+{ 5, 0x11ff, 0, {0x72,0x01,0x12,0x11,0x75} },
+{ 1, 0x1204, 0, {0x22} },
+{ 2, 0x1205, 0, {0x7a,0x04} },
+{ 3, 0x1207, 0, {0xba,0x40,0x00} },
+{ 2, 0x120a, 0, {0x50,0x36} },
+{ 14, 0x120c, 0, {0xea,0x24,0xc0,0xf5,0x82,0xe4,0x34,0x7d,0xf5,0x83,0xe0,0xfb,0x7c,0x00} },
+{ 3, 0x121a, 0, {0xbc,0x08,0x00} },
+{ 2, 0x121d, 0, {0x50,0x20} },
+{ 6, 0x121f, 0, {0x8b,0x05,0xed,0x30,0xe7,0x0b} },
+{ 11, 0x1225, 0, {0x90,0x7f,0x96,0x74,0x42,0xf0,0x74,0xc3,0xf0,0x80,0x08} },
+{ 8, 0x1230, 0, {0x90,0x7f,0x96,0xe4,0xf0,0x74,0x81,0xf0} },
+{ 7, 0x1238, 0, {0xeb,0x25,0xe0,0xfb,0x0c,0x80,0xdb} },
+{ 3, 0x123f, 0, {0x0a,0x80,0xc5} },
+{ 1, 0x1242, 0, {0x22} },
+{ 4, 0x1243, 0, {0x7a,0x00,0x7b,0xef} },
+{ 3, 0x1247, 0, {0xba,0x10,0x00} },
+{ 2, 0x124a, 0, {0x50,0x20} },
+{ 14, 0x124c, 0, {0x74,0x11,0x2b,0xfb,0x24,0x00,0xfc,0xe4,0x34,0x18,0xfd,0x8c,0x82,0x8d} },
+{ 14, 0x125a, 0, {0x83,0xe4,0xf0,0xea,0x24,0x00,0xf5,0x82,0xe4,0x34,0x19,0xf5,0x83,0xe4} },
+{ 4, 0x1268, 0, {0xf0,0x0a,0x80,0xdb} },
+{ 1, 0x126c, 0, {0x22} },
+{ 14, 0x126d, 0, {0x74,0xf8,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} },
+{ 14, 0x127b, 0, {0x74,0xf9,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} },
+{ 14, 0x1289, 0, {0x74,0xfa,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} },
+{ 14, 0x1297, 0, {0x74,0xfb,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} },
+{ 14, 0x12a5, 0, {0x74,0xff,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} },
+{ 1, 0x12b3, 0, {0x22} },
+{ 14, 0x12b4, 0, {0x12,0x03,0xcb,0x12,0x12,0x6d,0x7a,0xc0,0x7b,0x87,0x7c,0x01,0x74,0x01} },
+{ 14, 0x12c2, 0, {0x2a,0xfd,0xe4,0x3b,0xfe,0x8c,0x07,0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x74} },
+{ 14, 0x12d0, 0, {0x01,0x12,0x14,0xbf,0x2d,0xfa,0xe4,0x3e,0xfb,0x8f,0x04,0x8d,0x82,0x8e} },
+{ 14, 0x12de, 0, {0x83,0x8f,0xf0,0x74,0x06,0x12,0x14,0xbf,0x74,0x01,0x2a,0xfd,0xe4,0x3b} },
+{ 14, 0x12ec, 0, {0xfe,0x8c,0x07,0x8a,0x82,0x8b,0x83,0x8c,0xf0,0xe4,0x12,0x14,0xbf,0x74} },
+{ 14, 0x12fa, 0, {0x01,0x2d,0xfa,0xe4,0x3e,0xfb,0x8f,0x04,0x8d,0x82,0x8e,0x83,0x8f,0xf0} },
+{ 14, 0x1308, 0, {0x74,0x0b,0x12,0x14,0xbf,0x74,0x01,0x2a,0xfd,0xe4,0x3b,0xfe,0x8c,0x07} },
+{ 14, 0x1316, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x74,0x08,0x12,0x14,0xbf,0x74,0x01,0x2d} },
+{ 14, 0x1324, 0, {0xfa,0xe4,0x3e,0xfb,0x8f,0x04,0x8d,0x82,0x8e,0x83,0x8f,0xf0,0x74,0x01} },
+{ 14, 0x1332, 0, {0x12,0x14,0xbf,0x2a,0xfd,0xe4,0x3b,0xfe,0x8c,0x07,0x8a,0x82,0x8b,0x83} },
+{ 14, 0x1340, 0, {0x8c,0xf0,0xe4,0x12,0x14,0xbf,0x74,0x01,0x2d,0xfa,0xe4,0x3e,0xfb,0x8f} },
+{ 14, 0x134e, 0, {0x04,0x8d,0x82,0x8e,0x83,0x8f,0xf0,0x74,0x03,0x12,0x14,0xbf,0x7d,0x00} },
+{ 3, 0x135c, 0, {0xbd,0x06,0x00} },
+{ 2, 0x135f, 0, {0x50,0x12} },
+{ 11, 0x1361, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x0a,0xba,0x00,0x01,0x0b} },
+{ 7, 0x136c, 0, {0xe4,0x12,0x14,0xbf,0x0d,0x80,0xe9} },
+{ 13, 0x1373, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0xe5,0x74,0x12,0x14,0xbf,0x74,0xf9} },
+{ 14, 0x1380, 0, {0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0x74,0x0f,0xf0,0x74} },
+{ 14, 0x138e, 0, {0xfe,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0x74,0x01,0xf0} },
+{ 6, 0x139c, 0, {0x12,0x03,0xe1,0x12,0x04,0xf7} },
+{ 1, 0x13a2, 0, {0x22} },
+{ 13, 0x13a3, 0, {0x90,0x7d,0xc1,0xe0,0xfa,0x24,0x00,0xfb,0xe4,0x34,0x19,0xfc,0x90} },
+{ 14, 0x13b0, 0, {0x7d,0xc2,0xe0,0xfd,0x8b,0x82,0x8c,0x83,0xf0,0x75,0xf0,0x11,0xea,0xa4} },
+{ 3, 0x13be, 0, {0xfa,0x7b,0x00} },
+{ 3, 0x13c1, 0, {0xbb,0x10,0x00} },
+{ 2, 0x13c4, 0, {0x50,0x24} },
+{ 14, 0x13c6, 0, {0xea,0x24,0x00,0xfc,0xe4,0x34,0x18,0xfd,0xeb,0x2c,0xfc,0xe4,0x3d,0xfd} },
+{ 14, 0x13d4, 0, {0x74,0x04,0x2b,0x24,0xc0,0xf5,0x82,0xe4,0x34,0x7d,0xf5,0x83,0xe0,0xfe} },
+{ 8, 0x13e2, 0, {0x8c,0x82,0x8d,0x83,0xf0,0x0b,0x80,0xd7} },
+{ 14, 0x13ea, 0, {0xea,0x24,0x00,0xfa,0xe4,0x34,0x18,0xfb,0x74,0x10,0x2a,0xf5,0x82,0xe4} },
+{ 5, 0x13f8, 0, {0x3b,0xf5,0x83,0xe4,0xf0} },
+{ 1, 0x13fd, 0, {0x22} },
+{ 4, 0x13fe, 0, {0xe5,0x76,0x60,0x02} },
+{ 2, 0x1402, 0, {0x80,0x16} },
+{ 12, 0x1404, 0, {0x74,0x0f,0x55,0x75,0xfa,0x8a,0x75,0x24,0x00,0xf5,0x82,0xe4} },
+{ 10, 0x1410, 0, {0x34,0x19,0xf5,0x83,0xe0,0xf5,0x74,0x12,0x12,0xb4} },
+{ 10, 0x141a, 0, {0x12,0x10,0xca,0x75,0x6e,0x00,0x75,0x6f,0x00,0x12} },
+{ 6, 0x1424, 0, {0x11,0x44,0x75,0x70,0xb9,0x75} },
+{ 6, 0x142a, 0, {0x71,0x14,0x75,0x72,0x02,0x12} },
+{ 11, 0x1430, 0, {0x11,0x75,0xe5,0x76,0xb4,0x02,0x04,0x74,0x01,0x80,0x01} },
+{ 1, 0x143b, 0, {0xe4} },
+{ 3, 0x143c, 0, {0xfa,0x70,0x0f} },
+{ 12, 0x143f, 0, {0x74,0x01,0x25,0x75,0xf5,0x73,0xc0,0x02,0x12,0x11,0xa0,0xd0} },
+{ 3, 0x144b, 0, {0x02,0x80,0x0a} },
+{ 10, 0x144e, 0, {0x85,0x75,0x73,0xc0,0x02,0x12,0x11,0xa0,0xd0,0x02} },
+{ 12, 0x1458, 0, {0x75,0x6e,0x00,0x75,0x6f,0x01,0xc0,0x02,0x12,0x11,0x44,0xd0} },
+{ 4, 0x1464, 0, {0x02,0xea,0x70,0x1a} },
+{ 13, 0x1468, 0, {0x75,0xf0,0x11,0xe5,0x75,0xa4,0xfa,0x24,0x00,0xfa,0xe4,0x34,0x18} },
+{ 9, 0x1475, 0, {0xfb,0x8a,0x70,0x8b,0x71,0x75,0x72,0x01,0x12} },
+{ 4, 0x147e, 0, {0x11,0x75,0x80,0x36} },
+{ 2, 0x1482, 0, {0x7a,0x00} },
+{ 3, 0x1484, 0, {0xba,0x10,0x00} },
+{ 2, 0x1487, 0, {0x50,0x2f} },
+{ 13, 0x1489, 0, {0xea,0x24,0x00,0xf5,0x82,0xe4,0x34,0x19,0xf5,0x83,0xe0,0xfb,0xe5} },
+{ 4, 0x1496, 0, {0x75,0xb5,0x03,0x1b} },
+{ 14, 0x149a, 0, {0x75,0xf0,0x11,0xea,0xa4,0xfb,0x24,0x00,0xfb,0xe4,0x34,0x18,0xfc,0x8b} },
+{ 9, 0x14a8, 0, {0x70,0x8c,0x71,0x75,0x72,0x01,0xc0,0x02,0x12} },
+{ 4, 0x14b1, 0, {0x11,0x75,0xd0,0x02} },
+{ 3, 0x14b5, 0, {0x0a,0x80,0xcc} },
+{ 1, 0x14b8, 0, {0x22} },
+{ 6, 0x14b9, 0, {0x50,0x72,0x6f,0x67,0x20,0x00} },
+{ 14, 0x14bf, 0, {0xc8,0xc0,0xe0,0xc8,0xc0,0xe0,0xe5,0xf0,0x60,0x0b,0x14,0x60,0x0f,0x14} },
+{ 7, 0x14cd, 0, {0x60,0x11,0x14,0x60,0x12,0x80,0x15} },
+{ 7, 0x14d4, 0, {0xd0,0xe0,0xa8,0x82,0xf6,0x80,0x0e} },
+{ 5, 0x14db, 0, {0xd0,0xe0,0xf0,0x80,0x09} },
+{ 4, 0x14e0, 0, {0xd0,0xe0,0x80,0x05} },
+{ 5, 0x14e4, 0, {0xd0,0xe0,0xa8,0x82,0xf2} },
+{ 4, 0x14e9, 0, {0xc8,0xd0,0xe0,0xc8} },
+{ 1, 0x14ed, 0, {0x22} },
+{ 14, 0x14ee, 0, {0xc8,0xc0,0xe0,0xe5,0xf0,0x60,0x0d,0x14,0x60,0x0f,0x14,0x60,0x0f,0x14} },
+{ 6, 0x14fc, 0, {0x60,0x10,0x74,0xff,0x80,0x0f} },
+{ 5, 0x1502, 0, {0xa8,0x82,0xe6,0x80,0x0a} },
+{ 3, 0x1507, 0, {0xe0,0x80,0x07} },
+{ 4, 0x150a, 0, {0xe4,0x93,0x80,0x03} },
+{ 3, 0x150e, 0, {0xa8,0x82,0xe2} },
+{ 4, 0x1511, 0, {0xf8,0xd0,0xe0,0xc8} },
+{ 1, 0x1515, 0, {0x22} },
+{ 0, 0x0000, 1, {0} }
+
+};
+
+static unsigned char bitstream[] = {
+
+0x00,0x09,0x0F,0xF0,0x0F,0xF0,0x0F,0xF0, 0x0F,0xF0,0x00,0x00,0x01,0x61,0x00,0x0D,
+0x64,0x61,0x62,0x75,0x73,0x62,0x74,0x72, 0x2E,0x6E,0x63,0x64,0x00,0x62,0x00,0x0B,
+0x73,0x31,0x30,0x78,0x6C,0x76,0x71,0x31, 0x30,0x30,0x00,0x63,0x00,0x0B,0x31,0x39,
+0x39,0x39,0x2F,0x30,0x39,0x2F,0x32,0x34, 0x00,0x64,0x00,0x09,0x31,0x30,0x3A,0x34,
+0x32,0x3A,0x34,0x36,0x00,0x65,0x00,0x00, 0x2E,0xC0,0xFF,0x20,0x17,0x5F,0x9F,0x5B,
+0xFE,0xFB,0xBB,0xB7,0xBB,0xBB,0xFB,0xBF, 0xAF,0xEF,0xFB,0xDF,0xB7,0xFB,0xFB,0x7F,
+0xBF,0xB7,0xEF,0xF2,0xFF,0xFB,0xFE,0xFF, 0xFF,0xEF,0xFF,0xFE,0xFF,0xBF,0xFF,0xFF,
+0xFF,0xFF,0xAF,0xFF,0xFA,0xFF,0xFF,0xFF, 0xC9,0xFF,0xFF,0xFF,0xDF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFB,0xFF,0xA3,0xFF,0xFB,
+0xFE,0xFF,0xBF,0xEF,0xE3,0xFE,0xFF,0xBF, 0xE3,0xFE,0xFF,0xBF,0x6F,0xFB,0xF6,0xFF,
+0xBF,0xFF,0x47,0xFF,0xFF,0x9F,0xEE,0xF9, 0xFE,0xCF,0x9F,0xEF,0xFB,0xCF,0x9B,0xEE,
+0xF8,0xFE,0xEF,0x8F,0xEE,0xFB,0xFE,0x0B, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xBF,0xFF,0xFF,0xFB,0xFF,0xFF, 0xBF,0xFF,0xFF,0xFC,0x17,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0x7F, 0xFF,0xFF,0xFB,0xFF,0xFF,0x7F,0xFF,0xFF,
+0xFC,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFB,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0x5F,0xFF, 0xFF,0xFD,0xFF,0xFF,0xDB,0xFF,0xFD,0xFF,
+0x77,0xFF,0xFD,0xFF,0xFF,0xDF,0xFE,0xFD, 0xFF,0xFF,0xF2,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFD,0xFF,0xFF,0xFF,0xFD,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE1,
+0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xE3,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBF,
+0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0x67,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0x7F,0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF, 0xFF,0xFF,0xDF,0xFF,0xFF,0xFF,0x2F,0xFF,
+0xF3,0xFD,0xFF,0x7F,0xDE,0xF7,0xFD,0xFF, 0x7F,0xF7,0x7D,0xFF,0x7F,0xDF,0xF7,0xBD,
+0xFF,0x7F,0xFF,0x1F,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xEF,0xFB,
+0xFE,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFF, 0x3F,0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,
+0x9F,0xE7,0xFA,0x7F,0x9F,0xE7,0xF9,0xFE, 0x7F,0x9F,0xE7,0xFF,0xFC,0x7F,0xBF,0xBF,
+0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0xB7, 0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,
+0xFF,0xE0,0xFD,0xF9,0xFE,0x7F,0x9F,0xE7, 0xF9,0xFE,0x7F,0x9D,0xF9,0xFE,0x7D,0x9D,
+0xE7,0xF9,0xFE,0x7F,0x9F,0xED,0xED,0xFF, 0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0x7F,
+0xDF,0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF, 0x7F,0xDF,0xFF,0x9B,0xFF,0xEF,0xFB,0xFE,
+0xFB,0xBF,0xEF,0xBB,0xFE,0xFF,0xAF,0xBB, 0xBE,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFF,
+0xB7,0xBF,0xDB,0xF6,0xBD,0xBF,0x6B,0xDB, 0xF6,0xF9,0xBF,0x5B,0xD6,0xF9,0xBF,0x6F,
+0xDB,0xF6,0xFD,0xBF,0xFF,0x0E,0xFF,0xFF, 0xFF,0xFF,0x5F,0xFF,0xF7,0xFF,0xFF,0x7F,
+0xF7,0xBD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xDF,0x9F,0xFF,0xFF,0xFF,0xFE,0xFF,
+0xFF,0xEF,0xFE,0xFE,0xFF,0xFF,0x77,0xFF, 0xFB,0xFB,0xFF,0xFF,0xFF,0xFF,0xF8,0x3F,
+0xFF,0xFD,0xFF,0xFF,0xFF,0xFD,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xF4,0x7F,0xFF,0xFE,0xFD, 0xBE,0xFF,0xDF,0xFE,0xFF,0xFF,0xEF,0x7F,
+0xFF,0xCF,0xFF,0xCF,0xFF,0xFF,0xFF,0xDF, 0xE6,0xFF,0xFF,0x7F,0xDF,0xF7,0xDD,0x7F,
+0x7F,0xDF,0xF7,0xFF,0x7F,0xDF,0xD7,0xFD, 0xFF,0x7F,0xDF,0xF7,0xFF,0xCD,0xFF,0xF2,
+0xFF,0xFF,0x4F,0x7F,0xF4,0xFF,0xFF,0xFF, 0xE7,0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xBB,0xFF,0xEF,0xFF,0xFE,0xFF, 0xFF,0xFF,0xEF,0xFF,0xFF,0xEF,0xFF,0xFB,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x65, 0xEF,0xFF,0xFF,0x7F,0xFF,0xFD,0xEF,0xFF,
+0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFE,0xCF,0xDF,0xFE,0xFF,
+0xFF,0xFB,0xFF,0xFF,0xFF,0xFF,0xF3,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFE,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xBF,0xFF, 0xFF,0xFF,0xE3,0x7F,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xEF,0xEB,0xFF,0xFE,0xBF,0xFF, 0xEB,0xFF,0xFC,0x7F,0xFF,0xFF,0xFF,0xEE,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xDD,0xFF, 0xD6,0xFF,0xFD,0xBF,0xFF,0xFB,0xFF,0xFE,
+0xFD,0xFF,0xFF,0xFD,0xEF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xDE,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xBF,0xFF,0xFD,0xFF,0x7F,0xBF, 0xFF,0x5F,0xDF,0xFF,0xFF,0xBF,0x77,0xFF,
+0xFF,0xFF,0x7F,0xD7,0xFF,0xFF,0xFF,0xFF, 0xFF,0xC3,0xFF,0xFF,0xFF,0xFF,0xDF,0xEF,
+0xFF,0xFF,0xFE,0xFB,0xFF,0xFF,0xDF,0xBF, 0xFF,0xFF,0xFF,0xFF,0xED,0xFF,0xB7,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xAF,0x7F,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xDF,0xBF,0xDF,0xF3,0xFD,0xFB,0xFF,0x5B,
+0xFD,0xFF,0xBF,0xEF,0xF7,0xFF,0xFF,0x7D, 0xFF,0xFF,0xFF,0xFF,0xF8,0x3B,0xFF,0xBF,
+0x6F,0xFF,0xFE,0xFF,0xBF,0xFF,0xEB,0x7D, 0xFF,0xEF,0xFB,0xFE,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xF2,0x7F,0xFC,0xFF,0x3F,0xDF,0xED, 0xFE,0xFF,0xFF,0xFF,0xFF,0xEF,0x5F,0xF7,
+0xB5,0xFF,0xEF,0xFF,0xFF,0xFF,0xE0,0x3F, 0x9F,0x9E,0xFF,0xFF,0xEF,0xFF,0xDF,0xFF,
+0xBF,0x5F,0xBF,0xCF,0xF3,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0x69,0xAF,0x33,0xFD,0xFF,
+0xFB,0xFF,0xFF,0xFF,0xFF,0xFC,0xFF,0x7F, 0xD9,0xFF,0xDF,0xFF,0xFF,0xFF,0xFF,0xF5,
+0xA3,0xDF,0x6E,0xDE,0xFF,0xFF,0xBD,0xFF, 0xFF,0xFE,0xFF,0xFF,0xFF,0xFE,0xE7,0xFD,
+0xFF,0xFF,0xFF,0xF9,0xEF,0xC6,0xFE,0xB7, 0xAD,0xE5,0xF9,0xFF,0xFF,0xFF,0xCF,0xFF,
+0xFF,0xFF,0xCD,0xFB,0x7F,0xFF,0xFF,0xFF, 0xF9,0xF6,0x0F,0xDF,0xEC,0xCF,0x7F,0xFF,
+0xFB,0x7F,0xFF,0xFF,0xFF,0xFD,0xFF,0xFE, 0xF9,0xFD,0x7F,0xFF,0x7F,0xFF,0xF9,0x5B,
+0xFF,0x73,0xDC,0xFD,0x7B,0xDF,0xFF,0xFF, 0xFF,0x7B,0xFF,0xFF,0xF7,0x53,0xD6,0xFF,
+0xFF,0xFF,0xFF,0xD8,0x9F,0xFE,0xFF,0xEF, 0x7F,0xEE,0xFF,0xFF,0xFF,0xFB,0xED,0xED,
+0xFD,0xFF,0xFE,0xFF,0xFF,0xFB,0x7F,0xFF, 0xE2,0x7F,0xFF,0x6F,0xD8,0x57,0xF7,0xFF,
+0xFF,0xFF,0xDF,0xFF,0xE8,0xFF,0xFF,0xFD, 0xFF,0xFF,0xFC,0x7F,0xFF,0xE4,0xFF,0xFB,
+0xEF,0xFB,0xFE,0xDF,0xB7,0xED,0xFF,0xFE, 0xDF,0x7F,0xFF,0xFE,0x7F,0xB7,0xFF,0xFF,
+0xFF,0xFF,0x89,0xFF,0xFF,0xCF,0xF3,0xFE, 0x7F,0xFF,0xEF,0xFF,0xFE,0x7E,0x7F,0xFB,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF1, 0xFF,0xEB,0x7A,0xD5,0xBF,0x6F,0xDB,0xBE,
+0xFD,0xB7,0xD8,0xF6,0xE5,0xBF,0x6F,0xFB, 0xFE,0xF5,0xBD,0x7E,0x06,0xFF,0xDF,0xF7,
+0xFB,0xF6,0xFF,0x3F,0xFF,0xDB,0xFF,0xFF, 0x6F,0xFB,0xF7,0xFF,0xFF,0xFF,0xFB,0xFE,
+0xF7,0xAF,0xFF,0xB7,0xED,0xEF,0xF7,0xFE, 0xFF,0xFF,0xDF,0xFF,0xFE,0xFF,0xEF,0xFF,
+0xFF,0xFF,0xFF,0xBF,0xF7,0xFC,0x1F,0xEE, 0xFB,0xFE,0xBD,0xFF,0x7F,0x5F,0xD7,0xFD,
+0xFB,0x43,0xFF,0xFF,0xFD,0xFF,0x5F,0xFF, 0xF7,0xFF,0xF9,0x3F,0xFF,0xCF,0xF3,0xFD,
+0xF7,0x7E,0xEF,0xA7,0xF9,0xFE,0x8F,0xA7, 0xE9,0xF3,0x7E,0x9F,0xFB,0xF8,0xFF,0xFF,
+0x3F,0xFD,0x7F,0x5F,0xDF,0xFD,0xFF,0xFF, 0x5F,0xFF,0xFD,0x5F,0xFF,0xFF,0x7F,0xFD,
+0x7F,0xFD,0x9F,0xFF,0xE0,0xFF,0xFA,0xF8, 0xBE,0x6F,0x9F,0xE6,0xF8,0xBE,0x3F,0x9A,
+0xF9,0xBE,0x6F,0x9F,0xE2,0xF9,0xFE,0x6F, 0x9F,0xF9,0xFF,0xF5,0xFD,0x7F,0xCF,0xDF,
+0xFD,0xFD,0x7F,0xFF,0xF5,0xFF,0xFF,0xFF, 0xF7,0xF5,0xFD,0x0F,0xDB,0xFF,0xD3,0xFF,
+0xEB,0xFA,0xFF,0xFF,0xBF,0xFF,0xFA,0xFF, 0xFF,0xCB,0xFB,0xFE,0xFF,0xFF,0xEB,0xFA,
+0xFE,0xFF,0xFF,0xB7,0xFF,0xFF,0xFF,0xFF, 0xBF,0xFF,0xDF,0xF5,0xFF,0xFF,0xD7,0xFF,
+0xFF,0xFF,0xDF,0xD7,0xF5,0xFF,0x7F,0xFE, 0x4F,0xFF,0xFD,0xFF,0x7F,0x7F,0xFF,0xAD,
+0xEB,0xFB,0xFF,0xAD,0xFF,0xFF,0xFF,0xFF, 0xAF,0xEB,0xFB,0xFF,0xFC,0x0D,0xFF,0xFF,
+0xDF,0xD2,0xFD,0xFF,0xFF,0xFD,0xF6,0xFF, 0xFF,0x7F,0xFF,0xFF,0x1F,0xFF,0xFF,0xFF,
+0xFF,0xFB,0x3F,0x7D,0xEB,0x32,0xFE,0xBF, 0x2F,0xEB,0xFA,0xAE,0xBD,0xE0,0xFA,0x7E,
+0xBF,0xAD,0xEB,0xFA,0xFE,0xBF,0xF5,0x7F, 0xFF,0xDE,0xFE,0xE3,0xFB,0xFF,0xFF,0xFF,
+0xDF,0xEF,0x4F,0xDF,0xFF,0x7F,0xDF,0xFF, 0xF7,0xFF,0xFF,0xF8,0x7F,0xFF,0xFF,0xEF,
+0xFB,0xFF,0xFF,0xFF,0xEF,0xFF,0xFF,0xDF, 0xED,0xFB,0xDF,0xFF,0xBF,0xFF,0xFF,0xFF,
+0x81,0xFF,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF, 0xFF,0xFF,0xFE,0xDD,0xFE,0xEF,0xFD,0xFF,
+0xFF,0xFB,0xFE,0xF7,0xFF,0x93,0xFD,0xFB, 0x7E,0xFF,0xFE,0x87,0xE9,0xFF,0x7F,0xB3,
+0x9F,0xFE,0xFE,0xFF,0xAF,0xFD,0xFE,0x7E, 0x3F,0xFE,0x67,0xFF,0xFF,0xF7,0xFF,0xFF,
+0xFC,0xF7,0xDF,0xFD,0xFF,0x7F,0xFF,0xFF, 0x7F,0x6D,0xFF,0xFF,0xFE,0xFF,0xFF,0x2F,
+0xFF,0xBF,0xFF,0xFF,0xEE,0xFF,0xBE,0xFF, 0xFF,0xFE,0xFF,0xEF,0xFF,0xFF,0xFE,0xFF,
+0xEF,0xFF,0xFF,0xFA,0x5F,0xFF,0xFF,0xFB, 0xFF,0xFF,0xEF,0xFF,0xFB,0xFE,0xFD,0xFF,
+0xFE,0xFF,0xFB,0xFF,0xFF,0xFF,0x7F,0xFF, 0xFE,0xBF,0xDF,0xFF,0xFB,0xFF,0xFF,0xF7,
+0xFC,0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F, 0xFF,0xFF,0xFF,0xFF,0xFF,0xF2,0x7F,0xFF,
+0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0xFF, 0xF3,0xFF,0xFF,0xFF,0xEF,0xFB,0xFF,0xFF,
+0xFF,0xDF,0xE2,0xFF,0xFF,0xFB,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFB,0xE7,0xFF,0xFD,
+0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xED, 0xEF,0xFD,0xFF,0xFF,0xDF,0xD7,0xF5,0xFD,
+0x7F,0x5D,0xFD,0xFF,0x7F,0xDF,0x97,0xF4, 0xFD,0x7B,0x5F,0xFF,0xC9,0xFF,0xFB,0xFE,
+0xFF,0xBF,0xFF,0x5F,0xFF,0xFF,0xF7,0xFF, 0xEF,0xFD,0xFF,0xEF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xF7,0xFF,0xD7,0xFD,0x7D,0x7F,0xFF, 0xFF,0xFF,0xFF,0xEF,0xDF,0xF7,0xFD,0xFF,
+0xBB,0xFF,0xFF,0x7F,0xFF,0xFE,0xE3,0xFF, 0xF9,0xFE,0x7F,0xBF,0xEF,0xFB,0xFE,0xFF,
+0xBF,0xF9,0xFE,0xFF,0x9F,0xEF,0xF9,0xFE, 0xFF,0xBF,0xF3,0xDA,0xFF,0x37,0xCD,0xF3,
+0x7C,0xDF,0x37,0xCD,0xF3,0x7F,0x37,0xCD, 0xF3,0x7C,0xDF,0x37,0xCC,0xF3,0x7F,0x5A,
+0xBD,0xF6,0xFD,0xBF,0x6F,0xDB,0xF6,0xFD, 0xBF,0x6F,0xDE,0xFD,0xBF,0x6F,0xDB,0xF6,
+0xFD,0xBF,0x6F,0xFE,0xF1,0x6F,0xEB,0x7A, 0xDE,0xB7,0xAD,0xEB,0x7A,0xDE,0xB7,0xAF,
+0x7A,0xDE,0xB7,0xAD,0xEB,0x7A,0xDE,0xB7, 0xFF,0x7E,0xFF,0xFE,0xCD,0xB3,0x6C,0xDB,
+0x36,0xCD,0xB3,0x6C,0xDE,0xCD,0xB3,0x6C, 0xDB,0x36,0xCD,0xB3,0x6C,0xDF,0xC9,0xBF,
+0xF7,0xBD,0xEF,0x7A,0x9E,0xA7,0xA9,0xEA, 0x7A,0xB7,0xBD,0xEA,0x7B,0xDE,0xA7,0xBD,
+0xCA,0x72,0x8D,0x91,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xEF,0xFB,0xFE,0xF7,0xEF,0xFB,
+0xFE,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFE, 0x87,0xFF,0xF6,0xFD,0xBF,0x6F,0xDB,0xF6,
+0xFD,0xBF,0x6F,0xF6,0xFD,0xBF,0x6F,0xDB, 0xF6,0xFD,0xBF,0x6F,0xFE,0x4F,0xFF,0xBF,
+0xEF,0xBB,0xEE,0xFB,0xBE,0xEF,0xBB,0xEF, 0xBE,0xEF,0xBB,0xEE,0xFB,0xBE,0xEF,0xBB,
+0xEF,0xFC,0x5F,0xFF,0xFF,0xFF,0x3F,0xCF, 0xF3,0xFC,0xFF,0x3F,0xCF,0xFC,0xFF,0x3F,
+0xCF,0xF3,0xFC,0xFF,0x3F,0xCF,0xFD,0x9F, 0xFE,0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF,
+0xEB,0xFE,0xBF,0xAF,0xEB,0xFA,0xFE,0xBF, 0xAF,0xEB,0xFF,0xE1,0x6F,0xFD,0xFF,0x7F,
+0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFD,0xFF, 0x7F,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFF,
+0x7A,0xBF,0xFB,0xFE,0xDF,0xB7,0xED,0xFB, 0x7E,0xDF,0xB7,0xFB,0x7E,0xDF,0xB7,0xED,
+0xFB,0x7E,0xDF,0xB7,0xFF,0xC9,0xFF,0xFF, 0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,
+0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEE, 0xFB,0xFE,0xBB,0xFF,0xFE,0xFF,0xBF,0xEF,
+0xFB,0xFE,0xFF,0xBF,0xEF,0xFE,0xFF,0xBF, 0xEF,0xFB,0xFE,0xFF,0x3F,0xCF,0xFF,0xE7,
+0xFE,0xFF,0xF5,0xFD,0x77,0x5D,0xD7,0x35, 0xDD,0x77,0xD7,0xF5,0xCD,0x7B,0x5D,0xD7,
+0xF5,0xDD,0x77,0xFE,0x27,0xFF,0xFF,0x8B, 0xE2,0xF8,0xBE,0x2F,0x8B,0xE2,0xF9,0xAF,
+0x8B,0xE2,0xF8,0xBE,0x2F,0x8B,0xE2,0xF9, 0xFE,0x1F,0xFF,0x5F,0xD7,0xF5,0xFD,0x7F,
+0x5F,0xD7,0xF5,0xFF,0x5F,0xD7,0xF5,0xFD, 0x7F,0x5F,0xD7,0xF5,0xFF,0xFA,0x3F,0xFE,
+0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF,0xEB, 0xEC,0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF,
+0xEB,0xFF,0xFE,0x7F,0xFD,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE6, 0xFF,0xFA,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,
+0xF7,0xFC,0xFF,0xDF,0xF7,0xFD,0xFF,0x7F, 0xDF,0xF7,0xFD,0xFF,0xF5,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0x02,0xFF,0xFE,0xBF,0xAB,0xEB,0xFA, 0xBE,0xBF,0x23,0xEB,0xDE,0x1F,0xAF,0xEA,
+0xFA,0xFE,0xAF,0xAF,0xEB,0xFD,0x97,0xFF, 0xF3,0xFC,0x7B,0x1F,0xCF,0xF1,0xFC,0x7F,
+0x1F,0xF1,0xFC,0x77,0x1F,0xCD,0xF1,0xFC, 0xFF,0x1F,0xFE,0x87,0xFF,0xAF,0xEF,0xFA,
+0xFE,0xFF,0xAF,0xEF,0xFA,0xFD,0xBF,0x2B, 0xFB,0x7E,0xBF,0xBF,0xEB,0xFB,0xFB,0xFB,
+0xDF,0xFF,0xFB,0xF7,0xFF,0xFF,0x7F,0xF7, 0xF7,0xFF,0xFD,0xDF,0xFE,0xFC,0xDF,0xFF,
+0xDF,0xFF,0xFD,0xFF,0xDA,0xBF,0xFF,0xBB, 0xEF,0xFB,0xF9,0xFF,0xBE,0xEF,0xFB,0xFB,
+0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFF,0xF7,0x7F,0xFD,0xD7,0xFF,0xFF,0x7F,
+0xFF,0xFF,0xFF,0xFE,0xF7,0xFF,0xFE,0xFF, 0xF7,0xFF,0xFF,0x7F,0xFF,0xFF,0xEC,0xFF,
+0xFF,0xFE,0xDF,0xBF,0xFF,0xFB,0xFE,0xFF, 0xBB,0x68,0xAE,0x1F,0xAE,0xFB,0xFB,0xFF,
+0xFF,0xBF,0xFF,0xD5,0xFF,0x7F,0xFF,0xFF, 0xF7,0xFE,0xFE,0xFF,0xBF,0xEF,0x9F,0xFD,
+0x7F,0xFF,0xCB,0xFF,0xFF,0xDF,0xFF,0xFF, 0xBB,0xF7,0xBF,0xFF,0xFF,0xFF,0xFF,0xDF,
+0xFF,0xBF,0xFB,0xFF,0xFF,0xFF,0xDE,0x3F, 0xFF,0xFF,0xFF,0xFF,0xFF,0xA7,0xFF,0xFF,
+0xFF,0xFF,0xEF,0xFF,0x7F,0xFB,0xFD,0xFB, 0x7F,0xFF,0xFF,0xFF,0xFF,0xCF,0xF3,0x7C,
+0xFF,0x7F,0x8D,0x7F,0xFF,0xFF,0xFF,0xFF, 0xFB,0xFF,0xF7,0xFB,0xFE,0xFD,0xFF,0xFF,
+0xFF,0xFF,0xF7,0xFD,0xFF,0x7F,0xFD,0x1F, 0xFD,0xFF,0xFF,0xFF,0xFF,0xBF,0xDF,0xFF,
+0xFF,0xFE,0x5C,0xFF,0x6D,0xFF,0x7F,0xAB, 0xE7,0xF1,0xFF,0xFD,0x9F,0xFF,0xFF,0xAD,
+0xEB,0x7A,0x3F,0x1F,0xFF,0xFF,0xFE,0xBF, 0xAF,0xF3,0xDE,0xF5,0xFF,0x8F,0xFB,0xDF,
+0xE6,0x7F,0xFF,0xDF,0xF3,0xFD,0xFF,0x7E, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0xF7,0xF3,
+0x7F,0xDF,0xF7,0xEF,0xFF,0xF6,0x3F,0x9F, 0xDF,0xFF,0xFF,0xEE,0xFF,0xFF,0xEF,0xFB,
+0xFF,0xFF,0xF9,0xFB,0xFE,0x4F,0xBF,0xEF, 0xBB,0xFF,0x69,0xAF,0xAF,0xFC,0xFF,0x3F,
+0xDD,0xFF,0xFC,0xBF,0x8F,0xFF,0xFD,0xF3, 0xBF,0xED,0x9E,0xFC,0xBF,0x6F,0xF5,0xD3,
+0xDF,0xFF,0xDB,0xD6,0xF5,0xEF,0xFD,0xFE, 0xFF,0xB9,0xFF,0x1F,0xD2,0xA9,0xAF,0xFF,
+0xDB,0xF7,0xBF,0xEF,0x46,0xFF,0xFF,0xAD, 0xEB,0x7A,0xDF,0xEF,0xF7,0xFF,0x7F,0xF7,
+0x9F,0xED,0xFF,0x7F,0xFF,0xAD,0xEB,0x7F, 0xF5,0x6F,0xFF,0xFD,0xFB,0xD6,0xF4,0xF7,
+0xFB,0xF9,0x7E,0x7F,0xFF,0x5F,0xC2,0xFE, 0xBF,0xFD,0xFB,0x33,0xDF,0xF9,0x5B,0xFF,
+0xFF,0xDD,0x67,0x7D,0xCF,0xEF,0xDB,0xEC, 0xFF,0x77,0xDD,0xF7,0xFD,0xFF,0xFF,0xDE,
+0xA7,0xBF,0xD4,0x9F,0xFF,0xFF,0xBF,0xEF, 0xFE,0xFF,0xDF,0xEF,0xBB,0xFF,0xFF,0xEF,
+0xEB,0xFA,0xFF,0xEF,0xBD,0xFB,0xFF,0xE2, 0x7F,0xFF,0xDF,0xDF,0xF7,0xFD,0xBF,0xBB,
+0x73,0xF7,0xFD,0x7F,0xDF,0xDE,0xF7,0xBF, 0xEA,0xDB,0xF6,0xFF,0xD6,0xFF,0xFF,0x66,
+0xFF,0xBE,0xFF,0xBF,0x6B,0xD9,0xF6,0xDF, 0xFF,0xFB,0x7E,0x7F,0xB7,0x7E,0xFF,0xFE,
+0xFF,0xCD,0xFF,0xFE,0x7F,0xFF,0xFC,0xFD, 0x3F,0xFB,0xFB,0xF7,0xFF,0xFF,0xFB,0xF6,
+0x7D,0xFE,0x7F,0xFF,0xFC,0xFF,0xB9,0xFF, 0xF9,0xFA,0xFE,0xBF,0xAF,0x5B,0xD6,0xED,
+0xAD,0x7B,0xF6,0xF9,0xBF,0xEF,0xF8,0xFA, 0xFE,0xBF,0xFE,0xE6,0xFF,0xFF,0xF7,0xFD,
+0xFF,0x7F,0xBF,0xEF,0xF3,0xFF,0xFF,0x6F, 0xF7,0xFE,0xFF,0xFF,0xF7,0xFD,0xFE,0xF7,
+0xEF,0xFF,0xFB,0xEF,0xFB,0x7E,0xDE,0xFE, 0xFF,0xBF,0xFF,0xFE,0xFF,0xFF,0xFB,0xFF,
+0xFF,0xEF,0xFB,0x6F,0xFC,0x1F,0xFE,0xE7, 0xFF,0xFF,0xFF,0xEF,0xFF,0xD3,0xB4,0xBB,
+0xFF,0xFF,0xFD,0xBF,0x6F,0xE3,0xFE,0xFF, 0xBF,0xFC,0xBF,0xF7,0xCF,0xF7,0xFD,0xFF,
+0x2F,0xDF,0xAB,0xEA,0xFF,0xDF,0xE7,0xEA, 0x9A,0xAF,0xEF,0xFB,0xFE,0xFF,0xF5,0x3F,
+0xFD,0x7E,0xFF,0xD7,0xF5,0xFB,0xFF,0xFD, 0xF7,0xFF,0x7F,0xFE,0xF7,0xFD,0xFF,0xD7,
+0xFF,0xD7,0x7F,0xEE,0x7F,0xFA,0x79,0xFE, 0x2F,0x8B,0xE6,0xF9,0xFE,0x3F,0x9E,0xF9,
+0xBE,0x2F,0x0B,0xE7,0xF9,0xFE,0x2F,0x9F, 0xFD,0xFF,0xFE,0x7D,0x7F,0x5F,0xD7,0xFF,
+0xFF,0x7F,0xFF,0xFD,0xFF,0x7F,0x5F,0x97, 0xFF,0xFD,0x7F,0x5F,0xFF,0xE3,0xFF,0xFF,
+0xFA,0xFE,0xBF,0xAF,0xFB,0xFB,0xFF,0xFF, 0xCF,0xEB,0xFE,0xBF,0xAF,0xFF,0xFA,0xFE,
+0xBF,0xFF,0x87,0xFF,0xFF,0xF5,0xFF,0xFF, 0xFF,0xFF,0xFD,0xFF,0x7F,0xFF,0xFF,0xFF,
+0xFB,0xFF,0xFF,0xF5,0xFF,0xFF,0xFE,0x0F, 0xFF,0xFD,0xEB,0xFF,0xFF,0xF7,0xFF,0xEF,
+0x7B,0xDF,0xFE,0xFF,0xFF,0xDF,0xF7,0xFD, 0xEB,0x7F,0xDF,0xFF,0x5F,0xFF,0xFF,0xFF,
+0xFF,0xFD,0xBF,0xFF,0x7E,0xFA,0xBF,0xC7, 0xDB,0xF7,0xBD,0x3F,0xFB,0xFF,0xF6,0xFF,
+0xFA,0xAF,0xFF,0xEB,0xFA,0xFE,0x3F,0x2F, 0xEA,0xFA,0x3E,0xAD,0xC9,0xBA,0xF6,0xAD,
+0xAF,0xEB,0xFA,0xF6,0xBF,0xFE,0x7F,0xFF, 0xFF,0xFD,0xFF,0xF1,0x7F,0x3F,0xCF,0xF1,
+0xEF,0xFF,0x7F,0xFF,0xBC,0xDF,0xDF,0xF7, 0xDD,0xFF,0xE0,0x7F,0xFF,0xFF,0xFE,0xFF,
+0xFA,0xEC,0xBB,0x7F,0x5F,0xFF,0xFB,0xEC, 0xFF,0xEF,0xB7,0xFF,0xF7,0xFF,0xFF,0xB5,
+0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0xEE,0xDF, 0x5F,0xDF,0xDE,0xFF,0xAE,0xE7,0x77,0xFF,
+0xFF,0xDF,0xF7,0xFF,0xE3,0xFF,0xFA,0xBB, 0xFE,0xFF,0xAF,0xFD,0xFB,0xFE,0xBF,0xAB,
+0xF9,0xFE,0xFF,0xBF,0x7F,0xBF,0xFE,0xBD, 0xFE,0xD7,0xFF,0x9F,0xFD,0xFF,0xBE,0xEF,
+0xFF,0xEE,0xFD,0xBB,0x5B,0xEF,0xFF,0x7F, 0xEF,0xFF,0xEF,0xFF,0x7F,0xFF,0x4F,0xFF,
+0xEF,0xFB,0xBC,0xFC,0xFF,0xFF,0xFF,0xFE, 0xFE,0xFD,0xFA,0xFE,0xFB,0xFF,0xFD,0xF3,
+0xFB,0xFF,0xF8,0x5F,0xFF,0xFF,0xD7,0xF5, 0xFD,0xDF,0xEF,0xFF,0xF3,0xDC,0x5F,0xCE,
+0xF5,0xBD,0xFF,0xFF,0xD7,0xFF,0xFF,0xF9, 0x3F,0xFF,0xDF,0xF7,0xFF,0xFE,0xFF,0xFD,
+0xFF,0xFB,0xFF,0xF7,0xB9,0x7D,0xFE,0xDF, 0xFF,0xFF,0xFF,0xFF,0xF9,0x7F,0xFF,0xFE,
+0xFF,0xFF,0x7F,0xFF,0xFE,0xFF,0xFF,0xF7, 0xF6,0xFF,0xBF,0xF1,0xF8,0xFF,0xFF,0xFF,
+0xFF,0xE0,0xFF,0xFF,0xFF,0xFF,0xF9,0xFF, 0xFF,0xFF,0xFF,0xFF,0xEF,0xEF,0xFF,0xFF,
+0x9B,0xFB,0x7F,0xFF,0xFF,0xFF,0xC1,0xFF, 0xDF,0xFF,0x3F,0x5F,0xD7,0xBF,0xEF,0xBB,
+0xDE,0xEE,0xFF,0x7F,0xDF,0xFF,0xFE,0xF5, 0x7F,0xDF,0xFF,0x99,0xFF,0xFF,0xFA,0xFF,
+0xBF,0xFD,0xEB,0x7A,0xFF,0xB7,0xFE,0xFE, 0xFF,0xFF,0xEF,0xFF,0xFF,0xFD,0xBF,0xFF,
+0x97,0xFF,0xFD,0xF7,0xFF,0x7F,0xF7,0xFF, 0xFF,0xFD,0x5F,0xFE,0xF3,0xF9,0xDF,0xDF,
+0xFF,0xFF,0xFC,0xFF,0xFF,0x83,0xFF,0xFF, 0xFE,0xFF,0x9E,0xEC,0xFB,0xEE,0xFF,0x9F,
+0xBF,0xEF,0xFF,0xFE,0xED,0x7B,0xFF,0xFF, 0xFF,0xF1,0x5A,0xFF,0xFF,0xFD,0xFF,0x7C,
+0x69,0x3B,0xDF,0xFF,0x7F,0x1F,0xDF,0xFF, 0xFD,0xBA,0xFF,0xFF,0xFB,0xFF,0x5B,0xBD,
+0xFF,0xFF,0xFF,0xFF,0xD7,0xB6,0xED,0xE9, 0xFF,0xD6,0xBD,0x6F,0x5F,0xFB,0xFF,0xEF,
+0xFF,0x5F,0xFE,0xF6,0x6F,0xFF,0xFF,0xFF, 0xFF,0xF7,0xEB,0x7A,0xDF,0xFF,0x9F,0x7F,
+0x7F,0xFF,0xB7,0xFF,0xFF,0xFE,0xDF,0xFF, 0x6C,0xFF,0xFB,0xFF,0xBB,0x6F,0xEB,0xFE,
+0xCC,0xF7,0xA5,0xFA,0x5C,0xF5,0x75,0xBB, 0xB7,0xDF,0xFE,0x6F,0x5F,0xC5,0xBF,0xFD,
+0x7B,0xFE,0xFF,0x95,0xE7,0x29,0xCF,0x4F, 0xF5,0x91,0xEE,0x6B,0xDF,0xEF,0xFD,0x54,
+0xF5,0xBD,0xB1,0xFF,0xEF,0xEE,0xFB,0xBE, 0xBF,0xAF,0xFE,0xDE,0xBD,0x6F,0xDA,0xF2,
+0xFF,0xAF,0xBE,0xFF,0xFF,0xFD,0x7E,0xA7, 0xFF,0xF7,0xFF,0xBF,0xEF,0x7B,0xF6,0xFD,
+0xBD,0x4A,0xF2,0x85,0x85,0xBF,0x5B,0xFE, 0xB5,0xFD,0xFA,0xFF,0x4F,0xFF,0xFE,0xDF,
+0xFF,0xED,0xFF,0xBF,0xFF,0xBF,0x7F,0xFE, 0xFF,0xB7,0x6D,0xFF,0xF7,0xBF,0xBF,0xEF,
+0xFD,0x1F,0xFF,0xFE,0x7D,0xFF,0x67,0xFF, 0xFF,0xFF,0x3F,0x7F,0xFE,0xBF,0xFF,0xE7,
+0xDF,0xE7,0xFF,0xEF,0x6B,0xFC,0x1F,0xFF, 0xBF,0xEF,0xFB,0xFE,0xDE,0xBF,0xAF,0xFA,
+0xFF,0xB6,0xEF,0xF9,0xFE,0xFF,0x8F,0xEF, 0xDB,0xEF,0xAB,0x6F,0xFB,0xFE,0xFF,0xFF,
+0xEF,0xFD,0xFF,0x7F,0xFF,0xFF,0xDE,0xFF, 0xFF,0xEF,0xFF,0xFF,0xFF,0x3F,0xFF,0x6C,
+0xFF,0xBF,0xFB,0xFF,0xFE,0xFF,0xFB,0xFE, 0xDF,0xFF,0xFF,0xEF,0xFF,0xFF,0xBF,0xFF,
+0xFF,0xFE,0xFB,0xFF,0xD5,0x7F,0xFF,0xFF, 0xEF,0xFB,0xFF,0xFF,0xBF,0xEF,0x43,0xB5,
+0xFD,0x6F,0xCF,0xD6,0xBE,0x3F,0x7F,0xDB, 0xFE,0xC3,0xFF,0xFD,0xFF,0xAF,0xEB,0xFB,
+0xFC,0xFF,0x3E,0xEF,0xE8,0xFA,0xBD,0xCD, 0xAA,0xFE,0xFE,0x7D,0xCF,0xFF,0xB7,0xFF,
+0xF7,0xFF,0xFF,0xFF,0xFD,0xFF,0x75,0xCD, 0x52,0xD7,0xFD,0xFB,0xF7,0xDD,0xFB,0xEF,
+0xEB,0xFF,0xFF,0x4F,0xFF,0xBF,0x9F,0xE7, 0xF9,0xFC,0x7F,0x8B,0xC3,0xF9,0xAF,0x8F,
+0xE7,0xE9,0xBE,0x7F,0x9F,0xE6,0xF9,0xFC, 0x5F,0xFF,0xFF,0xF7,0xFD,0xFF,0x7A,0x5F,
+0xD7,0xED,0xFF,0xFF,0xD7,0xFF,0xDD,0x7F, 0xE7,0xFF,0xFC,0xFF,0xFC,0x3F,0xFF,0xFF,
+0xFF,0xFB,0xFF,0xFE,0xBF,0xAF,0xFF,0xFD, 0xFF,0xEF,0xFF,0xEB,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xF7,0x7F,0xFF,0x7F,0xDF,0xFF,0xFD, 0xFD,0x7F,0xFE,0xF7,0xFD,0x7F,0xDF,0xFF,
+0xFD,0xFF,0xFF,0xDF,0xFB,0xFF,0xEE,0xFF, 0xFB,0xFF,0xF7,0xFD,0xFF,0x7A,0xDF,0xF5,
+0xFD,0xFA,0xDF,0xF7,0xFC,0xFF,0x7F,0xDF, 0xBF,0xED,0xFF,0xC9,0xFF,0xDF,0xFF,0xBF,
+0x2F,0xFB,0xFF,0xBC,0xAD,0xFF,0xF7,0xFF, 0xFF,0xEF,0xD3,0xFF,0x7D,0xBF,0x6F,0xFF,
+0xFA,0xFF,0xFE,0xBF,0xAE,0xEA,0xFA,0xBE, 0xAD,0xA5,0xEB,0xCE,0xBF,0xA7,0xEB,0x5A,
+0xDE,0xBD,0xAF,0x6B,0xFD,0x57,0xFF,0xFF, 0xF4,0x7F,0x1F,0x7F,0xFD,0xFF,0x7F,0x36,
+0xF0,0xDF,0x79,0xFF,0xFF,0xFF,0xF7,0xFD, 0xBF,0xFF,0x87,0xFF,0xFB,0xF3,0xFC,0xFF,
+0xFF,0xFF,0xFF,0x7E,0xFF,0xBF,0xDF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0xBF,0xF8,0x9F,
+0xFF,0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFD, 0xF7,0xFC,0xBD,0xFF,0xFE,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFB,0xF9,0xBF,0xFF,0xFF,0xEB, 0xE2,0xFE,0xFF,0xBF,0xEF,0xA9,0xBA,0x2F,
+0xEB,0xF9,0xFE,0x77,0xDF,0xF7,0xFF,0xFF, 0xF9,0x7F,0xFF,0xFF,0x7F,0xEF,0xD7,0xFF,
+0xFD,0xFF,0xFB,0xF5,0xFF,0xBF,0x6F,0xDF, 0xFF,0xFF,0xFD,0xFF,0xFF,0xF0,0xFF,0xFF,
+0xFF,0x3F,0xCF,0xFF,0xBA,0xEE,0x9B,0xBF, 0xEE,0xD7,0xFE,0xCD,0xEF,0xFF,0xDF,0xBF,
+0xFF,0xFF,0xC5,0xFF,0xFF,0xFD,0x7F,0x4F, 0xFD,0xF6,0xD9,0xFF,0x4F,0xD6,0xFD,0xBF,
+0x6E,0xFF,0xFF,0xF4,0x7F,0xFF,0x7F,0x8B, 0xFF,0xFF,0xFF,0xFF,0xF7,0xFF,0xF9,0xFE,
+0x37,0xFF,0xD9,0xFB,0xF5,0xAF,0xFD,0xFF, 0xFF,0xFB,0xFF,0xFF,0x07,0xFF,0xFF,0xFF,
+0xFB,0xF7,0xFF,0xFD,0xFF,0x7C,0xFA,0x7E, 0x4F,0xFC,0xDF,0x1D,0xC7,0xFF,0xFF,0xFF,
+0xFF,0xAE,0xFF,0xFF,0xFF,0xFF,0xFD,0xFB, 0xFF,0xFF,0xFE,0xFE,0xFC,0xFF,0x7F,0x7F,
+0xBF,0xEF,0xFE,0xFF,0xFF,0xFF,0x5F,0xFD, 0xFF,0xFF,0xFF,0xFD,0x6F,0x5A,0xD7,0x7B,
+0xBE,0x5F,0xFE,0x39,0xFF,0xF7,0xFF,0xF7, 0xFD,0xFE,0xAA,0x1F,0xFF,0xFF,0xFF,0xFF,
+0xFE,0xFE,0xAB,0xAF,0xFD,0xFE,0xBF,0xFF, 0xF7,0xFF,0x7F,0xFE,0x8F,0xE3,0xFB,0xEE,
+0x7F,0xFF,0xFF,0xFF,0xFF,0xEB,0xFB,0xFF, 0xFD,0xBF,0xEF,0xDF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFB,0xE4,0x3F,0xFF,0xDF, 0xFF,0xFF,0xFF,0xFF,0xF3,0xEF,0xBB,0xFB,
+0xBF,0xEF,0xBB,0xFF,0xD7,0xBF,0xFF,0xFF, 0xFF,0x29,0xAF,0xF7,0xFF,0xFF,0xFB,0xFF,
+0xFB,0xE6,0xFF,0x0F,0xFB,0x3F,0xDF,0x0F, 0xFF,0xAF,0xFF,0xFF,0xFF,0xF5,0xC3,0xDF,
+0x5F,0xFF,0xFF,0xFF,0xFE,0x6B,0xCA,0xBE, 0xBC,0xFF,0x9F,0xF2,0xBF,0xFF,0xFE,0xFA,
+0xFF,0xFF,0xEF,0x16,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFC,0xDF,0x97,0xFD,0x79,0xFF,0x37,
+0xE7,0x7F,0xFF,0xFF,0xB5,0xFF,0xFF,0xF6, 0x2F,0xFF,0xFD,0xFB,0xFE,0xFF,0xFF,0xFD,
+0x5F,0x57,0x5F,0xFF,0xDB,0x52,0xDF,0xFF, 0xFD,0xBF,0xFF,0xFF,0xFC,0xDB,0xFF,0x7B,
+0xB5,0xFD,0x7F,0xFF,0x71,0x9C,0x6E,0xFF, 0xF6,0x35,0xA5,0x9B,0xFF,0xFF,0xFD,0xFF,
+0xFF,0xDB,0x9E,0x7F,0xFE,0xEF,0xFB,0xFF, 0xFF,0xBD,0xEF,0xFF,0xDE,0xB7,0xF9,0x4B,
+0xFF,0xF5,0xEF,0xFF,0xFF,0xFF,0xE8,0x7E, 0xFF,0xEA,0xDF,0xF7,0xFF,0xFD,0x69,0x5B,
+0xFC,0x9F,0xEF,0x78,0xD6,0xFF,0xEB,0xEF, 0xFF,0xFF,0xFF,0xE8,0xFF,0xFF,0xED,0xFF,
+0xFF,0xFF,0xFF,0xE3,0xF9,0xF6,0xBF,0xFF, 0xFF,0xFE,0xDF,0xFF,0x7F,0xFF,0xFF,0xFF,
+0xD1,0xFF,0xFF,0xE7,0xFF,0xFF,0xFF,0xFF, 0xE7,0xF9,0xFF,0xBF,0x7F,0xD9,0xFF,0xFD,
+0xFE,0x7F,0xFF,0xFE,0xFF,0xF9,0xFF,0xFB, 0xD6,0xDF,0xBF,0xEF,0x5B,0xD6,0xFF,0xBF,
+0xFB,0xF6,0xFF,0xBF,0xEF,0xF8,0xF6,0xDD, 0xBE,0xFE,0x16,0xFF,0xBF,0xEF,0xFF,0xFE,
+0xFF,0xBF,0xEF,0xFF,0xFF,0xFF,0x6F,0xFB, 0xFF,0xFF,0xFF,0x6F,0xF3,0xFF,0xF7,0xEF,
+0xFB,0xFF,0xBF,0xFF,0xEF,0xFE,0xFF,0xBF, 0xFF,0xFF,0xFF,0xBE,0xBF,0xFF,0xEF,0xFF,
+0x7F,0xEF,0xFF,0xFD,0x17,0xFB,0x7B,0xFF, 0xFF,0xFD,0x7F,0xDB,0xF6,0xF4,0x7F,0xFA,
+0xFE,0xF5,0xBF,0xEB,0xE3,0xF7,0xFF,0xFF, 0xE9,0xBF,0xFF,0xAF,0xF7,0xFD,0xF3,0x7E,
+0x8F,0xA3,0xEA,0xFF,0xCB,0xF3,0xEE,0xFF, 0xBF,0xEF,0xF7,0xF9,0xFF,0xFE,0x7F,0xFF,
+0xFF,0xFF,0xFF,0xF5,0xFB,0xF6,0xFF,0xF5, 0x2F,0xFE,0xFB,0xD7,0xBF,0xFF,0xBE,0xDF,
+0x9F,0xFF,0xF0,0xFF,0xFF,0xF9,0xFE,0x7F, 0x8F,0xA3,0xF8,0xFE,0x6F,0x9F,0xF9,0xF6,
+0x2F,0x9F,0xE7,0xF9,0xFE,0x2F,0x9F,0xE1, 0xFF,0xFF,0xFF,0x7F,0xDF,0xF7,0xF5,0xFD,
+0x7F,0x7F,0xF5,0xFF,0x9F,0x5F,0xFB,0xFE, 0xFF,0x7F,0xFF,0xFF,0xCB,0xFF,0xFF,0xFB,
+0xFE,0xFF,0xBF,0xAF,0xFB,0xFE,0xFF,0xDF, 0xFE,0xFE,0xBF,0xF7,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xC7,0xFF,0xFF,0xFD,0xFF,0x7F,0xDD, 0xF7,0xFD,0xFF,0xFF,0xD7,0xFF,0xFD,0x7F,
+0xFF,0xFB,0xFD,0xFF,0xFF,0xFE,0xEF,0x7F, 0xFD,0xEF,0xFB,0xFE,0xFB,0xFD,0xFF,0x7F,
+0xDF,0xFD,0xFF,0x7A,0xDF,0xF7,0xFD,0xFF, 0xFF,0xFF,0xFF,0x1F,0xFF,0xFF,0xD3,0xF7,
+0xFF,0xFF,0x6F,0xDB,0xFF,0xFF,0xEF,0xCB, 0xF4,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
+0x29,0xFF,0xE8,0xDA,0x76,0x9F,0xAF,0x6A, 0xDA,0xFE,0x35,0xEB,0xDA,0xD6,0xBF,0xAB,
+0xEB,0x7A,0xDE,0xBF,0xD7,0x7F,0xFF,0xFE, 0xFF,0xBF,0xEF,0xFD,0xDF,0x77,0xBF,0xFD,
+0x37,0xEF,0xFF,0xEF,0xFF,0x3F,0xFF,0xFF, 0xFF,0xFE,0x7F,0xFF,0xFF,0xFF,0xF7,0x7E,
+0xDF,0xFF,0xFF,0xFF,0xFA,0xB7,0x7F,0xFF, 0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0x89,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0x9F,0xFB,0xFF,0xFF,0xFF,0xE7,0xFF,
+0xFF,0xFF,0xFF,0xAA,0xFF,0xAB,0xFB,0xFA, 0xEF,0xBF,0xFF,0xDF,0xFA,0x7B,0xB9,0xFE,
+0xFE,0xFF,0xFD,0xFF,0xF7,0xFE,0x3F,0xFF, 0xB7,0xFF,0xF7,0xEE,0xFF,0x7F,0xEF,0xFF,
+0xFF,0x7F,0xFF,0x1F,0xFB,0xFF,0xBF,0xFB, 0xFE,0xFF,0xBD,0xFF,0xFF,0x2F,0xFF,0xBF,
+0xFF,0x7F,0xDF,0xFA,0xFF,0xFF,0xFC,0xEE, 0xF5,0xF3,0xBE,0xFB,0x0F,0xEF,0xF3,0xBE,
+0xEF,0xFC,0x5F,0xFF,0x5A,0xFF,0xF7,0xDF, 0xFF,0xFF,0xFE,0xD5,0xFC,0x5F,0xFB,0xF2,
+0xFF,0xFF,0x2F,0xBB,0xF3,0xFF,0xFF,0xBF, 0xFF,0xEF,0xFF,0xEF,0xFF,0xFF,0xFF,0xFF,
+0xBF,0xFF,0xFF,0xFD,0x7B,0xFF,0xDF,0xB9, 0xFF,0xFB,0xFF,0xD8,0x7F,0xFF,0xFF,0xFF,
+0xFB,0xFF,0xFC,0x7F,0x1F,0xBF,0xE0,0xDF, 0xF7,0xEF,0xFF,0xFD,0x7F,0xFE,0xDF,0xFF,
+0xE0,0xFF,0xFF,0xFD,0xEF,0xFB,0xFF,0xFE, 0xF7,0xDF,0xFF,0xEB,0x5F,0xFF,0xF7,0xFF,
+0xFF,0xFF,0xFF,0xBF,0xFF,0xFD,0xFF,0xFD, 0xFF,0xFF,0xFF,0xF7,0xFD,0xFF,0x3B,0xDC,
+0xFD,0x6D,0x7B,0x5F,0x57,0xF5,0xFD,0x7F, 0x5F,0xFF,0xB1,0xFF,0xEB,0xFF,0xFF,0xFF,
+0xFB,0xFB,0xFE,0xFF,0xBF,0xFB,0xBE,0xFF, 0xBF,0xEF,0xFB,0xFE,0xFF,0xAF,0xFE,0xF7,
+0xDF,0xDF,0xFF,0xFF,0xFF,0x7F,0xCF,0xF3, 0xF8,0xFF,0xD7,0xFB,0xFF,0x5F,0xBF,0xF7,
+0xFB,0xFF,0x7F,0xFE,0x23,0xFF,0xFF,0xFE, 0x7F,0xF3,0xFF,0xFB,0xFE,0xFF,0xFF,0xF3,
+0xFF,0xFF,0xF5,0xF9,0xFF,0x3F,0xFF,0xFF, 0xF0,0x9A,0xFF,0xBE,0x7F,0xFF,0xFC,0xF9,
+0xFF,0xFD,0xAF,0xEB,0xFE,0xBF,0xFF,0xCF, 0xF3,0xFE,0x7F,0xFF,0xFF,0x5B,0xBD,0xFF,
+0xBC,0xEB,0xFF,0xD7,0xD4,0xAF,0xAF,0xFD, 0xFF,0xCF,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7,
+0xFD,0xFE,0xFF,0x6F,0xFF,0xFB,0xFF,0xFF, 0xFF,0xFD,0x7F,0x5E,0xFD,0xBF,0xDB,0xF6,
+0xFD,0xBF,0x6F,0xFB,0xEE,0xFD,0xFF,0x7A, 0xFF,0xFA,0xFB,0xFF,0x3F,0xFB,0xB7,0x5F,
+0xD6,0xF7,0x1F,0x71,0xDC,0x77,0x1D,0xC7, 0x31,0xDC,0x77,0xDF,0xF9,0xBF,0xF5,0x5B,
+0xF4,0xD7,0x9D,0xAE,0xFF,0xBF,0xFD,0xBF, 0xDB,0xF6,0xFD,0xBF,0x6F,0xDB,0xF6,0xFE,
+0x3D,0x81,0xFF,0xEB,0xFE,0xFE,0xFE,0xFF, 0xEB,0x7A,0xDF,0x7D,0x77,0x7D,0xF5,0x79,
+0xDF,0x57,0xDD,0xF5,0x7D,0x7E,0xE6,0xFF, 0xD6,0x3F,0xBF,0x7F,0xFF,0xD4,0xF5,0x3F,
+0xBF,0xFB,0xBE,0xEF,0xB3,0xEE,0xFB,0x9E, 0xEF,0xBB,0xFE,0x8B,0xFF,0xFE,0xDF,0xB7,
+0xED,0xFF,0xF7,0xFD,0xFE,0xFF,0xEF,0xBB, 0xEE,0xFF,0xBE,0xEF,0xBB,0xEE,0xEB,0xFC,
+0x1F,0xFF,0xFF,0xFD,0xFF,0xE7,0xFF,0xF7, 0xFD,0xFF,0xEF,0xFE,0xFF,0xBF,0xEF,0xFB,
+0xFE,0xFF,0xBF,0xEB,0xFA,0x1F,0xFF,0xB7, 0xEF,0x5B,0xFE,0xFF,0xAF,0xEB,0xDD,0xE7,
+0xDE,0x77,0x9D,0xE7,0x79,0xDE,0x77,0x9D, 0xBF,0xE6,0x6F,0xFF,0xFE,0xFF,0xBF,0xEF,
+0xFB,0xFE,0xFD,0xBF,0x6F,0xF6,0xFD,0xBF, 0x6F,0xDB,0xF6,0xFD,0xBF,0xFF,0x7E,0xFF,
+0xFF,0xFB,0xFE,0xFE,0xFF,0xEF,0xFB,0xFD, 0xEF,0x7E,0xF7,0xBD,0xEF,0x7B,0xDE,0xF7,
+0xBD,0xEF,0xFF,0xD5,0xFF,0xBF,0xFF,0xEF, 0xFE,0xFF,0xFC,0x3F,0x0F,0xE7,0xFE,0x7F,
+0x9F,0xE7,0xF9,0xFE,0x7F,0x9F,0xE7,0xFE, 0xF3,0xFF,0xFE,0xDF,0xAD,0xDF,0x67,0xEE,
+0xFB,0xBF,0xEF,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFE,0xFF,0xBF,0xEF,0xFF,0x23,0xFF,0xFF,
+0xFF,0xFF,0x7F,0xFF,0xF3,0xBC,0xDB,0xFE, 0xFB,0xFF,0xFB,0xBE,0xF7,0xFB,0xFF,0x7F,
+0xDF,0xFF,0xCF,0xFB,0xFF,0x9F,0xE3,0xF9, 0xBE,0x3F,0x8F,0xE7,0x79,0xFF,0x9D,0xE7,
+0xF9,0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x5F, 0xFF,0xCF,0xF7,0xFF,0xFF,0xFF,0xDF,0xF7,
+0xFE,0x7F,0xE7,0xF9,0xFE,0x7F,0xFF,0xFF, 0xFB,0xFE,0xFF,0xFF,0xBF,0xFF,0xBF,0xBF,
+0xFF,0xFE,0xFF,0xBF,0xEF,0xFF,0xFD,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFD,0xFF,
+0xFF,0x3F,0xFF,0xBF,0xFF,0xF7,0xFF,0xFF, 0x7F,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xE8,0xEF,0xFF, 0x5F,0xF7,0xBF,0xF9,0xFE,0xDF,0xB7,0xFD,
+0xFF,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7, 0xFD,0xFF,0xDD,0xFF,0xF2,0xFF,0xBF,0xFF,
+0xFF,0xBF,0xFF,0xFF,0x2F,0xF2,0xFF,0xBF, 0x2F,0x7B,0xD2,0xF7,0xBF,0x2F,0xFF,0xBB,
+0xFF,0xEE,0x8F,0xAF,0xEB,0xFA,0xFE,0x3F, 0xA7,0x69,0xCE,0x8F,0xA4,0xEA,0xFA,0xEE,
+0xB7,0xAE,0xEB,0xFD,0xC7,0xFF,0xF7,0xF7, 0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x3E,0xF3,
+0x74,0xFF,0x3F,0x4F,0xFF,0xE7,0xFF,0x3F, 0xFE,0xA7,0xFF,0xFF,0xDF,0xF7,0xB7,0xFF,
+0xF7,0xFF,0xBA,0xEF,0x37,0xEB,0xFB,0xFE, 0xBF,0xFB,0xFE,0xF3,0xFF,0xF9,0xDF,0xFF,
+0xBF,0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF, 0xFD,0xDF,0xFF,0xFD,0xFF,0xFF,0xFB,0xFE,
+0xFD,0xFF,0xFB,0xBF,0xFE,0x3F,0xED,0xFF, 0xDF,0xBE,0x3D,0xA7,0xFB,0xFA,0x3F,0xE6,
+0xE1,0xFE,0xFE,0x3F,0xEF,0xE3,0xDF,0xF5, 0x7F,0xFE,0xFF,0x7E,0xFF,0xFF,0xFF,0xFF,
+0xEF,0x6F,0xF6,0xFF,0x7D,0xEF,0xD7,0xDE, 0xFF,0x7D,0xEF,0xFF,0xF2,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0x7B,0xDE,0xFB,0xE6,0xEE, 0xEF,0x37,0x6E,0xF3,0x7E,0xEB,0x37,0xEF,
+0xFF,0xC1,0xFF,0xFE,0xFF,0xF7,0xEF,0xFF, 0xFF,0xFF,0xBF,0x3F,0xD2,0xDF,0xBF,0x2F,
+0x7B,0xE2,0xFF,0xFE,0x3B,0xBD,0xDB,0xFF, 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,0xFE,
+0xFF,0xFB,0xFF,0xFF,0xBF,0xFF,0xFB,0xDF, 0xFF,0xBF,0xFF,0xB7,0xFF,0xFF,0xBF,0xEF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xFF, 0x7F,0xFF,0x1F,0xEF,0xF1,0xFD,0xFF,0xF6,
+0xAF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,0xFF, 0xFF,0xFF,0xFE,0x9F,0xFF,0xFF,0xFF,0x77,
+0xEF,0xF7,0xFB,0xFF,0xFE,0x5F,0xFF,0xFF, 0xBF,0xCF,0xFB,0xF7,0xDD,0xF7,0xF5,0xFF,
+0x5F,0xD5,0xF5,0xFD,0x7F,0x5F,0xD7,0xF5, 0xFF,0xFB,0x0F,0xFF,0xFF,0xA9,0xEA,0x7A,
+0xFF,0xAF,0x8F,0xFE,0xDF,0xAF,0xEF,0xFB, 0xFE,0xFF,0xBF,0xEF,0xFB,0xDF,0xE5,0x5F,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xBD,0x57,0xFF, 0xFF,0x6F,0x77,0xBF,0xF7,0xFB,0xFF,0x7F,
+0xBF,0xF7,0xFF,0xFC,0xBF,0xFF,0x9F,0xFF, 0xFF,0xEF,0xFF,0xFE,0xFF,0xFF,0xFF,0x1F,
+0xCF,0xFF,0xFC,0xFF,0xFF,0xFF,0xFF,0xFB, 0x65,0xAF,0xF3,0x7C,0xFF,0x3F,0xDF,0xFF,
+0xFD,0xE9,0xFE,0x7F,0xE7,0xFF,0xFE,0x7F, 0xFF,0xFF,0xFF,0xFF,0xFD,0xE3,0xDF,0xFB,
+0xDB,0xF6,0xFD,0xEF,0x5B,0xFB,0xFF,0xDF, 0xFC,0xFF,0x3F,0xDF,0xF3,0xFD,0xFF,0x7F,
+0xDF,0xEF,0x66,0xFF,0xDF,0xAD,0xEB,0x7A, 0xDE,0xF7,0xF7,0xE7,0xD9,0xFD,0x9F,0x67,
+0xD9,0xF6,0x7D,0x9F,0xE7,0xDF,0xF5,0x47, 0xFD,0x65,0x5B,0xD6,0xF4,0xFE,0xFF,0xEF,
+0xFF,0x6D,0xF6,0xDD,0xB7,0x6D,0xDB,0x76, 0xDC,0xB7,0x7D,0xFA,0x9B,0xF6,0x6D,0x9D,
+0x67,0x59,0xDF,0xF7,0xDD,0xFF,0xEB,0xFE, 0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF,0xE3,
+0xD1,0x9F,0xFF,0xBD,0xBF,0xEF,0xFE,0xF7, 0xBF,0xBF,0xF7,0xD7,0x7F,0xDD,0xF7,0x9D,
+0xDF,0x7F,0xDF,0xF7,0xFF,0xE0,0x7F,0xFD, 0xC1,0xDF,0xF7,0xFD,0xC7,0x7F,0x7F,0xFB,
+0xFF,0xBB,0xEC,0xFB,0x3E,0xFF,0xBF,0xEC, 0xFB,0xFF,0xD8,0x7F,0xBF,0x6C,0xFF,0xBE,
+0xFF,0xBF,0xED,0xFF,0xEF,0xFE,0xFB,0xBF, 0xEF,0xFB,0xFE,0xFF,0xBF,0xEE,0xFF,0xC5,
+0xFF,0xAF,0x6F,0xFF,0xFC,0xFD,0x3F,0xE7, 0xFF,0xFE,0xFF,0xEF,0xFB,0xFE,0xFF,0xBF,
+0xEF,0xFB,0xFE,0xBF,0x89,0xFE,0xFA,0xBA, 0xFE,0xBF,0xAF,0xFB,0xF6,0xF5,0xD9,0x7D,
+0x97,0x65,0xD9,0x74,0x5D,0x97,0x65,0xD3, 0xFE,0xD6,0xFF,0xBF,0xF7,0xFD,0xFF,0x7F,
+0xBF,0xCF,0xFB,0xFE,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xEF,0xFB,0xFF,0xF6,0x8F,0xFB,
+0xFF,0xEF,0xFB,0x7E,0xDB,0xFE,0xFF,0xBE, 0xEF,0xEE,0xFB,0xBE,0xEF,0xBB,0xEE,0xFB,
+0xBE,0xFF,0xFF,0xDF,0xFF,0x43,0xFF,0xFF, 0xFB,0xEF,0x5F,0xB7,0xFE,0x7F,0xE7,0xF9,
+0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0xF9, 0xBF,0xFE,0xAF,0x77,0xFD,0xFF,0x2F,0xAF,
+0xA7,0xFE,0xFF,0xEF,0xFB,0xFE,0xFF,0xBF, 0xEF,0xFB,0xFE,0xFF,0xF1,0x7F,0xEF,0xDF,
+0xFF,0x97,0xF5,0xEF,0xFF,0xDF,0xFF,0xFF, 0xBF,0xFF,0xBF,0xFF,0xFF,0xFE,0xFF,0xFF,
+0xFF,0xE0,0xFF,0xFF,0xF9,0xFE,0x2F,0x8B, 0xE3,0xF8,0xBE,0x77,0x9F,0xF9,0xDA,0x77,
+0x9D,0xE7,0x79,0xDE,0x77,0x9F,0xDD,0xFF, 0xFD,0xFD,0x7F,0x5F,0xD7,0xFD,0xFF,0x7F,
+0xE7,0xFE,0x7F,0x97,0xE7,0xFB,0xFE,0xFF, 0xBF,0xEF,0xFF,0xAB,0xFF,0xEF,0xFA,0xFE,
+0xBF,0xAF,0xFF,0xFA,0xFF,0xFF,0xDF,0xFF, 0xFB,0xFF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFF,
+0x67,0xFF,0xF7,0xF5,0xFF,0xFF,0xFF,0xDF, 0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,0xFF,0xBD, 0xEB,0xFF,0xFF,0xF7,0xAD,0xEB,0xFF,0xDF,
+0xFD,0xFF,0x3F,0xDF,0xF7,0xFD,0xFF,0x7F, 0xDF,0xFF,0x5F,0xFF,0xF7,0xFF,0xFF,0xFD,
+0xBF,0xFF,0xCB,0xF4,0xFF,0x7F,0xD3,0xF7, 0xFD,0x3F,0x7F,0xD3,0xF7,0xFF,0xFC,0x3F,
+0xFF,0xEA,0xFA,0xBE,0xAF,0xAB,0xEB,0xBA, 0xF4,0x95,0x6B,0x52,0xD4,0xAD,0x2F,0x4A,
+0xD2,0xF6,0xBF,0xD2,0x7F,0xF7,0x3F,0xFF, 0xFF,0xF3,0x7F,0xFF,0xFF,0xF7,0xFF,0xBA,
+0xDF,0xFB,0xFD,0xFF,0xBF,0xFF,0xFB,0xFF, 0xF8,0x7F,0xEA,0xFF,0xFE,0xFE,0xDF,0xFF,
+0xF7,0xFF,0x7F,0xBB,0xFF,0xFF,0xBF,0xDF, 0xFB,0xFF,0xFF,0xBF,0xFF,0xB1,0x7F,0xFF,
+0xFB,0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBF, 0xCF,0xFE,0xFF,0xFF,0xEF,0xFF,0xF7,0xFF,
+0xFF,0xFF,0xF1,0xFF,0x69,0xBE,0xFA,0xBF, 0xAF,0xE2,0xFF,0xFE,0xFD,0xAF,0xF3,0xFE,
+0xFF,0xBF,0xEF,0xFB,0xFC,0xFF,0xFF,0x07, 0xFD,0x95,0xDB,0xDF,0x7F,0xDF,0xAF,0xFF,
+0xF7,0xAF,0x36,0xFE,0xBF,0x65,0xEB,0xF6, 0xFE,0x9F,0x6F,0xFE,0x07,0xFF,0xCF,0xFF,
+0xF8,0xFE,0xFF,0xCF,0xFF,0xF6,0xFA,0xE7, 0xFB,0xFE,0xFF,0xBB,0xED,0xF9,0xFF,0xFF,
+0xFF,0x5F,0xFF,0xFF,0xFF,0x75,0xFF,0xEF, 0x7E,0xFD,0xE0,0xE8,0x5E,0xD3,0xE5,0xF9,
+0x3E,0x5F,0xD7,0xF7,0xFF,0xFA,0x2F,0xFB, 0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0x7F,
+0x7F,0xD7,0xF5,0x7D,0x5F,0x57,0xD5,0xF5, 0xEF,0xFF,0xF3,0x7F,0xFC,0x7F,0xFF,0xC7,
+0xF1,0xFF,0xFF,0x1F,0xCF,0xB0,0xFF,0x3F, 0xCF,0xF3,0xFC,0xFF,0x3F,0xCE,0xFF,0xE4,
+0xFF,0xDF,0x7F,0xFE,0xF7,0xBB,0xFF,0xFF, 0xDF,0xEF,0xEE,0xFF,0xBF,0xEF,0xFB,0xFE,
+0xBF,0xBF,0xEF,0xFF,0xD1,0xFF,0xFF,0xFF, 0xFD,0xFB,0xFF,0xFD,0xFF,0xFB,0x9F,0xE9,
+0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0xBF, 0xFF,0xB3,0xFF,0xFF,0xF7,0xFF,0xFF,0xAF,
+0xF7,0xFF,0xB6,0x3F,0xEB,0xFA,0xFE,0xBF, 0xAF,0xEB,0xFA,0xFE,0xBF,0xFE,0xA7,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xF7,0xFF,0xFF,0xFF, 0xFE,0x9F,0xF7,0xF9,0xFF,0x7F,0x9F,0xE7,
+0xFF,0xFF,0xFE,0xAF,0x6F,0xFF,0xFF,0xFF, 0x9F,0xFF,0xDF,0xFF,0x7D,0x5F,0xDD,0xFF,
+0xFB,0xBF,0xE7,0xBB,0xFF,0xFB,0xDF,0x6D, 0x5F,0x7E,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xEB,0xF7,0xFF,0xE7,0xEF,0xF7,0xFF,0xFF, 0x7F,0xFF,0xF7,0xFF,0xFC,0x8F,0xFF,0xEF,
+0xFD,0xFE,0xFF,0xBE,0xF4,0xF2,0x7D,0xD7, 0xCF,0xFF,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xCF,0x6B,0xFF,0xBF,0x3F,0xFB,0xF2, 0xFC,0x7F,0xEB,0xFF,0x9F,0xFA,0xFF,0xFF,
+0x3F,0xFF,0xF3,0xFF,0xFF,0xFD,0x70,0xF7, 0xFF,0xFF,0xBF,0xFF,0xFB,0xD7,0xFE,0xF5,
+0x77,0xFF,0x15,0xDD,0x77,0xFD,0xFF,0x7F, 0xDF,0xF7,0xFB,0xCD,0xBF,0xFF,0xFD,0xFF,
+0xFF,0xDF,0x37,0xCD,0xF9,0xEC,0xFE,0xEF, 0xBB,0xF4,0xFB,0x3F,0x4F,0xB3,0xFF,0xFD,
+0xCB,0xFF,0xE9,0x7E,0x54,0x9F,0xE5,0x4B, 0xB7,0xFF,0xDD,0x7D,0xC7,0x71,0xDD,0x77,
+0x5D,0xD7,0x75,0xCD,0x7F,0xD6,0xFF,0xD3, 0xF6,0xF9,0x3F,0x6D,0x95,0xAF,0x7F,0xFE,
+0xFF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFE,0xF6,0xC7,0xFF,0xAD,0x7B,0xCA,0xFF,
+0xBF,0xBF,0xEF,0xFD,0xE3,0xDF,0xB7,0xED, 0xFB,0x7E,0xDF,0x37,0xED,0xE3,0xFB,0xDF,
+0xFF,0x52,0x5C,0x15,0xFD,0xCF,0x7F,0xDF, 0xFE,0xEF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEC,
+0x7B,0xFE,0xFF,0xFE,0x3E,0x7F,0xDA,0xF7, 0xFD,0xFF,0x7F,0xFF,0xFF,0xFB,0xEF,0xBB,
+0x6F,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0xFF, 0xF7,0x7D,0xFF,0xD8,0xFF,0xFD,0xBF,0x7F,
+0xFB,0xFF,0xFF,0x9F,0xFB,0xFE,0x7F,0x9F, 0xE7,0xF9,0xFE,0x7F,0x9F,0xEA,0x7F,0xF6,
+0xBF,0xBD,0x6A,0x5A,0xF6,0xE5,0xBF,0x77, 0x5F,0x6D,0xDD,0x77,0x5D,0xD7,0x75,0xDD,
+0x77,0xFF,0xA5,0xBF,0xCF,0xFB,0xFF,0xFF, 0xBF,0xCF,0xFB,0xFD,0xFF,0xBF,0xF3,0xFE,
+0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFD,0xAB, 0xFF,0xBF,0xBF,0xFF,0xFB,0xFF,0x7F,0xEF,
+0xFF,0xBE,0xFB,0xEE,0xFB,0xBE,0xEF,0xBB, 0xEE,0xFB,0xBF,0xFF,0xB5,0xFF,0xD0,0xBC,
+0xFD,0x2F,0x4B,0xF7,0xFF,0xFF,0x9F,0xF9, 0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0x9F,
+0xFA,0x8F,0xFD,0xAB,0xFA,0xDA,0xBF,0xAF, 0xB3,0xFD,0xFF,0xBF,0xFB,0xFE,0xFF,0xBF,
+0xEF,0xFB,0xFE,0xF7,0xBF,0xFF,0x9F,0xFF, 0x77,0xF7,0xBD,0xFD,0x77,0xDF,0xFF,0x7E,
+0xDF,0xED,0xBB,0xFE,0xFF,0xBE,0xEF,0xFB, 0xFE,0xFF,0xFA,0x3F,0xFF,0xBE,0x6F,0x8F,
+0xE6,0xF9,0xFE,0x7F,0x9F,0xC7,0xFE,0x7F, 0x9F,0xE7,0xF9,0xFE,0x7F,0x9F,0xE7,0xFB,
+0x7F,0xFF,0x7F,0xCF,0xFF,0xFD,0xFF,0xFF, 0xDF,0xFB,0xAF,0xBF,0xEF,0xFF,0xFE,0xFF,
+0x9F,0xEF,0xFB,0xFF,0xFC,0xFF,0xFB,0xFE, 0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xF7,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xF5,0xFF,0xFF,0xFF,0x3F,0xDF,0xF7,
+0xFF,0xFF,0x7F,0xEF,0xFE,0xFF,0xBF,0xFF, 0xFB,0xFF,0xFF,0xBF,0xEF,0xFF,0xB3,0x7F,
+0xFF,0x7B,0x5E,0xF7,0xFD,0xFF,0x7B,0x7F, 0xF7,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0x7F,
+0xDF,0xF7,0xFF,0x17,0xFF,0xFF,0xFF,0x7F, 0xFF,0xFF,0xDD,0xF6,0xFC,0xBF,0xCB,0xF2,
+0xBC,0xBF,0x2F,0xCB,0xF2,0xFC,0xBF,0xFE, 0x8F,0xFF,0xFA,0x7E,0xBF,0xA7,0xEB,0xDA,
+0xFC,0xBF,0xAF,0x7A,0xFE,0xBF,0xAF,0xEA, 0xFA,0xFE,0xBF,0xAF,0xF4,0xDF,0xFE,0xFF,
+0xF3,0x3C,0x7F,0x3E,0xFF,0xCF,0xF8,0xBF, 0x8F,0xE3,0xF8,0xFE,0x3F,0x8F,0xE7,0xE8,
+0xFF,0xFC,0x9F,0xFF,0xFF,0xCF,0xEB,0xB3, 0xE7,0xFB,0x7B,0xF3,0xFE,0xFF,0xCF,0xDB,
+0xFB,0xFB,0xBF,0x6F,0x6F,0xDF,0xEC,0x7F, 0xFF,0xFF,0xF7,0xFD,0xFD,0xFF,0xFF,0xFF,
+0xFF,0xB2,0xBF,0xFF,0xDE,0xFD,0xBD,0xEF, 0xFB,0xF6,0xDF,0xEA,0xE7,0xDB,0xFE,0xBB,
+0xFF,0xEB,0xFB,0xBF,0x9F,0x8F,0xE8,0xFE, 0x3F,0x8F,0xA3,0xF8,0xFE,0x3F,0x8F,0xFF,
+0xF8,0x7E,0xFD,0xFD,0x7F,0xFF,0xFB,0xCD, 0xFF,0xFD,0xFF,0x5F,0xEF,0xFD,0xFF,0xFF,
+0xDF,0xF7,0xFD,0xFF,0xBE,0x90,0xFF,0xFF, 0xEE,0xFF,0x3F,0xBF,0xF3,0xBB,0xFE,0xB7,
+0xAB,0xFA,0xFE,0xAF,0xAD,0xEA,0xFA,0xDE, 0xAB,0xFF,0x63,0xFF,0xFE,0xF2,0xFF,0xB3,
+0xFF,0xDF,0xEE,0x7D,0xFF,0x03,0xF1,0xF4, 0x3F,0x1F,0xC3,0xF1,0xEC,0x7F,0xFE,0x6F,
+0xFF,0xFB,0xFB,0xFF,0x9F,0xFF,0xBF,0xFF, 0x7B,0x5F,0xFD,0xFF,0xDF,0xF7,0xFD,0xFD,
+0x7F,0x7F,0xDF,0xFE,0xCF,0xFB,0xFF,0xFF, 0xAF,0xFB,0xFF,0x1F,0xEF,0xA5,0xFD,0xBF,
+0xDF,0xFB,0x7D,0xFF,0xBF,0xDF,0xFB,0xFF, 0xFD,0x3B,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,
+0xAF,0xF3,0xFF,0xFB,0x7F,0xBF,0xD7,0xFB, 0xBF,0x7F,0xBB,0xF7,0xFF,0xF8,0x7F,0xFF,
+0xFA,0x5F,0xD7,0xFF,0xDF,0x7F,0xEF,0xFF, 0xFF,0x7F,0xDB,0xF7,0xFD,0xFF,0x7F,0xDF,
+0xB7,0xFB,0xEC,0xFF,0xFF,0xF7,0xBF,0xEF, 0xFD,0xFC,0xFB,0xFF,0xEF,0xF0,0xFE,0x3F,
+0x8F,0xE3,0xF8,0xFE,0x3F,0x8F,0xEF,0x8D, 0xFF,0xFF,0xEF,0x7F,0xBF,0xFF,0xFB,0xFF,
+0xDB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xEF,0xD8,0xFF,0x2E,0x7F,
+0xBE,0xEF,0xFE,0x6E,0xFF,0xBF,0xF9,0xFF, 0xFF,0xF3,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFC,0x66,0xBE,0x47,0xF3,0x7F,0xDF,0xFE, 0x87,0x9F,0xFF,0xFF,0xFF,0xFF,0xE7,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xD6,0x6F,0x7C, 0xFB,0x4F,0xD2,0xFF,0xFD,0x2B,0xFE,0xFF,
+0xFF,0xFD,0x5F,0xD7,0xD5,0xF5,0x7D,0xFF, 0xFF,0xFF,0xBF,0x9B,0xFF,0xFF,0xDF,0xB7,
+0xFF,0xFF,0xDF,0xFF,0x3F,0xCF,0xFE,0x7F, 0xBF,0xEF,0xFB,0xFC,0xFF,0x3F,0xFF,0xD9,
+0xBF,0xFE,0x97,0xEC,0x8F,0xB7,0xFE,0x9B, 0x7D,0xFD,0xB7,0xDD,0x77,0x1D,0xC7,0x71,
+0xDD,0x77,0x5D,0xD7,0xF3,0x6F,0xFD,0x3F, 0x73,0xDD,0xAF,0xFD,0x7A,0xFF,0xFF,0xAF,
+0xFE,0xFD,0xBF,0xEF,0xFB,0xFE,0xFF,0xBF, 0xEF,0x66,0x7F,0xFF,0xFF,0xBF,0xBF,0xFF,
+0xFB,0xFF,0xF7,0xDF,0xFD,0xFB,0x7D,0xDF, 0xB7,0xCD,0xF3,0x7C,0x5F,0x3F,0x91,0x3F,
+0xFF,0x3D,0xEF,0x7B,0xFF,0xFC,0xFF,0xCA, 0xEF,0xFE,0xFF,0xBD,0xEF,0xFB,0x1E,0xE7,
+0xBB,0xEC,0x7F,0xB3,0xFF,0xFD,0x9F,0xFF, 0xFF,0xFE,0xFF,0xFF,0x7F,0xBF,0xFB,0xFE,
+0xFF,0xBF,0xEF,0xFB,0xEE,0xFB,0xBF,0xDF, 0x67,0xFF,0xFF,0xBF,0xEF,0xDB,0xFF,0xBC,
+0xFE,0x7F,0xFB,0xFF,0x9F,0xEF,0xF9,0xFE, 0x7F,0x9F,0xE7,0xF9,0xFE,0x87,0xFF,0xEE,
+0xFB,0xBE,0xE5,0xBF,0xEF,0xF9,0xD7,0x65, 0xF7,0xDD,0xE7,0x7D,0xDF,0x77,0x5D,0xD7,
+0x7F,0xF8,0x9B,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFF,0xFF,0xBF,0xEF,0xFB,0xFF,0x7F,0xCF,
+0xF3,0xFC,0xFF,0xBF,0xEF,0xFF,0xDB,0x3F, 0xEF,0xFB,0xFE,0xFF,0xDF,0xFF,0xFE,0xFB,
+0xBB,0xEF,0xBF,0xEF,0xBB,0xEE,0xFB,0xBE, 0xEF,0xBB,0xFF,0xFC,0x7F,0xFD,0x3B,0x5B,
+0xD6,0xE5,0xFD,0x4F,0xC3,0xFB,0xFF,0xBF, 0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0xFF,
+0xB4,0xFF,0xFA,0xBC,0x8F,0xB2,0xE9,0xD2, 0x2E,0xCF,0xFB,0xFF,0xBF,0xEF,0xFB,0xFE,
+0xFF,0xBF,0xEF,0xFB,0xFF,0xEC,0xFF,0xFD, 0xFD,0x7F,0xDF,0xF7,0xE4,0xDF,0x5F,0xFF,
+0xFF,0xFB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xC3,0xFF,0xEF,0xE6,0xF8,0xFE,
+0x3F,0x8B,0x83,0xF9,0xFE,0x7F,0xE7,0xF9, 0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0x17,
+0xFD,0xFF,0xFF,0xFF,0x7F,0x5F,0xF7,0x2C, 0xFF,0xFF,0xFF,0xFE,0x7F,0xFF,0xE7,0xF9,
+0xFE,0x7F,0x9F,0xFE,0x2F,0xFF,0xFF,0xEF, 0xFF,0xFE,0xBF,0xEF,0xAD,0xFF,0xFF,0x7F,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFE,0xDF,0xFF,0xDF,0xFF,0xFD,0xFD,0x7F,
+0xDF,0xF7,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFA,0x3F,0xFE,
+0xF7,0xFD,0xEF,0x7A,0xFF,0xB1,0xBD,0xFF, 0x7F,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,
+0xFF,0x7F,0xF3,0x27,0xFF,0xDF,0xFF,0xDD, 0xFF,0xFC,0x9B,0xFF,0xCB,0xFC,0xBF,0x2F,
+0xCB,0xF2,0xFC,0xBF,0x2F,0xC9,0xFF,0xDE, 0xFF,0xDF,0xAF,0xEB,0xDA,0xFE,0xBB,0xAF,
+0xEB,0xF8,0xF7,0xAF,0xE8,0xFA,0xFE,0xBF, 0xAF,0xEB,0xF2,0xFF,0xFD,0xFF,0xFF,0xEF,
+0xBD,0xD7,0xBF,0xFF,0xFF,0xDE,0x8F,0xB8, 0xDE,0x37,0x8D,0xA3,0x78,0xDA,0x3F,0x8F,
+0xFF,0xA1,0xFF,0xFF,0xFB,0xFB,0xFF,0xFF, 0xFF,0xFF,0xA7,0xBD,0xFB,0x76,0xFD,0xBF,
+0xEF,0xDB,0xFE,0xBB,0xBF,0xFE,0x27,0x7F, 0xFF,0xFE,0xFE,0xFD,0xF5,0xFF,0xEF,0xF5,
+0xDF,0x1F,0xE7,0xFD,0xFF,0x7F,0xDF,0xF7, 0xFD,0xFF,0xFF,0xCD,0xFD,0xAE,0xFF,0xFA,
+0x3E,0x3F,0xAB,0xFD,0xF8,0x7E,0x8F,0xE3, 0xF8,0xFE,0x3E,0x8F,0xE3,0xF8,0xFF,0xFE,
+0x1F,0xEF,0xDF,0xBF,0xFE,0xDE,0xDF,0xD9, 0xFF,0xDF,0xBC,0xFF,0xFF,0x7F,0xFF,0xEF,
+0xFD,0x7F,0xDF,0xF7,0xF9,0x3F,0xFE,0xFF, 0xFF,0x6F,0xFE,0xDE,0xBF,0xF7,0xED,0xEA,
+0xFD,0x8F,0x83,0xF8,0xEA,0x3F,0x8F,0xEF, 0xFF,0xF4,0x7F,0xFF,0xEF,0xEF,0x7B,0xF3,
+0xF1,0x5F,0xFF,0xFF,0xF1,0x3B,0x7F,0xDF, 0xF7,0xFD,0xFF,0xFF,0xFF,0xFF,0xE0,0xFF,
+0xFF,0xFF,0xF7,0xFF,0x6F,0xFF,0x7F,0xFF, 0xFF,0xF7,0xDE,0xF7,0xBF,0xEF,0xFB,0xF7,
+0xFD,0xFF,0xFF,0xF5,0xFA,0xFF,0xFF,0xFB, 0xE7,0xFF,0xF3,0xF8,0x7F,0xF3,0xDF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F,0xEF, 0xBB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,
+0xFF,0x7F,0xFF,0x9F,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xCF,0xFF,0x37,0xFF,0xFF,
+0x7F,0xDF,0x77,0x5D,0xE7,0xFC,0xFF,0xBF, 0xF7,0xF5,0xFB,0xFF,0xFF,0xD7,0xF5,0xFB,
+0xFF,0xFF,0x45,0xFD,0x7F,0xEA,0xFD,0xBE, 0xBF,0xDF,0xF7,0xFF,0xFF,0xDB,0xFB,0xFE,
+0xFF,0xBF,0xEF,0xFF,0xFF,0xFF,0xFB,0x5F, 0x7F,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFE,0xFF,0xEF,0xFD,0xFF,0x7F,0xDF, 0xFF,0xEF,0xFB,0xF8,0x0F,0xF3,0xFF,0xF9,
+0x2E,0xFB,0xFE,0xFC,0xF3,0xEF,0xFF,0xFF, 0xBF,0xFF,0xFB,0xE7,0xFF,0xFE,0x7E,0xFF,
+0xC0,0x6B,0xCF,0xFF,0x34,0xDF,0xF1,0xFD, 0xFF,0xEF,0xFF,0xFF,0xFF,0xDF,0xF7,0xFD,
+0xCF,0x7F,0x9C,0xFD,0xFD,0x6C,0xF7,0xFF, 0xF6,0xFD,0xEB,0x2B,0x9F,0xFF,0xFC,0xFE,
+0x7E,0xFF,0xFF,0xFF,0xFF,0xD7,0xF3,0xF7, 0xFF,0xFB,0xE1,0xBF,0xFF,0xEB,0x7A,0xDE,
+0xD7,0xFB,0xFF,0xF9,0xFE,0xFF,0xFF,0xF3, 0xDE,0x7F,0xFD,0xE7,0x7F,0xFF,0xFD,0xBB,
+0xFF,0xFF,0x7E,0xCC,0xF6,0xAF,0x5F,0x7F, 0xFE,0xF4,0x7D,0xF7,0xFD,0xBB,0x6E,0xDB,
+0xB7,0xFF,0xF7,0xDF,0x66,0xFF,0xFF,0xF7, 0x3D,0xCF,0xDE,0xBD,0xFF,0xFF,0xDE,0xDB,
+0x8D,0xF7,0x7E,0xDF,0xB7,0xEF,0x7F,0xFF, 0xF6,0x87,0xFF,0xFF,0xEF,0xFE,0xDE,0xBF,
+0xFF,0xFF,0xFF,0xBB,0xEF,0xFD,0xFF,0x7B, 0xDE,0xF7,0x3F,0xFF,0xBF,0xFB,0xDB,0xFF,
+0xF2,0xB6,0xFD,0xBD,0x7F,0xE7,0xFF,0xFF, 0xFF,0x6F,0xF7,0xFF,0xFF,0xFF,0xFE,0x77,
+0xFF,0xBF,0xF8,0xAF,0xFF,0xDF,0xBF,0xFF, 0xBF,0x7F,0xFB,0xFF,0xFF,0xFF,0xDB,0xFE,
+0xFF,0xBF,0xFF,0xFA,0xFF,0xFD,0xFF,0xF6, 0x7F,0xFF,0x9F,0xFF,0xFF,0x3F,0xEF,0xF8,
+0xEE,0x7E,0x9F,0xBA,0xFE,0xBF,0x8F,0xEF, 0xFE,0xFE,0xF9,0xFF,0xFA,0x7F,0xFE,0x7E,
+0xBF,0xAF,0xFB,0x96,0xFD,0x9F,0xEF,0x5E, 0x65,0xBE,0xEF,0x5B,0xB6,0xFF,0xBE,0xE3,
+0xFF,0xB5,0xBF,0xFF,0xFD,0xFF,0x7F,0xFF, 0xEF,0xDF,0xFE,0xFF,0xBF,0xFB,0xFE,0xFF,
+0xBF,0xCF,0xFF,0xFF,0xFF,0xFD,0x9B,0xFF, 0xFE,0xFB,0xFE,0xDF,0xFF,0x7F,0xFF,0xF7,
+0xFE,0xFF,0xDF,0xFB,0xFB,0xFE,0xFF,0xFF, 0xFF,0xFF,0xFF,0xB7,0xFE,0xFA,0xFF,0xAB,
+0xEF,0xFF,0xFD,0xB5,0x7B,0x7F,0xFB,0xF7, 0xFD,0xFF,0xFF,0xDD,0xFF,0xEF,0x8F,0xFF,
+0x2F,0xFF,0xFB,0x7C,0xFF,0x3F,0xDF,0x73, 0xEB,0xFE,0x3F,0xFF,0xEF,0xFB,0xFE,0xFF,
+0xEF,0xFD,0xFF,0xBF,0xFD,0x0F,0xFF,0xFF, 0xFF,0xF5,0xF9,0xFF,0x7F,0xD7,0xFD,0xFF,
+0xDF,0xFF,0xF7,0xFB,0xFF,0x7F,0xBF,0xFF, 0xFF,0xF0,0x9F,0xFF,0xFE,0x7F,0x8B,0xE3,
+0xF9,0xDE,0x27,0x9B,0xE6,0xBE,0x7F,0x9B, 0xC3,0xF8,0xDE,0x7F,0x9D,0xE7,0xFE,0x7F,
+0xFF,0xFF,0x5F,0xD7,0xFF,0xFF,0xFF,0x4F, 0xFB,0xFF,0xFF,0x7F,0xFF,0xAF,0xFF,0x9F,
+0x7F,0xFB,0xFF,0xE8,0xFF,0xFF,0xFE,0xBF, 0xAF,0xFF,0xFF,0xFE,0xBF,0xEF,0xF7,0xFF,
+0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFF, 0xFC,0xFF,0xFF,0xFD,0x7F,0xFF,0xFF,0xFF,
+0xFD,0x3F,0xCF,0xFF,0xFF,0xFF,0xFF,0xF7, 0xFF,0xFD,0x7F,0xFF,0xFF,0x93,0xFF,0xFF,
+0x7A,0xDF,0xF7,0xFF,0xFF,0x7B,0x7F,0xB7, 0xEF,0xFF,0xFF,0xFD,0xBF,0xFD,0xFB,0xFF,
+0xF7,0xFF,0xD7,0xFF,0xFF,0xFF,0xFC,0x9F, 0x6F,0xCB,0xFF,0xF4,0xBB,0xDF,0xD6,0xFD,
+0xBF,0x2F,0xD3,0xF7,0xFF,0xDF,0xFF,0xCF, 0xFF,0xFA,0xBE,0xBD,0xAF,0x6A,0xDA,0xBE,
+0xBB,0xAB,0x3A,0xBE,0x2D,0xAE,0xEB,0xDA, 0xF6,0x3F,0xAD,0xF5,0xDD,0xFF,0xCF,0xF1,
+0xFF,0xF9,0x7F,0xFF,0x73,0xFE,0xFF,0xCF, 0xC3,0xF4,0xF7,0x2F,0xF3,0xFF,0xFC,0xFF,
+0x7C,0x1F,0xFF,0x3F,0x4F,0xFF,0x7E,0xFF, 0xEF,0xBD,0xF6,0xFE,0xFF,0x2B,0xEF,0xDC,
+0xFB,0xFD,0xFF,0xFB,0xFF,0xEA,0x7B,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFB,0xF7,0xDF,0xFF,
+0xE3,0x7D,0xFF,0xB7,0xFF,0xBF,0xFF,0xFF, 0xDF,0xFF,0xF8,0xFF,0xBF,0xFF,0xBF,0xEB,
+0xE7,0xFA,0xFE,0x3D,0xBF,0xE9,0xFC,0xBF, 0xFF,0xFA,0xFB,0xFE,0xFF,0xFF,0xFF,0xD9,
+0xFF,0xFF,0xFF,0xF6,0x7F,0xFF,0xF6,0x7D, 0xFF,0xDF,0xCF,0xFD,0xBF,0xFB,0xEF,0x7E,
+0xFF,0x7F,0xFF,0xFF,0xD3,0xFF,0xFD,0xFB, 0xFF,0xFB,0xFF,0xFF,0xFF,0xEF,0xFF,0xBF,
+0xFE,0xFF,0xF7,0xEF,0xFF,0xFF,0xFF,0xFB, 0xFF,0x87,0xFF,0xFD,0xFF,0xFF,0xFF,0xFF,
+0x7B,0xFE,0xFF,0xFE,0x3B,0xF7,0xF7,0xFF, 0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xFF,
+0xFF,0xFF,0xFF,0xFB,0xFF,0xFF,0xFF,0xF7, 0xFF,0xFF,0xAD,0xFF,0xFE,0xF7,0xFF,0xFF,
+0x5F,0xFF,0xFF,0xDF,0xFF,0xFD,0xFF,0xF5, 0xFF,0xDF,0xFF,0xBD,0xFF,0xE9,0xFF,0xC7,
+0xF3,0xFF,0xFF,0xF7,0xFF,0xF3,0xFF,0xF8, 0x3B,0xFF,0xFF,0x7B,0xDF,0xBF,0xFB,0xEF,
+0xFB,0xFF,0xFB,0xF7,0xF7,0xBB,0xFF,0xFF, 0xFF,0xFF,0xFB,0xFF,0xFE,0x7F,0xF3,0x7F,
+0x5E,0xB7,0xBF,0xFD,0x7F,0xFF,0xF9,0x7F, 0xFB,0xFF,0xEB,0xFD,0x7F,0x7F,0xFF,0xEF,
+0xFB,0xE0,0x3F,0xFE,0xBF,0xBF,0xDF,0xFF, 0x7E,0xFF,0xF7,0xFF,0xFF,0xFE,0xBF,0xFF,
+0xDB,0x78,0xFF,0xFF,0xFF,0xEE,0xA1,0xBF, 0xF5,0xDE,0xFB,0xF7,0xFF,0xFB,0xFF,0xFF,
+0xFF,0xFF,0xFB,0xFF,0xFF,0xD7,0xFF,0xFF, 0xFF,0xFF,0xEF,0xF0,0xFF,0xFF,0xFF,0xF3,
+0xF7,0xFF,0xEF,0xFF,0xE7,0xCF,0xFF,0xFB, 0xFF,0xEF,0xFF,0xFF,0x9F,0x9F,0xEF,0xFC,
+0x16,0xBF,0xFE,0xF3,0xE4,0xFF,0xFF,0xC6, 0xFF,0xE7,0xFF,0xFF,0xFD,0xFF,0xBF,0xFF,
+0xFF,0x3F,0xFF,0xBF,0xD6,0xAF,0x7F,0xFE, 0x6B,0x7E,0x7F,0xFF,0xAF,0xFF,0xFF,0xBF,
+0xFF,0x5F,0xFF,0xFE,0xFF,0xFF,0xFE,0xFF, 0xFF,0xBD,0xDB,0xFF,0xFE,0x5F,0xF2,0xFF,
+0xFF,0x5F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xEF,0x7F,0xFF,0xFF,0xFF,0xFF,0xDE,0xBF,
+0xFF,0xFF,0xEF,0xFB,0x77,0xFE,0xBD,0x7F, 0x5F,0xFF,0xFF,0xFF,0xDF,0x6F,0xED,0xFF,
+0xFD,0xFF,0x7F,0xFD,0x6F,0xFF,0xFF,0x77, 0xDA,0xCF,0xFD,0x5F,0xFF,0xBF,0xFF,0xFF,
+0xDF,0x7F,0xFF,0xFB,0xFF,0xFF,0xFF,0xFF, 0x66,0x7F,0xFF,0xFE,0xBF,0xE7,0xBF,0xFA,
+0xFF,0xFE,0xFF,0xFF,0xFF,0xDF,0xFF,0x59, 0xEF,0xFF,0xEF,0xFB,0x7F,0x89,0xFF,0xFF,
+0xE9,0xFF,0x6F,0xFF,0xF5,0xFF,0xFF,0xFF, 0xFF,0xFF,0x7F,0xF2,0xF7,0xFF,0xFF,0xEF,
+0xF8,0x7F,0xFB,0xFF,0xFD,0xFF,0xFF,0xD9, 0xFF,0xEF,0xBB,0xFF,0xFF,0xFF,0xBF,0xEF,
+0xDE,0xFF,0xFF,0x9F,0x7F,0xDF,0xFF,0xF7, 0xFF,0xFF,0xFF,0xFF,0xDF,0xFF,0xFF,0xAF,
+0xFF,0xFF,0xF7,0x3F,0xEB,0x9F,0xFE,0x7F, 0x9E,0x7F,0x9F,0xFE,0x87,0xFF,0xED,0xDB,
+0x56,0xFF,0xBF,0xAF,0x0B,0xD2,0xFF,0xEF, 0xDB,0x6E,0x7D,0xBD,0x6F,0xF8,0xFE,0x3F,
+0xFA,0x5B,0xFF,0xFD,0xBF,0xEF,0xFF,0xBF, 0x6F,0xDB,0xE6,0xFF,0xFF,0x3F,0xFF,0xDF,
+0xFE,0xFF,0xFF,0xFF,0xFF,0xDA,0x3F,0xFF, 0xFB,0xFE,0xFE,0xFF,0xFF,0xDF,0xF7,0xBD,
+0xFF,0xFD,0xFF,0xFE,0xFF,0xFB,0xFF,0xFF, 0xFF,0xFF,0xF1,0x5F,0xFD,0x9F,0xDF,0xFD,
+0xFF,0xFD,0x7F,0xFF,0xFF,0xFF,0xFF,0x76, 0xFA,0xFF,0xFF,0x7F,0xE3,0xF8,0xFF,0xAE,
+0xFF,0xFB,0x7E,0x9D,0x73,0xFF,0xFA,0x7F, 0xDF,0xFF,0xFF,0x7F,0xFF,0xFB,0xCD,0xFF,
+0x7F,0xEF,0xFB,0xFF,0xFD,0xFF,0xF7,0x7F, 0x7F,0xEF,0xFF,0xED,0xFF,0xFF,0xFF,0xB5,
+0xFF,0xBF,0xFF,0xBF,0xFD,0xEF,0xDB,0xF7, 0xFF,0x93,0xFF,0xEF,0xE2,0xF9,0xBE,0x7F,
+0x8B,0xE7,0xF9,0xFE,0x6B,0xE7,0xF9,0xFE, 0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0x47,0xFF,
+0xFF,0xFD,0xFF,0x9F,0xFF,0xD7,0xFF,0xFF, 0xFF,0xFF,0xF5,0xFF,0x9F,0xFF,0xF7,0xFE,
+0xFF,0xBF,0xFE,0x6F,0xFF,0xFF,0xFB,0xFF, 0xFF,0xFF,0xAF,0xFF,0xFF,0xFF,0x7F,0xFB,
+0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD, 0xDF,0xFF,0xFF,0xF7,0xFF,0xFF,0xFF,0xDF,
+0xFF,0xFF,0xFF,0x5F,0xFF,0xFF,0xFF,0xFF, 0x5F,0xFB,0xFE,0xFF,0xF8,0x37,0xFF,0xFF,
+0xEF,0xFF,0x7F,0xFE,0xBF,0xFF,0xFF,0xFE, 0xBF,0xFF,0xFF,0x7F,0xFF,0xBF,0xFD,0xFF,
+0x7F,0xFA,0x7F,0xFF,0xFF,0x6F,0xFF,0xFF, 0x7D,0xFF,0xCF,0xFF,0xFF,0xFF,0x4F,0xFF,
+0xF2,0xFF,0xFF,0xFF,0xFF,0xFF,0xFA,0xBF, 0xFF,0xAE,0xEB,0xFA,0xFE,0xBB,0xAD,0xEB,
+0xFA,0xF7,0xAF,0x6B,0xFA,0xF6,0xBF,0x25, 0xE9,0xF2,0x7F,0x45,0xFF,0xFF,0xFD,0xF7,
+0xF7,0xBF,0xFF,0xDF,0xFF,0xFF,0xBF,0xFB, 0xFF,0xDF,0xF3,0xFF,0xF7,0x3F,0xCF,0xFF,
+0xA1,0xFF,0xFF,0xBF,0xE7,0xFF,0xFF,0x7F, 0xFF,0x3D,0xFF,0xFF,0xFF,0xF7,0xFF,0x2F,
+0xFF,0xFB,0xF5,0x7F,0xFE,0x57,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,
+0x3F,0xFF,0xFE,0xFF,0xFF,0xFF,0xFD,0xFE, 0xF7,0xEE,0xAF,0xFE,0xEE,0xE7,0xFA,0xFF,
+0xFE,0x9D,0xF9,0x5E,0xFE,0xFF,0xEB,0xFF, 0xFF,0xDF,0xA7,0xFF,0xFF,0xFF,0xFC,0xDB,
+0xFF,0xFF,0xFF,0x7E,0xFB,0xFF,0xFF,0xEF, 0xFB,0xFD,0xFF,0xDB,0xFF,0xFF,0xFF,0xEF,
+0xFF,0xFF,0xFF,0xFD,0xBF,0xFE,0xBF,0xFF, 0x6F,0x7F,0xFF,0xF7,0xFF,0xFF,0xF9,0xFF,
+0xF7,0xFF,0xBF,0xDE,0xF7,0xFF,0xFF,0xFF, 0xFA,0x7F,0xFD,0xBF,0x5F,0xFF,0xFF,0xBF,
+0xFF,0xED,0xFF,0xF7,0xBF,0xFF,0xFF,0xEF, 0xFF,0xDF,0xFF,0xFF,0xFF,0xE6,0xFF,0xFB,
+0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEB,0xFF,
+0xFD,0xFF,0xF5,0xFF,0xF6,0x7F,0xDF,0xBD, 0xCF,0xFF,0xFF,0xFF,0xFF,0xDF,0xFF,0xFF,
+0xFF,0xF9,0xFF,0xFF,0xFF,0xFF,0xFF,0xE3, 0xFF,0xEE,0xBF,0xFF,0x7D,0xEF,0xFE,0xFF,
+0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xFE, 0xFF,0xFF,0xFF,0xFF,0xE7,0xFF,0xB5,0xAE,
+0xFF,0xFF,0xB6,0xFE,0xBF,0xFF,0xFF,0xBF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0x27,0xFF,0xEF,0xFE,0x7F,0xDF,0xFF, 0x7E,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFD,0xFF,0xF7,0xF9,0x9F,0xFF, 0x5F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,
+0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0x0F,0xFF,0xE7,0xBF,0xFE,
+0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xFC,0xBF, 0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xC4,
+0x6B,0xFF,0x29,0x1F,0xFB,0xAF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xEF,0x1B,0xFE,0xFF,0xFC,
+0x6F,0xFF,0xFF,0xFD,0x6A,0xF7,0xD7,0xF5, 0xBF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFE,0xBF,0xFF,0xFF,0xFA,0xFF,0xFF,0xF7, 0xFB,0xDD,0xBF,0xFF,0xE7,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0x7F,0xFF, 0xFF,0xF5,0xFF,0xFF,0xF7,0xFD,0xB3,0xEF,
+0xFD,0x7E,0x5D,0xFF,0xFD,0xFF,0xFF,0xFF, 0xFD,0x7F,0xD2,0xF5,0xFB,0x7E,0xCB,0xB7,
+0xFF,0xFF,0xFF,0xC6,0xFF,0xFD,0xEE,0x63, 0xFF,0xFF,0xFF,0xFF,0xFF,0xF6,0xFD,0x65,
+0x5B,0xDF,0xFF,0xD5,0xFF,0xFF,0xFF,0xF6, 0xE7,0xBF,0xF7,0xA9,0xFF,0xFF,0xED,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xEB,0xFF,0xFF,0xFF, 0xAF,0xFF,0xFF,0xFF,0xF8,0x1B,0xFF,0xE3,
+0xD0,0xBF,0xFF,0xE1,0xFF,0xFF,0xFF,0xFF, 0xFF,0xD7,0xFF,0xFF,0xFF,0x5F,0xFF,0xFF,
+0xFF,0xFF,0xAF,0xFF,0xDB,0x76,0xBF,0xFF, 0x7F,0xFF,0xBF,0xEF,0xFE,0xFF,0xBF,0xEF,
+0xFB,0xFE,0xFF,0xFF,0xFF,0xBF,0xF2,0x7F, 0xFF,0x9F,0xFE,0xBD,0xFE,0x7F,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xF7,0x3F,0xEC,0x7F,0xF6,0x95,0xBB,
+0xEF,0xF8,0xFE,0xFC,0xBF,0x2F,0xDA,0xFC, 0xBF,0x2F,0xCB,0xF2,0xFC,0xBF,0xEF,0xFF,
+0xA9,0xBF,0xCF,0xFB,0xFF,0xFF,0xFF,0xFE, 0xDD,0xB7,0x6D,0xF6,0xD9,0xB6,0x6D,0x9B,
+0x76,0xD9,0xBF,0xFB,0xFD,0xA3,0xFF,0xBF, 0xEF,0xFF,0xEF,0xFF,0xFF,0xFF,0x7F,0xDF,
+0xFD,0xEF,0x7B,0xDE,0xF7,0xFD,0xEF,0x7F, 0xFF,0xFF,0x05,0xFF,0xFA,0xFE,0x7F,0xEF,
+0xE3,0xFF,0xFF,0xFD,0x7F,0xFF,0xFF,0xFF, 0xFF,0x5F,0xFF,0xFF,0xFD,0x7F,0xFB,0xAF,
+0xFF,0x63,0xC8,0xFF,0xBF,0xEF,0xFF,0xFF, 0xFA,0x7F,0xFF,0xFF,0xFF,0xFE,0x9F,0xF7,
+0xFF,0xFA,0xBF,0xFE,0x9F,0xFB,0x7F,0xFF, 0xFF,0xEF,0xD7,0xFF,0xFF,0xF5,0xFF,0xFF,
+0xFF,0xFF,0xFD,0x7F,0xFF,0xFF,0xBF,0xFF, 0xF9,0xBF,0xFF,0xBE,0x27,0x9F,0xE7,0xF9,
+0xFE,0x7F,0x8B,0xE7,0xFE,0x7F,0x9F,0xE2, 0xF9,0xFE,0x7F,0x9F,0xE7,0xF1,0x7F,0xFF,
+0xFF,0xFF,0xFB,0xFE,0xFF,0xFF,0xFF,0xD7, 0xFF,0xFF,0xFF,0xFF,0xF5,0xFF,0xFF,0xFF,
+0xD7,0xFF,0xFA,0xFF,0xFE,0xFF,0xFF,0xFF, 0xFD,0xFF,0xFF,0xFF,0xAF,0xF7,0xFF,0xFF,
+0xFF,0xEB,0xFF,0xFF,0xFF,0xAF,0xFF,0xC4, 0xFF,0xF7,0xFF,0xFF,0xEF,0xFF,0xFF,0xFF,
+0xFF,0x5F,0xFF,0xFF,0xFF,0xFF,0xD7,0xFF, 0xFF,0xFF,0xFF,0xFF,0xEB,0xFF,0xFB,0x7A,
+0xDF,0xF7,0xFD,0xFF,0xFF,0xFE,0xBF,0xFF, 0xFF,0x7F,0xFF,0xAF,0xFF,0xFF,0xFF,0xF7,
+0xEF,0xE3,0xFF,0xDD,0xD2,0xFF,0xDF,0xFF, 0xFF,0xF2,0xFC,0xBF,0xCB,0xF6,0xFD,0xBF,
+0x2F,0xCB,0xFF,0x7F,0xDF,0xDE,0xAF,0xFF, 0xDA,0xEE,0xBF,0xAF,0xE9,0xFA,0xF4,0xBD,
+0xAF,0x5A,0xAE,0xBB,0xAB,0x6B,0xDA,0xDE, 0xBF,0xAD,0xD7,0x5E,0xFF,0xFF,0xBF,0xFC,
+0xFF,0xDF,0xFD,0xFF,0xFF,0xFF,0xFF,0xDF, 0xF7,0xFF,0xFF,0xFF,0xFF,0xFD,0xFF,0xFA,
+0x1F,0xFF,0xFE,0xFB,0xEF,0xBF,0xFD,0xFF, 0xFD,0xBD,0x77,0xFF,0xFF,0xFF,0xFF,0x9D,
+0xEF,0xFF,0xFF,0xFF,0xEF,0x7D,0xFF,0xFB, 0xFE,0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEE, 0xBF,0xE4,0xFB,0xFF,0xFE,0x3F,0xFE,0xFF,
+0xFF,0xFF,0xFF,0xAF,0xEA,0xFE,0xBF,0xAF, 0xEB,0xFA,0xFE,0xFF,0xFF,0xFF,0x55,0xF6,
+0xFF,0xFE,0xF7,0xFF,0x7F,0xFF,0xEB,0xF7, 0x5F,0xC5,0xFD,0x7F,0x5F,0xD7,0xF5,0xFF,
+0x6F,0xFB,0xFF,0x8A,0xFF,0xFF,0xFF,0xFF, 0xEB,0xFF,0xFF,0xFF,0xFF,0xFB,0xBF,0xBF,
+0xEF,0xFB,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF, 0x77,0xDF,0xFB,0xFF,0xFD,0x7F,0xEF,0xFF,
+0xFF,0xFF,0xBF,0x7F,0xFF,0xDF,0xBF,0xFF, 0xFB,0xFF,0xFF,0xFF,0xFE,0xEF,0xDF,0xFF,
+0xFE,0xFF,0x9F,0xEF,0x7D,0xFF,0xF7,0xFF, 0x7F,0xFF,0xFF,0xDF,0xF7,0xFD,0xFF,0xEF,
+0xDF,0xFF,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0xFF,0xFF,0xFB,
+0xFD,0xFF,0xBF,0xDF,0xD1,0xFF,0xF8,0x3B, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0x7E,0xDB,0xFD,0xFF,0x77,0xDB,0xB7,0x7D, 0xBF,0xFB,0xFF,0xF8,0x7F,0xED,0x7B,0x5E,
+0xFF,0xFE,0xFF,0xFF,0x4F,0xD7,0xFD,0x7F, 0xDF,0xD7,0xF5,0xFF,0x7F,0xFF,0xFF,0xFF,
+0xF2,0x3F,0xFE,0xFF,0xBF,0xFF,0xFF,0xFF, 0xFF,0xBF,0xEF,0xFE,0xFF,0x3B,0xEE,0xFF,
+0xFC,0xEF,0xFF,0xFF,0xFF,0x85,0xFF,0xFD, 0xFE,0xFF,0xF5,0xFF,0xFF,0xFE,0xFF,0xDF,
+0xFB,0xFF,0x5F,0xBF,0xFF,0xFD,0xFF,0xFF, 0xFF,0xFF,0xA8,0xFF,0xFF,0x9F,0x9E,0xFF,
+0xFF,0xFF,0x7F,0xF3,0xFF,0xFF,0xCF,0xFF, 0xF7,0xFD,0xFF,0x7F,0xFF,0xFF,0xFC,0x16,
+0xBF,0xCF,0xA3,0xE5,0xEF,0x7F,0xFF,0xF3, 0xE4,0xFF,0xCF,0x93,0xFC,0xFF,0x3F,0xCF,
+0xFF,0xFF,0xFF,0xD6,0x0F,0x7D,0xBF,0x6E, 0xFB,0xF4,0xFC,0xAF,0x6D,0xDB,0x77,0xB7,
+0x6D,0xDB,0xF6,0xFD,0xBF,0xFF,0xFF,0xFF, 0xBF,0x9B,0xFA,0xDE,0xB7,0xB7,0xED,0xF9,
+0x7E,0xB7,0xAC,0xEB,0xD6,0xB3,0xAD,0xEB, 0x7A,0xDF,0xFF,0xFF,0xFF,0xD8,0xBF,0xFF,
+0xB7,0xED,0x9F,0x6F,0xDD,0xF7,0x68,0xDB, 0x37,0xB3,0x6C,0xDB,0x36,0xCD,0xB3,0x7F,
+0xFF,0x7F,0xF5,0x6F,0xFD,0xEF,0x79,0x3D, 0xF7,0x93,0xE4,0x7A,0x9E,0xAD,0xEA,0x7A,
+0x9E,0xF7,0xBD,0xEF,0xFF,0xFF,0xFF,0x76, 0x7F,0xFB,0xC6,0xFF,0xBB,0xEF,0xDA,0xFE,
+0xFD,0xBF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFF,0xFF,0xFB,0xFF,0xA5,0xFF,0xFD,0xAB,
+0x6F,0x78,0xDE,0x17,0x8F,0x79,0xDF,0xFD, 0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0xFF,0xFB,
+0xFF,0xFB,0xFF,0xEF,0xFB,0xEF,0xFB,0xFE, 0xFF,0xBB,0xDA,0xF3,0xEF,0x3B,0xCE,0xF3,
+0xBC,0xEF,0x3F,0xCF,0xDF,0xFF,0xB7,0xFF, 0xFF,0xFF,0xCF,0x73,0xFF,0xBF,0xEF,0xFF,
+0xF3,0xFF,0x3F,0xCF,0xF3,0xFC,0xFF,0x3D, 0xCF,0x9F,0xFE,0x07,0xFF,0xAF,0xEB,0xFE,
+0xFD,0xBF,0xEF,0xEB,0xFA,0xFF,0xAF,0xEB, 0xFA,0xFE,0xBF,0xAF,0xFB,0xFE,0x3F,0xFB,
+0x9B,0xFF,0x7F,0xDF,0xFF,0xF3,0xFE,0xFF, 0xDE,0xF7,0xBF,0x7B,0xDE,0xF7,0xBD,0xEF,
+0x7B,0xFE,0xFF,0xFF,0xDF,0x3F,0xFE,0xFF, 0xB7,0xFF,0xEF,0xF7,0xFF,0xBF,0xED,0xFE,
+0xDF,0xB7,0xED,0xFB,0x7E,0xDF,0xFF,0xFF, 0xFF,0xFD,0x5F,0xEF,0xEB,0xFA,0xFE,0xF5,
+0xBF,0x6F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFE,0xF8,0xFF,0xA8,0xFF,
+0xFF,0xBF,0xEF,0xFB,0x6A,0xFB,0xB7,0xEF, 0xFB,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,
+0xEF,0xFB,0xFF,0xE0,0xFF,0xFF,0xFD,0x7F, 0x5C,0xD7,0x7D,0xDF,0xF3,0x5C,0xF5,0xCD,
+0x73,0x5E,0xD7,0xB5,0xFD,0x7F,0xEF,0xFF, 0xDB,0xFF,0xFF,0xE2,0xF8,0xBE,0x2F,0x8F,
+0xE7,0xF8,0xBE,0x6B,0xE2,0xF8,0xBE,0x2F, 0x8B,0xE2,0xF9,0xFE,0x7F,0xE7,0xFF,0xD7,
+0xF5,0xFD,0x7F,0xFF,0xF7,0xF5,0xFD,0x7F, 0xD7,0xF5,0xFD,0x7F,0x5F,0xD7,0xF5,0xFF,
+0xFF,0xFF,0x8F,0xFF,0xAF,0xEB,0xFA,0xFF, 0xFF,0xBF,0xEB,0xFA,0xFF,0x2F,0xEB,0xFA,
+0xFE,0xBF,0xAF,0xEB,0xFF,0xFF,0xFE,0x5F, 0xFF,0x5F,0xFF,0xFF,0xFD,0xFF,0xFF,0xD7,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xBF,0xFE,0xB7,0xFD,
+0xFF,0x7E,0xDF,0xF7,0xAD,0xFF,0x7F,0xF7, 0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0x7F,
+0xF6,0x7F,0xFF,0xFF,0xFF,0xDB,0xF6,0xFC, 0xAF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xEC,0xBF,0xFF, 0xAF,0xEB,0xFA,0xF6,0xAB,0x8F,0xEB,0xFA,
+0xF7,0xA5,0xEB,0xFA,0xBE,0xBF,0xAF,0xEB, 0xFA,0xFF,0x6D,0xFF,0xFF,0x7F,0xDF,0x33,
+0xDD,0xFF,0x7F,0xFE,0xF7,0xFC,0x7F,0xFB, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xA9,
+0xFF,0xFD,0xFF,0xFF,0xFE,0xFF,0xFF,0xDF, 0xFF,0xFF,0xEF,0xEF,0xFD,0xFF,0x7F,0xFF,
+0xFF,0xFF,0xFF,0xFE,0xA7,0xFF,0xFF,0xFF, 0x77,0xDF,0xF7,0xFD,0x9F,0x7F,0xFE,0x77,
+0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xAF,0xBF,0xAF,0xFF,0xF9,0xBE,0xBF,
+0x8F,0xFB,0xFE,0xFE,0xEF,0xFB,0xFE,0xFF, 0xBF,0xEF,0xFB,0xFF,0xFF,0xFD,0xDF,0x6F,
+0xEF,0xFF,0x7F,0xFF,0xBF,0xBF,0xDF,0xFF, 0xFC,0xFF,0xDF,0xF7,0xFD,0xEF,0x7F,0xDF,
+0xFF,0xFF,0xFF,0x3F,0xF6,0xFF,0xCF,0xFF, 0xDB,0xFB,0xF7,0xFF,0xEB,0x7A,0xFF,0xFF,
+0xFF,0xBF,0xEF,0xFB,0xFF,0xFF,0xFF,0xFE, 0x6D,0xFD,0xFF,0x5F,0xFB,0xFF,0xFF,0xF7,
+0xFF,0x5F,0xF5,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xF8,0xFF,0xFB,0xFF,
+0xFF,0xFD,0xFF,0xFF,0xFF,0xFF,0xE7,0xF6, 0xBF,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF,0xFF,
+0xFF,0xC9,0xFF,0xFF,0xFF,0xBD,0xFF,0xBF, 0xAF,0xEF,0xEF,0x3F,0xD1,0xFC,0x7F,0xFB,
+0xC7,0xFF,0xFF,0xFF,0xFF,0xFF,0xE3,0xFF, 0xFF,0xFF,0xFF,0xFD,0xFF,0xFF,0x77,0xFF,
+0xDF,0xB7,0xFD,0xF7,0xFD,0xF7,0xFF,0xFF, 0xFF,0xFF,0xFF,0x57,0xFF,0xF7,0xA5,0xFD,
+0x3F,0xDF,0xBF,0xBF,0xFE,0x7F,0xFF,0xFF, 0xFF,0xDF,0xFA,0xFD,0xFF,0xFF,0xFF,0xFE,
+0x87,0xFF,0xE9,0xFF,0xFE,0xEF,0xBF,0xEF, 0xFE,0xFE,0xFF,0xEF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFA,0x9F,0xFF,0x3F, 0xFF,0xFD,0xFD,0x57,0xDF,0xFD,0xF3,0xFF,
+0xDF,0xFD,0xFF,0x5F,0xDF,0xF5,0xFD,0xFF, 0xFF,0xF9,0x8F,0xFF,0xFF,0xFF,0xEE,0x7F,
+0xFF,0xFF,0xBF,0x5E,0xFE,0xEC,0xFB,0x3F, 0x7F,0x9F,0xEF,0xF9,0xFF,0xFF,0xCD,0x6B,
+0xFF,0xFF,0xFF,0xC5,0xF3,0xFC,0xFA,0x38, 0xFF,0xAF,0x3F,0xEE,0x7F,0x9F,0xFF,0xD9,
+0xFF,0xFF,0xFD,0x7A,0xF7,0xFF,0xF3,0xFF, 0xAF,0x6F,0xDB,0xF2,0xB9,0xE9,0xFB,0xFF,
+0xFF,0xFF,0xFE,0xFF,0xFF,0xEF,0xFF,0xFB, 0xC5,0xBF,0xFF,0xEF,0xFF,0x5E,0xB7,0xAD,
+0xCD,0x79,0x7C,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0x93,0xFF,0xEF,
+0xEA,0xFE,0xBF,0xEF,0x5B,0xD2,0xCD,0xF5, 0x6D,0x77,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,
+0xFF,0xFF,0x66,0xFF,0xD5,0x65,0x7D,0x5F, 0x75,0x9D,0x65,0x7F,0xD6,0xFB,0x4F,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF6,0xC7, 0xFF,0xBF,0xEF,0xFA,0xFE,0xFF,0xBF,0xEB,
+0xFF,0xDF,0xFF,0x7E,0xFF,0xFF,0xEF,0xFD, 0x7E,0xD7,0xFF,0x78,0xDF,0xFF,0x5F,0xDF,
+0xF5,0xBF,0x7F,0xDF,0xC5,0xFF,0x3F,0xF6, 0x7E,0xFF,0x0F,0xEF,0xF2,0x3E,0xBF,0xFF,
+0xFB,0x3F,0xFF,0xFB,0x7F,0xFF,0xB3,0xFE, 0xFB,0xF6,0xFD,0xFF,0xDA,0xF7,0xFD,0xFF,
+0x7F,0xDF,0xF7,0xBF,0xFF,0xFA,0x7F,0xFF, 0xFF,0xFF,0xFF,0x9F,0xFF,0xF3,0xDC,0xF9,
+0xBF,0xCE,0xE7,0xF9,0xFE,0x7F,0x9F,0xE7, 0xFF,0xFF,0xE2,0x7F,0xFE,0xFF,0xBF,0xEF,
+0xEB,0xFA,0xFF,0x9F,0x67,0x1E,0xFF,0x8F, 0xE7,0xF8,0xFE,0x7F,0x8F,0xEF,0xFF,0xBD,
+0xBF,0xFF,0xFB,0xFF,0xFF,0xDF,0xF7,0xFF, 0xFC,0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFD,0xB3,0xFF,0xFF,0xEF, 0xFF,0xFF,0xBF,0xED,0xFF,0xFB,0xEE,0xFE,
+0xFF,0xFF,0xEF,0xFF,0xFE,0xFF,0xFF,0xFF, 0xFF,0xB5,0xFF,0xB7,0xFD,0xFD,0x6E,0xFF,
+0xFF,0xFE,0xFD,0x2F,0xD8,0xFE,0xBF,0x8F, 0xEB,0xF9,0xFE,0x3F,0xFF,0xFA,0xCF,0xFF,
+0xE7,0xD9,0xFA,0xBF,0xDF,0x77,0xFC,0xFB, 0x3F,0xAB,0xFE,0xFF,0xBF,0xEF,0xFB,0xFE,
+0xFF,0xFF,0xEE,0x1F,0xFF,0xDF,0xF7,0xFF, 0xFF,0xFF,0x5F,0x97,0x35,0xBF,0x5E,0xFE,
+0xBF,0xEF,0xFF,0xF7,0xFD,0xFF,0xFF,0xFA, 0xBF,0xFF,0xBE,0x6F,0x9F,0xE7,0xF8,0xBE,
+0x2F,0x8B,0x66,0x94,0x7D,0x9D,0xE7,0xF9, 0xFE,0x7F,0x9F,0xE7,0xF1,0x7F,0xFF,0xFF,
+0xFF,0xF7,0xF5,0xFD,0x7F,0x5F,0xFB,0xFD, 0x9E,0xFF,0xFB,0xFE,0xFF,0xFF,0xEF,0xFF,
+0xFF,0xA0,0xFF,0xFF,0xFF,0xBF,0xEF,0xEB, 0xFA,0xFE,0xBF,0xB7,0xF7,0xF7,0xFF,0xFF,
+0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xDD,0xFF, 0xFD,0xFF,0xFF,0xFF,0xD7,0xFF,0xFF,0xFF,
+0x7F,0xF5,0xFF,0xFF,0xEF,0xFF,0xFF,0xFF, 0xBF,0xFF,0xFF,0xAB,0xFE,0xFB,0xFE,0xFF,
+0xF7,0xAF,0xFF,0xFF,0xDE,0xF7,0xEB,0x5F, 0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFF,0xFF,
+0xB3,0xFF,0xC9,0xFE,0xFF,0xFF,0xFF,0xFF, 0xD6,0xFF,0xFF,0xCB,0xFF,0xFF,0xDF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFC,0x8F,0xFF,0xBA, 0xBE,0xBF,0xAF,0xEB,0x78,0xFE,0xB7,0xAD,
+0x3A,0xFE,0xB7,0xAF,0xEB,0x7A,0xFE,0xBF, 0xAF,0xFF,0x9F,0xFF,0xFF,0xDF,0xFC,0xFF,
+0xFF,0xFE,0xC3,0xFE,0xFF,0xFF,0x33,0xFC, 0xFF,0xBF,0xDF,0xF3,0xFF,0xFF,0xBB,0x9F,
+0xFF,0xFF,0xFF,0xEB,0xDF,0xFF,0xFF,0xAF, 0xF7,0x6F,0xF9,0xBF,0xEF,0xFD,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xE3,0x7F,0xFF,0xFF,0xFF, 0xFB,0xFF,0xFF,0xBF,0xFD,0xFB,0xF7,0xFF,
+0xDF,0xF7,0xFF,0xFE,0xEF,0x5F,0xBD,0xFF, 0xFA,0xFF,0xF8,0xFF,0xBF,0xAF,0xFB,0xFE,
+0xFE,0x3F,0xEF,0xE8,0xFF,0xDF,0xF3,0xFD, 0xFF,0xFF,0xFF,0xFF,0xFF,0xED,0xFF,0xFB,
+0xFD,0xFF,0xAF,0xFF,0xFF,0xFE,0xFE,0xBF, 0xDB,0xFF,0xFF,0xFF,0xBF,0xFF,0xDF,0xFF,
+0xFD,0xFF,0xCB,0xFF,0xFF,0xFF,0xFF,0xFF, 0xBF,0x6F,0xFF,0x7F,0xB7,0xB3,0xFF,0xFF,
+0xDF,0xFF,0xFB,0xEF,0xFF,0xFF,0xFF,0x07, 0xFF,0xFB,0xFF,0xFF,0xFF,0xED,0xFF,0xF5,
+0x7C,0xFF,0x7F,0xFE,0xFF,0xFF,0xEF,0xCF, 0xFF,0xFB,0xFF,0xFF,0x2F,0xFF,0xFF,0xFF,
+0xFF,0xF3,0xFF,0xFB,0xFF,0xFE,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,
+0xFD,0x1B,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7C,0xFF,0xFF,0xFF,0xFF,
+0xEF,0xFF,0xFF,0xFF,0xFF,0xFB,0xBF,0x7F, 0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xDB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD, 0xFF,0xFF,0xF0,0x7F,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF,0xDF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0xBF,0xFE,
+0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xEF,0xFE,0xFF,0xBF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xEF,0xFA,0xB5,0xFF,0xFF,0xFF, 0xF7,0xF7,0xFF,0xFF,0xFF,0xFF,0xDF,0xFB,
+0xFC,0xFF,0xFF,0xFE,0xFF,0x7F,0xDF,0xBF, 0xFF,0xCB,0xBF,0xF9,0xFE,0x7F,0x9F,0xE7,
+0xF9,0xFE,0x7F,0x97,0xE1,0xFE,0x79,0x9F, 0xE7,0xFD,0xFE,0x7F,0xDF,0xFE,0x37,0xFF,
+0xFB,0xDE,0xDE,0xBD,0xEF,0xF3,0xFE,0xFB, 0xAF,0xEB,0xFE,0xFF,0xFF,0xCF,0xFF,0xFE,
+0xFF,0xBF,0xFF,0x8F,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xE7,0xF9,0x5E,0x7F,0xEF,0xFB,
+0xDA,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFD, 0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xDF,
+0xFF,0xFF,0x7F,0xFF,0xFF,0xF7,0xFB,0x7F, 0xFF,0xFF,0xFF,0xFF,0xFC,0x3F,0xFF,0xBF,
+0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0x7B,0x7F, 0xBF,0xEF,0xFB,0xFE,0xFF,0xB5,0xEF,0xFB,
+0xBF,0xFA,0x7F,0xFC,0xFF,0x3F,0xCF,0xF3, 0xFC,0xFF,0x3F,0xCF,0xBC,0xFF,0x3F,0xEF,
+0xF3,0xFC,0xFE,0x3F,0xCF,0xFF,0xEE,0xEF, 0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0x6A,0xD7,
+0xB7,0xFB,0xF8,0xFF,0xB7,0xEF,0xBA,0xFE, 0xFF,0xBF,0x7F,0xE9,0xFF,0xF9,0x7E,0x5F,
+0x97,0xE5,0xF9,0xFE,0x7F,0xBF,0xF9,0x7E, 0x5F,0x9F,0xE5,0xFB,0xFE,0x5F,0xB7,0xFF,
+0xA3,0xFF,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7, 0xFD,0xFF,0x5E,0xF7,0x7D,0xFF,0x77,0xDF,
+0xF7,0xFD,0xFF,0x7F,0xFF,0xD7,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0xDF,0xFB,0x7F,
+0xFF,0xFF,0xEF,0xFF,0xFE,0xFB,0xFF,0xFF, 0xBF,0xFE,0x8F,0xFF,0xDF,0xF7,0xFD,0xFD,
+0x7F,0xDF,0xF7,0xFD,0x3E,0xDF,0xF5,0xBD, 0xFF,0x7F,0xDF,0xF7,0xFD,0xF7,0xFF,0x9F,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFD,0xFF,0xBE,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFD,0x3F,0xFF,0xDF,0xF7, 0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0xCF,
+0x77,0xFC,0xFF,0x5F,0xDF,0xF7,0xFD,0xFF, 0xF4,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFD,0xFF,0xFF,0xFF,0xEE,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xED,0xFB,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xE9,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFB,0xFF,0xFF,0xFF,0xD3,0xFF,0xFF,
+0xBF,0x3F,0xFB,0xFF,0xFF,0xFF,0xFB,0xF3, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xF7, 0xFF,0xFF,0xFF,0xFF,0x17,0xFF,0xFF,0xFF,
+0xDF,0xFF,0xFD,0xFF,0xFF,0xFF,0xFF,0xFF, 0xDF,0xDF,0xFF,0xFD,0xFF,0xFF,0xDF,0xF7,
+0xFF,0x4F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFD,
+0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0x9F,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFF, 0xFF,0xFF,0x7A,0x3F,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF2,
+0x7F,0xFF,0xFB,0xFE,0xFF,0xBF,0xEF,0xF8, 0xFE,0xFF,0xBF,0xFB,0xFE,0xFF,0x8F,0xEC,
+0xFB,0xFE,0xFF,0xBF,0xF8,0xF7,0xFE,0xFF, 0xBF,0xEF,0xFB,0xFE,0xFD,0xBF,0xCF,0xEC,
+0xFF,0x3F,0xEF,0xDB,0xF8,0xFF,0xBF,0xCF, 0xFF,0xF9,0xFF,0xFF,0xBF,0xFF,0xFB,0xFF,
+0xFF,0xFF,0xEF,0xFB,0xDF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xBB,0xFF,
+0xEF,0xFB,0xFE,0xEF,0xBF,0xEE,0xEB,0xFB, 0xFE,0xFF,0xEF,0xFE,0xEE,0xBF,0xFE,0xEB,
+0xFF,0xEF,0xFF,0x17,0xFF,0x7E,0xEB,0xBB, 0xFE,0xBF,0xBE,0xFB,0xEF,0x5B,0xF7,0xBD,
+0xFB,0xCF,0xBF,0xBF,0xBB,0xFB,0x7E,0xCC, 0xEF,0xFF
+
+};
diff --git a/linux/drivers/media/video/dabusb.c b/linux/drivers/media/video/dabusb.c
new file mode 100644
index 000000000..b9ba95f5e
--- /dev/null
+++ b/linux/drivers/media/video/dabusb.c
@@ -0,0 +1,874 @@
+/*****************************************************************************/
+
+/*
+ * dabusb.c -- dab usb driver.
+ *
+ * Copyright (C) 1999 Deti Fliegl (deti@fliegl.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *
+ * $Id: dabusb.c,v 1.54 2000/07/24 21:39:39 deti Exp $
+ *
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/socket.h>
+#include <linux/list.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/atomic.h>
+#include <linux/delay.h>
+#include <linux/usb.h>
+#include <linux/smp_lock.h>
+#include <linux/mutex.h>
+
+#include "dabusb.h"
+#include "dabfirmware.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.54"
+#define DRIVER_AUTHOR "Deti Fliegl, deti@fliegl.de"
+#define DRIVER_DESC "DAB-USB Interface Driver for Linux (c)1999"
+
+/* --------------------------------------------------------------------- */
+
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define NRDABUSB 256
+#else
+#define NRDABUSB 4
+#endif
+
+/*-------------------------------------------------------------------*/
+
+static dabusb_t dabusb[NRDABUSB];
+static int buffers = 256;
+static struct usb_driver dabusb_driver;
+
+/*-------------------------------------------------------------------*/
+
+static int dabusb_add_buf_tail (pdabusb_t s, struct list_head *dst, struct list_head *src)
+{
+ unsigned long flags;
+ struct list_head *tmp;
+ int ret = 0;
+
+ spin_lock_irqsave (&s->lock, flags);
+
+ if (list_empty (src)) {
+ // no elements in source buffer
+ ret = -1;
+ goto err;
+ }
+ tmp = src->next;
+ list_move_tail (tmp, dst);
+
+ err: spin_unlock_irqrestore (&s->lock, flags);
+ return ret;
+}
+/*-------------------------------------------------------------------*/
+#ifdef DEBUG
+static void dump_urb (struct urb *urb)
+{
+ dbg("urb :%p", urb);
+ dbg("dev :%p", urb->dev);
+ dbg("pipe :%08X", urb->pipe);
+ dbg("status :%d", urb->status);
+ dbg("transfer_flags :%08X", urb->transfer_flags);
+ dbg("transfer_buffer :%p", urb->transfer_buffer);
+ dbg("transfer_buffer_length:%d", urb->transfer_buffer_length);
+ dbg("actual_length :%d", urb->actual_length);
+ dbg("setup_packet :%p", urb->setup_packet);
+ dbg("start_frame :%d", urb->start_frame);
+ dbg("number_of_packets :%d", urb->number_of_packets);
+ dbg("interval :%d", urb->interval);
+ dbg("error_count :%d", urb->error_count);
+ dbg("context :%p", urb->context);
+ dbg("complete :%p", urb->complete);
+}
+#endif
+/*-------------------------------------------------------------------*/
+static int dabusb_cancel_queue (pdabusb_t s, struct list_head *q)
+{
+ unsigned long flags;
+ pbuff_t b;
+
+ dbg("dabusb_cancel_queue");
+
+ spin_lock_irqsave (&s->lock, flags);
+
+ list_for_each_entry(b, q, buff_list) {
+#ifdef DEBUG
+ dump_urb(b->purb);
+#endif
+ usb_unlink_urb (b->purb);
+ }
+ spin_unlock_irqrestore (&s->lock, flags);
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+static int dabusb_free_queue (struct list_head *q)
+{
+ struct list_head *tmp;
+ struct list_head *p;
+ pbuff_t b;
+
+ dbg("dabusb_free_queue");
+ for (p = q->next; p != q;) {
+ b = list_entry (p, buff_t, buff_list);
+
+#ifdef DEBUG
+ dump_urb(b->purb);
+#endif
+ kfree(b->purb->transfer_buffer);
+ usb_free_urb(b->purb);
+ tmp = p->next;
+ list_del (p);
+ kfree (b);
+ p = tmp;
+ }
+
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+static int dabusb_free_buffers (pdabusb_t s)
+{
+ unsigned long flags;
+ dbg("dabusb_free_buffers");
+
+ spin_lock_irqsave(&s->lock, flags);
+
+ dabusb_free_queue (&s->free_buff_list);
+ dabusb_free_queue (&s->rec_buff_list);
+
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ s->got_mem = 0;
+ return 0;
+}
+/*-------------------------------------------------------------------*/
+static void dabusb_iso_complete (struct urb *purb, struct pt_regs *regs)
+{
+ pbuff_t b = purb->context;
+ pdabusb_t s = b->s;
+ int i;
+ int len;
+ int dst = 0;
+ void *buf = purb->transfer_buffer;
+
+ dbg("dabusb_iso_complete");
+
+ // process if URB was not killed
+ if (purb->status != -ENOENT) {
+ unsigned int pipe = usb_rcvisocpipe (purb->dev, _DABUSB_ISOPIPE);
+ int pipesize = usb_maxpacket (purb->dev, pipe, usb_pipeout (pipe));
+ for (i = 0; i < purb->number_of_packets; i++)
+ if (!purb->iso_frame_desc[i].status) {
+ len = purb->iso_frame_desc[i].actual_length;
+ if (len <= pipesize) {
+ memcpy (buf + dst, buf + purb->iso_frame_desc[i].offset, len);
+ dst += len;
+ }
+ else
+ err("dabusb_iso_complete: invalid len %d", len);
+ }
+ else
+ warn("dabusb_iso_complete: corrupted packet status: %d", purb->iso_frame_desc[i].status);
+ if (dst != purb->actual_length)
+ err("dst!=purb->actual_length:%d!=%d", dst, purb->actual_length);
+ }
+
+ if (atomic_dec_and_test (&s->pending_io) && !s->remove_pending && s->state != _stopped) {
+ s->overruns++;
+ err("overrun (%d)", s->overruns);
+ }
+ wake_up (&s->wait);
+}
+/*-------------------------------------------------------------------*/
+static int dabusb_alloc_buffers (pdabusb_t s)
+{
+ int buffers = 0;
+ pbuff_t b;
+ unsigned int pipe = usb_rcvisocpipe (s->usbdev, _DABUSB_ISOPIPE);
+ int pipesize = usb_maxpacket (s->usbdev, pipe, usb_pipeout (pipe));
+ int packets = _ISOPIPESIZE / pipesize;
+ int transfer_buffer_length = packets * pipesize;
+ int i;
+
+ dbg("dabusb_alloc_buffers pipesize:%d packets:%d transfer_buffer_len:%d",
+ pipesize, packets, transfer_buffer_length);
+
+ while (buffers < (s->total_buffer_size << 10)) {
+ b = (pbuff_t) kzalloc (sizeof (buff_t), GFP_KERNEL);
+ if (!b) {
+ err("kzalloc(sizeof(buff_t))==NULL");
+ goto err;
+ }
+ b->s = s;
+ b->purb = usb_alloc_urb(packets, GFP_KERNEL);
+ if (!b->purb) {
+ err("usb_alloc_urb == NULL");
+ kfree (b);
+ goto err;
+ }
+
+ b->purb->transfer_buffer = kmalloc (transfer_buffer_length, GFP_KERNEL);
+ if (!b->purb->transfer_buffer) {
+ kfree (b->purb);
+ kfree (b);
+ err("kmalloc(%d)==NULL", transfer_buffer_length);
+ goto err;
+ }
+
+ b->purb->transfer_buffer_length = transfer_buffer_length;
+ b->purb->number_of_packets = packets;
+ b->purb->complete = dabusb_iso_complete;
+ b->purb->context = b;
+ b->purb->dev = s->usbdev;
+ b->purb->pipe = pipe;
+ b->purb->transfer_flags = URB_ISO_ASAP;
+
+ for (i = 0; i < packets; i++) {
+ b->purb->iso_frame_desc[i].offset = i * pipesize;
+ b->purb->iso_frame_desc[i].length = pipesize;
+ }
+
+ buffers += transfer_buffer_length;
+ list_add_tail (&b->buff_list, &s->free_buff_list);
+ }
+ s->got_mem = buffers;
+
+ return 0;
+
+ err:
+ dabusb_free_buffers (s);
+ return -ENOMEM;
+}
+/*-------------------------------------------------------------------*/
+static int dabusb_bulk (pdabusb_t s, pbulk_transfer_t pb)
+{
+ int ret;
+ unsigned int pipe;
+ int actual_length;
+
+ dbg("dabusb_bulk");
+
+ if (!pb->pipe)
+ pipe = usb_rcvbulkpipe (s->usbdev, 2);
+ else
+ pipe = usb_sndbulkpipe (s->usbdev, 2);
+
+ ret=usb_bulk_msg(s->usbdev, pipe, pb->data, pb->size, &actual_length, 100);
+ if(ret<0) {
+ err("dabusb: usb_bulk_msg failed(%d)",ret);
+
+ if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) {
+ err("set_interface failed");
+ return -EINVAL;
+ }
+
+ }
+
+ if( ret == -EPIPE ) {
+ warn("CLEAR_FEATURE request to remove STALL condition.");
+ if(usb_clear_halt(s->usbdev, usb_pipeendpoint(pipe)))
+ err("request failed");
+ }
+
+ pb->size = actual_length;
+ return ret;
+}
+/* --------------------------------------------------------------------- */
+static int dabusb_writemem (pdabusb_t s, int pos, unsigned char *data, int len)
+{
+ int ret;
+ unsigned char *transfer_buffer = kmalloc (len, GFP_KERNEL);
+
+ if (!transfer_buffer) {
+ err("dabusb_writemem: kmalloc(%d) failed.", len);
+ return -ENOMEM;
+ }
+
+ memcpy (transfer_buffer, data, len);
+
+ ret=usb_control_msg(s->usbdev, usb_sndctrlpipe( s->usbdev, 0 ), 0xa0, 0x40, pos, 0, transfer_buffer, len, 300);
+
+ kfree (transfer_buffer);
+ return ret;
+}
+/* --------------------------------------------------------------------- */
+static int dabusb_8051_reset (pdabusb_t s, unsigned char reset_bit)
+{
+ dbg("dabusb_8051_reset: %d",reset_bit);
+ return dabusb_writemem (s, CPUCS_REG, &reset_bit, 1);
+}
+/* --------------------------------------------------------------------- */
+static int dabusb_loadmem (pdabusb_t s, const char *fname)
+{
+ int ret;
+ PINTEL_HEX_RECORD ptr = firmware;
+
+ dbg("Enter dabusb_loadmem (internal)");
+
+ ret = dabusb_8051_reset (s, 1);
+ while (ptr->Type == 0) {
+
+ dbg("dabusb_writemem: %04X %p %d)", ptr->Address, ptr->Data, ptr->Length);
+
+ ret = dabusb_writemem (s, ptr->Address, ptr->Data, ptr->Length);
+ if (ret < 0) {
+ err("dabusb_writemem failed (%d %04X %p %d)", ret, ptr->Address, ptr->Data, ptr->Length);
+ break;
+ }
+ ptr++;
+ }
+ ret = dabusb_8051_reset (s, 0);
+
+ dbg("dabusb_loadmem: exit");
+
+ return ret;
+}
+/* --------------------------------------------------------------------- */
+static int dabusb_fpga_clear (pdabusb_t s, pbulk_transfer_t b)
+{
+ b->size = 4;
+ b->data[0] = 0x2a;
+ b->data[1] = 0;
+ b->data[2] = 0;
+ b->data[3] = 0;
+
+ dbg("dabusb_fpga_clear");
+
+ return dabusb_bulk (s, b);
+}
+/* --------------------------------------------------------------------- */
+static int dabusb_fpga_init (pdabusb_t s, pbulk_transfer_t b)
+{
+ b->size = 4;
+ b->data[0] = 0x2c;
+ b->data[1] = 0;
+ b->data[2] = 0;
+ b->data[3] = 0;
+
+ dbg("dabusb_fpga_init");
+
+ return dabusb_bulk (s, b);
+}
+/* --------------------------------------------------------------------- */
+static int dabusb_fpga_download (pdabusb_t s, const char *fname)
+{
+ pbulk_transfer_t b = kmalloc (sizeof (bulk_transfer_t), GFP_KERNEL);
+ unsigned int blen, n;
+ int ret;
+ unsigned char *buf = bitstream;
+
+ dbg("Enter dabusb_fpga_download (internal)");
+
+ if (!b) {
+ err("kmalloc(sizeof(bulk_transfer_t))==NULL");
+ return -ENOMEM;
+ }
+
+ b->pipe = 1;
+ ret = dabusb_fpga_clear (s, b);
+ mdelay (10);
+ blen = buf[73] + (buf[72] << 8);
+
+ dbg("Bitstream len: %i", blen);
+
+ b->data[0] = 0x2b;
+ b->data[1] = 0;
+ b->data[2] = 0;
+ b->data[3] = 60;
+
+ for (n = 0; n <= blen + 60; n += 60) {
+ // some cclks for startup
+ b->size = 64;
+ memcpy (b->data + 4, buf + 74 + n, 60);
+ ret = dabusb_bulk (s, b);
+ if (ret < 0) {
+ err("dabusb_bulk failed.");
+ break;
+ }
+ mdelay (1);
+ }
+
+ ret = dabusb_fpga_init (s, b);
+ kfree (b);
+
+ dbg("exit dabusb_fpga_download");
+
+ return ret;
+}
+
+static int dabusb_stop (pdabusb_t s)
+{
+ dbg("dabusb_stop");
+
+ s->state = _stopped;
+ dabusb_cancel_queue (s, &s->rec_buff_list);
+
+ dbg("pending_io: %d", s->pending_io.counter);
+
+ s->pending_io.counter = 0;
+ return 0;
+}
+
+static int dabusb_startrek (pdabusb_t s)
+{
+ if (!s->got_mem && s->state != _started) {
+
+ dbg("dabusb_startrek");
+
+ if (dabusb_alloc_buffers (s) < 0)
+ return -ENOMEM;
+ dabusb_stop (s);
+ s->state = _started;
+ s->readptr = 0;
+ }
+
+ if (!list_empty (&s->free_buff_list)) {
+ pbuff_t end;
+ int ret;
+
+ while (!dabusb_add_buf_tail (s, &s->rec_buff_list, &s->free_buff_list)) {
+
+ dbg("submitting: end:%p s->rec_buff_list:%p", s->rec_buff_list.prev, &s->rec_buff_list);
+
+ end = list_entry (s->rec_buff_list.prev, buff_t, buff_list);
+
+ ret = usb_submit_urb (end->purb, GFP_KERNEL);
+ if (ret) {
+ err("usb_submit_urb returned:%d", ret);
+ if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list))
+ err("startrek: dabusb_add_buf_tail failed");
+ break;
+ }
+ else
+ atomic_inc (&s->pending_io);
+ }
+ dbg("pending_io: %d",s->pending_io.counter);
+ }
+
+ return 0;
+}
+
+static ssize_t dabusb_read (struct file *file, char __user *buf, size_t count, loff_t * ppos)
+{
+ pdabusb_t s = (pdabusb_t) file->private_data;
+ unsigned long flags;
+ unsigned ret = 0;
+ int rem;
+ int cnt;
+ pbuff_t b;
+ struct urb *purb = NULL;
+
+ dbg("dabusb_read");
+
+ if (*ppos)
+ return -ESPIPE;
+
+ if (s->remove_pending)
+ return -EIO;
+
+
+ if (!s->usbdev)
+ return -EIO;
+
+ while (count > 0) {
+ dabusb_startrek (s);
+
+ spin_lock_irqsave (&s->lock, flags);
+
+ if (list_empty (&s->rec_buff_list)) {
+
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ err("error: rec_buf_list is empty");
+ goto err;
+ }
+
+ b = list_entry (s->rec_buff_list.next, buff_t, buff_list);
+ purb = b->purb;
+
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ if (purb->status == -EINPROGRESS) {
+ if (file->f_flags & O_NONBLOCK) // return nonblocking
+ {
+ if (!ret)
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ interruptible_sleep_on (&s->wait);
+
+ if (signal_pending (current)) {
+ if (!ret)
+ ret = -ERESTARTSYS;
+ goto err;
+ }
+
+ spin_lock_irqsave (&s->lock, flags);
+
+ if (list_empty (&s->rec_buff_list)) {
+ spin_unlock_irqrestore(&s->lock, flags);
+ err("error: still no buffer available.");
+ goto err;
+ }
+ spin_unlock_irqrestore(&s->lock, flags);
+ s->readptr = 0;
+ }
+ if (s->remove_pending) {
+ ret = -EIO;
+ goto err;
+ }
+
+ rem = purb->actual_length - s->readptr; // set remaining bytes to copy
+
+ if (count >= rem)
+ cnt = rem;
+ else
+ cnt = count;
+
+ dbg("copy_to_user:%p %p %d",buf, purb->transfer_buffer + s->readptr, cnt);
+
+ if (copy_to_user (buf, purb->transfer_buffer + s->readptr, cnt)) {
+ err("read: copy_to_user failed");
+ if (!ret)
+ ret = -EFAULT;
+ goto err;
+ }
+
+ s->readptr += cnt;
+ count -= cnt;
+ buf += cnt;
+ ret += cnt;
+
+ if (s->readptr == purb->actual_length) {
+ // finished, take next buffer
+ if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list))
+ err("read: dabusb_add_buf_tail failed");
+ s->readptr = 0;
+ }
+ }
+ err: //mutex_unlock(&s->mutex);
+ return ret;
+}
+
+static int dabusb_open (struct inode *inode, struct file *file)
+{
+ int devnum = iminor(inode);
+ pdabusb_t s;
+
+ if (devnum < DABUSB_MINOR || devnum >= (DABUSB_MINOR + NRDABUSB))
+ return -EIO;
+
+ s = &dabusb[devnum - DABUSB_MINOR];
+
+ dbg("dabusb_open");
+ mutex_lock(&s->mutex);
+
+ while (!s->usbdev || s->opened) {
+ mutex_unlock(&s->mutex);
+
+ if (file->f_flags & O_NONBLOCK) {
+ return -EBUSY;
+ }
+ msleep_interruptible(500);
+
+ if (signal_pending (current)) {
+ return -EAGAIN;
+ }
+ mutex_lock(&s->mutex);
+ }
+ if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) {
+ mutex_unlock(&s->mutex);
+ err("set_interface failed");
+ return -EINVAL;
+ }
+ s->opened = 1;
+ mutex_unlock(&s->mutex);
+
+ file->f_pos = 0;
+ file->private_data = s;
+
+ return nonseekable_open(inode, file);
+}
+
+static int dabusb_release (struct inode *inode, struct file *file)
+{
+ pdabusb_t s = (pdabusb_t) file->private_data;
+
+ dbg("dabusb_release");
+
+ mutex_lock(&s->mutex);
+ dabusb_stop (s);
+ dabusb_free_buffers (s);
+ mutex_unlock(&s->mutex);
+
+ if (!s->remove_pending) {
+ if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0)
+ err("set_interface failed");
+ }
+ else
+ wake_up (&s->remove_ok);
+
+ s->opened = 0;
+ return 0;
+}
+
+static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ pdabusb_t s = (pdabusb_t) file->private_data;
+ pbulk_transfer_t pbulk;
+ int ret = 0;
+ int version = DABUSB_VERSION;
+
+ dbg("dabusb_ioctl");
+
+ if (s->remove_pending)
+ return -EIO;
+
+ mutex_lock(&s->mutex);
+
+ if (!s->usbdev) {
+ mutex_unlock(&s->mutex);
+ return -EIO;
+ }
+
+ switch (cmd) {
+
+ case IOCTL_DAB_BULK:
+ pbulk = (pbulk_transfer_t) kmalloc (sizeof (bulk_transfer_t), GFP_KERNEL);
+
+ if (!pbulk) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ if (copy_from_user (pbulk, (void __user *) arg, sizeof (bulk_transfer_t))) {
+ ret = -EFAULT;
+ kfree (pbulk);
+ break;
+ }
+
+ ret=dabusb_bulk (s, pbulk);
+ if(ret==0)
+ if (copy_to_user((void __user *)arg, pbulk,
+ sizeof(bulk_transfer_t)))
+ ret = -EFAULT;
+ kfree (pbulk);
+ break;
+
+ case IOCTL_DAB_OVERRUNS:
+ ret = put_user (s->overruns, (unsigned int __user *) arg);
+ break;
+
+ case IOCTL_DAB_VERSION:
+ ret = put_user (version, (unsigned int __user *) arg);
+ break;
+
+ default:
+ ret = -ENOIOCTLCMD;
+ break;
+ }
+ mutex_unlock(&s->mutex);
+ return ret;
+}
+
+static struct file_operations dabusb_fops =
+{
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = dabusb_read,
+ .ioctl = dabusb_ioctl,
+ .open = dabusb_open,
+ .release = dabusb_release,
+};
+
+static struct usb_class_driver dabusb_class = {
+ .name = "dabusb%d",
+ .fops = &dabusb_fops,
+ .minor_base = DABUSB_MINOR,
+};
+
+
+/* --------------------------------------------------------------------- */
+static int dabusb_probe (struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+ int retval;
+ pdabusb_t s;
+
+ dbg("dabusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d",
+ le16_to_cpu(usbdev->descriptor.idVendor),
+ le16_to_cpu(usbdev->descriptor.idProduct),
+ intf->altsetting->desc.bInterfaceNumber);
+
+ /* We don't handle multiple configurations */
+ if (usbdev->descriptor.bNumConfigurations != 1)
+ return -ENODEV;
+
+ if (intf->altsetting->desc.bInterfaceNumber != _DABUSB_IF &&
+ le16_to_cpu(usbdev->descriptor.idProduct) == 0x9999)
+ return -ENODEV;
+
+
+
+ s = &dabusb[intf->minor];
+
+ mutex_lock(&s->mutex);
+ s->remove_pending = 0;
+ s->usbdev = usbdev;
+ s->devnum = intf->minor;
+
+ if (usb_reset_configuration (usbdev) < 0) {
+ err("reset_configuration failed");
+ goto reject;
+ }
+ if (le16_to_cpu(usbdev->descriptor.idProduct) == 0x2131) {
+ dabusb_loadmem (s, NULL);
+ goto reject;
+ }
+ else {
+ dabusb_fpga_download (s, NULL);
+
+ if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0) {
+ err("set_interface failed");
+ goto reject;
+ }
+ }
+ dbg("bound to interface: %d", intf->altsetting->desc.bInterfaceNumber);
+ usb_set_intfdata (intf, s);
+ mutex_unlock(&s->mutex);
+
+ retval = usb_register_dev(intf, &dabusb_class);
+ if (retval) {
+ usb_set_intfdata (intf, NULL);
+ return -ENOMEM;
+ }
+
+ return 0;
+
+ reject:
+ mutex_unlock(&s->mutex);
+ s->usbdev = NULL;
+ return -ENODEV;
+}
+
+static void dabusb_disconnect (struct usb_interface *intf)
+{
+ wait_queue_t __wait;
+ pdabusb_t s = usb_get_intfdata (intf);
+
+ dbg("dabusb_disconnect");
+
+ init_waitqueue_entry(&__wait, current);
+
+ usb_set_intfdata (intf, NULL);
+ if (s) {
+ usb_deregister_dev (intf, &dabusb_class);
+ s->remove_pending = 1;
+ wake_up (&s->wait);
+ add_wait_queue(&s->remove_ok, &__wait);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (s->state == _started)
+ schedule();
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&s->remove_ok, &__wait);
+
+ s->usbdev = NULL;
+ s->overruns = 0;
+ }
+}
+
+static struct usb_device_id dabusb_ids [] = {
+ // { USB_DEVICE(0x0547, 0x2131) }, /* An2131 chip, no boot ROM */
+ { USB_DEVICE(0x0547, 0x9999) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, dabusb_ids);
+
+static struct usb_driver dabusb_driver = {
+ .name = "dabusb",
+ .probe = dabusb_probe,
+ .disconnect = dabusb_disconnect,
+ .id_table = dabusb_ids,
+};
+
+/* --------------------------------------------------------------------- */
+
+static int __init dabusb_init (void)
+{
+ int retval;
+ unsigned u;
+
+ /* initialize struct */
+ for (u = 0; u < NRDABUSB; u++) {
+ pdabusb_t s = &dabusb[u];
+ memset (s, 0, sizeof (dabusb_t));
+ mutex_init (&s->mutex);
+ s->usbdev = NULL;
+ s->total_buffer_size = buffers;
+ init_waitqueue_head (&s->wait);
+ init_waitqueue_head (&s->remove_ok);
+ spin_lock_init (&s->lock);
+ INIT_LIST_HEAD (&s->free_buff_list);
+ INIT_LIST_HEAD (&s->rec_buff_list);
+ }
+
+ /* register misc device */
+ retval = usb_register(&dabusb_driver);
+ if (retval)
+ goto out;
+
+ dbg("dabusb_init: driver registered");
+
+ info(DRIVER_VERSION ":" DRIVER_DESC);
+
+out:
+ return retval;
+}
+
+static void __exit dabusb_cleanup (void)
+{
+ dbg("dabusb_cleanup");
+
+ usb_deregister (&dabusb_driver);
+}
+
+/* --------------------------------------------------------------------- */
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
+
+module_param(buffers, int, 0);
+MODULE_PARM_DESC (buffers, "Number of buffers (default=256)");
+
+module_init (dabusb_init);
+module_exit (dabusb_cleanup);
+
+/* --------------------------------------------------------------------- */
diff --git a/linux/drivers/media/video/dabusb.h b/linux/drivers/media/video/dabusb.h
new file mode 100644
index 000000000..00eb34c86
--- /dev/null
+++ b/linux/drivers/media/video/dabusb.h
@@ -0,0 +1,85 @@
+#define _BULK_DATA_LEN 64
+typedef struct
+{
+ unsigned char data[_BULK_DATA_LEN];
+ unsigned int size;
+ unsigned int pipe;
+}bulk_transfer_t,*pbulk_transfer_t;
+
+#define DABUSB_MINOR 240 /* some unassigned USB minor */
+#define DABUSB_VERSION 0x1000
+#define IOCTL_DAB_BULK _IOWR('d', 0x30, bulk_transfer_t)
+#define IOCTL_DAB_OVERRUNS _IOR('d', 0x15, int)
+#define IOCTL_DAB_VERSION _IOR('d', 0x3f, int)
+
+#ifdef __KERNEL__
+
+typedef enum { _stopped=0, _started } driver_state_t;
+
+typedef struct
+{
+ struct mutex mutex;
+ struct usb_device *usbdev;
+ wait_queue_head_t wait;
+ wait_queue_head_t remove_ok;
+ spinlock_t lock;
+ atomic_t pending_io;
+ driver_state_t state;
+ int remove_pending;
+ int got_mem;
+ int total_buffer_size;
+ unsigned int overruns;
+ int readptr;
+ int opened;
+ int devnum;
+ struct list_head free_buff_list;
+ struct list_head rec_buff_list;
+} dabusb_t,*pdabusb_t;
+
+typedef struct
+{
+ pdabusb_t s;
+ struct urb *purb;
+ struct list_head buff_list;
+} buff_t,*pbuff_t;
+
+typedef struct
+{
+ wait_queue_head_t wait;
+} bulk_completion_context_t, *pbulk_completion_context_t;
+
+
+#define _DABUSB_IF 2
+#define _DABUSB_ISOPIPE 0x09
+#define _ISOPIPESIZE 16384
+
+#define _BULK_DATA_LEN 64
+// Vendor specific request code for Anchor Upload/Download
+// This one is implemented in the core
+#define ANCHOR_LOAD_INTERNAL 0xA0
+
+// EZ-USB Control and Status Register. Bit 0 controls 8051 reset
+#define CPUCS_REG 0x7F92
+#define _TOTAL_BUFFERS 384
+
+#define MAX_INTEL_HEX_RECORD_LENGTH 16
+
+#ifndef _BYTE_DEFINED
+#define _BYTE_DEFINED
+typedef unsigned char BYTE;
+#endif // !_BYTE_DEFINED
+
+#ifndef _WORD_DEFINED
+#define _WORD_DEFINED
+typedef unsigned short WORD;
+#endif // !_WORD_DEFINED
+
+typedef struct _INTEL_HEX_RECORD
+{
+ BYTE Length;
+ WORD Address;
+ BYTE Type;
+ BYTE Data[MAX_INTEL_HEX_RECORD_LENGTH];
+} INTEL_HEX_RECORD, *PINTEL_HEX_RECORD;
+
+#endif
diff --git a/linux/drivers/media/video/dsbr100.c b/linux/drivers/media/video/dsbr100.c
new file mode 100644
index 000000000..a4cee9886
--- /dev/null
+++ b/linux/drivers/media/video/dsbr100.c
@@ -0,0 +1,430 @@
+/* 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
+ only deals with initialisation and frequency setting, the
+ audio data has to be handled by a sound driver.
+
+ Major issue: I can't find out where the device reports the signal
+ strength, and indeed the windows software appearantly just looks
+ at the stereo indicator as well. So, scanning will only find
+ stereo stations. Sad, but I can't help it.
+
+ Also, the windows program sends oodles of messages over to the
+ device, and I couldn't figure out their meaning. My suspicion
+ is that they don't have any:-)
+
+ You might find some interesting stuff about this module at
+ http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr
+
+ Copyright (c) 2000 Markus Demleitner <msdemlei@cl.uni-heidelberg.de>
+
+ 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
+
+ History:
+
+ Version 0.40:
+ Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing
+
+ Version 0.30:
+ Markus: Updates for 2.5.x kernel and more ISO compliant source
+
+ Version 0.25:
+ PSL and Markus: Cleanup, radio now doesn't stop on device close
+
+ Version 0.24:
+ Markus: Hope I got these silly VIDEO_TUNER_LOW issues finally
+ right. Some minor cleanup, improved standalone compilation
+
+ Version 0.23:
+ Markus: Sign extension bug fixed by declaring transfer_buffer unsigned
+
+ Version 0.22:
+ Markus: Some (brown bag) cleanup in what VIDIOCSTUNER returns,
+ thanks to Mike Cox for pointing the problem out.
+
+ Version 0.21:
+ Markus: Minor cleanup, warnings if something goes wrong, lame attempt
+ to adhere to Documentation/CodingStyle
+
+ Version 0.2:
+ Brad Hards <bradh@dynamite.com.au>: Fixes to make it work as non-module
+ Markus: Copyright clarification
+
+ Version 0.01: Markus: initial release
+
+*/
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include "compat.h"
+#include <linux/videodev.h>
+#include <linux/usb.h>
+#include <linux/smp_lock.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.40"
+#define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>"
+#define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver"
+
+#define DSB100_VENDOR 0x04b4
+#define DSB100_PRODUCT 0x1002
+
+/* Commands the device appears to understand */
+#define DSB100_TUNE 1
+#define DSB100_ONOFF 2
+
+#define TB_LEN 16
+
+/* Frequency limits in MHz -- these are European values. For Japanese
+devices, that would be 76 and 91. */
+#define FREQ_MIN 87.5
+#define FREQ_MAX 108.0
+#define FREQ_MUL 16000
+
+
+static int usb_dsbr100_probe(struct usb_interface *intf,
+ const struct usb_device_id *id);
+static void usb_dsbr100_disconnect(struct usb_interface *intf);
+static int usb_dsbr100_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+static int usb_dsbr100_open(struct inode *inode, struct file *file);
+static int usb_dsbr100_close(struct inode *inode, struct file *file);
+
+static int radio_nr = -1;
+module_param(radio_nr, int, 0);
+
+/* Data for one (physical) device */
+typedef struct {
+ struct usb_device *usbdev;
+ struct video_device *videodev;
+ unsigned char transfer_buffer[TB_LEN];
+ int curfreq;
+ int stereo;
+ int users;
+ int removed;
+} dsbr100_device;
+
+
+/* File system interface */
+static struct file_operations usb_dsbr100_fops = {
+ .owner = THIS_MODULE,
+ .open = usb_dsbr100_open,
+ .release = usb_dsbr100_close,
+ .ioctl = usb_dsbr100_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+/* V4L interface */
+static struct video_device dsbr100_videodev_template=
+{
+ .owner = THIS_MODULE,
+ .name = "D-Link DSB-R 100",
+ .type = VID_TYPE_TUNER,
+ .hardware = VID_HARDWARE_AZTECH,
+ .fops = &usb_dsbr100_fops,
+ .release = video_device_release,
+};
+
+static struct usb_device_id usb_dsbr100_device_table [] = {
+ { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table);
+
+/* USB subsystem interface */
+static struct usb_driver usb_dsbr100_driver = {
+ .name = "dsbr100",
+ .probe = usb_dsbr100_probe,
+ .disconnect = usb_dsbr100_disconnect,
+ .id_table = usb_dsbr100_device_table,
+};
+
+/* Low-level device interface begins here */
+
+/* switch on radio */
+static int dsbr100_start(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;
+ return (radio->transfer_buffer)[0];
+}
+
+
+/* switch off radio */
+static int dsbr100_stop(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;
+ return (radio->transfer_buffer)[0];
+}
+
+/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
+static int dsbr100_setfreq(dsbr100_device *radio, int freq)
+{
+ 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;
+ }
+ radio->stereo = ! ((radio->transfer_buffer)[0]&0x01);
+ return (radio->transfer_buffer)[0];
+}
+
+/* return the device status. This is, in effect, just whether it
+sees a stereo signal or not. Pity. */
+static void dsbr100_getstat(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 , 0x24, radio->transfer_buffer, 8, 300)<0)
+ radio->stereo = -1;
+ else
+ radio->stereo = ! (radio->transfer_buffer[0]&0x01);
+}
+
+
+/* USB subsystem interface begins here */
+
+/* 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)
+{
+ dsbr100_device *radio;
+
+ if (!(radio = kmalloc(sizeof(dsbr100_device), GFP_KERNEL)))
+ return -ENOMEM;
+ if (!(radio->videodev = video_device_alloc())) {
+ kfree(radio);
+ return -ENOMEM;
+ }
+ memcpy(radio->videodev, &dsbr100_videodev_template,
+ sizeof(dsbr100_videodev_template));
+ 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)) {
+ warn("Could not register video device");
+ video_device_release(radio->videodev);
+ kfree(radio);
+ return -EIO;
+ }
+ usb_set_intfdata(intf, radio);
+ return 0;
+}
+
+/* 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. stv680.c does not relase its private
+data, so I don't do this here either. Checking out the
+code I'd expect I better did that, but if there's a memory
+leak here it's tiny (~50 bytes per disconnect) */
+static void usb_dsbr100_disconnect(struct usb_interface *intf)
+{
+ 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);
+ } else {
+ radio->removed = 1;
+ }
+ }
+}
+
+
+/* Video for Linux interface */
+
+static int usb_dsbr100_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ dsbr100_device *radio=video_get_drvdata(video_devdata(file));
+
+ if (!radio)
+ return -EIO;
+
+ switch(cmd) {
+ case VIDIOCGCAP: {
+ struct video_capability *v = arg;
+
+ memset(v, 0, sizeof(*v));
+ v->type = VID_TYPE_TUNER;
+ v->channels = 1;
+ v->audios = 1;
+ strcpy(v->name, "D-Link R-100 USB FM Radio");
+ return 0;
+ }
+ case VIDIOCGTUNER: {
+ struct video_tuner *v = arg;
+
+ dsbr100_getstat(radio);
+ if(v->tuner) /* Only 1 tuner */
+ return -EINVAL;
+ v->rangelow = FREQ_MIN*FREQ_MUL;
+ v->rangehigh = FREQ_MAX*FREQ_MUL;
+ v->flags = VIDEO_TUNER_LOW;
+ v->mode = VIDEO_MODE_AUTO;
+ v->signal = radio->stereo*0x7000;
+ /* Don't know how to get signal strength */
+ v->flags |= VIDEO_TUNER_STEREO_ON*radio->stereo;
+ strcpy(v->name, "DSB R-100");
+ return 0;
+ }
+ case VIDIOCSTUNER: {
+ struct video_tuner *v = arg;
+
+ if(v->tuner!=0)
+ return -EINVAL;
+ /* Only 1 tuner so no setting needed ! */
+ return 0;
+ }
+ case VIDIOCGFREQ: {
+ int *freq = arg;
+
+ if (radio->curfreq==-1)
+ return -EINVAL;
+ *freq = radio->curfreq;
+ return 0;
+ }
+ case VIDIOCSFREQ: {
+ int *freq = arg;
+
+ radio->curfreq = *freq;
+ if (dsbr100_setfreq(radio, radio->curfreq)==-1)
+ warn("Set frequency failed");
+ return 0;
+ }
+ case VIDIOCGAUDIO: {
+ struct video_audio *v = arg;
+
+ memset(v, 0, sizeof(*v));
+ v->flags |= VIDEO_AUDIO_MUTABLE;
+ v->mode = VIDEO_SOUND_STEREO;
+ v->volume = 1;
+ v->step = 1;
+ strcpy(v->name, "Radio");
+ return 0;
+ }
+ case VIDIOCSAUDIO: {
+ struct video_audio *v = arg;
+
+ if (v->audio)
+ return -EINVAL;
+ if (v->flags&VIDEO_AUDIO_MUTE) {
+ if (dsbr100_stop(radio)==-1)
+ warn("Radio did not respond properly");
+ }
+ else
+ if (dsbr100_start(radio)==-1)
+ warn("Radio did not respond properly");
+ return 0;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int usb_dsbr100_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, usb_dsbr100_do_ioctl);
+}
+
+static int usb_dsbr100_open(struct inode *inode, struct file *file)
+{
+ dsbr100_device *radio=video_get_drvdata(video_devdata(file));
+
+ radio->users = 1;
+ if (dsbr100_start(radio)<0) {
+ warn("Radio did not start up properly");
+ radio->users = 0;
+ return -EIO;
+ }
+ dsbr100_setfreq(radio, radio->curfreq);
+ return 0;
+}
+
+static int usb_dsbr100_close(struct inode *inode, struct file *file)
+{
+ dsbr100_device *radio=video_get_drvdata(video_devdata(file));
+
+ if (!radio)
+ return -ENODEV;
+ radio->users = 0;
+ if (radio->removed) {
+ kfree(radio);
+ }
+ return 0;
+}
+
+static int __init dsbr100_init(void)
+{
+ int retval = usb_register(&usb_dsbr100_driver);
+ info(DRIVER_VERSION ":" DRIVER_DESC);
+ return retval;
+}
+
+static void __exit dsbr100_exit(void)
+{
+ usb_deregister(&usb_dsbr100_driver);
+}
+
+module_init (dsbr100_init);
+module_exit (dsbr100_exit);
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/video/ibmmpeg2.h b/linux/drivers/media/video/ibmmpeg2.h
new file mode 100644
index 000000000..68e10387c
--- /dev/null
+++ b/linux/drivers/media/video/ibmmpeg2.h
@@ -0,0 +1,94 @@
+/* ibmmpeg2.h - IBM MPEGCD21 definitions */
+
+#ifndef __IBM_MPEG2__
+#define __IBM_MPEG2__
+
+/* Define all MPEG Decoder registers */
+/* Chip Control and Status */
+#define IBM_MP2_CHIP_CONTROL 0x200*2
+#define IBM_MP2_CHIP_MODE 0x201*2
+/* Timer Control and Status */
+#define IBM_MP2_SYNC_STC2 0x202*2
+#define IBM_MP2_SYNC_STC1 0x203*2
+#define IBM_MP2_SYNC_STC0 0x204*2
+#define IBM_MP2_SYNC_PTS2 0x205*2
+#define IBM_MP2_SYNC_PTS1 0x206*2
+#define IBM_MP2_SYNC_PTS0 0x207*2
+/* Video FIFO Control */
+#define IBM_MP2_FIFO 0x208*2
+#define IBM_MP2_FIFOW 0x100*2
+#define IBM_MP2_FIFO_STAT 0x209*2
+#define IBM_MP2_RB_THRESHOLD 0x22b*2
+/* Command buffer */
+#define IBM_MP2_COMMAND 0x20a*2
+#define IBM_MP2_CMD_DATA 0x20b*2
+#define IBM_MP2_CMD_STAT 0x20c*2
+#define IBM_MP2_CMD_ADDR 0x20d*2
+/* Internal Processor Control and Status */
+#define IBM_MP2_PROC_IADDR 0x20e*2
+#define IBM_MP2_PROC_IDATA 0x20f*2
+#define IBM_MP2_WR_PROT 0x235*2
+/* DRAM Access */
+#define IBM_MP2_DRAM_ADDR 0x210*2
+#define IBM_MP2_DRAM_DATA 0x212*2
+#define IBM_MP2_DRAM_CMD_STAT 0x213*2
+#define IBM_MP2_BLOCK_SIZE 0x23b*2
+#define IBM_MP2_SRC_ADDR 0x23c*2
+/* Onscreen Display */
+#define IBM_MP2_OSD_ADDR 0x214*2
+#define IBM_MP2_OSD_DATA 0x215*2
+#define IBM_MP2_OSD_MODE 0x217*2
+#define IBM_MP2_OSD_LINK_ADDR 0x229*2
+#define IBM_MP2_OSD_SIZE 0x22a*2
+/* Interrupt Control */
+#define IBM_MP2_HOST_INT 0x218*2
+#define IBM_MP2_MASK0 0x219*2
+#define IBM_MP2_HOST_INT1 0x23e*2
+#define IBM_MP2_MASK1 0x23f*2
+/* Audio Control */
+#define IBM_MP2_AUD_IADDR 0x21a*2
+#define IBM_MP2_AUD_IDATA 0x21b*2
+#define IBM_MP2_AUD_FIFO 0x21c*2
+#define IBM_MP2_AUD_FIFOW 0x101*2
+#define IBM_MP2_AUD_CTL 0x21d*2
+#define IBM_MP2_BEEP_CTL 0x21e*2
+#define IBM_MP2_FRNT_ATTEN 0x22d*2
+/* Display Control */
+#define IBM_MP2_DISP_MODE 0x220*2
+#define IBM_MP2_DISP_DLY 0x221*2
+#define IBM_MP2_VBI_CTL 0x222*2
+#define IBM_MP2_DISP_LBOR 0x223*2
+#define IBM_MP2_DISP_TBOR 0x224*2
+/* Polarity Control */
+#define IBM_MP2_INFC_CTL 0x22c*2
+
+/* control commands */
+#define IBM_MP2_PLAY 0
+#define IBM_MP2_PAUSE 1
+#define IBM_MP2_SINGLE_FRAME 2
+#define IBM_MP2_FAST_FORWARD 3
+#define IBM_MP2_SLOW_MOTION 4
+#define IBM_MP2_IMED_NORM_PLAY 5
+#define IBM_MP2_RESET_WINDOW 6
+#define IBM_MP2_FREEZE_FRAME 7
+#define IBM_MP2_RESET_VID_RATE 8
+#define IBM_MP2_CONFIG_DECODER 9
+#define IBM_MP2_CHANNEL_SWITCH 10
+#define IBM_MP2_RESET_AUD_RATE 11
+#define IBM_MP2_PRE_OP_CHN_SW 12
+#define IBM_MP2_SET_STILL_MODE 14
+
+/* Define Xilinx FPGA Internal Registers */
+
+/* general control register 0 */
+#define XILINX_CTL0 0x600
+/* genlock delay resister 1 */
+#define XILINX_GLDELAY 0x602
+/* send 16 bits to CS3310 port */
+#define XILINX_CS3310 0x604
+/* send 16 bits to CS3310 and complete */
+#define XILINX_CS3310_CMPLT 0x60c
+/* pulse width modulator control */
+#define XILINX_PWM 0x606
+
+#endif
diff --git a/linux/drivers/media/video/indycam.c b/linux/drivers/media/video/indycam.c
new file mode 100644
index 000000000..7420b79e9
--- /dev/null
+++ b/linux/drivers/media/video/indycam.c
@@ -0,0 +1,471 @@
+/*
+ * indycam.c - Silicon Graphics IndyCam digital camera driver
+ *
+ * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <linux/videodev.h>
+/* IndyCam decodes stream of photons into digital image representation ;-) */
+#include <linux/video_decoder.h>
+#include <linux/i2c.h>
+
+#include "indycam.h"
+
+#define INDYCAM_MODULE_VERSION "0.0.5"
+
+MODULE_DESCRIPTION("SGI IndyCam driver");
+MODULE_VERSION(INDYCAM_MODULE_VERSION);
+MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>");
+MODULE_LICENSE("GPL");
+
+// #define INDYCAM_DEBUG
+
+#ifdef INDYCAM_DEBUG
+#define dprintk(x...) printk("IndyCam: " x);
+#define indycam_regdump(client) indycam_regdump_debug(client)
+#else
+#define dprintk(x...)
+#define indycam_regdump(client)
+#endif
+
+struct indycam {
+ struct i2c_client *client;
+ u8 version;
+};
+
+static struct i2c_driver i2c_driver_indycam;
+
+static const u8 initseq[] = {
+ INDYCAM_CONTROL_AGCENA, /* INDYCAM_CONTROL */
+ INDYCAM_SHUTTER_60, /* INDYCAM_SHUTTER */
+ INDYCAM_GAIN_DEFAULT, /* INDYCAM_GAIN */
+ 0x00, /* INDYCAM_BRIGHTNESS (read-only) */
+ INDYCAM_RED_BALANCE_DEFAULT, /* INDYCAM_RED_BALANCE */
+ INDYCAM_BLUE_BALANCE_DEFAULT, /* INDYCAM_BLUE_BALANCE */
+ INDYCAM_RED_SATURATION_DEFAULT, /* INDYCAM_RED_SATURATION */
+ INDYCAM_BLUE_SATURATION_DEFAULT,/* INDYCAM_BLUE_SATURATION */
+};
+
+/* IndyCam register handling */
+
+static int indycam_read_reg(struct i2c_client *client, u8 reg, u8 *value)
+{
+ int ret;
+
+ if (reg == INDYCAM_REG_RESET) {
+ dprintk("indycam_read_reg(): "
+ "skipping write-only register %d\n", reg);
+ *value = 0;
+ return 0;
+ }
+
+ ret = i2c_smbus_read_byte_data(client, reg);
+
+ if (ret < 0) {
+ printk(KERN_ERR "IndyCam: indycam_read_reg(): read failed, "
+ "register = 0x%02x\n", reg);
+ return ret;
+ }
+
+ *value = (u8)ret;
+
+ return 0;
+}
+
+static int indycam_write_reg(struct i2c_client *client, u8 reg, u8 value)
+{
+ int err;
+
+ if ((reg == INDYCAM_REG_BRIGHTNESS)
+ || (reg == INDYCAM_REG_VERSION)) {
+ dprintk("indycam_write_reg(): "
+ "skipping read-only register %d\n", reg);
+ return 0;
+ }
+
+ dprintk("Writing Reg %d = 0x%02x\n", reg, value);
+ err = i2c_smbus_write_byte_data(client, reg, value);
+
+ if (err) {
+ printk(KERN_ERR "IndyCam: indycam_write_reg(): write failed, "
+ "register = 0x%02x, value = 0x%02x\n", reg, value);
+ }
+ return err;
+}
+
+static int indycam_write_block(struct i2c_client *client, u8 reg,
+ u8 length, u8 *data)
+{
+ int i, err;
+
+ for (i = 0; i < length; i++) {
+ err = indycam_write_reg(client, reg + i, data[i]);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/* Helper functions */
+
+#ifdef INDYCAM_DEBUG
+static void indycam_regdump_debug(struct i2c_client *client)
+{
+ int i;
+ u8 val;
+
+ for (i = 0; i < 9; i++) {
+ indycam_read_reg(client, i, &val);
+ dprintk("Reg %d = 0x%02x\n", i, val);
+ }
+}
+#endif
+
+static int indycam_get_control(struct i2c_client *client,
+ struct indycam_control *ctrl)
+{
+ struct indycam *camera = i2c_get_clientdata(client);
+ u8 reg;
+ int ret = 0;
+
+ switch (ctrl->type) {
+ case INDYCAM_CONTROL_AGC:
+ case INDYCAM_CONTROL_AWB:
+ ret = indycam_read_reg(client, INDYCAM_REG_CONTROL, &reg);
+ if (ret)
+ return -EIO;
+ if (ctrl->type == INDYCAM_CONTROL_AGC)
+ ctrl->value = (reg & INDYCAM_CONTROL_AGCENA)
+ ? 1 : 0;
+ else
+ ctrl->value = (reg & INDYCAM_CONTROL_AWBCTL)
+ ? 1 : 0;
+ break;
+ case INDYCAM_CONTROL_SHUTTER:
+ ret = indycam_read_reg(client, INDYCAM_REG_SHUTTER, &reg);
+ if (ret)
+ return -EIO;
+ ctrl->value = ((s32)reg == 0x00) ? 0xff : ((s32)reg - 1);
+ break;
+ case INDYCAM_CONTROL_GAIN:
+ ret = indycam_read_reg(client, INDYCAM_REG_GAIN, &reg);
+ if (ret)
+ return -EIO;
+ ctrl->value = (s32)reg;
+ break;
+ case INDYCAM_CONTROL_RED_BALANCE:
+ ret = indycam_read_reg(client, INDYCAM_REG_RED_BALANCE, &reg);
+ if (ret)
+ return -EIO;
+ ctrl->value = (s32)reg;
+ break;
+ case INDYCAM_CONTROL_BLUE_BALANCE:
+ ret = indycam_read_reg(client, INDYCAM_REG_BLUE_BALANCE, &reg);
+ if (ret)
+ return -EIO;
+ ctrl->value = (s32)reg;
+ break;
+ case INDYCAM_CONTROL_RED_SATURATION:
+ ret = indycam_read_reg(client,
+ INDYCAM_REG_RED_SATURATION, &reg);
+ if (ret)
+ return -EIO;
+ ctrl->value = (s32)reg;
+ break;
+ case INDYCAM_CONTROL_BLUE_SATURATION:
+ ret = indycam_read_reg(client,
+ INDYCAM_REG_BLUE_SATURATION, &reg);
+ if (ret)
+ return -EIO;
+ ctrl->value = (s32)reg;
+ break;
+ case INDYCAM_CONTROL_GAMMA:
+ if (camera->version == CAMERA_VERSION_MOOSE) {
+ ret = indycam_read_reg(client,
+ INDYCAM_REG_GAMMA, &reg);
+ if (ret)
+ return -EIO;
+ ctrl->value = (s32)reg;
+ } else {
+ ctrl->value = INDYCAM_GAMMA_DEFAULT;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int indycam_set_control(struct i2c_client *client,
+ struct indycam_control *ctrl)
+{
+ struct indycam *camera = i2c_get_clientdata(client);
+ u8 reg;
+ int ret = 0;
+
+ switch (ctrl->type) {
+ case INDYCAM_CONTROL_AGC:
+ case INDYCAM_CONTROL_AWB:
+ ret = indycam_read_reg(client, INDYCAM_REG_CONTROL, &reg);
+ if (ret)
+ break;
+
+ if (ctrl->type == INDYCAM_CONTROL_AGC) {
+ if (ctrl->value)
+ reg |= INDYCAM_CONTROL_AGCENA;
+ else
+ reg &= ~INDYCAM_CONTROL_AGCENA;
+ } else {
+ if (ctrl->value)
+ reg |= INDYCAM_CONTROL_AWBCTL;
+ else
+ reg &= ~INDYCAM_CONTROL_AWBCTL;
+ }
+
+ ret = indycam_write_reg(client, INDYCAM_REG_CONTROL, reg);
+ break;
+ case INDYCAM_CONTROL_SHUTTER:
+ reg = (ctrl->value == 0xff) ? 0x00 : (ctrl->value + 1);
+ ret = indycam_write_reg(client, INDYCAM_REG_SHUTTER, reg);
+ break;
+ case INDYCAM_CONTROL_GAIN:
+ ret = indycam_write_reg(client, INDYCAM_REG_GAIN, ctrl->value);
+ break;
+ case INDYCAM_CONTROL_RED_BALANCE:
+ ret = indycam_write_reg(client, INDYCAM_REG_RED_BALANCE,
+ ctrl->value);
+ break;
+ case INDYCAM_CONTROL_BLUE_BALANCE:
+ ret = indycam_write_reg(client, INDYCAM_REG_BLUE_BALANCE,
+ ctrl->value);
+ break;
+ case INDYCAM_CONTROL_RED_SATURATION:
+ ret = indycam_write_reg(client, INDYCAM_REG_RED_SATURATION,
+ ctrl->value);
+ break;
+ case INDYCAM_CONTROL_BLUE_SATURATION:
+ ret = indycam_write_reg(client, INDYCAM_REG_BLUE_SATURATION,
+ ctrl->value);
+ break;
+ case INDYCAM_CONTROL_GAMMA:
+ if (camera->version == CAMERA_VERSION_MOOSE) {
+ ret = indycam_write_reg(client, INDYCAM_REG_GAMMA,
+ ctrl->value);
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/* I2C-interface */
+
+static int indycam_attach(struct i2c_adapter *adap, int addr, int kind)
+{
+ int err = 0;
+ struct indycam *camera;
+ struct i2c_client *client;
+
+ printk(KERN_INFO "SGI IndyCam driver version %s\n",
+ INDYCAM_MODULE_VERSION);
+
+ client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (!client)
+ return -ENOMEM;
+ camera = kzalloc(sizeof(struct indycam), GFP_KERNEL);
+ if (!camera) {
+ err = -ENOMEM;
+ goto out_free_client;
+ }
+
+ client->addr = addr;
+ client->adapter = adap;
+ client->driver = &i2c_driver_indycam;
+ client->flags = 0;
+ strcpy(client->name, "IndyCam client");
+ i2c_set_clientdata(client, camera);
+
+ camera->client = client;
+
+ err = i2c_attach_client(client);
+ if (err)
+ goto out_free_camera;
+
+ camera->version = i2c_smbus_read_byte_data(client,
+ INDYCAM_REG_VERSION);
+ if (camera->version != CAMERA_VERSION_INDY &&
+ camera->version != CAMERA_VERSION_MOOSE) {
+ err = -ENODEV;
+ goto out_detach_client;
+ }
+ printk(KERN_INFO "IndyCam v%d.%d detected\n",
+ INDYCAM_VERSION_MAJOR(camera->version),
+ INDYCAM_VERSION_MINOR(camera->version));
+
+ indycam_regdump(client);
+
+ // initialize
+ err = indycam_write_block(client, 0, sizeof(initseq), (u8 *)&initseq);
+ if (err) {
+ printk(KERN_ERR "IndyCam initalization failed\n");
+ err = -EIO;
+ goto out_detach_client;
+ }
+
+ indycam_regdump(client);
+
+ // white balance
+ err = indycam_write_reg(client, INDYCAM_REG_CONTROL,
+ INDYCAM_CONTROL_AGCENA | INDYCAM_CONTROL_AWBCTL);
+ if (err) {
+ printk(KERN_ERR "IndyCam: White balancing camera failed\n");
+ err = -EIO;
+ goto out_detach_client;
+ }
+
+ indycam_regdump(client);
+
+ printk(KERN_INFO "IndyCam initialized\n");
+
+ return 0;
+
+out_detach_client:
+ i2c_detach_client(client);
+out_free_camera:
+ kfree(camera);
+out_free_client:
+ kfree(client);
+ return err;
+}
+
+static int indycam_probe(struct i2c_adapter *adap)
+{
+ /* Indy specific crap */
+ if (adap->id == I2C_HW_SGI_VINO)
+ return indycam_attach(adap, INDYCAM_ADDR, 0);
+ /* Feel free to add probe here :-) */
+ return -ENODEV;
+}
+
+static int indycam_detach(struct i2c_client *client)
+{
+ struct indycam *camera = i2c_get_clientdata(client);
+
+ i2c_detach_client(client);
+ kfree(camera);
+ kfree(client);
+ return 0;
+}
+
+static int indycam_command(struct i2c_client *client, unsigned int cmd,
+ void *arg)
+{
+ // struct indycam *camera = i2c_get_clientdata(client);
+
+ /* The old video_decoder interface just isn't enough,
+ * so we'll use some custom commands. */
+ switch (cmd) {
+ case DECODER_GET_CAPABILITIES: {
+ struct video_decoder_capability *cap = arg;
+
+ cap->flags = VIDEO_DECODER_NTSC;
+ cap->inputs = 1;
+ cap->outputs = 1;
+ break;
+ }
+ case DECODER_GET_STATUS: {
+ int *iarg = arg;
+
+ *iarg = DECODER_STATUS_GOOD | DECODER_STATUS_NTSC |
+ DECODER_STATUS_COLOR;
+ break;
+ }
+ case DECODER_SET_NORM: {
+ int *iarg = arg;
+
+ switch (*iarg) {
+ case VIDEO_MODE_NTSC:
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ }
+ case DECODER_SET_INPUT: {
+ int *iarg = arg;
+
+ if (*iarg != 0)
+ return -EINVAL;
+ break;
+ }
+ case DECODER_SET_OUTPUT: {
+ int *iarg = arg;
+
+ if (*iarg != 0)
+ return -EINVAL;
+ break;
+ }
+ case DECODER_ENABLE_OUTPUT: {
+ /* Always enabled */
+ break;
+ }
+ case DECODER_SET_PICTURE: {
+ // struct video_picture *pic = arg;
+ /* TODO: convert values for indycam_set_controls() */
+ break;
+ }
+ case DECODER_INDYCAM_GET_CONTROL: {
+ return indycam_get_control(client, arg);
+ }
+ case DECODER_INDYCAM_SET_CONTROL: {
+ return indycam_set_control(client, arg);
+ }
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct i2c_driver i2c_driver_indycam = {
+ .driver = {
+ .name = "indycam",
+ },
+ .id = I2C_DRIVERID_INDYCAM,
+ .attach_adapter = indycam_probe,
+ .detach_client = indycam_detach,
+ .command = indycam_command,
+};
+
+static int __init indycam_init(void)
+{
+ return i2c_add_driver(&i2c_driver_indycam);
+}
+
+static void __exit indycam_exit(void)
+{
+ i2c_del_driver(&i2c_driver_indycam);
+}
+
+module_init(indycam_init);
+module_exit(indycam_exit);
diff --git a/linux/drivers/media/video/indycam.h b/linux/drivers/media/video/indycam.h
new file mode 100644
index 000000000..e6ee82063
--- /dev/null
+++ b/linux/drivers/media/video/indycam.h
@@ -0,0 +1,108 @@
+/*
+ * indycam.h - Silicon Graphics IndyCam digital camera driver
+ *
+ * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _INDYCAM_H_
+#define _INDYCAM_H_
+
+/* I2C address for the Guinness Camera */
+#define INDYCAM_ADDR 0x56
+
+/* Camera version */
+#define CAMERA_VERSION_INDY 0x10 /* v1.0 */
+#define CAMERA_VERSION_MOOSE 0x12 /* v1.2 */
+#define INDYCAM_VERSION_MAJOR(x) (((x) & 0xf0) >> 4)
+#define INDYCAM_VERSION_MINOR(x) ((x) & 0x0f)
+
+/* Register bus addresses */
+#define INDYCAM_REG_CONTROL 0x00
+#define INDYCAM_REG_SHUTTER 0x01
+#define INDYCAM_REG_GAIN 0x02
+#define INDYCAM_REG_BRIGHTNESS 0x03 /* read-only */
+#define INDYCAM_REG_RED_BALANCE 0x04
+#define INDYCAM_REG_BLUE_BALANCE 0x05
+#define INDYCAM_REG_RED_SATURATION 0x06
+#define INDYCAM_REG_BLUE_SATURATION 0x07
+#define INDYCAM_REG_GAMMA 0x08
+#define INDYCAM_REG_VERSION 0x0e /* read-only */
+#define INDYCAM_REG_RESET 0x0f /* write-only */
+
+#define INDYCAM_REG_LED 0x46
+#define INDYCAM_REG_ORIENTATION 0x47
+#define INDYCAM_REG_BUTTON 0x48
+
+/* Field definitions of registers */
+#define INDYCAM_CONTROL_AGCENA (1<<0) /* automatic gain control */
+#define INDYCAM_CONTROL_AWBCTL (1<<1) /* automatic white balance */
+ /* 2-3 are reserved */
+#define INDYCAM_CONTROL_EVNFLD (1<<4) /* read-only */
+
+#define INDYCAM_SHUTTER_10000 0x02 /* 1/10000 second */
+#define INDYCAM_SHUTTER_4000 0x04 /* 1/4000 second */
+#define INDYCAM_SHUTTER_2000 0x08 /* 1/2000 second */
+#define INDYCAM_SHUTTER_1000 0x10 /* 1/1000 second */
+#define INDYCAM_SHUTTER_500 0x20 /* 1/500 second */
+#define INDYCAM_SHUTTER_250 0x3f /* 1/250 second */
+#define INDYCAM_SHUTTER_125 0x7e /* 1/125 second */
+#define INDYCAM_SHUTTER_100 0x9e /* 1/100 second */
+#define INDYCAM_SHUTTER_60 0x00 /* 1/60 second */
+
+#define INDYCAM_LED_ACTIVE 0x10
+#define INDYCAM_LED_INACTIVE 0x30
+#define INDYCAM_ORIENTATION_BOTTOM_TO_TOP 0x40
+#define INDYCAM_BUTTON_RELEASED 0x10
+
+/* Values for controls */
+#define INDYCAM_SHUTTER_MIN 0x00
+#define INDYCAM_SHUTTER_MAX 0xff
+#define INDYCAM_GAIN_MIN 0x00
+#define INDYCAM_GAIN_MAX 0xff
+#define INDYCAM_RED_BALANCE_MIN 0x00
+#define INDYCAM_RED_BALANCE_MAX 0xff
+#define INDYCAM_BLUE_BALANCE_MIN 0x00
+#define INDYCAM_BLUE_BALANCE_MAX 0xff
+#define INDYCAM_RED_SATURATION_MIN 0x00
+#define INDYCAM_RED_SATURATION_MAX 0xff
+#define INDYCAM_BLUE_SATURATION_MIN 0x00
+#define INDYCAM_BLUE_SATURATION_MAX 0xff
+#define INDYCAM_GAMMA_MIN 0x00
+#define INDYCAM_GAMMA_MAX 0xff
+
+#define INDYCAM_AGC_DEFAULT 1
+#define INDYCAM_AWB_DEFAULT 0
+#define INDYCAM_SHUTTER_DEFAULT 0xff
+#define INDYCAM_GAIN_DEFAULT 0x80
+#define INDYCAM_RED_BALANCE_DEFAULT 0x18
+#define INDYCAM_BLUE_BALANCE_DEFAULT 0xa4
+#define INDYCAM_RED_SATURATION_DEFAULT 0x80
+#define INDYCAM_BLUE_SATURATION_DEFAULT 0xc0
+#define INDYCAM_GAMMA_DEFAULT 0x80
+
+/* Driver interface definitions */
+
+#define INDYCAM_CONTROL_AGC 0 /* boolean */
+#define INDYCAM_CONTROL_AWB 1 /* boolean */
+#define INDYCAM_CONTROL_SHUTTER 2
+#define INDYCAM_CONTROL_GAIN 3
+#define INDYCAM_CONTROL_RED_BALANCE 4
+#define INDYCAM_CONTROL_BLUE_BALANCE 5
+#define INDYCAM_CONTROL_RED_SATURATION 6
+#define INDYCAM_CONTROL_BLUE_SATURATION 7
+#define INDYCAM_CONTROL_GAMMA 8
+
+struct indycam_control {
+ u8 type;
+ s32 value;
+};
+
+#define DECODER_INDYCAM_GET_CONTROL _IOR('d', 193, struct indycam_control)
+#define DECODER_INDYCAM_SET_CONTROL _IOW('d', 194, struct indycam_control)
+
+#endif
diff --git a/linux/drivers/media/video/meye.c b/linux/drivers/media/video/meye.c
new file mode 100644
index 000000000..dcd90f56b
--- /dev/null
+++ b/linux/drivers/media/video/meye.c
@@ -0,0 +1,2044 @@
+/*
+ * Motion Eye video4linux driver for Sony Vaio PictureBook
+ *
+ * Copyright (C) 2001-2004 Stelian Pop <stelian@popies.net>
+ *
+ * Copyright (C) 2001-2002 Alcôve <www.alcove.com>
+ *
+ * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com>
+ *
+ * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras.
+ *
+ * Some parts borrowed from various video4linux drivers, especially
+ * bttv-driver.c and zoran.c, see original files for credits.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include "compat.h"
+#include <linux/videodev.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/dma-mapping.h>
+
+#include "meye.h"
+#include <linux/meye.h>
+
+MODULE_AUTHOR("Stelian Pop <stelian@popies.net>");
+MODULE_DESCRIPTION("v4l/v4l2 driver for the MotionEye camera");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(MEYE_DRIVER_VERSION);
+
+/* force usage of V4L1 API */
+static int forcev4l1; /* = 0 */
+module_param(forcev4l1, int, 0644);
+MODULE_PARM_DESC(forcev4l1, "force use of V4L1 instead of V4L2");
+
+/* number of grab buffers */
+static unsigned int gbuffers = 2;
+module_param(gbuffers, int, 0444);
+MODULE_PARM_DESC(gbuffers, "number of capture buffers, default is 2 (32 max)");
+
+/* size of a grab buffer */
+static unsigned int gbufsize = MEYE_MAX_BUFSIZE;
+module_param(gbufsize, int, 0444);
+MODULE_PARM_DESC(gbufsize, "size of the capture buffers, default is 614400"
+ " (will be rounded up to a page multiple)");
+
+/* /dev/videoX registration number */
+static int video_nr = -1;
+module_param(video_nr, int, 0444);
+MODULE_PARM_DESC(video_nr, "video device to register (0=/dev/video0, etc)");
+
+/* driver structure - only one possible */
+static struct meye meye;
+
+/****************************************************************************/
+/* Memory allocation routines (stolen from bttv-driver.c) */
+/****************************************************************************/
+static void *rvmalloc(unsigned long size)
+{
+ void *mem;
+ unsigned long adr;
+
+ size = PAGE_ALIGN(size);
+ mem = vmalloc_32(size);
+ if (mem) {
+ memset(mem, 0, size);
+ adr = (unsigned long) mem;
+ while (size > 0) {
+ SetPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ }
+ return mem;
+}
+
+static void rvfree(void * mem, unsigned long size)
+{
+ unsigned long adr;
+
+ if (mem) {
+ adr = (unsigned long) mem;
+ while ((long) size > 0) {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ vfree(mem);
+ }
+}
+
+/*
+ * return a page table pointing to N pages of locked memory
+ *
+ * NOTE: The meye device expects DMA addresses on 32 bits, we build
+ * a table of 1024 entries = 4 bytes * 1024 = 4096 bytes.
+ */
+static int ptable_alloc(void)
+{
+ u32 *pt;
+ int i;
+
+ memset(meye.mchip_ptable, 0, sizeof(meye.mchip_ptable));
+
+ /* give only 32 bit DMA addresses */
+ if (dma_set_mask(&meye.mchip_dev->dev, DMA_32BIT_MASK))
+ return -1;
+
+ meye.mchip_ptable_toc = dma_alloc_coherent(&meye.mchip_dev->dev,
+ PAGE_SIZE,
+ &meye.mchip_dmahandle,
+ GFP_KERNEL);
+ if (!meye.mchip_ptable_toc) {
+ meye.mchip_dmahandle = 0;
+ return -1;
+ }
+
+ pt = meye.mchip_ptable_toc;
+ for (i = 0; i < MCHIP_NB_PAGES; i++) {
+ dma_addr_t dma;
+ meye.mchip_ptable[i] = dma_alloc_coherent(&meye.mchip_dev->dev,
+ PAGE_SIZE,
+ &dma,
+ GFP_KERNEL);
+ if (!meye.mchip_ptable[i]) {
+ int j;
+ pt = meye.mchip_ptable_toc;
+ for (j = 0; j < i; ++j) {
+ dma = (dma_addr_t) *pt;
+ dma_free_coherent(&meye.mchip_dev->dev,
+ PAGE_SIZE,
+ meye.mchip_ptable[j], dma);
+ pt++;
+ }
+ dma_free_coherent(&meye.mchip_dev->dev,
+ PAGE_SIZE,
+ meye.mchip_ptable_toc,
+ meye.mchip_dmahandle);
+ meye.mchip_ptable_toc = NULL;
+ meye.mchip_dmahandle = 0;
+ return -1;
+ }
+ *pt = (u32) dma;
+ pt++;
+ }
+ return 0;
+}
+
+static void ptable_free(void)
+{
+ u32 *pt;
+ int i;
+
+ pt = meye.mchip_ptable_toc;
+ for (i = 0; i < MCHIP_NB_PAGES; i++) {
+ dma_addr_t dma = (dma_addr_t) *pt;
+ if (meye.mchip_ptable[i])
+ dma_free_coherent(&meye.mchip_dev->dev,
+ PAGE_SIZE,
+ meye.mchip_ptable[i], dma);
+ pt++;
+ }
+
+ if (meye.mchip_ptable_toc)
+ dma_free_coherent(&meye.mchip_dev->dev,
+ PAGE_SIZE,
+ meye.mchip_ptable_toc,
+ meye.mchip_dmahandle);
+
+ memset(meye.mchip_ptable, 0, sizeof(meye.mchip_ptable));
+ meye.mchip_ptable_toc = NULL;
+ meye.mchip_dmahandle = 0;
+}
+
+/* copy data from ptable into buf */
+static void ptable_copy(u8 *buf, int start, int size, int pt_pages)
+{
+ int i;
+
+ for (i = 0; i < (size / PAGE_SIZE) * PAGE_SIZE; i += PAGE_SIZE) {
+ memcpy(buf + i, meye.mchip_ptable[start++], PAGE_SIZE);
+ if (start >= pt_pages)
+ start = 0;
+ }
+ memcpy(buf + i, meye.mchip_ptable[start], size % PAGE_SIZE);
+}
+
+/****************************************************************************/
+/* JPEG tables at different qualities to load into the VRJ chip */
+/****************************************************************************/
+
+/* return a set of quantisation tables based on a quality from 1 to 10 */
+static u16 *jpeg_quantisation_tables(int *length, int quality)
+{
+ static u16 jpeg_tables[][70] = { {
+ 0xdbff, 0x4300, 0xff00, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff,
+ 0xdbff, 0x4300, 0xff01, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff,
+ },
+ {
+ 0xdbff, 0x4300, 0x5000, 0x3c37, 0x3c46, 0x5032, 0x4146, 0x5a46,
+ 0x5055, 0x785f, 0x82c8, 0x6e78, 0x786e, 0xaff5, 0x91b9, 0xffc8,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff,
+ 0xdbff, 0x4300, 0x5501, 0x5a5a, 0x6978, 0xeb78, 0x8282, 0xffeb,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff,
+ },
+ {
+ 0xdbff, 0x4300, 0x2800, 0x1e1c, 0x1e23, 0x2819, 0x2123, 0x2d23,
+ 0x282b, 0x3c30, 0x4164, 0x373c, 0x3c37, 0x587b, 0x495d, 0x9164,
+ 0x9980, 0x8f96, 0x8c80, 0xa08a, 0xe6b4, 0xa0c3, 0xdaaa, 0x8aad,
+ 0xc88c, 0xcbff, 0xeeda, 0xfff5, 0xffff, 0xc19b, 0xffff, 0xfaff,
+ 0xe6ff, 0xfffd, 0xfff8,
+ 0xdbff, 0x4300, 0x2b01, 0x2d2d, 0x353c, 0x763c, 0x4141, 0xf876,
+ 0x8ca5, 0xf8a5, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8,
+ 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8,
+ 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8,
+ 0xf8f8, 0xf8f8, 0xfff8,
+ },
+ {
+ 0xdbff, 0x4300, 0x1b00, 0x1412, 0x1417, 0x1b11, 0x1617, 0x1e17,
+ 0x1b1c, 0x2820, 0x2b42, 0x2528, 0x2825, 0x3a51, 0x303d, 0x6042,
+ 0x6555, 0x5f64, 0x5d55, 0x6a5b, 0x9978, 0x6a81, 0x9071, 0x5b73,
+ 0x855d, 0x86b5, 0x9e90, 0xaba3, 0xabad, 0x8067, 0xc9bc, 0xa6ba,
+ 0x99c7, 0xaba8, 0xffa4,
+ 0xdbff, 0x4300, 0x1c01, 0x1e1e, 0x2328, 0x4e28, 0x2b2b, 0xa44e,
+ 0x5d6e, 0xa46e, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4,
+ 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4,
+ 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4,
+ 0xa4a4, 0xa4a4, 0xffa4,
+ },
+ {
+ 0xdbff, 0x4300, 0x1400, 0x0f0e, 0x0f12, 0x140d, 0x1012, 0x1712,
+ 0x1415, 0x1e18, 0x2132, 0x1c1e, 0x1e1c, 0x2c3d, 0x242e, 0x4932,
+ 0x4c40, 0x474b, 0x4640, 0x5045, 0x735a, 0x5062, 0x6d55, 0x4556,
+ 0x6446, 0x6588, 0x776d, 0x817b, 0x8182, 0x604e, 0x978d, 0x7d8c,
+ 0x7396, 0x817e, 0xff7c,
+ 0xdbff, 0x4300, 0x1501, 0x1717, 0x1a1e, 0x3b1e, 0x2121, 0x7c3b,
+ 0x4653, 0x7c53, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c,
+ 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c,
+ 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c,
+ 0x7c7c, 0x7c7c, 0xff7c,
+ },
+ {
+ 0xdbff, 0x4300, 0x1000, 0x0c0b, 0x0c0e, 0x100a, 0x0d0e, 0x120e,
+ 0x1011, 0x1813, 0x1a28, 0x1618, 0x1816, 0x2331, 0x1d25, 0x3a28,
+ 0x3d33, 0x393c, 0x3833, 0x4037, 0x5c48, 0x404e, 0x5744, 0x3745,
+ 0x5038, 0x516d, 0x5f57, 0x6762, 0x6768, 0x4d3e, 0x7971, 0x6470,
+ 0x5c78, 0x6765, 0xff63,
+ 0xdbff, 0x4300, 0x1101, 0x1212, 0x1518, 0x2f18, 0x1a1a, 0x632f,
+ 0x3842, 0x6342, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363,
+ 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363,
+ 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363,
+ 0x6363, 0x6363, 0xff63,
+ },
+ {
+ 0xdbff, 0x4300, 0x0d00, 0x0a09, 0x0a0b, 0x0d08, 0x0a0b, 0x0e0b,
+ 0x0d0e, 0x130f, 0x1520, 0x1213, 0x1312, 0x1c27, 0x171e, 0x2e20,
+ 0x3129, 0x2e30, 0x2d29, 0x332c, 0x4a3a, 0x333e, 0x4636, 0x2c37,
+ 0x402d, 0x4157, 0x4c46, 0x524e, 0x5253, 0x3e32, 0x615a, 0x505a,
+ 0x4a60, 0x5251, 0xff4f,
+ 0xdbff, 0x4300, 0x0e01, 0x0e0e, 0x1113, 0x2613, 0x1515, 0x4f26,
+ 0x2d35, 0x4f35, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f,
+ 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f,
+ 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f,
+ 0x4f4f, 0x4f4f, 0xff4f,
+ },
+ {
+ 0xdbff, 0x4300, 0x0a00, 0x0707, 0x0708, 0x0a06, 0x0808, 0x0b08,
+ 0x0a0a, 0x0e0b, 0x1018, 0x0d0e, 0x0e0d, 0x151d, 0x1116, 0x2318,
+ 0x251f, 0x2224, 0x221f, 0x2621, 0x372b, 0x262f, 0x3429, 0x2129,
+ 0x3022, 0x3141, 0x3934, 0x3e3b, 0x3e3e, 0x2e25, 0x4944, 0x3c43,
+ 0x3748, 0x3e3d, 0xff3b,
+ 0xdbff, 0x4300, 0x0a01, 0x0b0b, 0x0d0e, 0x1c0e, 0x1010, 0x3b1c,
+ 0x2228, 0x3b28, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b,
+ 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b,
+ 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b,
+ 0x3b3b, 0x3b3b, 0xff3b,
+ },
+ {
+ 0xdbff, 0x4300, 0x0600, 0x0504, 0x0506, 0x0604, 0x0506, 0x0706,
+ 0x0607, 0x0a08, 0x0a10, 0x090a, 0x0a09, 0x0e14, 0x0c0f, 0x1710,
+ 0x1814, 0x1718, 0x1614, 0x1a16, 0x251d, 0x1a1f, 0x231b, 0x161c,
+ 0x2016, 0x202c, 0x2623, 0x2927, 0x292a, 0x1f19, 0x302d, 0x282d,
+ 0x2530, 0x2928, 0xff28,
+ 0xdbff, 0x4300, 0x0701, 0x0707, 0x080a, 0x130a, 0x0a0a, 0x2813,
+ 0x161a, 0x281a, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828,
+ 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828,
+ 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828,
+ 0x2828, 0x2828, 0xff28,
+ },
+ {
+ 0xdbff, 0x4300, 0x0300, 0x0202, 0x0203, 0x0302, 0x0303, 0x0403,
+ 0x0303, 0x0504, 0x0508, 0x0405, 0x0504, 0x070a, 0x0607, 0x0c08,
+ 0x0c0a, 0x0b0c, 0x0b0a, 0x0d0b, 0x120e, 0x0d10, 0x110e, 0x0b0e,
+ 0x100b, 0x1016, 0x1311, 0x1514, 0x1515, 0x0f0c, 0x1817, 0x1416,
+ 0x1218, 0x1514, 0xff14,
+ 0xdbff, 0x4300, 0x0301, 0x0404, 0x0405, 0x0905, 0x0505, 0x1409,
+ 0x0b0d, 0x140d, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414,
+ 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414,
+ 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414,
+ 0x1414, 0x1414, 0xff14,
+ },
+ {
+ 0xdbff, 0x4300, 0x0100, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+ 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+ 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+ 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+ 0x0101, 0x0101, 0xff01,
+ 0xdbff, 0x4300, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+ 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+ 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+ 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101,
+ 0x0101, 0x0101, 0xff01,
+ } };
+
+ if (quality < 0 || quality > 10) {
+ printk(KERN_WARNING
+ "meye: invalid quality level %d - using 8\n", quality);
+ quality = 8;
+ }
+
+ *length = ARRAY_SIZE(jpeg_tables[quality]);
+ return jpeg_tables[quality];
+}
+
+/* return a generic set of huffman tables */
+static u16 *jpeg_huffman_tables(int *length)
+{
+ static u16 tables[] = {
+ 0xC4FF, 0xB500, 0x0010, 0x0102, 0x0303, 0x0402, 0x0503, 0x0405,
+ 0x0004, 0x0100, 0x017D, 0x0302, 0x0400, 0x0511, 0x2112, 0x4131,
+ 0x1306, 0x6151, 0x2207, 0x1471, 0x8132, 0xA191, 0x2308, 0xB142,
+ 0x15C1, 0xD152, 0x24F0, 0x6233, 0x8272, 0x0A09, 0x1716, 0x1918,
+ 0x251A, 0x2726, 0x2928, 0x342A, 0x3635, 0x3837, 0x3A39, 0x4443,
+ 0x4645, 0x4847, 0x4A49, 0x5453, 0x5655, 0x5857, 0x5A59, 0x6463,
+ 0x6665, 0x6867, 0x6A69, 0x7473, 0x7675, 0x7877, 0x7A79, 0x8483,
+ 0x8685, 0x8887, 0x8A89, 0x9392, 0x9594, 0x9796, 0x9998, 0xA29A,
+ 0xA4A3, 0xA6A5, 0xA8A7, 0xAAA9, 0xB3B2, 0xB5B4, 0xB7B6, 0xB9B8,
+ 0xC2BA, 0xC4C3, 0xC6C5, 0xC8C7, 0xCAC9, 0xD3D2, 0xD5D4, 0xD7D6,
+ 0xD9D8, 0xE1DA, 0xE3E2, 0xE5E4, 0xE7E6, 0xE9E8, 0xF1EA, 0xF3F2,
+ 0xF5F4, 0xF7F6, 0xF9F8, 0xFFFA,
+ 0xC4FF, 0xB500, 0x0011, 0x0102, 0x0402, 0x0304, 0x0704, 0x0405,
+ 0x0004, 0x0201, 0x0077, 0x0201, 0x1103, 0x0504, 0x3121, 0x1206,
+ 0x5141, 0x6107, 0x1371, 0x3222, 0x0881, 0x4214, 0xA191, 0xC1B1,
+ 0x2309, 0x5233, 0x15F0, 0x7262, 0x0AD1, 0x2416, 0xE134, 0xF125,
+ 0x1817, 0x1A19, 0x2726, 0x2928, 0x352A, 0x3736, 0x3938, 0x433A,
+ 0x4544, 0x4746, 0x4948, 0x534A, 0x5554, 0x5756, 0x5958, 0x635A,
+ 0x6564, 0x6766, 0x6968, 0x736A, 0x7574, 0x7776, 0x7978, 0x827A,
+ 0x8483, 0x8685, 0x8887, 0x8A89, 0x9392, 0x9594, 0x9796, 0x9998,
+ 0xA29A, 0xA4A3, 0xA6A5, 0xA8A7, 0xAAA9, 0xB3B2, 0xB5B4, 0xB7B6,
+ 0xB9B8, 0xC2BA, 0xC4C3, 0xC6C5, 0xC8C7, 0xCAC9, 0xD3D2, 0xD5D4,
+ 0xD7D6, 0xD9D8, 0xE2DA, 0xE4E3, 0xE6E5, 0xE8E7, 0xEAE9, 0xF3F2,
+ 0xF5F4, 0xF7F6, 0xF9F8, 0xFFFA,
+ 0xC4FF, 0x1F00, 0x0000, 0x0501, 0x0101, 0x0101, 0x0101, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0A09,
+ 0xFF0B,
+ 0xC4FF, 0x1F00, 0x0001, 0x0103, 0x0101, 0x0101, 0x0101, 0x0101,
+ 0x0000, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0A09,
+ 0xFF0B
+ };
+
+ *length = ARRAY_SIZE(tables);
+ return tables;
+}
+
+/****************************************************************************/
+/* MCHIP low-level functions */
+/****************************************************************************/
+
+/* returns the horizontal capture size */
+static inline int mchip_hsize(void)
+{
+ return meye.params.subsample ? 320 : 640;
+}
+
+/* returns the vertical capture size */
+static inline int mchip_vsize(void)
+{
+ return meye.params.subsample ? 240 : 480;
+}
+
+/* waits for a register to be available */
+static void mchip_sync(int reg)
+{
+ u32 status;
+ int i;
+
+ if (reg == MCHIP_MM_FIFO_DATA) {
+ for (i = 0; i < MCHIP_REG_TIMEOUT; i++) {
+ status = readl(meye.mchip_mmregs +
+ MCHIP_MM_FIFO_STATUS);
+ if (!(status & MCHIP_MM_FIFO_WAIT)) {
+ printk(KERN_WARNING "meye: fifo not ready\n");
+ return;
+ }
+ if (status & MCHIP_MM_FIFO_READY)
+ return;
+ udelay(1);
+ }
+ } else if (reg > 0x80) {
+ u32 mask = (reg < 0x100) ? MCHIP_HIC_STATUS_MCC_RDY
+ : MCHIP_HIC_STATUS_VRJ_RDY;
+ for (i = 0; i < MCHIP_REG_TIMEOUT; i++) {
+ status = readl(meye.mchip_mmregs + MCHIP_HIC_STATUS);
+ if (status & mask)
+ return;
+ udelay(1);
+ }
+ } else
+ return;
+ printk(KERN_WARNING
+ "meye: mchip_sync() timeout on reg 0x%x status=0x%x\n",
+ reg, status);
+}
+
+/* sets a value into the register */
+static inline void mchip_set(int reg, u32 v)
+{
+ mchip_sync(reg);
+ writel(v, meye.mchip_mmregs + reg);
+}
+
+/* get the register value */
+static inline u32 mchip_read(int reg)
+{
+ mchip_sync(reg);
+ return readl(meye.mchip_mmregs + reg);
+}
+
+/* wait for a register to become a particular value */
+static inline int mchip_delay(u32 reg, u32 v)
+{
+ int n = 10;
+ while (--n && mchip_read(reg) != v)
+ udelay(1);
+ return n;
+}
+
+/* setup subsampling */
+static void mchip_subsample(void)
+{
+ mchip_set(MCHIP_MCC_R_SAMPLING, meye.params.subsample);
+ mchip_set(MCHIP_MCC_R_XRANGE, mchip_hsize());
+ mchip_set(MCHIP_MCC_R_YRANGE, mchip_vsize());
+ mchip_set(MCHIP_MCC_B_XRANGE, mchip_hsize());
+ mchip_set(MCHIP_MCC_B_YRANGE, mchip_vsize());
+ mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE);
+}
+
+/* set the framerate into the mchip */
+static void mchip_set_framerate(void)
+{
+ mchip_set(MCHIP_HIC_S_RATE, meye.params.framerate);
+}
+
+/* load some huffman and quantisation tables into the VRJ chip ready
+ for JPEG compression */
+static void mchip_load_tables(void)
+{
+ int i;
+ int length;
+ u16 *tables;
+
+ tables = jpeg_huffman_tables(&length);
+ for (i = 0; i < length; i++)
+ writel(tables[i], meye.mchip_mmregs + MCHIP_VRJ_TABLE_DATA);
+
+ tables = jpeg_quantisation_tables(&length, meye.params.quality);
+ for (i = 0; i < length; i++)
+ writel(tables[i], meye.mchip_mmregs + MCHIP_VRJ_TABLE_DATA);
+}
+
+/* setup the VRJ parameters in the chip */
+static void mchip_vrj_setup(u8 mode)
+{
+ mchip_set(MCHIP_VRJ_BUS_MODE, 5);
+ mchip_set(MCHIP_VRJ_SIGNAL_ACTIVE_LEVEL, 0x1f);
+ mchip_set(MCHIP_VRJ_PDAT_USE, 1);
+ mchip_set(MCHIP_VRJ_IRQ_FLAG, 0xa0);
+ mchip_set(MCHIP_VRJ_MODE_SPECIFY, mode);
+ mchip_set(MCHIP_VRJ_NUM_LINES, mchip_vsize());
+ mchip_set(MCHIP_VRJ_NUM_PIXELS, mchip_hsize());
+ mchip_set(MCHIP_VRJ_NUM_COMPONENTS, 0x1b);
+ mchip_set(MCHIP_VRJ_LIMIT_COMPRESSED_LO, 0xFFFF);
+ mchip_set(MCHIP_VRJ_LIMIT_COMPRESSED_HI, 0xFFFF);
+ mchip_set(MCHIP_VRJ_COMP_DATA_FORMAT, 0xC);
+ mchip_set(MCHIP_VRJ_RESTART_INTERVAL, 0);
+ mchip_set(MCHIP_VRJ_SOF1, 0x601);
+ mchip_set(MCHIP_VRJ_SOF2, 0x1502);
+ mchip_set(MCHIP_VRJ_SOF3, 0x1503);
+ mchip_set(MCHIP_VRJ_SOF4, 0x1596);
+ mchip_set(MCHIP_VRJ_SOS, 0x0ed0);
+
+ mchip_load_tables();
+}
+
+/* sets the DMA parameters into the chip */
+static void mchip_dma_setup(dma_addr_t dma_addr)
+{
+ int i;
+
+ mchip_set(MCHIP_MM_PT_ADDR, (u32)dma_addr);
+ for (i = 0; i < 4; i++)
+ mchip_set(MCHIP_MM_FIR(i), 0);
+ meye.mchip_fnum = 0;
+}
+
+/* setup for DMA transfers - also zeros the framebuffer */
+static int mchip_dma_alloc(void)
+{
+ if (!meye.mchip_dmahandle)
+ if (ptable_alloc())
+ return -1;
+ return 0;
+}
+
+/* frees the DMA buffer */
+static void mchip_dma_free(void)
+{
+ if (meye.mchip_dmahandle) {
+ mchip_dma_setup(0);
+ ptable_free();
+ }
+}
+
+/* stop any existing HIC action and wait for any dma to complete then
+ reset the dma engine */
+static void mchip_hic_stop(void)
+{
+ int i, j;
+
+ meye.mchip_mode = MCHIP_HIC_MODE_NOOP;
+ if (!(mchip_read(MCHIP_HIC_STATUS) & MCHIP_HIC_STATUS_BUSY))
+ return;
+ for (i = 0; i < 20; ++i) {
+ mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_STOP);
+ mchip_delay(MCHIP_HIC_CMD, 0);
+ for (j = 0; j < 100; ++j) {
+ if (mchip_delay(MCHIP_HIC_STATUS,
+ MCHIP_HIC_STATUS_IDLE))
+ return;
+ msleep(1);
+ }
+ printk(KERN_ERR "meye: need to reset HIC!\n");
+
+ mchip_set(MCHIP_HIC_CTL, MCHIP_HIC_CTL_SOFT_RESET);
+ msleep(250);
+ }
+ printk(KERN_ERR "meye: resetting HIC hanged!\n");
+}
+
+/****************************************************************************/
+/* MCHIP frame processing functions */
+/****************************************************************************/
+
+/* get the next ready frame from the dma engine */
+static u32 mchip_get_frame(void)
+{
+ u32 v;
+
+ v = mchip_read(MCHIP_MM_FIR(meye.mchip_fnum));
+ return v;
+}
+
+/* frees the current frame from the dma engine */
+static void mchip_free_frame(void)
+{
+ mchip_set(MCHIP_MM_FIR(meye.mchip_fnum), 0);
+ meye.mchip_fnum++;
+ meye.mchip_fnum %= 4;
+}
+
+/* read one frame from the framebuffer assuming it was captured using
+ a uncompressed transfer */
+static void mchip_cont_read_frame(u32 v, u8 *buf, int size)
+{
+ int pt_id;
+
+ pt_id = (v >> 17) & 0x3FF;
+
+ ptable_copy(buf, pt_id, size, MCHIP_NB_PAGES);
+}
+
+/* read a compressed frame from the framebuffer */
+static int mchip_comp_read_frame(u32 v, u8 *buf, int size)
+{
+ int pt_start, pt_end, trailer;
+ int fsize;
+ int i;
+
+ pt_start = (v >> 19) & 0xFF;
+ pt_end = (v >> 11) & 0xFF;
+ trailer = (v >> 1) & 0x3FF;
+
+ if (pt_end < pt_start)
+ fsize = (MCHIP_NB_PAGES_MJPEG - pt_start) * PAGE_SIZE +
+ pt_end * PAGE_SIZE + trailer * 4;
+ else
+ fsize = (pt_end - pt_start) * PAGE_SIZE + trailer * 4;
+
+ if (fsize > size) {
+ printk(KERN_WARNING "meye: oversized compressed frame %d\n",
+ fsize);
+ return -1;
+ }
+
+ ptable_copy(buf, pt_start, fsize, MCHIP_NB_PAGES_MJPEG);
+
+#ifdef MEYE_JPEG_CORRECTION
+
+ /* Some mchip generated jpeg frames are incorrect. In most
+ * (all ?) of those cases, the final EOI (0xff 0xd9) marker
+ * is not present at the end of the frame.
+ *
+ * Since adding the final marker is not enough to restore
+ * the jpeg integrity, we drop the frame.
+ */
+
+ for (i = fsize - 1; i > 0 && buf[i] == 0xff; i--) ;
+
+ if (i < 2 || buf[i - 1] != 0xff || buf[i] != 0xd9)
+ return -1;
+
+#endif
+
+ return fsize;
+}
+
+/* take a picture into SDRAM */
+static void mchip_take_picture(void)
+{
+ int i;
+
+ mchip_hic_stop();
+ mchip_subsample();
+ mchip_dma_setup(meye.mchip_dmahandle);
+
+ mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_CAP);
+ mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+ mchip_delay(MCHIP_HIC_CMD, 0);
+
+ for (i = 0; i < 100; ++i) {
+ if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE))
+ break;
+ msleep(1);
+ }
+}
+
+/* dma a previously taken picture into a buffer */
+static void mchip_get_picture(u8 *buf, int bufsize)
+{
+ u32 v;
+ int i;
+
+ mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_OUT);
+ mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+ mchip_delay(MCHIP_HIC_CMD, 0);
+ for (i = 0; i < 100; ++i) {
+ if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE))
+ break;
+ msleep(1);
+ }
+ for (i = 0; i < 4; ++i) {
+ v = mchip_get_frame();
+ if (v & MCHIP_MM_FIR_RDY) {
+ mchip_cont_read_frame(v, buf, bufsize);
+ break;
+ }
+ mchip_free_frame();
+ }
+}
+
+/* start continuous dma capture */
+static void mchip_continuous_start(void)
+{
+ mchip_hic_stop();
+ mchip_subsample();
+ mchip_set_framerate();
+ mchip_dma_setup(meye.mchip_dmahandle);
+
+ meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT;
+
+ mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_CONT_OUT);
+ mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+ mchip_delay(MCHIP_HIC_CMD, 0);
+}
+
+/* compress one frame into a buffer */
+static int mchip_compress_frame(u8 *buf, int bufsize)
+{
+ u32 v;
+ int len = -1, i;
+
+ mchip_vrj_setup(0x3f);
+ udelay(50);
+
+ mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_COMP);
+ mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+ mchip_delay(MCHIP_HIC_CMD, 0);
+ for (i = 0; i < 100; ++i) {
+ if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE))
+ break;
+ msleep(1);
+ }
+
+ for (i = 0; i < 4; ++i) {
+ v = mchip_get_frame();
+ if (v & MCHIP_MM_FIR_RDY) {
+ len = mchip_comp_read_frame(v, buf, bufsize);
+ break;
+ }
+ mchip_free_frame();
+ }
+ return len;
+}
+
+#if 0
+/* uncompress one image into a buffer */
+static int mchip_uncompress_frame(u8 *img, int imgsize, u8 *buf, int bufsize)
+{
+ mchip_vrj_setup(0x3f);
+ udelay(50);
+
+ mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_DECOMP);
+ mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+ mchip_delay(MCHIP_HIC_CMD, 0);
+
+ return mchip_comp_read_frame(buf, bufsize);
+}
+#endif
+
+/* start continuous compressed capture */
+static void mchip_cont_compression_start(void)
+{
+ mchip_hic_stop();
+ mchip_vrj_setup(0x3f);
+ mchip_subsample();
+ mchip_set_framerate();
+ mchip_dma_setup(meye.mchip_dmahandle);
+
+ meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP;
+
+ mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_CONT_COMP);
+ mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+ mchip_delay(MCHIP_HIC_CMD, 0);
+}
+
+/****************************************************************************/
+/* Interrupt handling */
+/****************************************************************************/
+
+static irqreturn_t meye_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+ u32 v;
+ int reqnr;
+ static int sequence = 0;
+
+ v = mchip_read(MCHIP_MM_INTA);
+
+ if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_OUT &&
+ meye.mchip_mode != MCHIP_HIC_MODE_CONT_COMP)
+ return IRQ_NONE;
+
+again:
+ v = mchip_get_frame();
+ if (!(v & MCHIP_MM_FIR_RDY))
+ return IRQ_HANDLED;
+
+ if (meye.mchip_mode == MCHIP_HIC_MODE_CONT_OUT) {
+ if (kfifo_get(meye.grabq, (unsigned char *)&reqnr,
+ sizeof(int)) != sizeof(int)) {
+ mchip_free_frame();
+ return IRQ_HANDLED;
+ }
+ mchip_cont_read_frame(v, meye.grab_fbuffer + gbufsize * reqnr,
+ mchip_hsize() * mchip_vsize() * 2);
+ meye.grab_buffer[reqnr].size = mchip_hsize() * mchip_vsize() * 2;
+ meye.grab_buffer[reqnr].state = MEYE_BUF_DONE;
+ do_gettimeofday(&meye.grab_buffer[reqnr].timestamp);
+ meye.grab_buffer[reqnr].sequence = sequence++;
+ kfifo_put(meye.doneq, (unsigned char *)&reqnr, sizeof(int));
+ wake_up_interruptible(&meye.proc_list);
+ } else {
+ int size;
+ size = mchip_comp_read_frame(v, meye.grab_temp, gbufsize);
+ if (size == -1) {
+ mchip_free_frame();
+ goto again;
+ }
+ if (kfifo_get(meye.grabq, (unsigned char *)&reqnr,
+ sizeof(int)) != sizeof(int)) {
+ mchip_free_frame();
+ goto again;
+ }
+ memcpy(meye.grab_fbuffer + gbufsize * reqnr, meye.grab_temp,
+ size);
+ meye.grab_buffer[reqnr].size = size;
+ meye.grab_buffer[reqnr].state = MEYE_BUF_DONE;
+ do_gettimeofday(&meye.grab_buffer[reqnr].timestamp);
+ meye.grab_buffer[reqnr].sequence = sequence++;
+ kfifo_put(meye.doneq, (unsigned char *)&reqnr, sizeof(int));
+ wake_up_interruptible(&meye.proc_list);
+ }
+ mchip_free_frame();
+ goto again;
+}
+
+/****************************************************************************/
+/* video4linux integration */
+/****************************************************************************/
+
+static int meye_open(struct inode *inode, struct file *file)
+{
+ int i, err;
+
+ err = video_exclusive_open(inode, file);
+ if (err < 0)
+ return err;
+
+ mchip_hic_stop();
+
+ if (mchip_dma_alloc()) {
+ printk(KERN_ERR "meye: mchip framebuffer allocation failed\n");
+ video_exclusive_release(inode, file);
+ return -ENOBUFS;
+ }
+
+ for (i = 0; i < MEYE_MAX_BUFNBRS; i++)
+ meye.grab_buffer[i].state = MEYE_BUF_UNUSED;
+ kfifo_reset(meye.grabq);
+ kfifo_reset(meye.doneq);
+ return 0;
+}
+
+static int meye_release(struct inode *inode, struct file *file)
+{
+ mchip_hic_stop();
+ mchip_dma_free();
+ video_exclusive_release(inode, file);
+ return 0;
+}
+
+static int meye_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ switch (cmd) {
+
+ case VIDIOCGCAP: {
+ struct video_capability *b = arg;
+ strcpy(b->name,meye.video_dev->name);
+ b->type = VID_TYPE_CAPTURE;
+ b->channels = 1;
+ b->audios = 0;
+ b->maxwidth = 640;
+ b->maxheight = 480;
+ b->minwidth = 320;
+ b->minheight = 240;
+ break;
+ }
+
+ case VIDIOCGCHAN: {
+ struct video_channel *v = arg;
+ v->flags = 0;
+ v->tuners = 0;
+ v->type = VIDEO_TYPE_CAMERA;
+ if (v->channel != 0)
+ return -EINVAL;
+ strcpy(v->name,"Camera");
+ break;
+ }
+
+ case VIDIOCSCHAN: {
+ struct video_channel *v = arg;
+ if (v->channel != 0)
+ return -EINVAL;
+ break;
+ }
+
+ case VIDIOCGPICT: {
+ struct video_picture *p = arg;
+ *p = meye.picture;
+ break;
+ }
+
+ case VIDIOCSPICT: {
+ struct video_picture *p = arg;
+ if (p->depth != 16)
+ return -EINVAL;
+ if (p->palette != VIDEO_PALETTE_YUV422)
+ return -EINVAL;
+ mutex_lock(&meye.lock);
+ sonypi_camera_command(SONYPI_COMMAND_SETCAMERABRIGHTNESS,
+ p->brightness >> 10);
+ sonypi_camera_command(SONYPI_COMMAND_SETCAMERAHUE,
+ p->hue >> 10);
+ sonypi_camera_command(SONYPI_COMMAND_SETCAMERACOLOR,
+ p->colour >> 10);
+ sonypi_camera_command(SONYPI_COMMAND_SETCAMERACONTRAST,
+ p->contrast >> 10);
+ meye.picture = *p;
+ mutex_unlock(&meye.lock);
+ break;
+ }
+
+ case VIDIOCSYNC: {
+ int *i = arg;
+ int unused;
+
+ if (*i < 0 || *i >= gbuffers)
+ return -EINVAL;
+
+ mutex_lock(&meye.lock);
+
+ switch (meye.grab_buffer[*i].state) {
+
+ case MEYE_BUF_UNUSED:
+ mutex_unlock(&meye.lock);
+ return -EINVAL;
+ case MEYE_BUF_USING:
+ if (file->f_flags & O_NONBLOCK) {
+ mutex_unlock(&meye.lock);
+ return -EAGAIN;
+ }
+ if (wait_event_interruptible(meye.proc_list,
+ (meye.grab_buffer[*i].state != MEYE_BUF_USING))) {
+ mutex_unlock(&meye.lock);
+ return -EINTR;
+ }
+ /* fall through */
+ case MEYE_BUF_DONE:
+ meye.grab_buffer[*i].state = MEYE_BUF_UNUSED;
+ kfifo_get(meye.doneq, (unsigned char *)&unused, sizeof(int));
+ }
+ mutex_unlock(&meye.lock);
+ break;
+ }
+
+ case VIDIOCMCAPTURE: {
+ struct video_mmap *vm = arg;
+ int restart = 0;
+
+ if (vm->frame >= gbuffers || vm->frame < 0)
+ return -EINVAL;
+ if (vm->format != VIDEO_PALETTE_YUV422)
+ return -EINVAL;
+ if (vm->height * vm->width * 2 > gbufsize)
+ return -EINVAL;
+ if (!meye.grab_fbuffer)
+ return -EINVAL;
+ if (meye.grab_buffer[vm->frame].state != MEYE_BUF_UNUSED)
+ return -EBUSY;
+
+ mutex_lock(&meye.lock);
+ if (vm->width == 640 && vm->height == 480) {
+ if (meye.params.subsample) {
+ meye.params.subsample = 0;
+ restart = 1;
+ }
+ } else if (vm->width == 320 && vm->height == 240) {
+ if (!meye.params.subsample) {
+ meye.params.subsample = 1;
+ restart = 1;
+ }
+ } else {
+ mutex_unlock(&meye.lock);
+ return -EINVAL;
+ }
+
+ if (restart || meye.mchip_mode != MCHIP_HIC_MODE_CONT_OUT)
+ mchip_continuous_start();
+ meye.grab_buffer[vm->frame].state = MEYE_BUF_USING;
+ kfifo_put(meye.grabq, (unsigned char *)&vm->frame, sizeof(int));
+ mutex_unlock(&meye.lock);
+ break;
+ }
+
+ case VIDIOCGMBUF: {
+ struct video_mbuf *vm = arg;
+ int i;
+
+ memset(vm, 0 , sizeof(*vm));
+ vm->size = gbufsize * gbuffers;
+ vm->frames = gbuffers;
+ for (i = 0; i < gbuffers; i++)
+ vm->offsets[i] = i * gbufsize;
+ break;
+ }
+
+ case MEYEIOC_G_PARAMS: {
+ struct meye_params *p = arg;
+ *p = meye.params;
+ break;
+ }
+
+ case MEYEIOC_S_PARAMS: {
+ struct meye_params *jp = arg;
+ if (jp->subsample > 1)
+ return -EINVAL;
+ if (jp->quality > 10)
+ return -EINVAL;
+ if (jp->sharpness > 63 || jp->agc > 63 || jp->picture > 63)
+ return -EINVAL;
+ if (jp->framerate > 31)
+ return -EINVAL;
+ mutex_lock(&meye.lock);
+ if (meye.params.subsample != jp->subsample ||
+ meye.params.quality != jp->quality)
+ mchip_hic_stop(); /* need restart */
+ meye.params = *jp;
+ sonypi_camera_command(SONYPI_COMMAND_SETCAMERASHARPNESS,
+ meye.params.sharpness);
+ sonypi_camera_command(SONYPI_COMMAND_SETCAMERAAGC,
+ meye.params.agc);
+ sonypi_camera_command(SONYPI_COMMAND_SETCAMERAPICTURE,
+ meye.params.picture);
+ mutex_unlock(&meye.lock);
+ break;
+ }
+
+ case MEYEIOC_QBUF_CAPT: {
+ int *nb = arg;
+
+ if (!meye.grab_fbuffer)
+ return -EINVAL;
+ if (*nb >= gbuffers)
+ return -EINVAL;
+ if (*nb < 0) {
+ /* stop capture */
+ mchip_hic_stop();
+ return 0;
+ }
+ if (meye.grab_buffer[*nb].state != MEYE_BUF_UNUSED)
+ return -EBUSY;
+ mutex_lock(&meye.lock);
+ if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_COMP)
+ mchip_cont_compression_start();
+ meye.grab_buffer[*nb].state = MEYE_BUF_USING;
+ kfifo_put(meye.grabq, (unsigned char *)nb, sizeof(int));
+ mutex_unlock(&meye.lock);
+ break;
+ }
+
+ case MEYEIOC_SYNC: {
+ int *i = arg;
+ int unused;
+
+ if (*i < 0 || *i >= gbuffers)
+ return -EINVAL;
+
+ mutex_lock(&meye.lock);
+ switch (meye.grab_buffer[*i].state) {
+
+ case MEYE_BUF_UNUSED:
+ mutex_unlock(&meye.lock);
+ return -EINVAL;
+ case MEYE_BUF_USING:
+ if (file->f_flags & O_NONBLOCK) {
+ mutex_unlock(&meye.lock);
+ return -EAGAIN;
+ }
+ if (wait_event_interruptible(meye.proc_list,
+ (meye.grab_buffer[*i].state != MEYE_BUF_USING))) {
+ mutex_unlock(&meye.lock);
+ return -EINTR;
+ }
+ /* fall through */
+ case MEYE_BUF_DONE:
+ meye.grab_buffer[*i].state = MEYE_BUF_UNUSED;
+ kfifo_get(meye.doneq, (unsigned char *)&unused, sizeof(int));
+ }
+ *i = meye.grab_buffer[*i].size;
+ mutex_unlock(&meye.lock);
+ break;
+ }
+
+ case MEYEIOC_STILLCAPT: {
+
+ if (!meye.grab_fbuffer)
+ return -EINVAL;
+ if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED)
+ return -EBUSY;
+ mutex_lock(&meye.lock);
+ meye.grab_buffer[0].state = MEYE_BUF_USING;
+ mchip_take_picture();
+ mchip_get_picture(
+ meye.grab_fbuffer,
+ mchip_hsize() * mchip_vsize() * 2);
+ meye.grab_buffer[0].state = MEYE_BUF_DONE;
+ mutex_unlock(&meye.lock);
+ break;
+ }
+
+ case MEYEIOC_STILLJCAPT: {
+ int *len = arg;
+
+ if (!meye.grab_fbuffer)
+ return -EINVAL;
+ if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED)
+ return -EBUSY;
+ mutex_lock(&meye.lock);
+ meye.grab_buffer[0].state = MEYE_BUF_USING;
+ *len = -1;
+ while (*len == -1) {
+ mchip_take_picture();
+ *len = mchip_compress_frame(meye.grab_fbuffer, gbufsize);
+ }
+ meye.grab_buffer[0].state = MEYE_BUF_DONE;
+ mutex_unlock(&meye.lock);
+ break;
+ }
+
+ case VIDIOC_QUERYCAP: {
+ struct v4l2_capability *cap = arg;
+
+ if (forcev4l1)
+ return -EINVAL;
+
+ memset(cap, 0, sizeof(*cap));
+ strcpy(cap->driver, "meye");
+ strcpy(cap->card, "meye");
+ sprintf(cap->bus_info, "PCI:%s", pci_name(meye.mchip_dev));
+ cap->version = (MEYE_DRIVER_MAJORVERSION << 8) +
+ MEYE_DRIVER_MINORVERSION;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING;
+ break;
+ }
+
+ case VIDIOC_ENUMINPUT: {
+ struct v4l2_input *i = arg;
+
+ if (i->index != 0)
+ return -EINVAL;
+ memset(i, 0, sizeof(*i));
+ i->index = 0;
+ strcpy(i->name, "Camera");
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ break;
+ }
+
+ case VIDIOC_G_INPUT: {
+ int *i = arg;
+
+ *i = 0;
+ break;
+ }
+
+ case VIDIOC_S_INPUT: {
+ int *i = arg;
+
+ if (*i != 0)
+ return -EINVAL;
+ break;
+ }
+
+ case VIDIOC_QUERYCTRL: {
+ struct v4l2_queryctrl *c = arg;
+
+ switch (c->id) {
+
+ case V4L2_CID_BRIGHTNESS:
+ c->type = V4L2_CTRL_TYPE_INTEGER;
+ strcpy(c->name, "Brightness");
+ c->minimum = 0;
+ c->maximum = 63;
+ c->step = 1;
+ c->default_value = 32;
+ c->flags = 0;
+ break;
+ case V4L2_CID_HUE:
+ c->type = V4L2_CTRL_TYPE_INTEGER;
+ strcpy(c->name, "Hue");
+ c->minimum = 0;
+ c->maximum = 63;
+ c->step = 1;
+ c->default_value = 32;
+ c->flags = 0;
+ break;
+ case V4L2_CID_CONTRAST:
+ c->type = V4L2_CTRL_TYPE_INTEGER;
+ strcpy(c->name, "Contrast");
+ c->minimum = 0;
+ c->maximum = 63;
+ c->step = 1;
+ c->default_value = 32;
+ c->flags = 0;
+ break;
+ case V4L2_CID_SATURATION:
+ c->type = V4L2_CTRL_TYPE_INTEGER;
+ strcpy(c->name, "Saturation");
+ c->minimum = 0;
+ c->maximum = 63;
+ c->step = 1;
+ c->default_value = 32;
+ c->flags = 0;
+ break;
+ case V4L2_CID_AGC:
+ c->type = V4L2_CTRL_TYPE_INTEGER;
+ strcpy(c->name, "Agc");
+ c->minimum = 0;
+ c->maximum = 63;
+ c->step = 1;
+ c->default_value = 48;
+ c->flags = 0;
+ break;
+ case V4L2_CID_SHARPNESS:
+ c->type = V4L2_CTRL_TYPE_INTEGER;
+ strcpy(c->name, "Sharpness");
+ c->minimum = 0;
+ c->maximum = 63;
+ c->step = 1;
+ c->default_value = 32;
+ c->flags = 0;
+ break;
+ case V4L2_CID_PICTURE:
+ c->type = V4L2_CTRL_TYPE_INTEGER;
+ strcpy(c->name, "Picture");
+ c->minimum = 0;
+ c->maximum = 63;
+ c->step = 1;
+ c->default_value = 0;
+ c->flags = 0;
+ break;
+ case V4L2_CID_JPEGQUAL:
+ c->type = V4L2_CTRL_TYPE_INTEGER;
+ strcpy(c->name, "JPEG quality");
+ c->minimum = 0;
+ c->maximum = 10;
+ c->step = 1;
+ c->default_value = 8;
+ c->flags = 0;
+ break;
+ case V4L2_CID_FRAMERATE:
+ c->type = V4L2_CTRL_TYPE_INTEGER;
+ strcpy(c->name, "Framerate");
+ c->minimum = 0;
+ c->maximum = 31;
+ c->step = 1;
+ c->default_value = 0;
+ c->flags = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ }
+
+ case VIDIOC_S_CTRL: {
+ struct v4l2_control *c = arg;
+
+ mutex_lock(&meye.lock);
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ sonypi_camera_command(
+ SONYPI_COMMAND_SETCAMERABRIGHTNESS, c->value);
+ meye.picture.brightness = c->value << 10;
+ break;
+ case V4L2_CID_HUE:
+ sonypi_camera_command(
+ SONYPI_COMMAND_SETCAMERAHUE, c->value);
+ meye.picture.hue = c->value << 10;
+ break;
+ case V4L2_CID_CONTRAST:
+ sonypi_camera_command(
+ SONYPI_COMMAND_SETCAMERACONTRAST, c->value);
+ meye.picture.contrast = c->value << 10;
+ break;
+ case V4L2_CID_SATURATION:
+ sonypi_camera_command(
+ SONYPI_COMMAND_SETCAMERACOLOR, c->value);
+ meye.picture.colour = c->value << 10;
+ break;
+ case V4L2_CID_AGC:
+ sonypi_camera_command(
+ SONYPI_COMMAND_SETCAMERAAGC, c->value);
+ meye.params.agc = c->value;
+ break;
+ case V4L2_CID_SHARPNESS:
+ sonypi_camera_command(
+ SONYPI_COMMAND_SETCAMERASHARPNESS, c->value);
+ meye.params.sharpness = c->value;
+ break;
+ case V4L2_CID_PICTURE:
+ sonypi_camera_command(
+ SONYPI_COMMAND_SETCAMERAPICTURE, c->value);
+ meye.params.picture = c->value;
+ break;
+ case V4L2_CID_JPEGQUAL:
+ meye.params.quality = c->value;
+ break;
+ case V4L2_CID_FRAMERATE:
+ meye.params.framerate = c->value;
+ break;
+ default:
+ mutex_unlock(&meye.lock);
+ return -EINVAL;
+ }
+ mutex_unlock(&meye.lock);
+ break;
+ }
+
+ case VIDIOC_G_CTRL: {
+ struct v4l2_control *c = arg;
+
+ mutex_lock(&meye.lock);
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ c->value = meye.picture.brightness >> 10;
+ break;
+ case V4L2_CID_HUE:
+ c->value = meye.picture.hue >> 10;
+ break;
+ case V4L2_CID_CONTRAST:
+ c->value = meye.picture.contrast >> 10;
+ break;
+ case V4L2_CID_SATURATION:
+ c->value = meye.picture.colour >> 10;
+ break;
+ case V4L2_CID_AGC:
+ c->value = meye.params.agc;
+ break;
+ case V4L2_CID_SHARPNESS:
+ c->value = meye.params.sharpness;
+ break;
+ case V4L2_CID_PICTURE:
+ c->value = meye.params.picture;
+ break;
+ case V4L2_CID_JPEGQUAL:
+ c->value = meye.params.quality;
+ break;
+ case V4L2_CID_FRAMERATE:
+ c->value = meye.params.framerate;
+ break;
+ default:
+ mutex_unlock(&meye.lock);
+ return -EINVAL;
+ }
+ mutex_unlock(&meye.lock);
+ break;
+ }
+
+ case VIDIOC_ENUM_FMT: {
+ struct v4l2_fmtdesc *f = arg;
+
+ if (f->index > 1)
+ return -EINVAL;
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (f->index == 0) {
+ /* standard YUV 422 capture */
+ memset(f, 0, sizeof(*f));
+ f->index = 0;
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->flags = 0;
+ strcpy(f->description, "YUV422");
+ f->pixelformat = V4L2_PIX_FMT_YUYV;
+ } else {
+ /* compressed MJPEG capture */
+ memset(f, 0, sizeof(*f));
+ f->index = 1;
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->flags = V4L2_FMT_FLAG_COMPRESSED;
+ strcpy(f->description, "MJPEG");
+ f->pixelformat = V4L2_PIX_FMT_MJPEG;
+ }
+ break;
+ }
+
+ case VIDIOC_TRY_FMT: {
+ struct v4l2_format *f = arg;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV &&
+ f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
+ return -EINVAL;
+ if (f->fmt.pix.field != V4L2_FIELD_ANY &&
+ f->fmt.pix.field != V4L2_FIELD_NONE)
+ return -EINVAL;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ if (f->fmt.pix.width <= 320) {
+ f->fmt.pix.width = 320;
+ f->fmt.pix.height = 240;
+ } else {
+ f->fmt.pix.width = 640;
+ f->fmt.pix.height = 480;
+ }
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+ f->fmt.pix.sizeimage = f->fmt.pix.height *
+ f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.priv = 0;
+ break;
+ }
+
+ case VIDIOC_G_FMT: {
+ struct v4l2_format *f = arg;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format));
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ switch (meye.mchip_mode) {
+ case MCHIP_HIC_MODE_CONT_OUT:
+ default:
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+ break;
+ case MCHIP_HIC_MODE_CONT_COMP:
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
+ break;
+ }
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.width = mchip_hsize();
+ f->fmt.pix.height = mchip_vsize();
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+ f->fmt.pix.sizeimage = f->fmt.pix.height *
+ f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.priv = 0;
+ break;
+ }
+
+ case VIDIOC_S_FMT: {
+ struct v4l2_format *f = arg;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV &&
+ f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
+ return -EINVAL;
+ if (f->fmt.pix.field != V4L2_FIELD_ANY &&
+ f->fmt.pix.field != V4L2_FIELD_NONE)
+ return -EINVAL;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ mutex_lock(&meye.lock);
+ if (f->fmt.pix.width <= 320) {
+ f->fmt.pix.width = 320;
+ f->fmt.pix.height = 240;
+ meye.params.subsample = 1;
+ } else {
+ f->fmt.pix.width = 640;
+ f->fmt.pix.height = 480;
+ meye.params.subsample = 0;
+ }
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUYV:
+ meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT;
+ break;
+ case V4L2_PIX_FMT_MJPEG:
+ meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP;
+ break;
+ }
+ mutex_unlock(&meye.lock);
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+ f->fmt.pix.sizeimage = f->fmt.pix.height *
+ f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.priv = 0;
+
+ break;
+ }
+
+ case VIDIOC_REQBUFS: {
+ struct v4l2_requestbuffers *req = arg;
+ int i;
+
+ if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (req->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+ if (meye.grab_fbuffer && req->count == gbuffers) {
+ /* already allocated, no modifications */
+ break;
+ }
+ mutex_lock(&meye.lock);
+ if (meye.grab_fbuffer) {
+ for (i = 0; i < gbuffers; i++)
+ if (meye.vma_use_count[i]) {
+ mutex_unlock(&meye.lock);
+ return -EINVAL;
+ }
+ rvfree(meye.grab_fbuffer, gbuffers * gbufsize);
+ meye.grab_fbuffer = NULL;
+ }
+ gbuffers = max(2, min((int)req->count, MEYE_MAX_BUFNBRS));
+ req->count = gbuffers;
+ meye.grab_fbuffer = rvmalloc(gbuffers * gbufsize);
+ if (!meye.grab_fbuffer) {
+ printk(KERN_ERR "meye: v4l framebuffer allocation"
+ " failed\n");
+ mutex_unlock(&meye.lock);
+ return -ENOMEM;
+ }
+ for (i = 0; i < gbuffers; i++)
+ meye.vma_use_count[i] = 0;
+ mutex_unlock(&meye.lock);
+ break;
+ }
+
+ case VIDIOC_QUERYBUF: {
+ struct v4l2_buffer *buf = arg;
+ int index = buf->index;
+
+ if (index < 0 || index >= gbuffers)
+ return -EINVAL;
+ memset(buf, 0, sizeof(*buf));
+ buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf->index = index;
+ buf->bytesused = meye.grab_buffer[index].size;
+ buf->flags = V4L2_BUF_FLAG_MAPPED;
+ if (meye.grab_buffer[index].state == MEYE_BUF_USING)
+ buf->flags |= V4L2_BUF_FLAG_QUEUED;
+ if (meye.grab_buffer[index].state == MEYE_BUF_DONE)
+ buf->flags |= V4L2_BUF_FLAG_DONE;
+ buf->field = V4L2_FIELD_NONE;
+ buf->timestamp = meye.grab_buffer[index].timestamp;
+ buf->sequence = meye.grab_buffer[index].sequence;
+ buf->memory = V4L2_MEMORY_MMAP;
+ buf->m.offset = index * gbufsize;
+ buf->length = gbufsize;
+ break;
+ }
+
+ case VIDIOC_QBUF: {
+ struct v4l2_buffer *buf = arg;
+
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (buf->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+ if (buf->index < 0 || buf->index >= gbuffers)
+ return -EINVAL;
+ if (meye.grab_buffer[buf->index].state != MEYE_BUF_UNUSED)
+ return -EINVAL;
+ mutex_lock(&meye.lock);
+ buf->flags |= V4L2_BUF_FLAG_QUEUED;
+ buf->flags &= ~V4L2_BUF_FLAG_DONE;
+ meye.grab_buffer[buf->index].state = MEYE_BUF_USING;
+ kfifo_put(meye.grabq, (unsigned char *)&buf->index, sizeof(int));
+ mutex_unlock(&meye.lock);
+ break;
+ }
+
+ case VIDIOC_DQBUF: {
+ struct v4l2_buffer *buf = arg;
+ int reqnr;
+
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (buf->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ mutex_lock(&meye.lock);
+ if (kfifo_len(meye.doneq) == 0 && file->f_flags & O_NONBLOCK) {
+ mutex_unlock(&meye.lock);
+ return -EAGAIN;
+ }
+ if (wait_event_interruptible(meye.proc_list,
+ kfifo_len(meye.doneq) != 0) < 0) {
+ mutex_unlock(&meye.lock);
+ return -EINTR;
+ }
+ if (!kfifo_get(meye.doneq, (unsigned char *)&reqnr,
+ sizeof(int))) {
+ mutex_unlock(&meye.lock);
+ return -EBUSY;
+ }
+ if (meye.grab_buffer[reqnr].state != MEYE_BUF_DONE) {
+ mutex_unlock(&meye.lock);
+ return -EINVAL;
+ }
+ buf->index = reqnr;
+ buf->bytesused = meye.grab_buffer[reqnr].size;
+ buf->flags = V4L2_BUF_FLAG_MAPPED;
+ buf->field = V4L2_FIELD_NONE;
+ buf->timestamp = meye.grab_buffer[reqnr].timestamp;
+ buf->sequence = meye.grab_buffer[reqnr].sequence;
+ buf->memory = V4L2_MEMORY_MMAP;
+ buf->m.offset = reqnr * gbufsize;
+ buf->length = gbufsize;
+ meye.grab_buffer[reqnr].state = MEYE_BUF_UNUSED;
+ mutex_unlock(&meye.lock);
+ break;
+ }
+
+ case VIDIOC_STREAMON: {
+ mutex_lock(&meye.lock);
+ switch (meye.mchip_mode) {
+ case MCHIP_HIC_MODE_CONT_OUT:
+ mchip_continuous_start();
+ break;
+ case MCHIP_HIC_MODE_CONT_COMP:
+ mchip_cont_compression_start();
+ break;
+ default:
+ mutex_unlock(&meye.lock);
+ return -EINVAL;
+ }
+ mutex_unlock(&meye.lock);
+ break;
+ }
+
+ case VIDIOC_STREAMOFF: {
+ int i;
+
+ mutex_lock(&meye.lock);
+ mchip_hic_stop();
+ kfifo_reset(meye.grabq);
+ kfifo_reset(meye.doneq);
+ for (i = 0; i < MEYE_MAX_BUFNBRS; i++)
+ meye.grab_buffer[i].state = MEYE_BUF_UNUSED;
+ mutex_unlock(&meye.lock);
+ break;
+ }
+
+ /*
+ * XXX what about private snapshot ioctls ?
+ * Do they need to be converted to V4L2 ?
+ */
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return 0;
+}
+
+static int meye_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, meye_do_ioctl);
+}
+
+static unsigned int meye_poll(struct file *file, poll_table *wait)
+{
+ unsigned int res = 0;
+
+ mutex_lock(&meye.lock);
+ poll_wait(file, &meye.proc_list, wait);
+ if (kfifo_len(meye.doneq))
+ res = POLLIN | POLLRDNORM;
+ mutex_unlock(&meye.lock);
+ return res;
+}
+
+static void meye_vm_open(struct vm_area_struct *vma)
+{
+ int idx = (int)vma->vm_private_data;
+ meye.vma_use_count[idx]++;
+}
+
+static void meye_vm_close(struct vm_area_struct *vma)
+{
+ int idx = (int)vma->vm_private_data;
+ meye.vma_use_count[idx]--;
+}
+
+static struct vm_operations_struct meye_vm_ops = {
+ .open = meye_vm_open,
+ .close = meye_vm_close,
+};
+
+static int meye_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long page, pos;
+
+ mutex_lock(&meye.lock);
+ if (size > gbuffers * gbufsize) {
+ mutex_unlock(&meye.lock);
+ return -EINVAL;
+ }
+ if (!meye.grab_fbuffer) {
+ int i;
+
+ /* lazy allocation */
+ meye.grab_fbuffer = rvmalloc(gbuffers*gbufsize);
+ if (!meye.grab_fbuffer) {
+ printk(KERN_ERR "meye: v4l framebuffer allocation failed\n");
+ mutex_unlock(&meye.lock);
+ return -ENOMEM;
+ }
+ for (i = 0; i < gbuffers; i++)
+ meye.vma_use_count[i] = 0;
+ }
+ pos = (unsigned long)meye.grab_fbuffer + offset;
+
+ while (size > 0) {
+ page = vmalloc_to_pfn((void *)pos);
+ if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
+ mutex_unlock(&meye.lock);
+ return -EAGAIN;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+
+ vma->vm_ops = &meye_vm_ops;
+ vma->vm_flags &= ~VM_IO; /* not I/O memory */
+ vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
+ vma->vm_private_data = (void *) (offset / gbufsize);
+ meye_vm_open(vma);
+
+ mutex_unlock(&meye.lock);
+ return 0;
+}
+
+static struct file_operations meye_fops = {
+ .owner = THIS_MODULE,
+ .open = meye_open,
+ .release = meye_release,
+ .mmap = meye_mmap,
+ .ioctl = meye_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .poll = meye_poll,
+ .llseek = no_llseek,
+};
+
+static struct video_device meye_template = {
+ .owner = THIS_MODULE,
+ .name = "meye",
+ .type = VID_TYPE_CAPTURE,
+ .hardware = VID_HARDWARE_MEYE,
+ .fops = &meye_fops,
+ .release = video_device_release,
+ .minor = -1,
+};
+
+#ifdef CONFIG_PM
+static int meye_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ pci_save_state(pdev);
+ meye.pm_mchip_mode = meye.mchip_mode;
+ mchip_hic_stop();
+ mchip_set(MCHIP_MM_INTA, 0x0);
+ return 0;
+}
+
+static int meye_resume(struct pci_dev *pdev)
+{
+ pci_restore_state(pdev);
+ pci_write_config_word(meye.mchip_dev, MCHIP_PCI_SOFTRESET_SET, 1);
+
+ mchip_delay(MCHIP_HIC_CMD, 0);
+ mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE);
+ msleep(1);
+ mchip_set(MCHIP_VRJ_SOFT_RESET, 1);
+ msleep(1);
+ mchip_set(MCHIP_MM_PCI_MODE, 5);
+ msleep(1);
+ mchip_set(MCHIP_MM_INTA, MCHIP_MM_INTA_HIC_1_MASK);
+
+ switch (meye.pm_mchip_mode) {
+ case MCHIP_HIC_MODE_CONT_OUT:
+ mchip_continuous_start();
+ break;
+ case MCHIP_HIC_MODE_CONT_COMP:
+ mchip_cont_compression_start();
+ break;
+ }
+ return 0;
+}
+#endif
+
+static int __devinit meye_probe(struct pci_dev *pcidev,
+ const struct pci_device_id *ent)
+{
+ int ret = -EBUSY;
+ unsigned long mchip_adr;
+ u8 revision;
+
+ if (meye.mchip_dev != NULL) {
+ printk(KERN_ERR "meye: only one device allowed!\n");
+ goto outnotdev;
+ }
+
+ meye.mchip_dev = pcidev;
+ meye.video_dev = video_device_alloc();
+ if (!meye.video_dev) {
+ printk(KERN_ERR "meye: video_device_alloc() failed!\n");
+ goto outnotdev;
+ }
+
+ ret = -ENOMEM;
+ meye.grab_temp = vmalloc(MCHIP_NB_PAGES_MJPEG * PAGE_SIZE);
+ if (!meye.grab_temp) {
+ printk(KERN_ERR "meye: grab buffer allocation failed\n");
+ goto outvmalloc;
+ }
+
+ spin_lock_init(&meye.grabq_lock);
+ meye.grabq = kfifo_alloc(sizeof(int) * MEYE_MAX_BUFNBRS, GFP_KERNEL,
+ &meye.grabq_lock);
+ if (IS_ERR(meye.grabq)) {
+ printk(KERN_ERR "meye: fifo allocation failed\n");
+ goto outkfifoalloc1;
+ }
+ spin_lock_init(&meye.doneq_lock);
+ meye.doneq = kfifo_alloc(sizeof(int) * MEYE_MAX_BUFNBRS, GFP_KERNEL,
+ &meye.doneq_lock);
+ if (IS_ERR(meye.doneq)) {
+ printk(KERN_ERR "meye: fifo allocation failed\n");
+ goto outkfifoalloc2;
+ }
+
+ memcpy(meye.video_dev, &meye_template, sizeof(meye_template));
+ meye.video_dev->dev = &meye.mchip_dev->dev;
+
+ if ((ret = sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 1))) {
+ printk(KERN_ERR "meye: unable to power on the camera\n");
+ printk(KERN_ERR "meye: did you enable the camera in "
+ "sonypi using the module options ?\n");
+ goto outsonypienable;
+ }
+
+ ret = -EIO;
+ if ((ret = pci_enable_device(meye.mchip_dev))) {
+ printk(KERN_ERR "meye: pci_enable_device failed\n");
+ goto outenabledev;
+ }
+
+ mchip_adr = pci_resource_start(meye.mchip_dev,0);
+ if (!mchip_adr) {
+ printk(KERN_ERR "meye: mchip has no device base address\n");
+ goto outregions;
+ }
+ if (!request_mem_region(pci_resource_start(meye.mchip_dev, 0),
+ pci_resource_len(meye.mchip_dev, 0),
+ "meye")) {
+ printk(KERN_ERR "meye: request_mem_region failed\n");
+ goto outregions;
+ }
+ meye.mchip_mmregs = ioremap(mchip_adr, MCHIP_MM_REGS);
+ if (!meye.mchip_mmregs) {
+ printk(KERN_ERR "meye: ioremap failed\n");
+ goto outremap;
+ }
+
+ meye.mchip_irq = pcidev->irq;
+ if (request_irq(meye.mchip_irq, meye_irq,
+ SA_INTERRUPT | SA_SHIRQ, "meye", meye_irq)) {
+ printk(KERN_ERR "meye: request_irq failed\n");
+ goto outreqirq;
+ }
+
+ pci_read_config_byte(meye.mchip_dev, PCI_REVISION_ID, &revision);
+ pci_write_config_byte(meye.mchip_dev, PCI_CACHE_LINE_SIZE, 8);
+ pci_write_config_byte(meye.mchip_dev, PCI_LATENCY_TIMER, 64);
+
+ pci_set_master(meye.mchip_dev);
+
+ /* Ask the camera to perform a soft reset. */
+ pci_write_config_word(meye.mchip_dev, MCHIP_PCI_SOFTRESET_SET, 1);
+
+ mchip_delay(MCHIP_HIC_CMD, 0);
+ mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE);
+
+ msleep(1);
+ mchip_set(MCHIP_VRJ_SOFT_RESET, 1);
+
+ msleep(1);
+ mchip_set(MCHIP_MM_PCI_MODE, 5);
+
+ msleep(1);
+ mchip_set(MCHIP_MM_INTA, MCHIP_MM_INTA_HIC_1_MASK);
+
+ if (video_register_device(meye.video_dev, VFL_TYPE_GRABBER,
+ video_nr) < 0) {
+ printk(KERN_ERR "meye: video_register_device failed\n");
+ goto outvideoreg;
+ }
+
+ mutex_init(&meye.lock);
+ init_waitqueue_head(&meye.proc_list);
+ meye.picture.depth = 16;
+ meye.picture.palette = VIDEO_PALETTE_YUV422;
+ meye.picture.brightness = 32 << 10;
+ meye.picture.hue = 32 << 10;
+ meye.picture.colour = 32 << 10;
+ meye.picture.contrast = 32 << 10;
+ meye.picture.whiteness = 0;
+ meye.params.subsample = 0;
+ meye.params.quality = 8;
+ meye.params.sharpness = 32;
+ meye.params.agc = 48;
+ meye.params.picture = 0;
+ meye.params.framerate = 0;
+
+ sonypi_camera_command(SONYPI_COMMAND_SETCAMERABRIGHTNESS, 32);
+ sonypi_camera_command(SONYPI_COMMAND_SETCAMERAHUE, 32);
+ sonypi_camera_command(SONYPI_COMMAND_SETCAMERACOLOR, 32);
+ sonypi_camera_command(SONYPI_COMMAND_SETCAMERACONTRAST, 32);
+ sonypi_camera_command(SONYPI_COMMAND_SETCAMERASHARPNESS, 32);
+ sonypi_camera_command(SONYPI_COMMAND_SETCAMERAPICTURE, 0);
+ sonypi_camera_command(SONYPI_COMMAND_SETCAMERAAGC, 48);
+
+ printk(KERN_INFO "meye: Motion Eye Camera Driver v%s.\n",
+ MEYE_DRIVER_VERSION);
+ printk(KERN_INFO "meye: mchip KL5A72002 rev. %d, base %lx, irq %d\n",
+ revision, mchip_adr, meye.mchip_irq);
+
+ return 0;
+
+outvideoreg:
+ free_irq(meye.mchip_irq, meye_irq);
+outreqirq:
+ iounmap(meye.mchip_mmregs);
+outremap:
+ release_mem_region(pci_resource_start(meye.mchip_dev, 0),
+ pci_resource_len(meye.mchip_dev, 0));
+outregions:
+ pci_disable_device(meye.mchip_dev);
+outenabledev:
+ sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 0);
+outsonypienable:
+ kfifo_free(meye.doneq);
+outkfifoalloc2:
+ kfifo_free(meye.grabq);
+outkfifoalloc1:
+ vfree(meye.grab_temp);
+outvmalloc:
+ video_device_release(meye.video_dev);
+outnotdev:
+ return ret;
+}
+
+static void __devexit meye_remove(struct pci_dev *pcidev)
+{
+ video_unregister_device(meye.video_dev);
+
+ mchip_hic_stop();
+
+ mchip_dma_free();
+
+ /* disable interrupts */
+ mchip_set(MCHIP_MM_INTA, 0x0);
+
+ free_irq(meye.mchip_irq, meye_irq);
+
+ iounmap(meye.mchip_mmregs);
+
+ release_mem_region(pci_resource_start(meye.mchip_dev, 0),
+ pci_resource_len(meye.mchip_dev, 0));
+
+ pci_disable_device(meye.mchip_dev);
+
+ sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 0);
+
+ kfifo_free(meye.doneq);
+ kfifo_free(meye.grabq);
+
+ vfree(meye.grab_temp);
+
+ if (meye.grab_fbuffer) {
+ rvfree(meye.grab_fbuffer, gbuffers*gbufsize);
+ meye.grab_fbuffer = NULL;
+ }
+
+ printk(KERN_INFO "meye: removed\n");
+}
+
+static struct pci_device_id meye_pci_tbl[] = {
+ { PCI_VENDOR_ID_KAWASAKI, PCI_DEVICE_ID_MCHIP_KL5A72002,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, meye_pci_tbl);
+
+static struct pci_driver meye_driver = {
+ .name = "meye",
+ .id_table = meye_pci_tbl,
+ .probe = meye_probe,
+ .remove = __devexit_p(meye_remove),
+#ifdef CONFIG_PM
+ .suspend = meye_suspend,
+ .resume = meye_resume,
+#endif
+};
+
+static int __init meye_init(void)
+{
+ gbuffers = max(2, min((int)gbuffers, MEYE_MAX_BUFNBRS));
+ if (gbufsize < 0 || gbufsize > MEYE_MAX_BUFSIZE)
+ gbufsize = MEYE_MAX_BUFSIZE;
+ gbufsize = PAGE_ALIGN(gbufsize);
+ printk(KERN_INFO "meye: using %d buffers with %dk (%dk total)"
+ "for capture\n",
+ gbuffers,
+ gbufsize / 1024, gbuffers * gbufsize / 1024);
+ return pci_register_driver(&meye_driver);
+}
+
+static void __exit meye_exit(void)
+{
+ pci_unregister_driver(&meye_driver);
+}
+
+module_init(meye_init);
+module_exit(meye_exit);
diff --git a/linux/drivers/media/video/meye.h b/linux/drivers/media/video/meye.h
new file mode 100644
index 000000000..0d09a0e38
--- /dev/null
+++ b/linux/drivers/media/video/meye.h
@@ -0,0 +1,320 @@
+/*
+ * Motion Eye video4linux driver for Sony Vaio PictureBook
+ *
+ * Copyright (C) 2001-2004 Stelian Pop <stelian@popies.net>
+ *
+ * Copyright (C) 2001-2002 Alcôve <www.alcove.com>
+ *
+ * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com>
+ *
+ * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras.
+ *
+ * Some parts borrowed from various video4linux drivers, especially
+ * bttv-driver.c and zoran.c, see original files for credits.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _MEYE_PRIV_H_
+#define _MEYE_PRIV_H_
+
+#define MEYE_DRIVER_MAJORVERSION 1
+#define MEYE_DRIVER_MINORVERSION 13
+
+#define MEYE_DRIVER_VERSION __stringify(MEYE_DRIVER_MAJORVERSION) "." \
+ __stringify(MEYE_DRIVER_MINORVERSION)
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/kfifo.h>
+
+/****************************************************************************/
+/* Motion JPEG chip registers */
+/****************************************************************************/
+
+/* Motion JPEG chip PCI configuration registers */
+#define MCHIP_PCI_POWER_CSR 0x54
+#define MCHIP_PCI_MCORE_STATUS 0x60 /* see HIC_STATUS */
+#define MCHIP_PCI_HOSTUSEREQ_SET 0x64
+#define MCHIP_PCI_HOSTUSEREQ_CLR 0x68
+#define MCHIP_PCI_LOWPOWER_SET 0x6c
+#define MCHIP_PCI_LOWPOWER_CLR 0x70
+#define MCHIP_PCI_SOFTRESET_SET 0x74
+
+/* Motion JPEG chip memory mapped registers */
+#define MCHIP_MM_REGS 0x200 /* 512 bytes */
+#define MCHIP_REG_TIMEOUT 1000 /* reg access, ~us */
+#define MCHIP_MCC_VRJ_TIMEOUT 1000 /* MCC & VRJ access */
+
+#define MCHIP_MM_PCI_MODE 0x00 /* PCI access mode */
+#define MCHIP_MM_PCI_MODE_RETRY 0x00000001 /* retry mode */
+#define MCHIP_MM_PCI_MODE_MASTER 0x00000002 /* master access */
+#define MCHIP_MM_PCI_MODE_READ_LINE 0x00000004 /* read line */
+
+#define MCHIP_MM_INTA 0x04 /* Int status/mask */
+#define MCHIP_MM_INTA_MCC 0x00000001 /* MCC interrupt */
+#define MCHIP_MM_INTA_VRJ 0x00000002 /* VRJ interrupt */
+#define MCHIP_MM_INTA_HIC_1 0x00000004 /* one frame done */
+#define MCHIP_MM_INTA_HIC_1_MASK 0x00000400 /* 1: enable */
+#define MCHIP_MM_INTA_HIC_END 0x00000008 /* all frames done */
+#define MCHIP_MM_INTA_HIC_END_MASK 0x00000800
+#define MCHIP_MM_INTA_JPEG 0x00000010 /* decompress. error */
+#define MCHIP_MM_INTA_JPEG_MASK 0x00001000
+#define MCHIP_MM_INTA_CAPTURE 0x00000020 /* capture end */
+#define MCHIP_MM_INTA_PCI_ERR 0x00000040 /* PCI error */
+#define MCHIP_MM_INTA_PCI_ERR_MASK 0x00004000
+
+#define MCHIP_MM_PT_ADDR 0x08 /* page table address*/
+ /* n*4kB */
+#define MCHIP_NB_PAGES 1024 /* pages for display */
+#define MCHIP_NB_PAGES_MJPEG 256 /* pages for mjpeg */
+
+#define MCHIP_MM_FIR(n) (0x0c+(n)*4) /* Frame info 0-3 */
+#define MCHIP_MM_FIR_RDY 0x00000001 /* frame ready */
+#define MCHIP_MM_FIR_FAILFR_MASK 0xf8000000 /* # of failed frames */
+#define MCHIP_MM_FIR_FAILFR_SHIFT 27
+
+ /* continuous comp/decomp mode */
+#define MCHIP_MM_FIR_C_ENDL_MASK 0x000007fe /* end DW [10] */
+#define MCHIP_MM_FIR_C_ENDL_SHIFT 1
+#define MCHIP_MM_FIR_C_ENDP_MASK 0x0007f800 /* end page [8] */
+#define MCHIP_MM_FIR_C_ENDP_SHIFT 11
+#define MCHIP_MM_FIR_C_STARTP_MASK 0x07f80000 /* start page [8] */
+#define MCHIP_MM_FIR_C_STARTP_SHIFT 19
+
+ /* continuous picture output mode */
+#define MCHIP_MM_FIR_O_STARTP_MASK 0x7ffe0000 /* start page [10] */
+#define MCHIP_MM_FIR_O_STARTP_SHIFT 17
+
+#define MCHIP_MM_FIFO_DATA 0x1c /* PCI TGT FIFO data */
+#define MCHIP_MM_FIFO_STATUS 0x20 /* PCI TGT FIFO stat */
+#define MCHIP_MM_FIFO_MASK 0x00000003
+#define MCHIP_MM_FIFO_WAIT_OR_READY 0x00000002 /* Bits common to WAIT & READY*/
+#define MCHIP_MM_FIFO_IDLE 0x0 /* HIC idle */
+#define MCHIP_MM_FIFO_IDLE1 0x1 /* idem ??? */
+#define MCHIP_MM_FIFO_WAIT 0x2 /* wait request */
+#define MCHIP_MM_FIFO_READY 0x3 /* data ready */
+
+#define MCHIP_HIC_HOST_USEREQ 0x40 /* host uses MCORE */
+
+#define MCHIP_HIC_TP_BUSY 0x44 /* taking picture */
+
+#define MCHIP_HIC_PIC_SAVED 0x48 /* pic in SDRAM */
+
+#define MCHIP_HIC_LOWPOWER 0x4c /* clock stopped */
+
+#define MCHIP_HIC_CTL 0x50 /* HIC control */
+#define MCHIP_HIC_CTL_SOFT_RESET 0x00000001 /* MCORE reset */
+#define MCHIP_HIC_CTL_MCORE_RDY 0x00000002 /* MCORE ready */
+
+#define MCHIP_HIC_CMD 0x54 /* HIC command */
+#define MCHIP_HIC_CMD_BITS 0x00000003 /* cmd width=[1:0]*/
+#define MCHIP_HIC_CMD_NOOP 0x0
+#define MCHIP_HIC_CMD_START 0x1
+#define MCHIP_HIC_CMD_STOP 0x2
+
+#define MCHIP_HIC_MODE 0x58
+#define MCHIP_HIC_MODE_NOOP 0x0
+#define MCHIP_HIC_MODE_STILL_CAP 0x1 /* still pic capt */
+#define MCHIP_HIC_MODE_DISPLAY 0x2 /* display */
+#define MCHIP_HIC_MODE_STILL_COMP 0x3 /* still pic comp. */
+#define MCHIP_HIC_MODE_STILL_DECOMP 0x4 /* still pic decomp. */
+#define MCHIP_HIC_MODE_CONT_COMP 0x5 /* cont capt+comp */
+#define MCHIP_HIC_MODE_CONT_DECOMP 0x6 /* cont decomp+disp */
+#define MCHIP_HIC_MODE_STILL_OUT 0x7 /* still pic output */
+#define MCHIP_HIC_MODE_CONT_OUT 0x8 /* cont output */
+
+#define MCHIP_HIC_STATUS 0x5c
+#define MCHIP_HIC_STATUS_MCC_RDY 0x00000001 /* MCC reg acc ok */
+#define MCHIP_HIC_STATUS_VRJ_RDY 0x00000002 /* VRJ reg acc ok */
+#define MCHIP_HIC_STATUS_IDLE 0x00000003
+#define MCHIP_HIC_STATUS_CAPDIS 0x00000004 /* cap/disp in prog */
+#define MCHIP_HIC_STATUS_COMPDEC 0x00000008 /* (de)comp in prog */
+#define MCHIP_HIC_STATUS_BUSY 0x00000010 /* HIC busy */
+
+#define MCHIP_HIC_S_RATE 0x60 /* MJPEG # frames */
+
+#define MCHIP_HIC_PCI_VFMT 0x64 /* video format */
+#define MCHIP_HIC_PCI_VFMT_YVYU 0x00000001 /* 0: V Y' U Y */
+ /* 1: Y' V Y U */
+
+#define MCHIP_MCC_CMD 0x80 /* MCC commands */
+#define MCHIP_MCC_CMD_INITIAL 0x0 /* idle ? */
+#define MCHIP_MCC_CMD_IIC_START_SET 0x1
+#define MCHIP_MCC_CMD_IIC_END_SET 0x2
+#define MCHIP_MCC_CMD_FM_WRITE 0x3 /* frame memory */
+#define MCHIP_MCC_CMD_FM_READ 0x4
+#define MCHIP_MCC_CMD_FM_STOP 0x5
+#define MCHIP_MCC_CMD_CAPTURE 0x6
+#define MCHIP_MCC_CMD_DISPLAY 0x7
+#define MCHIP_MCC_CMD_END_DISP 0x8
+#define MCHIP_MCC_CMD_STILL_COMP 0x9
+#define MCHIP_MCC_CMD_STILL_DECOMP 0xa
+#define MCHIP_MCC_CMD_STILL_OUTPUT 0xb
+#define MCHIP_MCC_CMD_CONT_OUTPUT 0xc
+#define MCHIP_MCC_CMD_CONT_COMP 0xd
+#define MCHIP_MCC_CMD_CONT_DECOMP 0xe
+#define MCHIP_MCC_CMD_RESET 0xf /* MCC reset */
+
+#define MCHIP_MCC_IIC_WR 0x84
+
+#define MCHIP_MCC_MCC_WR 0x88
+
+#define MCHIP_MCC_MCC_RD 0x8c
+
+#define MCHIP_MCC_STATUS 0x90
+#define MCHIP_MCC_STATUS_CAPT 0x00000001 /* capturing */
+#define MCHIP_MCC_STATUS_DISP 0x00000002 /* displaying */
+#define MCHIP_MCC_STATUS_COMP 0x00000004 /* compressing */
+#define MCHIP_MCC_STATUS_DECOMP 0x00000008 /* decompressing */
+#define MCHIP_MCC_STATUS_MCC_WR 0x00000010 /* register ready */
+#define MCHIP_MCC_STATUS_MCC_RD 0x00000020 /* register ready */
+#define MCHIP_MCC_STATUS_IIC_WR 0x00000040 /* register ready */
+#define MCHIP_MCC_STATUS_OUTPUT 0x00000080 /* output in prog */
+
+#define MCHIP_MCC_SIG_POLARITY 0x94
+#define MCHIP_MCC_SIG_POL_VS_H 0x00000001 /* VS active-high */
+#define MCHIP_MCC_SIG_POL_HS_H 0x00000002 /* HS active-high */
+#define MCHIP_MCC_SIG_POL_DOE_H 0x00000004 /* DOE active-high */
+
+#define MCHIP_MCC_IRQ 0x98
+#define MCHIP_MCC_IRQ_CAPDIS_STRT 0x00000001 /* cap/disp started */
+#define MCHIP_MCC_IRQ_CAPDIS_STRT_MASK 0x00000010
+#define MCHIP_MCC_IRQ_CAPDIS_END 0x00000002 /* cap/disp ended */
+#define MCHIP_MCC_IRQ_CAPDIS_END_MASK 0x00000020
+#define MCHIP_MCC_IRQ_COMPDEC_STRT 0x00000004 /* (de)comp started */
+#define MCHIP_MCC_IRQ_COMPDEC_STRT_MASK 0x00000040
+#define MCHIP_MCC_IRQ_COMPDEC_END 0x00000008 /* (de)comp ended */
+#define MCHIP_MCC_IRQ_COMPDEC_END_MASK 0x00000080
+
+#define MCHIP_MCC_HSTART 0x9c /* video in */
+#define MCHIP_MCC_VSTART 0xa0
+#define MCHIP_MCC_HCOUNT 0xa4
+#define MCHIP_MCC_VCOUNT 0xa8
+#define MCHIP_MCC_R_XBASE 0xac /* capt/disp */
+#define MCHIP_MCC_R_YBASE 0xb0
+#define MCHIP_MCC_R_XRANGE 0xb4
+#define MCHIP_MCC_R_YRANGE 0xb8
+#define MCHIP_MCC_B_XBASE 0xbc /* comp/decomp */
+#define MCHIP_MCC_B_YBASE 0xc0
+#define MCHIP_MCC_B_XRANGE 0xc4
+#define MCHIP_MCC_B_YRANGE 0xc8
+
+#define MCHIP_MCC_R_SAMPLING 0xcc /* 1: 1:4 */
+
+#define MCHIP_VRJ_CMD 0x100 /* VRJ commands */
+
+/* VRJ registers (see table 12.2.4) */
+#define MCHIP_VRJ_COMPRESSED_DATA 0x1b0
+#define MCHIP_VRJ_PIXEL_DATA 0x1b8
+
+#define MCHIP_VRJ_BUS_MODE 0x100
+#define MCHIP_VRJ_SIGNAL_ACTIVE_LEVEL 0x108
+#define MCHIP_VRJ_PDAT_USE 0x110
+#define MCHIP_VRJ_MODE_SPECIFY 0x118
+#define MCHIP_VRJ_LIMIT_COMPRESSED_LO 0x120
+#define MCHIP_VRJ_LIMIT_COMPRESSED_HI 0x124
+#define MCHIP_VRJ_COMP_DATA_FORMAT 0x128
+#define MCHIP_VRJ_TABLE_DATA 0x140
+#define MCHIP_VRJ_RESTART_INTERVAL 0x148
+#define MCHIP_VRJ_NUM_LINES 0x150
+#define MCHIP_VRJ_NUM_PIXELS 0x158
+#define MCHIP_VRJ_NUM_COMPONENTS 0x160
+#define MCHIP_VRJ_SOF1 0x168
+#define MCHIP_VRJ_SOF2 0x170
+#define MCHIP_VRJ_SOF3 0x178
+#define MCHIP_VRJ_SOF4 0x180
+#define MCHIP_VRJ_SOS 0x188
+#define MCHIP_VRJ_SOFT_RESET 0x190
+
+#define MCHIP_VRJ_STATUS 0x1c0
+#define MCHIP_VRJ_STATUS_BUSY 0x00001
+#define MCHIP_VRJ_STATUS_COMP_ACCESS 0x00002
+#define MCHIP_VRJ_STATUS_PIXEL_ACCESS 0x00004
+#define MCHIP_VRJ_STATUS_ERROR 0x00008
+
+#define MCHIP_VRJ_IRQ_FLAG 0x1c8
+#define MCHIP_VRJ_ERROR_REPORT 0x1d8
+
+#define MCHIP_VRJ_START_COMMAND 0x1a0
+
+/****************************************************************************/
+/* Driver definitions. */
+/****************************************************************************/
+
+/* Sony Programmable I/O Controller for accessing the camera commands */
+#include <linux/sonypi.h>
+
+/* private API definitions */
+#include <linux/meye.h>
+#include <linux/mutex.h>
+
+
+/* Enable jpg software correction */
+#define MEYE_JPEG_CORRECTION 1
+
+/* Maximum size of a buffer */
+#define MEYE_MAX_BUFSIZE 614400 /* 640 * 480 * 2 */
+
+/* Maximum number of buffers */
+#define MEYE_MAX_BUFNBRS 32
+
+/* State of a buffer */
+#define MEYE_BUF_UNUSED 0 /* not used */
+#define MEYE_BUF_USING 1 /* currently grabbing / playing */
+#define MEYE_BUF_DONE 2 /* done */
+
+/* grab buffer */
+struct meye_grab_buffer {
+ int state; /* state of buffer */
+ unsigned long size; /* size of jpg frame */
+ struct timeval timestamp; /* timestamp */
+ unsigned long sequence; /* sequence number */
+};
+
+/* size of kfifos containings buffer indices */
+#define MEYE_QUEUE_SIZE MEYE_MAX_BUFNBRS
+
+/* Motion Eye device structure */
+struct meye {
+ struct pci_dev *mchip_dev; /* pci device */
+ u8 mchip_irq; /* irq */
+ u8 mchip_mode; /* actual mchip mode: HIC_MODE... */
+ u8 mchip_fnum; /* current mchip frame number */
+ unsigned char __iomem *mchip_mmregs;/* mchip: memory mapped registers */
+ u8 *mchip_ptable[MCHIP_NB_PAGES];/* mchip: ptable */
+ void *mchip_ptable_toc; /* mchip: ptable toc */
+ dma_addr_t mchip_dmahandle; /* mchip: dma handle to ptable toc */
+ unsigned char *grab_fbuffer; /* capture framebuffer */
+ unsigned char *grab_temp; /* temporary buffer */
+ /* list of buffers */
+ struct meye_grab_buffer grab_buffer[MEYE_MAX_BUFNBRS];
+ int vma_use_count[MEYE_MAX_BUFNBRS]; /* mmap count */
+ struct mutex lock; /* mutex for open/mmap... */
+ struct kfifo *grabq; /* queue for buffers to be grabbed */
+ spinlock_t grabq_lock; /* lock protecting the queue */
+ struct kfifo *doneq; /* queue for grabbed buffers */
+ spinlock_t doneq_lock; /* lock protecting the queue */
+ wait_queue_head_t proc_list; /* wait queue */
+ struct video_device *video_dev; /* video device parameters */
+ struct video_picture picture; /* video picture parameters */
+ struct meye_params params; /* additional parameters */
+#ifdef CONFIG_PM
+ u8 pm_mchip_mode; /* old mchip mode */
+#endif
+};
+
+#endif
diff --git a/linux/drivers/media/video/ov511.c b/linux/drivers/media/video/ov511.c
new file mode 100644
index 000000000..fdc8e3f13
--- /dev/null
+++ b/linux/drivers/media/video/ov511.c
@@ -0,0 +1,5932 @@
+/*
+ * OmniVision OV511 Camera-to-USB Bridge Driver
+ *
+ * Copyright (c) 1999-2003 Mark W. McClelland
+ * Original decompression code Copyright 1998-2000 OmniVision Technologies
+ * Many improvements by Bret Wallach <bwallac1@san.rr.com>
+ * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
+ * Snapshot code by Kevin Moore
+ * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
+ * Changes by Claudio Matsuoka <claudio@conectiva.com>
+ * Original SAA7111A code by Dave Perks <dperks@ibm.net>
+ * URB error messages from pwc driver by Nemosoft
+ * generic_ioctl() code from videodev.c by Gerd Knorr and Alan Cox
+ * Memory management (rvmalloc) code from bttv driver, by Gerd Knorr and others
+ *
+ * Based on the Linux CPiA driver written by Peter Pregler,
+ * Scott J. Bertin and Johannes Erdfelt.
+ *
+ * Please see the file: Documentation/usb/ov511.txt
+ * and the website at: http://alpha.dyndns.org/ov511
+ * for more info.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/pagemap.h>
+#include <asm/semaphore.h>
+#include <asm/processor.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+
+#if defined (__i386__)
+ #include <asm/cpufeature.h>
+#endif
+
+#include "ov511.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.64 for Linux 2.5"
+#define EMAIL "mark@alpha.dyndns.org"
+#define DRIVER_AUTHOR "Mark McClelland <mark@alpha.dyndns.org> & Bret Wallach \
+ & Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha \
+ <cpbotha@ieee.org> & Claudio Matsuoka <claudio@conectiva.com>"
+#define DRIVER_DESC "ov511 USB Camera Driver"
+
+#define OV511_I2C_RETRIES 3
+#define ENABLE_Y_QUANTABLE 1
+#define ENABLE_UV_QUANTABLE 1
+
+#define OV511_MAX_UNIT_VIDEO 16
+
+/* Pixel count * bytes per YUV420 pixel (1.5) */
+#define MAX_FRAME_SIZE(w, h) ((w) * (h) * 3 / 2)
+
+#define MAX_DATA_SIZE(w, h) (MAX_FRAME_SIZE(w, h) + sizeof(struct timeval))
+
+/* Max size * bytes per YUV420 pixel (1.5) + one extra isoc frame for safety */
+#define MAX_RAW_DATA_SIZE(w, h) ((w) * (h) * 3 / 2 + 1024)
+
+#define FATAL_ERROR(rc) ((rc) < 0 && (rc) != -EPERM)
+
+/**********************************************************************
+ * Module Parameters
+ * (See ov511.txt for detailed descriptions of these)
+ **********************************************************************/
+
+/* These variables (and all static globals) default to zero */
+static int autobright = 1;
+static int autogain = 1;
+static int autoexp = 1;
+static int debug;
+static int snapshot;
+static int cams = 1;
+static int compress;
+static int testpat;
+static int dumppix;
+static int led = 1;
+static int dump_bridge;
+static int dump_sensor;
+static int printph;
+static int phy = 0x1f;
+static int phuv = 0x05;
+static int pvy = 0x06;
+static int pvuv = 0x06;
+static int qhy = 0x14;
+static int qhuv = 0x03;
+static int qvy = 0x04;
+static int qvuv = 0x04;
+static int lightfreq;
+static int bandingfilter;
+static int clockdiv = -1;
+static int packetsize = -1;
+static int framedrop = -1;
+static int fastset;
+static int force_palette;
+static int backlight;
+static int unit_video[OV511_MAX_UNIT_VIDEO];
+static int remove_zeros;
+static int mirror;
+static int ov518_color;
+
+module_param(autobright, int, 0);
+MODULE_PARM_DESC(autobright, "Sensor automatically changes brightness");
+module_param(autogain, int, 0);
+MODULE_PARM_DESC(autogain, "Sensor automatically changes gain");
+module_param(autoexp, int, 0);
+MODULE_PARM_DESC(autoexp, "Sensor automatically changes exposure");
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug,
+ "Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=max");
+module_param(snapshot, int, 0);
+MODULE_PARM_DESC(snapshot, "Enable snapshot mode");
+module_param(cams, int, 0);
+MODULE_PARM_DESC(cams, "Number of simultaneous cameras");
+module_param(compress, int, 0);
+MODULE_PARM_DESC(compress, "Turn on compression");
+module_param(testpat, int, 0);
+MODULE_PARM_DESC(testpat,
+ "Replace image with vertical bar testpattern (only partially working)");
+module_param(dumppix, int, 0);
+MODULE_PARM_DESC(dumppix, "Dump raw pixel data");
+module_param(led, int, 0);
+MODULE_PARM_DESC(led,
+ "LED policy (OV511+ or later). 0=off, 1=on (default), 2=auto (on when open)");
+module_param(dump_bridge, int, 0);
+MODULE_PARM_DESC(dump_bridge, "Dump the bridge registers");
+module_param(dump_sensor, int, 0);
+MODULE_PARM_DESC(dump_sensor, "Dump the sensor registers");
+module_param(printph, int, 0);
+MODULE_PARM_DESC(printph, "Print frame start/end headers");
+module_param(phy, int, 0);
+MODULE_PARM_DESC(phy, "Prediction range (horiz. Y)");
+module_param(phuv, int, 0);
+MODULE_PARM_DESC(phuv, "Prediction range (horiz. UV)");
+module_param(pvy, int, 0);
+MODULE_PARM_DESC(pvy, "Prediction range (vert. Y)");
+module_param(pvuv, int, 0);
+MODULE_PARM_DESC(pvuv, "Prediction range (vert. UV)");
+module_param(qhy, int, 0);
+MODULE_PARM_DESC(qhy, "Quantization threshold (horiz. Y)");
+module_param(qhuv, int, 0);
+MODULE_PARM_DESC(qhuv, "Quantization threshold (horiz. UV)");
+module_param(qvy, int, 0);
+MODULE_PARM_DESC(qvy, "Quantization threshold (vert. Y)");
+module_param(qvuv, int, 0);
+MODULE_PARM_DESC(qvuv, "Quantization threshold (vert. UV)");
+module_param(lightfreq, int, 0);
+MODULE_PARM_DESC(lightfreq,
+ "Light frequency. Set to 50 or 60 Hz, or zero for default settings");
+module_param(bandingfilter, int, 0);
+MODULE_PARM_DESC(bandingfilter,
+ "Enable banding filter (to reduce effects of fluorescent lighting)");
+module_param(clockdiv, int, 0);
+MODULE_PARM_DESC(clockdiv, "Force pixel clock divisor to a specific value");
+module_param(packetsize, int, 0);
+MODULE_PARM_DESC(packetsize, "Force a specific isoc packet size");
+module_param(framedrop, int, 0);
+MODULE_PARM_DESC(framedrop, "Force a specific frame drop register setting");
+module_param(fastset, int, 0);
+MODULE_PARM_DESC(fastset, "Allows picture settings to take effect immediately");
+module_param(force_palette, int, 0);
+MODULE_PARM_DESC(force_palette, "Force the palette to a specific value");
+module_param(backlight, int, 0);
+MODULE_PARM_DESC(backlight, "For objects that are lit from behind");
+static int num_uv;
+module_param_array(unit_video, int, &num_uv, 0);
+MODULE_PARM_DESC(unit_video,
+ "Force use of specific minor number(s). 0 is not allowed.");
+module_param(remove_zeros, int, 0);
+MODULE_PARM_DESC(remove_zeros,
+ "Remove zero-padding from uncompressed incoming data");
+module_param(mirror, int, 0);
+MODULE_PARM_DESC(mirror, "Reverse image horizontally");
+module_param(ov518_color, int, 0);
+MODULE_PARM_DESC(ov518_color, "Enable OV518 color (experimental)");
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/**********************************************************************
+ * Miscellaneous Globals
+ **********************************************************************/
+
+static struct usb_driver ov511_driver;
+
+/* Number of times to retry a failed I2C transaction. Increase this if you
+ * are getting "Failed to read sensor ID..." */
+static const int i2c_detect_tries = 5;
+
+static struct usb_device_id device_table [] = {
+ { USB_DEVICE(VEND_OMNIVISION, PROD_OV511) },
+ { USB_DEVICE(VEND_OMNIVISION, PROD_OV511PLUS) },
+ { USB_DEVICE(VEND_OMNIVISION, PROD_OV518) },
+ { USB_DEVICE(VEND_OMNIVISION, PROD_OV518PLUS) },
+ { USB_DEVICE(VEND_MATTEL, PROD_ME2CAM) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, device_table);
+
+static unsigned char yQuanTable511[] = OV511_YQUANTABLE;
+static unsigned char uvQuanTable511[] = OV511_UVQUANTABLE;
+static unsigned char yQuanTable518[] = OV518_YQUANTABLE;
+static unsigned char uvQuanTable518[] = OV518_UVQUANTABLE;
+
+/**********************************************************************
+ * Symbolic Names
+ **********************************************************************/
+
+/* Known OV511-based cameras */
+static struct symbolic_list camlist[] = {
+ { 0, "Generic Camera (no ID)" },
+ { 1, "Mustek WCam 3X" },
+ { 3, "D-Link DSB-C300" },
+ { 4, "Generic OV511/OV7610" },
+ { 5, "Puretek PT-6007" },
+ { 6, "Lifeview USB Life TV (NTSC)" },
+ { 21, "Creative Labs WebCam 3" },
+ { 22, "Lifeview USB Life TV (PAL D/K+B/G)" },
+ { 36, "Koala-Cam" },
+ { 38, "Lifeview USB Life TV (PAL)" },
+ { 41, "Samsung Anycam MPC-M10" },
+ { 43, "Mtekvision Zeca MV402" },
+ { 46, "Suma eON" },
+ { 70, "Lifeview USB Life TV (PAL/SECAM)" },
+ { 100, "Lifeview RoboCam" },
+ { 102, "AverMedia InterCam Elite" },
+ { 112, "MediaForte MV300" }, /* or OV7110 evaluation kit */
+ { 134, "Ezonics EZCam II" },
+ { 192, "Webeye 2000B" },
+ { 253, "Alpha Vision Tech. AlphaCam SE" },
+ { -1, NULL }
+};
+
+/* Video4Linux1 Palettes */
+static struct symbolic_list v4l1_plist[] = {
+ { VIDEO_PALETTE_GREY, "GREY" },
+ { VIDEO_PALETTE_HI240, "HI240" },
+ { VIDEO_PALETTE_RGB565, "RGB565" },
+ { VIDEO_PALETTE_RGB24, "RGB24" },
+ { VIDEO_PALETTE_RGB32, "RGB32" },
+ { VIDEO_PALETTE_RGB555, "RGB555" },
+ { VIDEO_PALETTE_YUV422, "YUV422" },
+ { VIDEO_PALETTE_YUYV, "YUYV" },
+ { VIDEO_PALETTE_UYVY, "UYVY" },
+ { VIDEO_PALETTE_YUV420, "YUV420" },
+ { VIDEO_PALETTE_YUV411, "YUV411" },
+ { VIDEO_PALETTE_RAW, "RAW" },
+ { VIDEO_PALETTE_YUV422P,"YUV422P" },
+ { VIDEO_PALETTE_YUV411P,"YUV411P" },
+ { VIDEO_PALETTE_YUV420P,"YUV420P" },
+ { VIDEO_PALETTE_YUV410P,"YUV410P" },
+ { -1, NULL }
+};
+
+static struct symbolic_list brglist[] = {
+ { BRG_OV511, "OV511" },
+ { BRG_OV511PLUS, "OV511+" },
+ { BRG_OV518, "OV518" },
+ { BRG_OV518PLUS, "OV518+" },
+ { -1, NULL }
+};
+
+static struct symbolic_list senlist[] = {
+ { SEN_OV76BE, "OV76BE" },
+ { SEN_OV7610, "OV7610" },
+ { SEN_OV7620, "OV7620" },
+ { SEN_OV7620AE, "OV7620AE" },
+ { SEN_OV6620, "OV6620" },
+ { SEN_OV6630, "OV6630" },
+ { SEN_OV6630AE, "OV6630AE" },
+ { SEN_OV6630AF, "OV6630AF" },
+ { SEN_OV8600, "OV8600" },
+ { SEN_KS0127, "KS0127" },
+ { SEN_KS0127B, "KS0127B" },
+ { SEN_SAA7111A, "SAA7111A" },
+ { -1, NULL }
+};
+
+/* URB error codes: */
+static struct symbolic_list urb_errlist[] = {
+ { -ENOSR, "Buffer error (overrun)" },
+ { -EPIPE, "Stalled (device not responding)" },
+ { -EOVERFLOW, "Babble (bad cable?)" },
+ { -EPROTO, "Bit-stuff error (bad cable?)" },
+ { -EILSEQ, "CRC/Timeout" },
+ { -ETIMEDOUT, "NAK (device does not respond)" },
+ { -1, NULL }
+};
+
+/**********************************************************************
+ * Memory management
+ **********************************************************************/
+static void *
+rvmalloc(unsigned long size)
+{
+ void *mem;
+ unsigned long adr;
+
+ size = PAGE_ALIGN(size);
+ mem = vmalloc_32(size);
+ if (!mem)
+ return NULL;
+
+ memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+ adr = (unsigned long) mem;
+ while (size > 0) {
+ SetPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ return mem;
+}
+
+static void
+rvfree(void *mem, unsigned long size)
+{
+ unsigned long adr;
+
+ if (!mem)
+ return;
+
+ adr = (unsigned long) mem;
+ while ((long) size > 0) {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ vfree(mem);
+}
+
+/**********************************************************************
+ *
+ * Register I/O
+ *
+ **********************************************************************/
+
+/* Write an OV51x register */
+static int
+reg_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value)
+{
+ int rc;
+
+ PDEBUG(5, "0x%02X:0x%02X", reg, value);
+
+ mutex_lock(&ov->cbuf_lock);
+ ov->cbuf[0] = value;
+ rc = usb_control_msg(ov->dev,
+ usb_sndctrlpipe(ov->dev, 0),
+ (ov->bclass == BCL_OV518)?1:2 /* REG_IO */,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, (__u16)reg, &ov->cbuf[0], 1, 1000);
+ mutex_unlock(&ov->cbuf_lock);
+
+ if (rc < 0)
+ err("reg write: error %d: %s", rc, symbolic(urb_errlist, rc));
+
+ return rc;
+}
+
+/* Read from an OV51x register */
+/* returns: negative is error, pos or zero is data */
+static int
+reg_r(struct usb_ov511 *ov, unsigned char reg)
+{
+ int rc;
+
+ mutex_lock(&ov->cbuf_lock);
+ rc = usb_control_msg(ov->dev,
+ usb_rcvctrlpipe(ov->dev, 0),
+ (ov->bclass == BCL_OV518)?1:3 /* REG_IO */,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, (__u16)reg, &ov->cbuf[0], 1, 1000);
+
+ if (rc < 0) {
+ err("reg read: error %d: %s", rc, symbolic(urb_errlist, rc));
+ } else {
+ rc = ov->cbuf[0];
+ PDEBUG(5, "0x%02X:0x%02X", reg, ov->cbuf[0]);
+ }
+
+ mutex_unlock(&ov->cbuf_lock);
+
+ return rc;
+}
+
+/*
+ * Writes bits at positions specified by mask to an OV51x reg. Bits that are in
+ * the same position as 1's in "mask" are cleared and set to "value". Bits
+ * that are in the same position as 0's in "mask" are preserved, regardless
+ * of their respective state in "value".
+ */
+static int
+reg_w_mask(struct usb_ov511 *ov,
+ unsigned char reg,
+ unsigned char value,
+ unsigned char mask)
+{
+ int ret;
+ unsigned char oldval, newval;
+
+ ret = reg_r(ov, reg);
+ if (ret < 0)
+ return ret;
+
+ oldval = (unsigned char) ret;
+ oldval &= (~mask); /* Clear the masked bits */
+ value &= mask; /* Enforce mask on value */
+ newval = oldval | value; /* Set the desired bits */
+
+ return (reg_w(ov, reg, newval));
+}
+
+/*
+ * Writes multiple (n) byte value to a single register. Only valid with certain
+ * registers (0x30 and 0xc4 - 0xce).
+ */
+static int
+ov518_reg_w32(struct usb_ov511 *ov, unsigned char reg, u32 val, int n)
+{
+ int rc;
+
+ PDEBUG(5, "0x%02X:%7d, n=%d", reg, val, n);
+
+ mutex_lock(&ov->cbuf_lock);
+
+ *((__le32 *)ov->cbuf) = __cpu_to_le32(val);
+
+ rc = usb_control_msg(ov->dev,
+ usb_sndctrlpipe(ov->dev, 0),
+ 1 /* REG_IO */,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, (__u16)reg, ov->cbuf, n, 1000);
+ mutex_unlock(&ov->cbuf_lock);
+
+ if (rc < 0)
+ err("reg write multiple: error %d: %s", rc,
+ symbolic(urb_errlist, rc));
+
+ return rc;
+}
+
+static int
+ov511_upload_quan_tables(struct usb_ov511 *ov)
+{
+ unsigned char *pYTable = yQuanTable511;
+ unsigned char *pUVTable = uvQuanTable511;
+ unsigned char val0, val1;
+ int i, rc, reg = R511_COMP_LUT_BEGIN;
+
+ PDEBUG(4, "Uploading quantization tables");
+
+ for (i = 0; i < OV511_QUANTABLESIZE / 2; i++) {
+ if (ENABLE_Y_QUANTABLE) {
+ val0 = *pYTable++;
+ val1 = *pYTable++;
+ val0 &= 0x0f;
+ val1 &= 0x0f;
+ val0 |= val1 << 4;
+ rc = reg_w(ov, reg, val0);
+ if (rc < 0)
+ return rc;
+ }
+
+ if (ENABLE_UV_QUANTABLE) {
+ val0 = *pUVTable++;
+ val1 = *pUVTable++;
+ val0 &= 0x0f;
+ val1 &= 0x0f;
+ val0 |= val1 << 4;
+ rc = reg_w(ov, reg + OV511_QUANTABLESIZE/2, val0);
+ if (rc < 0)
+ return rc;
+ }
+
+ reg++;
+ }
+
+ return 0;
+}
+
+/* OV518 quantization tables are 8x4 (instead of 8x8) */
+static int
+ov518_upload_quan_tables(struct usb_ov511 *ov)
+{
+ unsigned char *pYTable = yQuanTable518;
+ unsigned char *pUVTable = uvQuanTable518;
+ unsigned char val0, val1;
+ int i, rc, reg = R511_COMP_LUT_BEGIN;
+
+ PDEBUG(4, "Uploading quantization tables");
+
+ for (i = 0; i < OV518_QUANTABLESIZE / 2; i++) {
+ if (ENABLE_Y_QUANTABLE) {
+ val0 = *pYTable++;
+ val1 = *pYTable++;
+ val0 &= 0x0f;
+ val1 &= 0x0f;
+ val0 |= val1 << 4;
+ rc = reg_w(ov, reg, val0);
+ if (rc < 0)
+ return rc;
+ }
+
+ if (ENABLE_UV_QUANTABLE) {
+ val0 = *pUVTable++;
+ val1 = *pUVTable++;
+ val0 &= 0x0f;
+ val1 &= 0x0f;
+ val0 |= val1 << 4;
+ rc = reg_w(ov, reg + OV518_QUANTABLESIZE/2, val0);
+ if (rc < 0)
+ return rc;
+ }
+
+ reg++;
+ }
+
+ return 0;
+}
+
+static int
+ov51x_reset(struct usb_ov511 *ov, unsigned char reset_type)
+{
+ int rc;
+
+ /* Setting bit 0 not allowed on 518/518Plus */
+ if (ov->bclass == BCL_OV518)
+ reset_type &= 0xfe;
+
+ PDEBUG(4, "Reset: type=0x%02X", reset_type);
+
+ rc = reg_w(ov, R51x_SYS_RESET, reset_type);
+ rc = reg_w(ov, R51x_SYS_RESET, 0);
+
+ if (rc < 0)
+ err("reset: command failed");
+
+ return rc;
+}
+
+/**********************************************************************
+ *
+ * Low-level I2C I/O functions
+ *
+ **********************************************************************/
+
+/* NOTE: Do not call this function directly!
+ * The OV518 I2C I/O procedure is different, hence, this function.
+ * This is normally only called from i2c_w(). Note that this function
+ * always succeeds regardless of whether the sensor is present and working.
+ */
+static int
+ov518_i2c_write_internal(struct usb_ov511 *ov,
+ unsigned char reg,
+ unsigned char value)
+{
+ int rc;
+
+ PDEBUG(5, "0x%02X:0x%02X", reg, value);
+
+ /* Select camera register */
+ rc = reg_w(ov, R51x_I2C_SADDR_3, reg);
+ if (rc < 0)
+ return rc;
+
+ /* Write "value" to I2C data port of OV511 */
+ rc = reg_w(ov, R51x_I2C_DATA, value);
+ if (rc < 0)
+ return rc;
+
+ /* Initiate 3-byte write cycle */
+ rc = reg_w(ov, R518_I2C_CTL, 0x01);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+/* NOTE: Do not call this function directly! */
+static int
+ov511_i2c_write_internal(struct usb_ov511 *ov,
+ unsigned char reg,
+ unsigned char value)
+{
+ int rc, retries;
+
+ PDEBUG(5, "0x%02X:0x%02X", reg, value);
+
+ /* Three byte write cycle */
+ for (retries = OV511_I2C_RETRIES; ; ) {
+ /* Select camera register */
+ rc = reg_w(ov, R51x_I2C_SADDR_3, reg);
+ if (rc < 0)
+ break;
+
+ /* Write "value" to I2C data port of OV511 */
+ rc = reg_w(ov, R51x_I2C_DATA, value);
+ if (rc < 0)
+ break;
+
+ /* Initiate 3-byte write cycle */
+ rc = reg_w(ov, R511_I2C_CTL, 0x01);
+ if (rc < 0)
+ break;
+
+ /* Retry until idle */
+ do
+ rc = reg_r(ov, R511_I2C_CTL);
+ while (rc > 0 && ((rc&1) == 0));
+ if (rc < 0)
+ break;
+
+ /* Ack? */
+ if ((rc&2) == 0) {
+ rc = 0;
+ break;
+ }
+#if 0
+ /* I2C abort */
+ reg_w(ov, R511_I2C_CTL, 0x10);
+#endif
+ if (--retries < 0) {
+ err("i2c write retries exhausted");
+ rc = -1;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+/* NOTE: Do not call this function directly!
+ * The OV518 I2C I/O procedure is different, hence, this function.
+ * This is normally only called from i2c_r(). Note that this function
+ * always succeeds regardless of whether the sensor is present and working.
+ */
+static int
+ov518_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg)
+{
+ int rc, value;
+
+ /* Select camera register */
+ rc = reg_w(ov, R51x_I2C_SADDR_2, reg);
+ if (rc < 0)
+ return rc;
+
+ /* Initiate 2-byte write cycle */
+ rc = reg_w(ov, R518_I2C_CTL, 0x03);
+ if (rc < 0)
+ return rc;
+
+ /* Initiate 2-byte read cycle */
+ rc = reg_w(ov, R518_I2C_CTL, 0x05);
+ if (rc < 0)
+ return rc;
+
+ value = reg_r(ov, R51x_I2C_DATA);
+
+ PDEBUG(5, "0x%02X:0x%02X", reg, value);
+
+ return value;
+}
+
+/* NOTE: Do not call this function directly!
+ * returns: negative is error, pos or zero is data */
+static int
+ov511_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg)
+{
+ int rc, value, retries;
+
+ /* Two byte write cycle */
+ for (retries = OV511_I2C_RETRIES; ; ) {
+ /* Select camera register */
+ rc = reg_w(ov, R51x_I2C_SADDR_2, reg);
+ if (rc < 0)
+ return rc;
+
+ /* Initiate 2-byte write cycle */
+ rc = reg_w(ov, R511_I2C_CTL, 0x03);
+ if (rc < 0)
+ return rc;
+
+ /* Retry until idle */
+ do
+ rc = reg_r(ov, R511_I2C_CTL);
+ while (rc > 0 && ((rc&1) == 0));
+ if (rc < 0)
+ return rc;
+
+ if ((rc&2) == 0) /* Ack? */
+ break;
+
+ /* I2C abort */
+ reg_w(ov, R511_I2C_CTL, 0x10);
+
+ if (--retries < 0) {
+ err("i2c write retries exhausted");
+ return -1;
+ }
+ }
+
+ /* Two byte read cycle */
+ for (retries = OV511_I2C_RETRIES; ; ) {
+ /* Initiate 2-byte read cycle */
+ rc = reg_w(ov, R511_I2C_CTL, 0x05);
+ if (rc < 0)
+ return rc;
+
+ /* Retry until idle */
+ do
+ rc = reg_r(ov, R511_I2C_CTL);
+ while (rc > 0 && ((rc&1) == 0));
+ if (rc < 0)
+ return rc;
+
+ if ((rc&2) == 0) /* Ack? */
+ break;
+
+ /* I2C abort */
+ rc = reg_w(ov, R511_I2C_CTL, 0x10);
+ if (rc < 0)
+ return rc;
+
+ if (--retries < 0) {
+ err("i2c read retries exhausted");
+ return -1;
+ }
+ }
+
+ value = reg_r(ov, R51x_I2C_DATA);
+
+ PDEBUG(5, "0x%02X:0x%02X", reg, value);
+
+ /* This is needed to make i2c_w() work */
+ rc = reg_w(ov, R511_I2C_CTL, 0x05);
+ if (rc < 0)
+ return rc;
+
+ return value;
+}
+
+/* returns: negative is error, pos or zero is data */
+static int
+i2c_r(struct usb_ov511 *ov, unsigned char reg)
+{
+ int rc;
+
+ mutex_lock(&ov->i2c_lock);
+
+ if (ov->bclass == BCL_OV518)
+ rc = ov518_i2c_read_internal(ov, reg);
+ else
+ rc = ov511_i2c_read_internal(ov, reg);
+
+ mutex_unlock(&ov->i2c_lock);
+
+ return rc;
+}
+
+static int
+i2c_w(struct usb_ov511 *ov, unsigned char reg, unsigned char value)
+{
+ int rc;
+
+ mutex_lock(&ov->i2c_lock);
+
+ if (ov->bclass == BCL_OV518)
+ rc = ov518_i2c_write_internal(ov, reg, value);
+ else
+ rc = ov511_i2c_write_internal(ov, reg, value);
+
+ mutex_unlock(&ov->i2c_lock);
+
+ return rc;
+}
+
+/* Do not call this function directly! */
+static int
+ov51x_i2c_write_mask_internal(struct usb_ov511 *ov,
+ unsigned char reg,
+ unsigned char value,
+ unsigned char mask)
+{
+ int rc;
+ unsigned char oldval, newval;
+
+ if (mask == 0xff) {
+ newval = value;
+ } else {
+ if (ov->bclass == BCL_OV518)
+ rc = ov518_i2c_read_internal(ov, reg);
+ else
+ rc = ov511_i2c_read_internal(ov, reg);
+ if (rc < 0)
+ return rc;
+
+ oldval = (unsigned char) rc;
+ oldval &= (~mask); /* Clear the masked bits */
+ value &= mask; /* Enforce mask on value */
+ newval = oldval | value; /* Set the desired bits */
+ }
+
+ if (ov->bclass == BCL_OV518)
+ return (ov518_i2c_write_internal(ov, reg, newval));
+ else
+ return (ov511_i2c_write_internal(ov, reg, newval));
+}
+
+/* Writes bits at positions specified by mask to an I2C reg. Bits that are in
+ * the same position as 1's in "mask" are cleared and set to "value". Bits
+ * that are in the same position as 0's in "mask" are preserved, regardless
+ * of their respective state in "value".
+ */
+static int
+i2c_w_mask(struct usb_ov511 *ov,
+ unsigned char reg,
+ unsigned char value,
+ unsigned char mask)
+{
+ int rc;
+
+ mutex_lock(&ov->i2c_lock);
+ rc = ov51x_i2c_write_mask_internal(ov, reg, value, mask);
+ mutex_unlock(&ov->i2c_lock);
+
+ return rc;
+}
+
+/* Set the read and write slave IDs. The "slave" argument is the write slave,
+ * and the read slave will be set to (slave + 1). ov->i2c_lock should be held
+ * when calling this. This should not be called from outside the i2c I/O
+ * functions.
+ */
+static int
+i2c_set_slave_internal(struct usb_ov511 *ov, unsigned char slave)
+{
+ int rc;
+
+ rc = reg_w(ov, R51x_I2C_W_SID, slave);
+ if (rc < 0)
+ return rc;
+
+ rc = reg_w(ov, R51x_I2C_R_SID, slave + 1);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+/* Write to a specific I2C slave ID and register, using the specified mask */
+static int
+i2c_w_slave(struct usb_ov511 *ov,
+ unsigned char slave,
+ unsigned char reg,
+ unsigned char value,
+ unsigned char mask)
+{
+ int rc = 0;
+
+ mutex_lock(&ov->i2c_lock);
+
+ /* Set new slave IDs */
+ rc = i2c_set_slave_internal(ov, slave);
+ if (rc < 0)
+ goto out;
+
+ rc = ov51x_i2c_write_mask_internal(ov, reg, value, mask);
+
+out:
+ /* Restore primary IDs */
+ if (i2c_set_slave_internal(ov, ov->primary_i2c_slave) < 0)
+ err("Couldn't restore primary I2C slave");
+
+ mutex_unlock(&ov->i2c_lock);
+ return rc;
+}
+
+/* Read from a specific I2C slave ID and register */
+static int
+i2c_r_slave(struct usb_ov511 *ov,
+ unsigned char slave,
+ unsigned char reg)
+{
+ int rc;
+
+ mutex_lock(&ov->i2c_lock);
+
+ /* Set new slave IDs */
+ rc = i2c_set_slave_internal(ov, slave);
+ if (rc < 0)
+ goto out;
+
+ if (ov->bclass == BCL_OV518)
+ rc = ov518_i2c_read_internal(ov, reg);
+ else
+ rc = ov511_i2c_read_internal(ov, reg);
+
+out:
+ /* Restore primary IDs */
+ if (i2c_set_slave_internal(ov, ov->primary_i2c_slave) < 0)
+ err("Couldn't restore primary I2C slave");
+
+ mutex_unlock(&ov->i2c_lock);
+ return rc;
+}
+
+/* Sets I2C read and write slave IDs. Returns <0 for error */
+static int
+ov51x_set_slave_ids(struct usb_ov511 *ov, unsigned char sid)
+{
+ int rc;
+
+ mutex_lock(&ov->i2c_lock);
+
+ rc = i2c_set_slave_internal(ov, sid);
+ if (rc < 0)
+ goto out;
+
+ // FIXME: Is this actually necessary?
+ rc = ov51x_reset(ov, OV511_RESET_NOREGS);
+out:
+ mutex_unlock(&ov->i2c_lock);
+ return rc;
+}
+
+static int
+write_regvals(struct usb_ov511 *ov, struct ov511_regvals * pRegvals)
+{
+ int rc;
+
+ while (pRegvals->bus != OV511_DONE_BUS) {
+ if (pRegvals->bus == OV511_REG_BUS) {
+ if ((rc = reg_w(ov, pRegvals->reg, pRegvals->val)) < 0)
+ return rc;
+ } else if (pRegvals->bus == OV511_I2C_BUS) {
+ if ((rc = i2c_w(ov, pRegvals->reg, pRegvals->val)) < 0)
+ return rc;
+ } else {
+ err("Bad regval array");
+ return -1;
+ }
+ pRegvals++;
+ }
+ return 0;
+}
+
+#ifdef OV511_DEBUG
+static void
+dump_i2c_range(struct usb_ov511 *ov, int reg1, int regn)
+{
+ int i, rc;
+
+ for (i = reg1; i <= regn; i++) {
+ rc = i2c_r(ov, i);
+ info("Sensor[0x%02X] = 0x%02X", i, rc);
+ }
+}
+
+static void
+dump_i2c_regs(struct usb_ov511 *ov)
+{
+ info("I2C REGS");
+ dump_i2c_range(ov, 0x00, 0x7C);
+}
+
+static void
+dump_reg_range(struct usb_ov511 *ov, int reg1, int regn)
+{
+ int i, rc;
+
+ for (i = reg1; i <= regn; i++) {
+ rc = reg_r(ov, i);
+ info("OV511[0x%02X] = 0x%02X", i, rc);
+ }
+}
+
+static void
+ov511_dump_regs(struct usb_ov511 *ov)
+{
+ info("CAMERA INTERFACE REGS");
+ dump_reg_range(ov, 0x10, 0x1f);
+ info("DRAM INTERFACE REGS");
+ dump_reg_range(ov, 0x20, 0x23);
+ info("ISO FIFO REGS");
+ dump_reg_range(ov, 0x30, 0x31);
+ info("PIO REGS");
+ dump_reg_range(ov, 0x38, 0x39);
+ dump_reg_range(ov, 0x3e, 0x3e);
+ info("I2C REGS");
+ dump_reg_range(ov, 0x40, 0x49);
+ info("SYSTEM CONTROL REGS");
+ dump_reg_range(ov, 0x50, 0x55);
+ dump_reg_range(ov, 0x5e, 0x5f);
+ info("OmniCE REGS");
+ dump_reg_range(ov, 0x70, 0x79);
+ /* NOTE: Quantization tables are not readable. You will get the value
+ * in reg. 0x79 for every table register */
+ dump_reg_range(ov, 0x80, 0x9f);
+ dump_reg_range(ov, 0xa0, 0xbf);
+
+}
+
+static void
+ov518_dump_regs(struct usb_ov511 *ov)
+{
+ info("VIDEO MODE REGS");
+ dump_reg_range(ov, 0x20, 0x2f);
+ info("DATA PUMP AND SNAPSHOT REGS");
+ dump_reg_range(ov, 0x30, 0x3f);
+ info("I2C REGS");
+ dump_reg_range(ov, 0x40, 0x4f);
+ info("SYSTEM CONTROL AND VENDOR REGS");
+ dump_reg_range(ov, 0x50, 0x5f);
+ info("60 - 6F");
+ dump_reg_range(ov, 0x60, 0x6f);
+ info("70 - 7F");
+ dump_reg_range(ov, 0x70, 0x7f);
+ info("Y QUANTIZATION TABLE");
+ dump_reg_range(ov, 0x80, 0x8f);
+ info("UV QUANTIZATION TABLE");
+ dump_reg_range(ov, 0x90, 0x9f);
+ info("A0 - BF");
+ dump_reg_range(ov, 0xa0, 0xbf);
+ info("CBR");
+ dump_reg_range(ov, 0xc0, 0xcf);
+}
+#endif
+
+/*****************************************************************************/
+
+/* Temporarily stops OV511 from functioning. Must do this before changing
+ * registers while the camera is streaming */
+static inline int
+ov51x_stop(struct usb_ov511 *ov)
+{
+ PDEBUG(4, "stopping");
+ ov->stopped = 1;
+ if (ov->bclass == BCL_OV518)
+ return (reg_w_mask(ov, R51x_SYS_RESET, 0x3a, 0x3a));
+ else
+ return (reg_w(ov, R51x_SYS_RESET, 0x3d));
+}
+
+/* Restarts OV511 after ov511_stop() is called. Has no effect if it is not
+ * actually stopped (for performance). */
+static inline int
+ov51x_restart(struct usb_ov511 *ov)
+{
+ if (ov->stopped) {
+ PDEBUG(4, "restarting");
+ ov->stopped = 0;
+
+ /* Reinitialize the stream */
+ if (ov->bclass == BCL_OV518)
+ reg_w(ov, 0x2f, 0x80);
+
+ return (reg_w(ov, R51x_SYS_RESET, 0x00));
+ }
+
+ return 0;
+}
+
+/* Sleeps until no frames are active. Returns !0 if got signal */
+static int
+ov51x_wait_frames_inactive(struct usb_ov511 *ov)
+{
+ return wait_event_interruptible(ov->wq, ov->curframe < 0);
+}
+
+/* Resets the hardware snapshot button */
+static void
+ov51x_clear_snapshot(struct usb_ov511 *ov)
+{
+ if (ov->bclass == BCL_OV511) {
+ reg_w(ov, R51x_SYS_SNAP, 0x00);
+ reg_w(ov, R51x_SYS_SNAP, 0x02);
+ reg_w(ov, R51x_SYS_SNAP, 0x00);
+ } else if (ov->bclass == BCL_OV518) {
+ warn("snapshot reset not supported yet on OV518(+)");
+ } else {
+ err("clear snap: invalid bridge type");
+ }
+}
+
+#if 0
+/* Checks the status of the snapshot button. Returns 1 if it was pressed since
+ * it was last cleared, and zero in all other cases (including errors) */
+static int
+ov51x_check_snapshot(struct usb_ov511 *ov)
+{
+ int ret, status = 0;
+
+ if (ov->bclass == BCL_OV511) {
+ ret = reg_r(ov, R51x_SYS_SNAP);
+ if (ret < 0) {
+ err("Error checking snspshot status (%d)", ret);
+ } else if (ret & 0x08) {
+ status = 1;
+ }
+ } else if (ov->bclass == BCL_OV518) {
+ warn("snapshot check not supported yet on OV518(+)");
+ } else {
+ err("check snap: invalid bridge type");
+ }
+
+ return status;
+}
+#endif
+
+/* This does an initial reset of an OmniVision sensor and ensures that I2C
+ * is synchronized. Returns <0 for failure.
+ */
+static int
+init_ov_sensor(struct usb_ov511 *ov)
+{
+ int i, success;
+
+ /* Reset the sensor */
+ if (i2c_w(ov, 0x12, 0x80) < 0)
+ return -EIO;
+
+ /* Wait for it to initialize */
+ msleep(150);
+
+ for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) {
+ if ((i2c_r(ov, OV7610_REG_ID_HIGH) == 0x7F) &&
+ (i2c_r(ov, OV7610_REG_ID_LOW) == 0xA2)) {
+ success = 1;
+ continue;
+ }
+
+ /* Reset the sensor */
+ if (i2c_w(ov, 0x12, 0x80) < 0)
+ return -EIO;
+ /* Wait for it to initialize */
+ msleep(150);
+ /* Dummy read to sync I2C */
+ if (i2c_r(ov, 0x00) < 0)
+ return -EIO;
+ }
+
+ if (!success)
+ return -EIO;
+
+ PDEBUG(1, "I2C synced in %d attempt(s)", i);
+
+ return 0;
+}
+
+static int
+ov511_set_packet_size(struct usb_ov511 *ov, int size)
+{
+ int alt, mult;
+
+ if (ov51x_stop(ov) < 0)
+ return -EIO;
+
+ mult = size >> 5;
+
+ if (ov->bridge == BRG_OV511) {
+ if (size == 0)
+ alt = OV511_ALT_SIZE_0;
+ else if (size == 257)
+ alt = OV511_ALT_SIZE_257;
+ else if (size == 513)
+ alt = OV511_ALT_SIZE_513;
+ else if (size == 769)
+ alt = OV511_ALT_SIZE_769;
+ else if (size == 993)
+ alt = OV511_ALT_SIZE_993;
+ else {
+ err("Set packet size: invalid size (%d)", size);
+ return -EINVAL;
+ }
+ } else if (ov->bridge == BRG_OV511PLUS) {
+ if (size == 0)
+ alt = OV511PLUS_ALT_SIZE_0;
+ else if (size == 33)
+ alt = OV511PLUS_ALT_SIZE_33;
+ else if (size == 129)
+ alt = OV511PLUS_ALT_SIZE_129;
+ else if (size == 257)
+ alt = OV511PLUS_ALT_SIZE_257;
+ else if (size == 385)
+ alt = OV511PLUS_ALT_SIZE_385;
+ else if (size == 513)
+ alt = OV511PLUS_ALT_SIZE_513;
+ else if (size == 769)
+ alt = OV511PLUS_ALT_SIZE_769;
+ else if (size == 961)
+ alt = OV511PLUS_ALT_SIZE_961;
+ else {
+ err("Set packet size: invalid size (%d)", size);
+ return -EINVAL;
+ }
+ } else {
+ err("Set packet size: Invalid bridge type");
+ return -EINVAL;
+ }
+
+ PDEBUG(3, "%d, mult=%d, alt=%d", size, mult, alt);
+
+ if (reg_w(ov, R51x_FIFO_PSIZE, mult) < 0)
+ return -EIO;
+
+ if (usb_set_interface(ov->dev, ov->iface, alt) < 0) {
+ err("Set packet size: set interface error");
+ return -EBUSY;
+ }
+
+ if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0)
+ return -EIO;
+
+ ov->packet_size = size;
+
+ if (ov51x_restart(ov) < 0)
+ return -EIO;
+
+ return 0;
+}
+
+/* Note: Unlike the OV511/OV511+, the size argument does NOT include the
+ * optional packet number byte. The actual size *is* stored in ov->packet_size,
+ * though. */
+static int
+ov518_set_packet_size(struct usb_ov511 *ov, int size)
+{
+ int alt;
+
+ if (ov51x_stop(ov) < 0)
+ return -EIO;
+
+ if (ov->bclass == BCL_OV518) {
+ if (size == 0)
+ alt = OV518_ALT_SIZE_0;
+ else if (size == 128)
+ alt = OV518_ALT_SIZE_128;
+ else if (size == 256)
+ alt = OV518_ALT_SIZE_256;
+ else if (size == 384)
+ alt = OV518_ALT_SIZE_384;
+ else if (size == 512)
+ alt = OV518_ALT_SIZE_512;
+ else if (size == 640)
+ alt = OV518_ALT_SIZE_640;
+ else if (size == 768)
+ alt = OV518_ALT_SIZE_768;
+ else if (size == 896)
+ alt = OV518_ALT_SIZE_896;
+ else {
+ err("Set packet size: invalid size (%d)", size);
+ return -EINVAL;
+ }
+ } else {
+ err("Set packet size: Invalid bridge type");
+ return -EINVAL;
+ }
+
+ PDEBUG(3, "%d, alt=%d", size, alt);
+
+ ov->packet_size = size;
+ if (size > 0) {
+ /* Program ISO FIFO size reg (packet number isn't included) */
+ ov518_reg_w32(ov, 0x30, size, 2);
+
+ if (ov->packet_numbering)
+ ++ov->packet_size;
+ }
+
+ if (usb_set_interface(ov->dev, ov->iface, alt) < 0) {
+ err("Set packet size: set interface error");
+ return -EBUSY;
+ }
+
+ /* Initialize the stream */
+ if (reg_w(ov, 0x2f, 0x80) < 0)
+ return -EIO;
+
+ if (ov51x_restart(ov) < 0)
+ return -EIO;
+
+ if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0)
+ return -EIO;
+
+ return 0;
+}
+
+/* Upload compression params and quantization tables. Returns 0 for success. */
+static int
+ov511_init_compression(struct usb_ov511 *ov)
+{
+ int rc = 0;
+
+ if (!ov->compress_inited) {
+ reg_w(ov, 0x70, phy);
+ reg_w(ov, 0x71, phuv);
+ reg_w(ov, 0x72, pvy);
+ reg_w(ov, 0x73, pvuv);
+ reg_w(ov, 0x74, qhy);
+ reg_w(ov, 0x75, qhuv);
+ reg_w(ov, 0x76, qvy);
+ reg_w(ov, 0x77, qvuv);
+
+ if (ov511_upload_quan_tables(ov) < 0) {
+ err("Error uploading quantization tables");
+ rc = -EIO;
+ goto out;
+ }
+ }
+
+ ov->compress_inited = 1;
+out:
+ return rc;
+}
+
+/* Upload compression params and quantization tables. Returns 0 for success. */
+static int
+ov518_init_compression(struct usb_ov511 *ov)
+{
+ int rc = 0;
+
+ if (!ov->compress_inited) {
+ if (ov518_upload_quan_tables(ov) < 0) {
+ err("Error uploading quantization tables");
+ rc = -EIO;
+ goto out;
+ }
+ }
+
+ ov->compress_inited = 1;
+out:
+ return rc;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* Sets sensor's contrast setting to "val" */
+static int
+sensor_set_contrast(struct usb_ov511 *ov, unsigned short val)
+{
+ int rc;
+
+ PDEBUG(3, "%d", val);
+
+ if (ov->stop_during_set)
+ if (ov51x_stop(ov) < 0)
+ return -EIO;
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV6620:
+ {
+ rc = i2c_w(ov, OV7610_REG_CNT, val >> 8);
+ if (rc < 0)
+ goto out;
+ break;
+ }
+ case SEN_OV6630:
+ {
+ rc = i2c_w_mask(ov, OV7610_REG_CNT, val >> 12, 0x0f);
+ if (rc < 0)
+ goto out;
+ break;
+ }
+ case SEN_OV7620:
+ {
+ unsigned char ctab[] = {
+ 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57,
+ 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff
+ };
+
+ /* Use Y gamma control instead. Bit 0 enables it. */
+ rc = i2c_w(ov, 0x64, ctab[val>>12]);
+ if (rc < 0)
+ goto out;
+ break;
+ }
+ case SEN_SAA7111A:
+ {
+ rc = i2c_w(ov, 0x0b, val >> 9);
+ if (rc < 0)
+ goto out;
+ break;
+ }
+ default:
+ {
+ PDEBUG(3, "Unsupported with this sensor");
+ rc = -EPERM;
+ goto out;
+ }
+ }
+
+ rc = 0; /* Success */
+ ov->contrast = val;
+out:
+ if (ov51x_restart(ov) < 0)
+ return -EIO;
+
+ return rc;
+}
+
+/* Gets sensor's contrast setting */
+static int
+sensor_get_contrast(struct usb_ov511 *ov, unsigned short *val)
+{
+ int rc;
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV6620:
+ rc = i2c_r(ov, OV7610_REG_CNT);
+ if (rc < 0)
+ return rc;
+ else
+ *val = rc << 8;
+ break;
+ case SEN_OV6630:
+ rc = i2c_r(ov, OV7610_REG_CNT);
+ if (rc < 0)
+ return rc;
+ else
+ *val = rc << 12;
+ break;
+ case SEN_OV7620:
+ /* Use Y gamma reg instead. Bit 0 is the enable bit. */
+ rc = i2c_r(ov, 0x64);
+ if (rc < 0)
+ return rc;
+ else
+ *val = (rc & 0xfe) << 8;
+ break;
+ case SEN_SAA7111A:
+ *val = ov->contrast;
+ break;
+ default:
+ PDEBUG(3, "Unsupported with this sensor");
+ return -EPERM;
+ }
+
+ PDEBUG(3, "%d", *val);
+ ov->contrast = *val;
+
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* Sets sensor's brightness setting to "val" */
+static int
+sensor_set_brightness(struct usb_ov511 *ov, unsigned short val)
+{
+ int rc;
+
+ PDEBUG(4, "%d", val);
+
+ if (ov->stop_during_set)
+ if (ov51x_stop(ov) < 0)
+ return -EIO;
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV76BE:
+ case SEN_OV6620:
+ case SEN_OV6630:
+ rc = i2c_w(ov, OV7610_REG_BRT, val >> 8);
+ if (rc < 0)
+ goto out;
+ break;
+ case SEN_OV7620:
+ /* 7620 doesn't like manual changes when in auto mode */
+ if (!ov->auto_brt) {
+ rc = i2c_w(ov, OV7610_REG_BRT, val >> 8);
+ if (rc < 0)
+ goto out;
+ }
+ break;
+ case SEN_SAA7111A:
+ rc = i2c_w(ov, 0x0a, val >> 8);
+ if (rc < 0)
+ goto out;
+ break;
+ default:
+ PDEBUG(3, "Unsupported with this sensor");
+ rc = -EPERM;
+ goto out;
+ }
+
+ rc = 0; /* Success */
+ ov->brightness = val;
+out:
+ if (ov51x_restart(ov) < 0)
+ return -EIO;
+
+ return rc;
+}
+
+/* Gets sensor's brightness setting */
+static int
+sensor_get_brightness(struct usb_ov511 *ov, unsigned short *val)
+{
+ int rc;
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV76BE:
+ case SEN_OV7620:
+ case SEN_OV6620:
+ case SEN_OV6630:
+ rc = i2c_r(ov, OV7610_REG_BRT);
+ if (rc < 0)
+ return rc;
+ else
+ *val = rc << 8;
+ break;
+ case SEN_SAA7111A:
+ *val = ov->brightness;
+ break;
+ default:
+ PDEBUG(3, "Unsupported with this sensor");
+ return -EPERM;
+ }
+
+ PDEBUG(3, "%d", *val);
+ ov->brightness = *val;
+
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* Sets sensor's saturation (color intensity) setting to "val" */
+static int
+sensor_set_saturation(struct usb_ov511 *ov, unsigned short val)
+{
+ int rc;
+
+ PDEBUG(3, "%d", val);
+
+ if (ov->stop_during_set)
+ if (ov51x_stop(ov) < 0)
+ return -EIO;
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV76BE:
+ case SEN_OV6620:
+ case SEN_OV6630:
+ rc = i2c_w(ov, OV7610_REG_SAT, val >> 8);
+ if (rc < 0)
+ goto out;
+ break;
+ case SEN_OV7620:
+// /* Use UV gamma control instead. Bits 0 & 7 are reserved. */
+// rc = ov_i2c_write(ov->dev, 0x62, (val >> 9) & 0x7e);
+// if (rc < 0)
+// goto out;
+ rc = i2c_w(ov, OV7610_REG_SAT, val >> 8);
+ if (rc < 0)
+ goto out;
+ break;
+ case SEN_SAA7111A:
+ rc = i2c_w(ov, 0x0c, val >> 9);
+ if (rc < 0)
+ goto out;
+ break;
+ default:
+ PDEBUG(3, "Unsupported with this sensor");
+ rc = -EPERM;
+ goto out;
+ }
+
+ rc = 0; /* Success */
+ ov->colour = val;
+out:
+ if (ov51x_restart(ov) < 0)
+ return -EIO;
+
+ return rc;
+}
+
+/* Gets sensor's saturation (color intensity) setting */
+static int
+sensor_get_saturation(struct usb_ov511 *ov, unsigned short *val)
+{
+ int rc;
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV76BE:
+ case SEN_OV6620:
+ case SEN_OV6630:
+ rc = i2c_r(ov, OV7610_REG_SAT);
+ if (rc < 0)
+ return rc;
+ else
+ *val = rc << 8;
+ break;
+ case SEN_OV7620:
+// /* Use UV gamma reg instead. Bits 0 & 7 are reserved. */
+// rc = i2c_r(ov, 0x62);
+// if (rc < 0)
+// return rc;
+// else
+// *val = (rc & 0x7e) << 9;
+ rc = i2c_r(ov, OV7610_REG_SAT);
+ if (rc < 0)
+ return rc;
+ else
+ *val = rc << 8;
+ break;
+ case SEN_SAA7111A:
+ *val = ov->colour;
+ break;
+ default:
+ PDEBUG(3, "Unsupported with this sensor");
+ return -EPERM;
+ }
+
+ PDEBUG(3, "%d", *val);
+ ov->colour = *val;
+
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* Sets sensor's hue (red/blue balance) setting to "val" */
+static int
+sensor_set_hue(struct usb_ov511 *ov, unsigned short val)
+{
+ int rc;
+
+ PDEBUG(3, "%d", val);
+
+ if (ov->stop_during_set)
+ if (ov51x_stop(ov) < 0)
+ return -EIO;
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV6620:
+ case SEN_OV6630:
+ rc = i2c_w(ov, OV7610_REG_RED, 0xFF - (val >> 8));
+ if (rc < 0)
+ goto out;
+
+ rc = i2c_w(ov, OV7610_REG_BLUE, val >> 8);
+ if (rc < 0)
+ goto out;
+ break;
+ case SEN_OV7620:
+// Hue control is causing problems. I will enable it once it's fixed.
+#if 0
+ rc = i2c_w(ov, 0x7a, (unsigned char)(val >> 8) + 0xb);
+ if (rc < 0)
+ goto out;
+
+ rc = i2c_w(ov, 0x79, (unsigned char)(val >> 8) + 0xb);
+ if (rc < 0)
+ goto out;
+#endif
+ break;
+ case SEN_SAA7111A:
+ rc = i2c_w(ov, 0x0d, (val + 32768) >> 8);
+ if (rc < 0)
+ goto out;
+ break;
+ default:
+ PDEBUG(3, "Unsupported with this sensor");
+ rc = -EPERM;
+ goto out;
+ }
+
+ rc = 0; /* Success */
+ ov->hue = val;
+out:
+ if (ov51x_restart(ov) < 0)
+ return -EIO;
+
+ return rc;
+}
+
+/* Gets sensor's hue (red/blue balance) setting */
+static int
+sensor_get_hue(struct usb_ov511 *ov, unsigned short *val)
+{
+ int rc;
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV6620:
+ case SEN_OV6630:
+ rc = i2c_r(ov, OV7610_REG_BLUE);
+ if (rc < 0)
+ return rc;
+ else
+ *val = rc << 8;
+ break;
+ case SEN_OV7620:
+ rc = i2c_r(ov, 0x7a);
+ if (rc < 0)
+ return rc;
+ else
+ *val = rc << 8;
+ break;
+ case SEN_SAA7111A:
+ *val = ov->hue;
+ break;
+ default:
+ PDEBUG(3, "Unsupported with this sensor");
+ return -EPERM;
+ }
+
+ PDEBUG(3, "%d", *val);
+ ov->hue = *val;
+
+ return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static int
+sensor_set_picture(struct usb_ov511 *ov, struct video_picture *p)
+{
+ int rc;
+
+ PDEBUG(4, "sensor_set_picture");
+
+ ov->whiteness = p->whiteness;
+
+ /* Don't return error if a setting is unsupported, or rest of settings
+ * will not be performed */
+
+ rc = sensor_set_contrast(ov, p->contrast);
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ rc = sensor_set_brightness(ov, p->brightness);
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ rc = sensor_set_saturation(ov, p->colour);
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ rc = sensor_set_hue(ov, p->hue);
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ return 0;
+}
+
+static int
+sensor_get_picture(struct usb_ov511 *ov, struct video_picture *p)
+{
+ int rc;
+
+ PDEBUG(4, "sensor_get_picture");
+
+ /* Don't return error if a setting is unsupported, or rest of settings
+ * will not be performed */
+
+ rc = sensor_get_contrast(ov, &(p->contrast));
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ rc = sensor_get_brightness(ov, &(p->brightness));
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ rc = sensor_get_saturation(ov, &(p->colour));
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ rc = sensor_get_hue(ov, &(p->hue));
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ p->whiteness = 105 << 8;
+
+ return 0;
+}
+
+#if 0
+// FIXME: Exposure range is only 0x00-0x7f in interlace mode
+/* Sets current exposure for sensor. This only has an effect if auto-exposure
+ * is off */
+static inline int
+sensor_set_exposure(struct usb_ov511 *ov, unsigned char val)
+{
+ int rc;
+
+ PDEBUG(3, "%d", val);
+
+ if (ov->stop_during_set)
+ if (ov51x_stop(ov) < 0)
+ return -EIO;
+
+ switch (ov->sensor) {
+ case SEN_OV6620:
+ case SEN_OV6630:
+ case SEN_OV7610:
+ case SEN_OV7620:
+ case SEN_OV76BE:
+ case SEN_OV8600:
+ rc = i2c_w(ov, 0x10, val);
+ if (rc < 0)
+ goto out;
+
+ break;
+ case SEN_KS0127:
+ case SEN_KS0127B:
+ case SEN_SAA7111A:
+ PDEBUG(3, "Unsupported with this sensor");
+ return -EPERM;
+ default:
+ err("Sensor not supported for set_exposure");
+ return -EINVAL;
+ }
+
+ rc = 0; /* Success */
+ ov->exposure = val;
+out:
+ if (ov51x_restart(ov) < 0)
+ return -EIO;
+
+ return rc;
+}
+#endif
+
+/* Gets current exposure level from sensor, regardless of whether it is under
+ * manual control. */
+static int
+sensor_get_exposure(struct usb_ov511 *ov, unsigned char *val)
+{
+ int rc;
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV6620:
+ case SEN_OV6630:
+ case SEN_OV7620:
+ case SEN_OV76BE:
+ case SEN_OV8600:
+ rc = i2c_r(ov, 0x10);
+ if (rc < 0)
+ return rc;
+ else
+ *val = rc;
+ break;
+ case SEN_KS0127:
+ case SEN_KS0127B:
+ case SEN_SAA7111A:
+ val = NULL;
+ PDEBUG(3, "Unsupported with this sensor");
+ return -EPERM;
+ default:
+ err("Sensor not supported for get_exposure");
+ return -EINVAL;
+ }
+
+ PDEBUG(3, "%d", *val);
+ ov->exposure = *val;
+
+ return 0;
+}
+
+/* Turns on or off the LED. Only has an effect with OV511+/OV518(+) */
+static void
+ov51x_led_control(struct usb_ov511 *ov, int enable)
+{
+ PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+ if (ov->bridge == BRG_OV511PLUS)
+ reg_w(ov, R511_SYS_LED_CTL, enable ? 1 : 0);
+ else if (ov->bclass == BCL_OV518)
+ reg_w_mask(ov, R518_GPIO_OUT, enable ? 0x02 : 0x00, 0x02);
+
+ return;
+}
+
+/* Matches the sensor's internal frame rate to the lighting frequency.
+ * Valid frequencies are:
+ * 50 - 50Hz, for European and Asian lighting
+ * 60 - 60Hz, for American lighting
+ *
+ * Tested with: OV7610, OV7620, OV76BE, OV6620
+ * Unsupported: KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static int
+sensor_set_light_freq(struct usb_ov511 *ov, int freq)
+{
+ int sixty;
+
+ PDEBUG(4, "%d Hz", freq);
+
+ if (freq == 60)
+ sixty = 1;
+ else if (freq == 50)
+ sixty = 0;
+ else {
+ err("Invalid light freq (%d Hz)", freq);
+ return -EINVAL;
+ }
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ i2c_w_mask(ov, 0x2a, sixty?0x00:0x80, 0x80);
+ i2c_w(ov, 0x2b, sixty?0x00:0xac);
+ i2c_w_mask(ov, 0x13, 0x10, 0x10);
+ i2c_w_mask(ov, 0x13, 0x00, 0x10);
+ break;
+ case SEN_OV7620:
+ case SEN_OV76BE:
+ case SEN_OV8600:
+ i2c_w_mask(ov, 0x2a, sixty?0x00:0x80, 0x80);
+ i2c_w(ov, 0x2b, sixty?0x00:0xac);
+ i2c_w_mask(ov, 0x76, 0x01, 0x01);
+ break;
+ case SEN_OV6620:
+ case SEN_OV6630:
+ i2c_w(ov, 0x2b, sixty?0xa8:0x28);
+ i2c_w(ov, 0x2a, sixty?0x84:0xa4);
+ break;
+ case SEN_KS0127:
+ case SEN_KS0127B:
+ case SEN_SAA7111A:
+ PDEBUG(5, "Unsupported with this sensor");
+ return -EPERM;
+ default:
+ err("Sensor not supported for set_light_freq");
+ return -EINVAL;
+ }
+
+ ov->lightfreq = freq;
+
+ return 0;
+}
+
+/* If enable is true, turn on the sensor's banding filter, otherwise turn it
+ * off. This filter tries to reduce the pattern of horizontal light/dark bands
+ * caused by some (usually fluorescent) lighting. The light frequency must be
+ * set either before or after enabling it with ov51x_set_light_freq().
+ *
+ * Tested with: OV7610, OV7620, OV76BE, OV6620.
+ * Unsupported: KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static int
+sensor_set_banding_filter(struct usb_ov511 *ov, int enable)
+{
+ int rc;
+
+ PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+ if (ov->sensor == SEN_KS0127 || ov->sensor == SEN_KS0127B
+ || ov->sensor == SEN_SAA7111A) {
+ PDEBUG(5, "Unsupported with this sensor");
+ return -EPERM;
+ }
+
+ rc = i2c_w_mask(ov, 0x2d, enable?0x04:0x00, 0x04);
+ if (rc < 0)
+ return rc;
+
+ ov->bandfilt = enable;
+
+ return 0;
+}
+
+/* If enable is true, turn on the sensor's auto brightness control, otherwise
+ * turn it off.
+ *
+ * Unsupported: KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static int
+sensor_set_auto_brightness(struct usb_ov511 *ov, int enable)
+{
+ int rc;
+
+ PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+ if (ov->sensor == SEN_KS0127 || ov->sensor == SEN_KS0127B
+ || ov->sensor == SEN_SAA7111A) {
+ PDEBUG(5, "Unsupported with this sensor");
+ return -EPERM;
+ }
+
+ rc = i2c_w_mask(ov, 0x2d, enable?0x10:0x00, 0x10);
+ if (rc < 0)
+ return rc;
+
+ ov->auto_brt = enable;
+
+ return 0;
+}
+
+/* If enable is true, turn on the sensor's auto exposure control, otherwise
+ * turn it off.
+ *
+ * Unsupported: KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static int
+sensor_set_auto_exposure(struct usb_ov511 *ov, int enable)
+{
+ PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ i2c_w_mask(ov, 0x29, enable?0x00:0x80, 0x80);
+ break;
+ case SEN_OV6620:
+ case SEN_OV7620:
+ case SEN_OV76BE:
+ case SEN_OV8600:
+ i2c_w_mask(ov, 0x13, enable?0x01:0x00, 0x01);
+ break;
+ case SEN_OV6630:
+ i2c_w_mask(ov, 0x28, enable?0x00:0x10, 0x10);
+ break;
+ case SEN_KS0127:
+ case SEN_KS0127B:
+ case SEN_SAA7111A:
+ PDEBUG(5, "Unsupported with this sensor");
+ return -EPERM;
+ default:
+ err("Sensor not supported for set_auto_exposure");
+ return -EINVAL;
+ }
+
+ ov->auto_exp = enable;
+
+ return 0;
+}
+
+/* Modifies the sensor's exposure algorithm to allow proper exposure of objects
+ * that are illuminated from behind.
+ *
+ * Tested with: OV6620, OV7620
+ * Unsupported: OV7610, OV76BE, KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static int
+sensor_set_backlight(struct usb_ov511 *ov, int enable)
+{
+ PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+ switch (ov->sensor) {
+ case SEN_OV7620:
+ case SEN_OV8600:
+ i2c_w_mask(ov, 0x68, enable?0xe0:0xc0, 0xe0);
+ i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08);
+ i2c_w_mask(ov, 0x28, enable?0x02:0x00, 0x02);
+ break;
+ case SEN_OV6620:
+ i2c_w_mask(ov, 0x4e, enable?0xe0:0xc0, 0xe0);
+ i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08);
+ i2c_w_mask(ov, 0x0e, enable?0x80:0x00, 0x80);
+ break;
+ case SEN_OV6630:
+ i2c_w_mask(ov, 0x4e, enable?0x80:0x60, 0xe0);
+ i2c_w_mask(ov, 0x29, enable?0x08:0x00, 0x08);
+ i2c_w_mask(ov, 0x28, enable?0x02:0x00, 0x02);
+ break;
+ case SEN_OV7610:
+ case SEN_OV76BE:
+ case SEN_KS0127:
+ case SEN_KS0127B:
+ case SEN_SAA7111A:
+ PDEBUG(5, "Unsupported with this sensor");
+ return -EPERM;
+ default:
+ err("Sensor not supported for set_backlight");
+ return -EINVAL;
+ }
+
+ ov->backlight = enable;
+
+ return 0;
+}
+
+static int
+sensor_set_mirror(struct usb_ov511 *ov, int enable)
+{
+ PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+ switch (ov->sensor) {
+ case SEN_OV6620:
+ case SEN_OV6630:
+ case SEN_OV7610:
+ case SEN_OV7620:
+ case SEN_OV76BE:
+ case SEN_OV8600:
+ i2c_w_mask(ov, 0x12, enable?0x40:0x00, 0x40);
+ break;
+ case SEN_KS0127:
+ case SEN_KS0127B:
+ case SEN_SAA7111A:
+ PDEBUG(5, "Unsupported with this sensor");
+ return -EPERM;
+ default:
+ err("Sensor not supported for set_mirror");
+ return -EINVAL;
+ }
+
+ ov->mirror = enable;
+
+ return 0;
+}
+
+/* Returns number of bits per pixel (regardless of where they are located;
+ * planar or not), or zero for unsupported format.
+ */
+static inline int
+get_depth(int palette)
+{
+ switch (palette) {
+ case VIDEO_PALETTE_GREY: return 8;
+ case VIDEO_PALETTE_YUV420: return 12;
+ case VIDEO_PALETTE_YUV420P: return 12; /* Planar */
+ default: return 0; /* Invalid format */
+ }
+}
+
+/* Bytes per frame. Used by read(). Return of 0 indicates error */
+static inline long int
+get_frame_length(struct ov511_frame *frame)
+{
+ if (!frame)
+ return 0;
+ else
+ return ((frame->width * frame->height
+ * get_depth(frame->format)) >> 3);
+}
+
+static int
+mode_init_ov_sensor_regs(struct usb_ov511 *ov, int width, int height,
+ int mode, int sub_flag, int qvga)
+{
+ int clock;
+
+ /******** Mode (VGA/QVGA) and sensor specific regs ********/
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ i2c_w(ov, 0x14, qvga?0x24:0x04);
+// FIXME: Does this improve the image quality or frame rate?
+#if 0
+ i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20);
+ i2c_w(ov, 0x24, 0x10);
+ i2c_w(ov, 0x25, qvga?0x40:0x8a);
+ i2c_w(ov, 0x2f, qvga?0x30:0xb0);
+ i2c_w(ov, 0x35, qvga?0x1c:0x9c);
+#endif
+ break;
+ case SEN_OV7620:
+// i2c_w(ov, 0x2b, 0x00);
+ i2c_w(ov, 0x14, qvga?0xa4:0x84);
+ i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20);
+ i2c_w(ov, 0x24, qvga?0x20:0x3a);
+ i2c_w(ov, 0x25, qvga?0x30:0x60);
+ i2c_w_mask(ov, 0x2d, qvga?0x40:0x00, 0x40);
+ i2c_w_mask(ov, 0x67, qvga?0xf0:0x90, 0xf0);
+ i2c_w_mask(ov, 0x74, qvga?0x20:0x00, 0x20);
+ break;
+ case SEN_OV76BE:
+// i2c_w(ov, 0x2b, 0x00);
+ i2c_w(ov, 0x14, qvga?0xa4:0x84);
+// FIXME: Enable this once 7620AE uses 7620 initial settings
+#if 0
+ i2c_w_mask(ov, 0x28, qvga?0x00:0x20, 0x20);
+ i2c_w(ov, 0x24, qvga?0x20:0x3a);
+ i2c_w(ov, 0x25, qvga?0x30:0x60);
+ i2c_w_mask(ov, 0x2d, qvga?0x40:0x00, 0x40);
+ i2c_w_mask(ov, 0x67, qvga?0xb0:0x90, 0xf0);
+ i2c_w_mask(ov, 0x74, qvga?0x20:0x00, 0x20);
+#endif
+ break;
+ case SEN_OV6620:
+ i2c_w(ov, 0x14, qvga?0x24:0x04);
+ break;
+ case SEN_OV6630:
+ i2c_w(ov, 0x14, qvga?0xa0:0x80);
+ break;
+ default:
+ err("Invalid sensor");
+ return -EINVAL;
+ }
+
+ /******** Palette-specific regs ********/
+
+ if (mode == VIDEO_PALETTE_GREY) {
+ if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) {
+ /* these aren't valid on the OV6620/OV7620/6630? */
+ i2c_w_mask(ov, 0x0e, 0x40, 0x40);
+ }
+
+ if (ov->sensor == SEN_OV6630 && ov->bridge == BRG_OV518
+ && ov518_color) {
+ i2c_w_mask(ov, 0x12, 0x00, 0x10);
+ i2c_w_mask(ov, 0x13, 0x00, 0x20);
+ } else {
+ i2c_w_mask(ov, 0x13, 0x20, 0x20);
+ }
+ } else {
+ if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) {
+ /* not valid on the OV6620/OV7620/6630? */
+ i2c_w_mask(ov, 0x0e, 0x00, 0x40);
+ }
+
+ /* The OV518 needs special treatment. Although both the OV518
+ * and the OV6630 support a 16-bit video bus, only the 8 bit Y
+ * bus is actually used. The UV bus is tied to ground.
+ * Therefore, the OV6630 needs to be in 8-bit multiplexed
+ * output mode */
+
+ if (ov->sensor == SEN_OV6630 && ov->bridge == BRG_OV518
+ && ov518_color) {
+ i2c_w_mask(ov, 0x12, 0x10, 0x10);
+ i2c_w_mask(ov, 0x13, 0x20, 0x20);
+ } else {
+ i2c_w_mask(ov, 0x13, 0x00, 0x20);
+ }
+ }
+
+ /******** Clock programming ********/
+
+ /* The OV6620 needs special handling. This prevents the
+ * severe banding that normally occurs */
+ if (ov->sensor == SEN_OV6620 || ov->sensor == SEN_OV6630)
+ {
+ /* Clock down */
+
+ i2c_w(ov, 0x2a, 0x04);
+
+ if (ov->compress) {
+// clock = 0; /* This ensures the highest frame rate */
+ clock = 3;
+ } else if (clockdiv == -1) { /* If user didn't override it */
+ clock = 3; /* Gives better exposure time */
+ } else {
+ clock = clockdiv;
+ }
+
+ PDEBUG(4, "Setting clock divisor to %d", clock);
+
+ i2c_w(ov, 0x11, clock);
+
+ i2c_w(ov, 0x2a, 0x84);
+ /* This next setting is critical. It seems to improve
+ * the gain or the contrast. The "reserved" bits seem
+ * to have some effect in this case. */
+ i2c_w(ov, 0x2d, 0x85);
+ }
+ else
+ {
+ if (ov->compress) {
+ clock = 1; /* This ensures the highest frame rate */
+ } else if (clockdiv == -1) { /* If user didn't override it */
+ /* Calculate and set the clock divisor */
+ clock = ((sub_flag ? ov->subw * ov->subh
+ : width * height)
+ * (mode == VIDEO_PALETTE_GREY ? 2 : 3) / 2)
+ / 66000;
+ } else {
+ clock = clockdiv;
+ }
+
+ PDEBUG(4, "Setting clock divisor to %d", clock);
+
+ i2c_w(ov, 0x11, clock);
+ }
+
+ /******** Special Features ********/
+
+ if (framedrop >= 0)
+ i2c_w(ov, 0x16, framedrop);
+
+ /* Test Pattern */
+ i2c_w_mask(ov, 0x12, (testpat?0x02:0x00), 0x02);
+
+ /* Enable auto white balance */
+ i2c_w_mask(ov, 0x12, 0x04, 0x04);
+
+ // This will go away as soon as ov51x_mode_init_sensor_regs()
+ // is fully tested.
+ /* 7620/6620/6630? don't have register 0x35, so play it safe */
+ if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) {
+ if (width == 640 && height == 480)
+ i2c_w(ov, 0x35, 0x9e);
+ else
+ i2c_w(ov, 0x35, 0x1e);
+ }
+
+ return 0;
+}
+
+static int
+set_ov_sensor_window(struct usb_ov511 *ov, int width, int height, int mode,
+ int sub_flag)
+{
+ int ret;
+ int hwsbase, hwebase, vwsbase, vwebase, hwsize, vwsize;
+ int hoffset, voffset, hwscale = 0, vwscale = 0;
+
+ /* The different sensor ICs handle setting up of window differently.
+ * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!!! */
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV76BE:
+ hwsbase = 0x38;
+ hwebase = 0x3a;
+ vwsbase = vwebase = 0x05;
+ break;
+ case SEN_OV6620:
+ case SEN_OV6630:
+ hwsbase = 0x38;
+ hwebase = 0x3a;
+ vwsbase = 0x05;
+ vwebase = 0x06;
+ break;
+ case SEN_OV7620:
+ hwsbase = 0x2f; /* From 7620.SET (spec is wrong) */
+ hwebase = 0x2f;
+ vwsbase = vwebase = 0x05;
+ break;
+ default:
+ err("Invalid sensor");
+ return -EINVAL;
+ }
+
+ if (ov->sensor == SEN_OV6620 || ov->sensor == SEN_OV6630) {
+ /* Note: OV518(+) does downsample on its own) */
+ if ((width > 176 && height > 144)
+ || ov->bclass == BCL_OV518) { /* CIF */
+ ret = mode_init_ov_sensor_regs(ov, width, height,
+ mode, sub_flag, 0);
+ if (ret < 0)
+ return ret;
+ hwscale = 1;
+ vwscale = 1; /* The datasheet says 0; it's wrong */
+ hwsize = 352;
+ vwsize = 288;
+ } else if (width > 176 || height > 144) {
+ err("Illegal dimensions");
+ return -EINVAL;
+ } else { /* QCIF */
+ ret = mode_init_ov_sensor_regs(ov, width, height,
+ mode, sub_flag, 1);
+ if (ret < 0)
+ return ret;
+ hwsize = 176;
+ vwsize = 144;
+ }
+ } else {
+ if (width > 320 && height > 240) { /* VGA */
+ ret = mode_init_ov_sensor_regs(ov, width, height,
+ mode, sub_flag, 0);
+ if (ret < 0)
+ return ret;
+ hwscale = 2;
+ vwscale = 1;
+ hwsize = 640;
+ vwsize = 480;
+ } else if (width > 320 || height > 240) {
+ err("Illegal dimensions");
+ return -EINVAL;
+ } else { /* QVGA */
+ ret = mode_init_ov_sensor_regs(ov, width, height,
+ mode, sub_flag, 1);
+ if (ret < 0)
+ return ret;
+ hwscale = 1;
+ hwsize = 320;
+ vwsize = 240;
+ }
+ }
+
+ /* Center the window */
+ hoffset = ((hwsize - width) / 2) >> hwscale;
+ voffset = ((vwsize - height) / 2) >> vwscale;
+
+ /* FIXME! - This needs to be changed to support 160x120 and 6620!!! */
+ if (sub_flag) {
+ i2c_w(ov, 0x17, hwsbase+(ov->subx>>hwscale));
+ i2c_w(ov, 0x18, hwebase+((ov->subx+ov->subw)>>hwscale));
+ i2c_w(ov, 0x19, vwsbase+(ov->suby>>vwscale));
+ i2c_w(ov, 0x1a, vwebase+((ov->suby+ov->subh)>>vwscale));
+ } else {
+ i2c_w(ov, 0x17, hwsbase + hoffset);
+ i2c_w(ov, 0x18, hwebase + hoffset + (hwsize>>hwscale));
+ i2c_w(ov, 0x19, vwsbase + voffset);
+ i2c_w(ov, 0x1a, vwebase + voffset + (vwsize>>vwscale));
+ }
+
+#ifdef OV511_DEBUG
+ if (dump_sensor)
+ dump_i2c_regs(ov);
+#endif
+
+ return 0;
+}
+
+/* Set up the OV511/OV511+ with the given image parameters.
+ *
+ * Do not put any sensor-specific code in here (including I2C I/O functions)
+ */
+static int
+ov511_mode_init_regs(struct usb_ov511 *ov,
+ int width, int height, int mode, int sub_flag)
+{
+ int hsegs, vsegs;
+
+ if (sub_flag) {
+ width = ov->subw;
+ height = ov->subh;
+ }
+
+ PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d",
+ width, height, mode, sub_flag);
+
+ // FIXME: This should be moved to a 7111a-specific function once
+ // subcapture is dealt with properly
+ if (ov->sensor == SEN_SAA7111A) {
+ if (width == 320 && height == 240) {
+ /* No need to do anything special */
+ } else if (width == 640 && height == 480) {
+ /* Set the OV511 up as 320x480, but keep the
+ * V4L resolution as 640x480 */
+ width = 320;
+ } else {
+ err("SAA7111A only allows 320x240 or 640x480");
+ return -EINVAL;
+ }
+ }
+
+ /* Make sure width and height are a multiple of 8 */
+ if (width % 8 || height % 8) {
+ err("Invalid size (%d, %d) (mode = %d)", width, height, mode);
+ return -EINVAL;
+ }
+
+ if (width < ov->minwidth || height < ov->minheight) {
+ err("Requested dimensions are too small");
+ return -EINVAL;
+ }
+
+ if (ov51x_stop(ov) < 0)
+ return -EIO;
+
+ if (mode == VIDEO_PALETTE_GREY) {
+ reg_w(ov, R511_CAM_UV_EN, 0x00);
+ reg_w(ov, R511_SNAP_UV_EN, 0x00);
+ reg_w(ov, R511_SNAP_OPTS, 0x01);
+ } else {
+ reg_w(ov, R511_CAM_UV_EN, 0x01);
+ reg_w(ov, R511_SNAP_UV_EN, 0x01);
+ reg_w(ov, R511_SNAP_OPTS, 0x03);
+ }
+
+ /* Here I'm assuming that snapshot size == image size.
+ * I hope that's always true. --claudio
+ */
+ hsegs = (width >> 3) - 1;
+ vsegs = (height >> 3) - 1;
+
+ reg_w(ov, R511_CAM_PXCNT, hsegs);
+ reg_w(ov, R511_CAM_LNCNT, vsegs);
+ reg_w(ov, R511_CAM_PXDIV, 0x00);
+ reg_w(ov, R511_CAM_LNDIV, 0x00);
+
+ /* YUV420, low pass filter on */
+ reg_w(ov, R511_CAM_OPTS, 0x03);
+
+ /* Snapshot additions */
+ reg_w(ov, R511_SNAP_PXCNT, hsegs);
+ reg_w(ov, R511_SNAP_LNCNT, vsegs);
+ reg_w(ov, R511_SNAP_PXDIV, 0x00);
+ reg_w(ov, R511_SNAP_LNDIV, 0x00);
+
+ if (ov->compress) {
+ /* Enable Y and UV quantization and compression */
+ reg_w(ov, R511_COMP_EN, 0x07);
+ reg_w(ov, R511_COMP_LUT_EN, 0x03);
+ ov51x_reset(ov, OV511_RESET_OMNICE);
+ }
+
+ if (ov51x_restart(ov) < 0)
+ return -EIO;
+
+ return 0;
+}
+
+/* Sets up the OV518/OV518+ with the given image parameters
+ *
+ * OV518 needs a completely different approach, until we can figure out what
+ * the individual registers do. Also, only 15 FPS is supported now.
+ *
+ * Do not put any sensor-specific code in here (including I2C I/O functions)
+ */
+static int
+ov518_mode_init_regs(struct usb_ov511 *ov,
+ int width, int height, int mode, int sub_flag)
+{
+ int hsegs, vsegs, hi_res;
+
+ if (sub_flag) {
+ width = ov->subw;
+ height = ov->subh;
+ }
+
+ PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d",
+ width, height, mode, sub_flag);
+
+ if (width % 16 || height % 8) {
+ err("Invalid size (%d, %d)", width, height);
+ return -EINVAL;
+ }
+
+ if (width < ov->minwidth || height < ov->minheight) {
+ err("Requested dimensions are too small");
+ return -EINVAL;
+ }
+
+ if (width >= 320 && height >= 240) {
+ hi_res = 1;
+ } else if (width >= 320 || height >= 240) {
+ err("Invalid width/height combination (%d, %d)", width, height);
+ return -EINVAL;
+ } else {
+ hi_res = 0;
+ }
+
+ if (ov51x_stop(ov) < 0)
+ return -EIO;
+
+ /******** Set the mode ********/
+
+ reg_w(ov, 0x2b, 0);
+ reg_w(ov, 0x2c, 0);
+ reg_w(ov, 0x2d, 0);
+ reg_w(ov, 0x2e, 0);
+ reg_w(ov, 0x3b, 0);
+ reg_w(ov, 0x3c, 0);
+ reg_w(ov, 0x3d, 0);
+ reg_w(ov, 0x3e, 0);
+
+ if (ov->bridge == BRG_OV518 && ov518_color) {
+ /* OV518 needs U and V swapped */
+ i2c_w_mask(ov, 0x15, 0x00, 0x01);
+
+ if (mode == VIDEO_PALETTE_GREY) {
+ /* Set 16-bit input format (UV data are ignored) */
+ reg_w_mask(ov, 0x20, 0x00, 0x08);
+
+ /* Set 8-bit (4:0:0) output format */
+ reg_w_mask(ov, 0x28, 0x00, 0xf0);
+ reg_w_mask(ov, 0x38, 0x00, 0xf0);
+ } else {
+ /* Set 8-bit (YVYU) input format */
+ reg_w_mask(ov, 0x20, 0x08, 0x08);
+
+ /* Set 12-bit (4:2:0) output format */
+ reg_w_mask(ov, 0x28, 0x80, 0xf0);
+ reg_w_mask(ov, 0x38, 0x80, 0xf0);
+ }
+ } else {
+ reg_w(ov, 0x28, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80);
+ reg_w(ov, 0x38, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80);
+ }
+
+ hsegs = width / 16;
+ vsegs = height / 4;
+
+ reg_w(ov, 0x29, hsegs);
+ reg_w(ov, 0x2a, vsegs);
+
+ reg_w(ov, 0x39, hsegs);
+ reg_w(ov, 0x3a, vsegs);
+
+ /* Windows driver does this here; who knows why */
+ reg_w(ov, 0x2f, 0x80);
+
+ /******** Set the framerate (to 15 FPS) ********/
+
+ /* Mode independent, but framerate dependent, regs */
+ reg_w(ov, 0x51, 0x02); /* Clock divider; lower==faster */
+ reg_w(ov, 0x22, 0x18);
+ reg_w(ov, 0x23, 0xff);
+
+ if (ov->bridge == BRG_OV518PLUS)
+ reg_w(ov, 0x21, 0x19);
+ else
+ reg_w(ov, 0x71, 0x19); /* Compression-related? */
+
+ // FIXME: Sensor-specific
+ /* Bit 5 is what matters here. Of course, it is "reserved" */
+ i2c_w(ov, 0x54, 0x23);
+
+ reg_w(ov, 0x2f, 0x80);
+
+ if (ov->bridge == BRG_OV518PLUS) {
+ reg_w(ov, 0x24, 0x94);
+ reg_w(ov, 0x25, 0x90);
+ ov518_reg_w32(ov, 0xc4, 400, 2); /* 190h */
+ ov518_reg_w32(ov, 0xc6, 540, 2); /* 21ch */
+ ov518_reg_w32(ov, 0xc7, 540, 2); /* 21ch */
+ ov518_reg_w32(ov, 0xc8, 108, 2); /* 6ch */
+ ov518_reg_w32(ov, 0xca, 131098, 3); /* 2001ah */
+ ov518_reg_w32(ov, 0xcb, 532, 2); /* 214h */
+ ov518_reg_w32(ov, 0xcc, 2400, 2); /* 960h */
+ ov518_reg_w32(ov, 0xcd, 32, 2); /* 20h */
+ ov518_reg_w32(ov, 0xce, 608, 2); /* 260h */
+ } else {
+ reg_w(ov, 0x24, 0x9f);
+ reg_w(ov, 0x25, 0x90);
+ ov518_reg_w32(ov, 0xc4, 400, 2); /* 190h */
+ ov518_reg_w32(ov, 0xc6, 500, 2); /* 1f4h */
+ ov518_reg_w32(ov, 0xc7, 500, 2); /* 1f4h */
+ ov518_reg_w32(ov, 0xc8, 142, 2); /* 8eh */
+ ov518_reg_w32(ov, 0xca, 131098, 3); /* 2001ah */
+ ov518_reg_w32(ov, 0xcb, 532, 2); /* 214h */
+ ov518_reg_w32(ov, 0xcc, 2000, 2); /* 7d0h */
+ ov518_reg_w32(ov, 0xcd, 32, 2); /* 20h */
+ ov518_reg_w32(ov, 0xce, 608, 2); /* 260h */
+ }
+
+ reg_w(ov, 0x2f, 0x80);
+
+ if (ov51x_restart(ov) < 0)
+ return -EIO;
+
+ /* Reset it just for good measure */
+ if (ov51x_reset(ov, OV511_RESET_NOREGS) < 0)
+ return -EIO;
+
+ return 0;
+}
+
+/* This is a wrapper around the OV511, OV518, and sensor specific functions */
+static int
+mode_init_regs(struct usb_ov511 *ov,
+ int width, int height, int mode, int sub_flag)
+{
+ int rc = 0;
+
+ if (!ov || !ov->dev)
+ return -EFAULT;
+
+ if (ov->bclass == BCL_OV518) {
+ rc = ov518_mode_init_regs(ov, width, height, mode, sub_flag);
+ } else {
+ rc = ov511_mode_init_regs(ov, width, height, mode, sub_flag);
+ }
+
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ switch (ov->sensor) {
+ case SEN_OV7610:
+ case SEN_OV7620:
+ case SEN_OV76BE:
+ case SEN_OV8600:
+ case SEN_OV6620:
+ case SEN_OV6630:
+ rc = set_ov_sensor_window(ov, width, height, mode, sub_flag);
+ break;
+ case SEN_KS0127:
+ case SEN_KS0127B:
+ err("KS0127-series decoders not supported yet");
+ rc = -EINVAL;
+ break;
+ case SEN_SAA7111A:
+// rc = mode_init_saa_sensor_regs(ov, width, height, mode,
+// sub_flag);
+
+ PDEBUG(1, "SAA status = 0x%02X", i2c_r(ov, 0x1f));
+ break;
+ default:
+ err("Unknown sensor");
+ rc = -EINVAL;
+ }
+
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ /* Sensor-independent settings */
+ rc = sensor_set_auto_brightness(ov, ov->auto_brt);
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ rc = sensor_set_auto_exposure(ov, ov->auto_exp);
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ rc = sensor_set_banding_filter(ov, bandingfilter);
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ if (ov->lightfreq) {
+ rc = sensor_set_light_freq(ov, lightfreq);
+ if (FATAL_ERROR(rc))
+ return rc;
+ }
+
+ rc = sensor_set_backlight(ov, ov->backlight);
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ rc = sensor_set_mirror(ov, ov->mirror);
+ if (FATAL_ERROR(rc))
+ return rc;
+
+ return 0;
+}
+
+/* This sets the default image parameters. This is useful for apps that use
+ * read() and do not set these.
+ */
+static int
+ov51x_set_default_params(struct usb_ov511 *ov)
+{
+ int i;
+
+ /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
+ * (using read() instead). */
+ for (i = 0; i < OV511_NUMFRAMES; i++) {
+ ov->frame[i].width = ov->maxwidth;
+ ov->frame[i].height = ov->maxheight;
+ ov->frame[i].bytes_read = 0;
+ if (force_palette)
+ ov->frame[i].format = force_palette;
+ else
+ ov->frame[i].format = VIDEO_PALETTE_YUV420;
+
+ ov->frame[i].depth = get_depth(ov->frame[i].format);
+ }
+
+ PDEBUG(3, "%dx%d, %s", ov->maxwidth, ov->maxheight,
+ symbolic(v4l1_plist, ov->frame[0].format));
+
+ /* Initialize to max width/height, YUV420 or RGB24 (if supported) */
+ if (mode_init_regs(ov, ov->maxwidth, ov->maxheight,
+ ov->frame[0].format, 0) < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**********************************************************************
+ *
+ * Video decoder stuff
+ *
+ **********************************************************************/
+
+/* Set analog input port of decoder */
+static int
+decoder_set_input(struct usb_ov511 *ov, int input)
+{
+ PDEBUG(4, "port %d", input);
+
+ switch (ov->sensor) {
+ case SEN_SAA7111A:
+ {
+ /* Select mode */
+ i2c_w_mask(ov, 0x02, input, 0x07);
+ /* Bypass chrominance trap for modes 4..7 */
+ i2c_w_mask(ov, 0x09, (input > 3) ? 0x80:0x00, 0x80);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Get ASCII name of video input */
+static int
+decoder_get_input_name(struct usb_ov511 *ov, int input, char *name)
+{
+ switch (ov->sensor) {
+ case SEN_SAA7111A:
+ {
+ if (input < 0 || input > 7)
+ return -EINVAL;
+ else if (input < 4)
+ sprintf(name, "CVBS-%d", input);
+ else // if (input < 8)
+ sprintf(name, "S-Video-%d", input - 4);
+ break;
+ }
+ default:
+ sprintf(name, "%s", "Camera");
+ }
+
+ return 0;
+}
+
+/* Set norm (NTSC, PAL, SECAM, AUTO) */
+static int
+decoder_set_norm(struct usb_ov511 *ov, int norm)
+{
+ PDEBUG(4, "%d", norm);
+
+ switch (ov->sensor) {
+ case SEN_SAA7111A:
+ {
+ int reg_8, reg_e;
+
+ if (norm == VIDEO_MODE_NTSC) {
+ reg_8 = 0x40; /* 60 Hz */
+ reg_e = 0x00; /* NTSC M / PAL BGHI */
+ } else if (norm == VIDEO_MODE_PAL) {
+ reg_8 = 0x00; /* 50 Hz */
+ reg_e = 0x00; /* NTSC M / PAL BGHI */
+ } else if (norm == VIDEO_MODE_AUTO) {
+ reg_8 = 0x80; /* Auto field detect */
+ reg_e = 0x00; /* NTSC M / PAL BGHI */
+ } else if (norm == VIDEO_MODE_SECAM) {
+ reg_8 = 0x00; /* 50 Hz */
+ reg_e = 0x50; /* SECAM / PAL 4.43 */
+ } else {
+ return -EINVAL;
+ }
+
+ i2c_w_mask(ov, 0x08, reg_8, 0xc0);
+ i2c_w_mask(ov, 0x0e, reg_e, 0x70);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**********************************************************************
+ *
+ * Raw data parsing
+ *
+ **********************************************************************/
+
+/* Copies a 64-byte segment at pIn to an 8x8 block at pOut. The width of the
+ * image at pOut is specified by w.
+ */
+static inline void
+make_8x8(unsigned char *pIn, unsigned char *pOut, int w)
+{
+ unsigned char *pOut1 = pOut;
+ int x, y;
+
+ for (y = 0; y < 8; y++) {
+ pOut1 = pOut;
+ for (x = 0; x < 8; x++) {
+ *pOut1++ = *pIn++;
+ }
+ pOut += w;
+ }
+}
+
+/*
+ * For RAW BW (YUV 4:0:0) images, data show up in 256 byte segments.
+ * The segments represent 4 squares of 8x8 pixels as follows:
+ *
+ * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199
+ * 8 9 ... 15 72 73 ... 79 200 201 ... 207
+ * ... ... ...
+ * 56 57 ... 63 120 121 ... 127 248 249 ... 255
+ *
+ */
+static void
+yuv400raw_to_yuv400p(struct ov511_frame *frame,
+ unsigned char *pIn0, unsigned char *pOut0)
+{
+ int x, y;
+ unsigned char *pIn, *pOut, *pOutLine;
+
+ /* Copy Y */
+ pIn = pIn0;
+ pOutLine = pOut0;
+ for (y = 0; y < frame->rawheight - 1; y += 8) {
+ pOut = pOutLine;
+ for (x = 0; x < frame->rawwidth - 1; x += 8) {
+ make_8x8(pIn, pOut, frame->rawwidth);
+ pIn += 64;
+ pOut += 8;
+ }
+ pOutLine += 8 * frame->rawwidth;
+ }
+}
+
+/*
+ * For YUV 4:2:0 images, the data show up in 384 byte segments.
+ * The first 64 bytes of each segment are U, the next 64 are V. The U and
+ * V are arranged as follows:
+ *
+ * 0 1 ... 7
+ * 8 9 ... 15
+ * ...
+ * 56 57 ... 63
+ *
+ * U and V are shipped at half resolution (1 U,V sample -> one 2x2 block).
+ *
+ * The next 256 bytes are full resolution Y data and represent 4 squares
+ * of 8x8 pixels as follows:
+ *
+ * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199
+ * 8 9 ... 15 72 73 ... 79 200 201 ... 207
+ * ... ... ...
+ * 56 57 ... 63 120 121 ... 127 ... 248 249 ... 255
+ *
+ * Note that the U and V data in one segment represent a 16 x 16 pixel
+ * area, but the Y data represent a 32 x 8 pixel area. If the width is not an
+ * even multiple of 32, the extra 8x8 blocks within a 32x8 block belong to the
+ * next horizontal stripe.
+ *
+ * If dumppix module param is set, _parse_data just dumps the incoming segments,
+ * verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480
+ * this puts the data on the standard output and can be analyzed with the
+ * parseppm.c utility I wrote. That's a much faster way for figuring out how
+ * these data are scrambled.
+ */
+
+/* Converts from raw, uncompressed segments at pIn0 to a YUV420P frame at pOut0.
+ *
+ * FIXME: Currently only handles width and height that are multiples of 16
+ */
+static void
+yuv420raw_to_yuv420p(struct ov511_frame *frame,
+ unsigned char *pIn0, unsigned char *pOut0)
+{
+ int k, x, y;
+ unsigned char *pIn, *pOut, *pOutLine;
+ const unsigned int a = frame->rawwidth * frame->rawheight;
+ const unsigned int w = frame->rawwidth / 2;
+
+ /* Copy U and V */
+ pIn = pIn0;
+ pOutLine = pOut0 + a;
+ for (y = 0; y < frame->rawheight - 1; y += 16) {
+ pOut = pOutLine;
+ for (x = 0; x < frame->rawwidth - 1; x += 16) {
+ make_8x8(pIn, pOut, w);
+ make_8x8(pIn + 64, pOut + a/4, w);
+ pIn += 384;
+ pOut += 8;
+ }
+ pOutLine += 8 * w;
+ }
+
+ /* Copy Y */
+ pIn = pIn0 + 128;
+ pOutLine = pOut0;
+ k = 0;
+ for (y = 0; y < frame->rawheight - 1; y += 8) {
+ pOut = pOutLine;
+ for (x = 0; x < frame->rawwidth - 1; x += 8) {
+ make_8x8(pIn, pOut, frame->rawwidth);
+ pIn += 64;
+ pOut += 8;
+ if ((++k) > 3) {
+ k = 0;
+ pIn += 128;
+ }
+ }
+ pOutLine += 8 * frame->rawwidth;
+ }
+}
+
+/**********************************************************************
+ *
+ * Decompression
+ *
+ **********************************************************************/
+
+static int
+request_decompressor(struct usb_ov511 *ov)
+{
+ if (ov->bclass == BCL_OV511 || ov->bclass == BCL_OV518) {
+ err("No decompressor available");
+ } else {
+ err("Unknown bridge");
+ }
+
+ return -ENOSYS;
+}
+
+static void
+decompress(struct usb_ov511 *ov, struct ov511_frame *frame,
+ unsigned char *pIn0, unsigned char *pOut0)
+{
+ if (!ov->decomp_ops)
+ if (request_decompressor(ov))
+ return;
+
+}
+
+/**********************************************************************
+ *
+ * Format conversion
+ *
+ **********************************************************************/
+
+/* Fuses even and odd fields together, and doubles width.
+ * INPUT: an odd field followed by an even field at pIn0, in YUV planar format
+ * OUTPUT: a normal YUV planar image, with correct aspect ratio
+ */
+static void
+deinterlace(struct ov511_frame *frame, int rawformat,
+ unsigned char *pIn0, unsigned char *pOut0)
+{
+ const int fieldheight = frame->rawheight / 2;
+ const int fieldpix = fieldheight * frame->rawwidth;
+ const int w = frame->width;
+ int x, y;
+ unsigned char *pInEven, *pInOdd, *pOut;
+
+ PDEBUG(5, "fieldheight=%d", fieldheight);
+
+ if (frame->rawheight != frame->height) {
+ err("invalid height");
+ return;
+ }
+
+ if ((frame->rawwidth * 2) != frame->width) {
+ err("invalid width");
+ return;
+ }
+
+ /* Y */
+ pInOdd = pIn0;
+ pInEven = pInOdd + fieldpix;
+ pOut = pOut0;
+ for (y = 0; y < fieldheight; y++) {
+ for (x = 0; x < frame->rawwidth; x++) {
+ *pOut = *pInEven;
+ *(pOut+1) = *pInEven++;
+ *(pOut+w) = *pInOdd;
+ *(pOut+w+1) = *pInOdd++;
+ pOut += 2;
+ }
+ pOut += w;
+ }
+
+ if (rawformat == RAWFMT_YUV420) {
+ /* U */
+ pInOdd = pIn0 + fieldpix * 2;
+ pInEven = pInOdd + fieldpix / 4;
+ for (y = 0; y < fieldheight / 2; y++) {
+ for (x = 0; x < frame->rawwidth / 2; x++) {
+ *pOut = *pInEven;
+ *(pOut+1) = *pInEven++;
+ *(pOut+w/2) = *pInOdd;
+ *(pOut+w/2+1) = *pInOdd++;
+ pOut += 2;
+ }
+ pOut += w/2;
+ }
+ /* V */
+ pInOdd = pIn0 + fieldpix * 2 + fieldpix / 2;
+ pInEven = pInOdd + fieldpix / 4;
+ for (y = 0; y < fieldheight / 2; y++) {
+ for (x = 0; x < frame->rawwidth / 2; x++) {
+ *pOut = *pInEven;
+ *(pOut+1) = *pInEven++;
+ *(pOut+w/2) = *pInOdd;
+ *(pOut+w/2+1) = *pInOdd++;
+ pOut += 2;
+ }
+ pOut += w/2;
+ }
+ }
+}
+
+static void
+ov51x_postprocess_grey(struct usb_ov511 *ov, struct ov511_frame *frame)
+{
+ /* Deinterlace frame, if necessary */
+ if (ov->sensor == SEN_SAA7111A && frame->rawheight >= 480) {
+ if (frame->compressed)
+ decompress(ov, frame, frame->rawdata,
+ frame->tempdata);
+ else
+ yuv400raw_to_yuv400p(frame, frame->rawdata,
+ frame->tempdata);
+
+ deinterlace(frame, RAWFMT_YUV400, frame->tempdata,
+ frame->data);
+ } else {
+ if (frame->compressed)
+ decompress(ov, frame, frame->rawdata,
+ frame->data);
+ else
+ yuv400raw_to_yuv400p(frame, frame->rawdata,
+ frame->data);
+ }
+}
+
+/* Process raw YUV420 data into standard YUV420P */
+static void
+ov51x_postprocess_yuv420(struct usb_ov511 *ov, struct ov511_frame *frame)
+{
+ /* Deinterlace frame, if necessary */
+ if (ov->sensor == SEN_SAA7111A && frame->rawheight >= 480) {
+ if (frame->compressed)
+ decompress(ov, frame, frame->rawdata, frame->tempdata);
+ else
+ yuv420raw_to_yuv420p(frame, frame->rawdata,
+ frame->tempdata);
+
+ deinterlace(frame, RAWFMT_YUV420, frame->tempdata,
+ frame->data);
+ } else {
+ if (frame->compressed)
+ decompress(ov, frame, frame->rawdata, frame->data);
+ else
+ yuv420raw_to_yuv420p(frame, frame->rawdata,
+ frame->data);
+ }
+}
+
+/* Post-processes the specified frame. This consists of:
+ * 1. Decompress frame, if necessary
+ * 2. Deinterlace frame and scale to proper size, if necessary
+ * 3. Convert from YUV planar to destination format, if necessary
+ * 4. Fix the RGB offset, if necessary
+ */
+static void
+ov51x_postprocess(struct usb_ov511 *ov, struct ov511_frame *frame)
+{
+ if (dumppix) {
+ memset(frame->data, 0,
+ MAX_DATA_SIZE(ov->maxwidth, ov->maxheight));
+ PDEBUG(4, "Dumping %d bytes", frame->bytes_recvd);
+ memcpy(frame->data, frame->rawdata, frame->bytes_recvd);
+ } else {
+ switch (frame->format) {
+ case VIDEO_PALETTE_GREY:
+ ov51x_postprocess_grey(ov, frame);
+ break;
+ case VIDEO_PALETTE_YUV420:
+ case VIDEO_PALETTE_YUV420P:
+ ov51x_postprocess_yuv420(ov, frame);
+ break;
+ default:
+ err("Cannot convert data to %s",
+ symbolic(v4l1_plist, frame->format));
+ }
+ }
+}
+
+/**********************************************************************
+ *
+ * OV51x data transfer, IRQ handler
+ *
+ **********************************************************************/
+
+static inline void
+ov511_move_data(struct usb_ov511 *ov, unsigned char *in, int n)
+{
+ int num, offset;
+ int pnum = in[ov->packet_size - 1]; /* Get packet number */
+ int max_raw = MAX_RAW_DATA_SIZE(ov->maxwidth, ov->maxheight);
+ struct ov511_frame *frame = &ov->frame[ov->curframe];
+ struct timeval *ts;
+
+ /* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th
+ * byte non-zero. The EOF packet has image width/height in the
+ * 10th and 11th bytes. The 9th byte is given as follows:
+ *
+ * bit 7: EOF
+ * 6: compression enabled
+ * 5: 422/420/400 modes
+ * 4: 422/420/400 modes
+ * 3: 1
+ * 2: snapshot button on
+ * 1: snapshot frame
+ * 0: even/odd field
+ */
+
+ if (printph) {
+ info("ph(%3d): %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x",
+ pnum, in[0], in[1], in[2], in[3], in[4], in[5], in[6],
+ in[7], in[8], in[9], in[10], in[11]);
+ }
+
+ /* Check for SOF/EOF packet */
+ if ((in[0] | in[1] | in[2] | in[3] | in[4] | in[5] | in[6] | in[7]) ||
+ (~in[8] & 0x08))
+ goto check_middle;
+
+ /* Frame end */
+ if (in[8] & 0x80) {
+ ts = (struct timeval *)(frame->data
+ + MAX_FRAME_SIZE(ov->maxwidth, ov->maxheight));
+ do_gettimeofday(ts);
+
+ /* Get the actual frame size from the EOF header */
+ frame->rawwidth = ((int)(in[9]) + 1) * 8;
+ frame->rawheight = ((int)(in[10]) + 1) * 8;
+
+ PDEBUG(4, "Frame end, frame=%d, pnum=%d, w=%d, h=%d, recvd=%d",
+ ov->curframe, pnum, frame->rawwidth, frame->rawheight,
+ frame->bytes_recvd);
+
+ /* Validate the header data */
+ RESTRICT_TO_RANGE(frame->rawwidth, ov->minwidth, ov->maxwidth);
+ RESTRICT_TO_RANGE(frame->rawheight, ov->minheight,
+ ov->maxheight);
+
+ /* Don't allow byte count to exceed buffer size */
+ RESTRICT_TO_RANGE(frame->bytes_recvd, 8, max_raw);
+
+ if (frame->scanstate == STATE_LINES) {
+ int nextf;
+
+ frame->grabstate = FRAME_DONE;
+ wake_up_interruptible(&frame->wq);
+
+ /* If next frame is ready or grabbing,
+ * point to it */
+ nextf = (ov->curframe + 1) % OV511_NUMFRAMES;
+ if (ov->frame[nextf].grabstate == FRAME_READY
+ || ov->frame[nextf].grabstate == FRAME_GRABBING) {
+ ov->curframe = nextf;
+ ov->frame[nextf].scanstate = STATE_SCANNING;
+ } else {
+ if (frame->grabstate == FRAME_DONE) {
+ PDEBUG(4, "** Frame done **");
+ } else {
+ PDEBUG(4, "Frame not ready? state = %d",
+ ov->frame[nextf].grabstate);
+ }
+
+ ov->curframe = -1;
+ }
+ } else {
+ PDEBUG(5, "Frame done, but not scanning");
+ }
+ /* Image corruption caused by misplaced frame->segment = 0
+ * fixed by carlosf@conectiva.com.br
+ */
+ } else {
+ /* Frame start */
+ PDEBUG(4, "Frame start, framenum = %d", ov->curframe);
+
+ /* Check to see if it's a snapshot frame */
+ /* FIXME?? Should the snapshot reset go here? Performance? */
+ if (in[8] & 0x02) {
+ frame->snapshot = 1;
+ PDEBUG(3, "snapshot detected");
+ }
+
+ frame->scanstate = STATE_LINES;
+ frame->bytes_recvd = 0;
+ frame->compressed = in[8] & 0x40;
+ }
+
+check_middle:
+ /* Are we in a frame? */
+ if (frame->scanstate != STATE_LINES) {
+ PDEBUG(5, "Not in a frame; packet skipped");
+ return;
+ }
+
+ /* If frame start, skip header */
+ if (frame->bytes_recvd == 0)
+ offset = 9;
+ else
+ offset = 0;
+
+ num = n - offset - 1;
+
+ /* Dump all data exactly as received */
+ if (dumppix == 2) {
+ frame->bytes_recvd += n - 1;
+ if (frame->bytes_recvd <= max_raw)
+ memcpy(frame->rawdata + frame->bytes_recvd - (n - 1),
+ in, n - 1);
+ else
+ PDEBUG(3, "Raw data buffer overrun!! (%d)",
+ frame->bytes_recvd - max_raw);
+ } else if (!frame->compressed && !remove_zeros) {
+ frame->bytes_recvd += num;
+ if (frame->bytes_recvd <= max_raw)
+ memcpy(frame->rawdata + frame->bytes_recvd - num,
+ in + offset, num);
+ else
+ PDEBUG(3, "Raw data buffer overrun!! (%d)",
+ frame->bytes_recvd - max_raw);
+ } else { /* Remove all-zero FIFO lines (aligned 32-byte blocks) */
+ int b, read = 0, allzero, copied = 0;
+ if (offset) {
+ frame->bytes_recvd += 32 - offset; // Bytes out
+ memcpy(frame->rawdata, in + offset, 32 - offset);
+ read += 32;
+ }
+
+ while (read < n - 1) {
+ allzero = 1;
+ for (b = 0; b < 32; b++) {
+ if (in[read + b]) {
+ allzero = 0;
+ break;
+ }
+ }
+
+ if (allzero) {
+ /* Don't copy it */
+ } else {
+ if (frame->bytes_recvd + copied + 32 <= max_raw)
+ {
+ memcpy(frame->rawdata
+ + frame->bytes_recvd + copied,
+ in + read, 32);
+ copied += 32;
+ } else {
+ PDEBUG(3, "Raw data buffer overrun!!");
+ }
+ }
+ read += 32;
+ }
+
+ frame->bytes_recvd += copied;
+ }
+}
+
+static inline void
+ov518_move_data(struct usb_ov511 *ov, unsigned char *in, int n)
+{
+ int max_raw = MAX_RAW_DATA_SIZE(ov->maxwidth, ov->maxheight);
+ struct ov511_frame *frame = &ov->frame[ov->curframe];
+ struct timeval *ts;
+
+ /* Don't copy the packet number byte */
+ if (ov->packet_numbering)
+ --n;
+
+ /* A false positive here is likely, until OVT gives me
+ * the definitive SOF/EOF format */
+ if ((!(in[0] | in[1] | in[2] | in[3] | in[5])) && in[6]) {
+ if (printph) {
+ info("ph: %2x %2x %2x %2x %2x %2x %2x %2x", in[0],
+ in[1], in[2], in[3], in[4], in[5], in[6], in[7]);
+ }
+
+ if (frame->scanstate == STATE_LINES) {
+ PDEBUG(4, "Detected frame end/start");
+ goto eof;
+ } else { //scanstate == STATE_SCANNING
+ /* Frame start */
+ PDEBUG(4, "Frame start, framenum = %d", ov->curframe);
+ goto sof;
+ }
+ } else {
+ goto check_middle;
+ }
+
+eof:
+ ts = (struct timeval *)(frame->data
+ + MAX_FRAME_SIZE(ov->maxwidth, ov->maxheight));
+ do_gettimeofday(ts);
+
+ PDEBUG(4, "Frame end, curframe = %d, hw=%d, vw=%d, recvd=%d",
+ ov->curframe,
+ (int)(in[9]), (int)(in[10]), frame->bytes_recvd);
+
+ // FIXME: Since we don't know the header formats yet,
+ // there is no way to know what the actual image size is
+ frame->rawwidth = frame->width;
+ frame->rawheight = frame->height;
+
+ /* Validate the header data */
+ RESTRICT_TO_RANGE(frame->rawwidth, ov->minwidth, ov->maxwidth);
+ RESTRICT_TO_RANGE(frame->rawheight, ov->minheight, ov->maxheight);
+
+ /* Don't allow byte count to exceed buffer size */
+ RESTRICT_TO_RANGE(frame->bytes_recvd, 8, max_raw);
+
+ if (frame->scanstate == STATE_LINES) {
+ int nextf;
+
+ frame->grabstate = FRAME_DONE;
+ wake_up_interruptible(&frame->wq);
+
+ /* If next frame is ready or grabbing,
+ * point to it */
+ nextf = (ov->curframe + 1) % OV511_NUMFRAMES;
+ if (ov->frame[nextf].grabstate == FRAME_READY
+ || ov->frame[nextf].grabstate == FRAME_GRABBING) {
+ ov->curframe = nextf;
+ ov->frame[nextf].scanstate = STATE_SCANNING;
+ frame = &ov->frame[nextf];
+ } else {
+ if (frame->grabstate == FRAME_DONE) {
+ PDEBUG(4, "** Frame done **");
+ } else {
+ PDEBUG(4, "Frame not ready? state = %d",
+ ov->frame[nextf].grabstate);
+ }
+
+ ov->curframe = -1;
+ PDEBUG(4, "SOF dropped (no active frame)");
+ return; /* Nowhere to store this frame */
+ }
+ }
+sof:
+ PDEBUG(4, "Starting capture on frame %d", frame->framenum);
+
+// Snapshot not reverse-engineered yet.
+#if 0
+ /* Check to see if it's a snapshot frame */
+ /* FIXME?? Should the snapshot reset go here? Performance? */
+ if (in[8] & 0x02) {
+ frame->snapshot = 1;
+ PDEBUG(3, "snapshot detected");
+ }
+#endif
+ frame->scanstate = STATE_LINES;
+ frame->bytes_recvd = 0;
+ frame->compressed = 1;
+
+check_middle:
+ /* Are we in a frame? */
+ if (frame->scanstate != STATE_LINES) {
+ PDEBUG(4, "scanstate: no SOF yet");
+ return;
+ }
+
+ /* Dump all data exactly as received */
+ if (dumppix == 2) {
+ frame->bytes_recvd += n;
+ if (frame->bytes_recvd <= max_raw)
+ memcpy(frame->rawdata + frame->bytes_recvd - n, in, n);
+ else
+ PDEBUG(3, "Raw data buffer overrun!! (%d)",
+ frame->bytes_recvd - max_raw);
+ } else {
+ /* All incoming data are divided into 8-byte segments. If the
+ * segment contains all zero bytes, it must be skipped. These
+ * zero-segments allow the OV518 to mainain a constant data rate
+ * regardless of the effectiveness of the compression. Segments
+ * are aligned relative to the beginning of each isochronous
+ * packet. The first segment in each image is a header (the
+ * decompressor skips it later).
+ */
+
+ int b, read = 0, allzero, copied = 0;
+
+ while (read < n) {
+ allzero = 1;
+ for (b = 0; b < 8; b++) {
+ if (in[read + b]) {
+ allzero = 0;
+ break;
+ }
+ }
+
+ if (allzero) {
+ /* Don't copy it */
+ } else {
+ if (frame->bytes_recvd + copied + 8 <= max_raw)
+ {
+ memcpy(frame->rawdata
+ + frame->bytes_recvd + copied,
+ in + read, 8);
+ copied += 8;
+ } else {
+ PDEBUG(3, "Raw data buffer overrun!!");
+ }
+ }
+ read += 8;
+ }
+ frame->bytes_recvd += copied;
+ }
+}
+
+static void
+ov51x_isoc_irq(struct urb *urb, struct pt_regs *regs)
+{
+ int i;
+ struct usb_ov511 *ov;
+ struct ov511_sbuf *sbuf;
+
+ if (!urb->context) {
+ PDEBUG(4, "no context");
+ return;
+ }
+
+ sbuf = urb->context;
+ ov = sbuf->ov;
+
+ if (!ov || !ov->dev || !ov->user) {
+ PDEBUG(4, "no device, or not open");
+ return;
+ }
+
+ if (!ov->streaming) {
+ PDEBUG(4, "hmmm... not streaming, but got interrupt");
+ return;
+ }
+
+ if (urb->status == -ENOENT || urb->status == -ECONNRESET) {
+ PDEBUG(4, "URB unlinked");
+ return;
+ }
+
+ if (urb->status != -EINPROGRESS && urb->status != 0) {
+ err("ERROR: urb->status=%d: %s", urb->status,
+ symbolic(urb_errlist, urb->status));
+ }
+
+ /* Copy the data received into our frame buffer */
+ PDEBUG(5, "sbuf[%d]: Moving %d packets", sbuf->n,
+ urb->number_of_packets);
+ for (i = 0; i < urb->number_of_packets; i++) {
+ /* Warning: Don't call *_move_data() if no frame active! */
+ if (ov->curframe >= 0) {
+ int n = urb->iso_frame_desc[i].actual_length;
+ int st = urb->iso_frame_desc[i].status;
+ unsigned char *cdata;
+
+ urb->iso_frame_desc[i].actual_length = 0;
+ urb->iso_frame_desc[i].status = 0;
+
+ cdata = urb->transfer_buffer
+ + urb->iso_frame_desc[i].offset;
+
+ if (!n) {
+ PDEBUG(4, "Zero-length packet");
+ continue;
+ }
+
+ if (st)
+ PDEBUG(2, "data error: [%d] len=%d, status=%d",
+ i, n, st);
+
+ if (ov->bclass == BCL_OV511)
+ ov511_move_data(ov, cdata, n);
+ else if (ov->bclass == BCL_OV518)
+ ov518_move_data(ov, cdata, n);
+ else
+ err("Unknown bridge device (%d)", ov->bridge);
+
+ } else if (waitqueue_active(&ov->wq)) {
+ wake_up_interruptible(&ov->wq);
+ }
+ }
+
+ /* Resubmit this URB */
+ urb->dev = ov->dev;
+ if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0)
+ err("usb_submit_urb() ret %d", i);
+
+ return;
+}
+
+/****************************************************************************
+ *
+ * Stream initialization and termination
+ *
+ ***************************************************************************/
+
+static int
+ov51x_init_isoc(struct usb_ov511 *ov)
+{
+ struct urb *urb;
+ int fx, err, n, size;
+
+ PDEBUG(3, "*** Initializing capture ***");
+
+ ov->curframe = -1;
+
+ if (ov->bridge == BRG_OV511) {
+ if (cams == 1)
+ size = 993;
+ else if (cams == 2)
+ size = 513;
+ else if (cams == 3 || cams == 4)
+ size = 257;
+ else {
+ err("\"cams\" parameter too high!");
+ return -1;
+ }
+ } else if (ov->bridge == BRG_OV511PLUS) {
+ if (cams == 1)
+ size = 961;
+ else if (cams == 2)
+ size = 513;
+ else if (cams == 3 || cams == 4)
+ size = 257;
+ else if (cams >= 5 && cams <= 8)
+ size = 129;
+ else if (cams >= 9 && cams <= 31)
+ size = 33;
+ else {
+ err("\"cams\" parameter too high!");
+ return -1;
+ }
+ } else if (ov->bclass == BCL_OV518) {
+ if (cams == 1)
+ size = 896;
+ else if (cams == 2)
+ size = 512;
+ else if (cams == 3 || cams == 4)
+ size = 256;
+ else if (cams >= 5 && cams <= 8)
+ size = 128;
+ else {
+ err("\"cams\" parameter too high!");
+ return -1;
+ }
+ } else {
+ err("invalid bridge type");
+ return -1;
+ }
+
+ // FIXME: OV518 is hardcoded to 15 FPS (alternate 5) for now
+ if (ov->bclass == BCL_OV518) {
+ if (packetsize == -1) {
+ ov518_set_packet_size(ov, 640);
+ } else {
+ info("Forcing packet size to %d", packetsize);
+ ov518_set_packet_size(ov, packetsize);
+ }
+ } else {
+ if (packetsize == -1) {
+ ov511_set_packet_size(ov, size);
+ } else {
+ info("Forcing packet size to %d", packetsize);
+ ov511_set_packet_size(ov, packetsize);
+ }
+ }
+
+ for (n = 0; n < OV511_NUMSBUF; n++) {
+ urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL);
+ if (!urb) {
+ err("init isoc: usb_alloc_urb ret. NULL");
+ return -ENOMEM;
+ }
+ ov->sbuf[n].urb = urb;
+ urb->dev = ov->dev;
+ urb->context = &ov->sbuf[n];
+ urb->pipe = usb_rcvisocpipe(ov->dev, OV511_ENDPOINT_ADDRESS);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = ov->sbuf[n].data;
+ urb->complete = ov51x_isoc_irq;
+ urb->number_of_packets = FRAMES_PER_DESC;
+ urb->transfer_buffer_length = ov->packet_size * FRAMES_PER_DESC;
+ urb->interval = 1;
+ for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
+ urb->iso_frame_desc[fx].offset = ov->packet_size * fx;
+ urb->iso_frame_desc[fx].length = ov->packet_size;
+ }
+ }
+
+ ov->streaming = 1;
+
+ for (n = 0; n < OV511_NUMSBUF; n++) {
+ ov->sbuf[n].urb->dev = ov->dev;
+ err = usb_submit_urb(ov->sbuf[n].urb, GFP_KERNEL);
+ if (err) {
+ err("init isoc: usb_submit_urb(%d) ret %d", n, err);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static void
+ov51x_unlink_isoc(struct usb_ov511 *ov)
+{
+ int n;
+
+ /* Unschedule all of the iso td's */
+ for (n = OV511_NUMSBUF - 1; n >= 0; n--) {
+ if (ov->sbuf[n].urb) {
+ usb_kill_urb(ov->sbuf[n].urb);
+ usb_free_urb(ov->sbuf[n].urb);
+ ov->sbuf[n].urb = NULL;
+ }
+ }
+}
+
+static void
+ov51x_stop_isoc(struct usb_ov511 *ov)
+{
+ if (!ov->streaming || !ov->dev)
+ return;
+
+ PDEBUG(3, "*** Stopping capture ***");
+
+ if (ov->bclass == BCL_OV518)
+ ov518_set_packet_size(ov, 0);
+ else
+ ov511_set_packet_size(ov, 0);
+
+ ov->streaming = 0;
+
+ ov51x_unlink_isoc(ov);
+}
+
+static int
+ov51x_new_frame(struct usb_ov511 *ov, int framenum)
+{
+ struct ov511_frame *frame;
+ int newnum;
+
+ PDEBUG(4, "ov->curframe = %d, framenum = %d", ov->curframe, framenum);
+
+ if (!ov->dev)
+ return -1;
+
+ /* If we're not grabbing a frame right now and the other frame is */
+ /* ready to be grabbed into, then use it instead */
+ if (ov->curframe == -1) {
+ newnum = (framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES;
+ if (ov->frame[newnum].grabstate == FRAME_READY)
+ framenum = newnum;
+ } else
+ return 0;
+
+ frame = &ov->frame[framenum];
+
+ PDEBUG(4, "framenum = %d, width = %d, height = %d", framenum,
+ frame->width, frame->height);
+
+ frame->grabstate = FRAME_GRABBING;
+ frame->scanstate = STATE_SCANNING;
+ frame->snapshot = 0;
+
+ ov->curframe = framenum;
+
+ /* Make sure it's not too big */
+ if (frame->width > ov->maxwidth)
+ frame->width = ov->maxwidth;
+
+ frame->width &= ~7L; /* Multiple of 8 */
+
+ if (frame->height > ov->maxheight)
+ frame->height = ov->maxheight;
+
+ frame->height &= ~3L; /* Multiple of 4 */
+
+ return 0;
+}
+
+/****************************************************************************
+ *
+ * Buffer management
+ *
+ ***************************************************************************/
+
+/*
+ * - You must acquire buf_lock before entering this function.
+ * - Because this code will free any non-null pointer, you must be sure to null
+ * them if you explicitly free them somewhere else!
+ */
+static void
+ov51x_do_dealloc(struct usb_ov511 *ov)
+{
+ int i;
+ PDEBUG(4, "entered");
+
+ if (ov->fbuf) {
+ rvfree(ov->fbuf, OV511_NUMFRAMES
+ * MAX_DATA_SIZE(ov->maxwidth, ov->maxheight));
+ ov->fbuf = NULL;
+ }
+
+ vfree(ov->rawfbuf);
+ ov->rawfbuf = NULL;
+
+ vfree(ov->tempfbuf);
+ ov->tempfbuf = NULL;
+
+ for (i = 0; i < OV511_NUMSBUF; i++) {
+ kfree(ov->sbuf[i].data);
+ ov->sbuf[i].data = NULL;
+ }
+
+ for (i = 0; i < OV511_NUMFRAMES; i++) {
+ ov->frame[i].data = NULL;
+ ov->frame[i].rawdata = NULL;
+ ov->frame[i].tempdata = NULL;
+ if (ov->frame[i].compbuf) {
+ free_page((unsigned long) ov->frame[i].compbuf);
+ ov->frame[i].compbuf = NULL;
+ }
+ }
+
+ PDEBUG(4, "buffer memory deallocated");
+ ov->buf_state = BUF_NOT_ALLOCATED;
+ PDEBUG(4, "leaving");
+}
+
+static int
+ov51x_alloc(struct usb_ov511 *ov)
+{
+ int i;
+ const int w = ov->maxwidth;
+ const int h = ov->maxheight;
+ const int data_bufsize = OV511_NUMFRAMES * MAX_DATA_SIZE(w, h);
+ const int raw_bufsize = OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h);
+
+ PDEBUG(4, "entered");
+ mutex_lock(&ov->buf_lock);
+
+ if (ov->buf_state == BUF_ALLOCATED)
+ goto out;
+
+ ov->fbuf = rvmalloc(data_bufsize);
+ if (!ov->fbuf)
+ goto error;
+
+ ov->rawfbuf = vmalloc(raw_bufsize);
+ if (!ov->rawfbuf)
+ goto error;
+
+ memset(ov->rawfbuf, 0, raw_bufsize);
+
+ ov->tempfbuf = vmalloc(raw_bufsize);
+ if (!ov->tempfbuf)
+ goto error;
+
+ memset(ov->tempfbuf, 0, raw_bufsize);
+
+ for (i = 0; i < OV511_NUMSBUF; i++) {
+ ov->sbuf[i].data = kmalloc(FRAMES_PER_DESC *
+ MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL);
+ if (!ov->sbuf[i].data)
+ goto error;
+
+ PDEBUG(4, "sbuf[%d] @ %p", i, ov->sbuf[i].data);
+ }
+
+ for (i = 0; i < OV511_NUMFRAMES; i++) {
+ ov->frame[i].data = ov->fbuf + i * MAX_DATA_SIZE(w, h);
+ ov->frame[i].rawdata = ov->rawfbuf
+ + i * MAX_RAW_DATA_SIZE(w, h);
+ ov->frame[i].tempdata = ov->tempfbuf
+ + i * MAX_RAW_DATA_SIZE(w, h);
+
+ ov->frame[i].compbuf =
+ (unsigned char *) __get_free_page(GFP_KERNEL);
+ if (!ov->frame[i].compbuf)
+ goto error;
+
+ PDEBUG(4, "frame[%d] @ %p", i, ov->frame[i].data);
+ }
+
+ ov->buf_state = BUF_ALLOCATED;
+out:
+ mutex_unlock(&ov->buf_lock);
+ PDEBUG(4, "leaving");
+ return 0;
+error:
+ ov51x_do_dealloc(ov);
+ mutex_unlock(&ov->buf_lock);
+ PDEBUG(4, "errored");
+ return -ENOMEM;
+}
+
+static void
+ov51x_dealloc(struct usb_ov511 *ov)
+{
+ PDEBUG(4, "entered");
+ mutex_lock(&ov->buf_lock);
+ ov51x_do_dealloc(ov);
+ mutex_unlock(&ov->buf_lock);
+ PDEBUG(4, "leaving");
+}
+
+/****************************************************************************
+ *
+ * V4L 1 API
+ *
+ ***************************************************************************/
+
+static int
+ov51x_v4l1_open(struct inode *inode, struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct usb_ov511 *ov = video_get_drvdata(vdev);
+ int err, i;
+
+ PDEBUG(4, "opening");
+
+ mutex_lock(&ov->lock);
+
+ err = -EBUSY;
+ if (ov->user)
+ goto out;
+
+ ov->sub_flag = 0;
+
+ /* In case app doesn't set them... */
+ err = ov51x_set_default_params(ov);
+ if (err < 0)
+ goto out;
+
+ /* Make sure frames are reset */
+ for (i = 0; i < OV511_NUMFRAMES; i++) {
+ ov->frame[i].grabstate = FRAME_UNUSED;
+ ov->frame[i].bytes_read = 0;
+ }
+
+ /* If compression is on, make sure now that a
+ * decompressor can be loaded */
+ if (ov->compress && !ov->decomp_ops) {
+ err = request_decompressor(ov);
+ if (err && !dumppix)
+ goto out;
+ }
+
+ err = ov51x_alloc(ov);
+ if (err < 0)
+ goto out;
+
+ err = ov51x_init_isoc(ov);
+ if (err) {
+ ov51x_dealloc(ov);
+ goto out;
+ }
+
+ ov->user++;
+ file->private_data = vdev;
+
+ if (ov->led_policy == LED_AUTO)
+ ov51x_led_control(ov, 1);
+
+out:
+ mutex_unlock(&ov->lock);
+ return err;
+}
+
+static int
+ov51x_v4l1_close(struct inode *inode, struct file *file)
+{
+ struct video_device *vdev = file->private_data;
+ struct usb_ov511 *ov = video_get_drvdata(vdev);
+
+ PDEBUG(4, "ov511_close");
+
+ mutex_lock(&ov->lock);
+
+ ov->user--;
+ ov51x_stop_isoc(ov);
+
+ if (ov->led_policy == LED_AUTO)
+ ov51x_led_control(ov, 0);
+
+ if (ov->dev)
+ ov51x_dealloc(ov);
+
+ mutex_unlock(&ov->lock);
+
+ /* Device unplugged while open. Only a minimum of unregistration is done
+ * here; the disconnect callback already did the rest. */
+ if (!ov->dev) {
+ mutex_lock(&ov->cbuf_lock);
+ kfree(ov->cbuf);
+ ov->cbuf = NULL;
+ mutex_unlock(&ov->cbuf_lock);
+
+ ov51x_dealloc(ov);
+ kfree(ov);
+ ov = NULL;
+ }
+
+ file->private_data = NULL;
+ return 0;
+}
+
+/* Do not call this function directly! */
+static int
+ov51x_v4l1_ioctl_internal(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = file->private_data;
+ struct usb_ov511 *ov = video_get_drvdata(vdev);
+ PDEBUG(5, "IOCtl: 0x%X", cmd);
+
+ if (!ov->dev)
+ return -EIO;
+
+ switch (cmd) {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *b = arg;
+
+ PDEBUG(4, "VIDIOCGCAP");
+
+ memset(b, 0, sizeof(struct video_capability));
+ sprintf(b->name, "%s USB Camera",
+ symbolic(brglist, ov->bridge));
+ b->type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
+ b->channels = ov->num_inputs;
+ b->audios = 0;
+ b->maxwidth = ov->maxwidth;
+ b->maxheight = ov->maxheight;
+ b->minwidth = ov->minwidth;
+ b->minheight = ov->minheight;
+
+ return 0;
+ }
+ case VIDIOCGCHAN:
+ {
+ struct video_channel *v = arg;
+
+ PDEBUG(4, "VIDIOCGCHAN");
+
+ if ((unsigned)(v->channel) >= ov->num_inputs) {
+ err("Invalid channel (%d)", v->channel);
+ return -EINVAL;
+ }
+
+ v->norm = ov->norm;
+ v->type = VIDEO_TYPE_CAMERA;
+ v->flags = 0;
+// v->flags |= (ov->has_decoder) ? VIDEO_VC_NORM : 0;
+ v->tuners = 0;
+ decoder_get_input_name(ov, v->channel, v->name);
+
+ return 0;
+ }
+ case VIDIOCSCHAN:
+ {
+ struct video_channel *v = arg;
+ int err;
+
+ PDEBUG(4, "VIDIOCSCHAN");
+
+ /* Make sure it's not a camera */
+ if (!ov->has_decoder) {
+ if (v->channel == 0)
+ return 0;
+ else
+ return -EINVAL;
+ }
+
+ if (v->norm != VIDEO_MODE_PAL &&
+ v->norm != VIDEO_MODE_NTSC &&
+ v->norm != VIDEO_MODE_SECAM &&
+ v->norm != VIDEO_MODE_AUTO) {
+ err("Invalid norm (%d)", v->norm);
+ return -EINVAL;
+ }
+
+ if ((unsigned)(v->channel) >= ov->num_inputs) {
+ err("Invalid channel (%d)", v->channel);
+ return -EINVAL;
+ }
+
+ err = decoder_set_input(ov, v->channel);
+ if (err)
+ return err;
+
+ err = decoder_set_norm(ov, v->norm);
+ if (err)
+ return err;
+
+ return 0;
+ }
+ case VIDIOCGPICT:
+ {
+ struct video_picture *p = arg;
+
+ PDEBUG(4, "VIDIOCGPICT");
+
+ memset(p, 0, sizeof(struct video_picture));
+ if (sensor_get_picture(ov, p))
+ return -EIO;
+
+ /* Can we get these from frame[0]? -claudio? */
+ p->depth = ov->frame[0].depth;
+ p->palette = ov->frame[0].format;
+
+ return 0;
+ }
+ case VIDIOCSPICT:
+ {
+ struct video_picture *p = arg;
+ int i, rc;
+
+ PDEBUG(4, "VIDIOCSPICT");
+
+ if (!get_depth(p->palette))
+ return -EINVAL;
+
+ if (sensor_set_picture(ov, p))
+ return -EIO;
+
+ if (force_palette && p->palette != force_palette) {
+ info("Palette rejected (%s)",
+ symbolic(v4l1_plist, p->palette));
+ return -EINVAL;
+ }
+
+ // FIXME: Format should be independent of frames
+ if (p->palette != ov->frame[0].format) {
+ PDEBUG(4, "Detected format change");
+
+ rc = ov51x_wait_frames_inactive(ov);
+ if (rc)
+ return rc;
+
+ mode_init_regs(ov, ov->frame[0].width,
+ ov->frame[0].height, p->palette, ov->sub_flag);
+ }
+
+ PDEBUG(4, "Setting depth=%d, palette=%s",
+ p->depth, symbolic(v4l1_plist, p->palette));
+
+ for (i = 0; i < OV511_NUMFRAMES; i++) {
+ ov->frame[i].depth = p->depth;
+ ov->frame[i].format = p->palette;
+ }
+
+ return 0;
+ }
+ case VIDIOCGCAPTURE:
+ {
+ int *vf = arg;
+
+ PDEBUG(4, "VIDIOCGCAPTURE");
+
+ ov->sub_flag = *vf;
+ return 0;
+ }
+ case VIDIOCSCAPTURE:
+ {
+ struct video_capture *vc = arg;
+
+ PDEBUG(4, "VIDIOCSCAPTURE");
+
+ if (vc->flags)
+ return -EINVAL;
+ if (vc->decimation)
+ return -EINVAL;
+
+ vc->x &= ~3L;
+ vc->y &= ~1L;
+ vc->y &= ~31L;
+
+ if (vc->width == 0)
+ vc->width = 32;
+
+ vc->height /= 16;
+ vc->height *= 16;
+ if (vc->height == 0)
+ vc->height = 16;
+
+ ov->subx = vc->x;
+ ov->suby = vc->y;
+ ov->subw = vc->width;
+ ov->subh = vc->height;
+
+ return 0;
+ }
+ case VIDIOCSWIN:
+ {
+ struct video_window *vw = arg;
+ int i, rc;
+
+ PDEBUG(4, "VIDIOCSWIN: %dx%d", vw->width, vw->height);
+
+#if 0
+ if (vw->flags)
+ return -EINVAL;
+ if (vw->clipcount)
+ return -EINVAL;
+ if (vw->height != ov->maxheight)
+ return -EINVAL;
+ if (vw->width != ov->maxwidth)
+ return -EINVAL;
+#endif
+
+ rc = ov51x_wait_frames_inactive(ov);
+ if (rc)
+ return rc;
+
+ rc = mode_init_regs(ov, vw->width, vw->height,
+ ov->frame[0].format, ov->sub_flag);
+ if (rc < 0)
+ return rc;
+
+ for (i = 0; i < OV511_NUMFRAMES; i++) {
+ ov->frame[i].width = vw->width;
+ ov->frame[i].height = vw->height;
+ }
+
+ return 0;
+ }
+ case VIDIOCGWIN:
+ {
+ struct video_window *vw = arg;
+
+ memset(vw, 0, sizeof(struct video_window));
+ vw->x = 0; /* FIXME */
+ vw->y = 0;
+ vw->width = ov->frame[0].width;
+ vw->height = ov->frame[0].height;
+ vw->flags = 30;
+
+ PDEBUG(4, "VIDIOCGWIN: %dx%d", vw->width, vw->height);
+
+ return 0;
+ }
+ case VIDIOCGMBUF:
+ {
+ struct video_mbuf *vm = arg;
+ int i;
+
+ PDEBUG(4, "VIDIOCGMBUF");
+
+ memset(vm, 0, sizeof(struct video_mbuf));
+ vm->size = OV511_NUMFRAMES
+ * MAX_DATA_SIZE(ov->maxwidth, ov->maxheight);
+ vm->frames = OV511_NUMFRAMES;
+
+ vm->offsets[0] = 0;
+ for (i = 1; i < OV511_NUMFRAMES; i++) {
+ vm->offsets[i] = vm->offsets[i-1]
+ + MAX_DATA_SIZE(ov->maxwidth, ov->maxheight);
+ }
+
+ return 0;
+ }
+ case VIDIOCMCAPTURE:
+ {
+ struct video_mmap *vm = arg;
+ int rc, depth;
+ unsigned int f = vm->frame;
+
+ PDEBUG(4, "VIDIOCMCAPTURE: frame: %d, %dx%d, %s", f, vm->width,
+ vm->height, symbolic(v4l1_plist, vm->format));
+
+ depth = get_depth(vm->format);
+ if (!depth) {
+ PDEBUG(2, "VIDIOCMCAPTURE: invalid format (%s)",
+ symbolic(v4l1_plist, vm->format));
+ return -EINVAL;
+ }
+
+ if (f >= OV511_NUMFRAMES) {
+ err("VIDIOCMCAPTURE: invalid frame (%d)", f);
+ return -EINVAL;
+ }
+
+ if (vm->width > ov->maxwidth
+ || vm->height > ov->maxheight) {
+ err("VIDIOCMCAPTURE: requested dimensions too big");
+ return -EINVAL;
+ }
+
+ if (ov->frame[f].grabstate == FRAME_GRABBING) {
+ PDEBUG(4, "VIDIOCMCAPTURE: already grabbing");
+ return -EBUSY;
+ }
+
+ if (force_palette && (vm->format != force_palette)) {
+ PDEBUG(2, "palette rejected (%s)",
+ symbolic(v4l1_plist, vm->format));
+ return -EINVAL;
+ }
+
+ if ((ov->frame[f].width != vm->width) ||
+ (ov->frame[f].height != vm->height) ||
+ (ov->frame[f].format != vm->format) ||
+ (ov->frame[f].sub_flag != ov->sub_flag) ||
+ (ov->frame[f].depth != depth)) {
+ PDEBUG(4, "VIDIOCMCAPTURE: change in image parameters");
+
+ rc = ov51x_wait_frames_inactive(ov);
+ if (rc)
+ return rc;
+
+ rc = mode_init_regs(ov, vm->width, vm->height,
+ vm->format, ov->sub_flag);
+#if 0
+ if (rc < 0) {
+ PDEBUG(1, "Got error while initializing regs ");
+ return ret;
+ }
+#endif
+ ov->frame[f].width = vm->width;
+ ov->frame[f].height = vm->height;
+ ov->frame[f].format = vm->format;
+ ov->frame[f].sub_flag = ov->sub_flag;
+ ov->frame[f].depth = depth;
+ }
+
+ /* Mark it as ready */
+ ov->frame[f].grabstate = FRAME_READY;
+
+ PDEBUG(4, "VIDIOCMCAPTURE: renewing frame %d", f);
+
+ return ov51x_new_frame(ov, f);
+ }
+ case VIDIOCSYNC:
+ {
+ unsigned int fnum = *((unsigned int *) arg);
+ struct ov511_frame *frame;
+ int rc;
+
+ if (fnum >= OV511_NUMFRAMES) {
+ err("VIDIOCSYNC: invalid frame (%d)", fnum);
+ return -EINVAL;
+ }
+
+ frame = &ov->frame[fnum];
+
+ PDEBUG(4, "syncing to frame %d, grabstate = %d", fnum,
+ frame->grabstate);
+
+ switch (frame->grabstate) {
+ case FRAME_UNUSED:
+ return -EINVAL;
+ case FRAME_READY:
+ case FRAME_GRABBING:
+ case FRAME_ERROR:
+redo:
+ if (!ov->dev)
+ return -EIO;
+
+ rc = wait_event_interruptible(frame->wq,
+ (frame->grabstate == FRAME_DONE)
+ || (frame->grabstate == FRAME_ERROR));
+
+ if (rc)
+ return rc;
+
+ if (frame->grabstate == FRAME_ERROR) {
+ if ((rc = ov51x_new_frame(ov, fnum)) < 0)
+ return rc;
+ goto redo;
+ }
+ /* Fall through */
+ case FRAME_DONE:
+ if (ov->snap_enabled && !frame->snapshot) {
+ if ((rc = ov51x_new_frame(ov, fnum)) < 0)
+ return rc;
+ goto redo;
+ }
+
+ frame->grabstate = FRAME_UNUSED;
+
+ /* Reset the hardware snapshot button */
+ /* FIXME - Is this the best place for this? */
+ if ((ov->snap_enabled) && (frame->snapshot)) {
+ frame->snapshot = 0;
+ ov51x_clear_snapshot(ov);
+ }
+
+ /* Decompression, format conversion, etc... */
+ ov51x_postprocess(ov, frame);
+
+ break;
+ } /* end switch */
+
+ return 0;
+ }
+ case VIDIOCGFBUF:
+ {
+ struct video_buffer *vb = arg;
+
+ PDEBUG(4, "VIDIOCGFBUF");
+
+ memset(vb, 0, sizeof(struct video_buffer));
+
+ return 0;
+ }
+ case VIDIOCGUNIT:
+ {
+ struct video_unit *vu = arg;
+
+ PDEBUG(4, "VIDIOCGUNIT");
+
+ memset(vu, 0, sizeof(struct video_unit));
+
+ vu->video = ov->vdev->minor;
+ vu->vbi = VIDEO_NO_UNIT;
+ vu->radio = VIDEO_NO_UNIT;
+ vu->audio = VIDEO_NO_UNIT;
+ vu->teletext = VIDEO_NO_UNIT;
+
+ return 0;
+ }
+ case OV511IOC_WI2C:
+ {
+ struct ov511_i2c_struct *w = arg;
+
+ return i2c_w_slave(ov, w->slave, w->reg, w->value, w->mask);
+ }
+ case OV511IOC_RI2C:
+ {
+ struct ov511_i2c_struct *r = arg;
+ int rc;
+
+ rc = i2c_r_slave(ov, r->slave, r->reg);
+ if (rc < 0)
+ return rc;
+
+ r->value = rc;
+ return 0;
+ }
+ default:
+ PDEBUG(3, "Unsupported IOCtl: 0x%X", cmd);
+ return -ENOIOCTLCMD;
+ } /* end switch */
+
+ return 0;
+}
+
+static int
+ov51x_v4l1_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vdev = file->private_data;
+ struct usb_ov511 *ov = video_get_drvdata(vdev);
+ int rc;
+
+ if (mutex_lock_interruptible(&ov->lock))
+ return -EINTR;
+
+ rc = video_usercopy(inode, file, cmd, arg, ov51x_v4l1_ioctl_internal);
+
+ mutex_unlock(&ov->lock);
+ return rc;
+}
+
+static ssize_t
+ov51x_v4l1_read(struct file *file, char __user *buf, size_t cnt, loff_t *ppos)
+{
+ struct video_device *vdev = file->private_data;
+ int noblock = file->f_flags&O_NONBLOCK;
+ unsigned long count = cnt;
+ struct usb_ov511 *ov = video_get_drvdata(vdev);
+ int i, rc = 0, frmx = -1;
+ struct ov511_frame *frame;
+
+ if (mutex_lock_interruptible(&ov->lock))
+ return -EINTR;
+
+ PDEBUG(4, "%ld bytes, noblock=%d", count, noblock);
+
+ if (!vdev || !buf) {
+ rc = -EFAULT;
+ goto error;
+ }
+
+ if (!ov->dev) {
+ rc = -EIO;
+ goto error;
+ }
+
+// FIXME: Only supports two frames
+ /* See if a frame is completed, then use it. */
+ if (ov->frame[0].grabstate >= FRAME_DONE) /* _DONE or _ERROR */
+ frmx = 0;
+ else if (ov->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */
+ frmx = 1;
+
+ /* If nonblocking we return immediately */
+ if (noblock && (frmx == -1)) {
+ rc = -EAGAIN;
+ goto error;
+ }
+
+ /* If no FRAME_DONE, look for a FRAME_GRABBING state. */
+ /* See if a frame is in process (grabbing), then use it. */
+ if (frmx == -1) {
+ if (ov->frame[0].grabstate == FRAME_GRABBING)
+ frmx = 0;
+ else if (ov->frame[1].grabstate == FRAME_GRABBING)
+ frmx = 1;
+ }
+
+ /* If no frame is active, start one. */
+ if (frmx == -1) {
+ if ((rc = ov51x_new_frame(ov, frmx = 0))) {
+ err("read: ov51x_new_frame error");
+ goto error;
+ }
+ }
+
+ frame = &ov->frame[frmx];
+
+restart:
+ if (!ov->dev) {
+ rc = -EIO;
+ goto error;
+ }
+
+ /* Wait while we're grabbing the image */
+ PDEBUG(4, "Waiting image grabbing");
+ rc = wait_event_interruptible(frame->wq,
+ (frame->grabstate == FRAME_DONE)
+ || (frame->grabstate == FRAME_ERROR));
+
+ if (rc)
+ goto error;
+
+ PDEBUG(4, "Got image, frame->grabstate = %d", frame->grabstate);
+ PDEBUG(4, "bytes_recvd = %d", frame->bytes_recvd);
+
+ if (frame->grabstate == FRAME_ERROR) {
+ frame->bytes_read = 0;
+ err("** ick! ** Errored frame %d", ov->curframe);
+ if (ov51x_new_frame(ov, frmx)) {
+ err("read: ov51x_new_frame error");
+ goto error;
+ }
+ goto restart;
+ }
+
+
+ /* Repeat until we get a snapshot frame */
+ if (ov->snap_enabled)
+ PDEBUG(4, "Waiting snapshot frame");
+ if (ov->snap_enabled && !frame->snapshot) {
+ frame->bytes_read = 0;
+ if ((rc = ov51x_new_frame(ov, frmx))) {
+ err("read: ov51x_new_frame error");
+ goto error;
+ }
+ goto restart;
+ }
+
+ /* Clear the snapshot */
+ if (ov->snap_enabled && frame->snapshot) {
+ frame->snapshot = 0;
+ ov51x_clear_snapshot(ov);
+ }
+
+ /* Decompression, format conversion, etc... */
+ ov51x_postprocess(ov, frame);
+
+ PDEBUG(4, "frmx=%d, bytes_read=%ld, length=%ld", frmx,
+ frame->bytes_read,
+ get_frame_length(frame));
+
+ /* copy bytes to user space; we allow for partials reads */
+// if ((count + frame->bytes_read)
+// > get_frame_length((struct ov511_frame *)frame))
+// count = frame->scanlength - frame->bytes_read;
+
+ /* FIXME - count hardwired to be one frame... */
+ count = get_frame_length(frame);
+
+ PDEBUG(4, "Copy to user space: %ld bytes", count);
+ if ((i = copy_to_user(buf, frame->data + frame->bytes_read, count))) {
+ PDEBUG(4, "Copy failed! %d bytes not copied", i);
+ rc = -EFAULT;
+ goto error;
+ }
+
+ frame->bytes_read += count;
+ PDEBUG(4, "{copy} count used=%ld, new bytes_read=%ld",
+ count, frame->bytes_read);
+
+ /* If all data have been read... */
+ if (frame->bytes_read
+ >= get_frame_length(frame)) {
+ frame->bytes_read = 0;
+
+// FIXME: Only supports two frames
+ /* Mark it as available to be used again. */
+ ov->frame[frmx].grabstate = FRAME_UNUSED;
+ if ((rc = ov51x_new_frame(ov, !frmx))) {
+ err("ov51x_new_frame returned error");
+ goto error;
+ }
+ }
+
+ PDEBUG(4, "read finished, returning %ld (sweet)", count);
+
+ mutex_unlock(&ov->lock);
+ return count;
+
+error:
+ mutex_unlock(&ov->lock);
+ return rc;
+}
+
+static int
+ov51x_v4l1_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *vdev = file->private_data;
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ struct usb_ov511 *ov = video_get_drvdata(vdev);
+ unsigned long page, pos;
+
+ if (ov->dev == NULL)
+ return -EIO;
+
+ PDEBUG(4, "mmap: %ld (%lX) bytes", size, size);
+
+ if (size > (((OV511_NUMFRAMES
+ * MAX_DATA_SIZE(ov->maxwidth, ov->maxheight)
+ + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))))
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&ov->lock))
+ return -EINTR;
+
+ pos = (unsigned long)ov->fbuf;
+ while (size > 0) {
+ page = vmalloc_to_pfn((void *)pos);
+ if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
+ mutex_unlock(&ov->lock);
+ return -EAGAIN;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+
+ mutex_unlock(&ov->lock);
+ return 0;
+}
+
+static struct file_operations ov511_fops = {
+ .owner = THIS_MODULE,
+ .open = ov51x_v4l1_open,
+ .release = ov51x_v4l1_close,
+ .read = ov51x_v4l1_read,
+ .mmap = ov51x_v4l1_mmap,
+ .ioctl = ov51x_v4l1_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device vdev_template = {
+ .owner = THIS_MODULE,
+ .name = "OV511 USB Camera",
+ .type = VID_TYPE_CAPTURE,
+ .hardware = VID_HARDWARE_OV511,
+ .fops = &ov511_fops,
+ .release = video_device_release,
+ .minor = -1,
+};
+
+/****************************************************************************
+ *
+ * OV511 and sensor configuration
+ *
+ ***************************************************************************/
+
+/* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses
+ * the same register settings as the OV7610, since they are very similar.
+ */
+static int
+ov7xx0_configure(struct usb_ov511 *ov)
+{
+ int i, success;
+ int rc;
+
+ /* Lawrence Glaister <lg@jfm.bc.ca> reports:
+ *
+ * Register 0x0f in the 7610 has the following effects:
+ *
+ * 0x85 (AEC method 1): Best overall, good contrast range
+ * 0x45 (AEC method 2): Very overexposed
+ * 0xa5 (spec sheet default): Ok, but the black level is
+ * shifted resulting in loss of contrast
+ * 0x05 (old driver setting): very overexposed, too much
+ * contrast
+ */
+ static struct ov511_regvals aRegvalsNorm7610[] = {
+ { OV511_I2C_BUS, 0x10, 0xff },
+ { OV511_I2C_BUS, 0x16, 0x06 },
+ { OV511_I2C_BUS, 0x28, 0x24 },
+ { OV511_I2C_BUS, 0x2b, 0xac },
+ { OV511_I2C_BUS, 0x12, 0x00 },
+ { OV511_I2C_BUS, 0x38, 0x81 },
+ { OV511_I2C_BUS, 0x28, 0x24 }, /* 0c */
+ { OV511_I2C_BUS, 0x0f, 0x85 }, /* lg's setting */
+ { OV511_I2C_BUS, 0x15, 0x01 },
+ { OV511_I2C_BUS, 0x20, 0x1c },
+ { OV511_I2C_BUS, 0x23, 0x2a },
+ { OV511_I2C_BUS, 0x24, 0x10 },
+ { OV511_I2C_BUS, 0x25, 0x8a },
+ { OV511_I2C_BUS, 0x26, 0xa2 },
+ { OV511_I2C_BUS, 0x27, 0xc2 },
+ { OV511_I2C_BUS, 0x2a, 0x04 },
+ { OV511_I2C_BUS, 0x2c, 0xfe },
+ { OV511_I2C_BUS, 0x2d, 0x93 },
+ { OV511_I2C_BUS, 0x30, 0x71 },
+ { OV511_I2C_BUS, 0x31, 0x60 },
+ { OV511_I2C_BUS, 0x32, 0x26 },
+ { OV511_I2C_BUS, 0x33, 0x20 },
+ { OV511_I2C_BUS, 0x34, 0x48 },
+ { OV511_I2C_BUS, 0x12, 0x24 },
+ { OV511_I2C_BUS, 0x11, 0x01 },
+ { OV511_I2C_BUS, 0x0c, 0x24 },
+ { OV511_I2C_BUS, 0x0d, 0x24 },
+ { OV511_DONE_BUS, 0x0, 0x00 },
+ };
+
+ static struct ov511_regvals aRegvalsNorm7620[] = {
+ { OV511_I2C_BUS, 0x00, 0x00 },
+ { OV511_I2C_BUS, 0x01, 0x80 },
+ { OV511_I2C_BUS, 0x02, 0x80 },
+ { OV511_I2C_BUS, 0x03, 0xc0 },
+ { OV511_I2C_BUS, 0x06, 0x60 },
+ { OV511_I2C_BUS, 0x07, 0x00 },
+ { OV511_I2C_BUS, 0x0c, 0x24 },
+ { OV511_I2C_BUS, 0x0c, 0x24 },
+ { OV511_I2C_BUS, 0x0d, 0x24 },
+ { OV511_I2C_BUS, 0x11, 0x01 },
+ { OV511_I2C_BUS, 0x12, 0x24 },
+ { OV511_I2C_BUS, 0x13, 0x01 },
+ { OV511_I2C_BUS, 0x14, 0x84 },
+ { OV511_I2C_BUS, 0x15, 0x01 },
+ { OV511_I2C_BUS, 0x16, 0x03 },
+ { OV511_I2C_BUS, 0x17, 0x2f },
+ { OV511_I2C_BUS, 0x18, 0xcf },
+ { OV511_I2C_BUS, 0x19, 0x06 },
+ { OV511_I2C_BUS, 0x1a, 0xf5 },
+ { OV511_I2C_BUS, 0x1b, 0x00 },
+ { OV511_I2C_BUS, 0x20, 0x18 },
+ { OV511_I2C_BUS, 0x21, 0x80 },
+ { OV511_I2C_BUS, 0x22, 0x80 },
+ { OV511_I2C_BUS, 0x23, 0x00 },
+ { OV511_I2C_BUS, 0x26, 0xa2 },
+ { OV511_I2C_BUS, 0x27, 0xea },
+ { OV511_I2C_BUS, 0x28, 0x20 },
+ { OV511_I2C_BUS, 0x29, 0x00 },
+ { OV511_I2C_BUS, 0x2a, 0x10 },
+ { OV511_I2C_BUS, 0x2b, 0x00 },
+ { OV511_I2C_BUS, 0x2c, 0x88 },
+ { OV511_I2C_BUS, 0x2d, 0x91 },
+ { OV511_I2C_BUS, 0x2e, 0x80 },
+ { OV511_I2C_BUS, 0x2f, 0x44 },
+ { OV511_I2C_BUS, 0x60, 0x27 },
+ { OV511_I2C_BUS, 0x61, 0x02 },
+ { OV511_I2C_BUS, 0x62, 0x5f },
+ { OV511_I2C_BUS, 0x63, 0xd5 },
+ { OV511_I2C_BUS, 0x64, 0x57 },
+ { OV511_I2C_BUS, 0x65, 0x83 },
+ { OV511_I2C_BUS, 0x66, 0x55 },
+ { OV511_I2C_BUS, 0x67, 0x92 },
+ { OV511_I2C_BUS, 0x68, 0xcf },
+ { OV511_I2C_BUS, 0x69, 0x76 },
+ { OV511_I2C_BUS, 0x6a, 0x22 },
+ { OV511_I2C_BUS, 0x6b, 0x00 },
+ { OV511_I2C_BUS, 0x6c, 0x02 },
+ { OV511_I2C_BUS, 0x6d, 0x44 },
+ { OV511_I2C_BUS, 0x6e, 0x80 },
+ { OV511_I2C_BUS, 0x6f, 0x1d },
+ { OV511_I2C_BUS, 0x70, 0x8b },
+ { OV511_I2C_BUS, 0x71, 0x00 },
+ { OV511_I2C_BUS, 0x72, 0x14 },
+ { OV511_I2C_BUS, 0x73, 0x54 },
+ { OV511_I2C_BUS, 0x74, 0x00 },
+ { OV511_I2C_BUS, 0x75, 0x8e },
+ { OV511_I2C_BUS, 0x76, 0x00 },
+ { OV511_I2C_BUS, 0x77, 0xff },
+ { OV511_I2C_BUS, 0x78, 0x80 },
+ { OV511_I2C_BUS, 0x79, 0x80 },
+ { OV511_I2C_BUS, 0x7a, 0x80 },
+ { OV511_I2C_BUS, 0x7b, 0xe2 },
+ { OV511_I2C_BUS, 0x7c, 0x00 },
+ { OV511_DONE_BUS, 0x0, 0x00 },
+ };
+
+ PDEBUG(4, "starting configuration");
+
+ /* This looks redundant, but is necessary for WebCam 3 */
+ ov->primary_i2c_slave = OV7xx0_SID;
+ if (ov51x_set_slave_ids(ov, OV7xx0_SID) < 0)
+ return -1;
+
+ if (init_ov_sensor(ov) >= 0) {
+ PDEBUG(1, "OV7xx0 sensor initalized (method 1)");
+ } else {
+ /* Reset the 76xx */
+ if (i2c_w(ov, 0x12, 0x80) < 0)
+ return -1;
+
+ /* Wait for it to initialize */
+ msleep(150);
+
+ i = 0;
+ success = 0;
+ while (i <= i2c_detect_tries) {
+ if ((i2c_r(ov, OV7610_REG_ID_HIGH) == 0x7F) &&
+ (i2c_r(ov, OV7610_REG_ID_LOW) == 0xA2)) {
+ success = 1;
+ break;
+ } else {
+ i++;
+ }
+ }
+
+// Was (i == i2c_detect_tries) previously. This obviously used to always report
+// success. Whether anyone actually depended on that bug is unknown
+ if ((i >= i2c_detect_tries) && (success == 0)) {
+ err("Failed to read sensor ID. You might not have an");
+ err("OV7610/20, or it may be not responding. Report");
+ err("this to " EMAIL);
+ err("This is only a warning. You can attempt to use");
+ err("your camera anyway");
+// Only issue a warning for now
+// return -1;
+ } else {
+ PDEBUG(1, "OV7xx0 initialized (method 2, %dx)", i+1);
+ }
+ }
+
+ /* Detect sensor (sub)type */
+ rc = i2c_r(ov, OV7610_REG_COM_I);
+
+ if (rc < 0) {
+ err("Error detecting sensor type");
+ return -1;
+ } else if ((rc & 3) == 3) {
+ info("Sensor is an OV7610");
+ ov->sensor = SEN_OV7610;
+ } else if ((rc & 3) == 1) {
+ /* I don't know what's different about the 76BE yet. */
+ if (i2c_r(ov, 0x15) & 1)
+ info("Sensor is an OV7620AE");
+ else
+ info("Sensor is an OV76BE");
+
+ /* OV511+ will return all zero isoc data unless we
+ * configure the sensor as a 7620. Someone needs to
+ * find the exact reg. setting that causes this. */
+ if (ov->bridge == BRG_OV511PLUS) {
+ info("Enabling 511+/7620AE workaround");
+ ov->sensor = SEN_OV7620;
+ } else {
+ ov->sensor = SEN_OV76BE;
+ }
+ } else if ((rc & 3) == 0) {
+ info("Sensor is an OV7620");
+ ov->sensor = SEN_OV7620;
+ } else {
+ err("Unknown image sensor version: %d", rc & 3);
+ return -1;
+ }
+
+ if (ov->sensor == SEN_OV7620) {
+ PDEBUG(4, "Writing 7620 registers");
+ if (write_regvals(ov, aRegvalsNorm7620))
+ return -1;
+ } else {
+ PDEBUG(4, "Writing 7610 registers");
+ if (write_regvals(ov, aRegvalsNorm7610))
+ return -1;
+ }
+
+ /* Set sensor-specific vars */
+ ov->maxwidth = 640;
+ ov->maxheight = 480;
+ ov->minwidth = 64;
+ ov->minheight = 48;
+
+ // FIXME: These do not match the actual settings yet
+ ov->brightness = 0x80 << 8;
+ ov->contrast = 0x80 << 8;
+ ov->colour = 0x80 << 8;
+ ov->hue = 0x80 << 8;
+
+ return 0;
+}
+
+/* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */
+static int
+ov6xx0_configure(struct usb_ov511 *ov)
+{
+ int rc;
+
+ static struct ov511_regvals aRegvalsNorm6x20[] = {
+ { OV511_I2C_BUS, 0x12, 0x80 }, /* reset */
+ { OV511_I2C_BUS, 0x11, 0x01 },
+ { OV511_I2C_BUS, 0x03, 0x60 },
+ { OV511_I2C_BUS, 0x05, 0x7f }, /* For when autoadjust is off */
+ { OV511_I2C_BUS, 0x07, 0xa8 },
+ /* The ratio of 0x0c and 0x0d controls the white point */
+ { OV511_I2C_BUS, 0x0c, 0x24 },
+ { OV511_I2C_BUS, 0x0d, 0x24 },
+ { OV511_I2C_BUS, 0x0f, 0x15 }, /* COMS */
+ { OV511_I2C_BUS, 0x10, 0x75 }, /* AEC Exposure time */
+ { OV511_I2C_BUS, 0x12, 0x24 }, /* Enable AGC */
+ { OV511_I2C_BUS, 0x14, 0x04 },
+ /* 0x16: 0x06 helps frame stability with moving objects */
+ { OV511_I2C_BUS, 0x16, 0x06 },
+// { OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */
+ { OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */
+ /* 0x28: 0x05 Selects RGB format if RGB on */
+ { OV511_I2C_BUS, 0x28, 0x05 },
+ { OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */
+// { OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */
+ { OV511_I2C_BUS, 0x2d, 0x99 },
+ { OV511_I2C_BUS, 0x33, 0xa0 }, /* Color Processing Parameter */
+ { OV511_I2C_BUS, 0x34, 0xd2 }, /* Max A/D range */
+ { OV511_I2C_BUS, 0x38, 0x8b },
+ { OV511_I2C_BUS, 0x39, 0x40 },
+
+ { OV511_I2C_BUS, 0x3c, 0x39 }, /* Enable AEC mode changing */
+ { OV511_I2C_BUS, 0x3c, 0x3c }, /* Change AEC mode */
+ { OV511_I2C_BUS, 0x3c, 0x24 }, /* Disable AEC mode changing */
+
+ { OV511_I2C_BUS, 0x3d, 0x80 },
+ /* These next two registers (0x4a, 0x4b) are undocumented. They
+ * control the color balance */
+ { OV511_I2C_BUS, 0x4a, 0x80 },
+ { OV511_I2C_BUS, 0x4b, 0x80 },
+ { OV511_I2C_BUS, 0x4d, 0xd2 }, /* This reduces noise a bit */
+ { OV511_I2C_BUS, 0x4e, 0xc1 },
+ { OV511_I2C_BUS, 0x4f, 0x04 },
+// Do 50-53 have any effect?
+// Toggle 0x12[2] off and on here?
+ { OV511_DONE_BUS, 0x0, 0x00 }, /* END MARKER */
+ };
+
+ static struct ov511_regvals aRegvalsNorm6x30[] = {
+ /*OK*/ { OV511_I2C_BUS, 0x12, 0x80 }, /* reset */
+ { OV511_I2C_BUS, 0x11, 0x00 },
+ /*OK*/ { OV511_I2C_BUS, 0x03, 0x60 },
+ /*0A?*/ { OV511_I2C_BUS, 0x05, 0x7f }, /* For when autoadjust is off */
+ { OV511_I2C_BUS, 0x07, 0xa8 },
+ /* The ratio of 0x0c and 0x0d controls the white point */
+ /*OK*/ { OV511_I2C_BUS, 0x0c, 0x24 },
+ /*OK*/ { OV511_I2C_BUS, 0x0d, 0x24 },
+ /*A*/ { OV511_I2C_BUS, 0x0e, 0x20 },
+// /*04?*/ { OV511_I2C_BUS, 0x14, 0x80 },
+ { OV511_I2C_BUS, 0x16, 0x03 },
+// /*OK*/ { OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */
+ // 21 & 22? The suggested values look wrong. Go with default
+ /*A*/ { OV511_I2C_BUS, 0x23, 0xc0 },
+ /*A*/ { OV511_I2C_BUS, 0x25, 0x9a }, // Check this against default
+// /*OK*/ { OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */
+
+ /* 0x28: 0x05 Selects RGB format if RGB on */
+// /*04?*/ { OV511_I2C_BUS, 0x28, 0x05 },
+// /*04?*/ { OV511_I2C_BUS, 0x28, 0x45 }, // DEBUG: Tristate UV bus
+
+ /*OK*/ { OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */
+// /*OK*/ { OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */
+ { OV511_I2C_BUS, 0x2d, 0x99 },
+// /*A*/ { OV511_I2C_BUS, 0x33, 0x26 }, // Reserved bits on 6620
+// /*d2?*/ { OV511_I2C_BUS, 0x34, 0x03 }, /* Max A/D range */
+// /*8b?*/ { OV511_I2C_BUS, 0x38, 0x83 },
+// /*40?*/ { OV511_I2C_BUS, 0x39, 0xc0 }, // 6630 adds bit 7
+// { OV511_I2C_BUS, 0x3c, 0x39 }, /* Enable AEC mode changing */
+// { OV511_I2C_BUS, 0x3c, 0x3c }, /* Change AEC mode */
+// { OV511_I2C_BUS, 0x3c, 0x24 }, /* Disable AEC mode changing */
+ { OV511_I2C_BUS, 0x3d, 0x80 },
+// /*A*/ { OV511_I2C_BUS, 0x3f, 0x0e },
+
+ /* These next two registers (0x4a, 0x4b) are undocumented. They
+ * control the color balance */
+// /*OK?*/ { OV511_I2C_BUS, 0x4a, 0x80 }, // Check these
+// /*OK?*/ { OV511_I2C_BUS, 0x4b, 0x80 },
+ { OV511_I2C_BUS, 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */
+ /*c1?*/ { OV511_I2C_BUS, 0x4e, 0x40 },
+
+ /* UV average mode, color killer: strongest */
+ { OV511_I2C_BUS, 0x4f, 0x07 },
+
+ { OV511_I2C_BUS, 0x54, 0x23 }, /* Max AGC gain: 18dB */
+ { OV511_I2C_BUS, 0x57, 0x81 }, /* (default) */
+ { OV511_I2C_BUS, 0x59, 0x01 }, /* AGC dark current comp: +1 */
+ { OV511_I2C_BUS, 0x5a, 0x2c }, /* (undocumented) */
+ { OV511_I2C_BUS, 0x5b, 0x0f }, /* AWB chrominance levels */
+// { OV511_I2C_BUS, 0x5c, 0x10 },
+ { OV511_DONE_BUS, 0x0, 0x00 }, /* END MARKER */
+ };
+
+ PDEBUG(4, "starting sensor configuration");
+
+ if (init_ov_sensor(ov) < 0) {
+ err("Failed to read sensor ID. You might not have an OV6xx0,");
+ err("or it may be not responding. Report this to " EMAIL);
+ return -1;
+ } else {
+ PDEBUG(1, "OV6xx0 sensor detected");
+ }
+
+ /* Detect sensor (sub)type */
+ rc = i2c_r(ov, OV7610_REG_COM_I);
+
+ if (rc < 0) {
+ err("Error detecting sensor type");
+ return -1;
+ }
+
+ if ((rc & 3) == 0) {
+ ov->sensor = SEN_OV6630;
+ info("Sensor is an OV6630");
+ } else if ((rc & 3) == 1) {
+ ov->sensor = SEN_OV6620;
+ info("Sensor is an OV6620");
+ } else if ((rc & 3) == 2) {
+ ov->sensor = SEN_OV6630;
+ info("Sensor is an OV6630AE");
+ } else if ((rc & 3) == 3) {
+ ov->sensor = SEN_OV6630;
+ info("Sensor is an OV6630AF");
+ }
+
+ /* Set sensor-specific vars */
+ ov->maxwidth = 352;
+ ov->maxheight = 288;
+ ov->minwidth = 64;
+ ov->minheight = 48;
+
+ // FIXME: These do not match the actual settings yet
+ ov->brightness = 0x80 << 8;
+ ov->contrast = 0x80 << 8;
+ ov->colour = 0x80 << 8;
+ ov->hue = 0x80 << 8;
+
+ if (ov->sensor == SEN_OV6620) {
+ PDEBUG(4, "Writing 6x20 registers");
+ if (write_regvals(ov, aRegvalsNorm6x20))
+ return -1;
+ } else {
+ PDEBUG(4, "Writing 6x30 registers");
+ if (write_regvals(ov, aRegvalsNorm6x30))
+ return -1;
+ }
+
+ return 0;
+}
+
+/* This initializes the KS0127 and KS0127B video decoders. */
+static int
+ks0127_configure(struct usb_ov511 *ov)
+{
+ int rc;
+
+// FIXME: I don't know how to sync or reset it yet
+#if 0
+ if (ov51x_init_ks_sensor(ov) < 0) {
+ err("Failed to initialize the KS0127");
+ return -1;
+ } else {
+ PDEBUG(1, "KS012x(B) sensor detected");
+ }
+#endif
+
+ /* Detect decoder subtype */
+ rc = i2c_r(ov, 0x00);
+ if (rc < 0) {
+ err("Error detecting sensor type");
+ return -1;
+ } else if (rc & 0x08) {
+ rc = i2c_r(ov, 0x3d);
+ if (rc < 0) {
+ err("Error detecting sensor type");
+ return -1;
+ } else if ((rc & 0x0f) == 0) {
+ info("Sensor is a KS0127");
+ ov->sensor = SEN_KS0127;
+ } else if ((rc & 0x0f) == 9) {
+ info("Sensor is a KS0127B Rev. A");
+ ov->sensor = SEN_KS0127B;
+ }
+ } else {
+ err("Error: Sensor is an unsupported KS0122");
+ return -1;
+ }
+
+ /* Set sensor-specific vars */
+ ov->maxwidth = 640;
+ ov->maxheight = 480;
+ ov->minwidth = 64;
+ ov->minheight = 48;
+
+ // FIXME: These do not match the actual settings yet
+ ov->brightness = 0x80 << 8;
+ ov->contrast = 0x80 << 8;
+ ov->colour = 0x80 << 8;
+ ov->hue = 0x80 << 8;
+
+ /* This device is not supported yet. Bail out now... */
+ err("This sensor is not supported yet.");
+ return -1;
+
+ return 0;
+}
+
+/* This initializes the SAA7111A video decoder. */
+static int
+saa7111a_configure(struct usb_ov511 *ov)
+{
+ int rc;
+
+ /* Since there is no register reset command, all registers must be
+ * written, otherwise gives erratic results */
+ static struct ov511_regvals aRegvalsNormSAA7111A[] = {
+ { OV511_I2C_BUS, 0x06, 0xce },
+ { OV511_I2C_BUS, 0x07, 0x00 },
+ { OV511_I2C_BUS, 0x10, 0x44 }, /* YUV422, 240/286 lines */
+ { OV511_I2C_BUS, 0x0e, 0x01 }, /* NTSC M or PAL BGHI */
+ { OV511_I2C_BUS, 0x00, 0x00 },
+ { OV511_I2C_BUS, 0x01, 0x00 },
+ { OV511_I2C_BUS, 0x03, 0x23 },
+ { OV511_I2C_BUS, 0x04, 0x00 },
+ { OV511_I2C_BUS, 0x05, 0x00 },
+ { OV511_I2C_BUS, 0x08, 0xc8 }, /* Auto field freq */
+ { OV511_I2C_BUS, 0x09, 0x01 }, /* Chrom. trap off, APER=0.25 */
+ { OV511_I2C_BUS, 0x0a, 0x80 }, /* BRIG=128 */
+ { OV511_I2C_BUS, 0x0b, 0x40 }, /* CONT=1.0 */
+ { OV511_I2C_BUS, 0x0c, 0x40 }, /* SATN=1.0 */
+ { OV511_I2C_BUS, 0x0d, 0x00 }, /* HUE=0 */
+ { OV511_I2C_BUS, 0x0f, 0x00 },
+ { OV511_I2C_BUS, 0x11, 0x0c },
+ { OV511_I2C_BUS, 0x12, 0x00 },
+ { OV511_I2C_BUS, 0x13, 0x00 },
+ { OV511_I2C_BUS, 0x14, 0x00 },
+ { OV511_I2C_BUS, 0x15, 0x00 },
+ { OV511_I2C_BUS, 0x16, 0x00 },
+ { OV511_I2C_BUS, 0x17, 0x00 },
+ { OV511_I2C_BUS, 0x02, 0xc0 }, /* Composite input 0 */
+ { OV511_DONE_BUS, 0x0, 0x00 },
+ };
+
+// FIXME: I don't know how to sync or reset it yet
+#if 0
+ if (ov51x_init_saa_sensor(ov) < 0) {
+ err("Failed to initialize the SAA7111A");
+ return -1;
+ } else {
+ PDEBUG(1, "SAA7111A sensor detected");
+ }
+#endif
+
+ /* 640x480 not supported with PAL */
+ if (ov->pal) {
+ ov->maxwidth = 320;
+ ov->maxheight = 240; /* Even field only */
+ } else {
+ ov->maxwidth = 640;
+ ov->maxheight = 480; /* Even/Odd fields */
+ }
+
+ ov->minwidth = 320;
+ ov->minheight = 240; /* Even field only */
+
+ ov->has_decoder = 1;
+ ov->num_inputs = 8;
+ ov->norm = VIDEO_MODE_AUTO;
+ ov->stop_during_set = 0; /* Decoder guarantees stable image */
+
+ /* Decoder doesn't change these values, so we use these instead of
+ * acutally reading the registers (which doesn't work) */
+ ov->brightness = 0x80 << 8;
+ ov->contrast = 0x40 << 9;
+ ov->colour = 0x40 << 9;
+ ov->hue = 32768;
+
+ PDEBUG(4, "Writing SAA7111A registers");
+ if (write_regvals(ov, aRegvalsNormSAA7111A))
+ return -1;
+
+ /* Detect version of decoder. This must be done after writing the
+ * initial regs or the decoder will lock up. */
+ rc = i2c_r(ov, 0x00);
+
+ if (rc < 0) {
+ err("Error detecting sensor version");
+ return -1;
+ } else {
+ info("Sensor is an SAA7111A (version 0x%x)", rc);
+ ov->sensor = SEN_SAA7111A;
+ }
+
+ // FIXME: Fix this for OV518(+)
+ /* Latch to negative edge of clock. Otherwise, we get incorrect
+ * colors and jitter in the digital signal. */
+ if (ov->bclass == BCL_OV511)
+ reg_w(ov, 0x11, 0x00);
+ else
+ warn("SAA7111A not yet supported with OV518/OV518+");
+
+ return 0;
+}
+
+/* This initializes the OV511/OV511+ and the sensor */
+static int
+ov511_configure(struct usb_ov511 *ov)
+{
+ static struct ov511_regvals aRegvalsInit511[] = {
+ { OV511_REG_BUS, R51x_SYS_RESET, 0x7f },
+ { OV511_REG_BUS, R51x_SYS_INIT, 0x01 },
+ { OV511_REG_BUS, R51x_SYS_RESET, 0x7f },
+ { OV511_REG_BUS, R51x_SYS_INIT, 0x01 },
+ { OV511_REG_BUS, R51x_SYS_RESET, 0x3f },
+ { OV511_REG_BUS, R51x_SYS_INIT, 0x01 },
+ { OV511_REG_BUS, R51x_SYS_RESET, 0x3d },
+ { OV511_DONE_BUS, 0x0, 0x00},
+ };
+
+ static struct ov511_regvals aRegvalsNorm511[] = {
+ { OV511_REG_BUS, R511_DRAM_FLOW_CTL, 0x01 },
+ { OV511_REG_BUS, R51x_SYS_SNAP, 0x00 },
+ { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 },
+ { OV511_REG_BUS, R51x_SYS_SNAP, 0x00 },
+ { OV511_REG_BUS, R511_FIFO_OPTS, 0x1f },
+ { OV511_REG_BUS, R511_COMP_EN, 0x00 },
+ { OV511_REG_BUS, R511_COMP_LUT_EN, 0x03 },
+ { OV511_DONE_BUS, 0x0, 0x00 },
+ };
+
+ static struct ov511_regvals aRegvalsNorm511Plus[] = {
+ { OV511_REG_BUS, R511_DRAM_FLOW_CTL, 0xff },
+ { OV511_REG_BUS, R51x_SYS_SNAP, 0x00 },
+ { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 },
+ { OV511_REG_BUS, R51x_SYS_SNAP, 0x00 },
+ { OV511_REG_BUS, R511_FIFO_OPTS, 0xff },
+ { OV511_REG_BUS, R511_COMP_EN, 0x00 },
+ { OV511_REG_BUS, R511_COMP_LUT_EN, 0x03 },
+ { OV511_DONE_BUS, 0x0, 0x00 },
+ };
+
+ PDEBUG(4, "");
+
+ ov->customid = reg_r(ov, R511_SYS_CUST_ID);
+ if (ov->customid < 0) {
+ err("Unable to read camera bridge registers");
+ goto error;
+ }
+
+ PDEBUG (1, "CustomID = %d", ov->customid);
+ ov->desc = symbolic(camlist, ov->customid);
+ info("model: %s", ov->desc);
+
+ if (0 == strcmp(ov->desc, NOT_DEFINED_STR)) {
+ err("Camera type (%d) not recognized", ov->customid);
+ err("Please notify " EMAIL " of the name,");
+ err("manufacturer, model, and this number of your camera.");
+ err("Also include the output of the detection process.");
+ }
+
+ if (ov->customid == 70) /* USB Life TV (PAL/SECAM) */
+ ov->pal = 1;
+
+ if (write_regvals(ov, aRegvalsInit511))
+ goto error;
+
+ if (ov->led_policy == LED_OFF || ov->led_policy == LED_AUTO)
+ ov51x_led_control(ov, 0);
+
+ /* The OV511+ has undocumented bits in the flow control register.
+ * Setting it to 0xff fixes the corruption with moving objects. */
+ if (ov->bridge == BRG_OV511) {
+ if (write_regvals(ov, aRegvalsNorm511))
+ goto error;
+ } else if (ov->bridge == BRG_OV511PLUS) {
+ if (write_regvals(ov, aRegvalsNorm511Plus))
+ goto error;
+ } else {
+ err("Invalid bridge");
+ }
+
+ if (ov511_init_compression(ov))
+ goto error;
+
+ ov->packet_numbering = 1;
+ ov511_set_packet_size(ov, 0);
+
+ ov->snap_enabled = snapshot;
+
+ /* Test for 7xx0 */
+ PDEBUG(3, "Testing for 0V7xx0");
+ ov->primary_i2c_slave = OV7xx0_SID;
+ if (ov51x_set_slave_ids(ov, OV7xx0_SID) < 0)
+ goto error;
+
+ if (i2c_w(ov, 0x12, 0x80) < 0) {
+ /* Test for 6xx0 */
+ PDEBUG(3, "Testing for 0V6xx0");
+ ov->primary_i2c_slave = OV6xx0_SID;
+ if (ov51x_set_slave_ids(ov, OV6xx0_SID) < 0)
+ goto error;
+
+ if (i2c_w(ov, 0x12, 0x80) < 0) {
+ /* Test for 8xx0 */
+ PDEBUG(3, "Testing for 0V8xx0");
+ ov->primary_i2c_slave = OV8xx0_SID;
+ if (ov51x_set_slave_ids(ov, OV8xx0_SID) < 0)
+ goto error;
+
+ if (i2c_w(ov, 0x12, 0x80) < 0) {
+ /* Test for SAA7111A */
+ PDEBUG(3, "Testing for SAA7111A");
+ ov->primary_i2c_slave = SAA7111A_SID;
+ if (ov51x_set_slave_ids(ov, SAA7111A_SID) < 0)
+ goto error;
+
+ if (i2c_w(ov, 0x0d, 0x00) < 0) {
+ /* Test for KS0127 */
+ PDEBUG(3, "Testing for KS0127");
+ ov->primary_i2c_slave = KS0127_SID;
+ if (ov51x_set_slave_ids(ov, KS0127_SID) < 0)
+ goto error;
+
+ if (i2c_w(ov, 0x10, 0x00) < 0) {
+ err("Can't determine sensor slave IDs");
+ goto error;
+ } else {
+ if (ks0127_configure(ov) < 0) {
+ err("Failed to configure KS0127");
+ goto error;
+ }
+ }
+ } else {
+ if (saa7111a_configure(ov) < 0) {
+ err("Failed to configure SAA7111A");
+ goto error;
+ }
+ }
+ } else {
+ err("Detected unsupported OV8xx0 sensor");
+ goto error;
+ }
+ } else {
+ if (ov6xx0_configure(ov) < 0) {
+ err("Failed to configure OV6xx0");
+ goto error;
+ }
+ }
+ } else {
+ if (ov7xx0_configure(ov) < 0) {
+ err("Failed to configure OV7xx0");
+ goto error;
+ }
+ }
+
+ return 0;
+
+error:
+ err("OV511 Config failed");
+
+ return -EBUSY;
+}
+
+/* This initializes the OV518/OV518+ and the sensor */
+static int
+ov518_configure(struct usb_ov511 *ov)
+{
+ /* For 518 and 518+ */
+ static struct ov511_regvals aRegvalsInit518[] = {
+ { OV511_REG_BUS, R51x_SYS_RESET, 0x40 },
+ { OV511_REG_BUS, R51x_SYS_INIT, 0xe1 },
+ { OV511_REG_BUS, R51x_SYS_RESET, 0x3e },
+ { OV511_REG_BUS, R51x_SYS_INIT, 0xe1 },
+ { OV511_REG_BUS, R51x_SYS_RESET, 0x00 },
+ { OV511_REG_BUS, R51x_SYS_INIT, 0xe1 },
+ { OV511_REG_BUS, 0x46, 0x00 },
+ { OV511_REG_BUS, 0x5d, 0x03 },
+ { OV511_DONE_BUS, 0x0, 0x00},
+ };
+
+ static struct ov511_regvals aRegvalsNorm518[] = {
+ { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 }, /* Reset */
+ { OV511_REG_BUS, R51x_SYS_SNAP, 0x01 }, /* Enable */
+ { OV511_REG_BUS, 0x31, 0x0f },
+ { OV511_REG_BUS, 0x5d, 0x03 },
+ { OV511_REG_BUS, 0x24, 0x9f },
+ { OV511_REG_BUS, 0x25, 0x90 },
+ { OV511_REG_BUS, 0x20, 0x00 },
+ { OV511_REG_BUS, 0x51, 0x04 },
+ { OV511_REG_BUS, 0x71, 0x19 },
+ { OV511_DONE_BUS, 0x0, 0x00 },
+ };
+
+ static struct ov511_regvals aRegvalsNorm518Plus[] = {
+ { OV511_REG_BUS, R51x_SYS_SNAP, 0x02 }, /* Reset */
+ { OV511_REG_BUS, R51x_SYS_SNAP, 0x01 }, /* Enable */
+ { OV511_REG_BUS, 0x31, 0x0f },
+ { OV511_REG_BUS, 0x5d, 0x03 },
+ { OV511_REG_BUS, 0x24, 0x9f },
+ { OV511_REG_BUS, 0x25, 0x90 },
+ { OV511_REG_BUS, 0x20, 0x60 },
+ { OV511_REG_BUS, 0x51, 0x02 },
+ { OV511_REG_BUS, 0x71, 0x19 },
+ { OV511_REG_BUS, 0x40, 0xff },
+ { OV511_REG_BUS, 0x41, 0x42 },
+ { OV511_REG_BUS, 0x46, 0x00 },
+ { OV511_REG_BUS, 0x33, 0x04 },
+ { OV511_REG_BUS, 0x21, 0x19 },
+ { OV511_REG_BUS, 0x3f, 0x10 },
+ { OV511_DONE_BUS, 0x0, 0x00 },
+ };
+
+ PDEBUG(4, "");
+
+ /* First 5 bits of custom ID reg are a revision ID on OV518 */
+ info("Device revision %d", 0x1F & reg_r(ov, R511_SYS_CUST_ID));
+
+ /* Give it the default description */
+ ov->desc = symbolic(camlist, 0);
+
+ if (write_regvals(ov, aRegvalsInit518))
+ goto error;
+
+ /* Set LED GPIO pin to output mode */
+ if (reg_w_mask(ov, 0x57, 0x00, 0x02) < 0)
+ goto error;
+
+ /* LED is off by default with OV518; have to explicitly turn it on */
+ if (ov->led_policy == LED_OFF || ov->led_policy == LED_AUTO)
+ ov51x_led_control(ov, 0);
+ else
+ ov51x_led_control(ov, 1);
+
+ /* Don't require compression if dumppix is enabled; otherwise it's
+ * required. OV518 has no uncompressed mode, to save RAM. */
+ if (!dumppix && !ov->compress) {
+ ov->compress = 1;
+ warn("Compression required with OV518...enabling");
+ }
+
+ if (ov->bridge == BRG_OV518) {
+ if (write_regvals(ov, aRegvalsNorm518))
+ goto error;
+ } else if (ov->bridge == BRG_OV518PLUS) {
+ if (write_regvals(ov, aRegvalsNorm518Plus))
+ goto error;
+ } else {
+ err("Invalid bridge");
+ }
+
+ if (reg_w(ov, 0x2f, 0x80) < 0)
+ goto error;
+
+ if (ov518_init_compression(ov))
+ goto error;
+
+ if (ov->bridge == BRG_OV518)
+ {
+ struct usb_interface *ifp;
+ struct usb_host_interface *alt;
+ __u16 mxps = 0;
+
+ ifp = usb_ifnum_to_if(ov->dev, 0);
+ if (ifp) {
+ alt = usb_altnum_to_altsetting(ifp, 7);
+ if (alt)
+ mxps = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+ }
+
+ /* Some OV518s have packet numbering by default, some don't */
+ if (mxps == 897)
+ ov->packet_numbering = 1;
+ else
+ ov->packet_numbering = 0;
+ } else {
+ /* OV518+ has packet numbering turned on by default */
+ ov->packet_numbering = 1;
+ }
+
+ ov518_set_packet_size(ov, 0);
+
+ ov->snap_enabled = snapshot;
+
+ /* Test for 76xx */
+ ov->primary_i2c_slave = OV7xx0_SID;
+ if (ov51x_set_slave_ids(ov, OV7xx0_SID) < 0)
+ goto error;
+
+ /* The OV518 must be more aggressive about sensor detection since
+ * I2C write will never fail if the sensor is not present. We have
+ * to try to initialize the sensor to detect its presence */
+
+ if (init_ov_sensor(ov) < 0) {
+ /* Test for 6xx0 */
+ ov->primary_i2c_slave = OV6xx0_SID;
+ if (ov51x_set_slave_ids(ov, OV6xx0_SID) < 0)
+ goto error;
+
+ if (init_ov_sensor(ov) < 0) {
+ /* Test for 8xx0 */
+ ov->primary_i2c_slave = OV8xx0_SID;
+ if (ov51x_set_slave_ids(ov, OV8xx0_SID) < 0)
+ goto error;
+
+ if (init_ov_sensor(ov) < 0) {
+ err("Can't determine sensor slave IDs");
+ goto error;
+ } else {
+ err("Detected unsupported OV8xx0 sensor");
+ goto error;
+ }
+ } else {
+ if (ov6xx0_configure(ov) < 0) {
+ err("Failed to configure OV6xx0");
+ goto error;
+ }
+ }
+ } else {
+ if (ov7xx0_configure(ov) < 0) {
+ err("Failed to configure OV7xx0");
+ goto error;
+ }
+ }
+
+ ov->maxwidth = 352;
+ ov->maxheight = 288;
+
+ // The OV518 cannot go as low as the sensor can
+ ov->minwidth = 160;
+ ov->minheight = 120;
+
+ return 0;
+
+error:
+ err("OV518 Config failed");
+
+ return -EBUSY;
+}
+
+/****************************************************************************
+ * sysfs
+ ***************************************************************************/
+
+static inline struct usb_ov511 *cd_to_ov(struct class_device *cd)
+{
+ struct video_device *vdev = to_video_device(cd);
+ return video_get_drvdata(vdev);
+}
+
+static ssize_t show_custom_id(struct class_device *cd, char *buf)
+{
+ struct usb_ov511 *ov = cd_to_ov(cd);
+ return sprintf(buf, "%d\n", ov->customid);
+}
+static CLASS_DEVICE_ATTR(custom_id, S_IRUGO, show_custom_id, NULL);
+
+static ssize_t show_model(struct class_device *cd, char *buf)
+{
+ struct usb_ov511 *ov = cd_to_ov(cd);
+ return sprintf(buf, "%s\n", ov->desc);
+}
+static CLASS_DEVICE_ATTR(model, S_IRUGO, show_model, NULL);
+
+static ssize_t show_bridge(struct class_device *cd, char *buf)
+{
+ struct usb_ov511 *ov = cd_to_ov(cd);
+ return sprintf(buf, "%s\n", symbolic(brglist, ov->bridge));
+}
+static CLASS_DEVICE_ATTR(bridge, S_IRUGO, show_bridge, NULL);
+
+static ssize_t show_sensor(struct class_device *cd, char *buf)
+{
+ struct usb_ov511 *ov = cd_to_ov(cd);
+ return sprintf(buf, "%s\n", symbolic(senlist, ov->sensor));
+}
+static CLASS_DEVICE_ATTR(sensor, S_IRUGO, show_sensor, NULL);
+
+static ssize_t show_brightness(struct class_device *cd, char *buf)
+{
+ struct usb_ov511 *ov = cd_to_ov(cd);
+ unsigned short x;
+
+ if (!ov->dev)
+ return -ENODEV;
+ sensor_get_brightness(ov, &x);
+ return sprintf(buf, "%d\n", x >> 8);
+}
+static CLASS_DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL);
+
+static ssize_t show_saturation(struct class_device *cd, char *buf)
+{
+ struct usb_ov511 *ov = cd_to_ov(cd);
+ unsigned short x;
+
+ if (!ov->dev)
+ return -ENODEV;
+ sensor_get_saturation(ov, &x);
+ return sprintf(buf, "%d\n", x >> 8);
+}
+static CLASS_DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL);
+
+static ssize_t show_contrast(struct class_device *cd, char *buf)
+{
+ struct usb_ov511 *ov = cd_to_ov(cd);
+ unsigned short x;
+
+ if (!ov->dev)
+ return -ENODEV;
+ sensor_get_contrast(ov, &x);
+ return sprintf(buf, "%d\n", x >> 8);
+}
+static CLASS_DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL);
+
+static ssize_t show_hue(struct class_device *cd, char *buf)
+{
+ struct usb_ov511 *ov = cd_to_ov(cd);
+ unsigned short x;
+
+ if (!ov->dev)
+ return -ENODEV;
+ sensor_get_hue(ov, &x);
+ return sprintf(buf, "%d\n", x >> 8);
+}
+static CLASS_DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL);
+
+static ssize_t show_exposure(struct class_device *cd, char *buf)
+{
+ struct usb_ov511 *ov = cd_to_ov(cd);
+ unsigned char exp = 0;
+
+ if (!ov->dev)
+ return -ENODEV;
+ sensor_get_exposure(ov, &exp);
+ return sprintf(buf, "%d\n", exp >> 8);
+}
+static CLASS_DEVICE_ATTR(exposure, S_IRUGO, show_exposure, NULL);
+
+static void ov_create_sysfs(struct video_device *vdev)
+{
+ video_device_create_file(vdev, &class_device_attr_custom_id);
+ video_device_create_file(vdev, &class_device_attr_model);
+ video_device_create_file(vdev, &class_device_attr_bridge);
+ video_device_create_file(vdev, &class_device_attr_sensor);
+ video_device_create_file(vdev, &class_device_attr_brightness);
+ video_device_create_file(vdev, &class_device_attr_saturation);
+ video_device_create_file(vdev, &class_device_attr_contrast);
+ video_device_create_file(vdev, &class_device_attr_hue);
+ video_device_create_file(vdev, &class_device_attr_exposure);
+}
+
+/****************************************************************************
+ * USB routines
+ ***************************************************************************/
+
+static int
+ov51x_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_interface_descriptor *idesc;
+ struct usb_ov511 *ov;
+ int i;
+
+ PDEBUG(1, "probing for device...");
+
+ /* We don't handle multi-config cameras */
+ if (dev->descriptor.bNumConfigurations != 1)
+ return -ENODEV;
+
+ idesc = &intf->cur_altsetting->desc;
+
+ if (idesc->bInterfaceClass != 0xFF)
+ return -ENODEV;
+ if (idesc->bInterfaceSubClass != 0x00)
+ return -ENODEV;
+
+ if ((ov = kzalloc(sizeof(*ov), GFP_KERNEL)) == NULL) {
+ err("couldn't kmalloc ov struct");
+ goto error_out;
+ }
+
+ ov->dev = dev;
+ ov->iface = idesc->bInterfaceNumber;
+ ov->led_policy = led;
+ ov->compress = compress;
+ ov->lightfreq = lightfreq;
+ ov->num_inputs = 1; /* Video decoder init functs. change this */
+ ov->stop_during_set = !fastset;
+ ov->backlight = backlight;
+ ov->mirror = mirror;
+ ov->auto_brt = autobright;
+ ov->auto_gain = autogain;
+ ov->auto_exp = autoexp;
+
+ switch (le16_to_cpu(dev->descriptor.idProduct)) {
+ case PROD_OV511:
+ ov->bridge = BRG_OV511;
+ ov->bclass = BCL_OV511;
+ break;
+ case PROD_OV511PLUS:
+ ov->bridge = BRG_OV511PLUS;
+ ov->bclass = BCL_OV511;
+ break;
+ case PROD_OV518:
+ ov->bridge = BRG_OV518;
+ ov->bclass = BCL_OV518;
+ break;
+ case PROD_OV518PLUS:
+ ov->bridge = BRG_OV518PLUS;
+ ov->bclass = BCL_OV518;
+ break;
+ case PROD_ME2CAM:
+ if (le16_to_cpu(dev->descriptor.idVendor) != VEND_MATTEL)
+ goto error;
+ ov->bridge = BRG_OV511PLUS;
+ ov->bclass = BCL_OV511;
+ break;
+ default:
+ err("Unknown product ID 0x%04x", le16_to_cpu(dev->descriptor.idProduct));
+ goto error;
+ }
+
+ info("USB %s video device found", symbolic(brglist, ov->bridge));
+
+ init_waitqueue_head(&ov->wq);
+
+ mutex_init(&ov->lock); /* to 1 == available */
+ mutex_init(&ov->buf_lock);
+ mutex_init(&ov->i2c_lock);
+ mutex_init(&ov->cbuf_lock);
+
+ ov->buf_state = BUF_NOT_ALLOCATED;
+
+ if (usb_make_path(dev, ov->usb_path, OV511_USB_PATH_LEN) < 0) {
+ err("usb_make_path error");
+ goto error;
+ }
+
+ /* Allocate control transfer buffer. */
+ /* Must be kmalloc()'ed, for DMA compatibility */
+ ov->cbuf = kmalloc(OV511_CBUF_SIZE, GFP_KERNEL);
+ if (!ov->cbuf)
+ goto error;
+
+ if (ov->bclass == BCL_OV518) {
+ if (ov518_configure(ov) < 0)
+ goto error;
+ } else {
+ if (ov511_configure(ov) < 0)
+ goto error;
+ }
+
+ for (i = 0; i < OV511_NUMFRAMES; i++) {
+ ov->frame[i].framenum = i;
+ init_waitqueue_head(&ov->frame[i].wq);
+ }
+
+ for (i = 0; i < OV511_NUMSBUF; i++) {
+ ov->sbuf[i].ov = ov;
+ spin_lock_init(&ov->sbuf[i].lock);
+ ov->sbuf[i].n = i;
+ }
+
+ /* Unnecessary? (This is done on open(). Need to make sure variables
+ * are properly initialized without this before removing it, though). */
+ if (ov51x_set_default_params(ov) < 0)
+ goto error;
+
+#ifdef OV511_DEBUG
+ if (dump_bridge) {
+ if (ov->bclass == BCL_OV511)
+ ov511_dump_regs(ov);
+ else
+ ov518_dump_regs(ov);
+ }
+#endif
+
+ ov->vdev = video_device_alloc();
+ if (!ov->vdev)
+ goto error;
+
+ memcpy(ov->vdev, &vdev_template, sizeof(*ov->vdev));
+ ov->vdev->dev = &dev->dev;
+ video_set_drvdata(ov->vdev, ov);
+
+ for (i = 0; i < OV511_MAX_UNIT_VIDEO; i++) {
+ /* Minor 0 cannot be specified; assume user wants autodetect */
+ if (unit_video[i] == 0)
+ break;
+
+ if (video_register_device(ov->vdev, VFL_TYPE_GRABBER,
+ unit_video[i]) >= 0) {
+ break;
+ }
+ }
+
+ /* Use the next available one */
+ if ((ov->vdev->minor == -1) &&
+ video_register_device(ov->vdev, VFL_TYPE_GRABBER, -1) < 0) {
+ err("video_register_device failed");
+ goto error;
+ }
+
+ info("Device at %s registered to minor %d", ov->usb_path,
+ ov->vdev->minor);
+
+ usb_set_intfdata(intf, ov);
+ ov_create_sysfs(ov->vdev);
+ return 0;
+
+error:
+ if (ov->vdev) {
+ if (-1 == ov->vdev->minor)
+ video_device_release(ov->vdev);
+ else
+ video_unregister_device(ov->vdev);
+ ov->vdev = NULL;
+ }
+
+ if (ov->cbuf) {
+ mutex_lock(&ov->cbuf_lock);
+ kfree(ov->cbuf);
+ ov->cbuf = NULL;
+ mutex_unlock(&ov->cbuf_lock);
+ }
+
+ kfree(ov);
+ ov = NULL;
+
+error_out:
+ err("Camera initialization failed");
+ return -EIO;
+}
+
+static void
+ov51x_disconnect(struct usb_interface *intf)
+{
+ struct usb_ov511 *ov = usb_get_intfdata(intf);
+ int n;
+
+ PDEBUG(3, "");
+
+ usb_set_intfdata (intf, NULL);
+
+ if (!ov)
+ return;
+
+ if (ov->vdev)
+ video_unregister_device(ov->vdev);
+
+ for (n = 0; n < OV511_NUMFRAMES; n++)
+ ov->frame[n].grabstate = FRAME_ERROR;
+
+ ov->curframe = -1;
+
+ /* This will cause the process to request another frame */
+ for (n = 0; n < OV511_NUMFRAMES; n++)
+ wake_up_interruptible(&ov->frame[n].wq);
+
+ wake_up_interruptible(&ov->wq);
+
+ ov->streaming = 0;
+ ov51x_unlink_isoc(ov);
+
+ ov->dev = NULL;
+
+ /* Free the memory */
+ if (ov && !ov->user) {
+ mutex_lock(&ov->cbuf_lock);
+ kfree(ov->cbuf);
+ ov->cbuf = NULL;
+ mutex_unlock(&ov->cbuf_lock);
+
+ ov51x_dealloc(ov);
+ kfree(ov);
+ ov = NULL;
+ }
+
+ PDEBUG(3, "Disconnect complete");
+}
+
+static struct usb_driver ov511_driver = {
+ .name = "ov511",
+ .id_table = device_table,
+ .probe = ov51x_probe,
+ .disconnect = ov51x_disconnect
+};
+
+/****************************************************************************
+ *
+ * Module routines
+ *
+ ***************************************************************************/
+
+static int __init
+usb_ov511_init(void)
+{
+ int retval;
+
+ retval = usb_register(&ov511_driver);
+ if (retval)
+ goto out;
+
+ info(DRIVER_VERSION " : " DRIVER_DESC);
+
+out:
+ return retval;
+}
+
+static void __exit
+usb_ov511_exit(void)
+{
+ usb_deregister(&ov511_driver);
+ info("driver deregistered");
+
+}
+
+module_init(usb_ov511_init);
+module_exit(usb_ov511_exit);
+
diff --git a/linux/drivers/media/video/ov511.h b/linux/drivers/media/video/ov511.h
new file mode 100644
index 000000000..63da4e8d8
--- /dev/null
+++ b/linux/drivers/media/video/ov511.h
@@ -0,0 +1,569 @@
+#ifndef __LINUX_OV511_H
+#define __LINUX_OV511_H
+
+#include <asm/uaccess.h>
+#include "compat.h"
+#include <linux/videodev.h>
+#include <linux/smp_lock.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+
+#define OV511_DEBUG /* Turn on debug messages */
+
+#ifdef OV511_DEBUG
+ #define PDEBUG(level, fmt, args...) \
+ if (debug >= (level)) info("[%s:%d] " fmt, \
+ __FUNCTION__, __LINE__ , ## args)
+#else
+ #define PDEBUG(level, fmt, args...) do {} while(0)
+#endif
+
+/* This macro restricts an int variable to an inclusive range */
+#define RESTRICT_TO_RANGE(v,mi,ma) { \
+ if ((v) < (mi)) (v) = (mi); \
+ else if ((v) > (ma)) (v) = (ma); \
+}
+
+/* --------------------------------- */
+/* DEFINES FOR OV511 AND OTHER CHIPS */
+/* --------------------------------- */
+
+/* USB IDs */
+#define VEND_OMNIVISION 0x05A9
+#define PROD_OV511 0x0511
+#define PROD_OV511PLUS 0xA511
+#define PROD_OV518 0x0518
+#define PROD_OV518PLUS 0xA518
+
+#define VEND_MATTEL 0x0813
+#define PROD_ME2CAM 0x0002
+
+/* --------------------------------- */
+/* OV51x REGISTER MNEMONICS */
+/* --------------------------------- */
+
+/* Camera interface register numbers */
+#define R511_CAM_DELAY 0x10
+#define R511_CAM_EDGE 0x11
+#define R511_CAM_PXCNT 0x12
+#define R511_CAM_LNCNT 0x13
+#define R511_CAM_PXDIV 0x14
+#define R511_CAM_LNDIV 0x15
+#define R511_CAM_UV_EN 0x16
+#define R511_CAM_LINE_MODE 0x17
+#define R511_CAM_OPTS 0x18
+
+/* Snapshot mode camera interface register numbers */
+#define R511_SNAP_FRAME 0x19
+#define R511_SNAP_PXCNT 0x1A
+#define R511_SNAP_LNCNT 0x1B
+#define R511_SNAP_PXDIV 0x1C
+#define R511_SNAP_LNDIV 0x1D
+#define R511_SNAP_UV_EN 0x1E
+#define R511_SNAP_OPTS 0x1F
+
+/* DRAM register numbers */
+#define R511_DRAM_FLOW_CTL 0x20
+#define R511_DRAM_ARCP 0x21
+#define R511_DRAM_MRC 0x22
+#define R511_DRAM_RFC 0x23
+
+/* ISO FIFO register numbers */
+#define R51x_FIFO_PSIZE 0x30 /* 2 bytes wide w/ OV518(+) */
+#define R511_FIFO_OPTS 0x31
+
+/* Parallel IO register numbers */
+#define R511_PIO_OPTS 0x38
+#define R511_PIO_DATA 0x39
+#define R511_PIO_BIST 0x3E
+#define R518_GPIO_IN 0x55 /* OV518(+) only */
+#define R518_GPIO_OUT 0x56 /* OV518(+) only */
+#define R518_GPIO_CTL 0x57 /* OV518(+) only */
+#define R518_GPIO_PULSE_IN 0x58 /* OV518(+) only */
+#define R518_GPIO_PULSE_CLEAR 0x59 /* OV518(+) only */
+#define R518_GPIO_PULSE_POL 0x5a /* OV518(+) only */
+#define R518_GPIO_PULSE_EN 0x5b /* OV518(+) only */
+#define R518_GPIO_RESET 0x5c /* OV518(+) only */
+
+/* I2C registers */
+#define R511_I2C_CTL 0x40
+#define R518_I2C_CTL 0x47 /* OV518(+) only */
+#define R51x_I2C_W_SID 0x41
+#define R51x_I2C_SADDR_3 0x42
+#define R51x_I2C_SADDR_2 0x43
+#define R51x_I2C_R_SID 0x44
+#define R51x_I2C_DATA 0x45
+#define R51x_I2C_CLOCK 0x46
+#define R51x_I2C_TIMEOUT 0x47
+
+/* I2C snapshot registers */
+#define R511_SI2C_SADDR_3 0x48
+#define R511_SI2C_DATA 0x49
+
+/* System control registers */
+#define R51x_SYS_RESET 0x50
+ /* Reset type definitions */
+#define OV511_RESET_UDC 0x01
+#define OV511_RESET_I2C 0x02
+#define OV511_RESET_FIFO 0x04
+#define OV511_RESET_OMNICE 0x08
+#define OV511_RESET_DRAM 0x10
+#define OV511_RESET_CAM_INT 0x20
+#define OV511_RESET_OV511 0x40
+#define OV511_RESET_NOREGS 0x3F /* All but OV511 & regs */
+#define OV511_RESET_ALL 0x7F
+
+#define R511_SYS_CLOCK_DIV 0x51
+#define R51x_SYS_SNAP 0x52
+#define R51x_SYS_INIT 0x53
+#define R511_SYS_PWR_CLK 0x54 /* OV511+/OV518(+) only */
+#define R511_SYS_LED_CTL 0x55 /* OV511+ only */
+#define R511_SYS_USER 0x5E
+#define R511_SYS_CUST_ID 0x5F
+
+/* OmniCE (compression) registers */
+#define R511_COMP_PHY 0x70
+#define R511_COMP_PHUV 0x71
+#define R511_COMP_PVY 0x72
+#define R511_COMP_PVUV 0x73
+#define R511_COMP_QHY 0x74
+#define R511_COMP_QHUV 0x75
+#define R511_COMP_QVY 0x76
+#define R511_COMP_QVUV 0x77
+#define R511_COMP_EN 0x78
+#define R511_COMP_LUT_EN 0x79
+#define R511_COMP_LUT_BEGIN 0x80
+
+/* --------------------------------- */
+/* ALTERNATE NUMBERS */
+/* --------------------------------- */
+
+/* Alternate numbers for various max packet sizes (OV511 only) */
+#define OV511_ALT_SIZE_992 0
+#define OV511_ALT_SIZE_993 1
+#define OV511_ALT_SIZE_768 2
+#define OV511_ALT_SIZE_769 3
+#define OV511_ALT_SIZE_512 4
+#define OV511_ALT_SIZE_513 5
+#define OV511_ALT_SIZE_257 6
+#define OV511_ALT_SIZE_0 7
+
+/* Alternate numbers for various max packet sizes (OV511+ only) */
+#define OV511PLUS_ALT_SIZE_0 0
+#define OV511PLUS_ALT_SIZE_33 1
+#define OV511PLUS_ALT_SIZE_129 2
+#define OV511PLUS_ALT_SIZE_257 3
+#define OV511PLUS_ALT_SIZE_385 4
+#define OV511PLUS_ALT_SIZE_513 5
+#define OV511PLUS_ALT_SIZE_769 6
+#define OV511PLUS_ALT_SIZE_961 7
+
+/* Alternate numbers for various max packet sizes (OV518(+) only) */
+#define OV518_ALT_SIZE_0 0
+#define OV518_ALT_SIZE_128 1
+#define OV518_ALT_SIZE_256 2
+#define OV518_ALT_SIZE_384 3
+#define OV518_ALT_SIZE_512 4
+#define OV518_ALT_SIZE_640 5
+#define OV518_ALT_SIZE_768 6
+#define OV518_ALT_SIZE_896 7
+
+/* --------------------------------- */
+/* OV7610 REGISTER MNEMONICS */
+/* --------------------------------- */
+
+/* OV7610 registers */
+#define OV7610_REG_GAIN 0x00 /* gain setting (5:0) */
+#define OV7610_REG_BLUE 0x01 /* blue channel balance */
+#define OV7610_REG_RED 0x02 /* red channel balance */
+#define OV7610_REG_SAT 0x03 /* saturation */
+ /* 04 reserved */
+#define OV7610_REG_CNT 0x05 /* Y contrast */
+#define OV7610_REG_BRT 0x06 /* Y brightness */
+ /* 08-0b reserved */
+#define OV7610_REG_BLUE_BIAS 0x0C /* blue channel bias (5:0) */
+#define OV7610_REG_RED_BIAS 0x0D /* read channel bias (5:0) */
+#define OV7610_REG_GAMMA_COEFF 0x0E /* gamma settings */
+#define OV7610_REG_WB_RANGE 0x0F /* AEC/ALC/S-AWB settings */
+#define OV7610_REG_EXP 0x10 /* manual exposure setting */
+#define OV7610_REG_CLOCK 0x11 /* polarity/clock prescaler */
+#define OV7610_REG_COM_A 0x12 /* misc common regs */
+#define OV7610_REG_COM_B 0x13 /* misc common regs */
+#define OV7610_REG_COM_C 0x14 /* misc common regs */
+#define OV7610_REG_COM_D 0x15 /* misc common regs */
+#define OV7610_REG_FIELD_DIVIDE 0x16 /* field interval/mode settings */
+#define OV7610_REG_HWIN_START 0x17 /* horizontal window start */
+#define OV7610_REG_HWIN_END 0x18 /* horizontal window end */
+#define OV7610_REG_VWIN_START 0x19 /* vertical window start */
+#define OV7610_REG_VWIN_END 0x1A /* vertical window end */
+#define OV7610_REG_PIXEL_SHIFT 0x1B /* pixel shift */
+#define OV7610_REG_ID_HIGH 0x1C /* manufacturer ID MSB */
+#define OV7610_REG_ID_LOW 0x1D /* manufacturer ID LSB */
+ /* 0e-0f reserved */
+#define OV7610_REG_COM_E 0x20 /* misc common regs */
+#define OV7610_REG_YOFFSET 0x21 /* Y channel offset */
+#define OV7610_REG_UOFFSET 0x22 /* U channel offset */
+ /* 23 reserved */
+#define OV7610_REG_ECW 0x24 /* Exposure white level for AEC */
+#define OV7610_REG_ECB 0x25 /* Exposure black level for AEC */
+#define OV7610_REG_COM_F 0x26 /* misc settings */
+#define OV7610_REG_COM_G 0x27 /* misc settings */
+#define OV7610_REG_COM_H 0x28 /* misc settings */
+#define OV7610_REG_COM_I 0x29 /* misc settings */
+#define OV7610_REG_FRAMERATE_H 0x2A /* frame rate MSB + misc */
+#define OV7610_REG_FRAMERATE_L 0x2B /* frame rate LSB */
+#define OV7610_REG_ALC 0x2C /* Auto Level Control settings */
+#define OV7610_REG_COM_J 0x2D /* misc settings */
+#define OV7610_REG_VOFFSET 0x2E /* V channel offset adjustment */
+#define OV7610_REG_ARRAY_BIAS 0x2F /* Array bias -- don't change */
+ /* 30-32 reserved */
+#define OV7610_REG_YGAMMA 0x33 /* misc gamma settings (7:6) */
+#define OV7610_REG_BIAS_ADJUST 0x34 /* misc bias settings */
+#define OV7610_REG_COM_L 0x35 /* misc settings */
+ /* 36-37 reserved */
+#define OV7610_REG_COM_K 0x38 /* misc registers */
+
+/* --------------------------------- */
+/* I2C ADDRESSES */
+/* --------------------------------- */
+
+#define OV7xx0_SID 0x42
+#define OV6xx0_SID 0xC0
+#define OV8xx0_SID 0xA0
+#define KS0127_SID 0xD8
+#define SAA7111A_SID 0x48
+
+/* --------------------------------- */
+/* MISCELLANEOUS DEFINES */
+/* --------------------------------- */
+
+#define I2C_CLOCK_PRESCALER 0x03
+
+#define FRAMES_PER_DESC 10 /* FIXME - What should this be? */
+#define MAX_FRAME_SIZE_PER_DESC 993 /* For statically allocated stuff */
+#define PIXELS_PER_SEG 256 /* Pixels per segment */
+
+#define OV511_ENDPOINT_ADDRESS 1 /* Isoc endpoint number */
+
+#define OV511_NUMFRAMES 2
+#if OV511_NUMFRAMES > VIDEO_MAX_FRAME
+ #error "OV511_NUMFRAMES is too high"
+#endif
+
+#define OV511_NUMSBUF 2
+
+/* Control transfers use up to 4 bytes */
+#define OV511_CBUF_SIZE 4
+
+/* Size of usb_make_path() buffer */
+#define OV511_USB_PATH_LEN 64
+
+/* Bridge types */
+enum {
+ BRG_UNKNOWN,
+ BRG_OV511,
+ BRG_OV511PLUS,
+ BRG_OV518,
+ BRG_OV518PLUS,
+};
+
+/* Bridge classes */
+enum {
+ BCL_UNKNOWN,
+ BCL_OV511,
+ BCL_OV518,
+};
+
+/* Sensor types */
+enum {
+ SEN_UNKNOWN,
+ SEN_OV76BE,
+ SEN_OV7610,
+ SEN_OV7620,
+ SEN_OV7620AE,
+ SEN_OV6620,
+ SEN_OV6630,
+ SEN_OV6630AE,
+ SEN_OV6630AF,
+ SEN_OV8600,
+ SEN_KS0127,
+ SEN_KS0127B,
+ SEN_SAA7111A,
+};
+
+enum {
+ STATE_SCANNING, /* Scanning for start */
+ STATE_HEADER, /* Parsing header */
+ STATE_LINES, /* Parsing lines */
+};
+
+/* Buffer states */
+enum {
+ BUF_NOT_ALLOCATED,
+ BUF_ALLOCATED,
+};
+
+/* --------- Definition of ioctl interface --------- */
+
+#define OV511_INTERFACE_VER 101
+
+/* LED options */
+enum {
+ LED_OFF,
+ LED_ON,
+ LED_AUTO,
+};
+
+/* Raw frame formats */
+enum {
+ RAWFMT_INVALID,
+ RAWFMT_YUV400,
+ RAWFMT_YUV420,
+ RAWFMT_YUV422,
+ RAWFMT_GBR422,
+};
+
+struct ov511_i2c_struct {
+ unsigned char slave; /* Write slave ID (read ID - 1) */
+ unsigned char reg; /* Index of register */
+ unsigned char value; /* User sets this w/ write, driver does w/ read */
+ unsigned char mask; /* Bits to be changed. Not used with read ops */
+};
+
+/* ioctls */
+#define OV511IOC_WI2C _IOW('v', BASE_VIDIOCPRIVATE + 5, \
+ struct ov511_i2c_struct)
+#define OV511IOC_RI2C _IOWR('v', BASE_VIDIOCPRIVATE + 6, \
+ struct ov511_i2c_struct)
+/* ------------- End IOCTL interface -------------- */
+
+struct usb_ov511; /* Forward declaration */
+
+struct ov511_sbuf {
+ struct usb_ov511 *ov;
+ unsigned char *data;
+ struct urb *urb;
+ spinlock_t lock;
+ int n;
+};
+
+enum {
+ FRAME_UNUSED, /* Unused (no MCAPTURE) */
+ FRAME_READY, /* Ready to start grabbing */
+ FRAME_GRABBING, /* In the process of being grabbed into */
+ FRAME_DONE, /* Finished grabbing, but not been synced yet */
+ FRAME_ERROR, /* Something bad happened while processing */
+};
+
+struct ov511_regvals {
+ enum {
+ OV511_DONE_BUS,
+ OV511_REG_BUS,
+ OV511_I2C_BUS,
+ } bus;
+ unsigned char reg;
+ unsigned char val;
+};
+
+struct ov511_frame {
+ int framenum; /* Index of this frame */
+ unsigned char *data; /* Frame buffer */
+ unsigned char *tempdata; /* Temp buffer for multi-stage conversions */
+ unsigned char *rawdata; /* Raw camera data buffer */
+ unsigned char *compbuf; /* Temp buffer for decompressor */
+
+ int depth; /* Bytes per pixel */
+ int width; /* Width application is expecting */
+ int height; /* Height application is expecting */
+
+ int rawwidth; /* Actual width of frame sent from camera */
+ int rawheight; /* Actual height of frame sent from camera */
+
+ int sub_flag; /* Sub-capture mode for this frame? */
+ unsigned int format; /* Format for this frame */
+ int compressed; /* Is frame compressed? */
+
+ volatile int grabstate; /* State of grabbing */
+ int scanstate; /* State of scanning */
+
+ int bytes_recvd; /* Number of image bytes received from camera */
+
+ long bytes_read; /* Amount that has been read() */
+
+ wait_queue_head_t wq; /* Processes waiting */
+
+ int snapshot; /* True if frame was a snapshot */
+};
+
+#define DECOMP_INTERFACE_VER 4
+
+/* Compression module operations */
+struct ov51x_decomp_ops {
+ int (*decomp_400)(unsigned char *, unsigned char *, unsigned char *,
+ int, int, int);
+ int (*decomp_420)(unsigned char *, unsigned char *, unsigned char *,
+ int, int, int);
+ int (*decomp_422)(unsigned char *, unsigned char *, unsigned char *,
+ int, int, int);
+ struct module *owner;
+};
+
+struct usb_ov511 {
+ struct video_device *vdev;
+ struct usb_device *dev;
+
+ int customid;
+ char *desc;
+ unsigned char iface;
+ char usb_path[OV511_USB_PATH_LEN];
+
+ /* Determined by sensor type */
+ int maxwidth;
+ int maxheight;
+ int minwidth;
+ int minheight;
+
+ int brightness;
+ int colour;
+ int contrast;
+ int hue;
+ int whiteness;
+ int exposure;
+ int auto_brt; /* Auto brightness enabled flag */
+ int auto_gain; /* Auto gain control enabled flag */
+ int auto_exp; /* Auto exposure enabled flag */
+ int backlight; /* Backlight exposure algorithm flag */
+ int mirror; /* Image is reversed horizontally */
+
+ int led_policy; /* LED: off|on|auto; OV511+ only */
+
+ struct mutex lock; /* Serializes user-accessible operations */
+ int user; /* user count for exclusive use */
+
+ int streaming; /* Are we streaming Isochronous? */
+ int grabbing; /* Are we grabbing? */
+
+ int compress; /* Should the next frame be compressed? */
+ int compress_inited; /* Are compression params uploaded? */
+
+ int lightfreq; /* Power (lighting) frequency */
+ int bandfilt; /* Banding filter enabled flag */
+
+ unsigned char *fbuf; /* Videodev buffer area */
+ unsigned char *tempfbuf; /* Temporary (intermediate) buffer area */
+ unsigned char *rawfbuf; /* Raw camera data buffer area */
+
+ int sub_flag; /* Pix Array subcapture on flag */
+ int subx; /* Pix Array subcapture x offset */
+ int suby; /* Pix Array subcapture y offset */
+ int subw; /* Pix Array subcapture width */
+ int subh; /* Pix Array subcapture height */
+
+ int curframe; /* Current receiving sbuf */
+ struct ov511_frame frame[OV511_NUMFRAMES];
+
+ struct ov511_sbuf sbuf[OV511_NUMSBUF];
+
+ wait_queue_head_t wq; /* Processes waiting */
+
+ int snap_enabled; /* Snapshot mode enabled */
+
+ int bridge; /* Type of bridge (BRG_*) */
+ int bclass; /* Class of bridge (BCL_*) */
+ int sensor; /* Type of image sensor chip (SEN_*) */
+
+ int packet_size; /* Frame size per isoc desc */
+ int packet_numbering; /* Is ISO frame numbering enabled? */
+
+ /* Framebuffer/sbuf management */
+ int buf_state;
+ struct mutex buf_lock;
+
+ struct ov51x_decomp_ops *decomp_ops;
+
+ /* Stop streaming while changing picture settings */
+ int stop_during_set;
+
+ int stopped; /* Streaming is temporarily paused */
+
+ /* Video decoder stuff */
+ int input; /* Composite, S-VIDEO, etc... */
+ int num_inputs; /* Number of inputs */
+ int norm; /* NTSC / PAL / SECAM */
+ int has_decoder; /* Device has a video decoder */
+ int pal; /* Device is designed for PAL resolution */
+
+ /* I2C interface */
+ struct mutex i2c_lock; /* Protect I2C controller regs */
+ unsigned char primary_i2c_slave; /* I2C write id of sensor */
+
+ /* Control transaction stuff */
+ unsigned char *cbuf; /* Buffer for payload */
+ struct mutex cbuf_lock;
+};
+
+/* Used to represent a list of values and their respective symbolic names */
+struct symbolic_list {
+ int num;
+ char *name;
+};
+
+#define NOT_DEFINED_STR "Unknown"
+
+/* Returns the name of the matching element in the symbolic_list array. The
+ * end of the list must be marked with an element that has a NULL name.
+ */
+static inline char *
+symbolic(struct symbolic_list list[], int num)
+{
+ int i;
+
+ for (i = 0; list[i].name != NULL; i++)
+ if (list[i].num == num)
+ return (list[i].name);
+
+ return (NOT_DEFINED_STR);
+}
+
+/* Compression stuff */
+
+#define OV511_QUANTABLESIZE 64
+#define OV518_QUANTABLESIZE 32
+
+#define OV511_YQUANTABLE { \
+ 0, 1, 1, 2, 2, 3, 3, 4, \
+ 1, 1, 1, 2, 2, 3, 4, 4, \
+ 1, 1, 2, 2, 3, 4, 4, 4, \
+ 2, 2, 2, 3, 4, 4, 4, 4, \
+ 2, 2, 3, 4, 4, 5, 5, 5, \
+ 3, 3, 4, 4, 5, 5, 5, 5, \
+ 3, 4, 4, 4, 5, 5, 5, 5, \
+ 4, 4, 4, 4, 5, 5, 5, 5 \
+}
+
+#define OV511_UVQUANTABLE { \
+ 0, 2, 2, 3, 4, 4, 4, 4, \
+ 2, 2, 2, 4, 4, 4, 4, 4, \
+ 2, 2, 3, 4, 4, 4, 4, 4, \
+ 3, 4, 4, 4, 4, 4, 4, 4, \
+ 4, 4, 4, 4, 4, 4, 4, 4, \
+ 4, 4, 4, 4, 4, 4, 4, 4, \
+ 4, 4, 4, 4, 4, 4, 4, 4, \
+ 4, 4, 4, 4, 4, 4, 4, 4 \
+}
+
+#define OV518_YQUANTABLE { \
+ 5, 4, 5, 6, 6, 7, 7, 7, \
+ 5, 5, 5, 5, 6, 7, 7, 7, \
+ 6, 6, 6, 6, 7, 7, 7, 8, \
+ 7, 7, 6, 7, 7, 7, 8, 8 \
+}
+
+#define OV518_UVQUANTABLE { \
+ 6, 6, 6, 7, 7, 7, 7, 7, \
+ 6, 6, 6, 7, 7, 7, 7, 7, \
+ 6, 6, 6, 7, 7, 7, 7, 8, \
+ 7, 7, 7, 7, 7, 7, 8, 8 \
+}
+
+#endif
diff --git a/linux/drivers/media/video/planb.c b/linux/drivers/media/video/planb.c
new file mode 100644
index 000000000..c3026d701
--- /dev/null
+++ b/linux/drivers/media/video/planb.c
@@ -0,0 +1,2304 @@
+/*
+ planb - PlanB frame grabber driver
+
+ PlanB is used in the 7x00/8x00 series of PowerMacintosh
+ Computers as video input DMA controller.
+
+ Copyright (C) 1998 Michel Lanners (mlan@cpu.lu)
+
+ Based largely on the bttv driver by Ralph Metzler (rjkm@thp.uni-koeln.de)
+
+ Additional debugging and coding by Takashi Oe (toe@unlserve.unl.edu)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* $Id: planb.c,v 1.18 1999/05/02 17:36:34 mlan Exp $ */
+
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include "compat.h"
+#include <linux/videodev.h>
+#include <linux/wait.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/dbdma.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/irq.h>
+#include <linux/mutex.h>
+
+#include "planb.h"
+#include "saa7196.h"
+
+/* Would you mind for some ugly debugging? */
+#if 0
+#define DEBUG(x...) printk(KERN_DEBUG ## x) /* Debug driver */
+#else
+#define DEBUG(x...) /* Don't debug driver */
+#endif
+
+#if 0
+#define IDEBUG(x...) printk(KERN_DEBUG ## x) /* Debug interrupt part */
+#else
+#define IDEBUG(x...) /* Don't debug interrupt part */
+#endif
+
+/* Ever seen a Mac with more than 1 of these? */
+#define PLANB_MAX 1
+
+static int planb_num;
+static struct planb planbs[PLANB_MAX];
+static volatile struct planb_registers *planb_regs;
+
+static int def_norm = PLANB_DEF_NORM; /* default norm */
+static int video_nr = -1;
+
+module_param(def_norm, int, 0);
+MODULE_PARM_DESC(def_norm, "Default startup norm (0=PAL, 1=NTSC, 2=SECAM)");
+module_param(video_nr, int, 0);
+MODULE_LICENSE("GPL");
+
+
+/* ------------------ PlanB Exported Functions ------------------ */
+static long planb_write(struct video_device *, const char *, unsigned long, int);
+static long planb_read(struct video_device *, char *, unsigned long, int);
+static int planb_open(struct video_device *, int);
+static void planb_close(struct video_device *);
+static int planb_ioctl(struct video_device *, unsigned int, void *);
+static int planb_init_done(struct video_device *);
+static int planb_mmap(struct video_device *, const char *, unsigned long);
+static void planb_irq(int, void *, struct pt_regs *);
+static void release_planb(void);
+int init_planbs(struct video_init *);
+
+/* ------------------ PlanB Internal Functions ------------------ */
+static int planb_prepare_open(struct planb *);
+static void planb_prepare_close(struct planb *);
+static void saa_write_reg(unsigned char, unsigned char);
+static unsigned char saa_status(int, struct planb *);
+static void saa_set(unsigned char, unsigned char, struct planb *);
+static void saa_init_regs(struct planb *);
+static int grabbuf_alloc(struct planb *);
+static int vgrab(struct planb *, struct video_mmap *);
+static void add_clip(struct planb *, struct video_clip *);
+static void fill_cmd_buff(struct planb *);
+static void cmd_buff(struct planb *);
+static volatile struct dbdma_cmd *setup_grab_cmd(int, struct planb *);
+static void overlay_start(struct planb *);
+static void overlay_stop(struct planb *);
+static inline void tab_cmd_dbdma(volatile struct dbdma_cmd *, unsigned short,
+ unsigned int);
+static inline void tab_cmd_store(volatile struct dbdma_cmd *, unsigned int,
+ unsigned int);
+static inline void tab_cmd_gen(volatile struct dbdma_cmd *, unsigned short,
+ unsigned short, unsigned int, unsigned int);
+static int init_planb(struct planb *);
+static int find_planb(void);
+static void planb_pre_capture(int, int, struct planb *);
+static volatile struct dbdma_cmd *cmd_geo_setup(volatile struct dbdma_cmd *,
+ int, int, int, int, int, struct planb *);
+static inline void planb_dbdma_stop(volatile struct dbdma_regs *);
+static unsigned int saa_geo_setup(int, int, int, int, struct planb *);
+static inline int overlay_is_active(struct planb *);
+
+/*******************************/
+/* Memory management functions */
+/*******************************/
+
+static int grabbuf_alloc(struct planb *pb)
+{
+ int i, npage;
+
+ npage = MAX_GBUFFERS * ((PLANB_MAX_FBUF / PAGE_SIZE + 1)
+#ifndef PLANB_GSCANLINE
+ + MAX_LNUM
+#endif /* PLANB_GSCANLINE */
+ );
+ if ((pb->rawbuf = (unsigned char**) kmalloc (npage
+ * sizeof(unsigned long), GFP_KERNEL)) == 0)
+ return -ENOMEM;
+ for (i = 0; i < npage; i++) {
+ pb->rawbuf[i] = (unsigned char *)__get_free_pages(GFP_KERNEL
+ |GFP_DMA, 0);
+ if (!pb->rawbuf[i])
+ break;
+ SetPageReserved(virt_to_page(pb->rawbuf[i]));
+ }
+ if (i-- < npage) {
+ printk(KERN_DEBUG "PlanB: init_grab: grab buffer not allocated\n");
+ for (; i > 0; i--) {
+ ClearPageReserved(virt_to_page(pb->rawbuf[i]));
+ free_pages((unsigned long)pb->rawbuf[i], 0);
+ }
+ kfree(pb->rawbuf);
+ return -ENOBUFS;
+ }
+ pb->rawbuf_size = npage;
+ return 0;
+}
+
+/*****************************/
+/* Hardware access functions */
+/*****************************/
+
+static void saa_write_reg(unsigned char addr, unsigned char val)
+{
+ planb_regs->saa_addr = addr; eieio();
+ planb_regs->saa_regval = val; eieio();
+ return;
+}
+
+/* return status byte 0 or 1: */
+static unsigned char saa_status(int byte, struct planb *pb)
+{
+ saa_regs[pb->win.norm][SAA7196_STDC] =
+ (saa_regs[pb->win.norm][SAA7196_STDC] & ~2) | ((byte & 1) << 1);
+ saa_write_reg (SAA7196_STDC, saa_regs[pb->win.norm][SAA7196_STDC]);
+
+ /* Let's wait 30msec for this one */
+ msleep_interruptible(30);
+
+ return (unsigned char)in_8 (&planb_regs->saa_status);
+}
+
+static void saa_set(unsigned char addr, unsigned char val, struct planb *pb)
+{
+ if(saa_regs[pb->win.norm][addr] != val) {
+ saa_regs[pb->win.norm][addr] = val;
+ saa_write_reg (addr, val);
+ }
+ return;
+}
+
+static void saa_init_regs(struct planb *pb)
+{
+ int i;
+
+ for (i = 0; i < SAA7196_NUMREGS; i++)
+ saa_write_reg (i, saa_regs[pb->win.norm][i]);
+}
+
+static unsigned int saa_geo_setup(int width, int height, int interlace, int bpp,
+ struct planb *pb)
+{
+ int ht, norm = pb->win.norm;
+
+ switch(bpp) {
+ case 2:
+ /* RGB555+a 1x16-bit + 16-bit transparent */
+ saa_regs[norm][SAA7196_FMTS] &= ~0x3;
+ break;
+ case 1:
+ case 4:
+ /* RGB888 1x24-bit + 8-bit transparent */
+ saa_regs[norm][SAA7196_FMTS] &= ~0x1;
+ saa_regs[norm][SAA7196_FMTS] |= 0x2;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ht = (interlace ? height / 2 : height);
+ saa_regs[norm][SAA7196_OUTPIX] = (unsigned char) (width & 0x00ff);
+ saa_regs[norm][SAA7196_HFILT] = (saa_regs[norm][SAA7196_HFILT] & ~0x3)
+ | (width >> 8 & 0x3);
+ saa_regs[norm][SAA7196_OUTLINE] = (unsigned char) (ht & 0xff);
+ saa_regs[norm][SAA7196_VYP] = (saa_regs[norm][SAA7196_VYP] & ~0x3)
+ | (ht >> 8 & 0x3);
+ /* feed both fields if interlaced, or else feed only even fields */
+ saa_regs[norm][SAA7196_FMTS] = (interlace) ?
+ (saa_regs[norm][SAA7196_FMTS] & ~0x60)
+ : (saa_regs[norm][SAA7196_FMTS] | 0x60);
+ /* transparent mode; extended format enabled */
+ saa_regs[norm][SAA7196_DPATH] |= 0x3;
+
+ return 0;
+}
+
+/***************************/
+/* DBDMA support functions */
+/***************************/
+
+static inline void planb_dbdma_restart(volatile struct dbdma_regs *ch)
+{
+ out_le32(&ch->control, PLANB_CLR(RUN));
+ out_le32(&ch->control, PLANB_SET(RUN|WAKE) | PLANB_CLR(PAUSE));
+}
+
+static inline void planb_dbdma_stop(volatile struct dbdma_regs *ch)
+{
+ int i = 0;
+
+ out_le32(&ch->control, PLANB_CLR(RUN) | PLANB_SET(FLUSH));
+ while((in_le32(&ch->status) == (ACTIVE | FLUSH)) && (i < 999)) {
+ IDEBUG("PlanB: waiting for DMA to stop\n");
+ i++;
+ }
+}
+
+static inline void tab_cmd_dbdma(volatile struct dbdma_cmd *ch,
+ unsigned short command, unsigned int cmd_dep)
+{
+ st_le16(&ch->command, command);
+ st_le32(&ch->cmd_dep, cmd_dep);
+}
+
+static inline void tab_cmd_store(volatile struct dbdma_cmd *ch,
+ unsigned int phy_addr, unsigned int cmd_dep)
+{
+ st_le16(&ch->command, STORE_WORD | KEY_SYSTEM);
+ st_le16(&ch->req_count, 4);
+ st_le32(&ch->phy_addr, phy_addr);
+ st_le32(&ch->cmd_dep, cmd_dep);
+}
+
+static inline void tab_cmd_gen(volatile struct dbdma_cmd *ch,
+ unsigned short command, unsigned short req_count,
+ unsigned int phy_addr, unsigned int cmd_dep)
+{
+ st_le16(&ch->command, command);
+ st_le16(&ch->req_count, req_count);
+ st_le32(&ch->phy_addr, phy_addr);
+ st_le32(&ch->cmd_dep, cmd_dep);
+}
+
+static volatile struct dbdma_cmd *cmd_geo_setup(
+ volatile struct dbdma_cmd *c1, int width, int height, int interlace,
+ int bpp, int clip, struct planb *pb)
+{
+ int norm = pb->win.norm;
+
+ if((saa_geo_setup(width, height, interlace, bpp, pb)) != 0)
+ return (volatile struct dbdma_cmd *)NULL;
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
+ SAA7196_FMTS);
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
+ saa_regs[norm][SAA7196_FMTS]);
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
+ SAA7196_DPATH);
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
+ saa_regs[norm][SAA7196_DPATH]);
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->even),
+ bpp | ((clip)? PLANB_CLIPMASK: 0));
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->odd),
+ bpp | ((clip)? PLANB_CLIPMASK: 0));
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
+ SAA7196_OUTPIX);
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
+ saa_regs[norm][SAA7196_OUTPIX]);
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
+ SAA7196_HFILT);
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
+ saa_regs[norm][SAA7196_HFILT]);
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
+ SAA7196_OUTLINE);
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
+ saa_regs[norm][SAA7196_OUTLINE]);
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
+ SAA7196_VYP);
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
+ saa_regs[norm][SAA7196_VYP]);
+ return c1;
+}
+
+/******************************/
+/* misc. supporting functions */
+/******************************/
+
+static inline void planb_lock(struct planb *pb)
+{
+ mutex_lock(&pb->lock);
+}
+
+static inline void planb_unlock(struct planb *pb)
+{
+ mutex_unlock(&pb->lock);
+}
+
+/***************/
+/* Driver Core */
+/***************/
+
+static int planb_prepare_open(struct planb *pb)
+{
+ int i, size;
+
+ /* allocate memory for two plus alpha command buffers (size: max lines,
+ plus 40 commands handling, plus 1 alignment), plus dummy command buf,
+ plus clipmask buffer, plus frame grabbing status */
+ size = (pb->tab_size*(2+MAX_GBUFFERS*TAB_FACTOR)+1+MAX_GBUFFERS
+ * PLANB_DUMMY)*sizeof(struct dbdma_cmd)
+ +(PLANB_MAXLINES*((PLANB_MAXPIXELS+7)& ~7))/8
+ +MAX_GBUFFERS*sizeof(unsigned int);
+ if ((pb->priv_space = kmalloc (size, GFP_KERNEL)) == 0)
+ return -ENOMEM;
+ memset ((void *) pb->priv_space, 0, size);
+ pb->overlay_last1 = pb->ch1_cmd = (volatile struct dbdma_cmd *)
+ DBDMA_ALIGN (pb->priv_space);
+ pb->overlay_last2 = pb->ch2_cmd = pb->ch1_cmd + pb->tab_size;
+ pb->ch1_cmd_phys = virt_to_bus(pb->ch1_cmd);
+ pb->cap_cmd[0] = pb->ch2_cmd + pb->tab_size;
+ pb->pre_cmd[0] = pb->cap_cmd[0] + pb->tab_size * TAB_FACTOR;
+ for (i = 1; i < MAX_GBUFFERS; i++) {
+ pb->cap_cmd[i] = pb->pre_cmd[i-1] + PLANB_DUMMY;
+ pb->pre_cmd[i] = pb->cap_cmd[i] + pb->tab_size * TAB_FACTOR;
+ }
+ pb->frame_stat=(volatile unsigned int *)(pb->pre_cmd[MAX_GBUFFERS-1]
+ + PLANB_DUMMY);
+ pb->mask = (unsigned char *)(pb->frame_stat+MAX_GBUFFERS);
+
+ pb->rawbuf = NULL;
+ pb->rawbuf_size = 0;
+ pb->grabbing = 0;
+ for (i = 0; i < MAX_GBUFFERS; i++) {
+ pb->frame_stat[i] = GBUFFER_UNUSED;
+ pb->gwidth[i] = 0;
+ pb->gheight[i] = 0;
+ pb->gfmt[i] = 0;
+ pb->gnorm_switch[i] = 0;
+#ifndef PLANB_GSCANLINE
+ pb->lsize[i] = 0;
+ pb->lnum[i] = 0;
+#endif /* PLANB_GSCANLINE */
+ }
+ pb->gcount = 0;
+ pb->suspend = 0;
+ pb->last_fr = -999;
+ pb->prev_last_fr = -999;
+
+ /* Reset DMA controllers */
+ planb_dbdma_stop(&pb->planb_base->ch2);
+ planb_dbdma_stop(&pb->planb_base->ch1);
+
+ return 0;
+}
+
+static void planb_prepare_close(struct planb *pb)
+{
+ int i;
+
+ /* make sure the dma's are idle */
+ planb_dbdma_stop(&pb->planb_base->ch2);
+ planb_dbdma_stop(&pb->planb_base->ch1);
+ /* free kernel memory of command buffers */
+ if(pb->priv_space != 0) {
+ kfree (pb->priv_space);
+ pb->priv_space = 0;
+ pb->cmd_buff_inited = 0;
+ }
+ if(pb->rawbuf) {
+ for (i = 0; i < pb->rawbuf_size; i++) {
+ ClearPageReserved(virt_to_page(pb->rawbuf[i]));
+ free_pages((unsigned long)pb->rawbuf[i], 0);
+ }
+ kfree(pb->rawbuf);
+ }
+ pb->rawbuf = NULL;
+}
+
+/*****************************/
+/* overlay support functions */
+/*****************************/
+
+static inline int overlay_is_active(struct planb *pb)
+{
+ unsigned int size = pb->tab_size * sizeof(struct dbdma_cmd);
+ unsigned int caddr = (unsigned)in_le32(&pb->planb_base->ch1.cmdptr);
+
+ return (in_le32(&pb->overlay_last1->cmd_dep) == pb->ch1_cmd_phys)
+ && (caddr < (pb->ch1_cmd_phys + size))
+ && (caddr >= (unsigned)pb->ch1_cmd_phys);
+}
+
+static void overlay_start(struct planb *pb)
+{
+
+ DEBUG("PlanB: overlay_start()\n");
+
+ if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) {
+
+ DEBUG("PlanB: presumably, grabbing is in progress...\n");
+
+ planb_dbdma_stop(&pb->planb_base->ch2);
+ out_le32 (&pb->planb_base->ch2.cmdptr,
+ virt_to_bus(pb->ch2_cmd));
+ planb_dbdma_restart(&pb->planb_base->ch2);
+ st_le16 (&pb->ch1_cmd->command, DBDMA_NOP);
+ tab_cmd_dbdma(pb->last_cmd[pb->last_fr],
+ DBDMA_NOP | BR_ALWAYS,
+ virt_to_bus(pb->ch1_cmd));
+ eieio();
+ pb->prev_last_fr = pb->last_fr;
+ pb->last_fr = -2;
+ if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) {
+ IDEBUG("PlanB: became inactive "
+ "in the mean time... reactivating\n");
+ planb_dbdma_stop(&pb->planb_base->ch1);
+ out_le32 (&pb->planb_base->ch1.cmdptr,
+ virt_to_bus(pb->ch1_cmd));
+ planb_dbdma_restart(&pb->planb_base->ch1);
+ }
+ } else {
+
+ DEBUG("PlanB: currently idle, so can do whatever\n");
+
+ planb_dbdma_stop(&pb->planb_base->ch2);
+ planb_dbdma_stop(&pb->planb_base->ch1);
+ st_le32 (&pb->planb_base->ch2.cmdptr,
+ virt_to_bus(pb->ch2_cmd));
+ st_le32 (&pb->planb_base->ch1.cmdptr,
+ virt_to_bus(pb->ch1_cmd));
+ out_le16 (&pb->ch1_cmd->command, DBDMA_NOP);
+ planb_dbdma_restart(&pb->planb_base->ch2);
+ planb_dbdma_restart(&pb->planb_base->ch1);
+ pb->last_fr = -1;
+ }
+ return;
+}
+
+static void overlay_stop(struct planb *pb)
+{
+ DEBUG("PlanB: overlay_stop()\n");
+
+ if(pb->last_fr == -1) {
+
+ DEBUG("PlanB: no grabbing, it seems...\n");
+
+ planb_dbdma_stop(&pb->planb_base->ch2);
+ planb_dbdma_stop(&pb->planb_base->ch1);
+ pb->last_fr = -999;
+ } else if(pb->last_fr == -2) {
+ unsigned int cmd_dep;
+ tab_cmd_dbdma(pb->cap_cmd[pb->prev_last_fr], DBDMA_STOP, 0);
+ eieio();
+ cmd_dep = (unsigned int)in_le32(&pb->overlay_last1->cmd_dep);
+ if(overlay_is_active(pb)) {
+
+ DEBUG("PlanB: overlay is currently active\n");
+
+ planb_dbdma_stop(&pb->planb_base->ch2);
+ planb_dbdma_stop(&pb->planb_base->ch1);
+ if(cmd_dep != pb->ch1_cmd_phys) {
+ out_le32(&pb->planb_base->ch1.cmdptr,
+ virt_to_bus(pb->overlay_last1));
+ planb_dbdma_restart(&pb->planb_base->ch1);
+ }
+ }
+ pb->last_fr = pb->prev_last_fr;
+ pb->prev_last_fr = -999;
+ }
+ return;
+}
+
+static void suspend_overlay(struct planb *pb)
+{
+ int fr = -1;
+ struct dbdma_cmd last;
+
+ DEBUG("PlanB: suspend_overlay: %d\n", pb->suspend);
+
+ if(pb->suspend++)
+ return;
+ if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) {
+ if(pb->last_fr == -2) {
+ fr = pb->prev_last_fr;
+ memcpy(&last, (void*)pb->last_cmd[fr], sizeof(last));
+ tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0);
+ }
+ if(overlay_is_active(pb)) {
+ planb_dbdma_stop(&pb->planb_base->ch2);
+ planb_dbdma_stop(&pb->planb_base->ch1);
+ pb->suspended.overlay = 1;
+ pb->suspended.frame = fr;
+ memcpy(&pb->suspended.cmd, &last, sizeof(last));
+ return;
+ }
+ }
+ pb->suspended.overlay = 0;
+ pb->suspended.frame = fr;
+ memcpy(&pb->suspended.cmd, &last, sizeof(last));
+ return;
+}
+
+static void resume_overlay(struct planb *pb)
+{
+
+ DEBUG("PlanB: resume_overlay: %d\n", pb->suspend);
+
+ if(pb->suspend > 1)
+ return;
+ if(pb->suspended.frame != -1) {
+ memcpy((void*)pb->last_cmd[pb->suspended.frame],
+ &pb->suspended.cmd, sizeof(pb->suspended.cmd));
+ }
+ if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) {
+ goto finish;
+ }
+ if(pb->suspended.overlay) {
+
+ DEBUG("PlanB: overlay being resumed\n");
+
+ st_le16 (&pb->ch1_cmd->command, DBDMA_NOP);
+ st_le16 (&pb->ch2_cmd->command, DBDMA_NOP);
+ /* Set command buffer addresses */
+ st_le32(&pb->planb_base->ch1.cmdptr,
+ virt_to_bus(pb->overlay_last1));
+ out_le32(&pb->planb_base->ch2.cmdptr,
+ virt_to_bus(pb->overlay_last2));
+ /* Start the DMA controller */
+ out_le32 (&pb->planb_base->ch2.control,
+ PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE));
+ out_le32 (&pb->planb_base->ch1.control,
+ PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE));
+ } else if(pb->suspended.frame != -1) {
+ out_le32(&pb->planb_base->ch1.cmdptr,
+ virt_to_bus(pb->last_cmd[pb->suspended.frame]));
+ out_le32 (&pb->planb_base->ch1.control,
+ PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE));
+ }
+
+finish:
+ pb->suspend--;
+ wake_up_interruptible(&pb->suspendq);
+}
+
+static void add_clip(struct planb *pb, struct video_clip *clip)
+{
+ volatile unsigned char *base;
+ int xc = clip->x, yc = clip->y;
+ int wc = clip->width, hc = clip->height;
+ int ww = pb->win.width, hw = pb->win.height;
+ int x, y, xtmp1, xtmp2;
+
+ DEBUG("PlanB: clip %dx%d+%d+%d\n", wc, hc, xc, yc);
+
+ if(xc < 0) {
+ wc += xc;
+ xc = 0;
+ }
+ if(yc < 0) {
+ hc += yc;
+ yc = 0;
+ }
+ if(xc + wc > ww)
+ wc = ww - xc;
+ if(wc <= 0) /* Nothing to do */
+ return;
+ if(yc + hc > hw)
+ hc = hw - yc;
+
+ for (y = yc; y < yc+hc; y++) {
+ xtmp1=xc>>3;
+ xtmp2=(xc+wc)>>3;
+ base = pb->mask + y*96;
+ if(xc != 0 || wc >= 8)
+ *(base + xtmp1) &= (unsigned char)(0x00ff &
+ (0xff00 >> (xc&7)));
+ for (x = xtmp1 + 1; x < xtmp2; x++) {
+ *(base + x) = 0;
+ }
+ if(xc < (ww & ~0x7))
+ *(base + xtmp2) &= (unsigned char)(0x00ff >>
+ ((xc+wc) & 7));
+ }
+
+ return;
+}
+
+static void fill_cmd_buff(struct planb *pb)
+{
+ int restore = 0;
+ volatile struct dbdma_cmd last;
+
+ DEBUG("PlanB: fill_cmd_buff()\n");
+
+ if(pb->overlay_last1 != pb->ch1_cmd) {
+ restore = 1;
+ last = *(pb->overlay_last1);
+ }
+ memset ((void *) pb->ch1_cmd, 0, 2 * pb->tab_size
+ * sizeof(struct dbdma_cmd));
+ cmd_buff (pb);
+ if(restore)
+ *(pb->overlay_last1) = last;
+ if(pb->suspended.overlay) {
+ unsigned long jump_addr = in_le32(&pb->overlay_last1->cmd_dep);
+ if(jump_addr != pb->ch1_cmd_phys) {
+ int i;
+
+ DEBUG("PlanB: adjusting ch1's jump address\n");
+
+ for(i = 0; i < MAX_GBUFFERS; i++) {
+ if(pb->need_pre_capture[i]) {
+ if(jump_addr == virt_to_bus(pb->pre_cmd[i]))
+ goto found;
+ } else {
+ if(jump_addr == virt_to_bus(pb->cap_cmd[i]))
+ goto found;
+ }
+ }
+
+ DEBUG("PlanB: not found...\n");
+
+ goto out;
+found:
+ if(pb->need_pre_capture[i])
+ out_le32(&pb->pre_cmd[i]->phy_addr,
+ virt_to_bus(pb->overlay_last1));
+ else
+ out_le32(&pb->cap_cmd[i]->phy_addr,
+ virt_to_bus(pb->overlay_last1));
+ }
+ }
+out:
+ pb->cmd_buff_inited = 1;
+
+ return;
+}
+
+static void cmd_buff(struct planb *pb)
+{
+ int i, bpp, count, nlines, stepsize, interlace;
+ unsigned long base, jump, addr_com, addr_dep;
+ volatile struct dbdma_cmd *c1 = pb->ch1_cmd;
+ volatile struct dbdma_cmd *c2 = pb->ch2_cmd;
+
+ interlace = pb->win.interlace;
+ bpp = pb->win.bpp;
+ count = (bpp * ((pb->win.x + pb->win.width > pb->win.swidth) ?
+ (pb->win.swidth - pb->win.x) : pb->win.width));
+ nlines = ((pb->win.y + pb->win.height > pb->win.sheight) ?
+ (pb->win.sheight - pb->win.y) : pb->win.height);
+
+ /* Do video in: */
+
+ /* Preamble commands: */
+ addr_com = virt_to_bus(c1);
+ addr_dep = virt_to_bus(&c1->cmd_dep);
+ tab_cmd_dbdma(c1++, DBDMA_NOP, 0);
+ jump = virt_to_bus(c1+16); /* 14 by cmd_geo_setup() and 2 for padding */
+ if((c1 = cmd_geo_setup(c1, pb->win.width, pb->win.height, interlace,
+ bpp, 1, pb)) == NULL) {
+ printk(KERN_WARNING "PlanB: encountered serious problems\n");
+ tab_cmd_dbdma(pb->ch1_cmd + 1, DBDMA_STOP, 0);
+ tab_cmd_dbdma(pb->ch2_cmd + 1, DBDMA_STOP, 0);
+ return;
+ }
+ tab_cmd_store(c1++, addr_com, (unsigned)(DBDMA_NOP | BR_ALWAYS) << 16);
+ tab_cmd_store(c1++, addr_dep, jump);
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel),
+ PLANB_SET(FIELD_SYNC));
+ /* (1) wait for field sync to be set */
+ tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+ PLANB_SET(ODD_FIELD));
+ /* wait for field sync to be cleared */
+ tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
+ /* if not odd field, wait until field sync is set again */
+ tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++;
+ /* assert ch_sync to ch2 */
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch2.control),
+ PLANB_SET(CH_SYNC));
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+ PLANB_SET(DMA_ABORT));
+
+ base = (pb->frame_buffer_phys + pb->offset + pb->win.y * (pb->win.bpl
+ + pb->win.pad) + pb->win.x * bpp);
+
+ if (interlace) {
+ stepsize = 2;
+ jump = virt_to_bus(c1 + (nlines + 1) / 2);
+ } else {
+ stepsize = 1;
+ jump = virt_to_bus(c1 + nlines);
+ }
+
+ /* even field data: */
+ for (i=0; i < nlines; i += stepsize, c1++)
+ tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET,
+ count, base + i * (pb->win.bpl + pb->win.pad), jump);
+
+ /* For non-interlaced, we use even fields only */
+ if (!interlace)
+ goto cmd_tab_data_end;
+
+ /* Resync to odd field */
+ /* (2) wait for field sync to be set */
+ tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+ PLANB_SET(ODD_FIELD));
+ /* wait for field sync to be cleared */
+ tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
+ /* if not odd field, wait until field sync is set again */
+ tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++;
+ /* assert ch_sync to ch2 */
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch2.control),
+ PLANB_SET(CH_SYNC));
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+ PLANB_SET(DMA_ABORT));
+
+ /* odd field data: */
+ jump = virt_to_bus(c1 + nlines / 2);
+ for (i=1; i < nlines; i += stepsize, c1++)
+ tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count,
+ base + i * (pb->win.bpl + pb->win.pad), jump);
+
+ /* And jump back to the start */
+cmd_tab_data_end:
+ pb->overlay_last1 = c1; /* keep a pointer to the last command */
+ tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->ch1_cmd));
+
+ /* Clipmask command buffer */
+
+ /* Preamble commands: */
+ tab_cmd_dbdma(c2++, DBDMA_NOP, 0);
+ tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.wait_sel),
+ PLANB_SET(CH_SYNC));
+ /* wait until ch1 asserts ch_sync */
+ tab_cmd_dbdma(c2++, DBDMA_NOP | WAIT_IFCLR, 0);
+ /* clear ch_sync asserted by ch1 */
+ tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.control),
+ PLANB_CLR(CH_SYNC));
+ tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.wait_sel),
+ PLANB_SET(FIELD_SYNC));
+ tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel),
+ PLANB_SET(ODD_FIELD));
+
+ /* jump to end of even field if appropriate */
+ /* this points to (interlace)? pos. C: pos. B */
+ jump = (interlace) ? virt_to_bus(c2 + (nlines + 1) / 2 + 2):
+ virt_to_bus(c2 + nlines + 2);
+ /* if odd field, skip over to odd field clipmasking */
+ tab_cmd_dbdma(c2++, DBDMA_NOP | BR_IFSET, jump);
+
+ /* even field mask: */
+ tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel),
+ PLANB_SET(DMA_ABORT));
+ /* this points to pos. B */
+ jump = (interlace) ? virt_to_bus(c2 + nlines + 1):
+ virt_to_bus(c2 + nlines);
+ base = virt_to_bus(pb->mask);
+ for (i=0; i < nlines; i += stepsize, c2++)
+ tab_cmd_gen(c2, OUTPUT_MORE | KEY_STREAM0 | BR_IFSET, 96,
+ base + i * 96, jump);
+
+ /* For non-interlaced, we use only even fields */
+ if(!interlace)
+ goto cmd_tab_mask_end;
+
+ /* odd field mask: */
+/* C */ tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel),
+ PLANB_SET(DMA_ABORT));
+ /* this points to pos. B */
+ jump = virt_to_bus(c2 + nlines / 2);
+ base = virt_to_bus(pb->mask);
+ for (i=1; i < nlines; i += 2, c2++) /* abort if set */
+ tab_cmd_gen(c2, OUTPUT_MORE | KEY_STREAM0 | BR_IFSET, 96,
+ base + i * 96, jump);
+
+ /* Inform channel 1 and jump back to start */
+cmd_tab_mask_end:
+ /* ok, I just realized this is kind of flawed. */
+ /* this part is reached only after odd field clipmasking. */
+ /* wanna clean up? */
+ /* wait for field sync to be set */
+ /* corresponds to fsync (1) of ch1 */
+/* B */ tab_cmd_dbdma(c2++, DBDMA_NOP | WAIT_IFCLR, 0);
+ /* restart ch1, meant to clear any dead bit or something */
+ tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch1.control),
+ PLANB_CLR(RUN));
+ tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch1.control),
+ PLANB_SET(RUN));
+ pb->overlay_last2 = c2; /* keep a pointer to the last command */
+ /* start over even field clipmasking */
+ tab_cmd_dbdma(c2, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->ch2_cmd));
+
+ eieio();
+ return;
+}
+
+/*********************************/
+/* grabdisplay support functions */
+/*********************************/
+
+static int palette2fmt[] = {
+ 0,
+ PLANB_GRAY,
+ 0,
+ 0,
+ 0,
+ PLANB_COLOUR32,
+ PLANB_COLOUR15,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+};
+
+#define PLANB_PALETTE_MAX 15
+
+static int vgrab(struct planb *pb, struct video_mmap *mp)
+{
+ unsigned int fr = mp->frame;
+ unsigned int format;
+
+ if(pb->rawbuf==NULL) {
+ int err;
+ if((err=grabbuf_alloc(pb)))
+ return err;
+ }
+
+ IDEBUG("PlanB: grab %d: %dx%d(%u)\n", pb->grabbing,
+ mp->width, mp->height, fr);
+
+ if(pb->grabbing >= MAX_GBUFFERS)
+ return -ENOBUFS;
+ if(fr > (MAX_GBUFFERS - 1) || fr < 0)
+ return -EINVAL;
+ if(mp->height <= 0 || mp->width <= 0)
+ return -EINVAL;
+ if(mp->format < 0 || mp->format >= PLANB_PALETTE_MAX)
+ return -EINVAL;
+ if((format = palette2fmt[mp->format]) == 0)
+ return -EINVAL;
+ if (mp->height * mp->width * format > PLANB_MAX_FBUF) /* format = bpp */
+ return -EINVAL;
+
+ planb_lock(pb);
+ if(mp->width != pb->gwidth[fr] || mp->height != pb->gheight[fr] ||
+ format != pb->gfmt[fr] || (pb->gnorm_switch[fr])) {
+ int i;
+#ifndef PLANB_GSCANLINE
+ unsigned int osize = pb->gwidth[fr] * pb->gheight[fr]
+ * pb->gfmt[fr];
+ unsigned int nsize = mp->width * mp->height * format;
+#endif
+
+ IDEBUG("PlanB: gwidth = %d, gheight = %d, mp->format = %u\n",
+ mp->width, mp->height, mp->format);
+
+#ifndef PLANB_GSCANLINE
+ if(pb->gnorm_switch[fr])
+ nsize = 0;
+ if (nsize < osize) {
+ for(i = pb->gbuf_idx[fr]; osize > 0; i++) {
+ memset((void *)pb->rawbuf[i], 0, PAGE_SIZE);
+ osize -= PAGE_SIZE;
+ }
+ }
+ for(i = pb->l_fr_addr_idx[fr]; i < pb->l_fr_addr_idx[fr]
+ + pb->lnum[fr]; i++)
+ memset((void *)pb->rawbuf[i], 0, PAGE_SIZE);
+#else
+/* XXX TODO */
+/*
+ if(pb->gnorm_switch[fr])
+ memset((void *)pb->gbuffer[fr], 0,
+ pb->gbytes_per_line * pb->gheight[fr]);
+ else {
+ if(mp->
+ for(i = 0; i < pb->gheight[fr]; i++) {
+ memset((void *)(pb->gbuffer[fr]
+ + pb->gbytes_per_line * i
+ }
+ }
+*/
+#endif
+ pb->gwidth[fr] = mp->width;
+ pb->gheight[fr] = mp->height;
+ pb->gfmt[fr] = format;
+ pb->last_cmd[fr] = setup_grab_cmd(fr, pb);
+ planb_pre_capture(fr, pb->gfmt[fr], pb); /* gfmt = bpp */
+ pb->need_pre_capture[fr] = 1;
+ pb->gnorm_switch[fr] = 0;
+ } else
+ pb->need_pre_capture[fr] = 0;
+ pb->frame_stat[fr] = GBUFFER_GRABBING;
+ if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) {
+
+ IDEBUG("PlanB: ch1 inactive, initiating grabbing\n");
+
+ planb_dbdma_stop(&pb->planb_base->ch1);
+ if(pb->need_pre_capture[fr]) {
+
+ IDEBUG("PlanB: padding pre-capture sequence\n");
+
+ out_le32 (&pb->planb_base->ch1.cmdptr,
+ virt_to_bus(pb->pre_cmd[fr]));
+ } else {
+ tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0);
+ tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0);
+ /* let's be on the safe side. here is not timing critical. */
+ tab_cmd_dbdma((pb->cap_cmd[fr] + 1), DBDMA_NOP, 0);
+ out_le32 (&pb->planb_base->ch1.cmdptr,
+ virt_to_bus(pb->cap_cmd[fr]));
+ }
+ planb_dbdma_restart(&pb->planb_base->ch1);
+ pb->last_fr = fr;
+ } else {
+ int i;
+
+ IDEBUG("PlanB: ch1 active, grabbing being queued\n");
+
+ if((pb->last_fr == -1) || ((pb->last_fr == -2) &&
+ overlay_is_active(pb))) {
+
+ IDEBUG("PlanB: overlay is active, grabbing defered\n");
+
+ tab_cmd_dbdma(pb->last_cmd[fr],
+ DBDMA_NOP | BR_ALWAYS,
+ virt_to_bus(pb->ch1_cmd));
+ if(pb->need_pre_capture[fr]) {
+
+ IDEBUG("PlanB: padding pre-capture sequence\n");
+
+ tab_cmd_store(pb->pre_cmd[fr],
+ virt_to_bus(&pb->overlay_last1->cmd_dep),
+ virt_to_bus(pb->ch1_cmd));
+ eieio();
+ out_le32 (&pb->overlay_last1->cmd_dep,
+ virt_to_bus(pb->pre_cmd[fr]));
+ } else {
+ tab_cmd_store(pb->cap_cmd[fr],
+ virt_to_bus(&pb->overlay_last1->cmd_dep),
+ virt_to_bus(pb->ch1_cmd));
+ tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
+ DBDMA_NOP, 0);
+ eieio();
+ out_le32 (&pb->overlay_last1->cmd_dep,
+ virt_to_bus(pb->cap_cmd[fr]));
+ }
+ for(i = 0; overlay_is_active(pb) && i < 999; i++)
+ IDEBUG("PlanB: waiting for overlay done\n");
+ tab_cmd_dbdma(pb->ch1_cmd, DBDMA_NOP, 0);
+ pb->prev_last_fr = fr;
+ pb->last_fr = -2;
+ } else if(pb->last_fr == -2) {
+
+ IDEBUG("PlanB: mixed mode detected, grabbing"
+ " will be done before activating overlay\n");
+
+ tab_cmd_dbdma(pb->ch1_cmd, DBDMA_NOP, 0);
+ if(pb->need_pre_capture[fr]) {
+
+ IDEBUG("PlanB: padding pre-capture sequence\n");
+
+ tab_cmd_dbdma(pb->last_cmd[pb->prev_last_fr],
+ DBDMA_NOP | BR_ALWAYS,
+ virt_to_bus(pb->pre_cmd[fr]));
+ eieio();
+ } else {
+ tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0);
+ if(pb->gwidth[pb->prev_last_fr] !=
+ pb->gwidth[fr]
+ || pb->gheight[pb->prev_last_fr] !=
+ pb->gheight[fr]
+ || pb->gfmt[pb->prev_last_fr] !=
+ pb->gfmt[fr])
+ tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
+ DBDMA_NOP, 0);
+ else
+ tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
+ DBDMA_NOP | BR_ALWAYS,
+ virt_to_bus(pb->cap_cmd[fr] + 16));
+ tab_cmd_dbdma(pb->last_cmd[pb->prev_last_fr],
+ DBDMA_NOP | BR_ALWAYS,
+ virt_to_bus(pb->cap_cmd[fr]));
+ eieio();
+ }
+ tab_cmd_dbdma(pb->last_cmd[fr],
+ DBDMA_NOP | BR_ALWAYS,
+ virt_to_bus(pb->ch1_cmd));
+ eieio();
+ pb->prev_last_fr = fr;
+ pb->last_fr = -2;
+ } else {
+
+ IDEBUG("PlanB: active grabbing session detected\n");
+
+ if(pb->need_pre_capture[fr]) {
+
+ IDEBUG("PlanB: padding pre-capture sequence\n");
+
+ tab_cmd_dbdma(pb->last_cmd[pb->last_fr],
+ DBDMA_NOP | BR_ALWAYS,
+ virt_to_bus(pb->pre_cmd[fr]));
+ eieio();
+ } else {
+ tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0);
+ tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0);
+ if(pb->gwidth[pb->last_fr] != pb->gwidth[fr]
+ || pb->gheight[pb->last_fr] !=
+ pb->gheight[fr]
+ || pb->gfmt[pb->last_fr] !=
+ pb->gfmt[fr])
+ tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
+ DBDMA_NOP, 0);
+ else
+ tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
+ DBDMA_NOP | BR_ALWAYS,
+ virt_to_bus(pb->cap_cmd[fr] + 16));
+ tab_cmd_dbdma(pb->last_cmd[pb->last_fr],
+ DBDMA_NOP | BR_ALWAYS,
+ virt_to_bus(pb->cap_cmd[fr]));
+ eieio();
+ }
+ pb->last_fr = fr;
+ }
+ if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) {
+
+ IDEBUG("PlanB: became inactive in the mean time..."
+ "reactivating\n");
+
+ planb_dbdma_stop(&pb->planb_base->ch1);
+ out_le32 (&pb->planb_base->ch1.cmdptr,
+ virt_to_bus(pb->cap_cmd[fr]));
+ planb_dbdma_restart(&pb->planb_base->ch1);
+ }
+ }
+ pb->grabbing++;
+ planb_unlock(pb);
+
+ return 0;
+}
+
+static void planb_pre_capture(int fr, int bpp, struct planb *pb)
+{
+ volatile struct dbdma_cmd *c1 = pb->pre_cmd[fr];
+ int interlace = (pb->gheight[fr] > pb->maxlines/2)? 1: 0;
+
+ tab_cmd_dbdma(c1++, DBDMA_NOP, 0);
+ if((c1 = cmd_geo_setup(c1, pb->gwidth[fr], pb->gheight[fr], interlace,
+ bpp, 0, pb)) == NULL) {
+ printk(KERN_WARNING "PlanB: encountered some problems\n");
+ tab_cmd_dbdma(pb->pre_cmd[fr] + 1, DBDMA_STOP, 0);
+ return;
+ }
+ /* Sync to even field */
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel),
+ PLANB_SET(FIELD_SYNC));
+ tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+ PLANB_SET(ODD_FIELD));
+ tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
+ tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++;
+ tab_cmd_dbdma(c1++, DBDMA_NOP | INTR_ALWAYS, 0);
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+ PLANB_SET(DMA_ABORT));
+ /* For non-interlaced, we use even fields only */
+ if (pb->gheight[fr] <= pb->maxlines/2)
+ goto cmd_tab_data_end;
+ /* Sync to odd field */
+ tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+ PLANB_SET(ODD_FIELD));
+ tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
+ tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++;
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+ PLANB_SET(DMA_ABORT));
+cmd_tab_data_end:
+ tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->cap_cmd[fr]));
+
+ eieio();
+}
+
+static volatile struct dbdma_cmd *setup_grab_cmd(int fr, struct planb *pb)
+{
+ int i, bpp, count, nlines, stepsize, interlace;
+#ifdef PLANB_GSCANLINE
+ int scanline;
+#else
+ int nlpp, leftover1;
+ unsigned long base;
+#endif
+ unsigned long jump;
+ int pagei;
+ volatile struct dbdma_cmd *c1;
+ volatile struct dbdma_cmd *jump_addr;
+
+ c1 = pb->cap_cmd[fr];
+ interlace = (pb->gheight[fr] > pb->maxlines/2)? 1: 0;
+ bpp = pb->gfmt[fr]; /* gfmt = bpp */
+ count = bpp * pb->gwidth[fr];
+ nlines = pb->gheight[fr];
+#ifdef PLANB_GSCANLINE
+ scanline = pb->gbytes_per_line;
+#else
+ pb->lsize[fr] = count;
+ pb->lnum[fr] = 0;
+#endif
+
+ /* Do video in: */
+
+ /* Preamble commands: */
+ tab_cmd_dbdma(c1++, DBDMA_NOP, 0);
+ tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(c1 + 16)); c1++;
+ if((c1 = cmd_geo_setup(c1, pb->gwidth[fr], pb->gheight[fr], interlace,
+ bpp, 0, pb)) == NULL) {
+ printk(KERN_WARNING "PlanB: encountered serious problems\n");
+ tab_cmd_dbdma(pb->cap_cmd[fr] + 1, DBDMA_STOP, 0);
+ return (pb->cap_cmd[fr] + 2);
+ }
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel),
+ PLANB_SET(FIELD_SYNC));
+ tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+ PLANB_SET(ODD_FIELD));
+ tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
+ tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++;
+ tab_cmd_dbdma(c1++, DBDMA_NOP | INTR_ALWAYS, 0);
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+ PLANB_SET(DMA_ABORT));
+
+ if (interlace) {
+ stepsize = 2;
+ jump_addr = c1 + TAB_FACTOR * (nlines + 1) / 2;
+ } else {
+ stepsize = 1;
+ jump_addr = c1 + TAB_FACTOR * nlines;
+ }
+ jump = virt_to_bus(jump_addr);
+
+ /* even field data: */
+
+ pagei = pb->gbuf_idx[fr];
+#ifdef PLANB_GSCANLINE
+ for (i = 0; i < nlines; i += stepsize) {
+ tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
+ virt_to_bus(pb->rawbuf[pagei
+ + i * scanline / PAGE_SIZE]), jump);
+ }
+#else
+ i = 0;
+ leftover1 = 0;
+ do {
+ int j;
+
+ base = virt_to_bus(pb->rawbuf[pagei]);
+ nlpp = (PAGE_SIZE - leftover1) / count / stepsize;
+ for(j = 0; j < nlpp && i < nlines; j++, i += stepsize, c1++)
+ tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET,
+ count, base + count * j * stepsize + leftover1, jump);
+ if(i < nlines) {
+ int lov0 = PAGE_SIZE - count * nlpp * stepsize - leftover1;
+
+ if(lov0 == 0)
+ leftover1 = 0;
+ else {
+ if(lov0 >= count) {
+ tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, base
+ + count * nlpp * stepsize + leftover1, jump);
+ } else {
+ pb->l_to_addr[fr][pb->lnum[fr]] = pb->rawbuf[pagei]
+ + count * nlpp * stepsize + leftover1;
+ pb->l_to_next_idx[fr][pb->lnum[fr]] = pagei + 1;
+ pb->l_to_next_size[fr][pb->lnum[fr]] = count - lov0;
+ tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
+ virt_to_bus(pb->rawbuf[pb->l_fr_addr_idx[fr]
+ + pb->lnum[fr]]), jump);
+ if(++pb->lnum[fr] > MAX_LNUM)
+ pb->lnum[fr]--;
+ }
+ leftover1 = count * stepsize - lov0;
+ i += stepsize;
+ }
+ }
+ pagei++;
+ } while(i < nlines);
+ tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, jump);
+ c1 = jump_addr;
+#endif /* PLANB_GSCANLINE */
+
+ /* For non-interlaced, we use even fields only */
+ if (!interlace)
+ goto cmd_tab_data_end;
+
+ /* Sync to odd field */
+ tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+ PLANB_SET(ODD_FIELD));
+ tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
+ tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++;
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+ PLANB_SET(DMA_ABORT));
+
+ /* odd field data: */
+ jump_addr = c1 + TAB_FACTOR * nlines / 2;
+ jump = virt_to_bus(jump_addr);
+#ifdef PLANB_GSCANLINE
+ for (i = 1; i < nlines; i += stepsize) {
+ tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
+ virt_to_bus(pb->rawbuf[pagei
+ + i * scanline / PAGE_SIZE]), jump);
+ }
+#else
+ i = 1;
+ leftover1 = 0;
+ pagei = pb->gbuf_idx[fr];
+ if(nlines <= 1)
+ goto skip;
+ do {
+ int j;
+
+ base = virt_to_bus(pb->rawbuf[pagei]);
+ nlpp = (PAGE_SIZE - leftover1) / count / stepsize;
+ if(leftover1 >= count) {
+ tab_cmd_gen(c1++, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count,
+ base + leftover1 - count, jump);
+ i += stepsize;
+ }
+ for(j = 0; j < nlpp && i < nlines; j++, i += stepsize, c1++)
+ tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count,
+ base + count * (j * stepsize + 1) + leftover1, jump);
+ if(i < nlines) {
+ int lov0 = PAGE_SIZE - count * nlpp * stepsize - leftover1;
+
+ if(lov0 == 0)
+ leftover1 = 0;
+ else {
+ if(lov0 > count) {
+ pb->l_to_addr[fr][pb->lnum[fr]] = pb->rawbuf[pagei]
+ + count * (nlpp * stepsize + 1) + leftover1;
+ pb->l_to_next_idx[fr][pb->lnum[fr]] = pagei + 1;
+ pb->l_to_next_size[fr][pb->lnum[fr]] = count * stepsize
+ - lov0;
+ tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
+ virt_to_bus(pb->rawbuf[pb->l_fr_addr_idx[fr]
+ + pb->lnum[fr]]), jump);
+ if(++pb->lnum[fr] > MAX_LNUM)
+ pb->lnum[fr]--;
+ i += stepsize;
+ }
+ leftover1 = count * stepsize - lov0;
+ }
+ }
+ pagei++;
+ } while(i < nlines);
+skip:
+ tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, jump);
+ c1 = jump_addr;
+#endif /* PLANB_GSCANLINE */
+
+cmd_tab_data_end:
+ tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->intr_stat),
+ (fr << 9) | PLANB_FRM_IRQ | PLANB_GEN_IRQ);
+ /* stop it */
+ tab_cmd_dbdma(c1, DBDMA_STOP, 0);
+
+ eieio();
+ return c1;
+}
+
+static void planb_irq(int irq, void *dev_id, struct pt_regs * regs)
+{
+ unsigned int stat, astat;
+ struct planb *pb = (struct planb *)dev_id;
+
+ IDEBUG("PlanB: planb_irq()\n");
+
+ /* get/clear interrupt status bits */
+ eieio();
+ stat = in_le32(&pb->planb_base->intr_stat);
+ astat = stat & pb->intr_mask;
+ out_le32(&pb->planb_base->intr_stat, PLANB_FRM_IRQ
+ & ~astat & stat & ~PLANB_GEN_IRQ);
+ IDEBUG("PlanB: stat = %X, astat = %X\n", stat, astat);
+
+ if(astat & PLANB_FRM_IRQ) {
+ unsigned int fr = stat >> 9;
+#ifndef PLANB_GSCANLINE
+ int i;
+#endif
+ IDEBUG("PlanB: PLANB_FRM_IRQ\n");
+
+ pb->gcount++;
+
+ IDEBUG("PlanB: grab %d: fr = %d, gcount = %d\n",
+ pb->grabbing, fr, pb->gcount);
+#ifndef PLANB_GSCANLINE
+ IDEBUG("PlanB: %d * %d bytes are being copied over\n",
+ pb->lnum[fr], pb->lsize[fr]);
+ for(i = 0; i < pb->lnum[fr]; i++) {
+ int first = pb->lsize[fr] - pb->l_to_next_size[fr][i];
+
+ memcpy(pb->l_to_addr[fr][i],
+ pb->rawbuf[pb->l_fr_addr_idx[fr] + i],
+ first);
+ memcpy(pb->rawbuf[pb->l_to_next_idx[fr][i]],
+ pb->rawbuf[pb->l_fr_addr_idx[fr] + i] + first,
+ pb->l_to_next_size[fr][i]);
+ }
+#endif
+ pb->frame_stat[fr] = GBUFFER_DONE;
+ pb->grabbing--;
+ wake_up_interruptible(&pb->capq);
+ return;
+ }
+ /* incorrect interrupts? */
+ pb->intr_mask = PLANB_CLR_IRQ;
+ out_le32(&pb->planb_base->intr_stat, PLANB_CLR_IRQ);
+ printk(KERN_ERR "PlanB: IRQ lockup, cleared intrrupts"
+ " unconditionally\n");
+}
+
+/*******************************
+ * Device Operations functions *
+ *******************************/
+
+static int planb_open(struct video_device *dev, int mode)
+{
+ struct planb *pb = (struct planb *)dev;
+
+ if (pb->user == 0) {
+ int err;
+ if((err = planb_prepare_open(pb)) != 0)
+ return err;
+ }
+ pb->user++;
+
+ DEBUG("PlanB: device opened\n");
+ return 0;
+}
+
+static void planb_close(struct video_device *dev)
+{
+ struct planb *pb = (struct planb *)dev;
+
+ if(pb->user < 1) /* ??? */
+ return;
+ planb_lock(pb);
+ if (pb->user == 1) {
+ if (pb->overlay) {
+ planb_dbdma_stop(&pb->planb_base->ch2);
+ planb_dbdma_stop(&pb->planb_base->ch1);
+ pb->overlay = 0;
+ }
+ planb_prepare_close(pb);
+ }
+ pb->user--;
+ planb_unlock(pb);
+
+ DEBUG("PlanB: device closed\n");
+}
+
+static long planb_read(struct video_device *v, char *buf, unsigned long count,
+ int nonblock)
+{
+ DEBUG("planb: read request\n");
+ return -EINVAL;
+}
+
+static long planb_write(struct video_device *v, const char *buf,
+ unsigned long count, int nonblock)
+{
+ DEBUG("planb: write request\n");
+ return -EINVAL;
+}
+
+static int planb_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+ struct planb *pb=(struct planb *)dev;
+
+ switch (cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability b;
+
+ DEBUG("PlanB: IOCTL VIDIOCGCAP\n");
+
+ strcpy (b.name, pb->video_dev.name);
+ b.type = VID_TYPE_OVERLAY | VID_TYPE_CLIPPING |
+ VID_TYPE_FRAMERAM | VID_TYPE_SCALES |
+ VID_TYPE_CAPTURE;
+ b.channels = 2; /* composite & svhs */
+ b.audios = 0;
+ b.maxwidth = PLANB_MAXPIXELS;
+ b.maxheight = PLANB_MAXLINES;
+ b.minwidth = 32; /* wild guess */
+ b.minheight = 32;
+ if (copy_to_user(arg,&b,sizeof(b)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSFBUF:
+ {
+ struct video_buffer v;
+ unsigned short bpp;
+ unsigned int fmt;
+
+ DEBUG("PlanB: IOCTL VIDIOCSFBUF\n");
+
+ if (!capable(CAP_SYS_ADMIN)
+ || !capable(CAP_SYS_RAWIO))
+ return -EPERM;
+ if (copy_from_user(&v, arg,sizeof(v)))
+ return -EFAULT;
+ planb_lock(pb);
+ switch(v.depth) {
+ case 8:
+ bpp = 1;
+ fmt = PLANB_GRAY;
+ break;
+ case 15:
+ case 16:
+ bpp = 2;
+ fmt = PLANB_COLOUR15;
+ break;
+ case 24:
+ case 32:
+ bpp = 4;
+ fmt = PLANB_COLOUR32;
+ break;
+ default:
+ planb_unlock(pb);
+ return -EINVAL;
+ }
+ if (bpp * v.width > v.bytesperline) {
+ planb_unlock(pb);
+ return -EINVAL;
+ }
+ pb->win.bpp = bpp;
+ pb->win.color_fmt = fmt;
+ pb->frame_buffer_phys = (unsigned long) v.base;
+ pb->win.sheight = v.height;
+ pb->win.swidth = v.width;
+ pb->picture.depth = pb->win.depth = v.depth;
+ pb->win.bpl = pb->win.bpp * pb->win.swidth;
+ pb->win.pad = v.bytesperline - pb->win.bpl;
+
+ DEBUG("PlanB: Display at %p is %d by %d, bytedepth %d,"
+ " bpl %d (+ %d)\n", v.base, v.width,v.height,
+ pb->win.bpp, pb->win.bpl, pb->win.pad);
+
+ pb->cmd_buff_inited = 0;
+ if(pb->overlay) {
+ suspend_overlay(pb);
+ fill_cmd_buff(pb);
+ resume_overlay(pb);
+ }
+ planb_unlock(pb);
+ return 0;
+ }
+ case VIDIOCGFBUF:
+ {
+ struct video_buffer v;
+
+ DEBUG("PlanB: IOCTL VIDIOCGFBUF\n");
+
+ v.base = (void *)pb->frame_buffer_phys;
+ v.height = pb->win.sheight;
+ v.width = pb->win.swidth;
+ v.depth = pb->win.depth;
+ v.bytesperline = pb->win.bpl + pb->win.pad;
+ if (copy_to_user(arg, &v, sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCCAPTURE:
+ {
+ int i;
+
+ if(copy_from_user(&i, arg, sizeof(i)))
+ return -EFAULT;
+ if(i==0) {
+ DEBUG("PlanB: IOCTL VIDIOCCAPTURE Stop\n");
+
+ if (!(pb->overlay))
+ return 0;
+ planb_lock(pb);
+ pb->overlay = 0;
+ overlay_stop(pb);
+ planb_unlock(pb);
+ } else {
+ DEBUG("PlanB: IOCTL VIDIOCCAPTURE Start\n");
+
+ if (pb->frame_buffer_phys == 0 ||
+ pb->win.width == 0 ||
+ pb->win.height == 0)
+ return -EINVAL;
+ if (pb->overlay)
+ return 0;
+ planb_lock(pb);
+ pb->overlay = 1;
+ if(!(pb->cmd_buff_inited))
+ fill_cmd_buff(pb);
+ overlay_start(pb);
+ planb_unlock(pb);
+ }
+ return 0;
+ }
+ case VIDIOCGCHAN:
+ {
+ struct video_channel v;
+
+ DEBUG("PlanB: IOCTL VIDIOCGCHAN\n");
+
+ if(copy_from_user(&v, arg,sizeof(v)))
+ return -EFAULT;
+ v.flags = 0;
+ v.tuners = 0;
+ v.type = VIDEO_TYPE_CAMERA;
+ v.norm = pb->win.norm;
+ switch(v.channel)
+ {
+ case 0:
+ strcpy(v.name,"Composite");
+ break;
+ case 1:
+ strcpy(v.name,"SVHS");
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ if(copy_to_user(arg,&v,sizeof(v)))
+ return -EFAULT;
+
+ return 0;
+ }
+ case VIDIOCSCHAN:
+ {
+ struct video_channel v;
+
+ DEBUG("PlanB: IOCTL VIDIOCSCHAN\n");
+
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+
+ if (v.norm != pb->win.norm) {
+ int i, maxlines;
+
+ switch (v.norm)
+ {
+ case VIDEO_MODE_PAL:
+ case VIDEO_MODE_SECAM:
+ maxlines = PLANB_MAXLINES;
+ break;
+ case VIDEO_MODE_NTSC:
+ maxlines = PLANB_NTSC_MAXLINES;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ planb_lock(pb);
+ /* empty the grabbing queue */
+ wait_event(pb->capq, !pb->grabbing);
+ pb->maxlines = maxlines;
+ pb->win.norm = v.norm;
+ /* Stop overlay if running */
+ suspend_overlay(pb);
+ for(i = 0; i < MAX_GBUFFERS; i++)
+ pb->gnorm_switch[i] = 1;
+ /* I know it's an overkill, but.... */
+ fill_cmd_buff(pb);
+ /* ok, now init it accordingly */
+ saa_init_regs (pb);
+ /* restart overlay if it was running */
+ resume_overlay(pb);
+ planb_unlock(pb);
+ }
+
+ switch(v.channel)
+ {
+ case 0: /* Composite */
+ saa_set (SAA7196_IOCC,
+ ((saa_regs[pb->win.norm][SAA7196_IOCC] &
+ ~7) | 3), pb);
+ break;
+ case 1: /* SVHS */
+ saa_set (SAA7196_IOCC,
+ ((saa_regs[pb->win.norm][SAA7196_IOCC] &
+ ~7) | 4), pb);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ return 0;
+ }
+ case VIDIOCGPICT:
+ {
+ struct video_picture vp = pb->picture;
+
+ DEBUG("PlanB: IOCTL VIDIOCGPICT\n");
+
+ switch(pb->win.color_fmt) {
+ case PLANB_GRAY:
+ vp.palette = VIDEO_PALETTE_GREY;
+ case PLANB_COLOUR15:
+ vp.palette = VIDEO_PALETTE_RGB555;
+ break;
+ case PLANB_COLOUR32:
+ vp.palette = VIDEO_PALETTE_RGB32;
+ break;
+ default:
+ vp.palette = 0;
+ break;
+ }
+
+ if(copy_to_user(arg,&vp,sizeof(vp)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSPICT:
+ {
+ struct video_picture vp;
+
+ DEBUG("PlanB: IOCTL VIDIOCSPICT\n");
+
+ if(copy_from_user(&vp,arg,sizeof(vp)))
+ return -EFAULT;
+ pb->picture = vp;
+ /* Should we do sanity checks here? */
+ saa_set (SAA7196_BRIG, (unsigned char)
+ ((pb->picture.brightness) >> 8), pb);
+ saa_set (SAA7196_HUEC, (unsigned char)
+ ((pb->picture.hue) >> 8) ^ 0x80, pb);
+ saa_set (SAA7196_CSAT, (unsigned char)
+ ((pb->picture.colour) >> 9), pb);
+ saa_set (SAA7196_CONT, (unsigned char)
+ ((pb->picture.contrast) >> 9), pb);
+
+ return 0;
+ }
+ case VIDIOCSWIN:
+ {
+ struct video_window vw;
+ struct video_clip clip;
+ int i;
+
+ DEBUG("PlanB: IOCTL VIDIOCSWIN\n");
+
+ if(copy_from_user(&vw,arg,sizeof(vw)))
+ return -EFAULT;
+
+ planb_lock(pb);
+ /* Stop overlay if running */
+ suspend_overlay(pb);
+ pb->win.interlace = (vw.height > pb->maxlines/2)? 1: 0;
+ if (pb->win.x != vw.x ||
+ pb->win.y != vw.y ||
+ pb->win.width != vw.width ||
+ pb->win.height != vw.height ||
+ !pb->cmd_buff_inited) {
+ pb->win.x = vw.x;
+ pb->win.y = vw.y;
+ pb->win.width = vw.width;
+ pb->win.height = vw.height;
+ fill_cmd_buff(pb);
+ }
+ /* Reset clip mask */
+ memset ((void *) pb->mask, 0xff, (pb->maxlines
+ * ((PLANB_MAXPIXELS + 7) & ~7)) / 8);
+ /* Add any clip rects */
+ for (i = 0; i < vw.clipcount; i++) {
+ if (copy_from_user(&clip, vw.clips + i,
+ sizeof(struct video_clip)))
+ return -EFAULT;
+ add_clip(pb, &clip);
+ }
+ /* restart overlay if it was running */
+ resume_overlay(pb);
+ planb_unlock(pb);
+ return 0;
+ }
+ case VIDIOCGWIN:
+ {
+ struct video_window vw;
+
+ DEBUG("PlanB: IOCTL VIDIOCGWIN\n");
+
+ vw.x=pb->win.x;
+ vw.y=pb->win.y;
+ vw.width=pb->win.width;
+ vw.height=pb->win.height;
+ vw.chromakey=0;
+ vw.flags=0;
+ if(pb->win.interlace)
+ vw.flags|=VIDEO_WINDOW_INTERLACE;
+ if(copy_to_user(arg,&vw,sizeof(vw)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSYNC: {
+ int i;
+
+ IDEBUG("PlanB: IOCTL VIDIOCSYNC\n");
+
+ if(copy_from_user((void *)&i,arg,sizeof(int)))
+ return -EFAULT;
+
+ IDEBUG("PlanB: sync to frame %d\n", i);
+
+ if(i > (MAX_GBUFFERS - 1) || i < 0)
+ return -EINVAL;
+chk_grab:
+ switch (pb->frame_stat[i]) {
+ case GBUFFER_UNUSED:
+ return -EINVAL;
+ case GBUFFER_GRABBING:
+ IDEBUG("PlanB: waiting for grab"
+ " done (%d)\n", i);
+ interruptible_sleep_on(&pb->capq);
+ if(signal_pending(current))
+ return -EINTR;
+ goto chk_grab;
+ case GBUFFER_DONE:
+ pb->frame_stat[i] = GBUFFER_UNUSED;
+ break;
+ }
+ return 0;
+ }
+
+ case VIDIOCMCAPTURE:
+ {
+ struct video_mmap vm;
+ volatile unsigned int status;
+
+ IDEBUG("PlanB: IOCTL VIDIOCMCAPTURE\n");
+
+ if(copy_from_user((void *) &vm,(void *)arg,sizeof(vm)))
+ return -EFAULT;
+ status = pb->frame_stat[vm.frame];
+ if (status != GBUFFER_UNUSED)
+ return -EBUSY;
+
+ return vgrab(pb, &vm);
+ }
+
+ case VIDIOCGMBUF:
+ {
+ int i;
+ struct video_mbuf vm;
+
+ DEBUG("PlanB: IOCTL VIDIOCGMBUF\n");
+
+ memset(&vm, 0 , sizeof(vm));
+ vm.size = PLANB_MAX_FBUF * MAX_GBUFFERS;
+ vm.frames = MAX_GBUFFERS;
+ for(i = 0; i<MAX_GBUFFERS; i++)
+ vm.offsets[i] = PLANB_MAX_FBUF * i;
+ if(copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
+ return -EFAULT;
+ return 0;
+ }
+
+ case PLANBIOCGSAAREGS:
+ {
+ struct planb_saa_regs preg;
+
+ DEBUG("PlanB: IOCTL PLANBIOCGSAAREGS\n");
+
+ if(copy_from_user(&preg, arg, sizeof(preg)))
+ return -EFAULT;
+ if(preg.addr >= SAA7196_NUMREGS)
+ return -EINVAL;
+ preg.val = saa_regs[pb->win.norm][preg.addr];
+ if(copy_to_user((void *)arg, (void *)&preg,
+ sizeof(preg)))
+ return -EFAULT;
+ return 0;
+ }
+
+ case PLANBIOCSSAAREGS:
+ {
+ struct planb_saa_regs preg;
+
+ DEBUG("PlanB: IOCTL PLANBIOCSSAAREGS\n");
+
+ if(copy_from_user(&preg, arg, sizeof(preg)))
+ return -EFAULT;
+ if(preg.addr >= SAA7196_NUMREGS)
+ return -EINVAL;
+ saa_set (preg.addr, preg.val, pb);
+ return 0;
+ }
+
+ case PLANBIOCGSTAT:
+ {
+ struct planb_stat_regs pstat;
+
+ DEBUG("PlanB: IOCTL PLANBIOCGSTAT\n");
+
+ pstat.ch1_stat = in_le32(&pb->planb_base->ch1.status);
+ pstat.ch2_stat = in_le32(&pb->planb_base->ch2.status);
+ pstat.saa_stat0 = saa_status(0, pb);
+ pstat.saa_stat1 = saa_status(1, pb);
+
+ if(copy_to_user((void *)arg, (void *)&pstat,
+ sizeof(pstat)))
+ return -EFAULT;
+ return 0;
+ }
+
+ case PLANBIOCSMODE: {
+ int v;
+
+ DEBUG("PlanB: IOCTL PLANBIOCSMODE\n");
+
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+
+ switch(v)
+ {
+ case PLANB_TV_MODE:
+ saa_set (SAA7196_STDC,
+ (saa_regs[pb->win.norm][SAA7196_STDC] &
+ 0x7f), pb);
+ break;
+ case PLANB_VTR_MODE:
+ saa_set (SAA7196_STDC,
+ (saa_regs[pb->win.norm][SAA7196_STDC] |
+ 0x80), pb);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ pb->win.mode = v;
+ return 0;
+ }
+ case PLANBIOCGMODE: {
+ int v=pb->win.mode;
+
+ DEBUG("PlanB: IOCTL PLANBIOCGMODE\n");
+
+ if(copy_to_user(arg,&v,sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+#ifdef PLANB_GSCANLINE
+ case PLANBG_GRAB_BPL: {
+ int v=pb->gbytes_per_line;
+
+ DEBUG("PlanB: IOCTL PLANBG_GRAB_BPL\n");
+
+ if(copy_to_user(arg,&v,sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+#endif /* PLANB_GSCANLINE */
+ case PLANB_INTR_DEBUG: {
+ int i;
+
+ DEBUG("PlanB: IOCTL PLANB_INTR_DEBUG\n");
+
+ if(copy_from_user(&i, arg, sizeof(i)))
+ return -EFAULT;
+
+ /* avoid hang ups all together */
+ for (i = 0; i < MAX_GBUFFERS; i++) {
+ if(pb->frame_stat[i] == GBUFFER_GRABBING) {
+ pb->frame_stat[i] = GBUFFER_DONE;
+ }
+ }
+ if(pb->grabbing)
+ pb->grabbing--;
+ wake_up_interruptible(&pb->capq);
+ return 0;
+ }
+ case PLANB_INV_REGS: {
+ int i;
+ struct planb_any_regs any;
+
+ DEBUG("PlanB: IOCTL PLANB_INV_REGS\n");
+
+ if(copy_from_user(&any, arg, sizeof(any)))
+ return -EFAULT;
+ if(any.offset < 0 || any.offset + any.bytes > 0x400)
+ return -EINVAL;
+ if(any.bytes > 128)
+ return -EINVAL;
+ for (i = 0; i < any.bytes; i++) {
+ any.data[i] =
+ in_8((unsigned char *)pb->planb_base
+ + any.offset + i);
+ }
+ if(copy_to_user(arg,&any,sizeof(any)))
+ return -EFAULT;
+ return 0;
+ }
+ default:
+ {
+ DEBUG("PlanB: Unimplemented IOCTL\n");
+ return -ENOIOCTLCMD;
+ }
+ /* Some IOCTLs are currently unsupported on PlanB */
+ case VIDIOCGTUNER: {
+ DEBUG("PlanB: IOCTL VIDIOCGTUNER\n");
+ goto unimplemented; }
+ case VIDIOCSTUNER: {
+ DEBUG("PlanB: IOCTL VIDIOCSTUNER\n");
+ goto unimplemented; }
+ case VIDIOCSFREQ: {
+ DEBUG("PlanB: IOCTL VIDIOCSFREQ\n");
+ goto unimplemented; }
+ case VIDIOCGFREQ: {
+ DEBUG("PlanB: IOCTL VIDIOCGFREQ\n");
+ goto unimplemented; }
+ case VIDIOCKEY: {
+ DEBUG("PlanB: IOCTL VIDIOCKEY\n");
+ goto unimplemented; }
+ case VIDIOCSAUDIO: {
+ DEBUG("PlanB: IOCTL VIDIOCSAUDIO\n");
+ goto unimplemented; }
+ case VIDIOCGAUDIO: {
+ DEBUG("PlanB: IOCTL VIDIOCGAUDIO\n");
+ goto unimplemented; }
+unimplemented:
+ DEBUG(" Unimplemented\n");
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static int planb_mmap(struct vm_area_struct *vma, struct video_device *dev, const char *adr, unsigned long size)
+{
+ int i;
+ struct planb *pb = (struct planb *)dev;
+ unsigned long start = (unsigned long)adr;
+
+ if (size > MAX_GBUFFERS * PLANB_MAX_FBUF)
+ return -EINVAL;
+ if (!pb->rawbuf) {
+ int err;
+ if((err=grabbuf_alloc(pb)))
+ return err;
+ }
+ for (i = 0; i < pb->rawbuf_size; i++) {
+ unsigned long pfn;
+
+ pfn = virt_to_phys((void *)pb->rawbuf[i]) >> PAGE_SHIFT;
+ if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED))
+ return -EAGAIN;
+ start += PAGE_SIZE;
+ if (size <= PAGE_SIZE)
+ break;
+ size -= PAGE_SIZE;
+ }
+ return 0;
+}
+
+static struct video_device planb_template=
+{
+ .owner = THIS_MODULE,
+ .name = PLANB_DEVICE_NAME,
+ .type = VID_TYPE_OVERLAY,
+ .hardware = VID_HARDWARE_PLANB,
+ .open = planb_open,
+ .close = planb_close,
+ .read = planb_read,
+ .write = planb_write,
+ .ioctl = planb_ioctl,
+ .mmap = planb_mmap, /* mmap? */
+};
+
+static int init_planb(struct planb *pb)
+{
+ unsigned char saa_rev;
+ int i, result;
+
+ memset ((void *) &pb->win, 0, sizeof (struct planb_window));
+ /* Simple sanity check */
+ if(def_norm >= NUM_SUPPORTED_NORM || def_norm < 0) {
+ printk(KERN_ERR "PlanB: Option(s) invalid\n");
+ return -2;
+ }
+ pb->win.norm = def_norm;
+ pb->win.mode = PLANB_TV_MODE; /* TV mode */
+ pb->win.interlace=1;
+ pb->win.x=0;
+ pb->win.y=0;
+ pb->win.width=768; /* 640 */
+ pb->win.height=576; /* 480 */
+ pb->maxlines=576;
+#if 0
+ btv->win.cropwidth=768; /* 640 */
+ btv->win.cropheight=576; /* 480 */
+ btv->win.cropx=0;
+ btv->win.cropy=0;
+#endif
+ pb->win.pad=0;
+ pb->win.bpp=4;
+ pb->win.depth=32;
+ pb->win.color_fmt=PLANB_COLOUR32;
+ pb->win.bpl=1024*pb->win.bpp;
+ pb->win.swidth=1024;
+ pb->win.sheight=768;
+#ifdef PLANB_GSCANLINE
+ if((pb->gbytes_per_line = PLANB_MAXPIXELS * 4) > PAGE_SIZE
+ || (pb->gbytes_per_line <= 0))
+ return -3;
+ else {
+ /* page align pb->gbytes_per_line for DMA purpose */
+ for(i = PAGE_SIZE; pb->gbytes_per_line < (i>>1);)
+ i>>=1;
+ pb->gbytes_per_line = i;
+ }
+#endif
+ pb->tab_size = PLANB_MAXLINES + 40;
+ pb->suspend = 0;
+ mutex_init(&pb->lock);
+ pb->ch1_cmd = 0;
+ pb->ch2_cmd = 0;
+ pb->mask = 0;
+ pb->priv_space = 0;
+ pb->offset = 0;
+ pb->user = 0;
+ pb->overlay = 0;
+ init_waitqueue_head(&pb->suspendq);
+ pb->cmd_buff_inited = 0;
+ pb->frame_buffer_phys = 0;
+
+ /* Reset DMA controllers */
+ planb_dbdma_stop(&pb->planb_base->ch2);
+ planb_dbdma_stop(&pb->planb_base->ch1);
+
+ saa_rev = (saa_status(0, pb) & 0xf0) >> 4;
+ printk(KERN_INFO "PlanB: SAA7196 video processor rev. %d\n", saa_rev);
+ /* Initialize the SAA registers in memory and on chip */
+ saa_init_regs (pb);
+
+ /* clear interrupt mask */
+ pb->intr_mask = PLANB_CLR_IRQ;
+
+ result = request_irq(pb->irq, planb_irq, 0, "PlanB", (void *)pb);
+ if (result < 0) {
+ if (result==-EINVAL)
+ printk(KERN_ERR "PlanB: Bad irq number (%d) "
+ "or handler\n", (int)pb->irq);
+ else if (result==-EBUSY)
+ printk(KERN_ERR "PlanB: I don't know why, "
+ "but IRQ %d is busy\n", (int)pb->irq);
+ return result;
+ }
+ disable_irq(pb->irq);
+
+ /* Now add the template and register the device unit. */
+ memcpy(&pb->video_dev,&planb_template,sizeof(planb_template));
+
+ pb->picture.brightness=0x90<<8;
+ pb->picture.contrast = 0x70 << 8;
+ pb->picture.colour = 0x70<<8;
+ pb->picture.hue = 0x8000;
+ pb->picture.whiteness = 0;
+ pb->picture.depth = pb->win.depth;
+
+ pb->frame_stat=NULL;
+ init_waitqueue_head(&pb->capq);
+ for(i=0; i<MAX_GBUFFERS; i++) {
+ pb->gbuf_idx[i] = PLANB_MAX_FBUF * i / PAGE_SIZE;
+ pb->gwidth[i]=0;
+ pb->gheight[i]=0;
+ pb->gfmt[i]=0;
+ pb->cap_cmd[i]=NULL;
+#ifndef PLANB_GSCANLINE
+ pb->l_fr_addr_idx[i] = MAX_GBUFFERS * (PLANB_MAX_FBUF
+ / PAGE_SIZE + 1) + MAX_LNUM * i;
+ pb->lsize[i] = 0;
+ pb->lnum[i] = 0;
+#endif
+ }
+ pb->rawbuf=NULL;
+ pb->grabbing=0;
+
+ /* enable interrupts */
+ out_le32(&pb->planb_base->intr_stat, PLANB_CLR_IRQ);
+ pb->intr_mask = PLANB_FRM_IRQ;
+ enable_irq(pb->irq);
+
+ if(video_register_device(&pb->video_dev, VFL_TYPE_GRABBER, video_nr)<0)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Scan for a PlanB controller, request the irq and map the io memory
+ */
+
+static int find_planb(void)
+{
+ struct planb *pb;
+ struct device_node *planb_devices;
+ unsigned char dev_fn, confreg, bus;
+ unsigned int old_base, new_base;
+ unsigned int irq;
+ struct pci_dev *pdev;
+ int rc;
+
+ if (!machine_is(powermac))
+ return 0;
+
+ planb_devices = find_devices("planb");
+ if (planb_devices == 0) {
+ planb_num=0;
+ printk(KERN_WARNING "PlanB: no device found!\n");
+ return planb_num;
+ }
+
+ if (planb_devices->next != NULL)
+ printk(KERN_ERR "Warning: only using first PlanB device!\n");
+ pb = &planbs[0];
+ planb_num = 1;
+
+ if (planb_devices->n_addrs != 1) {
+ printk (KERN_WARNING "PlanB: expecting 1 address for planb "
+ "(got %d)", planb_devices->n_addrs);
+ return 0;
+ }
+
+ if (planb_devices->n_intrs == 0) {
+ printk(KERN_WARNING "PlanB: no intrs for device %s\n",
+ planb_devices->full_name);
+ return 0;
+ } else {
+ irq = planb_devices->intrs[0].line;
+ }
+
+ /* Initialize PlanB's PCI registers */
+
+ /* There is a bug with the way OF assigns addresses
+ to the devices behind the chaos bridge.
+ control needs only 0x1000 of space, but decodes only
+ the upper 16 bits. It therefore occupies a full 64K.
+ OF assigns the planb controller memory within this space;
+ so we need to change that here in order to access planb. */
+
+ /* We remap to 0xf1000000 in hope that nobody uses it ! */
+
+ bus = (planb_devices->addrs[0].space >> 16) & 0xff;
+ dev_fn = (planb_devices->addrs[0].space >> 8) & 0xff;
+ confreg = planb_devices->addrs[0].space & 0xff;
+ old_base = planb_devices->addrs[0].address;
+ new_base = 0xf1000000;
+
+ DEBUG("PlanB: Found on bus %d, dev %d, func %d, "
+ "membase 0x%x (base reg. 0x%x)\n",
+ bus, PCI_SLOT(dev_fn), PCI_FUNC(dev_fn), old_base, confreg);
+
+ pdev = pci_find_slot (bus, dev_fn);
+ if (!pdev) {
+ printk(KERN_ERR "planb: cannot find slot\n");
+ goto err_out;
+ }
+
+ /* Enable response in memory space, bus mastering,
+ use memory write and invalidate */
+ rc = pci_enable_device(pdev);
+ if (rc) {
+ printk(KERN_ERR "planb: cannot enable PCI device %s\n",
+ pci_name(pdev));
+ goto err_out;
+ }
+ rc = pci_set_mwi(pdev);
+ if (rc) {
+ printk(KERN_ERR "planb: cannot enable MWI on PCI device %s\n",
+ pci_name(pdev));
+ goto err_out_disable;
+ }
+ pci_set_master(pdev);
+
+ /* Set the new base address */
+ pci_write_config_dword (pdev, confreg, new_base);
+
+ planb_regs = (volatile struct planb_registers *)
+ ioremap (new_base, 0x400);
+ pb->planb_base = planb_regs;
+ pb->planb_base_phys = (struct planb_registers *)new_base;
+ pb->irq = irq;
+
+ return planb_num;
+
+err_out_disable:
+ pci_disable_device(pdev);
+err_out:
+ /* FIXME handle error */ /* comment moved from pci_find_slot, above */
+ return 0;
+}
+
+static void release_planb(void)
+{
+ int i;
+ struct planb *pb;
+
+ for (i=0;i<planb_num; i++)
+ {
+ pb=&planbs[i];
+
+ /* stop and flash DMAs unconditionally */
+ planb_dbdma_stop(&pb->planb_base->ch2);
+ planb_dbdma_stop(&pb->planb_base->ch1);
+
+ /* clear and free interrupts */
+ pb->intr_mask = PLANB_CLR_IRQ;
+ out_le32 (&pb->planb_base->intr_stat, PLANB_CLR_IRQ);
+ free_irq(pb->irq, pb);
+
+ /* make sure all allocated memory are freed */
+ planb_prepare_close(pb);
+
+ printk(KERN_INFO "PlanB: unregistering with v4l\n");
+ video_unregister_device(&pb->video_dev);
+
+ /* note that iounmap() does nothing on the PPC right now */
+ iounmap ((void *)pb->planb_base);
+ }
+}
+
+static int __init init_planbs(void)
+{
+ int i;
+
+ if (find_planb()<=0)
+ return -EIO;
+
+ for (i=0; i<planb_num; i++) {
+ if (init_planb(&planbs[i])<0) {
+ printk(KERN_ERR "PlanB: error registering device %d"
+ " with v4l\n", i);
+ release_planb();
+ return -EIO;
+ }
+ printk(KERN_INFO "PlanB: registered device %d with v4l\n", i);
+ }
+ return 0;
+}
+
+static void __exit exit_planbs(void)
+{
+ release_planb();
+}
+
+module_init(init_planbs);
+module_exit(exit_planbs);
diff --git a/linux/drivers/media/video/planb.h b/linux/drivers/media/video/planb.h
new file mode 100644
index 000000000..92823211d
--- /dev/null
+++ b/linux/drivers/media/video/planb.h
@@ -0,0 +1,231 @@
+/*
+ planb - PlanB frame grabber driver
+
+ PlanB is used in the 7x00/8x00 series of PowerMacintosh
+ Computers as video input DMA controller.
+
+ Copyright (C) 1998 Michel Lanners (mlan@cpu.lu)
+
+ Based largely on the bttv driver by Ralph Metzler (rjkm@thp.uni-koeln.de)
+
+ Additional debugging and coding by Takashi Oe (toe@unlserve.unl.edu)
+
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* $Id: planb.h,v 1.13 1999/05/03 19:28:56 mlan Exp $ */
+
+#ifndef _PLANB_H_
+#define _PLANB_H_
+
+#ifdef __KERNEL__
+#include <asm/dbdma.h>
+#include "saa7196.h"
+#endif /* __KERNEL__ */
+
+#define PLANB_DEVICE_NAME "Apple PlanB Video-In"
+#define PLANB_REV "1.0"
+
+#ifdef __KERNEL__
+//#define PLANB_GSCANLINE /* use this if apps have the notion of */
+ /* grab buffer scanline */
+/* This should be safe for both PAL and NTSC */
+#define PLANB_MAXPIXELS 768
+#define PLANB_MAXLINES 576
+#define PLANB_NTSC_MAXLINES 480
+
+/* Uncomment your preferred norm ;-) */
+#define PLANB_DEF_NORM VIDEO_MODE_PAL
+//#define PLANB_DEF_NORM VIDEO_MODE_NTSC
+//#define PLANB_DEF_NORM VIDEO_MODE_SECAM
+
+/* fields settings */
+#define PLANB_GRAY 0x1 /* 8-bit mono? */
+#define PLANB_COLOUR15 0x2 /* 16-bit mode */
+#define PLANB_COLOUR32 0x4 /* 32-bit mode */
+#define PLANB_CLIPMASK 0x8 /* hardware clipmasking */
+
+/* misc. flags for PlanB DMA operation */
+#define CH_SYNC 0x1 /* synchronize channels (set by ch1;
+ cleared by ch2) */
+#define FIELD_SYNC 0x2 /* used for the start of each field
+ (0 -> 1 -> 0 for ch1; 0 -> 1 for ch2) */
+#define EVEN_FIELD 0x0 /* even field is detected if unset */
+#define DMA_ABORT 0x2 /* error or just out of sync if set */
+#define ODD_FIELD 0x4 /* odd field is detected if set */
+
+/* for capture operations */
+#define MAX_GBUFFERS 2
+/* note PLANB_MAX_FBUF must be divisible by PAGE_SIZE */
+#ifdef PLANB_GSCANLINE
+#define PLANB_MAX_FBUF 0x240000 /* 576 * 1024 * 4 */
+#define TAB_FACTOR (1)
+#else
+#define PLANB_MAX_FBUF 0x1b0000 /* 576 * 768 * 4 */
+#define TAB_FACTOR (2)
+#endif
+#endif /* __KERNEL__ */
+
+struct planb_saa_regs {
+ unsigned char addr;
+ unsigned char val;
+};
+
+struct planb_stat_regs {
+ unsigned int ch1_stat;
+ unsigned int ch2_stat;
+ unsigned char saa_stat0;
+ unsigned char saa_stat1;
+};
+
+struct planb_any_regs {
+ unsigned int offset;
+ unsigned int bytes;
+ unsigned char data[128];
+};
+
+/* planb private ioctls */
+#define PLANBIOCGSAAREGS _IOWR('v', BASE_VIDIOCPRIVATE, struct planb_saa_regs) /* Read a saa7196 reg value */
+#define PLANBIOCSSAAREGS _IOW('v', BASE_VIDIOCPRIVATE + 1, struct planb_saa_regs) /* Set a saa7196 reg value */
+#define PLANBIOCGSTAT _IOR('v', BASE_VIDIOCPRIVATE + 2, struct planb_stat_regs) /* Read planb status */
+#define PLANB_TV_MODE 1
+#define PLANB_VTR_MODE 2
+#define PLANBIOCGMODE _IOR('v', BASE_VIDIOCPRIVATE + 3, int) /* Get TV/VTR mode */
+#define PLANBIOCSMODE _IOW('v', BASE_VIDIOCPRIVATE + 4, int) /* Set TV/VTR mode */
+
+#ifdef PLANB_GSCANLINE
+#define PLANBG_GRAB_BPL _IOR('v', BASE_VIDIOCPRIVATE + 5, int) /* # of bytes per scanline in grab buffer */
+#endif
+
+/* call wake_up_interruptible() with appropriate actions */
+#define PLANB_INTR_DEBUG _IOW('v', BASE_VIDIOCPRIVATE + 20, int)
+/* investigate which reg does what */
+#define PLANB_INV_REGS _IOWR('v', BASE_VIDIOCPRIVATE + 21, struct planb_any_regs)
+
+#ifdef __KERNEL__
+
+/* Potentially useful macros */
+#define PLANB_SET(x) ((x) << 16 | (x))
+#define PLANB_CLR(x) ((x) << 16)
+
+/* This represents the physical register layout */
+struct planb_registers {
+ volatile struct dbdma_regs ch1; /* 0x00: video in */
+ volatile unsigned int even; /* 0x40: even field setting */
+ volatile unsigned int odd; /* 0x44; odd field setting */
+ unsigned int pad1[14]; /* empty? */
+ volatile struct dbdma_regs ch2; /* 0x80: clipmask out */
+ unsigned int pad2[16]; /* 0xc0: empty? */
+ volatile unsigned int reg3; /* 0x100: ???? */
+ volatile unsigned int intr_stat; /* 0x104: irq status */
+#define PLANB_CLR_IRQ 0x00 /* clear Plan B interrupt */
+#define PLANB_GEN_IRQ 0x01 /* assert Plan B interrupt */
+#define PLANB_FRM_IRQ 0x0100 /* end of frame */
+ unsigned int pad3[1]; /* empty? */
+ volatile unsigned int reg5; /* 0x10c: ??? */
+ unsigned int pad4[60]; /* empty? */
+ volatile unsigned char saa_addr; /* 0x200: SAA subadr */
+ char pad5[3];
+ volatile unsigned char saa_regval; /* SAA7196 write reg. val */
+ char pad6[3];
+ volatile unsigned char saa_status; /* SAA7196 status byte */
+ /* There is more unused stuff here */
+};
+
+struct planb_window {
+ int x, y;
+ ushort width, height;
+ ushort bpp, bpl, depth, pad;
+ ushort swidth, sheight;
+ int norm;
+ int interlace;
+ u32 color_fmt;
+ int chromakey;
+ int mode; /* used to switch between TV/VTR modes */
+};
+
+struct planb_suspend {
+ int overlay;
+ int frame;
+ struct dbdma_cmd cmd;
+};
+
+struct planb {
+ struct video_device video_dev;
+ struct video_picture picture; /* Current picture params */
+ struct video_audio audio_dev; /* Current audio params */
+
+ volatile struct planb_registers *planb_base; /* virt base of planb */
+ struct planb_registers *planb_base_phys; /* phys base of planb */
+ void *priv_space; /* Org. alloc. mem for kfree */
+ int user;
+ unsigned int tab_size;
+ int maxlines;
+ struct mutex lock;
+ unsigned int irq; /* interrupt number */
+ volatile unsigned int intr_mask;
+
+ int overlay; /* overlay running? */
+ struct planb_window win;
+ unsigned long frame_buffer_phys; /* We need phys for DMA */
+ int offset; /* offset of pixel 1 */
+ volatile struct dbdma_cmd *ch1_cmd; /* Video In DMA cmd buffer */
+ volatile struct dbdma_cmd *ch2_cmd; /* Clip Out DMA cmd buffer */
+ volatile struct dbdma_cmd *overlay_last1;
+ volatile struct dbdma_cmd *overlay_last2;
+ unsigned long ch1_cmd_phys;
+ volatile unsigned char *mask; /* Clipmask buffer */
+ int suspend;
+ wait_queue_head_t suspendq;
+ struct planb_suspend suspended;
+ int cmd_buff_inited; /* cmd buffer inited? */
+
+ int grabbing;
+ unsigned int gcount;
+ wait_queue_head_t capq;
+ int last_fr;
+ int prev_last_fr;
+ unsigned char **rawbuf;
+ int rawbuf_size;
+ int gbuf_idx[MAX_GBUFFERS];
+ volatile struct dbdma_cmd *cap_cmd[MAX_GBUFFERS];
+ volatile struct dbdma_cmd *last_cmd[MAX_GBUFFERS];
+ volatile struct dbdma_cmd *pre_cmd[MAX_GBUFFERS];
+ int need_pre_capture[MAX_GBUFFERS];
+#define PLANB_DUMMY 40 /* # of command buf's allocated for pre-capture seq. */
+ int gwidth[MAX_GBUFFERS], gheight[MAX_GBUFFERS];
+ unsigned int gfmt[MAX_GBUFFERS];
+ int gnorm_switch[MAX_GBUFFERS];
+ volatile unsigned int *frame_stat;
+#define GBUFFER_UNUSED 0x00U
+#define GBUFFER_GRABBING 0x01U
+#define GBUFFER_DONE 0x02U
+#ifdef PLANB_GSCANLINE
+ int gbytes_per_line;
+#else
+#define MAX_LNUM 431 /* change this if PLANB_MAXLINES or */
+ /* PLANB_MAXPIXELS changes */
+ int l_fr_addr_idx[MAX_GBUFFERS];
+ unsigned char *l_to_addr[MAX_GBUFFERS][MAX_LNUM];
+ int l_to_next_idx[MAX_GBUFFERS][MAX_LNUM];
+ int l_to_next_size[MAX_GBUFFERS][MAX_LNUM];
+ int lsize[MAX_GBUFFERS], lnum[MAX_GBUFFERS];
+#endif
+};
+
+#endif /* __KERNEL__ */
+
+#endif /* _PLANB_H_ */
diff --git a/linux/drivers/media/video/pms.c b/linux/drivers/media/video/pms.c
new file mode 100644
index 000000000..7df71e084
--- /dev/null
+++ b/linux/drivers/media/video/pms.c
@@ -0,0 +1,1064 @@
+/*
+ * Media Vision Pro Movie Studio
+ * or
+ * "all you need is an I2C bus some RAM and a prayer"
+ *
+ * This draws heavily on code
+ *
+ * (c) Wolfgang Koehler, wolf@first.gmd.de, Dec. 1994
+ * Kiefernring 15
+ * 14478 Potsdam, Germany
+ *
+ * Most of this code is directly derived from his userspace driver.
+ * His driver works so send any reports to alan@redhat.com unless the
+ * userspace driver also doesn't work for you...
+ *
+ * Changes:
+ * 08/07/2003 Daniele Bellucci <bellucda@tiscali.it>
+ * - pms_capture: report back -EFAULT
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <linux/sched.h>
+#include "compat.h"
+#include <linux/videodev.h>
+#include <linux/mutex.h>
+
+#include <asm/uaccess.h>
+
+
+#define MOTOROLA 1
+#define PHILIPS2 2
+#define PHILIPS1 3
+#define MVVMEMORYWIDTH 0x40 /* 512 bytes */
+
+struct pms_device
+{
+ struct video_device v;
+ struct video_picture picture;
+ int height;
+ int width;
+ struct mutex lock;
+};
+
+struct i2c_info
+{
+ u8 slave;
+ u8 sub;
+ u8 data;
+ u8 hits;
+};
+
+static int i2c_count = 0;
+static struct i2c_info i2cinfo[64];
+
+static int decoder = PHILIPS2;
+static int standard = 0; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */
+
+/*
+ * I/O ports and Shared Memory
+ */
+
+static int io_port = 0x250;
+static int data_port = 0x251;
+static int mem_base = 0xC8000;
+static void __iomem *mem;
+static int video_nr = -1;
+
+
+
+static inline void mvv_write(u8 index, u8 value)
+{
+ outw(index|(value<<8), io_port);
+}
+
+static inline u8 mvv_read(u8 index)
+{
+ outb(index, io_port);
+ return inb(data_port);
+}
+
+static int pms_i2c_stat(u8 slave)
+{
+ int counter;
+ int i;
+
+ outb(0x28, io_port);
+
+ counter=0;
+ while((inb(data_port)&0x01)==0)
+ if(counter++==256)
+ break;
+
+ while((inb(data_port)&0x01)!=0)
+ if(counter++==256)
+ break;
+
+ outb(slave, io_port);
+
+ counter=0;
+ while((inb(data_port)&0x01)==0)
+ if(counter++==256)
+ break;
+
+ while((inb(data_port)&0x01)!=0)
+ if(counter++==256)
+ break;
+
+ for(i=0;i<12;i++)
+ {
+ char st=inb(data_port);
+ if((st&2)!=0)
+ return -1;
+ if((st&1)==0)
+ break;
+ }
+ outb(0x29, io_port);
+ return inb(data_port);
+}
+
+static int pms_i2c_write(u16 slave, u16 sub, u16 data)
+{
+ int skip=0;
+ int count;
+ int i;
+
+ for(i=0;i<i2c_count;i++)
+ {
+ if((i2cinfo[i].slave==slave) &&
+ (i2cinfo[i].sub == sub))
+ {
+ if(i2cinfo[i].data==data)
+ skip=1;
+ i2cinfo[i].data=data;
+ i=i2c_count+1;
+ }
+ }
+
+ if(i==i2c_count && i2c_count<64)
+ {
+ i2cinfo[i2c_count].slave=slave;
+ i2cinfo[i2c_count].sub=sub;
+ i2cinfo[i2c_count].data=data;
+ i2c_count++;
+ }
+
+ if(skip)
+ return 0;
+
+ mvv_write(0x29, sub);
+ mvv_write(0x2A, data);
+ mvv_write(0x28, slave);
+
+ outb(0x28, io_port);
+
+ count=0;
+ while((inb(data_port)&1)==0)
+ if(count>255)
+ break;
+ while((inb(data_port)&1)!=0)
+ if(count>255)
+ break;
+
+ count=inb(data_port);
+
+ if(count&2)
+ return -1;
+ return count;
+}
+
+static int pms_i2c_read(int slave, int sub)
+{
+ int i=0;
+ for(i=0;i<i2c_count;i++)
+ {
+ if(i2cinfo[i].slave==slave && i2cinfo[i].sub==sub)
+ return i2cinfo[i].data;
+ }
+ return 0;
+}
+
+
+static void pms_i2c_andor(int slave, int sub, int and, int or)
+{
+ u8 tmp;
+
+ tmp=pms_i2c_read(slave, sub);
+ tmp = (tmp&and)|or;
+ pms_i2c_write(slave, sub, tmp);
+}
+
+/*
+ * Control functions
+ */
+
+
+static void pms_videosource(short source)
+{
+ mvv_write(0x2E, source?0x31:0x30);
+}
+
+static void pms_hue(short hue)
+{
+ switch(decoder)
+ {
+ case MOTOROLA:
+ pms_i2c_write(0x8A, 0x00, hue);
+ break;
+ case PHILIPS2:
+ pms_i2c_write(0x8A, 0x07, hue);
+ break;
+ case PHILIPS1:
+ pms_i2c_write(0x42, 0x07, hue);
+ break;
+ }
+}
+
+static void pms_colour(short colour)
+{
+ switch(decoder)
+ {
+ case MOTOROLA:
+ pms_i2c_write(0x8A, 0x00, colour);
+ break;
+ case PHILIPS1:
+ pms_i2c_write(0x42, 0x12, colour);
+ break;
+ }
+}
+
+
+static void pms_contrast(short contrast)
+{
+ switch(decoder)
+ {
+ case MOTOROLA:
+ pms_i2c_write(0x8A, 0x00, contrast);
+ break;
+ case PHILIPS1:
+ pms_i2c_write(0x42, 0x13, contrast);
+ break;
+ }
+}
+
+static void pms_brightness(short brightness)
+{
+ switch(decoder)
+ {
+ case MOTOROLA:
+ pms_i2c_write(0x8A, 0x00, brightness);
+ pms_i2c_write(0x8A, 0x00, brightness);
+ pms_i2c_write(0x8A, 0x00, brightness);
+ break;
+ case PHILIPS1:
+ pms_i2c_write(0x42, 0x19, brightness);
+ break;
+ }
+}
+
+
+static void pms_format(short format)
+{
+ int target;
+ standard = format;
+
+ if(decoder==PHILIPS1)
+ target=0x42;
+ else if(decoder==PHILIPS2)
+ target=0x8A;
+ else
+ return;
+
+ switch(format)
+ {
+ case 0: /* Auto */
+ pms_i2c_andor(target, 0x0D, 0xFE,0x00);
+ pms_i2c_andor(target, 0x0F, 0x3F,0x80);
+ break;
+ case 1: /* NTSC */
+ pms_i2c_andor(target, 0x0D, 0xFE, 0x00);
+ pms_i2c_andor(target, 0x0F, 0x3F, 0x40);
+ break;
+ case 2: /* PAL */
+ pms_i2c_andor(target, 0x0D, 0xFE, 0x00);
+ pms_i2c_andor(target, 0x0F, 0x3F, 0x00);
+ break;
+ case 3: /* SECAM */
+ pms_i2c_andor(target, 0x0D, 0xFE, 0x01);
+ pms_i2c_andor(target, 0x0F, 0x3F, 0x00);
+ break;
+ }
+}
+
+#ifdef FOR_FUTURE_EXPANSION
+
+/*
+ * These features of the PMS card are not currently exposes. They
+ * could become a private v4l ioctl for PMSCONFIG or somesuch if
+ * people need it. We also don't yet use the PMS interrupt.
+ */
+
+static void pms_hstart(short start)
+{
+ switch(decoder)
+ {
+ case PHILIPS1:
+ pms_i2c_write(0x8A, 0x05, start);
+ pms_i2c_write(0x8A, 0x18, start);
+ break;
+ case PHILIPS2:
+ pms_i2c_write(0x42, 0x05, start);
+ pms_i2c_write(0x42, 0x18, start);
+ break;
+ }
+}
+
+/*
+ * Bandpass filters
+ */
+
+static void pms_bandpass(short pass)
+{
+ if(decoder==PHILIPS2)
+ pms_i2c_andor(0x8A, 0x06, 0xCF, (pass&0x03)<<4);
+ else if(decoder==PHILIPS1)
+ pms_i2c_andor(0x42, 0x06, 0xCF, (pass&0x03)<<4);
+}
+
+static void pms_antisnow(short snow)
+{
+ if(decoder==PHILIPS2)
+ pms_i2c_andor(0x8A, 0x06, 0xF3, (snow&0x03)<<2);
+ else if(decoder==PHILIPS1)
+ pms_i2c_andor(0x42, 0x06, 0xF3, (snow&0x03)<<2);
+}
+
+static void pms_sharpness(short sharp)
+{
+ if(decoder==PHILIPS2)
+ pms_i2c_andor(0x8A, 0x06, 0xFC, sharp&0x03);
+ else if(decoder==PHILIPS1)
+ pms_i2c_andor(0x42, 0x06, 0xFC, sharp&0x03);
+}
+
+static void pms_chromaagc(short agc)
+{
+ if(decoder==PHILIPS2)
+ pms_i2c_andor(0x8A, 0x0C, 0x9F, (agc&0x03)<<5);
+ else if(decoder==PHILIPS1)
+ pms_i2c_andor(0x42, 0x0C, 0x9F, (agc&0x03)<<5);
+}
+
+static void pms_vertnoise(short noise)
+{
+ if(decoder==PHILIPS2)
+ pms_i2c_andor(0x8A, 0x10, 0xFC, noise&3);
+ else if(decoder==PHILIPS1)
+ pms_i2c_andor(0x42, 0x10, 0xFC, noise&3);
+}
+
+static void pms_forcecolour(short colour)
+{
+ if(decoder==PHILIPS2)
+ pms_i2c_andor(0x8A, 0x0C, 0x7F, (colour&1)<<7);
+ else if(decoder==PHILIPS1)
+ pms_i2c_andor(0x42, 0x0C, 0x7, (colour&1)<<7);
+}
+
+static void pms_antigamma(short gamma)
+{
+ if(decoder==PHILIPS2)
+ pms_i2c_andor(0xB8, 0x00, 0x7F, (gamma&1)<<7);
+ else if(decoder==PHILIPS1)
+ pms_i2c_andor(0x42, 0x20, 0x7, (gamma&1)<<7);
+}
+
+static void pms_prefilter(short filter)
+{
+ if(decoder==PHILIPS2)
+ pms_i2c_andor(0x8A, 0x06, 0xBF, (filter&1)<<6);
+ else if(decoder==PHILIPS1)
+ pms_i2c_andor(0x42, 0x06, 0xBF, (filter&1)<<6);
+}
+
+static void pms_hfilter(short filter)
+{
+ if(decoder==PHILIPS2)
+ pms_i2c_andor(0xB8, 0x04, 0x1F, (filter&7)<<5);
+ else if(decoder==PHILIPS1)
+ pms_i2c_andor(0x42, 0x24, 0x1F, (filter&7)<<5);
+}
+
+static void pms_vfilter(short filter)
+{
+ if(decoder==PHILIPS2)
+ pms_i2c_andor(0xB8, 0x08, 0x9F, (filter&3)<<5);
+ else if(decoder==PHILIPS1)
+ pms_i2c_andor(0x42, 0x28, 0x9F, (filter&3)<<5);
+}
+
+static void pms_killcolour(short colour)
+{
+ if(decoder==PHILIPS2)
+ {
+ pms_i2c_andor(0x8A, 0x08, 0x07, (colour&0x1F)<<3);
+ pms_i2c_andor(0x8A, 0x09, 0x07, (colour&0x1F)<<3);
+ }
+ else if(decoder==PHILIPS1)
+ {
+ pms_i2c_andor(0x42, 0x08, 0x07, (colour&0x1F)<<3);
+ pms_i2c_andor(0x42, 0x09, 0x07, (colour&0x1F)<<3);
+ }
+}
+
+static void pms_chromagain(short chroma)
+{
+ if(decoder==PHILIPS2)
+ {
+ pms_i2c_write(0x8A, 0x11, chroma);
+ }
+ else if(decoder==PHILIPS1)
+ {
+ pms_i2c_write(0x42, 0x11, chroma);
+ }
+}
+
+
+static void pms_spacialcompl(short data)
+{
+ mvv_write(0x3B, data);
+}
+
+static void pms_spacialcomph(short data)
+{
+ mvv_write(0x3A, data);
+}
+
+static void pms_vstart(short start)
+{
+ mvv_write(0x16, start);
+ mvv_write(0x17, (start>>8)&0x01);
+}
+
+#endif
+
+static void pms_secamcross(short cross)
+{
+ if(decoder==PHILIPS2)
+ pms_i2c_andor(0x8A, 0x0F, 0xDF, (cross&1)<<5);
+ else if(decoder==PHILIPS1)
+ pms_i2c_andor(0x42, 0x0F, 0xDF, (cross&1)<<5);
+}
+
+
+static void pms_swsense(short sense)
+{
+ if(decoder==PHILIPS2)
+ {
+ pms_i2c_write(0x8A, 0x0A, sense);
+ pms_i2c_write(0x8A, 0x0B, sense);
+ }
+ else if(decoder==PHILIPS1)
+ {
+ pms_i2c_write(0x42, 0x0A, sense);
+ pms_i2c_write(0x42, 0x0B, sense);
+ }
+}
+
+
+static void pms_framerate(short frr)
+{
+ int fps=(standard==1)?30:25;
+ if(frr==0)
+ return;
+ fps=fps/frr;
+ mvv_write(0x14,0x80|fps);
+ mvv_write(0x15,1);
+}
+
+static void pms_vert(u8 deciden, u8 decinum)
+{
+ mvv_write(0x1C, deciden); /* Denominator */
+ mvv_write(0x1D, decinum); /* Numerator */
+}
+
+/*
+ * Turn 16bit ratios into best small ratio the chipset can grok
+ */
+
+static void pms_vertdeci(unsigned short decinum, unsigned short deciden)
+{
+ /* Knock it down by /5 once */
+ if(decinum%5==0)
+ {
+ deciden/=5;
+ decinum/=5;
+ }
+ /*
+ * 3's
+ */
+ while(decinum%3==0 && deciden%3==0)
+ {
+ deciden/=3;
+ decinum/=3;
+ }
+ /*
+ * 2's
+ */
+ while(decinum%2==0 && deciden%2==0)
+ {
+ decinum/=2;
+ deciden/=2;
+ }
+ /*
+ * Fudgyify
+ */
+ while(deciden>32)
+ {
+ deciden/=2;
+ decinum=(decinum+1)/2;
+ }
+ if(deciden==32)
+ deciden--;
+ pms_vert(deciden,decinum);
+}
+
+static void pms_horzdeci(short decinum, short deciden)
+{
+ if(decinum<=512)
+ {
+ if(decinum%5==0)
+ {
+ decinum/=5;
+ deciden/=5;
+ }
+ }
+ else
+ {
+ decinum=512;
+ deciden=640; /* 768 would be ideal */
+ }
+
+ while(((decinum|deciden)&1)==0)
+ {
+ decinum>>=1;
+ deciden>>=1;
+ }
+ while(deciden>32)
+ {
+ deciden>>=1;
+ decinum=(decinum+1)>>1;
+ }
+ if(deciden==32)
+ deciden--;
+
+ mvv_write(0x24, 0x80|deciden);
+ mvv_write(0x25, decinum);
+}
+
+static void pms_resolution(short width, short height)
+{
+ int fg_height;
+
+ fg_height=height;
+ if(fg_height>280)
+ fg_height=280;
+
+ mvv_write(0x18, fg_height);
+ mvv_write(0x19, fg_height>>8);
+
+ if(standard==1)
+ {
+ mvv_write(0x1A, 0xFC);
+ mvv_write(0x1B, 0x00);
+ if(height>fg_height)
+ pms_vertdeci(240,240);
+ else
+ pms_vertdeci(fg_height,240);
+ }
+ else
+ {
+ mvv_write(0x1A, 0x1A);
+ mvv_write(0x1B, 0x01);
+ if(fg_height>256)
+ pms_vertdeci(270,270);
+ else
+ pms_vertdeci(fg_height, 270);
+ }
+ mvv_write(0x12,0);
+ mvv_write(0x13, MVVMEMORYWIDTH);
+ mvv_write(0x42, 0x00);
+ mvv_write(0x43, 0x00);
+ mvv_write(0x44, MVVMEMORYWIDTH);
+
+ mvv_write(0x22, width+8);
+ mvv_write(0x23, (width+8)>> 8);
+
+ if(standard==1)
+ pms_horzdeci(width,640);
+ else
+ pms_horzdeci(width+8, 768);
+
+ mvv_write(0x30, mvv_read(0x30)&0xFE);
+ mvv_write(0x08, mvv_read(0x08)|0x01);
+ mvv_write(0x01, mvv_read(0x01)&0xFD);
+ mvv_write(0x32, 0x00);
+ mvv_write(0x33, MVVMEMORYWIDTH);
+}
+
+
+/*
+ * Set Input
+ */
+
+static void pms_vcrinput(short input)
+{
+ if(decoder==PHILIPS2)
+ pms_i2c_andor(0x8A,0x0D,0x7F,(input&1)<<7);
+ else if(decoder==PHILIPS1)
+ pms_i2c_andor(0x42,0x0D,0x7F,(input&1)<<7);
+}
+
+
+static int pms_capture(struct pms_device *dev, char __user *buf, int rgb555, int count)
+{
+ int y;
+ int dw = 2*dev->width;
+
+ char tmp[dw+32]; /* using a temp buffer is faster than direct */
+ int cnt = 0;
+ int len=0;
+ unsigned char r8 = 0x5; /* value for reg8 */
+
+ if (rgb555)
+ r8 |= 0x20; /* else use untranslated rgb = 565 */
+ mvv_write(0x08,r8); /* capture rgb555/565, init DRAM, PC enable */
+
+/* printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */
+
+ for (y = 0; y < dev->height; y++ )
+ {
+ writeb(0, mem); /* synchronisiert neue Zeile */
+
+ /*
+ * This is in truth a fifo, be very careful as if you
+ * forgot this odd things will occur 8)
+ */
+
+ memcpy_fromio(tmp, mem, dw+32); /* discard 16 word */
+ cnt -= dev->height;
+ while (cnt <= 0)
+ {
+ /*
+ * Don't copy too far
+ */
+ int dt=dw;
+ if(dt+len>count)
+ dt=count-len;
+ cnt += dev->height;
+ if (copy_to_user(buf, tmp+32, dt))
+ return len ? len : -EFAULT;
+ buf += dt;
+ len += dt;
+ }
+ }
+ return len;
+}
+
+
+/*
+ * Video4linux interfacing
+ */
+
+static int pms_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct pms_device *pd=(struct pms_device *)dev;
+
+ switch(cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *b = arg;
+ strcpy(b->name, "Mediavision PMS");
+ b->type = VID_TYPE_CAPTURE|VID_TYPE_SCALES;
+ b->channels = 4;
+ b->audios = 0;
+ b->maxwidth = 640;
+ b->maxheight = 480;
+ b->minwidth = 16;
+ b->minheight = 16;
+ return 0;
+ }
+ case VIDIOCGCHAN:
+ {
+ struct video_channel *v = arg;
+ if(v->channel<0 || v->channel>3)
+ return -EINVAL;
+ v->flags=0;
+ v->tuners=1;
+ /* Good question.. its composite or SVHS so.. */
+ v->type = VIDEO_TYPE_CAMERA;
+ switch(v->channel)
+ {
+ case 0:
+ strcpy(v->name, "Composite");break;
+ case 1:
+ strcpy(v->name, "SVideo");break;
+ case 2:
+ strcpy(v->name, "Composite(VCR)");break;
+ case 3:
+ strcpy(v->name, "SVideo(VCR)");break;
+ }
+ return 0;
+ }
+ case VIDIOCSCHAN:
+ {
+ struct video_channel *v = arg;
+ if(v->channel<0 || v->channel>3)
+ return -EINVAL;
+ mutex_lock(&pd->lock);
+ pms_videosource(v->channel&1);
+ pms_vcrinput(v->channel>>1);
+ mutex_unlock(&pd->lock);
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner *v = arg;
+ if(v->tuner)
+ return -EINVAL;
+ strcpy(v->name, "Format");
+ v->rangelow=0;
+ v->rangehigh=0;
+ v->flags= VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM;
+ switch(standard)
+ {
+ case 0:
+ v->mode = VIDEO_MODE_AUTO;
+ break;
+ case 1:
+ v->mode = VIDEO_MODE_NTSC;
+ break;
+ case 2:
+ v->mode = VIDEO_MODE_PAL;
+ break;
+ case 3:
+ v->mode = VIDEO_MODE_SECAM;
+ break;
+ }
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner *v = arg;
+ if(v->tuner)
+ return -EINVAL;
+ mutex_lock(&pd->lock);
+ switch(v->mode)
+ {
+ case VIDEO_MODE_AUTO:
+ pms_framerate(25);
+ pms_secamcross(0);
+ pms_format(0);
+ break;
+ case VIDEO_MODE_NTSC:
+ pms_framerate(30);
+ pms_secamcross(0);
+ pms_format(1);
+ break;
+ case VIDEO_MODE_PAL:
+ pms_framerate(25);
+ pms_secamcross(0);
+ pms_format(2);
+ break;
+ case VIDEO_MODE_SECAM:
+ pms_framerate(25);
+ pms_secamcross(1);
+ pms_format(2);
+ break;
+ default:
+ mutex_unlock(&pd->lock);
+ return -EINVAL;
+ }
+ mutex_unlock(&pd->lock);
+ return 0;
+ }
+ case VIDIOCGPICT:
+ {
+ struct video_picture *p = arg;
+ *p = pd->picture;
+ return 0;
+ }
+ case VIDIOCSPICT:
+ {
+ struct video_picture *p = arg;
+ if(!((p->palette==VIDEO_PALETTE_RGB565 && p->depth==16)
+ ||(p->palette==VIDEO_PALETTE_RGB555 && p->depth==15)))
+ return -EINVAL;
+ pd->picture= *p;
+
+ /*
+ * Now load the card.
+ */
+
+ mutex_lock(&pd->lock);
+ pms_brightness(p->brightness>>8);
+ pms_hue(p->hue>>8);
+ pms_colour(p->colour>>8);
+ pms_contrast(p->contrast>>8);
+ mutex_unlock(&pd->lock);
+ return 0;
+ }
+ case VIDIOCSWIN:
+ {
+ struct video_window *vw = arg;
+ if(vw->flags)
+ return -EINVAL;
+ if(vw->clipcount)
+ return -EINVAL;
+ if(vw->height<16||vw->height>480)
+ return -EINVAL;
+ if(vw->width<16||vw->width>640)
+ return -EINVAL;
+ pd->width=vw->width;
+ pd->height=vw->height;
+ mutex_lock(&pd->lock);
+ pms_resolution(pd->width, pd->height);
+ mutex_unlock(&pd->lock); /* Ok we figured out what to use from our wide choice */
+ return 0;
+ }
+ case VIDIOCGWIN:
+ {
+ struct video_window *vw = arg;
+ memset(vw,0,sizeof(*vw));
+ vw->width=pd->width;
+ vw->height=pd->height;
+ return 0;
+ }
+ case VIDIOCKEY:
+ return 0;
+ case VIDIOCCAPTURE:
+ case VIDIOCGFBUF:
+ case VIDIOCSFBUF:
+ case VIDIOCGFREQ:
+ case VIDIOCSFREQ:
+ case VIDIOCGAUDIO:
+ case VIDIOCSAUDIO:
+ return -EINVAL;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static int pms_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, pms_do_ioctl);
+}
+
+static ssize_t pms_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct video_device *v = video_devdata(file);
+ struct pms_device *pd=(struct pms_device *)v;
+ int len;
+
+ mutex_lock(&pd->lock);
+ len=pms_capture(pd, buf, (pd->picture.depth==16)?0:1,count);
+ mutex_unlock(&pd->lock);
+ return len;
+}
+
+static struct file_operations pms_fops = {
+ .owner = THIS_MODULE,
+ .open = video_exclusive_open,
+ .release = video_exclusive_release,
+ .ioctl = pms_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .read = pms_read,
+ .llseek = no_llseek,
+};
+
+static struct video_device pms_template=
+{
+ .owner = THIS_MODULE,
+ .name = "Mediavision PMS",
+ .type = VID_TYPE_CAPTURE,
+ .hardware = VID_HARDWARE_PMS,
+ .fops = &pms_fops,
+};
+
+static struct pms_device pms_device;
+
+
+/*
+ * Probe for and initialise the Mediavision PMS
+ */
+
+static int init_mediavision(void)
+{
+ int id;
+ int idec, decst;
+ int i;
+
+ unsigned char i2c_defs[]={
+ 0x4C,0x30,0x00,0xE8,
+ 0xB6,0xE2,0x00,0x00,
+ 0xFF,0xFF,0x00,0x00,
+ 0x00,0x00,0x78,0x98,
+ 0x00,0x00,0x00,0x00,
+ 0x34,0x0A,0xF4,0xCE,
+ 0xE4
+ };
+
+ mem = ioremap(mem_base, 0x800);
+ if (!mem)
+ return -ENOMEM;
+
+ if (!request_region(0x9A01, 1, "Mediavision PMS config"))
+ {
+ printk(KERN_WARNING "mediavision: unable to detect: 0x9A01 in use.\n");
+ iounmap(mem);
+ return -EBUSY;
+ }
+ if (!request_region(io_port, 3, "Mediavision PMS"))
+ {
+ printk(KERN_WARNING "mediavision: I/O port %d in use.\n", io_port);
+ release_region(0x9A01, 1);
+ iounmap(mem);
+ return -EBUSY;
+ }
+ outb(0xB8, 0x9A01); /* Unlock */
+ outb(io_port>>4, 0x9A01); /* Set IO port */
+
+
+ id=mvv_read(3);
+ decst=pms_i2c_stat(0x43);
+
+ if(decst!=-1)
+ idec=2;
+ else if(pms_i2c_stat(0xb9)!=-1)
+ idec=3;
+ else if(pms_i2c_stat(0x8b)!=-1)
+ idec=1;
+ else
+ idec=0;
+
+ printk(KERN_INFO "PMS type is %d\n", idec);
+ if(idec == 0) {
+ release_region(io_port, 3);
+ release_region(0x9A01, 1);
+ iounmap(mem);
+ return -ENODEV;
+ }
+
+ /*
+ * Ok we have a PMS of some sort
+ */
+
+ mvv_write(0x04, mem_base>>12); /* Set the memory area */
+
+ /* Ok now load the defaults */
+
+ for(i=0;i<0x19;i++)
+ {
+ if(i2c_defs[i]==0xFF)
+ pms_i2c_andor(0x8A, i, 0x07,0x00);
+ else
+ pms_i2c_write(0x8A, i, i2c_defs[i]);
+ }
+
+ pms_i2c_write(0xB8,0x00,0x12);
+ pms_i2c_write(0xB8,0x04,0x00);
+ pms_i2c_write(0xB8,0x07,0x00);
+ pms_i2c_write(0xB8,0x08,0x00);
+ pms_i2c_write(0xB8,0x09,0xFF);
+ pms_i2c_write(0xB8,0x0A,0x00);
+ pms_i2c_write(0xB8,0x0B,0x10);
+ pms_i2c_write(0xB8,0x10,0x03);
+
+ mvv_write(0x01, 0x00);
+ mvv_write(0x05, 0xA0);
+ mvv_write(0x08, 0x25);
+ mvv_write(0x09, 0x00);
+ mvv_write(0x0A, 0x20|MVVMEMORYWIDTH);
+
+ mvv_write(0x10, 0x02);
+ mvv_write(0x1E, 0x0C);
+ mvv_write(0x1F, 0x03);
+ mvv_write(0x26, 0x06);
+
+ mvv_write(0x2B, 0x00);
+ mvv_write(0x2C, 0x20);
+ mvv_write(0x2D, 0x00);
+ mvv_write(0x2F, 0x70);
+ mvv_write(0x32, 0x00);
+ mvv_write(0x33, MVVMEMORYWIDTH);
+ mvv_write(0x34, 0x00);
+ mvv_write(0x35, 0x00);
+ mvv_write(0x3A, 0x80);
+ mvv_write(0x3B, 0x10);
+ mvv_write(0x20, 0x00);
+ mvv_write(0x21, 0x00);
+ mvv_write(0x30, 0x22);
+ return 0;
+}
+
+/*
+ * Initialization and module stuff
+ */
+
+static int __init init_pms_cards(void)
+{
+ printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.02\n");
+
+ data_port = io_port +1;
+
+ if(init_mediavision())
+ {
+ printk(KERN_INFO "Board not found.\n");
+ return -ENODEV;
+ }
+ memcpy(&pms_device, &pms_template, sizeof(pms_template));
+ mutex_init(&pms_device.lock);
+ pms_device.height=240;
+ pms_device.width=320;
+ pms_swsense(75);
+ pms_resolution(320,240);
+ return video_register_device((struct video_device *)&pms_device, VFL_TYPE_GRABBER, video_nr);
+}
+
+module_param(io_port, int, 0);
+module_param(mem_base, int, 0);
+module_param(video_nr, int, 0);
+MODULE_LICENSE("GPL");
+
+
+static void __exit shutdown_mediavision(void)
+{
+ release_region(io_port,3);
+ release_region(0x9A01, 1);
+}
+
+static void __exit cleanup_pms_module(void)
+{
+ shutdown_mediavision();
+ video_unregister_device((struct video_device *)&pms_device);
+ iounmap(mem);
+}
+
+module_init(init_pms_cards);
+module_exit(cleanup_pms_module);
+
diff --git a/linux/drivers/media/video/saa5246a.c b/linux/drivers/media/video/saa5246a.c
new file mode 100644
index 000000000..bfc970087
--- /dev/null
+++ b/linux/drivers/media/video/saa5246a.c
@@ -0,0 +1,837 @@
+/*
+ * Driver for the SAA5246A or SAA5281 Teletext (=Videotext) decoder chips from
+ * Philips.
+ *
+ * Only capturing of Teletext pages is tested. The videotext chips also have a
+ * TV output but my hardware doesn't use it. For this reason this driver does
+ * not support changing any TV display settings.
+ *
+ * Copyright (C) 2004 Michael Geng <linux@MichaelGeng.de>
+ *
+ * Derived from
+ *
+ * saa5249 driver
+ * Copyright (C) 1998 Richard Guenther
+ * <richard.guenther@student.uni-tuebingen.de>
+ *
+ * with changes by
+ * Alan Cox <Alan.Cox@linux.org>
+ *
+ * and
+ *
+ * vtx.c
+ * Copyright (C) 1994-97 Martin Buck <martin-2.buck@student.uni-ulm.de>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/videotext.h>
+#include "compat.h"
+#include <linux/videodev.h>
+#include <linux/mutex.h>
+
+#include "saa5246a.h"
+
+MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>");
+MODULE_DESCRIPTION("Philips SAA5246A, SAA5281 Teletext decoder driver");
+MODULE_LICENSE("GPL");
+
+struct saa5246a_device
+{
+ u8 pgbuf[NUM_DAUS][VTX_VIRTUALSIZE];
+ int is_searching[NUM_DAUS];
+ struct i2c_client *client;
+ struct mutex lock;
+};
+
+static struct video_device saa_template; /* Declared near bottom */
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { I2C_ADDRESS, I2C_CLIENT_END };
+I2C_CLIENT_INSMOD;
+
+static struct i2c_client client_template;
+
+static int saa5246a_attach(struct i2c_adapter *adap, int addr, int kind)
+{
+ int pgbuf;
+ int err;
+ struct i2c_client *client;
+ struct video_device *vd;
+ struct saa5246a_device *t;
+
+ printk(KERN_INFO "saa5246a: teletext chip found.\n");
+ client=kmalloc(sizeof(*client), GFP_KERNEL);
+ if(client==NULL)
+ return -ENOMEM;
+ client_template.adapter = adap;
+ client_template.addr = addr;
+ memcpy(client, &client_template, sizeof(*client));
+ t = kzalloc(sizeof(*t), GFP_KERNEL);
+ if(t==NULL)
+ {
+ kfree(client);
+ return -ENOMEM;
+ }
+ strlcpy(client->name, IF_NAME, I2C_NAME_SIZE);
+ mutex_init(&t->lock);
+
+ /*
+ * Now create a video4linux device
+ */
+
+ vd = video_device_alloc();
+ if(vd==NULL)
+ {
+ kfree(t);
+ kfree(client);
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(client, vd);
+ memcpy(vd, &saa_template, sizeof(*vd));
+
+ for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++)
+ {
+ memset(t->pgbuf[pgbuf], ' ', sizeof(t->pgbuf[0]));
+ t->is_searching[pgbuf] = FALSE;
+ }
+ vd->priv=t;
+
+
+ /*
+ * Register it
+ */
+
+ if((err=video_register_device(vd, VFL_TYPE_VTX,-1))<0)
+ {
+ kfree(t);
+ kfree(client);
+ video_device_release(vd);
+ return err;
+ }
+ t->client = client;
+ i2c_attach_client(client);
+ return 0;
+}
+
+/*
+ * We do most of the hard work when we become a device on the i2c.
+ */
+static int saa5246a_probe(struct i2c_adapter *adap)
+{
+ if (adap->class & I2C_CLASS_TV_ANALOG)
+ return i2c_probe(adap, &addr_data, saa5246a_attach);
+ return 0;
+}
+
+static int saa5246a_detach(struct i2c_client *client)
+{
+ struct video_device *vd = i2c_get_clientdata(client);
+ i2c_detach_client(client);
+ video_unregister_device(vd);
+ kfree(vd->priv);
+ kfree(client);
+ return 0;
+}
+
+/*
+ * I2C interfaces
+ */
+
+static struct i2c_driver i2c_driver_videotext =
+{
+ .driver = {
+ .name = IF_NAME, /* name */
+ },
+ .id = I2C_DRIVERID_SAA5249, /* in i2c.h */
+ .attach_adapter = saa5246a_probe,
+ .detach_client = saa5246a_detach,
+};
+
+static struct i2c_client client_template = {
+ .driver = &i2c_driver_videotext,
+ .name = "(unset)",
+};
+
+static int i2c_sendbuf(struct saa5246a_device *t, int reg, int count, u8 *data)
+{
+ char buf[64];
+
+ buf[0] = reg;
+ memcpy(buf+1, data, count);
+
+ if(i2c_master_send(t->client, buf, count+1)==count+1)
+ return 0;
+ return -1;
+}
+
+static int i2c_senddata(struct saa5246a_device *t, ...)
+{
+ unsigned char buf[64];
+ int v;
+ int ct=0;
+ va_list argp;
+ va_start(argp,t);
+
+ while((v=va_arg(argp,int))!=-1)
+ buf[ct++]=v;
+ return i2c_sendbuf(t, buf[0], ct-1, buf+1);
+}
+
+/* Get count number of bytes from I²C-device at address adr, store them in buf.
+ * Start & stop handshaking is done by this routine, ack will be sent after the
+ * last byte to inhibit further sending of data. If uaccess is TRUE, data is
+ * written to user-space with put_user. Returns -1 if I²C-device didn't send
+ * acknowledge, 0 otherwise
+ */
+static int i2c_getdata(struct saa5246a_device *t, int count, u8 *buf)
+{
+ if(i2c_master_recv(t->client, buf, count)!=count)
+ return -1;
+ return 0;
+}
+
+/* When a page is found then the not FOUND bit in one of the status registers
+ * of the SAA5264A chip is cleared. Unfortunately this bit is not set
+ * automatically when a new page is requested. Instead this function must be
+ * called after a page has been requested.
+ *
+ * Return value: 0 if successful
+ */
+static int saa5246a_clear_found_bit(struct saa5246a_device *t,
+ unsigned char dau_no)
+{
+ unsigned char row_25_column_8;
+
+ if (i2c_senddata(t, SAA5246A_REGISTER_R8,
+
+ dau_no |
+ R8_DO_NOT_CLEAR_MEMORY,
+
+ R9_CURSER_ROW_25,
+
+ R10_CURSER_COLUMN_8,
+
+ COMMAND_END) ||
+ i2c_getdata(t, 1, &row_25_column_8))
+ {
+ return -EIO;
+ }
+ row_25_column_8 |= ROW25_COLUMN8_PAGE_NOT_FOUND;
+ if (i2c_senddata(t, SAA5246A_REGISTER_R8,
+
+ dau_no |
+ R8_DO_NOT_CLEAR_MEMORY,
+
+ R9_CURSER_ROW_25,
+
+ R10_CURSER_COLUMN_8,
+
+ row_25_column_8,
+
+ COMMAND_END))
+ {
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/* Requests one videotext page as described in req. The fields of req are
+ * checked and an error is returned if something is invalid.
+ *
+ * Return value: 0 if successful
+ */
+static int saa5246a_request_page(struct saa5246a_device *t,
+ vtx_pagereq_t *req)
+{
+ if (req->pagemask < 0 || req->pagemask >= PGMASK_MAX)
+ return -EINVAL;
+ if (req->pagemask & PGMASK_PAGE)
+ if (req->page < 0 || req->page > PAGE_MAX)
+ return -EINVAL;
+ if (req->pagemask & PGMASK_HOUR)
+ if (req->hour < 0 || req->hour > HOUR_MAX)
+ return -EINVAL;
+ if (req->pagemask & PGMASK_MINUTE)
+ if (req->minute < 0 || req->minute > MINUTE_MAX)
+ return -EINVAL;
+ if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
+ return -EINVAL;
+
+ if (i2c_senddata(t, SAA5246A_REGISTER_R2,
+
+ R2_IN_R3_SELECT_PAGE_HUNDREDS |
+ req->pgbuf << 4 |
+ R2_BANK_0 |
+ R2_HAMMING_CHECK_OFF,
+
+ HUNDREDS_OF_PAGE(req->page) |
+ R3_HOLD_PAGE |
+ (req->pagemask & PG_HUND ?
+ R3_PAGE_HUNDREDS_DO_CARE :
+ R3_PAGE_HUNDREDS_DO_NOT_CARE),
+
+ TENS_OF_PAGE(req->page) |
+ (req->pagemask & PG_TEN ?
+ R3_PAGE_TENS_DO_CARE :
+ R3_PAGE_TENS_DO_NOT_CARE),
+
+ UNITS_OF_PAGE(req->page) |
+ (req->pagemask & PG_UNIT ?
+ R3_PAGE_UNITS_DO_CARE :
+ R3_PAGE_UNITS_DO_NOT_CARE),
+
+ TENS_OF_HOUR(req->hour) |
+ (req->pagemask & HR_TEN ?
+ R3_HOURS_TENS_DO_CARE :
+ R3_HOURS_TENS_DO_NOT_CARE),
+
+ UNITS_OF_HOUR(req->hour) |
+ (req->pagemask & HR_UNIT ?
+ R3_HOURS_UNITS_DO_CARE :
+ R3_HOURS_UNITS_DO_NOT_CARE),
+
+ TENS_OF_MINUTE(req->minute) |
+ (req->pagemask & MIN_TEN ?
+ R3_MINUTES_TENS_DO_CARE :
+ R3_MINUTES_TENS_DO_NOT_CARE),
+
+ UNITS_OF_MINUTE(req->minute) |
+ (req->pagemask & MIN_UNIT ?
+ R3_MINUTES_UNITS_DO_CARE :
+ R3_MINUTES_UNITS_DO_NOT_CARE),
+
+ COMMAND_END) || i2c_senddata(t, SAA5246A_REGISTER_R2,
+
+ R2_IN_R3_SELECT_PAGE_HUNDREDS |
+ req->pgbuf << 4 |
+ R2_BANK_0 |
+ R2_HAMMING_CHECK_OFF,
+
+ HUNDREDS_OF_PAGE(req->page) |
+ R3_UPDATE_PAGE |
+ (req->pagemask & PG_HUND ?
+ R3_PAGE_HUNDREDS_DO_CARE :
+ R3_PAGE_HUNDREDS_DO_NOT_CARE),
+
+ COMMAND_END))
+ {
+ return -EIO;
+ }
+
+ t->is_searching[req->pgbuf] = TRUE;
+ return 0;
+}
+
+/* This routine decodes the page number from the infobits contained in line 25.
+ *
+ * Parameters:
+ * infobits: must be bits 0 to 9 of column 25
+ *
+ * Return value: page number coded in hexadecimal, i. e. page 123 is coded 0x123
+ */
+static inline int saa5246a_extract_pagenum_from_infobits(
+ unsigned char infobits[10])
+{
+ int page_hundreds, page_tens, page_units;
+
+ page_units = infobits[0] & ROW25_COLUMN0_PAGE_UNITS;
+ page_tens = infobits[1] & ROW25_COLUMN1_PAGE_TENS;
+ page_hundreds = infobits[8] & ROW25_COLUMN8_PAGE_HUNDREDS;
+
+ /* page 0x.. means page 8.. */
+ if (page_hundreds == 0)
+ page_hundreds = 8;
+
+ return((page_hundreds << 8) | (page_tens << 4) | page_units);
+}
+
+/* Decodes the hour from the infobits contained in line 25.
+ *
+ * Parameters:
+ * infobits: must be bits 0 to 9 of column 25
+ *
+ * Return: hour coded in hexadecimal, i. e. 12h is coded 0x12
+ */
+static inline int saa5246a_extract_hour_from_infobits(
+ unsigned char infobits[10])
+{
+ int hour_tens, hour_units;
+
+ hour_units = infobits[4] & ROW25_COLUMN4_HOUR_UNITS;
+ hour_tens = infobits[5] & ROW25_COLUMN5_HOUR_TENS;
+
+ return((hour_tens << 4) | hour_units);
+}
+
+/* Decodes the minutes from the infobits contained in line 25.
+ *
+ * Parameters:
+ * infobits: must be bits 0 to 9 of column 25
+ *
+ * Return: minutes coded in hexadecimal, i. e. 10min is coded 0x10
+ */
+static inline int saa5246a_extract_minutes_from_infobits(
+ unsigned char infobits[10])
+{
+ int minutes_tens, minutes_units;
+
+ minutes_units = infobits[2] & ROW25_COLUMN2_MINUTES_UNITS;
+ minutes_tens = infobits[3] & ROW25_COLUMN3_MINUTES_TENS;
+
+ return((minutes_tens << 4) | minutes_units);
+}
+
+/* Reads the status bits contained in the first 10 columns of the first line
+ * and extracts the information into info.
+ *
+ * Return value: 0 if successful
+ */
+static inline int saa5246a_get_status(struct saa5246a_device *t,
+ vtx_pageinfo_t *info, unsigned char dau_no)
+{
+ unsigned char infobits[10];
+ int column;
+
+ if (dau_no >= NUM_DAUS)
+ return -EINVAL;
+
+ if (i2c_senddata(t, SAA5246A_REGISTER_R8,
+
+ dau_no |
+ R8_DO_NOT_CLEAR_MEMORY,
+
+ R9_CURSER_ROW_25,
+
+ R10_CURSER_COLUMN_0,
+
+ COMMAND_END) ||
+ i2c_getdata(t, 10, infobits))
+ {
+ return -EIO;
+ }
+
+ info->pagenum = saa5246a_extract_pagenum_from_infobits(infobits);
+ info->hour = saa5246a_extract_hour_from_infobits(infobits);
+ info->minute = saa5246a_extract_minutes_from_infobits(infobits);
+ info->charset = ((infobits[7] & ROW25_COLUMN7_CHARACTER_SET) >> 1);
+ info->delete = !!(infobits[3] & ROW25_COLUMN3_DELETE_PAGE);
+ info->headline = !!(infobits[5] & ROW25_COLUMN5_INSERT_HEADLINE);
+ info->subtitle = !!(infobits[5] & ROW25_COLUMN5_INSERT_SUBTITLE);
+ info->supp_header = !!(infobits[6] & ROW25_COLUMN6_SUPPRESS_HEADER);
+ info->update = !!(infobits[6] & ROW25_COLUMN6_UPDATE_PAGE);
+ info->inter_seq = !!(infobits[6] & ROW25_COLUMN6_INTERRUPTED_SEQUENCE);
+ info->dis_disp = !!(infobits[6] & ROW25_COLUMN6_SUPPRESS_DISPLAY);
+ info->serial = !!(infobits[7] & ROW25_COLUMN7_SERIAL_MODE);
+ info->notfound = !!(infobits[8] & ROW25_COLUMN8_PAGE_NOT_FOUND);
+ info->pblf = !!(infobits[9] & ROW25_COLUMN9_PAGE_BEING_LOOKED_FOR);
+ info->hamming = 0;
+ for (column = 0; column <= 7; column++) {
+ if (infobits[column] & ROW25_COLUMN0_TO_7_HAMMING_ERROR) {
+ info->hamming = 1;
+ break;
+ }
+ }
+ if (!info->hamming && !info->notfound)
+ t->is_searching[dau_no] = FALSE;
+ return 0;
+}
+
+/* Reads 1 videotext page buffer of the SAA5246A.
+ *
+ * req is used both as input and as output. It contains information which part
+ * must be read. The videotext page is copied into req->buffer.
+ *
+ * Return value: 0 if successful
+ */
+static inline int saa5246a_get_page(struct saa5246a_device *t,
+ vtx_pagereq_t *req)
+{
+ int start, end, size;
+ char *buf;
+ int err;
+
+ if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS ||
+ req->start < 0 || req->start > req->end || req->end >= VTX_PAGESIZE)
+ return -EINVAL;
+
+ buf = kmalloc(VTX_PAGESIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* Read "normal" part of page */
+ err = -EIO;
+
+ end = min(req->end, VTX_PAGESIZE - 1);
+ if (i2c_senddata(t, SAA5246A_REGISTER_R8,
+ req->pgbuf | R8_DO_NOT_CLEAR_MEMORY,
+ ROW(req->start), COLUMN(req->start), COMMAND_END))
+ goto out;
+ if (i2c_getdata(t, end - req->start + 1, buf))
+ goto out;
+ err = -EFAULT;
+ if (copy_to_user(req->buffer, buf, end - req->start + 1))
+ goto out;
+
+ /* Always get the time from buffer 4, since this stupid SAA5246A only
+ * updates the currently displayed buffer...
+ */
+ if (REQ_CONTAINS_TIME(req)) {
+ start = max(req->start, POS_TIME_START);
+ end = min(req->end, POS_TIME_END);
+ size = end - start + 1;
+ err = -EINVAL;
+ if (size < 0)
+ goto out;
+ err = -EIO;
+ if (i2c_senddata(t, SAA5246A_REGISTER_R8,
+ R8_ACTIVE_CHAPTER_4 | R8_DO_NOT_CLEAR_MEMORY,
+ R9_CURSER_ROW_0, start, COMMAND_END))
+ goto out;
+ if (i2c_getdata(t, size, buf))
+ goto out;
+ err = -EFAULT;
+ if (copy_to_user(req->buffer + start - req->start, buf, size))
+ goto out;
+ }
+ /* Insert the header from buffer 4 only, if acquisition circuit is still searching for a page */
+ if (REQ_CONTAINS_HEADER(req) && t->is_searching[req->pgbuf]) {
+ start = max(req->start, POS_HEADER_START);
+ end = min(req->end, POS_HEADER_END);
+ size = end - start + 1;
+ err = -EINVAL;
+ if (size < 0)
+ goto out;
+ err = -EIO;
+ if (i2c_senddata(t, SAA5246A_REGISTER_R8,
+ R8_ACTIVE_CHAPTER_4 | R8_DO_NOT_CLEAR_MEMORY,
+ R9_CURSER_ROW_0, start, COMMAND_END))
+ goto out;
+ if (i2c_getdata(t, end - start + 1, buf))
+ goto out;
+ err = -EFAULT;
+ if (copy_to_user(req->buffer + start - req->start, buf, size))
+ goto out;
+ }
+ err = 0;
+out:
+ kfree(buf);
+ return err;
+}
+
+/* Stops the acquisition circuit given in dau_no. The page buffer associated
+ * with this acquisition circuit will no more be updated. The other daus are
+ * not affected.
+ *
+ * Return value: 0 if successful
+ */
+static inline int saa5246a_stop_dau(struct saa5246a_device *t,
+ unsigned char dau_no)
+{
+ if (dau_no >= NUM_DAUS)
+ return -EINVAL;
+ if (i2c_senddata(t, SAA5246A_REGISTER_R2,
+
+ R2_IN_R3_SELECT_PAGE_HUNDREDS |
+ dau_no << 4 |
+ R2_BANK_0 |
+ R2_HAMMING_CHECK_OFF,
+
+ R3_PAGE_HUNDREDS_0 |
+ R3_HOLD_PAGE |
+ R3_PAGE_HUNDREDS_DO_NOT_CARE,
+
+ COMMAND_END))
+ {
+ return -EIO;
+ }
+ t->is_searching[dau_no] = FALSE;
+ return 0;
+}
+
+/* Handles ioctls defined in videotext.h
+ *
+ * Returns 0 if successful
+ */
+static int do_saa5246a_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *vd = video_devdata(file);
+ struct saa5246a_device *t=vd->priv;
+ switch(cmd)
+ {
+ case VTXIOCGETINFO:
+ {
+ vtx_info_t *info = arg;
+
+ info->version_major = MAJOR_VERSION;
+ info->version_minor = MINOR_VERSION;
+ info->numpages = NUM_DAUS;
+ return 0;
+ }
+
+ case VTXIOCCLRPAGE:
+ {
+ vtx_pagereq_t *req = arg;
+
+ if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
+ return -EINVAL;
+ memset(t->pgbuf[req->pgbuf], ' ', sizeof(t->pgbuf[0]));
+ return 0;
+ }
+
+ case VTXIOCCLRFOUND:
+ {
+ vtx_pagereq_t *req = arg;
+
+ if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
+ return -EINVAL;
+ return(saa5246a_clear_found_bit(t, req->pgbuf));
+ }
+
+ case VTXIOCPAGEREQ:
+ {
+ vtx_pagereq_t *req = arg;
+
+ return(saa5246a_request_page(t, req));
+ }
+
+ case VTXIOCGETSTAT:
+ {
+ vtx_pagereq_t *req = arg;
+ vtx_pageinfo_t info;
+ int rval;
+
+ if ((rval = saa5246a_get_status(t, &info, req->pgbuf)))
+ return rval;
+ if(copy_to_user(req->buffer, &info,
+ sizeof(vtx_pageinfo_t)))
+ return -EFAULT;
+ return 0;
+ }
+
+ case VTXIOCGETPAGE:
+ {
+ vtx_pagereq_t *req = arg;
+
+ return(saa5246a_get_page(t, req));
+ }
+
+ case VTXIOCSTOPDAU:
+ {
+ vtx_pagereq_t *req = arg;
+
+ return(saa5246a_stop_dau(t, req->pgbuf));
+ }
+
+ case VTXIOCPUTPAGE:
+ case VTXIOCSETDISP:
+ case VTXIOCPUTSTAT:
+ return 0;
+
+ case VTXIOCCLRCACHE:
+ {
+ return 0;
+ }
+
+ case VTXIOCSETVIRT:
+ {
+ /* I do not know what "virtual mode" means */
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+/*
+ * Translates old vtx IOCTLs to new ones
+ *
+ * This keeps new kernel versions compatible with old userspace programs.
+ */
+static inline unsigned int vtx_fix_command(unsigned int cmd)
+{
+ switch (cmd) {
+ case VTXIOCGETINFO_OLD:
+ cmd = VTXIOCGETINFO;
+ break;
+ case VTXIOCCLRPAGE_OLD:
+ cmd = VTXIOCCLRPAGE;
+ break;
+ case VTXIOCCLRFOUND_OLD:
+ cmd = VTXIOCCLRFOUND;
+ break;
+ case VTXIOCPAGEREQ_OLD:
+ cmd = VTXIOCPAGEREQ;
+ break;
+ case VTXIOCGETSTAT_OLD:
+ cmd = VTXIOCGETSTAT;
+ break;
+ case VTXIOCGETPAGE_OLD:
+ cmd = VTXIOCGETPAGE;
+ break;
+ case VTXIOCSTOPDAU_OLD:
+ cmd = VTXIOCSTOPDAU;
+ break;
+ case VTXIOCPUTPAGE_OLD:
+ cmd = VTXIOCPUTPAGE;
+ break;
+ case VTXIOCSETDISP_OLD:
+ cmd = VTXIOCSETDISP;
+ break;
+ case VTXIOCPUTSTAT_OLD:
+ cmd = VTXIOCPUTSTAT;
+ break;
+ case VTXIOCCLRCACHE_OLD:
+ cmd = VTXIOCCLRCACHE;
+ break;
+ case VTXIOCSETVIRT_OLD:
+ cmd = VTXIOCSETVIRT;
+ break;
+ }
+ return cmd;
+}
+
+/*
+ * Handle the locking
+ */
+static int saa5246a_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vd = video_devdata(file);
+ struct saa5246a_device *t = vd->priv;
+ int err;
+
+ cmd = vtx_fix_command(cmd);
+ mutex_lock(&t->lock);
+ err = video_usercopy(inode, file, cmd, arg, do_saa5246a_ioctl);
+ mutex_unlock(&t->lock);
+ return err;
+}
+
+static int saa5246a_open(struct inode *inode, struct file *file)
+{
+ struct video_device *vd = video_devdata(file);
+ struct saa5246a_device *t = vd->priv;
+ int err;
+
+ err = video_exclusive_open(inode,file);
+ if (err < 0)
+ return err;
+
+ if (t->client==NULL) {
+ err = -ENODEV;
+ goto fail;
+ }
+
+ if (i2c_senddata(t, SAA5246A_REGISTER_R0,
+
+ R0_SELECT_R11 |
+ R0_PLL_TIME_CONSTANT_LONG |
+ R0_ENABLE_nODD_EVEN_OUTPUT |
+ R0_ENABLE_HDR_POLL |
+ R0_DO_NOT_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED |
+ R0_NO_FREE_RUN_PLL |
+ R0_NO_AUTOMATIC_FASTEXT_PROMPT,
+
+ R1_NON_INTERLACED_312_312_LINES |
+ R1_DEW |
+ R1_EXTENDED_PACKET_DISABLE |
+ R1_DAUS_ALL_ON |
+ R1_8_BITS_NO_PARITY |
+ R1_VCS_TO_SCS,
+
+ COMMAND_END) ||
+ i2c_senddata(t, SAA5246A_REGISTER_R4,
+
+ /* We do not care much for the TV display but nevertheless we
+ * need the currently displayed page later because only on that
+ * page the time is updated. */
+ R4_DISPLAY_PAGE_4,
+
+ COMMAND_END))
+ {
+ err = -EIO;
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ video_exclusive_release(inode,file);
+ return err;
+}
+
+static int saa5246a_release(struct inode *inode, struct file *file)
+{
+ struct video_device *vd = video_devdata(file);
+ struct saa5246a_device *t = vd->priv;
+
+ /* Stop all acquisition circuits. */
+ i2c_senddata(t, SAA5246A_REGISTER_R1,
+
+ R1_INTERLACED_312_AND_HALF_312_AND_HALF_LINES |
+ R1_DEW |
+ R1_EXTENDED_PACKET_DISABLE |
+ R1_DAUS_ALL_OFF |
+ R1_8_BITS_NO_PARITY |
+ R1_VCS_TO_SCS,
+
+ COMMAND_END);
+ video_exclusive_release(inode,file);
+ return 0;
+}
+
+static int __init init_saa_5246a (void)
+{
+ printk(KERN_INFO
+ "SAA5246A (or compatible) Teletext decoder driver version %d.%d\n",
+ MAJOR_VERSION, MINOR_VERSION);
+ return i2c_add_driver(&i2c_driver_videotext);
+}
+
+static void __exit cleanup_saa_5246a (void)
+{
+ i2c_del_driver(&i2c_driver_videotext);
+}
+
+module_init(init_saa_5246a);
+module_exit(cleanup_saa_5246a);
+
+static struct file_operations saa_fops = {
+ .owner = THIS_MODULE,
+ .open = saa5246a_open,
+ .release = saa5246a_release,
+ .ioctl = saa5246a_ioctl,
+ .llseek = no_llseek,
+};
+
+static struct video_device saa_template =
+{
+ .owner = THIS_MODULE,
+ .name = IF_NAME,
+ .type = VID_TYPE_TELETEXT,
+ .hardware = VID_HARDWARE_SAA5249,
+ .fops = &saa_fops,
+ .release = video_device_release,
+ .minor = -1,
+};
diff --git a/linux/drivers/media/video/saa5246a.h b/linux/drivers/media/video/saa5246a.h
new file mode 100644
index 000000000..7b9111230
--- /dev/null
+++ b/linux/drivers/media/video/saa5246a.h
@@ -0,0 +1,364 @@
+/*
+ Driver for the SAA5246A or SAA5281 Teletext (=Videotext) decoder chips from
+ Philips.
+
+ Copyright (C) 2004 Michael Geng (linux@MichaelGeng.de)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+#ifndef __SAA5246A_H__
+#define __SAA5246A_H__
+
+#define MAJOR_VERSION 1 /* driver major version number */
+#define MINOR_VERSION 8 /* driver minor version number */
+
+#define IF_NAME "SAA5246A"
+
+#define I2C_ADDRESS 17
+
+/* Number of DAUs = number of pages that can be searched at the same time. */
+#define NUM_DAUS 4
+
+#define NUM_ROWS_PER_PAGE 40
+
+/* first column is 0 (not 1) */
+#define POS_TIME_START 32
+#define POS_TIME_END 39
+
+#define POS_HEADER_START 7
+#define POS_HEADER_END 31
+
+/* Returns TRUE if the part of the videotext page described with req contains
+ (at least parts of) the time field */
+#define REQ_CONTAINS_TIME(p_req) \
+ ((p_req)->start <= POS_TIME_END && \
+ (p_req)->end >= POS_TIME_START)
+
+/* Returns TRUE if the part of the videotext page described with req contains
+ (at least parts of) the page header */
+#define REQ_CONTAINS_HEADER(p_req) \
+ ((p_req)->start <= POS_HEADER_END && \
+ (p_req)->end >= POS_HEADER_START)
+
+#ifndef FALSE
+#define FALSE 0
+#define TRUE 1
+#endif
+
+/*****************************************************************************/
+/* Mode register numbers of the SAA5246A */
+/*****************************************************************************/
+#define SAA5246A_REGISTER_R0 0
+#define SAA5246A_REGISTER_R1 1
+#define SAA5246A_REGISTER_R2 2
+#define SAA5246A_REGISTER_R3 3
+#define SAA5246A_REGISTER_R4 4
+#define SAA5246A_REGISTER_R5 5
+#define SAA5246A_REGISTER_R6 6
+#define SAA5246A_REGISTER_R7 7
+#define SAA5246A_REGISTER_R8 8
+#define SAA5246A_REGISTER_R9 9
+#define SAA5246A_REGISTER_R10 10
+#define SAA5246A_REGISTER_R11 11
+#define SAA5246A_REGISTER_R11B 11
+
+/* SAA5246A mode registers often autoincrement to the next register.
+ Therefore we use variable argument lists. The following macro indicates
+ the end of a command list. */
+#define COMMAND_END (- 1)
+
+/*****************************************************************************/
+/* Contents of the mode registers of the SAA5246A */
+/*****************************************************************************/
+/* Register R0 (Advanced Control) */
+#define R0_SELECT_R11 0x00
+#define R0_SELECT_R11B 0x01
+
+#define R0_PLL_TIME_CONSTANT_LONG 0x00
+#define R0_PLL_TIME_CONSTANT_SHORT 0x02
+
+#define R0_ENABLE_nODD_EVEN_OUTPUT 0x00
+#define R0_DISABLE_nODD_EVEN_OUTPUT 0x04
+
+#define R0_ENABLE_HDR_POLL 0x00
+#define R0_DISABLE_HDR_POLL 0x10
+
+#define R0_DO_NOT_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED 0x00
+#define R0_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED 0x20
+
+#define R0_NO_FREE_RUN_PLL 0x00
+#define R0_FREE_RUN_PLL 0x40
+
+#define R0_NO_AUTOMATIC_FASTEXT_PROMPT 0x00
+#define R0_AUTOMATIC_FASTEXT_PROMPT 0x80
+
+/* Register R1 (Mode) */
+#define R1_INTERLACED_312_AND_HALF_312_AND_HALF_LINES 0x00
+#define R1_NON_INTERLACED_312_313_LINES 0x01
+#define R1_NON_INTERLACED_312_312_LINES 0x02
+#define R1_FFB_LEADING_EDGE_IN_FIRST_BROAD_PULSE 0x03
+#define R1_FFB_LEADING_EDGE_IN_SECOND_BROAD_PULSE 0x07
+
+#define R1_DEW 0x00
+#define R1_FULL_FIELD 0x08
+
+#define R1_EXTENDED_PACKET_DISABLE 0x00
+#define R1_EXTENDED_PACKET_ENABLE 0x10
+
+#define R1_DAUS_ALL_ON 0x00
+#define R1_DAUS_ALL_OFF 0x20
+
+#define R1_7_BITS_PLUS_PARITY 0x00
+#define R1_8_BITS_NO_PARITY 0x40
+
+#define R1_VCS_TO_SCS 0x00
+#define R1_NO_VCS_TO_SCS 0x80
+
+/* Register R2 (Page request address) */
+#define R2_IN_R3_SELECT_PAGE_HUNDREDS 0x00
+#define R2_IN_R3_SELECT_PAGE_TENS 0x01
+#define R2_IN_R3_SELECT_PAGE_UNITS 0x02
+#define R2_IN_R3_SELECT_HOURS_TENS 0x03
+#define R2_IN_R3_SELECT_HOURS_UNITS 0x04
+#define R2_IN_R3_SELECT_MINUTES_TENS 0x05
+#define R2_IN_R3_SELECT_MINUTES_UNITS 0x06
+
+#define R2_DAU_0 0x00
+#define R2_DAU_1 0x10
+#define R2_DAU_2 0x20
+#define R2_DAU_3 0x30
+
+#define R2_BANK_0 0x00
+#define R2_BANK 1 0x40
+
+#define R2_HAMMING_CHECK_ON 0x80
+#define R2_HAMMING_CHECK_OFF 0x00
+
+/* Register R3 (Page request data) */
+#define R3_PAGE_HUNDREDS_0 0x00
+#define R3_PAGE_HUNDREDS_1 0x01
+#define R3_PAGE_HUNDREDS_2 0x02
+#define R3_PAGE_HUNDREDS_3 0x03
+#define R3_PAGE_HUNDREDS_4 0x04
+#define R3_PAGE_HUNDREDS_5 0x05
+#define R3_PAGE_HUNDREDS_6 0x06
+#define R3_PAGE_HUNDREDS_7 0x07
+
+#define R3_HOLD_PAGE 0x00
+#define R3_UPDATE_PAGE 0x08
+
+#define R3_PAGE_HUNDREDS_DO_NOT_CARE 0x00
+#define R3_PAGE_HUNDREDS_DO_CARE 0x10
+
+#define R3_PAGE_TENS_DO_NOT_CARE 0x00
+#define R3_PAGE_TENS_DO_CARE 0x10
+
+#define R3_PAGE_UNITS_DO_NOT_CARE 0x00
+#define R3_PAGE_UNITS_DO_CARE 0x10
+
+#define R3_HOURS_TENS_DO_NOT_CARE 0x00
+#define R3_HOURS_TENS_DO_CARE 0x10
+
+#define R3_HOURS_UNITS_DO_NOT_CARE 0x00
+#define R3_HOURS_UNITS_DO_CARE 0x10
+
+#define R3_MINUTES_TENS_DO_NOT_CARE 0x00
+#define R3_MINUTES_TENS_DO_CARE 0x10
+
+#define R3_MINUTES_UNITS_DO_NOT_CARE 0x00
+#define R3_MINUTES_UNITS_DO_CARE 0x10
+
+/* Register R4 (Display chapter) */
+#define R4_DISPLAY_PAGE_0 0x00
+#define R4_DISPLAY_PAGE_1 0x01
+#define R4_DISPLAY_PAGE_2 0x02
+#define R4_DISPLAY_PAGE_3 0x03
+#define R4_DISPLAY_PAGE_4 0x04
+#define R4_DISPLAY_PAGE_5 0x05
+#define R4_DISPLAY_PAGE_6 0x06
+#define R4_DISPLAY_PAGE_7 0x07
+
+/* Register R5 (Normal display control) */
+#define R5_PICTURE_INSIDE_BOXING_OFF 0x00
+#define R5_PICTURE_INSIDE_BOXING_ON 0x01
+
+#define R5_PICTURE_OUTSIDE_BOXING_OFF 0x00
+#define R5_PICTURE_OUTSIDE_BOXING_ON 0x02
+
+#define R5_TEXT_INSIDE_BOXING_OFF 0x00
+#define R5_TEXT_INSIDE_BOXING_ON 0x04
+
+#define R5_TEXT_OUTSIDE_BOXING_OFF 0x00
+#define R5_TEXT_OUTSIDE_BOXING_ON 0x08
+
+#define R5_CONTRAST_REDUCTION_INSIDE_BOXING_OFF 0x00
+#define R5_CONTRAST_REDUCTION_INSIDE_BOXING_ON 0x10
+
+#define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00
+#define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON 0x20
+
+#define R5_BACKGROUND_COLOR_INSIDE_BOXING_OFF 0x00
+#define R5_BACKGROUND_COLOR_INSIDE_BOXING_ON 0x40
+
+#define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF 0x00
+#define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_ON 0x80
+
+/* Register R6 (Newsflash display) */
+#define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_OFF 0x00
+#define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_ON 0x01
+
+#define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_OFF 0x00
+#define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_ON 0x02
+
+#define R6_NEWSFLASH_TEXT_INSIDE_BOXING_OFF 0x00
+#define R6_NEWSFLASH_TEXT_INSIDE_BOXING_ON 0x04
+
+#define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_OFF 0x00
+#define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_ON 0x08
+
+#define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_OFF 0x00
+#define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_ON 0x10
+
+#define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00
+#define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON 0x20
+
+#define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_OFF 0x00
+#define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_ON 0x40
+
+#define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF 0x00
+#define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_ON 0x80
+
+/* Register R7 (Display mode) */
+#define R7_BOX_OFF_ROW_0 0x00
+#define R7_BOX_ON_ROW_0 0x01
+
+#define R7_BOX_OFF_ROW_1_TO_23 0x00
+#define R7_BOX_ON_ROW_1_TO_23 0x02
+
+#define R7_BOX_OFF_ROW_24 0x00
+#define R7_BOX_ON_ROW_24 0x04
+
+#define R7_SINGLE_HEIGHT 0x00
+#define R7_DOUBLE_HEIGHT 0x08
+
+#define R7_TOP_HALF 0x00
+#define R7_BOTTOM_HALF 0x10
+
+#define R7_REVEAL_OFF 0x00
+#define R7_REVEAL_ON 0x20
+
+#define R7_CURSER_OFF 0x00
+#define R7_CURSER_ON 0x40
+
+#define R7_STATUS_BOTTOM 0x00
+#define R7_STATUS_TOP 0x80
+
+/* Register R8 (Active chapter) */
+#define R8_ACTIVE_CHAPTER_0 0x00
+#define R8_ACTIVE_CHAPTER_1 0x01
+#define R8_ACTIVE_CHAPTER_2 0x02
+#define R8_ACTIVE_CHAPTER_3 0x03
+#define R8_ACTIVE_CHAPTER_4 0x04
+#define R8_ACTIVE_CHAPTER_5 0x05
+#define R8_ACTIVE_CHAPTER_6 0x06
+#define R8_ACTIVE_CHAPTER_7 0x07
+
+#define R8_CLEAR_MEMORY 0x08
+#define R8_DO_NOT_CLEAR_MEMORY 0x00
+
+/* Register R9 (Curser row) */
+#define R9_CURSER_ROW_0 0x00
+#define R9_CURSER_ROW_1 0x01
+#define R9_CURSER_ROW_2 0x02
+#define R9_CURSER_ROW_25 0x19
+
+/* Register R10 (Curser column) */
+#define R10_CURSER_COLUMN_0 0x00
+#define R10_CURSER_COLUMN_6 0x06
+#define R10_CURSER_COLUMN_8 0x08
+
+/*****************************************************************************/
+/* Row 25 control data in column 0 to 9 */
+/*****************************************************************************/
+#define ROW25_COLUMN0_PAGE_UNITS 0x0F
+
+#define ROW25_COLUMN1_PAGE_TENS 0x0F
+
+#define ROW25_COLUMN2_MINUTES_UNITS 0x0F
+
+#define ROW25_COLUMN3_MINUTES_TENS 0x07
+#define ROW25_COLUMN3_DELETE_PAGE 0x08
+
+#define ROW25_COLUMN4_HOUR_UNITS 0x0F
+
+#define ROW25_COLUMN5_HOUR_TENS 0x03
+#define ROW25_COLUMN5_INSERT_HEADLINE 0x04
+#define ROW25_COLUMN5_INSERT_SUBTITLE 0x08
+
+#define ROW25_COLUMN6_SUPPRESS_HEADER 0x01
+#define ROW25_COLUMN6_UPDATE_PAGE 0x02
+#define ROW25_COLUMN6_INTERRUPTED_SEQUENCE 0x04
+#define ROW25_COLUMN6_SUPPRESS_DISPLAY 0x08
+
+#define ROW25_COLUMN7_SERIAL_MODE 0x01
+#define ROW25_COLUMN7_CHARACTER_SET 0x0E
+
+#define ROW25_COLUMN8_PAGE_HUNDREDS 0x07
+#define ROW25_COLUMN8_PAGE_NOT_FOUND 0x10
+
+#define ROW25_COLUMN9_PAGE_BEING_LOOKED_FOR 0x20
+
+#define ROW25_COLUMN0_TO_7_HAMMING_ERROR 0x10
+
+/*****************************************************************************/
+/* Helper macros for extracting page, hour and minute digits */
+/*****************************************************************************/
+/* BYTE_POS 0 is at row 0, column 0,
+ BYTE_POS 1 is at row 0, column 1,
+ BYTE_POS 40 is at row 1, column 0, (with NUM_ROWS_PER_PAGE = 40)
+ BYTE_POS 41 is at row 1, column 1, (with NUM_ROWS_PER_PAGE = 40),
+ ... */
+#define ROW(BYTE_POS) (BYTE_POS / NUM_ROWS_PER_PAGE)
+#define COLUMN(BYTE_POS) (BYTE_POS % NUM_ROWS_PER_PAGE)
+
+/*****************************************************************************/
+/* Helper macros for extracting page, hour and minute digits */
+/*****************************************************************************/
+/* Macros for extracting hundreds, tens and units of a page number which
+ must be in the range 0 ... 0x799.
+ Note that page is coded in hexadecimal, i.e. 0x123 means page 123.
+ page 0x.. means page 8.. */
+#define HUNDREDS_OF_PAGE(page) (((page) / 0x100) & 0x7)
+#define TENS_OF_PAGE(page) (((page) / 0x10) & 0xF)
+#define UNITS_OF_PAGE(page) ((page) & 0xF)
+
+/* Macros for extracting tens and units of a hour information which
+ must be in the range 0 ... 0x24.
+ Note that hour is coded in hexadecimal, i.e. 0x12 means 12 hours */
+#define TENS_OF_HOUR(hour) ((hour) / 0x10)
+#define UNITS_OF_HOUR(hour) ((hour) & 0xF)
+
+/* Macros for extracting tens and units of a minute information which
+ must be in the range 0 ... 0x59.
+ Note that minute is coded in hexadecimal, i.e. 0x12 means 12 minutes */
+#define TENS_OF_MINUTE(minute) ((minute) / 0x10)
+#define UNITS_OF_MINUTE(minute) ((minute) & 0xF)
+
+#define HOUR_MAX 0x23
+#define MINUTE_MAX 0x59
+#define PAGE_MAX 0x8FF
+
+#endif /* __SAA5246A_H__ */
diff --git a/linux/drivers/media/video/saa5249.c b/linux/drivers/media/video/saa5249.c
new file mode 100644
index 000000000..42c2052e4
--- /dev/null
+++ b/linux/drivers/media/video/saa5249.c
@@ -0,0 +1,720 @@
+/*
+ * Modified in order to keep it compatible both with new and old videotext IOCTLs by
+ * Michael Geng <linux@MichaelGeng.de>
+ *
+ * Cleaned up to use existing videodev interface and allow the idea
+ * of multiple teletext decoders on the video4linux iface. Changed i2c
+ * to cover addressing clashes on device busses. It's also rebuilt so
+ * you can add arbitary multiple teletext devices to Linux video4linux
+ * now (well 32 anyway).
+ *
+ * Alan Cox <Alan.Cox@linux.org>
+ *
+ * The original driver was heavily modified to match the i2c interface
+ * It was truncated to use the WinTV boards, too.
+ *
+ * Copyright (c) 1998 Richard Guenther <richard.guenther@student.uni-tuebingen.de>
+ *
+ * $Id: saa5249.c,v 1.1 1998/03/30 22:23:23 alan Exp $
+ *
+ * Derived From
+ *
+ * vtx.c:
+ * This is a loadable character-device-driver for videotext-interfaces
+ * (aka teletext). Please check the Makefile/README for a list of supported
+ * interfaces.
+ *
+ * Copyright (c) 1994-97 Martin Buck <martin-2.buck@student.uni-ulm.de>
+ *
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <stdarg.h>
+#include <linux/i2c.h>
+#include <linux/videotext.h>
+#include "compat.h"
+#include <linux/videodev.h>
+#include <linux/mutex.h>
+
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#define VTX_VER_MAJ 1
+#define VTX_VER_MIN 8
+
+
+
+#define NUM_DAUS 4
+#define NUM_BUFS 8
+#define IF_NAME "SAA5249"
+
+static const int disp_modes[8][3] =
+{
+ { 0x46, 0x03, 0x03 }, /* DISPOFF */
+ { 0x46, 0xcc, 0xcc }, /* DISPNORM */
+ { 0x44, 0x0f, 0x0f }, /* DISPTRANS */
+ { 0x46, 0xcc, 0x46 }, /* DISPINS */
+ { 0x44, 0x03, 0x03 }, /* DISPOFF, interlaced */
+ { 0x44, 0xcc, 0xcc }, /* DISPNORM, interlaced */
+ { 0x44, 0x0f, 0x0f }, /* DISPTRANS, interlaced */
+ { 0x44, 0xcc, 0x46 } /* DISPINS, interlaced */
+};
+
+
+
+#define PAGE_WAIT (300*HZ/1000) /* Time between requesting page and */
+ /* checking status bits */
+#define PGBUF_EXPIRE (15*HZ) /* Time to wait before retransmitting */
+ /* page regardless of infobits */
+typedef struct {
+ u8 pgbuf[VTX_VIRTUALSIZE]; /* Page-buffer */
+ u8 laststat[10]; /* Last value of infobits for DAU */
+ u8 sregs[7]; /* Page-request registers */
+ unsigned long expire; /* Time when page will be expired */
+ unsigned clrfound : 1; /* VTXIOCCLRFOUND has been called */
+ unsigned stopped : 1; /* VTXIOCSTOPDAU has been called */
+} vdau_t;
+
+struct saa5249_device
+{
+ vdau_t vdau[NUM_DAUS]; /* Data for virtual DAUs (the 5249 only has one */
+ /* real DAU, so we have to simulate some more) */
+ int vtx_use_count;
+ int is_searching[NUM_DAUS];
+ int disp_mode;
+ int virtual_mode;
+ struct i2c_client *client;
+ struct mutex lock;
+};
+
+
+#define CCTWR 34 /* I²C write/read-address of vtx-chip */
+#define CCTRD 35
+#define NOACK_REPEAT 10 /* Retry access this many times on failure */
+#define CLEAR_DELAY (HZ/20) /* Time required to clear a page */
+#define READY_TIMEOUT (30*HZ/1000) /* Time to wait for ready signal of I²C-bus interface */
+#define INIT_DELAY 500 /* Time in usec to wait at initialization of CEA interface */
+#define START_DELAY 10 /* Time in usec to wait before starting write-cycle (CEA) */
+
+#define VTX_DEV_MINOR 0
+
+/* General defines and debugging support */
+
+#ifndef FALSE
+#define FALSE 0
+#define TRUE 1
+#endif
+
+#define RESCHED do { cond_resched(); } while(0)
+
+static struct video_device saa_template; /* Declared near bottom */
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = {34>>1,I2C_CLIENT_END};
+I2C_CLIENT_INSMOD;
+
+static struct i2c_client client_template;
+
+static int saa5249_attach(struct i2c_adapter *adap, int addr, int kind)
+{
+ int pgbuf;
+ int err;
+ struct i2c_client *client;
+ struct video_device *vd;
+ struct saa5249_device *t;
+
+ printk(KERN_INFO "saa5249: teletext chip found.\n");
+ client=kmalloc(sizeof(*client), GFP_KERNEL);
+ if(client==NULL)
+ return -ENOMEM;
+ client_template.adapter = adap;
+ client_template.addr = addr;
+ memcpy(client, &client_template, sizeof(*client));
+ t = kzalloc(sizeof(*t), GFP_KERNEL);
+ if(t==NULL)
+ {
+ kfree(client);
+ return -ENOMEM;
+ }
+ strlcpy(client->name, IF_NAME, I2C_NAME_SIZE);
+ mutex_init(&t->lock);
+
+ /*
+ * Now create a video4linux device
+ */
+
+ vd = kmalloc(sizeof(struct video_device), GFP_KERNEL);
+ if(vd==NULL)
+ {
+ kfree(t);
+ kfree(client);
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(client, vd);
+ memcpy(vd, &saa_template, sizeof(*vd));
+
+ for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++)
+ {
+ memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf));
+ memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs));
+ memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat));
+ t->vdau[pgbuf].expire = 0;
+ t->vdau[pgbuf].clrfound = TRUE;
+ t->vdau[pgbuf].stopped = TRUE;
+ t->is_searching[pgbuf] = FALSE;
+ }
+ vd->priv=t;
+
+
+ /*
+ * Register it
+ */
+
+ if((err=video_register_device(vd, VFL_TYPE_VTX,-1))<0)
+ {
+ kfree(t);
+ kfree(vd);
+ kfree(client);
+ return err;
+ }
+ t->client = client;
+ i2c_attach_client(client);
+ return 0;
+}
+
+/*
+ * We do most of the hard work when we become a device on the i2c.
+ */
+
+static int saa5249_probe(struct i2c_adapter *adap)
+{
+ if (adap->class & I2C_CLASS_TV_ANALOG)
+ return i2c_probe(adap, &addr_data, saa5249_attach);
+ return 0;
+}
+
+static int saa5249_detach(struct i2c_client *client)
+{
+ struct video_device *vd = i2c_get_clientdata(client);
+ i2c_detach_client(client);
+ video_unregister_device(vd);
+ kfree(vd->priv);
+ kfree(vd);
+ kfree(client);
+ return 0;
+}
+
+/* new I2C driver support */
+
+static struct i2c_driver i2c_driver_videotext =
+{
+ .driver = {
+ .name = IF_NAME, /* name */
+ },
+ .id = I2C_DRIVERID_SAA5249, /* in i2c.h */
+ .attach_adapter = saa5249_probe,
+ .detach_client = saa5249_detach,
+};
+
+static struct i2c_client client_template = {
+ .driver = &i2c_driver_videotext,
+ .name = "(unset)",
+};
+
+/*
+ * Wait the given number of jiffies (10ms). This calls the scheduler, so the actual
+ * delay may be longer.
+ */
+
+static void jdelay(unsigned long delay)
+{
+ sigset_t oldblocked = current->blocked;
+
+ spin_lock_irq(&current->sighand->siglock);
+ sigfillset(&current->blocked);
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+ msleep_interruptible(jiffies_to_msecs(delay));
+
+ spin_lock_irq(&current->sighand->siglock);
+ current->blocked = oldblocked;
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+}
+
+
+/*
+ * I2C interfaces
+ */
+
+static int i2c_sendbuf(struct saa5249_device *t, int reg, int count, u8 *data)
+{
+ char buf[64];
+
+ buf[0] = reg;
+ memcpy(buf+1, data, count);
+
+ if(i2c_master_send(t->client, buf, count+1)==count+1)
+ return 0;
+ return -1;
+}
+
+static int i2c_senddata(struct saa5249_device *t, ...)
+{
+ unsigned char buf[64];
+ int v;
+ int ct=0;
+ va_list argp;
+ va_start(argp,t);
+
+ while((v=va_arg(argp,int))!=-1)
+ buf[ct++]=v;
+ return i2c_sendbuf(t, buf[0], ct-1, buf+1);
+}
+
+/* Get count number of bytes from I²C-device at address adr, store them in buf. Start & stop
+ * handshaking is done by this routine, ack will be sent after the last byte to inhibit further
+ * sending of data. If uaccess is TRUE, data is written to user-space with put_user.
+ * Returns -1 if I²C-device didn't send acknowledge, 0 otherwise
+ */
+
+static int i2c_getdata(struct saa5249_device *t, int count, u8 *buf)
+{
+ if(i2c_master_recv(t->client, buf, count)!=count)
+ return -1;
+ return 0;
+}
+
+
+/*
+ * Standard character-device-driver functions
+ */
+
+static int do_saa5249_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ static int virtual_mode = FALSE;
+ struct video_device *vd = video_devdata(file);
+ struct saa5249_device *t=vd->priv;
+
+ switch(cmd)
+ {
+ case VTXIOCGETINFO:
+ {
+ vtx_info_t *info = arg;
+ info->version_major = VTX_VER_MAJ;
+ info->version_minor = VTX_VER_MIN;
+ info->numpages = NUM_DAUS;
+ /*info->cct_type = CCT_TYPE;*/
+ return 0;
+ }
+
+ case VTXIOCCLRPAGE:
+ {
+ vtx_pagereq_t *req = arg;
+
+ if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
+ return -EINVAL;
+ memset(t->vdau[req->pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf));
+ t->vdau[req->pgbuf].clrfound = TRUE;
+ return 0;
+ }
+
+ case VTXIOCCLRFOUND:
+ {
+ vtx_pagereq_t *req = arg;
+
+ if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
+ return -EINVAL;
+ t->vdau[req->pgbuf].clrfound = TRUE;
+ return 0;
+ }
+
+ case VTXIOCPAGEREQ:
+ {
+ vtx_pagereq_t *req = arg;
+ if (!(req->pagemask & PGMASK_PAGE))
+ req->page = 0;
+ if (!(req->pagemask & PGMASK_HOUR))
+ req->hour = 0;
+ if (!(req->pagemask & PGMASK_MINUTE))
+ req->minute = 0;
+ if (req->page < 0 || req->page > 0x8ff) /* 7FF ?? */
+ return -EINVAL;
+ req->page &= 0x7ff;
+ if (req->hour < 0 || req->hour > 0x3f || req->minute < 0 || req->minute > 0x7f ||
+ req->pagemask < 0 || req->pagemask >= PGMASK_MAX || req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
+ return -EINVAL;
+ t->vdau[req->pgbuf].sregs[0] = (req->pagemask & PG_HUND ? 0x10 : 0) | (req->page / 0x100);
+ t->vdau[req->pgbuf].sregs[1] = (req->pagemask & PG_TEN ? 0x10 : 0) | ((req->page / 0x10) & 0xf);
+ t->vdau[req->pgbuf].sregs[2] = (req->pagemask & PG_UNIT ? 0x10 : 0) | (req->page & 0xf);
+ t->vdau[req->pgbuf].sregs[3] = (req->pagemask & HR_TEN ? 0x10 : 0) | (req->hour / 0x10);
+ t->vdau[req->pgbuf].sregs[4] = (req->pagemask & HR_UNIT ? 0x10 : 0) | (req->hour & 0xf);
+ t->vdau[req->pgbuf].sregs[5] = (req->pagemask & MIN_TEN ? 0x10 : 0) | (req->minute / 0x10);
+ t->vdau[req->pgbuf].sregs[6] = (req->pagemask & MIN_UNIT ? 0x10 : 0) | (req->minute & 0xf);
+ t->vdau[req->pgbuf].stopped = FALSE;
+ t->vdau[req->pgbuf].clrfound = TRUE;
+ t->is_searching[req->pgbuf] = TRUE;
+ return 0;
+ }
+
+ case VTXIOCGETSTAT:
+ {
+ vtx_pagereq_t *req = arg;
+ u8 infobits[10];
+ vtx_pageinfo_t info;
+ int a;
+
+ if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
+ return -EINVAL;
+ if (!t->vdau[req->pgbuf].stopped)
+ {
+ if (i2c_senddata(t, 2, 0, -1) ||
+ i2c_sendbuf(t, 3, sizeof(t->vdau[0].sregs), t->vdau[req->pgbuf].sregs) ||
+ i2c_senddata(t, 8, 0, 25, 0, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', -1) ||
+ i2c_senddata(t, 2, 0, t->vdau[req->pgbuf].sregs[0] | 8, -1) ||
+ i2c_senddata(t, 8, 0, 25, 0, -1))
+ return -EIO;
+ jdelay(PAGE_WAIT);
+ if (i2c_getdata(t, 10, infobits))
+ return -EIO;
+
+ if (!(infobits[8] & 0x10) && !(infobits[7] & 0xf0) && /* check FOUND-bit */
+ (memcmp(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)) ||
+ time_after_eq(jiffies, t->vdau[req->pgbuf].expire)))
+ { /* check if new page arrived */
+ if (i2c_senddata(t, 8, 0, 0, 0, -1) ||
+ i2c_getdata(t, VTX_PAGESIZE, t->vdau[req->pgbuf].pgbuf))
+ return -EIO;
+ t->vdau[req->pgbuf].expire = jiffies + PGBUF_EXPIRE;
+ memset(t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE, ' ', VTX_VIRTUALSIZE - VTX_PAGESIZE);
+ if (t->virtual_mode)
+ {
+ /* Packet X/24 */
+ if (i2c_senddata(t, 8, 0, 0x20, 0, -1) ||
+ i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 20 * 40))
+ return -EIO;
+ /* Packet X/27/0 */
+ if (i2c_senddata(t, 8, 0, 0x21, 0, -1) ||
+ i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 16 * 40))
+ return -EIO;
+ /* Packet 8/30/0...8/30/15
+ * FIXME: AFAIK, the 5249 does hamming-decoding for some bytes in packet 8/30,
+ * so we should undo this here.
+ */
+ if (i2c_senddata(t, 8, 0, 0x22, 0, -1) ||
+ i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 23 * 40))
+ return -EIO;
+ }
+ t->vdau[req->pgbuf].clrfound = FALSE;
+ memcpy(t->vdau[req->pgbuf].laststat, infobits, sizeof(infobits));
+ }
+ else
+ {
+ memcpy(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits));
+ }
+ }
+ else
+ {
+ memcpy(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits));
+ }
+
+ info.pagenum = ((infobits[8] << 8) & 0x700) | ((infobits[1] << 4) & 0xf0) | (infobits[0] & 0x0f);
+ if (info.pagenum < 0x100)
+ info.pagenum += 0x800;
+ info.hour = ((infobits[5] << 4) & 0x30) | (infobits[4] & 0x0f);
+ info.minute = ((infobits[3] << 4) & 0x70) | (infobits[2] & 0x0f);
+ info.charset = ((infobits[7] >> 1) & 7);
+ info.delete = !!(infobits[3] & 8);
+ info.headline = !!(infobits[5] & 4);
+ info.subtitle = !!(infobits[5] & 8);
+ info.supp_header = !!(infobits[6] & 1);
+ info.update = !!(infobits[6] & 2);
+ info.inter_seq = !!(infobits[6] & 4);
+ info.dis_disp = !!(infobits[6] & 8);
+ info.serial = !!(infobits[7] & 1);
+ info.notfound = !!(infobits[8] & 0x10);
+ info.pblf = !!(infobits[9] & 0x20);
+ info.hamming = 0;
+ for (a = 0; a <= 7; a++)
+ {
+ if (infobits[a] & 0xf0)
+ {
+ info.hamming = 1;
+ break;
+ }
+ }
+ if (t->vdau[req->pgbuf].clrfound)
+ info.notfound = 1;
+ if(copy_to_user(req->buffer, &info, sizeof(vtx_pageinfo_t)))
+ return -EFAULT;
+ if (!info.hamming && !info.notfound)
+ {
+ t->is_searching[req->pgbuf] = FALSE;
+ }
+ return 0;
+ }
+
+ case VTXIOCGETPAGE:
+ {
+ vtx_pagereq_t *req = arg;
+ int start, end;
+
+ if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS || req->start < 0 ||
+ req->start > req->end || req->end >= (virtual_mode ? VTX_VIRTUALSIZE : VTX_PAGESIZE))
+ return -EINVAL;
+ if(copy_to_user(req->buffer, &t->vdau[req->pgbuf].pgbuf[req->start], req->end - req->start + 1))
+ return -EFAULT;
+
+ /*
+ * Always read the time directly from SAA5249
+ */
+
+ if (req->start <= 39 && req->end >= 32)
+ {
+ int len;
+ char buf[16];
+ start = max(req->start, 32);
+ end = min(req->end, 39);
+ len=end-start+1;
+ if (i2c_senddata(t, 8, 0, 0, start, -1) ||
+ i2c_getdata(t, len, buf))
+ return -EIO;
+ if(copy_to_user(req->buffer+start-req->start, buf, len))
+ return -EFAULT;
+ }
+ /* Insert the current header if DAU is still searching for a page */
+ if (req->start <= 31 && req->end >= 7 && t->is_searching[req->pgbuf])
+ {
+ char buf[32];
+ int len;
+ start = max(req->start, 7);
+ end = min(req->end, 31);
+ len=end-start+1;
+ if (i2c_senddata(t, 8, 0, 0, start, -1) ||
+ i2c_getdata(t, len, buf))
+ return -EIO;
+ if(copy_to_user(req->buffer+start-req->start, buf, len))
+ return -EFAULT;
+ }
+ return 0;
+ }
+
+ case VTXIOCSTOPDAU:
+ {
+ vtx_pagereq_t *req = arg;
+
+ if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS)
+ return -EINVAL;
+ t->vdau[req->pgbuf].stopped = TRUE;
+ t->is_searching[req->pgbuf] = FALSE;
+ return 0;
+ }
+
+ case VTXIOCPUTPAGE:
+ case VTXIOCSETDISP:
+ case VTXIOCPUTSTAT:
+ return 0;
+
+ case VTXIOCCLRCACHE:
+ {
+ if (i2c_senddata(t, 0, NUM_DAUS, 0, 8, -1) || i2c_senddata(t, 11,
+ ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
+ ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', -1))
+ return -EIO;
+ if (i2c_senddata(t, 3, 0x20, -1))
+ return -EIO;
+ jdelay(10 * CLEAR_DELAY); /* I have no idea how long we have to wait here */
+ return 0;
+ }
+
+ case VTXIOCSETVIRT:
+ {
+ /* The SAA5249 has virtual-row reception turned on always */
+ t->virtual_mode = (int)(long)arg;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+/*
+ * Translates old vtx IOCTLs to new ones
+ *
+ * This keeps new kernel versions compatible with old userspace programs.
+ */
+static inline unsigned int vtx_fix_command(unsigned int cmd)
+{
+ switch (cmd) {
+ case VTXIOCGETINFO_OLD:
+ cmd = VTXIOCGETINFO;
+ break;
+ case VTXIOCCLRPAGE_OLD:
+ cmd = VTXIOCCLRPAGE;
+ break;
+ case VTXIOCCLRFOUND_OLD:
+ cmd = VTXIOCCLRFOUND;
+ break;
+ case VTXIOCPAGEREQ_OLD:
+ cmd = VTXIOCPAGEREQ;
+ break;
+ case VTXIOCGETSTAT_OLD:
+ cmd = VTXIOCGETSTAT;
+ break;
+ case VTXIOCGETPAGE_OLD:
+ cmd = VTXIOCGETPAGE;
+ break;
+ case VTXIOCSTOPDAU_OLD:
+ cmd = VTXIOCSTOPDAU;
+ break;
+ case VTXIOCPUTPAGE_OLD:
+ cmd = VTXIOCPUTPAGE;
+ break;
+ case VTXIOCSETDISP_OLD:
+ cmd = VTXIOCSETDISP;
+ break;
+ case VTXIOCPUTSTAT_OLD:
+ cmd = VTXIOCPUTSTAT;
+ break;
+ case VTXIOCCLRCACHE_OLD:
+ cmd = VTXIOCCLRCACHE;
+ break;
+ case VTXIOCSETVIRT_OLD:
+ cmd = VTXIOCSETVIRT;
+ break;
+ }
+ return cmd;
+}
+
+/*
+ * Handle the locking
+ */
+
+static int saa5249_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vd = video_devdata(file);
+ struct saa5249_device *t=vd->priv;
+ int err;
+
+ cmd = vtx_fix_command(cmd);
+ mutex_lock(&t->lock);
+ err = video_usercopy(inode,file,cmd,arg,do_saa5249_ioctl);
+ mutex_unlock(&t->lock);
+ return err;
+}
+
+static int saa5249_open(struct inode *inode, struct file *file)
+{
+ struct video_device *vd = video_devdata(file);
+ struct saa5249_device *t=vd->priv;
+ int err,pgbuf;
+
+ err = video_exclusive_open(inode,file);
+ if (err < 0)
+ return err;
+
+ if (t->client==NULL) {
+ err = -ENODEV;
+ goto fail;
+ }
+
+ if (i2c_senddata(t, 0, 0, -1) || /* Select R11 */
+ /* Turn off parity checks (we do this ourselves) */
+ i2c_senddata(t, 1, disp_modes[t->disp_mode][0], 0, -1) ||
+ /* Display TV-picture, no virtual rows */
+ i2c_senddata(t, 4, NUM_DAUS, disp_modes[t->disp_mode][1], disp_modes[t->disp_mode][2], 7, -1)) /* Set display to page 4 */
+
+ {
+ err = -EIO;
+ goto fail;
+ }
+
+ for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++)
+ {
+ memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf));
+ memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs));
+ memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat));
+ t->vdau[pgbuf].expire = 0;
+ t->vdau[pgbuf].clrfound = TRUE;
+ t->vdau[pgbuf].stopped = TRUE;
+ t->is_searching[pgbuf] = FALSE;
+ }
+ t->virtual_mode=FALSE;
+ return 0;
+
+ fail:
+ video_exclusive_release(inode,file);
+ return err;
+}
+
+
+
+static int saa5249_release(struct inode *inode, struct file *file)
+{
+ struct video_device *vd = video_devdata(file);
+ struct saa5249_device *t=vd->priv;
+ i2c_senddata(t, 1, 0x20, -1); /* Turn off CCT */
+ i2c_senddata(t, 5, 3, 3, -1); /* Turn off TV-display */
+ video_exclusive_release(inode,file);
+ return 0;
+}
+
+static int __init init_saa_5249 (void)
+{
+ printk(KERN_INFO "SAA5249 driver (" IF_NAME " interface) for VideoText version %d.%d\n",
+ VTX_VER_MAJ, VTX_VER_MIN);
+ return i2c_add_driver(&i2c_driver_videotext);
+}
+
+static void __exit cleanup_saa_5249 (void)
+{
+ i2c_del_driver(&i2c_driver_videotext);
+}
+
+module_init(init_saa_5249);
+module_exit(cleanup_saa_5249);
+
+static struct file_operations saa_fops = {
+ .owner = THIS_MODULE,
+ .open = saa5249_open,
+ .release = saa5249_release,
+ .ioctl = saa5249_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+
+static struct video_device saa_template =
+{
+ .owner = THIS_MODULE,
+ .name = IF_NAME,
+ .type = VID_TYPE_TELETEXT, /*| VID_TYPE_TUNER ?? */
+ .hardware = VID_HARDWARE_SAA5249,
+ .fops = &saa_fops,
+};
+
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/video/saa7110.c b/linux/drivers/media/video/saa7110.c
new file mode 100644
index 000000000..b1797cbb6
--- /dev/null
+++ b/linux/drivers/media/video/saa7110.c
@@ -0,0 +1,604 @@
+/*
+ * saa7110 - Philips SAA7110(A) video decoder driver
+ *
+ * Copyright (C) 1998 Pauline Middelink <middelin@polyware.nl>
+ *
+ * Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net>
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ * - some corrections for Pinnacle Systems Inc. DC10plus card.
+ *
+ * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * - moved over to linux>=2.4.x i2c protocol (1/1/2003)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+MODULE_DESCRIPTION("Philips SAA7110 video decoder driver");
+MODULE_AUTHOR("Pauline Middelink");
+MODULE_LICENSE("GPL");
+
+#include <linux/i2c.h>
+
+#define I2C_NAME(s) (s)->name
+
+#include "compat.h"
+#include <linux/videodev.h>
+#include <linux/video_decoder.h>
+
+static int debug = 0;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+#define SAA7110_MAX_INPUT 9 /* 6 CVBS, 3 SVHS */
+#define SAA7110_MAX_OUTPUT 0 /* its a decoder only */
+
+#define I2C_SAA7110 0x9C /* or 0x9E */
+
+#define SAA7110_NR_REG 0x35
+
+struct saa7110 {
+ u8 reg[SAA7110_NR_REG];
+
+ int norm;
+ int input;
+ int enable;
+ int bright;
+ int contrast;
+ int hue;
+ int sat;
+
+ wait_queue_head_t wq;
+};
+
+/* ----------------------------------------------------------------------- */
+/* I2C support functions */
+/* ----------------------------------------------------------------------- */
+
+static int
+saa7110_write (struct i2c_client *client,
+ u8 reg,
+ u8 value)
+{
+ struct saa7110 *decoder = i2c_get_clientdata(client);
+
+ decoder->reg[reg] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int
+saa7110_write_block (struct i2c_client *client,
+ const u8 *data,
+ unsigned int len)
+{
+ int ret = -1;
+ u8 reg = *data; /* first register to write to */
+
+ /* Sanity check */
+ if (reg + (len - 1) > SAA7110_NR_REG)
+ return ret;
+
+ /* the saa7110 has an autoincrement function, use it if
+ * the adapter understands raw I2C */
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ struct saa7110 *decoder = i2c_get_clientdata(client);
+
+ ret = i2c_master_send(client, data, len);
+
+ /* Cache the written data */
+ memcpy(decoder->reg + reg, data + 1, len - 1);
+ } else {
+ for (++data, --len; len; len--) {
+ if ((ret = saa7110_write(client, reg++,
+ *data++)) < 0)
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static inline int
+saa7110_read (struct i2c_client *client)
+{
+ return i2c_smbus_read_byte(client);
+}
+
+/* ----------------------------------------------------------------------- */
+/* SAA7110 functions */
+/* ----------------------------------------------------------------------- */
+
+#define FRESP_06H_COMPST 0x03 //0x13
+#define FRESP_06H_SVIDEO 0x83 //0xC0
+
+
+static int
+saa7110_selmux (struct i2c_client *client,
+ int chan)
+{
+ static const unsigned char modes[9][8] = {
+ /* mode 0 */
+ {FRESP_06H_COMPST, 0xD9, 0x17, 0x40, 0x03,
+ 0x44, 0x75, 0x16},
+ /* mode 1 */
+ {FRESP_06H_COMPST, 0xD8, 0x17, 0x40, 0x03,
+ 0x44, 0x75, 0x16},
+ /* mode 2 */
+ {FRESP_06H_COMPST, 0xBA, 0x07, 0x91, 0x03,
+ 0x60, 0xB5, 0x05},
+ /* mode 3 */
+ {FRESP_06H_COMPST, 0xB8, 0x07, 0x91, 0x03,
+ 0x60, 0xB5, 0x05},
+ /* mode 4 */
+ {FRESP_06H_COMPST, 0x7C, 0x07, 0xD2, 0x83,
+ 0x60, 0xB5, 0x03},
+ /* mode 5 */
+ {FRESP_06H_COMPST, 0x78, 0x07, 0xD2, 0x83,
+ 0x60, 0xB5, 0x03},
+ /* mode 6 */
+ {FRESP_06H_SVIDEO, 0x59, 0x17, 0x42, 0xA3,
+ 0x44, 0x75, 0x12},
+ /* mode 7 */
+ {FRESP_06H_SVIDEO, 0x9A, 0x17, 0xB1, 0x13,
+ 0x60, 0xB5, 0x14},
+ /* mode 8 */
+ {FRESP_06H_SVIDEO, 0x3C, 0x27, 0xC1, 0x23,
+ 0x44, 0x75, 0x21}
+ };
+ struct saa7110 *decoder = i2c_get_clientdata(client);
+ const unsigned char *ptr = modes[chan];
+
+ saa7110_write(client, 0x06, ptr[0]); /* Luminance control */
+ saa7110_write(client, 0x20, ptr[1]); /* Analog Control #1 */
+ saa7110_write(client, 0x21, ptr[2]); /* Analog Control #2 */
+ saa7110_write(client, 0x22, ptr[3]); /* Mixer Control #1 */
+ saa7110_write(client, 0x2C, ptr[4]); /* Mixer Control #2 */
+ saa7110_write(client, 0x30, ptr[5]); /* ADCs gain control */
+ saa7110_write(client, 0x31, ptr[6]); /* Mixer Control #3 */
+ saa7110_write(client, 0x21, ptr[7]); /* Analog Control #2 */
+ decoder->input = chan;
+
+ return 0;
+}
+
+static const unsigned char initseq[1 + SAA7110_NR_REG] = {
+ 0, 0x4C, 0x3C, 0x0D, 0xEF, 0xBD, 0xF2, 0x03, 0x00,
+ /* 0x08 */ 0xF8, 0xF8, 0x60, 0x60, 0x00, 0x86, 0x18, 0x90,
+ /* 0x10 */ 0x00, 0x59, 0x40, 0x46, 0x42, 0x1A, 0xFF, 0xDA,
+ /* 0x18 */ 0xF2, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x20 */ 0xD9, 0x16, 0x40, 0x41, 0x80, 0x41, 0x80, 0x4F,
+ /* 0x28 */ 0xFE, 0x01, 0xCF, 0x0F, 0x03, 0x01, 0x03, 0x0C,
+ /* 0x30 */ 0x44, 0x71, 0x02, 0x8C, 0x02
+};
+
+static int
+determine_norm (struct i2c_client *client)
+{
+ DEFINE_WAIT(wait);
+ struct saa7110 *decoder = i2c_get_clientdata(client);
+ int status;
+
+ /* mode changed, start automatic detection */
+ saa7110_write_block(client, initseq, sizeof(initseq));
+ saa7110_selmux(client, decoder->input);
+ prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/4);
+ finish_wait(&decoder->wq, &wait);
+ status = saa7110_read(client);
+ if (status & 0x40) {
+ dprintk(1, KERN_INFO "%s: status=0x%02x (no signal)\n",
+ I2C_NAME(client), status);
+ return decoder->norm; // no change
+ }
+ if ((status & 3) == 0) {
+ saa7110_write(client, 0x06, 0x83);
+ if (status & 0x20) {
+ dprintk(1,
+ KERN_INFO
+ "%s: status=0x%02x (NTSC/no color)\n",
+ I2C_NAME(client), status);
+ //saa7110_write(client,0x2E,0x81);
+ return VIDEO_MODE_NTSC;
+ }
+ dprintk(1, KERN_INFO "%s: status=0x%02x (PAL/no color)\n",
+ I2C_NAME(client), status);
+ //saa7110_write(client,0x2E,0x9A);
+ return VIDEO_MODE_PAL;
+ }
+ //saa7110_write(client,0x06,0x03);
+ if (status & 0x20) { /* 60Hz */
+ dprintk(1, KERN_INFO "%s: status=0x%02x (NTSC)\n",
+ I2C_NAME(client), status);
+ saa7110_write(client, 0x0D, 0x86);
+ saa7110_write(client, 0x0F, 0x50);
+ saa7110_write(client, 0x11, 0x2C);
+ //saa7110_write(client,0x2E,0x81);
+ return VIDEO_MODE_NTSC;
+ }
+
+ /* 50Hz -> PAL/SECAM */
+ saa7110_write(client, 0x0D, 0x86);
+ saa7110_write(client, 0x0F, 0x10);
+ saa7110_write(client, 0x11, 0x59);
+ //saa7110_write(client,0x2E,0x9A);
+
+ prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ/4);
+ finish_wait(&decoder->wq, &wait);
+
+ status = saa7110_read(client);
+ if ((status & 0x03) == 0x01) {
+ dprintk(1, KERN_INFO "%s: status=0x%02x (SECAM)\n",
+ I2C_NAME(client), status);
+ saa7110_write(client, 0x0D, 0x87);
+ return VIDEO_MODE_SECAM;
+ }
+ dprintk(1, KERN_INFO "%s: status=0x%02x (PAL)\n", I2C_NAME(client),
+ status);
+ return VIDEO_MODE_PAL;
+}
+
+static int
+saa7110_command (struct i2c_client *client,
+ unsigned int cmd,
+ void *arg)
+{
+ struct saa7110 *decoder = i2c_get_clientdata(client);
+ int v;
+
+ switch (cmd) {
+ case 0:
+ //saa7110_write_block(client, initseq, sizeof(initseq));
+ break;
+
+ case DECODER_GET_CAPABILITIES:
+ {
+ struct video_decoder_capability *dc = arg;
+
+ dc->flags =
+ VIDEO_DECODER_PAL | VIDEO_DECODER_NTSC |
+ VIDEO_DECODER_SECAM | VIDEO_DECODER_AUTO;
+ dc->inputs = SAA7110_MAX_INPUT;
+ dc->outputs = SAA7110_MAX_OUTPUT;
+ }
+ break;
+
+ case DECODER_GET_STATUS:
+ {
+ int status;
+ int res = 0;
+
+ status = saa7110_read(client);
+ dprintk(1, KERN_INFO "%s: status=0x%02x norm=%d\n",
+ I2C_NAME(client), status, decoder->norm);
+ if (!(status & 0x40))
+ res |= DECODER_STATUS_GOOD;
+ if (status & 0x03)
+ res |= DECODER_STATUS_COLOR;
+
+ switch (decoder->norm) {
+ case VIDEO_MODE_NTSC:
+ res |= DECODER_STATUS_NTSC;
+ break;
+ case VIDEO_MODE_PAL:
+ res |= DECODER_STATUS_PAL;
+ break;
+ case VIDEO_MODE_SECAM:
+ res |= DECODER_STATUS_SECAM;
+ break;
+ }
+ *(int *) arg = res;
+ }
+ break;
+
+ case DECODER_SET_NORM:
+ v = *(int *) arg;
+ if (decoder->norm != v) {
+ decoder->norm = v;
+ //saa7110_write(client, 0x06, 0x03);
+ switch (v) {
+ case VIDEO_MODE_NTSC:
+ saa7110_write(client, 0x0D, 0x86);
+ saa7110_write(client, 0x0F, 0x50);
+ saa7110_write(client, 0x11, 0x2C);
+ //saa7110_write(client, 0x2E, 0x81);
+ dprintk(1,
+ KERN_INFO "%s: switched to NTSC\n",
+ I2C_NAME(client));
+ break;
+ case VIDEO_MODE_PAL:
+ saa7110_write(client, 0x0D, 0x86);
+ saa7110_write(client, 0x0F, 0x10);
+ saa7110_write(client, 0x11, 0x59);
+ //saa7110_write(client, 0x2E, 0x9A);
+ dprintk(1,
+ KERN_INFO "%s: switched to PAL\n",
+ I2C_NAME(client));
+ break;
+ case VIDEO_MODE_SECAM:
+ saa7110_write(client, 0x0D, 0x87);
+ saa7110_write(client, 0x0F, 0x10);
+ saa7110_write(client, 0x11, 0x59);
+ //saa7110_write(client, 0x2E, 0x9A);
+ dprintk(1,
+ KERN_INFO
+ "%s: switched to SECAM\n",
+ I2C_NAME(client));
+ break;
+ case VIDEO_MODE_AUTO:
+ dprintk(1,
+ KERN_INFO
+ "%s: TV standard detection...\n",
+ I2C_NAME(client));
+ decoder->norm = determine_norm(client);
+ *(int *) arg = decoder->norm;
+ break;
+ default:
+ return -EPERM;
+ }
+ }
+ break;
+
+ case DECODER_SET_INPUT:
+ v = *(int *) arg;
+ if (v < 0 || v > SAA7110_MAX_INPUT) {
+ dprintk(1,
+ KERN_INFO "%s: input=%d not available\n",
+ I2C_NAME(client), v);
+ return -EINVAL;
+ }
+ if (decoder->input != v) {
+ saa7110_selmux(client, v);
+ dprintk(1, KERN_INFO "%s: switched to input=%d\n",
+ I2C_NAME(client), v);
+ }
+ break;
+
+ case DECODER_SET_OUTPUT:
+ v = *(int *) arg;
+ /* not much choice of outputs */
+ if (v != 0)
+ return -EINVAL;
+ break;
+
+ case DECODER_ENABLE_OUTPUT:
+ v = *(int *) arg;
+ if (decoder->enable != v) {
+ decoder->enable = v;
+ saa7110_write(client, 0x0E, v ? 0x18 : 0x80);
+ dprintk(1, KERN_INFO "%s: YUV %s\n", I2C_NAME(client),
+ v ? "on" : "off");
+ }
+ break;
+
+ case DECODER_SET_PICTURE:
+ {
+ struct video_picture *pic = arg;
+
+ if (decoder->bright != pic->brightness) {
+ /* We want 0 to 255 we get 0-65535 */
+ decoder->bright = pic->brightness;
+ saa7110_write(client, 0x19, decoder->bright >> 8);
+ }
+ if (decoder->contrast != pic->contrast) {
+ /* We want 0 to 127 we get 0-65535 */
+ decoder->contrast = pic->contrast;
+ saa7110_write(client, 0x13,
+ decoder->contrast >> 9);
+ }
+ if (decoder->sat != pic->colour) {
+ /* We want 0 to 127 we get 0-65535 */
+ decoder->sat = pic->colour;
+ saa7110_write(client, 0x12, decoder->sat >> 9);
+ }
+ if (decoder->hue != pic->hue) {
+ /* We want -128 to 127 we get 0-65535 */
+ decoder->hue = pic->hue;
+ saa7110_write(client, 0x07,
+ (decoder->hue >> 8) - 128);
+ }
+ }
+ break;
+
+ case DECODER_DUMP:
+ for (v = 0; v < SAA7110_NR_REG; v += 16) {
+ int j;
+ dprintk(1, KERN_DEBUG "%s: %02x:", I2C_NAME(client),
+ v);
+ for (j = 0; j < 16 && v + j < SAA7110_NR_REG; j++)
+ dprintk(1, " %02x", decoder->reg[v + j]);
+ dprintk(1, "\n");
+ }
+ break;
+
+ default:
+ dprintk(1, KERN_INFO "unknown saa7110_command??(%d)\n",
+ cmd);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/*
+ * Generic i2c probe
+ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+ */
+static unsigned short normal_i2c[] = {
+ I2C_SAA7110 >> 1,
+ (I2C_SAA7110 >> 1) + 1,
+ I2C_CLIENT_END
+};
+
+static unsigned short ignore = I2C_CLIENT_END;
+
+static struct i2c_client_address_data addr_data = {
+ .normal_i2c = normal_i2c,
+ .probe = &ignore,
+ .ignore = &ignore,
+};
+
+static struct i2c_driver i2c_driver_saa7110;
+
+static int
+saa7110_detect_client (struct i2c_adapter *adapter,
+ int address,
+ int kind)
+{
+ struct i2c_client *client;
+ struct saa7110 *decoder;
+ int rv;
+
+ dprintk(1,
+ KERN_INFO
+ "saa7110.c: detecting saa7110 client on address 0x%x\n",
+ address << 1);
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality
+ (adapter,
+ I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+ return 0;
+
+ client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (client == 0)
+ return -ENOMEM;
+ client->addr = address;
+ client->adapter = adapter;
+ client->driver = &i2c_driver_saa7110;
+ strlcpy(I2C_NAME(client), "saa7110", sizeof(I2C_NAME(client)));
+
+ decoder = kzalloc(sizeof(struct saa7110), GFP_KERNEL);
+ if (decoder == 0) {
+ kfree(client);
+ return -ENOMEM;
+ }
+ decoder->norm = VIDEO_MODE_PAL;
+ decoder->input = 0;
+ decoder->enable = 1;
+ decoder->bright = 32768;
+ decoder->contrast = 32768;
+ decoder->hue = 32768;
+ decoder->sat = 32768;
+ init_waitqueue_head(&decoder->wq);
+ i2c_set_clientdata(client, decoder);
+
+ rv = i2c_attach_client(client);
+ if (rv) {
+ kfree(client);
+ kfree(decoder);
+ return rv;
+ }
+
+ rv = saa7110_write_block(client, initseq, sizeof(initseq));
+ if (rv < 0)
+ dprintk(1, KERN_ERR "%s_attach: init status %d\n",
+ I2C_NAME(client), rv);
+ else {
+ int ver, status;
+ saa7110_write(client, 0x21, 0x10);
+ saa7110_write(client, 0x0e, 0x18);
+ saa7110_write(client, 0x0D, 0x04);
+ ver = saa7110_read(client);
+ saa7110_write(client, 0x0D, 0x06);
+ //mdelay(150);
+ status = saa7110_read(client);
+ dprintk(1,
+ KERN_INFO
+ "%s_attach: SAA7110A version %x at 0x%02x, status=0x%02x\n",
+ I2C_NAME(client), ver, client->addr << 1, status);
+ saa7110_write(client, 0x0D, 0x86);
+ saa7110_write(client, 0x0F, 0x10);
+ saa7110_write(client, 0x11, 0x59);
+ //saa7110_write(client, 0x2E, 0x9A);
+ }
+
+ //saa7110_selmux(client,0);
+ //determine_norm(client);
+ /* setup and implicit mode 0 select has been performed */
+
+ return 0;
+}
+
+static int
+saa7110_attach_adapter (struct i2c_adapter *adapter)
+{
+ dprintk(1,
+ KERN_INFO
+ "saa7110.c: starting probe for adapter %s (0x%x)\n",
+ I2C_NAME(adapter), adapter->id);
+ return i2c_probe(adapter, &addr_data, &saa7110_detect_client);
+}
+
+static int
+saa7110_detach_client (struct i2c_client *client)
+{
+ struct saa7110 *decoder = i2c_get_clientdata(client);
+ int err;
+
+ err = i2c_detach_client(client);
+ if (err) {
+ return err;
+ }
+
+ kfree(decoder);
+ kfree(client);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_driver i2c_driver_saa7110 = {
+ .driver = {
+ .name = "saa7110",
+ },
+
+ .id = I2C_DRIVERID_SAA7110,
+
+ .attach_adapter = saa7110_attach_adapter,
+ .detach_client = saa7110_detach_client,
+ .command = saa7110_command,
+};
+
+static int __init
+saa7110_init (void)
+{
+ return i2c_add_driver(&i2c_driver_saa7110);
+}
+
+static void __exit
+saa7110_exit (void)
+{
+ i2c_del_driver(&i2c_driver_saa7110);
+}
+
+module_init(saa7110_init);
+module_exit(saa7110_exit);
diff --git a/linux/drivers/media/video/saa7111.c b/linux/drivers/media/video/saa7111.c
new file mode 100644
index 000000000..1fbd343c3
--- /dev/null
+++ b/linux/drivers/media/video/saa7111.c
@@ -0,0 +1,614 @@
+/*
+ * saa7111 - Philips SAA7111A video decoder driver version 0.0.3
+ *
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ *
+ * Slight changes for video timing and attachment output by
+ * Wolfgang Scherr <scherr@net4you.net>
+ *
+ * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * - moved over to linux>=2.4.x i2c protocol (1/1/2003)
+ *
+ * Changes by Michael Hunold <michael@mihu.de>
+ * - implemented DECODER_SET_GPIO, DECODER_INIT, DECODER_SET_VBI_BYPASS
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/signal.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+
+#include "compat.h"
+#include <linux/videodev.h>
+#include <asm/uaccess.h>
+
+MODULE_DESCRIPTION("Philips SAA7111 video decoder driver");
+MODULE_AUTHOR("Dave Perks");
+MODULE_LICENSE("GPL");
+
+#include <linux/i2c.h>
+
+#define I2C_NAME(s) (s)->name
+
+#include <linux/video_decoder.h>
+
+static int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+/* ----------------------------------------------------------------------- */
+
+#define SAA7111_NR_REG 0x18
+
+struct saa7111 {
+ unsigned char reg[SAA7111_NR_REG];
+
+ int norm;
+ int input;
+ int enable;
+ int bright;
+ int contrast;
+ int hue;
+ int sat;
+};
+
+#define I2C_SAA7111 0x48
+
+/* ----------------------------------------------------------------------- */
+
+static inline int
+saa7111_write (struct i2c_client *client,
+ u8 reg,
+ u8 value)
+{
+ struct saa7111 *decoder = i2c_get_clientdata(client);
+
+ decoder->reg[reg] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int
+saa7111_write_block (struct i2c_client *client,
+ const u8 *data,
+ unsigned int len)
+{
+ int ret = -1;
+ u8 reg;
+
+ /* the saa7111 has an autoincrement function, use it if
+ * the adapter understands raw I2C */
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ /* do raw I2C, not smbus compatible */
+ struct saa7111 *decoder = i2c_get_clientdata(client);
+ u8 block_data[32];
+ int block_len;
+
+ while (len >= 2) {
+ block_len = 0;
+ block_data[block_len++] = reg = data[0];
+ do {
+ block_data[block_len++] =
+ decoder->reg[reg++] = data[1];
+ len -= 2;
+ data += 2;
+ } while (len >= 2 && data[0] == reg &&
+ block_len < 32);
+ if ((ret = i2c_master_send(client, block_data,
+ block_len)) < 0)
+ break;
+ }
+ } else {
+ /* do some slow I2C emulation kind of thing */
+ while (len >= 2) {
+ reg = *data++;
+ if ((ret = saa7111_write(client, reg,
+ *data++)) < 0)
+ break;
+ len -= 2;
+ }
+ }
+
+ return ret;
+}
+
+static int
+saa7111_init_decoder (struct i2c_client *client,
+ struct video_decoder_init *init)
+{
+ return saa7111_write_block(client, init->data, init->len);
+}
+
+static inline int
+saa7111_read (struct i2c_client *client,
+ u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const unsigned char saa7111_i2c_init[] = {
+ 0x00, 0x00, /* 00 - ID byte */
+ 0x01, 0x00, /* 01 - reserved */
+
+ /*front end */
+ 0x02, 0xd0, /* 02 - FUSE=3, GUDL=2, MODE=0 */
+ 0x03, 0x23, /* 03 - HLNRS=0, VBSL=1, WPOFF=0,
+ * HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */
+ 0x04, 0x00, /* 04 - GAI1=256 */
+ 0x05, 0x00, /* 05 - GAI2=256 */
+
+ /* decoder */
+ 0x06, 0xf3, /* 06 - HSB at 13(50Hz) / 17(60Hz)
+ * pixels after end of last line */
+ /*0x07, 0x13, * 07 - HSS at 113(50Hz) / 117(60Hz) pixels
+ * after end of last line */
+ 0x07, 0xe8, /* 07 - HSS seems to be needed to
+ * work with NTSC, too */
+ 0x08, 0xc8, /* 08 - AUFD=1, FSEL=1, EXFIL=0,
+ * VTRC=1, HPLL=0, VNOI=0 */
+ 0x09, 0x01, /* 09 - BYPS=0, PREF=0, BPSS=0,
+ * VBLB=0, UPTCV=0, APER=1 */
+ 0x0a, 0x80, /* 0a - BRIG=128 */
+ 0x0b, 0x47, /* 0b - CONT=1.109 */
+ 0x0c, 0x40, /* 0c - SATN=1.0 */
+ 0x0d, 0x00, /* 0d - HUE=0 */
+ 0x0e, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0,
+ * FCTC=0, CHBW=1 */
+ 0x0f, 0x00, /* 0f - reserved */
+ 0x10, 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */
+ 0x11, 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1,
+ * OEYC=1, OEHV=1, VIPB=0, COLO=0 */
+ 0x12, 0x00, /* 12 - output control 2 */
+ 0x13, 0x00, /* 13 - output control 3 */
+ 0x14, 0x00, /* 14 - reserved */
+ 0x15, 0x00, /* 15 - VBI */
+ 0x16, 0x00, /* 16 - VBI */
+ 0x17, 0x00, /* 17 - VBI */
+};
+
+static int
+saa7111_command (struct i2c_client *client,
+ unsigned int cmd,
+ void *arg)
+{
+ struct saa7111 *decoder = i2c_get_clientdata(client);
+
+ switch (cmd) {
+
+ case 0:
+ break;
+ case DECODER_INIT:
+ {
+ struct video_decoder_init *init = arg;
+ if (NULL != init)
+ return saa7111_init_decoder(client, init);
+ else {
+ struct video_decoder_init vdi;
+ vdi.data = saa7111_i2c_init;
+ vdi.len = sizeof(saa7111_i2c_init);
+ return saa7111_init_decoder(client, &vdi);
+ }
+ }
+
+ case DECODER_DUMP:
+ {
+ int i;
+
+ for (i = 0; i < SAA7111_NR_REG; i += 16) {
+ int j;
+
+ printk(KERN_DEBUG "%s: %03x", I2C_NAME(client), i);
+ for (j = 0; j < 16 && i + j < SAA7111_NR_REG; ++j) {
+ printk(" %02x",
+ saa7111_read(client, i + j));
+ }
+ printk("\n");
+ }
+ }
+ break;
+
+ case DECODER_GET_CAPABILITIES:
+ {
+ struct video_decoder_capability *cap = arg;
+
+ cap->flags = VIDEO_DECODER_PAL |
+ VIDEO_DECODER_NTSC |
+ VIDEO_DECODER_SECAM |
+ VIDEO_DECODER_AUTO |
+ VIDEO_DECODER_CCIR;
+ cap->inputs = 8;
+ cap->outputs = 1;
+ }
+ break;
+
+ case DECODER_GET_STATUS:
+ {
+ int *iarg = arg;
+ int status;
+ int res;
+
+ status = saa7111_read(client, 0x1f);
+ dprintk(1, KERN_DEBUG "%s status: 0x%02x\n", I2C_NAME(client),
+ status);
+ res = 0;
+ if ((status & (1 << 6)) == 0) {
+ res |= DECODER_STATUS_GOOD;
+ }
+ switch (decoder->norm) {
+ case VIDEO_MODE_NTSC:
+ res |= DECODER_STATUS_NTSC;
+ break;
+ case VIDEO_MODE_PAL:
+ res |= DECODER_STATUS_PAL;
+ break;
+ case VIDEO_MODE_SECAM:
+ res |= DECODER_STATUS_SECAM;
+ break;
+ default:
+ case VIDEO_MODE_AUTO:
+ if ((status & (1 << 5)) != 0) {
+ res |= DECODER_STATUS_NTSC;
+ } else {
+ res |= DECODER_STATUS_PAL;
+ }
+ break;
+ }
+ if ((status & (1 << 0)) != 0) {
+ res |= DECODER_STATUS_COLOR;
+ }
+ *iarg = res;
+ }
+ break;
+
+ case DECODER_SET_GPIO:
+ {
+ int *iarg = arg;
+ if (0 != *iarg) {
+ saa7111_write(client, 0x11,
+ (decoder->reg[0x11] | 0x80));
+ } else {
+ saa7111_write(client, 0x11,
+ (decoder->reg[0x11] & 0x7f));
+ }
+ break;
+ }
+
+ case DECODER_SET_VBI_BYPASS:
+ {
+ int *iarg = arg;
+ if (0 != *iarg) {
+ saa7111_write(client, 0x13,
+ (decoder->reg[0x13] & 0xf0) | 0x0a);
+ } else {
+ saa7111_write(client, 0x13,
+ (decoder->reg[0x13] & 0xf0));
+ }
+ break;
+ }
+
+ case DECODER_SET_NORM:
+ {
+ int *iarg = arg;
+
+ switch (*iarg) {
+
+ case VIDEO_MODE_NTSC:
+ saa7111_write(client, 0x08,
+ (decoder->reg[0x08] & 0x3f) | 0x40);
+ saa7111_write(client, 0x0e,
+ (decoder->reg[0x0e] & 0x8f));
+ break;
+
+ case VIDEO_MODE_PAL:
+ saa7111_write(client, 0x08,
+ (decoder->reg[0x08] & 0x3f) | 0x00);
+ saa7111_write(client, 0x0e,
+ (decoder->reg[0x0e] & 0x8f));
+ break;
+
+ case VIDEO_MODE_SECAM:
+ saa7111_write(client, 0x08,
+ (decoder->reg[0x08] & 0x3f) | 0x00);
+ saa7111_write(client, 0x0e,
+ (decoder->reg[0x0e] & 0x8f) | 0x50);
+ break;
+
+ case VIDEO_MODE_AUTO:
+ saa7111_write(client, 0x08,
+ (decoder->reg[0x08] & 0x3f) | 0x80);
+ saa7111_write(client, 0x0e,
+ (decoder->reg[0x0e] & 0x8f));
+ break;
+
+ default:
+ return -EINVAL;
+
+ }
+ decoder->norm = *iarg;
+ }
+ break;
+
+ case DECODER_SET_INPUT:
+ {
+ int *iarg = arg;
+
+ if (*iarg < 0 || *iarg > 7) {
+ return -EINVAL;
+ }
+
+ if (decoder->input != *iarg) {
+ decoder->input = *iarg;
+ /* select mode */
+ saa7111_write(client, 0x02,
+ (decoder->
+ reg[0x02] & 0xf8) | decoder->input);
+ /* bypass chrominance trap for modes 4..7 */
+ saa7111_write(client, 0x09,
+ (decoder->
+ reg[0x09] & 0x7f) | ((decoder->
+ input >
+ 3) ? 0x80 :
+ 0));
+ }
+ }
+ break;
+
+ case DECODER_SET_OUTPUT:
+ {
+ int *iarg = arg;
+
+ /* not much choice of outputs */
+ if (*iarg != 0) {
+ return -EINVAL;
+ }
+ }
+ break;
+
+ case DECODER_ENABLE_OUTPUT:
+ {
+ int *iarg = arg;
+ int enable = (*iarg != 0);
+
+ if (decoder->enable != enable) {
+ decoder->enable = enable;
+
+ /* RJ: If output should be disabled (for
+ * playing videos), we also need a open PLL.
+ * The input is set to 0 (where no input
+ * source is connected), although this
+ * is not necessary.
+ *
+ * If output should be enabled, we have to
+ * reverse the above.
+ */
+
+ if (decoder->enable) {
+ saa7111_write(client, 0x02,
+ (decoder->
+ reg[0x02] & 0xf8) |
+ decoder->input);
+ saa7111_write(client, 0x08,
+ (decoder->reg[0x08] & 0xfb));
+ saa7111_write(client, 0x11,
+ (decoder->
+ reg[0x11] & 0xf3) | 0x0c);
+ } else {
+ saa7111_write(client, 0x02,
+ (decoder->reg[0x02] & 0xf8));
+ saa7111_write(client, 0x08,
+ (decoder->
+ reg[0x08] & 0xfb) | 0x04);
+ saa7111_write(client, 0x11,
+ (decoder->reg[0x11] & 0xf3));
+ }
+ }
+ }
+ break;
+
+ case DECODER_SET_PICTURE:
+ {
+ struct video_picture *pic = arg;
+
+ if (decoder->bright != pic->brightness) {
+ /* We want 0 to 255 we get 0-65535 */
+ decoder->bright = pic->brightness;
+ saa7111_write(client, 0x0a, decoder->bright >> 8);
+ }
+ if (decoder->contrast != pic->contrast) {
+ /* We want 0 to 127 we get 0-65535 */
+ decoder->contrast = pic->contrast;
+ saa7111_write(client, 0x0b,
+ decoder->contrast >> 9);
+ }
+ if (decoder->sat != pic->colour) {
+ /* We want 0 to 127 we get 0-65535 */
+ decoder->sat = pic->colour;
+ saa7111_write(client, 0x0c, decoder->sat >> 9);
+ }
+ if (decoder->hue != pic->hue) {
+ /* We want -128 to 127 we get 0-65535 */
+ decoder->hue = pic->hue;
+ saa7111_write(client, 0x0d,
+ (decoder->hue - 32768) >> 8);
+ }
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/*
+ * Generic i2c probe
+ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+ */
+static unsigned short normal_i2c[] = { I2C_SAA7111 >> 1, I2C_CLIENT_END };
+
+static unsigned short ignore = I2C_CLIENT_END;
+
+static struct i2c_client_address_data addr_data = {
+ .normal_i2c = normal_i2c,
+ .probe = &ignore,
+ .ignore = &ignore,
+};
+
+static struct i2c_driver i2c_driver_saa7111;
+
+static int
+saa7111_detect_client (struct i2c_adapter *adapter,
+ int address,
+ int kind)
+{
+ int i;
+ struct i2c_client *client;
+ struct saa7111 *decoder;
+ struct video_decoder_init vdi;
+
+ dprintk(1,
+ KERN_INFO
+ "saa7111.c: detecting saa7111 client on address 0x%x\n",
+ address << 1);
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return 0;
+
+ client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (client == 0)
+ return -ENOMEM;
+ client->addr = address;
+ client->adapter = adapter;
+ client->driver = &i2c_driver_saa7111;
+ strlcpy(I2C_NAME(client), "saa7111", sizeof(I2C_NAME(client)));
+
+ decoder = kzalloc(sizeof(struct saa7111), GFP_KERNEL);
+ if (decoder == NULL) {
+ kfree(client);
+ return -ENOMEM;
+ }
+ decoder->norm = VIDEO_MODE_NTSC;
+ decoder->input = 0;
+ decoder->enable = 1;
+ decoder->bright = 32768;
+ decoder->contrast = 32768;
+ decoder->hue = 32768;
+ decoder->sat = 32768;
+ i2c_set_clientdata(client, decoder);
+
+ i = i2c_attach_client(client);
+ if (i) {
+ kfree(client);
+ kfree(decoder);
+ return i;
+ }
+
+ vdi.data = saa7111_i2c_init;
+ vdi.len = sizeof(saa7111_i2c_init);
+ i = saa7111_init_decoder(client, &vdi);
+ if (i < 0) {
+ dprintk(1, KERN_ERR "%s_attach error: init status %d\n",
+ I2C_NAME(client), i);
+ } else {
+ dprintk(1,
+ KERN_INFO
+ "%s_attach: chip version %x at address 0x%x\n",
+ I2C_NAME(client), saa7111_read(client, 0x00) >> 4,
+ client->addr << 1);
+ }
+
+ return 0;
+}
+
+static int
+saa7111_attach_adapter (struct i2c_adapter *adapter)
+{
+ dprintk(1,
+ KERN_INFO
+ "saa7111.c: starting probe for adapter %s (0x%x)\n",
+ I2C_NAME(adapter), adapter->id);
+ return i2c_probe(adapter, &addr_data, &saa7111_detect_client);
+}
+
+static int
+saa7111_detach_client (struct i2c_client *client)
+{
+ struct saa7111 *decoder = i2c_get_clientdata(client);
+ int err;
+
+ err = i2c_detach_client(client);
+ if (err) {
+ return err;
+ }
+
+ kfree(decoder);
+ kfree(client);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_driver i2c_driver_saa7111 = {
+ .driver = {
+ .name = "saa7111",
+ },
+
+ .id = I2C_DRIVERID_SAA7111A,
+
+ .attach_adapter = saa7111_attach_adapter,
+ .detach_client = saa7111_detach_client,
+ .command = saa7111_command,
+};
+
+static int __init
+saa7111_init (void)
+{
+ return i2c_add_driver(&i2c_driver_saa7111);
+}
+
+static void __exit
+saa7111_exit (void)
+{
+ i2c_del_driver(&i2c_driver_saa7111);
+}
+
+module_init(saa7111_init);
+module_exit(saa7111_exit);
diff --git a/linux/drivers/media/video/saa7114.c b/linux/drivers/media/video/saa7114.c
new file mode 100644
index 000000000..45242ad54
--- /dev/null
+++ b/linux/drivers/media/video/saa7114.c
@@ -0,0 +1,1221 @@
+/*
+ * saa7114 - Philips SAA7114H video decoder driver version 0.0.1
+ *
+ * Copyright (C) 2002 Maxim Yevtyushkin <max@linuxmedialabs.com>
+ *
+ * Based on saa7111 driver by Dave Perks
+ *
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ *
+ * Slight changes for video timing and attachment output by
+ * Wolfgang Scherr <scherr@net4you.net>
+ *
+ * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * - moved over to linux>=2.4.x i2c protocol (1/1/2003)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+
+#include <linux/slab.h>
+
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/signal.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+
+#include "compat.h"
+#include <linux/videodev.h>
+#include <asm/uaccess.h>
+
+MODULE_DESCRIPTION("Philips SAA7114H video decoder driver");
+MODULE_AUTHOR("Maxim Yevtyushkin");
+MODULE_LICENSE("GPL");
+
+#include <linux/i2c.h>
+
+#define I2C_NAME(x) (x)->name
+
+#include <linux/video_decoder.h>
+
+static int debug = 0;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+/* ----------------------------------------------------------------------- */
+
+struct saa7114 {
+ unsigned char reg[0xf0 * 2];
+
+ int norm;
+ int input;
+ int enable;
+ int bright;
+ int contrast;
+ int hue;
+ int sat;
+ int playback;
+};
+
+#define I2C_SAA7114 0x42
+#define I2C_SAA7114A 0x40
+
+#define I2C_DELAY 10
+
+
+//#define SAA_7114_NTSC_HSYNC_START (-3)
+//#define SAA_7114_NTSC_HSYNC_STOP (-18)
+
+#define SAA_7114_NTSC_HSYNC_START (-17)
+#define SAA_7114_NTSC_HSYNC_STOP (-32)
+
+//#define SAA_7114_NTSC_HOFFSET (5)
+#define SAA_7114_NTSC_HOFFSET (6)
+#define SAA_7114_NTSC_VOFFSET (10)
+#define SAA_7114_NTSC_WIDTH (720)
+#define SAA_7114_NTSC_HEIGHT (250)
+
+#define SAA_7114_SECAM_HSYNC_START (-17)
+#define SAA_7114_SECAM_HSYNC_STOP (-32)
+
+#define SAA_7114_SECAM_HOFFSET (2)
+#define SAA_7114_SECAM_VOFFSET (10)
+#define SAA_7114_SECAM_WIDTH (720)
+#define SAA_7114_SECAM_HEIGHT (300)
+
+#define SAA_7114_PAL_HSYNC_START (-17)
+#define SAA_7114_PAL_HSYNC_STOP (-32)
+
+#define SAA_7114_PAL_HOFFSET (2)
+#define SAA_7114_PAL_VOFFSET (10)
+#define SAA_7114_PAL_WIDTH (720)
+#define SAA_7114_PAL_HEIGHT (300)
+
+
+
+#define SAA_7114_VERTICAL_CHROMA_OFFSET 0 //0x50504040
+#define SAA_7114_VERTICAL_LUMA_OFFSET 0
+
+#define REG_ADDR(x) (((x) << 1) + 1)
+#define LOBYTE(x) ((unsigned char)((x) & 0xff))
+#define HIBYTE(x) ((unsigned char)(((x) >> 8) & 0xff))
+#define LOWORD(x) ((unsigned short int)((x) & 0xffff))
+#define HIWORD(x) ((unsigned short int)(((x) >> 16) & 0xffff))
+
+
+/* ----------------------------------------------------------------------- */
+
+static inline int
+saa7114_write (struct i2c_client *client,
+ u8 reg,
+ u8 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int
+saa7114_write_block (struct i2c_client *client,
+ const u8 *data,
+ unsigned int len)
+{
+ int ret = -1;
+ u8 reg;
+
+ /* the saa7114 has an autoincrement function, use it if
+ * the adapter understands raw I2C */
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ /* do raw I2C, not smbus compatible */
+ u8 block_data[32];
+ int block_len;
+
+ while (len >= 2) {
+ block_len = 0;
+ block_data[block_len++] = reg = data[0];
+ do {
+ block_data[block_len++] = data[1];
+ reg++;
+ len -= 2;
+ data += 2;
+ } while (len >= 2 && data[0] == reg &&
+ block_len < 32);
+ if ((ret = i2c_master_send(client, block_data,
+ block_len)) < 0)
+ break;
+ }
+ } else {
+ /* do some slow I2C emulation kind of thing */
+ while (len >= 2) {
+ reg = *data++;
+ if ((ret = saa7114_write(client, reg,
+ *data++)) < 0)
+ break;
+ len -= 2;
+ }
+ }
+
+ return ret;
+}
+
+static inline int
+saa7114_read (struct i2c_client *client,
+ u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+// initially set NTSC, composite
+
+
+static const unsigned char init[] = {
+ 0x00, 0x00, /* 00 - ID byte , chip version,
+ * read only */
+ 0x01, 0x08, /* 01 - X,X,X,X, IDEL3 to IDEL0 -
+ * horizontal increment delay,
+ * recommended position */
+ 0x02, 0x00, /* 02 - FUSE=3, GUDL=2, MODE=0 ;
+ * input control */
+ 0x03, 0x10, /* 03 - HLNRS=0, VBSL=1, WPOFF=0,
+ * HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */
+ 0x04, 0x90, /* 04 - GAI1=256 */
+ 0x05, 0x90, /* 05 - GAI2=256 */
+ 0x06, SAA_7114_NTSC_HSYNC_START, /* 06 - HSB: hsync start,
+ * depends on the video standard */
+ 0x07, SAA_7114_NTSC_HSYNC_STOP, /* 07 - HSS: hsync stop, depends
+ *on the video standard */
+ 0x08, 0xb8, /* 08 - AUFD=1, FSEL=1, EXFIL=0, VTRC=1,
+ * HPLL: free running in playback, locked
+ * in capture, VNOI=0 */
+ 0x09, 0x80, /* 09 - BYPS=0, PREF=0, BPSS=0, VBLB=0,
+ * UPTCV=0, APER=1; depends from input */
+ 0x0a, 0x80, /* 0a - BRIG=128 */
+ 0x0b, 0x44, /* 0b - CONT=1.109 */
+ 0x0c, 0x40, /* 0c - SATN=1.0 */
+ 0x0d, 0x00, /* 0d - HUE=0 */
+ 0x0e, 0x84, /* 0e - CDTO, CSTD2 to 0, DCVF, FCTC,
+ * CCOMB; depends from video standard */
+ 0x0f, 0x24, /* 0f - ACGC,CGAIN6 to CGAIN0; depends
+ * from video standard */
+ 0x10, 0x03, /* 10 - OFFU1 to 0, OFFV1 to 0, CHBW,
+ * LCBW2 to 0 */
+ 0x11, 0x59, /* 11 - COLO, RTP1, HEDL1 to 0, RTP0,
+ * YDEL2 to 0 */
+ 0x12, 0xc9, /* 12 - RT signal control RTSE13 to 10
+ * and 03 to 00 */
+ 0x13, 0x80, /* 13 - RT/X port output control */
+ 0x14, 0x00, /* 14 - analog, ADC, compatibility control */
+ 0x15, 0x00, /* 15 - VGATE start FID change */
+ 0x16, 0xfe, /* 16 - VGATE stop */
+ 0x17, 0x00, /* 17 - Misc., VGATE MSBs */
+ 0x18, 0x40, /* RAWG */
+ 0x19, 0x80, /* RAWO */
+ 0x1a, 0x00,
+ 0x1b, 0x00,
+ 0x1c, 0x00,
+ 0x1d, 0x00,
+ 0x1e, 0x00,
+ 0x1f, 0x00, /* status byte, read only */
+ 0x20, 0x00, /* video decoder reserved part */
+ 0x21, 0x00,
+ 0x22, 0x00,
+ 0x23, 0x00,
+ 0x24, 0x00,
+ 0x25, 0x00,
+ 0x26, 0x00,
+ 0x27, 0x00,
+ 0x28, 0x00,
+ 0x29, 0x00,
+ 0x2a, 0x00,
+ 0x2b, 0x00,
+ 0x2c, 0x00,
+ 0x2d, 0x00,
+ 0x2e, 0x00,
+ 0x2f, 0x00,
+ 0x30, 0xbc, /* audio clock generator */
+ 0x31, 0xdf,
+ 0x32, 0x02,
+ 0x33, 0x00,
+ 0x34, 0xcd,
+ 0x35, 0xcc,
+ 0x36, 0x3a,
+ 0x37, 0x00,
+ 0x38, 0x03,
+ 0x39, 0x10,
+ 0x3a, 0x00,
+ 0x3b, 0x00,
+ 0x3c, 0x00,
+ 0x3d, 0x00,
+ 0x3e, 0x00,
+ 0x3f, 0x00,
+ 0x40, 0x00, /* VBI data slicer */
+ 0x41, 0xff,
+ 0x42, 0xff,
+ 0x43, 0xff,
+ 0x44, 0xff,
+ 0x45, 0xff,
+ 0x46, 0xff,
+ 0x47, 0xff,
+ 0x48, 0xff,
+ 0x49, 0xff,
+ 0x4a, 0xff,
+ 0x4b, 0xff,
+ 0x4c, 0xff,
+ 0x4d, 0xff,
+ 0x4e, 0xff,
+ 0x4f, 0xff,
+ 0x50, 0xff,
+ 0x51, 0xff,
+ 0x52, 0xff,
+ 0x53, 0xff,
+ 0x54, 0xff,
+ 0x55, 0xff,
+ 0x56, 0xff,
+ 0x57, 0xff,
+ 0x58, 0x40, // framing code
+ 0x59, 0x47, // horizontal offset
+ 0x5a, 0x06, // vertical offset
+ 0x5b, 0x83, // field offset
+ 0x5c, 0x00, // reserved
+ 0x5d, 0x3e, // header and data
+ 0x5e, 0x00, // sliced data
+ 0x5f, 0x00, // reserved
+ 0x60, 0x00, /* video decoder reserved part */
+ 0x61, 0x00,
+ 0x62, 0x00,
+ 0x63, 0x00,
+ 0x64, 0x00,
+ 0x65, 0x00,
+ 0x66, 0x00,
+ 0x67, 0x00,
+ 0x68, 0x00,
+ 0x69, 0x00,
+ 0x6a, 0x00,
+ 0x6b, 0x00,
+ 0x6c, 0x00,
+ 0x6d, 0x00,
+ 0x6e, 0x00,
+ 0x6f, 0x00,
+ 0x70, 0x00, /* video decoder reserved part */
+ 0x71, 0x00,
+ 0x72, 0x00,
+ 0x73, 0x00,
+ 0x74, 0x00,
+ 0x75, 0x00,
+ 0x76, 0x00,
+ 0x77, 0x00,
+ 0x78, 0x00,
+ 0x79, 0x00,
+ 0x7a, 0x00,
+ 0x7b, 0x00,
+ 0x7c, 0x00,
+ 0x7d, 0x00,
+ 0x7e, 0x00,
+ 0x7f, 0x00,
+ 0x80, 0x00, /* X-port, I-port and scaler */
+ 0x81, 0x00,
+ 0x82, 0x00,
+ 0x83, 0x00,
+ 0x84, 0xc5,
+ 0x85, 0x0d, // hsync and vsync ?
+ 0x86, 0x40,
+ 0x87, 0x01,
+ 0x88, 0x00,
+ 0x89, 0x00,
+ 0x8a, 0x00,
+ 0x8b, 0x00,
+ 0x8c, 0x00,
+ 0x8d, 0x00,
+ 0x8e, 0x00,
+ 0x8f, 0x00,
+ 0x90, 0x03, /* Task A definition */
+ 0x91, 0x08,
+ 0x92, 0x00,
+ 0x93, 0x40,
+ 0x94, 0x00, // window settings
+ 0x95, 0x00,
+ 0x96, 0x00,
+ 0x97, 0x00,
+ 0x98, 0x00,
+ 0x99, 0x00,
+ 0x9a, 0x00,
+ 0x9b, 0x00,
+ 0x9c, 0x00,
+ 0x9d, 0x00,
+ 0x9e, 0x00,
+ 0x9f, 0x00,
+ 0xa0, 0x01, /* horizontal integer prescaling ratio */
+ 0xa1, 0x00, /* horizontal prescaler accumulation
+ * sequence length */
+ 0xa2, 0x00, /* UV FIR filter, Y FIR filter, prescaler
+ * DC gain */
+ 0xa3, 0x00,
+ 0xa4, 0x80, // luminance brightness
+ 0xa5, 0x40, // luminance gain
+ 0xa6, 0x40, // chrominance saturation
+ 0xa7, 0x00,
+ 0xa8, 0x00, // horizontal luminance scaling increment
+ 0xa9, 0x04,
+ 0xaa, 0x00, // horizontal luminance phase offset
+ 0xab, 0x00,
+ 0xac, 0x00, // horizontal chrominance scaling increment
+ 0xad, 0x02,
+ 0xae, 0x00, // horizontal chrominance phase offset
+ 0xaf, 0x00,
+ 0xb0, 0x00, // vertical luminance scaling increment
+ 0xb1, 0x04,
+ 0xb2, 0x00, // vertical chrominance scaling increment
+ 0xb3, 0x04,
+ 0xb4, 0x00,
+ 0xb5, 0x00,
+ 0xb6, 0x00,
+ 0xb7, 0x00,
+ 0xb8, 0x00,
+ 0xb9, 0x00,
+ 0xba, 0x00,
+ 0xbb, 0x00,
+ 0xbc, 0x00,
+ 0xbd, 0x00,
+ 0xbe, 0x00,
+ 0xbf, 0x00,
+ 0xc0, 0x02, // Task B definition
+ 0xc1, 0x08,
+ 0xc2, 0x00,
+ 0xc3, 0x40,
+ 0xc4, 0x00, // window settings
+ 0xc5, 0x00,
+ 0xc6, 0x00,
+ 0xc7, 0x00,
+ 0xc8, 0x00,
+ 0xc9, 0x00,
+ 0xca, 0x00,
+ 0xcb, 0x00,
+ 0xcc, 0x00,
+ 0xcd, 0x00,
+ 0xce, 0x00,
+ 0xcf, 0x00,
+ 0xd0, 0x01, // horizontal integer prescaling ratio
+ 0xd1, 0x00, // horizontal prescaler accumulation sequence length
+ 0xd2, 0x00, // UV FIR filter, Y FIR filter, prescaler DC gain
+ 0xd3, 0x00,
+ 0xd4, 0x80, // luminance brightness
+ 0xd5, 0x40, // luminance gain
+ 0xd6, 0x40, // chrominance saturation
+ 0xd7, 0x00,
+ 0xd8, 0x00, // horizontal luminance scaling increment
+ 0xd9, 0x04,
+ 0xda, 0x00, // horizontal luminance phase offset
+ 0xdb, 0x00,
+ 0xdc, 0x00, // horizontal chrominance scaling increment
+ 0xdd, 0x02,
+ 0xde, 0x00, // horizontal chrominance phase offset
+ 0xdf, 0x00,
+ 0xe0, 0x00, // vertical luminance scaling increment
+ 0xe1, 0x04,
+ 0xe2, 0x00, // vertical chrominance scaling increment
+ 0xe3, 0x04,
+ 0xe4, 0x00,
+ 0xe5, 0x00,
+ 0xe6, 0x00,
+ 0xe7, 0x00,
+ 0xe8, 0x00,
+ 0xe9, 0x00,
+ 0xea, 0x00,
+ 0xeb, 0x00,
+ 0xec, 0x00,
+ 0xed, 0x00,
+ 0xee, 0x00,
+ 0xef, 0x00
+};
+
+static int
+saa7114_command (struct i2c_client *client,
+ unsigned int cmd,
+ void *arg)
+{
+ struct saa7114 *decoder = i2c_get_clientdata(client);
+
+ switch (cmd) {
+
+ case 0:
+ //dprintk(1, KERN_INFO "%s: writing init\n", I2C_NAME(client));
+ //saa7114_write_block(client, init, sizeof(init));
+ break;
+
+ case DECODER_DUMP:
+ {
+ int i;
+
+ dprintk(1, KERN_INFO "%s: decoder dump\n", I2C_NAME(client));
+
+ for (i = 0; i < 32; i += 16) {
+ int j;
+
+ printk(KERN_DEBUG "%s: %03x", I2C_NAME(client), i);
+ for (j = 0; j < 16; ++j) {
+ printk(" %02x",
+ saa7114_read(client, i + j));
+ }
+ printk("\n");
+ }
+ }
+ break;
+
+ case DECODER_GET_CAPABILITIES:
+ {
+ struct video_decoder_capability *cap = arg;
+
+ dprintk(1, KERN_DEBUG "%s: decoder get capabilities\n",
+ I2C_NAME(client));
+
+ cap->flags = VIDEO_DECODER_PAL |
+ VIDEO_DECODER_NTSC |
+ VIDEO_DECODER_AUTO |
+ VIDEO_DECODER_CCIR;
+ cap->inputs = 8;
+ cap->outputs = 1;
+ }
+ break;
+
+ case DECODER_GET_STATUS:
+ {
+ int *iarg = arg;
+ int status;
+ int res;
+
+ status = saa7114_read(client, 0x1f);
+
+ dprintk(1, KERN_DEBUG "%s status: 0x%02x\n", I2C_NAME(client),
+ status);
+ res = 0;
+ if ((status & (1 << 6)) == 0) {
+ res |= DECODER_STATUS_GOOD;
+ }
+ switch (decoder->norm) {
+ case VIDEO_MODE_NTSC:
+ res |= DECODER_STATUS_NTSC;
+ break;
+ case VIDEO_MODE_PAL:
+ res |= DECODER_STATUS_PAL;
+ break;
+ case VIDEO_MODE_SECAM:
+ res |= DECODER_STATUS_SECAM;
+ break;
+ default:
+ case VIDEO_MODE_AUTO:
+ if ((status & (1 << 5)) != 0) {
+ res |= DECODER_STATUS_NTSC;
+ } else {
+ res |= DECODER_STATUS_PAL;
+ }
+ break;
+ }
+ if ((status & (1 << 0)) != 0) {
+ res |= DECODER_STATUS_COLOR;
+ }
+ *iarg = res;
+ }
+ break;
+
+ case DECODER_SET_NORM:
+ {
+ int *iarg = arg;
+
+ short int hoff = 0, voff = 0, w = 0, h = 0;
+
+ dprintk(1, KERN_DEBUG "%s: decoder set norm ",
+ I2C_NAME(client));
+ switch (*iarg) {
+
+ case VIDEO_MODE_NTSC:
+ dprintk(1, "NTSC\n");
+ decoder->reg[REG_ADDR(0x06)] =
+ SAA_7114_NTSC_HSYNC_START;
+ decoder->reg[REG_ADDR(0x07)] =
+ SAA_7114_NTSC_HSYNC_STOP;
+
+ decoder->reg[REG_ADDR(0x08)] = decoder->playback ? 0x7c : 0xb8; // PLL free when playback, PLL close when capture
+
+ decoder->reg[REG_ADDR(0x0e)] = 0x85;
+ decoder->reg[REG_ADDR(0x0f)] = 0x24;
+
+ hoff = SAA_7114_NTSC_HOFFSET;
+ voff = SAA_7114_NTSC_VOFFSET;
+ w = SAA_7114_NTSC_WIDTH;
+ h = SAA_7114_NTSC_HEIGHT;
+
+ break;
+
+ case VIDEO_MODE_PAL:
+ dprintk(1, "PAL\n");
+ decoder->reg[REG_ADDR(0x06)] =
+ SAA_7114_PAL_HSYNC_START;
+ decoder->reg[REG_ADDR(0x07)] =
+ SAA_7114_PAL_HSYNC_STOP;
+
+ decoder->reg[REG_ADDR(0x08)] = decoder->playback ? 0x7c : 0xb8; // PLL free when playback, PLL close when capture
+
+ decoder->reg[REG_ADDR(0x0e)] = 0x81;
+ decoder->reg[REG_ADDR(0x0f)] = 0x24;
+
+ hoff = SAA_7114_PAL_HOFFSET;
+ voff = SAA_7114_PAL_VOFFSET;
+ w = SAA_7114_PAL_WIDTH;
+ h = SAA_7114_PAL_HEIGHT;
+
+ break;
+
+ default:
+ dprintk(1, " Unknown video mode!!!\n");
+ return -EINVAL;
+
+ }
+
+
+ decoder->reg[REG_ADDR(0x94)] = LOBYTE(hoff); // hoffset low
+ decoder->reg[REG_ADDR(0x95)] = HIBYTE(hoff) & 0x0f; // hoffset high
+ decoder->reg[REG_ADDR(0x96)] = LOBYTE(w); // width low
+ decoder->reg[REG_ADDR(0x97)] = HIBYTE(w) & 0x0f; // width high
+ decoder->reg[REG_ADDR(0x98)] = LOBYTE(voff); // voffset low
+ decoder->reg[REG_ADDR(0x99)] = HIBYTE(voff) & 0x0f; // voffset high
+ decoder->reg[REG_ADDR(0x9a)] = LOBYTE(h + 2); // height low
+ decoder->reg[REG_ADDR(0x9b)] = HIBYTE(h + 2) & 0x0f; // height high
+ decoder->reg[REG_ADDR(0x9c)] = LOBYTE(w); // out width low
+ decoder->reg[REG_ADDR(0x9d)] = HIBYTE(w) & 0x0f; // out width high
+ decoder->reg[REG_ADDR(0x9e)] = LOBYTE(h); // out height low
+ decoder->reg[REG_ADDR(0x9f)] = HIBYTE(h) & 0x0f; // out height high
+
+ decoder->reg[REG_ADDR(0xc4)] = LOBYTE(hoff); // hoffset low
+ decoder->reg[REG_ADDR(0xc5)] = HIBYTE(hoff) & 0x0f; // hoffset high
+ decoder->reg[REG_ADDR(0xc6)] = LOBYTE(w); // width low
+ decoder->reg[REG_ADDR(0xc7)] = HIBYTE(w) & 0x0f; // width high
+ decoder->reg[REG_ADDR(0xc8)] = LOBYTE(voff); // voffset low
+ decoder->reg[REG_ADDR(0xc9)] = HIBYTE(voff) & 0x0f; // voffset high
+ decoder->reg[REG_ADDR(0xca)] = LOBYTE(h + 2); // height low
+ decoder->reg[REG_ADDR(0xcb)] = HIBYTE(h + 2) & 0x0f; // height high
+ decoder->reg[REG_ADDR(0xcc)] = LOBYTE(w); // out width low
+ decoder->reg[REG_ADDR(0xcd)] = HIBYTE(w) & 0x0f; // out width high
+ decoder->reg[REG_ADDR(0xce)] = LOBYTE(h); // out height low
+ decoder->reg[REG_ADDR(0xcf)] = HIBYTE(h) & 0x0f; // out height high
+
+
+ saa7114_write(client, 0x80, 0x06); // i-port and scaler back end clock selection, task A&B off
+ saa7114_write(client, 0x88, 0xd8); // sw reset scaler
+ saa7114_write(client, 0x88, 0xf8); // sw reset scaler release
+
+ saa7114_write_block(client, decoder->reg + (0x06 << 1),
+ 3 << 1);
+ saa7114_write_block(client, decoder->reg + (0x0e << 1),
+ 2 << 1);
+ saa7114_write_block(client, decoder->reg + (0x5a << 1),
+ 2 << 1);
+
+ saa7114_write_block(client, decoder->reg + (0x94 << 1),
+ (0x9f + 1 - 0x94) << 1);
+ saa7114_write_block(client, decoder->reg + (0xc4 << 1),
+ (0xcf + 1 - 0xc4) << 1);
+
+ saa7114_write(client, 0x88, 0xd8); // sw reset scaler
+ saa7114_write(client, 0x88, 0xf8); // sw reset scaler release
+ saa7114_write(client, 0x80, 0x36); // i-port and scaler back end clock selection
+
+ decoder->norm = *iarg;
+ }
+ break;
+
+ case DECODER_SET_INPUT:
+ {
+ int *iarg = arg;
+
+ dprintk(1, KERN_DEBUG "%s: decoder set input (%d)\n",
+ I2C_NAME(client), *iarg);
+ if (*iarg < 0 || *iarg > 7) {
+ return -EINVAL;
+ }
+
+ if (decoder->input != *iarg) {
+ dprintk(1, KERN_DEBUG "%s: now setting %s input\n",
+ I2C_NAME(client),
+ *iarg >= 6 ? "S-Video" : "Composite");
+ decoder->input = *iarg;
+
+ /* select mode */
+ decoder->reg[REG_ADDR(0x02)] =
+ (decoder->
+ reg[REG_ADDR(0x02)] & 0xf0) | (decoder->
+ input <
+ 6 ? 0x0 : 0x9);
+ saa7114_write(client, 0x02,
+ decoder->reg[REG_ADDR(0x02)]);
+
+ /* bypass chrominance trap for modes 6..9 */
+ decoder->reg[REG_ADDR(0x09)] =
+ (decoder->
+ reg[REG_ADDR(0x09)] & 0x7f) | (decoder->
+ input <
+ 6 ? 0x0 :
+ 0x80);
+ saa7114_write(client, 0x09,
+ decoder->reg[REG_ADDR(0x09)]);
+
+ decoder->reg[REG_ADDR(0x0e)] =
+ decoder->input <
+ 6 ? decoder->
+ reg[REG_ADDR(0x0e)] | 1 : decoder->
+ reg[REG_ADDR(0x0e)] & ~1;
+ saa7114_write(client, 0x0e,
+ decoder->reg[REG_ADDR(0x0e)]);
+ }
+ }
+ break;
+
+ case DECODER_SET_OUTPUT:
+ {
+ int *iarg = arg;
+
+ dprintk(1, KERN_DEBUG "%s: decoder set output\n",
+ I2C_NAME(client));
+
+ /* not much choice of outputs */
+ if (*iarg != 0) {
+ return -EINVAL;
+ }
+ }
+ break;
+
+ case DECODER_ENABLE_OUTPUT:
+ {
+ int *iarg = arg;
+ int enable = (*iarg != 0);
+
+ dprintk(1, KERN_DEBUG "%s: decoder %s output\n",
+ I2C_NAME(client), enable ? "enable" : "disable");
+
+ decoder->playback = !enable;
+
+ if (decoder->enable != enable) {
+ decoder->enable = enable;
+
+ /* RJ: If output should be disabled (for
+ * playing videos), we also need a open PLL.
+ * The input is set to 0 (where no input
+ * source is connected), although this
+ * is not necessary.
+ *
+ * If output should be enabled, we have to
+ * reverse the above.
+ */
+
+ if (decoder->enable) {
+ decoder->reg[REG_ADDR(0x08)] = 0xb8;
+ decoder->reg[REG_ADDR(0x12)] = 0xc9;
+ decoder->reg[REG_ADDR(0x13)] = 0x80;
+ decoder->reg[REG_ADDR(0x87)] = 0x01;
+ } else {
+ decoder->reg[REG_ADDR(0x08)] = 0x7c;
+ decoder->reg[REG_ADDR(0x12)] = 0x00;
+ decoder->reg[REG_ADDR(0x13)] = 0x00;
+ decoder->reg[REG_ADDR(0x87)] = 0x00;
+ }
+
+ saa7114_write_block(client,
+ decoder->reg + (0x12 << 1),
+ 2 << 1);
+ saa7114_write(client, 0x08,
+ decoder->reg[REG_ADDR(0x08)]);
+ saa7114_write(client, 0x87,
+ decoder->reg[REG_ADDR(0x87)]);
+ saa7114_write(client, 0x88, 0xd8); // sw reset scaler
+ saa7114_write(client, 0x88, 0xf8); // sw reset scaler release
+ saa7114_write(client, 0x80, 0x36);
+
+ }
+ }
+ break;
+
+ case DECODER_SET_PICTURE:
+ {
+ struct video_picture *pic = arg;
+
+ dprintk(1,
+ KERN_DEBUG
+ "%s: decoder set picture bright=%d contrast=%d saturation=%d hue=%d\n",
+ I2C_NAME(client), pic->brightness, pic->contrast,
+ pic->colour, pic->hue);
+
+ if (decoder->bright != pic->brightness) {
+ /* We want 0 to 255 we get 0-65535 */
+ decoder->bright = pic->brightness;
+ saa7114_write(client, 0x0a, decoder->bright >> 8);
+ }
+ if (decoder->contrast != pic->contrast) {
+ /* We want 0 to 127 we get 0-65535 */
+ decoder->contrast = pic->contrast;
+ saa7114_write(client, 0x0b,
+ decoder->contrast >> 9);
+ }
+ if (decoder->sat != pic->colour) {
+ /* We want 0 to 127 we get 0-65535 */
+ decoder->sat = pic->colour;
+ saa7114_write(client, 0x0c, decoder->sat >> 9);
+ }
+ if (decoder->hue != pic->hue) {
+ /* We want -128 to 127 we get 0-65535 */
+ decoder->hue = pic->hue;
+ saa7114_write(client, 0x0d,
+ (decoder->hue - 32768) >> 8);
+ }
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/*
+ * Generic i2c probe
+ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+ */
+static unsigned short normal_i2c[] =
+ { I2C_SAA7114 >> 1, I2C_SAA7114A >> 1, I2C_CLIENT_END };
+
+static unsigned short ignore = I2C_CLIENT_END;
+
+static struct i2c_client_address_data addr_data = {
+ .normal_i2c = normal_i2c,
+ .probe = &ignore,
+ .ignore = &ignore,
+};
+
+static struct i2c_driver i2c_driver_saa7114;
+
+static int
+saa7114_detect_client (struct i2c_adapter *adapter,
+ int address,
+ int kind)
+{
+ int i, err[30];
+ short int hoff = SAA_7114_NTSC_HOFFSET;
+ short int voff = SAA_7114_NTSC_VOFFSET;
+ short int w = SAA_7114_NTSC_WIDTH;
+ short int h = SAA_7114_NTSC_HEIGHT;
+ struct i2c_client *client;
+ struct saa7114 *decoder;
+
+ dprintk(1,
+ KERN_INFO
+ "saa7114.c: detecting saa7114 client on address 0x%x\n",
+ address << 1);
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return 0;
+
+ client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (client == 0)
+ return -ENOMEM;
+ client->addr = address;
+ client->adapter = adapter;
+ client->driver = &i2c_driver_saa7114;
+ strlcpy(I2C_NAME(client), "saa7114", sizeof(I2C_NAME(client)));
+
+ decoder = kzalloc(sizeof(struct saa7114), GFP_KERNEL);
+ if (decoder == NULL) {
+ kfree(client);
+ return -ENOMEM;
+ }
+ decoder->norm = VIDEO_MODE_NTSC;
+ decoder->input = -1;
+ decoder->enable = 1;
+ decoder->bright = 32768;
+ decoder->contrast = 32768;
+ decoder->hue = 32768;
+ decoder->sat = 32768;
+ decoder->playback = 0; // initially capture mode useda
+ i2c_set_clientdata(client, decoder);
+
+ memcpy(decoder->reg, init, sizeof(init));
+
+ decoder->reg[REG_ADDR(0x94)] = LOBYTE(hoff); // hoffset low
+ decoder->reg[REG_ADDR(0x95)] = HIBYTE(hoff) & 0x0f; // hoffset high
+ decoder->reg[REG_ADDR(0x96)] = LOBYTE(w); // width low
+ decoder->reg[REG_ADDR(0x97)] = HIBYTE(w) & 0x0f; // width high
+ decoder->reg[REG_ADDR(0x98)] = LOBYTE(voff); // voffset low
+ decoder->reg[REG_ADDR(0x99)] = HIBYTE(voff) & 0x0f; // voffset high
+ decoder->reg[REG_ADDR(0x9a)] = LOBYTE(h + 2); // height low
+ decoder->reg[REG_ADDR(0x9b)] = HIBYTE(h + 2) & 0x0f; // height high
+ decoder->reg[REG_ADDR(0x9c)] = LOBYTE(w); // out width low
+ decoder->reg[REG_ADDR(0x9d)] = HIBYTE(w) & 0x0f; // out width high
+ decoder->reg[REG_ADDR(0x9e)] = LOBYTE(h); // out height low
+ decoder->reg[REG_ADDR(0x9f)] = HIBYTE(h) & 0x0f; // out height high
+
+ decoder->reg[REG_ADDR(0xc4)] = LOBYTE(hoff); // hoffset low
+ decoder->reg[REG_ADDR(0xc5)] = HIBYTE(hoff) & 0x0f; // hoffset high
+ decoder->reg[REG_ADDR(0xc6)] = LOBYTE(w); // width low
+ decoder->reg[REG_ADDR(0xc7)] = HIBYTE(w) & 0x0f; // width high
+ decoder->reg[REG_ADDR(0xc8)] = LOBYTE(voff); // voffset low
+ decoder->reg[REG_ADDR(0xc9)] = HIBYTE(voff) & 0x0f; // voffset high
+ decoder->reg[REG_ADDR(0xca)] = LOBYTE(h + 2); // height low
+ decoder->reg[REG_ADDR(0xcb)] = HIBYTE(h + 2) & 0x0f; // height high
+ decoder->reg[REG_ADDR(0xcc)] = LOBYTE(w); // out width low
+ decoder->reg[REG_ADDR(0xcd)] = HIBYTE(w) & 0x0f; // out width high
+ decoder->reg[REG_ADDR(0xce)] = LOBYTE(h); // out height low
+ decoder->reg[REG_ADDR(0xcf)] = HIBYTE(h) & 0x0f; // out height high
+
+ decoder->reg[REG_ADDR(0xb8)] =
+ LOBYTE(LOWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
+ decoder->reg[REG_ADDR(0xb9)] =
+ HIBYTE(LOWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
+ decoder->reg[REG_ADDR(0xba)] =
+ LOBYTE(HIWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
+ decoder->reg[REG_ADDR(0xbb)] =
+ HIBYTE(HIWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
+
+ decoder->reg[REG_ADDR(0xbc)] =
+ LOBYTE(LOWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
+ decoder->reg[REG_ADDR(0xbd)] =
+ HIBYTE(LOWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
+ decoder->reg[REG_ADDR(0xbe)] =
+ LOBYTE(HIWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
+ decoder->reg[REG_ADDR(0xbf)] =
+ HIBYTE(HIWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
+
+ decoder->reg[REG_ADDR(0xe8)] =
+ LOBYTE(LOWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
+ decoder->reg[REG_ADDR(0xe9)] =
+ HIBYTE(LOWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
+ decoder->reg[REG_ADDR(0xea)] =
+ LOBYTE(HIWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
+ decoder->reg[REG_ADDR(0xeb)] =
+ HIBYTE(HIWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
+
+ decoder->reg[REG_ADDR(0xec)] =
+ LOBYTE(LOWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
+ decoder->reg[REG_ADDR(0xed)] =
+ HIBYTE(LOWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
+ decoder->reg[REG_ADDR(0xee)] =
+ LOBYTE(HIWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
+ decoder->reg[REG_ADDR(0xef)] =
+ HIBYTE(HIWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
+
+
+ decoder->reg[REG_ADDR(0x13)] = 0x80; // RTC0 on
+ decoder->reg[REG_ADDR(0x87)] = 0x01; // I-Port
+ decoder->reg[REG_ADDR(0x12)] = 0xc9; // RTS0
+
+ decoder->reg[REG_ADDR(0x02)] = 0xc0; // set composite1 input, aveasy
+ decoder->reg[REG_ADDR(0x09)] = 0x00; // chrominance trap
+ decoder->reg[REG_ADDR(0x0e)] |= 1; // combfilter on
+
+
+ dprintk(1, KERN_DEBUG "%s_attach: starting decoder init\n",
+ I2C_NAME(client));
+
+ err[0] =
+ saa7114_write_block(client, decoder->reg + (0x20 << 1),
+ 0x10 << 1);
+ err[1] =
+ saa7114_write_block(client, decoder->reg + (0x30 << 1),
+ 0x10 << 1);
+ err[2] =
+ saa7114_write_block(client, decoder->reg + (0x63 << 1),
+ (0x7f + 1 - 0x63) << 1);
+ err[3] =
+ saa7114_write_block(client, decoder->reg + (0x89 << 1),
+ 6 << 1);
+ err[4] =
+ saa7114_write_block(client, decoder->reg + (0xb8 << 1),
+ 8 << 1);
+ err[5] =
+ saa7114_write_block(client, decoder->reg + (0xe8 << 1),
+ 8 << 1);
+
+
+ for (i = 0; i <= 5; i++) {
+ if (err[i] < 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s_attach: init error %d at stage %d, leaving attach.\n",
+ I2C_NAME(client), i, err[i]);
+ kfree(decoder);
+ kfree(client);
+ return 0;
+ }
+ }
+
+ for (i = 6; i < 8; i++) {
+ dprintk(1,
+ KERN_DEBUG
+ "%s_attach: reg[0x%02x] = 0x%02x (0x%02x)\n",
+ I2C_NAME(client), i, saa7114_read(client, i),
+ decoder->reg[REG_ADDR(i)]);
+ }
+
+ dprintk(1,
+ KERN_DEBUG
+ "%s_attach: performing decoder reset sequence\n",
+ I2C_NAME(client));
+
+ err[6] = saa7114_write(client, 0x80, 0x06); // i-port and scaler backend clock selection, task A&B off
+ err[7] = saa7114_write(client, 0x88, 0xd8); // sw reset scaler
+ err[8] = saa7114_write(client, 0x88, 0xf8); // sw reset scaler release
+
+ for (i = 6; i <= 8; i++) {
+ if (err[i] < 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s_attach: init error %d at stage %d, leaving attach.\n",
+ I2C_NAME(client), i, err[i]);
+ kfree(decoder);
+ kfree(client);
+ return 0;
+ }
+ }
+
+ dprintk(1, KERN_INFO "%s_attach: performing the rest of init\n",
+ I2C_NAME(client));
+
+
+ err[9] = saa7114_write(client, 0x01, decoder->reg[REG_ADDR(0x01)]);
+ err[10] = saa7114_write_block(client, decoder->reg + (0x03 << 1), (0x1e + 1 - 0x03) << 1); // big seq
+ err[11] = saa7114_write_block(client, decoder->reg + (0x40 << 1), (0x5f + 1 - 0x40) << 1); // slicer
+ err[12] = saa7114_write_block(client, decoder->reg + (0x81 << 1), 2 << 1); // ?
+ err[13] = saa7114_write_block(client, decoder->reg + (0x83 << 1), 5 << 1); // ?
+ err[14] = saa7114_write_block(client, decoder->reg + (0x90 << 1), 4 << 1); // Task A
+ err[15] =
+ saa7114_write_block(client, decoder->reg + (0x94 << 1),
+ 12 << 1);
+ err[16] =
+ saa7114_write_block(client, decoder->reg + (0xa0 << 1),
+ 8 << 1);
+ err[17] =
+ saa7114_write_block(client, decoder->reg + (0xa8 << 1),
+ 8 << 1);
+ err[18] =
+ saa7114_write_block(client, decoder->reg + (0xb0 << 1),
+ 8 << 1);
+ err[19] = saa7114_write_block(client, decoder->reg + (0xc0 << 1), 4 << 1); // Task B
+ err[15] =
+ saa7114_write_block(client, decoder->reg + (0xc4 << 1),
+ 12 << 1);
+ err[16] =
+ saa7114_write_block(client, decoder->reg + (0xd0 << 1),
+ 8 << 1);
+ err[17] =
+ saa7114_write_block(client, decoder->reg + (0xd8 << 1),
+ 8 << 1);
+ err[18] =
+ saa7114_write_block(client, decoder->reg + (0xe0 << 1),
+ 8 << 1);
+
+ for (i = 9; i <= 18; i++) {
+ if (err[i] < 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s_attach: init error %d at stage %d, leaving attach.\n",
+ I2C_NAME(client), i, err[i]);
+ kfree(decoder);
+ kfree(client);
+ return 0;
+ }
+ }
+
+
+ for (i = 6; i < 8; i++) {
+ dprintk(1,
+ KERN_DEBUG
+ "%s_attach: reg[0x%02x] = 0x%02x (0x%02x)\n",
+ I2C_NAME(client), i, saa7114_read(client, i),
+ decoder->reg[REG_ADDR(i)]);
+ }
+
+
+ for (i = 0x11; i <= 0x13; i++) {
+ dprintk(1,
+ KERN_DEBUG
+ "%s_attach: reg[0x%02x] = 0x%02x (0x%02x)\n",
+ I2C_NAME(client), i, saa7114_read(client, i),
+ decoder->reg[REG_ADDR(i)]);
+ }
+
+
+ dprintk(1, KERN_DEBUG "%s_attach: setting video input\n",
+ I2C_NAME(client));
+
+ err[19] =
+ saa7114_write(client, 0x02, decoder->reg[REG_ADDR(0x02)]);
+ err[20] =
+ saa7114_write(client, 0x09, decoder->reg[REG_ADDR(0x09)]);
+ err[21] =
+ saa7114_write(client, 0x0e, decoder->reg[REG_ADDR(0x0e)]);
+
+ for (i = 19; i <= 21; i++) {
+ if (err[i] < 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s_attach: init error %d at stage %d, leaving attach.\n",
+ I2C_NAME(client), i, err[i]);
+ kfree(decoder);
+ kfree(client);
+ return 0;
+ }
+ }
+
+ dprintk(1,
+ KERN_DEBUG
+ "%s_attach: performing decoder reset sequence\n",
+ I2C_NAME(client));
+
+ err[22] = saa7114_write(client, 0x88, 0xd8); // sw reset scaler
+ err[23] = saa7114_write(client, 0x88, 0xf8); // sw reset scaler release
+ err[24] = saa7114_write(client, 0x80, 0x36); // i-port and scaler backend clock selection, task A&B off
+
+
+ for (i = 22; i <= 24; i++) {
+ if (err[i] < 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s_attach: init error %d at stage %d, leaving attach.\n",
+ I2C_NAME(client), i, err[i]);
+ kfree(decoder);
+ kfree(client);
+ return 0;
+ }
+ }
+
+ err[25] = saa7114_write(client, 0x06, init[REG_ADDR(0x06)]);
+ err[26] = saa7114_write(client, 0x07, init[REG_ADDR(0x07)]);
+ err[27] = saa7114_write(client, 0x10, init[REG_ADDR(0x10)]);
+
+ dprintk(1,
+ KERN_INFO
+ "%s_attach: chip version %x, decoder status 0x%02x\n",
+ I2C_NAME(client), saa7114_read(client, 0x00) >> 4,
+ saa7114_read(client, 0x1f));
+ dprintk(1,
+ KERN_DEBUG
+ "%s_attach: power save control: 0x%02x, scaler status: 0x%02x\n",
+ I2C_NAME(client), saa7114_read(client, 0x88),
+ saa7114_read(client, 0x8f));
+
+
+ for (i = 0x94; i < 0x96; i++) {
+ dprintk(1,
+ KERN_DEBUG
+ "%s_attach: reg[0x%02x] = 0x%02x (0x%02x)\n",
+ I2C_NAME(client), i, saa7114_read(client, i),
+ decoder->reg[REG_ADDR(i)]);
+ }
+
+ i = i2c_attach_client(client);
+ if (i) {
+ kfree(client);
+ kfree(decoder);
+ return i;
+ }
+
+ //i = saa7114_write_block(client, init, sizeof(init));
+ i = 0;
+ if (i < 0) {
+ dprintk(1, KERN_ERR "%s_attach error: init status %d\n",
+ I2C_NAME(client), i);
+ } else {
+ dprintk(1,
+ KERN_INFO
+ "%s_attach: chip version %x at address 0x%x\n",
+ I2C_NAME(client), saa7114_read(client, 0x00) >> 4,
+ client->addr << 1);
+ }
+
+ return 0;
+}
+
+static int
+saa7114_attach_adapter (struct i2c_adapter *adapter)
+{
+ dprintk(1,
+ KERN_INFO
+ "saa7114.c: starting probe for adapter %s (0x%x)\n",
+ I2C_NAME(adapter), adapter->id);
+ return i2c_probe(adapter, &addr_data, &saa7114_detect_client);
+}
+
+static int
+saa7114_detach_client (struct i2c_client *client)
+{
+ struct saa7114 *decoder = i2c_get_clientdata(client);
+ int err;
+
+ err = i2c_detach_client(client);
+ if (err) {
+ return err;
+ }
+
+ kfree(decoder);
+ kfree(client);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_driver i2c_driver_saa7114 = {
+ .driver = {
+ .name = "saa7114",
+ },
+
+ .id = I2C_DRIVERID_SAA7114,
+
+ .attach_adapter = saa7114_attach_adapter,
+ .detach_client = saa7114_detach_client,
+ .command = saa7114_command,
+};
+
+static int __init
+saa7114_init (void)
+{
+ return i2c_add_driver(&i2c_driver_saa7114);
+}
+
+static void __exit
+saa7114_exit (void)
+{
+ i2c_del_driver(&i2c_driver_saa7114);
+}
+
+module_init(saa7114_init);
+module_exit(saa7114_exit);
diff --git a/linux/drivers/media/video/saa7121.h b/linux/drivers/media/video/saa7121.h
new file mode 100644
index 000000000..66967ae37
--- /dev/null
+++ b/linux/drivers/media/video/saa7121.h
@@ -0,0 +1,132 @@
+/* saa7121.h - saa7121 initializations
+ Copyright (C) 1999 Nathan Laredo (laredo@gnu.org)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+#ifndef __SAA7121_H__
+#define __SAA7121_H__
+
+#define NTSC_BURST_START 0x19 /* 28 */
+#define NTSC_BURST_END 0x1d /* 29 */
+#define NTSC_CHROMA_PHASE 0x67 /* 5a */
+#define NTSC_GAINU 0x76 /* 5b */
+#define NTSC_GAINV 0xa5 /* 5c */
+#define NTSC_BLACK_LEVEL 0x2a /* 5d */
+#define NTSC_BLANKING_LEVEL 0x2e /* 5e */
+#define NTSC_VBI_BLANKING 0x2e /* 5f */
+#define NTSC_DAC_CONTROL 0x11 /* 61 */
+#define NTSC_BURST_AMP 0x3f /* 62 */
+#define NTSC_SUBC3 0x1f /* 63 */
+#define NTSC_SUBC2 0x7c /* 64 */
+#define NTSC_SUBC1 0xf0 /* 65 */
+#define NTSC_SUBC0 0x21 /* 66 */
+#define NTSC_HTRIG 0x72 /* 6c */
+#define NTSC_VTRIG 0x00 /* 6c */
+#define NTSC_MULTI 0x30 /* 6e */
+#define NTSC_CCTTX 0x11 /* 6f */
+#define NTSC_FIRST_ACTIVE 0x12 /* 7a */
+#define NTSC_LAST_ACTIVE 0x02 /* 7b */
+#define NTSC_MSB_VERTICAL 0x40 /* 7c */
+
+#define PAL_BURST_START 0x21 /* 28 */
+#define PAL_BURST_END 0x1d /* 29 */
+#define PAL_CHROMA_PHASE 0x3f /* 5a */
+#define PAL_GAINU 0x7d /* 5b */
+#define PAL_GAINV 0xaf /* 5c */
+#define PAL_BLACK_LEVEL 0x23 /* 5d */
+#define PAL_BLANKING_LEVEL 0x35 /* 5e */
+#define PAL_VBI_BLANKING 0x35 /* 5f */
+#define PAL_DAC_CONTROL 0x02 /* 61 */
+#define PAL_BURST_AMP 0x2f /* 62 */
+#define PAL_SUBC3 0xcb /* 63 */
+#define PAL_SUBC2 0x8a /* 64 */
+#define PAL_SUBC1 0x09 /* 65 */
+#define PAL_SUBC0 0x2a /* 66 */
+#define PAL_HTRIG 0x86 /* 6c */
+#define PAL_VTRIG 0x04 /* 6d */
+#define PAL_MULTI 0x20 /* 6e */
+#define PAL_CCTTX 0x15 /* 6f */
+#define PAL_FIRST_ACTIVE 0x16 /* 7a */
+#define PAL_LAST_ACTIVE 0x36 /* 7b */
+#define PAL_MSB_VERTICAL 0x40 /* 7c */
+
+/* Initialization Sequence */
+
+static __u8 init7121ntsc[] = {
+ 0x26, 0x0, 0x27, 0x0,
+ 0x28, NTSC_BURST_START, 0x29, NTSC_BURST_END,
+ 0x2a, 0x0, 0x2b, 0x0, 0x2c, 0x0, 0x2d, 0x0,
+ 0x2e, 0x0, 0x2f, 0x0, 0x30, 0x0, 0x31, 0x0,
+ 0x32, 0x0, 0x33, 0x0, 0x34, 0x0, 0x35, 0x0,
+ 0x36, 0x0, 0x37, 0x0, 0x38, 0x0, 0x39, 0x0,
+ 0x3a, 0x03, 0x3b, 0x0, 0x3c, 0x0, 0x3d, 0x0,
+ 0x3e, 0x0, 0x3f, 0x0, 0x40, 0x0, 0x41, 0x0,
+ 0x42, 0x0, 0x43, 0x0, 0x44, 0x0, 0x45, 0x0,
+ 0x46, 0x0, 0x47, 0x0, 0x48, 0x0, 0x49, 0x0,
+ 0x4a, 0x0, 0x4b, 0x0, 0x4c, 0x0, 0x4d, 0x0,
+ 0x4e, 0x0, 0x4f, 0x0, 0x50, 0x0, 0x51, 0x0,
+ 0x52, 0x0, 0x53, 0x0, 0x54, 0x0, 0x55, 0x0,
+ 0x56, 0x0, 0x57, 0x0, 0x58, 0x0, 0x59, 0x0,
+ 0x5a, NTSC_CHROMA_PHASE, 0x5b, NTSC_GAINU,
+ 0x5c, NTSC_GAINV, 0x5d, NTSC_BLACK_LEVEL,
+ 0x5e, NTSC_BLANKING_LEVEL, 0x5f, NTSC_VBI_BLANKING,
+ 0x60, 0x0, 0x61, NTSC_DAC_CONTROL,
+ 0x62, NTSC_BURST_AMP, 0x63, NTSC_SUBC3,
+ 0x64, NTSC_SUBC2, 0x65, NTSC_SUBC1,
+ 0x66, NTSC_SUBC0, 0x67, 0x80, 0x68, 0x80,
+ 0x69, 0x80, 0x6a, 0x80, 0x6b, 0x29,
+ 0x6c, NTSC_HTRIG, 0x6d, NTSC_VTRIG,
+ 0x6e, NTSC_MULTI, 0x6f, NTSC_CCTTX,
+ 0x70, 0xc9, 0x71, 0x68, 0x72, 0x60, 0x73, 0x0,
+ 0x74, 0x0, 0x75, 0x0, 0x76, 0x0, 0x77, 0x0,
+ 0x78, 0x0, 0x79, 0x0, 0x7a, NTSC_FIRST_ACTIVE,
+ 0x7b, NTSC_LAST_ACTIVE, 0x7c, NTSC_MSB_VERTICAL,
+ 0x7d, 0x0, 0x7e, 0x0, 0x7f, 0x0
+};
+#define INIT7121LEN (sizeof(init7121ntsc)/2)
+
+static __u8 init7121pal[] = {
+ 0x26, 0x0, 0x27, 0x0,
+ 0x28, PAL_BURST_START, 0x29, PAL_BURST_END,
+ 0x2a, 0x0, 0x2b, 0x0, 0x2c, 0x0, 0x2d, 0x0,
+ 0x2e, 0x0, 0x2f, 0x0, 0x30, 0x0, 0x31, 0x0,
+ 0x32, 0x0, 0x33, 0x0, 0x34, 0x0, 0x35, 0x0,
+ 0x36, 0x0, 0x37, 0x0, 0x38, 0x0, 0x39, 0x0,
+ 0x3a, 0x03, 0x3b, 0x0, 0x3c, 0x0, 0x3d, 0x0,
+ 0x3e, 0x0, 0x3f, 0x0, 0x40, 0x0, 0x41, 0x0,
+ 0x42, 0x0, 0x43, 0x0, 0x44, 0x0, 0x45, 0x0,
+ 0x46, 0x0, 0x47, 0x0, 0x48, 0x0, 0x49, 0x0,
+ 0x4a, 0x0, 0x4b, 0x0, 0x4c, 0x0, 0x4d, 0x0,
+ 0x4e, 0x0, 0x4f, 0x0, 0x50, 0x0, 0x51, 0x0,
+ 0x52, 0x0, 0x53, 0x0, 0x54, 0x0, 0x55, 0x0,
+ 0x56, 0x0, 0x57, 0x0, 0x58, 0x0, 0x59, 0x0,
+ 0x5a, PAL_CHROMA_PHASE, 0x5b, PAL_GAINU,
+ 0x5c, PAL_GAINV, 0x5d, PAL_BLACK_LEVEL,
+ 0x5e, PAL_BLANKING_LEVEL, 0x5f, PAL_VBI_BLANKING,
+ 0x60, 0x0, 0x61, PAL_DAC_CONTROL,
+ 0x62, PAL_BURST_AMP, 0x63, PAL_SUBC3,
+ 0x64, PAL_SUBC2, 0x65, PAL_SUBC1,
+ 0x66, PAL_SUBC0, 0x67, 0x80, 0x68, 0x80,
+ 0x69, 0x80, 0x6a, 0x80, 0x6b, 0x29,
+ 0x6c, PAL_HTRIG, 0x6d, PAL_VTRIG,
+ 0x6e, PAL_MULTI, 0x6f, PAL_CCTTX,
+ 0x70, 0xc9, 0x71, 0x68, 0x72, 0x60, 0x73, 0x0,
+ 0x74, 0x0, 0x75, 0x0, 0x76, 0x0, 0x77, 0x0,
+ 0x78, 0x0, 0x79, 0x0, 0x7a, PAL_FIRST_ACTIVE,
+ 0x7b, PAL_LAST_ACTIVE, 0x7c, PAL_MSB_VERTICAL,
+ 0x7d, 0x0, 0x7e, 0x0, 0x7f, 0x0
+};
+#endif
diff --git a/linux/drivers/media/video/saa7146.h b/linux/drivers/media/video/saa7146.h
new file mode 100644
index 000000000..2830b5e33
--- /dev/null
+++ b/linux/drivers/media/video/saa7146.h
@@ -0,0 +1,114 @@
+/*
+ saa7146.h - definitions philips saa7146 based cards
+ Copyright (C) 1999 Nathan Laredo (laredo@gnu.org)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __SAA7146__
+#define __SAA7146__
+
+#define SAA7146_VERSION_CODE 0x000101
+
+#include <linux/types.h>
+#include <linux/wait.h>
+
+#include <linux/videodev.h>
+
+#ifndef O_NONCAP
+#define O_NONCAP O_TRUNC
+#endif
+
+#define MAX_GBUFFERS 2
+#define FBUF_SIZE 0x190000
+
+#ifdef __KERNEL__
+
+struct saa7146_window
+{
+ int x, y;
+ ushort width, height;
+ ushort bpp, bpl;
+ ushort swidth, sheight;
+ short cropx, cropy;
+ ushort cropwidth, cropheight;
+ unsigned long vidadr;
+ int color_fmt;
+ ushort depth;
+};
+
+/* Per-open data for handling multiple opens on one device */
+struct device_open
+{
+ int isopen;
+ int noncapturing;
+ struct saa7146 *dev;
+};
+#define MAX_OPENS 3
+
+struct saa7146
+{
+ struct video_device video_dev;
+ struct video_picture picture;
+ struct video_audio audio_dev;
+ struct video_info vidinfo;
+ int user;
+ int cap;
+ int capuser;
+ int irqstate; /* irq routine is state driven */
+ int writemode;
+ int playmode;
+ unsigned int nr;
+ unsigned long irq; /* IRQ used by SAA7146 card */
+ unsigned short id;
+ unsigned char revision;
+ unsigned char boardcfg[64]; /* 64 bytes of config from eeprom */
+ unsigned long saa7146_adr; /* bus address of IO mem from PCI BIOS */
+ struct saa7146_window win;
+ unsigned char __iomem *saa7146_mem; /* pointer to mapped IO memory */
+ struct device_open open_data[MAX_OPENS];
+#define MAX_MARKS 16
+ /* for a/v sync */
+ int endmark[MAX_MARKS], endmarkhead, endmarktail;
+ u32 *dmaRPS1, *pageRPS1, *dmaRPS2, *pageRPS2, *dmavid1, *dmavid2,
+ *dmavid3, *dmaa1in, *dmaa1out, *dmaa2in, *dmaa2out,
+ *pagedebi, *pagevid1, *pagevid2, *pagevid3, *pagea1in,
+ *pagea1out, *pagea2in, *pagea2out;
+ wait_queue_head_t i2cq, debiq, audq, vidq;
+ u8 *vidbuf, *audbuf, *osdbuf, *dmadebi;
+ int audhead, vidhead, osdhead, audtail, vidtail, osdtail;
+ spinlock_t lock; /* the device lock */
+};
+#endif
+
+#ifdef _ALPHA_SAA7146
+#define saawrite(dat,adr) writel((dat), saa->saa7146_adr+(adr))
+#define saaread(adr) readl(saa->saa7146_adr+(adr))
+#else
+#define saawrite(dat,adr) writel((dat), saa->saa7146_mem+(adr))
+#define saaread(adr) readl(saa->saa7146_mem+(adr))
+#endif
+
+#define saaand(dat,adr) saawrite((dat) & saaread(adr), adr)
+#define saaor(dat,adr) saawrite((dat) | saaread(adr), adr)
+#define saaaor(dat,mask,adr) saawrite((dat) | ((mask) & saaread(adr)), adr)
+
+/* bitmask of attached hardware found */
+#define SAA7146_UNKNOWN 0x00000000
+#define SAA7146_SAA7111 0x00000001
+#define SAA7146_SAA7121 0x00000002
+#define SAA7146_IBMMPEG 0x00000004
+
+#endif
diff --git a/linux/drivers/media/video/saa7146reg.h b/linux/drivers/media/video/saa7146reg.h
new file mode 100644
index 000000000..80ec2c146
--- /dev/null
+++ b/linux/drivers/media/video/saa7146reg.h
@@ -0,0 +1,283 @@
+/*
+ saa7146.h - definitions philips saa7146 based cards
+ Copyright (C) 1999 Nathan Laredo (laredo@gnu.org)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __SAA7146_REG__
+#define __SAA7146_REG__
+#define SAA7146_BASE_ODD1 0x00
+#define SAA7146_BASE_EVEN1 0x04
+#define SAA7146_PROT_ADDR1 0x08
+#define SAA7146_PITCH1 0x0c
+#define SAA7146_PAGE1 0x10
+#define SAA7146_NUM_LINE_BYTE1 0x14
+#define SAA7146_BASE_ODD2 0x18
+#define SAA7146_BASE_EVEN2 0x1c
+#define SAA7146_PROT_ADDR2 0x20
+#define SAA7146_PITCH2 0x24
+#define SAA7146_PAGE2 0x28
+#define SAA7146_NUM_LINE_BYTE2 0x2c
+#define SAA7146_BASE_ODD3 0x30
+#define SAA7146_BASE_EVEN3 0x34
+#define SAA7146_PROT_ADDR3 0x38
+#define SAA7146_PITCH3 0x3c
+#define SAA7146_PAGE3 0x40
+#define SAA7146_NUM_LINE_BYTE3 0x44
+#define SAA7146_PCI_BT_V1 0x48
+#define SAA7146_PCI_BT_V2 0x49
+#define SAA7146_PCI_BT_V3 0x4a
+#define SAA7146_PCI_BT_DEBI 0x4b
+#define SAA7146_PCI_BT_A 0x4c
+#define SAA7146_DD1_INIT 0x50
+#define SAA7146_DD1_STREAM_B 0x54
+#define SAA7146_DD1_STREAM_A 0x56
+#define SAA7146_BRS_CTRL 0x58
+#define SAA7146_HPS_CTRL 0x5c
+#define SAA7146_HPS_V_SCALE 0x60
+#define SAA7146_HPS_V_GAIN 0x64
+#define SAA7146_HPS_H_PRESCALE 0x68
+#define SAA7146_HPS_H_SCALE 0x6c
+#define SAA7146_BCS_CTRL 0x70
+#define SAA7146_CHROMA_KEY_RANGE 0x74
+#define SAA7146_CLIP_FORMAT_CTRL 0x78
+#define SAA7146_DEBI_CONFIG 0x7c
+#define SAA7146_DEBI_COMMAND 0x80
+#define SAA7146_DEBI_PAGE 0x84
+#define SAA7146_DEBI_AD 0x88
+#define SAA7146_I2C_TRANSFER 0x8c
+#define SAA7146_I2C_STATUS 0x90
+#define SAA7146_BASE_A1_IN 0x94
+#define SAA7146_PROT_A1_IN 0x98
+#define SAA7146_PAGE_A1_IN 0x9C
+#define SAA7146_BASE_A1_OUT 0xa0
+#define SAA7146_PROT_A1_OUT 0xa4
+#define SAA7146_PAGE_A1_OUT 0xa8
+#define SAA7146_BASE_A2_IN 0xac
+#define SAA7146_PROT_A2_IN 0xb0
+#define SAA7146_PAGE_A2_IN 0xb4
+#define SAA7146_BASE_A2_OUT 0xb8
+#define SAA7146_PROT_A2_OUT 0xbc
+#define SAA7146_PAGE_A2_OUT 0xc0
+#define SAA7146_RPS_PAGE0 0xc4
+#define SAA7146_RPS_PAGE1 0xc8
+#define SAA7146_RPS_THRESH0 0xcc
+#define SAA7146_RPS_THRESH1 0xd0
+#define SAA7146_RPS_TOV0 0xd4
+#define SAA7146_RPS_TOV1 0xd8
+#define SAA7146_IER 0xdc
+#define SAA7146_GPIO_CTRL 0xe0
+#define SAA7146_EC1SSR 0xe4
+#define SAA7146_EC2SSR 0xe8
+#define SAA7146_ECT1R 0xec
+#define SAA7146_ECT2R 0xf0
+#define SAA7146_ACON1 0xf4
+#define SAA7146_ACON2 0xf8
+#define SAA7146_MC1 0xfc
+#define SAA7146_MC2 0x100
+#define SAA7146_RPS_ADDR0 0x104
+#define SAA7146_RPS_ADDR1 0x108
+#define SAA7146_ISR 0x10c
+#define SAA7146_PSR 0x110
+#define SAA7146_SSR 0x114
+#define SAA7146_EC1R 0x118
+#define SAA7146_EC2R 0x11c
+#define SAA7146_VDP1 0x120
+#define SAA7146_VDP2 0x124
+#define SAA7146_VDP3 0x128
+#define SAA7146_ADP1 0x12c
+#define SAA7146_ADP2 0x130
+#define SAA7146_ADP3 0x134
+#define SAA7146_ADP4 0x138
+#define SAA7146_DDP 0x13c
+#define SAA7146_LEVEL_REP 0x140
+#define SAA7146_FB_BUFFER1 0x144
+#define SAA7146_FB_BUFFER2 0x148
+#define SAA7146_A_TIME_SLOT1 0x180
+#define SAA7146_A_TIME_SLOT2 0x1C0
+
+/* bitfield defines */
+#define MASK_31 0x80000000
+#define MASK_30 0x40000000
+#define MASK_29 0x20000000
+#define MASK_28 0x10000000
+#define MASK_27 0x08000000
+#define MASK_26 0x04000000
+#define MASK_25 0x02000000
+#define MASK_24 0x01000000
+#define MASK_23 0x00800000
+#define MASK_22 0x00400000
+#define MASK_21 0x00200000
+#define MASK_20 0x00100000
+#define MASK_19 0x00080000
+#define MASK_18 0x00040000
+#define MASK_17 0x00020000
+#define MASK_16 0x00010000
+#define MASK_15 0x00008000
+#define MASK_14 0x00004000
+#define MASK_13 0x00002000
+#define MASK_12 0x00001000
+#define MASK_11 0x00000800
+#define MASK_10 0x00000400
+#define MASK_09 0x00000200
+#define MASK_08 0x00000100
+#define MASK_07 0x00000080
+#define MASK_06 0x00000040
+#define MASK_05 0x00000020
+#define MASK_04 0x00000010
+#define MASK_03 0x00000008
+#define MASK_02 0x00000004
+#define MASK_01 0x00000002
+#define MASK_00 0x00000001
+#define MASK_B0 0x000000ff
+#define MASK_B1 0x0000ff00
+#define MASK_B2 0x00ff0000
+#define MASK_B3 0xff000000
+#define MASK_W0 0x0000ffff
+#define MASK_W1 0xffff0000
+#define MASK_PA 0xfffffffc
+#define MASK_PR 0xfffffffe
+#define MASK_ER 0xffffffff
+#define MASK_NONE 0x00000000
+
+#define SAA7146_PAGE_MAP_EN MASK_11
+/* main control register 1 */
+#define SAA7146_MC1_MRST_N MASK_15
+#define SAA7146_MC1_ERPS1 MASK_13
+#define SAA7146_MC1_ERPS0 MASK_12
+#define SAA7146_MC1_EDP MASK_11
+#define SAA7146_MC1_EVP MASK_10
+#define SAA7146_MC1_EAP MASK_09
+#define SAA7146_MC1_EI2C MASK_08
+#define SAA7146_MC1_TR_E_DEBI MASK_07
+#define SAA7146_MC1_TR_E_1 MASK_06
+#define SAA7146_MC1_TR_E_2 MASK_05
+#define SAA7146_MC1_TR_E_3 MASK_04
+#define SAA7146_MC1_TR_E_A2_OUT MASK_03
+#define SAA7146_MC1_TR_E_A2_IN MASK_02
+#define SAA7146_MC1_TR_E_A1_OUT MASK_01
+#define SAA7146_MC1_TR_E_A1_IN MASK_00
+/* main control register 2 */
+#define SAA7146_MC2_RPS_SIG4 MASK_15
+#define SAA7146_MC2_RPS_SIG3 MASK_14
+#define SAA7146_MC2_RPS_SIG2 MASK_13
+#define SAA7146_MC2_RPS_SIG1 MASK_12
+#define SAA7146_MC2_RPS_SIG0 MASK_11
+#define SAA7146_MC2_UPLD_D1_B MASK_10
+#define SAA7146_MC2_UPLD_D1_A MASK_09
+#define SAA7146_MC2_UPLD_BRS MASK_08
+#define SAA7146_MC2_UPLD_HPS_H MASK_06
+#define SAA7146_MC2_UPLD_HPS_V MASK_05
+#define SAA7146_MC2_UPLD_DMA3 MASK_04
+#define SAA7146_MC2_UPLD_DMA2 MASK_03
+#define SAA7146_MC2_UPLD_DMA1 MASK_02
+#define SAA7146_MC2_UPLD_DEBI MASK_01
+#define SAA7146_MC2_UPLD_I2C MASK_00
+/* Primary Status Register and Interrupt Enable/Status Registers */
+#define SAA7146_PSR_PPEF MASK_31
+#define SAA7146_PSR_PABO MASK_30
+#define SAA7146_PSR_PPED MASK_29
+#define SAA7146_PSR_RPS_I1 MASK_28
+#define SAA7146_PSR_RPS_I0 MASK_27
+#define SAA7146_PSR_RPS_LATE1 MASK_26
+#define SAA7146_PSR_RPS_LATE0 MASK_25
+#define SAA7146_PSR_RPS_E1 MASK_24
+#define SAA7146_PSR_RPS_E0 MASK_23
+#define SAA7146_PSR_RPS_TO1 MASK_22
+#define SAA7146_PSR_RPS_TO0 MASK_21
+#define SAA7146_PSR_UPLD MASK_20
+#define SAA7146_PSR_DEBI_S MASK_19
+#define SAA7146_PSR_DEBI_E MASK_18
+#define SAA7146_PSR_I2C_S MASK_17
+#define SAA7146_PSR_I2C_E MASK_16
+#define SAA7146_PSR_A2_IN MASK_15
+#define SAA7146_PSR_A2_OUT MASK_14
+#define SAA7146_PSR_A1_IN MASK_13
+#define SAA7146_PSR_A1_OUT MASK_12
+#define SAA7146_PSR_AFOU MASK_11
+#define SAA7146_PSR_V_PE MASK_10
+#define SAA7146_PSR_VFOU MASK_09
+#define SAA7146_PSR_FIDA MASK_08
+#define SAA7146_PSR_FIDB MASK_07
+#define SAA7146_PSR_PIN3 MASK_06
+#define SAA7146_PSR_PIN2 MASK_05
+#define SAA7146_PSR_PIN1 MASK_04
+#define SAA7146_PSR_PIN0 MASK_03
+#define SAA7146_PSR_ECS MASK_02
+#define SAA7146_PSR_EC3S MASK_01
+#define SAA7146_PSR_EC0S MASK_00
+/* Secondary Status Register */
+#define SAA7146_SSR_PRQ MASK_31
+#define SAA7146_SSR_PMA MASK_30
+#define SAA7146_SSR_RPS_RE1 MASK_29
+#define SAA7146_SSR_RPS_PE1 MASK_28
+#define SAA7146_SSR_RPS_A1 MASK_27
+#define SAA7146_SSR_RPS_RE0 MASK_26
+#define SAA7146_SSR_RPS_PE0 MASK_25
+#define SAA7146_SSR_RPS_A0 MASK_24
+#define SAA7146_SSR_DEBI_TO MASK_23
+#define SAA7146_SSR_DEBI_EF MASK_22
+#define SAA7146_SSR_I2C_EA MASK_21
+#define SAA7146_SSR_I2C_EW MASK_20
+#define SAA7146_SSR_I2C_ER MASK_19
+#define SAA7146_SSR_I2C_EL MASK_18
+#define SAA7146_SSR_I2C_EF MASK_17
+#define SAA7146_SSR_V3P MASK_16
+#define SAA7146_SSR_V2P MASK_15
+#define SAA7146_SSR_V1P MASK_14
+#define SAA7146_SSR_VF3 MASK_13
+#define SAA7146_SSR_VF2 MASK_12
+#define SAA7146_SSR_VF1 MASK_11
+#define SAA7146_SSR_AF2_IN MASK_10
+#define SAA7146_SSR_AF2_OUT MASK_09
+#define SAA7146_SSR_AF1_IN MASK_08
+#define SAA7146_SSR_AF1_OUT MASK_07
+#define SAA7146_SSR_VGT MASK_05
+#define SAA7146_SSR_LNQG MASK_04
+#define SAA7146_SSR_EC5S MASK_03
+#define SAA7146_SSR_EC4S MASK_02
+#define SAA7146_SSR_EC2S MASK_01
+#define SAA7146_SSR_EC1S MASK_00
+/* I2C status register */
+#define SAA7146_I2C_ABORT MASK_07
+#define SAA7146_I2C_SPERR MASK_06
+#define SAA7146_I2C_APERR MASK_05
+#define SAA7146_I2C_DTERR MASK_04
+#define SAA7146_I2C_DRERR MASK_03
+#define SAA7146_I2C_AL MASK_02
+#define SAA7146_I2C_ERR MASK_01
+#define SAA7146_I2C_BUSY MASK_00
+/* output formats */
+#define SAA7146_YUV422 0
+#define SAA7146_RGB16 0
+#define SAA7146_YUV444 1
+#define SAA7146_RGB24 1
+#define SAA7146_ARGB32 2
+#define SAA7146_YUV411 3
+#define SAA7146_ARGB15 3
+#define SAA7146_YUV2 4
+#define SAA7146_RGAB15 4
+#define SAA7146_Y8 6
+#define SAA7146_YUV8 7
+#define SAA7146_RGB8 7
+#define SAA7146_YUV444p 8
+#define SAA7146_YUV422p 9
+#define SAA7146_YUV420p 10
+#define SAA7146_YUV1620 11
+#define SAA7146_Y1 13
+#define SAA7146_Y2 14
+#define SAA7146_YUV1 15
+#endif
diff --git a/linux/drivers/media/video/saa7185.c b/linux/drivers/media/video/saa7185.c
new file mode 100644
index 000000000..a9b45c25a
--- /dev/null
+++ b/linux/drivers/media/video/saa7185.c
@@ -0,0 +1,508 @@
+/*
+ * saa7185 - Philips SAA7185B video encoder driver version 0.0.3
+ *
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ *
+ * Slight changes for video timing and attachment output by
+ * Wolfgang Scherr <scherr@net4you.net>
+ *
+ * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * - moved over to linux>=2.4.x i2c protocol (1/1/2003)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/signal.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+
+#include "compat.h"
+#include <linux/videodev.h>
+#include <asm/uaccess.h>
+
+MODULE_DESCRIPTION("Philips SAA7185 video encoder driver");
+MODULE_AUTHOR("Dave Perks");
+MODULE_LICENSE("GPL");
+
+#include <linux/i2c.h>
+
+#define I2C_NAME(s) (s)->name
+
+#include <linux/video_encoder.h>
+
+static int debug = 0;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+/* ----------------------------------------------------------------------- */
+
+struct saa7185 {
+ unsigned char reg[128];
+
+ int norm;
+ int enable;
+ int bright;
+ int contrast;
+ int hue;
+ int sat;
+};
+
+#define I2C_SAA7185 0x88
+
+/* ----------------------------------------------------------------------- */
+
+static inline int
+saa7185_read (struct i2c_client *client)
+{
+ return i2c_smbus_read_byte(client);
+}
+
+static int
+saa7185_write (struct i2c_client *client,
+ u8 reg,
+ u8 value)
+{
+ struct saa7185 *encoder = i2c_get_clientdata(client);
+
+ dprintk(1, KERN_DEBUG "SAA7185: %02x set to %02x\n", reg, value);
+ encoder->reg[reg] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int
+saa7185_write_block (struct i2c_client *client,
+ const u8 *data,
+ unsigned int len)
+{
+ int ret = -1;
+ u8 reg;
+
+ /* the adv7175 has an autoincrement function, use it if
+ * the adapter understands raw I2C */
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ /* do raw I2C, not smbus compatible */
+ struct saa7185 *encoder = i2c_get_clientdata(client);
+ u8 block_data[32];
+ int block_len;
+
+ while (len >= 2) {
+ block_len = 0;
+ block_data[block_len++] = reg = data[0];
+ do {
+ block_data[block_len++] =
+ encoder->reg[reg++] = data[1];
+ len -= 2;
+ data += 2;
+ } while (len >= 2 && data[0] == reg &&
+ block_len < 32);
+ if ((ret = i2c_master_send(client, block_data,
+ block_len)) < 0)
+ break;
+ }
+ } else {
+ /* do some slow I2C emulation kind of thing */
+ while (len >= 2) {
+ reg = *data++;
+ if ((ret = saa7185_write(client, reg,
+ *data++)) < 0)
+ break;
+ len -= 2;
+ }
+ }
+
+ return ret;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const unsigned char init_common[] = {
+ 0x3a, 0x0f, /* CBENB=0, V656=0, VY2C=1,
+ * YUV2C=1, MY2C=1, MUV2C=1 */
+
+ 0x42, 0x6b, /* OVLY0=107 */
+ 0x43, 0x00, /* OVLU0=0 white */
+ 0x44, 0x00, /* OVLV0=0 */
+ 0x45, 0x22, /* OVLY1=34 */
+ 0x46, 0xac, /* OVLU1=172 yellow */
+ 0x47, 0x0e, /* OVLV1=14 */
+ 0x48, 0x03, /* OVLY2=3 */
+ 0x49, 0x1d, /* OVLU2=29 cyan */
+ 0x4a, 0xac, /* OVLV2=172 */
+ 0x4b, 0xf0, /* OVLY3=240 */
+ 0x4c, 0xc8, /* OVLU3=200 green */
+ 0x4d, 0xb9, /* OVLV3=185 */
+ 0x4e, 0xd4, /* OVLY4=212 */
+ 0x4f, 0x38, /* OVLU4=56 magenta */
+ 0x50, 0x47, /* OVLV4=71 */
+ 0x51, 0xc1, /* OVLY5=193 */
+ 0x52, 0xe3, /* OVLU5=227 red */
+ 0x53, 0x54, /* OVLV5=84 */
+ 0x54, 0xa3, /* OVLY6=163 */
+ 0x55, 0x54, /* OVLU6=84 blue */
+ 0x56, 0xf2, /* OVLV6=242 */
+ 0x57, 0x90, /* OVLY7=144 */
+ 0x58, 0x00, /* OVLU7=0 black */
+ 0x59, 0x00, /* OVLV7=0 */
+
+ 0x5a, 0x00, /* CHPS=0 */
+ 0x5b, 0x76, /* GAINU=118 */
+ 0x5c, 0xa5, /* GAINV=165 */
+ 0x5d, 0x3c, /* BLCKL=60 */
+ 0x5e, 0x3a, /* BLNNL=58 */
+ 0x5f, 0x3a, /* CCRS=0, BLNVB=58 */
+ 0x60, 0x00, /* NULL */
+
+ /* 0x61 - 0x66 set according to norm */
+
+ 0x67, 0x00, /* 0 : caption 1st byte odd field */
+ 0x68, 0x00, /* 0 : caption 2nd byte odd field */
+ 0x69, 0x00, /* 0 : caption 1st byte even field */
+ 0x6a, 0x00, /* 0 : caption 2nd byte even field */
+
+ 0x6b, 0x91, /* MODIN=2, PCREF=0, SCCLN=17 */
+ 0x6c, 0x20, /* SRCV1=0, TRCV2=1, ORCV1=0, PRCV1=0,
+ * CBLF=0, ORCV2=0, PRCV2=0 */
+ 0x6d, 0x00, /* SRCM1=0, CCEN=0 */
+
+ 0x6e, 0x0e, /* HTRIG=0x005, approx. centered, at
+ * least for PAL */
+ 0x6f, 0x00, /* HTRIG upper bits */
+ 0x70, 0x20, /* PHRES=0, SBLN=1, VTRIG=0 */
+
+ /* The following should not be needed */
+
+ 0x71, 0x15, /* BMRQ=0x115 */
+ 0x72, 0x90, /* EMRQ=0x690 */
+ 0x73, 0x61, /* EMRQ=0x690, BMRQ=0x115 */
+ 0x74, 0x00, /* NULL */
+ 0x75, 0x00, /* NULL */
+ 0x76, 0x00, /* NULL */
+ 0x77, 0x15, /* BRCV=0x115 */
+ 0x78, 0x90, /* ERCV=0x690 */
+ 0x79, 0x61, /* ERCV=0x690, BRCV=0x115 */
+
+ /* Field length controls */
+
+ 0x7a, 0x70, /* FLC=0 */
+
+ /* The following should not be needed if SBLN = 1 */
+
+ 0x7b, 0x16, /* FAL=22 */
+ 0x7c, 0x35, /* LAL=244 */
+ 0x7d, 0x20, /* LAL=244, FAL=22 */
+};
+
+static const unsigned char init_pal[] = {
+ 0x61, 0x1e, /* FISE=0, PAL=1, SCBW=1, RTCE=1,
+ * YGS=1, INPI=0, DOWN=0 */
+ 0x62, 0xc8, /* DECTYP=1, BSTA=72 */
+ 0x63, 0xcb, /* FSC0 */
+ 0x64, 0x8a, /* FSC1 */
+ 0x65, 0x09, /* FSC2 */
+ 0x66, 0x2a, /* FSC3 */
+};
+
+static const unsigned char init_ntsc[] = {
+ 0x61, 0x1d, /* FISE=1, PAL=0, SCBW=1, RTCE=1,
+ * YGS=1, INPI=0, DOWN=0 */
+ 0x62, 0xe6, /* DECTYP=1, BSTA=102 */
+ 0x63, 0x1f, /* FSC0 */
+ 0x64, 0x7c, /* FSC1 */
+ 0x65, 0xf0, /* FSC2 */
+ 0x66, 0x21, /* FSC3 */
+};
+
+static int
+saa7185_command (struct i2c_client *client,
+ unsigned int cmd,
+ void *arg)
+{
+ struct saa7185 *encoder = i2c_get_clientdata(client);
+
+ switch (cmd) {
+
+ case 0:
+ saa7185_write_block(client, init_common,
+ sizeof(init_common));
+ switch (encoder->norm) {
+
+ case VIDEO_MODE_NTSC:
+ saa7185_write_block(client, init_ntsc,
+ sizeof(init_ntsc));
+ break;
+
+ case VIDEO_MODE_PAL:
+ saa7185_write_block(client, init_pal,
+ sizeof(init_pal));
+ break;
+ }
+
+ break;
+
+ case ENCODER_GET_CAPABILITIES:
+ {
+ struct video_encoder_capability *cap = arg;
+
+ cap->flags =
+ VIDEO_ENCODER_PAL | VIDEO_ENCODER_NTSC |
+ VIDEO_ENCODER_SECAM | VIDEO_ENCODER_CCIR;
+ cap->inputs = 1;
+ cap->outputs = 1;
+ }
+ break;
+
+ case ENCODER_SET_NORM:
+ {
+ int *iarg = arg;
+
+ //saa7185_write_block(client, init_common, sizeof(init_common));
+
+ switch (*iarg) {
+
+ case VIDEO_MODE_NTSC:
+ saa7185_write_block(client, init_ntsc,
+ sizeof(init_ntsc));
+ break;
+
+ case VIDEO_MODE_PAL:
+ saa7185_write_block(client, init_pal,
+ sizeof(init_pal));
+ break;
+
+ case VIDEO_MODE_SECAM:
+ default:
+ return -EINVAL;
+
+ }
+ encoder->norm = *iarg;
+ }
+ break;
+
+ case ENCODER_SET_INPUT:
+ {
+ int *iarg = arg;
+
+ /* RJ: *iarg = 0: input is from SA7111
+ *iarg = 1: input is from ZR36060 */
+
+ switch (*iarg) {
+
+ case 0:
+ /* Switch RTCE to 1 */
+ saa7185_write(client, 0x61,
+ (encoder->reg[0x61] & 0xf7) | 0x08);
+ saa7185_write(client, 0x6e, 0x01);
+ break;
+
+ case 1:
+ /* Switch RTCE to 0 */
+ saa7185_write(client, 0x61,
+ (encoder->reg[0x61] & 0xf7) | 0x00);
+ /* SW: a slight sync problem... */
+ saa7185_write(client, 0x6e, 0x00);
+ break;
+
+ default:
+ return -EINVAL;
+
+ }
+ }
+ break;
+
+ case ENCODER_SET_OUTPUT:
+ {
+ int *iarg = arg;
+
+ /* not much choice of outputs */
+ if (*iarg != 0) {
+ return -EINVAL;
+ }
+ }
+ break;
+
+ case ENCODER_ENABLE_OUTPUT:
+ {
+ int *iarg = arg;
+
+ encoder->enable = !!*iarg;
+ saa7185_write(client, 0x61,
+ (encoder->reg[0x61] & 0xbf) |
+ (encoder->enable ? 0x00 : 0x40));
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+/*
+ * Generic i2c probe
+ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+ */
+static unsigned short normal_i2c[] = { I2C_SAA7185 >> 1, I2C_CLIENT_END };
+
+static unsigned short ignore = I2C_CLIENT_END;
+
+static struct i2c_client_address_data addr_data = {
+ .normal_i2c = normal_i2c,
+ .probe = &ignore,
+ .ignore = &ignore,
+};
+
+static struct i2c_driver i2c_driver_saa7185;
+
+static int
+saa7185_detect_client (struct i2c_adapter *adapter,
+ int address,
+ int kind)
+{
+ int i;
+ struct i2c_client *client;
+ struct saa7185 *encoder;
+
+ dprintk(1,
+ KERN_INFO
+ "saa7185.c: detecting saa7185 client on address 0x%x\n",
+ address << 1);
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return 0;
+
+ client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (client == 0)
+ return -ENOMEM;
+ client->addr = address;
+ client->adapter = adapter;
+ client->driver = &i2c_driver_saa7185;
+ strlcpy(I2C_NAME(client), "saa7185", sizeof(I2C_NAME(client)));
+
+ encoder = kzalloc(sizeof(struct saa7185), GFP_KERNEL);
+ if (encoder == NULL) {
+ kfree(client);
+ return -ENOMEM;
+ }
+ encoder->norm = VIDEO_MODE_NTSC;
+ encoder->enable = 1;
+ i2c_set_clientdata(client, encoder);
+
+ i = i2c_attach_client(client);
+ if (i) {
+ kfree(client);
+ kfree(encoder);
+ return i;
+ }
+
+ i = saa7185_write_block(client, init_common, sizeof(init_common));
+ if (i >= 0) {
+ i = saa7185_write_block(client, init_ntsc,
+ sizeof(init_ntsc));
+ }
+ if (i < 0) {
+ dprintk(1, KERN_ERR "%s_attach: init error %d\n",
+ I2C_NAME(client), i);
+ } else {
+ dprintk(1,
+ KERN_INFO
+ "%s_attach: chip version %d at address 0x%x\n",
+ I2C_NAME(client), saa7185_read(client) >> 5,
+ client->addr << 1);
+ }
+
+ return 0;
+}
+
+static int
+saa7185_attach_adapter (struct i2c_adapter *adapter)
+{
+ dprintk(1,
+ KERN_INFO
+ "saa7185.c: starting probe for adapter %s (0x%x)\n",
+ I2C_NAME(adapter), adapter->id);
+ return i2c_probe(adapter, &addr_data, &saa7185_detect_client);
+}
+
+static int
+saa7185_detach_client (struct i2c_client *client)
+{
+ struct saa7185 *encoder = i2c_get_clientdata(client);
+ int err;
+
+ err = i2c_detach_client(client);
+ if (err) {
+ return err;
+ }
+
+ saa7185_write(client, 0x61, (encoder->reg[0x61]) | 0x40); /* SW: output off is active */
+ //saa7185_write(client, 0x3a, (encoder->reg[0x3a]) | 0x80); /* SW: color bar */
+
+ kfree(encoder);
+ kfree(client);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_driver i2c_driver_saa7185 = {
+ .driver = {
+ .name = "saa7185", /* name */
+ },
+
+ .id = I2C_DRIVERID_SAA7185B,
+
+ .attach_adapter = saa7185_attach_adapter,
+ .detach_client = saa7185_detach_client,
+ .command = saa7185_command,
+};
+
+static int __init
+saa7185_init (void)
+{
+ return i2c_add_driver(&i2c_driver_saa7185);
+}
+
+static void __exit
+saa7185_exit (void)
+{
+ i2c_del_driver(&i2c_driver_saa7185);
+}
+
+module_init(saa7185_init);
+module_exit(saa7185_exit);
diff --git a/linux/drivers/media/video/saa7191.c b/linux/drivers/media/video/saa7191.c
new file mode 100644
index 000000000..746cadb8f
--- /dev/null
+++ b/linux/drivers/media/video/saa7191.c
@@ -0,0 +1,808 @@
+/*
+ * saa7191.c - Philips SAA7191 video decoder driver
+ *
+ * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <linux/videodev.h>
+#include <linux/video_decoder.h>
+#include <linux/i2c.h>
+
+#include "saa7191.h"
+
+#define SAA7191_MODULE_VERSION "0.0.5"
+
+MODULE_DESCRIPTION("Philips SAA7191 video decoder driver");
+MODULE_VERSION(SAA7191_MODULE_VERSION);
+MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>");
+MODULE_LICENSE("GPL");
+
+// #define SAA7191_DEBUG
+
+#ifdef SAA7191_DEBUG
+#define dprintk(x...) printk("SAA7191: " x);
+#else
+#define dprintk(x...)
+#endif
+
+#define SAA7191_SYNC_COUNT 30
+#define SAA7191_SYNC_DELAY 100 /* milliseconds */
+
+struct saa7191 {
+ struct i2c_client *client;
+
+ /* the register values are stored here as the actual
+ * I2C-registers are write-only */
+ u8 reg[25];
+
+ int input;
+ int norm;
+};
+
+static struct i2c_driver i2c_driver_saa7191;
+
+static const u8 initseq[] = {
+ 0, /* Subaddress */
+
+ 0x50, /* (0x50) SAA7191_REG_IDEL */
+
+ /* 50 Hz signal timing */
+ 0x30, /* (0x30) SAA7191_REG_HSYB */
+ 0x00, /* (0x00) SAA7191_REG_HSYS */
+ 0xe8, /* (0xe8) SAA7191_REG_HCLB */
+ 0xb6, /* (0xb6) SAA7191_REG_HCLS */
+ 0xf4, /* (0xf4) SAA7191_REG_HPHI */
+
+ /* control */
+ SAA7191_LUMA_APER_1, /* (0x01) SAA7191_REG_LUMA - CVBS mode */
+ 0x00, /* (0x00) SAA7191_REG_HUEC */
+ 0xf8, /* (0xf8) SAA7191_REG_CKTQ */
+ 0xf8, /* (0xf8) SAA7191_REG_CKTS */
+ 0x90, /* (0x90) SAA7191_REG_PLSE */
+ 0x90, /* (0x90) SAA7191_REG_SESE */
+ 0x00, /* (0x00) SAA7191_REG_GAIN */
+ SAA7191_STDC_NFEN | SAA7191_STDC_HRMV, /* (0x0c) SAA7191_REG_STDC
+ * - not SECAM,
+ * slow time constant */
+ SAA7191_IOCK_OEDC | SAA7191_IOCK_OEHS | SAA7191_IOCK_OEVS
+ | SAA7191_IOCK_OEDY, /* (0x78) SAA7191_REG_IOCK
+ * - chroma from CVBS, GPSW1 & 2 off */
+ SAA7191_CTL3_AUFD | SAA7191_CTL3_SCEN | SAA7191_CTL3_OFTS
+ | SAA7191_CTL3_YDEL0, /* (0x99) SAA7191_REG_CTL3
+ * - automatic field detection */
+ 0x00, /* (0x00) SAA7191_REG_CTL4 */
+ 0x2c, /* (0x2c) SAA7191_REG_CHCV - PAL nominal value */
+ 0x00, /* unused */
+ 0x00, /* unused */
+
+ /* 60 Hz signal timing */
+ 0x34, /* (0x34) SAA7191_REG_HS6B */
+ 0x0a, /* (0x0a) SAA7191_REG_HS6S */
+ 0xf4, /* (0xf4) SAA7191_REG_HC6B */
+ 0xce, /* (0xce) SAA7191_REG_HC6S */
+ 0xf4, /* (0xf4) SAA7191_REG_HP6I */
+};
+
+/* SAA7191 register handling */
+
+static u8 saa7191_read_reg(struct i2c_client *client,
+ u8 reg)
+{
+ return ((struct saa7191 *)i2c_get_clientdata(client))->reg[reg];
+}
+
+static int saa7191_read_status(struct i2c_client *client,
+ u8 *value)
+{
+ int ret;
+
+ ret = i2c_master_recv(client, value, 1);
+ if (ret < 0) {
+ printk(KERN_ERR "SAA7191: saa7191_read_status(): read failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+
+static int saa7191_write_reg(struct i2c_client *client, u8 reg,
+ u8 value)
+{
+ ((struct saa7191 *)i2c_get_clientdata(client))->reg[reg] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* the first byte of data must be the first subaddress number (register) */
+static int saa7191_write_block(struct i2c_client *client,
+ u8 length, u8 *data)
+{
+ int i;
+ int ret;
+
+ struct saa7191 *decoder = (struct saa7191 *)i2c_get_clientdata(client);
+ for (i = 0; i < (length - 1); i++) {
+ decoder->reg[data[0] + i] = data[i + 1];
+ }
+
+ ret = i2c_master_send(client, data, length);
+ if (ret < 0) {
+ printk(KERN_ERR "SAA7191: saa7191_write_block(): "
+ "write failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Helper functions */
+
+static int saa7191_set_input(struct i2c_client *client, int input)
+{
+ struct saa7191 *decoder = i2c_get_clientdata(client);
+ u8 luma = saa7191_read_reg(client, SAA7191_REG_LUMA);
+ u8 iock = saa7191_read_reg(client, SAA7191_REG_IOCK);
+ int err;
+
+ switch (input) {
+ case SAA7191_INPUT_COMPOSITE: /* Set Composite input */
+ iock &= ~(SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW1
+ | SAA7191_IOCK_GPSW2);
+ /* Chrominance trap active */
+ luma &= ~SAA7191_LUMA_BYPS;
+ break;
+ case SAA7191_INPUT_SVIDEO: /* Set S-Video input */
+ iock |= SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW2;
+ /* Chrominance trap bypassed */
+ luma |= SAA7191_LUMA_BYPS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ err = saa7191_write_reg(client, SAA7191_REG_LUMA, luma);
+ if (err)
+ return -EIO;
+ err = saa7191_write_reg(client, SAA7191_REG_IOCK, iock);
+ if (err)
+ return -EIO;
+
+ decoder->input = input;
+
+ return 0;
+}
+
+static int saa7191_set_norm(struct i2c_client *client, int norm)
+{
+ struct saa7191 *decoder = i2c_get_clientdata(client);
+ u8 stdc = saa7191_read_reg(client, SAA7191_REG_STDC);
+ u8 ctl3 = saa7191_read_reg(client, SAA7191_REG_CTL3);
+ u8 chcv = saa7191_read_reg(client, SAA7191_REG_CHCV);
+ int err;
+
+ switch(norm) {
+ case SAA7191_NORM_PAL:
+ stdc &= ~SAA7191_STDC_SECS;
+ ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL);
+ chcv = SAA7191_CHCV_PAL;
+ break;
+ case SAA7191_NORM_NTSC:
+ stdc &= ~SAA7191_STDC_SECS;
+ ctl3 &= ~SAA7191_CTL3_AUFD;
+ ctl3 |= SAA7191_CTL3_FSEL;
+ chcv = SAA7191_CHCV_NTSC;
+ break;
+ case SAA7191_NORM_SECAM:
+ stdc |= SAA7191_STDC_SECS;
+ ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL);
+ chcv = SAA7191_CHCV_PAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3);
+ if (err)
+ return -EIO;
+ err = saa7191_write_reg(client, SAA7191_REG_STDC, stdc);
+ if (err)
+ return -EIO;
+ err = saa7191_write_reg(client, SAA7191_REG_CHCV, chcv);
+ if (err)
+ return -EIO;
+
+ decoder->norm = norm;
+
+ dprintk("ctl3: %02x stdc: %02x chcv: %02x\n", ctl3,
+ stdc, chcv);
+ dprintk("norm: %d\n", norm);
+
+ return 0;
+}
+
+static int saa7191_wait_for_signal(struct i2c_client *client, u8 *status)
+{
+ int i = 0;
+
+ dprintk("Checking for signal...\n");
+
+ for (i = 0; i < SAA7191_SYNC_COUNT; i++) {
+ if (saa7191_read_status(client, status))
+ return -EIO;
+
+ if (((*status) & SAA7191_STATUS_HLCK) == 0) {
+ dprintk("Signal found\n");
+ return 0;
+ }
+
+ msleep(SAA7191_SYNC_DELAY);
+ }
+
+ dprintk("No signal\n");
+
+ return -EBUSY;
+}
+
+static int saa7191_autodetect_norm_extended(struct i2c_client *client)
+{
+ u8 stdc = saa7191_read_reg(client, SAA7191_REG_STDC);
+ u8 ctl3 = saa7191_read_reg(client, SAA7191_REG_CTL3);
+ u8 status;
+ int err = 0;
+
+ dprintk("SAA7191 extended signal auto-detection...\n");
+
+ stdc &= ~SAA7191_STDC_SECS;
+ ctl3 &= ~(SAA7191_CTL3_FSEL);
+
+ err = saa7191_write_reg(client, SAA7191_REG_STDC, stdc);
+ if (err) {
+ err = -EIO;
+ goto out;
+ }
+ err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3);
+ if (err) {
+ err = -EIO;
+ goto out;
+ }
+
+ ctl3 |= SAA7191_CTL3_AUFD;
+ err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3);
+ if (err) {
+ err = -EIO;
+ goto out;
+ }
+
+ msleep(SAA7191_SYNC_DELAY);
+
+ err = saa7191_wait_for_signal(client, &status);
+ if (err)
+ goto out;
+
+ if (status & SAA7191_STATUS_FIDT) {
+ /* 60Hz signal -> NTSC */
+ dprintk("60Hz signal: NTSC\n");
+ return saa7191_set_norm(client, SAA7191_NORM_NTSC);
+ }
+
+ /* 50Hz signal */
+ dprintk("50Hz signal: Trying PAL...\n");
+
+ /* try PAL first */
+ err = saa7191_set_norm(client, SAA7191_NORM_PAL);
+ if (err)
+ goto out;
+
+ msleep(SAA7191_SYNC_DELAY);
+
+ err = saa7191_wait_for_signal(client, &status);
+ if (err)
+ goto out;
+
+ /* not 50Hz ? */
+ if (status & SAA7191_STATUS_FIDT) {
+ dprintk("No 50Hz signal\n");
+ err = -EAGAIN;
+ goto out;
+ }
+
+ if (status & SAA7191_STATUS_CODE) {
+ dprintk("PAL\n");
+ return 0;
+ }
+
+ dprintk("No color detected with PAL - Trying SECAM...\n");
+
+ /* no color detected ? -> try SECAM */
+ err = saa7191_set_norm(client,
+ SAA7191_NORM_SECAM);
+ if (err)
+ goto out;
+
+ msleep(SAA7191_SYNC_DELAY);
+
+ err = saa7191_wait_for_signal(client, &status);
+ if (err)
+ goto out;
+
+ /* not 50Hz ? */
+ if (status & SAA7191_STATUS_FIDT) {
+ dprintk("No 50Hz signal\n");
+ err = -EAGAIN;
+ goto out;
+ }
+
+ if (status & SAA7191_STATUS_CODE) {
+ /* Color detected -> SECAM */
+ dprintk("SECAM\n");
+ return 0;
+ }
+
+ dprintk("No color detected with SECAM - Going back to PAL.\n");
+
+ /* still no color detected ?
+ * -> set norm back to PAL */
+ err = saa7191_set_norm(client,
+ SAA7191_NORM_PAL);
+ if (err)
+ goto out;
+
+out:
+ ctl3 = saa7191_read_reg(client, SAA7191_REG_CTL3);
+ if (ctl3 & SAA7191_CTL3_AUFD) {
+ ctl3 &= ~(SAA7191_CTL3_AUFD);
+ err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3);
+ if (err) {
+ err = -EIO;
+ }
+ }
+
+ return err;
+}
+
+static int saa7191_autodetect_norm(struct i2c_client *client)
+{
+ u8 status;
+
+ dprintk("SAA7191 signal auto-detection...\n");
+
+ dprintk("Reading status...\n");
+
+ if (saa7191_read_status(client, &status))
+ return -EIO;
+
+ dprintk("Checking for signal...\n");
+
+ /* no signal ? */
+ if (status & SAA7191_STATUS_HLCK) {
+ dprintk("No signal\n");
+ return -EBUSY;
+ }
+
+ dprintk("Signal found\n");
+
+ if (status & SAA7191_STATUS_FIDT) {
+ /* 60hz signal -> NTSC */
+ dprintk("NTSC\n");
+ return saa7191_set_norm(client, SAA7191_NORM_NTSC);
+ } else {
+ /* 50hz signal -> PAL */
+ dprintk("PAL\n");
+ return saa7191_set_norm(client, SAA7191_NORM_PAL);
+ }
+}
+
+static int saa7191_get_control(struct i2c_client *client,
+ struct saa7191_control *ctrl)
+{
+ u8 reg;
+ int ret = 0;
+
+ switch (ctrl->type) {
+ case SAA7191_CONTROL_BANDPASS:
+ case SAA7191_CONTROL_BANDPASS_WEIGHT:
+ case SAA7191_CONTROL_CORING:
+ reg = saa7191_read_reg(client, SAA7191_REG_LUMA);
+ switch (ctrl->type) {
+ case SAA7191_CONTROL_BANDPASS:
+ ctrl->value = ((s32)reg & SAA7191_LUMA_BPSS_MASK)
+ >> SAA7191_LUMA_BPSS_SHIFT;
+ break;
+ case SAA7191_CONTROL_BANDPASS_WEIGHT:
+ ctrl->value = ((s32)reg & SAA7191_LUMA_APER_MASK)
+ >> SAA7191_LUMA_APER_SHIFT;
+ break;
+ case SAA7191_CONTROL_CORING:
+ ctrl->value = ((s32)reg & SAA7191_LUMA_CORI_MASK)
+ >> SAA7191_LUMA_CORI_SHIFT;
+ break;
+ }
+ break;
+ case SAA7191_CONTROL_FORCE_COLOUR:
+ case SAA7191_CONTROL_CHROMA_GAIN:
+ reg = saa7191_read_reg(client, SAA7191_REG_GAIN);
+ if (ctrl->type == SAA7191_CONTROL_FORCE_COLOUR)
+ ctrl->value = ((s32)reg & SAA7191_GAIN_COLO) ? 1 : 0;
+ else
+ ctrl->value = ((s32)reg & SAA7191_GAIN_LFIS_MASK)
+ >> SAA7191_GAIN_LFIS_SHIFT;
+ break;
+ case SAA7191_CONTROL_HUE:
+ reg = saa7191_read_reg(client, SAA7191_REG_HUEC);
+ if (reg < 0x80)
+ reg += 0x80;
+ else
+ reg -= 0x80;
+ ctrl->value = (s32)reg;
+ break;
+ case SAA7191_CONTROL_VTRC:
+ reg = saa7191_read_reg(client, SAA7191_REG_STDC);
+ ctrl->value = ((s32)reg & SAA7191_STDC_VTRC) ? 1 : 0;
+ break;
+ case SAA7191_CONTROL_LUMA_DELAY:
+ reg = saa7191_read_reg(client, SAA7191_REG_CTL3);
+ ctrl->value = ((s32)reg & SAA7191_CTL3_YDEL_MASK)
+ >> SAA7191_CTL3_YDEL_SHIFT;
+ if (ctrl->value >= 4)
+ ctrl->value -= 8;
+ break;
+ case SAA7191_CONTROL_VNR:
+ reg = saa7191_read_reg(client, SAA7191_REG_CTL4);
+ ctrl->value = ((s32)reg & SAA7191_CTL4_VNOI_MASK)
+ >> SAA7191_CTL4_VNOI_SHIFT;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int saa7191_set_control(struct i2c_client *client,
+ struct saa7191_control *ctrl)
+{
+ u8 reg;
+ int ret = 0;
+
+ switch (ctrl->type) {
+ case SAA7191_CONTROL_BANDPASS:
+ case SAA7191_CONTROL_BANDPASS_WEIGHT:
+ case SAA7191_CONTROL_CORING:
+ reg = saa7191_read_reg(client, SAA7191_REG_LUMA);
+ switch (ctrl->type) {
+ case SAA7191_CONTROL_BANDPASS:
+ reg &= ~SAA7191_LUMA_BPSS_MASK;
+ reg |= (ctrl->value << SAA7191_LUMA_BPSS_SHIFT)
+ & SAA7191_LUMA_BPSS_MASK;
+ break;
+ case SAA7191_CONTROL_BANDPASS_WEIGHT:
+ reg &= ~SAA7191_LUMA_APER_MASK;
+ reg |= (ctrl->value << SAA7191_LUMA_APER_SHIFT)
+ & SAA7191_LUMA_APER_MASK;
+ break;
+ case SAA7191_CONTROL_CORING:
+ reg &= ~SAA7191_LUMA_CORI_MASK;
+ reg |= (ctrl->value << SAA7191_LUMA_CORI_SHIFT)
+ & SAA7191_LUMA_CORI_MASK;
+ break;
+ }
+ ret = saa7191_write_reg(client, SAA7191_REG_LUMA, reg);
+ break;
+ case SAA7191_CONTROL_FORCE_COLOUR:
+ case SAA7191_CONTROL_CHROMA_GAIN:
+ reg = saa7191_read_reg(client, SAA7191_REG_GAIN);
+ if (ctrl->type == SAA7191_CONTROL_FORCE_COLOUR) {
+ if (ctrl->value)
+ reg |= SAA7191_GAIN_COLO;
+ else
+ reg &= ~SAA7191_GAIN_COLO;
+ } else {
+ reg &= ~SAA7191_GAIN_LFIS_MASK;
+ reg |= (ctrl->value << SAA7191_GAIN_LFIS_SHIFT)
+ & SAA7191_GAIN_LFIS_MASK;
+ }
+ ret = saa7191_write_reg(client, SAA7191_REG_GAIN, reg);
+ break;
+ case SAA7191_CONTROL_HUE:
+ reg = ctrl->value & 0xff;
+ if (reg < 0x80)
+ reg += 0x80;
+ else
+ reg -= 0x80;
+ ret = saa7191_write_reg(client, SAA7191_REG_HUEC, reg);
+ break;
+ case SAA7191_CONTROL_VTRC:
+ reg = saa7191_read_reg(client, SAA7191_REG_STDC);
+ if (ctrl->value)
+ reg |= SAA7191_STDC_VTRC;
+ else
+ reg &= ~SAA7191_STDC_VTRC;
+ ret = saa7191_write_reg(client, SAA7191_REG_STDC, reg);
+ break;
+ case SAA7191_CONTROL_LUMA_DELAY: {
+ s32 value = ctrl->value;
+ if (value < 0)
+ value += 8;
+ reg = saa7191_read_reg(client, SAA7191_REG_CTL3);
+ reg &= ~SAA7191_CTL3_YDEL_MASK;
+ reg |= (value << SAA7191_CTL3_YDEL_SHIFT)
+ & SAA7191_CTL3_YDEL_MASK;
+ ret = saa7191_write_reg(client, SAA7191_REG_CTL3, reg);
+ break;
+ }
+ case SAA7191_CONTROL_VNR:
+ reg = saa7191_read_reg(client, SAA7191_REG_CTL4);
+ reg &= ~SAA7191_CTL4_VNOI_MASK;
+ reg |= (ctrl->value << SAA7191_CTL4_VNOI_SHIFT)
+ & SAA7191_CTL4_VNOI_MASK;
+ ret = saa7191_write_reg(client, SAA7191_REG_CTL4, reg);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/* I2C-interface */
+
+static int saa7191_attach(struct i2c_adapter *adap, int addr, int kind)
+{
+ int err = 0;
+ struct saa7191 *decoder;
+ struct i2c_client *client;
+
+ printk(KERN_INFO "Philips SAA7191 driver version %s\n",
+ SAA7191_MODULE_VERSION);
+
+ client = kzalloc(sizeof(*client), GFP_KERNEL);
+ if (!client)
+ return -ENOMEM;
+ decoder = kzalloc(sizeof(*decoder), GFP_KERNEL);
+ if (!decoder) {
+ err = -ENOMEM;
+ goto out_free_client;
+ }
+
+ client->addr = addr;
+ client->adapter = adap;
+ client->driver = &i2c_driver_saa7191;
+ client->flags = 0;
+ strcpy(client->name, "saa7191 client");
+ i2c_set_clientdata(client, decoder);
+
+ decoder->client = client;
+
+ err = i2c_attach_client(client);
+ if (err)
+ goto out_free_decoder;
+
+ err = saa7191_write_block(client, sizeof(initseq), (u8 *)initseq);
+ if (err) {
+ printk(KERN_ERR "SAA7191 initialization failed\n");
+ goto out_detach_client;
+ }
+
+ printk(KERN_INFO "SAA7191 initialized\n");
+
+ decoder->input = SAA7191_INPUT_COMPOSITE;
+ decoder->norm = SAA7191_NORM_PAL;
+
+ err = saa7191_autodetect_norm(client);
+ if (err && (err != -EBUSY)) {
+ printk(KERN_ERR "SAA7191: Signal auto-detection failed\n");
+ }
+
+ return 0;
+
+out_detach_client:
+ i2c_detach_client(client);
+out_free_decoder:
+ kfree(decoder);
+out_free_client:
+ kfree(client);
+ return err;
+}
+
+static int saa7191_probe(struct i2c_adapter *adap)
+{
+ /* Always connected to VINO */
+ if (adap->id == I2C_HW_SGI_VINO)
+ return saa7191_attach(adap, SAA7191_ADDR, 0);
+ /* Feel free to add probe here :-) */
+ return -ENODEV;
+}
+
+static int saa7191_detach(struct i2c_client *client)
+{
+ struct saa7191 *decoder = i2c_get_clientdata(client);
+
+ i2c_detach_client(client);
+ kfree(decoder);
+ kfree(client);
+ return 0;
+}
+
+static int saa7191_command(struct i2c_client *client, unsigned int cmd,
+ void *arg)
+{
+ struct saa7191 *decoder = i2c_get_clientdata(client);
+
+ switch (cmd) {
+ case DECODER_GET_CAPABILITIES: {
+ struct video_decoder_capability *cap = arg;
+
+ cap->flags = VIDEO_DECODER_PAL | VIDEO_DECODER_NTSC |
+ VIDEO_DECODER_SECAM | VIDEO_DECODER_AUTO;
+ cap->inputs = (client->adapter->id == I2C_HW_SGI_VINO) ? 2 : 1;
+ cap->outputs = 1;
+ break;
+ }
+ case DECODER_GET_STATUS: {
+ int *iarg = arg;
+ u8 status;
+ int res = 0;
+
+ if (saa7191_read_status(client, &status)) {
+ return -EIO;
+ }
+ if ((status & SAA7191_STATUS_HLCK) == 0)
+ res |= DECODER_STATUS_GOOD;
+ if (status & SAA7191_STATUS_CODE)
+ res |= DECODER_STATUS_COLOR;
+ switch (decoder->norm) {
+ case SAA7191_NORM_NTSC:
+ res |= DECODER_STATUS_NTSC;
+ break;
+ case SAA7191_NORM_PAL:
+ res |= DECODER_STATUS_PAL;
+ break;
+ case SAA7191_NORM_SECAM:
+ res |= DECODER_STATUS_SECAM;
+ break;
+ case SAA7191_NORM_AUTO:
+ default:
+ if (status & SAA7191_STATUS_FIDT)
+ res |= DECODER_STATUS_NTSC;
+ else
+ res |= DECODER_STATUS_PAL;
+ break;
+ }
+ *iarg = res;
+ break;
+ }
+ case DECODER_SET_NORM: {
+ int *iarg = arg;
+
+ switch (*iarg) {
+ case VIDEO_MODE_AUTO:
+ return saa7191_autodetect_norm(client);
+ case VIDEO_MODE_PAL:
+ return saa7191_set_norm(client, SAA7191_NORM_PAL);
+ case VIDEO_MODE_NTSC:
+ return saa7191_set_norm(client, SAA7191_NORM_NTSC);
+ case VIDEO_MODE_SECAM:
+ return saa7191_set_norm(client, SAA7191_NORM_SECAM);
+ default:
+ return -EINVAL;
+ }
+ break;
+ }
+ case DECODER_SET_INPUT: {
+ int *iarg = arg;
+
+ switch (client->adapter->id) {
+ case I2C_HW_SGI_VINO:
+ return saa7191_set_input(client, *iarg);
+ default:
+ if (*iarg != 0)
+ return -EINVAL;
+ }
+ break;
+ }
+ case DECODER_SET_OUTPUT: {
+ int *iarg = arg;
+
+ /* not much choice of outputs */
+ if (*iarg != 0)
+ return -EINVAL;
+ break;
+ }
+ case DECODER_ENABLE_OUTPUT: {
+ /* Always enabled */
+ break;
+ }
+ case DECODER_SET_PICTURE: {
+ struct video_picture *pic = arg;
+ unsigned val;
+ int err;
+
+ val = (pic->hue >> 8) - 0x80;
+
+ err = saa7191_write_reg(client, SAA7191_REG_HUEC, val);
+ if (err)
+ return -EIO;
+
+ break;
+ }
+ case DECODER_SAA7191_GET_STATUS: {
+ struct saa7191_status *status = arg;
+ u8 status_reg;
+
+ if (saa7191_read_status(client, &status_reg))
+ return -EIO;
+
+ status->signal = ((status_reg & SAA7191_STATUS_HLCK) == 0)
+ ? 1 : 0;
+ status->signal_60hz = (status_reg & SAA7191_STATUS_FIDT)
+ ? 1 : 0;
+ status->color = (status_reg & SAA7191_STATUS_CODE) ? 1 : 0;
+
+ status->input = decoder->input;
+ status->norm = decoder->norm;
+
+ break;
+ }
+ case DECODER_SAA7191_SET_NORM: {
+ int *norm = arg;
+
+ switch (*norm) {
+ case SAA7191_NORM_AUTO:
+ return saa7191_autodetect_norm(client);
+ case SAA7191_NORM_AUTO_EXT:
+ return saa7191_autodetect_norm_extended(client);
+ default:
+ return saa7191_set_norm(client, *norm);
+ }
+ }
+ case DECODER_SAA7191_GET_CONTROL: {
+ return saa7191_get_control(client, arg);
+ }
+ case DECODER_SAA7191_SET_CONTROL: {
+ return saa7191_set_control(client, arg);
+ }
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct i2c_driver i2c_driver_saa7191 = {
+ .driver = {
+ .name = "saa7191",
+ },
+ .id = I2C_DRIVERID_SAA7191,
+ .attach_adapter = saa7191_probe,
+ .detach_client = saa7191_detach,
+ .command = saa7191_command
+};
+
+static int saa7191_init(void)
+{
+ return i2c_add_driver(&i2c_driver_saa7191);
+}
+
+static void saa7191_exit(void)
+{
+ i2c_del_driver(&i2c_driver_saa7191);
+}
+
+module_init(saa7191_init);
+module_exit(saa7191_exit);
diff --git a/linux/drivers/media/video/saa7191.h b/linux/drivers/media/video/saa7191.h
new file mode 100644
index 000000000..a2310da19
--- /dev/null
+++ b/linux/drivers/media/video/saa7191.h
@@ -0,0 +1,255 @@
+/*
+ * saa7191.h - Philips SAA7191 video decoder driver
+ *
+ * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _SAA7191_H_
+#define _SAA7191_H_
+
+/* Philips SAA7191 DMSD I2C bus address */
+#define SAA7191_ADDR 0x8a
+
+/* Register subaddresses. */
+#define SAA7191_REG_IDEL 0x00
+#define SAA7191_REG_HSYB 0x01
+#define SAA7191_REG_HSYS 0x02
+#define SAA7191_REG_HCLB 0x03
+#define SAA7191_REG_HCLS 0x04
+#define SAA7191_REG_HPHI 0x05
+#define SAA7191_REG_LUMA 0x06
+#define SAA7191_REG_HUEC 0x07
+#define SAA7191_REG_CKTQ 0x08 /* bits 3-7 */
+#define SAA7191_REG_CKTS 0x09 /* bits 3-7 */
+#define SAA7191_REG_PLSE 0x0a
+#define SAA7191_REG_SESE 0x0b
+#define SAA7191_REG_GAIN 0x0c
+#define SAA7191_REG_STDC 0x0d
+#define SAA7191_REG_IOCK 0x0e
+#define SAA7191_REG_CTL3 0x0f
+#define SAA7191_REG_CTL4 0x10
+#define SAA7191_REG_CHCV 0x11
+#define SAA7191_REG_HS6B 0x14
+#define SAA7191_REG_HS6S 0x15
+#define SAA7191_REG_HC6B 0x16
+#define SAA7191_REG_HC6S 0x17
+#define SAA7191_REG_HP6I 0x18
+#define SAA7191_REG_STATUS 0xff /* not really a subaddress */
+
+/* Status Register definitions */
+#define SAA7191_STATUS_CODE 0x01 /* color detected flag */
+#define SAA7191_STATUS_FIDT 0x20 /* signal type 50/60 Hz */
+#define SAA7191_STATUS_HLCK 0x40 /* PLL unlocked(1)/locked(0) */
+#define SAA7191_STATUS_STTC 0x80 /* tv/vtr time constant */
+
+/* Luminance Control Register definitions */
+/* input mode select bit:
+ * 0=CVBS (chrominance trap active), 1=S-Video (trap bypassed) */
+#define SAA7191_LUMA_BYPS 0x80
+/* pre-filter (only when chrominance trap is active) */
+#define SAA7191_LUMA_PREF 0x40
+/* aperture bandpass to select different characteristics with maximums
+ * (bits 4-5) */
+#define SAA7191_LUMA_BPSS_MASK 0x30
+#define SAA7191_LUMA_BPSS_SHIFT 4
+#define SAA7191_LUMA_BPSS_3 0x30
+#define SAA7191_LUMA_BPSS_2 0x20
+#define SAA7191_LUMA_BPSS_1 0x10
+#define SAA7191_LUMA_BPSS_0 0x00
+/* coring range for high frequency components according to 8-bit luminance
+ * (bits 2-3)
+ * 0=coring off, n= (+-)n LSB */
+#define SAA7191_LUMA_CORI_MASK 0x0c
+#define SAA7191_LUMA_CORI_SHIFT 2
+#define SAA7191_LUMA_CORI_3 0x0c
+#define SAA7191_LUMA_CORI_2 0x08
+#define SAA7191_LUMA_CORI_1 0x04
+#define SAA7191_LUMA_CORI_0 0x00
+/* aperture bandpass filter weights high frequency components of luminance
+ * signal (bits 0-1)
+ * 0=factor 0, 1=0.25, 2=0.5, 3=1 */
+#define SAA7191_LUMA_APER_MASK 0x03
+#define SAA7191_LUMA_APER_SHIFT 0
+#define SAA7191_LUMA_APER_3 0x03
+#define SAA7191_LUMA_APER_2 0x02
+#define SAA7191_LUMA_APER_1 0x01
+#define SAA7191_LUMA_APER_0 0x00
+
+/* Chrominance Gain Control Settings Register definitions */
+/* colour on: 0=automatic colour-killer enabled, 1=forced colour on */
+#define SAA7191_GAIN_COLO 0x80
+/* chrominance gain control (AGC filter)
+ * 0=loop filter time constant slow, 1=medium, 2=fast, 3=actual gain */
+#define SAA7191_GAIN_LFIS_MASK 0x60
+#define SAA7191_GAIN_LFIS_SHIFT 5
+#define SAA7191_GAIN_LFIS_3 0x60
+#define SAA7191_GAIN_LFIS_2 0x40
+#define SAA7191_GAIN_LFIS_1 0x20
+#define SAA7191_GAIN_LFIS_0 0x00
+
+/* Standard/Mode Control Register definitions */
+/* tv/vtr mode bit: 0=TV mode (slow time constant),
+ * 1=VTR mode (fast time constant) */
+#define SAA7191_STDC_VTRC 0x80
+/* SAA7191B-specific functions enable (RTCO, ODD and GPSW0 outputs)
+ * 0=outputs set to high-impedance (circuit equals SAA7191), 1=enabled */
+#define SAA7191_STDC_NFEN 0x08
+/* HREF generation: 0=like SAA7191, 1=HREF is 8xLLC2 clocks earlier */
+#define SAA7191_STDC_HRMV 0x04
+/* general purpose switch 0
+ * (not used with VINO afaik) */
+#define SAA7191_STDC_GPSW0 0x02
+/* SECAM mode bit: 0=other standards, 1=SECAM */
+#define SAA7191_STDC_SECS 0x01
+
+/* I/O and Clock Control Register definitions */
+/* horizontal clock PLL: 0=PLL closed,
+ * 1=PLL circuit open and horizontal freq fixed */
+#define SAA7191_IOCK_HPLL 0x80
+/* colour-difference output enable (outputs UV0-UV7) */
+#define SAA7191_IOCK_OEDC 0x40
+/* H-sync output enable */
+#define SAA7191_IOCK_OEHS 0x20
+/* V-sync output enable */
+#define SAA7191_IOCK_OEVS 0x10
+/* luminance output enable (outputs Y0-Y7) */
+#define SAA7191_IOCK_OEDY 0x08
+/* S-VHS bit (chrominance from CVBS or from chrominance input):
+ * 0=controlled by BYPS-bit, 1=from chrominance input */
+#define SAA7191_IOCK_CHRS 0x04
+/* general purpose switch 2
+ * VINO-specific: 0=used with CVBS, 1=used with S-Video */
+#define SAA7191_IOCK_GPSW2 0x02
+/* general purpose switch 1 */
+/* VINO-specific: 0=always, 1=not used!*/
+#define SAA7191_IOCK_GPSW1 0x01
+
+/* Miscellaneous Control #1 Register definitions */
+/* automatic field detection (50/60Hz standard) */
+#define SAA7191_CTL3_AUFD 0x80
+/* field select: (if AUFD=0)
+ * 0=50Hz (625 lines), 1=60Hz (525 lines) */
+#define SAA7191_CTL3_FSEL 0x40
+/* SECAM cross-colour reduction enable */
+#define SAA7191_CTL3_SXCR 0x20
+/* sync and clamping pulse enable (HCL and HSY outputs) */
+#define SAA7191_CTL3_SCEN 0x10
+/* output format: 0=4:1:1, 1=4:2:2 (4:2:2 for VINO) */
+#define SAA7191_CTL3_OFTS 0x08
+/* luminance delay compensation
+ * 0=0*2/LLC, 1=+1*2/LLC, 2=+2*2/LLC, 3=+3*2/LLC,
+ * 4=-4*2/LLC, 5=-3*2/LLC, 6=-2*2/LLC, 7=-1*2/LLC
+ * step size = 2/LLC = 67.8ns for 50Hz, 81.5ns for 60Hz */
+#define SAA7191_CTL3_YDEL_MASK 0x07
+#define SAA7191_CTL3_YDEL_SHIFT 0
+#define SAA7191_CTL3_YDEL2 0x04
+#define SAA7191_CTL3_YDEL1 0x02
+#define SAA7191_CTL3_YDEL0 0x01
+
+/* Miscellaneous Control #2 Register definitions */
+/* select HREF position
+ * 0=normal, HREF is matched to YUV output port,
+ * 1=HREF is matched to CVBS input port */
+#define SAA7191_CTL4_HRFS 0x04
+/* vertical noise reduction
+ * 0=normal, 1=searching window, 2=auto-deflection, 3=reduction bypassed */
+#define SAA7191_CTL4_VNOI_MASK 0x03
+#define SAA7191_CTL4_VNOI_SHIFT 0
+#define SAA7191_CTL4_VNOI_3 0x03
+#define SAA7191_CTL4_VNOI_2 0x02
+#define SAA7191_CTL4_VNOI_1 0x01
+#define SAA7191_CTL4_VNOI_0 0x00
+
+/* Chrominance Gain Control Register definitions
+ * - for QAM-modulated input signals, effects output amplitude
+ * (SECAM gain fixed)
+ * (nominal values for UV CCIR level) */
+#define SAA7191_CHCV_NTSC 0x2c
+#define SAA7191_CHCV_PAL 0x59
+
+/* Driver interface definitions */
+#define SAA7191_INPUT_COMPOSITE 0
+#define SAA7191_INPUT_SVIDEO 1
+
+#define SAA7191_NORM_AUTO 0
+#define SAA7191_NORM_PAL 1
+#define SAA7191_NORM_NTSC 2
+#define SAA7191_NORM_SECAM 3
+#define SAA7191_NORM_AUTO_EXT 4 /* extended auto-detection */
+
+struct saa7191_status {
+ /* 0=no signal, 1=signal detected */
+ int signal;
+ /* 0=50hz (pal) signal, 1=60hz (ntsc) signal */
+ int signal_60hz;
+ /* 0=no color detected, 1=color detected */
+ int color;
+
+ /* current SAA7191_INPUT_ */
+ int input;
+ /* current SAA7191_NORM_ */
+ int norm;
+};
+
+#define SAA7191_BANDPASS_MIN 0x00
+#define SAA7191_BANDPASS_MAX 0x03
+#define SAA7191_BANDPASS_DEFAULT 0x00
+
+#define SAA7191_BANDPASS_WEIGHT_MIN 0x00
+#define SAA7191_BANDPASS_WEIGHT_MAX 0x03
+#define SAA7191_BANDPASS_WEIGHT_DEFAULT 0x01
+
+#define SAA7191_CORING_MIN 0x00
+#define SAA7191_CORING_MAX 0x03
+#define SAA7191_CORING_DEFAULT 0x00
+
+#define SAA7191_HUE_MIN 0x00
+#define SAA7191_HUE_MAX 0xff
+#define SAA7191_HUE_DEFAULT 0x80
+
+#define SAA7191_VTRC_MIN 0x00
+#define SAA7191_VTRC_MAX 0x01
+#define SAA7191_VTRC_DEFAULT 0x00
+
+#define SAA7191_FORCE_COLOUR_MIN 0x00
+#define SAA7191_FORCE_COLOUR_MAX 0x01
+#define SAA7191_FORCE_COLOUR_DEFAULT 0x00
+
+#define SAA7191_CHROMA_GAIN_MIN 0x00
+#define SAA7191_CHROMA_GAIN_MAX 0x03
+#define SAA7191_CHROMA_GAIN_DEFAULT 0x00
+
+#define SAA7191_LUMA_DELAY_MIN -0x04
+#define SAA7191_LUMA_DELAY_MAX 0x03
+#define SAA7191_LUMA_DELAY_DEFAULT 0x01
+
+#define SAA7191_VNR_MIN 0x00
+#define SAA7191_VNR_MAX 0x03
+#define SAA7191_VNR_DEFAULT 0x00
+
+#define SAA7191_CONTROL_BANDPASS 0
+#define SAA7191_CONTROL_BANDPASS_WEIGHT 1
+#define SAA7191_CONTROL_CORING 2
+#define SAA7191_CONTROL_FORCE_COLOUR 3 /* boolean */
+#define SAA7191_CONTROL_CHROMA_GAIN 4
+#define SAA7191_CONTROL_HUE 5
+#define SAA7191_CONTROL_VTRC 6 /* boolean */
+#define SAA7191_CONTROL_LUMA_DELAY 7
+#define SAA7191_CONTROL_VNR 8
+
+struct saa7191_control {
+ u8 type;
+ s32 value;
+};
+
+#define DECODER_SAA7191_GET_STATUS _IOR('d', 195, struct saa7191_status)
+#define DECODER_SAA7191_SET_NORM _IOW('d', 196, int)
+#define DECODER_SAA7191_GET_CONTROL _IOR('d', 197, struct saa7191_control)
+#define DECODER_SAA7191_SET_CONTROL _IOW('d', 198, struct saa7191_control)
+
+#endif
diff --git a/linux/drivers/media/video/saa7196.h b/linux/drivers/media/video/saa7196.h
new file mode 100644
index 000000000..cd4b6354a
--- /dev/null
+++ b/linux/drivers/media/video/saa7196.h
@@ -0,0 +1,117 @@
+/*
+ Definitions for the Philips SAA7196 digital video decoder,
+ scaler, and clock generator circuit (DESCpro), as used in
+ the PlanB video input of the Powermac 7x00/8x00 series.
+
+ Copyright (C) 1998 Michel Lanners (mlan@cpu.lu)
+
+ The register defines are shamelessly copied from the meteor
+ driver out of NetBSD (with permission),
+ and are copyrighted (c) 1995 Mark Tinguely and Jim Lowe
+ (Thanks !)
+
+ Additional debugging and coding by Takashi Oe (toe@unlinfo.unl.edu)
+
+ The default values used for PlanB are my mistakes.
+*/
+
+/* $Id: saa7196.h,v 1.5 1999/03/26 23:28:47 mlan Exp $ */
+
+#ifndef _SAA7196_H_
+#define _SAA7196_H_
+
+#define SAA7196_NUMREGS 0x31 /* Number of registers (used)*/
+#define NUM_SUPPORTED_NORM 3 /* Number of supported norms by PlanB */
+
+/* Decoder part: */
+#define SAA7196_IDEL 0x00 /* Increment delay */
+#define SAA7196_HSB5 0x01 /* H-sync begin; 50 hz */
+#define SAA7196_HSS5 0x02 /* H-sync stop; 50 hz */
+#define SAA7196_HCB5 0x03 /* H-clamp begin; 50 hz */
+#define SAA7196_HCS5 0x04 /* H-clamp stop; 50 hz */
+#define SAA7196_HSP5 0x05 /* H-sync after PHI1; 50 hz */
+#define SAA7196_LUMC 0x06 /* Luminance control */
+#define SAA7196_HUEC 0x07 /* Hue control */
+#define SAA7196_CKTQ 0x08 /* Colour Killer Threshold QAM (PAL, NTSC) */
+#define SAA7196_CKTS 0x09 /* Colour Killer Threshold SECAM */
+#define SAA7196_PALS 0x0a /* PAL switch sensitivity */
+#define SAA7196_SECAMS 0x0b /* SECAM switch sensitivity */
+#define SAA7196_CGAINC 0x0c /* Chroma gain control */
+#define SAA7196_STDC 0x0d /* Standard/Mode control */
+#define SAA7196_IOCC 0x0e /* I/O and Clock Control */
+#define SAA7196_CTRL1 0x0f /* Control #1 */
+#define SAA7196_CTRL2 0x10 /* Control #2 */
+#define SAA7196_CGAINR 0x11 /* Chroma Gain Reference */
+#define SAA7196_CSAT 0x12 /* Chroma Saturation */
+#define SAA7196_CONT 0x13 /* Luminance Contrast */
+#define SAA7196_HSB6 0x14 /* H-sync begin; 60 hz */
+#define SAA7196_HSS6 0x15 /* H-sync stop; 60 hz */
+#define SAA7196_HCB6 0x16 /* H-clamp begin; 60 hz */
+#define SAA7196_HCS6 0x17 /* H-clamp stop; 60 hz */
+#define SAA7196_HSP6 0x18 /* H-sync after PHI1; 60 hz */
+#define SAA7196_BRIG 0x19 /* Luminance Brightness */
+
+/* Scaler part: */
+#define SAA7196_FMTS 0x20 /* Formats and sequence */
+#define SAA7196_OUTPIX 0x21 /* Output data pixel/line */
+#define SAA7196_INPIX 0x22 /* Input data pixel/line */
+#define SAA7196_HWS 0x23 /* Horiz. window start */
+#define SAA7196_HFILT 0x24 /* Horiz. filter */
+#define SAA7196_OUTLINE 0x25 /* Output data lines/field */
+#define SAA7196_INLINE 0x26 /* Input data lines/field */
+#define SAA7196_VWS 0x27 /* Vertical window start */
+#define SAA7196_VYP 0x28 /* AFS/vertical Y processing */
+#define SAA7196_VBS 0x29 /* Vertical Bypass start */
+#define SAA7196_VBCNT 0x2a /* Vertical Bypass count */
+#define SAA7196_VBP 0x2b /* veritcal Bypass Polarity */
+#define SAA7196_VLOW 0x2c /* Colour-keying lower V limit */
+#define SAA7196_VHIGH 0x2d /* Colour-keying upper V limit */
+#define SAA7196_ULOW 0x2e /* Colour-keying lower U limit */
+#define SAA7196_UHIGH 0x2f /* Colour-keying upper U limit */
+#define SAA7196_DPATH 0x30 /* Data path setting */
+
+/* Initialization default values: */
+
+unsigned char saa_regs[NUM_SUPPORTED_NORM][SAA7196_NUMREGS] = {
+
+/* PAL, 768x576 (no scaling), composite video-in */
+/* Decoder: */
+ { 0x50, 0x30, 0x00, 0xe8, 0xb6, 0xe5, 0x63, 0xff,
+ 0xfe, 0xf0, 0xfe, 0xe0, 0x20, 0x06, 0x3b, 0x98,
+ 0x00, 0x59, 0x41, 0x45, 0x34, 0x0a, 0xf4, 0xd2,
+ 0xe9, 0xa2,
+/* Padding */
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+/* Scaler: */
+ 0x72, 0x80, 0x00, 0x03, 0x8d, 0x20, 0x20, 0x12,
+ 0xa5, 0x12, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00,
+ 0x87 },
+
+/* NTSC, 640x480? (no scaling), composite video-in */
+/* Decoder: */
+ { 0x50, 0x30, 0x00, 0xe8, 0xb6, 0xe5, 0x50, 0x00,
+ 0xf8, 0xf0, 0xfe, 0xe0, 0x00, 0x06, 0x3b, 0x98,
+ 0x00, 0x2c, 0x3d, 0x40, 0x34, 0x0a, 0xf4, 0xd2,
+ 0xe9, 0x98,
+/* Padding */
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+/* Scaler: */
+ 0x72, 0x80, 0x80, 0x03, 0x89, 0xf0, 0xf0, 0x0d,
+ 0xa0, 0x0d, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00,
+ 0x87 },
+
+/* SECAM, 768x576 (no scaling), composite video-in */
+/* Decoder: */
+ { 0x50, 0x30, 0x00, 0xe8, 0xb6, 0xe5, 0x63, 0xff,
+ 0xfe, 0xf0, 0xfe, 0xe0, 0x20, 0x07, 0x3b, 0x98,
+ 0x00, 0x59, 0x41, 0x45, 0x34, 0x0a, 0xf4, 0xd2,
+ 0xe9, 0xa2,
+/* Padding */
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+/* Scaler: */
+ 0x72, 0x80, 0x00, 0x03, 0x8d, 0x20, 0x20, 0x12,
+ 0xa5, 0x12, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00,
+ 0x87 }
+ };
+
+#endif /* _SAA7196_H_ */
diff --git a/linux/drivers/media/video/se401.c b/linux/drivers/media/video/se401.c
new file mode 100644
index 000000000..a846ebc78
--- /dev/null
+++ b/linux/drivers/media/video/se401.c
@@ -0,0 +1,1435 @@
+/*
+ * Endpoints (formerly known as AOX) se401 USB Camera Driver
+ *
+ * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org)
+ *
+ * Still somewhat based on the Linux ov511 driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * Thanks to Endpoints Inc. (www.endpoints.com) for making documentation on
+ * their chipset available and supporting me while writing this driver.
+ * - Jeroen Vreeken
+ */
+
+static const char version[] = "0.24";
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/usb.h>
+#include "se401.h"
+
+static int flickerless=0;
+static int video_nr = -1;
+
+static struct usb_device_id device_table [] = {
+ { USB_DEVICE(0x03e8, 0x0004) },/* Endpoints/Aox SE401 */
+ { USB_DEVICE(0x0471, 0x030b) },/* Philips PCVC665K */
+ { USB_DEVICE(0x047d, 0x5001) },/* Kensington 67014 */
+ { USB_DEVICE(0x047d, 0x5002) },/* Kensington 6701(5/7) */
+ { USB_DEVICE(0x047d, 0x5003) },/* Kensington 67016 */
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+MODULE_AUTHOR("Jeroen Vreeken <pe1rxq@amsat.org>");
+MODULE_DESCRIPTION("SE401 USB Camera Driver");
+MODULE_LICENSE("GPL");
+module_param(flickerless, int, 0);
+MODULE_PARM_DESC(flickerless, "Net frequency to adjust exposure time to (0/50/60)");
+module_param(video_nr, int, 0);
+
+static struct usb_driver se401_driver;
+
+
+/**********************************************************************
+ *
+ * Memory management
+ *
+ **********************************************************************/
+static void *rvmalloc(unsigned long size)
+{
+ void *mem;
+ unsigned long adr;
+
+ size = PAGE_ALIGN(size);
+ mem = vmalloc_32(size);
+ if (!mem)
+ return NULL;
+
+ memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+ adr = (unsigned long) mem;
+ while (size > 0) {
+ SetPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ return mem;
+}
+
+static void rvfree(void *mem, unsigned long size)
+{
+ unsigned long adr;
+
+ if (!mem)
+ return;
+
+ adr = (unsigned long) mem;
+ while ((long) size > 0) {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ vfree(mem);
+}
+
+
+
+/****************************************************************************
+ *
+ * se401 register read/write functions
+ *
+ ***************************************************************************/
+
+static int se401_sndctrl(int set, struct usb_se401 *se401, unsigned short req,
+ unsigned short value, unsigned char *cp, int size)
+{
+ return usb_control_msg (
+ se401->dev,
+ set ? usb_sndctrlpipe(se401->dev, 0) : usb_rcvctrlpipe(se401->dev, 0),
+ req,
+ (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value,
+ 0,
+ cp,
+ size,
+ 1000
+ );
+}
+
+static int se401_set_feature(struct usb_se401 *se401, unsigned short selector,
+ unsigned short param)
+{
+ /* specs say that the selector (address) should go in the value field
+ and the param in index, but in the logs of the windows driver they do
+ this the other way around...
+ */
+ return usb_control_msg (
+ se401->dev,
+ usb_sndctrlpipe(se401->dev, 0),
+ SE401_REQ_SET_EXT_FEATURE,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ param,
+ selector,
+ NULL,
+ 0,
+ 1000
+ );
+}
+
+static unsigned short se401_get_feature(struct usb_se401 *se401,
+ unsigned short selector)
+{
+ /* For 'set' the selecetor should be in index, not sure if the spec is
+ wrong here to....
+ */
+ unsigned char cp[2];
+ usb_control_msg (
+ se401->dev,
+ usb_rcvctrlpipe(se401->dev, 0),
+ SE401_REQ_GET_EXT_FEATURE,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0,
+ selector,
+ cp,
+ 2,
+ 1000
+ );
+ return cp[0]+cp[1]*256;
+}
+
+/****************************************************************************
+ *
+ * Camera control
+ *
+ ***************************************************************************/
+
+
+static int se401_send_pict(struct usb_se401 *se401)
+{
+ se401_set_feature(se401, HV7131_REG_TITL, se401->expose_l);/* integration time low */
+ se401_set_feature(se401, HV7131_REG_TITM, se401->expose_m);/* integration time mid */
+ se401_set_feature(se401, HV7131_REG_TITU, se401->expose_h);/* integration time mid */
+ se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel);/* reset level value */
+ se401_set_feature(se401, HV7131_REG_ARCG, se401->rgain);/* red color gain */
+ se401_set_feature(se401, HV7131_REG_AGCG, se401->ggain);/* green color gain */
+ se401_set_feature(se401, HV7131_REG_ABCG, se401->bgain);/* blue color gain */
+
+ return 0;
+}
+
+static void se401_set_exposure(struct usb_se401 *se401, int brightness)
+{
+ int integration=brightness<<5;
+
+ if (flickerless==50) {
+ integration=integration-integration%106667;
+ }
+ if (flickerless==60) {
+ integration=integration-integration%88889;
+ }
+ se401->brightness=integration>>5;
+ se401->expose_h=(integration>>16)&0xff;
+ se401->expose_m=(integration>>8)&0xff;
+ se401->expose_l=integration&0xff;
+}
+
+static int se401_get_pict(struct usb_se401 *se401, struct video_picture *p)
+{
+ p->brightness=se401->brightness;
+ if (se401->enhance) {
+ p->whiteness=32768;
+ } else {
+ p->whiteness=0;
+ }
+ p->colour=65535;
+ p->contrast=65535;
+ p->hue=se401->rgain<<10;
+ p->palette=se401->palette;
+ p->depth=3; /* rgb24 */
+ return 0;
+}
+
+
+static int se401_set_pict(struct usb_se401 *se401, struct video_picture *p)
+{
+ if (p->palette != VIDEO_PALETTE_RGB24)
+ return 1;
+ se401->palette=p->palette;
+ if (p->hue!=se401->hue) {
+ se401->rgain= p->hue>>10;
+ se401->bgain= 0x40-(p->hue>>10);
+ se401->hue=p->hue;
+ }
+ if (p->brightness!=se401->brightness) {
+ se401_set_exposure(se401, p->brightness);
+ }
+ if (p->whiteness>=32768) {
+ se401->enhance=1;
+ } else {
+ se401->enhance=0;
+ }
+ se401_send_pict(se401);
+ se401_send_pict(se401);
+ return 0;
+}
+
+/*
+ Hyundai have some really nice docs about this and other sensor related
+ stuff on their homepage: www.hei.co.kr
+*/
+static void se401_auto_resetlevel(struct usb_se401 *se401)
+{
+ unsigned int ahrc, alrc;
+ int oldreset=se401->resetlevel;
+
+ /* For some reason this normally read-only register doesn't get reset
+ to zero after reading them just once...
+ */
+ se401_get_feature(se401, HV7131_REG_HIREFNOH);
+ se401_get_feature(se401, HV7131_REG_HIREFNOL);
+ se401_get_feature(se401, HV7131_REG_LOREFNOH);
+ se401_get_feature(se401, HV7131_REG_LOREFNOL);
+ ahrc=256*se401_get_feature(se401, HV7131_REG_HIREFNOH) +
+ se401_get_feature(se401, HV7131_REG_HIREFNOL);
+ alrc=256*se401_get_feature(se401, HV7131_REG_LOREFNOH) +
+ se401_get_feature(se401, HV7131_REG_LOREFNOL);
+
+ /* Not an exact science, but it seems to work pretty well... */
+ if (alrc > 10) {
+ while (alrc>=10 && se401->resetlevel < 63) {
+ se401->resetlevel++;
+ alrc /=2;
+ }
+ } else if (ahrc > 20) {
+ while (ahrc>=20 && se401->resetlevel > 0) {
+ se401->resetlevel--;
+ ahrc /=2;
+ }
+ }
+ if (se401->resetlevel!=oldreset)
+ se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel);
+
+ return;
+}
+
+/* irq handler for snapshot button */
+static void se401_button_irq(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_se401 *se401 = urb->context;
+ int status;
+
+ if (!se401->dev) {
+ info("ohoh: device vapourished");
+ return;
+ }
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+ goto exit;
+ }
+
+ if (urb->actual_length >=2) {
+ if (se401->button)
+ se401->buttonpressed=1;
+ }
+exit:
+ status = usb_submit_urb (urb, GFP_ATOMIC);
+ if (status)
+ err ("%s - usb_submit_urb failed with result %d",
+ __FUNCTION__, status);
+}
+
+static void se401_video_irq(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_se401 *se401 = urb->context;
+ int length = urb->actual_length;
+
+ /* ohoh... */
+ if (!se401->streaming)
+ return;
+
+ if (!se401->dev) {
+ info ("ohoh: device vapourished");
+ return;
+ }
+
+ /* 0 sized packets happen if we are to fast, but sometimes the camera
+ keeps sending them forever...
+ */
+ if (length && !urb->status) {
+ se401->nullpackets=0;
+ switch(se401->scratch[se401->scratch_next].state) {
+ case BUFFER_READY:
+ case BUFFER_BUSY: {
+ se401->dropped++;
+ break;
+ }
+ case BUFFER_UNUSED: {
+ memcpy(se401->scratch[se401->scratch_next].data, (unsigned char *)urb->transfer_buffer, length);
+ se401->scratch[se401->scratch_next].state=BUFFER_READY;
+ se401->scratch[se401->scratch_next].offset=se401->bayeroffset;
+ se401->scratch[se401->scratch_next].length=length;
+ if (waitqueue_active(&se401->wq)) {
+ wake_up_interruptible(&se401->wq);
+ }
+ se401->scratch_overflow=0;
+ se401->scratch_next++;
+ if (se401->scratch_next>=SE401_NUMSCRATCH)
+ se401->scratch_next=0;
+ break;
+ }
+ }
+ se401->bayeroffset+=length;
+ if (se401->bayeroffset>=se401->cheight*se401->cwidth) {
+ se401->bayeroffset=0;
+ }
+ } else {
+ se401->nullpackets++;
+ if (se401->nullpackets > SE401_MAX_NULLPACKETS) {
+ if (waitqueue_active(&se401->wq)) {
+ wake_up_interruptible(&se401->wq);
+ }
+ }
+ }
+
+ /* Resubmit urb for new data */
+ urb->status=0;
+ urb->dev=se401->dev;
+ if(usb_submit_urb(urb, GFP_KERNEL))
+ info("urb burned down");
+ return;
+}
+
+static void se401_send_size(struct usb_se401 *se401, int width, int height)
+{
+ int i=0;
+ int mode=0x03; /* No compression */
+ int sendheight=height;
+ int sendwidth=width;
+
+ /* JangGu compression can only be used with the camera supported sizes,
+ but bayer seems to work with any size that fits on the sensor.
+ We check if we can use compression with the current size with either
+ 4 or 16 times subcapturing, if not we use uncompressed bayer data
+ but this will result in cutouts of the maximum size....
+ */
+ while (i<se401->sizes && !(se401->width[i]==width && se401->height[i]==height))
+ i++;
+ while (i<se401->sizes) {
+ if (se401->width[i]==width*2 && se401->height[i]==height*2) {
+ sendheight=se401->height[i];
+ sendwidth=se401->width[i];
+ mode=0x40;
+ }
+ if (se401->width[i]==width*4 && se401->height[i]==height*4) {
+ sendheight=se401->height[i];
+ sendwidth=se401->width[i];
+ mode=0x42;
+ }
+ i++;
+ }
+
+ se401_sndctrl(1, se401, SE401_REQ_SET_WIDTH, sendwidth, NULL, 0);
+ se401_sndctrl(1, se401, SE401_REQ_SET_HEIGHT, sendheight, NULL, 0);
+ se401_set_feature(se401, SE401_OPERATINGMODE, mode);
+
+ if (mode==0x03) {
+ se401->format=FMT_BAYER;
+ } else {
+ se401->format=FMT_JANGGU;
+ }
+
+ return;
+}
+
+/*
+ In this function se401_send_pict is called several times,
+ for some reason (depending on the state of the sensor and the phase of
+ the moon :) doing this only in either place doesn't always work...
+*/
+static int se401_start_stream(struct usb_se401 *se401)
+{
+ struct urb *urb;
+ int err=0, i;
+ se401->streaming=1;
+
+ se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0);
+ se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0);
+
+ /* Set picture settings */
+ se401_set_feature(se401, HV7131_REG_MODE_B, 0x05);/*windowed + pix intg */
+ se401_send_pict(se401);
+
+ se401_send_size(se401, se401->cwidth, se401->cheight);
+
+ se401_sndctrl(1, se401, SE401_REQ_START_CONTINUOUS_CAPTURE, 0, NULL, 0);
+
+ /* Do some memory allocation */
+ for (i=0; i<SE401_NUMFRAMES; i++) {
+ se401->frame[i].data=se401->fbuf + i * se401->maxframesize;
+ se401->frame[i].curpix=0;
+ }
+ for (i=0; i<SE401_NUMSBUF; i++) {
+ se401->sbuf[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL);
+ }
+
+ se401->bayeroffset=0;
+ se401->scratch_next=0;
+ se401->scratch_use=0;
+ se401->scratch_overflow=0;
+ for (i=0; i<SE401_NUMSCRATCH; i++) {
+ se401->scratch[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL);
+ se401->scratch[i].state=BUFFER_UNUSED;
+ }
+
+ for (i=0; i<SE401_NUMSBUF; i++) {
+ urb=usb_alloc_urb(0, GFP_KERNEL);
+ if(!urb)
+ return -ENOMEM;
+
+ usb_fill_bulk_urb(urb, se401->dev,
+ usb_rcvbulkpipe(se401->dev, SE401_VIDEO_ENDPOINT),
+ se401->sbuf[i].data, SE401_PACKETSIZE,
+ se401_video_irq,
+ se401);
+
+ se401->urb[i]=urb;
+
+ err=usb_submit_urb(se401->urb[i], GFP_KERNEL);
+ if(err)
+ err("urb burned down");
+ }
+
+ se401->framecount=0;
+
+ return 0;
+}
+
+static int se401_stop_stream(struct usb_se401 *se401)
+{
+ int i;
+
+ if (!se401->streaming || !se401->dev)
+ return 1;
+
+ se401->streaming=0;
+
+ se401_sndctrl(1, se401, SE401_REQ_STOP_CONTINUOUS_CAPTURE, 0, NULL, 0);
+
+ se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0);
+ se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0);
+
+ for (i=0; i<SE401_NUMSBUF; i++) if (se401->urb[i]) {
+ usb_kill_urb(se401->urb[i]);
+ usb_free_urb(se401->urb[i]);
+ se401->urb[i]=NULL;
+ kfree(se401->sbuf[i].data);
+ }
+ for (i=0; i<SE401_NUMSCRATCH; i++) {
+ kfree(se401->scratch[i].data);
+ se401->scratch[i].data=NULL;
+ }
+
+ return 0;
+}
+
+static int se401_set_size(struct usb_se401 *se401, int width, int height)
+{
+ int wasstreaming=se401->streaming;
+ /* Check to see if we need to change */
+ if (se401->cwidth==width && se401->cheight==height)
+ return 0;
+
+ /* Check for a valid mode */
+ if (!width || !height)
+ return 1;
+ if ((width & 1) || (height & 1))
+ return 1;
+ if (width>se401->width[se401->sizes-1])
+ return 1;
+ if (height>se401->height[se401->sizes-1])
+ return 1;
+
+ /* Stop a current stream and start it again at the new size */
+ if (wasstreaming)
+ se401_stop_stream(se401);
+ se401->cwidth=width;
+ se401->cheight=height;
+ if (wasstreaming)
+ se401_start_stream(se401);
+ return 0;
+}
+
+
+/****************************************************************************
+ *
+ * Video Decoding
+ *
+ ***************************************************************************/
+
+/*
+ This shouldn't really be done in a v4l driver....
+ But it does make the image look a lot more usable.
+ Basically it lifts the dark pixels more than the light pixels.
+*/
+static inline void enhance_picture(unsigned char *frame, int len)
+{
+ while (len--) {
+ *frame=(((*frame^255)*(*frame^255))/255)^255;
+ frame++;
+ }
+}
+
+static inline void decode_JangGu_integrate(struct usb_se401 *se401, int data)
+{
+ struct se401_frame *frame=&se401->frame[se401->curframe];
+ int linelength=se401->cwidth*3;
+
+ if (frame->curlinepix >= linelength) {
+ frame->curlinepix=0;
+ frame->curline+=linelength;
+ }
+
+ /* First three are absolute, all others relative.
+ * Format is rgb from right to left (mirrorred image),
+ * we flip it to get bgr from left to right. */
+ if (frame->curlinepix < 3) {
+ *(frame->curline-frame->curlinepix)=1+data*4;
+ } else {
+ *(frame->curline-frame->curlinepix)=
+ *(frame->curline-frame->curlinepix+3)+data*4;
+ }
+ frame->curlinepix++;
+}
+
+static inline void decode_JangGu_vlc (struct usb_se401 *se401, unsigned char *data, int bit_exp, int packetlength)
+{
+ int pos=0;
+ int vlc_cod=0;
+ int vlc_size=0;
+ int vlc_data=0;
+ int bit_cur;
+ int bit;
+ data+=4;
+ while (pos < packetlength) {
+ bit_cur=8;
+ while (bit_cur && bit_exp) {
+ bit=((*data)>>(bit_cur-1))&1;
+ if (!vlc_cod) {
+ if (bit) {
+ vlc_size++;
+ } else {
+ if (!vlc_size) {
+ decode_JangGu_integrate(se401, 0);
+ } else {
+ vlc_cod=2;
+ vlc_data=0;
+ }
+ }
+ } else {
+ if (vlc_cod==2) {
+ if (!bit)
+ vlc_data = -(1<<vlc_size) + 1;
+ vlc_cod--;
+ }
+ vlc_size--;
+ vlc_data+=bit<<vlc_size;
+ if (!vlc_size) {
+ decode_JangGu_integrate(se401, vlc_data);
+ vlc_cod=0;
+ }
+ }
+ bit_cur--;
+ bit_exp--;
+ }
+ pos++;
+ data++;
+ }
+}
+
+static inline void decode_JangGu (struct usb_se401 *se401, struct se401_scratch *buffer)
+{
+ unsigned char *data=buffer->data;
+ int len=buffer->length;
+ int bit_exp=0, pix_exp=0, frameinfo=0, packetlength=0, size;
+ int datapos=0;
+
+ /* New image? */
+ if (!se401->frame[se401->curframe].curpix) {
+ se401->frame[se401->curframe].curlinepix=0;
+ se401->frame[se401->curframe].curline=
+ se401->frame[se401->curframe].data+
+ se401->cwidth*3-1;
+ if (se401->frame[se401->curframe].grabstate==FRAME_READY)
+ se401->frame[se401->curframe].grabstate=FRAME_GRABBING;
+ se401->vlcdatapos=0;
+ }
+ while (datapos < len) {
+ size=1024-se401->vlcdatapos;
+ if (size+datapos > len)
+ size=len-datapos;
+ memcpy(se401->vlcdata+se401->vlcdatapos, data+datapos, size);
+ se401->vlcdatapos+=size;
+ packetlength=0;
+ if (se401->vlcdatapos >= 4) {
+ bit_exp=se401->vlcdata[3]+(se401->vlcdata[2]<<8);
+ pix_exp=se401->vlcdata[1]+((se401->vlcdata[0]&0x3f)<<8);
+ frameinfo=se401->vlcdata[0]&0xc0;
+ packetlength=((bit_exp+47)>>4)<<1;
+ if (packetlength > 1024) {
+ se401->vlcdatapos=0;
+ datapos=len;
+ packetlength=0;
+ se401->error++;
+ se401->frame[se401->curframe].curpix=0;
+ }
+ }
+ if (packetlength && se401->vlcdatapos >= packetlength) {
+ decode_JangGu_vlc(se401, se401->vlcdata, bit_exp, packetlength);
+ se401->frame[se401->curframe].curpix+=pix_exp*3;
+ datapos+=size-(se401->vlcdatapos-packetlength);
+ se401->vlcdatapos=0;
+ if (se401->frame[se401->curframe].curpix>=se401->cwidth*se401->cheight*3) {
+ if (se401->frame[se401->curframe].curpix==se401->cwidth*se401->cheight*3) {
+ if (se401->frame[se401->curframe].grabstate==FRAME_GRABBING) {
+ se401->frame[se401->curframe].grabstate=FRAME_DONE;
+ se401->framecount++;
+ se401->readcount++;
+ }
+ if (se401->frame[(se401->curframe+1)&(SE401_NUMFRAMES-1)].grabstate==FRAME_READY) {
+ se401->curframe=(se401->curframe+1) & (SE401_NUMFRAMES-1);
+ }
+ } else {
+ se401->error++;
+ }
+ se401->frame[se401->curframe].curpix=0;
+ datapos=len;
+ }
+ } else {
+ datapos+=size;
+ }
+ }
+}
+
+static inline void decode_bayer (struct usb_se401 *se401, struct se401_scratch *buffer)
+{
+ unsigned char *data=buffer->data;
+ int len=buffer->length;
+ int offset=buffer->offset;
+ int datasize=se401->cwidth*se401->cheight;
+ struct se401_frame *frame=&se401->frame[se401->curframe];
+
+ unsigned char *framedata=frame->data, *curline, *nextline;
+ int width=se401->cwidth;
+ int blineoffset=0, bline;
+ int linelength=width*3, i;
+
+
+ if (frame->curpix==0) {
+ if (frame->grabstate==FRAME_READY) {
+ frame->grabstate=FRAME_GRABBING;
+ }
+ frame->curline=framedata+linelength;
+ frame->curlinepix=0;
+ }
+
+ if (offset!=frame->curpix) {
+ /* Regard frame as lost :( */
+ frame->curpix=0;
+ se401->error++;
+ return;
+ }
+
+ /* Check if we have to much data */
+ if (frame->curpix+len > datasize) {
+ len=datasize-frame->curpix;
+ }
+ if (se401->cheight%4)
+ blineoffset=1;
+ bline=frame->curpix/se401->cwidth+blineoffset;
+
+ curline=frame->curline;
+ nextline=curline+linelength;
+ if (nextline >= framedata+datasize*3)
+ nextline=curline;
+ while (len) {
+ if (frame->curlinepix>=width) {
+ frame->curlinepix-=width;
+ bline=frame->curpix/width+blineoffset;
+ curline+=linelength*2;
+ nextline+=linelength*2;
+ if (curline >= framedata+datasize*3) {
+ frame->curlinepix++;
+ curline-=3;
+ nextline-=3;
+ len--;
+ data++;
+ frame->curpix++;
+ }
+ if (nextline >= framedata+datasize*3)
+ nextline=curline;
+ }
+ if ((bline&1)) {
+ if ((frame->curlinepix&1)) {
+ *(curline+2)=*data;
+ *(curline-1)=*data;
+ *(nextline+2)=*data;
+ *(nextline-1)=*data;
+ } else {
+ *(curline+1)=
+ (*(curline+1)+*data)/2;
+ *(curline-2)=
+ (*(curline-2)+*data)/2;
+ *(nextline+1)=*data;
+ *(nextline-2)=*data;
+ }
+ } else {
+ if ((frame->curlinepix&1)) {
+ *(curline+1)=
+ (*(curline+1)+*data)/2;
+ *(curline-2)=
+ (*(curline-2)+*data)/2;
+ *(nextline+1)=*data;
+ *(nextline-2)=*data;
+ } else {
+ *curline=*data;
+ *(curline-3)=*data;
+ *nextline=*data;
+ *(nextline-3)=*data;
+ }
+ }
+ frame->curlinepix++;
+ curline-=3;
+ nextline-=3;
+ len--;
+ data++;
+ frame->curpix++;
+ }
+ frame->curline=curline;
+
+ if (frame->curpix>=datasize) {
+ /* Fix the top line */
+ framedata+=linelength;
+ for (i=0; i<linelength; i++) {
+ framedata--;
+ *framedata=*(framedata+linelength);
+ }
+ /* Fix the left side (green is already present) */
+ for (i=0; i<se401->cheight; i++) {
+ *framedata=*(framedata+3);
+ *(framedata+1)=*(framedata+4);
+ *(framedata+2)=*(framedata+5);
+ framedata+=linelength;
+ }
+ frame->curpix=0;
+ frame->grabstate=FRAME_DONE;
+ se401->framecount++;
+ se401->readcount++;
+ if (se401->frame[(se401->curframe+1)&(SE401_NUMFRAMES-1)].grabstate==FRAME_READY) {
+ se401->curframe=(se401->curframe+1) & (SE401_NUMFRAMES-1);
+ }
+ }
+}
+
+static int se401_newframe(struct usb_se401 *se401, int framenr)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ int errors=0;
+
+ while (se401->streaming &&
+ (se401->frame[framenr].grabstate==FRAME_READY ||
+ se401->frame[framenr].grabstate==FRAME_GRABBING) ) {
+ if(!se401->frame[framenr].curpix) {
+ errors++;
+ }
+ wait_interruptible(
+ se401->scratch[se401->scratch_use].state!=BUFFER_READY,
+ &se401->wq,
+ &wait
+ );
+ if (se401->nullpackets > SE401_MAX_NULLPACKETS) {
+ se401->nullpackets=0;
+ info("to many null length packets, restarting capture");
+ se401_stop_stream(se401);
+ se401_start_stream(se401);
+ } else {
+ if (se401->scratch[se401->scratch_use].state!=BUFFER_READY) {
+ se401->frame[framenr].grabstate=FRAME_ERROR;
+ return -EIO;
+ }
+ se401->scratch[se401->scratch_use].state=BUFFER_BUSY;
+ if (se401->format==FMT_JANGGU) {
+ decode_JangGu(se401, &se401->scratch[se401->scratch_use]);
+ } else {
+ decode_bayer(se401, &se401->scratch[se401->scratch_use]);
+ }
+ se401->scratch[se401->scratch_use].state=BUFFER_UNUSED;
+ se401->scratch_use++;
+ if (se401->scratch_use>=SE401_NUMSCRATCH)
+ se401->scratch_use=0;
+ if (errors > SE401_MAX_ERRORS) {
+ errors=0;
+ info("to much errors, restarting capture");
+ se401_stop_stream(se401);
+ se401_start_stream(se401);
+ }
+ }
+ }
+
+ if (se401->frame[framenr].grabstate==FRAME_DONE)
+ if (se401->enhance)
+ enhance_picture(se401->frame[framenr].data, se401->cheight*se401->cwidth*3);
+ return 0;
+}
+
+static void usb_se401_remove_disconnected (struct usb_se401 *se401)
+{
+ int i;
+
+ se401->dev = NULL;
+
+ for (i=0; i<SE401_NUMSBUF; i++)
+ if (se401->urb[i]) {
+ usb_kill_urb(se401->urb[i]);
+ usb_free_urb(se401->urb[i]);
+ se401->urb[i] = NULL;
+ kfree(se401->sbuf[i].data);
+ }
+ for (i=0; i<SE401_NUMSCRATCH; i++) {
+ kfree(se401->scratch[i].data);
+ }
+ if (se401->inturb) {
+ usb_kill_urb(se401->inturb);
+ usb_free_urb(se401->inturb);
+ }
+ info("%s disconnected", se401->camera_name);
+
+ /* Free the memory */
+ kfree(se401->width);
+ kfree(se401->height);
+ kfree(se401);
+}
+
+
+
+/****************************************************************************
+ *
+ * Video4Linux
+ *
+ ***************************************************************************/
+
+
+static int se401_open(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct usb_se401 *se401 = (struct usb_se401 *)dev;
+ int err = 0;
+
+ if (se401->user)
+ return -EBUSY;
+ se401->fbuf = rvmalloc(se401->maxframesize * SE401_NUMFRAMES);
+ if (se401->fbuf)
+ file->private_data = dev;
+ else
+ err = -ENOMEM;
+ se401->user = !err;
+
+ return err;
+}
+
+static int se401_close(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = file->private_data;
+ struct usb_se401 *se401 = (struct usb_se401 *)dev;
+ int i;
+
+ rvfree(se401->fbuf, se401->maxframesize * SE401_NUMFRAMES);
+ if (se401->removed) {
+ usb_se401_remove_disconnected(se401);
+ info("device unregistered");
+ } else {
+ for (i=0; i<SE401_NUMFRAMES; i++)
+ se401->frame[i].grabstate=FRAME_UNUSED;
+ if (se401->streaming)
+ se401_stop_stream(se401);
+ se401->user=0;
+ }
+ file->private_data = NULL;
+ return 0;
+}
+
+static int se401_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = file->private_data;
+ struct usb_se401 *se401 = (struct usb_se401 *)vdev;
+
+ if (!se401->dev)
+ return -EIO;
+
+ switch (cmd) {
+ case VIDIOCGCAP:
+ {
+ struct video_capability *b = arg;
+ strcpy(b->name, se401->camera_name);
+ b->type = VID_TYPE_CAPTURE;
+ b->channels = 1;
+ b->audios = 0;
+ b->maxwidth = se401->width[se401->sizes-1];
+ b->maxheight = se401->height[se401->sizes-1];
+ b->minwidth = se401->width[0];
+ b->minheight = se401->height[0];
+ return 0;
+ }
+ case VIDIOCGCHAN:
+ {
+ struct video_channel *v = arg;
+
+ if (v->channel != 0)
+ return -EINVAL;
+ v->flags = 0;
+ v->tuners = 0;
+ v->type = VIDEO_TYPE_CAMERA;
+ strcpy(v->name, "Camera");
+ return 0;
+ }
+ case VIDIOCSCHAN:
+ {
+ struct video_channel *v = arg;
+
+ if (v->channel != 0)
+ return -EINVAL;
+ return 0;
+ }
+ case VIDIOCGPICT:
+ {
+ struct video_picture *p = arg;
+
+ se401_get_pict(se401, p);
+ return 0;
+ }
+ case VIDIOCSPICT:
+ {
+ struct video_picture *p = arg;
+
+ if (se401_set_pict(se401, p))
+ return -EINVAL;
+ return 0;
+ }
+ case VIDIOCSWIN:
+ {
+ struct video_window *vw = arg;
+
+ if (vw->flags)
+ return -EINVAL;
+ if (vw->clipcount)
+ return -EINVAL;
+ if (se401_set_size(se401, vw->width, vw->height))
+ return -EINVAL;
+ return 0;
+ }
+ case VIDIOCGWIN:
+ {
+ struct video_window *vw = arg;
+
+ vw->x = 0; /* FIXME */
+ vw->y = 0;
+ vw->chromakey = 0;
+ vw->flags = 0;
+ vw->clipcount = 0;
+ vw->width = se401->cwidth;
+ vw->height = se401->cheight;
+ return 0;
+ }
+ case VIDIOCGMBUF:
+ {
+ struct video_mbuf *vm = arg;
+ int i;
+
+ memset(vm, 0, sizeof(*vm));
+ vm->size = SE401_NUMFRAMES * se401->maxframesize;
+ vm->frames = SE401_NUMFRAMES;
+ for (i=0; i<SE401_NUMFRAMES; i++)
+ vm->offsets[i] = se401->maxframesize * i;
+ return 0;
+ }
+ case VIDIOCMCAPTURE:
+ {
+ struct video_mmap *vm = arg;
+
+ if (vm->format != VIDEO_PALETTE_RGB24)
+ return -EINVAL;
+ if (vm->frame >= SE401_NUMFRAMES)
+ return -EINVAL;
+ if (se401->frame[vm->frame].grabstate != FRAME_UNUSED)
+ return -EBUSY;
+
+ /* Is this according to the v4l spec??? */
+ if (se401_set_size(se401, vm->width, vm->height))
+ return -EINVAL;
+ se401->frame[vm->frame].grabstate=FRAME_READY;
+
+ if (!se401->streaming)
+ se401_start_stream(se401);
+
+ /* Set the picture properties */
+ if (se401->framecount==0)
+ se401_send_pict(se401);
+ /* Calibrate the reset level after a few frames. */
+ if (se401->framecount%20==1)
+ se401_auto_resetlevel(se401);
+
+ return 0;
+ }
+ case VIDIOCSYNC:
+ {
+ int *frame = arg;
+ int ret=0;
+
+ if(*frame <0 || *frame >= SE401_NUMFRAMES)
+ return -EINVAL;
+
+ ret=se401_newframe(se401, *frame);
+ se401->frame[*frame].grabstate=FRAME_UNUSED;
+ return ret;
+ }
+ case VIDIOCGFBUF:
+ {
+ struct video_buffer *vb = arg;
+
+ memset(vb, 0, sizeof(*vb));
+ return 0;
+ }
+ case VIDIOCKEY:
+ return 0;
+ case VIDIOCCAPTURE:
+ return -EINVAL;
+ case VIDIOCSFBUF:
+ return -EINVAL;
+ case VIDIOCGTUNER:
+ case VIDIOCSTUNER:
+ return -EINVAL;
+ case VIDIOCGFREQ:
+ case VIDIOCSFREQ:
+ return -EINVAL;
+ case VIDIOCGAUDIO:
+ case VIDIOCSAUDIO:
+ return -EINVAL;
+ default:
+ return -ENOIOCTLCMD;
+ } /* end switch */
+
+ return 0;
+}
+
+static int se401_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, se401_do_ioctl);
+}
+
+static ssize_t se401_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int realcount=count, ret=0;
+ struct video_device *dev = file->private_data;
+ struct usb_se401 *se401 = (struct usb_se401 *)dev;
+
+
+ if (se401->dev == NULL)
+ return -EIO;
+ if (realcount > se401->cwidth*se401->cheight*3)
+ realcount=se401->cwidth*se401->cheight*3;
+
+ /* Shouldn't happen: */
+ if (se401->frame[0].grabstate==FRAME_GRABBING)
+ return -EBUSY;
+ se401->frame[0].grabstate=FRAME_READY;
+ se401->frame[1].grabstate=FRAME_UNUSED;
+ se401->curframe=0;
+
+ if (!se401->streaming)
+ se401_start_stream(se401);
+
+ /* Set the picture properties */
+ if (se401->framecount==0)
+ se401_send_pict(se401);
+ /* Calibrate the reset level after a few frames. */
+ if (se401->framecount%20==1)
+ se401_auto_resetlevel(se401);
+
+ ret=se401_newframe(se401, 0);
+
+ se401->frame[0].grabstate=FRAME_UNUSED;
+ if (ret)
+ return ret;
+ if (copy_to_user(buf, se401->frame[0].data, realcount))
+ return -EFAULT;
+
+ return realcount;
+}
+
+static int se401_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *dev = file->private_data;
+ struct usb_se401 *se401 = (struct usb_se401 *)dev;
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end-vma->vm_start;
+ unsigned long page, pos;
+
+ mutex_lock(&se401->lock);
+
+ if (se401->dev == NULL) {
+ mutex_unlock(&se401->lock);
+ return -EIO;
+ }
+ if (size > (((SE401_NUMFRAMES * se401->maxframesize) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) {
+ mutex_unlock(&se401->lock);
+ return -EINVAL;
+ }
+ pos = (unsigned long)se401->fbuf;
+ while (size > 0) {
+ page = vmalloc_to_pfn((void *)pos);
+ if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
+ mutex_unlock(&se401->lock);
+ return -EAGAIN;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+ mutex_unlock(&se401->lock);
+
+ return 0;
+}
+
+static struct file_operations se401_fops = {
+ .owner = THIS_MODULE,
+ .open = se401_open,
+ .release = se401_close,
+ .read = se401_read,
+ .mmap = se401_mmap,
+ .ioctl = se401_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+static struct video_device se401_template = {
+ .owner = THIS_MODULE,
+ .name = "se401 USB camera",
+ .type = VID_TYPE_CAPTURE,
+ .hardware = VID_HARDWARE_SE401,
+ .fops = &se401_fops,
+};
+
+
+
+/***************************/
+static int se401_init(struct usb_se401 *se401, int button)
+{
+ int i=0, rc;
+ unsigned char cp[0x40];
+ char temp[200];
+
+ /* led on */
+ se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0);
+
+ /* get camera descriptor */
+ rc=se401_sndctrl(0, se401, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0, cp, sizeof(cp));
+ if (cp[1]!=0x41) {
+ err("Wrong descriptor type");
+ return 1;
+ }
+ sprintf (temp, "ExtraFeatures: %d", cp[3]);
+
+ se401->sizes=cp[4]+cp[5]*256;
+ se401->width=kmalloc(se401->sizes*sizeof(int), GFP_KERNEL);
+ if (!se401->width)
+ return 1;
+ se401->height=kmalloc(se401->sizes*sizeof(int), GFP_KERNEL);
+ if (!se401->height) {
+ kfree(se401->width);
+ return 1;
+ }
+ for (i=0; i<se401->sizes; i++) {
+ se401->width[i]=cp[6+i*4+0]+cp[6+i*4+1]*256;
+ se401->height[i]=cp[6+i*4+2]+cp[6+i*4+3]*256;
+ }
+ sprintf (temp, "%s Sizes:", temp);
+ for (i=0; i<se401->sizes; i++) {
+ sprintf(temp, "%s %dx%d", temp, se401->width[i], se401->height[i]);
+ }
+ info("%s", temp);
+ se401->maxframesize=se401->width[se401->sizes-1]*se401->height[se401->sizes-1]*3;
+
+ rc=se401_sndctrl(0, se401, SE401_REQ_GET_WIDTH, 0, cp, sizeof(cp));
+ se401->cwidth=cp[0]+cp[1]*256;
+ rc=se401_sndctrl(0, se401, SE401_REQ_GET_HEIGHT, 0, cp, sizeof(cp));
+ se401->cheight=cp[0]+cp[1]*256;
+
+ if (!cp[2] && SE401_FORMAT_BAYER) {
+ err("Bayer format not supported!");
+ return 1;
+ }
+ /* set output mode (BAYER) */
+ se401_sndctrl(1, se401, SE401_REQ_SET_OUTPUT_MODE, SE401_FORMAT_BAYER, NULL, 0);
+
+ rc=se401_sndctrl(0, se401, SE401_REQ_GET_BRT, 0, cp, sizeof(cp));
+ se401->brightness=cp[0]+cp[1]*256;
+ /* some default values */
+ se401->resetlevel=0x2d;
+ se401->rgain=0x20;
+ se401->ggain=0x20;
+ se401->bgain=0x20;
+ se401_set_exposure(se401, 20000);
+ se401->palette=VIDEO_PALETTE_RGB24;
+ se401->enhance=1;
+ se401->dropped=0;
+ se401->error=0;
+ se401->framecount=0;
+ se401->readcount=0;
+
+ /* Start interrupt transfers for snapshot button */
+ if (button) {
+ se401->inturb=usb_alloc_urb(0, GFP_KERNEL);
+ if (!se401->inturb) {
+ info("Allocation of inturb failed");
+ return 1;
+ }
+ usb_fill_int_urb(se401->inturb, se401->dev,
+ usb_rcvintpipe(se401->dev, SE401_BUTTON_ENDPOINT),
+ &se401->button, sizeof(se401->button),
+ se401_button_irq,
+ se401,
+ 8
+ );
+ if (usb_submit_urb(se401->inturb, GFP_KERNEL)) {
+ info("int urb burned down");
+ return 1;
+ }
+ } else
+ se401->inturb=NULL;
+
+ /* Flash the led */
+ se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0);
+ se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0);
+ se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0);
+ se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0);
+
+ return 0;
+}
+
+static int se401_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_interface_descriptor *interface;
+ struct usb_se401 *se401;
+ char *camera_name=NULL;
+ int button=1;
+
+ /* We don't handle multi-config cameras */
+ if (dev->descriptor.bNumConfigurations != 1)
+ return -ENODEV;
+
+ interface = &intf->cur_altsetting->desc;
+
+ /* Is it an se401? */
+ if (le16_to_cpu(dev->descriptor.idVendor) == 0x03e8 &&
+ le16_to_cpu(dev->descriptor.idProduct) == 0x0004) {
+ camera_name="Endpoints/Aox SE401";
+ } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x0471 &&
+ le16_to_cpu(dev->descriptor.idProduct) == 0x030b) {
+ camera_name="Philips PCVC665K";
+ } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d &&
+ le16_to_cpu(dev->descriptor.idProduct) == 0x5001) {
+ camera_name="Kensington VideoCAM 67014";
+ } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d &&
+ le16_to_cpu(dev->descriptor.idProduct) == 0x5002) {
+ camera_name="Kensington VideoCAM 6701(5/7)";
+ } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d &&
+ le16_to_cpu(dev->descriptor.idProduct) == 0x5003) {
+ camera_name="Kensington VideoCAM 67016";
+ button=0;
+ } else
+ return -ENODEV;
+
+ /* Checking vendor/product should be enough, but what the hell */
+ if (interface->bInterfaceClass != 0x00)
+ return -ENODEV;
+ if (interface->bInterfaceSubClass != 0x00)
+ return -ENODEV;
+
+ /* We found one */
+ info("SE401 camera found: %s", camera_name);
+
+ if ((se401 = kzalloc(sizeof(*se401), GFP_KERNEL)) == NULL) {
+ err("couldn't kmalloc se401 struct");
+ return -ENOMEM;
+ }
+
+ se401->dev = dev;
+ se401->iface = interface->bInterfaceNumber;
+ se401->camera_name = camera_name;
+
+ info("firmware version: %02x", le16_to_cpu(dev->descriptor.bcdDevice) & 255);
+
+ if (se401_init(se401, button)) {
+ kfree(se401);
+ return -EIO;
+ }
+
+ memcpy(&se401->vdev, &se401_template, sizeof(se401_template));
+ memcpy(se401->vdev.name, se401->camera_name, strlen(se401->camera_name));
+ init_waitqueue_head(&se401->wq);
+ mutex_init(&se401->lock);
+ wmb();
+
+ if (video_register_device(&se401->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
+ kfree(se401);
+ err("video_register_device failed");
+ return -EIO;
+ }
+ info("registered new video device: video%d", se401->vdev.minor);
+
+ usb_set_intfdata (intf, se401);
+ return 0;
+}
+
+static void se401_disconnect(struct usb_interface *intf)
+{
+ struct usb_se401 *se401 = usb_get_intfdata (intf);
+
+ usb_set_intfdata (intf, NULL);
+ if (se401) {
+ video_unregister_device(&se401->vdev);
+ if (!se401->user){
+ usb_se401_remove_disconnected(se401);
+ } else {
+ se401->frame[0].grabstate = FRAME_ERROR;
+ se401->frame[0].grabstate = FRAME_ERROR;
+
+ se401->streaming = 0;
+
+ wake_up_interruptible(&se401->wq);
+ se401->removed = 1;
+ }
+ }
+}
+
+static struct usb_driver se401_driver = {
+ .name = "se401",
+ .id_table = device_table,
+ .probe = se401_probe,
+ .disconnect = se401_disconnect,
+};
+
+
+
+/****************************************************************************
+ *
+ * Module routines
+ *
+ ***************************************************************************/
+
+static int __init usb_se401_init(void)
+{
+ info("SE401 usb camera driver version %s registering", version);
+ if (flickerless)
+ if (flickerless!=50 && flickerless!=60) {
+ info("Invallid flickerless value, use 0, 50 or 60.");
+ return -1;
+ }
+ return usb_register(&se401_driver);
+}
+
+static void __exit usb_se401_exit(void)
+{
+ usb_deregister(&se401_driver);
+ info("SE401 driver deregistered");
+}
+
+module_init(usb_se401_init);
+module_exit(usb_se401_exit);
diff --git a/linux/drivers/media/video/se401.h b/linux/drivers/media/video/se401.h
new file mode 100644
index 000000000..a78aa0720
--- /dev/null
+++ b/linux/drivers/media/video/se401.h
@@ -0,0 +1,235 @@
+
+#ifndef __LINUX_se401_H
+#define __LINUX_se401_H
+
+#include <asm/uaccess.h>
+#include "compat.h"
+#include <linux/videodev.h>
+#include <linux/smp_lock.h>
+#include <linux/mutex.h>
+
+#define se401_DEBUG /* Turn on debug messages */
+
+#ifdef se401_DEBUG
+# define PDEBUG(level, fmt, args...) \
+if (debug >= level) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args)
+#else
+# define PDEBUG(level, fmt, args...) do {} while(0)
+#endif
+
+/* An almost drop-in replacement for sleep_on_interruptible */
+#define wait_interruptible(test, queue, wait) \
+{ \
+ add_wait_queue(queue, wait); \
+ set_current_state(TASK_INTERRUPTIBLE); \
+ if (test) \
+ schedule(); \
+ remove_wait_queue(queue, wait); \
+ set_current_state(TASK_RUNNING); \
+ if (signal_pending(current)) \
+ break; \
+}
+
+#define SE401_REQ_GET_CAMERA_DESCRIPTOR 0x06
+#define SE401_REQ_START_CONTINUOUS_CAPTURE 0x41
+#define SE401_REQ_STOP_CONTINUOUS_CAPTURE 0x42
+#define SE401_REQ_CAPTURE_FRAME 0x43
+#define SE401_REQ_GET_BRT 0x44
+#define SE401_REQ_SET_BRT 0x45
+#define SE401_REQ_GET_WIDTH 0x4c
+#define SE401_REQ_SET_WIDTH 0x4d
+#define SE401_REQ_GET_HEIGHT 0x4e
+#define SE401_REQ_SET_HEIGHT 0x4f
+#define SE401_REQ_GET_OUTPUT_MODE 0x50
+#define SE401_REQ_SET_OUTPUT_MODE 0x51
+#define SE401_REQ_GET_EXT_FEATURE 0x52
+#define SE401_REQ_SET_EXT_FEATURE 0x53
+#define SE401_REQ_CAMERA_POWER 0x56
+#define SE401_REQ_LED_CONTROL 0x57
+#define SE401_REQ_BIOS 0xff
+
+#define SE401_BIOS_READ 0x07
+
+#define SE401_FORMAT_BAYER 0x40
+
+/* Hyundai hv7131b registers
+ 7121 and 7141 should be the same (haven't really checked...) */
+/* Mode registers: */
+#define HV7131_REG_MODE_A 0x00
+#define HV7131_REG_MODE_B 0x01
+#define HV7131_REG_MODE_C 0x02
+/* Frame registers: */
+#define HV7131_REG_FRSU 0x10
+#define HV7131_REG_FRSL 0x11
+#define HV7131_REG_FCSU 0x12
+#define HV7131_REG_FCSL 0x13
+#define HV7131_REG_FWHU 0x14
+#define HV7131_REG_FWHL 0x15
+#define HV7131_REG_FWWU 0x16
+#define HV7131_REG_FWWL 0x17
+/* Timing registers: */
+#define HV7131_REG_THBU 0x20
+#define HV7131_REG_THBL 0x21
+#define HV7131_REG_TVBU 0x22
+#define HV7131_REG_TVBL 0x23
+#define HV7131_REG_TITU 0x25
+#define HV7131_REG_TITM 0x26
+#define HV7131_REG_TITL 0x27
+#define HV7131_REG_TMCD 0x28
+/* Adjust Registers: */
+#define HV7131_REG_ARLV 0x30
+#define HV7131_REG_ARCG 0x31
+#define HV7131_REG_AGCG 0x32
+#define HV7131_REG_ABCG 0x33
+#define HV7131_REG_APBV 0x34
+#define HV7131_REG_ASLP 0x54
+/* Offset Registers: */
+#define HV7131_REG_OFSR 0x50
+#define HV7131_REG_OFSG 0x51
+#define HV7131_REG_OFSB 0x52
+/* REset level statistics registers: */
+#define HV7131_REG_LOREFNOH 0x57
+#define HV7131_REG_LOREFNOL 0x58
+#define HV7131_REG_HIREFNOH 0x59
+#define HV7131_REG_HIREFNOL 0x5a
+
+/* se401 registers */
+#define SE401_OPERATINGMODE 0x2000
+
+
+/* size of usb transfers */
+#define SE401_PACKETSIZE 4096
+/* number of queued bulk transfers to use, should be about 8 */
+#define SE401_NUMSBUF 1
+/* read the usb specs for this one :) */
+#define SE401_VIDEO_ENDPOINT 1
+#define SE401_BUTTON_ENDPOINT 2
+/* number of frames supported by the v4l part */
+#define SE401_NUMFRAMES 2
+/* scratch buffers for passing data to the decoders */
+#define SE401_NUMSCRATCH 32
+/* maximum amount of data in a JangGu packet */
+#define SE401_VLCDATALEN 1024
+/* number of nul sized packets to receive before kicking the camera */
+#define SE401_MAX_NULLPACKETS 4000
+/* number of decoding errors before kicking the camera */
+#define SE401_MAX_ERRORS 200
+
+struct usb_device;
+
+struct se401_sbuf {
+ unsigned char *data;
+};
+
+enum {
+ FRAME_UNUSED, /* Unused (no MCAPTURE) */
+ FRAME_READY, /* Ready to start grabbing */
+ FRAME_GRABBING, /* In the process of being grabbed into */
+ FRAME_DONE, /* Finished grabbing, but not been synced yet */
+ FRAME_ERROR, /* Something bad happened while processing */
+};
+
+enum {
+ FMT_BAYER,
+ FMT_JANGGU,
+};
+
+enum {
+ BUFFER_UNUSED,
+ BUFFER_READY,
+ BUFFER_BUSY,
+ BUFFER_DONE,
+};
+
+struct se401_scratch {
+ unsigned char *data;
+ volatile int state;
+ int offset;
+ int length;
+};
+
+struct se401_frame {
+ unsigned char *data; /* Frame buffer */
+
+ volatile int grabstate; /* State of grabbing */
+
+ unsigned char *curline;
+ int curlinepix;
+ int curpix;
+};
+
+struct usb_se401 {
+ struct video_device vdev;
+
+ /* Device structure */
+ struct usb_device *dev;
+
+ unsigned char iface;
+
+ char *camera_name;
+
+ int change;
+ int brightness;
+ int hue;
+ int rgain;
+ int ggain;
+ int bgain;
+ int expose_h;
+ int expose_m;
+ int expose_l;
+ int resetlevel;
+
+ int enhance;
+
+ int format;
+ int sizes;
+ int *width;
+ int *height;
+ int cwidth; /* current width */
+ int cheight; /* current height */
+ int palette;
+ int maxframesize;
+ int cframesize; /* current framesize */
+
+ struct mutex lock;
+ int user; /* user count for exclusive use */
+ int removed; /* device disconnected */
+
+ int streaming; /* Are we streaming video? */
+
+ char *fbuf; /* Videodev buffer area */
+
+ struct urb *urb[SE401_NUMSBUF];
+ struct urb *inturb;
+
+ int button;
+ int buttonpressed;
+
+ int curframe; /* Current receiving frame */
+ struct se401_frame frame[SE401_NUMFRAMES];
+ int readcount;
+ int framecount;
+ int error;
+ int dropped;
+
+ int scratch_next;
+ int scratch_use;
+ int scratch_overflow;
+ struct se401_scratch scratch[SE401_NUMSCRATCH];
+
+ /* Decoder specific data: */
+ unsigned char vlcdata[SE401_VLCDATALEN];
+ int vlcdatapos;
+ int bayeroffset;
+
+ struct se401_sbuf sbuf[SE401_NUMSBUF];
+
+ wait_queue_head_t wq; /* Processes waiting */
+
+ int nullpackets;
+};
+
+
+
+#endif
+
diff --git a/linux/drivers/media/video/stradis.c b/linux/drivers/media/video/stradis.c
new file mode 100644
index 000000000..9aad8d66d
--- /dev/null
+++ b/linux/drivers/media/video/stradis.c
@@ -0,0 +1,2213 @@
+/*
+ * stradis.c - stradis 4:2:2 mpeg decoder driver
+ *
+ * Stradis 4:2:2 MPEG-2 Decoder Driver
+ * Copyright (C) 1999 Nathan Laredo <laredo@gnu.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/pci.h>
+#include <linux/signal.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/sched.h>
+#include <asm/types.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <asm/uaccess.h>
+#include <linux/vmalloc.h>
+#include "compat.h"
+#include <linux/videodev.h>
+
+#include "saa7146.h"
+#include "saa7146reg.h"
+#include "ibmmpeg2.h"
+#include "saa7121.h"
+#include "cs8420.h"
+
+#define DEBUG(x) /* debug driver */
+#undef IDEBUG /* debug irq handler */
+#undef MDEBUG /* debug memory management */
+
+#define SAA7146_MAX 6
+
+static struct saa7146 saa7146s[SAA7146_MAX];
+
+static int saa_num = 0; /* number of SAA7146s in use */
+
+static int video_nr = -1;
+module_param(video_nr, int, 0);
+MODULE_LICENSE("GPL");
+
+#define nDebNormal 0x00480000
+#define nDebNoInc 0x00480000
+#define nDebVideo 0xd0480000
+#define nDebAudio 0xd0400000
+#define nDebDMA 0x02c80000
+
+#define oDebNormal 0x13c80000
+#define oDebNoInc 0x13c80000
+#define oDebVideo 0xd1080000
+#define oDebAudio 0xd1080000
+#define oDebDMA 0x03080000
+
+#define NewCard (saa->boardcfg[3])
+#define ChipControl (saa->boardcfg[1])
+#define NTSCFirstActive (saa->boardcfg[4])
+#define PALFirstActive (saa->boardcfg[5])
+#define NTSCLastActive (saa->boardcfg[54])
+#define PALLastActive (saa->boardcfg[55])
+#define Have2MB (saa->boardcfg[18] & 0x40)
+#define HaveCS8420 (saa->boardcfg[18] & 0x04)
+#define IBMMPEGCD20 (saa->boardcfg[18] & 0x20)
+#define HaveCS3310 (saa->boardcfg[18] & 0x01)
+#define CS3310MaxLvl ((saa->boardcfg[30] << 8) | saa->boardcfg[31])
+#define HaveCS4341 (saa->boardcfg[40] == 2)
+#define SDIType (saa->boardcfg[27])
+#define CurrentMode (saa->boardcfg[2])
+
+#define debNormal (NewCard ? nDebNormal : oDebNormal)
+#define debNoInc (NewCard ? nDebNoInc : oDebNoInc)
+#define debVideo (NewCard ? nDebVideo : oDebVideo)
+#define debAudio (NewCard ? nDebAudio : oDebAudio)
+#define debDMA (NewCard ? nDebDMA : oDebDMA)
+
+#ifdef USE_RESCUE_EEPROM_SDM275
+static unsigned char rescue_eeprom[64] = {
+ 0x00, 0x01, 0x04, 0x13, 0x26, 0x0f, 0x10, 0x00, 0x00, 0x00, 0x43, 0x63,
+ 0x22, 0x01, 0x29, 0x15, 0x73, 0x00, 0x1f, 'd', 'e', 'c', 'x', 'l',
+ 'd', 'v', 'a', 0x02, 0x00, 0x01, 0x00, 0xcc, 0xa4, 0x63, 0x09, 0xe2,
+ 0x10, 0x00, 0x0a, 0x00, 0x02, 0x02, 'd', 'e', 'c', 'x', 'l', 'a',
+ 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+};
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* Hardware I2C functions */
+static void I2CWipe(struct saa7146 *saa)
+{
+ int i;
+ /* set i2c to ~=100kHz, abort transfer, clear busy */
+ saawrite(0x600 | SAA7146_I2C_ABORT, SAA7146_I2C_STATUS);
+ saawrite((SAA7146_MC2_UPLD_I2C << 16) |
+ SAA7146_MC2_UPLD_I2C, SAA7146_MC2);
+ /* wait for i2c registers to be programmed */
+ for (i = 0; i < 1000 &&
+ !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++)
+ schedule();
+ saawrite(0x600, SAA7146_I2C_STATUS);
+ saawrite((SAA7146_MC2_UPLD_I2C << 16) |
+ SAA7146_MC2_UPLD_I2C, SAA7146_MC2);
+ /* wait for i2c registers to be programmed */
+ for (i = 0; i < 1000 &&
+ !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++)
+ schedule();
+ saawrite(0x600, SAA7146_I2C_STATUS);
+ saawrite((SAA7146_MC2_UPLD_I2C << 16) |
+ SAA7146_MC2_UPLD_I2C, SAA7146_MC2);
+ /* wait for i2c registers to be programmed */
+ for (i = 0; i < 1000 &&
+ !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++)
+ schedule();
+}
+
+/* read I2C */
+static int I2CRead(struct saa7146 *saa, unsigned char addr,
+ unsigned char subaddr, int dosub)
+{
+ int i;
+
+ if (saaread(SAA7146_I2C_STATUS) & 0x3c)
+ I2CWipe(saa);
+ for (i = 0;
+ i < 1000 && (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY);
+ i++)
+ schedule();
+ if (i == 1000)
+ I2CWipe(saa);
+ if (dosub)
+ saawrite(((addr & 0xfe) << 24) | (((addr | 1) & 0xff) << 8) |
+ ((subaddr & 0xff) << 16) | 0xed, SAA7146_I2C_TRANSFER);
+ else
+ saawrite(((addr & 0xfe) << 24) | (((addr | 1) & 0xff) << 16) |
+ 0xf1, SAA7146_I2C_TRANSFER);
+ saawrite((SAA7146_MC2_UPLD_I2C << 16) |
+ SAA7146_MC2_UPLD_I2C, SAA7146_MC2);
+ /* wait for i2c registers to be programmed */
+ for (i = 0; i < 1000 &&
+ !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++)
+ schedule();
+ /* wait for valid data */
+ for (i = 0; i < 1000 &&
+ (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); i++)
+ schedule();
+ if (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_ERR)
+ return -1;
+ if (i == 1000)
+ printk("i2c setup read timeout\n");
+ saawrite(0x41, SAA7146_I2C_TRANSFER);
+ saawrite((SAA7146_MC2_UPLD_I2C << 16) |
+ SAA7146_MC2_UPLD_I2C, SAA7146_MC2);
+ /* wait for i2c registers to be programmed */
+ for (i = 0; i < 1000 &&
+ !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_I2C); i++)
+ schedule();
+ /* wait for valid data */
+ for (i = 0; i < 1000 &&
+ (saaread(SAA7146_I2C_TRANSFER) & SAA7146_I2C_BUSY); i++)
+ schedule();
+ if (saaread(SAA7146_I2C_TRANSFER) & SAA7146_I2C_ERR)
+ return -1;
+ if (i == 1000)
+ printk("i2c read timeout\n");
+ return ((saaread(SAA7146_I2C_TRANSFER) >> 24) & 0xff);
+}
+
+/* set both to write both bytes, reset it to write only b1 */
+
+static int I2CWrite(struct saa7146 *saa, unsigned char addr, unsigned char b1,
+ unsigned char b2, int both)
+{
+ int i;
+ u32 data;
+
+ if (saaread(SAA7146_I2C_STATUS) & 0x3c)
+ I2CWipe(saa);
+ for (i = 0; i < 1000 &&
+ (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); i++)
+ schedule();
+ if (i == 1000)
+ I2CWipe(saa);
+ data = ((addr & 0xfe) << 24) | ((b1 & 0xff) << 16);
+ if (both)
+ data |= ((b2 & 0xff) << 8) | 0xe5;
+ else
+ data |= 0xd1;
+ saawrite(data, SAA7146_I2C_TRANSFER);
+ saawrite((SAA7146_MC2_UPLD_I2C << 16) | SAA7146_MC2_UPLD_I2C,
+ SAA7146_MC2);
+ return 0;
+}
+
+static void attach_inform(struct saa7146 *saa, int id)
+{
+ int i;
+
+ DEBUG(printk(KERN_DEBUG "stradis%d: i2c: device found=%02x\n", saa->nr,
+ id));
+ if (id == 0xa0) { /* we have rev2 or later board, fill in info */
+ for (i = 0; i < 64; i++)
+ saa->boardcfg[i] = I2CRead(saa, 0xa0, i, 1);
+#ifdef USE_RESCUE_EEPROM_SDM275
+ if (saa->boardcfg[0] != 0) {
+ printk("stradis%d: WARNING: EEPROM STORED VALUES HAVE "
+ "BEEN IGNORED\n", saa->nr);
+ for (i = 0; i < 64; i++)
+ saa->boardcfg[i] = rescue_eeprom[i];
+ }
+#endif
+ printk("stradis%d: config =", saa->nr);
+ for (i = 0; i < 51; i++) {
+ printk(" %02x", saa->boardcfg[i]);
+ }
+ printk("\n");
+ }
+}
+
+static void I2CBusScan(struct saa7146 *saa)
+{
+ int i;
+ for (i = 0; i < 0xff; i += 2)
+ if ((I2CRead(saa, i, 0, 0)) >= 0)
+ attach_inform(saa, i);
+}
+
+static int debiwait_maxwait = 0;
+
+static int wait_for_debi_done(struct saa7146 *saa)
+{
+ int i;
+
+ /* wait for registers to be programmed */
+ for (i = 0; i < 100000 &&
+ !(saaread(SAA7146_MC2) & SAA7146_MC2_UPLD_DEBI); i++)
+ saaread(SAA7146_MC2);
+ /* wait for transfer to complete */
+ for (i = 0; i < 500000 &&
+ (saaread(SAA7146_PSR) & SAA7146_PSR_DEBI_S); i++)
+ saaread(SAA7146_MC2);
+
+ if (i > debiwait_maxwait)
+ printk("wait-for-debi-done maxwait: %d\n",
+ debiwait_maxwait = i);
+
+ if (i == 500000)
+ return -1;
+
+ return 0;
+}
+
+static int debiwrite(struct saa7146 *saa, u32 config, int addr,
+ u32 val, int count)
+{
+ u32 cmd;
+ if (count <= 0 || count > 32764)
+ return -1;
+ if (wait_for_debi_done(saa) < 0)
+ return -1;
+ saawrite(config, SAA7146_DEBI_CONFIG);
+ if (count <= 4) /* immediate transfer */
+ saawrite(val, SAA7146_DEBI_AD);
+ else /* block transfer */
+ saawrite(virt_to_bus(saa->dmadebi), SAA7146_DEBI_AD);
+ saawrite((cmd = (count << 17) | (addr & 0xffff)), SAA7146_DEBI_COMMAND);
+ saawrite((SAA7146_MC2_UPLD_DEBI << 16) | SAA7146_MC2_UPLD_DEBI,
+ SAA7146_MC2);
+ return 0;
+}
+
+static u32 debiread(struct saa7146 *saa, u32 config, int addr, int count)
+{
+ u32 result = 0;
+
+ if (count > 32764 || count <= 0)
+ return 0;
+ if (wait_for_debi_done(saa) < 0)
+ return 0;
+ saawrite(virt_to_bus(saa->dmadebi), SAA7146_DEBI_AD);
+ saawrite((count << 17) | 0x10000 | (addr & 0xffff),
+ SAA7146_DEBI_COMMAND);
+ saawrite(config, SAA7146_DEBI_CONFIG);
+ saawrite((SAA7146_MC2_UPLD_DEBI << 16) | SAA7146_MC2_UPLD_DEBI,
+ SAA7146_MC2);
+ if (count > 4) /* not an immediate transfer */
+ return count;
+ wait_for_debi_done(saa);
+ result = saaread(SAA7146_DEBI_AD);
+ if (count == 1)
+ result &= 0xff;
+ if (count == 2)
+ result &= 0xffff;
+ if (count == 3)
+ result &= 0xffffff;
+ return result;
+}
+
+static void do_irq_send_data(struct saa7146 *saa)
+{
+ int split, audbytes, vidbytes;
+
+ saawrite(SAA7146_PSR_PIN1, SAA7146_IER);
+ /* if special feature mode in effect, disable audio sending */
+ if (saa->playmode != VID_PLAY_NORMAL)
+ saa->audtail = saa->audhead = 0;
+ if (saa->audhead <= saa->audtail)
+ audbytes = saa->audtail - saa->audhead;
+ else
+ audbytes = 65536 - (saa->audhead - saa->audtail);
+ if (saa->vidhead <= saa->vidtail)
+ vidbytes = saa->vidtail - saa->vidhead;
+ else
+ vidbytes = 524288 - (saa->vidhead - saa->vidtail);
+ if (audbytes == 0 && vidbytes == 0 && saa->osdtail == saa->osdhead) {
+ saawrite(0, SAA7146_IER);
+ return;
+ }
+ /* if at least 1 block audio waiting and audio fifo isn't full */
+ if (audbytes >= 2048 && (debiread(saa, debNormal, IBM_MP2_AUD_FIFO, 2)
+ & 0xff) < 60) {
+ if (saa->audhead > saa->audtail)
+ split = 65536 - saa->audhead;
+ else
+ split = 0;
+ audbytes = 2048;
+ if (split > 0 && split < 2048) {
+ memcpy(saa->dmadebi, saa->audbuf + saa->audhead, split);
+ saa->audhead = 0;
+ audbytes -= split;
+ } else
+ split = 0;
+ memcpy(saa->dmadebi + split, saa->audbuf + saa->audhead,
+ audbytes);
+ saa->audhead += audbytes;
+ saa->audhead &= 0xffff;
+ debiwrite(saa, debAudio, (NewCard ? IBM_MP2_AUD_FIFO :
+ IBM_MP2_AUD_FIFOW), 0, 2048);
+ wake_up_interruptible(&saa->audq);
+ /* if at least 1 block video waiting and video fifo isn't full */
+ } else if (vidbytes >= 30720 && (debiread(saa, debNormal,
+ IBM_MP2_FIFO, 2)) < 16384) {
+ if (saa->vidhead > saa->vidtail)
+ split = 524288 - saa->vidhead;
+ else
+ split = 0;
+ vidbytes = 30720;
+ if (split > 0 && split < 30720) {
+ memcpy(saa->dmadebi, saa->vidbuf + saa->vidhead, split);
+ saa->vidhead = 0;
+ vidbytes -= split;
+ } else
+ split = 0;
+ memcpy(saa->dmadebi + split, saa->vidbuf + saa->vidhead,
+ vidbytes);
+ saa->vidhead += vidbytes;
+ saa->vidhead &= 0x7ffff;
+ debiwrite(saa, debVideo, (NewCard ? IBM_MP2_FIFO :
+ IBM_MP2_FIFOW), 0, 30720);
+ wake_up_interruptible(&saa->vidq);
+ }
+ saawrite(SAA7146_PSR_DEBI_S | SAA7146_PSR_PIN1, SAA7146_IER);
+}
+
+static void send_osd_data(struct saa7146 *saa)
+{
+ int size = saa->osdtail - saa->osdhead;
+ if (size > 30720)
+ size = 30720;
+ /* ensure some multiple of 8 bytes is transferred */
+ size = 8 * ((size + 8) >> 3);
+ if (size) {
+ debiwrite(saa, debNormal, IBM_MP2_OSD_ADDR,
+ (saa->osdhead >> 3), 2);
+ memcpy(saa->dmadebi, &saa->osdbuf[saa->osdhead], size);
+ saa->osdhead += size;
+ /* block transfer of next 8 bytes to ~32k bytes */
+ debiwrite(saa, debNormal, IBM_MP2_OSD_DATA, 0, size);
+ }
+ if (saa->osdhead >= saa->osdtail) {
+ saa->osdhead = saa->osdtail = 0;
+ debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00c, 2);
+ }
+}
+
+static irqreturn_t saa7146_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct saa7146 *saa = dev_id;
+ u32 stat, astat;
+ int count;
+ int handled = 0;
+
+ count = 0;
+ while (1) {
+ /* get/clear interrupt status bits */
+ stat = saaread(SAA7146_ISR);
+ astat = stat & saaread(SAA7146_IER);
+ if (!astat)
+ break;
+ handled = 1;
+ saawrite(astat, SAA7146_ISR);
+ if (astat & SAA7146_PSR_DEBI_S) {
+ do_irq_send_data(saa);
+ }
+ if (astat & SAA7146_PSR_PIN1) {
+ int istat;
+ /* the following read will trigger DEBI_S */
+ istat = debiread(saa, debNormal, IBM_MP2_HOST_INT, 2);
+ if (istat & 1) {
+ saawrite(0, SAA7146_IER);
+ send_osd_data(saa);
+ saawrite(SAA7146_PSR_DEBI_S |
+ SAA7146_PSR_PIN1, SAA7146_IER);
+ }
+ if (istat & 0x20) { /* Video Start */
+ saa->vidinfo.frame_count++;
+ }
+ if (istat & 0x400) { /* Picture Start */
+ /* update temporal reference */
+ }
+ if (istat & 0x200) { /* Picture Resolution Change */
+ /* read new resolution */
+ }
+ if (istat & 0x100) { /* New User Data found */
+ /* read new user data */
+ }
+ if (istat & 0x1000) { /* new GOP/SMPTE */
+ /* read new SMPTE */
+ }
+ if (istat & 0x8000) { /* Sequence Start Code */
+ /* reset frame counter, load sizes */
+ saa->vidinfo.frame_count = 0;
+ saa->vidinfo.h_size = 704;
+ saa->vidinfo.v_size = 480;
+#if 0
+ if (saa->endmarkhead != saa->endmarktail) {
+ saa->audhead =
+ saa->endmark[saa->endmarkhead];
+ saa->endmarkhead++;
+ if (saa->endmarkhead >= MAX_MARKS)
+ saa->endmarkhead = 0;
+ }
+#endif
+ }
+ if (istat & 0x4000) { /* Sequence Error Code */
+ if (saa->endmarkhead != saa->endmarktail) {
+ saa->audhead =
+ saa->endmark[saa->endmarkhead];
+ saa->endmarkhead++;
+ if (saa->endmarkhead >= MAX_MARKS)
+ saa->endmarkhead = 0;
+ }
+ }
+ }
+#ifdef IDEBUG
+ if (astat & SAA7146_PSR_PPEF) {
+ IDEBUG(printk("stradis%d irq: PPEF\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_PABO) {
+ IDEBUG(printk("stradis%d irq: PABO\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_PPED) {
+ IDEBUG(printk("stradis%d irq: PPED\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_RPS_I1) {
+ IDEBUG(printk("stradis%d irq: RPS_I1\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_RPS_I0) {
+ IDEBUG(printk("stradis%d irq: RPS_I0\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_RPS_LATE1) {
+ IDEBUG(printk("stradis%d irq: RPS_LATE1\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_RPS_LATE0) {
+ IDEBUG(printk("stradis%d irq: RPS_LATE0\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_RPS_E1) {
+ IDEBUG(printk("stradis%d irq: RPS_E1\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_RPS_E0) {
+ IDEBUG(printk("stradis%d irq: RPS_E0\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_RPS_TO1) {
+ IDEBUG(printk("stradis%d irq: RPS_TO1\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_RPS_TO0) {
+ IDEBUG(printk("stradis%d irq: RPS_TO0\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_UPLD) {
+ IDEBUG(printk("stradis%d irq: UPLD\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_DEBI_E) {
+ IDEBUG(printk("stradis%d irq: DEBI_E\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_I2C_S) {
+ IDEBUG(printk("stradis%d irq: I2C_S\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_I2C_E) {
+ IDEBUG(printk("stradis%d irq: I2C_E\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_A2_IN) {
+ IDEBUG(printk("stradis%d irq: A2_IN\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_A2_OUT) {
+ IDEBUG(printk("stradis%d irq: A2_OUT\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_A1_IN) {
+ IDEBUG(printk("stradis%d irq: A1_IN\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_A1_OUT) {
+ IDEBUG(printk("stradis%d irq: A1_OUT\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_AFOU) {
+ IDEBUG(printk("stradis%d irq: AFOU\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_V_PE) {
+ IDEBUG(printk("stradis%d irq: V_PE\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_VFOU) {
+ IDEBUG(printk("stradis%d irq: VFOU\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_FIDA) {
+ IDEBUG(printk("stradis%d irq: FIDA\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_FIDB) {
+ IDEBUG(printk("stradis%d irq: FIDB\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_PIN3) {
+ IDEBUG(printk("stradis%d irq: PIN3\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_PIN2) {
+ IDEBUG(printk("stradis%d irq: PIN2\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_PIN0) {
+ IDEBUG(printk("stradis%d irq: PIN0\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_ECS) {
+ IDEBUG(printk("stradis%d irq: ECS\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_EC3S) {
+ IDEBUG(printk("stradis%d irq: EC3S\n", saa->nr));
+ }
+ if (astat & SAA7146_PSR_EC0S) {
+ IDEBUG(printk("stradis%d irq: EC0S\n", saa->nr));
+ }
+#endif
+ count++;
+ if (count > 15)
+ printk(KERN_WARNING "stradis%d: irq loop %d\n",
+ saa->nr, count);
+ if (count > 20) {
+ saawrite(0, SAA7146_IER);
+ printk(KERN_ERR
+ "stradis%d: IRQ loop cleared\n", saa->nr);
+ }
+ }
+ return IRQ_RETVAL(handled);
+}
+
+static int ibm_send_command(struct saa7146 *saa,
+ int command, int data, int chain)
+{
+ int i;
+
+ if (chain)
+ debiwrite(saa, debNormal, IBM_MP2_COMMAND, (command << 1)| 1,2);
+ else
+ debiwrite(saa, debNormal, IBM_MP2_COMMAND, command << 1, 2);
+ debiwrite(saa, debNormal, IBM_MP2_CMD_DATA, data, 2);
+ debiwrite(saa, debNormal, IBM_MP2_CMD_STAT, 1, 2);
+ for (i = 0; i < 100 &&
+ (debiread(saa, debNormal, IBM_MP2_CMD_STAT, 2) & 1); i++)
+ schedule();
+ if (i == 100)
+ return -1;
+ return 0;
+}
+
+static void cs4341_setlevel(struct saa7146 *saa, int left, int right)
+{
+ I2CWrite(saa, 0x22, 0x03, left > 94 ? 94 : left, 2);
+ I2CWrite(saa, 0x22, 0x04, right > 94 ? 94 : right, 2);
+}
+
+static void initialize_cs4341(struct saa7146 *saa)
+{
+ int i;
+ for (i = 0; i < 200; i++) {
+ /* auto mute off, power on, no de-emphasis */
+ /* I2S data up to 24-bit 64xFs internal SCLK */
+ I2CWrite(saa, 0x22, 0x01, 0x11, 2);
+ /* ATAPI mixer settings */
+ I2CWrite(saa, 0x22, 0x02, 0x49, 2);
+ /* attenuation left 3db */
+ I2CWrite(saa, 0x22, 0x03, 0x00, 2);
+ /* attenuation right 3db */
+ I2CWrite(saa, 0x22, 0x04, 0x00, 2);
+ I2CWrite(saa, 0x22, 0x01, 0x10, 2);
+ if (I2CRead(saa, 0x22, 0x02, 1) == 0x49)
+ break;
+ schedule();
+ }
+ printk("stradis%d: CS4341 initialized (%d)\n", saa->nr, i);
+ return;
+}
+
+static void initialize_cs8420(struct saa7146 *saa, int pro)
+{
+ int i;
+ u8 *sequence;
+ if (pro)
+ sequence = mode8420pro;
+ else
+ sequence = mode8420con;
+ for (i = 0; i < INIT8420LEN; i++)
+ I2CWrite(saa, 0x20, init8420[i * 2], init8420[i * 2 + 1], 2);
+ for (i = 0; i < MODE8420LEN; i++)
+ I2CWrite(saa, 0x20, sequence[i * 2], sequence[i * 2 + 1], 2);
+ printk("stradis%d: CS8420 initialized\n", saa->nr);
+}
+
+static void initialize_saa7121(struct saa7146 *saa, int dopal)
+{
+ int i, mod;
+ u8 *sequence;
+ if (dopal)
+ sequence = init7121pal;
+ else
+ sequence = init7121ntsc;
+ mod = saaread(SAA7146_PSR) & 0x08;
+ /* initialize PAL/NTSC video encoder */
+ for (i = 0; i < INIT7121LEN; i++) {
+ if (NewCard) { /* handle new card encoder differences */
+ if (sequence[i * 2] == 0x3a)
+ I2CWrite(saa, 0x88, 0x3a, 0x13, 2);
+ else if (sequence[i * 2] == 0x6b)
+ I2CWrite(saa, 0x88, 0x6b, 0x20, 2);
+ else if (sequence[i * 2] == 0x6c)
+ I2CWrite(saa, 0x88, 0x6c,
+ dopal ? 0x09 : 0xf5, 2);
+ else if (sequence[i * 2] == 0x6d)
+ I2CWrite(saa, 0x88, 0x6d,
+ dopal ? 0x20 : 0x00, 2);
+ else if (sequence[i * 2] == 0x7a)
+ I2CWrite(saa, 0x88, 0x7a,
+ dopal ? (PALFirstActive - 1) :
+ (NTSCFirstActive - 4), 2);
+ else if (sequence[i * 2] == 0x7b)
+ I2CWrite(saa, 0x88, 0x7b,
+ dopal ? PALLastActive :
+ NTSCLastActive, 2);
+ else
+ I2CWrite(saa, 0x88, sequence[i * 2],
+ sequence[i * 2 + 1], 2);
+ } else {
+ if (sequence[i * 2] == 0x6b && mod)
+ I2CWrite(saa, 0x88, 0x6b,
+ (sequence[i * 2 + 1] ^ 0x09), 2);
+ else if (sequence[i * 2] == 0x7a)
+ I2CWrite(saa, 0x88, 0x7a,
+ dopal ? (PALFirstActive - 1) :
+ (NTSCFirstActive - 4), 2);
+ else if (sequence[i * 2] == 0x7b)
+ I2CWrite(saa, 0x88, 0x7b,
+ dopal ? PALLastActive :
+ NTSCLastActive, 2);
+ else
+ I2CWrite(saa, 0x88, sequence[i * 2],
+ sequence[i * 2 + 1], 2);
+ }
+ }
+}
+
+static void set_genlock_offset(struct saa7146 *saa, int noffset)
+{
+ int nCode;
+ int PixelsPerLine = 858;
+ if (CurrentMode == VIDEO_MODE_PAL)
+ PixelsPerLine = 864;
+ if (noffset > 500)
+ noffset = 500;
+ else if (noffset < -500)
+ noffset = -500;
+ nCode = noffset + 0x100;
+ if (nCode == 1)
+ nCode = 0x401;
+ else if (nCode < 1)
+ nCode = 0x400 + PixelsPerLine + nCode;
+ debiwrite(saa, debNormal, XILINX_GLDELAY, nCode, 2);
+}
+
+static void set_out_format(struct saa7146 *saa, int mode)
+{
+ initialize_saa7121(saa, (mode == VIDEO_MODE_NTSC ? 0 : 1));
+ saa->boardcfg[2] = mode;
+ /* do not adjust analog video parameters here, use saa7121 init */
+ /* you will affect the SDI output on the new card */
+ if (mode == VIDEO_MODE_PAL) { /* PAL */
+ debiwrite(saa, debNormal, XILINX_CTL0, 0x0808, 2);
+ mdelay(50);
+ saawrite(0x012002c0, SAA7146_NUM_LINE_BYTE1);
+ if (NewCard) {
+ debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, 0xe100, 2);
+ mdelay(50);
+ }
+ debiwrite(saa, debNormal, IBM_MP2_DISP_MODE,
+ NewCard ? 0xe500 : 0x6500, 2);
+ debiwrite(saa, debNormal, IBM_MP2_DISP_DLY,
+ (1 << 8) |
+ (NewCard ? PALFirstActive : PALFirstActive - 6), 2);
+ } else { /* NTSC */
+ debiwrite(saa, debNormal, XILINX_CTL0, 0x0800, 2);
+ mdelay(50);
+ saawrite(0x00f002c0, SAA7146_NUM_LINE_BYTE1);
+ debiwrite(saa, debNormal, IBM_MP2_DISP_MODE,
+ NewCard ? 0xe100 : 0x6100, 2);
+ debiwrite(saa, debNormal, IBM_MP2_DISP_DLY,
+ (1 << 8) |
+ (NewCard ? NTSCFirstActive : NTSCFirstActive - 6), 2);
+ }
+}
+
+/* Intialize bitmangler to map from a byte value to the mangled word that
+ * must be output to program the Xilinx part through the DEBI port.
+ * Xilinx Data Bit->DEBI Bit: 0->15 1->7 2->6 3->12 4->11 5->2 6->1 7->0
+ * transfer FPGA code, init IBM chip, transfer IBM microcode
+ * rev2 card mangles: 0->7 1->6 2->5 3->4 4->3 5->2 6->1 7->0
+ */
+static u16 bitmangler[256];
+
+static int initialize_fpga(struct video_code *bitdata)
+{
+ int i, num, startindex, failure = 0, loadtwo, loadfile = 0;
+ u16 *dmabuf;
+ u8 *newdma;
+ struct saa7146 *saa;
+
+ /* verify fpga code */
+ for (startindex = 0; startindex < bitdata->datasize; startindex++)
+ if (bitdata->data[startindex] == 255)
+ break;
+ if (startindex == bitdata->datasize) {
+ printk(KERN_INFO "stradis: bad fpga code\n");
+ return -1;
+ }
+ /* initialize all detected cards */
+ for (num = 0; num < saa_num; num++) {
+ saa = &saa7146s[num];
+ if (saa->boardcfg[0] > 20)
+ continue; /* card was programmed */
+ loadtwo = (saa->boardcfg[18] & 0x10);
+ if (!NewCard) /* we have an old board */
+ for (i = 0; i < 256; i++)
+ bitmangler[i] = ((i & 0x01) << 15) |
+ ((i & 0x02) << 6) | ((i & 0x04) << 4) |
+ ((i & 0x08) << 9) | ((i & 0x10) << 7) |
+ ((i & 0x20) >> 3) | ((i & 0x40) >> 5) |
+ ((i & 0x80) >> 7);
+ else /* else we have a new board */
+ for (i = 0; i < 256; i++)
+ bitmangler[i] = ((i & 0x01) << 7) |
+ ((i & 0x02) << 5) | ((i & 0x04) << 3) |
+ ((i & 0x08) << 1) | ((i & 0x10) >> 1) |
+ ((i & 0x20) >> 3) | ((i & 0x40) >> 5) |
+ ((i & 0x80) >> 7);
+
+ dmabuf = (u16 *) saa->dmadebi;
+ newdma = (u8 *) saa->dmadebi;
+ if (NewCard) { /* SDM2xxx */
+ if (!strncmp(bitdata->loadwhat, "decoder2", 8))
+ continue; /* fpga not for this card */
+ if (!strncmp(&saa->boardcfg[42], bitdata->loadwhat, 8))
+ loadfile = 1;
+ else if (loadtwo && !strncmp(&saa->boardcfg[19],
+ bitdata->loadwhat, 8))
+ loadfile = 2;
+ else if (!saa->boardcfg[42] && !strncmp("decxl",
+ bitdata->loadwhat, 8))
+ loadfile = 1; /* special */
+ else
+ continue; /* fpga not for this card */
+ if (loadfile != 1 && loadfile != 2)
+ continue; /* skip to next card */
+ if (saa->boardcfg[0] && loadfile == 1)
+ continue; /* skip to next card */
+ if (saa->boardcfg[0] != 1 && loadfile == 2)
+ continue; /* skip to next card */
+ saa->boardcfg[0]++; /* mark fpga handled */
+ printk("stradis%d: loading %s\n", saa->nr,
+ bitdata->loadwhat);
+ if (loadtwo && loadfile == 2)
+ goto send_fpga_stuff;
+ /* turn on the Audio interface to set PROG low */
+ saawrite(0x00400040, SAA7146_GPIO_CTRL);
+ saaread(SAA7146_PSR); /* ensure posted write */
+ /* wait for everyone to reset */
+ mdelay(10);
+ saawrite(0x00400000, SAA7146_GPIO_CTRL);
+ } else { /* original card */
+ if (strncmp(bitdata->loadwhat, "decoder2", 8))
+ continue; /* fpga not for this card */
+ /* Pull the Xilinx PROG signal WS3 low */
+ saawrite(0x02000200, SAA7146_MC1);
+ /* Turn on the Audio interface so can set PROG low */
+ saawrite(0x000000c0, SAA7146_ACON1);
+ /* Pull the Xilinx INIT signal (GPIO2) low */
+ saawrite(0x00400000, SAA7146_GPIO_CTRL);
+ /* Make sure everybody resets */
+ saaread(SAA7146_PSR); /* ensure posted write */
+ mdelay(10);
+ /* Release the Xilinx PROG signal */
+ saawrite(0x00000000, SAA7146_ACON1);
+ /* Turn off the Audio interface */
+ saawrite(0x02000000, SAA7146_MC1);
+ }
+ /* Release Xilinx INIT signal (WS2) */
+ saawrite(0x00000000, SAA7146_GPIO_CTRL);
+ /* Wait for the INIT to go High */
+ for (i = 0;
+ i < 10000 && !(saaread(SAA7146_PSR) & SAA7146_PSR_PIN2);
+ i++)
+ schedule();
+ if (i == 1000) {
+ printk(KERN_INFO "stradis%d: no fpga INIT\n", saa->nr);
+ return -1;
+ }
+send_fpga_stuff:
+ if (NewCard) {
+ for (i = startindex; i < bitdata->datasize; i++)
+ newdma[i - startindex] =
+ bitmangler[bitdata->data[i]];
+ debiwrite(saa, 0x01420000, 0, 0,
+ ((bitdata->datasize - startindex) + 5));
+ if (loadtwo && loadfile == 1) {
+ printk("stradis%d: awaiting 2nd FPGA bitfile\n",
+ saa->nr);
+ continue; /* skip to next card */
+ }
+ } else {
+ for (i = startindex; i < bitdata->datasize; i++)
+ dmabuf[i - startindex] =
+ bitmangler[bitdata->data[i]];
+ debiwrite(saa, 0x014a0000, 0, 0,
+ ((bitdata->datasize - startindex) + 5) * 2);
+ }
+ for (i = 0;
+ i < 1000 && !(saaread(SAA7146_PSR) & SAA7146_PSR_PIN2);
+ i++)
+ schedule();
+ if (i == 1000) {
+ printk(KERN_INFO "stradis%d: FPGA load failed\n",
+ saa->nr);
+ failure++;
+ continue;
+ }
+ if (!NewCard) {
+ /* Pull the Xilinx INIT signal (GPIO2) low */
+ saawrite(0x00400000, SAA7146_GPIO_CTRL);
+ saaread(SAA7146_PSR); /* ensure posted write */
+ mdelay(2);
+ saawrite(0x00000000, SAA7146_GPIO_CTRL);
+ mdelay(2);
+ }
+ printk(KERN_INFO "stradis%d: FPGA Loaded\n", saa->nr);
+ saa->boardcfg[0] = 26; /* mark fpga programmed */
+ /* set VXCO to its lowest frequency */
+ debiwrite(saa, debNormal, XILINX_PWM, 0, 2);
+ if (NewCard) {
+ /* mute CS3310 */
+ if (HaveCS3310)
+ debiwrite(saa, debNormal, XILINX_CS3310_CMPLT,
+ 0, 2);
+ /* set VXCO to PWM mode, release reset, blank on */
+ debiwrite(saa, debNormal, XILINX_CTL0, 0xffc4, 2);
+ mdelay(10);
+ /* unmute CS3310 */
+ if (HaveCS3310)
+ debiwrite(saa, debNormal, XILINX_CTL0,
+ 0x2020, 2);
+ }
+ /* set source Black */
+ debiwrite(saa, debNormal, XILINX_CTL0, 0x1707, 2);
+ saa->boardcfg[4] = 22; /* set NTSC First Active Line */
+ saa->boardcfg[5] = 23; /* set PAL First Active Line */
+ saa->boardcfg[54] = 2; /* set NTSC Last Active Line - 256 */
+ saa->boardcfg[55] = 54; /* set PAL Last Active Line - 256 */
+ set_out_format(saa, VIDEO_MODE_NTSC);
+ mdelay(50);
+ /* begin IBM chip init */
+ debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 4, 2);
+ saaread(SAA7146_PSR); /* wait for reset */
+ mdelay(5);
+ debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 0, 2);
+ debiread(saa, debNormal, IBM_MP2_CHIP_CONTROL, 2);
+ debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 0x10, 2);
+ debiwrite(saa, debNormal, IBM_MP2_CMD_ADDR, 0, 2);
+ debiwrite(saa, debNormal, IBM_MP2_CHIP_MODE, 0x2e, 2);
+ if (NewCard) {
+ mdelay(5);
+ /* set i2s rate converter to 48KHz */
+ debiwrite(saa, debNormal, 0x80c0, 6, 2);
+ /* we must init CS8420 first since rev b pulls i2s */
+ /* master clock low and CS4341 needs i2s master to */
+ /* run the i2c port. */
+ if (HaveCS8420)
+ /* 0=consumer, 1=pro */
+ initialize_cs8420(saa, 0);
+
+ mdelay(5);
+ if (HaveCS4341)
+ initialize_cs4341(saa);
+ }
+ debiwrite(saa, debNormal, IBM_MP2_INFC_CTL, 0x48, 2);
+ debiwrite(saa, debNormal, IBM_MP2_BEEP_CTL, 0xa000, 2);
+ debiwrite(saa, debNormal, IBM_MP2_DISP_LBOR, 0, 2);
+ debiwrite(saa, debNormal, IBM_MP2_DISP_TBOR, 0, 2);
+ if (NewCard)
+ set_genlock_offset(saa, 0);
+ debiwrite(saa, debNormal, IBM_MP2_FRNT_ATTEN, 0, 2);
+#if 0
+ /* enable genlock */
+ debiwrite(saa, debNormal, XILINX_CTL0, 0x8000, 2);
+#else
+ /* disable genlock */
+ debiwrite(saa, debNormal, XILINX_CTL0, 0x8080, 2);
+#endif
+ }
+
+ return failure;
+}
+
+static int do_ibm_reset(struct saa7146 *saa)
+{
+ /* failure if decoder not previously programmed */
+ if (saa->boardcfg[0] < 37)
+ return -EIO;
+ /* mute CS3310 */
+ if (HaveCS3310)
+ debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, 0, 2);
+ /* disable interrupts */
+ saawrite(0, SAA7146_IER);
+ saa->audhead = saa->audtail = 0;
+ saa->vidhead = saa->vidtail = 0;
+ /* tristate debi bus, disable debi transfers */
+ saawrite(0x00880000, SAA7146_MC1);
+ /* ensure posted write */
+ saaread(SAA7146_MC1);
+ mdelay(50);
+ /* re-enable debi transfers */
+ saawrite(0x00880088, SAA7146_MC1);
+ /* set source Black */
+ debiwrite(saa, debNormal, XILINX_CTL0, 0x1707, 2);
+ /* begin IBM chip init */
+ set_out_format(saa, CurrentMode);
+ debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 4, 2);
+ saaread(SAA7146_PSR); /* wait for reset */
+ mdelay(5);
+ debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, 0, 2);
+ debiread(saa, debNormal, IBM_MP2_CHIP_CONTROL, 2);
+ debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, ChipControl, 2);
+ debiwrite(saa, debNormal, IBM_MP2_CHIP_MODE, 0x2e, 2);
+ if (NewCard) {
+ mdelay(5);
+ /* set i2s rate converter to 48KHz */
+ debiwrite(saa, debNormal, 0x80c0, 6, 2);
+ /* we must init CS8420 first since rev b pulls i2s */
+ /* master clock low and CS4341 needs i2s master to */
+ /* run the i2c port. */
+ if (HaveCS8420)
+ /* 0=consumer, 1=pro */
+ initialize_cs8420(saa, 1);
+
+ mdelay(5);
+ if (HaveCS4341)
+ initialize_cs4341(saa);
+ }
+ debiwrite(saa, debNormal, IBM_MP2_INFC_CTL, 0x48, 2);
+ debiwrite(saa, debNormal, IBM_MP2_BEEP_CTL, 0xa000, 2);
+ debiwrite(saa, debNormal, IBM_MP2_DISP_LBOR, 0, 2);
+ debiwrite(saa, debNormal, IBM_MP2_DISP_TBOR, 0, 2);
+ if (NewCard)
+ set_genlock_offset(saa, 0);
+ debiwrite(saa, debNormal, IBM_MP2_FRNT_ATTEN, 0, 2);
+ debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0x2000, 2);
+ debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4552, 2);
+ if (ibm_send_command(saa, IBM_MP2_CONFIG_DECODER,
+ (ChipControl == 0x43 ? 0xe800 : 0xe000), 1)) {
+ printk(KERN_ERR "stradis%d: IBM config failed\n", saa->nr);
+ }
+ if (HaveCS3310) {
+ int i = CS3310MaxLvl;
+ debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, ((i << 8)| i),2);
+ }
+ /* start video decoder */
+ debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, ChipControl, 2);
+ /* 256k vid, 3520 bytes aud */
+ debiwrite(saa, debNormal, IBM_MP2_RB_THRESHOLD, 0x4037, 2);
+ debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4573, 2);
+ ibm_send_command(saa, IBM_MP2_PLAY, 0, 0);
+ /* enable buffer threshold irq */
+ debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00c, 2);
+ /* clear pending interrupts */
+ debiread(saa, debNormal, IBM_MP2_HOST_INT, 2);
+ debiwrite(saa, debNormal, XILINX_CTL0, 0x1711, 2);
+
+ return 0;
+}
+
+/* load the decoder microcode */
+static int initialize_ibmmpeg2(struct video_code *microcode)
+{
+ int i, num;
+ struct saa7146 *saa;
+
+ for (num = 0; num < saa_num; num++) {
+ saa = &saa7146s[num];
+ /* check that FPGA is loaded */
+ debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0xa55a, 2);
+ i = debiread(saa, debNormal, IBM_MP2_OSD_SIZE, 2);
+ if (i != 0xa55a) {
+ printk(KERN_INFO "stradis%d: %04x != 0xa55a\n",
+ saa->nr, i);
+#if 0
+ return -1;
+#endif
+ }
+ if (!strncmp(microcode->loadwhat, "decoder.vid", 11)) {
+ if (saa->boardcfg[0] > 27)
+ continue; /* skip to next card */
+ /* load video control store */
+ saa->boardcfg[1] = 0x13; /* no-sync default */
+ debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 1, 2);
+ debiwrite(saa, debNormal, IBM_MP2_PROC_IADDR, 0, 2);
+ for (i = 0; i < microcode->datasize / 2; i++)
+ debiwrite(saa, debNormal, IBM_MP2_PROC_IDATA,
+ (microcode->data[i * 2] << 8) |
+ microcode->data[i * 2 + 1], 2);
+ debiwrite(saa, debNormal, IBM_MP2_PROC_IADDR, 0, 2);
+ debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 0, 2);
+ debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL,
+ ChipControl, 2);
+ saa->boardcfg[0] = 28;
+ }
+ if (!strncmp(microcode->loadwhat, "decoder.aud", 11)) {
+ if (saa->boardcfg[0] > 35)
+ continue; /* skip to next card */
+ /* load audio control store */
+ debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 1, 2);
+ debiwrite(saa, debNormal, IBM_MP2_AUD_IADDR, 0, 2);
+ for (i = 0; i < microcode->datasize; i++)
+ debiwrite(saa, debNormal, IBM_MP2_AUD_IDATA,
+ microcode->data[i], 1);
+ debiwrite(saa, debNormal, IBM_MP2_AUD_IADDR, 0, 2);
+ debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 0, 2);
+ debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0x2000, 2);
+ debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4552, 2);
+ if (ibm_send_command(saa, IBM_MP2_CONFIG_DECODER,
+ 0xe000, 1)) {
+ printk(KERN_ERR "stradis%d: IBM config "
+ "failed\n", saa->nr);
+ return -1;
+ }
+ /* set PWM to center value */
+ if (NewCard) {
+ debiwrite(saa, debNormal, XILINX_PWM,
+ saa->boardcfg[14] +
+ (saa->boardcfg[13] << 8), 2);
+ } else
+ debiwrite(saa, debNormal, XILINX_PWM, 0x46, 2);
+
+ if (HaveCS3310) {
+ i = CS3310MaxLvl;
+ debiwrite(saa, debNormal, XILINX_CS3310_CMPLT,
+ (i << 8) | i, 2);
+ }
+ printk(KERN_INFO "stradis%d: IBM MPEGCD%d Inited\n",
+ saa->nr, 18 + (debiread(saa, debNormal,
+ IBM_MP2_CHIP_CONTROL, 2) >> 12));
+ /* start video decoder */
+ debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL,
+ ChipControl, 2);
+ debiwrite(saa, debNormal, IBM_MP2_RB_THRESHOLD, 0x4037,
+ 2); /* 256k vid, 3520 bytes aud */
+ debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4573, 2);
+ ibm_send_command(saa, IBM_MP2_PLAY, 0, 0);
+ /* enable buffer threshold irq */
+ debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00c, 2);
+ debiread(saa, debNormal, IBM_MP2_HOST_INT, 2);
+ /* enable gpio irq */
+ saawrite(0x00002000, SAA7146_GPIO_CTRL);
+ /* enable decoder output to HPS */
+ debiwrite(saa, debNormal, XILINX_CTL0, 0x1711, 2);
+ saa->boardcfg[0] = 37;
+ }
+ }
+
+ return 0;
+}
+
+static u32 palette2fmt[] = { /* some of these YUV translations are wrong */
+ 0xffffffff, 0x86000000, 0x87000000, 0x80000000, 0x8100000, 0x82000000,
+ 0x83000000, 0x00000000, 0x03000000, 0x03000000, 0x0a00000, 0x03000000,
+ 0x06000000, 0x00000000, 0x03000000, 0x0a000000, 0x0300000
+};
+static int bpp2fmt[4] = {
+ VIDEO_PALETTE_HI240, VIDEO_PALETTE_RGB565, VIDEO_PALETTE_RGB24,
+ VIDEO_PALETTE_RGB32
+};
+
+/* I wish I could find a formula to calculate these... */
+static u32 h_prescale[64] = {
+ 0x10000000, 0x18040202, 0x18080000, 0x380c0606, 0x38100204, 0x38140808,
+ 0x38180000, 0x381c0000, 0x3820161c, 0x38242a3b, 0x38281230, 0x382c4460,
+ 0x38301040, 0x38340080, 0x38380000, 0x383c0000, 0x3840fefe, 0x3844ee9f,
+ 0x3848ee9f, 0x384cee9f, 0x3850ee9f, 0x38542a3b, 0x38581230, 0x385c0000,
+ 0x38600000, 0x38640000, 0x38680000, 0x386c0000, 0x38700000, 0x38740000,
+ 0x38780000, 0x387c0000, 0x30800000, 0x38840000, 0x38880000, 0x388c0000,
+ 0x38900000, 0x38940000, 0x38980000, 0x389c0000, 0x38a00000, 0x38a40000,
+ 0x38a80000, 0x38ac0000, 0x38b00000, 0x38b40000, 0x38b80000, 0x38bc0000,
+ 0x38c00000, 0x38c40000, 0x38c80000, 0x38cc0000, 0x38d00000, 0x38d40000,
+ 0x38d80000, 0x38dc0000, 0x38e00000, 0x38e40000, 0x38e80000, 0x38ec0000,
+ 0x38f00000, 0x38f40000, 0x38f80000, 0x38fc0000,
+};
+static u32 v_gain[64] = {
+ 0x016000ff, 0x016100ff, 0x016100ff, 0x016200ff, 0x016200ff, 0x016200ff,
+ 0x016200ff, 0x016300ff, 0x016300ff, 0x016300ff, 0x016300ff, 0x016300ff,
+ 0x016300ff, 0x016300ff, 0x016300ff, 0x016400ff, 0x016400ff, 0x016400ff,
+ 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff,
+ 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff,
+ 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff,
+ 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff,
+ 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff,
+ 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff,
+ 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff,
+ 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff,
+};
+
+static void saa7146_set_winsize(struct saa7146 *saa)
+{
+ u32 format;
+ int offset, yacl, ysci;
+ saa->win.color_fmt = format =
+ (saa->win.depth == 15) ? palette2fmt[VIDEO_PALETTE_RGB555] :
+ palette2fmt[bpp2fmt[(saa->win.bpp - 1) & 3]];
+ offset = saa->win.x * saa->win.bpp + saa->win.y * saa->win.bpl;
+ saawrite(saa->win.vidadr + offset, SAA7146_BASE_EVEN1);
+ saawrite(saa->win.vidadr + offset + saa->win.bpl, SAA7146_BASE_ODD1);
+ saawrite(saa->win.bpl * 2, SAA7146_PITCH1);
+ saawrite(saa->win.vidadr + saa->win.bpl * saa->win.sheight,
+ SAA7146_PROT_ADDR1);
+ saawrite(0, SAA7146_PAGE1);
+ saawrite(format | 0x60, SAA7146_CLIP_FORMAT_CTRL);
+ offset = (704 / (saa->win.width - 1)) & 0x3f;
+ saawrite(h_prescale[offset], SAA7146_HPS_H_PRESCALE);
+ offset = (720896 / saa->win.width) / (offset + 1);
+ saawrite((offset << 12) | 0x0c, SAA7146_HPS_H_SCALE);
+ if (CurrentMode == VIDEO_MODE_NTSC) {
+ yacl = /*(480 / saa->win.height - 1) & 0x3f */ 0;
+ ysci = 1024 - (saa->win.height * 1024 / 480);
+ } else {
+ yacl = /*(576 / saa->win.height - 1) & 0x3f */ 0;
+ ysci = 1024 - (saa->win.height * 1024 / 576);
+ }
+ saawrite((1 << 31) | (ysci << 21) | (yacl << 15), SAA7146_HPS_V_SCALE);
+ saawrite(v_gain[yacl], SAA7146_HPS_V_GAIN);
+ saawrite(((SAA7146_MC2_UPLD_DMA1 | SAA7146_MC2_UPLD_HPS_V |
+ SAA7146_MC2_UPLD_HPS_H) << 16) | (SAA7146_MC2_UPLD_DMA1 |
+ SAA7146_MC2_UPLD_HPS_V | SAA7146_MC2_UPLD_HPS_H), SAA7146_MC2);
+}
+
+/* clip_draw_rectangle(cm,x,y,w,h) -- handle clipping an area
+ * bitmap is fixed width, 128 bytes (1024 pixels represented)
+ * arranged most-sigificant-bit-left in 32-bit words
+ * based on saa7146 clipping hardware, it swaps bytes if LE
+ * much of this makes up for egcs brain damage -- so if you
+ * are wondering "why did he do this?" it is because the C
+ * was adjusted to generate the optimal asm output without
+ * writing non-portable __asm__ directives.
+ */
+
+static void clip_draw_rectangle(u32 *clipmap, int x, int y, int w, int h)
+{
+ register int startword, endword;
+ register u32 bitsleft, bitsright;
+ u32 *temp;
+ if (x < 0) {
+ w += x;
+ x = 0;
+ }
+ if (y < 0) {
+ h += y;
+ y = 0;
+ }
+ if (w <= 0 || h <= 0 || x > 1023 || y > 639)
+ return; /* throw away bad clips */
+ if (x + w > 1024)
+ w = 1024 - x;
+ if (y + h > 640)
+ h = 640 - y;
+ startword = (x >> 5);
+ endword = ((x + w) >> 5);
+ bitsleft = (0xffffffff >> (x & 31));
+ bitsright = (0xffffffff << (~((x + w) - (endword << 5))));
+ temp = &clipmap[(y << 5) + startword];
+ w = endword - startword;
+ if (!w) {
+ bitsleft |= bitsright;
+ for (y = 0; y < h; y++) {
+ *temp |= bitsleft;
+ temp += 32;
+ }
+ } else {
+ for (y = 0; y < h; y++) {
+ *temp++ |= bitsleft;
+ for (x = 1; x < w; x++)
+ *temp++ = 0xffffffff;
+ *temp |= bitsright;
+ temp += (32 - w);
+ }
+ }
+}
+
+static void make_clip_tab(struct saa7146 *saa, struct video_clip *cr, int ncr)
+{
+ int i, width, height;
+ u32 *clipmap;
+
+ clipmap = saa->dmavid2;
+ if ((width = saa->win.width) > 1023)
+ width = 1023; /* sanity check */
+ if ((height = saa->win.height) > 640)
+ height = 639; /* sanity check */
+ if (ncr > 0) { /* rectangles pased */
+ /* convert rectangular clips to a bitmap */
+ memset(clipmap, 0, VIDEO_CLIPMAP_SIZE); /* clear map */
+ for (i = 0; i < ncr; i++)
+ clip_draw_rectangle(clipmap, cr[i].x, cr[i].y,
+ cr[i].width, cr[i].height);
+ }
+ /* clip against viewing window AND screen
+ so we do not have to rely on the user program
+ */
+ clip_draw_rectangle(clipmap, (saa->win.x + width > saa->win.swidth) ?
+ (saa->win.swidth - saa->win.x) : width, 0, 1024, 768);
+ clip_draw_rectangle(clipmap, 0,
+ (saa->win.y + height > saa->win.sheight) ?
+ (saa->win.sheight - saa->win.y) : height, 1024, 768);
+ if (saa->win.x < 0)
+ clip_draw_rectangle(clipmap, 0, 0, -saa->win.x, 768);
+ if (saa->win.y < 0)
+ clip_draw_rectangle(clipmap, 0, 0, 1024, -saa->win.y);
+}
+
+static int saa_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long argl)
+{
+ struct saa7146 *saa = file->private_data;
+ void __user *arg = (void __user *)argl;
+
+ switch (cmd) {
+ case VIDIOCGCAP:
+ {
+ struct video_capability b;
+ strcpy(b.name, saa->video_dev.name);
+ b.type = VID_TYPE_CAPTURE | VID_TYPE_OVERLAY |
+ VID_TYPE_CLIPPING | VID_TYPE_FRAMERAM |
+ VID_TYPE_SCALES;
+ b.channels = 1;
+ b.audios = 1;
+ b.maxwidth = 768;
+ b.maxheight = 576;
+ b.minwidth = 32;
+ b.minheight = 32;
+ if (copy_to_user(arg, &b, sizeof(b)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCGPICT:
+ {
+ struct video_picture p = saa->picture;
+ if (saa->win.depth == 8)
+ p.palette = VIDEO_PALETTE_HI240;
+ if (saa->win.depth == 15)
+ p.palette = VIDEO_PALETTE_RGB555;
+ if (saa->win.depth == 16)
+ p.palette = VIDEO_PALETTE_RGB565;
+ if (saa->win.depth == 24)
+ p.palette = VIDEO_PALETTE_RGB24;
+ if (saa->win.depth == 32)
+ p.palette = VIDEO_PALETTE_RGB32;
+ if (copy_to_user(arg, &p, sizeof(p)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSPICT:
+ {
+ struct video_picture p;
+ u32 format;
+ if (copy_from_user(&p, arg, sizeof(p)))
+ return -EFAULT;
+ if (p.palette < sizeof(palette2fmt) / sizeof(u32)) {
+ format = palette2fmt[p.palette];
+ saa->win.color_fmt = format;
+ saawrite(format | 0x60,
+ SAA7146_CLIP_FORMAT_CTRL);
+ }
+ saawrite(((p.brightness & 0xff00) << 16) |
+ ((p.contrast & 0xfe00) << 7) |
+ ((p.colour & 0xfe00) >> 9), SAA7146_BCS_CTRL);
+ saa->picture = p;
+ /* upload changed registers */
+ saawrite(((SAA7146_MC2_UPLD_HPS_H |
+ SAA7146_MC2_UPLD_HPS_V) << 16) |
+ SAA7146_MC2_UPLD_HPS_H |
+ SAA7146_MC2_UPLD_HPS_V, SAA7146_MC2);
+ return 0;
+ }
+ case VIDIOCSWIN:
+ {
+ struct video_window vw;
+ struct video_clip *vcp = NULL;
+
+ if (copy_from_user(&vw, arg, sizeof(vw)))
+ return -EFAULT;
+
+ /* stop capture */
+ if (vw.flags || vw.width < 16 || vw.height < 16) {
+ saawrite((SAA7146_MC1_TR_E_1 << 16),
+ SAA7146_MC1);
+ return -EINVAL;
+ }
+ /* 32-bit align start and adjust width */
+ if (saa->win.bpp < 4) {
+ int i = vw.x;
+ vw.x = (vw.x + 3) & ~3;
+ i = vw.x - i;
+ vw.width -= i;
+ }
+ saa->win.x = vw.x;
+ saa->win.y = vw.y;
+ saa->win.width = vw.width;
+ if (saa->win.width > 768)
+ saa->win.width = 768;
+ saa->win.height = vw.height;
+ if (CurrentMode == VIDEO_MODE_NTSC) {
+ if (saa->win.height > 480)
+ saa->win.height = 480;
+ } else {
+ if (saa->win.height > 576)
+ saa->win.height = 576;
+ }
+
+ /* stop capture */
+ saawrite((SAA7146_MC1_TR_E_1 << 16), SAA7146_MC1);
+ saa7146_set_winsize(saa);
+
+ /*
+ * Do any clips.
+ */
+ if (vw.clipcount < 0) {
+ if (copy_from_user(saa->dmavid2, vw.clips,
+ VIDEO_CLIPMAP_SIZE))
+ return -EFAULT;
+ } else if (vw.clipcount > 16384) {
+ return -EINVAL;
+ } else if (vw.clipcount > 0) {
+ vcp = vmalloc(sizeof(struct video_clip) *
+ vw.clipcount);
+ if (vcp == NULL)
+ return -ENOMEM;
+ if (copy_from_user(vcp, vw.clips,
+ sizeof(struct video_clip) *
+ vw.clipcount)) {
+ vfree(vcp);
+ return -EFAULT;
+ }
+ } else /* nothing clipped */
+ memset(saa->dmavid2, 0, VIDEO_CLIPMAP_SIZE);
+
+ make_clip_tab(saa, vcp, vw.clipcount);
+ if (vw.clipcount > 0)
+ vfree(vcp);
+
+ /* start capture & clip dma if we have an address */
+ if ((saa->cap & 3) && saa->win.vidadr != 0)
+ saawrite(((SAA7146_MC1_TR_E_1 |
+ SAA7146_MC1_TR_E_2) << 16) | 0xffff,
+ SAA7146_MC1);
+ return 0;
+ }
+ case VIDIOCGWIN:
+ {
+ struct video_window vw;
+ vw.x = saa->win.x;
+ vw.y = saa->win.y;
+ vw.width = saa->win.width;
+ vw.height = saa->win.height;
+ vw.chromakey = 0;
+ vw.flags = 0;
+ if (copy_to_user(arg, &vw, sizeof(vw)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCCAPTURE:
+ {
+ int v;
+ if (copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if (v == 0) {
+ saa->cap &= ~1;
+ saawrite((SAA7146_MC1_TR_E_1 << 16),
+ SAA7146_MC1);
+ } else {
+ if (saa->win.vidadr == 0 || saa->win.width == 0
+ || saa->win.height == 0)
+ return -EINVAL;
+ saa->cap |= 1;
+ saawrite((SAA7146_MC1_TR_E_1 << 16) | 0xffff,
+ SAA7146_MC1);
+ }
+ return 0;
+ }
+ case VIDIOCGFBUF:
+ {
+ struct video_buffer v;
+ v.base = (void *)saa->win.vidadr;
+ v.height = saa->win.sheight;
+ v.width = saa->win.swidth;
+ v.depth = saa->win.depth;
+ v.bytesperline = saa->win.bpl;
+ if (copy_to_user(arg, &v, sizeof(v)))
+ return -EFAULT;
+ return 0;
+
+ }
+ case VIDIOCSFBUF:
+ {
+ struct video_buffer v;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if (v.depth != 8 && v.depth != 15 && v.depth != 16 &&
+ v.depth != 24 && v.depth != 32 && v.width > 16 &&
+ v.height > 16 && v.bytesperline > 16)
+ return -EINVAL;
+ if (v.base)
+ saa->win.vidadr = (unsigned long)v.base;
+ saa->win.sheight = v.height;
+ saa->win.swidth = v.width;
+ saa->win.bpp = ((v.depth + 7) & 0x38) / 8;
+ saa->win.depth = v.depth;
+ saa->win.bpl = v.bytesperline;
+
+ DEBUG(printk("Display at %p is %d by %d, bytedepth %d, "
+ "bpl %d\n", v.base, v.width, v.height,
+ saa->win.bpp, saa->win.bpl));
+ saa7146_set_winsize(saa);
+ return 0;
+ }
+ case VIDIOCKEY:
+ {
+ /* Will be handled higher up .. */
+ return 0;
+ }
+
+ case VIDIOCGAUDIO:
+ {
+ struct video_audio v;
+ v = saa->audio_dev;
+ v.flags &= ~(VIDEO_AUDIO_MUTE | VIDEO_AUDIO_MUTABLE);
+ v.flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME;
+ strcpy(v.name, "MPEG");
+ v.mode = VIDEO_SOUND_STEREO;
+ if (copy_to_user(arg, &v, sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSAUDIO:
+ {
+ struct video_audio v;
+ int i;
+ if (copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ i = (~(v.volume >> 8)) & 0xff;
+ if (!HaveCS4341) {
+ if (v.flags & VIDEO_AUDIO_MUTE)
+ debiwrite(saa, debNormal,
+ IBM_MP2_FRNT_ATTEN, 0xffff, 2);
+ if (!(v.flags & VIDEO_AUDIO_MUTE))
+ debiwrite(saa, debNormal,
+ IBM_MP2_FRNT_ATTEN, 0x0000, 2);
+ if (v.flags & VIDEO_AUDIO_VOLUME)
+ debiwrite(saa, debNormal,
+ IBM_MP2_FRNT_ATTEN,
+ (i << 8) | i, 2);
+ } else {
+ if (v.flags & VIDEO_AUDIO_MUTE)
+ cs4341_setlevel(saa, 0xff, 0xff);
+ if (!(v.flags & VIDEO_AUDIO_MUTE))
+ cs4341_setlevel(saa, 0, 0);
+ if (v.flags & VIDEO_AUDIO_VOLUME)
+ cs4341_setlevel(saa, i, i);
+ }
+ saa->audio_dev = v;
+ return 0;
+ }
+
+ case VIDIOCGUNIT:
+ {
+ struct video_unit vu;
+ vu.video = saa->video_dev.minor;
+ vu.vbi = VIDEO_NO_UNIT;
+ vu.radio = VIDEO_NO_UNIT;
+ vu.audio = VIDEO_NO_UNIT;
+ vu.teletext = VIDEO_NO_UNIT;
+ if (copy_to_user(arg, &vu, sizeof(vu)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSPLAYMODE:
+ {
+ struct video_play_mode pmode;
+ if (copy_from_user((void *)&pmode, arg,
+ sizeof(struct video_play_mode)))
+ return -EFAULT;
+ switch (pmode.mode) {
+ case VID_PLAY_VID_OUT_MODE:
+ if (pmode.p1 != VIDEO_MODE_NTSC &&
+ pmode.p1 != VIDEO_MODE_PAL)
+ return -EINVAL;
+ set_out_format(saa, pmode.p1);
+ return 0;
+ case VID_PLAY_GENLOCK:
+ debiwrite(saa, debNormal, XILINX_CTL0,
+ pmode.p1 ? 0x8000 : 0x8080, 2);
+ if (NewCard)
+ set_genlock_offset(saa, pmode.p2);
+ return 0;
+ case VID_PLAY_NORMAL:
+ debiwrite(saa, debNormal,
+ IBM_MP2_CHIP_CONTROL, ChipControl, 2);
+ ibm_send_command(saa, IBM_MP2_PLAY, 0, 0);
+ saa->playmode = pmode.mode;
+ return 0;
+ case VID_PLAY_PAUSE:
+ /* IBM removed the PAUSE command */
+ /* they say use SINGLE_FRAME now */
+ case VID_PLAY_SINGLE_FRAME:
+ ibm_send_command(saa, IBM_MP2_SINGLE_FRAME,0,0);
+ if (saa->playmode == pmode.mode) {
+ debiwrite(saa, debNormal,
+ IBM_MP2_CHIP_CONTROL,
+ ChipControl, 2);
+ }
+ saa->playmode = pmode.mode;
+ return 0;
+ case VID_PLAY_FAST_FORWARD:
+ ibm_send_command(saa, IBM_MP2_FAST_FORWARD,0,0);
+ saa->playmode = pmode.mode;
+ return 0;
+ case VID_PLAY_SLOW_MOTION:
+ ibm_send_command(saa, IBM_MP2_SLOW_MOTION,
+ pmode.p1, 0);
+ saa->playmode = pmode.mode;
+ return 0;
+ case VID_PLAY_IMMEDIATE_NORMAL:
+ /* ensure transfers resume */
+ debiwrite(saa, debNormal,
+ IBM_MP2_CHIP_CONTROL, ChipControl, 2);
+ ibm_send_command(saa, IBM_MP2_IMED_NORM_PLAY,
+ 0, 0);
+ saa->playmode = VID_PLAY_NORMAL;
+ return 0;
+ case VID_PLAY_SWITCH_CHANNELS:
+ saa->audhead = saa->audtail = 0;
+ saa->vidhead = saa->vidtail = 0;
+ ibm_send_command(saa, IBM_MP2_FREEZE_FRAME,0,1);
+ ibm_send_command(saa, IBM_MP2_RESET_AUD_RATE,
+ 0, 1);
+ debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL,
+ 0, 2);
+ ibm_send_command(saa, IBM_MP2_CHANNEL_SWITCH,
+ 0, 1);
+ debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL,
+ ChipControl, 2);
+ ibm_send_command(saa, IBM_MP2_PLAY, 0, 0);
+ saa->playmode = VID_PLAY_NORMAL;
+ return 0;
+ case VID_PLAY_FREEZE_FRAME:
+ ibm_send_command(saa, IBM_MP2_FREEZE_FRAME,0,0);
+ saa->playmode = pmode.mode;
+ return 0;
+ case VID_PLAY_STILL_MODE:
+ ibm_send_command(saa, IBM_MP2_SET_STILL_MODE,
+ 0, 0);
+ saa->playmode = pmode.mode;
+ return 0;
+ case VID_PLAY_MASTER_MODE:
+ if (pmode.p1 == VID_PLAY_MASTER_NONE)
+ saa->boardcfg[1] = 0x13;
+ else if (pmode.p1 == VID_PLAY_MASTER_VIDEO)
+ saa->boardcfg[1] = 0x23;
+ else if (pmode.p1 == VID_PLAY_MASTER_AUDIO)
+ saa->boardcfg[1] = 0x43;
+ else
+ return -EINVAL;
+ debiwrite(saa, debNormal,
+ IBM_MP2_CHIP_CONTROL, ChipControl, 2);
+ return 0;
+ case VID_PLAY_ACTIVE_SCANLINES:
+ if (CurrentMode == VIDEO_MODE_PAL) {
+ if (pmode.p1 < 1 || pmode.p2 > 625)
+ return -EINVAL;
+ saa->boardcfg[5] = pmode.p1;
+ saa->boardcfg[55] = (pmode.p1 +
+ (pmode.p2 / 2) - 1) & 0xff;
+ } else {
+ if (pmode.p1 < 4 || pmode.p2 > 525)
+ return -EINVAL;
+ saa->boardcfg[4] = pmode.p1;
+ saa->boardcfg[54] = (pmode.p1 +
+ (pmode.p2 / 2) - 4) & 0xff;
+ }
+ set_out_format(saa, CurrentMode);
+ case VID_PLAY_RESET:
+ return do_ibm_reset(saa);
+ case VID_PLAY_END_MARK:
+ if (saa->endmarktail < saa->endmarkhead) {
+ if (saa->endmarkhead -
+ saa->endmarktail < 2)
+ return -ENOSPC;
+ } else if (saa->endmarkhead <=saa->endmarktail){
+ if (saa->endmarktail - saa->endmarkhead
+ > (MAX_MARKS - 2))
+ return -ENOSPC;
+ } else
+ return -ENOSPC;
+ saa->endmark[saa->endmarktail] = saa->audtail;
+ saa->endmarktail++;
+ if (saa->endmarktail >= MAX_MARKS)
+ saa->endmarktail = 0;
+ }
+ return -EINVAL;
+ }
+ case VIDIOCSWRITEMODE:
+ {
+ int mode;
+ if (copy_from_user((void *)&mode, arg, sizeof(int)))
+ return -EFAULT;
+ if (mode == VID_WRITE_MPEG_AUD ||
+ mode == VID_WRITE_MPEG_VID ||
+ mode == VID_WRITE_CC ||
+ mode == VID_WRITE_TTX ||
+ mode == VID_WRITE_OSD) {
+ saa->writemode = mode;
+ return 0;
+ }
+ return -EINVAL;
+ }
+ case VIDIOCSMICROCODE:
+ {
+ struct video_code ucode;
+ __u8 *udata;
+ int i;
+ if (copy_from_user(&ucode, arg, sizeof(ucode)))
+ return -EFAULT;
+ if (ucode.datasize > 65536 || ucode.datasize < 1024 ||
+ strncmp(ucode.loadwhat, "dec", 3))
+ return -EINVAL;
+ if ((udata = vmalloc(ucode.datasize)) == NULL)
+ return -ENOMEM;
+ if (copy_from_user(udata, ucode.data, ucode.datasize)) {
+ vfree(udata);
+ return -EFAULT;
+ }
+ ucode.data = udata;
+ if (!strncmp(ucode.loadwhat, "decoder.aud", 11) ||
+ !strncmp(ucode.loadwhat, "decoder.vid", 11))
+ i = initialize_ibmmpeg2(&ucode);
+ else
+ i = initialize_fpga(&ucode);
+ vfree(udata);
+ if (i)
+ return -EINVAL;
+ return 0;
+
+ }
+ case VIDIOCGCHAN: /* this makes xawtv happy */
+ {
+ struct video_channel v;
+ if (copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ v.flags = VIDEO_VC_AUDIO;
+ v.tuners = 0;
+ v.type = VID_TYPE_MPEG_DECODER;
+ v.norm = CurrentMode;
+ strcpy(v.name, "MPEG2");
+ if (copy_to_user(arg, &v, sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSCHAN: /* this makes xawtv happy */
+ {
+ struct video_channel v;
+ if (copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ /* do nothing */
+ return 0;
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static int saa_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct saa7146 *saa = file->private_data;
+ printk(KERN_DEBUG "stradis%d: saa_mmap called\n", saa->nr);
+ return -EINVAL;
+}
+
+static ssize_t saa_read(struct file *file, char __user * buf,
+ size_t count, loff_t * ppos)
+{
+ return -EINVAL;
+}
+
+static ssize_t saa_write(struct file *file, const char __user * buf,
+ size_t count, loff_t * ppos)
+{
+ struct saa7146 *saa = file->private_data;
+ unsigned long todo = count;
+ int blocksize, split;
+ unsigned long flags;
+
+ while (todo > 0) {
+ if (saa->writemode == VID_WRITE_MPEG_AUD) {
+ spin_lock_irqsave(&saa->lock, flags);
+ if (saa->audhead <= saa->audtail)
+ blocksize = 65536 -
+ (saa->audtail - saa->audhead);
+ else
+ blocksize = saa->audhead - saa->audtail;
+ spin_unlock_irqrestore(&saa->lock, flags);
+ if (blocksize < 16384) {
+ saawrite(SAA7146_PSR_DEBI_S |
+ SAA7146_PSR_PIN1, SAA7146_IER);
+ saawrite(SAA7146_PSR_PIN1, SAA7146_PSR);
+ /* wait for buffer space to open */
+ interruptible_sleep_on(&saa->audq);
+ }
+ spin_lock_irqsave(&saa->lock, flags);
+ if (saa->audhead <= saa->audtail) {
+ blocksize = 65536 -
+ (saa->audtail - saa->audhead);
+ split = 65536 - saa->audtail;
+ } else {
+ blocksize = saa->audhead - saa->audtail;
+ split = 65536;
+ }
+ spin_unlock_irqrestore(&saa->lock, flags);
+ blocksize--;
+ if (blocksize > todo)
+ blocksize = todo;
+ /* double check that we really have space */
+ if (!blocksize)
+ return -ENOSPC;
+ if (split < blocksize) {
+ if (copy_from_user(saa->audbuf +
+ saa->audtail, buf, split))
+ return -EFAULT;
+ buf += split;
+ todo -= split;
+ blocksize -= split;
+ saa->audtail = 0;
+ }
+ if (copy_from_user(saa->audbuf + saa->audtail, buf,
+ blocksize))
+ return -EFAULT;
+ saa->audtail += blocksize;
+ todo -= blocksize;
+ buf += blocksize;
+ saa->audtail &= 0xffff;
+ } else if (saa->writemode == VID_WRITE_MPEG_VID) {
+ spin_lock_irqsave(&saa->lock, flags);
+ if (saa->vidhead <= saa->vidtail)
+ blocksize = 524288 -
+ (saa->vidtail - saa->vidhead);
+ else
+ blocksize = saa->vidhead - saa->vidtail;
+ spin_unlock_irqrestore(&saa->lock, flags);
+ if (blocksize < 65536) {
+ saawrite(SAA7146_PSR_DEBI_S |
+ SAA7146_PSR_PIN1, SAA7146_IER);
+ saawrite(SAA7146_PSR_PIN1, SAA7146_PSR);
+ /* wait for buffer space to open */
+ interruptible_sleep_on(&saa->vidq);
+ }
+ spin_lock_irqsave(&saa->lock, flags);
+ if (saa->vidhead <= saa->vidtail) {
+ blocksize = 524288 -
+ (saa->vidtail - saa->vidhead);
+ split = 524288 - saa->vidtail;
+ } else {
+ blocksize = saa->vidhead - saa->vidtail;
+ split = 524288;
+ }
+ spin_unlock_irqrestore(&saa->lock, flags);
+ blocksize--;
+ if (blocksize > todo)
+ blocksize = todo;
+ /* double check that we really have space */
+ if (!blocksize)
+ return -ENOSPC;
+ if (split < blocksize) {
+ if (copy_from_user(saa->vidbuf +
+ saa->vidtail, buf, split))
+ return -EFAULT;
+ buf += split;
+ todo -= split;
+ blocksize -= split;
+ saa->vidtail = 0;
+ }
+ if (copy_from_user(saa->vidbuf + saa->vidtail, buf,
+ blocksize))
+ return -EFAULT;
+ saa->vidtail += blocksize;
+ todo -= blocksize;
+ buf += blocksize;
+ saa->vidtail &= 0x7ffff;
+ } else if (saa->writemode == VID_WRITE_OSD) {
+ if (count > 131072)
+ return -ENOSPC;
+ if (copy_from_user(saa->osdbuf, buf, count))
+ return -EFAULT;
+ buf += count;
+ saa->osdhead = 0;
+ saa->osdtail = count;
+ debiwrite(saa, debNormal, IBM_MP2_OSD_ADDR, 0, 2);
+ debiwrite(saa, debNormal, IBM_MP2_OSD_LINK_ADDR, 0, 2);
+ debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00d, 2);
+ debiwrite(saa, debNormal, IBM_MP2_DISP_MODE,
+ debiread(saa, debNormal,
+ IBM_MP2_DISP_MODE, 2) | 1, 2);
+ /* trigger osd data transfer */
+ saawrite(SAA7146_PSR_DEBI_S |
+ SAA7146_PSR_PIN1, SAA7146_IER);
+ saawrite(SAA7146_PSR_PIN1, SAA7146_PSR);
+ }
+ }
+ return count;
+}
+
+static int saa_open(struct inode *inode, struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146 *saa = container_of(vdev, struct saa7146, video_dev);
+
+ file->private_data = saa;
+
+ saa->user++;
+ if (saa->user > 1)
+ return 0; /* device open already, don't reset */
+ saa->writemode = VID_WRITE_MPEG_VID; /* default to video */
+ return 0;
+}
+
+static int saa_release(struct inode *inode, struct file *file)
+{
+ struct saa7146 *saa = file->private_data;
+ saa->user--;
+
+ if (saa->user > 0) /* still someone using device */
+ return 0;
+ saawrite(0x007f0000, SAA7146_MC1); /* stop all overlay dma */
+ return 0;
+}
+
+static struct file_operations saa_fops = {
+ .owner = THIS_MODULE,
+ .open = saa_open,
+ .release = saa_release,
+ .ioctl = saa_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .read = saa_read,
+ .llseek = no_llseek,
+ .write = saa_write,
+ .mmap = saa_mmap,
+};
+
+/* template for video_device-structure */
+static struct video_device saa_template = {
+ .name = "SAA7146A",
+ .type = VID_TYPE_CAPTURE | VID_TYPE_OVERLAY,
+ .hardware = VID_HARDWARE_SAA7146,
+ .fops = &saa_fops,
+ .minor = -1,
+};
+
+static int __devinit configure_saa7146(struct pci_dev *pdev, int num)
+{
+ int retval;
+ struct saa7146 *saa = pci_get_drvdata(pdev);
+
+ saa->endmarkhead = saa->endmarktail = 0;
+ saa->win.x = saa->win.y = 0;
+ saa->win.width = saa->win.cropwidth = 720;
+ saa->win.height = saa->win.cropheight = 480;
+ saa->win.cropx = saa->win.cropy = 0;
+ saa->win.bpp = 2;
+ saa->win.depth = 16;
+ saa->win.color_fmt = palette2fmt[VIDEO_PALETTE_RGB565];
+ saa->win.bpl = 1024 * saa->win.bpp;
+ saa->win.swidth = 1024;
+ saa->win.sheight = 768;
+ saa->picture.brightness = 32768;
+ saa->picture.contrast = 38768;
+ saa->picture.colour = 32768;
+ saa->cap = 0;
+ saa->nr = num;
+ saa->playmode = VID_PLAY_NORMAL;
+ memset(saa->boardcfg, 0, 64); /* clear board config area */
+ saa->saa7146_mem = NULL;
+ saa->dmavid1 = saa->dmavid2 = saa->dmavid3 = saa->dmaa1in =
+ saa->dmaa1out = saa->dmaa2in = saa->dmaa2out =
+ saa->pagevid1 = saa->pagevid2 = saa->pagevid3 = saa->pagea1in =
+ saa->pagea1out = saa->pagea2in = saa->pagea2out =
+ saa->pagedebi = saa->dmaRPS1 = saa->dmaRPS2 = saa->pageRPS1 =
+ saa->pageRPS2 = NULL;
+ saa->audbuf = saa->vidbuf = saa->osdbuf = saa->dmadebi = NULL;
+ saa->audhead = saa->vidtail = 0;
+
+ init_waitqueue_head(&saa->i2cq);
+ init_waitqueue_head(&saa->audq);
+ init_waitqueue_head(&saa->debiq);
+ init_waitqueue_head(&saa->vidq);
+ spin_lock_init(&saa->lock);
+
+ retval = pci_enable_device(pdev);
+ if (retval) {
+ dev_err(&pdev->dev, "%d: pci_enable_device failed!\n", num);
+ goto err;
+ }
+
+ saa->id = pdev->device;
+ saa->irq = pdev->irq;
+ saa->video_dev.minor = -1;
+ saa->saa7146_adr = pci_resource_start(pdev, 0);
+ pci_read_config_byte(pdev, PCI_CLASS_REVISION, &saa->revision);
+
+ saa->saa7146_mem = ioremap(saa->saa7146_adr, 0x200);
+ if (saa->saa7146_mem == NULL) {
+ dev_err(&pdev->dev, "%d: ioremap failed!\n", num);
+ retval = -EIO;
+ goto err;
+ }
+
+ memcpy(&saa->video_dev, &saa_template, sizeof(saa_template));
+ saawrite(0, SAA7146_IER); /* turn off all interrupts */
+
+ retval = request_irq(saa->irq, saa7146_irq, SA_SHIRQ | SA_INTERRUPT,
+ "stradis", saa);
+ if (retval == -EINVAL)
+ dev_err(&pdev->dev, "%d: Bad irq number or handler\n", num);
+ else if (retval == -EBUSY)
+ dev_err(&pdev->dev, "%d: IRQ %ld busy, change your PnP config "
+ "in BIOS\n", num, saa->irq);
+ if (retval < 0)
+ goto errio;
+
+ pci_set_master(pdev);
+ retval = video_register_device(&saa->video_dev, VFL_TYPE_GRABBER,
+ video_nr);
+ if (retval < 0) {
+ dev_err(&pdev->dev, "%d: error in registering video device!\n",
+ num);
+ goto errio;
+ }
+
+ return 0;
+errio:
+ iounmap(saa->saa7146_mem);
+err:
+ return retval;
+}
+
+static int __devinit init_saa7146(struct pci_dev *pdev)
+{
+ struct saa7146 *saa = pci_get_drvdata(pdev);
+
+ saa->user = 0;
+ /* reset the saa7146 */
+ saawrite(0xffff0000, SAA7146_MC1);
+ mdelay(5);
+ /* enable debi and i2c transfers and pins */
+ saawrite(((SAA7146_MC1_EDP | SAA7146_MC1_EI2C |
+ SAA7146_MC1_TR_E_DEBI) << 16) | 0xffff, SAA7146_MC1);
+ /* ensure proper state of chip */
+ saawrite(0x00000000, SAA7146_PAGE1);
+ saawrite(0x00f302c0, SAA7146_NUM_LINE_BYTE1);
+ saawrite(0x00000000, SAA7146_PAGE2);
+ saawrite(0x01400080, SAA7146_NUM_LINE_BYTE2);
+ saawrite(0x00000000, SAA7146_DD1_INIT);
+ saawrite(0x00000000, SAA7146_DD1_STREAM_B);
+ saawrite(0x00000000, SAA7146_DD1_STREAM_A);
+ saawrite(0x00000000, SAA7146_BRS_CTRL);
+ saawrite(0x80400040, SAA7146_BCS_CTRL);
+ saawrite(0x0000e000 /*| (1<<29) */ , SAA7146_HPS_CTRL);
+ saawrite(0x00000060, SAA7146_CLIP_FORMAT_CTRL);
+ saawrite(0x00000000, SAA7146_ACON1);
+ saawrite(0x00000000, SAA7146_ACON2);
+ saawrite(0x00000600, SAA7146_I2C_STATUS);
+ saawrite(((SAA7146_MC2_UPLD_D1_B | SAA7146_MC2_UPLD_D1_A |
+ SAA7146_MC2_UPLD_BRS | SAA7146_MC2_UPLD_HPS_H |
+ SAA7146_MC2_UPLD_HPS_V | SAA7146_MC2_UPLD_DMA2 |
+ SAA7146_MC2_UPLD_DMA1 | SAA7146_MC2_UPLD_I2C) << 16) | 0xffff,
+ SAA7146_MC2);
+ /* setup arbitration control registers */
+ saawrite(0x1412121a, SAA7146_PCI_BT_V1);
+
+ /* allocate 32k dma buffer + 4k for page table */
+ if ((saa->dmadebi = kmalloc(32768 + 4096, GFP_KERNEL)) == NULL) {
+ dev_err(&pdev->dev, "%d: debi kmalloc failed\n", saa->nr);
+ goto err;
+ }
+#if 0
+ saa->pagedebi = saa->dmadebi + 32768; /* top 4k is for mmu */
+ saawrite(virt_to_bus(saa->pagedebi) /*|0x800 */ , SAA7146_DEBI_PAGE);
+ for (i = 0; i < 12; i++) /* setup mmu page table */
+ saa->pagedebi[i] = virt_to_bus((saa->dmadebi + i * 4096));
+#endif
+ saa->audhead = saa->vidhead = saa->osdhead = 0;
+ saa->audtail = saa->vidtail = saa->osdtail = 0;
+ if (saa->vidbuf == NULL && (saa->vidbuf = vmalloc(524288)) == NULL) {
+ dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr);
+ goto err;
+ }
+ if (saa->audbuf == NULL && (saa->audbuf = vmalloc(65536)) == NULL) {
+ dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr);
+ goto errfree;
+ }
+ if (saa->osdbuf == NULL && (saa->osdbuf = vmalloc(131072)) == NULL) {
+ dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr);
+ goto errfree;
+ }
+ /* allocate 81920 byte buffer for clipping */
+ if ((saa->dmavid2 = kzalloc(VIDEO_CLIPMAP_SIZE, GFP_KERNEL)) == NULL) {
+ dev_err(&pdev->dev, "%d: clip kmalloc failed\n", saa->nr);
+ goto errfree;
+ }
+ /* setup clipping registers */
+ saawrite(virt_to_bus(saa->dmavid2), SAA7146_BASE_EVEN2);
+ saawrite(virt_to_bus(saa->dmavid2) + 128, SAA7146_BASE_ODD2);
+ saawrite(virt_to_bus(saa->dmavid2) + VIDEO_CLIPMAP_SIZE,
+ SAA7146_PROT_ADDR2);
+ saawrite(256, SAA7146_PITCH2);
+ saawrite(4, SAA7146_PAGE2); /* dma direction: read, no byteswap */
+ saawrite(((SAA7146_MC2_UPLD_DMA2) << 16) | SAA7146_MC2_UPLD_DMA2,
+ SAA7146_MC2);
+ I2CBusScan(saa);
+
+ return 0;
+errfree:
+ vfree(saa->osdbuf);
+ vfree(saa->audbuf);
+ vfree(saa->vidbuf);
+ saa->audbuf = saa->osdbuf = saa->vidbuf = NULL;
+err:
+ return -ENOMEM;
+}
+
+static void stradis_release_saa(struct pci_dev *pdev)
+{
+ u8 command;
+ struct saa7146 *saa = pci_get_drvdata(pdev);
+
+ /* turn off all capturing, DMA and IRQs */
+ saawrite(0xffff0000, SAA7146_MC1); /* reset chip */
+ saawrite(0, SAA7146_MC2);
+ saawrite(0, SAA7146_IER);
+ saawrite(0xffffffffUL, SAA7146_ISR);
+
+ /* disable PCI bus-mastering */
+ pci_read_config_byte(pdev, PCI_COMMAND, &command);
+ command &= ~PCI_COMMAND_MASTER;
+ pci_write_config_byte(pdev, PCI_COMMAND, command);
+
+ /* unmap and free memory */
+ saa->audhead = saa->audtail = saa->osdhead = 0;
+ saa->vidhead = saa->vidtail = saa->osdtail = 0;
+ vfree(saa->vidbuf);
+ vfree(saa->audbuf);
+ vfree(saa->osdbuf);
+ kfree(saa->dmavid2);
+ saa->audbuf = saa->vidbuf = saa->osdbuf = NULL;
+ saa->dmavid2 = NULL;
+ kfree(saa->dmadebi);
+ kfree(saa->dmavid1);
+ kfree(saa->dmavid3);
+ kfree(saa->dmaa1in);
+ kfree(saa->dmaa1out);
+ kfree(saa->dmaa2in);
+ kfree(saa->dmaa2out);
+ kfree(saa->dmaRPS1);
+ kfree(saa->dmaRPS2);
+ free_irq(saa->irq, saa);
+ if (saa->saa7146_mem)
+ iounmap(saa->saa7146_mem);
+ if (saa->video_dev.minor != -1)
+ video_unregister_device(&saa->video_dev);
+}
+
+static int __devinit stradis_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int retval = -EINVAL;
+
+ if (saa_num >= SAA7146_MAX)
+ goto err;
+
+ if (!pdev->subsystem_vendor)
+ dev_info(&pdev->dev, "%d: rev1 decoder\n", saa_num);
+ else
+ dev_info(&pdev->dev, "%d: SDM2xx found\n", saa_num);
+
+ pci_set_drvdata(pdev, &saa7146s[saa_num]);
+
+ retval = configure_saa7146(pdev, saa_num);
+ if (retval) {
+ dev_err(&pdev->dev, "%d: error in configuring\n", saa_num);
+ goto err;
+ }
+
+ if (init_saa7146(pdev) < 0) {
+ dev_err(&pdev->dev, "%d: error in initialization\n", saa_num);
+ retval = -EIO;
+ goto errrel;
+ }
+
+ saa_num++;
+
+ return 0;
+errrel:
+ stradis_release_saa(pdev);
+err:
+ return retval;
+}
+
+static void __devexit stradis_remove(struct pci_dev *pdev)
+{
+ stradis_release_saa(pdev);
+}
+
+static struct pci_device_id stradis_pci_tbl[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146) },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, stradis_pci_tbl);
+
+static struct pci_driver stradis_driver = {
+ .name = "stradis",
+ .id_table = stradis_pci_tbl,
+ .probe = stradis_probe,
+ .remove = __devexit_p(stradis_remove)
+};
+
+int __init stradis_init(void)
+{
+ int retval;
+
+ saa_num = 0;
+
+ retval = pci_register_driver(&stradis_driver);
+ if (retval)
+ printk(KERN_ERR "stradis: Unable to register pci driver.\n");
+
+ return retval;
+}
+
+void __exit stradis_exit(void)
+{
+ pci_unregister_driver(&stradis_driver);
+ printk(KERN_INFO "stradis: module cleanup complete\n");
+}
+
+module_init(stradis_init);
+module_exit(stradis_exit);
diff --git a/linux/drivers/media/video/stv680.c b/linux/drivers/media/video/stv680.c
new file mode 100644
index 000000000..b33ed6f73
--- /dev/null
+++ b/linux/drivers/media/video/stv680.c
@@ -0,0 +1,1509 @@
+/*
+ * STV0680 USB Camera Driver, by Kevin Sisson (kjsisson@bellsouth.net)
+ *
+ * Thanks to STMicroelectronics for information on the usb commands, and
+ * to Steve Miller at STM for his help and encouragement while I was
+ * writing this driver.
+ *
+ * This driver is based heavily on the
+ * Endpoints (formerly known as AOX) se401 USB Camera Driver
+ * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org)
+ *
+ * Still somewhat based on the Linux ov511 driver.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * History:
+ * ver 0.1 October, 2001. Initial attempt.
+ *
+ * ver 0.2 November, 2001. Fixed asbility to resize, added brightness
+ * function, made more stable (?)
+ *
+ * ver 0.21 Nov, 2001. Added gamma correction and white balance,
+ * due to Alexander Schwartz. Still trying to
+ * improve stablility. Moved stuff into stv680.h
+ *
+ * ver 0.22 Nov, 2001. Added sharpen function (by Michael Sweet,
+ * mike@easysw.com) from GIMP, also used in pencam.
+ * Simple, fast, good integer math routine.
+ *
+ * ver 0.23 Dec, 2001 (gkh)
+ * Took out sharpen function, ran code through
+ * Lindent, and did other minor tweaks to get
+ * things to work properly with 2.5.1
+ *
+ * ver 0.24 Jan, 2002 (kjs)
+ * Fixed the problem with webcam crashing after
+ * two pictures. Changed the way pic is halved to
+ * improve quality. Got rid of green line around
+ * frame. Fix brightness reset when changing size
+ * bug. Adjusted gamma filters slightly.
+ *
+ * ver 0.25 Jan, 2002 (kjs)
+ * Fixed a bug in which the driver sometimes attempted
+ * to set to a non-supported size. This allowed
+ * gnomemeeting to work.
+ * Fixed proc entry removal bug.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/errno.h>
+#include "compat.h"
+#include <linux/videodev.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+
+#include "stv680.h"
+
+static int video_nr = -1;
+static int swapRGB = 0; /* default for auto sleect */
+static int swapRGB_on = 0; /* default to allow auto select; -1=swap never, +1= swap always */
+
+static unsigned int debug = 0;
+
+#define PDEBUG(level, fmt, args...) \
+ do { \
+ if (debug >= level) \
+ info("[%s:%d] " fmt, __FUNCTION__, __LINE__ , ## args); \
+ } while (0)
+
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.25"
+#define DRIVER_AUTHOR "Kevin Sisson <kjsisson@bellsouth.net>"
+#define DRIVER_DESC "STV0680 USB Camera Driver"
+
+MODULE_AUTHOR (DRIVER_AUTHOR);
+MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_LICENSE ("GPL");
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC (debug, "Debug enabled or not");
+module_param(swapRGB_on, int, 0);
+MODULE_PARM_DESC (swapRGB_on, "Red/blue swap: 1=always, 0=auto, -1=never");
+module_param(video_nr, int, 0);
+
+/********************************************************************
+ *
+ * Memory management
+ *
+ * This is a shameless copy from the USB-cpia driver (linux kernel
+ * version 2.3.29 or so, I have no idea what this code actually does ;).
+ * Actually it seems to be a copy of a shameless copy of the bttv-driver.
+ * Or that is a copy of a shameless copy of ... (To the powers: is there
+ * no generic kernel-function to do this sort of stuff?)
+ *
+ * Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says
+ * there will be one, but apparentely not yet -jerdfelt
+ *
+ * So I copied it again for the ov511 driver -claudio
+ *
+ * Same for the se401 driver -Jeroen
+ *
+ * And the STV0680 driver - Kevin
+ ********************************************************************/
+static void *rvmalloc (unsigned long size)
+{
+ void *mem;
+ unsigned long adr;
+
+ size = PAGE_ALIGN(size);
+ mem = vmalloc_32 (size);
+ if (!mem)
+ return NULL;
+
+ memset (mem, 0, size); /* Clear the ram out, no junk to the user */
+ adr = (unsigned long) mem;
+ while (size > 0) {
+ SetPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ return mem;
+}
+
+static void rvfree (void *mem, unsigned long size)
+{
+ unsigned long adr;
+
+ if (!mem)
+ return;
+
+ adr = (unsigned long) mem;
+ while ((long) size > 0) {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ vfree (mem);
+}
+
+
+/*********************************************************************
+ * pencam read/write functions
+ ********************************************************************/
+
+static int stv_sndctrl (int set, struct usb_stv *stv680, unsigned short req, unsigned short value, unsigned char *buffer, int size)
+{
+ int ret = -1;
+
+ switch (set) {
+ case 0: /* 0xc1 */
+ ret = usb_control_msg (stv680->udev,
+ usb_rcvctrlpipe (stv680->udev, 0),
+ req,
+ (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT),
+ value, 0, buffer, size, PENCAM_TIMEOUT);
+ break;
+
+ case 1: /* 0x41 */
+ ret = usb_control_msg (stv680->udev,
+ usb_sndctrlpipe (stv680->udev, 0),
+ req,
+ (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT),
+ value, 0, buffer, size, PENCAM_TIMEOUT);
+ break;
+
+ case 2: /* 0x80 */
+ ret = usb_control_msg (stv680->udev,
+ usb_rcvctrlpipe (stv680->udev, 0),
+ req,
+ (USB_DIR_IN | USB_RECIP_DEVICE),
+ value, 0, buffer, size, PENCAM_TIMEOUT);
+ break;
+
+ case 3: /* 0x40 */
+ ret = usb_control_msg (stv680->udev,
+ usb_sndctrlpipe (stv680->udev, 0),
+ req,
+ (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE),
+ value, 0, buffer, size, PENCAM_TIMEOUT);
+ break;
+
+ }
+ if ((ret < 0) && (req != 0x0a)) {
+ PDEBUG (1, "STV(e): usb_control_msg error %i, request = 0x%x, error = %i", set, req, ret);
+ }
+ return ret;
+}
+
+static int stv_set_config (struct usb_stv *dev, int configuration, int interface, int alternate)
+{
+
+ if (configuration != dev->udev->actconfig->desc.bConfigurationValue
+ || usb_reset_configuration (dev->udev) < 0) {
+ PDEBUG (1, "STV(e): FAILED to reset configuration %i", configuration);
+ return -1;
+ }
+ if (usb_set_interface (dev->udev, interface, alternate) < 0) {
+ PDEBUG (1, "STV(e): FAILED to set alternate interface %i", alternate);
+ return -1;
+ }
+ return 0;
+}
+
+static int stv_stop_video (struct usb_stv *dev)
+{
+ int i;
+ unsigned char *buf;
+
+ buf = kmalloc (40, GFP_KERNEL);
+ if (buf == NULL) {
+ PDEBUG (0, "STV(e): Out of (small buf) memory");
+ return -1;
+ }
+
+ /* this is a high priority command; it stops all lower order commands */
+ if ((i = stv_sndctrl (1, dev, 0x04, 0x0000, buf, 0x0)) < 0) {
+ i = stv_sndctrl (0, dev, 0x80, 0, buf, 0x02); /* Get Last Error; 2 = busy */
+ PDEBUG (1, "STV(i): last error: %i, command = 0x%x", buf[0], buf[1]);
+ } else {
+ PDEBUG (1, "STV(i): Camera reset to idle mode.");
+ }
+
+ if ((i = stv_set_config (dev, 1, 0, 0)) < 0)
+ PDEBUG (1, "STV(e): Reset config during exit failed");
+
+ /* get current mode */
+ buf[0] = 0xf0;
+ if ((i = stv_sndctrl (0, dev, 0x87, 0, buf, 0x08)) != 0x08) /* get mode */
+ PDEBUG (0, "STV(e): Stop_video: problem setting original mode");
+ if (dev->origMode != buf[0]) {
+ memset (buf, 0, 8);
+ buf[0] = (unsigned char) dev->origMode;
+ if ((i = stv_sndctrl (3, dev, 0x07, 0x0100, buf, 0x08)) != 0x08) {
+ PDEBUG (0, "STV(e): Stop_video: Set_Camera_Mode failed");
+ i = -1;
+ }
+ buf[0] = 0xf0;
+ i = stv_sndctrl (0, dev, 0x87, 0, buf, 0x08);
+ if ((i != 0x08) || (buf[0] != dev->origMode)) {
+ PDEBUG (0, "STV(e): camera NOT set to original resolution.");
+ i = -1;
+ } else
+ PDEBUG (0, "STV(i): Camera set to original resolution");
+ }
+ /* origMode */
+ kfree(buf);
+ return i;
+}
+
+static int stv_set_video_mode (struct usb_stv *dev)
+{
+ int i, stop_video = 1;
+ unsigned char *buf;
+
+ buf = kmalloc (40, GFP_KERNEL);
+ if (buf == NULL) {
+ PDEBUG (0, "STV(e): Out of (small buf) memory");
+ return -1;
+ }
+
+ if ((i = stv_set_config (dev, 1, 0, 0)) < 0) {
+ kfree(buf);
+ return i;
+ }
+
+ i = stv_sndctrl (2, dev, 0x06, 0x0100, buf, 0x12);
+ if (!(i > 0) && (buf[8] == 0x53) && (buf[9] == 0x05)) {
+ PDEBUG (1, "STV(e): Could not get descriptor 0100.");
+ goto error;
+ }
+
+ /* set alternate interface 1 */
+ if ((i = stv_set_config (dev, 1, 0, 1)) < 0)
+ goto error;
+
+ if ((i = stv_sndctrl (0, dev, 0x85, 0, buf, 0x10)) != 0x10)
+ goto error;
+ PDEBUG (1, "STV(i): Setting video mode.");
+ /* Switch to Video mode: 0x0100 = VGA (640x480), 0x0000 = CIF (352x288) 0x0300 = QVGA (320x240) */
+ if ((i = stv_sndctrl (1, dev, 0x09, dev->VideoMode, buf, 0x0)) < 0) {
+ stop_video = 0;
+ goto error;
+ }
+ goto exit;
+
+error:
+ kfree(buf);
+ if (stop_video == 1)
+ stv_stop_video (dev);
+ return -1;
+
+exit:
+ kfree(buf);
+ return 0;
+}
+
+static int stv_init (struct usb_stv *stv680)
+{
+ int i = 0;
+ unsigned char *buffer;
+ unsigned long int bufsize;
+
+ buffer = kzalloc (40, GFP_KERNEL);
+ if (buffer == NULL) {
+ PDEBUG (0, "STV(e): Out of (small buf) memory");
+ return -1;
+ }
+ udelay (100);
+
+ /* set config 1, interface 0, alternate 0 */
+ if ((i = stv_set_config (stv680, 1, 0, 0)) < 0) {
+ kfree(buffer);
+ PDEBUG (0, "STV(e): set config 1,0,0 failed");
+ return -1;
+ }
+ /* ping camera to be sure STV0680 is present */
+ if ((i = stv_sndctrl (0, stv680, 0x88, 0x5678, buffer, 0x02)) != 0x02)
+ goto error;
+ if ((buffer[0] != 0x56) || (buffer[1] != 0x78)) {
+ PDEBUG (1, "STV(e): camera ping failed!!");
+ goto error;
+ }
+
+ /* get camera descriptor */
+ if ((i = stv_sndctrl (2, stv680, 0x06, 0x0200, buffer, 0x09)) != 0x09)
+ goto error;
+ i = stv_sndctrl (2, stv680, 0x06, 0x0200, buffer, 0x22);
+ if (!(i >= 0) && (buffer[7] == 0xa0) && (buffer[8] == 0x23)) {
+ PDEBUG (1, "STV(e): Could not get descriptor 0200.");
+ goto error;
+ }
+ if ((i = stv_sndctrl (0, stv680, 0x8a, 0, buffer, 0x02)) != 0x02)
+ goto error;
+ if ((i = stv_sndctrl (0, stv680, 0x8b, 0, buffer, 0x24)) != 0x24)
+ goto error;
+ if ((i = stv_sndctrl (0, stv680, 0x85, 0, buffer, 0x10)) != 0x10)
+ goto error;
+
+ stv680->SupportedModes = buffer[7];
+ i = stv680->SupportedModes;
+ stv680->CIF = 0;
+ stv680->VGA = 0;
+ stv680->QVGA = 0;
+ if (i & 1)
+ stv680->CIF = 1;
+ if (i & 2)
+ stv680->VGA = 1;
+ if (i & 8)
+ stv680->QVGA = 1;
+ if (stv680->SupportedModes == 0) {
+ PDEBUG (0, "STV(e): There are NO supported STV680 modes!!");
+ i = -1;
+ goto error;
+ } else {
+ if (stv680->CIF)
+ PDEBUG (0, "STV(i): CIF is supported");
+ if (stv680->QVGA)
+ PDEBUG (0, "STV(i): QVGA is supported");
+ }
+ /* FW rev, ASIC rev, sensor ID */
+ PDEBUG (1, "STV(i): Firmware rev is %i.%i", buffer[0], buffer[1]);
+ PDEBUG (1, "STV(i): ASIC rev is %i.%i", buffer[2], buffer[3]);
+ PDEBUG (1, "STV(i): Sensor ID is %i", (buffer[4]*16) + (buffer[5]>>4));
+
+ /* set alternate interface 1 */
+ if ((i = stv_set_config (stv680, 1, 0, 1)) < 0)
+ goto error;
+
+ if ((i = stv_sndctrl (0, stv680, 0x85, 0, buffer, 0x10)) != 0x10)
+ goto error;
+ if ((i = stv_sndctrl (0, stv680, 0x8d, 0, buffer, 0x08)) != 0x08)
+ goto error;
+ i = buffer[3];
+ PDEBUG (0, "STV(i): Camera has %i pictures.", i);
+
+ /* get current mode */
+ if ((i = stv_sndctrl (0, stv680, 0x87, 0, buffer, 0x08)) != 0x08)
+ goto error;
+ stv680->origMode = buffer[0]; /* 01 = VGA, 03 = QVGA, 00 = CIF */
+
+ /* This will attemp CIF mode, if supported. If not, set to QVGA */
+ memset (buffer, 0, 8);
+ if (stv680->CIF)
+ buffer[0] = 0x00;
+ else if (stv680->QVGA)
+ buffer[0] = 0x03;
+ if ((i = stv_sndctrl (3, stv680, 0x07, 0x0100, buffer, 0x08)) != 0x08) {
+ PDEBUG (0, "STV(i): Set_Camera_Mode failed");
+ i = -1;
+ goto error;
+ }
+ buffer[0] = 0xf0;
+ stv_sndctrl (0, stv680, 0x87, 0, buffer, 0x08);
+ if (((stv680->CIF == 1) && (buffer[0] != 0x00)) || ((stv680->QVGA == 1) && (buffer[0] != 0x03))) {
+ PDEBUG (0, "STV(e): Error setting camera video mode!");
+ i = -1;
+ goto error;
+ } else {
+ if (buffer[0] == 0) {
+ stv680->VideoMode = 0x0000;
+ PDEBUG (0, "STV(i): Video Mode set to CIF");
+ }
+ if (buffer[0] == 0x03) {
+ stv680->VideoMode = 0x0300;
+ PDEBUG (0, "STV(i): Video Mode set to QVGA");
+ }
+ }
+ if ((i = stv_sndctrl (0, stv680, 0x8f, 0, buffer, 0x10)) != 0x10)
+ goto error;
+ bufsize = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | (buffer[3]);
+ stv680->cwidth = (buffer[4] << 8) | (buffer[5]); /* ->camera = 322, 356, 644 */
+ stv680->cheight = (buffer[6] << 8) | (buffer[7]); /* ->camera = 242, 292, 484 */
+ stv680->origGain = buffer[12];
+
+ goto exit;
+
+error:
+ i = stv_sndctrl (0, stv680, 0x80, 0, buffer, 0x02); /* Get Last Error */
+ PDEBUG (1, "STV(i): last error: %i, command = 0x%x", buffer[0], buffer[1]);
+ kfree(buffer);
+ return -1;
+
+exit:
+ kfree(buffer);
+
+ /* video = 320x240, 352x288 */
+ if (stv680->CIF == 1) {
+ stv680->maxwidth = 352;
+ stv680->maxheight = 288;
+ stv680->vwidth = 352;
+ stv680->vheight = 288;
+ }
+ if (stv680->QVGA == 1) {
+ stv680->maxwidth = 320;
+ stv680->maxheight = 240;
+ stv680->vwidth = 320;
+ stv680->vheight = 240;
+ }
+
+ stv680->rawbufsize = bufsize; /* must be ./. by 8 */
+ stv680->maxframesize = bufsize * 3; /* RGB size */
+ PDEBUG (2, "STV(i): cwidth = %i, cheight = %i", stv680->cwidth, stv680->cheight);
+ PDEBUG (1, "STV(i): width = %i, height = %i, rawbufsize = %li", stv680->vwidth, stv680->vheight, stv680->rawbufsize);
+
+ /* some default values */
+ stv680->bulk_in_endpointAddr = 0x82;
+ stv680->dropped = 0;
+ stv680->error = 0;
+ stv680->framecount = 0;
+ stv680->readcount = 0;
+ stv680->streaming = 0;
+ /* bright, white, colour, hue, contrast are set by software, not in stv0680 */
+ stv680->brightness = 32767;
+ stv680->chgbright = 0;
+ stv680->whiteness = 0; /* only for greyscale */
+ stv680->colour = 32767;
+ stv680->contrast = 32767;
+ stv680->hue = 32767;
+ stv680->palette = STV_VIDEO_PALETTE;
+ stv680->depth = 24; /* rgb24 bits */
+ if ((swapRGB_on == 0) && (swapRGB == 0))
+ PDEBUG (1, "STV(i): swapRGB is (auto) OFF");
+ else if ((swapRGB_on == 0) && (swapRGB == 1))
+ PDEBUG (1, "STV(i): swapRGB is (auto) ON");
+ else if (swapRGB_on == 1)
+ PDEBUG (1, "STV(i): swapRGB is (forced) ON");
+ else if (swapRGB_on == -1)
+ PDEBUG (1, "STV(i): swapRGB is (forced) OFF");
+
+ if (stv_set_video_mode (stv680) < 0) {
+ PDEBUG (0, "STV(e): Could not set video mode in stv_init");
+ return -1;
+ }
+
+ return 0;
+}
+
+/***************** last of pencam routines *******************/
+
+/****************************************************************************
+ * sysfs
+ ***************************************************************************/
+#define stv680_file(name, variable, field) \
+static ssize_t show_##name(struct class_device *class_dev, char *buf) \
+{ \
+ struct video_device *vdev = to_video_device(class_dev); \
+ struct usb_stv *stv = video_get_drvdata(vdev); \
+ return sprintf(buf, field, stv->variable); \
+} \
+static CLASS_DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
+
+stv680_file(model, camera_name, "%s\n");
+stv680_file(in_use, user, "%d\n");
+stv680_file(streaming, streaming, "%d\n");
+stv680_file(palette, palette, "%i\n");
+stv680_file(frames_total, readcount, "%d\n");
+stv680_file(frames_read, framecount, "%d\n");
+stv680_file(packets_dropped, dropped, "%d\n");
+stv680_file(decoding_errors, error, "%d\n");
+
+static void stv680_create_sysfs_files(struct video_device *vdev)
+{
+ video_device_create_file(vdev, &class_device_attr_model);
+ video_device_create_file(vdev, &class_device_attr_in_use);
+ video_device_create_file(vdev, &class_device_attr_streaming);
+ video_device_create_file(vdev, &class_device_attr_palette);
+ video_device_create_file(vdev, &class_device_attr_frames_total);
+ video_device_create_file(vdev, &class_device_attr_frames_read);
+ video_device_create_file(vdev, &class_device_attr_packets_dropped);
+ video_device_create_file(vdev, &class_device_attr_decoding_errors);
+}
+
+static void stv680_remove_sysfs_files(struct video_device *vdev)
+{
+ video_device_remove_file(vdev, &class_device_attr_model);
+ video_device_remove_file(vdev, &class_device_attr_in_use);
+ video_device_remove_file(vdev, &class_device_attr_streaming);
+ video_device_remove_file(vdev, &class_device_attr_palette);
+ video_device_remove_file(vdev, &class_device_attr_frames_total);
+ video_device_remove_file(vdev, &class_device_attr_frames_read);
+ video_device_remove_file(vdev, &class_device_attr_packets_dropped);
+ video_device_remove_file(vdev, &class_device_attr_decoding_errors);
+}
+
+/********************************************************************
+ * Camera control
+ *******************************************************************/
+
+static int stv680_get_pict (struct usb_stv *stv680, struct video_picture *p)
+{
+ /* This sets values for v4l interface. max/min = 65535/0 */
+
+ p->brightness = stv680->brightness;
+ p->whiteness = stv680->whiteness; /* greyscale */
+ p->colour = stv680->colour;
+ p->contrast = stv680->contrast;
+ p->hue = stv680->hue;
+ p->palette = stv680->palette;
+ p->depth = stv680->depth;
+ return 0;
+}
+
+static int stv680_set_pict (struct usb_stv *stv680, struct video_picture *p)
+{
+ /* See above stv680_get_pict */
+
+ if (p->palette != STV_VIDEO_PALETTE) {
+ PDEBUG (2, "STV(e): Palette set error in _set_pic");
+ return 1;
+ }
+
+ if (stv680->brightness != p->brightness) {
+ stv680->chgbright = 1;
+ stv680->brightness = p->brightness;
+ }
+
+ stv680->whiteness = p->whiteness; /* greyscale */
+ stv680->colour = p->colour;
+ stv680->contrast = p->contrast;
+ stv680->hue = p->hue;
+ stv680->palette = p->palette;
+ stv680->depth = p->depth;
+
+ return 0;
+}
+
+static void stv680_video_irq (struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_stv *stv680 = urb->context;
+ int length = urb->actual_length;
+
+ if (length < stv680->rawbufsize)
+ PDEBUG (2, "STV(i): Lost data in transfer: exp %li, got %i", stv680->rawbufsize, length);
+
+ /* ohoh... */
+ if (!stv680->streaming)
+ return;
+
+ if (!stv680->udev) {
+ PDEBUG (0, "STV(e): device vapourished in video_irq");
+ return;
+ }
+
+ /* 0 sized packets happen if we are to fast, but sometimes the camera
+ keeps sending them forever...
+ */
+ if (length && !urb->status) {
+ stv680->nullpackets = 0;
+ switch (stv680->scratch[stv680->scratch_next].state) {
+ case BUFFER_READY:
+ case BUFFER_BUSY:
+ stv680->dropped++;
+ break;
+
+ case BUFFER_UNUSED:
+ memcpy (stv680->scratch[stv680->scratch_next].data,
+ (unsigned char *) urb->transfer_buffer, length);
+ stv680->scratch[stv680->scratch_next].state = BUFFER_READY;
+ stv680->scratch[stv680->scratch_next].length = length;
+ if (waitqueue_active (&stv680->wq)) {
+ wake_up_interruptible (&stv680->wq);
+ }
+ stv680->scratch_overflow = 0;
+ stv680->scratch_next++;
+ if (stv680->scratch_next >= STV680_NUMSCRATCH)
+ stv680->scratch_next = 0;
+ break;
+ } /* switch */
+ } else {
+ stv680->nullpackets++;
+ if (stv680->nullpackets > STV680_MAX_NULLPACKETS) {
+ if (waitqueue_active (&stv680->wq)) {
+ wake_up_interruptible (&stv680->wq);
+ }
+ }
+ } /* if - else */
+
+ /* Resubmit urb for new data */
+ urb->status = 0;
+ urb->dev = stv680->udev;
+ if (usb_submit_urb (urb, GFP_ATOMIC))
+ PDEBUG (0, "STV(e): urb burned down in video irq");
+ return;
+} /* _video_irq */
+
+static int stv680_start_stream (struct usb_stv *stv680)
+{
+ struct urb *urb;
+ int err = 0, i;
+
+ stv680->streaming = 1;
+
+ /* Do some memory allocation */
+ for (i = 0; i < STV680_NUMFRAMES; i++) {
+ stv680->frame[i].data = stv680->fbuf + i * stv680->maxframesize;
+ stv680->frame[i].curpix = 0;
+ }
+ /* packet size = 4096 */
+ for (i = 0; i < STV680_NUMSBUF; i++) {
+ stv680->sbuf[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL);
+ if (stv680->sbuf[i].data == NULL) {
+ PDEBUG (0, "STV(e): Could not kmalloc raw data buffer %i", i);
+ return -1;
+ }
+ }
+
+ stv680->scratch_next = 0;
+ stv680->scratch_use = 0;
+ stv680->scratch_overflow = 0;
+ for (i = 0; i < STV680_NUMSCRATCH; i++) {
+ stv680->scratch[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL);
+ if (stv680->scratch[i].data == NULL) {
+ PDEBUG (0, "STV(e): Could not kmalloc raw scratch buffer %i", i);
+ return -1;
+ }
+ stv680->scratch[i].state = BUFFER_UNUSED;
+ }
+
+ for (i = 0; i < STV680_NUMSBUF; i++) {
+ urb = usb_alloc_urb (0, GFP_KERNEL);
+ if (!urb)
+ return -ENOMEM;
+
+ /* sbuf is urb->transfer_buffer, later gets memcpyed to scratch */
+ usb_fill_bulk_urb (urb, stv680->udev,
+ usb_rcvbulkpipe (stv680->udev, stv680->bulk_in_endpointAddr),
+ stv680->sbuf[i].data, stv680->rawbufsize,
+ stv680_video_irq, stv680);
+ stv680->urb[i] = urb;
+ err = usb_submit_urb (stv680->urb[i], GFP_KERNEL);
+ if (err)
+ PDEBUG (0, "STV(e): urb burned down in start stream");
+ } /* i STV680_NUMSBUF */
+
+ stv680->framecount = 0;
+ return 0;
+}
+
+static int stv680_stop_stream (struct usb_stv *stv680)
+{
+ int i;
+
+ if (!stv680->streaming || !stv680->udev)
+ return 1;
+
+ stv680->streaming = 0;
+
+ for (i = 0; i < STV680_NUMSBUF; i++)
+ if (stv680->urb[i]) {
+ usb_kill_urb (stv680->urb[i]);
+ usb_free_urb (stv680->urb[i]);
+ stv680->urb[i] = NULL;
+ kfree(stv680->sbuf[i].data);
+ }
+ for (i = 0; i < STV680_NUMSCRATCH; i++) {
+ kfree(stv680->scratch[i].data);
+ stv680->scratch[i].data = NULL;
+ }
+
+ return 0;
+}
+
+static int stv680_set_size (struct usb_stv *stv680, int width, int height)
+{
+ int wasstreaming = stv680->streaming;
+
+ /* Check to see if we need to change */
+ if ((stv680->vwidth == width) && (stv680->vheight == height))
+ return 0;
+
+ PDEBUG (1, "STV(i): size request for %i x %i", width, height);
+ /* Check for a valid mode */
+ if ((!width || !height) || ((width & 1) || (height & 1))) {
+ PDEBUG (1, "STV(e): set_size error: request: v.width = %i, v.height = %i actual: stv.width = %i, stv.height = %i", width, height, stv680->vwidth, stv680->vheight);
+ return 1;
+ }
+
+ if ((width < (stv680->maxwidth / 2)) || (height < (stv680->maxheight / 2))) {
+ width = stv680->maxwidth / 2;
+ height = stv680->maxheight / 2;
+ } else if ((width >= 158) && (width <= 166) && (stv680->QVGA == 1)) {
+ width = 160;
+ height = 120;
+ } else if ((width >= 172) && (width <= 180) && (stv680->CIF == 1)) {
+ width = 176;
+ height = 144;
+ } else if ((width >= 318) && (width <= 350) && (stv680->QVGA == 1)) {
+ width = 320;
+ height = 240;
+ } else if ((width >= 350) && (width <= 358) && (stv680->CIF == 1)) {
+ width = 352;
+ height = 288;
+ } else {
+ PDEBUG (1, "STV(e): request for non-supported size: request: v.width = %i, v.height = %i actual: stv.width = %i, stv.height = %i", width, height, stv680->vwidth, stv680->vheight);
+ return 1;
+ }
+
+ /* Stop a current stream and start it again at the new size */
+ if (wasstreaming)
+ stv680_stop_stream (stv680);
+ stv680->vwidth = width;
+ stv680->vheight = height;
+ PDEBUG (1, "STV(i): size set to %i x %i", stv680->vwidth, stv680->vheight);
+ if (wasstreaming)
+ stv680_start_stream (stv680);
+
+ return 0;
+}
+
+/**********************************************************************
+ * Video Decoding
+ **********************************************************************/
+
+/******* routines from the pencam program; hey, they work! ********/
+
+/*
+ * STV0680 Vision Camera Chipset Driver
+ * Copyright (C) 2000 Adam Harrison <adam@antispin.org>
+*/
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+#define AD(x, y, w) (((y)*(w)+(x))*3)
+
+static void bayer_unshuffle (struct usb_stv *stv680, struct stv680_scratch *buffer)
+{
+ int x, y, i;
+ int w = stv680->cwidth;
+ int vw = stv680->cwidth, vh = stv680->cheight;
+ unsigned int p = 0;
+ int colour = 0, bayer = 0;
+ unsigned char *raw = buffer->data;
+ struct stv680_frame *frame = &stv680->frame[stv680->curframe];
+ unsigned char *output = frame->data;
+ unsigned char *temp = frame->data;
+ int offset = buffer->offset;
+
+ if (frame->curpix == 0) {
+ if (frame->grabstate == FRAME_READY) {
+ frame->grabstate = FRAME_GRABBING;
+ }
+ }
+ if (offset != frame->curpix) { /* Regard frame as lost :( */
+ frame->curpix = 0;
+ stv680->error++;
+ return;
+ }
+
+ if ((stv680->vwidth == 320) || (stv680->vwidth == 160)) {
+ vw = 320;
+ vh = 240;
+ }
+ if ((stv680->vwidth == 352) || (stv680->vwidth == 176)) {
+ vw = 352;
+ vh = 288;
+ }
+
+ memset (output, 0, 3 * vw * vh); /* clear output matrix. */
+
+ for (y = 0; y < vh; y++) {
+ for (x = 0; x < vw; x++) {
+ if (x & 1)
+ p = *(raw + y * w + (x >> 1));
+ else
+ p = *(raw + y * w + (x >> 1) + (w >> 1));
+
+ if (y & 1)
+ bayer = 2;
+ else
+ bayer = 0;
+ if (x & 1)
+ bayer++;
+
+ switch (bayer) {
+ case 0:
+ case 3:
+ colour = 1;
+ break;
+ case 1:
+ colour = 0;
+ break;
+ case 2:
+ colour = 2;
+ break;
+ }
+ i = (y * vw + x) * 3;
+ *(output + i + colour) = (unsigned char) p;
+ } /* for x */
+
+ } /* for y */
+
+ /****** gamma correction plus hardcoded white balance */
+ /* Thanks to Alexander Schwartx <alexander.schwartx@gmx.net> for this code.
+ Correction values red[], green[], blue[], are generated by
+ (pow(i/256.0, GAMMA)*255.0)*white balanceRGB where GAMMA=0.55, 1<i<255.
+ White balance (RGB)= 1.0, 1.17, 1.48. Values are calculated as double float and
+ converted to unsigned char. Values are in stv680.h */
+
+ for (y = 0; y < vh; y++) {
+ for (x = 0; x < vw; x++) {
+ i = (y * vw + x) * 3;
+ *(output + i) = red[*(output + i)];
+ *(output + i + 1) = green[*(output + i + 1)];
+ *(output + i + 2) = blue[*(output + i + 2)];
+ }
+ }
+
+ /****** bayer demosaic ******/
+ for (y = 1; y < (vh - 1); y++) {
+ for (x = 1; x < (vw - 1); x++) { /* work out pixel type */
+ if (y & 1)
+ bayer = 0;
+ else
+ bayer = 2;
+ if (!(x & 1))
+ bayer++;
+
+ switch (bayer) {
+ case 0: /* green. blue lr, red tb */
+ *(output + AD (x, y, vw) + BLUE) = ((int) *(output + AD (x - 1, y, vw) + BLUE) + (int) *(output + AD (x + 1, y, vw) + BLUE)) >> 1;
+ *(output + AD (x, y, vw) + RED) = ((int) *(output + AD (x, y - 1, vw) + RED) + (int) *(output + AD (x, y + 1, vw) + RED)) >> 1;
+ break;
+
+ case 1: /* blue. green lrtb, red diagonals */
+ *(output + AD (x, y, vw) + GREEN) = ((int) *(output + AD (x - 1, y, vw) + GREEN) + (int) *(output + AD (x + 1, y, vw) + GREEN) + (int) *(output + AD (x, y - 1, vw) + GREEN) + (int) *(output + AD (x, y + 1, vw) + GREEN)) >> 2;
+ *(output + AD (x, y, vw) + RED) = ((int) *(output + AD (x - 1, y - 1, vw) + RED) + (int) *(output + AD (x - 1, y + 1, vw) + RED) + (int) *(output + AD (x + 1, y - 1, vw) + RED) + (int) *(output + AD (x + 1, y + 1, vw) + RED)) >> 2;
+ break;
+
+ case 2: /* red. green lrtb, blue diagonals */
+ *(output + AD (x, y, vw) + GREEN) = ((int) *(output + AD (x - 1, y, vw) + GREEN) + (int) *(output + AD (x + 1, y, vw) + GREEN) + (int) *(output + AD (x, y - 1, vw) + GREEN) + (int) *(output + AD (x, y + 1, vw) + GREEN)) >> 2;
+ *(output + AD (x, y, vw) + BLUE) = ((int) *(output + AD (x - 1, y - 1, vw) + BLUE) + (int) *(output + AD (x + 1, y - 1, vw) + BLUE) + (int) *(output + AD (x - 1, y + 1, vw) + BLUE) + (int) *(output + AD (x + 1, y + 1, vw) + BLUE)) >> 2;
+ break;
+
+ case 3: /* green. red lr, blue tb */
+ *(output + AD (x, y, vw) + RED) = ((int) *(output + AD (x - 1, y, vw) + RED) + (int) *(output + AD (x + 1, y, vw) + RED)) >> 1;
+ *(output + AD (x, y, vw) + BLUE) = ((int) *(output + AD (x, y - 1, vw) + BLUE) + (int) *(output + AD (x, y + 1, vw) + BLUE)) >> 1;
+ break;
+ } /* switch */
+ } /* for x */
+ } /* for y - end demosaic */
+
+ /* fix top and bottom row, left and right side */
+ i = vw * 3;
+ memcpy (output, (output + i), i);
+ memcpy ((output + (vh * i)), (output + ((vh - 1) * i)), i);
+ for (y = 0; y < vh; y++) {
+ i = y * vw * 3;
+ memcpy ((output + i), (output + i + 3), 3);
+ memcpy ((output + i + (vw * 3)), (output + i + (vw - 1) * 3), 3);
+ }
+
+ /* process all raw data, then trim to size if necessary */
+ if ((stv680->vwidth == 160) || (stv680->vwidth == 176)) {
+ i = 0;
+ for (y = 0; y < vh; y++) {
+ if (!(y & 1)) {
+ for (x = 0; x < vw; x++) {
+ p = (y * vw + x) * 3;
+ if (!(x & 1)) {
+ *(output + i) = *(output + p);
+ *(output + i + 1) = *(output + p + 1);
+ *(output + i + 2) = *(output + p + 2);
+ i += 3;
+ }
+ } /* for x */
+ }
+ } /* for y */
+ }
+ /* reset to proper width */
+ if ((stv680->vwidth == 160)) {
+ vw = 160;
+ vh = 120;
+ }
+ if ((stv680->vwidth == 176)) {
+ vw = 176;
+ vh = 144;
+ }
+
+ /* output is RGB; some programs want BGR */
+ /* swapRGB_on=0 -> program decides; swapRGB_on=1, always swap */
+ /* swapRGB_on=-1, never swap */
+ if (((swapRGB == 1) && (swapRGB_on != -1)) || (swapRGB_on == 1)) {
+ for (y = 0; y < vh; y++) {
+ for (x = 0; x < vw; x++) {
+ i = (y * vw + x) * 3;
+ *(temp) = *(output + i);
+ *(output + i) = *(output + i + 2);
+ *(output + i + 2) = *(temp);
+ }
+ }
+ }
+ /* brightness */
+ if (stv680->chgbright == 1) {
+ if (stv680->brightness >= 32767) {
+ p = (stv680->brightness - 32767) / 256;
+ for (x = 0; x < (vw * vh * 3); x++) {
+ if ((*(output + x) + (unsigned char) p) > 255)
+ *(output + x) = 255;
+ else
+ *(output + x) += (unsigned char) p;
+ } /* for */
+ } else {
+ p = (32767 - stv680->brightness) / 256;
+ for (x = 0; x < (vw * vh * 3); x++) {
+ if ((unsigned char) p > *(output + x))
+ *(output + x) = 0;
+ else
+ *(output + x) -= (unsigned char) p;
+ } /* for */
+ } /* else */
+ }
+ /* if */
+ frame->curpix = 0;
+ frame->curlinepix = 0;
+ frame->grabstate = FRAME_DONE;
+ stv680->framecount++;
+ stv680->readcount++;
+ if (stv680->frame[(stv680->curframe + 1) & (STV680_NUMFRAMES - 1)].grabstate == FRAME_READY) {
+ stv680->curframe = (stv680->curframe + 1) & (STV680_NUMFRAMES - 1);
+ }
+
+} /* bayer_unshuffle */
+
+/******* end routines from the pencam program *********/
+
+static int stv680_newframe (struct usb_stv *stv680, int framenr)
+{
+ int errors = 0;
+
+ while (stv680->streaming && (stv680->frame[framenr].grabstate == FRAME_READY || stv680->frame[framenr].grabstate == FRAME_GRABBING)) {
+ if (!stv680->frame[framenr].curpix) {
+ errors++;
+ }
+ wait_event_interruptible (stv680->wq, (stv680->scratch[stv680->scratch_use].state == BUFFER_READY));
+
+ if (stv680->nullpackets > STV680_MAX_NULLPACKETS) {
+ stv680->nullpackets = 0;
+ PDEBUG (2, "STV(i): too many null length packets, restarting capture");
+ stv680_stop_stream (stv680);
+ stv680_start_stream (stv680);
+ } else {
+ if (stv680->scratch[stv680->scratch_use].state != BUFFER_READY) {
+ stv680->frame[framenr].grabstate = FRAME_ERROR;
+ PDEBUG (2, "STV(e): FRAME_ERROR in _newframe");
+ return -EIO;
+ }
+ stv680->scratch[stv680->scratch_use].state = BUFFER_BUSY;
+
+ bayer_unshuffle (stv680, &stv680->scratch[stv680->scratch_use]);
+
+ stv680->scratch[stv680->scratch_use].state = BUFFER_UNUSED;
+ stv680->scratch_use++;
+ if (stv680->scratch_use >= STV680_NUMSCRATCH)
+ stv680->scratch_use = 0;
+ if (errors > STV680_MAX_ERRORS) {
+ errors = 0;
+ PDEBUG (2, "STV(i): too many errors, restarting capture");
+ stv680_stop_stream (stv680);
+ stv680_start_stream (stv680);
+ }
+ } /* else */
+ } /* while */
+ return 0;
+}
+
+/*********************************************************************
+ * Video4Linux
+ *********************************************************************/
+
+static int stv_open (struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct usb_stv *stv680 = video_get_drvdata(dev);
+ int err = 0;
+
+ /* we are called with the BKL held */
+ stv680->user = 1;
+ err = stv_init (stv680); /* main initialization routine for camera */
+
+ if (err >= 0) {
+ stv680->fbuf = rvmalloc (stv680->maxframesize * STV680_NUMFRAMES);
+ if (!stv680->fbuf) {
+ PDEBUG (0, "STV(e): Could not rvmalloc frame bufer");
+ err = -ENOMEM;
+ }
+ file->private_data = dev;
+ }
+ if (err)
+ stv680->user = 0;
+
+ return err;
+}
+
+static int stv_close (struct inode *inode, struct file *file)
+{
+ struct video_device *dev = file->private_data;
+ struct usb_stv *stv680 = video_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < STV680_NUMFRAMES; i++)
+ stv680->frame[i].grabstate = FRAME_UNUSED;
+ if (stv680->streaming)
+ stv680_stop_stream (stv680);
+
+ if ((i = stv_stop_video (stv680)) < 0)
+ PDEBUG (1, "STV(e): stop_video failed in stv_close");
+
+ rvfree (stv680->fbuf, stv680->maxframesize * STV680_NUMFRAMES);
+ stv680->user = 0;
+
+ if (stv680->removed) {
+ kfree(stv680);
+ stv680 = NULL;
+ PDEBUG (0, "STV(i): device unregistered");
+ }
+ file->private_data = NULL;
+ return 0;
+}
+
+static int stv680_do_ioctl (struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = file->private_data;
+ struct usb_stv *stv680 = video_get_drvdata(vdev);
+
+ if (!stv680->udev)
+ return -EIO;
+
+ switch (cmd) {
+ case VIDIOCGCAP:{
+ struct video_capability *b = arg;
+
+ strcpy (b->name, stv680->camera_name);
+ b->type = VID_TYPE_CAPTURE;
+ b->channels = 1;
+ b->audios = 0;
+ b->maxwidth = stv680->maxwidth;
+ b->maxheight = stv680->maxheight;
+ b->minwidth = stv680->maxwidth / 2;
+ b->minheight = stv680->maxheight / 2;
+ return 0;
+ }
+ case VIDIOCGCHAN:{
+ struct video_channel *v = arg;
+
+ if (v->channel != 0)
+ return -EINVAL;
+ v->flags = 0;
+ v->tuners = 0;
+ v->type = VIDEO_TYPE_CAMERA;
+ strcpy (v->name, "STV Camera");
+ return 0;
+ }
+ case VIDIOCSCHAN:{
+ struct video_channel *v = arg;
+ if (v->channel != 0)
+ return -EINVAL;
+ return 0;
+ }
+ case VIDIOCGPICT:{
+ struct video_picture *p = arg;
+
+ stv680_get_pict (stv680, p);
+ return 0;
+ }
+ case VIDIOCSPICT:{
+ struct video_picture *p = arg;
+
+ if (stv680_set_pict (stv680, p))
+ return -EINVAL;
+ return 0;
+ }
+ case VIDIOCSWIN:{
+ struct video_window *vw = arg;
+
+ if (vw->flags)
+ return -EINVAL;
+ if (vw->clipcount)
+ return -EINVAL;
+ if (vw->width != stv680->vwidth) {
+ if (stv680_set_size (stv680, vw->width, vw->height)) {
+ PDEBUG (2, "STV(e): failed (from user) set size in VIDIOCSWIN");
+ return -EINVAL;
+ }
+ }
+ return 0;
+ }
+ case VIDIOCGWIN:{
+ struct video_window *vw = arg;
+
+ vw->x = 0; /* FIXME */
+ vw->y = 0;
+ vw->chromakey = 0;
+ vw->flags = 0;
+ vw->clipcount = 0;
+ vw->width = stv680->vwidth;
+ vw->height = stv680->vheight;
+ return 0;
+ }
+ case VIDIOCGMBUF:{
+ struct video_mbuf *vm = arg;
+ int i;
+
+ memset (vm, 0, sizeof (*vm));
+ vm->size = STV680_NUMFRAMES * stv680->maxframesize;
+ vm->frames = STV680_NUMFRAMES;
+ for (i = 0; i < STV680_NUMFRAMES; i++)
+ vm->offsets[i] = stv680->maxframesize * i;
+ return 0;
+ }
+ case VIDIOCMCAPTURE:{
+ struct video_mmap *vm = arg;
+
+ if (vm->format != STV_VIDEO_PALETTE) {
+ PDEBUG (2, "STV(i): VIDIOCMCAPTURE vm.format (%i) != VIDEO_PALETTE (%i)",
+ vm->format, STV_VIDEO_PALETTE);
+ if ((vm->format == 3) && (swapRGB_on == 0)) {
+ PDEBUG (2, "STV(i): VIDIOCMCAPTURE swapRGB is (auto) ON");
+ /* this may fix those apps (e.g., xawtv) that want BGR */
+ swapRGB = 1;
+ }
+ return -EINVAL;
+ }
+ if (vm->frame >= STV680_NUMFRAMES) {
+ PDEBUG (2, "STV(e): VIDIOCMCAPTURE vm.frame > NUMFRAMES");
+ return -EINVAL;
+ }
+ if ((stv680->frame[vm->frame].grabstate == FRAME_ERROR)
+ || (stv680->frame[vm->frame].grabstate == FRAME_GRABBING)) {
+ PDEBUG (2, "STV(e): VIDIOCMCAPTURE grabstate (%i) error",
+ stv680->frame[vm->frame].grabstate);
+ return -EBUSY;
+ }
+ /* Is this according to the v4l spec??? */
+ if (stv680->vwidth != vm->width) {
+ if (stv680_set_size (stv680, vm->width, vm->height)) {
+ PDEBUG (2, "STV(e): VIDIOCMCAPTURE set_size failed");
+ return -EINVAL;
+ }
+ }
+ stv680->frame[vm->frame].grabstate = FRAME_READY;
+
+ if (!stv680->streaming)
+ stv680_start_stream (stv680);
+
+ return 0;
+ }
+ case VIDIOCSYNC:{
+ int *frame = arg;
+ int ret = 0;
+
+ if (*frame < 0 || *frame >= STV680_NUMFRAMES) {
+ PDEBUG (2, "STV(e): Bad frame # in VIDIOCSYNC");
+ return -EINVAL;
+ }
+ ret = stv680_newframe (stv680, *frame);
+ stv680->frame[*frame].grabstate = FRAME_UNUSED;
+ return ret;
+ }
+ case VIDIOCGFBUF:{
+ struct video_buffer *vb = arg;
+
+ memset (vb, 0, sizeof (*vb));
+ return 0;
+ }
+ case VIDIOCKEY:
+ return 0;
+ case VIDIOCCAPTURE:
+ {
+ PDEBUG (2, "STV(e): VIDIOCCAPTURE failed");
+ return -EINVAL;
+ }
+ case VIDIOCSFBUF:
+ case VIDIOCGTUNER:
+ case VIDIOCSTUNER:
+ case VIDIOCGFREQ:
+ case VIDIOCSFREQ:
+ case VIDIOCGAUDIO:
+ case VIDIOCSAUDIO:
+ return -EINVAL;
+ default:
+ return -ENOIOCTLCMD;
+ } /* end switch */
+
+ return 0;
+}
+
+static int stv680_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, stv680_do_ioctl);
+}
+
+static int stv680_mmap (struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *dev = file->private_data;
+ struct usb_stv *stv680 = video_get_drvdata(dev);
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end-vma->vm_start;
+ unsigned long page, pos;
+
+ mutex_lock(&stv680->lock);
+
+ if (stv680->udev == NULL) {
+ mutex_unlock(&stv680->lock);
+ return -EIO;
+ }
+ if (size > (((STV680_NUMFRAMES * stv680->maxframesize) + PAGE_SIZE - 1)
+ & ~(PAGE_SIZE - 1))) {
+ mutex_unlock(&stv680->lock);
+ return -EINVAL;
+ }
+ pos = (unsigned long) stv680->fbuf;
+ while (size > 0) {
+ page = vmalloc_to_pfn((void *)pos);
+ if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
+ mutex_unlock(&stv680->lock);
+ return -EAGAIN;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+ mutex_unlock(&stv680->lock);
+
+ return 0;
+}
+
+static ssize_t stv680_read (struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct video_device *dev = file->private_data;
+ unsigned long int realcount = count;
+ int ret = 0;
+ struct usb_stv *stv680 = video_get_drvdata(dev);
+ unsigned long int i;
+
+ if (STV680_NUMFRAMES != 2) {
+ PDEBUG (0, "STV(e): STV680_NUMFRAMES needs to be 2!");
+ return -1;
+ }
+ if (stv680->udev == NULL)
+ return -EIO;
+ if (realcount > (stv680->vwidth * stv680->vheight * 3))
+ realcount = stv680->vwidth * stv680->vheight * 3;
+
+ /* Shouldn't happen: */
+ if (stv680->frame[0].grabstate == FRAME_GRABBING) {
+ PDEBUG (2, "STV(e): FRAME_GRABBING in stv680_read");
+ return -EBUSY;
+ }
+ stv680->frame[0].grabstate = FRAME_READY;
+ stv680->frame[1].grabstate = FRAME_UNUSED;
+ stv680->curframe = 0;
+
+ if (!stv680->streaming)
+ stv680_start_stream (stv680);
+
+ if (!stv680->streaming) {
+ ret = stv680_newframe (stv680, 0); /* ret should = 0 */
+ }
+
+ ret = stv680_newframe (stv680, 0);
+
+ if (!ret) {
+ if ((i = copy_to_user (buf, stv680->frame[0].data, realcount)) != 0) {
+ PDEBUG (2, "STV(e): copy_to_user frame 0 failed, ret count = %li", i);
+ return -EFAULT;
+ }
+ } else {
+ realcount = ret;
+ }
+ stv680->frame[0].grabstate = FRAME_UNUSED;
+ return realcount;
+} /* stv680_read */
+
+static struct file_operations stv680_fops = {
+ .owner = THIS_MODULE,
+ .open = stv_open,
+ .release = stv_close,
+ .read = stv680_read,
+ .mmap = stv680_mmap,
+ .ioctl = stv680_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+};
+static struct video_device stv680_template = {
+ .owner = THIS_MODULE,
+ .name = "STV0680 USB camera",
+ .type = VID_TYPE_CAPTURE,
+ .hardware = VID_HARDWARE_SE401,
+ .fops = &stv680_fops,
+ .release = video_device_release,
+ .minor = -1,
+};
+
+static int stv680_probe (struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_host_interface *interface;
+ struct usb_stv *stv680 = NULL;
+ char *camera_name = NULL;
+ int retval = 0;
+
+ /* We don't handle multi-config cameras */
+ if (dev->descriptor.bNumConfigurations != 1) {
+ PDEBUG (0, "STV(e): Number of Configurations != 1");
+ return -ENODEV;
+ }
+
+ interface = &intf->altsetting[0];
+ /* Is it a STV680? */
+ if ((le16_to_cpu(dev->descriptor.idVendor) == USB_PENCAM_VENDOR_ID) &&
+ (le16_to_cpu(dev->descriptor.idProduct) == USB_PENCAM_PRODUCT_ID)) {
+ camera_name = "STV0680";
+ PDEBUG (0, "STV(i): STV0680 camera found.");
+ } else if ((le16_to_cpu(dev->descriptor.idVendor) == USB_CREATIVEGOMINI_VENDOR_ID) &&
+ (le16_to_cpu(dev->descriptor.idProduct) == USB_CREATIVEGOMINI_PRODUCT_ID)) {
+ camera_name = "Creative WebCam Go Mini";
+ PDEBUG (0, "STV(i): Creative WebCam Go Mini found.");
+ } else {
+ PDEBUG (0, "STV(e): Vendor/Product ID do not match STV0680 or Creative WebCam Go Mini values.");
+ PDEBUG (0, "STV(e): Check that the STV0680 or Creative WebCam Go Mini camera is connected to the computer.");
+ retval = -ENODEV;
+ goto error;
+ }
+ /* We found one */
+ if ((stv680 = kzalloc (sizeof (*stv680), GFP_KERNEL)) == NULL) {
+ PDEBUG (0, "STV(e): couldn't kmalloc stv680 struct.");
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ stv680->udev = dev;
+ stv680->camera_name = camera_name;
+
+ stv680->vdev = video_device_alloc();
+ if (!stv680->vdev) {
+ retval = -ENOMEM;
+ goto error;
+ }
+ memcpy(stv680->vdev, &stv680_template, sizeof(stv680_template));
+ stv680->vdev->dev = &intf->dev;
+ video_set_drvdata(stv680->vdev, stv680);
+
+ memcpy (stv680->vdev->name, stv680->camera_name, strlen (stv680->camera_name));
+ init_waitqueue_head (&stv680->wq);
+ mutex_init (&stv680->lock);
+ wmb ();
+
+ if (video_register_device (stv680->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
+ PDEBUG (0, "STV(e): video_register_device failed");
+ retval = -EIO;
+ goto error_vdev;
+ }
+ PDEBUG (0, "STV(i): registered new video device: video%d", stv680->vdev->minor);
+
+ usb_set_intfdata (intf, stv680);
+ stv680_create_sysfs_files(stv680->vdev);
+ return 0;
+
+error_vdev:
+ video_device_release(stv680->vdev);
+error:
+ kfree(stv680);
+ return retval;
+}
+
+static inline void usb_stv680_remove_disconnected (struct usb_stv *stv680)
+{
+ int i;
+
+ stv680->udev = NULL;
+ stv680->frame[0].grabstate = FRAME_ERROR;
+ stv680->frame[1].grabstate = FRAME_ERROR;
+ stv680->streaming = 0;
+
+ wake_up_interruptible (&stv680->wq);
+
+ for (i = 0; i < STV680_NUMSBUF; i++)
+ if (stv680->urb[i]) {
+ usb_kill_urb (stv680->urb[i]);
+ usb_free_urb (stv680->urb[i]);
+ stv680->urb[i] = NULL;
+ kfree(stv680->sbuf[i].data);
+ }
+ for (i = 0; i < STV680_NUMSCRATCH; i++)
+ kfree(stv680->scratch[i].data);
+ PDEBUG (0, "STV(i): %s disconnected", stv680->camera_name);
+
+ /* Free the memory */
+ kfree(stv680);
+}
+
+static void stv680_disconnect (struct usb_interface *intf)
+{
+ struct usb_stv *stv680 = usb_get_intfdata (intf);
+
+ usb_set_intfdata (intf, NULL);
+
+ if (stv680) {
+ /* We don't want people trying to open up the device */
+ if (stv680->vdev) {
+ stv680_remove_sysfs_files(stv680->vdev);
+ video_unregister_device(stv680->vdev);
+ stv680->vdev = NULL;
+ }
+ if (!stv680->user) {
+ usb_stv680_remove_disconnected (stv680);
+ } else {
+ stv680->removed = 1;
+ }
+ }
+}
+
+static struct usb_driver stv680_driver = {
+ .name = "stv680",
+ .probe = stv680_probe,
+ .disconnect = stv680_disconnect,
+ .id_table = device_table
+};
+
+/********************************************************************
+ * Module routines
+ ********************************************************************/
+
+static int __init usb_stv680_init (void)
+{
+ if (usb_register (&stv680_driver) < 0) {
+ PDEBUG (0, "STV(e): Could not setup STV0680 driver");
+ return -1;
+ }
+ PDEBUG (0, "STV(i): usb camera driver version %s registering", DRIVER_VERSION);
+
+ info(DRIVER_DESC " " DRIVER_VERSION);
+ return 0;
+}
+
+static void __exit usb_stv680_exit (void)
+{
+ usb_deregister (&stv680_driver);
+ PDEBUG (0, "STV(i): driver deregistered");
+}
+
+module_init (usb_stv680_init);
+module_exit (usb_stv680_exit);
diff --git a/linux/drivers/media/video/stv680.h b/linux/drivers/media/video/stv680.h
new file mode 100644
index 000000000..a08f1b08a
--- /dev/null
+++ b/linux/drivers/media/video/stv680.h
@@ -0,0 +1,227 @@
+/****************************************************************************
+ *
+ * Filename: stv680.h
+ *
+ * Description:
+ * This is a USB driver for STV0680 based usb video cameras.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************************/
+
+/* size of usb transfers */
+#define STV680_PACKETSIZE 4096
+
+/* number of queued bulk transfers to use, may have problems if > 1 */
+#define STV680_NUMSBUF 1
+
+/* number of frames supported by the v4l part */
+#define STV680_NUMFRAMES 2
+
+/* scratch buffers for passing data to the decoders: 2 or 4 are good */
+#define STV680_NUMSCRATCH 2
+
+/* number of nul sized packets to receive before kicking the camera */
+#define STV680_MAX_NULLPACKETS 200
+
+/* number of decoding errors before kicking the camera */
+#define STV680_MAX_ERRORS 100
+
+#define USB_PENCAM_VENDOR_ID 0x0553
+#define USB_PENCAM_PRODUCT_ID 0x0202
+
+#define USB_CREATIVEGOMINI_VENDOR_ID 0x041e
+#define USB_CREATIVEGOMINI_PRODUCT_ID 0x4007
+
+#define PENCAM_TIMEOUT 1000
+/* fmt 4 */
+#define STV_VIDEO_PALETTE VIDEO_PALETTE_RGB24
+
+static struct usb_device_id device_table[] = {
+ {USB_DEVICE (USB_PENCAM_VENDOR_ID, USB_PENCAM_PRODUCT_ID)},
+ {USB_DEVICE (USB_CREATIVEGOMINI_VENDOR_ID, USB_CREATIVEGOMINI_PRODUCT_ID)},
+ {}
+};
+MODULE_DEVICE_TABLE (usb, device_table);
+
+struct stv680_sbuf {
+ unsigned char *data;
+};
+
+enum {
+ FRAME_UNUSED, /* Unused (no MCAPTURE) */
+ FRAME_READY, /* Ready to start grabbing */
+ FRAME_GRABBING, /* In the process of being grabbed into */
+ FRAME_DONE, /* Finished grabbing, but not been synced yet */
+ FRAME_ERROR, /* Something bad happened while processing */
+};
+
+enum {
+ BUFFER_UNUSED,
+ BUFFER_READY,
+ BUFFER_BUSY,
+ BUFFER_DONE,
+};
+
+/* raw camera data <- sbuf (urb transfer buf) */
+struct stv680_scratch {
+ unsigned char *data;
+ volatile int state;
+ int offset;
+ int length;
+};
+
+/* processed data for display ends up here, after bayer */
+struct stv680_frame {
+ unsigned char *data; /* Frame buffer */
+ volatile int grabstate; /* State of grabbing */
+ unsigned char *curline;
+ int curlinepix;
+ int curpix;
+};
+
+/* this is almost the video structure uvd_t, with extra parameters for stv */
+struct usb_stv {
+ struct video_device *vdev;
+
+ struct usb_device *udev;
+
+ unsigned char bulk_in_endpointAddr; /* __u8 the address of the bulk in endpoint */
+ char *camera_name;
+
+ unsigned int VideoMode; /* 0x0100 = VGA, 0x0000 = CIF, 0x0300 = QVGA */
+ int SupportedModes;
+ int CIF;
+ int VGA;
+ int QVGA;
+ int cwidth; /* camera width */
+ int cheight; /* camera height */
+ int maxwidth; /* max video width */
+ int maxheight; /* max video height */
+ int vwidth; /* current width for video window */
+ int vheight; /* current height for video window */
+ unsigned long int rawbufsize;
+ unsigned long int maxframesize; /* rawbufsize * 3 for RGB */
+
+ int origGain;
+ int origMode; /* original camera mode */
+
+ struct mutex lock; /* to lock the structure */
+ int user; /* user count for exclusive use */
+ int removed; /* device disconnected */
+ int streaming; /* Are we streaming video? */
+ char *fbuf; /* Videodev buffer area */
+ struct urb *urb[STV680_NUMSBUF]; /* # of queued bulk transfers */
+ int curframe; /* Current receiving frame */
+ struct stv680_frame frame[STV680_NUMFRAMES]; /* # frames supported by v4l part */
+ int readcount;
+ int framecount;
+ int error;
+ int dropped;
+ int scratch_next;
+ int scratch_use;
+ int scratch_overflow;
+ struct stv680_scratch scratch[STV680_NUMSCRATCH]; /* for decoders */
+ struct stv680_sbuf sbuf[STV680_NUMSBUF];
+
+ unsigned int brightness;
+ unsigned int chgbright;
+ unsigned int whiteness;
+ unsigned int colour;
+ unsigned int contrast;
+ unsigned int hue;
+ unsigned int palette;
+ unsigned int depth; /* rgb24 in bits */
+
+ wait_queue_head_t wq; /* Processes waiting */
+
+ int nullpackets;
+};
+
+
+static const unsigned char red[256] = {
+ 0, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 25, 30, 35, 38, 42,
+ 44, 47, 50, 53, 54, 57, 59, 61, 63, 65, 67, 69,
+ 71, 71, 73, 75, 77, 78, 80, 81, 82, 84, 85, 87,
+ 88, 89, 90, 91, 93, 94, 95, 97, 98, 98, 99, 101,
+ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
+ 114, 115, 116, 116, 117, 118, 119, 120, 121, 122, 123, 124,
+ 125, 125, 126, 127, 128, 129, 129, 130, 131, 132, 133, 134,
+ 134, 135, 135, 136, 137, 138, 139, 140, 140, 141, 142, 143,
+ 143, 143, 144, 145, 146, 147, 147, 148, 149, 150, 150, 151,
+ 152, 152, 152, 153, 154, 154, 155, 156, 157, 157, 158, 159,
+ 159, 160, 161, 161, 161, 162, 163, 163, 164, 165, 165, 166,
+ 167, 167, 168, 168, 169, 170, 170, 170, 171, 171, 172, 173,
+ 173, 174, 174, 175, 176, 176, 177, 178, 178, 179, 179, 179,
+ 180, 180, 181, 181, 182, 183, 183, 184, 184, 185, 185, 186,
+ 187, 187, 188, 188, 188, 188, 189, 190, 190, 191, 191, 192,
+ 192, 193, 193, 194, 195, 195, 196, 196, 197, 197, 197, 197,
+ 198, 198, 199, 199, 200, 201, 201, 202, 202, 203, 203, 204,
+ 204, 205, 205, 206, 206, 206, 206, 207, 207, 208, 208, 209,
+ 209, 210, 210, 211, 211, 212, 212, 213, 213, 214, 214, 215,
+ 215, 215, 215, 216, 216, 217, 217, 218, 218, 218, 219, 219,
+ 220, 220, 221, 221
+};
+
+static const unsigned char green[256] = {
+ 0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 28, 34, 39, 43, 47,
+ 50, 53, 56, 59, 61, 64, 66, 68, 71, 73, 75, 77,
+ 79, 80, 82, 84, 86, 87, 89, 91, 92, 94, 95, 97,
+ 98, 100, 101, 102, 104, 105, 106, 108, 109, 110, 111, 113,
+ 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
+ 139, 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 149,
+ 150, 151, 151, 152, 153, 154, 155, 156, 156, 157, 158, 159,
+ 160, 160, 161, 162, 163, 164, 164, 165, 166, 167, 167, 168,
+ 169, 170, 170, 171, 172, 172, 173, 174, 175, 175, 176, 177,
+ 177, 178, 179, 179, 180, 181, 182, 182, 183, 184, 184, 185,
+ 186, 186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 193,
+ 193, 194, 194, 195, 196, 196, 197, 198, 198, 199, 199, 200,
+ 201, 201, 202, 202, 203, 204, 204, 205, 205, 206, 206, 207,
+ 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214,
+ 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 220,
+ 221, 221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227,
+ 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233,
+ 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239,
+ 239, 240, 240, 241, 241, 242, 242, 243, 243, 243, 244, 244,
+ 245, 245, 246, 246
+};
+
+static const unsigned char blue[256] = {
+ 0, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 30, 37, 42, 47, 51,
+ 55, 58, 61, 64, 67, 70, 72, 74, 78, 80, 82, 84,
+ 86, 88, 90, 92, 94, 95, 97, 100, 101, 103, 104, 106,
+ 107, 110, 111, 112, 114, 115, 116, 118, 119, 121, 122, 124,
+ 125, 126, 127, 128, 129, 132, 133, 134, 135, 136, 137, 138,
+ 139, 140, 141, 143, 144, 145, 146, 147, 148, 149, 150, 151,
+ 152, 154, 155, 156, 157, 158, 158, 159, 160, 161, 162, 163,
+ 165, 166, 166, 167, 168, 169, 170, 171, 171, 172, 173, 174,
+ 176, 176, 177, 178, 179, 180, 180, 181, 182, 183, 183, 184,
+ 185, 187, 187, 188, 189, 189, 190, 191, 192, 192, 193, 194,
+ 194, 195, 196, 196, 198, 199, 200, 200, 201, 202, 202, 203,
+ 204, 204, 205, 205, 206, 207, 207, 209, 210, 210, 211, 212,
+ 212, 213, 213, 214, 215, 215, 216, 217, 217, 218, 218, 220,
+ 221, 221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227,
+ 228, 228, 229, 229, 231, 231, 232, 233, 233, 234, 234, 235,
+ 235, 236, 236, 237, 238, 238, 239, 239, 240, 240, 242, 242,
+ 243, 243, 244, 244, 245, 246, 246, 247, 247, 248, 248, 249,
+ 249, 250, 250, 251, 251, 253, 253, 254, 254, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255
+};
diff --git a/linux/drivers/media/video/tuner-3036.c b/linux/drivers/media/video/tuner-3036.c
new file mode 100644
index 000000000..545f54eaa
--- /dev/null
+++ b/linux/drivers/media/video/tuner-3036.c
@@ -0,0 +1,214 @@
+/*
+ * Driver for Philips SAB3036 "CITAC" tuner control chip.
+ *
+ * Author: Phil Blundell <philb@gnu.org>
+ *
+ * The SAB3036 is just about different enough from the chips that
+ * tuner.c copes with to make it not worth the effort to crowbar
+ * the support into that file. So instead we have a separate driver.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+#include <linux/i2c.h>
+#include "compat.h"
+#include <linux/videodev.h>
+
+#include <media/tuner.h>
+
+static int debug; /* insmod parameter */
+static int this_adap;
+
+static struct i2c_client client_template;
+
+/* Addresses to scan */
+static unsigned short normal_i2c[] = { 0x60, 0x61, I2C_CLIENT_END };
+static unsigned short ignore = I2C_CLIENT_END;
+
+static struct i2c_client_address_data addr_data = {
+ .normal_i2c = normal_i2c,
+ .probe = &ignore,
+ .ignore = &ignore,
+};
+
+/* ---------------------------------------------------------------------- */
+
+static unsigned char
+tuner_getstatus (struct i2c_client *c)
+{
+ unsigned char byte;
+ if (i2c_master_recv(c, &byte, 1) != 1)
+ printk(KERN_ERR "tuner-3036: I/O error.\n");
+ return byte;
+}
+
+#define TUNER_FL 0x80
+
+static int
+tuner_islocked (struct i2c_client *c)
+{
+ return (tuner_getstatus(c) & TUNER_FL);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void
+set_tv_freq(struct i2c_client *c, int freq)
+{
+ u16 div = ((freq * 20) / 16);
+ unsigned long give_up = jiffies + HZ;
+ unsigned char buffer[2];
+
+ if (debug)
+ printk(KERN_DEBUG "tuner: setting frequency %dMHz, divisor %x\n", freq / 16, div);
+
+ /* Select high tuning current */
+ buffer[0] = 0x29;
+ buffer[1] = 0x3e;
+
+ if (i2c_master_send(c, buffer, 2) != 2)
+ printk("tuner: i2c i/o error 1\n");
+
+ buffer[0] = 0x80 | ((div>>8) & 0x7f);
+ buffer[1] = div & 0xff;
+
+ if (i2c_master_send(c, buffer, 2) != 2)
+ printk("tuner: i2c i/o error 2\n");
+
+ while (!tuner_islocked(c) && time_before(jiffies, give_up))
+ schedule();
+
+ if (!tuner_islocked(c))
+ printk(KERN_WARNING "tuner: failed to achieve PLL lock\n");
+
+ /* Select low tuning current and engage AFC */
+ buffer[0] = 0x29;
+ buffer[1] = 0xb2;
+
+ if (i2c_master_send(c, buffer, 2) != 2)
+ printk("tuner: i2c i/o error 3\n");
+
+ if (debug)
+ printk(KERN_DEBUG "tuner: status %02x\n", tuner_getstatus(c));
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int
+tuner_attach(struct i2c_adapter *adap, int addr, int kind)
+{
+ static unsigned char buffer[] = { 0x29, 0x32, 0x2a, 0, 0x2b, 0 };
+
+ struct i2c_client *client;
+
+ if (this_adap > 0)
+ return -1;
+ this_adap++;
+
+ client_template.adapter = adap;
+ client_template.addr = addr;
+
+ client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (client == NULL)
+ return -ENOMEM;
+ memcpy(client, &client_template, sizeof(struct i2c_client));
+
+ printk("tuner: SAB3036 found, status %02x\n", tuner_getstatus(client));
+
+ i2c_attach_client(client);
+
+ if (i2c_master_send(client, buffer, 2) != 2)
+ printk("tuner: i2c i/o error 1\n");
+ if (i2c_master_send(client, buffer+2, 2) != 2)
+ printk("tuner: i2c i/o error 2\n");
+ if (i2c_master_send(client, buffer+4, 2) != 2)
+ printk("tuner: i2c i/o error 3\n");
+ return 0;
+}
+
+static int
+tuner_detach(struct i2c_client *c)
+{
+ return 0;
+}
+
+static int
+tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+ int *iarg = (int*)arg;
+
+ switch (cmd)
+ {
+ case VIDIOCSFREQ:
+ set_tv_freq(client, *iarg);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int
+tuner_probe(struct i2c_adapter *adap)
+{
+ this_adap = 0;
+ if (adap->id == I2C_HW_B_LP)
+ return i2c_probe(adap, &addr_data, tuner_attach);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_driver
+i2c_driver_tuner =
+{
+ .driver = {
+ .name = "sab3036",
+ },
+ .id = I2C_DRIVERID_SAB3036,
+ .attach_adapter = tuner_probe,
+ .detach_client = tuner_detach,
+ .command = tuner_command
+};
+
+static struct i2c_client client_template =
+{
+ .driver = &i2c_driver_tuner,
+ .name = "SAB3036",
+};
+
+static int __init
+tuner3036_init(void)
+{
+ return i2c_add_driver(&i2c_driver_tuner);
+}
+
+static void __exit
+tuner3036_exit(void)
+{
+ i2c_del_driver(&i2c_driver_tuner);
+}
+
+MODULE_DESCRIPTION("SAB3036 tuner driver");
+MODULE_AUTHOR("Philip Blundell <philb@gnu.org>");
+MODULE_LICENSE("GPL");
+
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug,"Enable debugging output");
+
+module_init(tuner3036_init);
+module_exit(tuner3036_exit);
diff --git a/linux/drivers/media/video/videocodec.c b/linux/drivers/media/video/videocodec.c
new file mode 100644
index 000000000..8f271de57
--- /dev/null
+++ b/linux/drivers/media/video/videocodec.c
@@ -0,0 +1,481 @@
+/*
+ * VIDEO MOTION CODECs internal API for video devices
+ *
+ * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's
+ * bound to a master device.
+ *
+ * (c) 2002 Wolfgang Scherr <scherr@net4you.at>
+ *
+ * $Id: videocodec.c,v 1.1.2.8 2003/03/29 07:16:04 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+#define VIDEOCODEC_VERSION "v0.2"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+
+// kernel config is here (procfs flag)
+#include <linux/config.h>
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#endif
+
+#include "videocodec.h"
+
+static int debug = 0;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-4)");
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+struct attached_list {
+ struct videocodec *codec;
+ struct attached_list *next;
+};
+
+struct codec_list {
+ const struct videocodec *codec;
+ int attached;
+ struct attached_list *list;
+ struct codec_list *next;
+};
+
+static struct codec_list *codeclist_top = NULL;
+
+/* ================================================= */
+/* function prototypes of the master/slave interface */
+/* ================================================= */
+
+struct videocodec *
+videocodec_attach (struct videocodec_master *master)
+{
+ struct codec_list *h = codeclist_top;
+ struct attached_list *a, *ptr;
+ struct videocodec *codec;
+ int res;
+
+ if (!master) {
+ dprintk(1, KERN_ERR "videocodec_attach: no data\n");
+ return NULL;
+ }
+
+ dprintk(2,
+ "videocodec_attach: '%s', type: %x, flags %lx, magic %lx\n",
+ master->name, master->type, master->flags, master->magic);
+
+ if (!h) {
+ dprintk(1,
+ KERN_ERR
+ "videocodec_attach: no device available\n");
+ return NULL;
+ }
+
+ while (h) {
+ // attach only if the slave has at least the flags
+ // expected by the master
+ if ((master->flags & h->codec->flags) == master->flags) {
+ dprintk(4, "videocodec_attach: try '%s'\n",
+ h->codec->name);
+
+ if (!try_module_get(h->codec->owner))
+ return NULL;
+
+ codec =
+ kmalloc(sizeof(struct videocodec), GFP_KERNEL);
+ if (!codec) {
+ dprintk(1,
+ KERN_ERR
+ "videocodec_attach: no mem\n");
+ goto out_module_put;
+ }
+ memcpy(codec, h->codec, sizeof(struct videocodec));
+
+ snprintf(codec->name, sizeof(codec->name),
+ "%s[%d]", codec->name, h->attached);
+ codec->master_data = master;
+ res = codec->setup(codec);
+ if (res == 0) {
+ dprintk(3, "videocodec_attach '%s'\n",
+ codec->name);
+ ptr = kzalloc(sizeof(struct attached_list), GFP_KERNEL);
+ if (!ptr) {
+ dprintk(1,
+ KERN_ERR
+ "videocodec_attach: no memory\n");
+ goto out_kfree;
+ }
+ ptr->codec = codec;
+
+ a = h->list;
+ if (!a) {
+ h->list = ptr;
+ dprintk(4,
+ "videocodec: first element\n");
+ } else {
+ while (a->next)
+ a = a->next; // find end
+ a->next = ptr;
+ dprintk(4,
+ "videocodec: in after '%s'\n",
+ h->codec->name);
+ }
+
+ h->attached += 1;
+ return codec;
+ } else {
+ kfree(codec);
+ }
+ }
+ h = h->next;
+ }
+
+ dprintk(1, KERN_ERR "videocodec_attach: no codec found!\n");
+ return NULL;
+
+ out_module_put:
+ module_put(h->codec->owner);
+ out_kfree:
+ kfree(codec);
+ return NULL;
+}
+
+int
+videocodec_detach (struct videocodec *codec)
+{
+ struct codec_list *h = codeclist_top;
+ struct attached_list *a, *prev;
+ int res;
+
+ if (!codec) {
+ dprintk(1, KERN_ERR "videocodec_detach: no data\n");
+ return -EINVAL;
+ }
+
+ dprintk(2,
+ "videocodec_detach: '%s', type: %x, flags %lx, magic %lx\n",
+ codec->name, codec->type, codec->flags, codec->magic);
+
+ if (!h) {
+ dprintk(1,
+ KERN_ERR "videocodec_detach: no device left...\n");
+ return -ENXIO;
+ }
+
+ while (h) {
+ a = h->list;
+ prev = NULL;
+ while (a) {
+ if (codec == a->codec) {
+ res = a->codec->unset(a->codec);
+ if (res >= 0) {
+ dprintk(3,
+ "videocodec_detach: '%s'\n",
+ a->codec->name);
+ a->codec->master_data = NULL;
+ } else {
+ dprintk(1,
+ KERN_ERR
+ "videocodec_detach: '%s'\n",
+ a->codec->name);
+ a->codec->master_data = NULL;
+ }
+ if (prev == NULL) {
+ h->list = a->next;
+ dprintk(4,
+ "videocodec: delete first\n");
+ } else {
+ prev->next = a->next;
+ dprintk(4,
+ "videocodec: delete middle\n");
+ }
+ module_put(a->codec->owner);
+ kfree(a->codec);
+ kfree(a);
+ h->attached -= 1;
+ return 0;
+ }
+ prev = a;
+ a = a->next;
+ }
+ h = h->next;
+ }
+
+ dprintk(1, KERN_ERR "videocodec_detach: given codec not found!\n");
+ return -EINVAL;
+}
+
+int
+videocodec_register (const struct videocodec *codec)
+{
+ struct codec_list *ptr, *h = codeclist_top;
+
+ if (!codec) {
+ dprintk(1, KERN_ERR "videocodec_register: no data!\n");
+ return -EINVAL;
+ }
+
+ dprintk(2,
+ "videocodec: register '%s', type: %x, flags %lx, magic %lx\n",
+ codec->name, codec->type, codec->flags, codec->magic);
+
+ ptr = kzalloc(sizeof(struct codec_list), GFP_KERNEL);
+ if (!ptr) {
+ dprintk(1, KERN_ERR "videocodec_register: no memory\n");
+ return -ENOMEM;
+ }
+ ptr->codec = codec;
+
+ if (!h) {
+ codeclist_top = ptr;
+ dprintk(4, "videocodec: hooked in as first element\n");
+ } else {
+ while (h->next)
+ h = h->next; // find the end
+ h->next = ptr;
+ dprintk(4, "videocodec: hooked in after '%s'\n",
+ h->codec->name);
+ }
+
+ return 0;
+}
+
+int
+videocodec_unregister (const struct videocodec *codec)
+{
+ struct codec_list *prev = NULL, *h = codeclist_top;
+
+ if (!codec) {
+ dprintk(1, KERN_ERR "videocodec_unregister: no data!\n");
+ return -EINVAL;
+ }
+
+ dprintk(2,
+ "videocodec: unregister '%s', type: %x, flags %lx, magic %lx\n",
+ codec->name, codec->type, codec->flags, codec->magic);
+
+ if (!h) {
+ dprintk(1,
+ KERN_ERR
+ "videocodec_unregister: no device left...\n");
+ return -ENXIO;
+ }
+
+ while (h) {
+ if (codec == h->codec) {
+ if (h->attached) {
+ dprintk(1,
+ KERN_ERR
+ "videocodec: '%s' is used\n",
+ h->codec->name);
+ return -EBUSY;
+ }
+ dprintk(3, "videocodec: unregister '%s' is ok.\n",
+ h->codec->name);
+ if (prev == NULL) {
+ codeclist_top = h->next;
+ dprintk(4,
+ "videocodec: delete first element\n");
+ } else {
+ prev->next = h->next;
+ dprintk(4,
+ "videocodec: delete middle element\n");
+ }
+ kfree(h);
+ return 0;
+ }
+ prev = h;
+ h = h->next;
+ }
+
+ dprintk(1,
+ KERN_ERR
+ "videocodec_unregister: given codec not found!\n");
+ return -EINVAL;
+}
+
+#ifdef CONFIG_PROC_FS
+/* ============ */
+/* procfs stuff */
+/* ============ */
+
+static char *videocodec_buf = NULL;
+static int videocodec_bufsize = 0;
+
+static int
+videocodec_build_table (void)
+{
+ struct codec_list *h = codeclist_top;
+ struct attached_list *a;
+ int i = 0, size;
+
+ // sum up amount of slaves plus their attached masters
+ while (h) {
+ i += h->attached + 1;
+ h = h->next;
+ }
+#define LINESIZE 100
+ size = LINESIZE * (i + 1);
+
+ dprintk(3, "videocodec_build table: %d entries, %d bytes\n", i,
+ size);
+
+ kfree(videocodec_buf);
+ videocodec_buf = (char *) kmalloc(size, GFP_KERNEL);
+
+ i = 0;
+ i += scnprintf(videocodec_buf + i, size - 1,
+ "<S>lave or attached <M>aster name type flags magic ");
+ i += scnprintf(videocodec_buf + i, size -i - 1, "(connected as)\n");
+
+ h = codeclist_top;
+ while (h) {
+ if (i > (size - LINESIZE))
+ break; // security check
+ i += scnprintf(videocodec_buf + i, size -i -1,
+ "S %32s %04x %08lx %08lx (TEMPLATE)\n",
+ h->codec->name, h->codec->type,
+ h->codec->flags, h->codec->magic);
+ a = h->list;
+ while (a) {
+ if (i > (size - LINESIZE))
+ break; // security check
+ i += scnprintf(videocodec_buf + i, size -i -1,
+ "M %32s %04x %08lx %08lx (%s)\n",
+ a->codec->master_data->name,
+ a->codec->master_data->type,
+ a->codec->master_data->flags,
+ a->codec->master_data->magic,
+ a->codec->name);
+ a = a->next;
+ }
+ h = h->next;
+ }
+
+ return i;
+}
+
+//The definition:
+//typedef int (read_proc_t)(char *page, char **start, off_t off,
+// int count, int *eof, void *data);
+
+static int
+videocodec_info (char *buffer,
+ char **buffer_location,
+ off_t offset,
+ int buffer_length,
+ int *eof,
+ void *data)
+{
+ int size;
+
+ dprintk(3, "videocodec_info: offset: %ld, len %d / size %d\n",
+ offset, buffer_length, videocodec_bufsize);
+
+ if (offset == 0) {
+ videocodec_bufsize = videocodec_build_table();
+ }
+ if ((offset < 0) || (offset >= videocodec_bufsize)) {
+ dprintk(4,
+ "videocodec_info: call delivers no result, return 0\n");
+ *eof = 1;
+ return 0;
+ }
+
+ if (buffer_length < (videocodec_bufsize - offset)) {
+ dprintk(4, "videocodec_info: %ld needed, %d got\n",
+ videocodec_bufsize - offset, buffer_length);
+ size = buffer_length;
+ } else {
+ dprintk(4, "videocodec_info: last reading of %ld bytes\n",
+ videocodec_bufsize - offset);
+ size = videocodec_bufsize - offset;
+ *eof = 1;
+ }
+
+ memcpy(buffer, videocodec_buf + offset, size);
+ /* doesn't work... */
+ /* copy_to_user(buffer, videocodec_buf+offset, size); */
+ /* *buffer_location = videocodec_buf+offset; */
+
+ return size;
+}
+#endif
+
+/* ===================== */
+/* hook in driver module */
+/* ===================== */
+static int __init
+videocodec_init (void)
+{
+#ifdef CONFIG_PROC_FS
+ static struct proc_dir_entry *videocodec_proc_entry;
+#endif
+
+ printk(KERN_INFO "Linux video codec intermediate layer: %s\n",
+ VIDEOCODEC_VERSION);
+
+#ifdef CONFIG_PROC_FS
+ videocodec_buf = NULL;
+ videocodec_bufsize = 0;
+
+ videocodec_proc_entry = create_proc_entry("videocodecs", 0, NULL);
+ if (videocodec_proc_entry) {
+ videocodec_proc_entry->read_proc = videocodec_info;
+ videocodec_proc_entry->write_proc = NULL;
+ videocodec_proc_entry->data = NULL;
+ videocodec_proc_entry->owner = THIS_MODULE;
+ } else {
+ dprintk(1, KERN_ERR "videocodec: can't init procfs.\n");
+ }
+#endif
+ return 0;
+}
+
+static void __exit
+videocodec_exit (void)
+{
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry("videocodecs", NULL);
+ kfree(videocodec_buf);
+#endif
+}
+
+EXPORT_SYMBOL(videocodec_attach);
+EXPORT_SYMBOL(videocodec_detach);
+EXPORT_SYMBOL(videocodec_register);
+EXPORT_SYMBOL(videocodec_unregister);
+
+module_init(videocodec_init);
+module_exit(videocodec_exit);
+
+MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>");
+MODULE_DESCRIPTION("Intermediate API module for video codecs "
+ VIDEOCODEC_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/video/videocodec.h b/linux/drivers/media/video/videocodec.h
new file mode 100644
index 000000000..3f968eb7b
--- /dev/null
+++ b/linux/drivers/media/video/videocodec.h
@@ -0,0 +1,359 @@
+/*
+ * VIDEO MOTION CODECs internal API for video devices
+ *
+ * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's
+ * bound to a master device.
+ *
+ * (c) 2002 Wolfgang Scherr <scherr@net4you.at>
+ *
+ * $Id: videocodec.h,v 1.1.2.4 2003/01/14 21:15:03 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+/* =================== */
+/* general description */
+/* =================== */
+
+/* Should ease the (re-)usage of drivers supporting cards with (different)
+ video codecs. The codecs register to this module their functionality,
+ and the processors (masters) can attach to them if they fit.
+
+ The codecs are typically have a "strong" binding to their master - so I
+ don't think it makes sense to have a full blown interfacing as with e.g.
+ i2c. If you have an other opinion, let's discuss & implement it :-)))
+
+ Usage:
+
+ The slave has just to setup the videocodec structure and use two functions:
+ videocodec_register(codecdata);
+ videocodec_unregister(codecdata);
+ The best is just calling them at module (de-)initialisation.
+
+ The master sets up the structure videocodec_master and calls:
+ codecdata=videocodec_attach(master_codecdata);
+ videocodec_detach(codecdata);
+
+ The slave is called during attach/detach via functions setup previously
+ during register. At that time, the master_data pointer is set up
+ and the slave can access any io registers of the master device (in the case
+ the slave is bound to it). Otherwise it doesn't need this functions and
+ therfor they may not be initialized.
+
+ The other fuctions are just for convenience, as they are for sure used by
+ most/all of the codecs. The last ones may be ommited, too.
+
+ See the structure declaration below for more information and which data has
+ to be set up for the master and the slave.
+
+ ----------------------------------------------------------------------------
+ The master should have "knowledge" of the slave and vice versa. So the data
+ structures sent to/from slave via set_data/get_data set_image/get_image are
+ device dependent and vary between MJPEG/MPEG/WAVELET/... devices. (!!!!)
+ ----------------------------------------------------------------------------
+*/
+
+
+/* ========================================== */
+/* description of the videocodec_io structure */
+/* ========================================== */
+
+/*
+ ==== master setup ====
+ name -> name of the device structure for reference and debugging
+ master_data -> data ref. for the master (e.g. the zr36055,57,67)
+ readreg -> ref. to read-fn from register (setup by master, used by slave)
+ writereg -> ref. to write-fn to register (setup by master, used by slave)
+ this two functions do the lowlevel I/O job
+
+ ==== slave functionality setup ====
+ slave_data -> data ref. for the slave (e.g. the zr36050,60)
+ check -> fn-ref. checks availability of an device, returns -EIO on failure or
+ the type on success
+ this makes espcecially sense if a driver module supports more than
+ one codec which may be quite similar to access, nevertheless it
+ is good for a first functionality check
+
+ -- main functions you always need for compression/decompression --
+
+ set_mode -> this fn-ref. resets the entire codec, and sets up the mode
+ with the last defined norm/size (or device default if not
+ available) - it returns 0 if the mode is possible
+ set_size -> this fn-ref. sets the norm and image size for
+ compression/decompression (returns 0 on success)
+ the norm param is defined in videodev.h (VIDEO_MODE_*)
+
+ additional setup may be available, too - but the codec should work with
+ some default values even without this
+
+ set_data -> sets device-specific data (tables, quality etc.)
+ get_data -> query device-specific data (tables, quality etc.)
+
+ if the device delivers interrupts, they may be setup/handled here
+ setup_interrupt -> codec irq setup (not needed for 36050/60)
+ handle_interrupt -> codec irq handling (not needed for 36050/60)
+
+ if the device delivers pictures, they may be handled here
+ put_image -> puts image data to the codec (not needed for 36050/60)
+ get_image -> gets image data from the codec (not needed for 36050/60)
+ the calls include frame numbers and flags (even/odd/...)
+ if needed and a flag which allows blocking until its ready
+*/
+
+/* ============== */
+/* user interface */
+/* ============== */
+
+/*
+ Currently there is only a information display planned, as the layer
+ is not visible for the user space at all.
+
+ Information is available via procfs. The current entry is "/proc/videocodecs"
+ but it makes sense to "hide" it in the /proc/video tree of v4l(2) --TODO--.
+
+A example for such an output is:
+
+<S>lave or attached <M>aster name type flags magic (connected as)
+S zr36050 0002 0000d001 00000000 (TEMPLATE)
+M zr36055[0] 0001 0000c001 00000000 (zr36050[0])
+M zr36055[1] 0001 0000c001 00000000 (zr36050[1])
+
+*/
+
+
+/* =============================================== */
+/* special defines for the videocodec_io structure */
+/* =============================================== */
+
+#ifndef __LINUX_VIDEOCODEC_H
+#define __LINUX_VIDEOCODEC_H
+
+#include "compat.h"
+#include <linux/videodev.h>
+
+//should be in videodev.h ??? (VID_DO_....)
+#define CODEC_DO_COMPRESSION 0
+#define CODEC_DO_EXPANSION 1
+
+/* this are the current codec flags I think they are needed */
+/* -> type value in structure */
+#define CODEC_FLAG_JPEG 0x00000001L // JPEG codec
+#define CODEC_FLAG_MPEG 0x00000002L // MPEG1/2/4 codec
+#define CODEC_FLAG_DIVX 0x00000004L // DIVX codec
+#define CODEC_FLAG_WAVELET 0x00000008L // WAVELET codec
+ // room for other types
+
+#define CODEC_FLAG_MAGIC 0x00000800L // magic key must match
+#define CODEC_FLAG_HARDWARE 0x00001000L // is a hardware codec
+#define CODEC_FLAG_VFE 0x00002000L // has direct video frontend
+#define CODEC_FLAG_ENCODER 0x00004000L // compression capability
+#define CODEC_FLAG_DECODER 0x00008000L // decompression capability
+#define CODEC_FLAG_NEEDIRQ 0x00010000L // needs irq handling
+#define CODEC_FLAG_RDWRPIC 0x00020000L // handles picture I/O
+
+/* a list of modes, some are just examples (is there any HW?) */
+#define CODEC_MODE_BJPG 0x0001 // Baseline JPEG
+#define CODEC_MODE_LJPG 0x0002 // Lossless JPEG
+#define CODEC_MODE_MPEG1 0x0003 // MPEG 1
+#define CODEC_MODE_MPEG2 0x0004 // MPEG 2
+#define CODEC_MODE_MPEG4 0x0005 // MPEG 4
+#define CODEC_MODE_MSDIVX 0x0006 // MS DivX
+#define CODEC_MODE_ODIVX 0x0007 // Open DivX
+#define CODEC_MODE_WAVELET 0x0008 // Wavelet
+
+/* this are the current codec types I want to implement */
+/* -> type value in structure */
+#define CODEC_TYPE_NONE 0
+#define CODEC_TYPE_L64702 1
+#define CODEC_TYPE_ZR36050 2
+#define CODEC_TYPE_ZR36016 3
+#define CODEC_TYPE_ZR36060 4
+
+/* the type of data may be enhanced by future implementations (data-fn.'s) */
+/* -> used in command */
+#define CODEC_G_STATUS 0x0000 /* codec status (query only) */
+#define CODEC_S_CODEC_MODE 0x0001 /* codec mode (baseline JPEG, MPEG1,... */
+#define CODEC_G_CODEC_MODE 0x8001
+#define CODEC_S_VFE 0x0002 /* additional video frontend setup */
+#define CODEC_G_VFE 0x8002
+#define CODEC_S_MMAP 0x0003 /* MMAP setup (if available) */
+
+#define CODEC_S_JPEG_TDS_BYTE 0x0010 /* target data size in bytes */
+#define CODEC_G_JPEG_TDS_BYTE 0x8010
+#define CODEC_S_JPEG_SCALE 0x0011 /* scaling factor for quant. tables */
+#define CODEC_G_JPEG_SCALE 0x8011
+#define CODEC_S_JPEG_HDT_DATA 0x0018 /* huffman-tables */
+#define CODEC_G_JPEG_HDT_DATA 0x8018
+#define CODEC_S_JPEG_QDT_DATA 0x0019 /* quantizing-tables */
+#define CODEC_G_JPEG_QDT_DATA 0x8019
+#define CODEC_S_JPEG_APP_DATA 0x001A /* APP marker */
+#define CODEC_G_JPEG_APP_DATA 0x801A
+#define CODEC_S_JPEG_COM_DATA 0x001B /* COM marker */
+#define CODEC_G_JPEG_COM_DATA 0x801B
+
+#define CODEC_S_PRIVATE 0x1000 /* "private" commands start here */
+#define CODEC_G_PRIVATE 0x9000
+
+#define CODEC_G_FLAG 0x8000 /* this is how 'get' is detected */
+
+/* types of transfer, directly user space or a kernel buffer (image-fn.'s) */
+/* -> used in get_image, put_image */
+#define CODEC_TRANSFER_KERNEL 0 /* use "memcopy" */
+#define CODEC_TRANSFER_USER 1 /* use "to/from_user" */
+
+
+/* ========================= */
+/* the structures itself ... */
+/* ========================= */
+
+struct vfe_polarity {
+ int vsync_pol:1;
+ int hsync_pol:1;
+ int field_pol:1;
+ int blank_pol:1;
+ int subimg_pol:1;
+ int poe_pol:1;
+ int pvalid_pol:1;
+ int vclk_pol:1;
+};
+
+struct vfe_settings {
+ __u32 x, y; /* Offsets into image */
+ __u32 width, height; /* Area to capture */
+ __u16 decimation; /* Decimation divider */
+ __u16 flags; /* Flags for capture */
+/* flags are the same as in struct video_capture - see videodev.h:
+#define VIDEO_CAPTURE_ODD 0
+#define VIDEO_CAPTURE_EVEN 1
+*/
+ __u16 quality; /* quality of the video */
+};
+
+struct tvnorm {
+ u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart;
+};
+
+struct jpeg_com_marker {
+ int len; /* number of usable bytes in data */
+ char data[60];
+};
+
+struct jpeg_app_marker {
+ int appn; /* number app segment */
+ int len; /* number of usable bytes in data */
+ char data[60];
+};
+
+struct videocodec {
+ struct module *owner;
+ /* -- filled in by slave device during register -- */
+ char name[32];
+ unsigned long magic; /* may be used for client<->master attaching */
+ unsigned long flags; /* functionality flags */
+ unsigned int type; /* codec type */
+
+ /* -- these is filled in later during master device attach -- */
+
+ struct videocodec_master *master_data;
+
+ /* -- these are filled in by the slave device during register -- */
+
+ void *data; /* private slave data */
+
+ /* attach/detach client functions (indirect call) */
+ int (*setup) (struct videocodec * codec);
+ int (*unset) (struct videocodec * codec);
+
+ /* main functions, every client needs them for sure! */
+ // set compression or decompression (or freeze, stop, standby, etc)
+ int (*set_mode) (struct videocodec * codec,
+ int mode);
+ // setup picture size and norm (for the codec's video frontend)
+ int (*set_video) (struct videocodec * codec,
+ struct tvnorm * norm,
+ struct vfe_settings * cap,
+ struct vfe_polarity * pol);
+ // other control commands, also mmap setup etc.
+ int (*control) (struct videocodec * codec,
+ int type,
+ int size,
+ void *data);
+
+ /* additional setup/query/processing (may be NULL pointer) */
+ // interrupt setup / handling (for irq's delivered by master)
+ int (*setup_interrupt) (struct videocodec * codec,
+ long mode);
+ int (*handle_interrupt) (struct videocodec * codec,
+ int source,
+ long flag);
+ // picture interface (if any)
+ long (*put_image) (struct videocodec * codec,
+ int tr_type,
+ int block,
+ long *fr_num,
+ long *flag,
+ long size,
+ void *buf);
+ long (*get_image) (struct videocodec * codec,
+ int tr_type,
+ int block,
+ long *fr_num,
+ long *flag,
+ long size,
+ void *buf);
+};
+
+struct videocodec_master {
+ /* -- filled in by master device for registration -- */
+ char name[32];
+ unsigned long magic; /* may be used for client<->master attaching */
+ unsigned long flags; /* functionality flags */
+ unsigned int type; /* master type */
+
+ void *data; /* private master data */
+
+ __u32(*readreg) (struct videocodec * codec,
+ __u16 reg);
+ void (*writereg) (struct videocodec * codec,
+ __u16 reg,
+ __u32 value);
+};
+
+
+/* ================================================= */
+/* function prototypes of the master/slave interface */
+/* ================================================= */
+
+/* attach and detach commands for the master */
+// * master structure needs to be kmalloc'ed before calling attach
+// and free'd after calling detach
+// * returns pointer on success, NULL on failure
+extern struct videocodec *videocodec_attach(struct videocodec_master *);
+// * 0 on success, <0 (errno) on failure
+extern int videocodec_detach(struct videocodec *);
+
+/* register and unregister commands for the slaves */
+// * 0 on success, <0 (errno) on failure
+extern int videocodec_register(const struct videocodec *);
+// * 0 on success, <0 (errno) on failure
+extern int videocodec_unregister(const struct videocodec *);
+
+/* the other calls are directly done via the videocodec structure! */
+
+#endif /*ifndef __LINUX_VIDEOCODEC_H */
diff --git a/linux/drivers/media/video/vino.c b/linux/drivers/media/video/vino.c
new file mode 100644
index 000000000..a8c101494
--- /dev/null
+++ b/linux/drivers/media/video/vino.c
@@ -0,0 +1,4680 @@
+/*
+ * Driver for the VINO (Video In No Out) system found in SGI Indys.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
+ *
+ * Based on the previous version of the driver for 2.4 kernels by:
+ * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ */
+
+/*
+ * TODO:
+ * - remove "mark pages reserved-hacks" from memory allocation code
+ * and implement nopage()
+ * - check decimation, calculating and reporting image size when
+ * using decimation
+ * - implement read(), user mode buffers and overlay (?)
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/version.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#include <linux/i2c.h>
+#include <linux/i2c-algo-sgi.h>
+
+#include <linux/videodev.h>
+#include <linux/videodev2.h>
+#include <linux/video_decoder.h>
+#include <linux/mutex.h>
+
+#include <asm/paccess.h>
+#include <asm/io.h>
+#include <asm/sgi/ip22.h>
+#include <asm/sgi/mc.h>
+
+#include "vino.h"
+#include "saa7191.h"
+#include "indycam.h"
+
+/* Uncomment the following line to get lots and lots of (mostly useless)
+ * debug info.
+ * Note that the debug output also slows down the driver significantly */
+// #define VINO_DEBUG
+// #define VINO_DEBUG_INT
+
+#define VINO_MODULE_VERSION "0.0.5"
+#define VINO_VERSION_CODE KERNEL_VERSION(0, 0, 5)
+
+MODULE_DESCRIPTION("SGI VINO Video4Linux2 driver");
+MODULE_VERSION(VINO_MODULE_VERSION);
+MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>");
+MODULE_LICENSE("GPL");
+
+#ifdef VINO_DEBUG
+#define dprintk(x...) printk("VINO: " x);
+#else
+#define dprintk(x...)
+#endif
+
+#define VINO_NO_CHANNEL 0
+#define VINO_CHANNEL_A 1
+#define VINO_CHANNEL_B 2
+
+#define VINO_PAL_WIDTH 768
+#define VINO_PAL_HEIGHT 576
+#define VINO_NTSC_WIDTH 640
+#define VINO_NTSC_HEIGHT 480
+
+#define VINO_MIN_WIDTH 32
+#define VINO_MIN_HEIGHT 32
+
+#define VINO_CLIPPING_START_ODD_D1 1
+#define VINO_CLIPPING_START_ODD_PAL 15
+#define VINO_CLIPPING_START_ODD_NTSC 12
+
+#define VINO_CLIPPING_START_EVEN_D1 2
+#define VINO_CLIPPING_START_EVEN_PAL 15
+#define VINO_CLIPPING_START_EVEN_NTSC 12
+
+#define VINO_INPUT_CHANNEL_COUNT 3
+
+/* the number is the index for vino_inputs */
+#define VINO_INPUT_NONE -1
+#define VINO_INPUT_COMPOSITE 0
+#define VINO_INPUT_SVIDEO 1
+#define VINO_INPUT_D1 2
+
+#define VINO_PAGE_RATIO (PAGE_SIZE / VINO_PAGE_SIZE)
+
+#define VINO_FIFO_THRESHOLD_DEFAULT 16
+
+#define VINO_FRAMEBUFFER_SIZE ((VINO_PAL_WIDTH \
+ * VINO_PAL_HEIGHT * 4 \
+ + 3 * PAGE_SIZE) & ~(PAGE_SIZE - 1))
+
+#define VINO_FRAMEBUFFER_COUNT_MAX 8
+
+#define VINO_FRAMEBUFFER_UNUSED 0
+#define VINO_FRAMEBUFFER_IN_USE 1
+#define VINO_FRAMEBUFFER_READY 2
+
+#define VINO_QUEUE_ERROR -1
+#define VINO_QUEUE_MAGIC 0x20050125
+
+#define VINO_MEMORY_NONE 0
+#define VINO_MEMORY_MMAP 1
+#define VINO_MEMORY_USERPTR 2
+
+#define VINO_DUMMY_DESC_COUNT 4
+#define VINO_DESC_FETCH_DELAY 5 /* microseconds */
+
+#define VINO_MAX_FRAME_SKIP_COUNT 128
+
+/* the number is the index for vino_data_formats */
+#define VINO_DATA_FMT_NONE -1
+#define VINO_DATA_FMT_GREY 0
+#define VINO_DATA_FMT_RGB332 1
+#define VINO_DATA_FMT_RGB32 2
+#define VINO_DATA_FMT_YUV 3
+
+#define VINO_DATA_FMT_COUNT 4
+
+/* the number is the index for vino_data_norms */
+#define VINO_DATA_NORM_NONE -1
+#define VINO_DATA_NORM_NTSC 0
+#define VINO_DATA_NORM_PAL 1
+#define VINO_DATA_NORM_SECAM 2
+#define VINO_DATA_NORM_D1 3
+/* The following are special entries that can be used to
+ * autodetect the norm. */
+#define VINO_DATA_NORM_AUTO 0xfe
+#define VINO_DATA_NORM_AUTO_EXT 0xff
+
+#define VINO_DATA_NORM_COUNT 4
+
+/* Internal data structure definitions */
+
+struct vino_input {
+ char *name;
+ v4l2_std_id std;
+};
+
+struct vino_clipping {
+ unsigned int left, right, top, bottom;
+};
+
+struct vino_data_format {
+ /* the description */
+ char *description;
+ /* bytes per pixel */
+ unsigned int bpp;
+ /* V4L2 fourcc code */
+ __u32 pixelformat;
+ /* V4L2 colorspace (duh!) */
+ enum v4l2_colorspace colorspace;
+};
+
+struct vino_data_norm {
+ char *description;
+ unsigned int width, height;
+ struct vino_clipping odd;
+ struct vino_clipping even;
+
+ v4l2_std_id std;
+ unsigned int fps_min, fps_max;
+ __u32 framelines;
+};
+
+struct vino_descriptor_table {
+ /* the number of PAGE_SIZE sized pages in the buffer */
+ unsigned int page_count;
+ /* virtual (kmalloc'd) pointers to the actual data
+ * (in PAGE_SIZE chunks, used with mmap streaming) */
+ unsigned long *virtual;
+
+ /* cpu address for the VINO descriptor table
+ * (contains DMA addresses, VINO_PAGE_SIZE chunks) */
+ unsigned long *dma_cpu;
+ /* dma address for the VINO descriptor table
+ * (contains DMA addresses, VINO_PAGE_SIZE chunks) */
+ dma_addr_t dma;
+};
+
+struct vino_framebuffer {
+ /* identifier nubmer */
+ unsigned int id;
+ /* the length of the whole buffer */
+ unsigned int size;
+ /* the length of actual data in buffer */
+ unsigned int data_size;
+ /* the data format */
+ unsigned int data_format;
+ /* the state of buffer data */
+ unsigned int state;
+ /* is the buffer mapped in user space? */
+ unsigned int map_count;
+ /* memory offset for mmap() */
+ unsigned int offset;
+ /* frame counter */
+ unsigned int frame_counter;
+ /* timestamp (written when image capture finishes) */
+ struct timeval timestamp;
+
+ struct vino_descriptor_table desc_table;
+
+ spinlock_t state_lock;
+};
+
+struct vino_framebuffer_fifo {
+ unsigned int length;
+
+ unsigned int used;
+ unsigned int head;
+ unsigned int tail;
+
+ unsigned int data[VINO_FRAMEBUFFER_COUNT_MAX];
+};
+
+struct vino_framebuffer_queue {
+ unsigned int magic;
+
+ /* VINO_MEMORY_NONE, VINO_MEMORY_MMAP or VINO_MEMORY_USERPTR */
+ unsigned int type;
+ unsigned int length;
+
+ /* data field of in and out contain index numbers for buffer */
+ struct vino_framebuffer_fifo in;
+ struct vino_framebuffer_fifo out;
+
+ struct vino_framebuffer *buffer[VINO_FRAMEBUFFER_COUNT_MAX];
+
+ spinlock_t queue_lock;
+ struct mutex queue_mutex;
+ wait_queue_head_t frame_wait_queue;
+};
+
+struct vino_interrupt_data {
+ struct timeval timestamp;
+ unsigned int frame_counter;
+ unsigned int skip_count;
+ unsigned int skip;
+};
+
+struct vino_channel_settings {
+ unsigned int channel;
+
+ int input;
+ unsigned int data_format;
+ unsigned int data_norm;
+ struct vino_clipping clipping;
+ unsigned int decimation;
+ unsigned int line_size;
+ unsigned int alpha;
+ unsigned int fps;
+ unsigned int framert_reg;
+
+ unsigned int fifo_threshold;
+
+ struct vino_framebuffer_queue fb_queue;
+
+ /* number of the current field */
+ unsigned int field;
+
+ /* read in progress */
+ int reading;
+ /* streaming is active */
+ int streaming;
+ /* the driver is currently processing the queue */
+ int capturing;
+
+ struct mutex mutex;
+ spinlock_t capture_lock;
+
+ unsigned int users;
+
+ struct vino_interrupt_data int_data;
+
+ /* V4L support */
+ struct video_device *v4l_device;
+};
+
+struct vino_client {
+ /* the channel which owns this client:
+ * VINO_NO_CHANNEL, VINO_CHANNEL_A or VINO_CHANNEL_B */
+ unsigned int owner;
+ struct i2c_client *driver;
+};
+
+struct vino_settings {
+ struct vino_channel_settings a;
+ struct vino_channel_settings b;
+
+ struct vino_client decoder;
+ struct vino_client camera;
+
+ /* a lock for vino register access */
+ spinlock_t vino_lock;
+ /* a lock for channel input changes */
+ spinlock_t input_lock;
+
+ unsigned long dummy_page;
+ struct vino_descriptor_table dummy_desc_table;
+};
+
+/* Module parameters */
+
+/*
+ * Using vino_pixel_conversion the ABGR32-format pixels supplied
+ * by the VINO chip can be converted to more common formats
+ * like RGBA32 (or probably RGB24 in the future). This way we
+ * can give out data that can be specified correctly with
+ * the V4L2-definitions.
+ *
+ * The pixel format is specified as RGBA32 when no conversion
+ * is used.
+ *
+ * Note that this only affects the 32-bit bit depth.
+ *
+ * Use non-zero value to enable conversion.
+ */
+static int vino_pixel_conversion = 0;
+
+module_param_named(pixelconv, vino_pixel_conversion, int, 0);
+
+MODULE_PARM_DESC(pixelconv,
+ "enable pixel conversion (non-zero value enables)");
+
+/* Internal data structures */
+
+static struct sgi_vino *vino;
+
+static struct vino_settings *vino_drvdata;
+
+static const char *vino_driver_name = "vino";
+static const char *vino_driver_description = "SGI VINO";
+static const char *vino_bus_name = "GIO64 bus";
+static const char *vino_v4l_device_name_a = "SGI VINO Channel A";
+static const char *vino_v4l_device_name_b = "SGI VINO Channel B";
+
+static void vino_capture_tasklet(unsigned long channel);
+
+DECLARE_TASKLET(vino_tasklet_a, vino_capture_tasklet, VINO_CHANNEL_A);
+DECLARE_TASKLET(vino_tasklet_b, vino_capture_tasklet, VINO_CHANNEL_B);
+
+static const struct vino_input vino_inputs[] = {
+ {
+ .name = "Composite",
+ .std = V4L2_STD_NTSC | V4L2_STD_PAL
+ | V4L2_STD_SECAM,
+ },{
+ .name = "S-Video",
+ .std = V4L2_STD_NTSC | V4L2_STD_PAL
+ | V4L2_STD_SECAM,
+ },{
+ .name = "D1/IndyCam",
+ .std = V4L2_STD_NTSC,
+ }
+};
+
+static const struct vino_data_format vino_data_formats[] = {
+ {
+ .description = "8-bit greyscale",
+ .bpp = 1,
+ .pixelformat = V4L2_PIX_FMT_GREY,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ },{
+ .description = "8-bit dithered RGB 3-3-2",
+ .bpp = 1,
+ .pixelformat = V4L2_PIX_FMT_RGB332,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ },{
+ .description = "32-bit RGB",
+ .bpp = 4,
+ .pixelformat = V4L2_PIX_FMT_RGB32,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ },{
+ .description = "YUV 4:2:2",
+ .bpp = 2,
+ .pixelformat = V4L2_PIX_FMT_YUYV, // XXX: swapped?
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ }
+};
+
+static const struct vino_data_norm vino_data_norms[] = {
+ {
+ .description = "NTSC",
+ .std = V4L2_STD_NTSC,
+ .fps_min = 6,
+ .fps_max = 30,
+ .framelines = 525,
+ .width = VINO_NTSC_WIDTH,
+ .height = VINO_NTSC_HEIGHT,
+ .odd = {
+ .top = VINO_CLIPPING_START_ODD_NTSC,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_ODD_NTSC
+ + VINO_NTSC_HEIGHT / 2 - 1,
+ .right = VINO_NTSC_WIDTH,
+ },
+ .even = {
+ .top = VINO_CLIPPING_START_EVEN_NTSC,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_EVEN_NTSC
+ + VINO_NTSC_HEIGHT / 2 - 1,
+ .right = VINO_NTSC_WIDTH,
+ },
+ },{
+ .description = "PAL",
+ .std = V4L2_STD_PAL,
+ .fps_min = 5,
+ .fps_max = 25,
+ .framelines = 625,
+ .width = VINO_PAL_WIDTH,
+ .height = VINO_PAL_HEIGHT,
+ .odd = {
+ .top = VINO_CLIPPING_START_ODD_PAL,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_ODD_PAL
+ + VINO_PAL_HEIGHT / 2 - 1,
+ .right = VINO_PAL_WIDTH,
+ },
+ .even = {
+ .top = VINO_CLIPPING_START_EVEN_PAL,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_EVEN_PAL
+ + VINO_PAL_HEIGHT / 2 - 1,
+ .right = VINO_PAL_WIDTH,
+ },
+ },{
+ .description = "SECAM",
+ .std = V4L2_STD_SECAM,
+ .fps_min = 5,
+ .fps_max = 25,
+ .framelines = 625,
+ .width = VINO_PAL_WIDTH,
+ .height = VINO_PAL_HEIGHT,
+ .odd = {
+ .top = VINO_CLIPPING_START_ODD_PAL,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_ODD_PAL
+ + VINO_PAL_HEIGHT / 2 - 1,
+ .right = VINO_PAL_WIDTH,
+ },
+ .even = {
+ .top = VINO_CLIPPING_START_EVEN_PAL,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_EVEN_PAL
+ + VINO_PAL_HEIGHT / 2 - 1,
+ .right = VINO_PAL_WIDTH,
+ },
+ },{
+ .description = "NTSC/D1",
+ .std = V4L2_STD_NTSC,
+ .fps_min = 6,
+ .fps_max = 30,
+ .framelines = 525,
+ .width = VINO_NTSC_WIDTH,
+ .height = VINO_NTSC_HEIGHT,
+ .odd = {
+ .top = VINO_CLIPPING_START_ODD_D1,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_ODD_D1
+ + VINO_NTSC_HEIGHT / 2 - 1,
+ .right = VINO_NTSC_WIDTH,
+ },
+ .even = {
+ .top = VINO_CLIPPING_START_EVEN_D1,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_EVEN_D1
+ + VINO_NTSC_HEIGHT / 2 - 1,
+ .right = VINO_NTSC_WIDTH,
+ },
+ }
+};
+
+#define VINO_INDYCAM_V4L2_CONTROL_COUNT 9
+
+struct v4l2_queryctrl vino_indycam_v4l2_controls[] = {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Automatic Gain Control",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = INDYCAM_AGC_DEFAULT,
+ .flags = 0,
+ .reserved = { INDYCAM_CONTROL_AGC, 0 },
+ },{
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Automatic White Balance",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = INDYCAM_AWB_DEFAULT,
+ .flags = 0,
+ .reserved = { INDYCAM_CONTROL_AWB, 0 },
+ },{
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = INDYCAM_GAIN_MIN,
+ .maximum = INDYCAM_GAIN_MAX,
+ .step = 1,
+ .default_value = INDYCAM_GAIN_DEFAULT,
+ .flags = 0,
+ .reserved = { INDYCAM_CONTROL_GAIN, 0 },
+ },{
+ .id = V4L2_CID_PRIVATE_BASE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Saturation",
+ .minimum = INDYCAM_RED_SATURATION_MIN,
+ .maximum = INDYCAM_RED_SATURATION_MAX,
+ .step = 1,
+ .default_value = INDYCAM_RED_SATURATION_DEFAULT,
+ .flags = 0,
+ .reserved = { INDYCAM_CONTROL_RED_SATURATION, 0 },
+ },{
+ .id = V4L2_CID_PRIVATE_BASE + 1,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Saturation",
+ .minimum = INDYCAM_BLUE_SATURATION_MIN,
+ .maximum = INDYCAM_BLUE_SATURATION_MAX,
+ .step = 1,
+ .default_value = INDYCAM_BLUE_SATURATION_DEFAULT,
+ .flags = 0,
+ .reserved = { INDYCAM_CONTROL_BLUE_SATURATION, 0 },
+ },{
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = INDYCAM_RED_BALANCE_MIN,
+ .maximum = INDYCAM_RED_BALANCE_MAX,
+ .step = 1,
+ .default_value = INDYCAM_RED_BALANCE_DEFAULT,
+ .flags = 0,
+ .reserved = { INDYCAM_CONTROL_RED_BALANCE, 0 },
+ },{
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = INDYCAM_BLUE_BALANCE_MIN,
+ .maximum = INDYCAM_BLUE_BALANCE_MAX,
+ .step = 1,
+ .default_value = INDYCAM_BLUE_BALANCE_DEFAULT,
+ .flags = 0,
+ .reserved = { INDYCAM_CONTROL_BLUE_BALANCE, 0 },
+ },{
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Shutter Control",
+ .minimum = INDYCAM_SHUTTER_MIN,
+ .maximum = INDYCAM_SHUTTER_MAX,
+ .step = 1,
+ .default_value = INDYCAM_SHUTTER_DEFAULT,
+ .flags = 0,
+ .reserved = { INDYCAM_CONTROL_SHUTTER, 0 },
+ },{
+ .id = V4L2_CID_GAMMA,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gamma",
+ .minimum = INDYCAM_GAMMA_MIN,
+ .maximum = INDYCAM_GAMMA_MAX,
+ .step = 1,
+ .default_value = INDYCAM_GAMMA_DEFAULT,
+ .flags = 0,
+ .reserved = { INDYCAM_CONTROL_GAMMA, 0 },
+ }
+};
+
+#define VINO_SAA7191_V4L2_CONTROL_COUNT 9
+
+struct v4l2_queryctrl vino_saa7191_v4l2_controls[] = {
+ {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Hue",
+ .minimum = SAA7191_HUE_MIN,
+ .maximum = SAA7191_HUE_MAX,
+ .step = 1,
+ .default_value = SAA7191_HUE_DEFAULT,
+ .flags = 0,
+ .reserved = { SAA7191_CONTROL_HUE, 0 },
+ },{
+ .id = V4L2_CID_PRIVATE_BASE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Luminance Bandpass",
+ .minimum = SAA7191_BANDPASS_MIN,
+ .maximum = SAA7191_BANDPASS_MAX,
+ .step = 1,
+ .default_value = SAA7191_BANDPASS_DEFAULT,
+ .flags = 0,
+ .reserved = { SAA7191_CONTROL_BANDPASS, 0 },
+ },{
+ .id = V4L2_CID_PRIVATE_BASE + 1,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Luminance Bandpass Weight",
+ .minimum = SAA7191_BANDPASS_WEIGHT_MIN,
+ .maximum = SAA7191_BANDPASS_WEIGHT_MAX,
+ .step = 1,
+ .default_value = SAA7191_BANDPASS_WEIGHT_DEFAULT,
+ .flags = 0,
+ .reserved = { SAA7191_CONTROL_BANDPASS_WEIGHT, 0 },
+ },{
+ .id = V4L2_CID_PRIVATE_BASE + 2,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "HF Luminance Coring",
+ .minimum = SAA7191_CORING_MIN,
+ .maximum = SAA7191_CORING_MAX,
+ .step = 1,
+ .default_value = SAA7191_CORING_DEFAULT,
+ .flags = 0,
+ .reserved = { SAA7191_CONTROL_CORING, 0 },
+ },{
+ .id = V4L2_CID_PRIVATE_BASE + 3,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Force Colour",
+ .minimum = SAA7191_FORCE_COLOUR_MIN,
+ .maximum = SAA7191_FORCE_COLOUR_MAX,
+ .step = 1,
+ .default_value = SAA7191_FORCE_COLOUR_DEFAULT,
+ .flags = 0,
+ .reserved = { SAA7191_CONTROL_FORCE_COLOUR, 0 },
+ },{
+ .id = V4L2_CID_PRIVATE_BASE + 4,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Chrominance Gain Control",
+ .minimum = SAA7191_CHROMA_GAIN_MIN,
+ .maximum = SAA7191_CHROMA_GAIN_MAX,
+ .step = 1,
+ .default_value = SAA7191_CHROMA_GAIN_DEFAULT,
+ .flags = 0,
+ .reserved = { SAA7191_CONTROL_CHROMA_GAIN, 0 },
+ },{
+ .id = V4L2_CID_PRIVATE_BASE + 5,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "VTR Time Constant",
+ .minimum = SAA7191_VTRC_MIN,
+ .maximum = SAA7191_VTRC_MAX,
+ .step = 1,
+ .default_value = SAA7191_VTRC_DEFAULT,
+ .flags = 0,
+ .reserved = { SAA7191_CONTROL_VTRC, 0 },
+ },{
+ .id = V4L2_CID_PRIVATE_BASE + 6,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Luminance Delay Compensation",
+ .minimum = SAA7191_LUMA_DELAY_MIN,
+ .maximum = SAA7191_LUMA_DELAY_MAX,
+ .step = 1,
+ .default_value = SAA7191_LUMA_DELAY_DEFAULT,
+ .flags = 0,
+ .reserved = { SAA7191_CONTROL_LUMA_DELAY, 0 },
+ },{
+ .id = V4L2_CID_PRIVATE_BASE + 7,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Vertical Noise Reduction",
+ .minimum = SAA7191_VNR_MIN,
+ .maximum = SAA7191_VNR_MAX,
+ .step = 1,
+ .default_value = SAA7191_VNR_DEFAULT,
+ .flags = 0,
+ .reserved = { SAA7191_CONTROL_VNR, 0 },
+ }
+};
+
+/* VINO I2C bus functions */
+
+unsigned i2c_vino_getctrl(void *data)
+{
+ return vino->i2c_control;
+}
+
+void i2c_vino_setctrl(void *data, unsigned val)
+{
+ vino->i2c_control = val;
+}
+
+unsigned i2c_vino_rdata(void *data)
+{
+ return vino->i2c_data;
+}
+
+void i2c_vino_wdata(void *data, unsigned val)
+{
+ vino->i2c_data = val;
+}
+
+static struct i2c_algo_sgi_data i2c_sgi_vino_data =
+{
+ .getctrl = &i2c_vino_getctrl,
+ .setctrl = &i2c_vino_setctrl,
+ .rdata = &i2c_vino_rdata,
+ .wdata = &i2c_vino_wdata,
+ .xfer_timeout = 200,
+ .ack_timeout = 1000,
+};
+
+/*
+ * There are two possible clients on VINO I2C bus, so we limit usage only
+ * to them.
+ */
+static int i2c_vino_client_reg(struct i2c_client *client)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+ switch (client->driver->id) {
+ case I2C_DRIVERID_SAA7191:
+ if (vino_drvdata->decoder.driver)
+ ret = -EBUSY;
+ else
+ vino_drvdata->decoder.driver = client;
+ break;
+ case I2C_DRIVERID_INDYCAM:
+ if (vino_drvdata->camera.driver)
+ ret = -EBUSY;
+ else
+ vino_drvdata->camera.driver = client;
+ break;
+ default:
+ ret = -ENODEV;
+ }
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return ret;
+}
+
+static int i2c_vino_client_unreg(struct i2c_client *client)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+ if (client == vino_drvdata->decoder.driver) {
+ if (vino_drvdata->decoder.owner != VINO_NO_CHANNEL)
+ ret = -EBUSY;
+ else
+ vino_drvdata->decoder.driver = NULL;
+ } else if (client == vino_drvdata->camera.driver) {
+ if (vino_drvdata->camera.owner != VINO_NO_CHANNEL)
+ ret = -EBUSY;
+ else
+ vino_drvdata->camera.driver = NULL;
+ }
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return ret;
+}
+
+static struct i2c_adapter vino_i2c_adapter =
+{
+ .name = "VINO I2C bus",
+ .id = I2C_HW_SGI_VINO,
+ .algo_data = &i2c_sgi_vino_data,
+ .client_register = &i2c_vino_client_reg,
+ .client_unregister = &i2c_vino_client_unreg,
+};
+
+static int vino_i2c_add_bus(void)
+{
+ return i2c_sgi_add_bus(&vino_i2c_adapter);
+}
+
+static int vino_i2c_del_bus(void)
+{
+ return i2c_sgi_del_bus(&vino_i2c_adapter);
+}
+
+static int i2c_camera_command(unsigned int cmd, void *arg)
+{
+ return vino_drvdata->camera.driver->
+ driver->command(vino_drvdata->camera.driver,
+ cmd, arg);
+}
+
+static int i2c_decoder_command(unsigned int cmd, void *arg)
+{
+ return vino_drvdata->decoder.driver->
+ driver->command(vino_drvdata->decoder.driver,
+ cmd, arg);
+}
+
+/* VINO framebuffer/DMA descriptor management */
+
+static void vino_free_buffer_with_count(struct vino_framebuffer *fb,
+ unsigned int count)
+{
+ unsigned int i;
+
+ dprintk("vino_free_buffer_with_count(): count = %d\n", count);
+
+ for (i = 0; i < count; i++) {
+ ClearPageReserved(virt_to_page(fb->desc_table.virtual[i]));
+ dma_unmap_single(NULL,
+ fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i],
+ PAGE_SIZE, DMA_FROM_DEVICE);
+ free_page(fb->desc_table.virtual[i]);
+ }
+
+ dma_free_coherent(NULL,
+ VINO_PAGE_RATIO * (fb->desc_table.page_count + 4) *
+ sizeof(dma_addr_t), (void *)fb->desc_table.dma_cpu,
+ fb->desc_table.dma);
+ kfree(fb->desc_table.virtual);
+
+ memset(fb, 0, sizeof(struct vino_framebuffer));
+}
+
+static void vino_free_buffer(struct vino_framebuffer *fb)
+{
+ vino_free_buffer_with_count(fb, fb->desc_table.page_count);
+}
+
+static int vino_allocate_buffer(struct vino_framebuffer *fb,
+ unsigned int size)
+{
+ unsigned int count, i, j;
+ int ret = 0;
+
+ dprintk("vino_allocate_buffer():\n");
+
+ if (size < 1)
+ return -EINVAL;
+
+ memset(fb, 0, sizeof(struct vino_framebuffer));
+
+ count = ((size / PAGE_SIZE) + 4) & ~3;
+
+ dprintk("vino_allocate_buffer(): size = %d, count = %d\n",
+ size, count);
+
+ /* allocate memory for table with virtual (page) addresses */
+ fb->desc_table.virtual = (unsigned long *)
+ kmalloc(count * sizeof(unsigned long), GFP_KERNEL);
+ if (!fb->desc_table.virtual)
+ return -ENOMEM;
+
+ /* allocate memory for table with dma addresses
+ * (has space for four extra descriptors) */
+ fb->desc_table.dma_cpu =
+ dma_alloc_coherent(NULL, VINO_PAGE_RATIO * (count + 4) *
+ sizeof(dma_addr_t), &fb->desc_table.dma,
+ GFP_KERNEL | GFP_DMA);
+ if (!fb->desc_table.dma_cpu) {
+ ret = -ENOMEM;
+ goto out_free_virtual;
+ }
+
+ /* allocate pages for the buffer and acquire the according
+ * dma addresses */
+ for (i = 0; i < count; i++) {
+ dma_addr_t dma_data_addr;
+
+ fb->desc_table.virtual[i] =
+ get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!fb->desc_table.virtual[i]) {
+ ret = -ENOBUFS;
+ break;
+ }
+
+ dma_data_addr =
+ dma_map_single(NULL,
+ (void *)fb->desc_table.virtual[i],
+ PAGE_SIZE, DMA_FROM_DEVICE);
+
+ for (j = 0; j < VINO_PAGE_RATIO; j++) {
+ fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i + j] =
+ dma_data_addr + VINO_PAGE_SIZE * j;
+ }
+
+ SetPageReserved(virt_to_page(fb->desc_table.virtual[i]));
+ }
+
+ /* page_count needs to be set anyway, because the descriptor table has
+ * been allocated according to this number */
+ fb->desc_table.page_count = count;
+
+ if (ret) {
+ /* the descriptor with index i doesn't contain
+ * a valid address yet */
+ vino_free_buffer_with_count(fb, i);
+ return ret;
+ }
+
+ //fb->size = size;
+ fb->size = count * PAGE_SIZE;
+ fb->data_format = VINO_DATA_FMT_NONE;
+
+ /* set the dma stop-bit for the last (count+1)th descriptor */
+ fb->desc_table.dma_cpu[VINO_PAGE_RATIO * count] = VINO_DESC_STOP;
+ return 0;
+
+ out_free_virtual:
+ kfree(fb->desc_table.virtual);
+ return ret;
+}
+
+#if 0
+/* user buffers not fully implemented yet */
+static int vino_prepare_user_buffer(struct vino_framebuffer *fb,
+ void *user,
+ unsigned int size)
+{
+ unsigned int count, i, j;
+ int ret = 0;
+
+ dprintk("vino_prepare_user_buffer():\n");
+
+ if (size < 1)
+ return -EINVAL;
+
+ memset(fb, 0, sizeof(struct vino_framebuffer));
+
+ count = ((size / PAGE_SIZE)) & ~3;
+
+ dprintk("vino_prepare_user_buffer(): size = %d, count = %d\n",
+ size, count);
+
+ /* allocate memory for table with virtual (page) addresses */
+ fb->desc_table.virtual = (unsigned long *)
+ kmalloc(count * sizeof(unsigned long), GFP_KERNEL);
+ if (!fb->desc_table.virtual)
+ return -ENOMEM;
+
+ /* allocate memory for table with dma addresses
+ * (has space for four extra descriptors) */
+ fb->desc_table.dma_cpu =
+ dma_alloc_coherent(NULL, VINO_PAGE_RATIO * (count + 4) *
+ sizeof(dma_addr_t), &fb->desc_table.dma,
+ GFP_KERNEL | GFP_DMA);
+ if (!fb->desc_table.dma_cpu) {
+ ret = -ENOMEM;
+ goto out_free_virtual;
+ }
+
+ /* allocate pages for the buffer and acquire the according
+ * dma addresses */
+ for (i = 0; i < count; i++) {
+ dma_addr_t dma_data_addr;
+
+ fb->desc_table.virtual[i] =
+ get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!fb->desc_table.virtual[i]) {
+ ret = -ENOBUFS;
+ break;
+ }
+
+ dma_data_addr =
+ dma_map_single(NULL,
+ (void *)fb->desc_table.virtual[i],
+ PAGE_SIZE, DMA_FROM_DEVICE);
+
+ for (j = 0; j < VINO_PAGE_RATIO; j++) {
+ fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i + j] =
+ dma_data_addr + VINO_PAGE_SIZE * j;
+ }
+
+ SetPageReserved(virt_to_page(fb->desc_table.virtual[i]));
+ }
+
+ /* page_count needs to be set anyway, because the descriptor table has
+ * been allocated according to this number */
+ fb->desc_table.page_count = count;
+
+ if (ret) {
+ /* the descriptor with index i doesn't contain
+ * a valid address yet */
+ vino_free_buffer_with_count(fb, i);
+ return ret;
+ }
+
+ //fb->size = size;
+ fb->size = count * PAGE_SIZE;
+
+ /* set the dma stop-bit for the last (count+1)th descriptor */
+ fb->desc_table.dma_cpu[VINO_PAGE_RATIO * count] = VINO_DESC_STOP;
+ return 0;
+
+ out_free_virtual:
+ kfree(fb->desc_table.virtual);
+ return ret;
+}
+#endif
+
+static void vino_sync_buffer(struct vino_framebuffer *fb)
+{
+ int i;
+
+ dprintk("vino_sync_buffer():\n");
+
+ for (i = 0; i < fb->desc_table.page_count; i++)
+ dma_sync_single(NULL,
+ fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i],
+ PAGE_SIZE, DMA_FROM_DEVICE);
+}
+
+/* Framebuffer fifo functions (need to be locked externally) */
+
+static inline void vino_fifo_init(struct vino_framebuffer_fifo *f,
+ unsigned int length)
+{
+ f->length = 0;
+ f->used = 0;
+ f->head = 0;
+ f->tail = 0;
+
+ if (length > VINO_FRAMEBUFFER_COUNT_MAX)
+ length = VINO_FRAMEBUFFER_COUNT_MAX;
+
+ f->length = length;
+}
+
+/* returns true/false */
+static inline int vino_fifo_has_id(struct vino_framebuffer_fifo *f,
+ unsigned int id)
+{
+ unsigned int i;
+
+ for (i = f->head; i == (f->tail - 1); i = (i + 1) % f->length) {
+ if (f->data[i] == id)
+ return 1;
+ }
+
+ return 0;
+}
+
+#if 0
+/* returns true/false */
+static inline int vino_fifo_full(struct vino_framebuffer_fifo *f)
+{
+ return (f->used == f->length);
+}
+#endif
+
+static inline unsigned int vino_fifo_get_used(struct vino_framebuffer_fifo *f)
+{
+ return f->used;
+}
+
+static int vino_fifo_enqueue(struct vino_framebuffer_fifo *f, unsigned int id)
+{
+ if (id >= f->length) {
+ return VINO_QUEUE_ERROR;
+ }
+
+ if (vino_fifo_has_id(f, id)) {
+ return VINO_QUEUE_ERROR;
+ }
+
+ if (f->used < f->length) {
+ f->data[f->tail] = id;
+ f->tail = (f->tail + 1) % f->length;
+ f->used++;
+ } else {
+ return VINO_QUEUE_ERROR;
+ }
+
+ return 0;
+}
+
+static int vino_fifo_peek(struct vino_framebuffer_fifo *f, unsigned int *id)
+{
+ if (f->used > 0) {
+ *id = f->data[f->head];
+ } else {
+ return VINO_QUEUE_ERROR;
+ }
+
+ return 0;
+}
+
+static int vino_fifo_dequeue(struct vino_framebuffer_fifo *f, unsigned int *id)
+{
+ if (f->used > 0) {
+ *id = f->data[f->head];
+ f->head = (f->head + 1) % f->length;
+ f->used--;
+ } else {
+ return VINO_QUEUE_ERROR;
+ }
+
+ return 0;
+}
+
+/* Framebuffer queue functions */
+
+/* execute with queue_lock locked */
+static void vino_queue_free_with_count(struct vino_framebuffer_queue *q,
+ unsigned int length)
+{
+ unsigned int i;
+
+ q->length = 0;
+ memset(&q->in, 0, sizeof(struct vino_framebuffer_fifo));
+ memset(&q->out, 0, sizeof(struct vino_framebuffer_fifo));
+ for (i = 0; i < length; i++) {
+ dprintk("vino_queue_free_with_count(): freeing buffer %d\n",
+ i);
+ vino_free_buffer(q->buffer[i]);
+ kfree(q->buffer[i]);
+ }
+
+ q->type = VINO_MEMORY_NONE;
+ q->magic = 0;
+}
+
+static void vino_queue_free(struct vino_framebuffer_queue *q)
+{
+ dprintk("vino_queue_free():\n");
+
+ if (q->magic != VINO_QUEUE_MAGIC)
+ return;
+ if (q->type != VINO_MEMORY_MMAP)
+ return;
+
+ mutex_lock(&q->queue_mutex);
+
+ vino_queue_free_with_count(q, q->length);
+
+ mutex_unlock(&q->queue_mutex);
+}
+
+static int vino_queue_init(struct vino_framebuffer_queue *q,
+ unsigned int *length)
+{
+ unsigned int i;
+ int ret = 0;
+
+ dprintk("vino_queue_init(): length = %d\n", *length);
+
+ if (q->magic == VINO_QUEUE_MAGIC) {
+ dprintk("vino_queue_init(): queue already initialized!\n");
+ return -EINVAL;
+ }
+
+ if (q->type != VINO_MEMORY_NONE) {
+ dprintk("vino_queue_init(): queue already initialized!\n");
+ return -EINVAL;
+ }
+
+ if (*length < 1)
+ return -EINVAL;
+
+ mutex_lock(&q->queue_mutex);
+
+ if (*length > VINO_FRAMEBUFFER_COUNT_MAX)
+ *length = VINO_FRAMEBUFFER_COUNT_MAX;
+
+ q->length = 0;
+
+ for (i = 0; i < *length; i++) {
+ dprintk("vino_queue_init(): allocating buffer %d\n", i);
+ q->buffer[i] = kmalloc(sizeof(struct vino_framebuffer),
+ GFP_KERNEL);
+ if (!q->buffer[i]) {
+ dprintk("vino_queue_init(): kmalloc() failed\n");
+ ret = -ENOMEM;
+ break;
+ }
+
+ ret = vino_allocate_buffer(q->buffer[i],
+ VINO_FRAMEBUFFER_SIZE);
+ if (ret) {
+ kfree(q->buffer[i]);
+ dprintk("vino_queue_init(): "
+ "vino_allocate_buffer() failed\n");
+ break;
+ }
+
+ q->buffer[i]->id = i;
+ if (i > 0) {
+ q->buffer[i]->offset = q->buffer[i - 1]->offset +
+ q->buffer[i - 1]->size;
+ } else {
+ q->buffer[i]->offset = 0;
+ }
+
+ spin_lock_init(&q->buffer[i]->state_lock);
+
+ dprintk("vino_queue_init(): buffer = %d, offset = %d, "
+ "size = %d\n", i, q->buffer[i]->offset,
+ q->buffer[i]->size);
+ }
+
+ if (ret) {
+ vino_queue_free_with_count(q, i);
+ *length = 0;
+ } else {
+ q->length = *length;
+ vino_fifo_init(&q->in, q->length);
+ vino_fifo_init(&q->out, q->length);
+ q->type = VINO_MEMORY_MMAP;
+ q->magic = VINO_QUEUE_MAGIC;
+ }
+
+ mutex_unlock(&q->queue_mutex);
+
+ return ret;
+}
+
+static struct vino_framebuffer *vino_queue_add(struct
+ vino_framebuffer_queue *q,
+ unsigned int id)
+{
+ struct vino_framebuffer *ret = NULL;
+ unsigned int total;
+ unsigned long flags;
+
+ dprintk("vino_queue_add(): id = %d\n", id);
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0)
+ goto out;
+
+ if (id >= q->length)
+ goto out;
+
+ /* not needed?: if (vino_fifo_full(&q->out)) {
+ goto out;
+ }*/
+ /* check that outgoing queue isn't already full
+ * (or that it won't become full) */
+ total = vino_fifo_get_used(&q->in) +
+ vino_fifo_get_used(&q->out);
+ if (total >= q->length)
+ goto out;
+
+ if (vino_fifo_enqueue(&q->in, id))
+ goto out;
+
+ ret = q->buffer[id];
+
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+static struct vino_framebuffer *vino_queue_transfer(struct
+ vino_framebuffer_queue *q)
+{
+ struct vino_framebuffer *ret = NULL;
+ struct vino_framebuffer *fb;
+ int id;
+ unsigned long flags;
+
+ dprintk("vino_queue_transfer():\n");
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0)
+ goto out;
+
+ // now this actually removes an entry from the incoming queue
+ if (vino_fifo_dequeue(&q->in, &id)) {
+ goto out;
+ }
+
+ dprintk("vino_queue_transfer(): id = %d\n", id);
+ fb = q->buffer[id];
+
+ // we have already checked that the outgoing queue is not full, but...
+ if (vino_fifo_enqueue(&q->out, id)) {
+ printk(KERN_ERR "vino_queue_transfer(): "
+ "outgoing queue is full, this shouldn't happen!\n");
+ goto out;
+ }
+
+ ret = fb;
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+/* returns true/false */
+static int vino_queue_incoming_contains(struct vino_framebuffer_queue *q,
+ unsigned int id)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0)
+ goto out;
+
+ ret = vino_fifo_has_id(&q->in, id);
+
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+/* returns true/false */
+static int vino_queue_outgoing_contains(struct vino_framebuffer_queue *q,
+ unsigned int id)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0)
+ goto out;
+
+ ret = vino_fifo_has_id(&q->out, id);
+
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+static int vino_queue_get_incoming(struct vino_framebuffer_queue *q,
+ unsigned int *used)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return VINO_QUEUE_ERROR;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0) {
+ ret = VINO_QUEUE_ERROR;
+ goto out;
+ }
+
+ *used = vino_fifo_get_used(&q->in);
+
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+static int vino_queue_get_outgoing(struct vino_framebuffer_queue *q,
+ unsigned int *used)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return VINO_QUEUE_ERROR;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0) {
+ ret = VINO_QUEUE_ERROR;
+ goto out;
+ }
+
+ *used = vino_fifo_get_used(&q->out);
+
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+#if 0
+static int vino_queue_get_total(struct vino_framebuffer_queue *q,
+ unsigned int *total)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return VINO_QUEUE_ERROR;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0) {
+ ret = VINO_QUEUE_ERROR;
+ goto out;
+ }
+
+ *total = vino_fifo_get_used(&q->in) +
+ vino_fifo_get_used(&q->out);
+
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+#endif
+
+static struct vino_framebuffer *vino_queue_peek(struct
+ vino_framebuffer_queue *q,
+ unsigned int *id)
+{
+ struct vino_framebuffer *ret = NULL;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0)
+ goto out;
+
+ if (vino_fifo_peek(&q->in, id)) {
+ goto out;
+ }
+
+ ret = q->buffer[*id];
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+static struct vino_framebuffer *vino_queue_remove(struct
+ vino_framebuffer_queue *q,
+ unsigned int *id)
+{
+ struct vino_framebuffer *ret = NULL;
+ unsigned long flags;
+ dprintk("vino_queue_remove():\n");
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0)
+ goto out;
+
+ if (vino_fifo_dequeue(&q->out, id)) {
+ goto out;
+ }
+
+ dprintk("vino_queue_remove(): id = %d\n", *id);
+ ret = q->buffer[*id];
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+static struct
+vino_framebuffer *vino_queue_get_buffer(struct vino_framebuffer_queue *q,
+ unsigned int id)
+{
+ struct vino_framebuffer *ret = NULL;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0)
+ goto out;
+
+ if (id >= q->length)
+ goto out;
+
+ ret = q->buffer[id];
+ out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+static unsigned int vino_queue_get_length(struct vino_framebuffer_queue *q)
+{
+ unsigned int length = 0;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return length;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+ length = q->length;
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return length;
+}
+
+static int vino_queue_has_mapped_buffers(struct vino_framebuffer_queue *q)
+{
+ unsigned int i;
+ int ret = 0;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+ for (i = 0; i < q->length; i++) {
+ if (q->buffer[i]->map_count > 0) {
+ ret = 1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+/* VINO functions */
+
+/* execute with input_lock locked */
+static void vino_update_line_size(struct vino_channel_settings *vcs)
+{
+ unsigned int w = vcs->clipping.right - vcs->clipping.left;
+ unsigned int d = vcs->decimation;
+ unsigned int bpp = vino_data_formats[vcs->data_format].bpp;
+ unsigned int lsize;
+
+ dprintk("update_line_size(): before: w = %d, d = %d, "
+ "line_size = %d\n", w, d, vcs->line_size);
+
+ /* line size must be multiple of 8 bytes */
+ lsize = (bpp * (w / d)) & ~7;
+ w = (lsize / bpp) * d;
+
+ vcs->clipping.right = vcs->clipping.left + w;
+ vcs->line_size = lsize;
+
+ dprintk("update_line_size(): after: w = %d, d = %d, "
+ "line_size = %d\n", w, d, vcs->line_size);
+}
+
+/* execute with input_lock locked */
+static void vino_set_clipping(struct vino_channel_settings *vcs,
+ unsigned int x, unsigned int y,
+ unsigned int w, unsigned int h)
+{
+ unsigned int maxwidth, maxheight;
+ unsigned int d;
+
+ maxwidth = vino_data_norms[vcs->data_norm].width;
+ maxheight = vino_data_norms[vcs->data_norm].height;
+ d = vcs->decimation;
+
+ y &= ~1; /* odd/even fields */
+
+ if (x > maxwidth) {
+ x = 0;
+ }
+ if (y > maxheight) {
+ y = 0;
+ }
+
+ if (((w / d) < VINO_MIN_WIDTH)
+ || ((h / d) < VINO_MIN_HEIGHT)) {
+ w = VINO_MIN_WIDTH * d;
+ h = VINO_MIN_HEIGHT * d;
+ }
+
+ if ((x + w) > maxwidth) {
+ w = maxwidth - x;
+ if ((w / d) < VINO_MIN_WIDTH)
+ x = maxwidth - VINO_MIN_WIDTH * d;
+ }
+ if ((y + h) > maxheight) {
+ h = maxheight - y;
+ if ((h / d) < VINO_MIN_HEIGHT)
+ y = maxheight - VINO_MIN_HEIGHT * d;
+ }
+
+ vcs->clipping.left = x;
+ vcs->clipping.top = y;
+ vcs->clipping.right = x + w;
+ vcs->clipping.bottom = y + h;
+
+ vino_update_line_size(vcs);
+
+ dprintk("clipping %d, %d, %d, %d / %d - %d\n",
+ vcs->clipping.left, vcs->clipping.top, vcs->clipping.right,
+ vcs->clipping.bottom, vcs->decimation, vcs->line_size);
+}
+
+/* execute with input_lock locked */
+static inline void vino_set_default_clipping(struct vino_channel_settings *vcs)
+{
+ vino_set_clipping(vcs, 0, 0, vino_data_norms[vcs->data_norm].width,
+ vino_data_norms[vcs->data_norm].height);
+}
+
+/* execute with input_lock locked */
+static void vino_set_scaling(struct vino_channel_settings *vcs,
+ unsigned int w, unsigned int h)
+{
+ unsigned int x, y, curw, curh, d;
+
+ x = vcs->clipping.left;
+ y = vcs->clipping.top;
+ curw = vcs->clipping.right - vcs->clipping.left;
+ curh = vcs->clipping.bottom - vcs->clipping.top;
+
+ d = max(curw / w, curh / h);
+
+ dprintk("scaling w: %d, h: %d, curw: %d, curh: %d, d: %d\n",
+ w, h, curw, curh, d);
+
+ if (d < 1) {
+ d = 1;
+ } else if (d > 8) {
+ d = 8;
+ }
+
+ vcs->decimation = d;
+ vino_set_clipping(vcs, x, y, w * d, h * d);
+
+ dprintk("scaling %d, %d, %d, %d / %d - %d\n", vcs->clipping.left,
+ vcs->clipping.top, vcs->clipping.right, vcs->clipping.bottom,
+ vcs->decimation, vcs->line_size);
+}
+
+/* execute with input_lock locked */
+static inline void vino_set_default_scaling(struct vino_channel_settings *vcs)
+{
+ vino_set_scaling(vcs, vcs->clipping.right - vcs->clipping.left,
+ vcs->clipping.bottom - vcs->clipping.top);
+}
+
+/* execute with input_lock locked */
+static void vino_set_framerate(struct vino_channel_settings *vcs,
+ unsigned int fps)
+{
+ unsigned int mask;
+
+ switch (vcs->data_norm) {
+ case VINO_DATA_NORM_NTSC:
+ case VINO_DATA_NORM_D1:
+ fps = (unsigned int)(fps / 6) * 6; // FIXME: round!
+
+ if (fps < vino_data_norms[vcs->data_norm].fps_min)
+ fps = vino_data_norms[vcs->data_norm].fps_min;
+ if (fps > vino_data_norms[vcs->data_norm].fps_max)
+ fps = vino_data_norms[vcs->data_norm].fps_max;
+
+ switch (fps) {
+ case 6:
+ mask = 0x003;
+ break;
+ case 12:
+ mask = 0x0c3;
+ break;
+ case 18:
+ mask = 0x333;
+ break;
+ case 24:
+ mask = 0x3ff;
+ break;
+ case 30:
+ mask = 0xfff;
+ break;
+ default:
+ mask = VINO_FRAMERT_FULL;
+ }
+ vcs->framert_reg = VINO_FRAMERT_RT(mask);
+ break;
+ case VINO_DATA_NORM_PAL:
+ case VINO_DATA_NORM_SECAM:
+ fps = (unsigned int)(fps / 5) * 5; // FIXME: round!
+
+ if (fps < vino_data_norms[vcs->data_norm].fps_min)
+ fps = vino_data_norms[vcs->data_norm].fps_min;
+ if (fps > vino_data_norms[vcs->data_norm].fps_max)
+ fps = vino_data_norms[vcs->data_norm].fps_max;
+
+ switch (fps) {
+ case 5:
+ mask = 0x003;
+ break;
+ case 10:
+ mask = 0x0c3;
+ break;
+ case 15:
+ mask = 0x333;
+ break;
+ case 20:
+ mask = 0x0ff;
+ break;
+ case 25:
+ mask = 0x3ff;
+ break;
+ default:
+ mask = VINO_FRAMERT_FULL;
+ }
+ vcs->framert_reg = VINO_FRAMERT_RT(mask) | VINO_FRAMERT_PAL;
+ break;
+ }
+
+ vcs->fps = fps;
+}
+
+/* execute with input_lock locked */
+static inline void vino_set_default_framerate(struct
+ vino_channel_settings *vcs)
+{
+ vino_set_framerate(vcs, vino_data_norms[vcs->data_norm].fps_max);
+}
+
+/*
+ * Prepare VINO for DMA transfer...
+ * (execute only with vino_lock and input_lock locked)
+ */
+static int vino_dma_setup(struct vino_channel_settings *vcs,
+ struct vino_framebuffer *fb)
+{
+ u32 ctrl, intr;
+ struct sgi_vino_channel *ch;
+ const struct vino_data_norm *norm;
+
+ dprintk("vino_dma_setup():\n");
+
+ vcs->field = 0;
+ fb->frame_counter = 0;
+
+ ch = (vcs->channel == VINO_CHANNEL_A) ? &vino->a : &vino->b;
+ norm = &vino_data_norms[vcs->data_norm];
+
+ ch->page_index = 0;
+ ch->line_count = 0;
+
+ /* VINO line size register is set 8 bytes less than actual */
+ ch->line_size = vcs->line_size - 8;
+
+ /* let VINO know where to transfer data */
+ ch->start_desc_tbl = fb->desc_table.dma;
+ ch->next_4_desc = fb->desc_table.dma;
+
+ /* give vino time to fetch the first four descriptors, 5 usec
+ * should be more than enough time */
+ udelay(VINO_DESC_FETCH_DELAY);
+
+ dprintk("vino_dma_setup(): start desc = %08x, next 4 desc = %08x\n",
+ ch->start_desc_tbl, ch->next_4_desc);
+
+ /* set the alpha register */
+ ch->alpha = vcs->alpha;
+
+ /* set clipping registers */
+ ch->clip_start = VINO_CLIP_ODD(norm->odd.top + vcs->clipping.top / 2) |
+ VINO_CLIP_EVEN(norm->even.top +
+ vcs->clipping.top / 2) |
+ VINO_CLIP_X(vcs->clipping.left);
+ ch->clip_end = VINO_CLIP_ODD(norm->odd.top +
+ vcs->clipping.bottom / 2 - 1) |
+ VINO_CLIP_EVEN(norm->even.top +
+ vcs->clipping.bottom / 2 - 1) |
+ VINO_CLIP_X(vcs->clipping.right);
+
+ /* set the size of actual content in the buffer (DECIMATION !) */
+ fb->data_size = ((vcs->clipping.right - vcs->clipping.left) /
+ vcs->decimation) *
+ ((vcs->clipping.bottom - vcs->clipping.top) /
+ vcs->decimation) *
+ vino_data_formats[vcs->data_format].bpp;
+
+ ch->frame_rate = vcs->framert_reg;
+
+ ctrl = vino->control;
+ intr = vino->intr_status;
+
+ if (vcs->channel == VINO_CHANNEL_A) {
+ /* All interrupt conditions for this channel was cleared
+ * so clear the interrupt status register and enable
+ * interrupts */
+ intr &= ~VINO_INTSTAT_A;
+ ctrl |= VINO_CTRL_A_INT;
+
+ /* enable synchronization */
+ ctrl |= VINO_CTRL_A_SYNC_ENBL;
+
+ /* enable frame assembly */
+ ctrl |= VINO_CTRL_A_INTERLEAVE_ENBL;
+
+ /* set decimation used */
+ if (vcs->decimation < 2)
+ ctrl &= ~VINO_CTRL_A_DEC_ENBL;
+ else {
+ ctrl |= VINO_CTRL_A_DEC_ENBL;
+ ctrl &= ~VINO_CTRL_A_DEC_SCALE_MASK;
+ ctrl |= (vcs->decimation - 1) <<
+ VINO_CTRL_A_DEC_SCALE_SHIFT;
+ }
+
+ /* select input interface */
+ if (vcs->input == VINO_INPUT_D1)
+ ctrl |= VINO_CTRL_A_SELECT;
+ else
+ ctrl &= ~VINO_CTRL_A_SELECT;
+
+ /* palette */
+ ctrl &= ~(VINO_CTRL_A_LUMA_ONLY | VINO_CTRL_A_RGB |
+ VINO_CTRL_A_DITHER);
+ } else {
+ intr &= ~VINO_INTSTAT_B;
+ ctrl |= VINO_CTRL_B_INT;
+
+ ctrl |= VINO_CTRL_B_SYNC_ENBL;
+ ctrl |= VINO_CTRL_B_INTERLEAVE_ENBL;
+
+ if (vcs->decimation < 2)
+ ctrl &= ~VINO_CTRL_B_DEC_ENBL;
+ else {
+ ctrl |= VINO_CTRL_B_DEC_ENBL;
+ ctrl &= ~VINO_CTRL_B_DEC_SCALE_MASK;
+ ctrl |= (vcs->decimation - 1) <<
+ VINO_CTRL_B_DEC_SCALE_SHIFT;
+
+ }
+ if (vcs->input == VINO_INPUT_D1)
+ ctrl |= VINO_CTRL_B_SELECT;
+ else
+ ctrl &= ~VINO_CTRL_B_SELECT;
+
+ ctrl &= ~(VINO_CTRL_B_LUMA_ONLY | VINO_CTRL_B_RGB |
+ VINO_CTRL_B_DITHER);
+ }
+
+ /* set palette */
+ fb->data_format = vcs->data_format;
+
+ switch (vcs->data_format) {
+ case VINO_DATA_FMT_GREY:
+ ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
+ VINO_CTRL_A_LUMA_ONLY : VINO_CTRL_B_LUMA_ONLY;
+ break;
+ case VINO_DATA_FMT_RGB32:
+ ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
+ VINO_CTRL_A_RGB : VINO_CTRL_B_RGB;
+ break;
+ case VINO_DATA_FMT_YUV:
+ /* nothing needs to be done */
+ break;
+ case VINO_DATA_FMT_RGB332:
+ ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
+ VINO_CTRL_A_RGB | VINO_CTRL_A_DITHER :
+ VINO_CTRL_B_RGB | VINO_CTRL_B_DITHER;
+ break;
+ }
+
+ vino->intr_status = intr;
+ vino->control = ctrl;
+
+ return 0;
+}
+
+/* (execute only with vino_lock locked) */
+static inline void vino_dma_start(struct vino_channel_settings *vcs)
+{
+ u32 ctrl = vino->control;
+
+ dprintk("vino_dma_start():\n");
+ ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
+ VINO_CTRL_A_DMA_ENBL : VINO_CTRL_B_DMA_ENBL;
+ vino->control = ctrl;
+}
+
+/* (execute only with vino_lock locked) */
+static inline void vino_dma_stop(struct vino_channel_settings *vcs)
+{
+ u32 ctrl = vino->control;
+
+ ctrl &= (vcs->channel == VINO_CHANNEL_A) ?
+ ~VINO_CTRL_A_DMA_ENBL : ~VINO_CTRL_B_DMA_ENBL;
+ ctrl &= (vcs->channel == VINO_CHANNEL_A) ?
+ ~VINO_CTRL_A_INT : ~VINO_CTRL_B_INT;
+ vino->control = ctrl;
+ dprintk("vino_dma_stop():\n");
+}
+
+/*
+ * Load dummy page to descriptor registers. This prevents generating of
+ * spurious interrupts. (execute only with vino_lock locked)
+ */
+static void vino_clear_interrupt(struct vino_channel_settings *vcs)
+{
+ struct sgi_vino_channel *ch;
+
+ ch = (vcs->channel == VINO_CHANNEL_A) ? &vino->a : &vino->b;
+
+ ch->page_index = 0;
+ ch->line_count = 0;
+
+ ch->start_desc_tbl = vino_drvdata->dummy_desc_table.dma;
+ ch->next_4_desc = vino_drvdata->dummy_desc_table.dma;
+
+ udelay(VINO_DESC_FETCH_DELAY);
+ dprintk("channel %c clear interrupt condition\n",
+ (vcs->channel == VINO_CHANNEL_A) ? 'A':'B');
+}
+
+static int vino_capture(struct vino_channel_settings *vcs,
+ struct vino_framebuffer *fb)
+{
+ int err = 0;
+ unsigned long flags, flags2;
+
+ spin_lock_irqsave(&fb->state_lock, flags);
+
+ if (fb->state == VINO_FRAMEBUFFER_IN_USE)
+ err = -EBUSY;
+ fb->state = VINO_FRAMEBUFFER_IN_USE;
+
+ spin_unlock_irqrestore(&fb->state_lock, flags);
+
+ if (err)
+ return err;
+
+ spin_lock_irqsave(&vino_drvdata->vino_lock, flags);
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags2);
+
+ vino_dma_setup(vcs, fb);
+ vino_dma_start(vcs);
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags2);
+ spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags);
+
+ return err;
+}
+
+static
+struct vino_framebuffer *vino_capture_enqueue(struct
+ vino_channel_settings *vcs,
+ unsigned int index)
+{
+ struct vino_framebuffer *fb;
+ unsigned long flags;
+
+ dprintk("vino_capture_enqueue():\n");
+
+ spin_lock_irqsave(&vcs->capture_lock, flags);
+
+ fb = vino_queue_add(&vcs->fb_queue, index);
+ if (fb == NULL) {
+ dprintk("vino_capture_enqueue(): vino_queue_add() failed, "
+ "queue full?\n");
+ goto out;
+ }
+out:
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+ return fb;
+}
+
+static int vino_capture_next(struct vino_channel_settings *vcs, int start)
+{
+ struct vino_framebuffer *fb;
+ unsigned int incoming, id;
+ int err = 0;
+ unsigned long flags;
+
+ dprintk("vino_capture_next():\n");
+
+ spin_lock_irqsave(&vcs->capture_lock, flags);
+
+ if (start) {
+ /* start capture only if capture isn't in progress already */
+ if (vcs->capturing) {
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+ return 0;
+ }
+
+ } else {
+ /* capture next frame:
+ * stop capture if capturing is not set */
+ if (!vcs->capturing) {
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+ return 0;
+ }
+ }
+
+ err = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
+ if (err) {
+ dprintk("vino_capture_next(): vino_queue_get_incoming() "
+ "failed\n");
+ err = -EINVAL;
+ goto out;
+ }
+ if (incoming == 0) {
+ dprintk("vino_capture_next(): no buffers available\n");
+ goto out;
+ }
+
+ fb = vino_queue_peek(&vcs->fb_queue, &id);
+ if (fb == NULL) {
+ dprintk("vino_capture_next(): vino_queue_peek() failed\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (start) {
+ vcs->capturing = 1;
+ }
+
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+ err = vino_capture(vcs, fb);
+
+ return err;
+
+out:
+ vcs->capturing = 0;
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+ return err;
+}
+
+static inline int vino_is_capturing(struct vino_channel_settings *vcs)
+{
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vcs->capture_lock, flags);
+
+ ret = vcs->capturing;
+
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+ return ret;
+}
+
+/* waits until a frame is captured */
+static int vino_wait_for_frame(struct vino_channel_settings *vcs)
+{
+ wait_queue_t wait;
+ int err = 0;
+
+ dprintk("vino_wait_for_frame():\n");
+
+ init_waitqueue_entry(&wait, current);
+ /* add ourselves into wait queue */
+ add_wait_queue(&vcs->fb_queue.frame_wait_queue, &wait);
+ /* and set current state */
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ /* to ensure that schedule_timeout will return immediately
+ * if VINO interrupt was triggred meanwhile */
+ schedule_timeout(HZ / 10);
+
+ if (signal_pending(current))
+ err = -EINTR;
+
+ remove_wait_queue(&vcs->fb_queue.frame_wait_queue, &wait);
+
+ dprintk("vino_wait_for_frame(): waiting for frame %s\n",
+ err ? "failed" : "ok");
+
+ return err;
+}
+
+/* the function assumes that PAGE_SIZE % 4 == 0 */
+static void vino_convert_to_rgba(struct vino_framebuffer *fb) {
+ unsigned char *pageptr;
+ unsigned int page, i;
+ unsigned char a;
+
+ for (page = 0; page < fb->desc_table.page_count; page++) {
+ pageptr = (unsigned char *)fb->desc_table.virtual[page];
+
+ for (i = 0; i < PAGE_SIZE; i += 4) {
+ a = pageptr[0];
+ pageptr[0] = pageptr[3];
+ pageptr[1] = pageptr[2];
+ pageptr[2] = pageptr[1];
+ pageptr[3] = a;
+ pageptr += 4;
+ }
+ }
+}
+
+/* checks if the buffer is in correct state and syncs data */
+static int vino_check_buffer(struct vino_channel_settings *vcs,
+ struct vino_framebuffer *fb)
+{
+ int err = 0;
+ unsigned long flags;
+
+ dprintk("vino_check_buffer():\n");
+
+ spin_lock_irqsave(&fb->state_lock, flags);
+ switch (fb->state) {
+ case VINO_FRAMEBUFFER_IN_USE:
+ err = -EIO;
+ break;
+ case VINO_FRAMEBUFFER_READY:
+ vino_sync_buffer(fb);
+ fb->state = VINO_FRAMEBUFFER_UNUSED;
+ break;
+ default:
+ err = -EINVAL;
+ }
+ spin_unlock_irqrestore(&fb->state_lock, flags);
+
+ if (!err) {
+ if (vino_pixel_conversion
+ && (fb->data_format == VINO_DATA_FMT_RGB32)) {
+ vino_convert_to_rgba(fb);
+ }
+ } else if (err && (err != -EINVAL)) {
+ dprintk("vino_check_buffer(): buffer not ready\n");
+
+ spin_lock_irqsave(&vino_drvdata->vino_lock, flags);
+ vino_dma_stop(vcs);
+ vino_clear_interrupt(vcs);
+ spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags);
+ }
+
+ return err;
+}
+
+/* forcefully terminates capture */
+static void vino_capture_stop(struct vino_channel_settings *vcs)
+{
+ unsigned int incoming = 0, outgoing = 0, id;
+ unsigned long flags, flags2;
+
+ dprintk("vino_capture_stop():\n");
+
+ spin_lock_irqsave(&vcs->capture_lock, flags);
+
+ /* unset capturing to stop queue processing */
+ vcs->capturing = 0;
+
+ spin_lock_irqsave(&vino_drvdata->vino_lock, flags2);
+
+ vino_dma_stop(vcs);
+ vino_clear_interrupt(vcs);
+
+ spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags2);
+
+ /* remove all items from the queue */
+ if (vino_queue_get_incoming(&vcs->fb_queue, &incoming)) {
+ dprintk("vino_capture_stop(): "
+ "vino_queue_get_incoming() failed\n");
+ goto out;
+ }
+ while (incoming > 0) {
+ vino_queue_transfer(&vcs->fb_queue);
+
+ if (vino_queue_get_incoming(&vcs->fb_queue, &incoming)) {
+ dprintk("vino_capture_stop(): "
+ "vino_queue_get_incoming() failed\n");
+ goto out;
+ }
+ }
+
+ if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
+ dprintk("vino_capture_stop(): "
+ "vino_queue_get_outgoing() failed\n");
+ goto out;
+ }
+ while (outgoing > 0) {
+ vino_queue_remove(&vcs->fb_queue, &id);
+
+ if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
+ dprintk("vino_capture_stop(): "
+ "vino_queue_get_outgoing() failed\n");
+ goto out;
+ }
+ }
+
+out:
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+}
+
+#if 0
+static int vino_capture_failed(struct vino_channel_settings *vcs)
+{
+ struct vino_framebuffer *fb;
+ unsigned long flags;
+ unsigned int i;
+ int ret;
+
+ dprintk("vino_capture_failed():\n");
+
+ spin_lock_irqsave(&vino_drvdata->vino_lock, flags);
+
+ vino_dma_stop(vcs);
+ vino_clear_interrupt(vcs);
+
+ spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags);
+
+ ret = vino_queue_get_incoming(&vcs->fb_queue, &i);
+ if (ret == VINO_QUEUE_ERROR) {
+ dprintk("vino_queue_get_incoming() failed\n");
+ return -EINVAL;
+ }
+ if (i == 0) {
+ /* no buffers to process */
+ return 0;
+ }
+
+ fb = vino_queue_peek(&vcs->fb_queue, &i);
+ if (fb == NULL) {
+ dprintk("vino_queue_peek() failed\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&fb->state_lock, flags);
+ if (fb->state == VINO_FRAMEBUFFER_IN_USE) {
+ fb->state = VINO_FRAMEBUFFER_UNUSED;
+ vino_queue_transfer(&vcs->fb_queue);
+ vino_queue_remove(&vcs->fb_queue, &i);
+ /* we should actually discard the newest frame,
+ * but who cares ... */
+ }
+ spin_unlock_irqrestore(&fb->state_lock, flags);
+
+ return 0;
+}
+#endif
+
+static void vino_skip_frame(struct vino_channel_settings *vcs)
+{
+ struct vino_framebuffer *fb;
+ unsigned long flags;
+ unsigned int id;
+
+ spin_lock_irqsave(&vcs->capture_lock, flags);
+ fb = vino_queue_peek(&vcs->fb_queue, &id);
+ if (!fb) {
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+ dprintk("vino_skip_frame(): vino_queue_peek() failed!\n");
+ return;
+ }
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+ spin_lock_irqsave(&fb->state_lock, flags);
+ fb->state = VINO_FRAMEBUFFER_UNUSED;
+ spin_unlock_irqrestore(&fb->state_lock, flags);
+
+ vino_capture_next(vcs, 0);
+}
+
+static void vino_frame_done(struct vino_channel_settings *vcs)
+{
+ struct vino_framebuffer *fb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vcs->capture_lock, flags);
+ fb = vino_queue_transfer(&vcs->fb_queue);
+ if (!fb) {
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+ dprintk("vino_frame_done(): vino_queue_transfer() failed!\n");
+ return;
+ }
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+ fb->frame_counter = vcs->int_data.frame_counter;
+ memcpy(&fb->timestamp, &vcs->int_data.timestamp,
+ sizeof(struct timeval));
+
+ spin_lock_irqsave(&fb->state_lock, flags);
+ if (fb->state == VINO_FRAMEBUFFER_IN_USE)
+ fb->state = VINO_FRAMEBUFFER_READY;
+ spin_unlock_irqrestore(&fb->state_lock, flags);
+
+ wake_up(&vcs->fb_queue.frame_wait_queue);
+
+ vino_capture_next(vcs, 0);
+}
+
+static void vino_capture_tasklet(unsigned long channel) {
+ struct vino_channel_settings *vcs;
+
+ vcs = (channel == VINO_CHANNEL_A)
+ ? &vino_drvdata->a : &vino_drvdata->b;
+
+ if (vcs->int_data.skip)
+ vcs->int_data.skip_count++;
+
+ if (vcs->int_data.skip && (vcs->int_data.skip_count
+ <= VINO_MAX_FRAME_SKIP_COUNT)) {
+ vino_skip_frame(vcs);
+ } else {
+ vcs->int_data.skip_count = 0;
+ vino_frame_done(vcs);
+ }
+}
+
+static irqreturn_t vino_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ u32 ctrl, intr;
+ unsigned int fc_a, fc_b;
+ int handled_a = 0, skip_a = 0, done_a = 0;
+ int handled_b = 0, skip_b = 0, done_b = 0;
+
+#ifdef VINO_DEBUG_INT
+ int loop = 0;
+ unsigned int line_count = vino->a.line_count,
+ page_index = vino->a.page_index,
+ field_counter = vino->a.field_counter,
+ start_desc_tbl = vino->a.start_desc_tbl,
+ next_4_desc = vino->a.next_4_desc;
+ unsigned int line_count_2,
+ page_index_2,
+ field_counter_2,
+ start_desc_tbl_2,
+ next_4_desc_2;
+#endif
+
+ spin_lock(&vino_drvdata->vino_lock);
+
+ while ((intr = vino->intr_status)) {
+ fc_a = vino->a.field_counter >> 1;
+ fc_b = vino->b.field_counter >> 1;
+
+ /* handle error-interrupts in some special way ?
+ * --> skips frames */
+ if (intr & VINO_INTSTAT_A) {
+ if (intr & VINO_INTSTAT_A_EOF) {
+ vino_drvdata->a.field++;
+ if (vino_drvdata->a.field > 1) {
+ vino_dma_stop(&vino_drvdata->a);
+ vino_clear_interrupt(&vino_drvdata->a);
+ vino_drvdata->a.field = 0;
+ done_a = 1;
+ } else {
+ if (vino->a.page_index
+ != vino_drvdata->a.line_size) {
+ vino->a.line_count = 0;
+ vino->a.page_index =
+ vino_drvdata->
+ a.line_size;
+ vino->a.next_4_desc =
+ vino->a.start_desc_tbl;
+ }
+ }
+ dprintk("channel A end-of-field "
+ "interrupt: %04x\n", intr);
+ } else {
+ vino_dma_stop(&vino_drvdata->a);
+ vino_clear_interrupt(&vino_drvdata->a);
+ vino_drvdata->a.field = 0;
+ skip_a = 1;
+ dprintk("channel A error interrupt: %04x\n",
+ intr);
+ }
+
+#ifdef VINO_DEBUG_INT
+ line_count_2 = vino->a.line_count;
+ page_index_2 = vino->a.page_index;
+ field_counter_2 = vino->a.field_counter;
+ start_desc_tbl_2 = vino->a.start_desc_tbl;
+ next_4_desc_2 = vino->a.next_4_desc;
+
+ printk("intr = %04x, loop = %d, field = %d\n",
+ intr, loop, vino_drvdata->a.field);
+ printk("1- line count = %04d, page index = %04d, "
+ "start = %08x, next = %08x\n"
+ " fieldc = %d, framec = %d\n",
+ line_count, page_index, start_desc_tbl,
+ next_4_desc, field_counter, fc_a);
+ printk("12-line count = %04d, page index = %04d, "
+ " start = %08x, next = %08x\n",
+ line_count_2, page_index_2, start_desc_tbl_2,
+ next_4_desc_2);
+
+ if (done_a)
+ printk("\n");
+#endif
+ }
+
+ if (intr & VINO_INTSTAT_B) {
+ if (intr & VINO_INTSTAT_B_EOF) {
+ vino_drvdata->b.field++;
+ if (vino_drvdata->b.field > 1) {
+ vino_dma_stop(&vino_drvdata->b);
+ vino_clear_interrupt(&vino_drvdata->b);
+ vino_drvdata->b.field = 0;
+ done_b = 1;
+ }
+ dprintk("channel B end-of-field "
+ "interrupt: %04x\n", intr);
+ } else {
+ vino_dma_stop(&vino_drvdata->b);
+ vino_clear_interrupt(&vino_drvdata->b);
+ vino_drvdata->b.field = 0;
+ skip_b = 1;
+ dprintk("channel B error interrupt: %04x\n",
+ intr);
+ }
+ }
+
+ /* Always remember to clear interrupt status.
+ * Disable VINO interrupts while we do this. */
+ ctrl = vino->control;
+ vino->control = ctrl & ~(VINO_CTRL_A_INT | VINO_CTRL_B_INT);
+ vino->intr_status = ~intr;
+ vino->control = ctrl;
+
+ spin_unlock(&vino_drvdata->vino_lock);
+
+ if ((!handled_a) && (done_a || skip_a)) {
+ if (!skip_a) {
+ do_gettimeofday(&vino_drvdata->
+ a.int_data.timestamp);
+ vino_drvdata->a.int_data.frame_counter = fc_a;
+ }
+ vino_drvdata->a.int_data.skip = skip_a;
+
+ dprintk("channel A %s, interrupt: %d\n",
+ skip_a ? "skipping frame" : "frame done",
+ intr);
+ tasklet_hi_schedule(&vino_tasklet_a);
+ handled_a = 1;
+ }
+
+ if ((!handled_b) && (done_b || skip_b)) {
+ if (!skip_b) {
+ do_gettimeofday(&vino_drvdata->
+ b.int_data.timestamp);
+ vino_drvdata->b.int_data.frame_counter = fc_b;
+ }
+ vino_drvdata->b.int_data.skip = skip_b;
+
+ dprintk("channel B %s, interrupt: %d\n",
+ skip_b ? "skipping frame" : "frame done",
+ intr);
+ tasklet_hi_schedule(&vino_tasklet_b);
+ handled_b = 1;
+ }
+
+#ifdef VINO_DEBUG_INT
+ loop++;
+#endif
+ spin_lock(&vino_drvdata->vino_lock);
+ }
+
+ spin_unlock(&vino_drvdata->vino_lock);
+
+ return IRQ_HANDLED;
+}
+
+/* VINO video input management */
+
+static int vino_get_saa7191_input(int input)
+{
+ switch (input) {
+ case VINO_INPUT_COMPOSITE:
+ return SAA7191_INPUT_COMPOSITE;
+ case VINO_INPUT_SVIDEO:
+ return SAA7191_INPUT_SVIDEO;
+ default:
+ printk(KERN_ERR "VINO: vino_get_saa7191_input(): "
+ "invalid input!\n");
+ return -1;
+ }
+}
+
+static int vino_get_saa7191_norm(unsigned int data_norm)
+{
+ switch (data_norm) {
+ case VINO_DATA_NORM_AUTO:
+ return SAA7191_NORM_AUTO;
+ case VINO_DATA_NORM_AUTO_EXT:
+ return SAA7191_NORM_AUTO_EXT;
+ case VINO_DATA_NORM_PAL:
+ return SAA7191_NORM_PAL;
+ case VINO_DATA_NORM_NTSC:
+ return SAA7191_NORM_NTSC;
+ case VINO_DATA_NORM_SECAM:
+ return SAA7191_NORM_SECAM;
+ default:
+ printk(KERN_ERR "VINO: vino_get_saa7191_norm(): "
+ "invalid norm!\n");
+ return -1;
+ }
+}
+
+static int vino_get_from_saa7191_norm(int saa7191_norm)
+{
+ switch (saa7191_norm) {
+ case SAA7191_NORM_PAL:
+ return VINO_DATA_NORM_PAL;
+ case SAA7191_NORM_NTSC:
+ return VINO_DATA_NORM_NTSC;
+ case SAA7191_NORM_SECAM:
+ return VINO_DATA_NORM_SECAM;
+ default:
+ printk(KERN_ERR "VINO: vino_get_from_saa7191_norm(): "
+ "invalid norm!\n");
+ return VINO_DATA_NORM_NONE;
+ }
+}
+
+static int vino_saa7191_set_norm(unsigned int *data_norm)
+{
+ int saa7191_norm, new_data_norm;
+ int err = 0;
+
+ saa7191_norm = vino_get_saa7191_norm(*data_norm);
+
+ err = i2c_decoder_command(DECODER_SAA7191_SET_NORM,
+ &saa7191_norm);
+ if (err)
+ goto out;
+
+ if ((*data_norm == VINO_DATA_NORM_AUTO)
+ || (*data_norm == VINO_DATA_NORM_AUTO_EXT)) {
+ struct saa7191_status status;
+
+ err = i2c_decoder_command(DECODER_SAA7191_GET_STATUS,
+ &status);
+ if (err)
+ goto out;
+
+ new_data_norm =
+ vino_get_from_saa7191_norm(status.norm);
+ if (new_data_norm == VINO_DATA_NORM_NONE) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ *data_norm = (unsigned int)new_data_norm;
+ }
+
+out:
+ return err;
+}
+
+/* execute with input_lock locked */
+static int vino_is_input_owner(struct vino_channel_settings *vcs)
+{
+ switch(vcs->input) {
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO:
+ return (vino_drvdata->decoder.owner == vcs->channel);
+ case VINO_INPUT_D1:
+ return (vino_drvdata->camera.owner == vcs->channel);
+ default:
+ return 0;
+ }
+}
+
+static int vino_acquire_input(struct vino_channel_settings *vcs)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ dprintk("vino_acquire_input():\n");
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ /* First try D1 and then SAA7191 */
+ if (vino_drvdata->camera.driver
+ && (vino_drvdata->camera.owner == VINO_NO_CHANNEL)) {
+ if (i2c_use_client(vino_drvdata->camera.driver)) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ vino_drvdata->camera.owner = vcs->channel;
+ vcs->input = VINO_INPUT_D1;
+ vcs->data_norm = VINO_DATA_NORM_D1;
+ } else if (vino_drvdata->decoder.driver
+ && (vino_drvdata->decoder.owner == VINO_NO_CHANNEL)) {
+ int input, data_norm;
+ int saa7191_input;
+
+ if (i2c_use_client(vino_drvdata->decoder.driver)) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ input = VINO_INPUT_COMPOSITE;
+
+ saa7191_input = vino_get_saa7191_input(input);
+ ret = i2c_decoder_command(DECODER_SET_INPUT,
+ &saa7191_input);
+ if (ret) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ /* Don't hold spinlocks while auto-detecting norm
+ * as it may take a while... */
+
+ data_norm = VINO_DATA_NORM_AUTO_EXT;
+
+ ret = vino_saa7191_set_norm(&data_norm);
+ if ((ret == -EBUSY) || (ret == -EAGAIN)) {
+ data_norm = VINO_DATA_NORM_PAL;
+ ret = vino_saa7191_set_norm(&data_norm);
+ }
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ if (ret) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ vino_drvdata->decoder.owner = vcs->channel;
+
+ vcs->input = input;
+ vcs->data_norm = data_norm;
+ } else {
+ vcs->input = (vcs->channel == VINO_CHANNEL_A) ?
+ vino_drvdata->b.input : vino_drvdata->a.input;
+ vcs->data_norm = (vcs->channel == VINO_CHANNEL_A) ?
+ vino_drvdata->b.data_norm : vino_drvdata->a.data_norm;
+ }
+
+ if (vcs->input == VINO_INPUT_NONE) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ vino_set_default_clipping(vcs);
+ vino_set_default_scaling(vcs);
+ vino_set_default_framerate(vcs);
+
+ dprintk("vino_acquire_input(): %s\n", vino_inputs[vcs->input].name);
+
+out:
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return ret;
+}
+
+static int vino_set_input(struct vino_channel_settings *vcs, int input)
+{
+ struct vino_channel_settings *vcs2 = (vcs->channel == VINO_CHANNEL_A) ?
+ &vino_drvdata->b : &vino_drvdata->a;
+ unsigned long flags;
+ int ret = 0;
+
+ dprintk("vino_set_input():\n");
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ if (vcs->input == input)
+ goto out;
+
+ switch (input) {
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO:
+ if (!vino_drvdata->decoder.driver) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (vino_drvdata->decoder.owner == VINO_NO_CHANNEL) {
+ if (i2c_use_client(vino_drvdata->decoder.driver)) {
+ ret = -ENODEV;
+ goto out;
+ }
+ vino_drvdata->decoder.owner = vcs->channel;
+ }
+
+ if (vino_drvdata->decoder.owner == vcs->channel) {
+ int data_norm;
+ int saa7191_input;
+
+ saa7191_input = vino_get_saa7191_input(input);
+ ret = i2c_decoder_command(DECODER_SET_INPUT,
+ &saa7191_input);
+ if (ret) {
+ vino_drvdata->decoder.owner = VINO_NO_CHANNEL;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ /* Don't hold spinlocks while auto-detecting norm
+ * as it may take a while... */
+
+ data_norm = VINO_DATA_NORM_AUTO_EXT;
+
+ ret = vino_saa7191_set_norm(&data_norm);
+ if ((ret == -EBUSY) || (ret == -EAGAIN)) {
+ data_norm = VINO_DATA_NORM_PAL;
+ ret = vino_saa7191_set_norm(&data_norm);
+ }
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ if (ret) {
+ vino_drvdata->decoder.owner = VINO_NO_CHANNEL;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ vcs->input = input;
+ vcs->data_norm = data_norm;
+ } else {
+ if (input != vcs2->input) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ vcs->input = input;
+ vcs->data_norm = vcs2->data_norm;
+ }
+
+ if (vino_drvdata->camera.owner == vcs->channel) {
+ /* Transfer the ownership or release the input */
+ if (vcs2->input == VINO_INPUT_D1) {
+ vino_drvdata->camera.owner = vcs2->channel;
+ } else {
+ i2c_release_client(vino_drvdata->
+ camera.driver);
+ vino_drvdata->camera.owner = VINO_NO_CHANNEL;
+ }
+ }
+ break;
+ case VINO_INPUT_D1:
+ if (!vino_drvdata->camera.driver) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (vino_drvdata->camera.owner == VINO_NO_CHANNEL) {
+ if (i2c_use_client(vino_drvdata->camera.driver)) {
+ ret = -ENODEV;
+ goto out;
+ }
+ vino_drvdata->camera.owner = vcs->channel;
+ }
+
+ if (vino_drvdata->decoder.owner == vcs->channel) {
+ /* Transfer the ownership or release the input */
+ if ((vcs2->input == VINO_INPUT_COMPOSITE) ||
+ (vcs2->input == VINO_INPUT_SVIDEO)) {
+ vino_drvdata->decoder.owner = vcs2->channel;
+ } else {
+ i2c_release_client(vino_drvdata->
+ decoder.driver);
+ vino_drvdata->decoder.owner = VINO_NO_CHANNEL;
+ }
+ }
+
+ vcs->input = input;
+ vcs->data_norm = VINO_DATA_NORM_D1;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ vino_set_default_clipping(vcs);
+ vino_set_default_scaling(vcs);
+ vino_set_default_framerate(vcs);
+
+ dprintk("vino_set_input(): %s\n", vino_inputs[vcs->input].name);
+
+out:
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return ret;
+}
+
+static void vino_release_input(struct vino_channel_settings *vcs)
+{
+ struct vino_channel_settings *vcs2 = (vcs->channel == VINO_CHANNEL_A) ?
+ &vino_drvdata->b : &vino_drvdata->a;
+ unsigned long flags;
+
+ dprintk("vino_release_input():\n");
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ /* Release ownership of the channel
+ * and if the other channel takes input from
+ * the same source, transfer the ownership */
+ if (vino_drvdata->camera.owner == vcs->channel) {
+ if (vcs2->input == VINO_INPUT_D1) {
+ vino_drvdata->camera.owner = vcs2->channel;
+ } else {
+ i2c_release_client(vino_drvdata->camera.driver);
+ vino_drvdata->camera.owner = VINO_NO_CHANNEL;
+ }
+ } else if (vino_drvdata->decoder.owner == vcs->channel) {
+ if ((vcs2->input == VINO_INPUT_COMPOSITE) ||
+ (vcs2->input == VINO_INPUT_SVIDEO)) {
+ vino_drvdata->decoder.owner = vcs2->channel;
+ } else {
+ i2c_release_client(vino_drvdata->decoder.driver);
+ vino_drvdata->decoder.owner = VINO_NO_CHANNEL;
+ }
+ }
+ vcs->input = VINO_INPUT_NONE;
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+}
+
+/* execute with input_lock locked */
+static int vino_set_data_norm(struct vino_channel_settings *vcs,
+ unsigned int data_norm,
+ unsigned long *flags)
+{
+ int err = 0;
+
+ if (data_norm == vcs->data_norm)
+ return 0;
+
+ switch (vcs->input) {
+ case VINO_INPUT_D1:
+ /* only one "norm" supported */
+ if ((data_norm != VINO_DATA_NORM_D1)
+ && (data_norm != VINO_DATA_NORM_AUTO)
+ && (data_norm != VINO_DATA_NORM_AUTO_EXT))
+ return -EINVAL;
+ break;
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO: {
+ if ((data_norm != VINO_DATA_NORM_PAL)
+ && (data_norm != VINO_DATA_NORM_NTSC)
+ && (data_norm != VINO_DATA_NORM_SECAM)
+ && (data_norm != VINO_DATA_NORM_AUTO)
+ && (data_norm != VINO_DATA_NORM_AUTO_EXT))
+ return -EINVAL;
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, *flags);
+
+ /* Don't hold spinlocks while setting norm
+ * as it may take a while... */
+
+ err = vino_saa7191_set_norm(&data_norm);
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, *flags);
+
+ if (err)
+ goto out;
+
+ vcs->data_norm = data_norm;
+
+ vino_set_default_clipping(vcs);
+ vino_set_default_scaling(vcs);
+ vino_set_default_framerate(vcs);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+out:
+ return err;
+}
+
+/* V4L2 helper functions */
+
+static int vino_find_data_format(__u32 pixelformat)
+{
+ int i;
+
+ for (i = 0; i < VINO_DATA_FMT_COUNT; i++) {
+ if (vino_data_formats[i].pixelformat == pixelformat)
+ return i;
+ }
+
+ return VINO_DATA_FMT_NONE;
+}
+
+static int vino_enum_data_norm(struct vino_channel_settings *vcs, __u32 index)
+{
+ int data_norm = VINO_DATA_NORM_NONE;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+ switch(vcs->input) {
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO:
+ if (index == 0) {
+ data_norm = VINO_DATA_NORM_PAL;
+ } else if (index == 1) {
+ data_norm = VINO_DATA_NORM_NTSC;
+ } else if (index == 2) {
+ data_norm = VINO_DATA_NORM_SECAM;
+ }
+ break;
+ case VINO_INPUT_D1:
+ if (index == 0) {
+ data_norm = VINO_DATA_NORM_D1;
+ }
+ break;
+ }
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return data_norm;
+}
+
+static int vino_enum_input(struct vino_channel_settings *vcs, __u32 index)
+{
+ int input = VINO_INPUT_NONE;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+ if (vino_drvdata->decoder.driver && vino_drvdata->camera.driver) {
+ switch (index) {
+ case 0:
+ input = VINO_INPUT_COMPOSITE;
+ break;
+ case 1:
+ input = VINO_INPUT_SVIDEO;
+ break;
+ case 2:
+ input = VINO_INPUT_D1;
+ break;
+ }
+ } else if (vino_drvdata->decoder.driver) {
+ switch (index) {
+ case 0:
+ input = VINO_INPUT_COMPOSITE;
+ break;
+ case 1:
+ input = VINO_INPUT_SVIDEO;
+ break;
+ }
+ } else if (vino_drvdata->camera.driver) {
+ switch (index) {
+ case 0:
+ input = VINO_INPUT_D1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return input;
+}
+
+/* execute with input_lock locked */
+static __u32 vino_find_input_index(struct vino_channel_settings *vcs)
+{
+ __u32 index = 0;
+ // FIXME: detect when no inputs available
+
+ if (vino_drvdata->decoder.driver && vino_drvdata->camera.driver) {
+ switch (vcs->input) {
+ case VINO_INPUT_COMPOSITE:
+ index = 0;
+ break;
+ case VINO_INPUT_SVIDEO:
+ index = 1;
+ break;
+ case VINO_INPUT_D1:
+ index = 2;
+ break;
+ }
+ } else if (vino_drvdata->decoder.driver) {
+ switch (vcs->input) {
+ case VINO_INPUT_COMPOSITE:
+ index = 0;
+ break;
+ case VINO_INPUT_SVIDEO:
+ index = 1;
+ break;
+ }
+ } else if (vino_drvdata->camera.driver) {
+ switch (vcs->input) {
+ case VINO_INPUT_D1:
+ index = 0;
+ break;
+ }
+ }
+
+ return index;
+}
+
+/* V4L2 ioctls */
+
+static void vino_v4l2_querycap(struct v4l2_capability *cap)
+{
+ memset(cap, 0, sizeof(struct v4l2_capability));
+
+ strcpy(cap->driver, vino_driver_name);
+ strcpy(cap->card, vino_driver_description);
+ strcpy(cap->bus_info, vino_bus_name);
+ cap->version = VINO_VERSION_CODE;
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING;
+ // V4L2_CAP_OVERLAY, V4L2_CAP_READWRITE
+}
+
+static int vino_v4l2_enuminput(struct vino_channel_settings *vcs,
+ struct v4l2_input *i)
+{
+ __u32 index = i->index;
+ int input;
+ dprintk("requested index = %d\n", index);
+
+ input = vino_enum_input(vcs, index);
+ if (input == VINO_INPUT_NONE)
+ return -EINVAL;
+
+ memset(i, 0, sizeof(struct v4l2_input));
+
+ i->index = index;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ i->std = vino_inputs[input].std;
+ strcpy(i->name, vino_inputs[input].name);
+
+ if ((input == VINO_INPUT_COMPOSITE)
+ || (input == VINO_INPUT_SVIDEO)) {
+ struct saa7191_status status;
+ i2c_decoder_command(DECODER_SAA7191_GET_STATUS, &status);
+ i->status |= status.signal ? 0 : V4L2_IN_ST_NO_SIGNAL;
+ i->status |= status.color ? 0 : V4L2_IN_ST_NO_COLOR;
+ }
+
+ return 0;
+}
+
+static int vino_v4l2_g_input(struct vino_channel_settings *vcs,
+ unsigned int *i)
+{
+ __u32 index;
+ int input;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+ input = vcs->input;
+ index = vino_find_input_index(vcs);
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ dprintk("input = %d\n", input);
+
+ if (input == VINO_INPUT_NONE) {
+ return -EINVAL;
+ }
+
+ *i = index;
+
+ return 0;
+}
+
+static int vino_v4l2_s_input(struct vino_channel_settings *vcs,
+ unsigned int *i)
+{
+ int input;
+ dprintk("requested input = %d\n", *i);
+
+ input = vino_enum_input(vcs, *i);
+ if (input == VINO_INPUT_NONE)
+ return -EINVAL;
+
+ return vino_set_input(vcs, input);
+}
+
+static int vino_v4l2_enumstd(struct vino_channel_settings *vcs,
+ struct v4l2_standard *s)
+{
+ int index = s->index;
+ int data_norm;
+
+ data_norm = vino_enum_data_norm(vcs, index);
+ dprintk("standard index = %d\n", index);
+
+ if (data_norm == VINO_DATA_NORM_NONE)
+ return -EINVAL;
+
+ dprintk("standard name = %s\n",
+ vino_data_norms[data_norm].description);
+
+ memset(s, 0, sizeof(struct v4l2_standard));
+ s->index = index;
+
+ s->id = vino_data_norms[data_norm].std;
+ s->frameperiod.numerator = 1;
+ s->frameperiod.denominator =
+ vino_data_norms[data_norm].fps_max;
+ s->framelines =
+ vino_data_norms[data_norm].framelines;
+ strcpy(s->name,
+ vino_data_norms[data_norm].description);
+
+ return 0;
+}
+
+static int vino_v4l2_querystd(struct vino_channel_settings *vcs,
+ v4l2_std_id *std)
+{
+ unsigned long flags;
+ int err = 0;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ switch (vcs->input) {
+ case VINO_INPUT_D1:
+ *std = vino_inputs[vcs->input].std;
+ break;
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO: {
+ struct saa7191_status status;
+
+ i2c_decoder_command(DECODER_SAA7191_GET_STATUS, &status);
+
+ if (status.signal) {
+ if (status.signal_60hz) {
+ *std = V4L2_STD_NTSC;
+ } else {
+ *std = V4L2_STD_PAL | V4L2_STD_SECAM;
+ }
+ } else {
+ *std = vino_inputs[vcs->input].std;
+ }
+ break;
+ }
+ default:
+ err = -EINVAL;
+ }
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return err;
+}
+
+static int vino_v4l2_g_std(struct vino_channel_settings *vcs,
+ v4l2_std_id *std)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ *std = vino_data_norms[vcs->data_norm].std;
+ dprintk("current standard = %d\n", vcs->data_norm);
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return 0;
+}
+
+static int vino_v4l2_s_std(struct vino_channel_settings *vcs,
+ v4l2_std_id *std)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ if (!vino_is_input_owner(vcs)) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* check if the standard is valid for the current input */
+ if ((*std) & vino_inputs[vcs->input].std) {
+ dprintk("standard accepted\n");
+
+ /* change the video norm for SAA7191
+ * and accept NTSC for D1 (do nothing) */
+
+ if (vcs->input == VINO_INPUT_D1)
+ goto out;
+
+ if (((*std) & V4L2_STD_PAL)
+ && ((*std) & V4L2_STD_NTSC)
+ && ((*std) & V4L2_STD_SECAM)) {
+ ret = vino_set_data_norm(vcs, VINO_DATA_NORM_AUTO_EXT,
+ &flags);
+ } else if ((*std) & V4L2_STD_PAL) {
+ ret = vino_set_data_norm(vcs, VINO_DATA_NORM_PAL,
+ &flags);
+ } else if ((*std) & V4L2_STD_NTSC) {
+ ret = vino_set_data_norm(vcs, VINO_DATA_NORM_NTSC,
+ &flags);
+ } else if ((*std) & V4L2_STD_SECAM) {
+ ret = vino_set_data_norm(vcs, VINO_DATA_NORM_SECAM,
+ &flags);
+ } else {
+ ret = -EINVAL;
+ }
+
+ if (ret) {
+ ret = -EINVAL;
+ }
+ } else {
+ ret = -EINVAL;
+ }
+
+out:
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return ret;
+}
+
+static int vino_v4l2_enum_fmt(struct vino_channel_settings *vcs,
+ struct v4l2_fmtdesc *fd)
+{
+ enum v4l2_buf_type type = fd->type;
+ int index = fd->index;
+ dprintk("format index = %d\n", index);
+
+ switch (fd->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if ((fd->index < 0) ||
+ (fd->index >= VINO_DATA_FMT_COUNT))
+ return -EINVAL;
+ dprintk("format name = %s\n",
+ vino_data_formats[index].description);
+
+ memset(fd, 0, sizeof(struct v4l2_fmtdesc));
+ fd->index = index;
+ fd->type = type;
+ fd->pixelformat = vino_data_formats[index].pixelformat;
+ strcpy(fd->description, vino_data_formats[index].description);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vino_v4l2_try_fmt(struct vino_channel_settings *vcs,
+ struct v4l2_format *f)
+{
+ struct vino_channel_settings tempvcs;
+ unsigned long flags;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ struct v4l2_pix_format *pf = &f->fmt.pix;
+
+ dprintk("requested: w = %d, h = %d\n",
+ pf->width, pf->height);
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+ memcpy(&tempvcs, vcs, sizeof(struct vino_channel_settings));
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ tempvcs.data_format = vino_find_data_format(pf->pixelformat);
+ if (tempvcs.data_format == VINO_DATA_FMT_NONE) {
+ tempvcs.data_format = VINO_DATA_FMT_GREY;
+ pf->pixelformat =
+ vino_data_formats[tempvcs.data_format].
+ pixelformat;
+ }
+
+ /* data format must be set before clipping/scaling */
+ vino_set_scaling(&tempvcs, pf->width, pf->height);
+
+ dprintk("data format = %s\n",
+ vino_data_formats[tempvcs.data_format].description);
+
+ pf->width = (tempvcs.clipping.right - tempvcs.clipping.left) /
+ tempvcs.decimation;
+ pf->height = (tempvcs.clipping.bottom - tempvcs.clipping.top) /
+ tempvcs.decimation;
+
+ pf->field = V4L2_FIELD_INTERLACED;
+ pf->bytesperline = tempvcs.line_size;
+ pf->sizeimage = tempvcs.line_size *
+ (tempvcs.clipping.bottom - tempvcs.clipping.top) /
+ tempvcs.decimation;
+ pf->colorspace =
+ vino_data_formats[tempvcs.data_format].colorspace;
+
+ pf->priv = 0;
+ break;
+ }
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vino_v4l2_g_fmt(struct vino_channel_settings *vcs,
+ struct v4l2_format *f)
+{
+ unsigned long flags;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ struct v4l2_pix_format *pf = &f->fmt.pix;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ pf->width = (vcs->clipping.right - vcs->clipping.left) /
+ vcs->decimation;
+ pf->height = (vcs->clipping.bottom - vcs->clipping.top) /
+ vcs->decimation;
+ pf->pixelformat =
+ vino_data_formats[vcs->data_format].pixelformat;
+
+ pf->field = V4L2_FIELD_INTERLACED;
+ pf->bytesperline = vcs->line_size;
+ pf->sizeimage = vcs->line_size *
+ (vcs->clipping.bottom - vcs->clipping.top) /
+ vcs->decimation;
+ pf->colorspace =
+ vino_data_formats[vcs->data_format].colorspace;
+
+ pf->priv = 0;
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+ break;
+ }
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vino_v4l2_s_fmt(struct vino_channel_settings *vcs,
+ struct v4l2_format *f)
+{
+ int data_format;
+ unsigned long flags;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ struct v4l2_pix_format *pf = &f->fmt.pix;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ data_format = vino_find_data_format(pf->pixelformat);
+
+ if (data_format == VINO_DATA_FMT_NONE) {
+ vcs->data_format = VINO_DATA_FMT_GREY;
+ pf->pixelformat =
+ vino_data_formats[vcs->data_format].
+ pixelformat;
+ } else {
+ vcs->data_format = data_format;
+ }
+
+ /* data format must be set before clipping/scaling */
+ vino_set_scaling(vcs, pf->width, pf->height);
+
+ dprintk("data format = %s\n",
+ vino_data_formats[vcs->data_format].description);
+
+ pf->width = vcs->clipping.right - vcs->clipping.left;
+ pf->height = vcs->clipping.bottom - vcs->clipping.top;
+
+ pf->field = V4L2_FIELD_INTERLACED;
+ pf->bytesperline = vcs->line_size;
+ pf->sizeimage = vcs->line_size *
+ (vcs->clipping.bottom - vcs->clipping.top) /
+ vcs->decimation;
+ pf->colorspace =
+ vino_data_formats[vcs->data_format].colorspace;
+
+ pf->priv = 0;
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+ break;
+ }
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vino_v4l2_cropcap(struct vino_channel_settings *vcs,
+ struct v4l2_cropcap *ccap)
+{
+ const struct vino_data_norm *norm;
+ unsigned long flags;
+
+ switch (ccap->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ norm = &vino_data_norms[vcs->data_norm];
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ ccap->bounds.left = 0;
+ ccap->bounds.top = 0;
+ ccap->bounds.width = norm->width;
+ ccap->bounds.height = norm->height;
+ memcpy(&ccap->defrect, &ccap->bounds,
+ sizeof(struct v4l2_rect));
+
+ ccap->pixelaspect.numerator = 1;
+ ccap->pixelaspect.denominator = 1;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vino_v4l2_g_crop(struct vino_channel_settings *vcs,
+ struct v4l2_crop *c)
+{
+ unsigned long flags;
+
+ switch (c->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ c->c.left = vcs->clipping.left;
+ c->c.top = vcs->clipping.top;
+ c->c.width = vcs->clipping.right - vcs->clipping.left;
+ c->c.height = vcs->clipping.bottom - vcs->clipping.top;
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vino_v4l2_s_crop(struct vino_channel_settings *vcs,
+ struct v4l2_crop *c)
+{
+ unsigned long flags;
+
+ switch (c->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ vino_set_clipping(vcs, c->c.left, c->c.top,
+ c->c.width, c->c.height);
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vino_v4l2_g_parm(struct vino_channel_settings *vcs,
+ struct v4l2_streamparm *sp)
+{
+ unsigned long flags;
+
+ switch (sp->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ struct v4l2_captureparm *cp = &sp->parm.capture;
+ memset(cp, 0, sizeof(struct v4l2_captureparm));
+
+ cp->capability = V4L2_CAP_TIMEPERFRAME;
+ cp->timeperframe.numerator = 1;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ cp->timeperframe.denominator = vcs->fps;
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ // TODO: cp->readbuffers = xxx;
+ break;
+ }
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vino_v4l2_s_parm(struct vino_channel_settings *vcs,
+ struct v4l2_streamparm *sp)
+{
+ unsigned long flags;
+
+ switch (sp->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ struct v4l2_captureparm *cp = &sp->parm.capture;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ if ((cp->timeperframe.numerator == 0) ||
+ (cp->timeperframe.denominator == 0)) {
+ /* reset framerate */
+ vino_set_default_framerate(vcs);
+ } else {
+ vino_set_framerate(vcs, cp->timeperframe.denominator /
+ cp->timeperframe.numerator);
+ }
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ // TODO: set buffers according to cp->readbuffers
+ break;
+ }
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vino_v4l2_reqbufs(struct vino_channel_settings *vcs,
+ struct v4l2_requestbuffers *rb)
+{
+ if (vcs->reading)
+ return -EBUSY;
+
+ switch (rb->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ // TODO: check queue type
+ if (rb->memory != V4L2_MEMORY_MMAP) {
+ dprintk("type not mmap\n");
+ return -EINVAL;
+ }
+
+ dprintk("count = %d\n", rb->count);
+ if (rb->count > 0) {
+ if (vino_is_capturing(vcs)) {
+ dprintk("busy, capturing\n");
+ return -EBUSY;
+ }
+
+ if (vino_queue_has_mapped_buffers(&vcs->fb_queue)) {
+ dprintk("busy, buffers still mapped\n");
+ return -EBUSY;
+ } else {
+ vcs->streaming = 0;
+ vino_queue_free(&vcs->fb_queue);
+ vino_queue_init(&vcs->fb_queue, &rb->count);
+ }
+ } else {
+ vcs->streaming = 0;
+ vino_capture_stop(vcs);
+ vino_queue_free(&vcs->fb_queue);
+ }
+ break;
+ }
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void vino_v4l2_get_buffer_status(struct vino_channel_settings *vcs,
+ struct vino_framebuffer *fb,
+ struct v4l2_buffer *b)
+{
+ if (vino_queue_outgoing_contains(&vcs->fb_queue,
+ fb->id)) {
+ b->flags &= ~V4L2_BUF_FLAG_QUEUED;
+ b->flags |= V4L2_BUF_FLAG_DONE;
+ } else if (vino_queue_incoming_contains(&vcs->fb_queue,
+ fb->id)) {
+ b->flags &= ~V4L2_BUF_FLAG_DONE;
+ b->flags |= V4L2_BUF_FLAG_QUEUED;
+ } else {
+ b->flags &= ~(V4L2_BUF_FLAG_DONE |
+ V4L2_BUF_FLAG_QUEUED);
+ }
+
+ b->flags &= ~(V4L2_BUF_FLAG_TIMECODE);
+
+ if (fb->map_count > 0)
+ b->flags |= V4L2_BUF_FLAG_MAPPED;
+
+ b->index = fb->id;
+ b->memory = (vcs->fb_queue.type == VINO_MEMORY_MMAP) ?
+ V4L2_MEMORY_MMAP : V4L2_MEMORY_USERPTR;
+ b->m.offset = fb->offset;
+ b->bytesused = fb->data_size;
+ b->length = fb->size;
+ b->field = V4L2_FIELD_INTERLACED;
+ b->sequence = fb->frame_counter;
+ memcpy(&b->timestamp, &fb->timestamp,
+ sizeof(struct timeval));
+ // b->input ?
+
+ dprintk("buffer %d: length = %d, bytesused = %d, offset = %d\n",
+ fb->id, fb->size, fb->data_size, fb->offset);
+}
+
+static int vino_v4l2_querybuf(struct vino_channel_settings *vcs,
+ struct v4l2_buffer *b)
+{
+ if (vcs->reading)
+ return -EBUSY;
+
+ switch (b->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ struct vino_framebuffer *fb;
+
+ // TODO: check queue type
+ if (b->index >= vino_queue_get_length(&vcs->fb_queue)) {
+ dprintk("invalid index = %d\n",
+ b->index);
+ return -EINVAL;
+ }
+
+ fb = vino_queue_get_buffer(&vcs->fb_queue,
+ b->index);
+ if (fb == NULL) {
+ dprintk("vino_queue_get_buffer() failed");
+ return -EINVAL;
+ }
+
+ vino_v4l2_get_buffer_status(vcs, fb, b);
+ break;
+ }
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vino_v4l2_qbuf(struct vino_channel_settings *vcs,
+ struct v4l2_buffer *b)
+{
+ if (vcs->reading)
+ return -EBUSY;
+
+ switch (b->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ struct vino_framebuffer *fb;
+ int ret;
+
+ // TODO: check queue type
+ if (b->memory != V4L2_MEMORY_MMAP) {
+ dprintk("type not mmap\n");
+ return -EINVAL;
+ }
+
+ fb = vino_capture_enqueue(vcs, b->index);
+ if (fb == NULL)
+ return -EINVAL;
+
+ vino_v4l2_get_buffer_status(vcs, fb, b);
+
+ if (vcs->streaming) {
+ ret = vino_capture_next(vcs, 1);
+ if (ret)
+ return ret;
+ }
+ break;
+ }
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vino_v4l2_dqbuf(struct vino_channel_settings *vcs,
+ struct v4l2_buffer *b,
+ unsigned int nonblocking)
+{
+ if (vcs->reading)
+ return -EBUSY;
+
+ switch (b->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ struct vino_framebuffer *fb;
+ unsigned int incoming, outgoing;
+ int err;
+
+ // TODO: check queue type
+
+ err = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
+ if (err) {
+ dprintk("vino_queue_get_incoming() failed\n");
+ return -EINVAL;
+ }
+ err = vino_queue_get_outgoing(&vcs->fb_queue, &outgoing);
+ if (err) {
+ dprintk("vino_queue_get_outgoing() failed\n");
+ return -EINVAL;
+ }
+
+ dprintk("incoming = %d, outgoing = %d\n", incoming, outgoing);
+
+ if (outgoing == 0) {
+ if (incoming == 0) {
+ dprintk("no incoming or outgoing buffers\n");
+ return -EINVAL;
+ }
+ if (nonblocking) {
+ dprintk("non-blocking I/O was selected and "
+ "there are no buffers to dequeue\n");
+ return -EAGAIN;
+ }
+
+ err = vino_wait_for_frame(vcs);
+ if (err) {
+ err = vino_wait_for_frame(vcs);
+ if (err) {
+ /* interrupted or
+ * no frames captured because
+ * of frame skipping */
+ // vino_capture_failed(vcs);
+ return -EIO;
+ }
+ }
+ }
+
+ fb = vino_queue_remove(&vcs->fb_queue, &b->index);
+ if (fb == NULL) {
+ dprintk("vino_queue_remove() failed\n");
+ return -EINVAL;
+ }
+
+ err = vino_check_buffer(vcs, fb);
+
+ vino_v4l2_get_buffer_status(vcs, fb, b);
+
+ if (err)
+ return -EIO;
+
+ break;
+ }
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vino_v4l2_streamon(struct vino_channel_settings *vcs)
+{
+ unsigned int incoming;
+ int ret;
+ if (vcs->reading)
+ return -EBUSY;
+
+ if (vcs->streaming)
+ return 0;
+
+ // TODO: check queue type
+
+ if (vino_queue_get_length(&vcs->fb_queue) < 1) {
+ dprintk("no buffers allocated\n");
+ return -EINVAL;
+ }
+
+ ret = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
+ if (ret) {
+ dprintk("vino_queue_get_incoming() failed\n");
+ return -EINVAL;
+ }
+
+ vcs->streaming = 1;
+
+ if (incoming > 0) {
+ ret = vino_capture_next(vcs, 1);
+ if (ret) {
+ vcs->streaming = 0;
+
+ dprintk("couldn't start capture\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int vino_v4l2_streamoff(struct vino_channel_settings *vcs)
+{
+ if (vcs->reading)
+ return -EBUSY;
+
+ if (!vcs->streaming)
+ return 0;
+
+ vcs->streaming = 0;
+ vino_capture_stop(vcs);
+
+ return 0;
+}
+
+static int vino_v4l2_queryctrl(struct vino_channel_settings *vcs,
+ struct v4l2_queryctrl *queryctrl)
+{
+ unsigned long flags;
+ int i;
+ int err = 0;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ switch (vcs->input) {
+ case VINO_INPUT_D1:
+ for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
+ if (vino_indycam_v4l2_controls[i].id ==
+ queryctrl->id) {
+ memcpy(queryctrl,
+ &vino_indycam_v4l2_controls[i],
+ sizeof(struct v4l2_queryctrl));
+ queryctrl->reserved[0] = 0;
+ goto found;
+ }
+ }
+
+ err = -EINVAL;
+ break;
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO:
+ for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
+ if (vino_saa7191_v4l2_controls[i].id ==
+ queryctrl->id) {
+ memcpy(queryctrl,
+ &vino_saa7191_v4l2_controls[i],
+ sizeof(struct v4l2_queryctrl));
+ queryctrl->reserved[0] = 0;
+ goto found;
+ }
+ }
+
+ err = -EINVAL;
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ found:
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return err;
+}
+
+static int vino_v4l2_g_ctrl(struct vino_channel_settings *vcs,
+ struct v4l2_control *control)
+{
+ unsigned long flags;
+ int i;
+ int err = 0;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ switch (vcs->input) {
+ case VINO_INPUT_D1: {
+ struct indycam_control indycam_ctrl;
+
+ for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
+ if (vino_indycam_v4l2_controls[i].id ==
+ control->id) {
+ goto found1;
+ }
+ }
+
+ err = -EINVAL;
+ goto out;
+
+found1:
+ indycam_ctrl.type = vino_indycam_v4l2_controls[i].reserved[0];
+
+ err = i2c_camera_command(DECODER_INDYCAM_GET_CONTROL,
+ &indycam_ctrl);
+ if (err) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ control->value = indycam_ctrl.value;
+ break;
+ }
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO: {
+ struct saa7191_control saa7191_ctrl;
+
+ for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
+ if (vino_saa7191_v4l2_controls[i].id ==
+ control->id) {
+ goto found2;
+ }
+ }
+
+ err = -EINVAL;
+ goto out;
+
+found2:
+ saa7191_ctrl.type = vino_saa7191_v4l2_controls[i].reserved[0];
+
+ err = i2c_decoder_command(DECODER_SAA7191_GET_CONTROL,
+ &saa7191_ctrl);
+ if (err) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ control->value = saa7191_ctrl.value;
+ break;
+ }
+ default:
+ err = -EINVAL;
+ }
+
+out:
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return err;
+}
+
+static int vino_v4l2_s_ctrl(struct vino_channel_settings *vcs,
+ struct v4l2_control *control)
+{
+ unsigned long flags;
+ int i;
+ int err = 0;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ if (!vino_is_input_owner(vcs)) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ switch (vcs->input) {
+ case VINO_INPUT_D1: {
+ struct indycam_control indycam_ctrl;
+
+ for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
+ if (vino_indycam_v4l2_controls[i].id ==
+ control->id) {
+ if ((control->value >=
+ vino_indycam_v4l2_controls[i].minimum)
+ && (control->value <=
+ vino_indycam_v4l2_controls[i].
+ maximum)) {
+ goto found1;
+ } else {
+ err = -ERANGE;
+ goto out;
+ }
+ }
+ }
+
+ err = -EINVAL;
+ goto out;
+
+found1:
+ indycam_ctrl.type = vino_indycam_v4l2_controls[i].reserved[0];
+ indycam_ctrl.value = control->value;
+
+ err = i2c_camera_command(DECODER_INDYCAM_SET_CONTROL,
+ &indycam_ctrl);
+ if (err)
+ err = -EINVAL;
+ break;
+ }
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO: {
+ struct saa7191_control saa7191_ctrl;
+
+ for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
+ if (vino_saa7191_v4l2_controls[i].id ==
+ control->id) {
+ if ((control->value >=
+ vino_saa7191_v4l2_controls[i].minimum)
+ && (control->value <=
+ vino_saa7191_v4l2_controls[i].
+ maximum)) {
+ goto found2;
+ } else {
+ err = -ERANGE;
+ goto out;
+ }
+ }
+ }
+ err = -EINVAL;
+ goto out;
+
+found2:
+ saa7191_ctrl.type = vino_saa7191_v4l2_controls[i].reserved[0];
+ saa7191_ctrl.value = control->value;
+
+ err = i2c_decoder_command(DECODER_SAA7191_SET_CONTROL,
+ &saa7191_ctrl);
+ if (err)
+ err = -EINVAL;
+ break;
+ }
+ default:
+ err = -EINVAL;
+ }
+
+out:
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return err;
+}
+
+/* File operations */
+
+static int vino_open(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct vino_channel_settings *vcs = video_get_drvdata(dev);
+ int ret = 0;
+ dprintk("open(): channel = %c\n",
+ (vcs->channel == VINO_CHANNEL_A) ? 'A' : 'B');
+
+ mutex_lock(&vcs->mutex);
+
+ if (vcs->users) {
+ dprintk("open(): driver busy\n");
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ret = vino_acquire_input(vcs);
+ if (ret) {
+ dprintk("open(): vino_acquire_input() failed\n");
+ goto out;
+ }
+
+ vcs->users++;
+
+ out:
+ mutex_unlock(&vcs->mutex);
+
+ dprintk("open(): %s!\n", ret ? "failed" : "complete");
+
+ return ret;
+}
+
+static int vino_close(struct inode *inode, struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct vino_channel_settings *vcs = video_get_drvdata(dev);
+ dprintk("close():\n");
+
+ mutex_lock(&vcs->mutex);
+
+ vcs->users--;
+
+ if (!vcs->users) {
+ vino_release_input(vcs);
+
+ /* stop DMA and free buffers */
+ vino_capture_stop(vcs);
+ vino_queue_free(&vcs->fb_queue);
+ }
+
+ mutex_unlock(&vcs->mutex);
+
+ return 0;
+}
+
+static void vino_vm_open(struct vm_area_struct *vma)
+{
+ struct vino_framebuffer *fb = vma->vm_private_data;
+
+ fb->map_count++;
+ dprintk("vino_vm_open(): count = %d\n", fb->map_count);
+}
+
+static void vino_vm_close(struct vm_area_struct *vma)
+{
+ struct vino_framebuffer *fb = vma->vm_private_data;
+
+ fb->map_count--;
+ dprintk("vino_vm_close(): count = %d\n", fb->map_count);
+}
+
+static struct vm_operations_struct vino_vm_ops = {
+ .open = vino_vm_open,
+ .close = vino_vm_close,
+};
+
+static int vino_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *dev = video_devdata(file);
+ struct vino_channel_settings *vcs = video_get_drvdata(dev);
+
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+
+ struct vino_framebuffer *fb = NULL;
+ unsigned int i, length;
+ int ret = 0;
+
+ dprintk("mmap():\n");
+
+ // TODO: reject mmap if already mapped
+
+ if (mutex_lock_interruptible(&vcs->mutex))
+ return -EINTR;
+
+ if (vcs->reading) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ // TODO: check queue type
+
+ if (!(vma->vm_flags & VM_WRITE)) {
+ dprintk("mmap(): app bug: PROT_WRITE please\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!(vma->vm_flags & VM_SHARED)) {
+ dprintk("mmap(): app bug: MAP_SHARED please\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* find the correct buffer using offset */
+ length = vino_queue_get_length(&vcs->fb_queue);
+ if (length == 0) {
+ dprintk("mmap(): queue not initialized\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < length; i++) {
+ fb = vino_queue_get_buffer(&vcs->fb_queue, i);
+ if (fb == NULL) {
+ dprintk("mmap(): vino_queue_get_buffer() failed\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (fb->offset == offset)
+ goto found;
+ }
+
+ dprintk("mmap(): invalid offset = %lu\n", offset);
+ ret = -EINVAL;
+ goto out;
+
+found:
+ dprintk("mmap(): buffer = %d\n", i);
+
+ if (size > (fb->desc_table.page_count * PAGE_SIZE)) {
+ dprintk("mmap(): failed: size = %lu > %lu\n",
+ size, fb->desc_table.page_count * PAGE_SIZE);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < fb->desc_table.page_count; i++) {
+ unsigned long pfn =
+ virt_to_phys((void *)fb->desc_table.virtual[i]) >>
+ PAGE_SHIFT;
+
+ if (size < PAGE_SIZE)
+ break;
+
+ // protection was: PAGE_READONLY
+ if (remap_pfn_range(vma, start, pfn, PAGE_SIZE,
+ vma->vm_page_prot)) {
+ dprintk("mmap(): remap_pfn_range() failed\n");
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ start += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ fb->map_count = 1;
+
+ vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED;
+ vma->vm_flags &= ~VM_IO;
+ vma->vm_private_data = fb;
+ vma->vm_file = file;
+ vma->vm_ops = &vino_vm_ops;
+
+out:
+ mutex_unlock(&vcs->mutex);
+
+ return ret;
+}
+
+static unsigned int vino_poll(struct file *file, poll_table *pt)
+{
+ struct video_device *dev = video_devdata(file);
+ struct vino_channel_settings *vcs = video_get_drvdata(dev);
+ unsigned int outgoing;
+ unsigned int ret = 0;
+
+ // lock mutex (?)
+ // TODO: this has to be corrected for different read modes
+
+ dprintk("poll():\n");
+
+ if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
+ dprintk("poll(): vino_queue_get_outgoing() failed\n");
+ ret = POLLERR;
+ goto error;
+ }
+ if (outgoing > 0)
+ goto over;
+
+ poll_wait(file, &vcs->fb_queue.frame_wait_queue, pt);
+
+ if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
+ dprintk("poll(): vino_queue_get_outgoing() failed\n");
+ ret = POLLERR;
+ goto error;
+ }
+
+over:
+ dprintk("poll(): data %savailable\n",
+ (outgoing > 0) ? "" : "not ");
+
+ if (outgoing > 0)
+ ret = POLLIN | POLLRDNORM;
+
+error:
+
+ return ret;
+}
+
+static int vino_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct vino_channel_settings *vcs = video_get_drvdata(dev);
+
+#ifdef VINO_DEBUG
+ switch (_IOC_TYPE(cmd)) {
+ case 'v':
+ dprintk("ioctl(): V4L1 unsupported (0x%08x)\n", cmd);
+ break;
+ case 'V':
+ dprintk("ioctl(): V4L2 %s (0x%08x)\n",
+ v4l2_ioctl_names[_IOC_NR(cmd)], cmd);
+ break;
+ default:
+ dprintk("ioctl(): unsupported command 0x%08x\n", cmd);
+ }
+#endif
+
+ switch (cmd) {
+ /* V4L2 interface */
+ case VIDIOC_QUERYCAP: {
+ vino_v4l2_querycap(arg);
+ break;
+ }
+ case VIDIOC_ENUMINPUT: {
+ return vino_v4l2_enuminput(vcs, arg);
+ }
+ case VIDIOC_G_INPUT: {
+ return vino_v4l2_g_input(vcs, arg);
+ }
+ case VIDIOC_S_INPUT: {
+ return vino_v4l2_s_input(vcs, arg);
+ }
+ case VIDIOC_ENUMSTD: {
+ return vino_v4l2_enumstd(vcs, arg);
+ }
+ case VIDIOC_QUERYSTD: {
+ return vino_v4l2_querystd(vcs, arg);
+ }
+ case VIDIOC_G_STD: {
+ return vino_v4l2_g_std(vcs, arg);
+ }
+ case VIDIOC_S_STD: {
+ return vino_v4l2_s_std(vcs, arg);
+ }
+ case VIDIOC_ENUM_FMT: {
+ return vino_v4l2_enum_fmt(vcs, arg);
+ }
+ case VIDIOC_TRY_FMT: {
+ return vino_v4l2_try_fmt(vcs, arg);
+ }
+ case VIDIOC_G_FMT: {
+ return vino_v4l2_g_fmt(vcs, arg);
+ }
+ case VIDIOC_S_FMT: {
+ return vino_v4l2_s_fmt(vcs, arg);
+ }
+ case VIDIOC_CROPCAP: {
+ return vino_v4l2_cropcap(vcs, arg);
+ }
+ case VIDIOC_G_CROP: {
+ return vino_v4l2_g_crop(vcs, arg);
+ }
+ case VIDIOC_S_CROP: {
+ return vino_v4l2_s_crop(vcs, arg);
+ }
+ case VIDIOC_G_PARM: {
+ return vino_v4l2_g_parm(vcs, arg);
+ }
+ case VIDIOC_S_PARM: {
+ return vino_v4l2_s_parm(vcs, arg);
+ }
+ case VIDIOC_REQBUFS: {
+ return vino_v4l2_reqbufs(vcs, arg);
+ }
+ case VIDIOC_QUERYBUF: {
+ return vino_v4l2_querybuf(vcs, arg);
+ }
+ case VIDIOC_QBUF: {
+ return vino_v4l2_qbuf(vcs, arg);
+ }
+ case VIDIOC_DQBUF: {
+ return vino_v4l2_dqbuf(vcs, arg, file->f_flags & O_NONBLOCK);
+ }
+ case VIDIOC_STREAMON: {
+ return vino_v4l2_streamon(vcs);
+ }
+ case VIDIOC_STREAMOFF: {
+ return vino_v4l2_streamoff(vcs);
+ }
+ case VIDIOC_QUERYCTRL: {
+ return vino_v4l2_queryctrl(vcs, arg);
+ }
+ case VIDIOC_G_CTRL: {
+ return vino_v4l2_g_ctrl(vcs, arg);
+ }
+ case VIDIOC_S_CTRL: {
+ return vino_v4l2_s_ctrl(vcs, arg);
+ }
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return 0;
+}
+
+static int vino_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct video_device *dev = video_devdata(file);
+ struct vino_channel_settings *vcs = video_get_drvdata(dev);
+ int ret;
+
+ if (mutex_lock_interruptible(&vcs->mutex))
+ return -EINTR;
+
+ ret = video_usercopy(inode, file, cmd, arg, vino_do_ioctl);
+
+ mutex_unlock(&vcs->mutex);
+
+ return ret;
+}
+
+/* Initialization and cleanup */
+
+// __initdata
+static int vino_init_stage = 0;
+
+static struct file_operations vino_fops = {
+ .owner = THIS_MODULE,
+ .open = vino_open,
+ .release = vino_close,
+ .ioctl = vino_ioctl,
+ .mmap = vino_mmap,
+ .poll = vino_poll,
+ .llseek = no_llseek,
+};
+
+static struct video_device v4l_device_template = {
+ .name = "NOT SET",
+ //.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE |
+ // VID_TYPE_CLIPPING | VID_TYPE_SCALES, VID_TYPE_OVERLAY
+ .hardware = VID_HARDWARE_VINO,
+ .fops = &vino_fops,
+ .minor = -1,
+};
+
+static void vino_module_cleanup(int stage)
+{
+ switch(stage) {
+ case 10:
+ video_unregister_device(vino_drvdata->b.v4l_device);
+ vino_drvdata->b.v4l_device = NULL;
+ case 9:
+ video_unregister_device(vino_drvdata->a.v4l_device);
+ vino_drvdata->a.v4l_device = NULL;
+ case 8:
+ vino_i2c_del_bus();
+ case 7:
+ free_irq(SGI_VINO_IRQ, NULL);
+ case 6:
+ if (vino_drvdata->b.v4l_device) {
+ video_device_release(vino_drvdata->b.v4l_device);
+ vino_drvdata->b.v4l_device = NULL;
+ }
+ case 5:
+ if (vino_drvdata->a.v4l_device) {
+ video_device_release(vino_drvdata->a.v4l_device);
+ vino_drvdata->a.v4l_device = NULL;
+ }
+ case 4:
+ /* all entries in dma_cpu dummy table have the same address */
+ dma_unmap_single(NULL,
+ vino_drvdata->dummy_desc_table.dma_cpu[0],
+ PAGE_SIZE, DMA_FROM_DEVICE);
+ dma_free_coherent(NULL, VINO_DUMMY_DESC_COUNT
+ * sizeof(dma_addr_t),
+ (void *)vino_drvdata->
+ dummy_desc_table.dma_cpu,
+ vino_drvdata->dummy_desc_table.dma);
+ case 3:
+ free_page(vino_drvdata->dummy_page);
+ case 2:
+ kfree(vino_drvdata);
+ case 1:
+ iounmap(vino);
+ case 0:
+ break;
+ default:
+ dprintk("vino_module_cleanup(): invalid cleanup stage = %d\n",
+ stage);
+ }
+}
+
+static int vino_probe(void)
+{
+ unsigned long rev_id;
+
+ if (ip22_is_fullhouse()) {
+ printk(KERN_ERR "VINO doesn't exist in IP22 Fullhouse\n");
+ return -ENODEV;
+ }
+
+ if (!(sgimc->systemid & SGIMC_SYSID_EPRESENT)) {
+ printk(KERN_ERR "VINO is not found (EISA BUS not present)\n");
+ return -ENODEV;
+ }
+
+ vino = (struct sgi_vino *)ioremap(VINO_BASE, sizeof(struct sgi_vino));
+ if (!vino) {
+ printk(KERN_ERR "VINO: ioremap() failed\n");
+ return -EIO;
+ }
+ vino_init_stage++;
+
+ if (get_dbe(rev_id, &(vino->rev_id))) {
+ printk(KERN_ERR "Failed to read VINO revision register\n");
+ vino_module_cleanup(vino_init_stage);
+ return -ENODEV;
+ }
+
+ if (VINO_ID_VALUE(rev_id) != VINO_CHIP_ID) {
+ printk(KERN_ERR "Unknown VINO chip ID (Rev/ID: 0x%02lx)\n",
+ rev_id);
+ vino_module_cleanup(vino_init_stage);
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "VINO revision %ld found\n", VINO_REV_NUM(rev_id));
+
+ return 0;
+}
+
+static int vino_init(void)
+{
+ dma_addr_t dma_dummy_address;
+ int i;
+
+ vino_drvdata = kzalloc(sizeof(struct vino_settings), GFP_KERNEL);
+ if (!vino_drvdata) {
+ vino_module_cleanup(vino_init_stage);
+ return -ENOMEM;
+ }
+ vino_init_stage++;
+
+ /* create a dummy dma descriptor */
+ vino_drvdata->dummy_page = get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!vino_drvdata->dummy_page) {
+ vino_module_cleanup(vino_init_stage);
+ return -ENOMEM;
+ }
+ vino_init_stage++;
+
+ // TODO: use page_count in dummy_desc_table
+
+ vino_drvdata->dummy_desc_table.dma_cpu =
+ dma_alloc_coherent(NULL,
+ VINO_DUMMY_DESC_COUNT * sizeof(dma_addr_t),
+ &vino_drvdata->dummy_desc_table.dma,
+ GFP_KERNEL | GFP_DMA);
+ if (!vino_drvdata->dummy_desc_table.dma_cpu) {
+ vino_module_cleanup(vino_init_stage);
+ return -ENOMEM;
+ }
+ vino_init_stage++;
+
+ dma_dummy_address = dma_map_single(NULL,
+ (void *)vino_drvdata->dummy_page,
+ PAGE_SIZE, DMA_FROM_DEVICE);
+ for (i = 0; i < VINO_DUMMY_DESC_COUNT; i++) {
+ vino_drvdata->dummy_desc_table.dma_cpu[i] = dma_dummy_address;
+ }
+
+ /* initialize VINO */
+
+ vino->control = 0;
+ vino->a.next_4_desc = vino_drvdata->dummy_desc_table.dma;
+ vino->b.next_4_desc = vino_drvdata->dummy_desc_table.dma;
+ udelay(VINO_DESC_FETCH_DELAY);
+
+ vino->intr_status = 0;
+
+ vino->a.fifo_thres = VINO_FIFO_THRESHOLD_DEFAULT;
+ vino->b.fifo_thres = VINO_FIFO_THRESHOLD_DEFAULT;
+
+ return 0;
+}
+
+static int vino_init_channel_settings(struct vino_channel_settings *vcs,
+ unsigned int channel, const char *name)
+{
+ vcs->channel = channel;
+ vcs->input = VINO_INPUT_NONE;
+ vcs->alpha = 0;
+ vcs->users = 0;
+ vcs->data_format = VINO_DATA_FMT_GREY;
+ vcs->data_norm = VINO_DATA_NORM_NTSC;
+ vcs->decimation = 1;
+ vino_set_default_clipping(vcs);
+ vino_set_default_framerate(vcs);
+
+ vcs->capturing = 0;
+
+ mutex_init(&vcs->mutex);
+ spin_lock_init(&vcs->capture_lock);
+
+ mutex_init(&vcs->fb_queue.queue_mutex);
+ spin_lock_init(&vcs->fb_queue.queue_lock);
+ init_waitqueue_head(&vcs->fb_queue.frame_wait_queue);
+
+ vcs->v4l_device = video_device_alloc();
+ if (!vcs->v4l_device) {
+ vino_module_cleanup(vino_init_stage);
+ return -ENOMEM;
+ }
+ vino_init_stage++;
+
+ memcpy(vcs->v4l_device, &v4l_device_template,
+ sizeof(struct video_device));
+ strcpy(vcs->v4l_device->name, name);
+ vcs->v4l_device->release = video_device_release;
+
+ video_set_drvdata(vcs->v4l_device, vcs);
+
+ return 0;
+}
+
+static int __init vino_module_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO "SGI VINO driver version %s\n",
+ VINO_MODULE_VERSION);
+
+ ret = vino_probe();
+ if (ret)
+ return ret;
+
+ ret = vino_init();
+ if (ret)
+ return ret;
+
+ /* initialize data structures */
+
+ spin_lock_init(&vino_drvdata->vino_lock);
+ spin_lock_init(&vino_drvdata->input_lock);
+
+ ret = vino_init_channel_settings(&vino_drvdata->a, VINO_CHANNEL_A,
+ vino_v4l_device_name_a);
+ if (ret)
+ return ret;
+
+ ret = vino_init_channel_settings(&vino_drvdata->b, VINO_CHANNEL_B,
+ vino_v4l_device_name_b);
+ if (ret)
+ return ret;
+
+ /* initialize hardware and register V4L devices */
+
+ ret = request_irq(SGI_VINO_IRQ, vino_interrupt, 0,
+ vino_driver_description, NULL);
+ if (ret) {
+ printk(KERN_ERR "VINO: requesting IRQ %02d failed\n",
+ SGI_VINO_IRQ);
+ vino_module_cleanup(vino_init_stage);
+ return -EAGAIN;
+ }
+ vino_init_stage++;
+
+ ret = vino_i2c_add_bus();
+ if (ret) {
+ printk(KERN_ERR "VINO I2C bus registration failed\n");
+ vino_module_cleanup(vino_init_stage);
+ return ret;
+ }
+ vino_init_stage++;
+
+ ret = video_register_device(vino_drvdata->a.v4l_device,
+ VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ printk(KERN_ERR "VINO channel A Video4Linux-device "
+ "registration failed\n");
+ vino_module_cleanup(vino_init_stage);
+ return -EINVAL;
+ }
+ vino_init_stage++;
+
+ ret = video_register_device(vino_drvdata->b.v4l_device,
+ VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ printk(KERN_ERR "VINO channel B Video4Linux-device "
+ "registration failed\n");
+ vino_module_cleanup(vino_init_stage);
+ return -EINVAL;
+ }
+ vino_init_stage++;
+
+#if defined(CONFIG_KMOD) && defined(MODULE)
+ request_module("saa7191");
+ request_module("indycam");
+#endif
+
+ dprintk("init complete!\n");
+
+ return 0;
+}
+
+static void __exit vino_module_exit(void)
+{
+ dprintk("exiting, stage = %d ...\n", vino_init_stage);
+ vino_module_cleanup(vino_init_stage);
+ dprintk("cleanup complete, exit!\n");
+}
+
+module_init(vino_module_init);
+module_exit(vino_module_exit);
diff --git a/linux/drivers/media/video/vino.h b/linux/drivers/media/video/vino.h
new file mode 100644
index 000000000..de2d615ae
--- /dev/null
+++ b/linux/drivers/media/video/vino.h
@@ -0,0 +1,138 @@
+/*
+ * Driver for the VINO (Video In No Out) system found in SGI Indys.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * Copyright (C) 1999 Ulf Karlsson <ulfc@bun.falkenberg.se>
+ * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ */
+
+#ifndef _VINO_H_
+#define _VINO_H_
+
+#define VINO_BASE 0x00080000 /* Vino is in the EISA address space,
+ * but it is not an EISA bus card */
+#define VINO_PAGE_SIZE 4096
+
+struct sgi_vino_channel {
+ u32 _pad_alpha;
+ volatile u32 alpha;
+
+#define VINO_CLIP_X(x) ((x) & 0x3ff) /* bits 0:9 */
+#define VINO_CLIP_ODD(x) (((x) & 0x1ff) << 10) /* bits 10:18 */
+#define VINO_CLIP_EVEN(x) (((x) & 0x1ff) << 19) /* bits 19:27 */
+ u32 _pad_clip_start;
+ volatile u32 clip_start;
+ u32 _pad_clip_end;
+ volatile u32 clip_end;
+
+#define VINO_FRAMERT_FULL 0xfff
+#define VINO_FRAMERT_PAL (1<<0) /* 0=NTSC 1=PAL */
+#define VINO_FRAMERT_RT(x) (((x) & 0xfff) << 1) /* bits 1:12 */
+ u32 _pad_frame_rate;
+ volatile u32 frame_rate;
+
+ u32 _pad_field_counter;
+ volatile u32 field_counter;
+ u32 _pad_line_size;
+ volatile u32 line_size;
+ u32 _pad_line_count;
+ volatile u32 line_count;
+ u32 _pad_page_index;
+ volatile u32 page_index;
+ u32 _pad_next_4_desc;
+ volatile u32 next_4_desc;
+ u32 _pad_start_desc_tbl;
+ volatile u32 start_desc_tbl;
+
+#define VINO_DESC_JUMP (1<<30)
+#define VINO_DESC_STOP (1<<31)
+#define VINO_DESC_VALID (1<<32)
+ u32 _pad_desc_0;
+ volatile u32 desc_0;
+ u32 _pad_desc_1;
+ volatile u32 desc_1;
+ u32 _pad_desc_2;
+ volatile u32 desc_2;
+ u32 _pad_Bdesc_3;
+ volatile u32 desc_3;
+
+ u32 _pad_fifo_thres;
+ volatile u32 fifo_thres;
+ u32 _pad_fifo_read;
+ volatile u32 fifo_read;
+ u32 _pad_fifo_write;
+ volatile u32 fifo_write;
+};
+
+struct sgi_vino {
+#define VINO_CHIP_ID 0xb
+#define VINO_REV_NUM(x) ((x) & 0x0f)
+#define VINO_ID_VALUE(x) (((x) & 0xf0) >> 4)
+ u32 _pad_rev_id;
+ volatile u32 rev_id;
+
+#define VINO_CTRL_LITTLE_ENDIAN (1<<0)
+#define VINO_CTRL_A_EOF_INT (1<<1) /* Field transferred int */
+#define VINO_CTRL_A_FIFO_INT (1<<2) /* FIFO overflow int */
+#define VINO_CTRL_A_EOD_INT (1<<3) /* End of desc table int */
+#define VINO_CTRL_A_INT (VINO_CTRL_A_EOF_INT | \
+ VINO_CTRL_A_FIFO_INT | \
+ VINO_CTRL_A_EOD_INT)
+#define VINO_CTRL_B_EOF_INT (1<<4) /* Field transferred int */
+#define VINO_CTRL_B_FIFO_INT (1<<5) /* FIFO overflow int */
+#define VINO_CTRL_B_EOD_INT (1<<6) /* End of desc table int */
+#define VINO_CTRL_B_INT (VINO_CTRL_B_EOF_INT | \
+ VINO_CTRL_B_FIFO_INT | \
+ VINO_CTRL_B_EOD_INT)
+#define VINO_CTRL_A_DMA_ENBL (1<<7)
+#define VINO_CTRL_A_INTERLEAVE_ENBL (1<<8)
+#define VINO_CTRL_A_SYNC_ENBL (1<<9)
+#define VINO_CTRL_A_SELECT (1<<10) /* 1=D1 0=Philips */
+#define VINO_CTRL_A_RGB (1<<11) /* 1=RGB 0=YUV */
+#define VINO_CTRL_A_LUMA_ONLY (1<<12)
+#define VINO_CTRL_A_DEC_ENBL (1<<13) /* Decimation */
+#define VINO_CTRL_A_DEC_SCALE_MASK 0x1c000 /* bits 14:17 */
+#define VINO_CTRL_A_DEC_SCALE_SHIFT (14)
+#define VINO_CTRL_A_DEC_HOR_ONLY (1<<17) /* Horizontal only */
+#define VINO_CTRL_A_DITHER (1<<18) /* 24 -> 8 bit dither */
+#define VINO_CTRL_B_DMA_ENBL (1<<19)
+#define VINO_CTRL_B_INTERLEAVE_ENBL (1<<20)
+#define VINO_CTRL_B_SYNC_ENBL (1<<21)
+#define VINO_CTRL_B_SELECT (1<<22) /* 1=D1 0=Philips */
+#define VINO_CTRL_B_RGB (1<<23) /* 1=RGB 0=YUV */
+#define VINO_CTRL_B_LUMA_ONLY (1<<24)
+#define VINO_CTRL_B_DEC_ENBL (1<<25) /* Decimation */
+#define VINO_CTRL_B_DEC_SCALE_MASK 0x1c000000 /* bits 26:28 */
+#define VINO_CTRL_B_DEC_SCALE_SHIFT (26)
+#define VINO_CTRL_B_DEC_HOR_ONLY (1<<29) /* Decimation horizontal only */
+#define VINO_CTRL_B_DITHER (1<<30) /* ChanB 24 -> 8 bit dither */
+ u32 _pad_control;
+ volatile u32 control;
+
+#define VINO_INTSTAT_A_EOF (1<<0) /* Field transferred int */
+#define VINO_INTSTAT_A_FIFO (1<<1) /* FIFO overflow int */
+#define VINO_INTSTAT_A_EOD (1<<2) /* End of desc table int */
+#define VINO_INTSTAT_A (VINO_INTSTAT_A_EOF | \
+ VINO_INTSTAT_A_FIFO | \
+ VINO_INTSTAT_A_EOD)
+#define VINO_INTSTAT_B_EOF (1<<3) /* Field transferred int */
+#define VINO_INTSTAT_B_FIFO (1<<4) /* FIFO overflow int */
+#define VINO_INTSTAT_B_EOD (1<<5) /* End of desc table int */
+#define VINO_INTSTAT_B (VINO_INTSTAT_B_EOF | \
+ VINO_INTSTAT_B_FIFO | \
+ VINO_INTSTAT_B_EOD)
+ u32 _pad_intr_status;
+ volatile u32 intr_status;
+
+ u32 _pad_i2c_control;
+ volatile u32 i2c_control;
+ u32 _pad_i2c_data;
+ volatile u32 i2c_data;
+
+ struct sgi_vino_channel a;
+ struct sgi_vino_channel b;
+};
+
+#endif
diff --git a/linux/drivers/media/video/vpx3220.c b/linux/drivers/media/video/vpx3220.c
new file mode 100644
index 000000000..a34b47326
--- /dev/null
+++ b/linux/drivers/media/video/vpx3220.c
@@ -0,0 +1,749 @@
+/*
+ * vpx3220a, vpx3216b & vpx3214c video decoder driver version 0.0.1
+ *
+ * Copyright (C) 2001 Laurent Pinchart <lpinchart@freegates.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+
+#include <linux/byteorder/swab.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include <linux/i2c.h>
+
+#define I2C_NAME(x) (x)->name
+
+#include "compat.h"
+#include <linux/videodev.h>
+#include <linux/video_decoder.h>
+
+#define I2C_VPX3220 0x86
+#define VPX3220_DEBUG KERN_DEBUG "vpx3220: "
+
+static int debug = 0;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+#define VPX_TIMEOUT_COUNT 10
+
+/* ----------------------------------------------------------------------- */
+
+struct vpx3220 {
+ unsigned char reg[255];
+
+ int norm;
+ int input;
+ int enable;
+ int bright;
+ int contrast;
+ int hue;
+ int sat;
+};
+
+static char *inputs[] = { "internal", "composite", "svideo" };
+
+/* ----------------------------------------------------------------------- */
+static inline int
+vpx3220_write (struct i2c_client *client,
+ u8 reg,
+ u8 value)
+{
+ struct vpx3220 *decoder = i2c_get_clientdata(client);
+
+ decoder->reg[reg] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static inline int
+vpx3220_read (struct i2c_client *client,
+ u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int
+vpx3220_fp_status (struct i2c_client *client)
+{
+ unsigned char status;
+ unsigned int i;
+
+ for (i = 0; i < VPX_TIMEOUT_COUNT; i++) {
+ status = vpx3220_read(client, 0x29);
+
+ if (!(status & 4))
+ return 0;
+
+ udelay(10);
+
+ if (need_resched())
+ cond_resched();
+ }
+
+ return -1;
+}
+
+static int
+vpx3220_fp_write (struct i2c_client *client,
+ u8 fpaddr,
+ u16 data)
+{
+ /* Write the 16-bit address to the FPWR register */
+ if (i2c_smbus_write_word_data(client, 0x27, swab16(fpaddr)) == -1) {
+ dprintk(1, VPX3220_DEBUG "%s: failed\n", __func__);
+ return -1;
+ }
+
+ if (vpx3220_fp_status(client) < 0)
+ return -1;
+
+ /* Write the 16-bit data to the FPDAT register */
+ if (i2c_smbus_write_word_data(client, 0x28, swab16(data)) == -1) {
+ dprintk(1, VPX3220_DEBUG "%s: failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static u16
+vpx3220_fp_read (struct i2c_client *client,
+ u16 fpaddr)
+{
+ s16 data;
+
+ /* Write the 16-bit address to the FPRD register */
+ if (i2c_smbus_write_word_data(client, 0x26, swab16(fpaddr)) == -1) {
+ dprintk(1, VPX3220_DEBUG "%s: failed\n", __func__);
+ return -1;
+ }
+
+ if (vpx3220_fp_status(client) < 0)
+ return -1;
+
+ /* Read the 16-bit data from the FPDAT register */
+ data = i2c_smbus_read_word_data(client, 0x28);
+ if (data == -1) {
+ dprintk(1, VPX3220_DEBUG "%s: failed\n", __func__);
+ return -1;
+ }
+
+ return swab16(data);
+}
+
+static int
+vpx3220_write_block (struct i2c_client *client,
+ const u8 *data,
+ unsigned int len)
+{
+ u8 reg;
+ int ret = -1;
+
+ while (len >= 2) {
+ reg = *data++;
+ if ((ret =
+ vpx3220_write(client, reg, *data++)) < 0)
+ break;
+ len -= 2;
+ }
+
+ return ret;
+}
+
+static int
+vpx3220_write_fp_block (struct i2c_client *client,
+ const u16 *data,
+ unsigned int len)
+{
+ u8 reg;
+ int ret = 0;
+
+ while (len > 1) {
+ reg = *data++;
+ ret |= vpx3220_fp_write(client, reg, *data++);
+ len -= 2;
+ }
+
+ return ret;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static const unsigned short init_ntsc[] = {
+ 0x1c, 0x00, /* NTSC tint angle */
+ 0x88, 17, /* Window 1 vertical */
+ 0x89, 240, /* Vertical lines in */
+ 0x8a, 240, /* Vertical lines out */
+ 0x8b, 000, /* Horizontal begin */
+ 0x8c, 640, /* Horizontal length */
+ 0x8d, 640, /* Number of pixels */
+ 0x8f, 0xc00, /* Disable window 2 */
+ 0xf0, 0x73, /* 13.5 MHz transport, Forced
+ * mode, latch windows */
+ 0xf2, 0x13, /* NTSC M, composite input */
+ 0xe7, 0x1e1, /* Enable vertical standard
+ * locking @ 240 lines */
+};
+
+static const unsigned short init_pal[] = {
+ 0x88, 23, /* Window 1 vertical begin */
+ 0x89, 288, /* Vertical lines in (16 lines
+ * skipped by the VFE) */
+ 0x8a, 288, /* Vertical lines out (16 lines
+ * skipped by the VFE) */
+ 0x8b, 16, /* Horizontal begin */
+ 0x8c, 768, /* Horizontal length */
+ 0x8d, 784, /* Number of pixels
+ * Must be >= Horizontal begin + Horizontal length */
+ 0x8f, 0xc00, /* Disable window 2 */
+ 0xf0, 0x77, /* 13.5 MHz transport, Forced
+ * mode, latch windows */
+ 0xf2, 0x3d1, /* PAL B,G,H,I, composite input */
+ 0xe7, 0x241, /* PAL/SECAM set to 288 lines */
+};
+
+static const unsigned short init_secam[] = {
+ 0x88, 23, /* Window 1 vertical begin */
+ 0x89, 288, /* Vertical lines in (16 lines
+ * skipped by the VFE) */
+ 0x8a, 288, /* Vertical lines out (16 lines
+ * skipped by the VFE) */
+ 0x8b, 16, /* Horizontal begin */
+ 0x8c, 768, /* Horizontal length */
+ 0x8d, 784, /* Number of pixels
+ * Must be >= Horizontal begin + Horizontal length */
+ 0x8f, 0xc00, /* Disable window 2 */
+ 0xf0, 0x77, /* 13.5 MHz transport, Forced
+ * mode, latch windows */
+ 0xf2, 0x3d5, /* SECAM, composite input */
+ 0xe7, 0x241, /* PAL/SECAM set to 288 lines */
+};
+
+static const unsigned char init_common[] = {
+ 0xf2, 0x00, /* Disable all outputs */
+ 0x33, 0x0d, /* Luma : VIN2, Chroma : CIN
+ * (clamp off) */
+ 0xd8, 0xa8, /* HREF/VREF active high, VREF
+ * pulse = 2, Odd/Even flag */
+ 0x20, 0x03, /* IF compensation 0dB/oct */
+ 0xe0, 0xff, /* Open up all comparators */
+ 0xe1, 0x00,
+ 0xe2, 0x7f,
+ 0xe3, 0x80,
+ 0xe4, 0x7f,
+ 0xe5, 0x80,
+ 0xe6, 0x00, /* Brightness set to 0 */
+ 0xe7, 0xe0, /* Contrast to 1.0, noise shaping
+ * 10 to 8 2-bit error diffusion */
+ 0xe8, 0xf8, /* YUV422, CbCr binary offset,
+ * ... (p.32) */
+ 0xea, 0x18, /* LLC2 connected, output FIFO
+ * reset with VACTintern */
+ 0xf0, 0x8a, /* Half full level to 10, bus
+ * shuffler [7:0, 23:16, 15:8] */
+ 0xf1, 0x18, /* Single clock, sync mode, no
+ * FE delay, no HLEN counter */
+ 0xf8, 0x12, /* Port A, PIXCLK, HF# & FE#
+ * strength to 2 */
+ 0xf9, 0x24, /* Port B, HREF, VREF, PREF &
+ * ALPHA strength to 4 */
+};
+
+static const unsigned short init_fp[] = {
+ 0x59, 0,
+ 0xa0, 2070, /* ACC reference */
+ 0xa3, 0,
+ 0xa4, 0,
+ 0xa8, 30,
+ 0xb2, 768,
+ 0xbe, 27,
+ 0x58, 0,
+ 0x26, 0,
+ 0x4b, 0x298, /* PLL gain */
+};
+
+static void
+vpx3220_dump_i2c (struct i2c_client *client)
+{
+ int len = sizeof(init_common);
+ const unsigned char *data = init_common;
+
+ while (len > 1) {
+ dprintk(1,
+ KERN_DEBUG "vpx3216b i2c reg 0x%02x data 0x%02x\n",
+ *data, vpx3220_read(client, *data));
+ data += 2;
+ len -= 2;
+ }
+}
+
+static int
+vpx3220_command (struct i2c_client *client,
+ unsigned int cmd,
+ void *arg)
+{
+ struct vpx3220 *decoder = i2c_get_clientdata(client);
+
+ switch (cmd) {
+ case 0:
+ {
+ vpx3220_write_block(client, init_common,
+ sizeof(init_common));
+ vpx3220_write_fp_block(client, init_fp,
+ sizeof(init_fp) >> 1);
+ switch (decoder->norm) {
+
+ case VIDEO_MODE_NTSC:
+ vpx3220_write_fp_block(client, init_ntsc,
+ sizeof(init_ntsc) >> 1);
+ break;
+
+ case VIDEO_MODE_PAL:
+ vpx3220_write_fp_block(client, init_pal,
+ sizeof(init_pal) >> 1);
+ break;
+ case VIDEO_MODE_SECAM:
+ vpx3220_write_fp_block(client, init_secam,
+ sizeof(init_secam) >> 1);
+ break;
+ default:
+ vpx3220_write_fp_block(client, init_pal,
+ sizeof(init_pal) >> 1);
+ break;
+ }
+ }
+ break;
+
+ case DECODER_DUMP:
+ {
+ vpx3220_dump_i2c(client);
+ }
+ break;
+
+ case DECODER_GET_CAPABILITIES:
+ {
+ struct video_decoder_capability *cap = arg;
+
+ dprintk(1, KERN_DEBUG "%s: DECODER_GET_CAPABILITIES\n",
+ I2C_NAME(client));
+
+ cap->flags = VIDEO_DECODER_PAL |
+ VIDEO_DECODER_NTSC |
+ VIDEO_DECODER_SECAM |
+ VIDEO_DECODER_AUTO |
+ VIDEO_DECODER_CCIR;
+ cap->inputs = 3;
+ cap->outputs = 1;
+ }
+ break;
+
+ case DECODER_GET_STATUS:
+ {
+ int res = 0, status;
+
+ dprintk(1, KERN_INFO "%s: DECODER_GET_STATUS\n",
+ I2C_NAME(client));
+
+ status = vpx3220_fp_read(client, 0x0f3);
+
+ dprintk(1, KERN_INFO "%s: status: 0x%04x\n", I2C_NAME(client),
+ status);
+
+ if (status < 0)
+ return status;
+
+ if ((status & 0x20) == 0) {
+ res |= DECODER_STATUS_GOOD | DECODER_STATUS_COLOR;
+
+ switch (status & 0x18) {
+
+ case 0x00:
+ case 0x10:
+ case 0x14:
+ case 0x18:
+ res |= DECODER_STATUS_PAL;
+ break;
+
+ case 0x08:
+ res |= DECODER_STATUS_SECAM;
+ break;
+
+ case 0x04:
+ case 0x0c:
+ case 0x1c:
+ res |= DECODER_STATUS_NTSC;
+ break;
+ }
+ }
+
+ *(int *) arg = res;
+ }
+ break;
+
+ case DECODER_SET_NORM:
+ {
+ int *iarg = arg, data;
+ int temp_input;
+
+ /* Here we back up the input selection because it gets
+ overwritten when we fill the registers with the
+ choosen video norm */
+ temp_input = vpx3220_fp_read(client, 0xf2);
+
+ dprintk(1, KERN_DEBUG "%s: DECODER_SET_NORM %d\n",
+ I2C_NAME(client), *iarg);
+ switch (*iarg) {
+
+ case VIDEO_MODE_NTSC:
+ vpx3220_write_fp_block(client, init_ntsc,
+ sizeof(init_ntsc) >> 1);
+ dprintk(1, KERN_INFO "%s: norm switched to NTSC\n",
+ I2C_NAME(client));
+ break;
+
+ case VIDEO_MODE_PAL:
+ vpx3220_write_fp_block(client, init_pal,
+ sizeof(init_pal) >> 1);
+ dprintk(1, KERN_INFO "%s: norm switched to PAL\n",
+ I2C_NAME(client));
+ break;
+
+ case VIDEO_MODE_SECAM:
+ vpx3220_write_fp_block(client, init_secam,
+ sizeof(init_secam) >> 1);
+ dprintk(1, KERN_INFO "%s: norm switched to SECAM\n",
+ I2C_NAME(client));
+ break;
+
+ case VIDEO_MODE_AUTO:
+ /* FIXME This is only preliminary support */
+ data = vpx3220_fp_read(client, 0xf2) & 0x20;
+ vpx3220_fp_write(client, 0xf2, 0x00c0 | data);
+ dprintk(1, KERN_INFO "%s: norm switched to Auto\n",
+ I2C_NAME(client));
+ break;
+
+ default:
+ return -EINVAL;
+
+ }
+ decoder->norm = *iarg;
+
+ /* And here we set the backed up video input again */
+ vpx3220_fp_write(client, 0xf2, temp_input | 0x0010);
+ udelay(10);
+ }
+ break;
+
+ case DECODER_SET_INPUT:
+ {
+ int *iarg = arg, data;
+
+ /* RJ: *iarg = 0: ST8 (PCTV) input
+ *iarg = 1: COMPOSITE input
+ *iarg = 2: SVHS input */
+
+ const int input[3][2] = {
+ {0x0c, 0},
+ {0x0d, 0},
+ {0x0e, 1}
+ };
+
+ if (*iarg < 0 || *iarg > 2)
+ return -EINVAL;
+
+ dprintk(1, KERN_INFO "%s: input switched to %s\n",
+ I2C_NAME(client), inputs[*iarg]);
+
+ vpx3220_write(client, 0x33, input[*iarg][0]);
+
+ data = vpx3220_fp_read(client, 0xf2) & ~(0x0020);
+ if (data < 0)
+ return data;
+ /* 0x0010 is required to latch the setting */
+ vpx3220_fp_write(client, 0xf2,
+ data | (input[*iarg][1] << 5) | 0x0010);
+
+ udelay(10);
+ }
+ break;
+
+ case DECODER_SET_OUTPUT:
+ {
+ int *iarg = arg;
+
+ /* not much choice of outputs */
+ if (*iarg != 0) {
+ return -EINVAL;
+ }
+ }
+ break;
+
+ case DECODER_ENABLE_OUTPUT:
+ {
+ int *iarg = arg;
+
+ dprintk(1, KERN_DEBUG "%s: DECODER_ENABLE_OUTPUT %d\n",
+ I2C_NAME(client), *iarg);
+
+ vpx3220_write(client, 0xf2, (*iarg ? 0x1b : 0x00));
+ }
+ break;
+
+ case DECODER_SET_PICTURE:
+ {
+ struct video_picture *pic = arg;
+
+ if (decoder->bright != pic->brightness) {
+ /* We want -128 to 128 we get 0-65535 */
+ decoder->bright = pic->brightness;
+ vpx3220_write(client, 0xe6,
+ (decoder->bright - 32768) >> 8);
+ }
+ if (decoder->contrast != pic->contrast) {
+ /* We want 0 to 64 we get 0-65535 */
+ /* Bit 7 and 8 is for noise shaping */
+ decoder->contrast = pic->contrast;
+ vpx3220_write(client, 0xe7,
+ (decoder->contrast >> 10) + 192);
+ }
+ if (decoder->sat != pic->colour) {
+ /* We want 0 to 4096 we get 0-65535 */
+ decoder->sat = pic->colour;
+ vpx3220_fp_write(client, 0xa0,
+ decoder->sat >> 4);
+ }
+ if (decoder->hue != pic->hue) {
+ /* We want -512 to 512 we get 0-65535 */
+ decoder->hue = pic->hue;
+ vpx3220_fp_write(client, 0x1c,
+ ((decoder->hue - 32768) >> 6) & 0xFFF);
+ }
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+vpx3220_init_client (struct i2c_client *client)
+{
+ vpx3220_write_block(client, init_common, sizeof(init_common));
+ vpx3220_write_fp_block(client, init_fp, sizeof(init_fp) >> 1);
+ /* Default to PAL */
+ vpx3220_write_fp_block(client, init_pal, sizeof(init_pal) >> 1);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------
+ * Client managment code
+ */
+
+/*
+ * Generic i2c probe
+ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+ */
+static unsigned short normal_i2c[] =
+ { I2C_VPX3220 >> 1, (I2C_VPX3220 >> 1) + 4,
+ I2C_CLIENT_END
+};
+
+static unsigned short ignore = I2C_CLIENT_END;
+
+static struct i2c_client_address_data addr_data = {
+ .normal_i2c = normal_i2c,
+ .probe = &ignore,
+ .ignore = &ignore,
+};
+
+static struct i2c_driver vpx3220_i2c_driver;
+
+static int
+vpx3220_detach_client (struct i2c_client *client)
+{
+ struct vpx3220 *decoder = i2c_get_clientdata(client);
+ int err;
+
+ err = i2c_detach_client(client);
+ if (err) {
+ return err;
+ }
+
+ kfree(decoder);
+ kfree(client);
+
+ return 0;
+}
+
+static int
+vpx3220_detect_client (struct i2c_adapter *adapter,
+ int address,
+ int kind)
+{
+ int err;
+ struct i2c_client *client;
+ struct vpx3220 *decoder;
+
+ dprintk(1, VPX3220_DEBUG "%s\n", __func__);
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality
+ (adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
+ return 0;
+
+ client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+ if (client == NULL) {
+ return -ENOMEM;
+ }
+
+ client->addr = address;
+ client->adapter = adapter;
+ client->driver = &vpx3220_i2c_driver;
+
+ /* Check for manufacture ID and part number */
+ if (kind < 0) {
+ u8 id;
+ u16 pn;
+
+ id = vpx3220_read(client, 0x00);
+ if (id != 0xec) {
+ dprintk(1,
+ KERN_INFO
+ "vpx3220_attach: Wrong manufacturer ID (0x%02x)\n",
+ id);
+ kfree(client);
+ return 0;
+ }
+
+ pn = (vpx3220_read(client, 0x02) << 8) +
+ vpx3220_read(client, 0x01);
+ switch (pn) {
+ case 0x4680:
+ strlcpy(I2C_NAME(client), "vpx3220a",
+ sizeof(I2C_NAME(client)));
+ break;
+ case 0x4260:
+ strlcpy(I2C_NAME(client), "vpx3216b",
+ sizeof(I2C_NAME(client)));
+ break;
+ case 0x4280:
+ strlcpy(I2C_NAME(client), "vpx3214c",
+ sizeof(I2C_NAME(client)));
+ break;
+ default:
+ dprintk(1,
+ KERN_INFO
+ "%s: Wrong part number (0x%04x)\n",
+ __func__, pn);
+ kfree(client);
+ return 0;
+ }
+ } else {
+ strlcpy(I2C_NAME(client), "forced vpx32xx",
+ sizeof(I2C_NAME(client)));
+ }
+
+ decoder = kzalloc(sizeof(struct vpx3220), GFP_KERNEL);
+ if (decoder == NULL) {
+ kfree(client);
+ return -ENOMEM;
+ }
+ decoder->norm = VIDEO_MODE_PAL;
+ decoder->input = 0;
+ decoder->enable = 1;
+ decoder->bright = 32768;
+ decoder->contrast = 32768;
+ decoder->hue = 32768;
+ decoder->sat = 32768;
+ i2c_set_clientdata(client, decoder);
+
+ err = i2c_attach_client(client);
+ if (err) {
+ kfree(client);
+ kfree(decoder);
+ return err;
+ }
+
+ dprintk(1, KERN_INFO "%s: vpx32xx client found at address 0x%02x\n",
+ I2C_NAME(client), client->addr << 1);
+
+ vpx3220_init_client(client);
+
+ return 0;
+}
+
+static int
+vpx3220_attach_adapter (struct i2c_adapter *adapter)
+{
+ int ret;
+
+ ret = i2c_probe(adapter, &addr_data, &vpx3220_detect_client);
+ dprintk(1, VPX3220_DEBUG "%s: i2c_probe returned %d\n",
+ __func__, ret);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------
+ * Driver initialization and cleanup code
+ */
+
+static struct i2c_driver vpx3220_i2c_driver = {
+ .driver = {
+ .name = "vpx3220",
+ },
+
+ .id = I2C_DRIVERID_VPX3220,
+
+ .attach_adapter = vpx3220_attach_adapter,
+ .detach_client = vpx3220_detach_client,
+ .command = vpx3220_command,
+};
+
+static int __init
+vpx3220_init (void)
+{
+ return i2c_add_driver(&vpx3220_i2c_driver);
+}
+
+static void __exit
+vpx3220_cleanup (void)
+{
+ i2c_del_driver(&vpx3220_i2c_driver);
+}
+
+module_init(vpx3220_init);
+module_exit(vpx3220_cleanup);
+
+MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video encoder driver");
+MODULE_AUTHOR("Laurent Pinchart");
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/video/w9966.c b/linux/drivers/media/video/w9966.c
new file mode 100644
index 000000000..5aea29c69
--- /dev/null
+++ b/linux/drivers/media/video/w9966.c
@@ -0,0 +1,986 @@
+/*
+ Winbond w9966cf Webcam parport driver.
+
+ Version 0.32
+
+ Copyright (C) 2001 Jakob Kemi <jakob.kemi@post.utfors.se>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+ Supported devices:
+ *Lifeview FlyCam Supra (using the Philips saa7111a chip)
+
+ Does any other model using the w9966 interface chip exist ?
+
+ Todo:
+
+ *Add a working EPP mode, since DMA ECP read isn't implemented
+ in the parport drivers. (That's why it's so sloow)
+
+ *Add support for other ccd-control chips than the saa7111
+ please send me feedback on what kind of chips you have.
+
+ *Add proper probing. I don't know what's wrong with the IEEE1284
+ parport drivers but (IEEE1284_MODE_NIBBLE|IEEE1284_DEVICE_ID)
+ and nibble read seems to be broken for some peripherals.
+
+ *Add probing for onboard SRAM, port directions etc. (if possible)
+
+ *Add support for the hardware compressed modes (maybe using v4l2)
+
+ *Fix better support for the capture window (no skewed images, v4l
+ interface to capt. window)
+
+ *Probably some bugs that I don't know of
+
+ Please support me by sending feedback!
+
+ Changes:
+
+ Alan Cox: Removed RGB mode for kernel merge, added THIS_MODULE
+ and owner support for newer module locks
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include "compat.h"
+#include <linux/videodev.h>
+#include <linux/parport.h>
+
+//#define DEBUG // Undef me for production
+
+#ifdef DEBUG
+#define DPRINTF(x, a...) printk(KERN_DEBUG "W9966: %s(): "x, __FUNCTION__ , ##a)
+#else
+#define DPRINTF(x...)
+#endif
+
+/*
+ * Defines, simple typedefs etc.
+ */
+
+#define W9966_DRIVERNAME "W9966CF Webcam"
+#define W9966_MAXCAMS 4 // Maximum number of cameras
+#define W9966_RBUFFER 2048 // Read buffer (must be an even number)
+#define W9966_SRAMSIZE 131072 // 128kb
+#define W9966_SRAMID 0x02 // check w9966cf.pdf
+
+// Empirically determined window limits
+#define W9966_WND_MIN_X 16
+#define W9966_WND_MIN_Y 14
+#define W9966_WND_MAX_X 705
+#define W9966_WND_MAX_Y 253
+#define W9966_WND_MAX_W (W9966_WND_MAX_X - W9966_WND_MIN_X)
+#define W9966_WND_MAX_H (W9966_WND_MAX_Y - W9966_WND_MIN_Y)
+
+// Keep track of our current state
+#define W9966_STATE_PDEV 0x01
+#define W9966_STATE_CLAIMED 0x02
+#define W9966_STATE_VDEV 0x04
+
+#define W9966_I2C_W_ID 0x48
+#define W9966_I2C_R_ID 0x49
+#define W9966_I2C_R_DATA 0x08
+#define W9966_I2C_R_CLOCK 0x04
+#define W9966_I2C_W_DATA 0x02
+#define W9966_I2C_W_CLOCK 0x01
+
+struct w9966_dev {
+ unsigned char dev_state;
+ unsigned char i2c_state;
+ unsigned short ppmode;
+ struct parport* pport;
+ struct pardevice* pdev;
+ struct video_device vdev;
+ unsigned short width;
+ unsigned short height;
+ unsigned char brightness;
+ signed char contrast;
+ signed char color;
+ signed char hue;
+};
+
+/*
+ * Module specific properties
+ */
+
+MODULE_AUTHOR("Jakob Kemi <jakob.kemi@post.utfors.se>");
+MODULE_DESCRIPTION("Winbond w9966cf WebCam driver (0.32)");
+MODULE_LICENSE("GPL");
+
+
+#ifdef MODULE
+static const char* pardev[] = {[0 ... W9966_MAXCAMS] = ""};
+#else
+static const char* pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"};
+#endif
+module_param_array(pardev, charp, NULL, 0);
+MODULE_PARM_DESC(pardev, "pardev: where to search for\n\
+\teach camera. 'aggressive' means brute-force search.\n\
+\tEg: >pardev=parport3,aggressive,parport2,parport1< would assign\n\
+\tcam 1 to parport3 and search every parport for cam 2 etc...");
+
+static int parmode = 0;
+module_param(parmode, int, 0);
+MODULE_PARM_DESC(parmode, "parmode: transfer mode (0=auto, 1=ecp, 2=epp");
+
+static int video_nr = -1;
+module_param(video_nr, int, 0);
+
+/*
+ * Private data
+ */
+
+static struct w9966_dev w9966_cams[W9966_MAXCAMS];
+
+/*
+ * Private function declares
+ */
+
+static inline void w9966_setState(struct w9966_dev* cam, int mask, int val);
+static inline int w9966_getState(struct w9966_dev* cam, int mask, int val);
+static inline void w9966_pdev_claim(struct w9966_dev *vdev);
+static inline void w9966_pdev_release(struct w9966_dev *vdev);
+
+static int w9966_rReg(struct w9966_dev* cam, int reg);
+static int w9966_wReg(struct w9966_dev* cam, int reg, int data);
+#if 0
+static int w9966_rReg_i2c(struct w9966_dev* cam, int reg);
+#endif
+static int w9966_wReg_i2c(struct w9966_dev* cam, int reg, int data);
+static int w9966_findlen(int near, int size, int maxlen);
+static int w9966_calcscale(int size, int min, int max, int* beg, int* end, unsigned char* factor);
+static int w9966_setup(struct w9966_dev* cam, int x1, int y1, int x2, int y2, int w, int h);
+
+static int w9966_init(struct w9966_dev* cam, struct parport* port);
+static void w9966_term(struct w9966_dev* cam);
+
+static inline void w9966_i2c_setsda(struct w9966_dev* cam, int state);
+static inline int w9966_i2c_setscl(struct w9966_dev* cam, int state);
+static inline int w9966_i2c_getsda(struct w9966_dev* cam);
+static inline int w9966_i2c_getscl(struct w9966_dev* cam);
+static int w9966_i2c_wbyte(struct w9966_dev* cam, int data);
+#if 0
+static int w9966_i2c_rbyte(struct w9966_dev* cam);
+#endif
+
+static int w9966_v4l_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+static ssize_t w9966_v4l_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos);
+
+static struct file_operations w9966_fops = {
+ .owner = THIS_MODULE,
+ .open = video_exclusive_open,
+ .release = video_exclusive_release,
+ .ioctl = w9966_v4l_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .read = w9966_v4l_read,
+ .llseek = no_llseek,
+};
+static struct video_device w9966_template = {
+ .owner = THIS_MODULE,
+ .name = W9966_DRIVERNAME,
+ .type = VID_TYPE_CAPTURE | VID_TYPE_SCALES,
+ .hardware = VID_HARDWARE_W9966,
+ .fops = &w9966_fops,
+};
+
+/*
+ * Private function defines
+ */
+
+
+// Set camera phase flags, so we know what to uninit when terminating
+static inline void w9966_setState(struct w9966_dev* cam, int mask, int val)
+{
+ cam->dev_state = (cam->dev_state & ~mask) ^ val;
+}
+
+// Get camera phase flags
+static inline int w9966_getState(struct w9966_dev* cam, int mask, int val)
+{
+ return ((cam->dev_state & mask) == val);
+}
+
+// Claim parport for ourself
+static inline void w9966_pdev_claim(struct w9966_dev* cam)
+{
+ if (w9966_getState(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED))
+ return;
+ parport_claim_or_block(cam->pdev);
+ w9966_setState(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED);
+}
+
+// Release parport for others to use
+static inline void w9966_pdev_release(struct w9966_dev* cam)
+{
+ if (w9966_getState(cam, W9966_STATE_CLAIMED, 0))
+ return;
+ parport_release(cam->pdev);
+ w9966_setState(cam, W9966_STATE_CLAIMED, 0);
+}
+
+// Read register from W9966 interface-chip
+// Expects a claimed pdev
+// -1 on error, else register data (byte)
+static int w9966_rReg(struct w9966_dev* cam, int reg)
+{
+ // ECP, read, regtransfer, REG, REG, REG, REG, REG
+ const unsigned char addr = 0x80 | (reg & 0x1f);
+ unsigned char val;
+
+ if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0)
+ return -1;
+ if (parport_write(cam->pport, &addr, 1) != 1)
+ return -1;
+ if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0)
+ return -1;
+ if (parport_read(cam->pport, &val, 1) != 1)
+ return -1;
+
+ return val;
+}
+
+// Write register to W9966 interface-chip
+// Expects a claimed pdev
+// -1 on error
+static int w9966_wReg(struct w9966_dev* cam, int reg, int data)
+{
+ // ECP, write, regtransfer, REG, REG, REG, REG, REG
+ const unsigned char addr = 0xc0 | (reg & 0x1f);
+ const unsigned char val = data;
+
+ if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0)
+ return -1;
+ if (parport_write(cam->pport, &addr, 1) != 1)
+ return -1;
+ if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0)
+ return -1;
+ if (parport_write(cam->pport, &val, 1) != 1)
+ return -1;
+
+ return 0;
+}
+
+// Initialize camera device. Setup all internal flags, set a
+// default video mode, setup ccd-chip, register v4l device etc..
+// Also used for 'probing' of hardware.
+// -1 on error
+static int w9966_init(struct w9966_dev* cam, struct parport* port)
+{
+ if (cam->dev_state != 0)
+ return -1;
+
+ cam->pport = port;
+ cam->brightness = 128;
+ cam->contrast = 64;
+ cam->color = 64;
+ cam->hue = 0;
+
+// Select requested transfer mode
+ switch(parmode)
+ {
+ default: // Auto-detect (priority: hw-ecp, hw-epp, sw-ecp)
+ case 0:
+ if (port->modes & PARPORT_MODE_ECP)
+ cam->ppmode = IEEE1284_MODE_ECP;
+ else if (port->modes & PARPORT_MODE_EPP)
+ cam->ppmode = IEEE1284_MODE_EPP;
+ else
+ cam->ppmode = IEEE1284_MODE_ECP;
+ break;
+ case 1: // hw- or sw-ecp
+ cam->ppmode = IEEE1284_MODE_ECP;
+ break;
+ case 2: // hw- or sw-epp
+ cam->ppmode = IEEE1284_MODE_EPP;
+ break;
+ }
+
+// Tell the parport driver that we exists
+ cam->pdev = parport_register_device(port, "w9966", NULL, NULL, NULL, 0, NULL);
+ if (cam->pdev == NULL) {
+ DPRINTF("parport_register_device() failed\n");
+ return -1;
+ }
+ w9966_setState(cam, W9966_STATE_PDEV, W9966_STATE_PDEV);
+
+ w9966_pdev_claim(cam);
+
+// Setup a default capture mode
+ if (w9966_setup(cam, 0, 0, 1023, 1023, 200, 160) != 0) {
+ DPRINTF("w9966_setup() failed.\n");
+ return -1;
+ }
+
+ w9966_pdev_release(cam);
+
+// Fill in the video_device struct and register us to v4l
+ memcpy(&cam->vdev, &w9966_template, sizeof(struct video_device));
+ cam->vdev.priv = cam;
+
+ if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) == -1)
+ return -1;
+
+ w9966_setState(cam, W9966_STATE_VDEV, W9966_STATE_VDEV);
+
+ // All ok
+ printk(
+ "w9966cf: Found and initialized a webcam on %s.\n",
+ cam->pport->name
+ );
+ return 0;
+}
+
+
+// Terminate everything gracefully
+static void w9966_term(struct w9966_dev* cam)
+{
+// Unregister from v4l
+ if (w9966_getState(cam, W9966_STATE_VDEV, W9966_STATE_VDEV)) {
+ video_unregister_device(&cam->vdev);
+ w9966_setState(cam, W9966_STATE_VDEV, 0);
+ }
+
+// Terminate from IEEE1284 mode and release pdev block
+ if (w9966_getState(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) {
+ w9966_pdev_claim(cam);
+ parport_negotiate(cam->pport, IEEE1284_MODE_COMPAT);
+ w9966_pdev_release(cam);
+ }
+
+// Unregister from parport
+ if (w9966_getState(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) {
+ parport_unregister_device(cam->pdev);
+ w9966_setState(cam, W9966_STATE_PDEV, 0);
+ }
+}
+
+
+// Find a good length for capture window (used both for W and H)
+// A bit ugly but pretty functional. The capture length
+// have to match the downscale
+static int w9966_findlen(int near, int size, int maxlen)
+{
+ int bestlen = size;
+ int besterr = abs(near - bestlen);
+ int len;
+
+ for(len = size+1;len < maxlen;len++)
+ {
+ int err;
+ if ( ((64*size) %len) != 0)
+ continue;
+
+ err = abs(near - len);
+
+ // Only continue as long as we keep getting better values
+ if (err > besterr)
+ break;
+
+ besterr = err;
+ bestlen = len;
+ }
+
+ return bestlen;
+}
+
+// Modify capture window (if necessary)
+// and calculate downscaling
+// Return -1 on error
+static int w9966_calcscale(int size, int min, int max, int* beg, int* end, unsigned char* factor)
+{
+ int maxlen = max - min;
+ int len = *end - *beg + 1;
+ int newlen = w9966_findlen(len, size, maxlen);
+ int err = newlen - len;
+
+ // Check for bad format
+ if (newlen > maxlen || newlen < size)
+ return -1;
+
+ // Set factor (6 bit fixed)
+ *factor = (64*size) / newlen;
+ if (*factor == 64)
+ *factor = 0x00; // downscale is disabled
+ else
+ *factor |= 0x80; // set downscale-enable bit
+
+ // Modify old beginning and end
+ *beg -= err / 2;
+ *end += err - (err / 2);
+
+ // Move window if outside borders
+ if (*beg < min) {
+ *end += min - *beg;
+ *beg += min - *beg;
+ }
+ if (*end > max) {
+ *beg -= *end - max;
+ *end -= *end - max;
+ }
+
+ return 0;
+}
+
+// Setup the cameras capture window etc.
+// Expects a claimed pdev
+// return -1 on error
+static int w9966_setup(struct w9966_dev* cam, int x1, int y1, int x2, int y2, int w, int h)
+{
+ unsigned int i;
+ unsigned int enh_s, enh_e;
+ unsigned char scale_x, scale_y;
+ unsigned char regs[0x1c];
+ unsigned char saa7111_regs[] = {
+ 0x21, 0x00, 0xd8, 0x23, 0x00, 0x80, 0x80, 0x00,
+ 0x88, 0x10, 0x80, 0x40, 0x40, 0x00, 0x01, 0x00,
+ 0x48, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x71, 0xe7, 0x00, 0x00, 0xc0
+ };
+
+
+ if (w*h*2 > W9966_SRAMSIZE)
+ {
+ DPRINTF("capture window exceeds SRAM size!.\n");
+ w = 200; h = 160; // Pick default values
+ }
+
+ w &= ~0x1;
+ if (w < 2) w = 2;
+ if (h < 1) h = 1;
+ if (w > W9966_WND_MAX_W) w = W9966_WND_MAX_W;
+ if (h > W9966_WND_MAX_H) h = W9966_WND_MAX_H;
+
+ cam->width = w;
+ cam->height = h;
+
+ enh_s = 0;
+ enh_e = w*h*2;
+
+// Modify capture window if necessary and calculate downscaling
+ if (
+ w9966_calcscale(w, W9966_WND_MIN_X, W9966_WND_MAX_X, &x1, &x2, &scale_x) != 0 ||
+ w9966_calcscale(h, W9966_WND_MIN_Y, W9966_WND_MAX_Y, &y1, &y2, &scale_y) != 0
+ ) return -1;
+
+ DPRINTF(
+ "%dx%d, x: %d<->%d, y: %d<->%d, sx: %d/64, sy: %d/64.\n",
+ w, h, x1, x2, y1, y2, scale_x&~0x80, scale_y&~0x80
+ );
+
+// Setup registers
+ regs[0x00] = 0x00; // Set normal operation
+ regs[0x01] = 0x18; // Capture mode
+ regs[0x02] = scale_y; // V-scaling
+ regs[0x03] = scale_x; // H-scaling
+
+ // Capture window
+ regs[0x04] = (x1 & 0x0ff); // X-start (8 low bits)
+ regs[0x05] = (x1 & 0x300)>>8; // X-start (2 high bits)
+ regs[0x06] = (y1 & 0x0ff); // Y-start (8 low bits)
+ regs[0x07] = (y1 & 0x300)>>8; // Y-start (2 high bits)
+ regs[0x08] = (x2 & 0x0ff); // X-end (8 low bits)
+ regs[0x09] = (x2 & 0x300)>>8; // X-end (2 high bits)
+ regs[0x0a] = (y2 & 0x0ff); // Y-end (8 low bits)
+
+ regs[0x0c] = W9966_SRAMID; // SRAM-banks (1x 128kb)
+
+ // Enhancement layer
+ regs[0x0d] = (enh_s& 0x000ff); // Enh. start (0-7)
+ regs[0x0e] = (enh_s& 0x0ff00)>>8; // Enh. start (8-15)
+ regs[0x0f] = (enh_s& 0x70000)>>16; // Enh. start (16-17/18??)
+ regs[0x10] = (enh_e& 0x000ff); // Enh. end (0-7)
+ regs[0x11] = (enh_e& 0x0ff00)>>8; // Enh. end (8-15)
+ regs[0x12] = (enh_e& 0x70000)>>16; // Enh. end (16-17/18??)
+
+ // Misc
+ regs[0x13] = 0x40; // VEE control (raw 4:2:2)
+ regs[0x17] = 0x00; // ???
+ regs[0x18] = cam->i2c_state = 0x00; // Serial bus
+ regs[0x19] = 0xff; // I/O port direction control
+ regs[0x1a] = 0xff; // I/O port data register
+ regs[0x1b] = 0x10; // ???
+
+ // SAA7111 chip settings
+ saa7111_regs[0x0a] = cam->brightness;
+ saa7111_regs[0x0b] = cam->contrast;
+ saa7111_regs[0x0c] = cam->color;
+ saa7111_regs[0x0d] = cam->hue;
+
+// Reset (ECP-fifo & serial-bus)
+ if (w9966_wReg(cam, 0x00, 0x03) == -1)
+ return -1;
+
+// Write regs to w9966cf chip
+ for (i = 0; i < 0x1c; i++)
+ if (w9966_wReg(cam, i, regs[i]) == -1)
+ return -1;
+
+// Write regs to saa7111 chip
+ for (i = 0; i < 0x20; i++)
+ if (w9966_wReg_i2c(cam, i, saa7111_regs[i]) == -1)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Ugly and primitive i2c protocol functions
+ */
+
+// Sets the data line on the i2c bus.
+// Expects a claimed pdev.
+static inline void w9966_i2c_setsda(struct w9966_dev* cam, int state)
+{
+ if (state)
+ cam->i2c_state |= W9966_I2C_W_DATA;
+ else
+ cam->i2c_state &= ~W9966_I2C_W_DATA;
+
+ w9966_wReg(cam, 0x18, cam->i2c_state);
+ udelay(5);
+}
+
+// Get peripheral clock line
+// Expects a claimed pdev.
+static inline int w9966_i2c_getscl(struct w9966_dev* cam)
+{
+ const unsigned char state = w9966_rReg(cam, 0x18);
+ return ((state & W9966_I2C_R_CLOCK) > 0);
+}
+
+// Sets the clock line on the i2c bus.
+// Expects a claimed pdev. -1 on error
+static inline int w9966_i2c_setscl(struct w9966_dev* cam, int state)
+{
+ unsigned long timeout;
+
+ if (state)
+ cam->i2c_state |= W9966_I2C_W_CLOCK;
+ else
+ cam->i2c_state &= ~W9966_I2C_W_CLOCK;
+
+ w9966_wReg(cam, 0x18, cam->i2c_state);
+ udelay(5);
+
+ // we go to high, we also expect the peripheral to ack.
+ if (state) {
+ timeout = jiffies + 100;
+ while (!w9966_i2c_getscl(cam)) {
+ if (time_after(jiffies, timeout))
+ return -1;
+ }
+ }
+ return 0;
+}
+
+// Get peripheral data line
+// Expects a claimed pdev.
+static inline int w9966_i2c_getsda(struct w9966_dev* cam)
+{
+ const unsigned char state = w9966_rReg(cam, 0x18);
+ return ((state & W9966_I2C_R_DATA) > 0);
+}
+
+// Write a byte with ack to the i2c bus.
+// Expects a claimed pdev. -1 on error
+static int w9966_i2c_wbyte(struct w9966_dev* cam, int data)
+{
+ int i;
+ for (i = 7; i >= 0; i--)
+ {
+ w9966_i2c_setsda(cam, (data >> i) & 0x01);
+
+ if (w9966_i2c_setscl(cam, 1) == -1)
+ return -1;
+ w9966_i2c_setscl(cam, 0);
+ }
+
+ w9966_i2c_setsda(cam, 1);
+
+ if (w9966_i2c_setscl(cam, 1) == -1)
+ return -1;
+ w9966_i2c_setscl(cam, 0);
+
+ return 0;
+}
+
+// Read a data byte with ack from the i2c-bus
+// Expects a claimed pdev. -1 on error
+#if 0
+static int w9966_i2c_rbyte(struct w9966_dev* cam)
+{
+ unsigned char data = 0x00;
+ int i;
+
+ w9966_i2c_setsda(cam, 1);
+
+ for (i = 0; i < 8; i++)
+ {
+ if (w9966_i2c_setscl(cam, 1) == -1)
+ return -1;
+ data = data << 1;
+ if (w9966_i2c_getsda(cam))
+ data |= 0x01;
+
+ w9966_i2c_setscl(cam, 0);
+ }
+ return data;
+}
+#endif
+
+// Read a register from the i2c device.
+// Expects claimed pdev. -1 on error
+#if 0
+static int w9966_rReg_i2c(struct w9966_dev* cam, int reg)
+{
+ int data;
+
+ w9966_i2c_setsda(cam, 0);
+ w9966_i2c_setscl(cam, 0);
+
+ if (
+ w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 ||
+ w9966_i2c_wbyte(cam, reg) == -1
+ )
+ return -1;
+
+ w9966_i2c_setsda(cam, 1);
+ if (w9966_i2c_setscl(cam, 1) == -1)
+ return -1;
+ w9966_i2c_setsda(cam, 0);
+ w9966_i2c_setscl(cam, 0);
+
+ if (
+ w9966_i2c_wbyte(cam, W9966_I2C_R_ID) == -1 ||
+ (data = w9966_i2c_rbyte(cam)) == -1
+ )
+ return -1;
+
+ w9966_i2c_setsda(cam, 0);
+
+ if (w9966_i2c_setscl(cam, 1) == -1)
+ return -1;
+ w9966_i2c_setsda(cam, 1);
+
+ return data;
+}
+#endif
+
+// Write a register to the i2c device.
+// Expects claimed pdev. -1 on error
+static int w9966_wReg_i2c(struct w9966_dev* cam, int reg, int data)
+{
+ w9966_i2c_setsda(cam, 0);
+ w9966_i2c_setscl(cam, 0);
+
+ if (
+ w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 ||
+ w9966_i2c_wbyte(cam, reg) == -1 ||
+ w9966_i2c_wbyte(cam, data) == -1
+ )
+ return -1;
+
+ w9966_i2c_setsda(cam, 0);
+ if (w9966_i2c_setscl(cam, 1) == -1)
+ return -1;
+
+ w9966_i2c_setsda(cam, 1);
+
+ return 0;
+}
+
+/*
+ * Video4linux interfacing
+ */
+
+static int w9966_v4l_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct w9966_dev *cam = vdev->priv;
+
+ switch(cmd)
+ {
+ case VIDIOCGCAP:
+ {
+ static struct video_capability vcap = {
+ .name = W9966_DRIVERNAME,
+ .type = VID_TYPE_CAPTURE | VID_TYPE_SCALES,
+ .channels = 1,
+ .maxwidth = W9966_WND_MAX_W,
+ .maxheight = W9966_WND_MAX_H,
+ .minwidth = 2,
+ .minheight = 1,
+ };
+ struct video_capability *cap = arg;
+ *cap = vcap;
+ return 0;
+ }
+ case VIDIOCGCHAN:
+ {
+ struct video_channel *vch = arg;
+ if(vch->channel != 0) // We only support one channel (#0)
+ return -EINVAL;
+ memset(vch,0,sizeof(*vch));
+ strcpy(vch->name, "CCD-input");
+ vch->type = VIDEO_TYPE_CAMERA;
+ return 0;
+ }
+ case VIDIOCSCHAN:
+ {
+ struct video_channel *vch = arg;
+ if(vch->channel != 0)
+ return -EINVAL;
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner *vtune = arg;
+ if(vtune->tuner != 0)
+ return -EINVAL;
+ strcpy(vtune->name, "no tuner");
+ vtune->rangelow = 0;
+ vtune->rangehigh = 0;
+ vtune->flags = VIDEO_TUNER_NORM;
+ vtune->mode = VIDEO_MODE_AUTO;
+ vtune->signal = 0xffff;
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner *vtune = arg;
+ if (vtune->tuner != 0)
+ return -EINVAL;
+ if (vtune->mode != VIDEO_MODE_AUTO)
+ return -EINVAL;
+ return 0;
+ }
+ case VIDIOCGPICT:
+ {
+ struct video_picture vpic = {
+ cam->brightness << 8, // brightness
+ (cam->hue + 128) << 8, // hue
+ cam->color << 9, // color
+ cam->contrast << 9, // contrast
+ 0x8000, // whiteness
+ 16, VIDEO_PALETTE_YUV422// bpp, palette format
+ };
+ struct video_picture *pic = arg;
+ *pic = vpic;
+ return 0;
+ }
+ case VIDIOCSPICT:
+ {
+ struct video_picture *vpic = arg;
+ if (vpic->depth != 16 || vpic->palette != VIDEO_PALETTE_YUV422)
+ return -EINVAL;
+
+ cam->brightness = vpic->brightness >> 8;
+ cam->hue = (vpic->hue >> 8) - 128;
+ cam->color = vpic->colour >> 9;
+ cam->contrast = vpic->contrast >> 9;
+
+ w9966_pdev_claim(cam);
+
+ if (
+ w9966_wReg_i2c(cam, 0x0a, cam->brightness) == -1 ||
+ w9966_wReg_i2c(cam, 0x0b, cam->contrast) == -1 ||
+ w9966_wReg_i2c(cam, 0x0c, cam->color) == -1 ||
+ w9966_wReg_i2c(cam, 0x0d, cam->hue) == -1
+ ) {
+ w9966_pdev_release(cam);
+ return -EIO;
+ }
+
+ w9966_pdev_release(cam);
+ return 0;
+ }
+ case VIDIOCSWIN:
+ {
+ int ret;
+ struct video_window *vwin = arg;
+
+ if (vwin->flags != 0)
+ return -EINVAL;
+ if (vwin->clipcount != 0)
+ return -EINVAL;
+ if (vwin->width < 2 || vwin->width > W9966_WND_MAX_W)
+ return -EINVAL;
+ if (vwin->height < 1 || vwin->height > W9966_WND_MAX_H)
+ return -EINVAL;
+
+ // Update camera regs
+ w9966_pdev_claim(cam);
+ ret = w9966_setup(cam, 0, 0, 1023, 1023, vwin->width, vwin->height);
+ w9966_pdev_release(cam);
+
+ if (ret != 0) {
+ DPRINTF("VIDIOCSWIN: w9966_setup() failed.\n");
+ return -EIO;
+ }
+
+ return 0;
+ }
+ case VIDIOCGWIN:
+ {
+ struct video_window *vwin = arg;
+ memset(vwin, 0, sizeof(*vwin));
+ vwin->width = cam->width;
+ vwin->height = cam->height;
+ return 0;
+ }
+ // Unimplemented
+ case VIDIOCCAPTURE:
+ case VIDIOCGFBUF:
+ case VIDIOCSFBUF:
+ case VIDIOCKEY:
+ case VIDIOCGFREQ:
+ case VIDIOCSFREQ:
+ case VIDIOCGAUDIO:
+ case VIDIOCSAUDIO:
+ return -EINVAL;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static int w9966_v4l_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, w9966_v4l_do_ioctl);
+}
+
+// Capture data
+static ssize_t w9966_v4l_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct w9966_dev *cam = vdev->priv;
+ unsigned char addr = 0xa0; // ECP, read, CCD-transfer, 00000
+ unsigned char __user *dest = (unsigned char __user *)buf;
+ unsigned long dleft = count;
+ unsigned char *tbuf;
+
+ // Why would anyone want more than this??
+ if (count > cam->width * cam->height * 2)
+ return -EINVAL;
+
+ w9966_pdev_claim(cam);
+ w9966_wReg(cam, 0x00, 0x02); // Reset ECP-FIFO buffer
+ w9966_wReg(cam, 0x00, 0x00); // Return to normal operation
+ w9966_wReg(cam, 0x01, 0x98); // Enable capture
+
+ // write special capture-addr and negotiate into data transfer
+ if (
+ (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_ADDR) != 0 )||
+ (parport_write(cam->pport, &addr, 1) != 1 )||
+ (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_DATA) != 0 )
+ ) {
+ w9966_pdev_release(cam);
+ return -EFAULT;
+ }
+
+ tbuf = kmalloc(W9966_RBUFFER, GFP_KERNEL);
+ if (tbuf == NULL) {
+ count = -ENOMEM;
+ goto out;
+ }
+
+ while(dleft > 0)
+ {
+ unsigned long tsize = (dleft > W9966_RBUFFER) ? W9966_RBUFFER : dleft;
+
+ if (parport_read(cam->pport, tbuf, tsize) < tsize) {
+ count = -EFAULT;
+ goto out;
+ }
+ if (copy_to_user(dest, tbuf, tsize) != 0) {
+ count = -EFAULT;
+ goto out;
+ }
+ dest += tsize;
+ dleft -= tsize;
+ }
+
+ w9966_wReg(cam, 0x01, 0x18); // Disable capture
+
+out:
+ kfree(tbuf);
+ w9966_pdev_release(cam);
+
+ return count;
+}
+
+
+// Called once for every parport on init
+static void w9966_attach(struct parport *port)
+{
+ int i;
+
+ for (i = 0; i < W9966_MAXCAMS; i++)
+ {
+ if (w9966_cams[i].dev_state != 0) // Cam is already assigned
+ continue;
+ if (
+ strcmp(pardev[i], "aggressive") == 0 ||
+ strcmp(pardev[i], port->name) == 0
+ ) {
+ if (w9966_init(&w9966_cams[i], port) != 0)
+ w9966_term(&w9966_cams[i]);
+ break; // return
+ }
+ }
+}
+
+// Called once for every parport on termination
+static void w9966_detach(struct parport *port)
+{
+ int i;
+ for (i = 0; i < W9966_MAXCAMS; i++)
+ if (w9966_cams[i].dev_state != 0 && w9966_cams[i].pport == port)
+ w9966_term(&w9966_cams[i]);
+}
+
+
+static struct parport_driver w9966_ppd = {
+ .name = W9966_DRIVERNAME,
+ .attach = w9966_attach,
+ .detach = w9966_detach,
+};
+
+// Module entry point
+static int __init w9966_mod_init(void)
+{
+ int i;
+ for (i = 0; i < W9966_MAXCAMS; i++)
+ w9966_cams[i].dev_state = 0;
+
+ return parport_register_driver(&w9966_ppd);
+}
+
+// Module cleanup
+static void __exit w9966_mod_term(void)
+{
+ parport_unregister_driver(&w9966_ppd);
+}
+
+module_init(w9966_mod_init);
+module_exit(w9966_mod_term);
diff --git a/linux/drivers/media/video/w9968cf.c b/linux/drivers/media/video/w9968cf.c
new file mode 100644
index 000000000..20f211b55
--- /dev/null
+++ b/linux/drivers/media/video/w9968cf.c
@@ -0,0 +1,3691 @@
+/***************************************************************************
+ * Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip. *
+ * *
+ * Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * - Memory management code from bttv driver by Ralph Metzler, *
+ * Marcus Metzler and Gerd Knorr. *
+ * - I2C interface to kernel, high-level image sensor control routines and *
+ * some symbolic names from OV511 driver by Mark W. McClelland. *
+ * - Low-level I2C fast write function by Piotr Czerczak. *
+ * - Low-level I2C read function by Frederic Jouault. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <linux/stddef.h>
+#include <asm/page.h>
+#include <asm/uaccess.h>
+#include <linux/page-flags.h>
+#include <linux/moduleparam.h>
+
+#include "w9968cf.h"
+#include "w9968cf_decoder.h"
+
+static struct w9968cf_vpp_t* w9968cf_vpp;
+static DECLARE_WAIT_QUEUE_HEAD(w9968cf_vppmod_wait);
+
+static LIST_HEAD(w9968cf_dev_list); /* head of V4L registered cameras list */
+static DEFINE_MUTEX(w9968cf_devlist_mutex); /* semaphore for list traversal */
+
+static DECLARE_RWSEM(w9968cf_disconnect); /* prevent races with open() */
+
+
+/****************************************************************************
+ * Module macros and parameters *
+ ****************************************************************************/
+
+MODULE_DEVICE_TABLE(usb, winbond_id_table);
+
+MODULE_AUTHOR(W9968CF_MODULE_AUTHOR" "W9968CF_AUTHOR_EMAIL);
+MODULE_DESCRIPTION(W9968CF_MODULE_NAME);
+MODULE_VERSION(W9968CF_MODULE_VERSION);
+MODULE_LICENSE(W9968CF_MODULE_LICENSE);
+MODULE_SUPPORTED_DEVICE("Video");
+
+static int ovmod_load = W9968CF_OVMOD_LOAD;
+static unsigned short simcams = W9968CF_SIMCAMS;
+static short video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /*-1=first free*/
+static unsigned int packet_size[] = {[0 ... W9968CF_MAX_DEVICES-1] =
+ W9968CF_PACKET_SIZE};
+static unsigned short max_buffers[] = {[0 ... W9968CF_MAX_DEVICES-1] =
+ W9968CF_BUFFERS};
+static int double_buffer[] = {[0 ... W9968CF_MAX_DEVICES-1] =
+ W9968CF_DOUBLE_BUFFER};
+static int clamping[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLAMPING};
+static unsigned short filter_type[]= {[0 ... W9968CF_MAX_DEVICES-1] =
+ W9968CF_FILTER_TYPE};
+static int largeview[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LARGEVIEW};
+static unsigned short decompression[] = {[0 ... W9968CF_MAX_DEVICES-1] =
+ W9968CF_DECOMPRESSION};
+static int upscaling[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_UPSCALING};
+static unsigned short force_palette[] = {[0 ... W9968CF_MAX_DEVICES-1] = 0};
+static int force_rgb[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FORCE_RGB};
+static int autobright[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOBRIGHT};
+static int autoexp[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOEXP};
+static unsigned short lightfreq[] = {[0 ... W9968CF_MAX_DEVICES-1] =
+ W9968CF_LIGHTFREQ};
+static int bandingfilter[] = {[0 ... W9968CF_MAX_DEVICES-1]=
+ W9968CF_BANDINGFILTER};
+static short clockdiv[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLOCKDIV};
+static int backlight[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BACKLIGHT};
+static int mirror[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_MIRROR};
+static int monochrome[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_MONOCHROME};
+static unsigned int brightness[] = {[0 ... W9968CF_MAX_DEVICES-1] =
+ W9968CF_BRIGHTNESS};
+static unsigned int hue[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_HUE};
+static unsigned int colour[]={[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_COLOUR};
+static unsigned int contrast[] = {[0 ... W9968CF_MAX_DEVICES-1] =
+ W9968CF_CONTRAST};
+static unsigned int whiteness[] = {[0 ... W9968CF_MAX_DEVICES-1] =
+ W9968CF_WHITENESS};
+#ifdef W9968CF_DEBUG
+static unsigned short debug = W9968CF_DEBUG_LEVEL;
+static int specific_debug = W9968CF_SPECIFIC_DEBUG;
+#endif
+
+static unsigned int param_nv[24]; /* number of values per parameter */
+
+#ifdef CONFIG_KMOD
+module_param(ovmod_load, bool, 0644);
+#endif
+module_param(simcams, ushort, 0644);
+module_param_array(video_nr, short, &param_nv[0], 0444);
+module_param_array(packet_size, uint, &param_nv[1], 0444);
+module_param_array(max_buffers, ushort, &param_nv[2], 0444);
+module_param_array(double_buffer, bool, &param_nv[3], 0444);
+module_param_array(clamping, bool, &param_nv[4], 0444);
+module_param_array(filter_type, ushort, &param_nv[5], 0444);
+module_param_array(largeview, bool, &param_nv[6], 0444);
+module_param_array(decompression, ushort, &param_nv[7], 0444);
+module_param_array(upscaling, bool, &param_nv[8], 0444);
+module_param_array(force_palette, ushort, &param_nv[9], 0444);
+module_param_array(force_rgb, ushort, &param_nv[10], 0444);
+module_param_array(autobright, bool, &param_nv[11], 0444);
+module_param_array(autoexp, bool, &param_nv[12], 0444);
+module_param_array(lightfreq, ushort, &param_nv[13], 0444);
+module_param_array(bandingfilter, bool, &param_nv[14], 0444);
+module_param_array(clockdiv, short, &param_nv[15], 0444);
+module_param_array(backlight, bool, &param_nv[16], 0444);
+module_param_array(mirror, bool, &param_nv[17], 0444);
+module_param_array(monochrome, bool, &param_nv[18], 0444);
+module_param_array(brightness, uint, &param_nv[19], 0444);
+module_param_array(hue, uint, &param_nv[20], 0444);
+module_param_array(colour, uint, &param_nv[21], 0444);
+module_param_array(contrast, uint, &param_nv[22], 0444);
+module_param_array(whiteness, uint, &param_nv[23], 0444);
+#ifdef W9968CF_DEBUG
+module_param(debug, ushort, 0644);
+module_param(specific_debug, bool, 0644);
+#endif
+
+#ifdef CONFIG_KMOD
+MODULE_PARM_DESC(ovmod_load,
+ "\n<0|1> Automatic 'ovcamchip' module loading."
+ "\n0 disabled, 1 enabled."
+ "\nIf enabled,'insmod' searches for the required 'ovcamchip'"
+ "\nmodule in the system, according to its configuration, and"
+ "\nattempts to load that module automatically. This action is"
+ "\nperformed once as soon as the 'w9968cf' module is loaded"
+ "\ninto memory."
+ "\nDefault value is "__MODULE_STRING(W9968CF_OVMOD_LOAD)"."
+ "\n");
+#endif
+MODULE_PARM_DESC(simcams,
+ "\n<n> Number of cameras allowed to stream simultaneously."
+ "\nn may vary from 0 to "
+ __MODULE_STRING(W9968CF_MAX_DEVICES)"."
+ "\nDefault value is "__MODULE_STRING(W9968CF_SIMCAMS)"."
+ "\n");
+MODULE_PARM_DESC(video_nr,
+ "\n<-1|n[,...]> Specify V4L minor mode number."
+ "\n -1 = use next available (default)"
+ "\n n = use minor number n (integer >= 0)"
+ "\nYou can specify up to "__MODULE_STRING(W9968CF_MAX_DEVICES)
+ " cameras this way."
+ "\nFor example:"
+ "\nvideo_nr=-1,2,-1 would assign minor number 2 to"
+ "\nthe second camera and use auto for the first"
+ "\none and for every other camera."
+ "\n");
+MODULE_PARM_DESC(packet_size,
+ "\n<n[,...]> Specify the maximum data payload"
+ "\nsize in bytes for alternate settings, for each device."
+ "\nn is scaled between 63 and 1023 "
+ "(default is "__MODULE_STRING(W9968CF_PACKET_SIZE)")."
+ "\n");
+MODULE_PARM_DESC(max_buffers,
+ "\n<n[,...]> For advanced users."
+ "\nSpecify the maximum number of video frame buffers"
+ "\nto allocate for each device, from 2 to "
+ __MODULE_STRING(W9968CF_MAX_BUFFERS)
+ ". (default is "__MODULE_STRING(W9968CF_BUFFERS)")."
+ "\n");
+MODULE_PARM_DESC(double_buffer,
+ "\n<0|1[,...]> "
+ "Hardware double buffering: 0 disabled, 1 enabled."
+ "\nIt should be enabled if you want smooth video output: if"
+ "\nyou obtain out of sync. video, disable it, or try to"
+ "\ndecrease the 'clockdiv' module parameter value."
+ "\nDefault value is "__MODULE_STRING(W9968CF_DOUBLE_BUFFER)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(clamping,
+ "\n<0|1[,...]> Video data clamping: 0 disabled, 1 enabled."
+ "\nDefault value is "__MODULE_STRING(W9968CF_CLAMPING)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(filter_type,
+ "\n<0|1|2[,...]> Video filter type."
+ "\n0 none, 1 (1-2-1) 3-tap filter, "
+ "2 (2-3-6-3-2) 5-tap filter."
+ "\nDefault value is "__MODULE_STRING(W9968CF_FILTER_TYPE)
+ " for every device."
+ "\nThe filter is used to reduce noise and aliasing artifacts"
+ "\nproduced by the CCD or CMOS image sensor, and the scaling"
+ " process."
+ "\n");
+MODULE_PARM_DESC(largeview,
+ "\n<0|1[,...]> Large view: 0 disabled, 1 enabled."
+ "\nDefault value is "__MODULE_STRING(W9968CF_LARGEVIEW)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(upscaling,
+ "\n<0|1[,...]> Software scaling (for non-compressed video):"
+ "\n0 disabled, 1 enabled."
+ "\nDisable it if you have a slow CPU or you don't have"
+ " enough memory."
+ "\nDefault value is "__MODULE_STRING(W9968CF_UPSCALING)
+ " for every device."
+ "\nIf 'w9968cf-vpp' is not present, this parameter is"
+ " set to 0."
+ "\n");
+MODULE_PARM_DESC(decompression,
+ "\n<0|1|2[,...]> Software video decompression:"
+ "\n- 0 disables decompression (doesn't allow formats needing"
+ " decompression)"
+ "\n- 1 forces decompression (allows formats needing"
+ " decompression only);"
+ "\n- 2 allows any permitted formats."
+ "\nFormats supporting compressed video are YUV422P and"
+ " YUV420P/YUV420 "
+ "\nin any resolutions where both width and height are "
+ "a multiple of 16."
+ "\nDefault value is "__MODULE_STRING(W9968CF_DECOMPRESSION)
+ " for every device."
+ "\nIf 'w9968cf-vpp' is not present, forcing decompression is "
+ "\nnot allowed; in this case this parameter is set to 2."
+ "\n");
+MODULE_PARM_DESC(force_palette,
+ "\n<0"
+ "|" __MODULE_STRING(VIDEO_PALETTE_UYVY)
+ "|" __MODULE_STRING(VIDEO_PALETTE_YUV420)
+ "|" __MODULE_STRING(VIDEO_PALETTE_YUV422P)
+ "|" __MODULE_STRING(VIDEO_PALETTE_YUV420P)
+ "|" __MODULE_STRING(VIDEO_PALETTE_YUYV)
+ "|" __MODULE_STRING(VIDEO_PALETTE_YUV422)
+ "|" __MODULE_STRING(VIDEO_PALETTE_GREY)
+ "|" __MODULE_STRING(VIDEO_PALETTE_RGB555)
+ "|" __MODULE_STRING(VIDEO_PALETTE_RGB565)
+ "|" __MODULE_STRING(VIDEO_PALETTE_RGB24)
+ "|" __MODULE_STRING(VIDEO_PALETTE_RGB32)
+ "[,...]>"
+ " Force picture palette."
+ "\nIn order:"
+ "\n- 0 allows any of the following formats:"
+ "\n- UYVY 16 bpp - Original video, compression disabled"
+ "\n- YUV420 12 bpp - Original video, compression enabled"
+ "\n- YUV422P 16 bpp - Original video, compression enabled"
+ "\n- YUV420P 12 bpp - Original video, compression enabled"
+ "\n- YUVY 16 bpp - Software conversion from UYVY"
+ "\n- YUV422 16 bpp - Software conversion from UYVY"
+ "\n- GREY 8 bpp - Software conversion from UYVY"
+ "\n- RGB555 16 bpp - Software conversion from UYVY"
+ "\n- RGB565 16 bpp - Software conversion from UYVY"
+ "\n- RGB24 24 bpp - Software conversion from UYVY"
+ "\n- RGB32 32 bpp - Software conversion from UYVY"
+ "\nWhen not 0, this parameter will override 'decompression'."
+ "\nDefault value is 0 for every device."
+ "\nInitial palette is "
+ __MODULE_STRING(W9968CF_PALETTE_DECOMP_ON)"."
+ "\nIf 'w9968cf-vpp' is not present, this parameter is"
+ " set to 9 (UYVY)."
+ "\n");
+MODULE_PARM_DESC(force_rgb,
+ "\n<0|1[,...]> Read RGB video data instead of BGR:"
+ "\n 1 = use RGB component ordering."
+ "\n 0 = use BGR component ordering."
+ "\nThis parameter has effect when using RGBX palettes only."
+ "\nDefault value is "__MODULE_STRING(W9968CF_FORCE_RGB)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(autobright,
+ "\n<0|1[,...]> Image sensor automatically changes brightness:"
+ "\n 0 = no, 1 = yes"
+ "\nDefault value is "__MODULE_STRING(W9968CF_AUTOBRIGHT)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(autoexp,
+ "\n<0|1[,...]> Image sensor automatically changes exposure:"
+ "\n 0 = no, 1 = yes"
+ "\nDefault value is "__MODULE_STRING(W9968CF_AUTOEXP)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(lightfreq,
+ "\n<50|60[,...]> Light frequency in Hz:"
+ "\n 50 for European and Asian lighting,"
+ " 60 for American lighting."
+ "\nDefault value is "__MODULE_STRING(W9968CF_LIGHTFREQ)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(bandingfilter,
+ "\n<0|1[,...]> Banding filter to reduce effects of"
+ " fluorescent lighting:"
+ "\n 0 disabled, 1 enabled."
+ "\nThis filter tries to reduce the pattern of horizontal"
+ "\nlight/dark bands caused by some (usually fluorescent)"
+ " lighting."
+ "\nDefault value is "__MODULE_STRING(W9968CF_BANDINGFILTER)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(clockdiv,
+ "\n<-1|n[,...]> "
+ "Force pixel clock divisor to a specific value (for experts):"
+ "\n n may vary from 0 to 127."
+ "\n -1 for automatic value."
+ "\nSee also the 'double_buffer' module parameter."
+ "\nDefault value is "__MODULE_STRING(W9968CF_CLOCKDIV)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(backlight,
+ "\n<0|1[,...]> Objects are lit from behind:"
+ "\n 0 = no, 1 = yes"
+ "\nDefault value is "__MODULE_STRING(W9968CF_BACKLIGHT)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(mirror,
+ "\n<0|1[,...]> Reverse image horizontally:"
+ "\n 0 = no, 1 = yes"
+ "\nDefault value is "__MODULE_STRING(W9968CF_MIRROR)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(monochrome,
+ "\n<0|1[,...]> Use image sensor as monochrome sensor:"
+ "\n 0 = no, 1 = yes"
+ "\nNot all the sensors support monochrome color."
+ "\nDefault value is "__MODULE_STRING(W9968CF_MONOCHROME)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(brightness,
+ "\n<n[,...]> Set picture brightness (0-65535)."
+ "\nDefault value is "__MODULE_STRING(W9968CF_BRIGHTNESS)
+ " for every device."
+ "\nThis parameter has no effect if 'autobright' is enabled."
+ "\n");
+MODULE_PARM_DESC(hue,
+ "\n<n[,...]> Set picture hue (0-65535)."
+ "\nDefault value is "__MODULE_STRING(W9968CF_HUE)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(colour,
+ "\n<n[,...]> Set picture saturation (0-65535)."
+ "\nDefault value is "__MODULE_STRING(W9968CF_COLOUR)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(contrast,
+ "\n<n[,...]> Set picture contrast (0-65535)."
+ "\nDefault value is "__MODULE_STRING(W9968CF_CONTRAST)
+ " for every device."
+ "\n");
+MODULE_PARM_DESC(whiteness,
+ "\n<n[,...]> Set picture whiteness (0-65535)."
+ "\nDefault value is "__MODULE_STRING(W9968CF_WHITENESS)
+ " for every device."
+ "\n");
+#ifdef W9968CF_DEBUG
+MODULE_PARM_DESC(debug,
+ "\n<n> Debugging information level, from 0 to 6:"
+ "\n0 = none (use carefully)"
+ "\n1 = critical errors"
+ "\n2 = significant informations"
+ "\n3 = configuration or general messages"
+ "\n4 = warnings"
+ "\n5 = called functions"
+ "\n6 = function internals"
+ "\nLevel 5 and 6 are useful for testing only, when only "
+ "one device is used."
+ "\nDefault value is "__MODULE_STRING(W9968CF_DEBUG_LEVEL)"."
+ "\n");
+MODULE_PARM_DESC(specific_debug,
+ "\n<0|1> Enable or disable specific debugging messages:"
+ "\n0 = print messages concerning every level"
+ " <= 'debug' level."
+ "\n1 = print messages concerning the level"
+ " indicated by 'debug'."
+ "\nDefault value is "
+ __MODULE_STRING(W9968CF_SPECIFIC_DEBUG)"."
+ "\n");
+#endif /* W9968CF_DEBUG */
+
+
+
+/****************************************************************************
+ * Some prototypes *
+ ****************************************************************************/
+
+/* Video4linux interface */
+static struct file_operations w9968cf_fops;
+static int w9968cf_open(struct inode*, struct file*);
+static int w9968cf_release(struct inode*, struct file*);
+static int w9968cf_mmap(struct file*, struct vm_area_struct*);
+static int w9968cf_ioctl(struct inode*, struct file*, unsigned, unsigned long);
+static ssize_t w9968cf_read(struct file*, char __user *, size_t, loff_t*);
+static int w9968cf_v4l_ioctl(struct inode*, struct file*, unsigned int,
+ void __user *);
+
+/* USB-specific */
+static int w9968cf_start_transfer(struct w9968cf_device*);
+static int w9968cf_stop_transfer(struct w9968cf_device*);
+static int w9968cf_write_reg(struct w9968cf_device*, u16 value, u16 index);
+static int w9968cf_read_reg(struct w9968cf_device*, u16 index);
+static int w9968cf_write_fsb(struct w9968cf_device*, u16* data);
+static int w9968cf_write_sb(struct w9968cf_device*, u16 value);
+static int w9968cf_read_sb(struct w9968cf_device*);
+static int w9968cf_upload_quantizationtables(struct w9968cf_device*);
+static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs);
+
+/* Low-level I2C (SMBus) I/O */
+static int w9968cf_smbus_start(struct w9968cf_device*);
+static int w9968cf_smbus_stop(struct w9968cf_device*);
+static int w9968cf_smbus_write_byte(struct w9968cf_device*, u8 v);
+static int w9968cf_smbus_read_byte(struct w9968cf_device*, u8* v);
+static int w9968cf_smbus_write_ack(struct w9968cf_device*);
+static int w9968cf_smbus_read_ack(struct w9968cf_device*);
+static int w9968cf_smbus_refresh_bus(struct w9968cf_device*);
+static int w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam,
+ u16 address, u8* value);
+static int w9968cf_i2c_adap_read_byte_data(struct w9968cf_device*, u16 address,
+ u8 subaddress, u8* value);
+static int w9968cf_i2c_adap_write_byte(struct w9968cf_device*,
+ u16 address, u8 subaddress);
+static int w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device*,
+ u16 address, u8 subaddress,
+ u8 value);
+
+/* I2C interface to kernel */
+static int w9968cf_i2c_init(struct w9968cf_device*);
+static int w9968cf_i2c_smbus_xfer(struct i2c_adapter*, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size, union i2c_smbus_data*);
+static u32 w9968cf_i2c_func(struct i2c_adapter*);
+static int w9968cf_i2c_attach_inform(struct i2c_client*);
+static int w9968cf_i2c_detach_inform(struct i2c_client*);
+static int w9968cf_i2c_control(struct i2c_adapter*, unsigned int cmd,
+ unsigned long arg);
+
+/* Memory management */
+static void* rvmalloc(unsigned long size);
+static void rvfree(void *mem, unsigned long size);
+static void w9968cf_deallocate_memory(struct w9968cf_device*);
+static int w9968cf_allocate_memory(struct w9968cf_device*);
+
+/* High-level image sensor control functions */
+static int w9968cf_sensor_set_control(struct w9968cf_device*,int cid,int val);
+static int w9968cf_sensor_get_control(struct w9968cf_device*,int cid,int *val);
+static int w9968cf_sensor_cmd(struct w9968cf_device*,
+ unsigned int cmd, void *arg);
+static int w9968cf_sensor_init(struct w9968cf_device*);
+static int w9968cf_sensor_update_settings(struct w9968cf_device*);
+static int w9968cf_sensor_get_picture(struct w9968cf_device*);
+static int w9968cf_sensor_update_picture(struct w9968cf_device*,
+ struct video_picture pict);
+
+/* Other helper functions */
+static void w9968cf_configure_camera(struct w9968cf_device*,struct usb_device*,
+ enum w9968cf_model_id,
+ const unsigned short dev_nr);
+static void w9968cf_adjust_configuration(struct w9968cf_device*);
+static int w9968cf_turn_on_led(struct w9968cf_device*);
+static int w9968cf_init_chip(struct w9968cf_device*);
+static inline u16 w9968cf_valid_palette(u16 palette);
+static inline u16 w9968cf_valid_depth(u16 palette);
+static inline u8 w9968cf_need_decompression(u16 palette);
+static int w9968cf_set_picture(struct w9968cf_device*, struct video_picture);
+static int w9968cf_set_window(struct w9968cf_device*, struct video_window);
+static int w9968cf_postprocess_frame(struct w9968cf_device*,
+ struct w9968cf_frame_t*);
+static int w9968cf_adjust_window_size(struct w9968cf_device*, u16* w, u16* h);
+static void w9968cf_init_framelist(struct w9968cf_device*);
+static void w9968cf_push_frame(struct w9968cf_device*, u8 f_num);
+static void w9968cf_pop_frame(struct w9968cf_device*,struct w9968cf_frame_t**);
+static void w9968cf_release_resources(struct w9968cf_device*);
+
+
+
+/****************************************************************************
+ * Symbolic names *
+ ****************************************************************************/
+
+/* Used to represent a list of values and their respective symbolic names */
+struct w9968cf_symbolic_list {
+ const int num;
+ const char *name;
+};
+
+/*--------------------------------------------------------------------------
+ Returns the name of the matching element in the symbolic_list array. The
+ end of the list must be marked with an element that has a NULL name.
+ --------------------------------------------------------------------------*/
+static inline const char *
+symbolic(struct w9968cf_symbolic_list list[], const int num)
+{
+ int i;
+
+ for (i = 0; list[i].name != NULL; i++)
+ if (list[i].num == num)
+ return (list[i].name);
+
+ return "Unknown";
+}
+
+static struct w9968cf_symbolic_list camlist[] = {
+ { W9968CF_MOD_GENERIC, "W996[87]CF JPEG USB Dual Mode Camera" },
+ { W9968CF_MOD_CLVBWGP, "Creative Labs Video Blaster WebCam Go Plus" },
+
+ /* Other cameras (having the same descriptors as Generic W996[87]CF) */
+ { W9968CF_MOD_ADPVDMA, "Aroma Digi Pen VGA Dual Mode ADG-5000" },
+ { W9986CF_MOD_AAU, "AVerMedia AVerTV USB" },
+ { W9968CF_MOD_CLVBWG, "Creative Labs Video Blaster WebCam Go" },
+ { W9968CF_MOD_LL, "Lebon LDC-035A" },
+ { W9968CF_MOD_EEEMC, "Ezonics EZ-802 EZMega Cam" },
+ { W9968CF_MOD_OOE, "OmniVision OV8610-EDE" },
+ { W9968CF_MOD_ODPVDMPC, "OPCOM Digi Pen VGA Dual Mode Pen Camera" },
+ { W9968CF_MOD_PDPII, "Pretec Digi Pen-II" },
+ { W9968CF_MOD_PDP480, "Pretec DigiPen-480" },
+
+ { -1, NULL }
+};
+
+static struct w9968cf_symbolic_list senlist[] = {
+ { CC_OV76BE, "OV76BE" },
+ { CC_OV7610, "OV7610" },
+ { CC_OV7620, "OV7620" },
+ { CC_OV7620AE, "OV7620AE" },
+ { CC_OV6620, "OV6620" },
+ { CC_OV6630, "OV6630" },
+ { CC_OV6630AE, "OV6630AE" },
+ { CC_OV6630AF, "OV6630AF" },
+ { -1, NULL }
+};
+
+/* Video4Linux1 palettes */
+static struct w9968cf_symbolic_list v4l1_plist[] = {
+ { VIDEO_PALETTE_GREY, "GREY" },
+ { VIDEO_PALETTE_HI240, "HI240" },
+ { VIDEO_PALETTE_RGB565, "RGB565" },
+ { VIDEO_PALETTE_RGB24, "RGB24" },
+ { VIDEO_PALETTE_RGB32, "RGB32" },
+ { VIDEO_PALETTE_RGB555, "RGB555" },
+ { VIDEO_PALETTE_YUV422, "YUV422" },
+ { VIDEO_PALETTE_YUYV, "YUYV" },
+ { VIDEO_PALETTE_UYVY, "UYVY" },
+ { VIDEO_PALETTE_YUV420, "YUV420" },
+ { VIDEO_PALETTE_YUV411, "YUV411" },
+ { VIDEO_PALETTE_RAW, "RAW" },
+ { VIDEO_PALETTE_YUV422P, "YUV422P" },
+ { VIDEO_PALETTE_YUV411P, "YUV411P" },
+ { VIDEO_PALETTE_YUV420P, "YUV420P" },
+ { VIDEO_PALETTE_YUV410P, "YUV410P" },
+ { -1, NULL }
+};
+
+/* Decoder error codes: */
+static struct w9968cf_symbolic_list decoder_errlist[] = {
+ { W9968CF_DEC_ERR_CORRUPTED_DATA, "Corrupted data" },
+ { W9968CF_DEC_ERR_BUF_OVERFLOW, "Buffer overflow" },
+ { W9968CF_DEC_ERR_NO_SOI, "SOI marker not found" },
+ { W9968CF_DEC_ERR_NO_SOF0, "SOF0 marker not found" },
+ { W9968CF_DEC_ERR_NO_SOS, "SOS marker not found" },
+ { W9968CF_DEC_ERR_NO_EOI, "EOI marker not found" },
+ { -1, NULL }
+};
+
+/* URB error codes: */
+static struct w9968cf_symbolic_list urb_errlist[] = {
+ { -ENOMEM, "No memory for allocation of internal structures" },
+ { -ENOSPC, "The host controller's bandwidth is already consumed" },
+ { -ENOENT, "URB was canceled by unlink_urb" },
+ { -EXDEV, "ISO transfer only partially completed" },
+ { -EAGAIN, "Too match scheduled for the future" },
+ { -ENXIO, "URB already queued" },
+ { -EFBIG, "Too much ISO frames requested" },
+ { -ENOSR, "Buffer error (overrun)" },
+ { -EPIPE, "Specified endpoint is stalled (device not responding)"},
+ { -EOVERFLOW, "Babble (bad cable?)" },
+ { -EPROTO, "Bit-stuff error (bad cable?)" },
+ { -EILSEQ, "CRC/Timeout" },
+ { -ETIMEDOUT, "NAK (device does not respond)" },
+ { -1, NULL }
+};
+
+
+
+/****************************************************************************
+ * Memory management functions *
+ ****************************************************************************/
+static void* rvmalloc(unsigned long size)
+{
+ void* mem;
+ unsigned long adr;
+
+ size = PAGE_ALIGN(size);
+ mem = vmalloc_32(size);
+ if (!mem)
+ return NULL;
+
+ memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+ adr = (unsigned long) mem;
+ while (size > 0) {
+ SetPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ return mem;
+}
+
+
+static void rvfree(void* mem, unsigned long size)
+{
+ unsigned long adr;
+
+ if (!mem)
+ return;
+
+ adr = (unsigned long) mem;
+ while ((long) size > 0) {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ vfree(mem);
+}
+
+
+/*--------------------------------------------------------------------------
+ Deallocate previously allocated memory.
+ --------------------------------------------------------------------------*/
+static void w9968cf_deallocate_memory(struct w9968cf_device* cam)
+{
+ u8 i;
+
+ /* Free the isochronous transfer buffers */
+ for (i = 0; i < W9968CF_URBS; i++) {
+ kfree(cam->transfer_buffer[i]);
+ cam->transfer_buffer[i] = NULL;
+ }
+
+ /* Free temporary frame buffer */
+ if (cam->frame_tmp.buffer) {
+ rvfree(cam->frame_tmp.buffer, cam->frame_tmp.size);
+ cam->frame_tmp.buffer = NULL;
+ }
+
+ /* Free helper buffer */
+ if (cam->frame_vpp.buffer) {
+ rvfree(cam->frame_vpp.buffer, cam->frame_vpp.size);
+ cam->frame_vpp.buffer = NULL;
+ }
+
+ /* Free video frame buffers */
+ if (cam->frame[0].buffer) {
+ rvfree(cam->frame[0].buffer, cam->nbuffers*cam->frame[0].size);
+ cam->frame[0].buffer = NULL;
+ }
+
+ cam->nbuffers = 0;
+
+ DBG(5, "Memory successfully deallocated")
+}
+
+
+/*--------------------------------------------------------------------------
+ Allocate memory buffers for USB transfers and video frames.
+ This function is called by open() only.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_allocate_memory(struct w9968cf_device* cam)
+{
+ const u16 p_size = wMaxPacketSize[cam->altsetting-1];
+ void* buff = NULL;
+ unsigned long hw_bufsize, vpp_bufsize;
+ u8 i, bpp;
+
+ /* NOTE: Deallocation is done elsewhere in case of error */
+
+ /* Calculate the max amount of raw data per frame from the device */
+ hw_bufsize = cam->maxwidth*cam->maxheight*2;
+
+ /* Calculate the max buf. size needed for post-processing routines */
+ bpp = (w9968cf_vpp) ? 4 : 2;
+ if (cam->upscaling)
+ vpp_bufsize = max(W9968CF_MAX_WIDTH*W9968CF_MAX_HEIGHT*bpp,
+ cam->maxwidth*cam->maxheight*bpp);
+ else
+ vpp_bufsize = cam->maxwidth*cam->maxheight*bpp;
+
+ /* Allocate memory for the isochronous transfer buffers */
+ for (i = 0; i < W9968CF_URBS; i++) {
+ if (!(cam->transfer_buffer[i] =
+ kzalloc(W9968CF_ISO_PACKETS*p_size, GFP_KERNEL))) {
+ DBG(1, "Couldn't allocate memory for the isochronous "
+ "transfer buffers (%u bytes)",
+ p_size * W9968CF_ISO_PACKETS)
+ return -ENOMEM;
+ }
+ }
+
+ /* Allocate memory for the temporary frame buffer */
+ if (!(cam->frame_tmp.buffer = rvmalloc(hw_bufsize))) {
+ DBG(1, "Couldn't allocate memory for the temporary "
+ "video frame buffer (%lu bytes)", hw_bufsize)
+ return -ENOMEM;
+ }
+ cam->frame_tmp.size = hw_bufsize;
+ cam->frame_tmp.number = -1;
+
+ /* Allocate memory for the helper buffer */
+ if (w9968cf_vpp) {
+ if (!(cam->frame_vpp.buffer = rvmalloc(vpp_bufsize))) {
+ DBG(1, "Couldn't allocate memory for the helper buffer"
+ " (%lu bytes)", vpp_bufsize)
+ return -ENOMEM;
+ }
+ cam->frame_vpp.size = vpp_bufsize;
+ } else
+ cam->frame_vpp.buffer = NULL;
+
+ /* Allocate memory for video frame buffers */
+ cam->nbuffers = cam->max_buffers;
+ while (cam->nbuffers >= 2) {
+ if ((buff = rvmalloc(cam->nbuffers * vpp_bufsize)))
+ break;
+ else
+ cam->nbuffers--;
+ }
+
+ if (!buff) {
+ DBG(1, "Couldn't allocate memory for the video frame buffers")
+ cam->nbuffers = 0;
+ return -ENOMEM;
+ }
+
+ if (cam->nbuffers != cam->max_buffers)
+ DBG(2, "Couldn't allocate memory for %u video frame buffers. "
+ "Only memory for %u buffers has been allocated",
+ cam->max_buffers, cam->nbuffers)
+
+ for (i = 0; i < cam->nbuffers; i++) {
+ cam->frame[i].buffer = buff + i*vpp_bufsize;
+ cam->frame[i].size = vpp_bufsize;
+ cam->frame[i].number = i;
+ /* Circular list */
+ if (i != cam->nbuffers-1)
+ cam->frame[i].next = &cam->frame[i+1];
+ else
+ cam->frame[i].next = &cam->frame[0];
+ cam->frame[i].status = F_UNUSED;
+ }
+
+ DBG(5, "Memory successfully allocated")
+ return 0;
+}
+
+
+
+/****************************************************************************
+ * USB-specific functions *
+ ****************************************************************************/
+
+/*--------------------------------------------------------------------------
+ This is an handler function which is called after the URBs are completed.
+ It collects multiple data packets coming from the camera by putting them
+ into frame buffers: one or more zero data length data packets are used to
+ mark the end of a video frame; the first non-zero data packet is the start
+ of the next video frame; if an error is encountered in a packet, the entire
+ video frame is discarded and grabbed again.
+ If there are no requested frames in the FIFO list, packets are collected into
+ a temporary buffer.
+ --------------------------------------------------------------------------*/
+static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs)
+{
+ struct w9968cf_device* cam = (struct w9968cf_device*)urb->context;
+ struct w9968cf_frame_t** f;
+ unsigned int len, status;
+ void* pos;
+ u8 i;
+ int err = 0;
+
+ if ((!cam->streaming) || cam->disconnected) {
+ DBG(4, "Got interrupt, but not streaming")
+ return;
+ }
+
+ /* "(*f)" will be used instead of "cam->frame_current" */
+ f = &cam->frame_current;
+
+ /* If a frame has been requested and we are grabbing into
+ the temporary frame, we'll switch to that requested frame */
+ if ((*f) == &cam->frame_tmp && *cam->requested_frame) {
+ if (cam->frame_tmp.status == F_GRABBING) {
+ w9968cf_pop_frame(cam, &cam->frame_current);
+ (*f)->status = F_GRABBING;
+ (*f)->length = cam->frame_tmp.length;
+ memcpy((*f)->buffer, cam->frame_tmp.buffer,
+ (*f)->length);
+ DBG(6, "Switched from temp. frame to frame #%d",
+ (*f)->number)
+ }
+ }
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ len = urb->iso_frame_desc[i].actual_length;
+ status = urb->iso_frame_desc[i].status;
+ pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
+
+ if (status && len != 0) {
+ DBG(4, "URB failed, error in data packet "
+ "(error #%u, %s)",
+ status, symbolic(urb_errlist, status))
+ (*f)->status = F_ERROR;
+ continue;
+ }
+
+ if (len) { /* start of frame */
+
+ if ((*f)->status == F_UNUSED) {
+ (*f)->status = F_GRABBING;
+ (*f)->length = 0;
+ }
+
+ /* Buffer overflows shouldn't happen, however...*/
+ if ((*f)->length + len > (*f)->size) {
+ DBG(4, "Buffer overflow: bad data packets")
+ (*f)->status = F_ERROR;
+ }
+
+ if ((*f)->status == F_GRABBING) {
+ memcpy((*f)->buffer + (*f)->length, pos, len);
+ (*f)->length += len;
+ }
+
+ } else if ((*f)->status == F_GRABBING) { /* end of frame */
+
+ DBG(6, "Frame #%d successfully grabbed", (*f)->number)
+
+ if (cam->vpp_flag & VPP_DECOMPRESSION) {
+ err = w9968cf_vpp->check_headers((*f)->buffer,
+ (*f)->length);
+ if (err) {
+ DBG(4, "Skip corrupted frame: %s",
+ symbolic(decoder_errlist, err))
+ (*f)->status = F_UNUSED;
+ continue; /* grab this frame again */
+ }
+ }
+
+ (*f)->status = F_READY;
+ (*f)->queued = 0;
+
+ /* Take a pointer to the new frame from the FIFO list.
+ If the list is empty,we'll use the temporary frame*/
+ if (*cam->requested_frame)
+ w9968cf_pop_frame(cam, &cam->frame_current);
+ else {
+ cam->frame_current = &cam->frame_tmp;
+ (*f)->status = F_UNUSED;
+ }
+
+ } else if ((*f)->status == F_ERROR)
+ (*f)->status = F_UNUSED; /* grab it again */
+
+ PDBGG("Frame length %lu | pack.#%u | pack.len. %u | state %d",
+ (unsigned long)(*f)->length, i, len, (*f)->status)
+
+ } /* end for */
+
+ /* Resubmit this URB */
+ urb->dev = cam->usbdev;
+ urb->status = 0;
+ spin_lock(&cam->urb_lock);
+ if (cam->streaming)
+ if ((err = usb_submit_urb(urb, GFP_ATOMIC))) {
+ cam->misconfigured = 1;
+ DBG(1, "Couldn't resubmit the URB: error %d, %s",
+ err, symbolic(urb_errlist, err))
+ }
+ spin_unlock(&cam->urb_lock);
+
+ /* Wake up the user process */
+ wake_up_interruptible(&cam->wait_queue);
+}
+
+
+/*---------------------------------------------------------------------------
+ Setup the URB structures for the isochronous transfer.
+ Submit the URBs so that the data transfer begins.
+ Return 0 on success, a negative number otherwise.
+ ---------------------------------------------------------------------------*/
+static int w9968cf_start_transfer(struct w9968cf_device* cam)
+{
+ struct usb_device *udev = cam->usbdev;
+ struct urb* urb;
+ const u16 p_size = wMaxPacketSize[cam->altsetting-1];
+ u16 w, h, d;
+ int vidcapt;
+ u32 t_size;
+ int err = 0;
+ s8 i, j;
+
+ for (i = 0; i < W9968CF_URBS; i++) {
+ urb = usb_alloc_urb(W9968CF_ISO_PACKETS, GFP_KERNEL);
+ cam->urb[i] = urb;
+ if (!urb) {
+ for (j = 0; j < i; j++)
+ usb_free_urb(cam->urb[j]);
+ DBG(1, "Couldn't allocate the URB structures")
+ return -ENOMEM;
+ }
+
+ urb->dev = udev;
+ urb->context = (void*)cam;
+ urb->pipe = usb_rcvisocpipe(udev, 1);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->number_of_packets = W9968CF_ISO_PACKETS;
+ urb->complete = w9968cf_urb_complete;
+ urb->transfer_buffer = cam->transfer_buffer[i];
+ urb->transfer_buffer_length = p_size*W9968CF_ISO_PACKETS;
+ urb->interval = 1;
+ for (j = 0; j < W9968CF_ISO_PACKETS; j++) {
+ urb->iso_frame_desc[j].offset = p_size*j;
+ urb->iso_frame_desc[j].length = p_size;
+ }
+ }
+
+ /* Transfer size per frame, in WORD ! */
+ d = cam->hw_depth;
+ w = cam->hw_width;
+ h = cam->hw_height;
+
+ t_size = (w*h*d)/16;
+
+ err = w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */
+ err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* normal operation */
+
+ /* Transfer size */
+ err += w9968cf_write_reg(cam, t_size & 0xffff, 0x3d); /* low bits */
+ err += w9968cf_write_reg(cam, t_size >> 16, 0x3e); /* high bits */
+
+ if (cam->vpp_flag & VPP_DECOMPRESSION)
+ err += w9968cf_upload_quantizationtables(cam);
+
+ vidcapt = w9968cf_read_reg(cam, 0x16); /* read picture settings */
+ err += w9968cf_write_reg(cam, vidcapt|0x8000, 0x16); /* capt. enable */
+
+ err += usb_set_interface(udev, 0, cam->altsetting);
+ err += w9968cf_write_reg(cam, 0x8a05, 0x3c); /* USB FIFO enable */
+
+ if (err || (vidcapt < 0)) {
+ for (i = 0; i < W9968CF_URBS; i++)
+ usb_free_urb(cam->urb[i]);
+ DBG(1, "Couldn't tell the camera to start the data transfer")
+ return err;
+ }
+
+ w9968cf_init_framelist(cam);
+
+ /* Begin to grab into the temporary buffer */
+ cam->frame_tmp.status = F_UNUSED;
+ cam->frame_tmp.queued = 0;
+ cam->frame_current = &cam->frame_tmp;
+
+ if (!(cam->vpp_flag & VPP_DECOMPRESSION))
+ DBG(5, "Isochronous transfer size: %lu bytes/frame",
+ (unsigned long)t_size*2)
+
+ DBG(5, "Starting the isochronous transfer...")
+
+ cam->streaming = 1;
+
+ /* Submit the URBs */
+ for (i = 0; i < W9968CF_URBS; i++) {
+ err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
+ if (err) {
+ cam->streaming = 0;
+ for (j = i-1; j >= 0; j--) {
+ usb_kill_urb(cam->urb[j]);
+ usb_free_urb(cam->urb[j]);
+ }
+ DBG(1, "Couldn't send a transfer request to the "
+ "USB core (error #%d, %s)", err,
+ symbolic(urb_errlist, err))
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Stop the isochronous transfer and set alternate setting to 0 (0Mb/s).
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_stop_transfer(struct w9968cf_device* cam)
+{
+ struct usb_device *udev = cam->usbdev;
+ unsigned long lock_flags;
+ int err = 0;
+ s8 i;
+
+ if (!cam->streaming)
+ return 0;
+
+ /* This avoids race conditions with usb_submit_urb()
+ in the URB completition handler */
+ spin_lock_irqsave(&cam->urb_lock, lock_flags);
+ cam->streaming = 0;
+ spin_unlock_irqrestore(&cam->urb_lock, lock_flags);
+
+ for (i = W9968CF_URBS-1; i >= 0; i--)
+ if (cam->urb[i]) {
+ usb_kill_urb(cam->urb[i]);
+ usb_free_urb(cam->urb[i]);
+ cam->urb[i] = NULL;
+ }
+
+ if (cam->disconnected)
+ goto exit;
+
+ err = w9968cf_write_reg(cam, 0x0a05, 0x3c); /* stop USB transfer */
+ err += usb_set_interface(udev, 0, 0); /* 0 Mb/s */
+ err += w9968cf_write_reg(cam, 0x0000, 0x39); /* disable JPEG encoder */
+ err += w9968cf_write_reg(cam, 0x0000, 0x16); /* stop video capture */
+
+ if (err) {
+ DBG(2, "Failed to tell the camera to stop the isochronous "
+ "transfer. However this is not a critical error.")
+ return -EIO;
+ }
+
+exit:
+ DBG(5, "Isochronous transfer stopped")
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Write a W9968CF register.
+ Return 0 on success, -1 otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_write_reg(struct w9968cf_device* cam, u16 value, u16 index)
+{
+ struct usb_device* udev = cam->usbdev;
+ int res;
+
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0,
+ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+ value, index, NULL, 0, W9968CF_USB_CTRL_TIMEOUT);
+
+ if (res < 0)
+ DBG(4, "Failed to write a register "
+ "(value 0x%04X, index 0x%02X, error #%d, %s)",
+ value, index, res, symbolic(urb_errlist, res))
+
+ return (res >= 0) ? 0 : -1;
+}
+
+
+/*--------------------------------------------------------------------------
+ Read a W9968CF register.
+ Return the register value on success, -1 otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_read_reg(struct w9968cf_device* cam, u16 index)
+{
+ struct usb_device* udev = cam->usbdev;
+ u16* buff = cam->control_buffer;
+ int res;
+
+ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 1,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, buff, 2, W9968CF_USB_CTRL_TIMEOUT);
+
+ if (res < 0)
+ DBG(4, "Failed to read a register "
+ "(index 0x%02X, error #%d, %s)",
+ index, res, symbolic(urb_errlist, res))
+
+ return (res >= 0) ? (int)(*buff) : -1;
+}
+
+
+/*--------------------------------------------------------------------------
+ Write 64-bit data to the fast serial bus registers.
+ Return 0 on success, -1 otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_write_fsb(struct w9968cf_device* cam, u16* data)
+{
+ struct usb_device* udev = cam->usbdev;
+ u16 value;
+ int res;
+
+ value = *data++;
+
+ res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0,
+ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+ value, 0x06, data, 6, W9968CF_USB_CTRL_TIMEOUT);
+
+ if (res < 0)
+ DBG(4, "Failed to write the FSB registers "
+ "(error #%d, %s)", res, symbolic(urb_errlist, res))
+
+ return (res >= 0) ? 0 : -1;
+}
+
+
+/*--------------------------------------------------------------------------
+ Write data to the serial bus control register.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_write_sb(struct w9968cf_device* cam, u16 value)
+{
+ int err = 0;
+
+ err = w9968cf_write_reg(cam, value, 0x01);
+ udelay(W9968CF_I2C_BUS_DELAY);
+
+ return err;
+}
+
+
+/*--------------------------------------------------------------------------
+ Read data from the serial bus control register.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_read_sb(struct w9968cf_device* cam)
+{
+ int v = 0;
+
+ v = w9968cf_read_reg(cam, 0x01);
+ udelay(W9968CF_I2C_BUS_DELAY);
+
+ return v;
+}
+
+
+/*--------------------------------------------------------------------------
+ Upload quantization tables for the JPEG compression.
+ This function is called by w9968cf_start_transfer().
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_upload_quantizationtables(struct w9968cf_device* cam)
+{
+ u16 a, b;
+ int err = 0, i, j;
+
+ err += w9968cf_write_reg(cam, 0x0010, 0x39); /* JPEG clock enable */
+
+ for (i = 0, j = 0; i < 32; i++, j += 2) {
+ a = Y_QUANTABLE[j] | ((unsigned)(Y_QUANTABLE[j+1]) << 8);
+ b = UV_QUANTABLE[j] | ((unsigned)(UV_QUANTABLE[j+1]) << 8);
+ err += w9968cf_write_reg(cam, a, 0x40+i);
+ err += w9968cf_write_reg(cam, b, 0x60+i);
+ }
+ err += w9968cf_write_reg(cam, 0x0012, 0x39); /* JPEG encoder enable */
+
+ return err;
+}
+
+
+
+/****************************************************************************
+ * Low-level I2C I/O functions. *
+ * The adapter supports the following I2C transfer functions: *
+ * i2c_adap_fastwrite_byte_data() (at 400 kHz bit frequency only) *
+ * i2c_adap_read_byte_data() *
+ * i2c_adap_read_byte() *
+ ****************************************************************************/
+
+static int w9968cf_smbus_start(struct w9968cf_device* cam)
+{
+ int err = 0;
+
+ err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */
+ err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */
+
+ return err;
+}
+
+
+static int w9968cf_smbus_stop(struct w9968cf_device* cam)
+{
+ int err = 0;
+
+ err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */
+ err += w9968cf_write_sb(cam, 0x0013); /* SDE=1, SDA=1, SCL=1 */
+
+ return err;
+}
+
+
+static int w9968cf_smbus_write_byte(struct w9968cf_device* cam, u8 v)
+{
+ u8 bit;
+ int err = 0, sda;
+
+ for (bit = 0 ; bit < 8 ; bit++) {
+ sda = (v & 0x80) ? 2 : 0;
+ v <<= 1;
+ /* SDE=1, SDA=sda, SCL=0 */
+ err += w9968cf_write_sb(cam, 0x10 | sda);
+ /* SDE=1, SDA=sda, SCL=1 */
+ err += w9968cf_write_sb(cam, 0x11 | sda);
+ /* SDE=1, SDA=sda, SCL=0 */
+ err += w9968cf_write_sb(cam, 0x10 | sda);
+ }
+
+ return err;
+}
+
+
+static int w9968cf_smbus_read_byte(struct w9968cf_device* cam, u8* v)
+{
+ u8 bit;
+ int err = 0;
+
+ *v = 0;
+ for (bit = 0 ; bit < 8 ; bit++) {
+ *v <<= 1;
+ err += w9968cf_write_sb(cam, 0x0013);
+ *v |= (w9968cf_read_sb(cam) & 0x0008) ? 1 : 0;
+ err += w9968cf_write_sb(cam, 0x0012);
+ }
+
+ return err;
+}
+
+
+static int w9968cf_smbus_write_ack(struct w9968cf_device* cam)
+{
+ int err = 0;
+
+ err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */
+ err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */
+ err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */
+
+ return err;
+}
+
+
+static int w9968cf_smbus_read_ack(struct w9968cf_device* cam)
+{
+ int err = 0, sda;
+
+ err += w9968cf_write_sb(cam, 0x0013); /* SDE=1, SDA=1, SCL=1 */
+ sda = (w9968cf_read_sb(cam) & 0x08) ? 1 : 0; /* sda = SDA */
+ err += w9968cf_write_sb(cam, 0x0012); /* SDE=1, SDA=1, SCL=0 */
+ if (sda < 0)
+ err += sda;
+ if (sda == 1) {
+ DBG(6, "Couldn't receive the ACK")
+ err += -1;
+ }
+
+ return err;
+}
+
+
+/* This seems to refresh the communication through the serial bus */
+static int w9968cf_smbus_refresh_bus(struct w9968cf_device* cam)
+{
+ int err = 0, j;
+
+ for (j = 1; j <= 10; j++) {
+ err = w9968cf_write_reg(cam, 0x0020, 0x01);
+ err += w9968cf_write_reg(cam, 0x0000, 0x01);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
+
+/* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */
+static int
+w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device* cam,
+ u16 address, u8 subaddress,u8 value)
+{
+ u16* data = cam->data_buffer;
+ int err = 0;
+
+ err += w9968cf_smbus_refresh_bus(cam);
+
+ /* Enable SBUS outputs */
+ err += w9968cf_write_sb(cam, 0x0020);
+
+ data[0] = 0x082f | ((address & 0x80) ? 0x1500 : 0x0);
+ data[0] |= (address & 0x40) ? 0x4000 : 0x0;
+ data[1] = 0x2082 | ((address & 0x40) ? 0x0005 : 0x0);
+ data[1] |= (address & 0x20) ? 0x0150 : 0x0;
+ data[1] |= (address & 0x10) ? 0x5400 : 0x0;
+ data[2] = 0x8208 | ((address & 0x08) ? 0x0015 : 0x0);
+ data[2] |= (address & 0x04) ? 0x0540 : 0x0;
+ data[2] |= (address & 0x02) ? 0x5000 : 0x0;
+ data[3] = 0x1d20 | ((address & 0x02) ? 0x0001 : 0x0);
+ data[3] |= (address & 0x01) ? 0x0054 : 0x0;
+
+ err += w9968cf_write_fsb(cam, data);
+
+ data[0] = 0x8208 | ((subaddress & 0x80) ? 0x0015 : 0x0);
+ data[0] |= (subaddress & 0x40) ? 0x0540 : 0x0;
+ data[0] |= (subaddress & 0x20) ? 0x5000 : 0x0;
+ data[1] = 0x0820 | ((subaddress & 0x20) ? 0x0001 : 0x0);
+ data[1] |= (subaddress & 0x10) ? 0x0054 : 0x0;
+ data[1] |= (subaddress & 0x08) ? 0x1500 : 0x0;
+ data[1] |= (subaddress & 0x04) ? 0x4000 : 0x0;
+ data[2] = 0x2082 | ((subaddress & 0x04) ? 0x0005 : 0x0);
+ data[2] |= (subaddress & 0x02) ? 0x0150 : 0x0;
+ data[2] |= (subaddress & 0x01) ? 0x5400 : 0x0;
+ data[3] = 0x001d;
+
+ err += w9968cf_write_fsb(cam, data);
+
+ data[0] = 0x8208 | ((value & 0x80) ? 0x0015 : 0x0);
+ data[0] |= (value & 0x40) ? 0x0540 : 0x0;
+ data[0] |= (value & 0x20) ? 0x5000 : 0x0;
+ data[1] = 0x0820 | ((value & 0x20) ? 0x0001 : 0x0);
+ data[1] |= (value & 0x10) ? 0x0054 : 0x0;
+ data[1] |= (value & 0x08) ? 0x1500 : 0x0;
+ data[1] |= (value & 0x04) ? 0x4000 : 0x0;
+ data[2] = 0x2082 | ((value & 0x04) ? 0x0005 : 0x0);
+ data[2] |= (value & 0x02) ? 0x0150 : 0x0;
+ data[2] |= (value & 0x01) ? 0x5400 : 0x0;
+ data[3] = 0xfe1d;
+
+ err += w9968cf_write_fsb(cam, data);
+
+ /* Disable SBUS outputs */
+ err += w9968cf_write_sb(cam, 0x0000);
+
+ if (!err)
+ DBG(5, "I2C write byte data done, addr.0x%04X, subaddr.0x%02X "
+ "value 0x%02X", address, subaddress, value)
+ else
+ DBG(5, "I2C write byte data failed, addr.0x%04X, "
+ "subaddr.0x%02X, value 0x%02X",
+ address, subaddress, value)
+
+ return err;
+}
+
+
+/* SMBus protocol: S Addr Wr [A] Subaddr [A] P S Addr+1 Rd [A] [Value] NA P */
+static int
+w9968cf_i2c_adap_read_byte_data(struct w9968cf_device* cam,
+ u16 address, u8 subaddress,
+ u8* value)
+{
+ int err = 0;
+
+ /* Serial data enable */
+ err += w9968cf_write_sb(cam, 0x0013); /* don't change ! */
+
+ err += w9968cf_smbus_start(cam);
+ err += w9968cf_smbus_write_byte(cam, address);
+ err += w9968cf_smbus_read_ack(cam);
+ err += w9968cf_smbus_write_byte(cam, subaddress);
+ err += w9968cf_smbus_read_ack(cam);
+ err += w9968cf_smbus_stop(cam);
+ err += w9968cf_smbus_start(cam);
+ err += w9968cf_smbus_write_byte(cam, address + 1);
+ err += w9968cf_smbus_read_ack(cam);
+ err += w9968cf_smbus_read_byte(cam, value);
+ err += w9968cf_smbus_write_ack(cam);
+ err += w9968cf_smbus_stop(cam);
+
+ /* Serial data disable */
+ err += w9968cf_write_sb(cam, 0x0000);
+
+ if (!err)
+ DBG(5, "I2C read byte data done, addr.0x%04X, "
+ "subaddr.0x%02X, value 0x%02X",
+ address, subaddress, *value)
+ else
+ DBG(5, "I2C read byte data failed, addr.0x%04X, "
+ "subaddr.0x%02X, wrong value 0x%02X",
+ address, subaddress, *value)
+
+ return err;
+}
+
+
+/* SMBus protocol: S Addr+1 Rd [A] [Value] NA P */
+static int
+w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam,
+ u16 address, u8* value)
+{
+ int err = 0;
+
+ /* Serial data enable */
+ err += w9968cf_write_sb(cam, 0x0013);
+
+ err += w9968cf_smbus_start(cam);
+ err += w9968cf_smbus_write_byte(cam, address + 1);
+ err += w9968cf_smbus_read_ack(cam);
+ err += w9968cf_smbus_read_byte(cam, value);
+ err += w9968cf_smbus_write_ack(cam);
+ err += w9968cf_smbus_stop(cam);
+
+ /* Serial data disable */
+ err += w9968cf_write_sb(cam, 0x0000);
+
+ if (!err)
+ DBG(5, "I2C read byte done, addr.0x%04X, "
+ "value 0x%02X", address, *value)
+ else
+ DBG(5, "I2C read byte failed, addr.0x%04X, "
+ "wrong value 0x%02X", address, *value)
+
+ return err;
+}
+
+
+/* SMBus protocol: S Addr Wr [A] Value [A] P */
+static int
+w9968cf_i2c_adap_write_byte(struct w9968cf_device* cam,
+ u16 address, u8 value)
+{
+ DBG(4, "i2c_write_byte() is an unsupported transfer mode")
+ return -EINVAL;
+}
+
+
+
+/****************************************************************************
+ * I2C interface to kernel *
+ ****************************************************************************/
+
+static int
+w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
+ unsigned short flags, char read_write, u8 command,
+ int size, union i2c_smbus_data *data)
+{
+ struct w9968cf_device* cam = i2c_get_adapdata(adapter);
+ u8 i;
+ int err = 0;
+
+ switch (addr) {
+ case OV6xx0_SID:
+ case OV7xx0_SID:
+ break;
+ default:
+ DBG(4, "Rejected slave ID 0x%04X", addr)
+ return -EINVAL;
+ }
+
+ if (size == I2C_SMBUS_BYTE) {
+ /* Why addr <<= 1? See OVXXX0_SID defines in ovcamchip.h */
+ addr <<= 1;
+
+ if (read_write == I2C_SMBUS_WRITE)
+ err = w9968cf_i2c_adap_write_byte(cam, addr, command);
+ else if (read_write == I2C_SMBUS_READ)
+ err = w9968cf_i2c_adap_read_byte(cam,addr,&data->byte);
+
+ } else if (size == I2C_SMBUS_BYTE_DATA) {
+ addr <<= 1;
+
+ if (read_write == I2C_SMBUS_WRITE)
+ err = w9968cf_i2c_adap_fastwrite_byte_data(cam, addr,
+ command, data->byte);
+ else if (read_write == I2C_SMBUS_READ) {
+ for (i = 1; i <= W9968CF_I2C_RW_RETRIES; i++) {
+ err = w9968cf_i2c_adap_read_byte_data(cam,addr,
+ command, &data->byte);
+ if (err) {
+ if (w9968cf_smbus_refresh_bus(cam)) {
+ err = -EIO;
+ break;
+ }
+ } else
+ break;
+ }
+
+ } else
+ return -EINVAL;
+
+ } else {
+ DBG(4, "Unsupported I2C transfer mode (%d)", size)
+ return -EINVAL;
+ }
+
+ return err;
+}
+
+
+static u32 w9968cf_i2c_func(struct i2c_adapter* adap)
+{
+ return I2C_FUNC_SMBUS_READ_BYTE |
+ I2C_FUNC_SMBUS_READ_BYTE_DATA |
+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
+}
+
+
+static int w9968cf_i2c_attach_inform(struct i2c_client* client)
+{
+ struct w9968cf_device* cam = i2c_get_adapdata(client->adapter);
+ int id = client->driver->id, err = 0;
+
+ if (id == I2C_DRIVERID_OVCAMCHIP) {
+ cam->sensor_client = client;
+ err = w9968cf_sensor_init(cam);
+ if (err) {
+ cam->sensor_client = NULL;
+ return err;
+ }
+ } else {
+ DBG(4, "Rejected client [%s] with driver [%s]",
+ client->name, client->driver->driver.name)
+ return -EINVAL;
+ }
+
+ DBG(5, "I2C attach client [%s] with driver [%s]",
+ client->name, client->driver->driver.name)
+
+ return 0;
+}
+
+
+static int w9968cf_i2c_detach_inform(struct i2c_client* client)
+{
+ struct w9968cf_device* cam = i2c_get_adapdata(client->adapter);
+
+ if (cam->sensor_client == client)
+ cam->sensor_client = NULL;
+
+ DBG(5, "I2C detach client [%s]", client->name)
+
+ return 0;
+}
+
+
+static int
+w9968cf_i2c_control(struct i2c_adapter* adapter, unsigned int cmd,
+ unsigned long arg)
+{
+ return 0;
+}
+
+
+static int w9968cf_i2c_init(struct w9968cf_device* cam)
+{
+ int err = 0;
+
+ static struct i2c_algorithm algo = {
+ .smbus_xfer = w9968cf_i2c_smbus_xfer,
+ .algo_control = w9968cf_i2c_control,
+ .functionality = w9968cf_i2c_func,
+ };
+
+ static struct i2c_adapter adap = {
+ .id = I2C_HW_SMBUS_W9968CF,
+ .class = I2C_CLASS_CAM_DIGITAL,
+ .owner = THIS_MODULE,
+ .client_register = w9968cf_i2c_attach_inform,
+ .client_unregister = w9968cf_i2c_detach_inform,
+ .algo = &algo,
+ };
+
+ memcpy(&cam->i2c_adapter, &adap, sizeof(struct i2c_adapter));
+ strcpy(cam->i2c_adapter.name, "w9968cf");
+ i2c_set_adapdata(&cam->i2c_adapter, cam);
+
+ DBG(6, "Registering I2C adapter with kernel...")
+
+ err = i2c_add_adapter(&cam->i2c_adapter);
+ if (err)
+ DBG(1, "Failed to register the I2C adapter")
+ else
+ DBG(5, "I2C adapter registered")
+
+ return err;
+}
+
+
+
+/****************************************************************************
+ * Helper functions *
+ ****************************************************************************/
+
+/*--------------------------------------------------------------------------
+ Turn on the LED on some webcams. A beep should be heard too.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_turn_on_led(struct w9968cf_device* cam)
+{
+ int err = 0;
+
+ err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power-down */
+ err += w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */
+ err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* normal operation */
+ err += w9968cf_write_reg(cam, 0x0010, 0x01); /* serial bus, SDS high */
+ err += w9968cf_write_reg(cam, 0x0000, 0x01); /* serial bus, SDS low */
+ err += w9968cf_write_reg(cam, 0x0010, 0x01); /* ..high 'beep-beep' */
+
+ if (err)
+ DBG(2, "Couldn't turn on the LED")
+
+ DBG(5, "LED turned on")
+
+ return err;
+}
+
+
+/*--------------------------------------------------------------------------
+ Write some registers for the device initialization.
+ This function is called once on open().
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_init_chip(struct w9968cf_device* cam)
+{
+ unsigned long hw_bufsize = cam->maxwidth*cam->maxheight*2,
+ y0 = 0x0000,
+ u0 = y0 + hw_bufsize/2,
+ v0 = u0 + hw_bufsize/4,
+ y1 = v0 + hw_bufsize/4,
+ u1 = y1 + hw_bufsize/2,
+ v1 = u1 + hw_bufsize/4;
+ int err = 0;
+
+ err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power off */
+ err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* power on */
+
+ err += w9968cf_write_reg(cam, 0x405d, 0x03); /* DRAM timings */
+ err += w9968cf_write_reg(cam, 0x0030, 0x04); /* SDRAM timings */
+
+ err += w9968cf_write_reg(cam, y0 & 0xffff, 0x20); /* Y buf.0, low */
+ err += w9968cf_write_reg(cam, y0 >> 16, 0x21); /* Y buf.0, high */
+ err += w9968cf_write_reg(cam, u0 & 0xffff, 0x24); /* U buf.0, low */
+ err += w9968cf_write_reg(cam, u0 >> 16, 0x25); /* U buf.0, high */
+ err += w9968cf_write_reg(cam, v0 & 0xffff, 0x28); /* V buf.0, low */
+ err += w9968cf_write_reg(cam, v0 >> 16, 0x29); /* V buf.0, high */
+
+ err += w9968cf_write_reg(cam, y1 & 0xffff, 0x22); /* Y buf.1, low */
+ err += w9968cf_write_reg(cam, y1 >> 16, 0x23); /* Y buf.1, high */
+ err += w9968cf_write_reg(cam, u1 & 0xffff, 0x26); /* U buf.1, low */
+ err += w9968cf_write_reg(cam, u1 >> 16, 0x27); /* U buf.1, high */
+ err += w9968cf_write_reg(cam, v1 & 0xffff, 0x2a); /* V buf.1, low */
+ err += w9968cf_write_reg(cam, v1 >> 16, 0x2b); /* V buf.1, high */
+
+ err += w9968cf_write_reg(cam, y1 & 0xffff, 0x32); /* JPEG buf 0 low */
+ err += w9968cf_write_reg(cam, y1 >> 16, 0x33); /* JPEG buf 0 high */
+
+ err += w9968cf_write_reg(cam, y1 & 0xffff, 0x34); /* JPEG buf 1 low */
+ err += w9968cf_write_reg(cam, y1 >> 16, 0x35); /* JPEG bug 1 high */
+
+ err += w9968cf_write_reg(cam, 0x0000, 0x36);/* JPEG restart interval */
+ err += w9968cf_write_reg(cam, 0x0804, 0x37);/*JPEG VLE FIFO threshold*/
+ err += w9968cf_write_reg(cam, 0x0000, 0x38);/* disable hw up-scaling */
+ err += w9968cf_write_reg(cam, 0x0000, 0x3f); /* JPEG/MCTL test data */
+
+ err += w9968cf_set_picture(cam, cam->picture); /* this before */
+ err += w9968cf_set_window(cam, cam->window);
+
+ if (err)
+ DBG(1, "Chip initialization failed")
+ else
+ DBG(5, "Chip successfully initialized")
+
+ return err;
+}
+
+
+/*--------------------------------------------------------------------------
+ Return non-zero if the palette is supported, 0 otherwise.
+ --------------------------------------------------------------------------*/
+static inline u16 w9968cf_valid_palette(u16 palette)
+{
+ u8 i = 0;
+ while (w9968cf_formatlist[i].palette != 0) {
+ if (palette == w9968cf_formatlist[i].palette)
+ return palette;
+ i++;
+ }
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Return the depth corresponding to the given palette.
+ Palette _must_ be supported !
+ --------------------------------------------------------------------------*/
+static inline u16 w9968cf_valid_depth(u16 palette)
+{
+ u8 i=0;
+ while (w9968cf_formatlist[i].palette != palette)
+ i++;
+
+ return w9968cf_formatlist[i].depth;
+}
+
+
+/*--------------------------------------------------------------------------
+ Return non-zero if the format requires decompression, 0 otherwise.
+ --------------------------------------------------------------------------*/
+static inline u8 w9968cf_need_decompression(u16 palette)
+{
+ u8 i = 0;
+ while (w9968cf_formatlist[i].palette != 0) {
+ if (palette == w9968cf_formatlist[i].palette)
+ return w9968cf_formatlist[i].compression;
+ i++;
+ }
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Change the picture settings of the camera.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int
+w9968cf_set_picture(struct w9968cf_device* cam, struct video_picture pict)
+{
+ u16 fmt, hw_depth, hw_palette, reg_v = 0x0000;
+ int err = 0;
+
+ /* Make sure we are using a valid depth */
+ pict.depth = w9968cf_valid_depth(pict.palette);
+
+ fmt = pict.palette;
+
+ hw_depth = pict.depth; /* depth used by the winbond chip */
+ hw_palette = pict.palette; /* palette used by the winbond chip */
+
+ /* VS & HS polarities */
+ reg_v = (cam->vs_polarity << 12) | (cam->hs_polarity << 11);
+
+ switch (fmt)
+ {
+ case VIDEO_PALETTE_UYVY:
+ reg_v |= 0x0000;
+ cam->vpp_flag = VPP_NONE;
+ break;
+ case VIDEO_PALETTE_YUV422P:
+ reg_v |= 0x0002;
+ cam->vpp_flag = VPP_DECOMPRESSION;
+ break;
+ case VIDEO_PALETTE_YUV420:
+ case VIDEO_PALETTE_YUV420P:
+ reg_v |= 0x0003;
+ cam->vpp_flag = VPP_DECOMPRESSION;
+ break;
+ case VIDEO_PALETTE_YUYV:
+ case VIDEO_PALETTE_YUV422:
+ reg_v |= 0x0000;
+ cam->vpp_flag = VPP_SWAP_YUV_BYTES;
+ hw_palette = VIDEO_PALETTE_UYVY;
+ break;
+ /* Original video is used instead of RGBX palettes.
+ Software conversion later. */
+ case VIDEO_PALETTE_GREY:
+ case VIDEO_PALETTE_RGB555:
+ case VIDEO_PALETTE_RGB565:
+ case VIDEO_PALETTE_RGB24:
+ case VIDEO_PALETTE_RGB32:
+ reg_v |= 0x0000; /* UYVY 16 bit is used */
+ hw_depth = 16;
+ hw_palette = VIDEO_PALETTE_UYVY;
+ cam->vpp_flag = VPP_UYVY_TO_RGBX;
+ break;
+ }
+
+ /* NOTE: due to memory issues, it is better to disable the hardware
+ double buffering during compression */
+ if (cam->double_buffer && !(cam->vpp_flag & VPP_DECOMPRESSION))
+ reg_v |= 0x0080;
+
+ if (cam->clamping)
+ reg_v |= 0x0020;
+
+ if (cam->filter_type == 1)
+ reg_v |= 0x0008;
+ else if (cam->filter_type == 2)
+ reg_v |= 0x000c;
+
+ if ((err = w9968cf_write_reg(cam, reg_v, 0x16)))
+ goto error;
+
+ if ((err = w9968cf_sensor_update_picture(cam, pict)))
+ goto error;
+
+ /* If all went well, update the device data structure */
+ memcpy(&cam->picture, &pict, sizeof(pict));
+ cam->hw_depth = hw_depth;
+ cam->hw_palette = hw_palette;
+
+ /* Settings changed, so we clear the frame buffers */
+ memset(cam->frame[0].buffer, 0, cam->nbuffers*cam->frame[0].size);
+
+ DBG(4, "Palette is %s, depth is %u bpp",
+ symbolic(v4l1_plist, pict.palette), pict.depth)
+
+ return 0;
+
+error:
+ DBG(1, "Failed to change picture settings")
+ return err;
+}
+
+
+/*--------------------------------------------------------------------------
+ Change the capture area size of the camera.
+ This function _must_ be called _after_ w9968cf_set_picture().
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int
+w9968cf_set_window(struct w9968cf_device* cam, struct video_window win)
+{
+ u16 x, y, w, h, scx, scy, cw, ch, ax, ay;
+ unsigned long fw, fh;
+ struct ovcamchip_window s_win;
+ int err = 0;
+
+ /* Work around to avoid FP arithmetics */
+ #define __SC(x) ((x) << 10)
+ #define __UNSC(x) ((x) >> 10)
+
+ /* Make sure we are using a supported resolution */
+ if ((err = w9968cf_adjust_window_size(cam, (u16*)&win.width,
+ (u16*)&win.height)))
+ goto error;
+
+ /* Scaling factors */
+ fw = __SC(win.width) / cam->maxwidth;
+ fh = __SC(win.height) / cam->maxheight;
+
+ /* Set up the width and height values used by the chip */
+ if ((win.width > cam->maxwidth) || (win.height > cam->maxheight)) {
+ cam->vpp_flag |= VPP_UPSCALE;
+ /* Calculate largest w,h mantaining the same w/h ratio */
+ w = (fw >= fh) ? cam->maxwidth : __SC(win.width)/fh;
+ h = (fw >= fh) ? __SC(win.height)/fw : cam->maxheight;
+ if (w < cam->minwidth) /* just in case */
+ w = cam->minwidth;
+ if (h < cam->minheight) /* just in case */
+ h = cam->minheight;
+ } else {
+ cam->vpp_flag &= ~VPP_UPSCALE;
+ w = win.width;
+ h = win.height;
+ }
+
+ /* x,y offsets of the cropped area */
+ scx = cam->start_cropx;
+ scy = cam->start_cropy;
+
+ /* Calculate cropped area manteining the right w/h ratio */
+ if (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE)) {
+ cw = (fw >= fh) ? cam->maxwidth : __SC(win.width)/fh;
+ ch = (fw >= fh) ? __SC(win.height)/fw : cam->maxheight;
+ } else {
+ cw = w;
+ ch = h;
+ }
+
+ /* Setup the window of the sensor */
+ s_win.format = VIDEO_PALETTE_UYVY;
+ s_win.width = cam->maxwidth;
+ s_win.height = cam->maxheight;
+ s_win.quarter = 0; /* full progressive video */
+
+ /* Center it */
+ s_win.x = (s_win.width - cw) / 2;
+ s_win.y = (s_win.height - ch) / 2;
+
+ /* Clock divisor */
+ if (cam->clockdiv >= 0)
+ s_win.clockdiv = cam->clockdiv; /* manual override */
+ else
+ switch (cam->sensor) {
+ case CC_OV6620:
+ s_win.clockdiv = 0;
+ break;
+ case CC_OV6630:
+ s_win.clockdiv = 0;
+ break;
+ case CC_OV76BE:
+ case CC_OV7610:
+ case CC_OV7620:
+ s_win.clockdiv = 0;
+ break;
+ default:
+ s_win.clockdiv = W9968CF_DEF_CLOCKDIVISOR;
+ }
+
+ /* We have to scale win.x and win.y offsets */
+ if ( (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE))
+ || (cam->vpp_flag & VPP_UPSCALE) ) {
+ ax = __SC(win.x)/fw;
+ ay = __SC(win.y)/fh;
+ } else {
+ ax = win.x;
+ ay = win.y;
+ }
+
+ if ((ax + cw) > cam->maxwidth)
+ ax = cam->maxwidth - cw;
+
+ if ((ay + ch) > cam->maxheight)
+ ay = cam->maxheight - ch;
+
+ /* Adjust win.x, win.y */
+ if ( (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE))
+ || (cam->vpp_flag & VPP_UPSCALE) ) {
+ win.x = __UNSC(ax*fw);
+ win.y = __UNSC(ay*fh);
+ } else {
+ win.x = ax;
+ win.y = ay;
+ }
+
+ /* Offsets used by the chip */
+ x = ax + s_win.x;
+ y = ay + s_win.y;
+
+ /* Go ! */
+ if ((err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_MODE, &s_win)))
+ goto error;
+
+ err += w9968cf_write_reg(cam, scx + x, 0x10);
+ err += w9968cf_write_reg(cam, scy + y, 0x11);
+ err += w9968cf_write_reg(cam, scx + x + cw, 0x12);
+ err += w9968cf_write_reg(cam, scy + y + ch, 0x13);
+ err += w9968cf_write_reg(cam, w, 0x14);
+ err += w9968cf_write_reg(cam, h, 0x15);
+
+ /* JPEG width & height */
+ err += w9968cf_write_reg(cam, w, 0x30);
+ err += w9968cf_write_reg(cam, h, 0x31);
+
+ /* Y & UV frame buffer strides (in WORD) */
+ if (cam->vpp_flag & VPP_DECOMPRESSION) {
+ err += w9968cf_write_reg(cam, w/2, 0x2c);
+ err += w9968cf_write_reg(cam, w/4, 0x2d);
+ } else
+ err += w9968cf_write_reg(cam, w, 0x2c);
+
+ if (err)
+ goto error;
+
+ /* If all went well, update the device data structure */
+ memcpy(&cam->window, &win, sizeof(win));
+ cam->hw_width = w;
+ cam->hw_height = h;
+
+ /* Settings changed, so we clear the frame buffers */
+ memset(cam->frame[0].buffer, 0, cam->nbuffers*cam->frame[0].size);
+
+ DBG(4, "The capture area is %dx%d, Offset (x,y)=(%u,%u)",
+ win.width, win.height, win.x, win.y)
+
+ PDBGG("x=%u ,y=%u, w=%u, h=%u, ax=%u, ay=%u, s_win.x=%u, s_win.y=%u, "
+ "cw=%u, ch=%u, win.x=%u, win.y=%u, win.width=%u, win.height=%u",
+ x, y, w, h, ax, ay, s_win.x, s_win.y, cw, ch, win.x, win.y,
+ win.width, win.height)
+
+ return 0;
+
+error:
+ DBG(1, "Failed to change the capture area size")
+ return err;
+}
+
+
+/*--------------------------------------------------------------------------
+ Adjust the asked values for window width and height.
+ Return 0 on success, -1 otherwise.
+ --------------------------------------------------------------------------*/
+static int
+w9968cf_adjust_window_size(struct w9968cf_device* cam, u16* width, u16* height)
+{
+ u16 maxw, maxh;
+
+ if ((*width < cam->minwidth) || (*height < cam->minheight))
+ return -ERANGE;
+
+ maxw = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) &&
+ w9968cf_vpp ? max((u16)W9968CF_MAX_WIDTH, cam->maxwidth)
+ : cam->maxwidth;
+ maxh = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) &&
+ w9968cf_vpp ? max((u16)W9968CF_MAX_HEIGHT, cam->maxheight)
+ : cam->maxheight;
+
+ if (*width > maxw)
+ *width = maxw;
+ if (*height > maxh)
+ *height = maxh;
+
+ if (cam->vpp_flag & VPP_DECOMPRESSION) {
+ *width &= ~15L; /* multiple of 16 */
+ *height &= ~15L;
+ }
+
+ PDBGG("Window size adjusted w=%u, h=%u ", *width, *height)
+
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Initialize the FIFO list of requested frames.
+ --------------------------------------------------------------------------*/
+static void w9968cf_init_framelist(struct w9968cf_device* cam)
+{
+ u8 i;
+
+ for (i = 0; i < cam->nbuffers; i++) {
+ cam->requested_frame[i] = NULL;
+ cam->frame[i].queued = 0;
+ cam->frame[i].status = F_UNUSED;
+ }
+}
+
+
+/*--------------------------------------------------------------------------
+ Add a frame in the FIFO list of requested frames.
+ This function is called in process context.
+ --------------------------------------------------------------------------*/
+static void w9968cf_push_frame(struct w9968cf_device* cam, u8 f_num)
+{
+ u8 f;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&cam->flist_lock, lock_flags);
+
+ for (f=0; cam->requested_frame[f] != NULL; f++);
+ cam->requested_frame[f] = &cam->frame[f_num];
+ cam->frame[f_num].queued = 1;
+ cam->frame[f_num].status = F_UNUSED; /* clear the status */
+
+ spin_unlock_irqrestore(&cam->flist_lock, lock_flags);
+
+ DBG(6, "Frame #%u pushed into the FIFO list. Position %u", f_num, f)
+}
+
+
+/*--------------------------------------------------------------------------
+ Read, store and remove the first pointer in the FIFO list of requested
+ frames. This function is called in interrupt context.
+ --------------------------------------------------------------------------*/
+static void
+w9968cf_pop_frame(struct w9968cf_device* cam, struct w9968cf_frame_t** framep)
+{
+ u8 i;
+
+ spin_lock(&cam->flist_lock);
+
+ *framep = cam->requested_frame[0];
+
+ /* Shift the list of pointers */
+ for (i = 0; i < cam->nbuffers-1; i++)
+ cam->requested_frame[i] = cam->requested_frame[i+1];
+ cam->requested_frame[i] = NULL;
+
+ spin_unlock(&cam->flist_lock);
+
+ DBG(6,"Popped frame #%d from the list", (*framep)->number)
+}
+
+
+/*--------------------------------------------------------------------------
+ High-level video post-processing routine on grabbed frames.
+ Return 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int
+w9968cf_postprocess_frame(struct w9968cf_device* cam,
+ struct w9968cf_frame_t* fr)
+{
+ void *pIn = fr->buffer, *pOut = cam->frame_vpp.buffer, *tmp;
+ u16 w = cam->window.width,
+ h = cam->window.height,
+ d = cam->picture.depth,
+ fmt = cam->picture.palette,
+ rgb = cam->force_rgb,
+ hw_w = cam->hw_width,
+ hw_h = cam->hw_height,
+ hw_d = cam->hw_depth;
+ int err = 0;
+
+ #define _PSWAP(pIn, pOut) {tmp = (pIn); (pIn) = (pOut); (pOut) = tmp;}
+
+ if (cam->vpp_flag & VPP_DECOMPRESSION) {
+ memcpy(pOut, pIn, fr->length);
+ _PSWAP(pIn, pOut)
+ err = w9968cf_vpp->decode(pIn, fr->length, hw_w, hw_h, pOut);
+ PDBGG("Compressed frame length: %lu",(unsigned long)fr->length)
+ fr->length = (hw_w*hw_h*hw_d)/8;
+ _PSWAP(pIn, pOut)
+ if (err) {
+ DBG(4, "An error occurred while decoding the frame: "
+ "%s", symbolic(decoder_errlist, err))
+ return err;
+ } else
+ DBG(6, "Frame decoded")
+ }
+
+ if (cam->vpp_flag & VPP_SWAP_YUV_BYTES) {
+ w9968cf_vpp->swap_yuvbytes(pIn, fr->length);
+ DBG(6, "Original UYVY component ordering changed")
+ }
+
+ if (cam->vpp_flag & VPP_UPSCALE) {
+ w9968cf_vpp->scale_up(pIn, pOut, hw_w, hw_h, hw_d, w, h);
+ fr->length = (w*h*hw_d)/8;
+ _PSWAP(pIn, pOut)
+ DBG(6, "Vertical up-scaling done: %u,%u,%ubpp->%u,%u",
+ hw_w, hw_h, hw_d, w, h)
+ }
+
+ if (cam->vpp_flag & VPP_UYVY_TO_RGBX) {
+ w9968cf_vpp->uyvy_to_rgbx(pIn, fr->length, pOut, fmt, rgb);
+ fr->length = (w*h*d)/8;
+ _PSWAP(pIn, pOut)
+ DBG(6, "UYVY-16bit to %s conversion done",
+ symbolic(v4l1_plist, fmt))
+ }
+
+ if (pOut == fr->buffer)
+ memcpy(fr->buffer, cam->frame_vpp.buffer, fr->length);
+
+ return 0;
+}
+
+
+
+/****************************************************************************
+ * Image sensor control routines *
+ ****************************************************************************/
+
+static int
+w9968cf_sensor_set_control(struct w9968cf_device* cam, int cid, int val)
+{
+ struct ovcamchip_control ctl;
+ int err;
+
+ ctl.id = cid;
+ ctl.value = val;
+
+ err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_CTRL, &ctl);
+
+ return err;
+}
+
+
+static int
+w9968cf_sensor_get_control(struct w9968cf_device* cam, int cid, int* val)
+{
+ struct ovcamchip_control ctl;
+ int err;
+
+ ctl.id = cid;
+
+ err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_G_CTRL, &ctl);
+ if (!err)
+ *val = ctl.value;
+
+ return err;
+}
+
+
+static int
+w9968cf_sensor_cmd(struct w9968cf_device* cam, unsigned int cmd, void* arg)
+{
+ struct i2c_client* c = cam->sensor_client;
+ int rc = 0;
+
+ if (!c || !c->driver || !c->driver->command)
+ return -EINVAL;
+
+ rc = c->driver->command(c, cmd, arg);
+ /* The I2C driver returns -EPERM on non-supported controls */
+ return (rc < 0 && rc != -EPERM) ? rc : 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Update some settings of the image sensor.
+ Returns: 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_sensor_update_settings(struct w9968cf_device* cam)
+{
+ int err = 0;
+
+ /* Auto brightness */
+ err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOBRIGHT,
+ cam->auto_brt);
+ if (err)
+ return err;
+
+ /* Auto exposure */
+ err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOEXP,
+ cam->auto_exp);
+ if (err)
+ return err;
+
+ /* Banding filter */
+ err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BANDFILT,
+ cam->bandfilt);
+ if (err)
+ return err;
+
+ /* Light frequency */
+ err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_FREQ,
+ cam->lightfreq);
+ if (err)
+ return err;
+
+ /* Back light */
+ err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BACKLIGHT,
+ cam->backlight);
+ if (err)
+ return err;
+
+ /* Mirror */
+ err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_MIRROR,
+ cam->mirror);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Get some current picture settings from the image sensor and update the
+ internal 'picture' structure of the camera.
+ Returns: 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_sensor_get_picture(struct w9968cf_device* cam)
+{
+ int err, v;
+
+ err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_CONT, &v);
+ if (err)
+ return err;
+ cam->picture.contrast = v;
+
+ err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_BRIGHT, &v);
+ if (err)
+ return err;
+ cam->picture.brightness = v;
+
+ err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_SAT, &v);
+ if (err)
+ return err;
+ cam->picture.colour = v;
+
+ err = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_HUE, &v);
+ if (err)
+ return err;
+ cam->picture.hue = v;
+
+ DBG(5, "Got picture settings from the image sensor")
+
+ PDBGG("Brightness, contrast, hue, colour, whiteness are "
+ "%u,%u,%u,%u,%u", cam->picture.brightness,cam->picture.contrast,
+ cam->picture.hue, cam->picture.colour, cam->picture.whiteness)
+
+ return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+ Update picture settings of the image sensor.
+ Returns: 0 on success, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int
+w9968cf_sensor_update_picture(struct w9968cf_device* cam,
+ struct video_picture pict)
+{
+ int err = 0;
+
+ if ((!cam->sensor_initialized)
+ || pict.contrast != cam->picture.contrast) {
+ err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_CONT,
+ pict.contrast);
+ if (err)
+ goto fail;
+ DBG(4, "Contrast changed from %u to %u",
+ cam->picture.contrast, pict.contrast)
+ cam->picture.contrast = pict.contrast;
+ }
+
+ if (((!cam->sensor_initialized) ||
+ pict.brightness != cam->picture.brightness) && (!cam->auto_brt)) {
+ err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BRIGHT,
+ pict.brightness);
+ if (err)
+ goto fail;
+ DBG(4, "Brightness changed from %u to %u",
+ cam->picture.brightness, pict.brightness)
+ cam->picture.brightness = pict.brightness;
+ }
+
+ if ((!cam->sensor_initialized) || pict.colour != cam->picture.colour) {
+ err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_SAT,
+ pict.colour);
+ if (err)
+ goto fail;
+ DBG(4, "Colour changed from %u to %u",
+ cam->picture.colour, pict.colour)
+ cam->picture.colour = pict.colour;
+ }
+
+ if ((!cam->sensor_initialized) || pict.hue != cam->picture.hue) {
+ err = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_HUE,
+ pict.hue);
+ if (err)
+ goto fail;
+ DBG(4, "Hue changed from %u to %u",
+ cam->picture.hue, pict.hue)
+ cam->picture.hue = pict.hue;
+ }
+
+ return 0;
+
+fail:
+ DBG(4, "Failed to change sensor picture setting")
+ return err;
+}
+
+
+
+/****************************************************************************
+ * Camera configuration *
+ ****************************************************************************/
+
+/*--------------------------------------------------------------------------
+ This function is called when a supported image sensor is detected.
+ Return 0 if the initialization succeeds, a negative number otherwise.
+ --------------------------------------------------------------------------*/
+static int w9968cf_sensor_init(struct w9968cf_device* cam)
+{
+ int err = 0;
+
+ if ((err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_INITIALIZE,
+ &cam->monochrome)))
+ goto error;
+
+ if ((err = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_Q_SUBTYPE,
+ &cam->sensor)))
+ goto error;
+
+ /* NOTE: Make sure width and height are a multiple of 16 */
+ switch (cam->sensor_client->addr) {
+ case OV6xx0_SID:
+ cam->maxwidth = 352;
+ cam->maxheight = 288;
+ cam->minwidth = 64;
+ cam->minheight = 48;
+ break;
+ case OV7xx0_SID:
+ cam->maxwidth = 640;
+ cam->maxheight = 480;
+ cam->minwidth = 64;
+ cam->minheight = 48;
+ break;
+ default:
+ DBG(1, "Not supported image sensor detected for %s",
+ symbolic(camlist, cam->id))
+ return -EINVAL;
+ }
+
+ /* These values depend on the ones in the ovxxx0.c sources */
+ switch (cam->sensor) {
+ case CC_OV7620:
+ cam->start_cropx = 287;
+ cam->start_cropy = 35;
+ /* Seems to work around a bug in the image sensor */
+ cam->vs_polarity = 1;
+ cam->hs_polarity = 1;
+ break;
+ default:
+ cam->start_cropx = 320;
+ cam->start_cropy = 35;
+ cam->vs_polarity = 1;
+ cam->hs_polarity = 0;
+ }
+
+ if ((err = w9968cf_sensor_update_settings(cam)))
+ goto error;
+
+ if ((err = w9968cf_sensor_update_picture(cam, cam->picture)))
+ goto error;
+
+ cam->sensor_initialized = 1;
+
+ DBG(2, "%s image sensor initialized", symbolic(senlist, cam->sensor))
+ return 0;
+
+error:
+ cam->sensor_initialized = 0;
+ cam->sensor = CC_UNKNOWN;
+ DBG(1, "Image sensor initialization failed for %s (/dev/video%d). "
+ "Try to detach and attach this device again",
+ symbolic(camlist, cam->id), cam->v4ldev->minor)
+ return err;
+}
+
+
+/*--------------------------------------------------------------------------
+ Fill some basic fields in the main device data structure.
+ This function is called once on w9968cf_usb_probe() for each recognized
+ camera.
+ --------------------------------------------------------------------------*/
+static void
+w9968cf_configure_camera(struct w9968cf_device* cam,
+ struct usb_device* udev,
+ enum w9968cf_model_id mod_id,
+ const unsigned short dev_nr)
+{
+ mutex_init(&cam->fileop_mutex);
+ init_waitqueue_head(&cam->open);
+ spin_lock_init(&cam->urb_lock);
+ spin_lock_init(&cam->flist_lock);
+
+ cam->users = 0;
+ cam->disconnected = 0;
+ cam->id = mod_id;
+ cam->sensor = CC_UNKNOWN;
+ cam->sensor_initialized = 0;
+
+ /* Calculate the alternate setting number (from 1 to 16)
+ according to the 'packet_size' module parameter */
+ if (packet_size[dev_nr] < W9968CF_MIN_PACKET_SIZE)
+ packet_size[dev_nr] = W9968CF_MIN_PACKET_SIZE;
+ for (cam->altsetting = 1;
+ packet_size[dev_nr] < wMaxPacketSize[cam->altsetting-1];
+ cam->altsetting++);
+
+ cam->max_buffers = (max_buffers[dev_nr] < 2 ||
+ max_buffers[dev_nr] > W9968CF_MAX_BUFFERS)
+ ? W9968CF_BUFFERS : (u8)max_buffers[dev_nr];
+
+ cam->double_buffer = (double_buffer[dev_nr] == 0 ||
+ double_buffer[dev_nr] == 1)
+ ? (u8)double_buffer[dev_nr]:W9968CF_DOUBLE_BUFFER;
+
+ cam->clamping = (clamping[dev_nr] == 0 || clamping[dev_nr] == 1)
+ ? (u8)clamping[dev_nr] : W9968CF_CLAMPING;
+
+ cam->filter_type = (filter_type[dev_nr] == 0 ||
+ filter_type[dev_nr] == 1 ||
+ filter_type[dev_nr] == 2)
+ ? (u8)filter_type[dev_nr] : W9968CF_FILTER_TYPE;
+
+ cam->capture = 1;
+
+ cam->largeview = (largeview[dev_nr] == 0 || largeview[dev_nr] == 1)
+ ? (u8)largeview[dev_nr] : W9968CF_LARGEVIEW;
+
+ cam->decompression = (decompression[dev_nr] == 0 ||
+ decompression[dev_nr] == 1 ||
+ decompression[dev_nr] == 2)
+ ? (u8)decompression[dev_nr]:W9968CF_DECOMPRESSION;
+
+ cam->upscaling = (upscaling[dev_nr] == 0 ||
+ upscaling[dev_nr] == 1)
+ ? (u8)upscaling[dev_nr] : W9968CF_UPSCALING;
+
+ cam->auto_brt = (autobright[dev_nr] == 0 || autobright[dev_nr] == 1)
+ ? (u8)autobright[dev_nr] : W9968CF_AUTOBRIGHT;
+
+ cam->auto_exp = (autoexp[dev_nr] == 0 || autoexp[dev_nr] == 1)
+ ? (u8)autoexp[dev_nr] : W9968CF_AUTOEXP;
+
+ cam->lightfreq = (lightfreq[dev_nr] == 50 || lightfreq[dev_nr] == 60)
+ ? (u8)lightfreq[dev_nr] : W9968CF_LIGHTFREQ;
+
+ cam->bandfilt = (bandingfilter[dev_nr] == 0 ||
+ bandingfilter[dev_nr] == 1)
+ ? (u8)bandingfilter[dev_nr] : W9968CF_BANDINGFILTER;
+
+ cam->backlight = (backlight[dev_nr] == 0 || backlight[dev_nr] == 1)
+ ? (u8)backlight[dev_nr] : W9968CF_BACKLIGHT;
+
+ cam->clockdiv = (clockdiv[dev_nr] == -1 || clockdiv[dev_nr] >= 0)
+ ? (s8)clockdiv[dev_nr] : W9968CF_CLOCKDIV;
+
+ cam->mirror = (mirror[dev_nr] == 0 || mirror[dev_nr] == 1)
+ ? (u8)mirror[dev_nr] : W9968CF_MIRROR;
+
+ cam->monochrome = (monochrome[dev_nr] == 0 || monochrome[dev_nr] == 1)
+ ? monochrome[dev_nr] : W9968CF_MONOCHROME;
+
+ cam->picture.brightness = (u16)brightness[dev_nr];
+ cam->picture.hue = (u16)hue[dev_nr];
+ cam->picture.colour = (u16)colour[dev_nr];
+ cam->picture.contrast = (u16)contrast[dev_nr];
+ cam->picture.whiteness = (u16)whiteness[dev_nr];
+ if (w9968cf_valid_palette((u16)force_palette[dev_nr])) {
+ cam->picture.palette = (u16)force_palette[dev_nr];
+ cam->force_palette = 1;
+ } else {
+ cam->force_palette = 0;
+ if (cam->decompression == 0)
+ cam->picture.palette = W9968CF_PALETTE_DECOMP_OFF;
+ else if (cam->decompression == 1)
+ cam->picture.palette = W9968CF_PALETTE_DECOMP_FORCE;
+ else
+ cam->picture.palette = W9968CF_PALETTE_DECOMP_ON;
+ }
+ cam->picture.depth = w9968cf_valid_depth(cam->picture.palette);
+
+ cam->force_rgb = (force_rgb[dev_nr] == 0 || force_rgb[dev_nr] == 1)
+ ? (u8)force_rgb[dev_nr] : W9968CF_FORCE_RGB;
+
+ cam->window.x = 0;
+ cam->window.y = 0;
+ cam->window.width = W9968CF_WIDTH;
+ cam->window.height = W9968CF_HEIGHT;
+ cam->window.chromakey = 0;
+ cam->window.clipcount = 0;
+ cam->window.flags = 0;
+
+ DBG(3, "%s configured with settings #%u:",
+ symbolic(camlist, cam->id), dev_nr)
+
+ DBG(3, "- Data packet size for USB isochrnous transfer: %u bytes",
+ wMaxPacketSize[cam->altsetting-1])
+
+ DBG(3, "- Number of requested video frame buffers: %u",
+ cam->max_buffers)
+
+ if (cam->double_buffer)
+ DBG(3, "- Hardware double buffering enabled")
+ else
+ DBG(3, "- Hardware double buffering disabled")
+
+ if (cam->filter_type == 0)
+ DBG(3, "- Video filtering disabled")
+ else if (cam->filter_type == 1)
+ DBG(3, "- Video filtering enabled: type 1-2-1")
+ else if (cam->filter_type == 2)
+ DBG(3, "- Video filtering enabled: type 2-3-6-3-2")
+
+ if (cam->clamping)
+ DBG(3, "- Video data clamping (CCIR-601 format) enabled")
+ else
+ DBG(3, "- Video data clamping (CCIR-601 format) disabled")
+
+ if (cam->largeview)
+ DBG(3, "- Large view enabled")
+ else
+ DBG(3, "- Large view disabled")
+
+ if ((cam->decompression) == 0 && (!cam->force_palette))
+ DBG(3, "- Decompression disabled")
+ else if ((cam->decompression) == 1 && (!cam->force_palette))
+ DBG(3, "- Decompression forced")
+ else if ((cam->decompression) == 2 && (!cam->force_palette))
+ DBG(3, "- Decompression allowed")
+
+ if (cam->upscaling)
+ DBG(3, "- Software image scaling enabled")
+ else
+ DBG(3, "- Software image scaling disabled")
+
+ if (cam->force_palette)
+ DBG(3, "- Image palette forced to %s",
+ symbolic(v4l1_plist, cam->picture.palette))
+
+ if (cam->force_rgb)
+ DBG(3, "- RGB component ordering will be used instead of BGR")
+
+ if (cam->auto_brt)
+ DBG(3, "- Auto brightness enabled")
+ else
+ DBG(3, "- Auto brightness disabled")
+
+ if (cam->auto_exp)
+ DBG(3, "- Auto exposure enabled")
+ else
+ DBG(3, "- Auto exposure disabled")
+
+ if (cam->backlight)
+ DBG(3, "- Backlight exposure algorithm enabled")
+ else
+ DBG(3, "- Backlight exposure algorithm disabled")
+
+ if (cam->mirror)
+ DBG(3, "- Mirror enabled")
+ else
+ DBG(3, "- Mirror disabled")
+
+ if (cam->bandfilt)
+ DBG(3, "- Banding filter enabled")
+ else
+ DBG(3, "- Banding filter disabled")
+
+ DBG(3, "- Power lighting frequency: %u", cam->lightfreq)
+
+ if (cam->clockdiv == -1)
+ DBG(3, "- Automatic clock divisor enabled")
+ else
+ DBG(3, "- Clock divisor: %d", cam->clockdiv)
+
+ if (cam->monochrome)
+ DBG(3, "- Image sensor used as monochrome")
+ else
+ DBG(3, "- Image sensor not used as monochrome")
+}
+
+
+/*--------------------------------------------------------------------------
+ If the video post-processing module is not loaded, some parameters
+ must be overridden.
+ --------------------------------------------------------------------------*/
+static void w9968cf_adjust_configuration(struct w9968cf_device* cam)
+{
+ if (!w9968cf_vpp) {
+ if (cam->decompression == 1) {
+ cam->decompression = 2;
+ DBG(2, "Video post-processing module not found: "
+ "'decompression' parameter forced to 2")
+ }
+ if (cam->upscaling) {
+ cam->upscaling = 0;
+ DBG(2, "Video post-processing module not found: "
+ "'upscaling' parameter forced to 0")
+ }
+ if (cam->picture.palette != VIDEO_PALETTE_UYVY) {
+ cam->force_palette = 0;
+ DBG(2, "Video post-processing module not found: "
+ "'force_palette' parameter forced to 0")
+ }
+ cam->picture.palette = VIDEO_PALETTE_UYVY;
+ cam->picture.depth = w9968cf_valid_depth(cam->picture.palette);
+ }
+}
+
+
+/*--------------------------------------------------------------------------
+ Release the resources used by the driver.
+ This function is called on disconnect
+ (or on close if deallocation has been deferred)
+ --------------------------------------------------------------------------*/
+static void w9968cf_release_resources(struct w9968cf_device* cam)
+{
+ mutex_lock(&w9968cf_devlist_mutex);
+
+ DBG(2, "V4L device deregistered: /dev/video%d", cam->v4ldev->minor)
+
+ video_unregister_device(cam->v4ldev);
+ list_del(&cam->v4llist);
+ i2c_del_adapter(&cam->i2c_adapter);
+ w9968cf_deallocate_memory(cam);
+ kfree(cam->control_buffer);
+ kfree(cam->data_buffer);
+
+ mutex_unlock(&w9968cf_devlist_mutex);
+}
+
+
+
+/****************************************************************************
+ * Video4Linux interface *
+ ****************************************************************************/
+
+static int w9968cf_open(struct inode* inode, struct file* filp)
+{
+ struct w9968cf_device* cam;
+ int err;
+
+ /* This the only safe way to prevent race conditions with disconnect */
+ if (!down_read_trylock(&w9968cf_disconnect))
+ return -ERESTARTSYS;
+
+ cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
+
+ mutex_lock(&cam->dev_mutex);
+
+ if (cam->sensor == CC_UNKNOWN) {
+ DBG(2, "No supported image sensor has been detected by the "
+ "'ovcamchip' module for the %s (/dev/video%d). Make "
+ "sure it is loaded *before* (re)connecting the camera.",
+ symbolic(camlist, cam->id), cam->v4ldev->minor)
+ mutex_unlock(&cam->dev_mutex);
+ up_read(&w9968cf_disconnect);
+ return -ENODEV;
+ }
+
+ if (cam->users) {
+ DBG(2, "%s (/dev/video%d) has been already occupied by '%s'",
+ symbolic(camlist, cam->id),cam->v4ldev->minor,cam->command)
+ if ((filp->f_flags & O_NONBLOCK)||(filp->f_flags & O_NDELAY)) {
+ mutex_unlock(&cam->dev_mutex);
+ up_read(&w9968cf_disconnect);
+ return -EWOULDBLOCK;
+ }
+ mutex_unlock(&cam->dev_mutex);
+ err = wait_event_interruptible_exclusive(cam->open,
+ cam->disconnected ||
+ !cam->users);
+ if (err) {
+ up_read(&w9968cf_disconnect);
+ return err;
+ }
+ if (cam->disconnected) {
+ up_read(&w9968cf_disconnect);
+ return -ENODEV;
+ }
+ mutex_lock(&cam->dev_mutex);
+ }
+
+ DBG(5, "Opening '%s', /dev/video%d ...",
+ symbolic(camlist, cam->id), cam->v4ldev->minor)
+
+ cam->streaming = 0;
+ cam->misconfigured = 0;
+
+ w9968cf_adjust_configuration(cam);
+
+ if ((err = w9968cf_allocate_memory(cam)))
+ goto deallocate_memory;
+
+ if ((err = w9968cf_init_chip(cam)))
+ goto deallocate_memory;
+
+ if ((err = w9968cf_start_transfer(cam)))
+ goto deallocate_memory;
+
+ filp->private_data = cam;
+
+ cam->users++;
+ strcpy(cam->command, current->comm);
+
+ init_waitqueue_head(&cam->wait_queue);
+
+ DBG(5, "Video device is open")
+
+ mutex_unlock(&cam->dev_mutex);
+ up_read(&w9968cf_disconnect);
+
+ return 0;
+
+deallocate_memory:
+ w9968cf_deallocate_memory(cam);
+ DBG(2, "Failed to open the video device")
+ mutex_unlock(&cam->dev_mutex);
+ up_read(&w9968cf_disconnect);
+ return err;
+}
+
+
+static int w9968cf_release(struct inode* inode, struct file* filp)
+{
+ struct w9968cf_device* cam;
+
+ cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
+
+ mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */
+
+ w9968cf_stop_transfer(cam);
+
+ if (cam->disconnected) {
+ w9968cf_release_resources(cam);
+ mutex_unlock(&cam->dev_mutex);
+ kfree(cam);
+ return 0;
+ }
+
+ cam->users--;
+ w9968cf_deallocate_memory(cam);
+ wake_up_interruptible_nr(&cam->open, 1);
+
+ DBG(5, "Video device closed")
+ mutex_unlock(&cam->dev_mutex);
+ return 0;
+}
+
+
+static ssize_t
+w9968cf_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos)
+{
+ struct w9968cf_device* cam;
+ struct w9968cf_frame_t* fr;
+ int err = 0;
+
+ cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
+
+ if (filp->f_flags & O_NONBLOCK)
+ return -EWOULDBLOCK;
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->disconnected) {
+ DBG(2, "Device not present")
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->misconfigured) {
+ DBG(2, "The camera is misconfigured. Close and open it again.")
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ if (!cam->frame[0].queued)
+ w9968cf_push_frame(cam, 0);
+
+ if (!cam->frame[1].queued)
+ w9968cf_push_frame(cam, 1);
+
+ err = wait_event_interruptible(cam->wait_queue,
+ cam->frame[0].status == F_READY ||
+ cam->frame[1].status == F_READY ||
+ cam->disconnected);
+ if (err) {
+ mutex_unlock(&cam->fileop_mutex);
+ return err;
+ }
+ if (cam->disconnected) {
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ fr = (cam->frame[0].status == F_READY) ? &cam->frame[0]:&cam->frame[1];
+
+ if (w9968cf_vpp)
+ w9968cf_postprocess_frame(cam, fr);
+
+ if (count > fr->length)
+ count = fr->length;
+
+ if (copy_to_user(buf, fr->buffer, count)) {
+ fr->status = F_UNUSED;
+ mutex_unlock(&cam->fileop_mutex);
+ return -EFAULT;
+ }
+ *f_pos += count;
+
+ fr->status = F_UNUSED;
+
+ DBG(5, "%zu bytes read", count)
+
+ mutex_unlock(&cam->fileop_mutex);
+ return count;
+}
+
+
+static int w9968cf_mmap(struct file* filp, struct vm_area_struct *vma)
+{
+ struct w9968cf_device* cam = (struct w9968cf_device*)
+ video_get_drvdata(video_devdata(filp));
+ unsigned long vsize = vma->vm_end - vma->vm_start,
+ psize = cam->nbuffers * cam->frame[0].size,
+ start = vma->vm_start,
+ pos = (unsigned long)cam->frame[0].buffer,
+ page;
+
+ if (cam->disconnected) {
+ DBG(2, "Device not present")
+ return -ENODEV;
+ }
+
+ if (cam->misconfigured) {
+ DBG(2, "The camera is misconfigured. Close and open it again")
+ return -EIO;
+ }
+
+ PDBGG("mmapping %lu bytes...", vsize)
+
+ if (vsize > psize - (vma->vm_pgoff << PAGE_SHIFT))
+ return -EINVAL;
+
+ while (vsize > 0) {
+ page = vmalloc_to_pfn((void *)pos);
+ if (remap_pfn_range(vma, start, page + vma->vm_pgoff,
+ PAGE_SIZE, vma->vm_page_prot))
+ return -EAGAIN;
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ vsize -= PAGE_SIZE;
+ }
+
+ DBG(5, "mmap method successfully called")
+ return 0;
+}
+
+
+static int
+w9968cf_ioctl(struct inode* inode, struct file* filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct w9968cf_device* cam;
+ int err;
+
+ cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
+
+ if (mutex_lock_interruptible(&cam->fileop_mutex))
+ return -ERESTARTSYS;
+
+ if (cam->disconnected) {
+ DBG(2, "Device not present")
+ mutex_unlock(&cam->fileop_mutex);
+ return -ENODEV;
+ }
+
+ if (cam->misconfigured) {
+ DBG(2, "The camera is misconfigured. Close and open it again.")
+ mutex_unlock(&cam->fileop_mutex);
+ return -EIO;
+ }
+
+ err = w9968cf_v4l_ioctl(inode, filp, cmd, (void __user *)arg);
+
+ mutex_unlock(&cam->fileop_mutex);
+ return err;
+}
+
+
+static int w9968cf_v4l_ioctl(struct inode* inode, struct file* filp,
+ unsigned int cmd, void __user * arg)
+{
+ struct w9968cf_device* cam;
+ const char* v4l1_ioctls[] = {
+ "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER",
+ "GPICT", "SPICT", "CCAPTURE", "GWIN", "SWIN", "GFBUF",
+ "SFBUF", "KEY", "GFREQ", "SFREQ", "GAUDIO", "SAUDIO",
+ "SYNC", "MCAPTURE", "GMBUF", "GUNIT", "GCAPTURE", "SCAPTURE",
+ "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", "SMICROCODE",
+ "GVBIFMT", "SVBIFMT"
+ };
+
+ #define V4L1_IOCTL(cmd) \
+ ((_IOC_NR((cmd)) < ARRAY_SIZE(v4l1_ioctls)) ? \
+ v4l1_ioctls[_IOC_NR((cmd))] : "?")
+
+ cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp));
+
+ switch (cmd) {
+
+ case VIDIOCGCAP: /* get video capability */
+ {
+ struct video_capability cap = {
+ .type = VID_TYPE_CAPTURE | VID_TYPE_SCALES,
+ .channels = 1,
+ .audios = 0,
+ .minwidth = cam->minwidth,
+ .minheight = cam->minheight,
+ };
+ sprintf(cap.name, "W996[87]CF USB Camera #%d",
+ cam->v4ldev->minor);
+ cap.maxwidth = (cam->upscaling && w9968cf_vpp)
+ ? max((u16)W9968CF_MAX_WIDTH, cam->maxwidth)
+ : cam->maxwidth;
+ cap.maxheight = (cam->upscaling && w9968cf_vpp)
+ ? max((u16)W9968CF_MAX_HEIGHT, cam->maxheight)
+ : cam->maxheight;
+
+ if (copy_to_user(arg, &cap, sizeof(cap)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGCAP successfully called")
+ return 0;
+ }
+
+ case VIDIOCGCHAN: /* get video channel informations */
+ {
+ struct video_channel chan;
+ if (copy_from_user(&chan, arg, sizeof(chan)))
+ return -EFAULT;
+
+ if (chan.channel != 0)
+ return -EINVAL;
+
+ strcpy(chan.name, "Camera");
+ chan.tuners = 0;
+ chan.flags = 0;
+ chan.type = VIDEO_TYPE_CAMERA;
+ chan.norm = VIDEO_MODE_AUTO;
+
+ if (copy_to_user(arg, &chan, sizeof(chan)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGCHAN successfully called")
+ return 0;
+ }
+
+ case VIDIOCSCHAN: /* set active channel */
+ {
+ struct video_channel chan;
+
+ if (copy_from_user(&chan, arg, sizeof(chan)))
+ return -EFAULT;
+
+ if (chan.channel != 0)
+ return -EINVAL;
+
+ DBG(5, "VIDIOCSCHAN successfully called")
+ return 0;
+ }
+
+ case VIDIOCGPICT: /* get image properties of the picture */
+ {
+ if (w9968cf_sensor_get_picture(cam))
+ return -EIO;
+
+ if (copy_to_user(arg, &cam->picture, sizeof(cam->picture)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGPICT successfully called")
+ return 0;
+ }
+
+ case VIDIOCSPICT: /* change picture settings */
+ {
+ struct video_picture pict;
+ int err = 0;
+
+ if (copy_from_user(&pict, arg, sizeof(pict)))
+ return -EFAULT;
+
+ if ( (cam->force_palette || !w9968cf_vpp)
+ && pict.palette != cam->picture.palette ) {
+ DBG(4, "Palette %s rejected: only %s is allowed",
+ symbolic(v4l1_plist, pict.palette),
+ symbolic(v4l1_plist, cam->picture.palette))
+ return -EINVAL;
+ }
+
+ if (!w9968cf_valid_palette(pict.palette)) {
+ DBG(4, "Palette %s not supported. VIDIOCSPICT failed",
+ symbolic(v4l1_plist, pict.palette))
+ return -EINVAL;
+ }
+
+ if (!cam->force_palette) {
+ if (cam->decompression == 0) {
+ if (w9968cf_need_decompression(pict.palette)) {
+ DBG(4, "Decompression disabled: palette %s is not "
+ "allowed. VIDIOCSPICT failed",
+ symbolic(v4l1_plist, pict.palette))
+ return -EINVAL;
+ }
+ } else if (cam->decompression == 1) {
+ if (!w9968cf_need_decompression(pict.palette)) {
+ DBG(4, "Decompression forced: palette %s is not "
+ "allowed. VIDIOCSPICT failed",
+ symbolic(v4l1_plist, pict.palette))
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (pict.depth != w9968cf_valid_depth(pict.palette)) {
+ DBG(4, "Requested depth %u bpp is not valid for %s "
+ "palette: ignored and changed to %u bpp",
+ pict.depth, symbolic(v4l1_plist, pict.palette),
+ w9968cf_valid_depth(pict.palette))
+ pict.depth = w9968cf_valid_depth(pict.palette);
+ }
+
+ if (pict.palette != cam->picture.palette) {
+ if(*cam->requested_frame
+ || cam->frame_current->queued) {
+ err = wait_event_interruptible
+ ( cam->wait_queue,
+ cam->disconnected ||
+ (!*cam->requested_frame &&
+ !cam->frame_current->queued) );
+ if (err)
+ return err;
+ if (cam->disconnected)
+ return -ENODEV;
+ }
+
+ if (w9968cf_stop_transfer(cam))
+ goto ioctl_fail;
+
+ if (w9968cf_set_picture(cam, pict))
+ goto ioctl_fail;
+
+ if (w9968cf_start_transfer(cam))
+ goto ioctl_fail;
+
+ } else if (w9968cf_sensor_update_picture(cam, pict))
+ return -EIO;
+
+
+ DBG(5, "VIDIOCSPICT successfully called")
+ return 0;
+ }
+
+ case VIDIOCSWIN: /* set capture area */
+ {
+ struct video_window win;
+ int err = 0;
+
+ if (copy_from_user(&win, arg, sizeof(win)))
+ return -EFAULT;
+
+ DBG(6, "VIDIOCSWIN called: clipcount=%d, flags=%u, "
+ "x=%u, y=%u, %ux%u", win.clipcount, win.flags,
+ win.x, win.y, win.width, win.height)
+
+ if (win.clipcount != 0 || win.flags != 0)
+ return -EINVAL;
+
+ if ((err = w9968cf_adjust_window_size(cam, (u16*)&win.width,
+ (u16*)&win.height))) {
+ DBG(4, "Resolution not supported (%ux%u). "
+ "VIDIOCSWIN failed", win.width, win.height)
+ return err;
+ }
+
+ if (win.x != cam->window.x ||
+ win.y != cam->window.y ||
+ win.width != cam->window.width ||
+ win.height != cam->window.height) {
+ if(*cam->requested_frame
+ || cam->frame_current->queued) {
+ err = wait_event_interruptible
+ ( cam->wait_queue,
+ cam->disconnected ||
+ (!*cam->requested_frame &&
+ !cam->frame_current->queued) );
+ if (err)
+ return err;
+ if (cam->disconnected)
+ return -ENODEV;
+ }
+
+ if (w9968cf_stop_transfer(cam))
+ goto ioctl_fail;
+
+ /* This _must_ be called before set_window() */
+ if (w9968cf_set_picture(cam, cam->picture))
+ goto ioctl_fail;
+
+ if (w9968cf_set_window(cam, win))
+ goto ioctl_fail;
+
+ if (w9968cf_start_transfer(cam))
+ goto ioctl_fail;
+ }
+
+ DBG(5, "VIDIOCSWIN successfully called. ")
+ return 0;
+ }
+
+ case VIDIOCGWIN: /* get current window properties */
+ {
+ if (copy_to_user(arg,&cam->window,sizeof(struct video_window)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGWIN successfully called")
+ return 0;
+ }
+
+ case VIDIOCGMBUF: /* request for memory (mapped) buffer */
+ {
+ struct video_mbuf mbuf;
+ u8 i;
+
+ mbuf.size = cam->nbuffers * cam->frame[0].size;
+ mbuf.frames = cam->nbuffers;
+ for (i = 0; i < cam->nbuffers; i++)
+ mbuf.offsets[i] = (unsigned long)cam->frame[i].buffer -
+ (unsigned long)cam->frame[0].buffer;
+
+ if (copy_to_user(arg, &mbuf, sizeof(mbuf)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGMBUF successfully called")
+ return 0;
+ }
+
+ case VIDIOCMCAPTURE: /* start the capture to a frame */
+ {
+ struct video_mmap mmap;
+ struct w9968cf_frame_t* fr;
+ int err = 0;
+
+ if (copy_from_user(&mmap, arg, sizeof(mmap)))
+ return -EFAULT;
+
+ DBG(6, "VIDIOCMCAPTURE called: frame #%u, format=%s, %dx%d",
+ mmap.frame, symbolic(v4l1_plist, mmap.format),
+ mmap.width, mmap.height)
+
+ if (mmap.frame >= cam->nbuffers) {
+ DBG(4, "Invalid frame number (%u). "
+ "VIDIOCMCAPTURE failed", mmap.frame)
+ return -EINVAL;
+ }
+
+ if (mmap.format!=cam->picture.palette &&
+ (cam->force_palette || !w9968cf_vpp)) {
+ DBG(4, "Palette %s rejected: only %s is allowed",
+ symbolic(v4l1_plist, mmap.format),
+ symbolic(v4l1_plist, cam->picture.palette))
+ return -EINVAL;
+ }
+
+ if (!w9968cf_valid_palette(mmap.format)) {
+ DBG(4, "Palette %s not supported. "
+ "VIDIOCMCAPTURE failed",
+ symbolic(v4l1_plist, mmap.format))
+ return -EINVAL;
+ }
+
+ if (!cam->force_palette) {
+ if (cam->decompression == 0) {
+ if (w9968cf_need_decompression(mmap.format)) {
+ DBG(4, "Decompression disabled: palette %s is not "
+ "allowed. VIDIOCSPICT failed",
+ symbolic(v4l1_plist, mmap.format))
+ return -EINVAL;
+ }
+ } else if (cam->decompression == 1) {
+ if (!w9968cf_need_decompression(mmap.format)) {
+ DBG(4, "Decompression forced: palette %s is not "
+ "allowed. VIDIOCSPICT failed",
+ symbolic(v4l1_plist, mmap.format))
+ return -EINVAL;
+ }
+ }
+ }
+
+ if ((err = w9968cf_adjust_window_size(cam, (u16*)&mmap.width,
+ (u16*)&mmap.height))) {
+ DBG(4, "Resolution not supported (%dx%d). "
+ "VIDIOCMCAPTURE failed",
+ mmap.width, mmap.height)
+ return err;
+ }
+
+ fr = &cam->frame[mmap.frame];
+
+ if (mmap.width != cam->window.width ||
+ mmap.height != cam->window.height ||
+ mmap.format != cam->picture.palette) {
+
+ struct video_window win;
+ struct video_picture pict;
+
+ if(*cam->requested_frame
+ || cam->frame_current->queued) {
+ DBG(6, "VIDIOCMCAPTURE. Change settings for "
+ "frame #%u: %dx%d, format %s. Wait...",
+ mmap.frame, mmap.width, mmap.height,
+ symbolic(v4l1_plist, mmap.format))
+ err = wait_event_interruptible
+ ( cam->wait_queue,
+ cam->disconnected ||
+ (!*cam->requested_frame &&
+ !cam->frame_current->queued) );
+ if (err)
+ return err;
+ if (cam->disconnected)
+ return -ENODEV;
+ }
+
+ memcpy(&win, &cam->window, sizeof(win));
+ memcpy(&pict, &cam->picture, sizeof(pict));
+ win.width = mmap.width;
+ win.height = mmap.height;
+ pict.palette = mmap.format;
+
+ if (w9968cf_stop_transfer(cam))
+ goto ioctl_fail;
+
+ /* This before set_window */
+ if (w9968cf_set_picture(cam, pict))
+ goto ioctl_fail;
+
+ if (w9968cf_set_window(cam, win))
+ goto ioctl_fail;
+
+ if (w9968cf_start_transfer(cam))
+ goto ioctl_fail;
+
+ } else if (fr->queued) {
+
+ DBG(6, "Wait until frame #%u is free", mmap.frame)
+
+ err = wait_event_interruptible(cam->wait_queue,
+ cam->disconnected ||
+ (!fr->queued));
+ if (err)
+ return err;
+ if (cam->disconnected)
+ return -ENODEV;
+ }
+
+ w9968cf_push_frame(cam, mmap.frame);
+ DBG(5, "VIDIOCMCAPTURE(%u): successfully called", mmap.frame)
+ return 0;
+ }
+
+ case VIDIOCSYNC: /* wait until the capture of a frame is finished */
+ {
+ unsigned int f_num;
+ struct w9968cf_frame_t* fr;
+ int err = 0;
+
+ if (copy_from_user(&f_num, arg, sizeof(f_num)))
+ return -EFAULT;
+
+ if (f_num >= cam->nbuffers) {
+ DBG(4, "Invalid frame number (%u). "
+ "VIDIOCMCAPTURE failed", f_num)
+ return -EINVAL;
+ }
+
+ DBG(6, "VIDIOCSYNC called for frame #%u", f_num)
+
+ fr = &cam->frame[f_num];
+
+ switch (fr->status) {
+ case F_UNUSED:
+ if (!fr->queued) {
+ DBG(4, "VIDIOSYNC: Frame #%u not requested!",
+ f_num)
+ return -EFAULT;
+ }
+ case F_ERROR:
+ case F_GRABBING:
+ err = wait_event_interruptible(cam->wait_queue,
+ (fr->status == F_READY)
+ || cam->disconnected);
+ if (err)
+ return err;
+ if (cam->disconnected)
+ return -ENODEV;
+ break;
+ case F_READY:
+ break;
+ }
+
+ if (w9968cf_vpp)
+ w9968cf_postprocess_frame(cam, fr);
+
+ fr->status = F_UNUSED;
+
+ DBG(5, "VIDIOCSYNC(%u) successfully called", f_num)
+ return 0;
+ }
+
+ case VIDIOCGUNIT:/* report the unit numbers of the associated devices*/
+ {
+ struct video_unit unit = {
+ .video = cam->v4ldev->minor,
+ .vbi = VIDEO_NO_UNIT,
+ .radio = VIDEO_NO_UNIT,
+ .audio = VIDEO_NO_UNIT,
+ .teletext = VIDEO_NO_UNIT,
+ };
+
+ if (copy_to_user(arg, &unit, sizeof(unit)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGUNIT successfully called")
+ return 0;
+ }
+
+ case VIDIOCKEY:
+ return 0;
+
+ case VIDIOCGFBUF:
+ {
+ if (clear_user(arg, sizeof(struct video_buffer)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGFBUF successfully called")
+ return 0;
+ }
+
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner tuner;
+ if (copy_from_user(&tuner, arg, sizeof(tuner)))
+ return -EFAULT;
+
+ if (tuner.tuner != 0)
+ return -EINVAL;
+
+ strcpy(tuner.name, "no_tuner");
+ tuner.rangelow = 0;
+ tuner.rangehigh = 0;
+ tuner.flags = VIDEO_TUNER_NORM;
+ tuner.mode = VIDEO_MODE_AUTO;
+ tuner.signal = 0xffff;
+
+ if (copy_to_user(arg, &tuner, sizeof(tuner)))
+ return -EFAULT;
+
+ DBG(5, "VIDIOCGTUNER successfully called")
+ return 0;
+ }
+
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner tuner;
+ if (copy_from_user(&tuner, arg, sizeof(tuner)))
+ return -EFAULT;
+
+ if (tuner.tuner != 0)
+ return -EINVAL;
+
+ if (tuner.mode != VIDEO_MODE_AUTO)
+ return -EINVAL;
+
+ DBG(5, "VIDIOCSTUNER successfully called")
+ return 0;
+ }
+
+ case VIDIOCSFBUF:
+ case VIDIOCCAPTURE:
+ case VIDIOCGFREQ:
+ case VIDIOCSFREQ:
+ case VIDIOCGAUDIO:
+ case VIDIOCSAUDIO:
+ case VIDIOCSPLAYMODE:
+ case VIDIOCSWRITEMODE:
+ case VIDIOCGPLAYINFO:
+ case VIDIOCSMICROCODE:
+ case VIDIOCGVBIFMT:
+ case VIDIOCSVBIFMT:
+ DBG(4, "Unsupported V4L1 IOCtl: VIDIOC%s "
+ "(type 0x%01X, "
+ "n. 0x%01X, "
+ "dir. 0x%01X, "
+ "size 0x%02X)",
+ V4L1_IOCTL(cmd),
+ _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd))
+
+ return -EINVAL;
+
+ default:
+ DBG(4, "Invalid V4L1 IOCtl: VIDIOC%s "
+ "type 0x%01X, "
+ "n. 0x%01X, "
+ "dir. 0x%01X, "
+ "size 0x%02X",
+ V4L1_IOCTL(cmd),
+ _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd))
+
+ return -ENOIOCTLCMD;
+
+ } /* end of switch */
+
+ioctl_fail:
+ cam->misconfigured = 1;
+ DBG(1, "VIDIOC%s failed because of hardware problems. "
+ "To use the camera, close and open it again.", V4L1_IOCTL(cmd))
+ return -EFAULT;
+}
+
+
+static struct file_operations w9968cf_fops = {
+ .owner = THIS_MODULE,
+ .open = w9968cf_open,
+ .release = w9968cf_release,
+ .read = w9968cf_read,
+ .ioctl = w9968cf_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .mmap = w9968cf_mmap,
+ .llseek = no_llseek,
+};
+
+
+
+/****************************************************************************
+ * USB probe and V4L registration, disconnect and id_table[] definition *
+ ****************************************************************************/
+
+static int
+w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct w9968cf_device* cam;
+ int err = 0;
+ enum w9968cf_model_id mod_id;
+ struct list_head* ptr;
+ u8 sc = 0; /* number of simultaneous cameras */
+ static unsigned short dev_nr = 0; /* we are handling device number n */
+
+ if (le16_to_cpu(udev->descriptor.idVendor) == winbond_id_table[0].idVendor &&
+ le16_to_cpu(udev->descriptor.idProduct) == winbond_id_table[0].idProduct)
+ mod_id = W9968CF_MOD_CLVBWGP; /* see camlist[] table */
+ else if (le16_to_cpu(udev->descriptor.idVendor) == winbond_id_table[1].idVendor &&
+ le16_to_cpu(udev->descriptor.idProduct) == winbond_id_table[1].idProduct)
+ mod_id = W9968CF_MOD_GENERIC; /* see camlist[] table */
+ else
+ return -ENODEV;
+
+ cam = (struct w9968cf_device*)
+ kzalloc(sizeof(struct w9968cf_device), GFP_KERNEL);
+ if (!cam)
+ return -ENOMEM;
+
+ mutex_init(&cam->dev_mutex);
+ mutex_lock(&cam->dev_mutex);
+
+ cam->usbdev = udev;
+ /* NOTE: a local copy is used to avoid possible race conditions */
+ memcpy(&cam->dev, &udev->dev, sizeof(struct device));
+
+ DBG(2, "%s detected", symbolic(camlist, mod_id))
+
+ if (simcams > W9968CF_MAX_DEVICES)
+ simcams = W9968CF_SIMCAMS;
+
+ /* How many cameras are connected ? */
+ mutex_lock(&w9968cf_devlist_mutex);
+ list_for_each(ptr, &w9968cf_dev_list)
+ sc++;
+ mutex_unlock(&w9968cf_devlist_mutex);
+
+ if (sc >= simcams) {
+ DBG(2, "Device rejected: too many connected cameras "
+ "(max. %u)", simcams)
+ err = -EPERM;
+ goto fail;
+ }
+
+
+ /* Allocate 2 bytes of memory for camera control USB transfers */
+ if (!(cam->control_buffer = kzalloc(2, GFP_KERNEL))) {
+ DBG(1,"Couldn't allocate memory for camera control transfers")
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ /* Allocate 8 bytes of memory for USB data transfers to the FSB */
+ if (!(cam->data_buffer = kzalloc(8, GFP_KERNEL))) {
+ DBG(1, "Couldn't allocate memory for data "
+ "transfers to the FSB")
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ /* Register the V4L device */
+ cam->v4ldev = video_device_alloc();
+ if (!cam->v4ldev) {
+ DBG(1, "Could not allocate memory for a V4L structure")
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ strcpy(cam->v4ldev->name, symbolic(camlist, mod_id));
+ cam->v4ldev->owner = THIS_MODULE;
+ cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
+ cam->v4ldev->hardware = VID_HARDWARE_W9968CF;
+ cam->v4ldev->fops = &w9968cf_fops;
+ cam->v4ldev->minor = video_nr[dev_nr];
+ cam->v4ldev->release = video_device_release;
+ video_set_drvdata(cam->v4ldev, cam);
+ cam->v4ldev->dev = &cam->dev;
+
+ err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
+ video_nr[dev_nr]);
+ if (err) {
+ DBG(1, "V4L device registration failed")
+ if (err == -ENFILE && video_nr[dev_nr] == -1)
+ DBG(2, "Couldn't find a free /dev/videoX node")
+ video_nr[dev_nr] = -1;
+ dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0;
+ goto fail;
+ }
+
+ DBG(2, "V4L device registered as /dev/video%d", cam->v4ldev->minor)
+
+ /* Set some basic constants */
+ w9968cf_configure_camera(cam, udev, mod_id, dev_nr);
+
+ /* Add a new entry into the list of V4L registered devices */
+ mutex_lock(&w9968cf_devlist_mutex);
+ list_add(&cam->v4llist, &w9968cf_dev_list);
+ mutex_unlock(&w9968cf_devlist_mutex);
+ dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0;
+
+ w9968cf_turn_on_led(cam);
+
+ w9968cf_i2c_init(cam);
+
+ usb_set_intfdata(intf, cam);
+ mutex_unlock(&cam->dev_mutex);
+ return 0;
+
+fail: /* Free unused memory */
+ kfree(cam->control_buffer);
+ kfree(cam->data_buffer);
+ if (cam->v4ldev)
+ video_device_release(cam->v4ldev);
+ mutex_unlock(&cam->dev_mutex);
+ kfree(cam);
+ return err;
+}
+
+
+static void w9968cf_usb_disconnect(struct usb_interface* intf)
+{
+ struct w9968cf_device* cam =
+ (struct w9968cf_device*)usb_get_intfdata(intf);
+
+ down_write(&w9968cf_disconnect);
+
+ if (cam) {
+ /* Prevent concurrent accesses to data */
+ mutex_lock(&cam->dev_mutex);
+
+ cam->disconnected = 1;
+
+ DBG(2, "Disconnecting %s...", symbolic(camlist, cam->id))
+
+ wake_up_interruptible_all(&cam->open);
+
+ if (cam->users) {
+ DBG(2, "The device is open (/dev/video%d)! "
+ "Process name: %s. Deregistration and memory "
+ "deallocation are deferred on close.",
+ cam->v4ldev->minor, cam->command)
+ cam->misconfigured = 1;
+ w9968cf_stop_transfer(cam);
+ wake_up_interruptible(&cam->wait_queue);
+ } else
+ w9968cf_release_resources(cam);
+
+ mutex_unlock(&cam->dev_mutex);
+
+ if (!cam->users)
+ kfree(cam);
+ }
+
+ up_write(&w9968cf_disconnect);
+}
+
+
+static struct usb_driver w9968cf_usb_driver = {
+ .name = "w9968cf",
+ .id_table = winbond_id_table,
+ .probe = w9968cf_usb_probe,
+ .disconnect = w9968cf_usb_disconnect,
+};
+
+
+
+/****************************************************************************
+ * Module init, exit and intermodule communication *
+ ****************************************************************************/
+
+static int __init w9968cf_module_init(void)
+{
+ int err;
+
+ KDBG(2, W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION)
+ KDBG(3, W9968CF_MODULE_AUTHOR)
+
+ if (ovmod_load)
+ request_module("ovcamchip");
+
+ if ((err = usb_register(&w9968cf_usb_driver)))
+ return err;
+
+ return 0;
+}
+
+
+static void __exit w9968cf_module_exit(void)
+{
+ /* w9968cf_usb_disconnect() will be called */
+ usb_deregister(&w9968cf_usb_driver);
+
+ KDBG(2, W9968CF_MODULE_NAME" deregistered")
+}
+
+
+module_init(w9968cf_module_init);
+module_exit(w9968cf_module_exit);
+
diff --git a/linux/drivers/media/video/w9968cf.h b/linux/drivers/media/video/w9968cf.h
new file mode 100644
index 000000000..a89013fa3
--- /dev/null
+++ b/linux/drivers/media/video/w9968cf.h
@@ -0,0 +1,331 @@
+/***************************************************************************
+ * Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip. *
+ * *
+ * Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _W9968CF_H_
+#define _W9968CF_H_
+
+#include "compat.h"
+#include <linux/videodev.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/config.h>
+#include <linux/param.h>
+#include <linux/types.h>
+#include <linux/rwsem.h>
+#include <linux/mutex.h>
+
+#include <media/ovcamchip.h>
+
+#include "w9968cf_vpp.h"
+
+
+/****************************************************************************
+ * Default values *
+ ****************************************************************************/
+
+#define W9968CF_OVMOD_LOAD 1 /* automatic 'ovcamchip' module loading */
+#define W9968CF_VPPMOD_LOAD 1 /* automatic 'w9968cf-vpp' module loading */
+
+/* Comment/uncomment the following line to enable/disable debugging messages */
+#define W9968CF_DEBUG
+
+/* These have effect only if W9968CF_DEBUG is defined */
+#define W9968CF_DEBUG_LEVEL 2 /* from 0 to 6. 0 for no debug informations */
+#define W9968CF_SPECIFIC_DEBUG 0 /* 0 or 1 */
+
+#define W9968CF_MAX_DEVICES 32
+#define W9968CF_SIMCAMS W9968CF_MAX_DEVICES /* simultaneous cameras */
+
+#define W9968CF_MAX_BUFFERS 32
+#define W9968CF_BUFFERS 2 /* n. of frame buffers from 2 to MAX_BUFFERS */
+
+/* Maximum data payload sizes in bytes for alternate settings */
+static const u16 wMaxPacketSize[] = {1023, 959, 895, 831, 767, 703, 639, 575,
+ 511, 447, 383, 319, 255, 191, 127, 63};
+#define W9968CF_PACKET_SIZE 1023 /* according to wMaxPacketSizes[] */
+#define W9968CF_MIN_PACKET_SIZE 63 /* minimum value */
+#define W9968CF_ISO_PACKETS 5 /* n.of packets for isochronous transfers */
+#define W9968CF_USB_CTRL_TIMEOUT 1000 /* timeout (ms) for usb control commands */
+#define W9968CF_URBS 2 /* n. of scheduled URBs for ISO transfer */
+
+#define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */
+#define W9968CF_I2C_RW_RETRIES 15 /* number of max I2C r/w retries */
+
+/* Available video formats */
+struct w9968cf_format {
+ const u16 palette;
+ const u16 depth;
+ const u8 compression;
+};
+
+static const struct w9968cf_format w9968cf_formatlist[] = {
+ { VIDEO_PALETTE_UYVY, 16, 0 }, /* original video */
+ { VIDEO_PALETTE_YUV422P, 16, 1 }, /* with JPEG compression */
+ { VIDEO_PALETTE_YUV420P, 12, 1 }, /* with JPEG compression */
+ { VIDEO_PALETTE_YUV420, 12, 1 }, /* same as YUV420P */
+ { VIDEO_PALETTE_YUYV, 16, 0 }, /* software conversion */
+ { VIDEO_PALETTE_YUV422, 16, 0 }, /* software conversion */
+ { VIDEO_PALETTE_GREY, 8, 0 }, /* software conversion */
+ { VIDEO_PALETTE_RGB555, 16, 0 }, /* software conversion */
+ { VIDEO_PALETTE_RGB565, 16, 0 }, /* software conversion */
+ { VIDEO_PALETTE_RGB24, 24, 0 }, /* software conversion */
+ { VIDEO_PALETTE_RGB32, 32, 0 }, /* software conversion */
+ { 0, 0, 0 } /* 0 is a terminating entry */
+};
+
+#define W9968CF_DECOMPRESSION 2 /* decomp:0=disable,1=force,2=any formats */
+#define W9968CF_PALETTE_DECOMP_OFF VIDEO_PALETTE_UYVY /* when decomp=0 */
+#define W9968CF_PALETTE_DECOMP_FORCE VIDEO_PALETTE_YUV420P /* when decomp=1 */
+#define W9968CF_PALETTE_DECOMP_ON VIDEO_PALETTE_UYVY /* when decomp=2 */
+
+#define W9968CF_FORCE_RGB 0 /* read RGB instead of BGR, yes=1/no=0 */
+
+#define W9968CF_MAX_WIDTH 800 /* Has effect if up-scaling is on */
+#define W9968CF_MAX_HEIGHT 600 /* Has effect if up-scaling is on */
+#define W9968CF_WIDTH 320 /* from 128 to 352, multiple of 16 */
+#define W9968CF_HEIGHT 240 /* from 96 to 288, multiple of 16 */
+
+#define W9968CF_CLAMPING 0 /* 0 disable, 1 enable video data clamping */
+#define W9968CF_FILTER_TYPE 0 /* 0 disable 1 (1-2-1), 2 (2-3-6-3-2) */
+#define W9968CF_DOUBLE_BUFFER 1 /* 0 disable, 1 enable double buffer */
+#define W9968CF_LARGEVIEW 1 /* 0 disable, 1 enable */
+#define W9968CF_UPSCALING 0 /* 0 disable, 1 enable */
+
+#define W9968CF_MONOCHROME 0 /* 0 not monochrome, 1 monochrome sensor */
+#define W9968CF_BRIGHTNESS 31000 /* from 0 to 65535 */
+#define W9968CF_HUE 32768 /* from 0 to 65535 */
+#define W9968CF_COLOUR 32768 /* from 0 to 65535 */
+#define W9968CF_CONTRAST 50000 /* from 0 to 65535 */
+#define W9968CF_WHITENESS 32768 /* from 0 to 65535 */
+
+#define W9968CF_AUTOBRIGHT 0 /* 0 disable, 1 enable automatic brightness */
+#define W9968CF_AUTOEXP 1 /* 0 disable, 1 enable automatic exposure */
+#define W9968CF_LIGHTFREQ 50 /* light frequency. 50Hz (Europe) or 60Hz */
+#define W9968CF_BANDINGFILTER 0 /* 0 disable, 1 enable banding filter */
+#define W9968CF_BACKLIGHT 0 /* 0 or 1, 1=object is lit from behind */
+#define W9968CF_MIRROR 0 /* 0 or 1 [don't] reverse image horizontally*/
+
+#define W9968CF_CLOCKDIV -1 /* -1 = automatic clock divisor */
+#define W9968CF_DEF_CLOCKDIVISOR 0 /* default sensor clock divisor value */
+
+
+/****************************************************************************
+ * Globals *
+ ****************************************************************************/
+
+#define W9968CF_MODULE_NAME "V4L driver for W996[87]CF JPEG USB " \
+ "Dual Mode Camera Chip"
+#define W9968CF_MODULE_VERSION "1:1.33-basic"
+#define W9968CF_MODULE_AUTHOR "(C) 2002-2004 Luca Risolia"
+#define W9968CF_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
+#define W9968CF_MODULE_LICENSE "GPL"
+
+static const struct usb_device_id winbond_id_table[] = {
+ {
+ /* Creative Labs Video Blaster WebCam Go Plus */
+ USB_DEVICE(0x041e, 0x4003),
+ .driver_info = (unsigned long)"w9968cf",
+ },
+ {
+ /* Generic W996[87]CF JPEG USB Dual Mode Camera */
+ USB_DEVICE(0x1046, 0x9967),
+ .driver_info = (unsigned long)"w9968cf",
+ },
+ { } /* terminating entry */
+};
+
+/* W996[87]CF camera models, internal ids: */
+enum w9968cf_model_id {
+ W9968CF_MOD_GENERIC = 1, /* Generic W996[87]CF based device */
+ W9968CF_MOD_CLVBWGP = 11,/*Creative Labs Video Blaster WebCam Go Plus*/
+ W9968CF_MOD_ADPVDMA = 21, /* Aroma Digi Pen VGA Dual Mode ADG-5000 */
+ W9986CF_MOD_AAU = 31, /* AVerMedia AVerTV USB */
+ W9968CF_MOD_CLVBWG = 34, /* Creative Labs Video Blaster WebCam Go */
+ W9968CF_MOD_LL = 37, /* Lebon LDC-035A */
+ W9968CF_MOD_EEEMC = 40, /* Ezonics EZ-802 EZMega Cam */
+ W9968CF_MOD_OOE = 42, /* OmniVision OV8610-EDE */
+ W9968CF_MOD_ODPVDMPC = 43,/* OPCOM Digi Pen VGA Dual Mode Pen Camera */
+ W9968CF_MOD_PDPII = 46, /* Pretec Digi Pen-II */
+ W9968CF_MOD_PDP480 = 49, /* Pretec DigiPen-480 */
+};
+
+enum w9968cf_frame_status {
+ F_READY, /* finished grabbing & ready to be read/synced */
+ F_GRABBING, /* in the process of being grabbed into */
+ F_ERROR, /* something bad happened while processing */
+ F_UNUSED /* unused (no VIDIOCMCAPTURE) */
+};
+
+struct w9968cf_frame_t {
+ void* buffer;
+ unsigned long size;
+ u32 length;
+ int number;
+ enum w9968cf_frame_status status;
+ struct w9968cf_frame_t* next;
+ u8 queued;
+};
+
+enum w9968cf_vpp_flag {
+ VPP_NONE = 0x00,
+ VPP_UPSCALE = 0x01,
+ VPP_SWAP_YUV_BYTES = 0x02,
+ VPP_DECOMPRESSION = 0x04,
+ VPP_UYVY_TO_RGBX = 0x08,
+};
+
+/* Main device driver structure */
+struct w9968cf_device {
+ struct device dev; /* device structure */
+
+ enum w9968cf_model_id id; /* private device identifier */
+
+ struct video_device* v4ldev; /* -> V4L structure */
+ struct list_head v4llist; /* entry of the list of V4L cameras */
+
+ struct usb_device* usbdev; /* -> main USB structure */
+ struct urb* urb[W9968CF_URBS]; /* -> USB request block structs */
+ void* transfer_buffer[W9968CF_URBS]; /* -> ISO transfer buffers */
+ u16* control_buffer; /* -> buffer for control req.*/
+ u16* data_buffer; /* -> data to send to the FSB */
+
+ struct w9968cf_frame_t frame[W9968CF_MAX_BUFFERS];
+ struct w9968cf_frame_t frame_tmp; /* temporary frame */
+ struct w9968cf_frame_t frame_vpp; /* helper frame.*/
+ struct w9968cf_frame_t* frame_current; /* -> frame being grabbed */
+ struct w9968cf_frame_t* requested_frame[W9968CF_MAX_BUFFERS];
+
+ u8 max_buffers, /* number of requested buffers */
+ force_palette, /* yes=1/no=0 */
+ force_rgb, /* read RGB instead of BGR, yes=1, no=0 */
+ double_buffer, /* hardware double buffering yes=1/no=0 */
+ clamping, /* video data clamping yes=1/no=0 */
+ filter_type, /* 0=disabled, 1=3 tap, 2=5 tap filter */
+ capture, /* 0=disabled, 1=enabled */
+ largeview, /* 0=disabled, 1=enabled */
+ decompression, /* 0=disabled, 1=forced, 2=allowed */
+ upscaling; /* software image scaling, 0=enabled, 1=disabled */
+
+ struct video_picture picture; /* current picture settings */
+ struct video_window window; /* current window settings */
+
+ u16 hw_depth, /* depth (used by the chip) */
+ hw_palette, /* palette (used by the chip) */
+ hw_width, /* width (used by the chip) */
+ hw_height, /* height (used by the chip) */
+ hs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */
+ vs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */
+ start_cropx, /* pixels from HS inactive edge to 1st cropped pixel*/
+ start_cropy; /* pixels from VS inactive edge to 1st cropped pixel*/
+
+ enum w9968cf_vpp_flag vpp_flag; /* post-processing routines in use */
+
+ u8 nbuffers, /* number of allocated frame buffers */
+ altsetting, /* camera alternate setting */
+ disconnected, /* flag: yes=1, no=0 */
+ misconfigured, /* flag: yes=1, no=0 */
+ users, /* flag: number of users holding the device */
+ streaming; /* flag: yes=1, no=0 */
+
+ u8 sensor_initialized; /* flag: yes=1, no=0 */
+
+ /* Determined by the image sensor type: */
+ int sensor, /* type of image sensor chip (CC_*) */
+ monochrome; /* image sensor is (probably) monochrome */
+ u16 maxwidth, /* maximum width supported by the image sensor */
+ maxheight, /* maximum height supported by the image sensor */
+ minwidth, /* minimum width supported by the image sensor */
+ minheight; /* minimum height supported by the image sensor */
+ u8 auto_brt, /* auto brightness enabled flag */
+ auto_exp, /* auto exposure enabled flag */
+ backlight, /* backlight exposure algorithm flag */
+ mirror, /* image is reversed horizontally */
+ lightfreq, /* power (lighting) frequency */
+ bandfilt; /* banding filter enabled flag */
+ s8 clockdiv; /* clock divisor */
+
+ /* I2C interface to kernel */
+ struct i2c_adapter i2c_adapter;
+ struct i2c_client* sensor_client;
+
+ /* Locks */
+ struct mutex dev_mutex, /* for probe, disconnect,open and close */
+ fileop_mutex; /* for read and ioctl */
+ spinlock_t urb_lock, /* for submit_urb() and unlink_urb() */
+ flist_lock; /* for requested frame list accesses */
+ wait_queue_head_t open, wait_queue;
+
+ char command[16]; /* name of the program holding the device */
+};
+
+
+/****************************************************************************
+ * Macros for debugging *
+ ****************************************************************************/
+
+#undef DBG
+#undef KDBG
+#ifdef W9968CF_DEBUG
+/* For device specific debugging messages */
+# define DBG(level, fmt, args...) \
+{ \
+ if ( ((specific_debug) && (debug == (level))) || \
+ ((!specific_debug) && (debug >= (level))) ) { \
+ if ((level) == 1) \
+ dev_err(&cam->dev, fmt "\n", ## args); \
+ else if ((level) == 2 || (level) == 3) \
+ dev_info(&cam->dev, fmt "\n", ## args); \
+ else if ((level) == 4) \
+ dev_warn(&cam->dev, fmt "\n", ## args); \
+ else if ((level) >= 5) \
+ dev_info(&cam->dev, "[%s:%d] " fmt "\n", \
+ __FUNCTION__, __LINE__ , ## args); \
+ } \
+}
+/* For generic kernel (not device specific) messages */
+# define KDBG(level, fmt, args...) \
+{ \
+ if ( ((specific_debug) && (debug == (level))) || \
+ ((!specific_debug) && (debug >= (level))) ) { \
+ if ((level) >= 1 && (level) <= 4) \
+ pr_info("w9968cf: " fmt "\n", ## args); \
+ else if ((level) >= 5) \
+ pr_debug("w9968cf: [%s:%d] " fmt "\n", __FUNCTION__, \
+ __LINE__ , ## args); \
+ } \
+}
+#else
+ /* Not debugging: nothing */
+# define DBG(level, fmt, args...) do {;} while(0);
+# define KDBG(level, fmt, args...) do {;} while(0);
+#endif
+
+#undef PDBG
+#define PDBG(fmt, args...) \
+dev_info(&cam->dev, "[%s:%d] " fmt "\n", __FUNCTION__, __LINE__ , ## args);
+
+#undef PDBGG
+#define PDBGG(fmt, args...) do {;} while(0); /* nothing: it's a placeholder */
+
+#endif /* _W9968CF_H_ */
diff --git a/linux/drivers/media/video/w9968cf_decoder.h b/linux/drivers/media/video/w9968cf_decoder.h
new file mode 100644
index 000000000..59decbfc5
--- /dev/null
+++ b/linux/drivers/media/video/w9968cf_decoder.h
@@ -0,0 +1,86 @@
+/***************************************************************************
+ * Video decoder for the W996[87]CF driver for Linux. *
+ * *
+ * Copyright (C) 2003 2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _W9968CF_DECODER_H_
+#define _W9968CF_DECODER_H_
+
+/* Comment/uncomment this for high/low quality of compressed video */
+#define W9968CF_DEC_FAST_LOWQUALITY_VIDEO
+
+#ifdef W9968CF_DEC_FAST_LOWQUALITY_VIDEO
+static const unsigned char Y_QUANTABLE[64] = {
+ 16, 11, 10, 16, 24, 40, 51, 61,
+ 12, 12, 14, 19, 26, 58, 60, 55,
+ 14, 13, 16, 24, 40, 57, 69, 56,
+ 14, 17, 22, 29, 51, 87, 80, 62,
+ 18, 22, 37, 56, 68, 109, 103, 77,
+ 24, 35, 55, 64, 81, 104, 113, 92,
+ 49, 64, 78, 87, 103, 121, 120, 101,
+ 72, 92, 95, 98, 112, 100, 103, 99
+};
+
+static const unsigned char UV_QUANTABLE[64] = {
+ 17, 18, 24, 47, 99, 99, 99, 99,
+ 18, 21, 26, 66, 99, 99, 99, 99,
+ 24, 26, 56, 99, 99, 99, 99, 99,
+ 47, 66, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99
+};
+#else
+static const unsigned char Y_QUANTABLE[64] = {
+ 8, 5, 5, 8, 12, 20, 25, 30,
+ 6, 6, 7, 9, 13, 29, 30, 27,
+ 7, 6, 8, 12, 20, 28, 34, 28,
+ 7, 8, 11, 14, 25, 43, 40, 31,
+ 9, 11, 18, 28, 34, 54, 51, 38,
+ 12, 17, 27, 32, 40, 52, 56, 46,
+ 24, 32, 39, 43, 51, 60, 60, 50,
+ 36, 46, 47, 49, 56, 50, 51, 49
+};
+
+static const unsigned char UV_QUANTABLE[64] = {
+ 8, 9, 12, 23, 49, 49, 49, 49,
+ 9, 10, 13, 33, 49, 49, 49, 49,
+ 12, 13, 28, 49, 49, 49, 49, 49,
+ 23, 33, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49
+};
+#endif
+
+#define W9968CF_DEC_ERR_CORRUPTED_DATA -1
+#define W9968CF_DEC_ERR_BUF_OVERFLOW -2
+#define W9968CF_DEC_ERR_NO_SOI -3
+#define W9968CF_DEC_ERR_NO_SOF0 -4
+#define W9968CF_DEC_ERR_NO_SOS -5
+#define W9968CF_DEC_ERR_NO_EOI -6
+
+extern void w9968cf_init_decoder(void);
+extern int w9968cf_check_headers(const unsigned char* Pin,
+ const unsigned long BUF_SIZE);
+extern int w9968cf_decode(const char* Pin, const unsigned long BUF_SIZE,
+ const unsigned W, const unsigned H, char* Pout);
+
+#endif /* _W9968CF_DECODER_H_ */
diff --git a/linux/drivers/media/video/w9968cf_vpp.h b/linux/drivers/media/video/w9968cf_vpp.h
new file mode 100644
index 000000000..88c9b6c0c
--- /dev/null
+++ b/linux/drivers/media/video/w9968cf_vpp.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+ * Interface for video post-processing functions for the W996[87]CF driver *
+ * for Linux. *
+ * *
+ * Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
+ ***************************************************************************/
+
+#ifndef _W9968CF_VPP_H_
+#define _W9968CF_VPP_H_
+
+#include <linux/module.h>
+#include <asm/types.h>
+
+struct w9968cf_vpp_t {
+ struct module* owner;
+ int (*check_headers)(const unsigned char*, const unsigned long);
+ int (*decode)(const char*, const unsigned long, const unsigned,
+ const unsigned, char*);
+ void (*swap_yuvbytes)(void*, unsigned long);
+ void (*uyvy_to_rgbx)(u8*, unsigned long, u8*, u16, u8);
+ void (*scale_up)(u8*, u8*, u16, u16, u16, u16, u16);
+
+ u8 busy; /* read-only flag: module is/is not in use */
+};
+
+#endif /* _W9968CF_VPP_H_ */
diff --git a/linux/drivers/media/video/zoran.h b/linux/drivers/media/video/zoran.h
new file mode 100644
index 000000000..0166f555a
--- /dev/null
+++ b/linux/drivers/media/video/zoran.h
@@ -0,0 +1,515 @@
+/*
+ * zoran - Iomega Buz driver
+ *
+ * Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de>
+ *
+ * based on
+ *
+ * zoran.0.0.3 Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ *
+ * and
+ *
+ * bttv - Bt848 frame grabber driver
+ * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ * & Marcus Metzler (mocm@thp.uni-koeln.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _BUZ_H_
+#define _BUZ_H_
+
+struct zoran_requestbuffers {
+ unsigned long count; /* Number of buffers for MJPEG grabbing */
+ unsigned long size; /* Size PER BUFFER in bytes */
+};
+
+struct zoran_sync {
+ unsigned long frame; /* number of buffer that has been free'd */
+ unsigned long length; /* number of code bytes in buffer (capture only) */
+ unsigned long seq; /* frame sequence number */
+ struct timeval timestamp; /* timestamp */
+};
+
+struct zoran_status {
+ int input; /* Input channel, has to be set prior to BUZIOC_G_STATUS */
+ int signal; /* Returned: 1 if valid video signal detected */
+ int norm; /* Returned: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */
+ int color; /* Returned: 1 if color signal detected */
+};
+
+struct zoran_params {
+
+ /* The following parameters can only be queried */
+
+ int major_version; /* Major version number of driver */
+ int minor_version; /* Minor version number of driver */
+
+ /* Main control parameters */
+
+ int input; /* Input channel: 0 = Composite, 1 = S-VHS */
+ int norm; /* Norm: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */
+ int decimation; /* decimation of captured video,
+ * enlargement of video played back.
+ * Valid values are 1, 2, 4 or 0.
+ * 0 is a special value where the user
+ * has full control over video scaling */
+
+ /* The following parameters only have to be set if decimation==0,
+ * for other values of decimation they provide the data how the image is captured */
+
+ int HorDcm; /* Horizontal decimation: 1, 2 or 4 */
+ int VerDcm; /* Vertical decimation: 1 or 2 */
+ int TmpDcm; /* Temporal decimation: 1 or 2,
+ * if TmpDcm==2 in capture every second frame is dropped,
+ * in playback every frame is played twice */
+ int field_per_buff; /* Number of fields per buffer: 1 or 2 */
+ int img_x; /* start of image in x direction */
+ int img_y; /* start of image in y direction */
+ int img_width; /* image width BEFORE decimation,
+ * must be a multiple of HorDcm*16 */
+ int img_height; /* image height BEFORE decimation,
+ * must be a multiple of VerDcm*8 */
+
+ /* --- End of parameters for decimation==0 only --- */
+
+ /* JPEG control parameters */
+
+ int quality; /* Measure for quality of compressed images.
+ * Scales linearly with the size of the compressed images.
+ * Must be beetween 0 and 100, 100 is a compression
+ * ratio of 1:4 */
+
+ int odd_even; /* Which field should come first ??? */
+
+ int APPn; /* Number of APP segment to be written, must be 0..15 */
+ int APP_len; /* Length of data in JPEG APPn segment */
+ char APP_data[60]; /* Data in the JPEG APPn segment. */
+
+ int COM_len; /* Length of data in JPEG COM segment */
+ char COM_data[60]; /* Data in JPEG COM segment */
+
+ unsigned long jpeg_markers; /* Which markers should go into the JPEG output.
+ * Unless you exactly know what you do, leave them untouched.
+ * Inluding less markers will make the resulting code
+ * smaller, but there will be fewer aplications
+ * which can read it.
+ * The presence of the APP and COM marker is
+ * influenced by APP0_len and COM_len ONLY! */
+#define JPEG_MARKER_DHT (1<<3) /* Define Huffman Tables */
+#define JPEG_MARKER_DQT (1<<4) /* Define Quantization Tables */
+#define JPEG_MARKER_DRI (1<<5) /* Define Restart Interval */
+#define JPEG_MARKER_COM (1<<6) /* Comment segment */
+#define JPEG_MARKER_APP (1<<7) /* App segment, driver will allways use APP0 */
+
+ int VFIFO_FB; /* Flag for enabling Video Fifo Feedback.
+ * If this flag is turned on and JPEG decompressing
+ * is going to the screen, the decompress process
+ * is stopped every time the Video Fifo is full.
+ * This enables a smooth decompress to the screen
+ * but the video output signal will get scrambled */
+
+ /* Misc */
+
+ char reserved[312]; /* Makes 512 bytes for this structure */
+};
+
+/*
+Private IOCTL to set up for displaying MJPEG
+*/
+#define BUZIOC_G_PARAMS _IOR ('v', BASE_VIDIOCPRIVATE+0, struct zoran_params)
+#define BUZIOC_S_PARAMS _IOWR('v', BASE_VIDIOCPRIVATE+1, struct zoran_params)
+#define BUZIOC_REQBUFS _IOWR('v', BASE_VIDIOCPRIVATE+2, struct zoran_requestbuffers)
+#define BUZIOC_QBUF_CAPT _IOW ('v', BASE_VIDIOCPRIVATE+3, int)
+#define BUZIOC_QBUF_PLAY _IOW ('v', BASE_VIDIOCPRIVATE+4, int)
+#define BUZIOC_SYNC _IOR ('v', BASE_VIDIOCPRIVATE+5, struct zoran_sync)
+#define BUZIOC_G_STATUS _IOWR('v', BASE_VIDIOCPRIVATE+6, struct zoran_status)
+
+
+#ifdef __KERNEL__
+
+#define MAJOR_VERSION 0 /* driver major version */
+#define MINOR_VERSION 9 /* driver minor version */
+#define RELEASE_VERSION 5 /* release version */
+
+#define ZORAN_NAME "ZORAN" /* name of the device */
+
+#define ZR_DEVNAME(zr) ((zr)->name)
+
+#define BUZ_MAX_WIDTH (zr->timing->Wa)
+#define BUZ_MAX_HEIGHT (zr->timing->Ha)
+#define BUZ_MIN_WIDTH 32 /* never display less than 32 pixels */
+#define BUZ_MIN_HEIGHT 24 /* never display less than 24 rows */
+
+#define BUZ_NUM_STAT_COM 4
+#define BUZ_MASK_STAT_COM 3
+
+#define BUZ_MAX_FRAME 256 /* Must be a power of 2 */
+#define BUZ_MASK_FRAME 255 /* Must be BUZ_MAX_FRAME-1 */
+
+#define BUZ_MAX_INPUT 8
+
+#if VIDEO_MAX_FRAME <= 32
+# define V4L_MAX_FRAME 32
+#elif VIDEO_MAX_FRAME <= 64
+# define V4L_MAX_FRAME 64
+#else
+# error "Too many video frame buffers to handle"
+#endif
+#define V4L_MASK_FRAME (V4L_MAX_FRAME - 1)
+
+#define MAX_KMALLOC_MEM (128*1024)
+
+#include "zr36057.h"
+
+enum card_type {
+ UNKNOWN = -1,
+
+ /* Pinnacle/Miro */
+ DC10_old, /* DC30 like */
+ DC10_new, /* DC10plus like */
+ DC10plus,
+ DC30,
+ DC30plus,
+
+ /* Linux Media Labs */
+ LML33,
+ LML33R10,
+
+ /* Iomega */
+ BUZ,
+
+ /* total number of cards */
+ NUM_CARDS
+};
+
+enum zoran_codec_mode {
+ BUZ_MODE_IDLE, /* nothing going on */
+ BUZ_MODE_MOTION_COMPRESS, /* grabbing frames */
+ BUZ_MODE_MOTION_DECOMPRESS, /* playing frames */
+ BUZ_MODE_STILL_COMPRESS, /* still frame conversion */
+ BUZ_MODE_STILL_DECOMPRESS /* still frame conversion */
+};
+
+enum zoran_buffer_state {
+ BUZ_STATE_USER, /* buffer is owned by application */
+ BUZ_STATE_PEND, /* buffer is queued in pend[] ready to feed to I/O */
+ BUZ_STATE_DMA, /* buffer is queued in dma[] for I/O */
+ BUZ_STATE_DONE /* buffer is ready to return to application */
+};
+
+enum zoran_map_mode {
+ ZORAN_MAP_MODE_RAW,
+ ZORAN_MAP_MODE_JPG_REC,
+#define ZORAN_MAP_MODE_JPG ZORAN_MAP_MODE_JPG_REC
+ ZORAN_MAP_MODE_JPG_PLAY,
+};
+
+enum gpio_type {
+ GPIO_JPEG_SLEEP = 0,
+ GPIO_JPEG_RESET,
+ GPIO_JPEG_FRAME,
+ GPIO_VID_DIR,
+ GPIO_VID_EN,
+ GPIO_VID_RESET,
+ GPIO_CLK_SEL1,
+ GPIO_CLK_SEL2,
+ GPIO_MAX,
+};
+
+enum gpcs_type {
+ GPCS_JPEG_RESET = 0,
+ GPCS_JPEG_START,
+ GPCS_MAX,
+};
+
+struct zoran_format {
+ char *name;
+ int palette;
+ __u32 fourcc;
+ int colorspace;
+ int depth;
+ __u32 flags;
+};
+/* flags */
+#define ZORAN_FORMAT_COMPRESSED 1<<0
+#define ZORAN_FORMAT_OVERLAY 1<<1
+#define ZORAN_FORMAT_CAPTURE 1<<2
+#define ZORAN_FORMAT_PLAYBACK 1<<3
+
+/* overlay-settings */
+struct zoran_overlay_settings {
+ int is_set;
+ int x, y, width, height; /* position */
+ int clipcount; /* position and number of clips */
+ const struct zoran_format *format; /* overlay format */
+};
+
+/* v4l-capture settings */
+struct zoran_v4l_settings {
+ int width, height, bytesperline; /* capture size */
+ const struct zoran_format *format; /* capture format */
+};
+
+/* whoops, this one is undeclared if !v4l2 */
+#ifndef HAVE_V4L2
+struct v4l2_jpegcompression {
+ int quality;
+ int APPn;
+ int APP_len;
+ char APP_data[60];
+ int COM_len;
+ char COM_data[60];
+ __u32 jpeg_markers;
+ __u8 reserved[116];
+};
+#endif
+
+/* jpg-capture/-playback settings */
+struct zoran_jpg_settings {
+ int decimation; /* this bit is used to set everything to default */
+ int HorDcm, VerDcm, TmpDcm; /* capture decimation settings (TmpDcm=1 means both fields) */
+ int field_per_buff, odd_even; /* field-settings (odd_even=1 (+TmpDcm=1) means top-field-first) */
+ int img_x, img_y, img_width, img_height; /* crop settings (subframe capture) */
+ struct v4l2_jpegcompression jpg_comp; /* JPEG-specific capture settings */
+};
+
+struct zoran_mapping {
+ struct file *file;
+ int count;
+};
+
+struct zoran_jpg_buffer {
+ struct zoran_mapping *map;
+ u32 *frag_tab; /* addresses of frag table */
+ u32 frag_tab_bus; /* same value cached to save time in ISR */
+ enum zoran_buffer_state state; /* non-zero if corresponding buffer is in use in grab queue */
+ struct zoran_sync bs; /* DONE: info to return to application */
+};
+
+struct zoran_v4l_buffer {
+ struct zoran_mapping *map;
+ char *fbuffer; /* virtual address of frame buffer */
+ unsigned long fbuffer_phys; /* physical address of frame buffer */
+ unsigned long fbuffer_bus; /* bus address of frame buffer */
+ enum zoran_buffer_state state; /* state: unused/pending/done */
+ struct zoran_sync bs; /* DONE: info to return to application */
+};
+
+enum zoran_lock_activity {
+ ZORAN_FREE, /* free for use */
+ ZORAN_ACTIVE, /* active but unlocked */
+ ZORAN_LOCKED, /* locked */
+};
+
+/* buffer collections */
+struct zoran_jpg_struct {
+ enum zoran_lock_activity active; /* feature currently in use? */
+ struct zoran_jpg_buffer buffer[BUZ_MAX_FRAME]; /* buffers */
+ int num_buffers, buffer_size;
+ u8 allocated; /* Flag if buffers are allocated */
+ u8 ready_to_be_freed; /* hack - see zoran_driver.c */
+ u8 need_contiguous; /* Flag if contiguous buffers are needed */
+};
+
+struct zoran_v4l_struct {
+ enum zoran_lock_activity active; /* feature currently in use? */
+ struct zoran_v4l_buffer buffer[VIDEO_MAX_FRAME]; /* buffers */
+ int num_buffers, buffer_size;
+ u8 allocated; /* Flag if buffers are allocated */
+ u8 ready_to_be_freed; /* hack - see zoran_driver.c */
+};
+
+struct zoran;
+
+/* zoran_fh contains per-open() settings */
+struct zoran_fh {
+ struct zoran *zr;
+
+ enum zoran_map_mode map_mode; /* Flag which bufferset will map by next mmap() */
+
+ struct zoran_overlay_settings overlay_settings;
+ u32 *overlay_mask; /* overlay mask */
+ enum zoran_lock_activity overlay_active; /* feature currently in use? */
+
+ struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */
+ struct zoran_v4l_struct v4l_buffers; /* V4L buffers' info */
+
+ struct zoran_jpg_settings jpg_settings; /* structure with a lot of things to play with */
+ struct zoran_jpg_struct jpg_buffers; /* MJPEG buffers' info */
+};
+
+struct card_info {
+ enum card_type type;
+ char name[32];
+ u16 i2c_decoder, i2c_encoder; /* I2C types */
+ u16 video_vfe, video_codec; /* videocodec types */
+ u16 audio_chip; /* audio type */
+ u16 vendor_id, device_id; /* subsystem vendor/device ID */
+
+ int inputs; /* number of video inputs */
+ struct input {
+ int muxsel;
+ char name[32];
+ } input[BUZ_MAX_INPUT];
+
+ int norms;
+ struct tvnorm *tvn[3]; /* supported TV norms */
+
+ u32 jpeg_int; /* JPEG interrupt */
+ u32 vsync_int; /* VSYNC interrupt */
+ s8 gpio[GPIO_MAX];
+ u8 gpcs[GPCS_MAX];
+
+ struct vfe_polarity vfe_pol;
+ u8 gpio_pol[GPIO_MAX];
+
+ /* is the /GWS line conected? */
+ u8 gws_not_connected;
+
+ void (*init) (struct zoran * zr);
+};
+
+struct zoran {
+ struct video_device *video_dev;
+
+ struct i2c_adapter i2c_adapter; /* */
+ struct i2c_algo_bit_data i2c_algo; /* */
+ u32 i2cbr;
+
+ struct i2c_client *decoder; /* video decoder i2c client */
+ struct i2c_client *encoder; /* video encoder i2c client */
+
+ struct videocodec *codec; /* video codec */
+ struct videocodec *vfe; /* video front end */
+
+ struct mutex resource_lock; /* prevent evil stuff */
+
+ u8 initialized; /* flag if zoran has been correctly initalized */
+ int user; /* number of current users */
+ struct card_info card;
+ struct tvnorm *timing;
+
+ unsigned short id; /* number of this device */
+ char name[32]; /* name of this device */
+ struct pci_dev *pci_dev; /* PCI device */
+ unsigned char revision; /* revision of zr36057 */
+ unsigned int zr36057_adr; /* bus address of IO mem returned by PCI BIOS */
+ unsigned char __iomem *zr36057_mem;/* pointer to mapped IO memory */
+
+ spinlock_t spinlock; /* Spinlock */
+
+ /* Video for Linux parameters */
+ int input, norm; /* card's norm and input - norm=VIDEO_MODE_* */
+ int hue, saturation, contrast, brightness; /* Current picture params */
+ struct video_buffer buffer; /* Current buffer params */
+ struct zoran_overlay_settings overlay_settings;
+ u32 *overlay_mask; /* overlay mask */
+ enum zoran_lock_activity overlay_active; /* feature currently in use? */
+
+ wait_queue_head_t v4l_capq;
+
+ int v4l_overlay_active; /* Overlay grab is activated */
+ int v4l_memgrab_active; /* Memory grab is activated */
+
+ int v4l_grab_frame; /* Frame number being currently grabbed */
+#define NO_GRAB_ACTIVE (-1)
+ unsigned long v4l_grab_seq; /* Number of frames grabbed */
+ struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */
+
+ /* V4L grab queue of frames pending */
+ unsigned long v4l_pend_head;
+ unsigned long v4l_pend_tail;
+ unsigned long v4l_sync_tail;
+ int v4l_pend[V4L_MAX_FRAME];
+ struct zoran_v4l_struct v4l_buffers; /* V4L buffers' info */
+
+ /* Buz MJPEG parameters */
+ enum zoran_codec_mode codec_mode; /* status of codec */
+ struct zoran_jpg_settings jpg_settings; /* structure with a lot of things to play with */
+
+ wait_queue_head_t jpg_capq; /* wait here for grab to finish */
+
+ /* grab queue counts/indices, mask with BUZ_MASK_STAT_COM before using as index */
+ /* (dma_head - dma_tail) is number active in DMA, must be <= BUZ_NUM_STAT_COM */
+ /* (value & BUZ_MASK_STAT_COM) corresponds to index in stat_com table */
+ unsigned long jpg_que_head; /* Index where to put next buffer which is queued */
+ unsigned long jpg_dma_head; /* Index of next buffer which goes into stat_com */
+ unsigned long jpg_dma_tail; /* Index of last buffer in stat_com */
+ unsigned long jpg_que_tail; /* Index of last buffer in queue */
+ unsigned long jpg_seq_num; /* count of frames since grab/play started */
+ unsigned long jpg_err_seq; /* last seq_num before error */
+ unsigned long jpg_err_shift;
+ unsigned long jpg_queued_num; /* count of frames queued since grab/play started */
+
+ /* zr36057's code buffer table */
+ u32 *stat_com; /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */
+
+ /* (value & BUZ_MASK_FRAME) corresponds to index in pend[] queue */
+ int jpg_pend[BUZ_MAX_FRAME];
+
+ /* array indexed by frame number */
+ struct zoran_jpg_struct jpg_buffers; /* MJPEG buffers' info */
+
+ /* Additional stuff for testing */
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *zoran_proc;
+#else
+ void *zoran_proc;
+#endif
+ int testing;
+ int jpeg_error;
+ int intr_counter_GIRQ1;
+ int intr_counter_GIRQ0;
+ int intr_counter_CodRepIRQ;
+ int intr_counter_JPEGRepIRQ;
+ int field_counter;
+ int IRQ1_in;
+ int IRQ1_out;
+ int JPEG_in;
+ int JPEG_out;
+ int JPEG_0;
+ int JPEG_1;
+ int END_event_missed;
+ int JPEG_missed;
+ int JPEG_error;
+ int num_errors;
+ int JPEG_max_missed;
+ int JPEG_min_missed;
+
+ u32 last_isr;
+ unsigned long frame_num;
+
+ wait_queue_head_t test_q;
+};
+
+/*The following should be done in more portable way. It depends on define
+ of _ALPHA_BUZ in the Makefile.*/
+
+#ifdef _ALPHA_BUZ
+#define btwrite(dat,adr) writel((dat), zr->zr36057_adr+(adr))
+#define btread(adr) readl(zr->zr36057_adr+(adr))
+#else
+#define btwrite(dat,adr) writel((dat), zr->zr36057_mem+(adr))
+#define btread(adr) readl(zr->zr36057_mem+(adr))
+#endif
+
+#define btand(dat,adr) btwrite((dat) & btread(adr), adr)
+#define btor(dat,adr) btwrite((dat) | btread(adr), adr)
+#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
+
+#endif /* __kernel__ */
+
+#endif
diff --git a/linux/drivers/media/video/zoran_card.c b/linux/drivers/media/video/zoran_card.c
new file mode 100644
index 000000000..54c8b2a2c
--- /dev/null
+++ b/linux/drivers/media/video/zoran_card.c
@@ -0,0 +1,1578 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * This part handles card-specific data and detection
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Currently maintained by:
+ * Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * Laurent Pinchart <laurent.pinchart@skynet.be>
+ * Mailinglist <mjpeg-users@lists.sf.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+
+#include <linux/proc_fs.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include "compat.h"
+#include <linux/videodev.h>
+#include <linux/spinlock.h>
+#include <linux/sem.h>
+#include <linux/kmod.h>
+#include <linux/wait.h>
+
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/video_decoder.h>
+#include <linux/video_encoder.h>
+#include <linux/mutex.h>
+
+#include <asm/io.h>
+
+#include "videocodec.h"
+#include "zoran.h"
+#include "zoran_card.h"
+#include "zoran_device.h"
+#include "zoran_procfs.h"
+
+#define I2C_NAME(x) (x)->name
+
+extern const struct zoran_format zoran_formats[];
+
+static int card[BUZ_MAX] = { -1, -1, -1, -1 };
+module_param_array(card, int, NULL, 0);
+MODULE_PARM_DESC(card, "The type of card");
+
+static int encoder[BUZ_MAX] = { -1, -1, -1, -1 };
+module_param_array(encoder, int, NULL, 0);
+MODULE_PARM_DESC(encoder, "i2c TV encoder");
+
+static int decoder[BUZ_MAX] = { -1, -1, -1, -1 };
+module_param_array(decoder, int, NULL, 0);
+MODULE_PARM_DESC(decoder, "i2c TV decoder");
+
+/*
+ The video mem address of the video card.
+ The driver has a little database for some videocards
+ to determine it from there. If your video card is not in there
+ you have either to give it to the driver as a parameter
+ or set in in a VIDIOCSFBUF ioctl
+ */
+
+static unsigned long vidmem = 0; /* Video memory base address */
+module_param(vidmem, ulong, 0);
+
+/*
+ Default input and video norm at startup of the driver.
+*/
+
+static int default_input = 0; /* 0=Composite, 1=S-Video */
+module_param(default_input, int, 0);
+MODULE_PARM_DESC(default_input,
+ "Default input (0=Composite, 1=S-Video, 2=Internal)");
+
+static int default_norm = 0; /* 0=PAL, 1=NTSC 2=SECAM */
+module_param(default_norm, int, 0);
+MODULE_PARM_DESC(default_norm, "Default norm (0=PAL, 1=NTSC, 2=SECAM)");
+
+static int video_nr = -1; /* /dev/videoN, -1 for autodetect */
+module_param(video_nr, int, 0);
+MODULE_PARM_DESC(video_nr, "video device number");
+
+/*
+ Number and size of grab buffers for Video 4 Linux
+ The vast majority of applications should not need more than 2,
+ the very popular BTTV driver actually does ONLY have 2.
+ Time sensitive applications might need more, the maximum
+ is VIDEO_MAX_FRAME (defined in <linux/videodev.h>).
+
+ The size is set so that the maximum possible request
+ can be satisfied. Decrease it, if bigphys_area alloc'd
+ memory is low. If you don't have the bigphys_area patch,
+ set it to 128 KB. Will you allow only to grab small
+ images with V4L, but that's better than nothing.
+
+ v4l_bufsize has to be given in KB !
+
+*/
+
+int v4l_nbufs = 2;
+int v4l_bufsize = 128; /* Everybody should be able to work with this setting */
+module_param(v4l_nbufs, int, 0);
+MODULE_PARM_DESC(v4l_nbufs, "Maximum number of V4L buffers to use");
+module_param(v4l_bufsize, int, 0);
+MODULE_PARM_DESC(v4l_bufsize, "Maximum size per V4L buffer (in kB)");
+
+int jpg_nbufs = 32;
+int jpg_bufsize = 512; /* max size for 100% quality full-PAL frame */
+module_param(jpg_nbufs, int, 0);
+MODULE_PARM_DESC(jpg_nbufs, "Maximum number of JPG buffers to use");
+module_param(jpg_bufsize, int, 0);
+MODULE_PARM_DESC(jpg_bufsize, "Maximum size per JPG buffer (in kB)");
+
+int pass_through = 0; /* 1=Pass through TV signal when device is not used */
+ /* 0=Show color bar when device is not used (LML33: only if lml33dpath=1) */
+module_param(pass_through, int, 0);
+MODULE_PARM_DESC(pass_through,
+ "Pass TV signal through to TV-out when idling");
+
+static int debug = 1;
+int *zr_debug = &debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-4)");
+
+MODULE_DESCRIPTION("Zoran-36057/36067 JPEG codec driver");
+MODULE_AUTHOR("Serguei Miridonov");
+MODULE_LICENSE("GPL");
+
+static struct pci_device_id zr36067_pci_tbl[] = {
+ {PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36057,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0}
+};
+MODULE_DEVICE_TABLE(pci, zr36067_pci_tbl);
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (*zr_debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+int zoran_num; /* number of Buzs in use */
+struct zoran zoran[BUZ_MAX];
+
+/* videocodec bus functions ZR36060 */
+static u32
+zr36060_read (struct videocodec *codec,
+ u16 reg)
+{
+ struct zoran *zr = (struct zoran *) codec->master_data->data;
+ __u32 data;
+
+ if (post_office_wait(zr)
+ || post_office_write(zr, 0, 1, reg >> 8)
+ || post_office_write(zr, 0, 2, reg & 0xff)) {
+ return -1;
+ }
+
+ data = post_office_read(zr, 0, 3) & 0xff;
+ return data;
+}
+
+static void
+zr36060_write (struct videocodec *codec,
+ u16 reg,
+ u32 val)
+{
+ struct zoran *zr = (struct zoran *) codec->master_data->data;
+
+ if (post_office_wait(zr)
+ || post_office_write(zr, 0, 1, reg >> 8)
+ || post_office_write(zr, 0, 2, reg & 0xff)) {
+ return;
+ }
+
+ post_office_write(zr, 0, 3, val & 0xff);
+}
+
+/* videocodec bus functions ZR36050 */
+static u32
+zr36050_read (struct videocodec *codec,
+ u16 reg)
+{
+ struct zoran *zr = (struct zoran *) codec->master_data->data;
+ __u32 data;
+
+ if (post_office_wait(zr)
+ || post_office_write(zr, 1, 0, reg >> 2)) { // reg. HIGHBYTES
+ return -1;
+ }
+
+ data = post_office_read(zr, 0, reg & 0x03) & 0xff; // reg. LOWBYTES + read
+ return data;
+}
+
+static void
+zr36050_write (struct videocodec *codec,
+ u16 reg,
+ u32 val)
+{
+ struct zoran *zr = (struct zoran *) codec->master_data->data;
+
+ if (post_office_wait(zr)
+ || post_office_write(zr, 1, 0, reg >> 2)) { // reg. HIGHBYTES
+ return;
+ }
+
+ post_office_write(zr, 0, reg & 0x03, val & 0xff); // reg. LOWBYTES + wr. data
+}
+
+/* videocodec bus functions ZR36016 */
+static u32
+zr36016_read (struct videocodec *codec,
+ u16 reg)
+{
+ struct zoran *zr = (struct zoran *) codec->master_data->data;
+ __u32 data;
+
+ if (post_office_wait(zr)) {
+ return -1;
+ }
+
+ data = post_office_read(zr, 2, reg & 0x03) & 0xff; // read
+ return data;
+}
+
+/* hack for in zoran_device.c */
+void
+zr36016_write (struct videocodec *codec,
+ u16 reg,
+ u32 val)
+{
+ struct zoran *zr = (struct zoran *) codec->master_data->data;
+
+ if (post_office_wait(zr)) {
+ return;
+ }
+
+ post_office_write(zr, 2, reg & 0x03, val & 0x0ff); // wr. data
+}
+
+/*
+ * Board specific information
+ */
+
+static void
+dc10_init (struct zoran *zr)
+{
+ dprintk(3, KERN_DEBUG "%s: dc10_init()\n", ZR_DEVNAME(zr));
+
+ /* Pixel clock selection */
+ GPIO(zr, 4, 0);
+ GPIO(zr, 5, 1);
+ /* Enable the video bus sync signals */
+ GPIO(zr, 7, 0);
+}
+
+static void
+dc10plus_init (struct zoran *zr)
+{
+ dprintk(3, KERN_DEBUG "%s: dc10plus_init()\n", ZR_DEVNAME(zr));
+}
+
+static void
+buz_init (struct zoran *zr)
+{
+ dprintk(3, KERN_DEBUG "%s: buz_init()\n", ZR_DEVNAME(zr));
+
+ /* some stuff from Iomega */
+ pci_write_config_dword(zr->pci_dev, 0xfc, 0x90680f15);
+ pci_write_config_dword(zr->pci_dev, 0x0c, 0x00012020);
+ pci_write_config_dword(zr->pci_dev, 0xe8, 0xc0200000);
+}
+
+static void
+lml33_init (struct zoran *zr)
+{
+ dprintk(3, KERN_DEBUG "%s: lml33_init()\n", ZR_DEVNAME(zr));
+
+ GPIO(zr, 2, 1); // Set Composite input/output
+}
+
+static char *
+i2cid_to_modulename (u16 i2c_id)
+{
+ char *name = NULL;
+
+ switch (i2c_id) {
+ case I2C_DRIVERID_SAA7110:
+ name = "saa7110";
+ break;
+ case I2C_DRIVERID_SAA7111A:
+ name = "saa7111";
+ break;
+ case I2C_DRIVERID_SAA7114:
+ name = "saa7114";
+ break;
+ case I2C_DRIVERID_SAA7185B:
+ name = "saa7185";
+ break;
+ case I2C_DRIVERID_ADV7170:
+ name = "adv7170";
+ break;
+ case I2C_DRIVERID_ADV7175:
+ name = "adv7175";
+ break;
+ case I2C_DRIVERID_BT819:
+ name = "bt819";
+ break;
+ case I2C_DRIVERID_BT856:
+ name = "bt856";
+ break;
+ case I2C_DRIVERID_VPX3220:
+ name = "vpx3220";
+ break;
+/* case I2C_DRIVERID_VPX3224:
+ name = "vpx3224";
+ break;
+ case I2C_DRIVERID_MSE3000:
+ name = "mse3000";
+ break;*/
+ default:
+ break;
+ }
+
+ return name;
+}
+
+static char *
+codecid_to_modulename (u16 codecid)
+{
+ char *name = NULL;
+
+ switch (codecid) {
+ case CODEC_TYPE_ZR36060:
+ name = "zr36060";
+ break;
+ case CODEC_TYPE_ZR36050:
+ name = "zr36050";
+ break;
+ case CODEC_TYPE_ZR36016:
+ name = "zr36016";
+ break;
+ default:
+ break;
+ }
+
+ return name;
+}
+
+// struct tvnorm {
+// u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart;
+// };
+
+static struct tvnorm f50sqpixel = { 944, 768, 83, 880, 625, 576, 16 };
+static struct tvnorm f60sqpixel = { 780, 640, 51, 716, 525, 480, 12 };
+static struct tvnorm f50ccir601 = { 864, 720, 75, 804, 625, 576, 18 };
+static struct tvnorm f60ccir601 = { 858, 720, 57, 788, 525, 480, 16 };
+
+static struct tvnorm f50ccir601_lml33 = { 864, 720, 75+34, 804, 625, 576, 18 };
+static struct tvnorm f60ccir601_lml33 = { 858, 720, 57+34, 788, 525, 480, 16 };
+
+/* The DC10 (57/16/50) uses VActive as HSync, so HStart must be 0 */
+static struct tvnorm f50sqpixel_dc10 = { 944, 768, 0, 880, 625, 576, 0 };
+static struct tvnorm f60sqpixel_dc10 = { 780, 640, 0, 716, 525, 480, 12 };
+
+/* FIXME: I cannot swap U and V in saa7114, so i do one
+ * pixel left shift in zoran (75 -> 74)
+ * (Maxim Yevtyushkin <max@linuxmedialabs.com>) */
+static struct tvnorm f50ccir601_lm33r10 = { 864, 720, 74+54, 804, 625, 576, 18 };
+static struct tvnorm f60ccir601_lm33r10 = { 858, 720, 56+54, 788, 525, 480, 16 };
+
+static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+ {
+ .type = DC10_old,
+ .name = "DC10(old)",
+ .i2c_decoder = I2C_DRIVERID_VPX3220,
+ /*.i2c_encoder = I2C_DRIVERID_MSE3000,*/
+ .video_codec = CODEC_TYPE_ZR36050,
+ .video_vfe = CODEC_TYPE_ZR36016,
+
+ .inputs = 3,
+ .input = {
+ { 1, "Composite" },
+ { 2, "S-Video" },
+ { 0, "Internal/comp" }
+ },
+ .norms = 3,
+ .tvn = {
+ &f50sqpixel_dc10,
+ &f60sqpixel_dc10,
+ &f50sqpixel_dc10
+ },
+ .jpeg_int = 0,
+ .vsync_int = ZR36057_ISR_GIRQ1,
+ .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 },
+ .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 },
+ .gpcs = { -1, 0 },
+ .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+ .gws_not_connected = 0,
+ .init = &dc10_init,
+ }, {
+ .type = DC10_new,
+ .name = "DC10(new)",
+ .i2c_decoder = I2C_DRIVERID_SAA7110,
+ .i2c_encoder = I2C_DRIVERID_ADV7175,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 3,
+ .input = {
+ { 0, "Composite" },
+ { 7, "S-Video" },
+ { 5, "Internal/comp" }
+ },
+ .norms = 3,
+ .tvn = {
+ &f50sqpixel,
+ &f60sqpixel,
+ &f50sqpixel},
+ .jpeg_int = ZR36057_ISR_GIRQ0,
+ .vsync_int = ZR36057_ISR_GIRQ1,
+ .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 },
+ .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+ .gpcs = { -1, 1},
+ .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 },
+ .gws_not_connected = 0,
+ .init = &dc10plus_init,
+ }, {
+ .type = DC10plus,
+ .name = "DC10plus",
+ .vendor_id = PCI_VENDOR_ID_MIRO,
+ .device_id = PCI_DEVICE_ID_MIRO_DC10PLUS,
+ .i2c_decoder = I2C_DRIVERID_SAA7110,
+ .i2c_encoder = I2C_DRIVERID_ADV7175,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 3,
+ .input = {
+ { 0, "Composite" },
+ { 7, "S-Video" },
+ { 5, "Internal/comp" }
+ },
+ .norms = 3,
+ .tvn = {
+ &f50sqpixel,
+ &f60sqpixel,
+ &f50sqpixel
+ },
+ .jpeg_int = ZR36057_ISR_GIRQ0,
+ .vsync_int = ZR36057_ISR_GIRQ1,
+ .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 },
+ .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+ .gpcs = { -1, 1 },
+ .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 },
+ .gws_not_connected = 0,
+ .init = &dc10plus_init,
+ }, {
+ .type = DC30,
+ .name = "DC30",
+ .i2c_decoder = I2C_DRIVERID_VPX3220,
+ .i2c_encoder = I2C_DRIVERID_ADV7175,
+ .video_codec = CODEC_TYPE_ZR36050,
+ .video_vfe = CODEC_TYPE_ZR36016,
+
+ .inputs = 3,
+ .input = {
+ { 1, "Composite" },
+ { 2, "S-Video" },
+ { 0, "Internal/comp" }
+ },
+ .norms = 3,
+ .tvn = {
+ &f50sqpixel_dc10,
+ &f60sqpixel_dc10,
+ &f50sqpixel_dc10
+ },
+ .jpeg_int = 0,
+ .vsync_int = ZR36057_ISR_GIRQ1,
+ .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 },
+ .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 },
+ .gpcs = { -1, 0 },
+ .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+ .gws_not_connected = 0,
+ .init = &dc10_init,
+ }, {
+ .type = DC30plus,
+ .name = "DC30plus",
+ .vendor_id = PCI_VENDOR_ID_MIRO,
+ .device_id = PCI_DEVICE_ID_MIRO_DC30PLUS,
+ .i2c_decoder = I2C_DRIVERID_VPX3220,
+ .i2c_encoder = I2C_DRIVERID_ADV7175,
+ .video_codec = CODEC_TYPE_ZR36050,
+ .video_vfe = CODEC_TYPE_ZR36016,
+
+ .inputs = 3,
+ .input = {
+ { 1, "Composite" },
+ { 2, "S-Video" },
+ { 0, "Internal/comp" }
+ },
+ .norms = 3,
+ .tvn = {
+ &f50sqpixel_dc10,
+ &f60sqpixel_dc10,
+ &f50sqpixel_dc10
+ },
+ .jpeg_int = 0,
+ .vsync_int = ZR36057_ISR_GIRQ1,
+ .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 },
+ .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 },
+ .gpcs = { -1, 0 },
+ .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+ .gws_not_connected = 0,
+ .init = &dc10_init,
+ }, {
+ .type = LML33,
+ .name = "LML33",
+ .i2c_decoder = I2C_DRIVERID_BT819,
+ .i2c_encoder = I2C_DRIVERID_BT856,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 2,
+ .input = {
+ { 0, "Composite" },
+ { 7, "S-Video" }
+ },
+ .norms = 2,
+ .tvn = {
+ &f50ccir601_lml33,
+ &f60ccir601_lml33,
+ NULL
+ },
+ .jpeg_int = ZR36057_ISR_GIRQ1,
+ .vsync_int = ZR36057_ISR_GIRQ0,
+ .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 },
+ .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 },
+ .gpcs = { 3, 1 },
+ .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 },
+ .gws_not_connected = 1,
+ .init = &lml33_init,
+ }, {
+ .type = LML33R10,
+ .name = "LML33R10",
+ .vendor_id = PCI_VENDOR_ID_ELECTRONICDESIGNGMBH,
+ .device_id = PCI_DEVICE_ID_LML_33R10,
+ .i2c_decoder = I2C_DRIVERID_SAA7114,
+ .i2c_encoder = I2C_DRIVERID_ADV7170,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 2,
+ .input = {
+ { 0, "Composite" },
+ { 7, "S-Video" }
+ },
+ .norms = 2,
+ .tvn = {
+ &f50ccir601_lm33r10,
+ &f60ccir601_lm33r10,
+ NULL
+ },
+ .jpeg_int = ZR36057_ISR_GIRQ1,
+ .vsync_int = ZR36057_ISR_GIRQ0,
+ .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 },
+ .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 },
+ .gpcs = { 3, 1 },
+ .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 },
+ .gws_not_connected = 1,
+ .init = &lml33_init,
+ }, {
+ .type = BUZ,
+ .name = "Buz",
+ .vendor_id = PCI_VENDOR_ID_IOMEGA,
+ .device_id = PCI_DEVICE_ID_IOMEGA_BUZ,
+ .i2c_decoder = I2C_DRIVERID_SAA7111A,
+ .i2c_encoder = I2C_DRIVERID_SAA7185B,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 2,
+ .input = {
+ { 3, "Composite" },
+ { 7, "S-Video" }
+ },
+ .norms = 3,
+ .tvn = {
+ &f50ccir601,
+ &f60ccir601,
+ &f50ccir601
+ },
+ .jpeg_int = ZR36057_ISR_GIRQ1,
+ .vsync_int = ZR36057_ISR_GIRQ0,
+ .gpio = { 1, -1, 3, -1, -1, -1, -1, -1 },
+ .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+ .gpcs = { 3, 1 },
+ .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 },
+ .gws_not_connected = 1,
+ .init = &buz_init,
+ }
+};
+
+/*
+ * I2C functions
+ */
+/* software I2C functions */
+static int
+zoran_i2c_getsda (void *data)
+{
+ struct zoran *zr = (struct zoran *) data;
+
+ return (btread(ZR36057_I2CBR) >> 1) & 1;
+}
+
+static int
+zoran_i2c_getscl (void *data)
+{
+ struct zoran *zr = (struct zoran *) data;
+
+ return btread(ZR36057_I2CBR) & 1;
+}
+
+static void
+zoran_i2c_setsda (void *data,
+ int state)
+{
+ struct zoran *zr = (struct zoran *) data;
+
+ if (state)
+ zr->i2cbr |= 2;
+ else
+ zr->i2cbr &= ~2;
+ btwrite(zr->i2cbr, ZR36057_I2CBR);
+}
+
+static void
+zoran_i2c_setscl (void *data,
+ int state)
+{
+ struct zoran *zr = (struct zoran *) data;
+
+ if (state)
+ zr->i2cbr |= 1;
+ else
+ zr->i2cbr &= ~1;
+ btwrite(zr->i2cbr, ZR36057_I2CBR);
+}
+
+static int
+zoran_i2c_client_register (struct i2c_client *client)
+{
+ struct zoran *zr = (struct zoran *) i2c_get_adapdata(client->adapter);
+ int res = 0;
+
+ dprintk(2,
+ KERN_DEBUG "%s: i2c_client_register() - driver id = %d\n",
+ ZR_DEVNAME(zr), client->driver->id);
+
+ mutex_lock(&zr->resource_lock);
+
+ if (zr->user > 0) {
+ /* we're already busy, so we keep a reference to
+ * them... Could do a lot of stuff here, but this
+ * is easiest. (Did I ever mention I'm a lazy ass?)
+ */
+ res = -EBUSY;
+ goto clientreg_unlock_and_return;
+ }
+
+ if (client->driver->id == zr->card.i2c_decoder)
+ zr->decoder = client;
+ else if (client->driver->id == zr->card.i2c_encoder)
+ zr->encoder = client;
+ else {
+ res = -ENODEV;
+ goto clientreg_unlock_and_return;
+ }
+
+clientreg_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static int
+zoran_i2c_client_unregister (struct i2c_client *client)
+{
+ struct zoran *zr = (struct zoran *) i2c_get_adapdata(client->adapter);
+ int res = 0;
+
+ dprintk(2, KERN_DEBUG "%s: i2c_client_unregister()\n", ZR_DEVNAME(zr));
+
+ mutex_lock(&zr->resource_lock);
+
+ if (zr->user > 0) {
+ res = -EBUSY;
+ goto clientunreg_unlock_and_return;
+ }
+
+ /* try to locate it */
+ if (client == zr->encoder) {
+ zr->encoder = NULL;
+ } else if (client == zr->decoder) {
+ zr->decoder = NULL;
+ snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%d]", zr->id);
+ }
+clientunreg_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ return res;
+}
+
+static struct i2c_algo_bit_data zoran_i2c_bit_data_template = {
+ .setsda = zoran_i2c_setsda,
+ .setscl = zoran_i2c_setscl,
+ .getsda = zoran_i2c_getsda,
+ .getscl = zoran_i2c_getscl,
+ .udelay = 10,
+ .mdelay = 0,
+ .timeout = 100,
+};
+
+static struct i2c_adapter zoran_i2c_adapter_template = {
+ .name = "zr36057",
+ .id = I2C_HW_B_ZR36067,
+ .algo = NULL,
+ .client_register = zoran_i2c_client_register,
+ .client_unregister = zoran_i2c_client_unregister,
+};
+
+static int
+zoran_register_i2c (struct zoran *zr)
+{
+ memcpy(&zr->i2c_algo, &zoran_i2c_bit_data_template,
+ sizeof(struct i2c_algo_bit_data));
+ zr->i2c_algo.data = zr;
+ memcpy(&zr->i2c_adapter, &zoran_i2c_adapter_template,
+ sizeof(struct i2c_adapter));
+ strncpy(I2C_NAME(&zr->i2c_adapter), ZR_DEVNAME(zr),
+ sizeof(I2C_NAME(&zr->i2c_adapter)) - 1);
+ i2c_set_adapdata(&zr->i2c_adapter, zr);
+ zr->i2c_adapter.algo_data = &zr->i2c_algo;
+ return i2c_bit_add_bus(&zr->i2c_adapter);
+}
+
+static void
+zoran_unregister_i2c (struct zoran *zr)
+{
+ i2c_bit_del_bus((&zr->i2c_adapter));
+}
+
+/* Check a zoran_params struct for correctness, insert default params */
+
+int
+zoran_check_jpg_settings (struct zoran *zr,
+ struct zoran_jpg_settings *settings)
+{
+ int err = 0, err0 = 0;
+
+ dprintk(4,
+ KERN_DEBUG
+ "%s: check_jpg_settings() - dec: %d, Hdcm: %d, Vdcm: %d, Tdcm: %d\n",
+ ZR_DEVNAME(zr), settings->decimation, settings->HorDcm,
+ settings->VerDcm, settings->TmpDcm);
+ dprintk(4,
+ KERN_DEBUG
+ "%s: check_jpg_settings() - x: %d, y: %d, w: %d, y: %d\n",
+ ZR_DEVNAME(zr), settings->img_x, settings->img_y,
+ settings->img_width, settings->img_height);
+ /* Check decimation, set default values for decimation = 1, 2, 4 */
+ switch (settings->decimation) {
+ case 1:
+
+ settings->HorDcm = 1;
+ settings->VerDcm = 1;
+ settings->TmpDcm = 1;
+ settings->field_per_buff = 2;
+ settings->img_x = 0;
+ settings->img_y = 0;
+ settings->img_width = BUZ_MAX_WIDTH;
+ settings->img_height = BUZ_MAX_HEIGHT / 2;
+ break;
+ case 2:
+
+ settings->HorDcm = 2;
+ settings->VerDcm = 1;
+ settings->TmpDcm = 2;
+ settings->field_per_buff = 1;
+ settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
+ settings->img_y = 0;
+ settings->img_width =
+ (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
+ settings->img_height = BUZ_MAX_HEIGHT / 2;
+ break;
+ case 4:
+
+ if (zr->card.type == DC10_new) {
+ dprintk(1,
+ KERN_DEBUG
+ "%s: check_jpg_settings() - HDec by 4 is not supported on the DC10\n",
+ ZR_DEVNAME(zr));
+ err0++;
+ break;
+ }
+
+ settings->HorDcm = 4;
+ settings->VerDcm = 2;
+ settings->TmpDcm = 2;
+ settings->field_per_buff = 1;
+ settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
+ settings->img_y = 0;
+ settings->img_width =
+ (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
+ settings->img_height = BUZ_MAX_HEIGHT / 2;
+ break;
+ case 0:
+
+ /* We have to check the data the user has set */
+
+ if (settings->HorDcm != 1 && settings->HorDcm != 2 &&
+ (zr->card.type == DC10_new || settings->HorDcm != 4))
+ err0++;
+ if (settings->VerDcm != 1 && settings->VerDcm != 2)
+ err0++;
+ if (settings->TmpDcm != 1 && settings->TmpDcm != 2)
+ err0++;
+ if (settings->field_per_buff != 1 &&
+ settings->field_per_buff != 2)
+ err0++;
+ if (settings->img_x < 0)
+ err0++;
+ if (settings->img_y < 0)
+ err0++;
+ if (settings->img_width < 0)
+ err0++;
+ if (settings->img_height < 0)
+ err0++;
+ if (settings->img_x + settings->img_width > BUZ_MAX_WIDTH)
+ err0++;
+ if (settings->img_y + settings->img_height >
+ BUZ_MAX_HEIGHT / 2)
+ err0++;
+ if (settings->HorDcm && settings->VerDcm) {
+ if (settings->img_width %
+ (16 * settings->HorDcm) != 0)
+ err0++;
+ if (settings->img_height %
+ (8 * settings->VerDcm) != 0)
+ err0++;
+ }
+
+ if (err0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: check_jpg_settings() - error in params for decimation = 0\n",
+ ZR_DEVNAME(zr));
+ err++;
+ }
+ break;
+ default:
+ dprintk(1,
+ KERN_ERR
+ "%s: check_jpg_settings() - decimation = %d, must be 0, 1, 2 or 4\n",
+ ZR_DEVNAME(zr), settings->decimation);
+ err++;
+ break;
+ }
+
+ if (settings->jpg_comp.quality > 100)
+ settings->jpg_comp.quality = 100;
+ if (settings->jpg_comp.quality < 5)
+ settings->jpg_comp.quality = 5;
+ if (settings->jpg_comp.APPn < 0)
+ settings->jpg_comp.APPn = 0;
+ if (settings->jpg_comp.APPn > 15)
+ settings->jpg_comp.APPn = 15;
+ if (settings->jpg_comp.APP_len < 0)
+ settings->jpg_comp.APP_len = 0;
+ if (settings->jpg_comp.APP_len > 60)
+ settings->jpg_comp.APP_len = 60;
+ if (settings->jpg_comp.COM_len < 0)
+ settings->jpg_comp.COM_len = 0;
+ if (settings->jpg_comp.COM_len > 60)
+ settings->jpg_comp.COM_len = 60;
+ if (err)
+ return -EINVAL;
+ return 0;
+}
+
+void
+zoran_open_init_params (struct zoran *zr)
+{
+ int i;
+
+ /* User must explicitly set a window */
+ zr->overlay_settings.is_set = 0;
+ zr->overlay_mask = NULL;
+ zr->overlay_active = ZORAN_FREE;
+
+ zr->v4l_memgrab_active = 0;
+ zr->v4l_overlay_active = 0;
+ zr->v4l_grab_frame = NO_GRAB_ACTIVE;
+ zr->v4l_grab_seq = 0;
+ zr->v4l_settings.width = 192;
+ zr->v4l_settings.height = 144;
+ zr->v4l_settings.format = &zoran_formats[4]; /* YUY2 - YUV-4:2:2 packed */
+ zr->v4l_settings.bytesperline =
+ zr->v4l_settings.width *
+ ((zr->v4l_settings.format->depth + 7) / 8);
+
+ /* DMA ring stuff for V4L */
+ zr->v4l_pend_tail = 0;
+ zr->v4l_pend_head = 0;
+ zr->v4l_sync_tail = 0;
+ zr->v4l_buffers.active = ZORAN_FREE;
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ zr->v4l_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
+ }
+ zr->v4l_buffers.allocated = 0;
+
+ for (i = 0; i < BUZ_MAX_FRAME; i++) {
+ zr->jpg_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
+ }
+ zr->jpg_buffers.active = ZORAN_FREE;
+ zr->jpg_buffers.allocated = 0;
+ /* Set necessary params and call zoran_check_jpg_settings to set the defaults */
+ zr->jpg_settings.decimation = 1;
+ zr->jpg_settings.jpg_comp.quality = 50; /* default compression factor 8 */
+ if (zr->card.type != BUZ)
+ zr->jpg_settings.odd_even = 1;
+ else
+ zr->jpg_settings.odd_even = 0;
+ zr->jpg_settings.jpg_comp.APPn = 0;
+ zr->jpg_settings.jpg_comp.APP_len = 0; /* No APPn marker */
+ memset(zr->jpg_settings.jpg_comp.APP_data, 0,
+ sizeof(zr->jpg_settings.jpg_comp.APP_data));
+ zr->jpg_settings.jpg_comp.COM_len = 0; /* No COM marker */
+ memset(zr->jpg_settings.jpg_comp.COM_data, 0,
+ sizeof(zr->jpg_settings.jpg_comp.COM_data));
+ zr->jpg_settings.jpg_comp.jpeg_markers =
+ JPEG_MARKER_DHT | JPEG_MARKER_DQT;
+ i = zoran_check_jpg_settings(zr, &zr->jpg_settings);
+ if (i)
+ dprintk(1,
+ KERN_ERR
+ "%s: zoran_open_init_params() internal error\n",
+ ZR_DEVNAME(zr));
+
+ clear_interrupt_counters(zr);
+ zr->testing = 0;
+}
+
+static void __devinit
+test_interrupts (struct zoran *zr)
+{
+ DEFINE_WAIT(wait);
+ int timeout, icr;
+
+ clear_interrupt_counters(zr);
+
+ zr->testing = 1;
+ icr = btread(ZR36057_ICR);
+ btwrite(0x78000000 | ZR36057_ICR_IntPinEn, ZR36057_ICR);
+ prepare_to_wait(&zr->test_q, &wait, TASK_INTERRUPTIBLE);
+ timeout = schedule_timeout(HZ);
+ finish_wait(&zr->test_q, &wait);
+ btwrite(0, ZR36057_ICR);
+ btwrite(0x78000000, ZR36057_ISR);
+ zr->testing = 0;
+ dprintk(5, KERN_INFO "%s: Testing interrupts...\n", ZR_DEVNAME(zr));
+ if (timeout) {
+ dprintk(1, ": time spent: %d\n", 1 * HZ - timeout);
+ }
+ if (*zr_debug > 1)
+ print_interrupts(zr);
+ btwrite(icr, ZR36057_ICR);
+}
+
+static int __devinit
+zr36057_init (struct zoran *zr)
+{
+ int j, err;
+ int two = 2;
+ int zero = 0;
+
+ dprintk(1,
+ KERN_INFO
+ "%s: zr36057_init() - initializing card[%d], zr=%p\n",
+ ZR_DEVNAME(zr), zr->id, zr);
+
+ /* default setup of all parameters which will persist between opens */
+ zr->user = 0;
+
+ init_waitqueue_head(&zr->v4l_capq);
+ init_waitqueue_head(&zr->jpg_capq);
+ init_waitqueue_head(&zr->test_q);
+ zr->jpg_buffers.allocated = 0;
+ zr->v4l_buffers.allocated = 0;
+
+ zr->buffer.base = (void *) vidmem;
+ zr->buffer.width = 0;
+ zr->buffer.height = 0;
+ zr->buffer.depth = 0;
+ zr->buffer.bytesperline = 0;
+
+ /* Avoid nonsense settings from user for default input/norm */
+ if (default_norm < VIDEO_MODE_PAL &&
+ default_norm > VIDEO_MODE_SECAM)
+ default_norm = VIDEO_MODE_PAL;
+ zr->norm = default_norm;
+ if (!(zr->timing = zr->card.tvn[zr->norm])) {
+ dprintk(1,
+ KERN_WARNING
+ "%s: zr36057_init() - default TV standard not supported by hardware. PAL will be used.\n",
+ ZR_DEVNAME(zr));
+ zr->norm = VIDEO_MODE_PAL;
+ zr->timing = zr->card.tvn[zr->norm];
+ }
+
+ zr->input = default_input = (default_input ? 1 : 0);
+
+ /* Should the following be reset at every open ? */
+ zr->hue = 32768;
+ zr->contrast = 32768;
+ zr->saturation = 32768;
+ zr->brightness = 32768;
+
+ /* default setup (will be repeated at every open) */
+ zoran_open_init_params(zr);
+
+ /* allocate memory *before* doing anything to the hardware
+ * in case allocation fails */
+ zr->stat_com = kzalloc(BUZ_NUM_STAT_COM * 4, GFP_KERNEL);
+ zr->video_dev = kmalloc(sizeof(struct video_device), GFP_KERNEL);
+ if (!zr->stat_com || !zr->video_dev) {
+ dprintk(1,
+ KERN_ERR
+ "%s: zr36057_init() - kmalloc (STAT_COM) failed\n",
+ ZR_DEVNAME(zr));
+ err = -ENOMEM;
+ goto exit_free;
+ }
+ for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
+ zr->stat_com[j] = 1; /* mark as unavailable to zr36057 */
+ }
+
+ /*
+ * Now add the template and register the device unit.
+ */
+ memcpy(zr->video_dev, &zoran_template, sizeof(zoran_template));
+ strcpy(zr->video_dev->name, ZR_DEVNAME(zr));
+ err = video_register_device(zr->video_dev, VFL_TYPE_GRABBER, video_nr);
+ if (err < 0)
+ goto exit_unregister;
+
+ zoran_init_hardware(zr);
+ if (*zr_debug > 2)
+ detect_guest_activity(zr);
+ test_interrupts(zr);
+ if (!pass_through) {
+ decoder_command(zr, DECODER_ENABLE_OUTPUT, &zero);
+ encoder_command(zr, ENCODER_SET_INPUT, &two);
+ }
+
+ zr->zoran_proc = NULL;
+ zr->initialized = 1;
+ return 0;
+
+exit_unregister:
+ zoran_unregister_i2c(zr);
+exit_free:
+ kfree(zr->stat_com);
+ kfree(zr->video_dev);
+ return err;
+}
+
+static void
+zoran_release (struct zoran *zr)
+{
+ if (!zr->initialized)
+ return;
+ /* unregister videocodec bus */
+ if (zr->codec) {
+ struct videocodec_master *master = zr->codec->master_data;
+
+ videocodec_detach(zr->codec);
+ kfree(master);
+ }
+ if (zr->vfe) {
+ struct videocodec_master *master = zr->vfe->master_data;
+
+ videocodec_detach(zr->vfe);
+ kfree(master);
+ }
+
+ /* unregister i2c bus */
+ zoran_unregister_i2c(zr);
+ /* disable PCI bus-mastering */
+ zoran_set_pci_master(zr, 0);
+ /* put chip into reset */
+ btwrite(0, ZR36057_SPGPPCR);
+ free_irq(zr->pci_dev->irq, zr);
+ /* unmap and free memory */
+ kfree(zr->stat_com);
+ zoran_proc_cleanup(zr);
+ iounmap(zr->zr36057_mem);
+ pci_disable_device(zr->pci_dev);
+ video_unregister_device(zr->video_dev);
+}
+
+void
+zoran_vdev_release (struct video_device *vdev)
+{
+ kfree(vdev);
+}
+
+static struct videocodec_master * __devinit
+zoran_setup_videocodec (struct zoran *zr,
+ int type)
+{
+ struct videocodec_master *m = NULL;
+
+ m = kmalloc(sizeof(struct videocodec_master), GFP_KERNEL);
+ if (!m) {
+ dprintk(1,
+ KERN_ERR
+ "%s: zoran_setup_videocodec() - no memory\n",
+ ZR_DEVNAME(zr));
+ return m;
+ }
+
+ m->magic = 0L; /* magic not used */
+ m->type = VID_HARDWARE_ZR36067;
+ m->flags = CODEC_FLAG_ENCODER | CODEC_FLAG_DECODER;
+ strncpy(m->name, ZR_DEVNAME(zr), sizeof(m->name));
+ m->data = zr;
+
+ switch (type)
+ {
+ case CODEC_TYPE_ZR36060:
+ m->readreg = zr36060_read;
+ m->writereg = zr36060_write;
+ m->flags |= CODEC_FLAG_JPEG | CODEC_FLAG_VFE;
+ break;
+ case CODEC_TYPE_ZR36050:
+ m->readreg = zr36050_read;
+ m->writereg = zr36050_write;
+ m->flags |= CODEC_FLAG_JPEG;
+ break;
+ case CODEC_TYPE_ZR36016:
+ m->readreg = zr36016_read;
+ m->writereg = zr36016_write;
+ m->flags |= CODEC_FLAG_VFE;
+ break;
+ }
+
+ return m;
+}
+
+/*
+ * Scan for a Buz card (actually for the PCI contoler ZR36057),
+ * request the irq and map the io memory
+ */
+static int __devinit
+find_zr36057 (void)
+{
+ unsigned char latency, need_latency;
+ struct zoran *zr;
+ struct pci_dev *dev = NULL;
+ int result;
+ struct videocodec_master *master_vfe = NULL;
+ struct videocodec_master *master_codec = NULL;
+ int card_num;
+ char *i2c_enc_name, *i2c_dec_name, *codec_name, *vfe_name;
+
+ zoran_num = 0;
+ while (zoran_num < BUZ_MAX &&
+ (dev =
+ pci_find_device(PCI_VENDOR_ID_ZORAN,
+ PCI_DEVICE_ID_ZORAN_36057, dev)) != NULL) {
+ card_num = card[zoran_num];
+ zr = &zoran[zoran_num];
+ memset(zr, 0, sizeof(struct zoran)); // Just in case if previous cycle failed
+ zr->pci_dev = dev;
+ //zr->zr36057_mem = NULL;
+ zr->id = zoran_num;
+ snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%u]", zr->id);
+ spin_lock_init(&zr->spinlock);
+ mutex_init(&zr->resource_lock);
+ if (pci_enable_device(dev))
+ continue;
+ zr->zr36057_adr = pci_resource_start(zr->pci_dev, 0);
+ pci_read_config_byte(zr->pci_dev, PCI_CLASS_REVISION,
+ &zr->revision);
+ if (zr->revision < 2) {
+ dprintk(1,
+ KERN_INFO
+ "%s: Zoran ZR36057 (rev %d) irq: %d, memory: 0x%08x.\n",
+ ZR_DEVNAME(zr), zr->revision, zr->pci_dev->irq,
+ zr->zr36057_adr);
+
+ if (card_num == -1) {
+ dprintk(1,
+ KERN_ERR
+ "%s: find_zr36057() - no card specified, please use the card=X insmod option\n",
+ ZR_DEVNAME(zr));
+ continue;
+ }
+ } else {
+ int i;
+ unsigned short ss_vendor, ss_device;
+
+ ss_vendor = zr->pci_dev->subsystem_vendor;
+ ss_device = zr->pci_dev->subsystem_device;
+ dprintk(1,
+ KERN_INFO
+ "%s: Zoran ZR36067 (rev %d) irq: %d, memory: 0x%08x\n",
+ ZR_DEVNAME(zr), zr->revision, zr->pci_dev->irq,
+ zr->zr36057_adr);
+ dprintk(1,
+ KERN_INFO
+ "%s: subsystem vendor=0x%04x id=0x%04x\n",
+ ZR_DEVNAME(zr), ss_vendor, ss_device);
+ if (card_num == -1) {
+ dprintk(3,
+ KERN_DEBUG
+ "%s: find_zr36057() - trying to autodetect card type\n",
+ ZR_DEVNAME(zr));
+ for (i=0;i<NUM_CARDS;i++) {
+ if (ss_vendor == zoran_cards[i].vendor_id &&
+ ss_device == zoran_cards[i].device_id) {
+ dprintk(3,
+ KERN_DEBUG
+ "%s: find_zr36057() - card %s detected\n",
+ ZR_DEVNAME(zr),
+ zoran_cards[i].name);
+ card_num = i;
+ break;
+ }
+ }
+ if (i == NUM_CARDS) {
+ dprintk(1,
+ KERN_ERR
+ "%s: find_zr36057() - unknown card\n",
+ ZR_DEVNAME(zr));
+ continue;
+ }
+ }
+ }
+
+ if (card_num < 0 || card_num >= NUM_CARDS) {
+ dprintk(2,
+ KERN_ERR
+ "%s: find_zr36057() - invalid cardnum %d\n",
+ ZR_DEVNAME(zr), card_num);
+ continue;
+ }
+
+ /* even though we make this a non pointer and thus
+ * theoretically allow for making changes to this struct
+ * on a per-individual card basis at runtime, this is
+ * strongly discouraged. This structure is intended to
+ * keep general card information, no settings or anything */
+ zr->card = zoran_cards[card_num];
+ snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)),
+ "%s[%u]", zr->card.name, zr->id);
+
+ zr->zr36057_mem = ioremap_nocache(zr->zr36057_adr, 0x1000);
+ if (!zr->zr36057_mem) {
+ dprintk(1,
+ KERN_ERR
+ "%s: find_zr36057() - ioremap failed\n",
+ ZR_DEVNAME(zr));
+ continue;
+ }
+
+ result = request_irq(zr->pci_dev->irq,
+ zoran_irq,
+ SA_SHIRQ | SA_INTERRUPT,
+ ZR_DEVNAME(zr),
+ (void *) zr);
+ if (result < 0) {
+ if (result == -EINVAL) {
+ dprintk(1,
+ KERN_ERR
+ "%s: find_zr36057() - bad irq number or handler\n",
+ ZR_DEVNAME(zr));
+ } else if (result == -EBUSY) {
+ dprintk(1,
+ KERN_ERR
+ "%s: find_zr36057() - IRQ %d busy, change your PnP config in BIOS\n",
+ ZR_DEVNAME(zr), zr->pci_dev->irq);
+ } else {
+ dprintk(1,
+ KERN_ERR
+ "%s: find_zr36057() - can't assign irq, error code %d\n",
+ ZR_DEVNAME(zr), result);
+ }
+ goto zr_unmap;
+ }
+
+ /* set PCI latency timer */
+ pci_read_config_byte(zr->pci_dev, PCI_LATENCY_TIMER,
+ &latency);
+ need_latency = zr->revision > 1 ? 32 : 48;
+ if (latency != need_latency) {
+ dprintk(2,
+ KERN_INFO
+ "%s: Changing PCI latency from %d to %d.\n",
+ ZR_DEVNAME(zr), latency, need_latency);
+ pci_write_config_byte(zr->pci_dev,
+ PCI_LATENCY_TIMER,
+ need_latency);
+ }
+
+ zr36057_restart(zr);
+ /* i2c */
+ dprintk(2, KERN_INFO "%s: Initializing i2c bus...\n",
+ ZR_DEVNAME(zr));
+
+ /* i2c decoder */
+ if (decoder[zr->id] != -1) {
+ i2c_dec_name = i2cid_to_modulename(decoder[zr->id]);
+ zr->card.i2c_decoder = decoder[zr->id];
+ } else if (zr->card.i2c_decoder != 0) {
+ i2c_dec_name =
+ i2cid_to_modulename(zr->card.i2c_decoder);
+ } else {
+ i2c_dec_name = NULL;
+ }
+
+ if (i2c_dec_name) {
+ if ((result = request_module(i2c_dec_name)) < 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: failed to load module %s: %d\n",
+ ZR_DEVNAME(zr), i2c_dec_name, result);
+ }
+ }
+
+ /* i2c encoder */
+ if (encoder[zr->id] != -1) {
+ i2c_enc_name = i2cid_to_modulename(encoder[zr->id]);
+ zr->card.i2c_encoder = encoder[zr->id];
+ } else if (zr->card.i2c_encoder != 0) {
+ i2c_enc_name =
+ i2cid_to_modulename(zr->card.i2c_encoder);
+ } else {
+ i2c_enc_name = NULL;
+ }
+
+ if (i2c_enc_name) {
+ if ((result = request_module(i2c_enc_name)) < 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: failed to load module %s: %d\n",
+ ZR_DEVNAME(zr), i2c_enc_name, result);
+ }
+ }
+
+ if (zoran_register_i2c(zr) < 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: find_zr36057() - can't initialize i2c bus\n",
+ ZR_DEVNAME(zr));
+ goto zr_free_irq;
+ }
+
+ dprintk(2,
+ KERN_INFO "%s: Initializing videocodec bus...\n",
+ ZR_DEVNAME(zr));
+
+ if (zr->card.video_codec != 0 &&
+ (codec_name =
+ codecid_to_modulename(zr->card.video_codec)) != NULL) {
+ if ((result = request_module(codec_name)) < 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: failed to load modules %s: %d\n",
+ ZR_DEVNAME(zr), codec_name, result);
+ }
+ }
+ if (zr->card.video_vfe != 0 &&
+ (vfe_name =
+ codecid_to_modulename(zr->card.video_vfe)) != NULL) {
+ if ((result = request_module(vfe_name)) < 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: failed to load modules %s: %d\n",
+ ZR_DEVNAME(zr), vfe_name, result);
+ }
+ }
+
+ /* reset JPEG codec */
+ jpeg_codec_sleep(zr, 1);
+ jpeg_codec_reset(zr);
+ /* video bus enabled */
+ /* display codec revision */
+ if (zr->card.video_codec != 0) {
+ master_codec = zoran_setup_videocodec(zr,
+ zr->card.video_codec);
+ if (!master_codec)
+ goto zr_unreg_i2c;
+ zr->codec = videocodec_attach(master_codec);
+ if (!zr->codec) {
+ dprintk(1,
+ KERN_ERR
+ "%s: find_zr36057() - no codec found\n",
+ ZR_DEVNAME(zr));
+ goto zr_free_codec;
+ }
+ if (zr->codec->type != zr->card.video_codec) {
+ dprintk(1,
+ KERN_ERR
+ "%s: find_zr36057() - wrong codec\n",
+ ZR_DEVNAME(zr));
+ goto zr_detach_codec;
+ }
+ }
+ if (zr->card.video_vfe != 0) {
+ master_vfe = zoran_setup_videocodec(zr,
+ zr->card.video_vfe);
+ if (!master_vfe)
+ goto zr_detach_codec;
+ zr->vfe = videocodec_attach(master_vfe);
+ if (!zr->vfe) {
+ dprintk(1,
+ KERN_ERR
+ "%s: find_zr36057() - no VFE found\n",
+ ZR_DEVNAME(zr));
+ goto zr_free_vfe;
+ }
+ if (zr->vfe->type != zr->card.video_vfe) {
+ dprintk(1,
+ KERN_ERR
+ "%s: find_zr36057() = wrong VFE\n",
+ ZR_DEVNAME(zr));
+ goto zr_detach_vfe;
+ }
+ }
+
+ zoran_num++;
+ continue;
+
+ // Init errors
+ zr_detach_vfe:
+ videocodec_detach(zr->vfe);
+ zr_free_vfe:
+ kfree(master_vfe);
+ zr_detach_codec:
+ videocodec_detach(zr->codec);
+ zr_free_codec:
+ kfree(master_codec);
+ zr_unreg_i2c:
+ zoran_unregister_i2c(zr);
+ zr_free_irq:
+ btwrite(0, ZR36057_SPGPPCR);
+ free_irq(zr->pci_dev->irq, zr);
+ zr_unmap:
+ iounmap(zr->zr36057_mem);
+ continue;
+ }
+ if (zoran_num == 0) {
+ dprintk(1, KERN_INFO "No known MJPEG cards found.\n");
+ }
+ return zoran_num;
+}
+
+static int __init
+init_dc10_cards (void)
+{
+ int i;
+
+ memset(zoran, 0, sizeof(zoran));
+ printk(KERN_INFO "Zoran MJPEG board driver version %d.%d.%d\n",
+ MAJOR_VERSION, MINOR_VERSION, RELEASE_VERSION);
+
+ /* Look for cards */
+ if (find_zr36057() < 0) {
+ return -EIO;
+ }
+ if (zoran_num == 0)
+ return -ENODEV;
+ dprintk(1, KERN_INFO "%s: %d card(s) found\n", ZORAN_NAME,
+ zoran_num);
+ /* check the parameters we have been given, adjust if necessary */
+ if (v4l_nbufs < 2)
+ v4l_nbufs = 2;
+ if (v4l_nbufs > VIDEO_MAX_FRAME)
+ v4l_nbufs = VIDEO_MAX_FRAME;
+ /* The user specfies the in KB, we want them in byte
+ * (and page aligned) */
+ v4l_bufsize = PAGE_ALIGN(v4l_bufsize * 1024);
+ if (v4l_bufsize < 32768)
+ v4l_bufsize = 32768;
+ /* 2 MB is arbitrary but sufficient for the maximum possible images */
+ if (v4l_bufsize > 2048 * 1024)
+ v4l_bufsize = 2048 * 1024;
+ if (jpg_nbufs < 4)
+ jpg_nbufs = 4;
+ if (jpg_nbufs > BUZ_MAX_FRAME)
+ jpg_nbufs = BUZ_MAX_FRAME;
+ jpg_bufsize = PAGE_ALIGN(jpg_bufsize * 1024);
+ if (jpg_bufsize < 8192)
+ jpg_bufsize = 8192;
+ if (jpg_bufsize > (512 * 1024))
+ jpg_bufsize = 512 * 1024;
+ /* Use parameter for vidmem or try to find a video card */
+ if (vidmem) {
+ dprintk(1,
+ KERN_INFO
+ "%s: Using supplied video memory base address @ 0x%lx\n",
+ ZORAN_NAME, vidmem);
+ }
+
+ /* random nonsense */
+ dprintk(5, KERN_DEBUG "Jotti is een held!\n");
+
+ /* some mainboards might not do PCI-PCI data transfer well */
+ if (pci_pci_problems & PCIPCI_FAIL) {
+ dprintk(1,
+ KERN_WARNING
+ "%s: chipset may not support reliable PCI-PCI DMA\n",
+ ZORAN_NAME);
+ }
+
+ /* take care of Natoma chipset and a revision 1 zr36057 */
+ for (i = 0; i < zoran_num; i++) {
+ struct zoran *zr = &zoran[i];
+
+ if (pci_pci_problems & PCIPCI_NATOMA && zr->revision <= 1) {
+ zr->jpg_buffers.need_contiguous = 1;
+ dprintk(1,
+ KERN_INFO
+ "%s: ZR36057/Natoma bug, max. buffer size is 128K\n",
+ ZR_DEVNAME(zr));
+ }
+
+ if (zr36057_init(zr) < 0) {
+ for (i = 0; i < zoran_num; i++)
+ zoran_release(&zoran[i]);
+ return -EIO;
+ }
+ zoran_proc_init(zr);
+ }
+
+ return 0;
+}
+
+static void __exit
+unload_dc10_cards (void)
+{
+ int i;
+
+ for (i = 0; i < zoran_num; i++)
+ zoran_release(&zoran[i]);
+}
+
+module_init(init_dc10_cards);
+module_exit(unload_dc10_cards);
diff --git a/linux/drivers/media/video/zoran_card.h b/linux/drivers/media/video/zoran_card.h
new file mode 100644
index 000000000..ad997c30b
--- /dev/null
+++ b/linux/drivers/media/video/zoran_card.h
@@ -0,0 +1,45 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * This part handles card-specific data and detection
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Currently maintained by:
+ * Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * Laurent Pinchart <laurent.pinchart@skynet.be>
+ * Mailinglist <mjpeg-users@lists.sf.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ZORAN_CARD_H__
+#define __ZORAN_CARD_H__
+
+/* Anybody who uses more than four? */
+#define BUZ_MAX 4
+extern int zoran_num;
+extern struct zoran zoran[BUZ_MAX];
+
+extern struct video_device zoran_template;
+
+extern int zoran_check_jpg_settings(struct zoran *zr,
+ struct zoran_jpg_settings *settings);
+extern void zoran_open_init_params(struct zoran *zr);
+extern void zoran_vdev_release(struct video_device *vdev);
+
+#endif /* __ZORAN_CARD_H__ */
diff --git a/linux/drivers/media/video/zoran_device.c b/linux/drivers/media/video/zoran_device.c
new file mode 100644
index 000000000..bb5011280
--- /dev/null
+++ b/linux/drivers/media/video/zoran_device.c
@@ -0,0 +1,1786 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * This part handles device access (PCI/I2C/codec/...)
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Currently maintained by:
+ * Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * Laurent Pinchart <laurent.pinchart@skynet.be>
+ * Mailinglist <mjpeg-users@lists.sf.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/byteorder/generic.h>
+
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include "compat.h"
+#include <linux/videodev.h>
+#include <linux/spinlock.h>
+#include <linux/sem.h>
+
+#include <linux/pci.h>
+#include <linux/video_decoder.h>
+#include <linux/video_encoder.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+
+#include <asm/io.h>
+
+#include "videocodec.h"
+#include "zoran.h"
+#include "zoran_device.h"
+
+#define IRQ_MASK ( ZR36057_ISR_GIRQ0 | \
+ ZR36057_ISR_GIRQ1 | \
+ ZR36057_ISR_JPEGRepIRQ )
+
+extern const struct zoran_format zoran_formats[];
+
+extern int *zr_debug;
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (*zr_debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+static int lml33dpath = 0; /* 1 will use digital path in capture
+ * mode instead of analog. It can be
+ * used for picture adjustments using
+ * tool like xawtv while watching image
+ * on TV monitor connected to the output.
+ * However, due to absence of 75 Ohm
+ * load on Bt819 input, there will be
+ * some image imperfections */
+
+module_param(lml33dpath, bool, 0);
+MODULE_PARM_DESC(lml33dpath,
+ "Use digital path capture mode (on LML33 cards)");
+
+static void
+zr36057_init_vfe (struct zoran *zr);
+
+/*
+ * General Purpose I/O and Guest bus access
+ */
+
+/*
+ * This is a bit tricky. When a board lacks a GPIO function, the corresponding
+ * GPIO bit number in the card_info structure is set to 0.
+ */
+
+void
+GPIO (struct zoran *zr,
+ int bit,
+ unsigned int value)
+{
+ u32 reg;
+ u32 mask;
+
+ /* Make sure the bit number is legal
+ * A bit number of -1 (lacking) gives a mask of 0,
+ * making it harmless */
+ mask = (1 << (24 + bit)) & 0xff000000;
+ reg = btread(ZR36057_GPPGCR1) & ~mask;
+ if (value) {
+ reg |= mask;
+ }
+ btwrite(reg, ZR36057_GPPGCR1);
+ udelay(1);
+}
+
+/*
+ * Wait til post office is no longer busy
+ */
+
+int
+post_office_wait (struct zoran *zr)
+{
+ u32 por;
+
+// while (((por = btread(ZR36057_POR)) & (ZR36057_POR_POPen | ZR36057_POR_POTime)) == ZR36057_POR_POPen) {
+ while ((por = btread(ZR36057_POR)) & ZR36057_POR_POPen) {
+ /* wait for something to happen */
+ }
+ if ((por & ZR36057_POR_POTime) && !zr->card.gws_not_connected) {
+ /* In LML33/BUZ \GWS line is not connected, so it has always timeout set */
+ dprintk(1, KERN_INFO "%s: pop timeout %08x\n", ZR_DEVNAME(zr),
+ por);
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+post_office_write (struct zoran *zr,
+ unsigned int guest,
+ unsigned int reg,
+ unsigned int value)
+{
+ u32 por;
+
+ por =
+ ZR36057_POR_PODir | ZR36057_POR_POTime | ((guest & 7) << 20) |
+ ((reg & 7) << 16) | (value & 0xFF);
+ btwrite(por, ZR36057_POR);
+
+ return post_office_wait(zr);
+}
+
+int
+post_office_read (struct zoran *zr,
+ unsigned int guest,
+ unsigned int reg)
+{
+ u32 por;
+
+ por = ZR36057_POR_POTime | ((guest & 7) << 20) | ((reg & 7) << 16);
+ btwrite(por, ZR36057_POR);
+ if (post_office_wait(zr) < 0) {
+ return -1;
+ }
+
+ return btread(ZR36057_POR) & 0xFF;
+}
+
+/*
+ * detect guests
+ */
+
+static void
+dump_guests (struct zoran *zr)
+{
+ if (*zr_debug > 2) {
+ int i, guest[8];
+
+ for (i = 1; i < 8; i++) { // Don't read jpeg codec here
+ guest[i] = post_office_read(zr, i, 0);
+ }
+
+ printk(KERN_INFO "%s: Guests:", ZR_DEVNAME(zr));
+
+ for (i = 1; i < 8; i++) {
+ printk(" 0x%02x", guest[i]);
+ }
+ printk("\n");
+ }
+}
+
+static inline unsigned long
+get_time (void)
+{
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+ return (1000000 * tv.tv_sec + tv.tv_usec);
+}
+
+void
+detect_guest_activity (struct zoran *zr)
+{
+ int timeout, i, j, res, guest[8], guest0[8], change[8][3];
+ unsigned long t0, t1;
+
+ dump_guests(zr);
+ printk(KERN_INFO "%s: Detecting guests activity, please wait...\n",
+ ZR_DEVNAME(zr));
+ for (i = 1; i < 8; i++) { // Don't read jpeg codec here
+ guest0[i] = guest[i] = post_office_read(zr, i, 0);
+ }
+
+ timeout = 0;
+ j = 0;
+ t0 = get_time();
+ while (timeout < 10000) {
+ udelay(10);
+ timeout++;
+ for (i = 1; (i < 8) && (j < 8); i++) {
+ res = post_office_read(zr, i, 0);
+ if (res != guest[i]) {
+ t1 = get_time();
+ change[j][0] = (t1 - t0);
+ t0 = t1;
+ change[j][1] = i;
+ change[j][2] = res;
+ j++;
+ guest[i] = res;
+ }
+ }
+ if (j >= 8)
+ break;
+ }
+ printk(KERN_INFO "%s: Guests:", ZR_DEVNAME(zr));
+
+ for (i = 1; i < 8; i++) {
+ printk(" 0x%02x", guest0[i]);
+ }
+ printk("\n");
+ if (j == 0) {
+ printk(KERN_INFO "%s: No activity detected.\n", ZR_DEVNAME(zr));
+ return;
+ }
+ for (i = 0; i < j; i++) {
+ printk(KERN_INFO "%s: %6d: %d => 0x%02x\n", ZR_DEVNAME(zr),
+ change[i][0], change[i][1], change[i][2]);
+ }
+}
+
+/*
+ * JPEG Codec access
+ */
+
+void
+jpeg_codec_sleep (struct zoran *zr,
+ int sleep)
+{
+ GPIO(zr, zr->card.gpio[GPIO_JPEG_SLEEP], !sleep);
+ if (!sleep) {
+ dprintk(3,
+ KERN_DEBUG
+ "%s: jpeg_codec_sleep() - wake GPIO=0x%08x\n",
+ ZR_DEVNAME(zr), btread(ZR36057_GPPGCR1));
+ udelay(500);
+ } else {
+ dprintk(3,
+ KERN_DEBUG
+ "%s: jpeg_codec_sleep() - sleep GPIO=0x%08x\n",
+ ZR_DEVNAME(zr), btread(ZR36057_GPPGCR1));
+ udelay(2);
+ }
+}
+
+int
+jpeg_codec_reset (struct zoran *zr)
+{
+ /* Take the codec out of sleep */
+ jpeg_codec_sleep(zr, 0);
+
+ if (zr->card.gpcs[GPCS_JPEG_RESET] != 0xff) {
+ post_office_write(zr, zr->card.gpcs[GPCS_JPEG_RESET], 0,
+ 0);
+ udelay(2);
+ } else {
+ GPIO(zr, zr->card.gpio[GPIO_JPEG_RESET], 0);
+ udelay(2);
+ GPIO(zr, zr->card.gpio[GPIO_JPEG_RESET], 1);
+ udelay(2);
+ }
+
+ return 0;
+}
+
+/*
+ * Set the registers for the size we have specified. Don't bother
+ * trying to understand this without the ZR36057 manual in front of
+ * you [AC].
+ *
+ * PS: The manual is free for download in .pdf format from
+ * www.zoran.com - nicely done those folks.
+ */
+
+static void
+zr36057_adjust_vfe (struct zoran *zr,
+ enum zoran_codec_mode mode)
+{
+ u32 reg;
+
+ switch (mode) {
+ case BUZ_MODE_MOTION_DECOMPRESS:
+ btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
+ reg = btread(ZR36057_VFEHCR);
+ if ((reg & (1 << 10)) && zr->card.type != LML33R10) {
+ reg += ((1 << 10) | 1);
+ }
+ btwrite(reg, ZR36057_VFEHCR);
+ break;
+ case BUZ_MODE_MOTION_COMPRESS:
+ case BUZ_MODE_IDLE:
+ default:
+ if (zr->norm == VIDEO_MODE_NTSC ||
+ (zr->card.type == LML33R10 &&
+ zr->norm == VIDEO_MODE_PAL))
+ btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
+ else
+ btor(ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
+ reg = btread(ZR36057_VFEHCR);
+ if (!(reg & (1 << 10)) && zr->card.type != LML33R10) {
+ reg -= ((1 << 10) | 1);
+ }
+ btwrite(reg, ZR36057_VFEHCR);
+ break;
+ }
+}
+
+/*
+ * set geometry
+ */
+
+static void
+zr36057_set_vfe (struct zoran *zr,
+ int video_width,
+ int video_height,
+ const struct zoran_format *format)
+{
+ struct tvnorm *tvn;
+ unsigned HStart, HEnd, VStart, VEnd;
+ unsigned DispMode;
+ unsigned VidWinWid, VidWinHt;
+ unsigned hcrop1, hcrop2, vcrop1, vcrop2;
+ unsigned Wa, We, Ha, He;
+ unsigned X, Y, HorDcm, VerDcm;
+ u32 reg;
+ unsigned mask_line_size;
+
+ tvn = zr->timing;
+
+ Wa = tvn->Wa;
+ Ha = tvn->Ha;
+
+ dprintk(2, KERN_INFO "%s: set_vfe() - width = %d, height = %d\n",
+ ZR_DEVNAME(zr), video_width, video_height);
+
+ if (zr->norm != VIDEO_MODE_PAL &&
+ zr->norm != VIDEO_MODE_NTSC &&
+ zr->norm != VIDEO_MODE_SECAM) {
+ dprintk(1,
+ KERN_ERR "%s: set_vfe() - norm = %d not valid\n",
+ ZR_DEVNAME(zr), zr->norm);
+ return;
+ }
+ if (video_width < BUZ_MIN_WIDTH ||
+ video_height < BUZ_MIN_HEIGHT ||
+ video_width > Wa || video_height > Ha) {
+ dprintk(1, KERN_ERR "%s: set_vfe: w=%d h=%d not valid\n",
+ ZR_DEVNAME(zr), video_width, video_height);
+ return;
+ }
+
+ /**** zr36057 ****/
+
+ /* horizontal */
+ VidWinWid = video_width;
+ X = (VidWinWid * 64 + tvn->Wa - 1) / tvn->Wa;
+ We = (VidWinWid * 64) / X;
+ HorDcm = 64 - X;
+ hcrop1 = 2 * ((tvn->Wa - We) / 4);
+ hcrop2 = tvn->Wa - We - hcrop1;
+ HStart = tvn->HStart ? tvn->HStart : 1;
+ /* (Ronald) Original comment:
+ * "| 1 Doesn't have any effect, tested on both a DC10 and a DC10+"
+ * this is false. It inverses chroma values on the LML33R10 (so Cr
+ * suddenly is shown as Cb and reverse, really cool effect if you
+ * want to see blue faces, not useful otherwise). So don't use |1.
+ * However, the DC10 has '0' as HStart, but does need |1, so we
+ * use a dirty check...
+ */
+ HEnd = HStart + tvn->Wa - 1;
+ HStart += hcrop1;
+ HEnd -= hcrop2;
+ reg = ((HStart & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HStart)
+ | ((HEnd & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HEnd);
+ if (zr->card.vfe_pol.hsync_pol)
+ reg |= ZR36057_VFEHCR_HSPol;
+ btwrite(reg, ZR36057_VFEHCR);
+
+ /* Vertical */
+ DispMode = !(video_height > BUZ_MAX_HEIGHT / 2);
+ VidWinHt = DispMode ? video_height : video_height / 2;
+ Y = (VidWinHt * 64 * 2 + tvn->Ha - 1) / tvn->Ha;
+ He = (VidWinHt * 64) / Y;
+ VerDcm = 64 - Y;
+ vcrop1 = (tvn->Ha / 2 - He) / 2;
+ vcrop2 = tvn->Ha / 2 - He - vcrop1;
+ VStart = tvn->VStart;
+ VEnd = VStart + tvn->Ha / 2; // - 1; FIXME SnapShot times out with -1 in 768*576 on the DC10 - LP
+ VStart += vcrop1;
+ VEnd -= vcrop2;
+ reg = ((VStart & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VStart)
+ | ((VEnd & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VEnd);
+ if (zr->card.vfe_pol.vsync_pol)
+ reg |= ZR36057_VFEVCR_VSPol;
+ btwrite(reg, ZR36057_VFEVCR);
+
+ /* scaler and pixel format */
+ reg = 0;
+ reg |= (HorDcm << ZR36057_VFESPFR_HorDcm);
+ reg |= (VerDcm << ZR36057_VFESPFR_VerDcm);
+ reg |= (DispMode << ZR36057_VFESPFR_DispMode);
+ if (format->palette != VIDEO_PALETTE_YUV422)
+ reg |= ZR36057_VFESPFR_LittleEndian;
+ /* RJ: I don't know, why the following has to be the opposite
+ * of the corresponding ZR36060 setting, but only this way
+ * we get the correct colors when uncompressing to the screen */
+ //reg |= ZR36057_VFESPFR_VCLKPol; /**/
+ /* RJ: Don't know if that is needed for NTSC also */
+ if (zr->norm != VIDEO_MODE_NTSC)
+ reg |= ZR36057_VFESPFR_ExtFl; // NEEDED!!!!!!! Wolfgang
+ reg |= ZR36057_VFESPFR_TopField;
+ switch (format->palette) {
+
+ case VIDEO_PALETTE_YUV422:
+ reg |= ZR36057_VFESPFR_YUV422;
+ break;
+
+ case VIDEO_PALETTE_RGB555:
+ reg |= ZR36057_VFESPFR_RGB555 | ZR36057_VFESPFR_ErrDif;
+ break;
+
+ case VIDEO_PALETTE_RGB565:
+ reg |= ZR36057_VFESPFR_RGB565 | ZR36057_VFESPFR_ErrDif;
+ break;
+
+ case VIDEO_PALETTE_RGB24:
+ reg |= ZR36057_VFESPFR_RGB888 | ZR36057_VFESPFR_Pack24;
+ break;
+
+ case VIDEO_PALETTE_RGB32:
+ reg |= ZR36057_VFESPFR_RGB888;
+ break;
+
+ default:
+ dprintk(1,
+ KERN_INFO "%s: set_vfe() - unknown color_fmt=%x\n",
+ ZR_DEVNAME(zr), format->palette);
+ return;
+
+ }
+ if (HorDcm >= 48) {
+ reg |= 3 << ZR36057_VFESPFR_HFilter; /* 5 tap filter */
+ } else if (HorDcm >= 32) {
+ reg |= 2 << ZR36057_VFESPFR_HFilter; /* 4 tap filter */
+ } else if (HorDcm >= 16) {
+ reg |= 1 << ZR36057_VFESPFR_HFilter; /* 3 tap filter */
+ }
+ btwrite(reg, ZR36057_VFESPFR);
+
+ /* display configuration */
+ reg = (16 << ZR36057_VDCR_MinPix)
+ | (VidWinHt << ZR36057_VDCR_VidWinHt)
+ | (VidWinWid << ZR36057_VDCR_VidWinWid);
+ if (pci_pci_problems & PCIPCI_TRITON)
+ // || zr->revision < 1) // Revision 1 has also Triton support
+ reg &= ~ZR36057_VDCR_Triton;
+ else
+ reg |= ZR36057_VDCR_Triton;
+ btwrite(reg, ZR36057_VDCR);
+
+ /* (Ronald) don't write this if overlay_mask = NULL */
+ if (zr->overlay_mask) {
+ /* Write overlay clipping mask data, but don't enable overlay clipping */
+ /* RJ: since this makes only sense on the screen, we use
+ * zr->overlay_settings.width instead of video_width */
+
+ mask_line_size = (BUZ_MAX_WIDTH + 31) / 32;
+ reg = virt_to_bus(zr->overlay_mask);
+ btwrite(reg, ZR36057_MMTR);
+ reg = virt_to_bus(zr->overlay_mask + mask_line_size);
+ btwrite(reg, ZR36057_MMBR);
+ reg =
+ mask_line_size - (zr->overlay_settings.width +
+ 31) / 32;
+ if (DispMode == 0)
+ reg += mask_line_size;
+ reg <<= ZR36057_OCR_MaskStride;
+ btwrite(reg, ZR36057_OCR);
+ }
+
+ zr36057_adjust_vfe(zr, zr->codec_mode);
+}
+
+/*
+ * Switch overlay on or off
+ */
+
+void
+zr36057_overlay (struct zoran *zr,
+ int on)
+{
+ u32 reg;
+
+ if (on) {
+ /* do the necessary settings ... */
+ btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); /* switch it off first */
+
+ zr36057_set_vfe(zr,
+ zr->overlay_settings.width,
+ zr->overlay_settings.height,
+ zr->overlay_settings.format);
+
+ /* Start and length of each line MUST be 4-byte aligned.
+ * This should be allready checked before the call to this routine.
+ * All error messages are internal driver checking only! */
+
+ /* video display top and bottom registers */
+ reg = (u32) zr->buffer.base +
+ zr->overlay_settings.x *
+ ((zr->overlay_settings.format->depth + 7) / 8) +
+ zr->overlay_settings.y *
+ zr->buffer.bytesperline;
+ btwrite(reg, ZR36057_VDTR);
+ if (reg & 3)
+ dprintk(1,
+ KERN_ERR
+ "%s: zr36057_overlay() - video_address not aligned\n",
+ ZR_DEVNAME(zr));
+ if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2)
+ reg += zr->buffer.bytesperline;
+ btwrite(reg, ZR36057_VDBR);
+
+ /* video stride, status, and frame grab register */
+ reg = zr->buffer.bytesperline -
+ zr->overlay_settings.width *
+ ((zr->overlay_settings.format->depth + 7) / 8);
+ if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2)
+ reg += zr->buffer.bytesperline;
+ if (reg & 3)
+ dprintk(1,
+ KERN_ERR
+ "%s: zr36057_overlay() - video_stride not aligned\n",
+ ZR_DEVNAME(zr));
+ reg = (reg << ZR36057_VSSFGR_DispStride);
+ reg |= ZR36057_VSSFGR_VidOvf; /* clear overflow status */
+ btwrite(reg, ZR36057_VSSFGR);
+
+ /* Set overlay clipping */
+ if (zr->overlay_settings.clipcount > 0)
+ btor(ZR36057_OCR_OvlEnable, ZR36057_OCR);
+
+ /* ... and switch it on */
+ btor(ZR36057_VDCR_VidEn, ZR36057_VDCR);
+ } else {
+ /* Switch it off */
+ btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR);
+ }
+}
+
+/*
+ * The overlay mask has one bit for each pixel on a scan line,
+ * and the maximum window size is BUZ_MAX_WIDTH * BUZ_MAX_HEIGHT pixels.
+ */
+
+void
+write_overlay_mask (struct file *file,
+ struct video_clip *vp,
+ int count)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ unsigned mask_line_size = (BUZ_MAX_WIDTH + 31) / 32;
+ u32 *mask;
+ int x, y, width, height;
+ unsigned i, j, k;
+ u32 reg;
+
+ /* fill mask with one bits */
+ memset(fh->overlay_mask, ~0, mask_line_size * 4 * BUZ_MAX_HEIGHT);
+ reg = 0;
+
+ for (i = 0; i < count; ++i) {
+ /* pick up local copy of clip */
+ x = vp[i].x;
+ y = vp[i].y;
+ width = vp[i].width;
+ height = vp[i].height;
+
+ /* trim clips that extend beyond the window */
+ if (x < 0) {
+ width += x;
+ x = 0;
+ }
+ if (y < 0) {
+ height += y;
+ y = 0;
+ }
+ if (x + width > fh->overlay_settings.width) {
+ width = fh->overlay_settings.width - x;
+ }
+ if (y + height > fh->overlay_settings.height) {
+ height = fh->overlay_settings.height - y;
+ }
+
+ /* ignore degenerate clips */
+ if (height <= 0) {
+ continue;
+ }
+ if (width <= 0) {
+ continue;
+ }
+
+ /* apply clip for each scan line */
+ for (j = 0; j < height; ++j) {
+ /* reset bit for each pixel */
+ /* this can be optimized later if need be */
+ mask = fh->overlay_mask + (y + j) * mask_line_size;
+ for (k = 0; k < width; ++k) {
+ mask[(x + k) / 32] &=
+ ~((u32) 1 << (x + k) % 32);
+ }
+ }
+ }
+}
+
+/* Enable/Disable uncompressed memory grabbing of the 36057 */
+
+void
+zr36057_set_memgrab (struct zoran *zr,
+ int mode)
+{
+ if (mode) {
+ if (btread(ZR36057_VSSFGR) &
+ (ZR36057_VSSFGR_SnapShot | ZR36057_VSSFGR_FrameGrab))
+ dprintk(1,
+ KERN_WARNING
+ "%s: zr36057_set_memgrab(1) with SnapShot or FrameGrab on!?\n",
+ ZR_DEVNAME(zr));
+
+ /* switch on VSync interrupts */
+ btwrite(IRQ_MASK, ZR36057_ISR); // Clear Interrupts
+ btor(zr->card.vsync_int, ZR36057_ICR); // SW
+
+ /* enable SnapShot */
+ btor(ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR);
+
+ /* Set zr36057 video front end and enable video */
+ zr36057_set_vfe(zr, zr->v4l_settings.width,
+ zr->v4l_settings.height,
+ zr->v4l_settings.format);
+
+ zr->v4l_memgrab_active = 1;
+ } else {
+ zr->v4l_memgrab_active = 0;
+
+ /* switch off VSync interrupts */
+ btand(~zr->card.vsync_int, ZR36057_ICR); // SW
+
+ /* reenable grabbing to screen if it was running */
+ if (zr->v4l_overlay_active) {
+ zr36057_overlay(zr, 1);
+ } else {
+ btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR);
+ btand(~ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR);
+ }
+ }
+}
+
+int
+wait_grab_pending (struct zoran *zr)
+{
+ unsigned long flags;
+
+ /* wait until all pending grabs are finished */
+
+ if (!zr->v4l_memgrab_active)
+ return 0;
+
+ wait_event_interruptible(zr->v4l_capq,
+ (zr->v4l_pend_tail == zr->v4l_pend_head));
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+ zr36057_set_memgrab(zr, 0);
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ return 0;
+}
+
+/*****************************************************************************
+ * *
+ * Set up the Buz-specific MJPEG part *
+ * *
+ *****************************************************************************/
+
+static inline void
+set_frame (struct zoran *zr,
+ int val)
+{
+ GPIO(zr, zr->card.gpio[GPIO_JPEG_FRAME], val);
+}
+
+static void
+set_videobus_dir (struct zoran *zr,
+ int val)
+{
+ switch (zr->card.type) {
+ case LML33:
+ case LML33R10:
+ if (lml33dpath == 0)
+ GPIO(zr, 5, val);
+ else
+ GPIO(zr, 5, 1);
+ break;
+ default:
+ GPIO(zr, zr->card.gpio[GPIO_VID_DIR],
+ zr->card.gpio_pol[GPIO_VID_DIR] ? !val : val);
+ break;
+ }
+}
+
+static void
+init_jpeg_queue (struct zoran *zr)
+{
+ int i;
+
+ /* re-initialize DMA ring stuff */
+ zr->jpg_que_head = 0;
+ zr->jpg_dma_head = 0;
+ zr->jpg_dma_tail = 0;
+ zr->jpg_que_tail = 0;
+ zr->jpg_seq_num = 0;
+ zr->JPEG_error = 0;
+ zr->num_errors = 0;
+ zr->jpg_err_seq = 0;
+ zr->jpg_err_shift = 0;
+ zr->jpg_queued_num = 0;
+ for (i = 0; i < zr->jpg_buffers.num_buffers; i++) {
+ zr->jpg_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
+ }
+ for (i = 0; i < BUZ_NUM_STAT_COM; i++) {
+ zr->stat_com[i] = cpu_to_le32(1); /* mark as unavailable to zr36057 */
+ }
+}
+
+static void
+zr36057_set_jpg (struct zoran *zr,
+ enum zoran_codec_mode mode)
+{
+ struct tvnorm *tvn;
+ u32 reg;
+
+ tvn = zr->timing;
+
+ /* assert P_Reset, disable code transfer, deassert Active */
+ btwrite(0, ZR36057_JPC);
+
+ /* MJPEG compression mode */
+ switch (mode) {
+
+ case BUZ_MODE_MOTION_COMPRESS:
+ default:
+ reg = ZR36057_JMC_MJPGCmpMode;
+ break;
+
+ case BUZ_MODE_MOTION_DECOMPRESS:
+ reg = ZR36057_JMC_MJPGExpMode;
+ reg |= ZR36057_JMC_SyncMstr;
+ /* RJ: The following is experimental - improves the output to screen */
+ //if(zr->jpg_settings.VFIFO_FB) reg |= ZR36057_JMC_VFIFO_FB; // No, it doesn't. SM
+ break;
+
+ case BUZ_MODE_STILL_COMPRESS:
+ reg = ZR36057_JMC_JPGCmpMode;
+ break;
+
+ case BUZ_MODE_STILL_DECOMPRESS:
+ reg = ZR36057_JMC_JPGExpMode;
+ break;
+
+ }
+ reg |= ZR36057_JMC_JPG;
+ if (zr->jpg_settings.field_per_buff == 1)
+ reg |= ZR36057_JMC_Fld_per_buff;
+ btwrite(reg, ZR36057_JMC);
+
+ /* vertical */
+ btor(ZR36057_VFEVCR_VSPol, ZR36057_VFEVCR);
+ reg = (6 << ZR36057_VSP_VsyncSize) |
+ (tvn->Ht << ZR36057_VSP_FrmTot);
+ btwrite(reg, ZR36057_VSP);
+ reg = ((zr->jpg_settings.img_y + tvn->VStart) << ZR36057_FVAP_NAY) |
+ (zr->jpg_settings.img_height << ZR36057_FVAP_PAY);
+ btwrite(reg, ZR36057_FVAP);
+
+ /* horizontal */
+ if (zr->card.vfe_pol.hsync_pol)
+ btor(ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR);
+ else
+ btand(~ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR);
+ reg = ((tvn->HSyncStart) << ZR36057_HSP_HsyncStart) |
+ (tvn->Wt << ZR36057_HSP_LineTot);
+ btwrite(reg, ZR36057_HSP);
+ reg = ((zr->jpg_settings.img_x +
+ tvn->HStart + 4) << ZR36057_FHAP_NAX) |
+ (zr->jpg_settings.img_width << ZR36057_FHAP_PAX);
+ btwrite(reg, ZR36057_FHAP);
+
+ /* field process parameters */
+ if (zr->jpg_settings.odd_even)
+ reg = ZR36057_FPP_Odd_Even;
+ else
+ reg = 0;
+
+ btwrite(reg, ZR36057_FPP);
+
+ /* Set proper VCLK Polarity, else colors will be wrong during playback */
+ //btor(ZR36057_VFESPFR_VCLKPol, ZR36057_VFESPFR);
+
+ /* code base address */
+ reg = virt_to_bus(zr->stat_com);
+ btwrite(reg, ZR36057_JCBA);
+
+ /* FIFO threshold (FIFO is 160. double words) */
+ /* NOTE: decimal values here */
+ switch (mode) {
+
+ case BUZ_MODE_STILL_COMPRESS:
+ case BUZ_MODE_MOTION_COMPRESS:
+ if (zr->card.type != BUZ)
+ reg = 140;
+ else
+ reg = 60;
+ break;
+
+ case BUZ_MODE_STILL_DECOMPRESS:
+ case BUZ_MODE_MOTION_DECOMPRESS:
+ reg = 20;
+ break;
+
+ default:
+ reg = 80;
+ break;
+
+ }
+ btwrite(reg, ZR36057_JCFT);
+ zr36057_adjust_vfe(zr, mode);
+
+}
+
+void
+print_interrupts (struct zoran *zr)
+{
+ int res, noerr = 0;
+
+ printk(KERN_INFO "%s: interrupts received:", ZR_DEVNAME(zr));
+ if ((res = zr->field_counter) < -1 || res > 1) {
+ printk(" FD:%d", res);
+ }
+ if ((res = zr->intr_counter_GIRQ1) != 0) {
+ printk(" GIRQ1:%d", res);
+ noerr++;
+ }
+ if ((res = zr->intr_counter_GIRQ0) != 0) {
+ printk(" GIRQ0:%d", res);
+ noerr++;
+ }
+ if ((res = zr->intr_counter_CodRepIRQ) != 0) {
+ printk(" CodRepIRQ:%d", res);
+ noerr++;
+ }
+ if ((res = zr->intr_counter_JPEGRepIRQ) != 0) {
+ printk(" JPEGRepIRQ:%d", res);
+ noerr++;
+ }
+ if (zr->JPEG_max_missed) {
+ printk(" JPEG delays: max=%d min=%d", zr->JPEG_max_missed,
+ zr->JPEG_min_missed);
+ }
+ if (zr->END_event_missed) {
+ printk(" ENDs missed: %d", zr->END_event_missed);
+ }
+ //if (zr->jpg_queued_num) {
+ printk(" queue_state=%ld/%ld/%ld/%ld", zr->jpg_que_tail,
+ zr->jpg_dma_tail, zr->jpg_dma_head, zr->jpg_que_head);
+ //}
+ if (!noerr) {
+ printk(": no interrupts detected.");
+ }
+ printk("\n");
+}
+
+void
+clear_interrupt_counters (struct zoran *zr)
+{
+ zr->intr_counter_GIRQ1 = 0;
+ zr->intr_counter_GIRQ0 = 0;
+ zr->intr_counter_CodRepIRQ = 0;
+ zr->intr_counter_JPEGRepIRQ = 0;
+ zr->field_counter = 0;
+ zr->IRQ1_in = 0;
+ zr->IRQ1_out = 0;
+ zr->JPEG_in = 0;
+ zr->JPEG_out = 0;
+ zr->JPEG_0 = 0;
+ zr->JPEG_1 = 0;
+ zr->END_event_missed = 0;
+ zr->JPEG_missed = 0;
+ zr->JPEG_max_missed = 0;
+ zr->JPEG_min_missed = 0x7fffffff;
+}
+
+static u32
+count_reset_interrupt (struct zoran *zr)
+{
+ u32 isr;
+
+ if ((isr = btread(ZR36057_ISR) & 0x78000000)) {
+ if (isr & ZR36057_ISR_GIRQ1) {
+ btwrite(ZR36057_ISR_GIRQ1, ZR36057_ISR);
+ zr->intr_counter_GIRQ1++;
+ }
+ if (isr & ZR36057_ISR_GIRQ0) {
+ btwrite(ZR36057_ISR_GIRQ0, ZR36057_ISR);
+ zr->intr_counter_GIRQ0++;
+ }
+ if (isr & ZR36057_ISR_CodRepIRQ) {
+ btwrite(ZR36057_ISR_CodRepIRQ, ZR36057_ISR);
+ zr->intr_counter_CodRepIRQ++;
+ }
+ if (isr & ZR36057_ISR_JPEGRepIRQ) {
+ btwrite(ZR36057_ISR_JPEGRepIRQ, ZR36057_ISR);
+ zr->intr_counter_JPEGRepIRQ++;
+ }
+ }
+ return isr;
+}
+
+/* hack */
+extern void zr36016_write (struct videocodec *codec,
+ u16 reg,
+ u32 val);
+
+void
+jpeg_start (struct zoran *zr)
+{
+ int reg;
+
+ zr->frame_num = 0;
+
+ /* deassert P_reset, disable code transfer, deassert Active */
+ btwrite(ZR36057_JPC_P_Reset, ZR36057_JPC);
+ /* stop flushing the internal code buffer */
+ btand(~ZR36057_MCTCR_CFlush, ZR36057_MCTCR);
+ /* enable code transfer */
+ btor(ZR36057_JPC_CodTrnsEn, ZR36057_JPC);
+
+ /* clear IRQs */
+ btwrite(IRQ_MASK, ZR36057_ISR);
+ /* enable the JPEG IRQs */
+ btwrite(zr->card.jpeg_int |
+ ZR36057_ICR_JPEGRepIRQ |
+ ZR36057_ICR_IntPinEn,
+ ZR36057_ICR);
+
+ set_frame(zr, 0); // \FRAME
+
+ /* set the JPEG codec guest ID */
+ reg = (zr->card.gpcs[1] << ZR36057_JCGI_JPEGuestID) |
+ (0 << ZR36057_JCGI_JPEGuestReg);
+ btwrite(reg, ZR36057_JCGI);
+
+ if (zr->card.video_vfe == CODEC_TYPE_ZR36016 &&
+ zr->card.video_codec == CODEC_TYPE_ZR36050) {
+ /* Enable processing on the ZR36016 */
+ if (zr->vfe)
+ zr36016_write(zr->vfe, 0, 1);
+
+ /* load the address of the GO register in the ZR36050 latch */
+ post_office_write(zr, 0, 0, 0);
+ }
+
+ /* assert Active */
+ btor(ZR36057_JPC_Active, ZR36057_JPC);
+
+ /* enable the Go generation */
+ btor(ZR36057_JMC_Go_en, ZR36057_JMC);
+ udelay(30);
+
+ set_frame(zr, 1); // /FRAME
+
+ dprintk(3, KERN_DEBUG "%s: jpeg_start\n", ZR_DEVNAME(zr));
+}
+
+void
+zr36057_enable_jpg (struct zoran *zr,
+ enum zoran_codec_mode mode)
+{
+ static int zero = 0;
+ static int one = 1;
+ struct vfe_settings cap;
+ int field_size =
+ zr->jpg_buffers.buffer_size / zr->jpg_settings.field_per_buff;
+
+ zr->codec_mode = mode;
+
+ cap.x = zr->jpg_settings.img_x;
+ cap.y = zr->jpg_settings.img_y;
+ cap.width = zr->jpg_settings.img_width;
+ cap.height = zr->jpg_settings.img_height;
+ cap.decimation =
+ zr->jpg_settings.HorDcm | (zr->jpg_settings.VerDcm << 8);
+ cap.quality = zr->jpg_settings.jpg_comp.quality;
+
+ switch (mode) {
+
+ case BUZ_MODE_MOTION_COMPRESS: {
+ struct jpeg_app_marker app;
+ struct jpeg_com_marker com;
+
+ /* In motion compress mode, the decoder output must be enabled, and
+ * the video bus direction set to input.
+ */
+ set_videobus_dir(zr, 0);
+ decoder_command(zr, DECODER_ENABLE_OUTPUT, &one);
+ encoder_command(zr, ENCODER_SET_INPUT, &zero);
+
+ /* Take the JPEG codec and the VFE out of sleep */
+ jpeg_codec_sleep(zr, 0);
+
+ /* set JPEG app/com marker */
+ app.appn = zr->jpg_settings.jpg_comp.APPn;
+ app.len = zr->jpg_settings.jpg_comp.APP_len;
+ memcpy(app.data, zr->jpg_settings.jpg_comp.APP_data, 60);
+ zr->codec->control(zr->codec, CODEC_S_JPEG_APP_DATA,
+ sizeof(struct jpeg_app_marker), &app);
+
+ com.len = zr->jpg_settings.jpg_comp.COM_len;
+ memcpy(com.data, zr->jpg_settings.jpg_comp.COM_data, 60);
+ zr->codec->control(zr->codec, CODEC_S_JPEG_COM_DATA,
+ sizeof(struct jpeg_com_marker), &com);
+
+ /* Setup the JPEG codec */
+ zr->codec->control(zr->codec, CODEC_S_JPEG_TDS_BYTE,
+ sizeof(int), &field_size);
+ zr->codec->set_video(zr->codec, zr->timing, &cap,
+ &zr->card.vfe_pol);
+ zr->codec->set_mode(zr->codec, CODEC_DO_COMPRESSION);
+
+ /* Setup the VFE */
+ if (zr->vfe) {
+ zr->vfe->control(zr->vfe, CODEC_S_JPEG_TDS_BYTE,
+ sizeof(int), &field_size);
+ zr->vfe->set_video(zr->vfe, zr->timing, &cap,
+ &zr->card.vfe_pol);
+ zr->vfe->set_mode(zr->vfe, CODEC_DO_COMPRESSION);
+ }
+
+ init_jpeg_queue(zr);
+ zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO
+
+ clear_interrupt_counters(zr);
+ dprintk(2, KERN_INFO "%s: enable_jpg(MOTION_COMPRESS)\n",
+ ZR_DEVNAME(zr));
+ break;
+ }
+
+ case BUZ_MODE_MOTION_DECOMPRESS:
+ /* In motion decompression mode, the decoder output must be disabled, and
+ * the video bus direction set to output.
+ */
+ decoder_command(zr, DECODER_ENABLE_OUTPUT, &zero);
+ set_videobus_dir(zr, 1);
+ encoder_command(zr, ENCODER_SET_INPUT, &one);
+
+ /* Take the JPEG codec and the VFE out of sleep */
+ jpeg_codec_sleep(zr, 0);
+ /* Setup the VFE */
+ if (zr->vfe) {
+ zr->vfe->set_video(zr->vfe, zr->timing, &cap,
+ &zr->card.vfe_pol);
+ zr->vfe->set_mode(zr->vfe, CODEC_DO_EXPANSION);
+ }
+ /* Setup the JPEG codec */
+ zr->codec->set_video(zr->codec, zr->timing, &cap,
+ &zr->card.vfe_pol);
+ zr->codec->set_mode(zr->codec, CODEC_DO_EXPANSION);
+
+ init_jpeg_queue(zr);
+ zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO
+
+ clear_interrupt_counters(zr);
+ dprintk(2, KERN_INFO "%s: enable_jpg(MOTION_DECOMPRESS)\n",
+ ZR_DEVNAME(zr));
+ break;
+
+ case BUZ_MODE_IDLE:
+ default:
+ /* shut down processing */
+ btand(~(zr->card.jpeg_int | ZR36057_ICR_JPEGRepIRQ),
+ ZR36057_ICR);
+ btwrite(zr->card.jpeg_int | ZR36057_ICR_JPEGRepIRQ,
+ ZR36057_ISR);
+ btand(~ZR36057_JMC_Go_en, ZR36057_JMC); // \Go_en
+
+ msleep(50);
+
+ set_videobus_dir(zr, 0);
+ set_frame(zr, 1); // /FRAME
+ btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR); // /CFlush
+ btwrite(0, ZR36057_JPC); // \P_Reset,\CodTrnsEn,\Active
+ btand(~ZR36057_JMC_VFIFO_FB, ZR36057_JMC);
+ btand(~ZR36057_JMC_SyncMstr, ZR36057_JMC);
+ jpeg_codec_reset(zr);
+ jpeg_codec_sleep(zr, 1);
+ zr36057_adjust_vfe(zr, mode);
+
+ decoder_command(zr, DECODER_ENABLE_OUTPUT, &one);
+ encoder_command(zr, ENCODER_SET_INPUT, &zero);
+
+ dprintk(2, KERN_INFO "%s: enable_jpg(IDLE)\n", ZR_DEVNAME(zr));
+ break;
+
+ }
+}
+
+/* when this is called the spinlock must be held */
+void
+zoran_feed_stat_com (struct zoran *zr)
+{
+ /* move frames from pending queue to DMA */
+
+ int frame, i, max_stat_com;
+
+ max_stat_com =
+ (zr->jpg_settings.TmpDcm ==
+ 1) ? BUZ_NUM_STAT_COM : (BUZ_NUM_STAT_COM >> 1);
+
+ while ((zr->jpg_dma_head - zr->jpg_dma_tail) < max_stat_com &&
+ zr->jpg_dma_head < zr->jpg_que_head) {
+
+ frame = zr->jpg_pend[zr->jpg_dma_head & BUZ_MASK_FRAME];
+ if (zr->jpg_settings.TmpDcm == 1) {
+ /* fill 1 stat_com entry */
+ i = (zr->jpg_dma_head -
+ zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
+ if (!(zr->stat_com[i] & cpu_to_le32(1)))
+ break;
+ zr->stat_com[i] =
+ cpu_to_le32(zr->jpg_buffers.buffer[frame].frag_tab_bus);
+ } else {
+ /* fill 2 stat_com entries */
+ i = ((zr->jpg_dma_head -
+ zr->jpg_err_shift) & 1) * 2;
+ if (!(zr->stat_com[i] & cpu_to_le32(1)))
+ break;
+ zr->stat_com[i] =
+ cpu_to_le32(zr->jpg_buffers.buffer[frame].frag_tab_bus);
+ zr->stat_com[i + 1] =
+ cpu_to_le32(zr->jpg_buffers.buffer[frame].frag_tab_bus);
+ }
+ zr->jpg_buffers.buffer[frame].state = BUZ_STATE_DMA;
+ zr->jpg_dma_head++;
+
+ }
+ if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS)
+ zr->jpg_queued_num++;
+}
+
+/* when this is called the spinlock must be held */
+static void
+zoran_reap_stat_com (struct zoran *zr)
+{
+ /* move frames from DMA queue to done queue */
+
+ int i;
+ u32 stat_com;
+ unsigned int seq;
+ unsigned int dif;
+ struct zoran_jpg_buffer *buffer;
+ int frame;
+
+ /* In motion decompress we don't have a hardware frame counter,
+ * we just count the interrupts here */
+
+ if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) {
+ zr->jpg_seq_num++;
+ }
+ while (zr->jpg_dma_tail < zr->jpg_dma_head) {
+ if (zr->jpg_settings.TmpDcm == 1)
+ i = (zr->jpg_dma_tail -
+ zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
+ else
+ i = ((zr->jpg_dma_tail -
+ zr->jpg_err_shift) & 1) * 2 + 1;
+
+ stat_com = le32_to_cpu(zr->stat_com[i]);
+
+ if ((stat_com & 1) == 0) {
+ return;
+ }
+ frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME];
+ buffer = &zr->jpg_buffers.buffer[frame];
+ do_gettimeofday(&buffer->bs.timestamp);
+
+ if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
+ buffer->bs.length = (stat_com & 0x7fffff) >> 1;
+
+ /* update sequence number with the help of the counter in stat_com */
+
+ seq = ((stat_com >> 24) + zr->jpg_err_seq) & 0xff;
+ dif = (seq - zr->jpg_seq_num) & 0xff;
+ zr->jpg_seq_num += dif;
+ } else {
+ buffer->bs.length = 0;
+ }
+ buffer->bs.seq =
+ zr->jpg_settings.TmpDcm ==
+ 2 ? (zr->jpg_seq_num >> 1) : zr->jpg_seq_num;
+ buffer->state = BUZ_STATE_DONE;
+
+ zr->jpg_dma_tail++;
+ }
+}
+
+static void
+error_handler (struct zoran *zr,
+ u32 astat,
+ u32 stat)
+{
+ /* This is JPEG error handling part */
+ if ((zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) &&
+ (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS)) {
+ //dprintk(1, KERN_ERR "%s: Internal error: error handling request in mode %d\n", ZR_DEVNAME(zr), zr->codec_mode);
+ return;
+ }
+
+ if ((stat & 1) == 0 &&
+ zr->codec_mode == BUZ_MODE_MOTION_COMPRESS &&
+ zr->jpg_dma_tail - zr->jpg_que_tail >=
+ zr->jpg_buffers.num_buffers) {
+ /* No free buffers... */
+ zoran_reap_stat_com(zr);
+ zoran_feed_stat_com(zr);
+ wake_up_interruptible(&zr->jpg_capq);
+ zr->JPEG_missed = 0;
+ return;
+ }
+
+ if (zr->JPEG_error != 1) {
+ /*
+ * First entry: error just happened during normal operation
+ *
+ * In BUZ_MODE_MOTION_COMPRESS:
+ *
+ * Possible glitch in TV signal. In this case we should
+ * stop the codec and wait for good quality signal before
+ * restarting it to avoid further problems
+ *
+ * In BUZ_MODE_MOTION_DECOMPRESS:
+ *
+ * Bad JPEG frame: we have to mark it as processed (codec crashed
+ * and was not able to do it itself), and to remove it from queue.
+ */
+ btand(~ZR36057_JMC_Go_en, ZR36057_JMC);
+ udelay(1);
+ stat = stat | (post_office_read(zr, 7, 0) & 3) << 8;
+ btwrite(0, ZR36057_JPC);
+ btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR);
+ jpeg_codec_reset(zr);
+ jpeg_codec_sleep(zr, 1);
+ zr->JPEG_error = 1;
+ zr->num_errors++;
+
+ /* Report error */
+ if (*zr_debug > 1 && zr->num_errors <= 8) {
+ long frame;
+ frame =
+ zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME];
+ printk(KERN_ERR
+ "%s: JPEG error stat=0x%08x(0x%08x) queue_state=%ld/%ld/%ld/%ld seq=%ld frame=%ld. Codec stopped. ",
+ ZR_DEVNAME(zr), stat, zr->last_isr,
+ zr->jpg_que_tail, zr->jpg_dma_tail,
+ zr->jpg_dma_head, zr->jpg_que_head,
+ zr->jpg_seq_num, frame);
+ printk("stat_com frames:");
+ {
+ int i, j;
+ for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
+ for (i = 0;
+ i < zr->jpg_buffers.num_buffers;
+ i++) {
+ if (le32_to_cpu(zr->stat_com[j]) ==
+ zr->jpg_buffers.
+ buffer[i].
+ frag_tab_bus) {
+ printk("% d->%d",
+ j, i);
+ }
+ }
+ }
+ printk("\n");
+ }
+ }
+ /* Find an entry in stat_com and rotate contents */
+ {
+ int i;
+
+ if (zr->jpg_settings.TmpDcm == 1)
+ i = (zr->jpg_dma_tail -
+ zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
+ else
+ i = ((zr->jpg_dma_tail -
+ zr->jpg_err_shift) & 1) * 2;
+ if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) {
+ /* Mimic zr36067 operation */
+ zr->stat_com[i] |= cpu_to_le32(1);
+ if (zr->jpg_settings.TmpDcm != 1)
+ zr->stat_com[i + 1] |= cpu_to_le32(1);
+ /* Refill */
+ zoran_reap_stat_com(zr);
+ zoran_feed_stat_com(zr);
+ wake_up_interruptible(&zr->jpg_capq);
+ /* Find an entry in stat_com again after refill */
+ if (zr->jpg_settings.TmpDcm == 1)
+ i = (zr->jpg_dma_tail -
+ zr->jpg_err_shift) &
+ BUZ_MASK_STAT_COM;
+ else
+ i = ((zr->jpg_dma_tail -
+ zr->jpg_err_shift) & 1) * 2;
+ }
+ if (i) {
+ /* Rotate stat_comm entries to make current entry first */
+ int j;
+ u32 bus_addr[BUZ_NUM_STAT_COM];
+
+ /* Here we are copying the stat_com array, which
+ * is already in little endian format, so
+ * no endian conversions here
+ */
+ memcpy(bus_addr, zr->stat_com,
+ sizeof(bus_addr));
+ for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
+ zr->stat_com[j] =
+ bus_addr[(i + j) &
+ BUZ_MASK_STAT_COM];
+
+ }
+ zr->jpg_err_shift += i;
+ zr->jpg_err_shift &= BUZ_MASK_STAT_COM;
+ }
+ if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)
+ zr->jpg_err_seq = zr->jpg_seq_num; /* + 1; */
+ }
+ }
+
+ /* Now the stat_comm buffer is ready for restart */
+ do {
+ int status, mode;
+
+ if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
+ decoder_command(zr, DECODER_GET_STATUS, &status);
+ mode = CODEC_DO_COMPRESSION;
+ } else {
+ status = 0;
+ mode = CODEC_DO_EXPANSION;
+ }
+ if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS ||
+ (status & DECODER_STATUS_GOOD)) {
+ /********** RESTART code *************/
+ jpeg_codec_reset(zr);
+ zr->codec->set_mode(zr->codec, mode);
+ zr36057_set_jpg(zr, zr->codec_mode);
+ jpeg_start(zr);
+
+ if (zr->num_errors <= 8)
+ dprintk(2, KERN_INFO "%s: Restart\n",
+ ZR_DEVNAME(zr));
+
+ zr->JPEG_missed = 0;
+ zr->JPEG_error = 2;
+ /********** End RESTART code ***********/
+ }
+ } while (0);
+}
+
+irqreturn_t
+zoran_irq (int irq,
+ void *dev_id,
+ struct pt_regs *regs)
+{
+ u32 stat, astat;
+ int count;
+ struct zoran *zr;
+ unsigned long flags;
+
+ zr = (struct zoran *) dev_id;
+ count = 0;
+
+ if (zr->testing) {
+ /* Testing interrupts */
+ spin_lock_irqsave(&zr->spinlock, flags);
+ while ((stat = count_reset_interrupt(zr))) {
+ if (count++ > 100) {
+ btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
+ dprintk(1,
+ KERN_ERR
+ "%s: IRQ lockup while testing, isr=0x%08x, cleared int mask\n",
+ ZR_DEVNAME(zr), stat);
+ wake_up_interruptible(&zr->test_q);
+ }
+ }
+ zr->last_isr = stat;
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+ return IRQ_HANDLED;
+ }
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+ while (1) {
+ /* get/clear interrupt status bits */
+ stat = count_reset_interrupt(zr);
+ astat = stat & IRQ_MASK;
+ if (!astat) {
+ break;
+ }
+ dprintk(4,
+ KERN_DEBUG
+ "zoran_irq: astat: 0x%08x, mask: 0x%08x\n",
+ astat, btread(ZR36057_ICR));
+ if (astat & zr->card.vsync_int) { // SW
+
+ if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS ||
+ zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
+ /* count missed interrupts */
+ zr->JPEG_missed++;
+ }
+ //post_office_read(zr,1,0);
+ /* Interrupts may still happen when
+ * zr->v4l_memgrab_active is switched off.
+ * We simply ignore them */
+
+ if (zr->v4l_memgrab_active) {
+
+ /* A lot more checks should be here ... */
+ if ((btread(ZR36057_VSSFGR) &
+ ZR36057_VSSFGR_SnapShot) == 0)
+ dprintk(1,
+ KERN_WARNING
+ "%s: BuzIRQ with SnapShot off ???\n",
+ ZR_DEVNAME(zr));
+
+ if (zr->v4l_grab_frame != NO_GRAB_ACTIVE) {
+ /* There is a grab on a frame going on, check if it has finished */
+
+ if ((btread(ZR36057_VSSFGR) &
+ ZR36057_VSSFGR_FrameGrab) ==
+ 0) {
+ /* it is finished, notify the user */
+
+ zr->v4l_buffers.buffer[zr->v4l_grab_frame].state = BUZ_STATE_DONE;
+ zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.seq = zr->v4l_grab_seq;
+ do_gettimeofday(&zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.timestamp);
+ zr->v4l_grab_frame = NO_GRAB_ACTIVE;
+ zr->v4l_pend_tail++;
+ }
+ }
+
+ if (zr->v4l_grab_frame == NO_GRAB_ACTIVE)
+ wake_up_interruptible(&zr->v4l_capq);
+
+ /* Check if there is another grab queued */
+
+ if (zr->v4l_grab_frame == NO_GRAB_ACTIVE &&
+ zr->v4l_pend_tail != zr->v4l_pend_head) {
+
+ int frame = zr->v4l_pend[zr->v4l_pend_tail &
+ V4L_MASK_FRAME];
+ u32 reg;
+
+ zr->v4l_grab_frame = frame;
+
+ /* Set zr36057 video front end and enable video */
+
+ /* Buffer address */
+
+ reg =
+ zr->v4l_buffers.buffer[frame].
+ fbuffer_bus;
+ btwrite(reg, ZR36057_VDTR);
+ if (zr->v4l_settings.height >
+ BUZ_MAX_HEIGHT / 2)
+ reg +=
+ zr->v4l_settings.
+ bytesperline;
+ btwrite(reg, ZR36057_VDBR);
+
+ /* video stride, status, and frame grab register */
+ reg = 0;
+ if (zr->v4l_settings.height >
+ BUZ_MAX_HEIGHT / 2)
+ reg +=
+ zr->v4l_settings.
+ bytesperline;
+ reg =
+ (reg <<
+ ZR36057_VSSFGR_DispStride);
+ reg |= ZR36057_VSSFGR_VidOvf;
+ reg |= ZR36057_VSSFGR_SnapShot;
+ reg |= ZR36057_VSSFGR_FrameGrab;
+ btwrite(reg, ZR36057_VSSFGR);
+
+ btor(ZR36057_VDCR_VidEn,
+ ZR36057_VDCR);
+ }
+ }
+
+ /* even if we don't grab, we do want to increment
+ * the sequence counter to see lost frames */
+ zr->v4l_grab_seq++;
+ }
+#if (IRQ_MASK & ZR36057_ISR_CodRepIRQ)
+ if (astat & ZR36057_ISR_CodRepIRQ) {
+ zr->intr_counter_CodRepIRQ++;
+ IDEBUG(printk
+ (KERN_DEBUG "%s: ZR36057_ISR_CodRepIRQ\n",
+ ZR_DEVNAME(zr)));
+ btand(~ZR36057_ICR_CodRepIRQ, ZR36057_ICR);
+ }
+#endif /* (IRQ_MASK & ZR36057_ISR_CodRepIRQ) */
+
+#if (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ)
+ if (astat & ZR36057_ISR_JPEGRepIRQ) {
+
+ if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS ||
+ zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
+ if (*zr_debug > 1 &&
+ (!zr->frame_num || zr->JPEG_error)) {
+ printk(KERN_INFO
+ "%s: first frame ready: state=0x%08x odd_even=%d field_per_buff=%d delay=%d\n",
+ ZR_DEVNAME(zr), stat,
+ zr->jpg_settings.odd_even,
+ zr->jpg_settings.
+ field_per_buff,
+ zr->JPEG_missed);
+ {
+ char sc[] = "0000";
+ char sv[5];
+ int i;
+ strcpy(sv, sc);
+ for (i = 0; i < 4; i++) {
+ if (le32_to_cpu(zr->stat_com[i]) & 1)
+ sv[i] = '1';
+ }
+ sv[4] = 0;
+ printk(KERN_INFO
+ "%s: stat_com=%s queue_state=%ld/%ld/%ld/%ld\n",
+ ZR_DEVNAME(zr), sv,
+ zr->jpg_que_tail,
+ zr->jpg_dma_tail,
+ zr->jpg_dma_head,
+ zr->jpg_que_head);
+ }
+ } else {
+ if (zr->JPEG_missed > zr->JPEG_max_missed) // Get statistics
+ zr->JPEG_max_missed =
+ zr->JPEG_missed;
+ if (zr->JPEG_missed <
+ zr->JPEG_min_missed)
+ zr->JPEG_min_missed =
+ zr->JPEG_missed;
+ }
+
+ if (*zr_debug > 2 && zr->frame_num < 6) {
+ int i;
+ printk("%s: seq=%ld stat_com:",
+ ZR_DEVNAME(zr), zr->jpg_seq_num);
+ for (i = 0; i < 4; i++) {
+ printk(" %08x",
+ le32_to_cpu(zr->stat_com[i]));
+ }
+ printk("\n");
+ }
+ zr->frame_num++;
+ zr->JPEG_missed = 0;
+ zr->JPEG_error = 0;
+ zoran_reap_stat_com(zr);
+ zoran_feed_stat_com(zr);
+ wake_up_interruptible(&zr->jpg_capq);
+ } /*else {
+ dprintk(1,
+ KERN_ERR
+ "%s: JPEG interrupt while not in motion (de)compress mode!\n",
+ ZR_DEVNAME(zr));
+ }*/
+ }
+#endif /* (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) */
+
+ /* DATERR, too many fields missed, error processing */
+ if ((astat & zr->card.jpeg_int) ||
+ zr->JPEG_missed > 25 ||
+ zr->JPEG_error == 1 ||
+ ((zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) &&
+ (zr->frame_num & (zr->JPEG_missed >
+ zr->jpg_settings.field_per_buff)))) {
+ error_handler(zr, astat, stat);
+ }
+
+ count++;
+ if (count > 10) {
+ dprintk(2, KERN_WARNING "%s: irq loop %d\n",
+ ZR_DEVNAME(zr), count);
+ if (count > 20) {
+ btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
+ dprintk(2,
+ KERN_ERR
+ "%s: IRQ lockup, cleared int mask\n",
+ ZR_DEVNAME(zr));
+ break;
+ }
+ }
+ zr->last_isr = stat;
+ }
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ return IRQ_HANDLED;
+}
+
+void
+zoran_set_pci_master (struct zoran *zr,
+ int set_master)
+{
+ if (set_master) {
+ pci_set_master(zr->pci_dev);
+ } else {
+ u16 command;
+
+ pci_read_config_word(zr->pci_dev, PCI_COMMAND, &command);
+ command &= ~PCI_COMMAND_MASTER;
+ pci_write_config_word(zr->pci_dev, PCI_COMMAND, command);
+ }
+}
+
+void
+zoran_init_hardware (struct zoran *zr)
+{
+ int j, zero = 0;
+
+ /* Enable bus-mastering */
+ zoran_set_pci_master(zr, 1);
+
+ /* Initialize the board */
+ if (zr->card.init) {
+ zr->card.init(zr);
+ }
+
+ j = zr->card.input[zr->input].muxsel;
+
+ decoder_command(zr, 0, NULL);
+ decoder_command(zr, DECODER_SET_NORM, &zr->norm);
+ decoder_command(zr, DECODER_SET_INPUT, &j);
+
+ encoder_command(zr, 0, NULL);
+ encoder_command(zr, ENCODER_SET_NORM, &zr->norm);
+ encoder_command(zr, ENCODER_SET_INPUT, &zero);
+
+ /* toggle JPEG codec sleep to sync PLL */
+ jpeg_codec_sleep(zr, 1);
+ jpeg_codec_sleep(zr, 0);
+
+ /* set individual interrupt enables (without GIRQ1)
+ * but don't global enable until zoran_open() */
+
+ //btwrite(IRQ_MASK & ~ZR36057_ISR_GIRQ1, ZR36057_ICR); // SW
+ // It looks like using only JPEGRepIRQEn is not always reliable,
+ // may be when JPEG codec crashes it won't generate IRQ? So,
+ /*CP*/ // btwrite(IRQ_MASK, ZR36057_ICR); // Enable Vsync interrupts too. SM WHY ? LP
+ zr36057_init_vfe(zr);
+
+ zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+
+ btwrite(IRQ_MASK, ZR36057_ISR); // Clears interrupts
+}
+
+void
+zr36057_restart (struct zoran *zr)
+{
+ btwrite(0, ZR36057_SPGPPCR);
+ mdelay(1);
+ btor(ZR36057_SPGPPCR_SoftReset, ZR36057_SPGPPCR);
+ mdelay(1);
+
+ /* assert P_Reset */
+ btwrite(0, ZR36057_JPC);
+ /* set up GPIO direction - all output */
+ btwrite(ZR36057_SPGPPCR_SoftReset | 0, ZR36057_SPGPPCR);
+
+ /* set up GPIO pins and guest bus timing */
+ btwrite((0x81 << 24) | 0x8888, ZR36057_GPPGCR1);
+}
+
+/*
+ * initialize video front end
+ */
+
+static void
+zr36057_init_vfe (struct zoran *zr)
+{
+ u32 reg;
+
+ reg = btread(ZR36057_VFESPFR);
+ reg |= ZR36057_VFESPFR_LittleEndian;
+ reg &= ~ZR36057_VFESPFR_VCLKPol;
+ reg |= ZR36057_VFESPFR_ExtFl;
+ reg |= ZR36057_VFESPFR_TopField;
+ btwrite(reg, ZR36057_VFESPFR);
+ reg = btread(ZR36057_VDCR);
+ if (pci_pci_problems & PCIPCI_TRITON)
+ // || zr->revision < 1) // Revision 1 has also Triton support
+ reg &= ~ZR36057_VDCR_Triton;
+ else
+ reg |= ZR36057_VDCR_Triton;
+ btwrite(reg, ZR36057_VDCR);
+}
+
+/*
+ * Interface to decoder and encoder chips using i2c bus
+ */
+
+int
+decoder_command (struct zoran *zr,
+ int cmd,
+ void *data)
+{
+ if (zr->decoder == NULL)
+ return -EIO;
+
+ if (zr->card.type == LML33 &&
+ (cmd == DECODER_SET_NORM || DECODER_SET_INPUT)) {
+ int res;
+
+ // Bt819 needs to reset its FIFO buffer using #FRST pin and
+ // LML33 card uses GPIO(7) for that.
+ GPIO(zr, 7, 0);
+ res = zr->decoder->driver->command(zr->decoder, cmd, data);
+ // Pull #FRST high.
+ GPIO(zr, 7, 1);
+ return res;
+ } else
+ return zr->decoder->driver->command(zr->decoder, cmd,
+ data);
+}
+
+int
+encoder_command (struct zoran *zr,
+ int cmd,
+ void *data)
+{
+ if (zr->encoder == NULL)
+ return -1;
+
+ return zr->encoder->driver->command(zr->encoder, cmd, data);
+}
diff --git a/linux/drivers/media/video/zoran_device.h b/linux/drivers/media/video/zoran_device.h
new file mode 100644
index 000000000..f19705cbd
--- /dev/null
+++ b/linux/drivers/media/video/zoran_device.h
@@ -0,0 +1,91 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * This part handles card-specific data and detection
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Currently maintained by:
+ * Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * Laurent Pinchart <laurent.pinchart@skynet.be>
+ * Mailinglist <mjpeg-users@lists.sf.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ZORAN_DEVICE_H__
+#define __ZORAN_DEVICE_H__
+
+/* general purpose I/O */
+extern void GPIO(struct zoran *zr,
+ int bit,
+ unsigned int value);
+
+/* codec (or actually: guest bus) access */
+extern int post_office_wait(struct zoran *zr);
+extern int post_office_write(struct zoran *zr,
+ unsigned guest,
+ unsigned reg,
+ unsigned value);
+extern int post_office_read(struct zoran *zr,
+ unsigned guest,
+ unsigned reg);
+
+extern void detect_guest_activity(struct zoran *zr);
+
+extern void jpeg_codec_sleep(struct zoran *zr,
+ int sleep);
+extern int jpeg_codec_reset(struct zoran *zr);
+
+/* zr360x7 access to raw capture */
+extern void zr36057_overlay(struct zoran *zr,
+ int on);
+extern void write_overlay_mask(struct file *file,
+ struct video_clip *vp,
+ int count);
+extern void zr36057_set_memgrab(struct zoran *zr,
+ int mode);
+extern int wait_grab_pending(struct zoran *zr);
+
+/* interrupts */
+extern void print_interrupts(struct zoran *zr);
+extern void clear_interrupt_counters(struct zoran *zr);
+extern irqreturn_t zoran_irq(int irq,
+ void *dev_id,
+ struct pt_regs *regs);
+
+/* JPEG codec access */
+extern void jpeg_start(struct zoran *zr);
+extern void zr36057_enable_jpg(struct zoran *zr,
+ enum zoran_codec_mode mode);
+extern void zoran_feed_stat_com(struct zoran *zr);
+
+/* general */
+extern void zoran_set_pci_master(struct zoran *zr,
+ int set_master);
+extern void zoran_init_hardware(struct zoran *zr);
+extern void zr36057_restart(struct zoran *zr);
+
+/* i2c */
+extern int decoder_command(struct zoran *zr,
+ int cmd,
+ void *data);
+extern int encoder_command(struct zoran *zr,
+ int cmd,
+ void *data);
+
+#endif /* __ZORAN_DEVICE_H__ */
diff --git a/linux/drivers/media/video/zoran_driver.c b/linux/drivers/media/video/zoran_driver.c
new file mode 100644
index 000000000..b5a576a37
--- /dev/null
+++ b/linux/drivers/media/video/zoran_driver.c
@@ -0,0 +1,4700 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Changes for BUZ by Wolfgang Scherr <scherr@net4you.net>
+ *
+ * Changes for DC10/DC30 by Laurent Pinchart <laurent.pinchart@skynet.be>
+ *
+ * Changes for LML33R10 by Maxim Yevtyushkin <max@linuxmedialabs.com>
+ *
+ * Changes for videodev2/v4l2 by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * Based on
+ *
+ * Miro DC10 driver
+ * Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net>
+ *
+ * Iomega Buz driver version 1.0
+ * Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de>
+ *
+ * buz.0.0.3
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ *
+ * bttv - Bt848 frame grabber driver
+ * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ * & Marcus Metzler (mocm@thp.uni-koeln.de)
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+#include <linux/byteorder/generic.h>
+
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+
+#include <linux/spinlock.h>
+#define MAP_NR(x) virt_to_page(x)
+#define ZORAN_HARDWARE VID_HARDWARE_ZR36067
+#define ZORAN_VID_TYPE ( \
+ VID_TYPE_CAPTURE | \
+ VID_TYPE_OVERLAY | \
+ VID_TYPE_CLIPPING | \
+ VID_TYPE_FRAMERAM | \
+ VID_TYPE_SCALES | \
+ VID_TYPE_MJPEG_DECODER | \
+ VID_TYPE_MJPEG_ENCODER \
+ )
+
+#include <linux/videodev.h>
+#include "videocodec.h"
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/proc_fs.h>
+
+#include <linux/video_decoder.h>
+#include <linux/video_encoder.h>
+#include <linux/mutex.h>
+#include "zoran.h"
+#include "zoran_device.h"
+#include "zoran_card.h"
+
+#ifdef HAVE_V4L2
+ /* we declare some card type definitions here, they mean
+ * the same as the v4l1 ZORAN_VID_TYPE above, except it's v4l2 */
+#define ZORAN_V4L2_VID_FLAGS ( \
+ V4L2_CAP_STREAMING |\
+ V4L2_CAP_VIDEO_CAPTURE |\
+ V4L2_CAP_VIDEO_OUTPUT |\
+ V4L2_CAP_VIDEO_OVERLAY \
+ )
+#endif
+
+#include <asm/byteorder.h>
+
+const struct zoran_format zoran_formats[] = {
+ {
+ .name = "15-bit RGB",
+ .palette = VIDEO_PALETTE_RGB555,
+#ifdef HAVE_V4L2
+#ifdef __LITTLE_ENDIAN
+ .fourcc = V4L2_PIX_FMT_RGB555,
+#else
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+#endif
+ .colorspace = V4L2_COLORSPACE_SRGB,
+#endif
+ .depth = 15,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ }, {
+ .name = "16-bit RGB",
+ .palette = VIDEO_PALETTE_RGB565,
+#ifdef HAVE_V4L2
+#ifdef __LITTLE_ENDIAN
+ .fourcc = V4L2_PIX_FMT_RGB565,
+#else
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+#endif
+ .colorspace = V4L2_COLORSPACE_SRGB,
+#endif
+ .depth = 16,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ }, {
+ .name = "24-bit RGB",
+ .palette = VIDEO_PALETTE_RGB24,
+#ifdef HAVE_V4L2
+#ifdef __LITTLE_ENDIAN
+ .fourcc = V4L2_PIX_FMT_BGR24,
+#else
+ .fourcc = V4L2_PIX_FMT_RGB24,
+#endif
+ .colorspace = V4L2_COLORSPACE_SRGB,
+#endif
+ .depth = 24,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ }, {
+ .name = "32-bit RGB",
+ .palette = VIDEO_PALETTE_RGB32,
+#ifdef HAVE_V4L2
+#ifdef __LITTLE_ENDIAN
+ .fourcc = V4L2_PIX_FMT_BGR32,
+#else
+ .fourcc = V4L2_PIX_FMT_RGB32,
+#endif
+ .colorspace = V4L2_COLORSPACE_SRGB,
+#endif
+ .depth = 32,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ }, {
+ .name = "4:2:2, packed, YUYV",
+ .palette = VIDEO_PALETTE_YUV422,
+#ifdef HAVE_V4L2
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+#endif
+ .depth = 16,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ }, {
+ .name = "Hardware-encoded Motion-JPEG",
+ .palette = -1,
+#ifdef HAVE_V4L2
+ .fourcc = V4L2_PIX_FMT_MJPEG,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+#endif
+ .depth = 0,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_PLAYBACK |
+ ZORAN_FORMAT_COMPRESSED,
+ }
+};
+static const int zoran_num_formats =
+ (sizeof(zoran_formats) / sizeof(struct zoran_format));
+
+// RJ: Test only - want to test BUZ_USE_HIMEM even when CONFIG_BIGPHYS_AREA is defined
+#if !defined(CONFIG_BIGPHYS_AREA)
+//#undef CONFIG_BIGPHYS_AREA
+#define BUZ_USE_HIMEM
+#endif
+
+#if defined(CONFIG_BIGPHYS_AREA)
+# include <linux/bigphysarea.h>
+#endif
+
+extern int *zr_debug;
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (*zr_debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+extern int v4l_nbufs;
+extern int v4l_bufsize;
+extern int jpg_nbufs;
+extern int jpg_bufsize;
+extern int pass_through;
+
+static int lock_norm = 0; /* 1=Don't change TV standard (norm) */
+module_param(lock_norm, int, 0);
+MODULE_PARM_DESC(lock_norm, "Users can't change norm");
+
+#ifdef HAVE_V4L2
+ /* small helper function for calculating buffersizes for v4l2
+ * we calculate the nearest higher power-of-two, which
+ * will be the recommended buffersize */
+static __u32
+zoran_v4l2_calc_bufsize (struct zoran_jpg_settings *settings)
+{
+ __u8 div = settings->VerDcm * settings->HorDcm * settings->TmpDcm;
+ __u32 num = (1024 * 512) / (div);
+ __u32 result = 2;
+
+ num--;
+ while (num) {
+ num >>= 1;
+ result <<= 1;
+ }
+
+ if (result > jpg_bufsize)
+ return jpg_bufsize;
+ if (result < 8192)
+ return 8192;
+ return result;
+}
+#endif
+
+/* forward references */
+static void v4l_fbuffer_free(struct file *file);
+static void jpg_fbuffer_free(struct file *file);
+
+/*
+ * Allocate the V4L grab buffers
+ *
+ * These have to be pysically contiguous.
+ * If v4l_bufsize <= MAX_KMALLOC_MEM we use kmalloc
+ * else we try to allocate them with bigphysarea_alloc_pages
+ * if the bigphysarea patch is present in the kernel,
+ * else we try to use high memory (if the user has bootet
+ * Linux with the necessary memory left over).
+ */
+
+#if defined(BUZ_USE_HIMEM) && !defined(CONFIG_BIGPHYS_AREA)
+static unsigned long
+get_high_mem (unsigned long size)
+{
+/*
+ * Check if there is usable memory at the end of Linux memory
+ * of at least size. Return the physical address of this memory,
+ * return 0 on failure.
+ *
+ * The idea is from Alexandro Rubini's book "Linux device drivers".
+ * The driver from him which is downloadable from O'Reilly's
+ * web site misses the "virt_to_phys(high_memory)" part
+ * (and therefore doesn't work at all - at least with 2.2.x kernels).
+ *
+ * It should be unnecessary to mention that THIS IS DANGEROUS,
+ * if more than one driver at a time has the idea to use this memory!!!!
+ */
+
+ volatile unsigned char __iomem *mem;
+ unsigned char c;
+ unsigned long hi_mem_ph;
+ unsigned long i;
+
+ /* Map the high memory to user space */
+
+ hi_mem_ph = virt_to_phys(high_memory);
+
+ mem = ioremap(hi_mem_ph, size);
+ if (!mem) {
+ dprintk(1,
+ KERN_ERR "%s: get_high_mem() - ioremap failed\n",
+ ZORAN_NAME);
+ return 0;
+ }
+
+ for (i = 0; i < size; i++) {
+ /* Check if it is memory */
+ c = i & 0xff;
+ writeb(c, mem + i);
+ if (readb(mem + i) != c)
+ break;
+ c = 255 - c;
+ writeb(c, mem + i);
+ if (readb(mem + i) != c)
+ break;
+ writeb(0, mem + i); /* zero out memory */
+
+ /* give the kernel air to breath */
+ if ((i & 0x3ffff) == 0x3ffff)
+ schedule();
+ }
+
+ iounmap(mem);
+
+ if (i != size) {
+ dprintk(1,
+ KERN_ERR
+ "%s: get_high_mem() - requested %lu, avail %lu\n",
+ ZORAN_NAME, size, i);
+ return 0;
+ }
+
+ return hi_mem_ph;
+}
+#endif
+
+static int
+v4l_fbuffer_alloc (struct file *file)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ int i, off;
+ unsigned char *mem;
+#if defined(BUZ_USE_HIMEM) && !defined(CONFIG_BIGPHYS_AREA)
+ unsigned long pmem = 0;
+#endif
+
+ /* we might have old buffers lying around... */
+ if (fh->v4l_buffers.ready_to_be_freed) {
+ v4l_fbuffer_free(file);
+ }
+
+ for (i = 0; i < fh->v4l_buffers.num_buffers; i++) {
+ if (fh->v4l_buffers.buffer[i].fbuffer)
+ dprintk(2,
+ KERN_WARNING
+ "%s: v4l_fbuffer_alloc() - buffer %d allready allocated!?\n",
+ ZR_DEVNAME(zr), i);
+
+ //udelay(20);
+ if (fh->v4l_buffers.buffer_size <= MAX_KMALLOC_MEM) {
+ /* Use kmalloc */
+
+ mem =
+ (unsigned char *) kmalloc(fh->v4l_buffers.
+ buffer_size,
+ GFP_KERNEL);
+ if (mem == 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: v4l_fbuffer_alloc() - kmalloc for V4L buf %d failed\n",
+ ZR_DEVNAME(zr), i);
+ v4l_fbuffer_free(file);
+ return -ENOBUFS;
+ }
+ fh->v4l_buffers.buffer[i].fbuffer = mem;
+ fh->v4l_buffers.buffer[i].fbuffer_phys =
+ virt_to_phys(mem);
+ fh->v4l_buffers.buffer[i].fbuffer_bus =
+ virt_to_bus(mem);
+ for (off = 0; off < fh->v4l_buffers.buffer_size;
+ off += PAGE_SIZE)
+ SetPageReserved(MAP_NR(mem + off));
+ dprintk(4,
+ KERN_INFO
+ "%s: v4l_fbuffer_alloc() - V4L frame %d mem 0x%lx (bus: 0x%lx)\n",
+ ZR_DEVNAME(zr), i, (unsigned long) mem,
+ virt_to_bus(mem));
+ } else {
+#if defined(CONFIG_BIGPHYS_AREA)
+ /* Use bigphysarea_alloc_pages */
+
+ int n =
+ (fh->v4l_buffers.buffer_size + PAGE_SIZE -
+ 1) / PAGE_SIZE;
+
+ mem =
+ (unsigned char *) bigphysarea_alloc_pages(n, 0,
+ GFP_KERNEL);
+ if (mem == 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: v4l_fbuffer_alloc() - bigphysarea_alloc_pages for V4L buf %d failed\n",
+ ZR_DEVNAME(zr), i);
+ v4l_fbuffer_free(file);
+ return -ENOBUFS;
+ }
+ fh->v4l_buffers.buffer[i].fbuffer = mem;
+ fh->v4l_buffers.buffer[i].fbuffer_phys =
+ virt_to_phys(mem);
+ fh->v4l_buffers.buffer[i].fbuffer_bus =
+ virt_to_bus(mem);
+ dprintk(4,
+ KERN_INFO
+ "%s: Bigphysarea frame %d mem 0x%x (bus: 0x%x)\n",
+ ZR_DEVNAME(zr), i, (unsigned) mem,
+ (unsigned) virt_to_bus(mem));
+
+ /* Zero out the allocated memory */
+ memset(fh->v4l_buffers.buffer[i].fbuffer, 0,
+ fh->v4l_buffers.buffer_size);
+#elif defined(BUZ_USE_HIMEM)
+
+ /* Use high memory which has been left at boot time */
+
+ /* Ok., Ok. this is an evil hack - we make
+ * the assumption that physical addresses are
+ * the same as bus addresses (true at least
+ * for Intel processors). The whole method of
+ * obtaining and using this memory is not very
+ * nice - but I hope it saves some poor users
+ * from kernel hacking, which might have even
+ * more evil results */
+
+ if (i == 0) {
+ int size =
+ fh->v4l_buffers.num_buffers *
+ fh->v4l_buffers.buffer_size;
+
+ pmem = get_high_mem(size);
+ if (pmem == 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: v4l_fbuffer_alloc() - get_high_mem (size = %d KB) for V4L bufs failed\n",
+ ZR_DEVNAME(zr), size >> 10);
+ return -ENOBUFS;
+ }
+ fh->v4l_buffers.buffer[0].fbuffer = NULL;
+ fh->v4l_buffers.buffer[0].fbuffer_phys = pmem;
+ fh->v4l_buffers.buffer[0].fbuffer_bus = pmem;
+ dprintk(4,
+ KERN_INFO
+ "%s: v4l_fbuffer_alloc() - using %d KB high memory\n",
+ ZR_DEVNAME(zr), size >> 10);
+ } else {
+ fh->v4l_buffers.buffer[i].fbuffer = NULL;
+ fh->v4l_buffers.buffer[i].fbuffer_phys =
+ pmem + i * fh->v4l_buffers.buffer_size;
+ fh->v4l_buffers.buffer[i].fbuffer_bus =
+ pmem + i * fh->v4l_buffers.buffer_size;
+ }
+#else
+ /* No bigphysarea present, usage of high memory disabled,
+ * but user wants buffers of more than MAX_KMALLOC_MEM */
+ dprintk(1,
+ KERN_ERR
+ "%s: v4l_fbuffer_alloc() - no bigphysarea_patch present, usage of high memory disabled,\n",
+ ZR_DEVNAME(zr));
+ dprintk(1,
+ KERN_ERR
+ "%s: v4l_fbuffer_alloc() - sorry, could not allocate %d V4L buffers of size %d KB.\n",
+ ZR_DEVNAME(zr), fh->v4l_buffers.num_buffers,
+ fh->v4l_buffers.buffer_size >> 10);
+ return -ENOBUFS;
+#endif
+ }
+ }
+
+ fh->v4l_buffers.allocated = 1;
+
+ return 0;
+}
+
+/* free the V4L grab buffers */
+static void
+v4l_fbuffer_free (struct file *file)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ int i, off;
+ unsigned char *mem;
+
+ dprintk(4, KERN_INFO "%s: v4l_fbuffer_free()\n", ZR_DEVNAME(zr));
+
+ for (i = 0; i < fh->v4l_buffers.num_buffers; i++) {
+ if (!fh->v4l_buffers.buffer[i].fbuffer)
+ continue;
+
+ if (fh->v4l_buffers.buffer_size <= MAX_KMALLOC_MEM) {
+ mem = fh->v4l_buffers.buffer[i].fbuffer;
+ for (off = 0; off < fh->v4l_buffers.buffer_size;
+ off += PAGE_SIZE)
+ ClearPageReserved(MAP_NR(mem + off));
+ kfree((void *) fh->v4l_buffers.buffer[i].fbuffer);
+ }
+#if defined(CONFIG_BIGPHYS_AREA)
+ else
+ bigphysarea_free_pages((void *) fh->v4l_buffers.
+ buffer[i].fbuffer);
+#endif
+ fh->v4l_buffers.buffer[i].fbuffer = NULL;
+ }
+
+ fh->v4l_buffers.allocated = 0;
+ fh->v4l_buffers.ready_to_be_freed = 0;
+}
+
+/*
+ * Allocate the MJPEG grab buffers.
+ *
+ * If the requested buffer size is smaller than MAX_KMALLOC_MEM,
+ * kmalloc is used to request a physically contiguous area,
+ * else we allocate the memory in framgents with get_zeroed_page.
+ *
+ * If a Natoma chipset is present and this is a revision 1 zr36057,
+ * each MJPEG buffer needs to be physically contiguous.
+ * (RJ: This statement is from Dave Perks' original driver,
+ * I could never check it because I have a zr36067)
+ * The driver cares about this because it reduces the buffer
+ * size to MAX_KMALLOC_MEM in that case (which forces contiguous allocation).
+ *
+ * RJ: The contents grab buffers needs never be accessed in the driver.
+ * Therefore there is no need to allocate them with vmalloc in order
+ * to get a contiguous virtual memory space.
+ * I don't understand why many other drivers first allocate them with
+ * vmalloc (which uses internally also get_zeroed_page, but delivers you
+ * virtual addresses) and then again have to make a lot of efforts
+ * to get the physical address.
+ *
+ * Ben Capper:
+ * On big-endian architectures (such as ppc) some extra steps
+ * are needed. When reading and writing to the stat_com array
+ * and fragment buffers, the device expects to see little-
+ * endian values. The use of cpu_to_le32() and le32_to_cpu()
+ * in this function (and one or two others in zoran_device.c)
+ * ensure that these values are always stored in little-endian
+ * form, regardless of architecture. The zr36057 does Very Bad
+ * Things on big endian architectures if the stat_com array
+ * and fragment buffers are not little-endian.
+ */
+
+static int
+jpg_fbuffer_alloc (struct file *file)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ int i, j, off;
+ unsigned long mem;
+
+ /* we might have old buffers lying around */
+ if (fh->jpg_buffers.ready_to_be_freed) {
+ jpg_fbuffer_free(file);
+ }
+
+ for (i = 0; i < fh->jpg_buffers.num_buffers; i++) {
+ if (fh->jpg_buffers.buffer[i].frag_tab)
+ dprintk(2,
+ KERN_WARNING
+ "%s: jpg_fbuffer_alloc() - buffer %d allready allocated!?\n",
+ ZR_DEVNAME(zr), i);
+
+ /* Allocate fragment table for this buffer */
+
+ mem = get_zeroed_page(GFP_KERNEL);
+ if (mem == 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: jpg_fbuffer_alloc() - get_zeroed_page (frag_tab) failed for buffer %d\n",
+ ZR_DEVNAME(zr), i);
+ jpg_fbuffer_free(file);
+ return -ENOBUFS;
+ }
+ memset((void *) mem, 0, PAGE_SIZE);
+ fh->jpg_buffers.buffer[i].frag_tab = (u32 *) mem;
+ fh->jpg_buffers.buffer[i].frag_tab_bus =
+ virt_to_bus((void *) mem);
+
+ //if (alloc_contig) {
+ if (fh->jpg_buffers.need_contiguous) {
+ mem =
+ (unsigned long) kmalloc(fh->jpg_buffers.
+ buffer_size,
+ GFP_KERNEL);
+ if (mem == 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: jpg_fbuffer_alloc() - kmalloc failed for buffer %d\n",
+ ZR_DEVNAME(zr), i);
+ jpg_fbuffer_free(file);
+ return -ENOBUFS;
+ }
+ fh->jpg_buffers.buffer[i].frag_tab[0] =
+ cpu_to_le32(virt_to_bus((void *) mem));
+ fh->jpg_buffers.buffer[i].frag_tab[1] =
+ cpu_to_le32(((fh->jpg_buffers.buffer_size / 4) << 1) | 1);
+ for (off = 0; off < fh->jpg_buffers.buffer_size;
+ off += PAGE_SIZE)
+ SetPageReserved(MAP_NR(mem + off));
+ } else {
+ /* jpg_bufsize is allreay page aligned */
+ for (j = 0;
+ j < fh->jpg_buffers.buffer_size / PAGE_SIZE;
+ j++) {
+ mem = get_zeroed_page(GFP_KERNEL);
+ if (mem == 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: jpg_fbuffer_alloc() - get_zeroed_page failed for buffer %d\n",
+ ZR_DEVNAME(zr), i);
+ jpg_fbuffer_free(file);
+ return -ENOBUFS;
+ }
+
+ fh->jpg_buffers.buffer[i].frag_tab[2 * j] =
+ cpu_to_le32(virt_to_bus((void *) mem));
+ fh->jpg_buffers.buffer[i].frag_tab[2 * j +
+ 1] =
+ cpu_to_le32((PAGE_SIZE / 4) << 1);
+ SetPageReserved(MAP_NR(mem));
+ }
+
+ fh->jpg_buffers.buffer[i].frag_tab[2 * j - 1] |= cpu_to_le32(1);
+ }
+ }
+
+ dprintk(4,
+ KERN_DEBUG "%s: jpg_fbuffer_alloc() - %d KB allocated\n",
+ ZR_DEVNAME(zr),
+ (fh->jpg_buffers.num_buffers *
+ fh->jpg_buffers.buffer_size) >> 10);
+
+ fh->jpg_buffers.allocated = 1;
+
+ return 0;
+}
+
+/* free the MJPEG grab buffers */
+static void
+jpg_fbuffer_free (struct file *file)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ int i, j, off;
+ unsigned char *mem;
+
+ dprintk(4, KERN_DEBUG "%s: jpg_fbuffer_free()\n", ZR_DEVNAME(zr));
+
+ for (i = 0; i < fh->jpg_buffers.num_buffers; i++) {
+ if (!fh->jpg_buffers.buffer[i].frag_tab)
+ continue;
+
+ //if (alloc_contig) {
+ if (fh->jpg_buffers.need_contiguous) {
+ if (fh->jpg_buffers.buffer[i].frag_tab[0]) {
+ mem = (unsigned char *) bus_to_virt(le32_to_cpu(
+ fh->jpg_buffers.buffer[i].frag_tab[0]));
+ for (off = 0;
+ off < fh->jpg_buffers.buffer_size;
+ off += PAGE_SIZE)
+ ClearPageReserved(MAP_NR
+ (mem + off));
+ kfree(mem);
+ fh->jpg_buffers.buffer[i].frag_tab[0] = 0;
+ fh->jpg_buffers.buffer[i].frag_tab[1] = 0;
+ }
+ } else {
+ for (j = 0;
+ j < fh->jpg_buffers.buffer_size / PAGE_SIZE;
+ j++) {
+ if (!fh->jpg_buffers.buffer[i].
+ frag_tab[2 * j])
+ break;
+ ClearPageReserved(MAP_NR
+ (bus_to_virt
+ (le32_to_cpu
+ (fh->jpg_buffers.
+ buffer[i].frag_tab[2 *
+ j]))));
+ free_page((unsigned long)
+ bus_to_virt
+ (le32_to_cpu
+ (fh->jpg_buffers.
+ buffer[i].
+ frag_tab[2 * j])));
+ fh->jpg_buffers.buffer[i].frag_tab[2 * j] =
+ 0;
+ fh->jpg_buffers.buffer[i].frag_tab[2 * j +
+ 1] = 0;
+ }
+ }
+
+ free_page((unsigned long) fh->jpg_buffers.buffer[i].
+ frag_tab);
+ fh->jpg_buffers.buffer[i].frag_tab = NULL;
+ }
+
+ fh->jpg_buffers.allocated = 0;
+ fh->jpg_buffers.ready_to_be_freed = 0;
+}
+
+/*
+ * V4L Buffer grabbing
+ */
+
+static int
+zoran_v4l_set_format (struct file *file,
+ int width,
+ int height,
+ const struct zoran_format *format)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ int bpp;
+
+ /* Check size and format of the grab wanted */
+
+ if (height < BUZ_MIN_HEIGHT || width < BUZ_MIN_WIDTH ||
+ height > BUZ_MAX_HEIGHT || width > BUZ_MAX_WIDTH) {
+ dprintk(1,
+ KERN_ERR
+ "%s: v4l_set_format() - wrong frame size (%dx%d)\n",
+ ZR_DEVNAME(zr), width, height);
+ return -EINVAL;
+ }
+
+ bpp = (format->depth + 7) / 8;
+
+ /* Check against available buffer size */
+ if (height * width * bpp > fh->v4l_buffers.buffer_size) {
+ dprintk(1,
+ KERN_ERR
+ "%s: v4l_set_format() - video buffer size (%d kB) is too small\n",
+ ZR_DEVNAME(zr), fh->v4l_buffers.buffer_size >> 10);
+ return -EINVAL;
+ }
+
+ /* The video front end needs 4-byte alinged line sizes */
+
+ if ((bpp == 2 && (width & 1)) || (bpp == 3 && (width & 3))) {
+ dprintk(1,
+ KERN_ERR
+ "%s: v4l_set_format() - wrong frame alingment\n",
+ ZR_DEVNAME(zr));
+ return -EINVAL;
+ }
+
+ fh->v4l_settings.width = width;
+ fh->v4l_settings.height = height;
+ fh->v4l_settings.format = format;
+ fh->v4l_settings.bytesperline = bpp * fh->v4l_settings.width;
+
+ return 0;
+}
+
+static int
+zoran_v4l_queue_frame (struct file *file,
+ int num)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ unsigned long flags;
+ int res = 0;
+
+ if (!fh->v4l_buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+ "%s: v4l_queue_frame() - buffers not yet allocated\n",
+ ZR_DEVNAME(zr));
+ res = -ENOMEM;
+ }
+
+ /* No grabbing outside the buffer range! */
+ if (num >= fh->v4l_buffers.num_buffers || num < 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: v4l_queue_frame() - buffer %d is out of range\n",
+ ZR_DEVNAME(zr), num);
+ res = -EINVAL;
+ }
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+
+ if (fh->v4l_buffers.active == ZORAN_FREE) {
+ if (zr->v4l_buffers.active == ZORAN_FREE) {
+ zr->v4l_buffers = fh->v4l_buffers;
+ fh->v4l_buffers.active = ZORAN_ACTIVE;
+ } else {
+ dprintk(1,
+ KERN_ERR
+ "%s: v4l_queue_frame() - another session is already capturing\n",
+ ZR_DEVNAME(zr));
+ res = -EBUSY;
+ }
+ }
+
+ /* make sure a grab isn't going on currently with this buffer */
+ if (!res) {
+ switch (zr->v4l_buffers.buffer[num].state) {
+ default:
+ case BUZ_STATE_PEND:
+ if (zr->v4l_buffers.active == ZORAN_FREE) {
+ fh->v4l_buffers.active = ZORAN_FREE;
+ zr->v4l_buffers.allocated = 0;
+ }
+ res = -EBUSY; /* what are you doing? */
+ break;
+ case BUZ_STATE_DONE:
+ dprintk(2,
+ KERN_WARNING
+ "%s: v4l_queue_frame() - queueing buffer %d in state DONE!?\n",
+ ZR_DEVNAME(zr), num);
+ case BUZ_STATE_USER:
+ /* since there is at least one unused buffer there's room for at least
+ * one more pend[] entry */
+ zr->v4l_pend[zr->v4l_pend_head++ &
+ V4L_MASK_FRAME] = num;
+ zr->v4l_buffers.buffer[num].state = BUZ_STATE_PEND;
+ zr->v4l_buffers.buffer[num].bs.length =
+ fh->v4l_settings.bytesperline *
+ zr->v4l_settings.height;
+ fh->v4l_buffers.buffer[num] =
+ zr->v4l_buffers.buffer[num];
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ if (!res && zr->v4l_buffers.active == ZORAN_FREE)
+ zr->v4l_buffers.active = fh->v4l_buffers.active;
+
+ return res;
+}
+
+static int
+v4l_grab (struct file *file,
+ struct video_mmap *mp)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ int res = 0, i;
+
+ for (i = 0; i < zoran_num_formats; i++) {
+ if (zoran_formats[i].palette == mp->format &&
+ zoran_formats[i].flags & ZORAN_FORMAT_CAPTURE &&
+ !(zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED))
+ break;
+ }
+ if (i == zoran_num_formats || zoran_formats[i].depth == 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: v4l_grab() - wrong bytes-per-pixel format\n",
+ ZR_DEVNAME(zr));
+ return -EINVAL;
+ }
+
+ /*
+ * To minimize the time spent in the IRQ routine, we avoid setting up
+ * the video front end there.
+ * If this grab has different parameters from a running streaming capture
+ * we stop the streaming capture and start it over again.
+ */
+ if (zr->v4l_memgrab_active &&
+ (zr->v4l_settings.width != mp->width ||
+ zr->v4l_settings.height != mp->height ||
+ zr->v4l_settings.format->palette != mp->format)) {
+ res = wait_grab_pending(zr);
+ if (res)
+ return res;
+ }
+ if ((res = zoran_v4l_set_format(file,
+ mp->width,
+ mp->height,
+ &zoran_formats[i])))
+ return res;
+ zr->v4l_settings = fh->v4l_settings;
+
+ /* queue the frame in the pending queue */
+ if ((res = zoran_v4l_queue_frame(file, mp->frame))) {
+ fh->v4l_buffers.active = ZORAN_FREE;
+ return res;
+ }
+
+ /* put the 36057 into frame grabbing mode */
+ if (!res && !zr->v4l_memgrab_active)
+ zr36057_set_memgrab(zr, 1);
+
+ //dprintk(4, KERN_INFO "%s: Frame grab 3...\n", ZR_DEVNAME(zr));
+
+ return res;
+}
+
+/*
+ * Sync on a V4L buffer
+ */
+
+static int
+v4l_sync (struct file *file,
+ int frame)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ unsigned long flags;
+
+ if (fh->v4l_buffers.active == ZORAN_FREE) {
+ dprintk(1,
+ KERN_ERR
+ "%s: v4l_sync() - no grab active for this session\n",
+ ZR_DEVNAME(zr));
+ return -EINVAL;
+ }
+
+ /* check passed-in frame number */
+ if (frame >= fh->v4l_buffers.num_buffers || frame < 0) {
+ dprintk(1,
+ KERN_ERR "%s: v4l_sync() - frame %d is invalid\n",
+ ZR_DEVNAME(zr), frame);
+ return -EINVAL;
+ }
+
+ /* Check if is buffer was queued at all */
+ if (zr->v4l_buffers.buffer[frame].state == BUZ_STATE_USER) {
+ dprintk(1,
+ KERN_ERR
+ "%s: v4l_sync() - attempt to sync on a buffer which was not queued?\n",
+ ZR_DEVNAME(zr));
+ return -EPROTO;
+ }
+
+ /* wait on this buffer to get ready */
+ if (!wait_event_interruptible_timeout(zr->v4l_capq,
+ (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_PEND),
+ 10*HZ))
+ return -ETIME;
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+
+ /* buffer should now be in BUZ_STATE_DONE */
+ if (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_DONE)
+ dprintk(2,
+ KERN_ERR "%s: v4l_sync() - internal state error\n",
+ ZR_DEVNAME(zr));
+
+ zr->v4l_buffers.buffer[frame].state = BUZ_STATE_USER;
+ fh->v4l_buffers.buffer[frame] = zr->v4l_buffers.buffer[frame];
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+
+ /* Check if streaming capture has finished */
+ if (zr->v4l_pend_tail == zr->v4l_pend_head) {
+ zr36057_set_memgrab(zr, 0);
+ if (zr->v4l_buffers.active == ZORAN_ACTIVE) {
+ fh->v4l_buffers.active = zr->v4l_buffers.active =
+ ZORAN_FREE;
+ zr->v4l_buffers.allocated = 0;
+ }
+ }
+
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ return 0;
+}
+
+/*
+ * Queue a MJPEG buffer for capture/playback
+ */
+
+static int
+zoran_jpg_queue_frame (struct file *file,
+ int num,
+ enum zoran_codec_mode mode)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ unsigned long flags;
+ int res = 0;
+
+ /* Check if buffers are allocated */
+ if (!fh->jpg_buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+ "%s: jpg_queue_frame() - buffers not yet allocated\n",
+ ZR_DEVNAME(zr));
+ return -ENOMEM;
+ }
+
+ /* No grabbing outside the buffer range! */
+ if (num >= fh->jpg_buffers.num_buffers || num < 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: jpg_queue_frame() - buffer %d out of range\n",
+ ZR_DEVNAME(zr), num);
+ return -EINVAL;
+ }
+
+ /* what is the codec mode right now? */
+ if (zr->codec_mode == BUZ_MODE_IDLE) {
+ zr->jpg_settings = fh->jpg_settings;
+ } else if (zr->codec_mode != mode) {
+ /* wrong codec mode active - invalid */
+ dprintk(1,
+ KERN_ERR
+ "%s: jpg_queue_frame() - codec in wrong mode\n",
+ ZR_DEVNAME(zr));
+ return -EINVAL;
+ }
+
+ if (fh->jpg_buffers.active == ZORAN_FREE) {
+ if (zr->jpg_buffers.active == ZORAN_FREE) {
+ zr->jpg_buffers = fh->jpg_buffers;
+ fh->jpg_buffers.active = ZORAN_ACTIVE;
+ } else {
+ dprintk(1,
+ KERN_ERR
+ "%s: jpg_queue_frame() - another session is already capturing\n",
+ ZR_DEVNAME(zr));
+ res = -EBUSY;
+ }
+ }
+
+ if (!res && zr->codec_mode == BUZ_MODE_IDLE) {
+ /* Ok load up the jpeg codec */
+ zr36057_enable_jpg(zr, mode);
+ }
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+
+ if (!res) {
+ switch (zr->jpg_buffers.buffer[num].state) {
+ case BUZ_STATE_DONE:
+ dprintk(2,
+ KERN_WARNING
+ "%s: jpg_queue_frame() - queing frame in BUZ_STATE_DONE state!?\n",
+ ZR_DEVNAME(zr));
+ case BUZ_STATE_USER:
+ /* since there is at least one unused buffer there's room for at
+ *least one more pend[] entry */
+ zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] =
+ num;
+ zr->jpg_buffers.buffer[num].state = BUZ_STATE_PEND;
+ fh->jpg_buffers.buffer[num] =
+ zr->jpg_buffers.buffer[num];
+ zoran_feed_stat_com(zr);
+ break;
+ default:
+ case BUZ_STATE_DMA:
+ case BUZ_STATE_PEND:
+ if (zr->jpg_buffers.active == ZORAN_FREE) {
+ fh->jpg_buffers.active = ZORAN_FREE;
+ zr->jpg_buffers.allocated = 0;
+ }
+ res = -EBUSY; /* what are you doing? */
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ if (!res && zr->jpg_buffers.active == ZORAN_FREE) {
+ zr->jpg_buffers.active = fh->jpg_buffers.active;
+ }
+
+ return res;
+}
+
+static int
+jpg_qbuf (struct file *file,
+ int frame,
+ enum zoran_codec_mode mode)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ int res = 0;
+
+ /* Does the user want to stop streaming? */
+ if (frame < 0) {
+ if (zr->codec_mode == mode) {
+ if (fh->jpg_buffers.active == ZORAN_FREE) {
+ dprintk(1,
+ KERN_ERR
+ "%s: jpg_qbuf(-1) - session not active\n",
+ ZR_DEVNAME(zr));
+ return -EINVAL;
+ }
+ fh->jpg_buffers.active = zr->jpg_buffers.active =
+ ZORAN_FREE;
+ zr->jpg_buffers.allocated = 0;
+ zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+ return 0;
+ } else {
+ dprintk(1,
+ KERN_ERR
+ "%s: jpg_qbuf() - stop streaming but not in streaming mode\n",
+ ZR_DEVNAME(zr));
+ return -EINVAL;
+ }
+ }
+
+ if ((res = zoran_jpg_queue_frame(file, frame, mode)))
+ return res;
+
+ /* Start the jpeg codec when the first frame is queued */
+ if (!res && zr->jpg_que_head == 1)
+ jpeg_start(zr);
+
+ return res;
+}
+
+/*
+ * Sync on a MJPEG buffer
+ */
+
+static int
+jpg_sync (struct file *file,
+ struct zoran_sync *bs)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ unsigned long flags;
+ int frame;
+
+ if (fh->jpg_buffers.active == ZORAN_FREE) {
+ dprintk(1,
+ KERN_ERR
+ "%s: jpg_sync() - capture is not currently active\n",
+ ZR_DEVNAME(zr));
+ return -EINVAL;
+ }
+ if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS &&
+ zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) {
+ dprintk(1,
+ KERN_ERR
+ "%s: jpg_sync() - codec not in streaming mode\n",
+ ZR_DEVNAME(zr));
+ return -EINVAL;
+ }
+ if (!wait_event_interruptible_timeout(zr->jpg_capq,
+ (zr->jpg_que_tail != zr->jpg_dma_tail ||
+ zr->jpg_dma_tail == zr->jpg_dma_head),
+ 10*HZ)) {
+ int isr;
+
+ btand(~ZR36057_JMC_Go_en, ZR36057_JMC);
+ udelay(1);
+ zr->codec->control(zr->codec, CODEC_G_STATUS,
+ sizeof(isr), &isr);
+ dprintk(1,
+ KERN_ERR
+ "%s: jpg_sync() - timeout: codec isr=0x%02x\n",
+ ZR_DEVNAME(zr), isr);
+
+ return -ETIME;
+
+ }
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+
+ if (zr->jpg_dma_tail != zr->jpg_dma_head)
+ frame = zr->jpg_pend[zr->jpg_que_tail++ & BUZ_MASK_FRAME];
+ else
+ frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME];
+
+ /* buffer should now be in BUZ_STATE_DONE */
+ if (*zr_debug > 0)
+ if (zr->jpg_buffers.buffer[frame].state != BUZ_STATE_DONE)
+ dprintk(2,
+ KERN_ERR
+ "%s: jpg_sync() - internal state error\n",
+ ZR_DEVNAME(zr));
+
+ *bs = zr->jpg_buffers.buffer[frame].bs;
+ bs->frame = frame;
+ zr->jpg_buffers.buffer[frame].state = BUZ_STATE_USER;
+ fh->jpg_buffers.buffer[frame] = zr->jpg_buffers.buffer[frame];
+
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ return 0;
+}
+
+static void
+zoran_open_init_session (struct file *file)
+{
+ int i;
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+
+ /* Per default, map the V4L Buffers */
+ fh->map_mode = ZORAN_MAP_MODE_RAW;
+
+ /* take over the card's current settings */
+ fh->overlay_settings = zr->overlay_settings;
+ fh->overlay_settings.is_set = 0;
+ fh->overlay_settings.format = zr->overlay_settings.format;
+ fh->overlay_active = ZORAN_FREE;
+
+ /* v4l settings */
+ fh->v4l_settings = zr->v4l_settings;
+
+ /* v4l_buffers */
+ memset(&fh->v4l_buffers, 0, sizeof(struct zoran_v4l_struct));
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ fh->v4l_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
+ fh->v4l_buffers.buffer[i].bs.frame = i;
+ }
+ fh->v4l_buffers.allocated = 0;
+ fh->v4l_buffers.ready_to_be_freed = 0;
+ fh->v4l_buffers.active = ZORAN_FREE;
+ fh->v4l_buffers.buffer_size = v4l_bufsize;
+ fh->v4l_buffers.num_buffers = v4l_nbufs;
+
+ /* jpg settings */
+ fh->jpg_settings = zr->jpg_settings;
+
+ /* jpg_buffers */
+ memset(&fh->jpg_buffers, 0, sizeof(struct zoran_jpg_struct));
+ for (i = 0; i < BUZ_MAX_FRAME; i++) {
+ fh->jpg_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
+ fh->jpg_buffers.buffer[i].bs.frame = i;
+ }
+ fh->jpg_buffers.need_contiguous = zr->jpg_buffers.need_contiguous;
+ fh->jpg_buffers.allocated = 0;
+ fh->jpg_buffers.ready_to_be_freed = 0;
+ fh->jpg_buffers.active = ZORAN_FREE;
+ fh->jpg_buffers.buffer_size = jpg_bufsize;
+ fh->jpg_buffers.num_buffers = jpg_nbufs;
+}
+
+static void
+zoran_close_end_session (struct file *file)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+
+ /* overlay */
+ if (fh->overlay_active != ZORAN_FREE) {
+ fh->overlay_active = zr->overlay_active = ZORAN_FREE;
+ zr->v4l_overlay_active = 0;
+ if (!zr->v4l_memgrab_active)
+ zr36057_overlay(zr, 0);
+ zr->overlay_mask = NULL;
+ }
+
+ /* v4l capture */
+ if (fh->v4l_buffers.active != ZORAN_FREE) {
+ zr36057_set_memgrab(zr, 0);
+ zr->v4l_buffers.allocated = 0;
+ zr->v4l_buffers.active = fh->v4l_buffers.active =
+ ZORAN_FREE;
+ }
+
+ /* v4l buffers */
+ if (fh->v4l_buffers.allocated ||
+ fh->v4l_buffers.ready_to_be_freed) {
+ v4l_fbuffer_free(file);
+ }
+
+ /* jpg capture */
+ if (fh->jpg_buffers.active != ZORAN_FREE) {
+ zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+ zr->jpg_buffers.allocated = 0;
+ zr->jpg_buffers.active = fh->jpg_buffers.active =
+ ZORAN_FREE;
+ }
+
+ /* jpg buffers */
+ if (fh->jpg_buffers.allocated ||
+ fh->jpg_buffers.ready_to_be_freed) {
+ jpg_fbuffer_free(file);
+ }
+}
+
+/*
+ * Open a zoran card. Right now the flags stuff is just playing
+ */
+
+static int
+zoran_open (struct inode *inode,
+ struct file *file)
+{
+ unsigned int minor = iminor(inode);
+ struct zoran *zr = NULL;
+ struct zoran_fh *fh;
+ int i, res, first_open = 0, have_module_locks = 0;
+
+ /* find the device */
+ for (i = 0; i < zoran_num; i++) {
+ if (zoran[i].video_dev->minor == minor) {
+ zr = &zoran[i];
+ break;
+ }
+ }
+
+ if (!zr) {
+ dprintk(1, KERN_ERR "%s: device not found!\n", ZORAN_NAME);
+ res = -ENODEV;
+ goto open_unlock_and_return;
+ }
+
+ /* see fs/device.c - the kernel already locks during open(),
+ * so locking ourselves only causes deadlocks */
+ /*mutex_lock(&zr->resource_lock);*/
+
+ if (!zr->decoder) {
+ dprintk(1,
+ KERN_ERR "%s: no TV decoder loaded for device!\n",
+ ZR_DEVNAME(zr));
+ res = -EIO;
+ goto open_unlock_and_return;
+ }
+
+ /* try to grab a module lock */
+ if (!try_module_get(THIS_MODULE)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: failed to acquire my own lock! PANIC!\n",
+ ZR_DEVNAME(zr));
+ res = -ENODEV;
+ goto open_unlock_and_return;
+ }
+ if (!try_module_get(zr->decoder->driver->driver.owner)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: failed to grab ownership of i2c decoder\n",
+ ZR_DEVNAME(zr));
+ res = -EIO;
+ module_put(THIS_MODULE);
+ goto open_unlock_and_return;
+ }
+ if (zr->encoder &&
+ !try_module_get(zr->encoder->driver->driver.owner)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: failed to grab ownership of i2c encoder\n",
+ ZR_DEVNAME(zr));
+ res = -EIO;
+ module_put(zr->decoder->driver->driver.owner);
+ module_put(THIS_MODULE);
+ goto open_unlock_and_return;
+ }
+
+ have_module_locks = 1;
+
+ if (zr->user >= 2048) {
+ dprintk(1, KERN_ERR "%s: too many users (%d) on device\n",
+ ZR_DEVNAME(zr), zr->user);
+ res = -EBUSY;
+ goto open_unlock_and_return;
+ }
+
+ dprintk(1, KERN_INFO "%s: zoran_open(%s, pid=[%d]), users(-)=%d\n",
+ ZR_DEVNAME(zr), current->comm, current->pid, zr->user);
+
+ /* now, create the open()-specific file_ops struct */
+ fh = kzalloc(sizeof(struct zoran_fh), GFP_KERNEL);
+ if (!fh) {
+ dprintk(1,
+ KERN_ERR
+ "%s: zoran_open() - allocation of zoran_fh failed\n",
+ ZR_DEVNAME(zr));
+ res = -ENOMEM;
+ goto open_unlock_and_return;
+ }
+ /* used to be BUZ_MAX_WIDTH/HEIGHT, but that gives overflows
+ * on norm-change! */
+ fh->overlay_mask =
+ kmalloc(((768 + 31) / 32) * 576 * 4, GFP_KERNEL);
+ if (!fh->overlay_mask) {
+ dprintk(1,
+ KERN_ERR
+ "%s: zoran_open() - allocation of overlay_mask failed\n",
+ ZR_DEVNAME(zr));
+ kfree(fh);
+ res = -ENOMEM;
+ goto open_unlock_and_return;
+ }
+
+ if (zr->user++ == 0)
+ first_open = 1;
+
+ /*mutex_unlock(&zr->resource_lock);*/
+
+ /* default setup - TODO: look at flags */
+ if (first_open) { /* First device open */
+ zr36057_restart(zr);
+ zoran_open_init_params(zr);
+ zoran_init_hardware(zr);
+
+ btor(ZR36057_ICR_IntPinEn, ZR36057_ICR);
+ }
+
+ /* set file_ops stuff */
+ file->private_data = fh;
+ fh->zr = zr;
+ zoran_open_init_session(file);
+
+ return 0;
+
+open_unlock_and_return:
+ /* if we grabbed locks, release them accordingly */
+ if (have_module_locks) {
+ module_put(zr->decoder->driver->driver.owner);
+ if (zr->encoder) {
+ module_put(zr->encoder->driver->driver.owner);
+ }
+ module_put(THIS_MODULE);
+ }
+
+ /* if there's no device found, we didn't obtain the lock either */
+ if (zr) {
+ /*mutex_unlock(&zr->resource_lock);*/
+ }
+
+ return res;
+}
+
+static int
+zoran_close (struct inode *inode,
+ struct file *file)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+
+ dprintk(1, KERN_INFO "%s: zoran_close(%s, pid=[%d]), users(+)=%d\n",
+ ZR_DEVNAME(zr), current->comm, current->pid, zr->user);
+
+ /* kernel locks (fs/device.c), so don't do that ourselves
+ * (prevents deadlocks) */
+ /*mutex_lock(&zr->resource_lock);*/
+
+ zoran_close_end_session(file);
+
+ if (zr->user-- == 1) { /* Last process */
+ /* Clean up JPEG process */
+ wake_up_interruptible(&zr->jpg_capq);
+ zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+ zr->jpg_buffers.allocated = 0;
+ zr->jpg_buffers.active = ZORAN_FREE;
+
+ /* disable interrupts */
+ btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
+
+ if (*zr_debug > 1)
+ print_interrupts(zr);
+
+ /* Overlay off */
+ zr->v4l_overlay_active = 0;
+ zr36057_overlay(zr, 0);
+ zr->overlay_mask = NULL;
+
+ /* capture off */
+ wake_up_interruptible(&zr->v4l_capq);
+ zr36057_set_memgrab(zr, 0);
+ zr->v4l_buffers.allocated = 0;
+ zr->v4l_buffers.active = ZORAN_FREE;
+ zoran_set_pci_master(zr, 0);
+
+ if (!pass_through) { /* Switch to color bar */
+ int zero = 0, two = 2;
+ decoder_command(zr, DECODER_ENABLE_OUTPUT, &zero);
+ encoder_command(zr, ENCODER_SET_INPUT, &two);
+ }
+ }
+
+ file->private_data = NULL;
+ kfree(fh->overlay_mask);
+ kfree(fh);
+
+ /* release locks on the i2c modules */
+ module_put(zr->decoder->driver->driver.owner);
+ if (zr->encoder) {
+ module_put(zr->encoder->driver->driver.owner);
+ }
+ module_put(THIS_MODULE);
+
+ /*mutex_unlock(&zr->resource_lock);*/
+
+ dprintk(4, KERN_INFO "%s: zoran_close() done\n", ZR_DEVNAME(zr));
+
+ return 0;
+}
+
+
+static ssize_t
+zoran_read (struct file *file,
+ char __user *data,
+ size_t count,
+ loff_t *ppos)
+{
+ /* we simply don't support read() (yet)... */
+
+ return -EINVAL;
+}
+
+static ssize_t
+zoran_write (struct file *file,
+ const char __user *data,
+ size_t count,
+ loff_t *ppos)
+{
+ /* ...and the same goes for write() */
+
+ return -EINVAL;
+}
+
+static int
+setup_fbuffer (struct file *file,
+ void *base,
+ const struct zoran_format *fmt,
+ int width,
+ int height,
+ int bytesperline)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+
+ /* (Ronald) v4l/v4l2 guidelines */
+ if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ /* we need a bytesperline value, even if not given */
+ if (!bytesperline)
+ bytesperline = width * ((fmt->depth + 7) & ~7) / 8;
+
+#if 0
+ if (zr->overlay_active) {
+ /* dzjee... stupid users... don't even bother to turn off
+ * overlay before changing the memory location...
+ * normally, we would return errors here. However, one of
+ * the tools that does this is... xawtv! and since xawtv
+ * is used by +/- 99% of the users, we'd rather be user-
+ * friendly and silently do as if nothing went wrong */
+ dprintk(3,
+ KERN_ERR
+ "%s: setup_fbuffer() - forced overlay turnoff because framebuffer changed\n",
+ ZR_DEVNAME(zr));
+ zr36057_overlay(zr, 0);
+ }
+#endif
+
+ if (!(fmt->flags & ZORAN_FORMAT_OVERLAY)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: setup_fbuffer() - no valid overlay format given\n",
+ ZR_DEVNAME(zr));
+ return -EINVAL;
+ }
+ if (height <= 0 || width <= 0 || bytesperline <= 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: setup_fbuffer() - invalid height/width/bpl value (%d|%d|%d)\n",
+ ZR_DEVNAME(zr), width, height, bytesperline);
+ return -EINVAL;
+ }
+ if (bytesperline & 3) {
+ dprintk(1,
+ KERN_ERR
+ "%s: setup_fbuffer() - bytesperline (%d) must be 4-byte aligned\n",
+ ZR_DEVNAME(zr), bytesperline);
+ return -EINVAL;
+ }
+
+ zr->buffer.base = (void *) ((unsigned long) base & ~3);
+ zr->buffer.height = height;
+ zr->buffer.width = width;
+ zr->buffer.depth = fmt->depth;
+ zr->overlay_settings.format = fmt;
+ zr->buffer.bytesperline = bytesperline;
+
+ /* The user should set new window parameters */
+ zr->overlay_settings.is_set = 0;
+
+ return 0;
+}
+
+
+static int
+setup_window (struct file *file,
+ int x,
+ int y,
+ int width,
+ int height,
+ struct video_clip __user *clips,
+ int clipcount,
+ void __user *bitmap)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ struct video_clip *vcp = NULL;
+ int on, end;
+
+
+ if (!zr->buffer.base) {
+ dprintk(1,
+ KERN_ERR
+ "%s: setup_window() - frame buffer has to be set first\n",
+ ZR_DEVNAME(zr));
+ return -EINVAL;
+ }
+
+ if (!fh->overlay_settings.format) {
+ dprintk(1,
+ KERN_ERR
+ "%s: setup_window() - no overlay format set\n",
+ ZR_DEVNAME(zr));
+ return -EINVAL;
+ }
+
+ /*
+ * The video front end needs 4-byte alinged line sizes, we correct that
+ * silently here if necessary
+ */
+ if (zr->buffer.depth == 15 || zr->buffer.depth == 16) {
+ end = (x + width) & ~1; /* round down */
+ x = (x + 1) & ~1; /* round up */
+ width = end - x;
+ }
+
+ if (zr->buffer.depth == 24) {
+ end = (x + width) & ~3; /* round down */
+ x = (x + 3) & ~3; /* round up */
+ width = end - x;
+ }
+
+ if (width > BUZ_MAX_WIDTH)
+ width = BUZ_MAX_WIDTH;
+ if (height > BUZ_MAX_HEIGHT)
+ height = BUZ_MAX_HEIGHT;
+
+ /* Check for vaild parameters */
+ if (width < BUZ_MIN_WIDTH || height < BUZ_MIN_HEIGHT ||
+ width > BUZ_MAX_WIDTH || height > BUZ_MAX_HEIGHT) {
+ dprintk(1,
+ KERN_ERR
+ "%s: setup_window() - width = %d or height = %d invalid\n",
+ ZR_DEVNAME(zr), width, height);
+ return -EINVAL;
+ }
+
+ fh->overlay_settings.x = x;
+ fh->overlay_settings.y = y;
+ fh->overlay_settings.width = width;
+ fh->overlay_settings.height = height;
+ fh->overlay_settings.clipcount = clipcount;
+
+ /*
+ * If an overlay is running, we have to switch it off
+ * and switch it on again in order to get the new settings in effect.
+ *
+ * We also want to avoid that the overlay mask is written
+ * when an overlay is running.
+ */
+
+ on = zr->v4l_overlay_active && !zr->v4l_memgrab_active &&
+ zr->overlay_active != ZORAN_FREE &&
+ fh->overlay_active != ZORAN_FREE;
+ if (on)
+ zr36057_overlay(zr, 0);
+
+ /*
+ * Write the overlay mask if clips are wanted.
+ * We prefer a bitmap.
+ */
+ if (bitmap) {
+ /* fake value - it just means we want clips */
+ fh->overlay_settings.clipcount = 1;
+
+ if (copy_from_user(fh->overlay_mask, bitmap,
+ (width * height + 7) / 8)) {
+ return -EFAULT;
+ }
+ } else if (clipcount > 0) {
+ /* write our own bitmap from the clips */
+ vcp = vmalloc(sizeof(struct video_clip) * (clipcount + 4));
+ if (vcp == NULL) {
+ dprintk(1,
+ KERN_ERR
+ "%s: setup_window() - Alloc of clip mask failed\n",
+ ZR_DEVNAME(zr));
+ return -ENOMEM;
+ }
+ if (copy_from_user
+ (vcp, clips, sizeof(struct video_clip) * clipcount)) {
+ vfree(vcp);
+ return -EFAULT;
+ }
+ write_overlay_mask(file, vcp, clipcount);
+ vfree(vcp);
+ }
+
+ fh->overlay_settings.is_set = 1;
+ if (fh->overlay_active != ZORAN_FREE &&
+ zr->overlay_active != ZORAN_FREE)
+ zr->overlay_settings = fh->overlay_settings;
+
+ if (on)
+ zr36057_overlay(zr, 1);
+
+ /* Make sure the changes come into effect */
+ return wait_grab_pending(zr);
+}
+
+static int
+setup_overlay (struct file *file,
+ int on)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+
+ /* If there is nothing to do, return immediatly */
+ if ((on && fh->overlay_active != ZORAN_FREE) ||
+ (!on && fh->overlay_active == ZORAN_FREE))
+ return 0;
+
+ /* check whether we're touching someone else's overlay */
+ if (on && zr->overlay_active != ZORAN_FREE &&
+ fh->overlay_active == ZORAN_FREE) {
+ dprintk(1,
+ KERN_ERR
+ "%s: setup_overlay() - overlay is already active for another session\n",
+ ZR_DEVNAME(zr));
+ return -EBUSY;
+ }
+ if (!on && zr->overlay_active != ZORAN_FREE &&
+ fh->overlay_active == ZORAN_FREE) {
+ dprintk(1,
+ KERN_ERR
+ "%s: setup_overlay() - you cannot cancel someone else's session\n",
+ ZR_DEVNAME(zr));
+ return -EPERM;
+ }
+
+ if (on == 0) {
+ zr->overlay_active = fh->overlay_active = ZORAN_FREE;
+ zr->v4l_overlay_active = 0;
+ /* When a grab is running, the video simply
+ * won't be switched on any more */
+ if (!zr->v4l_memgrab_active)
+ zr36057_overlay(zr, 0);
+ zr->overlay_mask = NULL;
+ } else {
+ if (!zr->buffer.base || !fh->overlay_settings.is_set) {
+ dprintk(1,
+ KERN_ERR
+ "%s: setup_overlay() - buffer or window not set\n",
+ ZR_DEVNAME(zr));
+ return -EINVAL;
+ }
+ if (!fh->overlay_settings.format) {
+ dprintk(1,
+ KERN_ERR
+ "%s: setup_overlay() - no overlay format set\n",
+ ZR_DEVNAME(zr));
+ return -EINVAL;
+ }
+ zr->overlay_active = fh->overlay_active = ZORAN_LOCKED;
+ zr->v4l_overlay_active = 1;
+ zr->overlay_mask = fh->overlay_mask;
+ zr->overlay_settings = fh->overlay_settings;
+ if (!zr->v4l_memgrab_active)
+ zr36057_overlay(zr, 1);
+ /* When a grab is running, the video will be
+ * switched on when grab is finished */
+ }
+
+ /* Make sure the changes come into effect */
+ return wait_grab_pending(zr);
+}
+
+#ifdef HAVE_V4L2
+ /* get the status of a buffer in the clients buffer queue */
+static int
+zoran_v4l2_buffer_status (struct file *file,
+ struct v4l2_buffer *buf,
+ int num)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+
+ buf->flags = V4L2_BUF_FLAG_MAPPED;
+
+ switch (fh->map_mode) {
+ case ZORAN_MAP_MODE_RAW:
+
+ /* check range */
+ if (num < 0 || num >= fh->v4l_buffers.num_buffers ||
+ !fh->v4l_buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+ "%s: v4l2_buffer_status() - wrong number or buffers not allocated\n",
+ ZR_DEVNAME(zr));
+ return -EINVAL;
+ }
+
+ buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf->length = fh->v4l_buffers.buffer_size;
+
+ /* get buffer */
+ buf->bytesused = fh->v4l_buffers.buffer[num].bs.length;
+ if (fh->v4l_buffers.buffer[num].state == BUZ_STATE_DONE ||
+ fh->v4l_buffers.buffer[num].state == BUZ_STATE_USER) {
+ buf->sequence = fh->v4l_buffers.buffer[num].bs.seq;
+ buf->flags |= V4L2_BUF_FLAG_DONE;
+ buf->timestamp =
+ fh->v4l_buffers.buffer[num].bs.timestamp;
+ } else {
+ buf->flags |= V4L2_BUF_FLAG_QUEUED;
+ }
+
+ if (fh->v4l_settings.height <= BUZ_MAX_HEIGHT / 2)
+ buf->field = V4L2_FIELD_TOP;
+ else
+ buf->field = V4L2_FIELD_INTERLACED;
+
+ break;
+
+ case ZORAN_MAP_MODE_JPG_REC:
+ case ZORAN_MAP_MODE_JPG_PLAY:
+
+ /* check range */
+ if (num < 0 || num >= fh->jpg_buffers.num_buffers ||
+ !fh->jpg_buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+ "%s: v4l2_buffer_status() - wrong number or buffers not allocated\n",
+ ZR_DEVNAME(zr));
+ return -EINVAL;
+ }
+
+ buf->type = (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ?
+ V4L2_BUF_TYPE_VIDEO_CAPTURE :
+ V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ buf->length = fh->jpg_buffers.buffer_size;
+
+ /* these variables are only written after frame has been captured */
+ if (fh->jpg_buffers.buffer[num].state == BUZ_STATE_DONE ||
+ fh->jpg_buffers.buffer[num].state == BUZ_STATE_USER) {
+ buf->sequence = fh->jpg_buffers.buffer[num].bs.seq;
+ buf->timestamp =
+ fh->jpg_buffers.buffer[num].bs.timestamp;
+ buf->bytesused =
+ fh->jpg_buffers.buffer[num].bs.length;
+ buf->flags |= V4L2_BUF_FLAG_DONE;
+ } else {
+ buf->flags |= V4L2_BUF_FLAG_QUEUED;
+ }
+
+ /* which fields are these? */
+ if (fh->jpg_settings.TmpDcm != 1)
+ buf->field =
+ fh->jpg_settings.
+ odd_even ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM;
+ else
+ buf->field =
+ fh->jpg_settings.
+ odd_even ? V4L2_FIELD_SEQ_TB :
+ V4L2_FIELD_SEQ_BT;
+
+ break;
+
+ default:
+
+ dprintk(5,
+ KERN_ERR
+ "%s: v4l2_buffer_status() - invalid buffer type|map_mode (%d|%d)\n",
+ ZR_DEVNAME(zr), buf->type, fh->map_mode);
+ return -EINVAL;
+ }
+
+ buf->memory = V4L2_MEMORY_MMAP;
+ buf->index = num;
+ buf->m.offset = buf->length * num;
+
+ return 0;
+}
+#endif
+
+static int
+zoran_set_norm (struct zoran *zr,
+ int norm) /* VIDEO_MODE_* */
+{
+ int norm_encoder, on;
+
+ if (zr->v4l_buffers.active != ZORAN_FREE ||
+ zr->jpg_buffers.active != ZORAN_FREE) {
+ dprintk(1,
+ KERN_WARNING
+ "%s: set_norm() called while in playback/capture mode\n",
+ ZR_DEVNAME(zr));
+ return -EBUSY;
+ }
+
+ if (lock_norm && norm != zr->norm) {
+ if (lock_norm > 1) {
+ dprintk(1,
+ KERN_WARNING
+ "%s: set_norm() - TV standard is locked, can not switch norm\n",
+ ZR_DEVNAME(zr));
+ return -EPERM;
+ } else {
+ dprintk(1,
+ KERN_WARNING
+ "%s: set_norm() - TV standard is locked, norm was not changed\n",
+ ZR_DEVNAME(zr));
+ norm = zr->norm;
+ }
+ }
+
+ if (norm != VIDEO_MODE_AUTO &&
+ (norm < 0 || norm >= zr->card.norms ||
+ !zr->card.tvn[norm])) {
+ dprintk(1,
+ KERN_ERR "%s: set_norm() - unsupported norm %d\n",
+ ZR_DEVNAME(zr), norm);
+ return -EINVAL;
+ }
+
+ if (norm == VIDEO_MODE_AUTO) {
+ int status;
+
+ /* if we have autodetect, ... */
+ struct video_decoder_capability caps;
+ decoder_command(zr, DECODER_GET_CAPABILITIES, &caps);
+ if (!(caps.flags & VIDEO_DECODER_AUTO)) {
+ dprintk(1, KERN_ERR "%s: norm=auto unsupported\n",
+ ZR_DEVNAME(zr));
+ return -EINVAL;
+ }
+
+ decoder_command(zr, DECODER_SET_NORM, &norm);
+
+ /* let changes come into effect */
+ ssleep(2);
+
+ decoder_command(zr, DECODER_GET_STATUS, &status);
+ if (!(status & DECODER_STATUS_GOOD)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: set_norm() - no norm detected\n",
+ ZR_DEVNAME(zr));
+ /* reset norm */
+ decoder_command(zr, DECODER_SET_NORM, &zr->norm);
+ return -EIO;
+ }
+
+ if (status & DECODER_STATUS_NTSC)
+ norm = VIDEO_MODE_NTSC;
+ else if (status & DECODER_STATUS_SECAM)
+ norm = VIDEO_MODE_SECAM;
+ else
+ norm = VIDEO_MODE_PAL;
+ }
+ zr->timing = zr->card.tvn[norm];
+ norm_encoder = norm;
+
+ /* We switch overlay off and on since a change in the
+ * norm needs different VFE settings */
+ on = zr->overlay_active && !zr->v4l_memgrab_active;
+ if (on)
+ zr36057_overlay(zr, 0);
+
+ decoder_command(zr, DECODER_SET_NORM, &norm);
+ encoder_command(zr, ENCODER_SET_NORM, &norm_encoder);
+
+ if (on)
+ zr36057_overlay(zr, 1);
+
+ /* Make sure the changes come into effect */
+ zr->norm = norm;
+
+ return 0;
+}
+
+static int
+zoran_set_input (struct zoran *zr,
+ int input)
+{
+ int realinput;
+
+ if (input == zr->input) {
+ return 0;
+ }
+
+ if (zr->v4l_buffers.active != ZORAN_FREE ||
+ zr->jpg_buffers.active != ZORAN_FREE) {
+ dprintk(1,
+ KERN_WARNING
+ "%s: set_input() called while in playback/capture mode\n",
+ ZR_DEVNAME(zr));
+ return -EBUSY;
+ }
+
+ if (input < 0 || input >= zr->card.inputs) {
+ dprintk(1,
+ KERN_ERR
+ "%s: set_input() - unnsupported input %d\n",
+ ZR_DEVNAME(zr), input);
+ return -EINVAL;
+ }
+
+ realinput = zr->card.input[input].muxsel;
+ zr->input = input;
+
+ decoder_command(zr, DECODER_SET_INPUT, &realinput);
+
+ return 0;
+}
+
+/*
+ * ioctl routine
+ */
+
+static int
+zoran_do_ioctl (struct inode *inode,
+ struct file *file,
+ unsigned int cmd,
+ void *arg)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ /* CAREFUL: used in multiple places here */
+ struct zoran_jpg_settings settings;
+
+ /* we might have older buffers lying around... We don't want
+ * to wait, but we do want to try cleaning them up ASAP. So
+ * we try to obtain the lock and free them. If that fails, we
+ * don't do anything and wait for the next turn. In the end,
+ * zoran_close() or a new allocation will still free them...
+ * This is just a 'the sooner the better' extra 'feature'
+ *
+ * We don't free the buffers right on munmap() because that
+ * causes oopses (kfree() inside munmap() oopses for no
+ * apparent reason - it's also not reproduceable in any way,
+ * but moving the free code outside the munmap() handler fixes
+ * all this... If someone knows why, please explain me (Ronald)
+ */
+ if (!!mutex_trylock(&zr->resource_lock)) {
+ /* we obtained it! Let's try to free some things */
+ if (fh->jpg_buffers.ready_to_be_freed)
+ jpg_fbuffer_free(file);
+ if (fh->v4l_buffers.ready_to_be_freed)
+ v4l_fbuffer_free(file);
+
+ mutex_unlock(&zr->resource_lock);
+ }
+
+ switch (cmd) {
+
+ case VIDIOCGCAP:
+ {
+ struct video_capability *vcap = arg;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOCGCAP\n", ZR_DEVNAME(zr));
+
+ memset(vcap, 0, sizeof(struct video_capability));
+ strncpy(vcap->name, ZR_DEVNAME(zr), sizeof(vcap->name));
+ vcap->type = ZORAN_VID_TYPE;
+
+ vcap->channels = zr->card.inputs;
+ vcap->audios = 0;
+ mutex_lock(&zr->resource_lock);
+ vcap->maxwidth = BUZ_MAX_WIDTH;
+ vcap->maxheight = BUZ_MAX_HEIGHT;
+ vcap->minwidth = BUZ_MIN_WIDTH;
+ vcap->minheight = BUZ_MIN_HEIGHT;
+ mutex_unlock(&zr->resource_lock);
+
+ return 0;
+ }
+ break;
+
+ case VIDIOCGCHAN:
+ {
+ struct video_channel *vchan = arg;
+ int channel = vchan->channel;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOCGCHAN - channel=%d\n",
+ ZR_DEVNAME(zr), vchan->channel);
+
+ memset(vchan, 0, sizeof(struct video_channel));
+ if (channel > zr->card.inputs || channel < 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOCGCHAN on not existing channel %d\n",
+ ZR_DEVNAME(zr), channel);
+ return -EINVAL;
+ }
+
+ strcpy(vchan->name, zr->card.input[channel].name);
+
+ vchan->tuners = 0;
+ vchan->flags = 0;
+ vchan->type = VIDEO_TYPE_CAMERA;
+ mutex_lock(&zr->resource_lock);
+ vchan->norm = zr->norm;
+ mutex_unlock(&zr->resource_lock);
+ vchan->channel = channel;
+
+ return 0;
+ }
+ break;
+
+ /* RJ: the documentation at http://roadrunner.swansea.linux.org.uk/v4lapi.shtml says:
+ *
+ * * "The VIDIOCSCHAN ioctl takes an integer argument and switches the capture to this input."
+ * * ^^^^^^^
+ * * The famos BTTV driver has it implemented with a struct video_channel argument
+ * * and we follow it for compatibility reasons
+ * *
+ * * BTW: this is the only way the user can set the norm!
+ */
+
+ case VIDIOCSCHAN:
+ {
+ struct video_channel *vchan = arg;
+ int res;
+
+ dprintk(3,
+ KERN_DEBUG
+ "%s: VIDIOCSCHAN - channel=%d, norm=%d\n",
+ ZR_DEVNAME(zr), vchan->channel, vchan->norm);
+
+ mutex_lock(&zr->resource_lock);
+ if ((res = zoran_set_input(zr, vchan->channel)))
+ goto schan_unlock_and_return;
+ if ((res = zoran_set_norm(zr, vchan->norm)))
+ goto schan_unlock_and_return;
+
+ /* Make sure the changes come into effect */
+ res = wait_grab_pending(zr);
+ schan_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ return res;
+ }
+ break;
+
+ case VIDIOCGPICT:
+ {
+ struct video_picture *vpict = arg;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOCGPICT\n", ZR_DEVNAME(zr));
+
+ memset(vpict, 0, sizeof(struct video_picture));
+ mutex_lock(&zr->resource_lock);
+ vpict->hue = zr->hue;
+ vpict->brightness = zr->brightness;
+ vpict->contrast = zr->contrast;
+ vpict->colour = zr->saturation;
+ if (fh->overlay_settings.format) {
+ vpict->depth = fh->overlay_settings.format->depth;
+ vpict->palette = fh->overlay_settings.format->palette;
+ } else {
+ vpict->depth = 0;
+ }
+ mutex_unlock(&zr->resource_lock);
+
+ return 0;
+ }
+ break;
+
+ case VIDIOCSPICT:
+ {
+ struct video_picture *vpict = arg;
+ int i;
+
+ dprintk(3,
+ KERN_DEBUG
+ "%s: VIDIOCSPICT - bri=%d, hue=%d, col=%d, con=%d, dep=%d, pal=%d\n",
+ ZR_DEVNAME(zr), vpict->brightness, vpict->hue,
+ vpict->colour, vpict->contrast, vpict->depth,
+ vpict->palette);
+
+ for (i = 0; i < zoran_num_formats; i++) {
+ const struct zoran_format *fmt = &zoran_formats[i];
+
+ if (fmt->palette != -1 &&
+ fmt->flags & ZORAN_FORMAT_OVERLAY &&
+ fmt->palette == vpict->palette &&
+ fmt->depth == vpict->depth)
+ break;
+ }
+ if (i == zoran_num_formats) {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOCSPICT - Invalid palette %d\n",
+ ZR_DEVNAME(zr), vpict->palette);
+ return -EINVAL;
+ }
+
+ mutex_lock(&zr->resource_lock);
+
+ decoder_command(zr, DECODER_SET_PICTURE, vpict);
+
+ zr->hue = vpict->hue;
+ zr->contrast = vpict->contrast;
+ zr->saturation = vpict->colour;
+ zr->brightness = vpict->brightness;
+
+ fh->overlay_settings.format = &zoran_formats[i];
+
+ mutex_unlock(&zr->resource_lock);
+
+ return 0;
+ }
+ break;
+
+ case VIDIOCCAPTURE:
+ {
+ int *on = arg, res;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOCCAPTURE - on=%d\n",
+ ZR_DEVNAME(zr), *on);
+
+ mutex_lock(&zr->resource_lock);
+ res = setup_overlay(file, *on);
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+ break;
+
+ case VIDIOCGWIN:
+ {
+ struct video_window *vwin = arg;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOCGWIN\n", ZR_DEVNAME(zr));
+
+ memset(vwin, 0, sizeof(struct video_window));
+ mutex_lock(&zr->resource_lock);
+ vwin->x = fh->overlay_settings.x;
+ vwin->y = fh->overlay_settings.y;
+ vwin->width = fh->overlay_settings.width;
+ vwin->height = fh->overlay_settings.height;
+ mutex_unlock(&zr->resource_lock);
+ vwin->clipcount = 0;
+ return 0;
+ }
+ break;
+
+ case VIDIOCSWIN:
+ {
+ struct video_window *vwin = arg;
+ int res;
+
+ dprintk(3,
+ KERN_DEBUG
+ "%s: VIDIOCSWIN - x=%d, y=%d, w=%d, h=%d, clipcount=%d\n",
+ ZR_DEVNAME(zr), vwin->x, vwin->y, vwin->width,
+ vwin->height, vwin->clipcount);
+
+ mutex_lock(&zr->resource_lock);
+ res =
+ setup_window(file, vwin->x, vwin->y, vwin->width,
+ vwin->height, vwin->clips,
+ vwin->clipcount, NULL);
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+ break;
+
+ case VIDIOCGFBUF:
+ {
+ struct video_buffer *vbuf = arg;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOCGFBUF\n", ZR_DEVNAME(zr));
+
+ mutex_lock(&zr->resource_lock);
+ *vbuf = zr->buffer;
+ mutex_unlock(&zr->resource_lock);
+ return 0;
+ }
+ break;
+
+ case VIDIOCSFBUF:
+ {
+ struct video_buffer *vbuf = arg;
+ int i, res = 0;
+
+ dprintk(3,
+ KERN_DEBUG
+ "%s: VIDIOCSFBUF - base=%p, w=%d, h=%d, depth=%d, bpl=%d\n",
+ ZR_DEVNAME(zr), vbuf->base, vbuf->width,
+ vbuf->height, vbuf->depth, vbuf->bytesperline);
+
+ for (i = 0; i < zoran_num_formats; i++)
+ if (zoran_formats[i].depth == vbuf->depth)
+ break;
+ if (i == zoran_num_formats) {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOCSFBUF - invalid fbuf depth %d\n",
+ ZR_DEVNAME(zr), vbuf->depth);
+ return -EINVAL;
+ }
+
+ mutex_lock(&zr->resource_lock);
+ res =
+ setup_fbuffer(file, vbuf->base, &zoran_formats[i],
+ vbuf->width, vbuf->height,
+ vbuf->bytesperline);
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+ break;
+
+ case VIDIOCSYNC:
+ {
+ int *frame = arg, res;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOCSYNC - frame=%d\n",
+ ZR_DEVNAME(zr), *frame);
+
+ mutex_lock(&zr->resource_lock);
+ res = v4l_sync(file, *frame);
+ mutex_unlock(&zr->resource_lock);
+ if (!res)
+ zr->v4l_sync_tail++;
+ return res;
+ }
+ break;
+
+ case VIDIOCMCAPTURE:
+ {
+ struct video_mmap *vmap = arg;
+ int res;
+
+ dprintk(3,
+ KERN_DEBUG
+ "%s: VIDIOCMCAPTURE - frame=%d, geom=%dx%d, fmt=%d\n",
+ ZR_DEVNAME(zr), vmap->frame, vmap->width, vmap->height,
+ vmap->format);
+
+ mutex_lock(&zr->resource_lock);
+ res = v4l_grab(file, vmap);
+ mutex_unlock(&zr->resource_lock);
+ return res;
+ }
+ break;
+
+ case VIDIOCGMBUF:
+ {
+ struct video_mbuf *vmbuf = arg;
+ int i, res = 0;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOCGMBUF\n", ZR_DEVNAME(zr));
+
+ vmbuf->size =
+ fh->v4l_buffers.num_buffers *
+ fh->v4l_buffers.buffer_size;
+ vmbuf->frames = fh->v4l_buffers.num_buffers;
+ for (i = 0; i < vmbuf->frames; i++) {
+ vmbuf->offsets[i] =
+ i * fh->v4l_buffers.buffer_size;
+ }
+
+ mutex_lock(&zr->resource_lock);
+
+ if (fh->jpg_buffers.allocated || fh->v4l_buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOCGMBUF - buffers already allocated\n",
+ ZR_DEVNAME(zr));
+ res = -EINVAL;
+ goto v4l1reqbuf_unlock_and_return;
+ }
+
+ if (v4l_fbuffer_alloc(file)) {
+ res = -ENOMEM;
+ goto v4l1reqbuf_unlock_and_return;
+ }
+
+ /* The next mmap will map the V4L buffers */
+ fh->map_mode = ZORAN_MAP_MODE_RAW;
+ v4l1reqbuf_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+ break;
+
+ case VIDIOCGUNIT:
+ {
+ struct video_unit *vunit = arg;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOCGUNIT\n", ZR_DEVNAME(zr));
+
+ vunit->video = zr->video_dev->minor;
+ vunit->vbi = VIDEO_NO_UNIT;
+ vunit->radio = VIDEO_NO_UNIT;
+ vunit->audio = VIDEO_NO_UNIT;
+ vunit->teletext = VIDEO_NO_UNIT;
+
+ return 0;
+ }
+ break;
+
+ /*
+ * RJ: In principal we could support subcaptures for V4L grabbing.
+ * Not even the famous BTTV driver has them, however.
+ * If there should be a strong demand, one could consider
+ * to implement them.
+ */
+ case VIDIOCGCAPTURE:
+ {
+ dprintk(3, KERN_ERR "%s: VIDIOCGCAPTURE not supported\n",
+ ZR_DEVNAME(zr));
+ return -EINVAL;
+ }
+ break;
+
+ case VIDIOCSCAPTURE:
+ {
+ dprintk(3, KERN_ERR "%s: VIDIOCSCAPTURE not supported\n",
+ ZR_DEVNAME(zr));
+ return -EINVAL;
+ }
+ break;
+
+ case BUZIOC_G_PARAMS:
+ {
+ struct zoran_params *bparams = arg;
+
+ dprintk(3, KERN_DEBUG "%s: BUZIOC_G_PARAMS\n", ZR_DEVNAME(zr));
+
+ memset(bparams, 0, sizeof(struct zoran_params));
+ bparams->major_version = MAJOR_VERSION;
+ bparams->minor_version = MINOR_VERSION;
+
+ mutex_lock(&zr->resource_lock);
+
+ bparams->norm = zr->norm;
+ bparams->input = zr->input;
+
+ bparams->decimation = fh->jpg_settings.decimation;
+ bparams->HorDcm = fh->jpg_settings.HorDcm;
+ bparams->VerDcm = fh->jpg_settings.VerDcm;
+ bparams->TmpDcm = fh->jpg_settings.TmpDcm;
+ bparams->field_per_buff = fh->jpg_settings.field_per_buff;
+ bparams->img_x = fh->jpg_settings.img_x;
+ bparams->img_y = fh->jpg_settings.img_y;
+ bparams->img_width = fh->jpg_settings.img_width;
+ bparams->img_height = fh->jpg_settings.img_height;
+ bparams->odd_even = fh->jpg_settings.odd_even;
+
+ bparams->quality = fh->jpg_settings.jpg_comp.quality;
+ bparams->APPn = fh->jpg_settings.jpg_comp.APPn;
+ bparams->APP_len = fh->jpg_settings.jpg_comp.APP_len;
+ memcpy(bparams->APP_data,
+ fh->jpg_settings.jpg_comp.APP_data,
+ sizeof(bparams->APP_data));
+ bparams->COM_len = zr->jpg_settings.jpg_comp.COM_len;
+ memcpy(bparams->COM_data,
+ fh->jpg_settings.jpg_comp.COM_data,
+ sizeof(bparams->COM_data));
+ bparams->jpeg_markers =
+ fh->jpg_settings.jpg_comp.jpeg_markers;
+
+ mutex_unlock(&zr->resource_lock);
+
+ bparams->VFIFO_FB = 0;
+
+ return 0;
+ }
+ break;
+
+ case BUZIOC_S_PARAMS:
+ {
+ struct zoran_params *bparams = arg;
+ int res = 0;
+
+ dprintk(3, KERN_DEBUG "%s: BUZIOC_S_PARAMS\n", ZR_DEVNAME(zr));
+
+ settings.decimation = bparams->decimation;
+ settings.HorDcm = bparams->HorDcm;
+ settings.VerDcm = bparams->VerDcm;
+ settings.TmpDcm = bparams->TmpDcm;
+ settings.field_per_buff = bparams->field_per_buff;
+ settings.img_x = bparams->img_x;
+ settings.img_y = bparams->img_y;
+ settings.img_width = bparams->img_width;
+ settings.img_height = bparams->img_height;
+ settings.odd_even = bparams->odd_even;
+
+ settings.jpg_comp.quality = bparams->quality;
+ settings.jpg_comp.APPn = bparams->APPn;
+ settings.jpg_comp.APP_len = bparams->APP_len;
+ memcpy(settings.jpg_comp.APP_data, bparams->APP_data,
+ sizeof(bparams->APP_data));
+ settings.jpg_comp.COM_len = bparams->COM_len;
+ memcpy(settings.jpg_comp.COM_data, bparams->COM_data,
+ sizeof(bparams->COM_data));
+ settings.jpg_comp.jpeg_markers = bparams->jpeg_markers;
+
+ mutex_lock(&zr->resource_lock);
+
+ if (zr->codec_mode != BUZ_MODE_IDLE) {
+ dprintk(1,
+ KERN_ERR
+ "%s: BUZIOC_S_PARAMS called, but Buz in capture/playback mode\n",
+ ZR_DEVNAME(zr));
+ res = -EINVAL;
+ goto sparams_unlock_and_return;
+ }
+
+ /* Check the params first before overwriting our
+ * nternal values */
+ if (zoran_check_jpg_settings(zr, &settings)) {
+ res = -EINVAL;
+ goto sparams_unlock_and_return;
+ }
+
+ fh->jpg_settings = settings;
+ sparams_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+ break;
+
+ case BUZIOC_REQBUFS:
+ {
+ struct zoran_requestbuffers *breq = arg;
+ int res = 0;
+
+ dprintk(3,
+ KERN_DEBUG
+ "%s: BUZIOC_REQBUFS - count=%lu, size=%lu\n",
+ ZR_DEVNAME(zr), breq->count, breq->size);
+
+ /* Enforce reasonable lower and upper limits */
+ if (breq->count < 4)
+ breq->count = 4; /* Could be choosen smaller */
+ if (breq->count > jpg_nbufs)
+ breq->count = jpg_nbufs;
+ breq->size = PAGE_ALIGN(breq->size);
+ if (breq->size < 8192)
+ breq->size = 8192; /* Arbitrary */
+ /* breq->size is limited by 1 page for the stat_com
+ * tables to a Maximum of 2 MB */
+ if (breq->size > jpg_bufsize)
+ breq->size = jpg_bufsize;
+ if (fh->jpg_buffers.need_contiguous &&
+ breq->size > MAX_KMALLOC_MEM)
+ breq->size = MAX_KMALLOC_MEM;
+
+ mutex_lock(&zr->resource_lock);
+
+ if (fh->jpg_buffers.allocated || fh->v4l_buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+ "%s: BUZIOC_REQBUFS - buffers allready allocated\n",
+ ZR_DEVNAME(zr));
+ res = -EBUSY;
+ goto jpgreqbuf_unlock_and_return;
+ }
+
+ fh->jpg_buffers.num_buffers = breq->count;
+ fh->jpg_buffers.buffer_size = breq->size;
+
+ if (jpg_fbuffer_alloc(file)) {
+ res = -ENOMEM;
+ goto jpgreqbuf_unlock_and_return;
+ }
+
+ /* The next mmap will map the MJPEG buffers - could
+ * also be *_PLAY, but it doesn't matter here */
+ fh->map_mode = ZORAN_MAP_MODE_JPG_REC;
+ jpgreqbuf_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+ break;
+
+ case BUZIOC_QBUF_CAPT:
+ {
+ int *frame = arg, res;
+
+ dprintk(3, KERN_DEBUG "%s: BUZIOC_QBUF_CAPT - frame=%d\n",
+ ZR_DEVNAME(zr), *frame);
+
+ mutex_lock(&zr->resource_lock);
+ res = jpg_qbuf(file, *frame, BUZ_MODE_MOTION_COMPRESS);
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+ break;
+
+ case BUZIOC_QBUF_PLAY:
+ {
+ int *frame = arg, res;
+
+ dprintk(3, KERN_DEBUG "%s: BUZIOC_QBUF_PLAY - frame=%d\n",
+ ZR_DEVNAME(zr), *frame);
+
+ mutex_lock(&zr->resource_lock);
+ res = jpg_qbuf(file, *frame, BUZ_MODE_MOTION_DECOMPRESS);
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+ break;
+
+ case BUZIOC_SYNC:
+ {
+ struct zoran_sync *bsync = arg;
+ int res;
+
+ dprintk(3, KERN_DEBUG "%s: BUZIOC_SYNC\n", ZR_DEVNAME(zr));
+
+ mutex_lock(&zr->resource_lock);
+ res = jpg_sync(file, bsync);
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+ break;
+
+ case BUZIOC_G_STATUS:
+ {
+ struct zoran_status *bstat = arg;
+ int norm, input, status, res = 0;
+
+ dprintk(3, KERN_DEBUG "%s: BUZIOC_G_STATUS\n", ZR_DEVNAME(zr));
+
+ if (zr->codec_mode != BUZ_MODE_IDLE) {
+ dprintk(1,
+ KERN_ERR
+ "%s: BUZIOC_G_STATUS called but Buz in capture/playback mode\n",
+ ZR_DEVNAME(zr));
+ return -EINVAL;
+ }
+
+ input = zr->card.input[bstat->input].muxsel;
+ norm = VIDEO_MODE_AUTO;
+
+ mutex_lock(&zr->resource_lock);
+
+ if (zr->codec_mode != BUZ_MODE_IDLE) {
+ dprintk(1,
+ KERN_ERR
+ "%s: BUZIOC_G_STATUS called, but Buz in capture/playback mode\n",
+ ZR_DEVNAME(zr));
+ res = -EINVAL;
+ goto gstat_unlock_and_return;
+ }
+
+ decoder_command(zr, DECODER_SET_INPUT, &input);
+ decoder_command(zr, DECODER_SET_NORM, &norm);
+
+ /* sleep 1 second */
+ ssleep(1);
+
+ /* Get status of video decoder */
+ decoder_command(zr, DECODER_GET_STATUS, &status);
+
+ /* restore previous input and norm */
+ input = zr->card.input[zr->input].muxsel;
+ decoder_command(zr, DECODER_SET_INPUT, &input);
+ decoder_command(zr, DECODER_SET_NORM, &zr->norm);
+ gstat_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ if (!res) {
+ bstat->signal =
+ (status & DECODER_STATUS_GOOD) ? 1 : 0;
+ if (status & DECODER_STATUS_NTSC)
+ bstat->norm = VIDEO_MODE_NTSC;
+ else if (status & DECODER_STATUS_SECAM)
+ bstat->norm = VIDEO_MODE_SECAM;
+ else
+ bstat->norm = VIDEO_MODE_PAL;
+
+ bstat->color =
+ (status & DECODER_STATUS_COLOR) ? 1 : 0;
+ }
+
+ return res;
+ }
+ break;
+
+#ifdef HAVE_V4L2
+
+ /* The new video4linux2 capture interface - much nicer than video4linux1, since
+ * it allows for integrating the JPEG capturing calls inside standard v4l2
+ */
+
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *cap = arg;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_QUERYCAP\n", ZR_DEVNAME(zr));
+
+ memset(cap, 0, sizeof(*cap));
+ strncpy(cap->card, ZR_DEVNAME(zr), sizeof(cap->card));
+ strncpy(cap->driver, "zoran", sizeof(cap->driver));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s",
+ pci_name(zr->pci_dev));
+ cap->version =
+ KERNEL_VERSION(MAJOR_VERSION, MINOR_VERSION,
+ RELEASE_VERSION);
+ cap->capabilities = ZORAN_V4L2_VID_FLAGS;
+
+ return 0;
+ }
+ break;
+
+ case VIDIOC_ENUM_FMT:
+ {
+ struct v4l2_fmtdesc *fmt = arg;
+ int index = fmt->index, num = -1, i, flag = 0, type =
+ fmt->type;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_ENUM_FMT - index=%d\n",
+ ZR_DEVNAME(zr), fmt->index);
+
+ switch (fmt->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ flag = ZORAN_FORMAT_CAPTURE;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ flag = ZORAN_FORMAT_PLAYBACK;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ flag = ZORAN_FORMAT_OVERLAY;
+ break;
+ default:
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_ENUM_FMT - unknown type %d\n",
+ ZR_DEVNAME(zr), fmt->type);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < zoran_num_formats; i++) {
+ if (zoran_formats[i].flags & flag)
+ num++;
+ if (num == fmt->index)
+ break;
+ }
+ if (fmt->index < 0 /* late, but not too late */ ||
+ i == zoran_num_formats)
+ return -EINVAL;
+
+ memset(fmt, 0, sizeof(*fmt));
+ fmt->index = index;
+ fmt->type = type;
+ strncpy(fmt->description, zoran_formats[i].name, 31);
+ fmt->pixelformat = zoran_formats[i].fourcc;
+ if (zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED)
+ fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
+
+ return 0;
+ }
+ break;
+
+ case VIDIOC_G_FMT:
+ {
+ struct v4l2_format *fmt = arg;
+ int type = fmt->type;
+
+ dprintk(5, KERN_DEBUG "%s: VIDIOC_G_FMT\n", ZR_DEVNAME(zr));
+
+ memset(fmt, 0, sizeof(*fmt));
+ fmt->type = type;
+
+ switch (fmt->type) {
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+
+ mutex_lock(&zr->resource_lock);
+
+ fmt->fmt.win.w.left = fh->overlay_settings.x;
+ fmt->fmt.win.w.top = fh->overlay_settings.y;
+ fmt->fmt.win.w.width = fh->overlay_settings.width;
+ fmt->fmt.win.w.height =
+ fh->overlay_settings.height;
+ if (fh->overlay_settings.width * 2 >
+ BUZ_MAX_HEIGHT)
+ fmt->fmt.win.field = V4L2_FIELD_INTERLACED;
+ else
+ fmt->fmt.win.field = V4L2_FIELD_TOP;
+
+ mutex_unlock(&zr->resource_lock);
+
+ break;
+
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+
+ mutex_lock(&zr->resource_lock);
+
+ if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ fh->map_mode == ZORAN_MAP_MODE_RAW) {
+
+ fmt->fmt.pix.width =
+ fh->v4l_settings.width;
+ fmt->fmt.pix.height =
+ fh->v4l_settings.height;
+ fmt->fmt.pix.sizeimage =
+ fh->v4l_buffers.buffer_size;
+ fmt->fmt.pix.pixelformat =
+ fh->v4l_settings.format->fourcc;
+ fmt->fmt.pix.colorspace =
+ fh->v4l_settings.format->colorspace;
+ fmt->fmt.pix.bytesperline = 0;
+ if (BUZ_MAX_HEIGHT <
+ (fh->v4l_settings.height * 2))
+ fmt->fmt.pix.field =
+ V4L2_FIELD_INTERLACED;
+ else
+ fmt->fmt.pix.field =
+ V4L2_FIELD_TOP;
+
+ } else {
+
+ fmt->fmt.pix.width =
+ fh->jpg_settings.img_width /
+ fh->jpg_settings.HorDcm;
+ fmt->fmt.pix.height =
+ fh->jpg_settings.img_height /
+ (fh->jpg_settings.VerDcm *
+ fh->jpg_settings.TmpDcm);
+ fmt->fmt.pix.sizeimage =
+ zoran_v4l2_calc_bufsize(&fh->
+ jpg_settings);
+ fmt->fmt.pix.pixelformat =
+ V4L2_PIX_FMT_MJPEG;
+ if (fh->jpg_settings.TmpDcm == 1)
+ fmt->fmt.pix.field =
+ (fh->jpg_settings.
+ odd_even ? V4L2_FIELD_SEQ_BT :
+ V4L2_FIELD_SEQ_BT);
+ else
+ fmt->fmt.pix.field =
+ (fh->jpg_settings.
+ odd_even ? V4L2_FIELD_TOP :
+ V4L2_FIELD_BOTTOM);
+
+ fmt->fmt.pix.bytesperline = 0;
+ fmt->fmt.pix.colorspace =
+ V4L2_COLORSPACE_SMPTE170M;
+ }
+
+ mutex_unlock(&zr->resource_lock);
+
+ break;
+
+ default:
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_G_FMT - unsupported type %d\n",
+ ZR_DEVNAME(zr), fmt->type);
+ return -EINVAL;
+ }
+ return 0;
+ }
+ break;
+
+ case VIDIOC_S_FMT:
+ {
+ struct v4l2_format *fmt = arg;
+ int i, res = 0;
+ __u32 printformat;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_S_FMT - type=%d, ",
+ ZR_DEVNAME(zr), fmt->type);
+
+ switch (fmt->type) {
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+
+ dprintk(3, "x=%d, y=%d, w=%d, h=%d, cnt=%d, map=0x%p\n",
+ fmt->fmt.win.w.left, fmt->fmt.win.w.top,
+ fmt->fmt.win.w.width,
+ fmt->fmt.win.w.height,
+ fmt->fmt.win.clipcount,
+ fmt->fmt.win.bitmap);
+ mutex_lock(&zr->resource_lock);
+ res =
+ setup_window(file, fmt->fmt.win.w.left,
+ fmt->fmt.win.w.top,
+ fmt->fmt.win.w.width,
+ fmt->fmt.win.w.height,
+ (struct video_clip __user *)
+ fmt->fmt.win.clips,
+ fmt->fmt.win.clipcount,
+ fmt->fmt.win.bitmap);
+ mutex_unlock(&zr->resource_lock);
+ return res;
+ break;
+
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+
+ printformat =
+ __cpu_to_le32(fmt->fmt.pix.pixelformat);
+ dprintk(3, "size=%dx%d, fmt=0x%x (%4.4s)\n",
+ fmt->fmt.pix.width, fmt->fmt.pix.height,
+ fmt->fmt.pix.pixelformat,
+ (char *) &printformat);
+
+ if (fmt->fmt.pix.bytesperline > 0) {
+ dprintk(5,
+ KERN_ERR "%s: bpl not supported\n",
+ ZR_DEVNAME(zr));
+ return -EINVAL;
+ }
+
+ /* we can be requested to do JPEG/raw playback/capture */
+ if (!
+ (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ fmt->fmt.pix.pixelformat ==
+ V4L2_PIX_FMT_MJPEG))) {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_S_FMT - unknown type %d/0x%x(%4.4s) combination\n",
+ ZR_DEVNAME(zr), fmt->type,
+ fmt->fmt.pix.pixelformat,
+ (char *) &printformat);
+ return -EINVAL;
+ }
+
+ if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) {
+ mutex_lock(&zr->resource_lock);
+
+ settings = fh->jpg_settings;
+
+ if (fh->v4l_buffers.allocated ||
+ fh->jpg_buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_S_FMT - cannot change capture mode\n",
+ ZR_DEVNAME(zr));
+ res = -EBUSY;
+ goto sfmtjpg_unlock_and_return;
+ }
+
+ /* we actually need to set 'real' parameters now */
+ if ((fmt->fmt.pix.height * 2) >
+ BUZ_MAX_HEIGHT)
+ settings.TmpDcm = 1;
+ else
+ settings.TmpDcm = 2;
+ settings.decimation = 0;
+ if (fmt->fmt.pix.height <=
+ fh->jpg_settings.img_height / 2)
+ settings.VerDcm = 2;
+ else
+ settings.VerDcm = 1;
+ if (fmt->fmt.pix.width <=
+ fh->jpg_settings.img_width / 4)
+ settings.HorDcm = 4;
+ else if (fmt->fmt.pix.width <=
+ fh->jpg_settings.img_width / 2)
+ settings.HorDcm = 2;
+ else
+ settings.HorDcm = 1;
+ if (settings.TmpDcm == 1)
+ settings.field_per_buff = 2;
+ else
+ settings.field_per_buff = 1;
+
+ /* check */
+ if ((res =
+ zoran_check_jpg_settings(zr,
+ &settings)))
+ goto sfmtjpg_unlock_and_return;
+
+ /* it's ok, so set them */
+ fh->jpg_settings = settings;
+
+ /* tell the user what we actually did */
+ fmt->fmt.pix.width =
+ settings.img_width / settings.HorDcm;
+ fmt->fmt.pix.height =
+ settings.img_height * 2 /
+ (settings.TmpDcm * settings.VerDcm);
+ if (settings.TmpDcm == 1)
+ fmt->fmt.pix.field =
+ (fh->jpg_settings.
+ odd_even ? V4L2_FIELD_SEQ_TB :
+ V4L2_FIELD_SEQ_BT);
+ else
+ fmt->fmt.pix.field =
+ (fh->jpg_settings.
+ odd_even ? V4L2_FIELD_TOP :
+ V4L2_FIELD_BOTTOM);
+ fh->jpg_buffers.buffer_size =
+ zoran_v4l2_calc_bufsize(&fh->
+ jpg_settings);
+ fmt->fmt.pix.sizeimage =
+ fh->jpg_buffers.buffer_size;
+
+ /* we hereby abuse this variable to show that
+ * we're gonna do mjpeg capture */
+ fh->map_mode =
+ (fmt->type ==
+ V4L2_BUF_TYPE_VIDEO_CAPTURE) ?
+ ZORAN_MAP_MODE_JPG_REC :
+ ZORAN_MAP_MODE_JPG_PLAY;
+ sfmtjpg_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ } else {
+ for (i = 0; i < zoran_num_formats; i++)
+ if (fmt->fmt.pix.pixelformat ==
+ zoran_formats[i].fourcc)
+ break;
+ if (i == zoran_num_formats) {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_S_FMT - unknown/unsupported format 0x%x (%4.4s)\n",
+ ZR_DEVNAME(zr),
+ fmt->fmt.pix.pixelformat,
+ (char *) &printformat);
+ return -EINVAL;
+ }
+ mutex_lock(&zr->resource_lock);
+ if (fh->jpg_buffers.allocated ||
+ (fh->v4l_buffers.allocated &&
+ fh->v4l_buffers.active !=
+ ZORAN_FREE)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_S_FMT - cannot change capture mode\n",
+ ZR_DEVNAME(zr));
+ res = -EBUSY;
+ goto sfmtv4l_unlock_and_return;
+ }
+ if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT)
+ fmt->fmt.pix.height =
+ BUZ_MAX_HEIGHT;
+ if (fmt->fmt.pix.width > BUZ_MAX_WIDTH)
+ fmt->fmt.pix.width = BUZ_MAX_WIDTH;
+
+ if ((res =
+ zoran_v4l_set_format(file,
+ fmt->fmt.pix.
+ width,
+ fmt->fmt.pix.
+ height,
+ &zoran_formats
+ [i])))
+ goto sfmtv4l_unlock_and_return;
+
+ /* tell the user the
+ * results/missing stuff */
+ fmt->fmt.pix.sizeimage = fh->v4l_buffers.buffer_size /*zr->gbpl * zr->gheight */
+ ;
+ if (BUZ_MAX_HEIGHT <
+ (fh->v4l_settings.height * 2))
+ fmt->fmt.pix.field =
+ V4L2_FIELD_INTERLACED;
+ else
+ fmt->fmt.pix.field =
+ V4L2_FIELD_TOP;
+
+ fh->map_mode = ZORAN_MAP_MODE_RAW;
+ sfmtv4l_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ }
+
+ break;
+
+ default:
+ dprintk(3, "unsupported\n");
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_S_FMT - unsupported type %d\n",
+ ZR_DEVNAME(zr), fmt->type);
+ return -EINVAL;
+ }
+
+ return res;
+ }
+ break;
+
+ case VIDIOC_G_FBUF:
+ {
+ struct v4l2_framebuffer *fb = arg;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_G_FBUF\n", ZR_DEVNAME(zr));
+
+ memset(fb, 0, sizeof(*fb));
+ mutex_lock(&zr->resource_lock);
+ fb->base = zr->buffer.base;
+ fb->fmt.width = zr->buffer.width;
+ fb->fmt.height = zr->buffer.height;
+ if (zr->overlay_settings.format) {
+ fb->fmt.pixelformat =
+ fh->overlay_settings.format->fourcc;
+ }
+ fb->fmt.bytesperline = zr->buffer.bytesperline;
+ mutex_unlock(&zr->resource_lock);
+ fb->fmt.colorspace = V4L2_COLORSPACE_SRGB;
+ fb->fmt.field = V4L2_FIELD_INTERLACED;
+ fb->flags = V4L2_FBUF_FLAG_OVERLAY;
+ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+
+ return 0;
+ }
+ break;
+
+ case VIDIOC_S_FBUF:
+ {
+ int i, res = 0;
+ struct v4l2_framebuffer *fb = arg;
+ __u32 printformat = __cpu_to_le32(fb->fmt.pixelformat);
+
+ dprintk(3,
+ KERN_DEBUG
+ "%s: VIDIOC_S_FBUF - base=0x%p, size=%dx%d, bpl=%d, fmt=0x%x (%4.4s)\n",
+ ZR_DEVNAME(zr), fb->base, fb->fmt.width, fb->fmt.height,
+ fb->fmt.bytesperline, fb->fmt.pixelformat,
+ (char *) &printformat);
+
+ for (i = 0; i < zoran_num_formats; i++)
+ if (zoran_formats[i].fourcc == fb->fmt.pixelformat)
+ break;
+ if (i == zoran_num_formats) {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_S_FBUF - format=0x%x (%4.4s) not allowed\n",
+ ZR_DEVNAME(zr), fb->fmt.pixelformat,
+ (char *) &printformat);
+ return -EINVAL;
+ }
+
+ mutex_lock(&zr->resource_lock);
+ res =
+ setup_fbuffer(file, fb->base, &zoran_formats[i],
+ fb->fmt.width, fb->fmt.height,
+ fb->fmt.bytesperline);
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+ break;
+
+ case VIDIOC_OVERLAY:
+ {
+ int *on = arg, res;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_PREVIEW - on=%d\n",
+ ZR_DEVNAME(zr), *on);
+
+ mutex_lock(&zr->resource_lock);
+ res = setup_overlay(file, *on);
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+ break;
+
+ case VIDIOC_REQBUFS:
+ {
+ struct v4l2_requestbuffers *req = arg;
+ int res = 0;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_REQBUFS - type=%d\n",
+ ZR_DEVNAME(zr), req->type);
+
+ if (req->memory != V4L2_MEMORY_MMAP) {
+ dprintk(1,
+ KERN_ERR
+ "%s: only MEMORY_MMAP capture is supported, not %d\n",
+ ZR_DEVNAME(zr), req->memory);
+ return -EINVAL;
+ }
+
+ mutex_lock(&zr->resource_lock);
+
+ if (fh->v4l_buffers.allocated || fh->jpg_buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_REQBUFS - buffers allready allocated\n",
+ ZR_DEVNAME(zr));
+ res = -EBUSY;
+ goto v4l2reqbuf_unlock_and_return;
+ }
+
+ if (fh->map_mode == ZORAN_MAP_MODE_RAW &&
+ req->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+
+ /* control user input */
+ if (req->count < 2)
+ req->count = 2;
+ if (req->count > v4l_nbufs)
+ req->count = v4l_nbufs;
+ fh->v4l_buffers.num_buffers = req->count;
+
+ if (v4l_fbuffer_alloc(file)) {
+ res = -ENOMEM;
+ goto v4l2reqbuf_unlock_and_return;
+ }
+
+ /* The next mmap will map the V4L buffers */
+ fh->map_mode = ZORAN_MAP_MODE_RAW;
+
+ } else if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC ||
+ fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) {
+
+ /* we need to calculate size ourselves now */
+ if (req->count < 4)
+ req->count = 4;
+ if (req->count > jpg_nbufs)
+ req->count = jpg_nbufs;
+ fh->jpg_buffers.num_buffers = req->count;
+ fh->jpg_buffers.buffer_size =
+ zoran_v4l2_calc_bufsize(&fh->jpg_settings);
+
+ if (jpg_fbuffer_alloc(file)) {
+ res = -ENOMEM;
+ goto v4l2reqbuf_unlock_and_return;
+ }
+
+ /* The next mmap will map the MJPEG buffers */
+ if (req->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ fh->map_mode = ZORAN_MAP_MODE_JPG_REC;
+ else
+ fh->map_mode = ZORAN_MAP_MODE_JPG_PLAY;
+
+ } else {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_REQBUFS - unknown type %d\n",
+ ZR_DEVNAME(zr), req->type);
+ res = -EINVAL;
+ goto v4l2reqbuf_unlock_and_return;
+ }
+ v4l2reqbuf_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return 0;
+ }
+ break;
+
+ case VIDIOC_QUERYBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+ __u32 type = buf->type;
+ int index = buf->index, res;
+
+ dprintk(3,
+ KERN_DEBUG
+ "%s: VIDIOC_QUERYBUF - index=%d, type=%d\n",
+ ZR_DEVNAME(zr), buf->index, buf->type);
+
+ memset(buf, 0, sizeof(buf));
+ buf->type = type;
+ buf->index = index;
+
+ mutex_lock(&zr->resource_lock);
+ res = zoran_v4l2_buffer_status(file, buf, buf->index);
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+ break;
+
+ case VIDIOC_QBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+ int res = 0, codec_mode, buf_type;
+
+ dprintk(3,
+ KERN_DEBUG "%s: VIDIOC_QBUF - type=%d, index=%d\n",
+ ZR_DEVNAME(zr), buf->type, buf->index);
+
+ mutex_lock(&zr->resource_lock);
+
+ switch (fh->map_mode) {
+ case ZORAN_MAP_MODE_RAW:
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
+ ZR_DEVNAME(zr), buf->type, fh->map_mode);
+ res = -EINVAL;
+ goto qbuf_unlock_and_return;
+ }
+
+ res = zoran_v4l_queue_frame(file, buf->index);
+ if (res)
+ goto qbuf_unlock_and_return;
+ if (!zr->v4l_memgrab_active &&
+ fh->v4l_buffers.active == ZORAN_LOCKED)
+ zr36057_set_memgrab(zr, 1);
+ break;
+
+ case ZORAN_MAP_MODE_JPG_REC:
+ case ZORAN_MAP_MODE_JPG_PLAY:
+ if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) {
+ buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ codec_mode = BUZ_MODE_MOTION_DECOMPRESS;
+ } else {
+ buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ codec_mode = BUZ_MODE_MOTION_COMPRESS;
+ }
+
+ if (buf->type != buf_type) {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
+ ZR_DEVNAME(zr), buf->type, fh->map_mode);
+ res = -EINVAL;
+ goto qbuf_unlock_and_return;
+ }
+
+ res =
+ zoran_jpg_queue_frame(file, buf->index,
+ codec_mode);
+ if (res != 0)
+ goto qbuf_unlock_and_return;
+ if (zr->codec_mode == BUZ_MODE_IDLE &&
+ fh->jpg_buffers.active == ZORAN_LOCKED) {
+ zr36057_enable_jpg(zr, codec_mode);
+ }
+ break;
+
+ default:
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_QBUF - unsupported type %d\n",
+ ZR_DEVNAME(zr), buf->type);
+ res = -EINVAL;
+ goto qbuf_unlock_and_return;
+ }
+ qbuf_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+ break;
+
+ case VIDIOC_DQBUF:
+ {
+ struct v4l2_buffer *buf = arg;
+ int res = 0, buf_type, num = -1; /* compiler borks here (?) */
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_DQBUF - type=%d\n",
+ ZR_DEVNAME(zr), buf->type);
+
+ mutex_lock(&zr->resource_lock);
+
+ switch (fh->map_mode) {
+ case ZORAN_MAP_MODE_RAW:
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
+ ZR_DEVNAME(zr), buf->type, fh->map_mode);
+ res = -EINVAL;
+ goto dqbuf_unlock_and_return;
+ }
+
+ num = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME];
+ if (file->f_flags & O_NONBLOCK &&
+ zr->v4l_buffers.buffer[num].state !=
+ BUZ_STATE_DONE) {
+ res = -EAGAIN;
+ goto dqbuf_unlock_and_return;
+ }
+ res = v4l_sync(file, num);
+ if (res)
+ goto dqbuf_unlock_and_return;
+ else
+ zr->v4l_sync_tail++;
+ res = zoran_v4l2_buffer_status(file, buf, num);
+ break;
+
+ case ZORAN_MAP_MODE_JPG_REC:
+ case ZORAN_MAP_MODE_JPG_PLAY:
+ {
+ struct zoran_sync bs;
+
+ if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY)
+ buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ else
+ buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ if (buf->type != buf_type) {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
+ ZR_DEVNAME(zr), buf->type, fh->map_mode);
+ res = -EINVAL;
+ goto dqbuf_unlock_and_return;
+ }
+
+ num =
+ zr->jpg_pend[zr->
+ jpg_que_tail & BUZ_MASK_FRAME];
+
+ if (file->f_flags & O_NONBLOCK &&
+ zr->jpg_buffers.buffer[num].state !=
+ BUZ_STATE_DONE) {
+ res = -EAGAIN;
+ goto dqbuf_unlock_and_return;
+ }
+ res = jpg_sync(file, &bs);
+ if (res)
+ goto dqbuf_unlock_and_return;
+ res =
+ zoran_v4l2_buffer_status(file, buf, bs.frame);
+ break;
+ }
+
+ default:
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_DQBUF - unsupported type %d\n",
+ ZR_DEVNAME(zr), buf->type);
+ res = -EINVAL;
+ goto dqbuf_unlock_and_return;
+ }
+ dqbuf_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+ break;
+
+ case VIDIOC_STREAMON:
+ {
+ int res = 0;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_STREAMON\n", ZR_DEVNAME(zr));
+
+ mutex_lock(&zr->resource_lock);
+
+ switch (fh->map_mode) {
+ case ZORAN_MAP_MODE_RAW: /* raw capture */
+ if (zr->v4l_buffers.active != ZORAN_ACTIVE ||
+ fh->v4l_buffers.active != ZORAN_ACTIVE) {
+ res = -EBUSY;
+ goto strmon_unlock_and_return;
+ }
+
+ zr->v4l_buffers.active = fh->v4l_buffers.active =
+ ZORAN_LOCKED;
+ zr->v4l_settings = fh->v4l_settings;
+
+ zr->v4l_sync_tail = zr->v4l_pend_tail;
+ if (!zr->v4l_memgrab_active &&
+ zr->v4l_pend_head != zr->v4l_pend_tail) {
+ zr36057_set_memgrab(zr, 1);
+ }
+ break;
+
+ case ZORAN_MAP_MODE_JPG_REC:
+ case ZORAN_MAP_MODE_JPG_PLAY:
+ /* what is the codec mode right now? */
+ if (zr->jpg_buffers.active != ZORAN_ACTIVE ||
+ fh->jpg_buffers.active != ZORAN_ACTIVE) {
+ res = -EBUSY;
+ goto strmon_unlock_and_return;
+ }
+
+ zr->jpg_buffers.active = fh->jpg_buffers.active =
+ ZORAN_LOCKED;
+
+ if (zr->jpg_que_head != zr->jpg_que_tail) {
+ /* Start the jpeg codec when the first frame is queued */
+ jpeg_start(zr);
+ }
+
+ break;
+ default:
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_STREAMON - invalid map mode %d\n",
+ ZR_DEVNAME(zr), fh->map_mode);
+ res = -EINVAL;
+ goto strmon_unlock_and_return;
+ }
+ strmon_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+ break;
+
+ case VIDIOC_STREAMOFF:
+ {
+ int i, res = 0;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_STREAMOFF\n", ZR_DEVNAME(zr));
+
+ mutex_lock(&zr->resource_lock);
+
+ switch (fh->map_mode) {
+ case ZORAN_MAP_MODE_RAW: /* raw capture */
+ if (fh->v4l_buffers.active == ZORAN_FREE &&
+ zr->v4l_buffers.active != ZORAN_FREE) {
+ res = -EPERM; /* stay off other's settings! */
+ goto strmoff_unlock_and_return;
+ }
+ if (zr->v4l_buffers.active == ZORAN_FREE)
+ goto strmoff_unlock_and_return;
+
+ /* unload capture */
+ if (zr->v4l_memgrab_active)
+ zr36057_set_memgrab(zr, 0);
+
+ for (i = 0; i < fh->v4l_buffers.num_buffers; i++)
+ zr->v4l_buffers.buffer[i].state =
+ BUZ_STATE_USER;
+ fh->v4l_buffers = zr->v4l_buffers;
+
+ zr->v4l_buffers.active = fh->v4l_buffers.active =
+ ZORAN_FREE;
+
+ zr->v4l_grab_seq = 0;
+ zr->v4l_pend_head = zr->v4l_pend_tail = 0;
+ zr->v4l_sync_tail = 0;
+
+ break;
+
+ case ZORAN_MAP_MODE_JPG_REC:
+ case ZORAN_MAP_MODE_JPG_PLAY:
+ if (fh->jpg_buffers.active == ZORAN_FREE &&
+ zr->jpg_buffers.active != ZORAN_FREE) {
+ res = -EPERM; /* stay off other's settings! */
+ goto strmoff_unlock_and_return;
+ }
+ if (zr->jpg_buffers.active == ZORAN_FREE)
+ goto strmoff_unlock_and_return;
+
+ res =
+ jpg_qbuf(file, -1,
+ (fh->map_mode ==
+ ZORAN_MAP_MODE_JPG_REC) ?
+ BUZ_MODE_MOTION_COMPRESS :
+ BUZ_MODE_MOTION_DECOMPRESS);
+ if (res)
+ goto strmoff_unlock_and_return;
+ break;
+ default:
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_STREAMOFF - invalid map mode %d\n",
+ ZR_DEVNAME(zr), fh->map_mode);
+ res = -EINVAL;
+ goto strmoff_unlock_and_return;
+ }
+ strmoff_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+ break;
+
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *ctrl = arg;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_QUERYCTRL - id=%d\n",
+ ZR_DEVNAME(zr), ctrl->id);
+
+ /* we only support hue/saturation/contrast/brightness */
+ if (ctrl->id < V4L2_CID_BRIGHTNESS ||
+ ctrl->id > V4L2_CID_HUE)
+ return -EINVAL;
+ else {
+ int id = ctrl->id;
+ memset(ctrl, 0, sizeof(*ctrl));
+ ctrl->id = id;
+ }
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ strncpy(ctrl->name, "Brightness", 31);
+ break;
+ case V4L2_CID_CONTRAST:
+ strncpy(ctrl->name, "Contrast", 31);
+ break;
+ case V4L2_CID_SATURATION:
+ strncpy(ctrl->name, "Saturation", 31);
+ break;
+ case V4L2_CID_HUE:
+ strncpy(ctrl->name, "Hue", 31);
+ break;
+ }
+
+ ctrl->minimum = 0;
+ ctrl->maximum = 65535;
+ ctrl->step = 1;
+ ctrl->default_value = 32768;
+ ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+
+ return 0;
+ }
+ break;
+
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_G_CTRL - id=%d\n",
+ ZR_DEVNAME(zr), ctrl->id);
+
+ /* we only support hue/saturation/contrast/brightness */
+ if (ctrl->id < V4L2_CID_BRIGHTNESS ||
+ ctrl->id > V4L2_CID_HUE)
+ return -EINVAL;
+
+ mutex_lock(&zr->resource_lock);
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = zr->brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = zr->contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = zr->saturation;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = zr->hue;
+ break;
+ }
+ mutex_unlock(&zr->resource_lock);
+
+ return 0;
+ }
+ break;
+
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+ struct video_picture pict;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_S_CTRL - id=%d\n",
+ ZR_DEVNAME(zr), ctrl->id);
+
+ /* we only support hue/saturation/contrast/brightness */
+ if (ctrl->id < V4L2_CID_BRIGHTNESS ||
+ ctrl->id > V4L2_CID_HUE)
+ return -EINVAL;
+
+ if (ctrl->value < 0 || ctrl->value > 65535) {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_S_CTRL - invalid value %d for id=%d\n",
+ ZR_DEVNAME(zr), ctrl->value, ctrl->id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&zr->resource_lock);
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ zr->brightness = ctrl->value;
+ break;
+ case V4L2_CID_CONTRAST:
+ zr->contrast = ctrl->value;
+ break;
+ case V4L2_CID_SATURATION:
+ zr->saturation = ctrl->value;
+ break;
+ case V4L2_CID_HUE:
+ zr->hue = ctrl->value;
+ break;
+ }
+ pict.brightness = zr->brightness;
+ pict.contrast = zr->contrast;
+ pict.colour = zr->saturation;
+ pict.hue = zr->hue;
+
+ decoder_command(zr, DECODER_SET_PICTURE, &pict);
+
+ mutex_unlock(&zr->resource_lock);
+
+ return 0;
+ }
+ break;
+
+ case VIDIOC_ENUMSTD:
+ {
+ struct v4l2_standard *std = arg;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_ENUMSTD - index=%d\n",
+ ZR_DEVNAME(zr), std->index);
+
+ if (std->index < 0 || std->index >= (zr->card.norms + 1))
+ return -EINVAL;
+ else {
+ int id = std->index;
+ memset(std, 0, sizeof(*std));
+ std->index = id;
+ }
+
+ if (std->index == zr->card.norms) {
+ /* if we have autodetect, ... */
+ struct video_decoder_capability caps;
+ decoder_command(zr, DECODER_GET_CAPABILITIES,
+ &caps);
+ if (caps.flags & VIDEO_DECODER_AUTO) {
+ std->id = V4L2_STD_ALL;
+ strncpy(std->name, "Autodetect", 31);
+ return 0;
+ } else
+ return -EINVAL;
+ }
+ switch (std->index) {
+ case 0:
+ std->id = V4L2_STD_PAL;
+ strncpy(std->name, "PAL", 31);
+ std->frameperiod.numerator = 1;
+ std->frameperiod.denominator = 25;
+ std->framelines = zr->card.tvn[0]->Ht;
+ break;
+ case 1:
+ std->id = V4L2_STD_NTSC;
+ strncpy(std->name, "NTSC", 31);
+ std->frameperiod.numerator = 1001;
+ std->frameperiod.denominator = 30000;
+ std->framelines = zr->card.tvn[1]->Ht;
+ break;
+ case 2:
+ std->id = V4L2_STD_SECAM;
+ strncpy(std->name, "SECAM", 31);
+ std->frameperiod.numerator = 1;
+ std->frameperiod.denominator = 25;
+ std->framelines = zr->card.tvn[2]->Ht;
+ break;
+ }
+
+ return 0;
+ }
+ break;
+
+ case VIDIOC_G_STD:
+ {
+ v4l2_std_id *std = arg;
+ int norm;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_G_STD\n", ZR_DEVNAME(zr));
+
+ mutex_lock(&zr->resource_lock);
+ norm = zr->norm;
+ mutex_unlock(&zr->resource_lock);
+
+ switch (norm) {
+ case VIDEO_MODE_PAL:
+ *std = V4L2_STD_PAL;
+ break;
+ case VIDEO_MODE_NTSC:
+ *std = V4L2_STD_NTSC;
+ break;
+ case VIDEO_MODE_SECAM:
+ *std = V4L2_STD_SECAM;
+ break;
+ }
+
+ return 0;
+ }
+ break;
+
+ case VIDIOC_S_STD:
+ {
+ int norm = -1, res = 0;
+ v4l2_std_id *std = arg;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_S_STD - norm=0x%llx\n",
+ ZR_DEVNAME(zr), (unsigned long long)*std);
+
+ if (*std == V4L2_STD_PAL)
+ norm = VIDEO_MODE_PAL;
+ else if (*std == V4L2_STD_NTSC)
+ norm = VIDEO_MODE_NTSC;
+ else if (*std == V4L2_STD_SECAM)
+ norm = VIDEO_MODE_SECAM;
+ else if (*std == V4L2_STD_ALL)
+ norm = VIDEO_MODE_AUTO;
+ else {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_S_STD - invalid norm 0x%llx\n",
+ ZR_DEVNAME(zr), (unsigned long long)*std);
+ return -EINVAL;
+ }
+
+ mutex_lock(&zr->resource_lock);
+ if ((res = zoran_set_norm(zr, norm)))
+ goto sstd_unlock_and_return;
+
+ res = wait_grab_pending(zr);
+ sstd_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ return res;
+ }
+ break;
+
+ case VIDIOC_ENUMINPUT:
+ {
+ struct v4l2_input *inp = arg;
+ int status;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_ENUMINPUT - index=%d\n",
+ ZR_DEVNAME(zr), inp->index);
+
+ if (inp->index < 0 || inp->index >= zr->card.inputs)
+ return -EINVAL;
+ else {
+ int id = inp->index;
+ memset(inp, 0, sizeof(*inp));
+ inp->index = id;
+ }
+
+ strncpy(inp->name, zr->card.input[inp->index].name,
+ sizeof(inp->name) - 1);
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = V4L2_STD_ALL;
+
+ /* Get status of video decoder */
+ mutex_lock(&zr->resource_lock);
+ decoder_command(zr, DECODER_GET_STATUS, &status);
+ mutex_unlock(&zr->resource_lock);
+
+ if (!(status & DECODER_STATUS_GOOD)) {
+ inp->status |= V4L2_IN_ST_NO_POWER;
+ inp->status |= V4L2_IN_ST_NO_SIGNAL;
+ }
+ if (!(status & DECODER_STATUS_COLOR))
+ inp->status |= V4L2_IN_ST_NO_COLOR;
+
+ return 0;
+ }
+ break;
+
+ case VIDIOC_G_INPUT:
+ {
+ int *input = arg;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_G_INPUT\n", ZR_DEVNAME(zr));
+
+ mutex_lock(&zr->resource_lock);
+ *input = zr->input;
+ mutex_unlock(&zr->resource_lock);
+
+ return 0;
+ }
+ break;
+
+ case VIDIOC_S_INPUT:
+ {
+ int *input = arg, res = 0;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_S_INPUT - input=%d\n",
+ ZR_DEVNAME(zr), *input);
+
+ mutex_lock(&zr->resource_lock);
+ if ((res = zoran_set_input(zr, *input)))
+ goto sinput_unlock_and_return;
+
+ /* Make sure the changes come into effect */
+ res = wait_grab_pending(zr);
+ sinput_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ return res;
+ }
+ break;
+
+ case VIDIOC_ENUMOUTPUT:
+ {
+ struct v4l2_output *outp = arg;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_ENUMOUTPUT - index=%d\n",
+ ZR_DEVNAME(zr), outp->index);
+
+ if (outp->index != 0)
+ return -EINVAL;
+
+ memset(outp, 0, sizeof(*outp));
+ outp->index = 0;
+ outp->type = V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY;
+ strncpy(outp->name, "Autodetect", 31);
+
+ return 0;
+ }
+ break;
+
+ case VIDIOC_G_OUTPUT:
+ {
+ int *output = arg;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_G_OUTPUT\n", ZR_DEVNAME(zr));
+
+ *output = 0;
+
+ return 0;
+ }
+ break;
+
+ case VIDIOC_S_OUTPUT:
+ {
+ int *output = arg;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_S_OUTPUT - output=%d\n",
+ ZR_DEVNAME(zr), *output);
+
+ if (*output != 0)
+ return -EINVAL;
+
+ return 0;
+ }
+ break;
+
+ /* cropping (sub-frame capture) */
+ case VIDIOC_CROPCAP:
+ {
+ struct v4l2_cropcap *cropcap = arg;
+ int type = cropcap->type, res = 0;
+
+ dprintk(3, KERN_ERR "%s: VIDIOC_CROPCAP - type=%d\n",
+ ZR_DEVNAME(zr), cropcap->type);
+
+ memset(cropcap, 0, sizeof(*cropcap));
+ cropcap->type = type;
+
+ mutex_lock(&zr->resource_lock);
+
+ if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ fh->map_mode == ZORAN_MAP_MODE_RAW)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_CROPCAP - subcapture only supported for compressed capture\n",
+ ZR_DEVNAME(zr));
+ res = -EINVAL;
+ goto cropcap_unlock_and_return;
+ }
+
+ cropcap->bounds.top = cropcap->bounds.left = 0;
+ cropcap->bounds.width = BUZ_MAX_WIDTH;
+ cropcap->bounds.height = BUZ_MAX_HEIGHT;
+ cropcap->defrect.top = cropcap->defrect.left = 0;
+ cropcap->defrect.width = BUZ_MIN_WIDTH;
+ cropcap->defrect.height = BUZ_MIN_HEIGHT;
+ cropcap_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ return res;
+ }
+ break;
+
+ case VIDIOC_G_CROP:
+ {
+ struct v4l2_crop *crop = arg;
+ int type = crop->type, res = 0;
+
+ dprintk(3, KERN_ERR "%s: VIDIOC_G_CROP - type=%d\n",
+ ZR_DEVNAME(zr), crop->type);
+
+ memset(crop, 0, sizeof(*crop));
+ crop->type = type;
+
+ mutex_lock(&zr->resource_lock);
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ fh->map_mode == ZORAN_MAP_MODE_RAW)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n",
+ ZR_DEVNAME(zr));
+ res = -EINVAL;
+ goto gcrop_unlock_and_return;
+ }
+
+ crop->c.top = fh->jpg_settings.img_y;
+ crop->c.left = fh->jpg_settings.img_x;
+ crop->c.width = fh->jpg_settings.img_width;
+ crop->c.height = fh->jpg_settings.img_height;
+
+ gcrop_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+ break;
+
+ case VIDIOC_S_CROP:
+ {
+ struct v4l2_crop *crop = arg;
+ int res = 0;
+
+ settings = fh->jpg_settings;
+
+ dprintk(3,
+ KERN_ERR
+ "%s: VIDIOC_S_CROP - type=%d, x=%d,y=%d,w=%d,h=%d\n",
+ ZR_DEVNAME(zr), crop->type, crop->c.left, crop->c.top,
+ crop->c.width, crop->c.height);
+
+ mutex_lock(&zr->resource_lock);
+
+ if (fh->jpg_buffers.allocated || fh->v4l_buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_S_CROP - cannot change settings while active\n",
+ ZR_DEVNAME(zr));
+ res = -EBUSY;
+ goto scrop_unlock_and_return;
+ }
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ fh->map_mode == ZORAN_MAP_MODE_RAW)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n",
+ ZR_DEVNAME(zr));
+ res = -EINVAL;
+ goto scrop_unlock_and_return;
+ }
+
+ /* move into a form that we understand */
+ settings.img_x = crop->c.left;
+ settings.img_y = crop->c.top;
+ settings.img_width = crop->c.width;
+ settings.img_height = crop->c.height;
+
+ /* check validity */
+ if ((res = zoran_check_jpg_settings(zr, &settings)))
+ goto scrop_unlock_and_return;
+
+ /* accept */
+ fh->jpg_settings = settings;
+
+ scrop_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ return res;
+ }
+ break;
+
+ case VIDIOC_G_JPEGCOMP:
+ {
+ struct v4l2_jpegcompression *params = arg;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_G_JPEGCOMP\n",
+ ZR_DEVNAME(zr));
+
+ memset(params, 0, sizeof(*params));
+
+ mutex_lock(&zr->resource_lock);
+
+ params->quality = fh->jpg_settings.jpg_comp.quality;
+ params->APPn = fh->jpg_settings.jpg_comp.APPn;
+ memcpy(params->APP_data,
+ fh->jpg_settings.jpg_comp.APP_data,
+ fh->jpg_settings.jpg_comp.APP_len);
+ params->APP_len = fh->jpg_settings.jpg_comp.APP_len;
+ memcpy(params->COM_data,
+ fh->jpg_settings.jpg_comp.COM_data,
+ fh->jpg_settings.jpg_comp.COM_len);
+ params->COM_len = fh->jpg_settings.jpg_comp.COM_len;
+ params->jpeg_markers =
+ fh->jpg_settings.jpg_comp.jpeg_markers;
+
+ mutex_unlock(&zr->resource_lock);
+
+ return 0;
+ }
+ break;
+
+ case VIDIOC_S_JPEGCOMP:
+ {
+ struct v4l2_jpegcompression *params = arg;
+ int res = 0;
+
+ settings = fh->jpg_settings;
+
+ dprintk(3,
+ KERN_DEBUG
+ "%s: VIDIOC_S_JPEGCOMP - quality=%d, APPN=%d, APP_len=%d, COM_len=%d\n",
+ ZR_DEVNAME(zr), params->quality, params->APPn,
+ params->APP_len, params->COM_len);
+
+ settings.jpg_comp = *params;
+
+ mutex_lock(&zr->resource_lock);
+
+ if (fh->v4l_buffers.active != ZORAN_FREE ||
+ fh->jpg_buffers.active != ZORAN_FREE) {
+ dprintk(1,
+ KERN_WARNING
+ "%s: VIDIOC_S_JPEGCOMP called while in playback/capture mode\n",
+ ZR_DEVNAME(zr));
+ res = -EBUSY;
+ goto sjpegc_unlock_and_return;
+ }
+
+ if ((res = zoran_check_jpg_settings(zr, &settings)))
+ goto sjpegc_unlock_and_return;
+ if (!fh->jpg_buffers.allocated)
+ fh->jpg_buffers.buffer_size =
+ zoran_v4l2_calc_bufsize(&fh->jpg_settings);
+ fh->jpg_settings.jpg_comp = *params = settings.jpg_comp;
+ sjpegc_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return 0;
+ }
+ break;
+
+ case VIDIOC_QUERYSTD: /* why is this useful? */
+ {
+ v4l2_std_id *std = arg;
+
+ dprintk(3,
+ KERN_DEBUG "%s: VIDIOC_QUERY_STD - std=0x%llx\n",
+ ZR_DEVNAME(zr), (unsigned long long)*std);
+
+ if (*std == V4L2_STD_ALL || *std == V4L2_STD_NTSC ||
+ *std == V4L2_STD_PAL || (*std == V4L2_STD_SECAM &&
+ zr->card.norms == 3)) {
+ return 0;
+ }
+
+ return -EINVAL;
+ }
+ break;
+
+ case VIDIOC_TRY_FMT:
+ {
+ struct v4l2_format *fmt = arg;
+ int res = 0;
+
+ dprintk(3, KERN_DEBUG "%s: VIDIOC_TRY_FMT - type=%d\n",
+ ZR_DEVNAME(zr), fmt->type);
+
+ switch (fmt->type) {
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ mutex_lock(&zr->resource_lock);
+
+ if (fmt->fmt.win.w.width > BUZ_MAX_WIDTH)
+ fmt->fmt.win.w.width = BUZ_MAX_WIDTH;
+ if (fmt->fmt.win.w.width < BUZ_MIN_WIDTH)
+ fmt->fmt.win.w.width = BUZ_MIN_WIDTH;
+ if (fmt->fmt.win.w.height > BUZ_MAX_HEIGHT)
+ fmt->fmt.win.w.height = BUZ_MAX_HEIGHT;
+ if (fmt->fmt.win.w.height < BUZ_MIN_HEIGHT)
+ fmt->fmt.win.w.height = BUZ_MIN_HEIGHT;
+
+ mutex_unlock(&zr->resource_lock);
+ break;
+
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ if (fmt->fmt.pix.bytesperline > 0)
+ return -EINVAL;
+
+ mutex_lock(&zr->resource_lock);
+
+ if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) {
+ settings = fh->jpg_settings;
+
+ /* we actually need to set 'real' parameters now */
+ if ((fmt->fmt.pix.height * 2) >
+ BUZ_MAX_HEIGHT)
+ settings.TmpDcm = 1;
+ else
+ settings.TmpDcm = 2;
+ settings.decimation = 0;
+ if (fmt->fmt.pix.height <=
+ fh->jpg_settings.img_height / 2)
+ settings.VerDcm = 2;
+ else
+ settings.VerDcm = 1;
+ if (fmt->fmt.pix.width <=
+ fh->jpg_settings.img_width / 4)
+ settings.HorDcm = 4;
+ else if (fmt->fmt.pix.width <=
+ fh->jpg_settings.img_width / 2)
+ settings.HorDcm = 2;
+ else
+ settings.HorDcm = 1;
+ if (settings.TmpDcm == 1)
+ settings.field_per_buff = 2;
+ else
+ settings.field_per_buff = 1;
+
+ /* check */
+ if ((res =
+ zoran_check_jpg_settings(zr,
+ &settings)))
+ goto tryfmt_unlock_and_return;
+
+ /* tell the user what we actually did */
+ fmt->fmt.pix.width =
+ settings.img_width / settings.HorDcm;
+ fmt->fmt.pix.height =
+ settings.img_height * 2 /
+ (settings.TmpDcm * settings.VerDcm);
+ if (settings.TmpDcm == 1)
+ fmt->fmt.pix.field =
+ (fh->jpg_settings.
+ odd_even ? V4L2_FIELD_SEQ_TB :
+ V4L2_FIELD_SEQ_BT);
+ else
+ fmt->fmt.pix.field =
+ (fh->jpg_settings.
+ odd_even ? V4L2_FIELD_TOP :
+ V4L2_FIELD_BOTTOM);
+
+ fmt->fmt.pix.sizeimage =
+ zoran_v4l2_calc_bufsize(&settings);
+ } else if (fmt->type ==
+ V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ int i;
+
+ for (i = 0; i < zoran_num_formats; i++)
+ if (zoran_formats[i].fourcc ==
+ fmt->fmt.pix.pixelformat)
+ break;
+ if (i == zoran_num_formats) {
+ res = -EINVAL;
+ goto tryfmt_unlock_and_return;
+ }
+
+ if (fmt->fmt.pix.width > BUZ_MAX_WIDTH)
+ fmt->fmt.pix.width = BUZ_MAX_WIDTH;
+ if (fmt->fmt.pix.width < BUZ_MIN_WIDTH)
+ fmt->fmt.pix.width = BUZ_MIN_WIDTH;
+ if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT)
+ fmt->fmt.pix.height =
+ BUZ_MAX_HEIGHT;
+ if (fmt->fmt.pix.height < BUZ_MIN_HEIGHT)
+ fmt->fmt.pix.height =
+ BUZ_MIN_HEIGHT;
+ } else {
+ res = -EINVAL;
+ goto tryfmt_unlock_and_return;
+ }
+ tryfmt_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+ break;
+#endif
+
+ default:
+ dprintk(1, KERN_DEBUG "%s: UNKNOWN ioctl cmd: 0x%x\n",
+ ZR_DEVNAME(zr), cmd);
+ return -ENOIOCTLCMD;
+ break;
+
+ }
+ return 0;
+}
+
+
+static int
+zoran_ioctl (struct inode *inode,
+ struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(inode, file, cmd, arg, zoran_do_ioctl);
+}
+
+static unsigned int
+zoran_poll (struct file *file,
+ poll_table *wait)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ wait_queue_head_t *queue = NULL;
+ int res = 0, frame;
+
+ /* we should check whether buffers are ready to be synced on
+ * (w/o waits - O_NONBLOCK) here
+ * if ready for read (sync), return POLLIN|POLLRDNORM,
+ * if ready for write (sync), return POLLOUT|POLLWRNORM,
+ * if error, return POLLERR,
+ * if no buffers queued or so, return POLLNVAL
+ */
+
+ mutex_lock(&zr->resource_lock);
+
+ switch (fh->map_mode) {
+ case ZORAN_MAP_MODE_RAW:
+ if (fh->v4l_buffers.active == ZORAN_FREE ||
+ zr->v4l_pend_head == zr->v4l_pend_tail) {
+ dprintk(1,
+ "%s: zoran_poll() - no buffers queued\n",
+ ZR_DEVNAME(zr));
+ res = POLLNVAL;
+ goto poll_unlock_and_return;
+ }
+ queue = &zr->v4l_capq;
+ frame = zr->v4l_pend[zr->v4l_pend_tail & V4L_MASK_FRAME];
+ poll_wait(file, queue, wait);
+ if (fh->v4l_buffers.buffer[frame].state == BUZ_STATE_DONE)
+ res = POLLIN | POLLRDNORM;
+ break;
+
+ case ZORAN_MAP_MODE_JPG_REC:
+ case ZORAN_MAP_MODE_JPG_PLAY:
+ if (fh->jpg_buffers.active == ZORAN_FREE ||
+ zr->jpg_que_head == zr->jpg_que_tail) {
+ dprintk(1,
+ "%s: zoran_poll() - no buffers queued\n",
+ ZR_DEVNAME(zr));
+ res = POLLNVAL;
+ goto poll_unlock_and_return;
+ }
+ queue = &zr->jpg_capq;
+ frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME];
+ poll_wait(file, queue, wait);
+ if (fh->jpg_buffers.buffer[frame].state == BUZ_STATE_DONE) {
+ if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC)
+ res = POLLIN | POLLRDNORM;
+ else
+ res = POLLOUT | POLLWRNORM;
+ }
+ break;
+
+ default:
+ dprintk(1,
+ "%s: zoran_poll() - internal error, unknown map_mode=%d\n",
+ ZR_DEVNAME(zr), fh->map_mode);
+ res = POLLNVAL;
+ goto poll_unlock_and_return;
+ }
+
+poll_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+
+/*
+ * This maps the buffers to user space.
+ *
+ * Depending on the state of fh->map_mode
+ * the V4L or the MJPEG buffers are mapped
+ * per buffer or all together
+ *
+ * Note that we need to connect to some
+ * unmap signal event to unmap the de-allocate
+ * the buffer accordingly (zoran_vm_close())
+ */
+
+static void
+zoran_vm_open (struct vm_area_struct *vma)
+{
+ struct zoran_mapping *map = vma->vm_private_data;
+
+ map->count++;
+}
+
+static void
+zoran_vm_close (struct vm_area_struct *vma)
+{
+ struct zoran_mapping *map = vma->vm_private_data;
+ struct file *file = map->file;
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ int i;
+
+ map->count--;
+ if (map->count == 0) {
+ switch (fh->map_mode) {
+ case ZORAN_MAP_MODE_JPG_REC:
+ case ZORAN_MAP_MODE_JPG_PLAY:
+
+ dprintk(3, KERN_INFO "%s: munmap(MJPEG)\n",
+ ZR_DEVNAME(zr));
+
+ for (i = 0; i < fh->jpg_buffers.num_buffers; i++) {
+ if (fh->jpg_buffers.buffer[i].map == map) {
+ fh->jpg_buffers.buffer[i].map =
+ NULL;
+ }
+ }
+ kfree(map);
+
+ for (i = 0; i < fh->jpg_buffers.num_buffers; i++)
+ if (fh->jpg_buffers.buffer[i].map)
+ break;
+ if (i == fh->jpg_buffers.num_buffers) {
+ mutex_lock(&zr->resource_lock);
+
+ if (fh->jpg_buffers.active != ZORAN_FREE) {
+ jpg_qbuf(file, -1, zr->codec_mode);
+ zr->jpg_buffers.allocated = 0;
+ zr->jpg_buffers.active =
+ fh->jpg_buffers.active =
+ ZORAN_FREE;
+ }
+ //jpg_fbuffer_free(file);
+ fh->jpg_buffers.allocated = 0;
+ fh->jpg_buffers.ready_to_be_freed = 1;
+
+ mutex_unlock(&zr->resource_lock);
+ }
+
+ break;
+
+ case ZORAN_MAP_MODE_RAW:
+
+ dprintk(3, KERN_INFO "%s: munmap(V4L)\n",
+ ZR_DEVNAME(zr));
+
+ for (i = 0; i < fh->v4l_buffers.num_buffers; i++) {
+ if (fh->v4l_buffers.buffer[i].map == map) {
+ /* unqueue/unmap */
+ fh->v4l_buffers.buffer[i].map =
+ NULL;
+ }
+ }
+ kfree(map);
+
+ for (i = 0; i < fh->v4l_buffers.num_buffers; i++)
+ if (fh->v4l_buffers.buffer[i].map)
+ break;
+ if (i == fh->v4l_buffers.num_buffers) {
+ mutex_lock(&zr->resource_lock);
+
+ if (fh->v4l_buffers.active != ZORAN_FREE) {
+ zr36057_set_memgrab(zr, 0);
+ zr->v4l_buffers.allocated = 0;
+ zr->v4l_buffers.active =
+ fh->v4l_buffers.active =
+ ZORAN_FREE;
+ }
+ //v4l_fbuffer_free(file);
+ fh->v4l_buffers.allocated = 0;
+ fh->v4l_buffers.ready_to_be_freed = 1;
+
+ mutex_unlock(&zr->resource_lock);
+ }
+
+ break;
+
+ default:
+ printk(KERN_ERR
+ "%s: munmap() - internal error - unknown map mode %d\n",
+ ZR_DEVNAME(zr), fh->map_mode);
+ break;
+
+ }
+ }
+}
+
+static struct vm_operations_struct zoran_vm_ops = {
+ .open = zoran_vm_open,
+ .close = zoran_vm_close,
+};
+
+static int
+zoran_mmap (struct file *file,
+ struct vm_area_struct *vma)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ unsigned long size = (vma->vm_end - vma->vm_start);
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ int i, j;
+ unsigned long page, start = vma->vm_start, todo, pos, fraglen;
+ int first, last;
+ struct zoran_mapping *map;
+ int res = 0;
+
+ dprintk(3,
+ KERN_INFO "%s: mmap(%s) of 0x%08lx-0x%08lx (size=%lu)\n",
+ ZR_DEVNAME(zr),
+ fh->map_mode == ZORAN_MAP_MODE_RAW ? "V4L" : "MJPEG",
+ vma->vm_start, vma->vm_end, size);
+
+ if (!(vma->vm_flags & VM_SHARED) || !(vma->vm_flags & VM_READ) ||
+ !(vma->vm_flags & VM_WRITE)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: mmap() - no MAP_SHARED/PROT_{READ,WRITE} given\n",
+ ZR_DEVNAME(zr));
+ return -EINVAL;
+ }
+
+ switch (fh->map_mode) {
+
+ case ZORAN_MAP_MODE_JPG_REC:
+ case ZORAN_MAP_MODE_JPG_PLAY:
+
+ /* lock */
+ mutex_lock(&zr->resource_lock);
+
+ /* Map the MJPEG buffers */
+ if (!fh->jpg_buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+ "%s: zoran_mmap(MJPEG) - buffers not yet allocated\n",
+ ZR_DEVNAME(zr));
+ res = -ENOMEM;
+ goto jpg_mmap_unlock_and_return;
+ }
+
+ first = offset / fh->jpg_buffers.buffer_size;
+ last = first - 1 + size / fh->jpg_buffers.buffer_size;
+ if (offset % fh->jpg_buffers.buffer_size != 0 ||
+ size % fh->jpg_buffers.buffer_size != 0 || first < 0 ||
+ last < 0 || first >= fh->jpg_buffers.num_buffers ||
+ last >= fh->jpg_buffers.num_buffers) {
+ dprintk(1,
+ KERN_ERR
+ "%s: mmap(MJPEG) - offset=%lu or size=%lu invalid for bufsize=%d and numbufs=%d\n",
+ ZR_DEVNAME(zr), offset, size,
+ fh->jpg_buffers.buffer_size,
+ fh->jpg_buffers.num_buffers);
+ res = -EINVAL;
+ goto jpg_mmap_unlock_and_return;
+ }
+ for (i = first; i <= last; i++) {
+ if (fh->jpg_buffers.buffer[i].map) {
+ dprintk(1,
+ KERN_ERR
+ "%s: mmap(MJPEG) - buffer %d already mapped\n",
+ ZR_DEVNAME(zr), i);
+ res = -EBUSY;
+ goto jpg_mmap_unlock_and_return;
+ }
+ }
+
+ /* map these buffers (v4l_buffers[i]) */
+ map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL);
+ if (!map) {
+ res = -ENOMEM;
+ goto jpg_mmap_unlock_and_return;
+ }
+ map->file = file;
+ map->count = 1;
+
+ vma->vm_ops = &zoran_vm_ops;
+ vma->vm_flags |= VM_DONTEXPAND;
+ vma->vm_private_data = map;
+
+ for (i = first; i <= last; i++) {
+ for (j = 0;
+ j < fh->jpg_buffers.buffer_size / PAGE_SIZE;
+ j++) {
+ fraglen =
+ (le32_to_cpu(fh->jpg_buffers.buffer[i].
+ frag_tab[2 * j + 1]) & ~1) << 1;
+ todo = size;
+ if (todo > fraglen)
+ todo = fraglen;
+ pos =
+ le32_to_cpu((unsigned long) fh->jpg_buffers.
+ buffer[i].frag_tab[2 * j]);
+ /* should just be pos on i386 */
+ page = virt_to_phys(bus_to_virt(pos))
+ >> PAGE_SHIFT;
+ if (remap_pfn_range(vma, start, page,
+ todo, PAGE_SHARED)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: zoran_mmap(V4L) - remap_pfn_range failed\n",
+ ZR_DEVNAME(zr));
+ res = -EAGAIN;
+ goto jpg_mmap_unlock_and_return;
+ }
+ size -= todo;
+ start += todo;
+ if (size == 0)
+ break;
+ if (le32_to_cpu(fh->jpg_buffers.buffer[i].
+ frag_tab[2 * j + 1]) & 1)
+ break; /* was last fragment */
+ }
+ fh->jpg_buffers.buffer[i].map = map;
+ if (size == 0)
+ break;
+
+ }
+ jpg_mmap_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ break;
+
+ case ZORAN_MAP_MODE_RAW:
+
+ mutex_lock(&zr->resource_lock);
+
+ /* Map the V4L buffers */
+ if (!fh->v4l_buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+ "%s: zoran_mmap(V4L) - buffers not yet allocated\n",
+ ZR_DEVNAME(zr));
+ res = -ENOMEM;
+ goto v4l_mmap_unlock_and_return;
+ }
+
+ first = offset / fh->v4l_buffers.buffer_size;
+ last = first - 1 + size / fh->v4l_buffers.buffer_size;
+ if (offset % fh->v4l_buffers.buffer_size != 0 ||
+ size % fh->v4l_buffers.buffer_size != 0 || first < 0 ||
+ last < 0 || first >= fh->v4l_buffers.num_buffers ||
+ last >= fh->v4l_buffers.buffer_size) {
+ dprintk(1,
+ KERN_ERR
+ "%s: mmap(V4L) - offset=%lu or size=%lu invalid for bufsize=%d and numbufs=%d\n",
+ ZR_DEVNAME(zr), offset, size,
+ fh->v4l_buffers.buffer_size,
+ fh->v4l_buffers.num_buffers);
+ res = -EINVAL;
+ goto v4l_mmap_unlock_and_return;
+ }
+ for (i = first; i <= last; i++) {
+ if (fh->v4l_buffers.buffer[i].map) {
+ dprintk(1,
+ KERN_ERR
+ "%s: mmap(V4L) - buffer %d already mapped\n",
+ ZR_DEVNAME(zr), i);
+ res = -EBUSY;
+ goto v4l_mmap_unlock_and_return;
+ }
+ }
+
+ /* map these buffers (v4l_buffers[i]) */
+ map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL);
+ if (!map) {
+ res = -ENOMEM;
+ goto v4l_mmap_unlock_and_return;
+ }
+ map->file = file;
+ map->count = 1;
+
+ vma->vm_ops = &zoran_vm_ops;
+ vma->vm_flags |= VM_DONTEXPAND;
+ vma->vm_private_data = map;
+
+ for (i = first; i <= last; i++) {
+ todo = size;
+ if (todo > fh->v4l_buffers.buffer_size)
+ todo = fh->v4l_buffers.buffer_size;
+ page = fh->v4l_buffers.buffer[i].fbuffer_phys;
+ if (remap_pfn_range(vma, start, page >> PAGE_SHIFT,
+ todo, PAGE_SHARED)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: zoran_mmap(V4L)i - remap_pfn_range failed\n",
+ ZR_DEVNAME(zr));
+ res = -EAGAIN;
+ goto v4l_mmap_unlock_and_return;
+ }
+ size -= todo;
+ start += todo;
+ fh->v4l_buffers.buffer[i].map = map;
+ if (size == 0)
+ break;
+ }
+ v4l_mmap_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ break;
+
+ default:
+ dprintk(1,
+ KERN_ERR
+ "%s: zoran_mmap() - internal error - unknown map mode %d\n",
+ ZR_DEVNAME(zr), fh->map_mode);
+ break;
+ }
+
+ return 0;
+}
+
+static struct file_operations zoran_fops = {
+ .owner = THIS_MODULE,
+ .open = zoran_open,
+ .release = zoran_close,
+ .ioctl = zoran_ioctl,
+ .compat_ioctl = v4l_compat_ioctl32,
+ .llseek = no_llseek,
+ .read = zoran_read,
+ .write = zoran_write,
+ .mmap = zoran_mmap,
+ .poll = zoran_poll,
+};
+
+struct video_device zoran_template __devinitdata = {
+ .name = ZORAN_NAME,
+ .type = ZORAN_VID_TYPE,
+#ifdef HAVE_V4L2
+ .type2 = ZORAN_V4L2_VID_FLAGS,
+#endif
+ .hardware = ZORAN_HARDWARE,
+ .fops = &zoran_fops,
+ .release = &zoran_vdev_release,
+ .minor = -1
+};
+
diff --git a/linux/drivers/media/video/zoran_procfs.c b/linux/drivers/media/video/zoran_procfs.c
new file mode 100644
index 000000000..3472c00d4
--- /dev/null
+++ b/linux/drivers/media/video/zoran_procfs.c
@@ -0,0 +1,234 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * This part handles the procFS entries (/proc/ZORAN[%d])
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Currently maintained by:
+ * Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * Laurent Pinchart <laurent.pinchart@skynet.be>
+ * Mailinglist <mjpeg-users@lists.sf.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include "compat.h"
+#include <linux/videodev.h>
+#include <linux/spinlock.h>
+#include <linux/sem.h>
+#include <linux/seq_file.h>
+
+#include <linux/ctype.h>
+#include <asm/io.h>
+
+#include "videocodec.h"
+#include "zoran.h"
+#include "zoran_procfs.h"
+
+extern int *zr_debug;
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (*zr_debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+#ifdef CONFIG_PROC_FS
+struct procfs_params_zr36067 {
+ char *name;
+ short reg;
+ u32 mask;
+ short bit;
+};
+
+static const struct procfs_params_zr36067 zr67[] = {
+ {"HSPol", 0x000, 1, 30},
+ {"HStart", 0x000, 0x3ff, 10},
+ {"HEnd", 0x000, 0x3ff, 0},
+
+ {"VSPol", 0x004, 1, 30},
+ {"VStart", 0x004, 0x3ff, 10},
+ {"VEnd", 0x004, 0x3ff, 0},
+
+ {"ExtFl", 0x008, 1, 26},
+ {"TopField", 0x008, 1, 25},
+ {"VCLKPol", 0x008, 1, 24},
+ {"DupFld", 0x008, 1, 20},
+ {"LittleEndian", 0x008, 1, 0},
+
+ {"HsyncStart", 0x10c, 0xffff, 16},
+ {"LineTot", 0x10c, 0xffff, 0},
+
+ {"NAX", 0x110, 0xffff, 16},
+ {"PAX", 0x110, 0xffff, 0},
+
+ {"NAY", 0x114, 0xffff, 16},
+ {"PAY", 0x114, 0xffff, 0},
+
+ /* {"",,,}, */
+
+ {NULL, 0, 0, 0},
+};
+
+static void
+setparam (struct zoran *zr,
+ char *name,
+ char *sval)
+{
+ int i = 0, reg0, reg, val;
+
+ while (zr67[i].name != NULL) {
+ if (!strncmp(name, zr67[i].name, strlen(zr67[i].name))) {
+ reg = reg0 = btread(zr67[i].reg);
+ reg &= ~(zr67[i].mask << zr67[i].bit);
+ if (!isdigit(sval[0]))
+ break;
+ val = simple_strtoul(sval, NULL, 0);
+ if ((val & ~zr67[i].mask))
+ break;
+ reg |= (val & zr67[i].mask) << zr67[i].bit;
+ dprintk(4,
+ KERN_INFO
+ "%s: setparam: setting ZR36067 register 0x%03x: 0x%08x=>0x%08x %s=%d\n",
+ ZR_DEVNAME(zr), zr67[i].reg, reg0, reg,
+ zr67[i].name, val);
+ btwrite(reg, zr67[i].reg);
+ break;
+ }
+ i++;
+ }
+}
+
+static int zoran_show(struct seq_file *p, void *v)
+{
+ struct zoran *zr = p->private;
+ int i;
+
+ seq_printf(p, "ZR36067 registers:\n");
+ for (i = 0; i < 0x130; i += 16)
+ seq_printf(p, "%03X %08X %08X %08X %08X \n", i,
+ btread(i), btread(i+4), btread(i+8), btread(i+12));
+ return 0;
+}
+
+static int zoran_open(struct inode *inode, struct file *file)
+{
+ struct zoran *data = PDE(inode)->data;
+ return single_open(file, zoran_show, data);
+}
+
+static ssize_t zoran_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct zoran *zr = PDE(file->f_dentry->d_inode)->data;
+ char *string, *sp;
+ char *line, *ldelim, *varname, *svar, *tdelim;
+
+ if (count > 32768) /* Stupidity filter */
+ return -EINVAL;
+
+ string = sp = vmalloc(count + 1);
+ if (!string) {
+ dprintk(1,
+ KERN_ERR
+ "%s: write_proc: can not allocate memory\n",
+ ZR_DEVNAME(zr));
+ return -ENOMEM;
+ }
+ if (copy_from_user(string, buffer, count)) {
+ vfree (string);
+ return -EFAULT;
+ }
+ string[count] = 0;
+ dprintk(4, KERN_INFO "%s: write_proc: name=%s count=%zu zr=%p\n",
+ ZR_DEVNAME(zr), file->f_dentry->d_name.name, count, zr);
+ ldelim = " \t\n";
+ tdelim = "=";
+ line = strpbrk(sp, ldelim);
+ while (line) {
+ *line = 0;
+ svar = strpbrk(sp, tdelim);
+ if (svar) {
+ *svar = 0;
+ varname = sp;
+ svar++;
+ setparam(zr, varname, svar);
+ }
+ sp = line + 1;
+ line = strpbrk(sp, ldelim);
+ }
+ vfree(string);
+
+ return count;
+}
+
+static struct file_operations zoran_operations = {
+ .open = zoran_open,
+ .read = seq_read,
+ .write = zoran_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+#endif
+
+int
+zoran_proc_init (struct zoran *zr)
+{
+#ifdef CONFIG_PROC_FS
+ char name[8];
+
+ snprintf(name, 7, "zoran%d", zr->id);
+ if ((zr->zoran_proc = create_proc_entry(name, 0, NULL))) {
+ zr->zoran_proc->data = zr;
+ zr->zoran_proc->owner = THIS_MODULE;
+ zr->zoran_proc->proc_fops = &zoran_operations;
+ dprintk(2,
+ KERN_INFO
+ "%s: procfs entry /proc/%s allocated. data=%p\n",
+ ZR_DEVNAME(zr), name, zr->zoran_proc->data);
+ } else {
+ dprintk(1, KERN_ERR "%s: Unable to initialise /proc/%s\n",
+ ZR_DEVNAME(zr), name);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+void
+zoran_proc_cleanup (struct zoran *zr)
+{
+#ifdef CONFIG_PROC_FS
+ char name[8];
+
+ snprintf(name, 7, "zoran%d", zr->id);
+ if (zr->zoran_proc)
+ remove_proc_entry(name, NULL);
+ zr->zoran_proc = NULL;
+#endif
+}
diff --git a/linux/drivers/media/video/zoran_procfs.h b/linux/drivers/media/video/zoran_procfs.h
new file mode 100644
index 000000000..f2d5b1ba4
--- /dev/null
+++ b/linux/drivers/media/video/zoran_procfs.h
@@ -0,0 +1,36 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * This part handles card-specific data and detection
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Currently maintained by:
+ * Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * Laurent Pinchart <laurent.pinchart@skynet.be>
+ * Mailinglist <mjpeg-users@lists.sf.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ZORAN_PROCFS_H__
+#define __ZORAN_PROCFS_H__
+
+extern int zoran_proc_init(struct zoran *zr);
+extern void zoran_proc_cleanup(struct zoran *zr);
+
+#endif /* __ZORAN_PROCFS_H__ */
diff --git a/linux/drivers/media/video/zr36016.c b/linux/drivers/media/video/zr36016.c
new file mode 100644
index 000000000..7f958d803
--- /dev/null
+++ b/linux/drivers/media/video/zr36016.c
@@ -0,0 +1,531 @@
+/*
+ * Zoran ZR36016 basic configuration functions
+ *
+ * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
+ *
+ * $Id: zr36016.c,v 1.1.2.14 2003/08/20 19:46:55 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+#define ZR016_VERSION "v0.7"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include <linux/types.h>
+#include <linux/wait.h>
+
+/* includes for structures and defines regarding video
+ #include<linux/videodev.h> */
+
+/* I/O commands, error codes */
+#include<asm/io.h>
+//#include<errno.h>
+
+/* v4l API */
+#include "compat.h"
+#include<linux/videodev.h>
+
+/* headerfile of this module */
+#include"zr36016.h"
+
+/* codec io API */
+#include"videocodec.h"
+
+/* it doesn't make sense to have more than 20 or so,
+ just to prevent some unwanted loops */
+#define MAX_CODECS 20
+
+/* amount of chips attached via this driver */
+static int zr36016_codecs = 0;
+
+/* debugging is available via module parameter */
+
+static int debug = 0;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-4)");
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+/* =========================================================================
+ Local hardware I/O functions:
+
+ read/write via codec layer (registers are located in the master device)
+ ========================================================================= */
+
+/* read and write functions */
+static u8
+zr36016_read (struct zr36016 *ptr,
+ u16 reg)
+{
+ u8 value = 0;
+
+ // just in case something is wrong...
+ if (ptr->codec->master_data->readreg)
+ value =
+ (ptr->codec->master_data->
+ readreg(ptr->codec, reg)) & 0xFF;
+ else
+ dprintk(1,
+ KERN_ERR "%s: invalid I/O setup, nothing read!\n",
+ ptr->name);
+
+ dprintk(4, "%s: reading from 0x%04x: %02x\n", ptr->name, reg,
+ value);
+
+ return value;
+}
+
+static void
+zr36016_write (struct zr36016 *ptr,
+ u16 reg,
+ u8 value)
+{
+ dprintk(4, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value,
+ reg);
+
+ // just in case something is wrong...
+ if (ptr->codec->master_data->writereg) {
+ ptr->codec->master_data->writereg(ptr->codec, reg, value);
+ } else
+ dprintk(1,
+ KERN_ERR
+ "%s: invalid I/O setup, nothing written!\n",
+ ptr->name);
+}
+
+/* indirect read and write functions */
+/* the 016 supports auto-addr-increment, but
+ * writing it all time cost not much and is safer... */
+static u8
+zr36016_readi (struct zr36016 *ptr,
+ u16 reg)
+{
+ u8 value = 0;
+
+ // just in case something is wrong...
+ if ((ptr->codec->master_data->writereg) &&
+ (ptr->codec->master_data->readreg)) {
+ ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); // ADDR
+ value = (ptr->codec->master_data->readreg(ptr->codec, ZR016_IDATA)) & 0xFF; // DATA
+ } else
+ dprintk(1,
+ KERN_ERR
+ "%s: invalid I/O setup, nothing read (i)!\n",
+ ptr->name);
+
+ dprintk(4, "%s: reading indirect from 0x%04x: %02x\n", ptr->name,
+ reg, value);
+ return value;
+}
+
+static void
+zr36016_writei (struct zr36016 *ptr,
+ u16 reg,
+ u8 value)
+{
+ dprintk(4, "%s: writing indirect 0x%02x to 0x%04x\n", ptr->name,
+ value, reg);
+
+ // just in case something is wrong...
+ if (ptr->codec->master_data->writereg) {
+ ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); // ADDR
+ ptr->codec->master_data->writereg(ptr->codec, ZR016_IDATA, value & 0x0FF); // DATA
+ } else
+ dprintk(1,
+ KERN_ERR
+ "%s: invalid I/O setup, nothing written (i)!\n",
+ ptr->name);
+}
+
+/* =========================================================================
+ Local helper function:
+
+ version read
+ ========================================================================= */
+
+/* version kept in datastructure */
+static u8
+zr36016_read_version (struct zr36016 *ptr)
+{
+ ptr->version = zr36016_read(ptr, 0) >> 4;
+ return ptr->version;
+}
+
+/* =========================================================================
+ Local helper function:
+
+ basic test of "connectivity", writes/reads to/from PAX-Lo register
+ ========================================================================= */
+
+static int
+zr36016_basic_test (struct zr36016 *ptr)
+{
+ if (debug) {
+ int i;
+ zr36016_writei(ptr, ZR016I_PAX_LO, 0x55);
+ dprintk(1, KERN_INFO "%s: registers: ", ptr->name);
+ for (i = 0; i <= 0x0b; i++)
+ dprintk(1, "%02x ", zr36016_readi(ptr, i));
+ dprintk(1, "\n");
+ }
+ // for testing just write 0, then the default value to a register and read
+ // it back in both cases
+ zr36016_writei(ptr, ZR016I_PAX_LO, 0x00);
+ if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: attach failed, can't connect to vfe processor!\n",
+ ptr->name);
+ return -ENXIO;
+ }
+ zr36016_writei(ptr, ZR016I_PAX_LO, 0x0d0);
+ if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0d0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: attach failed, can't connect to vfe processor!\n",
+ ptr->name);
+ return -ENXIO;
+ }
+ // we allow version numbers from 0-3, should be enough, though
+ zr36016_read_version(ptr);
+ if (ptr->version & 0x0c) {
+ dprintk(1,
+ KERN_ERR
+ "%s: attach failed, suspicious version %d found...\n",
+ ptr->name, ptr->version);
+ return -ENXIO;
+ }
+
+ return 0; /* looks good! */
+}
+
+/* =========================================================================
+ Local helper function:
+
+ simple loop for pushing the init datasets - NO USE --
+ ========================================================================= */
+
+#if 0
+static int zr36016_pushit (struct zr36016 *ptr,
+ u16 startreg,
+ u16 len,
+ const char *data)
+{
+ int i=0;
+
+ dprintk(4, "%s: write data block to 0x%04x (len=%d)\n",
+ ptr->name, startreg,len);
+ while (i<len) {
+ zr36016_writei(ptr, startreg++, data[i++]);
+ }
+
+ return i;
+}
+#endif
+
+/* =========================================================================
+ Basic datasets & init:
+
+ //TODO//
+ ========================================================================= */
+
+// needed offset values PAL NTSC SECAM
+static const int zr016_xoff[] = { 20, 20, 20 };
+static const int zr016_yoff[] = { 8, 9, 7 };
+
+static void
+zr36016_init (struct zr36016 *ptr)
+{
+ // stop any processing
+ zr36016_write(ptr, ZR016_GOSTOP, 0);
+
+ // mode setup (yuv422 in and out, compression/expansuon due to mode)
+ zr36016_write(ptr, ZR016_MODE,
+ ZR016_YUV422 | ZR016_YUV422_YUV422 |
+ (ptr->mode == CODEC_DO_COMPRESSION ?
+ ZR016_COMPRESSION : ZR016_EXPANSION));
+
+ // misc setup
+ zr36016_writei(ptr, ZR016I_SETUP1,
+ (ptr->xdec ? (ZR016_HRFL | ZR016_HORZ) : 0) |
+ (ptr->ydec ? ZR016_VERT : 0) | ZR016_CNTI);
+ zr36016_writei(ptr, ZR016I_SETUP2, ZR016_CCIR);
+
+ // Window setup
+ // (no extra offset for now, norm defines offset, default width height)
+ zr36016_writei(ptr, ZR016I_PAX_HI, ptr->width >> 8);
+ zr36016_writei(ptr, ZR016I_PAX_LO, ptr->width & 0xFF);
+ zr36016_writei(ptr, ZR016I_PAY_HI, ptr->height >> 8);
+ zr36016_writei(ptr, ZR016I_PAY_LO, ptr->height & 0xFF);
+ zr36016_writei(ptr, ZR016I_NAX_HI, ptr->xoff >> 8);
+ zr36016_writei(ptr, ZR016I_NAX_LO, ptr->xoff & 0xFF);
+ zr36016_writei(ptr, ZR016I_NAY_HI, ptr->yoff >> 8);
+ zr36016_writei(ptr, ZR016I_NAY_LO, ptr->yoff & 0xFF);
+
+ /* shall we continue now, please? */
+ zr36016_write(ptr, ZR016_GOSTOP, 1);
+}
+
+/* =========================================================================
+ CODEC API FUNCTIONS
+
+ this functions are accessed by the master via the API structure
+ ========================================================================= */
+
+/* set compression/expansion mode and launches codec -
+ this should be the last call from the master before starting processing */
+static int
+zr36016_set_mode (struct videocodec *codec,
+ int mode)
+{
+ struct zr36016 *ptr = (struct zr36016 *) codec->data;
+
+ dprintk(2, "%s: set_mode %d call\n", ptr->name, mode);
+
+ if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION))
+ return -EINVAL;
+
+ ptr->mode = mode;
+ zr36016_init(ptr);
+
+ return 0;
+}
+
+/* set picture size */
+static int
+zr36016_set_video (struct videocodec *codec,
+ struct tvnorm *norm,
+ struct vfe_settings *cap,
+ struct vfe_polarity *pol)
+{
+ struct zr36016 *ptr = (struct zr36016 *) codec->data;
+
+ dprintk(2, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) call\n",
+ ptr->name, norm->HStart, norm->VStart,
+ cap->x, cap->y, cap->width, cap->height,
+ cap->decimation);
+
+ /* if () return -EINVAL;
+ * trust the master driver that it knows what it does - so
+ * we allow invalid startx/y for now ... */
+ ptr->width = cap->width;
+ ptr->height = cap->height;
+ /* (Ronald) This is ugly. zoran_device.c, line 387
+ * already mentions what happens if HStart is even
+ * (blue faces, etc., cr/cb inversed). There's probably
+ * some good reason why HStart is 0 instead of 1, so I'm
+ * leaving it to this for now, but really... This can be
+ * done a lot simpler */
+ ptr->xoff = (norm->HStart ? norm->HStart : 1) + cap->x;
+ /* Something to note here (I don't understand it), setting
+ * VStart too high will cause the codec to 'not work'. I
+ * really don't get it. values of 16 (VStart) already break
+ * it here. Just '0' seems to work. More testing needed! */
+ ptr->yoff = norm->VStart + cap->y;
+ /* (Ronald) dzjeeh, can't this thing do hor_decimation = 4? */
+ ptr->xdec = ((cap->decimation & 0xff) == 1) ? 0 : 1;
+ ptr->ydec = (((cap->decimation >> 8) & 0xff) == 1) ? 0 : 1;
+
+ return 0;
+}
+
+/* additional control functions */
+static int
+zr36016_control (struct videocodec *codec,
+ int type,
+ int size,
+ void *data)
+{
+ struct zr36016 *ptr = (struct zr36016 *) codec->data;
+ int *ival = (int *) data;
+
+ dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type,
+ size);
+
+ switch (type) {
+ case CODEC_G_STATUS: /* get last status - we don't know it ... */
+ if (size != sizeof(int))
+ return -EFAULT;
+ *ival = 0;
+ break;
+
+ case CODEC_G_CODEC_MODE:
+ if (size != sizeof(int))
+ return -EFAULT;
+ *ival = 0;
+ break;
+
+ case CODEC_S_CODEC_MODE:
+ if (size != sizeof(int))
+ return -EFAULT;
+ if (*ival != 0)
+ return -EINVAL;
+ /* not needed, do nothing */
+ return 0;
+
+ case CODEC_G_VFE:
+ case CODEC_S_VFE:
+ return 0;
+
+ case CODEC_S_MMAP:
+ /* not available, give an error */
+ return -ENXIO;
+
+ default:
+ return -EINVAL;
+ }
+
+ return size;
+}
+
+/* =========================================================================
+ Exit and unregister function:
+
+ Deinitializes Zoran's JPEG processor
+ ========================================================================= */
+
+static int
+zr36016_unset (struct videocodec *codec)
+{
+ struct zr36016 *ptr = codec->data;
+
+ if (ptr) {
+ /* do wee need some codec deinit here, too ???? */
+
+ dprintk(1, "%s: finished codec #%d\n", ptr->name,
+ ptr->num);
+ kfree(ptr);
+ codec->data = NULL;
+
+ zr36016_codecs--;
+ return 0;
+ }
+
+ return -EFAULT;
+}
+
+/* =========================================================================
+ Setup and registry function:
+
+ Initializes Zoran's JPEG processor
+
+ Also sets pixel size, average code size, mode (compr./decompr.)
+ (the given size is determined by the processor with the video interface)
+ ========================================================================= */
+
+static int
+zr36016_setup (struct videocodec *codec)
+{
+ struct zr36016 *ptr;
+ int res;
+
+ dprintk(2, "zr36016: initializing VFE subsystem #%d.\n",
+ zr36016_codecs);
+
+ if (zr36016_codecs == MAX_CODECS) {
+ dprintk(1,
+ KERN_ERR "zr36016: Can't attach more codecs!\n");
+ return -ENOSPC;
+ }
+ //mem structure init
+ codec->data = ptr = kzalloc(sizeof(struct zr36016), GFP_KERNEL);
+ if (NULL == ptr) {
+ dprintk(1, KERN_ERR "zr36016: Can't get enough memory!\n");
+ return -ENOMEM;
+ }
+
+ snprintf(ptr->name, sizeof(ptr->name), "zr36016[%d]",
+ zr36016_codecs);
+ ptr->num = zr36016_codecs++;
+ ptr->codec = codec;
+
+ //testing
+ res = zr36016_basic_test(ptr);
+ if (res < 0) {
+ zr36016_unset(codec);
+ return res;
+ }
+ //final setup
+ ptr->mode = CODEC_DO_COMPRESSION;
+ ptr->width = 768;
+ ptr->height = 288;
+ ptr->xdec = 1;
+ ptr->ydec = 0;
+ zr36016_init(ptr);
+
+ dprintk(1, KERN_INFO "%s: codec v%d attached and running\n",
+ ptr->name, ptr->version);
+
+ return 0;
+}
+
+static const struct videocodec zr36016_codec = {
+ .owner = THIS_MODULE,
+ .name = "zr36016",
+ .magic = 0L, // magic not used
+ .flags =
+ CODEC_FLAG_HARDWARE | CODEC_FLAG_VFE | CODEC_FLAG_ENCODER |
+ CODEC_FLAG_DECODER,
+ .type = CODEC_TYPE_ZR36016,
+ .setup = zr36016_setup, // functionality
+ .unset = zr36016_unset,
+ .set_mode = zr36016_set_mode,
+ .set_video = zr36016_set_video,
+ .control = zr36016_control,
+ // others are not used
+};
+
+/* =========================================================================
+ HOOK IN DRIVER AS KERNEL MODULE
+ ========================================================================= */
+
+static int __init
+zr36016_init_module (void)
+{
+ //dprintk(1, "ZR36016 driver %s\n",ZR016_VERSION);
+ zr36016_codecs = 0;
+ return videocodec_register(&zr36016_codec);
+}
+
+static void __exit
+zr36016_cleanup_module (void)
+{
+ if (zr36016_codecs) {
+ dprintk(1,
+ "zr36016: something's wrong - %d codecs left somehow.\n",
+ zr36016_codecs);
+ }
+ videocodec_unregister(&zr36016_codec);
+}
+
+module_init(zr36016_init_module);
+module_exit(zr36016_cleanup_module);
+
+MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>");
+MODULE_DESCRIPTION("Driver module for ZR36016 video frontends "
+ ZR016_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/video/zr36016.h b/linux/drivers/media/video/zr36016.h
new file mode 100644
index 000000000..8c79229f6
--- /dev/null
+++ b/linux/drivers/media/video/zr36016.h
@@ -0,0 +1,111 @@
+/*
+ * Zoran ZR36016 basic configuration functions - header file
+ *
+ * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
+ *
+ * $Id: zr36016.h,v 1.1.2.3 2003/01/14 21:18:07 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+#ifndef ZR36016_H
+#define ZR36016_H
+
+/* data stored for each zoran jpeg codec chip */
+struct zr36016 {
+ char name[32];
+ int num;
+ /* io datastructure */
+ struct videocodec *codec;
+ // coder status
+ __u8 version;
+ // actual coder setup
+ int mode;
+
+ __u16 xoff;
+ __u16 yoff;
+ __u16 width;
+ __u16 height;
+ __u16 xdec;
+ __u16 ydec;
+};
+
+/* direct register addresses */
+#define ZR016_GOSTOP 0x00
+#define ZR016_MODE 0x01
+#define ZR016_IADDR 0x02
+#define ZR016_IDATA 0x03
+
+/* indirect register addresses */
+#define ZR016I_SETUP1 0x00
+#define ZR016I_SETUP2 0x01
+#define ZR016I_NAX_LO 0x02
+#define ZR016I_NAX_HI 0x03
+#define ZR016I_PAX_LO 0x04
+#define ZR016I_PAX_HI 0x05
+#define ZR016I_NAY_LO 0x06
+#define ZR016I_NAY_HI 0x07
+#define ZR016I_PAY_LO 0x08
+#define ZR016I_PAY_HI 0x09
+#define ZR016I_NOL_LO 0x0a
+#define ZR016I_NOL_HI 0x0b
+
+/* possible values for mode register */
+#define ZR016_RGB444_YUV444 0x00
+#define ZR016_RGB444_YUV422 0x01
+#define ZR016_RGB444_YUV411 0x02
+#define ZR016_RGB444_Y400 0x03
+#define ZR016_RGB444_RGB444 0x04
+#define ZR016_YUV444_YUV444 0x08
+#define ZR016_YUV444_YUV422 0x09
+#define ZR016_YUV444_YUV411 0x0a
+#define ZR016_YUV444_Y400 0x0b
+#define ZR016_YUV444_RGB444 0x0c
+#define ZR016_YUV422_YUV422 0x11
+#define ZR016_YUV422_YUV411 0x12
+#define ZR016_YUV422_Y400 0x13
+#define ZR016_YUV411_YUV411 0x16
+#define ZR016_YUV411_Y400 0x17
+#define ZR016_4444_4444 0x19
+#define ZR016_100_100 0x1b
+
+#define ZR016_RGB444 0x00
+#define ZR016_YUV444 0x20
+#define ZR016_YUV422 0x40
+
+#define ZR016_COMPRESSION 0x80
+#define ZR016_EXPANSION 0x80
+
+/* possible values for setup 1 register */
+#define ZR016_CKRT 0x80
+#define ZR016_VERT 0x40
+#define ZR016_HORZ 0x20
+#define ZR016_HRFL 0x10
+#define ZR016_DSFL 0x08
+#define ZR016_SBFL 0x04
+#define ZR016_RSTR 0x02
+#define ZR016_CNTI 0x01
+
+/* possible values for setup 2 register */
+#define ZR016_SYEN 0x40
+#define ZR016_CCIR 0x04
+#define ZR016_SIGN 0x02
+#define ZR016_YMCS 0x01
+
+#endif /*fndef ZR36016_H */
diff --git a/linux/drivers/media/video/zr36050.c b/linux/drivers/media/video/zr36050.c
new file mode 100644
index 000000000..a6bbd1256
--- /dev/null
+++ b/linux/drivers/media/video/zr36050.c
@@ -0,0 +1,905 @@
+/*
+ * Zoran ZR36050 basic configuration functions
+ *
+ * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
+ *
+ * $Id: zr36050.c,v 1.1.2.11 2003/08/03 14:54:53 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+#define ZR050_VERSION "v0.7.1"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include <linux/types.h>
+#include <linux/wait.h>
+
+/* includes for structures and defines regarding video
+ #include<linux/videodev.h> */
+
+/* I/O commands, error codes */
+#include<asm/io.h>
+//#include<errno.h>
+
+/* headerfile of this module */
+#include"zr36050.h"
+
+/* codec io API */
+#include"videocodec.h"
+
+/* it doesn't make sense to have more than 20 or so,
+ just to prevent some unwanted loops */
+#define MAX_CODECS 20
+
+/* amount of chips attached via this driver */
+static int zr36050_codecs = 0;
+
+/* debugging is available via module parameter */
+
+static int debug = 0;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-4)");
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+/* =========================================================================
+ Local hardware I/O functions:
+
+ read/write via codec layer (registers are located in the master device)
+ ========================================================================= */
+
+/* read and write functions */
+static u8
+zr36050_read (struct zr36050 *ptr,
+ u16 reg)
+{
+ u8 value = 0;
+
+ // just in case something is wrong...
+ if (ptr->codec->master_data->readreg)
+ value = (ptr->codec->master_data->readreg(ptr->codec,
+ reg)) & 0xFF;
+ else
+ dprintk(1,
+ KERN_ERR "%s: invalid I/O setup, nothing read!\n",
+ ptr->name);
+
+ dprintk(4, "%s: reading from 0x%04x: %02x\n", ptr->name, reg,
+ value);
+
+ return value;
+}
+
+static void
+zr36050_write (struct zr36050 *ptr,
+ u16 reg,
+ u8 value)
+{
+ dprintk(4, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value,
+ reg);
+
+ // just in case something is wrong...
+ if (ptr->codec->master_data->writereg)
+ ptr->codec->master_data->writereg(ptr->codec, reg, value);
+ else
+ dprintk(1,
+ KERN_ERR
+ "%s: invalid I/O setup, nothing written!\n",
+ ptr->name);
+}
+
+/* =========================================================================
+ Local helper function:
+
+ status read
+ ========================================================================= */
+
+/* status is kept in datastructure */
+static u8
+zr36050_read_status1 (struct zr36050 *ptr)
+{
+ ptr->status1 = zr36050_read(ptr, ZR050_STATUS_1);
+
+ zr36050_read(ptr, 0);
+ return ptr->status1;
+}
+
+/* =========================================================================
+ Local helper function:
+
+ scale factor read
+ ========================================================================= */
+
+/* scale factor is kept in datastructure */
+static u16
+zr36050_read_scalefactor (struct zr36050 *ptr)
+{
+ ptr->scalefact = (zr36050_read(ptr, ZR050_SF_HI) << 8) |
+ (zr36050_read(ptr, ZR050_SF_LO) & 0xFF);
+
+ /* leave 0 selected for an eventually GO from master */
+ zr36050_read(ptr, 0);
+ return ptr->scalefact;
+}
+
+/* =========================================================================
+ Local helper function:
+
+ wait if codec is ready to proceed (end of processing) or time is over
+ ========================================================================= */
+
+static void
+zr36050_wait_end (struct zr36050 *ptr)
+{
+ int i = 0;
+
+ while (!(zr36050_read_status1(ptr) & 0x4)) {
+ udelay(1);
+ if (i++ > 200000) { // 200ms, there is for sure something wrong!!!
+ dprintk(1,
+ "%s: timout at wait_end (last status: 0x%02x)\n",
+ ptr->name, ptr->status1);
+ break;
+ }
+ }
+}
+
+/* =========================================================================
+ Local helper function:
+
+ basic test of "connectivity", writes/reads to/from memory the SOF marker
+ ========================================================================= */
+
+static int
+zr36050_basic_test (struct zr36050 *ptr)
+{
+ zr36050_write(ptr, ZR050_SOF_IDX, 0x00);
+ zr36050_write(ptr, ZR050_SOF_IDX + 1, 0x00);
+ if ((zr36050_read(ptr, ZR050_SOF_IDX) |
+ zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0x0000) {
+ dprintk(1,
+ KERN_ERR
+ "%s: attach failed, can't connect to jpeg processor!\n",
+ ptr->name);
+ return -ENXIO;
+ }
+ zr36050_write(ptr, ZR050_SOF_IDX, 0xff);
+ zr36050_write(ptr, ZR050_SOF_IDX + 1, 0xc0);
+ if (((zr36050_read(ptr, ZR050_SOF_IDX) << 8) |
+ zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0xffc0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: attach failed, can't connect to jpeg processor!\n",
+ ptr->name);
+ return -ENXIO;
+ }
+
+ zr36050_wait_end(ptr);
+ if ((ptr->status1 & 0x4) == 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: attach failed, jpeg processor failed (end flag)!\n",
+ ptr->name);
+ return -EBUSY;
+ }
+
+ return 0; /* looks good! */
+}
+
+/* =========================================================================
+ Local helper function:
+
+ simple loop for pushing the init datasets
+ ========================================================================= */
+
+static int
+zr36050_pushit (struct zr36050 *ptr,
+ u16 startreg,
+ u16 len,
+ const char *data)
+{
+ int i = 0;
+
+ dprintk(4, "%s: write data block to 0x%04x (len=%d)\n", ptr->name,
+ startreg, len);
+ while (i < len) {
+ zr36050_write(ptr, startreg++, data[i++]);
+ }
+
+ return i;
+}
+
+/* =========================================================================
+ Basic datasets:
+
+ jpeg baseline setup data (you find it on lots places in internet, or just
+ extract it from any regular .jpg image...)
+
+ Could be variable, but until it's not needed it they are just fixed to save
+ memory. Otherwise expand zr36050 structure with arrays, push the values to
+ it and initalize from there, as e.g. the linux zr36057/60 driver does it.
+ ========================================================================= */
+
+static const char zr36050_dqt[0x86] = {
+ 0xff, 0xdb, //Marker: DQT
+ 0x00, 0x84, //Length: 2*65+2
+ 0x00, //Pq,Tq first table
+ 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
+ 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
+ 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
+ 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
+ 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
+ 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
+ 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
+ 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
+ 0x01, //Pq,Tq second table
+ 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
+ 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63
+};
+
+static const char zr36050_dht[0x1a4] = {
+ 0xff, 0xc4, //Marker: DHT
+ 0x01, 0xa2, //Length: 2*AC, 2*DC
+ 0x00, //DC first table
+ 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
+ 0x01, //DC second table
+ 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
+ 0x10, //AC first table
+ 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03,
+ 0x05, 0x05, 0x04, 0x04, 0x00, 0x00,
+ 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11,
+ 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61,
+ 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1,
+ 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24,
+ 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34,
+ 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
+ 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
+ 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+ 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
+ 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
+ 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9,
+ 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8,
+ 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
+ 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+ 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+ 0xF8, 0xF9, 0xFA,
+ 0x11, //AC second table
+ 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
+ 0x07, 0x05, 0x04, 0x04, 0x00, 0x01,
+ 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04,
+ 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
+ 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
+ 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62,
+ 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25,
+ 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A,
+ 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
+ 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
+ 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+ 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
+ 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8,
+ 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
+ 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8,
+ 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+ 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
+ 0xF9, 0xFA
+};
+
+/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */
+#define NO_OF_COMPONENTS 0x3 //Y,U,V
+#define BASELINE_PRECISION 0x8 //MCU size (?)
+static const char zr36050_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's QT
+static const char zr36050_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's DC
+static const char zr36050_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC
+
+/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */
+static const char zr36050_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 };
+static const char zr36050_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 };
+
+/* =========================================================================
+ Local helper functions:
+
+ calculation and setup of parameter-dependent JPEG baseline segments
+ (needed for compression only)
+ ========================================================================= */
+
+/* ------------------------------------------------------------------------- */
+
+/* SOF (start of frame) segment depends on width, height and sampling ratio
+ of each color component */
+
+static int
+zr36050_set_sof (struct zr36050 *ptr)
+{
+ char sof_data[34]; // max. size of register set
+ int i;
+
+ dprintk(3, "%s: write SOF (%dx%d, %d components)\n", ptr->name,
+ ptr->width, ptr->height, NO_OF_COMPONENTS);
+ sof_data[0] = 0xff;
+ sof_data[1] = 0xc0;
+ sof_data[2] = 0x00;
+ sof_data[3] = (3 * NO_OF_COMPONENTS) + 8;
+ sof_data[4] = BASELINE_PRECISION; // only '8' possible with zr36050
+ sof_data[5] = (ptr->height) >> 8;
+ sof_data[6] = (ptr->height) & 0xff;
+ sof_data[7] = (ptr->width) >> 8;
+ sof_data[8] = (ptr->width) & 0xff;
+ sof_data[9] = NO_OF_COMPONENTS;
+ for (i = 0; i < NO_OF_COMPONENTS; i++) {
+ sof_data[10 + (i * 3)] = i; // index identifier
+ sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) | (ptr->v_samp_ratio[i]); // sampling ratios
+ sof_data[12 + (i * 3)] = zr36050_tq[i]; // Q table selection
+ }
+ return zr36050_pushit(ptr, ZR050_SOF_IDX,
+ (3 * NO_OF_COMPONENTS) + 10, sof_data);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* SOS (start of scan) segment depends on the used scan components
+ of each color component */
+
+static int
+zr36050_set_sos (struct zr36050 *ptr)
+{
+ char sos_data[16]; // max. size of register set
+ int i;
+
+ dprintk(3, "%s: write SOS\n", ptr->name);
+ sos_data[0] = 0xff;
+ sos_data[1] = 0xda;
+ sos_data[2] = 0x00;
+ sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3;
+ sos_data[4] = NO_OF_COMPONENTS;
+ for (i = 0; i < NO_OF_COMPONENTS; i++) {
+ sos_data[5 + (i * 2)] = i; // index
+ sos_data[6 + (i * 2)] = (zr36050_td[i] << 4) | zr36050_ta[i]; // AC/DC tbl.sel.
+ }
+ sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00; // scan start
+ sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3F;
+ sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00;
+ return zr36050_pushit(ptr, ZR050_SOS1_IDX,
+ 4 + 1 + (2 * NO_OF_COMPONENTS) + 3,
+ sos_data);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* DRI (define restart interval) */
+
+static int
+zr36050_set_dri (struct zr36050 *ptr)
+{
+ char dri_data[6]; // max. size of register set
+
+ dprintk(3, "%s: write DRI\n", ptr->name);
+ dri_data[0] = 0xff;
+ dri_data[1] = 0xdd;
+ dri_data[2] = 0x00;
+ dri_data[3] = 0x04;
+ dri_data[4] = ptr->dri >> 8;
+ dri_data[5] = ptr->dri & 0xff;
+ return zr36050_pushit(ptr, ZR050_DRI_IDX, 6, dri_data);
+}
+
+/* =========================================================================
+ Setup function:
+
+ Setup compression/decompression of Zoran's JPEG processor
+ ( see also zoran 36050 manual )
+
+ ... sorry for the spaghetti code ...
+ ========================================================================= */
+static void
+zr36050_init (struct zr36050 *ptr)
+{
+ int sum = 0;
+ long bitcnt, tmp;
+
+ if (ptr->mode == CODEC_DO_COMPRESSION) {
+ dprintk(2, "%s: COMPRESSION SETUP\n", ptr->name);
+
+ /* 050 communicates with 057 in master mode */
+ zr36050_write(ptr, ZR050_HARDWARE, ZR050_HW_MSTR);
+
+ /* encoding table preload for compression */
+ zr36050_write(ptr, ZR050_MODE,
+ ZR050_MO_COMP | ZR050_MO_TLM);
+ zr36050_write(ptr, ZR050_OPTIONS, 0);
+
+ /* disable all IRQs */
+ zr36050_write(ptr, ZR050_INT_REQ_0, 0);
+ zr36050_write(ptr, ZR050_INT_REQ_1, 3); // low 2 bits always 1
+
+ /* volume control settings */
+ /*zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol);*/
+ zr36050_write(ptr, ZR050_SF_HI, ptr->scalefact >> 8);
+ zr36050_write(ptr, ZR050_SF_LO, ptr->scalefact & 0xff);
+
+ zr36050_write(ptr, ZR050_AF_HI, 0xff);
+ zr36050_write(ptr, ZR050_AF_M, 0xff);
+ zr36050_write(ptr, ZR050_AF_LO, 0xff);
+
+ /* setup the variable jpeg tables */
+ sum += zr36050_set_sof(ptr);
+ sum += zr36050_set_sos(ptr);
+ sum += zr36050_set_dri(ptr);
+
+ /* setup the fixed jpeg tables - maybe variable, though -
+ * (see table init section above) */
+ dprintk(3, "%s: write DQT, DHT, APP\n", ptr->name);
+ sum += zr36050_pushit(ptr, ZR050_DQT_IDX,
+ sizeof(zr36050_dqt), zr36050_dqt);
+ sum += zr36050_pushit(ptr, ZR050_DHT_IDX,
+ sizeof(zr36050_dht), zr36050_dht);
+ zr36050_write(ptr, ZR050_APP_IDX, 0xff);
+ zr36050_write(ptr, ZR050_APP_IDX + 1, 0xe0 + ptr->app.appn);
+ zr36050_write(ptr, ZR050_APP_IDX + 2, 0x00);
+ zr36050_write(ptr, ZR050_APP_IDX + 3, ptr->app.len + 2);
+ sum += zr36050_pushit(ptr, ZR050_APP_IDX + 4, 60,
+ ptr->app.data) + 4;
+ zr36050_write(ptr, ZR050_COM_IDX, 0xff);
+ zr36050_write(ptr, ZR050_COM_IDX + 1, 0xfe);
+ zr36050_write(ptr, ZR050_COM_IDX + 2, 0x00);
+ zr36050_write(ptr, ZR050_COM_IDX + 3, ptr->com.len + 2);
+ sum += zr36050_pushit(ptr, ZR050_COM_IDX + 4, 60,
+ ptr->com.data) + 4;
+
+ /* do the internal huffman table preload */
+ zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI);
+
+ zr36050_write(ptr, ZR050_GO, 1); // launch codec
+ zr36050_wait_end(ptr);
+ dprintk(2, "%s: Status after table preload: 0x%02x\n",
+ ptr->name, ptr->status1);
+
+ if ((ptr->status1 & 0x4) == 0) {
+ dprintk(1, KERN_ERR "%s: init aborted!\n",
+ ptr->name);
+ return; // something is wrong, its timed out!!!!
+ }
+
+ /* setup misc. data for compression (target code sizes) */
+
+ /* size of compressed code to reach without header data */
+ sum = ptr->real_code_vol - sum;
+ bitcnt = sum << 3; /* need the size in bits */
+
+ tmp = bitcnt >> 16;
+ dprintk(3,
+ "%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n",
+ ptr->name, sum, ptr->real_code_vol, bitcnt, tmp);
+ zr36050_write(ptr, ZR050_TCV_NET_HI, tmp >> 8);
+ zr36050_write(ptr, ZR050_TCV_NET_MH, tmp & 0xff);
+ tmp = bitcnt & 0xffff;
+ zr36050_write(ptr, ZR050_TCV_NET_ML, tmp >> 8);
+ zr36050_write(ptr, ZR050_TCV_NET_LO, tmp & 0xff);
+
+ bitcnt -= bitcnt >> 7; // bits without stuffing
+ bitcnt -= ((bitcnt * 5) >> 6); // bits without eob
+
+ tmp = bitcnt >> 16;
+ dprintk(3, "%s: code: nettobit=%ld, highnettobits=%ld\n",
+ ptr->name, bitcnt, tmp);
+ zr36050_write(ptr, ZR050_TCV_DATA_HI, tmp >> 8);
+ zr36050_write(ptr, ZR050_TCV_DATA_MH, tmp & 0xff);
+ tmp = bitcnt & 0xffff;
+ zr36050_write(ptr, ZR050_TCV_DATA_ML, tmp >> 8);
+ zr36050_write(ptr, ZR050_TCV_DATA_LO, tmp & 0xff);
+
+ /* compression setup with or without bitrate control */
+ zr36050_write(ptr, ZR050_MODE,
+ ZR050_MO_COMP | ZR050_MO_PASS2 |
+ (ptr->bitrate_ctrl ? ZR050_MO_BRC : 0));
+
+ /* this headers seem to deliver "valid AVI" jpeg frames */
+ zr36050_write(ptr, ZR050_MARKERS_EN,
+ ZR050_ME_DQT | ZR050_ME_DHT |
+ ((ptr->app.len > 0) ? ZR050_ME_APP : 0) |
+ ((ptr->com.len > 0) ? ZR050_ME_COM : 0));
+ } else {
+ dprintk(2, "%s: EXPANSION SETUP\n", ptr->name);
+
+ /* 050 communicates with 055 in master mode */
+ zr36050_write(ptr, ZR050_HARDWARE,
+ ZR050_HW_MSTR | ZR050_HW_CFIS_2_CLK);
+
+ /* encoding table preload */
+ zr36050_write(ptr, ZR050_MODE, ZR050_MO_TLM);
+
+ /* disable all IRQs */
+ zr36050_write(ptr, ZR050_INT_REQ_0, 0);
+ zr36050_write(ptr, ZR050_INT_REQ_1, 3); // low 2 bits always 1
+
+ dprintk(3, "%s: write DHT\n", ptr->name);
+ zr36050_pushit(ptr, ZR050_DHT_IDX, sizeof(zr36050_dht),
+ zr36050_dht);
+
+ /* do the internal huffman table preload */
+ zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI);
+
+ zr36050_write(ptr, ZR050_GO, 1); // launch codec
+ zr36050_wait_end(ptr);
+ dprintk(2, "%s: Status after table preload: 0x%02x\n",
+ ptr->name, ptr->status1);
+
+ if ((ptr->status1 & 0x4) == 0) {
+ dprintk(1, KERN_ERR "%s: init aborted!\n",
+ ptr->name);
+ return; // something is wrong, its timed out!!!!
+ }
+
+ /* setup misc. data for expansion */
+ zr36050_write(ptr, ZR050_MODE, 0);
+ zr36050_write(ptr, ZR050_MARKERS_EN, 0);
+ }
+
+ /* adr on selected, to allow GO from master */
+ zr36050_read(ptr, 0);
+}
+
+/* =========================================================================
+ CODEC API FUNCTIONS
+
+ this functions are accessed by the master via the API structure
+ ========================================================================= */
+
+/* set compression/expansion mode and launches codec -
+ this should be the last call from the master before starting processing */
+static int
+zr36050_set_mode (struct videocodec *codec,
+ int mode)
+{
+ struct zr36050 *ptr = (struct zr36050 *) codec->data;
+
+ dprintk(2, "%s: set_mode %d call\n", ptr->name, mode);
+
+ if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION))
+ return -EINVAL;
+
+ ptr->mode = mode;
+ zr36050_init(ptr);
+
+ return 0;
+}
+
+/* set picture size (norm is ignored as the codec doesn't know about it) */
+static int
+zr36050_set_video (struct videocodec *codec,
+ struct tvnorm *norm,
+ struct vfe_settings *cap,
+ struct vfe_polarity *pol)
+{
+ struct zr36050 *ptr = (struct zr36050 *) codec->data;
+ int size;
+
+ dprintk(2, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) q%d call\n",
+ ptr->name, norm->HStart, norm->VStart,
+ cap->x, cap->y, cap->width, cap->height,
+ cap->decimation, cap->quality);
+ /* if () return -EINVAL;
+ * trust the master driver that it knows what it does - so
+ * we allow invalid startx/y and norm for now ... */
+ ptr->width = cap->width / (cap->decimation & 0xff);
+ ptr->height = cap->height / ((cap->decimation >> 8) & 0xff);
+
+ /* (KM) JPEG quality */
+ size = ptr->width * ptr->height;
+ size *= 16; /* size in bits */
+ /* apply quality setting */
+ size = size * cap->quality / 200;
+
+ /* Minimum: 1kb */
+ if (size < 8192)
+ size = 8192;
+ /* Maximum: 7/8 of code buffer */
+ if (size > ptr->total_code_vol * 7)
+ size = ptr->total_code_vol * 7;
+
+ ptr->real_code_vol = size >> 3; /* in bytes */
+
+ /* Set max_block_vol here (previously in zr36050_init, moved
+ * here for consistency with zr36060 code */
+ zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol);
+
+ return 0;
+}
+
+/* additional control functions */
+static int
+zr36050_control (struct videocodec *codec,
+ int type,
+ int size,
+ void *data)
+{
+ struct zr36050 *ptr = (struct zr36050 *) codec->data;
+ int *ival = (int *) data;
+
+ dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type,
+ size);
+
+ switch (type) {
+ case CODEC_G_STATUS: /* get last status */
+ if (size != sizeof(int))
+ return -EFAULT;
+ zr36050_read_status1(ptr);
+ *ival = ptr->status1;
+ break;
+
+ case CODEC_G_CODEC_MODE:
+ if (size != sizeof(int))
+ return -EFAULT;
+ *ival = CODEC_MODE_BJPG;
+ break;
+
+ case CODEC_S_CODEC_MODE:
+ if (size != sizeof(int))
+ return -EFAULT;
+ if (*ival != CODEC_MODE_BJPG)
+ return -EINVAL;
+ /* not needed, do nothing */
+ return 0;
+
+ case CODEC_G_VFE:
+ case CODEC_S_VFE:
+ /* not needed, do nothing */
+ return 0;
+
+ case CODEC_S_MMAP:
+ /* not available, give an error */
+ return -ENXIO;
+
+ case CODEC_G_JPEG_TDS_BYTE: /* get target volume in byte */
+ if (size != sizeof(int))
+ return -EFAULT;
+ *ival = ptr->total_code_vol;
+ break;
+
+ case CODEC_S_JPEG_TDS_BYTE: /* get target volume in byte */
+ if (size != sizeof(int))
+ return -EFAULT;
+ ptr->total_code_vol = *ival;
+ /* (Kieran Morrissey)
+ * code copied from zr36060.c to ensure proper bitrate */
+ ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3;
+ break;
+
+ case CODEC_G_JPEG_SCALE: /* get scaling factor */
+ if (size != sizeof(int))
+ return -EFAULT;
+ *ival = zr36050_read_scalefactor(ptr);
+ break;
+
+ case CODEC_S_JPEG_SCALE: /* set scaling factor */
+ if (size != sizeof(int))
+ return -EFAULT;
+ ptr->scalefact = *ival;
+ break;
+
+ case CODEC_G_JPEG_APP_DATA: { /* get appn marker data */
+ struct jpeg_app_marker *app = data;
+
+ if (size != sizeof(struct jpeg_app_marker))
+ return -EFAULT;
+
+ *app = ptr->app;
+ break;
+ }
+
+ case CODEC_S_JPEG_APP_DATA: { /* set appn marker data */
+ struct jpeg_app_marker *app = data;
+
+ if (size != sizeof(struct jpeg_app_marker))
+ return -EFAULT;
+
+ ptr->app = *app;
+ break;
+ }
+
+ case CODEC_G_JPEG_COM_DATA: { /* get comment marker data */
+ struct jpeg_com_marker *com = data;
+
+ if (size != sizeof(struct jpeg_com_marker))
+ return -EFAULT;
+
+ *com = ptr->com;
+ break;
+ }
+
+ case CODEC_S_JPEG_COM_DATA: { /* set comment marker data */
+ struct jpeg_com_marker *com = data;
+
+ if (size != sizeof(struct jpeg_com_marker))
+ return -EFAULT;
+
+ ptr->com = *com;
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+
+ return size;
+}
+
+/* =========================================================================
+ Exit and unregister function:
+
+ Deinitializes Zoran's JPEG processor
+ ========================================================================= */
+
+static int
+zr36050_unset (struct videocodec *codec)
+{
+ struct zr36050 *ptr = codec->data;
+
+ if (ptr) {
+ /* do wee need some codec deinit here, too ???? */
+
+ dprintk(1, "%s: finished codec #%d\n", ptr->name,
+ ptr->num);
+ kfree(ptr);
+ codec->data = NULL;
+
+ zr36050_codecs--;
+ return 0;
+ }
+
+ return -EFAULT;
+}
+
+/* =========================================================================
+ Setup and registry function:
+
+ Initializes Zoran's JPEG processor
+
+ Also sets pixel size, average code size, mode (compr./decompr.)
+ (the given size is determined by the processor with the video interface)
+ ========================================================================= */
+
+static int
+zr36050_setup (struct videocodec *codec)
+{
+ struct zr36050 *ptr;
+ int res;
+
+ dprintk(2, "zr36050: initializing MJPEG subsystem #%d.\n",
+ zr36050_codecs);
+
+ if (zr36050_codecs == MAX_CODECS) {
+ dprintk(1,
+ KERN_ERR "zr36050: Can't attach more codecs!\n");
+ return -ENOSPC;
+ }
+ //mem structure init
+ codec->data = ptr = kzalloc(sizeof(struct zr36050), GFP_KERNEL);
+ if (NULL == ptr) {
+ dprintk(1, KERN_ERR "zr36050: Can't get enough memory!\n");
+ return -ENOMEM;
+ }
+
+ snprintf(ptr->name, sizeof(ptr->name), "zr36050[%d]",
+ zr36050_codecs);
+ ptr->num = zr36050_codecs++;
+ ptr->codec = codec;
+
+ //testing
+ res = zr36050_basic_test(ptr);
+ if (res < 0) {
+ zr36050_unset(codec);
+ return res;
+ }
+ //final setup
+ memcpy(ptr->h_samp_ratio, zr36050_decimation_h, 8);
+ memcpy(ptr->v_samp_ratio, zr36050_decimation_v, 8);
+
+ ptr->bitrate_ctrl = 0; /* 0 or 1 - fixed file size flag
+ * (what is the difference?) */
+ ptr->mode = CODEC_DO_COMPRESSION;
+ ptr->width = 384;
+ ptr->height = 288;
+ ptr->total_code_vol = 16000;
+ ptr->max_block_vol = 240;
+ ptr->scalefact = 0x100;
+ ptr->dri = 1;
+
+ /* no app/com marker by default */
+ ptr->app.appn = 0;
+ ptr->app.len = 0;
+ ptr->com.len = 0;
+
+ zr36050_init(ptr);
+
+ dprintk(1, KERN_INFO "%s: codec attached and running\n",
+ ptr->name);
+
+ return 0;
+}
+
+static const struct videocodec zr36050_codec = {
+ .owner = THIS_MODULE,
+ .name = "zr36050",
+ .magic = 0L, // magic not used
+ .flags =
+ CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER |
+ CODEC_FLAG_DECODER,
+ .type = CODEC_TYPE_ZR36050,
+ .setup = zr36050_setup, // functionality
+ .unset = zr36050_unset,
+ .set_mode = zr36050_set_mode,
+ .set_video = zr36050_set_video,
+ .control = zr36050_control,
+ // others are not used
+};
+
+/* =========================================================================
+ HOOK IN DRIVER AS KERNEL MODULE
+ ========================================================================= */
+
+static int __init
+zr36050_init_module (void)
+{
+ //dprintk(1, "ZR36050 driver %s\n",ZR050_VERSION);
+ zr36050_codecs = 0;
+ return videocodec_register(&zr36050_codec);
+}
+
+static void __exit
+zr36050_cleanup_module (void)
+{
+ if (zr36050_codecs) {
+ dprintk(1,
+ "zr36050: something's wrong - %d codecs left somehow.\n",
+ zr36050_codecs);
+ }
+ videocodec_unregister(&zr36050_codec);
+}
+
+module_init(zr36050_init_module);
+module_exit(zr36050_cleanup_module);
+
+MODULE_AUTHOR("Wolfgang Scherr <scherr@net4you.at>");
+MODULE_DESCRIPTION("Driver module for ZR36050 jpeg processors "
+ ZR050_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/video/zr36050.h b/linux/drivers/media/video/zr36050.h
new file mode 100644
index 000000000..9f52f0cdd
--- /dev/null
+++ b/linux/drivers/media/video/zr36050.h
@@ -0,0 +1,184 @@
+/*
+ * Zoran ZR36050 basic configuration functions - header file
+ *
+ * Copyright (C) 2001 Wolfgang Scherr <scherr@net4you.at>
+ *
+ * $Id: zr36050.h,v 1.1.2.2 2003/01/14 21:18:22 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+#ifndef ZR36050_H
+#define ZR36050_H
+
+#include "videocodec.h"
+
+/* data stored for each zoran jpeg codec chip */
+struct zr36050 {
+ char name[32];
+ int num;
+ /* io datastructure */
+ struct videocodec *codec;
+ // last coder status
+ __u8 status1;
+ // actual coder setup
+ int mode;
+
+ __u16 width;
+ __u16 height;
+
+ __u16 bitrate_ctrl;
+
+ __u32 total_code_vol;
+ __u32 real_code_vol;
+ __u16 max_block_vol;
+
+ __u8 h_samp_ratio[8];
+ __u8 v_samp_ratio[8];
+ __u16 scalefact;
+ __u16 dri;
+
+ /* com/app marker */
+ struct jpeg_com_marker com;
+ struct jpeg_app_marker app;
+};
+
+/* zr36050 register addresses */
+#define ZR050_GO 0x000
+#define ZR050_HARDWARE 0x002
+#define ZR050_MODE 0x003
+#define ZR050_OPTIONS 0x004
+#define ZR050_MBCV 0x005
+#define ZR050_MARKERS_EN 0x006
+#define ZR050_INT_REQ_0 0x007
+#define ZR050_INT_REQ_1 0x008
+#define ZR050_TCV_NET_HI 0x009
+#define ZR050_TCV_NET_MH 0x00a
+#define ZR050_TCV_NET_ML 0x00b
+#define ZR050_TCV_NET_LO 0x00c
+#define ZR050_TCV_DATA_HI 0x00d
+#define ZR050_TCV_DATA_MH 0x00e
+#define ZR050_TCV_DATA_ML 0x00f
+#define ZR050_TCV_DATA_LO 0x010
+#define ZR050_SF_HI 0x011
+#define ZR050_SF_LO 0x012
+#define ZR050_AF_HI 0x013
+#define ZR050_AF_M 0x014
+#define ZR050_AF_LO 0x015
+#define ZR050_ACV_HI 0x016
+#define ZR050_ACV_MH 0x017
+#define ZR050_ACV_ML 0x018
+#define ZR050_ACV_LO 0x019
+#define ZR050_ACT_HI 0x01a
+#define ZR050_ACT_MH 0x01b
+#define ZR050_ACT_ML 0x01c
+#define ZR050_ACT_LO 0x01d
+#define ZR050_ACV_TRUN_HI 0x01e
+#define ZR050_ACV_TRUN_MH 0x01f
+#define ZR050_ACV_TRUN_ML 0x020
+#define ZR050_ACV_TRUN_LO 0x021
+#define ZR050_STATUS_0 0x02e
+#define ZR050_STATUS_1 0x02f
+
+#define ZR050_SOF_IDX 0x040
+#define ZR050_SOS1_IDX 0x07a
+#define ZR050_SOS2_IDX 0x08a
+#define ZR050_SOS3_IDX 0x09a
+#define ZR050_SOS4_IDX 0x0aa
+#define ZR050_DRI_IDX 0x0c0
+#define ZR050_DNL_IDX 0x0c6
+#define ZR050_DQT_IDX 0x0cc
+#define ZR050_DHT_IDX 0x1d4
+#define ZR050_APP_IDX 0x380
+#define ZR050_COM_IDX 0x3c0
+
+/* zr36050 hardware register bits */
+
+#define ZR050_HW_BSWD 0x80
+#define ZR050_HW_MSTR 0x40
+#define ZR050_HW_DMA 0x20
+#define ZR050_HW_CFIS_1_CLK 0x00
+#define ZR050_HW_CFIS_2_CLK 0x04
+#define ZR050_HW_CFIS_3_CLK 0x08
+#define ZR050_HW_CFIS_4_CLK 0x0C
+#define ZR050_HW_CFIS_5_CLK 0x10
+#define ZR050_HW_CFIS_6_CLK 0x14
+#define ZR050_HW_CFIS_7_CLK 0x18
+#define ZR050_HW_CFIS_8_CLK 0x1C
+#define ZR050_HW_BELE 0x01
+
+/* zr36050 mode register bits */
+
+#define ZR050_MO_COMP 0x80
+#define ZR050_MO_COMP 0x80
+#define ZR050_MO_ATP 0x40
+#define ZR050_MO_PASS2 0x20
+#define ZR050_MO_TLM 0x10
+#define ZR050_MO_DCONLY 0x08
+#define ZR050_MO_BRC 0x04
+
+#define ZR050_MO_ATP 0x40
+#define ZR050_MO_PASS2 0x20
+#define ZR050_MO_TLM 0x10
+#define ZR050_MO_DCONLY 0x08
+
+/* zr36050 option register bits */
+
+#define ZR050_OP_NSCN_1 0x00
+#define ZR050_OP_NSCN_2 0x20
+#define ZR050_OP_NSCN_3 0x40
+#define ZR050_OP_NSCN_4 0x60
+#define ZR050_OP_NSCN_5 0x80
+#define ZR050_OP_NSCN_6 0xA0
+#define ZR050_OP_NSCN_7 0xC0
+#define ZR050_OP_NSCN_8 0xE0
+#define ZR050_OP_OVF 0x10
+
+
+/* zr36050 markers-enable register bits */
+
+#define ZR050_ME_APP 0x80
+#define ZR050_ME_COM 0x40
+#define ZR050_ME_DRI 0x20
+#define ZR050_ME_DQT 0x10
+#define ZR050_ME_DHT 0x08
+#define ZR050_ME_DNL 0x04
+#define ZR050_ME_DQTI 0x02
+#define ZR050_ME_DHTI 0x01
+
+/* zr36050 status0/1 register bit masks */
+
+#define ZR050_ST_RST_MASK 0x20
+#define ZR050_ST_SOF_MASK 0x02
+#define ZR050_ST_SOS_MASK 0x02
+#define ZR050_ST_DATRDY_MASK 0x80
+#define ZR050_ST_MRKDET_MASK 0x40
+#define ZR050_ST_RFM_MASK 0x10
+#define ZR050_ST_RFD_MASK 0x08
+#define ZR050_ST_END_MASK 0x04
+#define ZR050_ST_TCVOVF_MASK 0x02
+#define ZR050_ST_DATOVF_MASK 0x01
+
+/* pixel component idx */
+
+#define ZR050_Y_COMPONENT 0
+#define ZR050_U_COMPONENT 1
+#define ZR050_V_COMPONENT 2
+
+#endif /*fndef ZR36050_H */
diff --git a/linux/drivers/media/video/zr36057.h b/linux/drivers/media/video/zr36057.h
new file mode 100644
index 000000000..54c9362aa
--- /dev/null
+++ b/linux/drivers/media/video/zr36057.h
@@ -0,0 +1,168 @@
+/*
+ * zr36057.h - zr36057 register offsets
+ *
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _ZR36057_H_
+#define _ZR36057_H_
+
+
+/* Zoran ZR36057 registers */
+
+#define ZR36057_VFEHCR 0x000 /* Video Front End, Horizontal Configuration Register */
+#define ZR36057_VFEHCR_HSPol (1<<30)
+#define ZR36057_VFEHCR_HStart 10
+#define ZR36057_VFEHCR_HEnd 0
+#define ZR36057_VFEHCR_Hmask 0x3ff
+
+#define ZR36057_VFEVCR 0x004 /* Video Front End, Vertical Configuration Register */
+#define ZR36057_VFEVCR_VSPol (1<<30)
+#define ZR36057_VFEVCR_VStart 10
+#define ZR36057_VFEVCR_VEnd 0
+#define ZR36057_VFEVCR_Vmask 0x3ff
+
+#define ZR36057_VFESPFR 0x008 /* Video Front End, Scaler and Pixel Format Register */
+#define ZR36057_VFESPFR_ExtFl (1<<26)
+#define ZR36057_VFESPFR_TopField (1<<25)
+#define ZR36057_VFESPFR_VCLKPol (1<<24)
+#define ZR36057_VFESPFR_HFilter 21
+#define ZR36057_VFESPFR_HorDcm 14
+#define ZR36057_VFESPFR_VerDcm 8
+#define ZR36057_VFESPFR_DispMode 6
+#define ZR36057_VFESPFR_YUV422 (0<<3)
+#define ZR36057_VFESPFR_RGB888 (1<<3)
+#define ZR36057_VFESPFR_RGB565 (2<<3)
+#define ZR36057_VFESPFR_RGB555 (3<<3)
+#define ZR36057_VFESPFR_ErrDif (1<<2)
+#define ZR36057_VFESPFR_Pack24 (1<<1)
+#define ZR36057_VFESPFR_LittleEndian (1<<0)
+
+#define ZR36057_VDTR 0x00c /* Video Display "Top" Register */
+
+#define ZR36057_VDBR 0x010 /* Video Display "Bottom" Register */
+
+#define ZR36057_VSSFGR 0x014 /* Video Stride, Status, and Frame Grab Register */
+#define ZR36057_VSSFGR_DispStride 16
+#define ZR36057_VSSFGR_VidOvf (1<<8)
+#define ZR36057_VSSFGR_SnapShot (1<<1)
+#define ZR36057_VSSFGR_FrameGrab (1<<0)
+
+#define ZR36057_VDCR 0x018 /* Video Display Configuration Register */
+#define ZR36057_VDCR_VidEn (1<<31)
+#define ZR36057_VDCR_MinPix 24
+#define ZR36057_VDCR_Triton (1<<24)
+#define ZR36057_VDCR_VidWinHt 12
+#define ZR36057_VDCR_VidWinWid 0
+
+#define ZR36057_MMTR 0x01c /* Masking Map "Top" Register */
+
+#define ZR36057_MMBR 0x020 /* Masking Map "Bottom" Register */
+
+#define ZR36057_OCR 0x024 /* Overlay Control Register */
+#define ZR36057_OCR_OvlEnable (1 << 15)
+#define ZR36057_OCR_MaskStride 0
+
+#define ZR36057_SPGPPCR 0x028 /* System, PCI, and General Purpose Pins Control Register */
+#define ZR36057_SPGPPCR_SoftReset (1<<24)
+
+#define ZR36057_GPPGCR1 0x02c /* General Purpose Pins and GuestBus Control Register (1) */
+
+#define ZR36057_MCSAR 0x030 /* MPEG Code Source Address Register */
+
+#define ZR36057_MCTCR 0x034 /* MPEG Code Transfer Control Register */
+#define ZR36057_MCTCR_CodTime (1 << 30)
+#define ZR36057_MCTCR_CEmpty (1 << 29)
+#define ZR36057_MCTCR_CFlush (1 << 28)
+#define ZR36057_MCTCR_CodGuestID 20
+#define ZR36057_MCTCR_CodGuestReg 16
+
+#define ZR36057_MCMPR 0x038 /* MPEG Code Memory Pointer Register */
+
+#define ZR36057_ISR 0x03c /* Interrupt Status Register */
+#define ZR36057_ISR_GIRQ1 (1<<30)
+#define ZR36057_ISR_GIRQ0 (1<<29)
+#define ZR36057_ISR_CodRepIRQ (1<<28)
+#define ZR36057_ISR_JPEGRepIRQ (1<<27)
+
+#define ZR36057_ICR 0x040 /* Interrupt Control Register */
+#define ZR36057_ICR_GIRQ1 (1<<30)
+#define ZR36057_ICR_GIRQ0 (1<<29)
+#define ZR36057_ICR_CodRepIRQ (1<<28)
+#define ZR36057_ICR_JPEGRepIRQ (1<<27)
+#define ZR36057_ICR_IntPinEn (1<<24)
+
+#define ZR36057_I2CBR 0x044 /* I2C Bus Register */
+#define ZR36057_I2CBR_SDA (1<<1)
+#define ZR36057_I2CBR_SCL (1<<0)
+
+#define ZR36057_JMC 0x100 /* JPEG Mode and Control */
+#define ZR36057_JMC_JPG (1 << 31)
+#define ZR36057_JMC_JPGExpMode (0 << 29)
+#define ZR36057_JMC_JPGCmpMode (1 << 29)
+#define ZR36057_JMC_MJPGExpMode (2 << 29)
+#define ZR36057_JMC_MJPGCmpMode (3 << 29)
+#define ZR36057_JMC_RTBUSY_FB (1 << 6)
+#define ZR36057_JMC_Go_en (1 << 5)
+#define ZR36057_JMC_SyncMstr (1 << 4)
+#define ZR36057_JMC_Fld_per_buff (1 << 3)
+#define ZR36057_JMC_VFIFO_FB (1 << 2)
+#define ZR36057_JMC_CFIFO_FB (1 << 1)
+#define ZR36057_JMC_Stll_LitEndian (1 << 0)
+
+#define ZR36057_JPC 0x104 /* JPEG Process Control */
+#define ZR36057_JPC_P_Reset (1 << 7)
+#define ZR36057_JPC_CodTrnsEn (1 << 5)
+#define ZR36057_JPC_Active (1 << 0)
+
+#define ZR36057_VSP 0x108 /* Vertical Sync Parameters */
+#define ZR36057_VSP_VsyncSize 16
+#define ZR36057_VSP_FrmTot 0
+
+#define ZR36057_HSP 0x10c /* Horizontal Sync Parameters */
+#define ZR36057_HSP_HsyncStart 16
+#define ZR36057_HSP_LineTot 0
+
+#define ZR36057_FHAP 0x110 /* Field Horizontal Active Portion */
+#define ZR36057_FHAP_NAX 16
+#define ZR36057_FHAP_PAX 0
+
+#define ZR36057_FVAP 0x114 /* Field Vertical Active Portion */
+#define ZR36057_FVAP_NAY 16
+#define ZR36057_FVAP_PAY 0
+
+#define ZR36057_FPP 0x118 /* Field Process Parameters */
+#define ZR36057_FPP_Odd_Even (1 << 0)
+
+#define ZR36057_JCBA 0x11c /* JPEG Code Base Address */
+
+#define ZR36057_JCFT 0x120 /* JPEG Code FIFO Threshold */
+
+#define ZR36057_JCGI 0x124 /* JPEG Codec Guest ID */
+#define ZR36057_JCGI_JPEGuestID 4
+#define ZR36057_JCGI_JPEGuestReg 0
+
+#define ZR36057_GCR2 0x12c /* GuestBus Control Register (2) */
+
+#define ZR36057_POR 0x200 /* Post Office Register */
+#define ZR36057_POR_POPen (1<<25)
+#define ZR36057_POR_POTime (1<<24)
+#define ZR36057_POR_PODir (1<<23)
+
+#define ZR36057_STR 0x300 /* "Still" Transfer Register */
+
+#endif
diff --git a/linux/drivers/media/video/zr36060.c b/linux/drivers/media/video/zr36060.c
new file mode 100644
index 000000000..97c8f9b9d
--- /dev/null
+++ b/linux/drivers/media/video/zr36060.c
@@ -0,0 +1,1014 @@
+/*
+ * Zoran ZR36060 basic configuration functions
+ *
+ * Copyright (C) 2002 Laurent Pinchart <laurent.pinchart@skynet.be>
+ *
+ * $Id: zr36060.c,v 1.1.2.22 2003/05/06 09:35:36 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+#define ZR060_VERSION "v0.7"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include <linux/types.h>
+#include <linux/wait.h>
+
+/* includes for structures and defines regarding video
+ #include<linux/videodev.h> */
+
+/* I/O commands, error codes */
+#include<asm/io.h>
+//#include<errno.h>
+
+/* headerfile of this module */
+#include"zr36060.h"
+
+/* codec io API */
+#include"videocodec.h"
+
+/* it doesn't make sense to have more than 20 or so,
+ just to prevent some unwanted loops */
+#define MAX_CODECS 20
+
+/* amount of chips attached via this driver */
+static int zr36060_codecs = 0;
+
+static int low_bitrate = 0;
+module_param(low_bitrate, bool, 0);
+MODULE_PARM_DESC(low_bitrate, "Buz compatibility option, halves bitrate");
+
+/* debugging is available via module parameter */
+static int debug = 0;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-4)");
+
+#define dprintk(num, format, args...) \
+ do { \
+ if (debug >= num) \
+ printk(format, ##args); \
+ } while (0)
+
+/* =========================================================================
+ Local hardware I/O functions:
+
+ read/write via codec layer (registers are located in the master device)
+ ========================================================================= */
+
+/* read and write functions */
+static u8
+zr36060_read (struct zr36060 *ptr,
+ u16 reg)
+{
+ u8 value = 0;
+
+ // just in case something is wrong...
+ if (ptr->codec->master_data->readreg)
+ value = (ptr->codec->master_data->readreg(ptr->codec,
+ reg)) & 0xff;
+ else
+ dprintk(1,
+ KERN_ERR "%s: invalid I/O setup, nothing read!\n",
+ ptr->name);
+
+ //dprintk(4, "%s: reading from 0x%04x: %02x\n",ptr->name,reg,value);
+
+ return value;
+}
+
+static void
+zr36060_write(struct zr36060 *ptr,
+ u16 reg,
+ u8 value)
+{
+ //dprintk(4, "%s: writing 0x%02x to 0x%04x\n",ptr->name,value,reg);
+ dprintk(4, "0x%02x @0x%04x\n", value, reg);
+
+ // just in case something is wrong...
+ if (ptr->codec->master_data->writereg)
+ ptr->codec->master_data->writereg(ptr->codec, reg, value);
+ else
+ dprintk(1,
+ KERN_ERR
+ "%s: invalid I/O setup, nothing written!\n",
+ ptr->name);
+}
+
+/* =========================================================================
+ Local helper function:
+
+ status read
+ ========================================================================= */
+
+/* status is kept in datastructure */
+static u8
+zr36060_read_status (struct zr36060 *ptr)
+{
+ ptr->status = zr36060_read(ptr, ZR060_CFSR);
+
+ zr36060_read(ptr, 0);
+ return ptr->status;
+}
+
+/* =========================================================================
+ Local helper function:
+
+ scale factor read
+ ========================================================================= */
+
+/* scale factor is kept in datastructure */
+static u16
+zr36060_read_scalefactor (struct zr36060 *ptr)
+{
+ ptr->scalefact = (zr36060_read(ptr, ZR060_SF_HI) << 8) |
+ (zr36060_read(ptr, ZR060_SF_LO) & 0xFF);
+
+ /* leave 0 selected for an eventually GO from master */
+ zr36060_read(ptr, 0);
+ return ptr->scalefact;
+}
+
+/* =========================================================================
+ Local helper function:
+
+ wait if codec is ready to proceed (end of processing) or time is over
+ ========================================================================= */
+
+static void
+zr36060_wait_end (struct zr36060 *ptr)
+{
+ int i = 0;
+
+ while (zr36060_read_status(ptr) & ZR060_CFSR_Busy) {
+ udelay(1);
+ if (i++ > 200000) { // 200ms, there is for sure something wrong!!!
+ dprintk(1,
+ "%s: timout at wait_end (last status: 0x%02x)\n",
+ ptr->name, ptr->status);
+ break;
+ }
+ }
+}
+
+/* =========================================================================
+ Local helper function:
+
+ basic test of "connectivity", writes/reads to/from memory the SOF marker
+ ========================================================================= */
+
+static int
+zr36060_basic_test (struct zr36060 *ptr)
+{
+ if ((zr36060_read(ptr, ZR060_IDR_DEV) != 0x33) &&
+ (zr36060_read(ptr, ZR060_IDR_REV) != 0x01)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: attach failed, can't connect to jpeg processor!\n",
+ ptr->name);
+ return -ENXIO;
+ }
+
+ zr36060_wait_end(ptr);
+ if (ptr->status & ZR060_CFSR_Busy) {
+ dprintk(1,
+ KERN_ERR
+ "%s: attach failed, jpeg processor failed (end flag)!\n",
+ ptr->name);
+ return -EBUSY;
+ }
+
+ return 0; /* looks good! */
+}
+
+/* =========================================================================
+ Local helper function:
+
+ simple loop for pushing the init datasets
+ ========================================================================= */
+
+static int
+zr36060_pushit (struct zr36060 *ptr,
+ u16 startreg,
+ u16 len,
+ const char *data)
+{
+ int i = 0;
+
+ dprintk(4, "%s: write data block to 0x%04x (len=%d)\n", ptr->name,
+ startreg, len);
+ while (i < len) {
+ zr36060_write(ptr, startreg++, data[i++]);
+ }
+
+ return i;
+}
+
+/* =========================================================================
+ Basic datasets:
+
+ jpeg baseline setup data (you find it on lots places in internet, or just
+ extract it from any regular .jpg image...)
+
+ Could be variable, but until it's not needed it they are just fixed to save
+ memory. Otherwise expand zr36060 structure with arrays, push the values to
+ it and initalize from there, as e.g. the linux zr36057/60 driver does it.
+ ========================================================================= */
+
+static const char zr36060_dqt[0x86] = {
+ 0xff, 0xdb, //Marker: DQT
+ 0x00, 0x84, //Length: 2*65+2
+ 0x00, //Pq,Tq first table
+ 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
+ 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
+ 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
+ 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
+ 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
+ 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
+ 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
+ 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
+ 0x01, //Pq,Tq second table
+ 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
+ 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63
+};
+
+static const char zr36060_dht[0x1a4] = {
+ 0xff, 0xc4, //Marker: DHT
+ 0x01, 0xa2, //Length: 2*AC, 2*DC
+ 0x00, //DC first table
+ 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
+ 0x01, //DC second table
+ 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
+ 0x10, //AC first table
+ 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03,
+ 0x05, 0x05, 0x04, 0x04, 0x00, 0x00,
+ 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11,
+ 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61,
+ 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1,
+ 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24,
+ 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34,
+ 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
+ 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
+ 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+ 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
+ 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
+ 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9,
+ 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8,
+ 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9,
+ 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+ 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+ 0xF8, 0xF9, 0xFA,
+ 0x11, //AC second table
+ 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
+ 0x07, 0x05, 0x04, 0x04, 0x00, 0x01,
+ 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04,
+ 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
+ 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
+ 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62,
+ 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25,
+ 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A,
+ 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56,
+ 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66,
+ 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+ 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
+ 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8,
+ 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
+ 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8,
+ 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+ 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
+ 0xF9, 0xFA
+};
+
+/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */
+#define NO_OF_COMPONENTS 0x3 //Y,U,V
+#define BASELINE_PRECISION 0x8 //MCU size (?)
+static const char zr36060_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's QT
+static const char zr36060_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's DC
+static const char zr36060_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC
+
+/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */
+static const char zr36060_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 };
+static const char zr36060_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 };
+
+/* =========================================================================
+ Local helper functions:
+
+ calculation and setup of parameter-dependent JPEG baseline segments
+ (needed for compression only)
+ ========================================================================= */
+
+/* ------------------------------------------------------------------------- */
+
+/* SOF (start of frame) segment depends on width, height and sampling ratio
+ of each color component */
+
+static int
+zr36060_set_sof (struct zr36060 *ptr)
+{
+ char sof_data[34]; // max. size of register set
+ int i;
+
+ dprintk(3, "%s: write SOF (%dx%d, %d components)\n", ptr->name,
+ ptr->width, ptr->height, NO_OF_COMPONENTS);
+ sof_data[0] = 0xff;
+ sof_data[1] = 0xc0;
+ sof_data[2] = 0x00;
+ sof_data[3] = (3 * NO_OF_COMPONENTS) + 8;
+ sof_data[4] = BASELINE_PRECISION; // only '8' possible with zr36060
+ sof_data[5] = (ptr->height) >> 8;
+ sof_data[6] = (ptr->height) & 0xff;
+ sof_data[7] = (ptr->width) >> 8;
+ sof_data[8] = (ptr->width) & 0xff;
+ sof_data[9] = NO_OF_COMPONENTS;
+ for (i = 0; i < NO_OF_COMPONENTS; i++) {
+ sof_data[10 + (i * 3)] = i; // index identifier
+ sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) |
+ (ptr->v_samp_ratio[i]); // sampling ratios
+ sof_data[12 + (i * 3)] = zr36060_tq[i]; // Q table selection
+ }
+ return zr36060_pushit(ptr, ZR060_SOF_IDX,
+ (3 * NO_OF_COMPONENTS) + 10, sof_data);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* SOS (start of scan) segment depends on the used scan components
+ of each color component */
+
+static int
+zr36060_set_sos (struct zr36060 *ptr)
+{
+ char sos_data[16]; // max. size of register set
+ int i;
+
+ dprintk(3, "%s: write SOS\n", ptr->name);
+ sos_data[0] = 0xff;
+ sos_data[1] = 0xda;
+ sos_data[2] = 0x00;
+ sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3;
+ sos_data[4] = NO_OF_COMPONENTS;
+ for (i = 0; i < NO_OF_COMPONENTS; i++) {
+ sos_data[5 + (i * 2)] = i; // index
+ sos_data[6 + (i * 2)] = (zr36060_td[i] << 4) |
+ zr36060_ta[i]; // AC/DC tbl.sel.
+ }
+ sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00; // scan start
+ sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3f;
+ sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00;
+ return zr36060_pushit(ptr, ZR060_SOS_IDX,
+ 4 + 1 + (2 * NO_OF_COMPONENTS) + 3,
+ sos_data);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* DRI (define restart interval) */
+
+static int
+zr36060_set_dri (struct zr36060 *ptr)
+{
+ char dri_data[6]; // max. size of register set
+
+ dprintk(3, "%s: write DRI\n", ptr->name);
+ dri_data[0] = 0xff;
+ dri_data[1] = 0xdd;
+ dri_data[2] = 0x00;
+ dri_data[3] = 0x04;
+ dri_data[4] = (ptr->dri) >> 8;
+ dri_data[5] = (ptr->dri) & 0xff;
+ return zr36060_pushit(ptr, ZR060_DRI_IDX, 6, dri_data);
+}
+
+/* =========================================================================
+ Setup function:
+
+ Setup compression/decompression of Zoran's JPEG processor
+ ( see also zoran 36060 manual )
+
+ ... sorry for the spaghetti code ...
+ ========================================================================= */
+static void
+zr36060_init (struct zr36060 *ptr)
+{
+ int sum = 0;
+ long bitcnt, tmp;
+
+ if (ptr->mode == CODEC_DO_COMPRESSION) {
+ dprintk(2, "%s: COMPRESSION SETUP\n", ptr->name);
+
+ zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst);
+
+ /* 060 communicates with 067 in master mode */
+ zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CodeMstr);
+
+ /* Compression with or without variable scale factor */
+ /*FIXME: What about ptr->bitrate_ctrl? */
+ zr36060_write(ptr, ZR060_CMR,
+ ZR060_CMR_Comp | ZR060_CMR_Pass2 |
+ ZR060_CMR_BRB);
+
+ /* Must be zero */
+ zr36060_write(ptr, ZR060_MBZ, 0x00);
+ zr36060_write(ptr, ZR060_TCR_HI, 0x00);
+ zr36060_write(ptr, ZR060_TCR_LO, 0x00);
+
+ /* Disable all IRQs - no DataErr means autoreset */
+ zr36060_write(ptr, ZR060_IMR, 0);
+
+ /* volume control settings */
+ zr36060_write(ptr, ZR060_SF_HI, ptr->scalefact >> 8);
+ zr36060_write(ptr, ZR060_SF_LO, ptr->scalefact & 0xff);
+
+ zr36060_write(ptr, ZR060_AF_HI, 0xff);
+ zr36060_write(ptr, ZR060_AF_M, 0xff);
+ zr36060_write(ptr, ZR060_AF_LO, 0xff);
+
+ /* setup the variable jpeg tables */
+ sum += zr36060_set_sof(ptr);
+ sum += zr36060_set_sos(ptr);
+ sum += zr36060_set_dri(ptr);
+
+ /* setup the fixed jpeg tables - maybe variable, though -
+ * (see table init section above) */
+ sum +=
+ zr36060_pushit(ptr, ZR060_DQT_IDX, sizeof(zr36060_dqt),
+ zr36060_dqt);
+ sum +=
+ zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht),
+ zr36060_dht);
+ zr36060_write(ptr, ZR060_APP_IDX, 0xff);
+ zr36060_write(ptr, ZR060_APP_IDX + 1, 0xe0 + ptr->app.appn);
+ zr36060_write(ptr, ZR060_APP_IDX + 2, 0x00);
+ zr36060_write(ptr, ZR060_APP_IDX + 3, ptr->app.len + 2);
+ sum += zr36060_pushit(ptr, ZR060_APP_IDX + 4, 60,
+ ptr->app.data) + 4;
+ zr36060_write(ptr, ZR060_COM_IDX, 0xff);
+ zr36060_write(ptr, ZR060_COM_IDX + 1, 0xfe);
+ zr36060_write(ptr, ZR060_COM_IDX + 2, 0x00);
+ zr36060_write(ptr, ZR060_COM_IDX + 3, ptr->com.len + 2);
+ sum += zr36060_pushit(ptr, ZR060_COM_IDX + 4, 60,
+ ptr->com.data) + 4;
+
+ /* setup misc. data for compression (target code sizes) */
+
+ /* size of compressed code to reach without header data */
+ sum = ptr->real_code_vol - sum;
+ bitcnt = sum << 3; /* need the size in bits */
+
+ tmp = bitcnt >> 16;
+ dprintk(3,
+ "%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n",
+ ptr->name, sum, ptr->real_code_vol, bitcnt, tmp);
+ zr36060_write(ptr, ZR060_TCV_NET_HI, tmp >> 8);
+ zr36060_write(ptr, ZR060_TCV_NET_MH, tmp & 0xff);
+ tmp = bitcnt & 0xffff;
+ zr36060_write(ptr, ZR060_TCV_NET_ML, tmp >> 8);
+ zr36060_write(ptr, ZR060_TCV_NET_LO, tmp & 0xff);
+
+ bitcnt -= bitcnt >> 7; // bits without stuffing
+ bitcnt -= ((bitcnt * 5) >> 6); // bits without eob
+
+ tmp = bitcnt >> 16;
+ dprintk(3, "%s: code: nettobit=%ld, highnettobits=%ld\n",
+ ptr->name, bitcnt, tmp);
+ zr36060_write(ptr, ZR060_TCV_DATA_HI, tmp >> 8);
+ zr36060_write(ptr, ZR060_TCV_DATA_MH, tmp & 0xff);
+ tmp = bitcnt & 0xffff;
+ zr36060_write(ptr, ZR060_TCV_DATA_ML, tmp >> 8);
+ zr36060_write(ptr, ZR060_TCV_DATA_LO, tmp & 0xff);
+
+ /* JPEG markers to be included in the compressed stream */
+ zr36060_write(ptr, ZR060_MER,
+ ZR060_MER_DQT | ZR060_MER_DHT |
+ ((ptr->com.len > 0) ? ZR060_MER_Com : 0) |
+ ((ptr->app.len > 0) ? ZR060_MER_App : 0));
+
+ /* Setup the Video Frontend */
+ /* Limit pixel range to 16..235 as per CCIR-601 */
+ zr36060_write(ptr, ZR060_VCR, ZR060_VCR_Range);
+
+ } else {
+ dprintk(2, "%s: EXPANSION SETUP\n", ptr->name);
+
+ zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst);
+
+ /* 060 communicates with 067 in master mode */
+ zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CodeMstr);
+
+ /* Decompression */
+ zr36060_write(ptr, ZR060_CMR, 0);
+
+ /* Must be zero */
+ zr36060_write(ptr, ZR060_MBZ, 0x00);
+ zr36060_write(ptr, ZR060_TCR_HI, 0x00);
+ zr36060_write(ptr, ZR060_TCR_LO, 0x00);
+
+ /* Disable all IRQs - no DataErr means autoreset */
+ zr36060_write(ptr, ZR060_IMR, 0);
+
+ /* setup misc. data for expansion */
+ zr36060_write(ptr, ZR060_MER, 0);
+
+ /* setup the fixed jpeg tables - maybe variable, though -
+ * (see table init section above) */
+ zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht),
+ zr36060_dht);
+
+ /* Setup the Video Frontend */
+ //zr36060_write(ptr, ZR060_VCR, ZR060_VCR_FIExt);
+ //this doesn't seem right and doesn't work...
+ zr36060_write(ptr, ZR060_VCR, ZR060_VCR_Range);
+ }
+
+ /* Load the tables */
+ zr36060_write(ptr, ZR060_LOAD,
+ ZR060_LOAD_SyncRst | ZR060_LOAD_Load);
+ zr36060_wait_end(ptr);
+ dprintk(2, "%s: Status after table preload: 0x%02x\n", ptr->name,
+ ptr->status);
+
+ if (ptr->status & ZR060_CFSR_Busy) {
+ dprintk(1, KERN_ERR "%s: init aborted!\n", ptr->name);
+ return; // something is wrong, its timed out!!!!
+ }
+}
+
+/* =========================================================================
+ CODEC API FUNCTIONS
+
+ this functions are accessed by the master via the API structure
+ ========================================================================= */
+
+/* set compression/expansion mode and launches codec -
+ this should be the last call from the master before starting processing */
+static int
+zr36060_set_mode (struct videocodec *codec,
+ int mode)
+{
+ struct zr36060 *ptr = (struct zr36060 *) codec->data;
+
+ dprintk(2, "%s: set_mode %d call\n", ptr->name, mode);
+
+ if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION))
+ return -EINVAL;
+
+ ptr->mode = mode;
+ zr36060_init(ptr);
+
+ return 0;
+}
+
+/* set picture size (norm is ignored as the codec doesn't know about it) */
+static int
+zr36060_set_video (struct videocodec *codec,
+ struct tvnorm *norm,
+ struct vfe_settings *cap,
+ struct vfe_polarity *pol)
+{
+ struct zr36060 *ptr = (struct zr36060 *) codec->data;
+ u32 reg;
+ int size;
+
+ dprintk(2, "%s: set_video %d/%d-%dx%d (%%%d) call\n", ptr->name,
+ cap->x, cap->y, cap->width, cap->height, cap->decimation);
+
+ /* if () return -EINVAL;
+ * trust the master driver that it knows what it does - so
+ * we allow invalid startx/y and norm for now ... */
+ ptr->width = cap->width / (cap->decimation & 0xff);
+ ptr->height = cap->height / (cap->decimation >> 8);
+
+ zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst);
+
+ /* Note that VSPol/HSPol bits in zr36060 have the opposite
+ * meaning of their zr360x7 counterparts with the same names
+ * N.b. for VSPol this is only true if FIVEdge = 0 (default,
+ * left unchanged here - in accordance with datasheet).
+ */
+ reg = (!pol->vsync_pol ? ZR060_VPR_VSPol : 0)
+ | (!pol->hsync_pol ? ZR060_VPR_HSPol : 0)
+ | (pol->field_pol ? ZR060_VPR_FIPol : 0)
+ | (pol->blank_pol ? ZR060_VPR_BLPol : 0)
+ | (pol->subimg_pol ? ZR060_VPR_SImgPol : 0)
+ | (pol->poe_pol ? ZR060_VPR_PoePol : 0)
+ | (pol->pvalid_pol ? ZR060_VPR_PValPol : 0)
+ | (pol->vclk_pol ? ZR060_VPR_VCLKPol : 0);
+ zr36060_write(ptr, ZR060_VPR, reg);
+
+ reg = 0;
+ switch (cap->decimation & 0xff) {
+ default:
+ case 1:
+ break;
+
+ case 2:
+ reg |= ZR060_SR_HScale2;
+ break;
+
+ case 4:
+ reg |= ZR060_SR_HScale4;
+ break;
+ }
+
+ switch (cap->decimation >> 8) {
+ default:
+ case 1:
+ break;
+
+ case 2:
+ reg |= ZR060_SR_VScale;
+ break;
+ }
+ zr36060_write(ptr, ZR060_SR, reg);
+
+ zr36060_write(ptr, ZR060_BCR_Y, 0x00);
+ zr36060_write(ptr, ZR060_BCR_U, 0x80);
+ zr36060_write(ptr, ZR060_BCR_V, 0x80);
+
+ /* sync generator */
+
+ reg = norm->Ht - 1; /* Vtotal */
+ zr36060_write(ptr, ZR060_SGR_VTOTAL_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_SGR_VTOTAL_LO, (reg >> 0) & 0xff);
+
+ reg = norm->Wt - 1; /* Htotal */
+ zr36060_write(ptr, ZR060_SGR_HTOTAL_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_SGR_HTOTAL_LO, (reg >> 0) & 0xff);
+
+ reg = 6 - 1; /* VsyncSize */
+ zr36060_write(ptr, ZR060_SGR_VSYNC, reg);
+
+ //reg = 30 - 1; /* HsyncSize */
+///*CP*/ reg = (zr->params.norm == 1 ? 57 : 68);
+ reg = 68;
+ zr36060_write(ptr, ZR060_SGR_HSYNC, reg);
+
+ reg = norm->VStart - 1; /* BVstart */
+ zr36060_write(ptr, ZR060_SGR_BVSTART, reg);
+
+ reg += norm->Ha / 2; /* BVend */
+ zr36060_write(ptr, ZR060_SGR_BVEND_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_SGR_BVEND_LO, (reg >> 0) & 0xff);
+
+ reg = norm->HStart - 1; /* BHstart */
+ zr36060_write(ptr, ZR060_SGR_BHSTART, reg);
+
+ reg += norm->Wa; /* BHend */
+ zr36060_write(ptr, ZR060_SGR_BHEND_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_SGR_BHEND_LO, (reg >> 0) & 0xff);
+
+ /* active area */
+ reg = cap->y + norm->VStart; /* Vstart */
+ zr36060_write(ptr, ZR060_AAR_VSTART_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_AAR_VSTART_LO, (reg >> 0) & 0xff);
+
+ reg += cap->height; /* Vend */
+ zr36060_write(ptr, ZR060_AAR_VEND_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_AAR_VEND_LO, (reg >> 0) & 0xff);
+
+ reg = cap->x + norm->HStart; /* Hstart */
+ zr36060_write(ptr, ZR060_AAR_HSTART_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_AAR_HSTART_LO, (reg >> 0) & 0xff);
+
+ reg += cap->width; /* Hend */
+ zr36060_write(ptr, ZR060_AAR_HEND_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_AAR_HEND_LO, (reg >> 0) & 0xff);
+
+ /* subimage area */
+ reg = norm->VStart - 4; /* SVstart */
+ zr36060_write(ptr, ZR060_SWR_VSTART_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_SWR_VSTART_LO, (reg >> 0) & 0xff);
+
+ reg += norm->Ha / 2 + 8; /* SVend */
+ zr36060_write(ptr, ZR060_SWR_VEND_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_SWR_VEND_LO, (reg >> 0) & 0xff);
+
+ reg = norm->HStart /*+ 64 */ - 4; /* SHstart */
+ zr36060_write(ptr, ZR060_SWR_HSTART_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_SWR_HSTART_LO, (reg >> 0) & 0xff);
+
+ reg += norm->Wa + 8; /* SHend */
+ zr36060_write(ptr, ZR060_SWR_HEND_HI, (reg >> 8) & 0xff);
+ zr36060_write(ptr, ZR060_SWR_HEND_LO, (reg >> 0) & 0xff);
+
+ size = ptr->width * ptr->height;
+ /* Target compressed field size in bits: */
+ size = size * 16; /* uncompressed size in bits */
+ /* (Ronald) by default, quality = 100 is a compression
+ * ratio 1:2. Setting low_bitrate (insmod option) sets
+ * it to 1:4 (instead of 1:2, zr36060 max) as limit because the
+ * buz can't handle more at decimation=1... Use low_bitrate if
+ * you have a Buz, unless you know what you're doing */
+ size = size * cap->quality / (low_bitrate ? 400 : 200);
+ /* Lower limit (arbitrary, 1 KB) */
+ if (size < 8192)
+ size = 8192;
+ /* Upper limit: 7/8 of the code buffers */
+ if (size > ptr->total_code_vol * 7)
+ size = ptr->total_code_vol * 7;
+
+ ptr->real_code_vol = size >> 3; /* in bytes */
+
+ /* the MBCVR is the *maximum* block volume, according to the
+ * JPEG ISO specs, this shouldn't be used, since that allows
+ * for the best encoding quality. So set it to it's max value */
+ reg = ptr->max_block_vol;
+ zr36060_write(ptr, ZR060_MBCVR, reg);
+
+ return 0;
+}
+
+/* additional control functions */
+static int
+zr36060_control (struct videocodec *codec,
+ int type,
+ int size,
+ void *data)
+{
+ struct zr36060 *ptr = (struct zr36060 *) codec->data;
+ int *ival = (int *) data;
+
+ dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type,
+ size);
+
+ switch (type) {
+ case CODEC_G_STATUS: /* get last status */
+ if (size != sizeof(int))
+ return -EFAULT;
+ zr36060_read_status(ptr);
+ *ival = ptr->status;
+ break;
+
+ case CODEC_G_CODEC_MODE:
+ if (size != sizeof(int))
+ return -EFAULT;
+ *ival = CODEC_MODE_BJPG;
+ break;
+
+ case CODEC_S_CODEC_MODE:
+ if (size != sizeof(int))
+ return -EFAULT;
+ if (*ival != CODEC_MODE_BJPG)
+ return -EINVAL;
+ /* not needed, do nothing */
+ return 0;
+
+ case CODEC_G_VFE:
+ case CODEC_S_VFE:
+ /* not needed, do nothing */
+ return 0;
+
+ case CODEC_S_MMAP:
+ /* not available, give an error */
+ return -ENXIO;
+
+ case CODEC_G_JPEG_TDS_BYTE: /* get target volume in byte */
+ if (size != sizeof(int))
+ return -EFAULT;
+ *ival = ptr->total_code_vol;
+ break;
+
+ case CODEC_S_JPEG_TDS_BYTE: /* get target volume in byte */
+ if (size != sizeof(int))
+ return -EFAULT;
+ ptr->total_code_vol = *ival;
+ ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3;
+ break;
+
+ case CODEC_G_JPEG_SCALE: /* get scaling factor */
+ if (size != sizeof(int))
+ return -EFAULT;
+ *ival = zr36060_read_scalefactor(ptr);
+ break;
+
+ case CODEC_S_JPEG_SCALE: /* set scaling factor */
+ if (size != sizeof(int))
+ return -EFAULT;
+ ptr->scalefact = *ival;
+ break;
+
+ case CODEC_G_JPEG_APP_DATA: { /* get appn marker data */
+ struct jpeg_app_marker *app = data;
+
+ if (size != sizeof(struct jpeg_app_marker))
+ return -EFAULT;
+
+ *app = ptr->app;
+ break;
+ }
+
+ case CODEC_S_JPEG_APP_DATA: { /* set appn marker data */
+ struct jpeg_app_marker *app = data;
+
+ if (size != sizeof(struct jpeg_app_marker))
+ return -EFAULT;
+
+ ptr->app = *app;
+ break;
+ }
+
+ case CODEC_G_JPEG_COM_DATA: { /* get comment marker data */
+ struct jpeg_com_marker *com = data;
+
+ if (size != sizeof(struct jpeg_com_marker))
+ return -EFAULT;
+
+ *com = ptr->com;
+ break;
+ }
+
+ case CODEC_S_JPEG_COM_DATA: { /* set comment marker data */
+ struct jpeg_com_marker *com = data;
+
+ if (size != sizeof(struct jpeg_com_marker))
+ return -EFAULT;
+
+ ptr->com = *com;
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+
+ return size;
+}
+
+/* =========================================================================
+ Exit and unregister function:
+
+ Deinitializes Zoran's JPEG processor
+ ========================================================================= */
+
+static int
+zr36060_unset (struct videocodec *codec)
+{
+ struct zr36060 *ptr = codec->data;
+
+ if (ptr) {
+ /* do wee need some codec deinit here, too ???? */
+
+ dprintk(1, "%s: finished codec #%d\n", ptr->name,
+ ptr->num);
+ kfree(ptr);
+ codec->data = NULL;
+
+ zr36060_codecs--;
+ return 0;
+ }
+
+ return -EFAULT;
+}
+
+/* =========================================================================
+ Setup and registry function:
+
+ Initializes Zoran's JPEG processor
+
+ Also sets pixel size, average code size, mode (compr./decompr.)
+ (the given size is determined by the processor with the video interface)
+ ========================================================================= */
+
+static int
+zr36060_setup (struct videocodec *codec)
+{
+ struct zr36060 *ptr;
+ int res;
+
+ dprintk(2, "zr36060: initializing MJPEG subsystem #%d.\n",
+ zr36060_codecs);
+
+ if (zr36060_codecs == MAX_CODECS) {
+ dprintk(1,
+ KERN_ERR "zr36060: Can't attach more codecs!\n");
+ return -ENOSPC;
+ }
+ //mem structure init
+ codec->data = ptr = kzalloc(sizeof(struct zr36060), GFP_KERNEL);
+ if (NULL == ptr) {
+ dprintk(1, KERN_ERR "zr36060: Can't get enough memory!\n");
+ return -ENOMEM;
+ }
+
+ snprintf(ptr->name, sizeof(ptr->name), "zr36060[%d]",
+ zr36060_codecs);
+ ptr->num = zr36060_codecs++;
+ ptr->codec = codec;
+
+ //testing
+ res = zr36060_basic_test(ptr);
+ if (res < 0) {
+ zr36060_unset(codec);
+ return res;
+ }
+ //final setup
+ memcpy(ptr->h_samp_ratio, zr36060_decimation_h, 8);
+ memcpy(ptr->v_samp_ratio, zr36060_decimation_v, 8);
+
+ ptr->bitrate_ctrl = 0; /* 0 or 1 - fixed file size flag
+ * (what is the difference?) */
+ ptr->mode = CODEC_DO_COMPRESSION;
+ ptr->width = 384;
+ ptr->height = 288;
+ ptr->total_code_vol = 16000; /* CHECKME */
+ ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3;
+ ptr->max_block_vol = 240; /* CHECKME, was 120 is 240 */
+ ptr->scalefact = 0x100;
+ ptr->dri = 1; /* CHECKME, was 8 is 1 */
+
+ /* by default, no COM or APP markers - app should set those */
+ ptr->com.len = 0;
+ ptr->app.appn = 0;
+ ptr->app.len = 0;
+
+ zr36060_init(ptr);
+
+ dprintk(1, KERN_INFO "%s: codec attached and running\n",
+ ptr->name);
+
+ return 0;
+}
+
+static const struct videocodec zr36060_codec = {
+ .owner = THIS_MODULE,
+ .name = "zr36060",
+ .magic = 0L, // magic not used
+ .flags =
+ CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER |
+ CODEC_FLAG_DECODER | CODEC_FLAG_VFE,
+ .type = CODEC_TYPE_ZR36060,
+ .setup = zr36060_setup, // functionality
+ .unset = zr36060_unset,
+ .set_mode = zr36060_set_mode,
+ .set_video = zr36060_set_video,
+ .control = zr36060_control,
+ // others are not used
+};
+
+/* =========================================================================
+ HOOK IN DRIVER AS KERNEL MODULE
+ ========================================================================= */
+
+static int __init
+zr36060_init_module (void)
+{
+ //dprintk(1, "zr36060 driver %s\n",ZR060_VERSION);
+ zr36060_codecs = 0;
+ return videocodec_register(&zr36060_codec);
+}
+
+static void __exit
+zr36060_cleanup_module (void)
+{
+ if (zr36060_codecs) {
+ dprintk(1,
+ "zr36060: something's wrong - %d codecs left somehow.\n",
+ zr36060_codecs);
+ }
+
+ /* however, we can't just stay alive */
+ videocodec_unregister(&zr36060_codec);
+}
+
+module_init(zr36060_init_module);
+module_exit(zr36060_cleanup_module);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@skynet.be>");
+MODULE_DESCRIPTION("Driver module for ZR36060 jpeg processors "
+ ZR060_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/video/zr36060.h b/linux/drivers/media/video/zr36060.h
new file mode 100644
index 000000000..914ffa4ad
--- /dev/null
+++ b/linux/drivers/media/video/zr36060.h
@@ -0,0 +1,220 @@
+/*
+ * Zoran ZR36060 basic configuration functions - header file
+ *
+ * Copyright (C) 2002 Laurent Pinchart <laurent.pinchart@skynet.be>
+ *
+ * $Id: zr36060.h,v 1.1.1.1.2.3 2003/01/14 21:18:47 rbultje Exp $
+ *
+ * ------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+#ifndef ZR36060_H
+#define ZR36060_H
+
+#include "videocodec.h"
+
+/* data stored for each zoran jpeg codec chip */
+struct zr36060 {
+ char name[32];
+ int num;
+ /* io datastructure */
+ struct videocodec *codec;
+ // last coder status
+ __u8 status;
+ // actual coder setup
+ int mode;
+
+ __u16 width;
+ __u16 height;
+
+ __u16 bitrate_ctrl;
+
+ __u32 total_code_vol;
+ __u32 real_code_vol;
+ __u16 max_block_vol;
+
+ __u8 h_samp_ratio[8];
+ __u8 v_samp_ratio[8];
+ __u16 scalefact;
+ __u16 dri;
+
+ /* app/com marker data */
+ struct jpeg_app_marker app;
+ struct jpeg_com_marker com;
+};
+
+/* ZR36060 register addresses */
+#define ZR060_LOAD 0x000
+#define ZR060_CFSR 0x001
+#define ZR060_CIR 0x002
+#define ZR060_CMR 0x003
+#define ZR060_MBZ 0x004
+#define ZR060_MBCVR 0x005
+#define ZR060_MER 0x006
+#define ZR060_IMR 0x007
+#define ZR060_ISR 0x008
+#define ZR060_TCV_NET_HI 0x009
+#define ZR060_TCV_NET_MH 0x00a
+#define ZR060_TCV_NET_ML 0x00b
+#define ZR060_TCV_NET_LO 0x00c
+#define ZR060_TCV_DATA_HI 0x00d
+#define ZR060_TCV_DATA_MH 0x00e
+#define ZR060_TCV_DATA_ML 0x00f
+#define ZR060_TCV_DATA_LO 0x010
+#define ZR060_SF_HI 0x011
+#define ZR060_SF_LO 0x012
+#define ZR060_AF_HI 0x013
+#define ZR060_AF_M 0x014
+#define ZR060_AF_LO 0x015
+#define ZR060_ACV_HI 0x016
+#define ZR060_ACV_MH 0x017
+#define ZR060_ACV_ML 0x018
+#define ZR060_ACV_LO 0x019
+#define ZR060_ACT_HI 0x01a
+#define ZR060_ACT_MH 0x01b
+#define ZR060_ACT_ML 0x01c
+#define ZR060_ACT_LO 0x01d
+#define ZR060_ACV_TRUN_HI 0x01e
+#define ZR060_ACV_TRUN_MH 0x01f
+#define ZR060_ACV_TRUN_ML 0x020
+#define ZR060_ACV_TRUN_LO 0x021
+#define ZR060_IDR_DEV 0x022
+#define ZR060_IDR_REV 0x023
+#define ZR060_TCR_HI 0x024
+#define ZR060_TCR_LO 0x025
+#define ZR060_VCR 0x030
+#define ZR060_VPR 0x031
+#define ZR060_SR 0x032
+#define ZR060_BCR_Y 0x033
+#define ZR060_BCR_U 0x034
+#define ZR060_BCR_V 0x035
+#define ZR060_SGR_VTOTAL_HI 0x036
+#define ZR060_SGR_VTOTAL_LO 0x037
+#define ZR060_SGR_HTOTAL_HI 0x038
+#define ZR060_SGR_HTOTAL_LO 0x039
+#define ZR060_SGR_VSYNC 0x03a
+#define ZR060_SGR_HSYNC 0x03b
+#define ZR060_SGR_BVSTART 0x03c
+#define ZR060_SGR_BHSTART 0x03d
+#define ZR060_SGR_BVEND_HI 0x03e
+#define ZR060_SGR_BVEND_LO 0x03f
+#define ZR060_SGR_BHEND_HI 0x040
+#define ZR060_SGR_BHEND_LO 0x041
+#define ZR060_AAR_VSTART_HI 0x042
+#define ZR060_AAR_VSTART_LO 0x043
+#define ZR060_AAR_VEND_HI 0x044
+#define ZR060_AAR_VEND_LO 0x045
+#define ZR060_AAR_HSTART_HI 0x046
+#define ZR060_AAR_HSTART_LO 0x047
+#define ZR060_AAR_HEND_HI 0x048
+#define ZR060_AAR_HEND_LO 0x049
+#define ZR060_SWR_VSTART_HI 0x04a
+#define ZR060_SWR_VSTART_LO 0x04b
+#define ZR060_SWR_VEND_HI 0x04c
+#define ZR060_SWR_VEND_LO 0x04d
+#define ZR060_SWR_HSTART_HI 0x04e
+#define ZR060_SWR_HSTART_LO 0x04f
+#define ZR060_SWR_HEND_HI 0x050
+#define ZR060_SWR_HEND_LO 0x051
+
+#define ZR060_SOF_IDX 0x060
+#define ZR060_SOS_IDX 0x07a
+#define ZR060_DRI_IDX 0x0c0
+#define ZR060_DQT_IDX 0x0cc
+#define ZR060_DHT_IDX 0x1d4
+#define ZR060_APP_IDX 0x380
+#define ZR060_COM_IDX 0x3c0
+
+/* ZR36060 LOAD register bits */
+
+#define ZR060_LOAD_Load (1 << 7)
+#define ZR060_LOAD_SyncRst (1 << 0)
+
+/* ZR36060 Code FIFO Status register bits */
+
+#define ZR060_CFSR_Busy (1 << 7)
+#define ZR060_CFSR_CBusy (1 << 2)
+#define ZR060_CFSR_CFIFO (3 << 0)
+
+/* ZR36060 Code Interface register */
+
+#define ZR060_CIR_Code16 (1 << 7)
+#define ZR060_CIR_Endian (1 << 6)
+#define ZR060_CIR_CFIS (1 << 2)
+#define ZR060_CIR_CodeMstr (1 << 0)
+
+/* ZR36060 Codec Mode register */
+
+#define ZR060_CMR_Comp (1 << 7)
+#define ZR060_CMR_ATP (1 << 6)
+#define ZR060_CMR_Pass2 (1 << 5)
+#define ZR060_CMR_TLM (1 << 4)
+#define ZR060_CMR_BRB (1 << 2)
+#define ZR060_CMR_FSF (1 << 1)
+
+/* ZR36060 Markers Enable register */
+
+#define ZR060_MER_App (1 << 7)
+#define ZR060_MER_Com (1 << 6)
+#define ZR060_MER_DRI (1 << 5)
+#define ZR060_MER_DQT (1 << 4)
+#define ZR060_MER_DHT (1 << 3)
+
+/* ZR36060 Interrupt Mask register */
+
+#define ZR060_IMR_EOAV (1 << 3)
+#define ZR060_IMR_EOI (1 << 2)
+#define ZR060_IMR_End (1 << 1)
+#define ZR060_IMR_DataErr (1 << 0)
+
+/* ZR36060 Interrupt Status register */
+
+#define ZR060_ISR_ProCnt (3 << 6)
+#define ZR060_ISR_EOAV (1 << 3)
+#define ZR060_ISR_EOI (1 << 2)
+#define ZR060_ISR_End (1 << 1)
+#define ZR060_ISR_DataErr (1 << 0)
+
+/* ZR36060 Video Control register */
+
+#define ZR060_VCR_Video8 (1 << 7)
+#define ZR060_VCR_Range (1 << 6)
+#define ZR060_VCR_FIDet (1 << 3)
+#define ZR060_VCR_FIVedge (1 << 2)
+#define ZR060_VCR_FIExt (1 << 1)
+#define ZR060_VCR_SyncMstr (1 << 0)
+
+/* ZR36060 Video Polarity register */
+
+#define ZR060_VPR_VCLKPol (1 << 7)
+#define ZR060_VPR_PValPol (1 << 6)
+#define ZR060_VPR_PoePol (1 << 5)
+#define ZR060_VPR_SImgPol (1 << 4)
+#define ZR060_VPR_BLPol (1 << 3)
+#define ZR060_VPR_FIPol (1 << 2)
+#define ZR060_VPR_HSPol (1 << 1)
+#define ZR060_VPR_VSPol (1 << 0)
+
+/* ZR36060 Scaling register */
+
+#define ZR060_SR_VScale (1 << 2)
+#define ZR060_SR_HScale2 (1 << 0)
+#define ZR060_SR_HScale4 (2 << 0)
+
+#endif /*fndef ZR36060_H */