diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-04 17:38:08 (GMT) |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-04 17:38:08 (GMT) |
commit | f63b759c44b0561c76a67894c734157df3313b42 (patch) | |
tree | 4e9638f6c1aa5c0faa62ad4213282cc7cb39772a | |
parent | 4a35cee066df1b1958e25e71595b3845d06b192e (diff) | |
parent | 844a9e93d7fcd910cd94f6eb262e2cc43cacbe56 (diff) | |
download | linux-fsl-qoriq-f63b759c44b0561c76a67894c734157df3313b42.tar.xz |
Merge branch 'v4l_for_2.6.35' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6
* 'v4l_for_2.6.35' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6: (243 commits)
V4L/DVB: sms: Convert IR support to use the Remote Controller core
V4L/DVB: sms: properly initialize IR phys and IR name
V4L/DVB: standardize names at rc-dib0700 tables
V4L/DVB: smsusb: enable IR port for Hauppauge WinTV MiniStick
V4L/DVB: dib0700: Fix RC protocol logic to properly handle NEC/NECx and RC-5
V4L/DVB: dib0700: properly implement IR change_protocol
V4L/DVB: dib0700: break keytable into NEC and RC-5 variants
V4L/DVB: dib0700: avoid bad repeat
V4L/DVB: Port dib0700 to rc-core
V4L/DVB: Add a keymap file with dib0700 table
V4L/DVB: dvb-usb: add support for rc-core mode
V4L/DVB: dvb-usb: prepare drivers for using rc-core
V4L/DVB: dvb-usb: get rid of struct dvb_usb_rc_key
V4L/DVB: rj54n1cb0c: fix a comment in the driver
V4L/DVB: V4L2: sh_vou: VOU does support the full PAL resolution too
V4L/DVB: V4L2: sh_mobile_camera_ceu: add support for CSI2
V4L/DVB: V4L2: soc-camera: add a MIPI CSI-2 driver for SH-Mobile platforms
V4L/DVB: V4L2: soc-camera: export soc-camera bus type for notifications
V4L/DVB: V4L2: mediabus: add 12-bit Bayer and YUV420 pixel formats
V4L/DVB: mediabus: fix ambiguous pixel code names
...
320 files changed, 27790 insertions, 31664 deletions
diff --git a/Documentation/DocBook/dvb/dvbapi.xml b/Documentation/DocBook/dvb/dvbapi.xml index 63c528f..e3a97fd 100644 --- a/Documentation/DocBook/dvb/dvbapi.xml +++ b/Documentation/DocBook/dvb/dvbapi.xml @@ -12,10 +12,12 @@ <othername role="mi">O. C.</othername> <affiliation><address><email>rjkm@metzlerbros.de</email></address></affiliation> </author> +</authorgroup> +<authorgroup> <author> <firstname>Mauro</firstname> -<surname>Chehab</surname> <othername role="mi">Carvalho</othername> +<surname>Chehab</surname> <affiliation><address><email>mchehab@redhat.com</email></address></affiliation> <contrib>Ported document to Docbook XML.</contrib> </author> @@ -23,13 +25,24 @@ <copyright> <year>2002</year> <year>2003</year> - <year>2009</year> <holder>Convergence GmbH</holder> </copyright> +<copyright> + <year>2009-2010</year> + <holder>Mauro Carvalho Chehab</holder> +</copyright> <revhistory> <!-- Put document revisions here, newest first. --> <revision> + <revnumber>2.0.3</revnumber> + <date>2010-07-03</date> + <authorinitials>mcc</authorinitials> + <revremark> + Add some frontend capabilities flags, present on kernel, but missing at the specs. + </revremark> +</revision> +<revision> <revnumber>2.0.2</revnumber> <date>2009-10-25</date> <authorinitials>mcc</authorinitials> @@ -63,7 +76,7 @@ Added ISDB-T test originally written by Patrick Boettcher <title>LINUX DVB API</title> -<subtitle>Version 3</subtitle> +<subtitle>Version 5.2</subtitle> <!-- ADD THE CHAPTERS HERE --> <chapter id="dvb_introdution"> &sub-intro; diff --git a/Documentation/DocBook/dvb/frontend.h.xml b/Documentation/DocBook/dvb/frontend.h.xml index b99644f..d08e0d4 100644 --- a/Documentation/DocBook/dvb/frontend.h.xml +++ b/Documentation/DocBook/dvb/frontend.h.xml @@ -63,6 +63,7 @@ typedef enum fe_caps { FE_CAN_8VSB = 0x200000, FE_CAN_16VSB = 0x400000, FE_HAS_EXTENDED_CAPS = 0x800000, /* We need more bitspace for newer APIs, indicate this. */ + FE_CAN_TURBO_FEC = 0x8000000, /* frontend supports "turbo fec modulation" */ FE_CAN_2G_MODULATION = 0x10000000, /* frontend supports "2nd generation modulation" (DVB-S2) */ FE_NEEDS_BENDING = 0x20000000, /* not supported anymore, don't use (frontend requires frequency bending) */ FE_CAN_RECOVER = 0x40000000, /* frontend can recover from a cable unplug automatically */ diff --git a/Documentation/DocBook/dvb/frontend.xml b/Documentation/DocBook/dvb/frontend.xml index 300ba1f..78d756d 100644 --- a/Documentation/DocBook/dvb/frontend.xml +++ b/Documentation/DocBook/dvb/frontend.xml @@ -64,8 +64,14 @@ a specific frontend type.</para> FE_CAN_BANDWIDTH_AUTO = 0x40000, FE_CAN_GUARD_INTERVAL_AUTO = 0x80000, FE_CAN_HIERARCHY_AUTO = 0x100000, - FE_CAN_MUTE_TS = 0x80000000, - FE_CAN_CLEAN_SETUP = 0x40000000 + FE_CAN_8VSB = 0x200000, + FE_CAN_16VSB = 0x400000, + FE_HAS_EXTENDED_CAPS = 0x800000, + FE_CAN_TURBO_FEC = 0x8000000, + FE_CAN_2G_MODULATION = 0x10000000, + FE_NEEDS_BENDING = 0x20000000, + FE_CAN_RECOVER = 0x40000000, + FE_CAN_MUTE_TS = 0x80000000 } fe_caps_t; </programlisting> </section> diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl index 5d4d40f4..6ae9715 100644 --- a/Documentation/DocBook/media-entities.tmpl +++ b/Documentation/DocBook/media-entities.tmpl @@ -218,6 +218,7 @@ <!ENTITY sub-dev-teletext SYSTEM "v4l/dev-teletext.xml"> <!ENTITY sub-driver SYSTEM "v4l/driver.xml"> <!ENTITY sub-libv4l SYSTEM "v4l/libv4l.xml"> +<!ENTITY sub-lirc_device_interface SYSTEM "v4l/lirc_device_interface.xml"> <!ENTITY sub-remote_controllers SYSTEM "v4l/remote_controllers.xml"> <!ENTITY sub-fdl-appendix SYSTEM "v4l/fdl-appendix.xml"> <!ENTITY sub-close SYSTEM "v4l/func-close.xml"> diff --git a/Documentation/DocBook/media.tmpl b/Documentation/DocBook/media.tmpl index eea564b..f11048d 100644 --- a/Documentation/DocBook/media.tmpl +++ b/Documentation/DocBook/media.tmpl @@ -28,7 +28,7 @@ <title>LINUX MEDIA INFRASTRUCTURE API</title> <copyright> - <year>2009</year> + <year>2009-2010</year> <holder>LinuxTV Developers</holder> </copyright> @@ -61,7 +61,7 @@ Foundation. A copy of the license is included in the chapter entitled in fact it covers several different video standards including DVB-T, DVB-S, DVB-C and ATSC. The API is currently being updated to documment support also for DVB-S2, ISDB-T and ISDB-S.</para> - <para>The third part covers other API's used by all media infrastructure devices</para> + <para>The third part covers Remote Controller API</para> <para>For additional information and for the latest development code, see: <ulink url="http://linuxtv.org">http://linuxtv.org</ulink>.</para> <para>For discussing improvements, reporting troubles, sending new drivers, etc, please mail to: <ulink url="http://vger.kernel.org/vger-lists.html#linux-media">Linux Media Mailing List (LMML).</ulink>.</para> @@ -86,7 +86,7 @@ Foundation. A copy of the license is included in the chapter entitled </author> </authorgroup> <copyright> - <year>2009</year> + <year>2009-2010</year> <holder>Mauro Carvalho Chehab</holder> </copyright> @@ -101,7 +101,7 @@ Foundation. A copy of the license is included in the chapter entitled </revhistory> </partinfo> -<title>Other API's used by media infrastructure drivers</title> +<title>Remote Controller API</title> <chapter id="remote_controllers"> &sub-remote_controllers; </chapter> diff --git a/Documentation/DocBook/v4l/lirc_device_interface.xml b/Documentation/DocBook/v4l/lirc_device_interface.xml new file mode 100644 index 0000000..0413234 --- /dev/null +++ b/Documentation/DocBook/v4l/lirc_device_interface.xml @@ -0,0 +1,235 @@ +<section id="lirc_dev"> +<title>LIRC Device Interface</title> + + +<section id="lirc_dev_intro"> +<title>Introduction</title> + +<para>The LIRC device interface is a bi-directional interface for +transporting raw IR data between userspace and kernelspace. Fundamentally, +it is just a chardev (/dev/lircX, for X = 0, 1, 2, ...), with a number +of standard struct file_operations defined on it. With respect to +transporting raw IR data to and fro, the essential fops are read, write +and ioctl.</para> + +<para>Example dmesg output upon a driver registering w/LIRC:</para> + <blockquote> + <para>$ dmesg |grep lirc_dev</para> + <para>lirc_dev: IR Remote Control driver registered, major 248</para> + <para>rc rc0: lirc_dev: driver ir-lirc-codec (mceusb) registered at minor = 0</para> + </blockquote> + +<para>What you should see for a chardev:</para> + <blockquote> + <para>$ ls -l /dev/lirc*</para> + <para>crw-rw---- 1 root root 248, 0 Jul 2 22:20 /dev/lirc0</para> + </blockquote> +</section> + +<section id="lirc_read"> +<title>LIRC read fop</title> + +<para>The lircd userspace daemon reads raw IR data from the LIRC chardev. The +exact format of the data depends on what modes a driver supports, and what +mode has been selected. lircd obtains supported modes and sets the active mode +via the ioctl interface, detailed at <xref linkend="lirc_ioctl"/>. The generally +preferred mode is LIRC_MODE_MODE2, in which packets containing an int value +describing an IR signal are read from the chardev.</para> + +<para>See also <ulink url="http://www.lirc.org/html/technical.html">http://www.lirc.org/html/technical.html</ulink> for more info.</para> +</section> + +<section id="lirc_write"> +<title>LIRC write fop</title> + +<para>The data written to the chardev is a pulse/space sequence of integer +values. Pulses and spaces are only marked implicitly by their position. The +data must start and end with a pulse, therefore, the data must always include +an unevent number of samples. The write function must block until the data has +been transmitted by the hardware.</para> +</section> + +<section id="lirc_ioctl"> +<title>LIRC ioctl fop</title> + +<para>The LIRC device's ioctl definition is bound by the ioctl function +definition of struct file_operations, leaving us with an unsigned int +for the ioctl command and an unsigned long for the arg. For the purposes +of ioctl portability across 32-bit and 64-bit, these values are capped +to their 32-bit sizes.</para> + +<para>The following ioctls can be used to change specific hardware settings. +In general each driver should have a default set of settings. The driver +implementation is expected to re-apply the default settings when the device +is closed by user-space, so that every application opening the device can rely +on working with the default settings initially.</para> + +<variablelist> + <varlistentry> + <term>LIRC_GET_FEATURES</term> + <listitem> + <para>Obviously, get the underlying hardware device's features. If a driver + does not announce support of certain features, calling of the corresponding + ioctls is undefined.</para> + </listitem> + </varlistentry> + <varlistentry> + <term>LIRC_GET_SEND_MODE</term> + <listitem> + <para>Get supported transmit mode. Only LIRC_MODE_PULSE is supported by lircd.</para> + </listitem> + </varlistentry> + <varlistentry> + <term>LIRC_GET_REC_MODE</term> + <listitem> + <para>Get supported receive modes. Only LIRC_MODE_MODE2 and LIRC_MODE_LIRCCODE + are supported by lircd.</para> + </listitem> + </varlistentry> + <varlistentry> + <term>LIRC_GET_SEND_CARRIER</term> + <listitem> + <para>Get carrier frequency (in Hz) currently used for transmit.</para> + </listitem> + </varlistentry> + <varlistentry> + <term>LIRC_GET_REC_CARRIER</term> + <listitem> + <para>Get carrier frequency (in Hz) currently used for IR reception.</para> + </listitem> + </varlistentry> + <varlistentry> + <term>LIRC_{G,S}ET_{SEND,REC}_DUTY_CYCLE</term> + <listitem> + <para>Get/set the duty cycle (from 0 to 100) of the carrier signal. Currently, + no special meaning is defined for 0 or 100, but this could be used to switch + off carrier generation in the future, so these values should be reserved.</para> + </listitem> + </varlistentry> + <varlistentry> + <term>LIRC_GET_REC_RESOLUTION</term> + <listitem> + <para>Some receiver have maximum resolution which is defined by internal + sample rate or data format limitations. E.g. it's common that signals can + only be reported in 50 microsecond steps. This integer value is used by + lircd to automatically adjust the aeps tolerance value in the lircd + config file.</para> + </listitem> + </varlistentry> + <varlistentry> + <term>LIRC_GET_M{IN,AX}_TIMEOUT</term> + <listitem> + <para>Some devices have internal timers that can be used to detect when + there's no IR activity for a long time. This can help lircd in detecting + that a IR signal is finished and can speed up the decoding process. + Returns an integer value with the minimum/maximum timeout that can be + set. Some devices have a fixed timeout, in that case both ioctls will + return the same value even though the timeout cannot be changed.</para> + </listitem> + </varlistentry> + <varlistentry> + <term>LIRC_GET_M{IN,AX}_FILTER_{PULSE,SPACE}</term> + <listitem> + <para>Some devices are able to filter out spikes in the incoming signal + using given filter rules. These ioctls return the hardware capabilities + that describe the bounds of the possible filters. Filter settings depend + on the IR protocols that are expected. lircd derives the settings from + all protocols definitions found in its config file.</para> + </listitem> + </varlistentry> + <varlistentry> + <term>LIRC_GET_LENGTH</term> + <listitem> + <para>Retrieves the code length in bits (only for LIRC_MODE_LIRCCODE). + Reads on the device must be done in blocks matching the bit count. + The bit could should be rounded up so that it matches full bytes.</para> + </listitem> + </varlistentry> + <varlistentry> + <term>LIRC_SET_{SEND,REC}_MODE</term> + <listitem> + <para>Set send/receive mode. Largely obsolete for send, as only + LIRC_MODE_PULSE is supported.</para> + </listitem> + </varlistentry> + <varlistentry> + <term>LIRC_SET_{SEND,REC}_CARRIER</term> + <listitem> + <para>Set send/receive carrier (in Hz).</para> + </listitem> + </varlistentry> + <varlistentry> + <term>LIRC_SET_TRANSMITTER_MASK</term> + <listitem> + <para>This enables the given set of transmitters. The first transmitter + is encoded by the least significant bit, etc. When an invalid bit mask + is given, i.e. a bit is set, even though the device does not have so many + transitters, then this ioctl returns the number of available transitters + and does nothing otherwise.</para> + </listitem> + </varlistentry> + <varlistentry> + <term>LIRC_SET_REC_TIMEOUT</term> + <listitem> + <para>Sets the integer value for IR inactivity timeout (cf. + LIRC_GET_MIN_TIMEOUT and LIRC_GET_MAX_TIMEOUT). A value of 0 (if + supported by the hardware) disables all hardware timeouts and data should + be reported as soon as possible. If the exact value cannot be set, then + the next possible value _greater_ than the given value should be set.</para> + </listitem> + </varlistentry> + <varlistentry> + <term>LIRC_SET_REC_TIMEOUT_REPORTS</term> + <listitem> + <para>Enable (1) or disable (0) timeout reports in LIRC_MODE_MODE2. By + default, timeout reports should be turned off.</para> + </listitem> + </varlistentry> + <varlistentry> + <term>LIRC_SET_REC_FILTER_{,PULSE,SPACE}</term> + <listitem> + <para>Pulses/spaces shorter than this are filtered out by hardware. If + filters cannot be set independently for pulse/space, the corresponding + ioctls must return an error and LIRC_SET_REC_FILTER shall be used instead.</para> + </listitem> + </varlistentry> + <varlistentry> + <term>LIRC_SET_MEASURE_CARRIER_MODE</term> + <listitem> + <para>Enable (1)/disable (0) measure mode. If enabled, from the next key + press on, the driver will send LIRC_MODE2_FREQUENCY packets. By default + this should be turned off.</para> + </listitem> + </varlistentry> + <varlistentry> + <term>LIRC_SET_REC_{DUTY_CYCLE,CARRIER}_RANGE</term> + <listitem> + <para>To set a range use LIRC_SET_REC_DUTY_CYCLE_RANGE/LIRC_SET_REC_CARRIER_RANGE + with the lower bound first and later LIRC_SET_REC_DUTY_CYCLE/LIRC_SET_REC_CARRIER + with the upper bound.</para> + </listitem> + </varlistentry> + <varlistentry> + <term>LIRC_NOTIFY_DECODE</term> + <listitem> + <para>This ioctl is called by lircd whenever a successful decoding of an + incoming IR signal could be done. This can be used by supporting hardware + to give visual feedback to the user e.g. by flashing a LED.</para> + </listitem> + </varlistentry> + <varlistentry> + <term>LIRC_SETUP_{START,END}</term> + <listitem> + <para>Setting of several driver parameters can be optimized by encapsulating + the according ioctl calls with LIRC_SETUP_START/LIRC_SETUP_END. When a + driver receives a LIRC_SETUP_START ioctl it can choose to not commit + further setting changes to the hardware until a LIRC_SETUP_END is received. + But this is open to the driver implementation and every driver must also + handle parameter changes which are not encapsulated by LIRC_SETUP_START + and LIRC_SETUP_END. Drivers can also choose to ignore these ioctls.</para> + </listitem> + </varlistentry> +</variablelist> + +</section> +</section> diff --git a/Documentation/DocBook/v4l/remote_controllers.xml b/Documentation/DocBook/v4l/remote_controllers.xml index 73f5eab..3c3b667 100644 --- a/Documentation/DocBook/v4l/remote_controllers.xml +++ b/Documentation/DocBook/v4l/remote_controllers.xml @@ -173,3 +173,5 @@ keymapping.</para> <para>This program demonstrates how to replace the keymap tables.</para> &sub-keytable-c; </section> + +&sub-lirc_device_interface; diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware index 239cbdb..350959f 100644 --- a/Documentation/dvb/get_dvb_firmware +++ b/Documentation/dvb/get_dvb_firmware @@ -26,7 +26,7 @@ use IO::Handle; "dec3000s", "vp7041", "dibusb", "nxt2002", "nxt2004", "or51211", "or51132_qam", "or51132_vsb", "bluebird", "opera1", "cx231xx", "cx18", "cx23885", "pvrusb2", "mpc718", - "af9015", "ngene"); + "af9015", "ngene", "az6027"); # Check args syntax() if (scalar(@ARGV) != 1); @@ -518,11 +518,11 @@ sub bluebird { sub af9015 { my $sourcefile = "download.ashx?file=57"; my $url = "http://www.ite.com.tw/EN/Services/$sourcefile"; - my $hash = "ff5b096ed47c080870eacdab2de33ad6"; + my $hash = "e3f08935158038d385ad382442f4bb2d"; my $outfile = "dvb-usb-af9015.fw"; my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 1); - my $fwoffset = 0x22708; - my $fwlength = 18225; + my $fwoffset = 0x25690; + my $fwlength = 18725; my ($chunklength, $buf, $rcount); checkstandard(); @@ -567,6 +567,23 @@ sub ngene { "$file1, $file2"; } +sub az6027{ + my $file = "AZ6027_Linux_Driver.tar.gz"; + my $url = "http://linux.terratec.de/files/$file"; + my $firmware = "dvb-usb-az6027-03.fw"; + + wgetfile($file, $url); + + #untar + if( system("tar xzvf $file $firmware")){ + die "failed to untar firmware"; + } + if( system("rm $file")){ + die ("unable to remove unnecessary files"); + } + + $firmware; +} # --------------------------------------------------------------- # Utilities diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 1571c0c..79cb554 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -459,57 +459,6 @@ Who: Corentin Chary <corentin.chary@gmail.com> ---------------------------- -What: usbvideo quickcam_messenger driver -When: 2.6.35 -Files: drivers/media/video/usbvideo/quickcam_messenger.[ch] -Why: obsolete v4l1 driver replaced by gspca_stv06xx -Who: Hans de Goede <hdegoede@redhat.com> - ----------------------------- - -What: ov511 v4l1 driver -When: 2.6.35 -Files: drivers/media/video/ov511.[ch] -Why: obsolete v4l1 driver replaced by gspca_ov519 -Who: Hans de Goede <hdegoede@redhat.com> - ----------------------------- - -What: w9968cf v4l1 driver -When: 2.6.35 -Files: drivers/media/video/w9968cf*.[ch] -Why: obsolete v4l1 driver replaced by gspca_ov519 -Who: Hans de Goede <hdegoede@redhat.com> - ----------------------------- - -What: ovcamchip sensor framework -When: 2.6.35 -Files: drivers/media/video/ovcamchip/* -Why: Only used by obsoleted v4l1 drivers -Who: Hans de Goede <hdegoede@redhat.com> - ----------------------------- - -What: stv680 v4l1 driver -When: 2.6.35 -Files: drivers/media/video/stv680.[ch] -Why: obsolete v4l1 driver replaced by gspca_stv0680 -Who: Hans de Goede <hdegoede@redhat.com> - ----------------------------- - -What: zc0301 v4l driver -When: 2.6.35 -Files: drivers/media/video/zc0301/* -Why: Duplicate functionality with the gspca_zc3xx driver, zc0301 only - supports 2 USB-ID's (because it only supports a limited set of - sensors) wich are also supported by the gspca_zc3xx driver - (which supports 53 USB-ID's in total) -Who: Hans de Goede <hdegoede@redhat.com> - ----------------------------- - What: sysfs-class-rfkill state file When: Feb 2014 Files: net/rfkill/core.c diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885 index 16ca030..87c4634 100644 --- a/Documentation/video4linux/CARDLIST.cx23885 +++ b/Documentation/video4linux/CARDLIST.cx23885 @@ -17,9 +17,9 @@ 16 -> DVBWorld DVB-S2 2005 [0001:2005] 17 -> NetUP Dual DVB-S2 CI [1b55:2a2c] 18 -> Hauppauge WinTV-HVR1270 [0070:2211] - 19 -> Hauppauge WinTV-HVR1275 [0070:2215] - 20 -> Hauppauge WinTV-HVR1255 [0070:2251] - 21 -> Hauppauge WinTV-HVR1210 [0070:2291,0070:2295] + 19 -> Hauppauge WinTV-HVR1275 [0070:2215,0070:221d,0070:22f2] + 20 -> Hauppauge WinTV-HVR1255 [0070:2251,0070:2259,0070:22f1] + 21 -> Hauppauge WinTV-HVR1210 [0070:2291,0070:2295,0070:2299,0070:229d,0070:22f0,0070:22f3,0070:22f4,0070:22f5] 22 -> Mygica X8506 DMB-TH [14f1:8651] 23 -> Magic-Pro ProHDTV Extreme 2 [14f1:8657] 24 -> Hauppauge WinTV-HVR1850 [0070:8541] diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx index 3a623aa..5c56875 100644 --- a/Documentation/video4linux/CARDLIST.em28xx +++ b/Documentation/video4linux/CARDLIST.em28xx @@ -72,3 +72,4 @@ 73 -> Reddo DVB-C USB TV Box (em2870) 74 -> Actionmaster/LinXcel/Digitus VC211A (em2800) 75 -> Dikom DK300 (em2882) + 76 -> KWorld PlusTV 340U or UB435-Q (ATSC) (em2870) [1b80:a340] diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index 1387a69..4000c29 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -178,4 +178,5 @@ 177 -> Hawell HW-404M7 178 -> Beholder BeholdTV H7 [5ace:7190] 179 -> Beholder BeholdTV A7 [5ace:7090] -180 -> Avermedia M733A [1461:4155,1461:4255] +180 -> Avermedia PCI M733A [1461:4155,1461:4255] +181 -> TechoTrend TT-budget T-3000 [13c2:2804] diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index f13eb03..56ba7bb 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -29,8 +29,12 @@ zc3xx 041e:4029 Creative WebCam Vista Pro zc3xx 041e:4034 Creative Instant P0620 zc3xx 041e:4035 Creative Instant P0620D zc3xx 041e:4036 Creative Live ! +sq930x 041e:4038 Creative Joy-IT zc3xx 041e:403a Creative Nx Pro 2 spca561 041e:403b Creative Webcam Vista (VF0010) +sq930x 041e:403c Creative Live! Ultra +sq930x 041e:403d Creative Live! Ultra for Notebooks +sq930x 041e:4041 Creative Live! Motion zc3xx 041e:4051 Creative Live!Cam Notebook Pro (VF0250) ov519 041e:4052 Creative Live! VISTA IM zc3xx 041e:4053 Creative Live!Cam Video IM @@ -138,6 +142,7 @@ finepix 04cb:013d Fujifilm FinePix unknown model finepix 04cb:013f Fujifilm FinePix F420 sunplus 04f1:1001 JVC GC A50 spca561 04fc:0561 Flexcam 100 +spca1528 04fc:1528 Sunplus MD80 clone sunplus 04fc:500c Sunplus CA500C sunplus 04fc:504a Aiptek Mini PenCam 1.3 sunplus 04fc:504b Maxell MaxPocket LE 1.3 @@ -253,6 +258,7 @@ pac7302 093a:2620 Apollo AC-905 pac7302 093a:2621 PAC731x pac7302 093a:2622 Genius Eye 312 pac7302 093a:2624 PAC7302 +pac7302 093a:2625 Genius iSlim 310 pac7302 093a:2626 Labtec 2200 pac7302 093a:2628 Genius iLook 300 pac7302 093a:2629 Genious iSlim 300 @@ -362,6 +368,8 @@ sq905c 2770:9052 Disney pix micro 2 (VGA) sq905c 2770:905c All 11 known cameras with this ID sq905 2770:9120 All 24 known cameras with this ID sq905c 2770:913d All 4 known cameras with this ID +sq930x 2770:930b Sweex Motion Tracking / I-Tec iCam Tracer +sq930x 2770:930c Trust WB-3500T / NSG Robbie 2.0 spca500 2899:012c Toptro Industrial ov519 8020:ef04 ov519 spca508 8086:0110 Intel Easy PC Camera diff --git a/arch/sh/boards/mach-ap325rxa/setup.c b/arch/sh/boards/mach-ap325rxa/setup.c index 3a170bd..de375b6 100644 --- a/arch/sh/boards/mach-ap325rxa/setup.c +++ b/arch/sh/boards/mach-ap325rxa/setup.c @@ -316,7 +316,7 @@ static struct soc_camera_platform_info camera_info = { .format_name = "UYVY", .format_depth = 16, .format = { - .code = V4L2_MBUS_FMT_YUYV8_2X8_BE, + .code = V4L2_MBUS_FMT_UYVY8_2X8, .colorspace = V4L2_COLORSPACE_SMPTE170M, .field = V4L2_FIELD_NONE, .width = 640, diff --git a/drivers/media/IR/Kconfig b/drivers/media/IR/Kconfig index d22a8ec..999a825 100644 --- a/drivers/media/IR/Kconfig +++ b/drivers/media/IR/Kconfig @@ -8,6 +8,17 @@ config VIDEO_IR depends on IR_CORE default IR_CORE +config LIRC + tristate + default y + + ---help--- + Enable this option to build the Linux Infrared Remote + Control (LIRC) core device interface driver. The LIRC + interface passes raw IR to and from userspace, where the + LIRC daemon handles protocol decoding for IR reception ann + encoding for IR transmitting (aka "blasting"). + source "drivers/media/IR/keymaps/Kconfig" config IR_NEC_DECODER @@ -33,6 +44,7 @@ config IR_RC5_DECODER config IR_RC6_DECODER tristate "Enable IR raw decoder for the RC6 protocol" depends on IR_CORE + select BITREVERSE default y ---help--- @@ -42,6 +54,7 @@ config IR_RC6_DECODER config IR_JVC_DECODER tristate "Enable IR raw decoder for the JVC protocol" depends on IR_CORE + select BITREVERSE default y ---help--- @@ -57,6 +70,16 @@ config IR_SONY_DECODER Enable this option if you have an infrared remote control which uses the Sony protocol, and you need software decoding support. +config IR_LIRC_CODEC + tristate "Enable IR to LIRC bridge" + depends on IR_CORE + depends on LIRC + default y + + ---help--- + Enable this option to pass raw IR to and from userspace via + the LIRC interface. + config IR_IMON tristate "SoundGraph iMON Receiver and Display" depends on USB_ARCH_HAS_HCD @@ -68,3 +91,15 @@ config IR_IMON To compile this driver as a module, choose M here: the module will be called imon. + +config IR_MCEUSB + tristate "Windows Media Center Ed. eHome Infrared Transceiver" + depends on USB_ARCH_HAS_HCD + depends on IR_CORE + select USB + ---help--- + Say Y here if you want to use a Windows Media Center Edition + eHome Infrared Transceiver. + + To compile this driver as a module, choose M here: the + module will be called mceusb. diff --git a/drivers/media/IR/Makefile b/drivers/media/IR/Makefile index b998fcc..2ae4f3a 100644 --- a/drivers/media/IR/Makefile +++ b/drivers/media/IR/Makefile @@ -5,11 +5,14 @@ obj-y += keymaps/ obj-$(CONFIG_IR_CORE) += ir-core.o obj-$(CONFIG_VIDEO_IR) += ir-common.o +obj-$(CONFIG_LIRC) += lirc_dev.o obj-$(CONFIG_IR_NEC_DECODER) += ir-nec-decoder.o obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o obj-$(CONFIG_IR_SONY_DECODER) += ir-sony-decoder.o +obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o # stand-alone IR receivers/transmitters obj-$(CONFIG_IR_IMON) += imon.o +obj-$(CONFIG_IR_MCEUSB) += mceusb.o diff --git a/drivers/media/IR/imon.c b/drivers/media/IR/imon.c index 4bbd45f..65c125e 100644 --- a/drivers/media/IR/imon.c +++ b/drivers/media/IR/imon.c @@ -407,7 +407,7 @@ static int display_close(struct inode *inode, struct file *file) struct imon_context *ictx = NULL; int retval = 0; - ictx = (struct imon_context *)file->private_data; + ictx = file->private_data; if (!ictx) { err("%s: no context for device", __func__); @@ -812,7 +812,7 @@ static ssize_t vfd_write(struct file *file, const char *buf, const unsigned char vfd_packet6[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }; - ictx = (struct imon_context *)file->private_data; + ictx = file->private_data; if (!ictx) { err("%s: no context for device", __func__); return -ENODEV; @@ -896,7 +896,7 @@ static ssize_t lcd_write(struct file *file, const char *buf, int retval = 0; struct imon_context *ictx; - ictx = (struct imon_context *)file->private_data; + ictx = file->private_data; if (!ictx) { err("%s: no context for device", __func__); return -ENODEV; @@ -1943,7 +1943,7 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf) return ictx; urb_submit_failed: - input_unregister_device(ictx->idev); + ir_input_unregister(ictx->idev); input_free_device(ictx->idev); idev_setup_failed: find_endpoint_failed: @@ -2067,6 +2067,7 @@ static void imon_get_ffdc_type(struct imon_context *ictx) detected_display_type = IMON_DISPLAY_TYPE_VFD; break; /* iMON LCD, MCE IR */ + case 0x9e: case 0x9f: dev_info(ictx->dev, "0xffdc iMON LCD, MCE IR"); detected_display_type = IMON_DISPLAY_TYPE_LCD; @@ -2306,7 +2307,7 @@ static void __devexit imon_disconnect(struct usb_interface *interface) if (ifnum == 0) { ictx->dev_present_intf0 = false; usb_kill_urb(ictx->rx_urb_intf0); - input_unregister_device(ictx->idev); + ir_input_unregister(ictx->idev); if (ictx->display_supported) { if (ictx->display_type == IMON_DISPLAY_TYPE_LCD) usb_deregister_dev(interface, &imon_lcd_class); diff --git a/drivers/media/IR/ir-core-priv.h b/drivers/media/IR/ir-core-priv.h index 9a5e65a..babd520 100644 --- a/drivers/media/IR/ir-core-priv.h +++ b/drivers/media/IR/ir-core-priv.h @@ -22,17 +22,62 @@ struct ir_raw_handler { struct list_head list; + u64 protocols; /* which are handled by this handler */ int (*decode)(struct input_dev *input_dev, struct ir_raw_event event); + + /* These two should only be used by the lirc decoder */ int (*raw_register)(struct input_dev *input_dev); int (*raw_unregister)(struct input_dev *input_dev); }; struct ir_raw_event_ctrl { + struct list_head list; /* to keep track of raw clients */ struct work_struct rx_work; /* for the rx decoding workqueue */ struct kfifo kfifo; /* fifo for the pulse/space durations */ ktime_t last_event; /* when last event occurred */ enum raw_event_type last_type; /* last event type */ struct input_dev *input_dev; /* pointer to the parent input_dev */ + u64 enabled_protocols; /* enabled raw protocol decoders */ + + /* raw decoder state follows */ + struct ir_raw_event prev_ev; + struct nec_dec { + int state; + unsigned count; + u32 bits; + } nec; + struct rc5_dec { + int state; + u32 bits; + unsigned count; + unsigned wanted_bits; + } rc5; + struct rc6_dec { + int state; + u8 header; + u32 body; + bool toggle; + unsigned count; + unsigned wanted_bits; + } rc6; + struct sony_dec { + int state; + u32 bits; + unsigned count; + } sony; + struct jvc_dec { + int state; + u16 bits; + u16 old_bits; + unsigned count; + bool first; + bool toggle; + } jvc; + struct lirc_codec { + struct ir_input_dev *ir_dev; + struct lirc_driver *drv; + int lircdata; + } lirc; }; /* macros for IR decoders */ @@ -74,6 +119,7 @@ void ir_unregister_class(struct input_dev *input_dev); /* * Routines from ir-raw-event.c to be used internally and by decoders */ +u64 ir_raw_get_allowed_protocols(void); int ir_raw_event_register(struct input_dev *input_dev); void ir_raw_event_unregister(struct input_dev *input_dev); int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler); @@ -123,4 +169,12 @@ void ir_raw_init(void); #define load_sony_decode() 0 #endif +/* from ir-lirc-codec.c */ +#ifdef CONFIG_IR_LIRC_CODEC_MODULE +#define load_lirc_codec() request_module("ir-lirc-codec") +#else +#define load_lirc_codec() 0 +#endif + + #endif /* _IR_RAW_EVENT */ diff --git a/drivers/media/IR/ir-jvc-decoder.c b/drivers/media/IR/ir-jvc-decoder.c index 0b80494..8894d8b 100644 --- a/drivers/media/IR/ir-jvc-decoder.c +++ b/drivers/media/IR/ir-jvc-decoder.c @@ -25,10 +25,6 @@ #define JVC_TRAILER_PULSE (1 * JVC_UNIT) #define JVC_TRAILER_SPACE (35 * JVC_UNIT) -/* Used to register jvc_decoder clients */ -static LIST_HEAD(decoder_list); -DEFINE_SPINLOCK(decoder_lock); - enum jvc_state { STATE_INACTIVE, STATE_HEADER_SPACE, @@ -38,87 +34,6 @@ enum jvc_state { STATE_TRAILER_SPACE, }; -struct decoder_data { - struct list_head list; - struct ir_input_dev *ir_dev; - int enabled:1; - - /* State machine control */ - enum jvc_state state; - u16 jvc_bits; - u16 jvc_old_bits; - unsigned count; - bool first; - bool toggle; -}; - - -/** - * get_decoder_data() - gets decoder data - * @input_dev: input device - * - * Returns the struct decoder_data that corresponds to a device - */ -static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev) -{ - struct decoder_data *data = NULL; - - spin_lock(&decoder_lock); - list_for_each_entry(data, &decoder_list, list) { - if (data->ir_dev == ir_dev) - break; - } - spin_unlock(&decoder_lock); - return data; -} - -static ssize_t store_enabled(struct device *d, - struct device_attribute *mattr, - const char *buf, - size_t len) -{ - unsigned long value; - struct ir_input_dev *ir_dev = dev_get_drvdata(d); - struct decoder_data *data = get_decoder_data(ir_dev); - - if (!data) - return -EINVAL; - - if (strict_strtoul(buf, 10, &value) || value > 1) - return -EINVAL; - - data->enabled = value; - - return len; -} - -static ssize_t show_enabled(struct device *d, - struct device_attribute *mattr, char *buf) -{ - struct ir_input_dev *ir_dev = dev_get_drvdata(d); - struct decoder_data *data = get_decoder_data(ir_dev); - - if (!data) - return -EINVAL; - - if (data->enabled) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - -static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled); - -static struct attribute *decoder_attributes[] = { - &dev_attr_enabled.attr, - NULL -}; - -static struct attribute_group decoder_attribute_group = { - .name = "jvc_decoder", - .attrs = decoder_attributes, -}; - /** * ir_jvc_decode() - Decode one JVC pulse or space * @input_dev: the struct input_dev descriptor of the device @@ -128,14 +43,10 @@ static struct attribute_group decoder_attribute_group = { */ static int ir_jvc_decode(struct input_dev *input_dev, struct ir_raw_event ev) { - struct decoder_data *data; struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + struct jvc_dec *data = &ir_dev->raw->jvc; - data = get_decoder_data(ir_dev); - if (!data) - return -EINVAL; - - if (!data->enabled) + if (!(ir_dev->raw->enabled_protocols & IR_TYPE_JVC)) return 0; if (IS_RESET(ev)) { @@ -188,9 +99,9 @@ static int ir_jvc_decode(struct input_dev *input_dev, struct ir_raw_event ev) if (ev.pulse) break; - data->jvc_bits <<= 1; + data->bits <<= 1; if (eq_margin(ev.duration, JVC_BIT_1_SPACE, JVC_UNIT / 2)) { - data->jvc_bits |= 1; + data->bits |= 1; decrease_duration(&ev, JVC_BIT_1_SPACE); } else if (eq_margin(ev.duration, JVC_BIT_0_SPACE, JVC_UNIT / 2)) decrease_duration(&ev, JVC_BIT_0_SPACE); @@ -223,13 +134,13 @@ static int ir_jvc_decode(struct input_dev *input_dev, struct ir_raw_event ev) if (data->first) { u32 scancode; - scancode = (bitrev8((data->jvc_bits >> 8) & 0xff) << 8) | - (bitrev8((data->jvc_bits >> 0) & 0xff) << 0); + scancode = (bitrev8((data->bits >> 8) & 0xff) << 8) | + (bitrev8((data->bits >> 0) & 0xff) << 0); IR_dprintk(1, "JVC scancode 0x%04x\n", scancode); ir_keydown(input_dev, scancode, data->toggle); data->first = false; - data->jvc_old_bits = data->jvc_bits; - } else if (data->jvc_bits == data->jvc_old_bits) { + data->old_bits = data->bits; + } else if (data->bits == data->old_bits) { IR_dprintk(1, "JVC repeat\n"); ir_repeat(input_dev); } else { @@ -249,54 +160,9 @@ out: return -EINVAL; } -static int ir_jvc_register(struct input_dev *input_dev) -{ - struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); - struct decoder_data *data; - int rc; - - rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group); - if (rc < 0) - return rc; - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { - sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); - return -ENOMEM; - } - - data->ir_dev = ir_dev; - data->enabled = 1; - - spin_lock(&decoder_lock); - list_add_tail(&data->list, &decoder_list); - spin_unlock(&decoder_lock); - - return 0; -} - -static int ir_jvc_unregister(struct input_dev *input_dev) -{ - struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); - static struct decoder_data *data; - - data = get_decoder_data(ir_dev); - if (!data) - return 0; - - sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); - - spin_lock(&decoder_lock); - list_del(&data->list); - spin_unlock(&decoder_lock); - - return 0; -} - static struct ir_raw_handler jvc_handler = { + .protocols = IR_TYPE_JVC, .decode = ir_jvc_decode, - .raw_register = ir_jvc_register, - .raw_unregister = ir_jvc_unregister, }; static int __init ir_jvc_decode_init(void) diff --git a/drivers/media/IR/ir-keytable.c b/drivers/media/IR/ir-keytable.c index 94a8577..15a0f19 100644 --- a/drivers/media/IR/ir-keytable.c +++ b/drivers/media/IR/ir-keytable.c @@ -497,8 +497,9 @@ int __ir_input_register(struct input_dev *input_dev, goto out_event; } - IR_dprintk(1, "Registered input device on %s for %s remote.\n", - driver_name, rc_tab->name); + IR_dprintk(1, "Registered input device on %s for %s remote%s.\n", + driver_name, rc_tab->name, + ir_dev->props->driver_type == RC_DRIVER_IR_RAW ? " in raw mode" : ""); return 0; diff --git a/drivers/media/IR/ir-lirc-codec.c b/drivers/media/IR/ir-lirc-codec.c new file mode 100644 index 0000000..3ba482d --- /dev/null +++ b/drivers/media/IR/ir-lirc-codec.c @@ -0,0 +1,278 @@ +/* ir-lirc-codec.c - ir-core to classic lirc interface bridge + * + * Copyright (C) 2010 by Jarod Wilson <jarod@redhat.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 version 2 of the License. + * + * 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. + */ + +#include <linux/sched.h> +#include <linux/wait.h> +#include <media/lirc.h> +#include <media/lirc_dev.h> +#include <media/ir-core.h> +#include "ir-core-priv.h" + +#define LIRCBUF_SIZE 256 + +/** + * ir_lirc_decode() - Send raw IR data to lirc_dev to be relayed to the + * lircd userspace daemon for decoding. + * @input_dev: the struct input_dev descriptor of the device + * @duration: the struct ir_raw_event descriptor of the pulse/space + * + * This function returns -EINVAL if the lirc interfaces aren't wired up. + */ +static int ir_lirc_decode(struct input_dev *input_dev, struct ir_raw_event ev) +{ + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + + if (!(ir_dev->raw->enabled_protocols & IR_TYPE_LIRC)) + return 0; + + if (!ir_dev->raw->lirc.drv || !ir_dev->raw->lirc.drv->rbuf) + return -EINVAL; + + IR_dprintk(2, "LIRC data transfer started (%uus %s)\n", + TO_US(ev.duration), TO_STR(ev.pulse)); + + ir_dev->raw->lirc.lircdata += ev.duration / 1000; + if (ev.pulse) + ir_dev->raw->lirc.lircdata |= PULSE_BIT; + + lirc_buffer_write(ir_dev->raw->lirc.drv->rbuf, + (unsigned char *) &ir_dev->raw->lirc.lircdata); + wake_up(&ir_dev->raw->lirc.drv->rbuf->wait_poll); + + ir_dev->raw->lirc.lircdata = 0; + + return 0; +} + +static ssize_t ir_lirc_transmit_ir(struct file *file, const char *buf, + size_t n, loff_t *ppos) +{ + struct lirc_codec *lirc; + struct ir_input_dev *ir_dev; + int *txbuf; /* buffer with values to transmit */ + int ret = 0, count; + + lirc = lirc_get_pdata(file); + if (!lirc) + return -EFAULT; + + if (n % sizeof(int)) + return -EINVAL; + + count = n / sizeof(int); + if (count > LIRCBUF_SIZE || count % 2 == 0) + return -EINVAL; + + txbuf = memdup_user(buf, n); + if (IS_ERR(txbuf)) + return PTR_ERR(txbuf); + + ir_dev = lirc->ir_dev; + if (!ir_dev) { + ret = -EFAULT; + goto out; + } + + if (ir_dev->props && ir_dev->props->tx_ir) + ret = ir_dev->props->tx_ir(ir_dev->props->priv, txbuf, (u32)n); + +out: + kfree(txbuf); + return ret; +} + +static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + struct lirc_codec *lirc; + struct ir_input_dev *ir_dev; + int ret = 0; + void *drv_data; + unsigned long val; + + lirc = lirc_get_pdata(filep); + if (!lirc) + return -EFAULT; + + ir_dev = lirc->ir_dev; + if (!ir_dev || !ir_dev->props || !ir_dev->props->priv) + return -EFAULT; + + drv_data = ir_dev->props->priv; + + switch (cmd) { + case LIRC_SET_TRANSMITTER_MASK: + ret = get_user(val, (unsigned long *)arg); + if (ret) + return ret; + + if (ir_dev->props && ir_dev->props->s_tx_mask) + ret = ir_dev->props->s_tx_mask(drv_data, (u32)val); + else + return -EINVAL; + break; + + case LIRC_SET_SEND_CARRIER: + ret = get_user(val, (unsigned long *)arg); + if (ret) + return ret; + + if (ir_dev->props && ir_dev->props->s_tx_carrier) + ir_dev->props->s_tx_carrier(drv_data, (u32)val); + else + return -EINVAL; + break; + + case LIRC_GET_SEND_MODE: + val = LIRC_CAN_SEND_PULSE & LIRC_CAN_SEND_MASK; + ret = put_user(val, (unsigned long *)arg); + break; + + case LIRC_SET_SEND_MODE: + ret = get_user(val, (unsigned long *)arg); + if (ret) + return ret; + + if (val != (LIRC_MODE_PULSE & LIRC_CAN_SEND_MASK)) + return -EINVAL; + break; + + default: + return lirc_dev_fop_ioctl(filep, cmd, arg); + } + + return ret; +} + +static int ir_lirc_open(void *data) +{ + return 0; +} + +static void ir_lirc_close(void *data) +{ + return; +} + +static struct file_operations lirc_fops = { + .owner = THIS_MODULE, + .write = ir_lirc_transmit_ir, + .unlocked_ioctl = ir_lirc_ioctl, + .read = lirc_dev_fop_read, + .poll = lirc_dev_fop_poll, + .open = lirc_dev_fop_open, + .release = lirc_dev_fop_close, +}; + +static int ir_lirc_register(struct input_dev *input_dev) +{ + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + struct lirc_driver *drv; + struct lirc_buffer *rbuf; + int rc = -ENOMEM; + unsigned long features; + + drv = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); + if (!drv) + return rc; + + rbuf = kzalloc(sizeof(struct lirc_buffer), GFP_KERNEL); + if (!rbuf) + goto rbuf_alloc_failed; + + rc = lirc_buffer_init(rbuf, sizeof(int), LIRCBUF_SIZE); + if (rc) + goto rbuf_init_failed; + + features = LIRC_CAN_REC_MODE2; + if (ir_dev->props->tx_ir) { + features |= LIRC_CAN_SEND_PULSE; + if (ir_dev->props->s_tx_mask) + features |= LIRC_CAN_SET_TRANSMITTER_MASK; + if (ir_dev->props->s_tx_carrier) + features |= LIRC_CAN_SET_SEND_CARRIER; + } + + snprintf(drv->name, sizeof(drv->name), "ir-lirc-codec (%s)", + ir_dev->driver_name); + drv->minor = -1; + drv->features = features; + drv->data = &ir_dev->raw->lirc; + drv->rbuf = rbuf; + drv->set_use_inc = &ir_lirc_open; + drv->set_use_dec = &ir_lirc_close; + drv->code_length = sizeof(struct ir_raw_event) * 8; + drv->fops = &lirc_fops; + drv->dev = &ir_dev->dev; + drv->owner = THIS_MODULE; + + drv->minor = lirc_register_driver(drv); + if (drv->minor < 0) { + rc = -ENODEV; + goto lirc_register_failed; + } + + ir_dev->raw->lirc.drv = drv; + ir_dev->raw->lirc.ir_dev = ir_dev; + ir_dev->raw->lirc.lircdata = PULSE_MASK; + + return 0; + +lirc_register_failed: +rbuf_init_failed: + kfree(rbuf); +rbuf_alloc_failed: + kfree(drv); + + return rc; +} + +static int ir_lirc_unregister(struct input_dev *input_dev) +{ + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + struct lirc_codec *lirc = &ir_dev->raw->lirc; + + lirc_unregister_driver(lirc->drv->minor); + lirc_buffer_free(lirc->drv->rbuf); + kfree(lirc->drv); + + return 0; +} + +static struct ir_raw_handler lirc_handler = { + .protocols = IR_TYPE_LIRC, + .decode = ir_lirc_decode, + .raw_register = ir_lirc_register, + .raw_unregister = ir_lirc_unregister, +}; + +static int __init ir_lirc_codec_init(void) +{ + ir_raw_handler_register(&lirc_handler); + + printk(KERN_INFO "IR LIRC bridge handler initialized\n"); + return 0; +} + +static void __exit ir_lirc_codec_exit(void) +{ + ir_raw_handler_unregister(&lirc_handler); +} + +module_init(ir_lirc_codec_init); +module_exit(ir_lirc_codec_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>"); +MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)"); +MODULE_DESCRIPTION("LIRC IR handler bridge"); diff --git a/drivers/media/IR/ir-nec-decoder.c b/drivers/media/IR/ir-nec-decoder.c index ba79233..52e0f37 100644 --- a/drivers/media/IR/ir-nec-decoder.c +++ b/drivers/media/IR/ir-nec-decoder.c @@ -27,10 +27,6 @@ #define NEC_TRAILER_PULSE (1 * NEC_UNIT) #define NEC_TRAILER_SPACE (10 * NEC_UNIT) /* even longer in reality */ -/* Used to register nec_decoder clients */ -static LIST_HEAD(decoder_list); -static DEFINE_SPINLOCK(decoder_lock); - enum nec_state { STATE_INACTIVE, STATE_HEADER_SPACE, @@ -40,84 +36,6 @@ enum nec_state { STATE_TRAILER_SPACE, }; -struct decoder_data { - struct list_head list; - struct ir_input_dev *ir_dev; - int enabled:1; - - /* State machine control */ - enum nec_state state; - u32 nec_bits; - unsigned count; -}; - - -/** - * get_decoder_data() - gets decoder data - * @input_dev: input device - * - * Returns the struct decoder_data that corresponds to a device - */ -static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev) -{ - struct decoder_data *data = NULL; - - spin_lock(&decoder_lock); - list_for_each_entry(data, &decoder_list, list) { - if (data->ir_dev == ir_dev) - break; - } - spin_unlock(&decoder_lock); - return data; -} - -static ssize_t store_enabled(struct device *d, - struct device_attribute *mattr, - const char *buf, - size_t len) -{ - unsigned long value; - struct ir_input_dev *ir_dev = dev_get_drvdata(d); - struct decoder_data *data = get_decoder_data(ir_dev); - - if (!data) - return -EINVAL; - - if (strict_strtoul(buf, 10, &value) || value > 1) - return -EINVAL; - - data->enabled = value; - - return len; -} - -static ssize_t show_enabled(struct device *d, - struct device_attribute *mattr, char *buf) -{ - struct ir_input_dev *ir_dev = dev_get_drvdata(d); - struct decoder_data *data = get_decoder_data(ir_dev); - - if (!data) - return -EINVAL; - - if (data->enabled) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - -static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled); - -static struct attribute *decoder_attributes[] = { - &dev_attr_enabled.attr, - NULL -}; - -static struct attribute_group decoder_attribute_group = { - .name = "nec_decoder", - .attrs = decoder_attributes, -}; - /** * ir_nec_decode() - Decode one NEC pulse or space * @input_dev: the struct input_dev descriptor of the device @@ -127,16 +45,12 @@ static struct attribute_group decoder_attribute_group = { */ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev) { - struct decoder_data *data; struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + struct nec_dec *data = &ir_dev->raw->nec; u32 scancode; u8 address, not_address, command, not_command; - data = get_decoder_data(ir_dev); - if (!data) - return -EINVAL; - - if (!data->enabled) + if (!(ir_dev->raw->enabled_protocols & IR_TYPE_NEC)) return 0; if (IS_RESET(ev)) { @@ -191,9 +105,9 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev) if (ev.pulse) break; - data->nec_bits <<= 1; + data->bits <<= 1; if (eq_margin(ev.duration, NEC_BIT_1_SPACE, NEC_UNIT / 2)) - data->nec_bits |= 1; + data->bits |= 1; else if (!eq_margin(ev.duration, NEC_BIT_0_SPACE, NEC_UNIT / 2)) break; data->count++; @@ -222,14 +136,14 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev) if (!geq_margin(ev.duration, NEC_TRAILER_SPACE, NEC_UNIT / 2)) break; - address = bitrev8((data->nec_bits >> 24) & 0xff); - not_address = bitrev8((data->nec_bits >> 16) & 0xff); - command = bitrev8((data->nec_bits >> 8) & 0xff); - not_command = bitrev8((data->nec_bits >> 0) & 0xff); + address = bitrev8((data->bits >> 24) & 0xff); + not_address = bitrev8((data->bits >> 16) & 0xff); + command = bitrev8((data->bits >> 8) & 0xff); + not_command = bitrev8((data->bits >> 0) & 0xff); if ((command ^ not_command) != 0xff) { IR_dprintk(1, "NEC checksum error: received 0x%08x\n", - data->nec_bits); + data->bits); break; } @@ -256,54 +170,9 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev) return -EINVAL; } -static int ir_nec_register(struct input_dev *input_dev) -{ - struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); - struct decoder_data *data; - int rc; - - rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group); - if (rc < 0) - return rc; - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { - sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); - return -ENOMEM; - } - - data->ir_dev = ir_dev; - data->enabled = 1; - - spin_lock(&decoder_lock); - list_add_tail(&data->list, &decoder_list); - spin_unlock(&decoder_lock); - - return 0; -} - -static int ir_nec_unregister(struct input_dev *input_dev) -{ - struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); - static struct decoder_data *data; - - data = get_decoder_data(ir_dev); - if (!data) - return 0; - - sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); - - spin_lock(&decoder_lock); - list_del(&data->list); - spin_unlock(&decoder_lock); - - return 0; -} - static struct ir_raw_handler nec_handler = { + .protocols = IR_TYPE_NEC, .decode = ir_nec_decode, - .raw_register = ir_nec_register, - .raw_unregister = ir_nec_unregister, }; static int __init ir_nec_decode_init(void) diff --git a/drivers/media/IR/ir-raw-event.c b/drivers/media/IR/ir-raw-event.c index ea68a3f..6f192ef 100644 --- a/drivers/media/IR/ir-raw-event.c +++ b/drivers/media/IR/ir-raw-event.c @@ -20,35 +20,13 @@ /* Define the max number of pulse/space transitions to buffer */ #define MAX_IR_EVENT_SIZE 512 +/* Used to keep track of IR raw clients, protected by ir_raw_handler_lock */ +static LIST_HEAD(ir_raw_client_list); + /* Used to handle IR raw handler extensions */ -static LIST_HEAD(ir_raw_handler_list); static DEFINE_SPINLOCK(ir_raw_handler_lock); - -/** - * RUN_DECODER() - runs an operation on all IR decoders - * @ops: IR raw handler operation to be called - * @arg: arguments to be passed to the callback - * - * Calls ir_raw_handler::ops for all registered IR handlers. It prevents - * new decode addition/removal while running, by locking ir_raw_handler_lock - * mutex. If an error occurs, it stops the ops. Otherwise, it returns a sum - * of the return codes. - */ -#define RUN_DECODER(ops, ...) ({ \ - struct ir_raw_handler *_ir_raw_handler; \ - int _sumrc = 0, _rc; \ - spin_lock(&ir_raw_handler_lock); \ - list_for_each_entry(_ir_raw_handler, &ir_raw_handler_list, list) { \ - if (_ir_raw_handler->ops) { \ - _rc = _ir_raw_handler->ops(__VA_ARGS__); \ - if (_rc < 0) \ - break; \ - _sumrc += _rc; \ - } \ - } \ - spin_unlock(&ir_raw_handler_lock); \ - _sumrc; \ -}) +static LIST_HEAD(ir_raw_handler_list); +static u64 available_protocols; #ifdef MODULE /* Used to load the decoders */ @@ -58,57 +36,17 @@ static struct work_struct wq_load; static void ir_raw_event_work(struct work_struct *work) { struct ir_raw_event ev; + struct ir_raw_handler *handler; struct ir_raw_event_ctrl *raw = container_of(work, struct ir_raw_event_ctrl, rx_work); - while (kfifo_out(&raw->kfifo, &ev, sizeof(ev)) == sizeof(ev)) - RUN_DECODER(decode, raw->input_dev, ev); -} - -int ir_raw_event_register(struct input_dev *input_dev) -{ - struct ir_input_dev *ir = input_get_drvdata(input_dev); - int rc; - - ir->raw = kzalloc(sizeof(*ir->raw), GFP_KERNEL); - if (!ir->raw) - return -ENOMEM; - - ir->raw->input_dev = input_dev; - INIT_WORK(&ir->raw->rx_work, ir_raw_event_work); - - rc = kfifo_alloc(&ir->raw->kfifo, sizeof(s64) * MAX_IR_EVENT_SIZE, - GFP_KERNEL); - if (rc < 0) { - kfree(ir->raw); - ir->raw = NULL; - return rc; - } - - rc = RUN_DECODER(raw_register, input_dev); - if (rc < 0) { - kfifo_free(&ir->raw->kfifo); - kfree(ir->raw); - ir->raw = NULL; - return rc; + while (kfifo_out(&raw->kfifo, &ev, sizeof(ev)) == sizeof(ev)) { + spin_lock(&ir_raw_handler_lock); + list_for_each_entry(handler, &ir_raw_handler_list, list) + handler->decode(raw->input_dev, ev); + spin_unlock(&ir_raw_handler_lock); + raw->prev_ev = ev; } - - return rc; -} - -void ir_raw_event_unregister(struct input_dev *input_dev) -{ - struct ir_input_dev *ir = input_get_drvdata(input_dev); - - if (!ir->raw) - return; - - cancel_work_sync(&ir->raw->rx_work); - RUN_DECODER(raw_unregister, input_dev); - - kfifo_free(&ir->raw->kfifo); - kfree(ir->raw); - ir->raw = NULL; } /** @@ -204,23 +142,103 @@ void ir_raw_event_handle(struct input_dev *input_dev) } EXPORT_SYMBOL_GPL(ir_raw_event_handle); +/* used internally by the sysfs interface */ +u64 +ir_raw_get_allowed_protocols() +{ + u64 protocols; + spin_lock(&ir_raw_handler_lock); + protocols = available_protocols; + spin_unlock(&ir_raw_handler_lock); + return protocols; +} + +/* + * Used to (un)register raw event clients + */ +int ir_raw_event_register(struct input_dev *input_dev) +{ + struct ir_input_dev *ir = input_get_drvdata(input_dev); + int rc; + struct ir_raw_handler *handler; + + ir->raw = kzalloc(sizeof(*ir->raw), GFP_KERNEL); + if (!ir->raw) + return -ENOMEM; + + ir->raw->input_dev = input_dev; + INIT_WORK(&ir->raw->rx_work, ir_raw_event_work); + ir->raw->enabled_protocols = ~0; + rc = kfifo_alloc(&ir->raw->kfifo, sizeof(s64) * MAX_IR_EVENT_SIZE, + GFP_KERNEL); + if (rc < 0) { + kfree(ir->raw); + ir->raw = NULL; + return rc; + } + + spin_lock(&ir_raw_handler_lock); + list_add_tail(&ir->raw->list, &ir_raw_client_list); + list_for_each_entry(handler, &ir_raw_handler_list, list) + if (handler->raw_register) + handler->raw_register(ir->raw->input_dev); + spin_unlock(&ir_raw_handler_lock); + + return 0; +} + +void ir_raw_event_unregister(struct input_dev *input_dev) +{ + struct ir_input_dev *ir = input_get_drvdata(input_dev); + struct ir_raw_handler *handler; + + if (!ir->raw) + return; + + cancel_work_sync(&ir->raw->rx_work); + + spin_lock(&ir_raw_handler_lock); + list_del(&ir->raw->list); + list_for_each_entry(handler, &ir_raw_handler_list, list) + if (handler->raw_unregister) + handler->raw_unregister(ir->raw->input_dev); + spin_unlock(&ir_raw_handler_lock); + + kfifo_free(&ir->raw->kfifo); + kfree(ir->raw); + ir->raw = NULL; +} + /* * Extension interface - used to register the IR decoders */ int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler) { + struct ir_raw_event_ctrl *raw; + spin_lock(&ir_raw_handler_lock); list_add_tail(&ir_raw_handler->list, &ir_raw_handler_list); + if (ir_raw_handler->raw_register) + list_for_each_entry(raw, &ir_raw_client_list, list) + ir_raw_handler->raw_register(raw->input_dev); + available_protocols |= ir_raw_handler->protocols; spin_unlock(&ir_raw_handler_lock); + return 0; } EXPORT_SYMBOL(ir_raw_handler_register); void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler) { + struct ir_raw_event_ctrl *raw; + spin_lock(&ir_raw_handler_lock); list_del(&ir_raw_handler->list); + if (ir_raw_handler->raw_unregister) + list_for_each_entry(raw, &ir_raw_client_list, list) + ir_raw_handler->raw_unregister(raw->input_dev); + available_protocols &= ~ir_raw_handler->protocols; spin_unlock(&ir_raw_handler_lock); } EXPORT_SYMBOL(ir_raw_handler_unregister); @@ -235,6 +253,7 @@ static void init_decoders(struct work_struct *work) load_rc6_decode(); load_jvc_decode(); load_sony_decode(); + load_lirc_codec(); /* If needed, we may later add some init code. In this case, it is needed to change the CONFIG_MODULE test at ir-core.h diff --git a/drivers/media/IR/ir-rc5-decoder.c b/drivers/media/IR/ir-rc5-decoder.c index 23cdb1b..df4770d9 100644 --- a/drivers/media/IR/ir-rc5-decoder.c +++ b/drivers/media/IR/ir-rc5-decoder.c @@ -30,10 +30,6 @@ #define RC5_BIT_END (1 * RC5_UNIT) #define RC5X_SPACE (4 * RC5_UNIT) -/* Used to register rc5_decoder clients */ -static LIST_HEAD(decoder_list); -static DEFINE_SPINLOCK(decoder_lock); - enum rc5_state { STATE_INACTIVE, STATE_BIT_START, @@ -42,87 +38,6 @@ enum rc5_state { STATE_FINISHED, }; -struct decoder_data { - struct list_head list; - struct ir_input_dev *ir_dev; - int enabled:1; - - /* State machine control */ - enum rc5_state state; - u32 rc5_bits; - struct ir_raw_event prev_ev; - unsigned count; - unsigned wanted_bits; -}; - - -/** - * get_decoder_data() - gets decoder data - * @input_dev: input device - * - * Returns the struct decoder_data that corresponds to a device - */ - -static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev) -{ - struct decoder_data *data = NULL; - - spin_lock(&decoder_lock); - list_for_each_entry(data, &decoder_list, list) { - if (data->ir_dev == ir_dev) - break; - } - spin_unlock(&decoder_lock); - return data; -} - -static ssize_t store_enabled(struct device *d, - struct device_attribute *mattr, - const char *buf, - size_t len) -{ - unsigned long value; - struct ir_input_dev *ir_dev = dev_get_drvdata(d); - struct decoder_data *data = get_decoder_data(ir_dev); - - if (!data) - return -EINVAL; - - if (strict_strtoul(buf, 10, &value) || value > 1) - return -EINVAL; - - data->enabled = value; - - return len; -} - -static ssize_t show_enabled(struct device *d, - struct device_attribute *mattr, char *buf) -{ - struct ir_input_dev *ir_dev = dev_get_drvdata(d); - struct decoder_data *data = get_decoder_data(ir_dev); - - if (!data) - return -EINVAL; - - if (data->enabled) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - -static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled); - -static struct attribute *decoder_attributes[] = { - &dev_attr_enabled.attr, - NULL -}; - -static struct attribute_group decoder_attribute_group = { - .name = "rc5_decoder", - .attrs = decoder_attributes, -}; - /** * ir_rc5_decode() - Decode one RC-5 pulse or space * @input_dev: the struct input_dev descriptor of the device @@ -132,17 +47,13 @@ static struct attribute_group decoder_attribute_group = { */ static int ir_rc5_decode(struct input_dev *input_dev, struct ir_raw_event ev) { - struct decoder_data *data; struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + struct rc5_dec *data = &ir_dev->raw->rc5; u8 toggle; u32 scancode; - data = get_decoder_data(ir_dev); - if (!data) - return -EINVAL; - - if (!data->enabled) - return 0; + if (!(ir_dev->raw->enabled_protocols & IR_TYPE_RC5)) + return 0; if (IS_RESET(ev)) { data->state = STATE_INACTIVE; @@ -176,16 +87,15 @@ again: if (!eq_margin(ev.duration, RC5_BIT_START, RC5_UNIT / 2)) break; - data->rc5_bits <<= 1; + data->bits <<= 1; if (!ev.pulse) - data->rc5_bits |= 1; + data->bits |= 1; data->count++; - data->prev_ev = ev; data->state = STATE_BIT_END; return 0; case STATE_BIT_END: - if (!is_transition(&ev, &data->prev_ev)) + if (!is_transition(&ev, &ir_dev->raw->prev_ev)) break; if (data->count == data->wanted_bits) @@ -217,11 +127,11 @@ again: if (data->wanted_bits == RC5X_NBITS) { /* RC5X */ u8 xdata, command, system; - xdata = (data->rc5_bits & 0x0003F) >> 0; - command = (data->rc5_bits & 0x00FC0) >> 6; - system = (data->rc5_bits & 0x1F000) >> 12; - toggle = (data->rc5_bits & 0x20000) ? 1 : 0; - command += (data->rc5_bits & 0x01000) ? 0 : 0x40; + xdata = (data->bits & 0x0003F) >> 0; + command = (data->bits & 0x00FC0) >> 6; + system = (data->bits & 0x1F000) >> 12; + toggle = (data->bits & 0x20000) ? 1 : 0; + command += (data->bits & 0x01000) ? 0 : 0x40; scancode = system << 16 | command << 8 | xdata; IR_dprintk(1, "RC5X scancode 0x%06x (toggle: %u)\n", @@ -230,10 +140,10 @@ again: } else { /* RC5 */ u8 command, system; - command = (data->rc5_bits & 0x0003F) >> 0; - system = (data->rc5_bits & 0x007C0) >> 6; - toggle = (data->rc5_bits & 0x00800) ? 1 : 0; - command += (data->rc5_bits & 0x01000) ? 0 : 0x40; + command = (data->bits & 0x0003F) >> 0; + system = (data->bits & 0x007C0) >> 6; + toggle = (data->bits & 0x00800) ? 1 : 0; + command += (data->bits & 0x01000) ? 0 : 0x40; scancode = system << 8 | command; IR_dprintk(1, "RC5 scancode 0x%04x (toggle: %u)\n", @@ -252,54 +162,9 @@ out: return -EINVAL; } -static int ir_rc5_register(struct input_dev *input_dev) -{ - struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); - struct decoder_data *data; - int rc; - - rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group); - if (rc < 0) - return rc; - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { - sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); - return -ENOMEM; - } - - data->ir_dev = ir_dev; - data->enabled = 1; - - spin_lock(&decoder_lock); - list_add_tail(&data->list, &decoder_list); - spin_unlock(&decoder_lock); - - return 0; -} - -static int ir_rc5_unregister(struct input_dev *input_dev) -{ - struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); - static struct decoder_data *data; - - data = get_decoder_data(ir_dev); - if (!data) - return 0; - - sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); - - spin_lock(&decoder_lock); - list_del(&data->list); - spin_unlock(&decoder_lock); - - return 0; -} - static struct ir_raw_handler rc5_handler = { + .protocols = IR_TYPE_RC5, .decode = ir_rc5_decode, - .raw_register = ir_rc5_register, - .raw_unregister = ir_rc5_unregister, }; static int __init ir_rc5_decode_init(void) diff --git a/drivers/media/IR/ir-rc6-decoder.c b/drivers/media/IR/ir-rc6-decoder.c index 2bf479f..f1624b8 100644 --- a/drivers/media/IR/ir-rc6-decoder.c +++ b/drivers/media/IR/ir-rc6-decoder.c @@ -36,10 +36,6 @@ #define RC6_STARTBIT_MASK 0x08 /* for the header bits */ #define RC6_6A_MCE_TOGGLE_MASK 0x8000 /* for the body bits */ -/* Used to register rc6_decoder clients */ -static LIST_HEAD(decoder_list); -static DEFINE_SPINLOCK(decoder_lock); - enum rc6_mode { RC6_MODE_0, RC6_MODE_6A, @@ -58,89 +54,8 @@ enum rc6_state { STATE_FINISHED, }; -struct decoder_data { - struct list_head list; - struct ir_input_dev *ir_dev; - int enabled:1; - - /* State machine control */ - enum rc6_state state; - u8 header; - u32 body; - struct ir_raw_event prev_ev; - bool toggle; - unsigned count; - unsigned wanted_bits; -}; - - -/** - * get_decoder_data() - gets decoder data - * @input_dev: input device - * - * Returns the struct decoder_data that corresponds to a device - */ -static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev) -{ - struct decoder_data *data = NULL; - - spin_lock(&decoder_lock); - list_for_each_entry(data, &decoder_list, list) { - if (data->ir_dev == ir_dev) - break; - } - spin_unlock(&decoder_lock); - return data; -} - -static ssize_t store_enabled(struct device *d, - struct device_attribute *mattr, - const char *buf, - size_t len) +static enum rc6_mode rc6_mode(struct rc6_dec *data) { - unsigned long value; - struct ir_input_dev *ir_dev = dev_get_drvdata(d); - struct decoder_data *data = get_decoder_data(ir_dev); - - if (!data) - return -EINVAL; - - if (strict_strtoul(buf, 10, &value) || value > 1) - return -EINVAL; - - data->enabled = value; - - return len; -} - -static ssize_t show_enabled(struct device *d, - struct device_attribute *mattr, char *buf) -{ - struct ir_input_dev *ir_dev = dev_get_drvdata(d); - struct decoder_data *data = get_decoder_data(ir_dev); - - if (!data) - return -EINVAL; - - if (data->enabled) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - -static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled); - -static struct attribute *decoder_attributes[] = { - &dev_attr_enabled.attr, - NULL -}; - -static struct attribute_group decoder_attribute_group = { - .name = "rc6_decoder", - .attrs = decoder_attributes, -}; - -static enum rc6_mode rc6_mode(struct decoder_data *data) { switch (data->header & RC6_MODE_MASK) { case 0: return RC6_MODE_0; @@ -162,16 +77,12 @@ static enum rc6_mode rc6_mode(struct decoder_data *data) { */ static int ir_rc6_decode(struct input_dev *input_dev, struct ir_raw_event ev) { - struct decoder_data *data; struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + struct rc6_dec *data = &ir_dev->raw->rc6; u32 scancode; u8 toggle; - data = get_decoder_data(ir_dev); - if (!data) - return -EINVAL; - - if (!data->enabled) + if (!(ir_dev->raw->enabled_protocols & IR_TYPE_RC6)) return 0; if (IS_RESET(ev)) { @@ -223,12 +134,11 @@ again: if (ev.pulse) data->header |= 1; data->count++; - data->prev_ev = ev; data->state = STATE_HEADER_BIT_END; return 0; case STATE_HEADER_BIT_END: - if (!is_transition(&ev, &data->prev_ev)) + if (!is_transition(&ev, &ir_dev->raw->prev_ev)) break; if (data->count == RC6_HEADER_NBITS) @@ -244,12 +154,11 @@ again: break; data->toggle = ev.pulse; - data->prev_ev = ev; data->state = STATE_TOGGLE_END; return 0; case STATE_TOGGLE_END: - if (!is_transition(&ev, &data->prev_ev) || + if (!is_transition(&ev, &ir_dev->raw->prev_ev) || !geq_margin(ev.duration, RC6_TOGGLE_END, RC6_UNIT / 2)) break; @@ -259,7 +168,6 @@ again: } data->state = STATE_BODY_BIT_START; - data->prev_ev = ev; decrease_duration(&ev, RC6_TOGGLE_END); data->count = 0; @@ -291,13 +199,11 @@ again: if (ev.pulse) data->body |= 1; data->count++; - data->prev_ev = ev; - data->state = STATE_BODY_BIT_END; return 0; case STATE_BODY_BIT_END: - if (!is_transition(&ev, &data->prev_ev)) + if (!is_transition(&ev, &ir_dev->raw->prev_ev)) break; if (data->count == data->wanted_bits) @@ -348,54 +254,9 @@ out: return -EINVAL; } -static int ir_rc6_register(struct input_dev *input_dev) -{ - struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); - struct decoder_data *data; - int rc; - - rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group); - if (rc < 0) - return rc; - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { - sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); - return -ENOMEM; - } - - data->ir_dev = ir_dev; - data->enabled = 1; - - spin_lock(&decoder_lock); - list_add_tail(&data->list, &decoder_list); - spin_unlock(&decoder_lock); - - return 0; -} - -static int ir_rc6_unregister(struct input_dev *input_dev) -{ - struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); - static struct decoder_data *data; - - data = get_decoder_data(ir_dev); - if (!data) - return 0; - - sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); - - spin_lock(&decoder_lock); - list_del(&data->list); - spin_unlock(&decoder_lock); - - return 0; -} - static struct ir_raw_handler rc6_handler = { + .protocols = IR_TYPE_RC6, .decode = ir_rc6_decode, - .raw_register = ir_rc6_register, - .raw_unregister = ir_rc6_unregister, }; static int __init ir_rc6_decode_init(void) diff --git a/drivers/media/IR/ir-sony-decoder.c b/drivers/media/IR/ir-sony-decoder.c index 9f440c5..b9074f0 100644 --- a/drivers/media/IR/ir-sony-decoder.c +++ b/drivers/media/IR/ir-sony-decoder.c @@ -23,10 +23,6 @@ #define SONY_BIT_SPACE (1 * SONY_UNIT) #define SONY_TRAILER_SPACE (10 * SONY_UNIT) /* minimum */ -/* Used to register sony_decoder clients */ -static LIST_HEAD(decoder_list); -static DEFINE_SPINLOCK(decoder_lock); - enum sony_state { STATE_INACTIVE, STATE_HEADER_SPACE, @@ -35,84 +31,6 @@ enum sony_state { STATE_FINISHED, }; -struct decoder_data { - struct list_head list; - struct ir_input_dev *ir_dev; - int enabled:1; - - /* State machine control */ - enum sony_state state; - u32 sony_bits; - unsigned count; -}; - - -/** - * get_decoder_data() - gets decoder data - * @input_dev: input device - * - * Returns the struct decoder_data that corresponds to a device - */ -static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev) -{ - struct decoder_data *data = NULL; - - spin_lock(&decoder_lock); - list_for_each_entry(data, &decoder_list, list) { - if (data->ir_dev == ir_dev) - break; - } - spin_unlock(&decoder_lock); - return data; -} - -static ssize_t store_enabled(struct device *d, - struct device_attribute *mattr, - const char *buf, - size_t len) -{ - unsigned long value; - struct ir_input_dev *ir_dev = dev_get_drvdata(d); - struct decoder_data *data = get_decoder_data(ir_dev); - - if (!data) - return -EINVAL; - - if (strict_strtoul(buf, 10, &value) || value > 1) - return -EINVAL; - - data->enabled = value; - - return len; -} - -static ssize_t show_enabled(struct device *d, - struct device_attribute *mattr, char *buf) -{ - struct ir_input_dev *ir_dev = dev_get_drvdata(d); - struct decoder_data *data = get_decoder_data(ir_dev); - - if (!data) - return -EINVAL; - - if (data->enabled) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - -static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled); - -static struct attribute *decoder_attributes[] = { - &dev_attr_enabled.attr, - NULL -}; - -static struct attribute_group decoder_attribute_group = { - .name = "sony_decoder", - .attrs = decoder_attributes, -}; - /** * ir_sony_decode() - Decode one Sony pulse or space * @input_dev: the struct input_dev descriptor of the device @@ -122,16 +40,12 @@ static struct attribute_group decoder_attribute_group = { */ static int ir_sony_decode(struct input_dev *input_dev, struct ir_raw_event ev) { - struct decoder_data *data; struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + struct sony_dec *data = &ir_dev->raw->sony; u32 scancode; u8 device, subdevice, function; - data = get_decoder_data(ir_dev); - if (!data) - return -EINVAL; - - if (!data->enabled) + if (!(ir_dev->raw->enabled_protocols & IR_TYPE_SONY)) return 0; if (IS_RESET(ev)) { @@ -172,9 +86,9 @@ static int ir_sony_decode(struct input_dev *input_dev, struct ir_raw_event ev) if (!ev.pulse) break; - data->sony_bits <<= 1; + data->bits <<= 1; if (eq_margin(ev.duration, SONY_BIT_1_PULSE, SONY_UNIT / 2)) - data->sony_bits |= 1; + data->bits |= 1; else if (!eq_margin(ev.duration, SONY_BIT_0_PULSE, SONY_UNIT / 2)) break; @@ -208,19 +122,19 @@ static int ir_sony_decode(struct input_dev *input_dev, struct ir_raw_event ev) switch (data->count) { case 12: - device = bitrev8((data->sony_bits << 3) & 0xF8); + device = bitrev8((data->bits << 3) & 0xF8); subdevice = 0; - function = bitrev8((data->sony_bits >> 4) & 0xFE); + function = bitrev8((data->bits >> 4) & 0xFE); break; case 15: - device = bitrev8((data->sony_bits >> 0) & 0xFF); + device = bitrev8((data->bits >> 0) & 0xFF); subdevice = 0; - function = bitrev8((data->sony_bits >> 7) & 0xFD); + function = bitrev8((data->bits >> 7) & 0xFD); break; case 20: - device = bitrev8((data->sony_bits >> 5) & 0xF8); - subdevice = bitrev8((data->sony_bits >> 0) & 0xFF); - function = bitrev8((data->sony_bits >> 12) & 0xFE); + device = bitrev8((data->bits >> 5) & 0xF8); + subdevice = bitrev8((data->bits >> 0) & 0xFF); + function = bitrev8((data->bits >> 12) & 0xFE); break; default: IR_dprintk(1, "Sony invalid bitcount %u\n", data->count); @@ -241,54 +155,9 @@ out: return -EINVAL; } -static int ir_sony_register(struct input_dev *input_dev) -{ - struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); - struct decoder_data *data; - int rc; - - rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group); - if (rc < 0) - return rc; - - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { - sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); - return -ENOMEM; - } - - data->ir_dev = ir_dev; - data->enabled = 1; - - spin_lock(&decoder_lock); - list_add_tail(&data->list, &decoder_list); - spin_unlock(&decoder_lock); - - return 0; -} - -static int ir_sony_unregister(struct input_dev *input_dev) -{ - struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); - static struct decoder_data *data; - - data = get_decoder_data(ir_dev); - if (!data) - return 0; - - sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); - - spin_lock(&decoder_lock); - list_del(&data->list); - spin_unlock(&decoder_lock); - - return 0; -} - static struct ir_raw_handler sony_handler = { + .protocols = IR_TYPE_SONY, .decode = ir_sony_decode, - .raw_register = ir_sony_register, - .raw_unregister = ir_sony_unregister, }; static int __init ir_sony_decode_init(void) diff --git a/drivers/media/IR/ir-sysfs.c b/drivers/media/IR/ir-sysfs.c index 2098dd1..6273047 100644 --- a/drivers/media/IR/ir-sysfs.c +++ b/drivers/media/IR/ir-sysfs.c @@ -33,125 +33,172 @@ static struct class ir_input_class = { .devnode = ir_devnode, }; +static struct { + u64 type; + char *name; +} proto_names[] = { + { IR_TYPE_UNKNOWN, "unknown" }, + { IR_TYPE_RC5, "rc-5" }, + { IR_TYPE_NEC, "nec" }, + { IR_TYPE_RC6, "rc-6" }, + { IR_TYPE_JVC, "jvc" }, + { IR_TYPE_SONY, "sony" }, + { IR_TYPE_LIRC, "lirc" }, +}; + +#define PROTO_NONE "none" + /** - * show_protocol() - shows the current IR protocol + * show_protocols() - shows the current IR protocol(s) * @d: the device descriptor * @mattr: the device attribute struct (unused) * @buf: a pointer to the output buffer * - * This routine is a callback routine for input read the IR protocol type. - * it is trigged by reading /sys/class/rc/rc?/current_protocol. - * It returns the protocol name, as understood by the driver. + * This routine is a callback routine for input read the IR protocol type(s). + * it is trigged by reading /sys/class/rc/rc?/protocols. + * It returns the protocol names of supported protocols. + * Enabled protocols are printed in brackets. */ -static ssize_t show_protocol(struct device *d, - struct device_attribute *mattr, char *buf) +static ssize_t show_protocols(struct device *d, + struct device_attribute *mattr, char *buf) { - char *s; struct ir_input_dev *ir_dev = dev_get_drvdata(d); - u64 ir_type = ir_dev->rc_tab.ir_type; - - IR_dprintk(1, "Current protocol is %lld\n", (long long)ir_type); - - /* FIXME: doesn't support multiple protocols at the same time */ - if (ir_type == IR_TYPE_UNKNOWN) - s = "Unknown"; - else if (ir_type == IR_TYPE_RC5) - s = "rc-5"; - else if (ir_type == IR_TYPE_NEC) - s = "nec"; - else if (ir_type == IR_TYPE_RC6) - s = "rc6"; - else if (ir_type == IR_TYPE_JVC) - s = "jvc"; - else if (ir_type == IR_TYPE_SONY) - s = "sony"; - else - s = "other"; + u64 allowed, enabled; + char *tmp = buf; + int i; + + if (ir_dev->props->driver_type == RC_DRIVER_SCANCODE) { + enabled = ir_dev->rc_tab.ir_type; + allowed = ir_dev->props->allowed_protos; + } else { + enabled = ir_dev->raw->enabled_protocols; + allowed = ir_raw_get_allowed_protocols(); + } + + IR_dprintk(1, "allowed - 0x%llx, enabled - 0x%llx\n", + (long long)allowed, + (long long)enabled); - return sprintf(buf, "%s\n", s); + for (i = 0; i < ARRAY_SIZE(proto_names); i++) { + if (allowed & enabled & proto_names[i].type) + tmp += sprintf(tmp, "[%s] ", proto_names[i].name); + else if (allowed & proto_names[i].type) + tmp += sprintf(tmp, "%s ", proto_names[i].name); + } + + if (tmp != buf) + tmp--; + *tmp = '\n'; + return tmp + 1 - buf; } /** - * store_protocol() - shows the current IR protocol + * store_protocols() - changes the current IR protocol(s) * @d: the device descriptor * @mattr: the device attribute struct (unused) * @buf: a pointer to the input buffer * @len: length of the input buffer * * This routine is a callback routine for changing the IR protocol type. - * it is trigged by reading /sys/class/rc/rc?/current_protocol. - * It changes the IR the protocol name, if the IR type is recognized - * by the driver. - * If an unknown protocol name is used, returns -EINVAL. + * It is trigged by writing to /sys/class/rc/rc?/protocols. + * Writing "+proto" will add a protocol to the list of enabled protocols. + * Writing "-proto" will remove a protocol from the list of enabled protocols. + * Writing "proto" will enable only "proto". + * Writing "none" will disable all protocols. + * Returns -EINVAL if an invalid protocol combination or unknown protocol name + * is used, otherwise @len. */ -static ssize_t store_protocol(struct device *d, - struct device_attribute *mattr, - const char *data, - size_t len) +static ssize_t store_protocols(struct device *d, + struct device_attribute *mattr, + const char *data, + size_t len) { struct ir_input_dev *ir_dev = dev_get_drvdata(d); - u64 ir_type = 0; - int rc = -EINVAL; + bool enable, disable; + const char *tmp; + u64 type; + u64 mask; + int rc, i, count = 0; unsigned long flags; - char *buf; - - while ((buf = strsep((char **) &data, " \n")) != NULL) { - if (!strcasecmp(buf, "rc-5") || !strcasecmp(buf, "rc5")) - ir_type |= IR_TYPE_RC5; - if (!strcasecmp(buf, "nec")) - ir_type |= IR_TYPE_NEC; - if (!strcasecmp(buf, "jvc")) - ir_type |= IR_TYPE_JVC; - if (!strcasecmp(buf, "sony")) - ir_type |= IR_TYPE_SONY; + + if (ir_dev->props->driver_type == RC_DRIVER_SCANCODE) + type = ir_dev->rc_tab.ir_type; + else + type = ir_dev->raw->enabled_protocols; + + while ((tmp = strsep((char **) &data, " \n")) != NULL) { + if (!*tmp) + break; + + if (*tmp == '+') { + enable = true; + disable = false; + tmp++; + } else if (*tmp == '-') { + enable = false; + disable = true; + tmp++; + } else { + enable = false; + disable = false; + } + + if (!enable && !disable && !strncasecmp(tmp, PROTO_NONE, sizeof(PROTO_NONE))) { + tmp += sizeof(PROTO_NONE); + mask = 0; + count++; + } else { + for (i = 0; i < ARRAY_SIZE(proto_names); i++) { + if (!strncasecmp(tmp, proto_names[i].name, strlen(proto_names[i].name))) { + tmp += strlen(proto_names[i].name); + mask = proto_names[i].type; + break; + } + } + if (i == ARRAY_SIZE(proto_names)) { + IR_dprintk(1, "Unknown protocol: '%s'\n", tmp); + return -EINVAL; + } + count++; + } + + if (enable) + type |= mask; + else if (disable) + type &= ~mask; + else + type = mask; } - if (!ir_type) { - IR_dprintk(1, "Unknown protocol\n"); + if (!count) { + IR_dprintk(1, "Protocol not specified\n"); return -EINVAL; } - if (ir_dev->props && ir_dev->props->change_protocol) + if (ir_dev->props && ir_dev->props->change_protocol) { rc = ir_dev->props->change_protocol(ir_dev->props->priv, - ir_type); - - if (rc < 0) { - IR_dprintk(1, "Error setting protocol to %lld\n", - (long long)ir_type); - return -EINVAL; + type); + if (rc < 0) { + IR_dprintk(1, "Error setting protocols to 0x%llx\n", + (long long)type); + return -EINVAL; + } } - spin_lock_irqsave(&ir_dev->rc_tab.lock, flags); - ir_dev->rc_tab.ir_type = ir_type; - spin_unlock_irqrestore(&ir_dev->rc_tab.lock, flags); + if (ir_dev->props->driver_type == RC_DRIVER_SCANCODE) { + spin_lock_irqsave(&ir_dev->rc_tab.lock, flags); + ir_dev->rc_tab.ir_type = type; + spin_unlock_irqrestore(&ir_dev->rc_tab.lock, flags); + } else { + ir_dev->raw->enabled_protocols = type; + } - IR_dprintk(1, "Current protocol(s) is(are) %lld\n", - (long long)ir_type); + IR_dprintk(1, "Current protocol(s): 0x%llx\n", + (long long)type); return len; } -static ssize_t show_supported_protocols(struct device *d, - struct device_attribute *mattr, char *buf) -{ - char *orgbuf = buf; - struct ir_input_dev *ir_dev = dev_get_drvdata(d); - - /* FIXME: doesn't support multiple protocols at the same time */ - if (ir_dev->props->allowed_protos == IR_TYPE_UNKNOWN) - buf += sprintf(buf, "unknown "); - if (ir_dev->props->allowed_protos & IR_TYPE_RC5) - buf += sprintf(buf, "rc-5 "); - if (ir_dev->props->allowed_protos & IR_TYPE_NEC) - buf += sprintf(buf, "nec "); - if (buf == orgbuf) - buf += sprintf(buf, "other "); - - buf += sprintf(buf - 1, "\n"); - - return buf - orgbuf; -} - #define ADD_HOTPLUG_VAR(fmt, val...) \ do { \ int err = add_uevent_var(env, fmt, val); \ @@ -159,7 +206,7 @@ static ssize_t show_supported_protocols(struct device *d, return err; \ } while (0) -static int ir_dev_uevent(struct device *device, struct kobj_uevent_env *env) +static int rc_dev_uevent(struct device *device, struct kobj_uevent_env *env) { struct ir_input_dev *ir_dev = dev_get_drvdata(device); @@ -174,34 +221,26 @@ static int ir_dev_uevent(struct device *device, struct kobj_uevent_env *env) /* * Static device attribute struct with the sysfs attributes for IR's */ -static DEVICE_ATTR(protocol, S_IRUGO | S_IWUSR, - show_protocol, store_protocol); +static DEVICE_ATTR(protocols, S_IRUGO | S_IWUSR, + show_protocols, store_protocols); -static DEVICE_ATTR(supported_protocols, S_IRUGO | S_IWUSR, - show_supported_protocols, NULL); - -static struct attribute *ir_hw_dev_attrs[] = { - &dev_attr_protocol.attr, - &dev_attr_supported_protocols.attr, +static struct attribute *rc_dev_attrs[] = { + &dev_attr_protocols.attr, NULL, }; -static struct attribute_group ir_hw_dev_attr_grp = { - .attrs = ir_hw_dev_attrs, +static struct attribute_group rc_dev_attr_grp = { + .attrs = rc_dev_attrs, }; -static const struct attribute_group *ir_hw_dev_attr_groups[] = { - &ir_hw_dev_attr_grp, +static const struct attribute_group *rc_dev_attr_groups[] = { + &rc_dev_attr_grp, NULL }; static struct device_type rc_dev_type = { - .groups = ir_hw_dev_attr_groups, - .uevent = ir_dev_uevent, -}; - -static struct device_type ir_raw_dev_type = { - .uevent = ir_dev_uevent, + .groups = rc_dev_attr_groups, + .uevent = rc_dev_uevent, }; /** @@ -221,11 +260,7 @@ int ir_register_class(struct input_dev *input_dev) if (unlikely(devno < 0)) return devno; - if (ir_dev->props) { - if (ir_dev->props->driver_type == RC_DRIVER_SCANCODE) - ir_dev->dev.type = &rc_dev_type; - } else - ir_dev->dev.type = &ir_raw_dev_type; + ir_dev->dev.type = &rc_dev_type; ir_dev->dev.class = &ir_input_class; ir_dev->dev.parent = input_dev->dev.parent; diff --git a/drivers/media/IR/keymaps/Makefile b/drivers/media/IR/keymaps/Makefile index aea649f..cbee062 100644 --- a/drivers/media/IR/keymaps/Makefile +++ b/drivers/media/IR/keymaps/Makefile @@ -14,6 +14,8 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-budget-ci-old.o \ rc-cinergy-1400.o \ rc-cinergy.o \ + rc-dib0700-nec.o \ + rc-dib0700-rc5.o \ rc-dm1105-nec.o \ rc-dntv-live-dvb-t.o \ rc-dntv-live-dvbt-pro.o \ @@ -37,6 +39,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-kaiomy.o \ rc-kworld-315u.o \ rc-kworld-plus-tv-analog.o \ + rc-lirc.o \ rc-manli.o \ rc-msi-tvanywhere.o \ rc-msi-tvanywhere-plus.o \ @@ -57,6 +60,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-pv951.o \ rc-rc5-hauppauge-new.o \ rc-rc5-tv.o \ + rc-rc6-mce.o \ rc-real-audio-220-32-keys.o \ rc-tbs-nec.o \ rc-terratec-cinergy-xs.o \ diff --git a/drivers/media/IR/keymaps/rc-dib0700-nec.c b/drivers/media/IR/keymaps/rc-dib0700-nec.c new file mode 100644 index 0000000..ae18320 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-dib0700-nec.c @@ -0,0 +1,124 @@ +/* rc-dvb0700-big.c - Keytable for devices in dvb0700 + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * TODO: This table is a real mess, as it merges RC codes from several + * devices into a big table. It also has both RC-5 and NEC codes inside. + * It should be broken into small tables, and the protocols should properly + * be indentificated. + * + * The table were imported from dib0700_devices.c. + * + * 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 <media/rc-map.h> + +static struct ir_scancode dib0700_nec_table[] = { + /* Key codes for the Pixelview SBTVD remote */ + { 0x8613, KEY_MUTE }, + { 0x8612, KEY_POWER }, + { 0x8601, KEY_1 }, + { 0x8602, KEY_2 }, + { 0x8603, KEY_3 }, + { 0x8604, KEY_4 }, + { 0x8605, KEY_5 }, + { 0x8606, KEY_6 }, + { 0x8607, KEY_7 }, + { 0x8608, KEY_8 }, + { 0x8609, KEY_9 }, + { 0x8600, KEY_0 }, + { 0x860d, KEY_CHANNELUP }, + { 0x8619, KEY_CHANNELDOWN }, + { 0x8610, KEY_VOLUMEUP }, + { 0x860c, KEY_VOLUMEDOWN }, + + { 0x860a, KEY_CAMERA }, + { 0x860b, KEY_ZOOM }, + { 0x861b, KEY_BACKSPACE }, + { 0x8615, KEY_ENTER }, + + { 0x861d, KEY_UP }, + { 0x861e, KEY_DOWN }, + { 0x860e, KEY_LEFT }, + { 0x860f, KEY_RIGHT }, + + { 0x8618, KEY_RECORD }, + { 0x861a, KEY_STOP }, + + /* Key codes for the EvolutePC TVWay+ remote */ + { 0x7a00, KEY_MENU }, + { 0x7a01, KEY_RECORD }, + { 0x7a02, KEY_PLAY }, + { 0x7a03, KEY_STOP }, + { 0x7a10, KEY_CHANNELUP }, + { 0x7a11, KEY_CHANNELDOWN }, + { 0x7a12, KEY_VOLUMEUP }, + { 0x7a13, KEY_VOLUMEDOWN }, + { 0x7a40, KEY_POWER }, + { 0x7a41, KEY_MUTE }, + + /* Key codes for the Elgato EyeTV Diversity silver remote */ + { 0x4501, KEY_POWER }, + { 0x4502, KEY_MUTE }, + { 0x4503, KEY_1 }, + { 0x4504, KEY_2 }, + { 0x4505, KEY_3 }, + { 0x4506, KEY_4 }, + { 0x4507, KEY_5 }, + { 0x4508, KEY_6 }, + { 0x4509, KEY_7 }, + { 0x450a, KEY_8 }, + { 0x450b, KEY_9 }, + { 0x450c, KEY_LAST }, + { 0x450d, KEY_0 }, + { 0x450e, KEY_ENTER }, + { 0x450f, KEY_RED }, + { 0x4510, KEY_CHANNELUP }, + { 0x4511, KEY_GREEN }, + { 0x4512, KEY_VOLUMEDOWN }, + { 0x4513, KEY_OK }, + { 0x4514, KEY_VOLUMEUP }, + { 0x4515, KEY_YELLOW }, + { 0x4516, KEY_CHANNELDOWN }, + { 0x4517, KEY_BLUE }, + { 0x4518, KEY_LEFT }, /* Skip backwards */ + { 0x4519, KEY_PLAYPAUSE }, + { 0x451a, KEY_RIGHT }, /* Skip forward */ + { 0x451b, KEY_REWIND }, + { 0x451c, KEY_L }, /* Live */ + { 0x451d, KEY_FASTFORWARD }, + { 0x451e, KEY_STOP }, /* 'Reveal' for Teletext */ + { 0x451f, KEY_MENU }, /* KEY_TEXT for Teletext */ + { 0x4540, KEY_RECORD }, /* Font 'Size' for Teletext */ + { 0x4541, KEY_SCREEN }, /* Full screen toggle, 'Hold' for Teletext */ + { 0x4542, KEY_SELECT }, /* Select video input, 'Select' for Teletext */ +}; + +static struct rc_keymap dib0700_nec_map = { + .map = { + .scan = dib0700_nec_table, + .size = ARRAY_SIZE(dib0700_nec_table), + .ir_type = IR_TYPE_NEC, + .name = RC_MAP_DIB0700_NEC_TABLE, + } +}; + +static int __init init_rc_map(void) +{ + return ir_register_map(&dib0700_nec_map); +} + +static void __exit exit_rc_map(void) +{ + ir_unregister_map(&dib0700_nec_map); +} + +module_init(init_rc_map) +module_exit(exit_rc_map) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-dib0700-rc5.c b/drivers/media/IR/keymaps/rc-dib0700-rc5.c new file mode 100644 index 0000000..4a4797c --- /dev/null +++ b/drivers/media/IR/keymaps/rc-dib0700-rc5.c @@ -0,0 +1,235 @@ +/* rc-dvb0700-big.c - Keytable for devices in dvb0700 + * + * Copyright (c) 2010 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * TODO: This table is a real mess, as it merges RC codes from several + * devices into a big table. It also has both RC-5 and NEC codes inside. + * It should be broken into small tables, and the protocols should properly + * be indentificated. + * + * The table were imported from dib0700_devices.c. + * + * 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 <media/rc-map.h> + +static struct ir_scancode dib0700_rc5_table[] = { + /* Key codes for the tiny Pinnacle remote*/ + { 0x0700, KEY_MUTE }, + { 0x0701, KEY_MENU }, /* Pinnacle logo */ + { 0x0739, KEY_POWER }, + { 0x0703, KEY_VOLUMEUP }, + { 0x0709, KEY_VOLUMEDOWN }, + { 0x0706, KEY_CHANNELUP }, + { 0x070c, KEY_CHANNELDOWN }, + { 0x070f, KEY_1 }, + { 0x0715, KEY_2 }, + { 0x0710, KEY_3 }, + { 0x0718, KEY_4 }, + { 0x071b, KEY_5 }, + { 0x071e, KEY_6 }, + { 0x0711, KEY_7 }, + { 0x0721, KEY_8 }, + { 0x0712, KEY_9 }, + { 0x0727, KEY_0 }, + { 0x0724, KEY_SCREEN }, /* 'Square' key */ + { 0x072a, KEY_TEXT }, /* 'T' key */ + { 0x072d, KEY_REWIND }, + { 0x0730, KEY_PLAY }, + { 0x0733, KEY_FASTFORWARD }, + { 0x0736, KEY_RECORD }, + { 0x073c, KEY_STOP }, + { 0x073f, KEY_CANCEL }, /* '?' key */ + + /* Key codes for the Terratec Cinergy DT XS Diversity, similar to cinergyT2.c */ + { 0xeb01, KEY_POWER }, + { 0xeb02, KEY_1 }, + { 0xeb03, KEY_2 }, + { 0xeb04, KEY_3 }, + { 0xeb05, KEY_4 }, + { 0xeb06, KEY_5 }, + { 0xeb07, KEY_6 }, + { 0xeb08, KEY_7 }, + { 0xeb09, KEY_8 }, + { 0xeb0a, KEY_9 }, + { 0xeb0b, KEY_VIDEO }, + { 0xeb0c, KEY_0 }, + { 0xeb0d, KEY_REFRESH }, + { 0xeb0f, KEY_EPG }, + { 0xeb10, KEY_UP }, + { 0xeb11, KEY_LEFT }, + { 0xeb12, KEY_OK }, + { 0xeb13, KEY_RIGHT }, + { 0xeb14, KEY_DOWN }, + { 0xeb16, KEY_INFO }, + { 0xeb17, KEY_RED }, + { 0xeb18, KEY_GREEN }, + { 0xeb19, KEY_YELLOW }, + { 0xeb1a, KEY_BLUE }, + { 0xeb1b, KEY_CHANNELUP }, + { 0xeb1c, KEY_VOLUMEUP }, + { 0xeb1d, KEY_MUTE }, + { 0xeb1e, KEY_VOLUMEDOWN }, + { 0xeb1f, KEY_CHANNELDOWN }, + { 0xeb40, KEY_PAUSE }, + { 0xeb41, KEY_HOME }, + { 0xeb42, KEY_MENU }, /* DVD Menu */ + { 0xeb43, KEY_SUBTITLE }, + { 0xeb44, KEY_TEXT }, /* Teletext */ + { 0xeb45, KEY_DELETE }, + { 0xeb46, KEY_TV }, + { 0xeb47, KEY_DVD }, + { 0xeb48, KEY_STOP }, + { 0xeb49, KEY_VIDEO }, + { 0xeb4a, KEY_AUDIO }, /* Music */ + { 0xeb4b, KEY_SCREEN }, /* Pic */ + { 0xeb4c, KEY_PLAY }, + { 0xeb4d, KEY_BACK }, + { 0xeb4e, KEY_REWIND }, + { 0xeb4f, KEY_FASTFORWARD }, + { 0xeb54, KEY_PREVIOUS }, + { 0xeb58, KEY_RECORD }, + { 0xeb5c, KEY_NEXT }, + + /* Key codes for the Haupauge WinTV Nova-TD, copied from nova-t-usb2.c (Nova-T USB2) */ + { 0x1e00, KEY_0 }, + { 0x1e01, KEY_1 }, + { 0x1e02, KEY_2 }, + { 0x1e03, KEY_3 }, + { 0x1e04, KEY_4 }, + { 0x1e05, KEY_5 }, + { 0x1e06, KEY_6 }, + { 0x1e07, KEY_7 }, + { 0x1e08, KEY_8 }, + { 0x1e09, KEY_9 }, + { 0x1e0a, KEY_KPASTERISK }, + { 0x1e0b, KEY_RED }, + { 0x1e0c, KEY_RADIO }, + { 0x1e0d, KEY_MENU }, + { 0x1e0e, KEY_GRAVE }, /* # */ + { 0x1e0f, KEY_MUTE }, + { 0x1e10, KEY_VOLUMEUP }, + { 0x1e11, KEY_VOLUMEDOWN }, + { 0x1e12, KEY_CHANNEL }, + { 0x1e14, KEY_UP }, + { 0x1e15, KEY_DOWN }, + { 0x1e16, KEY_LEFT }, + { 0x1e17, KEY_RIGHT }, + { 0x1e18, KEY_VIDEO }, + { 0x1e19, KEY_AUDIO }, + { 0x1e1a, KEY_MEDIA }, + { 0x1e1b, KEY_EPG }, + { 0x1e1c, KEY_TV }, + { 0x1e1e, KEY_NEXT }, + { 0x1e1f, KEY_BACK }, + { 0x1e20, KEY_CHANNELUP }, + { 0x1e21, KEY_CHANNELDOWN }, + { 0x1e24, KEY_LAST }, /* Skip backwards */ + { 0x1e25, KEY_OK }, + { 0x1e29, KEY_BLUE}, + { 0x1e2e, KEY_GREEN }, + { 0x1e30, KEY_PAUSE }, + { 0x1e32, KEY_REWIND }, + { 0x1e34, KEY_FASTFORWARD }, + { 0x1e35, KEY_PLAY }, + { 0x1e36, KEY_STOP }, + { 0x1e37, KEY_RECORD }, + { 0x1e38, KEY_YELLOW }, + { 0x1e3b, KEY_GOTO }, + { 0x1e3d, KEY_POWER }, + + /* Key codes for the Leadtek Winfast DTV Dongle */ + { 0x0042, KEY_POWER }, + { 0x077c, KEY_TUNER }, + { 0x0f4e, KEY_PRINT }, /* PREVIEW */ + { 0x0840, KEY_SCREEN }, /* full screen toggle*/ + { 0x0f71, KEY_DOT }, /* frequency */ + { 0x0743, KEY_0 }, + { 0x0c41, KEY_1 }, + { 0x0443, KEY_2 }, + { 0x0b7f, KEY_3 }, + { 0x0e41, KEY_4 }, + { 0x0643, KEY_5 }, + { 0x097f, KEY_6 }, + { 0x0d7e, KEY_7 }, + { 0x057c, KEY_8 }, + { 0x0a40, KEY_9 }, + { 0x0e4e, KEY_CLEAR }, + { 0x047c, KEY_CHANNEL }, /* show channel number */ + { 0x0f41, KEY_LAST }, /* recall */ + { 0x0342, KEY_MUTE }, + { 0x064c, KEY_RESERVED }, /* PIP button*/ + { 0x0172, KEY_SHUFFLE }, /* SNAPSHOT */ + { 0x0c4e, KEY_PLAYPAUSE }, /* TIMESHIFT */ + { 0x0b70, KEY_RECORD }, + { 0x037d, KEY_VOLUMEUP }, + { 0x017d, KEY_VOLUMEDOWN }, + { 0x0242, KEY_CHANNELUP }, + { 0x007d, KEY_CHANNELDOWN }, + + /* Key codes for Nova-TD "credit card" remote control. */ + { 0x1d00, KEY_0 }, + { 0x1d01, KEY_1 }, + { 0x1d02, KEY_2 }, + { 0x1d03, KEY_3 }, + { 0x1d04, KEY_4 }, + { 0x1d05, KEY_5 }, + { 0x1d06, KEY_6 }, + { 0x1d07, KEY_7 }, + { 0x1d08, KEY_8 }, + { 0x1d09, KEY_9 }, + { 0x1d0a, KEY_TEXT }, + { 0x1d0d, KEY_MENU }, + { 0x1d0f, KEY_MUTE }, + { 0x1d10, KEY_VOLUMEUP }, + { 0x1d11, KEY_VOLUMEDOWN }, + { 0x1d12, KEY_CHANNEL }, + { 0x1d14, KEY_UP }, + { 0x1d15, KEY_DOWN }, + { 0x1d16, KEY_LEFT }, + { 0x1d17, KEY_RIGHT }, + { 0x1d1c, KEY_TV }, + { 0x1d1e, KEY_NEXT }, + { 0x1d1f, KEY_BACK }, + { 0x1d20, KEY_CHANNELUP }, + { 0x1d21, KEY_CHANNELDOWN }, + { 0x1d24, KEY_LAST }, + { 0x1d25, KEY_OK }, + { 0x1d30, KEY_PAUSE }, + { 0x1d32, KEY_REWIND }, + { 0x1d34, KEY_FASTFORWARD }, + { 0x1d35, KEY_PLAY }, + { 0x1d36, KEY_STOP }, + { 0x1d37, KEY_RECORD }, + { 0x1d3b, KEY_GOTO }, + { 0x1d3d, KEY_POWER }, +}; + +static struct rc_keymap dib0700_rc5_map = { + .map = { + .scan = dib0700_rc5_table, + .size = ARRAY_SIZE(dib0700_rc5_table), + .ir_type = IR_TYPE_RC5, + .name = RC_MAP_DIB0700_RC5_TABLE, + } +}; + +static int __init init_rc_map(void) +{ + return ir_register_map(&dib0700_rc5_map); +} + +static void __exit exit_rc_map(void) +{ + ir_unregister_map(&dib0700_rc5_map); +} + +module_init(init_rc_map) +module_exit(exit_rc_map) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-lirc.c b/drivers/media/IR/keymaps/rc-lirc.c new file mode 100644 index 0000000..43fcf90 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-lirc.c @@ -0,0 +1,41 @@ +/* rc-lirc.c - Empty dummy keytable, for use when its preferred to pass + * all raw IR data to the lirc userspace decoder. + * + * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.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. + */ + +#include <media/ir-core.h> + +static struct ir_scancode lirc[] = { + { }, +}; + +static struct rc_keymap lirc_map = { + .map = { + .scan = lirc, + .size = ARRAY_SIZE(lirc), + .ir_type = IR_TYPE_LIRC, + .name = RC_MAP_LIRC, + } +}; + +static int __init init_rc_map_lirc(void) +{ + return ir_register_map(&lirc_map); +} + +static void __exit exit_rc_map_lirc(void) +{ + ir_unregister_map(&lirc_map); +} + +module_init(init_rc_map_lirc) +module_exit(exit_rc_map_lirc) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>"); diff --git a/drivers/media/IR/keymaps/rc-rc6-mce.c b/drivers/media/IR/keymaps/rc-rc6-mce.c new file mode 100644 index 0000000..c6726a8 --- /dev/null +++ b/drivers/media/IR/keymaps/rc-rc6-mce.c @@ -0,0 +1,105 @@ +/* rc-rc6-mce.c - Keytable for Windows Media Center RC-6 remotes for use + * with the Media Center Edition eHome Infrared Transceiver. + * + * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.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. + */ + +#include <media/rc-map.h> + +static struct ir_scancode rc6_mce[] = { + { 0x800f0415, KEY_REWIND }, + { 0x800f0414, KEY_FASTFORWARD }, + { 0x800f041b, KEY_PREVIOUS }, + { 0x800f041a, KEY_NEXT }, + + { 0x800f0416, KEY_PLAY }, + { 0x800f0418, KEY_PAUSE }, + { 0x800f0419, KEY_STOP }, + { 0x800f0417, KEY_RECORD }, + + { 0x800f041e, KEY_UP }, + { 0x800f041f, KEY_DOWN }, + { 0x800f0420, KEY_LEFT }, + { 0x800f0421, KEY_RIGHT }, + + { 0x800f040b, KEY_ENTER }, + { 0x800f0422, KEY_OK }, + { 0x800f0423, KEY_EXIT }, + { 0x800f040a, KEY_DELETE }, + + { 0x800f040e, KEY_MUTE }, + { 0x800f0410, KEY_VOLUMEUP }, + { 0x800f0411, KEY_VOLUMEDOWN }, + { 0x800f0412, KEY_CHANNELUP }, + { 0x800f0413, KEY_CHANNELDOWN }, + + { 0x800f0401, KEY_NUMERIC_1 }, + { 0x800f0402, KEY_NUMERIC_2 }, + { 0x800f0403, KEY_NUMERIC_3 }, + { 0x800f0404, KEY_NUMERIC_4 }, + { 0x800f0405, KEY_NUMERIC_5 }, + { 0x800f0406, KEY_NUMERIC_6 }, + { 0x800f0407, KEY_NUMERIC_7 }, + { 0x800f0408, KEY_NUMERIC_8 }, + { 0x800f0409, KEY_NUMERIC_9 }, + { 0x800f0400, KEY_NUMERIC_0 }, + + { 0x800f041d, KEY_NUMERIC_STAR }, + { 0x800f041c, KEY_NUMERIC_POUND }, + + { 0x800f0446, KEY_TV }, + { 0x800f0447, KEY_AUDIO }, /* My Music */ + { 0x800f0448, KEY_PVR }, /* RecordedTV */ + { 0x800f0449, KEY_CAMERA }, + { 0x800f044a, KEY_VIDEO }, + { 0x800f0424, KEY_DVD }, + { 0x800f0425, KEY_TUNER }, /* LiveTV */ + { 0x800f0450, KEY_RADIO }, + + { 0x800f044c, KEY_LANGUAGE }, + { 0x800f0427, KEY_ZOOM }, /* Aspect */ + + { 0x800f045b, KEY_RED }, + { 0x800f045c, KEY_GREEN }, + { 0x800f045d, KEY_YELLOW }, + { 0x800f045e, KEY_BLUE }, + + { 0x800f040f, KEY_INFO }, + { 0x800f0426, KEY_EPG }, /* Guide */ + { 0x800f045a, KEY_SUBTITLE }, /* Caption/Teletext */ + { 0x800f044d, KEY_TITLE }, + + { 0x800f040c, KEY_POWER }, + { 0x800f040d, KEY_PROG1 }, /* Windows MCE button */ + +}; + +static struct rc_keymap rc6_mce_map = { + .map = { + .scan = rc6_mce, + .size = ARRAY_SIZE(rc6_mce), + .ir_type = IR_TYPE_RC6, + .name = RC_MAP_RC6_MCE, + } +}; + +static int __init init_rc_map_rc6_mce(void) +{ + return ir_register_map(&rc6_mce_map); +} + +static void __exit exit_rc_map_rc6_mce(void) +{ + ir_unregister_map(&rc6_mce_map); +} + +module_init(init_rc_map_rc6_mce) +module_exit(exit_rc_map_rc6_mce) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>"); diff --git a/drivers/media/IR/lirc_dev.c b/drivers/media/IR/lirc_dev.c new file mode 100644 index 0000000..899891b --- /dev/null +++ b/drivers/media/IR/lirc_dev.c @@ -0,0 +1,764 @@ +/* + * LIRC base driver + * + * by Artur Lipowski <alipowski@interia.pl> + * + * 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/errno.h> +#include <linux/ioctl.h> +#include <linux/fs.h> +#include <linux/poll.h> +#include <linux/completion.h> +#include <linux/errno.h> +#include <linux/mutex.h> +#include <linux/wait.h> +#include <linux/unistd.h> +#include <linux/kthread.h> +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/cdev.h> + +#include <media/lirc.h> +#include <media/lirc_dev.h> + +static int debug; + +#define IRCTL_DEV_NAME "BaseRemoteCtl" +#define NOPLUG -1 +#define LOGHEAD "lirc_dev (%s[%d]): " + +static dev_t lirc_base_dev; + +struct irctl { + struct lirc_driver d; + int attached; + int open; + + struct mutex irctl_lock; + struct lirc_buffer *buf; + unsigned int chunk_size; + + struct task_struct *task; + long jiffies_to_wait; + + struct cdev cdev; +}; + +static DEFINE_MUTEX(lirc_dev_lock); + +static struct irctl *irctls[MAX_IRCTL_DEVICES]; + +/* Only used for sysfs but defined to void otherwise */ +static struct class *lirc_class; + +/* helper function + * initializes the irctl structure + */ +static void init_irctl(struct irctl *ir) +{ + dev_dbg(ir->d.dev, LOGHEAD "initializing irctl\n", + ir->d.name, ir->d.minor); + mutex_init(&ir->irctl_lock); + ir->d.minor = NOPLUG; +} + +static void cleanup(struct irctl *ir) +{ + dev_dbg(ir->d.dev, LOGHEAD "cleaning up\n", ir->d.name, ir->d.minor); + + device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor)); + + if (ir->buf != ir->d.rbuf) { + lirc_buffer_free(ir->buf); + kfree(ir->buf); + } + ir->buf = NULL; +} + +/* helper function + * reads key codes from driver and puts them into buffer + * returns 0 on success + */ +static int add_to_buf(struct irctl *ir) +{ + if (ir->d.add_to_buf) { + int res = -ENODATA; + int got_data = 0; + + /* + * service the device as long as it is returning + * data and we have space + */ +get_data: + res = ir->d.add_to_buf(ir->d.data, ir->buf); + if (res == 0) { + got_data++; + goto get_data; + } + + if (res == -ENODEV) + kthread_stop(ir->task); + + return got_data ? 0 : res; + } + + return 0; +} + +/* main function of the polling thread + */ +static int lirc_thread(void *irctl) +{ + struct irctl *ir = irctl; + + dev_dbg(ir->d.dev, LOGHEAD "poll thread started\n", + ir->d.name, ir->d.minor); + + do { + if (ir->open) { + if (ir->jiffies_to_wait) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(ir->jiffies_to_wait); + } + if (kthread_should_stop()) + break; + if (!add_to_buf(ir)) + wake_up_interruptible(&ir->buf->wait_poll); + } else { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } + } while (!kthread_should_stop()); + + dev_dbg(ir->d.dev, LOGHEAD "poll thread ended\n", + ir->d.name, ir->d.minor); + + return 0; +} + + +static struct file_operations fops = { + .owner = THIS_MODULE, + .read = lirc_dev_fop_read, + .write = lirc_dev_fop_write, + .poll = lirc_dev_fop_poll, + .unlocked_ioctl = lirc_dev_fop_ioctl, + .open = lirc_dev_fop_open, + .release = lirc_dev_fop_close, +}; + +static int lirc_cdev_add(struct irctl *ir) +{ + int retval; + struct lirc_driver *d = &ir->d; + + if (d->fops) { + cdev_init(&ir->cdev, d->fops); + ir->cdev.owner = d->owner; + } else { + cdev_init(&ir->cdev, &fops); + ir->cdev.owner = THIS_MODULE; + } + kobject_set_name(&ir->cdev.kobj, "lirc%d", d->minor); + + retval = cdev_add(&ir->cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1); + if (retval) + kobject_put(&ir->cdev.kobj); + + return retval; +} + +int lirc_register_driver(struct lirc_driver *d) +{ + struct irctl *ir; + int minor; + int bytes_in_key; + unsigned int chunk_size; + unsigned int buffer_size; + int err; + + if (!d) { + printk(KERN_ERR "lirc_dev: lirc_register_driver: " + "driver pointer must be not NULL!\n"); + err = -EBADRQC; + goto out; + } + + if (MAX_IRCTL_DEVICES <= d->minor) { + dev_err(d->dev, "lirc_dev: lirc_register_driver: " + "\"minor\" must be between 0 and %d (%d)!\n", + MAX_IRCTL_DEVICES-1, d->minor); + err = -EBADRQC; + goto out; + } + + if (1 > d->code_length || (BUFLEN * 8) < d->code_length) { + dev_err(d->dev, "lirc_dev: lirc_register_driver: " + "code length in bits for minor (%d) " + "must be less than %d!\n", + d->minor, BUFLEN * 8); + err = -EBADRQC; + goto out; + } + + dev_dbg(d->dev, "lirc_dev: lirc_register_driver: sample_rate: %d\n", + d->sample_rate); + if (d->sample_rate) { + if (2 > d->sample_rate || HZ < d->sample_rate) { + dev_err(d->dev, "lirc_dev: lirc_register_driver: " + "sample_rate must be between 2 and %d!\n", HZ); + err = -EBADRQC; + goto out; + } + if (!d->add_to_buf) { + dev_err(d->dev, "lirc_dev: lirc_register_driver: " + "add_to_buf cannot be NULL when " + "sample_rate is set\n"); + err = -EBADRQC; + goto out; + } + } else if (!(d->fops && d->fops->read) && !d->rbuf) { + dev_err(d->dev, "lirc_dev: lirc_register_driver: " + "fops->read and rbuf cannot all be NULL!\n"); + err = -EBADRQC; + goto out; + } else if (!d->rbuf) { + if (!(d->fops && d->fops->read && d->fops->poll && + d->fops->unlocked_ioctl)) { + dev_err(d->dev, "lirc_dev: lirc_register_driver: " + "neither read, poll nor unlocked_ioctl can be NULL!\n"); + err = -EBADRQC; + goto out; + } + } + + mutex_lock(&lirc_dev_lock); + + minor = d->minor; + + if (minor < 0) { + /* find first free slot for driver */ + for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++) + if (!irctls[minor]) + break; + if (MAX_IRCTL_DEVICES == minor) { + dev_err(d->dev, "lirc_dev: lirc_register_driver: " + "no free slots for drivers!\n"); + err = -ENOMEM; + goto out_lock; + } + } else if (irctls[minor]) { + dev_err(d->dev, "lirc_dev: lirc_register_driver: " + "minor (%d) just registered!\n", minor); + err = -EBUSY; + goto out_lock; + } + + ir = kzalloc(sizeof(struct irctl), GFP_KERNEL); + if (!ir) { + err = -ENOMEM; + goto out_lock; + } + init_irctl(ir); + irctls[minor] = ir; + d->minor = minor; + + if (d->sample_rate) { + ir->jiffies_to_wait = HZ / d->sample_rate; + } else { + /* it means - wait for external event in task queue */ + ir->jiffies_to_wait = 0; + } + + /* some safety check 8-) */ + d->name[sizeof(d->name)-1] = '\0'; + + bytes_in_key = BITS_TO_LONGS(d->code_length) + + (d->code_length % 8 ? 1 : 0); + buffer_size = d->buffer_size ? d->buffer_size : BUFLEN / bytes_in_key; + chunk_size = d->chunk_size ? d->chunk_size : bytes_in_key; + + if (d->rbuf) { + ir->buf = d->rbuf; + } else { + ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); + if (!ir->buf) { + err = -ENOMEM; + goto out_lock; + } + err = lirc_buffer_init(ir->buf, chunk_size, buffer_size); + if (err) { + kfree(ir->buf); + goto out_lock; + } + } + ir->chunk_size = ir->buf->chunk_size; + + if (d->features == 0) + d->features = LIRC_CAN_REC_LIRCCODE; + + ir->d = *d; + ir->d.minor = minor; + + device_create(lirc_class, ir->d.dev, + MKDEV(MAJOR(lirc_base_dev), ir->d.minor), NULL, + "lirc%u", ir->d.minor); + + if (d->sample_rate) { + /* try to fire up polling thread */ + ir->task = kthread_run(lirc_thread, (void *)ir, "lirc_dev"); + if (IS_ERR(ir->task)) { + dev_err(d->dev, "lirc_dev: lirc_register_driver: " + "cannot run poll thread for minor = %d\n", + d->minor); + err = -ECHILD; + goto out_sysfs; + } + } + + err = lirc_cdev_add(ir); + if (err) + goto out_sysfs; + + ir->attached = 1; + mutex_unlock(&lirc_dev_lock); + + dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n", + ir->d.name, ir->d.minor); + return minor; + +out_sysfs: + device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor)); +out_lock: + mutex_unlock(&lirc_dev_lock); +out: + return err; +} +EXPORT_SYMBOL(lirc_register_driver); + +int lirc_unregister_driver(int minor) +{ + struct irctl *ir; + + if (minor < 0 || minor >= MAX_IRCTL_DEVICES) { + printk(KERN_ERR "lirc_dev: lirc_unregister_driver: " + "\"minor (%d)\" must be between 0 and %d!\n", + minor, MAX_IRCTL_DEVICES-1); + return -EBADRQC; + } + + ir = irctls[minor]; + + mutex_lock(&lirc_dev_lock); + + if (ir->d.minor != minor) { + printk(KERN_ERR "lirc_dev: lirc_unregister_driver: " + "minor (%d) device not registered!", minor); + mutex_unlock(&lirc_dev_lock); + return -ENOENT; + } + + /* end up polling thread */ + if (ir->task) + kthread_stop(ir->task); + + dev_dbg(ir->d.dev, "lirc_dev: driver %s unregistered from minor = %d\n", + ir->d.name, ir->d.minor); + + ir->attached = 0; + if (ir->open) { + dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n", + ir->d.name, ir->d.minor); + wake_up_interruptible(&ir->buf->wait_poll); + mutex_lock(&ir->irctl_lock); + ir->d.set_use_dec(ir->d.data); + module_put(ir->d.owner); + mutex_unlock(&ir->irctl_lock); + cdev_del(&ir->cdev); + } else { + cleanup(ir); + cdev_del(&ir->cdev); + kfree(ir); + irctls[minor] = NULL; + } + + mutex_unlock(&lirc_dev_lock); + + return 0; +} +EXPORT_SYMBOL(lirc_unregister_driver); + +int lirc_dev_fop_open(struct inode *inode, struct file *file) +{ + struct irctl *ir; + int retval = 0; + + if (iminor(inode) >= MAX_IRCTL_DEVICES) { + printk(KERN_WARNING "lirc_dev [%d]: open result = -ENODEV\n", + iminor(inode)); + return -ENODEV; + } + + if (mutex_lock_interruptible(&lirc_dev_lock)) + return -ERESTARTSYS; + + ir = irctls[iminor(inode)]; + if (!ir) { + retval = -ENODEV; + goto error; + } + file->private_data = ir; + + dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor); + + if (ir->d.minor == NOPLUG) { + retval = -ENODEV; + goto error; + } + + if (ir->open) { + retval = -EBUSY; + goto error; + } + + if (try_module_get(ir->d.owner)) { + ++ir->open; + retval = ir->d.set_use_inc(ir->d.data); + + if (retval) { + module_put(ir->d.owner); + --ir->open; + } else { + lirc_buffer_clear(ir->buf); + } + if (ir->task) + wake_up_process(ir->task); + } + +error: + if (ir) + dev_dbg(ir->d.dev, LOGHEAD "open result = %d\n", + ir->d.name, ir->d.minor, retval); + + mutex_unlock(&lirc_dev_lock); + + return retval; +} +EXPORT_SYMBOL(lirc_dev_fop_open); + +int lirc_dev_fop_close(struct inode *inode, struct file *file) +{ + struct irctl *ir = irctls[iminor(inode)]; + + dev_dbg(ir->d.dev, LOGHEAD "close called\n", ir->d.name, ir->d.minor); + + WARN_ON(mutex_lock_killable(&lirc_dev_lock)); + + --ir->open; + if (ir->attached) { + ir->d.set_use_dec(ir->d.data); + module_put(ir->d.owner); + } else { + cleanup(ir); + irctls[ir->d.minor] = NULL; + kfree(ir); + } + + mutex_unlock(&lirc_dev_lock); + + return 0; +} +EXPORT_SYMBOL(lirc_dev_fop_close); + +unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait) +{ + struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)]; + unsigned int ret; + + dev_dbg(ir->d.dev, LOGHEAD "poll called\n", ir->d.name, ir->d.minor); + + if (!ir->attached) { + mutex_unlock(&ir->irctl_lock); + return POLLERR; + } + + poll_wait(file, &ir->buf->wait_poll, wait); + + if (ir->buf) + if (lirc_buffer_empty(ir->buf)) + ret = 0; + else + ret = POLLIN | POLLRDNORM; + else + ret = POLLERR; + + dev_dbg(ir->d.dev, LOGHEAD "poll result = %d\n", + ir->d.name, ir->d.minor, ret); + + return ret; +} +EXPORT_SYMBOL(lirc_dev_fop_poll); + +long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + unsigned long mode; + int result = 0; + struct irctl *ir = file->private_data; + + dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n", + ir->d.name, ir->d.minor, cmd); + + if (ir->d.minor == NOPLUG || !ir->attached) { + dev_dbg(ir->d.dev, LOGHEAD "ioctl result = -ENODEV\n", + ir->d.name, ir->d.minor); + return -ENODEV; + } + + mutex_lock(&ir->irctl_lock); + + switch (cmd) { + case LIRC_GET_FEATURES: + result = put_user(ir->d.features, (unsigned long *)arg); + break; + case LIRC_GET_REC_MODE: + if (!(ir->d.features & LIRC_CAN_REC_MASK)) { + result = -ENOSYS; + break; + } + + result = put_user(LIRC_REC2MODE + (ir->d.features & LIRC_CAN_REC_MASK), + (unsigned long *)arg); + break; + case LIRC_SET_REC_MODE: + if (!(ir->d.features & LIRC_CAN_REC_MASK)) { + result = -ENOSYS; + break; + } + + result = get_user(mode, (unsigned long *)arg); + if (!result && !(LIRC_MODE2REC(mode) & ir->d.features)) + result = -EINVAL; + /* + * FIXME: We should actually set the mode somehow but + * for now, lirc_serial doesn't support mode changing either + */ + break; + case LIRC_GET_LENGTH: + result = put_user(ir->d.code_length, (unsigned long *)arg); + break; + case LIRC_GET_MIN_TIMEOUT: + if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || + ir->d.min_timeout == 0) { + result = -ENOSYS; + break; + } + + result = put_user(ir->d.min_timeout, (unsigned long *)arg); + break; + case LIRC_GET_MAX_TIMEOUT: + if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || + ir->d.max_timeout == 0) { + result = -ENOSYS; + break; + } + + result = put_user(ir->d.max_timeout, (unsigned long *)arg); + break; + default: + result = -EINVAL; + } + + dev_dbg(ir->d.dev, LOGHEAD "ioctl result = %d\n", + ir->d.name, ir->d.minor, result); + + mutex_unlock(&ir->irctl_lock); + + return result; +} +EXPORT_SYMBOL(lirc_dev_fop_ioctl); + +ssize_t lirc_dev_fop_read(struct file *file, + char *buffer, + size_t length, + loff_t *ppos) +{ + struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)]; + unsigned char buf[ir->chunk_size]; + int ret = 0, written = 0; + DECLARE_WAITQUEUE(wait, current); + + dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor); + + if (mutex_lock_interruptible(&ir->irctl_lock)) + return -ERESTARTSYS; + if (!ir->attached) { + mutex_unlock(&ir->irctl_lock); + return -ENODEV; + } + + if (length % ir->chunk_size) { + dev_dbg(ir->d.dev, LOGHEAD "read result = -EINVAL\n", + ir->d.name, ir->d.minor); + mutex_unlock(&ir->irctl_lock); + return -EINVAL; + } + + /* + * we add ourselves to the task queue before buffer check + * to avoid losing scan code (in case when queue is awaken somewhere + * between while condition checking and scheduling) + */ + add_wait_queue(&ir->buf->wait_poll, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + /* + * while we didn't provide 'length' bytes, device is opened in blocking + * mode and 'copy_to_user' is happy, wait for data. + */ + while (written < length && ret == 0) { + if (lirc_buffer_empty(ir->buf)) { + /* According to the read(2) man page, 'written' can be + * returned as less than 'length', instead of blocking + * again, returning -EWOULDBLOCK, or returning + * -ERESTARTSYS */ + if (written) + break; + if (file->f_flags & O_NONBLOCK) { + ret = -EWOULDBLOCK; + break; + } + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + + mutex_unlock(&ir->irctl_lock); + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + + if (mutex_lock_interruptible(&ir->irctl_lock)) { + ret = -ERESTARTSYS; + remove_wait_queue(&ir->buf->wait_poll, &wait); + set_current_state(TASK_RUNNING); + goto out_unlocked; + } + + if (!ir->attached) { + ret = -ENODEV; + break; + } + } else { + lirc_buffer_read(ir->buf, buf); + ret = copy_to_user((void *)buffer+written, buf, + ir->buf->chunk_size); + written += ir->buf->chunk_size; + } + } + + remove_wait_queue(&ir->buf->wait_poll, &wait); + set_current_state(TASK_RUNNING); + mutex_unlock(&ir->irctl_lock); + +out_unlocked: + dev_dbg(ir->d.dev, LOGHEAD "read result = %s (%d)\n", + ir->d.name, ir->d.minor, ret ? "-EFAULT" : "OK", ret); + + return ret ? ret : written; +} +EXPORT_SYMBOL(lirc_dev_fop_read); + +void *lirc_get_pdata(struct file *file) +{ + void *data = NULL; + + if (file && file->f_dentry && file->f_dentry->d_inode && + file->f_dentry->d_inode->i_rdev) { + struct irctl *ir; + ir = irctls[iminor(file->f_dentry->d_inode)]; + data = ir->d.data; + } + + return data; +} +EXPORT_SYMBOL(lirc_get_pdata); + + +ssize_t lirc_dev_fop_write(struct file *file, const char *buffer, + size_t length, loff_t *ppos) +{ + struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)]; + + dev_dbg(ir->d.dev, LOGHEAD "write called\n", ir->d.name, ir->d.minor); + + if (!ir->attached) + return -ENODEV; + + return -EINVAL; +} +EXPORT_SYMBOL(lirc_dev_fop_write); + + +static int __init lirc_dev_init(void) +{ + int retval; + + lirc_class = class_create(THIS_MODULE, "lirc"); + if (IS_ERR(lirc_class)) { + retval = PTR_ERR(lirc_class); + printk(KERN_ERR "lirc_dev: class_create failed\n"); + goto error; + } + + retval = alloc_chrdev_region(&lirc_base_dev, 0, MAX_IRCTL_DEVICES, + IRCTL_DEV_NAME); + if (retval) { + class_destroy(lirc_class); + printk(KERN_ERR "lirc_dev: alloc_chrdev_region failed\n"); + goto error; + } + + + printk(KERN_INFO "lirc_dev: IR Remote Control driver registered, " + "major %d \n", MAJOR(lirc_base_dev)); + +error: + return retval; +} + + + +static void __exit lirc_dev_exit(void) +{ + class_destroy(lirc_class); + unregister_chrdev_region(lirc_base_dev, MAX_IRCTL_DEVICES); + printk(KERN_INFO "lirc_dev: module unloaded\n"); +} + +module_init(lirc_dev_init); +module_exit(lirc_dev_exit); + +MODULE_DESCRIPTION("LIRC base driver module"); +MODULE_AUTHOR("Artur Lipowski"); +MODULE_LICENSE("GPL"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Enable debugging messages"); diff --git a/drivers/media/IR/mceusb.c b/drivers/media/IR/mceusb.c new file mode 100644 index 0000000..78bf7f7 --- /dev/null +++ b/drivers/media/IR/mceusb.c @@ -0,0 +1,1143 @@ +/* + * Driver for USB Windows Media Center Ed. eHome Infrared Transceivers + * + * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.com> + * + * Based on the original lirc_mceusb and lirc_mceusb2 drivers, by Dan + * Conti, Martin Blatter and Daniel Melander, the latter of which was + * in turn also based on the lirc_atiusb driver by Paul Miller. The + * two mce drivers were merged into one by Jarod Wilson, with transmit + * support for the 1st-gen device added primarily by Patrick Calhoun, + * with a bit of tweaks by Jarod. Debugging improvements and proper + * support for what appears to be 3rd-gen hardware added by Jarod. + * Initial port from lirc driver to ir-core drivery by Jarod, based + * partially on a port to an earlier proposed IR infrastructure by + * Jon Smirl, which included enhancements and simplifications to the + * incoming IR buffer parsing routines. + * + * + * 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/device.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/input.h> +#include <media/ir-core.h> +#include <media/ir-common.h> + +#define DRIVER_VERSION "1.91" +#define DRIVER_AUTHOR "Jarod Wilson <jarod@wilsonet.com>" +#define DRIVER_DESC "Windows Media Center Ed. eHome Infrared Transceiver " \ + "device driver" +#define DRIVER_NAME "mceusb" + +#define USB_BUFLEN 32 /* USB reception buffer length */ +#define USB_CTRL_MSG_SZ 2 /* Size of usb ctrl msg on gen1 hw */ +#define MCE_G1_INIT_MSGS 40 /* Init messages on gen1 hw to throw out */ + +/* MCE constants */ +#define MCE_CMDBUF_SIZE 384 /* MCE Command buffer length */ +#define MCE_TIME_UNIT 50 /* Approx 50us resolution */ +#define MCE_CODE_LENGTH 5 /* Normal length of packet (with header) */ +#define MCE_PACKET_SIZE 4 /* Normal length of packet (without header) */ +#define MCE_PACKET_HEADER 0x84 /* Actual header format is 0x80 + num_bytes */ +#define MCE_CONTROL_HEADER 0x9F /* MCE status header */ +#define MCE_TX_HEADER_LENGTH 3 /* # of bytes in the initializing tx header */ +#define MCE_MAX_CHANNELS 2 /* Two transmitters, hardware dependent? */ +#define MCE_DEFAULT_TX_MASK 0x03 /* Val opts: TX1=0x01, TX2=0x02, ALL=0x03 */ +#define MCE_PULSE_BIT 0x80 /* Pulse bit, MSB set == PULSE else SPACE */ +#define MCE_PULSE_MASK 0x7F /* Pulse mask */ +#define MCE_MAX_PULSE_LENGTH 0x7F /* Longest transmittable pulse symbol */ +#define MCE_PACKET_LENGTH_MASK 0x1F /* Packet length mask */ + + +/* module parameters */ +#ifdef CONFIG_USB_DEBUG +static int debug = 1; +#else +static int debug; +#endif + +/* general constants */ +#define SEND_FLAG_IN_PROGRESS 1 +#define SEND_FLAG_COMPLETE 2 +#define RECV_FLAG_IN_PROGRESS 3 +#define RECV_FLAG_COMPLETE 4 + +#define MCEUSB_RX 1 +#define MCEUSB_TX 2 + +#define VENDOR_PHILIPS 0x0471 +#define VENDOR_SMK 0x0609 +#define VENDOR_TATUNG 0x1460 +#define VENDOR_GATEWAY 0x107b +#define VENDOR_SHUTTLE 0x1308 +#define VENDOR_SHUTTLE2 0x051c +#define VENDOR_MITSUMI 0x03ee +#define VENDOR_TOPSEED 0x1784 +#define VENDOR_RICAVISION 0x179d +#define VENDOR_ITRON 0x195d +#define VENDOR_FIC 0x1509 +#define VENDOR_LG 0x043e +#define VENDOR_MICROSOFT 0x045e +#define VENDOR_FORMOSA 0x147a +#define VENDOR_FINTEK 0x1934 +#define VENDOR_PINNACLE 0x2304 +#define VENDOR_ECS 0x1019 +#define VENDOR_WISTRON 0x0fb8 +#define VENDOR_COMPRO 0x185b +#define VENDOR_NORTHSTAR 0x04eb +#define VENDOR_REALTEK 0x0bda +#define VENDOR_TIVO 0x105a + +static struct usb_device_id mceusb_dev_table[] = { + /* Original Microsoft MCE IR Transceiver (often HP-branded) */ + { USB_DEVICE(VENDOR_MICROSOFT, 0x006d) }, + /* Philips Infrared Transceiver - Sahara branded */ + { USB_DEVICE(VENDOR_PHILIPS, 0x0608) }, + /* Philips Infrared Transceiver - HP branded */ + { USB_DEVICE(VENDOR_PHILIPS, 0x060c) }, + /* Philips SRM5100 */ + { USB_DEVICE(VENDOR_PHILIPS, 0x060d) }, + /* Philips Infrared Transceiver - Omaura */ + { USB_DEVICE(VENDOR_PHILIPS, 0x060f) }, + /* Philips Infrared Transceiver - Spinel plus */ + { USB_DEVICE(VENDOR_PHILIPS, 0x0613) }, + /* Philips eHome Infrared Transceiver */ + { USB_DEVICE(VENDOR_PHILIPS, 0x0815) }, + /* Realtek MCE IR Receiver */ + { USB_DEVICE(VENDOR_REALTEK, 0x0161) }, + /* SMK/Toshiba G83C0004D410 */ + { USB_DEVICE(VENDOR_SMK, 0x031d) }, + /* SMK eHome Infrared Transceiver (Sony VAIO) */ + { USB_DEVICE(VENDOR_SMK, 0x0322) }, + /* bundled with Hauppauge PVR-150 */ + { USB_DEVICE(VENDOR_SMK, 0x0334) }, + /* SMK eHome Infrared Transceiver */ + { USB_DEVICE(VENDOR_SMK, 0x0338) }, + /* Tatung eHome Infrared Transceiver */ + { USB_DEVICE(VENDOR_TATUNG, 0x9150) }, + /* Shuttle eHome Infrared Transceiver */ + { USB_DEVICE(VENDOR_SHUTTLE, 0xc001) }, + /* Shuttle eHome Infrared Transceiver */ + { USB_DEVICE(VENDOR_SHUTTLE2, 0xc001) }, + /* Gateway eHome Infrared Transceiver */ + { USB_DEVICE(VENDOR_GATEWAY, 0x3009) }, + /* Mitsumi */ + { USB_DEVICE(VENDOR_MITSUMI, 0x2501) }, + /* Topseed eHome Infrared Transceiver */ + { USB_DEVICE(VENDOR_TOPSEED, 0x0001) }, + /* Topseed HP eHome Infrared Transceiver */ + { USB_DEVICE(VENDOR_TOPSEED, 0x0006) }, + /* Topseed eHome Infrared Transceiver */ + { USB_DEVICE(VENDOR_TOPSEED, 0x0007) }, + /* Topseed eHome Infrared Transceiver */ + { USB_DEVICE(VENDOR_TOPSEED, 0x0008) }, + /* Topseed eHome Infrared Transceiver */ + { USB_DEVICE(VENDOR_TOPSEED, 0x000a) }, + /* Topseed eHome Infrared Transceiver */ + { USB_DEVICE(VENDOR_TOPSEED, 0x0011) }, + /* Ricavision internal Infrared Transceiver */ + { USB_DEVICE(VENDOR_RICAVISION, 0x0010) }, + /* Itron ione Libra Q-11 */ + { USB_DEVICE(VENDOR_ITRON, 0x7002) }, + /* FIC eHome Infrared Transceiver */ + { USB_DEVICE(VENDOR_FIC, 0x9242) }, + /* LG eHome Infrared Transceiver */ + { USB_DEVICE(VENDOR_LG, 0x9803) }, + /* Microsoft MCE Infrared Transceiver */ + { USB_DEVICE(VENDOR_MICROSOFT, 0x00a0) }, + /* Formosa eHome Infrared Transceiver */ + { USB_DEVICE(VENDOR_FORMOSA, 0xe015) }, + /* Formosa21 / eHome Infrared Receiver */ + { USB_DEVICE(VENDOR_FORMOSA, 0xe016) }, + /* Formosa aim / Trust MCE Infrared Receiver */ + { USB_DEVICE(VENDOR_FORMOSA, 0xe017) }, + /* Formosa Industrial Computing / Beanbag Emulation Device */ + { USB_DEVICE(VENDOR_FORMOSA, 0xe018) }, + /* Formosa21 / eHome Infrared Receiver */ + { USB_DEVICE(VENDOR_FORMOSA, 0xe03a) }, + /* Formosa Industrial Computing AIM IR605/A */ + { USB_DEVICE(VENDOR_FORMOSA, 0xe03c) }, + /* Formosa Industrial Computing */ + { USB_DEVICE(VENDOR_FORMOSA, 0xe03e) }, + /* Fintek eHome Infrared Transceiver */ + { USB_DEVICE(VENDOR_FINTEK, 0x0602) }, + /* Fintek eHome Infrared Transceiver (in the AOpen MP45) */ + { USB_DEVICE(VENDOR_FINTEK, 0x0702) }, + /* Pinnacle Remote Kit */ + { USB_DEVICE(VENDOR_PINNACLE, 0x0225) }, + /* Elitegroup Computer Systems IR */ + { USB_DEVICE(VENDOR_ECS, 0x0f38) }, + /* Wistron Corp. eHome Infrared Receiver */ + { USB_DEVICE(VENDOR_WISTRON, 0x0002) }, + /* Compro K100 */ + { USB_DEVICE(VENDOR_COMPRO, 0x3020) }, + /* Compro K100 v2 */ + { USB_DEVICE(VENDOR_COMPRO, 0x3082) }, + /* Northstar Systems, Inc. eHome Infrared Transceiver */ + { USB_DEVICE(VENDOR_NORTHSTAR, 0xe004) }, + /* TiVo PC IR Receiver */ + { USB_DEVICE(VENDOR_TIVO, 0x2000) }, + /* Terminating entry */ + { } +}; + +static struct usb_device_id gen3_list[] = { + { USB_DEVICE(VENDOR_PINNACLE, 0x0225) }, + { USB_DEVICE(VENDOR_TOPSEED, 0x0008) }, + {} +}; + +static struct usb_device_id microsoft_gen1_list[] = { + { USB_DEVICE(VENDOR_MICROSOFT, 0x006d) }, + {} +}; + +static struct usb_device_id std_tx_mask_list[] = { + { USB_DEVICE(VENDOR_MICROSOFT, 0x006d) }, + { USB_DEVICE(VENDOR_PHILIPS, 0x060c) }, + { USB_DEVICE(VENDOR_SMK, 0x031d) }, + { USB_DEVICE(VENDOR_SMK, 0x0322) }, + { USB_DEVICE(VENDOR_SMK, 0x0334) }, + { USB_DEVICE(VENDOR_TOPSEED, 0x0001) }, + { USB_DEVICE(VENDOR_TOPSEED, 0x0006) }, + { USB_DEVICE(VENDOR_TOPSEED, 0x0007) }, + { USB_DEVICE(VENDOR_TOPSEED, 0x0008) }, + { USB_DEVICE(VENDOR_TOPSEED, 0x000a) }, + { USB_DEVICE(VENDOR_TOPSEED, 0x0011) }, + { USB_DEVICE(VENDOR_PINNACLE, 0x0225) }, + {} +}; + +/* data structure for each usb transceiver */ +struct mceusb_dev { + /* ir-core bits */ + struct ir_input_dev *irdev; + struct ir_dev_props *props; + struct ir_raw_event rawir; + + /* core device bits */ + struct device *dev; + struct input_dev *idev; + + /* usb */ + struct usb_device *usbdev; + struct urb *urb_in; + struct usb_endpoint_descriptor *usb_ep_in; + struct usb_endpoint_descriptor *usb_ep_out; + + /* buffers and dma */ + unsigned char *buf_in; + unsigned int len_in; + u8 cmd; /* MCE command type */ + u8 rem; /* Remaining IR data bytes in packet */ + dma_addr_t dma_in; + dma_addr_t dma_out; + + struct { + u32 connected:1; + u32 tx_mask_inverted:1; + u32 microsoft_gen1:1; + u32 reserved:29; + } flags; + + /* transmit support */ + int send_flags; + u32 carrier; + unsigned char tx_mask; + + char name[128]; + char phys[64]; +}; + +/* + * MCE Device Command Strings + * Device command responses vary from device to device... + * - DEVICE_RESET resets the hardware to its default state + * - GET_REVISION fetches the hardware/software revision, common + * replies are ff 0b 45 ff 1b 08 and ff 0b 50 ff 1b 42 + * - GET_CARRIER_FREQ gets the carrier mode and frequency of the + * device, with replies in the form of 9f 06 MM FF, where MM is 0-3, + * meaning clk of 10000000, 2500000, 625000 or 156250, and FF is + * ((clk / frequency) - 1) + * - GET_RX_TIMEOUT fetches the receiver timeout in units of 50us, + * response in the form of 9f 0c msb lsb + * - GET_TX_BITMASK fetches the transmitter bitmask, replies in + * the form of 9f 08 bm, where bm is the bitmask + * - GET_RX_SENSOR fetches the RX sensor setting -- long-range + * general use one or short-range learning one, in the form of + * 9f 14 ss, where ss is either 01 for long-range or 02 for short + * - SET_CARRIER_FREQ sets a new carrier mode and frequency + * - SET_TX_BITMASK sets the transmitter bitmask + * - SET_RX_TIMEOUT sets the receiver timeout + * - SET_RX_SENSOR sets which receiver sensor to use + */ +static char DEVICE_RESET[] = {0x00, 0xff, 0xaa}; +static char GET_REVISION[] = {0xff, 0x0b}; +static char GET_UNKNOWN[] = {0xff, 0x18}; +static char GET_UNKNOWN2[] = {0x9f, 0x05}; +static char GET_CARRIER_FREQ[] = {0x9f, 0x07}; +static char GET_RX_TIMEOUT[] = {0x9f, 0x0d}; +static char GET_TX_BITMASK[] = {0x9f, 0x13}; +static char GET_RX_SENSOR[] = {0x9f, 0x15}; +/* sub in desired values in lower byte or bytes for full command */ +/* FIXME: make use of these for transmit. +static char SET_CARRIER_FREQ[] = {0x9f, 0x06, 0x00, 0x00}; +static char SET_TX_BITMASK[] = {0x9f, 0x08, 0x00}; +static char SET_RX_TIMEOUT[] = {0x9f, 0x0c, 0x00, 0x00}; +static char SET_RX_SENSOR[] = {0x9f, 0x14, 0x00}; +*/ + +static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf, + int len, bool out) +{ + char codes[USB_BUFLEN * 3 + 1]; + char inout[9]; + int i; + u8 cmd, subcmd, data1, data2; + struct device *dev = ir->dev; + int idx = 0; + + /* skip meaningless 0xb1 0x60 header bytes on orig receiver */ + if (ir->flags.microsoft_gen1 && !out) + idx = 2; + + if (len <= idx) + return; + + for (i = 0; i < len && i < USB_BUFLEN; i++) + snprintf(codes + i * 3, 4, "%02x ", buf[i] & 0xFF); + + dev_info(dev, "%sx data: %s (length=%d)\n", + (out ? "t" : "r"), codes, len); + + if (out) + strcpy(inout, "Request\0"); + else + strcpy(inout, "Got\0"); + + cmd = buf[idx] & 0xff; + subcmd = buf[idx + 1] & 0xff; + data1 = buf[idx + 2] & 0xff; + data2 = buf[idx + 3] & 0xff; + + switch (cmd) { + case 0x00: + if (subcmd == 0xff && data1 == 0xaa) + dev_info(dev, "Device reset requested\n"); + else + dev_info(dev, "Unknown command 0x%02x 0x%02x\n", + cmd, subcmd); + break; + case 0xff: + switch (subcmd) { + case 0x0b: + if (len == 2) + dev_info(dev, "Get hw/sw rev?\n"); + else + dev_info(dev, "hw/sw rev 0x%02x 0x%02x " + "0x%02x 0x%02x\n", data1, data2, + buf[idx + 4], buf[idx + 5]); + break; + case 0xaa: + dev_info(dev, "Device reset requested\n"); + break; + case 0xfe: + dev_info(dev, "Previous command not supported\n"); + break; + case 0x18: + case 0x1b: + default: + dev_info(dev, "Unknown command 0x%02x 0x%02x\n", + cmd, subcmd); + break; + } + break; + case 0x9f: + switch (subcmd) { + case 0x03: + dev_info(dev, "Ping\n"); + break; + case 0x04: + dev_info(dev, "Resp to 9f 05 of 0x%02x 0x%02x\n", + data1, data2); + break; + case 0x06: + dev_info(dev, "%s carrier mode and freq of " + "0x%02x 0x%02x\n", inout, data1, data2); + break; + case 0x07: + dev_info(dev, "Get carrier mode and freq\n"); + break; + case 0x08: + dev_info(dev, "%s transmit blaster mask of 0x%02x\n", + inout, data1); + break; + case 0x0c: + /* value is in units of 50us, so x*50/100 or x/2 ms */ + dev_info(dev, "%s receive timeout of %d ms\n", + inout, ((data1 << 8) | data2) / 2); + break; + case 0x0d: + dev_info(dev, "Get receive timeout\n"); + break; + case 0x13: + dev_info(dev, "Get transmit blaster mask\n"); + break; + case 0x14: + dev_info(dev, "%s %s-range receive sensor in use\n", + inout, data1 == 0x02 ? "short" : "long"); + break; + case 0x15: + if (len == 2) + dev_info(dev, "Get receive sensor\n"); + else + dev_info(dev, "Received pulse count is %d\n", + ((data1 << 8) | data2)); + break; + case 0xfe: + dev_info(dev, "Error! Hardware is likely wedged...\n"); + break; + case 0x05: + case 0x09: + case 0x0f: + default: + dev_info(dev, "Unknown command 0x%02x 0x%02x\n", + cmd, subcmd); + break; + } + break; + default: + break; + } +} + +static void usb_async_callback(struct urb *urb, struct pt_regs *regs) +{ + struct mceusb_dev *ir; + int len; + + if (!urb) + return; + + ir = urb->context; + if (ir) { + len = urb->actual_length; + + dev_dbg(ir->dev, "callback called (status=%d len=%d)\n", + urb->status, len); + + if (debug) + mceusb_dev_printdata(ir, urb->transfer_buffer, + len, true); + } + +} + +/* request incoming or send outgoing usb packet - used to initialize remote */ +static void mce_request_packet(struct mceusb_dev *ir, + struct usb_endpoint_descriptor *ep, + unsigned char *data, int size, int urb_type) +{ + int res; + struct urb *async_urb; + struct device *dev = ir->dev; + unsigned char *async_buf; + + if (urb_type == MCEUSB_TX) { + async_urb = usb_alloc_urb(0, GFP_KERNEL); + if (unlikely(!async_urb)) { + dev_err(dev, "Error, couldn't allocate urb!\n"); + return; + } + + async_buf = kzalloc(size, GFP_KERNEL); + if (!async_buf) { + dev_err(dev, "Error, couldn't allocate buf!\n"); + usb_free_urb(async_urb); + return; + } + + /* outbound data */ + usb_fill_int_urb(async_urb, ir->usbdev, + usb_sndintpipe(ir->usbdev, ep->bEndpointAddress), + async_buf, size, (usb_complete_t) usb_async_callback, + ir, ep->bInterval); + memcpy(async_buf, data, size); + + } else if (urb_type == MCEUSB_RX) { + /* standard request */ + async_urb = ir->urb_in; + ir->send_flags = RECV_FLAG_IN_PROGRESS; + + } else { + dev_err(dev, "Error! Unknown urb type %d\n", urb_type); + return; + } + + dev_dbg(dev, "receive request called (size=%#x)\n", size); + + async_urb->transfer_buffer_length = size; + async_urb->dev = ir->usbdev; + + res = usb_submit_urb(async_urb, GFP_ATOMIC); + if (res) { + dev_dbg(dev, "receive request FAILED! (res=%d)\n", res); + return; + } + dev_dbg(dev, "receive request complete (res=%d)\n", res); +} + +static void mce_async_out(struct mceusb_dev *ir, unsigned char *data, int size) +{ + mce_request_packet(ir, ir->usb_ep_out, data, size, MCEUSB_TX); +} + +static void mce_sync_in(struct mceusb_dev *ir, unsigned char *data, int size) +{ + mce_request_packet(ir, ir->usb_ep_in, data, size, MCEUSB_RX); +} + +/* Send data out the IR blaster port(s) */ +static int mceusb_tx_ir(void *priv, int *txbuf, u32 n) +{ + struct mceusb_dev *ir = priv; + int i, ret = 0; + int count, cmdcount = 0; + unsigned char *cmdbuf; /* MCE command buffer */ + long signal_duration = 0; /* Singnal length in us */ + struct timeval start_time, end_time; + + do_gettimeofday(&start_time); + + count = n / sizeof(int); + + cmdbuf = kzalloc(sizeof(int) * MCE_CMDBUF_SIZE, GFP_KERNEL); + if (!cmdbuf) + return -ENOMEM; + + /* MCE tx init header */ + cmdbuf[cmdcount++] = MCE_CONTROL_HEADER; + cmdbuf[cmdcount++] = 0x08; + cmdbuf[cmdcount++] = ir->tx_mask; + + /* Generate mce packet data */ + for (i = 0; (i < count) && (cmdcount < MCE_CMDBUF_SIZE); i++) { + signal_duration += txbuf[i]; + txbuf[i] = txbuf[i] / MCE_TIME_UNIT; + + do { /* loop to support long pulses/spaces > 127*50us=6.35ms */ + + /* Insert mce packet header every 4th entry */ + if ((cmdcount < MCE_CMDBUF_SIZE) && + (cmdcount - MCE_TX_HEADER_LENGTH) % + MCE_CODE_LENGTH == 0) + cmdbuf[cmdcount++] = MCE_PACKET_HEADER; + + /* Insert mce packet data */ + if (cmdcount < MCE_CMDBUF_SIZE) + cmdbuf[cmdcount++] = + (txbuf[i] < MCE_PULSE_BIT ? + txbuf[i] : MCE_MAX_PULSE_LENGTH) | + (i & 1 ? 0x00 : MCE_PULSE_BIT); + else { + ret = -EINVAL; + goto out; + } + + } while ((txbuf[i] > MCE_MAX_PULSE_LENGTH) && + (txbuf[i] -= MCE_MAX_PULSE_LENGTH)); + } + + /* Fix packet length in last header */ + cmdbuf[cmdcount - (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH] = + 0x80 + (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH - 1; + + /* Check if we have room for the empty packet at the end */ + if (cmdcount >= MCE_CMDBUF_SIZE) { + ret = -EINVAL; + goto out; + } + + /* All mce commands end with an empty packet (0x80) */ + cmdbuf[cmdcount++] = 0x80; + + /* Transmit the command to the mce device */ + mce_async_out(ir, cmdbuf, cmdcount); + + /* + * The lircd gap calculation expects the write function to + * wait the time it takes for the ircommand to be sent before + * it returns. + */ + do_gettimeofday(&end_time); + signal_duration -= (end_time.tv_usec - start_time.tv_usec) + + (end_time.tv_sec - start_time.tv_sec) * 1000000; + + /* delay with the closest number of ticks */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(usecs_to_jiffies(signal_duration)); + +out: + kfree(cmdbuf); + return ret ? ret : n; +} + +/* Sets active IR outputs -- mce devices typically (all?) have two */ +static int mceusb_set_tx_mask(void *priv, u32 mask) +{ + struct mceusb_dev *ir = priv; + + if (ir->flags.tx_mask_inverted) + ir->tx_mask = (mask != 0x03 ? mask ^ 0x03 : mask) << 1; + else + ir->tx_mask = mask; + + return 0; +} + +/* Sets the send carrier frequency and mode */ +static int mceusb_set_tx_carrier(void *priv, u32 carrier) +{ + struct mceusb_dev *ir = priv; + int clk = 10000000; + int prescaler = 0, divisor = 0; + unsigned char cmdbuf[4] = { 0x9f, 0x06, 0x00, 0x00 }; + + /* Carrier has changed */ + if (ir->carrier != carrier) { + + if (carrier == 0) { + ir->carrier = carrier; + cmdbuf[2] = 0x01; + cmdbuf[3] = 0x80; + dev_dbg(ir->dev, "%s: disabling carrier " + "modulation\n", __func__); + mce_async_out(ir, cmdbuf, sizeof(cmdbuf)); + return carrier; + } + + for (prescaler = 0; prescaler < 4; ++prescaler) { + divisor = (clk >> (2 * prescaler)) / carrier; + if (divisor <= 0xFF) { + ir->carrier = carrier; + cmdbuf[2] = prescaler; + cmdbuf[3] = divisor; + dev_dbg(ir->dev, "%s: requesting %u HZ " + "carrier\n", __func__, carrier); + + /* Transmit new carrier to mce device */ + mce_async_out(ir, cmdbuf, sizeof(cmdbuf)); + return carrier; + } + } + + return -EINVAL; + + } + + return carrier; +} + +static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) +{ + struct ir_raw_event rawir = { .pulse = false, .duration = 0 }; + int i, start_index = 0; + u8 hdr = MCE_CONTROL_HEADER; + + /* skip meaningless 0xb1 0x60 header bytes on orig receiver */ + if (ir->flags.microsoft_gen1) + start_index = 2; + + for (i = start_index; i < buf_len;) { + if (ir->rem == 0) { + /* decode mce packets of the form (84),AA,BB,CC,DD */ + /* IR data packets can span USB messages - rem */ + hdr = ir->buf_in[i]; + ir->rem = (hdr & MCE_PACKET_LENGTH_MASK); + ir->cmd = (hdr & ~MCE_PACKET_LENGTH_MASK); + dev_dbg(ir->dev, "New data. rem: 0x%02x, cmd: 0x%02x\n", + ir->rem, ir->cmd); + i++; + } + + /* don't process MCE commands */ + if (hdr == MCE_CONTROL_HEADER || hdr == 0xff) { + ir->rem = 0; + return; + } + + for (; (ir->rem > 0) && (i < buf_len); i++) { + ir->rem--; + + rawir.pulse = ((ir->buf_in[i] & MCE_PULSE_BIT) != 0); + rawir.duration = (ir->buf_in[i] & MCE_PULSE_MASK) + * MCE_TIME_UNIT * 1000; + + if ((ir->buf_in[i] & MCE_PULSE_MASK) == 0x7f) { + if (ir->rawir.pulse == rawir.pulse) + ir->rawir.duration += rawir.duration; + else { + ir->rawir.duration = rawir.duration; + ir->rawir.pulse = rawir.pulse; + } + continue; + } + rawir.duration += ir->rawir.duration; + ir->rawir.duration = 0; + ir->rawir.pulse = rawir.pulse; + + dev_dbg(ir->dev, "Storing %s with duration %d\n", + rawir.pulse ? "pulse" : "space", + rawir.duration); + + ir_raw_event_store(ir->idev, &rawir); + } + + if (ir->buf_in[i] == 0x80 || ir->buf_in[i] == 0x9f) + ir->rem = 0; + + dev_dbg(ir->dev, "calling ir_raw_event_handle\n"); + ir_raw_event_handle(ir->idev); + } +} + +static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs) +{ + struct mceusb_dev *ir; + int buf_len; + + if (!urb) + return; + + ir = urb->context; + if (!ir) { + usb_unlink_urb(urb); + return; + } + + buf_len = urb->actual_length; + + if (debug) + mceusb_dev_printdata(ir, urb->transfer_buffer, buf_len, false); + + if (ir->send_flags == RECV_FLAG_IN_PROGRESS) { + ir->send_flags = SEND_FLAG_COMPLETE; + dev_dbg(&ir->irdev->dev, "setup answer received %d bytes\n", + buf_len); + } + + switch (urb->status) { + /* success */ + case 0: + mceusb_process_ir_data(ir, buf_len); + break; + + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + usb_unlink_urb(urb); + return; + + case -EPIPE: + default: + break; + } + + usb_submit_urb(urb, GFP_ATOMIC); +} + +static void mceusb_gen1_init(struct mceusb_dev *ir) +{ + int ret; + int maxp = ir->len_in; + struct device *dev = ir->dev; + char *data; + + data = kzalloc(USB_CTRL_MSG_SZ, GFP_KERNEL); + if (!data) { + dev_err(dev, "%s: memory allocation failed!\n", __func__); + return; + } + + /* + * This is a strange one. Windows issues a set address to the device + * on the receive control pipe and expect a certain value pair back + */ + ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), + USB_REQ_SET_ADDRESS, USB_TYPE_VENDOR, 0, 0, + data, USB_CTRL_MSG_SZ, HZ * 3); + dev_dbg(dev, "%s - ret = %d\n", __func__, ret); + dev_dbg(dev, "%s - data[0] = %d, data[1] = %d\n", + __func__, data[0], data[1]); + + /* set feature: bit rate 38400 bps */ + ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0), + USB_REQ_SET_FEATURE, USB_TYPE_VENDOR, + 0xc04e, 0x0000, NULL, 0, HZ * 3); + + dev_dbg(dev, "%s - ret = %d\n", __func__, ret); + + /* bRequest 4: set char length to 8 bits */ + ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0), + 4, USB_TYPE_VENDOR, + 0x0808, 0x0000, NULL, 0, HZ * 3); + dev_dbg(dev, "%s - retB = %d\n", __func__, ret); + + /* bRequest 2: set handshaking to use DTR/DSR */ + ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0), + 2, USB_TYPE_VENDOR, + 0x0000, 0x0100, NULL, 0, HZ * 3); + dev_dbg(dev, "%s - retC = %d\n", __func__, ret); + + /* device reset */ + mce_async_out(ir, DEVICE_RESET, sizeof(DEVICE_RESET)); + mce_sync_in(ir, NULL, maxp); + + /* get hw/sw revision? */ + mce_async_out(ir, GET_REVISION, sizeof(GET_REVISION)); + mce_sync_in(ir, NULL, maxp); + + kfree(data); +}; + +static void mceusb_gen2_init(struct mceusb_dev *ir) +{ + int maxp = ir->len_in; + + /* device reset */ + mce_async_out(ir, DEVICE_RESET, sizeof(DEVICE_RESET)); + mce_sync_in(ir, NULL, maxp); + + /* get hw/sw revision? */ + mce_async_out(ir, GET_REVISION, sizeof(GET_REVISION)); + mce_sync_in(ir, NULL, maxp); + + /* unknown what the next two actually return... */ + mce_async_out(ir, GET_UNKNOWN, sizeof(GET_UNKNOWN)); + mce_sync_in(ir, NULL, maxp); + mce_async_out(ir, GET_UNKNOWN2, sizeof(GET_UNKNOWN2)); + mce_sync_in(ir, NULL, maxp); +} + +static void mceusb_get_parameters(struct mceusb_dev *ir) +{ + int maxp = ir->len_in; + + /* get the carrier and frequency */ + mce_async_out(ir, GET_CARRIER_FREQ, sizeof(GET_CARRIER_FREQ)); + mce_sync_in(ir, NULL, maxp); + + /* get the transmitter bitmask */ + mce_async_out(ir, GET_TX_BITMASK, sizeof(GET_TX_BITMASK)); + mce_sync_in(ir, NULL, maxp); + + /* get receiver timeout value */ + mce_async_out(ir, GET_RX_TIMEOUT, sizeof(GET_RX_TIMEOUT)); + mce_sync_in(ir, NULL, maxp); + + /* get receiver sensor setting */ + mce_async_out(ir, GET_RX_SENSOR, sizeof(GET_RX_SENSOR)); + mce_sync_in(ir, NULL, maxp); +} + +static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir) +{ + struct input_dev *idev; + struct ir_dev_props *props; + struct ir_input_dev *irdev; + struct device *dev = ir->dev; + int ret = -ENODEV; + + idev = input_allocate_device(); + if (!idev) { + dev_err(dev, "remote input dev allocation failed\n"); + goto idev_alloc_failed; + } + + ret = -ENOMEM; + props = kzalloc(sizeof(struct ir_dev_props), GFP_KERNEL); + if (!props) { + dev_err(dev, "remote ir dev props allocation failed\n"); + goto props_alloc_failed; + } + + irdev = kzalloc(sizeof(struct ir_input_dev), GFP_KERNEL); + if (!irdev) { + dev_err(dev, "remote ir input dev allocation failed\n"); + goto ir_dev_alloc_failed; + } + + snprintf(ir->name, sizeof(ir->name), "Media Center Ed. eHome " + "Infrared Remote Transceiver (%04x:%04x)", + le16_to_cpu(ir->usbdev->descriptor.idVendor), + le16_to_cpu(ir->usbdev->descriptor.idProduct)); + + idev->name = ir->name; + usb_make_path(ir->usbdev, ir->phys, sizeof(ir->phys)); + strlcat(ir->phys, "/input0", sizeof(ir->phys)); + idev->phys = ir->phys; + + props->priv = ir; + props->driver_type = RC_DRIVER_IR_RAW; + props->allowed_protos = IR_TYPE_ALL; + props->s_tx_mask = mceusb_set_tx_mask; + props->s_tx_carrier = mceusb_set_tx_carrier; + props->tx_ir = mceusb_tx_ir; + + ir->props = props; + ir->irdev = irdev; + + input_set_drvdata(idev, irdev); + + ret = ir_input_register(idev, RC_MAP_RC6_MCE, props, DRIVER_NAME); + if (ret < 0) { + dev_err(dev, "remote input device register failed\n"); + goto irdev_failed; + } + + return idev; + +irdev_failed: + kfree(irdev); +ir_dev_alloc_failed: + kfree(props); +props_alloc_failed: + input_free_device(idev); +idev_alloc_failed: + return NULL; +} + +static int __devinit mceusb_dev_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_host_interface *idesc; + struct usb_endpoint_descriptor *ep = NULL; + struct usb_endpoint_descriptor *ep_in = NULL; + struct usb_endpoint_descriptor *ep_out = NULL; + struct usb_host_config *config; + struct mceusb_dev *ir = NULL; + int pipe, maxp, i; + char buf[63], name[128] = ""; + bool is_gen3; + bool is_microsoft_gen1; + bool tx_mask_inverted; + + dev_dbg(&intf->dev, ": %s called\n", __func__); + + config = dev->actconfig; + idesc = intf->cur_altsetting; + + is_gen3 = usb_match_id(intf, gen3_list) ? 1 : 0; + is_microsoft_gen1 = usb_match_id(intf, microsoft_gen1_list) ? 1 : 0; + tx_mask_inverted = usb_match_id(intf, std_tx_mask_list) ? 0 : 1; + + /* step through the endpoints to find first bulk in and out endpoint */ + for (i = 0; i < idesc->desc.bNumEndpoints; ++i) { + ep = &idesc->endpoint[i].desc; + + if ((ep_in == NULL) + && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + == USB_DIR_IN) + && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_BULK) + || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_INT))) { + + ep_in = ep; + ep_in->bmAttributes = USB_ENDPOINT_XFER_INT; + ep_in->bInterval = 1; + dev_dbg(&intf->dev, ": acceptable inbound endpoint " + "found\n"); + } + + if ((ep_out == NULL) + && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + == USB_DIR_OUT) + && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_BULK) + || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_INT))) { + + ep_out = ep; + ep_out->bmAttributes = USB_ENDPOINT_XFER_INT; + ep_out->bInterval = 1; + dev_dbg(&intf->dev, ": acceptable outbound endpoint " + "found\n"); + } + } + if (ep_in == NULL) { + dev_dbg(&intf->dev, ": inbound and/or endpoint not found\n"); + return -ENODEV; + } + + pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + ir = kzalloc(sizeof(struct mceusb_dev), GFP_KERNEL); + if (!ir) + goto mem_alloc_fail; + + ir->buf_in = usb_alloc_coherent(dev, maxp, GFP_ATOMIC, &ir->dma_in); + if (!ir->buf_in) + goto buf_in_alloc_fail; + + ir->urb_in = usb_alloc_urb(0, GFP_KERNEL); + if (!ir->urb_in) + goto urb_in_alloc_fail; + + ir->usbdev = dev; + ir->dev = &intf->dev; + ir->len_in = maxp; + ir->flags.microsoft_gen1 = is_microsoft_gen1; + ir->flags.tx_mask_inverted = tx_mask_inverted; + + /* Saving usb interface data for use by the transmitter routine */ + ir->usb_ep_in = ep_in; + ir->usb_ep_out = ep_out; + + if (dev->descriptor.iManufacturer + && usb_string(dev, dev->descriptor.iManufacturer, + buf, sizeof(buf)) > 0) + strlcpy(name, buf, sizeof(name)); + if (dev->descriptor.iProduct + && usb_string(dev, dev->descriptor.iProduct, + buf, sizeof(buf)) > 0) + snprintf(name + strlen(name), sizeof(name) - strlen(name), + " %s", buf); + + ir->idev = mceusb_init_input_dev(ir); + if (!ir->idev) + goto input_dev_fail; + + /* flush buffers on the device */ + mce_sync_in(ir, NULL, maxp); + mce_sync_in(ir, NULL, maxp); + + /* wire up inbound data handler */ + usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in, + maxp, (usb_complete_t) mceusb_dev_recv, ir, ep_in->bInterval); + ir->urb_in->transfer_dma = ir->dma_in; + ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* initialize device */ + if (ir->flags.microsoft_gen1) + mceusb_gen1_init(ir); + else if (!is_gen3) + mceusb_gen2_init(ir); + + mceusb_get_parameters(ir); + + mceusb_set_tx_mask(ir, MCE_DEFAULT_TX_MASK); + + usb_set_intfdata(intf, ir); + + dev_info(&intf->dev, "Registered %s on usb%d:%d\n", name, + dev->bus->busnum, dev->devnum); + + return 0; + + /* Error-handling path */ +input_dev_fail: + usb_free_urb(ir->urb_in); +urb_in_alloc_fail: + usb_free_coherent(dev, maxp, ir->buf_in, ir->dma_in); +buf_in_alloc_fail: + kfree(ir); +mem_alloc_fail: + dev_err(&intf->dev, "%s: device setup failed!\n", __func__); + + return -ENOMEM; +} + + +static void __devexit mceusb_dev_disconnect(struct usb_interface *intf) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct mceusb_dev *ir = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + if (!ir) + return; + + ir->usbdev = NULL; + ir_input_unregister(ir->idev); + usb_kill_urb(ir->urb_in); + usb_free_urb(ir->urb_in); + usb_free_coherent(dev, ir->len_in, ir->buf_in, ir->dma_in); + + kfree(ir); +} + +static int mceusb_dev_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct mceusb_dev *ir = usb_get_intfdata(intf); + dev_info(ir->dev, "suspend\n"); + usb_kill_urb(ir->urb_in); + return 0; +} + +static int mceusb_dev_resume(struct usb_interface *intf) +{ + struct mceusb_dev *ir = usb_get_intfdata(intf); + dev_info(ir->dev, "resume\n"); + if (usb_submit_urb(ir->urb_in, GFP_ATOMIC)) + return -EIO; + return 0; +} + +static struct usb_driver mceusb_dev_driver = { + .name = DRIVER_NAME, + .probe = mceusb_dev_probe, + .disconnect = mceusb_dev_disconnect, + .suspend = mceusb_dev_suspend, + .resume = mceusb_dev_resume, + .reset_resume = mceusb_dev_resume, + .id_table = mceusb_dev_table +}; + +static int __init mceusb_dev_init(void) +{ + int ret; + + ret = usb_register(&mceusb_dev_driver); + if (ret < 0) + printk(KERN_ERR DRIVER_NAME + ": usb register failed, result = %d\n", ret); + + return ret; +} + +static void __exit mceusb_dev_exit(void) +{ + usb_deregister(&mceusb_dev_driver); +} + +module_init(mceusb_dev_init); +module_exit(mceusb_dev_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(usb, mceusb_dev_table); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug enabled or not"); diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index 7364b96..4da2a54c 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -57,7 +57,7 @@ void saa7146_dma_free(struct saa7146_dev *dev,struct videobuf_queue *q, BUG_ON(in_interrupt()); videobuf_waiton(&buf->vb,0,0); - videobuf_dma_unmap(q, dma); + videobuf_dma_unmap(q->dev, dma); videobuf_dma_free(dma); buf->vb.state = VIDEOBUF_NEEDS_INIT; } diff --git a/drivers/media/common/saa7146_vbi.c b/drivers/media/common/saa7146_vbi.c index 74e2b56..8224c30 100644 --- a/drivers/media/common/saa7146_vbi.c +++ b/drivers/media/common/saa7146_vbi.c @@ -375,7 +375,7 @@ static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv) static int vbi_open(struct saa7146_dev *dev, struct file *file) { - struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data; + struct saa7146_fh *fh = file->private_data; u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1); int ret = 0; @@ -437,7 +437,7 @@ static int vbi_open(struct saa7146_dev *dev, struct file *file) static void vbi_close(struct saa7146_dev *dev, struct file *file) { - struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data; + struct saa7146_fh *fh = file->private_data; struct saa7146_vv *vv = dev->vv_data; DEB_VBI(("dev:%p, fh:%p\n",dev,fh)); diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index b8b2c55..a212a91 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -1370,7 +1370,7 @@ static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv) static int video_open(struct saa7146_dev *dev, struct file *file) { - struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data; + struct saa7146_fh *fh = file->private_data; struct saa7146_format *sfmt; fh->video_fmt.width = 384; @@ -1394,7 +1394,7 @@ static int video_open(struct saa7146_dev *dev, struct file *file) static void video_close(struct saa7146_dev *dev, struct file *file) { - struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data; + struct saa7146_fh *fh = file->private_data; struct saa7146_vv *vv = dev->vv_data; struct videobuf_queue *q = &fh->video_q; int err; diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c index b2e1545..7955e49 100644 --- a/drivers/media/common/tuners/tda18271-fe.c +++ b/drivers/media/common/tuners/tda18271-fe.c @@ -1249,7 +1249,7 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, struct tda18271_config *cfg) { struct tda18271_priv *priv = NULL; - int instance; + int instance, ret; mutex_lock(&tda18271_list_mutex); @@ -1268,10 +1268,12 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, priv->cal_initialized = false; mutex_init(&priv->lock); - if (tda_fail(tda18271_get_id(fe))) + ret = tda18271_get_id(fe); + if (tda_fail(ret)) goto fail; - if (tda_fail(tda18271_assign_map_layout(fe))) + ret = tda18271_assign_map_layout(fe); + if (tda_fail(ret)) goto fail; mutex_lock(&priv->lock); diff --git a/drivers/media/common/tuners/tuner-simple.c b/drivers/media/common/tuners/tuner-simple.c index 8cf2ab6..f8ee29e 100644 --- a/drivers/media/common/tuners/tuner-simple.c +++ b/drivers/media/common/tuners/tuner-simple.c @@ -546,14 +546,11 @@ static int simple_set_tv_freq(struct dvb_frontend *fe, struct tuner_simple_priv *priv = fe->tuner_priv; u8 config, cb; u16 div; - struct tunertype *tun; u8 buffer[4]; int rc, IFPCoff, i; enum param_type desired_type; struct tuner_params *t_params; - tun = priv->tun; - /* IFPCoff = Video Intermediate Frequency - Vif: 940 =16*58.75 NTSC/J (Japan) 732 =16*45.75 M/N STD diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index 432003d..d2b2c12 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -217,6 +217,7 @@ static int xc_send_i2c_data(struct xc5000_priv *priv, u8 *buf, int len) return XC_RESULT_SUCCESS; } +#if 0 /* This routine is never used because the only time we read data from the i2c bus is when we read registers, and we want that to be an atomic i2c transaction in case we are on a multi-master bus */ @@ -231,6 +232,27 @@ static int xc_read_i2c_data(struct xc5000_priv *priv, u8 *buf, int len) } return 0; } +#endif + +static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val) +{ + u8 buf[2] = { reg >> 8, reg & 0xff }; + u8 bval[2] = { 0, 0 }; + struct i2c_msg msg[2] = { + { .addr = priv->i2c_props.addr, + .flags = 0, .buf = &buf[0], .len = 2 }, + { .addr = priv->i2c_props.addr, + .flags = I2C_M_RD, .buf = &bval[0], .len = 2 }, + }; + + if (i2c_transfer(priv->i2c_props.adap, msg, 2) != 2) { + printk(KERN_WARNING "xc5000: I2C read failed\n"); + return -EREMOTEIO; + } + + *val = (bval[0] << 8) | bval[1]; + return XC_RESULT_SUCCESS; +} static void xc_wait(int wait_ms) { @@ -275,20 +297,14 @@ static int xc_write_reg(struct xc5000_priv *priv, u16 regAddr, u16 i2cData) if (result == XC_RESULT_SUCCESS) { /* wait for busy flag to clear */ while ((WatchDogTimer > 0) && (result == XC_RESULT_SUCCESS)) { - buf[0] = 0; - buf[1] = XREG_BUSY; - - result = xc_send_i2c_data(priv, buf, 2); + result = xc5000_readreg(priv, XREG_BUSY, (u16 *)buf); if (result == XC_RESULT_SUCCESS) { - result = xc_read_i2c_data(priv, buf, 2); - if (result == XC_RESULT_SUCCESS) { - if ((buf[0] == 0) && (buf[1] == 0)) { - /* busy flag cleared */ + if ((buf[0] == 0) && (buf[1] == 0)) { + /* busy flag cleared */ break; - } else { - xc_wait(5); /* wait 5 ms */ - WatchDogTimer--; - } + } else { + xc_wait(5); /* wait 5 ms */ + WatchDogTimer--; } } } @@ -526,25 +542,6 @@ static int xc_tune_channel(struct xc5000_priv *priv, u32 freq_hz, int mode) return found; } -static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val) -{ - u8 buf[2] = { reg >> 8, reg & 0xff }; - u8 bval[2] = { 0, 0 }; - struct i2c_msg msg[2] = { - { .addr = priv->i2c_props.addr, - .flags = 0, .buf = &buf[0], .len = 2 }, - { .addr = priv->i2c_props.addr, - .flags = I2C_M_RD, .buf = &bval[0], .len = 2 }, - }; - - if (i2c_transfer(priv->i2c_props.adap, msg, 2) != 2) { - printk(KERN_WARNING "xc5000: I2C read failed\n"); - return -EREMOTEIO; - } - - *val = (bval[0] << 8) | bval[1]; - return XC_RESULT_SUCCESS; -} static int xc5000_fwupload(struct dvb_frontend *fe) { diff --git a/drivers/media/dvb/bt8xx/dst_ca.c b/drivers/media/dvb/bt8xx/dst_ca.c index 770243c..cf87051 100644 --- a/drivers/media/dvb/bt8xx/dst_ca.c +++ b/drivers/media/dvb/bt8xx/dst_ca.c @@ -565,7 +565,7 @@ static long dst_ca_ioctl(struct file *file, unsigned int cmd, unsigned long ioct int result = 0; lock_kernel(); - dvbdev = (struct dvb_device *)file->private_data; + dvbdev = file->private_data; state = (struct dst_state *)dvbdev->priv; p_ca_message = kmalloc(sizeof (struct ca_msg), GFP_KERNEL); p_ca_slot_info = kmalloc(sizeof (struct ca_slot_info), GFP_KERNEL); diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c index 425862f..0042306 100644 --- a/drivers/media/dvb/dvb-core/dmxdev.c +++ b/drivers/media/dvb/dvb-core/dmxdev.c @@ -207,7 +207,7 @@ static int dvb_dvr_release(struct inode *inode, struct file *file) } /* TODO */ dvbdev->users--; - if(dvbdev->users==-1 && dmxdev->exit==1) { + if (dvbdev->users == 1 && dmxdev->exit == 1) { fops_put(file->f_op); file->f_op = NULL; mutex_unlock(&dmxdev->mutex); diff --git a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c index ef259a0..cb97e6b 100644 --- a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c @@ -1318,8 +1318,11 @@ static ssize_t dvb_ca_en50221_io_write(struct file *file, fragbuf[0] = connection_id; fragbuf[1] = ((fragpos + fraglen) < count) ? 0x80 : 0x00; - if ((status = copy_from_user(fragbuf + 2, buf + fragpos, fraglen)) != 0) + status = copy_from_user(fragbuf + 2, buf + fragpos, fraglen); + if (status) { + status = -EFAULT; goto exit; + } timeout = jiffies + HZ / 2; written = 0; @@ -1494,8 +1497,11 @@ static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user * buf, hdr[0] = slot; hdr[1] = connection_id; - if ((status = copy_to_user(buf, hdr, 2)) != 0) + status = copy_to_user(buf, hdr, 2); + if (status) { + status = -EFAULT; goto exit; + } status = pktlen; exit: diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c index 977ddba..4a88a3e 100644 --- a/drivers/media/dvb/dvb-core/dvb_demux.c +++ b/drivers/media/dvb/dvb-core/dvb_demux.c @@ -1130,13 +1130,9 @@ static int dvbdmx_write(struct dmx_demux *demux, const char __user *buf, size_t if ((!demux->frontend) || (demux->frontend->source != DMX_MEMORY_FE)) return -EINVAL; - p = kmalloc(count, GFP_USER); - if (!p) - return -ENOMEM; - if (copy_from_user(p, buf, count)) { - kfree(p); - return -EFAULT; - } + p = memdup_user(buf, count); + if (IS_ERR(p)) + return PTR_ERR(p); if (mutex_lock_interruptible(&dvbdemux->mutex)) { kfree(p); return -ERESTARTSYS; diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 44ae89e..4d45b7d 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -465,7 +465,7 @@ static void dvb_frontend_swzigzag(struct dvb_frontend *fe) if ((fepriv->state & FESTATE_SEARCHING_FAST) || (fepriv->state & FESTATE_RETUNE)) { fepriv->delay = fepriv->min_delay; - /* peform a tune */ + /* perform a tune */ retval = dvb_frontend_swzigzag_autotune(fe, fepriv->check_wrapped); if (retval < 0) { @@ -791,7 +791,7 @@ static int dvb_frontend_start(struct dvb_frontend *fe) return 0; } -static void dvb_frontend_get_frequeny_limits(struct dvb_frontend *fe, +static void dvb_frontend_get_frequency_limits(struct dvb_frontend *fe, u32 *freq_min, u32 *freq_max) { *freq_min = max(fe->ops.info.frequency_min, fe->ops.tuner_ops.info.frequency_min); @@ -815,7 +815,7 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe, u32 freq_max; /* range check: frequency */ - dvb_frontend_get_frequeny_limits(fe, &freq_min, &freq_max); + dvb_frontend_get_frequency_limits(fe, &freq_min, &freq_max); if ((freq_min && parms->frequency < freq_min) || (freq_max && parms->frequency > freq_max)) { printk(KERN_WARNING "DVB: adapter %i frontend %i frequency %u out of range (%u..%u)\n", @@ -1627,7 +1627,7 @@ static int dvb_frontend_ioctl_legacy(struct file *file, case FE_GET_INFO: { struct dvb_frontend_info* info = parg; memcpy(info, &fe->ops.info, sizeof(struct dvb_frontend_info)); - dvb_frontend_get_frequeny_limits(fe, &info->frequency_min, &info->frequency_max); + dvb_frontend_get_frequency_limits(fe, &info->frequency_min, &info->frequency_max); /* Force the CAN_INVERSION_AUTO bit on. If the frontend doesn't * do it, it is done for it. */ @@ -1726,7 +1726,7 @@ static int dvb_frontend_ioctl_legacy(struct file *file, * (stv0299 for instance) take longer than 8msec to * respond to a set_voltage command. Those switches * need custom routines to switch properly. For all - * other frontends, the following shoule work ok. + * other frontends, the following should work ok. * Dish network legacy switches (as used by Dish500) * are controlled by sending 9-bit command words * spaced 8msec apart. diff --git a/drivers/media/dvb/dvb-usb/a800.c b/drivers/media/dvb/dvb-usb/a800.c index b6cbb1d..a5c3637 100644 --- a/drivers/media/dvb/dvb-usb/a800.c +++ b/drivers/media/dvb/dvb-usb/a800.c @@ -37,7 +37,7 @@ static int a800_identify_state(struct usb_device *udev, struct dvb_usb_device_pr return 0; } -static struct dvb_usb_rc_key ir_codes_a800_table[] = { +static struct ir_scancode ir_codes_a800_table[] = { { 0x0201, KEY_PROG1 }, /* SOURCE */ { 0x0200, KEY_POWER }, /* POWER */ { 0x0205, KEY_1 }, /* 1 */ @@ -146,10 +146,12 @@ static struct dvb_usb_device_properties a800_properties = { .power_ctrl = a800_power_ctrl, .identify_state = a800_identify_state, - .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = ir_codes_a800_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_a800_table), - .rc_query = a800_rc_query, + .rc.legacy = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_key_map = ir_codes_a800_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_a800_table), + .rc_query = a800_rc_query, + }, .i2c_algo = &dibusb_i2c_algo, diff --git a/drivers/media/dvb/dvb-usb/af9005-remote.c b/drivers/media/dvb/dvb-usb/af9005-remote.c index b41fa87..696207f 100644 --- a/drivers/media/dvb/dvb-usb/af9005-remote.c +++ b/drivers/media/dvb/dvb-usb/af9005-remote.c @@ -33,7 +33,7 @@ MODULE_PARM_DESC(debug, #define deb_decode(args...) dprintk(dvb_usb_af9005_remote_debug,0x01,args) -struct dvb_usb_rc_key ir_codes_af9005_table[] = { +struct ir_scancode ir_codes_af9005_table[] = { {0x01b7, KEY_POWER}, {0x01a7, KEY_VOLUMEUP}, @@ -133,7 +133,7 @@ int af9005_rc_decode(struct dvb_usb_device *d, u8 * data, int len, u32 * event, for (i = 0; i < ir_codes_af9005_table_size; i++) { if (rc5_custom(&ir_codes_af9005_table[i]) == cust && rc5_data(&ir_codes_af9005_table[i]) == dat) { - *event = ir_codes_af9005_table[i].event; + *event = ir_codes_af9005_table[i].keycode; *state = REMOTE_KEY_PRESSED; deb_decode ("key pressed, event %x\n", *event); diff --git a/drivers/media/dvb/dvb-usb/af9005.c b/drivers/media/dvb/dvb-usb/af9005.c index cfd6107..8ecba88 100644 --- a/drivers/media/dvb/dvb-usb/af9005.c +++ b/drivers/media/dvb/dvb-usb/af9005.c @@ -54,50 +54,6 @@ struct af9005_device_state { int led_state; }; -static int af9005_usb_generic_rw(struct dvb_usb_device *d, u8 *wbuf, u16 wlen, - u8 *rbuf, u16 rlen, int delay_ms) -{ - int actlen, ret = -ENOMEM; - - if (wbuf == NULL || wlen == 0) - return -EINVAL; - - if ((ret = mutex_lock_interruptible(&d->usb_mutex))) - return ret; - - deb_xfer(">>> "); - debug_dump(wbuf, wlen, deb_xfer); - - ret = usb_bulk_msg(d->udev, usb_sndbulkpipe(d->udev, - 2), wbuf, wlen, - &actlen, 2000); - - if (ret) - err("bulk message failed: %d (%d/%d)", ret, wlen, actlen); - else - ret = actlen != wlen ? -1 : 0; - - /* an answer is expected, and no error before */ - if (!ret && rbuf && rlen) { - if (delay_ms) - msleep(delay_ms); - - ret = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev, - 0x01), rbuf, - rlen, &actlen, 2000); - - if (ret) - err("recv bulk message failed: %d", ret); - else { - deb_xfer("<<< "); - debug_dump(rbuf, actlen, deb_xfer); - } - } - - mutex_unlock(&d->usb_mutex); - return ret; -} - static int af9005_generic_read_write(struct dvb_usb_device *d, u16 reg, int readwrite, int type, u8 * values, int len) { @@ -146,7 +102,7 @@ static int af9005_generic_read_write(struct dvb_usb_device *d, u16 reg, obuf[8] = values[0]; obuf[7] = command; - ret = af9005_usb_generic_rw(d, obuf, 16, ibuf, 17, 0); + ret = dvb_usb_generic_rw(d, obuf, 16, ibuf, 17, 0); if (ret) return ret; @@ -534,7 +490,7 @@ int af9005_send_command(struct dvb_usb_device *d, u8 command, u8 * wbuf, buf[6] = wlen; for (i = 0; i < wlen; i++) buf[7 + i] = wbuf[i]; - ret = af9005_usb_generic_rw(d, buf, wlen + 7, ibuf, rlen + 7, 0); + ret = dvb_usb_generic_rw(d, buf, wlen + 7, ibuf, rlen + 7, 0); if (ret) return ret; if (ibuf[2] != 0x27) { @@ -581,7 +537,7 @@ int af9005_read_eeprom(struct dvb_usb_device *d, u8 address, u8 * values, obuf[6] = len; obuf[7] = address; - ret = af9005_usb_generic_rw(d, obuf, 16, ibuf, 14, 0); + ret = dvb_usb_generic_rw(d, obuf, 16, ibuf, 14, 0); if (ret) return ret; if (ibuf[2] != 0x2b) { @@ -882,7 +838,7 @@ static int af9005_rc_query(struct dvb_usb_device *d, u32 * event, int *state) obuf[2] = 0x40; /* read remote */ obuf[3] = 1; /* rest of packet length */ obuf[4] = st->sequence++; /* sequence number */ - ret = af9005_usb_generic_rw(d, obuf, 5, ibuf, 256, 0); + ret = dvb_usb_generic_rw(d, obuf, 5, ibuf, 256, 0); if (ret) { err("rc query failed"); return ret; @@ -1069,10 +1025,15 @@ static struct dvb_usb_device_properties af9005_properties = { .i2c_algo = &af9005_i2c_algo, - .rc_interval = 200, - .rc_key_map = NULL, - .rc_key_map_size = 0, - .rc_query = af9005_rc_query, + .rc.legacy = { + .rc_interval = 200, + .rc_key_map = NULL, + .rc_key_map_size = 0, + .rc_query = af9005_rc_query, + }, + + .generic_bulk_ctrl_endpoint = 2, + .generic_bulk_ctrl_endpoint_response = 1, .num_device_descs = 3, .devices = { @@ -1113,10 +1074,10 @@ static int __init af9005_usb_module_init(void) rc_keys_size = symbol_request(ir_codes_af9005_table_size); if (rc_decode == NULL || rc_keys == NULL || rc_keys_size == NULL) { err("af9005_rc_decode function not found, disabling remote"); - af9005_properties.rc_query = NULL; + af9005_properties.rc.legacy.rc_query = NULL; } else { - af9005_properties.rc_key_map = rc_keys; - af9005_properties.rc_key_map_size = *rc_keys_size; + af9005_properties.rc.legacy.rc_key_map = rc_keys; + af9005_properties.rc.legacy.rc_key_map_size = *rc_keys_size; } return 0; diff --git a/drivers/media/dvb/dvb-usb/af9005.h b/drivers/media/dvb/dvb-usb/af9005.h index 088e708..3c1fbd1 100644 --- a/drivers/media/dvb/dvb-usb/af9005.h +++ b/drivers/media/dvb/dvb-usb/af9005.h @@ -3490,7 +3490,7 @@ extern u8 regmask[8]; /* remote control decoder */ extern int af9005_rc_decode(struct dvb_usb_device *d, u8 * data, int len, u32 * event, int *state); -extern struct dvb_usb_rc_key ir_codes_af9005_table[]; +extern struct ir_scancode ir_codes_af9005_table[]; extern int ir_codes_af9005_table_size; #endif diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index 66c7c3e..ea1ed3b 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -735,7 +735,7 @@ error: struct af9015_setup { unsigned int id; - struct dvb_usb_rc_key *rc_key_map; + struct ir_scancode *rc_key_map; unsigned int rc_key_map_size; u8 *ir_table; unsigned int ir_table_size; @@ -847,8 +847,8 @@ static void af9015_set_remote_config(struct usb_device *udev, } if (table) { - props->rc_key_map = table->rc_key_map; - props->rc_key_map_size = table->rc_key_map_size; + props->rc.legacy.rc_key_map = table->rc_key_map; + props->rc.legacy.rc_key_map_size = table->rc_key_map_size; af9015_config.ir_table = table->ir_table; af9015_config.ir_table_size = table->ir_table_size; } @@ -878,8 +878,8 @@ static int af9015_read_config(struct usb_device *udev) deb_info("%s: IR mode:%d\n", __func__, val); for (i = 0; i < af9015_properties_count; i++) { if (val == AF9015_IR_MODE_DISABLED) { - af9015_properties[i].rc_key_map = NULL; - af9015_properties[i].rc_key_map_size = 0; + af9015_properties[i].rc.legacy.rc_key_map = NULL; + af9015_properties[i].rc.legacy.rc_key_map_size = 0; } else af9015_set_remote_config(udev, &af9015_properties[i]); } @@ -1063,7 +1063,7 @@ static int af9015_rc_query(struct dvb_usb_device *d, u32 *event, int *state) { u8 buf[8]; struct req_t req = {GET_IR_CODE, 0, 0, 0, 0, sizeof(buf), buf}; - struct dvb_usb_rc_key *keymap = d->props.rc_key_map; + struct ir_scancode *keymap = d->props.rc.legacy.rc_key_map; int i, ret; memset(buf, 0, sizeof(buf)); @@ -1075,10 +1075,10 @@ static int af9015_rc_query(struct dvb_usb_device *d, u32 *event, int *state) *event = 0; *state = REMOTE_NO_KEY_PRESSED; - for (i = 0; i < d->props.rc_key_map_size; i++) { + for (i = 0; i < d->props.rc.legacy.rc_key_map_size; i++) { if (!buf[1] && rc5_custom(&keymap[i]) == buf[0] && rc5_data(&keymap[i]) == buf[2]) { - *event = keymap[i].event; + *event = keymap[i].keycode; *state = REMOTE_KEY_PRESSED; break; } @@ -1299,6 +1299,7 @@ static struct usb_device_id af9015_usb_table[] = { {USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV2000DS)}, /* 30 */{USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB383_T)}, {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_4)}, + {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A815M)}, {0}, }; MODULE_DEVICE_TABLE(usb, af9015_usb_table); @@ -1353,8 +1354,10 @@ static struct dvb_usb_device_properties af9015_properties[] = { .identify_state = af9015_identify_state, - .rc_query = af9015_rc_query, - .rc_interval = 150, + .rc.legacy = { + .rc_query = af9015_rc_query, + .rc_interval = 150, + }, .i2c_algo = &af9015_i2c_algo, @@ -1460,8 +1463,10 @@ static struct dvb_usb_device_properties af9015_properties[] = { .identify_state = af9015_identify_state, - .rc_query = af9015_rc_query, - .rc_interval = 150, + .rc.legacy = { + .rc_query = af9015_rc_query, + .rc_interval = 150, + }, .i2c_algo = &af9015_i2c_algo, @@ -1567,12 +1572,14 @@ static struct dvb_usb_device_properties af9015_properties[] = { .identify_state = af9015_identify_state, - .rc_query = af9015_rc_query, - .rc_interval = 150, + .rc.legacy = { + .rc_query = af9015_rc_query, + .rc_interval = 150, + }, .i2c_algo = &af9015_i2c_algo, - .num_device_descs = 8, /* max 9 */ + .num_device_descs = 9, /* max 9 */ .devices = { { .name = "AverMedia AVerTV Volar GPS 805 (A805)", @@ -1617,6 +1624,11 @@ static struct dvb_usb_device_properties af9015_properties[] = { .cold_ids = {&af9015_usb_table[30], NULL}, .warm_ids = {NULL}, }, + { + .name = "AverMedia AVerTV Volar M (A815Mac)", + .cold_ids = {&af9015_usb_table[32], NULL}, + .warm_ids = {NULL}, + }, } }, }; diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h index 63b2a49..c8e9349 100644 --- a/drivers/media/dvb/dvb-usb/af9015.h +++ b/drivers/media/dvb/dvb-usb/af9015.h @@ -123,7 +123,7 @@ enum af9015_remote { /* LeadTek - Y04G0051 */ /* Leadtek WinFast DTV Dongle Gold */ -static struct dvb_usb_rc_key ir_codes_af9015_table_leadtek[] = { +static struct ir_scancode ir_codes_af9015_table_leadtek[] = { { 0x001e, KEY_1 }, { 0x001f, KEY_2 }, { 0x0020, KEY_3 }, @@ -227,7 +227,7 @@ static u8 af9015_ir_table_leadtek[] = { }; /* TwinHan AzureWave AD-TU700(704J) */ -static struct dvb_usb_rc_key ir_codes_af9015_table_twinhan[] = { +static struct ir_scancode ir_codes_af9015_table_twinhan[] = { { 0x053f, KEY_POWER }, { 0x0019, KEY_FAVORITES }, /* Favorite List */ { 0x0004, KEY_TEXT }, /* Teletext */ @@ -338,7 +338,7 @@ static u8 af9015_ir_table_twinhan[] = { }; /* A-Link DTU(m) */ -static struct dvb_usb_rc_key ir_codes_af9015_table_a_link[] = { +static struct ir_scancode ir_codes_af9015_table_a_link[] = { { 0x001e, KEY_1 }, { 0x001f, KEY_2 }, { 0x0020, KEY_3 }, @@ -381,7 +381,7 @@ static u8 af9015_ir_table_a_link[] = { }; /* MSI DIGIVOX mini II V3.0 */ -static struct dvb_usb_rc_key ir_codes_af9015_table_msi[] = { +static struct ir_scancode ir_codes_af9015_table_msi[] = { { 0x001e, KEY_1 }, { 0x001f, KEY_2 }, { 0x0020, KEY_3 }, @@ -424,7 +424,7 @@ static u8 af9015_ir_table_msi[] = { }; /* MYGICTV U718 */ -static struct dvb_usb_rc_key ir_codes_af9015_table_mygictv[] = { +static struct ir_scancode ir_codes_af9015_table_mygictv[] = { { 0x003d, KEY_SWITCHVIDEOMODE }, /* TV / AV */ { 0x0545, KEY_POWER }, @@ -550,7 +550,7 @@ static u8 af9015_ir_table_kworld[] = { }; /* AverMedia Volar X */ -static struct dvb_usb_rc_key ir_codes_af9015_table_avermedia[] = { +static struct ir_scancode ir_codes_af9015_table_avermedia[] = { { 0x053d, KEY_PROG1 }, /* SOURCE */ { 0x0512, KEY_POWER }, /* POWER */ { 0x051e, KEY_1 }, /* 1 */ @@ -656,7 +656,7 @@ static u8 af9015_ir_table_avermedia_ks[] = { }; /* Digittrade DVB-T USB Stick */ -static struct dvb_usb_rc_key ir_codes_af9015_table_digittrade[] = { +static struct ir_scancode ir_codes_af9015_table_digittrade[] = { { 0x010f, KEY_LAST }, /* RETURN */ { 0x0517, KEY_TEXT }, /* TELETEXT */ { 0x0108, KEY_EPG }, /* EPG */ @@ -719,7 +719,7 @@ static u8 af9015_ir_table_digittrade[] = { }; /* TREKSTOR DVB-T USB Stick */ -static struct dvb_usb_rc_key ir_codes_af9015_table_trekstor[] = { +static struct ir_scancode ir_codes_af9015_table_trekstor[] = { { 0x0704, KEY_AGAIN }, /* Home */ { 0x0705, KEY_MUTE }, /* Mute */ { 0x0706, KEY_UP }, /* Up */ @@ -782,7 +782,7 @@ static u8 af9015_ir_table_trekstor[] = { }; /* MSI DIGIVOX mini III */ -static struct dvb_usb_rc_key ir_codes_af9015_table_msi_digivox_iii[] = { +static struct ir_scancode ir_codes_af9015_table_msi_digivox_iii[] = { { 0x0713, KEY_POWER }, /* [red power button] */ { 0x073b, KEY_VIDEO }, /* Source */ { 0x073e, KEY_ZOOM }, /* Zoom */ diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c index faca1ad..4685259 100644 --- a/drivers/media/dvb/dvb-usb/anysee.c +++ b/drivers/media/dvb/dvb-usb/anysee.c @@ -377,7 +377,7 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap) static int anysee_rc_query(struct dvb_usb_device *d, u32 *event, int *state) { u8 buf[] = {CMD_GET_IR_CODE}; - struct dvb_usb_rc_key *keymap = d->props.rc_key_map; + struct ir_scancode *keymap = d->props.rc.legacy.rc_key_map; u8 ircode[2]; int i, ret; @@ -388,10 +388,10 @@ static int anysee_rc_query(struct dvb_usb_device *d, u32 *event, int *state) *event = 0; *state = REMOTE_NO_KEY_PRESSED; - for (i = 0; i < d->props.rc_key_map_size; i++) { + for (i = 0; i < d->props.rc.legacy.rc_key_map_size; i++) { if (rc5_custom(&keymap[i]) == ircode[0] && rc5_data(&keymap[i]) == ircode[1]) { - *event = keymap[i].event; + *event = keymap[i].keycode; *state = REMOTE_KEY_PRESSED; return 0; } @@ -399,7 +399,7 @@ static int anysee_rc_query(struct dvb_usb_device *d, u32 *event, int *state) return 0; } -static struct dvb_usb_rc_key ir_codes_anysee_table[] = { +static struct ir_scancode ir_codes_anysee_table[] = { { 0x0100, KEY_0 }, { 0x0101, KEY_1 }, { 0x0102, KEY_2 }, @@ -463,6 +463,11 @@ static int anysee_probe(struct usb_interface *intf, if (intf->num_altsetting < 1) return -ENODEV; + /* + * Anysee is always warm (its USB-bridge, Cypress FX2, uploads + * firmware from eeprom). If dvb_usb_device_init() succeeds that + * means d is a valid pointer. + */ ret = dvb_usb_device_init(intf, &anysee_properties, THIS_MODULE, &d, adapter_nr); if (ret) @@ -479,10 +484,7 @@ static int anysee_probe(struct usb_interface *intf, if (ret) return ret; - if (d) - ret = anysee_init(d); - - return ret; + return anysee_init(d); } static struct usb_device_id anysee_table[] = { @@ -518,10 +520,12 @@ static struct dvb_usb_device_properties anysee_properties = { } }, - .rc_key_map = ir_codes_anysee_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_anysee_table), - .rc_query = anysee_rc_query, - .rc_interval = 200, /* windows driver uses 500ms */ + .rc.legacy = { + .rc_key_map = ir_codes_anysee_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_anysee_table), + .rc_query = anysee_rc_query, + .rc_interval = 200, /* windows driver uses 500ms */ + }, .i2c_algo = &anysee_i2c_algo, diff --git a/drivers/media/dvb/dvb-usb/az6027.c b/drivers/media/dvb/dvb-usb/az6027.c index 6681ac1..62c5828 100644 --- a/drivers/media/dvb/dvb-usb/az6027.c +++ b/drivers/media/dvb/dvb-usb/az6027.c @@ -386,7 +386,7 @@ static int az6027_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) } /* keys for the enclosed remote control */ -static struct dvb_usb_rc_key ir_codes_az6027_table[] = { +static struct ir_scancode ir_codes_az6027_table[] = { { 0x01, KEY_1 }, { 0x02, KEY_2 }, }; @@ -1125,10 +1125,13 @@ static struct dvb_usb_device_properties az6027_properties = { .power_ctrl = az6027_power_ctrl, .read_mac_address = az6027_read_mac_addr, */ - .rc_key_map = ir_codes_az6027_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_az6027_table), - .rc_interval = 400, - .rc_query = az6027_rc_query, + .rc.legacy = { + .rc_key_map = ir_codes_az6027_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_az6027_table), + .rc_interval = 400, + .rc_query = az6027_rc_query, + }, + .i2c_algo = &az6027_i2c_algo, .num_device_descs = 5, diff --git a/drivers/media/dvb/dvb-usb/cinergyT2-core.c b/drivers/media/dvb/dvb-usb/cinergyT2-core.c index 5a9c14b..4f5aa83f 100644 --- a/drivers/media/dvb/dvb-usb/cinergyT2-core.c +++ b/drivers/media/dvb/dvb-usb/cinergyT2-core.c @@ -84,7 +84,7 @@ static int cinergyt2_frontend_attach(struct dvb_usb_adapter *adap) return 0; } -static struct dvb_usb_rc_key ir_codes_cinergyt2_table[] = { +static struct ir_scancode ir_codes_cinergyt2_table[] = { { 0x0401, KEY_POWER }, { 0x0402, KEY_1 }, { 0x0403, KEY_2 }, @@ -217,10 +217,12 @@ static struct dvb_usb_device_properties cinergyt2_properties = { .power_ctrl = cinergyt2_power_ctrl, - .rc_interval = 50, - .rc_key_map = ir_codes_cinergyt2_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_cinergyt2_table), - .rc_query = cinergyt2_rc_query, + .rc.legacy = { + .rc_interval = 50, + .rc_key_map = ir_codes_cinergyt2_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_cinergyt2_table), + .rc_query = cinergyt2_rc_query, + }, .generic_bulk_ctrl_endpoint = 1, diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index 11e9e85..cd9f362 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/drivers/media/dvb/dvb-usb/cxusb.c @@ -385,7 +385,7 @@ static int cxusb_d680_dmb_streaming_ctrl( static int cxusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state) { - struct dvb_usb_rc_key *keymap = d->props.rc_key_map; + struct ir_scancode *keymap = d->props.rc.legacy.rc_key_map; u8 ircode[4]; int i; @@ -394,10 +394,10 @@ static int cxusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state) *event = 0; *state = REMOTE_NO_KEY_PRESSED; - for (i = 0; i < d->props.rc_key_map_size; i++) { + for (i = 0; i < d->props.rc.legacy.rc_key_map_size; i++) { if (rc5_custom(&keymap[i]) == ircode[2] && rc5_data(&keymap[i]) == ircode[3]) { - *event = keymap[i].event; + *event = keymap[i].keycode; *state = REMOTE_KEY_PRESSED; return 0; @@ -410,7 +410,7 @@ static int cxusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state) static int cxusb_bluebird2_rc_query(struct dvb_usb_device *d, u32 *event, int *state) { - struct dvb_usb_rc_key *keymap = d->props.rc_key_map; + struct ir_scancode *keymap = d->props.rc.legacy.rc_key_map; u8 ircode[4]; int i; struct i2c_msg msg = { .addr = 0x6b, .flags = I2C_M_RD, @@ -422,10 +422,10 @@ static int cxusb_bluebird2_rc_query(struct dvb_usb_device *d, u32 *event, if (cxusb_i2c_xfer(&d->i2c_adap, &msg, 1) != 1) return 0; - for (i = 0; i < d->props.rc_key_map_size; i++) { + for (i = 0; i < d->props.rc.legacy.rc_key_map_size; i++) { if (rc5_custom(&keymap[i]) == ircode[1] && rc5_data(&keymap[i]) == ircode[2]) { - *event = keymap[i].event; + *event = keymap[i].keycode; *state = REMOTE_KEY_PRESSED; return 0; @@ -438,7 +438,7 @@ static int cxusb_bluebird2_rc_query(struct dvb_usb_device *d, u32 *event, static int cxusb_d680_dmb_rc_query(struct dvb_usb_device *d, u32 *event, int *state) { - struct dvb_usb_rc_key *keymap = d->props.rc_key_map; + struct ir_scancode *keymap = d->props.rc.legacy.rc_key_map; u8 ircode[2]; int i; @@ -448,10 +448,10 @@ static int cxusb_d680_dmb_rc_query(struct dvb_usb_device *d, u32 *event, if (cxusb_ctrl_msg(d, 0x10, NULL, 0, ircode, 2) < 0) return 0; - for (i = 0; i < d->props.rc_key_map_size; i++) { + for (i = 0; i < d->props.rc.legacy.rc_key_map_size; i++) { if (rc5_custom(&keymap[i]) == ircode[0] && rc5_data(&keymap[i]) == ircode[1]) { - *event = keymap[i].event; + *event = keymap[i].keycode; *state = REMOTE_KEY_PRESSED; return 0; @@ -461,7 +461,7 @@ static int cxusb_d680_dmb_rc_query(struct dvb_usb_device *d, u32 *event, return 0; } -static struct dvb_usb_rc_key ir_codes_dvico_mce_table[] = { +static struct ir_scancode ir_codes_dvico_mce_table[] = { { 0xfe02, KEY_TV }, { 0xfe0e, KEY_MP3 }, { 0xfe1a, KEY_DVD }, @@ -509,7 +509,7 @@ static struct dvb_usb_rc_key ir_codes_dvico_mce_table[] = { { 0xfe4e, KEY_POWER }, }; -static struct dvb_usb_rc_key ir_codes_dvico_portable_table[] = { +static struct ir_scancode ir_codes_dvico_portable_table[] = { { 0xfc02, KEY_SETUP }, /* Profile */ { 0xfc43, KEY_POWER2 }, { 0xfc06, KEY_EPG }, @@ -548,7 +548,7 @@ static struct dvb_usb_rc_key ir_codes_dvico_portable_table[] = { { 0xfc00, KEY_UNKNOWN }, /* HD */ }; -static struct dvb_usb_rc_key ir_codes_d680_dmb_table[] = { +static struct ir_scancode ir_codes_d680_dmb_table[] = { { 0x0038, KEY_UNKNOWN }, /* TV/AV */ { 0x080c, KEY_ZOOM }, { 0x0800, KEY_0 }, @@ -923,7 +923,7 @@ static int cxusb_dualdig4_frontend_attach(struct dvb_usb_adapter *adap) return -EIO; /* try to determine if there is no IR decoder on the I2C bus */ - for (i = 0; adap->dev->props.rc_key_map != NULL && i < 5; i++) { + for (i = 0; adap->dev->props.rc.legacy.rc_key_map != NULL && i < 5; i++) { msleep(20); if (cxusb_i2c_xfer(&adap->dev->i2c_adap, &msg, 1) != 1) goto no_IR; @@ -931,7 +931,7 @@ static int cxusb_dualdig4_frontend_attach(struct dvb_usb_adapter *adap) continue; if (ircode[2] + ircode[3] != 0xff) { no_IR: - adap->dev->props.rc_key_map = NULL; + adap->dev->props.rc.legacy.rc_key_map = NULL; info("No IR receiver detected on this device."); break; } @@ -1451,10 +1451,12 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = { .i2c_algo = &cxusb_i2c_algo, - .rc_interval = 100, - .rc_key_map = ir_codes_dvico_portable_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_portable_table), - .rc_query = cxusb_rc_query, + .rc.legacy = { + .rc_interval = 100, + .rc_key_map = ir_codes_dvico_portable_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_portable_table), + .rc_query = cxusb_rc_query, + }, .generic_bulk_ctrl_endpoint = 0x01, @@ -1502,10 +1504,12 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = { .i2c_algo = &cxusb_i2c_algo, - .rc_interval = 150, - .rc_key_map = ir_codes_dvico_mce_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_mce_table), - .rc_query = cxusb_rc_query, + .rc.legacy = { + .rc_interval = 150, + .rc_key_map = ir_codes_dvico_mce_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_mce_table), + .rc_query = cxusb_rc_query, + }, .generic_bulk_ctrl_endpoint = 0x01, @@ -1561,10 +1565,12 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties = { .i2c_algo = &cxusb_i2c_algo, - .rc_interval = 100, - .rc_key_map = ir_codes_dvico_portable_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_portable_table), - .rc_query = cxusb_rc_query, + .rc.legacy = { + .rc_interval = 100, + .rc_key_map = ir_codes_dvico_portable_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_portable_table), + .rc_query = cxusb_rc_query, + }, .generic_bulk_ctrl_endpoint = 0x01, .num_device_descs = 1, @@ -1611,10 +1617,12 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = { .i2c_algo = &cxusb_i2c_algo, - .rc_interval = 100, - .rc_key_map = ir_codes_dvico_portable_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_portable_table), - .rc_query = cxusb_rc_query, + .rc.legacy = { + .rc_interval = 100, + .rc_key_map = ir_codes_dvico_portable_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_portable_table), + .rc_query = cxusb_rc_query, + }, .generic_bulk_ctrl_endpoint = 0x01, @@ -1660,10 +1668,12 @@ static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties = { .generic_bulk_ctrl_endpoint = 0x01, - .rc_interval = 100, - .rc_key_map = ir_codes_dvico_mce_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_mce_table), - .rc_query = cxusb_bluebird2_rc_query, + .rc.legacy = { + .rc_interval = 100, + .rc_key_map = ir_codes_dvico_mce_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_mce_table), + .rc_query = cxusb_bluebird2_rc_query, + }, .num_device_descs = 1, .devices = { @@ -1708,10 +1718,12 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties = { .generic_bulk_ctrl_endpoint = 0x01, - .rc_interval = 100, - .rc_key_map = ir_codes_dvico_portable_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_portable_table), - .rc_query = cxusb_bluebird2_rc_query, + .rc.legacy = { + .rc_interval = 100, + .rc_key_map = ir_codes_dvico_portable_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_portable_table), + .rc_query = cxusb_bluebird2_rc_query, + }, .num_device_descs = 1, .devices = { @@ -1758,10 +1770,12 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_prope .generic_bulk_ctrl_endpoint = 0x01, - .rc_interval = 100, - .rc_key_map = ir_codes_dvico_portable_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_portable_table), - .rc_query = cxusb_rc_query, + .rc.legacy = { + .rc_interval = 100, + .rc_key_map = ir_codes_dvico_portable_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_portable_table), + .rc_query = cxusb_rc_query, + }, .num_device_descs = 1, .devices = { @@ -1849,10 +1863,12 @@ struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties = { .generic_bulk_ctrl_endpoint = 0x01, - .rc_interval = 100, - .rc_key_map = ir_codes_dvico_mce_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_mce_table), - .rc_query = cxusb_rc_query, + .rc.legacy = { + .rc_interval = 100, + .rc_key_map = ir_codes_dvico_mce_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dvico_mce_table), + .rc_query = cxusb_rc_query, + }, .num_device_descs = 1, .devices = { @@ -1897,10 +1913,12 @@ static struct dvb_usb_device_properties cxusb_d680_dmb_properties = { .generic_bulk_ctrl_endpoint = 0x01, - .rc_interval = 100, - .rc_key_map = ir_codes_d680_dmb_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_d680_dmb_table), - .rc_query = cxusb_d680_dmb_rc_query, + .rc.legacy = { + .rc_interval = 100, + .rc_key_map = ir_codes_d680_dmb_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_d680_dmb_table), + .rc_query = cxusb_d680_dmb_rc_query, + }, .num_device_descs = 1, .devices = { @@ -1946,10 +1964,12 @@ static struct dvb_usb_device_properties cxusb_mygica_d689_properties = { .generic_bulk_ctrl_endpoint = 0x01, - .rc_interval = 100, - .rc_key_map = ir_codes_d680_dmb_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_d680_dmb_table), - .rc_query = cxusb_d680_dmb_rc_query, + .rc.legacy = { + .rc_interval = 100, + .rc_key_map = ir_codes_d680_dmb_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_d680_dmb_table), + .rc_query = cxusb_d680_dmb_rc_query, + }, .num_device_descs = 1, .devices = { diff --git a/drivers/media/dvb/dvb-usb/dib0700.h b/drivers/media/dvb/dvb-usb/dib0700.h index 83fc24a..c2c9d23 100644 --- a/drivers/media/dvb/dvb-usb/dib0700.h +++ b/drivers/media/dvb/dvb-usb/dib0700.h @@ -60,6 +60,7 @@ extern int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff); extern struct i2c_algorithm dib0700_i2c_algo; extern int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props, struct dvb_usb_device_description **desc, int *cold); +extern int dib0700_change_protocol(void *priv, u64 ir_type); extern int dib0700_device_count; extern int dvb_usb_dib0700_ir_proto; diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c index 4f961d2..fe81834 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_core.c +++ b/drivers/media/dvb/dvb-usb/dib0700_core.c @@ -13,10 +13,6 @@ int dvb_usb_dib0700_debug; module_param_named(debug,dvb_usb_dib0700_debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level (1=info,2=fw,4=fwdata,8=data (or-able))." DVB_USB_DEBUG_STATUS); -int dvb_usb_dib0700_ir_proto = 1; -module_param(dvb_usb_dib0700_ir_proto, int, 0644); -MODULE_PARM_DESC(dvb_usb_dib0700_ir_proto, "set ir protocol (0=NEC, 1=RC5 (default), 2=RC6)."); - static int nb_packet_buffer_size = 21; module_param(nb_packet_buffer_size, int, 0644); MODULE_PARM_DESC(nb_packet_buffer_size, @@ -53,7 +49,7 @@ static int dib0700_ctrl_wr(struct dvb_usb_device *d, u8 *tx, u8 txlen) int status; deb_data(">>> "); - debug_dump(tx,txlen,deb_data); + debug_dump(tx, txlen, deb_data); status = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev,0), tx[0], USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, tx, txlen, @@ -98,7 +94,7 @@ int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen deb_info("ep 0 read error (status = %d)\n",status); deb_data("<<< "); - debug_dump(rx,rxlen,deb_data); + debug_dump(rx, rxlen, deb_data); return status; /* length in case of success */ } @@ -106,28 +102,29 @@ int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val) { u8 buf[3] = { REQUEST_SET_GPIO, gpio, ((gpio_dir & 0x01) << 7) | ((gpio_val & 0x01) << 6) }; - return dib0700_ctrl_wr(d,buf,3); + return dib0700_ctrl_wr(d, buf, sizeof(buf)); } static int dib0700_set_usb_xfer_len(struct dvb_usb_device *d, u16 nb_ts_packets) { - struct dib0700_state *st = d->priv; - u8 b[3]; - int ret; - - if (st->fw_version >= 0x10201) { - b[0] = REQUEST_SET_USB_XFER_LEN; - b[1] = (nb_ts_packets >> 8)&0xff; - b[2] = nb_ts_packets & 0xff; - - deb_info("set the USB xfer len to %i Ts packet\n", nb_ts_packets); - - ret = dib0700_ctrl_wr(d, b, 3); - } else { - deb_info("this firmware does not allow to change the USB xfer len\n"); - ret = -EIO; - } - return ret; + struct dib0700_state *st = d->priv; + u8 b[3]; + int ret; + + if (st->fw_version >= 0x10201) { + b[0] = REQUEST_SET_USB_XFER_LEN; + b[1] = (nb_ts_packets >> 8) & 0xff; + b[2] = nb_ts_packets & 0xff; + + deb_info("set the USB xfer len to %i Ts packet\n", nb_ts_packets); + + ret = dib0700_ctrl_wr(d, b, sizeof(b)); + } else { + deb_info("this firmware does not allow to change the USB xfer len\n"); + ret = -EIO; + } + + return ret; } /* @@ -178,7 +175,8 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg, value = ((en_start << 7) | (en_stop << 6) | (msg[i].len & 0x3F)) << 8 | i2c_dest; /* I2C ctrl + FE bus; */ - index = ((gen_mode<<6)&0xC0) | ((bus_mode<<4)&0x30); + index = ((gen_mode << 6) & 0xC0) | + ((bus_mode << 4) & 0x30); result = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), @@ -198,11 +196,12 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg, } else { /* Write request */ buf[0] = REQUEST_NEW_I2C_WRITE; - buf[1] = (msg[i].addr << 1); + buf[1] = msg[i].addr << 1; buf[2] = (en_start << 7) | (en_stop << 6) | (msg[i].len & 0x3F); /* I2C ctrl + FE bus; */ - buf[3] = ((gen_mode<<6)&0xC0) | ((bus_mode<<4)&0x30); + buf[3] = ((gen_mode << 6) & 0xC0) | + ((bus_mode << 4) & 0x30); /* The Actual i2c payload */ memcpy(&buf[4], msg[i].buf, msg[i].len); @@ -240,7 +239,7 @@ static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap, for (i = 0; i < num; i++) { /* fill in the address */ - buf[1] = (msg[i].addr << 1); + buf[1] = msg[i].addr << 1; /* fill the buffer */ memcpy(&buf[2], msg[i].buf, msg[i].len); @@ -368,7 +367,8 @@ int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw u8 buf[260]; while ((ret = dvb_usb_get_hexline(fw, &hx, &pos)) > 0) { - deb_fwdata("writing to address 0x%08x (buffer: 0x%02x %02x)\n",hx.addr, hx.len, hx.chk); + deb_fwdata("writing to address 0x%08x (buffer: 0x%02x %02x)\n", + hx.addr, hx.len, hx.chk); buf[0] = hx.len; buf[1] = (hx.addr >> 8) & 0xff; @@ -408,16 +408,16 @@ int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw REQUEST_GET_VERSION, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, b, sizeof(b), USB_CTRL_GET_TIMEOUT); - fw_version = (b[8] << 24) | (b[9] << 16) | (b[10] << 8) | b[11]; + fw_version = (b[8] << 24) | (b[9] << 16) | (b[10] << 8) | b[11]; /* set the buffer size - DVB-USB is allocating URB buffers * only after the firwmare download was successful */ for (i = 0; i < dib0700_device_count; i++) { for (adap_num = 0; adap_num < dib0700_devices[i].num_adapters; adap_num++) { - if (fw_version >= 0x10201) + if (fw_version >= 0x10201) { dib0700_devices[i].adapter[adap_num].stream.u.bulk.buffersize = 188*nb_packet_buffer_size; - else { + } else { /* for fw version older than 1.20.1, * the buffersize has to be n times 512 */ dib0700_devices[i].adapter[adap_num].stream.u.bulk.buffersize = ((188*nb_packet_buffer_size+188/2)/512)*512; @@ -453,7 +453,7 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) if (st->disable_streaming_master_mode == 1) b[2] = 0x00; else - b[2] = (0x01 << 4); /* Master mode */ + b[2] = 0x01 << 4; /* Master mode */ b[3] = 0x00; @@ -466,11 +466,44 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) b[2] |= st->channel_state; - deb_info("data for streaming: %x %x\n",b[1],b[2]); + deb_info("data for streaming: %x %x\n", b[1], b[2]); return dib0700_ctrl_wr(adap->dev, b, 4); } +int dib0700_change_protocol(void *priv, u64 ir_type) +{ + struct dvb_usb_device *d = priv; + struct dib0700_state *st = d->priv; + u8 rc_setup[3] = { REQUEST_SET_RC, 0, 0 }; + int new_proto, ret; + + /* Set the IR mode */ + if (ir_type == IR_TYPE_RC5) + new_proto = 1; + else if (ir_type == IR_TYPE_NEC) + new_proto = 0; + else if (ir_type == IR_TYPE_RC6) { + if (st->fw_version < 0x10200) + return -EINVAL; + + new_proto = 2; + } else + return -EINVAL; + + rc_setup[1] = new_proto; + + ret = dib0700_ctrl_wr(d, rc_setup, sizeof(rc_setup)); + if (ret < 0) { + err("ir protocol setup failed"); + return ret; + } + + d->props.rc.core.protocol = ir_type; + + return ret; +} + /* Number of keypresses to ignore before start repeating */ #define RC_REPEAT_DELAY_V1_20 10 @@ -478,7 +511,13 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) struct dib0700_rc_response { u8 report_id; u8 data_state; - u16 system; + union { + u16 system16; + struct { + u8 system; + u8 not_system; + }; + }; u8 data; u8 not_data; }; @@ -487,14 +526,10 @@ struct dib0700_rc_response { static void dib0700_rc_urb_completion(struct urb *purb) { struct dvb_usb_device *d = purb->context; - struct dvb_usb_rc_key *keymap; struct dib0700_state *st; - struct dib0700_rc_response poll_reply; - u8 *buf; - int found = 0; - u32 event; - int state; - int i; + struct dib0700_rc_response *poll_reply; + u32 uninitialized_var(keycode); + u8 toggle; deb_info("%s()\n", __func__); if (d == NULL) @@ -506,9 +541,8 @@ static void dib0700_rc_urb_completion(struct urb *purb) return; } - keymap = d->props.rc_key_map; st = d->priv; - buf = (u8 *)purb->transfer_buffer; + poll_reply = purb->transfer_buffer; if (purb->status < 0) { deb_info("discontinuing polling\n"); @@ -521,104 +555,52 @@ static void dib0700_rc_urb_completion(struct urb *purb) goto resubmit; } - /* Set initial results in case we exit the function early */ - event = 0; - state = REMOTE_NO_KEY_PRESSED; - - deb_data("IR raw %02X %02X %02X %02X %02X %02X (len %d)\n", buf[0], - buf[1], buf[2], buf[3], buf[4], buf[5], purb->actual_length); + deb_data("IR ID = %02X state = %02X System = %02X %02X Cmd = %02X %02X (len %d)\n", + poll_reply->report_id, poll_reply->data_state, + poll_reply->system, poll_reply->not_system, + poll_reply->data, poll_reply->not_data, + purb->actual_length); - switch (dvb_usb_dib0700_ir_proto) { - case 0: - /* NEC Protocol */ - poll_reply.report_id = 0; - poll_reply.data_state = 1; - poll_reply.system = buf[2]; - poll_reply.data = buf[4]; - poll_reply.not_data = buf[5]; + switch (d->props.rc.core.protocol) { + case IR_TYPE_NEC: + toggle = 0; /* NEC protocol sends repeat code as 0 0 0 FF */ - if ((poll_reply.system == 0x00) && (poll_reply.data == 0x00) - && (poll_reply.not_data == 0xff)) { - poll_reply.data_state = 2; + if ((poll_reply->system == 0x00) && (poll_reply->data == 0x00) + && (poll_reply->not_data == 0xff)) { + poll_reply->data_state = 2; break; } + + if ((poll_reply->system ^ poll_reply->not_system) != 0xff) { + deb_data("NEC extended protocol\n"); + /* NEC extended code - 24 bits */ + keycode = poll_reply->system16 << 8 | poll_reply->data; + } else { + deb_data("NEC normal protocol\n"); + /* normal NEC code - 16 bits */ + keycode = poll_reply->system << 8 | poll_reply->data; + } + break; default: + deb_data("RC5 protocol\n"); /* RC5 Protocol */ - poll_reply.report_id = buf[0]; - poll_reply.data_state = buf[1]; - poll_reply.system = (buf[2] << 8) | buf[3]; - poll_reply.data = buf[4]; - poll_reply.not_data = buf[5]; + toggle = poll_reply->report_id; + keycode = poll_reply->system16 << 8 | poll_reply->data; + break; } - if ((poll_reply.data + poll_reply.not_data) != 0xff) { + if ((poll_reply->data + poll_reply->not_data) != 0xff) { /* Key failed integrity check */ err("key failed integrity check: %04x %02x %02x", - poll_reply.system, - poll_reply.data, poll_reply.not_data); - goto resubmit; - } - - deb_data("rid=%02x ds=%02x sm=%04x d=%02x nd=%02x\n", - poll_reply.report_id, poll_reply.data_state, - poll_reply.system, poll_reply.data, poll_reply.not_data); - - /* Find the key in the map */ - for (i = 0; i < d->props.rc_key_map_size; i++) { - if (rc5_custom(&keymap[i]) == (poll_reply.system & 0xff) && - rc5_data(&keymap[i]) == poll_reply.data) { - event = keymap[i].event; - found = 1; - break; - } - } - - if (found == 0) { - err("Unknown remote controller key: %04x %02x %02x", - poll_reply.system, poll_reply.data, poll_reply.not_data); - d->last_event = 0; + poll_reply->system, + poll_reply->data, poll_reply->not_data); goto resubmit; } - if (poll_reply.data_state == 1) { - /* New key hit */ - st->rc_counter = 0; - event = keymap[i].event; - state = REMOTE_KEY_PRESSED; - d->last_event = keymap[i].event; - } else if (poll_reply.data_state == 2) { - /* Key repeated */ - st->rc_counter++; - - /* prevents unwanted double hits */ - if (st->rc_counter > RC_REPEAT_DELAY_V1_20) { - event = d->last_event; - state = REMOTE_KEY_PRESSED; - st->rc_counter = RC_REPEAT_DELAY_V1_20; - } - } else { - err("Unknown data state [%d]", poll_reply.data_state); - } - - switch (state) { - case REMOTE_NO_KEY_PRESSED: - break; - case REMOTE_KEY_PRESSED: - deb_info("key pressed\n"); - d->last_event = event; - case REMOTE_KEY_REPEAT: - deb_info("key repeated\n"); - input_event(d->rc_input_dev, EV_KEY, event, 1); - input_sync(d->rc_input_dev); - input_event(d->rc_input_dev, EV_KEY, d->last_event, 0); - input_sync(d->rc_input_dev); - break; - default: - break; - } + ir_keydown(d->rc_input_dev, keycode, toggle); resubmit: /* Clean the buffer before we requeue */ @@ -631,21 +613,10 @@ resubmit: int dib0700_rc_setup(struct dvb_usb_device *d) { struct dib0700_state *st = d->priv; - u8 rc_setup[3] = {REQUEST_SET_RC, dvb_usb_dib0700_ir_proto, 0}; struct urb *purb; int ret; - int i; - - if (d->props.rc_key_map == NULL) - return 0; - - /* Set the IR mode */ - i = dib0700_ctrl_wr(d, rc_setup, 3); - if (i<0) { - err("ir protocol setup failed"); - return -1; - } + /* Poll-based. Don't initialize bulk mode */ if (st->fw_version < 0x10200) return 0; @@ -653,14 +624,14 @@ int dib0700_rc_setup(struct dvb_usb_device *d) purb = usb_alloc_urb(0, GFP_KERNEL); if (purb == NULL) { err("rc usb alloc urb failed\n"); - return -1; + return -ENOMEM; } purb->transfer_buffer = kzalloc(RC_MSG_SIZE_V1_20, GFP_KERNEL); if (purb->transfer_buffer == NULL) { err("rc kzalloc failed\n"); usb_free_urb(purb); - return -1; + return -ENOMEM; } purb->status = -EINPROGRESS; @@ -669,12 +640,10 @@ int dib0700_rc_setup(struct dvb_usb_device *d) dib0700_rc_urb_completion, d); ret = usb_submit_urb(purb, GFP_ATOMIC); - if (ret != 0) { + if (ret) err("rc submit urb failed\n"); - return -1; - } - return 0; + return ret; } static int dib0700_probe(struct usb_interface *intf, @@ -698,6 +667,15 @@ static int dib0700_probe(struct usb_interface *intf, st->fw_version = fw_version; st->nb_packet_buffer_size = (u32)nb_packet_buffer_size; + /* Disable polling mode on newer firmwares */ + if (st->fw_version >= 0x10200) + dev->props.rc.core.bulk_mode = true; + else + dev->props.rc.core.bulk_mode = false; + + /* Need a higher delay, to avoid wrong repeat */ + dev->rc_input_dev->rep[REP_DELAY] = 500; + dib0700_rc_setup(dev); return 0; diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 800800a..f634d2e 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -473,16 +473,19 @@ static u8 rc_request[] = { REQUEST_POLL_RC, 0 }; /* Number of keypresses to ignore before start repeating */ #define RC_REPEAT_DELAY 6 -static int dib0700_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +/* + * This function is used only when firmware is < 1.20 version. Newer + * firmwares use bulk mode, with functions implemented at dib0700_core, + * at dib0700_rc_urb_completion() + */ +static int dib0700_rc_query_old_firmware(struct dvb_usb_device *d) { u8 key[4]; + u32 keycode; + u8 toggle; int i; - struct dvb_usb_rc_key *keymap = d->props.rc_key_map; struct dib0700_state *st = d->priv; - *event = 0; - *state = REMOTE_NO_KEY_PRESSED; - if (st->fw_version >= 0x10200) { /* For 1.20 firmware , We need to keep the RC polling callback so we can reuse the input device setup in @@ -491,348 +494,45 @@ static int dib0700_rc_query(struct dvb_usb_device *d, u32 *event, int *state) return 0; } - i=dib0700_ctrl_rd(d,rc_request,2,key,4); - if (i<=0) { + i = dib0700_ctrl_rd(d, rc_request, 2, key, 4); + if (i <= 0) { err("RC Query Failed"); return -1; } /* losing half of KEY_0 events from Philipps rc5 remotes.. */ - if (key[0]==0 && key[1]==0 && key[2]==0 && key[3]==0) return 0; + if (key[0] == 0 && key[1] == 0 && key[2] == 0 && key[3] == 0) + return 0; /* info("%d: %2X %2X %2X %2X",dvb_usb_dib0700_ir_proto,(int)key[3-2],(int)key[3-3],(int)key[3-1],(int)key[3]); */ dib0700_rc_setup(d); /* reset ir sensor data to prevent false events */ - switch (dvb_usb_dib0700_ir_proto) { - case 0: { + d->last_event = 0; + switch (d->props.rc.core.protocol) { + case IR_TYPE_NEC: /* NEC protocol sends repeat code as 0 0 0 FF */ if ((key[3-2] == 0x00) && (key[3-3] == 0x00) && - (key[3] == 0xFF)) { - st->rc_counter++; - if (st->rc_counter > RC_REPEAT_DELAY) { - *event = d->last_event; - *state = REMOTE_KEY_PRESSED; - st->rc_counter = RC_REPEAT_DELAY; - } - return 0; - } - for (i=0;i<d->props.rc_key_map_size; i++) { - if (rc5_custom(&keymap[i]) == key[3-2] && - rc5_data(&keymap[i]) == key[3-3]) { - st->rc_counter = 0; - *event = keymap[i].event; - *state = REMOTE_KEY_PRESSED; - d->last_event = keymap[i].event; - return 0; - } + (key[3] == 0xff)) + keycode = d->last_event; + else { + keycode = key[3-2] << 8 | key[3-3]; + d->last_event = keycode; } + + ir_keydown(d->rc_input_dev, keycode, 0); break; - } - default: { + default: /* RC-5 protocol changes toggle bit on new keypress */ - for (i = 0; i < d->props.rc_key_map_size; i++) { - if (rc5_custom(&keymap[i]) == key[3-2] && - rc5_data(&keymap[i]) == key[3-3]) { - if (d->last_event == keymap[i].event && - key[3-1] == st->rc_toggle) { - st->rc_counter++; - /* prevents unwanted double hits */ - if (st->rc_counter > RC_REPEAT_DELAY) { - *event = d->last_event; - *state = REMOTE_KEY_PRESSED; - st->rc_counter = RC_REPEAT_DELAY; - } - - return 0; - } - st->rc_counter = 0; - *event = keymap[i].event; - *state = REMOTE_KEY_PRESSED; - st->rc_toggle = key[3-1]; - d->last_event = keymap[i].event; - return 0; - } - } + keycode = key[3-2] << 8 | key[3-3]; + toggle = key[3-1]; + ir_keydown(d->rc_input_dev, keycode, toggle); + break; } - } - err("Unknown remote controller key: %2X %2X %2X %2X", (int) key[3-2], (int) key[3-3], (int) key[3-1], (int) key[3]); - d->last_event = 0; return 0; } -static struct dvb_usb_rc_key ir_codes_dib0700_table[] = { - /* Key codes for the tiny Pinnacle remote*/ - { 0x0700, KEY_MUTE }, - { 0x0701, KEY_MENU }, /* Pinnacle logo */ - { 0x0739, KEY_POWER }, - { 0x0703, KEY_VOLUMEUP }, - { 0x0709, KEY_VOLUMEDOWN }, - { 0x0706, KEY_CHANNELUP }, - { 0x070c, KEY_CHANNELDOWN }, - { 0x070f, KEY_1 }, - { 0x0715, KEY_2 }, - { 0x0710, KEY_3 }, - { 0x0718, KEY_4 }, - { 0x071b, KEY_5 }, - { 0x071e, KEY_6 }, - { 0x0711, KEY_7 }, - { 0x0721, KEY_8 }, - { 0x0712, KEY_9 }, - { 0x0727, KEY_0 }, - { 0x0724, KEY_SCREEN }, /* 'Square' key */ - { 0x072a, KEY_TEXT }, /* 'T' key */ - { 0x072d, KEY_REWIND }, - { 0x0730, KEY_PLAY }, - { 0x0733, KEY_FASTFORWARD }, - { 0x0736, KEY_RECORD }, - { 0x073c, KEY_STOP }, - { 0x073f, KEY_CANCEL }, /* '?' key */ - /* Key codes for the Terratec Cinergy DT XS Diversity, similar to cinergyT2.c */ - { 0xeb01, KEY_POWER }, - { 0xeb02, KEY_1 }, - { 0xeb03, KEY_2 }, - { 0xeb04, KEY_3 }, - { 0xeb05, KEY_4 }, - { 0xeb06, KEY_5 }, - { 0xeb07, KEY_6 }, - { 0xeb08, KEY_7 }, - { 0xeb09, KEY_8 }, - { 0xeb0a, KEY_9 }, - { 0xeb0b, KEY_VIDEO }, - { 0xeb0c, KEY_0 }, - { 0xeb0d, KEY_REFRESH }, - { 0xeb0f, KEY_EPG }, - { 0xeb10, KEY_UP }, - { 0xeb11, KEY_LEFT }, - { 0xeb12, KEY_OK }, - { 0xeb13, KEY_RIGHT }, - { 0xeb14, KEY_DOWN }, - { 0xeb16, KEY_INFO }, - { 0xeb17, KEY_RED }, - { 0xeb18, KEY_GREEN }, - { 0xeb19, KEY_YELLOW }, - { 0xeb1a, KEY_BLUE }, - { 0xeb1b, KEY_CHANNELUP }, - { 0xeb1c, KEY_VOLUMEUP }, - { 0xeb1d, KEY_MUTE }, - { 0xeb1e, KEY_VOLUMEDOWN }, - { 0xeb1f, KEY_CHANNELDOWN }, - { 0xeb40, KEY_PAUSE }, - { 0xeb41, KEY_HOME }, - { 0xeb42, KEY_MENU }, /* DVD Menu */ - { 0xeb43, KEY_SUBTITLE }, - { 0xeb44, KEY_TEXT }, /* Teletext */ - { 0xeb45, KEY_DELETE }, - { 0xeb46, KEY_TV }, - { 0xeb47, KEY_DVD }, - { 0xeb48, KEY_STOP }, - { 0xeb49, KEY_VIDEO }, - { 0xeb4a, KEY_AUDIO }, /* Music */ - { 0xeb4b, KEY_SCREEN }, /* Pic */ - { 0xeb4c, KEY_PLAY }, - { 0xeb4d, KEY_BACK }, - { 0xeb4e, KEY_REWIND }, - { 0xeb4f, KEY_FASTFORWARD }, - { 0xeb54, KEY_PREVIOUS }, - { 0xeb58, KEY_RECORD }, - { 0xeb5c, KEY_NEXT }, - - /* Key codes for the Haupauge WinTV Nova-TD, copied from nova-t-usb2.c (Nova-T USB2) */ - { 0x1e00, KEY_0 }, - { 0x1e01, KEY_1 }, - { 0x1e02, KEY_2 }, - { 0x1e03, KEY_3 }, - { 0x1e04, KEY_4 }, - { 0x1e05, KEY_5 }, - { 0x1e06, KEY_6 }, - { 0x1e07, KEY_7 }, - { 0x1e08, KEY_8 }, - { 0x1e09, KEY_9 }, - { 0x1e0a, KEY_KPASTERISK }, - { 0x1e0b, KEY_RED }, - { 0x1e0c, KEY_RADIO }, - { 0x1e0d, KEY_MENU }, - { 0x1e0e, KEY_GRAVE }, /* # */ - { 0x1e0f, KEY_MUTE }, - { 0x1e10, KEY_VOLUMEUP }, - { 0x1e11, KEY_VOLUMEDOWN }, - { 0x1e12, KEY_CHANNEL }, - { 0x1e14, KEY_UP }, - { 0x1e15, KEY_DOWN }, - { 0x1e16, KEY_LEFT }, - { 0x1e17, KEY_RIGHT }, - { 0x1e18, KEY_VIDEO }, - { 0x1e19, KEY_AUDIO }, - { 0x1e1a, KEY_MEDIA }, - { 0x1e1b, KEY_EPG }, - { 0x1e1c, KEY_TV }, - { 0x1e1e, KEY_NEXT }, - { 0x1e1f, KEY_BACK }, - { 0x1e20, KEY_CHANNELUP }, - { 0x1e21, KEY_CHANNELDOWN }, - { 0x1e24, KEY_LAST }, /* Skip backwards */ - { 0x1e25, KEY_OK }, - { 0x1e29, KEY_BLUE}, - { 0x1e2e, KEY_GREEN }, - { 0x1e30, KEY_PAUSE }, - { 0x1e32, KEY_REWIND }, - { 0x1e34, KEY_FASTFORWARD }, - { 0x1e35, KEY_PLAY }, - { 0x1e36, KEY_STOP }, - { 0x1e37, KEY_RECORD }, - { 0x1e38, KEY_YELLOW }, - { 0x1e3b, KEY_GOTO }, - { 0x1e3d, KEY_POWER }, - - /* Key codes for the Leadtek Winfast DTV Dongle */ - { 0x0042, KEY_POWER }, - { 0x077c, KEY_TUNER }, - { 0x0f4e, KEY_PRINT }, /* PREVIEW */ - { 0x0840, KEY_SCREEN }, /* full screen toggle*/ - { 0x0f71, KEY_DOT }, /* frequency */ - { 0x0743, KEY_0 }, - { 0x0c41, KEY_1 }, - { 0x0443, KEY_2 }, - { 0x0b7f, KEY_3 }, - { 0x0e41, KEY_4 }, - { 0x0643, KEY_5 }, - { 0x097f, KEY_6 }, - { 0x0d7e, KEY_7 }, - { 0x057c, KEY_8 }, - { 0x0a40, KEY_9 }, - { 0x0e4e, KEY_CLEAR }, - { 0x047c, KEY_CHANNEL }, /* show channel number */ - { 0x0f41, KEY_LAST }, /* recall */ - { 0x0342, KEY_MUTE }, - { 0x064c, KEY_RESERVED }, /* PIP button*/ - { 0x0172, KEY_SHUFFLE }, /* SNAPSHOT */ - { 0x0c4e, KEY_PLAYPAUSE }, /* TIMESHIFT */ - { 0x0b70, KEY_RECORD }, - { 0x037d, KEY_VOLUMEUP }, - { 0x017d, KEY_VOLUMEDOWN }, - { 0x0242, KEY_CHANNELUP }, - { 0x007d, KEY_CHANNELDOWN }, - - /* Key codes for Nova-TD "credit card" remote control. */ - { 0x1d00, KEY_0 }, - { 0x1d01, KEY_1 }, - { 0x1d02, KEY_2 }, - { 0x1d03, KEY_3 }, - { 0x1d04, KEY_4 }, - { 0x1d05, KEY_5 }, - { 0x1d06, KEY_6 }, - { 0x1d07, KEY_7 }, - { 0x1d08, KEY_8 }, - { 0x1d09, KEY_9 }, - { 0x1d0a, KEY_TEXT }, - { 0x1d0d, KEY_MENU }, - { 0x1d0f, KEY_MUTE }, - { 0x1d10, KEY_VOLUMEUP }, - { 0x1d11, KEY_VOLUMEDOWN }, - { 0x1d12, KEY_CHANNEL }, - { 0x1d14, KEY_UP }, - { 0x1d15, KEY_DOWN }, - { 0x1d16, KEY_LEFT }, - { 0x1d17, KEY_RIGHT }, - { 0x1d1c, KEY_TV }, - { 0x1d1e, KEY_NEXT }, - { 0x1d1f, KEY_BACK }, - { 0x1d20, KEY_CHANNELUP }, - { 0x1d21, KEY_CHANNELDOWN }, - { 0x1d24, KEY_LAST }, - { 0x1d25, KEY_OK }, - { 0x1d30, KEY_PAUSE }, - { 0x1d32, KEY_REWIND }, - { 0x1d34, KEY_FASTFORWARD }, - { 0x1d35, KEY_PLAY }, - { 0x1d36, KEY_STOP }, - { 0x1d37, KEY_RECORD }, - { 0x1d3b, KEY_GOTO }, - { 0x1d3d, KEY_POWER }, - - /* Key codes for the Pixelview SBTVD remote (proto NEC) */ - { 0x8613, KEY_MUTE }, - { 0x8612, KEY_POWER }, - { 0x8601, KEY_1 }, - { 0x8602, KEY_2 }, - { 0x8603, KEY_3 }, - { 0x8604, KEY_4 }, - { 0x8605, KEY_5 }, - { 0x8606, KEY_6 }, - { 0x8607, KEY_7 }, - { 0x8608, KEY_8 }, - { 0x8609, KEY_9 }, - { 0x8600, KEY_0 }, - { 0x860d, KEY_CHANNELUP }, - { 0x8619, KEY_CHANNELDOWN }, - { 0x8610, KEY_VOLUMEUP }, - { 0x860c, KEY_VOLUMEDOWN }, - - { 0x860a, KEY_CAMERA }, - { 0x860b, KEY_ZOOM }, - { 0x861b, KEY_BACKSPACE }, - { 0x8615, KEY_ENTER }, - - { 0x861d, KEY_UP }, - { 0x861e, KEY_DOWN }, - { 0x860e, KEY_LEFT }, - { 0x860f, KEY_RIGHT }, - - { 0x8618, KEY_RECORD }, - { 0x861a, KEY_STOP }, - - /* Key codes for the EvolutePC TVWay+ remote (proto NEC) */ - { 0x7a00, KEY_MENU }, - { 0x7a01, KEY_RECORD }, - { 0x7a02, KEY_PLAY }, - { 0x7a03, KEY_STOP }, - { 0x7a10, KEY_CHANNELUP }, - { 0x7a11, KEY_CHANNELDOWN }, - { 0x7a12, KEY_VOLUMEUP }, - { 0x7a13, KEY_VOLUMEDOWN }, - { 0x7a40, KEY_POWER }, - { 0x7a41, KEY_MUTE }, - - /* Key codes for the Elgato EyeTV Diversity silver remote, - set dvb_usb_dib0700_ir_proto=0 */ - { 0x4501, KEY_POWER }, - { 0x4502, KEY_MUTE }, - { 0x4503, KEY_1 }, - { 0x4504, KEY_2 }, - { 0x4505, KEY_3 }, - { 0x4506, KEY_4 }, - { 0x4507, KEY_5 }, - { 0x4508, KEY_6 }, - { 0x4509, KEY_7 }, - { 0x450a, KEY_8 }, - { 0x450b, KEY_9 }, - { 0x450c, KEY_LAST }, - { 0x450d, KEY_0 }, - { 0x450e, KEY_ENTER }, - { 0x450f, KEY_RED }, - { 0x4510, KEY_CHANNELUP }, - { 0x4511, KEY_GREEN }, - { 0x4512, KEY_VOLUMEDOWN }, - { 0x4513, KEY_OK }, - { 0x4514, KEY_VOLUMEUP }, - { 0x4515, KEY_YELLOW }, - { 0x4516, KEY_CHANNELDOWN }, - { 0x4517, KEY_BLUE }, - { 0x4518, KEY_LEFT }, /* Skip backwards */ - { 0x4519, KEY_PLAYPAUSE }, - { 0x451a, KEY_RIGHT }, /* Skip forward */ - { 0x451b, KEY_REWIND }, - { 0x451c, KEY_L }, /* Live */ - { 0x451d, KEY_FASTFORWARD }, - { 0x451e, KEY_STOP }, /* 'Reveal' for Teletext */ - { 0x451f, KEY_MENU }, /* KEY_TEXT for Teletext */ - { 0x4540, KEY_RECORD }, /* Font 'Size' for Teletext */ - { 0x4541, KEY_SCREEN }, /* Full screen toggle, 'Hold' for Teletext */ - { 0x4542, KEY_SELECT }, /* Select video input, 'Select' for Teletext */ -}; - /* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */ static struct dibx000_agc_config stk7700p_7000m_mt2060_agc_config = { BAND_UHF | BAND_VHF, @@ -2168,10 +1868,17 @@ struct dvb_usb_device_properties dib0700_devices[] = { } }, - .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = ir_codes_dib0700_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), - .rc_query = dib0700_rc_query + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .rc_query = dib0700_rc_query_old_firmware, + .rc_props = { + .allowed_protos = IR_TYPE_RC5 | + IR_TYPE_RC6 | + IR_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, .num_adapters = 2, @@ -2197,10 +1904,17 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, }, - .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = ir_codes_dib0700_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), - .rc_query = dib0700_rc_query + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .rc_query = dib0700_rc_query_old_firmware, + .rc_props = { + .allowed_protos = IR_TYPE_RC5 | + IR_TYPE_RC6 | + IR_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, .num_adapters = 2, @@ -2251,11 +1965,17 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, - .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = ir_codes_dib0700_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), - .rc_query = dib0700_rc_query - + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .rc_query = dib0700_rc_query_old_firmware, + .rc_props = { + .allowed_protos = IR_TYPE_RC5 | + IR_TYPE_RC6 | + IR_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, .num_adapters = 1, @@ -2288,10 +2008,18 @@ struct dvb_usb_device_properties dib0700_devices[] = { } }, - .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = ir_codes_dib0700_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), - .rc_query = dib0700_rc_query + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .rc_props = { + .allowed_protos = IR_TYPE_RC5 | + IR_TYPE_RC6 | + IR_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, .num_adapters = 1, @@ -2358,11 +2086,18 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, }, - .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = ir_codes_dib0700_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), - .rc_query = dib0700_rc_query - + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .rc_props = { + .allowed_protos = IR_TYPE_RC5 | + IR_TYPE_RC6 | + IR_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, .num_adapters = 1, @@ -2397,11 +2132,18 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, }, - .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = ir_codes_dib0700_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), - .rc_query = dib0700_rc_query - + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .rc_props = { + .allowed_protos = IR_TYPE_RC5 | + IR_TYPE_RC6 | + IR_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, .num_adapters = 2, @@ -2431,7 +2173,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { } }, - .num_device_descs = 7, + .num_device_descs = 6, .devices = { { "DiBcom STK7070PD reference design", { &dib0700_usb_id_table[17], NULL }, @@ -2458,15 +2200,69 @@ struct dvb_usb_device_properties dib0700_devices[] = { { &dib0700_usb_id_table[44], NULL }, { NULL }, }, + }, + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .rc_props = { + .allowed_protos = IR_TYPE_RC5 | + IR_TYPE_RC6 | + IR_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, + }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 2, + .adapter = { + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = stk7070pd_frontend_attach0, + .tuner_attach = dib7070p_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x02), + + .size_of_priv = sizeof(struct dib0700_adapter_state), + }, { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, + .frontend_attach = stk7070pd_frontend_attach1, + .tuner_attach = dib7070p_tuner_attach, + + DIB0700_DEFAULT_STREAMING_CONFIG(0x03), + + .size_of_priv = sizeof(struct dib0700_adapter_state), + } + }, + + .num_device_descs = 1, + .devices = { { "Elgato EyeTV Diversity", { &dib0700_usb_id_table[68], NULL }, { NULL }, }, }, - .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = ir_codes_dib0700_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), - .rc_query = dib0700_rc_query + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_NEC_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .rc_props = { + .allowed_protos = IR_TYPE_RC5 | + IR_TYPE_RC6 | + IR_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, .num_adapters = 1, @@ -2525,10 +2321,19 @@ struct dvb_usb_device_properties dib0700_devices[] = { { NULL }, }, }, - .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = ir_codes_dib0700_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), - .rc_query = dib0700_rc_query + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .rc_props = { + .allowed_protos = IR_TYPE_RC5 | + IR_TYPE_RC6 | + IR_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, .num_adapters = 1, .adapter = { @@ -2554,10 +2359,19 @@ struct dvb_usb_device_properties dib0700_devices[] = { { NULL }, }, }, - .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = ir_codes_dib0700_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), - .rc_query = dib0700_rc_query + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .rc_props = { + .allowed_protos = IR_TYPE_RC5 | + IR_TYPE_RC6 | + IR_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, .num_adapters = 1, .adapter = { @@ -2615,10 +2429,19 @@ struct dvb_usb_device_properties dib0700_devices[] = { { NULL }, }, }, - .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = ir_codes_dib0700_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), - .rc_query = dib0700_rc_query + + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .rc_props = { + .allowed_protos = IR_TYPE_RC5 | + IR_TYPE_RC6 | + IR_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, .num_adapters = 1, .adapter = { @@ -2653,11 +2476,18 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, }, - .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = ir_codes_dib0700_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), - .rc_query = dib0700_rc_query - + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_NEC_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .rc_props = { + .allowed_protos = IR_TYPE_RC5 | + IR_TYPE_RC6 | + IR_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, .num_adapters = 2, .adapter = { @@ -2697,10 +2527,18 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, }, - .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = ir_codes_dib0700_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), - .rc_query = dib0700_rc_query + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .rc_props = { + .allowed_protos = IR_TYPE_RC5 | + IR_TYPE_RC6 | + IR_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, .num_adapters = 1, .adapter = { @@ -2728,10 +2566,18 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, }, - .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = ir_codes_dib0700_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dib0700_table), - .rc_query = dib0700_rc_query + .rc.core = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_codes = RC_MAP_DIB0700_RC5_TABLE, + .module_name = "dib0700", + .rc_query = dib0700_rc_query_old_firmware, + .rc_props = { + .allowed_protos = IR_TYPE_RC5 | + IR_TYPE_RC6 | + IR_TYPE_NEC, + .change_protocol = dib0700_change_protocol, + }, + }, }, }; diff --git a/drivers/media/dvb/dvb-usb/dibusb-common.c b/drivers/media/dvb/dvb-usb/dibusb-common.c index bc08bc0..ba991aa 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-common.c +++ b/drivers/media/dvb/dvb-usb/dibusb-common.c @@ -327,7 +327,7 @@ EXPORT_SYMBOL(dibusb_dib3000mc_tuner_attach); /* * common remote control stuff */ -struct dvb_usb_rc_key ir_codes_dibusb_table[] = { +struct ir_scancode ir_codes_dibusb_table[] = { /* Key codes for the little Artec T1/Twinhan/HAMA/ remote. */ { 0x0016, KEY_POWER }, { 0x0010, KEY_MUTE }, diff --git a/drivers/media/dvb/dvb-usb/dibusb-mb.c b/drivers/media/dvb/dvb-usb/dibusb-mb.c index eb2e6f0..8e3c0d2 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-mb.c +++ b/drivers/media/dvb/dvb-usb/dibusb-mb.c @@ -211,10 +211,12 @@ static struct dvb_usb_device_properties dibusb1_1_properties = { .power_ctrl = dibusb_power_ctrl, - .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = ir_codes_dibusb_table, - .rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ - .rc_query = dibusb_rc_query, + .rc.legacy = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_key_map = ir_codes_dibusb_table, + .rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ + .rc_query = dibusb_rc_query, + }, .i2c_algo = &dibusb_i2c_algo, @@ -295,10 +297,12 @@ static struct dvb_usb_device_properties dibusb1_1_an2235_properties = { }, .power_ctrl = dibusb_power_ctrl, - .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = ir_codes_dibusb_table, - .rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ - .rc_query = dibusb_rc_query, + .rc.legacy = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_key_map = ir_codes_dibusb_table, + .rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ + .rc_query = dibusb_rc_query, + }, .i2c_algo = &dibusb_i2c_algo, @@ -359,10 +363,12 @@ static struct dvb_usb_device_properties dibusb2_0b_properties = { }, .power_ctrl = dibusb2_0_power_ctrl, - .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = ir_codes_dibusb_table, - .rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ - .rc_query = dibusb_rc_query, + .rc.legacy = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_key_map = ir_codes_dibusb_table, + .rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ + .rc_query = dibusb_rc_query, + }, .i2c_algo = &dibusb_i2c_algo, @@ -416,10 +422,12 @@ static struct dvb_usb_device_properties artec_t1_usb2_properties = { }, .power_ctrl = dibusb2_0_power_ctrl, - .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = ir_codes_dibusb_table, - .rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ - .rc_query = dibusb_rc_query, + .rc.legacy = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_key_map = ir_codes_dibusb_table, + .rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ + .rc_query = dibusb_rc_query, + }, .i2c_algo = &dibusb_i2c_algo, diff --git a/drivers/media/dvb/dvb-usb/dibusb-mc.c b/drivers/media/dvb/dvb-usb/dibusb-mc.c index 588308e..1cbc41c 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-mc.c +++ b/drivers/media/dvb/dvb-usb/dibusb-mc.c @@ -81,10 +81,12 @@ static struct dvb_usb_device_properties dibusb_mc_properties = { }, .power_ctrl = dibusb2_0_power_ctrl, - .rc_interval = DEFAULT_RC_INTERVAL, - .rc_key_map = ir_codes_dibusb_table, - .rc_key_map_size = 111, /* FIXME */ - .rc_query = dibusb_rc_query, + .rc.legacy = { + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_key_map = ir_codes_dibusb_table, + .rc_key_map_size = 111, /* FIXME */ + .rc_query = dibusb_rc_query, + }, .i2c_algo = &dibusb_i2c_algo, diff --git a/drivers/media/dvb/dvb-usb/dibusb.h b/drivers/media/dvb/dvb-usb/dibusb.h index 3d50ac5..61a6bf3 100644 --- a/drivers/media/dvb/dvb-usb/dibusb.h +++ b/drivers/media/dvb/dvb-usb/dibusb.h @@ -124,7 +124,7 @@ extern int dibusb2_0_power_ctrl(struct dvb_usb_device *, int); #define DEFAULT_RC_INTERVAL 150 //#define DEFAULT_RC_INTERVAL 100000 -extern struct dvb_usb_rc_key ir_codes_dibusb_table[]; +extern struct ir_scancode ir_codes_dibusb_table[]; extern int dibusb_rc_query(struct dvb_usb_device *, u32 *, int *); extern int dibusb_read_eeprom_byte(struct dvb_usb_device *, u8, u8 *); diff --git a/drivers/media/dvb/dvb-usb/digitv.c b/drivers/media/dvb/dvb-usb/digitv.c index e826077..13d006b 100644 --- a/drivers/media/dvb/dvb-usb/digitv.c +++ b/drivers/media/dvb/dvb-usb/digitv.c @@ -161,7 +161,7 @@ static int digitv_tuner_attach(struct dvb_usb_adapter *adap) return 0; } -static struct dvb_usb_rc_key ir_codes_digitv_table[] = { +static struct ir_scancode ir_codes_digitv_table[] = { { 0x5f55, KEY_0 }, { 0x6f55, KEY_1 }, { 0x9f55, KEY_2 }, @@ -237,10 +237,10 @@ static int digitv_rc_query(struct dvb_usb_device *d, u32 *event, int *state) /* if something is inside the buffer, simulate key press */ if (key[1] != 0) { - for (i = 0; i < d->props.rc_key_map_size; i++) { - if (rc5_custom(&d->props.rc_key_map[i]) == key[1] && - rc5_data(&d->props.rc_key_map[i]) == key[2]) { - *event = d->props.rc_key_map[i].event; + for (i = 0; i < d->props.rc.legacy.rc_key_map_size; i++) { + if (rc5_custom(&d->props.rc.legacy.rc_key_map[i]) == key[1] && + rc5_data(&d->props.rc.legacy.rc_key_map[i]) == key[2]) { + *event = d->props.rc.legacy.rc_key_map[i].keycode; *state = REMOTE_KEY_PRESSED; return 0; } @@ -310,10 +310,12 @@ static struct dvb_usb_device_properties digitv_properties = { }, .identify_state = digitv_identify_state, - .rc_interval = 1000, - .rc_key_map = ir_codes_digitv_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_digitv_table), - .rc_query = digitv_rc_query, + .rc.legacy = { + .rc_interval = 1000, + .rc_key_map = ir_codes_digitv_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_digitv_table), + .rc_query = digitv_rc_query, + }, .i2c_algo = &digitv_i2c_algo, diff --git a/drivers/media/dvb/dvb-usb/dtt200u.c b/drivers/media/dvb/dvb-usb/dtt200u.c index f57e590..ca495e0 100644 --- a/drivers/media/dvb/dvb-usb/dtt200u.c +++ b/drivers/media/dvb/dvb-usb/dtt200u.c @@ -57,7 +57,7 @@ static int dtt200u_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, /* remote control */ /* key list for the tiny remote control (Yakumo, don't know about the others) */ -static struct dvb_usb_rc_key ir_codes_dtt200u_table[] = { +static struct ir_scancode ir_codes_dtt200u_table[] = { { 0x8001, KEY_MUTE }, { 0x8002, KEY_CHANNELDOWN }, { 0x8003, KEY_VOLUMEDOWN }, @@ -161,10 +161,12 @@ static struct dvb_usb_device_properties dtt200u_properties = { }, .power_ctrl = dtt200u_power_ctrl, - .rc_interval = 300, - .rc_key_map = ir_codes_dtt200u_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dtt200u_table), - .rc_query = dtt200u_rc_query, + .rc.legacy = { + .rc_interval = 300, + .rc_key_map = ir_codes_dtt200u_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dtt200u_table), + .rc_query = dtt200u_rc_query, + }, .generic_bulk_ctrl_endpoint = 0x01, @@ -206,10 +208,12 @@ static struct dvb_usb_device_properties wt220u_properties = { }, .power_ctrl = dtt200u_power_ctrl, - .rc_interval = 300, - .rc_key_map = ir_codes_dtt200u_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dtt200u_table), - .rc_query = dtt200u_rc_query, + .rc.legacy = { + .rc_interval = 300, + .rc_key_map = ir_codes_dtt200u_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dtt200u_table), + .rc_query = dtt200u_rc_query, + }, .generic_bulk_ctrl_endpoint = 0x01, @@ -251,10 +255,12 @@ static struct dvb_usb_device_properties wt220u_fc_properties = { }, .power_ctrl = dtt200u_power_ctrl, - .rc_interval = 300, - .rc_key_map = ir_codes_dtt200u_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dtt200u_table), - .rc_query = dtt200u_rc_query, + .rc.legacy = { + .rc_interval = 300, + .rc_key_map = ir_codes_dtt200u_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dtt200u_table), + .rc_query = dtt200u_rc_query, + }, .generic_bulk_ctrl_endpoint = 0x01, @@ -296,10 +302,12 @@ static struct dvb_usb_device_properties wt220u_zl0353_properties = { }, .power_ctrl = dtt200u_power_ctrl, - .rc_interval = 300, - .rc_key_map = ir_codes_dtt200u_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dtt200u_table), - .rc_query = dtt200u_rc_query, + .rc.legacy = { + .rc_interval = 300, + .rc_key_map = ir_codes_dtt200u_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dtt200u_table), + .rc_query = dtt200u_rc_query, + }, .generic_bulk_ctrl_endpoint = 0x01, diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index b4afe6f..1a774d5 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -197,6 +197,7 @@ #define USB_PID_AVERMEDIA_A310 0xa310 #define USB_PID_AVERMEDIA_A850 0x850a #define USB_PID_AVERMEDIA_A805 0xa805 +#define USB_PID_AVERMEDIA_A815M 0x815a #define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006 #define USB_PID_TECHNOTREND_CONNECT_CT3650 0x300d #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-init.c b/drivers/media/dvb/dvb-usb/dvb-usb-init.c index 5d91f70..2e3ea0f 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-init.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-init.c @@ -15,7 +15,7 @@ /* debug */ int dvb_usb_debug; -module_param_named(debug,dvb_usb_debug, int, 0644); +module_param_named(debug, dvb_usb_debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,pll=4,ts=8,err=16,rc=32,fw=64,mem=128,uxfer=256 (or-able))." DVB_USB_DEBUG_STATUS); int dvb_usb_disable_rc_polling; @@ -29,7 +29,7 @@ MODULE_PARM_DESC(force_pid_filter_usage, "force all dvb-usb-devices to use a PID static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs) { struct dvb_usb_adapter *adap; - int ret,n; + int ret, n; for (n = 0; n < d->props.num_adapters; n++) { adap = &d->adapter[n]; @@ -38,7 +38,7 @@ static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs) memcpy(&adap->props, &d->props.adapter[n], sizeof(struct dvb_usb_adapter_properties)); -/* speed - when running at FULL speed we need a HW PID filter */ + /* speed - when running at FULL speed we need a HW PID filter */ if (d->udev->speed == USB_SPEED_FULL && !(adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER)) { err("This USB2.0 device cannot be run on a USB1.1 port. (it lacks a hardware PID filter)"); return -ENODEV; @@ -46,7 +46,7 @@ static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs) if ((d->udev->speed == USB_SPEED_FULL && adap->props.caps & DVB_USB_ADAP_HAS_PID_FILTER) || (adap->props.caps & DVB_USB_ADAP_NEED_PID_FILTERING)) { - info("will use the device's hardware PID filter (table count: %d).",adap->props.pid_filter_count); + info("will use the device's hardware PID filter (table count: %d).", adap->props.pid_filter_count); adap->pid_filtering = 1; adap->max_feed_count = adap->props.pid_filter_count; } else { @@ -64,9 +64,9 @@ static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs) } if (adap->props.size_of_priv > 0) { - adap->priv = kzalloc(adap->props.size_of_priv,GFP_KERNEL); + adap->priv = kzalloc(adap->props.size_of_priv, GFP_KERNEL); if (adap->priv == NULL) { - err("no memory for priv for adapter %d.",n); + err("no memory for priv for adapter %d.", n); return -ENOMEM; } } @@ -86,8 +86,8 @@ static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs) * sometimes a timeout occures, this helps */ if (d->props.generic_bulk_ctrl_endpoint != 0) { - usb_clear_halt(d->udev,usb_sndbulkpipe(d->udev,d->props.generic_bulk_ctrl_endpoint)); - usb_clear_halt(d->udev,usb_rcvbulkpipe(d->udev,d->props.generic_bulk_ctrl_endpoint)); + usb_clear_halt(d->udev, usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); } return 0; @@ -96,6 +96,7 @@ static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs) static int dvb_usb_adapter_exit(struct dvb_usb_device *d) { int n; + for (n = 0; n < d->num_adapters_initialized; n++) { dvb_usb_adapter_frontend_exit(&d->adapter[n]); dvb_usb_adapter_dvb_exit(&d->adapter[n]); @@ -111,11 +112,11 @@ static int dvb_usb_adapter_exit(struct dvb_usb_device *d) /* general initialization functions */ static int dvb_usb_exit(struct dvb_usb_device *d) { - deb_info("state before exiting everything: %x\n",d->state); + deb_info("state before exiting everything: %x\n", d->state); dvb_usb_remote_exit(d); dvb_usb_adapter_exit(d); dvb_usb_i2c_exit(d); - deb_info("state should be zero now: %x\n",d->state); + deb_info("state should be zero now: %x\n", d->state); d->state = DVB_USB_STATE_INIT; kfree(d->priv); kfree(d); @@ -132,14 +133,14 @@ static int dvb_usb_init(struct dvb_usb_device *d, short *adapter_nums) d->state = DVB_USB_STATE_INIT; if (d->props.size_of_priv > 0) { - d->priv = kzalloc(d->props.size_of_priv,GFP_KERNEL); + d->priv = kzalloc(d->props.size_of_priv, GFP_KERNEL); if (d->priv == NULL) { err("no memory for priv in 'struct dvb_usb_device'"); return -ENOMEM; } } -/* check the capabilities and set appropriate variables */ + /* check the capabilities and set appropriate variables */ dvb_usb_device_power_ctrl(d, 1); if ((ret = dvb_usb_i2c_init(d)) || @@ -157,16 +158,17 @@ static int dvb_usb_init(struct dvb_usb_device *d, short *adapter_nums) } /* determine the name and the state of the just found USB device */ -static struct dvb_usb_device_description * dvb_usb_find_device(struct usb_device *udev,struct dvb_usb_device_properties *props, int *cold) +static struct dvb_usb_device_description *dvb_usb_find_device(struct usb_device *udev, struct dvb_usb_device_properties *props, int *cold) { - int i,j; + int i, j; struct dvb_usb_device_description *desc = NULL; + *cold = -1; for (i = 0; i < props->num_device_descs; i++) { for (j = 0; j < DVB_USB_ID_MAX_NUM && props->devices[i].cold_ids[j] != NULL; j++) { - deb_info("check for cold %x %x\n",props->devices[i].cold_ids[j]->idVendor, props->devices[i].cold_ids[j]->idProduct); + deb_info("check for cold %x %x\n", props->devices[i].cold_ids[j]->idVendor, props->devices[i].cold_ids[j]->idProduct); if (props->devices[i].cold_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) && props->devices[i].cold_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) { *cold = 1; @@ -179,7 +181,7 @@ static struct dvb_usb_device_description * dvb_usb_find_device(struct usb_device break; for (j = 0; j < DVB_USB_ID_MAX_NUM && props->devices[i].warm_ids[j] != NULL; j++) { - deb_info("check for warm %x %x\n",props->devices[i].warm_ids[j]->idVendor, props->devices[i].warm_ids[j]->idProduct); + deb_info("check for warm %x %x\n", props->devices[i].warm_ids[j]->idVendor, props->devices[i].warm_ids[j]->idProduct); if (props->devices[i].warm_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) && props->devices[i].warm_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) { *cold = 0; @@ -190,7 +192,7 @@ static struct dvb_usb_device_description * dvb_usb_find_device(struct usb_device } if (desc != NULL && props->identify_state != NULL) - props->identify_state(udev,props,&desc,cold); + props->identify_state(udev, props, &desc, cold); return desc; } @@ -202,7 +204,7 @@ int dvb_usb_device_power_ctrl(struct dvb_usb_device *d, int onoff) else d->powered--; - if (d->powered == 0 || (onoff && d->powered == 1)) { // when switching from 1 to 0 or from 0 to 1 + if (d->powered == 0 || (onoff && d->powered == 1)) { /* when switching from 1 to 0 or from 0 to 1 */ deb_info("power control: %d\n", onoff); if (d->props.power_ctrl) return d->props.power_ctrl(d, onoff); @@ -222,32 +224,32 @@ int dvb_usb_device_init(struct usb_interface *intf, struct dvb_usb_device *d = NULL; struct dvb_usb_device_description *desc = NULL; - int ret = -ENOMEM,cold=0; + int ret = -ENOMEM, cold = 0; if (du != NULL) *du = NULL; - if ((desc = dvb_usb_find_device(udev,props,&cold)) == NULL) { + if ((desc = dvb_usb_find_device(udev, props, &cold)) == NULL) { deb_err("something went very wrong, device was not found in current device list - let's see what comes next.\n"); return -ENODEV; } if (cold) { - info("found a '%s' in cold state, will try to load a firmware",desc->name); - ret = dvb_usb_download_firmware(udev,props); + info("found a '%s' in cold state, will try to load a firmware", desc->name); + ret = dvb_usb_download_firmware(udev, props); if (!props->no_reconnect || ret != 0) return ret; } - info("found a '%s' in warm state.",desc->name); - d = kzalloc(sizeof(struct dvb_usb_device),GFP_KERNEL); + info("found a '%s' in warm state.", desc->name); + d = kzalloc(sizeof(struct dvb_usb_device), GFP_KERNEL); if (d == NULL) { err("no memory for 'struct dvb_usb_device'"); return -ENOMEM; } d->udev = udev; - memcpy(&d->props,props,sizeof(struct dvb_usb_device_properties)); + memcpy(&d->props, props, sizeof(struct dvb_usb_device_properties)); d->desc = desc; d->owner = owner; @@ -259,9 +261,9 @@ int dvb_usb_device_init(struct usb_interface *intf, ret = dvb_usb_init(d, adapter_nums); if (ret == 0) - info("%s successfully initialized and connected.",desc->name); + info("%s successfully initialized and connected.", desc->name); else - info("%s error while loading driver (%d)",desc->name,ret); + info("%s error while loading driver (%d)", desc->name, ret); return ret; } EXPORT_SYMBOL(dvb_usb_device_init); @@ -271,12 +273,12 @@ void dvb_usb_device_exit(struct usb_interface *intf) struct dvb_usb_device *d = usb_get_intfdata(intf); const char *name = "generic DVB-USB module"; - usb_set_intfdata(intf,NULL); + usb_set_intfdata(intf, NULL); if (d != NULL && d->desc != NULL) { name = d->desc->name; dvb_usb_exit(d); } - info("%s successfully deinitialized and disconnected.",name); + info("%s successfully deinitialized and disconnected.", name); } EXPORT_SYMBOL(dvb_usb_device_exit); diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c index 852fe89..b579fed 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c @@ -8,29 +8,29 @@ #include "dvb-usb-common.h" #include <linux/usb/input.h> -static int dvb_usb_getkeycode(struct input_dev *dev, +static int legacy_dvb_usb_getkeycode(struct input_dev *dev, unsigned int scancode, unsigned int *keycode) { struct dvb_usb_device *d = input_get_drvdata(dev); - struct dvb_usb_rc_key *keymap = d->props.rc_key_map; + struct ir_scancode *keymap = d->props.rc.legacy.rc_key_map; int i; /* See if we can match the raw key code. */ - for (i = 0; i < d->props.rc_key_map_size; i++) - if (keymap[i].scan == scancode) { - *keycode = keymap[i].event; + for (i = 0; i < d->props.rc.legacy.rc_key_map_size; i++) + if (keymap[i].scancode == scancode) { + *keycode = keymap[i].keycode; return 0; } /* * If is there extra space, returns KEY_RESERVED, - * otherwise, input core won't let dvb_usb_setkeycode + * otherwise, input core won't let legacy_dvb_usb_setkeycode * to work */ - for (i = 0; i < d->props.rc_key_map_size; i++) - if (keymap[i].event == KEY_RESERVED || - keymap[i].event == KEY_UNKNOWN) { + for (i = 0; i < d->props.rc.legacy.rc_key_map_size; i++) + if (keymap[i].keycode == KEY_RESERVED || + keymap[i].keycode == KEY_UNKNOWN) { *keycode = KEY_RESERVED; return 0; } @@ -38,27 +38,27 @@ static int dvb_usb_getkeycode(struct input_dev *dev, return -EINVAL; } -static int dvb_usb_setkeycode(struct input_dev *dev, +static int legacy_dvb_usb_setkeycode(struct input_dev *dev, unsigned int scancode, unsigned int keycode) { struct dvb_usb_device *d = input_get_drvdata(dev); - struct dvb_usb_rc_key *keymap = d->props.rc_key_map; + struct ir_scancode *keymap = d->props.rc.legacy.rc_key_map; int i; /* Search if it is replacing an existing keycode */ - for (i = 0; i < d->props.rc_key_map_size; i++) - if (keymap[i].scan == scancode) { - keymap[i].event = keycode; + for (i = 0; i < d->props.rc.legacy.rc_key_map_size; i++) + if (keymap[i].scancode == scancode) { + keymap[i].keycode = keycode; return 0; } /* Search if is there a clean entry. If so, use it */ - for (i = 0; i < d->props.rc_key_map_size; i++) - if (keymap[i].event == KEY_RESERVED || - keymap[i].event == KEY_UNKNOWN) { - keymap[i].scan = scancode; - keymap[i].event = keycode; + for (i = 0; i < d->props.rc.legacy.rc_key_map_size; i++) + if (keymap[i].keycode == KEY_RESERVED || + keymap[i].keycode == KEY_UNKNOWN) { + keymap[i].scancode = scancode; + keymap[i].keycode = keycode; return 0; } @@ -78,7 +78,7 @@ static int dvb_usb_setkeycode(struct input_dev *dev, * * TODO: Fix the repeat rate of the input device. */ -static void dvb_usb_read_remote_control(struct work_struct *work) +static void legacy_dvb_usb_read_remote_control(struct work_struct *work) { struct dvb_usb_device *d = container_of(work, struct dvb_usb_device, rc_query_work.work); @@ -92,7 +92,7 @@ static void dvb_usb_read_remote_control(struct work_struct *work) if (dvb_usb_disable_rc_polling) return; - if (d->props.rc_query(d,&event,&state)) { + if (d->props.rc.legacy.rc_query(d,&event,&state)) { err("error while querying for an remote control event."); goto schedule; } @@ -151,18 +151,117 @@ static void dvb_usb_read_remote_control(struct work_struct *work) */ schedule: - schedule_delayed_work(&d->rc_query_work,msecs_to_jiffies(d->props.rc_interval)); + schedule_delayed_work(&d->rc_query_work,msecs_to_jiffies(d->props.rc.legacy.rc_interval)); +} + +static int legacy_dvb_usb_remote_init(struct dvb_usb_device *d, + struct input_dev *input_dev) +{ + int i, err, rc_interval; + + input_dev->getkeycode = legacy_dvb_usb_getkeycode; + input_dev->setkeycode = legacy_dvb_usb_setkeycode; + + /* set the bits for the keys */ + deb_rc("key map size: %d\n", d->props.rc.legacy.rc_key_map_size); + for (i = 0; i < d->props.rc.legacy.rc_key_map_size; i++) { + deb_rc("setting bit for event %d item %d\n", + d->props.rc.legacy.rc_key_map[i].keycode, i); + set_bit(d->props.rc.legacy.rc_key_map[i].keycode, input_dev->keybit); + } + + /* setting these two values to non-zero, we have to manage key repeats */ + input_dev->rep[REP_PERIOD] = d->props.rc.legacy.rc_interval; + input_dev->rep[REP_DELAY] = d->props.rc.legacy.rc_interval + 150; + + input_set_drvdata(input_dev, d); + + err = input_register_device(input_dev); + if (err) + input_free_device(input_dev); + + rc_interval = d->props.rc.legacy.rc_interval; + + INIT_DELAYED_WORK(&d->rc_query_work, legacy_dvb_usb_read_remote_control); + + info("schedule remote query interval to %d msecs.", rc_interval); + schedule_delayed_work(&d->rc_query_work, + msecs_to_jiffies(rc_interval)); + + d->state |= DVB_USB_STATE_REMOTE; + + return err; +} + +/* Remote-control poll function - called every dib->rc_query_interval ms to see + * whether the remote control has received anything. + * + * TODO: Fix the repeat rate of the input device. + */ +static void dvb_usb_read_remote_control(struct work_struct *work) +{ + struct dvb_usb_device *d = + container_of(work, struct dvb_usb_device, rc_query_work.work); + int err; + + /* TODO: need a lock here. We can simply skip checking for the remote control + if we're busy. */ + + /* when the parameter has been set to 1 via sysfs while the + * driver was running, or when bulk mode is enabled after IR init + */ + if (dvb_usb_disable_rc_polling || d->props.rc.core.bulk_mode) + return; + + err = d->props.rc.core.rc_query(d); + if (err) + err("error %d while querying for an remote control event.", err); + + schedule_delayed_work(&d->rc_query_work, + msecs_to_jiffies(d->props.rc.core.rc_interval)); +} + +static int rc_core_dvb_usb_remote_init(struct dvb_usb_device *d, + struct input_dev *input_dev) +{ + int err, rc_interval; + + d->props.rc.core.rc_props.priv = d; + err = ir_input_register(input_dev, + d->props.rc.core.rc_codes, + &d->props.rc.core.rc_props, + d->props.rc.core.module_name); + if (err < 0) + return err; + + if (!d->props.rc.core.rc_query || d->props.rc.core.bulk_mode) + return 0; + + /* Polling mode - initialize a work queue for handling it */ + INIT_DELAYED_WORK(&d->rc_query_work, dvb_usb_read_remote_control); + + rc_interval = d->props.rc.core.rc_interval; + + info("schedule remote query interval to %d msecs.", rc_interval); + schedule_delayed_work(&d->rc_query_work, + msecs_to_jiffies(rc_interval)); + + return 0; } int dvb_usb_remote_init(struct dvb_usb_device *d) { struct input_dev *input_dev; - int i; int err; - if (d->props.rc_key_map == NULL || - d->props.rc_query == NULL || - dvb_usb_disable_rc_polling) + if (dvb_usb_disable_rc_polling) + return 0; + + if (d->props.rc.legacy.rc_key_map && d->props.rc.legacy.rc_query) + d->props.rc.mode = DVB_RC_LEGACY; + else if (d->props.rc.core.rc_codes) + d->props.rc.mode = DVB_RC_CORE; + else return 0; usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys)); @@ -177,39 +276,19 @@ int dvb_usb_remote_init(struct dvb_usb_device *d) input_dev->phys = d->rc_phys; usb_to_input_id(d->udev, &input_dev->id); input_dev->dev.parent = &d->udev->dev; - input_dev->getkeycode = dvb_usb_getkeycode; - input_dev->setkeycode = dvb_usb_setkeycode; - - /* set the bits for the keys */ - deb_rc("key map size: %d\n", d->props.rc_key_map_size); - for (i = 0; i < d->props.rc_key_map_size; i++) { - deb_rc("setting bit for event %d item %d\n", - d->props.rc_key_map[i].event, i); - set_bit(d->props.rc_key_map[i].event, input_dev->keybit); - } /* Start the remote-control polling. */ - if (d->props.rc_interval < 40) - d->props.rc_interval = 100; /* default */ - - /* setting these two values to non-zero, we have to manage key repeats */ - input_dev->rep[REP_PERIOD] = d->props.rc_interval; - input_dev->rep[REP_DELAY] = d->props.rc_interval + 150; - - input_set_drvdata(input_dev, d); - - err = input_register_device(input_dev); - if (err) { - input_free_device(input_dev); - return err; - } + if (d->props.rc.legacy.rc_interval < 40) + d->props.rc.legacy.rc_interval = 100; /* default */ d->rc_input_dev = input_dev; - INIT_DELAYED_WORK(&d->rc_query_work, dvb_usb_read_remote_control); - - info("schedule remote query interval to %d msecs.", d->props.rc_interval); - schedule_delayed_work(&d->rc_query_work,msecs_to_jiffies(d->props.rc_interval)); + if (d->props.rc.mode == DVB_RC_LEGACY) + err = legacy_dvb_usb_remote_init(d, input_dev); + else + err = rc_core_dvb_usb_remote_init(d, input_dev); + if (err) + return err; d->state |= DVB_USB_STATE_REMOTE; @@ -221,7 +300,10 @@ int dvb_usb_remote_exit(struct dvb_usb_device *d) if (d->state & DVB_USB_STATE_REMOTE) { cancel_rearming_delayed_work(&d->rc_query_work); flush_scheduled_work(); - input_unregister_device(d->rc_input_dev); + if (d->props.rc.mode == DVB_RC_LEGACY) + input_unregister_device(d->rc_input_dev); + else + ir_input_unregister(d->rc_input_dev); } d->state &= ~DVB_USB_STATE_REMOTE; return 0; @@ -234,7 +316,7 @@ int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *d, u8 keybuf[5], u32 *event, int *state) { int i; - struct dvb_usb_rc_key *keymap = d->props.rc_key_map; + struct ir_scancode *keymap = d->props.rc.legacy.rc_key_map; *event = 0; *state = REMOTE_NO_KEY_PRESSED; switch (keybuf[0]) { @@ -247,10 +329,10 @@ int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *d, break; } /* See if we can match the raw key code. */ - for (i = 0; i < d->props.rc_key_map_size; i++) + for (i = 0; i < d->props.rc.legacy.rc_key_map_size; i++) if (rc5_custom(&keymap[i]) == keybuf[1] && rc5_data(&keymap[i]) == keybuf[3]) { - *event = keymap[i].event; + *event = keymap[i].keycode; *state = REMOTE_KEY_PRESSED; return 0; } diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h index 4a9f676..34f7b3b 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb.h @@ -14,6 +14,7 @@ #include <linux/usb.h> #include <linux/firmware.h> #include <linux/mutex.h> +#include <media/ir-core.h> #include "dvb_frontend.h" #include "dvb_demux.h" @@ -74,30 +75,19 @@ struct dvb_usb_device_description { struct usb_device_id *warm_ids[DVB_USB_ID_MAX_NUM]; }; -/** - * struct dvb_usb_rc_key - a remote control key and its input-event - * @custom: the vendor/custom part of the key - * @data: the actual key part - * @event: the input event assigned to key identified by custom and data - */ -struct dvb_usb_rc_key { - u16 scan; - u32 event; -}; - -static inline u8 rc5_custom(struct dvb_usb_rc_key *key) +static inline u8 rc5_custom(struct ir_scancode *key) { - return (key->scan >> 8) & 0xff; + return (key->scancode >> 8) & 0xff; } -static inline u8 rc5_data(struct dvb_usb_rc_key *key) +static inline u8 rc5_data(struct ir_scancode *key) { - return key->scan & 0xff; + return key->scancode & 0xff; } -static inline u8 rc5_scan(struct dvb_usb_rc_key *key) +static inline u8 rc5_scan(struct ir_scancode *key) { - return key->scan & 0xffff; + return key->scancode & 0xffff; } struct dvb_usb_device; @@ -168,6 +158,55 @@ struct dvb_usb_adapter_properties { }; /** + * struct dvb_rc_legacy - old properties of remote controller + * @rc_key_map: a hard-wired array of struct ir_scancode (NULL to disable + * remote control handling). + * @rc_key_map_size: number of items in @rc_key_map. + * @rc_query: called to query an event event. + * @rc_interval: time in ms between two queries. + */ +struct dvb_rc_legacy { +/* remote control properties */ +#define REMOTE_NO_KEY_PRESSED 0x00 +#define REMOTE_KEY_PRESSED 0x01 +#define REMOTE_KEY_REPEAT 0x02 + struct ir_scancode *rc_key_map; + int rc_key_map_size; + int (*rc_query) (struct dvb_usb_device *, u32 *, int *); + int rc_interval; +}; + +/** + * struct dvb_rc properties of remote controller, using rc-core + * @rc_codes: name of rc codes table + * @protocol: type of protocol(s) currently used by the driver + * @rc_query: called to query an event event. + * @rc_interval: time in ms between two queries. + * @rc_props: remote controller properties + * @bulk_mode: device supports bulk mode for RC (disable polling mode) + */ +struct dvb_rc { + char *rc_codes; + u64 protocol; + char *module_name; + int (*rc_query) (struct dvb_usb_device *d); + int rc_interval; + struct ir_dev_props rc_props; + bool bulk_mode; /* uses bulk mode */ +}; + +/** + * enum dvb_usb_mode - Specifies if it is using a legacy driver or a new one + * based on rc-core + * This is initialized/used only inside dvb-usb-remote.c. + * It shouldn't be set by the drivers. + */ +enum dvb_usb_mode { + DVB_RC_LEGACY, + DVB_RC_CORE, +}; + +/** * struct dvb_usb_device_properties - properties of a dvb-usb-device * @usb_ctrl: which USB device-side controller is in use. Needed for firmware * download. @@ -185,11 +224,7 @@ struct dvb_usb_adapter_properties { * @identify_state: called to determine the state (cold or warm), when it * is not distinguishable by the USB IDs. * - * @rc_key_map: a hard-wired array of struct dvb_usb_rc_key (NULL to disable - * remote control handling). - * @rc_key_map_size: number of items in @rc_key_map. - * @rc_query: called to query an event event. - * @rc_interval: time in ms between two queries. + * @rc: remote controller properties * * @i2c_algo: i2c_algorithm if the device has I2CoverUSB. * @@ -233,14 +268,11 @@ struct dvb_usb_device_properties { int (*identify_state) (struct usb_device *, struct dvb_usb_device_properties *, struct dvb_usb_device_description **, int *); -/* remote control properties */ -#define REMOTE_NO_KEY_PRESSED 0x00 -#define REMOTE_KEY_PRESSED 0x01 -#define REMOTE_KEY_REPEAT 0x02 - struct dvb_usb_rc_key *rc_key_map; - int rc_key_map_size; - int (*rc_query) (struct dvb_usb_device *, u32 *, int *); - int rc_interval; + struct { + enum dvb_usb_mode mode; /* Drivers shouldn't touch on it */ + struct dvb_rc_legacy legacy; + struct dvb_rc core; + } rc; struct i2c_algorithm *i2c_algo; diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index e8fb853..774df88 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -74,7 +74,7 @@ "on firmware-problems." struct ir_codes_dvb_usb_table_table { - struct dvb_usb_rc_key *rc_keys; + struct ir_scancode *rc_keys; int rc_keys_size; }; @@ -948,7 +948,7 @@ static int dw3101_tuner_attach(struct dvb_usb_adapter *adap) return 0; } -static struct dvb_usb_rc_key ir_codes_dw210x_table[] = { +static struct ir_scancode ir_codes_dw210x_table[] = { { 0xf80a, KEY_Q }, /*power*/ { 0xf80c, KEY_M }, /*mute*/ { 0xf811, KEY_1 }, @@ -982,7 +982,7 @@ static struct dvb_usb_rc_key ir_codes_dw210x_table[] = { { 0xf81b, KEY_B }, /*recall*/ }; -static struct dvb_usb_rc_key ir_codes_tevii_table[] = { +static struct ir_scancode ir_codes_tevii_table[] = { { 0xf80a, KEY_POWER }, { 0xf80c, KEY_MUTE }, { 0xf811, KEY_1 }, @@ -1032,7 +1032,7 @@ static struct dvb_usb_rc_key ir_codes_tevii_table[] = { { 0xf858, KEY_SWITCHVIDEOMODE }, }; -static struct dvb_usb_rc_key ir_codes_tbs_table[] = { +static struct ir_scancode ir_codes_tbs_table[] = { { 0xf884, KEY_POWER }, { 0xf894, KEY_MUTE }, { 0xf887, KEY_1 }, @@ -1075,8 +1075,8 @@ static struct ir_codes_dvb_usb_table_table keys_tables[] = { static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state) { - struct dvb_usb_rc_key *keymap = d->props.rc_key_map; - int keymap_size = d->props.rc_key_map_size; + struct ir_scancode *keymap = d->props.rc.legacy.rc_key_map; + int keymap_size = d->props.rc.legacy.rc_key_map_size; u8 key[2]; struct i2c_msg msg = { .addr = DW2102_RC_QUERY, @@ -1096,7 +1096,7 @@ static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state) for (i = 0; i < keymap_size ; i++) { if (rc5_data(&keymap[i]) == msg.buf[0]) { *state = REMOTE_KEY_PRESSED; - *event = keymap[i].event; + *event = keymap[i].keycode; break; } @@ -1185,13 +1185,13 @@ static int dw2102_load_firmware(struct usb_device *dev, /* init registers */ switch (dev->descriptor.idProduct) { case USB_PID_PROF_1100: - s6x0_properties.rc_key_map = ir_codes_tbs_table; - s6x0_properties.rc_key_map_size = + s6x0_properties.rc.legacy.rc_key_map = ir_codes_tbs_table; + s6x0_properties.rc.legacy.rc_key_map_size = ARRAY_SIZE(ir_codes_tbs_table); break; case USB_PID_TEVII_S650: - dw2104_properties.rc_key_map = ir_codes_tevii_table; - dw2104_properties.rc_key_map_size = + dw2104_properties.rc.legacy.rc_key_map = ir_codes_tevii_table; + dw2104_properties.rc.legacy.rc_key_map_size = ARRAY_SIZE(ir_codes_tevii_table); case USB_PID_DW2104: reset = 1; @@ -1255,10 +1255,13 @@ static struct dvb_usb_device_properties dw2102_properties = { .no_reconnect = 1, .i2c_algo = &dw2102_serit_i2c_algo, - .rc_key_map = ir_codes_dw210x_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dw210x_table), - .rc_interval = 150, - .rc_query = dw2102_rc_query, + + .rc.legacy = { + .rc_key_map = ir_codes_dw210x_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dw210x_table), + .rc_interval = 150, + .rc_query = dw2102_rc_query, + }, .generic_bulk_ctrl_endpoint = 0x81, /* parameter for the MPEG2-data transfer */ @@ -1306,10 +1309,12 @@ static struct dvb_usb_device_properties dw2104_properties = { .no_reconnect = 1, .i2c_algo = &dw2104_i2c_algo, - .rc_key_map = ir_codes_dw210x_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dw210x_table), - .rc_interval = 150, - .rc_query = dw2102_rc_query, + .rc.legacy = { + .rc_key_map = ir_codes_dw210x_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dw210x_table), + .rc_interval = 150, + .rc_query = dw2102_rc_query, + }, .generic_bulk_ctrl_endpoint = 0x81, /* parameter for the MPEG2-data transfer */ @@ -1353,10 +1358,12 @@ static struct dvb_usb_device_properties dw3101_properties = { .no_reconnect = 1, .i2c_algo = &dw3101_i2c_algo, - .rc_key_map = ir_codes_dw210x_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_dw210x_table), - .rc_interval = 150, - .rc_query = dw2102_rc_query, + .rc.legacy = { + .rc_key_map = ir_codes_dw210x_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_dw210x_table), + .rc_interval = 150, + .rc_query = dw2102_rc_query, + }, .generic_bulk_ctrl_endpoint = 0x81, /* parameter for the MPEG2-data transfer */ @@ -1396,10 +1403,12 @@ static struct dvb_usb_device_properties s6x0_properties = { .no_reconnect = 1, .i2c_algo = &s6x0_i2c_algo, - .rc_key_map = ir_codes_tevii_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_tevii_table), - .rc_interval = 150, - .rc_query = dw2102_rc_query, + .rc.legacy = { + .rc_key_map = ir_codes_tevii_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_tevii_table), + .rc_interval = 150, + .rc_query = dw2102_rc_query, + }, .generic_bulk_ctrl_endpoint = 0x81, .num_adapters = 1, @@ -1459,8 +1468,8 @@ static int dw2102_probe(struct usb_interface *intf, /* fill only different fields */ p7500->firmware = "dvb-usb-p7500.fw"; p7500->devices[0] = d7500; - p7500->rc_key_map = ir_codes_tbs_table; - p7500->rc_key_map_size = ARRAY_SIZE(ir_codes_tbs_table); + p7500->rc.legacy.rc_key_map = ir_codes_tbs_table; + p7500->rc.legacy.rc_key_map_size = ARRAY_SIZE(ir_codes_tbs_table); p7500->adapter->frontend_attach = prof_7500_frontend_attach; if (0 == dvb_usb_device_init(intf, &dw2102_properties, diff --git a/drivers/media/dvb/dvb-usb/gp8psk-fe.c b/drivers/media/dvb/dvb-usb/gp8psk-fe.c index 7a7f1b2..dbdb534 100644 --- a/drivers/media/dvb/dvb-usb/gp8psk-fe.c +++ b/drivers/media/dvb/dvb-usb/gp8psk-fe.c @@ -349,7 +349,7 @@ static struct dvb_frontend_ops gp8psk_fe_ops = { * FE_CAN_QAM_16 is for compatibility * (Myth incorrectly detects Turbo-QPSK as plain QAM-16) */ - FE_CAN_QPSK | FE_CAN_QAM_16 + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_TURBO_FEC }, .release = gp8psk_fe_release, diff --git a/drivers/media/dvb/dvb-usb/m920x.c b/drivers/media/dvb/dvb-usb/m920x.c index c211fef..bdef1a1 100644 --- a/drivers/media/dvb/dvb-usb/m920x.c +++ b/drivers/media/dvb/dvb-usb/m920x.c @@ -69,7 +69,7 @@ static int m920x_init(struct dvb_usb_device *d, struct m920x_inits *rc_seq) int adap_enabled[M9206_MAX_ADAPTERS] = { 0 }; /* Remote controller init. */ - if (d->props.rc_query) { + if (d->props.rc.legacy.rc_query) { deb("Initialising remote control\n"); while (rc_seq->address) { if ((ret = m920x_write(d->udev, M9206_CORE, @@ -142,9 +142,9 @@ static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) if ((ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_KEY, rc_state + 1, 1)) != 0) goto unlock; - for (i = 0; i < d->props.rc_key_map_size; i++) - if (rc5_data(&d->props.rc_key_map[i]) == rc_state[1]) { - *event = d->props.rc_key_map[i].event; + for (i = 0; i < d->props.rc.legacy.rc_key_map_size; i++) + if (rc5_data(&d->props.rc.legacy.rc_key_map[i]) == rc_state[1]) { + *event = d->props.rc.legacy.rc_key_map[i].keycode; switch(rc_state[0]) { case 0x80: @@ -589,7 +589,7 @@ static struct m920x_inits pinnacle310e_init[] = { }; /* ir keymaps */ -static struct dvb_usb_rc_key ir_codes_megasky_table [] = { +static struct ir_scancode ir_codes_megasky_table[] = { { 0x0012, KEY_POWER }, { 0x001e, KEY_CYCLEWINDOWS }, /* min/max */ { 0x0002, KEY_CHANNELUP }, @@ -608,7 +608,7 @@ static struct dvb_usb_rc_key ir_codes_megasky_table [] = { { 0x000e, KEY_COFFEE }, /* "MTS" */ }; -static struct dvb_usb_rc_key ir_codes_tvwalkertwin_table [] = { +static struct ir_scancode ir_codes_tvwalkertwin_table[] = { { 0x0001, KEY_ZOOM }, /* Full Screen */ { 0x0002, KEY_CAMERA }, /* snapshot */ { 0x0003, KEY_MUTE }, @@ -628,7 +628,7 @@ static struct dvb_usb_rc_key ir_codes_tvwalkertwin_table [] = { { 0x001e, KEY_VOLUMEUP }, }; -static struct dvb_usb_rc_key ir_codes_pinnacle310e_table[] = { +static struct ir_scancode ir_codes_pinnacle310e_table[] = { { 0x16, KEY_POWER }, { 0x17, KEY_FAVORITES }, { 0x0f, KEY_TEXT }, @@ -784,10 +784,12 @@ static struct dvb_usb_device_properties megasky_properties = { .firmware = "dvb-usb-megasky-02.fw", .download_firmware = m920x_firmware_download, - .rc_interval = 100, - .rc_key_map = ir_codes_megasky_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_megasky_table), - .rc_query = m920x_rc_query, + .rc.legacy = { + .rc_interval = 100, + .rc_key_map = ir_codes_megasky_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_megasky_table), + .rc_query = m920x_rc_query, + }, .size_of_priv = sizeof(struct m920x_state), @@ -885,10 +887,12 @@ static struct dvb_usb_device_properties tvwalkertwin_properties = { .firmware = "dvb-usb-tvwalkert.fw", .download_firmware = m920x_firmware_download, - .rc_interval = 100, - .rc_key_map = ir_codes_tvwalkertwin_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_tvwalkertwin_table), - .rc_query = m920x_rc_query, + .rc.legacy = { + .rc_interval = 100, + .rc_key_map = ir_codes_tvwalkertwin_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_tvwalkertwin_table), + .rc_query = m920x_rc_query, + }, .size_of_priv = sizeof(struct m920x_state), @@ -992,10 +996,12 @@ static struct dvb_usb_device_properties pinnacle_pctv310e_properties = { .usb_ctrl = DEVICE_SPECIFIC, .download_firmware = NULL, - .rc_interval = 100, - .rc_key_map = ir_codes_pinnacle310e_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_pinnacle310e_table), - .rc_query = m920x_rc_query, + .rc.legacy = { + .rc_interval = 100, + .rc_key_map = ir_codes_pinnacle310e_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_pinnacle310e_table), + .rc_query = m920x_rc_query, + }, .size_of_priv = sizeof(struct m920x_state), diff --git a/drivers/media/dvb/dvb-usb/nova-t-usb2.c b/drivers/media/dvb/dvb-usb/nova-t-usb2.c index d195a58..181f36a 100644 --- a/drivers/media/dvb/dvb-usb/nova-t-usb2.c +++ b/drivers/media/dvb/dvb-usb/nova-t-usb2.c @@ -21,7 +21,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); #define deb_ee(args...) dprintk(debug,0x02,args) /* Hauppauge NOVA-T USB2 keys */ -static struct dvb_usb_rc_key ir_codes_haupp_table [] = { +static struct ir_scancode ir_codes_haupp_table[] = { { 0x1e00, KEY_0 }, { 0x1e01, KEY_1 }, { 0x1e02, KEY_2 }, @@ -98,7 +98,7 @@ static int nova_t_rc_query(struct dvb_usb_device *d, u32 *event, int *state) deb_rc("c: %x, d: %x\n", rc5_data(&ir_codes_haupp_table[i]), rc5_custom(&ir_codes_haupp_table[i])); - *event = ir_codes_haupp_table[i].event; + *event = ir_codes_haupp_table[i].keycode; *state = REMOTE_KEY_PRESSED; if (st->old_toggle == toggle) { if (st->last_repeat_count++ < 2) @@ -195,10 +195,12 @@ static struct dvb_usb_device_properties nova_t_properties = { .power_ctrl = dibusb2_0_power_ctrl, .read_mac_address = nova_t_read_mac_address, - .rc_interval = 100, - .rc_key_map = ir_codes_haupp_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_haupp_table), - .rc_query = nova_t_rc_query, + .rc.legacy = { + .rc_interval = 100, + .rc_key_map = ir_codes_haupp_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_haupp_table), + .rc_query = nova_t_rc_query, + }, .i2c_algo = &dibusb_i2c_algo, diff --git a/drivers/media/dvb/dvb-usb/opera1.c b/drivers/media/dvb/dvb-usb/opera1.c index dfb81ff..6b22ec6 100644 --- a/drivers/media/dvb/dvb-usb/opera1.c +++ b/drivers/media/dvb/dvb-usb/opera1.c @@ -331,7 +331,7 @@ static int opera1_pid_filter_control(struct dvb_usb_adapter *adap, int onoff) return 0; } -static struct dvb_usb_rc_key ir_codes_opera1_table[] = { +static struct ir_scancode ir_codes_opera1_table[] = { {0x5fa0, KEY_1}, {0x51af, KEY_2}, {0x5da2, KEY_3}, @@ -407,9 +407,9 @@ static int opera1_rc_query(struct dvb_usb_device *dev, u32 * event, int *state) for (i = 0; i < ARRAY_SIZE(ir_codes_opera1_table); i++) { if (rc5_scan(&ir_codes_opera1_table[i]) == (send_key & 0xffff)) { *state = REMOTE_KEY_PRESSED; - *event = ir_codes_opera1_table[i].event; + *event = ir_codes_opera1_table[i].keycode; opst->last_key_pressed = - ir_codes_opera1_table[i].event; + ir_codes_opera1_table[i].keycode; break; } opst->last_key_pressed = 0; @@ -498,10 +498,12 @@ static struct dvb_usb_device_properties opera1_properties = { .power_ctrl = opera1_power_ctrl, .i2c_algo = &opera1_i2c_algo, - .rc_key_map = ir_codes_opera1_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_opera1_table), - .rc_interval = 200, - .rc_query = opera1_rc_query, + .rc.legacy = { + .rc_key_map = ir_codes_opera1_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_opera1_table), + .rc_interval = 200, + .rc_query = opera1_rc_query, + }, .read_mac_address = opera1_read_mac_address, .generic_bulk_ctrl_endpoint = 0x00, /* parameter for the MPEG2-data transfer */ diff --git a/drivers/media/dvb/dvb-usb/vp702x.c b/drivers/media/dvb/dvb-usb/vp702x.c index 4d33245..5c9f327 100644 --- a/drivers/media/dvb/dvb-usb/vp702x.c +++ b/drivers/media/dvb/dvb-usb/vp702x.c @@ -174,7 +174,7 @@ static int vp702x_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) } /* keys for the enclosed remote control */ -static struct dvb_usb_rc_key ir_codes_vp702x_table[] = { +static struct ir_scancode ir_codes_vp702x_table[] = { { 0x0001, KEY_1 }, { 0x0002, KEY_2 }, }; @@ -200,7 +200,7 @@ static int vp702x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) for (i = 0; i < ARRAY_SIZE(ir_codes_vp702x_table); i++) if (rc5_custom(&ir_codes_vp702x_table[i]) == key[1]) { *state = REMOTE_KEY_PRESSED; - *event = ir_codes_vp702x_table[i].event; + *event = ir_codes_vp702x_table[i].keycode; break; } return 0; @@ -283,10 +283,12 @@ static struct dvb_usb_device_properties vp702x_properties = { }, .read_mac_address = vp702x_read_mac_addr, - .rc_key_map = ir_codes_vp702x_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_vp702x_table), - .rc_interval = 400, - .rc_query = vp702x_rc_query, + .rc.legacy = { + .rc_key_map = ir_codes_vp702x_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_vp702x_table), + .rc_interval = 400, + .rc_query = vp702x_rc_query, + }, .num_device_descs = 1, .devices = { diff --git a/drivers/media/dvb/dvb-usb/vp7045.c b/drivers/media/dvb/dvb-usb/vp7045.c index 036893f..f13791c 100644 --- a/drivers/media/dvb/dvb-usb/vp7045.c +++ b/drivers/media/dvb/dvb-usb/vp7045.c @@ -99,7 +99,7 @@ static int vp7045_power_ctrl(struct dvb_usb_device *d, int onoff) /* The keymapping struct. Somehow this should be loaded to the driver, but * currently it is hardcoded. */ -static struct dvb_usb_rc_key ir_codes_vp7045_table[] = { +static struct ir_scancode ir_codes_vp7045_table[] = { { 0x0016, KEY_POWER }, { 0x0010, KEY_MUTE }, { 0x0003, KEY_1 }, @@ -168,7 +168,7 @@ static int vp7045_rc_query(struct dvb_usb_device *d, u32 *event, int *state) for (i = 0; i < ARRAY_SIZE(ir_codes_vp7045_table); i++) if (rc5_data(&ir_codes_vp7045_table[i]) == key) { *state = REMOTE_KEY_PRESSED; - *event = ir_codes_vp7045_table[i].event; + *event = ir_codes_vp7045_table[i].keycode; break; } return 0; @@ -259,10 +259,12 @@ static struct dvb_usb_device_properties vp7045_properties = { .power_ctrl = vp7045_power_ctrl, .read_mac_address = vp7045_read_mac_addr, - .rc_interval = 400, - .rc_key_map = ir_codes_vp7045_table, - .rc_key_map_size = ARRAY_SIZE(ir_codes_vp7045_table), - .rc_query = vp7045_rc_query, + .rc.legacy = { + .rc_interval = 400, + .rc_key_map = ir_codes_vp7045_table, + .rc_key_map_size = ARRAY_SIZE(ir_codes_vp7045_table), + .rc_query = vp7045_rc_query, + }, .num_device_descs = 2, .devices = { diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index cd7f9b7..51d578a 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -584,6 +584,7 @@ config DVB_LGS8GL5 config DVB_LGS8GXX tristate "Legend Silicon LGS8913/LGS8GL5/LGS8GXX DMB-TH demodulator" depends on DVB_CORE && I2C + select FW_LOADER default m if DVB_FE_CUSTOMISE help A DMB-TH tuner module. Say Y when you want to support this frontend. diff --git a/drivers/media/dvb/frontends/af9013.c b/drivers/media/dvb/frontends/af9013.c index 12e018b..dac917f 100644 --- a/drivers/media/dvb/frontends/af9013.c +++ b/drivers/media/dvb/frontends/af9013.c @@ -1,5 +1,5 @@ /* - * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver + * Afatech AF9013 demodulator driver * * Copyright (C) 2007 Antti Palosaari <crope@iki.fi> * @@ -761,6 +761,10 @@ static int af9013_set_frontend(struct dvb_frontend *fe, state->frequency = params->frequency; + /* program tuner */ + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe, params); + /* program CFOE coefficients */ ret = af9013_set_coeff(state, params->u.ofdm.bandwidth); if (ret) @@ -791,10 +795,6 @@ static int af9013_set_frontend(struct dvb_frontend *fe, if (ret) goto error; - /* program tuner */ - if (fe->ops.tuner_ops.set_params) - fe->ops.tuner_ops.set_params(fe, params); - /* program TPS and bandwidth, check if auto mode needed */ ret = af9013_set_ofdm_params(state, ¶ms->u.ofdm, &auto_mode); if (ret) @@ -1184,45 +1184,49 @@ static int af9013_read_status(struct dvb_frontend *fe, fe_status_t *status) u8 tmp; *status = 0; - /* TPS lock */ - ret = af9013_read_reg_bits(state, 0xd330, 3, 1, &tmp); - if (ret) - goto error; - if (tmp) - *status |= FE_HAS_VITERBI | FE_HAS_CARRIER | FE_HAS_SIGNAL; - /* MPEG2 lock */ ret = af9013_read_reg_bits(state, 0xd507, 6, 1, &tmp); if (ret) goto error; if (tmp) - *status |= FE_HAS_SYNC | FE_HAS_LOCK; + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | + FE_HAS_SYNC | FE_HAS_LOCK; - if (!(*status & FE_HAS_SIGNAL)) { - /* AGC lock */ - ret = af9013_read_reg_bits(state, 0xd1a0, 6, 1, &tmp); + if (!*status) { + /* TPS lock */ + ret = af9013_read_reg_bits(state, 0xd330, 3, 1, &tmp); if (ret) goto error; if (tmp) - *status |= FE_HAS_SIGNAL; + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI; } - if (!(*status & FE_HAS_CARRIER)) { + if (!*status) { /* CFO lock */ ret = af9013_read_reg_bits(state, 0xd333, 7, 1, &tmp); if (ret) goto error; if (tmp) - *status |= FE_HAS_CARRIER; + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; } - if (!(*status & FE_HAS_CARRIER)) { + if (!*status) { /* SFOE lock */ ret = af9013_read_reg_bits(state, 0xd334, 6, 1, &tmp); if (ret) goto error; if (tmp) - *status |= FE_HAS_CARRIER; + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; + } + + if (!*status) { + /* AGC lock */ + ret = af9013_read_reg_bits(state, 0xd1a0, 6, 1, &tmp); + if (ret) + goto error; + if (tmp) + *status |= FE_HAS_SIGNAL; } ret = af9013_update_statistics(fe); @@ -1574,7 +1578,7 @@ struct dvb_frontend *af9013_attach(const struct af9013_config *config, { int ret; struct af9013_state *state = NULL; - u8 buf[3], i; + u8 buf[4], i; /* allocate memory for the internal state */ state = kzalloc(sizeof(struct af9013_state), GFP_KERNEL); @@ -1607,12 +1611,12 @@ struct dvb_frontend *af9013_attach(const struct af9013_config *config, } /* firmware version */ - for (i = 0; i < 3; i++) { + for (i = 0; i < 4; i++) { ret = af9013_read_reg(state, 0x5103 + i, &buf[i]); if (ret) goto error; } - info("firmware version:%d.%d.%d", buf[0], buf[1], buf[2]); + info("firmware version:%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3]); /* settings for mp2if */ if (state->config.output_mode == AF9013_OUTPUT_MODE_USB) { diff --git a/drivers/media/dvb/frontends/af9013.h b/drivers/media/dvb/frontends/af9013.h index e90fa92..72c71bb 100644 --- a/drivers/media/dvb/frontends/af9013.h +++ b/drivers/media/dvb/frontends/af9013.h @@ -1,5 +1,5 @@ /* - * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver + * Afatech AF9013 demodulator driver * * Copyright (C) 2007 Antti Palosaari <crope@iki.fi> * diff --git a/drivers/media/dvb/frontends/af9013_priv.h b/drivers/media/dvb/frontends/af9013_priv.h index 163e251..0fd42b7 100644 --- a/drivers/media/dvb/frontends/af9013_priv.h +++ b/drivers/media/dvb/frontends/af9013_priv.h @@ -1,5 +1,5 @@ /* - * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver + * Afatech AF9013 demodulator driver * * Copyright (C) 2007 Antti Palosaari <crope@iki.fi> * @@ -132,6 +132,8 @@ static struct regdesc ofsm_init[] = { { 0xd740, 2, 1, 0x00 }, { 0xd740, 3, 1, 0x01 }, { 0xd3c1, 4, 1, 0x01 }, + { 0x9124, 0, 8, 0x58 }, + { 0x9125, 0, 2, 0x02 }, { 0xd3a2, 0, 8, 0x00 }, { 0xd3a3, 0, 8, 0x04 }, { 0xd305, 0, 8, 0x32 }, @@ -143,7 +145,7 @@ static struct regdesc ofsm_init[] = { { 0x911b, 0, 1, 0x01 }, { 0x9bce, 0, 4, 0x02 }, { 0x9116, 0, 1, 0x01 }, - { 0x9bd1, 0, 1, 0x01 }, + { 0x9122, 0, 8, 0xd0 }, { 0xd2e0, 0, 8, 0xd0 }, { 0xd2e9, 0, 4, 0x0d }, { 0xd38c, 0, 8, 0xfc }, @@ -165,7 +167,6 @@ static struct regdesc ofsm_init[] = { { 0xd081, 4, 4, 0x09 }, { 0xd098, 4, 4, 0x0f }, { 0xd098, 0, 4, 0x03 }, - { 0xdbc0, 3, 1, 0x01 }, { 0xdbc0, 4, 1, 0x01 }, { 0xdbc7, 0, 8, 0x08 }, { 0xdbc8, 4, 4, 0x00 }, @@ -179,6 +180,7 @@ static struct regdesc ofsm_init[] = { { 0xd0f0, 0, 7, 0x1a }, { 0xd0f1, 4, 1, 0x01 }, { 0xd0f2, 0, 8, 0x0c }, + { 0xd101, 5, 3, 0x06 }, { 0xd103, 0, 4, 0x08 }, { 0xd0f8, 0, 7, 0x20 }, { 0xd111, 5, 1, 0x00 }, diff --git a/drivers/media/dvb/frontends/dib3000mb.c b/drivers/media/dvb/frontends/dib3000mb.c index ad4c8cf..e80c597 100644 --- a/drivers/media/dvb/frontends/dib3000mb.c +++ b/drivers/media/dvb/frontends/dib3000mb.c @@ -38,11 +38,10 @@ #define DRIVER_DESC "DiBcom 3000M-B DVB-T demodulator" #define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de" -#ifdef CONFIG_DVB_DIBCOM_DEBUG static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=setfe,8=getfe (|-able))."); -#endif + #define deb_info(args...) dprintk(0x01,args) #define deb_i2c(args...) dprintk(0x02,args) #define deb_srch(args...) dprintk(0x04,args) @@ -51,12 +50,6 @@ MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=setfe,8=getfe (|-a #define deb_setf(args...) dprintk(0x04,args) #define deb_getf(args...) dprintk(0x08,args) -#ifdef CONFIG_DVB_DIBCOM_DEBUG -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "set debugging level (1=info,2=i2c,4=srch (|-able))."); -#endif - static int dib3000_read_reg(struct dib3000_state *state, u16 reg) { u8 wb[] = { ((reg >> 8) | 0x80) & 0xff, reg & 0xff }; diff --git a/drivers/media/dvb/frontends/dib3000mb_priv.h b/drivers/media/dvb/frontends/dib3000mb_priv.h index 1a12747..16c5265 100644 --- a/drivers/media/dvb/frontends/dib3000mb_priv.h +++ b/drivers/media/dvb/frontends/dib3000mb_priv.h @@ -37,12 +37,8 @@ /* debug */ -#ifdef CONFIG_DVB_DIBCOM_DEBUG #define dprintk(level,args...) \ do { if ((debug & level)) { printk(args); } } while (0) -#else -#define dprintk(args...) do { } while (0) -#endif /* mask for enabling a specific pid for the pid_filter */ #define DIB3000_ACTIVATE_PID_FILTERING (0x2000) diff --git a/drivers/media/dvb/frontends/dib3000mc.c b/drivers/media/dvb/frontends/dib3000mc.c index afad252..088e7fa 100644 --- a/drivers/media/dvb/frontends/dib3000mc.c +++ b/drivers/media/dvb/frontends/dib3000mc.c @@ -822,7 +822,7 @@ int dib3000mc_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defa dmcst = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL); if (dmcst == NULL) - return -ENODEV; + return -ENOMEM; dmcst->i2c_adap = i2c; diff --git a/drivers/media/dvb/frontends/lgdt3305.c b/drivers/media/dvb/frontends/lgdt3305.c index d69c775..3272881 100644 --- a/drivers/media/dvb/frontends/lgdt3305.c +++ b/drivers/media/dvb/frontends/lgdt3305.c @@ -1,7 +1,9 @@ /* - * Support for LGDT3305 - VSB/QAM + * Support for LG Electronics LGDT3304 and LGDT3305 - VSB/QAM * - * Copyright (C) 2008, 2009 Michael Krufky <mkrufky@linuxtv.org> + * Copyright (C) 2008, 2009, 2010 Michael Krufky <mkrufky@linuxtv.org> + * + * LGDT3304 support by Jarod Wilson <jarod@redhat.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 @@ -65,6 +67,8 @@ struct lgdt3305_state { /* ------------------------------------------------------------------------ */ +/* FIXME: verify & document the LGDT3304 registers */ + #define LGDT3305_GEN_CTRL_1 0x0000 #define LGDT3305_GEN_CTRL_2 0x0001 #define LGDT3305_GEN_CTRL_3 0x0002 @@ -358,7 +362,12 @@ static int lgdt3305_rfagc_loop(struct lgdt3305_state *state, case QAM_256: agcdelay = 0x046b; rfbw = 0x8889; - ifbw = 0x8888; + /* FIXME: investigate optimal ifbw & rfbw values for the + * DT3304 and re-write this switch..case block */ + if (state->cfg->demod_chip == LGDT3304) + ifbw = 0x6666; + else /* (state->cfg->demod_chip == LGDT3305) */ + ifbw = 0x8888; break; default: return -EINVAL; @@ -410,8 +419,18 @@ static int lgdt3305_agc_setup(struct lgdt3305_state *state, lg_dbg("lockdten = %d, acqen = %d\n", lockdten, acqen); /* control agc function */ - lgdt3305_write_reg(state, LGDT3305_AGC_CTRL_4, 0xe1 | lockdten << 1); - lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 2, acqen); + switch (state->cfg->demod_chip) { + case LGDT3304: + lgdt3305_write_reg(state, 0x0314, 0xe1 | lockdten << 1); + lgdt3305_set_reg_bit(state, 0x030e, 2, acqen); + break; + case LGDT3305: + lgdt3305_write_reg(state, LGDT3305_AGC_CTRL_4, 0xe1 | lockdten << 1); + lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 2, acqen); + break; + default: + return -EINVAL; + } return lgdt3305_rfagc_loop(state, param); } @@ -577,61 +596,79 @@ static int lgdt3305_init(struct dvb_frontend *fe) struct lgdt3305_state *state = fe->demodulator_priv; int ret; + static struct lgdt3305_reg lgdt3304_init_data[] = { + { .reg = LGDT3305_GEN_CTRL_1, .val = 0x03, }, + { .reg = 0x000d, .val = 0x02, }, + { .reg = 0x000e, .val = 0x02, }, + { .reg = LGDT3305_DGTL_AGC_REF_1, .val = 0x32, }, + { .reg = LGDT3305_DGTL_AGC_REF_2, .val = 0xc4, }, + { .reg = LGDT3305_CR_CTR_FREQ_1, .val = 0x00, }, + { .reg = LGDT3305_CR_CTR_FREQ_2, .val = 0x00, }, + { .reg = LGDT3305_CR_CTR_FREQ_3, .val = 0x00, }, + { .reg = LGDT3305_CR_CTR_FREQ_4, .val = 0x00, }, + { .reg = LGDT3305_CR_CTRL_7, .val = 0xf9, }, + { .reg = 0x0112, .val = 0x17, }, + { .reg = 0x0113, .val = 0x15, }, + { .reg = 0x0114, .val = 0x18, }, + { .reg = 0x0115, .val = 0xff, }, + { .reg = 0x0116, .val = 0x3c, }, + { .reg = 0x0214, .val = 0x67, }, + { .reg = 0x0424, .val = 0x8d, }, + { .reg = 0x0427, .val = 0x12, }, + { .reg = 0x0428, .val = 0x4f, }, + { .reg = LGDT3305_IFBW_1, .val = 0x80, }, + { .reg = LGDT3305_IFBW_2, .val = 0x00, }, + { .reg = 0x030a, .val = 0x08, }, + { .reg = 0x030b, .val = 0x9b, }, + { .reg = 0x030d, .val = 0x00, }, + { .reg = 0x030e, .val = 0x1c, }, + { .reg = 0x0314, .val = 0xe1, }, + { .reg = 0x000d, .val = 0x82, }, + { .reg = LGDT3305_TP_CTRL_1, .val = 0x5b, }, + { .reg = LGDT3305_TP_CTRL_1, .val = 0x5b, }, + }; + static struct lgdt3305_reg lgdt3305_init_data[] = { - { .reg = LGDT3305_GEN_CTRL_1, - .val = 0x03, }, - { .reg = LGDT3305_GEN_CTRL_2, - .val = 0xb0, }, - { .reg = LGDT3305_GEN_CTRL_3, - .val = 0x01, }, - { .reg = LGDT3305_GEN_CONTROL, - .val = 0x6f, }, - { .reg = LGDT3305_GEN_CTRL_4, - .val = 0x03, }, - { .reg = LGDT3305_DGTL_AGC_REF_1, - .val = 0x32, }, - { .reg = LGDT3305_DGTL_AGC_REF_2, - .val = 0xc4, }, - { .reg = LGDT3305_CR_CTR_FREQ_1, - .val = 0x00, }, - { .reg = LGDT3305_CR_CTR_FREQ_2, - .val = 0x00, }, - { .reg = LGDT3305_CR_CTR_FREQ_3, - .val = 0x00, }, - { .reg = LGDT3305_CR_CTR_FREQ_4, - .val = 0x00, }, - { .reg = LGDT3305_CR_CTRL_7, - .val = 0x79, }, - { .reg = LGDT3305_AGC_POWER_REF_1, - .val = 0x32, }, - { .reg = LGDT3305_AGC_POWER_REF_2, - .val = 0xc4, }, - { .reg = LGDT3305_AGC_DELAY_PT_1, - .val = 0x0d, }, - { .reg = LGDT3305_AGC_DELAY_PT_2, - .val = 0x30, }, - { .reg = LGDT3305_RFAGC_LOOP_FLTR_BW_1, - .val = 0x80, }, - { .reg = LGDT3305_RFAGC_LOOP_FLTR_BW_2, - .val = 0x00, }, - { .reg = LGDT3305_IFBW_1, - .val = 0x80, }, - { .reg = LGDT3305_IFBW_2, - .val = 0x00, }, - { .reg = LGDT3305_AGC_CTRL_1, - .val = 0x30, }, - { .reg = LGDT3305_AGC_CTRL_4, - .val = 0x61, }, - { .reg = LGDT3305_FEC_BLOCK_CTRL, - .val = 0xff, }, - { .reg = LGDT3305_TP_CTRL_1, - .val = 0x1b, }, + { .reg = LGDT3305_GEN_CTRL_1, .val = 0x03, }, + { .reg = LGDT3305_GEN_CTRL_2, .val = 0xb0, }, + { .reg = LGDT3305_GEN_CTRL_3, .val = 0x01, }, + { .reg = LGDT3305_GEN_CONTROL, .val = 0x6f, }, + { .reg = LGDT3305_GEN_CTRL_4, .val = 0x03, }, + { .reg = LGDT3305_DGTL_AGC_REF_1, .val = 0x32, }, + { .reg = LGDT3305_DGTL_AGC_REF_2, .val = 0xc4, }, + { .reg = LGDT3305_CR_CTR_FREQ_1, .val = 0x00, }, + { .reg = LGDT3305_CR_CTR_FREQ_2, .val = 0x00, }, + { .reg = LGDT3305_CR_CTR_FREQ_3, .val = 0x00, }, + { .reg = LGDT3305_CR_CTR_FREQ_4, .val = 0x00, }, + { .reg = LGDT3305_CR_CTRL_7, .val = 0x79, }, + { .reg = LGDT3305_AGC_POWER_REF_1, .val = 0x32, }, + { .reg = LGDT3305_AGC_POWER_REF_2, .val = 0xc4, }, + { .reg = LGDT3305_AGC_DELAY_PT_1, .val = 0x0d, }, + { .reg = LGDT3305_AGC_DELAY_PT_2, .val = 0x30, }, + { .reg = LGDT3305_RFAGC_LOOP_FLTR_BW_1, .val = 0x80, }, + { .reg = LGDT3305_RFAGC_LOOP_FLTR_BW_2, .val = 0x00, }, + { .reg = LGDT3305_IFBW_1, .val = 0x80, }, + { .reg = LGDT3305_IFBW_2, .val = 0x00, }, + { .reg = LGDT3305_AGC_CTRL_1, .val = 0x30, }, + { .reg = LGDT3305_AGC_CTRL_4, .val = 0x61, }, + { .reg = LGDT3305_FEC_BLOCK_CTRL, .val = 0xff, }, + { .reg = LGDT3305_TP_CTRL_1, .val = 0x1b, }, }; lg_dbg("\n"); - ret = lgdt3305_write_regs(state, lgdt3305_init_data, - ARRAY_SIZE(lgdt3305_init_data)); + switch (state->cfg->demod_chip) { + case LGDT3304: + ret = lgdt3305_write_regs(state, lgdt3304_init_data, + ARRAY_SIZE(lgdt3304_init_data)); + break; + case LGDT3305: + ret = lgdt3305_write_regs(state, lgdt3305_init_data, + ARRAY_SIZE(lgdt3305_init_data)); + break; + default: + ret = -EINVAL; + } if (lg_fail(ret)) goto fail; @@ -640,6 +677,76 @@ fail: return ret; } +static int lgdt3304_set_parameters(struct dvb_frontend *fe, + struct dvb_frontend_parameters *param) +{ + struct lgdt3305_state *state = fe->demodulator_priv; + int ret; + + lg_dbg("(%d, %d)\n", param->frequency, param->u.vsb.modulation); + + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe, param); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + if (lg_fail(ret)) + goto fail; + state->current_frequency = param->frequency; + } + + ret = lgdt3305_set_modulation(state, param); + if (lg_fail(ret)) + goto fail; + + ret = lgdt3305_passband_digital_agc(state, param); + if (lg_fail(ret)) + goto fail; + + ret = lgdt3305_agc_setup(state, param); + if (lg_fail(ret)) + goto fail; + + /* reg 0x030d is 3304-only... seen in vsb and qam usbsnoops... */ + switch (param->u.vsb.modulation) { + case VSB_8: + lgdt3305_write_reg(state, 0x030d, 0x00); + lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_1, 0x4f); + lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_2, 0x0c); + lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_3, 0xac); + lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_4, 0xba); + break; + case QAM_64: + case QAM_256: + lgdt3305_write_reg(state, 0x030d, 0x14); + ret = lgdt3305_set_if(state, param); + if (lg_fail(ret)) + goto fail; + break; + default: + return -EINVAL; + } + + + ret = lgdt3305_spectral_inversion(state, param, + state->cfg->spectral_inversion + ? 1 : 0); + if (lg_fail(ret)) + goto fail; + + state->current_modulation = param->u.vsb.modulation; + + ret = lgdt3305_mpeg_mode(state, state->cfg->mpeg_mode); + if (lg_fail(ret)) + goto fail; + + /* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */ + ret = lgdt3305_mpeg_mode_polarity(state, + state->cfg->tpclk_edge, + state->cfg->tpvalid_polarity); +fail: + return ret; +} + static int lgdt3305_set_parameters(struct dvb_frontend *fe, struct dvb_frontend_parameters *param) { @@ -848,6 +955,10 @@ static int lgdt3305_read_status(struct dvb_frontend *fe, fe_status_t *status) switch (state->current_modulation) { case QAM_256: case QAM_64: + /* signal bit is unreliable on the DT3304 in QAM mode */ + if (((LGDT3304 == state->cfg->demod_chip)) && (cr_lock)) + *status |= FE_HAS_SIGNAL; + ret = lgdt3305_read_fec_lock_status(state, &fec_lock); if (lg_fail(ret)) goto fail; @@ -993,6 +1104,7 @@ static void lgdt3305_release(struct dvb_frontend *fe) kfree(state); } +static struct dvb_frontend_ops lgdt3304_ops; static struct dvb_frontend_ops lgdt3305_ops; struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, @@ -1013,11 +1125,21 @@ struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, state->cfg = config; state->i2c_adap = i2c_adap; - memcpy(&state->frontend.ops, &lgdt3305_ops, - sizeof(struct dvb_frontend_ops)); + switch (config->demod_chip) { + case LGDT3304: + memcpy(&state->frontend.ops, &lgdt3304_ops, + sizeof(struct dvb_frontend_ops)); + break; + case LGDT3305: + memcpy(&state->frontend.ops, &lgdt3305_ops, + sizeof(struct dvb_frontend_ops)); + break; + default: + goto fail; + } state->frontend.demodulator_priv = state; - /* verify that we're talking to a lg dt3305 */ + /* verify that we're talking to a lg dt3304/5 */ ret = lgdt3305_read_reg(state, LGDT3305_GEN_CTRL_2, &val); if ((lg_fail(ret)) | (val == 0)) goto fail; @@ -1036,12 +1158,35 @@ struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, return &state->frontend; fail: - lg_warn("unable to detect LGDT3305 hardware\n"); + lg_warn("unable to detect %s hardware\n", + config->demod_chip ? "LGDT3304" : "LGDT3305"); kfree(state); return NULL; } EXPORT_SYMBOL(lgdt3305_attach); +static struct dvb_frontend_ops lgdt3304_ops = { + .info = { + .name = "LG Electronics LGDT3304 VSB/QAM Frontend", + .type = FE_ATSC, + .frequency_min = 54000000, + .frequency_max = 858000000, + .frequency_stepsize = 62500, + .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB + }, + .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl, + .init = lgdt3305_init, + .set_frontend = lgdt3304_set_parameters, + .get_frontend = lgdt3305_get_frontend, + .get_tune_settings = lgdt3305_get_tune_settings, + .read_status = lgdt3305_read_status, + .read_ber = lgdt3305_read_ber, + .read_signal_strength = lgdt3305_read_signal_strength, + .read_snr = lgdt3305_read_snr, + .read_ucblocks = lgdt3305_read_ucblocks, + .release = lgdt3305_release, +}; + static struct dvb_frontend_ops lgdt3305_ops = { .info = { .name = "LG Electronics LGDT3305 VSB/QAM Frontend", @@ -1065,10 +1210,10 @@ static struct dvb_frontend_ops lgdt3305_ops = { .release = lgdt3305_release, }; -MODULE_DESCRIPTION("LG Electronics LGDT3305 ATSC/QAM-B Demodulator Driver"); +MODULE_DESCRIPTION("LG Electronics LGDT3304/5 ATSC/QAM-B Demodulator Driver"); MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.1"); +MODULE_VERSION("0.2"); /* * Local variables: diff --git a/drivers/media/dvb/frontends/lgdt3305.h b/drivers/media/dvb/frontends/lgdt3305.h index 9cb11c9..02172ec 100644 --- a/drivers/media/dvb/frontends/lgdt3305.h +++ b/drivers/media/dvb/frontends/lgdt3305.h @@ -1,7 +1,7 @@ /* - * Support for LGDT3305 - VSB/QAM + * Support for LG Electronics LGDT3304 and LGDT3305 - VSB/QAM * - * Copyright (C) 2008, 2009 Michael Krufky <mkrufky@linuxtv.org> + * Copyright (C) 2008, 2009, 2010 Michael Krufky <mkrufky@linuxtv.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 @@ -41,6 +41,11 @@ enum lgdt3305_tp_valid_polarity { LGDT3305_TP_VALID_HIGH = 1, }; +enum lgdt_demod_chip_type { + LGDT3305 = 0, + LGDT3304 = 1, +}; + struct lgdt3305_config { u8 i2c_addr; @@ -65,6 +70,7 @@ struct lgdt3305_config { enum lgdt3305_mpeg_mode mpeg_mode; enum lgdt3305_tp_clock_edge tpclk_edge; enum lgdt3305_tp_valid_polarity tpvalid_polarity; + enum lgdt_demod_chip_type demod_chip; }; #if defined(CONFIG_DVB_LGDT3305) || (defined(CONFIG_DVB_LGDT3305_MODULE) && \ diff --git a/drivers/media/dvb/frontends/lgs8gxx.c b/drivers/media/dvb/frontends/lgs8gxx.c index dee5396..5ea28ae 100644 --- a/drivers/media/dvb/frontends/lgs8gxx.c +++ b/drivers/media/dvb/frontends/lgs8gxx.c @@ -24,6 +24,7 @@ */ #include <asm/div64.h> +#include <linux/firmware.h> #include "dvb_frontend.h" @@ -46,42 +47,6 @@ module_param(fake_signal_str, int, 0644); MODULE_PARM_DESC(fake_signal_str, "fake signal strength for LGS8913." "Signal strength calculation is slow.(default:on)."); -static const u8 lgs8g75_initdat[] = { - 0x01, 0x30, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xE4, 0xF5, 0xA8, 0xF5, 0xB8, 0xF5, 0x88, 0xF5, - 0x89, 0xF5, 0x87, 0x75, 0xD0, 0x00, 0x11, 0x50, - 0x11, 0x50, 0xF4, 0xF5, 0x80, 0xF5, 0x90, 0xF5, - 0xA0, 0xF5, 0xB0, 0x75, 0x81, 0x30, 0x80, 0x01, - 0x32, 0x90, 0x80, 0x12, 0x74, 0xFF, 0xF0, 0x90, - 0x80, 0x13, 0x74, 0x1F, 0xF0, 0x90, 0x80, 0x23, - 0x74, 0x01, 0xF0, 0x90, 0x80, 0x22, 0xF0, 0x90, - 0x00, 0x48, 0x74, 0x00, 0xF0, 0x90, 0x80, 0x4D, - 0x74, 0x05, 0xF0, 0x90, 0x80, 0x09, 0xE0, 0x60, - 0x21, 0x12, 0x00, 0xDD, 0x14, 0x60, 0x1B, 0x12, - 0x00, 0xDD, 0x14, 0x60, 0x15, 0x12, 0x00, 0xDD, - 0x14, 0x60, 0x0F, 0x12, 0x00, 0xDD, 0x14, 0x60, - 0x09, 0x12, 0x00, 0xDD, 0x14, 0x60, 0x03, 0x12, - 0x00, 0xDD, 0x90, 0x80, 0x42, 0xE0, 0x60, 0x0B, - 0x14, 0x60, 0x0C, 0x14, 0x60, 0x0D, 0x14, 0x60, - 0x0E, 0x01, 0xB3, 0x74, 0x04, 0x01, 0xB9, 0x74, - 0x05, 0x01, 0xB9, 0x74, 0x07, 0x01, 0xB9, 0x74, - 0x0A, 0xC0, 0xE0, 0x74, 0xC8, 0x12, 0x00, 0xE2, - 0xD0, 0xE0, 0x14, 0x70, 0xF4, 0x90, 0x80, 0x09, - 0xE0, 0x70, 0xAE, 0x12, 0x00, 0xF6, 0x12, 0x00, - 0xFE, 0x90, 0x00, 0x48, 0xE0, 0x04, 0xF0, 0x90, - 0x80, 0x4E, 0xF0, 0x01, 0x73, 0x90, 0x80, 0x08, - 0xF0, 0x22, 0xF8, 0x7A, 0x0C, 0x79, 0xFD, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, - 0xF6, 0xDA, 0xF2, 0xD8, 0xEE, 0x22, 0x90, 0x80, - 0x65, 0xE0, 0x54, 0xFD, 0xF0, 0x22, 0x90, 0x80, - 0x65, 0xE0, 0x44, 0xC2, 0xF0, 0x22 -}; - /* LGS8GXX internal helper functions */ static int lgs8gxx_write_reg(struct lgs8gxx_state *priv, u8 reg, u8 data) @@ -627,9 +592,14 @@ static int lgs8913_init(struct lgs8gxx_state *priv) static int lgs8g75_init_data(struct lgs8gxx_state *priv) { - const u8 *p = lgs8g75_initdat; + const struct firmware *fw; + int rc; int i; + rc = request_firmware(&fw, "lgs8g75.fw", &priv->i2c->dev); + if (rc) + return rc; + lgs8gxx_write_reg(priv, 0xC6, 0x40); lgs8gxx_write_reg(priv, 0x3D, 0x04); @@ -640,16 +610,16 @@ static int lgs8g75_init_data(struct lgs8gxx_state *priv) lgs8gxx_write_reg(priv, 0x3B, 0x00); lgs8gxx_write_reg(priv, 0x38, 0x00); - for (i = 0; i < sizeof(lgs8g75_initdat); i++) { + for (i = 0; i < fw->size; i++) { lgs8gxx_write_reg(priv, 0x38, 0x00); lgs8gxx_write_reg(priv, 0x3A, (u8)(i&0xff)); lgs8gxx_write_reg(priv, 0x3B, (u8)(i>>8)); - lgs8gxx_write_reg(priv, 0x3C, *p); - p++; + lgs8gxx_write_reg(priv, 0x3C, fw->data[i]); } lgs8gxx_write_reg(priv, 0x38, 0x00); + release_firmware(fw); return 0; } diff --git a/drivers/media/dvb/frontends/mb86a16.c b/drivers/media/dvb/frontends/mb86a16.c index 599d1aa..33b6323 100644 --- a/drivers/media/dvb/frontends/mb86a16.c +++ b/drivers/media/dvb/frontends/mb86a16.c @@ -1833,7 +1833,6 @@ static struct dvb_frontend_ops mb86a16_ops = { .get_frontend_algo = mb86a16_frontend_algo, .search = mb86a16_search, - .read_status = mb86a16_read_status, .init = mb86a16_init, .sleep = mb86a16_sleep, .read_status = mb86a16_read_status, diff --git a/drivers/media/dvb/frontends/tda10048.c b/drivers/media/dvb/frontends/tda10048.c index 4e2a7c8..93f6a75 100644 --- a/drivers/media/dvb/frontends/tda10048.c +++ b/drivers/media/dvb/frontends/tda10048.c @@ -25,6 +25,7 @@ #include <linux/string.h> #include <linux/slab.h> #include <linux/delay.h> +#include <linux/math64.h> #include <asm/div64.h> #include "dvb_frontend.h" #include "dvb_math.h" @@ -49,8 +50,8 @@ #define TDA10048_CONF_C4_1 0x1E #define TDA10048_CONF_C4_2 0x1F #define TDA10048_CODE_IN_RAM 0x20 -#define TDA10048_CHANNEL_INFO_1_R 0x22 -#define TDA10048_CHANNEL_INFO_2_R 0x23 +#define TDA10048_CHANNEL_INFO1_R 0x22 +#define TDA10048_CHANNEL_INFO2_R 0x23 #define TDA10048_CHANNEL_INFO1 0x24 #define TDA10048_CHANNEL_INFO2 0x25 #define TDA10048_TIME_ERROR_R 0x26 @@ -63,8 +64,8 @@ #define TDA10048_IT_STAT 0x32 #define TDA10048_DSP_AD_LSB 0x3C #define TDA10048_DSP_AD_MSB 0x3D -#define TDA10048_DSP_REF_LSB 0x3E -#define TDA10048_DSP_REF_MSB 0x3F +#define TDA10048_DSP_REG_LSB 0x3E +#define TDA10048_DSP_REG_MSB 0x3F #define TDA10048_CONF_TRISTATE1 0x44 #define TDA10048_CONF_TRISTATE2 0x45 #define TDA10048_CONF_POLARITY 0x46 @@ -112,7 +113,7 @@ #define TDA10048_FREE_REG_1 0xB2 #define TDA10048_FREE_REG_2 0xB3 #define TDA10048_CONF_C3_1 0xC0 -#define TDA10048_CYBER_CTRL 0xC2 +#define TDA10048_CVBER_CTRL 0xC2 #define TDA10048_CBER_NMAX_LSB 0xC4 #define TDA10048_CBER_NMAX_MSB 0xC5 #define TDA10048_CBER_LSB 0xC6 @@ -120,7 +121,7 @@ #define TDA10048_VBER_LSB 0xC8 #define TDA10048_VBER_MID 0xC9 #define TDA10048_VBER_MSB 0xCA -#define TDA10048_CYBER_LUT 0xCC +#define TDA10048_CVBER_LUT 0xCC #define TDA10048_UNCOR_CTRL 0xCD #define TDA10048_UNCOR_CPT_LSB 0xCE #define TDA10048_UNCOR_CPT_MSB 0xCF @@ -183,7 +184,7 @@ static struct init_tab { { TDA10048_AGC_IF_MAX, 0xff }, { TDA10048_AGC_THRESHOLD_MSB, 0x00 }, { TDA10048_AGC_THRESHOLD_LSB, 0x70 }, - { TDA10048_CYBER_CTRL, 0x38 }, + { TDA10048_CVBER_CTRL, 0x38 }, { TDA10048_AGC_GAINS, 0x12 }, { TDA10048_CONF_XO, 0x00 }, { TDA10048_CONF_TS1, 0x07 }, @@ -688,7 +689,7 @@ static int tda10048_get_tps(struct tda10048_state *state, p->guard_interval = GUARD_INTERVAL_1_4; break; } - switch (val & 0x02) { + switch (val & 0x03) { case 0: p->transmission_mode = TRANSMISSION_MODE_2K; break; @@ -765,6 +766,8 @@ static int tda10048_set_frontend(struct dvb_frontend *fe, /* Enable demod TPS auto detection and begin acquisition */ tda10048_writereg(state, TDA10048_AUTO, 0x57); + /* trigger cber and vber acquisition */ + tda10048_writereg(state, TDA10048_CVBER_CTRL, 0x3B); return 0; } @@ -830,12 +833,27 @@ static int tda10048_read_status(struct dvb_frontend *fe, fe_status_t *status) static int tda10048_read_ber(struct dvb_frontend *fe, u32 *ber) { struct tda10048_state *state = fe->demodulator_priv; + static u32 cber_current; + u32 cber_nmax; + u64 cber_tmp; dprintk(1, "%s()\n", __func__); - /* TODO: A reset may be required here */ - *ber = tda10048_readreg(state, TDA10048_CBER_MSB) << 8 | - tda10048_readreg(state, TDA10048_CBER_LSB); + /* update cber on interrupt */ + if (tda10048_readreg(state, TDA10048_SOFT_IT_C3) & 0x01) { + cber_tmp = tda10048_readreg(state, TDA10048_CBER_MSB) << 8 | + tda10048_readreg(state, TDA10048_CBER_LSB); + cber_nmax = tda10048_readreg(state, TDA10048_CBER_NMAX_MSB) << 8 | + tda10048_readreg(state, TDA10048_CBER_NMAX_LSB); + cber_tmp *= 100000000; + cber_tmp *= 2; + cber_tmp = div_u64(cber_tmp, (cber_nmax * 32) + 1); + cber_current = (u32)cber_tmp; + /* retrigger cber acquisition */ + tda10048_writereg(state, TDA10048_CVBER_CTRL, 0x39); + } + /* actual cber is (*ber)/1e8 */ + *ber = cber_current; return 0; } @@ -1015,6 +1033,9 @@ static int tda10048_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) *ucblocks = tda10048_readreg(state, TDA10048_UNCOR_CPT_MSB) << 8 | tda10048_readreg(state, TDA10048_UNCOR_CPT_LSB); + /* clear the uncorrected TS packets counter when saturated */ + if (*ucblocks == 0xFFFF) + tda10048_writereg(state, TDA10048_UNCOR_CTRL, 0x80); return 0; } diff --git a/drivers/media/dvb/mantis/Kconfig b/drivers/media/dvb/mantis/Kconfig index f7b72a3..decdeda 100644 --- a/drivers/media/dvb/mantis/Kconfig +++ b/drivers/media/dvb/mantis/Kconfig @@ -10,9 +10,15 @@ config MANTIS_CORE config DVB_MANTIS tristate "MANTIS based cards" depends on MANTIS_CORE && DVB_CORE && PCI && I2C - select DVB_MB86A16 - select DVB_ZL10353 - select DVB_STV0299 + select DVB_MB86A16 if !DVB_FE_CUSTOMISE + select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_LNBP21 if !DVB_FE_CUSTOMISE + select DVB_STB0899 if !DVB_FE_CUSTOMISE + select DVB_STB6100 if !DVB_FE_CUSTOMISE + select DVB_TDA665x if !DVB_FE_CUSTOMISE + select DVB_TDA10021 if !DVB_FE_CUSTOMISE + select DVB_TDA10023 if !DVB_FE_CUSTOMISE select DVB_PLL help Support for PCI cards based on the Mantis PCI bridge. @@ -23,7 +29,7 @@ config DVB_MANTIS config DVB_HOPPER tristate "HOPPER based cards" depends on MANTIS_CORE && DVB_CORE && PCI && I2C - select DVB_ZL10353 + select DVB_ZL10353 if !DVB_FE_CUSTOMISE select DVB_PLL help Support for PCI cards based on the Hopper PCI bridge. diff --git a/drivers/media/dvb/mantis/mantis_input.c b/drivers/media/dvb/mantis/mantis_input.c index 3d4e466..a99489b 100644 --- a/drivers/media/dvb/mantis/mantis_input.c +++ b/drivers/media/dvb/mantis/mantis_input.c @@ -19,7 +19,7 @@ */ #include <linux/input.h> -#include <media/ir-common.h> +#include <media/ir-core.h> #include <linux/pci.h> #include "dmxdev.h" @@ -104,7 +104,6 @@ EXPORT_SYMBOL_GPL(ir_mantis); int mantis_input_init(struct mantis_pci *mantis) { struct input_dev *rc; - struct ir_input_state rc_state; char name[80], dev[80]; int err; @@ -120,8 +119,6 @@ int mantis_input_init(struct mantis_pci *mantis) rc->name = name; rc->phys = dev; - ir_input_init(rc, &rc_state, IR_TYPE_OTHER); - rc->id.bustype = BUS_PCI; rc->id.vendor = mantis->vendor_id; rc->id.product = mantis->device_id; diff --git a/drivers/media/dvb/siano/sms-cards.c b/drivers/media/dvb/siano/sms-cards.c index cff77e2..25b43e5 100644 --- a/drivers/media/dvb/siano/sms-cards.c +++ b/drivers/media/dvb/siano/sms-cards.c @@ -64,9 +64,11 @@ static struct sms_board sms_boards[] = { .type = SMS_NOVA_B0, .fw[DEVICE_MODE_ISDBT_BDA] = "sms1xxx-hcw-55xxx-isdbt-02.fw", .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", + .rc_codes = RC_MAP_RC5_HAUPPAUGE_NEW, .board_cfg.leds_power = 26, .board_cfg.led0 = 27, .board_cfg.led1 = 28, + .board_cfg.ir = 9, .led_power = 26, .led_lo = 27, .led_hi = 28, diff --git a/drivers/media/dvb/siano/sms-cards.h b/drivers/media/dvb/siano/sms-cards.h index 8f19fc0..d8cdf75 100644 --- a/drivers/media/dvb/siano/sms-cards.h +++ b/drivers/media/dvb/siano/sms-cards.h @@ -75,7 +75,7 @@ struct sms_board { enum sms_device_type_st type; char *name, *fw[DEVICE_MODE_MAX]; struct sms_board_gpio_cfg board_cfg; - enum ir_kb_type ir_kb_type; + char *rc_codes; /* Name of IR codes table */ /* gpios */ int led_power, led_hi, led_lo, lna_ctrl, rf_switch; diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c index 0c87a3c..828bcc2 100644 --- a/drivers/media/dvb/siano/smscoreapi.c +++ b/drivers/media/dvb/siano/smscoreapi.c @@ -116,9 +116,7 @@ static struct smscore_registry_entry_t *smscore_find_registry(char *devpath) return entry; } } - entry = (struct smscore_registry_entry_t *) - kmalloc(sizeof(struct smscore_registry_entry_t), - GFP_KERNEL); + entry = kmalloc(sizeof(struct smscore_registry_entry_t), GFP_KERNEL); if (entry) { entry->mode = default_mode; strcpy(entry->devpath, devpath); diff --git a/drivers/media/dvb/siano/smsir.c b/drivers/media/dvb/siano/smsir.c index a56eac7..d0e4639 100644 --- a/drivers/media/dvb/siano/smsir.c +++ b/drivers/media/dvb/siano/smsir.c @@ -4,6 +4,11 @@ MDTV receiver kernel modules. Copyright (C) 2006-2009, Uri Shkolnik + Copyright (c) 2010 - Mauro Carvalho Chehab + - Ported the driver to use rc-core + - IR raw event decoding is now done at rc-core + - Code almost re-written + 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 @@ -27,226 +32,28 @@ #include "smsir.h" #include "sms-cards.h" -/* In order to add new IR remote control - - * 1) Add it to the <enum ir_kb_type> @ smsir,h, - * 2) Add its map to keyboard_layout_maps below - * 3) Set your board (sms-cards sub-module) to use it - */ - -static struct keyboard_layout_map_t keyboard_layout_maps[] = { - [SMS_IR_KB_DEFAULT_TV] = { - .ir_protocol = IR_RC5, - .rc5_kbd_address = KEYBOARD_ADDRESS_TV1, - .keyboard_layout_map = { - KEY_0, KEY_1, KEY_2, - KEY_3, KEY_4, KEY_5, - KEY_6, KEY_7, KEY_8, - KEY_9, 0, 0, KEY_POWER, - KEY_MUTE, 0, 0, - KEY_VOLUMEUP, KEY_VOLUMEDOWN, - KEY_BRIGHTNESSUP, - KEY_BRIGHTNESSDOWN, KEY_CHANNELUP, - KEY_CHANNELDOWN, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - } - }, - [SMS_IR_KB_HCW_SILVER] = { - .ir_protocol = IR_RC5, - .rc5_kbd_address = KEYBOARD_ADDRESS_LIGHTING1, - .keyboard_layout_map = { - KEY_0, KEY_1, KEY_2, - KEY_3, KEY_4, KEY_5, - KEY_6, KEY_7, KEY_8, - KEY_9, KEY_TEXT, KEY_RED, - KEY_RADIO, KEY_MENU, - KEY_SUBTITLE, - KEY_MUTE, KEY_VOLUMEUP, - KEY_VOLUMEDOWN, KEY_PREVIOUS, 0, - KEY_UP, KEY_DOWN, KEY_LEFT, - KEY_RIGHT, KEY_VIDEO, KEY_AUDIO, - KEY_MHP, KEY_EPG, KEY_TV, - 0, KEY_NEXTSONG, KEY_EXIT, - KEY_CHANNELUP, KEY_CHANNELDOWN, - KEY_CHANNEL, 0, - KEY_PREVIOUSSONG, KEY_ENTER, - KEY_SLEEP, 0, 0, KEY_BLUE, - 0, 0, 0, 0, KEY_GREEN, 0, - KEY_PAUSE, 0, KEY_REWIND, - 0, KEY_FASTFORWARD, KEY_PLAY, - KEY_STOP, KEY_RECORD, - KEY_YELLOW, 0, 0, KEY_SELECT, - KEY_ZOOM, KEY_POWER, 0, 0 - } - }, - { } /* Terminating entry */ -}; - -static u32 ir_pos; -static u32 ir_word; -static u32 ir_toggle; - -#define RC5_PUSH_BIT(dst, bit, pos) \ - { dst <<= 1; dst |= bit; pos++; } - - -static void sms_ir_rc5_event(struct smscore_device_t *coredev, - u32 toggle, u32 addr, u32 cmd) -{ - bool toggle_changed; - u16 keycode; - - sms_log("IR RC5 word: address %d, command %d, toggle %d", - addr, cmd, toggle); - - toggle_changed = ir_toggle != toggle; - /* keep toggle */ - ir_toggle = toggle; - - if (addr != - keyboard_layout_maps[coredev->ir.ir_kb_type].rc5_kbd_address) - return; /* Check for valid address */ - - keycode = - keyboard_layout_maps - [coredev->ir.ir_kb_type].keyboard_layout_map[cmd]; +#define MODULE_NAME "smsmdtv" - if (!toggle_changed && - (keycode != KEY_VOLUMEUP && keycode != KEY_VOLUMEDOWN)) - return; /* accept only repeated volume, reject other keys */ - - sms_log("kernel input keycode (from ir) %d", keycode); - input_report_key(coredev->ir.input_dev, keycode, 1); - input_sync(coredev->ir.input_dev); - -} - -/* decode raw bit pattern to RC5 code */ -/* taken from ir-functions.c */ -static u32 ir_rc5_decode(unsigned int code) +void sms_ir_event(struct smscore_device_t *coredev, const char *buf, int len) { -/* unsigned int org_code = code;*/ - unsigned int pair; - unsigned int rc5 = 0; int i; + const s32 *samples = (const void *)buf; - for (i = 0; i < 14; ++i) { - pair = code & 0x3; - code >>= 2; - - rc5 <<= 1; - switch (pair) { - case 0: - case 2: - break; - case 1: - rc5 |= 1; - break; - case 3: -/* dprintk(1, "ir-common: ir_rc5_decode(%x) bad code\n", org_code);*/ - sms_log("bad code"); - return 0; - } - } -/* - dprintk(1, "ir-common: code=%x, rc5=%x, start=%x, - toggle=%x, address=%x, " - "instr=%x\n", rc5, org_code, RC5_START(rc5), - RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5)); -*/ - return rc5; -} - -static void sms_rc5_parse_word(struct smscore_device_t *coredev) -{ - #define RC5_START(x) (((x)>>12)&3) - #define RC5_TOGGLE(x) (((x)>>11)&1) - #define RC5_ADDR(x) (((x)>>6)&0x1F) - #define RC5_INSTR(x) ((x)&0x3F) - - int i, j; - u32 rc5_word = 0; - - /* Reverse the IR word direction */ - for (i = 0 ; i < 28 ; i++) - RC5_PUSH_BIT(rc5_word, (ir_word>>i)&1, j) - - rc5_word = ir_rc5_decode(rc5_word); - /* sms_log("temp = 0x%x, rc5_code = 0x%x", ir_word, rc5_word); */ - - sms_ir_rc5_event(coredev, - RC5_TOGGLE(rc5_word), - RC5_ADDR(rc5_word), - RC5_INSTR(rc5_word)); -} - - -static void sms_rc5_accumulate_bits(struct smscore_device_t *coredev, - s32 ir_sample) -{ - #define RC5_TIME_GRANULARITY 200 - #define RC5_DEF_BIT_TIME 889 - #define RC5_MAX_SAME_BIT_CONT 4 - #define RC5_WORD_LEN 27 /* 28 bit */ - - u32 i, j; - s32 delta_time; - u32 time = (ir_sample > 0) ? ir_sample : (0-ir_sample); - u32 level = (ir_sample < 0) ? 0 : 1; - - for (i = RC5_MAX_SAME_BIT_CONT; i > 0; i--) { - delta_time = time - (i*RC5_DEF_BIT_TIME) + RC5_TIME_GRANULARITY; - if (delta_time < 0) - continue; /* not so many consecutive bits */ - if (delta_time > (2 * RC5_TIME_GRANULARITY)) { - /* timeout */ - if (ir_pos == (RC5_WORD_LEN-1)) - /* complete last bit */ - RC5_PUSH_BIT(ir_word, level, ir_pos) - - if (ir_pos == RC5_WORD_LEN) - sms_rc5_parse_word(coredev); - else if (ir_pos) /* timeout within a word */ - sms_log("IR error parsing a word"); + for (i = 0; i < len >> 2; i++) { + struct ir_raw_event ev; - ir_pos = 0; - ir_word = 0; - /* sms_log("timeout %d", time); */ - break; - } - /* The time is within the range of this number of bits */ - for (j = 0 ; j < i ; j++) - RC5_PUSH_BIT(ir_word, level, ir_pos) + ev.duration = abs(samples[i]) * 1000; /* Convert to ns */ + ev.pulse = (samples[i] > 0) ? false : true; - break; + ir_raw_event_store(coredev->ir.input_dev, &ev); } -} - -void sms_ir_event(struct smscore_device_t *coredev, const char *buf, int len) -{ - #define IR_DATA_RECEIVE_MAX_LEN 520 /* 128*4 + 4 + 4 */ - u32 i; - enum ir_protocol ir_protocol = - keyboard_layout_maps[coredev->ir.ir_kb_type] - .ir_protocol; - s32 *samples; - int count = len>>2; - - samples = (s32 *)buf; -/* sms_log("IR buffer received, length = %d", count);*/ - - for (i = 0; i < count; i++) - if (ir_protocol == IR_RC5) - sms_rc5_accumulate_bits(coredev, samples[i]); - /* IR_RCMM not implemented */ + ir_raw_event_handle(coredev->ir.input_dev); } int sms_ir_init(struct smscore_device_t *coredev) { struct input_dev *input_dev; + int board_id = smscore_get_board_id(coredev); sms_log("Allocating input device"); input_dev = input_allocate_device(); @@ -256,33 +63,38 @@ int sms_ir_init(struct smscore_device_t *coredev) } coredev->ir.input_dev = input_dev; - coredev->ir.ir_kb_type = - sms_get_board(smscore_get_board_id(coredev))->ir_kb_type; - coredev->ir.keyboard_layout_map = - keyboard_layout_maps[coredev->ir.ir_kb_type]. - keyboard_layout_map; - sms_log("IR remote keyboard type is %d", coredev->ir.ir_kb_type); coredev->ir.controller = 0; /* Todo: vega/nova SPI number */ coredev->ir.timeout = IR_DEFAULT_TIMEOUT; sms_log("IR port %d, timeout %d ms", coredev->ir.controller, coredev->ir.timeout); - snprintf(coredev->ir.name, - IR_DEV_NAME_MAX_LEN, - "SMS IR w/kbd type %d", - coredev->ir.ir_kb_type); + snprintf(coredev->ir.name, sizeof(coredev->ir.name), + "SMS IR (%s)", sms_get_board(board_id)->name); + + strlcpy(coredev->ir.phys, coredev->devpath, sizeof(coredev->ir.phys)); + strlcat(coredev->ir.phys, "/ir0", sizeof(coredev->ir.phys)); + input_dev->name = coredev->ir.name; - input_dev->phys = coredev->ir.name; + input_dev->phys = coredev->ir.phys; input_dev->dev.parent = coredev->device; - /* Key press events only */ - input_dev->evbit[0] = BIT_MASK(EV_KEY); - input_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0); +#if 0 + /* TODO: properly initialize the parameters bellow */ + input_dev->id.bustype = BUS_USB; + input_dev->id.version = 1; + input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); + input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct); +#endif + + coredev->ir.props.priv = coredev; + coredev->ir.props.driver_type = RC_DRIVER_IR_RAW; + coredev->ir.props.allowed_protos = IR_TYPE_ALL; sms_log("Input device (IR) %s is set for key events", input_dev->name); - if (input_register_device(input_dev)) { + if (ir_input_register(input_dev, sms_get_board(board_id)->rc_codes, + &coredev->ir.props, MODULE_NAME)) { sms_err("Failed to register device"); input_free_device(input_dev); return -EACCES; @@ -294,8 +106,7 @@ int sms_ir_init(struct smscore_device_t *coredev) void sms_ir_exit(struct smscore_device_t *coredev) { if (coredev->ir.input_dev) - input_unregister_device(coredev->ir.input_dev); + ir_input_unregister(coredev->ir.input_dev); sms_log(""); } - diff --git a/drivers/media/dvb/siano/smsir.h b/drivers/media/dvb/siano/smsir.h index b7d703e..926e247 100644 --- a/drivers/media/dvb/siano/smsir.h +++ b/drivers/media/dvb/siano/smsir.h @@ -4,6 +4,11 @@ Siano Mobile Silicon, Inc. MDTV receiver kernel modules. Copyright (C) 2006-2009, Uri Shkolnik + Copyright (c) 2010 - Mauro Carvalho Chehab + - Ported the driver to use rc-core + - IR raw event decoding is now done at rc-core + - Code almost re-written + 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 @@ -23,63 +28,21 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #define __SMS_IR_H__ #include <linux/input.h> +#include <media/ir-core.h> -#define IR_DEV_NAME_MAX_LEN 23 /* "SMS IR kbd type nn\0" */ -#define IR_KEYBOARD_LAYOUT_SIZE 64 #define IR_DEFAULT_TIMEOUT 100 -enum ir_kb_type { - SMS_IR_KB_DEFAULT_TV, - SMS_IR_KB_HCW_SILVER -}; - -enum rc5_keyboard_address { - KEYBOARD_ADDRESS_TV1 = 0, - KEYBOARD_ADDRESS_TV2 = 1, - KEYBOARD_ADDRESS_TELETEXT = 2, - KEYBOARD_ADDRESS_VIDEO = 3, - KEYBOARD_ADDRESS_LV1 = 4, - KEYBOARD_ADDRESS_VCR1 = 5, - KEYBOARD_ADDRESS_VCR2 = 6, - KEYBOARD_ADDRESS_EXPERIMENTAL = 7, - KEYBOARD_ADDRESS_SAT1 = 8, - KEYBOARD_ADDRESS_CAMERA = 9, - KEYBOARD_ADDRESS_SAT2 = 10, - KEYBOARD_ADDRESS_CDV = 12, - KEYBOARD_ADDRESS_CAMCORDER = 13, - KEYBOARD_ADDRESS_PRE_AMP = 16, - KEYBOARD_ADDRESS_TUNER = 17, - KEYBOARD_ADDRESS_RECORDER1 = 18, - KEYBOARD_ADDRESS_PRE_AMP1 = 19, - KEYBOARD_ADDRESS_CD_PLAYER = 20, - KEYBOARD_ADDRESS_PHONO = 21, - KEYBOARD_ADDRESS_SATA = 22, - KEYBOARD_ADDRESS_RECORDER2 = 23, - KEYBOARD_ADDRESS_CDR = 26, - KEYBOARD_ADDRESS_LIGHTING = 29, - KEYBOARD_ADDRESS_LIGHTING1 = 30, /* KEYBOARD_ADDRESS_HCW_SILVER */ - KEYBOARD_ADDRESS_PHONE = 31, - KEYBOARD_ADDRESS_NOT_RC5 = 0xFFFF -}; - -enum ir_protocol { - IR_RC5, - IR_RCMM -}; - -struct keyboard_layout_map_t { - enum ir_protocol ir_protocol; - enum rc5_keyboard_address rc5_kbd_address; - u16 keyboard_layout_map[IR_KEYBOARD_LAYOUT_SIZE]; -}; - struct smscore_device_t; struct ir_t { struct input_dev *input_dev; - enum ir_kb_type ir_kb_type; - char name[IR_DEV_NAME_MAX_LEN+1]; - u16 *keyboard_layout_map; + char name[40]; + char phys[32]; + + char *rc_codes; + u64 protocol; + struct ir_dev_props props; + u32 timeout; u32 controller; }; diff --git a/drivers/media/dvb/siano/smsusb.c b/drivers/media/dvb/siano/smsusb.c index a9c27fb..50d4338 100644 --- a/drivers/media/dvb/siano/smsusb.c +++ b/drivers/media/dvb/siano/smsusb.c @@ -352,8 +352,7 @@ static int smsusb_init_device(struct usb_interface *intf, int board_id) params.num_buffers = MAX_BUFFERS; params.sendrequest_handler = smsusb_sendrequest; params.context = dev; - snprintf(params.devpath, sizeof(params.devpath), - "usb\\%d-%s", dev->udev->bus->busnum, dev->udev->devpath); + usb_make_path(dev->udev, params.devpath, sizeof(params.devpath)); /* register in smscore */ rc = smscore_register_device(¶ms, &dev->coredev); diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c index 47075fc..9927a59 100644 --- a/drivers/media/radio/si470x/radio-si470x-common.c +++ b/drivers/media/radio/si470x/radio-si470x-common.c @@ -748,7 +748,7 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner) { struct si470x_device *radio = video_drvdata(file); - int retval = -EINVAL; + int retval = 0; /* safety checks */ retval = si470x_disconnect_check(radio); diff --git a/drivers/media/radio/si4713-i2c.c b/drivers/media/radio/si4713-i2c.c index ab63dd5..fc7f4b7 100644 --- a/drivers/media/radio/si4713-i2c.c +++ b/drivers/media/radio/si4713-i2c.c @@ -1009,8 +1009,10 @@ static int si4713_write_econtrol_string(struct si4713_device *sdev, goto exit; } rval = copy_from_user(ps_name, control->string, len); - if (rval < 0) + if (rval) { + rval = -EFAULT; goto exit; + } ps_name[len] = '\0'; if (strlen(ps_name) % vqc.step) { @@ -1031,8 +1033,10 @@ static int si4713_write_econtrol_string(struct si4713_device *sdev, goto exit; } rval = copy_from_user(radio_text, control->string, len); - if (rval < 0) + if (rval) { + rval = -EFAULT; goto exit; + } radio_text[len] = '\0'; if (strlen(radio_text) % vqc.step) { @@ -1367,6 +1371,8 @@ static int si4713_read_econtrol_string(struct si4713_device *sdev, } rval = copy_to_user(control->string, sdev->rds_info.ps_name, strlen(sdev->rds_info.ps_name) + 1); + if (rval) + rval = -EFAULT; break; case V4L2_CID_RDS_TX_RADIO_TEXT: @@ -1377,6 +1383,8 @@ static int si4713_read_econtrol_string(struct si4713_device *sdev, } rval = copy_to_user(control->string, sdev->rds_info.radio_text, strlen(sdev->rds_info.radio_text) + 1); + if (rval) + rval = -EFAULT; break; default: diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 27e2acc..2e15903 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -517,19 +517,6 @@ config VIDEO_UPD64083 endmenu # encoder / decoder chips -config DISPLAY_DAVINCI_DM646X_EVM - tristate "DM646x EVM Video Display" - depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM - select VIDEOBUF_DMA_CONTIG - select VIDEO_DAVINCI_VPIF - select VIDEO_ADV7343 - select VIDEO_THS7303 - help - Support for DM6467 based display device. - - To compile this driver as a module, choose M here: the - module will be called vpif_display. - config VIDEO_SH_VOU tristate "SuperH VOU video output driver" depends on VIDEO_DEV && ARCH_SHMOBILE @@ -537,29 +524,22 @@ config VIDEO_SH_VOU help Support for the Video Output Unit (VOU) on SuperH SoCs. -config CAPTURE_DAVINCI_DM646X_EVM - tristate "DM646x EVM Video Capture" - depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM +config VIDEO_VIU + tristate "Freescale VIU Video Driver" + depends on VIDEO_V4L2 && PPC_MPC512x select VIDEOBUF_DMA_CONTIG - select VIDEO_DAVINCI_VPIF - help - Support for DM6467 based capture device. - - To compile this driver as a module, choose M here: the - module will be called vpif_capture. - -config VIDEO_DAVINCI_VPIF - tristate "DaVinci VPIF Driver" - depends on DISPLAY_DAVINCI_DM646X_EVM - help - Support for DaVinci VPIF Driver. + default y + ---help--- + Support for Freescale VIU video driver. This device captures + video data, or overlays video on DIU frame buffer. - To compile this driver as a module, choose M here: the - module will be called vpif. + Say Y here if you want to enable VIU device on MPC5121e Rev2+. + In doubt, say N. config VIDEO_VIVI tristate "Virtual Video Driver" - depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64 && FONTS + depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64 + depends on (FRAMEBUFFER_CONSOLE || STI_CONSOLE) && FONTS select FONT_8x16 select VIDEOBUF_VMALLOC default n @@ -570,66 +550,7 @@ config VIDEO_VIVI Say Y here if you want to test video apps or debug V4L devices. In doubt, say N. -config VIDEO_VPSS_SYSTEM - tristate "VPSS System module driver" - depends on ARCH_DAVINCI - help - Support for vpss system module for video driver - -config VIDEO_VPFE_CAPTURE - tristate "VPFE Video Capture Driver" - depends on VIDEO_V4L2 && ARCH_DAVINCI - select VIDEOBUF_DMA_CONTIG - help - Support for DMXXXX VPFE based frame grabber. This is the - common V4L2 module for following DMXXX SoCs from Texas - Instruments:- DM6446 & DM355. - - To compile this driver as a module, choose M here: the - module will be called vpfe-capture. - -config VIDEO_DM6446_CCDC - tristate "DM6446 CCDC HW module" - depends on ARCH_DAVINCI_DM644x && VIDEO_VPFE_CAPTURE - select VIDEO_VPSS_SYSTEM - default y - help - Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces - with decoder modules such as TVP5146 over BT656 or - sensor module such as MT9T001 over a raw interface. This - module configures the interface and CCDC/ISIF to do - video frame capture from slave decoders. - - To compile this driver as a module, choose M here: the - module will be called vpfe. - -config VIDEO_DM355_CCDC - tristate "DM355 CCDC HW module" - depends on ARCH_DAVINCI_DM355 && VIDEO_VPFE_CAPTURE - select VIDEO_VPSS_SYSTEM - default y - help - Enables DM355 CCD hw module. DM355 CCDC hw interfaces - with decoder modules such as TVP5146 over BT656 or - sensor module such as MT9T001 over a raw interface. This - module configures the interface and CCDC/ISIF to do - video frame capture from a slave decoders - - To compile this driver as a module, choose M here: the - module will be called vpfe. - -config VIDEO_ISIF - tristate "ISIF HW module" - depends on ARCH_DAVINCI_DM365 && VIDEO_VPFE_CAPTURE - select VIDEO_VPSS_SYSTEM - default y - help - Enables ISIF hw module. This is the hardware module for - configuring ISIF in VPFE to capture Raw Bayer RGB data from - a image sensor or YUV data from a YUV source. - - To compile this driver as a module, choose M here: the - module will be called vpfe. +source "drivers/media/video/davinci/Kconfig" source "drivers/media/video/omap/Kconfig" @@ -955,6 +876,12 @@ config VIDEO_PXA27x ---help--- This is a v4l2 driver for the PXA27x Quick Capture Interface +config VIDEO_SH_MOBILE_CSI2 + tristate "SuperH Mobile MIPI CSI-2 Interface driver" + depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK + ---help--- + This is a v4l2 driver for the SuperH MIPI CSI-2 Interface + config VIDEO_SH_MOBILE_CEU tristate "SuperH Mobile CEU Interface driver" depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK @@ -1013,61 +940,6 @@ source "drivers/media/video/usbvideo/Kconfig" source "drivers/media/video/et61x251/Kconfig" -config VIDEO_OVCAMCHIP - tristate "OmniVision Camera Chip support (DEPRECATED)" - depends on I2C && VIDEO_V4L1 - default n - ---help--- - This driver is DEPRECATED please use the gspca ov519 module - instead. Note that for the ov511 / ov518 support of the gspca module - you need atleast version 0.6.0 of libv4l and for the w9968cf - atleast version 0.6.3 of libv4l. - - Support for the OmniVision OV6xxx and OV7xxx series of camera chips. - This driver is intended to be used with the ov511 and w9968cf USB - camera drivers. - - To compile this driver as a module, choose M here: the - module will be called ovcamchip. - -config USB_W9968CF - tristate "USB W996[87]CF JPEG Dual Mode Camera support (DEPRECATED)" - depends on VIDEO_V4L1 && I2C && VIDEO_OVCAMCHIP - default n - ---help--- - This driver is DEPRECATED please use the gspca ov519 module - instead. Note that for the w9968cf support of the gspca module - you need atleast version 0.6.3 of libv4l. - - Say Y here if you want support for cameras based on OV681 or - Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips. - - This driver has an optional plugin, which is distributed as a - separate module only (released under GPL). It allows to use higher - resolutions and framerates, but cannot be included in the official - Linux kernel for performance purposes. - - See <file:Documentation/video4linux/w9968cf.txt> for more info. - - To compile this driver as a module, choose M here: the - module will be called w9968cf. - -config USB_OV511 - tristate "USB OV511 Camera support (DEPRECATED)" - depends on VIDEO_V4L1 - default n - ---help--- - This driver is DEPRECATED please use the gspca ov519 module - instead. Note that for the ov511 / ov518 support of the gspca module - you need atleast version 0.6.0 of libv4l. - - Say Y here if you want to connect this type of camera to your - computer's USB port. See <file:Documentation/video4linux/ov511.txt> - for more information and for a list of supported cameras. - - To compile this driver as a module, choose M here: the - module will be called ov511. - config USB_SE401 tristate "USB SE401 Camera support" depends on VIDEO_V4L1 @@ -1081,25 +953,6 @@ config USB_SE401 source "drivers/media/video/sn9c102/Kconfig" -config USB_STV680 - tristate "USB STV680 (Pencam) Camera support (DEPRECATED)" - depends on VIDEO_V4L1 - default n - ---help--- - This driver is DEPRECATED please use the gspca stv0680 module - instead. Note that for the gspca stv0680 module you need - atleast version 0.6.3 of libv4l. - - Say Y here if you want to connect this type of camera to your - computer's USB port. This includes the Pencam line of cameras. - See <file:Documentation/video4linux/stv680.txt> for more information - and for a list of supported cameras. - - To compile this driver as a module, choose M here: the - module will be called stv680. - -source "drivers/media/video/zc0301/Kconfig" - source "drivers/media/video/pwc/Kconfig" config USB_ZR364XX diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index b08bd2b..1051ecc6 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -105,7 +105,6 @@ obj-$(CONFIG_VIDEO_TLG2300) += tlg2300/ obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/ obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/ -obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip/ obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ obj-$(CONFIG_VIDEO_MXB) += mxb.o obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o @@ -127,17 +126,13 @@ obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o obj-$(CONFIG_USB_DABUSB) += dabusb.o -obj-$(CONFIG_USB_OV511) += ov511.o obj-$(CONFIG_USB_SE401) += se401.o -obj-$(CONFIG_USB_STV680) += stv680.o -obj-$(CONFIG_USB_W9968CF) += w9968cf.o obj-$(CONFIG_USB_ZR364XX) += zr364xx.o obj-$(CONFIG_USB_STKWEBCAM) += stkwebcam.o obj-$(CONFIG_USB_SN9C102) += sn9c102/ obj-$(CONFIG_USB_ET61X251) += et61x251/ obj-$(CONFIG_USB_PWC) += pwc/ -obj-$(CONFIG_USB_ZC0301) += zc0301/ obj-$(CONFIG_USB_GSPCA) += gspca/ obj-$(CONFIG_VIDEO_HDPVR) += hdpvr/ @@ -151,6 +146,7 @@ obj-$(CONFIG_USB_S2255) += s2255drv.o obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_CX18) += cx18/ +obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o obj-$(CONFIG_VIDEO_VIVI) += vivi.o obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o obj-$(CONFIG_VIDEO_CX23885) += cx23885/ @@ -165,6 +161,7 @@ obj-$(CONFIG_VIDEO_MX1) += mx1_camera.o obj-$(CONFIG_VIDEO_MX2) += mx2_camera.o obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o +obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o obj-$(CONFIG_ARCH_DAVINCI) += davinci/ @@ -178,7 +175,7 @@ obj-$(CONFIG_VIDEO_SAA7164) += saa7164/ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o -obj-$(CONFIG_ARCH_DAVINCI) += davinci/ +obj-y += davinci/ obj-$(CONFIG_ARCH_OMAP) += omap/ diff --git a/drivers/media/video/ak881x.c b/drivers/media/video/ak881x.c index 1573392..b388654 100644 --- a/drivers/media/video/ak881x.c +++ b/drivers/media/video/ak881x.c @@ -126,7 +126,7 @@ static int ak881x_try_g_mbus_fmt(struct v4l2_subdev *sd, v4l_bound_align_image(&mf->width, 0, 720, 2, &mf->height, 0, ak881x->lines, 1, 0); mf->field = V4L2_FIELD_INTERLACED; - mf->code = V4L2_MBUS_FMT_YUYV8_2X8_LE; + mf->code = V4L2_MBUS_FMT_YUYV8_2X8; mf->colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; @@ -136,7 +136,7 @@ static int ak881x_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) { if (mf->field != V4L2_FIELD_INTERLACED || - mf->code != V4L2_MBUS_FMT_YUYV8_2X8_LE) + mf->code != V4L2_MBUS_FMT_YUYV8_2X8) return -EINVAL; return ak881x_try_g_mbus_fmt(sd, mf); @@ -148,7 +148,7 @@ static int ak881x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, if (index) return -EINVAL; - *code = V4L2_MBUS_FMT_YUYV8_2X8_LE; + *code = V4L2_MBUS_FMT_YUYV8_2X8; return 0; } diff --git a/drivers/media/video/au0828/Makefile b/drivers/media/video/au0828/Makefile index 4d26231..5c7f2f7 100644 --- a/drivers/media/video/au0828/Makefile +++ b/drivers/media/video/au0828/Makefile @@ -1,4 +1,4 @@ -au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o au0828-video.o +au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o au0828-video.o au0828-vbi.o obj-$(CONFIG_VIDEO_AU0828) += au0828.o diff --git a/drivers/media/video/au0828/au0828-vbi.c b/drivers/media/video/au0828/au0828-vbi.c new file mode 100644 index 0000000..63f5930 --- /dev/null +++ b/drivers/media/video/au0828/au0828-vbi.c @@ -0,0 +1,138 @@ +/* + au0828-vbi.c - VBI driver for au0828 + + Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com> + + This work was sponsored by GetWellNetwork Inc. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> + +#include "au0828.h" + +static unsigned int vbibufs = 5; +module_param(vbibufs, int, 0644); +MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32"); + +/* ------------------------------------------------------------------ */ + +static void +free_buffer(struct videobuf_queue *vq, struct au0828_buffer *buf) +{ + struct au0828_fh *fh = vq->priv_data; + struct au0828_dev *dev = fh->dev; + unsigned long flags = 0; + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->slock, flags); + if (dev->isoc_ctl.vbi_buf == buf) + dev->isoc_ctl.vbi_buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) +{ + struct au0828_fh *fh = q->priv_data; + struct au0828_dev *dev = fh->dev; + + *size = dev->vbi_width * dev->vbi_height * 2; + + if (0 == *count) + *count = vbibufs; + if (*count < 2) + *count = 2; + if (*count > 32) + *count = 32; + return 0; +} + +static int +vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct au0828_fh *fh = q->priv_data; + struct au0828_dev *dev = fh->dev; + struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb); + int rc = 0; + + buf->vb.size = dev->vbi_width * dev->vbi_height * 2; + + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + buf->vb.width = dev->vbi_width; + buf->vb.height = dev->vbi_height; + buf->vb.field = field; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(q, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(q, buf); + return rc; +} + +static void +vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct au0828_buffer *buf = container_of(vb, + struct au0828_buffer, + vb); + struct au0828_fh *fh = vq->priv_data; + struct au0828_dev *dev = fh->dev; + struct au0828_dmaqueue *vbiq = &dev->vbiq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vbiq->active); +} + +static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb); + free_buffer(q, buf); +} + +struct videobuf_queue_ops au0828_vbi_qops = { + .buf_setup = vbi_setup, + .buf_prepare = vbi_prepare, + .buf_queue = vbi_queue, + .buf_release = vbi_release, +}; diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c index 52f25aa..7989a7b 100644 --- a/drivers/media/video/au0828/au0828-video.c +++ b/drivers/media/video/au0828/au0828-video.c @@ -314,6 +314,23 @@ static inline void buffer_filled(struct au0828_dev *dev, wake_up(&buf->vb.done); } +static inline void vbi_buffer_filled(struct au0828_dev *dev, + struct au0828_dmaqueue *dma_q, + struct au0828_buffer *buf) +{ + /* Advice that buffer was filled */ + au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); + + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + dev->isoc_ctl.vbi_buf = NULL; + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + /* * Identify the buffer header type and properly handles */ @@ -327,6 +344,9 @@ static void au0828_copy_video(struct au0828_dev *dev, int linesdone, currlinedone, offset, lencopy, remain; int bytesperline = dev->width << 1; /* Assumes 16-bit depth @@@@ */ + if (len == 0) + return; + if (dma_q->pos + len > buf->vb.size) len = buf->vb.size - dma_q->pos; @@ -414,17 +434,98 @@ static inline void get_next_buf(struct au0828_dmaqueue *dma_q, return; } +static void au0828_copy_vbi(struct au0828_dev *dev, + struct au0828_dmaqueue *dma_q, + struct au0828_buffer *buf, + unsigned char *p, + unsigned char *outp, unsigned long len) +{ + unsigned char *startwrite, *startread; + int bytesperline; + int i, j = 0; + + if (dev == NULL) { + au0828_isocdbg("dev is null\n"); + return; + } + + if (dma_q == NULL) { + au0828_isocdbg("dma_q is null\n"); + return; + } + if (buf == NULL) + return; + if (p == NULL) { + au0828_isocdbg("p is null\n"); + return; + } + if (outp == NULL) { + au0828_isocdbg("outp is null\n"); + return; + } + + bytesperline = dev->vbi_width; + + if (dma_q->pos + len > buf->vb.size) + len = buf->vb.size - dma_q->pos; + + startread = p; + startwrite = outp + (dma_q->pos / 2); + + /* Make sure the bottom field populates the second half of the frame */ + if (buf->top_field == 0) + startwrite += bytesperline * dev->vbi_height; + + for (i = 0; i < len; i += 2) + startwrite[j++] = startread[i+1]; + + dma_q->pos += len; +} + + +/* + * video-buf generic routine to get the next available VBI buffer + */ +static inline void vbi_get_next_buf(struct au0828_dmaqueue *dma_q, + struct au0828_buffer **buf) +{ + struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vbiq); + char *outp; + + if (list_empty(&dma_q->active)) { + au0828_isocdbg("No active queue to serve\n"); + dev->isoc_ctl.vbi_buf = NULL; + *buf = NULL; + return; + } + + /* Get the next buffer */ + *buf = list_entry(dma_q->active.next, struct au0828_buffer, vb.queue); + /* Cleans up buffer - Usefull for testing for frame/URB loss */ + outp = videobuf_to_vmalloc(&(*buf)->vb); + memset(outp, 0x00, (*buf)->vb.size); + + dev->isoc_ctl.vbi_buf = *buf; + + return; +} + /* * Controls the isoc copy of each urb packet */ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) { struct au0828_buffer *buf; + struct au0828_buffer *vbi_buf; struct au0828_dmaqueue *dma_q = urb->context; + struct au0828_dmaqueue *vbi_dma_q = &dev->vbiq; unsigned char *outp = NULL; + unsigned char *vbioutp = NULL; int i, len = 0, rc = 1; unsigned char *p; unsigned char fbyte; + unsigned int vbi_field_size; + unsigned int remain, lencopy; if (!dev) return 0; @@ -443,6 +544,10 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) if (buf != NULL) outp = videobuf_to_vmalloc(&buf->vb); + vbi_buf = dev->isoc_ctl.vbi_buf; + if (vbi_buf != NULL) + vbioutp = videobuf_to_vmalloc(&vbi_buf->vb); + for (i = 0; i < urb->number_of_packets; i++) { int status = urb->iso_frame_desc[i].status; @@ -472,6 +577,19 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) au0828_isocdbg("Video frame %s\n", (fbyte & 0x40) ? "odd" : "even"); if (!(fbyte & 0x40)) { + /* VBI */ + if (vbi_buf != NULL) + vbi_buffer_filled(dev, + vbi_dma_q, + vbi_buf); + vbi_get_next_buf(vbi_dma_q, &vbi_buf); + if (vbi_buf == NULL) + vbioutp = NULL; + else + vbioutp = videobuf_to_vmalloc( + &vbi_buf->vb); + + /* Video */ if (buf != NULL) buffer_filled(dev, dma_q, buf); get_next_buf(dma_q, &buf); @@ -488,9 +606,36 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) buf->top_field = 0; } + if (vbi_buf != NULL) { + if (fbyte & 0x40) + vbi_buf->top_field = 1; + else + vbi_buf->top_field = 0; + } + + dev->vbi_read = 0; + vbi_dma_q->pos = 0; dma_q->pos = 0; } - if (buf != NULL) + + vbi_field_size = dev->vbi_width * dev->vbi_height * 2; + if (dev->vbi_read < vbi_field_size) { + remain = vbi_field_size - dev->vbi_read; + if (len < remain) + lencopy = len; + else + lencopy = remain; + + if (vbi_buf != NULL) + au0828_copy_vbi(dev, vbi_dma_q, vbi_buf, p, + vbioutp, len); + + len -= lencopy; + p += lencopy; + dev->vbi_read += lencopy; + } + + if (dev->vbi_read >= vbi_field_size && buf != NULL) au0828_copy_video(dev, dma_q, buf, p, outp, len); } return rc; @@ -642,7 +787,7 @@ int au0828_analog_stream_enable(struct au0828_dev *d) au0828_writereg(d, 0x114, 0xa0); au0828_writereg(d, 0x115, 0x05); /* set y position */ - au0828_writereg(d, 0x112, 0x02); + au0828_writereg(d, 0x112, 0x00); au0828_writereg(d, 0x113, 0x00); au0828_writereg(d, 0x116, 0xf2); au0828_writereg(d, 0x117, 0x00); @@ -703,47 +848,83 @@ void au0828_analog_unregister(struct au0828_dev *dev) /* Usage lock check functions */ -static int res_get(struct au0828_fh *fh) +static int res_get(struct au0828_fh *fh, unsigned int bit) { - struct au0828_dev *dev = fh->dev; - int rc = 0; + struct au0828_dev *dev = fh->dev; - /* This instance already has stream_on */ - if (fh->stream_on) - return rc; + if (fh->resources & bit) + /* have it already allocated */ + return 1; - if (dev->stream_on) - return -EBUSY; + /* is it free? */ + mutex_lock(&dev->lock); + if (dev->resources & bit) { + /* no, someone else uses it */ + mutex_unlock(&dev->lock); + return 0; + } + /* it's free, grab it */ + fh->resources |= bit; + dev->resources |= bit; + dprintk(1, "res: get %d\n", bit); + mutex_unlock(&dev->lock); + return 1; +} - dev->stream_on = 1; - fh->stream_on = 1; - return rc; +static int res_check(struct au0828_fh *fh, unsigned int bit) +{ + return fh->resources & bit; } -static int res_check(struct au0828_fh *fh) +static int res_locked(struct au0828_dev *dev, unsigned int bit) { - return fh->stream_on; + return dev->resources & bit; } -static void res_free(struct au0828_fh *fh) +static void res_free(struct au0828_fh *fh, unsigned int bits) { - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = fh->dev; - fh->stream_on = 0; - dev->stream_on = 0; + BUG_ON((fh->resources & bits) != bits); + + mutex_lock(&dev->lock); + fh->resources &= ~bits; + dev->resources &= ~bits; + dprintk(1, "res: put %d\n", bits); + mutex_unlock(&dev->lock); +} + +static int get_ressource(struct au0828_fh *fh) +{ + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return AU0828_RESOURCE_VIDEO; + case V4L2_BUF_TYPE_VBI_CAPTURE: + return AU0828_RESOURCE_VBI; + default: + BUG(); + return 0; + } } static int au0828_v4l2_open(struct file *filp) { int ret = 0; + struct video_device *vdev = video_devdata(filp); struct au0828_dev *dev = video_drvdata(filp); struct au0828_fh *fh; - int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + int type; -#ifdef VBI_IS_WORKING - if (video_devdata(filp)->vfl_type == VFL_TYPE_GRABBER) + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + break; + case VFL_TYPE_VBI: type = V4L2_BUF_TYPE_VBI_CAPTURE; -#endif + break; + default: + return -EINVAL; + } fh = kzalloc(sizeof(struct au0828_fh), GFP_KERNEL); if (NULL == fh) { @@ -781,10 +962,21 @@ static int au0828_v4l2_open(struct file *filp) dev->users++; videobuf_queue_vmalloc_init(&fh->vb_vidq, &au0828_video_qops, - NULL, &dev->slock, fh->type, + NULL, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED, sizeof(struct au0828_buffer), fh); + /* VBI Setup */ + dev->vbi_width = 720; + dev->vbi_height = 1; + videobuf_queue_vmalloc_init(&fh->vb_vbiq, &au0828_vbi_qops, + NULL, &dev->slock, + V4L2_BUF_TYPE_VBI_CAPTURE, + V4L2_FIELD_SEQ_TB, + sizeof(struct au0828_buffer), fh); + + return ret; } @@ -794,17 +986,19 @@ static int au0828_v4l2_close(struct file *filp) struct au0828_fh *fh = filp->private_data; struct au0828_dev *dev = fh->dev; - mutex_lock(&dev->lock); - if (res_check(fh)) - res_free(fh); - - if (dev->users == 1) { + if (res_check(fh, AU0828_RESOURCE_VIDEO)) { videobuf_stop(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vidq); + res_free(fh, AU0828_RESOURCE_VIDEO); + } + + if (res_check(fh, AU0828_RESOURCE_VBI)) { + videobuf_stop(&fh->vb_vbiq); + res_free(fh, AU0828_RESOURCE_VBI); + } + if (dev->users == 1) { if (dev->dev_state & DEV_DISCONNECTED) { au0828_analog_unregister(dev); - mutex_unlock(&dev->lock); kfree(dev); return 0; } @@ -823,10 +1017,11 @@ static int au0828_v4l2_close(struct file *filp) printk(KERN_INFO "Au0828 can't set alternate to 0!\n"); } + videobuf_mmap_free(&fh->vb_vidq); + videobuf_mmap_free(&fh->vb_vbiq); kfree(fh); dev->users--; wake_up_interruptible_nr(&dev->open, 1); - mutex_unlock(&dev->lock); return 0; } @@ -842,16 +1037,21 @@ static ssize_t au0828_v4l2_read(struct file *filp, char __user *buf, return rc; if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - mutex_lock(&dev->lock); - rc = res_get(fh); - mutex_unlock(&dev->lock); - - if (unlikely(rc < 0)) - return rc; + if (res_locked(dev, AU0828_RESOURCE_VIDEO)) + return -EBUSY; return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, filp->f_flags & O_NONBLOCK); } + + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (!res_get(fh, AU0828_RESOURCE_VBI)) + return -EBUSY; + + return videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0, + filp->f_flags & O_NONBLOCK); + } + return 0; } @@ -865,17 +1065,17 @@ static unsigned int au0828_v4l2_poll(struct file *filp, poll_table *wait) if (rc < 0) return rc; - mutex_lock(&dev->lock); - rc = res_get(fh); - mutex_unlock(&dev->lock); - - if (unlikely(rc < 0)) - return POLLERR; - - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (!res_get(fh, AU0828_RESOURCE_VIDEO)) + return POLLERR; + return videobuf_poll_stream(filp, &fh->vb_vidq, wait); + } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (!res_get(fh, AU0828_RESOURCE_VBI)) + return POLLERR; + return videobuf_poll_stream(filp, &fh->vb_vbiq, wait); + } else { return POLLERR; - - return videobuf_poll_stream(filp, &fh->vb_vidq, wait); + } } static int au0828_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) @@ -888,14 +1088,10 @@ static int au0828_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) if (rc < 0) return rc; - mutex_lock(&dev->lock); - rc = res_get(fh); - mutex_unlock(&dev->lock); - - if (unlikely(rc < 0)) - return rc; - - rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); + else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma); return rc; } @@ -911,14 +1107,6 @@ static int au0828_set_format(struct au0828_dev *dev, unsigned int cmd, maxwidth = 720; maxheight = 480; -#ifdef VBI_IS_WORKING - if (format->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { - dprintk(1, "VBI format set: to be supported!\n"); - return 0; - } - if (format->type == V4L2_BUF_TYPE_VBI_CAPTURE) - return 0; -#endif if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -999,9 +1187,7 @@ static int vidioc_querycap(struct file *file, void *priv, /*set the device capabilities */ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | -#ifdef VBI_IS_WORKING V4L2_CAP_VBI_CAPTURE | -#endif V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | @@ -1056,20 +1242,21 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct au0828_dev *dev = fh->dev; int rc; + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + if (videobuf_queue_is_busy(&fh->vb_vidq)) { printk(KERN_INFO "%s queue busy\n", __func__); rc = -EBUSY; goto out; } - if (dev->stream_on && !fh->stream_on) { - printk(KERN_INFO "%s device in use by another fh\n", __func__); - rc = -EBUSY; - goto out; - } - - return au0828_set_format(dev, VIDIOC_S_FMT, f); + rc = au0828_set_format(dev, VIDIOC_S_FMT, f); out: + mutex_unlock(&dev->lock); return rc; } @@ -1300,6 +1487,29 @@ static int vidioc_s_frequency(struct file *file, void *priv, return 0; } + +/* RAW VBI ioctls */ + +static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *format) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + format->fmt.vbi.samples_per_line = dev->vbi_width; + format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + format->fmt.vbi.offset = 0; + format->fmt.vbi.flags = 0; + format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; + + format->fmt.vbi.count[0] = dev->vbi_height; + format->fmt.vbi.count[1] = dev->vbi_height; + format->fmt.vbi.start[0] = 21; + format->fmt.vbi.start[1] = 284; + + return 0; +} + static int vidioc_g_chip_ident(struct file *file, void *priv, struct v4l2_dbg_chip_ident *chip) { @@ -1345,25 +1555,32 @@ static int vidioc_cropcap(struct file *file, void *priv, static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - int rc; + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc = -EINVAL; rc = check_dev(dev); if (rc < 0) return rc; + if (unlikely(type != fh->type)) + return -EINVAL; + + dprintk(1, "vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n", + fh, type, fh->resources, dev->resources); + + if (unlikely(!res_get(fh, get_ressource(fh)))) + return -EBUSY; + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { au0828_analog_stream_enable(dev); v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1); } - mutex_lock(&dev->lock); - rc = res_get(fh); - - if (likely(rc >= 0)) + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) rc = videobuf_streamon(&fh->vb_vidq); - mutex_unlock(&dev->lock); + else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + rc = videobuf_streamon(&fh->vb_vbiq); return rc; } @@ -1371,38 +1588,42 @@ static int vidioc_streamon(struct file *file, void *priv, static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - int i; - int ret; - int rc; + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc; + int i; rc = check_dev(dev); if (rc < 0) return rc; - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) return -EINVAL; if (type != fh->type) return -EINVAL; - if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dprintk(1, "vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n", + fh, type, fh->resources, dev->resources); + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); - ret = au0828_stream_interrupt(dev); - if (ret != 0) - return ret; - } + rc = au0828_stream_interrupt(dev); + if (rc != 0) + return rc; - for (i = 0; i < AU0828_MAX_INPUT; i++) { - if (AUVI_INPUT(i).audio_setup == NULL) - continue; - (AUVI_INPUT(i).audio_setup)(dev, 0); - } + for (i = 0; i < AU0828_MAX_INPUT; i++) { + if (AUVI_INPUT(i).audio_setup == NULL) + continue; + (AUVI_INPUT(i).audio_setup)(dev, 0); + } - mutex_lock(&dev->lock); - videobuf_streamoff(&fh->vb_vidq); - res_free(fh); - mutex_unlock(&dev->lock); + videobuf_streamoff(&fh->vb_vidq); + res_free(fh, AU0828_RESOURCE_VIDEO); + } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + videobuf_streamoff(&fh->vb_vbiq); + res_free(fh, AU0828_RESOURCE_VBI); + } return 0; } @@ -1527,19 +1748,11 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, -#ifdef VBI_IS_WORKING .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, - .vidioc_try_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, - .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, -#endif + .vidioc_s_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, .vidioc_g_audio = vidioc_g_audio, .vidioc_s_audio = vidioc_s_audio, .vidioc_cropcap = vidioc_cropcap, -#ifdef VBI_IS_WORKING - .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap, - .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, - .vidioc_s_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, -#endif .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, @@ -1621,8 +1834,11 @@ int au0828_analog_register(struct au0828_dev *dev, spin_lock_init(&dev->slock); mutex_init(&dev->lock); + /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); INIT_LIST_HEAD(&dev->vidq.queued); + INIT_LIST_HEAD(&dev->vbiq.active); + INIT_LIST_HEAD(&dev->vbiq.queued); dev->width = NTSC_STD_W; dev->height = NTSC_STD_H; @@ -1638,26 +1854,23 @@ int au0828_analog_register(struct au0828_dev *dev, return -ENOMEM; } -#ifdef VBI_IS_WORKING + /* allocate the VBI struct */ dev->vbi_dev = video_device_alloc(); if (NULL == dev->vbi_dev) { dprintk(1, "Can't allocate vbi_device.\n"); kfree(dev->vdev); return -ENOMEM; } -#endif /* Fill the video capture device struct */ *dev->vdev = au0828_video_template; dev->vdev->parent = &dev->usbdev->dev; strcpy(dev->vdev->name, "au0828a video"); -#ifdef VBI_IS_WORKING /* Setup the VBI device */ *dev->vbi_dev = au0828_video_template; dev->vbi_dev->parent = &dev->usbdev->dev; strcpy(dev->vbi_dev->name, "au0828a vbi"); -#endif /* Register the v4l2 device */ video_set_drvdata(dev->vdev, dev); @@ -1669,7 +1882,6 @@ int au0828_analog_register(struct au0828_dev *dev, return -ENODEV; } -#ifdef VBI_IS_WORKING /* Register the vbi device */ video_set_drvdata(dev->vbi_dev, dev); retval = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, -1); @@ -1680,7 +1892,6 @@ int au0828_analog_register(struct au0828_dev *dev, video_device_release(dev->vdev); return -ENODEV; } -#endif dprintk(1, "%s completed!\n", __func__); diff --git a/drivers/media/video/au0828/au0828.h b/drivers/media/video/au0828/au0828.h index 207f32d..9905bc4 100644 --- a/drivers/media/video/au0828/au0828.h +++ b/drivers/media/video/au0828/au0828.h @@ -60,6 +60,10 @@ #define AU0828_MAX_INPUT 4 +/* au0828 resource types (used for res_get/res_lock etc */ +#define AU0828_RESOURCE_VIDEO 0x01 +#define AU0828_RESOURCE_VBI 0x02 + enum au0828_itype { AU0828_VMUX_UNDEFINED = 0, AU0828_VMUX_COMPOSITE, @@ -115,8 +119,10 @@ enum au0828_dev_state { struct au0828_fh { struct au0828_dev *dev; - unsigned int stream_on:1; /* Locks streams */ + unsigned int resources; + struct videobuf_queue vb_vidq; + struct videobuf_queue vb_vbiq; enum v4l2_buf_type type; }; @@ -145,7 +151,8 @@ struct au0828_usb_isoc_ctl { int tmp_buf_len; /* Stores already requested buffers */ - struct au0828_buffer *buf; + struct au0828_buffer *buf; + struct au0828_buffer *vbi_buf; /* Stores the number of received fields */ int nfields; @@ -194,11 +201,14 @@ struct au0828_dev { /* Analog */ struct v4l2_device v4l2_dev; int users; - unsigned int stream_on:1; /* Locks streams */ + unsigned int resources; /* resources in use */ struct video_device *vdev; struct video_device *vbi_dev; int width; int height; + int vbi_width; + int vbi_height; + u32 vbi_read; u32 field_size; u32 frame_size; u32 bytesperline; @@ -219,6 +229,7 @@ struct au0828_dev { /* Isoc control struct */ struct au0828_dmaqueue vidq; + struct au0828_dmaqueue vbiq; struct au0828_usb_isoc_ctl isoc_ctl; spinlock_t slock; @@ -278,6 +289,9 @@ void au0828_analog_unregister(struct au0828_dev *dev); extern int au0828_dvb_register(struct au0828_dev *dev); extern void au0828_dvb_unregister(struct au0828_dev *dev); +/* au0828-vbi.c */ +extern struct videobuf_queue_ops au0828_vbi_qops; + #define dprintk(level, fmt, arg...)\ do { if (au0828_debug & level)\ printk(KERN_DEBUG DRIVER_NAME "/0: " fmt, ## arg);\ diff --git a/drivers/media/video/bt8xx/bttv-risc.c b/drivers/media/video/bt8xx/bttv-risc.c index c24b1c1..0fa9f39 100644 --- a/drivers/media/video/bt8xx/bttv-risc.c +++ b/drivers/media/video/bt8xx/bttv-risc.c @@ -583,7 +583,7 @@ bttv_dma_free(struct videobuf_queue *q,struct bttv *btv, struct bttv_buffer *buf BUG_ON(in_interrupt()); videobuf_waiton(&buf->vb,0,0); - videobuf_dma_unmap(q, dma); + videobuf_dma_unmap(q->dev, dma); videobuf_dma_free(dma); btcx_riscmem_free(btv->c.pci,&buf->bottom); btcx_riscmem_free(btv->c.pci,&buf->top); diff --git a/drivers/media/video/cpia_usb.c b/drivers/media/video/cpia_usb.c index ef1f893..58d193f 100644 --- a/drivers/media/video/cpia_usb.c +++ b/drivers/media/video/cpia_usb.c @@ -584,7 +584,6 @@ 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) @@ -606,8 +605,6 @@ static void cpia_disconnect(struct usb_interface *intf) 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]); diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index 20eaf38..d679240 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -1081,7 +1081,7 @@ long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct video_device *vfd = video_devdata(filp); - struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data; + struct cx18_open_id *id = filp->private_data; struct cx18 *cx = id->cx; long res; diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index d639186..2014dae 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -408,10 +408,18 @@ struct cx23885_subid cx23885_subids[] = { .card = CX23885_BOARD_HAUPPAUGE_HVR1275, }, { .subvendor = 0x0070, + .subdevice = 0x221d, + .card = CX23885_BOARD_HAUPPAUGE_HVR1275, + }, { + .subvendor = 0x0070, .subdevice = 0x2251, .card = CX23885_BOARD_HAUPPAUGE_HVR1255, }, { .subvendor = 0x0070, + .subdevice = 0x2259, + .card = CX23885_BOARD_HAUPPAUGE_HVR1255, + }, { + .subvendor = 0x0070, .subdevice = 0x2291, .card = CX23885_BOARD_HAUPPAUGE_HVR1210, }, { @@ -419,6 +427,38 @@ struct cx23885_subid cx23885_subids[] = { .subdevice = 0x2295, .card = CX23885_BOARD_HAUPPAUGE_HVR1210, }, { + .subvendor = 0x0070, + .subdevice = 0x2299, + .card = CX23885_BOARD_HAUPPAUGE_HVR1210, + }, { + .subvendor = 0x0070, + .subdevice = 0x229d, + .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */ + }, { + .subvendor = 0x0070, + .subdevice = 0x22f0, + .card = CX23885_BOARD_HAUPPAUGE_HVR1210, + }, { + .subvendor = 0x0070, + .subdevice = 0x22f1, + .card = CX23885_BOARD_HAUPPAUGE_HVR1255, + }, { + .subvendor = 0x0070, + .subdevice = 0x22f2, + .card = CX23885_BOARD_HAUPPAUGE_HVR1275, + }, { + .subvendor = 0x0070, + .subdevice = 0x22f3, + .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */ + }, { + .subvendor = 0x0070, + .subdevice = 0x22f4, + .card = CX23885_BOARD_HAUPPAUGE_HVR1210, + }, { + .subvendor = 0x0070, + .subdevice = 0x22f5, + .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */ + }, { .subvendor = 0x14f1, .subdevice = 0x8651, .card = CX23885_BOARD_MYGICA_X8506, diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index 0dde57e..ff76f64 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -1142,7 +1142,7 @@ void cx23885_free_buffer(struct videobuf_queue *q, struct cx23885_buffer *buf) BUG_ON(in_interrupt()); videobuf_waiton(&buf->vb, 0, 0); - videobuf_dma_unmap(q, dma); + videobuf_dma_unmap(q->dev, dma); videobuf_dma_free(dma); btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc); buf->vb.state = VIDEOBUF_NEEDS_INIT; @@ -1953,8 +1953,12 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev, goto fail_irq; } - err = request_irq(pci_dev->irq, cx23885_irq, - IRQF_SHARED | IRQF_DISABLED, dev->name, dev); + if (!pci_enable_msi(pci_dev)) + err = request_irq(pci_dev->irq, cx23885_irq, + IRQF_DISABLED, dev->name, dev); + else + err = request_irq(pci_dev->irq, cx23885_irq, + IRQF_SHARED | IRQF_DISABLED, dev->name, dev); if (err < 0) { printk(KERN_ERR "%s: can't get IRQ %d\n", dev->name, pci_dev->irq); @@ -2000,6 +2004,7 @@ static void __devexit cx23885_finidev(struct pci_dev *pci_dev) /* unregister stuff */ free_irq(pci_dev->irq, dev); + pci_disable_msi(pci_dev); cx23885_dev_unregister(dev); v4l2_device_unregister(v4l2_dev); diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index 0a199d7..3d70af2 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -991,7 +991,7 @@ static int dvb_register(struct cx23885_tsport *port) ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port, &dev->pci->dev, adapter_nr, 0, cx23885_dvb_fe_ioctl_override); - if (!ret) + if (ret) return ret; /* init CI & MAC */ diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c index 5de6ba9..d0b1613 100644 --- a/drivers/media/video/cx23885/cx23885-input.c +++ b/drivers/media/video/cx23885/cx23885-input.c @@ -37,161 +37,55 @@ #include <linux/input.h> #include <linux/slab.h> -#include <media/ir-common.h> +#include <media/ir-core.h> #include <media/v4l2-subdev.h> #include "cx23885.h" -#define RC5_BITS 14 -#define RC5_HALF_BITS (2*RC5_BITS) -#define RC5_HALF_BITS_MASK ((1 << RC5_HALF_BITS) - 1) - -#define RC5_START_BITS_NORMAL 0x3 /* Command range 0 - 63 */ -#define RC5_START_BITS_EXTENDED 0x2 /* Command range 64 - 127 */ - -#define RC5_EXTENDED_COMMAND_OFFSET 64 - #define MODULE_NAME "cx23885" -static inline unsigned int rc5_command(u32 rc5_baseband) +static void convert_measurement(u32 x, struct ir_raw_event *y) { - return RC5_INSTR(rc5_baseband) + - ((RC5_START(rc5_baseband) == RC5_START_BITS_EXTENDED) - ? RC5_EXTENDED_COMMAND_OFFSET : 0); -} - -static void cx23885_input_process_raw_rc5(struct cx23885_dev *dev) -{ - struct card_ir *ir_input = dev->ir_input; - unsigned int code, command; - u32 rc5; - - /* Ignore codes that are too short to be valid RC-5 */ - if (ir_input->last_bit < (RC5_HALF_BITS - 1)) - return; - - /* The library has the manchester coding backwards; XOR to adapt. */ - code = (ir_input->code & RC5_HALF_BITS_MASK) ^ RC5_HALF_BITS_MASK; - rc5 = ir_rc5_decode(code); - - switch (RC5_START(rc5)) { - case RC5_START_BITS_NORMAL: - break; - case RC5_START_BITS_EXTENDED: - /* Don't allow if the remote only emits standard commands */ - if (ir_input->start == RC5_START_BITS_NORMAL) - return; - break; - default: + if (x == V4L2_SUBDEV_IR_PULSE_RX_SEQ_END) { + y->pulse = false; + y->duration = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS; return; } - if (ir_input->addr != RC5_ADDR(rc5)) - return; - - /* Don't generate a keypress for RC-5 auto-repeated keypresses */ - command = rc5_command(rc5); - if (RC5_TOGGLE(rc5) != RC5_TOGGLE(ir_input->last_rc5) || - command != rc5_command(ir_input->last_rc5) || - /* Catch T == 0, CMD == 0 (e.g. '0') as first keypress after init */ - RC5_START(ir_input->last_rc5) == 0) { - /* This keypress is differnet: not an auto repeat */ - ir_input_nokey(ir_input->dev, &ir_input->ir); - ir_input_keydown(ir_input->dev, &ir_input->ir, command); - } - ir_input->last_rc5 = rc5; - - /* Schedule when we should do the key up event: ir_input_nokey() */ - mod_timer(&ir_input->timer_keyup, - jiffies + msecs_to_jiffies(ir_input->rc5_key_timeout)); + y->pulse = (x & V4L2_SUBDEV_IR_PULSE_LEVEL_MASK) ? true : false; + y->duration = x & V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS; } -static void cx23885_input_next_pulse_width_rc5(struct cx23885_dev *dev, - u32 ns_pulse) +static void cx23885_input_process_measurements(struct cx23885_dev *dev, + bool overrun) { - const int rc5_quarterbit_ns = 444444; /* 32 cycles/36 kHz/2 = 444 us */ - struct card_ir *ir_input = dev->ir_input; - int i, level, quarterbits, halfbits; - - if (!ir_input->active) { - ir_input->active = 1; - /* assume an initial space that we may not detect or measure */ - ir_input->code = 0; - ir_input->last_bit = 0; - } + struct cx23885_kernel_ir *kernel_ir = dev->kernel_ir; + struct ir_raw_event kernel_ir_event; - if (ns_pulse == V4L2_SUBDEV_IR_PULSE_RX_SEQ_END) { - ir_input->last_bit++; /* Account for the final space */ - ir_input->active = 0; - cx23885_input_process_raw_rc5(dev); - return; - } - - level = (ns_pulse & V4L2_SUBDEV_IR_PULSE_LEVEL_MASK) ? 1 : 0; - - /* Skip any leading space to sync to the start bit */ - if (ir_input->last_bit == 0 && level == 0) - return; - - /* - * With valid RC-5 we can get up to two consecutive half-bits in a - * single pulse measurment. Experiments have shown that the duration - * of a half-bit can vary. Make sure we always end up with an even - * number of quarter bits at the same level (mark or space). - */ - ns_pulse &= V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS; - quarterbits = ns_pulse / rc5_quarterbit_ns; - if (quarterbits & 1) - quarterbits++; - halfbits = quarterbits / 2; - - for (i = 0; i < halfbits; i++) { - ir_input->last_bit++; - ir_input->code |= (level << ir_input->last_bit); - - if (ir_input->last_bit >= RC5_HALF_BITS-1) { - ir_input->active = 0; - cx23885_input_process_raw_rc5(dev); - /* - * If level is 1, a leading mark is invalid for RC5. - * If level is 0, we scan past extra intial space. - * Either way we don't want to reactivate collecting - * marks or spaces here with any left over half-bits. - */ - break; - } - } -} - -static void cx23885_input_process_pulse_widths_rc5(struct cx23885_dev *dev, - bool add_eom) -{ - struct card_ir *ir_input = dev->ir_input; - struct ir_input_state *ir_input_state = &ir_input->ir; - - u32 ns_pulse[RC5_HALF_BITS+1]; - ssize_t num = 0; + u32 sd_ir_data[64]; + ssize_t num; int count, i; + bool handle = false; do { - v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ns_pulse, - sizeof(ns_pulse), &num); + num = 0; + v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) sd_ir_data, + sizeof(sd_ir_data), &num); count = num / sizeof(u32); - /* Append an end of Rx seq, if the caller requested */ - if (add_eom && count < ARRAY_SIZE(ns_pulse)) { - ns_pulse[count] = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END; - count++; + for (i = 0; i < count; i++) { + convert_measurement(sd_ir_data[i], &kernel_ir_event); + ir_raw_event_store(kernel_ir->inp_dev, + &kernel_ir_event); + handle = true; } - - /* Just drain the Rx FIFO, if we're called, but not RC-5 */ - if (ir_input_state->ir_type != IR_TYPE_RC5) - continue; - - for (i = 0; i < count; i++) - cx23885_input_next_pulse_width_rc5(dev, ns_pulse[i]); } while (num != 0); + + if (overrun) + ir_raw_event_reset(kernel_ir->inp_dev); + else if (handle) + ir_raw_event_handle(kernel_ir->inp_dev); } void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events) @@ -230,7 +124,7 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events) } if (data_available) - cx23885_input_process_pulse_widths_rc5(dev, overrun); + cx23885_input_process_measurements(dev, overrun); if (overrun) { /* If there was a FIFO overrun, clear & restart the device */ @@ -241,34 +135,15 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events) } } -static void cx23885_input_ir_start(struct cx23885_dev *dev) +static int cx23885_input_ir_start(struct cx23885_dev *dev) { - struct card_ir *ir_input = dev->ir_input; - struct ir_input_state *ir_input_state = &ir_input->ir; struct v4l2_subdev_ir_parameters params; if (dev->sd_ir == NULL) - return; + return -ENODEV; atomic_set(&dev->ir_input_stopping, 0); - /* keyup timer set up, if needed */ - switch (dev->board) { - case CX23885_BOARD_HAUPPAUGE_HVR1850: - case CX23885_BOARD_HAUPPAUGE_HVR1290: - setup_timer(&ir_input->timer_keyup, - ir_rc5_timer_keyup, /* Not actually RC-5 specific */ - (unsigned long) ir_input); - if (ir_input_state->ir_type == IR_TYPE_RC5) { - /* - * RC-5 repeats a held key every - * 64 bits * (2 * 32/36000) sec/bit = 113.778 ms - */ - ir_input->rc5_key_timeout = 115; - } - break; - } - v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); switch (dev->board) { case CX23885_BOARD_HAUPPAUGE_HVR1850: @@ -299,11 +174,21 @@ static void cx23885_input_ir_start(struct cx23885_dev *dev) break; } v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); + return 0; +} + +static int cx23885_input_ir_open(void *priv) +{ + struct cx23885_kernel_ir *kernel_ir = priv; + + if (kernel_ir->cx == NULL) + return -ENODEV; + + return cx23885_input_ir_start(kernel_ir->cx); } static void cx23885_input_ir_stop(struct cx23885_dev *dev) { - struct card_ir *ir_input = dev->ir_input; struct v4l2_subdev_ir_parameters params; if (dev->sd_ir == NULL) @@ -327,21 +212,26 @@ static void cx23885_input_ir_stop(struct cx23885_dev *dev) } flush_scheduled_work(); +} - switch (dev->board) { - case CX23885_BOARD_HAUPPAUGE_HVR1850: - case CX23885_BOARD_HAUPPAUGE_HVR1290: - del_timer_sync(&ir_input->timer_keyup); - break; - } +static void cx23885_input_ir_close(void *priv) +{ + struct cx23885_kernel_ir *kernel_ir = priv; + + if (kernel_ir->cx != NULL) + cx23885_input_ir_stop(kernel_ir->cx); } int cx23885_input_init(struct cx23885_dev *dev) { - struct card_ir *ir; - struct input_dev *input_dev; - char *ir_codes = NULL; - int ir_type, ir_addr, ir_start; + struct cx23885_kernel_ir *kernel_ir; + struct input_dev *inp_dev; + struct ir_dev_props *props; + + char *rc_map; + enum rc_driver_type driver_type; + unsigned long allowed_protos; + int ret; /* @@ -354,53 +244,59 @@ int cx23885_input_init(struct cx23885_dev *dev) switch (dev->board) { case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: - /* Parameters for the grey Hauppauge remote for the HVR-1850 */ - ir_codes = RC_MAP_HAUPPAUGE_NEW; - ir_type = IR_TYPE_RC5; - ir_addr = 0x1e; /* RC-5 system bits emitted by the remote */ - ir_start = RC5_START_BITS_NORMAL; /* A basic RC-5 remote */ + /* Integrated CX23888 IR controller */ + driver_type = RC_DRIVER_IR_RAW; + allowed_protos = IR_TYPE_ALL; + /* The grey Hauppauge RC-5 remote */ + rc_map = RC_MAP_RC5_HAUPPAUGE_NEW; break; - } - if (ir_codes == NULL) + default: return -ENODEV; - - ir = kzalloc(sizeof(*ir), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!ir || !input_dev) { - ret = -ENOMEM; - goto err_out_free; } - ir->dev = input_dev; - ir->addr = ir_addr; - ir->start = ir_start; + /* cx23885 board instance kernel IR state */ + kernel_ir = kzalloc(sizeof(struct cx23885_kernel_ir), GFP_KERNEL); + if (kernel_ir == NULL) + return -ENOMEM; - /* init input device */ - snprintf(ir->name, sizeof(ir->name), "cx23885 IR (%s)", - cx23885_boards[dev->board].name); - snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(dev->pci)); + kernel_ir->cx = dev; + kernel_ir->name = kasprintf(GFP_KERNEL, "cx23885 IR (%s)", + cx23885_boards[dev->board].name); + kernel_ir->phys = kasprintf(GFP_KERNEL, "pci-%s/ir0", + pci_name(dev->pci)); - ret = ir_input_init(input_dev, &ir->ir, ir_type); - if (ret < 0) + /* input device */ + inp_dev = input_allocate_device(); + if (inp_dev == NULL) { + ret = -ENOMEM; goto err_out_free; + } - input_dev->name = ir->name; - input_dev->phys = ir->phys; - input_dev->id.bustype = BUS_PCI; - input_dev->id.version = 1; + kernel_ir->inp_dev = inp_dev; + inp_dev->name = kernel_ir->name; + inp_dev->phys = kernel_ir->phys; + inp_dev->id.bustype = BUS_PCI; + inp_dev->id.version = 1; if (dev->pci->subsystem_vendor) { - input_dev->id.vendor = dev->pci->subsystem_vendor; - input_dev->id.product = dev->pci->subsystem_device; + inp_dev->id.vendor = dev->pci->subsystem_vendor; + inp_dev->id.product = dev->pci->subsystem_device; } else { - input_dev->id.vendor = dev->pci->vendor; - input_dev->id.product = dev->pci->device; + inp_dev->id.vendor = dev->pci->vendor; + inp_dev->id.product = dev->pci->device; } - input_dev->dev.parent = &dev->pci->dev; - - dev->ir_input = ir; - cx23885_input_ir_start(dev); - - ret = ir_input_register(ir->dev, ir_codes, NULL, MODULE_NAME); + inp_dev->dev.parent = &dev->pci->dev; + + /* kernel ir device properties */ + props = &kernel_ir->props; + props->driver_type = driver_type; + props->allowed_protos = allowed_protos; + props->priv = kernel_ir; + props->open = cx23885_input_ir_open; + props->close = cx23885_input_ir_close; + + /* Go */ + dev->kernel_ir = kernel_ir; + ret = ir_input_register(inp_dev, rc_map, props, MODULE_NAME); if (ret) goto err_out_stop; @@ -408,9 +304,12 @@ int cx23885_input_init(struct cx23885_dev *dev) err_out_stop: cx23885_input_ir_stop(dev); - dev->ir_input = NULL; + dev->kernel_ir = NULL; + /* TODO: double check clean-up of kernel_ir->inp_dev */ err_out_free: - kfree(ir); + kfree(kernel_ir->phys); + kfree(kernel_ir->name); + kfree(kernel_ir); return ret; } @@ -419,9 +318,11 @@ void cx23885_input_fini(struct cx23885_dev *dev) /* Always stop the IR hardware from generating interrupts */ cx23885_input_ir_stop(dev); - if (dev->ir_input == NULL) + if (dev->kernel_ir == NULL) return; - ir_input_unregister(dev->ir_input->dev); - kfree(dev->ir_input); - dev->ir_input = NULL; + ir_input_unregister(dev->kernel_ir->inp_dev); + kfree(dev->kernel_ir->phys); + kfree(dev->kernel_ir->name); + kfree(dev->kernel_ir); + dev->kernel_ir = NULL; } diff --git a/drivers/media/video/cx23885/cx23885-ir.c b/drivers/media/video/cx23885/cx23885-ir.c index 9a677eb..6ceabd4 100644 --- a/drivers/media/video/cx23885/cx23885-ir.c +++ b/drivers/media/video/cx23885/cx23885-ir.c @@ -53,7 +53,7 @@ void cx23885_ir_rx_work_handler(struct work_struct *work) if (events == 0) return; - if (dev->ir_input) + if (dev->kernel_ir) cx23885_input_rx_work_handler(dev, events); } diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index 8d6a55e..a33f2b7 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -30,6 +30,7 @@ #include <media/tveeprom.h> #include <media/videobuf-dma-sg.h> #include <media/videobuf-dvb.h> +#include <media/ir-core.h> #include "btcx-risc.h" #include "cx23885-reg.h" @@ -304,6 +305,15 @@ struct cx23885_tsport { void *port_priv; }; +struct cx23885_kernel_ir { + struct cx23885_dev *cx; + char *name; + char *phys; + + struct input_dev *inp_dev; + struct ir_dev_props props; +}; + struct cx23885_dev { atomic_t refcount; struct v4l2_device v4l2_dev; @@ -363,7 +373,7 @@ struct cx23885_dev { struct work_struct ir_tx_work; unsigned long ir_tx_notifications; - struct card_ir *ir_input; + struct cx23885_kernel_ir *kernel_ir; atomic_t ir_input_stopping; /* V4l */ diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c index 33082c9..4f383cd 100644 --- a/drivers/media/video/cx88/cx88-alsa.c +++ b/drivers/media/video/cx88/cx88-alsa.c @@ -54,6 +54,12 @@ Data type declarations - Can be moded to a header file later ****************************************************************************/ +struct cx88_audio_buffer { + unsigned int bpl; + struct btcx_riscmem risc; + struct videobuf_dmabuf dma; +}; + struct cx88_audio_dev { struct cx88_core *core; struct cx88_dmaqueue q; @@ -75,7 +81,7 @@ struct cx88_audio_dev { struct videobuf_dmabuf *dma_risc; - struct cx88_buffer *buf; + struct cx88_audio_buffer *buf; struct snd_pcm_substream *substream; }; @@ -123,7 +129,7 @@ MODULE_PARM_DESC(debug,"enable debug messages"); static int _cx88_start_audio_dma(snd_cx88_card_t *chip) { - struct cx88_buffer *buf = chip->buf; + struct cx88_audio_buffer *buf = chip->buf; struct cx88_core *core=chip->core; struct sram_channel *audio_ch = &cx88_sram_channels[SRAM_CH25]; @@ -283,7 +289,7 @@ static int dsp_buffer_free(snd_cx88_card_t *chip) BUG_ON(!chip->dma_size); dprintk(2,"Freeing buffer\n"); - videobuf_sg_dma_unmap(&chip->pci->dev, chip->dma_risc); + videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc); videobuf_dma_free(chip->dma_risc); btcx_riscmem_free(chip->pci,&chip->buf->risc); kfree(chip->buf); @@ -376,7 +382,7 @@ static int snd_cx88_hw_params(struct snd_pcm_substream * substream, snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); struct videobuf_dmabuf *dma; - struct cx88_buffer *buf; + struct cx88_audio_buffer *buf; int ret; if (substream->runtime->dma_area) { @@ -391,30 +397,25 @@ static int snd_cx88_hw_params(struct snd_pcm_substream * substream, BUG_ON(!chip->dma_size); BUG_ON(chip->num_periods & (chip->num_periods-1)); - buf = videobuf_sg_alloc(sizeof(*buf)); + buf = kzalloc(sizeof(*buf), GFP_KERNEL); if (NULL == buf) return -ENOMEM; - buf->vb.memory = V4L2_MEMORY_MMAP; - buf->vb.field = V4L2_FIELD_NONE; - buf->vb.width = chip->period_size; - buf->bpl = chip->period_size; - buf->vb.height = chip->num_periods; - buf->vb.size = chip->dma_size; + buf->bpl = chip->period_size; - dma = videobuf_to_dma(&buf->vb); + dma = &buf->dma; videobuf_dma_init(dma); ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE, - (PAGE_ALIGN(buf->vb.size) >> PAGE_SHIFT)); + (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT)); if (ret < 0) goto error; - ret = videobuf_sg_dma_map(&chip->pci->dev, dma); + ret = videobuf_dma_map(&chip->pci->dev, dma); if (ret < 0) goto error; ret = cx88_risc_databuffer(chip->pci, &buf->risc, dma->sglist, - buf->vb.width, buf->vb.height, 1); + chip->period_size, chip->num_periods, 1); if (ret < 0) goto error; @@ -422,12 +423,10 @@ static int snd_cx88_hw_params(struct snd_pcm_substream * substream, buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC); buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - buf->vb.state = VIDEOBUF_PREPARED; - chip->buf = buf; chip->dma_risc = dma; - substream->runtime->dma_area = chip->dma_risc->vmalloc; + substream->runtime->dma_area = chip->dma_risc->vaddr; substream->runtime->dma_bytes = chip->dma_size; substream->runtime->dma_addr = 0; return 0; @@ -740,7 +739,7 @@ static int __devinit snd_cx88_create(struct snd_card *card, pci_set_master(pci); - chip = (snd_cx88_card_t *) card->private_data; + chip = card->private_data; core = cx88_core_get(pci); if (NULL == core) { diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 2918a6e..e8416b7 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -45,6 +45,10 @@ static unsigned int latency = UNSET; module_param(latency,int,0444); MODULE_PARM_DESC(latency,"pci latency timer"); +static int disable_ir; +module_param(disable_ir, int, 0444); +MODULE_PARM_DESC(latency, "Disable IR support"); + #define info_printk(core, fmt, arg...) \ printk(KERN_INFO "%s: " fmt, core->name , ## arg) @@ -3498,7 +3502,10 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) } cx88_card_setup(core); - cx88_ir_init(core, pci); + if (!disable_ir) { + cx88_i2c_init_ir(core); + cx88_ir_init(core, pci); + } return core; } diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c index 8b21457..85eb266f 100644 --- a/drivers/media/video/cx88/cx88-core.c +++ b/drivers/media/video/cx88/cx88-core.c @@ -218,7 +218,7 @@ cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf) BUG_ON(in_interrupt()); videobuf_waiton(&buf->vb,0,0); - videobuf_dma_unmap(q, dma); + videobuf_dma_unmap(q->dev, dma); videobuf_dma_free(dma); btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc); buf->vb.state = VIDEOBUF_NEEDS_INIT; diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c index fb39f11..375ad53 100644 --- a/drivers/media/video/cx88/cx88-i2c.c +++ b/drivers/media/video/cx88/cx88-i2c.c @@ -181,6 +181,11 @@ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci) } else printk("%s: i2c register FAILED\n", core->name); + return core->i2c_rc; +} + +void cx88_i2c_init_ir(struct cx88_core *core) +{ /* Instantiate the IR receiver device, if present */ if (0 == core->i2c_rc) { struct i2c_board_info info; @@ -207,7 +212,6 @@ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci) } } } - return core->i2c_rc; } /* ----------------------------------------------------------------------- */ diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index e185289..eccc5e4 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -30,6 +30,7 @@ #include <linux/module.h> #include "cx88.h" +#include <media/ir-core.h> #include <media/ir-common.h> #define MODULE_NAME "cx88xx" @@ -39,8 +40,8 @@ struct cx88_IR { struct cx88_core *core; struct input_dev *input; - struct ir_input_state ir; struct ir_dev_props props; + u64 ir_type; int users; @@ -51,7 +52,6 @@ struct cx88_IR { u32 sampling; u32 samples[16]; int scount; - unsigned long release; /* poll external decoder */ int polling; @@ -125,29 +125,21 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) data = (data << 4) | ((gpio_key & 0xf0) >> 4); - ir_input_keydown(ir->input, &ir->ir, data); - ir_input_nokey(ir->input, &ir->ir); + ir_keydown(ir->input, data, 0); } else if (ir->mask_keydown) { /* bit set on keydown */ - if (gpio & ir->mask_keydown) { - ir_input_keydown(ir->input, &ir->ir, data); - } else { - ir_input_nokey(ir->input, &ir->ir); - } + if (gpio & ir->mask_keydown) + ir_keydown(ir->input, data, 0); } else if (ir->mask_keyup) { /* bit cleared on keydown */ - if (0 == (gpio & ir->mask_keyup)) { - ir_input_keydown(ir->input, &ir->ir, data); - } else { - ir_input_nokey(ir->input, &ir->ir); - } + if (0 == (gpio & ir->mask_keyup)) + ir_keydown(ir->input, data, 0); } else { /* can't distinguish keydown/up :-/ */ - ir_input_keydown(ir->input, &ir->ir, data); - ir_input_nokey(ir->input, &ir->ir); + ir_keydown(ir->input, data, 0); } } @@ -439,9 +431,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)", core->board.name); snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(pci)); - err = ir_input_init(input_dev, &ir->ir, ir_type); - if (err < 0) - goto err_out_free; + ir->ir_type = ir_type; input_dev->name = ir->name; input_dev->phys = ir->phys; @@ -516,8 +506,6 @@ void cx88_ir_irq(struct cx88_core *core) } if (!ir->scount) { /* nothing to sample */ - if (ir->ir.keypressed && time_after(jiffies, ir->release)) - ir_input_nokey(ir->input, &ir->ir); return; } @@ -553,7 +541,7 @@ void cx88_ir_irq(struct cx88_core *core) if (ircode == 0) { /* key still pressed */ ir_dprintk("pulse distance decoded repeat code\n"); - ir->release = jiffies + msecs_to_jiffies(120); + ir_repeat(ir->input); break; } @@ -567,10 +555,8 @@ void cx88_ir_irq(struct cx88_core *core) break; } - ir_dprintk("Key Code: %x\n", (ircode >> 16) & 0x7f); - - ir_input_keydown(ir->input, &ir->ir, (ircode >> 16) & 0x7f); - ir->release = jiffies + msecs_to_jiffies(120); + ir_dprintk("Key Code: %x\n", (ircode >> 16) & 0xff); + ir_keydown(ir->input, (ircode >> 16) & 0xff, 0); break; case CX88_BOARD_HAUPPAUGE: case CX88_BOARD_HAUPPAUGE_DVB_T1: @@ -606,16 +592,16 @@ void cx88_ir_irq(struct cx88_core *core) if ( dev != 0x1e && dev != 0x1f ) /* not a hauppauge remote */ break; - ir_input_keydown(ir->input, &ir->ir, code); - ir->release = jiffies + msecs_to_jiffies(120); + ir_keydown(ir->input, code, toggle); break; case CX88_BOARD_PINNACLE_PCTV_HD_800i: ircode = ir_decode_biphase(ir->samples, ir->scount, 5, 7); ir_dprintk("biphase decoded: %x\n", ircode); if ((ircode & 0xfffff000) != 0x3000) break; - ir_input_keydown(ir->input, &ir->ir, ircode & 0x3f); - ir->release = jiffies + msecs_to_jiffies(120); + /* Note: bit 0x800 being the toggle is assumed, not checked + with real hardware */ + ir_keydown(ir->input, ircode & 0x3f, ircode & 0x0800 ? 1 : 0); break; } diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index bdb03d3..33d161a 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -636,6 +636,7 @@ extern struct videobuf_queue_ops cx8800_vbi_qops; /* cx88-i2c.c */ extern int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci); +extern void cx88_i2c_init_ir(struct cx88_core *core); /* ----------------------------------------------------------- */ diff --git a/drivers/media/video/dabusb.c b/drivers/media/video/dabusb.c index 0f50508..5b176bd 100644 --- a/drivers/media/video/dabusb.c +++ b/drivers/media/video/dabusb.c @@ -706,16 +706,11 @@ static long dabusb_ioctl (struct file *file, unsigned int cmd, unsigned long arg switch (cmd) { case IOCTL_DAB_BULK: - pbulk = kmalloc(sizeof (bulk_transfer_t), GFP_KERNEL); + pbulk = memdup_user((void __user *)arg, + sizeof(bulk_transfer_t)); - if (!pbulk) { - ret = -ENOMEM; - break; - } - - if (copy_from_user (pbulk, (void __user *) arg, sizeof (bulk_transfer_t))) { - ret = -EFAULT; - kfree (pbulk); + if (IS_ERR(pbulk)) { + ret = PTR_ERR(pbulk); break; } diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig new file mode 100644 index 0000000..6b19540 --- /dev/null +++ b/drivers/media/video/davinci/Kconfig @@ -0,0 +1,93 @@ +config DISPLAY_DAVINCI_DM646X_EVM + tristate "DM646x EVM Video Display" + depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM + select VIDEOBUF_DMA_CONTIG + select VIDEO_DAVINCI_VPIF + select VIDEO_ADV7343 + select VIDEO_THS7303 + help + Support for DM6467 based display device. + + To compile this driver as a module, choose M here: the + module will be called vpif_display. + +config CAPTURE_DAVINCI_DM646X_EVM + tristate "DM646x EVM Video Capture" + depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM + select VIDEOBUF_DMA_CONTIG + select VIDEO_DAVINCI_VPIF + help + Support for DM6467 based capture device. + + To compile this driver as a module, choose M here: the + module will be called vpif_capture. + +config VIDEO_DAVINCI_VPIF + tristate "DaVinci VPIF Driver" + depends on DISPLAY_DAVINCI_DM646X_EVM + help + Support for DaVinci VPIF Driver. + + To compile this driver as a module, choose M here: the + module will be called vpif. + +config VIDEO_VPSS_SYSTEM + tristate "VPSS System module driver" + depends on ARCH_DAVINCI + help + Support for vpss system module for video driver + +config VIDEO_VPFE_CAPTURE + tristate "VPFE Video Capture Driver" + depends on VIDEO_V4L2 && (ARCH_DAVINCI || ARCH_OMAP3) + select VIDEOBUF_DMA_CONTIG + help + Support for DMx/AMx VPFE based frame grabber. This is the + common V4L2 module for following DMx/AMx SoCs from Texas + Instruments:- DM6446, DM365, DM355 & AM3517/05. + + To compile this driver as a module, choose M here: the + module will be called vpfe-capture. + +config VIDEO_DM6446_CCDC + tristate "DM6446 CCDC HW module" + depends on VIDEO_VPFE_CAPTURE + select VIDEO_VPSS_SYSTEM + default y + help + Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces + with decoder modules such as TVP5146 over BT656 or + sensor module such as MT9T001 over a raw interface. This + module configures the interface and CCDC/ISIF to do + video frame capture from slave decoders. + + To compile this driver as a module, choose M here: the + module will be called vpfe. + +config VIDEO_DM355_CCDC + tristate "DM355 CCDC HW module" + depends on ARCH_DAVINCI_DM355 && VIDEO_VPFE_CAPTURE + select VIDEO_VPSS_SYSTEM + default y + help + Enables DM355 CCD hw module. DM355 CCDC hw interfaces + with decoder modules such as TVP5146 over BT656 or + sensor module such as MT9T001 over a raw interface. This + module configures the interface and CCDC/ISIF to do + video frame capture from a slave decoders + + To compile this driver as a module, choose M here: the + module will be called vpfe. + +config VIDEO_ISIF + tristate "ISIF HW module" + depends on ARCH_DAVINCI_DM365 && VIDEO_VPFE_CAPTURE + select VIDEO_VPSS_SYSTEM + default y + help + Enables ISIF hw module. This is the hardware module for + configuring ISIF in VPFE to capture Raw Bayer RGB data from + a image sensor or YUV data from a YUV source. + + To compile this driver as a module, choose M here: the + module will be called vpfe. diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 3a4fd85..ffbe544 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -158,6 +158,22 @@ static struct em28xx_reg_seq evga_indtube_digital[] = { { -1, -1, -1, -1}, }; +/* + * KWorld PlusTV 340U and UB435-Q (ATSC) GPIOs map: + * EM_GPIO_0 - currently unknown + * EM_GPIO_1 - LED disable/enable (1 = off, 0 = on) + * EM_GPIO_2 - currently unknown + * EM_GPIO_3 - currently unknown + * EM_GPIO_4 - TDA18271HD/C1 tuner (1 = active, 0 = in reset) + * EM_GPIO_5 - LGDT3304 ATSC/QAM demod (1 = active, 0 = in reset) + * EM_GPIO_6 - currently unknown + * EM_GPIO_7 - currently unknown + */ +static struct em28xx_reg_seq kworld_a340_digital[] = { + {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + /* Pinnacle Hybrid Pro eb1a:2881 */ static struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = { {EM28XX_R08_GPIO, 0xfd, ~EM_GPIO_4, 10}, @@ -1667,6 +1683,16 @@ struct em28xx_board em28xx_boards[] = { .tuner_gpio = reddo_dvb_c_usb_box, .has_dvb = 1, }, + /* 1b80:a340 - Empia EM2870, NXP TDA18271HD and LG DT3304, sold + * initially as the KWorld PlusTV 340U, then as the UB435-Q. + * Early variants have a TDA18271HD/C1, later ones a TDA18271HD/C2 */ + [EM2870_BOARD_KWORLD_A340] = { + .name = "KWorld PlusTV 340U or UB435-Q (ATSC)", + .tuner_type = TUNER_ABSENT, /* Digital-only TDA18271HD */ + .has_dvb = 1, + .dvb_gpio = kworld_a340_digital, + .tuner_gpio = default_tuner_gpio, + }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); @@ -1788,6 +1814,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2820_BOARD_IODATA_GVMVP_SZ }, { USB_DEVICE(0xeb1a, 0x50a6), .driver_info = EM2860_BOARD_GADMEI_UTV330 }, + { USB_DEVICE(0x1b80, 0xa340), + .driver_info = EM2870_BOARD_KWORLD_A340 }, { }, }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index cf1d8c3..3ac8d30 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -30,11 +30,13 @@ #include "tuner-simple.h" #include "lgdt330x.h" +#include "lgdt3305.h" #include "zl10353.h" #include "s5h1409.h" #include "mt352.h" #include "mt352_priv.h" /* FIXME */ #include "tda1002x.h" +#include "tda18271.h" MODULE_DESCRIPTION("driver for em28xx based DVB cards"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); @@ -231,6 +233,18 @@ static struct lgdt330x_config em2880_lgdt3303_dev = { .demod_chip = LGDT3303, }; +static struct lgdt3305_config em2870_lgdt3304_dev = { + .i2c_addr = 0x0e, + .demod_chip = LGDT3304, + .spectral_inversion = 1, + .deny_i2c_rptr = 1, + .mpeg_mode = LGDT3305_MPEG_PARALLEL, + .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE, + .tpvalid_polarity = LGDT3305_TP_VALID_HIGH, + .vsb_if_khz = 3250, + .qam_if_khz = 4000, +}; + static struct zl10353_config em28xx_zl10353_with_xc3028 = { .demod_address = (0x1e >> 1), .no_tuner = 1, @@ -247,6 +261,17 @@ static struct s5h1409_config em28xx_s5h1409_with_xc3028 = { .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK }; +static struct tda18271_std_map kworld_a340_std_map = { + .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 0, + .if_lvl = 1, .rfagc_top = 0x37, }, + .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 1, + .if_lvl = 1, .rfagc_top = 0x37, }, +}; + +static struct tda18271_config kworld_a340_config = { + .std_map = &kworld_a340_std_map, +}; + static struct zl10353_config em28xx_zl10353_xc3028_no_i2c_gate = { .demod_address = (0x1e >> 1), .no_tuner = 1, @@ -572,6 +597,14 @@ static int dvb_init(struct em28xx *dev) } } break; + case EM2870_BOARD_KWORLD_A340: + dvb->frontend = dvb_attach(lgdt3305_attach, + &em2870_lgdt3304_dev, + &dev->i2c_adap); + if (dvb->frontend != NULL) + dvb_attach(tda18271_attach, dvb->frontend, 0x60, + &dev->i2c_adap, &kworld_a340_config); + break; default: em28xx_errdev("/2: The frontend of your DVB/ATSC card" " isn't supported yet\n"); diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 5c3fd94..6759cd5 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -65,17 +65,14 @@ struct em28xx_ir_poll_result { struct em28xx_IR { struct em28xx *dev; struct input_dev *input; - struct ir_input_state ir; char name[32]; char phys[32]; /* poll external decoder */ int polling; struct delayed_work work; - unsigned int last_toggle:1; unsigned int full_code:1; unsigned int last_readcount; - unsigned int repeat_interval; int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); @@ -291,67 +288,39 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, static void em28xx_ir_handle_key(struct em28xx_IR *ir) { int result; - int do_sendkey = 0; struct em28xx_ir_poll_result poll_result; /* read the registers containing the IR status */ result = ir->get_key(ir, &poll_result); - if (result < 0) { + if (unlikely(result < 0)) { dprintk("ir->get_key() failed %d\n", result); return; } - dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x%02x\n", - poll_result.toggle_bit, poll_result.read_count, - ir->last_readcount, poll_result.rc_address, - poll_result.rc_data[0]); - - if (ir->dev->chip_id == CHIP_ID_EM2874) { - /* The em2874 clears the readcount field every time the - register is read. The em2860/2880 datasheet says that it - is supposed to clear the readcount, but it doesn't. So with - the em2874, we are looking for a non-zero read count as - opposed to a readcount that is incrementing */ - ir->last_readcount = 0; - } - - if (poll_result.read_count == 0) { - /* The button has not been pressed since the last read */ - } else if (ir->last_toggle != poll_result.toggle_bit) { - /* A button has been pressed */ - dprintk("button has been pressed\n"); - ir->last_toggle = poll_result.toggle_bit; - ir->repeat_interval = 0; - do_sendkey = 1; - } else if (poll_result.toggle_bit == ir->last_toggle && - poll_result.read_count > 0 && - poll_result.read_count != ir->last_readcount) { - /* The button is still being held down */ - dprintk("button being held down\n"); - - /* Debouncer for first keypress */ - if (ir->repeat_interval++ > 9) { - /* Start repeating after 1 second */ - do_sendkey = 1; - } - } - - if (do_sendkey) { - dprintk("sending keypress\n"); - + if (unlikely(poll_result.read_count != ir->last_readcount)) { + dprintk("%s: toggle: %d, count: %d, key 0x%02x%02x\n", __func__, + poll_result.toggle_bit, poll_result.read_count, + poll_result.rc_address, poll_result.rc_data[0]); if (ir->full_code) - ir_input_keydown(ir->input, &ir->ir, - poll_result.rc_address << 8 | - poll_result.rc_data[0]); + ir_keydown(ir->input, + poll_result.rc_address << 8 | + poll_result.rc_data[0], + poll_result.toggle_bit); else - ir_input_keydown(ir->input, &ir->ir, - poll_result.rc_data[0]); - - ir_input_nokey(ir->input, &ir->ir); + ir_keydown(ir->input, + poll_result.rc_data[0], + poll_result.toggle_bit); + + if (ir->dev->chip_id == CHIP_ID_EM2874) + /* The em2874 clears the readcount field every time the + register is read. The em2860/2880 datasheet says that it + is supposed to clear the readcount, but it doesn't. So with + the em2874, we are looking for a non-zero read count as + opposed to a readcount that is incrementing */ + ir->last_readcount = 0; + else + ir->last_readcount = poll_result.read_count; } - - ir->last_readcount = poll_result.read_count; - return; } static void em28xx_ir_work(struct work_struct *work) @@ -466,11 +435,6 @@ int em28xx_ir_init(struct em28xx *dev) usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); strlcat(ir->phys, "/input0", sizeof(ir->phys)); - /* Set IR protocol */ - err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER); - if (err < 0) - goto err_out_free; - input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_USB; diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 20090e3..7b9ec6e 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -654,12 +654,12 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) } if (buf != NULL && dev->capture_type == 2) { - if (len > 4 && p[0] == 0x88 && p[1] == 0x88 && + if (len >= 4 && p[0] == 0x88 && p[1] == 0x88 && p[2] == 0x88 && p[3] == 0x88) { p += 4; len -= 4; } - if (len > 4 && p[0] == 0x22 && p[1] == 0x5a) { + if (len >= 4 && p[0] == 0x22 && p[1] == 0x5a) { em28xx_isocdbg("Video frame %d, len=%i, %s\n", p[2], len, (p[2] & 1) ? "odd" : "even"); diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index b252d1b..1c61a6b 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -32,6 +32,7 @@ #include <linux/i2c.h> #include <linux/mutex.h> #include <media/ir-kbd-i2c.h> +#include <media/ir-core.h> #if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE) #include <media/videobuf-dvb.h> #endif @@ -113,6 +114,7 @@ #define EM2870_BOARD_REDDO_DVB_C_USB_BOX 73 #define EM2800_BOARD_VC211A 74 #define EM2882_BOARD_DIKOM_DK300 75 +#define EM2870_BOARD_KWORLD_A340 76 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 diff --git a/drivers/media/video/fsl-viu.c b/drivers/media/video/fsl-viu.c new file mode 100644 index 0000000..8f1c94f --- /dev/null +++ b/drivers/media/video/fsl-viu.c @@ -0,0 +1,1632 @@ +/* + * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * Freescale VIU video driver + * + * Authors: Hongjun Chen <hong-jun.chen@freescale.com> + * Porting to 2.6.35 by DENX Software Engineering, + * Anatolij Gustschin <agust@denx.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. + * + */ + +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/kernel.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/of_platform.h> +#include <linux/version.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf-dma-contig.h> + +#define DRV_NAME "fsl_viu" +#define VIU_MAJOR_VERSION 0 +#define VIU_MINOR_VERSION 5 +#define VIU_RELEASE 0 +#define VIU_VERSION KERNEL_VERSION(VIU_MAJOR_VERSION, \ + VIU_MINOR_VERSION, \ + VIU_RELEASE) + +#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ + +#define VIU_VID_MEM_LIMIT 4 /* Video memory limit, in Mb */ + +/* I2C address of video decoder chip is 0x4A */ +#define VIU_VIDEO_DECODER_ADDR 0x25 + +/* supported controls */ +static struct v4l2_queryctrl viu_qctrl[] = { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + .flags = 0, + }, { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 0x1, + .default_value = 0x10, + .flags = 0, + }, { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 255, + .step = 0x1, + .default_value = 127, + .flags = 0, + }, { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = -128, + .maximum = 127, + .step = 0x1, + .default_value = 0, + .flags = 0, + } +}; + +static int qctl_regs[ARRAY_SIZE(viu_qctrl)]; + +static int info_level; + +#define dprintk(level, fmt, arg...) \ + do { \ + if (level <= info_level) \ + printk(KERN_DEBUG "viu: " fmt , ## arg); \ + } while (0) + +/* + * Basic structures + */ +struct viu_fmt { + char name[32]; + u32 fourcc; /* v4l2 format id */ + u32 pixelformat; + int depth; +}; + +static struct viu_fmt formats[] = { + { + .name = "RGB-16 (5/B-6/G-5/R)", + .fourcc = V4L2_PIX_FMT_RGB565, + .pixelformat = V4L2_PIX_FMT_RGB565, + .depth = 16, + }, { + .name = "RGB-32 (A-R-G-B)", + .fourcc = V4L2_PIX_FMT_RGB32, + .pixelformat = V4L2_PIX_FMT_RGB32, + .depth = 32, + } +}; + +struct viu_dev; +struct viu_buf; + +/* buffer for one video frame */ +struct viu_buf { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + struct viu_fmt *fmt; +}; + +struct viu_dmaqueue { + struct viu_dev *dev; + struct list_head active; + struct list_head queued; + struct timer_list timeout; +}; + +struct viu_status { + u32 field_irq; + u32 vsync_irq; + u32 hsync_irq; + u32 vstart_irq; + u32 dma_end_irq; + u32 error_irq; +}; + +struct viu_reg { + u32 status_cfg; + u32 luminance; + u32 chroma_r; + u32 chroma_g; + u32 chroma_b; + u32 field_base_addr; + u32 dma_inc; + u32 picture_count; + u32 req_alarm; + u32 alpha; +} __attribute__ ((packed)); + +struct viu_dev { + struct v4l2_device v4l2_dev; + struct mutex lock; + spinlock_t slock; + int users; + + struct device *dev; + /* various device info */ + struct video_device *vdev; + struct viu_dmaqueue vidq; + enum v4l2_field capfield; + int field; + int first; + int dma_done; + + /* Hardware register area */ + struct viu_reg *vr; + + /* Interrupt vector */ + int irq; + struct viu_status irqs; + + /* video overlay */ + struct v4l2_framebuffer ovbuf; + struct viu_fmt *ovfmt; + unsigned int ovenable; + enum v4l2_field ovfield; + + /* crop */ + struct v4l2_rect crop_current; + + /* clock pointer */ + struct clk *clk; + + /* decoder */ + struct v4l2_subdev *decoder; +}; + +struct viu_fh { + struct viu_dev *dev; + + /* video capture */ + struct videobuf_queue vb_vidq; + spinlock_t vbq_lock; /* spinlock for the videobuf queue */ + + /* video overlay */ + struct v4l2_window win; + struct v4l2_clip clips[1]; + + /* video capture */ + struct viu_fmt *fmt; + int width, height, sizeimage; + enum v4l2_buf_type type; +}; + +static struct viu_reg reg_val; + +/* + * Macro definitions of VIU registers + */ + +/* STATUS_CONFIG register */ +enum status_config { + SOFT_RST = 1 << 0, + + ERR_MASK = 0x0f << 4, /* Error code mask */ + ERR_NO = 0x00, /* No error */ + ERR_DMA_V = 0x01 << 4, /* DMA in vertical active */ + ERR_DMA_VB = 0x02 << 4, /* DMA in vertical blanking */ + ERR_LINE_TOO_LONG = 0x04 << 4, /* Line too long */ + ERR_TOO_MANG_LINES = 0x05 << 4, /* Too many lines in field */ + ERR_LINE_TOO_SHORT = 0x06 << 4, /* Line too short */ + ERR_NOT_ENOUGH_LINE = 0x07 << 4, /* Not enough lines in field */ + ERR_FIFO_OVERFLOW = 0x08 << 4, /* FIFO overflow */ + ERR_FIFO_UNDERFLOW = 0x09 << 4, /* FIFO underflow */ + ERR_1bit_ECC = 0x0a << 4, /* One bit ECC error */ + ERR_MORE_ECC = 0x0b << 4, /* Two/more bits ECC error */ + + INT_FIELD_EN = 0x01 << 8, /* Enable field interrupt */ + INT_VSYNC_EN = 0x01 << 9, /* Enable vsync interrupt */ + INT_HSYNC_EN = 0x01 << 10, /* Enable hsync interrupt */ + INT_VSTART_EN = 0x01 << 11, /* Enable vstart interrupt */ + INT_DMA_END_EN = 0x01 << 12, /* Enable DMA end interrupt */ + INT_ERROR_EN = 0x01 << 13, /* Enable error interrupt */ + INT_ECC_EN = 0x01 << 14, /* Enable ECC interrupt */ + + INT_FIELD_STATUS = 0x01 << 16, /* field interrupt status */ + INT_VSYNC_STATUS = 0x01 << 17, /* vsync interrupt status */ + INT_HSYNC_STATUS = 0x01 << 18, /* hsync interrupt status */ + INT_VSTART_STATUS = 0x01 << 19, /* vstart interrupt status */ + INT_DMA_END_STATUS = 0x01 << 20, /* DMA end interrupt status */ + INT_ERROR_STATUS = 0x01 << 21, /* error interrupt status */ + + DMA_ACT = 0x01 << 27, /* Enable DMA transfer */ + FIELD_NO = 0x01 << 28, /* Field number */ + DITHER_ON = 0x01 << 29, /* Dithering is on */ + ROUND_ON = 0x01 << 30, /* Round is on */ + MODE_32BIT = 0x01 << 31, /* Data in RGBa888, + * 0 in RGB565 + */ +}; + +#define norm_maxw() 720 +#define norm_maxh() 576 + +#define INT_ALL_STATUS (INT_FIELD_STATUS | INT_VSYNC_STATUS | \ + INT_HSYNC_STATUS | INT_VSTART_STATUS | \ + INT_DMA_END_STATUS | INT_ERROR_STATUS) + +#define NUM_FORMATS ARRAY_SIZE(formats) + +static irqreturn_t viu_intr(int irq, void *dev_id); + +struct viu_fmt *format_by_fourcc(int fourcc) +{ + int i; + + for (i = 0; i < NUM_FORMATS; i++) { + if (formats[i].pixelformat == fourcc) + return formats + i; + } + + dprintk(0, "unknown pixelformat:'%4.4s'\n", (char *)&fourcc); + return NULL; +} + +void viu_start_dma(struct viu_dev *dev) +{ + struct viu_reg *vr = dev->vr; + + dev->field = 0; + + /* Enable DMA operation */ + out_be32(&vr->status_cfg, SOFT_RST); + out_be32(&vr->status_cfg, INT_FIELD_EN); +} + +void viu_stop_dma(struct viu_dev *dev) +{ + struct viu_reg *vr = dev->vr; + int cnt = 100; + u32 status_cfg; + + out_be32(&vr->status_cfg, 0); + + /* Clear pending interrupts */ + status_cfg = in_be32(&vr->status_cfg); + if (status_cfg & 0x3f0000) + out_be32(&vr->status_cfg, status_cfg & 0x3f0000); + + if (status_cfg & DMA_ACT) { + do { + status_cfg = in_be32(&vr->status_cfg); + if (status_cfg & INT_DMA_END_STATUS) + break; + } while (cnt--); + + if (cnt < 0) { + /* timed out, issue soft reset */ + out_be32(&vr->status_cfg, SOFT_RST); + out_be32(&vr->status_cfg, 0); + } else { + /* clear DMA_END and other pending irqs */ + out_be32(&vr->status_cfg, status_cfg & 0x3f0000); + } + } + + dev->field = 0; +} + +static int restart_video_queue(struct viu_dmaqueue *vidq) +{ + struct viu_buf *buf, *prev; + + dprintk(1, "%s vidq=0x%08lx\n", __func__, (unsigned long)vidq); + if (!list_empty(&vidq->active)) { + buf = list_entry(vidq->active.next, struct viu_buf, vb.queue); + dprintk(2, "restart_queue [%p/%d]: restart dma\n", + buf, buf->vb.i); + + viu_stop_dma(vidq->dev); + + /* cancel all outstanding capture requests */ + list_for_each_entry_safe(buf, prev, &vidq->active, vb.queue) { + list_del(&buf->vb.queue); + buf->vb.state = VIDEOBUF_ERROR; + wake_up(&buf->vb.done); + } + mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT); + return 0; + } + + prev = NULL; + for (;;) { + if (list_empty(&vidq->queued)) + return 0; + buf = list_entry(vidq->queued.next, struct viu_buf, vb.queue); + if (prev == NULL) { + list_del(&buf->vb.queue); + list_add_tail(&buf->vb.queue, &vidq->active); + + dprintk(1, "Restarting video dma\n"); + viu_stop_dma(vidq->dev); + viu_start_dma(vidq->dev); + + buf->vb.state = VIDEOBUF_ACTIVE; + mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT); + dprintk(2, "[%p/%d] restart_queue - first active\n", + buf, buf->vb.i); + + } else if (prev->vb.width == buf->vb.width && + prev->vb.height == buf->vb.height && + prev->fmt == buf->fmt) { + list_del(&buf->vb.queue); + list_add_tail(&buf->vb.queue, &vidq->active); + buf->vb.state = VIDEOBUF_ACTIVE; + dprintk(2, "[%p/%d] restart_queue - move to active\n", + buf, buf->vb.i); + } else { + return 0; + } + prev = buf; + } +} + +static void viu_vid_timeout(unsigned long data) +{ + struct viu_dev *dev = (struct viu_dev *)data; + struct viu_buf *buf; + struct viu_dmaqueue *vidq = &dev->vidq; + + while (!list_empty(&vidq->active)) { + buf = list_entry(vidq->active.next, struct viu_buf, vb.queue); + list_del(&buf->vb.queue); + buf->vb.state = VIDEOBUF_ERROR; + wake_up(&buf->vb.done); + dprintk(1, "viu/0: [%p/%d] timeout\n", buf, buf->vb.i); + } + + restart_video_queue(vidq); +} + +/* + * Videobuf operations + */ +static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct viu_fh *fh = vq->priv_data; + + *size = fh->width * fh->height * fh->fmt->depth >> 3; + if (*count == 0) + *count = 32; + + while (*size * *count > VIU_VID_MEM_LIMIT * 1024 * 1024) + (*count)--; + + dprintk(1, "%s, count=%d, size=%d\n", __func__, *count, *size); + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct viu_buf *buf) +{ + struct videobuf_buffer *vb = &buf->vb; + void *vaddr = NULL; + + BUG_ON(in_interrupt()); + + videobuf_waiton(&buf->vb, 0, 0); + + if (vq->int_ops && vq->int_ops->vaddr) + vaddr = vq->int_ops->vaddr(vb); + + if (vaddr) + videobuf_dma_contig_free(vq, &buf->vb); + + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +inline int buffer_activate(struct viu_dev *dev, struct viu_buf *buf) +{ + struct viu_reg *vr = dev->vr; + int bpp; + + /* setup the DMA base address */ + reg_val.field_base_addr = videobuf_to_dma_contig(&buf->vb); + + dprintk(1, "buffer_activate [%p/%d]: dma addr 0x%lx\n", + buf, buf->vb.i, (unsigned long)reg_val.field_base_addr); + + /* interlace is on by default, set horizontal DMA increment */ + reg_val.status_cfg = 0; + bpp = buf->fmt->depth >> 3; + switch (bpp) { + case 2: + reg_val.status_cfg &= ~MODE_32BIT; + reg_val.dma_inc = buf->vb.width * 2; + break; + case 4: + reg_val.status_cfg |= MODE_32BIT; + reg_val.dma_inc = buf->vb.width * 4; + break; + default: + dprintk(0, "doesn't support color depth(%d)\n", + bpp * 8); + return -EINVAL; + } + + /* setup picture_count register */ + reg_val.picture_count = (buf->vb.height / 2) << 16 | + buf->vb.width; + + reg_val.status_cfg |= DMA_ACT | INT_DMA_END_EN | INT_FIELD_EN; + + buf->vb.state = VIDEOBUF_ACTIVE; + dev->capfield = buf->vb.field; + + /* reset dma increment if needed */ + if (!V4L2_FIELD_HAS_BOTH(buf->vb.field)) + reg_val.dma_inc = 0; + + out_be32(&vr->dma_inc, reg_val.dma_inc); + out_be32(&vr->picture_count, reg_val.picture_count); + out_be32(&vr->field_base_addr, reg_val.field_base_addr); + mod_timer(&dev->vidq.timeout, jiffies + BUFFER_TIMEOUT); + return 0; +} + +static int buffer_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct viu_fh *fh = vq->priv_data; + struct viu_buf *buf = container_of(vb, struct viu_buf, vb); + int rc; + + BUG_ON(fh->fmt == NULL); + + if (fh->width < 48 || fh->width > norm_maxw() || + fh->height < 32 || fh->height > norm_maxh()) + return -EINVAL; + buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3; + if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + if (buf->fmt != fh->fmt || + buf->vb.width != fh->width || + buf->vb.height != fh->height || + buf->vb.field != field) { + buf->fmt = fh->fmt; + buf->vb.width = fh->width; + buf->vb.height = fh->height; + buf->vb.field = field; + } + + if (buf->vb.state == VIDEOBUF_NEEDS_INIT) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc != 0) + goto fail; + + buf->vb.width = fh->width; + buf->vb.height = fh->height; + buf->vb.field = field; + buf->fmt = fh->fmt; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct viu_buf *buf = container_of(vb, struct viu_buf, vb); + struct viu_fh *fh = vq->priv_data; + struct viu_dev *dev = fh->dev; + struct viu_dmaqueue *vidq = &dev->vidq; + struct viu_buf *prev; + + if (!list_empty(&vidq->queued)) { + dprintk(1, "adding vb queue=0x%08lx\n", + (unsigned long)&buf->vb.queue); + dprintk(1, "vidq pointer 0x%p, queued 0x%p\n", + vidq, &vidq->queued); + dprintk(1, "dev %p, queued: self %p, next %p, head %p\n", + dev, &vidq->queued, vidq->queued.next, + vidq->queued.prev); + list_add_tail(&buf->vb.queue, &vidq->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - append to queued\n", + buf, buf->vb.i); + } else if (list_empty(&vidq->active)) { + dprintk(1, "adding vb active=0x%08lx\n", + (unsigned long)&buf->vb.queue); + list_add_tail(&buf->vb.queue, &vidq->active); + buf->vb.state = VIDEOBUF_ACTIVE; + mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT); + dprintk(2, "[%p/%d] buffer_queue - first active\n", + buf, buf->vb.i); + + buffer_activate(dev, buf); + } else { + dprintk(1, "adding vb queue2=0x%08lx\n", + (unsigned long)&buf->vb.queue); + prev = list_entry(vidq->active.prev, struct viu_buf, vb.queue); + if (prev->vb.width == buf->vb.width && + prev->vb.height == buf->vb.height && + prev->fmt == buf->fmt) { + list_add_tail(&buf->vb.queue, &vidq->active); + buf->vb.state = VIDEOBUF_ACTIVE; + dprintk(2, "[%p/%d] buffer_queue - append to active\n", + buf, buf->vb.i); + } else { + list_add_tail(&buf->vb.queue, &vidq->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - first queued\n", + buf, buf->vb.i); + } + } +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct viu_buf *buf = container_of(vb, struct viu_buf, vb); + struct viu_fh *fh = vq->priv_data; + struct viu_dev *dev = (struct viu_dev *)fh->dev; + + viu_stop_dma(dev); + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops viu_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/* + * IOCTL vidioc handling + */ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strcpy(cap->driver, "viu"); + strcpy(cap->card, "viu"); + cap->version = VIU_VERSION; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_OVERLAY | + V4L2_CAP_READWRITE; + return 0; +} + +static int vidioc_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + int index = f->index; + + if (f->index > NUM_FORMATS) + return -EINVAL; + + strlcpy(f->description, formats[index].name, sizeof(f->description)); + f->pixelformat = formats[index].fourcc; + return 0; +} + +static int vidioc_g_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct viu_fh *fh = priv; + + f->fmt.pix.width = fh->width; + f->fmt.pix.height = fh->height; + f->fmt.pix.field = fh->vb_vidq.field; + f->fmt.pix.pixelformat = fh->fmt->pixelformat; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fh->fmt->depth) >> 3; + f->fmt.pix.sizeimage = fh->sizeimage; + return 0; +} + +static int vidioc_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct viu_fmt *fmt; + enum v4l2_field field; + unsigned int maxw, maxh; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (!fmt) { + dprintk(1, "Fourcc format (0x%08x) invalid.", + f->fmt.pix.pixelformat); + return -EINVAL; + } + + field = f->fmt.pix.field; + + if (field == V4L2_FIELD_ANY) { + field = V4L2_FIELD_INTERLACED; + } else if (field != V4L2_FIELD_INTERLACED) { + dprintk(1, "Field type invalid.\n"); + return -EINVAL; + } + + maxw = norm_maxw(); + maxh = norm_maxh(); + + f->fmt.pix.field = field; + if (f->fmt.pix.height < 32) + f->fmt.pix.height = 32; + if (f->fmt.pix.height > maxh) + f->fmt.pix.height = maxh; + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.width > maxw) + f->fmt.pix.width = maxw; + f->fmt.pix.width &= ~0x03; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fmt->depth) >> 3; + + return 0; +} + +static int vidioc_s_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct viu_fh *fh = priv; + int ret; + + ret = vidioc_try_fmt_cap(file, fh, f); + if (ret < 0) + return ret; + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + fh->sizeimage = f->fmt.pix.sizeimage; + fh->vb_vidq.field = f->fmt.pix.field; + fh->type = f->type; + dprintk(1, "set to pixelformat '%4.6s'\n", (char *)&fh->fmt->name); + return 0; +} + +static int vidioc_g_fmt_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct viu_fh *fh = priv; + + f->fmt.win = fh->win; + return 0; +} + +static int verify_preview(struct viu_dev *dev, struct v4l2_window *win) +{ + enum v4l2_field field; + int maxw, maxh; + + if (dev->ovbuf.base == NULL) + return -EINVAL; + if (dev->ovfmt == NULL) + return -EINVAL; + if (win->w.width < 48 || win->w.height < 32) + return -EINVAL; + + field = win->field; + maxw = dev->crop_current.width; + maxh = dev->crop_current.height; + + if (field == V4L2_FIELD_ANY) { + field = (win->w.height > maxh/2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_TOP; + } + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + maxh = maxh / 2; + break; + case V4L2_FIELD_INTERLACED: + break; + default: + return -EINVAL; + } + + win->field = field; + if (win->w.width > maxw) + win->w.width = maxw; + if (win->w.height > maxh) + win->w.height = maxh; + return 0; +} + +inline void viu_activate_overlay(struct viu_reg *viu_reg) +{ + struct viu_reg *vr = viu_reg; + + out_be32(&vr->field_base_addr, reg_val.field_base_addr); + out_be32(&vr->dma_inc, reg_val.dma_inc); + out_be32(&vr->picture_count, reg_val.picture_count); +} + +static int viu_start_preview(struct viu_dev *dev, struct viu_fh *fh) +{ + int bpp; + + dprintk(1, "%s %dx%d %s\n", __func__, + fh->win.w.width, fh->win.w.height, dev->ovfmt->name); + + reg_val.status_cfg = 0; + + /* setup window */ + reg_val.picture_count = (fh->win.w.height / 2) << 16 | + fh->win.w.width; + + /* setup color depth and dma increment */ + bpp = dev->ovfmt->depth / 8; + switch (bpp) { + case 2: + reg_val.status_cfg &= ~MODE_32BIT; + reg_val.dma_inc = fh->win.w.width * 2; + break; + case 4: + reg_val.status_cfg |= MODE_32BIT; + reg_val.dma_inc = fh->win.w.width * 4; + break; + default: + dprintk(0, "device doesn't support color depth(%d)\n", + bpp * 8); + return -EINVAL; + } + + dev->ovfield = fh->win.field; + if (!V4L2_FIELD_HAS_BOTH(dev->ovfield)) + reg_val.dma_inc = 0; + + reg_val.status_cfg |= DMA_ACT | INT_DMA_END_EN | INT_FIELD_EN; + + /* setup the base address of the overlay buffer */ + reg_val.field_base_addr = (u32)dev->ovbuf.base; + + dev->ovenable = 1; + viu_activate_overlay(dev->vr); + + /* start dma */ + viu_start_dma(dev); + return 0; +} + +static int vidioc_s_fmt_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct viu_fh *fh = priv; + struct viu_dev *dev = (struct viu_dev *)fh->dev; + unsigned long flags; + int err; + + err = verify_preview(dev, &f->fmt.win); + if (err) + return err; + + mutex_lock(&dev->lock); + fh->win = f->fmt.win; + + spin_lock_irqsave(&dev->slock, flags); + viu_start_preview(dev, fh); + spin_unlock_irqrestore(&dev->slock, flags); + mutex_unlock(&dev->lock); + return 0; +} + +static int vidioc_try_fmt_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + return 0; +} + +int vidioc_g_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg) +{ + struct viu_fh *fh = priv; + struct viu_dev *dev = fh->dev; + struct v4l2_framebuffer *fb = arg; + + *fb = dev->ovbuf; + fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; + return 0; +} + +int vidioc_s_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg) +{ + struct viu_fh *fh = priv; + struct viu_dev *dev = fh->dev; + struct v4l2_framebuffer *fb = arg; + struct viu_fmt *fmt; + + if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) + return -EPERM; + + /* check args */ + fmt = format_by_fourcc(fb->fmt.pixelformat); + if (fmt == NULL) + return -EINVAL; + + /* ok, accept it */ + dev->ovbuf = *fb; + dev->ovfmt = fmt; + if (dev->ovbuf.fmt.bytesperline == 0) { + dev->ovbuf.fmt.bytesperline = + dev->ovbuf.fmt.width * fmt->depth / 8; + } + return 0; +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct viu_fh *fh = priv; + + return videobuf_reqbufs(&fh->vb_vidq, p); +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct viu_fh *fh = priv; + + return videobuf_querybuf(&fh->vb_vidq, p); +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct viu_fh *fh = priv; + + return videobuf_qbuf(&fh->vb_vidq, p); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct viu_fh *fh = priv; + + return videobuf_dqbuf(&fh->vb_vidq, p, + file->f_flags & O_NONBLOCK); +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct viu_fh *fh = priv; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (fh->type != i) + return -EINVAL; + + return videobuf_streamon(&fh->vb_vidq); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct viu_fh *fh = priv; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (fh->type != i) + return -EINVAL; + + return videobuf_streamoff(&fh->vb_vidq); +} + +#define decoder_call(viu, o, f, args...) \ + v4l2_subdev_call(viu->decoder, o, f, ##args) + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct viu_fh *fh = priv; + + decoder_call(fh->dev, core, s_std, *id); + return 0; +} + +/* only one input in this driver */ +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct viu_fh *fh = priv; + + if (inp->index != 0) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = fh->dev->vdev->tvnorms; + strcpy(inp->name, "Camera"); + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + struct viu_fh *fh = priv; + + if (i > 1) + return -EINVAL; + + decoder_call(fh->dev, video, s_routing, i, 0, 0); + return 0; +} + +/* Controls */ +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) { + if (qc->id && qc->id == viu_qctrl[i].id) { + memcpy(qc, &(viu_qctrl[i]), sizeof(*qc)); + return 0; + } + } + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) { + if (ctrl->id == viu_qctrl[i].id) { + ctrl->value = qctl_regs[i]; + return 0; + } + } + return -EINVAL; +} +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) { + if (ctrl->id == viu_qctrl[i].id) { + if (ctrl->value < viu_qctrl[i].minimum + || ctrl->value > viu_qctrl[i].maximum) + return -ERANGE; + qctl_regs[i] = ctrl->value; + return 0; + } + } + return -EINVAL; +} + +inline void viu_activate_next_buf(struct viu_dev *dev, + struct viu_dmaqueue *viuq) +{ + struct viu_dmaqueue *vidq = viuq; + struct viu_buf *buf; + + /* launch another DMA operation for an active/queued buffer */ + if (!list_empty(&vidq->active)) { + buf = list_entry(vidq->active.next, struct viu_buf, + vb.queue); + dprintk(1, "start another queued buffer: 0x%p\n", buf); + buffer_activate(dev, buf); + } else if (!list_empty(&vidq->queued)) { + buf = list_entry(vidq->queued.next, struct viu_buf, + vb.queue); + list_del(&buf->vb.queue); + + dprintk(1, "start another queued buffer: 0x%p\n", buf); + list_add_tail(&buf->vb.queue, &vidq->active); + buf->vb.state = VIDEOBUF_ACTIVE; + buffer_activate(dev, buf); + } +} + +inline void viu_default_settings(struct viu_reg *viu_reg) +{ + struct viu_reg *vr = viu_reg; + + out_be32(&vr->luminance, 0x9512A254); + out_be32(&vr->chroma_r, 0x03310000); + out_be32(&vr->chroma_g, 0x06600F38); + out_be32(&vr->chroma_b, 0x00000409); + out_be32(&vr->alpha, 0x000000ff); + out_be32(&vr->req_alarm, 0x00000090); + dprintk(1, "status reg: 0x%08x, field base: 0x%08x\n", + in_be32(&vr->status_cfg), in_be32(&vr->field_base_addr)); +} + +static void viu_overlay_intr(struct viu_dev *dev, u32 status) +{ + struct viu_reg *vr = dev->vr; + + if (status & INT_DMA_END_STATUS) + dev->dma_done = 1; + + if (status & INT_FIELD_STATUS) { + if (dev->dma_done) { + u32 addr = reg_val.field_base_addr; + + dev->dma_done = 0; + if (status & FIELD_NO) + addr += reg_val.dma_inc; + + out_be32(&vr->field_base_addr, addr); + out_be32(&vr->dma_inc, reg_val.dma_inc); + out_be32(&vr->status_cfg, + (status & 0xffc0ffff) | + (status & INT_ALL_STATUS) | + reg_val.status_cfg); + } else if (status & INT_VSYNC_STATUS) { + out_be32(&vr->status_cfg, + (status & 0xffc0ffff) | + (status & INT_ALL_STATUS) | + reg_val.status_cfg); + } + } +} + +static void viu_capture_intr(struct viu_dev *dev, u32 status) +{ + struct viu_dmaqueue *vidq = &dev->vidq; + struct viu_reg *vr = dev->vr; + struct viu_buf *buf; + int field_num; + int need_two; + int dma_done = 0; + + field_num = status & FIELD_NO; + need_two = V4L2_FIELD_HAS_BOTH(dev->capfield); + + if (status & INT_DMA_END_STATUS) { + dma_done = 1; + if (((field_num == 0) && (dev->field == 0)) || + (field_num && (dev->field == 1))) + dev->field++; + } + + if (status & INT_FIELD_STATUS) { + dprintk(1, "irq: field %d, done %d\n", + !!field_num, dma_done); + if (unlikely(dev->first)) { + if (field_num == 0) { + dev->first = 0; + dprintk(1, "activate first buf\n"); + viu_activate_next_buf(dev, vidq); + } else + dprintk(1, "wait field 0\n"); + return; + } + + /* setup buffer address for next dma operation */ + if (!list_empty(&vidq->active)) { + u32 addr = reg_val.field_base_addr; + + if (field_num && need_two) { + addr += reg_val.dma_inc; + dprintk(1, "field 1, 0x%lx, dev field %d\n", + (unsigned long)addr, dev->field); + } + out_be32(&vr->field_base_addr, addr); + out_be32(&vr->dma_inc, reg_val.dma_inc); + out_be32(&vr->status_cfg, + (status & 0xffc0ffff) | + (status & INT_ALL_STATUS) | + reg_val.status_cfg); + return; + } + } + + if (dma_done && field_num && (dev->field == 2)) { + dev->field = 0; + buf = list_entry(vidq->active.next, + struct viu_buf, vb.queue); + dprintk(1, "viu/0: [%p/%d] 0x%lx/0x%lx: dma complete\n", + buf, buf->vb.i, + (unsigned long)videobuf_to_dma_contig(&buf->vb), + (unsigned long)in_be32(&vr->field_base_addr)); + + if (waitqueue_active(&buf->vb.done)) { + list_del(&buf->vb.queue); + do_gettimeofday(&buf->vb.ts); + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + wake_up(&buf->vb.done); + } + /* activate next dma buffer */ + viu_activate_next_buf(dev, vidq); + } +} + +static irqreturn_t viu_intr(int irq, void *dev_id) +{ + struct viu_dev *dev = (struct viu_dev *)dev_id; + struct viu_reg *vr = dev->vr; + u32 status; + u32 error; + + status = in_be32(&vr->status_cfg); + + if (status & INT_ERROR_STATUS) { + dev->irqs.error_irq++; + error = status & ERR_MASK; + if (error) + dprintk(1, "Err: error(%d), times:%d!\n", + error >> 4, dev->irqs.error_irq); + /* Clear interrupt error bit and error flags */ + out_be32(&vr->status_cfg, + (status & 0xffc0ffff) | INT_ERROR_STATUS); + } + + if (status & INT_DMA_END_STATUS) { + dev->irqs.dma_end_irq++; + dev->dma_done = 1; + dprintk(2, "VIU DMA end interrupt times: %d\n", + dev->irqs.dma_end_irq); + } + + if (status & INT_HSYNC_STATUS) + dev->irqs.hsync_irq++; + + if (status & INT_FIELD_STATUS) { + dev->irqs.field_irq++; + dprintk(2, "VIU field interrupt times: %d\n", + dev->irqs.field_irq); + } + + if (status & INT_VSTART_STATUS) + dev->irqs.vstart_irq++; + + if (status & INT_VSYNC_STATUS) { + dev->irqs.vsync_irq++; + dprintk(2, "VIU vsync interrupt times: %d\n", + dev->irqs.vsync_irq); + } + + /* clear all pending irqs */ + status = in_be32(&vr->status_cfg); + out_be32(&vr->status_cfg, + (status & 0xffc0ffff) | (status & INT_ALL_STATUS)); + + if (dev->ovenable) { + viu_overlay_intr(dev, status); + return IRQ_HANDLED; + } + + /* Capture mode */ + viu_capture_intr(dev, status); + return IRQ_HANDLED; +} + +/* + * File operations for the device + */ +static int viu_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct viu_dev *dev = video_get_drvdata(vdev); + struct viu_fh *fh; + struct viu_reg *vr; + int minor = vdev->minor; + u32 status_cfg; + int i; + + dprintk(1, "viu: open (minor=%d)\n", minor); + + dev->users++; + if (dev->users > 1) { + dev->users--; + return -EBUSY; + } + + vr = dev->vr; + + dprintk(1, "open minor=%d type=%s users=%d\n", minor, + v4l2_type_names[V4L2_BUF_TYPE_VIDEO_CAPTURE], dev->users); + + /* allocate and initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (!fh) { + dev->users--; + return -ENOMEM; + } + + file->private_data = fh; + fh->dev = dev; + + fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fh->fmt = format_by_fourcc(V4L2_PIX_FMT_RGB32); + fh->width = norm_maxw(); + fh->height = norm_maxh(); + dev->crop_current.width = fh->width; + dev->crop_current.height = fh->height; + + /* Put all controls at a sane state */ + for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) + qctl_regs[i] = viu_qctrl[i].default_value; + + dprintk(1, "Open: fh=0x%08lx, dev=0x%08lx, dev->vidq=0x%08lx\n", + (unsigned long)fh, (unsigned long)dev, + (unsigned long)&dev->vidq); + dprintk(1, "Open: list_empty queued=%d\n", + list_empty(&dev->vidq.queued)); + dprintk(1, "Open: list_empty active=%d\n", + list_empty(&dev->vidq.active)); + + viu_default_settings(vr); + + status_cfg = in_be32(&vr->status_cfg); + out_be32(&vr->status_cfg, + status_cfg & ~(INT_VSYNC_EN | INT_HSYNC_EN | + INT_FIELD_EN | INT_VSTART_EN | + INT_DMA_END_EN | INT_ERROR_EN | INT_ECC_EN)); + + status_cfg = in_be32(&vr->status_cfg); + out_be32(&vr->status_cfg, status_cfg | INT_ALL_STATUS); + + spin_lock_init(&fh->vbq_lock); + videobuf_queue_dma_contig_init(&fh->vb_vidq, &viu_video_qops, + dev->dev, &fh->vbq_lock, + fh->type, V4L2_FIELD_INTERLACED, + sizeof(struct viu_buf), fh); + return 0; +} + +static ssize_t viu_read(struct file *file, char __user *data, size_t count, + loff_t *ppos) +{ + struct viu_fh *fh = file->private_data; + struct viu_dev *dev = fh->dev; + int ret = 0; + + dprintk(2, "%s\n", __func__); + if (dev->ovenable) + dev->ovenable = 0; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + viu_start_dma(dev); + ret = videobuf_read_stream(&fh->vb_vidq, data, count, + ppos, 0, file->f_flags & O_NONBLOCK); + return ret; + } + return 0; +} + +static unsigned int viu_poll(struct file *file, struct poll_table_struct *wait) +{ + struct viu_fh *fh = file->private_data; + struct videobuf_queue *q = &fh->vb_vidq; + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + return POLLERR; + + return videobuf_poll_stream(file, q, wait); +} + +static int viu_release(struct file *file) +{ + struct viu_fh *fh = file->private_data; + struct viu_dev *dev = fh->dev; + int minor = video_devdata(file)->minor; + + viu_stop_dma(dev); + videobuf_stop(&fh->vb_vidq); + + kfree(fh); + + dev->users--; + dprintk(1, "close (minor=%d, users=%d)\n", + minor, dev->users); + return 0; +} + +void viu_reset(struct viu_reg *reg) +{ + out_be32(®->status_cfg, 0); + out_be32(®->luminance, 0x9512a254); + out_be32(®->chroma_r, 0x03310000); + out_be32(®->chroma_g, 0x06600f38); + out_be32(®->chroma_b, 0x00000409); + out_be32(®->field_base_addr, 0); + out_be32(®->dma_inc, 0); + out_be32(®->picture_count, 0x01e002d0); + out_be32(®->req_alarm, 0x00000090); + out_be32(®->alpha, 0x000000ff); +} + +static int viu_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct viu_fh *fh = file->private_data; + int ret; + + dprintk(1, "mmap called, vma=0x%08lx\n", (unsigned long)vma); + + ret = videobuf_mmap_mapper(&fh->vb_vidq, vma); + + dprintk(1, "vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end-(unsigned long)vma->vm_start, + ret); + + return ret; +} + +static struct v4l2_file_operations viu_fops = { + .owner = THIS_MODULE, + .open = viu_open, + .release = viu_release, + .read = viu_read, + .poll = viu_poll, + .ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .mmap = viu_mmap, +}; + +static const struct v4l2_ioctl_ops viu_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_cap, + .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt, + .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_overlay, + .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_overlay, + .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_overlay, + .vidioc_g_fbuf = vidioc_g_fbuf, + .vidioc_s_fbuf = vidioc_s_fbuf, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, +}; + +static struct video_device viu_template = { + .name = "FSL viu", + .fops = &viu_fops, + .minor = -1, + .ioctl_ops = &viu_ioctl_ops, + .release = video_device_release, + + .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL, + .current_norm = V4L2_STD_NTSC_M, +}; + +static int __devinit viu_of_probe(struct of_device *op, + const struct of_device_id *match) +{ + struct viu_dev *viu_dev; + struct video_device *vdev; + struct resource r; + struct viu_reg __iomem *viu_regs; + struct i2c_adapter *ad; + int ret, viu_irq; + + ret = of_address_to_resource(op->dev.of_node, 0, &r); + if (ret) { + dev_err(&op->dev, "Can't parse device node resource\n"); + return -ENODEV; + } + + viu_irq = irq_of_parse_and_map(op->dev.of_node, 0); + if (viu_irq == NO_IRQ) { + dev_err(&op->dev, "Error while mapping the irq\n"); + return -EINVAL; + } + + /* request mem region */ + if (!devm_request_mem_region(&op->dev, r.start, + sizeof(struct viu_reg), DRV_NAME)) { + dev_err(&op->dev, "Error while requesting mem region\n"); + ret = -EBUSY; + goto err; + } + + /* remap registers */ + viu_regs = devm_ioremap(&op->dev, r.start, sizeof(struct viu_reg)); + if (!viu_regs) { + dev_err(&op->dev, "Can't map register set\n"); + ret = -ENOMEM; + goto err; + } + + /* Prepare our private structure */ + viu_dev = devm_kzalloc(&op->dev, sizeof(struct viu_dev), GFP_ATOMIC); + if (!viu_dev) { + dev_err(&op->dev, "Can't allocate private structure\n"); + ret = -ENOMEM; + goto err; + } + + viu_dev->vr = viu_regs; + viu_dev->irq = viu_irq; + viu_dev->dev = &op->dev; + + /* init video dma queues */ + INIT_LIST_HEAD(&viu_dev->vidq.active); + INIT_LIST_HEAD(&viu_dev->vidq.queued); + + /* initialize locks */ + mutex_init(&viu_dev->lock); + + snprintf(viu_dev->v4l2_dev.name, + sizeof(viu_dev->v4l2_dev.name), "%s", "VIU"); + ret = v4l2_device_register(viu_dev->dev, &viu_dev->v4l2_dev); + if (ret < 0) { + dev_err(&op->dev, "v4l2_device_register() failed: %d\n", ret); + goto err; + } + + ad = i2c_get_adapter(0); + viu_dev->decoder = v4l2_i2c_new_subdev(&viu_dev->v4l2_dev, ad, + "saa7115", "saa7113", VIU_VIDEO_DECODER_ADDR, NULL); + + viu_dev->vidq.timeout.function = viu_vid_timeout; + viu_dev->vidq.timeout.data = (unsigned long)viu_dev; + init_timer(&viu_dev->vidq.timeout); + viu_dev->first = 1; + + /* Allocate memory for video device */ + vdev = video_device_alloc(); + if (vdev == NULL) { + ret = -ENOMEM; + goto err_vdev; + } + + memcpy(vdev, &viu_template, sizeof(viu_template)); + + vdev->v4l2_dev = &viu_dev->v4l2_dev; + + viu_dev->vdev = vdev; + + video_set_drvdata(viu_dev->vdev, viu_dev); + + ret = video_register_device(viu_dev->vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + video_device_release(viu_dev->vdev); + goto err_vdev; + } + + /* enable VIU clock */ + viu_dev->clk = clk_get(&op->dev, "viu_clk"); + if (IS_ERR(viu_dev->clk)) { + dev_err(&op->dev, "failed to find the clock module!\n"); + ret = -ENODEV; + goto err_clk; + } else { + clk_enable(viu_dev->clk); + } + + /* reset VIU module */ + viu_reset(viu_dev->vr); + + /* install interrupt handler */ + if (request_irq(viu_dev->irq, viu_intr, 0, "viu", (void *)viu_dev)) { + dev_err(&op->dev, "Request VIU IRQ failed.\n"); + ret = -ENODEV; + goto err_irq; + } + + dev_info(&op->dev, "Freescale VIU Video Capture Board\n"); + return ret; + +err_irq: + clk_disable(viu_dev->clk); + clk_put(viu_dev->clk); +err_clk: + video_unregister_device(viu_dev->vdev); +err_vdev: + i2c_put_adapter(ad); + v4l2_device_unregister(&viu_dev->v4l2_dev); +err: + irq_dispose_mapping(viu_irq); + return ret; +} + +static int __devexit viu_of_remove(struct of_device *op) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev); + struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev); + struct v4l2_subdev *sdev = list_entry(v4l2_dev->subdevs.next, + struct v4l2_subdev, list); + struct i2c_client *client = v4l2_get_subdevdata(sdev); + + free_irq(dev->irq, (void *)dev); + irq_dispose_mapping(dev->irq); + + clk_disable(dev->clk); + clk_put(dev->clk); + + video_unregister_device(dev->vdev); + i2c_put_adapter(client->adapter); + v4l2_device_unregister(&dev->v4l2_dev); + return 0; +} + +#ifdef CONFIG_PM +static int viu_suspend(struct of_device *op, pm_message_t state) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev); + struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev); + + clk_disable(dev->clk); + return 0; +} + +static int viu_resume(struct of_device *op) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev); + struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev); + + clk_enable(dev->clk); + return 0; +} +#endif + +/* + * Initialization and module stuff + */ +static struct of_device_id mpc512x_viu_of_match[] = { + { + .compatible = "fsl,mpc5121-viu", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, mpc512x_viu_of_match); + +static struct of_platform_driver viu_of_platform_driver = { + .probe = viu_of_probe, + .remove = __devexit_p(viu_of_remove), +#ifdef CONFIG_PM + .suspend = viu_suspend, + .resume = viu_resume, +#endif + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = mpc512x_viu_of_match, + }, +}; + +static int __init viu_init(void) +{ + return of_register_platform_driver(&viu_of_platform_driver); +} + +static void __exit viu_exit(void) +{ + of_unregister_platform_driver(&viu_of_platform_driver); +} + +module_init(viu_init); +module_exit(viu_exit); + +MODULE_DESCRIPTION("Freescale Video-In(VIU)"); +MODULE_AUTHOR("Hongjun Chen"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index 5d920e5..23db0c2 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -246,6 +246,15 @@ config USB_GSPCA_SPCA561 To compile this driver as a module, choose M here: the module will be called gspca_spca561. +config USB_GSPCA_SPCA1528 + tristate "SPCA1528 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SPCA1528 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_spca1528. + config USB_GSPCA_SQ905 tristate "SQ Technologies SQ905 based USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA @@ -264,6 +273,15 @@ config USB_GSPCA_SQ905C To compile this driver as a module, choose M here: the module will be called gspca_sq905c. +config USB_GSPCA_SQ930X + tristate "SQ Technologies SQ930X based USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SQ930X chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_sq930x. + config USB_GSPCA_STK014 tristate "Syntek DV4000 (STK014) USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index 6e4cf1c..f6616db 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -23,8 +23,10 @@ obj-$(CONFIG_USB_GSPCA_SPCA505) += gspca_spca505.o obj-$(CONFIG_USB_GSPCA_SPCA506) += gspca_spca506.o obj-$(CONFIG_USB_GSPCA_SPCA508) += gspca_spca508.o obj-$(CONFIG_USB_GSPCA_SPCA561) += gspca_spca561.o +obj-$(CONFIG_USB_GSPCA_SPCA1528) += gspca_spca1528.o obj-$(CONFIG_USB_GSPCA_SQ905) += gspca_sq905.o obj-$(CONFIG_USB_GSPCA_SQ905C) += gspca_sq905c.o +obj-$(CONFIG_USB_GSPCA_SQ930X) += gspca_sq930x.o obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o @@ -58,8 +60,10 @@ gspca_spca505-objs := spca505.o gspca_spca506-objs := spca506.o gspca_spca508-objs := spca508.o gspca_spca561-objs := spca561.o +gspca_spca1528-objs := spca1528.o gspca_sq905-objs := sq905.o gspca_sq905c-objs := sq905c.o +gspca_sq930x-objs := sq930x.o gspca_stk014-objs := stk014.o gspca_stv0680-objs := stv0680.o gspca_sunplus-objs := sunplus.o diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c index 19fe6b2..d6a7577 100644 --- a/drivers/media/video/gspca/conex.c +++ b/drivers/media/video/gspca/conex.c @@ -41,7 +41,7 @@ struct sd { #define QUALITY_MAX 60 #define QUALITY_DEF 40 - u8 *jpeg_hdr; + u8 jpeg_hdr[JPEG_HDR_SZ]; }; /* V4L2 controls supported by the driver */ @@ -845,9 +845,6 @@ static int sd_start(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; /* create the JPEG header */ - sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); - if (!sd->jpeg_hdr) - return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x22); /* JPEG 411 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); @@ -862,11 +859,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* called on streamoff with alt 0 and on disconnect */ static void sd_stop0(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; int retry = 50; - kfree(sd->jpeg_hdr); - if (!gspca_dev->present) return; reg_w_val(gspca_dev, 0x0000, 0x00); diff --git a/drivers/media/video/gspca/cpia1.c b/drivers/media/video/gspca/cpia1.c index 58b696f..3747a1d 100644 --- a/drivers/media/video/gspca/cpia1.c +++ b/drivers/media/video/gspca/cpia1.c @@ -1760,22 +1760,19 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data[25] == sd->params.roi.colEnd && data[26] == sd->params.roi.rowStart && data[27] == sd->params.roi.rowEnd) { - struct gspca_frame *frame = gspca_get_i_frame(gspca_dev); + u8 *image; atomic_set(&sd->cam_exposure, data[39] * 2); atomic_set(&sd->fps, data[41]); - if (frame == NULL) { - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - /* Check for proper EOF for last frame */ - if ((frame->data_end - frame->data) > 4 && - frame->data_end[-4] == 0xff && - frame->data_end[-3] == 0xff && - frame->data_end[-2] == 0xff && - frame->data_end[-1] == 0xff) + image = gspca_dev->image; + if (image != NULL && + gspca_dev->image_len > 4 && + image[gspca_dev->image_len - 4] == 0xff && + image[gspca_dev->image_len - 3] == 0xff && + image[gspca_dev->image_len - 2] == 0xff && + image[gspca_dev->image_len - 1] == 0xff) gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); diff --git a/drivers/media/video/gspca/gl860/gl860-mi2020.c b/drivers/media/video/gspca/gl860/gl860-mi2020.c index 7c31b4f..57782e0 100644 --- a/drivers/media/video/gspca/gl860/gl860-mi2020.c +++ b/drivers/media/video/gspca/gl860/gl860-mi2020.c @@ -1,6 +1,7 @@ /* Subdriver for the GL860 chip with the MI2020 sensor - * Author Olivier LORIN, from Ice/Soro2005's logs(A), Fret_saw/Hulkie's - * logs(B) and Tricid"s logs(C). With the help of Kytrix/BUGabundo/Blazercist. + * Author Olivier LORIN, from logs by Iceman/Soro2005 + Fret_saw/Hulkie/Tricid + * with the help of Kytrix/BUGabundo/Blazercist. + * Driver achieved thanks to a webcam gift by Kytrix. * * 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 @@ -20,47 +21,70 @@ #include "gl860.h" +static u8 dat_wbal1[] = {0x8c, 0xa2, 0x0c}; + static u8 dat_bright1[] = {0x8c, 0xa2, 0x06}; static u8 dat_bright3[] = {0x8c, 0xa1, 0x02}; static u8 dat_bright4[] = {0x90, 0x00, 0x0f}; static u8 dat_bright5[] = {0x8c, 0xa1, 0x03}; static u8 dat_bright6[] = {0x90, 0x00, 0x05}; -static u8 dat_dummy1[] = {0x90, 0x00, 0x06}; -/*static u8 dummy2[] = {0x8c, 0xa1, 0x02};*/ -/*static u8 dummy3[] = {0x90, 0x00, 0x1f};*/ - static u8 dat_hvflip1[] = {0x8c, 0x27, 0x19}; static u8 dat_hvflip3[] = {0x8c, 0x27, 0x3b}; static u8 dat_hvflip5[] = {0x8c, 0xa1, 0x03}; static u8 dat_hvflip6[] = {0x90, 0x00, 0x06}; +static struct idxdata tbl_middle_hvflip_low[] = { + {0x33, "\x90\x00\x06"}, + {6, "\xff\xff\xff"}, + {0x33, "\x90\x00\x06"}, + {6, "\xff\xff\xff"}, + {0x33, "\x90\x00\x06"}, + {6, "\xff\xff\xff"}, + {0x33, "\x90\x00\x06"}, + {6, "\xff\xff\xff"}, +}; + +static struct idxdata tbl_middle_hvflip_big[] = { + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa1\x20"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"}, + {102, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa1\x20"}, + {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, +}; + +static struct idxdata tbl_end_hvflip[] = { + {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, + {6, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, + {6, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, + {6, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, +}; + static u8 dat_freq1[] = { 0x8c, 0xa4, 0x04 }; static u8 dat_multi5[] = { 0x8c, 0xa1, 0x03 }; static u8 dat_multi6[] = { 0x90, 0x00, 0x05 }; -static struct validx tbl_common1[] = { - {0x0000, 0x0000}, - {1, 0xffff}, /* msleep(35); */ - {0x006a, 0x0007}, {0x0063, 0x0006}, {0x006a, 0x000d}, {0x0000, 0x00c0}, - {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2}, {0x0004, 0x00d8}, - {0x0000, 0x0058}, {0x0002, 0x0004}, {0x0041, 0x0000}, +static struct validx tbl_init_at_startup[] = { + {0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001,0x00c1}, + {0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d}, + {53, 0xffff}, + {0x0040, 0x0000}, {0x0063, 0x0006}, }; -static struct validx tbl_common2[] = { - {0x006a, 0x0007}, - {35, 0xffff}, - {0x00ef, 0x0006}, - {35, 0xffff}, - {0x006a, 0x000d}, - {35, 0xffff}, - {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2}, +static struct validx tbl_common_0B[] = { + {0x0002, 0x0004}, {0x006a, 0x0007}, {0x00ef, 0x0006}, {0x006a,0x000d}, + {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042,0x00c2}, {0x0004, 0x00d8}, {0x0000, 0x0058}, {0x0041, 0x0000}, }; -static struct idxdata tbl_common3[] = { - {0x32, "\x02\x00\x08"}, {0x33, "\xf4\x03\x1d"}, +static struct idxdata tbl_common_3B[] = { + {0x33, "\x86\x25\x01"}, {0x33, "\x86\x25\x00"}, + {2, "\xff\xff\xff"}, + {0x30, "\x1a\x0a\xcc"}, {0x32, "\x02\x00\x08"}, {0x33, "\xf4\x03\x1d"}, {6, "\xff\xff\xff"}, /* 12 */ {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, {2, "\xff\xff\xff"}, /* - */ @@ -98,85 +122,58 @@ static struct idxdata tbl_common3[] = { {0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"}, {0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"}, {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"}, - {1, "\xff\xff\xff"}, {0x33, "\x78\x00\x00"}, - {1, "\xff\xff\xff"}, + {2, "\xff\xff\xff"}, {0x35, "\xb8\x1f\x20"}, {0x33, "\x8c\xa2\x06"}, {0x33, "\x90\x00\x10"}, {0x33, "\x8c\xa2\x07"}, {0x33, "\x90\x00\x08"}, {0x33, "\x8c\xa2\x42"}, {0x33, "\x90\x00\x0b"}, {0x33, "\x8c\xa2\x4a"}, {0x33, "\x90\x00\x8c"}, {0x35, "\xba\xfa\x08"}, {0x33, "\x8c\xa2\x02"}, {0x33, "\x90\x00\x22"}, - {0x33, "\x8c\xa2\x03"}, {0x33, "\x90\x00\xbb"}, -}; - -static struct idxdata tbl_common4[] = { - {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa0"}, {0x33, "\x8c\xa4\x08"}, + {0x33, "\x8c\xa2\x03"}, {0x33, "\x90\x00\xbb"}, {0x33, "\x8c\xa4\x04"}, + {0x33, "\x90\x00\x80"}, {0x33, "\x8c\xa7\x9d"}, {0x33, "\x90\x00\x00"}, + {0x33, "\x8c\xa7\x9e"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa2\x0c"}, + {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa2\x15"}, {0x33, "\x90\x00\x04"}, + {0x33, "\x8c\xa2\x14"}, {0x33, "\x90\x00\x20"}, {0x33, "\x8c\xa1\x03"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, {0x33, "\x90\x21\x11"}, + {0x33, "\x8c\x27\x1b"}, {0x33, "\x90\x02\x4f"}, {0x33, "\x8c\x27\x25"}, + {0x33, "\x90\x06\x0f"}, {0x33, "\x8c\x27\x39"}, {0x33, "\x90\x21\x11"}, + {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, {0x33, "\x8c\x27\x47"}, + {0x33, "\x90\x09\x4c"}, {0x33, "\x8c\x27\x03"}, {0x33, "\x90\x02\x84"}, + {0x33, "\x8c\x27\x05"}, {0x33, "\x90\x01\xe2"}, {0x33, "\x8c\x27\x07"}, + {0x33, "\x90\x06\x40"}, {0x33, "\x8c\x27\x09"}, {0x33, "\x90\x04\xb0"}, + {0x33, "\x8c\x27\x0d"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x0f"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x11"}, {0x33, "\x90\x04\xbd"}, + {0x33, "\x8c\x27\x13"}, {0x33, "\x90\x06\x4d"}, {0x33, "\x8c\x27\x15"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, {0x33, "\x90\x21\x11"}, + {0x33, "\x8c\x27\x19"}, {0x33, "\x90\x04\x6c"}, {0x33, "\x8c\x27\x1b"}, + {0x33, "\x90\x02\x4f"}, {0x33, "\x8c\x27\x1d"}, {0x33, "\x90\x01\x02"}, + {0x33, "\x8c\x27\x1f"}, {0x33, "\x90\x02\x79"}, {0x33, "\x8c\x27\x21"}, + {0x33, "\x90\x01\x55"}, {0x33, "\x8c\x27\x23"}, {0x33, "\x90\x02\x85"}, + {0x33, "\x8c\x27\x25"}, {0x33, "\x90\x06\x0f"}, {0x33, "\x8c\x27\x27"}, + {0x33, "\x90\x20\x20"}, {0x33, "\x8c\x27\x29"}, {0x33, "\x90\x20\x20"}, + {0x33, "\x8c\x27\x2b"}, {0x33, "\x90\x10\x20"}, {0x33, "\x8c\x27\x2d"}, + {0x33, "\x90\x20\x07"}, {0x33, "\x8c\x27\x2f"}, {0x33, "\x90\x00\x04"}, + {0x33, "\x8c\x27\x31"}, {0x33, "\x90\x00\x04"}, {0x33, "\x8c\x27\x33"}, + {0x33, "\x90\x04\xbb"}, {0x33, "\x8c\x27\x35"}, {0x33, "\x90\x06\x4b"}, + {0x33, "\x8c\x27\x37"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x39"}, + {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x3b"}, {0x33, "\x90\x00\x24"}, + {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, {0x33, "\x8c\x27\x41"}, + {0x33, "\x90\x01\x69"}, {0x33, "\x8c\x27\x45"}, {0x33, "\x90\x04\xed"}, + {0x33, "\x8c\x27\x47"}, {0x33, "\x90\x09\x4c"}, {0x33, "\x8c\x27\x51"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x53"}, {0x33, "\x90\x03\x20"}, + {0x33, "\x8c\x27\x55"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x57"}, + {0x33, "\x90\x02\x58"}, {0x33, "\x8c\x27\x5f"}, {0x33, "\x90\x00\x00"}, + {0x33, "\x8c\x27\x61"}, {0x33, "\x90\x06\x40"}, {0x33, "\x8c\x27\x63"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x65"}, {0x33, "\x90\x04\xb0"}, + {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa1"}, {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x21"}, {0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\xa4\x0b"}, - {0x33, "\x90\x00\x27"}, {0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\xa0"}, - {0x33, "\x8c\x24\x13"}, {0x33, "\x90\x00\xc0"}, {0x33, "\x8c\x24\x15"}, - {0x33, "\x90\x00\xa0"}, {0x33, "\x8c\x24\x17"}, {0x33, "\x90\x00\xc0"}, -}; - -static struct idxdata tbl_common5[] = { - {0x33, "\x8c\xa4\x04"}, {0x33, "\x90\x00\x80"}, {0x33, "\x8c\xa7\x9d"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa7\x9e"}, {0x33, "\x90\x00\x00"}, - {0x33, "\x8c\xa2\x0c"}, {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa2\x15"}, - {0x33, "\x90\x00\x04"}, {0x33, "\x8c\xa2\x14"}, {0x33, "\x90\x00\x20"}, - {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, - /* msleep(53); */ - {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x1b"}, {0x33, "\x90\x02\x4f"}, - {0x33, "\x8c\x27\x25"}, {0x33, "\x90\x06\x0f"}, {0x33, "\x8c\x27\x39"}, - {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, - {0x33, "\x8c\x27\x47"}, {0x33, "\x90\x09\x4c"}, {0x33, "\x8c\x27\x03"}, - {0x33, "\x90\x02\x84"}, {0x33, "\x8c\x27\x05"}, {0x33, "\x90\x01\xe2"}, - {0x33, "\x8c\x27\x07"}, {0x33, "\x90\x06\x40"}, {0x33, "\x8c\x27\x09"}, - {0x33, "\x90\x04\xb0"}, {0x33, "\x8c\x27\x0d"}, {0x33, "\x90\x00\x00"}, - {0x33, "\x8c\x27\x0f"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x11"}, - {0x33, "\x90\x04\xbd"}, {0x33, "\x8c\x27\x13"}, {0x33, "\x90\x06\x4d"}, - {0x33, "\x8c\x27\x15"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, - {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x19"}, {0x33, "\x90\x04\x6c"}, - {0x33, "\x8c\x27\x1b"}, {0x33, "\x90\x02\x4f"}, {0x33, "\x8c\x27\x1d"}, - {0x33, "\x90\x01\x02"}, {0x33, "\x8c\x27\x1f"}, {0x33, "\x90\x02\x79"}, - {0x33, "\x8c\x27\x21"}, {0x33, "\x90\x01\x55"}, {0x33, "\x8c\x27\x23"}, - {0x33, "\x90\x02\x85"}, {0x33, "\x8c\x27\x25"}, {0x33, "\x90\x06\x0f"}, - {0x33, "\x8c\x27\x27"}, {0x33, "\x90\x20\x20"}, {0x33, "\x8c\x27\x29"}, - {0x33, "\x90\x20\x20"}, {0x33, "\x8c\x27\x2b"}, {0x33, "\x90\x10\x20"}, - {0x33, "\x8c\x27\x2d"}, {0x33, "\x90\x20\x07"}, {0x33, "\x8c\x27\x2f"}, - {0x33, "\x90\x00\x04"}, {0x33, "\x8c\x27\x31"}, {0x33, "\x90\x00\x04"}, - {0x33, "\x8c\x27\x33"}, {0x33, "\x90\x04\xbb"}, {0x33, "\x8c\x27\x35"}, - {0x33, "\x90\x06\x4b"}, {0x33, "\x8c\x27\x37"}, {0x33, "\x90\x00\x00"}, - {0x33, "\x8c\x27\x39"}, {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x3b"}, - {0x33, "\x90\x00\x24"}, {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, - {0x33, "\x8c\x27\x41"}, {0x33, "\x90\x01\x69"}, {0x33, "\x8c\x27\x45"}, - {0x33, "\x90\x04\xed"}, {0x33, "\x8c\x27\x47"}, {0x33, "\x90\x09\x4c"}, - {0x33, "\x8c\x27\x51"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x53"}, - {0x33, "\x90\x03\x20"}, {0x33, "\x8c\x27\x55"}, {0x33, "\x90\x00\x00"}, - {0x33, "\x8c\x27\x57"}, {0x33, "\x90\x02\x58"}, {0x33, "\x8c\x27\x5f"}, - {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x61"}, {0x33, "\x90\x06\x40"}, - {0x33, "\x8c\x27\x63"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x65"}, - {0x33, "\x90\x04\xb0"}, {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa1"}, - {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"}, - {0x33, "\x90\x00\x21"}, {0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"}, - {0x33, "\x8c\xa4\x0b"}, {0x33, "\x90\x00\x27"}, {0x33, "\x8c\x24\x11"}, - {0x33, "\x90\x00\xa1"}, {0x33, "\x8c\x24\x13"}, {0x33, "\x90\x00\xc1"}, - {0x33, "\x8c\x24\x15"}, -}; - -static struct validx tbl_init_at_startup[] = { - {0x0000, 0x0000}, - {53, 0xffff}, - {0x0010, 0x0010}, - {53, 0xffff}, - {0x0008, 0x00c0}, - {53, 0xffff}, - {0x0001, 0x00c1}, - {53, 0xffff}, - {0x0001, 0x00c2}, - {53, 0xffff}, - {0x0020, 0x0006}, - {53, 0xffff}, - {0x006a, 0x000d}, - {53, 0xffff}, + {0x33, "\x90\x00\x27"}, {0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\xa1"}, + {0x33, "\x8c\x24\x13"}, {0x33, "\x90\x00\xc1"}, {0x33, "\x8c\x24\x15"}, + {0x33, "\x90\x00\x6a"}, {0x33, "\x8c\x24\x17"}, {0x33, "\x90\x00\x80"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, + {3, "\xff\xff\xff"}, }; static struct idxdata tbl_init_post_alt_low1[] = { @@ -209,7 +206,7 @@ static struct idxdata tbl_init_post_alt_low3[] = { {2, "\xff\xff\xff"}, {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"}, - {2, "\xff\xff\xff"}, /* - * */ + {2, "\xff\xff\xff"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, {2, "\xff\xff\xff"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, @@ -217,61 +214,15 @@ static struct idxdata tbl_init_post_alt_low3[] = { {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, {2, "\xff\xff\xff"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, - {1, "\xff\xff\xff"}, -}; - -static struct idxdata tbl_init_post_alt_low4[] = { - {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, - {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, - {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, - {0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"}, - {0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"}, - {0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"}, - {0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"}, - {0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"}, - {0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"}, - {0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"}, - {0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"}, - {0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"}, - {0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"}, - {0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"}, - {0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"}, - {0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"}, - {0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"}, - {0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"}, - {0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"}, - {0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"}, - {0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"}, - {0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"}, - {0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"}, - {0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"}, - {0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"}, - {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, - /* Flip/Mirror h/v=1 */ - {0x33, "\x90\x00\x3c"}, {0x33, "\x8c\x27\x19"}, {0x33, "\x90\x04\x6c"}, - {0x33, "\x8c\x27\x3b"}, {0x33, "\x90\x00\x24"}, {0x33, "\x8c\xa1\x03"}, - {0x33, "\x90\x00\x06"}, - {130, "\xff\xff\xff"}, - {0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"}, - {0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"}, - {100, "\xff\xff\xff"}, - /* ?? */ - {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa1\x02"}, - {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, - {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, - /* Brigthness=70 */ - {0x33, "\x8c\xa2\x06"}, {0x33, "\x90\x00\x46"}, {0x33, "\x8c\xa1\x02"}, - {0x33, "\x90\x00\x0f"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, - /* Sharpness=20 */ - {0x32, "\x6c\x14\x08"}, }; -static struct idxdata tbl_init_post_alt_big1[] = { +static struct idxdata tbl_init_post_alt_big[] = { {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, {2, "\xff\xff\xff"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, {2, "\xff\xff\xff"}, {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, + {2, "\xff\xff\xff"}, {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, {2, "\xff\xff\xff"}, @@ -285,9 +236,17 @@ static struct idxdata tbl_init_post_alt_big1[] = { {0x33, "\x90\x00\x03"}, {0x33, "\x8c\xa1\x34"}, {0x33, "\x90\x00\x03"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x02"}, {0x33, "\x2e\x01\x00"}, {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, + {0x33, "\x8c\x27\x97"}, {0x33, "\x90\x01\x00"}, + {51, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"}, + {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"}, + {51, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x03"}, + {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, + {51, "\xff\xff\xff"}, }; -static struct idxdata tbl_init_post_alt_big2[] = { +static struct idxdata tbl_init_post_alt_3B[] = { {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, @@ -316,17 +275,6 @@ static struct idxdata tbl_init_post_alt_big2[] = { {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"}, }; -static struct idxdata tbl_init_post_alt_big3[] = { - {0x33, "\x8c\xa1\x02"}, - {0x33, "\x90\x00\x1f"}, - {0x33, "\x8c\xa1\x02"}, - {0x33, "\x90\x00\x1f"}, - {0x33, "\x8c\xa1\x02"}, - {0x33, "\x90\x00\x1f"}, - {0x33, "\x8c\xa1\x02"}, - {0x33, "\x90\x00\x1f"}, -}; - static u8 *dat_640 = "\xd0\x02\xd1\x08\xd2\xe1\xd3\x02\xd4\x10\xd5\x81"; static u8 *dat_800 = "\xd0\x02\xd1\x10\xd2\x57\xd3\x02\xd4\x18\xd5\x21"; static u8 *dat_1280 = "\xd0\x02\xd1\x20\xd2\x01\xd3\x02\xd4\x28\xd5\x01"; @@ -351,7 +299,7 @@ void mi2020_init_settings(struct gspca_dev *gspca_dev) sd->vcur.gamma = 0; sd->vcur.hue = 0; sd->vcur.saturation = 60; - sd->vcur.whitebal = 50; + sd->vcur.whitebal = 0; /* 50, not done by hardware */ sd->vcur.mirror = 0; sd->vcur.flip = 0; sd->vcur.AC50Hz = 1; @@ -361,17 +309,12 @@ void mi2020_init_settings(struct gspca_dev *gspca_dev) sd->vmax.sharpness = 40; sd->vmax.contrast = 3; sd->vmax.gamma = 2; - sd->vmax.hue = 0 + 1; /* 200 */ - sd->vmax.saturation = 0; /* 100 */ - sd->vmax.whitebal = 0; /* 100 */ + sd->vmax.hue = 0 + 1; /* 200, not done by hardware */ + sd->vmax.saturation = 0; /* 100, not done by hardware */ + sd->vmax.whitebal = 2; /* 100, not done by hardware */ sd->vmax.mirror = 1; sd->vmax.flip = 1; sd->vmax.AC50Hz = 1; - if (_MI2020b_) { - sd->vmax.contrast = 0; - sd->vmax.gamma = 0; - sd->vmax.backlight = 0; - } sd->dev_camera_settings = mi2020_camera_settings; sd->dev_init_at_startup = mi2020_init_at_startup; @@ -384,51 +327,9 @@ void mi2020_init_settings(struct gspca_dev *gspca_dev) static void common(struct gspca_dev *gspca_dev) { - s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; - - if (_MI2020b_) { - fetch_validx(gspca_dev, tbl_common1, ARRAY_SIZE(tbl_common1)); - } else { - if (_MI2020_) - ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x0004, 0, NULL); - else - ctrl_out(gspca_dev, 0x40, 1, 0x0002, 0x0004, 0, NULL); - msleep(35); - fetch_validx(gspca_dev, tbl_common2, ARRAY_SIZE(tbl_common2)); - } - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x01"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x00"); - msleep(2); /* - * */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0030, 3, "\x1a\x0a\xcc"); - if (reso == IMAGE_1600) - msleep(2); /* 1600 */ - fetch_idxdata(gspca_dev, tbl_common3, ARRAY_SIZE(tbl_common3)); - - if (_MI2020b_ || _MI2020_) - fetch_idxdata(gspca_dev, tbl_common4, - ARRAY_SIZE(tbl_common4)); - - fetch_idxdata(gspca_dev, tbl_common5, ARRAY_SIZE(tbl_common5)); - if (_MI2020b_ || _MI2020_) { - /* Different from fret */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x78"); - /* Same as fret */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\x24\x17"); - /* Different from fret */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x90"); - } else { - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x6a"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\x24\x17"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x80"); - } - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x05"); - msleep(2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); - if (reso == IMAGE_1600) - msleep(14); /* 1600 */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x06"); - msleep(2); + fetch_validx(gspca_dev, tbl_common_0B, ARRAY_SIZE(tbl_common_0B)); + fetch_idxdata(gspca_dev, tbl_common_3B, ARRAY_SIZE(tbl_common_3B)); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL); } static int mi2020_init_at_startup(struct gspca_dev *gspca_dev) @@ -441,8 +342,16 @@ static int mi2020_init_at_startup(struct gspca_dev *gspca_dev) fetch_validx(gspca_dev, tbl_init_at_startup, ARRAY_SIZE(tbl_init_at_startup)); + ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL); + ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &c); + common(gspca_dev); + msleep(61); +/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */ +/* msleep(36); */ + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0000, 0, NULL); + return 0; } @@ -450,17 +359,17 @@ static int mi2020_init_pre_alt(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - sd->mirrorMask = 0; + sd->mirrorMask = 0; + sd->vold.hue = -1; - sd->vold.backlight = -1; + /* These controls need to be reset */ sd->vold.brightness = -1; sd->vold.sharpness = -1; - sd->vold.contrast = -1; - sd->vold.gamma = -1; - sd->vold.hue = -1; - sd->vold.mirror = -1; - sd->vold.flip = -1; - sd->vold.AC50Hz = -1; + + /* If not different from default, they do not need to be set */ + sd->vold.contrast = 0; + sd->vold.gamma = 0; + sd->vold.backlight = 0; mi2020_init_post_alt(gspca_dev); @@ -472,10 +381,10 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; - s32 backlight = sd->vcur.backlight; s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0); s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0); s32 freq = (sd->vcur.AC50Hz > 0); + s32 wbal = sd->vcur.whitebal; u8 dat_freq2[] = {0x90, 0x00, 0x80}; u8 dat_multi1[] = {0x8c, 0xa7, 0x00}; @@ -484,6 +393,7 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) u8 dat_multi4[] = {0x90, 0x00, 0x00}; u8 dat_hvflip2[] = {0x90, 0x04, 0x6c}; u8 dat_hvflip4[] = {0x90, 0x00, 0x24}; + u8 dat_wbal2[] = {0x90, 0x00, 0x00}; u8 c; sd->nbIm = -1; @@ -491,23 +401,26 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) dat_freq2[2] = freq ? 0xc0 : 0x80; dat_multi1[2] = 0x9d; dat_multi3[2] = dat_multi1[2] + 1; - dat_multi4[2] = dat_multi2[2] = backlight; + if (wbal == 0) { + dat_multi4[2] = dat_multi2[2] = 0; + dat_wbal2[2] = 0x17; + } else if (wbal == 1) { + dat_multi4[2] = dat_multi2[2] = 0; + dat_wbal2[2] = 0x35; + } else if (wbal == 2) { + dat_multi4[2] = dat_multi2[2] = 0x20; + dat_wbal2[2] = 0x17; + } dat_hvflip2[2] = 0x6c + 2 * (1 - flip) + (1 - mirror); dat_hvflip4[2] = 0x24 + 2 * (1 - flip) + (1 - mirror); msleep(200); - ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); - msleep(3); /* 35 * */ + msleep(2); common(gspca_dev); - ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL); - msleep(70); - - if (_MI2020b_) - ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); - + msleep(142); ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL); ctrl_out(gspca_dev, 0x40, 1, 0x0003, 0x00c1, 0, NULL); ctrl_out(gspca_dev, 0x40, 1, 0x0042, 0x00c2, 0, NULL); @@ -523,8 +436,7 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_800); - if (_MI2020c_) - fetch_idxdata(gspca_dev, tbl_init_post_alt_low1, + fetch_idxdata(gspca_dev, tbl_init_post_alt_low1, ARRAY_SIZE(tbl_init_post_alt_low1)); if (reso == IMAGE_800) @@ -534,87 +446,10 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) fetch_idxdata(gspca_dev, tbl_init_post_alt_low3, ARRAY_SIZE(tbl_init_post_alt_low3)); - if (_MI2020b_) { - ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); - msleep(150); - } else if (_MI2020c_) { - ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); - msleep(120); - ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); - msleep(30); - } else if (_MI2020_) { - ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); - msleep(120); - ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); - msleep(30); - } - - /* AC power frequency */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2); - msleep(20); - /* backlight */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); - /* at init time but not after */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa2\x0c"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x17"); - /* finish the backlight */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); - msleep(5);/* " */ - - if (_MI2020c_) { - fetch_idxdata(gspca_dev, tbl_init_post_alt_low4, - ARRAY_SIZE(tbl_init_post_alt_low4)); - } else { - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c); - msleep(14); /* 0xd8 */ - - /* flip/mirror */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_hvflip1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_hvflip2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_hvflip3); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_hvflip4); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_hvflip5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_hvflip6); - msleep(21); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_dummy1); - msleep(5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_dummy1); - msleep(5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_dummy1); - msleep(5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_dummy1); - msleep(5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_dummy1); - msleep(5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, - 3, dat_dummy1); - /* end of flip/mirror main part */ - msleep(246); /* 146 */ - - sd->nbIm = 0; - } + ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); + msleep(120); break; case IMAGE_1280: @@ -643,108 +478,62 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) 3, "\x90\x04\xb0"); } - fetch_idxdata(gspca_dev, tbl_init_post_alt_big1, - ARRAY_SIZE(tbl_init_post_alt_big1)); - - if (reso == IMAGE_1600) - msleep(13); /* 1600 */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\x27\x97"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x01\x00"); - msleep(53); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01"); - if (reso == IMAGE_1600) - msleep(13); /* 1600 */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00"); - msleep(53); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x72"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x02"); - if (reso == IMAGE_1600) - msleep(13); /* 1600 */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01"); - msleep(53); - - if (_MI2020b_) { - ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); - if (reso == IMAGE_1600) - msleep(500); /* 1600 */ - ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); - msleep(1850); - } else if (_MI2020c_ || _MI2020_) { - ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); - msleep(1850); - ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); - msleep(30); - } + fetch_idxdata(gspca_dev, tbl_init_post_alt_big, + ARRAY_SIZE(tbl_init_post_alt_big)); - /* AC power frequency */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2); - msleep(20); - /* backlight */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); - /* at init time but not after */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa2\x0c"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x17"); - /* finish the backlight */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); - msleep(6); /* " */ + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); + msleep(1850); + } - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c); - msleep(14); + ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); + msleep(40); + + /* AC power frequency */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2); + msleep(33); + /* light source */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); + msleep(7); + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c); + + fetch_idxdata(gspca_dev, tbl_init_post_alt_3B, + ARRAY_SIZE(tbl_init_post_alt_3B)); + + /* hvflip */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip6); + msleep(250); + + if (reso == IMAGE_640 || reso == IMAGE_800) + fetch_idxdata(gspca_dev, tbl_middle_hvflip_low, + ARRAY_SIZE(tbl_middle_hvflip_low)); + else + fetch_idxdata(gspca_dev, tbl_middle_hvflip_big, + ARRAY_SIZE(tbl_middle_hvflip_big)); - if (_MI2020c_) - fetch_idxdata(gspca_dev, tbl_init_post_alt_big2, - ARRAY_SIZE(tbl_init_post_alt_big2)); + fetch_idxdata(gspca_dev, tbl_end_hvflip, + ARRAY_SIZE(tbl_end_hvflip)); - /* flip/mirror */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip3); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip4); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip6); - /* end of flip/mirror main part */ - msleep(16); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00"); - if (reso == IMAGE_1600) - msleep(25); /* 1600 */ - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00"); - msleep(103); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x02"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x72"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02"); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01"); - sd->nbIm = 0; - - if (_MI2020c_) - fetch_idxdata(gspca_dev, tbl_init_post_alt_big3, - ARRAY_SIZE(tbl_init_post_alt_big3)); - } + sd->nbIm = 0; sd->vold.mirror = mirror; sd->vold.flip = flip; sd->vold.AC50Hz = freq; - sd->vold.backlight = backlight; + sd->vold.whitebal = wbal; mi2020_camera_settings(gspca_dev); @@ -772,6 +561,7 @@ static int mi2020_configure_alt(struct gspca_dev *gspca_dev) static int mi2020_camera_settings(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; s32 backlight = sd->vcur.backlight; s32 bright = sd->vcur.brightness; @@ -782,6 +572,7 @@ static int mi2020_camera_settings(struct gspca_dev *gspca_dev) s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0); s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0); s32 freq = (sd->vcur.AC50Hz > 0); + s32 wbal = sd->vcur.whitebal; u8 dat_sharp[] = {0x6c, 0x00, 0x08}; u8 dat_bright2[] = {0x90, 0x00, 0x00}; @@ -792,6 +583,7 @@ static int mi2020_camera_settings(struct gspca_dev *gspca_dev) u8 dat_multi4[] = {0x90, 0x00, 0x00}; u8 dat_hvflip2[] = {0x90, 0x04, 0x6c}; u8 dat_hvflip4[] = {0x90, 0x00, 0x24}; + u8 dat_wbal2[] = {0x90, 0x00, 0x00}; /* Less than 4 images received -> too early to set the settings */ if (sd->nbIm < 4) { @@ -809,67 +601,89 @@ static int mi2020_camera_settings(struct gspca_dev *gspca_dev) msleep(20); } + if (wbal != sd->vold.whitebal) { + sd->vold.whitebal = wbal; + if (wbal < 0 || wbal > sd->vmax.whitebal) + wbal = 0; + + dat_multi1[2] = 0x9d; + dat_multi3[2] = dat_multi1[2] + 1; + if (wbal == 0) { + dat_multi4[2] = dat_multi2[2] = 0; + dat_wbal2[2] = 0x17; + } else if (wbal == 1) { + dat_multi4[2] = dat_multi2[2] = 0; + dat_wbal2[2] = 0x35; + } else if (wbal == 2) { + dat_multi4[2] = dat_multi2[2] = 0x20; + dat_wbal2[2] = 0x17; + } + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_wbal2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); + } + if (mirror != sd->vold.mirror || flip != sd->vold.flip) { sd->vold.mirror = mirror; sd->vold.flip = flip; dat_hvflip2[2] = 0x6c + 2 * (1 - flip) + (1 - mirror); dat_hvflip4[2] = 0x24 + 2 * (1 - flip) + (1 - mirror); + + fetch_idxdata(gspca_dev, tbl_init_post_alt_3B, + ARRAY_SIZE(tbl_init_post_alt_3B)); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1); ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip2); ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip3); ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip4); ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip5); ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip6); - msleep(130); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); - msleep(6); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); - msleep(6); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); - msleep(6); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); - msleep(6); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); - msleep(6); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); - msleep(6); - - /* Sometimes present, sometimes not, useful? */ - /* ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2); - * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3); - * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2); - * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3); - * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2); - * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3); - * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2); - * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3);*/ + msleep(40); + + if (reso == IMAGE_640 || reso == IMAGE_800) + fetch_idxdata(gspca_dev, tbl_middle_hvflip_low, + ARRAY_SIZE(tbl_middle_hvflip_low)); + else + fetch_idxdata(gspca_dev, tbl_middle_hvflip_big, + ARRAY_SIZE(tbl_middle_hvflip_big)); + + fetch_idxdata(gspca_dev, tbl_end_hvflip, + ARRAY_SIZE(tbl_end_hvflip)); } - if (backlight != sd->vold.backlight) { - sd->vold.backlight = backlight; - if (backlight < 0 || backlight > sd->vmax.backlight) - backlight = 0; + if (bright != sd->vold.brightness) { + sd->vold.brightness = bright; + if (bright < 0 || bright > sd->vmax.brightness) + bright = 0; - dat_multi1[2] = 0x9d; - dat_multi3[2] = dat_multi1[2] + 1; - dat_multi4[2] = dat_multi2[2] = backlight; - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); + dat_bright2[2] = bright; + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright6); } - if (gam != sd->vold.gamma) { + if (cntr != sd->vold.contrast || gam != sd->vold.gamma) { + sd->vold.contrast = cntr; + if (cntr < 0 || cntr > sd->vmax.contrast) + cntr = 0; sd->vold.gamma = gam; if (gam < 0 || gam > sd->vmax.gamma) gam = 0; dat_multi1[2] = 0x6d; dat_multi3[2] = dat_multi1[2] + 1; - dat_multi4[2] = dat_multi2[2] = 0x40 + gam; + if (cntr == 0) + cntr = 4; + dat_multi4[2] = dat_multi2[2] = cntr * 0x10 + 2 - gam; ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); @@ -878,14 +692,14 @@ static int mi2020_camera_settings(struct gspca_dev *gspca_dev) ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); } - if (cntr != sd->vold.contrast) { - sd->vold.contrast = cntr; - if (cntr < 0 || cntr > sd->vmax.contrast) - cntr = 0; + if (backlight != sd->vold.backlight) { + sd->vold.backlight = backlight; + if (backlight < 0 || backlight > sd->vmax.backlight) + backlight = 0; - dat_multi1[2] = 0x6d; + dat_multi1[2] = 0x9d; dat_multi3[2] = dat_multi1[2] + 1; - dat_multi4[2] = dat_multi2[2] = 0x12 + 16 * cntr; + dat_multi4[2] = dat_multi2[2] = backlight; ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); @@ -894,20 +708,6 @@ static int mi2020_camera_settings(struct gspca_dev *gspca_dev) ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); } - if (bright != sd->vold.brightness) { - sd->vold.brightness = bright; - if (bright < 0 || bright > sd->vmax.brightness) - bright = 0; - - dat_bright2[2] = bright; - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright1); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright2); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright3); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright4); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright5); - ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright6); - } - if (sharp != sd->vold.sharpness) { sd->vold.sharpness = sharp; if (sharp < 0 || sharp > sd->vmax.sharpness) @@ -928,9 +728,6 @@ static int mi2020_camera_settings(struct gspca_dev *gspca_dev) static void mi2020_post_unset_alt(struct gspca_dev *gspca_dev) { ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL); - msleep(20); - if (_MI2020c_ || _MI2020_) - ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0000, 0, NULL); - else - ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL); + msleep(40); + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0000, 0, NULL); } diff --git a/drivers/media/video/gspca/gl860/gl860-ov9655.c b/drivers/media/video/gspca/gl860/gl860-ov9655.c index d412694..5ae9619 100644 --- a/drivers/media/video/gspca/gl860/gl860-ov9655.c +++ b/drivers/media/video/gspca/gl860/gl860-ov9655.c @@ -69,7 +69,7 @@ static u8 *tbl_640[] = { "\xd0\x01\xd1\x08\xd2\xe0\xd3\x01" "\xd4\x10\xd5\x80" }; -static u8 *tbl_800[] = { +static u8 *tbl_1280[] = { "\x00\x40\x07\x6a\x06\xf3\x0d\x6a" "\x10\x10\xc1\x01" , "\x12\x80\x00\x00\x01\x98\x02\x80" "\x03\x12\x04\x01\x0b\x57\x0e\x61" @@ -217,7 +217,7 @@ static int ov9655_init_post_alt(struct gspca_dev *gspca_dev) ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); - tbl = (reso == IMAGE_640) ? tbl_640 : tbl_800; + tbl = (reso == IMAGE_640) ? tbl_640 : tbl_1280; ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, tbl_length[0], tbl[0]); diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c index 9e42476..e86eb8b 100644 --- a/drivers/media/video/gspca/gl860/gl860.c +++ b/drivers/media/video/gspca/gl860/gl860.c @@ -63,7 +63,7 @@ static int sd_set_##thename(struct gspca_dev *gspca_dev, s32 val)\ \ sd->vcur.thename = val;\ if (gspca_dev->streaming)\ - sd->dev_camera_settings(gspca_dev);\ + sd->waitSet = 1;\ return 0;\ } \ static int sd_get_##thename(struct gspca_dev *gspca_dev, s32 *val)\ @@ -91,7 +91,6 @@ SD_SETGET(contrast) /* control table */ static struct ctrl sd_ctrls_mi1320[GL860_NCTRLS]; static struct ctrl sd_ctrls_mi2020[GL860_NCTRLS]; -static struct ctrl sd_ctrls_mi2020b[GL860_NCTRLS]; static struct ctrl sd_ctrls_ov2640[GL860_NCTRLS]; static struct ctrl sd_ctrls_ov9655[GL860_NCTRLS]; @@ -121,8 +120,6 @@ static int gl860_build_control_table(struct gspca_dev *gspca_dev) sd_ctrls = sd_ctrls_mi1320; else if (_MI2020_) sd_ctrls = sd_ctrls_mi2020; - else if (_MI2020b_) - sd_ctrls = sd_ctrls_mi2020b; else if (_OV2640_) sd_ctrls = sd_ctrls_ov2640; else if (_OV9655_) @@ -187,19 +184,6 @@ static const struct sd_desc sd_desc_mi2020 = { .dq_callback = sd_callback, }; -static const struct sd_desc sd_desc_mi2020b = { - .name = MODULE_NAME, - .ctrls = sd_ctrls_mi2020b, - .nctrls = GL860_NCTRLS, - .config = sd_config, - .init = sd_init, - .isoc_init = sd_isoc_init, - .start = sd_start, - .stop0 = sd_stop0, - .pkt_scan = sd_pkt_scan, - .dq_callback = sd_callback, -}; - static const struct sd_desc sd_desc_ov2640 = { .name = MODULE_NAME, .ctrls = sd_ctrls_ov2640, @@ -235,9 +219,9 @@ static struct v4l2_pix_format mi2020_mode[] = { .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0 }, - { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + { 800, 598, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, .bytesperline = 800, - .sizeimage = 800 * 600, + .sizeimage = 800 * 598, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 1 }, @@ -247,9 +231,9 @@ static struct v4l2_pix_format mi2020_mode[] = { .colorspace = V4L2_COLORSPACE_SRGB, .priv = 2 }, - {1600, 1200, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + {1600, 1198, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, .bytesperline = 1600, - .sizeimage = 1600 * 1200, + .sizeimage = 1600 * 1198, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 3 }, @@ -344,8 +328,6 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->sensor = ID_OV9655; else if (strcmp(sensor, "MI2020") == 0) sd->sensor = ID_MI2020; - else if (strcmp(sensor, "MI2020b") == 0) - sd->sensor = ID_MI2020b; /* Get sensor and set the suitable init/start/../stop functions */ if (gl860_guess_sensor(gspca_dev, vendor_id, product_id) == -1) @@ -369,13 +351,6 @@ static int sd_config(struct gspca_dev *gspca_dev, dev_init_settings = mi2020_init_settings; break; - case ID_MI2020b: - gspca_dev->sd_desc = &sd_desc_mi2020b; - cam->cam_mode = mi2020_mode; - cam->nmodes = ARRAY_SIZE(mi2020_mode); - dev_init_settings = mi2020_init_settings; - break; - case ID_OV2640: gspca_dev->sd_desc = &sd_desc_ov2640; cam->cam_mode = ov2640_mode; @@ -620,10 +595,7 @@ int gl860_RTx(struct gspca_dev *gspca_dev, else if (len > 1 && r < len) PDEBUG(D_ERR, "short ctrl transfer %d/%d", r, len); - if ((_MI2020_ || _MI2020b_ || _MI2020c_) && (val || index)) - msleep(1); - if (_OV2640_) - msleep(1); + msleep(1); return r; } @@ -767,8 +739,6 @@ static int gl860_guess_sensor(struct gspca_dev *gspca_dev, PDEBUG(D_PROBE, "05e3:f191 sensor MI1320 (1.3M)"); } else if (_MI2020_) { PDEBUG(D_PROBE, "05e3:0503 sensor MI2020 (2.0M)"); - } else if (_MI2020b_) { - PDEBUG(D_PROBE, "05e3:0503 sensor MI2020 alt. driver (2.0M)"); } else if (_OV9655_) { PDEBUG(D_PROBE, "05e3:0503 sensor OV9655 (1.3M)"); } else if (_OV2640_) { diff --git a/drivers/media/video/gspca/gl860/gl860.h b/drivers/media/video/gspca/gl860/gl860.h index 305061f..49ad4ac 100644 --- a/drivers/media/video/gspca/gl860/gl860.h +++ b/drivers/media/video/gspca/gl860/gl860.h @@ -32,19 +32,16 @@ #define ID_OV2640 2 #define ID_OV9655 4 #define ID_MI2020 8 -#define ID_MI2020b 16 #define _MI1320_ (((struct sd *) gspca_dev)->sensor == ID_MI1320) #define _MI2020_ (((struct sd *) gspca_dev)->sensor == ID_MI2020) -#define _MI2020b_ (((struct sd *) gspca_dev)->sensor == ID_MI2020b) -#define _MI2020c_ 0 #define _OV2640_ (((struct sd *) gspca_dev)->sensor == ID_OV2640) #define _OV9655_ (((struct sd *) gspca_dev)->sensor == ID_OV9655) #define IMAGE_640 0 #define IMAGE_800 1 #define IMAGE_1280 2 -#define IMAGE_1600 3 +#define IMAGE_1600 3 struct sd_gl860 { u16 backlight; @@ -75,10 +72,10 @@ struct sd { int (*dev_camera_settings)(struct gspca_dev *); u8 swapRB; - u8 mirrorMask; - u8 sensor; - s32 nbIm; - s32 nbRightUp; + u8 mirrorMask; + u8 sensor; + s32 nbIm; + s32 nbRightUp; u8 waitSet; }; diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 678675b..d951b0f 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -201,7 +201,7 @@ static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev, buffer_len = le16_to_cpu(ep->wMaxPacketSize); interval = ep->bInterval; - PDEBUG(D_PROBE, "found int in endpoint: 0x%x, " + PDEBUG(D_CONF, "found int in endpoint: 0x%x, " "buffer_len=%u, interval=%u", ep->bEndpointAddress, buffer_len, interval); @@ -226,7 +226,7 @@ static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev, gspca_dev->int_urb = urb; ret = usb_submit_urb(urb, GFP_KERNEL); if (ret < 0) { - PDEBUG(D_ERR, "submit URB failed with error %i", ret); + PDEBUG(D_ERR, "submit int URB failed with error %i", ret); goto error_submit; } return ret; @@ -294,19 +294,6 @@ static inline int gspca_input_connect(struct gspca_dev *dev) } #endif -/* get the current input frame buffer */ -struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev) -{ - struct gspca_frame *frame; - - frame = gspca_dev->cur_frame; - if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS) - != V4L2_BUF_FLAG_QUEUED) - return NULL; - return frame; -} -EXPORT_SYMBOL(gspca_get_i_frame); - /* * fill a video frame from an URB and resubmit */ @@ -439,20 +426,20 @@ void gspca_frame_add(struct gspca_dev *gspca_dev, PDEBUG(D_PACK, "add t:%d l:%d", packet_type, len); - /* check the availability of the frame buffer */ - frame = gspca_dev->cur_frame; - if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS) - != V4L2_BUF_FLAG_QUEUED) { - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - - /* when start of a new frame, if the current frame buffer - * is not queued, discard the whole frame */ if (packet_type == FIRST_PACKET) { - frame->data_end = frame->data; + i = atomic_read(&gspca_dev->fr_i); + + /* if there are no queued buffer, discard the whole frame */ + if (i == atomic_read(&gspca_dev->fr_q)) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + j = gspca_dev->fr_queue[i]; + frame = &gspca_dev->frame[j]; frame->v4l2_buf.timestamp = ktime_to_timeval(ktime_get()); frame->v4l2_buf.sequence = ++gspca_dev->sequence; + gspca_dev->image = frame->data; + gspca_dev->image_len = 0; } else if (gspca_dev->last_packet_type == DISCARD_PACKET) { if (packet_type == LAST_PACKET) gspca_dev->last_packet_type = packet_type; @@ -461,34 +448,37 @@ void gspca_frame_add(struct gspca_dev *gspca_dev, /* append the packet to the frame buffer */ if (len > 0) { - if (frame->data_end - frame->data + len - > frame->v4l2_buf.length) { - PDEBUG(D_ERR|D_PACK, "frame overflow %zd > %d", - frame->data_end - frame->data + len, - frame->v4l2_buf.length); + if (gspca_dev->image_len + len > gspca_dev->frsz) { + PDEBUG(D_ERR|D_PACK, "frame overflow %d > %d", + gspca_dev->image_len + len, + gspca_dev->frsz); packet_type = DISCARD_PACKET; } else { - memcpy(frame->data_end, data, len); - frame->data_end += len; + memcpy(gspca_dev->image + gspca_dev->image_len, + data, len); + gspca_dev->image_len += len; } } gspca_dev->last_packet_type = packet_type; - /* if last packet, wake up the application and advance in the queue */ + /* if last packet, invalidate packet concatenation until + * next first packet, wake up the application and advance + * in the queue */ if (packet_type == LAST_PACKET) { - frame->v4l2_buf.bytesused = frame->data_end - frame->data; - frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED; - frame->v4l2_buf.flags |= V4L2_BUF_FLAG_DONE; - wake_up_interruptible(&gspca_dev->wq); /* event = new frame */ - i = (gspca_dev->fr_i + 1) % gspca_dev->nframes; - gspca_dev->fr_i = i; - PDEBUG(D_FRAM, "frame complete len:%d q:%d i:%d o:%d", - frame->v4l2_buf.bytesused, - gspca_dev->fr_q, - i, - gspca_dev->fr_o); + i = atomic_read(&gspca_dev->fr_i); j = gspca_dev->fr_queue[i]; - gspca_dev->cur_frame = &gspca_dev->frame[j]; + frame = &gspca_dev->frame[j]; + frame->v4l2_buf.bytesused = gspca_dev->image_len; + frame->v4l2_buf.flags = (frame->v4l2_buf.flags + | V4L2_BUF_FLAG_DONE) + & ~V4L2_BUF_FLAG_QUEUED; + i = (i + 1) % GSPCA_MAX_FRAMES; + atomic_set(&gspca_dev->fr_i, i); + wake_up_interruptible(&gspca_dev->wq); /* event = new frame */ + PDEBUG(D_FRAM, "frame complete len:%d", + frame->v4l2_buf.bytesused); + gspca_dev->image = NULL; + gspca_dev->image_len = 0; } } EXPORT_SYMBOL(gspca_frame_add); @@ -506,36 +496,6 @@ static int gspca_is_compressed(__u32 format) return 0; } -static void *rvmalloc(long size) -{ - void *mem; - unsigned long adr; - - mem = vmalloc_32(size); - if (mem != NULL) { - 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, long size) -{ - unsigned long adr; - - adr = (unsigned long) mem; - while (size > 0) { - ClearPageReserved(vmalloc_to_page((void *) adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); -} - static int frame_alloc(struct gspca_dev *gspca_dev, unsigned int count) { @@ -548,9 +508,9 @@ static int frame_alloc(struct gspca_dev *gspca_dev, PDEBUG(D_STREAM, "frame alloc frsz: %d", frsz); frsz = PAGE_ALIGN(frsz); gspca_dev->frsz = frsz; - if (count > GSPCA_MAX_FRAMES) - count = GSPCA_MAX_FRAMES; - gspca_dev->frbuf = rvmalloc(frsz * count); + if (count >= GSPCA_MAX_FRAMES) + count = GSPCA_MAX_FRAMES - 1; + gspca_dev->frbuf = vmalloc_32(frsz * count); if (!gspca_dev->frbuf) { err("frame alloc failed"); return -ENOMEM; @@ -565,14 +525,12 @@ static int frame_alloc(struct gspca_dev *gspca_dev, frame->v4l2_buf.length = frsz; frame->v4l2_buf.memory = gspca_dev->memory; frame->v4l2_buf.sequence = 0; - frame->data = frame->data_end = - gspca_dev->frbuf + i * frsz; + frame->data = gspca_dev->frbuf + i * frsz; frame->v4l2_buf.m.offset = i * frsz; } - gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0; - gspca_dev->cur_frame = &gspca_dev->frame[0]; - gspca_dev->last_packet_type = DISCARD_PACKET; - gspca_dev->sequence = 0; + atomic_set(&gspca_dev->fr_q, 0); + atomic_set(&gspca_dev->fr_i, 0); + gspca_dev->fr_o = 0; return 0; } @@ -582,8 +540,7 @@ static void frame_free(struct gspca_dev *gspca_dev) PDEBUG(D_STREAM, "frame free"); if (gspca_dev->frbuf != NULL) { - rvfree(gspca_dev->frbuf, - gspca_dev->nframes * gspca_dev->frsz); + vfree(gspca_dev->frbuf); gspca_dev->frbuf = NULL; for (i = 0; i < gspca_dev->nframes; i++) gspca_dev->frame[i].data = NULL; @@ -683,12 +640,16 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev) : USB_ENDPOINT_XFER_ISOC; i = gspca_dev->alt; /* previous alt setting */ if (gspca_dev->cam.reverse_alts) { + if (gspca_dev->audio) + i++; while (++i < gspca_dev->nbalt) { ep = alt_xfer(&intf->altsetting[i], xfer); if (ep) break; } } else { + if (gspca_dev->audio) + i--; while (--i >= 0) { ep = alt_xfer(&intf->altsetting[i], xfer); if (ep) @@ -811,6 +772,12 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) goto out; } + /* reset the streaming variables */ + gspca_dev->image = NULL; + gspca_dev->image_len = 0; + gspca_dev->last_packet_type = DISCARD_PACKET; + gspca_dev->sequence = 0; + gspca_dev->usb_err = 0; /* set the higher alternate setting and @@ -1433,34 +1400,6 @@ static int vidioc_g_ctrl(struct file *file, void *priv, return ret; } -/*fixme: have an audio flag in gspca_dev?*/ -static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *audio) -{ - if (audio->index != 0) - return -EINVAL; - return 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *audio) -{ - strcpy(audio->name, "Microphone"); - return 0; -} - -static int vidioc_enumaudio(struct file *file, void *priv, - struct v4l2_audio *audio) -{ - if (audio->index != 0) - return -EINVAL; - - strcpy(audio->name, "Microphone"); - audio->capability = 0; - audio->mode = 0; - return 0; -} - static int vidioc_querymenu(struct file *file, void *priv, struct v4l2_querymenu *qmenu) { @@ -1504,7 +1443,8 @@ static int vidioc_reqbufs(struct file *file, void *priv, struct gspca_dev *gspca_dev = priv; int i, ret = 0, streaming; - switch (rb->memory) { + i = rb->memory; /* (avoid compilation warning) */ + switch (i) { case GSPCA_MEMORY_READ: /* (internal call) */ case V4L2_MEMORY_MMAP: case V4L2_MEMORY_USERPTR: @@ -1626,7 +1566,7 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type buf_type) { struct gspca_dev *gspca_dev = priv; - int i, ret; + int ret; if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -1650,12 +1590,10 @@ static int vidioc_streamoff(struct file *file, void *priv, gspca_stream_off(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); - /* empty the application queues */ - for (i = 0; i < gspca_dev->nframes; i++) - gspca_dev->frame[i].v4l2_buf.flags &= ~BUF_ALL_FLAGS; - gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0; - gspca_dev->last_packet_type = DISCARD_PACKET; - gspca_dev->sequence = 0; + /* empty the transfer queues */ + atomic_set(&gspca_dev->fr_q, 0); + atomic_set(&gspca_dev->fr_i, 0); + gspca_dev->fr_o = 0; ret = 0; out: mutex_unlock(&gspca_dev->queue_lock); @@ -1732,7 +1670,7 @@ static int vidioc_s_parm(struct file *filp, void *priv, int n; n = parm->parm.capture.readbuffers; - if (n == 0 || n > GSPCA_MAX_FRAMES) + if (n == 0 || n >= GSPCA_MAX_FRAMES) parm->parm.capture.readbuffers = gspca_dev->nbufread; else gspca_dev->nbufread = n; @@ -1755,49 +1693,6 @@ static int vidioc_s_parm(struct file *filp, void *priv, return 0; } -#ifdef CONFIG_VIDEO_V4L1_COMPAT -static int vidiocgmbuf(struct file *file, void *priv, - struct video_mbuf *mbuf) -{ - struct gspca_dev *gspca_dev = file->private_data; - int i; - - PDEBUG(D_STREAM, "cgmbuf"); - if (gspca_dev->nframes == 0) { - int ret; - - { - struct v4l2_format fmt; - - fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - i = gspca_dev->cam.nmodes - 1; /* highest mode */ - fmt.fmt.pix.width = gspca_dev->cam.cam_mode[i].width; - fmt.fmt.pix.height = gspca_dev->cam.cam_mode[i].height; - fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24; - ret = vidioc_s_fmt_vid_cap(file, priv, &fmt); - if (ret != 0) - return ret; - } - { - struct v4l2_requestbuffers rb; - - memset(&rb, 0, sizeof rb); - rb.count = 4; - rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - rb.memory = V4L2_MEMORY_MMAP; - ret = vidioc_reqbufs(file, priv, &rb); - if (ret != 0) - return ret; - } - } - mbuf->frames = gspca_dev->nframes; - mbuf->size = gspca_dev->frsz * gspca_dev->nframes; - for (i = 0; i < mbuf->frames; i++) - mbuf->offsets[i] = gspca_dev->frame[i].v4l2_buf.m.offset; - return 0; -} -#endif - static int dev_mmap(struct file *file, struct vm_area_struct *vma) { struct gspca_dev *gspca_dev = file->private_data; @@ -1838,12 +1733,7 @@ static int dev_mmap(struct file *file, struct vm_area_struct *vma) ret = -EINVAL; goto out; } -#ifdef CONFIG_VIDEO_V4L1_COMPAT - /* v4l1 maps all the buffers */ - if (i != 0 - || size != frame->v4l2_buf.length * gspca_dev->nframes) -#endif - if (size != frame->v4l2_buf.length) { + if (size != frame->v4l2_buf.length) { PDEBUG(D_STREAM, "mmap bad size"); ret = -EINVAL; goto out; @@ -1883,21 +1773,17 @@ out: static int frame_wait(struct gspca_dev *gspca_dev, int nonblock_ing) { - struct gspca_frame *frame; - int i, j, ret; + int i, ret; /* check if a frame is ready */ i = gspca_dev->fr_o; - j = gspca_dev->fr_queue[i]; - frame = &gspca_dev->frame[j]; - - if (!(frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE)) { + if (i == atomic_read(&gspca_dev->fr_i)) { if (nonblock_ing) return -EAGAIN; /* wait till a frame is ready */ ret = wait_event_interruptible_timeout(gspca_dev->wq, - (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE) || + i != atomic_read(&gspca_dev->fr_i) || !gspca_dev->streaming || !gspca_dev->present, msecs_to_jiffies(3000)); if (ret < 0) @@ -1906,11 +1792,7 @@ static int frame_wait(struct gspca_dev *gspca_dev, return -EIO; } - gspca_dev->fr_o = (i + 1) % gspca_dev->nframes; - PDEBUG(D_FRAM, "frame wait q:%d i:%d o:%d", - gspca_dev->fr_q, - gspca_dev->fr_i, - gspca_dev->fr_o); + gspca_dev->fr_o = (i + 1) % GSPCA_MAX_FRAMES; if (gspca_dev->sd_desc->dq_callback) { mutex_lock(&gspca_dev->usb_lock); @@ -1919,7 +1801,7 @@ static int frame_wait(struct gspca_dev *gspca_dev, gspca_dev->sd_desc->dq_callback(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); } - return j; + return gspca_dev->fr_queue[i]; } /* @@ -2024,15 +1906,9 @@ static int vidioc_qbuf(struct file *file, void *priv, } /* put the buffer in the 'queued' queue */ - i = gspca_dev->fr_q; + i = atomic_read(&gspca_dev->fr_q); gspca_dev->fr_queue[i] = index; - if (gspca_dev->fr_i == i) - gspca_dev->cur_frame = frame; - gspca_dev->fr_q = (i + 1) % gspca_dev->nframes; - PDEBUG(D_FRAM, "qbuf q:%d i:%d o:%d", - gspca_dev->fr_q, - gspca_dev->fr_i, - gspca_dev->fr_o); + atomic_set(&gspca_dev->fr_q, (i + 1) % GSPCA_MAX_FRAMES); v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED; v4l2_buf->flags &= ~V4L2_BUF_FLAG_DONE; @@ -2088,7 +1964,7 @@ static int read_alloc(struct gspca_dev *gspca_dev, static unsigned int dev_poll(struct file *file, poll_table *wait) { struct gspca_dev *gspca_dev = file->private_data; - int i, ret; + int ret; PDEBUG(D_FRAM, "poll"); @@ -2106,11 +1982,9 @@ static unsigned int dev_poll(struct file *file, poll_table *wait) if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) return POLLERR; - /* check the next incoming buffer */ - i = gspca_dev->fr_o; - i = gspca_dev->fr_queue[i]; - if (gspca_dev->frame[i].v4l2_buf.flags & V4L2_BUF_FLAG_DONE) - ret = POLLIN | POLLRDNORM; /* something to read */ + /* check if an image has been received */ + if (gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i)) + ret = POLLIN | POLLRDNORM; /* yes */ else ret = 0; mutex_unlock(&gspca_dev->queue_lock); @@ -2214,9 +2088,6 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_queryctrl = vidioc_queryctrl, .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_enumaudio = vidioc_enumaudio, .vidioc_querymenu = vidioc_querymenu, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, @@ -2235,9 +2106,6 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_s_register = vidioc_s_register, #endif .vidioc_g_chip_ident = vidioc_g_chip_ident, -#ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = vidiocgmbuf, -#endif }; static struct video_device gspca_template = { @@ -2253,31 +2121,18 @@ static struct video_device gspca_template = { * This function must be called by the sub-driver when it is * called for probing a new device. */ -int gspca_dev_probe(struct usb_interface *intf, +int gspca_dev_probe2(struct usb_interface *intf, const struct usb_device_id *id, const struct sd_desc *sd_desc, int dev_size, struct module *module) { - struct usb_interface_descriptor *interface; struct gspca_dev *gspca_dev; struct usb_device *dev = interface_to_usbdev(intf); int ret; PDEBUG(D_PROBE, "probing %04x:%04x", id->idVendor, id->idProduct); - /* we don't handle multi-config cameras */ - if (dev->descriptor.bNumConfigurations != 1) { - PDEBUG(D_ERR, "Too many config"); - return -ENODEV; - } - - /* the USB video interface must be the first one */ - interface = &intf->cur_altsetting->desc; - if (dev->config->desc.bNumInterfaces != 1 && - interface->bInterfaceNumber != 0) - return -ENODEV; - /* create the device */ if (dev_size < sizeof *gspca_dev) dev_size = sizeof *gspca_dev; @@ -2293,8 +2148,26 @@ int gspca_dev_probe(struct usb_interface *intf, goto out; } gspca_dev->dev = dev; - gspca_dev->iface = interface->bInterfaceNumber; + gspca_dev->iface = intf->cur_altsetting->desc.bInterfaceNumber; gspca_dev->nbalt = intf->num_altsetting; + + /* check if any audio device */ + if (dev->config->desc.bNumInterfaces != 1) { + int i; + struct usb_interface *intf2; + + for (i = 0; i < dev->config->desc.bNumInterfaces; i++) { + intf2 = dev->config->interface[i]; + if (intf2 != NULL + && intf2->altsetting != NULL + && intf2->altsetting->desc.bInterfaceClass == + USB_CLASS_AUDIO) { + gspca_dev->audio = 1; + break; + } + } + } + gspca_dev->sd_desc = sd_desc; gspca_dev->nbufread = 2; gspca_dev->empty_packet = -1; /* don't check the empty packets */ @@ -2345,6 +2218,31 @@ out: kfree(gspca_dev); return ret; } +EXPORT_SYMBOL(gspca_dev_probe2); + +/* same function as the previous one, but check the interface */ +int gspca_dev_probe(struct usb_interface *intf, + const struct usb_device_id *id, + const struct sd_desc *sd_desc, + int dev_size, + struct module *module) +{ + struct usb_device *dev = interface_to_usbdev(intf); + + /* we don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) { + PDEBUG(D_ERR, "%04x:%04x too many config", + id->idVendor, id->idProduct); + return -ENODEV; + } + + /* the USB video interface must be the first one */ + if (dev->config->desc.bNumInterfaces != 1 + && intf->cur_altsetting->desc.bInterfaceNumber != 0) + return -ENODEV; + + return gspca_dev_probe2(intf, id, sd_desc, dev_size, module); +} EXPORT_SYMBOL(gspca_dev_probe); /* diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 8b963df..b749c36 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -7,7 +7,6 @@ #include <linux/videodev2.h> #include <media/v4l2-common.h> #include <linux/mutex.h> -#include <linux/slab.h> /* compilation option */ #define GSPCA_DEBUG 1 @@ -148,7 +147,6 @@ enum gspca_packet_type { struct gspca_frame { __u8 *data; /* frame buffer */ - __u8 *data_end; /* end of frame while filling */ int vma_use_count; struct v4l2_buffer v4l2_buf; }; @@ -177,13 +175,14 @@ struct gspca_dev { __u8 *frbuf; /* buffer for nframes */ struct gspca_frame frame[GSPCA_MAX_FRAMES]; - struct gspca_frame *cur_frame; /* frame beeing filled */ + u8 *image; /* image beeing filled */ __u32 frsz; /* frame size */ - char nframes; /* number of frames */ - char fr_i; /* frame being filled */ - char fr_q; /* next frame to queue */ - char fr_o; /* next frame to dequeue */ + u32 image_len; /* current length of image */ + atomic_t fr_q; /* next frame to queue */ + atomic_t fr_i; /* frame being filled */ signed char fr_queue[GSPCA_MAX_FRAMES]; /* frame queue */ + char nframes; /* number of frames */ + u8 fr_o; /* next frame to dequeue */ __u8 last_packet_type; __s8 empty_packet; /* if (-1) don't check empty packets */ __u8 streaming; @@ -199,6 +198,7 @@ struct gspca_dev { struct mutex read_lock; /* read protection */ struct mutex queue_lock; /* ISOC queue protection */ int usb_err; /* USB error - protected by usb_lock */ + u16 pkt_size; /* ISOC packet size */ #ifdef CONFIG_PM char frozen; /* suspend - resume */ #endif @@ -209,7 +209,7 @@ struct gspca_dev { __u8 iface; /* USB interface number */ __u8 alt; /* USB alternate setting */ __u8 nbalt; /* number of USB alternate settings */ - u16 pkt_size; /* ISOC packet size */ + u8 audio; /* presence of audio device */ }; int gspca_dev_probe(struct usb_interface *intf, @@ -217,12 +217,16 @@ int gspca_dev_probe(struct usb_interface *intf, const struct sd_desc *sd_desc, int dev_size, struct module *module); +int gspca_dev_probe2(struct usb_interface *intf, + const struct usb_device_id *id, + const struct sd_desc *sd_desc, + int dev_size, + struct module *module); void gspca_disconnect(struct usb_interface *intf); void gspca_frame_add(struct gspca_dev *gspca_dev, enum gspca_packet_type packet_type, const u8 *data, int len); -struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev); #ifdef CONFIG_PM int gspca_suspend(struct usb_interface *intf, pm_message_t message); int gspca_resume(struct usb_interface *intf); diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c index 84ecd56..12d9cf4 100644 --- a/drivers/media/video/gspca/jeilinj.c +++ b/drivers/media/video/gspca/jeilinj.c @@ -50,7 +50,7 @@ struct sd { struct workqueue_struct *work_thread; u8 quality; /* image quality */ u8 jpegqual; /* webcam quality */ - u8 *jpeg_hdr; + u8 jpeg_hdr[JPEG_HDR_SZ]; }; struct jlj_command { @@ -282,7 +282,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev) destroy_workqueue(dev->work_thread); dev->work_thread = NULL; mutex_lock(&gspca_dev->usb_lock); - kfree(dev->jpeg_hdr); } /* this function is called at probe and resume time */ @@ -298,9 +297,6 @@ static int sd_start(struct gspca_dev *gspca_dev) int ret; /* create the JPEG header */ - dev->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); - if (dev->jpeg_hdr == NULL) - return -ENOMEM; jpeg_define(dev->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ jpeg_set_qual(dev->jpeg_hdr, dev->quality); diff --git a/drivers/media/video/gspca/m5602/m5602_bridge.h b/drivers/media/video/gspca/m5602/m5602_bridge.h index 1127a40..51af3ee 100644 --- a/drivers/media/video/gspca/m5602/m5602_bridge.h +++ b/drivers/media/video/gspca/m5602/m5602_bridge.h @@ -19,6 +19,7 @@ #ifndef M5602_BRIDGE_H_ #define M5602_BRIDGE_H_ +#include <linux/slab.h> #include "gspca.h" #define MODULE_NAME "ALi m5602" diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c index 4294c75..b073d66 100644 --- a/drivers/media/video/gspca/m5602/m5602_core.c +++ b/drivers/media/video/gspca/m5602/m5602_core.c @@ -305,30 +305,23 @@ static void m5602_urb_complete(struct gspca_dev *gspca_dev, sd->frame_count); } else { - struct gspca_frame *frame; int cur_frame_len; - frame = gspca_get_i_frame(gspca_dev); - if (frame == NULL) { - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - - cur_frame_len = frame->data_end - frame->data; + cur_frame_len = gspca_dev->image_len; /* Remove urb header */ data += 4; len -= 4; - if (cur_frame_len + len <= frame->v4l2_buf.length) { + if (cur_frame_len + len <= gspca_dev->frsz) { PDEBUG(D_FRAM, "Continuing frame %d copying %d bytes", sd->frame_count, len); gspca_frame_add(gspca_dev, INTER_PACKET, data, len); - } else if (frame->v4l2_buf.length - cur_frame_len > 0) { + } else { /* Add the remaining data up to frame size */ gspca_frame_add(gspca_dev, INTER_PACKET, data, - frame->v4l2_buf.length - cur_frame_len); + gspca_dev->frsz - cur_frame_len); } } } diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/drivers/media/video/gspca/m5602/m5602_s5k83a.c index 6b3be4f..fbd9154 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k83a.c +++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.c @@ -17,7 +17,6 @@ */ #include <linux/kthread.h> -#include <linux/slab.h> #include "m5602_s5k83a.h" static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val); diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c index 3d9229e..031f719 100644 --- a/drivers/media/video/gspca/mars.c +++ b/drivers/media/video/gspca/mars.c @@ -41,7 +41,7 @@ struct sd { #define QUALITY_MAX 70 #define QUALITY_DEF 50 - u8 *jpeg_hdr; + u8 jpeg_hdr[JPEG_HDR_SZ]; }; /* V4L2 controls supported by the driver */ @@ -200,9 +200,6 @@ static int sd_start(struct gspca_dev *gspca_dev) int i; /* create the JPEG header */ - sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); - if (!sd->jpeg_hdr) - return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); @@ -317,13 +314,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) PDEBUG(D_ERR, "Camera Stop failed"); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - kfree(sd->jpeg_hdr); -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ int len) /* iso packet length */ @@ -486,7 +476,6 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .get_jcomp = sd_get_jcomp, .set_jcomp = sd_set_jcomp, diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index f36e11a..2b2cbdb 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -41,6 +41,11 @@ #include <linux/input.h> #include "gspca.h" +/* The jpeg_hdr is used by w996Xcf only */ +/* The CONEX_CAM define for jpeg.h needs renaming, now its used here too */ +#define CONEX_CAM +#include "jpeg.h" + MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("OV519 USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -90,6 +95,7 @@ struct sd { #define QUALITY_DEF 50 __u8 stopped; /* Streaming is temporarily paused */ + __u8 first_frame; __u8 frame_rate; /* current Framerate */ __u8 clockdiv; /* clockdiv override */ @@ -115,7 +121,7 @@ struct sd { int sensor_height; int sensor_reg_cache[256]; - u8 *jpeg_hdr; + u8 jpeg_hdr[JPEG_HDR_SZ]; }; /* Note this is a bit of a hack, but the w9968cf driver needs the code for all @@ -3147,7 +3153,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->autobrightness = AUTOBRIGHT_DEF; if (sd->sensor == SEN_OV7670) { sd->freq = OV7670_FREQ_DEF; - gspca_dev->ctrl_dis = 1 << FREQ_IDX; + gspca_dev->ctrl_dis = (1 << FREQ_IDX) | (1 << COLOR_IDX); } else { sd->freq = FREQ_DEF; gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | @@ -3961,6 +3967,8 @@ static int sd_start(struct gspca_dev *gspca_dev) sd_reset_snapshot(gspca_dev); sd->snapshot_pressed = 0; + sd->first_frame = 3; + ret = ov51x_restart(sd); if (ret < 0) goto out; @@ -4153,13 +4161,23 @@ static void ovfx2_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ int len) /* iso packet length */ { + struct sd *sd = (struct sd *) gspca_dev; + + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); + /* A short read signals EOF */ if (len < OVFX2_BULK_SIZE) { - gspca_frame_add(gspca_dev, LAST_PACKET, data, len); + /* If the frame is short, and it is one of the first ones + the sensor and bridge are still syncing, so drop it. */ + if (sd->first_frame) { + sd->first_frame--; + if (gspca_dev->image_len < + sd->gspca_dev.width * sd->gspca_dev.height) + gspca_dev->last_packet_type = DISCARD_PACKET; + } + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); - return; } - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static void sd_pkt_scan(struct gspca_dev *gspca_dev, diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index dc1e4ef..96cb3a9 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -987,13 +987,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data + 12, len - 12); /* If this packet is marked as EOF, end the frame */ } else if (data[1] & UVC_STREAM_EOF) { - struct gspca_frame *frame; - sd->last_pts = 0; - frame = gspca_get_i_frame(gspca_dev); - if (frame == NULL) - goto discard; - if (frame->data_end - frame->data + (len - 12) != + if (gspca_dev->image_len + len - 12 != gspca_dev->width * gspca_dev->height * 2) { PDEBUG(D_PACK, "wrong sized frame"); goto discard; diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index 2a68220..a66df07 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -402,7 +402,7 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, memcpy(gspca_dev->usb_buf, buffer, len); ret = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), - 1, /* request */ + 0, /* request */ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, /* value */ index, gspca_dev->usb_buf, len, @@ -804,7 +804,6 @@ static const unsigned char pac_jpeg_header2[] = { }; static void pac_start_frame(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, __u16 lines, __u16 samples_per_line) { unsigned char tmpbuf[4]; @@ -829,19 +828,13 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; - struct gspca_frame *frame; + u8 *image; unsigned char *sof; sof = pac_find_sof(&sd->sof_read, data, len); if (sof) { int n, lum_offset, footer_length; - frame = gspca_get_i_frame(gspca_dev); - if (frame == NULL) { - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - /* 6 bytes after the FF D9 EOF marker a number of lumination bytes are send corresponding to different parts of the image, the 14th and 15th byte after the EOF seem to @@ -852,16 +845,17 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* Finish decoding current frame */ n = (sof - data) - (footer_length + sizeof pac_sof_marker); if (n < 0) { - frame->data_end += n; + gspca_dev->image_len += n; n = 0; + } else { + gspca_frame_add(gspca_dev, INTER_PACKET, data, n); } - gspca_frame_add(gspca_dev, INTER_PACKET, - data, n); - if (gspca_dev->last_packet_type != DISCARD_PACKET && - frame->data_end[-2] == 0xff && - frame->data_end[-1] == 0xd9) - gspca_frame_add(gspca_dev, LAST_PACKET, - NULL, 0); + + image = gspca_dev->image; + if (image != NULL + && image[gspca_dev->image_len - 2] == 0xff + && image[gspca_dev->image_len - 1] == 0xd9) + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); n = sof - data; len -= n; @@ -877,7 +871,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* Start the new frame with the jpeg header */ /* The PAC7302 has the image rotated 90 degrees */ - pac_start_frame(gspca_dev, frame, + pac_start_frame(gspca_dev, gspca_dev->width, gspca_dev->height); } gspca_frame_add(gspca_dev, INTER_PACKET, data, len); @@ -1200,6 +1194,7 @@ static const struct usb_device_id device_table[] __devinitconst = { {USB_DEVICE(0x093a, 0x2621)}, {USB_DEVICE(0x093a, 0x2622), .driver_info = FL_VFLIP}, {USB_DEVICE(0x093a, 0x2624), .driver_info = FL_VFLIP}, + {USB_DEVICE(0x093a, 0x2625)}, {USB_DEVICE(0x093a, 0x2626)}, {USB_DEVICE(0x093a, 0x2628)}, {USB_DEVICE(0x093a, 0x2629), .driver_info = FL_VFLIP}, diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index 44fed96..1cb7e99 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -270,7 +270,7 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, memcpy(gspca_dev->usb_buf, buffer, len); ret = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), - 1, /* request */ + 0, /* request */ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, /* value */ index, gspca_dev->usb_buf, len, @@ -599,7 +599,6 @@ static const unsigned char pac_jpeg_header2[] = { }; static void pac_start_frame(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, __u16 lines, __u16 samples_per_line) { unsigned char tmpbuf[4]; @@ -624,19 +623,13 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; + u8 *image; unsigned char *sof; - struct gspca_frame *frame; sof = pac_find_sof(&sd->sof_read, data, len); if (sof) { int n, lum_offset, footer_length; - frame = gspca_get_i_frame(gspca_dev); - if (frame == NULL) { - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - /* 6 bytes after the FF D9 EOF marker a number of lumination bytes are send corresponding to different parts of the image, the 14th and 15th byte after the EOF seem to @@ -647,16 +640,16 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* Finish decoding current frame */ n = (sof - data) - (footer_length + sizeof pac_sof_marker); if (n < 0) { - frame->data_end += n; + gspca_dev->image_len += n; n = 0; + } else { + gspca_frame_add(gspca_dev, INTER_PACKET, data, n); } - gspca_frame_add(gspca_dev, INTER_PACKET, - data, n); - if (gspca_dev->last_packet_type != DISCARD_PACKET && - frame->data_end[-2] == 0xff && - frame->data_end[-1] == 0xd9) - gspca_frame_add(gspca_dev, LAST_PACKET, - NULL, 0); + image = gspca_dev->image; + if (image != NULL + && image[gspca_dev->image_len - 2] == 0xff + && image[gspca_dev->image_len - 1] == 0xd9) + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); n = sof - data; len -= n; @@ -671,7 +664,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, atomic_set(&sd->avg_lum, -1); /* Start the new frame with the jpeg header */ - pac_start_frame(gspca_dev, frame, + pac_start_frame(gspca_dev, gspca_dev->height, gspca_dev->width); } gspca_frame_add(gspca_dev, INTER_PACKET, data, len); diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index 644a7fd..83a718f 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -20,7 +20,6 @@ #ifdef CONFIG_INPUT #include <linux/input.h> -#include <linux/slab.h> #endif #include "gspca.h" @@ -89,7 +88,7 @@ struct sd { u8 hstart; u8 vstart; - u8 *jpeg_hdr; + u8 jpeg_hdr[JPEG_HDR_SZ]; u8 quality; u8 flags; @@ -2162,10 +2161,6 @@ static int sd_start(struct gspca_dev *gspca_dev) int height = gspca_dev->height; u8 fmt, scale = 0; - sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); - if (sd->jpeg_hdr == NULL) - return -ENOMEM; - jpeg_define(sd->jpeg_hdr, height, width, 0x21); jpeg_set_qual(sd->jpeg_hdr, sd->quality); @@ -2197,8 +2192,8 @@ static int sd_start(struct gspca_dev *gspca_dev) } configure_sensor_output(gspca_dev, mode); - reg_w(gspca_dev, 0x1100, sd->jpeg_hdr + JPEG_QT0_OFFSET, 64); - reg_w(gspca_dev, 0x1140, sd->jpeg_hdr + JPEG_QT1_OFFSET, 64); + reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64); + reg_w(gspca_dev, 0x1140, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64); reg_w(gspca_dev, 0x10fb, CLR_WIN(width, height), 5); reg_w(gspca_dev, 0x1180, HW_WIN(mode, sd->hstart, sd->vstart), 6); reg_w1(gspca_dev, 0x1189, scale); @@ -2226,12 +2221,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] & ~0x02); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - kfree(sd->jpeg_hdr); -} - static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) { struct sd *sd = (struct sd *) gspca_dev; @@ -2397,7 +2386,6 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, #ifdef CONFIG_INPUT .int_pkt_scan = sd_int_pkt_scan, diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index 95354a3..204bb3a 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -1251,16 +1251,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW) { /* In raw mode we sometimes get some garbage after the frame ignore this */ - struct gspca_frame *frame; int used; int size = cam->cam_mode[gspca_dev->curr_mode].sizeimage; - frame = gspca_get_i_frame(gspca_dev); - if (frame == NULL) { - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - used = frame->data_end - frame->data; + used = gspca_dev->image_len; if (used + len > size) len = size - used; } diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 176c5b3..ee17b03 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -22,7 +22,6 @@ #define MODULE_NAME "sonixj" #include <linux/input.h> -#include <linux/slab.h> #include "gspca.h" #include "jpeg.h" @@ -392,7 +391,7 @@ static const u8 sn_gc0307[0x1c] = { static const u8 sn_hv7131[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x03, 0x64, 0x00, 0x1a, 0x20, 0x20, 0x20, + 0x00, 0x03, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ 0x81, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ @@ -403,7 +402,7 @@ static const u8 sn_hv7131[0x1c] = { static const u8 sn_mi0360[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x61, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20, + 0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ 0x81, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ @@ -1644,6 +1643,7 @@ static void bridge_init(struct gspca_dev *gspca_dev, const u8 *sn9c1xx) { struct sd *sd = (struct sd *) gspca_dev; + u8 reg0102[2]; const u8 *reg9a; static const u8 reg9a_def[] = {0x00, 0x40, 0x20, 0x00, 0x00, 0x00}; @@ -1656,7 +1656,11 @@ static void bridge_init(struct gspca_dev *gspca_dev, reg_w1(gspca_dev, 0x01, sn9c1xx[1]); /* configure gpio */ - reg_w(gspca_dev, 0x01, &sn9c1xx[1], 2); + reg0102[0] = sn9c1xx[1]; + reg0102[1] = sn9c1xx[2]; + if (gspca_dev->audio) + reg0102[1] |= 0x04; /* keep the audio connection */ + reg_w(gspca_dev, 0x01, reg0102, 2); reg_w(gspca_dev, 0x08, &sn9c1xx[8], 2); reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5); switch (sd->sensor) { @@ -1737,13 +1741,12 @@ static void bridge_init(struct gspca_dev *gspca_dev, reg_w1(gspca_dev, 0x01, 0x40); break; case SENSOR_PO2030N: + case SENSOR_OV7660: reg_w1(gspca_dev, 0x01, 0x63); reg_w1(gspca_dev, 0x17, 0x20); reg_w1(gspca_dev, 0x01, 0x62); reg_w1(gspca_dev, 0x01, 0x42); break; - case SENSOR_OV7660: - /* fall thru */ case SENSOR_SP80708: reg_w1(gspca_dev, 0x01, 0x63); reg_w1(gspca_dev, 0x17, 0x20); @@ -1816,7 +1819,7 @@ static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; const u8 *sn9c1xx; - u8 regGpio[] = { 0x29, 0x74 }; + u8 regGpio[] = { 0x29, 0x74 }; /* with audio */ u8 regF1; /* setup a selector by bridge */ @@ -1856,7 +1859,7 @@ static int sd_init(struct gspca_dev *gspca_dev) po2030n_probe(gspca_dev); break; } - regGpio[1] = 0x70; + regGpio[1] = 0x70; /* no audio */ reg_w(gspca_dev, 0x01, regGpio, 2); break; default: @@ -2274,7 +2277,7 @@ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int i; - u8 reg1, reg2, reg17; + u8 reg1, reg17; const u8 *sn9c1xx; const u8 (*init)[8]; int mode; @@ -2304,23 +2307,6 @@ static int sd_start(struct gspca_dev *gspca_dev) /* initialize the sensor */ i2c_w_seq(gspca_dev, sensor_init[sd->sensor]); - switch (sd->sensor) { - case SENSOR_ADCM1700: - reg2 = 0x60; - break; - case SENSOR_OM6802: - reg2 = 0x71; - break; - case SENSOR_SP80708: - reg2 = 0x62; - break; - default: - reg2 = 0x40; - break; - } - reg_w1(gspca_dev, 0x02, reg2); - reg_w1(gspca_dev, 0x02, reg2); - reg_w1(gspca_dev, 0x15, sn9c1xx[0x15]); reg_w1(gspca_dev, 0x16, sn9c1xx[0x16]); reg_w1(gspca_dev, 0x12, sn9c1xx[0x12]); diff --git a/drivers/media/video/gspca/spca1528.c b/drivers/media/video/gspca/spca1528.c new file mode 100644 index 0000000..3f514eb --- /dev/null +++ b/drivers/media/video/gspca/spca1528.c @@ -0,0 +1,605 @@ +/* + * spca1528 subdriver + * + * Copyright (C) 2010 Jean-Francois Moine (http://moinejf.free.fr) + * + * 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 + * 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 + */ + +#define MODULE_NAME "spca1528" + +#include "gspca.h" +#include "jpeg.h" + +MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); +MODULE_DESCRIPTION("SPCA1528 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + u8 brightness; + u8 contrast; + u8 hue; + u8 color; + u8 sharpness; + + u8 pkt_seq; + + u8 jpeg_hdr[JPEG_HDR_SZ]; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val); +static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolor(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolor(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); + +static const struct ctrl sd_ctrls[] = { + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, +#define BRIGHTNESS_DEF 128 + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 8, + .step = 1, +#define CONTRAST_DEF 1 + .default_value = CONTRAST_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { + { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = 0, + .maximum = 255, + .step = 1, +#define HUE_DEF 0 + .default_value = HUE_DEF, + }, + .set = sd_sethue, + .get = sd_gethue, + }, + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 8, + .step = 1, +#define COLOR_DEF 1 + .default_value = COLOR_DEF, + }, + .set = sd_setcolor, + .get = sd_getcolor, + }, + { + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = 0, + .maximum = 255, + .step = 1, +#define SHARPNESS_DEF 0 + .default_value = SHARPNESS_DEF, + }, + .set = sd_setsharpness, + .get = sd_getsharpness, + }, +}; + +static const struct v4l2_pix_format vga_mode[] = { +/* (does not work correctly) + {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 5 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, +*/ + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 4 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, +}; + +/* read <len> bytes to gspca usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + u8 req, + u16 index, + int len) +{ +#if USB_BUF_SZ < 64 +#error "USB buffer too small" +#endif + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x0000, /* value */ + index, + gspca_dev->usb_buf, len, + 500); + PDEBUG(D_USBI, "GET %02x 0000 %04x %02x", req, index, + gspca_dev->usb_buf[0]); + if (ret < 0) { + PDEBUG(D_ERR, "reg_r err %d", ret); + gspca_dev->usb_err = ret; + } +} + +static void reg_w(struct gspca_dev *gspca_dev, + u8 req, + u16 value, + u16 index) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + PDEBUG(D_USBO, "SET %02x %04x %04x", req, value, index); + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, + NULL, 0, 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_w err %d", ret); + gspca_dev->usb_err = ret; + } +} + +static void reg_wb(struct gspca_dev *gspca_dev, + u8 req, + u16 value, + u16 index, + u8 byte) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + PDEBUG(D_USBO, "SET %02x %04x %04x %02x", req, value, index, byte); + gspca_dev->usb_buf[0] = byte; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, + gspca_dev->usb_buf, 1, 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_w err %d", ret); + gspca_dev->usb_err = ret; + } +} + +static void wait_status_0(struct gspca_dev *gspca_dev) +{ + int i; + + i = 20; + do { + reg_r(gspca_dev, 0x21, 0x0000, 1); + if (gspca_dev->usb_buf[0] == 0) + return; + msleep(30); + } while (--i > 0); + PDEBUG(D_ERR, "wait_status_0 timeout"); + gspca_dev->usb_err = -ETIME; +} + +static void wait_status_1(struct gspca_dev *gspca_dev) +{ + int i; + + i = 10; + do { + reg_r(gspca_dev, 0x21, 0x0001, 1); + msleep(10); + if (gspca_dev->usb_buf[0] == 1) { + reg_wb(gspca_dev, 0x21, 0x0000, 0x0001, 0x00); + reg_r(gspca_dev, 0x21, 0x0001, 1); + return; + } + } while (--i > 0); + PDEBUG(D_ERR, "wait_status_1 timeout"); + gspca_dev->usb_err = -ETIME; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_wb(gspca_dev, 0xc0, 0x0000, 0x00c0, sd->brightness); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_wb(gspca_dev, 0xc1, 0x0000, 0x00c1, sd->contrast); +} + +static void sethue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_wb(gspca_dev, 0xc2, 0x0000, 0x0000, sd->hue); +} + +static void setcolor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_wb(gspca_dev, 0xc3, 0x0000, 0x00c3, sd->color); +} + +static void setsharpness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_wb(gspca_dev, 0xc4, 0x0000, 0x00c4, sd->sharpness); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + + gspca_dev->cam.cam_mode = vga_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); + gspca_dev->cam.npkt = 128; /* number of packets per ISOC message */ + /*fixme: 256 in ms-win traces*/ + + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->hue = HUE_DEF; + sd->color = COLOR_DEF; + sd->sharpness = SHARPNESS_DEF; + + gspca_dev->nbalt = 4; /* use alternate setting 3 */ + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev, 0x00, 0x0001, 0x2067); + reg_w(gspca_dev, 0x00, 0x00d0, 0x206b); + reg_w(gspca_dev, 0x00, 0x0000, 0x206c); + reg_w(gspca_dev, 0x00, 0x0001, 0x2069); + msleep(8); + reg_w(gspca_dev, 0x00, 0x00c0, 0x206b); + reg_w(gspca_dev, 0x00, 0x0000, 0x206c); + reg_w(gspca_dev, 0x00, 0x0001, 0x2069); + + reg_r(gspca_dev, 0x20, 0x0000, 1); + reg_r(gspca_dev, 0x20, 0x0000, 5); + reg_r(gspca_dev, 0x23, 0x0000, 64); + PDEBUG(D_PROBE, "%s%s", &gspca_dev->usb_buf[0x1c], + &gspca_dev->usb_buf[0x30]); + reg_r(gspca_dev, 0x23, 0x0001, 64); + return gspca_dev->usb_err; +} + +/* function called at start time before URB creation */ +static int sd_isoc_init(struct gspca_dev *gspca_dev) +{ + u8 mode; + + reg_r(gspca_dev, 0x00, 0x2520, 1); + wait_status_0(gspca_dev); + reg_w(gspca_dev, 0xc5, 0x0003, 0x0000); + wait_status_1(gspca_dev); + + wait_status_0(gspca_dev); + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + reg_wb(gspca_dev, 0x25, 0x0000, 0x0004, mode); + reg_r(gspca_dev, 0x25, 0x0004, 1); + reg_wb(gspca_dev, 0x27, 0x0000, 0x0000, 0x06); + reg_r(gspca_dev, 0x27, 0x0000, 1); + return gspca_dev->usb_err; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* initialize the JPEG header */ + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x22); /* JPEG 411 */ + + /* the JPEG quality seems to be 82% */ + jpeg_set_qual(sd->jpeg_hdr, 82); + + /* set the controls */ + setbrightness(gspca_dev); + setcontrast(gspca_dev); + sethue(gspca_dev); + setcolor(gspca_dev); + setsharpness(gspca_dev); + + msleep(5); + reg_r(gspca_dev, 0x00, 0x2520, 1); + msleep(8); + + /* start the capture */ + wait_status_0(gspca_dev); + reg_w(gspca_dev, 0x31, 0x0000, 0x0004); + wait_status_1(gspca_dev); + wait_status_0(gspca_dev); + msleep(200); + + sd->pkt_seq = 0; + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* stop the capture */ + wait_status_0(gspca_dev); + reg_w(gspca_dev, 0x31, 0x0000, 0x0000); + wait_status_1(gspca_dev); + wait_status_0(gspca_dev); +} + +/* move a packet adding 0x00 after 0xff */ +static void add_packet(struct gspca_dev *gspca_dev, + u8 *data, + int len) +{ + int i; + + i = 0; + do { + if (data[i] == 0xff) { + gspca_frame_add(gspca_dev, INTER_PACKET, + data, i + 1); + len -= i; + data += i; + *data = 0x00; + i = 0; + } + } while (++i < len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + static const u8 ffd9[] = {0xff, 0xd9}; + + /* image packets start with: + * 02 8n + * with <n> bit: + * 0x01: even (0) / odd (1) image + * 0x02: end of image when set + */ + if (len < 3) + return; /* empty packet */ + if (*data == 0x02) { + if (data[1] & 0x02) { + sd->pkt_seq = !(data[1] & 1); + add_packet(gspca_dev, data + 2, len - 2); + gspca_frame_add(gspca_dev, LAST_PACKET, + ffd9, 2); + return; + } + if ((data[1] & 1) != sd->pkt_seq) + goto err; + if (gspca_dev->last_packet_type == LAST_PACKET) + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + add_packet(gspca_dev, data + 2, len - 2); + return; + } +err: + gspca_dev->last_packet_type = DISCARD_PACKET; +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hue = val; + if (gspca_dev->streaming) + sethue(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->hue; + return 0; +} + +static int sd_setcolor(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->color = val; + if (gspca_dev->streaming) + setcolor(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_getcolor(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->color; + return 0; +} + +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->sharpness = val; + if (gspca_dev->streaming) + setsharpness(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->sharpness; + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .init = sd_init, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x04fc, 0x1528)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + /* the video interface for isochronous transfer is 1 */ + if (intf->cur_altsetting->desc.bInterfaceNumber != 1) + return -ENODEV; + + return gspca_dev_probe2(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + int ret; + + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; + info("registered"); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + info("deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c index b866c73..c02beb6 100644 --- a/drivers/media/video/gspca/spca500.c +++ b/drivers/media/video/gspca/spca500.c @@ -57,7 +57,7 @@ struct sd { #define PalmPixDC85 13 #define ToptroIndus 14 - u8 *jpeg_hdr; + u8 jpeg_hdr[JPEG_HDR_SZ]; }; /* V4L2 controls supported by the driver */ @@ -669,9 +669,6 @@ static int sd_start(struct gspca_dev *gspca_dev) __u8 xmult, ymult; /* create the JPEG header */ - sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); - if (!sd->jpeg_hdr) - return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x22); /* JPEG 411 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); @@ -891,13 +888,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) gspca_dev->usb_buf[0]); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - kfree(sd->jpeg_hdr); -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ int len) /* iso packet length */ @@ -1055,7 +1045,6 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .get_jcomp = sd_get_jcomp, .set_jcomp = sd_set_jcomp, diff --git a/drivers/media/video/gspca/sq930x.c b/drivers/media/video/gspca/sq930x.c new file mode 100644 index 0000000..37cee5e --- /dev/null +++ b/drivers/media/video/gspca/sq930x.c @@ -0,0 +1,1402 @@ +/* + * SQ930x subdriver + * + * Copyright (C) 2010 Jean-François Moine <http://moinejf.free.fr> + * Copyright (C) 2006 -2008 Gerard Klaver <gerard at gkall dot hobby dot nl> + * Copyright (C) 2007 Sam Revitch <samr7@cs.washington.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 + * 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 + */ + +#define MODULE_NAME "sq930x" + +#include "gspca.h" +#include "jpeg.h" + +MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>\n" + "Gerard Klaver <gerard at gkall dot hobby dot nl\n" + "Sam Revitch <samr7@cs.washington.edu>"); +MODULE_DESCRIPTION("GSPCA/SQ930x USB Camera Driver"); +MODULE_LICENSE("GPL"); + +#define BULK_TRANSFER_LEN 5128 + +/* Structure to hold all of our device specific stuff */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + u16 expo; + u8 gain; + + u8 quality; /* webcam quality 0..3 */ +#define QUALITY_DEF 1 + + u8 gpio[2]; + + u8 eof_len; + u8 do_ctrl; + + u8 sensor; +enum { + SENSOR_ICX098BQ, + SENSOR_LZ24BP, + SENSOR_MI0360, + SENSOR_MT9V111, + SENSOR_OV7660, + SENSOR_OV9630, +} sensors; + u8 type; +#define Generic 0 +#define Creative_live_motion 1 + + u8 jpeg_hdr[JPEG_HDR_SZ]; +}; + +static int sd_setexpo(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getexpo(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); + +static const struct ctrl sd_ctrls[] = { + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0x0001, + .maximum = 0x0fff, + .step = 1, +#define EXPO_DEF 0x027d + .default_value = EXPO_DEF, + }, + .set = sd_setexpo, + .get = sd_getexpo, + }, + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0x01, + .maximum = 0xff, + .step = 1, +#define GAIN_DEF 0x61 + .default_value = GAIN_DEF, + }, + .set = sd_setgain, + .get = sd_getgain, + }, +}; + +static struct v4l2_pix_format vga_mode[] = { + {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 5 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 4 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, +}; + +/* JPEG quality indexed by webcam quality */ +#define QUAL_0 90 +#define QUAL_1 85 +#define QUAL_2 75 +#define QUAL_3 70 +static const u8 quality_tb[4] = { QUAL_0, QUAL_1, QUAL_2, QUAL_3 }; + +/* sq930x registers */ +#define SQ930_CTRL_UCBUS_IO 0x0001 +#define SQ930_CTRL_I2C_IO 0x0002 +#define SQ930_CTRL_GPIO 0x0005 +#define SQ930_CTRL_CAP_START 0x0010 +#define SQ930_CTRL_CAP_STOP 0x0011 +#define SQ930_CTRL_SET_EXPOSURE 0x001d +#define SQ930_CTRL_RESET 0x001e +#define SQ930_CTRL_GET_DEV_INFO 0x001f + +/* gpio 1 (8..15) */ +#define SQ930_GPIO_DFL_I2C_SDA 0x0001 +#define SQ930_GPIO_DFL_I2C_SCL 0x0002 +#define SQ930_GPIO_RSTBAR 0x0004 +#define SQ930_GPIO_EXTRA1 0x0040 +#define SQ930_GPIO_EXTRA2 0x0080 +/* gpio 3 (24..31) */ +#define SQ930_GPIO_POWER 0x0200 +#define SQ930_GPIO_DFL_LED 0x1000 + +struct ucbus_write_cmd { + u16 bw_addr; + u8 bw_data; +}; +struct i2c_write_cmd { + u8 reg; + u16 val; +}; + +static const struct ucbus_write_cmd icx098bq_start_0[] = { + {0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xce}, + {0xf802, 0xc1}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x0e}, + {0xf80a, 0x01}, {0xf80b, 0xee}, {0xf807, 0x60}, {0xf80c, 0x02}, + {0xf80d, 0xf0}, {0xf80e, 0x03}, {0xf80f, 0x0a}, {0xf81c, 0x02}, + {0xf81d, 0xf0}, {0xf81e, 0x03}, {0xf81f, 0x0a}, {0xf83a, 0x00}, + {0xf83b, 0x10}, {0xf83c, 0x00}, {0xf83d, 0x4e}, {0xf810, 0x04}, + {0xf811, 0x00}, {0xf812, 0x02}, {0xf813, 0x10}, {0xf803, 0x00}, + {0xf814, 0x01}, {0xf815, 0x18}, {0xf816, 0x00}, {0xf817, 0x48}, + {0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c}, + {0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff}, + {0xf823, 0x07}, {0xf824, 0xff}, {0xf825, 0x03}, {0xf826, 0xff}, + {0xf827, 0x06}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff}, + {0xf82b, 0x0c}, {0xf82c, 0xfd}, {0xf82d, 0x01}, {0xf82e, 0x00}, + {0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00}, + {0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24}, + {0xf854, 0x00}, {0xf855, 0x18}, {0xf856, 0x00}, {0xf857, 0x3c}, + {0xf858, 0x00}, {0xf859, 0x0c}, {0xf85a, 0x00}, {0xf85b, 0x30}, + {0xf85c, 0x00}, {0xf85d, 0x0c}, {0xf85e, 0x00}, {0xf85f, 0x30}, + {0xf860, 0x00}, {0xf861, 0x48}, {0xf862, 0x01}, {0xf863, 0xdc}, + {0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0}, + {0xf868, 0xff}, {0xf869, 0x70}, {0xf86c, 0xff}, {0xf86d, 0x00}, + {0xf86a, 0xff}, {0xf86b, 0x48}, {0xf86e, 0xff}, {0xf86f, 0x00}, + {0xf870, 0x01}, {0xf871, 0xdb}, {0xf872, 0x01}, {0xf873, 0xfa}, + {0xf874, 0x01}, {0xf875, 0xdb}, {0xf876, 0x01}, {0xf877, 0xfa}, + {0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff}, + {0xf800, 0x03} +}; +static const struct ucbus_write_cmd icx098bq_start_1[] = { + {0xf5f0, 0x00}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xc0}, + {0xf5f0, 0x49}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xc0}, + {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00}, + {0xf5f9, 0x00} +}; + +static const struct ucbus_write_cmd icx098bq_start_2[] = { + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x82}, {0xf806, 0x00}, + {0xf807, 0x7f}, {0xf800, 0x03}, + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x40}, {0xf806, 0x00}, + {0xf807, 0x7f}, {0xf800, 0x03}, + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xcf}, {0xf806, 0xd0}, + {0xf807, 0x7f}, {0xf800, 0x03}, + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00}, + {0xf807, 0x7f}, {0xf800, 0x03} +}; + +static const struct ucbus_write_cmd lz24bp_start_0[] = { + {0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xbe}, + {0xf802, 0xc6}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x06}, + {0xf80a, 0x01}, {0xf80b, 0xfe}, {0xf807, 0x84}, {0xf80c, 0x02}, + {0xf80d, 0xf7}, {0xf80e, 0x03}, {0xf80f, 0x0b}, {0xf81c, 0x00}, + {0xf81d, 0x49}, {0xf81e, 0x03}, {0xf81f, 0x0b}, {0xf83a, 0x00}, + {0xf83b, 0x01}, {0xf83c, 0x00}, {0xf83d, 0x6b}, {0xf810, 0x03}, + {0xf811, 0x10}, {0xf812, 0x02}, {0xf813, 0x6f}, {0xf803, 0x00}, + {0xf814, 0x00}, {0xf815, 0x44}, {0xf816, 0x00}, {0xf817, 0x48}, + {0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c}, + {0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff}, + {0xf823, 0x07}, {0xf824, 0xfd}, {0xf825, 0x07}, {0xf826, 0xf0}, + {0xf827, 0x0c}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff}, + {0xf82b, 0x0c}, {0xf82c, 0xfc}, {0xf82d, 0x01}, {0xf82e, 0x00}, + {0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00}, + {0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24}, + {0xf854, 0x00}, {0xf855, 0x0c}, {0xf856, 0x00}, {0xf857, 0x30}, + {0xf858, 0x00}, {0xf859, 0x18}, {0xf85a, 0x00}, {0xf85b, 0x3c}, + {0xf85c, 0x00}, {0xf85d, 0x18}, {0xf85e, 0x00}, {0xf85f, 0x3c}, + {0xf860, 0xff}, {0xf861, 0x37}, {0xf862, 0xff}, {0xf863, 0x1d}, + {0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0}, + {0xf868, 0x00}, {0xf869, 0x37}, {0xf86c, 0x02}, {0xf86d, 0x1d}, + {0xf86a, 0x00}, {0xf86b, 0x37}, {0xf86e, 0x02}, {0xf86f, 0x1d}, + {0xf870, 0x01}, {0xf871, 0xc6}, {0xf872, 0x02}, {0xf873, 0x04}, + {0xf874, 0x01}, {0xf875, 0xc6}, {0xf876, 0x02}, {0xf877, 0x04}, + {0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff}, + {0xf800, 0x03} +}; +static const struct ucbus_write_cmd lz24bp_start_1_gen[] = { + {0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xb3}, + {0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xb3}, + {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00}, + {0xf5f9, 0x00} +}; + +static const struct ucbus_write_cmd lz24bp_start_1_clm[] = { + {0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88}, + {0xf5f4, 0xc0}, + {0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88}, + {0xf5f4, 0xc0}, + {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00}, + {0xf5f9, 0x00} +}; + +static const struct ucbus_write_cmd lz24bp_start_2[] = { + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x80}, {0xf806, 0x00}, + {0xf807, 0x7f}, {0xf800, 0x03}, + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x4e}, {0xf806, 0x00}, + {0xf807, 0x7f}, {0xf800, 0x03}, + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xc0}, {0xf806, 0x48}, + {0xf807, 0x7f}, {0xf800, 0x03}, + {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00}, + {0xf807, 0x7f}, {0xf800, 0x03} +}; + +static const struct ucbus_write_cmd mi0360_start_0[] = { + {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0xcc}, {0xf333, 0xcc}, + {0xf334, 0xcc}, {0xf335, 0xcc}, {0xf33f, 0x00} +}; +static const struct i2c_write_cmd mi0360_init_23[] = { + {0x30, 0x0040}, /* reserved - def 0x0005 */ + {0x31, 0x0000}, /* reserved - def 0x002a */ + {0x34, 0x0100}, /* reserved - def 0x0100 */ + {0x3d, 0x068f}, /* reserved - def 0x068f */ +}; +static const struct i2c_write_cmd mi0360_init_24[] = { + {0x03, 0x01e5}, /* window height */ + {0x04, 0x0285}, /* window width */ +}; +static const struct i2c_write_cmd mi0360_init_25[] = { + {0x35, 0x0020}, /* global gain */ + {0x2b, 0x0020}, /* green1 gain */ + {0x2c, 0x002a}, /* blue gain */ + {0x2d, 0x0028}, /* red gain */ + {0x2e, 0x0020}, /* green2 gain */ +}; +static const struct ucbus_write_cmd mi0360_start_1[] = { + {0xf5f0, 0x11}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xa6}, + {0xf5f0, 0x51}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xa6}, + {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00}, + {0xf5f9, 0x00} +}; +static const struct i2c_write_cmd mi0360_start_2[] = { + {0x62, 0x041d}, /* reserved - def 0x0418 */ +}; +static const struct i2c_write_cmd mi0360_start_3[] = { + {0x05, 0x007b}, /* horiz blanking */ +}; +static const struct i2c_write_cmd mi0360_start_4[] = { + {0x05, 0x03f5}, /* horiz blanking */ +}; + +static const struct i2c_write_cmd mt9v111_init_0[] = { + {0x01, 0x0001}, /* select IFP/SOC registers */ + {0x06, 0x300c}, /* operating mode control */ + {0x08, 0xcc00}, /* output format control (RGB) */ + {0x01, 0x0004}, /* select core registers */ +}; +static const struct i2c_write_cmd mt9v111_init_1[] = { + {0x03, 0x01e5}, /* window height */ + {0x04, 0x0285}, /* window width */ +}; +static const struct i2c_write_cmd mt9v111_init_2[] = { + {0x30, 0x7800}, + {0x31, 0x0000}, + {0x07, 0x3002}, /* output control */ + {0x35, 0x0020}, /* global gain */ + {0x2b, 0x0020}, /* green1 gain */ + {0x2c, 0x0020}, /* blue gain */ + {0x2d, 0x0020}, /* red gain */ + {0x2e, 0x0020}, /* green2 gain */ +}; +static const struct ucbus_write_cmd mt9v111_start_1[] = { + {0xf5f0, 0x11}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xaa}, + {0xf5f0, 0x51}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80}, + {0xf5f4, 0xaa}, + {0xf5fa, 0x00}, {0xf5f6, 0x0a}, {0xf5f7, 0x0a}, {0xf5f8, 0x0a}, + {0xf5f9, 0x0a} +}; +static const struct i2c_write_cmd mt9v111_init_3[] = { + {0x62, 0x0405}, +}; +static const struct i2c_write_cmd mt9v111_init_4[] = { + {0x05, 0x00ce}, /* horizontal blanking */ +}; + +static const struct ucbus_write_cmd ov7660_start_0[] = { + {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0xc0}, + {0xf334, 0x39}, {0xf335, 0xe7}, {0xf33f, 0x03} +}; + +static const struct ucbus_write_cmd ov9630_start_0[] = { + {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0x00}, + {0xf334, 0x3e}, {0xf335, 0xf8}, {0xf33f, 0x03} +}; + +static const struct cap_s { + u8 cc_sizeid; + u8 cc_bytes[32]; +} capconfig[4][3] = { + [SENSOR_ICX098BQ] = { + {0, /* JPEG, 160x120 */ + {0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee, + 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x02, 0x8b, 0x00, 0x8b, 0x00, 0x41, 0x01, 0x41, + 0x01, 0x41, 0x01, 0x05, 0x40, 0x01, 0xf0, 0x00} }, + {2, /* JPEG, 320x240 */ + {0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee, + 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x02, 0xdf, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x3f, + 0x01, 0x00, 0x00, 0x05, 0x40, 0x01, 0xf0, 0x00} }, + {4, /* JPEG, 640x480 */ + {0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xf0, + 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x07, 0xe1, 0x01, 0xe1, 0x01, 0x3f, 0x01, 0x3f, + 0x01, 0x3f, 0x01, 0x05, 0x80, 0x02, 0xe0, 0x01} }, + }, + [SENSOR_LZ24BP] = { + {0, /* JPEG, 160x120 */ + {0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee, + 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x02, 0x8b, 0x00, 0x8b, 0x00, 0x41, 0x01, 0x41, + 0x01, 0x41, 0x01, 0x05, 0x40, 0x01, 0xf0, 0x00} }, + {2, /* JPEG, 320x240 */ + {0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee, + 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x02, 0xdf, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x3f, + 0x01, 0x00, 0x00, 0x05, 0x40, 0x01, 0xf0, 0x00} }, + {4, /* JPEG, 640x480 */ + {0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xf0, + 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x07, 0xe1, 0x01, 0xe1, 0x01, 0x3f, 0x01, 0x3f, + 0x01, 0x3f, 0x01, 0x05, 0x80, 0x02, 0xe0, 0x01} }, + }, + [SENSOR_MI0360] = { + {0, /* JPEG, 160x120 */ + {0x05, 0x3d, 0x20, 0x0b, 0x00, 0xbd, 0x02, 0x0b, + 0x02, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x9f, 0x00, 0x9f, + 0x00, 0x9f, 0x01, 0x05, 0xa0, 0x00, 0x80, 0x00} }, + {2, /* JPEG, 320x240 */ + {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1, +/*fixme 03 e3 */ + 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x02, 0xdf, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x3f, + 0x01, 0x00, 0x00, 0x05, 0x40, 0x01, 0xf0, 0x00} }, + {4, /* JPEG, 640x480 */ + {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe3, + 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x07, 0xe1, 0x01, 0xe1, 0x01, 0x3f, 0x01, 0x3f, + 0x01, 0x3f, 0x01, 0x05, 0x80, 0x02, 0xe0, 0x01} }, + }, + [SENSOR_MT9V111] = { + {0, /* JPEG, 160x120 */ + {0x05, 0x3d, 0x20, 0x0b, 0x00, 0xbd, 0x02, 0x0b, + 0x02, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x9f, 0x00, 0x9f, + 0x00, 0x9f, 0x01, 0x05, 0xa0, 0x00, 0x80, 0x00} }, + {2, /* JPEG, 320x240 */ + {0x01, 0x02, 0x20, 0x03, 0x20, 0x82, 0x02, 0xe3, + 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x02, 0xdf, 0x01, 0x00, 0x00, 0x3f, 0x01, 0x3f, + 0x01, 0x00, 0x00, 0x05, 0x40, 0x01, 0xf0, 0x00} }, + {4, /* JPEG, 640x480 */ + {0x01, 0x02, 0x20, 0x03, 0x20, 0x82, 0x02, 0xe3, + 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8, + 0x07, 0xe1, 0x01, 0xe1, 0x01, 0x3f, 0x01, 0x3f, + 0x01, 0x3f, 0x01, 0x05, 0x80, 0x02, 0xe0, 0x01} }, + }, +}; + +struct sensor_s { + const char *name; + u8 i2c_addr; + u8 i2c_dum; + u8 gpio[5]; + u8 cmd_len; + const struct ucbus_write_cmd *cmd; +}; + +static const struct sensor_s sensor_tb[] = { + [SENSOR_ICX098BQ] = { + "icx098bp", + 0x00, 0x00, + {0, + SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, + SQ930_GPIO_DFL_I2C_SDA, + 0, + SQ930_GPIO_RSTBAR + }, + 8, icx098bq_start_0 + }, + [SENSOR_LZ24BP] = { + "lz24bp", + 0x00, 0x00, + {0, + SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, + SQ930_GPIO_DFL_I2C_SDA, + 0, + SQ930_GPIO_RSTBAR + }, + 8, lz24bp_start_0 + }, + [SENSOR_MI0360] = { + "mi0360", + 0x5d, 0x80, + {SQ930_GPIO_RSTBAR, + SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, + SQ930_GPIO_DFL_I2C_SDA, + 0, + 0 + }, + 7, mi0360_start_0 + }, + [SENSOR_MT9V111] = { + "mt9v111", + 0x5c, 0x7f, + {SQ930_GPIO_RSTBAR, + SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, + SQ930_GPIO_DFL_I2C_SDA, + 0, + 0 + }, + 7, mi0360_start_0 + }, + [SENSOR_OV7660] = { + "ov7660", + 0x21, 0x00, + {0, + SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, + SQ930_GPIO_DFL_I2C_SDA, + 0, + SQ930_GPIO_RSTBAR + }, + 7, ov7660_start_0 + }, + [SENSOR_OV9630] = { + "ov9630", + 0x30, 0x00, + {0, + SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL, + SQ930_GPIO_DFL_I2C_SDA, + 0, + SQ930_GPIO_RSTBAR + }, + 7, ov9630_start_0 + }, +}; + +static void reg_r(struct gspca_dev *gspca_dev, + u16 value, int len) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + 0x0c, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, 0, gspca_dev->usb_buf, len, + 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_r %04x failed %d", value, ret); + gspca_dev->usb_err = ret; + } +} + +static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + PDEBUG(D_USBO, "reg_w v: %04x i: %04x", value, index); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x0c, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, + 500); + msleep(30); + if (ret < 0) { + PDEBUG(D_ERR, "reg_w %04x %04x failed %d", value, index, ret); + gspca_dev->usb_err = ret; + } +} + +static void reg_wb(struct gspca_dev *gspca_dev, u16 value, u16 index, + const u8 *data, int len) +{ + int ret; + + if (gspca_dev->usb_err < 0) + return; + PDEBUG(D_USBO, "reg_wb v: %04x i: %04x %02x...%02x", + value, index, *data, data[len - 1]); + memcpy(gspca_dev->usb_buf, data, len); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x0c, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, gspca_dev->usb_buf, len, + 1000); + msleep(30); + if (ret < 0) { + PDEBUG(D_ERR, "reg_wb %04x %04x failed %d", value, index, ret); + gspca_dev->usb_err = ret; + } +} + +static void i2c_write(struct sd *sd, + const struct i2c_write_cmd *cmd, + int ncmds) +{ + struct gspca_dev *gspca_dev = &sd->gspca_dev; + const struct sensor_s *sensor; + u16 val, idx; + u8 *buf; + int ret; + + if (gspca_dev->usb_err < 0) + return; + + sensor = &sensor_tb[sd->sensor]; + + val = (sensor->i2c_addr << 8) | SQ930_CTRL_I2C_IO; + idx = (cmd->val & 0xff00) | cmd->reg; + + buf = gspca_dev->usb_buf; + *buf++ = sensor->i2c_dum; + *buf++ = cmd->val; + + while (--ncmds > 0) { + cmd++; + *buf++ = cmd->reg; + *buf++ = cmd->val >> 8; + *buf++ = sensor->i2c_dum; + *buf++ = cmd->val; + } + + PDEBUG(D_USBO, "i2c_w v: %04x i: %04x %02x...%02x", + val, idx, gspca_dev->usb_buf[0], buf[-1]); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x0c, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + val, idx, + gspca_dev->usb_buf, buf - gspca_dev->usb_buf, + 500); + if (ret < 0) { + PDEBUG(D_ERR, "i2c_write failed %d", ret); + gspca_dev->usb_err = ret; + } +} + +static void ucbus_write(struct gspca_dev *gspca_dev, + const struct ucbus_write_cmd *cmd, + int ncmds, + int batchsize) +{ + u8 *buf; + u16 val, idx; + int len, ret; + + if (gspca_dev->usb_err < 0) + return; + +#ifdef GSPCA_DEBUG + if ((batchsize - 1) * 3 > USB_BUF_SZ) { + err("Bug: usb_buf overflow"); + gspca_dev->usb_err = -ENOMEM; + return; + } +#endif + + for (;;) { + len = ncmds; + if (len > batchsize) + len = batchsize; + ncmds -= len; + + val = (cmd->bw_addr << 8) | SQ930_CTRL_UCBUS_IO; + idx = (cmd->bw_data << 8) | (cmd->bw_addr >> 8); + + buf = gspca_dev->usb_buf; + while (--len > 0) { + cmd++; + *buf++ = cmd->bw_addr; + *buf++ = cmd->bw_addr >> 8; + *buf++ = cmd->bw_data; + } + if (buf != gspca_dev->usb_buf) + PDEBUG(D_USBO, "ucbus v: %04x i: %04x %02x...%02x", + val, idx, + gspca_dev->usb_buf[0], buf[-1]); + else + PDEBUG(D_USBO, "ucbus v: %04x i: %04x", + val, idx); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x0c, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + val, idx, + gspca_dev->usb_buf, buf - gspca_dev->usb_buf, + 500); + if (ret < 0) { + PDEBUG(D_ERR, "ucbus_write failed %d", ret); + gspca_dev->usb_err = ret; + return; + } + msleep(30); + if (ncmds <= 0) + break; + cmd++; + } +} + +static void gpio_set(struct sd *sd, u16 val, u16 mask) +{ + struct gspca_dev *gspca_dev = &sd->gspca_dev; + + if (mask & 0x00ff) { + sd->gpio[0] &= ~mask; + sd->gpio[0] |= val; + reg_w(gspca_dev, 0x0100 | SQ930_CTRL_GPIO, + ~sd->gpio[0] << 8); + } + mask >>= 8; + val >>= 8; + if (mask) { + sd->gpio[1] &= ~mask; + sd->gpio[1] |= val; + reg_w(gspca_dev, 0x0300 | SQ930_CTRL_GPIO, + ~sd->gpio[1] << 8); + } +} + +static void gpio_init(struct sd *sd, + const u8 *gpio) +{ + gpio_set(sd, *gpio++, 0x000f); + gpio_set(sd, *gpio++, 0x000f); + gpio_set(sd, *gpio++, 0x000f); + gpio_set(sd, *gpio++, 0x000f); + gpio_set(sd, *gpio, 0x000f); +} + +static void bridge_init(struct sd *sd) +{ + static const struct ucbus_write_cmd clkfreq_cmd = { + 0xf031, 0 /* SQ930_CLKFREQ_60MHZ */ + }; + + ucbus_write(&sd->gspca_dev, &clkfreq_cmd, 1, 1); + + gpio_set(sd, SQ930_GPIO_POWER, 0xff00); +} + +static void cmos_probe(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + const struct sensor_s *sensor; + static const u8 probe_order[] = { +/* SENSOR_LZ24BP, (tested as ccd) */ + SENSOR_OV9630, + SENSOR_MI0360, + SENSOR_OV7660, + SENSOR_MT9V111, + }; + + for (i = 0; i < ARRAY_SIZE(probe_order); i++) { + sensor = &sensor_tb[probe_order[i]]; + ucbus_write(&sd->gspca_dev, sensor->cmd, sensor->cmd_len, 8); + gpio_init(sd, sensor->gpio); + msleep(100); + reg_r(gspca_dev, (sensor->i2c_addr << 8) | 0x001c, 1); + msleep(100); + if (gspca_dev->usb_buf[0] != 0) + break; + } + if (i >= ARRAY_SIZE(probe_order)) + PDEBUG(D_PROBE, "Unknown sensor"); + else + sd->sensor = probe_order[i]; +} + +static void mt9v111_init(struct gspca_dev *gspca_dev) +{ + int i, nwait; + static const u8 cmd_001b[] = { + 0x00, 0x3b, 0xf6, 0x01, 0x03, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00 + }; + static const u8 cmd_011b[][7] = { + {0x10, 0x01, 0x66, 0x08, 0x00, 0x00, 0x00}, + {0x01, 0x00, 0x1a, 0x04, 0x00, 0x00, 0x00}, + {0x20, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00}, + {0x02, 0x01, 0xae, 0x01, 0x00, 0x00, 0x00}, + }; + + reg_wb(gspca_dev, 0x001b, 0x0000, cmd_001b, sizeof cmd_001b); + for (i = 0; i < ARRAY_SIZE(cmd_011b); i++) { + reg_wb(gspca_dev, 0x001b, 0x0000, cmd_011b[i], + ARRAY_SIZE(cmd_011b[0])); + msleep(400); + nwait = 20; + for (;;) { + reg_r(gspca_dev, 0x031b, 1); + if (gspca_dev->usb_buf[0] == 0 + || gspca_dev->usb_err != 0) + break; + if (--nwait < 0) { + PDEBUG(D_PROBE, "mt9v111_init timeout"); + gspca_dev->usb_err = -ETIME; + return; + } + msleep(50); + } + } +} + +static void global_init(struct sd *sd, int first_time) +{ + switch (sd->sensor) { + case SENSOR_ICX098BQ: + if (first_time) + ucbus_write(&sd->gspca_dev, + icx098bq_start_0, + 8, 8); + gpio_init(sd, sensor_tb[sd->sensor].gpio); + break; + case SENSOR_LZ24BP: + if (sd->type != Creative_live_motion) + gpio_set(sd, SQ930_GPIO_EXTRA1, 0x00ff); + else + gpio_set(sd, 0, 0x00ff); + msleep(50); + if (first_time) + ucbus_write(&sd->gspca_dev, + lz24bp_start_0, + 8, 8); + gpio_init(sd, sensor_tb[sd->sensor].gpio); + break; + case SENSOR_MI0360: + if (first_time) + ucbus_write(&sd->gspca_dev, + mi0360_start_0, + ARRAY_SIZE(mi0360_start_0), + 8); + gpio_init(sd, sensor_tb[sd->sensor].gpio); + gpio_set(sd, SQ930_GPIO_EXTRA2, SQ930_GPIO_EXTRA2); + break; + default: +/* case SENSOR_MT9V111: */ + if (first_time) + mt9v111_init(&sd->gspca_dev); + else + gpio_init(sd, sensor_tb[sd->sensor].gpio); + break; + } +} + +static void lz24bp_ppl(struct sd *sd, u16 ppl) +{ + struct ucbus_write_cmd cmds[2] = { + {0xf810, ppl >> 8}, + {0xf811, ppl} + }; + + ucbus_write(&sd->gspca_dev, cmds, ARRAY_SIZE(cmds), 2); +} + +static void setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, integclks, intstartclk, frameclks, min_frclk; + const struct sensor_s *sensor; + u16 cmd; + u8 buf[15]; + + integclks = sd->expo; + i = 0; + cmd = SQ930_CTRL_SET_EXPOSURE; + + switch (sd->sensor) { + case SENSOR_ICX098BQ: /* ccd */ + case SENSOR_LZ24BP: + min_frclk = sd->sensor == SENSOR_ICX098BQ ? 0x210 : 0x26f; + if (integclks >= min_frclk) { + intstartclk = 0; + frameclks = integclks; + } else { + intstartclk = min_frclk - integclks; + frameclks = min_frclk; + } + buf[i++] = intstartclk >> 8; + buf[i++] = intstartclk; + buf[i++] = frameclks >> 8; + buf[i++] = frameclks; + buf[i++] = sd->gain; + break; + default: /* cmos */ +/* case SENSOR_MI0360: */ +/* case SENSOR_MT9V111: */ + cmd |= 0x0100; + sensor = &sensor_tb[sd->sensor]; + buf[i++] = sensor->i2c_addr; /* i2c_slave_addr */ + buf[i++] = 0x08; /* 2 * ni2c */ + buf[i++] = 0x09; /* reg = shutter width */ + buf[i++] = integclks >> 8; /* val H */ + buf[i++] = sensor->i2c_dum; + buf[i++] = integclks; /* val L */ + buf[i++] = 0x35; /* reg = global gain */ + buf[i++] = 0x00; /* val H */ + buf[i++] = sensor->i2c_dum; + buf[i++] = sd->gain; /* val L */ + buf[i++] = 0x00; + buf[i++] = 0x00; + buf[i++] = 0x00; + buf[i++] = 0x00; + buf[i++] = 0x83; + break; + } + reg_wb(gspca_dev, cmd, 0, buf, i); +} + +/* This function is called at probe time just before sd_init */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam = &gspca_dev->cam; + + sd->sensor = id->driver_info >> 8; + sd->type = id->driver_info; + + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + + cam->bulk = 1; + cam->bulk_size = BULK_TRANSFER_LEN; +/* cam->bulk_nurbs = 2; fixme: if no setexpo sync */ + + sd->quality = QUALITY_DEF; + sd->gain = GAIN_DEF; + sd->expo = EXPO_DEF; + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gpio[0] = sd->gpio[1] = 0xff; /* force gpio rewrite */ + +/*fixme: is this needed for icx098bp and mi0360? + if (sd->sensor != SENSOR_LZ24BP) + reg_w(gspca_dev, SQ930_CTRL_RESET, 0x0000); + */ + + reg_r(gspca_dev, SQ930_CTRL_GET_DEV_INFO, 8); +/* it returns: + * 03 00 12 93 0b f6 c9 00 live! ultra + * 03 00 07 93 0b f6 ca 00 live! ultra for notebook + * 03 00 12 93 0b fe c8 00 Trust WB-3500T + * 02 00 06 93 0b fe c8 00 Joy-IT 318S + * 03 00 12 93 0b f6 cf 00 icam tracer - sensor icx098bq + * 02 00 12 93 0b fe cf 00 ProQ Motion Webcam + * + * byte + * 0: 02 = usb 1.0 (12Mbit) / 03 = usb2.0 (480Mbit) + * 1: 00 + * 2: 06 / 07 / 12 = mode webcam? firmware?? + * 3: 93 chip = 930b (930b or 930c) + * 4: 0b + * 5: f6 = cdd (icx098bq, lz24bp) / fe or de = cmos (i2c) (other sensors) + * 6: c8 / c9 / ca / cf = mode webcam?, sensor? webcam? + * 7: 00 + */ + PDEBUG(D_PROBE, "info: %02x %02x %02x %02x %02x %02x %02x %02x", + gspca_dev->usb_buf[0], + gspca_dev->usb_buf[1], + gspca_dev->usb_buf[2], + gspca_dev->usb_buf[3], + gspca_dev->usb_buf[4], + gspca_dev->usb_buf[5], + gspca_dev->usb_buf[6], + gspca_dev->usb_buf[7]); + + bridge_init(sd); + + if (sd->sensor == SENSOR_MI0360) { + + /* no sensor probe for icam tracer */ + if (gspca_dev->usb_buf[5] == 0xf6) { /* if CMOS */ + sd->sensor = SENSOR_ICX098BQ; + gspca_dev->cam.cam_mode = &vga_mode[1]; + gspca_dev->cam.nmodes = 1; /* only 320x240 */ + } else { + cmos_probe(gspca_dev); + } + } + + PDEBUG(D_PROBE, "Sensor %s", sensor_tb[sd->sensor].name); + + global_init(sd, 1); + return gspca_dev->usb_err; +} + +/* special function to create the quantization tables of the JPEG header */ +static void sd_jpeg_set_qual(u8 *jpeg_hdr, + int quality) +{ + int i, sc1, sc2; + + quality = quality_tb[quality]; /* convert to JPEG quality */ +/* + * approximative qualities for Y and U/V: + * quant = 0:94%/91% 1:91%/87% 2:82%/73% 3:69%/56% + * should have: + * quant = 0:94%/91% 1:91%/87.5% 2:81.5%/72% 3:69%/54.5% + */ + sc1 = 200 - quality * 2; + quality = quality * 7 / 5 - 40; /* UV quality */ + sc2 = 200 - quality * 2; + for (i = 0; i < 64; i++) { + jpeg_hdr[JPEG_QT0_OFFSET + i] = + (jpeg_head[JPEG_QT0_OFFSET + i] * sc1 + 50) / 100; + jpeg_hdr[JPEG_QT1_OFFSET + i] = + (jpeg_head[JPEG_QT1_OFFSET + i] * sc2 + 50) / 100; + } +} + +/* send the start/stop commands to the webcam */ +static void send_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + const struct cap_s *cap; + int mode, quality; + + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + cap = &capconfig[sd->sensor][mode]; + quality = sd->quality; + reg_wb(gspca_dev, (quality << 12) + | 0x0a00 /* 900 for Bayer */ + | SQ930_CTRL_CAP_START, + 0x0500 /* a00 for Bayer */ + | cap->cc_sizeid, + cap->cc_bytes, 32); +}; +static void send_stop(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0); +}; + +/* function called at start time before URB creation */ +static int sd_isoc_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + gspca_dev->cam.bulk_nurbs = 1; /* there must be one URB only */ + sd->do_ctrl = 0; + return 0; +} + +/* start the capture */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int mode; + + /* initialize the JPEG header */ + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x21); /* JPEG 422 */ + sd_jpeg_set_qual(sd->jpeg_hdr, sd->quality); + + bridge_init(sd); + global_init(sd, 0); + msleep(100); + + switch (sd->sensor) { + case SENSOR_ICX098BQ: + ucbus_write(gspca_dev, icx098bq_start_0, + ARRAY_SIZE(icx098bq_start_0), + 8); + ucbus_write(gspca_dev, icx098bq_start_1, + ARRAY_SIZE(icx098bq_start_1), + 5); + ucbus_write(gspca_dev, icx098bq_start_2, + ARRAY_SIZE(icx098bq_start_2), + 6); + msleep(50); + + /* 1st start */ + send_start(gspca_dev); + gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff); + msleep(70); + reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0x0000); + gpio_set(sd, 0x7f, 0x00ff); + + /* 2nd start */ + send_start(gspca_dev); + gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff); + goto out; + case SENSOR_LZ24BP: + ucbus_write(gspca_dev, lz24bp_start_0, + ARRAY_SIZE(lz24bp_start_0), + 8); + if (sd->type != Creative_live_motion) + ucbus_write(gspca_dev, lz24bp_start_1_gen, + ARRAY_SIZE(lz24bp_start_1_gen), + 5); + else + ucbus_write(gspca_dev, lz24bp_start_1_clm, + ARRAY_SIZE(lz24bp_start_1_clm), + 5); + ucbus_write(gspca_dev, lz24bp_start_2, + ARRAY_SIZE(lz24bp_start_2), + 6); + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + lz24bp_ppl(sd, mode == 2 ? 0x0564 : 0x0310); + msleep(10); + break; + case SENSOR_MI0360: + ucbus_write(gspca_dev, mi0360_start_0, + ARRAY_SIZE(mi0360_start_0), + 8); + i2c_write(sd, mi0360_init_23, + ARRAY_SIZE(mi0360_init_23)); + i2c_write(sd, mi0360_init_24, + ARRAY_SIZE(mi0360_init_24)); + i2c_write(sd, mi0360_init_25, + ARRAY_SIZE(mi0360_init_25)); + ucbus_write(gspca_dev, mi0360_start_1, + ARRAY_SIZE(mi0360_start_1), + 5); + i2c_write(sd, mi0360_start_2, + ARRAY_SIZE(mi0360_start_2)); + i2c_write(sd, mi0360_start_3, + ARRAY_SIZE(mi0360_start_3)); + + /* 1st start */ + send_start(gspca_dev); + msleep(60); + reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0x0000); + + i2c_write(sd, + mi0360_start_4, ARRAY_SIZE(mi0360_start_4)); + break; + default: +/* case SENSOR_MT9V111: */ + ucbus_write(gspca_dev, mi0360_start_0, + ARRAY_SIZE(mi0360_start_0), + 8); + i2c_write(sd, mt9v111_init_0, + ARRAY_SIZE(mt9v111_init_0)); + i2c_write(sd, mt9v111_init_1, + ARRAY_SIZE(mt9v111_init_1)); + i2c_write(sd, mt9v111_init_2, + ARRAY_SIZE(mt9v111_init_2)); + ucbus_write(gspca_dev, mt9v111_start_1, + ARRAY_SIZE(mt9v111_start_1), + 8); + i2c_write(sd, mt9v111_init_3, + ARRAY_SIZE(mt9v111_init_3)); + i2c_write(sd, mt9v111_init_4, + ARRAY_SIZE(mt9v111_init_4)); + break; + } + + send_start(gspca_dev); +out: + msleep(1000); + + sd->eof_len = 0; /* init packet scan */ + + if (sd->sensor == SENSOR_MT9V111) + gpio_set(sd, SQ930_GPIO_DFL_LED, SQ930_GPIO_DFL_LED); + + sd->do_ctrl = 1; /* set the exposure */ + + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_MT9V111) + gpio_set(sd, 0, SQ930_GPIO_DFL_LED); + send_stop(gspca_dev); +} + +/* function called when the application gets a new frame */ +/* It sets the exposure if required and restart the bulk transfer. */ +static void sd_dq_callback(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + if (!sd->do_ctrl || gspca_dev->cam.bulk_nurbs != 0) + return; + sd->do_ctrl = 0; + + setexposure(gspca_dev); + + gspca_dev->cam.bulk_nurbs = 1; + ret = usb_submit_urb(gspca_dev->urb[0], GFP_ATOMIC); + if (ret < 0) + PDEBUG(D_ERR|D_PACK, "sd_dq_callback() err %d", ret); + + /* wait a little time, otherwise the webcam crashes */ + msleep(100); +} + +/* move a packet adding 0x00 after 0xff */ +static void add_packet(struct gspca_dev *gspca_dev, + u8 *data, + int len) +{ + int i; + + i = 0; + do { + if (data[i] == 0xff) { + gspca_frame_add(gspca_dev, INTER_PACKET, + data, i + 1); + len -= i; + data += i; + *data = 0x00; + i = 0; + } + } while (++i < len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +/* end a frame and start a new one */ +static void eof_sof(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + static const u8 ffd9[] = {0xff, 0xd9}; + + /* if control set, stop bulk transfer */ + if (sd->do_ctrl + && gspca_dev->last_packet_type == INTER_PACKET) + gspca_dev->cam.bulk_nurbs = 0; + gspca_frame_add(gspca_dev, LAST_PACKET, + ffd9, 2); + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 *p; + int l; + + len -= 8; /* ignore last 8 bytes (00 00 55 aa 55 aa 00 00) */ + + /* + * the end/start of frame is indicated by + * 0x00 * 16 - 0xab * 8 + * aligned on 8 bytes boundary + */ + if (sd->eof_len != 0) { /* if 'abababab' in previous pkt */ + if (*((u32 *) data) == 0xabababab) { + /*fixme: should remove previous 0000ababab*/ + eof_sof(gspca_dev); + data += 4; + len -= 4; + } + sd->eof_len = 0; + } + p = data; + l = len; + for (;;) { + if (*((u32 *) p) == 0xabababab) { + if (l < 8) { /* (may be 4 only) */ + sd->eof_len = 1; + break; + } + if (*((u32 *) p + 1) == 0xabababab) { + add_packet(gspca_dev, data, p - data - 16); + /* remove previous zeros */ + eof_sof(gspca_dev); + p += 8; + l -= 8; + if (l <= 0) + return; + len = l; + data = p; + continue; + } + } + p += 4; + l -= 4; + if (l <= 0) + break; + } + add_packet(gspca_dev, data, len); +} + +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gain = val; + if (gspca_dev->streaming) + sd->do_ctrl = 1; + return 0; +} + +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->gain; + return 0; +} +static int sd_setexpo(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->expo = val; + if (gspca_dev->streaming) + sd->do_ctrl = 1; + return 0; +} + +static int sd_getexpo(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->expo; + return 0; +} + +static int sd_set_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + int quality; + + if (jcomp->quality >= (QUAL_0 + QUAL_1) / 2) + quality = 0; + else if (jcomp->quality >= (QUAL_1 + QUAL_2) / 2) + quality = 1; + else if (jcomp->quality >= (QUAL_2 + QUAL_3) / 2) + quality = 2; + else + quality = 3; + + if (quality != sd->quality) { + sd->quality = quality; + if (gspca_dev->streaming) { + send_stop(gspca_dev); + sd_jpeg_set_qual(sd->jpeg_hdr, sd->quality); + msleep(70); + send_start(gspca_dev); + } + } + return gspca_dev->usb_err; +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + memset(jcomp, 0, sizeof *jcomp); + jcomp->quality = quality_tb[sd->quality]; + jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT + | V4L2_JPEG_MARKER_DQT; + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .init = sd_init, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, + .dq_callback = sd_dq_callback, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, +}; + +/* Table of supported USB devices */ +#define ST(sensor, type) \ + .driver_info = (SENSOR_ ## sensor << 8) \ + | (type) +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x4038), ST(MI0360, 0)}, + {USB_DEVICE(0x041e, 0x403c), ST(LZ24BP, 0)}, + {USB_DEVICE(0x041e, 0x403d), ST(LZ24BP, 0)}, + {USB_DEVICE(0x041e, 0x4041), ST(LZ24BP, Creative_live_motion)}, + {USB_DEVICE(0x2770, 0x930b), ST(MI0360, 0)}, + {USB_DEVICE(0x2770, 0x930c), ST(MI0360, 0)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + int ret; + + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; + info("registered"); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + info("deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c index 0fb5342..2aedf4b 100644 --- a/drivers/media/video/gspca/stk014.c +++ b/drivers/media/video/gspca/stk014.c @@ -36,11 +36,11 @@ struct sd { unsigned char colors; unsigned char lightfreq; u8 quality; -#define QUALITY_MIN 60 +#define QUALITY_MIN 70 #define QUALITY_MAX 95 -#define QUALITY_DEF 80 +#define QUALITY_DEF 88 - u8 *jpeg_hdr; + u8 jpeg_hdr[JPEG_HDR_SZ]; }; /* V4L2 controls supported by the driver */ @@ -337,9 +337,6 @@ static int sd_start(struct gspca_dev *gspca_dev) int ret, value; /* create the JPEG header */ - sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); - if (!sd->jpeg_hdr) - return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x22); /* JPEG 411 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); @@ -412,13 +409,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) PDEBUG(D_STREAM, "camera stopped"); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - kfree(sd->jpeg_hdr); -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ int len) /* iso packet length */ @@ -578,7 +568,6 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, .get_jcomp = sd_get_jcomp, diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/video/gspca/stv06xx/stv06xx.h index 992ce53..053a27e 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx.h @@ -30,6 +30,7 @@ #ifndef STV06XX_H_ #define STV06XX_H_ +#include <linux/slab.h> #include "gspca.h" #define MODULE_NAME "STV06xx" diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c index 0c786e0..21d82ba 100644 --- a/drivers/media/video/gspca/sunplus.c +++ b/drivers/media/video/gspca/sunplus.c @@ -54,7 +54,7 @@ struct sd { #define MegapixV4 4 #define MegaImageVI 5 - u8 *jpeg_hdr; + u8 jpeg_hdr[JPEG_HDR_SZ]; }; /* V4L2 controls supported by the driver */ @@ -842,9 +842,6 @@ static int sd_start(struct gspca_dev *gspca_dev) int enable; /* create the JPEG header */ - sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); - if (!sd->jpeg_hdr) - return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x22); /* JPEG 411 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); @@ -954,13 +951,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) } } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - kfree(sd->jpeg_hdr); -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ int len) /* iso packet length */ @@ -1162,7 +1152,6 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .get_jcomp = sd_get_jcomp, .set_jcomp = sd_set_jcomp, diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c index 6301437..2a0f12d 100644 --- a/drivers/media/video/gspca/t613.c +++ b/drivers/media/video/gspca/t613.c @@ -1,5 +1,7 @@ /* - * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * T613 subdriver + * + * Copyright (C) 2010 Jean-Francois Moine (http://moinejf.free.fr) * * 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 @@ -26,6 +28,7 @@ #define MODULE_NAME "t613" +#include <linux/slab.h> #include "gspca.h" #define V4L2_CID_EFFECTS (V4L2_CID_PRIVATE_BASE + 0) @@ -44,18 +47,20 @@ struct sd { u8 gamma; u8 sharpness; u8 freq; - u8 red_balance; /* split balance */ - u8 blue_balance; - u8 global_gain; /* aka gain */ - u8 whitebalance; /* set default r/g/b and activate */ + u8 red_gain; + u8 blue_gain; + u8 green_gain; + u8 awb; /* set default r/g/b and activate */ u8 mirror; u8 effect; u8 sensor; -#define SENSOR_OM6802 0 -#define SENSOR_OTHER 1 -#define SENSOR_TAS5130A 2 -#define SENSOR_LT168G 3 /* must verify if this is the actual model */ +enum { + SENSOR_OM6802, + SENSOR_OTHER, + SENSOR_TAS5130A, + SENSOR_LT168G, /* must verify if this is the actual model */ +} sensors; }; /* V4L2 controls supported by the driver */ @@ -74,24 +79,22 @@ static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); - -static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setglobal_gain(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getglobal_gain(struct gspca_dev *gspca_dev, __s32 *val); - -static int sd_setflip(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setblue_gain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getblue_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setred_gain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getred_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); + +static int sd_setmirror(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getmirror(struct gspca_dev *gspca_dev, __s32 *val); static int sd_seteffect(struct gspca_dev *gspca_dev, __s32 val); static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val); static int sd_querymenu(struct gspca_dev *gspca_dev, struct v4l2_querymenu *menu); - static const struct ctrl sd_ctrls[] = { { { @@ -177,8 +180,8 @@ static const struct ctrl sd_ctrls[] = { #define MIRROR_DEF 0 .default_value = MIRROR_DEF, }, - .set = sd_setflip, - .get = sd_getflip + .set = sd_setmirror, + .get = sd_getmirror }, { { @@ -198,15 +201,15 @@ static const struct ctrl sd_ctrls[] = { { .id = V4L2_CID_AUTO_WHITE_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "White Balance", + .name = "Auto White Balance", .minimum = 0, .maximum = 1, .step = 1, -#define WHITE_BALANCE_DEF 0 - .default_value = WHITE_BALANCE_DEF, +#define AWB_DEF 0 + .default_value = AWB_DEF, }, - .set = sd_setwhitebalance, - .get = sd_getwhitebalance + .set = sd_setawb, + .get = sd_getawb }, { { @@ -244,11 +247,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0x10, .maximum = 0x40, .step = 1, -#define BLUE_BALANCE_DEF 0x20 - .default_value = BLUE_BALANCE_DEF, +#define BLUE_GAIN_DEF 0x20 + .default_value = BLUE_GAIN_DEF, }, - .set = sd_setblue_balance, - .get = sd_getblue_balance, + .set = sd_setblue_gain, + .get = sd_getblue_gain, }, { { @@ -258,11 +261,11 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0x10, .maximum = 0x40, .step = 1, -#define RED_BALANCE_DEF 0x20 - .default_value = RED_BALANCE_DEF, +#define RED_GAIN_DEF 0x20 + .default_value = RED_GAIN_DEF, }, - .set = sd_setred_balance, - .get = sd_getred_balance, + .set = sd_setred_gain, + .get = sd_getred_gain, }, { { @@ -272,24 +275,14 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0x10, .maximum = 0x40, .step = 1, -#define global_gain_DEF 0x20 - .default_value = global_gain_DEF, +#define GAIN_DEF 0x20 + .default_value = GAIN_DEF, }, - .set = sd_setglobal_gain, - .get = sd_getglobal_gain, + .set = sd_setgain, + .get = sd_getgain, }, }; -static char *effects_control[] = { - "Normal", - "Emboss", /* disabled */ - "Monochrome", - "Sepia", - "Sketch", - "Sun Effect", /* disabled */ - "Negative", -}; - static const struct v4l2_pix_format vga_mode_t16[] = { {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 160, @@ -327,7 +320,6 @@ struct additional_sensor_data { const u8 data1[10]; const u8 data2[9]; const u8 data3[9]; - const u8 data4[4]; const u8 data5[6]; const u8 stream[4]; }; @@ -375,7 +367,7 @@ static const u8 n4_lt168g[] = { }; static const struct additional_sensor_data sensor_data[] = { - { /* 0: OM6802 */ +[SENSOR_OM6802] = { .n3 = {0x61, 0x68, 0x65, 0x0a, 0x60, 0x04}, .n4 = n4_om6802, @@ -392,14 +384,12 @@ static const struct additional_sensor_data sensor_data[] = { .data3 = {0x80, 0xff, 0xff, 0x80, 0xff, 0xff, 0x80, 0xff, 0xff}, - .data4 = /*Freq (50/60Hz). Splitted for test purpose */ - {0x66, 0xca, 0xa8, 0xf0}, .data5 = /* this could be removed later */ {0x0c, 0x03, 0xab, 0x13, 0x81, 0x23}, .stream = {0x0b, 0x04, 0x0a, 0x78}, }, - { /* 1: OTHER */ +[SENSOR_OTHER] = { .n3 = {0x61, 0xc2, 0x65, 0x88, 0x60, 0x00}, .n4 = n4_other, @@ -416,14 +406,12 @@ static const struct additional_sensor_data sensor_data[] = { .data3 = {0x4e, 0x9c, 0xec, 0x40, 0x80, 0xc0, 0x48, 0x96, 0xd9}, - .data4 = - {0x66, 0x00, 0xa8, 0xa8}, .data5 = {0x0c, 0x03, 0xab, 0x29, 0x81, 0x69}, .stream = {0x0b, 0x04, 0x0a, 0x00}, }, - { /* 2: TAS5130A */ +[SENSOR_TAS5130A] = { .n3 = {0x61, 0xc2, 0x65, 0x0d, 0x60, 0x08}, .n4 = n4_tas5130a, @@ -440,14 +428,12 @@ static const struct additional_sensor_data sensor_data[] = { .data3 = {0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0}, - .data4 = /* Freq (50/60Hz). Splitted for test purpose */ - {0x66, 0x00, 0xa8, 0xe8}, .data5 = {0x0c, 0x03, 0xab, 0x10, 0x81, 0x20}, .stream = {0x0b, 0x04, 0x0a, 0x40}, }, - { /* 3: LT168G */ +[SENSOR_LT168G] = { .n3 = {0x61, 0xc2, 0x65, 0x68, 0x60, 0x00}, .n4 = n4_lt168g, .n4sz = sizeof n4_lt168g, @@ -460,7 +446,6 @@ static const struct additional_sensor_data sensor_data[] = { 0xff}, .data3 = {0x40, 0x80, 0xc0, 0x50, 0xa0, 0xf0, 0x53, 0xa6, 0xff}, - .data4 = {0x66, 0x41, 0xa8, 0xf0}, .data5 = {0x0c, 0x03, 0xab, 0x4b, 0x81, 0x2b}, .stream = {0x0b, 0x04, 0x0a, 0x28}, }, @@ -469,6 +454,15 @@ static const struct additional_sensor_data sensor_data[] = { #define MAX_EFFECTS 7 /* easily done by soft, this table could be removed, * i keep it here just in case */ +static char *effects_control[MAX_EFFECTS] = { + "Normal", + "Emboss", /* disabled */ + "Monochrome", + "Sepia", + "Sketch", + "Sun Effect", /* disabled */ + "Negative", +}; static const u8 effects_table[MAX_EFFECTS][6] = { {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x00}, /* Normal */ {0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x04}, /* Repujar */ @@ -480,40 +474,41 @@ static const u8 effects_table[MAX_EFFECTS][6] = { }; static const u8 gamma_table[GAMMA_MAX][17] = { - {0x00, 0x3e, 0x69, 0x85, 0x95, 0xa1, 0xae, 0xb9, /* 0 */ - 0xc2, 0xcb, 0xd4, 0xdb, 0xe3, 0xea, 0xf1, 0xf8, +/* gamma table from cam1690.ini */ + {0x00, 0x00, 0x01, 0x04, 0x08, 0x0e, 0x16, 0x21, /* 0 */ + 0x2e, 0x3d, 0x50, 0x65, 0x7d, 0x99, 0xb8, 0xdb, 0xff}, - {0x00, 0x33, 0x5a, 0x75, 0x85, 0x93, 0xa1, 0xad, /* 1 */ - 0xb7, 0xc2, 0xcb, 0xd4, 0xde, 0xe7, 0xf0, 0xf7, + {0x00, 0x01, 0x03, 0x08, 0x0e, 0x16, 0x21, 0x2d, /* 1 */ + 0x3c, 0x4d, 0x60, 0x75, 0x8d, 0xa6, 0xc2, 0xe1, 0xff}, - {0x00, 0x2f, 0x51, 0x6b, 0x7c, 0x8a, 0x99, 0xa6, /* 2 */ - 0xb1, 0xbc, 0xc6, 0xd0, 0xdb, 0xe4, 0xed, 0xf6, + {0x00, 0x01, 0x05, 0x0b, 0x12, 0x1c, 0x28, 0x35, /* 2 */ + 0x45, 0x56, 0x69, 0x7e, 0x95, 0xad, 0xc7, 0xe3, 0xff}, - {0x00, 0x29, 0x48, 0x60, 0x72, 0x81, 0x90, 0x9e, /* 3 */ - 0xaa, 0xb5, 0xbf, 0xcb, 0xd6, 0xe1, 0xeb, 0xf5, + {0x00, 0x02, 0x07, 0x0f, 0x18, 0x24, 0x30, 0x3f, /* 3 */ + 0x4f, 0x61, 0x73, 0x88, 0x9d, 0xb4, 0xcd, 0xe6, 0xff}, - {0x00, 0x23, 0x3f, 0x55, 0x68, 0x77, 0x86, 0x95, /* 4 */ - 0xa2, 0xad, 0xb9, 0xc6, 0xd2, 0xde, 0xe9, 0xf4, + {0x00, 0x04, 0x0B, 0x15, 0x20, 0x2d, 0x3b, 0x4a, /* 4 */ + 0x5b, 0x6c, 0x7f, 0x92, 0xa7, 0xbc, 0xd2, 0xe9, 0xff}, - {0x00, 0x1b, 0x33, 0x48, 0x59, 0x69, 0x79, 0x87, /* 5 */ - 0x96, 0xa3, 0xb1, 0xbe, 0xcc, 0xda, 0xe7, 0xf3, + {0x00, 0x07, 0x11, 0x15, 0x20, 0x2d, 0x48, 0x58, /* 5 */ + 0x68, 0x79, 0x8b, 0x9d, 0xb0, 0xc4, 0xd7, 0xec, 0xff}, - {0x00, 0x02, 0x10, 0x20, 0x32, 0x40, 0x57, 0x67, /* 6 */ + {0x00, 0x0c, 0x1a, 0x29, 0x38, 0x47, 0x57, 0x67, /* 6 */ 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, - {0x00, 0x02, 0x14, 0x26, 0x38, 0x4a, 0x60, 0x70, /* 7 */ + {0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, /* 7 */ 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xff}, - {0x00, 0x10, 0x22, 0x35, 0x47, 0x5a, 0x69, 0x79, /* 8 */ - 0x88, 0x97, 0xa7, 0xb6, 0xc4, 0xd3, 0xe0, 0xf0, + {0x00, 0x15, 0x27, 0x38, 0x49, 0x59, 0x69, 0x79, /* 8 */ + 0x88, 0x97, 0xa7, 0xb6, 0xc4, 0xd3, 0xe2, 0xf0, 0xff}, - {0x00, 0x10, 0x26, 0x40, 0x54, 0x65, 0x75, 0x84, /* 9 */ - 0x93, 0xa1, 0xb0, 0xbd, 0xca, 0xd6, 0xe0, 0xf0, + {0x00, 0x1c, 0x30, 0x43, 0x54, 0x65, 0x75, 0x84, /* 9 */ + 0x93, 0xa1, 0xb0, 0xbd, 0xca, 0xd8, 0xe5, 0xf2, 0xff}, - {0x00, 0x18, 0x2b, 0x44, 0x60, 0x70, 0x80, 0x8e, /* 10 */ - 0x9c, 0xaa, 0xb7, 0xc4, 0xd0, 0xd8, 0xe2, 0xf0, + {0x00, 0x24, 0x3b, 0x4f, 0x60, 0x70, 0x80, 0x8e, /* 10 */ + 0x9c, 0xaa, 0xb7, 0xc4, 0xd0, 0xdc, 0xe8, 0xf3, 0xff}, - {0x00, 0x1a, 0x34, 0x52, 0x66, 0x7e, 0x8d, 0x9b, /* 11 */ + {0x00, 0x2a, 0x3c, 0x5d, 0x6e, 0x7e, 0x8d, 0x9b, /* 11 */ 0xa8, 0xb4, 0xc0, 0xcb, 0xd6, 0xe1, 0xeb, 0xf5, 0xff}, {0x00, 0x3f, 0x5a, 0x6e, 0x7f, 0x8e, 0x9c, 0xa8, /* 12 */ @@ -577,12 +572,11 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, } else { u8 *tmpbuf; - tmpbuf = kmalloc(len, GFP_KERNEL); + tmpbuf = kmemdup(buffer, len, GFP_KERNEL); if (!tmpbuf) { err("Out of memory"); return; } - memcpy(tmpbuf, buffer, len); usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 0, @@ -625,7 +619,6 @@ static void reg_w_ixbuf(struct gspca_dev *gspca_dev, kfree(tmpbuf); } -/* Reported as OM6802*/ static void om6802_sensor_init(struct gspca_dev *gspca_dev) { int i; @@ -703,12 +696,12 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->autogain = AUTOGAIN_DEF; sd->mirror = MIRROR_DEF; sd->freq = FREQ_DEF; - sd->whitebalance = WHITE_BALANCE_DEF; + sd->awb = AWB_DEF; sd->sharpness = SHARPNESS_DEF; sd->effect = EFFECTS_DEF; - sd->red_balance = RED_BALANCE_DEF; - sd->blue_balance = BLUE_BALANCE_DEF; - sd->global_gain = global_gain_DEF; + sd->red_gain = RED_GAIN_DEF; + sd->blue_gain = BLUE_GAIN_DEF; + sd->green_gain = GAIN_DEF * 3 - RED_GAIN_DEF - BLUE_GAIN_DEF; return 0; } @@ -761,40 +754,59 @@ static void setgamma(struct gspca_dev *gspca_dev) reg_w_ixbuf(gspca_dev, 0x90, gamma_table[sd->gamma], sizeof gamma_table[0]); } -static void setglobalgain(struct gspca_dev *gspca_dev) -{ +static void setRGB(struct gspca_dev *gspca_dev) +{ struct sd *sd = (struct sd *) gspca_dev; - reg_w(gspca_dev, (sd->red_balance << 8) + 0x87); - reg_w(gspca_dev, (sd->blue_balance << 8) + 0x88); - reg_w(gspca_dev, (sd->global_gain << 8) + 0x89); + u8 all_gain_reg[6] = + {0x87, 0x00, 0x88, 0x00, 0x89, 0x00}; + + all_gain_reg[1] = sd->red_gain; + all_gain_reg[3] = sd->blue_gain; + all_gain_reg[5] = sd->green_gain; + reg_w_buf(gspca_dev, all_gain_reg, sizeof all_gain_reg); } -/* Generic fnc for r/b balance, exposure and whitebalance */ -static void setbalance(struct gspca_dev *gspca_dev) +/* Generic fnc for r/b balance, exposure and awb */ +static void setawb(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + u16 reg80; - /* on whitebalance leave defaults values */ - if (sd->whitebalance) { - reg_w(gspca_dev, 0x3c80); - } else { - reg_w(gspca_dev, 0x3880); + reg80 = (sensor_data[sd->sensor].reg80 << 8) | 0x80; + + /* on awb leave defaults values */ + if (!sd->awb) { /* shoud we wait here.. */ - /* update and reset 'global gain' with webcam parameters */ - sd->red_balance = reg_r(gspca_dev, 0x0087); - sd->blue_balance = reg_r(gspca_dev, 0x0088); - sd->global_gain = reg_r(gspca_dev, 0x0089); - setglobalgain(gspca_dev); + /* update and reset RGB gains with webcam values */ + sd->red_gain = reg_r(gspca_dev, 0x0087); + sd->blue_gain = reg_r(gspca_dev, 0x0088); + sd->green_gain = reg_r(gspca_dev, 0x0089); + reg80 &= ~0x0400; /* AWB off */ } - + reg_w(gspca_dev, reg80); + reg_w(gspca_dev, reg80); } - - -static void setwhitebalance(struct gspca_dev *gspca_dev) +static void init_gains(struct gspca_dev *gspca_dev) { - setbalance(gspca_dev); + struct sd *sd = (struct sd *) gspca_dev; + u16 reg80; + u8 all_gain_reg[8] = + {0x87, 0x00, 0x88, 0x00, 0x89, 0x00, 0x80, 0x00}; + + all_gain_reg[1] = sd->red_gain; + all_gain_reg[3] = sd->blue_gain; + all_gain_reg[5] = sd->green_gain; + reg80 = sensor_data[sd->sensor].reg80; + if (!sd->awb) + reg80 &= ~0x04; + all_gain_reg[7] = reg80; + reg_w_buf(gspca_dev, all_gain_reg, sizeof all_gain_reg); + + reg_w(gspca_dev, (sd->red_gain << 8) + 0x87); + reg_w(gspca_dev, (sd->blue_gain << 8) + 0x88); + reg_w(gspca_dev, (sd->green_gain << 8) + 0x89); } static void setsharpness(struct gspca_dev *gspca_dev) @@ -807,6 +819,38 @@ static void setsharpness(struct gspca_dev *gspca_dev) reg_w(gspca_dev, reg_to_write); } +static void setfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 reg66; + u8 freq[4] = { 0x66, 0x00, 0xa8, 0xe8 }; + + switch (sd->sensor) { + case SENSOR_LT168G: + if (sd->freq != 0) + freq[3] = 0xa8; + reg66 = 0x41; + break; + case SENSOR_OM6802: + reg66 = 0xca; + break; + default: + reg66 = 0x40; + break; + } + switch (sd->freq) { + case 0: /* no flicker */ + freq[3] = 0xf0; + break; + case 2: /* 60Hz */ + reg66 &= ~0x40; + break; + } + freq[1] = reg66; + + reg_w_buf(gspca_dev, freq, sizeof freq); +} + /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { @@ -901,13 +945,9 @@ static int sd_init(struct gspca_dev *gspca_dev) setgamma(gspca_dev); setcolors(gspca_dev); setsharpness(gspca_dev); - setwhitebalance(gspca_dev); - - reg_w(gspca_dev, 0x2087); /* tied to white balance? */ - reg_w(gspca_dev, 0x2088); - reg_w(gspca_dev, 0x2089); + init_gains(gspca_dev); + setfreq(gspca_dev); - reg_w_buf(gspca_dev, sensor->data4, sizeof sensor->data4); reg_w_buf(gspca_dev, sensor->data5, sizeof sensor->data5); reg_w_buf(gspca_dev, sensor->nset8, sizeof sensor->nset8); reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream); @@ -926,16 +966,16 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static void setflip(struct gspca_dev *gspca_dev) +static void setmirror(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - u8 flipcmd[8] = + u8 hflipcmd[8] = {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09}; if (sd->mirror) - flipcmd[3] = 0x01; + hflipcmd[3] = 0x01; - reg_w_buf(gspca_dev, flipcmd, sizeof flipcmd); + reg_w_buf(gspca_dev, hflipcmd, sizeof hflipcmd); } static void seteffect(struct gspca_dev *gspca_dev) @@ -956,17 +996,6 @@ static void seteffect(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0xfaa6); } -static void setlightfreq(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 freq[4] = { 0x66, 0x40, 0xa8, 0xe8 }; - - if (sd->freq == 2) /* 60hz */ - freq[1] = 0x00; - - reg_w_buf(gspca_dev, freq, sizeof freq); -} - /* Is this really needed? * i added some module parameters for test with some users */ static void poll_sensor(struct gspca_dev *gspca_dev) @@ -979,9 +1008,7 @@ static void poll_sensor(struct gspca_dev *gspca_dev) static const u8 poll2[] = {0x67, 0x02, 0x68, 0x71, 0x69, 0x72, 0x72, 0xa9, 0x73, 0x02, 0x73, 0x02, 0x60, 0x14}; - static const u8 poll3[] = - {0x87, 0x3f, 0x88, 0x20, 0x89, 0x2d}; - static const u8 poll4[] = + static const u8 noise03[] = /* (some differences / ms-drv) */ {0xa6, 0x0a, 0xea, 0xcf, 0xbe, 0x26, 0xb1, 0x5f, 0xa1, 0xb1, 0xda, 0x6b, 0xdb, 0x98, 0xdf, 0x0c, 0xc2, 0x80, 0xc3, 0x10}; @@ -989,8 +1016,7 @@ static void poll_sensor(struct gspca_dev *gspca_dev) PDEBUG(D_STREAM, "[Sensor requires polling]"); reg_w_buf(gspca_dev, poll1, sizeof poll1); reg_w_buf(gspca_dev, poll2, sizeof poll2); - reg_w_buf(gspca_dev, poll3, sizeof poll3); - reg_w_buf(gspca_dev, poll4, sizeof poll4); + reg_w_buf(gspca_dev, noise03, sizeof noise03); } static int sd_start(struct gspca_dev *gspca_dev) @@ -1025,12 +1051,7 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_OM6802: om6802_sensor_init(gspca_dev); break; - case SENSOR_LT168G: - break; - case SENSOR_OTHER: - break; - default: -/* case SENSOR_TAS5130A: */ + case SENSOR_TAS5130A: i = 0; for (;;) { reg_w_buf(gspca_dev, tas5130a_sensor_init[i], @@ -1047,7 +1068,7 @@ static int sd_start(struct gspca_dev *gspca_dev) break; } sensor = &sensor_data[sd->sensor]; - reg_w_buf(gspca_dev, sensor->data4, sizeof sensor->data4); + setfreq(gspca_dev); reg_r(gspca_dev, 0x0012); reg_w_buf(gspca_dev, t2, sizeof t2); reg_w_ixbuf(gspca_dev, 0xb3, t3, sizeof t3); @@ -1080,7 +1101,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ int len) /* iso packet length */ { - static u8 ffd9[] = { 0xff, 0xd9 }; + int pkt_type; if (data[0] == 0x5a) { /* Control Packet, after this came the header again, @@ -1090,84 +1111,88 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } data += 2; len -= 2; - if (data[0] == 0xff && data[1] == 0xd8) { - /* extra bytes....., could be processed too but would be - * a waste of time, right now leave the application and - * libjpeg do it for ourserlves.. */ - gspca_frame_add(gspca_dev, LAST_PACKET, - ffd9, 2); - gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); - return; - } - - if (data[len - 2] == 0xff && data[len - 1] == 0xd9) { - /* Just in case, i have seen packets with the marker, - * other's do not include it... */ - len -= 2; - } - gspca_frame_add(gspca_dev, INTER_PACKET, data, len); + if (data[0] == 0xff && data[1] == 0xd8) + pkt_type = FIRST_PACKET; + else if (data[len - 2] == 0xff && data[len - 1] == 0xd9) + pkt_type = LAST_PACKET; + else + pkt_type = INTER_PACKET; + gspca_frame_add(gspca_dev, pkt_type, data, len); } - -static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val) +static int sd_setblue_gain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - sd->blue_balance = val; + sd->blue_gain = val; if (gspca_dev->streaming) reg_w(gspca_dev, (val << 8) + 0x88); return 0; } -static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val) +static int sd_getblue_gain(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - *val = sd->blue_balance; + *val = sd->blue_gain; return 0; } -static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val) +static int sd_setred_gain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - sd->red_balance = val; + sd->red_gain = val; if (gspca_dev->streaming) reg_w(gspca_dev, (val << 8) + 0x87); return 0; } -static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val) +static int sd_getred_gain(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - *val = sd->red_balance; + *val = sd->red_gain; return 0; } - - -static int sd_setglobal_gain(struct gspca_dev *gspca_dev, __s32 val) +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; + u16 psg, nsg; + + psg = sd->red_gain + sd->blue_gain + sd->green_gain; + nsg = val * 3; + sd->red_gain = sd->red_gain * nsg / psg; + if (sd->red_gain > 0x40) + sd->red_gain = 0x40; + else if (sd->red_gain < 0x10) + sd->red_gain = 0x10; + sd->blue_gain = sd->blue_gain * nsg / psg; + if (sd->blue_gain > 0x40) + sd->blue_gain = 0x40; + else if (sd->blue_gain < 0x10) + sd->blue_gain = 0x10; + sd->green_gain = sd->green_gain * nsg / psg; + if (sd->green_gain > 0x40) + sd->green_gain = 0x40; + else if (sd->green_gain < 0x10) + sd->green_gain = 0x10; - sd->global_gain = val; if (gspca_dev->streaming) - setglobalgain(gspca_dev); - + setRGB(gspca_dev); return 0; } -static int sd_getglobal_gain(struct gspca_dev *gspca_dev, __s32 *val) +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - *val = sd->global_gain; + *val = (sd->red_gain + sd->blue_gain + sd->green_gain) / 3; return 0; } - static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -1186,35 +1211,35 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) return *val; } -static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val) +static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - sd->whitebalance = val; + sd->awb = val; if (gspca_dev->streaming) - setwhitebalance(gspca_dev); + setawb(gspca_dev); return 0; } -static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val) +static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - *val = sd->whitebalance; + *val = sd->awb; return *val; } -static int sd_setflip(struct gspca_dev *gspca_dev, __s32 val) +static int sd_setmirror(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; sd->mirror = val; if (gspca_dev->streaming) - setflip(gspca_dev); + setmirror(gspca_dev); return 0; } -static int sd_getflip(struct gspca_dev *gspca_dev, __s32 *val) +static int sd_getmirror(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; @@ -1300,7 +1325,7 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) sd->freq = val; if (gspca_dev->streaming) - setlightfreq(gspca_dev); + setfreq(gspca_dev); return 0; } @@ -1368,7 +1393,8 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, case V4L2_CID_EFFECTS: if ((unsigned) menu->index < ARRAY_SIZE(effects_control)) { strncpy((char *) menu->name, - effects_control[menu->index], 32); + effects_control[menu->index], + sizeof menu->name); return 0; } break; diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c index c7b6eb1..d9c5bf3 100644 --- a/drivers/media/video/gspca/tv8532.c +++ b/drivers/media/video/gspca/tv8532.c @@ -30,29 +30,46 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - __u16 brightness; + __u16 exposure; + __u16 gain; __u8 packet; }; /* V4L2 controls supported by the driver */ -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); static const struct ctrl sd_ctrls[] = { { { - .id = V4L2_CID_BRIGHTNESS, + .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", + .name = "Exposure", .minimum = 1, - .maximum = 0x15f, /* = 352 - 1 */ + .maximum = 0x18f, .step = 1, -#define BRIGHTNESS_DEF 0x14c - .default_value = BRIGHTNESS_DEF, +#define EXPOSURE_DEF 0x18f + .default_value = EXPOSURE_DEF, }, - .set = sd_setbrightness, - .get = sd_getbrightness, + .set = sd_setexposure, + .get = sd_getexposure, + }, + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 0x7ff, + .step = 1, +#define GAIN_DEF 0x100 + .default_value = GAIN_DEF, + }, + .set = sd_setgain, + .get = sd_getgain, }, }; @@ -92,6 +109,14 @@ static const struct v4l2_pix_format sif_mode[] = { #define R14_AD_ROW_BEGINL 0x14 #define R15_AD_ROWBEGINH 0x15 #define R1C_AD_EXPOSE_TIMEL 0x1c +#define R20_GAIN_G1L 0x20 +#define R21_GAIN_G1H 0x21 +#define R22_GAIN_RL 0x22 +#define R23_GAIN_RH 0x23 +#define R24_GAIN_BL 0x24 +#define R25_GAIN_BH 0x25 +#define R26_GAIN_G2L 0x26 +#define R27_GAIN_G2H 0x27 #define R28_QUANT 0x28 #define R29_LINE 0x29 #define R2C_POLARITY 0x2c @@ -129,18 +154,6 @@ static const u8 eeprom_data[][3] = { {0x05, 0x09, 0xf1}, }; -static int reg_r(struct gspca_dev *gspca_dev, - __u16 index) -{ - usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), - 0x03, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, /* value */ - index, gspca_dev->usb_buf, 1, - 500); - return gspca_dev->usb_buf[0]; -} /* write 1 byte */ static void reg_w1(struct gspca_dev *gspca_dev, @@ -183,7 +196,6 @@ static void tv_8532WriteEEprom(struct gspca_dev *gspca_dev) } reg_w1(gspca_dev, R07_TABLE_LEN, i); reg_w1(gspca_dev, R01_TIMING_CONTROL_LOW, CMD_EEprom_Close); - msleep(10); } /* this function is called at probe time */ @@ -197,53 +209,13 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = sif_mode; cam->nmodes = ARRAY_SIZE(sif_mode); - sd->brightness = BRIGHTNESS_DEF; + sd->exposure = EXPOSURE_DEF; + sd->gain = GAIN_DEF; return 0; } -static void tv_8532ReadRegisters(struct gspca_dev *gspca_dev) -{ - int i; - static u8 reg_tb[] = { - R0C_AD_WIDTHL, - R0D_AD_WIDTHH, - R28_QUANT, - R29_LINE, - R2C_POLARITY, - R2D_POINT, - R2E_POINTH, - R2F_POINTB, - R30_POINTBH, - R2A_HIGH_BUDGET, - R2B_LOW_BUDGET, - R34_VID, - R35_VIDH, - R36_PID, - R37_PIDH, - R83_AD_IDH, - R10_AD_COL_BEGINL, - R11_AD_COL_BEGINH, - R14_AD_ROW_BEGINL, - R15_AD_ROWBEGINH, - 0 - }; - - i = 0; - do { - reg_r(gspca_dev, reg_tb[i]); - i++; - } while (reg_tb[i] != 0); -} - static void tv_8532_setReg(struct gspca_dev *gspca_dev) { - reg_w1(gspca_dev, R10_AD_COL_BEGINL, 0x44); - /* begin active line */ - reg_w1(gspca_dev, R11_AD_COL_BEGINH, 0x00); - /* mirror and digital gain */ - reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE); - /* = 0x84 */ - reg_w1(gspca_dev, R3B_Test3, 0x0a); /* Test0Sel = 10 */ /******************************************************/ reg_w1(gspca_dev, R0E_AD_HEIGHTL, 0x90); @@ -255,100 +227,43 @@ static void tv_8532_setReg(struct gspca_dev *gspca_dev) /* mirror and digital gain */ reg_w1(gspca_dev, R14_AD_ROW_BEGINL, 0x0a); - reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x00); reg_w1(gspca_dev, R94_AD_BITCONTROL, 0x02); - - reg_w1(gspca_dev, R01_TIMING_CONTROL_LOW, CMD_EEprom_Close); - reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x00); reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE); /* = 0x84 */ } -static void tv_8532_PollReg(struct gspca_dev *gspca_dev) -{ - int i; - - /* strange polling from tgc */ - for (i = 0; i < 10; i++) { - reg_w1(gspca_dev, R2C_POLARITY, 0x10); - reg_w1(gspca_dev, R00_PART_CONTROL, - LATENT_CHANGE | EXPO_CHANGE); - reg_w1(gspca_dev, R31_UPD, 0x01); - } -} - /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { tv_8532WriteEEprom(gspca_dev); - reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x32); /* slope begin 1,7V, - * slope rate 2 */ - reg_w1(gspca_dev, R94_AD_BITCONTROL, 0x00); - tv_8532ReadRegisters(gspca_dev); - reg_w1(gspca_dev, R3B_Test3, 0x0b); - reg_w2(gspca_dev, R0E_AD_HEIGHTL, 0x0190); - reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, 0x018f); - reg_w1(gspca_dev, R0C_AD_WIDTHL, 0xe8); - reg_w1(gspca_dev, R0D_AD_WIDTHH, 0x03); - - /*******************************************************************/ - reg_w1(gspca_dev, R28_QUANT, 0x90); - /* no compress - fixed Q - quant 0 */ - reg_w1(gspca_dev, R29_LINE, 0x81); - /* 0x84; // CIF | 4 packet 0x29 */ - - /************************************************/ - reg_w1(gspca_dev, R2C_POLARITY, 0x10); - /* 0x48; //0x08; 0x2c */ - reg_w1(gspca_dev, R2D_POINT, 0x14); - /* 0x38; 0x2d */ - reg_w1(gspca_dev, R2E_POINTH, 0x01); - /* 0x04; 0x2e */ - reg_w1(gspca_dev, R2F_POINTB, 0x12); - /* 0x04; 0x2f */ - reg_w1(gspca_dev, R30_POINTBH, 0x01); - /* 0x04; 0x30 */ - reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE); - /* 0x00<-0x84 */ - /*************************************************/ - reg_w1(gspca_dev, R31_UPD, 0x01); /* update registers */ - msleep(200); - reg_w1(gspca_dev, R31_UPD, 0x00); /* end update */ - /*************************************************/ - tv_8532_setReg(gspca_dev); - /*************************************************/ - reg_w1(gspca_dev, R3B_Test3, 0x0b); /* Test0Sel = 11 = GPIO */ - /*************************************************/ - tv_8532_setReg(gspca_dev); - /*************************************************/ - tv_8532_PollReg(gspca_dev); return 0; } -static void setbrightness(struct gspca_dev *gspca_dev) +static void setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, sd->brightness); + reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, sd->exposure); reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE); /* 0x84 */ } -/* -- start the camera -- */ -static int sd_start(struct gspca_dev *gspca_dev) +static void setgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x32); /* slope begin 1,7V, - * slope rate 2 */ - reg_w1(gspca_dev, R94_AD_BITCONTROL, 0x00); - tv_8532ReadRegisters(gspca_dev); - reg_w1(gspca_dev, R3B_Test3, 0x0b); + reg_w2(gspca_dev, R20_GAIN_G1L, sd->gain); + reg_w2(gspca_dev, R22_GAIN_RL, sd->gain); + reg_w2(gspca_dev, R24_GAIN_BL, sd->gain); + reg_w2(gspca_dev, R26_GAIN_G2L, sd->gain); +} - reg_w2(gspca_dev, R0E_AD_HEIGHTL, 0x0190); - setbrightness(gspca_dev); +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; reg_w1(gspca_dev, R0C_AD_WIDTHL, 0xe8); /* 0x20; 0x0c */ reg_w1(gspca_dev, R0D_AD_WIDTHH, 0x03); @@ -371,19 +286,15 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, R2E_POINTH, 0x01); reg_w1(gspca_dev, R2F_POINTB, 0x12); reg_w1(gspca_dev, R30_POINTBH, 0x01); - reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE); + + tv_8532_setReg(gspca_dev); + + setexposure(gspca_dev); + setgain(gspca_dev); + /************************************************/ reg_w1(gspca_dev, R31_UPD, 0x01); /* update registers */ msleep(200); - reg_w1(gspca_dev, R31_UPD, 0x00); /* end update */ - /************************************************/ - tv_8532_setReg(gspca_dev); - /************************************************/ - reg_w1(gspca_dev, R3B_Test3, 0x0b); /* Test0Sel = 11 = GPIO */ - /************************************************/ - tv_8532_setReg(gspca_dev); - /************************************************/ - tv_8532_PollReg(gspca_dev); reg_w1(gspca_dev, R31_UPD, 0x00); /* end update */ gspca_dev->empty_packet = 0; /* check the empty packets */ @@ -428,21 +339,39 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data + gspca_dev->width + 5, gspca_dev->width); } -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->exposure = val; + if (gspca_dev->streaming) + setexposure(gspca_dev); + return 0; +} + +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->exposure; + return 0; +} + +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; - sd->brightness = val; + sd->gain = val; if (gspca_dev->streaming) - setbrightness(gspca_dev); + setgain(gspca_dev); return 0; } -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - *val = sd->brightness; + *val = sd->gain; return 0; } diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c index 732c3df..031266a 100644 --- a/drivers/media/video/gspca/vc032x.c +++ b/drivers/media/video/gspca/vc032x.c @@ -2748,11 +2748,11 @@ static const u8 poxxxx_init_common[][4] = { {0xb3, 0x04, 0x15, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x04, 0xcc}, + {0xb3, 0x22, 0x04, 0xcc}, /* sensor height = 1024 */ {0xb3, 0x23, 0x00, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x16, 0x04, 0xcc}, /* sensor width = 1280 */ {0xb3, 0x17, 0xff, 0xcc}, {0xb3, 0x2c, 0x03, 0xcc}, {0xb3, 0x2d, 0x56, 0xcc}, @@ -2919,7 +2919,7 @@ static const u8 poxxxx_initVGA[][4] = { {0x00, 0x20, 0x11, 0xaa}, {0x00, 0x33, 0x38, 0xaa}, {0x00, 0xbb, 0x0d, 0xaa}, - {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, /* change to 640x480 */ {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, @@ -2935,7 +2935,7 @@ static const u8 poxxxx_initQVGA[][4] = { {0x00, 0x20, 0x33, 0xaa}, {0x00, 0x33, 0x38, 0xaa}, {0x00, 0xbb, 0x0d, 0xaa}, - {0xb3, 0x22, 0x00, 0xcc}, + {0xb3, 0x22, 0x00, 0xcc}, /* change to 320x240 */ {0xb3, 0x23, 0xf0, 0xcc}, {0xb3, 0x16, 0x01, 0xcc}, {0xb3, 0x17, 0x3f, 0xcc}, @@ -3068,37 +3068,84 @@ static const struct sensor_info vc0323_probe_data[] = { }; /* read 'len' bytes in gspca_dev->usb_buf */ -static void reg_r(struct gspca_dev *gspca_dev, +static void reg_r_i(struct gspca_dev *gspca_dev, u16 req, u16 index, u16 len) { - usb_control_msg(gspca_dev->dev, + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), req, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 1, /* value */ index, gspca_dev->usb_buf, len, 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_r err %d", ret); + gspca_dev->usb_err = ret; + } +} +static void reg_r(struct gspca_dev *gspca_dev, + u16 req, + u16 index, + u16 len) +{ + reg_r_i(gspca_dev, req, index, len); +#ifdef GSPCA_DEBUG + if (gspca_dev->usb_err < 0) + return; + if (len == 1) + PDEBUG(D_USBI, "GET %02x 0001 %04x %02x", req, index, + gspca_dev->usb_buf[0]); + else + PDEBUG(D_USBI, "GET %02x 0001 %04x %02x %02x %02x", + req, index, + gspca_dev->usb_buf[0], + gspca_dev->usb_buf[1], + gspca_dev->usb_buf[2]); +#endif } -static void reg_w(struct usb_device *dev, +static void reg_w_i(struct gspca_dev *gspca_dev, u16 req, u16 value, u16 index) { - usb_control_msg(dev, - usb_sndctrlpipe(dev, 0), + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), req, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, index, NULL, 0, 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_w err %d", ret); + gspca_dev->usb_err = ret; + } +} +static void reg_w(struct gspca_dev *gspca_dev, + u16 req, + u16 value, + u16 index) +{ +#ifdef GSPCA_DEBUG + if (gspca_dev->usb_err < 0) + return; + PDEBUG(D_USBO, "SET %02x %04x %04x", req, value, index); +#endif + reg_w_i(gspca_dev, req, value, index); } static u16 read_sensor_register(struct gspca_dev *gspca_dev, u16 address) { - struct usb_device *dev = gspca_dev->dev; u8 ldata, mdata, hdata; int retry = 50; @@ -3108,8 +3155,8 @@ static u16 read_sensor_register(struct gspca_dev *gspca_dev, gspca_dev->usb_buf[0]); return 0; } - reg_w(dev, 0xa0, address, 0xb33a); - reg_w(dev, 0xa0, 0x02, 0xb339); + reg_w(gspca_dev, 0xa0, address, 0xb33a); + reg_w(gspca_dev, 0xa0, 0x02, 0xb339); do { reg_r(gspca_dev, 0xa1, 0xb33b, 1); @@ -3136,15 +3183,15 @@ static u16 read_sensor_register(struct gspca_dev *gspca_dev, static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; int i, n; u16 value; const struct sensor_info *ptsensor_info; /*fixme: should also check the other sensor (back mi1320_soc, front mc501cb)*/ if (sd->flags & FL_SAMSUNG) { - reg_w(dev, 0xa0, 0x01, 0xb301); - reg_w(dev, 0x89, 0xf0ff, 0xffff); /* select the back sensor */ + reg_w(gspca_dev, 0xa0, 0x01, 0xb301); + reg_w(gspca_dev, 0x89, 0xf0ff, 0xffff); + /* select the back sensor */ } reg_r(gspca_dev, 0xa1, 0xbfcf, 1); @@ -3158,13 +3205,13 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) n = ARRAY_SIZE(vc0323_probe_data); } for (i = 0; i < n; i++) { - reg_w(dev, 0xa0, 0x02, 0xb334); - reg_w(dev, 0xa0, ptsensor_info->m1, 0xb300); - reg_w(dev, 0xa0, ptsensor_info->m2, 0xb300); - reg_w(dev, 0xa0, 0x01, 0xb308); - reg_w(dev, 0xa0, 0x0c, 0xb309); - reg_w(dev, 0xa0, ptsensor_info->I2cAdd, 0xb335); - reg_w(dev, 0xa0, ptsensor_info->op, 0xb301); + reg_w(gspca_dev, 0xa0, 0x02, 0xb334); + reg_w(gspca_dev, 0xa0, ptsensor_info->m1, 0xb300); + reg_w(gspca_dev, 0xa0, ptsensor_info->m2, 0xb300); + reg_w(gspca_dev, 0xa0, 0x01, 0xb308); + reg_w(gspca_dev, 0xa0, 0x0c, 0xb309); + reg_w(gspca_dev, 0xa0, ptsensor_info->I2cAdd, 0xb335); + reg_w(gspca_dev, 0xa0, ptsensor_info->op, 0xb301); value = read_sensor_register(gspca_dev, ptsensor_info->IdAdd); if (value == 0 && ptsensor_info->IdAdd == 0x82) value = read_sensor_register(gspca_dev, 0x83); @@ -3192,26 +3239,33 @@ static void i2c_write(struct gspca_dev *gspca_dev, u8 reg, const u8 *val, u8 size) /* 1 or 2 */ { - struct usb_device *dev = gspca_dev->dev; int retry; - reg_r(gspca_dev, 0xa1, 0xb33f, 1); +#ifdef GSPCA_DEBUG + if (gspca_dev->usb_err < 0) + return; + if (size == 1) + PDEBUG(D_USBO, "i2c_w %02x %02x", reg, *val); + else + PDEBUG(D_USBO, "i2c_w %02x %02x%02x", reg, *val, val[1]); +#endif + reg_r_i(gspca_dev, 0xa1, 0xb33f, 1); /*fixme:should check if (!(gspca_dev->usb_buf[0] & 0x02)) error*/ - reg_w(dev, 0xa0, size, 0xb334); - reg_w(dev, 0xa0, reg, 0xb33a); - reg_w(dev, 0xa0, val[0], 0xb336); + reg_w_i(gspca_dev, 0xa0, size, 0xb334); + reg_w_i(gspca_dev, 0xa0, reg, 0xb33a); + reg_w_i(gspca_dev, 0xa0, val[0], 0xb336); if (size > 1) - reg_w(dev, 0xa0, val[1], 0xb337); - reg_w(dev, 0xa0, 0x01, 0xb339); + reg_w_i(gspca_dev, 0xa0, val[1], 0xb337); + reg_w_i(gspca_dev, 0xa0, 0x01, 0xb339); retry = 4; do { - reg_r(gspca_dev, 0xa1, 0xb33b, 1); + reg_r_i(gspca_dev, 0xa1, 0xb33b, 1); if (gspca_dev->usb_buf[0] == 0) break; msleep(20); } while (--retry > 0); if (retry <= 0) - PDEBUG(D_ERR, "i2c_write failed"); + PDEBUG(D_ERR, "i2c_write timeout"); } static void put_tab_to_reg(struct gspca_dev *gspca_dev, @@ -3221,13 +3275,12 @@ static void put_tab_to_reg(struct gspca_dev *gspca_dev, u16 ad = addr; for (j = 0; j < tabsize; j++) - reg_w(gspca_dev->dev, 0xa0, tab[j], ad++); + reg_w(gspca_dev, 0xa0, tab[j], ad++); } static void usb_exchange(struct gspca_dev *gspca_dev, const u8 data[][4]) { - struct usb_device *dev = gspca_dev->dev; int i = 0; for (;;) { @@ -3235,7 +3288,7 @@ static void usb_exchange(struct gspca_dev *gspca_dev, default: return; case 0xcc: /* normal write */ - reg_w(dev, 0xa0, data[i][2], + reg_w(gspca_dev, 0xa0, data[i][2], (data[i][0]) << 8 | data[i][1]); break; case 0xaa: /* i2c op */ @@ -3259,7 +3312,6 @@ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; struct cam *cam; int sensor; static u8 npkt[] = { /* number of packets per ISOC message */ @@ -3363,13 +3415,6 @@ static int sd_config(struct gspca_dev *gspca_dev, if (sd->sensor == SENSOR_OV7670) sd->flags |= FL_HFLIP | FL_VFLIP; - if (sd->bridge == BRIDGE_VC0321) { - reg_r(gspca_dev, 0x8a, 0, 3); - reg_w(dev, 0x87, 0x00, 0x0f0f); - - reg_r(gspca_dev, 0x8b, 0, 3); - reg_w(dev, 0x88, 0x00, 0x0202); - } return 0; } @@ -3378,15 +3423,21 @@ static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - if (sd->sensor == SENSOR_POxxxx) { - reg_r(gspca_dev, 0xa1, 0xb300, 1); - if (gspca_dev->usb_buf[0] != 0) { - reg_w(gspca_dev->dev, 0xa0, 0x26, 0xb300); - reg_w(gspca_dev->dev, 0xa0, 0x04, 0xb300); - reg_w(gspca_dev->dev, 0xa0, 0x00, 0xb300); + if (sd->bridge == BRIDGE_VC0321) { + reg_r(gspca_dev, 0x8a, 0, 3); + reg_w(gspca_dev, 0x87, 0x00, 0x0f0f); + reg_r(gspca_dev, 0x8b, 0, 3); + reg_w(gspca_dev, 0x88, 0x00, 0x0202); + if (sd->sensor == SENSOR_POxxxx) { + reg_r(gspca_dev, 0xa1, 0xb300, 1); + if (gspca_dev->usb_buf[0] != 0) { + reg_w(gspca_dev, 0xa0, 0x26, 0xb300); + reg_w(gspca_dev, 0xa0, 0x04, 0xb300); + reg_w(gspca_dev, 0xa0, 0x00, 0xb300); + } } } - return 0; + return gspca_dev->usb_err; } static void setbrightness(struct gspca_dev *gspca_dev) @@ -3516,17 +3567,17 @@ static int sd_start(struct gspca_dev *gspca_dev) /*fixme: back sensor only*/ if (sd->flags & FL_SAMSUNG) { - reg_w(gspca_dev->dev, 0x89, 0xf0ff, 0xffff); - reg_w(gspca_dev->dev, 0xa9, 0x8348, 0x000e); - reg_w(gspca_dev->dev, 0xa9, 0x0000, 0x001a); + reg_w(gspca_dev, 0x89, 0xf0ff, 0xffff); + reg_w(gspca_dev, 0xa9, 0x8348, 0x000e); + reg_w(gspca_dev, 0xa9, 0x0000, 0x001a); } /* Assume start use the good resolution from gspca_dev->mode */ if (sd->bridge == BRIDGE_VC0321) { - reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfec); - reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfed); - reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfee); - reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfef); + reg_w(gspca_dev, 0xa0, 0xff, 0xbfec); + reg_w(gspca_dev, 0xa0, 0xff, 0xbfed); + reg_w(gspca_dev, 0xa0, 0xff, 0xbfee); + reg_w(gspca_dev, 0xa0, 0xff, 0xbfef); sd->image_offset = 46; } else { if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].pixelformat @@ -3617,7 +3668,7 @@ static int sd_start(struct gspca_dev *gspca_dev) init = poxxxx_initVGA; usb_exchange(gspca_dev, init); reg_r(gspca_dev, 0x8c, 0x0000, 3); - reg_w(gspca_dev->dev, 0xa0, + reg_w(gspca_dev, 0xa0, gspca_dev->usb_buf[2] & 1 ? 0 : 1, 0xb35c); msleep(300); @@ -3635,10 +3686,10 @@ static int sd_start(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_PO1200: case SENSOR_HV7131R: - reg_w(gspca_dev->dev, 0x89, 0x0400, 0x1415); + reg_w(gspca_dev, 0x89, 0x0400, 0x1415); break; case SENSOR_MI1310_SOC: - reg_w(gspca_dev->dev, 0x89, 0x058c, 0x0000); + reg_w(gspca_dev, 0x89, 0x058c, 0x0000); break; } msleep(100); @@ -3648,9 +3699,9 @@ static int sd_start(struct gspca_dev *gspca_dev) } switch (sd->sensor) { case SENSOR_OV7670: - reg_w(gspca_dev->dev, 0x87, 0xffff, 0xffff); - reg_w(gspca_dev->dev, 0x88, 0xff00, 0xf0f1); - reg_w(gspca_dev->dev, 0xa0, 0x0000, 0xbfff); + reg_w(gspca_dev, 0x87, 0xffff, 0xffff); + reg_w(gspca_dev, 0x88, 0xff00, 0xf0f1); + reg_w(gspca_dev, 0xa0, 0x0000, 0xbfff); break; case SENSOR_POxxxx: setcolors(gspca_dev); @@ -3659,51 +3710,49 @@ static int sd_start(struct gspca_dev *gspca_dev) /* led on */ msleep(80); - reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); + reg_w(gspca_dev, 0x89, 0xffff, 0xfdff); usb_exchange(gspca_dev, poxxxx_init_end_2); break; } - return 0; + return gspca_dev->usb_err; } static void sd_stopN(struct gspca_dev *gspca_dev) { - struct usb_device *dev = gspca_dev->dev; struct sd *sd = (struct sd *) gspca_dev; switch (sd->sensor) { case SENSOR_MI1310_SOC: - reg_w(dev, 0x89, 0x058c, 0x00ff); + reg_w(gspca_dev, 0x89, 0x058c, 0x00ff); break; case SENSOR_POxxxx: return; default: if (!(sd->flags & FL_SAMSUNG)) - reg_w(dev, 0x89, 0xffff, 0xffff); + reg_w(gspca_dev, 0x89, 0xffff, 0xffff); break; } - reg_w(dev, 0xa0, 0x01, 0xb301); - reg_w(dev, 0xa0, 0x09, 0xb003); + reg_w(gspca_dev, 0xa0, 0x01, 0xb301); + reg_w(gspca_dev, 0xa0, 0x09, 0xb003); } /* called on streamoff with alt 0 and on disconnect */ static void sd_stop0(struct gspca_dev *gspca_dev) { - struct usb_device *dev = gspca_dev->dev; struct sd *sd = (struct sd *) gspca_dev; if (!gspca_dev->present) return; /*fixme: is this useful?*/ if (sd->sensor == SENSOR_MI1310_SOC) - reg_w(dev, 0x89, 0x058c, 0x00ff); + reg_w(gspca_dev, 0x89, 0x058c, 0x00ff); else if (!(sd->flags & FL_SAMSUNG)) - reg_w(dev, 0x89, 0xffff, 0xffff); + reg_w(gspca_dev, 0x89, 0xffff, 0xffff); if (sd->sensor == SENSOR_POxxxx) { - reg_w(dev, 0xa0, 0x26, 0xb300); - reg_w(dev, 0xa0, 0x04, 0xb300); - reg_w(dev, 0xa0, 0x00, 0xb300); + reg_w(gspca_dev, 0xa0, 0x26, 0xb300); + reg_w(gspca_dev, 0xa0, 0x04, 0xb300); + reg_w(gspca_dev, 0xa0, 0x00, 0xb300); } } @@ -3726,17 +3775,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* The vc0321 sends some additional data after sending the complete * frame, we ignore this. */ if (sd->bridge == BRIDGE_VC0321) { - struct gspca_frame *frame; - int l; + int size, l; - frame = gspca_get_i_frame(gspca_dev); - if (frame == NULL) { - gspca_dev->last_packet_type = DISCARD_PACKET; - return; - } - l = frame->data_end - frame->data; - if (len > frame->v4l2_buf.length - l) - len = frame->v4l2_buf.length - l; + l = gspca_dev->image_len; + size = gspca_dev->frsz; + if (len > size - l) + len = size - l; } gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } @@ -3748,7 +3792,7 @@ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) sd->brightness = val; if (gspca_dev->streaming) setbrightness(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) @@ -3766,7 +3810,7 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) sd->contrast = val; if (gspca_dev->streaming) setcontrast(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) @@ -3784,7 +3828,7 @@ static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) sd->colors = val; if (gspca_dev->streaming) setcolors(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) @@ -3802,7 +3846,7 @@ static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) sd->hflip = val; if (gspca_dev->streaming) sethvflip(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val) @@ -3820,7 +3864,7 @@ static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) sd->vflip = val; if (gspca_dev->streaming) sethvflip(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) @@ -3838,7 +3882,7 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) sd->lightfreq = val; if (gspca_dev->streaming) setlightfreq(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) @@ -3856,7 +3900,7 @@ static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) sd->sharpness = val; if (gspca_dev->streaming) setsharpness(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) diff --git a/drivers/media/video/gspca/w996Xcf.c b/drivers/media/video/gspca/w996Xcf.c index 2fffe20..38a6859 100644 --- a/drivers/media/video/gspca/w996Xcf.c +++ b/drivers/media/video/gspca/w996Xcf.c @@ -31,14 +31,10 @@ the sensor drivers to v4l2 sub drivers, and properly split of this driver from ov519.c */ -/* The CONEX_CAM define for jpeg.h needs renaming, now its used here too */ -#define CONEX_CAM -#include "jpeg.h" - #define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */ -#define Y_QUANTABLE (sd->jpeg_hdr + JPEG_QT0_OFFSET) -#define UV_QUANTABLE (sd->jpeg_hdr + JPEG_QT1_OFFSET) +#define Y_QUANTABLE (&sd->jpeg_hdr[JPEG_QT0_OFFSET]) +#define UV_QUANTABLE (&sd->jpeg_hdr[JPEG_QT1_OFFSET]) static const struct v4l2_pix_format w9968cf_vga_mode[] = { {160, 120, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, @@ -509,11 +505,6 @@ static int w9968cf_mode_init_regs(struct sd *sd) if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat == V4L2_PIX_FMT_JPEG) { /* We may get called multiple times (usb isoc bw negotiat.) */ - if (!sd->jpeg_hdr) - sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); - if (!sd->jpeg_hdr) - return -ENOMEM; - jpeg_define(sd->jpeg_hdr, sd->gspca_dev.height, sd->gspca_dev.width, 0x22); /* JPEG 420 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); @@ -562,9 +553,6 @@ static void w9968cf_stop0(struct sd *sd) reg_w(sd, 0x39, 0x0000); /* disable JPEG encoder */ reg_w(sd, 0x16, 0x0000); /* stop video capture */ } - - kfree(sd->jpeg_hdr); - sd->jpeg_hdr = NULL; } /* The w9968cf docs say that a 0 sized packet means EOF (and also SOF diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index d02aa5c..4473f0f 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -22,7 +22,6 @@ #define MODULE_NAME "zc3xx" #include <linux/input.h> -#include <linux/slab.h> #include "gspca.h" #include "jpeg.h" @@ -40,15 +39,16 @@ static int force_sensor = -1; struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ + u8 brightness; u8 contrast; u8 gamma; u8 autogain; u8 lightfreq; u8 sharpness; u8 quality; /* image quality */ -#define QUALITY_MIN 40 -#define QUALITY_MAX 60 -#define QUALITY_DEF 50 +#define QUALITY_MIN 50 +#define QUALITY_MAX 80 +#define QUALITY_DEF 70 u8 sensor; /* Type of image sensor chip */ /* !! values used in different tables */ @@ -75,10 +75,12 @@ struct sd { #define SENSOR_MAX 19 unsigned short chip_revision; - u8 *jpeg_hdr; + u8 jpeg_hdr[JPEG_HDR_SZ]; }; /* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); @@ -93,6 +95,20 @@ static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); static const struct ctrl sd_ctrls[] = { { { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, +#define BRIGHTNESS_DEF 128 + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { + { .id = V4L2_CID_CONTRAST, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Contrast", @@ -132,7 +148,7 @@ static const struct ctrl sd_ctrls[] = { .set = sd_setautogain, .get = sd_getautogain, }, -#define LIGHTFREQ_IDX 3 +#define LIGHTFREQ_IDX 4 { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -6011,9 +6027,12 @@ static void setcontrast(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; const u8 *Tgamma; - int g, i, k, adj, gp; + int g, i, brightness, contrast, adj, gp1, gp2; u8 gr[16]; - static const u8 delta_tb[16] = /* delta for contrast */ + static const u8 delta_b[16] = /* delta for brightness */ + {0x50, 0x38, 0x2d, 0x28, 0x24, 0x21, 0x1e, 0x1d, + 0x1d, 0x1b, 0x1b, 0x1b, 0x19, 0x18, 0x18, 0x18}; + static const u8 delta_c[16] = /* delta for contrast */ {0x2c, 0x1a, 0x12, 0x0c, 0x0a, 0x06, 0x06, 0x06, 0x04, 0x06, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02}; static const u8 gamma_tb[6][16] = { @@ -6033,30 +6052,30 @@ static void setcontrast(struct gspca_dev *gspca_dev) Tgamma = gamma_tb[sd->gamma - 1]; - k = ((int) sd->contrast - 128); /* -128 / 128 */ + contrast = ((int) sd->contrast - 128); /* -128 / 127 */ + brightness = ((int) sd->brightness - 128); /* -128 / 92 */ adj = 0; - gp = 0; + gp1 = gp2 = 0; for (i = 0; i < 16; i++) { - g = Tgamma[i] - delta_tb[i] * k / 256 - adj / 2; + g = Tgamma[i] + delta_b[i] * brightness / 256 + - delta_c[i] * contrast / 256 - adj / 2; if (g > 0xff) g = 0xff; else if (g < 0) g = 0; reg_w(dev, g, 0x0120 + i); /* gamma */ - if (k > 0) + if (contrast > 0) adj--; - else + else if (contrast < 0) adj++; - - if (i != 0) { - if (gp == 0) - gr[i - 1] = 0; - else - gr[i - 1] = g - gp; - } - gp = g; + if (i > 1) + gr[i - 1] = (g - gp2) / 2; + else if (i != 0) + gr[0] = gp1 == 0 ? 0 : (g - gp1); + gp2 = gp1; + gp1 = g; } - gr[15] = gr[14] / 2; + gr[15] = (0xff - gp2) / 2; for (i = 0; i < 16; i++) reg_w(dev, gr[i], 0x0130 + i); /* gradient */ } @@ -6744,6 +6763,7 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->nmodes = ARRAY_SIZE(broken_vga_mode); break; } + sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; sd->gamma = gamma[sd->sensor]; sd->autogain = AUTOGAIN_DEF; @@ -6798,9 +6818,6 @@ static int sd_start(struct gspca_dev *gspca_dev) }; /* create the JPEG header */ - sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); - if (!sd->jpeg_hdr) - return -ENOMEM; jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); @@ -6918,10 +6935,6 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(dev, 0x00, 0x0007); /* (from win traces) */ reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING); break; - case SENSOR_PAS202B: - reg_w(dev, 0x32, 0x0007); /* (from win traces) */ - reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING); - break; } return 0; } @@ -6931,7 +6944,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - kfree(sd->jpeg_hdr); if (!gspca_dev->present) return; send_unknown(gspca_dev->dev, sd->sensor); @@ -6962,6 +6974,24 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -7163,9 +7193,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x046d, 0x08aa)}, {USB_DEVICE(0x046d, 0x08ac)}, {USB_DEVICE(0x046d, 0x08ad)}, -#if !defined CONFIG_USB_ZC0301 && !defined CONFIG_USB_ZC0301_MODULE {USB_DEVICE(0x046d, 0x08ae)}, -#endif {USB_DEVICE(0x046d, 0x08af)}, {USB_DEVICE(0x046d, 0x08b9)}, {USB_DEVICE(0x046d, 0x08d7)}, diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c index 830d47b..0cae5b8 100644 --- a/drivers/media/video/hdpvr/hdpvr-core.c +++ b/drivers/media/video/hdpvr/hdpvr-core.c @@ -286,6 +286,8 @@ static int hdpvr_probe(struct usb_interface *interface, goto error; } + dev->workqueue = 0; + /* register v4l2_device early so it can be used for printks */ if (v4l2_device_register(&interface->dev, &dev->v4l2_dev)) { err("v4l2_device_register failed"); @@ -380,6 +382,9 @@ static int hdpvr_probe(struct usb_interface *interface, error: if (dev) { + /* Destroy single thread */ + if (dev->workqueue) + destroy_workqueue(dev->workqueue); /* this frees allocated memory */ hdpvr_delete(dev); } diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c index c338f3f..4863a21 100644 --- a/drivers/media/video/hdpvr/hdpvr-video.c +++ b/drivers/media/video/hdpvr/hdpvr-video.c @@ -394,7 +394,7 @@ err: static int hdpvr_release(struct file *file) { - struct hdpvr_fh *fh = (struct hdpvr_fh *)file->private_data; + struct hdpvr_fh *fh = file->private_data; struct hdpvr_device *dev = fh->dev; if (!dev) @@ -518,7 +518,7 @@ err: static unsigned int hdpvr_poll(struct file *filp, poll_table *wait) { struct hdpvr_buffer *buf = NULL; - struct hdpvr_fh *fh = (struct hdpvr_fh *)filp->private_data; + struct hdpvr_fh *fh = filp->private_data; struct hdpvr_device *dev = fh->dev; unsigned int mask = 0; diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index 29d4397..27ae8bb 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -47,7 +47,7 @@ #include <linux/i2c-id.h> #include <linux/workqueue.h> -#include <media/ir-common.h> +#include <media/ir-core.h> #include <media/ir-kbd-i2c.h> /* ----------------------------------------------------------------------- */ @@ -272,11 +272,8 @@ static void ir_key_poll(struct IR_i2c *ir) return; } - if (0 == rc) { - ir_input_nokey(ir->input, &ir->ir); - } else { - ir_input_keydown(ir->input, &ir->ir, ir_key); - } + if (rc) + ir_keydown(ir->input, ir_key, 0); } static void ir_work(struct work_struct *work) @@ -439,10 +436,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) dev_name(&client->dev)); /* init + register input device */ - err = ir_input_init(input_dev, &ir->ir, ir_type); - if (err < 0) - goto err_out_free; - + ir->ir_type = ir_type; input_dev->id.bustype = BUS_I2C; input_dev->name = ir->name; input_dev->phys = ir->phys; diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 1b79475..90daa6e 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -130,6 +130,9 @@ static int ivtv_yuv_threshold = -1; static int ivtv_pci_latency = 1; int ivtv_debug; +#ifdef CONFIG_VIDEO_ADV_DEBUG +int ivtv_fw_debug; +#endif static int tunertype = -1; static int newi2c = -1; @@ -141,6 +144,9 @@ module_param_string(pal, pal, sizeof(pal), 0644); module_param_string(secam, secam, sizeof(secam), 0644); module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); module_param_named(debug,ivtv_debug, int, 0644); +#ifdef CONFIG_VIDEO_ADV_DEBUG +module_param_named(fw_debug, ivtv_fw_debug, int, 0644); +#endif module_param(ivtv_pci_latency, int, 0644); module_param(ivtv_yuv_mode, int, 0644); module_param(ivtv_yuv_threshold, int, 0644); @@ -217,6 +223,10 @@ MODULE_PARM_DESC(debug, "\t\t\t 256/0x0100: yuv\n" "\t\t\t 512/0x0200: i2c\n" "\t\t\t1024/0x0400: high volume\n"); +#ifdef CONFIG_VIDEO_ADV_DEBUG +MODULE_PARM_DESC(fw_debug, + "Enable code for debugging firmware problems. Default: 0\n"); +#endif MODULE_PARM_DESC(ivtv_pci_latency, "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n" "\t\t\tDefault: Yes"); @@ -1425,12 +1435,16 @@ EXPORT_SYMBOL(ivtv_vapi); EXPORT_SYMBOL(ivtv_vapi_result); EXPORT_SYMBOL(ivtv_clear_irq_mask); EXPORT_SYMBOL(ivtv_debug); +#ifdef CONFIG_VIDEO_ADV_DEBUG +EXPORT_SYMBOL(ivtv_fw_debug); +#endif EXPORT_SYMBOL(ivtv_reset_ir_gpio); EXPORT_SYMBOL(ivtv_udma_setup); EXPORT_SYMBOL(ivtv_udma_unmap); EXPORT_SYMBOL(ivtv_udma_alloc); EXPORT_SYMBOL(ivtv_udma_prepare); EXPORT_SYMBOL(ivtv_init_on_first_open); +EXPORT_SYMBOL(ivtv_firmware_check); module_init(module_start); module_exit(module_cleanup); diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h index 5b45fd2..bd084df 100644 --- a/drivers/media/video/ivtv/ivtv-driver.h +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -122,6 +122,9 @@ /* debugging */ extern int ivtv_debug; +#ifdef CONFIG_VIDEO_ADV_DEBUG +extern int ivtv_fw_debug; +#endif #define IVTV_DBGFLG_WARN (1 << 0) #define IVTV_DBGFLG_INFO (1 << 1) @@ -734,6 +737,7 @@ struct ivtv { struct v4l2_rect osd_rect; /* current OSD position and size */ struct v4l2_rect main_rect; /* current Main window position and size */ struct osd_info *osd_info; /* ivtvfb private OSD info */ + void (*ivtvfb_restore)(struct ivtv *itv); /* Used for a warm start */ }; static inline struct ivtv *to_ivtv(struct v4l2_device *v4l2_dev) diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c index 3c2cc27..a6a2cdb 100644 --- a/drivers/media/video/ivtv/ivtv-fileops.c +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -32,6 +32,7 @@ #include "ivtv-yuv.h" #include "ivtv-ioctl.h" #include "ivtv-cards.h" +#include "ivtv-firmware.h" #include <media/v4l2-event.h> #include <media/saa7115.h> @@ -526,6 +527,7 @@ int ivtv_start_decoding(struct ivtv_open_id *id, int speed) { struct ivtv *itv = id->itv; struct ivtv_stream *s = &itv->streams[id->type]; + int rc; if (atomic_read(&itv->decoding) == 0) { if (ivtv_claim_stream(id, s->type)) { @@ -533,7 +535,13 @@ int ivtv_start_decoding(struct ivtv_open_id *id, int speed) IVTV_DEBUG_WARN("start decode, stream already claimed\n"); return -EBUSY; } - ivtv_start_v4l2_decode_stream(s, 0); + rc = ivtv_start_v4l2_decode_stream(s, 0); + if (rc < 0) { + if (rc == -EAGAIN) + rc = ivtv_start_v4l2_decode_stream(s, 0); + if (rc < 0) + return rc; + } } if (s->type == IVTV_DEC_STREAM_TYPE_MPG) return ivtv_set_speed(itv, speed); @@ -912,12 +920,32 @@ int ivtv_v4l2_close(struct file *filp) static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) { +#ifdef CONFIG_VIDEO_ADV_DEBUG + struct video_device *vdev = video_devdata(filp); +#endif struct ivtv *itv = s->itv; struct ivtv_open_id *item; int res = 0; IVTV_DEBUG_FILE("open %s\n", s->name); +#ifdef CONFIG_VIDEO_ADV_DEBUG + /* Unless ivtv_fw_debug is set, error out if firmware dead. */ + if (ivtv_fw_debug) { + IVTV_WARN("Opening %s with dead firmware lockout disabled\n", + video_device_node_name(vdev)); + IVTV_WARN("Selected firmware errors will be ignored\n"); + } else { +#else + if (1) { +#endif + res = ivtv_firmware_check(itv, "ivtv_serialized_open"); + if (res == -EAGAIN) + res = ivtv_firmware_check(itv, "ivtv_serialized_open"); + if (res < 0) + return -EIO; + } + if (s->type == IVTV_DEC_STREAM_TYPE_MPG && test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_YUV].s_flags)) return -EBUSY; diff --git a/drivers/media/video/ivtv/ivtv-firmware.c b/drivers/media/video/ivtv/ivtv-firmware.c index a71e8ba..d8bf2b0 100644 --- a/drivers/media/video/ivtv/ivtv-firmware.c +++ b/drivers/media/video/ivtv/ivtv-firmware.c @@ -23,7 +23,10 @@ #include "ivtv-mailbox.h" #include "ivtv-firmware.h" #include "ivtv-yuv.h" +#include "ivtv-ioctl.h" +#include "ivtv-cards.h" #include <linux/firmware.h> +#include <media/saa7127.h> #define IVTV_MASK_SPU_ENABLE 0xFFFFFFFE #define IVTV_MASK_VPU_ENABLE15 0xFFFFFFF6 @@ -271,3 +274,122 @@ void ivtv_init_mpeg_decoder(struct ivtv *itv) } ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 4, 0, 0, 0, 1); } + +/* Try to restart the card & restore previous settings */ +int ivtv_firmware_restart(struct ivtv *itv) +{ + int rc = 0; + v4l2_std_id std; + struct ivtv_open_id fh; + fh.itv = itv; + + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) + /* Display test image during restart */ + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing, + SAA7127_INPUT_TYPE_TEST_IMAGE, + itv->card->video_outputs[itv->active_output].video_output, + 0); + + mutex_lock(&itv->udma.lock); + + rc = ivtv_firmware_init(itv); + if (rc) { + mutex_unlock(&itv->udma.lock); + return rc; + } + + /* Allow settings to reload */ + ivtv_mailbox_cache_invalidate(itv); + + /* Restore video standard */ + std = itv->std; + itv->std = 0; + ivtv_s_std(NULL, &fh, &std); + + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { + ivtv_init_mpeg_decoder(itv); + + /* Restore framebuffer if active */ + if (itv->ivtvfb_restore) + itv->ivtvfb_restore(itv); + + /* Restore alpha settings */ + ivtv_set_osd_alpha(itv); + + /* Restore normal output */ + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing, + SAA7127_INPUT_TYPE_NORMAL, + itv->card->video_outputs[itv->active_output].video_output, + 0); + } + + mutex_unlock(&itv->udma.lock); + return rc; +} + +/* Check firmware running state. The checks fall through + allowing multiple failures to be logged. */ +int ivtv_firmware_check(struct ivtv *itv, char *where) +{ + int res = 0; + + /* Check encoder is still running */ + if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0) < 0) { + IVTV_WARN("Encoder has died : %s\n", where); + res = -1; + } + + /* Also check audio. Only check if not in use & encoder is okay */ + if (!res && !atomic_read(&itv->capturing) && + (!atomic_read(&itv->decoding) || + (atomic_read(&itv->decoding) < 2 && test_bit(IVTV_F_I_DEC_YUV, + &itv->i_flags)))) { + + if (ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12) < 0) { + IVTV_WARN("Audio has died (Encoder OK) : %s\n", where); + res = -2; + } + } + + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { + /* Second audio check. Skip if audio already failed */ + if (res != -2 && read_dec(0x100) != read_dec(0x104)) { + /* Wait & try again to be certain. */ + ivtv_msleep_timeout(14, 0); + if (read_dec(0x100) != read_dec(0x104)) { + IVTV_WARN("Audio has died (Decoder) : %s\n", + where); + res = -1; + } + } + + /* Check decoder is still running */ + if (ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0) < 0) { + IVTV_WARN("Decoder has died : %s\n", where); + res = -1; + } + } + + /* If something failed & currently idle, try to reload */ + if (res && !atomic_read(&itv->capturing) && + !atomic_read(&itv->decoding)) { + IVTV_INFO("Detected in %s that firmware had failed - " + "Reloading\n", where); + res = ivtv_firmware_restart(itv); + /* + * Even if restarted ok, still signal a problem had occured. + * The caller can come through this function again to check + * if things are really ok after the restart. + */ + if (!res) { + IVTV_INFO("Firmware restart okay\n"); + res = -EAGAIN; + } else { + IVTV_INFO("Firmware restart failed\n"); + } + } else if (res) { + res = -EIO; + } + + return res; +} diff --git a/drivers/media/video/ivtv/ivtv-firmware.h b/drivers/media/video/ivtv/ivtv-firmware.h index 041ba94..52bb4e5 100644 --- a/drivers/media/video/ivtv/ivtv-firmware.h +++ b/drivers/media/video/ivtv/ivtv-firmware.h @@ -26,5 +26,6 @@ int ivtv_firmware_init(struct ivtv *itv); void ivtv_firmware_versions(struct ivtv *itv); void ivtv_halt_firmware(struct ivtv *itv); void ivtv_init_mpeg_decoder(struct ivtv *itv); +int ivtv_firmware_check(struct ivtv *itv, char *where); #endif diff --git a/drivers/media/video/ivtv/ivtv-mailbox.c b/drivers/media/video/ivtv/ivtv-mailbox.c index 84577f6..e3ce967 100644 --- a/drivers/media/video/ivtv/ivtv-mailbox.c +++ b/drivers/media/video/ivtv/ivtv-mailbox.c @@ -377,3 +377,11 @@ void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb, for (i = 0; i < argc; i++, p++) data[i] = readl(p); } + +/* Wipe api cache */ +void ivtv_mailbox_cache_invalidate(struct ivtv *itv) +{ + int i; + for (i = 0; i < 256; i++) + itv->api_cache[i].last_jiffies = 0; +} diff --git a/drivers/media/video/ivtv/ivtv-mailbox.h b/drivers/media/video/ivtv/ivtv-mailbox.h index 8247662..2c834d2 100644 --- a/drivers/media/video/ivtv/ivtv-mailbox.h +++ b/drivers/media/video/ivtv/ivtv-mailbox.h @@ -30,5 +30,6 @@ int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[]); int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...); int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...); int ivtv_api_func(void *priv, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]); +void ivtv_mailbox_cache_invalidate(struct ivtv *itv); #endif diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index a937e2f..55df419 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -42,6 +42,7 @@ #include "ivtv-yuv.h" #include "ivtv-cards.h" #include "ivtv-streams.h" +#include "ivtv-firmware.h" #include <media/v4l2-event.h> static const struct v4l2_file_operations ivtv_v4l2_enc_fops = { @@ -674,12 +675,14 @@ static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s) /* Decoder sometimes dies here, so wait a moment */ ivtv_msleep_timeout(10, 0); - return 0; + /* Known failure point for firmware, so check */ + return ivtv_firmware_check(itv, "ivtv_setup_v4l2_decode_stream"); } int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset) { struct ivtv *itv = s->itv; + int rc; if (s->vdev == NULL) return -EINVAL; @@ -689,7 +692,11 @@ int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset) IVTV_DEBUG_INFO("Starting decode stream %s (gop_offset %d)\n", s->name, gop_offset); - ivtv_setup_v4l2_decode_stream(s); + rc = ivtv_setup_v4l2_decode_stream(s); + if (rc < 0) { + clear_bit(IVTV_F_S_STREAMING, &s->s_flags); + return rc; + } /* set dma size to 65536 bytes */ ivtv_vapi(itv, CX2341X_DEC_SET_DMA_BLOCK_SIZE, 1, 65536); diff --git a/drivers/media/video/ivtv/ivtv-version.h b/drivers/media/video/ivtv/ivtv-version.h index b530dec..b67a404 100644 --- a/drivers/media/video/ivtv/ivtv-version.h +++ b/drivers/media/video/ivtv/ivtv-version.h @@ -23,7 +23,7 @@ #define IVTV_DRIVER_NAME "ivtv" #define IVTV_DRIVER_VERSION_MAJOR 1 #define IVTV_DRIVER_VERSION_MINOR 4 -#define IVTV_DRIVER_VERSION_PATCHLEVEL 1 +#define IVTV_DRIVER_VERSION_PATCHLEVEL 2 #define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL) #define IVTV_DRIVER_VERSION KERNEL_VERSION(IVTV_DRIVER_VERSION_MAJOR,IVTV_DRIVER_VERSION_MINOR,IVTV_DRIVER_VERSION_PATCHLEVEL) diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c index 9ff3425..be03a71 100644 --- a/drivers/media/video/ivtv/ivtvfb.c +++ b/drivers/media/video/ivtv/ivtvfb.c @@ -53,6 +53,7 @@ #include "ivtv-i2c.h" #include "ivtv-udma.h" #include "ivtv-mailbox.h" +#include "ivtv-firmware.h" /* card parameters */ static int ivtvfb_card_id = -1; @@ -178,6 +179,12 @@ struct osd_info { struct fb_info ivtvfb_info; struct fb_var_screeninfo ivtvfb_defined; struct fb_fix_screeninfo ivtvfb_fix; + + /* Used for a warm start */ + struct fb_var_screeninfo fbvar_cur; + int blank_cur; + u32 palette_cur[256]; + u32 pan_cur; }; struct ivtv_osd_coords { @@ -199,6 +206,7 @@ static int ivtvfb_get_framebuffer(struct ivtv *itv, u32 *fbbase, u32 data[CX2341X_MBOX_MAX_DATA]; int rc; + ivtv_firmware_check(itv, "ivtvfb_get_framebuffer"); rc = ivtv_vapi_result(itv, data, CX2341X_OSD_GET_FRAMEBUFFER, 0); *fbbase = data[0]; *fblength = data[1]; @@ -581,8 +589,10 @@ static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var) ivtv_window.height = var->yres; /* Minimum margin cannot be 0, as X won't allow such a mode */ - if (!var->upper_margin) var->upper_margin++; - if (!var->left_margin) var->left_margin++; + if (!var->upper_margin) + var->upper_margin++; + if (!var->left_margin) + var->left_margin++; ivtv_window.top = var->upper_margin - 1; ivtv_window.left = var->left_margin - 1; @@ -595,6 +605,9 @@ static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var) /* Force update of yuv registers */ itv->yuv_info.yuv_forced_update = 1; + /* Keep a copy of these settings */ + memcpy(&oi->fbvar_cur, var, sizeof(oi->fbvar_cur)); + IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n", var->xres, var->yres, var->xres_virtual, var->yres_virtual, @@ -829,6 +842,8 @@ static int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *inf itv->yuv_info.osd_y_pan = var->yoffset; /* Force update of yuv registers */ itv->yuv_info.yuv_forced_update = 1; + /* Remember this value */ + itv->osd_info->pan_cur = osd_pan_index; return 0; } @@ -842,6 +857,7 @@ static int ivtvfb_set_par(struct fb_info *info) rc = ivtvfb_set_var(itv, &info->var); ivtvfb_pan_display(&info->var, info); ivtvfb_get_fix(itv, &info->fix); + ivtv_firmware_check(itv, "ivtvfb_set_par"); return rc; } @@ -859,6 +875,7 @@ static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green, if (info->var.bits_per_pixel <= 8) { write_reg(regno, 0x02a30); write_reg(color, 0x02a34); + itv->osd_info->palette_cur[regno] = color; return 0; } if (regno >= 16) @@ -911,6 +928,7 @@ static int ivtvfb_blank(int blank_mode, struct fb_info *info) ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0); break; } + itv->osd_info->blank_cur = blank_mode; return 0; } @@ -929,6 +947,21 @@ static struct fb_ops ivtvfb_ops = { .fb_blank = ivtvfb_blank, }; +/* Restore hardware after firmware restart */ +static void ivtvfb_restore(struct ivtv *itv) +{ + struct osd_info *oi = itv->osd_info; + int i; + + ivtvfb_set_var(itv, &oi->fbvar_cur); + ivtvfb_blank(oi->blank_cur, &oi->ivtvfb_info); + for (i = 0; i < 256; i++) { + write_reg(i, 0x02a30); + write_reg(oi->palette_cur[i], 0x02a34); + } + write_reg(oi->pan_cur, 0x02a0c); +} + /* Initialization */ @@ -1192,6 +1225,9 @@ static int ivtvfb_init_card(struct ivtv *itv) /* Enable the osd */ ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info); + /* Enable restart */ + itv->ivtvfb_restore = ivtvfb_restore; + /* Allocate DMA */ ivtv_udma_alloc(itv); return 0; @@ -1203,7 +1239,7 @@ static int __init ivtvfb_callback_init(struct device *dev, void *p) struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev); - if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { if (ivtvfb_init_card(itv) == 0) { IVTVFB_INFO("Framebuffer registered on %s\n", itv->v4l2_dev.name); @@ -1219,13 +1255,14 @@ static int ivtvfb_callback_cleanup(struct device *dev, void *p) struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev); struct osd_info *oi = itv->osd_info; - if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) { IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n", itv->instance); return 0; } IVTVFB_INFO("Unregister framebuffer %d\n", itv->instance); + itv->ivtvfb_restore = NULL; ivtvfb_blank(FB_BLANK_VSYNC_SUSPEND, &oi->ivtvfb_info); ivtvfb_release_buffers(itv); itv->osd_video_pbase = 0; diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c index 10ddecc..4525335 100644 --- a/drivers/media/video/mem2mem_testdev.c +++ b/drivers/media/video/mem2mem_testdev.c @@ -903,14 +903,14 @@ static int m2mtest_release(struct file *file) static unsigned int m2mtest_poll(struct file *file, struct poll_table_struct *wait) { - struct m2mtest_ctx *ctx = (struct m2mtest_ctx *)file->private_data; + struct m2mtest_ctx *ctx = file->private_data; return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); } static int m2mtest_mmap(struct file *file, struct vm_area_struct *vma) { - struct m2mtest_ctx *ctx = (struct m2mtest_ctx *)file->private_data; + struct m2mtest_ctx *ctx = file->private_data; return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); } diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c index fbd0fc7..31cc3d0 100644 --- a/drivers/media/video/mt9m111.c +++ b/drivers/media/video/mt9m111.c @@ -143,10 +143,10 @@ static const struct mt9m111_datafmt *mt9m111_find_datafmt( } static const struct mt9m111_datafmt mt9m111_colour_fmts[] = { - {V4L2_MBUS_FMT_YUYV8_2X8_LE, V4L2_COLORSPACE_JPEG}, - {V4L2_MBUS_FMT_YVYU8_2X8_LE, V4L2_COLORSPACE_JPEG}, - {V4L2_MBUS_FMT_YUYV8_2X8_BE, V4L2_COLORSPACE_JPEG}, - {V4L2_MBUS_FMT_YVYU8_2X8_BE, V4L2_COLORSPACE_JPEG}, + {V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG}, + {V4L2_MBUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG}, + {V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG}, + {V4L2_MBUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG}, {V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB}, {V4L2_MBUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB}, {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, @@ -505,22 +505,22 @@ static int mt9m111_set_pixfmt(struct i2c_client *client, case V4L2_MBUS_FMT_RGB565_2X8_LE: ret = mt9m111_setfmt_rgb565(client); break; - case V4L2_MBUS_FMT_YUYV8_2X8_BE: + case V4L2_MBUS_FMT_UYVY8_2X8: mt9m111->swap_yuv_y_chromas = 0; mt9m111->swap_yuv_cb_cr = 0; ret = mt9m111_setfmt_yuv(client); break; - case V4L2_MBUS_FMT_YVYU8_2X8_BE: + case V4L2_MBUS_FMT_VYUY8_2X8: mt9m111->swap_yuv_y_chromas = 0; mt9m111->swap_yuv_cb_cr = 1; ret = mt9m111_setfmt_yuv(client); break; - case V4L2_MBUS_FMT_YUYV8_2X8_LE: + case V4L2_MBUS_FMT_YUYV8_2X8: mt9m111->swap_yuv_y_chromas = 1; mt9m111->swap_yuv_cb_cr = 0; ret = mt9m111_setfmt_yuv(client); break; - case V4L2_MBUS_FMT_YVYU8_2X8_LE: + case V4L2_MBUS_FMT_YVYU8_2X8: mt9m111->swap_yuv_y_chromas = 1; mt9m111->swap_yuv_cb_cr = 1; ret = mt9m111_setfmt_yuv(client); diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c index e4bf1db..8ec47e4 100644 --- a/drivers/media/video/mt9t112.c +++ b/drivers/media/video/mt9t112.c @@ -121,22 +121,22 @@ struct mt9t112_priv { static const struct mt9t112_format mt9t112_cfmts[] = { { - .code = V4L2_MBUS_FMT_YUYV8_2X8_BE, + .code = V4L2_MBUS_FMT_UYVY8_2X8, .colorspace = V4L2_COLORSPACE_JPEG, .fmt = 1, .order = 0, }, { - .code = V4L2_MBUS_FMT_YVYU8_2X8_BE, + .code = V4L2_MBUS_FMT_VYUY8_2X8, .colorspace = V4L2_COLORSPACE_JPEG, .fmt = 1, .order = 1, }, { - .code = V4L2_MBUS_FMT_YUYV8_2X8_LE, + .code = V4L2_MBUS_FMT_YUYV8_2X8, .colorspace = V4L2_COLORSPACE_JPEG, .fmt = 1, .order = 2, }, { - .code = V4L2_MBUS_FMT_YVYU8_2X8_LE, + .code = V4L2_MBUS_FMT_YVYU8_2X8, .colorspace = V4L2_COLORSPACE_JPEG, .fmt = 1, .order = 3, @@ -972,7 +972,7 @@ static int mt9t112_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) struct v4l2_rect *rect = &a->c; return mt9t112_set_params(client, rect->width, rect->height, - V4L2_MBUS_FMT_YUYV8_2X8_BE); + V4L2_MBUS_FMT_UYVY8_2X8); } static int mt9t112_g_fmt(struct v4l2_subdev *sd, @@ -983,7 +983,7 @@ static int mt9t112_g_fmt(struct v4l2_subdev *sd, if (!priv->format) { int ret = mt9t112_set_params(client, VGA_WIDTH, VGA_HEIGHT, - V4L2_MBUS_FMT_YUYV8_2X8_BE); + V4L2_MBUS_FMT_UYVY8_2X8); if (ret < 0) return ret; } diff --git a/drivers/media/video/omap/omap_vout.c b/drivers/media/video/omap/omap_vout.c index 929073e..4ed51b1 100644 --- a/drivers/media/video/omap/omap_vout.c +++ b/drivers/media/video/omap/omap_vout.c @@ -2545,19 +2545,11 @@ static int __init omap_vout_probe(struct platform_device *pdev) /* set the update mode */ if (def_display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { -#ifdef CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE - if (dssdrv->enable_te) - dssdrv->enable_te(def_display, 1); - if (dssdrv->set_update_mode) - dssdrv->set_update_mode(def_display, - OMAP_DSS_UPDATE_AUTO); -#else /* MANUAL_UPDATE */ if (dssdrv->enable_te) dssdrv->enable_te(def_display, 0); if (dssdrv->set_update_mode) dssdrv->set_update_mode(def_display, OMAP_DSS_UPDATE_MANUAL); -#endif } else { if (dssdrv->set_update_mode) dssdrv->set_update_mode(def_display, diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c index f85b2ed..926a5aa 100644 --- a/drivers/media/video/omap24xxcam.c +++ b/drivers/media/video/omap24xxcam.c @@ -426,7 +426,7 @@ static void omap24xxcam_vbq_release(struct videobuf_queue *vbq, dma->direction); dma->direction = DMA_NONE; } else { - videobuf_dma_unmap(vbq, videobuf_to_dma(vb)); + videobuf_dma_unmap(vbq->dev, videobuf_to_dma(vb)); videobuf_dma_free(videobuf_to_dma(vb)); } diff --git a/drivers/media/video/ov511.c b/drivers/media/video/ov511.c deleted file mode 100644 index a109120..0000000 --- a/drivers/media/video/ov511.c +++ /dev/null @@ -1,5995 +0,0 @@ -/* - * 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/module.h> -#include <linux/init.h> -#include <linux/vmalloc.h> -#include <linux/slab.h> -#include <linux/ctype.h> -#include <linux/pagemap.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; -/* Bitmask marking allocated devices from 0 to OV511_MAX_UNIT_VIDEO */ -static unsigned long ov511_devused; -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 unsigned 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_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 (device sends too much data)" }, - { -EPROTO, "Bit-stuff error (bad cable?)" }, - { -EILSEQ, "CRC/Timeout (bad cable?)" }, - { -ETIME, "Device does not respond to token" }, - { -ETIMEDOUT, "Device does not respond to command" }, - { -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); - dev_info(&ov->dev->dev, "Sensor[0x%02X] = 0x%02X\n", i, rc); - } -} - -static void -dump_i2c_regs(struct usb_ov511 *ov) -{ - dev_info(&ov->dev->dev, "I2C REGS\n"); - 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); - dev_info(&ov->dev->dev, "OV511[0x%02X] = 0x%02X\n", i, rc); - } -} - -static void -ov511_dump_regs(struct usb_ov511 *ov) -{ - dev_info(&ov->dev->dev, "CAMERA INTERFACE REGS\n"); - dump_reg_range(ov, 0x10, 0x1f); - dev_info(&ov->dev->dev, "DRAM INTERFACE REGS\n"); - dump_reg_range(ov, 0x20, 0x23); - dev_info(&ov->dev->dev, "ISO FIFO REGS\n"); - dump_reg_range(ov, 0x30, 0x31); - dev_info(&ov->dev->dev, "PIO REGS\n"); - dump_reg_range(ov, 0x38, 0x39); - dump_reg_range(ov, 0x3e, 0x3e); - dev_info(&ov->dev->dev, "I2C REGS\n"); - dump_reg_range(ov, 0x40, 0x49); - dev_info(&ov->dev->dev, "SYSTEM CONTROL REGS\n"); - dump_reg_range(ov, 0x50, 0x55); - dump_reg_range(ov, 0x5e, 0x5f); - dev_info(&ov->dev->dev, "OmniCE REGS\n"); - 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) -{ - dev_info(&ov->dev->dev, "VIDEO MODE REGS\n"); - dump_reg_range(ov, 0x20, 0x2f); - dev_info(&ov->dev->dev, "DATA PUMP AND SNAPSHOT REGS\n"); - dump_reg_range(ov, 0x30, 0x3f); - dev_info(&ov->dev->dev, "I2C REGS\n"); - dump_reg_range(ov, 0x40, 0x4f); - dev_info(&ov->dev->dev, "SYSTEM CONTROL AND VENDOR REGS\n"); - dump_reg_range(ov, 0x50, 0x5f); - dev_info(&ov->dev->dev, "60 - 6F\n"); - dump_reg_range(ov, 0x60, 0x6f); - dev_info(&ov->dev->dev, "70 - 7F\n"); - dump_reg_range(ov, 0x70, 0x7f); - dev_info(&ov->dev->dev, "Y QUANTIZATION TABLE\n"); - dump_reg_range(ov, 0x80, 0x8f); - dev_info(&ov->dev->dev, "UV QUANTIZATION TABLE\n"); - dump_reg_range(ov, 0x90, 0x9f); - dev_info(&ov->dev->dev, "A0 - BF\n"); - dump_reg_range(ov, 0xa0, 0xbf); - dev_info(&ov->dev->dev, "CBR\n"); - 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) { - dev_warn(&ov->dev->dev, - "snapshot reset not supported yet on OV518(+)\n"); - } else { - dev_err(&ov->dev->dev, "clear snap: invalid bridge type\n"); - } -} - -#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) { - dev_err(&ov->dev->dev, - "Error checking snspshot status (%d)\n", ret); - } else if (ret & 0x08) { - status = 1; - } - } else if (ov->bclass == BCL_OV518) { - dev_warn(&ov->dev->dev, - "snapshot check not supported yet on OV518(+)\n"); - } else { - dev_err(&ov->dev->dev, "clear snap: invalid bridge type\n"); - } - - 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) { - dev_info(&ov->dev->dev, - "ph(%3d): %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x\n", - 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) { - dev_info(&ov->dev->dev, - "ph: %2x %2x %2x %2x %2x %2x %2x %2x\n", - 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) -{ - 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, i, 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 { - dev_info(&ov->dev->dev, "Forcing packet size to %d\n", - packetsize); - ov518_set_packet_size(ov, packetsize); - } - } else { - if (packetsize == -1) { - ov511_set_packet_size(ov, size); - } else { - dev_info(&ov->dev->dev, "Forcing packet size to %d\n", - 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"); - for (i = 0; i < n; i++) - usb_free_urb(ov->sbuf[i].urb); - 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 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 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 long -ov51x_v4l1_ioctl_internal(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) { - dev_info(&ov->dev->dev, "Palette rejected (%s)\n", - 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 long -ov51x_v4l1_ioctl(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(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 const struct v4l2_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, -}; - -static struct video_device vdev_template = { - .name = "OV511 USB Camera", - .fops = &ov511_fops, - .release = video_device_release, -}; - -/**************************************************************************** - * - * 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) { - dev_info(&ov->dev->dev, "Sensor is an OV7610\n"); - 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) - dev_info(&ov->dev->dev, "Sensor is an OV7620AE\n"); - else - dev_info(&ov->dev->dev, "Sensor is an OV76BE\n"); - - /* 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) { - dev_info(&ov->dev->dev, - "Enabling 511+/7620AE workaround\n"); - ov->sensor = SEN_OV7620; - } else { - ov->sensor = SEN_OV76BE; - } - } else if ((rc & 3) == 0) { - dev_info(&ov->dev->dev, "Sensor is an OV7620\n"); - 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; - dev_info(&ov->dev->dev, "Sensor is an OV6630\n"); - } else if ((rc & 3) == 1) { - ov->sensor = SEN_OV6620; - dev_info(&ov->dev->dev, "Sensor is an OV6620\n"); - } else if ((rc & 3) == 2) { - ov->sensor = SEN_OV6630; - dev_info(&ov->dev->dev, "Sensor is an OV6630AE\n"); - } else if ((rc & 3) == 3) { - ov->sensor = SEN_OV6630; - dev_info(&ov->dev->dev, "Sensor is an OV6630AF\n"); - } - - /* 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) { - dev_info(&ov->dev->dev, "Sensor is a KS0127\n"); - ov->sensor = SEN_KS0127; - } else if ((rc & 0x0f) == 9) { - dev_info(&ov->dev->dev, "Sensor is a KS0127B Rev. A\n"); - 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 { - dev_info(&ov->dev->dev, - "Sensor is an SAA7111A (version 0x%x)\n", 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 - dev_warn(&ov->dev->dev, - "SAA7111A not yet supported with OV518/OV518+\n"); - - 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); - dev_info(&ov->dev->dev, "model: %s\n", 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 */ - dev_info(&ov->dev->dev, "Device revision %d\n", - 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; - dev_warn(&ov->dev->dev, - "Compression required with OV518...enabling\n"); - } - - 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 device *cd) -{ - struct video_device *vdev = to_video_device(cd); - return video_get_drvdata(vdev); -} - -static ssize_t show_custom_id(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct usb_ov511 *ov = cd_to_ov(cd); - return sprintf(buf, "%d\n", ov->customid); -} -static DEVICE_ATTR(custom_id, S_IRUGO, show_custom_id, NULL); - -static ssize_t show_model(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct usb_ov511 *ov = cd_to_ov(cd); - return sprintf(buf, "%s\n", ov->desc); -} -static DEVICE_ATTR(model, S_IRUGO, show_model, NULL); - -static ssize_t show_bridge(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct usb_ov511 *ov = cd_to_ov(cd); - return sprintf(buf, "%s\n", symbolic(brglist, ov->bridge)); -} -static DEVICE_ATTR(bridge, S_IRUGO, show_bridge, NULL); - -static ssize_t show_sensor(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct usb_ov511 *ov = cd_to_ov(cd); - return sprintf(buf, "%s\n", symbolic(senlist, ov->sensor)); -} -static DEVICE_ATTR(sensor, S_IRUGO, show_sensor, NULL); - -static ssize_t show_brightness(struct device *cd, - struct device_attribute *attr, 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 DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL); - -static ssize_t show_saturation(struct device *cd, - struct device_attribute *attr, 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 DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL); - -static ssize_t show_contrast(struct device *cd, - struct device_attribute *attr, 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 DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL); - -static ssize_t show_hue(struct device *cd, - struct device_attribute *attr, 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 DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL); - -static ssize_t show_exposure(struct device *cd, - struct device_attribute *attr, 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); -} -static DEVICE_ATTR(exposure, S_IRUGO, show_exposure, NULL); - -static int ov_create_sysfs(struct video_device *vdev) -{ - int rc; - - rc = device_create_file(&vdev->dev, &dev_attr_custom_id); - if (rc) goto err; - rc = device_create_file(&vdev->dev, &dev_attr_model); - if (rc) goto err_id; - rc = device_create_file(&vdev->dev, &dev_attr_bridge); - if (rc) goto err_model; - rc = device_create_file(&vdev->dev, &dev_attr_sensor); - if (rc) goto err_bridge; - rc = device_create_file(&vdev->dev, &dev_attr_brightness); - if (rc) goto err_sensor; - rc = device_create_file(&vdev->dev, &dev_attr_saturation); - if (rc) goto err_bright; - rc = device_create_file(&vdev->dev, &dev_attr_contrast); - if (rc) goto err_sat; - rc = device_create_file(&vdev->dev, &dev_attr_hue); - if (rc) goto err_contrast; - rc = device_create_file(&vdev->dev, &dev_attr_exposure); - if (rc) goto err_hue; - - return 0; - -err_hue: - device_remove_file(&vdev->dev, &dev_attr_hue); -err_contrast: - device_remove_file(&vdev->dev, &dev_attr_contrast); -err_sat: - device_remove_file(&vdev->dev, &dev_attr_saturation); -err_bright: - device_remove_file(&vdev->dev, &dev_attr_brightness); -err_sensor: - device_remove_file(&vdev->dev, &dev_attr_sensor); -err_bridge: - device_remove_file(&vdev->dev, &dev_attr_bridge); -err_model: - device_remove_file(&vdev->dev, &dev_attr_model); -err_id: - device_remove_file(&vdev->dev, &dev_attr_custom_id); -err: - return rc; -} - -/**************************************************************************** - * 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, rc, nr; - - 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; - } - - dev_info(&intf->dev, "USB %s video device found\n", - 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->parent = &intf->dev; - video_set_drvdata(ov->vdev, ov); - - mutex_lock(&ov->lock); - - /* Check to see next free device and mark as used */ - nr = find_first_zero_bit(&ov511_devused, OV511_MAX_UNIT_VIDEO); - - /* Registers device */ - if (unit_video[nr] != 0) - rc = video_register_device(ov->vdev, VFL_TYPE_GRABBER, - unit_video[nr]); - else - rc = video_register_device(ov->vdev, VFL_TYPE_GRABBER, -1); - - if (rc < 0) { - err("video_register_device failed"); - mutex_unlock(&ov->lock); - goto error; - } - - /* Mark device as used */ - ov511_devused |= 1 << nr; - ov->nr = nr; - - dev_info(&intf->dev, "Device at %s registered to %s\n", - ov->usb_path, video_device_node_name(ov->vdev)); - - usb_set_intfdata(intf, ov); - if (ov_create_sysfs(ov->vdev)) { - err("ov_create_sysfs failed"); - ov511_devused &= ~(1 << nr); - mutex_unlock(&ov->lock); - goto error; - } - - mutex_unlock(&ov->lock); - - return 0; - -error: - if (ov->vdev) { - if (!video_is_registered(ov->vdev)) - 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, ""); - - mutex_lock(&ov->lock); - usb_set_intfdata (intf, NULL); - - /* Free device number */ - ov511_devused &= ~(1 << ov->nr); - - 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); - mutex_unlock(&ov->lock); - - ov->dev = NULL; - - /* Free the memory */ - if (!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; - - printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" - DRIVER_DESC "\n"); - -out: - return retval; -} - -static void __exit -usb_ov511_exit(void) -{ - usb_deregister(&ov511_driver); - printk(KERN_INFO KBUILD_MODNAME ": driver deregistered\n"); -} - -module_init(usb_ov511_init); -module_exit(usb_ov511_exit); - diff --git a/drivers/media/video/ov511.h b/drivers/media/video/ov511.h deleted file mode 100644 index c450c92..0000000 --- a/drivers/media/video/ov511.h +++ /dev/null @@ -1,573 +0,0 @@ -#ifndef __LINUX_OV511_H -#define __LINUX_OV511_H - -#include <asm/uaccess.h> -#include <linux/videodev.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.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)) \ - printk(KERN_INFO KBUILD_MODNAME "[%s:%d] \n" fmt, \ - __func__, __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 */ - - /* ov511 device number ID */ - int nr; /* Stores a device number */ - - /* 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/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c index 34034a7..25eb5d6 100644 --- a/drivers/media/video/ov772x.c +++ b/drivers/media/video/ov772x.c @@ -440,21 +440,21 @@ static const struct regval_list ov772x_vga_regs[] = { */ static const struct ov772x_color_format ov772x_cfmts[] = { { - .code = V4L2_MBUS_FMT_YUYV8_2X8_LE, + .code = V4L2_MBUS_FMT_YUYV8_2X8, .colorspace = V4L2_COLORSPACE_JPEG, .dsp3 = 0x0, .com3 = SWAP_YUV, .com7 = OFMT_YUV, }, { - .code = V4L2_MBUS_FMT_YVYU8_2X8_LE, + .code = V4L2_MBUS_FMT_YVYU8_2X8, .colorspace = V4L2_COLORSPACE_JPEG, .dsp3 = UV_ON, .com3 = SWAP_YUV, .com7 = OFMT_YUV, }, { - .code = V4L2_MBUS_FMT_YUYV8_2X8_BE, + .code = V4L2_MBUS_FMT_UYVY8_2X8, .colorspace = V4L2_COLORSPACE_JPEG, .dsp3 = 0x0, .com3 = 0x0, @@ -960,7 +960,7 @@ static int ov772x_g_fmt(struct v4l2_subdev *sd, if (!priv->win || !priv->cfmt) { u32 width = VGA_WIDTH, height = VGA_HEIGHT; int ret = ov772x_set_params(client, &width, &height, - V4L2_MBUS_FMT_YUYV8_2X8_LE); + V4L2_MBUS_FMT_YUYV8_2X8); if (ret < 0) return ret; } diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c index 7ce9e05..40cdfab 100644 --- a/drivers/media/video/ov9640.c +++ b/drivers/media/video/ov9640.c @@ -155,7 +155,7 @@ static const struct ov9640_reg ov9640_regs_rgb[] = { }; static enum v4l2_mbus_pixelcode ov9640_codes[] = { - V4L2_MBUS_FMT_YUYV8_2X8_BE, + V4L2_MBUS_FMT_UYVY8_2X8, V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, V4L2_MBUS_FMT_RGB565_2X8_LE, }; @@ -430,7 +430,7 @@ static void ov9640_alter_regs(enum v4l2_mbus_pixelcode code, { switch (code) { default: - case V4L2_MBUS_FMT_YUYV8_2X8_BE: + case V4L2_MBUS_FMT_UYVY8_2X8: alt->com12 = OV9640_COM12_YUV_AVG; alt->com13 = OV9640_COM13_Y_DELAY_EN | OV9640_COM13_YUV_DLY(0x01); @@ -493,7 +493,7 @@ static int ov9640_write_regs(struct i2c_client *client, u32 width, } /* select color matrix configuration for given color encoding */ - if (code == V4L2_MBUS_FMT_YUYV8_2X8_BE) { + if (code == V4L2_MBUS_FMT_UYVY8_2X8) { matrix_regs = ov9640_regs_yuv; matrix_regs_len = ARRAY_SIZE(ov9640_regs_yuv); } else { @@ -579,8 +579,8 @@ static int ov9640_s_fmt(struct v4l2_subdev *sd, cspace = V4L2_COLORSPACE_SRGB; break; default: - code = V4L2_MBUS_FMT_YUYV8_2X8_BE; - case V4L2_MBUS_FMT_YUYV8_2X8_BE: + code = V4L2_MBUS_FMT_UYVY8_2X8; + case V4L2_MBUS_FMT_UYVY8_2X8: cspace = V4L2_COLORSPACE_JPEG; } @@ -606,8 +606,8 @@ static int ov9640_try_fmt(struct v4l2_subdev *sd, mf->colorspace = V4L2_COLORSPACE_SRGB; break; default: - mf->code = V4L2_MBUS_FMT_YUYV8_2X8_BE; - case V4L2_MBUS_FMT_YUYV8_2X8_BE: + mf->code = V4L2_MBUS_FMT_UYVY8_2X8; + case V4L2_MBUS_FMT_UYVY8_2X8: mf->colorspace = V4L2_COLORSPACE_JPEG; } diff --git a/drivers/media/video/ovcamchip/Makefile b/drivers/media/video/ovcamchip/Makefile deleted file mode 100644 index cba4cdf..0000000 --- a/drivers/media/video/ovcamchip/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -ovcamchip-objs := ovcamchip_core.o ov6x20.o ov6x30.o ov7x10.o ov7x20.o \ - ov76be.o - -obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip.o diff --git a/drivers/media/video/ovcamchip/ov6x20.c b/drivers/media/video/ovcamchip/ov6x20.c deleted file mode 100644 index c04130d..0000000 --- a/drivers/media/video/ovcamchip/ov6x20.c +++ /dev/null @@ -1,414 +0,0 @@ -/* OmniVision OV6620/OV6120 Camera Chip Support Code - * - * Copyright (c) 1999-2004 Mark McClelland <mark@alpha.dyndns.org> - * http://alpha.dyndns.org/ov511/ - * - * 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. NO WARRANTY OF ANY KIND is expressed or implied. - */ - -#define DEBUG - -#include <linux/slab.h> -#include "ovcamchip_priv.h" - -/* Registers */ -#define REG_GAIN 0x00 /* gain [5:0] */ -#define REG_BLUE 0x01 /* blue gain */ -#define REG_RED 0x02 /* red gain */ -#define REG_SAT 0x03 /* saturation */ -#define REG_CNT 0x05 /* Y contrast */ -#define REG_BRT 0x06 /* Y brightness */ -#define REG_WB_BLUE 0x0C /* WB blue ratio [5:0] */ -#define REG_WB_RED 0x0D /* WB red ratio [5:0] */ -#define REG_EXP 0x10 /* exposure */ - -/* Window parameters */ -#define HWSBASE 0x38 -#define HWEBASE 0x3A -#define VWSBASE 0x05 -#define VWEBASE 0x06 - -struct ov6x20 { - int auto_brt; - int auto_exp; - int backlight; - int bandfilt; - int mirror; -}; - -/* Initial values for use with OV511/OV511+ cameras */ -static struct ovcamchip_regvals regvals_init_6x20_511[] = { - { 0x12, 0x80 }, /* reset */ - { 0x11, 0x01 }, - { 0x03, 0x60 }, - { 0x05, 0x7f }, /* For when autoadjust is off */ - { 0x07, 0xa8 }, - { 0x0c, 0x24 }, - { 0x0d, 0x24 }, - { 0x0f, 0x15 }, /* COMS */ - { 0x10, 0x75 }, /* AEC Exposure time */ - { 0x12, 0x24 }, /* Enable AGC and AWB */ - { 0x14, 0x04 }, - { 0x16, 0x03 }, - { 0x26, 0xb2 }, /* BLC enable */ - /* 0x28: 0x05 Selects RGB format if RGB on */ - { 0x28, 0x05 }, - { 0x2a, 0x04 }, /* Disable framerate adjust */ - { 0x2d, 0x99 }, - { 0x33, 0xa0 }, /* Color Processing Parameter */ - { 0x34, 0xd2 }, /* Max A/D range */ - { 0x38, 0x8b }, - { 0x39, 0x40 }, - - { 0x3c, 0x39 }, /* Enable AEC mode changing */ - { 0x3c, 0x3c }, /* Change AEC mode */ - { 0x3c, 0x24 }, /* Disable AEC mode changing */ - - { 0x3d, 0x80 }, - /* These next two registers (0x4a, 0x4b) are undocumented. They - * control the color balance */ - { 0x4a, 0x80 }, - { 0x4b, 0x80 }, - { 0x4d, 0xd2 }, /* This reduces noise a bit */ - { 0x4e, 0xc1 }, - { 0x4f, 0x04 }, - { 0xff, 0xff }, /* END MARKER */ -}; - -/* Initial values for use with OV518 cameras */ -static struct ovcamchip_regvals regvals_init_6x20_518[] = { - { 0x12, 0x80 }, /* Do a reset */ - { 0x03, 0xc0 }, /* Saturation */ - { 0x05, 0x8a }, /* Contrast */ - { 0x0c, 0x24 }, /* AWB blue */ - { 0x0d, 0x24 }, /* AWB red */ - { 0x0e, 0x8d }, /* Additional 2x gain */ - { 0x0f, 0x25 }, /* Black expanding level = 1.3V */ - { 0x11, 0x01 }, /* Clock div. */ - { 0x12, 0x24 }, /* Enable AGC and AWB */ - { 0x13, 0x01 }, /* (default) */ - { 0x14, 0x80 }, /* Set reserved bit 7 */ - { 0x15, 0x01 }, /* (default) */ - { 0x16, 0x03 }, /* (default) */ - { 0x17, 0x38 }, /* (default) */ - { 0x18, 0xea }, /* (default) */ - { 0x19, 0x04 }, - { 0x1a, 0x93 }, - { 0x1b, 0x00 }, /* (default) */ - { 0x1e, 0xc4 }, /* (default) */ - { 0x1f, 0x04 }, /* (default) */ - { 0x20, 0x20 }, /* Enable 1st stage aperture correction */ - { 0x21, 0x10 }, /* Y offset */ - { 0x22, 0x88 }, /* U offset */ - { 0x23, 0xc0 }, /* Set XTAL power level */ - { 0x24, 0x53 }, /* AEC bright ratio */ - { 0x25, 0x7a }, /* AEC black ratio */ - { 0x26, 0xb2 }, /* BLC enable */ - { 0x27, 0xa2 }, /* Full output range */ - { 0x28, 0x01 }, /* (default) */ - { 0x29, 0x00 }, /* (default) */ - { 0x2a, 0x84 }, /* (default) */ - { 0x2b, 0xa8 }, /* Set custom frame rate */ - { 0x2c, 0xa0 }, /* (reserved) */ - { 0x2d, 0x95 }, /* Enable banding filter */ - { 0x2e, 0x88 }, /* V offset */ - { 0x33, 0x22 }, /* Luminance gamma on */ - { 0x34, 0xc7 }, /* A/D bias */ - { 0x36, 0x12 }, /* (reserved) */ - { 0x37, 0x63 }, /* (reserved) */ - { 0x38, 0x8b }, /* Quick AEC/AEB */ - { 0x39, 0x00 }, /* (default) */ - { 0x3a, 0x0f }, /* (default) */ - { 0x3b, 0x3c }, /* (default) */ - { 0x3c, 0x5c }, /* AEC controls */ - { 0x3d, 0x80 }, /* Drop 1 (bad) frame when AEC change */ - { 0x3e, 0x80 }, /* (default) */ - { 0x3f, 0x02 }, /* (default) */ - { 0x40, 0x10 }, /* (reserved) */ - { 0x41, 0x10 }, /* (reserved) */ - { 0x42, 0x00 }, /* (reserved) */ - { 0x43, 0x7f }, /* (reserved) */ - { 0x44, 0x80 }, /* (reserved) */ - { 0x45, 0x1c }, /* (reserved) */ - { 0x46, 0x1c }, /* (reserved) */ - { 0x47, 0x80 }, /* (reserved) */ - { 0x48, 0x5f }, /* (reserved) */ - { 0x49, 0x00 }, /* (reserved) */ - { 0x4a, 0x00 }, /* Color balance (undocumented) */ - { 0x4b, 0x80 }, /* Color balance (undocumented) */ - { 0x4c, 0x58 }, /* (reserved) */ - { 0x4d, 0xd2 }, /* U *= .938, V *= .838 */ - { 0x4e, 0xa0 }, /* (default) */ - { 0x4f, 0x04 }, /* UV 3-point average */ - { 0x50, 0xff }, /* (reserved) */ - { 0x51, 0x58 }, /* (reserved) */ - { 0x52, 0xc0 }, /* (reserved) */ - { 0x53, 0x42 }, /* (reserved) */ - { 0x27, 0xa6 }, /* Enable manual offset adj. (reg 21 & 22) */ - { 0x12, 0x20 }, - { 0x12, 0x24 }, - - { 0xff, 0xff }, /* END MARKER */ -}; - -/* This initializes the OV6x20 camera chip and relevant variables. */ -static int ov6x20_init(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov6x20 *s; - int rc; - - DDEBUG(4, &c->dev, "entered"); - - switch (c->adapter->id) { - case I2C_HW_SMBUS_OV511: - rc = ov_write_regvals(c, regvals_init_6x20_511); - break; - case I2C_HW_SMBUS_OV518: - rc = ov_write_regvals(c, regvals_init_6x20_518); - break; - default: - dev_err(&c->dev, "ov6x20: Unsupported adapter\n"); - rc = -ENODEV; - } - - if (rc < 0) - return rc; - - ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); - if (!s) - return -ENOMEM; - - s->auto_brt = 1; - s->auto_exp = 1; - - return rc; -} - -static int ov6x20_free(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - - kfree(ov->spriv); - return 0; -} - -static int ov6x20_set_control(struct i2c_client *c, - struct ovcamchip_control *ctl) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov6x20 *s = ov->spriv; - int rc; - int v = ctl->value; - - switch (ctl->id) { - case OVCAMCHIP_CID_CONT: - rc = ov_write(c, REG_CNT, v >> 8); - break; - case OVCAMCHIP_CID_BRIGHT: - rc = ov_write(c, REG_BRT, v >> 8); - break; - case OVCAMCHIP_CID_SAT: - rc = ov_write(c, REG_SAT, v >> 8); - break; - case OVCAMCHIP_CID_HUE: - rc = ov_write(c, REG_RED, 0xFF - (v >> 8)); - if (rc < 0) - goto out; - - rc = ov_write(c, REG_BLUE, v >> 8); - break; - case OVCAMCHIP_CID_EXP: - rc = ov_write(c, REG_EXP, v); - break; - case OVCAMCHIP_CID_FREQ: - { - int sixty = (v == 60); - - rc = ov_write(c, 0x2b, sixty?0xa8:0x28); - if (rc < 0) - goto out; - - rc = ov_write(c, 0x2a, sixty?0x84:0xa4); - break; - } - case OVCAMCHIP_CID_BANDFILT: - rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); - s->bandfilt = v; - break; - case OVCAMCHIP_CID_AUTOBRIGHT: - rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); - s->auto_brt = v; - break; - case OVCAMCHIP_CID_AUTOEXP: - rc = ov_write_mask(c, 0x13, v?0x01:0x00, 0x01); - s->auto_exp = v; - break; - case OVCAMCHIP_CID_BACKLIGHT: - { - rc = ov_write_mask(c, 0x4e, v?0xe0:0xc0, 0xe0); - if (rc < 0) - goto out; - - rc = ov_write_mask(c, 0x29, v?0x08:0x00, 0x08); - if (rc < 0) - goto out; - - rc = ov_write_mask(c, 0x0e, v?0x80:0x00, 0x80); - s->backlight = v; - break; - } - case OVCAMCHIP_CID_MIRROR: - rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); - s->mirror = v; - break; - default: - DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); - return -EPERM; - } - -out: - DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); - return rc; -} - -static int ov6x20_get_control(struct i2c_client *c, - struct ovcamchip_control *ctl) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov6x20 *s = ov->spriv; - int rc = 0; - unsigned char val = 0; - - switch (ctl->id) { - case OVCAMCHIP_CID_CONT: - rc = ov_read(c, REG_CNT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_BRIGHT: - rc = ov_read(c, REG_BRT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_SAT: - rc = ov_read(c, REG_SAT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_HUE: - rc = ov_read(c, REG_BLUE, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_EXP: - rc = ov_read(c, REG_EXP, &val); - ctl->value = val; - break; - case OVCAMCHIP_CID_BANDFILT: - ctl->value = s->bandfilt; - break; - case OVCAMCHIP_CID_AUTOBRIGHT: - ctl->value = s->auto_brt; - break; - case OVCAMCHIP_CID_AUTOEXP: - ctl->value = s->auto_exp; - break; - case OVCAMCHIP_CID_BACKLIGHT: - ctl->value = s->backlight; - break; - case OVCAMCHIP_CID_MIRROR: - ctl->value = s->mirror; - break; - default: - DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); - return -EPERM; - } - - DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); - return rc; -} - -static int ov6x20_mode_init(struct i2c_client *c, struct ovcamchip_window *win) -{ - /******** QCIF-specific regs ********/ - - ov_write(c, 0x14, win->quarter?0x24:0x04); - - /******** Palette-specific regs ********/ - - /* OV518 needs 8 bit multiplexed in color mode, and 16 bit in B&W */ - if (c->adapter->id == I2C_HW_SMBUS_OV518) { - if (win->format == VIDEO_PALETTE_GREY) - ov_write_mask(c, 0x13, 0x00, 0x20); - else - ov_write_mask(c, 0x13, 0x20, 0x20); - } else { - if (win->format == VIDEO_PALETTE_GREY) - ov_write_mask(c, 0x13, 0x20, 0x20); - else - ov_write_mask(c, 0x13, 0x00, 0x20); - } - - /******** Clock programming ********/ - - /* The OV6620 needs special handling. This prevents the - * severe banding that normally occurs */ - - /* Clock down */ - ov_write(c, 0x2a, 0x04); - - ov_write(c, 0x11, win->clockdiv); - - ov_write(c, 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. */ - ov_write(c, 0x2d, 0x85); /* FIXME: This messes up banding filter */ - - return 0; -} - -static int ov6x20_set_window(struct i2c_client *c, struct ovcamchip_window *win) -{ - int ret, hwscale, vwscale; - - ret = ov6x20_mode_init(c, win); - if (ret < 0) - return ret; - - if (win->quarter) { - hwscale = 0; - vwscale = 0; - } else { - hwscale = 1; - vwscale = 1; /* The datasheet says 0; it's wrong */ - } - - ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); - ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); - ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); - ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); - - return 0; -} - -static int ov6x20_command(struct i2c_client *c, unsigned int cmd, void *arg) -{ - switch (cmd) { - case OVCAMCHIP_CMD_S_CTRL: - return ov6x20_set_control(c, arg); - case OVCAMCHIP_CMD_G_CTRL: - return ov6x20_get_control(c, arg); - case OVCAMCHIP_CMD_S_MODE: - return ov6x20_set_window(c, arg); - default: - DDEBUG(2, &c->dev, "command not supported: %d", cmd); - return -ENOIOCTLCMD; - } -} - -struct ovcamchip_ops ov6x20_ops = { - .init = ov6x20_init, - .free = ov6x20_free, - .command = ov6x20_command, -}; diff --git a/drivers/media/video/ovcamchip/ov6x30.c b/drivers/media/video/ovcamchip/ov6x30.c deleted file mode 100644 index 73b94f5..0000000 --- a/drivers/media/video/ovcamchip/ov6x30.c +++ /dev/null @@ -1,373 +0,0 @@ -/* OmniVision OV6630/OV6130 Camera Chip Support Code - * - * Copyright (c) 1999-2004 Mark McClelland <mark@alpha.dyndns.org> - * http://alpha.dyndns.org/ov511/ - * - * 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. NO WARRANTY OF ANY KIND is expressed or implied. - */ - -#define DEBUG - -#include <linux/slab.h> -#include "ovcamchip_priv.h" - -/* Registers */ -#define REG_GAIN 0x00 /* gain [5:0] */ -#define REG_BLUE 0x01 /* blue gain */ -#define REG_RED 0x02 /* red gain */ -#define REG_SAT 0x03 /* saturation [7:3] */ -#define REG_CNT 0x05 /* Y contrast [3:0] */ -#define REG_BRT 0x06 /* Y brightness */ -#define REG_SHARP 0x07 /* sharpness */ -#define REG_WB_BLUE 0x0C /* WB blue ratio [5:0] */ -#define REG_WB_RED 0x0D /* WB red ratio [5:0] */ -#define REG_EXP 0x10 /* exposure */ - -/* Window parameters */ -#define HWSBASE 0x38 -#define HWEBASE 0x3A -#define VWSBASE 0x05 -#define VWEBASE 0x06 - -struct ov6x30 { - int auto_brt; - int auto_exp; - int backlight; - int bandfilt; - int mirror; -}; - -static struct ovcamchip_regvals regvals_init_6x30[] = { - { 0x12, 0x80 }, /* reset */ - { 0x00, 0x1f }, /* Gain */ - { 0x01, 0x99 }, /* Blue gain */ - { 0x02, 0x7c }, /* Red gain */ - { 0x03, 0xc0 }, /* Saturation */ - { 0x05, 0x0a }, /* Contrast */ - { 0x06, 0x95 }, /* Brightness */ - { 0x07, 0x2d }, /* Sharpness */ - { 0x0c, 0x20 }, - { 0x0d, 0x20 }, - { 0x0e, 0x20 }, - { 0x0f, 0x05 }, - { 0x10, 0x9a }, /* "exposure check" */ - { 0x11, 0x00 }, /* Pixel clock = fastest */ - { 0x12, 0x24 }, /* Enable AGC and AWB */ - { 0x13, 0x21 }, - { 0x14, 0x80 }, - { 0x15, 0x01 }, - { 0x16, 0x03 }, - { 0x17, 0x38 }, - { 0x18, 0xea }, - { 0x19, 0x04 }, - { 0x1a, 0x93 }, - { 0x1b, 0x00 }, - { 0x1e, 0xc4 }, - { 0x1f, 0x04 }, - { 0x20, 0x20 }, - { 0x21, 0x10 }, - { 0x22, 0x88 }, - { 0x23, 0xc0 }, /* Crystal circuit power level */ - { 0x25, 0x9a }, /* Increase AEC black pixel ratio */ - { 0x26, 0xb2 }, /* BLC enable */ - { 0x27, 0xa2 }, - { 0x28, 0x00 }, - { 0x29, 0x00 }, - { 0x2a, 0x84 }, /* (keep) */ - { 0x2b, 0xa8 }, /* (keep) */ - { 0x2c, 0xa0 }, - { 0x2d, 0x95 }, /* Enable auto-brightness */ - { 0x2e, 0x88 }, - { 0x33, 0x26 }, - { 0x34, 0x03 }, - { 0x36, 0x8f }, - { 0x37, 0x80 }, - { 0x38, 0x83 }, - { 0x39, 0x80 }, - { 0x3a, 0x0f }, - { 0x3b, 0x3c }, - { 0x3c, 0x1a }, - { 0x3d, 0x80 }, - { 0x3e, 0x80 }, - { 0x3f, 0x0e }, - { 0x40, 0x00 }, /* White bal */ - { 0x41, 0x00 }, /* White bal */ - { 0x42, 0x80 }, - { 0x43, 0x3f }, /* White bal */ - { 0x44, 0x80 }, - { 0x45, 0x20 }, - { 0x46, 0x20 }, - { 0x47, 0x80 }, - { 0x48, 0x7f }, - { 0x49, 0x00 }, - { 0x4a, 0x00 }, - { 0x4b, 0x80 }, - { 0x4c, 0xd0 }, - { 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */ - { 0x4e, 0x40 }, - { 0x4f, 0x07 }, /* UV average mode, color killer: strongest */ - { 0x50, 0xff }, - { 0x54, 0x23 }, /* Max AGC gain: 18dB */ - { 0x55, 0xff }, - { 0x56, 0x12 }, - { 0x57, 0x81 }, /* (default) */ - { 0x58, 0x75 }, - { 0x59, 0x01 }, /* AGC dark current compensation: +1 */ - { 0x5a, 0x2c }, - { 0x5b, 0x0f }, /* AWB chrominance levels */ - { 0x5c, 0x10 }, - { 0x3d, 0x80 }, - { 0x27, 0xa6 }, - /* Toggle AWB off and on */ - { 0x12, 0x20 }, - { 0x12, 0x24 }, - - { 0xff, 0xff }, /* END MARKER */ -}; - -/* This initializes the OV6x30 camera chip and relevant variables. */ -static int ov6x30_init(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov6x30 *s; - int rc; - - DDEBUG(4, &c->dev, "entered"); - - rc = ov_write_regvals(c, regvals_init_6x30); - if (rc < 0) - return rc; - - ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); - if (!s) - return -ENOMEM; - - s->auto_brt = 1; - s->auto_exp = 1; - - return rc; -} - -static int ov6x30_free(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - - kfree(ov->spriv); - return 0; -} - -static int ov6x30_set_control(struct i2c_client *c, - struct ovcamchip_control *ctl) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov6x30 *s = ov->spriv; - int rc; - int v = ctl->value; - - switch (ctl->id) { - case OVCAMCHIP_CID_CONT: - rc = ov_write_mask(c, REG_CNT, v >> 12, 0x0f); - break; - case OVCAMCHIP_CID_BRIGHT: - rc = ov_write(c, REG_BRT, v >> 8); - break; - case OVCAMCHIP_CID_SAT: - rc = ov_write(c, REG_SAT, v >> 8); - break; - case OVCAMCHIP_CID_HUE: - rc = ov_write(c, REG_RED, 0xFF - (v >> 8)); - if (rc < 0) - goto out; - - rc = ov_write(c, REG_BLUE, v >> 8); - break; - case OVCAMCHIP_CID_EXP: - rc = ov_write(c, REG_EXP, v); - break; - case OVCAMCHIP_CID_FREQ: - { - int sixty = (v == 60); - - rc = ov_write(c, 0x2b, sixty?0xa8:0x28); - if (rc < 0) - goto out; - - rc = ov_write(c, 0x2a, sixty?0x84:0xa4); - break; - } - case OVCAMCHIP_CID_BANDFILT: - rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); - s->bandfilt = v; - break; - case OVCAMCHIP_CID_AUTOBRIGHT: - rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); - s->auto_brt = v; - break; - case OVCAMCHIP_CID_AUTOEXP: - rc = ov_write_mask(c, 0x28, v?0x00:0x10, 0x10); - s->auto_exp = v; - break; - case OVCAMCHIP_CID_BACKLIGHT: - { - rc = ov_write_mask(c, 0x4e, v?0x80:0x60, 0xe0); - if (rc < 0) - goto out; - - rc = ov_write_mask(c, 0x29, v?0x08:0x00, 0x08); - if (rc < 0) - goto out; - - rc = ov_write_mask(c, 0x28, v?0x02:0x00, 0x02); - s->backlight = v; - break; - } - case OVCAMCHIP_CID_MIRROR: - rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); - s->mirror = v; - break; - default: - DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); - return -EPERM; - } - -out: - DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); - return rc; -} - -static int ov6x30_get_control(struct i2c_client *c, - struct ovcamchip_control *ctl) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov6x30 *s = ov->spriv; - int rc = 0; - unsigned char val = 0; - - switch (ctl->id) { - case OVCAMCHIP_CID_CONT: - rc = ov_read(c, REG_CNT, &val); - ctl->value = (val & 0x0f) << 12; - break; - case OVCAMCHIP_CID_BRIGHT: - rc = ov_read(c, REG_BRT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_SAT: - rc = ov_read(c, REG_SAT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_HUE: - rc = ov_read(c, REG_BLUE, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_EXP: - rc = ov_read(c, REG_EXP, &val); - ctl->value = val; - break; - case OVCAMCHIP_CID_BANDFILT: - ctl->value = s->bandfilt; - break; - case OVCAMCHIP_CID_AUTOBRIGHT: - ctl->value = s->auto_brt; - break; - case OVCAMCHIP_CID_AUTOEXP: - ctl->value = s->auto_exp; - break; - case OVCAMCHIP_CID_BACKLIGHT: - ctl->value = s->backlight; - break; - case OVCAMCHIP_CID_MIRROR: - ctl->value = s->mirror; - break; - default: - DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); - return -EPERM; - } - - DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); - return rc; -} - -static int ov6x30_mode_init(struct i2c_client *c, struct ovcamchip_window *win) -{ - /******** QCIF-specific regs ********/ - - ov_write_mask(c, 0x14, win->quarter?0x20:0x00, 0x20); - - /******** Palette-specific regs ********/ - - if (win->format == VIDEO_PALETTE_GREY) { - if (c->adapter->id == I2C_HW_SMBUS_OV518) { - /* Do nothing - we're already in 8-bit mode */ - } else { - ov_write_mask(c, 0x13, 0x20, 0x20); - } - } else { - /* 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 (c->adapter->id == I2C_HW_SMBUS_OV518) { - /* Do nothing - we want to stay in 8-bit mode */ - /* Warning: Messing with reg 0x13 breaks OV518 color */ - } else { - ov_write_mask(c, 0x13, 0x00, 0x20); - } - } - - /******** Clock programming ********/ - - ov_write(c, 0x11, win->clockdiv); - - return 0; -} - -static int ov6x30_set_window(struct i2c_client *c, struct ovcamchip_window *win) -{ - int ret, hwscale, vwscale; - - ret = ov6x30_mode_init(c, win); - if (ret < 0) - return ret; - - if (win->quarter) { - hwscale = 0; - vwscale = 0; - } else { - hwscale = 1; - vwscale = 1; /* The datasheet says 0; it's wrong */ - } - - ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); - ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); - ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); - ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); - - return 0; -} - -static int ov6x30_command(struct i2c_client *c, unsigned int cmd, void *arg) -{ - switch (cmd) { - case OVCAMCHIP_CMD_S_CTRL: - return ov6x30_set_control(c, arg); - case OVCAMCHIP_CMD_G_CTRL: - return ov6x30_get_control(c, arg); - case OVCAMCHIP_CMD_S_MODE: - return ov6x30_set_window(c, arg); - default: - DDEBUG(2, &c->dev, "command not supported: %d", cmd); - return -ENOIOCTLCMD; - } -} - -struct ovcamchip_ops ov6x30_ops = { - .init = ov6x30_init, - .free = ov6x30_free, - .command = ov6x30_command, -}; diff --git a/drivers/media/video/ovcamchip/ov76be.c b/drivers/media/video/ovcamchip/ov76be.c deleted file mode 100644 index 11f6be9..0000000 --- a/drivers/media/video/ovcamchip/ov76be.c +++ /dev/null @@ -1,302 +0,0 @@ -/* OmniVision OV76BE Camera Chip Support Code - * - * Copyright (c) 1999-2004 Mark McClelland <mark@alpha.dyndns.org> - * http://alpha.dyndns.org/ov511/ - * - * 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. NO WARRANTY OF ANY KIND is expressed or implied. - */ - -#define DEBUG - -#include <linux/slab.h> -#include "ovcamchip_priv.h" - -/* OV7610 registers: Since the OV76BE is undocumented, we'll settle for these - * for now. */ -#define REG_GAIN 0x00 /* gain [5:0] */ -#define REG_BLUE 0x01 /* blue channel balance */ -#define REG_RED 0x02 /* red channel balance */ -#define REG_SAT 0x03 /* saturation */ -#define REG_CNT 0x05 /* Y contrast */ -#define REG_BRT 0x06 /* Y brightness */ -#define REG_BLUE_BIAS 0x0C /* blue channel bias [5:0] */ -#define REG_RED_BIAS 0x0D /* red channel bias [5:0] */ -#define REG_GAMMA_COEFF 0x0E /* gamma settings */ -#define REG_WB_RANGE 0x0F /* AEC/ALC/S-AWB settings */ -#define REG_EXP 0x10 /* manual exposure setting */ -#define REG_CLOCK 0x11 /* polarity/clock prescaler */ -#define REG_FIELD_DIVIDE 0x16 /* field interval/mode settings */ -#define REG_HWIN_START 0x17 /* horizontal window start */ -#define REG_HWIN_END 0x18 /* horizontal window end */ -#define REG_VWIN_START 0x19 /* vertical window start */ -#define REG_VWIN_END 0x1A /* vertical window end */ -#define REG_PIXEL_SHIFT 0x1B /* pixel shift */ -#define REG_YOFFSET 0x21 /* Y channel offset */ -#define REG_UOFFSET 0x22 /* U channel offset */ -#define REG_ECW 0x24 /* exposure white level for AEC */ -#define REG_ECB 0x25 /* exposure black level for AEC */ -#define REG_FRAMERATE_H 0x2A /* frame rate MSB + misc */ -#define REG_FRAMERATE_L 0x2B /* frame rate LSB */ -#define REG_ALC 0x2C /* Auto Level Control settings */ -#define REG_VOFFSET 0x2E /* V channel offset adjustment */ -#define REG_ARRAY_BIAS 0x2F /* array bias -- don't change */ -#define REG_YGAMMA 0x33 /* misc gamma settings [7:6] */ -#define REG_BIAS_ADJUST 0x34 /* misc bias settings */ - -/* Window parameters */ -#define HWSBASE 0x38 -#define HWEBASE 0x3a -#define VWSBASE 0x05 -#define VWEBASE 0x05 - -struct ov76be { - int auto_brt; - int auto_exp; - int bandfilt; - int mirror; -}; - -/* NOTE: These are the same as the 7x10 settings, but should eventually be - * optimized for the OV76BE */ -static struct ovcamchip_regvals regvals_init_76be[] = { - { 0x10, 0xff }, - { 0x16, 0x03 }, - { 0x28, 0x24 }, - { 0x2b, 0xac }, - { 0x12, 0x00 }, - { 0x38, 0x81 }, - { 0x28, 0x24 }, /* 0c */ - { 0x0f, 0x85 }, /* lg's setting */ - { 0x15, 0x01 }, - { 0x20, 0x1c }, - { 0x23, 0x2a }, - { 0x24, 0x10 }, - { 0x25, 0x8a }, - { 0x26, 0xa2 }, - { 0x27, 0xc2 }, - { 0x2a, 0x04 }, - { 0x2c, 0xfe }, - { 0x2d, 0x93 }, - { 0x30, 0x71 }, - { 0x31, 0x60 }, - { 0x32, 0x26 }, - { 0x33, 0x20 }, - { 0x34, 0x48 }, - { 0x12, 0x24 }, - { 0x11, 0x01 }, - { 0x0c, 0x24 }, - { 0x0d, 0x24 }, - { 0xff, 0xff }, /* END MARKER */ -}; - -/* This initializes the OV76be camera chip and relevant variables. */ -static int ov76be_init(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov76be *s; - int rc; - - DDEBUG(4, &c->dev, "entered"); - - rc = ov_write_regvals(c, regvals_init_76be); - if (rc < 0) - return rc; - - ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); - if (!s) - return -ENOMEM; - - s->auto_brt = 1; - s->auto_exp = 1; - - return rc; -} - -static int ov76be_free(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - - kfree(ov->spriv); - return 0; -} - -static int ov76be_set_control(struct i2c_client *c, - struct ovcamchip_control *ctl) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov76be *s = ov->spriv; - int rc; - int v = ctl->value; - - switch (ctl->id) { - case OVCAMCHIP_CID_BRIGHT: - rc = ov_write(c, REG_BRT, v >> 8); - break; - case OVCAMCHIP_CID_SAT: - rc = ov_write(c, REG_SAT, v >> 8); - break; - case OVCAMCHIP_CID_EXP: - rc = ov_write(c, REG_EXP, v); - break; - case OVCAMCHIP_CID_FREQ: - { - int sixty = (v == 60); - - rc = ov_write_mask(c, 0x2a, sixty?0x00:0x80, 0x80); - if (rc < 0) - goto out; - - rc = ov_write(c, 0x2b, sixty?0x00:0xac); - if (rc < 0) - goto out; - - rc = ov_write_mask(c, 0x76, 0x01, 0x01); - break; - } - case OVCAMCHIP_CID_BANDFILT: - rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); - s->bandfilt = v; - break; - case OVCAMCHIP_CID_AUTOBRIGHT: - rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); - s->auto_brt = v; - break; - case OVCAMCHIP_CID_AUTOEXP: - rc = ov_write_mask(c, 0x13, v?0x01:0x00, 0x01); - s->auto_exp = v; - break; - case OVCAMCHIP_CID_MIRROR: - rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); - s->mirror = v; - break; - default: - DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); - return -EPERM; - } - -out: - DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); - return rc; -} - -static int ov76be_get_control(struct i2c_client *c, - struct ovcamchip_control *ctl) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov76be *s = ov->spriv; - int rc = 0; - unsigned char val = 0; - - switch (ctl->id) { - case OVCAMCHIP_CID_BRIGHT: - rc = ov_read(c, REG_BRT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_SAT: - rc = ov_read(c, REG_SAT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_EXP: - rc = ov_read(c, REG_EXP, &val); - ctl->value = val; - break; - case OVCAMCHIP_CID_BANDFILT: - ctl->value = s->bandfilt; - break; - case OVCAMCHIP_CID_AUTOBRIGHT: - ctl->value = s->auto_brt; - break; - case OVCAMCHIP_CID_AUTOEXP: - ctl->value = s->auto_exp; - break; - case OVCAMCHIP_CID_MIRROR: - ctl->value = s->mirror; - break; - default: - DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); - return -EPERM; - } - - DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); - return rc; -} - -static int ov76be_mode_init(struct i2c_client *c, struct ovcamchip_window *win) -{ - int qvga = win->quarter; - - /******** QVGA-specific regs ********/ - - ov_write(c, 0x14, qvga?0xa4:0x84); - - /******** Palette-specific regs ********/ - - if (win->format == VIDEO_PALETTE_GREY) { - ov_write_mask(c, 0x0e, 0x40, 0x40); - ov_write_mask(c, 0x13, 0x20, 0x20); - } else { - ov_write_mask(c, 0x0e, 0x00, 0x40); - ov_write_mask(c, 0x13, 0x00, 0x20); - } - - /******** Clock programming ********/ - - ov_write(c, 0x11, win->clockdiv); - - /******** Resolution-specific ********/ - - if (win->width == 640 && win->height == 480) - ov_write(c, 0x35, 0x9e); - else - ov_write(c, 0x35, 0x1e); - - return 0; -} - -static int ov76be_set_window(struct i2c_client *c, struct ovcamchip_window *win) -{ - int ret, hwscale, vwscale; - - ret = ov76be_mode_init(c, win); - if (ret < 0) - return ret; - - if (win->quarter) { - hwscale = 1; - vwscale = 0; - } else { - hwscale = 2; - vwscale = 1; - } - - ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); - ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); - ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); - ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); - - return 0; -} - -static int ov76be_command(struct i2c_client *c, unsigned int cmd, void *arg) -{ - switch (cmd) { - case OVCAMCHIP_CMD_S_CTRL: - return ov76be_set_control(c, arg); - case OVCAMCHIP_CMD_G_CTRL: - return ov76be_get_control(c, arg); - case OVCAMCHIP_CMD_S_MODE: - return ov76be_set_window(c, arg); - default: - DDEBUG(2, &c->dev, "command not supported: %d", cmd); - return -ENOIOCTLCMD; - } -} - -struct ovcamchip_ops ov76be_ops = { - .init = ov76be_init, - .free = ov76be_free, - .command = ov76be_command, -}; diff --git a/drivers/media/video/ovcamchip/ov7x10.c b/drivers/media/video/ovcamchip/ov7x10.c deleted file mode 100644 index 5206e79..0000000 --- a/drivers/media/video/ovcamchip/ov7x10.c +++ /dev/null @@ -1,334 +0,0 @@ -/* OmniVision OV7610/OV7110 Camera Chip Support Code - * - * Copyright (c) 1999-2004 Mark McClelland <mark@alpha.dyndns.org> - * http://alpha.dyndns.org/ov511/ - * - * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000) - * - * 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. NO WARRANTY OF ANY KIND is expressed or implied. - */ - -#define DEBUG - -#include <linux/slab.h> -#include "ovcamchip_priv.h" - -/* Registers */ -#define REG_GAIN 0x00 /* gain [5:0] */ -#define REG_BLUE 0x01 /* blue channel balance */ -#define REG_RED 0x02 /* red channel balance */ -#define REG_SAT 0x03 /* saturation */ -#define REG_CNT 0x05 /* Y contrast */ -#define REG_BRT 0x06 /* Y brightness */ -#define REG_BLUE_BIAS 0x0C /* blue channel bias [5:0] */ -#define REG_RED_BIAS 0x0D /* red channel bias [5:0] */ -#define REG_GAMMA_COEFF 0x0E /* gamma settings */ -#define REG_WB_RANGE 0x0F /* AEC/ALC/S-AWB settings */ -#define REG_EXP 0x10 /* manual exposure setting */ -#define REG_CLOCK 0x11 /* polarity/clock prescaler */ -#define REG_FIELD_DIVIDE 0x16 /* field interval/mode settings */ -#define REG_HWIN_START 0x17 /* horizontal window start */ -#define REG_HWIN_END 0x18 /* horizontal window end */ -#define REG_VWIN_START 0x19 /* vertical window start */ -#define REG_VWIN_END 0x1A /* vertical window end */ -#define REG_PIXEL_SHIFT 0x1B /* pixel shift */ -#define REG_YOFFSET 0x21 /* Y channel offset */ -#define REG_UOFFSET 0x22 /* U channel offset */ -#define REG_ECW 0x24 /* exposure white level for AEC */ -#define REG_ECB 0x25 /* exposure black level for AEC */ -#define REG_FRAMERATE_H 0x2A /* frame rate MSB + misc */ -#define REG_FRAMERATE_L 0x2B /* frame rate LSB */ -#define REG_ALC 0x2C /* Auto Level Control settings */ -#define REG_VOFFSET 0x2E /* V channel offset adjustment */ -#define REG_ARRAY_BIAS 0x2F /* array bias -- don't change */ -#define REG_YGAMMA 0x33 /* misc gamma settings [7:6] */ -#define REG_BIAS_ADJUST 0x34 /* misc bias settings */ - -/* Window parameters */ -#define HWSBASE 0x38 -#define HWEBASE 0x3a -#define VWSBASE 0x05 -#define VWEBASE 0x05 - -struct ov7x10 { - int auto_brt; - int auto_exp; - int bandfilt; - int mirror; -}; - -/* 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 ovcamchip_regvals regvals_init_7x10[] = { - { 0x10, 0xff }, - { 0x16, 0x03 }, - { 0x28, 0x24 }, - { 0x2b, 0xac }, - { 0x12, 0x00 }, - { 0x38, 0x81 }, - { 0x28, 0x24 }, /* 0c */ - { 0x0f, 0x85 }, /* lg's setting */ - { 0x15, 0x01 }, - { 0x20, 0x1c }, - { 0x23, 0x2a }, - { 0x24, 0x10 }, - { 0x25, 0x8a }, - { 0x26, 0xa2 }, - { 0x27, 0xc2 }, - { 0x2a, 0x04 }, - { 0x2c, 0xfe }, - { 0x2d, 0x93 }, - { 0x30, 0x71 }, - { 0x31, 0x60 }, - { 0x32, 0x26 }, - { 0x33, 0x20 }, - { 0x34, 0x48 }, - { 0x12, 0x24 }, - { 0x11, 0x01 }, - { 0x0c, 0x24 }, - { 0x0d, 0x24 }, - { 0xff, 0xff }, /* END MARKER */ -}; - -/* This initializes the OV7x10 camera chip and relevant variables. */ -static int ov7x10_init(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov7x10 *s; - int rc; - - DDEBUG(4, &c->dev, "entered"); - - rc = ov_write_regvals(c, regvals_init_7x10); - if (rc < 0) - return rc; - - ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); - if (!s) - return -ENOMEM; - - s->auto_brt = 1; - s->auto_exp = 1; - - return rc; -} - -static int ov7x10_free(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - - kfree(ov->spriv); - return 0; -} - -static int ov7x10_set_control(struct i2c_client *c, - struct ovcamchip_control *ctl) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov7x10 *s = ov->spriv; - int rc; - int v = ctl->value; - - switch (ctl->id) { - case OVCAMCHIP_CID_CONT: - rc = ov_write(c, REG_CNT, v >> 8); - break; - case OVCAMCHIP_CID_BRIGHT: - rc = ov_write(c, REG_BRT, v >> 8); - break; - case OVCAMCHIP_CID_SAT: - rc = ov_write(c, REG_SAT, v >> 8); - break; - case OVCAMCHIP_CID_HUE: - rc = ov_write(c, REG_RED, 0xFF - (v >> 8)); - if (rc < 0) - goto out; - - rc = ov_write(c, REG_BLUE, v >> 8); - break; - case OVCAMCHIP_CID_EXP: - rc = ov_write(c, REG_EXP, v); - break; - case OVCAMCHIP_CID_FREQ: - { - int sixty = (v == 60); - - rc = ov_write_mask(c, 0x2a, sixty?0x00:0x80, 0x80); - if (rc < 0) - goto out; - - rc = ov_write(c, 0x2b, sixty?0x00:0xac); - if (rc < 0) - goto out; - - rc = ov_write_mask(c, 0x13, 0x10, 0x10); - if (rc < 0) - goto out; - - rc = ov_write_mask(c, 0x13, 0x00, 0x10); - break; - } - case OVCAMCHIP_CID_BANDFILT: - rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); - s->bandfilt = v; - break; - case OVCAMCHIP_CID_AUTOBRIGHT: - rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); - s->auto_brt = v; - break; - case OVCAMCHIP_CID_AUTOEXP: - rc = ov_write_mask(c, 0x29, v?0x00:0x80, 0x80); - s->auto_exp = v; - break; - case OVCAMCHIP_CID_MIRROR: - rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); - s->mirror = v; - break; - default: - DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); - return -EPERM; - } - -out: - DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); - return rc; -} - -static int ov7x10_get_control(struct i2c_client *c, - struct ovcamchip_control *ctl) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov7x10 *s = ov->spriv; - int rc = 0; - unsigned char val = 0; - - switch (ctl->id) { - case OVCAMCHIP_CID_CONT: - rc = ov_read(c, REG_CNT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_BRIGHT: - rc = ov_read(c, REG_BRT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_SAT: - rc = ov_read(c, REG_SAT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_HUE: - rc = ov_read(c, REG_BLUE, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_EXP: - rc = ov_read(c, REG_EXP, &val); - ctl->value = val; - break; - case OVCAMCHIP_CID_BANDFILT: - ctl->value = s->bandfilt; - break; - case OVCAMCHIP_CID_AUTOBRIGHT: - ctl->value = s->auto_brt; - break; - case OVCAMCHIP_CID_AUTOEXP: - ctl->value = s->auto_exp; - break; - case OVCAMCHIP_CID_MIRROR: - ctl->value = s->mirror; - break; - default: - DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); - return -EPERM; - } - - DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); - return rc; -} - -static int ov7x10_mode_init(struct i2c_client *c, struct ovcamchip_window *win) -{ - int qvga = win->quarter; - - /******** QVGA-specific regs ********/ - - ov_write(c, 0x14, qvga?0x24:0x04); - - /******** Palette-specific regs ********/ - - if (win->format == VIDEO_PALETTE_GREY) { - ov_write_mask(c, 0x0e, 0x40, 0x40); - ov_write_mask(c, 0x13, 0x20, 0x20); - } else { - ov_write_mask(c, 0x0e, 0x00, 0x40); - ov_write_mask(c, 0x13, 0x00, 0x20); - } - - /******** Clock programming ********/ - - ov_write(c, 0x11, win->clockdiv); - - /******** Resolution-specific ********/ - - if (win->width == 640 && win->height == 480) - ov_write(c, 0x35, 0x9e); - else - ov_write(c, 0x35, 0x1e); - - return 0; -} - -static int ov7x10_set_window(struct i2c_client *c, struct ovcamchip_window *win) -{ - int ret, hwscale, vwscale; - - ret = ov7x10_mode_init(c, win); - if (ret < 0) - return ret; - - if (win->quarter) { - hwscale = 1; - vwscale = 0; - } else { - hwscale = 2; - vwscale = 1; - } - - ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); - ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); - ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); - ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); - - return 0; -} - -static int ov7x10_command(struct i2c_client *c, unsigned int cmd, void *arg) -{ - switch (cmd) { - case OVCAMCHIP_CMD_S_CTRL: - return ov7x10_set_control(c, arg); - case OVCAMCHIP_CMD_G_CTRL: - return ov7x10_get_control(c, arg); - case OVCAMCHIP_CMD_S_MODE: - return ov7x10_set_window(c, arg); - default: - DDEBUG(2, &c->dev, "command not supported: %d", cmd); - return -ENOIOCTLCMD; - } -} - -struct ovcamchip_ops ov7x10_ops = { - .init = ov7x10_init, - .free = ov7x10_free, - .command = ov7x10_command, -}; diff --git a/drivers/media/video/ovcamchip/ov7x20.c b/drivers/media/video/ovcamchip/ov7x20.c deleted file mode 100644 index 8e26ae3..0000000 --- a/drivers/media/video/ovcamchip/ov7x20.c +++ /dev/null @@ -1,454 +0,0 @@ -/* OmniVision OV7620/OV7120 Camera Chip Support Code - * - * Copyright (c) 1999-2004 Mark McClelland <mark@alpha.dyndns.org> - * http://alpha.dyndns.org/ov511/ - * - * OV7620 fixes by Charl P. Botha <cpbotha@ieee.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. NO WARRANTY OF ANY KIND is expressed or implied. - */ - -#define DEBUG - -#include <linux/slab.h> -#include "ovcamchip_priv.h" - -/* Registers */ -#define REG_GAIN 0x00 /* gain [5:0] */ -#define REG_BLUE 0x01 /* blue gain */ -#define REG_RED 0x02 /* red gain */ -#define REG_SAT 0x03 /* saturation */ -#define REG_BRT 0x06 /* Y brightness */ -#define REG_SHARP 0x07 /* analog sharpness */ -#define REG_BLUE_BIAS 0x0C /* WB blue ratio [5:0] */ -#define REG_RED_BIAS 0x0D /* WB red ratio [5:0] */ -#define REG_EXP 0x10 /* exposure */ - -/* Default control settings. Values are in terms of V4L2 controls. */ -#define OV7120_DFL_BRIGHT 0x60 -#define OV7620_DFL_BRIGHT 0x60 -#define OV7120_DFL_SAT 0xb0 -#define OV7620_DFL_SAT 0xc0 -#define DFL_AUTO_EXP 1 -#define DFL_AUTO_GAIN 1 -#define OV7120_DFL_GAIN 0x00 -#define OV7620_DFL_GAIN 0x00 -/* NOTE: Since autoexposure is the default, these aren't programmed into the - * OV7x20 chip. They are just here because V4L2 expects a default */ -#define OV7120_DFL_EXP 0x7f -#define OV7620_DFL_EXP 0x7f - -/* Window parameters */ -#define HWSBASE 0x2F /* From 7620.SET (spec is wrong) */ -#define HWEBASE 0x2F -#define VWSBASE 0x05 -#define VWEBASE 0x05 - -struct ov7x20 { - int auto_brt; - int auto_exp; - int auto_gain; - int backlight; - int bandfilt; - int mirror; -}; - -/* Contrast look-up table */ -static unsigned char ctab[] = { - 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57, - 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff -}; - -/* Settings for (Black & White) OV7120 camera chip */ -static struct ovcamchip_regvals regvals_init_7120[] = { - { 0x12, 0x80 }, /* reset */ - { 0x13, 0x00 }, /* Autoadjust off */ - { 0x12, 0x20 }, /* Disable AWB */ - { 0x13, DFL_AUTO_GAIN?0x01:0x00 }, /* Autoadjust on (if desired) */ - { 0x00, OV7120_DFL_GAIN }, - { 0x01, 0x80 }, - { 0x02, 0x80 }, - { 0x03, OV7120_DFL_SAT }, - { 0x06, OV7120_DFL_BRIGHT }, - { 0x07, 0x00 }, - { 0x0c, 0x20 }, - { 0x0d, 0x20 }, - { 0x11, 0x01 }, - { 0x14, 0x84 }, - { 0x15, 0x01 }, - { 0x16, 0x03 }, - { 0x17, 0x2f }, - { 0x18, 0xcf }, - { 0x19, 0x06 }, - { 0x1a, 0xf5 }, - { 0x1b, 0x00 }, - { 0x20, 0x08 }, - { 0x21, 0x80 }, - { 0x22, 0x80 }, - { 0x23, 0x00 }, - { 0x26, 0xa0 }, - { 0x27, 0xfa }, - { 0x28, 0x20 }, /* DON'T set bit 6. It is for the OV7620 only */ - { 0x29, DFL_AUTO_EXP?0x00:0x80 }, - { 0x2a, 0x10 }, - { 0x2b, 0x00 }, - { 0x2c, 0x88 }, - { 0x2d, 0x95 }, - { 0x2e, 0x80 }, - { 0x2f, 0x44 }, - { 0x60, 0x20 }, - { 0x61, 0x02 }, - { 0x62, 0x5f }, - { 0x63, 0xd5 }, - { 0x64, 0x57 }, - { 0x65, 0x83 }, /* OV says "don't change this value" */ - { 0x66, 0x55 }, - { 0x67, 0x92 }, - { 0x68, 0xcf }, - { 0x69, 0x76 }, - { 0x6a, 0x22 }, - { 0x6b, 0xe2 }, - { 0x6c, 0x40 }, - { 0x6d, 0x48 }, - { 0x6e, 0x80 }, - { 0x6f, 0x0d }, - { 0x70, 0x89 }, - { 0x71, 0x00 }, - { 0x72, 0x14 }, - { 0x73, 0x54 }, - { 0x74, 0xa0 }, - { 0x75, 0x8e }, - { 0x76, 0x00 }, - { 0x77, 0xff }, - { 0x78, 0x80 }, - { 0x79, 0x80 }, - { 0x7a, 0x80 }, - { 0x7b, 0xe6 }, - { 0x7c, 0x00 }, - { 0x24, 0x3a }, - { 0x25, 0x60 }, - { 0xff, 0xff }, /* END MARKER */ -}; - -/* Settings for (color) OV7620 camera chip */ -static struct ovcamchip_regvals regvals_init_7620[] = { - { 0x12, 0x80 }, /* reset */ - { 0x00, OV7620_DFL_GAIN }, - { 0x01, 0x80 }, - { 0x02, 0x80 }, - { 0x03, OV7620_DFL_SAT }, - { 0x06, OV7620_DFL_BRIGHT }, - { 0x07, 0x00 }, - { 0x0c, 0x24 }, - { 0x0c, 0x24 }, - { 0x0d, 0x24 }, - { 0x11, 0x01 }, - { 0x12, 0x24 }, - { 0x13, DFL_AUTO_GAIN?0x01:0x00 }, - { 0x14, 0x84 }, - { 0x15, 0x01 }, - { 0x16, 0x03 }, - { 0x17, 0x2f }, - { 0x18, 0xcf }, - { 0x19, 0x06 }, - { 0x1a, 0xf5 }, - { 0x1b, 0x00 }, - { 0x20, 0x18 }, - { 0x21, 0x80 }, - { 0x22, 0x80 }, - { 0x23, 0x00 }, - { 0x26, 0xa2 }, - { 0x27, 0xea }, - { 0x28, 0x20 }, - { 0x29, DFL_AUTO_EXP?0x00:0x80 }, - { 0x2a, 0x10 }, - { 0x2b, 0x00 }, - { 0x2c, 0x88 }, - { 0x2d, 0x91 }, - { 0x2e, 0x80 }, - { 0x2f, 0x44 }, - { 0x60, 0x27 }, - { 0x61, 0x02 }, - { 0x62, 0x5f }, - { 0x63, 0xd5 }, - { 0x64, 0x57 }, - { 0x65, 0x83 }, - { 0x66, 0x55 }, - { 0x67, 0x92 }, - { 0x68, 0xcf }, - { 0x69, 0x76 }, - { 0x6a, 0x22 }, - { 0x6b, 0x00 }, - { 0x6c, 0x02 }, - { 0x6d, 0x44 }, - { 0x6e, 0x80 }, - { 0x6f, 0x1d }, - { 0x70, 0x8b }, - { 0x71, 0x00 }, - { 0x72, 0x14 }, - { 0x73, 0x54 }, - { 0x74, 0x00 }, - { 0x75, 0x8e }, - { 0x76, 0x00 }, - { 0x77, 0xff }, - { 0x78, 0x80 }, - { 0x79, 0x80 }, - { 0x7a, 0x80 }, - { 0x7b, 0xe2 }, - { 0x7c, 0x00 }, - { 0xff, 0xff }, /* END MARKER */ -}; - -/* Returns index into the specified look-up table, with 'n' elements, for which - * the value is greater than or equal to "val". If a match isn't found, (n-1) - * is returned. The entries in the table must be in ascending order. */ -static inline int ov7x20_lut_find(unsigned char lut[], int n, unsigned char val) -{ - int i = 0; - - while (lut[i] < val && i < n) - i++; - - return i; -} - -/* This initializes the OV7x20 camera chip and relevant variables. */ -static int ov7x20_init(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov7x20 *s; - int rc; - - DDEBUG(4, &c->dev, "entered"); - - if (ov->mono) - rc = ov_write_regvals(c, regvals_init_7120); - else - rc = ov_write_regvals(c, regvals_init_7620); - - if (rc < 0) - return rc; - - ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); - if (!s) - return -ENOMEM; - - s->auto_brt = 1; - s->auto_exp = DFL_AUTO_EXP; - s->auto_gain = DFL_AUTO_GAIN; - - return 0; -} - -static int ov7x20_free(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - - kfree(ov->spriv); - return 0; -} - -static int ov7x20_set_v4l1_control(struct i2c_client *c, - struct ovcamchip_control *ctl) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov7x20 *s = ov->spriv; - int rc; - int v = ctl->value; - - switch (ctl->id) { - case OVCAMCHIP_CID_CONT: - { - /* Use Y gamma control instead. Bit 0 enables it. */ - rc = ov_write(c, 0x64, ctab[v >> 12]); - break; - } - case OVCAMCHIP_CID_BRIGHT: - /* 7620 doesn't like manual changes when in auto mode */ - if (!s->auto_brt) - rc = ov_write(c, REG_BRT, v >> 8); - else - rc = 0; - break; - case OVCAMCHIP_CID_SAT: - rc = ov_write(c, REG_SAT, v >> 8); - break; - case OVCAMCHIP_CID_EXP: - if (!s->auto_exp) - rc = ov_write(c, REG_EXP, v); - else - rc = -EBUSY; - break; - case OVCAMCHIP_CID_FREQ: - { - int sixty = (v == 60); - - rc = ov_write_mask(c, 0x2a, sixty?0x00:0x80, 0x80); - if (rc < 0) - goto out; - - rc = ov_write(c, 0x2b, sixty?0x00:0xac); - if (rc < 0) - goto out; - - rc = ov_write_mask(c, 0x76, 0x01, 0x01); - break; - } - case OVCAMCHIP_CID_BANDFILT: - rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); - s->bandfilt = v; - break; - case OVCAMCHIP_CID_AUTOBRIGHT: - rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); - s->auto_brt = v; - break; - case OVCAMCHIP_CID_AUTOEXP: - rc = ov_write_mask(c, 0x13, v?0x01:0x00, 0x01); - s->auto_exp = v; - break; - case OVCAMCHIP_CID_BACKLIGHT: - { - rc = ov_write_mask(c, 0x68, v?0xe0:0xc0, 0xe0); - if (rc < 0) - goto out; - - rc = ov_write_mask(c, 0x29, v?0x08:0x00, 0x08); - if (rc < 0) - goto out; - - rc = ov_write_mask(c, 0x28, v?0x02:0x00, 0x02); - s->backlight = v; - break; - } - case OVCAMCHIP_CID_MIRROR: - rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); - s->mirror = v; - break; - default: - DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); - return -EPERM; - } - -out: - DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); - return rc; -} - -static int ov7x20_get_v4l1_control(struct i2c_client *c, - struct ovcamchip_control *ctl) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - struct ov7x20 *s = ov->spriv; - int rc = 0; - unsigned char val = 0; - - switch (ctl->id) { - case OVCAMCHIP_CID_CONT: - rc = ov_read(c, 0x64, &val); - ctl->value = ov7x20_lut_find(ctab, 16, val) << 12; - break; - case OVCAMCHIP_CID_BRIGHT: - rc = ov_read(c, REG_BRT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_SAT: - rc = ov_read(c, REG_SAT, &val); - ctl->value = val << 8; - break; - case OVCAMCHIP_CID_EXP: - rc = ov_read(c, REG_EXP, &val); - ctl->value = val; - break; - case OVCAMCHIP_CID_BANDFILT: - ctl->value = s->bandfilt; - break; - case OVCAMCHIP_CID_AUTOBRIGHT: - ctl->value = s->auto_brt; - break; - case OVCAMCHIP_CID_AUTOEXP: - ctl->value = s->auto_exp; - break; - case OVCAMCHIP_CID_BACKLIGHT: - ctl->value = s->backlight; - break; - case OVCAMCHIP_CID_MIRROR: - ctl->value = s->mirror; - break; - default: - DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); - return -EPERM; - } - - DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); - return rc; -} - -static int ov7x20_mode_init(struct i2c_client *c, struct ovcamchip_window *win) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - int qvga = win->quarter; - - /******** QVGA-specific regs ********/ - ov_write_mask(c, 0x14, qvga?0x20:0x00, 0x20); - ov_write_mask(c, 0x28, qvga?0x00:0x20, 0x20); - ov_write(c, 0x24, qvga?0x20:0x3a); - ov_write(c, 0x25, qvga?0x30:0x60); - ov_write_mask(c, 0x2d, qvga?0x40:0x00, 0x40); - if (!ov->mono) - ov_write_mask(c, 0x67, qvga?0xf0:0x90, 0xf0); - ov_write_mask(c, 0x74, qvga?0x20:0x00, 0x20); - - /******** Clock programming ********/ - - ov_write(c, 0x11, win->clockdiv); - - return 0; -} - -static int ov7x20_set_window(struct i2c_client *c, struct ovcamchip_window *win) -{ - int ret, hwscale, vwscale; - - ret = ov7x20_mode_init(c, win); - if (ret < 0) - return ret; - - if (win->quarter) { - hwscale = 1; - vwscale = 0; - } else { - hwscale = 2; - vwscale = 1; - } - - ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); - ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); - ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); - ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); - - return 0; -} - -static int ov7x20_command(struct i2c_client *c, unsigned int cmd, void *arg) -{ - switch (cmd) { - case OVCAMCHIP_CMD_S_CTRL: - return ov7x20_set_v4l1_control(c, arg); - case OVCAMCHIP_CMD_G_CTRL: - return ov7x20_get_v4l1_control(c, arg); - case OVCAMCHIP_CMD_S_MODE: - return ov7x20_set_window(c, arg); - default: - DDEBUG(2, &c->dev, "command not supported: %d", cmd); - return -ENOIOCTLCMD; - } -} - -struct ovcamchip_ops ov7x20_ops = { - .init = ov7x20_init, - .free = ov7x20_free, - .command = ov7x20_command, -}; diff --git a/drivers/media/video/ovcamchip/ovcamchip_core.c b/drivers/media/video/ovcamchip/ovcamchip_core.c deleted file mode 100644 index d573d84..0000000 --- a/drivers/media/video/ovcamchip/ovcamchip_core.c +++ /dev/null @@ -1,395 +0,0 @@ -/* Shared Code for OmniVision Camera Chip Drivers - * - * Copyright (c) 2004 Mark McClelland <mark@alpha.dyndns.org> - * http://alpha.dyndns.org/ov511/ - * - * 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. NO WARRANTY OF ANY KIND is expressed or implied. - */ - -#define DEBUG - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/delay.h> -#include <linux/i2c.h> -#include <media/v4l2-device.h> -#include <media/v4l2-i2c-drv.h> -#include "ovcamchip_priv.h" - -#define DRIVER_VERSION "v2.27 for Linux 2.6" -#define DRIVER_AUTHOR "Mark McClelland <mark@alpha.dyndns.org>" -#define DRIVER_DESC "OV camera chip I2C driver" - -#define PINFO(fmt, args...) printk(KERN_INFO "ovcamchip: " fmt "\n" , ## args); -#define PERROR(fmt, args...) printk(KERN_ERR "ovcamchip: " fmt "\n" , ## args); - -#ifdef DEBUG -int ovcamchip_debug = 0; -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, - "Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=all"); -#endif - -/* By default, let bridge driver tell us if chip is monochrome. mono=0 - * will ignore that and always treat chips as color. mono=1 will force - * monochrome mode for all chips. */ -static int mono = -1; -module_param(mono, int, 0); -MODULE_PARM_DESC(mono, - "1=chips are monochrome (OVx1xx), 0=force color, -1=autodetect (default)"); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); - - -/* Registers common to all chips, that are needed for detection */ -#define GENERIC_REG_ID_HIGH 0x1C /* manufacturer ID MSB */ -#define GENERIC_REG_ID_LOW 0x1D /* manufacturer ID LSB */ -#define GENERIC_REG_COM_I 0x29 /* misc ID bits */ - -static char *chip_names[NUM_CC_TYPES] = { - [CC_UNKNOWN] = "Unknown chip", - [CC_OV76BE] = "OV76BE", - [CC_OV7610] = "OV7610", - [CC_OV7620] = "OV7620", - [CC_OV7620AE] = "OV7620AE", - [CC_OV6620] = "OV6620", - [CC_OV6630] = "OV6630", - [CC_OV6630AE] = "OV6630AE", - [CC_OV6630AF] = "OV6630AF", -}; - -/* ----------------------------------------------------------------------- */ - -int ov_write_regvals(struct i2c_client *c, struct ovcamchip_regvals *rvals) -{ - int rc; - - while (rvals->reg != 0xff) { - rc = ov_write(c, rvals->reg, rvals->val); - if (rc < 0) - return rc; - rvals++; - } - - return 0; -} - -/* 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". - */ -int ov_write_mask(struct i2c_client *c, - unsigned char reg, - unsigned char value, - unsigned char mask) -{ - int rc; - unsigned char oldval, newval; - - if (mask == 0xff) { - newval = value; - } else { - rc = ov_read(c, reg, &oldval); - if (rc < 0) - return rc; - - oldval &= (~mask); /* Clear the masked bits */ - value &= mask; /* Enforce mask on value */ - newval = oldval | value; /* Set the desired bits */ - } - - return ov_write(c, reg, newval); -} - -/* ----------------------------------------------------------------------- */ - -/* Reset the chip and ensure that I2C is synchronized. Returns <0 if failure. - */ -static int init_camchip(struct i2c_client *c) -{ - int i, success; - unsigned char high, low; - - /* Reset the chip */ - ov_write(c, 0x12, 0x80); - - /* Wait for it to initialize */ - msleep(150); - - for (i = 0, success = 0; i < I2C_DETECT_RETRIES && !success; i++) { - if (ov_read(c, GENERIC_REG_ID_HIGH, &high) >= 0) { - if (ov_read(c, GENERIC_REG_ID_LOW, &low) >= 0) { - if (high == 0x7F && low == 0xA2) { - success = 1; - continue; - } - } - } - - /* Reset the chip */ - ov_write(c, 0x12, 0x80); - - /* Wait for it to initialize */ - msleep(150); - - /* Dummy read to sync I2C */ - ov_read(c, 0x00, &low); - } - - if (!success) - return -EIO; - - PDEBUG(1, "I2C synced in %d attempt(s)", i); - - return 0; -} - -/* This detects the OV7610, OV7620, or OV76BE chip. */ -static int ov7xx0_detect(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - int rc; - unsigned char val; - - PDEBUG(4, ""); - - /* Detect chip (sub)type */ - rc = ov_read(c, GENERIC_REG_COM_I, &val); - if (rc < 0) { - PERROR("Error detecting ov7xx0 type"); - return rc; - } - - if ((val & 3) == 3) { - PINFO("Camera chip is an OV7610"); - ov->subtype = CC_OV7610; - } else if ((val & 3) == 1) { - rc = ov_read(c, 0x15, &val); - if (rc < 0) { - PERROR("Error detecting ov7xx0 type"); - return rc; - } - - if (val & 1) { - PINFO("Camera chip is an OV7620AE"); - /* OV7620 is a close enough match for now. There are - * some definite differences though, so this should be - * fixed */ - ov->subtype = CC_OV7620; - } else { - PINFO("Camera chip is an OV76BE"); - ov->subtype = CC_OV76BE; - } - } else if ((val & 3) == 0) { - PINFO("Camera chip is an OV7620"); - ov->subtype = CC_OV7620; - } else { - PERROR("Unknown camera chip version: %d", val & 3); - return -ENOSYS; - } - - if (ov->subtype == CC_OV76BE) - ov->sops = &ov76be_ops; - else if (ov->subtype == CC_OV7620) - ov->sops = &ov7x20_ops; - else - ov->sops = &ov7x10_ops; - - return 0; -} - -/* This detects the OV6620, OV6630, OV6630AE, or OV6630AF chip. */ -static int ov6xx0_detect(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - int rc; - unsigned char val; - - PDEBUG(4, ""); - - /* Detect chip (sub)type */ - rc = ov_read(c, GENERIC_REG_COM_I, &val); - if (rc < 0) { - PERROR("Error detecting ov6xx0 type"); - return -1; - } - - if ((val & 3) == 0) { - ov->subtype = CC_OV6630; - PINFO("Camera chip is an OV6630"); - } else if ((val & 3) == 1) { - ov->subtype = CC_OV6620; - PINFO("Camera chip is an OV6620"); - } else if ((val & 3) == 2) { - ov->subtype = CC_OV6630; - PINFO("Camera chip is an OV6630AE"); - } else if ((val & 3) == 3) { - ov->subtype = CC_OV6630; - PINFO("Camera chip is an OV6630AF"); - } - - if (ov->subtype == CC_OV6620) - ov->sops = &ov6x20_ops; - else - ov->sops = &ov6x30_ops; - - return 0; -} - -static int ovcamchip_detect(struct i2c_client *c) -{ - /* Ideally we would just try a single register write and see if it NAKs. - * That isn't possible since the OV518 can't report I2C transaction - * failures. So, we have to try to initialize the chip (i.e. reset it - * and check the ID registers) to detect its presence. */ - - /* Test for 7xx0 */ - PDEBUG(3, "Testing for 0V7xx0"); - if (init_camchip(c) < 0) - return -ENODEV; - /* 7-bit addresses with bit 0 set are for the OV7xx0 */ - if (c->addr & 1) { - if (ov7xx0_detect(c) < 0) { - PERROR("Failed to init OV7xx0"); - return -EIO; - } - return 0; - } - /* Test for 6xx0 */ - PDEBUG(3, "Testing for 0V6xx0"); - if (ov6xx0_detect(c) < 0) { - PERROR("Failed to init OV6xx0"); - return -EIO; - } - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static long ovcamchip_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) -{ - struct ovcamchip *ov = to_ovcamchip(sd); - struct i2c_client *c = v4l2_get_subdevdata(sd); - - if (!ov->initialized && - cmd != OVCAMCHIP_CMD_Q_SUBTYPE && - cmd != OVCAMCHIP_CMD_INITIALIZE) { - v4l2_err(sd, "Camera chip not initialized yet!\n"); - return -EPERM; - } - - switch (cmd) { - case OVCAMCHIP_CMD_Q_SUBTYPE: - { - *(int *)arg = ov->subtype; - return 0; - } - case OVCAMCHIP_CMD_INITIALIZE: - { - int rc; - - if (mono == -1) - ov->mono = *(int *)arg; - else - ov->mono = mono; - - if (ov->mono) { - if (ov->subtype != CC_OV7620) - v4l2_warn(sd, "Monochrome not " - "implemented for this chip\n"); - else - v4l2_info(sd, "Initializing chip as " - "monochrome\n"); - } - - rc = ov->sops->init(c); - if (rc < 0) - return rc; - - ov->initialized = 1; - return 0; - } - default: - return ov->sops->command(c, cmd, arg); - } -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops ovcamchip_core_ops = { - .ioctl = ovcamchip_ioctl, -}; - -static const struct v4l2_subdev_ops ovcamchip_ops = { - .core = &ovcamchip_core_ops, -}; - -static int ovcamchip_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct ovcamchip *ov; - struct v4l2_subdev *sd; - int rc = 0; - - ov = kzalloc(sizeof *ov, GFP_KERNEL); - if (!ov) { - rc = -ENOMEM; - goto no_ov; - } - sd = &ov->sd; - v4l2_i2c_subdev_init(sd, client, &ovcamchip_ops); - - rc = ovcamchip_detect(client); - if (rc < 0) - goto error; - - v4l_info(client, "%s found @ 0x%02x (%s)\n", - chip_names[ov->subtype], client->addr << 1, client->adapter->name); - - PDEBUG(1, "Camera chip detection complete"); - - return rc; -error: - kfree(ov); -no_ov: - PDEBUG(1, "returning %d", rc); - return rc; -} - -static int ovcamchip_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ovcamchip *ov = to_ovcamchip(sd); - int rc; - - v4l2_device_unregister_subdev(sd); - rc = ov->sops->free(client); - if (rc < 0) - return rc; - - kfree(ov); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id ovcamchip_id[] = { - { "ovcamchip", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ovcamchip_id); - -static struct v4l2_i2c_driver_data v4l2_i2c_data = { - .name = "ovcamchip", - .probe = ovcamchip_probe, - .remove = ovcamchip_remove, - .id_table = ovcamchip_id, -}; diff --git a/drivers/media/video/ovcamchip/ovcamchip_priv.h b/drivers/media/video/ovcamchip/ovcamchip_priv.h deleted file mode 100644 index 4f07b78..0000000 --- a/drivers/media/video/ovcamchip/ovcamchip_priv.h +++ /dev/null @@ -1,101 +0,0 @@ -/* OmniVision* camera chip driver private definitions for core code and - * chip-specific code - * - * Copyright (c) 1999-2004 Mark McClelland - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. NO WARRANTY OF ANY KIND is expressed or implied. - * - * * OmniVision is a trademark of OmniVision Technologies, Inc. This driver - * is not sponsored or developed by them. - */ - -#ifndef __LINUX_OVCAMCHIP_PRIV_H -#define __LINUX_OVCAMCHIP_PRIV_H - -#include <linux/i2c.h> -#include <media/v4l2-subdev.h> -#include <media/ovcamchip.h> - -#ifdef DEBUG -extern int ovcamchip_debug; -#endif - -#define PDEBUG(level, fmt, args...) \ - if (ovcamchip_debug >= (level)) pr_debug("[%s:%d] " fmt "\n", \ - __func__, __LINE__ , ## args) - -#define DDEBUG(level, dev, fmt, args...) \ - if (ovcamchip_debug >= (level)) dev_dbg(dev, "[%s:%d] " fmt "\n", \ - __func__, __LINE__ , ## args) - -/* Number of times to retry chip detection. Increase this if you are getting - * "Failed to init camera chip" */ -#define I2C_DETECT_RETRIES 10 - -struct ovcamchip_regvals { - unsigned char reg; - unsigned char val; -}; - -struct ovcamchip_ops { - int (*init)(struct i2c_client *); - int (*free)(struct i2c_client *); - int (*command)(struct i2c_client *, unsigned int, void *); -}; - -struct ovcamchip { - struct v4l2_subdev sd; - struct ovcamchip_ops *sops; - void *spriv; /* Private data for OV7x10.c etc... */ - int subtype; /* = SEN_OV7610 etc... */ - int mono; /* Monochrome chip? (invalid until init) */ - int initialized; /* OVCAMCHIP_CMD_INITIALIZE was successful */ -}; - -static inline struct ovcamchip *to_ovcamchip(struct v4l2_subdev *sd) -{ - return container_of(sd, struct ovcamchip, sd); -} - -extern struct ovcamchip_ops ov6x20_ops; -extern struct ovcamchip_ops ov6x30_ops; -extern struct ovcamchip_ops ov7x10_ops; -extern struct ovcamchip_ops ov7x20_ops; -extern struct ovcamchip_ops ov76be_ops; - -/* --------------------------------- */ -/* I2C I/O */ -/* --------------------------------- */ - -static inline int ov_read(struct i2c_client *c, unsigned char reg, - unsigned char *value) -{ - int rc; - - rc = i2c_smbus_read_byte_data(c, reg); - *value = (unsigned char) rc; - return rc; -} - -static inline int ov_write(struct i2c_client *c, unsigned char reg, - unsigned char value ) -{ - return i2c_smbus_write_byte_data(c, reg, value); -} - -/* --------------------------------- */ -/* FUNCTION PROTOTYPES */ -/* --------------------------------- */ - -/* Functions in ovcamchip_core.c */ - -extern int ov_write_regvals(struct i2c_client *c, - struct ovcamchip_regvals *rvals); - -extern int ov_write_mask(struct i2c_client *c, unsigned char reg, - unsigned char value, unsigned char mask); - -#endif diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/drivers/media/video/pvrusb2/pvrusb2-ioread.c index b482478..bba6115 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-ioread.c +++ b/drivers/media/video/pvrusb2/pvrusb2-ioread.c @@ -223,7 +223,10 @@ int pvr2_ioread_setup(struct pvr2_ioread *cp,struct pvr2_stream *sp) " pvr2_ioread_setup (setup) id=%p",cp); pvr2_stream_kill(sp); ret = pvr2_stream_set_buffer_count(sp,BUFFER_COUNT); - if (ret < 0) return ret; + if (ret < 0) { + mutex_unlock(&cp->mutex); + return ret; + } for (idx = 0; idx < BUFFER_COUNT; idx++) { bp = pvr2_stream_get_buffer(sp,idx); pvr2_buffer_set_buffer(bp, diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index fb242f6..9de7d59 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -276,7 +276,7 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) * longer in STATE_QUEUED or STATE_ACTIVE */ videobuf_waiton(&buf->vb, 0, 0); - videobuf_dma_unmap(vq, dma); + videobuf_dma_unmap(vq->dev, dma); videobuf_dma_free(dma); for (i = 0; i < ARRAY_SIZE(buf->dmas); i++) { @@ -1284,7 +1284,7 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, unsigned int id } switch (code) { - case V4L2_MBUS_FMT_YUYV8_2X8_BE: + case V4L2_MBUS_FMT_UYVY8_2X8: formats++; if (xlate) { xlate->host_fmt = &pxa_camera_formats[0]; @@ -1293,9 +1293,9 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, unsigned int id dev_dbg(dev, "Providing format %s using code %d\n", pxa_camera_formats[0].name, code); } - case V4L2_MBUS_FMT_YVYU8_2X8_BE: - case V4L2_MBUS_FMT_YUYV8_2X8_LE: - case V4L2_MBUS_FMT_YVYU8_2X8_LE: + case V4L2_MBUS_FMT_VYUY8_2X8: + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_YVYU8_2X8: case V4L2_MBUS_FMT_RGB565_2X8_LE: case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE: if (xlate) diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c index 47fd207..ce78fff 100644 --- a/drivers/media/video/rj54n1cb0c.c +++ b/drivers/media/video/rj54n1cb0c.c @@ -1,5 +1,5 @@ /* - * Driver for RJ54N1CB0C CMOS Image Sensor from Micron + * Driver for RJ54N1CB0C CMOS Image Sensor from Sharp * * Copyright (C) 2009, Guennadi Liakhovetski <g.liakhovetski@gmx.de> * @@ -127,8 +127,8 @@ static const struct rj54n1_datafmt *rj54n1_find_datafmt( } static const struct rj54n1_datafmt rj54n1_colour_fmts[] = { - {V4L2_MBUS_FMT_YUYV8_2X8_LE, V4L2_COLORSPACE_JPEG}, - {V4L2_MBUS_FMT_YVYU8_2X8_LE, V4L2_COLORSPACE_JPEG}, + {V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG}, + {V4L2_MBUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG}, {V4L2_MBUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB}, {V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB}, {V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB}, @@ -1046,12 +1046,12 @@ static int rj54n1_s_fmt(struct v4l2_subdev *sd, /* RA_SEL_UL is only relevant for raw modes, ignored otherwise. */ switch (mf->code) { - case V4L2_MBUS_FMT_YUYV8_2X8_LE: + case V4L2_MBUS_FMT_YUYV8_2X8: ret = reg_write(client, RJ54N1_OUT_SEL, 0); if (!ret) ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); break; - case V4L2_MBUS_FMT_YVYU8_2X8_LE: + case V4L2_MBUS_FMT_YVYU8_2X8: ret = reg_write(client, RJ54N1_OUT_SEL, 0); if (!ret) ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c index 3c7a79f..8ec7c9a 100644 --- a/drivers/media/video/s2255drv.c +++ b/drivers/media/video/s2255drv.c @@ -191,7 +191,6 @@ struct s2255_bufferi { struct s2255_dmaqueue { struct list_head active; struct s2255_dev *dev; - int channel; }; /* for firmware loading, fw_state */ @@ -226,51 +225,60 @@ struct s2255_pipeinfo { }; struct s2255_fmt; /*forward declaration */ +struct s2255_dev; + +struct s2255_channel { + struct video_device vdev; + int resources; + struct s2255_dmaqueue vidq; + struct s2255_bufferi buffer; + struct s2255_mode mode; + /* jpeg compression */ + struct v4l2_jpegcompression jc; + /* capture parameters (for high quality mode full size) */ + struct v4l2_captureparm cap_parm; + int cur_frame; + int last_frame; + + int b_acquire; + /* allocated image size */ + unsigned long req_image_size; + /* received packet size */ + unsigned long pkt_size; + int bad_payload; + unsigned long frame_count; + /* if JPEG image */ + int jpg_size; + /* if channel configured to default state */ + int configured; + wait_queue_head_t wait_setmode; + int setmode_ready; + /* video status items */ + int vidstatus; + wait_queue_head_t wait_vidstatus; + int vidstatus_ready; + unsigned int width; + unsigned int height; + const struct s2255_fmt *fmt; + int idx; /* channel number on device, 0-3 */ +}; + struct s2255_dev { - struct video_device vdev[MAX_CHANNELS]; + struct s2255_channel channel[MAX_CHANNELS]; struct v4l2_device v4l2_dev; - atomic_t channels; /* number of channels registered */ + atomic_t num_channels; int frames; struct mutex lock; struct mutex open_lock; - int resources[MAX_CHANNELS]; struct usb_device *udev; struct usb_interface *interface; u8 read_endpoint; - - struct s2255_dmaqueue vidq[MAX_CHANNELS]; struct timer_list timer; struct s2255_fw *fw_data; struct s2255_pipeinfo pipe; - struct s2255_bufferi buffer[MAX_CHANNELS]; - struct s2255_mode mode[MAX_CHANNELS]; - /* jpeg compression */ - struct v4l2_jpegcompression jc[MAX_CHANNELS]; - /* capture parameters (for high quality mode full size) */ - struct v4l2_captureparm cap_parm[MAX_CHANNELS]; - const struct s2255_fmt *cur_fmt[MAX_CHANNELS]; - int cur_frame[MAX_CHANNELS]; - int last_frame[MAX_CHANNELS]; u32 cc; /* current channel */ - int b_acquire[MAX_CHANNELS]; - /* allocated image size */ - unsigned long req_image_size[MAX_CHANNELS]; - /* received packet size */ - unsigned long pkt_size[MAX_CHANNELS]; - int bad_payload[MAX_CHANNELS]; - unsigned long frame_count[MAX_CHANNELS]; int frame_ready; - /* if JPEG image */ - int jpg_size[MAX_CHANNELS]; - /* if channel configured to default state */ - int chn_configured[MAX_CHANNELS]; - wait_queue_head_t wait_setmode[MAX_CHANNELS]; - int setmode_ready[MAX_CHANNELS]; - /* video status items */ - int vidstatus[MAX_CHANNELS]; - wait_queue_head_t wait_vidstatus[MAX_CHANNELS]; - int vidstatus_ready[MAX_CHANNELS]; int chn_ready; spinlock_t slock; /* dsp firmware version (f2255usb.bin) */ @@ -298,16 +306,10 @@ struct s2255_buffer { struct s2255_fh { struct s2255_dev *dev; - const struct s2255_fmt *fmt; - unsigned int width; - unsigned int height; struct videobuf_queue vb_vidq; enum v4l2_buf_type type; - int channel; - /* mode below is the desired mode. - mode in s2255_dev is the current mode that was last set */ - struct s2255_mode mode; - int resources[MAX_CHANNELS]; + struct s2255_channel *channel; + int resources; }; /* current cypress EEPROM firmware version */ @@ -360,12 +362,11 @@ static int *s2255_debug = &debug; static int s2255_start_readpipe(struct s2255_dev *dev); static void s2255_stop_readpipe(struct s2255_dev *dev); -static int s2255_start_acquire(struct s2255_dev *dev, unsigned long chn); -static int s2255_stop_acquire(struct s2255_dev *dev, unsigned long chn); -static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, - int chn, int jpgsize); -static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, - struct s2255_mode *mode); +static int s2255_start_acquire(struct s2255_channel *channel); +static int s2255_stop_acquire(struct s2255_channel *channel); +static void s2255_fillbuff(struct s2255_channel *chn, struct s2255_buffer *buf, + int jpgsize); +static int s2255_set_mode(struct s2255_channel *chan, struct s2255_mode *mode); static int s2255_board_shutdown(struct s2255_dev *dev); static void s2255_fwload_start(struct s2255_dev *dev, int reset); static void s2255_destroy(struct s2255_dev *dev); @@ -577,10 +578,11 @@ static void s2255_fwchunk_complete(struct urb *urb) } -static int s2255_got_frame(struct s2255_dev *dev, int chn, int jpgsize) +static int s2255_got_frame(struct s2255_channel *channel, int jpgsize) { - struct s2255_dmaqueue *dma_q = &dev->vidq[chn]; + struct s2255_dmaqueue *dma_q = &channel->vidq; struct s2255_buffer *buf; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); unsigned long flags = 0; int rc = 0; spin_lock_irqsave(&dev->slock, flags); @@ -593,7 +595,7 @@ static int s2255_got_frame(struct s2255_dev *dev, int chn, int jpgsize) struct s2255_buffer, vb.queue); list_del(&buf->vb.queue); do_gettimeofday(&buf->vb.ts); - s2255_fillbuff(dev, buf, dma_q->channel, jpgsize); + s2255_fillbuff(channel, buf, jpgsize); wake_up(&buf->vb.done); dprintk(2, "%s: [buf/i] [%p/%d]\n", __func__, buf, buf->vb.i); unlock: @@ -621,8 +623,8 @@ static const struct s2255_fmt *format_by_fourcc(int fourcc) * http://v4l.videotechnology.com/ * */ -static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, - int chn, int jpgsize) +static void s2255_fillbuff(struct s2255_channel *channel, + struct s2255_buffer *buf, int jpgsize) { int pos = 0; struct timeval ts; @@ -633,12 +635,11 @@ static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, if (!vbuf) return; - - last_frame = dev->last_frame[chn]; + last_frame = channel->last_frame; if (last_frame != -1) { - frm = &dev->buffer[chn].frame[last_frame]; + frm = &channel->buffer.frame[last_frame]; tmpbuf = - (const char *)dev->buffer[chn].frame[last_frame].lpvbits; + (const char *)channel->buffer.frame[last_frame].lpvbits; switch (buf->fmt->fourcc) { case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: @@ -661,7 +662,7 @@ static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, default: printk(KERN_DEBUG "s2255: unknown format?\n"); } - dev->last_frame[chn] = -1; + channel->last_frame = -1; } else { printk(KERN_ERR "s2255: =======no frame\n"); return; @@ -671,7 +672,7 @@ static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, (unsigned long)vbuf, pos); /* tell v4l buffer was filled */ - buf->vb.field_count = dev->frame_count[chn] * 2; + buf->vb.field_count = channel->frame_count * 2; do_gettimeofday(&ts); buf->vb.ts = ts; buf->vb.state = VIDEOBUF_DONE; @@ -686,8 +687,8 @@ static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) { struct s2255_fh *fh = vq->priv_data; - - *size = fh->width * fh->height * (fh->fmt->depth >> 3); + struct s2255_channel *channel = fh->channel; + *size = channel->width * channel->height * (channel->fmt->depth >> 3); if (0 == *count) *count = S2255_DEF_BUFS; @@ -710,30 +711,31 @@ static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, enum v4l2_field field) { struct s2255_fh *fh = vq->priv_data; + struct s2255_channel *channel = fh->channel; struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); int rc; + int w = channel->width; + int h = channel->height; dprintk(4, "%s, field=%d\n", __func__, field); - if (fh->fmt == NULL) + if (channel->fmt == NULL) return -EINVAL; - if ((fh->width < norm_minw(&fh->dev->vdev[fh->channel])) || - (fh->width > norm_maxw(&fh->dev->vdev[fh->channel])) || - (fh->height < norm_minh(&fh->dev->vdev[fh->channel])) || - (fh->height > norm_maxh(&fh->dev->vdev[fh->channel]))) { + if ((w < norm_minw(&channel->vdev)) || + (w > norm_maxw(&channel->vdev)) || + (h < norm_minh(&channel->vdev)) || + (h > norm_maxh(&channel->vdev))) { dprintk(4, "invalid buffer prepare\n"); return -EINVAL; } - - buf->vb.size = fh->width * fh->height * (fh->fmt->depth >> 3); - + buf->vb.size = w * h * (channel->fmt->depth >> 3); if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) { dprintk(4, "invalid buffer prepare\n"); return -EINVAL; } - buf->fmt = fh->fmt; - buf->vb.width = fh->width; - buf->vb.height = fh->height; + buf->fmt = channel->fmt; + buf->vb.width = w; + buf->vb.height = h; buf->vb.field = field; if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { @@ -753,8 +755,8 @@ static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); struct s2255_fh *fh = vq->priv_data; - struct s2255_dev *dev = fh->dev; - struct s2255_dmaqueue *vidq = &dev->vidq[fh->channel]; + struct s2255_channel *channel = fh->channel; + struct s2255_dmaqueue *vidq = &channel->vidq; dprintk(1, "%s\n", __func__); buf->vb.state = VIDEOBUF_QUEUED; list_add_tail(&buf->vb.queue, &vidq->active); @@ -765,7 +767,7 @@ static void buffer_release(struct videobuf_queue *vq, { struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); struct s2255_fh *fh = vq->priv_data; - dprintk(4, "%s %d\n", __func__, fh->channel); + dprintk(4, "%s %d\n", __func__, fh->channel->idx); free_buffer(vq, buf); } @@ -777,39 +779,43 @@ static struct videobuf_queue_ops s2255_video_qops = { }; -static int res_get(struct s2255_dev *dev, struct s2255_fh *fh) +static int res_get(struct s2255_fh *fh) { + struct s2255_dev *dev = fh->dev; /* is it free? */ + struct s2255_channel *channel = fh->channel; mutex_lock(&dev->lock); - if (dev->resources[fh->channel]) { + if (channel->resources) { /* no, someone else uses it */ mutex_unlock(&dev->lock); return 0; } /* it's free, grab it */ - dev->resources[fh->channel] = 1; - fh->resources[fh->channel] = 1; + channel->resources = 1; + fh->resources = 1; dprintk(1, "s2255: res: get\n"); mutex_unlock(&dev->lock); return 1; } -static int res_locked(struct s2255_dev *dev, struct s2255_fh *fh) +static int res_locked(struct s2255_fh *fh) { - return dev->resources[fh->channel]; + return fh->channel->resources; } static int res_check(struct s2255_fh *fh) { - return fh->resources[fh->channel]; + return fh->resources; } -static void res_free(struct s2255_dev *dev, struct s2255_fh *fh) +static void res_free(struct s2255_fh *fh) { + struct s2255_channel *channel = fh->channel; + struct s2255_dev *dev = fh->dev; mutex_lock(&dev->lock); - dev->resources[fh->channel] = 0; - fh->resources[fh->channel] = 0; + channel->resources = 0; + fh->resources = 0; mutex_unlock(&dev->lock); dprintk(1, "res: put\n"); } @@ -869,12 +875,13 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; - f->fmt.pix.width = fh->width; - f->fmt.pix.height = fh->height; + f->fmt.pix.width = channel->width; + f->fmt.pix.height = channel->height; f->fmt.pix.field = fh->vb_vidq.field; - f->fmt.pix.pixelformat = fh->fmt->fourcc; - f->fmt.pix.bytesperline = f->fmt.pix.width * (fh->fmt->depth >> 3); + f->fmt.pix.pixelformat = channel->fmt->fourcc; + f->fmt.pix.bytesperline = f->fmt.pix.width * (channel->fmt->depth >> 3); f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; return 0; } @@ -886,11 +893,10 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, enum v4l2_field field; int b_any_field = 0; struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; + struct s2255_channel *channel = fh->channel; int is_ntsc; - is_ntsc = - (dev->vdev[fh->channel].current_norm & V4L2_STD_NTSC) ? 1 : 0; + (channel->vdev.current_norm & V4L2_STD_NTSC) ? 1 : 0; fmt = format_by_fourcc(f->fmt.pix.pixelformat); @@ -982,8 +988,10 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; const struct s2255_fmt *fmt; struct videobuf_queue *q = &fh->vb_vidq; + struct s2255_mode mode; int ret; int norm; @@ -1005,54 +1013,61 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, goto out_s_fmt; } - if (res_locked(fh->dev, fh)) { + if (res_locked(fh)) { dprintk(1, "%s: channel busy\n", __func__); ret = -EBUSY; goto out_s_fmt; } - - fh->fmt = fmt; - fh->width = f->fmt.pix.width; - fh->height = f->fmt.pix.height; + mode = channel->mode; + channel->fmt = fmt; + channel->width = f->fmt.pix.width; + channel->height = f->fmt.pix.height; fh->vb_vidq.field = f->fmt.pix.field; fh->type = f->type; - norm = norm_minw(&fh->dev->vdev[fh->channel]); - if (fh->width > norm_minw(&fh->dev->vdev[fh->channel])) { - if (fh->height > norm_minh(&fh->dev->vdev[fh->channel])) { - if (fh->dev->cap_parm[fh->channel].capturemode & + norm = norm_minw(&channel->vdev); + if (channel->width > norm_minw(&channel->vdev)) { + if (channel->height > norm_minh(&channel->vdev)) { + if (channel->cap_parm.capturemode & V4L2_MODE_HIGHQUALITY) - fh->mode.scale = SCALE_4CIFSI; + mode.scale = SCALE_4CIFSI; else - fh->mode.scale = SCALE_4CIFS; + mode.scale = SCALE_4CIFS; } else - fh->mode.scale = SCALE_2CIFS; + mode.scale = SCALE_2CIFS; } else { - fh->mode.scale = SCALE_1CIFS; + mode.scale = SCALE_1CIFS; } - /* color mode */ - switch (fh->fmt->fourcc) { + switch (channel->fmt->fourcc) { case V4L2_PIX_FMT_GREY: - fh->mode.color &= ~MASK_COLOR; - fh->mode.color |= COLOR_Y8; + mode.color &= ~MASK_COLOR; + mode.color |= COLOR_Y8; break; case V4L2_PIX_FMT_JPEG: - fh->mode.color &= ~MASK_COLOR; - fh->mode.color |= COLOR_JPG; - fh->mode.color |= (fh->dev->jc[fh->channel].quality << 8); + mode.color &= ~MASK_COLOR; + mode.color |= COLOR_JPG; + mode.color |= (channel->jc.quality << 8); break; case V4L2_PIX_FMT_YUV422P: - fh->mode.color &= ~MASK_COLOR; - fh->mode.color |= COLOR_YUVPL; + mode.color &= ~MASK_COLOR; + mode.color |= COLOR_YUVPL; break; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: default: - fh->mode.color &= ~MASK_COLOR; - fh->mode.color |= COLOR_YUVPK; + mode.color &= ~MASK_COLOR; + mode.color |= COLOR_YUVPK; break; } + if ((mode.color & MASK_COLOR) != (channel->mode.color & MASK_COLOR)) + mode.restart = 1; + else if (mode.scale != channel->mode.scale) + mode.restart = 1; + else if (mode.format != channel->mode.format) + mode.restart = 1; + channel->mode = mode; + (void) s2255_set_mode(channel, &mode); ret = 0; out_s_fmt: mutex_unlock(&q->vb_lock); @@ -1197,26 +1212,27 @@ static void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode) * When the restart parameter is set, we sleep for ONE frame to allow the * DSP time to get the new frame */ -static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, +static int s2255_set_mode(struct s2255_channel *channel, struct s2255_mode *mode) { int res; __le32 *buffer; unsigned long chn_rev; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); mutex_lock(&dev->lock); - chn_rev = G_chnmap[chn]; - dprintk(3, "%s channel %lu\n", __func__, chn); + chn_rev = G_chnmap[channel->idx]; + dprintk(3, "%s channel: %d\n", __func__, channel->idx); /* if JPEG, set the quality */ if ((mode->color & MASK_COLOR) == COLOR_JPG) { mode->color &= ~MASK_COLOR; mode->color |= COLOR_JPG; mode->color &= ~MASK_JPG_QUALITY; - mode->color |= (dev->jc[chn].quality << 8); + mode->color |= (channel->jc.quality << 8); } /* save the mode */ - dev->mode[chn] = *mode; - dev->req_image_size[chn] = get_transfer_size(mode); - dprintk(1, "%s: reqsize %ld\n", __func__, dev->req_image_size[chn]); + channel->mode = *mode; + channel->req_image_size = get_transfer_size(mode); + dprintk(1, "%s: reqsize %ld\n", __func__, channel->req_image_size); buffer = kzalloc(512, GFP_KERNEL); if (buffer == NULL) { dev_err(&dev->udev->dev, "out of mem\n"); @@ -1227,38 +1243,38 @@ static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, buffer[0] = IN_DATA_TOKEN; buffer[1] = (__le32) cpu_to_le32(chn_rev); buffer[2] = CMD_SET_MODE; - memcpy(&buffer[3], &dev->mode[chn], sizeof(struct s2255_mode)); - dev->setmode_ready[chn] = 0; + memcpy(&buffer[3], &channel->mode, sizeof(struct s2255_mode)); + channel->setmode_ready = 0; res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); if (debug) s2255_print_cfg(dev, mode); kfree(buffer); /* wait at least 3 frames before continuing */ if (mode->restart) { - wait_event_timeout(dev->wait_setmode[chn], - (dev->setmode_ready[chn] != 0), + wait_event_timeout(channel->wait_setmode, + (channel->setmode_ready != 0), msecs_to_jiffies(S2255_SETMODE_TIMEOUT)); - if (dev->setmode_ready[chn] != 1) { + if (channel->setmode_ready != 1) { printk(KERN_DEBUG "s2255: no set mode response\n"); res = -EFAULT; } } /* clear the restart flag */ - dev->mode[chn].restart = 0; + channel->mode.restart = 0; mutex_unlock(&dev->lock); - dprintk(1, "%s chn %lu, result: %d\n", __func__, chn, res); + dprintk(1, "%s chn %d, result: %d\n", __func__, channel->idx, res); return res; } -static int s2255_cmd_status(struct s2255_dev *dev, unsigned long chn, - u32 *pstatus) +static int s2255_cmd_status(struct s2255_channel *channel, u32 *pstatus) { int res; __le32 *buffer; u32 chn_rev; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); mutex_lock(&dev->lock); - chn_rev = G_chnmap[chn]; - dprintk(4, "%s chan %lu\n", __func__, chn); + chn_rev = G_chnmap[channel->idx]; + dprintk(4, "%s chan %d\n", __func__, channel->idx); buffer = kzalloc(512, GFP_KERNEL); if (buffer == NULL) { dev_err(&dev->udev->dev, "out of mem\n"); @@ -1270,17 +1286,17 @@ static int s2255_cmd_status(struct s2255_dev *dev, unsigned long chn, buffer[1] = (__le32) cpu_to_le32(chn_rev); buffer[2] = CMD_STATUS; *pstatus = 0; - dev->vidstatus_ready[chn] = 0; + channel->vidstatus_ready = 0; res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); kfree(buffer); - wait_event_timeout(dev->wait_vidstatus[chn], - (dev->vidstatus_ready[chn] != 0), + wait_event_timeout(channel->wait_vidstatus, + (channel->vidstatus_ready != 0), msecs_to_jiffies(S2255_VIDSTATUS_TIMEOUT)); - if (dev->vidstatus_ready[chn] != 1) { + if (channel->vidstatus_ready != 1) { printk(KERN_DEBUG "s2255: no vidstatus response\n"); res = -EFAULT; } - *pstatus = dev->vidstatus[chn]; + *pstatus = channel->vidstatus; dprintk(4, "%s, vid status %d\n", __func__, *pstatus); mutex_unlock(&dev->lock); return res; @@ -1291,9 +1307,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) int res; struct s2255_fh *fh = priv; struct s2255_dev *dev = fh->dev; - struct s2255_mode *new_mode; - struct s2255_mode *old_mode; - int chn; + struct s2255_channel *channel = fh->channel; int j; dprintk(4, "%s\n", __func__); if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { @@ -1305,51 +1319,32 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) return -EINVAL; } - if (!res_get(dev, fh)) { + if (!res_get(fh)) { s2255_dev_err(&dev->udev->dev, "stream busy\n"); return -EBUSY; } - - /* send a set mode command everytime with restart. - in case we switch resolutions or other parameters */ - chn = fh->channel; - new_mode = &fh->mode; - old_mode = &fh->dev->mode[chn]; - - if ((new_mode->color & MASK_COLOR) != (old_mode->color & MASK_COLOR)) - new_mode->restart = 1; - else if (new_mode->scale != old_mode->scale) - new_mode->restart = 1; - else if (new_mode->format != old_mode->format) - new_mode->restart = 1; - - s2255_set_mode(dev, chn, new_mode); - new_mode->restart = 0; - *old_mode = *new_mode; - dev->cur_fmt[chn] = fh->fmt; - dev->last_frame[chn] = -1; - dev->bad_payload[chn] = 0; - dev->cur_frame[chn] = 0; - dev->frame_count[chn] = 0; + channel->last_frame = -1; + channel->bad_payload = 0; + channel->cur_frame = 0; + channel->frame_count = 0; for (j = 0; j < SYS_FRAMES; j++) { - dev->buffer[chn].frame[j].ulState = S2255_READ_IDLE; - dev->buffer[chn].frame[j].cur_size = 0; + channel->buffer.frame[j].ulState = S2255_READ_IDLE; + channel->buffer.frame[j].cur_size = 0; } res = videobuf_streamon(&fh->vb_vidq); if (res == 0) { - s2255_start_acquire(dev, chn); - dev->b_acquire[chn] = 1; - } else { - res_free(dev, fh); - } + s2255_start_acquire(channel); + channel->b_acquire = 1; + } else + res_free(fh); + return res; } static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) { struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; - dprintk(4, "%s\n, channel: %d", __func__, fh->channel); + dprintk(4, "%s\n, channel: %d", __func__, fh->channel->idx); if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { printk(KERN_ERR "invalid fh type0\n"); return -EINVAL; @@ -1358,16 +1353,16 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) printk(KERN_ERR "invalid type i\n"); return -EINVAL; } - s2255_stop_acquire(dev, fh->channel); + s2255_stop_acquire(fh->channel); videobuf_streamoff(&fh->vb_vidq); - res_free(dev, fh); + res_free(fh); return 0; } static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) { struct s2255_fh *fh = priv; - struct s2255_mode *mode; + struct s2255_mode mode; struct videobuf_queue *q = &fh->vb_vidq; int ret = 0; mutex_lock(&q->vb_lock); @@ -1376,29 +1371,32 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) ret = -EBUSY; goto out_s_std; } - if (res_locked(fh->dev, fh)) { + if (res_locked(fh)) { dprintk(1, "can't change standard after started\n"); ret = -EBUSY; goto out_s_std; } - mode = &fh->mode; + mode = fh->channel->mode; if (*i & V4L2_STD_NTSC) { dprintk(4, "%s NTSC\n", __func__); /* if changing format, reset frame decimation/intervals */ - if (mode->format != FORMAT_NTSC) { - mode->format = FORMAT_NTSC; - mode->fdec = FDEC_1; + if (mode.format != FORMAT_NTSC) { + mode.restart = 1; + mode.format = FORMAT_NTSC; + mode.fdec = FDEC_1; } } else if (*i & V4L2_STD_PAL) { dprintk(4, "%s PAL\n", __func__); - mode->format = FORMAT_PAL; - if (mode->format != FORMAT_PAL) { - mode->format = FORMAT_PAL; - mode->fdec = FDEC_1; + if (mode.format != FORMAT_PAL) { + mode.restart = 1; + mode.format = FORMAT_PAL; + mode.fdec = FDEC_1; } } else { ret = -EINVAL; } + if (mode.restart) + s2255_set_mode(fh->channel, &mode); out_s_std: mutex_unlock(&q->vb_lock); return ret; @@ -1416,6 +1414,7 @@ static int vidioc_enum_input(struct file *file, void *priv, { struct s2255_fh *fh = priv; struct s2255_dev *dev = fh->dev; + struct s2255_channel *channel = fh->channel; u32 status = 0; if (inp->index != 0) return -EINVAL; @@ -1424,7 +1423,7 @@ static int vidioc_enum_input(struct file *file, void *priv, inp->status = 0; if (dev->dsp_fw_ver >= S2255_MIN_DSP_STATUS) { int rc; - rc = s2255_cmd_status(dev, fh->channel, &status); + rc = s2255_cmd_status(fh->channel, &status); dprintk(4, "s2255_cmd_status rc: %d status %x\n", rc, status); if (rc == 0) inp->status = (status & 0x01) ? 0 @@ -1436,7 +1435,7 @@ static int vidioc_enum_input(struct file *file, void *priv, strlcpy(inp->name, "Composite", sizeof(inp->name)); break; case 0x2257: - strlcpy(inp->name, (fh->channel < 2) ? "Composite" : "S-Video", + strlcpy(inp->name, (channel->idx < 2) ? "Composite" : "S-Video", sizeof(inp->name)); break; } @@ -1460,6 +1459,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *qc) { struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; struct s2255_dev *dev = fh->dev; switch (qc->id) { case V4L2_CID_BRIGHTNESS: @@ -1477,7 +1477,7 @@ static int vidioc_queryctrl(struct file *file, void *priv, case V4L2_CID_PRIVATE_COLORFILTER: if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) return -EINVAL; - if ((dev->pid == 0x2257) && (fh->channel > 1)) + if ((dev->pid == 0x2257) && (channel->idx > 1)) return -EINVAL; strlcpy(qc->name, "Color Filter", sizeof(qc->name)); qc->type = V4L2_CTRL_TYPE_MENU; @@ -1499,25 +1499,26 @@ static int vidioc_g_ctrl(struct file *file, void *priv, { struct s2255_fh *fh = priv; struct s2255_dev *dev = fh->dev; + struct s2255_channel *channel = fh->channel; switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - ctrl->value = fh->mode.bright; + ctrl->value = channel->mode.bright; break; case V4L2_CID_CONTRAST: - ctrl->value = fh->mode.contrast; + ctrl->value = channel->mode.contrast; break; case V4L2_CID_SATURATION: - ctrl->value = fh->mode.saturation; + ctrl->value = channel->mode.saturation; break; case V4L2_CID_HUE: - ctrl->value = fh->mode.hue; + ctrl->value = channel->mode.hue; break; case V4L2_CID_PRIVATE_COLORFILTER: if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) return -EINVAL; - if ((dev->pid == 0x2257) && (fh->channel > 1)) + if ((dev->pid == 0x2257) && (channel->idx > 1)) return -EINVAL; - ctrl->value = !((fh->mode.color & MASK_INPUT_TYPE) >> 16); + ctrl->value = !((channel->mode.color & MASK_INPUT_TYPE) >> 16); break; default: return -EINVAL; @@ -1530,41 +1531,42 @@ static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; - struct s2255_mode *mode; - mode = &fh->mode; + struct s2255_channel *channel = fh->channel; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); + struct s2255_mode mode; + mode = channel->mode; dprintk(4, "%s\n", __func__); /* update the mode to the corresponding value */ switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - mode->bright = ctrl->value; + mode.bright = ctrl->value; break; case V4L2_CID_CONTRAST: - mode->contrast = ctrl->value; + mode.contrast = ctrl->value; break; case V4L2_CID_HUE: - mode->hue = ctrl->value; + mode.hue = ctrl->value; break; case V4L2_CID_SATURATION: - mode->saturation = ctrl->value; + mode.saturation = ctrl->value; break; case V4L2_CID_PRIVATE_COLORFILTER: if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) return -EINVAL; - if ((dev->pid == 0x2257) && (fh->channel > 1)) + if ((dev->pid == 0x2257) && (channel->idx > 1)) return -EINVAL; - mode->color &= ~MASK_INPUT_TYPE; - mode->color |= ((ctrl->value ? 0 : 1) << 16); + mode.color &= ~MASK_INPUT_TYPE; + mode.color |= ((ctrl->value ? 0 : 1) << 16); break; default: return -EINVAL; } - mode->restart = 0; + mode.restart = 0; /* set mode here. Note: stream does not need restarted. some V4L programs restart stream unnecessarily after a s_crtl. */ - s2255_set_mode(dev, fh->channel, mode); + s2255_set_mode(fh->channel, &mode); return 0; } @@ -1572,8 +1574,8 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv, struct v4l2_jpegcompression *jc) { struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; - *jc = dev->jc[fh->channel]; + struct s2255_channel *channel = fh->channel; + *jc = channel->jc; dprintk(2, "%s: quality %d\n", __func__, jc->quality); return 0; } @@ -1582,10 +1584,10 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv, struct v4l2_jpegcompression *jc) { struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; + struct s2255_channel *channel = fh->channel; if (jc->quality < 0 || jc->quality > 100) return -EINVAL; - dev->jc[fh->channel].quality = jc->quality; + channel->jc.quality = jc->quality; dprintk(2, "%s: quality %d\n", __func__, jc->quality); return 0; } @@ -1594,17 +1596,17 @@ static int vidioc_g_parm(struct file *file, void *priv, struct v4l2_streamparm *sp) { struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; __u32 def_num, def_dem; + struct s2255_channel *channel = fh->channel; if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; memset(sp, 0, sizeof(struct v4l2_streamparm)); sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; - sp->parm.capture.capturemode = dev->cap_parm[fh->channel].capturemode; - def_num = (fh->mode.format == FORMAT_NTSC) ? 1001 : 1000; - def_dem = (fh->mode.format == FORMAT_NTSC) ? 30000 : 25000; + sp->parm.capture.capturemode = channel->cap_parm.capturemode; + def_num = (channel->mode.format == FORMAT_NTSC) ? 1001 : 1000; + def_dem = (channel->mode.format == FORMAT_NTSC) ? 30000 : 25000; sp->parm.capture.timeperframe.denominator = def_dem; - switch (fh->mode.fdec) { + switch (channel->mode.fdec) { default: case FDEC_1: sp->parm.capture.timeperframe.numerator = def_num; @@ -1630,17 +1632,19 @@ static int vidioc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *sp) { struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; + struct s2255_channel *channel = fh->channel; + struct s2255_mode mode; int fdec = FDEC_1; __u32 def_num, def_dem; if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + mode = channel->mode; /* high quality capture mode requires a stream restart */ - if (dev->cap_parm[fh->channel].capturemode - != sp->parm.capture.capturemode && res_locked(fh->dev, fh)) + if (channel->cap_parm.capturemode + != sp->parm.capture.capturemode && res_locked(fh)) return -EBUSY; - def_num = (fh->mode.format == FORMAT_NTSC) ? 1001 : 1000; - def_dem = (fh->mode.format == FORMAT_NTSC) ? 30000 : 25000; + def_num = (mode.format == FORMAT_NTSC) ? 1001 : 1000; + def_dem = (mode.format == FORMAT_NTSC) ? 30000 : 25000; if (def_dem != sp->parm.capture.timeperframe.denominator) sp->parm.capture.timeperframe.numerator = def_num; else if (sp->parm.capture.timeperframe.numerator <= def_num) @@ -1655,9 +1659,9 @@ static int vidioc_s_parm(struct file *file, void *priv, sp->parm.capture.timeperframe.numerator = def_num * 5; fdec = FDEC_5; } - fh->mode.fdec = fdec; + mode.fdec = fdec; sp->parm.capture.timeperframe.denominator = def_dem; - s2255_set_mode(dev, fh->channel, &fh->mode); + s2255_set_mode(channel, &mode); dprintk(4, "%s capture mode, %d timeperframe %d/%d, fdec %d\n", __func__, sp->parm.capture.capturemode, @@ -1707,24 +1711,13 @@ static int vidioc_enum_frameintervals(struct file *file, void *priv, static int s2255_open(struct file *file) { struct video_device *vdev = video_devdata(file); - struct s2255_dev *dev = video_drvdata(file); + struct s2255_channel *channel = video_drvdata(file); + struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev); struct s2255_fh *fh; enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - int i = 0; - int cur_channel = -1; int state; dprintk(1, "s2255: open called (dev=%s)\n", video_device_node_name(vdev)); - - for (i = 0; i < MAX_CHANNELS; i++) { - if (&dev->vdev[i] == vdev) { - cur_channel = i; - break; - } - } - if (i == MAX_CHANNELS) - return -ENODEV; - /* * open lock necessary to prevent multiple instances * of v4l-conf (or other programs) from simultaneously @@ -1806,24 +1799,20 @@ static int s2255_open(struct file *file) file->private_data = fh; fh->dev = dev; fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fh->mode = dev->mode[cur_channel]; - fh->fmt = dev->cur_fmt[cur_channel]; - /* default 4CIF NTSC */ - fh->width = LINE_SZ_4CIFS_NTSC; - fh->height = NUM_LINES_4CIFS_NTSC * 2; - fh->channel = cur_channel; - /* configure channel to default state */ - if (!dev->chn_configured[cur_channel]) { - s2255_set_mode(dev, cur_channel, &fh->mode); - dev->chn_configured[cur_channel] = 1; + fh->channel = channel; + if (!channel->configured) { + /* configure channel to default state */ + channel->fmt = &formats[0]; + s2255_set_mode(channel, &channel->mode); + channel->configured = 1; } dprintk(1, "%s: dev=%s type=%s\n", __func__, video_device_node_name(vdev), v4l2_type_names[type]); dprintk(2, "%s: fh=0x%08lx, dev=0x%08lx, vidq=0x%08lx\n", __func__, (unsigned long)fh, (unsigned long)dev, - (unsigned long)&dev->vidq[cur_channel]); + (unsigned long)&channel->vidq); dprintk(4, "%s: list_empty active=%d\n", __func__, - list_empty(&dev->vidq[cur_channel].active)); + list_empty(&channel->vidq.active)); videobuf_queue_vmalloc_init(&fh->vb_vidq, &s2255_video_qops, NULL, &dev->slock, fh->type, @@ -1865,6 +1854,7 @@ static void s2255_destroy(struct s2255_dev *dev) mutex_destroy(&dev->open_lock); mutex_destroy(&dev->lock); usb_put_dev(dev->udev); + v4l2_device_unregister(&dev->v4l2_dev); dprintk(1, "%s", __func__); kfree(dev); } @@ -1874,14 +1864,15 @@ static int s2255_release(struct file *file) struct s2255_fh *fh = file->private_data; struct s2255_dev *dev = fh->dev; struct video_device *vdev = video_devdata(file); + struct s2255_channel *channel = fh->channel; if (!dev) return -ENODEV; /* turn off stream */ if (res_check(fh)) { - if (dev->b_acquire[fh->channel]) - s2255_stop_acquire(dev, fh->channel); + if (channel->b_acquire) + s2255_stop_acquire(fh->channel); videobuf_streamoff(&fh->vb_vidq); - res_free(dev, fh); + res_free(fh); } videobuf_mmap_free(&fh->vb_vidq); dprintk(1, "%s (dev=%s)\n", __func__, video_device_node_name(vdev)); @@ -1945,9 +1936,10 @@ static const struct v4l2_ioctl_ops s2255_ioctl_ops = { static void s2255_video_device_release(struct video_device *vdev) { - struct s2255_dev *dev = video_get_drvdata(vdev); - dprintk(4, "%s, chnls: %d \n", __func__, atomic_read(&dev->channels)); - if (atomic_dec_and_test(&dev->channels)) + struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev); + dprintk(4, "%s, chnls: %d \n", __func__, + atomic_read(&dev->num_channels)); + if (atomic_dec_and_test(&dev->num_channels)) s2255_destroy(dev); return; } @@ -1966,47 +1958,48 @@ static int s2255_probe_v4l(struct s2255_dev *dev) int ret; int i; int cur_nr = video_nr; + struct s2255_channel *channel; ret = v4l2_device_register(&dev->interface->dev, &dev->v4l2_dev); if (ret) return ret; /* initialize all video 4 linux */ /* register 4 video devices */ for (i = 0; i < MAX_CHANNELS; i++) { - INIT_LIST_HEAD(&dev->vidq[i].active); - dev->vidq[i].dev = dev; - dev->vidq[i].channel = i; + channel = &dev->channel[i]; + INIT_LIST_HEAD(&channel->vidq.active); + channel->vidq.dev = dev; /* register 4 video devices */ - memcpy(&dev->vdev[i], &template, sizeof(struct video_device)); - dev->vdev[i].v4l2_dev = &dev->v4l2_dev; - video_set_drvdata(&dev->vdev[i], dev); + channel->vdev = template; + channel->vdev.v4l2_dev = &dev->v4l2_dev; + video_set_drvdata(&channel->vdev, channel); if (video_nr == -1) - ret = video_register_device(&dev->vdev[i], + ret = video_register_device(&channel->vdev, VFL_TYPE_GRABBER, video_nr); else - ret = video_register_device(&dev->vdev[i], + ret = video_register_device(&channel->vdev, VFL_TYPE_GRABBER, cur_nr + i); + if (ret) { dev_err(&dev->udev->dev, "failed to register video device!\n"); break; } - atomic_inc(&dev->channels); + atomic_inc(&dev->num_channels); v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n", - video_device_node_name(&dev->vdev[i])); + video_device_node_name(&channel->vdev)); } - printk(KERN_INFO "Sensoray 2255 V4L driver Revision: %d.%d\n", S2255_MAJOR_VERSION, S2255_MINOR_VERSION); /* if no channels registered, return error and probe will fail*/ - if (atomic_read(&dev->channels) == 0) { + if (atomic_read(&dev->num_channels) == 0) { v4l2_device_unregister(&dev->v4l2_dev); return ret; } - if (atomic_read(&dev->channels) != MAX_CHANNELS) + if (atomic_read(&dev->num_channels) != MAX_CHANNELS) printk(KERN_WARNING "s2255: Not all channels available.\n"); return 0; } @@ -2033,12 +2026,11 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) s32 idx = -1; struct s2255_framei *frm; unsigned char *pdata; - + struct s2255_channel *channel; dprintk(100, "buffer to user\n"); - - idx = dev->cur_frame[dev->cc]; - frm = &dev->buffer[dev->cc].frame[idx]; - + channel = &dev->channel[dev->cc]; + idx = channel->cur_frame; + frm = &channel->buffer.frame[idx]; if (frm->ulState == S2255_READ_IDLE) { int jj; unsigned int cc; @@ -2063,16 +2055,18 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) } /* reverse it */ dev->cc = G_chnmap[cc]; + channel = &dev->channel[dev->cc]; payload = pdword[3]; - if (payload > dev->req_image_size[dev->cc]) { - dev->bad_payload[dev->cc]++; + if (payload > channel->req_image_size) { + channel->bad_payload++; /* discard the bad frame */ return -EINVAL; } - dev->pkt_size[dev->cc] = payload; - dev->jpg_size[dev->cc] = pdword[4]; + channel->pkt_size = payload; + channel->jpg_size = pdword[4]; break; case S2255_MARKER_RESPONSE: + pdata += DEF_USB_BLOCK; jj += DEF_USB_BLOCK; if (pdword[1] >= MAX_CHANNELS) @@ -2080,12 +2074,13 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) cc = G_chnmap[pdword[1]]; if (cc >= MAX_CHANNELS) break; + channel = &dev->channel[cc]; switch (pdword[2]) { case S2255_RESPONSE_SETMODE: /* check if channel valid */ /* set mode ready */ - dev->setmode_ready[cc] = 1; - wake_up(&dev->wait_setmode[cc]); + channel->setmode_ready = 1; + wake_up(&channel->wait_setmode); dprintk(5, "setmode ready %d\n", cc); break; case S2255_RESPONSE_FW: @@ -2099,9 +2094,9 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) wake_up(&dev->fw_data->wait_fw); break; case S2255_RESPONSE_STATUS: - dev->vidstatus[cc] = pdword[3]; - dev->vidstatus_ready[cc] = 1; - wake_up(&dev->wait_vidstatus[cc]); + channel->vidstatus = pdword[3]; + channel->vidstatus_ready = 1; + wake_up(&channel->wait_vidstatus); dprintk(5, "got vidstatus %x chan %d\n", pdword[3], cc); break; @@ -2118,13 +2113,11 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) if (!bframe) return -EINVAL; } - - - idx = dev->cur_frame[dev->cc]; - frm = &dev->buffer[dev->cc].frame[idx]; - + channel = &dev->channel[dev->cc]; + idx = channel->cur_frame; + frm = &channel->buffer.frame[idx]; /* search done. now find out if should be acquiring on this channel */ - if (!dev->b_acquire[dev->cc]) { + if (!channel->b_acquire) { /* we found a frame, but this channel is turned off */ frm->ulState = S2255_READ_IDLE; return -EINVAL; @@ -2149,30 +2142,28 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) copy_size = (pipe_info->cur_transfer_size - offset); - size = dev->pkt_size[dev->cc] - PREFIX_SIZE; + size = channel->pkt_size - PREFIX_SIZE; /* sanity check on pdest */ - if ((copy_size + frm->cur_size) < dev->req_image_size[dev->cc]) + if ((copy_size + frm->cur_size) < channel->req_image_size) memcpy(pdest, psrc, copy_size); frm->cur_size += copy_size; dprintk(4, "cur_size size %lu size %lu \n", frm->cur_size, size); if (frm->cur_size >= size) { - - u32 cc = dev->cc; dprintk(2, "****************[%d]Buffer[%d]full*************\n", - cc, idx); - dev->last_frame[cc] = dev->cur_frame[cc]; - dev->cur_frame[cc]++; + dev->cc, idx); + channel->last_frame = channel->cur_frame; + channel->cur_frame++; /* end of system frame ring buffer, start at zero */ - if ((dev->cur_frame[cc] == SYS_FRAMES) || - (dev->cur_frame[cc] == dev->buffer[cc].dwFrames)) - dev->cur_frame[cc] = 0; + if ((channel->cur_frame == SYS_FRAMES) || + (channel->cur_frame == channel->buffer.dwFrames)) + channel->cur_frame = 0; /* frame ready */ - if (dev->b_acquire[cc]) - s2255_got_frame(dev, cc, dev->jpg_size[cc]); - dev->frame_count[cc]++; + if (channel->b_acquire) + s2255_got_frame(channel, channel->jpg_size); + channel->frame_count++; frm->ulState = S2255_READ_IDLE; frm->cur_size = 0; @@ -2245,16 +2236,12 @@ static int s2255_get_fx2fw(struct s2255_dev *dev) * Create the system ring buffer to copy frames into from the * usb read pipe. */ -static int s2255_create_sys_buffers(struct s2255_dev *dev, unsigned long chn) +static int s2255_create_sys_buffers(struct s2255_channel *channel) { unsigned long i; unsigned long reqsize; dprintk(1, "create sys buffers\n"); - if (chn >= MAX_CHANNELS) - return -1; - - dev->buffer[chn].dwFrames = SYS_FRAMES; - + channel->buffer.dwFrames = SYS_FRAMES; /* always allocate maximum size(PAL) for system buffers */ reqsize = SYS_FRAMES_MAXSIZE; @@ -2263,42 +2250,40 @@ static int s2255_create_sys_buffers(struct s2255_dev *dev, unsigned long chn) for (i = 0; i < SYS_FRAMES; i++) { /* allocate the frames */ - dev->buffer[chn].frame[i].lpvbits = vmalloc(reqsize); - - dprintk(1, "valloc %p chan %lu, idx %lu, pdata %p\n", - &dev->buffer[chn].frame[i], chn, i, - dev->buffer[chn].frame[i].lpvbits); - dev->buffer[chn].frame[i].size = reqsize; - if (dev->buffer[chn].frame[i].lpvbits == NULL) { + channel->buffer.frame[i].lpvbits = vmalloc(reqsize); + dprintk(1, "valloc %p chan %d, idx %lu, pdata %p\n", + &channel->buffer.frame[i], channel->idx, i, + channel->buffer.frame[i].lpvbits); + channel->buffer.frame[i].size = reqsize; + if (channel->buffer.frame[i].lpvbits == NULL) { printk(KERN_INFO "out of memory. using less frames\n"); - dev->buffer[chn].dwFrames = i; + channel->buffer.dwFrames = i; break; } } /* make sure internal states are set */ for (i = 0; i < SYS_FRAMES; i++) { - dev->buffer[chn].frame[i].ulState = 0; - dev->buffer[chn].frame[i].cur_size = 0; + channel->buffer.frame[i].ulState = 0; + channel->buffer.frame[i].cur_size = 0; } - dev->cur_frame[chn] = 0; - dev->last_frame[chn] = -1; + channel->cur_frame = 0; + channel->last_frame = -1; return 0; } -static int s2255_release_sys_buffers(struct s2255_dev *dev, - unsigned long channel) +static int s2255_release_sys_buffers(struct s2255_channel *channel) { unsigned long i; dprintk(1, "release sys buffers\n"); for (i = 0; i < SYS_FRAMES; i++) { - if (dev->buffer[channel].frame[i].lpvbits) { + if (channel->buffer.frame[i].lpvbits) { dprintk(1, "vfree %p\n", - dev->buffer[channel].frame[i].lpvbits); - vfree(dev->buffer[channel].frame[i].lpvbits); + channel->buffer.frame[i].lpvbits); + vfree(channel->buffer.frame[i].lpvbits); } - dev->buffer[channel].frame[i].lpvbits = NULL; + channel->buffer.frame[i].lpvbits = NULL; } return 0; } @@ -2335,17 +2320,20 @@ static int s2255_board_init(struct s2255_dev *dev) fw_ver & 0xff); for (j = 0; j < MAX_CHANNELS; j++) { - dev->b_acquire[j] = 0; - dev->mode[j] = mode_def; + struct s2255_channel *channel = &dev->channel[j]; + channel->b_acquire = 0; + channel->mode = mode_def; if (dev->pid == 0x2257 && j > 1) - dev->mode[j].color |= (1 << 16); - dev->jc[j].quality = S2255_DEF_JPEG_QUAL; - dev->cur_fmt[j] = &formats[0]; - dev->mode[j].restart = 1; - dev->req_image_size[j] = get_transfer_size(&mode_def); - dev->frame_count[j] = 0; + channel->mode.color |= (1 << 16); + channel->jc.quality = S2255_DEF_JPEG_QUAL; + channel->width = LINE_SZ_4CIFS_NTSC; + channel->height = NUM_LINES_4CIFS_NTSC * 2; + channel->fmt = &formats[0]; + channel->mode.restart = 1; + channel->req_image_size = get_transfer_size(&mode_def); + channel->frame_count = 0; /* create the system buffers */ - s2255_create_sys_buffers(dev, j); + s2255_create_sys_buffers(channel); } /* start read pipe */ s2255_start_readpipe(dev); @@ -2359,14 +2347,12 @@ static int s2255_board_shutdown(struct s2255_dev *dev) dprintk(1, "%s: dev: %p", __func__, dev); for (i = 0; i < MAX_CHANNELS; i++) { - if (dev->b_acquire[i]) - s2255_stop_acquire(dev, i); + if (dev->channel[i].b_acquire) + s2255_stop_acquire(&dev->channel[i]); } - s2255_stop_readpipe(dev); - for (i = 0; i < MAX_CHANNELS; i++) - s2255_release_sys_buffers(dev, i); + s2255_release_sys_buffers(&dev->channel[i]); /* release transfer buffer */ kfree(dev->pipe.transfer_buffer); return 0; @@ -2459,29 +2445,26 @@ static int s2255_start_readpipe(struct s2255_dev *dev) } /* starts acquisition process */ -static int s2255_start_acquire(struct s2255_dev *dev, unsigned long chn) +static int s2255_start_acquire(struct s2255_channel *channel) { unsigned char *buffer; int res; unsigned long chn_rev; int j; - if (chn >= MAX_CHANNELS) { - dprintk(2, "start acquire failed, bad channel %lu\n", chn); - return -1; - } - chn_rev = G_chnmap[chn]; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); + chn_rev = G_chnmap[channel->idx]; buffer = kzalloc(512, GFP_KERNEL); if (buffer == NULL) { dev_err(&dev->udev->dev, "out of mem\n"); return -ENOMEM; } - dev->last_frame[chn] = -1; - dev->bad_payload[chn] = 0; - dev->cur_frame[chn] = 0; + channel->last_frame = -1; + channel->bad_payload = 0; + channel->cur_frame = 0; for (j = 0; j < SYS_FRAMES; j++) { - dev->buffer[chn].frame[j].ulState = 0; - dev->buffer[chn].frame[j].cur_size = 0; + channel->buffer.frame[j].ulState = 0; + channel->buffer.frame[j].cur_size = 0; } /* send the start command */ @@ -2492,21 +2475,18 @@ static int s2255_start_acquire(struct s2255_dev *dev, unsigned long chn) if (res != 0) dev_err(&dev->udev->dev, "CMD_START error\n"); - dprintk(2, "start acquire exit[%lu] %d \n", chn, res); + dprintk(2, "start acquire exit[%d] %d \n", channel->idx, res); kfree(buffer); return 0; } -static int s2255_stop_acquire(struct s2255_dev *dev, unsigned long chn) +static int s2255_stop_acquire(struct s2255_channel *channel) { unsigned char *buffer; int res; unsigned long chn_rev; - if (chn >= MAX_CHANNELS) { - dprintk(2, "stop acquire failed, bad channel %lu\n", chn); - return -1; - } - chn_rev = G_chnmap[chn]; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); + chn_rev = G_chnmap[channel->idx]; buffer = kzalloc(512, GFP_KERNEL); if (buffer == NULL) { dev_err(&dev->udev->dev, "out of mem\n"); @@ -2520,8 +2500,8 @@ static int s2255_stop_acquire(struct s2255_dev *dev, unsigned long chn) if (res != 0) dev_err(&dev->udev->dev, "CMD_STOP error\n"); kfree(buffer); - dev->b_acquire[chn] = 0; - dprintk(4, "%s: chn %lu, res %d\n", __func__, chn, res); + channel->b_acquire = 0; + dprintk(4, "%s: chn %d, res %d\n", __func__, channel->idx, res); return res; } @@ -2575,7 +2555,7 @@ static int s2255_probe(struct usb_interface *interface, s2255_dev_err(&interface->dev, "out of memory\n"); return -ENOMEM; } - atomic_set(&dev->channels, 0); + atomic_set(&dev->num_channels, 0); dev->pid = id->idProduct; dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL); if (!dev->fw_data) @@ -2612,8 +2592,10 @@ static int s2255_probe(struct usb_interface *interface, dev->timer.data = (unsigned long)dev->fw_data; init_waitqueue_head(&dev->fw_data->wait_fw); for (i = 0; i < MAX_CHANNELS; i++) { - init_waitqueue_head(&dev->wait_setmode[i]); - init_waitqueue_head(&dev->wait_vidstatus[i]); + struct s2255_channel *channel = &dev->channel[i]; + dev->channel[i].idx = i; + init_waitqueue_head(&channel->wait_setmode); + init_waitqueue_head(&channel->wait_vidstatus); } dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL); @@ -2651,7 +2633,7 @@ static int s2255_probe(struct usb_interface *interface, printk(KERN_INFO "s2255: f2255usb.bin out of date.\n"); if (dev->pid == 0x2257 && *pRel < S2255_MIN_DSP_COLORFILTER) printk(KERN_WARNING "s2255: 2257 requires firmware %d" - "or above.\n", S2255_MIN_DSP_COLORFILTER); + " or above.\n", S2255_MIN_DSP_COLORFILTER); } usb_reset_device(dev->udev); /* load 2255 board specific */ @@ -2693,25 +2675,23 @@ static void s2255_disconnect(struct usb_interface *interface) { struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface)); int i; - int channels = atomic_read(&dev->channels); - v4l2_device_unregister(&dev->v4l2_dev); + int channels = atomic_read(&dev->num_channels); + v4l2_device_disconnect(&dev->v4l2_dev); /*see comments in the uvc_driver.c usb disconnect function */ - atomic_inc(&dev->channels); + atomic_inc(&dev->num_channels); /* unregister each video device. */ - for (i = 0; i < channels; i++) { - if (video_is_registered(&dev->vdev[i])) - video_unregister_device(&dev->vdev[i]); - } + for (i = 0; i < channels; i++) + video_unregister_device(&dev->channel[i].vdev); /* wake up any of our timers */ atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING); wake_up(&dev->fw_data->wait_fw); for (i = 0; i < MAX_CHANNELS; i++) { - dev->setmode_ready[i] = 1; - wake_up(&dev->wait_setmode[i]); - dev->vidstatus_ready[i] = 1; - wake_up(&dev->wait_vidstatus[i]); + dev->channel[i].setmode_ready = 1; + wake_up(&dev->channel[i].wait_setmode); + dev->channel[i].vidstatus_ready = 1; + wake_up(&dev->channel[i].wait_vidstatus); } - if (atomic_dec_and_test(&dev->channels)) + if (atomic_dec_and_test(&dev->num_channels)) s2255_destroy(dev); dev_info(&interface->dev, "%s\n", __func__); } diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c index d3bd82a..10460fd 100644 --- a/drivers/media/video/saa7134/saa7134-alsa.c +++ b/drivers/media/video/saa7134/saa7134-alsa.c @@ -630,7 +630,7 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, /* release the old buffer */ if (substream->runtime->dma_area) { saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); - videobuf_sg_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); + videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); dsp_buffer_free(dev); substream->runtime->dma_area = NULL; } @@ -646,12 +646,12 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, return err; } - if (0 != (err = videobuf_sg_dma_map(&dev->pci->dev, &dev->dmasound.dma))) { + if (0 != (err = videobuf_dma_map(&dev->pci->dev, &dev->dmasound.dma))) { dsp_buffer_free(dev); return err; } if (0 != (err = saa7134_pgtable_alloc(dev->pci,&dev->dmasound.pt))) { - videobuf_sg_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); + videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); dsp_buffer_free(dev); return err; } @@ -660,7 +660,7 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, dev->dmasound.dma.sglen, 0))) { saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); - videobuf_sg_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); + videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); dsp_buffer_free(dev); return err; } @@ -669,7 +669,7 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, byte, but it doesn't work. So I allocate the DMA using the V4L functions, and force ALSA to use that as the DMA area */ - substream->runtime->dma_area = dev->dmasound.dma.vmalloc; + substream->runtime->dma_area = dev->dmasound.dma.vaddr; substream->runtime->dma_bytes = dev->dmasound.bufsize; substream->runtime->dma_addr = 0; @@ -696,7 +696,7 @@ static int snd_card_saa7134_hw_free(struct snd_pcm_substream * substream) if (substream->runtime->dma_area) { saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); - videobuf_sg_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); + videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); dsp_buffer_free(dev); substream->runtime->dma_area = NULL; } @@ -1080,7 +1080,7 @@ static int alsa_card_saa7134_create(struct saa7134_dev *dev, int devnum) /* Card "creation" */ card->private_free = snd_saa7134_free; - chip = (snd_card_saa7134_t *) card->private_data; + chip = card->private_data; spin_lock_init(&chip->lock); spin_lock_init(&chip->mixer_lock); diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 07f6bb8..ec697fc 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -5462,6 +5462,30 @@ struct saa7134_board saa7134_boards[] = { .amux = TV, }, }, + [SAA7134_BOARD_TECHNOTREND_BUDGET_T3000] = { + .name = "TechoTrend TT-budget T-3000", + .tuner_type = TUNER_PHILIPS_TD1316, + .audio_clock = 0x00187de7, + .radio_type = UNSET, + .tuner_addr = 0x63, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + }, }; @@ -6631,6 +6655,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0x6655, .driver_data = SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S, }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x13c2, + .subdevice = 0x2804, + .driver_data = SAA7134_BOARD_TECHNOTREND_BUDGET_T3000, + }, { /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -7320,6 +7350,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) case SAA7134_BOARD_VIDEOMATE_DVBT_300: case SAA7134_BOARD_ASUS_EUROPA2_HYBRID: case SAA7134_BOARD_ASUS_EUROPA_HYBRID: + case SAA7134_BOARD_TECHNOTREND_BUDGET_T3000: { /* The Philips EUROPA based hybrid boards have the tuner diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 90f2318..40bc635 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -256,7 +256,7 @@ void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf) BUG_ON(in_interrupt()); videobuf_waiton(&buf->vb,0,0); - videobuf_dma_unmap(q, dma); + videobuf_dma_unmap(q->dev, dma); videobuf_dma_free(dma); buf->vb.state = VIDEOBUF_NEEDS_INIT; } diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 31e82be..f26fe76 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -481,6 +481,17 @@ static struct tda1004x_config medion_cardbus = { .request_firmware = philips_tda1004x_request_firmware }; +static struct tda1004x_config technotrend_budget_t3000_config = { + .demod_address = 0x8, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_4M, + .agc_config = TDA10046_AGC_DEFAULT, + .if_freq = TDA10046_FREQ_3617, + .tuner_address = 0x63, + .request_firmware = philips_tda1004x_request_firmware +}; + /* ------------------------------------------------------------------ * tda 1004x based cards with philips silicon tuner */ @@ -1168,6 +1179,18 @@ static int dvb_init(struct saa7134_dev *dev) fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params; } break; + case SAA7134_BOARD_TECHNOTREND_BUDGET_T3000: + fe0->dvb.frontend = dvb_attach(tda10046_attach, + &technotrend_budget_t3000_config, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + dev->original_demod_sleep = fe0->dvb.frontend->ops.sleep; + fe0->dvb.frontend->ops.sleep = philips_europa_demod_sleep; + fe0->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init; + fe0->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep; + fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params; + } + break; case SAA7134_BOARD_VIDEOMATE_DVBT_200: fe0->dvb.frontend = dvb_attach(tda10046_attach, &philips_tu1216_61_config, diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 756a1ca..c040a18 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -304,6 +304,7 @@ struct saa7134_format { #define SAA7134_BOARD_BEHOLD_H7 178 #define SAA7134_BOARD_BEHOLD_A7 179 #define SAA7134_BOARD_AVERMEDIA_M733A 180 +#define SAA7134_BOARD_TECHNOTREND_BUDGET_T3000 181 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 961bfa2..2b24bd0 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -633,6 +633,12 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) cdwdr_width *= 2; } + /* CSI2 special configuration */ + if (pcdev->pdata->csi2_dev) { + in_width = ((in_width - 2) * 2); + left_offset *= 2; + } + /* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */ camor = left_offset | (top_offset << 16); @@ -743,16 +749,16 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: switch (cam->code) { - case V4L2_MBUS_FMT_YUYV8_2X8_BE: + case V4L2_MBUS_FMT_UYVY8_2X8: value = 0x00000000; /* Cb0, Y0, Cr0, Y1 */ break; - case V4L2_MBUS_FMT_YVYU8_2X8_BE: + case V4L2_MBUS_FMT_VYUY8_2X8: value = 0x00000100; /* Cr0, Y0, Cb0, Y1 */ break; - case V4L2_MBUS_FMT_YUYV8_2X8_LE: + case V4L2_MBUS_FMT_YUYV8_2X8: value = 0x00000200; /* Y0, Cb0, Y1, Cr0 */ break; - case V4L2_MBUS_FMT_YVYU8_2X8_LE: + case V4L2_MBUS_FMT_YVYU8_2X8: value = 0x00000300; /* Y0, Cr0, Y1, Cb0 */ break; default: @@ -767,6 +773,11 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, value |= common_flags & SOCAM_VSYNC_ACTIVE_LOW ? 1 << 1 : 0; value |= common_flags & SOCAM_HSYNC_ACTIVE_LOW ? 1 << 0 : 0; value |= pcdev->is_16bit ? 1 << 12 : 0; + + /* CSI2 mode */ + if (pcdev->pdata->csi2_dev) + value |= 3 << 12; + ceu_write(pcdev, CAMCR, value); ceu_write(pcdev, CAPCR, 0x00300000); @@ -883,6 +894,8 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct device *dev = icd->dev.parent; + struct soc_camera_host *ici = to_soc_camera_host(dev); + struct sh_mobile_ceu_dev *pcdev = ici->priv; int ret, k, n; int formats = 0; struct sh_mobile_ceu_cam *cam; @@ -896,19 +909,19 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int fmt = soc_mbus_get_fmtdesc(code); if (!fmt) { - dev_err(icd->dev.parent, - "Invalid format code #%u: %d\n", idx, code); + dev_err(dev, "Invalid format code #%u: %d\n", idx, code); return -EINVAL; } - ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample); - if (ret < 0) - return 0; + if (!pcdev->pdata->csi2_dev) { + ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample); + if (ret < 0) + return 0; + } if (!icd->host_priv) { struct v4l2_mbus_framefmt mf; struct v4l2_rect rect; - struct device *dev = icd->dev.parent; int shift = 0; /* FIXME: subwindow is lost between close / open */ @@ -927,7 +940,8 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int /* Try 2560x1920, 1280x960, 640x480, 320x240 */ mf.width = 2560 >> shift; mf.height = 1920 >> shift; - ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf); + ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video, + s_mbus_fmt, &mf); if (ret < 0) return ret; shift++; @@ -965,10 +979,10 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int cam->extra_fmt = NULL; switch (code) { - case V4L2_MBUS_FMT_YUYV8_2X8_BE: - case V4L2_MBUS_FMT_YVYU8_2X8_BE: - case V4L2_MBUS_FMT_YUYV8_2X8_LE: - case V4L2_MBUS_FMT_YVYU8_2X8_LE: + case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_VYUY8_2X8: + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_YVYU8_2X8: if (cam->extra_fmt) break; @@ -1005,7 +1019,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int xlate->code = code; xlate++; dev_dbg(dev, "Providing format %s in pass-through mode\n", - xlate->host_fmt->name); + fmt->name); } return formats; @@ -1228,7 +1242,8 @@ static int client_s_fmt(struct soc_camera_device *icd, struct v4l2_cropcap cap; int ret; - ret = v4l2_subdev_call(sd, video, s_mbus_fmt, mf); + ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video, + s_mbus_fmt, mf); if (ret < 0) return ret; @@ -1257,7 +1272,8 @@ static int client_s_fmt(struct soc_camera_device *icd, tmp_h = min(2 * tmp_h, max_height); mf->width = tmp_w; mf->height = tmp_h; - ret = v4l2_subdev_call(sd, video, s_mbus_fmt, mf); + ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video, + s_mbus_fmt, mf); dev_geo(dev, "Camera scaled to %ux%u\n", mf->width, mf->height); if (ret < 0) { @@ -1514,7 +1530,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, struct device *dev = icd->dev.parent; __u32 pixfmt = pix->pixelformat; const struct soc_camera_format_xlate *xlate; - unsigned int ceu_sub_width, ceu_sub_height; + /* Keep Compiler Happy */ + unsigned int ceu_sub_width = 0, ceu_sub_height = 0; u16 scale_v, scale_h; int ret; bool image_mode; @@ -1569,8 +1586,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, /* Done with the camera. Now see if we can improve the result */ - dev_geo(dev, "Camera %d fmt %ux%u, requested %ux%u\n", - ret, mf.width, mf.height, pix->width, pix->height); + dev_geo(dev, "fmt %ux%u, requested %ux%u\n", + mf.width, mf.height, pix->width, pix->height); if (ret < 0) return ret; @@ -1634,6 +1651,9 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, int width, height; int ret; + dev_geo(icd->dev.parent, "TRY_FMT(pix=0x%x, %ux%u)\n", + pixfmt, pix->width, pix->height); + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (!xlate) { dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt); @@ -1660,7 +1680,7 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, mf.code = xlate->code; mf.colorspace = pix->colorspace; - ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf); + ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video, try_mbus_fmt, &mf); if (ret < 0) return ret; @@ -1684,7 +1704,8 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, */ mf.width = 2560; mf.height = 1920; - ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf); + ret = v4l2_device_call_until_err(sd->v4l2_dev, 0, video, + try_mbus_fmt, &mf); if (ret < 0) { /* Shouldn't actually happen... */ dev_err(icd->dev.parent, @@ -1699,6 +1720,9 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, pix->height = height; } + dev_geo(icd->dev.parent, "%s(): return %d, fmt 0x%x, %ux%u\n", + __func__, ret, pix->pixelformat, pix->width, pix->height); + return ret; } @@ -1853,6 +1877,30 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .num_controls = ARRAY_SIZE(sh_mobile_ceu_controls), }; +struct bus_wait { + struct notifier_block notifier; + struct completion completion; + struct device *dev; +}; + +static int bus_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct device *dev = data; + struct bus_wait *wait = container_of(nb, struct bus_wait, notifier); + + if (wait->dev != dev) + return NOTIFY_DONE; + + switch (action) { + case BUS_NOTIFY_UNBOUND_DRIVER: + /* Protect from module unloading */ + wait_for_completion(&wait->completion); + return NOTIFY_OK; + } + return NOTIFY_DONE; +} + static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) { struct sh_mobile_ceu_dev *pcdev; @@ -1860,6 +1908,11 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) void __iomem *base; unsigned int irq; int err = 0; + struct bus_wait wait = { + .completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion), + .notifier.notifier_call = bus_notify, + }; + struct device *csi2; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); @@ -1931,12 +1984,54 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) pcdev->ici.drv_name = dev_name(&pdev->dev); pcdev->ici.ops = &sh_mobile_ceu_host_ops; + /* CSI2 interfacing */ + csi2 = pcdev->pdata->csi2_dev; + if (csi2) { + wait.dev = csi2; + + err = bus_register_notifier(&platform_bus_type, &wait.notifier); + if (err < 0) + goto exit_free_clk; + + /* + * From this point the driver module will not unload, until + * we complete the completion. + */ + + if (!csi2->driver || !csi2->driver->owner) { + complete(&wait.completion); + /* Either too late, or probing failed */ + bus_unregister_notifier(&platform_bus_type, &wait.notifier); + err = -ENXIO; + goto exit_free_clk; + } + + /* + * The module is still loaded, in the worst case it is hanging + * in device release on our completion. So, _now_ dereferencing + * the "owner" is safe! + */ + + err = try_module_get(csi2->driver->owner); + + /* Let notifier complete, if it has been locked */ + complete(&wait.completion); + bus_unregister_notifier(&platform_bus_type, &wait.notifier); + if (!err) { + err = -ENODEV; + goto exit_free_clk; + } + } + err = soc_camera_host_register(&pcdev->ici); if (err) - goto exit_free_clk; + goto exit_module_put; return 0; +exit_module_put: + if (csi2 && csi2->driver) + module_put(csi2->driver->owner); exit_free_clk: pm_runtime_disable(&pdev->dev); free_irq(pcdev->irq, pcdev); @@ -1956,6 +2051,7 @@ static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev) struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); struct sh_mobile_ceu_dev *pcdev = container_of(soc_host, struct sh_mobile_ceu_dev, ici); + struct device *csi2 = pcdev->pdata->csi2_dev; soc_camera_host_unregister(soc_host); pm_runtime_disable(&pdev->dev); @@ -1963,7 +2059,10 @@ static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev) if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) dma_release_declared_memory(&pdev->dev); iounmap(pcdev->base); + if (csi2 && csi2->driver) + module_put(csi2->driver->owner); kfree(pcdev); + return 0; } @@ -1995,6 +2094,8 @@ static struct platform_driver sh_mobile_ceu_driver = { static int __init sh_mobile_ceu_init(void) { + /* Whatever return code */ + request_module("sh_mobile_csi2"); return platform_driver_register(&sh_mobile_ceu_driver); } diff --git a/drivers/media/video/sh_mobile_csi2.c b/drivers/media/video/sh_mobile_csi2.c new file mode 100644 index 0000000..84a6468 --- /dev/null +++ b/drivers/media/video/sh_mobile_csi2.c @@ -0,0 +1,354 @@ +/* + * Driver for the SH-Mobile MIPI CSI-2 unit + * + * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * 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/i2c.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/videodev2.h> + +#include <media/sh_mobile_csi2.h> +#include <media/soc_camera.h> +#include <media/v4l2-common.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/v4l2-mediabus.h> +#include <media/v4l2-subdev.h> + +#define SH_CSI2_TREF 0x00 +#define SH_CSI2_SRST 0x04 +#define SH_CSI2_PHYCNT 0x08 +#define SH_CSI2_CHKSUM 0x0C +#define SH_CSI2_VCDT 0x10 + +struct sh_csi2 { + struct v4l2_subdev subdev; + struct list_head list; + struct notifier_block notifier; + unsigned int irq; + void __iomem *base; + struct platform_device *pdev; + struct sh_csi2_client_config *client; +}; + +static int sh_csi2_try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev); + struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; + + if (mf->width > 8188) + mf->width = 8188; + else if (mf->width & 1) + mf->width &= ~1; + + switch (pdata->type) { + case SH_CSI2C: + switch (mf->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: /* YUV422 */ + case V4L2_MBUS_FMT_YUYV8_1_5X8: /* YUV420 */ + case V4L2_MBUS_FMT_GREY8_1X8: /* RAW8 */ + case V4L2_MBUS_FMT_SBGGR8_1X8: + case V4L2_MBUS_FMT_SGRBG8_1X8: + break; + default: + /* All MIPI CSI-2 devices must support one of primary formats */ + mf->code = V4L2_MBUS_FMT_YUYV8_2X8; + } + break; + case SH_CSI2I: + switch (mf->code) { + case V4L2_MBUS_FMT_GREY8_1X8: /* RAW8 */ + case V4L2_MBUS_FMT_SBGGR8_1X8: + case V4L2_MBUS_FMT_SGRBG8_1X8: + case V4L2_MBUS_FMT_SBGGR10_1X10: /* RAW10 */ + case V4L2_MBUS_FMT_SBGGR12_1X12: /* RAW12 */ + break; + default: + /* All MIPI CSI-2 devices must support one of primary formats */ + mf->code = V4L2_MBUS_FMT_SBGGR8_1X8; + } + break; + } + + return 0; +} + +/* + * We have done our best in try_fmt to try and tell the sensor, which formats + * we support. If now the configuration is unsuitable for us we can only + * error out. + */ +static int sh_csi2_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev); + u32 tmp = (priv->client->channel & 3) << 8; + + dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code); + if (mf->width > 8188 || mf->width & 1) + return -EINVAL; + + switch (mf->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + tmp |= 0x1e; /* YUV422 8 bit */ + break; + case V4L2_MBUS_FMT_YUYV8_1_5X8: + tmp |= 0x18; /* YUV420 8 bit */ + break; + case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE: + tmp |= 0x21; /* RGB555 */ + break; + case V4L2_MBUS_FMT_RGB565_2X8_BE: + tmp |= 0x22; /* RGB565 */ + break; + case V4L2_MBUS_FMT_GREY8_1X8: + case V4L2_MBUS_FMT_SBGGR8_1X8: + case V4L2_MBUS_FMT_SGRBG8_1X8: + tmp |= 0x2a; /* RAW8 */ + break; + default: + return -EINVAL; + } + + iowrite32(tmp, priv->base + SH_CSI2_VCDT); + + return 0; +} + +static struct v4l2_subdev_video_ops sh_csi2_subdev_video_ops = { + .s_mbus_fmt = sh_csi2_s_fmt, + .try_mbus_fmt = sh_csi2_try_fmt, +}; + +static struct v4l2_subdev_core_ops sh_csi2_subdev_core_ops; + +static struct v4l2_subdev_ops sh_csi2_subdev_ops = { + .core = &sh_csi2_subdev_core_ops, + .video = &sh_csi2_subdev_video_ops, +}; + +static void sh_csi2_hwinit(struct sh_csi2 *priv) +{ + struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; + __u32 tmp = 0x10; /* Enable MIPI CSI clock lane */ + + /* Reflect registers immediately */ + iowrite32(0x00000001, priv->base + SH_CSI2_TREF); + /* reset CSI2 harware */ + iowrite32(0x00000001, priv->base + SH_CSI2_SRST); + udelay(5); + iowrite32(0x00000000, priv->base + SH_CSI2_SRST); + + if (priv->client->lanes & 3) + tmp |= priv->client->lanes & 3; + else + /* Default - both lanes */ + tmp |= 3; + + if (priv->client->phy == SH_CSI2_PHY_MAIN) + tmp |= 0x8000; + + iowrite32(tmp, priv->base + SH_CSI2_PHYCNT); + + tmp = 0; + if (pdata->flags & SH_CSI2_ECC) + tmp |= 2; + if (pdata->flags & SH_CSI2_CRC) + tmp |= 1; + iowrite32(tmp, priv->base + SH_CSI2_CHKSUM); +} + +static int sh_csi2_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + return 0; +} + +static unsigned long sh_csi2_query_bus_param(struct soc_camera_device *icd) +{ + struct soc_camera_link *icl = to_soc_camera_link(icd); + const unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | + SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | + SOCAM_MASTER | SOCAM_DATAWIDTH_8 | SOCAM_DATA_ACTIVE_HIGH; + + return soc_camera_apply_sensor_flags(icl, flags); +} + +static int sh_csi2_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct device *dev = data; + struct soc_camera_device *icd = to_soc_camera_dev(dev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev->parent); + struct sh_csi2 *priv = + container_of(nb, struct sh_csi2, notifier); + struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; + int ret, i; + + for (i = 0; i < pdata->num_clients; i++) + if (&pdata->clients[i].pdev->dev == icd->pdev) + break; + + dev_dbg(dev, "%s(%p): action = %lu, found #%d\n", __func__, dev, action, i); + + if (i == pdata->num_clients) + return NOTIFY_DONE; + + switch (action) { + case BUS_NOTIFY_BOUND_DRIVER: + snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s%s", + dev_name(v4l2_dev->dev), ".mipi-csi"); + ret = v4l2_device_register_subdev(v4l2_dev, &priv->subdev); + dev_dbg(dev, "%s(%p): ret(register_subdev) = %d\n", __func__, priv, ret); + if (ret < 0) + return NOTIFY_DONE; + + priv->client = pdata->clients + i; + + icd->ops->set_bus_param = sh_csi2_set_bus_param; + icd->ops->query_bus_param = sh_csi2_query_bus_param; + + pm_runtime_get_sync(v4l2_get_subdevdata(&priv->subdev)); + + sh_csi2_hwinit(priv); + break; + case BUS_NOTIFY_UNBIND_DRIVER: + priv->client = NULL; + + /* Driver is about to be unbound */ + icd->ops->set_bus_param = NULL; + icd->ops->query_bus_param = NULL; + + v4l2_device_unregister_subdev(&priv->subdev); + + pm_runtime_put(v4l2_get_subdevdata(&priv->subdev)); + break; + } + + return NOTIFY_OK; +} + +static __devinit int sh_csi2_probe(struct platform_device *pdev) +{ + struct resource *res; + unsigned int irq; + int ret; + struct sh_csi2 *priv; + /* Platform data specify the PHY, lanes, ECC, CRC */ + struct sh_csi2_pdata *pdata = pdev->dev.platform_data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + /* Interrupt unused so far */ + irq = platform_get_irq(pdev, 0); + + if (!res || (int)irq <= 0 || !pdata) { + dev_err(&pdev->dev, "Not enough CSI2 platform resources.\n"); + return -ENODEV; + } + + /* TODO: Add support for CSI2I. Careful: different register layout! */ + if (pdata->type != SH_CSI2C) { + dev_err(&pdev->dev, "Only CSI2C supported ATM.\n"); + return -EINVAL; + } + + priv = kzalloc(sizeof(struct sh_csi2), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->irq = irq; + priv->notifier.notifier_call = sh_csi2_notify; + + /* We MUST attach after the MIPI sensor */ + ret = bus_register_notifier(&soc_camera_bus_type, &priv->notifier); + if (ret < 0) { + dev_err(&pdev->dev, "CSI2 cannot register notifier\n"); + goto ernotify; + } + + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "CSI2 register region already claimed\n"); + ret = -EBUSY; + goto ereqreg; + } + + priv->base = ioremap(res->start, resource_size(res)); + if (!priv->base) { + ret = -ENXIO; + dev_err(&pdev->dev, "Unable to ioremap CSI2 registers.\n"); + goto eremap; + } + + priv->pdev = pdev; + + v4l2_subdev_init(&priv->subdev, &sh_csi2_subdev_ops); + v4l2_set_subdevdata(&priv->subdev, &pdev->dev); + + platform_set_drvdata(pdev, priv); + + pm_runtime_enable(&pdev->dev); + + dev_dbg(&pdev->dev, "CSI2 probed.\n"); + + return 0; + +eremap: + release_mem_region(res->start, resource_size(res)); +ereqreg: + bus_unregister_notifier(&soc_camera_bus_type, &priv->notifier); +ernotify: + kfree(priv); + + return ret; +} + +static __devexit int sh_csi2_remove(struct platform_device *pdev) +{ + struct sh_csi2 *priv = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + bus_unregister_notifier(&soc_camera_bus_type, &priv->notifier); + pm_runtime_disable(&pdev->dev); + iounmap(priv->base); + release_mem_region(res->start, resource_size(res)); + platform_set_drvdata(pdev, NULL); + kfree(priv); + + return 0; +} + +static struct platform_driver __refdata sh_csi2_pdrv = { + .remove = __devexit_p(sh_csi2_remove), + .driver = { + .name = "sh-mobile-csi2", + .owner = THIS_MODULE, + }, +}; + +static int __init sh_csi2_init(void) +{ + return platform_driver_probe(&sh_csi2_pdrv, sh_csi2_probe); +} + +static void __exit sh_csi2_exit(void) +{ + platform_driver_unregister(&sh_csi2_pdrv); +} + +module_init(sh_csi2_init); +module_exit(sh_csi2_exit); + +MODULE_DESCRIPTION("SH-Mobile MIPI CSI-2 driver"); +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sh-mobile-csi2"); diff --git a/drivers/media/video/sh_vou.c b/drivers/media/video/sh_vou.c index f5b892a..d394187 100644 --- a/drivers/media/video/sh_vou.c +++ b/drivers/media/video/sh_vou.c @@ -18,6 +18,7 @@ #include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/slab.h> #include <linux/version.h> #include <linux/videodev2.h> @@ -57,7 +58,7 @@ enum sh_vou_status { }; #define VOU_MAX_IMAGE_WIDTH 720 -#define VOU_MAX_IMAGE_HEIGHT 480 +#define VOU_MAX_IMAGE_HEIGHT 576 struct sh_vou_device { struct v4l2_device v4l2_dev; @@ -527,20 +528,17 @@ struct sh_vou_geometry { static void vou_adjust_input(struct sh_vou_geometry *geo, v4l2_std_id std) { /* The compiler cannot know, that best and idx will indeed be set */ - unsigned int best_err = UINT_MAX, best = 0, width_max, height_max; + unsigned int best_err = UINT_MAX, best = 0, img_height_max; int i, idx = 0; - if (std & V4L2_STD_525_60) { - width_max = 858; - height_max = 262; - } else { - width_max = 864; - height_max = 312; - } + if (std & V4L2_STD_525_60) + img_height_max = 480; + else + img_height_max = 576; /* Image width must be a multiple of 4 */ v4l_bound_align_image(&geo->in_width, 0, VOU_MAX_IMAGE_WIDTH, 2, - &geo->in_height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0); + &geo->in_height, 0, img_height_max, 1, 0); /* Select scales to come as close as possible to the output image */ for (i = ARRAY_SIZE(vou_scale_h_num) - 1; i >= 0; i--) { @@ -573,7 +571,7 @@ static void vou_adjust_input(struct sh_vou_geometry *geo, v4l2_std_id std) unsigned int found = geo->output.height * vou_scale_v_den[i] / vou_scale_v_num[i]; - if (found > VOU_MAX_IMAGE_HEIGHT) + if (found > img_height_max) /* scales increase */ break; @@ -597,15 +595,18 @@ static void vou_adjust_input(struct sh_vou_geometry *geo, v4l2_std_id std) */ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std) { - unsigned int best_err = UINT_MAX, best, width_max, height_max; + unsigned int best_err = UINT_MAX, best, width_max, height_max, + img_height_max; int i, idx; if (std & V4L2_STD_525_60) { width_max = 858; height_max = 262 * 2; + img_height_max = 480; } else { width_max = 864; height_max = 312 * 2; + img_height_max = 576; } /* Select scales to come as close as possible to the output image */ @@ -644,7 +645,7 @@ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std) unsigned int found = geo->in_height * vou_scale_v_num[i] / vou_scale_v_den[i]; - if (found > VOU_MAX_IMAGE_HEIGHT) + if (found > img_height_max) /* scales increase */ break; @@ -673,11 +674,12 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, struct video_device *vdev = video_devdata(file); struct sh_vou_device *vou_dev = video_get_drvdata(vdev); struct v4l2_pix_format *pix = &fmt->fmt.pix; + unsigned int img_height_max; int pix_idx; struct sh_vou_geometry geo; struct v4l2_mbus_framefmt mbfmt = { /* Revisit: is this the correct code? */ - .code = V4L2_MBUS_FMT_YUYV8_2X8_LE, + .code = V4L2_MBUS_FMT_YUYV8_2X8, .field = V4L2_FIELD_INTERLACED, .colorspace = V4L2_COLORSPACE_SMPTE170M, }; @@ -701,9 +703,14 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, if (pix_idx == ARRAY_SIZE(vou_fmt)) return -EINVAL; + if (vou_dev->std & V4L2_STD_525_60) + img_height_max = 480; + else + img_height_max = 576; + /* Image width must be a multiple of 4 */ v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 2, - &pix->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0); + &pix->height, 0, img_height_max, 1, 0); geo.in_width = pix->width; geo.in_height = pix->height; @@ -724,8 +731,8 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, /* Sanity checks */ if ((unsigned)mbfmt.width > VOU_MAX_IMAGE_WIDTH || - (unsigned)mbfmt.height > VOU_MAX_IMAGE_HEIGHT || - mbfmt.code != V4L2_MBUS_FMT_YUYV8_2X8_LE) + (unsigned)mbfmt.height > img_height_max || + mbfmt.code != V4L2_MBUS_FMT_YUYV8_2X8) return -EIO; if (mbfmt.width != geo.output.width || @@ -936,10 +943,11 @@ static int sh_vou_s_crop(struct file *file, void *fh, struct v4l2_crop *a) struct sh_vou_geometry geo; struct v4l2_mbus_framefmt mbfmt = { /* Revisit: is this the correct code? */ - .code = V4L2_MBUS_FMT_YUYV8_2X8_LE, + .code = V4L2_MBUS_FMT_YUYV8_2X8, .field = V4L2_FIELD_INTERLACED, .colorspace = V4L2_COLORSPACE_SMPTE170M, }; + unsigned int img_height_max; int ret; dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u@%u:%u\n", __func__, @@ -948,14 +956,19 @@ static int sh_vou_s_crop(struct file *file, void *fh, struct v4l2_crop *a) if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) return -EINVAL; + if (vou_dev->std & V4L2_STD_525_60) + img_height_max = 480; + else + img_height_max = 576; + v4l_bound_align_image(&rect->width, 0, VOU_MAX_IMAGE_WIDTH, 1, - &rect->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0); + &rect->height, 0, img_height_max, 1, 0); if (rect->width + rect->left > VOU_MAX_IMAGE_WIDTH) rect->left = VOU_MAX_IMAGE_WIDTH - rect->width; - if (rect->height + rect->top > VOU_MAX_IMAGE_HEIGHT) - rect->top = VOU_MAX_IMAGE_HEIGHT - rect->height; + if (rect->height + rect->top > img_height_max) + rect->top = img_height_max - rect->height; geo.output = *rect; geo.in_width = pix->width; @@ -980,8 +993,8 @@ static int sh_vou_s_crop(struct file *file, void *fh, struct v4l2_crop *a) /* Sanity checks */ if ((unsigned)mbfmt.width > VOU_MAX_IMAGE_WIDTH || - (unsigned)mbfmt.height > VOU_MAX_IMAGE_HEIGHT || - mbfmt.code != V4L2_MBUS_FMT_YUYV8_2X8_LE) + (unsigned)mbfmt.height > img_height_max || + mbfmt.code != V4L2_MBUS_FMT_YUYV8_2X8) return -EIO; geo.output.width = mbfmt.width; @@ -1329,13 +1342,13 @@ static int __devinit sh_vou_probe(struct platform_device *pdev) rect->left = 0; rect->top = 0; rect->width = VOU_MAX_IMAGE_WIDTH; - rect->height = VOU_MAX_IMAGE_HEIGHT; + rect->height = 480; pix->width = VOU_MAX_IMAGE_WIDTH; - pix->height = VOU_MAX_IMAGE_HEIGHT; + pix->height = 480; pix->pixelformat = V4L2_PIX_FMT_YVYU; pix->field = V4L2_FIELD_NONE; pix->bytesperline = VOU_MAX_IMAGE_WIDTH * 2; - pix->sizeimage = VOU_MAX_IMAGE_WIDTH * 2 * VOU_MAX_IMAGE_HEIGHT; + pix->sizeimage = VOU_MAX_IMAGE_WIDTH * 2 * 480; pix->colorspace = V4L2_COLORSPACE_SMPTE170M; region = request_mem_region(reg_res->start, resource_size(reg_res), diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 475757b..f203293 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -1107,13 +1107,14 @@ static int soc_camera_resume(struct device *dev) return ret; } -static struct bus_type soc_camera_bus_type = { +struct bus_type soc_camera_bus_type = { .name = "soc-camera", .probe = soc_camera_probe, .remove = soc_camera_remove, .suspend = soc_camera_suspend, .resume = soc_camera_resume, }; +EXPORT_SYMBOL_GPL(soc_camera_bus_type); static struct device_driver ic_drv = { .name = "camera", diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c index 248c986..bf406e8 100644 --- a/drivers/media/video/soc_camera_platform.c +++ b/drivers/media/video/soc_camera_platform.c @@ -56,8 +56,8 @@ soc_camera_platform_query_bus_param(struct soc_camera_device *icd) return p->bus_param; } -static int soc_camera_platform_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) +static int soc_camera_platform_fill_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) { struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); @@ -65,6 +65,7 @@ static int soc_camera_platform_try_fmt(struct v4l2_subdev *sd, mf->height = p->format.height; mf->code = p->format.code; mf->colorspace = p->format.colorspace; + mf->field = p->format.field; return 0; } @@ -83,10 +84,45 @@ static int soc_camera_platform_enum_fmt(struct v4l2_subdev *sd, unsigned int ind return 0; } +static int soc_camera_platform_g_crop(struct v4l2_subdev *sd, + struct v4l2_crop *a) +{ + struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); + + a->c.left = 0; + a->c.top = 0; + a->c.width = p->format.width; + a->c.height = p->format.height; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int soc_camera_platform_cropcap(struct v4l2_subdev *sd, + struct v4l2_cropcap *a) +{ + struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); + + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = p->format.width; + a->bounds.height = p->format.height; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + static struct v4l2_subdev_video_ops platform_subdev_video_ops = { .s_stream = soc_camera_platform_s_stream, - .try_mbus_fmt = soc_camera_platform_try_fmt, .enum_mbus_fmt = soc_camera_platform_enum_fmt, + .cropcap = soc_camera_platform_cropcap, + .g_crop = soc_camera_platform_g_crop, + .try_mbus_fmt = soc_camera_platform_fill_fmt, + .g_mbus_fmt = soc_camera_platform_fill_fmt, + .s_mbus_fmt = soc_camera_platform_fill_fmt, }; static struct v4l2_subdev_ops platform_subdev_ops = { diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c index 8b63b65..9139121 100644 --- a/drivers/media/video/soc_mediabus.c +++ b/drivers/media/video/soc_mediabus.c @@ -18,28 +18,28 @@ #define MBUS_IDX(f) (V4L2_MBUS_FMT_ ## f - V4L2_MBUS_FMT_FIXED - 1) static const struct soc_mbus_pixelfmt mbus_fmt[] = { - [MBUS_IDX(YUYV8_2X8_LE)] = { + [MBUS_IDX(YUYV8_2X8)] = { .fourcc = V4L2_PIX_FMT_YUYV, .name = "YUYV", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, }, - [MBUS_IDX(YVYU8_2X8_LE)] = { + [MBUS_IDX(YVYU8_2X8)] = { .fourcc = V4L2_PIX_FMT_YVYU, .name = "YVYU", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, }, - [MBUS_IDX(YUYV8_2X8_BE)] = { + [MBUS_IDX(UYVY8_2X8)] = { .fourcc = V4L2_PIX_FMT_UYVY, .name = "UYVY", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, }, - [MBUS_IDX(YVYU8_2X8_BE)] = { + [MBUS_IDX(VYUY8_2X8)] = { .fourcc = V4L2_PIX_FMT_VYUY, .name = "VYUY", .bits_per_sample = 8, diff --git a/drivers/media/video/stv680.c b/drivers/media/video/stv680.c deleted file mode 100644 index 5938ad8..0000000 --- a/drivers/media/video/stv680.c +++ /dev/null @@ -1,1565 +0,0 @@ -/* - * 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/module.h> -#include <linux/init.h> -#include <linux/vmalloc.h> -#include <linux/slab.h> -#include <linux/smp_lock.h> -#include <linux/pagemap.h> -#include <linux/errno.h> -#include <linux/videodev.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> -#include <linux/usb.h> -#include <linux/mutex.h> - -#include "stv680.h" - -static int video_nr = -1; - -static int swapRGB; /* 0 = default for auto select */ - -/* 0 = default to allow auto select; -1 = swap never, +1 = swap always */ -static int swapRGB_on; - -static unsigned int debug; - -#define PDEBUG(level, fmt, args...) \ - do { \ - if (debug >= level) \ - printk(KERN_INFO KBUILD_MODNAME " [%s:%d] \n" fmt, \ - __func__, __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 device *class_dev, \ - struct device_attribute *attr, 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 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 int stv680_create_sysfs_files(struct video_device *vdev) -{ - int rc; - - rc = device_create_file(&vdev->dev, &dev_attr_model); - if (rc) goto err; - rc = device_create_file(&vdev->dev, &dev_attr_in_use); - if (rc) goto err_model; - rc = device_create_file(&vdev->dev, &dev_attr_streaming); - if (rc) goto err_inuse; - rc = device_create_file(&vdev->dev, &dev_attr_palette); - if (rc) goto err_stream; - rc = device_create_file(&vdev->dev, &dev_attr_frames_total); - if (rc) goto err_pal; - rc = device_create_file(&vdev->dev, &dev_attr_frames_read); - if (rc) goto err_framtot; - rc = device_create_file(&vdev->dev, &dev_attr_packets_dropped); - if (rc) goto err_framread; - rc = device_create_file(&vdev->dev, &dev_attr_decoding_errors); - if (rc) goto err_dropped; - - return 0; - -err_dropped: - device_remove_file(&vdev->dev, &dev_attr_packets_dropped); -err_framread: - device_remove_file(&vdev->dev, &dev_attr_frames_read); -err_framtot: - device_remove_file(&vdev->dev, &dev_attr_frames_total); -err_pal: - device_remove_file(&vdev->dev, &dev_attr_palette); -err_stream: - device_remove_file(&vdev->dev, &dev_attr_streaming); -err_inuse: - device_remove_file(&vdev->dev, &dev_attr_in_use); -err_model: - device_remove_file(&vdev->dev, &dev_attr_model); -err: - PDEBUG(0, "STV(e): Could not create sysfs files"); - return rc; -} - -static void stv680_remove_sysfs_files(struct video_device *vdev) -{ - device_remove_file(&vdev->dev, &dev_attr_model); - device_remove_file(&vdev->dev, &dev_attr_in_use); - device_remove_file(&vdev->dev, &dev_attr_streaming); - device_remove_file(&vdev->dev, &dev_attr_palette); - device_remove_file(&vdev->dev, &dev_attr_frames_total); - device_remove_file(&vdev->dev, &dev_attr_frames_read); - device_remove_file(&vdev->dev, &dev_attr_packets_dropped); - device_remove_file(&vdev->dev, &dev_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 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); - goto nomem_err; - } - } - - 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); - goto nomem_err; - } - stv680->scratch[i].state = BUFFER_UNUSED; - } - - for (i = 0; i < STV680_NUMSBUF; i++) { - urb = usb_alloc_urb (0, GFP_KERNEL); - if (!urb) - goto nomem_err; - - /* 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 with err " - "%d in start stream %d", err, i); - goto nomem_err; - } - } /* i STV680_NUMSBUF */ - - stv680->framecount = 0; - return 0; - - nomem_err: - for (i = 0; i < STV680_NUMSBUF; i++) { - usb_kill_urb(stv680->urb[i]); - usb_free_urb(stv680->urb[i]); - stv680->urb[i] = NULL; - kfree(stv680->sbuf[i].data); - stv680->sbuf[i].data = NULL; - } - /* used in irq, free only as all URBs are dead */ - for (i = 0; i < STV680_NUMSCRATCH; i++) { - kfree(stv680->scratch[i].data); - stv680->scratch[i].data = NULL; - } - return -ENOMEM; - -} - -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 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 */ - lock_kernel(); - 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; - unlock_kernel(); - - return err; -} - -static int stv_close(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 long stv680_do_ioctl(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 long stv680_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(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 const struct v4l2_file_operations stv680_fops = { - .owner = THIS_MODULE, - .open = stv_open, - .release = stv_close, - .read = stv680_read, - .mmap = stv680_mmap, - .ioctl = stv680_ioctl, -}; -static struct video_device stv680_template = { - .name = "STV0680 USB camera", - .fops = &stv680_fops, - .release = video_device_release, -}; - -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->parent = &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) < 0) { - PDEBUG (0, "STV(e): video_register_device failed"); - retval = -EIO; - goto error_vdev; - } - PDEBUG(0, "STV(i): registered new video device: %s", - video_device_node_name(stv680->vdev)); - - usb_set_intfdata (intf, stv680); - retval = stv680_create_sysfs_files(stv680->vdev); - if (retval) - goto error_unreg; - return 0; - -error_unreg: - video_unregister_device(stv680->vdev); -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); - - printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" - DRIVER_DESC "\n"); - 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/drivers/media/video/stv680.h b/drivers/media/video/stv680.h deleted file mode 100644 index a08f1b0..0000000 --- a/drivers/media/video/stv680.h +++ /dev/null @@ -1,227 +0,0 @@ -/**************************************************************************** - * - * 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/drivers/media/video/tlg2300/pd-main.c b/drivers/media/video/tlg2300/pd-main.c index 256cc55..4555f4a 100644 --- a/drivers/media/video/tlg2300/pd-main.c +++ b/drivers/media/video/tlg2300/pd-main.c @@ -227,12 +227,11 @@ static int firmware_download(struct usb_device *udev) fwlength = fw->size; - fwbuf = kzalloc(fwlength, GFP_KERNEL); + fwbuf = kmemdup(fw->data, fwlength, GFP_KERNEL); if (!fwbuf) { ret = -ENOMEM; goto out; } - memcpy(fwbuf, fw->data, fwlength); max_packet_size = udev->ep_out[0x1]->desc.wMaxPacketSize; log("\t\t download size : %d", (int)max_packet_size); diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c index 445dc93..a727962 100644 --- a/drivers/media/video/tw9910.c +++ b/drivers/media/video/tw9910.c @@ -768,7 +768,7 @@ static int tw9910_g_fmt(struct v4l2_subdev *sd, mf->width = priv->scale->width; mf->height = priv->scale->height; - mf->code = V4L2_MBUS_FMT_YUYV8_2X8_BE; + mf->code = V4L2_MBUS_FMT_UYVY8_2X8; mf->colorspace = V4L2_COLORSPACE_JPEG; mf->field = V4L2_FIELD_INTERLACED_BT; @@ -797,7 +797,7 @@ static int tw9910_s_fmt(struct v4l2_subdev *sd, /* * check color format */ - if (mf->code != V4L2_MBUS_FMT_YUYV8_2X8_BE) + if (mf->code != V4L2_MBUS_FMT_UYVY8_2X8) return -EINVAL; mf->colorspace = V4L2_COLORSPACE_JPEG; @@ -824,7 +824,7 @@ static int tw9910_try_fmt(struct v4l2_subdev *sd, return -EINVAL; } - mf->code = V4L2_MBUS_FMT_YUYV8_2X8_BE; + mf->code = V4L2_MBUS_FMT_UYVY8_2X8; mf->colorspace = V4L2_COLORSPACE_JPEG; /* @@ -909,7 +909,7 @@ static int tw9910_enum_fmt(struct v4l2_subdev *sd, unsigned int index, if (index) return -EINVAL; - *code = V4L2_MBUS_FMT_YUYV8_2X8_BE; + *code = V4L2_MBUS_FMT_UYVY8_2X8; return 0; } diff --git a/drivers/media/video/usbvideo/Kconfig b/drivers/media/video/usbvideo/Kconfig index adb1c04..d6e1695 100644 --- a/drivers/media/video/usbvideo/Kconfig +++ b/drivers/media/video/usbvideo/Kconfig @@ -37,17 +37,3 @@ config USB_KONICAWC To compile this driver as a module, choose M here: the module will be called konicawc. -config USB_QUICKCAM_MESSENGER - tristate "USB Logitech Quickcam Messenger (DEPRECATED)" - depends on VIDEO_V4L1 - select VIDEO_USBVIDEO - ---help--- - This driver is DEPRECATED please use the gspca stv06xx module - instead. - - Say Y or M here to enable support for the USB Logitech Quickcam - Messenger webcam. - - To compile this driver as a module, choose M here: the - module will be called quickcam_messenger. - diff --git a/drivers/media/video/usbvideo/Makefile b/drivers/media/video/usbvideo/Makefile index 4a1b144..bb52eb8 100644 --- a/drivers/media/video/usbvideo/Makefile +++ b/drivers/media/video/usbvideo/Makefile @@ -2,4 +2,3 @@ obj-$(CONFIG_VIDEO_USBVIDEO) += usbvideo.o obj-$(CONFIG_USB_IBMCAM) += ibmcam.o ultracam.o obj-$(CONFIG_USB_KONICAWC) += konicawc.o obj-$(CONFIG_USB_VICAM) += vicam.o -obj-$(CONFIG_USB_QUICKCAM_MESSENGER) += quickcam_messenger.o diff --git a/drivers/media/video/usbvideo/quickcam_messenger.c b/drivers/media/video/usbvideo/quickcam_messenger.c deleted file mode 100644 index fbd665f..0000000 --- a/drivers/media/video/usbvideo/quickcam_messenger.c +++ /dev/null @@ -1,1126 +0,0 @@ -/* - * Driver for Logitech Quickcam Messenger usb video camera - * Copyright (C) Jaya Kumar - * - * This work was sponsored by CIS(M) Sdn Bhd. - * History: - * 05/08/2006 - Jaya Kumar - * I wrote this based on the konicawc by Simon Evans. - * - - * Full credit for reverse engineering and creating an initial - * working linux driver for the VV6422 goes to the qce-ga project by - * Tuukka Toivonen, Jochen Hoenicke, Peter McConnell, - * Cristiano De Michele, Georg Acher, Jean-Frederic Clere as well as - * others. - * --- - * 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/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/input.h> -#include <linux/usb/input.h> -#include <linux/slab.h> - -#include "usbvideo.h" -#include "quickcam_messenger.h" - -/* - * Version Information - */ - -#ifdef CONFIG_USB_DEBUG -static int debug; -#define DEBUG(n, format, arg...) \ - if (n <= debug) { \ - printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __func__ , ## arg); \ - } -#else -#define DEBUG(n, arg...) -static const int debug; -#endif - -#define DRIVER_VERSION "v0.01" -#define DRIVER_DESC "Logitech Quickcam Messenger USB" - -#define USB_LOGITECH_VENDOR_ID 0x046D -#define USB_QCM_PRODUCT_ID 0x08F0 - -#define MAX_CAMERAS 1 - -#define MAX_COLOUR 32768 -#define MAX_HUE 32768 -#define MAX_BRIGHTNESS 32768 -#define MAX_CONTRAST 32768 -#define MAX_WHITENESS 32768 - -static int size = SIZE_320X240; -static int colour = MAX_COLOUR; -static int hue = MAX_HUE; -static int brightness = MAX_BRIGHTNESS; -static int contrast = MAX_CONTRAST; -static int whiteness = MAX_WHITENESS; - -static struct usbvideo *cams; - -static struct usb_device_id qcm_table [] = { - { USB_DEVICE(USB_LOGITECH_VENDOR_ID, USB_QCM_PRODUCT_ID) }, - { } -}; -MODULE_DEVICE_TABLE(usb, qcm_table); - -#ifdef CONFIG_INPUT -static void qcm_register_input(struct qcm *cam, struct usb_device *dev) -{ - struct input_dev *input_dev; - int error; - - usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname)); - strlcat(cam->input_physname, "/input0", sizeof(cam->input_physname)); - - cam->input = input_dev = input_allocate_device(); - if (!input_dev) { - dev_warn(&dev->dev, "insufficient mem for cam input device\n"); - return; - } - - input_dev->name = "QCM button"; - input_dev->phys = cam->input_physname; - usb_to_input_id(dev, &input_dev->id); - input_dev->dev.parent = &dev->dev; - - input_dev->evbit[0] = BIT_MASK(EV_KEY); - input_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA); - - error = input_register_device(cam->input); - if (error) { - dev_warn(&dev->dev, - "Failed to register camera's input device, err: %d\n", - error); - input_free_device(cam->input); - cam->input = NULL; - } -} - -static void qcm_unregister_input(struct qcm *cam) -{ - if (cam->input) { - input_unregister_device(cam->input); - cam->input = NULL; - } -} - -static void qcm_report_buttonstat(struct qcm *cam) -{ - if (cam->input) { - input_report_key(cam->input, KEY_CAMERA, cam->button_sts); - input_sync(cam->input); - } -} - -static void qcm_int_irq(struct urb *urb) -{ - int ret; - struct uvd *uvd = urb->context; - struct qcm *cam; - - if (!CAMERA_IS_OPERATIONAL(uvd)) - return; - - if (!uvd->streaming) - return; - - uvd->stats.urb_count++; - - if (urb->status < 0) - uvd->stats.iso_err_count++; - else { - if (urb->actual_length > 0 ) { - cam = (struct qcm *) uvd->user_data; - if (cam->button_sts_buf == 0x88) - cam->button_sts = 0x0; - else if (cam->button_sts_buf == 0x80) - cam->button_sts = 0x1; - qcm_report_buttonstat(cam); - } - } - - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret < 0) - err("usb_submit_urb error (%d)", ret); -} - -static int qcm_setup_input_int(struct qcm *cam, struct uvd *uvd) -{ - int errflag; - usb_fill_int_urb(cam->button_urb, uvd->dev, - usb_rcvintpipe(uvd->dev, uvd->video_endp + 1), - &cam->button_sts_buf, - 1, - qcm_int_irq, - uvd, 16); - - errflag = usb_submit_urb(cam->button_urb, GFP_KERNEL); - if (errflag) - err ("usb_submit_int ret %d", errflag); - return errflag; -} - -static void qcm_stop_int_data(struct qcm *cam) -{ - usb_kill_urb(cam->button_urb); -} - -static int qcm_alloc_int_urb(struct qcm *cam) -{ - cam->button_urb = usb_alloc_urb(0, GFP_KERNEL); - - if (!cam->button_urb) - return -ENOMEM; - - return 0; -} - -static void qcm_free_int(struct qcm *cam) -{ - usb_free_urb(cam->button_urb); -} -#endif /* CONFIG_INPUT */ - -static int qcm_stv_setb(struct usb_device *dev, u16 reg, u8 val) -{ - int ret; - - /* we'll wait up to 3 slices but no more */ - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0x04, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, - reg, 0, &val, 1, 3*HZ); - return ret; -} - -static int qcm_stv_setw(struct usb_device *dev, u16 reg, __le16 val) -{ - int ret; - - /* we'll wait up to 3 slices but no more */ - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 0x04, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, - reg, 0, &val, 2, 3*HZ); - return ret; -} - -static int qcm_stv_getw(struct usb_device *dev, unsigned short reg, - __le16 *val) -{ - int ret; - - /* we'll wait up to 3 slices but no more */ - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - 0x04, USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, - reg, 0, val, 2, 3*HZ); - return ret; -} - -static int qcm_camera_on(struct uvd *uvd) -{ - int ret; - CHECK_RET(ret, qcm_stv_setb(uvd->dev, STV_ISO_ENABLE, 0x01)); - return 0; -} - -static int qcm_camera_off(struct uvd *uvd) -{ - int ret; - CHECK_RET(ret, qcm_stv_setb(uvd->dev, STV_ISO_ENABLE, 0x00)); - return 0; -} - -static void qcm_hsv2rgb(u16 hue, u16 sat, u16 val, u16 *r, u16 *g, u16 *b) -{ - unsigned int segment, valsat; - signed int h = (signed int) hue; - unsigned int s = (sat - 32768) * 2; /* rescale */ - unsigned int v = val; - unsigned int p; - - /* - the registers controlling gain are 8 bit of which - we affect only the last 4 bits with our gain. - we know that if saturation is 0, (unsaturated) then - we're grayscale (center axis of the colour cone) so - we set rgb=value. we use a formula obtained from - wikipedia to map the cone to the RGB plane. it's - as follows for the human value case of h=0..360, - s=0..1, v=0..1 - h_i = h/60 % 6 , f = h/60 - h_i , p = v(1-s) - q = v(1 - f*s) , t = v(1 - (1-f)s) - h_i==0 => r=v , g=t, b=p - h_i==1 => r=q , g=v, b=p - h_i==2 => r=p , g=v, b=t - h_i==3 => r=p , g=q, b=v - h_i==4 => r=t , g=p, b=v - h_i==5 => r=v , g=p, b=q - the bottom side (the point) and the stuff just up - of that is black so we simplify those two cases. - */ - if (sat < 32768) { - /* anything less than this is unsaturated */ - *r = val; - *g = val; - *b = val; - return; - } - if (val <= (0xFFFF/8)) { - /* anything less than this is black */ - *r = 0; - *g = 0; - *b = 0; - return; - } - - /* the rest of this code is copying tukkat's - implementation of the hsv2rgb conversion as taken - from qc-usb-messenger code. the 10923 is 0xFFFF/6 - to divide the cone into 6 sectors. */ - - segment = (h + 10923) & 0xFFFF; - segment = segment*3 >> 16; /* 0..2: 0=R, 1=G, 2=B */ - hue -= segment * 21845; /* -10923..10923 */ - h = hue; - h *= 3; - valsat = v*s >> 16; /* 0..65534 */ - p = v - valsat; - if (h >= 0) { - unsigned int t = v - (valsat * (32769 - h) >> 15); - switch (segment) { - case 0: /* R-> */ - *r = v; - *g = t; - *b = p; - break; - case 1: /* G-> */ - *r = p; - *g = v; - *b = t; - break; - case 2: /* B-> */ - *r = t; - *g = p; - *b = v; - break; - } - } else { - unsigned int q = v - (valsat * (32769 + h) >> 15); - switch (segment) { - case 0: /* ->R */ - *r = v; - *g = p; - *b = q; - break; - case 1: /* ->G */ - *r = q; - *g = v; - *b = p; - break; - case 2: /* ->B */ - *r = p; - *g = q; - *b = v; - break; - } - } -} - -static int qcm_sensor_set_gains(struct uvd *uvd, u16 hue, - u16 saturation, u16 value) -{ - int ret; - u16 r=0,g=0,b=0; - - /* this code is based on qc-usb-messenger */ - qcm_hsv2rgb(hue, saturation, value, &r, &g, &b); - - r >>= 12; - g >>= 12; - b >>= 12; - - /* min val is 8 */ - r = max((u16) 8, r); - g = max((u16) 8, g); - b = max((u16) 8, b); - - r |= 0x30; - g |= 0x30; - b |= 0x30; - - /* set the r,g,b gain registers */ - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x0509, r)); - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050A, g)); - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050B, b)); - - /* doing as qc-usb did */ - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050C, 0x2A)); - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x050D, 0x01)); - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143F, 0x01)); - - return 0; -} - -static int qcm_sensor_set_exposure(struct uvd *uvd, int exposure) -{ - int ret; - int formedval; - - /* calculation was from qc-usb-messenger driver */ - formedval = ( exposure >> 12 ); - - /* max value for formedval is 14 */ - formedval = min(formedval, 14); - - CHECK_RET(ret, qcm_stv_setb(uvd->dev, - 0x143A, 0xF0 | formedval)); - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143F, 0x01)); - return 0; -} - -static int qcm_sensor_setlevels(struct uvd *uvd, int brightness, int contrast, - int hue, int colour) -{ - int ret; - /* brightness is exposure, contrast is gain, colour is saturation */ - CHECK_RET(ret, - qcm_sensor_set_exposure(uvd, brightness)); - CHECK_RET(ret, qcm_sensor_set_gains(uvd, hue, colour, contrast)); - - return 0; -} - -static int qcm_sensor_setsize(struct uvd *uvd, u8 size) -{ - int ret; - - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x1505, size)); - return 0; -} - -static int qcm_sensor_set_shutter(struct uvd *uvd, int whiteness) -{ - int ret; - /* some rescaling as done by the qc-usb-messenger code */ - if (whiteness > 0xC000) - whiteness = 0xC000 + (whiteness & 0x3FFF)*8; - - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143D, - (whiteness >> 8) & 0xFF)); - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143E, - (whiteness >> 16) & 0x03)); - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143F, 0x01)); - - return 0; -} - -static int qcm_sensor_init(struct uvd *uvd) -{ - struct qcm *cam = (struct qcm *) uvd->user_data; - int ret; - int i; - - for (i=0; i < ARRAY_SIZE(regval_table) ; i++) { - CHECK_RET(ret, qcm_stv_setb(uvd->dev, - regval_table[i].reg, - regval_table[i].val)); - } - - CHECK_RET(ret, qcm_stv_setw(uvd->dev, 0x15c1, - cpu_to_le16(ISOC_PACKET_SIZE))); - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x15c3, 0x08)); - CHECK_RET(ret, qcm_stv_setb(uvd->dev, 0x143f, 0x01)); - - CHECK_RET(ret, qcm_stv_setb(uvd->dev, STV_ISO_ENABLE, 0x00)); - - CHECK_RET(ret, qcm_sensor_setsize(uvd, camera_sizes[cam->size].cmd)); - - CHECK_RET(ret, qcm_sensor_setlevels(uvd, uvd->vpic.brightness, - uvd->vpic.contrast, uvd->vpic.hue, uvd->vpic.colour)); - - CHECK_RET(ret, qcm_sensor_set_shutter(uvd, uvd->vpic.whiteness)); - CHECK_RET(ret, qcm_sensor_setsize(uvd, camera_sizes[cam->size].cmd)); - - return 0; -} - -static int qcm_set_camera_size(struct uvd *uvd) -{ - int ret; - struct qcm *cam = (struct qcm *) uvd->user_data; - - CHECK_RET(ret, qcm_sensor_setsize(uvd, camera_sizes[cam->size].cmd)); - cam->width = camera_sizes[cam->size].width; - cam->height = camera_sizes[cam->size].height; - uvd->videosize = VIDEOSIZE(cam->width, cam->height); - - return 0; -} - -static int qcm_setup_on_open(struct uvd *uvd) -{ - int ret; - - CHECK_RET(ret, qcm_sensor_set_gains(uvd, uvd->vpic.hue, - uvd->vpic.colour, uvd->vpic.contrast)); - CHECK_RET(ret, qcm_sensor_set_exposure(uvd, uvd->vpic.brightness)); - CHECK_RET(ret, qcm_sensor_set_shutter(uvd, uvd->vpic.whiteness)); - CHECK_RET(ret, qcm_set_camera_size(uvd)); - CHECK_RET(ret, qcm_camera_on(uvd)); - return 0; -} - -static void qcm_adjust_picture(struct uvd *uvd) -{ - int ret; - struct qcm *cam = (struct qcm *) uvd->user_data; - - ret = qcm_camera_off(uvd); - if (ret) { - err("can't turn camera off. abandoning pic adjustment"); - return; - } - - /* if there's been a change in contrast, hue, or - colour then we need to recalculate hsv in order - to update gains */ - if ((cam->contrast != uvd->vpic.contrast) || - (cam->hue != uvd->vpic.hue) || - (cam->colour != uvd->vpic.colour)) { - cam->contrast = uvd->vpic.contrast; - cam->hue = uvd->vpic.hue; - cam->colour = uvd->vpic.colour; - ret = qcm_sensor_set_gains(uvd, cam->hue, cam->colour, - cam->contrast); - if (ret) { - err("can't set gains. abandoning pic adjustment"); - return; - } - } - - if (cam->brightness != uvd->vpic.brightness) { - cam->brightness = uvd->vpic.brightness; - ret = qcm_sensor_set_exposure(uvd, cam->brightness); - if (ret) { - err("can't set exposure. abandoning pic adjustment"); - return; - } - } - - if (cam->whiteness != uvd->vpic.whiteness) { - cam->whiteness = uvd->vpic.whiteness; - qcm_sensor_set_shutter(uvd, cam->whiteness); - if (ret) { - err("can't set shutter. abandoning pic adjustment"); - return; - } - } - - ret = qcm_camera_on(uvd); - if (ret) { - err("can't reenable camera. pic adjustment failed"); - return; - } -} - -static int qcm_process_frame(struct uvd *uvd, u8 *cdata, int framelen) -{ - int datalen; - int totaldata; - struct framehdr { - __be16 id; - __be16 len; - }; - struct framehdr *fhdr; - - totaldata = 0; - while (framelen) { - fhdr = (struct framehdr *) cdata; - datalen = be16_to_cpu(fhdr->len); - framelen -= 4; - cdata += 4; - - if ((fhdr->id) == cpu_to_be16(0x8001)) { - RingQueue_Enqueue(&uvd->dp, marker, 4); - totaldata += 4; - continue; - } - if ((fhdr->id & cpu_to_be16(0xFF00)) == cpu_to_be16(0x0200)) { - RingQueue_Enqueue(&uvd->dp, cdata, datalen); - totaldata += datalen; - } - framelen -= datalen; - cdata += datalen; - } - return totaldata; -} - -static int qcm_compress_iso(struct uvd *uvd, struct urb *dataurb) -{ - int totlen; - int i; - unsigned char *cdata; - - totlen=0; - for (i = 0; i < dataurb->number_of_packets; i++) { - int n = dataurb->iso_frame_desc[i].actual_length; - int st = dataurb->iso_frame_desc[i].status; - - cdata = dataurb->transfer_buffer + - dataurb->iso_frame_desc[i].offset; - - if (st < 0) { - dev_warn(&uvd->dev->dev, - "Data error: packet=%d. len=%d. status=%d.\n", - i, n, st); - uvd->stats.iso_err_count++; - continue; - } - if (!n) - continue; - - totlen += qcm_process_frame(uvd, cdata, n); - } - return totlen; -} - -static void resubmit_urb(struct uvd *uvd, struct urb *urb) -{ - int ret; - - urb->dev = uvd->dev; - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret) - err("usb_submit_urb error (%d)", ret); -} - -static void qcm_isoc_irq(struct urb *urb) -{ - int len; - struct uvd *uvd = urb->context; - - if (!CAMERA_IS_OPERATIONAL(uvd)) - return; - - if (!uvd->streaming) - return; - - uvd->stats.urb_count++; - - if (!urb->actual_length) { - resubmit_urb(uvd, urb); - return; - } - - len = qcm_compress_iso(uvd, urb); - resubmit_urb(uvd, urb); - uvd->stats.urb_length = len; - uvd->stats.data_count += len; - if (len) - RingQueue_WakeUpInterruptible(&uvd->dp); -} - -static int qcm_start_data(struct uvd *uvd) -{ - struct qcm *cam = (struct qcm *) uvd->user_data; - int i; - int errflag; - int pktsz; - int err; - - pktsz = uvd->iso_packet_len; - if (!CAMERA_IS_OPERATIONAL(uvd)) { - err("Camera is not operational"); - return -EFAULT; - } - - err = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltActive); - if (err < 0) { - err("usb_set_interface error"); - uvd->last_error = err; - return -EBUSY; - } - - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - int j, k; - struct urb *urb = uvd->sbuf[i].urb; - urb->dev = uvd->dev; - urb->context = uvd; - urb->pipe = usb_rcvisocpipe(uvd->dev, uvd->video_endp); - urb->interval = 1; - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = uvd->sbuf[i].data; - urb->complete = qcm_isoc_irq; - urb->number_of_packets = FRAMES_PER_DESC; - urb->transfer_buffer_length = pktsz * FRAMES_PER_DESC; - for (j=k=0; j < FRAMES_PER_DESC; j++, k += pktsz) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = pktsz; - } - } - - uvd->streaming = 1; - uvd->curframe = -1; - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - errflag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL); - if (errflag) - err ("usb_submit_isoc(%d) ret %d", i, errflag); - } - - CHECK_RET(err, qcm_setup_input_int(cam, uvd)); - CHECK_RET(err, qcm_camera_on(uvd)); - return 0; -} - -static void qcm_stop_data(struct uvd *uvd) -{ - struct qcm *cam; - int i, j; - int ret; - - if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL)) - return; - cam = (struct qcm *) uvd->user_data; - - ret = qcm_camera_off(uvd); - if (ret) - dev_warn(&uvd->dev->dev, "couldn't turn the cam off.\n"); - - uvd->streaming = 0; - - /* Unschedule all of the iso td's */ - for (i=0; i < USBVIDEO_NUMSBUF; i++) - usb_kill_urb(uvd->sbuf[i].urb); - - qcm_stop_int_data(cam); - - if (!uvd->remove_pending) { - /* Set packet size to 0 */ - j = usb_set_interface(uvd->dev, uvd->iface, - uvd->ifaceAltInactive); - if (j < 0) { - err("usb_set_interface() error %d.", j); - uvd->last_error = j; - } - } -} - -static void qcm_process_isoc(struct uvd *uvd, struct usbvideo_frame *frame) -{ - struct qcm *cam = (struct qcm *) uvd->user_data; - int x; - struct rgb *rgbL0; - struct rgb *rgbL1; - struct bayL0 *bayL0; - struct bayL1 *bayL1; - int hor,ver,hordel,verdel; - assert(frame != NULL); - - switch (cam->size) { - case SIZE_160X120: - hor = 162; ver = 124; hordel = 1; verdel = 2; - break; - case SIZE_320X240: - default: - hor = 324; ver = 248; hordel = 2; verdel = 4; - break; - } - - if (frame->scanstate == ScanState_Scanning) { - while (RingQueue_GetLength(&uvd->dp) >= - 4 + (hor*verdel + hordel)) { - if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) && - (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xff) && - (RING_QUEUE_PEEK(&uvd->dp, 2) == 0x00) && - (RING_QUEUE_PEEK(&uvd->dp, 3) == 0xff)) { - frame->curline = 0; - frame->scanstate = ScanState_Lines; - frame->frameState = FrameState_Grabbing; - RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 4); - /* - * if we're starting, we need to discard the first - * 4 lines of y bayer data - * and the first 2 gr elements of x bayer data - */ - RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, - (hor*verdel + hordel)); - break; - } - RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1); - } - } - - if (frame->scanstate == ScanState_Scanning) - return; - - /* now we can start processing bayer data so long as we have at least - * 2 lines worth of data. this is the simplest demosaicing method that - * I could think of. I use each 2x2 bayer element without interpolation - * to generate 4 rgb pixels. - */ - while ( frame->curline < cam->height && - (RingQueue_GetLength(&uvd->dp) >= hor*2)) { - /* get 2 lines of bayer for demosaicing - * into 2 lines of RGB */ - RingQueue_Dequeue(&uvd->dp, cam->scratch, hor*2); - bayL0 = (struct bayL0 *) cam->scratch; - bayL1 = (struct bayL1 *) (cam->scratch + hor); - /* frame->curline is the rgb y line */ - rgbL0 = (struct rgb *) - ( frame->data + (cam->width*3*frame->curline)); - /* w/2 because we're already doing 2 pixels */ - rgbL1 = rgbL0 + (cam->width/2); - - for (x=0; x < cam->width; x+=2) { - rgbL0->r = bayL0->r; - rgbL0->g = bayL0->g; - rgbL0->b = bayL1->b; - - rgbL0->r2 = bayL0->r; - rgbL0->g2 = bayL1->g; - rgbL0->b2 = bayL1->b; - - rgbL1->r = bayL0->r; - rgbL1->g = bayL1->g; - rgbL1->b = bayL1->b; - - rgbL1->r2 = bayL0->r; - rgbL1->g2 = bayL1->g; - rgbL1->b2 = bayL1->b; - - rgbL0++; - rgbL1++; - - bayL0++; - bayL1++; - } - - frame->seqRead_Length += cam->width*3*2; - frame->curline += 2; - } - /* See if we filled the frame */ - if (frame->curline == cam->height) { - frame->frameState = FrameState_Done_Hold; - frame->curline = 0; - uvd->curframe = -1; - uvd->stats.frame_num++; - } -} - -/* taken from konicawc */ -static int qcm_set_video_mode(struct uvd *uvd, struct video_window *vw) -{ - int ret; - int newsize; - int oldsize; - int x = vw->width; - int y = vw->height; - struct qcm *cam = (struct qcm *) uvd->user_data; - - if (x > 0 && y > 0) { - DEBUG(2, "trying to find size %d,%d", x, y); - for (newsize = 0; newsize <= MAX_FRAME_SIZE; newsize++) { - if ((camera_sizes[newsize].width == x) && - (camera_sizes[newsize].height == y)) - break; - } - } else - newsize = cam->size; - - if (newsize > MAX_FRAME_SIZE) { - DEBUG(1, "couldn't find size %d,%d", x, y); - return -EINVAL; - } - - if (newsize == cam->size) { - DEBUG(1, "Nothing to do"); - return 0; - } - - qcm_stop_data(uvd); - - if (cam->size != newsize) { - oldsize = cam->size; - cam->size = newsize; - ret = qcm_set_camera_size(uvd); - if (ret) { - err("Couldn't set camera size, err=%d",ret); - /* restore the original size */ - cam->size = oldsize; - return ret; - } - } - - /* Flush the input queue and clear any current frame in progress */ - - RingQueue_Flush(&uvd->dp); - if (uvd->curframe != -1) { - uvd->frame[uvd->curframe].curline = 0; - uvd->frame[uvd->curframe].seqRead_Length = 0; - uvd->frame[uvd->curframe].seqRead_Index = 0; - } - - CHECK_RET(ret, qcm_start_data(uvd)); - return 0; -} - -static int qcm_configure_video(struct uvd *uvd) -{ - int ret; - memset(&uvd->vpic, 0, sizeof(uvd->vpic)); - memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old)); - - uvd->vpic.colour = colour; - uvd->vpic.hue = hue; - uvd->vpic.brightness = brightness; - uvd->vpic.contrast = contrast; - uvd->vpic.whiteness = whiteness; - uvd->vpic.depth = 24; - uvd->vpic.palette = VIDEO_PALETTE_RGB24; - - memset(&uvd->vcap, 0, sizeof(uvd->vcap)); - strcpy(uvd->vcap.name, "QCM USB Camera"); - uvd->vcap.type = VID_TYPE_CAPTURE; - uvd->vcap.channels = 1; - uvd->vcap.audios = 0; - - uvd->vcap.minwidth = camera_sizes[SIZE_160X120].width; - uvd->vcap.minheight = camera_sizes[SIZE_160X120].height; - uvd->vcap.maxwidth = camera_sizes[SIZE_320X240].width; - uvd->vcap.maxheight = camera_sizes[SIZE_320X240].height; - - memset(&uvd->vchan, 0, sizeof(uvd->vchan)); - uvd->vchan.flags = 0 ; - uvd->vchan.tuners = 0; - uvd->vchan.channel = 0; - uvd->vchan.type = VIDEO_TYPE_CAMERA; - strcpy(uvd->vchan.name, "Camera"); - - CHECK_RET(ret, qcm_sensor_init(uvd)); - return 0; -} - -static int qcm_probe(struct usb_interface *intf, - const struct usb_device_id *devid) -{ - int err; - struct uvd *uvd; - struct usb_device *dev = interface_to_usbdev(intf); - struct qcm *cam; - size_t buffer_size; - unsigned char video_ep; - struct usb_host_interface *interface; - struct usb_endpoint_descriptor *endpoint; - int i,j; - unsigned int ifacenum, ifacenum_inact=0; - __le16 sensor_id; - - /* we don't support multiconfig cams */ - if (dev->descriptor.bNumConfigurations != 1) - return -ENODEV; - - /* first check for the video interface and not - * the audio interface */ - interface = &intf->cur_altsetting[0]; - if ((interface->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC) - || (interface->desc.bInterfaceSubClass != - USB_CLASS_VENDOR_SPEC)) - return -ENODEV; - - /* - walk through each endpoint in each setting in the interface - stop when we find the one that's an isochronous IN endpoint. - */ - for (i=0; i < intf->num_altsetting; i++) { - interface = &intf->cur_altsetting[i]; - ifacenum = interface->desc.bAlternateSetting; - /* walk the end points */ - for (j=0; j < interface->desc.bNumEndpoints; j++) { - endpoint = &interface->endpoint[j].desc; - - if (usb_endpoint_dir_out(endpoint)) - continue; /* not input then not good */ - - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); - if (!buffer_size) { - ifacenum_inact = ifacenum; - continue; /* 0 pkt size is not what we want */ - } - - if (usb_endpoint_xfer_isoc(endpoint)) { - video_ep = endpoint->bEndpointAddress; - /* break out of the search */ - goto good_videoep; - } - } - } - /* failed out since nothing useful was found */ - err("No suitable endpoint was found\n"); - return -ENODEV; - -good_videoep: - /* disable isochronous stream before doing anything else */ - err = qcm_stv_setb(dev, STV_ISO_ENABLE, 0); - if (err < 0) { - err("Failed to disable sensor stream"); - return -EIO; - } - - /* - Check that this is the same unknown sensor that is known to work. This - sensor is suspected to be the ST VV6422C001. I'll check the same value - that the qc-usb driver checks. This value is probably not even the - sensor ID since it matches the USB dev ID. Oh well. If it doesn't - match, it's probably a diff sensor so exit and apologize. - */ - err = qcm_stv_getw(dev, CMOS_SENSOR_IDREV, &sensor_id); - if (err < 0) { - err("Couldn't read sensor values. Err %d\n",err); - return err; - } - if (sensor_id != cpu_to_le16(0x08F0)) { - err("Sensor ID %x != %x. Unsupported. Sorry\n", - le16_to_cpu(sensor_id), (0x08F0)); - return -ENODEV; - } - - uvd = usbvideo_AllocateDevice(cams); - if (!uvd) - return -ENOMEM; - - cam = (struct qcm *) uvd->user_data; - - /* buf for doing demosaicing */ - cam->scratch = kmalloc(324*2, GFP_KERNEL); - if (!cam->scratch) /* uvd freed in dereg */ - return -ENOMEM; - - /* yes, if we fail after here, cam->scratch gets freed - by qcm_free_uvd */ - - err = qcm_alloc_int_urb(cam); - if (err < 0) - return err; - - /* yes, if we fail after here, int urb gets freed - by qcm_free_uvd */ - - RESTRICT_TO_RANGE(size, SIZE_160X120, SIZE_320X240); - cam->width = camera_sizes[size].width; - cam->height = camera_sizes[size].height; - cam->size = size; - - uvd->debug = debug; - uvd->flags = 0; - uvd->dev = dev; - uvd->iface = intf->altsetting->desc.bInterfaceNumber; - uvd->ifaceAltActive = ifacenum; - uvd->ifaceAltInactive = ifacenum_inact; - uvd->video_endp = video_ep; - uvd->iso_packet_len = buffer_size; - uvd->paletteBits = 1L << VIDEO_PALETTE_RGB24; - uvd->defaultPalette = VIDEO_PALETTE_RGB24; - uvd->canvas = VIDEOSIZE(320, 240); - uvd->videosize = VIDEOSIZE(cam->width, cam->height); - err = qcm_configure_video(uvd); - if (err) { - err("failed to configure video settings"); - return err; - } - - err = usbvideo_RegisterVideoDevice(uvd); - if (err) { /* the uvd gets freed in Deregister */ - err("usbvideo_RegisterVideoDevice() failed."); - return err; - } - - uvd->max_frame_size = (320 * 240 * 3); - qcm_register_input(cam, dev); - usb_set_intfdata(intf, uvd); - return 0; -} - -static void qcm_free_uvd(struct uvd *uvd) -{ - struct qcm *cam = (struct qcm *) uvd->user_data; - - kfree(cam->scratch); - qcm_unregister_input(cam); - qcm_free_int(cam); -} - -static struct usbvideo_cb qcm_driver = { - .probe = qcm_probe, - .setupOnOpen = qcm_setup_on_open, - .processData = qcm_process_isoc, - .setVideoMode = qcm_set_video_mode, - .startDataPump = qcm_start_data, - .stopDataPump = qcm_stop_data, - .adjustPicture = qcm_adjust_picture, - .userFree = qcm_free_uvd -}; - -static int __init qcm_init(void) -{ - printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" - DRIVER_DESC "\n"); - - return usbvideo_register( - &cams, - MAX_CAMERAS, - sizeof(struct qcm), - "QCM", - &qcm_driver, - THIS_MODULE, - qcm_table); -} - -static void __exit qcm_exit(void) -{ - usbvideo_Deregister(&cams); -} - -module_param(size, int, 0); -MODULE_PARM_DESC(size, "Initial Size 0: 160x120 1: 320x240"); -module_param(colour, int, 0); -MODULE_PARM_DESC(colour, "Initial colour"); -module_param(hue, int, 0); -MODULE_PARM_DESC(hue, "Initial hue"); -module_param(brightness, int, 0); -MODULE_PARM_DESC(brightness, "Initial brightness"); -module_param(contrast, int, 0); -MODULE_PARM_DESC(contrast, "Initial contrast"); -module_param(whiteness, int, 0); -MODULE_PARM_DESC(whiteness, "Initial whiteness"); - -#ifdef CONFIG_USB_DEBUG -module_param(debug, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)"); -#endif - -module_init(qcm_init); -module_exit(qcm_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jaya Kumar"); -MODULE_DESCRIPTION("QCM USB Camera"); -MODULE_SUPPORTED_DEVICE("QCM USB Camera"); diff --git a/drivers/media/video/usbvideo/quickcam_messenger.h b/drivers/media/video/usbvideo/quickcam_messenger.h deleted file mode 100644 index 17ace39..0000000 --- a/drivers/media/video/usbvideo/quickcam_messenger.h +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef quickcam_messenger_h -#define quickcam_messenger_h - -#ifndef CONFIG_INPUT -/* if we're not using input we dummy out these functions */ -#define qcm_register_input(...) -#define qcm_unregister_input(...) -#define qcm_report_buttonstat(...) -#define qcm_setup_input_int(...) 0 -#define qcm_stop_int_data(...) -#define qcm_alloc_int_urb(...) 0 -#define qcm_free_int(...) -#endif - - -#define CHECK_RET(ret, expr) \ - if ((ret = expr) < 0) return ret - -/* Control Registers for the STVV6422 ASIC - * - this define is taken from the qc-usb-messenger code - */ -#define STV_ISO_ENABLE 0x1440 -#define ISOC_PACKET_SIZE 1023 - -/* Chip identification number including revision indicator */ -#define CMOS_SENSOR_IDREV 0xE00A - -struct rgb { - u8 b; - u8 g; - u8 r; - u8 b2; - u8 g2; - u8 r2; -}; - -struct bayL0 { - u8 g; - u8 r; -}; - -struct bayL1 { - u8 b; - u8 g; -}; - -struct cam_size { - u16 width; - u16 height; - u8 cmd; -}; - -static const struct cam_size camera_sizes[] = { - { 160, 120, 0xf }, - { 320, 240, 0x2 }, -}; - -enum frame_sizes { - SIZE_160X120 = 0, - SIZE_320X240 = 1, -}; - -#define MAX_FRAME_SIZE SIZE_320X240 - -struct qcm { - u16 colour; - u16 hue; - u16 brightness; - u16 contrast; - u16 whiteness; - - u8 size; - int height; - int width; - u8 *scratch; - struct urb *button_urb; - u8 button_sts; - u8 button_sts_buf; - -#ifdef CONFIG_INPUT - struct input_dev *input; - char input_physname[64]; -#endif -}; - -struct regval { - u16 reg; - u8 val; -}; -/* this table is derived from the -qc-usb-messenger code */ -static const struct regval regval_table[] = { - { STV_ISO_ENABLE, 0x00 }, - { 0x1436, 0x00 }, { 0x1432, 0x03 }, - { 0x143a, 0xF9 }, { 0x0509, 0x38 }, - { 0x050a, 0x38 }, { 0x050b, 0x38 }, - { 0x050c, 0x2A }, { 0x050d, 0x01 }, - { 0x1431, 0x00 }, { 0x1433, 0x34 }, - { 0x1438, 0x18 }, { 0x1439, 0x00 }, - { 0x143b, 0x05 }, { 0x143c, 0x00 }, - { 0x143e, 0x01 }, { 0x143d, 0x00 }, - { 0x1442, 0xe2 }, { 0x1500, 0xd0 }, - { 0x1500, 0xd0 }, { 0x1500, 0x50 }, - { 0x1501, 0xaf }, { 0x1502, 0xc2 }, - { 0x1503, 0x45 }, { 0x1505, 0x02 }, - { 0x150e, 0x8e }, { 0x150f, 0x37 }, - { 0x15c0, 0x00 }, -}; - -static const unsigned char marker[] = { 0x00, 0xff, 0x00, 0xFF }; - -#endif /* quickcam_messenger_h */ diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 27a79f0..a350fad 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -606,6 +606,26 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .set = uvc_ctrl_set_zoom, }, { + .id = V4L2_CID_PAN_ABSOLUTE, + .name = "Pan (Absolute)", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL, + .size = 32, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_TILT_ABSOLUTE, + .name = "Tilt (Absolute)", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL, + .size = 32, + .offset = 32, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { .id = V4L2_CID_PRIVACY, .name = "Privacy", .entity = UVC_GUID_UVC_CAMERA, @@ -623,7 +643,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id) { - return ctrl->data + id * ctrl->info->size; + return ctrl->uvc_data + id * ctrl->info->size; } static inline int uvc_test_bit(const __u8 *data, int bit) @@ -678,6 +698,14 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping, int offset = mapping->offset; __u8 mask; + /* According to the v4l2 spec, writing any value to a button control + * should result in the action belonging to the button control being + * triggered. UVC devices however want to see a 1 written -> override + * value. + */ + if (mapping->v4l2_type == V4L2_CTRL_TYPE_BUTTON) + value = -1; + data += offset / 8; offset &= 7; @@ -1265,13 +1293,15 @@ int uvc_ctrl_resume_device(struct uvc_device *dev) * Control and mapping handling */ -static void uvc_ctrl_add_ctrl(struct uvc_device *dev, +static int uvc_ctrl_add_ctrl(struct uvc_device *dev, struct uvc_control_info *info) { struct uvc_entity *entity; struct uvc_control *ctrl = NULL; - int ret, found = 0; + int ret = 0, found = 0; unsigned int i; + u8 *uvc_info; + u8 *uvc_data; list_for_each_entry(entity, &dev->entities, list) { if (!uvc_entity_match_guid(entity, info->entity)) @@ -1290,56 +1320,69 @@ static void uvc_ctrl_add_ctrl(struct uvc_device *dev, } if (!found) - return; + return 0; + + uvc_data = kmalloc(info->size * UVC_CTRL_DATA_LAST + 1, GFP_KERNEL); + if (uvc_data == NULL) + return -ENOMEM; + + uvc_info = uvc_data + info->size * UVC_CTRL_DATA_LAST; if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) { /* Check if the device control information and length match * the user supplied information. */ - __u32 flags; - __le16 size; - __u8 inf; - ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, - dev->intfnum, info->selector, (__u8 *)&size, 2); + dev->intfnum, info->selector, uvc_data, 2); if (ret < 0) { uvc_trace(UVC_TRACE_CONTROL, "GET_LEN failed on control %pUl/%u (%d).\n", info->entity, info->selector, ret); - return; + goto done; } - if (info->size != le16_to_cpu(size)) { + if (info->size != le16_to_cpu(*(__le16 *)uvc_data)) { uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u size " "doesn't match user supplied value.\n", info->entity, info->selector); - return; + ret = -EINVAL; + goto done; } ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, - dev->intfnum, info->selector, &inf, 1); + dev->intfnum, info->selector, uvc_info, 1); if (ret < 0) { uvc_trace(UVC_TRACE_CONTROL, "GET_INFO failed on control %pUl/%u (%d).\n", info->entity, info->selector, ret); - return; + goto done; } - flags = info->flags; - if (((flags & UVC_CONTROL_GET_CUR) && !(inf & (1 << 0))) || - ((flags & UVC_CONTROL_SET_CUR) && !(inf & (1 << 1)))) { + if (((info->flags & UVC_CONTROL_GET_CUR) && + !(*uvc_info & UVC_CONTROL_CAP_GET)) || + ((info->flags & UVC_CONTROL_SET_CUR) && + !(*uvc_info & UVC_CONTROL_CAP_SET))) { uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u flags " "don't match supported operations.\n", info->entity, info->selector); - return; + ret = -EINVAL; + goto done; } } ctrl->info = info; - ctrl->data = kmalloc(ctrl->info->size * UVC_CTRL_DATA_LAST, GFP_KERNEL); + ctrl->uvc_data = uvc_data; + ctrl->uvc_info = uvc_info; + uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s " "entity %u\n", ctrl->info->entity, ctrl->info->selector, dev->udev->devpath, entity->id); + +done: + if (ret < 0) + kfree(uvc_data); + + return ret; } /* @@ -1572,12 +1615,34 @@ void uvc_ctrl_cleanup_device(struct uvc_device *dev) list_for_each_entry(entity, &dev->entities, list) { for (i = 0; i < entity->ncontrols; ++i) - kfree(entity->controls[i].data); + kfree(entity->controls[i].uvc_data); kfree(entity->controls); } } +void uvc_ctrl_cleanup(void) +{ + struct uvc_control_info *info; + struct uvc_control_info *ni; + struct uvc_control_mapping *mapping; + struct uvc_control_mapping *nm; + + list_for_each_entry_safe(info, ni, &uvc_driver.controls, list) { + if (!(info->flags & UVC_CONTROL_EXTENSION)) + continue; + + list_for_each_entry_safe(mapping, nm, &info->mappings, list) { + list_del(&mapping->list); + kfree(mapping->menu_info); + kfree(mapping); + } + + list_del(&info->list); + kfree(info); + } +} + void uvc_ctrl_init(void) { struct uvc_control_info *ctrl = uvc_ctrls; diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index 838b56f..7eaf99b 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -637,14 +637,13 @@ static int uvc_parse_streaming(struct uvc_device *dev, } streaming->header.bControlSize = n; - streaming->header.bmaControls = kmalloc(p*n, GFP_KERNEL); + streaming->header.bmaControls = kmemdup(&buffer[size], p * n, + GFP_KERNEL); if (streaming->header.bmaControls == NULL) { ret = -ENOMEM; goto error; } - memcpy(streaming->header.bmaControls, &buffer[size], p*n); - buflen -= buffer[0]; buffer += buffer[0]; @@ -2174,6 +2173,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS }, + /* Manta MM-353 Plako */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x18ec, + .idProduct = 0x3188, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* FSC WebCam V30S */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2261,6 +2269,7 @@ static int __init uvc_init(void) static void __exit uvc_cleanup(void) { usb_deregister(&uvc_driver.driver); + uvc_ctrl_cleanup(); } module_init(uvc_init); diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 7c9ab29..86db326 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -29,6 +29,71 @@ #include "uvcvideo.h" /* ------------------------------------------------------------------------ + * UVC ioctls + */ +static int uvc_ioctl_ctrl_map(struct uvc_xu_control_mapping *xmap, int old) +{ + struct uvc_control_mapping *map; + unsigned int size; + int ret; + + map = kzalloc(sizeof *map, GFP_KERNEL); + if (map == NULL) + return -ENOMEM; + + map->id = xmap->id; + memcpy(map->name, xmap->name, sizeof map->name); + memcpy(map->entity, xmap->entity, sizeof map->entity); + map->selector = xmap->selector; + map->size = xmap->size; + map->offset = xmap->offset; + map->v4l2_type = xmap->v4l2_type; + map->data_type = xmap->data_type; + + switch (xmap->v4l2_type) { + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_BOOLEAN: + case V4L2_CTRL_TYPE_BUTTON: + break; + + case V4L2_CTRL_TYPE_MENU: + if (old) { + ret = -EINVAL; + goto done; + } + + size = xmap->menu_count * sizeof(*map->menu_info); + map->menu_info = kmalloc(size, GFP_KERNEL); + if (map->menu_info == NULL) { + ret = -ENOMEM; + goto done; + } + + if (copy_from_user(map->menu_info, xmap->menu_info, size)) { + ret = -EFAULT; + goto done; + } + + map->menu_count = xmap->menu_count; + break; + + default: + ret = -EINVAL; + goto done; + } + + ret = uvc_ctrl_add_mapping(map); + +done: + if (ret < 0) { + kfree(map->menu_info); + kfree(map); + } + + return ret; +} + +/* ------------------------------------------------------------------------ * V4L2 interface */ @@ -451,7 +516,7 @@ static int uvc_v4l2_open(struct file *file) static int uvc_v4l2_release(struct file *file) { - struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_fh *handle = file->private_data; struct uvc_streaming *stream = handle->stream; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n"); @@ -482,7 +547,7 @@ static int uvc_v4l2_release(struct file *file) static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); - struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_fh *handle = file->private_data; struct uvc_video_chain *chain = handle->chain; struct uvc_streaming *stream = handle->stream; long ret = 0; @@ -963,6 +1028,9 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (!capable(CAP_SYS_ADMIN)) return -EPERM; + if (xinfo->size == 0) + return -EINVAL; + info = kzalloc(sizeof *info, GFP_KERNEL); if (info == NULL) return -ENOMEM; @@ -974,7 +1042,8 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) info->flags = xinfo->flags; info->flags |= UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX | - UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF; + UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF | + UVC_CONTROL_EXTENSION; ret = uvc_ctrl_add_info(info); if (ret < 0) @@ -982,32 +1051,12 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) break; } + case UVCIOC_CTRL_MAP_OLD: case UVCIOC_CTRL_MAP: - { - struct uvc_xu_control_mapping *xmap = arg; - struct uvc_control_mapping *map; - if (!capable(CAP_SYS_ADMIN)) return -EPERM; - map = kzalloc(sizeof *map, GFP_KERNEL); - if (map == NULL) - return -ENOMEM; - - map->id = xmap->id; - memcpy(map->name, xmap->name, sizeof map->name); - memcpy(map->entity, xmap->entity, sizeof map->entity); - map->selector = xmap->selector; - map->size = xmap->size; - map->offset = xmap->offset; - map->v4l2_type = xmap->v4l2_type; - map->data_type = xmap->data_type; - - ret = uvc_ctrl_add_mapping(map); - if (ret < 0) - kfree(map); - break; - } + return uvc_ioctl_ctrl_map(arg, cmd == UVCIOC_CTRL_MAP_OLD); case UVCIOC_CTRL_GET: return uvc_xu_ctrl_query(chain, arg, 0); @@ -1067,7 +1116,7 @@ static const struct vm_operations_struct uvc_vm_ops = { static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) { - struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_fh *handle = file->private_data; struct uvc_streaming *stream = handle->stream; struct uvc_video_queue *queue = &stream->queue; struct uvc_buffer *uninitialized_var(buffer); @@ -1122,7 +1171,7 @@ done: static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait) { - struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_fh *handle = file->private_data; struct uvc_streaming *stream = handle->stream; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n"); diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index d1f8840..ac27245 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -27,6 +27,8 @@ #define UVC_CONTROL_RESTORE (1 << 6) /* Control can be updated by the camera. */ #define UVC_CONTROL_AUTO_UPDATE (1 << 7) +/* Control is an extension unit control. */ +#define UVC_CONTROL_EXTENSION (1 << 8) #define UVC_CONTROL_GET_RANGE (UVC_CONTROL_GET_CUR | UVC_CONTROL_GET_MIN | \ UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \ @@ -40,6 +42,15 @@ struct uvc_xu_control_info { __u32 flags; }; +struct uvc_menu_info { + __u32 value; + __u8 name[32]; +}; + +struct uvc_xu_control_mapping_old { + __u8 reserved[64]; +}; + struct uvc_xu_control_mapping { __u32 id; __u8 name[32]; @@ -50,6 +61,11 @@ struct uvc_xu_control_mapping { __u8 offset; enum v4l2_ctrl_type v4l2_type; __u32 data_type; + + struct uvc_menu_info __user *menu_info; + __u32 menu_count; + + __u32 reserved[4]; }; struct uvc_xu_control { @@ -60,6 +76,7 @@ struct uvc_xu_control { }; #define UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info) +#define UVCIOC_CTRL_MAP_OLD _IOWR('U', 2, struct uvc_xu_control_mapping_old) #define UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping) #define UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control) #define UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control) @@ -179,30 +196,6 @@ struct uvc_device; /* TODO: Put the most frequently accessed fields at the beginning of * structures to maximize cache efficiency. */ -struct uvc_streaming_control { - __u16 bmHint; - __u8 bFormatIndex; - __u8 bFrameIndex; - __u32 dwFrameInterval; - __u16 wKeyFrameRate; - __u16 wPFrameRate; - __u16 wCompQuality; - __u16 wCompWindowSize; - __u16 wDelay; - __u32 dwMaxVideoFrameSize; - __u32 dwMaxPayloadTransferSize; - __u32 dwClockFrequency; - __u8 bmFramingInfo; - __u8 bPreferedVersion; - __u8 bMinVersion; - __u8 bMaxVersion; -}; - -struct uvc_menu_info { - __u32 value; - __u8 name[32]; -}; - struct uvc_control_info { struct list_head list; struct list_head mappings; @@ -250,7 +243,8 @@ struct uvc_control { modified : 1, cached : 1; - __u8 *data; + __u8 *uvc_data; + __u8 *uvc_info; }; struct uvc_format_desc { @@ -625,6 +619,7 @@ extern int uvc_ctrl_init_device(struct uvc_device *dev); extern void uvc_ctrl_cleanup_device(struct uvc_device *dev); extern int uvc_ctrl_resume_device(struct uvc_device *dev); extern void uvc_ctrl_init(void); +extern void uvc_ctrl_cleanup(void); extern int uvc_ctrl_begin(struct uvc_video_chain *chain); extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback); diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 0ca7ec9..9e89bf6 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -410,7 +410,7 @@ static int __video_register_device(struct video_device *vdev, int type, int nr, int minor_offset = 0; int minor_cnt = VIDEO_NUM_DEVICES; const char *name_base; - void *priv = video_get_drvdata(vdev); + void *priv = vdev->dev.p; /* A minor value of -1 marks this video device as never having been registered */ @@ -536,9 +536,9 @@ static int __video_register_device(struct video_device *vdev, int type, int nr, /* Part 4: register the device with sysfs */ memset(&vdev->dev, 0, sizeof(vdev->dev)); - /* The memset above cleared the device's drvdata, so + /* The memset above cleared the device's device_private, so put back the copy we made earlier. */ - video_set_drvdata(vdev, priv); + vdev->dev.p = priv; vdev->dev.class = &video_class; vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor); if (vdev->parent) diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index 7d33784..ce1595b 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -52,18 +52,18 @@ MODULE_LICENSE("GPL"); #define CALL(q, f, arg...) \ ((q->int_ops->f) ? q->int_ops->f(arg) : 0) -struct videobuf_buffer *videobuf_alloc(struct videobuf_queue *q) +struct videobuf_buffer *videobuf_alloc_vb(struct videobuf_queue *q) { struct videobuf_buffer *vb; BUG_ON(q->msize < sizeof(*vb)); - if (!q->int_ops || !q->int_ops->alloc) { + if (!q->int_ops || !q->int_ops->alloc_vb) { printk(KERN_ERR "No specific ops defined!\n"); BUG(); } - vb = q->int_ops->alloc(q->msize); + vb = q->int_ops->alloc_vb(q->msize); if (NULL != vb) { init_waitqueue_head(&vb->done); vb->magic = MAGIC_BUFFER; @@ -71,7 +71,7 @@ struct videobuf_buffer *videobuf_alloc(struct videobuf_queue *q) return vb; } -EXPORT_SYMBOL_GPL(videobuf_alloc); +EXPORT_SYMBOL_GPL(videobuf_alloc_vb); #define WAITON_CONDITION (vb->state != VIDEOBUF_ACTIVE &&\ vb->state != VIDEOBUF_QUEUED) @@ -195,6 +195,45 @@ int videobuf_queue_is_busy(struct videobuf_queue *q) } EXPORT_SYMBOL_GPL(videobuf_queue_is_busy); +/** + * __videobuf_free() - free all the buffers and their control structures + * + * This function can only be called if streaming/reading is off, i.e. no buffers + * are under control of the driver. + */ +/* Locking: Caller holds q->vb_lock */ +static int __videobuf_free(struct videobuf_queue *q) +{ + int i; + + dprintk(1, "%s\n", __func__); + if (!q) + return 0; + + if (q->streaming || q->reading) { + dprintk(1, "Cannot free buffers when streaming or reading\n"); + return -EBUSY; + } + + MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); + + for (i = 0; i < VIDEO_MAX_FRAME; i++) + if (q->bufs[i] && q->bufs[i]->map) { + dprintk(1, "Cannot free mmapped buffers\n"); + return -EBUSY; + } + + for (i = 0; i < VIDEO_MAX_FRAME; i++) { + if (NULL == q->bufs[i]) + continue; + q->ops->buf_release(q, q->bufs[i]); + kfree(q->bufs[i]); + q->bufs[i] = NULL; + } + + return 0; +} + /* Locking: Caller holds q->vb_lock */ void videobuf_queue_cancel(struct videobuf_queue *q) { @@ -308,36 +347,11 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, b->sequence = vb->field_count >> 1; } -/* Locking: Caller holds q->vb_lock */ -static int __videobuf_mmap_free(struct videobuf_queue *q) -{ - int i; - - if (!q) - return 0; - - MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - - for (i = 0; i < VIDEO_MAX_FRAME; i++) - if (q->bufs[i] && q->bufs[i]->map) - return -EBUSY; - - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - if (NULL == q->bufs[i]) - continue; - q->ops->buf_release(q, q->bufs[i]); - kfree(q->bufs[i]); - q->bufs[i] = NULL; - } - - return 0; -} - int videobuf_mmap_free(struct videobuf_queue *q) { int ret; mutex_lock(&q->vb_lock); - ret = __videobuf_mmap_free(q); + ret = __videobuf_free(q); mutex_unlock(&q->vb_lock); return ret; } @@ -353,13 +367,13 @@ int __videobuf_mmap_setup(struct videobuf_queue *q, MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - err = __videobuf_mmap_free(q); + err = __videobuf_free(q); if (0 != err) return err; /* Allocate and initialize buffers */ for (i = 0; i < bcount; i++) { - q->bufs[i] = videobuf_alloc(q); + q->bufs[i] = videobuf_alloc_vb(q); if (NULL == q->bufs[i]) break; @@ -766,7 +780,7 @@ static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q, MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); /* setup stuff */ - q->read_buf = videobuf_alloc(q); + q->read_buf = videobuf_alloc_vb(q); if (NULL == q->read_buf) return -ENOMEM; @@ -871,7 +885,7 @@ ssize_t videobuf_read_one(struct videobuf_queue *q, if (NULL == q->read_buf) { /* need to capture a new frame */ retval = -ENOMEM; - q->read_buf = videobuf_alloc(q); + q->read_buf = videobuf_alloc_vb(q); dprintk(1, "video alloc=0x%p\n", q->read_buf); if (NULL == q->read_buf) @@ -970,7 +984,7 @@ static void __videobuf_read_stop(struct videobuf_queue *q) int i; videobuf_queue_cancel(q); - __videobuf_mmap_free(q); + __videobuf_free(q); INIT_LIST_HEAD(&q->stream); for (i = 0; i < VIDEO_MAX_FRAME; i++) { if (NULL == q->bufs[i]) diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c index 74730c6..372b87e 100644 --- a/drivers/media/video/videobuf-dma-contig.c +++ b/drivers/media/video/videobuf-dma-contig.c @@ -190,7 +190,7 @@ static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem, return ret; } -static struct videobuf_buffer *__videobuf_alloc(size_t size) +static struct videobuf_buffer *__videobuf_alloc_vb(size_t size) { struct videobuf_dma_contig_memory *mem; struct videobuf_buffer *vb; @@ -280,8 +280,6 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, return -ENOMEM; buf->map = map; - map->start = vma->vm_start; - map->end = vma->vm_end; map->q = q; buf->baddr = vma->vm_start; @@ -338,7 +336,7 @@ error: static struct videobuf_qtype_ops qops = { .magic = MAGIC_QTYPE_OPS, - .alloc = __videobuf_alloc, + .alloc_vb = __videobuf_alloc_vb, .iolock = __videobuf_iolock, .mmap_mapper = __videobuf_mmap_mapper, .vaddr = __videobuf_to_vaddr, diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c index 8359e6b..06f9a9c 100644 --- a/drivers/media/video/videobuf-dma-sg.c +++ b/drivers/media/video/videobuf-dma-sg.c @@ -57,7 +57,13 @@ MODULE_LICENSE("GPL"); /* --------------------------------------------------------------------- */ -struct scatterlist *videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages) +/* + * Return a scatterlist for some page-aligned vmalloc()'ed memory + * block (NULL on errors). Memory for the scatterlist is allocated + * using kmalloc. The caller must free the memory. + */ +static struct scatterlist *videobuf_vmalloc_to_sg(unsigned char *virt, + int nr_pages) { struct scatterlist *sglist; struct page *pg; @@ -81,10 +87,14 @@ err: vfree(sglist); return NULL; } -EXPORT_SYMBOL_GPL(videobuf_vmalloc_to_sg); -struct scatterlist *videobuf_pages_to_sg(struct page **pages, int nr_pages, - int offset) +/* + * Return a scatterlist for a an array of userpages (NULL on errors). + * Memory for the scatterlist is allocated using kmalloc. The caller + * must free the memory. + */ +static struct scatterlist *videobuf_pages_to_sg(struct page **pages, + int nr_pages, int offset) { struct scatterlist *sglist; int i; @@ -201,17 +211,17 @@ int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, dprintk(1, "init kernel [%d pages]\n", nr_pages); dma->direction = direction; - dma->vmalloc = vmalloc_32(nr_pages << PAGE_SHIFT); - if (NULL == dma->vmalloc) { + dma->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT); + if (NULL == dma->vaddr) { dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages); return -ENOMEM; } dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n", - (unsigned long)dma->vmalloc, + (unsigned long)dma->vaddr, nr_pages << PAGE_SHIFT); - memset(dma->vmalloc, 0, nr_pages << PAGE_SHIFT); + memset(dma->vaddr, 0, nr_pages << PAGE_SHIFT); dma->nr_pages = nr_pages; return 0; @@ -235,7 +245,7 @@ int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction, } EXPORT_SYMBOL_GPL(videobuf_dma_init_overlay); -int videobuf_dma_map(struct videobuf_queue *q, struct videobuf_dmabuf *dma) +int videobuf_dma_map(struct device *dev, struct videobuf_dmabuf *dma) { MAGIC_CHECK(dma->magic, MAGIC_DMABUF); BUG_ON(0 == dma->nr_pages); @@ -244,8 +254,8 @@ int videobuf_dma_map(struct videobuf_queue *q, struct videobuf_dmabuf *dma) dma->sglist = videobuf_pages_to_sg(dma->pages, dma->nr_pages, dma->offset); } - if (dma->vmalloc) { - dma->sglist = videobuf_vmalloc_to_sg(dma->vmalloc, + if (dma->vaddr) { + dma->sglist = videobuf_vmalloc_to_sg(dma->vaddr, dma->nr_pages); } if (dma->bus_addr) { @@ -263,7 +273,7 @@ int videobuf_dma_map(struct videobuf_queue *q, struct videobuf_dmabuf *dma) return -ENOMEM; } if (!dma->bus_addr) { - dma->sglen = dma_map_sg(q->dev, dma->sglist, + dma->sglen = dma_map_sg(dev, dma->sglist, dma->nr_pages, dma->direction); if (0 == dma->sglen) { printk(KERN_WARNING @@ -279,14 +289,14 @@ int videobuf_dma_map(struct videobuf_queue *q, struct videobuf_dmabuf *dma) } EXPORT_SYMBOL_GPL(videobuf_dma_map); -int videobuf_dma_unmap(struct videobuf_queue *q, struct videobuf_dmabuf *dma) +int videobuf_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma) { MAGIC_CHECK(dma->magic, MAGIC_DMABUF); if (!dma->sglen) return 0; - dma_unmap_sg(q->dev, dma->sglist, dma->sglen, dma->direction); + dma_unmap_sg(dev, dma->sglist, dma->sglen, dma->direction); vfree(dma->sglist); dma->sglist = NULL; @@ -309,8 +319,8 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma) dma->pages = NULL; } - vfree(dma->vmalloc); - dma->vmalloc = NULL; + vfree(dma->vaddr); + dma->vaddr = NULL; if (dma->bus_addr) dma->bus_addr = 0; @@ -322,28 +332,6 @@ EXPORT_SYMBOL_GPL(videobuf_dma_free); /* --------------------------------------------------------------------- */ -int videobuf_sg_dma_map(struct device *dev, struct videobuf_dmabuf *dma) -{ - struct videobuf_queue q; - - q.dev = dev; - - return videobuf_dma_map(&q, dma); -} -EXPORT_SYMBOL_GPL(videobuf_sg_dma_map); - -int videobuf_sg_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma) -{ - struct videobuf_queue q; - - q.dev = dev; - - return videobuf_dma_unmap(&q, dma); -} -EXPORT_SYMBOL_GPL(videobuf_sg_dma_unmap); - -/* --------------------------------------------------------------------- */ - static void videobuf_vm_open(struct vm_area_struct *vma) { struct videobuf_mapping *map = vma->vm_private_data; @@ -428,7 +416,7 @@ static const struct vm_operations_struct videobuf_vm_ops = { struct videobuf_dma_sg_memory */ -static struct videobuf_buffer *__videobuf_alloc(size_t size) +static struct videobuf_buffer *__videobuf_alloc_vb(size_t size) { struct videobuf_dma_sg_memory *mem; struct videobuf_buffer *vb; @@ -456,7 +444,7 @@ static void *__videobuf_to_vaddr(struct videobuf_buffer *buf) MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); - return mem->dma.vmalloc; + return mem->dma.vaddr; } static int __videobuf_iolock(struct videobuf_queue *q, @@ -520,7 +508,7 @@ static int __videobuf_iolock(struct videobuf_queue *q, default: BUG(); } - err = videobuf_dma_map(q, &mem->dma); + err = videobuf_dma_map(q->dev, &mem->dma); if (0 != err) return err; @@ -620,8 +608,6 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, } map->count = 1; - map->start = vma->vm_start; - map->end = vma->vm_end; map->q = q; vma->vm_ops = &videobuf_vm_ops; vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED; @@ -638,7 +624,7 @@ done: static struct videobuf_qtype_ops sg_ops = { .magic = MAGIC_QTYPE_OPS, - .alloc = __videobuf_alloc, + .alloc_vb = __videobuf_alloc_vb, .iolock = __videobuf_iolock, .sync = __videobuf_sync, .mmap_mapper = __videobuf_mmap_mapper, @@ -654,7 +640,7 @@ void *videobuf_sg_alloc(size_t size) q.msize = size; - return videobuf_alloc(&q); + return videobuf_alloc_vb(&q); } EXPORT_SYMBOL_GPL(videobuf_sg_alloc); diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c index 583728f..e7fe31d 100644 --- a/drivers/media/video/videobuf-vmalloc.c +++ b/drivers/media/video/videobuf-vmalloc.c @@ -102,10 +102,10 @@ static void videobuf_vm_close(struct vm_area_struct *vma) called with IRQ's disabled */ dprintk(1, "%s: buf[%d] freeing (%p)\n", - __func__, i, mem->vmalloc); + __func__, i, mem->vaddr); - vfree(mem->vmalloc); - mem->vmalloc = NULL; + vfree(mem->vaddr); + mem->vaddr = NULL; } q->bufs[i]->map = NULL; @@ -135,7 +135,7 @@ static const struct vm_operations_struct videobuf_vm_ops = { struct videobuf_dma_sg_memory */ -static struct videobuf_buffer *__videobuf_alloc(size_t size) +static struct videobuf_buffer *__videobuf_alloc_vb(size_t size) { struct videobuf_vmalloc_memory *mem; struct videobuf_buffer *vb; @@ -170,7 +170,7 @@ static int __videobuf_iolock(struct videobuf_queue *q, dprintk(1, "%s memory method MMAP\n", __func__); /* All handling should be done by __videobuf_mmap_mapper() */ - if (!mem->vmalloc) { + if (!mem->vaddr) { printk(KERN_ERR "memory is not alloced/mmapped.\n"); return -EINVAL; } @@ -189,13 +189,13 @@ static int __videobuf_iolock(struct videobuf_queue *q, * read() method. */ - mem->vmalloc = vmalloc_user(pages); - if (!mem->vmalloc) { + mem->vaddr = vmalloc_user(pages); + if (!mem->vaddr) { printk(KERN_ERR "vmalloc (%d pages) failed\n", pages); return -ENOMEM; } dprintk(1, "vmalloc is at addr %p (%d pages)\n", - mem->vmalloc, pages); + mem->vaddr, pages); #if 0 int rc; @@ -245,8 +245,6 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, return -ENOMEM; buf->map = map; - map->start = vma->vm_start; - map->end = vma->vm_end; map->q = q; buf->baddr = vma->vm_start; @@ -256,18 +254,18 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); pages = PAGE_ALIGN(vma->vm_end - vma->vm_start); - mem->vmalloc = vmalloc_user(pages); - if (!mem->vmalloc) { + mem->vaddr = vmalloc_user(pages); + if (!mem->vaddr) { printk(KERN_ERR "vmalloc (%d pages) failed\n", pages); goto error; } - dprintk(1, "vmalloc is at addr %p (%d pages)\n", mem->vmalloc, pages); + dprintk(1, "vmalloc is at addr %p (%d pages)\n", mem->vaddr, pages); /* Try to remap memory */ - retval = remap_vmalloc_range(vma, mem->vmalloc, 0); + retval = remap_vmalloc_range(vma, mem->vaddr, 0); if (retval < 0) { printk(KERN_ERR "mmap: remap failed with error %d. ", retval); - vfree(mem->vmalloc); + vfree(mem->vaddr); goto error; } @@ -293,7 +291,7 @@ error: static struct videobuf_qtype_ops qops = { .magic = MAGIC_QTYPE_OPS, - .alloc = __videobuf_alloc, + .alloc_vb = __videobuf_alloc_vb, .iolock = __videobuf_iolock, .mmap_mapper = __videobuf_mmap_mapper, .vaddr = videobuf_to_vmalloc, @@ -319,7 +317,7 @@ void *videobuf_to_vmalloc(struct videobuf_buffer *buf) BUG_ON(!mem); MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); - return mem->vmalloc; + return mem->vaddr; } EXPORT_SYMBOL_GPL(videobuf_to_vmalloc); @@ -341,8 +339,8 @@ void videobuf_vmalloc_free(struct videobuf_buffer *buf) MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); - vfree(mem->vmalloc); - mem->vmalloc = NULL; + vfree(mem->vaddr); + mem->vaddr = NULL; return; } diff --git a/drivers/media/video/w9968cf.c b/drivers/media/video/w9968cf.c deleted file mode 100644 index d807eea..0000000 --- a/drivers/media/video/w9968cf.c +++ /dev/null @@ -1,3620 +0,0 @@ -/*************************************************************************** - * 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/videodev.h> -#include <media/v4l2-ioctl.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 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 */ - -module_param(simcams, ushort, 0644); -module_param_array(video_nr, short, ¶m_nv[0], 0444); -module_param_array(packet_size, uint, ¶m_nv[1], 0444); -module_param_array(max_buffers, ushort, ¶m_nv[2], 0444); -module_param_array(double_buffer, bool, ¶m_nv[3], 0444); -module_param_array(clamping, bool, ¶m_nv[4], 0444); -module_param_array(filter_type, ushort, ¶m_nv[5], 0444); -module_param_array(largeview, bool, ¶m_nv[6], 0444); -module_param_array(decompression, ushort, ¶m_nv[7], 0444); -module_param_array(upscaling, bool, ¶m_nv[8], 0444); -module_param_array(force_palette, ushort, ¶m_nv[9], 0444); -module_param_array(force_rgb, ushort, ¶m_nv[10], 0444); -module_param_array(autobright, bool, ¶m_nv[11], 0444); -module_param_array(autoexp, bool, ¶m_nv[12], 0444); -module_param_array(lightfreq, ushort, ¶m_nv[13], 0444); -module_param_array(bandingfilter, bool, ¶m_nv[14], 0444); -module_param_array(clockdiv, short, ¶m_nv[15], 0444); -module_param_array(backlight, bool, ¶m_nv[16], 0444); -module_param_array(mirror, bool, ¶m_nv[17], 0444); -module_param_array(monochrome, bool, ¶m_nv[18], 0444); -module_param_array(brightness, uint, ¶m_nv[19], 0444); -module_param_array(hue, uint, ¶m_nv[20], 0444); -module_param_array(colour, uint, ¶m_nv[21], 0444); -module_param_array(contrast, uint, ¶m_nv[22], 0444); -module_param_array(whiteness, uint, ¶m_nv[23], 0444); -#ifdef W9968CF_DEBUG -module_param(debug, ushort, 0644); -module_param(specific_debug, bool, 0644); -#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 const struct v4l2_file_operations w9968cf_fops; -static int w9968cf_open(struct file *); -static int w9968cf_release(struct file *); -static int w9968cf_mmap(struct file *, struct vm_area_struct *); -static long w9968cf_ioctl(struct file *, unsigned, unsigned long); -static ssize_t w9968cf_read(struct file *, char __user *, size_t, loff_t *); -static long w9968cf_v4l_ioctl(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); - -/* 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*); - -/* 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*, u32 *w, u32 *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 (too much data)" }, - { -EPROTO, "Bit-stuff error (bad cable?)" }, - { -EILSEQ, "CRC/Timeout" }, - { -ETIME, "Device does not respond to token" }, - { -ETIMEDOUT, "Device does not respond to command" }, - { -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 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); - if (!urb) { - for (j = 0; j < i; j++) - usb_free_urb(cam->urb[j]); - DBG(1, "Couldn't allocate the URB structures") - return -ENOMEM; - } - - cam->urb[i] = urb; - 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 v4l2_device *v4l2_dev = i2c_get_adapdata(adapter); - struct w9968cf_device *cam = to_cam(v4l2_dev); - u8 i; - int err = 0; - - 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) - for (i = 1; i <= W9968CF_I2C_RW_RETRIES; i++) { - err = w9968cf_i2c_adap_read_byte(cam, addr, - &data->byte); - if (err) { - if (w9968cf_smbus_refresh_bus(cam)) { - err = -EIO; - break; - } - } else - break; - } - } 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_init(struct w9968cf_device* cam) -{ - int err = 0; - - static struct i2c_algorithm algo = { - .smbus_xfer = w9968cf_i2c_smbus_xfer, - .functionality = w9968cf_i2c_func, - }; - - static struct i2c_adapter adap = { - .owner = THIS_MODULE, - .algo = &algo, - }; - - memcpy(&cam->i2c_adapter, &adap, sizeof(struct i2c_adapter)); - strcpy(cam->i2c_adapter.name, "w9968cf"); - cam->i2c_adapter.dev.parent = &cam->usbdev->dev; - i2c_set_adapdata(&cam->i2c_adapter, &cam->v4l2_dev); - - 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, &win.width, &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, u32 *width, u32 *height) -{ - unsigned int maxw, maxh, align; - - 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; - align = (cam->vpp_flag & VPP_DECOMPRESSION) ? 4 : 0; - - v4l_bound_align_image(width, cam->minwidth, maxw, align, - height, cam->minheight, maxh, align, 0); - - 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) -{ - int rc; - - rc = v4l2_subdev_call(cam->sensor_sd, core, ioctl, 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 (v4l2_i2c_subdev_addr(cam->sensor_sd)) { - 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 (%s). " - "Try to detach and attach this device again", - symbolic(camlist, cam->id), video_device_node_name(cam->v4ldev)) - 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: %s", - video_device_node_name(cam->v4ldev)) - - 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); - v4l2_device_unregister(&cam->v4l2_dev); - - mutex_unlock(&w9968cf_devlist_mutex); -} - - - -/**************************************************************************** - * Video4Linux interface * - ****************************************************************************/ - -static int w9968cf_open(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 -EAGAIN; - - 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 (%s). Make sure " - "it is loaded *before* (re)connecting the camera.", - symbolic(camlist, cam->id), - video_device_node_name(cam->v4ldev)) - mutex_unlock(&cam->dev_mutex); - up_read(&w9968cf_disconnect); - return -ENODEV; - } - - if (cam->users) { - DBG(2, "%s (%s) has been already occupied by '%s'", - symbolic(camlist, cam->id), - video_device_node_name(cam->v4ldev), 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', %s ...", - symbolic(camlist, cam->id), video_device_node_name(cam->v4ldev)) - - 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 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 long -w9968cf_ioctl(struct file *filp, - unsigned int cmd, unsigned long arg) -{ - struct w9968cf_device* cam; - long 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(filp, cmd, (void __user *)arg); - - mutex_unlock(&cam->fileop_mutex); - return err; -} - - -static long w9968cf_v4l_ioctl(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"); - 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, &win.width, - &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; - u32 w, h; - 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; - } - } - } - - w = mmap.width; h = mmap.height; - err = w9968cf_adjust_window_size(cam, &w, &h); - mmap.width = w; mmap.height = h; - if (err) { - 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 const struct v4l2_file_operations w9968cf_fops = { - .owner = THIS_MODULE, - .open = w9968cf_open, - .release = w9968cf_release, - .read = w9968cf_read, - .ioctl = w9968cf_ioctl, - .mmap = w9968cf_mmap, -}; - - - -/**************************************************************************** - * 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 */ - static unsigned short addrs[] = { - OV7xx0_SID, - OV6xx0_SID, - I2C_CLIENT_END - }; - - 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; - - err = v4l2_device_register(&intf->dev, &cam->v4l2_dev); - if (err) - goto fail0; - - mutex_init(&cam->dev_mutex); - mutex_lock(&cam->dev_mutex); - - cam->usbdev = udev; - - 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->fops = &w9968cf_fops; - cam->v4ldev->release = video_device_release; - video_set_drvdata(cam->v4ldev, cam); - cam->v4ldev->v4l2_dev = &cam->v4l2_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 %s", - video_device_node_name(cam->v4ldev)) - - /* 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); - cam->sensor_sd = v4l2_i2c_new_subdev(&cam->v4l2_dev, - &cam->i2c_adapter, - "ovcamchip", "ovcamchip", 0, addrs); - - usb_set_intfdata(intf, cam); - mutex_unlock(&cam->dev_mutex); - - err = w9968cf_sensor_init(cam); - 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); - v4l2_device_unregister(&cam->v4l2_dev); -fail0: - kfree(cam); - return err; -} - - -static void w9968cf_usb_disconnect(struct usb_interface* intf) -{ - struct w9968cf_device* cam = - (struct w9968cf_device*)usb_get_intfdata(intf); - - if (cam) { - down_write(&w9968cf_disconnect); - /* Prevent concurrent accesses to data */ - mutex_lock(&cam->dev_mutex); - - cam->disconnected = 1; - - DBG(2, "Disconnecting %s...", symbolic(camlist, cam->id)); - - v4l2_device_disconnect(&cam->v4l2_dev); - - wake_up_interruptible_all(&cam->open); - - if (cam->users) { - DBG(2, "The device is open (%s)! " - "Process name: %s. Deregistration and memory " - "deallocation are deferred on close.", - video_device_node_name(cam->v4ldev), 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); - up_write(&w9968cf_disconnect); - - if (!cam->users) { - kfree(cam); - } - } -} - - -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 ((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/drivers/media/video/w9968cf.h b/drivers/media/video/w9968cf.h deleted file mode 100644 index 73ad864..0000000 --- a/drivers/media/video/w9968cf.h +++ /dev/null @@ -1,333 +0,0 @@ -/*************************************************************************** - * 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 <linux/videodev2.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/param.h> -#include <linux/types.h> -#include <linux/rwsem.h> -#include <linux/mutex.h> - -#include <media/v4l2-device.h> -#include <media/ovcamchip.h> - -#include "w9968cf_vpp.h" - - -/**************************************************************************** - * Default values * - ****************************************************************************/ - -#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.34-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 { - enum w9968cf_model_id id; /* private device identifier */ - - struct v4l2_device v4l2_dev; - 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 v4l2_subdev *sensor_sd; - - /* 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 */ -}; - -static inline struct w9968cf_device *to_cam(struct v4l2_device *v4l2_dev) -{ - return container_of(v4l2_dev, struct w9968cf_device, v4l2_dev); -} - - -/**************************************************************************** - * 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) \ - v4l2_err(&cam->v4l2_dev, fmt "\n", ## args); \ - else if ((level) == 2 || (level) == 3) \ - v4l2_info(&cam->v4l2_dev, fmt "\n", ## args); \ - else if ((level) == 4) \ - v4l2_warn(&cam->v4l2_dev, fmt "\n", ## args); \ - else if ((level) >= 5) \ - v4l2_info(&cam->v4l2_dev, "[%s:%d] " fmt "\n", \ - __func__, __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", __func__, \ - __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...) \ -v4l2_info(&cam->v4l2_dev, "[%s:%d] " fmt "\n", __func__, __LINE__ , ## args); - -#undef PDBGG -#define PDBGG(fmt, args...) do {;} while(0); /* nothing: it's a placeholder */ - -#endif /* _W9968CF_H_ */ diff --git a/drivers/media/video/w9968cf_decoder.h b/drivers/media/video/w9968cf_decoder.h deleted file mode 100644 index 59decbf..0000000 --- a/drivers/media/video/w9968cf_decoder.h +++ /dev/null @@ -1,86 +0,0 @@ -/*************************************************************************** - * 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/drivers/media/video/w9968cf_vpp.h b/drivers/media/video/w9968cf_vpp.h deleted file mode 100644 index 88c9b6c..0000000 --- a/drivers/media/video/w9968cf_vpp.h +++ /dev/null @@ -1,40 +0,0 @@ -/*************************************************************************** - * 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/drivers/media/video/zc0301/Kconfig b/drivers/media/video/zc0301/Kconfig deleted file mode 100644 index a7e610e..0000000 --- a/drivers/media/video/zc0301/Kconfig +++ /dev/null @@ -1,15 +0,0 @@ -config USB_ZC0301 - tristate "USB ZC0301[P] webcam support (DEPRECATED)" - depends on VIDEO_V4L2 - default n - ---help--- - This driver is DEPRECATED please use the gspca zc3xx module - instead. - - Say Y here if you want support for cameras based on the ZC0301 or - ZC0301P Image Processors and Control Chips. - - See <file:Documentation/video4linux/zc0301.txt> for more info. - - To compile this driver as a module, choose M here: the - module will be called zc0301. diff --git a/drivers/media/video/zc0301/Makefile b/drivers/media/video/zc0301/Makefile deleted file mode 100644 index d9e6d97..0000000 --- a/drivers/media/video/zc0301/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -zc0301-objs := zc0301_core.o zc0301_pb0330.o zc0301_pas202bcb.o - -obj-$(CONFIG_USB_ZC0301) += zc0301.o diff --git a/drivers/media/video/zc0301/zc0301.h b/drivers/media/video/zc0301/zc0301.h deleted file mode 100644 index b1b5cce..0000000 --- a/drivers/media/video/zc0301/zc0301.h +++ /dev/null @@ -1,196 +0,0 @@ -/*************************************************************************** - * V4L2 driver for ZC0301[P] Image Processor and Control Chip * - * * - * Copyright (C) 2006-2007 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 _ZC0301_H_ -#define _ZC0301_H_ - -#include <linux/version.h> -#include <linux/usb.h> -#include <linux/videodev2.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> -#include <linux/device.h> -#include <linux/list.h> -#include <linux/spinlock.h> -#include <linux/time.h> -#include <linux/wait.h> -#include <linux/types.h> -#include <linux/param.h> -#include <linux/mutex.h> -#include <linux/rwsem.h> -#include <linux/stddef.h> -#include <linux/string.h> -#include <linux/kref.h> - -#include "zc0301_sensor.h" - -/*****************************************************************************/ - -#define ZC0301_DEBUG -#define ZC0301_DEBUG_LEVEL 2 -#define ZC0301_MAX_DEVICES 64 -#define ZC0301_FORCE_MUNMAP 0 -#define ZC0301_MAX_FRAMES 32 -#define ZC0301_COMPRESSION_QUALITY 0 -#define ZC0301_URBS 2 -#define ZC0301_ISO_PACKETS 7 -#define ZC0301_ALTERNATE_SETTING 7 -#define ZC0301_URB_TIMEOUT msecs_to_jiffies(2 * ZC0301_ISO_PACKETS) -#define ZC0301_CTRL_TIMEOUT 100 -#define ZC0301_FRAME_TIMEOUT 2 - -/*****************************************************************************/ - -ZC0301_ID_TABLE -ZC0301_SENSOR_TABLE - -enum zc0301_frame_state { - F_UNUSED, - F_QUEUED, - F_GRABBING, - F_DONE, - F_ERROR, -}; - -struct zc0301_frame_t { - void* bufmem; - struct v4l2_buffer buf; - enum zc0301_frame_state state; - struct list_head frame; - unsigned long vma_use_count; -}; - -enum zc0301_dev_state { - DEV_INITIALIZED = 0x01, - DEV_DISCONNECTED = 0x02, - DEV_MISCONFIGURED = 0x04, -}; - -enum zc0301_io_method { - IO_NONE, - IO_READ, - IO_MMAP, -}; - -enum zc0301_stream_state { - STREAM_OFF, - STREAM_INTERRUPT, - STREAM_ON, -}; - -struct zc0301_module_param { - u8 force_munmap; - u16 frame_timeout; -}; - -static DECLARE_RWSEM(zc0301_dev_lock); - -struct zc0301_device { - struct video_device* v4ldev; - - struct zc0301_sensor sensor; - - struct usb_device* usbdev; - struct urb* urb[ZC0301_URBS]; - void* transfer_buffer[ZC0301_URBS]; - u8* control_buffer; - - struct zc0301_frame_t *frame_current, frame[ZC0301_MAX_FRAMES]; - struct list_head inqueue, outqueue; - u32 frame_count, nbuffers, nreadbuffers; - - enum zc0301_io_method io; - enum zc0301_stream_state stream; - - struct v4l2_jpegcompression compression; - - struct zc0301_module_param module_param; - - struct kref kref; - enum zc0301_dev_state state; - u8 users; - - struct completion probe; - struct mutex open_mutex, fileop_mutex; - spinlock_t queue_lock; - wait_queue_head_t wait_open, wait_frame, wait_stream; -}; - -/*****************************************************************************/ - -struct zc0301_device* -zc0301_match_id(struct zc0301_device* cam, const struct usb_device_id *id) -{ - return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL; -} - -void -zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor) -{ - memcpy(&cam->sensor, sensor, sizeof(struct zc0301_sensor)); -} - -/*****************************************************************************/ - -#undef DBG -#undef KDBG -#ifdef ZC0301_DEBUG -# define DBG(level, fmt, args...) \ -do { \ - if (debug >= (level)) { \ - if ((level) == 1) \ - dev_err(&cam->usbdev->dev, fmt "\n", ## args); \ - else if ((level) == 2) \ - dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ - else if ((level) >= 3) \ - dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \ - __FILE__, __func__, __LINE__ , ## args); \ - } \ -} while (0) -# define KDBG(level, fmt, args...) \ -do { \ - if (debug >= (level)) { \ - if ((level) == 1 || (level) == 2) \ - pr_info("zc0301: " fmt "\n", ## args); \ - else if ((level) == 3) \ - pr_debug("sn9c102: [%s:%s:%d] " fmt "\n", __FILE__, \ - __func__, __LINE__ , ## args); \ - } \ -} while (0) -# define V4LDBG(level, name, cmd) \ -do { \ - if (debug >= (level)) \ - v4l_print_ioctl(name, cmd); \ -} while (0) -#else -# define DBG(level, fmt, args...) do {;} while(0) -# define KDBG(level, fmt, args...) do {;} while(0) -# define V4LDBG(level, name, cmd) do {;} while(0) -#endif - -#undef PDBG -#define PDBG(fmt, args...) \ -dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __func__, \ - __LINE__ , ## args) - -#undef PDBGG -#define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ - -#endif /* _ZC0301_H_ */ diff --git a/drivers/media/video/zc0301/zc0301_core.c b/drivers/media/video/zc0301/zc0301_core.c deleted file mode 100644 index bb51cfb..0000000 --- a/drivers/media/video/zc0301/zc0301_core.c +++ /dev/null @@ -1,2098 +0,0 @@ -/*************************************************************************** - * Video4Linux2 driver for ZC0301[P] Image Processor and Control Chip * - * * - * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * Informations about the chip internals needed to enable the I2C protocol * - * have been taken from the documentation of the ZC030x Video4Linux1 * - * driver written by Andrew Birkett <andy@nobugs.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/init.h> -#include <linux/kernel.h> -#include <linux/param.h> -#include <linux/errno.h> -#include <linux/slab.h> -#include <linux/device.h> -#include <linux/fs.h> -#include <linux/delay.h> -#include <linux/compiler.h> -#include <linux/ioctl.h> -#include <linux/poll.h> -#include <linux/stat.h> -#include <linux/mm.h> -#include <linux/vmalloc.h> -#include <linux/page-flags.h> -#include <asm/byteorder.h> -#include <asm/page.h> -#include <asm/uaccess.h> - -#include "zc0301.h" - -/*****************************************************************************/ - -#define ZC0301_MODULE_NAME "V4L2 driver for ZC0301[P] " \ - "Image Processor and Control Chip" -#define ZC0301_MODULE_AUTHOR "(C) 2006-2007 Luca Risolia" -#define ZC0301_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>" -#define ZC0301_MODULE_LICENSE "GPL" -#define ZC0301_MODULE_VERSION "1:1.10" -#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 10) - -/*****************************************************************************/ - -MODULE_DEVICE_TABLE(usb, zc0301_id_table); - -MODULE_AUTHOR(ZC0301_MODULE_AUTHOR " " ZC0301_AUTHOR_EMAIL); -MODULE_DESCRIPTION(ZC0301_MODULE_NAME); -MODULE_VERSION(ZC0301_MODULE_VERSION); -MODULE_LICENSE(ZC0301_MODULE_LICENSE); - -static short video_nr[] = {[0 ... ZC0301_MAX_DEVICES-1] = -1}; -module_param_array(video_nr, short, NULL, 0444); -MODULE_PARM_DESC(video_nr, - "\n<-1|n[,...]> Specify V4L2 minor mode number." - "\n -1 = use next available (default)" - "\n n = use minor number n (integer >= 0)" - "\nYou can specify up to " - __MODULE_STRING(ZC0301_MAX_DEVICES) " cameras this way." - "\nFor example:" - "\nvideo_nr=-1,2,-1 would assign minor number 2 to" - "\nthe second registered camera and use auto for the first" - "\none and for every other camera." - "\n"); - -static short force_munmap[] = {[0 ... ZC0301_MAX_DEVICES-1] = - ZC0301_FORCE_MUNMAP}; -module_param_array(force_munmap, bool, NULL, 0444); -MODULE_PARM_DESC(force_munmap, - "\n<0|1[,...]> Force the application to unmap previously" - "\nmapped buffer memory before calling any VIDIOC_S_CROP or" - "\nVIDIOC_S_FMT ioctl's. Not all the applications support" - "\nthis feature. This parameter is specific for each" - "\ndetected camera." - "\n 0 = do not force memory unmapping" - "\n 1 = force memory unmapping (save memory)" - "\nDefault value is "__MODULE_STRING(ZC0301_FORCE_MUNMAP)"." - "\n"); - -static unsigned int frame_timeout[] = {[0 ... ZC0301_MAX_DEVICES-1] = - ZC0301_FRAME_TIMEOUT}; -module_param_array(frame_timeout, uint, NULL, 0644); -MODULE_PARM_DESC(frame_timeout, - "\n<n[,...]> Timeout for a video frame in seconds." - "\nThis parameter is specific for each detected camera." - "\nDefault value is "__MODULE_STRING(ZC0301_FRAME_TIMEOUT)"." - "\n"); - -#ifdef ZC0301_DEBUG -static unsigned short debug = ZC0301_DEBUG_LEVEL; -module_param(debug, ushort, 0644); -MODULE_PARM_DESC(debug, - "\n<n> Debugging information level, from 0 to 3:" - "\n0 = none (use carefully)" - "\n1 = critical errors" - "\n2 = significant informations" - "\n3 = more verbose messages" - "\nLevel 3 is useful for testing only, when only " - "one device is used." - "\nDefault value is "__MODULE_STRING(ZC0301_DEBUG_LEVEL)"." - "\n"); -#endif - -/*****************************************************************************/ - -static u32 -zc0301_request_buffers(struct zc0301_device* cam, u32 count, - enum zc0301_io_method io) -{ - struct v4l2_pix_format* p = &(cam->sensor.pix_format); - struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); - const size_t imagesize = cam->module_param.force_munmap || - io == IO_READ ? - (p->width * p->height * p->priv) / 8 : - (r->width * r->height * p->priv) / 8; - void* buff = NULL; - u32 i; - - if (count > ZC0301_MAX_FRAMES) - count = ZC0301_MAX_FRAMES; - - cam->nbuffers = count; - while (cam->nbuffers > 0) { - if ((buff = vmalloc_32_user(cam->nbuffers * - PAGE_ALIGN(imagesize)))) - break; - cam->nbuffers--; - } - - for (i = 0; i < cam->nbuffers; i++) { - cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize); - cam->frame[i].buf.index = i; - cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize); - cam->frame[i].buf.length = imagesize; - cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - cam->frame[i].buf.sequence = 0; - cam->frame[i].buf.field = V4L2_FIELD_NONE; - cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; - cam->frame[i].buf.flags = 0; - } - - return cam->nbuffers; -} - - -static void zc0301_release_buffers(struct zc0301_device* cam) -{ - if (cam->nbuffers) { - vfree(cam->frame[0].bufmem); - cam->nbuffers = 0; - } - cam->frame_current = NULL; -} - - -static void zc0301_empty_framequeues(struct zc0301_device* cam) -{ - u32 i; - - INIT_LIST_HEAD(&cam->inqueue); - INIT_LIST_HEAD(&cam->outqueue); - - for (i = 0; i < ZC0301_MAX_FRAMES; i++) { - cam->frame[i].state = F_UNUSED; - cam->frame[i].buf.bytesused = 0; - } -} - - -static void zc0301_requeue_outqueue(struct zc0301_device* cam) -{ - struct zc0301_frame_t *i; - - list_for_each_entry(i, &cam->outqueue, frame) { - i->state = F_QUEUED; - list_add(&i->frame, &cam->inqueue); - } - - INIT_LIST_HEAD(&cam->outqueue); -} - - -static void zc0301_queue_unusedframes(struct zc0301_device* cam) -{ - unsigned long lock_flags; - u32 i; - - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].state == F_UNUSED) { - cam->frame[i].state = F_QUEUED; - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_add_tail(&cam->frame[i].frame, &cam->inqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - } -} - -/*****************************************************************************/ - -int zc0301_write_reg(struct zc0301_device* cam, u16 index, u16 value) -{ - struct usb_device* udev = cam->usbdev; - int res; - - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0xa0, 0x40, - value, index, NULL, 0, ZC0301_CTRL_TIMEOUT); - if (res < 0) { - DBG(3, "Failed to write a register (index 0x%04X, " - "value 0x%02X, error %d)",index, value, res); - return -1; - } - - return 0; -} - - -int zc0301_read_reg(struct zc0301_device* cam, u16 index) -{ - struct usb_device* udev = cam->usbdev; - u8* buff = cam->control_buffer; - int res; - - res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0xa1, 0xc0, - 0x0001, index, buff, 1, ZC0301_CTRL_TIMEOUT); - if (res < 0) - DBG(3, "Failed to read a register (index 0x%04X, error %d)", - index, res); - - PDBGG("Read: index 0x%04X, value: 0x%04X", index, (int)(*buff)); - - return (res >= 0) ? (int)(*buff) : -1; -} - - -int zc0301_i2c_read(struct zc0301_device* cam, u16 address, u8 length) -{ - int err = 0, res, r0, r1; - - err += zc0301_write_reg(cam, 0x0092, address); - err += zc0301_write_reg(cam, 0x0090, 0x02); - - msleep(1); - - res = zc0301_read_reg(cam, 0x0091); - if (res < 0) - err += res; - r0 = zc0301_read_reg(cam, 0x0095); - if (r0 < 0) - err += r0; - r1 = zc0301_read_reg(cam, 0x0096); - if (r1 < 0) - err += r1; - - res = (length <= 1) ? r0 : r0 | (r1 << 8); - - if (err) - DBG(3, "I2C read failed at address 0x%04X, value: 0x%04X", - address, res); - - - PDBGG("I2C read: address 0x%04X, value: 0x%04X", address, res); - - return err ? -1 : res; -} - - -int zc0301_i2c_write(struct zc0301_device* cam, u16 address, u16 value) -{ - int err = 0, res; - - err += zc0301_write_reg(cam, 0x0092, address); - err += zc0301_write_reg(cam, 0x0093, value & 0xff); - err += zc0301_write_reg(cam, 0x0094, value >> 8); - err += zc0301_write_reg(cam, 0x0090, 0x01); - - msleep(1); - - res = zc0301_read_reg(cam, 0x0091); - if (res < 0) - err += res; - - if (err) - DBG(3, "I2C write failed at address 0x%04X, value: 0x%04X", - address, value); - - PDBGG("I2C write: address 0x%04X, value: 0x%04X", address, value); - - return err ? -1 : 0; -} - -/*****************************************************************************/ - -static void zc0301_urb_complete(struct urb *urb) -{ - struct zc0301_device* cam = urb->context; - struct zc0301_frame_t** f; - size_t imagesize; - u8 i; - int err = 0; - - if (urb->status == -ENOENT) - return; - - f = &cam->frame_current; - - if (cam->stream == STREAM_INTERRUPT) { - cam->stream = STREAM_OFF; - if ((*f)) - (*f)->state = F_QUEUED; - DBG(3, "Stream interrupted"); - wake_up(&cam->wait_stream); - } - - if (cam->state & DEV_DISCONNECTED) - return; - - if (cam->state & DEV_MISCONFIGURED) { - wake_up_interruptible(&cam->wait_frame); - return; - } - - if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue)) - goto resubmit_urb; - - if (!(*f)) - (*f) = list_entry(cam->inqueue.next, struct zc0301_frame_t, - frame); - - imagesize = (cam->sensor.pix_format.width * - cam->sensor.pix_format.height * - cam->sensor.pix_format.priv) / 8; - - for (i = 0; i < urb->number_of_packets; i++) { - unsigned int len, status; - void *pos; - u16* soi; - u8 sof; - - 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) { - DBG(3, "Error in isochronous frame"); - (*f)->state = F_ERROR; - continue; - } - - sof = (*(soi = pos) == 0xd8ff); - - PDBGG("Isochrnous frame: length %u, #%u i,", len, i); - - if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) -start_of_frame: - if (sof) { - (*f)->state = F_GRABBING; - (*f)->buf.bytesused = 0; - do_gettimeofday(&(*f)->buf.timestamp); - DBG(3, "SOF detected: new video frame"); - } - - if ((*f)->state == F_GRABBING) { - if (sof && (*f)->buf.bytesused) - goto end_of_frame; - - if ((*f)->buf.bytesused + len > imagesize) { - DBG(3, "Video frame size exceeded"); - (*f)->state = F_ERROR; - continue; - } - - memcpy((*f)->bufmem+(*f)->buf.bytesused, pos, len); - (*f)->buf.bytesused += len; - - if ((*f)->buf.bytesused == imagesize) { - u32 b; -end_of_frame: - b = (*f)->buf.bytesused; - (*f)->state = F_DONE; - (*f)->buf.sequence= ++cam->frame_count; - spin_lock(&cam->queue_lock); - list_move_tail(&(*f)->frame, &cam->outqueue); - if (!list_empty(&cam->inqueue)) - (*f) = list_entry(cam->inqueue.next, - struct zc0301_frame_t, - frame); - else - (*f) = NULL; - spin_unlock(&cam->queue_lock); - DBG(3, "Video frame captured: : %lu bytes", - (unsigned long)(b)); - - if (!(*f)) - goto resubmit_urb; - - if (sof) - goto start_of_frame; - } - } - } - -resubmit_urb: - urb->dev = cam->usbdev; - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err < 0 && err != -EPERM) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "usb_submit_urb() failed"); - } - - wake_up_interruptible(&cam->wait_frame); -} - - -static int zc0301_start_transfer(struct zc0301_device* cam) -{ - struct usb_device *udev = cam->usbdev; - struct usb_host_interface* altsetting = usb_altnum_to_altsetting( - usb_ifnum_to_if(udev, 0), - ZC0301_ALTERNATE_SETTING); - const unsigned int psz = le16_to_cpu(altsetting-> - endpoint[0].desc.wMaxPacketSize); - struct urb* urb; - s8 i, j; - int err = 0; - - for (i = 0; i < ZC0301_URBS; i++) { - cam->transfer_buffer[i] = kzalloc(ZC0301_ISO_PACKETS * psz, - GFP_KERNEL); - if (!cam->transfer_buffer[i]) { - err = -ENOMEM; - DBG(1, "Not enough memory"); - goto free_buffers; - } - } - - for (i = 0; i < ZC0301_URBS; i++) { - urb = usb_alloc_urb(ZC0301_ISO_PACKETS, GFP_KERNEL); - cam->urb[i] = urb; - if (!urb) { - err = -ENOMEM; - DBG(1, "usb_alloc_urb() failed"); - goto free_urbs; - } - urb->dev = udev; - urb->context = cam; - urb->pipe = usb_rcvisocpipe(udev, 1); - urb->transfer_flags = URB_ISO_ASAP; - urb->number_of_packets = ZC0301_ISO_PACKETS; - urb->complete = zc0301_urb_complete; - urb->transfer_buffer = cam->transfer_buffer[i]; - urb->transfer_buffer_length = psz * ZC0301_ISO_PACKETS; - urb->interval = 1; - for (j = 0; j < ZC0301_ISO_PACKETS; j++) { - urb->iso_frame_desc[j].offset = psz * j; - urb->iso_frame_desc[j].length = psz; - } - } - - err = usb_set_interface(udev, 0, ZC0301_ALTERNATE_SETTING); - if (err) { - DBG(1, "usb_set_interface() failed"); - goto free_urbs; - } - - cam->frame_current = NULL; - - for (i = 0; i < ZC0301_URBS; i++) { - err = usb_submit_urb(cam->urb[i], GFP_KERNEL); - if (err) { - for (j = i-1; j >= 0; j--) - usb_kill_urb(cam->urb[j]); - DBG(1, "usb_submit_urb() failed, error %d", err); - goto free_urbs; - } - } - - return 0; - -free_urbs: - for (i = 0; (i < ZC0301_URBS) && cam->urb[i]; i++) - usb_free_urb(cam->urb[i]); - -free_buffers: - for (i = 0; (i < ZC0301_URBS) && cam->transfer_buffer[i]; i++) - kfree(cam->transfer_buffer[i]); - - return err; -} - - -static int zc0301_stop_transfer(struct zc0301_device* cam) -{ - struct usb_device *udev = cam->usbdev; - s8 i; - int err = 0; - - if (cam->state & DEV_DISCONNECTED) - return 0; - - for (i = ZC0301_URBS-1; i >= 0; i--) { - usb_kill_urb(cam->urb[i]); - usb_free_urb(cam->urb[i]); - kfree(cam->transfer_buffer[i]); - } - - err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */ - if (err) - DBG(3, "usb_set_interface() failed"); - - return err; -} - - -static int zc0301_stream_interrupt(struct zc0301_device* cam) -{ - long timeout; - - cam->stream = STREAM_INTERRUPT; - timeout = wait_event_timeout(cam->wait_stream, - (cam->stream == STREAM_OFF) || - (cam->state & DEV_DISCONNECTED), - ZC0301_URB_TIMEOUT); - if (cam->state & DEV_DISCONNECTED) - return -ENODEV; - else if (cam->stream != STREAM_OFF) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "URB timeout reached. The camera is misconfigured. To " - "use it, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - return 0; -} - -/*****************************************************************************/ - -static int -zc0301_set_compression(struct zc0301_device* cam, - struct v4l2_jpegcompression* compression) -{ - int r, err = 0; - - if ((r = zc0301_read_reg(cam, 0x0008)) < 0) - err += r; - err += zc0301_write_reg(cam, 0x0008, r | 0x11 | compression->quality); - - return err ? -EIO : 0; -} - - -static int zc0301_init(struct zc0301_device* cam) -{ - struct zc0301_sensor* s = &cam->sensor; - struct v4l2_control ctrl; - struct v4l2_queryctrl *qctrl; - struct v4l2_rect* rect; - u8 i = 0; - int err = 0; - - if (!(cam->state & DEV_INITIALIZED)) { - mutex_init(&cam->open_mutex); - init_waitqueue_head(&cam->wait_open); - qctrl = s->qctrl; - rect = &(s->cropcap.defrect); - cam->compression.quality = ZC0301_COMPRESSION_QUALITY; - } else { /* use current values */ - qctrl = s->_qctrl; - rect = &(s->_rect); - } - - if (s->init) { - err = s->init(cam); - if (err) { - DBG(3, "Sensor initialization failed"); - return err; - } - } - - if ((err = zc0301_set_compression(cam, &cam->compression))) { - DBG(3, "set_compression() failed"); - return err; - } - - if (s->set_crop) - if ((err = s->set_crop(cam, rect))) { - DBG(3, "set_crop() failed"); - return err; - } - - if (s->set_ctrl) { - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (s->qctrl[i].id != 0 && - !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) { - ctrl.id = s->qctrl[i].id; - ctrl.value = qctrl[i].default_value; - err = s->set_ctrl(cam, &ctrl); - if (err) { - DBG(3, "Set %s control failed", - s->qctrl[i].name); - return err; - } - DBG(3, "Image sensor supports '%s' control", - s->qctrl[i].name); - } - } - - if (!(cam->state & DEV_INITIALIZED)) { - mutex_init(&cam->fileop_mutex); - spin_lock_init(&cam->queue_lock); - init_waitqueue_head(&cam->wait_frame); - init_waitqueue_head(&cam->wait_stream); - cam->nreadbuffers = 2; - memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl)); - memcpy(&(s->_rect), &(s->cropcap.defrect), - sizeof(struct v4l2_rect)); - cam->state |= DEV_INITIALIZED; - } - - DBG(2, "Initialization succeeded"); - return 0; -} - -/*****************************************************************************/ - -static void zc0301_release_resources(struct kref *kref) -{ - struct zc0301_device *cam = container_of(kref, struct zc0301_device, - kref); - DBG(2, "V4L2 device %s deregistered", - video_device_node_name(cam->v4ldev)); - video_set_drvdata(cam->v4ldev, NULL); - video_unregister_device(cam->v4ldev); - usb_put_dev(cam->usbdev); - kfree(cam->control_buffer); - kfree(cam); -} - - -static int zc0301_open(struct file *filp) -{ - struct zc0301_device* cam; - int err = 0; - - if (!down_read_trylock(&zc0301_dev_lock)) - return -EAGAIN; - - cam = video_drvdata(filp); - - if (wait_for_completion_interruptible(&cam->probe)) { - up_read(&zc0301_dev_lock); - return -ERESTARTSYS; - } - - kref_get(&cam->kref); - - if (mutex_lock_interruptible(&cam->open_mutex)) { - kref_put(&cam->kref, zc0301_release_resources); - up_read(&zc0301_dev_lock); - return -ERESTARTSYS; - } - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - err = -ENODEV; - goto out; - } - - if (cam->users) { - DBG(2, "Device %s is busy...", - video_device_node_name(cam->v4ldev)); - DBG(3, "Simultaneous opens are not supported"); - if ((filp->f_flags & O_NONBLOCK) || - (filp->f_flags & O_NDELAY)) { - err = -EWOULDBLOCK; - goto out; - } - DBG(2, "A blocking open() has been requested. Wait for the " - "device to be released..."); - up_read(&zc0301_dev_lock); - err = wait_event_interruptible_exclusive(cam->wait_open, - (cam->state & DEV_DISCONNECTED) - || !cam->users); - down_read(&zc0301_dev_lock); - if (err) - goto out; - if (cam->state & DEV_DISCONNECTED) { - err = -ENODEV; - goto out; - } - } - - if (cam->state & DEV_MISCONFIGURED) { - err = zc0301_init(cam); - if (err) { - DBG(1, "Initialization failed again. " - "I will retry on next open()."); - goto out; - } - cam->state &= ~DEV_MISCONFIGURED; - } - - if ((err = zc0301_start_transfer(cam))) - goto out; - - filp->private_data = cam; - cam->users++; - cam->io = IO_NONE; - cam->stream = STREAM_OFF; - cam->nbuffers = 0; - cam->frame_count = 0; - zc0301_empty_framequeues(cam); - - DBG(3, "Video device %s is open", - video_device_node_name(cam->v4ldev)); - -out: - mutex_unlock(&cam->open_mutex); - if (err) - kref_put(&cam->kref, zc0301_release_resources); - up_read(&zc0301_dev_lock); - return err; -} - - -static int zc0301_release(struct file *filp) -{ - struct zc0301_device* cam; - - down_write(&zc0301_dev_lock); - - cam = video_drvdata(filp); - - zc0301_stop_transfer(cam); - zc0301_release_buffers(cam); - cam->users--; - wake_up_interruptible_nr(&cam->wait_open, 1); - - DBG(3, "Video device %s closed", - video_device_node_name(cam->v4ldev)); - - kref_put(&cam->kref, zc0301_release_resources); - - up_write(&zc0301_dev_lock); - - return 0; -} - - -static ssize_t -zc0301_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) -{ - struct zc0301_device *cam = video_drvdata(filp); - struct zc0301_frame_t* f, * i; - unsigned long lock_flags; - long timeout; - int err = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - if (cam->io == IO_MMAP) { - DBG(3, "Close and open the device again to choose the read " - "method"); - mutex_unlock(&cam->fileop_mutex); - return -EBUSY; - } - - if (cam->io == IO_NONE) { - if (!zc0301_request_buffers(cam, cam->nreadbuffers, IO_READ)) { - DBG(1, "read() failed, not enough memory"); - mutex_unlock(&cam->fileop_mutex); - return -ENOMEM; - } - cam->io = IO_READ; - cam->stream = STREAM_ON; - } - - if (list_empty(&cam->inqueue)) { - if (!list_empty(&cam->outqueue)) - zc0301_empty_framequeues(cam); - zc0301_queue_unusedframes(cam); - } - - if (!count) { - mutex_unlock(&cam->fileop_mutex); - return 0; - } - - if (list_empty(&cam->outqueue)) { - if (filp->f_flags & O_NONBLOCK) { - mutex_unlock(&cam->fileop_mutex); - return -EAGAIN; - } - timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - msecs_to_jiffies( - cam->module_param.frame_timeout * 1000 - ) - ); - if (timeout < 0) { - mutex_unlock(&cam->fileop_mutex); - return timeout; - } - if (cam->state & DEV_DISCONNECTED) { - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - if (!timeout || (cam->state & DEV_MISCONFIGURED)) { - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - } - - f = list_entry(cam->outqueue.prev, struct zc0301_frame_t, frame); - - if (count > f->buf.bytesused) - count = f->buf.bytesused; - - if (copy_to_user(buf, f->bufmem, count)) { - err = -EFAULT; - goto exit; - } - *f_pos += count; - -exit: - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_for_each_entry(i, &cam->outqueue, frame) - i->state = F_UNUSED; - INIT_LIST_HEAD(&cam->outqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - zc0301_queue_unusedframes(cam); - - PDBGG("Frame #%lu, bytes read: %zu", - (unsigned long)f->buf.index, count); - - mutex_unlock(&cam->fileop_mutex); - - return err ? err : count; -} - - -static unsigned int zc0301_poll(struct file *filp, poll_table *wait) -{ - struct zc0301_device *cam = video_drvdata(filp); - struct zc0301_frame_t* f; - unsigned long lock_flags; - unsigned int mask = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return POLLERR; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - goto error; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - goto error; - } - - if (cam->io == IO_NONE) { - if (!zc0301_request_buffers(cam, cam->nreadbuffers, IO_READ)) { - DBG(1, "poll() failed, not enough memory"); - goto error; - } - cam->io = IO_READ; - cam->stream = STREAM_ON; - } - - if (cam->io == IO_READ) { - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_for_each_entry(f, &cam->outqueue, frame) - f->state = F_UNUSED; - INIT_LIST_HEAD(&cam->outqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - zc0301_queue_unusedframes(cam); - } - - poll_wait(filp, &cam->wait_frame, wait); - - if (!list_empty(&cam->outqueue)) - mask |= POLLIN | POLLRDNORM; - - mutex_unlock(&cam->fileop_mutex); - - return mask; - -error: - mutex_unlock(&cam->fileop_mutex); - return POLLERR; -} - - -static void zc0301_vm_open(struct vm_area_struct* vma) -{ - struct zc0301_frame_t* f = vma->vm_private_data; - f->vma_use_count++; -} - - -static void zc0301_vm_close(struct vm_area_struct* vma) -{ - /* NOTE: buffers are not freed here */ - struct zc0301_frame_t* f = vma->vm_private_data; - f->vma_use_count--; -} - - -static const struct vm_operations_struct zc0301_vm_ops = { - .open = zc0301_vm_open, - .close = zc0301_vm_close, -}; - - -static int zc0301_mmap(struct file* filp, struct vm_area_struct *vma) -{ - struct zc0301_device *cam = video_drvdata(filp); - unsigned long size = vma->vm_end - vma->vm_start, - start = vma->vm_start; - void *pos; - u32 i; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - if (!(vma->vm_flags & (VM_WRITE | VM_READ))) { - mutex_unlock(&cam->fileop_mutex); - return -EACCES; - } - - if (cam->io != IO_MMAP || - size != PAGE_ALIGN(cam->frame[0].buf.length)) { - mutex_unlock(&cam->fileop_mutex); - return -EINVAL; - } - - for (i = 0; i < cam->nbuffers; i++) { - if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff) - break; - } - if (i == cam->nbuffers) { - mutex_unlock(&cam->fileop_mutex); - return -EINVAL; - } - - vma->vm_flags |= VM_IO; - vma->vm_flags |= VM_RESERVED; - - pos = cam->frame[i].bufmem; - while (size > 0) { /* size is page-aligned */ - if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { - mutex_unlock(&cam->fileop_mutex); - return -EAGAIN; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - size -= PAGE_SIZE; - } - - vma->vm_ops = &zc0301_vm_ops; - vma->vm_private_data = &cam->frame[i]; - zc0301_vm_open(vma); - - mutex_unlock(&cam->fileop_mutex); - - return 0; -} - -/*****************************************************************************/ - -static int -zc0301_vidioc_querycap(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_capability cap = { - .driver = "zc0301", - .version = ZC0301_MODULE_VERSION_CODE, - .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING, - }; - - strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); - if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) - strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev), - sizeof(cap.bus_info)); - - if (copy_to_user(arg, &cap, sizeof(cap))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_enuminput(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_input i; - - if (copy_from_user(&i, arg, sizeof(i))) - return -EFAULT; - - if (i.index) - return -EINVAL; - - memset(&i, 0, sizeof(i)); - strcpy(i.name, "Camera"); - i.type = V4L2_INPUT_TYPE_CAMERA; - - if (copy_to_user(arg, &i, sizeof(i))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_g_input(struct zc0301_device* cam, void __user * arg) -{ - int index = 0; - - if (copy_to_user(arg, &index, sizeof(index))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_s_input(struct zc0301_device* cam, void __user * arg) -{ - int index; - - if (copy_from_user(&index, arg, sizeof(index))) - return -EFAULT; - - if (index != 0) - return -EINVAL; - - return 0; -} - - -static int -zc0301_vidioc_query_ctrl(struct zc0301_device* cam, void __user * arg) -{ - struct zc0301_sensor* s = &cam->sensor; - struct v4l2_queryctrl qc; - u8 i; - - if (copy_from_user(&qc, arg, sizeof(qc))) - return -EFAULT; - - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (qc.id && qc.id == s->qctrl[i].id) { - memcpy(&qc, &(s->qctrl[i]), sizeof(qc)); - if (copy_to_user(arg, &qc, sizeof(qc))) - return -EFAULT; - return 0; - } - - return -EINVAL; -} - - -static int -zc0301_vidioc_g_ctrl(struct zc0301_device* cam, void __user * arg) -{ - struct zc0301_sensor* s = &cam->sensor; - struct v4l2_control ctrl; - int err = 0; - u8 i; - - if (!s->get_ctrl && !s->set_ctrl) - return -EINVAL; - - if (copy_from_user(&ctrl, arg, sizeof(ctrl))) - return -EFAULT; - - if (!s->get_ctrl) { - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (ctrl.id == s->qctrl[i].id) { - ctrl.value = s->_qctrl[i].default_value; - goto exit; - } - return -EINVAL; - } else - err = s->get_ctrl(cam, &ctrl); - -exit: - if (copy_to_user(arg, &ctrl, sizeof(ctrl))) - return -EFAULT; - - return err; -} - - -static int -zc0301_vidioc_s_ctrl(struct zc0301_device* cam, void __user * arg) -{ - struct zc0301_sensor* s = &cam->sensor; - struct v4l2_control ctrl; - u8 i; - int err = 0; - - if (!s->set_ctrl) - return -EINVAL; - - if (copy_from_user(&ctrl, arg, sizeof(ctrl))) - return -EFAULT; - - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) { - if (ctrl.id == s->qctrl[i].id) { - if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED) - return -EINVAL; - if (ctrl.value < s->qctrl[i].minimum || - ctrl.value > s->qctrl[i].maximum) - return -ERANGE; - ctrl.value -= ctrl.value % s->qctrl[i].step; - break; - } - } - if (i == ARRAY_SIZE(s->qctrl)) - return -EINVAL; - if ((err = s->set_ctrl(cam, &ctrl))) - return err; - - s->_qctrl[i].default_value = ctrl.value; - - return 0; -} - - -static int -zc0301_vidioc_cropcap(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_cropcap* cc = &(cam->sensor.cropcap); - - cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - cc->pixelaspect.numerator = 1; - cc->pixelaspect.denominator = 1; - - if (copy_to_user(arg, cc, sizeof(*cc))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_g_crop(struct zc0301_device* cam, void __user * arg) -{ - struct zc0301_sensor* s = &cam->sensor; - struct v4l2_crop crop = { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - }; - - memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect)); - - if (copy_to_user(arg, &crop, sizeof(crop))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_s_crop(struct zc0301_device* cam, void __user * arg) -{ - struct zc0301_sensor* s = &cam->sensor; - struct v4l2_crop crop; - struct v4l2_rect* rect; - struct v4l2_rect* bounds = &(s->cropcap.bounds); - const enum zc0301_stream_state stream = cam->stream; - const u32 nbuffers = cam->nbuffers; - u32 i; - int err = 0; - - if (copy_from_user(&crop, arg, sizeof(crop))) - return -EFAULT; - - rect = &(crop.c); - - if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (cam->module_param.force_munmap) - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_S_CROP failed. " - "Unmap the buffers first."); - return -EBUSY; - } - - if (!s->set_crop) { - memcpy(rect, &(s->_rect), sizeof(*rect)); - if (copy_to_user(arg, &crop, sizeof(crop))) - return -EFAULT; - return 0; - } - - rect->left &= ~7L; - rect->top &= ~7L; - if (rect->width < 8) - rect->width = 8; - if (rect->height < 8) - rect->height = 8; - if (rect->width > bounds->width) - rect->width = bounds->width; - if (rect->height > bounds->height) - rect->height = bounds->height; - if (rect->left < bounds->left) - rect->left = bounds->left; - if (rect->top < bounds->top) - rect->top = bounds->top; - if (rect->left + rect->width > bounds->left + bounds->width) - rect->left = bounds->left+bounds->width - rect->width; - if (rect->top + rect->height > bounds->top + bounds->height) - rect->top = bounds->top+bounds->height - rect->height; - rect->width &= ~7L; - rect->height &= ~7L; - - if (cam->stream == STREAM_ON) - if ((err = zc0301_stream_interrupt(cam))) - return err; - - if (copy_to_user(arg, &crop, sizeof(crop))) { - cam->stream = stream; - return -EFAULT; - } - - if (cam->module_param.force_munmap || cam->io == IO_READ) - zc0301_release_buffers(cam); - - if (s->set_crop) - err += s->set_crop(cam, rect); - - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - s->pix_format.width = rect->width; - s->pix_format.height = rect->height; - memcpy(&(s->_rect), rect, sizeof(*rect)); - - if ((cam->module_param.force_munmap || cam->io == IO_READ) && - nbuffers != zc0301_request_buffers(cam, nbuffers, cam->io)) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -ENOMEM; - } - - if (cam->io == IO_READ) - zc0301_empty_framequeues(cam); - else if (cam->module_param.force_munmap) - zc0301_requeue_outqueue(cam); - - cam->stream = stream; - - return 0; -} - - -static int -zc0301_vidioc_enum_framesizes(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_frmsizeenum frmsize; - - if (copy_from_user(&frmsize, arg, sizeof(frmsize))) - return -EFAULT; - - if (frmsize.index != 0 && frmsize.index != 1) - return -EINVAL; - - if (frmsize.pixel_format != V4L2_PIX_FMT_JPEG) - return -EINVAL; - - frmsize.type = V4L2_FRMSIZE_TYPE_DISCRETE; - - if (frmsize.index == 1) { - frmsize.discrete.width = cam->sensor.cropcap.defrect.width; - frmsize.discrete.height = cam->sensor.cropcap.defrect.height; - } - memset(&frmsize.reserved, 0, sizeof(frmsize.reserved)); - - if (copy_to_user(arg, &frmsize, sizeof(frmsize))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_enum_fmt(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_fmtdesc fmtd; - - if (copy_from_user(&fmtd, arg, sizeof(fmtd))) - return -EFAULT; - - if (fmtd.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (fmtd.index == 0) { - strcpy(fmtd.description, "JPEG"); - fmtd.pixelformat = V4L2_PIX_FMT_JPEG; - fmtd.flags = V4L2_FMT_FLAG_COMPRESSED; - } else - return -EINVAL; - - fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - memset(&fmtd.reserved, 0, sizeof(fmtd.reserved)); - - if (copy_to_user(arg, &fmtd, sizeof(fmtd))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_g_fmt(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_format format; - struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format); - - if (copy_from_user(&format, arg, sizeof(format))) - return -EFAULT; - - if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - pfmt->bytesperline = 0; - pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); - pfmt->field = V4L2_FIELD_NONE; - memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt)); - - if (copy_to_user(arg, &format, sizeof(format))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_try_s_fmt(struct zc0301_device* cam, unsigned int cmd, - void __user * arg) -{ - struct zc0301_sensor* s = &cam->sensor; - struct v4l2_format format; - struct v4l2_pix_format* pix; - struct v4l2_pix_format* pfmt = &(s->pix_format); - struct v4l2_rect* bounds = &(s->cropcap.bounds); - struct v4l2_rect rect; - const enum zc0301_stream_state stream = cam->stream; - const u32 nbuffers = cam->nbuffers; - u32 i; - int err = 0; - - if (copy_from_user(&format, arg, sizeof(format))) - return -EFAULT; - - pix = &(format.fmt.pix); - - if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memcpy(&rect, &(s->_rect), sizeof(rect)); - - if (!s->set_crop) { - pix->width = rect.width; - pix->height = rect.height; - } else { - rect.width = pix->width; - rect.height = pix->height; - } - - if (rect.width < 8) - rect.width = 8; - if (rect.height < 8) - rect.height = 8; - if (rect.width > bounds->left + bounds->width - rect.left) - rect.width = bounds->left + bounds->width - rect.left; - if (rect.height > bounds->top + bounds->height - rect.top) - rect.height = bounds->top + bounds->height - rect.top; - rect.width &= ~7L; - rect.height &= ~7L; - - pix->width = rect.width; - pix->height = rect.height; - pix->pixelformat = pfmt->pixelformat; - pix->priv = pfmt->priv; - pix->colorspace = pfmt->colorspace; - pix->bytesperline = 0; - pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8); - pix->field = V4L2_FIELD_NONE; - - if (cmd == VIDIOC_TRY_FMT) { - if (copy_to_user(arg, &format, sizeof(format))) - return -EFAULT; - return 0; - } - - if (cam->module_param.force_munmap) - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_S_FMT failed. " - "Unmap the buffers first."); - return -EBUSY; - } - - if (cam->stream == STREAM_ON) - if ((err = zc0301_stream_interrupt(cam))) - return err; - - if (copy_to_user(arg, &format, sizeof(format))) { - cam->stream = stream; - return -EFAULT; - } - - if (cam->module_param.force_munmap || cam->io == IO_READ) - zc0301_release_buffers(cam); - - if (s->set_crop) - err += s->set_crop(cam, &rect); - - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - memcpy(pfmt, pix, sizeof(*pix)); - memcpy(&(s->_rect), &rect, sizeof(rect)); - - if ((cam->module_param.force_munmap || cam->io == IO_READ) && - nbuffers != zc0301_request_buffers(cam, nbuffers, cam->io)) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -ENOMEM; - } - - if (cam->io == IO_READ) - zc0301_empty_framequeues(cam); - else if (cam->module_param.force_munmap) - zc0301_requeue_outqueue(cam); - - cam->stream = stream; - - return 0; -} - - -static int -zc0301_vidioc_g_jpegcomp(struct zc0301_device* cam, void __user * arg) -{ - if (copy_to_user(arg, &cam->compression, sizeof(cam->compression))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_s_jpegcomp(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_jpegcompression jc; - const enum zc0301_stream_state stream = cam->stream; - int err = 0; - - if (copy_from_user(&jc, arg, sizeof(jc))) - return -EFAULT; - - if (jc.quality != 0) - return -EINVAL; - - if (cam->stream == STREAM_ON) - if ((err = zc0301_stream_interrupt(cam))) - return err; - - err += zc0301_set_compression(cam, &jc); - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware " - "problems. To use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - cam->compression.quality = jc.quality; - - cam->stream = stream; - - return 0; -} - - -static int -zc0301_vidioc_reqbufs(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_requestbuffers rb; - u32 i; - int err; - - if (copy_from_user(&rb, arg, sizeof(rb))) - return -EFAULT; - - if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - rb.memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - if (cam->io == IO_READ) { - DBG(3, "Close and open the device again to choose the mmap " - "I/O method"); - return -EBUSY; - } - - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_REQBUFS failed. " - "Previous buffers are still mapped."); - return -EBUSY; - } - - if (cam->stream == STREAM_ON) - if ((err = zc0301_stream_interrupt(cam))) - return err; - - zc0301_empty_framequeues(cam); - - zc0301_release_buffers(cam); - if (rb.count) - rb.count = zc0301_request_buffers(cam, rb.count, IO_MMAP); - - if (copy_to_user(arg, &rb, sizeof(rb))) { - zc0301_release_buffers(cam); - cam->io = IO_NONE; - return -EFAULT; - } - - cam->io = rb.count ? IO_MMAP : IO_NONE; - - return 0; -} - - -static int -zc0301_vidioc_querybuf(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_buffer b; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - b.index >= cam->nbuffers || cam->io != IO_MMAP) - return -EINVAL; - - memcpy(&b, &cam->frame[b.index].buf, sizeof(b)); - - if (cam->frame[b.index].vma_use_count) - b.flags |= V4L2_BUF_FLAG_MAPPED; - - if (cam->frame[b.index].state == F_DONE) - b.flags |= V4L2_BUF_FLAG_DONE; - else if (cam->frame[b.index].state != F_UNUSED) - b.flags |= V4L2_BUF_FLAG_QUEUED; - - if (copy_to_user(arg, &b, sizeof(b))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_qbuf(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_buffer b; - unsigned long lock_flags; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - b.index >= cam->nbuffers || cam->io != IO_MMAP) - return -EINVAL; - - if (cam->frame[b.index].state != F_UNUSED) - return -EINVAL; - - cam->frame[b.index].state = F_QUEUED; - - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_add_tail(&cam->frame[b.index].frame, &cam->inqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - PDBGG("Frame #%lu queued", (unsigned long)b.index); - - return 0; -} - - -static int -zc0301_vidioc_dqbuf(struct zc0301_device* cam, struct file* filp, - void __user * arg) -{ - struct v4l2_buffer b; - struct zc0301_frame_t *f; - unsigned long lock_flags; - long timeout; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP) - return -EINVAL; - - if (list_empty(&cam->outqueue)) { - if (cam->stream == STREAM_OFF) - return -EINVAL; - if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - cam->module_param.frame_timeout * - 1000 * msecs_to_jiffies(1) ); - if (timeout < 0) - return timeout; - if (cam->state & DEV_DISCONNECTED) - return -ENODEV; - if (!timeout || (cam->state & DEV_MISCONFIGURED)) - return -EIO; - } - - spin_lock_irqsave(&cam->queue_lock, lock_flags); - f = list_entry(cam->outqueue.next, struct zc0301_frame_t, frame); - list_del(cam->outqueue.next); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - f->state = F_UNUSED; - - memcpy(&b, &f->buf, sizeof(b)); - if (f->vma_use_count) - b.flags |= V4L2_BUF_FLAG_MAPPED; - - if (copy_to_user(arg, &b, sizeof(b))) - return -EFAULT; - - PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index); - - return 0; -} - - -static int -zc0301_vidioc_streamon(struct zc0301_device* cam, void __user * arg) -{ - int type; - - if (copy_from_user(&type, arg, sizeof(type))) - return -EFAULT; - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) - return -EINVAL; - - cam->stream = STREAM_ON; - - DBG(3, "Stream on"); - - return 0; -} - - -static int -zc0301_vidioc_streamoff(struct zc0301_device* cam, void __user * arg) -{ - int type, err; - - if (copy_from_user(&type, arg, sizeof(type))) - return -EFAULT; - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) - return -EINVAL; - - if (cam->stream == STREAM_ON) - if ((err = zc0301_stream_interrupt(cam))) - return err; - - zc0301_empty_framequeues(cam); - - DBG(3, "Stream off"); - - return 0; -} - - -static int -zc0301_vidioc_g_parm(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_streamparm sp; - - if (copy_from_user(&sp, arg, sizeof(sp))) - return -EFAULT; - - if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - sp.parm.capture.extendedmode = 0; - sp.parm.capture.readbuffers = cam->nreadbuffers; - - if (copy_to_user(arg, &sp, sizeof(sp))) - return -EFAULT; - - return 0; -} - - -static int -zc0301_vidioc_s_parm(struct zc0301_device* cam, void __user * arg) -{ - struct v4l2_streamparm sp; - - if (copy_from_user(&sp, arg, sizeof(sp))) - return -EFAULT; - - if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - sp.parm.capture.extendedmode = 0; - - if (sp.parm.capture.readbuffers == 0) - sp.parm.capture.readbuffers = cam->nreadbuffers; - - if (sp.parm.capture.readbuffers > ZC0301_MAX_FRAMES) - sp.parm.capture.readbuffers = ZC0301_MAX_FRAMES; - - if (copy_to_user(arg, &sp, sizeof(sp))) - return -EFAULT; - - cam->nreadbuffers = sp.parm.capture.readbuffers; - - return 0; -} - - -static long zc0301_ioctl_v4l2(struct file *filp, - unsigned int cmd, void __user *arg) -{ - struct zc0301_device *cam = video_drvdata(filp); - - switch (cmd) { - - case VIDIOC_QUERYCAP: - return zc0301_vidioc_querycap(cam, arg); - - case VIDIOC_ENUMINPUT: - return zc0301_vidioc_enuminput(cam, arg); - - case VIDIOC_G_INPUT: - return zc0301_vidioc_g_input(cam, arg); - - case VIDIOC_S_INPUT: - return zc0301_vidioc_s_input(cam, arg); - - case VIDIOC_QUERYCTRL: - return zc0301_vidioc_query_ctrl(cam, arg); - - case VIDIOC_G_CTRL: - return zc0301_vidioc_g_ctrl(cam, arg); - - case VIDIOC_S_CTRL: - return zc0301_vidioc_s_ctrl(cam, arg); - - case VIDIOC_CROPCAP: - return zc0301_vidioc_cropcap(cam, arg); - - case VIDIOC_G_CROP: - return zc0301_vidioc_g_crop(cam, arg); - - case VIDIOC_S_CROP: - return zc0301_vidioc_s_crop(cam, arg); - - case VIDIOC_ENUM_FMT: - return zc0301_vidioc_enum_fmt(cam, arg); - - case VIDIOC_G_FMT: - return zc0301_vidioc_g_fmt(cam, arg); - - case VIDIOC_TRY_FMT: - case VIDIOC_S_FMT: - return zc0301_vidioc_try_s_fmt(cam, cmd, arg); - - case VIDIOC_ENUM_FRAMESIZES: - return zc0301_vidioc_enum_framesizes(cam, arg); - - case VIDIOC_G_JPEGCOMP: - return zc0301_vidioc_g_jpegcomp(cam, arg); - - case VIDIOC_S_JPEGCOMP: - return zc0301_vidioc_s_jpegcomp(cam, arg); - - case VIDIOC_REQBUFS: - return zc0301_vidioc_reqbufs(cam, arg); - - case VIDIOC_QUERYBUF: - return zc0301_vidioc_querybuf(cam, arg); - - case VIDIOC_QBUF: - return zc0301_vidioc_qbuf(cam, arg); - - case VIDIOC_DQBUF: - return zc0301_vidioc_dqbuf(cam, filp, arg); - - case VIDIOC_STREAMON: - return zc0301_vidioc_streamon(cam, arg); - - case VIDIOC_STREAMOFF: - return zc0301_vidioc_streamoff(cam, arg); - - case VIDIOC_G_PARM: - return zc0301_vidioc_g_parm(cam, arg); - - case VIDIOC_S_PARM: - return zc0301_vidioc_s_parm(cam, arg); - - case VIDIOC_G_STD: - case VIDIOC_S_STD: - case VIDIOC_QUERYSTD: - case VIDIOC_ENUMSTD: - case VIDIOC_QUERYMENU: - case VIDIOC_ENUM_FRAMEINTERVALS: - return -EINVAL; - - default: - return -EINVAL; - - } -} - - -static long zc0301_ioctl(struct file *filp, - unsigned int cmd, unsigned long arg) -{ - struct zc0301_device *cam = video_drvdata(filp); - int err = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - V4LDBG(3, "zc0301", cmd); - - err = zc0301_ioctl_v4l2(filp, cmd, (void __user *)arg); - - mutex_unlock(&cam->fileop_mutex); - - return err; -} - - -static const struct v4l2_file_operations zc0301_fops = { - .owner = THIS_MODULE, - .open = zc0301_open, - .release = zc0301_release, - .ioctl = zc0301_ioctl, - .read = zc0301_read, - .poll = zc0301_poll, - .mmap = zc0301_mmap, -}; - -/*****************************************************************************/ - -static int -zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct zc0301_device* cam; - static unsigned int dev_nr; - unsigned int i; - int err = 0; - - if (!(cam = kzalloc(sizeof(struct zc0301_device), GFP_KERNEL))) - return -ENOMEM; - - cam->usbdev = udev; - - if (!(cam->control_buffer = kzalloc(4, GFP_KERNEL))) { - DBG(1, "kmalloc() failed"); - err = -ENOMEM; - goto fail; - } - - if (!(cam->v4ldev = video_device_alloc())) { - DBG(1, "video_device_alloc() failed"); - err = -ENOMEM; - goto fail; - } - - DBG(2, "ZC0301[P] Image Processor and Control Chip detected " - "(vid/pid 0x%04X:0x%04X)",id->idVendor, id->idProduct); - - for (i = 0; zc0301_sensor_table[i]; i++) { - err = zc0301_sensor_table[i](cam); - if (!err) - break; - } - - if (!err) - DBG(2, "%s image sensor detected", cam->sensor.name); - else { - DBG(1, "No supported image sensor detected"); - err = -ENODEV; - goto fail; - } - - if (zc0301_init(cam)) { - DBG(1, "Initialization failed. I will retry on open()."); - cam->state |= DEV_MISCONFIGURED; - } - - strcpy(cam->v4ldev->name, "ZC0301[P] PC Camera"); - cam->v4ldev->fops = &zc0301_fops; - cam->v4ldev->release = video_device_release; - cam->v4ldev->parent = &udev->dev; - video_set_drvdata(cam->v4ldev, cam); - - init_completion(&cam->probe); - - err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, - video_nr[dev_nr]); - if (err) { - DBG(1, "V4L2 device registration failed"); - if (err == -ENFILE && video_nr[dev_nr] == -1) - DBG(1, "Free /dev/videoX node not found"); - video_nr[dev_nr] = -1; - dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0; - complete_all(&cam->probe); - goto fail; - } - - DBG(2, "V4L2 device registered as %s", - video_device_node_name(cam->v4ldev)); - - cam->module_param.force_munmap = force_munmap[dev_nr]; - cam->module_param.frame_timeout = frame_timeout[dev_nr]; - - dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0; - - usb_set_intfdata(intf, cam); - kref_init(&cam->kref); - usb_get_dev(cam->usbdev); - - complete_all(&cam->probe); - - return 0; - -fail: - if (cam) { - kfree(cam->control_buffer); - if (cam->v4ldev) - video_device_release(cam->v4ldev); - kfree(cam); - } - return err; -} - - -static void zc0301_usb_disconnect(struct usb_interface* intf) -{ - struct zc0301_device* cam; - - down_write(&zc0301_dev_lock); - - cam = usb_get_intfdata(intf); - - DBG(2, "Disconnecting %s...", cam->v4ldev->name); - - if (cam->users) { - DBG(2, "Device %s is open! Deregistration and " - "memory deallocation are deferred.", - video_device_node_name(cam->v4ldev)); - cam->state |= DEV_MISCONFIGURED; - zc0301_stop_transfer(cam); - cam->state |= DEV_DISCONNECTED; - wake_up_interruptible(&cam->wait_frame); - wake_up(&cam->wait_stream); - } else - cam->state |= DEV_DISCONNECTED; - - wake_up_interruptible_all(&cam->wait_open); - - kref_put(&cam->kref, zc0301_release_resources); - - up_write(&zc0301_dev_lock); -} - - -static struct usb_driver zc0301_usb_driver = { - .name = "zc0301", - .id_table = zc0301_id_table, - .probe = zc0301_usb_probe, - .disconnect = zc0301_usb_disconnect, -}; - -/*****************************************************************************/ - -static int __init zc0301_module_init(void) -{ - int err = 0; - - KDBG(2, ZC0301_MODULE_NAME " v" ZC0301_MODULE_VERSION); - KDBG(3, ZC0301_MODULE_AUTHOR); - - if ((err = usb_register(&zc0301_usb_driver))) - KDBG(1, "usb_register() failed"); - - return err; -} - - -static void __exit zc0301_module_exit(void) -{ - usb_deregister(&zc0301_usb_driver); -} - - -module_init(zc0301_module_init); -module_exit(zc0301_module_exit); diff --git a/drivers/media/video/zc0301/zc0301_pas202bcb.c b/drivers/media/video/zc0301/zc0301_pas202bcb.c deleted file mode 100644 index 24b0dfb..0000000 --- a/drivers/media/video/zc0301/zc0301_pas202bcb.c +++ /dev/null @@ -1,362 +0,0 @@ -/*************************************************************************** - * Plug-in for PAS202BCB image sensor connected to the ZC0301 Image * - * Processor and Control Chip * - * * - * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * Initialization values of the ZC0301[P] have been taken from the SPCA5XX * - * driver maintained by Michel Xhaard <mxhaard@magic.fr> * - * * - * 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. * - ***************************************************************************/ - -/* - NOTE: Sensor controls are disabled for now, becouse changing them while - streaming sometimes results in out-of-sync video frames. We'll use - the default initialization, until we know how to stop and start video - in the chip. However, the image quality still looks good under various - light conditions. -*/ - -#include <linux/delay.h> -#include "zc0301_sensor.h" - - -static struct zc0301_sensor pas202bcb; - - -static int pas202bcb_init(struct zc0301_device* cam) -{ - int err = 0; - - err += zc0301_write_reg(cam, 0x0002, 0x00); - err += zc0301_write_reg(cam, 0x0003, 0x02); - err += zc0301_write_reg(cam, 0x0004, 0x80); - err += zc0301_write_reg(cam, 0x0005, 0x01); - err += zc0301_write_reg(cam, 0x0006, 0xE0); - err += zc0301_write_reg(cam, 0x0098, 0x00); - err += zc0301_write_reg(cam, 0x009A, 0x03); - err += zc0301_write_reg(cam, 0x011A, 0x00); - err += zc0301_write_reg(cam, 0x011C, 0x03); - err += zc0301_write_reg(cam, 0x009B, 0x01); - err += zc0301_write_reg(cam, 0x009C, 0xE6); - err += zc0301_write_reg(cam, 0x009D, 0x02); - err += zc0301_write_reg(cam, 0x009E, 0x86); - - err += zc0301_i2c_write(cam, 0x02, 0x02); - err += zc0301_i2c_write(cam, 0x0A, 0x01); - err += zc0301_i2c_write(cam, 0x0B, 0x01); - err += zc0301_i2c_write(cam, 0x0D, 0x00); - err += zc0301_i2c_write(cam, 0x12, 0x05); - err += zc0301_i2c_write(cam, 0x13, 0x63); - err += zc0301_i2c_write(cam, 0x15, 0x70); - - err += zc0301_write_reg(cam, 0x0101, 0xB7); - err += zc0301_write_reg(cam, 0x0100, 0x0D); - err += zc0301_write_reg(cam, 0x0189, 0x06); - err += zc0301_write_reg(cam, 0x01AD, 0x00); - err += zc0301_write_reg(cam, 0x01C5, 0x03); - err += zc0301_write_reg(cam, 0x01CB, 0x13); - err += zc0301_write_reg(cam, 0x0250, 0x08); - err += zc0301_write_reg(cam, 0x0301, 0x08); - err += zc0301_write_reg(cam, 0x018D, 0x70); - err += zc0301_write_reg(cam, 0x0008, 0x03); - err += zc0301_write_reg(cam, 0x01C6, 0x04); - err += zc0301_write_reg(cam, 0x01CB, 0x07); - err += zc0301_write_reg(cam, 0x0120, 0x11); - err += zc0301_write_reg(cam, 0x0121, 0x37); - err += zc0301_write_reg(cam, 0x0122, 0x58); - err += zc0301_write_reg(cam, 0x0123, 0x79); - err += zc0301_write_reg(cam, 0x0124, 0x91); - err += zc0301_write_reg(cam, 0x0125, 0xA6); - err += zc0301_write_reg(cam, 0x0126, 0xB8); - err += zc0301_write_reg(cam, 0x0127, 0xC7); - err += zc0301_write_reg(cam, 0x0128, 0xD3); - err += zc0301_write_reg(cam, 0x0129, 0xDE); - err += zc0301_write_reg(cam, 0x012A, 0xE6); - err += zc0301_write_reg(cam, 0x012B, 0xED); - err += zc0301_write_reg(cam, 0x012C, 0xF3); - err += zc0301_write_reg(cam, 0x012D, 0xF8); - err += zc0301_write_reg(cam, 0x012E, 0xFB); - err += zc0301_write_reg(cam, 0x012F, 0xFF); - err += zc0301_write_reg(cam, 0x0130, 0x26); - err += zc0301_write_reg(cam, 0x0131, 0x23); - err += zc0301_write_reg(cam, 0x0132, 0x20); - err += zc0301_write_reg(cam, 0x0133, 0x1C); - err += zc0301_write_reg(cam, 0x0134, 0x16); - err += zc0301_write_reg(cam, 0x0135, 0x13); - err += zc0301_write_reg(cam, 0x0136, 0x10); - err += zc0301_write_reg(cam, 0x0137, 0x0D); - err += zc0301_write_reg(cam, 0x0138, 0x0B); - err += zc0301_write_reg(cam, 0x0139, 0x09); - err += zc0301_write_reg(cam, 0x013A, 0x07); - err += zc0301_write_reg(cam, 0x013B, 0x06); - err += zc0301_write_reg(cam, 0x013C, 0x05); - err += zc0301_write_reg(cam, 0x013D, 0x04); - err += zc0301_write_reg(cam, 0x013E, 0x03); - err += zc0301_write_reg(cam, 0x013F, 0x02); - err += zc0301_write_reg(cam, 0x010A, 0x4C); - err += zc0301_write_reg(cam, 0x010B, 0xF5); - err += zc0301_write_reg(cam, 0x010C, 0xFF); - err += zc0301_write_reg(cam, 0x010D, 0xF9); - err += zc0301_write_reg(cam, 0x010E, 0x51); - err += zc0301_write_reg(cam, 0x010F, 0xF5); - err += zc0301_write_reg(cam, 0x0110, 0xFB); - err += zc0301_write_reg(cam, 0x0111, 0xED); - err += zc0301_write_reg(cam, 0x0112, 0x5F); - err += zc0301_write_reg(cam, 0x0180, 0x00); - err += zc0301_write_reg(cam, 0x0019, 0x00); - err += zc0301_write_reg(cam, 0x0087, 0x20); - err += zc0301_write_reg(cam, 0x0088, 0x21); - - err += zc0301_i2c_write(cam, 0x20, 0x02); - err += zc0301_i2c_write(cam, 0x21, 0x1B); - err += zc0301_i2c_write(cam, 0x03, 0x44); - err += zc0301_i2c_write(cam, 0x0E, 0x01); - err += zc0301_i2c_write(cam, 0x0F, 0x00); - - err += zc0301_write_reg(cam, 0x01A9, 0x14); - err += zc0301_write_reg(cam, 0x01AA, 0x24); - err += zc0301_write_reg(cam, 0x0190, 0x00); - err += zc0301_write_reg(cam, 0x0191, 0x02); - err += zc0301_write_reg(cam, 0x0192, 0x1B); - err += zc0301_write_reg(cam, 0x0195, 0x00); - err += zc0301_write_reg(cam, 0x0196, 0x00); - err += zc0301_write_reg(cam, 0x0197, 0x4D); - err += zc0301_write_reg(cam, 0x018C, 0x10); - err += zc0301_write_reg(cam, 0x018F, 0x20); - err += zc0301_write_reg(cam, 0x001D, 0x44); - err += zc0301_write_reg(cam, 0x001E, 0x6F); - err += zc0301_write_reg(cam, 0x001F, 0xAD); - err += zc0301_write_reg(cam, 0x0020, 0xEB); - err += zc0301_write_reg(cam, 0x0087, 0x0F); - err += zc0301_write_reg(cam, 0x0088, 0x0E); - err += zc0301_write_reg(cam, 0x0180, 0x40); - err += zc0301_write_reg(cam, 0x0192, 0x1B); - err += zc0301_write_reg(cam, 0x0191, 0x02); - err += zc0301_write_reg(cam, 0x0190, 0x00); - err += zc0301_write_reg(cam, 0x0116, 0x1D); - err += zc0301_write_reg(cam, 0x0117, 0x40); - err += zc0301_write_reg(cam, 0x0118, 0x99); - err += zc0301_write_reg(cam, 0x0180, 0x42); - err += zc0301_write_reg(cam, 0x0116, 0x1D); - err += zc0301_write_reg(cam, 0x0117, 0x40); - err += zc0301_write_reg(cam, 0x0118, 0x99); - err += zc0301_write_reg(cam, 0x0007, 0x00); - - err += zc0301_i2c_write(cam, 0x11, 0x01); - - msleep(100); - - return err; -} - - -static int pas202bcb_get_ctrl(struct zc0301_device* cam, - struct v4l2_control* ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - { - int r1 = zc0301_i2c_read(cam, 0x04, 1), - r2 = zc0301_i2c_read(cam, 0x05, 1); - if (r1 < 0 || r2 < 0) - return -EIO; - ctrl->value = (r1 << 6) | (r2 & 0x3f); - } - return 0; - case V4L2_CID_RED_BALANCE: - if ((ctrl->value = zc0301_i2c_read(cam, 0x09, 1)) < 0) - return -EIO; - ctrl->value &= 0x0f; - return 0; - case V4L2_CID_BLUE_BALANCE: - if ((ctrl->value = zc0301_i2c_read(cam, 0x07, 1)) < 0) - return -EIO; - ctrl->value &= 0x0f; - return 0; - case V4L2_CID_GAIN: - if ((ctrl->value = zc0301_i2c_read(cam, 0x10, 1)) < 0) - return -EIO; - ctrl->value &= 0x1f; - return 0; - case ZC0301_V4L2_CID_GREEN_BALANCE: - if ((ctrl->value = zc0301_i2c_read(cam, 0x08, 1)) < 0) - return -EIO; - ctrl->value &= 0x0f; - return 0; - case ZC0301_V4L2_CID_DAC_MAGNITUDE: - if ((ctrl->value = zc0301_i2c_read(cam, 0x0c, 1)) < 0) - return -EIO; - return 0; - default: - return -EINVAL; - } -} - - -static int pas202bcb_set_ctrl(struct zc0301_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += zc0301_i2c_write(cam, 0x04, ctrl->value >> 6); - err += zc0301_i2c_write(cam, 0x05, ctrl->value & 0x3f); - break; - case V4L2_CID_RED_BALANCE: - err += zc0301_i2c_write(cam, 0x09, ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - err += zc0301_i2c_write(cam, 0x07, ctrl->value); - break; - case V4L2_CID_GAIN: - err += zc0301_i2c_write(cam, 0x10, ctrl->value); - break; - case ZC0301_V4L2_CID_GREEN_BALANCE: - err += zc0301_i2c_write(cam, 0x08, ctrl->value); - break; - case ZC0301_V4L2_CID_DAC_MAGNITUDE: - err += zc0301_i2c_write(cam, 0x0c, ctrl->value); - break; - default: - return -EINVAL; - } - err += zc0301_i2c_write(cam, 0x11, 0x01); - - return err ? -EIO : 0; -} - - -static struct zc0301_sensor pas202bcb = { - .name = "PAS202BCB", - .init = &pas202bcb_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x01e5, - .maximum = 0x3fff, - .step = 0x0001, - .default_value = 0x01e5, - .flags = V4L2_CTRL_FLAG_DISABLED, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x0c, - .flags = V4L2_CTRL_FLAG_DISABLED, - }, - { - .id = ZC0301_V4L2_CID_DAC_MAGNITUDE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "DAC magnitude", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x00, - .flags = V4L2_CTRL_FLAG_DISABLED, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x01, - .flags = V4L2_CTRL_FLAG_DISABLED, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x05, - .flags = V4L2_CTRL_FLAG_DISABLED, - }, - { - .id = ZC0301_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x00, - .flags = V4L2_CTRL_FLAG_DISABLED, - }, - }, - .get_ctrl = &pas202bcb_get_ctrl, - .set_ctrl = &pas202bcb_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_JPEG, - .priv = 8, - .colorspace = V4L2_COLORSPACE_JPEG, - }, -}; - - -int zc0301_probe_pas202bcb(struct zc0301_device* cam) -{ - int r0 = 0, r1 = 0, err = 0; - unsigned int pid = 0; - - err += zc0301_write_reg(cam, 0x0000, 0x01); - err += zc0301_write_reg(cam, 0x0010, 0x0e); - err += zc0301_write_reg(cam, 0x0001, 0x01); - err += zc0301_write_reg(cam, 0x0012, 0x03); - err += zc0301_write_reg(cam, 0x0012, 0x01); - err += zc0301_write_reg(cam, 0x008d, 0x08); - - msleep(10); - - r0 = zc0301_i2c_read(cam, 0x00, 1); - r1 = zc0301_i2c_read(cam, 0x01, 1); - - if (r0 < 0 || r1 < 0 || err) - return -EIO; - - pid = (r0 << 4) | ((r1 & 0xf0) >> 4); - if (pid != 0x017) - return -ENODEV; - - zc0301_attach_sensor(cam, &pas202bcb); - - return 0; -} diff --git a/drivers/media/video/zc0301/zc0301_pb0330.c b/drivers/media/video/zc0301/zc0301_pb0330.c deleted file mode 100644 index 9519aba..0000000 --- a/drivers/media/video/zc0301/zc0301_pb0330.c +++ /dev/null @@ -1,188 +0,0 @@ -/*************************************************************************** - * Plug-in for PB-0330 image sensor connected to the ZC0301P Image * - * Processor and Control Chip * - * * - * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * Initialization values of the ZC0301[P] have been taken from the SPCA5XX * - * driver maintained by Michel Xhaard <mxhaard@magic.fr> * - * * - * 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/delay.h> -#include "zc0301_sensor.h" - - -static struct zc0301_sensor pb0330; - - -static int pb0330_init(struct zc0301_device* cam) -{ - int err = 0; - - err += zc0301_write_reg(cam, 0x0000, 0x01); - err += zc0301_write_reg(cam, 0x0008, 0x03); - err += zc0301_write_reg(cam, 0x0010, 0x0A); - err += zc0301_write_reg(cam, 0x0002, 0x00); - err += zc0301_write_reg(cam, 0x0003, 0x02); - err += zc0301_write_reg(cam, 0x0004, 0x80); - err += zc0301_write_reg(cam, 0x0005, 0x01); - err += zc0301_write_reg(cam, 0x0006, 0xE0); - err += zc0301_write_reg(cam, 0x0001, 0x01); - err += zc0301_write_reg(cam, 0x0012, 0x05); - err += zc0301_write_reg(cam, 0x0012, 0x07); - err += zc0301_write_reg(cam, 0x0098, 0x00); - err += zc0301_write_reg(cam, 0x009A, 0x00); - err += zc0301_write_reg(cam, 0x011A, 0x00); - err += zc0301_write_reg(cam, 0x011C, 0x00); - err += zc0301_write_reg(cam, 0x0012, 0x05); - - err += zc0301_i2c_write(cam, 0x01, 0x0006); - err += zc0301_i2c_write(cam, 0x02, 0x0011); - err += zc0301_i2c_write(cam, 0x03, 0x01E7); - err += zc0301_i2c_write(cam, 0x04, 0x0287); - err += zc0301_i2c_write(cam, 0x06, 0x0003); - err += zc0301_i2c_write(cam, 0x07, 0x3002); - err += zc0301_i2c_write(cam, 0x20, 0x1100); - err += zc0301_i2c_write(cam, 0x2F, 0xF7B0); - err += zc0301_i2c_write(cam, 0x30, 0x0005); - err += zc0301_i2c_write(cam, 0x31, 0x0000); - err += zc0301_i2c_write(cam, 0x34, 0x0100); - err += zc0301_i2c_write(cam, 0x35, 0x0060); - err += zc0301_i2c_write(cam, 0x3D, 0x068F); - err += zc0301_i2c_write(cam, 0x40, 0x01E0); - err += zc0301_i2c_write(cam, 0x58, 0x0078); - err += zc0301_i2c_write(cam, 0x62, 0x0411); - - err += zc0301_write_reg(cam, 0x0087, 0x10); - err += zc0301_write_reg(cam, 0x0101, 0x37); - err += zc0301_write_reg(cam, 0x0012, 0x05); - err += zc0301_write_reg(cam, 0x0100, 0x0D); - err += zc0301_write_reg(cam, 0x0189, 0x06); - err += zc0301_write_reg(cam, 0x01AD, 0x00); - err += zc0301_write_reg(cam, 0x01C5, 0x03); - err += zc0301_write_reg(cam, 0x01CB, 0x13); - err += zc0301_write_reg(cam, 0x0250, 0x08); - err += zc0301_write_reg(cam, 0x0301, 0x08); - err += zc0301_write_reg(cam, 0x01A8, 0x60); - err += zc0301_write_reg(cam, 0x018D, 0x6C); - err += zc0301_write_reg(cam, 0x01AD, 0x09); - err += zc0301_write_reg(cam, 0x01AE, 0x15); - err += zc0301_write_reg(cam, 0x010A, 0x50); - err += zc0301_write_reg(cam, 0x010B, 0xF8); - err += zc0301_write_reg(cam, 0x010C, 0xF8); - err += zc0301_write_reg(cam, 0x010D, 0xF8); - err += zc0301_write_reg(cam, 0x010E, 0x50); - err += zc0301_write_reg(cam, 0x010F, 0xF8); - err += zc0301_write_reg(cam, 0x0110, 0xF8); - err += zc0301_write_reg(cam, 0x0111, 0xF8); - err += zc0301_write_reg(cam, 0x0112, 0x50); - err += zc0301_write_reg(cam, 0x0008, 0x03); - err += zc0301_write_reg(cam, 0x01C6, 0x08); - err += zc0301_write_reg(cam, 0x01CB, 0x0F); - err += zc0301_write_reg(cam, 0x010A, 0x50); - err += zc0301_write_reg(cam, 0x010B, 0xF8); - err += zc0301_write_reg(cam, 0x010C, 0xF8); - err += zc0301_write_reg(cam, 0x010D, 0xF8); - err += zc0301_write_reg(cam, 0x010E, 0x50); - err += zc0301_write_reg(cam, 0x010F, 0xF8); - err += zc0301_write_reg(cam, 0x0110, 0xF8); - err += zc0301_write_reg(cam, 0x0111, 0xF8); - err += zc0301_write_reg(cam, 0x0112, 0x50); - err += zc0301_write_reg(cam, 0x0180, 0x00); - err += zc0301_write_reg(cam, 0x0019, 0x00); - - err += zc0301_i2c_write(cam, 0x05, 0x0066); - err += zc0301_i2c_write(cam, 0x09, 0x02B2); - err += zc0301_i2c_write(cam, 0x10, 0x0002); - - err += zc0301_write_reg(cam, 0x011D, 0x60); - err += zc0301_write_reg(cam, 0x0190, 0x00); - err += zc0301_write_reg(cam, 0x0191, 0x07); - err += zc0301_write_reg(cam, 0x0192, 0x8C); - err += zc0301_write_reg(cam, 0x0195, 0x00); - err += zc0301_write_reg(cam, 0x0196, 0x00); - err += zc0301_write_reg(cam, 0x0197, 0x8A); - err += zc0301_write_reg(cam, 0x018C, 0x10); - err += zc0301_write_reg(cam, 0x018F, 0x20); - err += zc0301_write_reg(cam, 0x01A9, 0x14); - err += zc0301_write_reg(cam, 0x01AA, 0x24); - err += zc0301_write_reg(cam, 0x001D, 0xD7); - err += zc0301_write_reg(cam, 0x001E, 0xF0); - err += zc0301_write_reg(cam, 0x001F, 0xF8); - err += zc0301_write_reg(cam, 0x0020, 0xFF); - err += zc0301_write_reg(cam, 0x01AD, 0x09); - err += zc0301_write_reg(cam, 0x01AE, 0x15); - err += zc0301_write_reg(cam, 0x0180, 0x40); - err += zc0301_write_reg(cam, 0x0180, 0x42); - - msleep(100); - - return err; -} - - -static struct zc0301_sensor pb0330 = { - .name = "PB-0330", - .init = &pb0330_init, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_JPEG, - .priv = 8, - .colorspace = V4L2_COLORSPACE_JPEG, - }, -}; - - -int zc0301_probe_pb0330(struct zc0301_device* cam) -{ - int r0, err = 0; - - err += zc0301_write_reg(cam, 0x0000, 0x01); - err += zc0301_write_reg(cam, 0x0010, 0x0a); - err += zc0301_write_reg(cam, 0x0001, 0x01); - err += zc0301_write_reg(cam, 0x0012, 0x03); - err += zc0301_write_reg(cam, 0x0012, 0x01); - - msleep(10); - - r0 = zc0301_i2c_read(cam, 0x00, 2); - - if (r0 < 0 || err) - return -EIO; - - if (r0 != 0x8243) - return -ENODEV; - - zc0301_attach_sensor(cam, &pb0330); - - return 0; -} diff --git a/drivers/media/video/zc0301/zc0301_sensor.h b/drivers/media/video/zc0301/zc0301_sensor.h deleted file mode 100644 index 0be783c..0000000 --- a/drivers/media/video/zc0301/zc0301_sensor.h +++ /dev/null @@ -1,107 +0,0 @@ -/*************************************************************************** - * API for image sensors connected to the ZC0301[P] Image Processor and * - * Control Chip * - * * - * Copyright (C) 2006-2007 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 _ZC0301_SENSOR_H_ -#define _ZC0301_SENSOR_H_ - -#include <linux/usb.h> -#include <linux/videodev2.h> -#include <linux/device.h> -#include <linux/stddef.h> -#include <linux/errno.h> -#include <asm/types.h> - -struct zc0301_device; -struct zc0301_sensor; - -/*****************************************************************************/ - -extern int zc0301_probe_pas202bcb(struct zc0301_device* cam); -extern int zc0301_probe_pb0330(struct zc0301_device* cam); - -#define ZC0301_SENSOR_TABLE \ -/* Weak detections must go at the end of the list */ \ -static int (*zc0301_sensor_table[])(struct zc0301_device*) = { \ - &zc0301_probe_pas202bcb, \ - &zc0301_probe_pb0330, \ - NULL, \ -}; - -extern struct zc0301_device* -zc0301_match_id(struct zc0301_device* cam, const struct usb_device_id *id); - -extern void -zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor); - -#define ZC0301_USB_DEVICE(vend, prod, intclass) \ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ - USB_DEVICE_ID_MATCH_INT_CLASS, \ - .idVendor = (vend), \ - .idProduct = (prod), \ - .bInterfaceClass = (intclass) - -#if !defined CONFIG_USB_GSPCA_ZC3XX && !defined CONFIG_USB_GSPCA_ZC3XX_MODULE -#define ZC0301_ID_TABLE \ -static const struct usb_device_id zc0301_id_table[] = { \ - { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202 */ \ - { ZC0301_USB_DEVICE(0x0ac8, 0x303b, 0xff), }, /* PB-0330 */ \ - { } \ -}; -#else -#define ZC0301_ID_TABLE \ -static const struct usb_device_id zc0301_id_table[] = { \ - { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202 */ \ - { } \ -}; -#endif - -/*****************************************************************************/ - -extern int zc0301_write_reg(struct zc0301_device*, u16 index, u16 value); -extern int zc0301_read_reg(struct zc0301_device*, u16 index); -extern int zc0301_i2c_write(struct zc0301_device*, u16 address, u16 value); -extern int zc0301_i2c_read(struct zc0301_device*, u16 address, u8 length); - -/*****************************************************************************/ - -#define ZC0301_MAX_CTRLS (V4L2_CID_LASTP1 - V4L2_CID_BASE + 10) -#define ZC0301_V4L2_CID_DAC_MAGNITUDE (V4L2_CID_PRIVATE_BASE + 0) -#define ZC0301_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE + 1) - -struct zc0301_sensor { - char name[32]; - - struct v4l2_queryctrl qctrl[ZC0301_MAX_CTRLS]; - struct v4l2_cropcap cropcap; - struct v4l2_pix_format pix_format; - - int (*init)(struct zc0301_device*); - int (*get_ctrl)(struct zc0301_device*, struct v4l2_control* ctrl); - int (*set_ctrl)(struct zc0301_device*, - const struct v4l2_control* ctrl); - int (*set_crop)(struct zc0301_device*, const struct v4l2_rect* rect); - - /* Private */ - struct v4l2_queryctrl _qctrl[ZC0301_MAX_CTRLS]; - struct v4l2_rect _rect; -}; - -#endif /* _ZC0301_SENSOR_H_ */ diff --git a/drivers/media/video/zoran/videocodec.c b/drivers/media/video/zoran/videocodec.c index cf24956..c010716 100644 --- a/drivers/media/video/zoran/videocodec.c +++ b/drivers/media/video/zoran/videocodec.c @@ -107,15 +107,14 @@ videocodec_attach (struct videocodec_master *master) if (!try_module_get(h->codec->owner)) return NULL; - codec = - kmalloc(sizeof(struct videocodec), GFP_KERNEL); + codec = kmemdup(h->codec, 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); diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 984a754..9296517 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -147,5 +147,7 @@ source "drivers/staging/mrst-touchscreen/Kconfig" source "drivers/staging/msm/Kconfig" +source "drivers/staging/lirc/Kconfig" + endif # !STAGING_EXCLUDE_BUILD endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 9fa2513..6c5b523 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_SLICOSS) += slicoss/ obj-$(CONFIG_VIDEO_GO7007) += go7007/ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ +obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_USB_IP_COMMON) += usbip/ obj-$(CONFIG_W35UND) += winbond/ obj-$(CONFIG_PRISM2_USB) += wlan-ng/ diff --git a/drivers/staging/cx25821/Makefile b/drivers/staging/cx25821/Makefile index 10f87f0..d0eb16e 100644 --- a/drivers/staging/cx25821/Makefile +++ b/drivers/staging/cx25821/Makefile @@ -1,9 +1,8 @@ -cx25821-objs := cx25821-core.o cx25821-cards.o cx25821-i2c.o cx25821-gpio.o \ - cx25821-medusa-video.o cx25821-video.o cx25821-video0.o cx25821-video1.o \ - cx25821-video2.o cx25821-video3.o cx25821-video4.o cx25821-video5.o \ - cx25821-video6.o cx25821-video7.o cx25821-vidups9.o cx25821-vidups10.o \ - cx25821-audups11.o cx25821-video-upstream.o cx25821-video-upstream-ch2.o \ - cx25821-audio-upstream.o cx25821-videoioctl.o +cx25821-objs := cx25821-core.o cx25821-cards.o cx25821-i2c.o \ + cx25821-gpio.o cx25821-medusa-video.o \ + cx25821-video.o cx25821-video-upstream.o \ + cx25821-video-upstream-ch2.o \ + cx25821-audio-upstream.o obj-$(CONFIG_VIDEO_CX25821) += cx25821.o obj-$(CONFIG_VIDEO_CX25821_ALSA) += cx25821-alsa.o diff --git a/drivers/staging/cx25821/cx25821-alsa.c b/drivers/staging/cx25821/cx25821-alsa.c index 1798975..a43b188 100644 --- a/drivers/staging/cx25821/cx25821-alsa.c +++ b/drivers/staging/cx25821/cx25821-alsa.c @@ -55,6 +55,12 @@ static struct snd_card *snd_cx25821_cards[SNDRV_CARDS]; static int devno; +struct cx25821_audio_buffer { + unsigned int bpl; + struct btcx_riscmem risc; + struct videobuf_dmabuf dma; +}; + struct cx25821_audio_dev { struct cx25821_dev *dev; struct cx25821_dmaqueue q; @@ -77,7 +83,7 @@ struct cx25821_audio_dev { struct videobuf_dmabuf *dma_risc; - struct cx25821_buffer *buf; + struct cx25821_audio_buffer *buf; struct snd_pcm_substream *substream; }; @@ -136,7 +142,7 @@ MODULE_PARM_DESC(debug, "enable debug messages"); static int _cx25821_start_audio_dma(struct cx25821_audio_dev *chip) { - struct cx25821_buffer *buf = chip->buf; + struct cx25821_audio_buffer *buf = chip->buf; struct cx25821_dev *dev = chip->dev; struct sram_channel *audio_ch = &cx25821_sram_channels[AUDIO_SRAM_CHANNEL]; @@ -331,7 +337,7 @@ static int dsp_buffer_free(struct cx25821_audio_dev *chip) BUG_ON(!chip->dma_size); dprintk(2, "Freeing buffer\n"); - videobuf_sg_dma_unmap(&chip->pci->dev, chip->dma_risc); + videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc); videobuf_dma_free(chip->dma_risc); btcx_riscmem_free(chip->pci, &chip->buf->risc); kfree(chip->buf); @@ -432,7 +438,7 @@ static int snd_cx25821_hw_params(struct snd_pcm_substream *substream, struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); struct videobuf_dmabuf *dma; - struct cx25821_buffer *buf; + struct cx25821_audio_buffer *buf; int ret; if (substream->runtime->dma_area) { @@ -447,36 +453,31 @@ static int snd_cx25821_hw_params(struct snd_pcm_substream *substream, BUG_ON(!chip->dma_size); BUG_ON(chip->num_periods & (chip->num_periods - 1)); - buf = videobuf_sg_alloc(sizeof(*buf)); + buf = kzalloc(sizeof(*buf), GFP_KERNEL); if (NULL == buf) return -ENOMEM; if (chip->period_size > AUDIO_LINE_SIZE) chip->period_size = AUDIO_LINE_SIZE; - buf->vb.memory = V4L2_MEMORY_MMAP; - buf->vb.field = V4L2_FIELD_NONE; - buf->vb.width = chip->period_size; buf->bpl = chip->period_size; - buf->vb.height = chip->num_periods; - buf->vb.size = chip->dma_size; - dma = videobuf_to_dma(&buf->vb); + dma = &buf->dma; videobuf_dma_init(dma); - ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE, - (PAGE_ALIGN(buf->vb.size) >> + (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT)); if (ret < 0) goto error; - ret = videobuf_sg_dma_map(&chip->pci->dev, dma); + ret = videobuf_dma_map(&chip->pci->dev, dma); if (ret < 0) goto error; ret = cx25821_risc_databuffer_audio(chip->pci, &buf->risc, dma->sglist, - buf->vb.width, buf->vb.height, 1); + chip->period_size, chip->num_periods, + 1); if (ret < 0) { printk(KERN_INFO "DEBUG: ERROR after cx25821_risc_databuffer_audio()\n"); @@ -488,12 +489,10 @@ static int snd_cx25821_hw_params(struct snd_pcm_substream *substream, buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma); buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ - buf->vb.state = VIDEOBUF_PREPARED; - chip->buf = buf; chip->dma_risc = dma; - substream->runtime->dma_area = chip->dma_risc->vmalloc; + substream->runtime->dma_area = chip->dma_risc->vaddr; substream->runtime->dma_bytes = chip->dma_size; substream->runtime->dma_addr = 0; diff --git a/drivers/staging/cx25821/cx25821-audio-upstream.c b/drivers/staging/cx25821/cx25821-audio-upstream.c index eb39d13..cdff49f 100644 --- a/drivers/staging/cx25821/cx25821-audio-upstream.c +++ b/drivers/staging/cx25821/cx25821-audio-upstream.c @@ -106,7 +106,7 @@ static __le32 *cx25821_risc_field_upstream_audio(struct cx25821_dev *dev, { unsigned int line; struct sram_channel *sram_ch = - &dev->sram_channels[dev->_audio_upstream_channel_select]; + dev->channels[dev->_audio_upstream_channel_select].sram_channels; int offset = 0; /* scan lines */ @@ -217,7 +217,7 @@ void cx25821_free_memory_audio(struct cx25821_dev *dev) void cx25821_stop_upstream_audio(struct cx25821_dev *dev) { struct sram_channel *sram_ch = - &dev->sram_channels[AUDIO_UPSTREAM_SRAM_CHANNEL_B]; + dev->channels[AUDIO_UPSTREAM_SRAM_CHANNEL_B].sram_channels; u32 tmp = 0; if (!dev->_audio_is_running) { @@ -287,14 +287,14 @@ int cx25821_get_audio_data(struct cx25821_dev *dev, return PTR_ERR(myfile); } else { if (!(myfile->f_op)) { - printk("%s: File has no file operations registered!\n", + printk(KERN_ERR "%s: File has no file operations registered!\n", __func__); filp_close(myfile, NULL); return -EIO; } if (!myfile->f_op->read) { - printk("%s: File has no READ operations registered!\n", + printk(KERN_ERR "%s: File has no READ operations registered!\n", __func__); filp_close(myfile, NULL); return -EIO; @@ -353,8 +353,9 @@ static void cx25821_audioups_handler(struct work_struct *work) } cx25821_get_audio_data(dev, - &dev->sram_channels[dev-> - _audio_upstream_channel_select]); + dev->channels[dev-> + _audio_upstream_channel_select]. + sram_channels); } int cx25821_openfile_audio(struct cx25821_dev *dev, @@ -378,14 +379,14 @@ int cx25821_openfile_audio(struct cx25821_dev *dev, return PTR_ERR(myfile); } else { if (!(myfile->f_op)) { - printk("%s: File has no file operations registered!\n", + printk(KERN_ERR "%s: File has no file operations registered!\n", __func__); filp_close(myfile, NULL); return -EIO; } if (!myfile->f_op->read) { - printk("%s: File has no READ operations registered!\n", + printk(KERN_ERR "%s: File has no READ operations registered!\n", __func__); filp_close(myfile, NULL); return -EIO; @@ -505,7 +506,7 @@ int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num, { int i = 0; u32 int_msk_tmp; - struct sram_channel *channel = &dev->sram_channels[chan_num]; + struct sram_channel *channel = dev->channels[chan_num].sram_channels; dma_addr_t risc_phys_jump_addr; __le32 *rp; @@ -569,15 +570,15 @@ int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num, spin_unlock(&dev->slock); } else { if (status & FLD_AUD_SRC_OF) - printk("%s: Audio Received Overflow Error Interrupt!\n", + printk(KERN_WARNING "%s: Audio Received Overflow Error Interrupt!\n", __func__); if (status & FLD_AUD_SRC_SYNC) - printk("%s: Audio Received Sync Error Interrupt!\n", + printk(KERN_WARNING "%s: Audio Received Sync Error Interrupt!\n", __func__); if (status & FLD_AUD_SRC_OPC_ERR) - printk("%s: Audio Received OpCode Error Interrupt!\n", + printk(KERN_WARNING "%s: Audio Received OpCode Error Interrupt!\n", __func__); /* Read and write back the interrupt status register to clear @@ -586,7 +587,7 @@ int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num, } if (dev->_audiofile_status == END_OF_FILE) { - printk("cx25821: EOF Channel Audio Framecount = %d\n", + printk(KERN_WARNING "cx25821: EOF Channel Audio Framecount = %d\n", dev->_audioframe_count); return -1; } @@ -607,7 +608,8 @@ static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id) if (!dev) return -1; - sram_ch = &dev->sram_channels[dev->_audio_upstream_channel_select]; + sram_ch = dev->channels[dev->_audio_upstream_channel_select]. + sram_channels; msk_stat = cx_read(sram_ch->int_mstat); audio_status = cx_read(sram_ch->int_stat); @@ -644,8 +646,8 @@ static void cx25821_wait_fifo_enable(struct cx25821_dev *dev, /* 10 millisecond timeout */ if (count++ > 1000) { - printk - ("cx25821 ERROR: %s() fifo is NOT turned on. Timeout!\n", + printk(KERN_ERR + "cx25821 ERROR: %s() fifo is NOT turned on. Timeout!\n", __func__); return; } @@ -726,12 +728,12 @@ int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select) int str_length = 0; if (dev->_audio_is_running) { - printk("Audio Channel is still running so return!\n"); + printk(KERN_WARNING "Audio Channel is still running so return!\n"); return 0; } dev->_audio_upstream_channel_select = channel_select; - sram_ch = &dev->sram_channels[channel_select]; + sram_ch = dev->channels[channel_select].sram_channels; /* Work queue */ INIT_WORK(&dev->_audio_work_entry, cx25821_audioups_handler); diff --git a/drivers/staging/cx25821/cx25821-audio.h b/drivers/staging/cx25821/cx25821-audio.h index 503f42f..434b2a3 100644 --- a/drivers/staging/cx25821/cx25821-audio.h +++ b/drivers/staging/cx25821/cx25821-audio.h @@ -27,24 +27,25 @@ #define LINES_PER_BUFFER 15 #define AUDIO_LINE_SIZE 128 -//Number of buffer programs to use at once. +/* Number of buffer programs to use at once. */ #define NUMBER_OF_PROGRAMS 8 -//Max size of the RISC program for a buffer. - worst case is 2 writes per line -// Space is also added for the 4 no-op instructions added on the end. - +/* + Max size of the RISC program for a buffer. - worst case is 2 writes per line + Space is also added for the 4 no-op instructions added on the end. +*/ #ifndef USE_RISC_NOOP #define MAX_BUFFER_PROGRAM_SIZE \ (2*LINES_PER_BUFFER*RISC_WRITE_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE*4) #endif -// MAE 12 July 2005 Try to use NOOP RISC instruction instead +/* MAE 12 July 2005 Try to use NOOP RISC instruction instead */ #ifdef USE_RISC_NOOP #define MAX_BUFFER_PROGRAM_SIZE \ (2*LINES_PER_BUFFER*RISC_WRITE_INSTRUCTION_SIZE + RISC_NOOP_INSTRUCTION_SIZE*4) #endif -//Sizes of various instructions in bytes. Used when adding instructions. +/* Sizes of various instructions in bytes. Used when adding instructions. */ #define RISC_WRITE_INSTRUCTION_SIZE 12 #define RISC_JUMP_INSTRUCTION_SIZE 12 #define RISC_SKIP_INSTRUCTION_SIZE 4 diff --git a/drivers/staging/cx25821/cx25821-audups11.c b/drivers/staging/cx25821/cx25821-audups11.c deleted file mode 100644 index e49ead9..0000000 --- a/drivers/staging/cx25821/cx25821-audups11.c +++ /dev/null @@ -1,420 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> - * Based on Steven Toth <stoth@linuxtv.org> cx23885 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. - */ - -#include <linux/slab.h> - -#include "cx25821-video.h" - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct cx25821_buffer *buf = - container_of(vb, struct cx25821_buffer, vb); - struct cx25821_buffer *prev; - struct cx25821_fh *fh = vq->priv_data; - struct cx25821_dev *dev = fh->dev; - struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH11]; - - /* add jump to stopper */ - buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); - buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ - - dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); - - if (!list_empty(&q->queued)) { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, - buf->vb.i); - - } else if (list_empty(&q->active)) { - list_add_tail(&buf->vb.queue, &q->active); - cx25821_start_video_dma(dev, q, buf, - &dev->sram_channels[SRAM_CH11]); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - dprintk(2, - "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", - buf, buf->vb.i, buf->count, q->count); - } else { - prev = - list_entry(q->active.prev, struct cx25821_buffer, vb.queue); - if (prev->vb.width == buf->vb.width - && prev->vb.height == buf->vb.height - && prev->fmt == buf->fmt) { - list_add_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - - /* 64 bit bits 63-32 */ - prev->risc.jmp[2] = cpu_to_le32(0); - dprintk(2, - "[%p/%d] buffer_queue - append to active, buf->count=%d\n", - buf, buf->vb.i, buf->count); - - } else { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, - buf->vb.i); - } - } - - if (list_empty(&q->active)) { - dprintk(2, "active queue empty!\n"); - } -} - -static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = cx25821_buffer_setup, - .buf_prepare = cx25821_buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = cx25821_buffer_release, -}; - -static int video_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct cx25821_dev *dev = video_drvdata(file); - struct cx25821_fh *fh; - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - printk("open dev=%s type=%s\n", video_device_node_name(vdev), - v4l2_type_names[type]); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) - return -ENOMEM; - - lock_kernel(); - - file->private_data = fh; - fh->dev = dev; - fh->type = type; - fh->width = 720; - - if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) - fh->height = 576; - else - fh->height = 480; - - dev->channel_opened = 10; - fh->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV); - - v4l2_prio_open(&dev->prio, &fh->prio); - - videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct cx25821_buffer), fh); - - dprintk(1, "post videobuf_queue_init()\n"); - unlock_kernel(); - - return 0; -} - -static ssize_t video_read(struct file *file, char __user * data, size_t count, - loff_t * ppos) -{ - struct cx25821_fh *fh = file->private_data; - - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO11)) - return -EBUSY; - - return videobuf_read_one(&fh->vidq, data, count, ppos, - file->f_flags & O_NONBLOCK); - - default: - BUG(); - return 0; - } -} - -static unsigned int video_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_buffer *buf; - - if (cx25821_res_check(fh, RESOURCE_VIDEO11)) { - /* streaming capture */ - if (list_empty(&fh->vidq.stream)) - return POLLERR; - buf = list_entry(fh->vidq.stream.next, - struct cx25821_buffer, vb.stream); - } else { - /* read() capture */ - buf = (struct cx25821_buffer *)fh->vidq.read_buf; - if (NULL == buf) - return POLLERR; - } - - poll_wait(file, &buf->vb.done, wait); - if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) - return POLLIN | POLLRDNORM; - return 0; -} - -static int video_release(struct file *file) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_dev *dev = fh->dev; - - //stop the risc engine and fifo - //cx_write(channel11->dma_ctl, 0); - - /* stop video capture */ - if (cx25821_res_check(fh, RESOURCE_VIDEO11)) { - videobuf_queue_cancel(&fh->vidq); - cx25821_res_free(dev, fh, RESOURCE_VIDEO11); - } - - if (fh->vidq.read_buf) { - cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); - kfree(fh->vidq.read_buf); - } - - videobuf_mmap_free(&fh->vidq); - - v4l2_prio_close(&dev->prio, fh->prio); - - file->private_data = NULL; - kfree(fh); - - return 0; -} - -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - - if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { - return -EINVAL; - } - - if (unlikely(i != fh->type)) { - return -EINVAL; - } - - if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO11)))) { - return -EBUSY; - } - - return videobuf_streamon(get_queue(fh)); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - int err, res; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (i != fh->type) - return -EINVAL; - - res = cx25821_get_resource(fh, RESOURCE_VIDEO11); - err = videobuf_streamoff(get_queue(fh)); - if (err < 0) - return err; - cx25821_res_free(dev, fh, res); - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - dprintk(2, "%s()\n", __func__); - err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); - - if (0 != err) - return err; - fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - fh->width = f->fmt.pix.width; - fh->height = f->fmt.pix.height; - fh->vidq.field = f->fmt.pix.field; - dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, - fh->height, fh->vidq.field); - cx25821_call_all(dev, video, s_fmt, f); - return 0; -} - -static long video_ioctl_upstream11(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_dev *dev = fh->dev; - int command = 0; - struct upstream_user_struct *data_from_user; - - data_from_user = (struct upstream_user_struct *)arg; - - if (!data_from_user) { - printk - ("cx25821 in %s(): Upstream data is INVALID. Returning.\n", - __func__); - return 0; - } - - command = data_from_user->command; - - if (command != UPSTREAM_START_AUDIO && command != UPSTREAM_STOP_AUDIO) { - return 0; - } - - dev->input_filename = data_from_user->input_filename; - dev->input_audiofilename = data_from_user->input_filename; - dev->vid_stdname = data_from_user->vid_stdname; - dev->pixel_format = data_from_user->pixel_format; - dev->channel_select = data_from_user->channel_select; - dev->command = data_from_user->command; - - switch (command) { - case UPSTREAM_START_AUDIO: - cx25821_start_upstream_audio(dev, data_from_user); - break; - - case UPSTREAM_STOP_AUDIO: - cx25821_stop_upstream_audio(dev); - break; - } - - return 0; -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct cx25821_fh *fh = priv; - return videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); -} - -static int vidioc_log_status(struct file *file, void *priv) -{ - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - char name[32 + 2]; - - snprintf(name, sizeof(name), "%s/2", dev->name); - printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", - dev->name); - cx25821_call_all(dev, core, log_status); - printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", - dev->name); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev; - int err; - - if (fh) { - dev = fh->dev; - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - return 0; -} - -// exported stuff -static const struct v4l2_file_operations video_fops = { - .owner = THIS_MODULE, - .open = video_open, - .release = video_release, - .read = video_read, - .poll = video_poll, - .mmap = cx25821_video_mmap, - .ioctl = video_ioctl_upstream11, -}; - -static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = cx25821_vidioc_querycap, - .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = cx25821_vidioc_reqbufs, - .vidioc_querybuf = cx25821_vidioc_querybuf, - .vidioc_qbuf = cx25821_vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, -#ifdef TUNER_FLAG - .vidioc_s_std = cx25821_vidioc_s_std, - .vidioc_querystd = cx25821_vidioc_querystd, -#endif - .vidioc_cropcap = cx25821_vidioc_cropcap, - .vidioc_s_crop = cx25821_vidioc_s_crop, - .vidioc_g_crop = cx25821_vidioc_g_crop, - .vidioc_enum_input = cx25821_vidioc_enum_input, - .vidioc_g_input = cx25821_vidioc_g_input, - .vidioc_s_input = cx25821_vidioc_s_input, - .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = cx25821_vidioc_queryctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = cx25821_vidioc_g_priority, - .vidioc_s_priority = cx25821_vidioc_s_priority, -#ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = cx25821_vidiocgmbuf, -#endif -#ifdef TUNER_FLAG - .vidioc_g_tuner = cx25821_vidioc_g_tuner, - .vidioc_s_tuner = cx25821_vidioc_s_tuner, - .vidioc_g_frequency = cx25821_vidioc_g_frequency, - .vidioc_s_frequency = cx25821_vidioc_s_frequency, -#endif -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = cx25821_vidioc_g_register, - .vidioc_s_register = cx25821_vidioc_s_register, -#endif -}; - -struct video_device cx25821_video_template11 = { - .name = "cx25821-audioupstream", - .fops = &video_fops, - .ioctl_ops = &video_ioctl_ops, - .tvnorms = CX25821_NORMS, - .current_norm = V4L2_STD_NTSC_M, -}; diff --git a/drivers/staging/cx25821/cx25821-core.c b/drivers/staging/cx25821/cx25821-core.c index d90abb3..c487c19 100644 --- a/drivers/staging/cx25821/cx25821-core.c +++ b/drivers/staging/cx25821/cx25821-core.c @@ -781,14 +781,14 @@ static void cx25821_shutdown(struct cx25821_dev *dev) /* Disable Video A/B activity */ for (i = 0; i < VID_CHANNEL_NUM; i++) { - cx_write(dev->sram_channels[i].dma_ctl, 0); - cx_write(dev->sram_channels[i].int_msk, 0); + cx_write(dev->channels[i].sram_channels->dma_ctl, 0); + cx_write(dev->channels[i].sram_channels->int_msk, 0); } for (i = VID_UPSTREAM_SRAM_CHANNEL_I; i <= VID_UPSTREAM_SRAM_CHANNEL_J; i++) { - cx_write(dev->sram_channels[i].dma_ctl, 0); - cx_write(dev->sram_channels[i].int_msk, 0); + cx_write(dev->channels[i].sram_channels->dma_ctl, 0); + cx_write(dev->channels[i].sram_channels->int_msk, 0); } /* Disable Audio activity */ @@ -805,12 +805,10 @@ static void cx25821_shutdown(struct cx25821_dev *dev) void cx25821_set_pixel_format(struct cx25821_dev *dev, int channel_select, u32 format) { - struct sram_channel *ch; - if (channel_select <= 7 && channel_select >= 0) { - ch = &cx25821_sram_channels[channel_select]; - cx_write(ch->pix_frmt, format); - dev->pixel_formats[channel_select] = format; + cx_write(dev->channels[channel_select]. + sram_channels->pix_frmt, format); + dev->channels[channel_select].pixel_formats = format; } } @@ -831,7 +829,7 @@ static void cx25821_initialize(struct cx25821_dev *dev) cx_write(PCI_INT_STAT, 0xffffffff); for (i = 0; i < VID_CHANNEL_NUM; i++) - cx_write(dev->sram_channels[i].int_stat, 0xffffffff); + cx_write(dev->channels[i].sram_channels->int_stat, 0xffffffff); cx_write(AUD_A_INT_STAT, 0xffffffff); cx_write(AUD_B_INT_STAT, 0xffffffff); @@ -845,21 +843,22 @@ static void cx25821_initialize(struct cx25821_dev *dev) mdelay(100); for (i = 0; i < VID_CHANNEL_NUM; i++) { - cx25821_set_vip_mode(dev, &dev->sram_channels[i]); - cx25821_sram_channel_setup(dev, &dev->sram_channels[i], 1440, - 0); - dev->pixel_formats[i] = PIXEL_FRMT_422; - dev->use_cif_resolution[i] = FALSE; + cx25821_set_vip_mode(dev, dev->channels[i].sram_channels); + cx25821_sram_channel_setup(dev, dev->channels[i].sram_channels, + 1440, 0); + dev->channels[i].pixel_formats = PIXEL_FRMT_422; + dev->channels[i].use_cif_resolution = FALSE; } /* Probably only affect Downstream */ for (i = VID_UPSTREAM_SRAM_CHANNEL_I; i <= VID_UPSTREAM_SRAM_CHANNEL_J; i++) { - cx25821_set_vip_mode(dev, &dev->sram_channels[i]); + cx25821_set_vip_mode(dev, dev->channels[i].sram_channels); } - cx25821_sram_channel_setup_audio(dev, &dev->sram_channels[SRAM_CH08], - 128, 0); + cx25821_sram_channel_setup_audio(dev, + dev->channels[SRAM_CH08].sram_channels, + 128, 0); cx25821_gpio_init(dev); } @@ -902,21 +901,6 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) { int io_size = 0, i; - struct video_device *video_template[] = { - &cx25821_video_template0, - &cx25821_video_template1, - &cx25821_video_template2, - &cx25821_video_template3, - &cx25821_video_template4, - &cx25821_video_template5, - &cx25821_video_template6, - &cx25821_video_template7, - &cx25821_video_template9, - &cx25821_video_template10, - &cx25821_video_template11, - &cx25821_videoioctl_template, - }; - printk(KERN_INFO "\n***********************************\n"); printk(KERN_INFO "cx25821 set up\n"); printk(KERN_INFO "***********************************\n\n"); @@ -947,7 +931,8 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) /* Apply a sensible clock frequency for the PCIe bridge */ dev->clk_freq = 28000000; - dev->sram_channels = cx25821_sram_channels; + for (i = 0; i < MAX_VID_CHANNEL_NUM; i++) + dev->channels[i].sram_channels = &cx25821_sram_channels[i]; if (dev->nr > 1) CX25821_INFO("dev->nr > 1!"); @@ -970,7 +955,6 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) dev->i2c_bus[0].reg_wdata = I2C1_WDATA; dev->i2c_bus[0].i2c_period = (0x07 << 24); /* 1.95MHz */ - if (cx25821_get_resources(dev) < 0) { printk(KERN_ERR "%s No more PCIe resources for " "subsystem: %04x:%04x\n", @@ -1018,37 +1002,24 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) dev->i2c_bus[0].i2c_rc); cx25821_card_setup(dev); - medusa_video_init(dev); - for (i = 0; i < VID_CHANNEL_NUM; i++) { - if (cx25821_video_register(dev, i, video_template[i]) < 0) { - printk(KERN_ERR - "%s() Failed to register analog video adapters on VID channel %d\n", - __func__, i); - } - } + if (medusa_video_init(dev) < 0) + CX25821_ERR("%s() Failed to initialize medusa!\n" + , __func__); - for (i = VID_UPSTREAM_SRAM_CHANNEL_I; - i <= AUDIO_UPSTREAM_SRAM_CHANNEL_B; i++) { - /* Since we don't have template8 for Audio Downstream */ - if (cx25821_video_register(dev, i, video_template[i - 1]) < 0) { - printk(KERN_ERR - "%s() Failed to register analog video adapters for Upstream channel %d.\n", - __func__, i); - } - } + cx25821_video_register(dev); /* register IOCTL device */ dev->ioctl_dev = - cx25821_vdev_init(dev, dev->pci, video_template[VIDEO_IOCTL_CH], + cx25821_vdev_init(dev, dev->pci, &cx25821_videoioctl_template, "video"); if (video_register_device (dev->ioctl_dev, VFL_TYPE_GRABBER, VIDEO_IOCTL_CH) < 0) { cx25821_videoioctl_unregister(dev); printk(KERN_ERR - "%s() Failed to register video adapter for IOCTL so releasing.\n", - __func__); + "%s() Failed to register video adapter for IOCTL, so \ + unregistering videoioctl device.\n", __func__); } cx25821_dev_checkrevision(dev); @@ -1349,7 +1320,7 @@ void cx25821_free_buffer(struct videobuf_queue *q, struct cx25821_buffer *buf) BUG_ON(in_interrupt()); videobuf_waiton(&buf->vb, 0, 0); - videobuf_dma_unmap(q, dma); + videobuf_dma_unmap(q->dev, dma); videobuf_dma_free(dma); btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc); buf->vb.state = VIDEOBUF_NEEDS_INIT; @@ -1371,7 +1342,8 @@ static irqreturn_t cx25821_irq(int irq, void *dev_id) for (i = 0; i < VID_CHANNEL_NUM; i++) { if (pci_status & mask[i]) { - vid_status = cx_read(dev->sram_channels[i].int_stat); + vid_status = cx_read(dev->channels[i]. + sram_channels->int_stat); if (vid_status) handled += diff --git a/drivers/staging/cx25821/cx25821-i2c.c b/drivers/staging/cx25821/cx25821-i2c.c index 08f45b5..e43572e 100644 --- a/drivers/staging/cx25821/cx25821-i2c.c +++ b/drivers/staging/cx25821/cx25821-i2c.c @@ -282,6 +282,9 @@ static u32 cx25821_functionality(struct i2c_adapter *adap) static struct i2c_algorithm cx25821_i2c_algo_template = { .master_xfer = i2c_xfer, .functionality = cx25821_functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif }; static struct i2c_adapter cx25821_i2c_adap_template = { diff --git a/drivers/staging/cx25821/cx25821-medusa-defines.h b/drivers/staging/cx25821/cx25821-medusa-defines.h index b0d216b..60d197f 100644 --- a/drivers/staging/cx25821/cx25821-medusa-defines.h +++ b/drivers/staging/cx25821/cx25821-medusa-defines.h @@ -23,7 +23,7 @@ #ifndef _MEDUSA_DEF_H_ #define _MEDUSA_DEF_H_ -// Video deocder that we supported +/* Video deocder that we supported */ #define VDEC_A 0 #define VDEC_B 1 #define VDEC_C 2 @@ -33,19 +33,10 @@ #define VDEC_G 6 #define VDEC_H 7 -//#define AUTO_SWITCH_BIT[] = { 8, 9, 10, 11, 12, 13, 14, 15 }; - -// The following bit position enables automatic source switching for decoder A-H. -// Display index per camera. -//#define VDEC_INDEX[] = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7}; - -// Select input bit to video decoder A-H. -//#define CH_SRC_SEL_BIT[] = {24, 25, 26, 27, 28, 29, 30, 31}; - -// end of display sequence +/* end of display sequence */ #define END_OF_SEQ 0xF; -// registry string size +/* registry string size */ #define MAX_REGISTRY_SZ 40; #endif diff --git a/drivers/staging/cx25821/cx25821-medusa-reg.h b/drivers/staging/cx25821/cx25821-medusa-reg.h index 12c90f8..f7f33b3 100644 --- a/drivers/staging/cx25821/cx25821-medusa-reg.h +++ b/drivers/staging/cx25821/cx25821-medusa-reg.h @@ -23,11 +23,11 @@ #ifndef __MEDUSA_REGISTERS__ #define __MEDUSA_REGISTERS__ -// Serial Slave Registers +/* Serial Slave Registers */ #define HOST_REGISTER1 0x0000 #define HOST_REGISTER2 0x0001 -// Chip Configuration Registers +/* Chip Configuration Registers */ #define CHIP_CTRL 0x0100 #define AFE_AB_CTRL 0x0104 #define AFE_CD_CTRL 0x0108 @@ -92,7 +92,7 @@ #define ABIST_CLAMP_E 0x01F4 #define ABIST_CLAMP_F 0x01F8 -// Digital Video Encoder A Registers +/* Digital Video Encoder A Registers */ #define DENC_A_REG_1 0x0200 #define DENC_A_REG_2 0x0204 #define DENC_A_REG_3 0x0208 @@ -102,7 +102,7 @@ #define DENC_A_REG_7 0x0218 #define DENC_A_REG_8 0x021C -// Digital Video Encoder B Registers +/* Digital Video Encoder B Registers */ #define DENC_B_REG_1 0x0300 #define DENC_B_REG_2 0x0304 #define DENC_B_REG_3 0x0308 @@ -112,7 +112,7 @@ #define DENC_B_REG_7 0x0318 #define DENC_B_REG_8 0x031C -// Video Decoder A Registers +/* Video Decoder A Registers */ #define MODE_CTRL 0x1000 #define OUT_CTRL1 0x1004 #define OUT_CTRL_NS 0x1008 @@ -153,7 +153,7 @@ #define VERSION 0x11F8 #define SOFT_RST_CTRL 0x11FC -// Video Decoder B Registers +/* Video Decoder B Registers */ #define VDEC_B_MODE_CTRL 0x1200 #define VDEC_B_OUT_CTRL1 0x1204 #define VDEC_B_OUT_CTRL_NS 0x1208 @@ -194,7 +194,7 @@ #define VDEC_B_VERSION 0x13F8 #define VDEC_B_SOFT_RST_CTRL 0x13FC -// Video Decoder C Registers +/* Video Decoder C Registers */ #define VDEC_C_MODE_CTRL 0x1400 #define VDEC_C_OUT_CTRL1 0x1404 #define VDEC_C_OUT_CTRL_NS 0x1408 @@ -235,7 +235,7 @@ #define VDEC_C_VERSION 0x15F8 #define VDEC_C_SOFT_RST_CTRL 0x15FC -// Video Decoder D Registers +/* Video Decoder D Registers */ #define VDEC_D_MODE_CTRL 0x1600 #define VDEC_D_OUT_CTRL1 0x1604 #define VDEC_D_OUT_CTRL_NS 0x1608 @@ -276,7 +276,7 @@ #define VDEC_D_VERSION 0x17F8 #define VDEC_D_SOFT_RST_CTRL 0x17FC -// Video Decoder E Registers +/* Video Decoder E Registers */ #define VDEC_E_MODE_CTRL 0x1800 #define VDEC_E_OUT_CTRL1 0x1804 #define VDEC_E_OUT_CTRL_NS 0x1808 @@ -317,7 +317,7 @@ #define VDEC_E_VERSION 0x19F8 #define VDEC_E_SOFT_RST_CTRL 0x19FC -// Video Decoder F Registers +/* Video Decoder F Registers */ #define VDEC_F_MODE_CTRL 0x1A00 #define VDEC_F_OUT_CTRL1 0x1A04 #define VDEC_F_OUT_CTRL_NS 0x1A08 @@ -358,7 +358,7 @@ #define VDEC_F_VERSION 0x1BF8 #define VDEC_F_SOFT_RST_CTRL 0x1BFC -// Video Decoder G Registers +/* Video Decoder G Registers */ #define VDEC_G_MODE_CTRL 0x1C00 #define VDEC_G_OUT_CTRL1 0x1C04 #define VDEC_G_OUT_CTRL_NS 0x1C08 @@ -399,7 +399,7 @@ #define VDEC_G_VERSION 0x1DF8 #define VDEC_G_SOFT_RST_CTRL 0x1DFC -// Video Decoder H Registers +/* Video Decoder H Registers */ #define VDEC_H_MODE_CTRL 0x1E00 #define VDEC_H_OUT_CTRL1 0x1E04 #define VDEC_H_OUT_CTRL_NS 0x1E08 @@ -440,14 +440,14 @@ #define VDEC_H_VERSION 0x1FF8 #define VDEC_H_SOFT_RST_CTRL 0x1FFC -//***************************************************************************** -// LUMA_CTRL register fields +/*****************************************************************************/ +/* LUMA_CTRL register fields */ #define VDEC_A_BRITE_CTRL 0x1014 #define VDEC_A_CNTRST_CTRL 0x1015 #define VDEC_A_PEAK_SEL 0x1016 -//***************************************************************************** -// CHROMA_CTRL register fields +/*****************************************************************************/ +/* CHROMA_CTRL register fields */ #define VDEC_A_USAT_CTRL 0x1018 #define VDEC_A_VSAT_CTRL 0x1019 #define VDEC_A_HUE_CTRL 0x101A diff --git a/drivers/staging/cx25821/cx25821-medusa-video.c b/drivers/staging/cx25821/cx25821-medusa-video.c index 34616dc..ef9f2b8 100644 --- a/drivers/staging/cx25821/cx25821-medusa-video.c +++ b/drivers/staging/cx25821/cx25821-medusa-video.c @@ -778,9 +778,9 @@ int medusa_set_saturation(struct cx25821_dev *dev, int saturation, int decoder) int medusa_video_init(struct cx25821_dev *dev) { - u32 value, tmp = 0; - int ret_val; - int i; + u32 value = 0, tmp = 0; + int ret_val = 0; + int i = 0; mutex_lock(&dev->lock); @@ -790,6 +790,7 @@ int medusa_video_init(struct cx25821_dev *dev) value = cx25821_i2c_read(&dev->i2c_bus[0], MON_A_CTRL, &tmp); value &= 0xFFFFF0FF; ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MON_A_CTRL, value); + if (ret_val < 0) goto error; @@ -797,6 +798,7 @@ int medusa_video_init(struct cx25821_dev *dev) value = cx25821_i2c_read(&dev->i2c_bus[0], MON_A_CTRL, &tmp); value &= 0xFFFFFFDF; ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MON_A_CTRL, value); + if (ret_val < 0) goto error; @@ -812,6 +814,7 @@ int medusa_video_init(struct cx25821_dev *dev) value &= 0xFF70FF70; value |= 0x00090008; /* set en_active */ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_AB_CTRL, value); + if (ret_val < 0) goto error; @@ -826,8 +829,10 @@ int medusa_video_init(struct cx25821_dev *dev) /* select AFE clock to output mode */ value = cx25821_i2c_read(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, &tmp); value &= 0x83FFFFFF; - ret_val = cx25821_i2c_write(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, - value | 0x10000000); + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, + value | 0x10000000); + if (ret_val < 0) goto error; @@ -849,12 +854,15 @@ int medusa_video_init(struct cx25821_dev *dev) value |= 7; ret_val = cx25821_i2c_write(&dev->i2c_bus[0], PIN_OE_CTRL, value); + if (ret_val < 0) goto error; + mutex_unlock(&dev->lock); ret_val = medusa_set_videostandard(dev); + return ret_val; error: diff --git a/drivers/staging/cx25821/cx25821-medusa-video.h b/drivers/staging/cx25821/cx25821-medusa-video.h index 2fab4b2..6175e09 100644 --- a/drivers/staging/cx25821/cx25821-medusa-video.h +++ b/drivers/staging/cx25821/cx25821-medusa-video.h @@ -25,7 +25,7 @@ #include "cx25821-medusa-defines.h" -// Color control constants +/* Color control constants */ #define VIDEO_PROCAMP_MIN 0 #define VIDEO_PROCAMP_MAX 10000 #define UNSIGNED_BYTE_MIN 0 @@ -33,7 +33,7 @@ #define SIGNED_BYTE_MIN -128 #define SIGNED_BYTE_MAX 127 -// Default video color settings +/* Default video color settings */ #define SHARPNESS_DEFAULT 50 #define SATURATION_DEFAULT 5000 #define BRIGHTNESS_DEFAULT 6200 diff --git a/drivers/staging/cx25821/cx25821-reg.h b/drivers/staging/cx25821/cx25821-reg.h index 7241e7e..cfe0f32 100644 --- a/drivers/staging/cx25821/cx25821-reg.h +++ b/drivers/staging/cx25821/cx25821-reg.h @@ -48,24 +48,24 @@ #define RISC_SYNC_EVEN_VBI 0x00000207 #define RISC_NOOP 0xF0000000 -//***************************************************************************** -// ASB SRAM -//***************************************************************************** -#define TX_SRAM 0x000000 // Transmit SRAM - -//***************************************************************************** -#define RX_RAM 0x010000 // Receive SRAM - -//***************************************************************************** -// Application Layer (AL) -//***************************************************************************** -#define DEV_CNTRL2 0x040000 // Device control +/***************************************************************************** +* ASB SRAM + *****************************************************************************/ +#define TX_SRAM 0x000000 /* Transmit SRAM */ + +/*****************************************************************************/ +#define RX_RAM 0x010000 /* Receive SRAM */ + +/***************************************************************************** +* Application Layer (AL) + *****************************************************************************/ +#define DEV_CNTRL2 0x040000 /* Device control */ #define FLD_RUN_RISC 0x00000020 -//***************************************************************************** -#define PCI_INT_MSK 0x040010 // PCI interrupt mask -#define PCI_INT_STAT 0x040014 // PCI interrupt status -#define PCI_INT_MSTAT 0x040018 // PCI interrupt masked status +/* ***************************************************************************** */ +#define PCI_INT_MSK 0x040010 /* PCI interrupt mask */ +#define PCI_INT_STAT 0x040014 /* PCI interrupt status */ +#define PCI_INT_MSTAT 0x040018 /* PCI interrupt masked status */ #define FLD_HAMMERHEAD_INT (1 << 27) #define FLD_UART_INT (1 << 26) #define FLD_IRQN_INT (1 << 25) @@ -93,65 +93,65 @@ #define FLD_VID_B_INT (1 << 1) #define FLD_VID_A_INT (1 << 0) -//***************************************************************************** -#define VID_A_INT_MSK 0x040020 // Video A interrupt mask -#define VID_A_INT_STAT 0x040024 // Video A interrupt status -#define VID_A_INT_MSTAT 0x040028 // Video A interrupt masked status -#define VID_A_INT_SSTAT 0x04002C // Video A interrupt set status - -//***************************************************************************** -#define VID_B_INT_MSK 0x040030 // Video B interrupt mask -#define VID_B_INT_STAT 0x040034 // Video B interrupt status -#define VID_B_INT_MSTAT 0x040038 // Video B interrupt masked status -#define VID_B_INT_SSTAT 0x04003C // Video B interrupt set status - -//***************************************************************************** -#define VID_C_INT_MSK 0x040040 // Video C interrupt mask -#define VID_C_INT_STAT 0x040044 // Video C interrupt status -#define VID_C_INT_MSTAT 0x040048 // Video C interrupt masked status -#define VID_C_INT_SSTAT 0x04004C // Video C interrupt set status - -//***************************************************************************** -#define VID_D_INT_MSK 0x040050 // Video D interrupt mask -#define VID_D_INT_STAT 0x040054 // Video D interrupt status -#define VID_D_INT_MSTAT 0x040058 // Video D interrupt masked status -#define VID_D_INT_SSTAT 0x04005C // Video D interrupt set status - -//***************************************************************************** -#define VID_E_INT_MSK 0x040060 // Video E interrupt mask -#define VID_E_INT_STAT 0x040064 // Video E interrupt status -#define VID_E_INT_MSTAT 0x040068 // Video E interrupt masked status -#define VID_E_INT_SSTAT 0x04006C // Video E interrupt set status - -//***************************************************************************** -#define VID_F_INT_MSK 0x040070 // Video F interrupt mask -#define VID_F_INT_STAT 0x040074 // Video F interrupt status -#define VID_F_INT_MSTAT 0x040078 // Video F interrupt masked status -#define VID_F_INT_SSTAT 0x04007C // Video F interrupt set status - -//***************************************************************************** -#define VID_G_INT_MSK 0x040080 // Video G interrupt mask -#define VID_G_INT_STAT 0x040084 // Video G interrupt status -#define VID_G_INT_MSTAT 0x040088 // Video G interrupt masked status -#define VID_G_INT_SSTAT 0x04008C // Video G interrupt set status - -//***************************************************************************** -#define VID_H_INT_MSK 0x040090 // Video H interrupt mask -#define VID_H_INT_STAT 0x040094 // Video H interrupt status -#define VID_H_INT_MSTAT 0x040098 // Video H interrupt masked status -#define VID_H_INT_SSTAT 0x04009C // Video H interrupt set status - -//***************************************************************************** -#define VID_I_INT_MSK 0x0400A0 // Video I interrupt mask -#define VID_I_INT_STAT 0x0400A4 // Video I interrupt status -#define VID_I_INT_MSTAT 0x0400A8 // Video I interrupt masked status -#define VID_I_INT_SSTAT 0x0400AC // Video I interrupt set status - -//***************************************************************************** -#define VID_J_INT_MSK 0x0400B0 // Video J interrupt mask -#define VID_J_INT_STAT 0x0400B4 // Video J interrupt status -#define VID_J_INT_MSTAT 0x0400B8 // Video J interrupt masked status -#define VID_J_INT_SSTAT 0x0400BC // Video J interrupt set status +/* ***************************************************************************** */ +#define VID_A_INT_MSK 0x040020 /* Video A interrupt mask */ +#define VID_A_INT_STAT 0x040024 /* Video A interrupt status */ +#define VID_A_INT_MSTAT 0x040028 /* Video A interrupt masked status */ +#define VID_A_INT_SSTAT 0x04002C /* Video A interrupt set status */ + +/* ***************************************************************************** */ +#define VID_B_INT_MSK 0x040030 /* Video B interrupt mask */ +#define VID_B_INT_STAT 0x040034 /* Video B interrupt status */ +#define VID_B_INT_MSTAT 0x040038 /* Video B interrupt masked status */ +#define VID_B_INT_SSTAT 0x04003C /* Video B interrupt set status */ + +/* ***************************************************************************** */ +#define VID_C_INT_MSK 0x040040 /* Video C interrupt mask */ +#define VID_C_INT_STAT 0x040044 /* Video C interrupt status */ +#define VID_C_INT_MSTAT 0x040048 /* Video C interrupt masked status */ +#define VID_C_INT_SSTAT 0x04004C /* Video C interrupt set status */ + +/* ***************************************************************************** */ +#define VID_D_INT_MSK 0x040050 /* Video D interrupt mask */ +#define VID_D_INT_STAT 0x040054 /* Video D interrupt status */ +#define VID_D_INT_MSTAT 0x040058 /* Video D interrupt masked status */ +#define VID_D_INT_SSTAT 0x04005C /* Video D interrupt set status */ + +/* ***************************************************************************** */ +#define VID_E_INT_MSK 0x040060 /* Video E interrupt mask */ +#define VID_E_INT_STAT 0x040064 /* Video E interrupt status */ +#define VID_E_INT_MSTAT 0x040068 /* Video E interrupt masked status */ +#define VID_E_INT_SSTAT 0x04006C /* Video E interrupt set status */ + +/* ***************************************************************************** */ +#define VID_F_INT_MSK 0x040070 /* Video F interrupt mask */ +#define VID_F_INT_STAT 0x040074 /* Video F interrupt status */ +#define VID_F_INT_MSTAT 0x040078 /* Video F interrupt masked status */ +#define VID_F_INT_SSTAT 0x04007C /* Video F interrupt set status */ + +/* ***************************************************************************** */ +#define VID_G_INT_MSK 0x040080 /* Video G interrupt mask */ +#define VID_G_INT_STAT 0x040084 /* Video G interrupt status */ +#define VID_G_INT_MSTAT 0x040088 /* Video G interrupt masked status */ +#define VID_G_INT_SSTAT 0x04008C /* Video G interrupt set status */ + +/* ***************************************************************************** */ +#define VID_H_INT_MSK 0x040090 /* Video H interrupt mask */ +#define VID_H_INT_STAT 0x040094 /* Video H interrupt status */ +#define VID_H_INT_MSTAT 0x040098 /* Video H interrupt masked status */ +#define VID_H_INT_SSTAT 0x04009C /* Video H interrupt set status */ + +/* ***************************************************************************** */ +#define VID_I_INT_MSK 0x0400A0 /* Video I interrupt mask */ +#define VID_I_INT_STAT 0x0400A4 /* Video I interrupt status */ +#define VID_I_INT_MSTAT 0x0400A8 /* Video I interrupt masked status */ +#define VID_I_INT_SSTAT 0x0400AC /* Video I interrupt set status */ + +/* ***************************************************************************** */ +#define VID_J_INT_MSK 0x0400B0 /* Video J interrupt mask */ +#define VID_J_INT_STAT 0x0400B4 /* Video J interrupt status */ +#define VID_J_INT_MSTAT 0x0400B8 /* Video J interrupt masked status */ +#define VID_J_INT_SSTAT 0x0400BC /* Video J interrupt set status */ #define FLD_VID_SRC_OPC_ERR 0x00020000 #define FLD_VID_DST_OPC_ERR 0x00010000 @@ -166,35 +166,35 @@ #define FLD_VID_SRC_ERRORS FLD_VID_SRC_OPC_ERR | FLD_VID_SRC_SYNC | FLD_VID_SRC_UF #define FLD_VID_DST_ERRORS FLD_VID_DST_OPC_ERR | FLD_VID_DST_SYNC | FLD_VID_DST_OF -//***************************************************************************** -#define AUD_A_INT_MSK 0x0400C0 // Audio Int interrupt mask -#define AUD_A_INT_STAT 0x0400C4 // Audio Int interrupt status -#define AUD_A_INT_MSTAT 0x0400C8 // Audio Int interrupt masked status -#define AUD_A_INT_SSTAT 0x0400CC // Audio Int interrupt set status - -//***************************************************************************** -#define AUD_B_INT_MSK 0x0400D0 // Audio Int interrupt mask -#define AUD_B_INT_STAT 0x0400D4 // Audio Int interrupt status -#define AUD_B_INT_MSTAT 0x0400D8 // Audio Int interrupt masked status -#define AUD_B_INT_SSTAT 0x0400DC // Audio Int interrupt set status - -//***************************************************************************** -#define AUD_C_INT_MSK 0x0400E0 // Audio Int interrupt mask -#define AUD_C_INT_STAT 0x0400E4 // Audio Int interrupt status -#define AUD_C_INT_MSTAT 0x0400E8 // Audio Int interrupt masked status -#define AUD_C_INT_SSTAT 0x0400EC // Audio Int interrupt set status - -//***************************************************************************** -#define AUD_D_INT_MSK 0x0400F0 // Audio Int interrupt mask -#define AUD_D_INT_STAT 0x0400F4 // Audio Int interrupt status -#define AUD_D_INT_MSTAT 0x0400F8 // Audio Int interrupt masked status -#define AUD_D_INT_SSTAT 0x0400FC // Audio Int interrupt set status - -//***************************************************************************** -#define AUD_E_INT_MSK 0x040100 // Audio Int interrupt mask -#define AUD_E_INT_STAT 0x040104 // Audio Int interrupt status -#define AUD_E_INT_MSTAT 0x040108 // Audio Int interrupt masked status -#define AUD_E_INT_SSTAT 0x04010C // Audio Int interrupt set status +/* ***************************************************************************** */ +#define AUD_A_INT_MSK 0x0400C0 /* Audio Int interrupt mask */ +#define AUD_A_INT_STAT 0x0400C4 /* Audio Int interrupt status */ +#define AUD_A_INT_MSTAT 0x0400C8 /* Audio Int interrupt masked status */ +#define AUD_A_INT_SSTAT 0x0400CC /* Audio Int interrupt set status */ + +/* ***************************************************************************** */ +#define AUD_B_INT_MSK 0x0400D0 /* Audio Int interrupt mask */ +#define AUD_B_INT_STAT 0x0400D4 /* Audio Int interrupt status */ +#define AUD_B_INT_MSTAT 0x0400D8 /* Audio Int interrupt masked status */ +#define AUD_B_INT_SSTAT 0x0400DC /* Audio Int interrupt set status */ + +/* ***************************************************************************** */ +#define AUD_C_INT_MSK 0x0400E0 /* Audio Int interrupt mask */ +#define AUD_C_INT_STAT 0x0400E4 /* Audio Int interrupt status */ +#define AUD_C_INT_MSTAT 0x0400E8 /* Audio Int interrupt masked status */ +#define AUD_C_INT_SSTAT 0x0400EC /* Audio Int interrupt set status */ + +/* ***************************************************************************** */ +#define AUD_D_INT_MSK 0x0400F0 /* Audio Int interrupt mask */ +#define AUD_D_INT_STAT 0x0400F4 /* Audio Int interrupt status */ +#define AUD_D_INT_MSTAT 0x0400F8 /* Audio Int interrupt masked status */ +#define AUD_D_INT_SSTAT 0x0400FC /* Audio Int interrupt set status */ + +/* ***************************************************************************** */ +#define AUD_E_INT_MSK 0x040100 /* Audio Int interrupt mask */ +#define AUD_E_INT_STAT 0x040104 /* Audio Int interrupt status */ +#define AUD_E_INT_MSTAT 0x040108 /* Audio Int interrupt masked status */ +#define AUD_E_INT_SSTAT 0x04010C /* Audio Int interrupt set status */ #define FLD_AUD_SRC_OPC_ERR 0x00020000 #define FLD_AUD_DST_OPC_ERR 0x00010000 @@ -207,17 +207,17 @@ #define FLD_AUD_SRC_RISCI1 0x00000002 #define FLD_AUD_DST_RISCI1 0x00000001 -//***************************************************************************** -#define MBIF_A_INT_MSK 0x040110 // MBIF Int interrupt mask -#define MBIF_A_INT_STAT 0x040114 // MBIF Int interrupt status -#define MBIF_A_INT_MSTAT 0x040118 // MBIF Int interrupt masked status -#define MBIF_A_INT_SSTAT 0x04011C // MBIF Int interrupt set status +/* ***************************************************************************** */ +#define MBIF_A_INT_MSK 0x040110 /* MBIF Int interrupt mask */ +#define MBIF_A_INT_STAT 0x040114 /* MBIF Int interrupt status */ +#define MBIF_A_INT_MSTAT 0x040118 /* MBIF Int interrupt masked status */ +#define MBIF_A_INT_SSTAT 0x04011C /* MBIF Int interrupt set status */ -//***************************************************************************** -#define MBIF_B_INT_MSK 0x040120 // MBIF Int interrupt mask -#define MBIF_B_INT_STAT 0x040124 // MBIF Int interrupt status -#define MBIF_B_INT_MSTAT 0x040128 // MBIF Int interrupt masked status -#define MBIF_B_INT_SSTAT 0x04012C // MBIF Int interrupt set status +/* ***************************************************************************** */ +#define MBIF_B_INT_MSK 0x040120 /* MBIF Int interrupt mask */ +#define MBIF_B_INT_STAT 0x040124 /* MBIF Int interrupt status */ +#define MBIF_B_INT_MSTAT 0x040128 /* MBIF Int interrupt masked status */ +#define MBIF_B_INT_SSTAT 0x04012C /* MBIF Int interrupt set status */ #define FLD_MBIF_DST_OPC_ERR 0x00010000 #define FLD_MBIF_DST_SYNC 0x00001000 @@ -225,35 +225,35 @@ #define FLD_MBIF_DST_RISCI2 0x00000010 #define FLD_MBIF_DST_RISCI1 0x00000001 -//***************************************************************************** -#define AUD_EXT_INT_MSK 0x040060 // Audio Ext interrupt mask -#define AUD_EXT_INT_STAT 0x040064 // Audio Ext interrupt status -#define AUD_EXT_INT_MSTAT 0x040068 // Audio Ext interrupt masked status -#define AUD_EXT_INT_SSTAT 0x04006C // Audio Ext interrupt set status +/* ***************************************************************************** */ +#define AUD_EXT_INT_MSK 0x040060 /* Audio Ext interrupt mask */ +#define AUD_EXT_INT_STAT 0x040064 /* Audio Ext interrupt status */ +#define AUD_EXT_INT_MSTAT 0x040068 /* Audio Ext interrupt masked status */ +#define AUD_EXT_INT_SSTAT 0x04006C /* Audio Ext interrupt set status */ #define FLD_AUD_EXT_OPC_ERR 0x00010000 #define FLD_AUD_EXT_SYNC 0x00001000 #define FLD_AUD_EXT_OF 0x00000100 #define FLD_AUD_EXT_RISCI2 0x00000010 #define FLD_AUD_EXT_RISCI1 0x00000001 -//***************************************************************************** -#define GPIO_LO 0x110010 // Lower of GPIO pins [31:0] -#define GPIO_HI 0x110014 // Upper WORD of GPIO pins [47:31] +/* ***************************************************************************** */ +#define GPIO_LO 0x110010 /* Lower of GPIO pins [31:0] */ +#define GPIO_HI 0x110014 /* Upper WORD of GPIO pins [47:31] */ -#define GPIO_LO_OE 0x110018 // Lower of GPIO output enable [31:0] -#define GPIO_HI_OE 0x11001C // Upper word of GPIO output enable [47:32] +#define GPIO_LO_OE 0x110018 /* Lower of GPIO output enable [31:0] */ +#define GPIO_HI_OE 0x11001C /* Upper word of GPIO output enable [47:32] */ -#define GPIO_LO_INT_MSK 0x11003C // GPIO interrupt mask -#define GPIO_LO_INT_STAT 0x110044 // GPIO interrupt status -#define GPIO_LO_INT_MSTAT 0x11004C // GPIO interrupt masked status -#define GPIO_LO_ISM_SNS 0x110054 // GPIO interrupt sensitivity -#define GPIO_LO_ISM_POL 0x11005C // GPIO interrupt polarity +#define GPIO_LO_INT_MSK 0x11003C /* GPIO interrupt mask */ +#define GPIO_LO_INT_STAT 0x110044 /* GPIO interrupt status */ +#define GPIO_LO_INT_MSTAT 0x11004C /* GPIO interrupt masked status */ +#define GPIO_LO_ISM_SNS 0x110054 /* GPIO interrupt sensitivity */ +#define GPIO_LO_ISM_POL 0x11005C /* GPIO interrupt polarity */ -#define GPIO_HI_INT_MSK 0x110040 // GPIO interrupt mask -#define GPIO_HI_INT_STAT 0x110048 // GPIO interrupt status -#define GPIO_HI_INT_MSTAT 0x110050 // GPIO interrupt masked status -#define GPIO_HI_ISM_SNS 0x110058 // GPIO interrupt sensitivity -#define GPIO_HI_ISM_POL 0x110060 // GPIO interrupt polarity +#define GPIO_HI_INT_MSK 0x110040 /* GPIO interrupt mask */ +#define GPIO_HI_INT_STAT 0x110048 /* GPIO interrupt status */ +#define GPIO_HI_INT_MSTAT 0x110050 /* GPIO interrupt masked status */ +#define GPIO_HI_ISM_SNS 0x110058 /* GPIO interrupt sensitivity */ +#define GPIO_HI_ISM_POL 0x110060 /* GPIO interrupt polarity */ #define FLD_GPIO43_INT (1 << 11) #define FLD_GPIO42_INT (1 << 10) @@ -271,236 +271,236 @@ #define FLD_GPIO1_INT (1 << 1) #define FLD_GPIO0_INT (1 << 0) -//***************************************************************************** -#define TC_REQ 0x040090 // Rider PCI Express traFFic class request +/* ***************************************************************************** */ +#define TC_REQ 0x040090 /* Rider PCI Express traFFic class request */ -//***************************************************************************** -#define TC_REQ_SET 0x040094 // Rider PCI Express traFFic class request set +/* ***************************************************************************** */ +#define TC_REQ_SET 0x040094 /* Rider PCI Express traFFic class request set */ -//***************************************************************************** -// Rider -//***************************************************************************** +/* ***************************************************************************** */ +/* Rider */ +/* ***************************************************************************** */ -// PCI Compatible Header -//***************************************************************************** +/* PCI Compatible Header */ +/* ***************************************************************************** */ #define RDR_CFG0 0x050000 #define RDR_VENDOR_DEVICE_ID_CFG 0x050000 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_CFG1 0x050004 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_CFG2 0x050008 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_CFG3 0x05000C -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_CFG4 0x050010 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_CFG5 0x050014 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_CFG6 0x050018 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_CFG7 0x05001C -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_CFG8 0x050020 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_CFG9 0x050024 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_CFGA 0x050028 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_CFGB 0x05002C #define RDR_SUSSYSTEM_ID_CFG 0x05002C -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_CFGC 0x050030 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_CFGD 0x050034 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_CFGE 0x050038 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_CFGF 0x05003C -//***************************************************************************** -// PCI-Express Capabilities -//***************************************************************************** +/* ***************************************************************************** */ +/* PCI-Express Capabilities */ +/* ***************************************************************************** */ #define RDR_PECAP 0x050040 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_PEDEVCAP 0x050044 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_PEDEVSC 0x050048 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_PELINKCAP 0x05004C -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_PELINKSC 0x050050 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_PMICAP 0x050080 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_PMCSR 0x050084 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VPDCAP 0x050090 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VPDDATA 0x050094 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_MSICAP 0x0500A0 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_MSIARL 0x0500A4 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_MSIARU 0x0500A8 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_MSIDATA 0x0500AC -//***************************************************************************** -// PCI Express Extended Capabilities -//***************************************************************************** +/* ***************************************************************************** */ +/* PCI Express Extended Capabilities */ +/* ***************************************************************************** */ #define RDR_AERXCAP 0x050100 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_AERUESTA 0x050104 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_AERUEMSK 0x050108 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_AERUESEV 0x05010C -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_AERCESTA 0x050110 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_AERCEMSK 0x050114 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_AERCC 0x050118 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_AERHL0 0x05011C -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_AERHL1 0x050120 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_AERHL2 0x050124 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_AERHL3 0x050128 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCXCAP 0x050200 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCCAP1 0x050204 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCCAP2 0x050208 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCSC 0x05020C -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCR0_CAP 0x050210 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCR0_CTRL 0x050214 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCR0_STAT 0x050218 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCR1_CAP 0x05021C -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCR1_CTRL 0x050220 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCR1_STAT 0x050224 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCR2_CAP 0x050228 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCR2_CTRL 0x05022C -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCR2_STAT 0x050230 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCR3_CAP 0x050234 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCR3_CTRL 0x050238 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCR3_STAT 0x05023C -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCARB0 0x050240 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCARB1 0x050244 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCARB2 0x050248 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCARB3 0x05024C -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCARB4 0x050250 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCARB5 0x050254 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCARB6 0x050258 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCARB7 0x05025C -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_RDRSTAT0 0x050300 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_RDRSTAT1 0x050304 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_RDRCTL0 0x050308 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_RDRCTL1 0x05030C -//***************************************************************************** -// Transaction Layer Registers -//***************************************************************************** +/* ***************************************************************************** */ +/* Transaction Layer Registers */ +/* ***************************************************************************** */ #define RDR_TLSTAT0 0x050310 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_TLSTAT1 0x050314 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_TLCTL0 0x050318 #define FLD_CFG_UR_CPL_MODE 0x00000040 #define FLD_CFG_CORR_ERR_QUITE 0x00000020 @@ -510,569 +510,569 @@ #define FLD_CFG_RELAX_ORDER_MSK 0x00000002 #define FLD_CFG_TAG_ORDER_EN 0x00000001 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_TLCTL1 0x05031C -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_REQRCAL 0x050320 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_REQRCAU 0x050324 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_REQEPA 0x050328 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_REQCTRL 0x05032C -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_REQSTAT 0x050330 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_TL_TEST 0x050334 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCR01_CTL 0x050348 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_VCR23_CTL 0x05034C -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_RX_VCR0_FC 0x050350 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_RX_VCR1_FC 0x050354 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_RX_VCR2_FC 0x050358 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_RX_VCR3_FC 0x05035C -//***************************************************************************** -// Data Link Layer Registers -//***************************************************************************** +/* ***************************************************************************** */ +/* Data Link Layer Registers */ +/* ***************************************************************************** */ #define RDR_DLLSTAT 0x050360 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_DLLCTRL 0x050364 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_REPLAYTO 0x050368 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_ACKLATTO 0x05036C -//***************************************************************************** -// MAC Layer Registers -//***************************************************************************** +/* ***************************************************************************** */ +/* MAC Layer Registers */ +/* ***************************************************************************** */ #define RDR_MACSTAT0 0x050380 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_MACSTAT1 0x050384 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_MACCTRL0 0x050388 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_MACCTRL1 0x05038C -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_MACCTRL2 0x050390 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_MAC_LB_DATA 0x050394 -//***************************************************************************** +/* ***************************************************************************** */ #define RDR_L0S_EXIT_LAT 0x050398 -//***************************************************************************** -// DMAC -//***************************************************************************** -#define DMA1_PTR1 0x100000 // DMA Current Ptr : Ch#1 +/* ***************************************************************************** */ +/* DMAC */ +/* ***************************************************************************** */ +#define DMA1_PTR1 0x100000 /* DMA Current Ptr : Ch#1 */ -//***************************************************************************** -#define DMA2_PTR1 0x100004 // DMA Current Ptr : Ch#2 +/* ***************************************************************************** */ +#define DMA2_PTR1 0x100004 /* DMA Current Ptr : Ch#2 */ -//***************************************************************************** -#define DMA3_PTR1 0x100008 // DMA Current Ptr : Ch#3 +/* ***************************************************************************** */ +#define DMA3_PTR1 0x100008 /* DMA Current Ptr : Ch#3 */ -//***************************************************************************** -#define DMA4_PTR1 0x10000C // DMA Current Ptr : Ch#4 +/* ***************************************************************************** */ +#define DMA4_PTR1 0x10000C /* DMA Current Ptr : Ch#4 */ -//***************************************************************************** -#define DMA5_PTR1 0x100010 // DMA Current Ptr : Ch#5 +/* ***************************************************************************** */ +#define DMA5_PTR1 0x100010 /* DMA Current Ptr : Ch#5 */ -//***************************************************************************** -#define DMA6_PTR1 0x100014 // DMA Current Ptr : Ch#6 +/* ***************************************************************************** */ +#define DMA6_PTR1 0x100014 /* DMA Current Ptr : Ch#6 */ -//***************************************************************************** -#define DMA7_PTR1 0x100018 // DMA Current Ptr : Ch#7 +/* ***************************************************************************** */ +#define DMA7_PTR1 0x100018 /* DMA Current Ptr : Ch#7 */ -//***************************************************************************** -#define DMA8_PTR1 0x10001C // DMA Current Ptr : Ch#8 +/* ***************************************************************************** */ +#define DMA8_PTR1 0x10001C /* DMA Current Ptr : Ch#8 */ -//***************************************************************************** -#define DMA9_PTR1 0x100020 // DMA Current Ptr : Ch#9 +/* ***************************************************************************** */ +#define DMA9_PTR1 0x100020 /* DMA Current Ptr : Ch#9 */ -//***************************************************************************** -#define DMA10_PTR1 0x100024 // DMA Current Ptr : Ch#10 +/* ***************************************************************************** */ +#define DMA10_PTR1 0x100024 /* DMA Current Ptr : Ch#10 */ -//***************************************************************************** -#define DMA11_PTR1 0x100028 // DMA Current Ptr : Ch#11 +/* ***************************************************************************** */ +#define DMA11_PTR1 0x100028 /* DMA Current Ptr : Ch#11 */ -//***************************************************************************** -#define DMA12_PTR1 0x10002C // DMA Current Ptr : Ch#12 +/* ***************************************************************************** */ +#define DMA12_PTR1 0x10002C /* DMA Current Ptr : Ch#12 */ -//***************************************************************************** -#define DMA13_PTR1 0x100030 // DMA Current Ptr : Ch#13 +/* ***************************************************************************** */ +#define DMA13_PTR1 0x100030 /* DMA Current Ptr : Ch#13 */ -//***************************************************************************** -#define DMA14_PTR1 0x100034 // DMA Current Ptr : Ch#14 +/* ***************************************************************************** */ +#define DMA14_PTR1 0x100034 /* DMA Current Ptr : Ch#14 */ -//***************************************************************************** -#define DMA15_PTR1 0x100038 // DMA Current Ptr : Ch#15 +/* ***************************************************************************** */ +#define DMA15_PTR1 0x100038 /* DMA Current Ptr : Ch#15 */ -//***************************************************************************** -#define DMA16_PTR1 0x10003C // DMA Current Ptr : Ch#16 +/* ***************************************************************************** */ +#define DMA16_PTR1 0x10003C /* DMA Current Ptr : Ch#16 */ -//***************************************************************************** -#define DMA17_PTR1 0x100040 // DMA Current Ptr : Ch#17 +/* ***************************************************************************** */ +#define DMA17_PTR1 0x100040 /* DMA Current Ptr : Ch#17 */ -//***************************************************************************** -#define DMA18_PTR1 0x100044 // DMA Current Ptr : Ch#18 +/* ***************************************************************************** */ +#define DMA18_PTR1 0x100044 /* DMA Current Ptr : Ch#18 */ -//***************************************************************************** -#define DMA19_PTR1 0x100048 // DMA Current Ptr : Ch#19 +/* ***************************************************************************** */ +#define DMA19_PTR1 0x100048 /* DMA Current Ptr : Ch#19 */ -//***************************************************************************** -#define DMA20_PTR1 0x10004C // DMA Current Ptr : Ch#20 +/* ***************************************************************************** */ +#define DMA20_PTR1 0x10004C /* DMA Current Ptr : Ch#20 */ -//***************************************************************************** -#define DMA21_PTR1 0x100050 // DMA Current Ptr : Ch#21 +/* ***************************************************************************** */ +#define DMA21_PTR1 0x100050 /* DMA Current Ptr : Ch#21 */ -//***************************************************************************** -#define DMA22_PTR1 0x100054 // DMA Current Ptr : Ch#22 +/* ***************************************************************************** */ +#define DMA22_PTR1 0x100054 /* DMA Current Ptr : Ch#22 */ -//***************************************************************************** -#define DMA23_PTR1 0x100058 // DMA Current Ptr : Ch#23 +/* ***************************************************************************** */ +#define DMA23_PTR1 0x100058 /* DMA Current Ptr : Ch#23 */ -//***************************************************************************** -#define DMA24_PTR1 0x10005C // DMA Current Ptr : Ch#24 +/* ***************************************************************************** */ +#define DMA24_PTR1 0x10005C /* DMA Current Ptr : Ch#24 */ -//***************************************************************************** -#define DMA25_PTR1 0x100060 // DMA Current Ptr : Ch#25 +/* ***************************************************************************** */ +#define DMA25_PTR1 0x100060 /* DMA Current Ptr : Ch#25 */ -//***************************************************************************** -#define DMA26_PTR1 0x100064 // DMA Current Ptr : Ch#26 +/* ***************************************************************************** */ +#define DMA26_PTR1 0x100064 /* DMA Current Ptr : Ch#26 */ -//***************************************************************************** -#define DMA1_PTR2 0x100080 // DMA Tab Ptr : Ch#1 +/* ***************************************************************************** */ +#define DMA1_PTR2 0x100080 /* DMA Tab Ptr : Ch#1 */ -//***************************************************************************** -#define DMA2_PTR2 0x100084 // DMA Tab Ptr : Ch#2 +/* ***************************************************************************** */ +#define DMA2_PTR2 0x100084 /* DMA Tab Ptr : Ch#2 */ -//***************************************************************************** -#define DMA3_PTR2 0x100088 // DMA Tab Ptr : Ch#3 +/* ***************************************************************************** */ +#define DMA3_PTR2 0x100088 /* DMA Tab Ptr : Ch#3 */ -//***************************************************************************** -#define DMA4_PTR2 0x10008C // DMA Tab Ptr : Ch#4 +/* ***************************************************************************** */ +#define DMA4_PTR2 0x10008C /* DMA Tab Ptr : Ch#4 */ -//***************************************************************************** -#define DMA5_PTR2 0x100090 // DMA Tab Ptr : Ch#5 +/* ***************************************************************************** */ +#define DMA5_PTR2 0x100090 /* DMA Tab Ptr : Ch#5 */ -//***************************************************************************** -#define DMA6_PTR2 0x100094 // DMA Tab Ptr : Ch#6 +/* ***************************************************************************** */ +#define DMA6_PTR2 0x100094 /* DMA Tab Ptr : Ch#6 */ -//***************************************************************************** -#define DMA7_PTR2 0x100098 // DMA Tab Ptr : Ch#7 +/* ***************************************************************************** */ +#define DMA7_PTR2 0x100098 /* DMA Tab Ptr : Ch#7 */ -//***************************************************************************** -#define DMA8_PTR2 0x10009C // DMA Tab Ptr : Ch#8 +/* ***************************************************************************** */ +#define DMA8_PTR2 0x10009C /* DMA Tab Ptr : Ch#8 */ -//***************************************************************************** -#define DMA9_PTR2 0x1000A0 // DMA Tab Ptr : Ch#9 +/* ***************************************************************************** */ +#define DMA9_PTR2 0x1000A0 /* DMA Tab Ptr : Ch#9 */ -//***************************************************************************** -#define DMA10_PTR2 0x1000A4 // DMA Tab Ptr : Ch#10 +/* ***************************************************************************** */ +#define DMA10_PTR2 0x1000A4 /* DMA Tab Ptr : Ch#10 */ -//***************************************************************************** -#define DMA11_PTR2 0x1000A8 // DMA Tab Ptr : Ch#11 +/* ***************************************************************************** */ +#define DMA11_PTR2 0x1000A8 /* DMA Tab Ptr : Ch#11 */ -//***************************************************************************** -#define DMA12_PTR2 0x1000AC // DMA Tab Ptr : Ch#12 +/* ***************************************************************************** */ +#define DMA12_PTR2 0x1000AC /* DMA Tab Ptr : Ch#12 */ -//***************************************************************************** -#define DMA13_PTR2 0x1000B0 // DMA Tab Ptr : Ch#13 +/* ***************************************************************************** */ +#define DMA13_PTR2 0x1000B0 /* DMA Tab Ptr : Ch#13 */ -//***************************************************************************** -#define DMA14_PTR2 0x1000B4 // DMA Tab Ptr : Ch#14 +/* ***************************************************************************** */ +#define DMA14_PTR2 0x1000B4 /* DMA Tab Ptr : Ch#14 */ -//***************************************************************************** -#define DMA15_PTR2 0x1000B8 // DMA Tab Ptr : Ch#15 +/* ***************************************************************************** */ +#define DMA15_PTR2 0x1000B8 /* DMA Tab Ptr : Ch#15 */ -//***************************************************************************** -#define DMA16_PTR2 0x1000BC // DMA Tab Ptr : Ch#16 +/* ***************************************************************************** */ +#define DMA16_PTR2 0x1000BC /* DMA Tab Ptr : Ch#16 */ -//***************************************************************************** -#define DMA17_PTR2 0x1000C0 // DMA Tab Ptr : Ch#17 +/* ***************************************************************************** */ +#define DMA17_PTR2 0x1000C0 /* DMA Tab Ptr : Ch#17 */ -//***************************************************************************** -#define DMA18_PTR2 0x1000C4 // DMA Tab Ptr : Ch#18 +/* ***************************************************************************** */ +#define DMA18_PTR2 0x1000C4 /* DMA Tab Ptr : Ch#18 */ -//***************************************************************************** -#define DMA19_PTR2 0x1000C8 // DMA Tab Ptr : Ch#19 +/* ***************************************************************************** */ +#define DMA19_PTR2 0x1000C8 /* DMA Tab Ptr : Ch#19 */ -//***************************************************************************** -#define DMA20_PTR2 0x1000CC // DMA Tab Ptr : Ch#20 +/* ***************************************************************************** */ +#define DMA20_PTR2 0x1000CC /* DMA Tab Ptr : Ch#20 */ -//***************************************************************************** -#define DMA21_PTR2 0x1000D0 // DMA Tab Ptr : Ch#21 +/* ***************************************************************************** */ +#define DMA21_PTR2 0x1000D0 /* DMA Tab Ptr : Ch#21 */ -//***************************************************************************** -#define DMA22_PTR2 0x1000D4 // DMA Tab Ptr : Ch#22 +/* ***************************************************************************** */ +#define DMA22_PTR2 0x1000D4 /* DMA Tab Ptr : Ch#22 */ -//***************************************************************************** -#define DMA23_PTR2 0x1000D8 // DMA Tab Ptr : Ch#23 +/* ***************************************************************************** */ +#define DMA23_PTR2 0x1000D8 /* DMA Tab Ptr : Ch#23 */ -//***************************************************************************** -#define DMA24_PTR2 0x1000DC // DMA Tab Ptr : Ch#24 +/* ***************************************************************************** */ +#define DMA24_PTR2 0x1000DC /* DMA Tab Ptr : Ch#24 */ -//***************************************************************************** -#define DMA25_PTR2 0x1000E0 // DMA Tab Ptr : Ch#25 +/* ***************************************************************************** */ +#define DMA25_PTR2 0x1000E0 /* DMA Tab Ptr : Ch#25 */ -//***************************************************************************** -#define DMA26_PTR2 0x1000E4 // DMA Tab Ptr : Ch#26 +/* ***************************************************************************** */ +#define DMA26_PTR2 0x1000E4 /* DMA Tab Ptr : Ch#26 */ -//***************************************************************************** -#define DMA1_CNT1 0x100100 // DMA BuFFer Size : Ch#1 +/* ***************************************************************************** */ +#define DMA1_CNT1 0x100100 /* DMA BuFFer Size : Ch#1 */ -//***************************************************************************** -#define DMA2_CNT1 0x100104 // DMA BuFFer Size : Ch#2 +/* ***************************************************************************** */ +#define DMA2_CNT1 0x100104 /* DMA BuFFer Size : Ch#2 */ -//***************************************************************************** -#define DMA3_CNT1 0x100108 // DMA BuFFer Size : Ch#3 +/* ***************************************************************************** */ +#define DMA3_CNT1 0x100108 /* DMA BuFFer Size : Ch#3 */ -//***************************************************************************** -#define DMA4_CNT1 0x10010C // DMA BuFFer Size : Ch#4 +/* ***************************************************************************** */ +#define DMA4_CNT1 0x10010C /* DMA BuFFer Size : Ch#4 */ -//***************************************************************************** -#define DMA5_CNT1 0x100110 // DMA BuFFer Size : Ch#5 +/* ***************************************************************************** */ +#define DMA5_CNT1 0x100110 /* DMA BuFFer Size : Ch#5 */ -//***************************************************************************** -#define DMA6_CNT1 0x100114 // DMA BuFFer Size : Ch#6 +/* ***************************************************************************** */ +#define DMA6_CNT1 0x100114 /* DMA BuFFer Size : Ch#6 */ -//***************************************************************************** -#define DMA7_CNT1 0x100118 // DMA BuFFer Size : Ch#7 +/* ***************************************************************************** */ +#define DMA7_CNT1 0x100118 /* DMA BuFFer Size : Ch#7 */ -//***************************************************************************** -#define DMA8_CNT1 0x10011C // DMA BuFFer Size : Ch#8 +/* ***************************************************************************** */ +#define DMA8_CNT1 0x10011C /* DMA BuFFer Size : Ch#8 */ -//***************************************************************************** -#define DMA9_CNT1 0x100120 // DMA BuFFer Size : Ch#9 +/* ***************************************************************************** */ +#define DMA9_CNT1 0x100120 /* DMA BuFFer Size : Ch#9 */ -//***************************************************************************** -#define DMA10_CNT1 0x100124 // DMA BuFFer Size : Ch#10 +/* ***************************************************************************** */ +#define DMA10_CNT1 0x100124 /* DMA BuFFer Size : Ch#10 */ -//***************************************************************************** -#define DMA11_CNT1 0x100128 // DMA BuFFer Size : Ch#11 +/* ***************************************************************************** */ +#define DMA11_CNT1 0x100128 /* DMA BuFFer Size : Ch#11 */ -//***************************************************************************** -#define DMA12_CNT1 0x10012C // DMA BuFFer Size : Ch#12 +/* ***************************************************************************** */ +#define DMA12_CNT1 0x10012C /* DMA BuFFer Size : Ch#12 */ -//***************************************************************************** -#define DMA13_CNT1 0x100130 // DMA BuFFer Size : Ch#13 +/* ***************************************************************************** */ +#define DMA13_CNT1 0x100130 /* DMA BuFFer Size : Ch#13 */ -//***************************************************************************** -#define DMA14_CNT1 0x100134 // DMA BuFFer Size : Ch#14 +/* ***************************************************************************** */ +#define DMA14_CNT1 0x100134 /* DMA BuFFer Size : Ch#14 */ -//***************************************************************************** -#define DMA15_CNT1 0x100138 // DMA BuFFer Size : Ch#15 +/* ***************************************************************************** */ +#define DMA15_CNT1 0x100138 /* DMA BuFFer Size : Ch#15 */ -//***************************************************************************** -#define DMA16_CNT1 0x10013C // DMA BuFFer Size : Ch#16 +/* ***************************************************************************** */ +#define DMA16_CNT1 0x10013C /* DMA BuFFer Size : Ch#16 */ -//***************************************************************************** -#define DMA17_CNT1 0x100140 // DMA BuFFer Size : Ch#17 +/* ***************************************************************************** */ +#define DMA17_CNT1 0x100140 /* DMA BuFFer Size : Ch#17 */ -//***************************************************************************** -#define DMA18_CNT1 0x100144 // DMA BuFFer Size : Ch#18 +/* ***************************************************************************** */ +#define DMA18_CNT1 0x100144 /* DMA BuFFer Size : Ch#18 */ -//***************************************************************************** -#define DMA19_CNT1 0x100148 // DMA BuFFer Size : Ch#19 +/* ***************************************************************************** */ +#define DMA19_CNT1 0x100148 /* DMA BuFFer Size : Ch#19 */ -//***************************************************************************** -#define DMA20_CNT1 0x10014C // DMA BuFFer Size : Ch#20 +/* ***************************************************************************** */ +#define DMA20_CNT1 0x10014C /* DMA BuFFer Size : Ch#20 */ -//***************************************************************************** -#define DMA21_CNT1 0x100150 // DMA BuFFer Size : Ch#21 +/* ***************************************************************************** */ +#define DMA21_CNT1 0x100150 /* DMA BuFFer Size : Ch#21 */ -//***************************************************************************** -#define DMA22_CNT1 0x100154 // DMA BuFFer Size : Ch#22 +/* ***************************************************************************** */ +#define DMA22_CNT1 0x100154 /* DMA BuFFer Size : Ch#22 */ -//***************************************************************************** -#define DMA23_CNT1 0x100158 // DMA BuFFer Size : Ch#23 +/* ***************************************************************************** */ +#define DMA23_CNT1 0x100158 /* DMA BuFFer Size : Ch#23 */ -//***************************************************************************** -#define DMA24_CNT1 0x10015C // DMA BuFFer Size : Ch#24 +/* ***************************************************************************** */ +#define DMA24_CNT1 0x10015C /* DMA BuFFer Size : Ch#24 */ -//***************************************************************************** -#define DMA25_CNT1 0x100160 // DMA BuFFer Size : Ch#25 +/* ***************************************************************************** */ +#define DMA25_CNT1 0x100160 /* DMA BuFFer Size : Ch#25 */ -//***************************************************************************** -#define DMA26_CNT1 0x100164 // DMA BuFFer Size : Ch#26 +/* ***************************************************************************** */ +#define DMA26_CNT1 0x100164 /* DMA BuFFer Size : Ch#26 */ -//***************************************************************************** -#define DMA1_CNT2 0x100180 // DMA Table Size : Ch#1 +/* ***************************************************************************** */ +#define DMA1_CNT2 0x100180 /* DMA Table Size : Ch#1 */ -//***************************************************************************** -#define DMA2_CNT2 0x100184 // DMA Table Size : Ch#2 +/* ***************************************************************************** */ +#define DMA2_CNT2 0x100184 /* DMA Table Size : Ch#2 */ -//***************************************************************************** -#define DMA3_CNT2 0x100188 // DMA Table Size : Ch#3 +/* ***************************************************************************** */ +#define DMA3_CNT2 0x100188 /* DMA Table Size : Ch#3 */ -//***************************************************************************** -#define DMA4_CNT2 0x10018C // DMA Table Size : Ch#4 +/* ***************************************************************************** */ +#define DMA4_CNT2 0x10018C /* DMA Table Size : Ch#4 */ -//***************************************************************************** -#define DMA5_CNT2 0x100190 // DMA Table Size : Ch#5 +/* ***************************************************************************** */ +#define DMA5_CNT2 0x100190 /* DMA Table Size : Ch#5 */ -//***************************************************************************** -#define DMA6_CNT2 0x100194 // DMA Table Size : Ch#6 +/* ***************************************************************************** */ +#define DMA6_CNT2 0x100194 /* DMA Table Size : Ch#6 */ -//***************************************************************************** -#define DMA7_CNT2 0x100198 // DMA Table Size : Ch#7 +/* ***************************************************************************** */ +#define DMA7_CNT2 0x100198 /* DMA Table Size : Ch#7 */ -//***************************************************************************** -#define DMA8_CNT2 0x10019C // DMA Table Size : Ch#8 +/* ***************************************************************************** */ +#define DMA8_CNT2 0x10019C /* DMA Table Size : Ch#8 */ -//***************************************************************************** -#define DMA9_CNT2 0x1001A0 // DMA Table Size : Ch#9 +/* ***************************************************************************** */ +#define DMA9_CNT2 0x1001A0 /* DMA Table Size : Ch#9 */ -//***************************************************************************** -#define DMA10_CNT2 0x1001A4 // DMA Table Size : Ch#10 +/* ***************************************************************************** */ +#define DMA10_CNT2 0x1001A4 /* DMA Table Size : Ch#10 */ -//***************************************************************************** -#define DMA11_CNT2 0x1001A8 // DMA Table Size : Ch#11 +/* ***************************************************************************** */ +#define DMA11_CNT2 0x1001A8 /* DMA Table Size : Ch#11 */ -//***************************************************************************** -#define DMA12_CNT2 0x1001AC // DMA Table Size : Ch#12 +/* ***************************************************************************** */ +#define DMA12_CNT2 0x1001AC /* DMA Table Size : Ch#12 */ -//***************************************************************************** -#define DMA13_CNT2 0x1001B0 // DMA Table Size : Ch#13 +/* ***************************************************************************** */ +#define DMA13_CNT2 0x1001B0 /* DMA Table Size : Ch#13 */ -//***************************************************************************** -#define DMA14_CNT2 0x1001B4 // DMA Table Size : Ch#14 +/* ***************************************************************************** */ +#define DMA14_CNT2 0x1001B4 /* DMA Table Size : Ch#14 */ -//***************************************************************************** -#define DMA15_CNT2 0x1001B8 // DMA Table Size : Ch#15 +/* ***************************************************************************** */ +#define DMA15_CNT2 0x1001B8 /* DMA Table Size : Ch#15 */ -//***************************************************************************** -#define DMA16_CNT2 0x1001BC // DMA Table Size : Ch#16 +/* ***************************************************************************** */ +#define DMA16_CNT2 0x1001BC /* DMA Table Size : Ch#16 */ -//***************************************************************************** -#define DMA17_CNT2 0x1001C0 // DMA Table Size : Ch#17 +/* ***************************************************************************** */ +#define DMA17_CNT2 0x1001C0 /* DMA Table Size : Ch#17 */ -//***************************************************************************** -#define DMA18_CNT2 0x1001C4 // DMA Table Size : Ch#18 +/* ***************************************************************************** */ +#define DMA18_CNT2 0x1001C4 /* DMA Table Size : Ch#18 */ -//***************************************************************************** -#define DMA19_CNT2 0x1001C8 // DMA Table Size : Ch#19 +/* ***************************************************************************** */ +#define DMA19_CNT2 0x1001C8 /* DMA Table Size : Ch#19 */ -//***************************************************************************** -#define DMA20_CNT2 0x1001CC // DMA Table Size : Ch#20 +/* ***************************************************************************** */ +#define DMA20_CNT2 0x1001CC /* DMA Table Size : Ch#20 */ -//***************************************************************************** -#define DMA21_CNT2 0x1001D0 // DMA Table Size : Ch#21 +/* ***************************************************************************** */ +#define DMA21_CNT2 0x1001D0 /* DMA Table Size : Ch#21 */ -//***************************************************************************** -#define DMA22_CNT2 0x1001D4 // DMA Table Size : Ch#22 +/* ***************************************************************************** */ +#define DMA22_CNT2 0x1001D4 /* DMA Table Size : Ch#22 */ -//***************************************************************************** -#define DMA23_CNT2 0x1001D8 // DMA Table Size : Ch#23 +/* ***************************************************************************** */ +#define DMA23_CNT2 0x1001D8 /* DMA Table Size : Ch#23 */ -//***************************************************************************** -#define DMA24_CNT2 0x1001DC // DMA Table Size : Ch#24 +/* ***************************************************************************** */ +#define DMA24_CNT2 0x1001DC /* DMA Table Size : Ch#24 */ -//***************************************************************************** -#define DMA25_CNT2 0x1001E0 // DMA Table Size : Ch#25 +/* ***************************************************************************** */ +#define DMA25_CNT2 0x1001E0 /* DMA Table Size : Ch#25 */ -//***************************************************************************** -#define DMA26_CNT2 0x1001E4 // DMA Table Size : Ch#26 +/* ***************************************************************************** */ +#define DMA26_CNT2 0x1001E4 /* DMA Table Size : Ch#26 */ -//***************************************************************************** - // ITG -//***************************************************************************** -#define TM_CNT_LDW 0x110000 // Timer : Counter low +/* ***************************************************************************** */ + /* ITG */ +/* ***************************************************************************** */ +#define TM_CNT_LDW 0x110000 /* Timer : Counter low */ -//***************************************************************************** -#define TM_CNT_UW 0x110004 // Timer : Counter high word +/* ***************************************************************************** */ +#define TM_CNT_UW 0x110004 /* Timer : Counter high word */ -//***************************************************************************** -#define TM_LMT_LDW 0x110008 // Timer : Limit low +/* ***************************************************************************** */ +#define TM_LMT_LDW 0x110008 /* Timer : Limit low */ -//***************************************************************************** -#define TM_LMT_UW 0x11000C // Timer : Limit high word +/* ***************************************************************************** */ +#define TM_LMT_UW 0x11000C /* Timer : Limit high word */ -//***************************************************************************** -#define GP0_IO 0x110010 // GPIO output enables data I/O -#define FLD_GP_OE 0x00FF0000 // GPIO: GP_OE output enable -#define FLD_GP_IN 0x0000FF00 // GPIO: GP_IN status -#define FLD_GP_OUT 0x000000FF // GPIO: GP_OUT control +/* ***************************************************************************** */ +#define GP0_IO 0x110010 /* GPIO output enables data I/O */ +#define FLD_GP_OE 0x00FF0000 /* GPIO: GP_OE output enable */ +#define FLD_GP_IN 0x0000FF00 /* GPIO: GP_IN status */ +#define FLD_GP_OUT 0x000000FF /* GPIO: GP_OUT control */ -//***************************************************************************** -#define GPIO_ISM 0x110014 // GPIO interrupt sensitivity mode +/* ***************************************************************************** */ +#define GPIO_ISM 0x110014 /* GPIO interrupt sensitivity mode */ #define FLD_GP_ISM_SNS 0x00000070 #define FLD_GP_ISM_POL 0x00000007 -//***************************************************************************** -#define SOFT_RESET 0x11001C // Output system reset reg +/* ***************************************************************************** */ +#define SOFT_RESET 0x11001C /* Output system reset reg */ #define FLD_PECOS_SOFT_RESET 0x00000001 -//***************************************************************************** -#define MC416_RWD 0x110020 // MC416 GPIO[18:3] pin -#define MC416_OEN 0x110024 // Output enable of GPIO[18:3] +/* ***************************************************************************** */ +#define MC416_RWD 0x110020 /* MC416 GPIO[18:3] pin */ +#define MC416_OEN 0x110024 /* Output enable of GPIO[18:3] */ #define MC416_CTL 0x110028 -//***************************************************************************** -#define ALT_PIN_OUT_SEL 0x11002C // Alternate GPIO output select +/* ***************************************************************************** */ +#define ALT_PIN_OUT_SEL 0x11002C /* Alternate GPIO output select */ #define FLD_ALT_GPIO_OUT_SEL 0xF0000000 -// 0 Disabled <-- default -// 1 GPIO[0] -// 2 GPIO[10] -// 3 VIP_656_DATA_VAL -// 4 VIP_656_DATA[0] -// 5 VIP_656_CLK -// 6 VIP_656_DATA_EXT[1] -// 7 VIP_656_DATA_EXT[0] -// 8 ATT_IF +/* 0 Disabled <-- default */ +/* 1 GPIO[0] */ +/* 2 GPIO[10] */ +/* 3 VIP_656_DATA_VAL */ +/* 4 VIP_656_DATA[0] */ +/* 5 VIP_656_CLK */ +/* 6 VIP_656_DATA_EXT[1] */ +/* 7 VIP_656_DATA_EXT[0] */ +/* 8 ATT_IF */ #define FLD_AUX_PLL_CLK_ALT_SEL 0x0F000000 -// 0 AUX_PLL_CLK<-- default -// 1 GPIO[2] -// 2 GPIO[10] -// 3 VIP_656_DATA_VAL -// 4 VIP_656_DATA[0] -// 5 VIP_656_CLK -// 6 VIP_656_DATA_EXT[1] -// 7 VIP_656_DATA_EXT[0] +/* 0 AUX_PLL_CLK<-- default */ +/* 1 GPIO[2] */ +/* 2 GPIO[10] */ +/* 3 VIP_656_DATA_VAL */ +/* 4 VIP_656_DATA[0] */ +/* 5 VIP_656_CLK */ +/* 6 VIP_656_DATA_EXT[1] */ +/* 7 VIP_656_DATA_EXT[0] */ #define FLD_IR_TX_ALT_SEL 0x00F00000 -// 0 IR_TX <-- default -// 1 GPIO[1] -// 2 GPIO[10] -// 3 VIP_656_DATA_VAL -// 4 VIP_656_DATA[0] -// 5 VIP_656_CLK -// 6 VIP_656_DATA_EXT[1] -// 7 VIP_656_DATA_EXT[0] +/* 0 IR_TX <-- default */ +/* 1 GPIO[1] */ +/* 2 GPIO[10] */ +/* 3 VIP_656_DATA_VAL */ +/* 4 VIP_656_DATA[0] */ +/* 5 VIP_656_CLK */ +/* 6 VIP_656_DATA_EXT[1] */ +/* 7 VIP_656_DATA_EXT[0] */ #define FLD_IR_RX_ALT_SEL 0x000F0000 -// 0 IR_RX <-- default -// 1 GPIO[0] -// 2 GPIO[10] -// 3 VIP_656_DATA_VAL -// 4 VIP_656_DATA[0] -// 5 VIP_656_CLK -// 6 VIP_656_DATA_EXT[1] -// 7 VIP_656_DATA_EXT[0] +/* 0 IR_RX <-- default */ +/* 1 GPIO[0] */ +/* 2 GPIO[10] */ +/* 3 VIP_656_DATA_VAL */ +/* 4 VIP_656_DATA[0] */ +/* 5 VIP_656_CLK */ +/* 6 VIP_656_DATA_EXT[1] */ +/* 7 VIP_656_DATA_EXT[0] */ #define FLD_GPIO10_ALT_SEL 0x0000F000 -// 0 GPIO[10] <-- default -// 1 GPIO[0] -// 2 GPIO[10] -// 3 VIP_656_DATA_VAL -// 4 VIP_656_DATA[0] -// 5 VIP_656_CLK -// 6 VIP_656_DATA_EXT[1] -// 7 VIP_656_DATA_EXT[0] +/* 0 GPIO[10] <-- default */ +/* 1 GPIO[0] */ +/* 2 GPIO[10] */ +/* 3 VIP_656_DATA_VAL */ +/* 4 VIP_656_DATA[0] */ +/* 5 VIP_656_CLK */ +/* 6 VIP_656_DATA_EXT[1] */ +/* 7 VIP_656_DATA_EXT[0] */ #define FLD_GPIO2_ALT_SEL 0x00000F00 -// 0 GPIO[2] <-- default -// 1 GPIO[1] -// 2 GPIO[10] -// 3 VIP_656_DATA_VAL -// 4 VIP_656_DATA[0] -// 5 VIP_656_CLK -// 6 VIP_656_DATA_EXT[1] -// 7 VIP_656_DATA_EXT[0] +/* 0 GPIO[2] <-- default */ +/* 1 GPIO[1] */ +/* 2 GPIO[10] */ +/* 3 VIP_656_DATA_VAL */ +/* 4 VIP_656_DATA[0] */ +/* 5 VIP_656_CLK */ +/* 6 VIP_656_DATA_EXT[1] */ +/* 7 VIP_656_DATA_EXT[0] */ #define FLD_GPIO1_ALT_SEL 0x000000F0 -// 0 GPIO[1] <-- default -// 1 GPIO[0] -// 2 GPIO[10] -// 3 VIP_656_DATA_VAL -// 4 VIP_656_DATA[0] -// 5 VIP_656_CLK -// 6 VIP_656_DATA_EXT[1] -// 7 VIP_656_DATA_EXT[0] +/* 0 GPIO[1] <-- default */ +/* 1 GPIO[0] */ +/* 2 GPIO[10] */ +/* 3 VIP_656_DATA_VAL */ +/* 4 VIP_656_DATA[0] */ +/* 5 VIP_656_CLK */ +/* 6 VIP_656_DATA_EXT[1] */ +/* 7 VIP_656_DATA_EXT[0] */ #define FLD_GPIO0_ALT_SEL 0x0000000F -// 0 GPIO[0] <-- default -// 1 GPIO[1] -// 2 GPIO[10] -// 3 VIP_656_DATA_VAL -// 4 VIP_656_DATA[0] -// 5 VIP_656_CLK -// 6 VIP_656_DATA_EXT[1] -// 7 VIP_656_DATA_EXT[0] +/* 0 GPIO[0] <-- default */ +/* 1 GPIO[1] */ +/* 2 GPIO[10] */ +/* 3 VIP_656_DATA_VAL */ +/* 4 VIP_656_DATA[0] */ +/* 5 VIP_656_CLK */ +/* 6 VIP_656_DATA_EXT[1] */ +/* 7 VIP_656_DATA_EXT[0] */ -#define ALT_PIN_IN_SEL 0x110030 // Alternate GPIO input select +#define ALT_PIN_IN_SEL 0x110030 /* Alternate GPIO input select */ #define FLD_GPIO10_ALT_IN_SEL 0x0000F000 -// 0 GPIO[10] <-- default -// 1 IR_RX -// 2 IR_TX -// 3 AUX_PLL_CLK -// 4 IF_ATT_SEL -// 5 GPIO[0] -// 6 GPIO[1] -// 7 GPIO[2] +/* 0 GPIO[10] <-- default */ +/* 1 IR_RX */ +/* 2 IR_TX */ +/* 3 AUX_PLL_CLK */ +/* 4 IF_ATT_SEL */ +/* 5 GPIO[0] */ +/* 6 GPIO[1] */ +/* 7 GPIO[2] */ #define FLD_GPIO2_ALT_IN_SEL 0x00000F00 -// 0 GPIO[2] <-- default -// 1 IR_RX -// 2 IR_TX -// 3 AUX_PLL_CLK -// 4 IF_ATT_SEL +/* 0 GPIO[2] <-- default */ +/* 1 IR_RX */ +/* 2 IR_TX */ +/* 3 AUX_PLL_CLK */ +/* 4 IF_ATT_SEL */ #define FLD_GPIO1_ALT_IN_SEL 0x000000F0 -// 0 GPIO[1] <-- default -// 1 IR_RX -// 2 IR_TX -// 3 AUX_PLL_CLK -// 4 IF_ATT_SEL +/* 0 GPIO[1] <-- default */ +/* 1 IR_RX */ +/* 2 IR_TX */ +/* 3 AUX_PLL_CLK */ +/* 4 IF_ATT_SEL */ #define FLD_GPIO0_ALT_IN_SEL 0x0000000F -// 0 GPIO[0] <-- default -// 1 IR_RX -// 2 IR_TX -// 3 AUX_PLL_CLK -// 4 IF_ATT_SEL +/* 0 GPIO[0] <-- default */ +/* 1 IR_RX */ +/* 2 IR_TX */ +/* 3 AUX_PLL_CLK */ +/* 4 IF_ATT_SEL */ -//***************************************************************************** -#define TEST_BUS_CTL1 0x110040 // Test bus control register #1 +/* ***************************************************************************** */ +#define TEST_BUS_CTL1 0x110040 /* Test bus control register #1 */ -//***************************************************************************** -#define TEST_BUS_CTL2 0x110044 // Test bus control register #2 +/* ***************************************************************************** */ +#define TEST_BUS_CTL2 0x110044 /* Test bus control register #2 */ -//***************************************************************************** -#define CLK_DELAY 0x110048 // Clock delay -#define FLD_MOE_CLK_DIS 0x80000000 // Disable MoE clock +/* ***************************************************************************** */ +#define CLK_DELAY 0x110048 /* Clock delay */ +#define FLD_MOE_CLK_DIS 0x80000000 /* Disable MoE clock */ -//***************************************************************************** -#define PAD_CTRL 0x110068 // Pad drive strength control +/* ***************************************************************************** */ +#define PAD_CTRL 0x110068 /* Pad drive strength control */ -//***************************************************************************** -#define MBIST_CTRL 0x110050 // SRAM memory built-in self test control +/* ***************************************************************************** */ +#define MBIST_CTRL 0x110050 /* SRAM memory built-in self test control */ -//***************************************************************************** -#define MBIST_STAT 0x110054 // SRAM memory built-in self test status +/* ***************************************************************************** */ +#define MBIST_STAT 0x110054 /* SRAM memory built-in self test status */ -//***************************************************************************** -// PLL registers -//***************************************************************************** +/* ***************************************************************************** */ +/* PLL registers */ +/* ***************************************************************************** */ #define PLL_A_INT_FRAC 0x110088 #define PLL_A_POST_STAT_BIST 0x11008C #define PLL_B_INT_FRAC 0x110090 @@ -1090,260 +1090,260 @@ #define VID_CH_MODE_SEL 0x110078 #define VID_CH_CLK_SEL 0x11007C -//***************************************************************************** -#define VBI_A_DMA 0x130008 // VBI A DMA data port +/* ***************************************************************************** */ +#define VBI_A_DMA 0x130008 /* VBI A DMA data port */ -//***************************************************************************** -#define VID_A_VIP_CTL 0x130080 // Video A VIP format control +/* ***************************************************************************** */ +#define VID_A_VIP_CTL 0x130080 /* Video A VIP format control */ #define FLD_VIP_MODE 0x00000001 -//***************************************************************************** -#define VID_A_PIXEL_FRMT 0x130084 // Video A pixel format +/* ***************************************************************************** */ +#define VID_A_PIXEL_FRMT 0x130084 /* Video A pixel format */ #define FLD_VID_A_GAMMA_DIS 0x00000008 #define FLD_VID_A_FORMAT 0x00000007 #define FLD_VID_A_GAMMA_FACTOR 0x00000010 -//***************************************************************************** -#define VID_A_VBI_CTL 0x130088 // Video A VBI miscellaneous control +/* ***************************************************************************** */ +#define VID_A_VBI_CTL 0x130088 /* Video A VBI miscellaneous control */ #define FLD_VID_A_VIP_EXT 0x00000003 -//***************************************************************************** -#define VID_B_DMA 0x130100 // Video B DMA data port +/* ***************************************************************************** */ +#define VID_B_DMA 0x130100 /* Video B DMA data port */ -//***************************************************************************** -#define VBI_B_DMA 0x130108 // VBI B DMA data port +/* ***************************************************************************** */ +#define VBI_B_DMA 0x130108 /* VBI B DMA data port */ -//***************************************************************************** -#define VID_B_SRC_SEL 0x130144 // Video B source select +/* ***************************************************************************** */ +#define VID_B_SRC_SEL 0x130144 /* Video B source select */ #define FLD_VID_B_SRC_SEL 0x00000000 -//***************************************************************************** -#define VID_B_LNGTH 0x130150 // Video B line length +/* ***************************************************************************** */ +#define VID_B_LNGTH 0x130150 /* Video B line length */ #define FLD_VID_B_LN_LNGTH 0x00000FFF -//***************************************************************************** -#define VID_B_VIP_CTL 0x130180 // Video B VIP format control +/* ***************************************************************************** */ +#define VID_B_VIP_CTL 0x130180 /* Video B VIP format control */ -//***************************************************************************** -#define VID_B_PIXEL_FRMT 0x130184 // Video B pixel format +/* ***************************************************************************** */ +#define VID_B_PIXEL_FRMT 0x130184 /* Video B pixel format */ #define FLD_VID_B_GAMMA_DIS 0x00000008 #define FLD_VID_B_FORMAT 0x00000007 #define FLD_VID_B_GAMMA_FACTOR 0x00000010 -//***************************************************************************** -#define VID_C_DMA 0x130200 // Video C DMA data port +/* ***************************************************************************** */ +#define VID_C_DMA 0x130200 /* Video C DMA data port */ -//***************************************************************************** -#define VID_C_LNGTH 0x130250 // Video C line length +/* ***************************************************************************** */ +#define VID_C_LNGTH 0x130250 /* Video C line length */ #define FLD_VID_C_LN_LNGTH 0x00000FFF -//***************************************************************************** -// Video Destination Channels -//***************************************************************************** - -#define VID_DST_A_GPCNT 0x130020 // Video A general purpose counter -#define VID_DST_B_GPCNT 0x130120 // Video B general purpose counter -#define VID_DST_C_GPCNT 0x130220 // Video C general purpose counter -#define VID_DST_D_GPCNT 0x130320 // Video D general purpose counter -#define VID_DST_E_GPCNT 0x130420 // Video E general purpose counter -#define VID_DST_F_GPCNT 0x130520 // Video F general purpose counter -#define VID_DST_G_GPCNT 0x130620 // Video G general purpose counter -#define VID_DST_H_GPCNT 0x130720 // Video H general purpose counter - -//***************************************************************************** - -#define VID_DST_A_GPCNT_CTL 0x130030 // Video A general purpose control -#define VID_DST_B_GPCNT_CTL 0x130130 // Video B general purpose control -#define VID_DST_C_GPCNT_CTL 0x130230 // Video C general purpose control -#define VID_DST_D_GPCNT_CTL 0x130330 // Video D general purpose control -#define VID_DST_E_GPCNT_CTL 0x130430 // Video E general purpose control -#define VID_DST_F_GPCNT_CTL 0x130530 // Video F general purpose control -#define VID_DST_G_GPCNT_CTL 0x130630 // Video G general purpose control -#define VID_DST_H_GPCNT_CTL 0x130730 // Video H general purpose control - -//***************************************************************************** - -#define VID_DST_A_DMA_CTL 0x130040 // Video A DMA control -#define VID_DST_B_DMA_CTL 0x130140 // Video B DMA control -#define VID_DST_C_DMA_CTL 0x130240 // Video C DMA control -#define VID_DST_D_DMA_CTL 0x130340 // Video D DMA control -#define VID_DST_E_DMA_CTL 0x130440 // Video E DMA control -#define VID_DST_F_DMA_CTL 0x130540 // Video F DMA control -#define VID_DST_G_DMA_CTL 0x130640 // Video G DMA control -#define VID_DST_H_DMA_CTL 0x130740 // Video H DMA control +/* ***************************************************************************** */ +/* Video Destination Channels */ +/* ***************************************************************************** */ + +#define VID_DST_A_GPCNT 0x130020 /* Video A general purpose counter */ +#define VID_DST_B_GPCNT 0x130120 /* Video B general purpose counter */ +#define VID_DST_C_GPCNT 0x130220 /* Video C general purpose counter */ +#define VID_DST_D_GPCNT 0x130320 /* Video D general purpose counter */ +#define VID_DST_E_GPCNT 0x130420 /* Video E general purpose counter */ +#define VID_DST_F_GPCNT 0x130520 /* Video F general purpose counter */ +#define VID_DST_G_GPCNT 0x130620 /* Video G general purpose counter */ +#define VID_DST_H_GPCNT 0x130720 /* Video H general purpose counter */ + +/* ***************************************************************************** */ + +#define VID_DST_A_GPCNT_CTL 0x130030 /* Video A general purpose control */ +#define VID_DST_B_GPCNT_CTL 0x130130 /* Video B general purpose control */ +#define VID_DST_C_GPCNT_CTL 0x130230 /* Video C general purpose control */ +#define VID_DST_D_GPCNT_CTL 0x130330 /* Video D general purpose control */ +#define VID_DST_E_GPCNT_CTL 0x130430 /* Video E general purpose control */ +#define VID_DST_F_GPCNT_CTL 0x130530 /* Video F general purpose control */ +#define VID_DST_G_GPCNT_CTL 0x130630 /* Video G general purpose control */ +#define VID_DST_H_GPCNT_CTL 0x130730 /* Video H general purpose control */ + +/* ***************************************************************************** */ + +#define VID_DST_A_DMA_CTL 0x130040 /* Video A DMA control */ +#define VID_DST_B_DMA_CTL 0x130140 /* Video B DMA control */ +#define VID_DST_C_DMA_CTL 0x130240 /* Video C DMA control */ +#define VID_DST_D_DMA_CTL 0x130340 /* Video D DMA control */ +#define VID_DST_E_DMA_CTL 0x130440 /* Video E DMA control */ +#define VID_DST_F_DMA_CTL 0x130540 /* Video F DMA control */ +#define VID_DST_G_DMA_CTL 0x130640 /* Video G DMA control */ +#define VID_DST_H_DMA_CTL 0x130740 /* Video H DMA control */ #define FLD_VID_RISC_EN 0x00000010 #define FLD_VID_FIFO_EN 0x00000001 -//***************************************************************************** - -#define VID_DST_A_VIP_CTL 0x130080 // Video A VIP control -#define VID_DST_B_VIP_CTL 0x130180 // Video B VIP control -#define VID_DST_C_VIP_CTL 0x130280 // Video C VIP control -#define VID_DST_D_VIP_CTL 0x130380 // Video D VIP control -#define VID_DST_E_VIP_CTL 0x130480 // Video E VIP control -#define VID_DST_F_VIP_CTL 0x130580 // Video F VIP control -#define VID_DST_G_VIP_CTL 0x130680 // Video G VIP control -#define VID_DST_H_VIP_CTL 0x130780 // Video H VIP control - -//***************************************************************************** - -#define VID_DST_A_PIX_FRMT 0x130084 // Video A Pixel format -#define VID_DST_B_PIX_FRMT 0x130184 // Video B Pixel format -#define VID_DST_C_PIX_FRMT 0x130284 // Video C Pixel format -#define VID_DST_D_PIX_FRMT 0x130384 // Video D Pixel format -#define VID_DST_E_PIX_FRMT 0x130484 // Video E Pixel format -#define VID_DST_F_PIX_FRMT 0x130584 // Video F Pixel format -#define VID_DST_G_PIX_FRMT 0x130684 // Video G Pixel format -#define VID_DST_H_PIX_FRMT 0x130784 // Video H Pixel format - -//***************************************************************************** -// Video Source Channels -//***************************************************************************** - -#define VID_SRC_A_GPCNT_CTL 0x130804 // Video A general purpose control -#define VID_SRC_B_GPCNT_CTL 0x130904 // Video B general purpose control -#define VID_SRC_C_GPCNT_CTL 0x130A04 // Video C general purpose control -#define VID_SRC_D_GPCNT_CTL 0x130B04 // Video D general purpose control -#define VID_SRC_E_GPCNT_CTL 0x130C04 // Video E general purpose control -#define VID_SRC_F_GPCNT_CTL 0x130D04 // Video F general purpose control -#define VID_SRC_I_GPCNT_CTL 0x130E04 // Video I general purpose control -#define VID_SRC_J_GPCNT_CTL 0x130F04 // Video J general purpose control - -//***************************************************************************** - -#define VID_SRC_A_GPCNT 0x130808 // Video A general purpose counter -#define VID_SRC_B_GPCNT 0x130908 // Video B general purpose counter -#define VID_SRC_C_GPCNT 0x130A08 // Video C general purpose counter -#define VID_SRC_D_GPCNT 0x130B08 // Video D general purpose counter -#define VID_SRC_E_GPCNT 0x130C08 // Video E general purpose counter -#define VID_SRC_F_GPCNT 0x130D08 // Video F general purpose counter -#define VID_SRC_I_GPCNT 0x130E08 // Video I general purpose counter -#define VID_SRC_J_GPCNT 0x130F08 // Video J general purpose counter - -//***************************************************************************** - -#define VID_SRC_A_DMA_CTL 0x13080C // Video A DMA control -#define VID_SRC_B_DMA_CTL 0x13090C // Video B DMA control -#define VID_SRC_C_DMA_CTL 0x130A0C // Video C DMA control -#define VID_SRC_D_DMA_CTL 0x130B0C // Video D DMA control -#define VID_SRC_E_DMA_CTL 0x130C0C // Video E DMA control -#define VID_SRC_F_DMA_CTL 0x130D0C // Video F DMA control -#define VID_SRC_I_DMA_CTL 0x130E0C // Video I DMA control -#define VID_SRC_J_DMA_CTL 0x130F0C // Video J DMA control +/* ***************************************************************************** */ + +#define VID_DST_A_VIP_CTL 0x130080 /* Video A VIP control */ +#define VID_DST_B_VIP_CTL 0x130180 /* Video B VIP control */ +#define VID_DST_C_VIP_CTL 0x130280 /* Video C VIP control */ +#define VID_DST_D_VIP_CTL 0x130380 /* Video D VIP control */ +#define VID_DST_E_VIP_CTL 0x130480 /* Video E VIP control */ +#define VID_DST_F_VIP_CTL 0x130580 /* Video F VIP control */ +#define VID_DST_G_VIP_CTL 0x130680 /* Video G VIP control */ +#define VID_DST_H_VIP_CTL 0x130780 /* Video H VIP control */ + +/* ***************************************************************************** */ + +#define VID_DST_A_PIX_FRMT 0x130084 /* Video A Pixel format */ +#define VID_DST_B_PIX_FRMT 0x130184 /* Video B Pixel format */ +#define VID_DST_C_PIX_FRMT 0x130284 /* Video C Pixel format */ +#define VID_DST_D_PIX_FRMT 0x130384 /* Video D Pixel format */ +#define VID_DST_E_PIX_FRMT 0x130484 /* Video E Pixel format */ +#define VID_DST_F_PIX_FRMT 0x130584 /* Video F Pixel format */ +#define VID_DST_G_PIX_FRMT 0x130684 /* Video G Pixel format */ +#define VID_DST_H_PIX_FRMT 0x130784 /* Video H Pixel format */ + +/* ***************************************************************************** */ +/* Video Source Channels */ +/* ***************************************************************************** */ + +#define VID_SRC_A_GPCNT_CTL 0x130804 /* Video A general purpose control */ +#define VID_SRC_B_GPCNT_CTL 0x130904 /* Video B general purpose control */ +#define VID_SRC_C_GPCNT_CTL 0x130A04 /* Video C general purpose control */ +#define VID_SRC_D_GPCNT_CTL 0x130B04 /* Video D general purpose control */ +#define VID_SRC_E_GPCNT_CTL 0x130C04 /* Video E general purpose control */ +#define VID_SRC_F_GPCNT_CTL 0x130D04 /* Video F general purpose control */ +#define VID_SRC_I_GPCNT_CTL 0x130E04 /* Video I general purpose control */ +#define VID_SRC_J_GPCNT_CTL 0x130F04 /* Video J general purpose control */ + +/* ***************************************************************************** */ + +#define VID_SRC_A_GPCNT 0x130808 /* Video A general purpose counter */ +#define VID_SRC_B_GPCNT 0x130908 /* Video B general purpose counter */ +#define VID_SRC_C_GPCNT 0x130A08 /* Video C general purpose counter */ +#define VID_SRC_D_GPCNT 0x130B08 /* Video D general purpose counter */ +#define VID_SRC_E_GPCNT 0x130C08 /* Video E general purpose counter */ +#define VID_SRC_F_GPCNT 0x130D08 /* Video F general purpose counter */ +#define VID_SRC_I_GPCNT 0x130E08 /* Video I general purpose counter */ +#define VID_SRC_J_GPCNT 0x130F08 /* Video J general purpose counter */ + +/* ***************************************************************************** */ + +#define VID_SRC_A_DMA_CTL 0x13080C /* Video A DMA control */ +#define VID_SRC_B_DMA_CTL 0x13090C /* Video B DMA control */ +#define VID_SRC_C_DMA_CTL 0x130A0C /* Video C DMA control */ +#define VID_SRC_D_DMA_CTL 0x130B0C /* Video D DMA control */ +#define VID_SRC_E_DMA_CTL 0x130C0C /* Video E DMA control */ +#define VID_SRC_F_DMA_CTL 0x130D0C /* Video F DMA control */ +#define VID_SRC_I_DMA_CTL 0x130E0C /* Video I DMA control */ +#define VID_SRC_J_DMA_CTL 0x130F0C /* Video J DMA control */ #define FLD_APB_RISC_EN 0x00000010 #define FLD_APB_FIFO_EN 0x00000001 -//***************************************************************************** - -#define VID_SRC_A_FMT_CTL 0x130810 // Video A format control -#define VID_SRC_B_FMT_CTL 0x130910 // Video B format control -#define VID_SRC_C_FMT_CTL 0x130A10 // Video C format control -#define VID_SRC_D_FMT_CTL 0x130B10 // Video D format control -#define VID_SRC_E_FMT_CTL 0x130C10 // Video E format control -#define VID_SRC_F_FMT_CTL 0x130D10 // Video F format control -#define VID_SRC_I_FMT_CTL 0x130E10 // Video I format control -#define VID_SRC_J_FMT_CTL 0x130F10 // Video J format control - -//***************************************************************************** - -#define VID_SRC_A_ACTIVE_CTL1 0x130814 // Video A active control 1 -#define VID_SRC_B_ACTIVE_CTL1 0x130914 // Video B active control 1 -#define VID_SRC_C_ACTIVE_CTL1 0x130A14 // Video C active control 1 -#define VID_SRC_D_ACTIVE_CTL1 0x130B14 // Video D active control 1 -#define VID_SRC_E_ACTIVE_CTL1 0x130C14 // Video E active control 1 -#define VID_SRC_F_ACTIVE_CTL1 0x130D14 // Video F active control 1 -#define VID_SRC_I_ACTIVE_CTL1 0x130E14 // Video I active control 1 -#define VID_SRC_J_ACTIVE_CTL1 0x130F14 // Video J active control 1 - -//***************************************************************************** - -#define VID_SRC_A_ACTIVE_CTL2 0x130818 // Video A active control 2 -#define VID_SRC_B_ACTIVE_CTL2 0x130918 // Video B active control 2 -#define VID_SRC_C_ACTIVE_CTL2 0x130A18 // Video C active control 2 -#define VID_SRC_D_ACTIVE_CTL2 0x130B18 // Video D active control 2 -#define VID_SRC_E_ACTIVE_CTL2 0x130C18 // Video E active control 2 -#define VID_SRC_F_ACTIVE_CTL2 0x130D18 // Video F active control 2 -#define VID_SRC_I_ACTIVE_CTL2 0x130E18 // Video I active control 2 -#define VID_SRC_J_ACTIVE_CTL2 0x130F18 // Video J active control 2 - -//***************************************************************************** - -#define VID_SRC_A_CDT_SZ 0x13081C // Video A CDT size -#define VID_SRC_B_CDT_SZ 0x13091C // Video B CDT size -#define VID_SRC_C_CDT_SZ 0x130A1C // Video C CDT size -#define VID_SRC_D_CDT_SZ 0x130B1C // Video D CDT size -#define VID_SRC_E_CDT_SZ 0x130C1C // Video E CDT size -#define VID_SRC_F_CDT_SZ 0x130D1C // Video F CDT size -#define VID_SRC_I_CDT_SZ 0x130E1C // Video I CDT size -#define VID_SRC_J_CDT_SZ 0x130F1C // Video J CDT size - -//***************************************************************************** -// Audio I/F -//***************************************************************************** -#define AUD_DST_A_DMA 0x140000 // Audio Int A DMA data port -#define AUD_SRC_A_DMA 0x140008 // Audio Int A DMA data port - -#define AUD_A_GPCNT 0x140010 // Audio Int A gp counter +/* ***************************************************************************** */ + +#define VID_SRC_A_FMT_CTL 0x130810 /* Video A format control */ +#define VID_SRC_B_FMT_CTL 0x130910 /* Video B format control */ +#define VID_SRC_C_FMT_CTL 0x130A10 /* Video C format control */ +#define VID_SRC_D_FMT_CTL 0x130B10 /* Video D format control */ +#define VID_SRC_E_FMT_CTL 0x130C10 /* Video E format control */ +#define VID_SRC_F_FMT_CTL 0x130D10 /* Video F format control */ +#define VID_SRC_I_FMT_CTL 0x130E10 /* Video I format control */ +#define VID_SRC_J_FMT_CTL 0x130F10 /* Video J format control */ + +/* ***************************************************************************** */ + +#define VID_SRC_A_ACTIVE_CTL1 0x130814 /* Video A active control 1 */ +#define VID_SRC_B_ACTIVE_CTL1 0x130914 /* Video B active control 1 */ +#define VID_SRC_C_ACTIVE_CTL1 0x130A14 /* Video C active control 1 */ +#define VID_SRC_D_ACTIVE_CTL1 0x130B14 /* Video D active control 1 */ +#define VID_SRC_E_ACTIVE_CTL1 0x130C14 /* Video E active control 1 */ +#define VID_SRC_F_ACTIVE_CTL1 0x130D14 /* Video F active control 1 */ +#define VID_SRC_I_ACTIVE_CTL1 0x130E14 /* Video I active control 1 */ +#define VID_SRC_J_ACTIVE_CTL1 0x130F14 /* Video J active control 1 */ + +/* ***************************************************************************** */ + +#define VID_SRC_A_ACTIVE_CTL2 0x130818 /* Video A active control 2 */ +#define VID_SRC_B_ACTIVE_CTL2 0x130918 /* Video B active control 2 */ +#define VID_SRC_C_ACTIVE_CTL2 0x130A18 /* Video C active control 2 */ +#define VID_SRC_D_ACTIVE_CTL2 0x130B18 /* Video D active control 2 */ +#define VID_SRC_E_ACTIVE_CTL2 0x130C18 /* Video E active control 2 */ +#define VID_SRC_F_ACTIVE_CTL2 0x130D18 /* Video F active control 2 */ +#define VID_SRC_I_ACTIVE_CTL2 0x130E18 /* Video I active control 2 */ +#define VID_SRC_J_ACTIVE_CTL2 0x130F18 /* Video J active control 2 */ + +/* ***************************************************************************** */ + +#define VID_SRC_A_CDT_SZ 0x13081C /* Video A CDT size */ +#define VID_SRC_B_CDT_SZ 0x13091C /* Video B CDT size */ +#define VID_SRC_C_CDT_SZ 0x130A1C /* Video C CDT size */ +#define VID_SRC_D_CDT_SZ 0x130B1C /* Video D CDT size */ +#define VID_SRC_E_CDT_SZ 0x130C1C /* Video E CDT size */ +#define VID_SRC_F_CDT_SZ 0x130D1C /* Video F CDT size */ +#define VID_SRC_I_CDT_SZ 0x130E1C /* Video I CDT size */ +#define VID_SRC_J_CDT_SZ 0x130F1C /* Video J CDT size */ + +/* ***************************************************************************** */ +/* Audio I/F */ +/* ***************************************************************************** */ +#define AUD_DST_A_DMA 0x140000 /* Audio Int A DMA data port */ +#define AUD_SRC_A_DMA 0x140008 /* Audio Int A DMA data port */ + +#define AUD_A_GPCNT 0x140010 /* Audio Int A gp counter */ #define FLD_AUD_A_GP_CNT 0x0000FFFF -#define AUD_A_GPCNT_CTL 0x140014 // Audio Int A gp control +#define AUD_A_GPCNT_CTL 0x140014 /* Audio Int A gp control */ -#define AUD_A_LNGTH 0x140018 // Audio Int A line length +#define AUD_A_LNGTH 0x140018 /* Audio Int A line length */ -#define AUD_A_CFG 0x14001C // Audio Int A configuration +#define AUD_A_CFG 0x14001C /* Audio Int A configuration */ -//***************************************************************************** -#define AUD_DST_B_DMA 0x140100 // Audio Int B DMA data port -#define AUD_SRC_B_DMA 0x140108 // Audio Int B DMA data port +/* ***************************************************************************** */ +#define AUD_DST_B_DMA 0x140100 /* Audio Int B DMA data port */ +#define AUD_SRC_B_DMA 0x140108 /* Audio Int B DMA data port */ -#define AUD_B_GPCNT 0x140110 // Audio Int B gp counter +#define AUD_B_GPCNT 0x140110 /* Audio Int B gp counter */ #define FLD_AUD_B_GP_CNT 0x0000FFFF -#define AUD_B_GPCNT_CTL 0x140114 // Audio Int B gp control +#define AUD_B_GPCNT_CTL 0x140114 /* Audio Int B gp control */ -#define AUD_B_LNGTH 0x140118 // Audio Int B line length +#define AUD_B_LNGTH 0x140118 /* Audio Int B line length */ -#define AUD_B_CFG 0x14011C // Audio Int B configuration +#define AUD_B_CFG 0x14011C /* Audio Int B configuration */ -//***************************************************************************** -#define AUD_DST_C_DMA 0x140200 // Audio Int C DMA data port -#define AUD_SRC_C_DMA 0x140208 // Audio Int C DMA data port +/* ***************************************************************************** */ +#define AUD_DST_C_DMA 0x140200 /* Audio Int C DMA data port */ +#define AUD_SRC_C_DMA 0x140208 /* Audio Int C DMA data port */ -#define AUD_C_GPCNT 0x140210 // Audio Int C gp counter +#define AUD_C_GPCNT 0x140210 /* Audio Int C gp counter */ #define FLD_AUD_C_GP_CNT 0x0000FFFF -#define AUD_C_GPCNT_CTL 0x140214 // Audio Int C gp control +#define AUD_C_GPCNT_CTL 0x140214 /* Audio Int C gp control */ -#define AUD_C_LNGTH 0x140218 // Audio Int C line length +#define AUD_C_LNGTH 0x140218 /* Audio Int C line length */ -#define AUD_C_CFG 0x14021C // Audio Int C configuration +#define AUD_C_CFG 0x14021C /* Audio Int C configuration */ -//***************************************************************************** -#define AUD_DST_D_DMA 0x140300 // Audio Int D DMA data port -#define AUD_SRC_D_DMA 0x140308 // Audio Int D DMA data port +/* ***************************************************************************** */ +#define AUD_DST_D_DMA 0x140300 /* Audio Int D DMA data port */ +#define AUD_SRC_D_DMA 0x140308 /* Audio Int D DMA data port */ -#define AUD_D_GPCNT 0x140310 // Audio Int D gp counter +#define AUD_D_GPCNT 0x140310 /* Audio Int D gp counter */ #define FLD_AUD_D_GP_CNT 0x0000FFFF -#define AUD_D_GPCNT_CTL 0x140314 // Audio Int D gp control +#define AUD_D_GPCNT_CTL 0x140314 /* Audio Int D gp control */ -#define AUD_D_LNGTH 0x140318 // Audio Int D line length +#define AUD_D_LNGTH 0x140318 /* Audio Int D line length */ -#define AUD_D_CFG 0x14031C // Audio Int D configuration +#define AUD_D_CFG 0x14031C /* Audio Int D configuration */ -//***************************************************************************** -#define AUD_SRC_E_DMA 0x140400 // Audio Int E DMA data port +/* ***************************************************************************** */ +#define AUD_SRC_E_DMA 0x140400 /* Audio Int E DMA data port */ -#define AUD_E_GPCNT 0x140410 // Audio Int E gp counter +#define AUD_E_GPCNT 0x140410 /* Audio Int E gp counter */ #define FLD_AUD_E_GP_CNT 0x0000FFFF -#define AUD_E_GPCNT_CTL 0x140414 // Audio Int E gp control +#define AUD_E_GPCNT_CTL 0x140414 /* Audio Int E gp control */ -#define AUD_E_CFG 0x14041C // Audio Int E configuration +#define AUD_E_CFG 0x14041C /* Audio Int E configuration */ -//***************************************************************************** +/* ***************************************************************************** */ #define FLD_AUD_DST_LN_LNGTH 0x00000FFF @@ -1361,8 +1361,8 @@ #define FLD_AUD_SRC_ENABLE 0x00010000 -//***************************************************************************** -#define AUD_INT_DMA_CTL 0x140500 // Audio Int DMA control +/* ***************************************************************************** */ +#define AUD_INT_DMA_CTL 0x140500 /* Audio Int DMA control */ #define FLD_AUD_SRC_E_RISC_EN 0x00008000 #define FLD_AUD_SRC_C_RISC_EN 0x00004000 @@ -1384,15 +1384,15 @@ #define FLD_AUD_DST_B_FIFO_EN 0x00000002 #define FLD_AUD_DST_A_FIFO_EN 0x00000001 -//***************************************************************************** -// -// Mobilygen Interface Registers -// -//***************************************************************************** -// Mobilygen Interface A -//***************************************************************************** -#define MB_IF_A_DMA 0x150000 // MBIF A DMA data port -#define MB_IF_A_GPCN 0x150008 // MBIF A GP counter +/* ***************************************************************************** */ +/* */ +/* Mobilygen Interface Registers */ +/* */ +/* ***************************************************************************** */ +/* Mobilygen Interface A */ +/* ***************************************************************************** */ +#define MB_IF_A_DMA 0x150000 /* MBIF A DMA data port */ +#define MB_IF_A_GPCN 0x150008 /* MBIF A GP counter */ #define MB_IF_A_GPCN_CTRL 0x15000C #define MB_IF_A_DMA_CTRL 0x150010 #define MB_IF_A_LENGTH 0x150014 @@ -1415,11 +1415,11 @@ #define MB_IF_A_DATA_STRUCT_D 0x150058 #define MB_IF_A_DATA_STRUCT_E 0x15005C #define MB_IF_A_DATA_STRUCT_F 0x150060 -//***************************************************************************** -// Mobilygen Interface B -//***************************************************************************** -#define MB_IF_B_DMA 0x160000 // MBIF A DMA data port -#define MB_IF_B_GPCN 0x160008 // MBIF A GP counter +/* ***************************************************************************** */ +/* Mobilygen Interface B */ +/* ***************************************************************************** */ +#define MB_IF_B_DMA 0x160000 /* MBIF A DMA data port */ +#define MB_IF_B_GPCN 0x160008 /* MBIF A GP counter */ #define MB_IF_B_GPCN_CTRL 0x16000C #define MB_IF_B_DMA_CTRL 0x160010 #define MB_IF_B_LENGTH 0x160014 @@ -1443,14 +1443,14 @@ #define MB_IF_B_DATA_STRUCT_E 0x16005C #define MB_IF_B_DATA_STRUCT_F 0x160060 -// MB_DMA_CTRL +/* MB_DMA_CTRL */ #define FLD_MB_IF_RISC_EN 0x00000010 #define FLD_MB_IF_FIFO_EN 0x00000001 -// MB_LENGTH +/* MB_LENGTH */ #define FLD_MB_IF_LN_LNGTH 0x00000FFF -// MB_HCMD register +/* MB_HCMD register */ #define FLD_MB_HCMD_H_GO 0x80000000 #define FLD_MB_HCMD_H_BUSY 0x40000000 #define FLD_MB_HCMD_H_DMA_HOLD 0x10000000 @@ -1461,118 +1461,118 @@ #define FLD_MB_HCMD_H_ADDR 0x00FF0000 #define FLD_MB_HCMD_H_DATA 0x0000FFFF -//***************************************************************************** -// I2C #1 -//***************************************************************************** -#define I2C1_ADDR 0x180000 // I2C #1 address -#define FLD_I2C_DADDR 0xfe000000 // RW [31:25] I2C Device Address - // RO [24] reserved -//***************************************************************************** -#define FLD_I2C_SADDR 0x00FFFFFF // RW [23:0] I2C Sub-address - -//***************************************************************************** -#define I2C1_WDATA 0x180004 // I2C #1 write data -#define FLD_I2C_WDATA 0xFFFFFFFF // RW [31:0] - -//***************************************************************************** -#define I2C1_CTRL 0x180008 // I2C #1 control -#define FLD_I2C_PERIOD 0xFF000000 // RW [31:24] -#define FLD_I2C_SCL_IN 0x00200000 // RW [21] -#define FLD_I2C_SDA_IN 0x00100000 // RW [20] - // RO [19:18] reserved -#define FLD_I2C_SCL_OUT 0x00020000 // RW [17] -#define FLD_I2C_SDA_OUT 0x00010000 // RW [16] - // RO [15] reserved -#define FLD_I2C_DATA_LEN 0x00007000 // RW [14:12] -#define FLD_I2C_SADDR_INC 0x00000800 // RW [11] - // RO [10:9] reserved -#define FLD_I2C_SADDR_LEN 0x00000300 // RW [9:8] - // RO [7:6] reserved -#define FLD_I2C_SOFT 0x00000020 // RW [5] -#define FLD_I2C_NOSTOP 0x00000010 // RW [4] -#define FLD_I2C_EXTEND 0x00000008 // RW [3] -#define FLD_I2C_SYNC 0x00000004 // RW [2] -#define FLD_I2C_READ_SA 0x00000002 // RW [1] -#define FLD_I2C_READ_WRN 0x00000001 // RW [0] - -//***************************************************************************** -#define I2C1_RDATA 0x18000C // I2C #1 read data -#define FLD_I2C_RDATA 0xFFFFFFFF // RO [31:0] - -//***************************************************************************** -#define I2C1_STAT 0x180010 // I2C #1 status -#define FLD_I2C_XFER_IN_PROG 0x00000002 // RO [1] -#define FLD_I2C_RACK 0x00000001 // RO [0] - -//***************************************************************************** -// I2C #2 -//***************************************************************************** -#define I2C2_ADDR 0x190000 // I2C #2 address - -//***************************************************************************** -#define I2C2_WDATA 0x190004 // I2C #2 write data - -//***************************************************************************** -#define I2C2_CTRL 0x190008 // I2C #2 control - -//***************************************************************************** -#define I2C2_RDATA 0x19000C // I2C #2 read data - -//***************************************************************************** -#define I2C2_STAT 0x190010 // I2C #2 status - -//***************************************************************************** -// I2C #3 -//***************************************************************************** -#define I2C3_ADDR 0x1A0000 // I2C #3 address - -//***************************************************************************** -#define I2C3_WDATA 0x1A0004 // I2C #3 write data - -//***************************************************************************** -#define I2C3_CTRL 0x1A0008 // I2C #3 control - -//***************************************************************************** -#define I2C3_RDATA 0x1A000C // I2C #3 read data - -//***************************************************************************** -#define I2C3_STAT 0x1A0010 // I2C #3 status - -//***************************************************************************** -// UART -//***************************************************************************** -#define UART_CTL 0x1B0000 // UART Control Register -#define FLD_LOOP_BACK_EN (1 << 7) // RW field - default 0 -#define FLD_RX_TRG_SZ (3 << 2) // RW field - default 0 -#define FLD_RX_EN (1 << 1) // RW field - default 0 -#define FLD_TX_EN (1 << 0) // RW field - default 0 - -//***************************************************************************** -#define UART_BRD 0x1B0004 // UART Baud Rate Divisor -#define FLD_BRD 0x0000FFFF // RW field - default 0x197 - -//***************************************************************************** -#define UART_DBUF 0x1B0008 // UART Tx/Rx Data BuFFer -#define FLD_DB 0xFFFFFFFF // RW field - default 0 - -//***************************************************************************** -#define UART_ISR 0x1B000C // UART Interrupt Status -#define FLD_RXD_TIMEOUT_EN (1 << 7) // RW field - default 0 -#define FLD_FRM_ERR_EN (1 << 6) // RW field - default 0 -#define FLD_RXD_RDY_EN (1 << 5) // RW field - default 0 -#define FLD_TXD_EMPTY_EN (1 << 4) // RW field - default 0 -#define FLD_RXD_OVERFLOW (1 << 3) // RW field - default 0 -#define FLD_FRM_ERR (1 << 2) // RW field - default 0 -#define FLD_RXD_RDY (1 << 1) // RW field - default 0 -#define FLD_TXD_EMPTY (1 << 0) // RW field - default 0 - -//***************************************************************************** -#define UART_CNT 0x1B0010 // UART Tx/Rx FIFO Byte Count -#define FLD_TXD_CNT (0x1F << 8) // RW field - default 0 -#define FLD_RXD_CNT (0x1F << 0) // RW field - default 0 - -//***************************************************************************** -// Motion Detection +/* ***************************************************************************** */ +/* I2C #1 */ +/* ***************************************************************************** */ +#define I2C1_ADDR 0x180000 /* I2C #1 address */ +#define FLD_I2C_DADDR 0xfe000000 /* RW [31:25] I2C Device Address */ + /* RO [24] reserved */ +/* ***************************************************************************** */ +#define FLD_I2C_SADDR 0x00FFFFFF /* RW [23:0] I2C Sub-address */ + +/* ***************************************************************************** */ +#define I2C1_WDATA 0x180004 /* I2C #1 write data */ +#define FLD_I2C_WDATA 0xFFFFFFFF /* RW [31:0] */ + +/* ***************************************************************************** */ +#define I2C1_CTRL 0x180008 /* I2C #1 control */ +#define FLD_I2C_PERIOD 0xFF000000 /* RW [31:24] */ +#define FLD_I2C_SCL_IN 0x00200000 /* RW [21] */ +#define FLD_I2C_SDA_IN 0x00100000 /* RW [20] */ + /* RO [19:18] reserved */ +#define FLD_I2C_SCL_OUT 0x00020000 /* RW [17] */ +#define FLD_I2C_SDA_OUT 0x00010000 /* RW [16] */ + /* RO [15] reserved */ +#define FLD_I2C_DATA_LEN 0x00007000 /* RW [14:12] */ +#define FLD_I2C_SADDR_INC 0x00000800 /* RW [11] */ + /* RO [10:9] reserved */ +#define FLD_I2C_SADDR_LEN 0x00000300 /* RW [9:8] */ + /* RO [7:6] reserved */ +#define FLD_I2C_SOFT 0x00000020 /* RW [5] */ +#define FLD_I2C_NOSTOP 0x00000010 /* RW [4] */ +#define FLD_I2C_EXTEND 0x00000008 /* RW [3] */ +#define FLD_I2C_SYNC 0x00000004 /* RW [2] */ +#define FLD_I2C_READ_SA 0x00000002 /* RW [1] */ +#define FLD_I2C_READ_WRN 0x00000001 /* RW [0] */ + +/* ***************************************************************************** */ +#define I2C1_RDATA 0x18000C /* I2C #1 read data */ +#define FLD_I2C_RDATA 0xFFFFFFFF /* RO [31:0] */ + +/* ***************************************************************************** */ +#define I2C1_STAT 0x180010 /* I2C #1 status */ +#define FLD_I2C_XFER_IN_PROG 0x00000002 /* RO [1] */ +#define FLD_I2C_RACK 0x00000001 /* RO [0] */ + +/* ***************************************************************************** */ +/* I2C #2 */ +/* ***************************************************************************** */ +#define I2C2_ADDR 0x190000 /* I2C #2 address */ + +/* ***************************************************************************** */ +#define I2C2_WDATA 0x190004 /* I2C #2 write data */ + +/* ***************************************************************************** */ +#define I2C2_CTRL 0x190008 /* I2C #2 control */ + +/* ***************************************************************************** */ +#define I2C2_RDATA 0x19000C /* I2C #2 read data */ + +/* ***************************************************************************** */ +#define I2C2_STAT 0x190010 /* I2C #2 status */ + +/* ***************************************************************************** */ +/* I2C #3 */ +/* ***************************************************************************** */ +#define I2C3_ADDR 0x1A0000 /* I2C #3 address */ + +/* ***************************************************************************** */ +#define I2C3_WDATA 0x1A0004 /* I2C #3 write data */ + +/* ***************************************************************************** */ +#define I2C3_CTRL 0x1A0008 /* I2C #3 control */ + +/* ***************************************************************************** */ +#define I2C3_RDATA 0x1A000C /* I2C #3 read data */ + +/* ***************************************************************************** */ +#define I2C3_STAT 0x1A0010 /* I2C #3 status */ + +/* ***************************************************************************** */ +/* UART */ +/* ***************************************************************************** */ +#define UART_CTL 0x1B0000 /* UART Control Register */ +#define FLD_LOOP_BACK_EN (1 << 7) /* RW field - default 0 */ +#define FLD_RX_TRG_SZ (3 << 2) /* RW field - default 0 */ +#define FLD_RX_EN (1 << 1) /* RW field - default 0 */ +#define FLD_TX_EN (1 << 0) /* RW field - default 0 */ + +/* ***************************************************************************** */ +#define UART_BRD 0x1B0004 /* UART Baud Rate Divisor */ +#define FLD_BRD 0x0000FFFF /* RW field - default 0x197 */ + +/* ***************************************************************************** */ +#define UART_DBUF 0x1B0008 /* UART Tx/Rx Data BuFFer */ +#define FLD_DB 0xFFFFFFFF /* RW field - default 0 */ + +/* ***************************************************************************** */ +#define UART_ISR 0x1B000C /* UART Interrupt Status */ +#define FLD_RXD_TIMEOUT_EN (1 << 7) /* RW field - default 0 */ +#define FLD_FRM_ERR_EN (1 << 6) /* RW field - default 0 */ +#define FLD_RXD_RDY_EN (1 << 5) /* RW field - default 0 */ +#define FLD_TXD_EMPTY_EN (1 << 4) /* RW field - default 0 */ +#define FLD_RXD_OVERFLOW (1 << 3) /* RW field - default 0 */ +#define FLD_FRM_ERR (1 << 2) /* RW field - default 0 */ +#define FLD_RXD_RDY (1 << 1) /* RW field - default 0 */ +#define FLD_TXD_EMPTY (1 << 0) /* RW field - default 0 */ + +/* ***************************************************************************** */ +#define UART_CNT 0x1B0010 /* UART Tx/Rx FIFO Byte Count */ +#define FLD_TXD_CNT (0x1F << 8) /* RW field - default 0 */ +#define FLD_RXD_CNT (0x1F << 0) /* RW field - default 0 */ + +/* ***************************************************************************** */ +/* Motion Detection */ #define MD_CH0_GRID_BLOCK_YCNT 0x170014 #define MD_CH1_GRID_BLOCK_YCNT 0x170094 #define MD_CH2_GRID_BLOCK_YCNT 0x170114 @@ -1589,4 +1589,4 @@ #define PIXEL_ENGINE_VIP1 0 #define PIXEL_ENGINE_VIP2 1 -#endif //Athena_REGISTERS +#endif /* Athena_REGISTERS */ diff --git a/drivers/staging/cx25821/cx25821-sram.h b/drivers/staging/cx25821/cx25821-sram.h index bd677ee..5f05d15 100644 --- a/drivers/staging/cx25821/cx25821-sram.h +++ b/drivers/staging/cx25821/cx25821-sram.h @@ -23,34 +23,34 @@ #ifndef __ATHENA_SRAM_H__ #define __ATHENA_SRAM_H__ -//#define RX_SRAM_START_SIZE = 0; // Start of reserved SRAM -#define VID_CMDS_SIZE 80 // Video CMDS size in bytes -#define AUDIO_CMDS_SIZE 80 // AUDIO CMDS size in bytes -#define MBIF_CMDS_SIZE 80 // MBIF CMDS size in bytes +/* #define RX_SRAM_START_SIZE = 0; // Start of reserved SRAM */ +#define VID_CMDS_SIZE 80 /* Video CMDS size in bytes */ +#define AUDIO_CMDS_SIZE 80 /* AUDIO CMDS size in bytes */ +#define MBIF_CMDS_SIZE 80 /* MBIF CMDS size in bytes */ -//#define RX_SRAM_POOL_START_SIZE = 0; // Start of useable RX SRAM for buffers -#define VID_IQ_SIZE 64 // VID instruction queue size in bytes +/* #define RX_SRAM_POOL_START_SIZE = 0; // Start of useable RX SRAM for buffers */ +#define VID_IQ_SIZE 64 /* VID instruction queue size in bytes */ #define MBIF_IQ_SIZE 64 -#define AUDIO_IQ_SIZE 64 // AUD instruction queue size in bytes +#define AUDIO_IQ_SIZE 64 /* AUD instruction queue size in bytes */ -#define VID_CDT_SIZE 64 // VID cluster descriptor table size in bytes -#define MBIF_CDT_SIZE 64 // MBIF/HBI cluster descriptor table size in bytes -#define AUDIO_CDT_SIZE 48 // AUD cluster descriptor table size in bytes +#define VID_CDT_SIZE 64 /* VID cluster descriptor table size in bytes */ +#define MBIF_CDT_SIZE 64 /* MBIF/HBI cluster descriptor table size in bytes */ +#define AUDIO_CDT_SIZE 48 /* AUD cluster descriptor table size in bytes */ -//#define RX_SRAM_POOL_FREE_SIZE = 16; // Start of available RX SRAM -//#define RX_SRAM_END_SIZE = 0; // End of RX SRAM +/* #define RX_SRAM_POOL_FREE_SIZE = 16; // Start of available RX SRAM */ +/* #define RX_SRAM_END_SIZE = 0; // End of RX SRAM */ -//#define TX_SRAM_POOL_START_SIZE = 0; // Start of transmit pool SRAM -//#define MSI_DATA_SIZE = 64; // Reserved (MSI Data, RISC working stora +/* #define TX_SRAM_POOL_START_SIZE = 0; // Start of transmit pool SRAM */ +/* #define MSI_DATA_SIZE = 64; // Reserved (MSI Data, RISC working stora */ -#define VID_CLUSTER_SIZE 1440 // VID cluster data line -#define AUDIO_CLUSTER_SIZE 128 // AUDIO cluster data line -#define MBIF_CLUSTER_SIZE 1440 // MBIF/HBI cluster data line +#define VID_CLUSTER_SIZE 1440 /* VID cluster data line */ +#define AUDIO_CLUSTER_SIZE 128 /* AUDIO cluster data line */ +#define MBIF_CLUSTER_SIZE 1440 /* MBIF/HBI cluster data line */ -//#define TX_SRAM_POOL_FREE_SIZE = 704; // Start of available TX SRAM -//#define TX_SRAM_END_SIZE = 0; // End of TX SRAM +/* #define TX_SRAM_POOL_FREE_SIZE = 704; // Start of available TX SRAM */ +/* #define TX_SRAM_END_SIZE = 0; // End of TX SRAM */ -// Receive SRAM +/* Receive SRAM */ #define RX_SRAM_START 0x10000 #define VID_A_DOWN_CMDS 0x10000 #define VID_B_DOWN_CMDS 0x10050 @@ -78,9 +78,9 @@ #define AUD_E_UP_CMDS 0x10730 #define MBIF_A_DOWN_CMDS 0x10780 #define MBIF_B_DOWN_CMDS 0x107D0 -#define DMA_SCRATCH_PAD 0x10820 // Scratch pad area from 0x10820 to 0x10B40 +#define DMA_SCRATCH_PAD 0x10820 /* Scratch pad area from 0x10820 to 0x10B40 */ -//#define RX_SRAM_POOL_START = 0x105B0; +/* #define RX_SRAM_POOL_START = 0x105B0; */ #define VID_A_IQ 0x11000 #define VID_B_IQ 0x11040 @@ -118,7 +118,7 @@ #define MBIF_A_CDT 0x10C00 #define MBIF_B_CDT 0x10CC0 -// Cluster Buffer for RX +/* Cluster Buffer for RX */ #define VID_A_UP_CLUSTER_1 0x11400 #define VID_A_UP_CLUSTER_2 0x119A0 #define VID_A_UP_CLUSTER_3 0x11F40 @@ -178,9 +178,9 @@ #define RX_SRAM_POOL_FREE 0x1CE00 #define RX_SRAM_END 0x1D000 -// Free Receive SRAM 144 Bytes +/* Free Receive SRAM 144 Bytes */ -// Transmit SRAM +/* Transmit SRAM */ #define TX_SRAM_POOL_START 0x00000 #define VID_A_DOWN_CLUSTER_1 0x00040 diff --git a/drivers/staging/cx25821/cx25821-video-upstream-ch2.c b/drivers/staging/cx25821/cx25821-video-upstream-ch2.c index 343df66..d12dbb5 100644 --- a/drivers/staging/cx25821/cx25821-video-upstream-ch2.c +++ b/drivers/staging/cx25821/cx25821-video-upstream-ch2.c @@ -84,7 +84,7 @@ static __le32 *cx25821_risc_field_upstream_ch2(struct cx25821_dev *dev, { unsigned int line, i; struct sram_channel *sram_ch = - &dev->sram_channels[dev->_channel2_upstream_select]; + dev->channels[dev->_channel2_upstream_select].sram_channels; int dist_betwn_starts = bpl * 2; /* sync instruction */ @@ -110,8 +110,11 @@ static __le32 *cx25821_risc_field_upstream_ch2(struct cx25821_dev *dev, offset += dist_betwn_starts; } - // check if we need to enable the FIFO after the first 4 lines - // For the upstream video channel, the risc engine will enable the FIFO. + /* + check if we need to enable the FIFO after the first 4 lines + For the upstream video channel, the risc engine will enable + the FIFO. + */ if (fifo_enable && line == 3) { *(rp++) = RISC_WRITECR; *(rp++) = sram_ch->dma_ctl; @@ -130,7 +133,7 @@ int cx25821_risc_buffer_upstream_ch2(struct cx25821_dev *dev, { __le32 *rp; int fifo_enable = 0; - int singlefield_lines = lines >> 1; //get line count for single field + int singlefield_lines = lines >> 1; /*get line count for single field */ int odd_num_lines = singlefield_lines; int frame = 0; int frame_size = 0; @@ -174,7 +177,7 @@ int cx25821_risc_buffer_upstream_ch2(struct cx25821_dev *dev, fifo_enable = FIFO_DISABLE; - //Even field + /* Even field */ rp = cx25821_risc_field_upstream_ch2(dev, rp, dev-> _data_buf_phys_addr_ch2 + @@ -192,7 +195,10 @@ int cx25821_risc_buffer_upstream_ch2(struct cx25821_dev *dev, risc_phys_jump_addr = dev->_dma_phys_start_addr_ch2; } - // Loop to 2ndFrameRISC or to Start of Risc program & generate IRQ + /* + Loop to 2ndFrameRISC or to Start of + Risc program & generate IRQ + */ *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag); *(rp++) = cpu_to_le32(risc_phys_jump_addr); *(rp++) = cpu_to_le32(0); @@ -204,7 +210,7 @@ int cx25821_risc_buffer_upstream_ch2(struct cx25821_dev *dev, void cx25821_stop_upstream_video_ch2(struct cx25821_dev *dev) { struct sram_channel *sram_ch = - &dev->sram_channels[VID_UPSTREAM_SRAM_CHANNEL_J]; + dev->channels[VID_UPSTREAM_SRAM_CHANNEL_J].sram_channels; u32 tmp = 0; if (!dev->_is_running_ch2) { @@ -212,15 +218,15 @@ void cx25821_stop_upstream_video_ch2(struct cx25821_dev *dev) ("cx25821: No video file is currently running so return!\n"); return; } - //Disable RISC interrupts + /* Disable RISC interrupts */ tmp = cx_read(sram_ch->int_msk); cx_write(sram_ch->int_msk, tmp & ~_intr_msk); - //Turn OFF risc and fifo + /* Turn OFF risc and fifo */ tmp = cx_read(sram_ch->dma_ctl); cx_write(sram_ch->dma_ctl, tmp & ~(FLD_VID_FIFO_EN | FLD_VID_RISC_EN)); - //Clear data buffer memory + /* Clear data buffer memory */ if (dev->_data_buf_virt_addr_ch2) memset(dev->_data_buf_virt_addr_ch2, 0, dev->_data_buf_size_ch2); @@ -371,8 +377,8 @@ static void cx25821_vidups_handler_ch2(struct work_struct *work) } cx25821_get_frame_ch2(dev, - &dev->sram_channels[dev-> - _channel2_upstream_select]); + dev->channels[dev-> + _channel2_upstream_select].sram_channels); } int cx25821_openfile_ch2(struct cx25821_dev *dev, struct sram_channel *sram_ch) @@ -488,7 +494,7 @@ static int cx25821_upstream_buffer_prepare_ch2(struct cx25821_dev *dev, return -ENOMEM; } - //Iniitize at this address until n bytes to 0 + /* Iniitize at this address until n bytes to 0 */ memset(dev->_dma_virt_addr_ch2, 0, dev->_risc_size_ch2); if (dev->_data_buf_virt_addr_ch2 != NULL) { @@ -496,7 +502,7 @@ static int cx25821_upstream_buffer_prepare_ch2(struct cx25821_dev *dev, dev->_data_buf_virt_addr_ch2, dev->_data_buf_phys_addr_ch2); } - //For Video Data buffer allocation + /* For Video Data buffer allocation */ dev->_data_buf_virt_addr_ch2 = pci_alloc_consistent(dev->pci, dev->upstream_databuf_size_ch2, &data_dma_addr); @@ -509,14 +515,14 @@ static int cx25821_upstream_buffer_prepare_ch2(struct cx25821_dev *dev, return -ENOMEM; } - //Initialize at this address until n bytes to 0 + /* Initialize at this address until n bytes to 0 */ memset(dev->_data_buf_virt_addr_ch2, 0, dev->_data_buf_size_ch2); ret = cx25821_openfile_ch2(dev, sram_ch); if (ret < 0) return ret; - //Creating RISC programs + /* Creating RISC programs */ ret = cx25821_risc_buffer_upstream_ch2(dev, dev->pci, 0, bpl, dev->_lines_count_ch2); @@ -536,7 +542,7 @@ int cx25821_video_upstream_irq_ch2(struct cx25821_dev *dev, int chan_num, u32 status) { u32 int_msk_tmp; - struct sram_channel *channel = &dev->sram_channels[chan_num]; + struct sram_channel *channel = dev->channels[chan_num].sram_channels; int singlefield_lines = NTSC_FIELD_HEIGHT; int line_size_in_bytes = Y422_LINE_SZ; int odd_risc_prog_size = 0; @@ -544,10 +550,13 @@ int cx25821_video_upstream_irq_ch2(struct cx25821_dev *dev, int chan_num, __le32 *rp; if (status & FLD_VID_SRC_RISC1) { - // We should only process one program per call + /* We should only process one program per call */ u32 prog_cnt = cx_read(channel->gpcnt); - //Since we've identified our IRQ, clear our bits from the interrupt mask and interrupt status registers + /* + Since we've identified our IRQ, clear our bits from the + interrupt mask and interrupt status registers + */ int_msk_tmp = cx_read(channel->int_msk); cx_write(channel->int_msk, int_msk_tmp & ~_intr_msk); cx_write(channel->int_stat, _intr_msk); @@ -588,7 +597,7 @@ int cx25821_video_upstream_irq_ch2(struct cx25821_dev *dev, int chan_num, FIFO_DISABLE, ODD_FIELD); - // Jump to Even Risc program of 1st Frame + /* Jump to Even Risc program of 1st Frame */ *(rp++) = cpu_to_le32(RISC_JUMP); *(rp++) = cpu_to_le32(risc_phys_jump_addr); *(rp++) = cpu_to_le32(0); @@ -603,7 +612,7 @@ int cx25821_video_upstream_irq_ch2(struct cx25821_dev *dev, int chan_num, dev->_frame_count_ch2); return -1; } - //ElSE, set the interrupt mask register, re-enable irq. + /* ElSE, set the interrupt mask register, re-enable irq. */ int_msk_tmp = cx_read(channel->int_msk); cx_write(channel->int_msk, int_msk_tmp |= _intr_msk); @@ -623,12 +632,12 @@ static irqreturn_t cx25821_upstream_irq_ch2(int irq, void *dev_id) channel_num = VID_UPSTREAM_SRAM_CHANNEL_J; - sram_ch = &dev->sram_channels[channel_num]; + sram_ch = dev->channels[channel_num].sram_channels; msk_stat = cx_read(sram_ch->int_mstat); vid_status = cx_read(sram_ch->int_stat); - // Only deal with our interrupt + /* Only deal with our interrupt */ if (vid_status) { handled = cx25821_video_upstream_irq_ch2(dev, channel_num, @@ -658,7 +667,10 @@ static void cx25821_set_pixelengine_ch2(struct cx25821_dev *dev, value |= dev->_isNTSC_ch2 ? 0 : 0x10; cx_write(ch->vid_fmt_ctl, value); - // set number of active pixels in each line. Default is 720 pixels in both NTSC and PAL format + /* + set number of active pixels in each line. Default is 720 + pixels in both NTSC and PAL format + */ cx_write(ch->vid_active_ctl1, width); num_lines = (height / 2) & 0x3FF; @@ -670,7 +682,7 @@ static void cx25821_set_pixelengine_ch2(struct cx25821_dev *dev, value = (num_lines << 16) | odd_num_lines; - // set number of active lines in field 0 (top) and field 1 (bottom) + /* set number of active lines in field 0 (top) and field 1 (bottom) */ cx_write(ch->vid_active_ctl2, value); cx_write(ch->vid_cdt_size, VID_CDT_SIZE >> 3); @@ -682,21 +694,27 @@ int cx25821_start_video_dma_upstream_ch2(struct cx25821_dev *dev, u32 tmp = 0; int err = 0; - // 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for channel A-C + /* + 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface + for channel A-C + */ tmp = cx_read(VID_CH_MODE_SEL); cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); - // Set the physical start address of the RISC program in the initial program counter(IPC) member of the cmds. + /* + Set the physical start address of the RISC program in the initial + program counter(IPC) member of the cmds. + */ cx_write(sram_ch->cmds_start + 0, dev->_dma_phys_addr_ch2); - cx_write(sram_ch->cmds_start + 4, 0); /* Risc IPC High 64 bits 63-32 */ + cx_write(sram_ch->cmds_start + 4, 0); /* Risc IPC High 64 bits 63-32 */ /* reset counter */ cx_write(sram_ch->gpcnt_ctl, 3); - // Clear our bits from the interrupt status register. + /* Clear our bits from the interrupt status register. */ cx_write(sram_ch->int_stat, _intr_msk); - //Set the interrupt mask register, enable irq. + /* Set the interrupt mask register, enable irq. */ cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit)); tmp = cx_read(sram_ch->int_msk); cx_write(sram_ch->int_msk, tmp |= _intr_msk); @@ -709,7 +727,7 @@ int cx25821_start_video_dma_upstream_ch2(struct cx25821_dev *dev, dev->pci->irq); goto fail_irq; } - // Start the DMA engine + /* Start the DMA engine */ tmp = cx_read(sram_ch->dma_ctl); cx_set(sram_ch->dma_ctl, tmp | FLD_VID_RISC_EN); @@ -740,7 +758,7 @@ int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, int channel_select, } dev->_channel2_upstream_select = channel_select; - sram_ch = &dev->sram_channels[channel_select]; + sram_ch = dev->channels[channel_select].sram_channels; INIT_WORK(&dev->_irq_work_entry_ch2, cx25821_vidups_handler_ch2); dev->_irq_queues_ch2 = @@ -751,7 +769,10 @@ int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, int channel_select, ("cx25821: create_singlethread_workqueue() for Video FAILED!\n"); return -ENOMEM; } - // 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for channel A-C + /* + 656/VIP SRC Upstream Channel I & J and 7 - + Host Bus Interface for channel A-C + */ tmp = cx_read(VID_CH_MODE_SEL); cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); @@ -787,7 +808,7 @@ int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, int channel_select, str_length + 1); } - //Default if filename is empty string + /* Default if filename is empty string */ if (strcmp(dev->input_filename_ch2, "") == 0) { if (dev->_isNTSC_ch2) { dev->_filename_ch2 = @@ -812,7 +833,7 @@ int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, int channel_select, dev->upstream_riscbuf_size_ch2 = risc_buffer_size * 2; dev->upstream_databuf_size_ch2 = data_frame_size * 2; - //Allocating buffers and prepare RISC program + /* Allocating buffers and prepare RISC program */ retval = cx25821_upstream_buffer_prepare_ch2(dev, sram_ch, dev->_line_size_ch2); diff --git a/drivers/staging/cx25821/cx25821-video-upstream-ch2.h b/drivers/staging/cx25821/cx25821-video-upstream-ch2.h index 73feea1..6234063 100644 --- a/drivers/staging/cx25821/cx25821-video-upstream-ch2.h +++ b/drivers/staging/cx25821/cx25821-video-upstream-ch2.h @@ -37,7 +37,7 @@ #define RESET_STATUS -1 #define NUM_NO_OPS 5 -// PAL and NTSC line sizes and number of lines. +/* PAL and NTSC line sizes and number of lines. */ #define WIDTH_D1 720 #define NTSC_LINES_PER_FRAME 480 #define PAL_LINES_PER_FRAME 576 diff --git a/drivers/staging/cx25821/cx25821-video-upstream.c b/drivers/staging/cx25821/cx25821-video-upstream.c index 7a3dad9..756a820 100644 --- a/drivers/staging/cx25821/cx25821-video-upstream.c +++ b/drivers/staging/cx25821/cx25821-video-upstream.c @@ -134,7 +134,7 @@ static __le32 *cx25821_risc_field_upstream(struct cx25821_dev *dev, __le32 * rp, { unsigned int line, i; struct sram_channel *sram_ch = - &dev->sram_channels[dev->_channel_upstream_select]; + dev->channels[dev->_channel_upstream_select].sram_channels; int dist_betwn_starts = bpl * 2; /* sync instruction */ @@ -253,7 +253,7 @@ int cx25821_risc_buffer_upstream(struct cx25821_dev *dev, void cx25821_stop_upstream_video_ch1(struct cx25821_dev *dev) { struct sram_channel *sram_ch = - &dev->sram_channels[VID_UPSTREAM_SRAM_CHANNEL_I]; + dev->channels[VID_UPSTREAM_SRAM_CHANNEL_I].sram_channels; u32 tmp = 0; if (!dev->_is_running) { @@ -346,20 +346,23 @@ int cx25821_get_frame(struct cx25821_dev *dev, struct sram_channel *sram_ch) if (IS_ERR(myfile)) { const int open_errno = -PTR_ERR(myfile); - printk(KERN_ERR "%s(): ERROR opening file(%s) with errno = %d!\n", - __func__, dev->_filename, open_errno); + printk(KERN_ERR + "%s(): ERROR opening file(%s) with errno = %d!\n", + __func__, dev->_filename, open_errno); return PTR_ERR(myfile); } else { if (!(myfile->f_op)) { - printk(KERN_ERR "%s: File has no file operations registered!", - __func__); + printk(KERN_ERR + "%s: File has no file operations registered!", + __func__); filp_close(myfile, NULL); return -EIO; } if (!myfile->f_op->read) { - printk(KERN_ERR "%s: File has no READ operations registered!", - __func__); + printk(KERN_ERR + "%s: File has no READ operations registered!", + __func__); filp_close(myfile, NULL); return -EIO; } @@ -386,7 +389,8 @@ int cx25821_get_frame(struct cx25821_dev *dev, struct sram_channel *sram_ch) if (vfs_read_retval < line_size) { printk(KERN_INFO - "Done: exit %s() since no more bytes to read from Video file.\n", + "Done: exit %s() since no more bytes to \ + read from Video file.\n", __func__); break; } @@ -411,13 +415,15 @@ static void cx25821_vidups_handler(struct work_struct *work) container_of(work, struct cx25821_dev, _irq_work_entry); if (!dev) { - printk(KERN_ERR "ERROR %s(): since container_of(work_struct) FAILED!\n", - __func__); + printk(KERN_ERR + "ERROR %s(): since container_of(work_struct) FAILED!\n", + __func__); return; } cx25821_get_frame(dev, - &dev->sram_channels[dev->_channel_upstream_select]); + dev->channels[dev->_channel_upstream_select]. + sram_channels); } int cx25821_openfile(struct cx25821_dev *dev, struct sram_channel *sram_ch) @@ -437,20 +443,22 @@ int cx25821_openfile(struct cx25821_dev *dev, struct sram_channel *sram_ch) if (IS_ERR(myfile)) { const int open_errno = -PTR_ERR(myfile); - printk(KERN_ERR "%s(): ERROR opening file(%s) with errno = %d!\n", + printk(KERN_ERR "%s(): ERROR opening file(%s) with errno = %d!\n", __func__, dev->_filename, open_errno); return PTR_ERR(myfile); } else { if (!(myfile->f_op)) { - printk(KERN_ERR "%s: File has no file operations registered!", - __func__); + printk(KERN_ERR + "%s: File has no file operations registered!", + __func__); filp_close(myfile, NULL); return -EIO; } if (!myfile->f_op->read) { - printk - (KERN_ERR "%s: File has no READ operations registered! Returning.", + printk(KERN_ERR + "%s: File has no READ operations registered! \ + Returning.", __func__); filp_close(myfile, NULL); return -EIO; @@ -480,7 +488,8 @@ int cx25821_openfile(struct cx25821_dev *dev, struct sram_channel *sram_ch) if (vfs_read_retval < line_size) { printk(KERN_INFO - "Done: exit %s() since no more bytes to read from Video file.\n", + "Done: exit %s() since no more \ + bytes to read from Video file.\n", __func__); break; } @@ -526,7 +535,8 @@ int cx25821_upstream_buffer_prepare(struct cx25821_dev *dev, if (!dev->_dma_virt_addr) { printk - (KERN_ERR "cx25821: FAILED to allocate memory for Risc buffer! Returning.\n"); + (KERN_ERR "cx25821: FAILED to allocate memory for Risc \ + buffer! Returning.\n"); return -ENOMEM; } @@ -547,7 +557,8 @@ int cx25821_upstream_buffer_prepare(struct cx25821_dev *dev, if (!dev->_data_buf_virt_addr) { printk - (KERN_ERR "cx25821: FAILED to allocate memory for data buffer! Returning.\n"); + (KERN_ERR "cx25821: FAILED to allocate memory for data \ + buffer! Returning.\n"); return -ENOMEM; } @@ -578,7 +589,7 @@ int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num, u32 status) { u32 int_msk_tmp; - struct sram_channel *channel = &dev->sram_channels[chan_num]; + struct sram_channel *channel = dev->channels[chan_num].sram_channels; int singlefield_lines = NTSC_FIELD_HEIGHT; int line_size_in_bytes = Y422_LINE_SZ; int odd_risc_prog_size = 0; @@ -642,16 +653,16 @@ int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num, } else { if (status & FLD_VID_SRC_UF) printk - (KERN_ERR "%s: Video Received Underflow Error Interrupt!\n", - __func__); + (KERN_ERR "%s: Video Received Underflow Error \ + Interrupt!\n", __func__); if (status & FLD_VID_SRC_SYNC) - printk(KERN_ERR "%s: Video Received Sync Error Interrupt!\n", - __func__); + printk(KERN_ERR "%s: Video Received Sync Error \ + Interrupt!\n", __func__); if (status & FLD_VID_SRC_OPC_ERR) - printk(KERN_ERR "%s: Video Received OpCode Error Interrupt!\n", - __func__); + printk(KERN_ERR "%s: Video Received OpCode Error \ + Interrupt!\n", __func__); } if (dev->_file_status == END_OF_FILE) { @@ -679,7 +690,7 @@ static irqreturn_t cx25821_upstream_irq(int irq, void *dev_id) channel_num = VID_UPSTREAM_SRAM_CHANNEL_I; - sram_ch = &dev->sram_channels[channel_num]; + sram_ch = dev->channels[channel_num].sram_channels; msk_stat = cx_read(sram_ch->int_mstat); vid_status = cx_read(sram_ch->int_stat); @@ -800,14 +811,15 @@ int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, int channel_select, } dev->_channel_upstream_select = channel_select; - sram_ch = &dev->sram_channels[channel_select]; + sram_ch = dev->channels[channel_select].sram_channels; INIT_WORK(&dev->_irq_work_entry, cx25821_vidups_handler); dev->_irq_queues = create_singlethread_workqueue("cx25821_workqueue"); if (!dev->_irq_queues) { printk - (KERN_ERR "cx25821: create_singlethread_workqueue() for Video FAILED!\n"); + (KERN_ERR "cx25821: create_singlethread_workqueue() for \ + Video FAILED!\n"); return -ENOMEM; } /* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for diff --git a/drivers/staging/cx25821/cx25821-video-upstream.h b/drivers/staging/cx25821/cx25821-video-upstream.h index cc9f938..10dee5c 100644 --- a/drivers/staging/cx25821/cx25821-video-upstream.h +++ b/drivers/staging/cx25821/cx25821-video-upstream.h @@ -38,7 +38,7 @@ #define RESET_STATUS -1 #define NUM_NO_OPS 5 -// PAL and NTSC line sizes and number of lines. +/* PAL and NTSC line sizes and number of lines. */ #define WIDTH_D1 720 #define NTSC_LINES_PER_FRAME 480 #define PAL_LINES_PER_FRAME 576 diff --git a/drivers/staging/cx25821/cx25821-video.c b/drivers/staging/cx25821/cx25821-video.c index 791212c..1d5e879 100644 --- a/drivers/staging/cx25821/cx25821-video.c +++ b/drivers/staging/cx25821/cx25821-video.c @@ -4,6 +4,9 @@ * Copyright (C) 2009 Conexant Systems Inc. * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * Parts adapted/taken from Eduardo Moscoso Rubino + * Copyright (C) 2009 Eduardo Moscoso Rubino <moscoso@TopoLogica.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 @@ -24,7 +27,7 @@ #include "cx25821-video.h" MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); -MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>"); +MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>"); MODULE_LICENSE("GPL"); static unsigned int video_nr[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET }; @@ -48,7 +51,10 @@ unsigned int vid_limit = 16; module_param(vid_limit, int, 0644); MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); -static void init_controls(struct cx25821_dev *dev, int chan_num); +static void cx25821_init_controls(struct cx25821_dev *dev, int chan_num); + +static const struct v4l2_file_operations video_fops; +static const struct v4l2_ioctl_ops video_ioctl_ops; #define FORMAT_FLAGS_PACKED 0x01 @@ -211,7 +217,7 @@ static int cx25821_ctrl_query(struct v4l2_queryctrl *qctrl) } */ -// resource management +/* resource management */ int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned int bit) { dprintk(1, "%s()\n", __func__); @@ -221,14 +227,14 @@ int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned int /* is it free? */ mutex_lock(&dev->lock); - if (dev->resources & bit) { + if (dev->channels[fh->channel_id].resources & bit) { /* no, someone else uses it */ mutex_unlock(&dev->lock); return 0; } /* it's free, grab it */ fh->resources |= bit; - dev->resources |= bit; + dev->channels[fh->channel_id].resources |= bit; dprintk(1, "res: get %d\n", bit); mutex_unlock(&dev->lock); return 1; @@ -239,9 +245,9 @@ int cx25821_res_check(struct cx25821_fh *fh, unsigned int bit) return fh->resources & bit; } -int cx25821_res_locked(struct cx25821_dev *dev, unsigned int bit) +int cx25821_res_locked(struct cx25821_fh *fh, unsigned int bit) { - return dev->resources & bit; + return fh->dev->channels[fh->channel_id].resources & bit; } void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned int bits) @@ -251,7 +257,7 @@ void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned i mutex_lock(&dev->lock); fh->resources &= ~bits; - dev->resources &= ~bits; + dev->channels[fh->channel_id].resources &= ~bits; dprintk(1, "res: put %d\n", bits); mutex_unlock(&dev->lock); } @@ -358,11 +364,11 @@ void cx25821_vid_timeout(unsigned long data) struct cx25821_data *timeout_data = (struct cx25821_data *)data; struct cx25821_dev *dev = timeout_data->dev; struct sram_channel *channel = timeout_data->channel; - struct cx25821_dmaqueue *q = &dev->vidq[channel->i]; + struct cx25821_dmaqueue *q = &dev->channels[channel->i].vidq; struct cx25821_buffer *buf; unsigned long flags; - //cx25821_sram_channel_dump(dev, channel); + /* cx25821_sram_channel_dump(dev, channel); */ cx_clear(channel->dma_ctl, 0x11); spin_lock_irqsave(&dev->slock, flags); @@ -384,7 +390,7 @@ int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status) u32 count = 0; int handled = 0; u32 mask; - struct sram_channel *channel = &dev->sram_channels[chan_num]; + struct sram_channel *channel = dev->channels[chan_num].sram_channels; mask = cx_read(channel->int_msk); if (0 == (status & mask)) @@ -404,7 +410,8 @@ int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status) if (status & FLD_VID_DST_RISC1) { spin_lock(&dev->slock); count = cx_read(channel->gpcnt); - cx25821_video_wakeup(dev, &dev->vidq[channel->i], count); + cx25821_video_wakeup(dev, + &dev->channels[channel->i].vidq, count); spin_unlock(&dev->slock); handled++; } @@ -413,8 +420,9 @@ int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status) if (status & 0x10) { dprintk(2, "stopper video\n"); spin_lock(&dev->slock); - cx25821_restart_video_queue(dev, &dev->vidq[channel->i], - channel); + cx25821_restart_video_queue(dev, + &dev->channels[channel->i].vidq, + channel); spin_unlock(&dev->slock); handled++; } @@ -437,72 +445,95 @@ void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num) { cx_clear(PCI_INT_MSK, 1); - if (dev->video_dev[chan_num]) { - if (video_is_registered(dev->video_dev[chan_num])) - video_unregister_device(dev->video_dev[chan_num]); + if (dev->channels[chan_num].video_dev) { + if (video_is_registered(dev->channels[chan_num].video_dev)) + video_unregister_device( + dev->channels[chan_num].video_dev); else - video_device_release(dev->video_dev[chan_num]); + video_device_release( + dev->channels[chan_num].video_dev); - dev->video_dev[chan_num] = NULL; + dev->channels[chan_num].video_dev = NULL; - btcx_riscmem_free(dev->pci, &dev->vidq[chan_num].stopper); + btcx_riscmem_free(dev->pci, + &dev->channels[chan_num].vidq.stopper); printk(KERN_WARNING "device %d released!\n", chan_num); } } -int cx25821_video_register(struct cx25821_dev *dev, int chan_num, - struct video_device *video_template) +int cx25821_video_register(struct cx25821_dev *dev) { int err; + int i; + + struct video_device cx25821_video_device = { + .name = "cx25821-video", + .fops = &video_fops, + .minor = -1, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = CX25821_NORMS, + .current_norm = V4L2_STD_NTSC_M, + }; spin_lock_init(&dev->slock); - //printk(KERN_WARNING "Channel %d\n", chan_num); + for (i = 0; i < MAX_VID_CHANNEL_NUM - 1; ++i) { + cx25821_init_controls(dev, i); -#ifdef TUNER_FLAG - dev->tvnorm = video_template->current_norm; -#endif + cx25821_risc_stopper(dev->pci, + &dev->channels[i].vidq.stopper, + dev->channels[i].sram_channels->dma_ctl, + 0x11, 0); + + dev->channels[i].sram_channels = &cx25821_sram_channels[i]; + dev->channels[i].video_dev = NULL; + dev->channels[i].resources = 0; + + cx_write(dev->channels[i].sram_channels->int_stat, + 0xffffffff); + + INIT_LIST_HEAD(&dev->channels[i].vidq.active); + INIT_LIST_HEAD(&dev->channels[i].vidq.queued); + + dev->channels[i].timeout_data.dev = dev; + dev->channels[i].timeout_data.channel = + &cx25821_sram_channels[i]; + dev->channels[i].vidq.timeout.function = + cx25821_vid_timeout; + dev->channels[i].vidq.timeout.data = + (unsigned long)&dev->channels[i].timeout_data; + init_timer(&dev->channels[i].vidq.timeout); + + /* register v4l devices */ + dev->channels[i].video_dev = cx25821_vdev_init(dev, + dev->pci, &cx25821_video_device, "video"); + + err = video_register_device(dev->channels[i].video_dev, + VFL_TYPE_GRABBER, video_nr[dev->nr]); + + if (err < 0) + goto fail_unreg; - /* init video dma queues */ - dev->timeout_data[chan_num].dev = dev; - dev->timeout_data[chan_num].channel = &dev->sram_channels[chan_num]; - INIT_LIST_HEAD(&dev->vidq[chan_num].active); - INIT_LIST_HEAD(&dev->vidq[chan_num].queued); - dev->vidq[chan_num].timeout.function = cx25821_vid_timeout; - dev->vidq[chan_num].timeout.data = - (unsigned long)&dev->timeout_data[chan_num]; - init_timer(&dev->vidq[chan_num].timeout); - cx25821_risc_stopper(dev->pci, &dev->vidq[chan_num].stopper, - dev->sram_channels[chan_num].dma_ctl, 0x11, 0); - - /* register v4l devices */ - dev->video_dev[chan_num] = - cx25821_vdev_init(dev, dev->pci, video_template, "video"); - err = - video_register_device(dev->video_dev[chan_num], VFL_TYPE_GRABBER, - video_nr[dev->nr]); - - if (err < 0) { - goto fail_unreg; } - //set PCI interrupt + + /* set PCI interrupt */ cx_set(PCI_INT_MSK, 0xff); /* initial device configuration */ mutex_lock(&dev->lock); #ifdef TUNER_FLAG + dev->tvnorm = cx25821_video_device.current_norm; cx25821_set_tvnorm(dev, dev->tvnorm); #endif mutex_unlock(&dev->lock); - init_controls(dev, chan_num); - return 0; + return 0; - fail_unreg: - cx25821_video_unregister(dev, chan_num); +fail_unreg: + cx25821_video_unregister(dev, i); return err; } @@ -533,7 +564,7 @@ int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, u32 line0_offset, line1_offset; struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); int bpl_local = LINE_SIZE_D1; - int channel_opened = 0; + int channel_opened = fh->channel_id; BUG_ON(NULL == fh->fmt); if (fh->width < 48 || fh->width > 720 || @@ -572,26 +603,29 @@ int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, channel_opened = (channel_opened < 0 || channel_opened > 7) ? 7 : channel_opened; - if (dev->pixel_formats[channel_opened] == PIXEL_FRMT_411) + if (dev->channels[channel_opened] + .pixel_formats == PIXEL_FRMT_411) buf->bpl = (buf->fmt->depth * buf->vb.width) >> 3; else buf->bpl = (buf->fmt->depth >> 3) * (buf->vb.width); - if (dev->pixel_formats[channel_opened] == PIXEL_FRMT_411) { + if (dev->channels[channel_opened] + .pixel_formats == PIXEL_FRMT_411) { bpl_local = buf->bpl; } else { - bpl_local = buf->bpl; //Default + bpl_local = buf->bpl; /* Default */ if (channel_opened >= 0 && channel_opened <= 7) { - if (dev->use_cif_resolution[channel_opened]) { + if (dev->channels[channel_opened] + .use_cif_resolution) { if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) bpl_local = 352 << 1; else bpl_local = - dev-> - cif_width[channel_opened] << - 1; + dev->channels[channel_opened]. + cif_width << + 1; } } } @@ -685,6 +719,383 @@ int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma) return videobuf_mmap_mapper(get_queue(fh), vma); } + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct cx25821_buffer *buf = + container_of(vb, struct cx25821_buffer, vb); + struct cx25821_buffer *prev; + struct cx25821_fh *fh = vq->priv_data; + struct cx25821_dev *dev = fh->dev; + struct cx25821_dmaqueue *q = &dev->channels[fh->channel_id].vidq; + + /* add jump to stopper */ + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); + buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ + + dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); + + if (!list_empty(&q->queued)) { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, + buf->vb.i); + + } else if (list_empty(&q->active)) { + list_add_tail(&buf->vb.queue, &q->active); + cx25821_start_video_dma(dev, q, buf, + dev->channels[fh->channel_id]. + sram_channels); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); + dprintk(2, + "[%p/%d] buffer_queue - first active, buf cnt = %d, \ + q->count = %d\n", + buf, buf->vb.i, buf->count, q->count); + } else { + prev = + list_entry(q->active.prev, struct cx25821_buffer, vb.queue); + if (prev->vb.width == buf->vb.width + && prev->vb.height == buf->vb.height + && prev->fmt == buf->fmt) { + list_add_tail(&buf->vb.queue, &q->active); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + + /* 64 bit bits 63-32 */ + prev->risc.jmp[2] = cpu_to_le32(0); + dprintk(2, + "[%p/%d] buffer_queue - append to active, \ + buf->count=%d\n", + buf, buf->vb.i, buf->count); + + } else { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, + buf->vb.i); + } + } + + if (list_empty(&q->active)) + dprintk(2, "active queue empty!\n"); +} + +static struct videobuf_queue_ops cx25821_video_qops = { + .buf_setup = cx25821_buffer_setup, + .buf_prepare = cx25821_buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = cx25821_buffer_release, +}; + +static int video_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct cx25821_dev *h, *dev = video_drvdata(file); + struct cx25821_fh *fh; + struct list_head *list; + int minor = video_devdata(file)->minor; + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + u32 pix_format; + int ch_id = 0; + int i; + + dprintk(1, "open dev=%s type=%s\n", + video_device_node_name(vdev), + v4l2_type_names[type]); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) + return -ENOMEM; + + lock_kernel(); + + list_for_each(list, &cx25821_devlist) + { + h = list_entry(list, struct cx25821_dev, devlist); + + for (i = 0; i < MAX_VID_CHANNEL_NUM; i++) { + if (h->channels[i].video_dev && + h->channels[i].video_dev->minor == minor) { + dev = h; + ch_id = i; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } + } + } + + if (NULL == dev) { + unlock_kernel(); + return -ENODEV; + } + + file->private_data = fh; + fh->dev = dev; + fh->type = type; + fh->width = 720; + fh->channel_id = ch_id; + + if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) + fh->height = 576; + else + fh->height = 480; + + dev->channel_opened = fh->channel_id; + pix_format = + (dev->channels[ch_id].pixel_formats == + PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV; + fh->fmt = format_by_fourcc(pix_format); + + v4l2_prio_open(&dev->channels[ch_id].prio, &fh->prio); + + videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct cx25821_buffer), fh); + + dprintk(1, "post videobuf_queue_init()\n"); + unlock_kernel(); + + return 0; +} + +static ssize_t video_read(struct file *file, char __user * data, size_t count, + loff_t *ppos) +{ + struct cx25821_fh *fh = file->private_data; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (cx25821_res_locked(fh, RESOURCE_VIDEO0)) + return -EBUSY; + + return videobuf_read_one(&fh->vidq, data, count, ppos, + file->f_flags & O_NONBLOCK); + + default: + BUG(); + return 0; + } +} + +static unsigned int video_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_buffer *buf; + + if (cx25821_res_check(fh, RESOURCE_VIDEO0)) { + /* streaming capture */ + if (list_empty(&fh->vidq.stream)) + return POLLERR; + buf = list_entry(fh->vidq.stream.next, + struct cx25821_buffer, vb.stream); + } else { + /* read() capture */ + buf = (struct cx25821_buffer *)fh->vidq.read_buf; + if (NULL == buf) + return POLLERR; + } + + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) { + if (buf->vb.state == VIDEOBUF_DONE) { + struct cx25821_dev *dev = fh->dev; + + if (dev && dev->channels[fh->channel_id] + .use_cif_resolution) { + u8 cam_id = *((char *)buf->vb.baddr + 3); + memcpy((char *)buf->vb.baddr, + (char *)buf->vb.baddr + (fh->width * 2), + (fh->width * 2)); + *((char *)buf->vb.baddr + 3) = cam_id; + } + } + + return POLLIN | POLLRDNORM; + } + + return 0; +} + +static int video_release(struct file *file) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + + /* stop the risc engine and fifo */ + cx_write(channel0->dma_ctl, 0); /* FIFO and RISC disable */ + + /* stop video capture */ + if (cx25821_res_check(fh, RESOURCE_VIDEO0)) { + videobuf_queue_cancel(&fh->vidq); + cx25821_res_free(dev, fh, RESOURCE_VIDEO0); + } + + if (fh->vidq.read_buf) { + cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); + kfree(fh->vidq.read_buf); + } + + videobuf_mmap_free(&fh->vidq); + + v4l2_prio_close(&dev->channels[fh->channel_id].prio, fh->prio); + file->private_data = NULL; + kfree(fh); + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + + if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) + return -EINVAL; + + if (unlikely(i != fh->type)) + return -EINVAL; + + if (unlikely(!cx25821_res_get(dev, fh, + cx25821_get_resource(fh, RESOURCE_VIDEO0)))) + return -EBUSY; + + return videobuf_streamon(get_queue(fh)); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + int err, res; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + res = cx25821_get_resource(fh, RESOURCE_VIDEO0); + err = videobuf_streamoff(get_queue(fh)); + if (err < 0) + return err; + cx25821_res_free(dev, fh, res); + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + int pix_format = PIXEL_FRMT_422; + + if (fh) { + err = v4l2_prio_check(&dev->channels[fh->channel_id] + .prio, fh->prio); + if (0 != err) + return err; + } + + dprintk(2, "%s()\n", __func__); + err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); + + if (0 != err) + return err; + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->vidq.field = f->fmt.pix.field; + + /* check if width and height is valid based on set standard */ + if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm)) + fh->width = f->fmt.pix.width; + + if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm)) + fh->height = f->fmt.pix.height; + + if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) + pix_format = PIXEL_FRMT_411; + else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) + pix_format = PIXEL_FRMT_422; + else + return -EINVAL; + + cx25821_set_pixel_format(dev, SRAM_CH00, pix_format); + + /* check if cif resolution */ + if (fh->width == 320 || fh->width == 352) + dev->channels[fh->channel_id].use_cif_resolution = 1; + else + dev->channels[fh->channel_id].use_cif_resolution = 0; + + dev->channels[fh->channel_id].cif_width = fh->width; + medusa_set_resolution(dev, fh->width, SRAM_CH00); + + dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, + fh->height, fh->vidq.field); + cx25821_call_all(dev, video, s_fmt, f); + + return 0; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int ret_val = 0; + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + + ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); + + p->sequence = dev->channels[fh->channel_id].vidq.count; + + return ret_val; +} + +static int vidioc_log_status(struct file *file, void *priv) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + struct cx25821_fh *fh = priv; + char name[32 + 2]; + + struct sram_channel *sram_ch = dev->channels[fh->channel_id] + .sram_channels; + u32 tmp = 0; + + snprintf(name, sizeof(name), "%s/2", dev->name); + printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", + dev->name); + cx25821_call_all(dev, core, log_status); + tmp = cx_read(sram_ch->dma_ctl); + printk(KERN_INFO "Video input 0 is %s\n", + (tmp & 0x11) ? "streaming" : "stopped"); + printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", + dev->name); + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->channels[fh->channel_id] + .prio, fh->prio); + if (0 != err) + return err; + } + + return cx25821_set_control(dev, ctl, fh->channel_id); +} + /* VIDEO IOCTLS */ int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { @@ -822,8 +1233,9 @@ int cx25821_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) int cx25821_vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p) { struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev; + struct cx25821_fh *fh = f; - *p = v4l2_prio_max(&dev->prio); + *p = v4l2_prio_max(&dev->channels[fh->channel_id].prio); return 0; } @@ -833,7 +1245,8 @@ int cx25821_vidioc_s_priority(struct file *file, void *f, enum v4l2_priority pri struct cx25821_fh *fh = f; struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev; - return v4l2_prio_change(&dev->prio, &fh->prio, prio); + return v4l2_prio_change(&dev->channels[fh->channel_id] + .prio, &fh->prio, prio); } #ifdef TUNER_FLAG @@ -846,7 +1259,8 @@ int cx25821_vidioc_s_std(struct file *file, void *priv, v4l2_std_id * tvnorms) dprintk(1, "%s()\n", __func__); if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); + err = v4l2_prio_check(&dev->channels[fh->channel_id] + .prio, fh->prio); if (0 != err) return err; } @@ -916,7 +1330,8 @@ int cx25821_vidioc_s_input(struct file *file, void *priv, unsigned int i) dprintk(1, "%s(%d)\n", __func__, i); if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); + err = v4l2_prio_check(&dev->channels[fh->channel_id] + .prio, fh->prio); if (0 != err) return err; } @@ -967,9 +1382,14 @@ int cx25821_vidioc_s_frequency(struct file *file, void *priv, struct v4l2_freque int err; if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); + dev = fh->dev; + err = v4l2_prio_check(&dev->channels[fh->channel_id] + .prio, fh->prio); if (0 != err) return err; + } else { + printk(KERN_ERR "Invalid fh pointer!\n"); + return -EINVAL; } return cx25821_set_freq(dev, f); @@ -1031,7 +1451,8 @@ int cx25821_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) int err; if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); + err = v4l2_prio_check(&dev->channels[fh->channel_id] + .prio, fh->prio); if (0 != err) return err; } @@ -1046,7 +1467,7 @@ int cx25821_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) } #endif -// ****************************************************************************************** +/*****************************************************************************/ static const struct v4l2_queryctrl no_ctl = { .name = "42", .flags = V4L2_CTRL_FLAG_DISABLED, @@ -1129,6 +1550,7 @@ static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id) int cx25821_vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctl) { struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + struct cx25821_fh *fh = priv; const struct v4l2_queryctrl *ctrl; @@ -1138,16 +1560,16 @@ int cx25821_vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ct return -EINVAL; switch (ctl->id) { case V4L2_CID_BRIGHTNESS: - ctl->value = dev->ctl_bright; + ctl->value = dev->channels[fh->channel_id].ctl_bright; break; case V4L2_CID_HUE: - ctl->value = dev->ctl_hue; + ctl->value = dev->channels[fh->channel_id].ctl_hue; break; case V4L2_CID_CONTRAST: - ctl->value = dev->ctl_contrast; + ctl->value = dev->channels[fh->channel_id].ctl_contrast; break; case V4L2_CID_SATURATION: - ctl->value = dev->ctl_saturation; + ctl->value = dev->channels[fh->channel_id].ctl_saturation; break; } return 0; @@ -1181,19 +1603,19 @@ int cx25821_set_control(struct cx25821_dev *dev, switch (ctl->id) { case V4L2_CID_BRIGHTNESS: - dev->ctl_bright = ctl->value; + dev->channels[chan_num].ctl_bright = ctl->value; medusa_set_brightness(dev, ctl->value, chan_num); break; case V4L2_CID_HUE: - dev->ctl_hue = ctl->value; + dev->channels[chan_num].ctl_hue = ctl->value; medusa_set_hue(dev, ctl->value, chan_num); break; case V4L2_CID_CONTRAST: - dev->ctl_contrast = ctl->value; + dev->channels[chan_num].ctl_contrast = ctl->value; medusa_set_contrast(dev, ctl->value, chan_num); break; case V4L2_CID_SATURATION: - dev->ctl_saturation = ctl->value; + dev->channels[chan_num].ctl_saturation = ctl->value; medusa_set_saturation(dev, ctl->value, chan_num); break; } @@ -1203,7 +1625,7 @@ int cx25821_set_control(struct cx25821_dev *dev, return err; } -static void init_controls(struct cx25821_dev *dev, int chan_num) +static void cx25821_init_controls(struct cx25821_dev *dev, int chan_num) { struct v4l2_control ctrl; int i; @@ -1239,23 +1661,24 @@ int cx25821_vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop) int err; if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); + err = v4l2_prio_check(&dev->channels[fh->channel_id]. + prio, fh->prio); if (0 != err) return err; } - // cx25821_vidioc_s_crop not supported + /* cx25821_vidioc_s_crop not supported */ return -EINVAL; } int cx25821_vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) { - // cx25821_vidioc_g_crop not supported + /* cx25821_vidioc_g_crop not supported */ return -EINVAL; } int cx25821_vidioc_querystd(struct file *file, void *priv, v4l2_std_id * norm) { - // medusa does not support video standard sensing of current input + /* medusa does not support video standard sensing of current input */ *norm = CX25821_NORMS; return 0; @@ -1297,3 +1720,325 @@ int cx25821_is_valid_height(u32 height, v4l2_std_id tvnorm) return 0; } + +static long video_ioctl_upstream9(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + int command = 0; + struct upstream_user_struct *data_from_user; + + data_from_user = (struct upstream_user_struct *)arg; + + if (!data_from_user) { + printk + ("cx25821 in %s(): Upstream data is INVALID. Returning.\n", + __func__); + return 0; + } + + command = data_from_user->command; + + if (command != UPSTREAM_START_VIDEO && + command != UPSTREAM_STOP_VIDEO) + return 0; + + dev->input_filename = data_from_user->input_filename; + dev->input_audiofilename = data_from_user->input_filename; + dev->vid_stdname = data_from_user->vid_stdname; + dev->pixel_format = data_from_user->pixel_format; + dev->channel_select = data_from_user->channel_select; + dev->command = data_from_user->command; + + switch (command) { + case UPSTREAM_START_VIDEO: + cx25821_start_upstream_video_ch1(dev, data_from_user); + break; + + case UPSTREAM_STOP_VIDEO: + cx25821_stop_upstream_video_ch1(dev); + break; + } + + return 0; +} + +static long video_ioctl_upstream10(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + int command = 0; + struct upstream_user_struct *data_from_user; + + data_from_user = (struct upstream_user_struct *)arg; + + if (!data_from_user) { + printk + ("cx25821 in %s(): Upstream data is INVALID. Returning.\n", + __func__); + return 0; + } + + command = data_from_user->command; + + if (command != UPSTREAM_START_VIDEO && + command != UPSTREAM_STOP_VIDEO) + return 0; + + dev->input_filename_ch2 = data_from_user->input_filename; + dev->input_audiofilename = data_from_user->input_filename; + dev->vid_stdname_ch2 = data_from_user->vid_stdname; + dev->pixel_format_ch2 = data_from_user->pixel_format; + dev->channel_select_ch2 = data_from_user->channel_select; + dev->command_ch2 = data_from_user->command; + + switch (command) { + case UPSTREAM_START_VIDEO: + cx25821_start_upstream_video_ch2(dev, data_from_user); + break; + + case UPSTREAM_STOP_VIDEO: + cx25821_stop_upstream_video_ch2(dev); + break; + } + + return 0; +} + +static long video_ioctl_upstream11(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + int command = 0; + struct upstream_user_struct *data_from_user; + + data_from_user = (struct upstream_user_struct *)arg; + + if (!data_from_user) { + printk + ("cx25821 in %s(): Upstream data is INVALID. Returning.\n", + __func__); + return 0; + } + + command = data_from_user->command; + + if (command != UPSTREAM_START_AUDIO && + command != UPSTREAM_STOP_AUDIO) + return 0; + + dev->input_filename = data_from_user->input_filename; + dev->input_audiofilename = data_from_user->input_filename; + dev->vid_stdname = data_from_user->vid_stdname; + dev->pixel_format = data_from_user->pixel_format; + dev->channel_select = data_from_user->channel_select; + dev->command = data_from_user->command; + + switch (command) { + case UPSTREAM_START_AUDIO: + cx25821_start_upstream_audio(dev, data_from_user); + break; + + case UPSTREAM_STOP_AUDIO: + cx25821_stop_upstream_audio(dev); + break; + } + + return 0; +} + +static long video_ioctl_set(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + struct downstream_user_struct *data_from_user; + int command; + int width = 720; + int selected_channel = 0, pix_format = 0, i = 0; + int cif_enable = 0, cif_width = 0; + u32 value = 0; + + data_from_user = (struct downstream_user_struct *)arg; + + if (!data_from_user) { + printk( + "cx25821 in %s(): User data is INVALID. Returning.\n", + __func__); + return 0; + } + + command = data_from_user->command; + + if (command != SET_VIDEO_STD && command != SET_PIXEL_FORMAT + && command != ENABLE_CIF_RESOLUTION && command != REG_READ + && command != REG_WRITE && command != MEDUSA_READ + && command != MEDUSA_WRITE) { + return 0; + } + + switch (command) { + case SET_VIDEO_STD: + dev->tvnorm = + !strcmp(data_from_user->vid_stdname, + "PAL") ? V4L2_STD_PAL_BG : V4L2_STD_NTSC_M; + medusa_set_videostandard(dev); + break; + + case SET_PIXEL_FORMAT: + selected_channel = data_from_user->decoder_select; + pix_format = data_from_user->pixel_format; + + if (!(selected_channel <= 7 && selected_channel >= 0)) { + selected_channel -= 4; + selected_channel = selected_channel % 8; + } + + if (selected_channel >= 0) + cx25821_set_pixel_format(dev, selected_channel, + pix_format); + + break; + + case ENABLE_CIF_RESOLUTION: + selected_channel = data_from_user->decoder_select; + cif_enable = data_from_user->cif_resolution_enable; + cif_width = data_from_user->cif_width; + + if (cif_enable) { + if (dev->tvnorm & V4L2_STD_PAL_BG + || dev->tvnorm & V4L2_STD_PAL_DK) + width = 352; + else + width = (cif_width == 320 + || cif_width == 352) ? cif_width : 320; + } + + if (!(selected_channel <= 7 && selected_channel >= 0)) { + selected_channel -= 4; + selected_channel = selected_channel % 8; + } + + if (selected_channel <= 7 && selected_channel >= 0) { + dev->channels[selected_channel]. + use_cif_resolution = cif_enable; + dev->channels[selected_channel].cif_width = width; + } else { + for (i = 0; i < VID_CHANNEL_NUM; i++) { + dev->channels[i].use_cif_resolution = + cif_enable; + dev->channels[i].cif_width = width; + } + } + + medusa_set_resolution(dev, width, selected_channel); + break; + case REG_READ: + data_from_user->reg_data = cx_read(data_from_user->reg_address); + break; + case REG_WRITE: + cx_write(data_from_user->reg_address, data_from_user->reg_data); + break; + case MEDUSA_READ: + value = + cx25821_i2c_read(&dev->i2c_bus[0], + (u16) data_from_user->reg_address, + &data_from_user->reg_data); + break; + case MEDUSA_WRITE: + cx25821_i2c_write(&dev->i2c_bus[0], + (u16) data_from_user->reg_address, + data_from_user->reg_data); + break; + } + + return 0; +} + +static long cx25821_video_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + struct cx25821_fh *fh = file->private_data; + + /* check to see if it's the video upstream */ + if (fh->channel_id == SRAM_CH09) { + ret = video_ioctl_upstream9(file, cmd, arg); + return ret; + } else if (fh->channel_id == SRAM_CH10) { + ret = video_ioctl_upstream10(file, cmd, arg); + return ret; + } else if (fh->channel_id == SRAM_CH11) { + ret = video_ioctl_upstream11(file, cmd, arg); + ret = video_ioctl_set(file, cmd, arg); + return ret; + } + + return video_ioctl2(file, cmd, arg); +} + +/* exported stuff */ +static const struct v4l2_file_operations video_fops = { + .owner = THIS_MODULE, + .open = video_open, + .release = video_release, + .read = video_read, + .poll = video_poll, + .mmap = cx25821_video_mmap, + .ioctl = cx25821_video_ioctl, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = cx25821_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_reqbufs = cx25821_vidioc_reqbufs, + .vidioc_querybuf = cx25821_vidioc_querybuf, + .vidioc_qbuf = cx25821_vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, +#ifdef TUNER_FLAG + .vidioc_s_std = cx25821_vidioc_s_std, + .vidioc_querystd = cx25821_vidioc_querystd, +#endif + .vidioc_cropcap = cx25821_vidioc_cropcap, + .vidioc_s_crop = cx25821_vidioc_s_crop, + .vidioc_g_crop = cx25821_vidioc_g_crop, + .vidioc_enum_input = cx25821_vidioc_enum_input, + .vidioc_g_input = cx25821_vidioc_g_input, + .vidioc_s_input = cx25821_vidioc_s_input, + .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_queryctrl = cx25821_vidioc_queryctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = vidioc_log_status, + .vidioc_g_priority = cx25821_vidioc_g_priority, + .vidioc_s_priority = cx25821_vidioc_s_priority, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = cx25821_vidiocgmbuf, +#endif +#ifdef TUNER_FLAG + .vidioc_g_tuner = cx25821_vidioc_g_tuner, + .vidioc_s_tuner = cx25821_vidioc_s_tuner, + .vidioc_g_frequency = cx25821_vidioc_g_frequency, + .vidioc_s_frequency = cx25821_vidioc_s_frequency, +#endif +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = cx25821_vidioc_g_register, + .vidioc_s_register = cx25821_vidioc_s_register, +#endif +}; + +struct video_device cx25821_videoioctl_template = { + .name = "cx25821-videoioctl", + .fops = &video_fops, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = CX25821_NORMS, + .current_norm = V4L2_STD_NTSC_M, +}; diff --git a/drivers/staging/cx25821/cx25821-video.h b/drivers/staging/cx25821/cx25821-video.h index 0bddc02..cc6034b 100644 --- a/drivers/staging/cx25821/cx25821-video.h +++ b/drivers/staging/cx25821/cx25821-video.h @@ -54,7 +54,7 @@ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\ } while (0) -//For IOCTL to identify running upstream +/* For IOCTL to identify running upstream */ #define UPSTREAM_START_VIDEO 700 #define UPSTREAM_STOP_VIDEO 701 #define UPSTREAM_START_AUDIO 702 @@ -80,19 +80,8 @@ extern struct sram_channel *channel7; extern struct sram_channel *channel9; extern struct sram_channel *channel10; extern struct sram_channel *channel11; -extern struct video_device cx25821_video_template0; -extern struct video_device cx25821_video_template1; -extern struct video_device cx25821_video_template2; -extern struct video_device cx25821_video_template3; -extern struct video_device cx25821_video_template4; -extern struct video_device cx25821_video_template5; -extern struct video_device cx25821_video_template6; -extern struct video_device cx25821_video_template7; -extern struct video_device cx25821_video_template9; -extern struct video_device cx25821_video_template10; -extern struct video_device cx25821_video_template11; extern struct video_device cx25821_videoioctl_template; -//extern const u32 *ctrl_classes[]; +/* extern const u32 *ctrl_classes[]; */ extern unsigned int vid_limit; @@ -113,7 +102,7 @@ extern int cx25821_set_tvnorm(struct cx25821_dev *dev, v4l2_std_id norm); extern int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned int bit); extern int cx25821_res_check(struct cx25821_fh *fh, unsigned int bit); -extern int cx25821_res_locked(struct cx25821_dev *dev, unsigned int bit); +extern int cx25821_res_locked(struct cx25821_fh *fh, unsigned int bit); extern void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned int bits); extern int cx25821_video_mux(struct cx25821_dev *dev, unsigned int input); @@ -126,8 +115,7 @@ extern int cx25821_set_scale(struct cx25821_dev *dev, unsigned int width, unsigned int height, enum v4l2_field field); extern int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status); extern void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num); -extern int cx25821_video_register(struct cx25821_dev *dev, int chan_num, - struct video_device *video_template); +extern int cx25821_video_register(struct cx25821_dev *dev); extern int cx25821_get_format_size(void); extern int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count, diff --git a/drivers/staging/cx25821/cx25821-video0.c b/drivers/staging/cx25821/cx25821-video0.c deleted file mode 100644 index 0be2cc1..0000000 --- a/drivers/staging/cx25821/cx25821-video0.c +++ /dev/null @@ -1,434 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> - * Based on Steven Toth <stoth@linuxtv.org> cx23885 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. - */ - -#include "cx25821-video.h" - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct cx25821_buffer *buf = - container_of(vb, struct cx25821_buffer, vb); - struct cx25821_buffer *prev; - struct cx25821_fh *fh = vq->priv_data; - struct cx25821_dev *dev = fh->dev; - struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH00]; - - /* add jump to stopper */ - buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); - buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ - - dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); - - if (!list_empty(&q->queued)) { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, - buf->vb.i); - - } else if (list_empty(&q->active)) { - list_add_tail(&buf->vb.queue, &q->active); - cx25821_start_video_dma(dev, q, buf, - &dev->sram_channels[SRAM_CH00]); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - dprintk(2, - "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", - buf, buf->vb.i, buf->count, q->count); - } else { - prev = - list_entry(q->active.prev, struct cx25821_buffer, vb.queue); - if (prev->vb.width == buf->vb.width - && prev->vb.height == buf->vb.height - && prev->fmt == buf->fmt) { - list_add_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - - /* 64 bit bits 63-32 */ - prev->risc.jmp[2] = cpu_to_le32(0); - dprintk(2, - "[%p/%d] buffer_queue - append to active, buf->count=%d\n", - buf, buf->vb.i, buf->count); - - } else { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, - buf->vb.i); - } - } - - if (list_empty(&q->active)) { - dprintk(2, "active queue empty!\n"); - } -} - -static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = cx25821_buffer_setup, - .buf_prepare = cx25821_buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = cx25821_buffer_release, -}; - -static int video_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct cx25821_dev *dev = video_drvdata(file); - struct cx25821_fh *fh; - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - u32 pix_format; - - printk("open dev=%s type=%s\n", video_device_node_name(vdev), - v4l2_type_names[type]); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) - return -ENOMEM; - - lock_kernel(); - - file->private_data = fh; - fh->dev = dev; - fh->type = type; - fh->width = 720; - - if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) - fh->height = 576; - else - fh->height = 480; - - dev->channel_opened = SRAM_CH00; - pix_format = - (dev->pixel_formats[dev->channel_opened] == - PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV; - fh->fmt = format_by_fourcc(pix_format); - - v4l2_prio_open(&dev->prio, &fh->prio); - - videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct cx25821_buffer), fh); - - dprintk(1, "post videobuf_queue_init()\n"); - unlock_kernel(); - - return 0; -} - -static ssize_t video_read(struct file *file, char __user * data, size_t count, - loff_t * ppos) -{ - struct cx25821_fh *fh = file->private_data; - - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO0)) - return -EBUSY; - - return videobuf_read_one(&fh->vidq, data, count, ppos, - file->f_flags & O_NONBLOCK); - - default: - BUG(); - return 0; - } -} - -static unsigned int video_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_buffer *buf; - - if (cx25821_res_check(fh, RESOURCE_VIDEO0)) { - /* streaming capture */ - if (list_empty(&fh->vidq.stream)) - return POLLERR; - buf = list_entry(fh->vidq.stream.next, - struct cx25821_buffer, vb.stream); - } else { - /* read() capture */ - buf = (struct cx25821_buffer *)fh->vidq.read_buf; - if (NULL == buf) - return POLLERR; - } - - poll_wait(file, &buf->vb.done, wait); - if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) { - if (buf->vb.state == VIDEOBUF_DONE) { - struct cx25821_dev *dev = fh->dev; - - if (dev && dev->use_cif_resolution[SRAM_CH00]) { - u8 cam_id = *((char *)buf->vb.baddr + 3); - memcpy((char *)buf->vb.baddr, - (char *)buf->vb.baddr + (fh->width * 2), - (fh->width * 2)); - *((char *)buf->vb.baddr + 3) = cam_id; - } - } - - return POLLIN | POLLRDNORM; - } - - return 0; -} - -static int video_release(struct file *file) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_dev *dev = fh->dev; - - //stop the risc engine and fifo - cx_write(channel0->dma_ctl, 0); /* FIFO and RISC disable */ - - /* stop video capture */ - if (cx25821_res_check(fh, RESOURCE_VIDEO0)) { - videobuf_queue_cancel(&fh->vidq); - cx25821_res_free(dev, fh, RESOURCE_VIDEO0); - } - - if (fh->vidq.read_buf) { - cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); - kfree(fh->vidq.read_buf); - } - - videobuf_mmap_free(&fh->vidq); - - v4l2_prio_close(&dev->prio, fh->prio); - file->private_data = NULL; - kfree(fh); - - return 0; -} - -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - - if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { - return -EINVAL; - } - - if (unlikely(i != fh->type)) { - return -EINVAL; - } - - if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO0)))) { - return -EBUSY; - } - - return videobuf_streamon(get_queue(fh)); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - int err, res; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (i != fh->type) - return -EINVAL; - - res = cx25821_get_resource(fh, RESOURCE_VIDEO0); - err = videobuf_streamoff(get_queue(fh)); - if (err < 0) - return err; - cx25821_res_free(dev, fh, res); - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - int pix_format = PIXEL_FRMT_422; - - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - dprintk(2, "%s()\n", __func__); - err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); - - if (0 != err) - return err; - - fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - fh->vidq.field = f->fmt.pix.field; - - // check if width and height is valid based on set standard - if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm)) { - fh->width = f->fmt.pix.width; - } - - if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm)) { - fh->height = f->fmt.pix.height; - } - - if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) - pix_format = PIXEL_FRMT_411; - else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) - pix_format = PIXEL_FRMT_422; - else - return -EINVAL; - - cx25821_set_pixel_format(dev, SRAM_CH00, pix_format); - - // check if cif resolution - if (fh->width == 320 || fh->width == 352) { - dev->use_cif_resolution[SRAM_CH00] = 1; - } else { - dev->use_cif_resolution[SRAM_CH00] = 0; - } - dev->cif_width[SRAM_CH00] = fh->width; - medusa_set_resolution(dev, fh->width, SRAM_CH00); - - dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, - fh->height, fh->vidq.field); - cx25821_call_all(dev, video, s_fmt, f); - - return 0; -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - int ret_val = 0; - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - - ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); - - p->sequence = dev->vidq[SRAM_CH00].count; - - return ret_val; -} - -static int vidioc_log_status(struct file *file, void *priv) -{ - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - char name[32 + 2]; - - struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH00]; - u32 tmp = 0; - - snprintf(name, sizeof(name), "%s/2", dev->name); - printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", - dev->name); - cx25821_call_all(dev, core, log_status); - tmp = cx_read(sram_ch->dma_ctl); - printk(KERN_INFO "Video input 0 is %s\n", - (tmp & 0x11) ? "streaming" : "stopped"); - printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", - dev->name); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - return cx25821_set_control(dev, ctl, SRAM_CH00); -} - -// exported stuff -static const struct v4l2_file_operations video_fops = { - .owner = THIS_MODULE, - .open = video_open, - .release = video_release, - .read = video_read, - .poll = video_poll, - .mmap = cx25821_video_mmap, - .ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = cx25821_vidioc_querycap, - .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = cx25821_vidioc_reqbufs, - .vidioc_querybuf = cx25821_vidioc_querybuf, - .vidioc_qbuf = cx25821_vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, -#ifdef TUNER_FLAG - .vidioc_s_std = cx25821_vidioc_s_std, - .vidioc_querystd = cx25821_vidioc_querystd, -#endif - .vidioc_cropcap = cx25821_vidioc_cropcap, - .vidioc_s_crop = cx25821_vidioc_s_crop, - .vidioc_g_crop = cx25821_vidioc_g_crop, - .vidioc_enum_input = cx25821_vidioc_enum_input, - .vidioc_g_input = cx25821_vidioc_g_input, - .vidioc_s_input = cx25821_vidioc_s_input, - .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = cx25821_vidioc_queryctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = cx25821_vidioc_g_priority, - .vidioc_s_priority = cx25821_vidioc_s_priority, -#ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = cx25821_vidiocgmbuf, -#endif -#ifdef TUNER_FLAG - .vidioc_g_tuner = cx25821_vidioc_g_tuner, - .vidioc_s_tuner = cx25821_vidioc_s_tuner, - .vidioc_g_frequency = cx25821_vidioc_g_frequency, - .vidioc_s_frequency = cx25821_vidioc_s_frequency, -#endif -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = cx25821_vidioc_g_register, - .vidioc_s_register = cx25821_vidioc_s_register, -#endif -}; - -struct video_device cx25821_video_template0 = { - .name = "cx25821-video", - .fops = &video_fops, - .ioctl_ops = &video_ioctl_ops, - .tvnorms = CX25821_NORMS, - .current_norm = V4L2_STD_NTSC_M, -}; diff --git a/drivers/staging/cx25821/cx25821-video1.c b/drivers/staging/cx25821/cx25821-video1.c deleted file mode 100644 index b0bae627..0000000 --- a/drivers/staging/cx25821/cx25821-video1.c +++ /dev/null @@ -1,434 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> - * Based on Steven Toth <stoth@linuxtv.org> cx23885 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. - */ - -#include "cx25821-video.h" - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct cx25821_buffer *buf = - container_of(vb, struct cx25821_buffer, vb); - struct cx25821_buffer *prev; - struct cx25821_fh *fh = vq->priv_data; - struct cx25821_dev *dev = fh->dev; - struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH01]; - - /* add jump to stopper */ - buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); - buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ - - dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); - - if (!list_empty(&q->queued)) { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, - buf->vb.i); - - } else if (list_empty(&q->active)) { - list_add_tail(&buf->vb.queue, &q->active); - cx25821_start_video_dma(dev, q, buf, - &dev->sram_channels[SRAM_CH01]); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - dprintk(2, - "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", - buf, buf->vb.i, buf->count, q->count); - } else { - prev = - list_entry(q->active.prev, struct cx25821_buffer, vb.queue); - if (prev->vb.width == buf->vb.width - && prev->vb.height == buf->vb.height - && prev->fmt == buf->fmt) { - list_add_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - - /* 64 bit bits 63-32 */ - prev->risc.jmp[2] = cpu_to_le32(0); - dprintk(2, - "[%p/%d] buffer_queue - append to active, buf->count=%d\n", - buf, buf->vb.i, buf->count); - - } else { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, - buf->vb.i); - } - } - - if (list_empty(&q->active)) { - dprintk(2, "active queue empty!\n"); - } -} - -static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = cx25821_buffer_setup, - .buf_prepare = cx25821_buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = cx25821_buffer_release, -}; - -static int video_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct cx25821_dev *dev = video_drvdata(file); - struct cx25821_fh *fh; - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - u32 pix_format; - - printk("open dev=%s type=%s\n", video_device_node_name(vdev), - v4l2_type_names[type]); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) - return -ENOMEM; - - lock_kernel(); - - file->private_data = fh; - fh->dev = dev; - fh->type = type; - fh->width = 720; - - if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) - fh->height = 576; - else - fh->height = 480; - - dev->channel_opened = SRAM_CH01; - pix_format = - (dev->pixel_formats[dev->channel_opened] == - PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV; - fh->fmt = format_by_fourcc(pix_format); - - v4l2_prio_open(&dev->prio, &fh->prio); - - videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct cx25821_buffer), fh); - - dprintk(1, "post videobuf_queue_init()\n"); - unlock_kernel(); - - return 0; -} - -static ssize_t video_read(struct file *file, char __user * data, size_t count, - loff_t * ppos) -{ - struct cx25821_fh *fh = file->private_data; - - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO1)) - return -EBUSY; - - return videobuf_read_one(&fh->vidq, data, count, ppos, - file->f_flags & O_NONBLOCK); - - default: - BUG(); - return 0; - } -} - -static unsigned int video_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_buffer *buf; - - if (cx25821_res_check(fh, RESOURCE_VIDEO1)) { - /* streaming capture */ - if (list_empty(&fh->vidq.stream)) - return POLLERR; - buf = list_entry(fh->vidq.stream.next, - struct cx25821_buffer, vb.stream); - } else { - /* read() capture */ - buf = (struct cx25821_buffer *)fh->vidq.read_buf; - if (NULL == buf) - return POLLERR; - } - - poll_wait(file, &buf->vb.done, wait); - if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) { - if (buf->vb.state == VIDEOBUF_DONE) { - struct cx25821_dev *dev = fh->dev; - - if (dev && dev->use_cif_resolution[SRAM_CH01]) { - u8 cam_id = *((char *)buf->vb.baddr + 3); - memcpy((char *)buf->vb.baddr, - (char *)buf->vb.baddr + (fh->width * 2), - (fh->width * 2)); - *((char *)buf->vb.baddr + 3) = cam_id; - } - } - - return POLLIN | POLLRDNORM; - } - - return 0; -} - -static int video_release(struct file *file) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_dev *dev = fh->dev; - - //stop the risc engine and fifo - cx_write(channel1->dma_ctl, 0); /* FIFO and RISC disable */ - - /* stop video capture */ - if (cx25821_res_check(fh, RESOURCE_VIDEO1)) { - videobuf_queue_cancel(&fh->vidq); - cx25821_res_free(dev, fh, RESOURCE_VIDEO1); - } - - if (fh->vidq.read_buf) { - cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); - kfree(fh->vidq.read_buf); - } - - videobuf_mmap_free(&fh->vidq); - - v4l2_prio_close(&dev->prio, fh->prio); - file->private_data = NULL; - kfree(fh); - - return 0; -} - -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - - if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { - return -EINVAL; - } - - if (unlikely(i != fh->type)) { - return -EINVAL; - } - - if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO1)))) { - return -EBUSY; - } - - return videobuf_streamon(get_queue(fh)); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - int err, res; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (i != fh->type) - return -EINVAL; - - res = cx25821_get_resource(fh, RESOURCE_VIDEO1); - err = videobuf_streamoff(get_queue(fh)); - if (err < 0) - return err; - cx25821_res_free(dev, fh, res); - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - int pix_format = 0; - - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - dprintk(2, "%s()\n", __func__); - err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); - - if (0 != err) - return err; - - fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - fh->vidq.field = f->fmt.pix.field; - - // check if width and height is valid based on set standard - if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm)) { - fh->width = f->fmt.pix.width; - } - - if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm)) { - fh->height = f->fmt.pix.height; - } - - if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) - pix_format = PIXEL_FRMT_411; - else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) - pix_format = PIXEL_FRMT_422; - else - return -EINVAL; - - cx25821_set_pixel_format(dev, SRAM_CH01, pix_format); - - // check if cif resolution - if (fh->width == 320 || fh->width == 352) { - dev->use_cif_resolution[SRAM_CH01] = 1; - } else { - dev->use_cif_resolution[SRAM_CH01] = 0; - } - dev->cif_width[SRAM_CH01] = fh->width; - medusa_set_resolution(dev, fh->width, SRAM_CH01); - - dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, - fh->height, fh->vidq.field); - cx25821_call_all(dev, video, s_fmt, f); - - return 0; -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - int ret_val = 0; - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - - ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); - - p->sequence = dev->vidq[SRAM_CH01].count; - - return ret_val; -} - -static int vidioc_log_status(struct file *file, void *priv) -{ - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - char name[32 + 2]; - - struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH01]; - u32 tmp = 0; - - snprintf(name, sizeof(name), "%s/2", dev->name); - printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", - dev->name); - cx25821_call_all(dev, core, log_status); - tmp = cx_read(sram_ch->dma_ctl); - printk(KERN_INFO "Video input 1 is %s\n", - (tmp & 0x11) ? "streaming" : "stopped"); - printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", - dev->name); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - return cx25821_set_control(dev, ctl, SRAM_CH01); -} - -//exported stuff -static const struct v4l2_file_operations video_fops = { - .owner = THIS_MODULE, - .open = video_open, - .release = video_release, - .read = video_read, - .poll = video_poll, - .mmap = cx25821_video_mmap, - .ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = cx25821_vidioc_querycap, - .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = cx25821_vidioc_reqbufs, - .vidioc_querybuf = cx25821_vidioc_querybuf, - .vidioc_qbuf = cx25821_vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, -#ifdef TUNER_FLAG - .vidioc_s_std = cx25821_vidioc_s_std, - .vidioc_querystd = cx25821_vidioc_querystd, -#endif - .vidioc_cropcap = cx25821_vidioc_cropcap, - .vidioc_s_crop = cx25821_vidioc_s_crop, - .vidioc_g_crop = cx25821_vidioc_g_crop, - .vidioc_enum_input = cx25821_vidioc_enum_input, - .vidioc_g_input = cx25821_vidioc_g_input, - .vidioc_s_input = cx25821_vidioc_s_input, - .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = cx25821_vidioc_queryctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = cx25821_vidioc_g_priority, - .vidioc_s_priority = cx25821_vidioc_s_priority, -#ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = cx25821_vidiocgmbuf, -#endif -#ifdef TUNER_FLAG - .vidioc_g_tuner = cx25821_vidioc_g_tuner, - .vidioc_s_tuner = cx25821_vidioc_s_tuner, - .vidioc_g_frequency = cx25821_vidioc_g_frequency, - .vidioc_s_frequency = cx25821_vidioc_s_frequency, -#endif -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = cx25821_vidioc_g_register, - .vidioc_s_register = cx25821_vidioc_s_register, -#endif -}; - -struct video_device cx25821_video_template1 = { - .name = "cx25821-video", - .fops = &video_fops, - .ioctl_ops = &video_ioctl_ops, - .tvnorms = CX25821_NORMS, - .current_norm = V4L2_STD_NTSC_M, -}; diff --git a/drivers/staging/cx25821/cx25821-video2.c b/drivers/staging/cx25821/cx25821-video2.c deleted file mode 100644 index 400cdb8..0000000 --- a/drivers/staging/cx25821/cx25821-video2.c +++ /dev/null @@ -1,436 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> - * Based on Steven Toth <stoth@linuxtv.org> cx23885 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. - */ - -#include "cx25821-video.h" - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct cx25821_buffer *buf = - container_of(vb, struct cx25821_buffer, vb); - struct cx25821_buffer *prev; - struct cx25821_fh *fh = vq->priv_data; - struct cx25821_dev *dev = fh->dev; - struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH02]; - - /* add jump to stopper */ - buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); - buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ - - dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); - - if (!list_empty(&q->queued)) { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, - buf->vb.i); - - } else if (list_empty(&q->active)) { - list_add_tail(&buf->vb.queue, &q->active); - cx25821_start_video_dma(dev, q, buf, - &dev->sram_channels[SRAM_CH02]); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - dprintk(2, - "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", - buf, buf->vb.i, buf->count, q->count); - } else { - prev = - list_entry(q->active.prev, struct cx25821_buffer, vb.queue); - if (prev->vb.width == buf->vb.width - && prev->vb.height == buf->vb.height - && prev->fmt == buf->fmt) { - list_add_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - - /* 64 bit bits 63-32 */ - prev->risc.jmp[2] = cpu_to_le32(0); - dprintk(2, - "[%p/%d] buffer_queue - append to active, buf->count=%d\n", - buf, buf->vb.i, buf->count); - - } else { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, - buf->vb.i); - } - } - - if (list_empty(&q->active)) { - dprintk(2, "active queue empty!\n"); - } -} - -static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = cx25821_buffer_setup, - .buf_prepare = cx25821_buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = cx25821_buffer_release, -}; - -static int video_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct cx25821_dev *dev = video_drvdata(file); - struct cx25821_fh *fh; - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - u32 pix_format; - - printk("open dev=%s type=%s\n", video_device_node_name(vdev), - v4l2_type_names[type]); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) - return -ENOMEM; - - lock_kernel(); - - file->private_data = fh; - fh->dev = dev; - fh->type = type; - fh->width = 720; - - if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) - fh->height = 576; - else - fh->height = 480; - - dev->channel_opened = SRAM_CH02; - pix_format = - (dev->pixel_formats[dev->channel_opened] == - PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV; - fh->fmt = format_by_fourcc(pix_format); - - v4l2_prio_open(&dev->prio, &fh->prio); - - videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct cx25821_buffer), fh); - - dprintk(1, "post videobuf_queue_init()\n"); - unlock_kernel(); - - return 0; -} - -static ssize_t video_read(struct file *file, char __user * data, size_t count, - loff_t * ppos) -{ - struct cx25821_fh *fh = file->private_data; - - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO2)) - return -EBUSY; - - return videobuf_read_one(&fh->vidq, data, count, ppos, - file->f_flags & O_NONBLOCK); - - default: - BUG(); - return 0; - } -} - -static unsigned int video_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_buffer *buf; - - if (cx25821_res_check(fh, RESOURCE_VIDEO2)) { - /* streaming capture */ - if (list_empty(&fh->vidq.stream)) - return POLLERR; - buf = list_entry(fh->vidq.stream.next, - struct cx25821_buffer, vb.stream); - } else { - /* read() capture */ - buf = (struct cx25821_buffer *)fh->vidq.read_buf; - if (NULL == buf) - return POLLERR; - } - - poll_wait(file, &buf->vb.done, wait); - if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) { - if (buf->vb.state == VIDEOBUF_DONE) { - struct cx25821_dev *dev = fh->dev; - - if (dev && dev->use_cif_resolution[SRAM_CH02]) { - u8 cam_id = *((char *)buf->vb.baddr + 3); - memcpy((char *)buf->vb.baddr, - (char *)buf->vb.baddr + (fh->width * 2), - (fh->width * 2)); - *((char *)buf->vb.baddr + 3) = cam_id; - } - } - - return POLLIN | POLLRDNORM; - } - - return 0; -} - -static int video_release(struct file *file) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_dev *dev = fh->dev; - - //stop the risc engine and fifo - cx_write(channel2->dma_ctl, 0); /* FIFO and RISC disable */ - - /* stop video capture */ - if (cx25821_res_check(fh, RESOURCE_VIDEO2)) { - videobuf_queue_cancel(&fh->vidq); - cx25821_res_free(dev, fh, RESOURCE_VIDEO2); - } - - if (fh->vidq.read_buf) { - cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); - kfree(fh->vidq.read_buf); - } - - videobuf_mmap_free(&fh->vidq); - - v4l2_prio_close(&dev->prio, fh->prio); - file->private_data = NULL; - kfree(fh); - - return 0; -} - -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - - if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { - return -EINVAL; - } - - if (unlikely(i != fh->type)) { - return -EINVAL; - } - - if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO2)))) { - return -EBUSY; - } - - return videobuf_streamon(get_queue(fh)); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - int err, res; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (i != fh->type) - return -EINVAL; - - res = cx25821_get_resource(fh, RESOURCE_VIDEO2); - err = videobuf_streamoff(get_queue(fh)); - if (err < 0) - return err; - cx25821_res_free(dev, fh, res); - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - int pix_format = 0; - - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - dprintk(2, "%s()\n", __func__); - err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); - - if (0 != err) - return err; - - fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - fh->vidq.field = f->fmt.pix.field; - - // check if width and height is valid based on set standard - if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm)) { - fh->width = f->fmt.pix.width; - } - - if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm)) { - fh->height = f->fmt.pix.height; - } - - if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) - pix_format = PIXEL_FRMT_411; - else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) - pix_format = PIXEL_FRMT_422; - else - return -EINVAL; - - cx25821_set_pixel_format(dev, SRAM_CH02, pix_format); - - // check if cif resolution - if (fh->width == 320 || fh->width == 352) { - dev->use_cif_resolution[SRAM_CH02] = 1; - } else { - dev->use_cif_resolution[SRAM_CH02] = 0; - } - dev->cif_width[SRAM_CH02] = fh->width; - medusa_set_resolution(dev, fh->width, SRAM_CH02); - - dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, - fh->height, fh->vidq.field); - cx25821_call_all(dev, video, s_fmt, f); - - return 0; -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - int ret_val = 0; - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - - ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); - - p->sequence = dev->vidq[SRAM_CH02].count; - - return ret_val; -} - -static int vidioc_log_status(struct file *file, void *priv) -{ - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - char name[32 + 2]; - - struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH02]; - u32 tmp = 0; - - snprintf(name, sizeof(name), "%s/2", dev->name); - printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", - dev->name); - - cx25821_call_all(dev, core, log_status); - - tmp = cx_read(sram_ch->dma_ctl); - printk(KERN_INFO "Video input 2 is %s\n", - (tmp & 0x11) ? "streaming" : "stopped"); - printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", - dev->name); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - return cx25821_set_control(dev, ctl, SRAM_CH02); -} - -// exported stuff -static const struct v4l2_file_operations video_fops = { - .owner = THIS_MODULE, - .open = video_open, - .release = video_release, - .read = video_read, - .poll = video_poll, - .mmap = cx25821_video_mmap, - .ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = cx25821_vidioc_querycap, - .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = cx25821_vidioc_reqbufs, - .vidioc_querybuf = cx25821_vidioc_querybuf, - .vidioc_qbuf = cx25821_vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, -#ifdef TUNER_FLAG - .vidioc_s_std = cx25821_vidioc_s_std, - .vidioc_querystd = cx25821_vidioc_querystd, -#endif - .vidioc_cropcap = cx25821_vidioc_cropcap, - .vidioc_s_crop = cx25821_vidioc_s_crop, - .vidioc_g_crop = cx25821_vidioc_g_crop, - .vidioc_enum_input = cx25821_vidioc_enum_input, - .vidioc_g_input = cx25821_vidioc_g_input, - .vidioc_s_input = cx25821_vidioc_s_input, - .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = cx25821_vidioc_queryctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = cx25821_vidioc_g_priority, - .vidioc_s_priority = cx25821_vidioc_s_priority, -#ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = cx25821_vidiocgmbuf, -#endif -#ifdef TUNER_FLAG - .vidioc_g_tuner = cx25821_vidioc_g_tuner, - .vidioc_s_tuner = cx25821_vidioc_s_tuner, - .vidioc_g_frequency = cx25821_vidioc_g_frequency, - .vidioc_s_frequency = cx25821_vidioc_s_frequency, -#endif -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = cx25821_vidioc_g_register, - .vidioc_s_register = cx25821_vidioc_s_register, -#endif -}; - -struct video_device cx25821_video_template2 = { - .name = "cx25821-video", - .fops = &video_fops, - .ioctl_ops = &video_ioctl_ops, - .tvnorms = CX25821_NORMS, - .current_norm = V4L2_STD_NTSC_M, -}; diff --git a/drivers/staging/cx25821/cx25821-video3.c b/drivers/staging/cx25821/cx25821-video3.c deleted file mode 100644 index 3b216ed..0000000 --- a/drivers/staging/cx25821/cx25821-video3.c +++ /dev/null @@ -1,435 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> - * Based on Steven Toth <stoth@linuxtv.org> cx23885 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. - */ - -#include "cx25821-video.h" - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct cx25821_buffer *buf = - container_of(vb, struct cx25821_buffer, vb); - struct cx25821_buffer *prev; - struct cx25821_fh *fh = vq->priv_data; - struct cx25821_dev *dev = fh->dev; - struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH03]; - - /* add jump to stopper */ - buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); - buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ - - dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); - - if (!list_empty(&q->queued)) { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, - buf->vb.i); - - } else if (list_empty(&q->active)) { - list_add_tail(&buf->vb.queue, &q->active); - cx25821_start_video_dma(dev, q, buf, - &dev->sram_channels[SRAM_CH03]); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - dprintk(2, - "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", - buf, buf->vb.i, buf->count, q->count); - } else { - prev = - list_entry(q->active.prev, struct cx25821_buffer, vb.queue); - if (prev->vb.width == buf->vb.width - && prev->vb.height == buf->vb.height - && prev->fmt == buf->fmt) { - list_add_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - - /* 64 bit bits 63-32 */ - prev->risc.jmp[2] = cpu_to_le32(0); - dprintk(2, - "[%p/%d] buffer_queue - append to active, buf->count=%d\n", - buf, buf->vb.i, buf->count); - - } else { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, - buf->vb.i); - } - } - - if (list_empty(&q->active)) { - dprintk(2, "active queue empty!\n"); - } -} - -static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = cx25821_buffer_setup, - .buf_prepare = cx25821_buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = cx25821_buffer_release, -}; - -static int video_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct cx25821_dev *dev = video_drvdata(file); - struct cx25821_fh *fh; - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - u32 pix_format; - - printk("open dev=%s type=%s\n", video_device_node_name(vdev), - v4l2_type_names[type]); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) - return -ENOMEM; - - lock_kernel(); - - file->private_data = fh; - fh->dev = dev; - fh->type = type; - fh->width = 720; - - if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) - fh->height = 576; - else - fh->height = 480; - - dev->channel_opened = SRAM_CH03; - pix_format = - (dev->pixel_formats[dev->channel_opened] == - PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV; - fh->fmt = format_by_fourcc(pix_format); - - v4l2_prio_open(&dev->prio, &fh->prio); - - videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct cx25821_buffer), fh); - - dprintk(1, "post videobuf_queue_init()\n"); - unlock_kernel(); - - return 0; -} - -static ssize_t video_read(struct file *file, char __user * data, size_t count, - loff_t * ppos) -{ - struct cx25821_fh *fh = file->private_data; - - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO3)) - return -EBUSY; - - return videobuf_read_one(&fh->vidq, data, count, ppos, - file->f_flags & O_NONBLOCK); - - default: - BUG(); - return 0; - } -} - -static unsigned int video_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_buffer *buf; - - if (cx25821_res_check(fh, RESOURCE_VIDEO3)) { - /* streaming capture */ - if (list_empty(&fh->vidq.stream)) - return POLLERR; - buf = list_entry(fh->vidq.stream.next, - struct cx25821_buffer, vb.stream); - } else { - /* read() capture */ - buf = (struct cx25821_buffer *)fh->vidq.read_buf; - if (NULL == buf) - return POLLERR; - } - - poll_wait(file, &buf->vb.done, wait); - if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) { - if (buf->vb.state == VIDEOBUF_DONE) { - struct cx25821_dev *dev = fh->dev; - - if (dev && dev->use_cif_resolution[SRAM_CH03]) { - u8 cam_id = *((char *)buf->vb.baddr + 3); - memcpy((char *)buf->vb.baddr, - (char *)buf->vb.baddr + (fh->width * 2), - (fh->width * 2)); - *((char *)buf->vb.baddr + 3) = cam_id; - } - } - - return POLLIN | POLLRDNORM; - } - - return 0; -} - -static int video_release(struct file *file) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_dev *dev = fh->dev; - - //stop the risc engine and fifo - cx_write(channel3->dma_ctl, 0); /* FIFO and RISC disable */ - - /* stop video capture */ - if (cx25821_res_check(fh, RESOURCE_VIDEO3)) { - videobuf_queue_cancel(&fh->vidq); - cx25821_res_free(dev, fh, RESOURCE_VIDEO3); - } - - if (fh->vidq.read_buf) { - cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); - kfree(fh->vidq.read_buf); - } - - videobuf_mmap_free(&fh->vidq); - - v4l2_prio_close(&dev->prio, fh->prio); - file->private_data = NULL; - kfree(fh); - - return 0; -} - -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - - if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { - return -EINVAL; - } - - if (unlikely(i != fh->type)) { - return -EINVAL; - } - - if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO3)))) { - return -EBUSY; - } - - return videobuf_streamon(get_queue(fh)); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - int err, res; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (i != fh->type) - return -EINVAL; - - res = cx25821_get_resource(fh, RESOURCE_VIDEO3); - err = videobuf_streamoff(get_queue(fh)); - if (err < 0) - return err; - cx25821_res_free(dev, fh, res); - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - int pix_format = 0; - - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - dprintk(2, "%s()\n", __func__); - err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); - - if (0 != err) - return err; - - fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - fh->vidq.field = f->fmt.pix.field; - - // check if width and height is valid based on set standard - if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm)) { - fh->width = f->fmt.pix.width; - } - - if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm)) { - fh->height = f->fmt.pix.height; - } - - if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) - pix_format = PIXEL_FRMT_411; - else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) - pix_format = PIXEL_FRMT_422; - else - return -EINVAL; - - cx25821_set_pixel_format(dev, SRAM_CH03, pix_format); - - // check if cif resolution - if (fh->width == 320 || fh->width == 352) { - dev->use_cif_resolution[SRAM_CH03] = 1; - } else { - dev->use_cif_resolution[SRAM_CH03] = 0; - } - dev->cif_width[SRAM_CH03] = fh->width; - medusa_set_resolution(dev, fh->width, SRAM_CH03); - - dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, - fh->height, fh->vidq.field); - cx25821_call_all(dev, video, s_fmt, f); - - return 0; -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - int ret_val = 0; - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - - ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); - - p->sequence = dev->vidq[SRAM_CH03].count; - - return ret_val; -} - -static int vidioc_log_status(struct file *file, void *priv) -{ - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - char name[32 + 2]; - - struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH03]; - u32 tmp = 0; - - snprintf(name, sizeof(name), "%s/2", dev->name); - printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", - dev->name); - cx25821_call_all(dev, core, log_status); - - tmp = cx_read(sram_ch->dma_ctl); - printk(KERN_INFO "Video input 3 is %s\n", - (tmp & 0x11) ? "streaming" : "stopped"); - printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", - dev->name); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - return cx25821_set_control(dev, ctl, SRAM_CH03); -} - -// exported stuff -static const struct v4l2_file_operations video_fops = { - .owner = THIS_MODULE, - .open = video_open, - .release = video_release, - .read = video_read, - .poll = video_poll, - .mmap = cx25821_video_mmap, - .ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = cx25821_vidioc_querycap, - .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = cx25821_vidioc_reqbufs, - .vidioc_querybuf = cx25821_vidioc_querybuf, - .vidioc_qbuf = cx25821_vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, -#ifdef TUNER_FLAG - .vidioc_s_std = cx25821_vidioc_s_std, - .vidioc_querystd = cx25821_vidioc_querystd, -#endif - .vidioc_cropcap = cx25821_vidioc_cropcap, - .vidioc_s_crop = cx25821_vidioc_s_crop, - .vidioc_g_crop = cx25821_vidioc_g_crop, - .vidioc_enum_input = cx25821_vidioc_enum_input, - .vidioc_g_input = cx25821_vidioc_g_input, - .vidioc_s_input = cx25821_vidioc_s_input, - .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = cx25821_vidioc_queryctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = cx25821_vidioc_g_priority, - .vidioc_s_priority = cx25821_vidioc_s_priority, -#ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = cx25821_vidiocgmbuf, -#endif -#ifdef TUNER_FLAG - .vidioc_g_tuner = cx25821_vidioc_g_tuner, - .vidioc_s_tuner = cx25821_vidioc_s_tuner, - .vidioc_g_frequency = cx25821_vidioc_g_frequency, - .vidioc_s_frequency = cx25821_vidioc_s_frequency, -#endif -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = cx25821_vidioc_g_register, - .vidioc_s_register = cx25821_vidioc_s_register, -#endif -}; - -struct video_device cx25821_video_template3 = { - .name = "cx25821-video", - .fops = &video_fops, - .ioctl_ops = &video_ioctl_ops, - .tvnorms = CX25821_NORMS, - .current_norm = V4L2_STD_NTSC_M, -}; diff --git a/drivers/staging/cx25821/cx25821-video4.c b/drivers/staging/cx25821/cx25821-video4.c deleted file mode 100644 index f7b08c5..0000000 --- a/drivers/staging/cx25821/cx25821-video4.c +++ /dev/null @@ -1,434 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> - * Based on Steven Toth <stoth@linuxtv.org> cx23885 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. - */ - -#include "cx25821-video.h" - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct cx25821_buffer *buf = - container_of(vb, struct cx25821_buffer, vb); - struct cx25821_buffer *prev; - struct cx25821_fh *fh = vq->priv_data; - struct cx25821_dev *dev = fh->dev; - struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH04]; - - /* add jump to stopper */ - buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); - buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ - - dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); - - if (!list_empty(&q->queued)) { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, - buf->vb.i); - - } else if (list_empty(&q->active)) { - list_add_tail(&buf->vb.queue, &q->active); - cx25821_start_video_dma(dev, q, buf, - &dev->sram_channels[SRAM_CH04]); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - dprintk(2, - "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", - buf, buf->vb.i, buf->count, q->count); - } else { - prev = - list_entry(q->active.prev, struct cx25821_buffer, vb.queue); - if (prev->vb.width == buf->vb.width - && prev->vb.height == buf->vb.height - && prev->fmt == buf->fmt) { - list_add_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - - /* 64 bit bits 63-32 */ - prev->risc.jmp[2] = cpu_to_le32(0); - dprintk(2, - "[%p/%d] buffer_queue - append to active, buf->count=%d\n", - buf, buf->vb.i, buf->count); - - } else { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, - buf->vb.i); - } - } - - if (list_empty(&q->active)) { - dprintk(2, "active queue empty!\n"); - } -} - -static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = cx25821_buffer_setup, - .buf_prepare = cx25821_buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = cx25821_buffer_release, -}; - -static int video_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct cx25821_dev *dev = video_drvdata(file); - struct cx25821_fh *fh; - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - u32 pix_format; - - printk("open dev=%s type=%s\n", video_device_node_name(vdev), - v4l2_type_names[type]); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) - return -ENOMEM; - - lock_kernel(); - - file->private_data = fh; - fh->dev = dev; - fh->type = type; - fh->width = 720; - - if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) - fh->height = 576; - else - fh->height = 480; - - dev->channel_opened = SRAM_CH04; - pix_format = - (dev->pixel_formats[dev->channel_opened] == - PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV; - fh->fmt = format_by_fourcc(pix_format); - - v4l2_prio_open(&dev->prio, &fh->prio); - videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct cx25821_buffer), fh); - - dprintk(1, "post videobuf_queue_init()\n"); - unlock_kernel(); - - return 0; -} - -static ssize_t video_read(struct file *file, char __user * data, size_t count, - loff_t * ppos) -{ - struct cx25821_fh *fh = file->private_data; - - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO4)) - return -EBUSY; - - return videobuf_read_one(&fh->vidq, data, count, ppos, - file->f_flags & O_NONBLOCK); - - default: - BUG(); - return 0; - } -} - -static unsigned int video_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_buffer *buf; - - if (cx25821_res_check(fh, RESOURCE_VIDEO4)) { - /* streaming capture */ - if (list_empty(&fh->vidq.stream)) - return POLLERR; - buf = list_entry(fh->vidq.stream.next, - struct cx25821_buffer, vb.stream); - } else { - /* read() capture */ - buf = (struct cx25821_buffer *)fh->vidq.read_buf; - if (NULL == buf) - return POLLERR; - } - - poll_wait(file, &buf->vb.done, wait); - if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) { - if (buf->vb.state == VIDEOBUF_DONE) { - struct cx25821_dev *dev = fh->dev; - - if (dev && dev->use_cif_resolution[SRAM_CH04]) { - u8 cam_id = *((char *)buf->vb.baddr + 3); - memcpy((char *)buf->vb.baddr, - (char *)buf->vb.baddr + (fh->width * 2), - (fh->width * 2)); - *((char *)buf->vb.baddr + 3) = cam_id; - } - } - - return POLLIN | POLLRDNORM; - } - - return 0; -} - -static int video_release(struct file *file) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_dev *dev = fh->dev; - - //stop the risc engine and fifo - cx_write(channel4->dma_ctl, 0); /* FIFO and RISC disable */ - - /* stop video capture */ - if (cx25821_res_check(fh, RESOURCE_VIDEO4)) { - videobuf_queue_cancel(&fh->vidq); - cx25821_res_free(dev, fh, RESOURCE_VIDEO4); - } - - if (fh->vidq.read_buf) { - cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); - kfree(fh->vidq.read_buf); - } - - videobuf_mmap_free(&fh->vidq); - - v4l2_prio_close(&dev->prio, fh->prio); - file->private_data = NULL; - kfree(fh); - - return 0; -} - -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - - if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { - return -EINVAL; - } - - if (unlikely(i != fh->type)) { - return -EINVAL; - } - - if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO4)))) { - return -EBUSY; - } - - return videobuf_streamon(get_queue(fh)); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - int err, res; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (i != fh->type) - return -EINVAL; - - res = cx25821_get_resource(fh, RESOURCE_VIDEO4); - err = videobuf_streamoff(get_queue(fh)); - if (err < 0) - return err; - cx25821_res_free(dev, fh, res); - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - int pix_format = 0; - - // check priority - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - dprintk(2, "%s()\n", __func__); - err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); - - if (0 != err) - return err; - - fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - fh->vidq.field = f->fmt.pix.field; - - // check if width and height is valid based on set standard - if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm)) { - fh->width = f->fmt.pix.width; - } - - if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm)) { - fh->height = f->fmt.pix.height; - } - - if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) - pix_format = PIXEL_FRMT_411; - else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) - pix_format = PIXEL_FRMT_422; - else - return -EINVAL; - - cx25821_set_pixel_format(dev, SRAM_CH04, pix_format); - - // check if cif resolution - if (fh->width == 320 || fh->width == 352) { - dev->use_cif_resolution[SRAM_CH04] = 1; - } else { - dev->use_cif_resolution[SRAM_CH04] = 0; - } - dev->cif_width[SRAM_CH04] = fh->width; - medusa_set_resolution(dev, fh->width, SRAM_CH04); - - dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, - fh->height, fh->vidq.field); - cx25821_call_all(dev, video, s_fmt, f); - - return 0; -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - int ret_val = 0; - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - - ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); - - p->sequence = dev->vidq[SRAM_CH04].count; - - return ret_val; -} - -static int vidioc_log_status(struct file *file, void *priv) -{ - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - char name[32 + 2]; - - struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH04]; - u32 tmp = 0; - - snprintf(name, sizeof(name), "%s/2", dev->name); - printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", - dev->name); - cx25821_call_all(dev, core, log_status); - - tmp = cx_read(sram_ch->dma_ctl); - printk(KERN_INFO "Video input 4 is %s\n", - (tmp & 0x11) ? "streaming" : "stopped"); - printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", - dev->name); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - return cx25821_set_control(dev, ctl, SRAM_CH04); -} - -// exported stuff -static const struct v4l2_file_operations video_fops = { - .owner = THIS_MODULE, - .open = video_open, - .release = video_release, - .read = video_read, - .poll = video_poll, - .mmap = cx25821_video_mmap, - .ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = cx25821_vidioc_querycap, - .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = cx25821_vidioc_reqbufs, - .vidioc_querybuf = cx25821_vidioc_querybuf, - .vidioc_qbuf = cx25821_vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, -#ifdef TUNER_FLAG - .vidioc_s_std = cx25821_vidioc_s_std, - .vidioc_querystd = cx25821_vidioc_querystd, -#endif - .vidioc_cropcap = cx25821_vidioc_cropcap, - .vidioc_s_crop = cx25821_vidioc_s_crop, - .vidioc_g_crop = cx25821_vidioc_g_crop, - .vidioc_enum_input = cx25821_vidioc_enum_input, - .vidioc_g_input = cx25821_vidioc_g_input, - .vidioc_s_input = cx25821_vidioc_s_input, - .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = cx25821_vidioc_queryctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = cx25821_vidioc_g_priority, - .vidioc_s_priority = cx25821_vidioc_s_priority, -#ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = cx25821_vidiocgmbuf, -#endif -#ifdef TUNER_FLAG - .vidioc_g_tuner = cx25821_vidioc_g_tuner, - .vidioc_s_tuner = cx25821_vidioc_s_tuner, - .vidioc_g_frequency = cx25821_vidioc_g_frequency, - .vidioc_s_frequency = cx25821_vidioc_s_frequency, -#endif -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = cx25821_vidioc_g_register, - .vidioc_s_register = cx25821_vidioc_s_register, -#endif -}; - -struct video_device cx25821_video_template4 = { - .name = "cx25821-video", - .fops = &video_fops, - .ioctl_ops = &video_ioctl_ops, - .tvnorms = CX25821_NORMS, - .current_norm = V4L2_STD_NTSC_M, -}; diff --git a/drivers/staging/cx25821/cx25821-video5.c b/drivers/staging/cx25821/cx25821-video5.c deleted file mode 100644 index 5937033..0000000 --- a/drivers/staging/cx25821/cx25821-video5.c +++ /dev/null @@ -1,434 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> - * Based on Steven Toth <stoth@linuxtv.org> cx23885 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. - */ - -#include "cx25821-video.h" - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct cx25821_buffer *buf = - container_of(vb, struct cx25821_buffer, vb); - struct cx25821_buffer *prev; - struct cx25821_fh *fh = vq->priv_data; - struct cx25821_dev *dev = fh->dev; - struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH05]; - - /* add jump to stopper */ - buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); - buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ - - dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); - - if (!list_empty(&q->queued)) { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, - buf->vb.i); - - } else if (list_empty(&q->active)) { - list_add_tail(&buf->vb.queue, &q->active); - cx25821_start_video_dma(dev, q, buf, - &dev->sram_channels[SRAM_CH05]); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - dprintk(2, - "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", - buf, buf->vb.i, buf->count, q->count); - } else { - prev = - list_entry(q->active.prev, struct cx25821_buffer, vb.queue); - if (prev->vb.width == buf->vb.width - && prev->vb.height == buf->vb.height - && prev->fmt == buf->fmt) { - list_add_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - - /* 64 bit bits 63-32 */ - prev->risc.jmp[2] = cpu_to_le32(0); - dprintk(2, - "[%p/%d] buffer_queue - append to active, buf->count=%d\n", - buf, buf->vb.i, buf->count); - - } else { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, - buf->vb.i); - } - } - - if (list_empty(&q->active)) { - dprintk(2, "active queue empty!\n"); - } -} - -static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = cx25821_buffer_setup, - .buf_prepare = cx25821_buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = cx25821_buffer_release, -}; - -static int video_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct cx25821_dev *dev = video_drvdata(file); - struct cx25821_fh *fh; - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - u32 pix_format; - - printk("open dev=%s type=%s\n", video_device_node_name(vdev), - v4l2_type_names[type]); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) - return -ENOMEM; - - lock_kernel(); - - file->private_data = fh; - fh->dev = dev; - fh->type = type; - fh->width = 720; - - if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) - fh->height = 576; - else - fh->height = 480; - - dev->channel_opened = SRAM_CH05; - pix_format = - (dev->pixel_formats[dev->channel_opened] == - PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV; - fh->fmt = format_by_fourcc(pix_format); - - v4l2_prio_open(&dev->prio, &fh->prio); - - videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct cx25821_buffer), fh); - - dprintk(1, "post videobuf_queue_init()\n"); - unlock_kernel(); - - return 0; -} - -static ssize_t video_read(struct file *file, char __user * data, size_t count, - loff_t * ppos) -{ - struct cx25821_fh *fh = file->private_data; - - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO5)) - return -EBUSY; - - return videobuf_read_one(&fh->vidq, data, count, ppos, - file->f_flags & O_NONBLOCK); - - default: - BUG(); - return 0; - } -} - -static unsigned int video_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_buffer *buf; - - if (cx25821_res_check(fh, RESOURCE_VIDEO5)) { - /* streaming capture */ - if (list_empty(&fh->vidq.stream)) - return POLLERR; - buf = list_entry(fh->vidq.stream.next, - struct cx25821_buffer, vb.stream); - } else { - /* read() capture */ - buf = (struct cx25821_buffer *)fh->vidq.read_buf; - if (NULL == buf) - return POLLERR; - } - - poll_wait(file, &buf->vb.done, wait); - if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) { - if (buf->vb.state == VIDEOBUF_DONE) { - struct cx25821_dev *dev = fh->dev; - - if (dev && dev->use_cif_resolution[SRAM_CH05]) { - u8 cam_id = *((char *)buf->vb.baddr + 3); - memcpy((char *)buf->vb.baddr, - (char *)buf->vb.baddr + (fh->width * 2), - (fh->width * 2)); - *((char *)buf->vb.baddr + 3) = cam_id; - } - } - - return POLLIN | POLLRDNORM; - } - - return 0; -} - -static int video_release(struct file *file) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_dev *dev = fh->dev; - - //stop the risc engine and fifo - cx_write(channel5->dma_ctl, 0); /* FIFO and RISC disable */ - - /* stop video capture */ - if (cx25821_res_check(fh, RESOURCE_VIDEO5)) { - videobuf_queue_cancel(&fh->vidq); - cx25821_res_free(dev, fh, RESOURCE_VIDEO5); - } - - if (fh->vidq.read_buf) { - cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); - kfree(fh->vidq.read_buf); - } - - videobuf_mmap_free(&fh->vidq); - - v4l2_prio_close(&dev->prio, fh->prio); - file->private_data = NULL; - kfree(fh); - - return 0; -} - -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - - if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { - return -EINVAL; - } - - if (unlikely(i != fh->type)) { - return -EINVAL; - } - - if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO5)))) { - return -EBUSY; - } - - return videobuf_streamon(get_queue(fh)); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - int err, res; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (i != fh->type) - return -EINVAL; - - res = cx25821_get_resource(fh, RESOURCE_VIDEO5); - err = videobuf_streamoff(get_queue(fh)); - if (err < 0) - return err; - cx25821_res_free(dev, fh, res); - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - int pix_format = 0; - - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - dprintk(2, "%s()\n", __func__); - err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); - - if (0 != err) - return err; - - fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - fh->vidq.field = f->fmt.pix.field; - - // check if width and height is valid based on set standard - if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm)) { - fh->width = f->fmt.pix.width; - } - - if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm)) { - fh->height = f->fmt.pix.height; - } - - if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) - pix_format = PIXEL_FRMT_411; - else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) - pix_format = PIXEL_FRMT_422; - else - return -EINVAL; - - cx25821_set_pixel_format(dev, SRAM_CH05, pix_format); - - // check if cif resolution - if (fh->width == 320 || fh->width == 352) { - dev->use_cif_resolution[SRAM_CH05] = 1; - } else { - dev->use_cif_resolution[SRAM_CH05] = 0; - } - dev->cif_width[SRAM_CH05] = fh->width; - medusa_set_resolution(dev, fh->width, SRAM_CH05); - - dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, - fh->height, fh->vidq.field); - cx25821_call_all(dev, video, s_fmt, f); - - return 0; -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - int ret_val = 0; - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - - ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); - - p->sequence = dev->vidq[SRAM_CH05].count; - - return ret_val; -} -static int vidioc_log_status(struct file *file, void *priv) -{ - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - char name[32 + 2]; - - struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH05]; - u32 tmp = 0; - - snprintf(name, sizeof(name), "%s/2", dev->name); - printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", - dev->name); - cx25821_call_all(dev, core, log_status); - - tmp = cx_read(sram_ch->dma_ctl); - printk(KERN_INFO "Video input 5 is %s\n", - (tmp & 0x11) ? "streaming" : "stopped"); - printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", - dev->name); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - return cx25821_set_control(dev, ctl, SRAM_CH05); -} - -// exported stuff -static const struct v4l2_file_operations video_fops = { - .owner = THIS_MODULE, - .open = video_open, - .release = video_release, - .read = video_read, - .poll = video_poll, - .mmap = cx25821_video_mmap, - .ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = cx25821_vidioc_querycap, - .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = cx25821_vidioc_reqbufs, - .vidioc_querybuf = cx25821_vidioc_querybuf, - .vidioc_qbuf = cx25821_vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, -#ifdef TUNER_FLAG - .vidioc_s_std = cx25821_vidioc_s_std, - .vidioc_querystd = cx25821_vidioc_querystd, -#endif - .vidioc_cropcap = cx25821_vidioc_cropcap, - .vidioc_s_crop = cx25821_vidioc_s_crop, - .vidioc_g_crop = cx25821_vidioc_g_crop, - .vidioc_enum_input = cx25821_vidioc_enum_input, - .vidioc_g_input = cx25821_vidioc_g_input, - .vidioc_s_input = cx25821_vidioc_s_input, - .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = cx25821_vidioc_queryctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = cx25821_vidioc_g_priority, - .vidioc_s_priority = cx25821_vidioc_s_priority, -#ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = cx25821_vidiocgmbuf, -#endif -#ifdef TUNER_FLAG - .vidioc_g_tuner = cx25821_vidioc_g_tuner, - .vidioc_s_tuner = cx25821_vidioc_s_tuner, - .vidioc_g_frequency = cx25821_vidioc_g_frequency, - .vidioc_s_frequency = cx25821_vidioc_s_frequency, -#endif -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = cx25821_vidioc_g_register, - .vidioc_s_register = cx25821_vidioc_s_register, -#endif -}; - -struct video_device cx25821_video_template5 = { - .name = "cx25821-video", - .fops = &video_fops, - .ioctl_ops = &video_ioctl_ops, - .tvnorms = CX25821_NORMS, - .current_norm = V4L2_STD_NTSC_M, -}; diff --git a/drivers/staging/cx25821/cx25821-video6.c b/drivers/staging/cx25821/cx25821-video6.c deleted file mode 100644 index 4db2eb8..0000000 --- a/drivers/staging/cx25821/cx25821-video6.c +++ /dev/null @@ -1,434 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> - * Based on Steven Toth <stoth@linuxtv.org> cx23885 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. - */ - -#include "cx25821-video.h" - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct cx25821_buffer *buf = - container_of(vb, struct cx25821_buffer, vb); - struct cx25821_buffer *prev; - struct cx25821_fh *fh = vq->priv_data; - struct cx25821_dev *dev = fh->dev; - struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH06]; - - /* add jump to stopper */ - buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); - buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ - - dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); - - if (!list_empty(&q->queued)) { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, - buf->vb.i); - - } else if (list_empty(&q->active)) { - list_add_tail(&buf->vb.queue, &q->active); - cx25821_start_video_dma(dev, q, buf, - &dev->sram_channels[SRAM_CH06]); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - dprintk(2, - "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", - buf, buf->vb.i, buf->count, q->count); - } else { - prev = - list_entry(q->active.prev, struct cx25821_buffer, vb.queue); - if (prev->vb.width == buf->vb.width - && prev->vb.height == buf->vb.height - && prev->fmt == buf->fmt) { - list_add_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - - /* 64 bit bits 63-32 */ - prev->risc.jmp[2] = cpu_to_le32(0); - dprintk(2, - "[%p/%d] buffer_queue - append to active, buf->count=%d\n", - buf, buf->vb.i, buf->count); - - } else { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, - buf->vb.i); - } - } - - if (list_empty(&q->active)) { - dprintk(2, "active queue empty!\n"); - } -} - -static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = cx25821_buffer_setup, - .buf_prepare = cx25821_buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = cx25821_buffer_release, -}; - -static int video_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct cx25821_dev *dev = video_drvdata(file); - struct cx25821_fh *fh; - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - u32 pix_format; - - printk("open dev=%s type=%s\n", video_device_node_name(vdev), - v4l2_type_names[type]); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) - return -ENOMEM; - - lock_kernel(); - - file->private_data = fh; - fh->dev = dev; - fh->type = type; - fh->width = 720; - - if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) - fh->height = 576; - else - fh->height = 480; - - dev->channel_opened = SRAM_CH06; - pix_format = - (dev->pixel_formats[dev->channel_opened] == - PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV; - fh->fmt = format_by_fourcc(pix_format); - - v4l2_prio_open(&dev->prio, &fh->prio); - - videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct cx25821_buffer), fh); - - dprintk(1, "post videobuf_queue_init()\n"); - unlock_kernel(); - - return 0; -} - -static ssize_t video_read(struct file *file, char __user * data, size_t count, - loff_t * ppos) -{ - struct cx25821_fh *fh = file->private_data; - - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO6)) - return -EBUSY; - - return videobuf_read_one(&fh->vidq, data, count, ppos, - file->f_flags & O_NONBLOCK); - - default: - BUG(); - return 0; - } -} - -static unsigned int video_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_buffer *buf; - - if (cx25821_res_check(fh, RESOURCE_VIDEO6)) { - /* streaming capture */ - if (list_empty(&fh->vidq.stream)) - return POLLERR; - buf = list_entry(fh->vidq.stream.next, - struct cx25821_buffer, vb.stream); - } else { - /* read() capture */ - buf = (struct cx25821_buffer *)fh->vidq.read_buf; - if (NULL == buf) - return POLLERR; - } - - poll_wait(file, &buf->vb.done, wait); - if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) { - if (buf->vb.state == VIDEOBUF_DONE) { - struct cx25821_dev *dev = fh->dev; - - if (dev && dev->use_cif_resolution[SRAM_CH06]) { - u8 cam_id = *((char *)buf->vb.baddr + 3); - memcpy((char *)buf->vb.baddr, - (char *)buf->vb.baddr + (fh->width * 2), - (fh->width * 2)); - *((char *)buf->vb.baddr + 3) = cam_id; - } - } - - return POLLIN | POLLRDNORM; - } - - return 0; -} - -static int video_release(struct file *file) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_dev *dev = fh->dev; - - //stop the risc engine and fifo - cx_write(channel6->dma_ctl, 0); /* FIFO and RISC disable */ - - /* stop video capture */ - if (cx25821_res_check(fh, RESOURCE_VIDEO6)) { - videobuf_queue_cancel(&fh->vidq); - cx25821_res_free(dev, fh, RESOURCE_VIDEO6); - } - if (fh->vidq.read_buf) { - cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); - kfree(fh->vidq.read_buf); - } - - videobuf_mmap_free(&fh->vidq); - - v4l2_prio_close(&dev->prio, fh->prio); - file->private_data = NULL; - kfree(fh); - - return 0; -} - -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - - if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { - return -EINVAL; - } - - if (unlikely(i != fh->type)) { - return -EINVAL; - } - - if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO6)))) { - return -EBUSY; - } - - return videobuf_streamon(get_queue(fh)); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - int err, res; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (i != fh->type) - return -EINVAL; - - res = cx25821_get_resource(fh, RESOURCE_VIDEO6); - err = videobuf_streamoff(get_queue(fh)); - if (err < 0) - return err; - cx25821_res_free(dev, fh, res); - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - int pix_format = 0; - - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - dprintk(2, "%s()\n", __func__); - err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); - - if (0 != err) - return err; - - fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - fh->vidq.field = f->fmt.pix.field; - - // check if width and height is valid based on set standard - if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm)) { - fh->width = f->fmt.pix.width; - } - - if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm)) { - fh->height = f->fmt.pix.height; - } - - if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) - pix_format = PIXEL_FRMT_411; - else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) - pix_format = PIXEL_FRMT_422; - else - return -EINVAL; - - cx25821_set_pixel_format(dev, SRAM_CH06, pix_format); - - // check if cif resolution - if (fh->width == 320 || fh->width == 352) { - dev->use_cif_resolution[SRAM_CH06] = 1; - } else { - dev->use_cif_resolution[SRAM_CH06] = 0; - } - dev->cif_width[SRAM_CH06] = fh->width; - medusa_set_resolution(dev, fh->width, SRAM_CH06); - - dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, - fh->height, fh->vidq.field); - cx25821_call_all(dev, video, s_fmt, f); - - return 0; -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - int ret_val = 0; - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - - ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); - - p->sequence = dev->vidq[SRAM_CH06].count; - - return ret_val; -} - -static int vidioc_log_status(struct file *file, void *priv) -{ - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - char name[32 + 2]; - - struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH06]; - u32 tmp = 0; - - snprintf(name, sizeof(name), "%s/2", dev->name); - printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", - dev->name); - cx25821_call_all(dev, core, log_status); - - tmp = cx_read(sram_ch->dma_ctl); - printk(KERN_INFO "Video input 6 is %s\n", - (tmp & 0x11) ? "streaming" : "stopped"); - printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", - dev->name); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - return cx25821_set_control(dev, ctl, SRAM_CH06); -} - -// exported stuff -static const struct v4l2_file_operations video_fops = { - .owner = THIS_MODULE, - .open = video_open, - .release = video_release, - .read = video_read, - .poll = video_poll, - .mmap = cx25821_video_mmap, - .ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = cx25821_vidioc_querycap, - .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = cx25821_vidioc_reqbufs, - .vidioc_querybuf = cx25821_vidioc_querybuf, - .vidioc_qbuf = cx25821_vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, -#ifdef TUNER_FLAG - .vidioc_s_std = cx25821_vidioc_s_std, - .vidioc_querystd = cx25821_vidioc_querystd, -#endif - .vidioc_cropcap = cx25821_vidioc_cropcap, - .vidioc_s_crop = cx25821_vidioc_s_crop, - .vidioc_g_crop = cx25821_vidioc_g_crop, - .vidioc_enum_input = cx25821_vidioc_enum_input, - .vidioc_g_input = cx25821_vidioc_g_input, - .vidioc_s_input = cx25821_vidioc_s_input, - .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = cx25821_vidioc_queryctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = cx25821_vidioc_g_priority, - .vidioc_s_priority = cx25821_vidioc_s_priority, -#ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = cx25821_vidiocgmbuf, -#endif -#ifdef TUNER_FLAG - .vidioc_g_tuner = cx25821_vidioc_g_tuner, - .vidioc_s_tuner = cx25821_vidioc_s_tuner, - .vidioc_g_frequency = cx25821_vidioc_g_frequency, - .vidioc_s_frequency = cx25821_vidioc_s_frequency, -#endif -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = cx25821_vidioc_g_register, - .vidioc_s_register = cx25821_vidioc_s_register, -#endif -}; - -struct video_device cx25821_video_template6 = { - .name = "cx25821-video", - .fops = &video_fops, - .ioctl_ops = &video_ioctl_ops, - .tvnorms = CX25821_NORMS, - .current_norm = V4L2_STD_NTSC_M, -}; diff --git a/drivers/staging/cx25821/cx25821-video7.c b/drivers/staging/cx25821/cx25821-video7.c deleted file mode 100644 index 5e4a769..0000000 --- a/drivers/staging/cx25821/cx25821-video7.c +++ /dev/null @@ -1,433 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> - * Based on Steven Toth <stoth@linuxtv.org> cx23885 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. - */ - -#include "cx25821-video.h" - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct cx25821_buffer *buf = - container_of(vb, struct cx25821_buffer, vb); - struct cx25821_buffer *prev; - struct cx25821_fh *fh = vq->priv_data; - struct cx25821_dev *dev = fh->dev; - struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH07]; - - /* add jump to stopper */ - buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); - buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ - - dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); - if (!list_empty(&q->queued)) { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, - buf->vb.i); - - } else if (list_empty(&q->active)) { - list_add_tail(&buf->vb.queue, &q->active); - cx25821_start_video_dma(dev, q, buf, - &dev->sram_channels[SRAM_CH07]); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - dprintk(2, - "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", - buf, buf->vb.i, buf->count, q->count); - } else { - prev = - list_entry(q->active.prev, struct cx25821_buffer, vb.queue); - if (prev->vb.width == buf->vb.width - && prev->vb.height == buf->vb.height - && prev->fmt == buf->fmt) { - list_add_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - - /* 64 bit bits 63-32 */ - prev->risc.jmp[2] = cpu_to_le32(0); - dprintk(2, - "[%p/%d] buffer_queue - append to active, buf->count=%d\n", - buf, buf->vb.i, buf->count); - - } else { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, - buf->vb.i); - } - } - - if (list_empty(&q->active)) { - dprintk(2, "active queue empty!\n"); - } -} - -static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = cx25821_buffer_setup, - .buf_prepare = cx25821_buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = cx25821_buffer_release, -}; - -static int video_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct cx25821_dev *dev = video_drvdata(file); - struct cx25821_fh *fh; - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - u32 pix_format; - - printk("open dev=%s type=%s\n", video_device_node_name(vdev), - v4l2_type_names[type]); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) - return -ENOMEM; - - lock_kernel(); - - file->private_data = fh; - fh->dev = dev; - fh->type = type; - fh->width = 720; - - if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) - fh->height = 576; - else - fh->height = 480; - - dev->channel_opened = SRAM_CH07; - pix_format = - (dev->pixel_formats[dev->channel_opened] == - PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV; - fh->fmt = format_by_fourcc(pix_format); - - v4l2_prio_open(&dev->prio, &fh->prio); - - videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct cx25821_buffer), fh); - - dprintk(1, "post videobuf_queue_init()\n"); - unlock_kernel(); - - return 0; -} - -static ssize_t video_read(struct file *file, char __user * data, size_t count, - loff_t * ppos) -{ - struct cx25821_fh *fh = file->private_data; - - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO7)) - return -EBUSY; - - return videobuf_read_one(&fh->vidq, data, count, ppos, - file->f_flags & O_NONBLOCK); - - default: - BUG(); - return 0; - } -} - -static unsigned int video_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_buffer *buf; - - if (cx25821_res_check(fh, RESOURCE_VIDEO7)) { - /* streaming capture */ - if (list_empty(&fh->vidq.stream)) - return POLLERR; - buf = list_entry(fh->vidq.stream.next, - struct cx25821_buffer, vb.stream); - } else { - /* read() capture */ - buf = (struct cx25821_buffer *)fh->vidq.read_buf; - if (NULL == buf) - return POLLERR; - } - - poll_wait(file, &buf->vb.done, wait); - if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) { - if (buf->vb.state == VIDEOBUF_DONE) { - struct cx25821_dev *dev = fh->dev; - - if (dev && dev->use_cif_resolution[SRAM_CH07]) { - u8 cam_id = *((char *)buf->vb.baddr + 3); - memcpy((char *)buf->vb.baddr, - (char *)buf->vb.baddr + (fh->width * 2), - (fh->width * 2)); - *((char *)buf->vb.baddr + 3) = cam_id; - } - } - - return POLLIN | POLLRDNORM; - } - - return 0; -} - -static int video_release(struct file *file) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_dev *dev = fh->dev; - - //stop the risc engine and fifo - cx_write(channel7->dma_ctl, 0); /* FIFO and RISC disable */ - - /* stop video capture */ - if (cx25821_res_check(fh, RESOURCE_VIDEO7)) { - videobuf_queue_cancel(&fh->vidq); - cx25821_res_free(dev, fh, RESOURCE_VIDEO7); - } - - if (fh->vidq.read_buf) { - cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); - kfree(fh->vidq.read_buf); - } - - videobuf_mmap_free(&fh->vidq); - - v4l2_prio_close(&dev->prio, fh->prio); - file->private_data = NULL; - kfree(fh); - - return 0; -} - -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - - if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { - return -EINVAL; - } - - if (unlikely(i != fh->type)) { - return -EINVAL; - } - - if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO7)))) { - return -EBUSY; - } - - return videobuf_streamon(get_queue(fh)); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - int err, res; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (i != fh->type) - return -EINVAL; - - res = cx25821_get_resource(fh, RESOURCE_VIDEO7); - err = videobuf_streamoff(get_queue(fh)); - if (err < 0) - return err; - cx25821_res_free(dev, fh, res); - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - int pix_format = 0; - - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - dprintk(2, "%s()\n", __func__); - err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); - - if (0 != err) - return err; - - fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - fh->vidq.field = f->fmt.pix.field; - - // check if width and height is valid based on set standard - if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm)) { - fh->width = f->fmt.pix.width; - } - - if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm)) { - fh->height = f->fmt.pix.height; - } - - if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) - pix_format = PIXEL_FRMT_411; - else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) - pix_format = PIXEL_FRMT_422; - else - return -EINVAL; - - cx25821_set_pixel_format(dev, SRAM_CH07, pix_format); - - // check if cif resolution - if (fh->width == 320 || fh->width == 352) { - dev->use_cif_resolution[SRAM_CH07] = 1; - } else { - dev->use_cif_resolution[SRAM_CH07] = 0; - } - dev->cif_width[SRAM_CH07] = fh->width; - medusa_set_resolution(dev, fh->width, SRAM_CH07); - - dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, - fh->height, fh->vidq.field); - cx25821_call_all(dev, video, s_fmt, f); - - return 0; -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - int ret_val = 0; - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - - ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); - - p->sequence = dev->vidq[SRAM_CH07].count; - - return ret_val; -} -static int vidioc_log_status(struct file *file, void *priv) -{ - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - char name[32 + 2]; - - struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH07]; - u32 tmp = 0; - - snprintf(name, sizeof(name), "%s/2", dev->name); - printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", - dev->name); - cx25821_call_all(dev, core, log_status); - - tmp = cx_read(sram_ch->dma_ctl); - printk(KERN_INFO "Video input 7 is %s\n", - (tmp & 0x11) ? "streaming" : "stopped"); - printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", - dev->name); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - return cx25821_set_control(dev, ctl, SRAM_CH07); -} - -// exported stuff -static const struct v4l2_file_operations video_fops = { - .owner = THIS_MODULE, - .open = video_open, - .release = video_release, - .read = video_read, - .poll = video_poll, - .mmap = cx25821_video_mmap, - .ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = cx25821_vidioc_querycap, - .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = cx25821_vidioc_reqbufs, - .vidioc_querybuf = cx25821_vidioc_querybuf, - .vidioc_qbuf = cx25821_vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, -#ifdef TUNER_FLAG - .vidioc_s_std = cx25821_vidioc_s_std, - .vidioc_querystd = cx25821_vidioc_querystd, -#endif - .vidioc_cropcap = cx25821_vidioc_cropcap, - .vidioc_s_crop = cx25821_vidioc_s_crop, - .vidioc_g_crop = cx25821_vidioc_g_crop, - .vidioc_enum_input = cx25821_vidioc_enum_input, - .vidioc_g_input = cx25821_vidioc_g_input, - .vidioc_s_input = cx25821_vidioc_s_input, - .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = cx25821_vidioc_queryctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = cx25821_vidioc_g_priority, - .vidioc_s_priority = cx25821_vidioc_s_priority, -#ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = cx25821_vidiocgmbuf, -#endif -#ifdef TUNER_FLAG - .vidioc_g_tuner = cx25821_vidioc_g_tuner, - .vidioc_s_tuner = cx25821_vidioc_s_tuner, - .vidioc_g_frequency = cx25821_vidioc_g_frequency, - .vidioc_s_frequency = cx25821_vidioc_s_frequency, -#endif -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = cx25821_vidioc_g_register, - .vidioc_s_register = cx25821_vidioc_s_register, -#endif -}; - -struct video_device cx25821_video_template7 = { - .name = "cx25821-video", - .fops = &video_fops, - .ioctl_ops = &video_ioctl_ops, - .tvnorms = CX25821_NORMS, - .current_norm = V4L2_STD_NTSC_M, -}; diff --git a/drivers/staging/cx25821/cx25821-videoioctl.c b/drivers/staging/cx25821/cx25821-videoioctl.c deleted file mode 100644 index d16807d..0000000 --- a/drivers/staging/cx25821/cx25821-videoioctl.c +++ /dev/null @@ -1,480 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> - * Based on Steven Toth <stoth@linuxtv.org> cx23885 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. - */ - -#include "cx25821-video.h" - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct cx25821_buffer *buf = - container_of(vb, struct cx25821_buffer, vb); - struct cx25821_buffer *prev; - struct cx25821_fh *fh = vq->priv_data; - struct cx25821_dev *dev = fh->dev; - struct cx25821_dmaqueue *q = &dev->vidq[VIDEO_IOCTL_CH]; - - /* add jump to stopper */ - buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); - buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ - - dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); - - if (!list_empty(&q->queued)) { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, - buf->vb.i); - - } else if (list_empty(&q->active)) { - list_add_tail(&buf->vb.queue, &q->active); - cx25821_start_video_dma(dev, q, buf, - &dev->sram_channels[VIDEO_IOCTL_CH]); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - dprintk(2, - "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", - buf, buf->vb.i, buf->count, q->count); - } else { - prev = - list_entry(q->active.prev, struct cx25821_buffer, vb.queue); - if (prev->vb.width == buf->vb.width - && prev->vb.height == buf->vb.height - && prev->fmt == buf->fmt) { - list_add_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - - /* 64 bit bits 63-32 */ - prev->risc.jmp[2] = cpu_to_le32(0); - dprintk(2, - "[%p/%d] buffer_queue - append to active, buf->count=%d\n", - buf, buf->vb.i, buf->count); - - } else { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, - buf->vb.i); - } - } - - if (list_empty(&q->active)) { - dprintk(2, "active queue empty!\n"); - } -} - -static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = cx25821_buffer_setup, - .buf_prepare = cx25821_buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = cx25821_buffer_release, -}; - -static int video_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct cx25821_dev *dev = video_drvdata(file); - struct cx25821_fh *fh; - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - u32 pix_format; - - printk("open dev=%s type=%s\n", video_device_node_name(vdev), - v4l2_type_names[type]); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) - return -ENOMEM; - - lock_kernel(); - - file->private_data = fh; - fh->dev = dev; - fh->type = type; - fh->width = 720; - - if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) - fh->height = 576; - else - fh->height = 480; - - dev->channel_opened = VIDEO_IOCTL_CH; - pix_format = V4L2_PIX_FMT_YUYV; - fh->fmt = format_by_fourcc(pix_format); - - v4l2_prio_open(&dev->prio, &fh->prio); - - videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct cx25821_buffer), fh); - - dprintk(1, "post videobuf_queue_init()\n"); - unlock_kernel(); - - return 0; -} - -static ssize_t video_read(struct file *file, char __user * data, size_t count, - loff_t * ppos) -{ - struct cx25821_fh *fh = file->private_data; - - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO_IOCTL)) - return -EBUSY; - - return videobuf_read_one(&fh->vidq, data, count, ppos, - file->f_flags & O_NONBLOCK); - - default: - BUG(); - return 0; - } -} - -static unsigned int video_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_buffer *buf; - - if (cx25821_res_check(fh, RESOURCE_VIDEO_IOCTL)) { - /* streaming capture */ - if (list_empty(&fh->vidq.stream)) - return POLLERR; - buf = list_entry(fh->vidq.stream.next, - struct cx25821_buffer, vb.stream); - } else { - /* read() capture */ - buf = (struct cx25821_buffer *)fh->vidq.read_buf; - if (NULL == buf) - return POLLERR; - } - - poll_wait(file, &buf->vb.done, wait); - if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) - return POLLIN | POLLRDNORM; - - return 0; -} - -static int video_release(struct file *file) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_dev *dev = fh->dev; - - /* stop video capture */ - if (cx25821_res_check(fh, RESOURCE_VIDEO_IOCTL)) { - videobuf_queue_cancel(&fh->vidq); - cx25821_res_free(dev, fh, RESOURCE_VIDEO_IOCTL); - } - - if (fh->vidq.read_buf) { - cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); - kfree(fh->vidq.read_buf); - } - - videobuf_mmap_free(&fh->vidq); - - v4l2_prio_close(&dev->prio, fh->prio); - - file->private_data = NULL; - kfree(fh); - - return 0; -} - -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - - if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { - return -EINVAL; - } - - if (unlikely(i != fh->type)) { - return -EINVAL; - } - - if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO_IOCTL)))) { - return -EBUSY; - } - - return videobuf_streamon(get_queue(fh)); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - int err, res; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (i != fh->type) - return -EINVAL; - - res = cx25821_get_resource(fh, RESOURCE_VIDEO_IOCTL); - err = videobuf_streamoff(get_queue(fh)); - if (err < 0) - return err; - cx25821_res_free(dev, fh, res); - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - dprintk(2, "%s()\n", __func__); - err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); - - if (0 != err) - return err; - fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - fh->width = f->fmt.pix.width; - fh->height = f->fmt.pix.height; - fh->vidq.field = f->fmt.pix.field; - dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, - fh->height, fh->vidq.field); - cx25821_call_all(dev, video, s_fmt, f); - return 0; -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct cx25821_fh *fh = priv; - return videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); -} - -static long video_ioctl_set(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_dev *dev = fh->dev; - struct downstream_user_struct *data_from_user; - int command; - int width = 720; - int selected_channel = 0, pix_format = 0, i = 0; - int cif_enable = 0, cif_width = 0; - u32 value = 0; - - data_from_user = (struct downstream_user_struct *)arg; - - if (!data_from_user) { - printk("cx25821 in %s(): User data is INVALID. Returning.\n", - __func__); - return 0; - } - - command = data_from_user->command; - - if (command != SET_VIDEO_STD && command != SET_PIXEL_FORMAT - && command != ENABLE_CIF_RESOLUTION && command != REG_READ - && command != REG_WRITE && command != MEDUSA_READ - && command != MEDUSA_WRITE) { - return 0; - } - - switch (command) { - case SET_VIDEO_STD: - dev->tvnorm = - !strcmp(data_from_user->vid_stdname, - "PAL") ? V4L2_STD_PAL_BG : V4L2_STD_NTSC_M; - medusa_set_videostandard(dev); - break; - - case SET_PIXEL_FORMAT: - selected_channel = data_from_user->decoder_select; - pix_format = data_from_user->pixel_format; - - if (!(selected_channel <= 7 && selected_channel >= 0)) { - selected_channel -= 4; - selected_channel = selected_channel % 8; - } - - if (selected_channel >= 0) - cx25821_set_pixel_format(dev, selected_channel, - pix_format); - - break; - - case ENABLE_CIF_RESOLUTION: - selected_channel = data_from_user->decoder_select; - cif_enable = data_from_user->cif_resolution_enable; - cif_width = data_from_user->cif_width; - - if (cif_enable) { - if (dev->tvnorm & V4L2_STD_PAL_BG - || dev->tvnorm & V4L2_STD_PAL_DK) - width = 352; - else - width = (cif_width == 320 - || cif_width == 352) ? cif_width : 320; - } - - if (!(selected_channel <= 7 && selected_channel >= 0)) { - selected_channel -= 4; - selected_channel = selected_channel % 8; - } - - if (selected_channel <= 7 && selected_channel >= 0) { - dev->use_cif_resolution[selected_channel] = cif_enable; - dev->cif_width[selected_channel] = width; - } else { - for (i = 0; i < VID_CHANNEL_NUM; i++) { - dev->use_cif_resolution[i] = cif_enable; - dev->cif_width[i] = width; - } - } - - medusa_set_resolution(dev, width, selected_channel); - break; - case REG_READ: - data_from_user->reg_data = cx_read(data_from_user->reg_address); - break; - case REG_WRITE: - cx_write(data_from_user->reg_address, data_from_user->reg_data); - break; - case MEDUSA_READ: - value = - cx25821_i2c_read(&dev->i2c_bus[0], - (u16) data_from_user->reg_address, - &data_from_user->reg_data); - break; - case MEDUSA_WRITE: - cx25821_i2c_write(&dev->i2c_bus[0], - (u16) data_from_user->reg_address, - data_from_user->reg_data); - break; - } - - return 0; -} - -static int vidioc_log_status(struct file *file, void *priv) -{ - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - char name[32 + 2]; - - snprintf(name, sizeof(name), "%s/2", dev->name); - printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", - dev->name); - cx25821_call_all(dev, core, log_status); - printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", - dev->name); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - return 0; -} - -// exported stuff -static const struct v4l2_file_operations video_fops = { - .owner = THIS_MODULE, - .open = video_open, - .release = video_release, - .read = video_read, - .poll = video_poll, - .mmap = cx25821_video_mmap, - .ioctl = video_ioctl_set, -}; - -static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = cx25821_vidioc_querycap, - .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = cx25821_vidioc_reqbufs, - .vidioc_querybuf = cx25821_vidioc_querybuf, - .vidioc_qbuf = cx25821_vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, -#ifdef TUNER_FLAG - .vidioc_s_std = cx25821_vidioc_s_std, - .vidioc_querystd = cx25821_vidioc_querystd, -#endif - .vidioc_cropcap = cx25821_vidioc_cropcap, - .vidioc_s_crop = cx25821_vidioc_s_crop, - .vidioc_g_crop = cx25821_vidioc_g_crop, - .vidioc_enum_input = cx25821_vidioc_enum_input, - .vidioc_g_input = cx25821_vidioc_g_input, - .vidioc_s_input = cx25821_vidioc_s_input, - .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = cx25821_vidioc_queryctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = cx25821_vidioc_g_priority, - .vidioc_s_priority = cx25821_vidioc_s_priority, -#ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = cx25821_vidiocgmbuf, -#endif -#ifdef TUNER_FLAG - .vidioc_g_tuner = cx25821_vidioc_g_tuner, - .vidioc_s_tuner = cx25821_vidioc_s_tuner, - .vidioc_g_frequency = cx25821_vidioc_g_frequency, - .vidioc_s_frequency = cx25821_vidioc_s_frequency, -#endif -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = cx25821_vidioc_g_register, - .vidioc_s_register = cx25821_vidioc_s_register, -#endif -}; - -struct video_device cx25821_videoioctl_template = { - .name = "cx25821-videoioctl", - .fops = &video_fops, - .ioctl_ops = &video_ioctl_ops, - .tvnorms = CX25821_NORMS, - .current_norm = V4L2_STD_NTSC_M, -}; diff --git a/drivers/staging/cx25821/cx25821-vidups10.c b/drivers/staging/cx25821/cx25821-vidups10.c deleted file mode 100644 index c746a17..0000000 --- a/drivers/staging/cx25821/cx25821-vidups10.c +++ /dev/null @@ -1,418 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> - * Based on Steven Toth <stoth@linuxtv.org> cx23885 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. - */ - -#include "cx25821-video.h" - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct cx25821_buffer *buf = - container_of(vb, struct cx25821_buffer, vb); - struct cx25821_buffer *prev; - struct cx25821_fh *fh = vq->priv_data; - struct cx25821_dev *dev = fh->dev; - struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH10]; - - /* add jump to stopper */ - buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); - buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ - - dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); - - if (!list_empty(&q->queued)) { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, - buf->vb.i); - - } else if (list_empty(&q->active)) { - list_add_tail(&buf->vb.queue, &q->active); - cx25821_start_video_dma(dev, q, buf, - &dev->sram_channels[SRAM_CH10]); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - dprintk(2, - "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", - buf, buf->vb.i, buf->count, q->count); - } else { - prev = - list_entry(q->active.prev, struct cx25821_buffer, vb.queue); - if (prev->vb.width == buf->vb.width - && prev->vb.height == buf->vb.height - && prev->fmt == buf->fmt) { - list_add_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - - /* 64 bit bits 63-32 */ - prev->risc.jmp[2] = cpu_to_le32(0); - dprintk(2, - "[%p/%d] buffer_queue - append to active, buf->count=%d\n", - buf, buf->vb.i, buf->count); - - } else { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, - buf->vb.i); - } - } - - if (list_empty(&q->active)) { - dprintk(2, "active queue empty!\n"); - } -} - -static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = cx25821_buffer_setup, - .buf_prepare = cx25821_buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = cx25821_buffer_release, -}; - -static int video_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct cx25821_dev *dev = video_drvdata(file); - struct cx25821_fh *fh; - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - printk("open dev=%s type=%s\n", video_device_node_name(vdev), - v4l2_type_names[type]); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) - return -ENOMEM; - - lock_kernel(); - - file->private_data = fh; - fh->dev = dev; - fh->type = type; - fh->width = 720; - - if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) - fh->height = 576; - else - fh->height = 480; - - dev->channel_opened = 9; - fh->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV); - - v4l2_prio_open(&dev->prio, &fh->prio); - - videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct cx25821_buffer), fh); - - dprintk(1, "post videobuf_queue_init()\n"); - unlock_kernel(); - - return 0; -} - -static ssize_t video_read(struct file *file, char __user * data, size_t count, - loff_t * ppos) -{ - struct cx25821_fh *fh = file->private_data; - - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO10)) - return -EBUSY; - - return videobuf_read_one(&fh->vidq, data, count, ppos, - file->f_flags & O_NONBLOCK); - - default: - BUG(); - return 0; - } -} - -static unsigned int video_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_buffer *buf; - - if (cx25821_res_check(fh, RESOURCE_VIDEO10)) { - /* streaming capture */ - if (list_empty(&fh->vidq.stream)) - return POLLERR; - buf = list_entry(fh->vidq.stream.next, - struct cx25821_buffer, vb.stream); - } else { - /* read() capture */ - buf = (struct cx25821_buffer *)fh->vidq.read_buf; - if (NULL == buf) - return POLLERR; - } - - poll_wait(file, &buf->vb.done, wait); - if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) - return POLLIN | POLLRDNORM; - return 0; -} - -static int video_release(struct file *file) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_dev *dev = fh->dev; - - //stop the risc engine and fifo - //cx_write(channel10->dma_ctl, 0); - - /* stop video capture */ - if (cx25821_res_check(fh, RESOURCE_VIDEO10)) { - videobuf_queue_cancel(&fh->vidq); - cx25821_res_free(dev, fh, RESOURCE_VIDEO10); - } - - if (fh->vidq.read_buf) { - cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); - kfree(fh->vidq.read_buf); - } - - videobuf_mmap_free(&fh->vidq); - - v4l2_prio_close(&dev->prio, fh->prio); - - file->private_data = NULL; - kfree(fh); - - return 0; -} - -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - - if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { - return -EINVAL; - } - - if (unlikely(i != fh->type)) { - return -EINVAL; - } - - if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO10)))) { - return -EBUSY; - } - - return videobuf_streamon(get_queue(fh)); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - int err, res; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (i != fh->type) - return -EINVAL; - - res = cx25821_get_resource(fh, RESOURCE_VIDEO10); - err = videobuf_streamoff(get_queue(fh)); - if (err < 0) - return err; - cx25821_res_free(dev, fh, res); - return 0; -} - -static long video_ioctl_upstream10(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_dev *dev = fh->dev; - int command = 0; - struct upstream_user_struct *data_from_user; - - data_from_user = (struct upstream_user_struct *)arg; - - if (!data_from_user) { - printk - ("cx25821 in %s(): Upstream data is INVALID. Returning.\n", - __func__); - return 0; - } - - command = data_from_user->command; - - if (command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO) { - return 0; - } - - dev->input_filename_ch2 = data_from_user->input_filename; - dev->input_audiofilename = data_from_user->input_filename; - dev->vid_stdname_ch2 = data_from_user->vid_stdname; - dev->pixel_format_ch2 = data_from_user->pixel_format; - dev->channel_select_ch2 = data_from_user->channel_select; - dev->command_ch2 = data_from_user->command; - - switch (command) { - case UPSTREAM_START_VIDEO: - cx25821_start_upstream_video_ch2(dev, data_from_user); - break; - - case UPSTREAM_STOP_VIDEO: - cx25821_stop_upstream_video_ch2(dev); - break; - } - - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - dprintk(2, "%s()\n", __func__); - err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); - - if (0 != err) - return err; - fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - fh->width = f->fmt.pix.width; - fh->height = f->fmt.pix.height; - fh->vidq.field = f->fmt.pix.field; - dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, - fh->height, fh->vidq.field); - cx25821_call_all(dev, video, s_fmt, f); - return 0; -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct cx25821_fh *fh = priv; - return videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); -} - -static int vidioc_log_status(struct file *file, void *priv) -{ - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - char name[32 + 2]; - - snprintf(name, sizeof(name), "%s/2", dev->name); - printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", - dev->name); - cx25821_call_all(dev, core, log_status); - printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", - dev->name); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - return 0; -} - -//exported stuff -static const struct v4l2_file_operations video_fops = { - .owner = THIS_MODULE, - .open = video_open, - .release = video_release, - .read = video_read, - .poll = video_poll, - .mmap = cx25821_video_mmap, - .ioctl = video_ioctl_upstream10, -}; - -static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = cx25821_vidioc_querycap, - .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = cx25821_vidioc_reqbufs, - .vidioc_querybuf = cx25821_vidioc_querybuf, - .vidioc_qbuf = cx25821_vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, -#ifdef TUNER_FLAG - .vidioc_s_std = cx25821_vidioc_s_std, - .vidioc_querystd = cx25821_vidioc_querystd, -#endif - .vidioc_cropcap = cx25821_vidioc_cropcap, - .vidioc_s_crop = cx25821_vidioc_s_crop, - .vidioc_g_crop = cx25821_vidioc_g_crop, - .vidioc_enum_input = cx25821_vidioc_enum_input, - .vidioc_g_input = cx25821_vidioc_g_input, - .vidioc_s_input = cx25821_vidioc_s_input, - .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = cx25821_vidioc_queryctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = cx25821_vidioc_g_priority, - .vidioc_s_priority = cx25821_vidioc_s_priority, -#ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = cx25821_vidiocgmbuf, -#endif -#ifdef TUNER_FLAG - .vidioc_g_tuner = cx25821_vidioc_g_tuner, - .vidioc_s_tuner = cx25821_vidioc_s_tuner, - .vidioc_g_frequency = cx25821_vidioc_g_frequency, - .vidioc_s_frequency = cx25821_vidioc_s_frequency, -#endif -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = cx25821_vidioc_g_register, - .vidioc_s_register = cx25821_vidioc_s_register, -#endif -}; - -struct video_device cx25821_video_template10 = { - .name = "cx25821-upstream10", - .fops = &video_fops, - .ioctl_ops = &video_ioctl_ops, - .tvnorms = CX25821_NORMS, - .current_norm = V4L2_STD_NTSC_M, -}; diff --git a/drivers/staging/cx25821/cx25821-vidups9.c b/drivers/staging/cx25821/cx25821-vidups9.c deleted file mode 100644 index 466e0f3..0000000 --- a/drivers/staging/cx25821/cx25821-vidups9.c +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Driver for the Conexant CX25821 PCIe bridge - * - * Copyright (C) 2009 Conexant Systems Inc. - * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> - * Based on Steven Toth <stoth@linuxtv.org> cx23885 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. - */ - -#include "cx25821-video.h" - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct cx25821_buffer *buf = - container_of(vb, struct cx25821_buffer, vb); - struct cx25821_buffer *prev; - struct cx25821_fh *fh = vq->priv_data; - struct cx25821_dev *dev = fh->dev; - struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH09]; - - /* add jump to stopper */ - buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); - buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ - - dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); - - if (!list_empty(&q->queued)) { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, - buf->vb.i); - - } else if (list_empty(&q->active)) { - list_add_tail(&buf->vb.queue, &q->active); - cx25821_start_video_dma(dev, q, buf, - &dev->sram_channels[SRAM_CH09]); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - dprintk(2, - "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", - buf, buf->vb.i, buf->count, q->count); - } else { - prev = - list_entry(q->active.prev, struct cx25821_buffer, vb.queue); - if (prev->vb.width == buf->vb.width - && prev->vb.height == buf->vb.height - && prev->fmt == buf->fmt) { - list_add_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - - /* 64 bit bits 63-32 */ - prev->risc.jmp[2] = cpu_to_le32(0); - dprintk(2, - "[%p/%d] buffer_queue - append to active, buf->count=%d\n", - buf, buf->vb.i, buf->count); - - } else { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, - buf->vb.i); - } - } - - if (list_empty(&q->active)) { - dprintk(2, "active queue empty!\n"); - } -} - -static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = cx25821_buffer_setup, - .buf_prepare = cx25821_buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = cx25821_buffer_release, -}; - -static int video_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct cx25821_dev *dev = video_drvdata(file); - struct cx25821_fh *fh; - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - printk("open dev=%s type=%s\n", video_device_node_name(vdev), - v4l2_type_names[type]); - - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) - return -ENOMEM; - - lock_kernel(); - - file->private_data = fh; - fh->dev = dev; - fh->type = type; - fh->width = 720; - - if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) - fh->height = 576; - else - fh->height = 480; - - dev->channel_opened = 8; - fh->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV); - - v4l2_prio_open(&dev->prio, &fh->prio); - - videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, - &dev->pci->dev, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct cx25821_buffer), fh); - - dprintk(1, "post videobuf_queue_init()\n"); - unlock_kernel(); - - return 0; -} - -static ssize_t video_read(struct file *file, char __user * data, size_t count, - loff_t * ppos) -{ - struct cx25821_fh *fh = file->private_data; - - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (cx25821_res_locked(fh->dev, RESOURCE_VIDEO9)) - return -EBUSY; - - return videobuf_read_one(&fh->vidq, data, count, ppos, - file->f_flags & O_NONBLOCK); - - default: - BUG(); - return 0; - } -} - -static unsigned int video_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_buffer *buf; - - if (cx25821_res_check(fh, RESOURCE_VIDEO9)) { - /* streaming capture */ - if (list_empty(&fh->vidq.stream)) - return POLLERR; - buf = list_entry(fh->vidq.stream.next, - struct cx25821_buffer, vb.stream); - } else { - /* read() capture */ - buf = (struct cx25821_buffer *)fh->vidq.read_buf; - if (NULL == buf) - return POLLERR; - } - - poll_wait(file, &buf->vb.done, wait); - if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) - return POLLIN | POLLRDNORM; - return 0; -} - -static int video_release(struct file *file) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_dev *dev = fh->dev; - - //stop the risc engine and fifo - //cx_write(channel9->dma_ctl, 0); - - /* stop video capture */ - if (cx25821_res_check(fh, RESOURCE_VIDEO9)) { - videobuf_queue_cancel(&fh->vidq); - cx25821_res_free(dev, fh, RESOURCE_VIDEO9); - } - - if (fh->vidq.read_buf) { - cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); - kfree(fh->vidq.read_buf); - } - - videobuf_mmap_free(&fh->vidq); - - v4l2_prio_close(&dev->prio, fh->prio); - - file->private_data = NULL; - kfree(fh); - - return 0; -} - -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - - if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { - return -EINVAL; - } - - if (unlikely(i != fh->type)) { - return -EINVAL; - } - - if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, RESOURCE_VIDEO9)))) { - return -EBUSY; - } - - return videobuf_streamon(get_queue(fh)); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = fh->dev; - int err, res; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (i != fh->type) - return -EINVAL; - - res = cx25821_get_resource(fh, RESOURCE_VIDEO9); - err = videobuf_streamoff(get_queue(fh)); - if (err < 0) - return err; - cx25821_res_free(dev, fh, res); - return 0; -} - -static long video_ioctl_upstream9(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct cx25821_fh *fh = file->private_data; - struct cx25821_dev *dev = fh->dev; - int command = 0; - struct upstream_user_struct *data_from_user; - - data_from_user = (struct upstream_user_struct *)arg; - - if (!data_from_user) { - printk - ("cx25821 in %s(): Upstream data is INVALID. Returning.\n", - __func__); - return 0; - } - - command = data_from_user->command; - - if (command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO) { - return 0; - } - - dev->input_filename = data_from_user->input_filename; - dev->input_audiofilename = data_from_user->input_filename; - dev->vid_stdname = data_from_user->vid_stdname; - dev->pixel_format = data_from_user->pixel_format; - dev->channel_select = data_from_user->channel_select; - dev->command = data_from_user->command; - - switch (command) { - case UPSTREAM_START_VIDEO: - cx25821_start_upstream_video_ch1(dev, data_from_user); - break; - - case UPSTREAM_STOP_VIDEO: - cx25821_stop_upstream_video_ch1(dev); - break; - } - - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cx25821_fh *fh = priv; - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - int err; - - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - dprintk(2, "%s()\n", __func__); - err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); - - if (0 != err) - return err; - fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - fh->width = f->fmt.pix.width; - fh->height = f->fmt.pix.height; - fh->vidq.field = f->fmt.pix.field; - dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, - fh->height, fh->vidq.field); - cx25821_call_all(dev, video, s_fmt, f); - return 0; -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct cx25821_fh *fh = priv; - return videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); -} -static int vidioc_log_status(struct file *file, void *priv) -{ - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - char name[32 + 2]; - - snprintf(name, sizeof(name), "%s/2", dev->name); - printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", - dev->name); - cx25821_call_all(dev, core, log_status); - printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", - dev->name); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctl) -{ - struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; - struct cx25821_fh *fh = priv; - int err; - if (fh) { - err = v4l2_prio_check(&dev->prio, fh->prio); - if (0 != err) - return err; - } - - return 0; -} - -// exported stuff -static const struct v4l2_file_operations video_fops = { - .owner = THIS_MODULE, - .open = video_open, - .release = video_release, - .read = video_read, - .poll = video_poll, - .mmap = cx25821_video_mmap, - .ioctl = video_ioctl_upstream9, -}; - -static const struct v4l2_ioctl_ops video_ioctl_ops = { - .vidioc_querycap = cx25821_vidioc_querycap, - .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = cx25821_vidioc_reqbufs, - .vidioc_querybuf = cx25821_vidioc_querybuf, - .vidioc_qbuf = cx25821_vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, -#ifdef TUNER_FLAG - .vidioc_s_std = cx25821_vidioc_s_std, - .vidioc_querystd = cx25821_vidioc_querystd, -#endif - .vidioc_cropcap = cx25821_vidioc_cropcap, - .vidioc_s_crop = cx25821_vidioc_s_crop, - .vidioc_g_crop = cx25821_vidioc_g_crop, - .vidioc_enum_input = cx25821_vidioc_enum_input, - .vidioc_g_input = cx25821_vidioc_g_input, - .vidioc_s_input = cx25821_vidioc_s_input, - .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_queryctrl = cx25821_vidioc_queryctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_log_status = vidioc_log_status, - .vidioc_g_priority = cx25821_vidioc_g_priority, - .vidioc_s_priority = cx25821_vidioc_s_priority, -#ifdef CONFIG_VIDEO_V4L1_COMPAT - .vidiocgmbuf = cx25821_vidiocgmbuf, -#endif -#ifdef TUNER_FLAG - .vidioc_g_tuner = cx25821_vidioc_g_tuner, - .vidioc_s_tuner = cx25821_vidioc_s_tuner, - .vidioc_g_frequency = cx25821_vidioc_g_frequency, - .vidioc_s_frequency = cx25821_vidioc_s_frequency, -#endif -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = cx25821_vidioc_g_register, - .vidioc_s_register = cx25821_vidioc_s_register, -#endif -}; - -struct video_device cx25821_video_template9 = { - .name = "cx25821-upstream9", - .fops = &video_fops, - .ioctl_ops = &video_ioctl_ops, - .tvnorms = CX25821_NORMS, - .current_norm = V4L2_STD_NTSC_M, -}; diff --git a/drivers/staging/cx25821/cx25821.h b/drivers/staging/cx25821/cx25821.h index cf2286d..1b628f6 100644 --- a/drivers/staging/cx25821/cx25821.h +++ b/drivers/staging/cx25821/cx25821.h @@ -61,7 +61,7 @@ #define FALSE 0 #define LINE_SIZE_D1 1440 -// Number of decoders and encoders +/* Number of decoders and encoders */ #define MAX_DECODERS 8 #define MAX_ENCODERS 2 #define QUAD_DECODERS 4 @@ -139,6 +139,7 @@ struct cx25821_fh { /* video capture */ struct cx25821_fmt *fmt; unsigned int width, height; + int channel_id; /* vbi capture */ struct videobuf_queue vidq; @@ -236,13 +237,34 @@ struct cx25821_data { struct sram_channel *channel; }; +struct cx25821_channel { + struct v4l2_prio_state prio; + + int ctl_bright; + int ctl_contrast; + int ctl_hue; + int ctl_saturation; + + struct cx25821_data timeout_data; + + struct video_device *video_dev; + struct cx25821_dmaqueue vidq; + + struct sram_channel *sram_channels; + + struct mutex lock; + int resources; + + int pixel_formats; + int use_cif_resolution; + int cif_width; +}; + struct cx25821_dev { struct list_head devlist; atomic_t refcount; struct v4l2_device v4l2_dev; - struct v4l2_prio_state prio; - /* pci stuff */ struct pci_dev *pci; unsigned char pci_rev, pci_lat; @@ -261,13 +283,12 @@ struct cx25821_dev { int nr; struct mutex lock; + struct cx25821_channel channels[MAX_VID_CHANNEL_NUM]; + /* board details */ unsigned int board; char name[32]; - /* sram configuration */ - struct sram_channel *sram_channels; - /* Analog video */ u32 resources; unsigned int input; @@ -282,13 +303,6 @@ struct cx25821_dev { unsigned char videc_addr; unsigned short _max_num_decoders; - int ctl_bright; - int ctl_contrast; - int ctl_hue; - int ctl_saturation; - - struct cx25821_data timeout_data[MAX_VID_CHANNEL_NUM]; - /* Analog Audio Upstream */ int _audio_is_running; int _audiopixel_format; @@ -297,7 +311,7 @@ struct cx25821_dev { int _audio_lines_count; int _audioframe_count; int _audio_upstream_channel_select; - int _last_index_irq; //The last interrupt index processed. + int _last_index_irq; /* The last interrupt index processed. */ __le32 *_risc_audio_jmp_addr; __le32 *_risc_virt_start_addr; @@ -313,12 +327,10 @@ struct cx25821_dev { /* V4l */ u32 freq; - struct video_device *video_dev[MAX_VID_CHANNEL_NUM]; struct video_device *vbi_dev; struct video_device *radio_dev; struct video_device *ioctl_dev; - struct cx25821_dmaqueue vidq[MAX_VID_CHANNEL_NUM]; spinlock_t slock; /* Video Upstream */ @@ -401,9 +413,6 @@ struct cx25821_dev { int pixel_format; int channel_select; int command; - int pixel_formats[VID_CHANNEL_NUM]; - int use_cif_resolution[VID_CHANNEL_NUM]; - int cif_width[VID_CHANNEL_NUM]; int channel_opened; }; @@ -482,7 +491,7 @@ struct sram_channel { u32 fld_aud_fifo_en; u32 fld_aud_risc_en; - //For Upstream Video + /* For Upstream Video */ u32 vid_fmt_ctl; u32 vid_active_ctl1; u32 vid_active_ctl2; diff --git a/drivers/staging/lirc/Kconfig b/drivers/staging/lirc/Kconfig new file mode 100644 index 0000000..968c2ade --- /dev/null +++ b/drivers/staging/lirc/Kconfig @@ -0,0 +1,110 @@ +# +# LIRC driver(s) configuration +# +menuconfig LIRC_STAGING + bool "Linux Infrared Remote Control IR receiver/transmitter drivers" + help + Say Y here, and all supported Linux Infrared Remote Control IR and + RF receiver and transmitter drivers will be displayed. When paired + with a remote control and the lirc daemon, the receiver drivers + allow control of your Linux system via remote control. + +if LIRC_STAGING + +config LIRC_BT829 + tristate "BT829 based hardware" + depends on LIRC_STAGING + help + Driver for the IR interface on BT829-based hardware + +config LIRC_ENE0100 + tristate "ENE KB3924/ENE0100 CIR Port Reciever" + depends on LIRC_STAGING + help + This is a driver for CIR port handled by ENE KB3924 embedded + controller found on some notebooks. + It appears on PNP list as ENE0100. + +config LIRC_I2C + tristate "I2C Based IR Receivers" + depends on LIRC_STAGING + help + Driver for I2C-based IR receivers, such as those commonly + found onboard Hauppauge PVR-150/250/350 video capture cards + +config LIRC_IGORPLUGUSB + tristate "Igor Cesko's USB IR Receiver" + depends on LIRC_STAGING && USB + help + Driver for Igor Cesko's USB IR Receiver + +config LIRC_IMON + tristate "Legacy SoundGraph iMON Receiver and Display" + depends on LIRC_STAGING + help + Driver for the original SoundGraph iMON IR Receiver and Display + + Current generation iMON devices use the input layer imon driver. + +config LIRC_IT87 + tristate "ITE IT87XX CIR Port Receiver" + depends on LIRC_STAGING + help + Driver for the ITE IT87xx IR Receiver + +config LIRC_ITE8709 + tristate "ITE8709 CIR Port Receiver" + depends on LIRC_STAGING && PNP + help + Driver for the ITE8709 IR Receiver + +config LIRC_PARALLEL + tristate "Homebrew Parallel Port Receiver" + depends on LIRC_STAGING && !SMP + help + Driver for Homebrew Parallel Port Receivers + +config LIRC_SASEM + tristate "Sasem USB IR Remote" + depends on LIRC_STAGING + help + Driver for the Sasem OnAir Remocon-V or Dign HV5 HTPC IR/VFD Module + +config LIRC_SERIAL + tristate "Homebrew Serial Port Receiver" + depends on LIRC_STAGING + help + Driver for Homebrew Serial Port Receivers + +config LIRC_SERIAL_TRANSMITTER + bool "Serial Port Transmitter" + default y + depends on LIRC_SERIAL + help + Serial Port Transmitter support + +config LIRC_SIR + tristate "Built-in SIR IrDA port" + depends on LIRC_STAGING + help + Driver for the SIR IrDA port + +config LIRC_STREAMZAP + tristate "Streamzap PC Receiver" + depends on LIRC_STAGING + help + Driver for the Streamzap PC Receiver + +config LIRC_TTUSBIR + tristate "Technotrend USB IR Receiver" + depends on LIRC_STAGING && USB + help + Driver for the Technotrend USB IR Receiver + +config LIRC_ZILOG + tristate "Zilog/Hauppauge IR Transmitter" + depends on LIRC_STAGING + help + Driver for the Zilog/Hauppauge IR Transmitter, found on + PVR-150/500, HVR-1200/1250/1700/1800, HD-PVR and other cards +endif diff --git a/drivers/staging/lirc/Makefile b/drivers/staging/lirc/Makefile new file mode 100644 index 0000000..a019182 --- /dev/null +++ b/drivers/staging/lirc/Makefile @@ -0,0 +1,19 @@ +# Makefile for the lirc drivers. +# + +# Each configuration option enables a list of files. + +obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o +obj-$(CONFIG_LIRC_ENE0100) += lirc_ene0100.o +obj-$(CONFIG_LIRC_I2C) += lirc_i2c.o +obj-$(CONFIG_LIRC_IGORPLUGUSB) += lirc_igorplugusb.o +obj-$(CONFIG_LIRC_IMON) += lirc_imon.o +obj-$(CONFIG_LIRC_IT87) += lirc_it87.o +obj-$(CONFIG_LIRC_ITE8709) += lirc_ite8709.o +obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o +obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o +obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o +obj-$(CONFIG_LIRC_SIR) += lirc_sir.o +obj-$(CONFIG_LIRC_STREAMZAP) += lirc_streamzap.o +obj-$(CONFIG_LIRC_TTUSBIR) += lirc_ttusbir.o +obj-$(CONFIG_LIRC_ZILOG) += lirc_zilog.o diff --git a/drivers/staging/lirc/TODO b/drivers/staging/lirc/TODO new file mode 100644 index 0000000..b6cb593 --- /dev/null +++ b/drivers/staging/lirc/TODO @@ -0,0 +1,8 @@ +- All drivers should either be ported to ir-core, or dropped entirely + (see drivers/media/IR/mceusb.c vs. lirc_mceusb.c in lirc cvs for an + example of a previously completed port). + +Please send patches to: +Jarod Wilson <jarod@wilsonet.com> +Greg Kroah-Hartman <greg@kroah.com> + diff --git a/drivers/staging/lirc/TODO.lirc_i2c b/drivers/staging/lirc/TODO.lirc_i2c new file mode 100644 index 0000000..1f0a6ff --- /dev/null +++ b/drivers/staging/lirc/TODO.lirc_i2c @@ -0,0 +1,3 @@ +lirc_i2c provides support for some drivers that have already a RC +driver under drivers/media/video. It should be integrated into those +drivers, in special with drivers/media/video/ir-kbd-i2c.c. diff --git a/drivers/staging/lirc/lirc_bt829.c b/drivers/staging/lirc/lirc_bt829.c new file mode 100644 index 0000000..3388102 --- /dev/null +++ b/drivers/staging/lirc/lirc_bt829.c @@ -0,0 +1,383 @@ +/* + * Remote control driver for the TV-card based on bt829 + * + * by Leonid Froenchenko <lfroen@galileo.co.il> + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/threads.h> +#include <linux/sched.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/delay.h> + +#include <media/lirc_dev.h> + +static int poll_main(void); +static int atir_init_start(void); + +static void write_index(unsigned char index, unsigned int value); +static unsigned int read_index(unsigned char index); + +static void do_i2c_start(void); +static void do_i2c_stop(void); + +static void seems_wr_byte(unsigned char al); +static unsigned char seems_rd_byte(void); + +static unsigned int read_index(unsigned char al); +static void write_index(unsigned char ah, unsigned int edx); + +static void cycle_delay(int cycle); + +static void do_set_bits(unsigned char bl); +static unsigned char do_get_bits(void); + +#define DATA_PCI_OFF 0x7FFC00 +#define WAIT_CYCLE 20 + +#define DRIVER_NAME "lirc_bt829" + +static int debug; +#define dprintk(fmt, args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG DRIVER_NAME ": "fmt, ## args); \ + } while (0) + +static int atir_minor; +static unsigned long pci_addr_phys; +static unsigned char *pci_addr_lin; + +static struct lirc_driver atir_driver; + +static struct pci_dev *do_pci_probe(void) +{ + struct pci_dev *my_dev; + my_dev = pci_get_device(PCI_VENDOR_ID_ATI, + PCI_DEVICE_ID_ATI_264VT, NULL); + if (my_dev) { + printk(KERN_ERR DRIVER_NAME ": Using device: %s\n", + pci_name(my_dev)); + pci_addr_phys = 0; + if (my_dev->resource[0].flags & IORESOURCE_MEM) { + pci_addr_phys = my_dev->resource[0].start; + printk(KERN_INFO DRIVER_NAME ": memory at 0x%08X\n", + (unsigned int)pci_addr_phys); + } + if (pci_addr_phys == 0) { + printk(KERN_ERR DRIVER_NAME ": no memory resource ?\n"); + return NULL; + } + } else { + printk(KERN_ERR DRIVER_NAME ": pci_probe failed\n"); + return NULL; + } + return my_dev; +} + +static int atir_add_to_buf(void *data, struct lirc_buffer *buf) +{ + unsigned char key; + int status; + status = poll_main(); + key = (status >> 8) & 0xFF; + if (status & 0xFF) { + dprintk("reading key %02X\n", key); + lirc_buffer_write(buf, &key); + return 0; + } + return -ENODATA; +} + +static int atir_set_use_inc(void *data) +{ + dprintk("driver is opened\n"); + return 0; +} + +static void atir_set_use_dec(void *data) +{ + dprintk("driver is closed\n"); +} + +int init_module(void) +{ + struct pci_dev *pdev; + + pdev = do_pci_probe(); + if (pdev == NULL) + return 1; + + if (!atir_init_start()) + return 1; + + strcpy(atir_driver.name, "ATIR"); + atir_driver.minor = -1; + atir_driver.code_length = 8; + atir_driver.sample_rate = 10; + atir_driver.data = 0; + atir_driver.add_to_buf = atir_add_to_buf; + atir_driver.set_use_inc = atir_set_use_inc; + atir_driver.set_use_dec = atir_set_use_dec; + atir_driver.dev = &pdev->dev; + atir_driver.owner = THIS_MODULE; + + atir_minor = lirc_register_driver(&atir_driver); + if (atir_minor < 0) { + printk(KERN_ERR DRIVER_NAME ": failed to register driver!\n"); + return atir_minor; + } + dprintk("driver is registered on minor %d\n", atir_minor); + + return 0; +} + + +void cleanup_module(void) +{ + lirc_unregister_driver(atir_minor); +} + + +static int atir_init_start(void) +{ + pci_addr_lin = ioremap(pci_addr_phys + DATA_PCI_OFF, 0x400); + if (pci_addr_lin == 0) { + printk(KERN_INFO DRIVER_NAME ": pci mem must be mapped\n"); + return 0; + } + return 1; +} + +static void cycle_delay(int cycle) +{ + udelay(WAIT_CYCLE*cycle); +} + + +static int poll_main() +{ + unsigned char status_high, status_low; + + do_i2c_start(); + + seems_wr_byte(0xAA); + seems_wr_byte(0x01); + + do_i2c_start(); + + seems_wr_byte(0xAB); + + status_low = seems_rd_byte(); + status_high = seems_rd_byte(); + + do_i2c_stop(); + + return (status_high << 8) | status_low; +} + +static void do_i2c_start(void) +{ + do_set_bits(3); + cycle_delay(4); + + do_set_bits(1); + cycle_delay(7); + + do_set_bits(0); + cycle_delay(2); +} + +static void do_i2c_stop(void) +{ + unsigned char bits; + bits = do_get_bits() & 0xFD; + do_set_bits(bits); + cycle_delay(1); + + bits |= 1; + do_set_bits(bits); + cycle_delay(2); + + bits |= 2; + do_set_bits(bits); + bits = 3; + do_set_bits(bits); + cycle_delay(2); +} + +static void seems_wr_byte(unsigned char value) +{ + int i; + unsigned char reg; + + reg = do_get_bits(); + for (i = 0; i < 8; i++) { + if (value & 0x80) + reg |= 0x02; + else + reg &= 0xFD; + + do_set_bits(reg); + cycle_delay(1); + + reg |= 1; + do_set_bits(reg); + cycle_delay(1); + + reg &= 0xFE; + do_set_bits(reg); + cycle_delay(1); + value <<= 1; + } + cycle_delay(2); + + reg |= 2; + do_set_bits(reg); + + reg |= 1; + do_set_bits(reg); + + cycle_delay(1); + do_get_bits(); + + reg &= 0xFE; + do_set_bits(reg); + cycle_delay(3); +} + +static unsigned char seems_rd_byte(void) +{ + int i; + int rd_byte; + unsigned char bits_2, bits_1; + + bits_1 = do_get_bits() | 2; + do_set_bits(bits_1); + + rd_byte = 0; + for (i = 0; i < 8; i++) { + bits_1 &= 0xFE; + do_set_bits(bits_1); + cycle_delay(2); + + bits_1 |= 1; + do_set_bits(bits_1); + cycle_delay(1); + + bits_2 = do_get_bits(); + if (bits_2 & 2) + rd_byte |= 1; + + rd_byte <<= 1; + } + + bits_1 = 0; + if (bits_2 == 0) + bits_1 |= 2; + + do_set_bits(bits_1); + cycle_delay(2); + + bits_1 |= 1; + do_set_bits(bits_1); + cycle_delay(3); + + bits_1 &= 0xFE; + do_set_bits(bits_1); + cycle_delay(2); + + rd_byte >>= 1; + rd_byte &= 0xFF; + return rd_byte; +} + +static void do_set_bits(unsigned char new_bits) +{ + int reg_val; + reg_val = read_index(0x34); + if (new_bits & 2) { + reg_val &= 0xFFFFFFDF; + reg_val |= 1; + } else { + reg_val &= 0xFFFFFFFE; + reg_val |= 0x20; + } + reg_val |= 0x10; + write_index(0x34, reg_val); + + reg_val = read_index(0x31); + if (new_bits & 1) + reg_val |= 0x1000000; + else + reg_val &= 0xFEFFFFFF; + + reg_val |= 0x8000000; + write_index(0x31, reg_val); +} + +static unsigned char do_get_bits(void) +{ + unsigned char bits; + int reg_val; + + reg_val = read_index(0x34); + reg_val |= 0x10; + reg_val &= 0xFFFFFFDF; + write_index(0x34, reg_val); + + reg_val = read_index(0x34); + bits = 0; + if (reg_val & 8) + bits |= 2; + else + bits &= 0xFD; + + reg_val = read_index(0x31); + if (reg_val & 0x1000000) + bits |= 1; + else + bits &= 0xFE; + + return bits; +} + +static unsigned int read_index(unsigned char index) +{ + unsigned char *addr; + unsigned int value; + /* addr = pci_addr_lin + DATA_PCI_OFF + ((index & 0xFF) << 2); */ + addr = pci_addr_lin + ((index & 0xFF) << 2); + value = readl(addr); + return value; +} + +static void write_index(unsigned char index, unsigned int reg_val) +{ + unsigned char *addr; + addr = pci_addr_lin + ((index & 0xFF) << 2); + writel(reg_val, addr); +} + +MODULE_AUTHOR("Froenchenko Leonid"); +MODULE_DESCRIPTION("IR remote driver for bt829 based TV cards"); +MODULE_LICENSE("GPL"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug enabled or not"); diff --git a/drivers/staging/lirc/lirc_ene0100.c b/drivers/staging/lirc/lirc_ene0100.c new file mode 100644 index 0000000..a152c52 --- /dev/null +++ b/drivers/staging/lirc/lirc_ene0100.c @@ -0,0 +1,646 @@ +/* + * driver for ENE KB3926 B/C/D CIR (also known as ENE0100) + * + * Copyright (C) 2009 Maxim Levitsky <maximlevitsky@gmail.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pnp.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include "lirc_ene0100.h" + +static int sample_period = 75; +static int enable_idle = 1; +static int enable_learning; + +static void ene_set_idle(struct ene_device *dev, int idle); +static void ene_set_inputs(struct ene_device *dev, int enable); + +/* read a hardware register */ +static u8 ene_hw_read_reg(struct ene_device *dev, u16 reg) +{ + outb(reg >> 8, dev->hw_io + ENE_ADDR_HI); + outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO); + return inb(dev->hw_io + ENE_IO); +} + +/* write a hardware register */ +static void ene_hw_write_reg(struct ene_device *dev, u16 reg, u8 value) +{ + outb(reg >> 8, dev->hw_io + ENE_ADDR_HI); + outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO); + outb(value, dev->hw_io + ENE_IO); +} + +/* change specific bits in hardware register */ +static void ene_hw_write_reg_mask(struct ene_device *dev, + u16 reg, u8 value, u8 mask) +{ + u8 regvalue; + + outb(reg >> 8, dev->hw_io + ENE_ADDR_HI); + outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO); + + regvalue = inb(dev->hw_io + ENE_IO) & ~mask; + regvalue |= (value & mask); + outb(regvalue, dev->hw_io + ENE_IO); +} + +/* read irq status and ack it */ +static int ene_hw_irq_status(struct ene_device *dev, int *buffer_pointer) +{ + u8 irq_status; + u8 fw_flags1, fw_flags2; + + fw_flags2 = ene_hw_read_reg(dev, ENE_FW2); + + if (buffer_pointer) + *buffer_pointer = 4 * (fw_flags2 & ENE_FW2_BUF_HIGH); + + if (dev->hw_revision < ENE_HW_C) { + irq_status = ene_hw_read_reg(dev, ENEB_IRQ_STATUS); + + if (!(irq_status & ENEB_IRQ_STATUS_IR)) + return 0; + ene_hw_write_reg(dev, ENEB_IRQ_STATUS, + irq_status & ~ENEB_IRQ_STATUS_IR); + + /* rev B support only recieving */ + return ENE_IRQ_RX; + } + + irq_status = ene_hw_read_reg(dev, ENEC_IRQ); + + if (!(irq_status & ENEC_IRQ_STATUS)) + return 0; + + /* original driver does that twice - a workaround ? */ + ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS); + ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS); + + /* clear unknown flag in F8F9 */ + if (fw_flags2 & ENE_FW2_IRQ_CLR) + ene_hw_write_reg(dev, ENE_FW2, fw_flags2 & ~ENE_FW2_IRQ_CLR); + + /* check if this is a TX interrupt */ + fw_flags1 = ene_hw_read_reg(dev, ENE_FW1); + + if (fw_flags1 & ENE_FW1_TXIRQ) { + ene_hw_write_reg(dev, ENE_FW1, fw_flags1 & ~ENE_FW1_TXIRQ); + return ENE_IRQ_TX; + } else + return ENE_IRQ_RX; +} + +static int ene_hw_detect(struct ene_device *dev) +{ + u8 chip_major, chip_minor; + u8 hw_revision, old_ver; + u8 tmp; + u8 fw_capabilities; + + tmp = ene_hw_read_reg(dev, ENE_HW_UNK); + ene_hw_write_reg(dev, ENE_HW_UNK, tmp & ~ENE_HW_UNK_CLR); + + chip_major = ene_hw_read_reg(dev, ENE_HW_VER_MAJOR); + chip_minor = ene_hw_read_reg(dev, ENE_HW_VER_MINOR); + + ene_hw_write_reg(dev, ENE_HW_UNK, tmp); + hw_revision = ene_hw_read_reg(dev, ENE_HW_VERSION); + old_ver = ene_hw_read_reg(dev, ENE_HW_VER_OLD); + + if (hw_revision == 0xFF) { + + ene_printk(KERN_WARNING, "device seems to be disabled\n"); + ene_printk(KERN_WARNING, + "send a mail to lirc-list@lists.sourceforge.net\n"); + ene_printk(KERN_WARNING, "please attach output of acpidump\n"); + + return -ENODEV; + } + + if (chip_major == 0x33) { + ene_printk(KERN_WARNING, "chips 0x33xx aren't supported yet\n"); + return -ENODEV; + } + + if (chip_major == 0x39 && chip_minor == 0x26 && hw_revision == 0xC0) { + dev->hw_revision = ENE_HW_C; + ene_printk(KERN_WARNING, + "KB3926C detected, driver support is not complete!\n"); + + } else if (old_ver == 0x24 && hw_revision == 0xC0) { + dev->hw_revision = ENE_HW_B; + ene_printk(KERN_NOTICE, "KB3926B detected\n"); + } else { + dev->hw_revision = ENE_HW_D; + ene_printk(KERN_WARNING, + "unknown ENE chip detected, assuming KB3926D\n"); + ene_printk(KERN_WARNING, "driver support incomplete"); + + } + + ene_printk(KERN_DEBUG, "chip is 0x%02x%02x - 0x%02x, 0x%02x\n", + chip_major, chip_minor, old_ver, hw_revision); + + + /* detect features hardware supports */ + + if (dev->hw_revision < ENE_HW_C) + return 0; + + fw_capabilities = ene_hw_read_reg(dev, ENE_FW2); + + dev->hw_gpio40_learning = fw_capabilities & ENE_FW2_GP40_AS_LEARN; + dev->hw_learning_and_tx_capable = fw_capabilities & ENE_FW2_LEARNING; + + dev->hw_fan_as_normal_input = dev->hw_learning_and_tx_capable && + fw_capabilities & ENE_FW2_FAN_AS_NRML_IN; + + ene_printk(KERN_NOTICE, "hardware features:\n"); + ene_printk(KERN_NOTICE, + "learning and tx %s, gpio40_learn %s, fan_in %s\n", + dev->hw_learning_and_tx_capable ? "on" : "off", + dev->hw_gpio40_learning ? "on" : "off", + dev->hw_fan_as_normal_input ? "on" : "off"); + + if (!dev->hw_learning_and_tx_capable && enable_learning) + enable_learning = 0; + + if (dev->hw_learning_and_tx_capable) { + ene_printk(KERN_WARNING, + "Device supports transmitting, but the driver doesn't\n"); + ene_printk(KERN_WARNING, + "due to lack of hardware to test against.\n"); + ene_printk(KERN_WARNING, + "Send a mail to: lirc-list@lists.sourceforge.net\n"); + } + return 0; +} + +/* hardware initialization */ +static int ene_hw_init(void *data) +{ + u8 reg_value; + struct ene_device *dev = (struct ene_device *)data; + dev->in_use = 1; + + if (dev->hw_revision < ENE_HW_C) { + ene_hw_write_reg(dev, ENEB_IRQ, dev->irq << 1); + ene_hw_write_reg(dev, ENEB_IRQ_UNK1, 0x01); + } else { + reg_value = ene_hw_read_reg(dev, ENEC_IRQ) & 0xF0; + reg_value |= ENEC_IRQ_UNK_EN; + reg_value &= ~ENEC_IRQ_STATUS; + reg_value |= (dev->irq & ENEC_IRQ_MASK); + ene_hw_write_reg(dev, ENEC_IRQ, reg_value); + ene_hw_write_reg(dev, ENE_TX_UNK1, 0x63); + } + + ene_hw_write_reg(dev, ENE_CIR_CONF2, 0x00); + ene_set_inputs(dev, enable_learning); + + /* set sampling period */ + ene_hw_write_reg(dev, ENE_CIR_SAMPLE_PERIOD, sample_period); + + /* ack any pending irqs - just in case */ + ene_hw_irq_status(dev, NULL); + + /* enter idle mode */ + ene_set_idle(dev, 1); + + /* enable firmware bits */ + ene_hw_write_reg_mask(dev, ENE_FW1, + ENE_FW1_ENABLE | ENE_FW1_IRQ, + ENE_FW1_ENABLE | ENE_FW1_IRQ); + /* clear stats */ + dev->sample = 0; + return 0; +} + +/* this enables gpio40 signal, used if connected to wide band input*/ +static void ene_enable_gpio40(struct ene_device *dev, int enable) +{ + ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, enable ? + 0 : ENE_CIR_CONF2_GPIO40DIS, + ENE_CIR_CONF2_GPIO40DIS); +} + +/* this enables the classic sampler */ +static void ene_enable_normal_recieve(struct ene_device *dev, int enable) +{ + ene_hw_write_reg(dev, ENE_CIR_CONF1, enable ? ENE_CIR_CONF1_ADC_ON : 0); +} + +/* this enables recieve via fan input */ +static void ene_enable_fan_recieve(struct ene_device *dev, int enable) +{ + if (!enable) + ene_hw_write_reg(dev, ENE_FAN_AS_IN1, 0); + else { + ene_hw_write_reg(dev, ENE_FAN_AS_IN1, ENE_FAN_AS_IN1_EN); + ene_hw_write_reg(dev, ENE_FAN_AS_IN2, ENE_FAN_AS_IN2_EN); + } + dev->fan_input_inuse = enable; +} + +/* determine which input to use*/ +static void ene_set_inputs(struct ene_device *dev, int learning_enable) +{ + ene_enable_normal_recieve(dev, 1); + + /* old hardware doesn't support learning mode for sure */ + if (dev->hw_revision <= ENE_HW_B) + return; + + /* reciever not learning capable, still set gpio40 correctly */ + if (!dev->hw_learning_and_tx_capable) { + ene_enable_gpio40(dev, !dev->hw_gpio40_learning); + return; + } + + /* enable learning mode */ + if (learning_enable) { + ene_enable_gpio40(dev, dev->hw_gpio40_learning); + + /* fan input is not used for learning */ + if (dev->hw_fan_as_normal_input) + ene_enable_fan_recieve(dev, 0); + + /* disable learning mode */ + } else { + if (dev->hw_fan_as_normal_input) { + ene_enable_fan_recieve(dev, 1); + ene_enable_normal_recieve(dev, 0); + } else + ene_enable_gpio40(dev, !dev->hw_gpio40_learning); + } + + /* set few additional settings for this mode */ + ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, learning_enable ? + ENE_CIR_CONF1_LEARN1 : 0, ENE_CIR_CONF1_LEARN1); + + ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, learning_enable ? + ENE_CIR_CONF2_LEARN2 : 0, ENE_CIR_CONF2_LEARN2); +} + +/* deinitialization */ +static void ene_hw_deinit(void *data) +{ + struct ene_device *dev = (struct ene_device *)data; + + /* disable samplers */ + ene_enable_normal_recieve(dev, 0); + + if (dev->hw_fan_as_normal_input) + ene_enable_fan_recieve(dev, 0); + + /* disable hardware IRQ and firmware flag */ + ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_ENABLE | ENE_FW1_IRQ); + + ene_set_idle(dev, 1); + dev->in_use = 0; +} + +/* sends current sample to userspace */ +static void send_sample(struct ene_device *dev) +{ + int value = abs(dev->sample) & PULSE_MASK; + + if (dev->sample > 0) + value |= PULSE_BIT; + + if (!lirc_buffer_full(dev->lirc_driver->rbuf)) { + lirc_buffer_write(dev->lirc_driver->rbuf, (void *)&value); + wake_up(&dev->lirc_driver->rbuf->wait_poll); + } + dev->sample = 0; +} + +/* this updates current sample */ +static void update_sample(struct ene_device *dev, int sample) +{ + if (!dev->sample) + dev->sample = sample; + else if (same_sign(dev->sample, sample)) + dev->sample += sample; + else { + send_sample(dev); + dev->sample = sample; + } +} + +/* enable or disable idle mode */ +static void ene_set_idle(struct ene_device *dev, int idle) +{ + struct timeval now; + int disable = idle && enable_idle && (dev->hw_revision < ENE_HW_C); + + ene_hw_write_reg_mask(dev, ENE_CIR_SAMPLE_PERIOD, + disable ? 0 : ENE_CIR_SAMPLE_OVERFLOW, + ENE_CIR_SAMPLE_OVERFLOW); + dev->idle = idle; + + /* remember when we have entered the idle mode */ + if (idle) { + do_gettimeofday(&dev->gap_start); + return; + } + + /* send the gap between keypresses now */ + do_gettimeofday(&now); + + if (now.tv_sec - dev->gap_start.tv_sec > 16) + dev->sample = space(PULSE_MASK); + else + dev->sample = dev->sample + + space(1000000ull * (now.tv_sec - dev->gap_start.tv_sec)) + + space(now.tv_usec - dev->gap_start.tv_usec); + + if (abs(dev->sample) > PULSE_MASK) + dev->sample = space(PULSE_MASK); + send_sample(dev); +} + +/* interrupt handler */ +static irqreturn_t ene_hw_irq(int irq, void *data) +{ + u16 hw_value; + int i, hw_sample; + int space; + int buffer_pointer; + int irq_status; + + struct ene_device *dev = (struct ene_device *)data; + irq_status = ene_hw_irq_status(dev, &buffer_pointer); + + if (!irq_status) + return IRQ_NONE; + + /* TODO: only RX for now */ + if (irq_status == ENE_IRQ_TX) + return IRQ_HANDLED; + + for (i = 0; i < ENE_SAMPLES_SIZE; i++) { + + hw_value = ene_hw_read_reg(dev, + ENE_SAMPLE_BUFFER + buffer_pointer + i); + + if (dev->fan_input_inuse) { + /* read high part of the sample */ + hw_value |= ene_hw_read_reg(dev, + ENE_SAMPLE_BUFFER_FAN + buffer_pointer + i) << 8; + + /* test for _space_ bit */ + space = !(hw_value & ENE_FAN_SMPL_PULS_MSK); + + /* clear space bit, and other unused bits */ + hw_value &= ENE_FAN_VALUE_MASK; + hw_sample = hw_value * ENE_SAMPLE_PERIOD_FAN; + + } else { + space = hw_value & ENE_SAMPLE_SPC_MASK; + hw_value &= ENE_SAMPLE_VALUE_MASK; + hw_sample = hw_value * sample_period; + } + + /* no more data */ + if (!(hw_value)) + break; + + if (space) + hw_sample *= -1; + + /* overflow sample recieved, handle it */ + + if (!dev->fan_input_inuse && hw_value == ENE_SAMPLE_OVERFLOW) { + + if (dev->idle) + continue; + + if (dev->sample > 0 || abs(dev->sample) <= ENE_MAXGAP) + update_sample(dev, hw_sample); + else + ene_set_idle(dev, 1); + + continue; + } + + /* normal first sample recieved */ + if (!dev->fan_input_inuse && dev->idle) { + ene_set_idle(dev, 0); + + /* discard first recieved value, its random + since its the time signal was off before + first pulse if idle mode is enabled, HW + does that for us */ + + if (!enable_idle) + continue; + } + update_sample(dev, hw_sample); + send_sample(dev); + } + return IRQ_HANDLED; +} + +static int ene_probe(struct pnp_dev *pnp_dev, + const struct pnp_device_id *dev_id) +{ + struct ene_device *dev; + struct lirc_driver *lirc_driver; + int error = -ENOMEM; + + dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL); + + if (!dev) + goto err1; + + dev->pnp_dev = pnp_dev; + pnp_set_drvdata(pnp_dev, dev); + + + /* prepare lirc interface */ + error = -ENOMEM; + lirc_driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); + + if (!lirc_driver) + goto err2; + + dev->lirc_driver = lirc_driver; + + strcpy(lirc_driver->name, ENE_DRIVER_NAME); + lirc_driver->minor = -1; + lirc_driver->code_length = sizeof(int) * 8; + lirc_driver->features = LIRC_CAN_REC_MODE2; + lirc_driver->data = dev; + lirc_driver->set_use_inc = ene_hw_init; + lirc_driver->set_use_dec = ene_hw_deinit; + lirc_driver->dev = &pnp_dev->dev; + lirc_driver->owner = THIS_MODULE; + + lirc_driver->rbuf = kzalloc(sizeof(struct lirc_buffer), GFP_KERNEL); + + if (!lirc_driver->rbuf) + goto err3; + + if (lirc_buffer_init(lirc_driver->rbuf, sizeof(int), sizeof(int) * 256)) + goto err4; + + error = -ENODEV; + if (lirc_register_driver(lirc_driver)) + goto err5; + + /* validate resources */ + if (!pnp_port_valid(pnp_dev, 0) || + pnp_port_len(pnp_dev, 0) < ENE_MAX_IO) + goto err6; + + if (!pnp_irq_valid(pnp_dev, 0)) + goto err6; + + dev->hw_io = pnp_port_start(pnp_dev, 0); + dev->irq = pnp_irq(pnp_dev, 0); + + /* claim the resources */ + error = -EBUSY; + if (!request_region(dev->hw_io, ENE_MAX_IO, ENE_DRIVER_NAME)) + goto err6; + + if (request_irq(dev->irq, ene_hw_irq, + IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev)) + goto err7; + + /* detect hardware version and features */ + error = ene_hw_detect(dev); + if (error) + goto err8; + + ene_printk(KERN_NOTICE, "driver has been succesfully loaded\n"); + return 0; + +err8: + free_irq(dev->irq, dev); +err7: + release_region(dev->hw_io, ENE_MAX_IO); +err6: + lirc_unregister_driver(lirc_driver->minor); +err5: + lirc_buffer_free(lirc_driver->rbuf); +err4: + kfree(lirc_driver->rbuf); +err3: + kfree(lirc_driver); +err2: + kfree(dev); +err1: + return error; +} + +static void ene_remove(struct pnp_dev *pnp_dev) +{ + struct ene_device *dev = pnp_get_drvdata(pnp_dev); + ene_hw_deinit(dev); + free_irq(dev->irq, dev); + release_region(dev->hw_io, ENE_MAX_IO); + lirc_unregister_driver(dev->lirc_driver->minor); + lirc_buffer_free(dev->lirc_driver->rbuf); + kfree(dev->lirc_driver); + kfree(dev); +} + +#ifdef CONFIG_PM + +/* TODO: make 'wake on IR' configurable and add .shutdown */ +/* currently impossible due to lack of kernel support */ + +static int ene_suspend(struct pnp_dev *pnp_dev, pm_message_t state) +{ + struct ene_device *dev = pnp_get_drvdata(pnp_dev); + ene_hw_write_reg_mask(dev, ENE_FW1, ENE_FW1_WAKE, ENE_FW1_WAKE); + return 0; +} + +static int ene_resume(struct pnp_dev *pnp_dev) +{ + struct ene_device *dev = pnp_get_drvdata(pnp_dev); + if (dev->in_use) + ene_hw_init(dev); + + ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_WAKE); + return 0; +} + +#endif + +static const struct pnp_device_id ene_ids[] = { + {.id = "ENE0100",}, + {}, +}; + +static struct pnp_driver ene_driver = { + .name = ENE_DRIVER_NAME, + .id_table = ene_ids, + .flags = PNP_DRIVER_RES_DO_NOT_CHANGE, + + .probe = ene_probe, + .remove = __devexit_p(ene_remove), + +#ifdef CONFIG_PM + .suspend = ene_suspend, + .resume = ene_resume, +#endif +}; + +static int __init ene_init(void) +{ + if (sample_period < 5) { + ene_printk(KERN_ERR, "sample period must be at\n"); + ene_printk(KERN_ERR, "least 5 us, (at least 30 recommended)\n"); + return -EINVAL; + } + return pnp_register_driver(&ene_driver); +} + +static void ene_exit(void) +{ + pnp_unregister_driver(&ene_driver); +} + +module_param(sample_period, int, S_IRUGO); +MODULE_PARM_DESC(sample_period, "Hardware sample period (75 us default)"); + +module_param(enable_idle, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(enable_idle, + "Enables turning off signal sampling after long inactivity time; " + "if disabled might help detecting input signal (default: enabled)"); + +module_param(enable_learning, bool, S_IRUGO); +MODULE_PARM_DESC(enable_learning, "Use wide band (learning) reciever"); + +MODULE_DEVICE_TABLE(pnp, ene_ids); +MODULE_DESCRIPTION + ("LIRC driver for KB3926B/KB3926C/KB3926D (aka ENE0100) CIR port"); +MODULE_AUTHOR("Maxim Levitsky"); +MODULE_LICENSE("GPL"); + +module_init(ene_init); +module_exit(ene_exit); diff --git a/drivers/staging/lirc/lirc_ene0100.h b/drivers/staging/lirc/lirc_ene0100.h new file mode 100644 index 0000000..776b693 --- /dev/null +++ b/drivers/staging/lirc/lirc_ene0100.h @@ -0,0 +1,169 @@ +/* + * driver for ENE KB3926 B/C/D CIR (also known as ENE0100) + * + * Copyright (C) 2009 Maxim Levitsky <maximlevitsky@gmail.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include <media/lirc.h> +#include <media/lirc_dev.h> + +/* hardware address */ +#define ENE_STATUS 0 /* hardware status - unused */ +#define ENE_ADDR_HI 1 /* hi byte of register address */ +#define ENE_ADDR_LO 2 /* low byte of register address */ +#define ENE_IO 3 /* read/write window */ +#define ENE_MAX_IO 4 + +/* 8 bytes of samples, divided in 2 halfs*/ +#define ENE_SAMPLE_BUFFER 0xF8F0 /* regular sample buffer */ +#define ENE_SAMPLE_SPC_MASK (1 << 7) /* sample is space */ +#define ENE_SAMPLE_VALUE_MASK 0x7F +#define ENE_SAMPLE_OVERFLOW 0x7F +#define ENE_SAMPLES_SIZE 4 + +/* fan input sample buffer */ +#define ENE_SAMPLE_BUFFER_FAN 0xF8FB /* this buffer holds high byte of */ + /* each sample of normal buffer */ + +#define ENE_FAN_SMPL_PULS_MSK 0x8000 /* this bit of combined sample */ + /* if set, says that sample is pulse */ +#define ENE_FAN_VALUE_MASK 0x0FFF /* mask for valid bits of the value */ + +/* first firmware register */ +#define ENE_FW1 0xF8F8 +#define ENE_FW1_ENABLE (1 << 0) /* enable fw processing */ +#define ENE_FW1_TXIRQ (1 << 1) /* TX interrupt pending */ +#define ENE_FW1_WAKE (1 << 6) /* enable wake from S3 */ +#define ENE_FW1_IRQ (1 << 7) /* enable interrupt */ + +/* second firmware register */ +#define ENE_FW2 0xF8F9 +#define ENE_FW2_BUF_HIGH (1 << 0) /* which half of the buffer to read */ +#define ENE_FW2_IRQ_CLR (1 << 2) /* clear this on IRQ */ +#define ENE_FW2_GP40_AS_LEARN (1 << 4) /* normal input is used as */ + /* learning input */ +#define ENE_FW2_FAN_AS_NRML_IN (1 << 6) /* fan is used as normal input */ +#define ENE_FW2_LEARNING (1 << 7) /* hardware supports learning and TX */ + +/* fan as input settings - only if learning capable */ +#define ENE_FAN_AS_IN1 0xFE30 /* fan init reg 1 */ +#define ENE_FAN_AS_IN1_EN 0xCD +#define ENE_FAN_AS_IN2 0xFE31 /* fan init reg 2 */ +#define ENE_FAN_AS_IN2_EN 0x03 +#define ENE_SAMPLE_PERIOD_FAN 61 /* fan input has fixed sample period */ + +/* IRQ registers block (for revision B) */ +#define ENEB_IRQ 0xFD09 /* IRQ number */ +#define ENEB_IRQ_UNK1 0xFD17 /* unknown setting = 1 */ +#define ENEB_IRQ_STATUS 0xFD80 /* irq status */ +#define ENEB_IRQ_STATUS_IR (1 << 5) /* IR irq */ + +/* IRQ registers block (for revision C,D) */ +#define ENEC_IRQ 0xFE9B /* new irq settings register */ +#define ENEC_IRQ_MASK 0x0F /* irq number mask */ +#define ENEC_IRQ_UNK_EN (1 << 4) /* always enabled */ +#define ENEC_IRQ_STATUS (1 << 5) /* irq status and ACK */ + +/* CIR block settings */ +#define ENE_CIR_CONF1 0xFEC0 +#define ENE_CIR_CONF1_ADC_ON 0x7 /* reciever on gpio40 enabled */ +#define ENE_CIR_CONF1_LEARN1 (1 << 3) /* enabled on learning mode */ +#define ENE_CIR_CONF1_TX_ON 0x30 /* enabled on transmit */ +#define ENE_CIR_CONF1_TX_CARR (1 << 7) /* send TX carrier or not */ + +#define ENE_CIR_CONF2 0xFEC1 /* unknown setting = 0 */ +#define ENE_CIR_CONF2_LEARN2 (1 << 4) /* set on enable learning */ +#define ENE_CIR_CONF2_GPIO40DIS (1 << 5) /* disable normal input via gpio40 */ + +#define ENE_CIR_SAMPLE_PERIOD 0xFEC8 /* sample period in us */ +#define ENE_CIR_SAMPLE_OVERFLOW (1 << 7) /* interrupt on overflows if set */ + + +/* transmitter - not implemented yet */ +/* KB3926C and higher */ +/* transmission is very similiar to recieving, a byte is written to */ +/* ENE_TX_INPUT, in same manner as it is read from sample buffer */ +/* sample period is fixed*/ + + +/* transmitter ports */ +#define ENE_TX_PORT1 0xFC01 /* this enables one or both */ +#define ENE_TX_PORT1_EN (1 << 5) /* TX ports */ +#define ENE_TX_PORT2 0xFC08 +#define ENE_TX_PORT2_EN (1 << 1) + +#define ENE_TX_INPUT 0xFEC9 /* next byte to transmit */ +#define ENE_TX_SPC_MASK (1 << 7) /* Transmitted sample is space */ +#define ENE_TX_UNK1 0xFECB /* set to 0x63 */ +#define ENE_TX_SMPL_PERIOD 50 /* transmit sample period */ + + +#define ENE_TX_CARRIER 0xFECE /* TX carrier * 2 (khz) */ +#define ENE_TX_CARRIER_UNKBIT 0x80 /* This bit set on transmit */ +#define ENE_TX_CARRIER_LOW 0xFECF /* TX carrier / 2 */ + +/* Hardware versions */ +#define ENE_HW_VERSION 0xFF00 /* hardware revision */ +#define ENE_HW_UNK 0xFF1D +#define ENE_HW_UNK_CLR (1 << 2) +#define ENE_HW_VER_MAJOR 0xFF1E /* chip version */ +#define ENE_HW_VER_MINOR 0xFF1F +#define ENE_HW_VER_OLD 0xFD00 + +#define same_sign(a, b) ((((a) > 0) && (b) > 0) || ((a) < 0 && (b) < 0)) + +#define ENE_DRIVER_NAME "enecir" +#define ENE_MAXGAP 250000 /* this is amount of time we wait + before turning the sampler, chosen + arbitry */ + +#define space(len) (-(len)) /* add a space */ + +/* software defines */ +#define ENE_IRQ_RX 1 +#define ENE_IRQ_TX 2 + +#define ENE_HW_B 1 /* 3926B */ +#define ENE_HW_C 2 /* 3926C */ +#define ENE_HW_D 3 /* 3926D */ + +#define ene_printk(level, text, ...) \ + printk(level ENE_DRIVER_NAME ": " text, ## __VA_ARGS__) + +struct ene_device { + struct pnp_dev *pnp_dev; + struct lirc_driver *lirc_driver; + + /* hw settings */ + unsigned long hw_io; + int irq; + + int hw_revision; /* hardware revision */ + int hw_learning_and_tx_capable; /* learning capable */ + int hw_gpio40_learning; /* gpio40 is learning */ + int hw_fan_as_normal_input; /* fan input is used as regular input */ + + /* device data */ + int idle; + int fan_input_inuse; + + int sample; + int in_use; + + struct timeval gap_start; +}; diff --git a/drivers/staging/lirc/lirc_i2c.c b/drivers/staging/lirc/lirc_i2c.c new file mode 100644 index 0000000..6df2c0e --- /dev/null +++ b/drivers/staging/lirc/lirc_i2c.c @@ -0,0 +1,536 @@ +/* + * lirc_i2c.c + * + * i2c IR driver for the onboard IR port on many TV tuner cards, including: + * -Flavors of the Hauppauge PVR-150/250/350 + * -Hauppauge HVR-1300 + * -PixelView (BT878P+W/FM) + * -KNC ONE TV Station/Anubis Typhoon TView Tuner + * -Asus TV-Box and Creative/VisionTek BreakOut-Box + * -Leadtek Winfast PVR2000 + * + * Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de> + * modified for PixelView (BT878P+W/FM) by + * Michal Kochanowicz <mkochano@pld.org.pl> + * Christoph Bartelmus <lirc@bartelmus.de> + * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by + * Ulrich Mueller <ulrich.mueller42@web.de> + * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by + * Stefan Jahn <stefan@lkcc.org> + * modified for inclusion into kernel sources by + * Jerome Brock <jbrock@users.sourceforge.net> + * modified for Leadtek Winfast PVR2000 by + * Thomas Reitmayr (treitmayr@yahoo.com) + * modified for Hauppauge HVR-1300 by + * Jan Frey (jfrey@gmx.de) + * + * parts are cut&pasted from the old lirc_haup.c 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/kmod.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/i2c.h> +#include <linux/i2c-algo-bit.h> + +#include <media/lirc_dev.h> + +struct IR { + struct lirc_driver l; + struct i2c_client c; + int nextkey; + unsigned char b[3]; + unsigned char bits; + unsigned char flag; +}; + +#define DEVICE_NAME "lirc_i2c" + +/* module parameters */ +static int debug; /* debug output */ +static int minor = -1; /* minor number */ + +#define dprintk(fmt, args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG DEVICE_NAME ": " fmt, \ + ## args); \ + } while (0) + +static int reverse(int data, int bits) +{ + int i; + int c; + + for (c = 0, i = 0; i < bits; i++) + c |= ((data & (1<<i)) ? 1 : 0) << (bits-1-i); + + return c; +} + +static int add_to_buf_adap(void *data, struct lirc_buffer *buf) +{ + struct IR *ir = data; + unsigned char keybuf[4]; + + keybuf[0] = 0x00; + i2c_master_send(&ir->c, keybuf, 1); + /* poll IR chip */ + if (i2c_master_recv(&ir->c, keybuf, sizeof(keybuf)) != sizeof(keybuf)) { + dprintk("read error\n"); + return -EIO; + } + + dprintk("key (0x%02x%02x%02x%02x)\n", + keybuf[0], keybuf[1], keybuf[2], keybuf[3]); + + /* key pressed ? */ + if (keybuf[2] == 0xff) + return -ENODATA; + + /* remove repeat bit */ + keybuf[2] &= 0x7f; + keybuf[3] |= 0x80; + + lirc_buffer_write(buf, keybuf); + return 0; +} + +static int add_to_buf_pcf8574(void *data, struct lirc_buffer *buf) +{ + struct IR *ir = data; + int rc; + unsigned char all, mask; + unsigned char key; + + /* compute all valid bits (key code + pressed/release flag) */ + all = ir->bits | ir->flag; + + /* save IR writable mask bits */ + mask = i2c_smbus_read_byte(&ir->c) & ~all; + + /* send bit mask */ + rc = i2c_smbus_write_byte(&ir->c, (0xff & all) | mask); + + /* receive scan code */ + rc = i2c_smbus_read_byte(&ir->c); + + if (rc == -1) { + dprintk("%s read error\n", ir->c.name); + return -EIO; + } + + /* drop duplicate polls */ + if (ir->b[0] == (rc & all)) + return -ENODATA; + + ir->b[0] = rc & all; + + dprintk("%s key 0x%02X %s\n", ir->c.name, rc & ir->bits, + (rc & ir->flag) ? "released" : "pressed"); + + /* ignore released buttons */ + if (rc & ir->flag) + return -ENODATA; + + /* set valid key code */ + key = rc & ir->bits; + lirc_buffer_write(buf, &key); + return 0; +} + +/* common for Hauppauge IR receivers */ +static int add_to_buf_haup_common(void *data, struct lirc_buffer *buf, + unsigned char *keybuf, int size, int offset) +{ + struct IR *ir = data; + __u16 code; + unsigned char codes[2]; + int ret; + + /* poll IR chip */ + ret = i2c_master_recv(&ir->c, keybuf, size); + if (ret == size) { + ir->b[0] = keybuf[offset]; + ir->b[1] = keybuf[offset+1]; + ir->b[2] = keybuf[offset+2]; + if (ir->b[0] != 0x00 && ir->b[1] != 0x00) + dprintk("key (0x%02x/0x%02x)\n", ir->b[0], ir->b[1]); + } else { + dprintk("read error (ret=%d)\n", ret); + /* keep last successful read buffer */ + } + + /* key pressed ? */ + if ((ir->b[0] & 0x80) == 0) + return -ENODATA; + + /* look what we have */ + code = (((__u16)ir->b[0]&0x7f)<<6) | (ir->b[1]>>2); + + codes[0] = (code >> 8) & 0xff; + codes[1] = code & 0xff; + + /* return it */ + dprintk("sending code 0x%02x%02x to lirc\n", codes[0], codes[1]); + lirc_buffer_write(buf, codes); + return 0; +} + +/* specific for the Hauppauge PVR150 IR receiver */ +static int add_to_buf_haup_pvr150(void *data, struct lirc_buffer *buf) +{ + unsigned char keybuf[6]; + /* fetch 6 bytes, first relevant is at offset 3 */ + return add_to_buf_haup_common(data, buf, keybuf, 6, 3); +} + +/* used for all Hauppauge IR receivers but the PVR150 */ +static int add_to_buf_haup(void *data, struct lirc_buffer *buf) +{ + unsigned char keybuf[3]; + /* fetch 3 bytes, first relevant is at offset 0 */ + return add_to_buf_haup_common(data, buf, keybuf, 3, 0); +} + + +static int add_to_buf_pvr2000(void *data, struct lirc_buffer *buf) +{ + struct IR *ir = data; + unsigned char key; + s32 flags; + s32 code; + + /* poll IR chip */ + flags = i2c_smbus_read_byte_data(&ir->c, 0x10); + if (-1 == flags) { + dprintk("read error\n"); + return -ENODATA; + } + /* key pressed ? */ + if (0 == (flags & 0x80)) + return -ENODATA; + + /* read actual key code */ + code = i2c_smbus_read_byte_data(&ir->c, 0x00); + if (-1 == code) { + dprintk("read error\n"); + return -ENODATA; + } + + key = code & 0xFF; + + dprintk("IR Key/Flags: (0x%02x/0x%02x)\n", key, flags & 0xFF); + + /* return it */ + lirc_buffer_write(buf, &key); + return 0; +} + +static int add_to_buf_pixelview(void *data, struct lirc_buffer *buf) +{ + struct IR *ir = data; + unsigned char key; + + /* poll IR chip */ + if (1 != i2c_master_recv(&ir->c, &key, 1)) { + dprintk("read error\n"); + return -1; + } + dprintk("key %02x\n", key); + + /* return it */ + lirc_buffer_write(buf, &key); + return 0; +} + +static int add_to_buf_pv951(void *data, struct lirc_buffer *buf) +{ + struct IR *ir = data; + unsigned char key; + unsigned char codes[4]; + + /* poll IR chip */ + if (1 != i2c_master_recv(&ir->c, &key, 1)) { + dprintk("read error\n"); + return -ENODATA; + } + /* ignore 0xaa */ + if (key == 0xaa) + return -ENODATA; + dprintk("key %02x\n", key); + + codes[0] = 0x61; + codes[1] = 0xD6; + codes[2] = reverse(key, 8); + codes[3] = (~codes[2])&0xff; + + lirc_buffer_write(buf, codes); + return 0; +} + +static int add_to_buf_knc1(void *data, struct lirc_buffer *buf) +{ + static unsigned char last_key = 0xFF; + struct IR *ir = data; + unsigned char key; + + /* poll IR chip */ + if (1 != i2c_master_recv(&ir->c, &key, 1)) { + dprintk("read error\n"); + return -ENODATA; + } + + /* + * it seems that 0xFE indicates that a button is still held + * down, while 0xFF indicates that no button is held + * down. 0xFE sequences are sometimes interrupted by 0xFF + */ + + dprintk("key %02x\n", key); + + if (key == 0xFF) + return -ENODATA; + + if (key == 0xFE) + key = last_key; + + last_key = key; + lirc_buffer_write(buf, &key); + + return 0; +} + +static int set_use_inc(void *data) +{ + struct IR *ir = data; + + dprintk("%s called\n", __func__); + + /* lock bttv in memory while /dev/lirc is in use */ + i2c_use_client(&ir->c); + + return 0; +} + +static void set_use_dec(void *data) +{ + struct IR *ir = data; + + dprintk("%s called\n", __func__); + + i2c_release_client(&ir->c); +} + +static struct lirc_driver lirc_template = { + .name = "lirc_i2c", + .set_use_inc = set_use_inc, + .set_use_dec = set_use_dec, + .dev = NULL, + .owner = THIS_MODULE, +}; + +static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id); +static int ir_remove(struct i2c_client *client); +static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg); + +static const struct i2c_device_id ir_receiver_id[] = { + /* Generic entry for any IR receiver */ + { "ir_video", 0 }, + /* IR device specific entries could be added here */ + { } +}; + +static struct i2c_driver driver = { + .driver = { + .owner = THIS_MODULE, + .name = "i2c ir driver", + }, + .probe = ir_probe, + .remove = ir_remove, + .id_table = ir_receiver_id, + .command = ir_command, +}; + +static void pcf_probe(struct i2c_client *client, struct IR *ir) +{ + int ret1, ret2, ret3, ret4; + + ret1 = i2c_smbus_write_byte(client, 0xff); + ret2 = i2c_smbus_read_byte(client); + ret3 = i2c_smbus_write_byte(client, 0x00); + ret4 = i2c_smbus_read_byte(client); + + /* in the Asus TV-Box: bit 1-0 */ + if (((ret2 & 0x03) == 0x03) && ((ret4 & 0x03) == 0x00)) { + ir->bits = (unsigned char) ~0x07; + ir->flag = 0x04; + /* in the Creative/VisionTek BreakOut-Box: bit 7-6 */ + } else if (((ret2 & 0xc0) == 0xc0) && ((ret4 & 0xc0) == 0x00)) { + ir->bits = (unsigned char) ~0xe0; + ir->flag = 0x20; + } + + return; +} + +static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct IR *ir; + struct i2c_adapter *adap = client->adapter; + unsigned short addr = client->addr; + int retval; + + ir = kzalloc(sizeof(struct IR), GFP_KERNEL); + if (!ir) + return -ENOMEM; + memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver)); + memcpy(&ir->c, client, sizeof(struct i2c_client)); + + i2c_set_clientdata(client, ir); + ir->l.data = ir; + ir->l.minor = minor; + ir->l.sample_rate = 10; + ir->l.dev = &ir->c.dev; + ir->nextkey = -1; + + switch (addr) { + case 0x64: + strlcpy(ir->c.name, "Pixelview IR", I2C_NAME_SIZE); + ir->l.code_length = 8; + ir->l.add_to_buf = add_to_buf_pixelview; + break; + case 0x4b: + strlcpy(ir->c.name, "PV951 IR", I2C_NAME_SIZE); + ir->l.code_length = 32; + ir->l.add_to_buf = add_to_buf_pv951; + break; + case 0x71: + if (adap->id == I2C_HW_B_CX2388x) + strlcpy(ir->c.name, "Hauppauge HVR1300", I2C_NAME_SIZE); + else /* bt8xx or cx2341x */ + /* + * The PVR150 IR receiver uses the same protocol as + * other Hauppauge cards, but the data flow is + * different, so we need to deal with it by its own. + */ + strlcpy(ir->c.name, "Hauppauge PVR150", I2C_NAME_SIZE); + ir->l.code_length = 13; + ir->l.add_to_buf = add_to_buf_haup_pvr150; + break; + case 0x6b: + strlcpy(ir->c.name, "Adaptec IR", I2C_NAME_SIZE); + ir->l.code_length = 32; + ir->l.add_to_buf = add_to_buf_adap; + break; + case 0x18: + case 0x1a: + if (adap->id == I2C_HW_B_CX2388x) { + strlcpy(ir->c.name, "Leadtek IR", I2C_NAME_SIZE); + ir->l.code_length = 8; + ir->l.add_to_buf = add_to_buf_pvr2000; + } else { /* bt8xx or cx2341x */ + strlcpy(ir->c.name, "Hauppauge IR", I2C_NAME_SIZE); + ir->l.code_length = 13; + ir->l.add_to_buf = add_to_buf_haup; + } + break; + case 0x30: + strlcpy(ir->c.name, "KNC ONE IR", I2C_NAME_SIZE); + ir->l.code_length = 8; + ir->l.add_to_buf = add_to_buf_knc1; + break; + case 0x21: + case 0x23: + pcf_probe(client, ir); + strlcpy(ir->c.name, "TV-Box IR", I2C_NAME_SIZE); + ir->l.code_length = 8; + ir->l.add_to_buf = add_to_buf_pcf8574; + break; + default: + /* shouldn't happen */ + printk("lirc_i2c: Huh? unknown i2c address (0x%02x)?\n", addr); + kfree(ir); + return -EINVAL; + } + printk(KERN_INFO "lirc_i2c: chip 0x%x found @ 0x%02x (%s)\n", + adap->id, addr, ir->c.name); + + retval = lirc_register_driver(&ir->l); + + if (retval < 0) { + printk(KERN_ERR "lirc_i2c: failed to register driver!\n"); + kfree(ir); + return retval; + } + + ir->l.minor = retval; + + return 0; +} + +static int ir_remove(struct i2c_client *client) +{ + struct IR *ir = i2c_get_clientdata(client); + + /* unregister device */ + lirc_unregister_driver(ir->l.minor); + + /* free memory */ + kfree(ir); + return 0; +} + +static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + /* nothing */ + return 0; +} + +static int __init lirc_i2c_init(void) +{ + i2c_add_driver(&driver); + return 0; +} + +static void __exit lirc_i2c_exit(void) +{ + i2c_del_driver(&driver); +} + +MODULE_DESCRIPTION("Infrared receiver driver for Hauppauge and " + "Pixelview cards (i2c stack)"); +MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, " + "Ulrich Mueller, Stefan Jahn, Jerome Brock"); +MODULE_LICENSE("GPL"); + +module_param(minor, int, S_IRUGO); +MODULE_PARM_DESC(minor, "Preferred minor device number"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Enable debugging messages"); + +module_init(lirc_i2c_init); +module_exit(lirc_i2c_exit); diff --git a/drivers/staging/lirc/lirc_igorplugusb.c b/drivers/staging/lirc/lirc_igorplugusb.c new file mode 100644 index 0000000..bce600e --- /dev/null +++ b/drivers/staging/lirc/lirc_igorplugusb.c @@ -0,0 +1,555 @@ +/* + * lirc_igorplugusb - USB remote support for LIRC + * + * Supports the standard homebrew IgorPlugUSB receiver with Igor's firmware. + * See http://www.cesko.host.sk/IgorPlugUSB/IgorPlug-USB%20(AVR)_eng.htm + * + * The device can only record bursts of up to 36 pulses/spaces. + * Works fine with RC5. Longer commands lead to device buffer overrun. + * (Maybe a better firmware or a microcontroller with more ram can help?) + * + * Version 0.1 [beta status] + * + * Copyright (C) 2004 Jan M. Hochstein + * <hochstein@algo.informatik.tu-darmstadt.de> + * + * This driver was derived from: + * Paul Miller <pmiller9@users.sourceforge.net> + * "lirc_atiusb" module + * Vladimir Dergachev <volodya@minspring.com>'s 2002 + * "USB ATI Remote support" (input device) + * Adrian Dewhurst <sailor-lk@sailorfrag.net>'s 2002 + * "USB StreamZap remote driver" (LIRC) + * Artur Lipowski <alipowski@kki.net.pl>'s 2002 + * "lirc_dev" and "lirc_gpio" LIRC modules + */ + +/* + * 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/kmod.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/usb.h> +#include <linux/time.h> + +#include <media/lirc.h> +#include <media/lirc_dev.h> + + +/* module identification */ +#define DRIVER_VERSION "0.1" +#define DRIVER_AUTHOR \ + "Jan M. Hochstein <hochstein@algo.informatik.tu-darmstadt.de>" +#define DRIVER_DESC "USB remote driver for LIRC" +#define DRIVER_NAME "lirc_igorplugusb" + +/* debugging support */ +#ifdef CONFIG_USB_DEBUG +static int debug = 1; +#else +static int debug; +#endif + +#define dprintk(fmt, args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG fmt, ## args); \ + } while (0) + +/* One mode2 pulse/space has 4 bytes. */ +#define CODE_LENGTH sizeof(int) + +/* Igor's firmware cannot record bursts longer than 36. */ +#define DEVICE_BUFLEN 36 + +/* + * Header at the beginning of the device's buffer: + * unsigned char data_length + * unsigned char data_start (!=0 means ring-buffer overrun) + * unsigned char counter (incremented by each burst) + */ +#define DEVICE_HEADERLEN 3 + +/* This is for the gap */ +#define ADDITIONAL_LIRC_BYTES 2 + +/* times to poll per second */ +#define SAMPLE_RATE 100 +static int sample_rate = SAMPLE_RATE; + + +/**** Igor's USB Request Codes */ + +#define SET_INFRABUFFER_EMPTY 1 +/** + * Params: none + * Answer: empty + */ + +#define GET_INFRACODE 2 +/** + * Params: + * wValue: offset to begin reading infra buffer + * + * Answer: infra data + */ + +#define SET_DATAPORT_DIRECTION 3 +/** + * Params: + * wValue: (byte) 1 bit for each data port pin (0=in, 1=out) + * + * Answer: empty + */ + +#define GET_DATAPORT_DIRECTION 4 +/** + * Params: none + * + * Answer: (byte) 1 bit for each data port pin (0=in, 1=out) + */ + +#define SET_OUT_DATAPORT 5 +/** + * Params: + * wValue: byte to write to output data port + * + * Answer: empty + */ + +#define GET_OUT_DATAPORT 6 +/** + * Params: none + * + * Answer: least significant 3 bits read from output data port + */ + +#define GET_IN_DATAPORT 7 +/** + * Params: none + * + * Answer: least significant 3 bits read from input data port + */ + +#define READ_EEPROM 8 +/** + * Params: + * wValue: offset to begin reading EEPROM + * + * Answer: EEPROM bytes + */ + +#define WRITE_EEPROM 9 +/** + * Params: + * wValue: offset to EEPROM byte + * wIndex: byte to write + * + * Answer: empty + */ + +#define SEND_RS232 10 +/** + * Params: + * wValue: byte to send + * + * Answer: empty + */ + +#define RECV_RS232 11 +/** + * Params: none + * + * Answer: byte received + */ + +#define SET_RS232_BAUD 12 +/** + * Params: + * wValue: byte to write to UART bit rate register (UBRR) + * + * Answer: empty + */ + +#define GET_RS232_BAUD 13 +/** + * Params: none + * + * Answer: byte read from UART bit rate register (UBRR) + */ + + +/* data structure for each usb remote */ +struct igorplug { + + /* usb */ + struct usb_device *usbdev; + struct urb *urb_in; + int devnum; + + unsigned char *buf_in; + unsigned int len_in; + int in_space; + struct timeval last_time; + + dma_addr_t dma_in; + + /* lirc */ + struct lirc_driver *d; + + /* handle sending (init strings) */ + int send_flags; + wait_queue_head_t wait_out; +}; + +static int unregister_from_lirc(struct igorplug *ir) +{ + struct lirc_driver *d = ir->d; + int devnum; + + if (!ir->d) + return -EINVAL; + + devnum = ir->devnum; + dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum); + + lirc_unregister_driver(d->minor); + + printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum); + + kfree(d); + ir->d = NULL; + kfree(ir); + return 0; +} + +static int set_use_inc(void *data) +{ + struct igorplug *ir = data; + + if (!ir) { + printk(DRIVER_NAME "[?]: set_use_inc called with no context\n"); + return -EIO; + } + dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum); + + if (!ir->usbdev) + return -ENODEV; + + return 0; +} + +static void set_use_dec(void *data) +{ + struct igorplug *ir = data; + + if (!ir) { + printk(DRIVER_NAME "[?]: set_use_dec called with no context\n"); + return; + } + dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum); +} + + +/** + * Called in user context. + * return 0 if data was added to the buffer and + * -ENODATA if none was available. This should add some number of bits + * evenly divisible by code_length to the buffer + */ +static int usb_remote_poll(void *data, struct lirc_buffer *buf) +{ + int ret; + struct igorplug *ir = (struct igorplug *)data; + + if (!ir->usbdev) /* Has the device been removed? */ + return -ENODEV; + + memset(ir->buf_in, 0, ir->len_in); + + ret = usb_control_msg( + ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), + GET_INFRACODE, USB_TYPE_VENDOR|USB_DIR_IN, + 0/* offset */, /*unused*/0, + ir->buf_in, ir->len_in, + /*timeout*/HZ * USB_CTRL_GET_TIMEOUT); + if (ret > 0) { + int i = DEVICE_HEADERLEN; + int code, timediff; + struct timeval now; + + if (ret <= 1) /* ACK packet has 1 byte --> ignore */ + return -ENODATA; + + dprintk(DRIVER_NAME ": Got %d bytes. Header: %02x %02x %02x\n", + ret, ir->buf_in[0], ir->buf_in[1], ir->buf_in[2]); + + if (ir->buf_in[2] != 0) { + printk(DRIVER_NAME "[%d]: Device buffer overrun.\n", + ir->devnum); + /* start at earliest byte */ + i = DEVICE_HEADERLEN + ir->buf_in[2]; + /* where are we now? space, gap or pulse? */ + } + + do_gettimeofday(&now); + timediff = now.tv_sec - ir->last_time.tv_sec; + if (timediff + 1 > PULSE_MASK / 1000000) + timediff = PULSE_MASK; + else { + timediff *= 1000000; + timediff += now.tv_usec - ir->last_time.tv_usec; + } + ir->last_time.tv_sec = now.tv_sec; + ir->last_time.tv_usec = now.tv_usec; + + /* create leading gap */ + code = timediff; + lirc_buffer_write(buf, (unsigned char *)&code); + ir->in_space = 1; /* next comes a pulse */ + + /* MODE2: pulse/space (PULSE_BIT) in 1us units */ + + while (i < ret) { + /* 1 Igor-tick = 85.333333 us */ + code = (unsigned int)ir->buf_in[i] * 85 + + (unsigned int)ir->buf_in[i] / 3; + if (ir->in_space) + code |= PULSE_BIT; + lirc_buffer_write(buf, (unsigned char *)&code); + /* 1 chunk = CODE_LENGTH bytes */ + ir->in_space ^= 1; + ++i; + } + + ret = usb_control_msg( + ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), + SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN, + /*unused*/0, /*unused*/0, + /*dummy*/ir->buf_in, /*dummy*/ir->len_in, + /*timeout*/HZ * USB_CTRL_GET_TIMEOUT); + if (ret < 0) + printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: " + "error %d\n", ir->devnum, ret); + return 0; + } else if (ret < 0) + printk(DRIVER_NAME "[%d]: GET_INFRACODE: error %d\n", + ir->devnum, ret); + + return -ENODATA; +} + + + +static int usb_remote_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *dev = NULL; + struct usb_host_interface *idesc = NULL; + struct usb_host_endpoint *ep_ctl2; + struct igorplug *ir = NULL; + struct lirc_driver *driver = NULL; + int devnum, pipe, maxp; + int minor = 0; + char buf[63], name[128] = ""; + int mem_failure = 0; + int ret; + + dprintk(DRIVER_NAME ": usb probe called.\n"); + + dev = interface_to_usbdev(intf); + + idesc = intf->cur_altsetting; + + if (idesc->desc.bNumEndpoints != 1) + return -ENODEV; + ep_ctl2 = idesc->endpoint; + if (((ep_ctl2->desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) + != USB_DIR_IN) + || (ep_ctl2->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + != USB_ENDPOINT_XFER_CONTROL) + return -ENODEV; + pipe = usb_rcvctrlpipe(dev, ep_ctl2->desc.bEndpointAddress); + devnum = dev->devnum; + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + dprintk(DRIVER_NAME "[%d]: bytes_in_key=%lu maxp=%d\n", + devnum, CODE_LENGTH, maxp); + + + mem_failure = 0; + ir = kzalloc(sizeof(struct igorplug), GFP_KERNEL); + if (!ir) { + mem_failure = 1; + goto mem_failure_switch; + } + driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); + if (!driver) { + mem_failure = 2; + goto mem_failure_switch; + } + + ir->buf_in = usb_alloc_coherent(dev, + DEVICE_BUFLEN+DEVICE_HEADERLEN, + GFP_ATOMIC, &ir->dma_in); + if (!ir->buf_in) { + mem_failure = 3; + goto mem_failure_switch; + } + + strcpy(driver->name, DRIVER_NAME " "); + driver->minor = -1; + driver->code_length = CODE_LENGTH * 8; /* in bits */ + driver->features = LIRC_CAN_REC_MODE2; + driver->data = ir; + driver->chunk_size = CODE_LENGTH; + driver->buffer_size = DEVICE_BUFLEN + ADDITIONAL_LIRC_BYTES; + driver->set_use_inc = &set_use_inc; + driver->set_use_dec = &set_use_dec; + driver->sample_rate = sample_rate; /* per second */ + driver->add_to_buf = &usb_remote_poll; + driver->dev = &intf->dev; + driver->owner = THIS_MODULE; + + init_waitqueue_head(&ir->wait_out); + + minor = lirc_register_driver(driver); + if (minor < 0) + mem_failure = 9; + +mem_failure_switch: + + switch (mem_failure) { + case 9: + usb_free_coherent(dev, DEVICE_BUFLEN+DEVICE_HEADERLEN, + ir->buf_in, ir->dma_in); + case 3: + kfree(driver); + case 2: + kfree(ir); + case 1: + printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n", + devnum, mem_failure); + return -ENOMEM; + } + + driver->minor = minor; + ir->d = driver; + ir->devnum = devnum; + ir->usbdev = dev; + ir->len_in = DEVICE_BUFLEN+DEVICE_HEADERLEN; + ir->in_space = 1; /* First mode2 event is a space. */ + do_gettimeofday(&ir->last_time); + + if (dev->descriptor.iManufacturer + && usb_string(dev, dev->descriptor.iManufacturer, + buf, sizeof(buf)) > 0) + strlcpy(name, buf, sizeof(name)); + if (dev->descriptor.iProduct + && usb_string(dev, dev->descriptor.iProduct, buf, sizeof(buf)) > 0) + snprintf(name + strlen(name), sizeof(name) - strlen(name), + " %s", buf); + printk(DRIVER_NAME "[%d]: %s on usb%d:%d\n", devnum, name, + dev->bus->busnum, devnum); + + /* clear device buffer */ + ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), + SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN, + /*unused*/0, /*unused*/0, + /*dummy*/ir->buf_in, /*dummy*/ir->len_in, + /*timeout*/HZ * USB_CTRL_GET_TIMEOUT); + if (ret < 0) + printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: error %d\n", + devnum, ret); + + usb_set_intfdata(intf, ir); + return 0; +} + + +static void usb_remote_disconnect(struct usb_interface *intf) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct igorplug *ir = usb_get_intfdata(intf); + usb_set_intfdata(intf, NULL); + + if (!ir || !ir->d) + return; + + ir->usbdev = NULL; + wake_up_all(&ir->wait_out); + + usb_free_coherent(dev, ir->len_in, ir->buf_in, ir->dma_in); + + unregister_from_lirc(ir); +} + +static struct usb_device_id usb_remote_id_table[] = { + /* Igor Plug USB (Atmel's Manufact. ID) */ + { USB_DEVICE(0x03eb, 0x0002) }, + + /* Terminating entry */ + { } +}; + +static struct usb_driver usb_remote_driver = { + .name = DRIVER_NAME, + .probe = usb_remote_probe, + .disconnect = usb_remote_disconnect, + .id_table = usb_remote_id_table +}; + +static int __init usb_remote_init(void) +{ + int i; + + printk(KERN_INFO "\n" + DRIVER_NAME ": " DRIVER_DESC " v" DRIVER_VERSION "\n"); + printk(DRIVER_NAME ": " DRIVER_AUTHOR "\n"); + dprintk(DRIVER_NAME ": debug mode enabled\n"); + + i = usb_register(&usb_remote_driver); + if (i < 0) { + printk(DRIVER_NAME ": usb register failed, result = %d\n", i); + return -ENODEV; + } + + return 0; +} + +static void __exit usb_remote_exit(void) +{ + usb_deregister(&usb_remote_driver); +} + +module_init(usb_remote_init); +module_exit(usb_remote_exit); + +#include <linux/vermagic.h> +MODULE_INFO(vermagic, VERMAGIC_STRING); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(usb, usb_remote_id_table); + +module_param(sample_rate, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(sample_rate, "Sampling rate in Hz (default: 100)"); + diff --git a/drivers/staging/lirc/lirc_imon.c b/drivers/staging/lirc/lirc_imon.c new file mode 100644 index 0000000..6649325 --- /dev/null +++ b/drivers/staging/lirc/lirc_imon.c @@ -0,0 +1,1058 @@ +/* + * lirc_imon.c: LIRC/VFD/LCD driver for SoundGraph iMON IR/VFD/LCD + * including the iMON PAD model + * + * Copyright(C) 2004 Venky Raju(dev@venky.ws) + * Copyright(C) 2009 Jarod Wilson <jarod@wilsonet.com> + * + * lirc_imon 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/errno.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/usb.h> + +#include <media/lirc.h> +#include <media/lirc_dev.h> + + +#define MOD_AUTHOR "Venky Raju <dev@venky.ws>" +#define MOD_DESC "Driver for SoundGraph iMON MultiMedia IR/Display" +#define MOD_NAME "lirc_imon" +#define MOD_VERSION "0.8" + +#define DISPLAY_MINOR_BASE 144 +#define DEVICE_NAME "lcd%d" + +#define BUF_CHUNK_SIZE 4 +#define BUF_SIZE 128 + +#define BIT_DURATION 250 /* each bit received is 250us */ + +/*** P R O T O T Y P E S ***/ + +/* USB Callback prototypes */ +static int imon_probe(struct usb_interface *interface, + const struct usb_device_id *id); +static void imon_disconnect(struct usb_interface *interface); +static void usb_rx_callback(struct urb *urb); +static void usb_tx_callback(struct urb *urb); + +/* suspend/resume support */ +static int imon_resume(struct usb_interface *intf); +static int imon_suspend(struct usb_interface *intf, pm_message_t message); + +/* Display file_operations function prototypes */ +static int display_open(struct inode *inode, struct file *file); +static int display_close(struct inode *inode, struct file *file); + +/* VFD write operation */ +static ssize_t vfd_write(struct file *file, const char *buf, + size_t n_bytes, loff_t *pos); + +/* LIRC driver function prototypes */ +static int ir_open(void *data); +static void ir_close(void *data); + +/* Driver init/exit prototypes */ +static int __init imon_init(void); +static void __exit imon_exit(void); + +/*** G L O B A L S ***/ +#define IMON_DATA_BUF_SZ 35 + +struct imon_context { + struct usb_device *usbdev; + /* Newer devices have two interfaces */ + int display; /* not all controllers do */ + int display_isopen; /* display port has been opened */ + int ir_isopen; /* IR port open */ + int dev_present; /* USB device presence */ + struct mutex ctx_lock; /* to lock this object */ + wait_queue_head_t remove_ok; /* For unexpected USB disconnects */ + + int vfd_proto_6p; /* some VFD require a 6th packet */ + + struct lirc_driver *driver; + struct usb_endpoint_descriptor *rx_endpoint; + struct usb_endpoint_descriptor *tx_endpoint; + struct urb *rx_urb; + struct urb *tx_urb; + unsigned char usb_rx_buf[8]; + unsigned char usb_tx_buf[8]; + + struct rx_data { + int count; /* length of 0 or 1 sequence */ + int prev_bit; /* logic level of sequence */ + int initial_space; /* initial space flag */ + } rx; + + struct tx_t { + unsigned char data_buf[IMON_DATA_BUF_SZ]; /* user data buffer */ + struct completion finished; /* wait for write to finish */ + atomic_t busy; /* write in progress */ + int status; /* status of tx completion */ + } tx; +}; + +static const struct file_operations display_fops = { + .owner = THIS_MODULE, + .open = &display_open, + .write = &vfd_write, + .release = &display_close +}; + +/* + * USB Device ID for iMON USB Control Boards + * + * The Windows drivers contain 6 different inf files, more or less one for + * each new device until the 0x0034-0x0046 devices, which all use the same + * driver. Some of the devices in the 34-46 range haven't been definitively + * identified yet. Early devices have either a TriGem Computer, Inc. or a + * Samsung vendor ID (0x0aa8 and 0x04e8 respectively), while all later + * devices use the SoundGraph vendor ID (0x15c2). + */ +static struct usb_device_id imon_usb_id_table[] = { + /* TriGem iMON (IR only) -- TG_iMON.inf */ + { USB_DEVICE(0x0aa8, 0x8001) }, + + /* SoundGraph iMON (IR only) -- sg_imon.inf */ + { USB_DEVICE(0x04e8, 0xff30) }, + + /* SoundGraph iMON VFD (IR & VFD) -- iMON_VFD.inf */ + { USB_DEVICE(0x0aa8, 0xffda) }, + + /* SoundGraph iMON SS (IR & VFD) -- iMON_SS.inf */ + { USB_DEVICE(0x15c2, 0xffda) }, + + {} +}; + +/* Some iMON VFD models requires a 6th packet for VFD writes */ +static struct usb_device_id vfd_proto_6p_list[] = { + { USB_DEVICE(0x15c2, 0xffda) }, + {} +}; + +/* Some iMON devices have no lcd/vfd, don't set one up */ +static struct usb_device_id ir_only_list[] = { + { USB_DEVICE(0x0aa8, 0x8001) }, + { USB_DEVICE(0x04e8, 0xff30) }, + {} +}; + +/* USB Device data */ +static struct usb_driver imon_driver = { + .name = MOD_NAME, + .probe = imon_probe, + .disconnect = imon_disconnect, + .suspend = imon_suspend, + .resume = imon_resume, + .id_table = imon_usb_id_table, +}; + +static struct usb_class_driver imon_class = { + .name = DEVICE_NAME, + .fops = &display_fops, + .minor_base = DISPLAY_MINOR_BASE, +}; + +/* to prevent races between open() and disconnect(), probing, etc */ +static DEFINE_MUTEX(driver_lock); + +static int debug; + +/*** M O D U L E C O D E ***/ + +MODULE_AUTHOR(MOD_AUTHOR); +MODULE_DESCRIPTION(MOD_DESC); +MODULE_VERSION(MOD_VERSION); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(usb, imon_usb_id_table); +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes(default: no)"); + +static void free_imon_context(struct imon_context *context) +{ + struct device *dev = context->driver->dev; + usb_free_urb(context->tx_urb); + usb_free_urb(context->rx_urb); + lirc_buffer_free(context->driver->rbuf); + kfree(context->driver->rbuf); + kfree(context->driver); + kfree(context); + + dev_dbg(dev, "%s: iMON context freed\n", __func__); +} + +static void deregister_from_lirc(struct imon_context *context) +{ + int retval; + int minor = context->driver->minor; + + retval = lirc_unregister_driver(minor); + if (retval) + err("%s: unable to deregister from lirc(%d)", + __func__, retval); + else + printk(KERN_INFO MOD_NAME ": Deregistered iMON driver " + "(minor:%d)\n", minor); + +} + +/** + * Called when the Display device (e.g. /dev/lcd0) + * is opened by the application. + */ +static int display_open(struct inode *inode, struct file *file) +{ + struct usb_interface *interface; + struct imon_context *context = NULL; + int subminor; + int retval = 0; + + /* prevent races with disconnect */ + mutex_lock(&driver_lock); + + subminor = iminor(inode); + interface = usb_find_interface(&imon_driver, subminor); + if (!interface) { + err("%s: could not find interface for minor %d", + __func__, subminor); + retval = -ENODEV; + goto exit; + } + context = usb_get_intfdata(interface); + + if (!context) { + err("%s: no context found for minor %d", + __func__, subminor); + retval = -ENODEV; + goto exit; + } + + mutex_lock(&context->ctx_lock); + + if (!context->display) { + err("%s: display not supported by device", __func__); + retval = -ENODEV; + } else if (context->display_isopen) { + err("%s: display port is already open", __func__); + retval = -EBUSY; + } else { + context->display_isopen = 1; + file->private_data = context; + dev_info(context->driver->dev, "display port opened\n"); + } + + mutex_unlock(&context->ctx_lock); + +exit: + mutex_unlock(&driver_lock); + return retval; +} + +/** + * Called when the display device (e.g. /dev/lcd0) + * is closed by the application. + */ +static int display_close(struct inode *inode, struct file *file) +{ + struct imon_context *context = NULL; + int retval = 0; + + context = (struct imon_context *)file->private_data; + + if (!context) { + err("%s: no context for device", __func__); + return -ENODEV; + } + + mutex_lock(&context->ctx_lock); + + if (!context->display) { + err("%s: display not supported by device", __func__); + retval = -ENODEV; + } else if (!context->display_isopen) { + err("%s: display is not open", __func__); + retval = -EIO; + } else { + context->display_isopen = 0; + dev_info(context->driver->dev, "display port closed\n"); + if (!context->dev_present && !context->ir_isopen) { + /* + * Device disconnected before close and IR port is not + * open. If IR port is open, context will be deleted by + * ir_close. + */ + mutex_unlock(&context->ctx_lock); + free_imon_context(context); + return retval; + } + } + + mutex_unlock(&context->ctx_lock); + return retval; +} + +/** + * Sends a packet to the device -- this function must be called + * with context->ctx_lock held. + */ +static int send_packet(struct imon_context *context) +{ + unsigned int pipe; + int interval = 0; + int retval = 0; + struct usb_ctrlrequest *control_req = NULL; + + /* Check if we need to use control or interrupt urb */ + pipe = usb_sndintpipe(context->usbdev, + context->tx_endpoint->bEndpointAddress); + interval = context->tx_endpoint->bInterval; + + usb_fill_int_urb(context->tx_urb, context->usbdev, pipe, + context->usb_tx_buf, + sizeof(context->usb_tx_buf), + usb_tx_callback, context, interval); + + context->tx_urb->actual_length = 0; + + init_completion(&context->tx.finished); + atomic_set(&(context->tx.busy), 1); + + retval = usb_submit_urb(context->tx_urb, GFP_KERNEL); + if (retval) { + atomic_set(&(context->tx.busy), 0); + err("%s: error submitting urb(%d)", __func__, retval); + } else { + /* Wait for transmission to complete (or abort) */ + mutex_unlock(&context->ctx_lock); + retval = wait_for_completion_interruptible( + &context->tx.finished); + if (retval) + err("%s: task interrupted", __func__); + mutex_lock(&context->ctx_lock); + + retval = context->tx.status; + if (retval) + err("%s: packet tx failed (%d)", __func__, retval); + } + + kfree(control_req); + + return retval; +} + +/** + * Writes data to the VFD. The iMON VFD is 2x16 characters + * and requires data in 5 consecutive USB interrupt packets, + * each packet but the last carrying 7 bytes. + * + * I don't know if the VFD board supports features such as + * scrolling, clearing rows, blanking, etc. so at + * the caller must provide a full screen of data. If fewer + * than 32 bytes are provided spaces will be appended to + * generate a full screen. + */ +static ssize_t vfd_write(struct file *file, const char *buf, + size_t n_bytes, loff_t *pos) +{ + int i; + int offset; + int seq; + int retval = 0; + struct imon_context *context; + const unsigned char vfd_packet6[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }; + int *data_buf; + + context = (struct imon_context *)file->private_data; + if (!context) { + err("%s: no context for device", __func__); + return -ENODEV; + } + + mutex_lock(&context->ctx_lock); + + if (!context->dev_present) { + err("%s: no iMON device present", __func__); + retval = -ENODEV; + goto exit; + } + + if (n_bytes <= 0 || n_bytes > IMON_DATA_BUF_SZ - 3) { + err("%s: invalid payload size", __func__); + retval = -EINVAL; + goto exit; + } + + data_buf = memdup_user(buf, n_bytes); + if (IS_ERR(data_buf)) { + retval = PTR_ERR(data_buf); + goto exit; + } + + memcpy(context->tx.data_buf, data_buf, n_bytes); + + /* Pad with spaces */ + for (i = n_bytes; i < IMON_DATA_BUF_SZ - 3; ++i) + context->tx.data_buf[i] = ' '; + + for (i = IMON_DATA_BUF_SZ - 3; i < IMON_DATA_BUF_SZ; ++i) + context->tx.data_buf[i] = 0xFF; + + offset = 0; + seq = 0; + + do { + memcpy(context->usb_tx_buf, context->tx.data_buf + offset, 7); + context->usb_tx_buf[7] = (unsigned char) seq; + + retval = send_packet(context); + if (retval) { + err("%s: send packet failed for packet #%d", + __func__, seq/2); + goto exit; + } else { + seq += 2; + offset += 7; + } + + } while (offset < IMON_DATA_BUF_SZ); + + if (context->vfd_proto_6p) { + /* Send packet #6 */ + memcpy(context->usb_tx_buf, &vfd_packet6, sizeof(vfd_packet6)); + context->usb_tx_buf[7] = (unsigned char) seq; + retval = send_packet(context); + if (retval) + err("%s: send packet failed for packet #%d", + __func__, seq/2); + } + +exit: + mutex_unlock(&context->ctx_lock); + + return (!retval) ? n_bytes : retval; +} + +/** + * Callback function for USB core API: transmit data + */ +static void usb_tx_callback(struct urb *urb) +{ + struct imon_context *context; + + if (!urb) + return; + context = (struct imon_context *)urb->context; + if (!context) + return; + + context->tx.status = urb->status; + + /* notify waiters that write has finished */ + atomic_set(&context->tx.busy, 0); + complete(&context->tx.finished); + + return; +} + +/** + * Called by lirc_dev when the application opens /dev/lirc + */ +static int ir_open(void *data) +{ + int retval = 0; + struct imon_context *context; + + /* prevent races with disconnect */ + mutex_lock(&driver_lock); + + context = (struct imon_context *)data; + + /* initial IR protocol decode variables */ + context->rx.count = 0; + context->rx.initial_space = 1; + context->rx.prev_bit = 0; + + context->ir_isopen = 1; + dev_info(context->driver->dev, "IR port opened\n"); + + mutex_unlock(&driver_lock); + return retval; +} + +/** + * Called by lirc_dev when the application closes /dev/lirc + */ +static void ir_close(void *data) +{ + struct imon_context *context; + + context = (struct imon_context *)data; + if (!context) { + err("%s: no context for device", __func__); + return; + } + + mutex_lock(&context->ctx_lock); + + context->ir_isopen = 0; + dev_info(context->driver->dev, "IR port closed\n"); + + if (!context->dev_present) { + /* + * Device disconnected while IR port was still open. Driver + * was not deregistered at disconnect time, so do it now. + */ + deregister_from_lirc(context); + + if (!context->display_isopen) { + mutex_unlock(&context->ctx_lock); + free_imon_context(context); + return; + } + /* + * If display port is open, context will be deleted by + * display_close + */ + } + + mutex_unlock(&context->ctx_lock); + return; +} + +/** + * Convert bit count to time duration (in us) and submit + * the value to lirc_dev. + */ +static void submit_data(struct imon_context *context) +{ + unsigned char buf[4]; + int value = context->rx.count; + int i; + + dev_dbg(context->driver->dev, "submitting data to LIRC\n"); + + value *= BIT_DURATION; + value &= PULSE_MASK; + if (context->rx.prev_bit) + value |= PULSE_BIT; + + for (i = 0; i < 4; ++i) + buf[i] = value>>(i*8); + + lirc_buffer_write(context->driver->rbuf, buf); + wake_up(&context->driver->rbuf->wait_poll); + return; +} + +static inline int tv2int(const struct timeval *a, const struct timeval *b) +{ + int usecs = 0; + int sec = 0; + + if (b->tv_usec > a->tv_usec) { + usecs = 1000000; + sec--; + } + + usecs += a->tv_usec - b->tv_usec; + + sec += a->tv_sec - b->tv_sec; + sec *= 1000; + usecs /= 1000; + sec += usecs; + + if (sec < 0) + sec = 1000; + + return sec; +} + +/** + * Process the incoming packet + */ +static void imon_incoming_packet(struct imon_context *context, + struct urb *urb, int intf) +{ + int len = urb->actual_length; + unsigned char *buf = urb->transfer_buffer; + struct device *dev = context->driver->dev; + int octet, bit; + unsigned char mask; + int i, chunk_num; + + /* + * just bail out if no listening IR client + */ + if (!context->ir_isopen) + return; + + if (len != 8) { + dev_warn(dev, "imon %s: invalid incoming packet " + "size (len = %d, intf%d)\n", __func__, len, intf); + return; + } + + if (debug) { + printk(KERN_INFO "raw packet: "); + for (i = 0; i < len; ++i) + printk("%02x ", buf[i]); + printk("\n"); + } + + /* + * Translate received data to pulse and space lengths. + * Received data is active low, i.e. pulses are 0 and + * spaces are 1. + * + * My original algorithm was essentially similar to + * Changwoo Ryu's with the exception that he switched + * the incoming bits to active high and also fed an + * initial space to LIRC at the start of a new sequence + * if the previous bit was a pulse. + * + * I've decided to adopt his algorithm. + */ + + if (buf[7] == 1 && context->rx.initial_space) { + /* LIRC requires a leading space */ + context->rx.prev_bit = 0; + context->rx.count = 4; + submit_data(context); + context->rx.count = 0; + } + + for (octet = 0; octet < 5; ++octet) { + mask = 0x80; + for (bit = 0; bit < 8; ++bit) { + int curr_bit = !(buf[octet] & mask); + if (curr_bit != context->rx.prev_bit) { + if (context->rx.count) { + submit_data(context); + context->rx.count = 0; + } + context->rx.prev_bit = curr_bit; + } + ++context->rx.count; + mask >>= 1; + } + } + + if (chunk_num == 10) { + if (context->rx.count) { + submit_data(context); + context->rx.count = 0; + } + context->rx.initial_space = context->rx.prev_bit; + } +} + +/** + * Callback function for USB core API: receive data + */ +static void usb_rx_callback(struct urb *urb) +{ + struct imon_context *context; + unsigned char *buf; + int len; + int intfnum = 0; + + if (!urb) + return; + + context = (struct imon_context *)urb->context; + if (!context) + return; + + buf = urb->transfer_buffer; + len = urb->actual_length; + + switch (urb->status) { + case -ENOENT: /* usbcore unlink successful! */ + return; + + case 0: + imon_incoming_packet(context, urb, intfnum); + break; + + default: + dev_warn(context->driver->dev, "imon %s: status(%d): ignored\n", + __func__, urb->status); + break; + } + + usb_submit_urb(context->rx_urb, GFP_ATOMIC); + + return; +} + +/** + * Callback function for USB core API: Probe + */ +static int imon_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *usbdev = NULL; + struct usb_host_interface *iface_desc = NULL; + struct usb_endpoint_descriptor *rx_endpoint = NULL; + struct usb_endpoint_descriptor *tx_endpoint = NULL; + struct urb *rx_urb = NULL; + struct urb *tx_urb = NULL; + struct lirc_driver *driver = NULL; + struct lirc_buffer *rbuf = NULL; + struct device *dev = &interface->dev; + int ifnum; + int lirc_minor = 0; + int num_endpts; + int retval = 0; + int display_ep_found = 0; + int ir_ep_found = 0; + int alloc_status = 0; + int vfd_proto_6p = 0; + int code_length; + struct imon_context *context = NULL; + int i; + u16 vendor, product; + + context = kzalloc(sizeof(struct imon_context), GFP_KERNEL); + if (!context) { + err("%s: kzalloc failed for context", __func__); + alloc_status = 1; + goto alloc_status_switch; + } + + /* + * Try to auto-detect the type of display if the user hasn't set + * it by hand via the display_type modparam. Default is VFD. + */ + if (usb_match_id(interface, ir_only_list)) + context->display = 0; + else + context->display = 1; + + code_length = BUF_CHUNK_SIZE * 8; + + usbdev = usb_get_dev(interface_to_usbdev(interface)); + iface_desc = interface->cur_altsetting; + num_endpts = iface_desc->desc.bNumEndpoints; + ifnum = iface_desc->desc.bInterfaceNumber; + vendor = le16_to_cpu(usbdev->descriptor.idVendor); + product = le16_to_cpu(usbdev->descriptor.idProduct); + + dev_dbg(dev, "%s: found iMON device (%04x:%04x, intf%d)\n", + __func__, vendor, product, ifnum); + + /* prevent races probing devices w/multiple interfaces */ + mutex_lock(&driver_lock); + + /* + * Scan the endpoint list and set: + * first input endpoint = IR endpoint + * first output endpoint = display endpoint + */ + for (i = 0; i < num_endpts && !(ir_ep_found && display_ep_found); ++i) { + struct usb_endpoint_descriptor *ep; + int ep_dir; + int ep_type; + ep = &iface_desc->endpoint[i].desc; + ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK; + ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + + if (!ir_ep_found && + ep_dir == USB_DIR_IN && + ep_type == USB_ENDPOINT_XFER_INT) { + + rx_endpoint = ep; + ir_ep_found = 1; + dev_dbg(dev, "%s: found IR endpoint\n", __func__); + + } else if (!display_ep_found && ep_dir == USB_DIR_OUT && + ep_type == USB_ENDPOINT_XFER_INT) { + tx_endpoint = ep; + display_ep_found = 1; + dev_dbg(dev, "%s: found display endpoint\n", __func__); + } + } + + /* + * Some iMON receivers have no display. Unfortunately, it seems + * that SoundGraph recycles device IDs between devices both with + * and without... :\ + */ + if (context->display == 0) { + display_ep_found = 0; + dev_dbg(dev, "%s: device has no display\n", __func__); + } + + /* Input endpoint is mandatory */ + if (!ir_ep_found) { + err("%s: no valid input (IR) endpoint found.", __func__); + retval = -ENODEV; + alloc_status = 2; + goto alloc_status_switch; + } + + /* Determine if display requires 6 packets */ + if (display_ep_found) { + if (usb_match_id(interface, vfd_proto_6p_list)) + vfd_proto_6p = 1; + + dev_dbg(dev, "%s: vfd_proto_6p: %d\n", + __func__, vfd_proto_6p); + } + + driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); + if (!driver) { + err("%s: kzalloc failed for lirc_driver", __func__); + alloc_status = 2; + goto alloc_status_switch; + } + rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); + if (!rbuf) { + err("%s: kmalloc failed for lirc_buffer", __func__); + alloc_status = 3; + goto alloc_status_switch; + } + if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) { + err("%s: lirc_buffer_init failed", __func__); + alloc_status = 4; + goto alloc_status_switch; + } + rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!rx_urb) { + err("%s: usb_alloc_urb failed for IR urb", __func__); + alloc_status = 5; + goto alloc_status_switch; + } + tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!tx_urb) { + err("%s: usb_alloc_urb failed for display urb", + __func__); + alloc_status = 6; + goto alloc_status_switch; + } + + mutex_init(&context->ctx_lock); + context->vfd_proto_6p = vfd_proto_6p; + + strcpy(driver->name, MOD_NAME); + driver->minor = -1; + driver->code_length = sizeof(int) * 8; + driver->sample_rate = 0; + driver->features = LIRC_CAN_REC_MODE2; + driver->data = context; + driver->rbuf = rbuf; + driver->set_use_inc = ir_open; + driver->set_use_dec = ir_close; + driver->dev = &interface->dev; + driver->owner = THIS_MODULE; + + mutex_lock(&context->ctx_lock); + + context->driver = driver; + /* start out in keyboard mode */ + + lirc_minor = lirc_register_driver(driver); + if (lirc_minor < 0) { + err("%s: lirc_register_driver failed", __func__); + alloc_status = 7; + goto alloc_status_switch; + } else + dev_info(dev, "Registered iMON driver " + "(lirc minor: %d)\n", lirc_minor); + + /* Needed while unregistering! */ + driver->minor = lirc_minor; + + context->usbdev = usbdev; + context->dev_present = 1; + context->rx_endpoint = rx_endpoint; + context->rx_urb = rx_urb; + + /* + * tx is used to send characters to lcd/vfd, associate RF + * remotes, set IR protocol, and maybe more... + */ + context->tx_endpoint = tx_endpoint; + context->tx_urb = tx_urb; + + if (display_ep_found) + context->display = 1; + + usb_fill_int_urb(context->rx_urb, context->usbdev, + usb_rcvintpipe(context->usbdev, + context->rx_endpoint->bEndpointAddress), + context->usb_rx_buf, sizeof(context->usb_rx_buf), + usb_rx_callback, context, + context->rx_endpoint->bInterval); + + retval = usb_submit_urb(context->rx_urb, GFP_KERNEL); + + if (retval) { + err("%s: usb_submit_urb failed for intf0 (%d)", + __func__, retval); + mutex_unlock(&context->ctx_lock); + goto exit; + } + + usb_set_intfdata(interface, context); + + if (context->display && ifnum == 0) { + dev_dbg(dev, "%s: Registering iMON display with sysfs\n", + __func__); + + if (usb_register_dev(interface, &imon_class)) { + /* Not a fatal error, so ignore */ + dev_info(dev, "%s: could not get a minor number for " + "display\n", __func__); + } + } + + dev_info(dev, "iMON device (%04x:%04x, intf%d) on " + "usb<%d:%d> initialized\n", vendor, product, ifnum, + usbdev->bus->busnum, usbdev->devnum); + +alloc_status_switch: + mutex_unlock(&context->ctx_lock); + + switch (alloc_status) { + case 7: + usb_free_urb(tx_urb); + case 6: + usb_free_urb(rx_urb); + case 5: + if (rbuf) + lirc_buffer_free(rbuf); + case 4: + kfree(rbuf); + case 3: + kfree(driver); + case 2: + kfree(context); + context = NULL; + case 1: + if (retval != -ENODEV) + retval = -ENOMEM; + break; + case 0: + retval = 0; + } + +exit: + mutex_unlock(&driver_lock); + + return retval; +} + +/** + * Callback function for USB core API: disconnect + */ +static void imon_disconnect(struct usb_interface *interface) +{ + struct imon_context *context; + int ifnum; + + /* prevent races with ir_open()/display_open() */ + mutex_lock(&driver_lock); + + context = usb_get_intfdata(interface); + ifnum = interface->cur_altsetting->desc.bInterfaceNumber; + + mutex_lock(&context->ctx_lock); + + usb_set_intfdata(interface, NULL); + + /* Abort ongoing write */ + if (atomic_read(&context->tx.busy)) { + usb_kill_urb(context->tx_urb); + complete_all(&context->tx.finished); + } + + context->dev_present = 0; + usb_kill_urb(context->rx_urb); + if (context->display) + usb_deregister_dev(interface, &imon_class); + + if (!context->ir_isopen && !context->dev_present) { + deregister_from_lirc(context); + mutex_unlock(&context->ctx_lock); + if (!context->display_isopen) + free_imon_context(context); + } else + mutex_unlock(&context->ctx_lock); + + mutex_unlock(&driver_lock); + + printk(KERN_INFO "%s: iMON device (intf%d) disconnected\n", + __func__, ifnum); +} + +static int imon_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct imon_context *context = usb_get_intfdata(intf); + + usb_kill_urb(context->rx_urb); + + return 0; +} + +static int imon_resume(struct usb_interface *intf) +{ + int rc = 0; + struct imon_context *context = usb_get_intfdata(intf); + + usb_fill_int_urb(context->rx_urb, context->usbdev, + usb_rcvintpipe(context->usbdev, + context->rx_endpoint->bEndpointAddress), + context->usb_rx_buf, sizeof(context->usb_rx_buf), + usb_rx_callback, context, + context->rx_endpoint->bInterval); + + rc = usb_submit_urb(context->rx_urb, GFP_ATOMIC); + + return rc; +} + +static int __init imon_init(void) +{ + int rc; + + printk(KERN_INFO MOD_NAME ": " MOD_DESC ", v" MOD_VERSION "\n"); + + rc = usb_register(&imon_driver); + if (rc) { + err("%s: usb register failed(%d)", __func__, rc); + return -ENODEV; + } + + return 0; +} + +static void __exit imon_exit(void) +{ + usb_deregister(&imon_driver); + printk(KERN_INFO MOD_NAME ": module removed. Goodbye!\n"); +} + +module_init(imon_init); +module_exit(imon_exit); diff --git a/drivers/staging/lirc/lirc_it87.c b/drivers/staging/lirc/lirc_it87.c new file mode 100644 index 0000000..09f3696 --- /dev/null +++ b/drivers/staging/lirc/lirc_it87.c @@ -0,0 +1,1019 @@ +/* + * LIRC driver for ITE IT8712/IT8705 CIR port + * + * Copyright (C) 2001 Hans-Gunter Lutke Uphues <hg_lu@web.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 + * + * ITE IT8705 and IT8712(not tested) and IT8720 CIR-port support for lirc based + * via cut and paste from lirc_sir.c (C) 2000 Milan Pikula + * + * Attention: Sendmode only tested with debugging logs + * + * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> : + * reimplemented read function + * 2005/06/05 Andrew Calkin implemented support for Asus Digimatrix, + * based on work of the following member of the Outertrack Digimatrix + * Forum: Art103 <r_tay@hotmail.com> + * 2009/12/24 James Edwards <jimbo-lirc@edwardsclan.net> implemeted support + * for ITE8704/ITE8718, on my machine, the DSDT reports 8704, but the + * chip identifies as 18. + */ + +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/fs.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/time.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <linux/mm.h> +#include <linux/delay.h> +#include <linux/poll.h> +#include <asm/system.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/fcntl.h> + +#include <linux/timer.h> +#include <linux/pnp.h> + +#include <media/lirc.h> +#include <media/lirc_dev.h> + +#include "lirc_it87.h" + +#ifdef LIRC_IT87_DIGIMATRIX +static int digimatrix = 1; +static int it87_freq = 36; /* kHz */ +static int irq = 9; +#else +static int digimatrix; +static int it87_freq = 38; /* kHz */ +static int irq = IT87_CIR_DEFAULT_IRQ; +#endif + +static unsigned long it87_bits_in_byte_out; +static unsigned long it87_send_counter; +static unsigned char it87_RXEN_mask = IT87_CIR_RCR_RXEN; + +#define RBUF_LEN 1024 + +#define LIRC_DRIVER_NAME "lirc_it87" + +/* timeout for sequences in jiffies (=5/100s) */ +/* must be longer than TIME_CONST */ +#define IT87_TIMEOUT (HZ*5/100) + +/* module parameters */ +static int debug; +#define dprintk(fmt, args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \ + fmt, ## args); \ + } while (0) + +static int io = IT87_CIR_DEFAULT_IOBASE; +/* receiver demodulator default: off */ +static int it87_enable_demodulator; + +static int timer_enabled; +static DEFINE_SPINLOCK(timer_lock); +static struct timer_list timerlist; +/* time of last signal change detected */ +static struct timeval last_tv = {0, 0}; +/* time of last UART data ready interrupt */ +static struct timeval last_intr_tv = {0, 0}; +static int last_value; + +static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue); + +static DEFINE_SPINLOCK(hardware_lock); +static DEFINE_SPINLOCK(dev_lock); + +static int rx_buf[RBUF_LEN]; +unsigned int rx_tail, rx_head; + +static struct pnp_driver it87_pnp_driver; + +/* SECTION: Prototypes */ + +/* Communication with user-space */ +static int lirc_open(struct inode *inode, struct file *file); +static int lirc_close(struct inode *inode, struct file *file); +static unsigned int lirc_poll(struct file *file, poll_table *wait); +static ssize_t lirc_read(struct file *file, char *buf, + size_t count, loff_t *ppos); +static ssize_t lirc_write(struct file *file, const char *buf, + size_t n, loff_t *pos); +static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); +static void add_read_queue(int flag, unsigned long val); +static int init_chrdev(void); +static void drop_chrdev(void); +/* Hardware */ +static irqreturn_t it87_interrupt(int irq, void *dev_id); +static void send_space(unsigned long len); +static void send_pulse(unsigned long len); +static void init_send(void); +static void terminate_send(unsigned long len); +static int init_hardware(void); +static void drop_hardware(void); +/* Initialisation */ +static int init_port(void); +static void drop_port(void); + + +/* SECTION: Communication with user-space */ + +static int lirc_open(struct inode *inode, struct file *file) +{ + spin_lock(&dev_lock); + if (module_refcount(THIS_MODULE)) { + spin_unlock(&dev_lock); + return -EBUSY; + } + spin_unlock(&dev_lock); + return 0; +} + + +static int lirc_close(struct inode *inode, struct file *file) +{ + return 0; +} + + +static unsigned int lirc_poll(struct file *file, poll_table *wait) +{ + poll_wait(file, &lirc_read_queue, wait); + if (rx_head != rx_tail) + return POLLIN | POLLRDNORM; + return 0; +} + + +static ssize_t lirc_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + int n = 0; + int retval = 0; + + while (n < count) { + if (file->f_flags & O_NONBLOCK && rx_head == rx_tail) { + retval = -EAGAIN; + break; + } + retval = wait_event_interruptible(lirc_read_queue, + rx_head != rx_tail); + if (retval) + break; + + if (copy_to_user((void *) buf + n, (void *) (rx_buf + rx_head), + sizeof(int))) { + retval = -EFAULT; + break; + } + rx_head = (rx_head + 1) & (RBUF_LEN - 1); + n += sizeof(int); + } + if (n) + return n; + return retval; +} + + +static ssize_t lirc_write(struct file *file, const char *buf, + size_t n, loff_t *pos) +{ + int i = 0; + int *tx_buf; + + if (n % sizeof(int)) + return -EINVAL; + tx_buf = memdup_user(buf, n); + if (IS_ERR(tx_buf)) + return PTR_ERR(tx_buf); + n /= sizeof(int); + init_send(); + while (1) { + if (i >= n) + break; + if (tx_buf[i]) + send_pulse(tx_buf[i]); + i++; + if (i >= n) + break; + if (tx_buf[i]) + send_space(tx_buf[i]); + i++; + } + terminate_send(tx_buf[i - 1]); + return n; +} + + +static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + int retval = 0; + unsigned long value = 0; + unsigned int ivalue; + unsigned long hw_flags; + + if (cmd == LIRC_GET_FEATURES) + value = LIRC_CAN_SEND_PULSE | + LIRC_CAN_SET_SEND_CARRIER | + LIRC_CAN_REC_MODE2; + else if (cmd == LIRC_GET_SEND_MODE) + value = LIRC_MODE_PULSE; + else if (cmd == LIRC_GET_REC_MODE) + value = LIRC_MODE_MODE2; + + switch (cmd) { + case LIRC_GET_FEATURES: + case LIRC_GET_SEND_MODE: + case LIRC_GET_REC_MODE: + retval = put_user(value, (unsigned long *) arg); + break; + + case LIRC_SET_SEND_MODE: + case LIRC_SET_REC_MODE: + retval = get_user(value, (unsigned long *) arg); + break; + + case LIRC_SET_SEND_CARRIER: + retval = get_user(ivalue, (unsigned int *) arg); + if (retval) + return retval; + ivalue /= 1000; + if (ivalue > IT87_CIR_FREQ_MAX || + ivalue < IT87_CIR_FREQ_MIN) + return -EINVAL; + + it87_freq = ivalue; + + spin_lock_irqsave(&hardware_lock, hw_flags); + outb(((inb(io + IT87_CIR_TCR2) & IT87_CIR_TCR2_TXMPW) | + (it87_freq - IT87_CIR_FREQ_MIN) << 3), + io + IT87_CIR_TCR2); + spin_unlock_irqrestore(&hardware_lock, hw_flags); + dprintk("demodulation frequency: %d kHz\n", it87_freq); + + break; + + default: + retval = -EINVAL; + } + + if (retval) + return retval; + + if (cmd == LIRC_SET_REC_MODE) { + if (value != LIRC_MODE_MODE2) + retval = -ENOSYS; + } else if (cmd == LIRC_SET_SEND_MODE) { + if (value != LIRC_MODE_PULSE) + retval = -ENOSYS; + } + return retval; +} + +static void add_read_queue(int flag, unsigned long val) +{ + unsigned int new_rx_tail; + int newval; + + dprintk("add flag %d with val %lu\n", flag, val); + + newval = val & PULSE_MASK; + + /* + * statistically, pulses are ~TIME_CONST/2 too long. we could + * maybe make this more exact, but this is good enough + */ + if (flag) { + /* pulse */ + if (newval > TIME_CONST / 2) + newval -= TIME_CONST / 2; + else /* should not ever happen */ + newval = 1; + newval |= PULSE_BIT; + } else + newval += TIME_CONST / 2; + new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1); + if (new_rx_tail == rx_head) { + dprintk("Buffer overrun.\n"); + return; + } + rx_buf[rx_tail] = newval; + rx_tail = new_rx_tail; + wake_up_interruptible(&lirc_read_queue); +} + + +static const struct file_operations lirc_fops = { + .owner = THIS_MODULE, + .read = lirc_read, + .write = lirc_write, + .poll = lirc_poll, + .unlocked_ioctl = lirc_ioctl, + .open = lirc_open, + .release = lirc_close, +}; + +static int set_use_inc(void *data) +{ + return 0; +} + +static void set_use_dec(void *data) +{ +} + +static struct lirc_driver driver = { + .name = LIRC_DRIVER_NAME, + .minor = -1, + .code_length = 1, + .sample_rate = 0, + .data = NULL, + .add_to_buf = NULL, + .set_use_inc = set_use_inc, + .set_use_dec = set_use_dec, + .fops = &lirc_fops, + .dev = NULL, + .owner = THIS_MODULE, +}; + + +#ifdef MODULE +static int init_chrdev(void) +{ + driver.minor = lirc_register_driver(&driver); + + if (driver.minor < 0) { + printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n"); + return -EIO; + } + return 0; +} + + +static void drop_chrdev(void) +{ + lirc_unregister_driver(driver.minor); +} +#endif + + +/* SECTION: Hardware */ +static long delta(struct timeval *tv1, struct timeval *tv2) +{ + unsigned long deltv; + + deltv = tv2->tv_sec - tv1->tv_sec; + if (deltv > 15) + deltv = 0xFFFFFF; + else + deltv = deltv*1000000 + tv2->tv_usec - tv1->tv_usec; + return deltv; +} + +static void it87_timeout(unsigned long data) +{ + unsigned long flags; + + /* avoid interference with interrupt */ + spin_lock_irqsave(&timer_lock, flags); + + if (digimatrix) { + /* We have timed out. Disable the RX mechanism. */ + + outb((inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN) | + IT87_CIR_RCR_RXACT, io + IT87_CIR_RCR); + if (it87_RXEN_mask) + outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN, + io + IT87_CIR_RCR); + dprintk(" TIMEOUT\n"); + timer_enabled = 0; + + /* fifo clear */ + outb(inb(io + IT87_CIR_TCR1) | IT87_CIR_TCR1_FIFOCLR, + io+IT87_CIR_TCR1); + + } else { + /* + * if last received signal was a pulse, but receiving stopped + * within the 9 bit frame, we need to finish this pulse and + * simulate a signal change to from pulse to space. Otherwise + * upper layers will receive two sequences next time. + */ + + if (last_value) { + unsigned long pulse_end; + + /* determine 'virtual' pulse end: */ + pulse_end = delta(&last_tv, &last_intr_tv); + dprintk("timeout add %d for %lu usec\n", + last_value, pulse_end); + add_read_queue(last_value, pulse_end); + last_value = 0; + last_tv = last_intr_tv; + } + } + spin_unlock_irqrestore(&timer_lock, flags); +} + +static irqreturn_t it87_interrupt(int irq, void *dev_id) +{ + unsigned char data; + struct timeval curr_tv; + static unsigned long deltv; + unsigned long deltintrtv; + unsigned long flags, hw_flags; + int iir, lsr; + int fifo = 0; + static char lastbit; + char bit; + + /* Bit duration in microseconds */ + const unsigned long bit_duration = 1000000ul / + (115200 / IT87_CIR_BAUDRATE_DIVISOR); + + + iir = inb(io + IT87_CIR_IIR); + + switch (iir & IT87_CIR_IIR_IID) { + case 0x4: + case 0x6: + lsr = inb(io + IT87_CIR_RSR) & (IT87_CIR_RSR_RXFTO | + IT87_CIR_RSR_RXFBC); + fifo = lsr & IT87_CIR_RSR_RXFBC; + dprintk("iir: 0x%x fifo: 0x%x\n", iir, lsr); + + /* avoid interference with timer */ + spin_lock_irqsave(&timer_lock, flags); + spin_lock_irqsave(&hardware_lock, hw_flags); + if (digimatrix) { + static unsigned long acc_pulse; + static unsigned long acc_space; + + do { + data = inb(io + IT87_CIR_DR); + data = ~data; + fifo--; + if (data != 0x00) { + if (timer_enabled) + del_timer(&timerlist); + /* + * start timer for end of + * sequence detection + */ + timerlist.expires = jiffies + + IT87_TIMEOUT; + add_timer(&timerlist); + timer_enabled = 1; + } + /* Loop through */ + for (bit = 0; bit < 8; ++bit) { + if ((data >> bit) & 1) { + ++acc_pulse; + if (lastbit == 0) { + add_read_queue(0, + acc_space * + bit_duration); + acc_space = 0; + } + } else { + ++acc_space; + if (lastbit == 1) { + add_read_queue(1, + acc_pulse * + bit_duration); + acc_pulse = 0; + } + } + lastbit = (data >> bit) & 1; + } + + } while (fifo != 0); + } else { /* Normal Operation */ + do { + del_timer(&timerlist); + data = inb(io + IT87_CIR_DR); + + dprintk("data=%02x\n", data); + do_gettimeofday(&curr_tv); + deltv = delta(&last_tv, &curr_tv); + deltintrtv = delta(&last_intr_tv, &curr_tv); + + dprintk("t %lu , d %d\n", + deltintrtv, (int)data); + + /* + * if nothing came in last 2 cycles, + * it was gap + */ + if (deltintrtv > TIME_CONST * 2) { + if (last_value) { + dprintk("GAP\n"); + + /* simulate signal change */ + add_read_queue(last_value, + deltv - + deltintrtv); + last_value = 0; + last_tv.tv_sec = + last_intr_tv.tv_sec; + last_tv.tv_usec = + last_intr_tv.tv_usec; + deltv = deltintrtv; + } + } + data = 1; + if (data ^ last_value) { + /* + * deltintrtv > 2*TIME_CONST, + * remember ? the other case is + * timeout + */ + add_read_queue(last_value, + deltv-TIME_CONST); + last_value = data; + last_tv = curr_tv; + if (last_tv.tv_usec >= TIME_CONST) + last_tv.tv_usec -= TIME_CONST; + else { + last_tv.tv_sec--; + last_tv.tv_usec += 1000000 - + TIME_CONST; + } + } + last_intr_tv = curr_tv; + if (data) { + /* + * start timer for end of + * sequence detection + */ + timerlist.expires = + jiffies + IT87_TIMEOUT; + add_timer(&timerlist); + } + outb((inb(io + IT87_CIR_RCR) & + ~IT87_CIR_RCR_RXEN) | + IT87_CIR_RCR_RXACT, + io + IT87_CIR_RCR); + if (it87_RXEN_mask) + outb(inb(io + IT87_CIR_RCR) | + IT87_CIR_RCR_RXEN, + io + IT87_CIR_RCR); + fifo--; + } while (fifo != 0); + } + spin_unlock_irqrestore(&hardware_lock, hw_flags); + spin_unlock_irqrestore(&timer_lock, flags); + + return IRQ_RETVAL(IRQ_HANDLED); + + default: + /* not our irq */ + dprintk("unknown IRQ (shouldn't happen) !!\n"); + return IRQ_RETVAL(IRQ_NONE); + } +} + + +static void send_it87(unsigned long len, unsigned long stime, + unsigned char send_byte, unsigned int count_bits) +{ + long count = len / stime; + long time_left = 0; + static unsigned char byte_out; + unsigned long hw_flags; + + dprintk("%s: len=%ld, sb=%d\n", __func__, len, send_byte); + + time_left = (long)len - (long)count * (long)stime; + count += ((2 * time_left) / stime); + while (count) { + long i = 0; + for (i = 0; i < count_bits; i++) { + byte_out = (byte_out << 1) | (send_byte & 1); + it87_bits_in_byte_out++; + } + if (it87_bits_in_byte_out == 8) { + dprintk("out=0x%x, tsr_txfbc: 0x%x\n", + byte_out, + inb(io + IT87_CIR_TSR) & + IT87_CIR_TSR_TXFBC); + + while ((inb(io + IT87_CIR_TSR) & + IT87_CIR_TSR_TXFBC) >= IT87_CIR_FIFO_SIZE) + ; + + spin_lock_irqsave(&hardware_lock, hw_flags); + outb(byte_out, io + IT87_CIR_DR); + spin_unlock_irqrestore(&hardware_lock, hw_flags); + + it87_bits_in_byte_out = 0; + it87_send_counter++; + byte_out = 0; + } + count--; + } +} + + +/*TODO: maybe exchange space and pulse because it8705 only modulates 0-bits */ + +static void send_space(unsigned long len) +{ + send_it87(len, TIME_CONST, IT87_CIR_SPACE, IT87_CIR_BAUDRATE_DIVISOR); +} + +static void send_pulse(unsigned long len) +{ + send_it87(len, TIME_CONST, IT87_CIR_PULSE, IT87_CIR_BAUDRATE_DIVISOR); +} + + +static void init_send() +{ + unsigned long flags; + + spin_lock_irqsave(&hardware_lock, flags); + /* RXEN=0: receiver disable */ + it87_RXEN_mask = 0; + outb(inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN, + io + IT87_CIR_RCR); + spin_unlock_irqrestore(&hardware_lock, flags); + it87_bits_in_byte_out = 0; + it87_send_counter = 0; +} + + +static void terminate_send(unsigned long len) +{ + unsigned long flags; + unsigned long last = 0; + + last = it87_send_counter; + /* make sure all necessary data has been sent */ + while (last == it87_send_counter) + send_space(len); + /* wait until all data sent */ + while ((inb(io + IT87_CIR_TSR) & IT87_CIR_TSR_TXFBC) != 0) + ; + /* then re-enable receiver */ + spin_lock_irqsave(&hardware_lock, flags); + it87_RXEN_mask = IT87_CIR_RCR_RXEN; + outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN, + io + IT87_CIR_RCR); + spin_unlock_irqrestore(&hardware_lock, flags); +} + + +static int init_hardware(void) +{ + unsigned long flags; + unsigned char it87_rcr = 0; + + spin_lock_irqsave(&hardware_lock, flags); + /* init cir-port */ + /* enable r/w-access to Baudrate-Register */ + outb(IT87_CIR_IER_BR, io + IT87_CIR_IER); + outb(IT87_CIR_BAUDRATE_DIVISOR % 0x100, io+IT87_CIR_BDLR); + outb(IT87_CIR_BAUDRATE_DIVISOR / 0x100, io+IT87_CIR_BDHR); + /* Baudrate Register off, define IRQs: Input only */ + if (digimatrix) { + outb(IT87_CIR_IER_IEC | IT87_CIR_IER_RFOIE, io + IT87_CIR_IER); + /* RX: HCFS=0, RXDCR = 001b (33,75..38,25 kHz), RXEN=1 */ + } else { + outb(IT87_CIR_IER_IEC | IT87_CIR_IER_RDAIE, io + IT87_CIR_IER); + /* RX: HCFS=0, RXDCR = 001b (35,6..40,3 kHz), RXEN=1 */ + } + it87_rcr = (IT87_CIR_RCR_RXEN & it87_RXEN_mask) | 0x1; + if (it87_enable_demodulator) + it87_rcr |= IT87_CIR_RCR_RXEND; + outb(it87_rcr, io + IT87_CIR_RCR); + if (digimatrix) { + /* Set FIFO depth to 1 byte, and disable TX */ + outb(inb(io + IT87_CIR_TCR1) | 0x00, + io + IT87_CIR_TCR1); + + /* + * TX: it87_freq (36kHz), 'reserved' sensitivity + * setting (0x00) + */ + outb(((it87_freq - IT87_CIR_FREQ_MIN) << 3) | 0x00, + io + IT87_CIR_TCR2); + } else { + /* TX: 38kHz, 13,3us (pulse-width) */ + outb(((it87_freq - IT87_CIR_FREQ_MIN) << 3) | 0x06, + io + IT87_CIR_TCR2); + } + spin_unlock_irqrestore(&hardware_lock, flags); + return 0; +} + + +static void drop_hardware(void) +{ + unsigned long flags; + + spin_lock_irqsave(&hardware_lock, flags); + disable_irq(irq); + /* receiver disable */ + it87_RXEN_mask = 0; + outb(0x1, io + IT87_CIR_RCR); + /* turn off irqs */ + outb(0, io + IT87_CIR_IER); + /* fifo clear */ + outb(IT87_CIR_TCR1_FIFOCLR, io+IT87_CIR_TCR1); + /* reset */ + outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER); + enable_irq(irq); + spin_unlock_irqrestore(&hardware_lock, flags); +} + + +static unsigned char it87_read(unsigned char port) +{ + outb(port, IT87_ADRPORT); + return inb(IT87_DATAPORT); +} + + +static void it87_write(unsigned char port, unsigned char data) +{ + outb(port, IT87_ADRPORT); + outb(data, IT87_DATAPORT); +} + + +/* SECTION: Initialisation */ + +static int init_port(void) +{ + unsigned long hw_flags; + int retval = 0; + + unsigned char init_bytes[4] = IT87_INIT; + unsigned char it87_chipid = 0; + unsigned char ldn = 0; + unsigned int it87_io = 0; + unsigned int it87_irq = 0; + + /* Enter MB PnP Mode */ + outb(init_bytes[0], IT87_ADRPORT); + outb(init_bytes[1], IT87_ADRPORT); + outb(init_bytes[2], IT87_ADRPORT); + outb(init_bytes[3], IT87_ADRPORT); + + /* 8712 or 8705 ? */ + it87_chipid = it87_read(IT87_CHIP_ID1); + if (it87_chipid != 0x87) { + retval = -ENXIO; + return retval; + } + it87_chipid = it87_read(IT87_CHIP_ID2); + if ((it87_chipid != 0x05) && + (it87_chipid != 0x12) && + (it87_chipid != 0x18) && + (it87_chipid != 0x20)) { + printk(KERN_INFO LIRC_DRIVER_NAME + ": no IT8704/05/12/18/20 found (claimed IT87%02x), " + "exiting..\n", it87_chipid); + retval = -ENXIO; + return retval; + } + printk(KERN_INFO LIRC_DRIVER_NAME + ": found IT87%02x.\n", + it87_chipid); + + /* get I/O-Port and IRQ */ + if (it87_chipid == 0x12 || it87_chipid == 0x18) + ldn = IT8712_CIR_LDN; + else + ldn = IT8705_CIR_LDN; + it87_write(IT87_LDN, ldn); + + it87_io = it87_read(IT87_CIR_BASE_MSB) * 256 + + it87_read(IT87_CIR_BASE_LSB); + if (it87_io == 0) { + if (io == 0) + io = IT87_CIR_DEFAULT_IOBASE; + printk(KERN_INFO LIRC_DRIVER_NAME + ": set default io 0x%x\n", + io); + it87_write(IT87_CIR_BASE_MSB, io / 0x100); + it87_write(IT87_CIR_BASE_LSB, io % 0x100); + } else + io = it87_io; + + it87_irq = it87_read(IT87_CIR_IRQ); + if (digimatrix || it87_irq == 0) { + if (irq == 0) + irq = IT87_CIR_DEFAULT_IRQ; + printk(KERN_INFO LIRC_DRIVER_NAME + ": set default irq 0x%x\n", + irq); + it87_write(IT87_CIR_IRQ, irq); + } else + irq = it87_irq; + + spin_lock_irqsave(&hardware_lock, hw_flags); + /* reset */ + outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER); + /* fifo clear */ + outb(IT87_CIR_TCR1_FIFOCLR | + /* IT87_CIR_TCR1_ILE | */ + IT87_CIR_TCR1_TXRLE | + IT87_CIR_TCR1_TXENDF, io+IT87_CIR_TCR1); + spin_unlock_irqrestore(&hardware_lock, hw_flags); + + /* get I/O port access and IRQ line */ + if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) { + printk(KERN_ERR LIRC_DRIVER_NAME + ": i/o port 0x%.4x already in use.\n", io); + /* Leaving MB PnP Mode */ + it87_write(IT87_CFGCTRL, 0x2); + return -EBUSY; + } + + /* activate CIR-Device */ + it87_write(IT87_CIR_ACT, 0x1); + + /* Leaving MB PnP Mode */ + it87_write(IT87_CFGCTRL, 0x2); + + retval = request_irq(irq, it87_interrupt, 0 /*IRQF_DISABLED*/, + LIRC_DRIVER_NAME, NULL); + if (retval < 0) { + printk(KERN_ERR LIRC_DRIVER_NAME + ": IRQ %d already in use.\n", + irq); + release_region(io, 8); + return retval; + } + + printk(KERN_INFO LIRC_DRIVER_NAME + ": I/O port 0x%.4x, IRQ %d.\n", io, irq); + + init_timer(&timerlist); + timerlist.function = it87_timeout; + timerlist.data = 0xabadcafe; + + return 0; +} + + +static void drop_port(void) +{ +#if 0 + unsigned char init_bytes[4] = IT87_INIT; + + /* Enter MB PnP Mode */ + outb(init_bytes[0], IT87_ADRPORT); + outb(init_bytes[1], IT87_ADRPORT); + outb(init_bytes[2], IT87_ADRPORT); + outb(init_bytes[3], IT87_ADRPORT); + + /* deactivate CIR-Device */ + it87_write(IT87_CIR_ACT, 0x0); + + /* Leaving MB PnP Mode */ + it87_write(IT87_CFGCTRL, 0x2); +#endif + + del_timer_sync(&timerlist); + free_irq(irq, NULL); + release_region(io, 8); +} + + +static int init_lirc_it87(void) +{ + int retval; + + init_waitqueue_head(&lirc_read_queue); + retval = init_port(); + if (retval < 0) + return retval; + init_hardware(); + printk(KERN_INFO LIRC_DRIVER_NAME ": Installed.\n"); + return 0; +} + +static int it87_probe(struct pnp_dev *pnp_dev, + const struct pnp_device_id *dev_id) +{ + int retval; + + driver.dev = &pnp_dev->dev; + + retval = init_chrdev(); + if (retval < 0) + return retval; + + retval = init_lirc_it87(); + if (retval) + goto init_lirc_it87_failed; + + return 0; + +init_lirc_it87_failed: + drop_chrdev(); + + return retval; +} + +static int __init lirc_it87_init(void) +{ + return pnp_register_driver(&it87_pnp_driver); +} + + +static void __exit lirc_it87_exit(void) +{ + drop_hardware(); + drop_chrdev(); + drop_port(); + pnp_unregister_driver(&it87_pnp_driver); + printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n"); +} + +/* SECTION: PNP for ITE8704/18 */ + +static const struct pnp_device_id pnp_dev_table[] = { + {"ITE8704", 0}, + {} +}; + +MODULE_DEVICE_TABLE(pnp, pnp_dev_table); + +static struct pnp_driver it87_pnp_driver = { + .name = LIRC_DRIVER_NAME, + .id_table = pnp_dev_table, + .probe = it87_probe, +}; + +module_init(lirc_it87_init); +module_exit(lirc_it87_exit); + +MODULE_DESCRIPTION("LIRC driver for ITE IT8704/05/12/18/20 CIR port"); +MODULE_AUTHOR("Hans-Gunter Lutke Uphues"); +MODULE_LICENSE("GPL"); + +module_param(io, int, S_IRUGO); +MODULE_PARM_DESC(io, "I/O base address (default: 0x310)"); + +module_param(irq, int, S_IRUGO); +#ifdef LIRC_IT87_DIGIMATRIX +MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 9)"); +#else +MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 7)"); +#endif + +module_param(it87_enable_demodulator, bool, S_IRUGO); +MODULE_PARM_DESC(it87_enable_demodulator, + "Receiver demodulator enable/disable (1/0), default: 0"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Enable debugging messages"); + +module_param(digimatrix, bool, S_IRUGO | S_IWUSR); +#ifdef LIRC_IT87_DIGIMATRIX +MODULE_PARM_DESC(digimatrix, + "Asus Digimatrix it87 compat. enable/disable (1/0), default: 1"); +#else +MODULE_PARM_DESC(digimatrix, + "Asus Digimatrix it87 compat. enable/disable (1/0), default: 0"); +#endif + + +module_param(it87_freq, int, S_IRUGO); +#ifdef LIRC_IT87_DIGIMATRIX +MODULE_PARM_DESC(it87_freq, + "Carrier demodulator frequency (kHz), (default: 36)"); +#else +MODULE_PARM_DESC(it87_freq, + "Carrier demodulator frequency (kHz), (default: 38)"); +#endif diff --git a/drivers/staging/lirc/lirc_it87.h b/drivers/staging/lirc/lirc_it87.h new file mode 100644 index 0000000..cf021c8 --- /dev/null +++ b/drivers/staging/lirc/lirc_it87.h @@ -0,0 +1,116 @@ +/* lirc_it87.h */ +/* SECTION: Definitions */ + +/********************************* ITE IT87xx ************************/ + +/* based on the following documentation from ITE: + a) IT8712F Preliminary CIR Programming Guide V0.1 + b) IT8705F Simple LPC I/O Preliminary Specification V0.3 + c) IT8712F EC-LPC I/O Preliminary Specification V0.5 +*/ + +/* IT8712/05 Ports: */ +#define IT87_ADRPORT 0x2e +#define IT87_DATAPORT 0x2f +#define IT87_INIT {0x87, 0x01, 0x55, 0x55} + +/* alternate Ports: */ +/* +#define IT87_ADRPORT 0x4e +#define IT87_DATAPORT 0x4f +#define IT87_INIT {0x87, 0x01, 0x55, 0xaa} + */ + +/* IT8712/05 Registers */ +#define IT87_CFGCTRL 0x2 +#define IT87_LDN 0x7 +#define IT87_CHIP_ID1 0x20 +#define IT87_CHIP_ID2 0x21 +#define IT87_CFG_VERSION 0x22 +#define IT87_SWSUSPEND 0x23 + +#define IT8712_CIR_LDN 0xa +#define IT8705_CIR_LDN 0x7 + +/* CIR Configuration Registers: */ +#define IT87_CIR_ACT 0x30 +#define IT87_CIR_BASE_MSB 0x60 +#define IT87_CIR_BASE_LSB 0x61 +#define IT87_CIR_IRQ 0x70 +#define IT87_CIR_CONFIG 0xf0 + +/* List of IT87_CIR registers: offset to BaseAddr */ +#define IT87_CIR_DR 0 +#define IT87_CIR_IER 1 +#define IT87_CIR_RCR 2 +#define IT87_CIR_TCR1 3 +#define IT87_CIR_TCR2 4 +#define IT87_CIR_TSR 5 +#define IT87_CIR_RSR 6 +#define IT87_CIR_BDLR 5 +#define IT87_CIR_BDHR 6 +#define IT87_CIR_IIR 7 + +/* Bit Definition */ +/* IER: */ +#define IT87_CIR_IER_TM_EN 0x80 +#define IT87_CIR_IER_RESEVED 0x40 +#define IT87_CIR_IER_RESET 0x20 +#define IT87_CIR_IER_BR 0x10 +#define IT87_CIR_IER_IEC 0x8 +#define IT87_CIR_IER_RFOIE 0x4 +#define IT87_CIR_IER_RDAIE 0x2 +#define IT87_CIR_IER_TLDLIE 0x1 + +/* RCR: */ +#define IT87_CIR_RCR_RDWOS 0x80 +#define IT87_CIR_RCR_HCFS 0x40 +#define IT87_CIR_RCR_RXEN 0x20 +#define IT87_CIR_RCR_RXEND 0x10 +#define IT87_CIR_RCR_RXACT 0x8 +#define IT87_CIR_RCR_RXDCR 0x7 + +/* TCR1: */ +#define IT87_CIR_TCR1_FIFOCLR 0x80 +#define IT87_CIR_TCR1_ILE 0x40 +#define IT87_CIR_TCR1_FIFOTL 0x30 +#define IT87_CIR_TCR1_TXRLE 0x8 +#define IT87_CIR_TCR1_TXENDF 0x4 +#define IT87_CIR_TCR1_TXMPM 0x3 + +/* TCR2: */ +#define IT87_CIR_TCR2_CFQ 0xf8 +#define IT87_CIR_TCR2_TXMPW 0x7 + +/* TSR: */ +#define IT87_CIR_TSR_RESERVED 0xc0 +#define IT87_CIR_TSR_TXFBC 0x3f + +/* RSR: */ +#define IT87_CIR_RSR_RXFTO 0x80 +#define IT87_CIR_RSR_RESERVED 0x40 +#define IT87_CIR_RSR_RXFBC 0x3f + +/* IIR: */ +#define IT87_CIR_IIR_RESERVED 0xf8 +#define IT87_CIR_IIR_IID 0x6 +#define IT87_CIR_IIR_IIP 0x1 + +/* TM: */ +#define IT87_CIR_TM_IL_SEL 0x80 +#define IT87_CIR_TM_RESERVED 0x40 +#define IT87_CIR_TM_TM_REG 0x3f + +#define IT87_CIR_FIFO_SIZE 32 + +/* Baudratedivisor for IT87: power of 2: only 1,2,4 or 8) */ +#define IT87_CIR_BAUDRATE_DIVISOR 0x1 +#define IT87_CIR_DEFAULT_IOBASE 0x310 +#define IT87_CIR_DEFAULT_IRQ 0x7 +#define IT87_CIR_SPACE 0x00 +#define IT87_CIR_PULSE 0xff +#define IT87_CIR_FREQ_MIN 27 +#define IT87_CIR_FREQ_MAX 58 +#define TIME_CONST (IT87_CIR_BAUDRATE_DIVISOR * 8000000ul / 115200ul) + +/********************************* ITE IT87xx ************************/ diff --git a/drivers/staging/lirc/lirc_ite8709.c b/drivers/staging/lirc/lirc_ite8709.c new file mode 100644 index 0000000..9352f45 --- /dev/null +++ b/drivers/staging/lirc/lirc_ite8709.c @@ -0,0 +1,542 @@ +/* + * LIRC driver for ITE8709 CIR port + * + * Copyright (C) 2008 Grégory Lardière <spmf2004-lirc@yahoo.fr> + * + * 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/interrupt.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/pnp.h> +#include <linux/io.h> + +#include <media/lirc.h> +#include <media/lirc_dev.h> + +#define LIRC_DRIVER_NAME "lirc_ite8709" + +#define BUF_CHUNK_SIZE sizeof(int) +#define BUF_SIZE (128*BUF_CHUNK_SIZE) + +/* + * The ITE8709 device seems to be the combination of IT8512 superIO chip and + * a specific firmware running on the IT8512's embedded micro-controller. + * In addition of the embedded micro-controller, the IT8512 chip contains a + * CIR module and several other modules. A few modules are directly accessible + * by the host CPU, but most of them are only accessible by the + * micro-controller. The CIR module is only accessible by the micro-controller. + * The battery-backed SRAM module is accessible by the host CPU and the + * micro-controller. So one of the MC's firmware role is to act as a bridge + * between the host CPU and the CIR module. The firmware implements a kind of + * communication protocol using the SRAM module as a shared memory. The IT8512 + * specification is publicly available on ITE's web site, but the communication + * protocol is not, so it was reverse-engineered. + */ + +/* ITE8709 Registers addresses and values (reverse-engineered) */ +#define ITE8709_MODE 0x1a +#define ITE8709_REG_ADR 0x1b +#define ITE8709_REG_VAL 0x1c +#define ITE8709_IIR 0x1e /* Interrupt identification register */ +#define ITE8709_RFSR 0x1f /* Receiver FIFO status register */ +#define ITE8709_FIFO_START 0x20 + +#define ITE8709_MODE_READY 0X00 +#define ITE8709_MODE_WRITE 0X01 +#define ITE8709_MODE_READ 0X02 +#define ITE8709_IIR_RDAI 0x02 /* Receiver data available interrupt */ +#define ITE8709_IIR_RFOI 0x04 /* Receiver FIFO overrun interrupt */ +#define ITE8709_RFSR_MASK 0x3f /* FIFO byte count mask */ + +/* + * IT8512 CIR-module registers addresses and values + * (from IT8512 E/F specification v0.4.1) + */ +#define IT8512_REG_MSTCR 0x01 /* Master control register */ +#define IT8512_REG_IER 0x02 /* Interrupt enable register */ +#define IT8512_REG_CFR 0x04 /* Carrier frequency register */ +#define IT8512_REG_RCR 0x05 /* Receive control register */ +#define IT8512_REG_BDLR 0x08 /* Baud rate divisor low byte register */ +#define IT8512_REG_BDHR 0x09 /* Baud rate divisor high byte register */ + +#define IT8512_MSTCR_RESET 0x01 /* Reset registers to default value */ +#define IT8512_MSTCR_FIFOCLR 0x02 /* Clear FIFO */ +#define IT8512_MSTCR_FIFOTL_7 0x04 /* FIFO threshold level : 7 */ +#define IT8512_MSTCR_FIFOTL_25 0x0c /* FIFO threshold level : 25 */ +#define IT8512_IER_RDAIE 0x02 /* Enable data interrupt request */ +#define IT8512_IER_RFOIE 0x04 /* Enable FIFO overrun interrupt req */ +#define IT8512_IER_IEC 0x80 /* Enable interrupt request */ +#define IT8512_CFR_CF_36KHZ 0x09 /* Carrier freq : low speed, 36kHz */ +#define IT8512_RCR_RXDCR_1 0x01 /* Demodulation carrier range : 1 */ +#define IT8512_RCR_RXACT 0x08 /* Receiver active */ +#define IT8512_RCR_RXEN 0x80 /* Receiver enable */ +#define IT8512_BDR_6 6 /* Baud rate divisor : 6 */ + +/* Actual values used by this driver */ +#define CFG_FIFOTL IT8512_MSTCR_FIFOTL_25 +#define CFG_CR_FREQ IT8512_CFR_CF_36KHZ +#define CFG_DCR IT8512_RCR_RXDCR_1 +#define CFG_BDR IT8512_BDR_6 +#define CFG_TIMEOUT 100000 /* Rearm interrupt when a space is > 100 ms */ + +static int debug; + +struct ite8709_device { + int use_count; + int io; + int irq; + spinlock_t hardware_lock; + unsigned long long acc_pulse; + unsigned long long acc_space; + char lastbit; + struct timeval last_tv; + struct lirc_driver driver; + struct tasklet_struct tasklet; + char force_rearm; + char rearmed; + char device_busy; +}; + +#define dprintk(fmt, args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \ + fmt, ## args); \ + } while (0) + + +static unsigned char ite8709_read(struct ite8709_device *dev, + unsigned char port) +{ + outb(port, dev->io); + return inb(dev->io+1); +} + +static void ite8709_write(struct ite8709_device *dev, unsigned char port, + unsigned char data) +{ + outb(port, dev->io); + outb(data, dev->io+1); +} + +static void ite8709_wait_device(struct ite8709_device *dev) +{ + int i = 0; + /* + * loop until device tells it's ready to continue + * iterations count is usually ~750 but can sometimes achieve 13000 + */ + for (i = 0; i < 15000; i++) { + udelay(2); + if (ite8709_read(dev, ITE8709_MODE) == ITE8709_MODE_READY) + break; + } +} + +static void ite8709_write_register(struct ite8709_device *dev, + unsigned char reg_adr, unsigned char reg_value) +{ + ite8709_wait_device(dev); + + ite8709_write(dev, ITE8709_REG_VAL, reg_value); + ite8709_write(dev, ITE8709_REG_ADR, reg_adr); + ite8709_write(dev, ITE8709_MODE, ITE8709_MODE_WRITE); +} + +static void ite8709_init_hardware(struct ite8709_device *dev) +{ + spin_lock_irq(&dev->hardware_lock); + dev->device_busy = 1; + spin_unlock_irq(&dev->hardware_lock); + + ite8709_write_register(dev, IT8512_REG_BDHR, (CFG_BDR >> 8) & 0xff); + ite8709_write_register(dev, IT8512_REG_BDLR, CFG_BDR & 0xff); + ite8709_write_register(dev, IT8512_REG_CFR, CFG_CR_FREQ); + ite8709_write_register(dev, IT8512_REG_IER, + IT8512_IER_IEC | IT8512_IER_RFOIE | IT8512_IER_RDAIE); + ite8709_write_register(dev, IT8512_REG_RCR, CFG_DCR); + ite8709_write_register(dev, IT8512_REG_MSTCR, + CFG_FIFOTL | IT8512_MSTCR_FIFOCLR); + ite8709_write_register(dev, IT8512_REG_RCR, + IT8512_RCR_RXEN | IT8512_RCR_RXACT | CFG_DCR); + + spin_lock_irq(&dev->hardware_lock); + dev->device_busy = 0; + spin_unlock_irq(&dev->hardware_lock); + + tasklet_enable(&dev->tasklet); +} + +static void ite8709_drop_hardware(struct ite8709_device *dev) +{ + tasklet_disable(&dev->tasklet); + + spin_lock_irq(&dev->hardware_lock); + dev->device_busy = 1; + spin_unlock_irq(&dev->hardware_lock); + + ite8709_write_register(dev, IT8512_REG_RCR, 0); + ite8709_write_register(dev, IT8512_REG_MSTCR, + IT8512_MSTCR_RESET | IT8512_MSTCR_FIFOCLR); + + spin_lock_irq(&dev->hardware_lock); + dev->device_busy = 0; + spin_unlock_irq(&dev->hardware_lock); +} + +static int ite8709_set_use_inc(void *data) +{ + struct ite8709_device *dev; + dev = data; + if (dev->use_count == 0) + ite8709_init_hardware(dev); + dev->use_count++; + return 0; +} + +static void ite8709_set_use_dec(void *data) +{ + struct ite8709_device *dev; + dev = data; + dev->use_count--; + if (dev->use_count == 0) + ite8709_drop_hardware(dev); +} + +static void ite8709_add_read_queue(struct ite8709_device *dev, int flag, + unsigned long long val) +{ + int value; + + dprintk("add a %llu usec %s\n", val, flag ? "pulse" : "space"); + + value = (val > PULSE_MASK) ? PULSE_MASK : val; + if (flag) + value |= PULSE_BIT; + + if (!lirc_buffer_full(dev->driver.rbuf)) { + lirc_buffer_write(dev->driver.rbuf, (void *) &value); + wake_up(&dev->driver.rbuf->wait_poll); + } +} + +static irqreturn_t ite8709_interrupt(int irq, void *dev_id) +{ + unsigned char data; + int iir, rfsr, i; + int fifo = 0; + char bit; + struct timeval curr_tv; + + /* Bit duration in microseconds */ + const unsigned long bit_duration = 1000000ul / (115200 / CFG_BDR); + + struct ite8709_device *dev; + dev = dev_id; + + /* + * If device is busy, we simply discard data because we are in one of + * these two cases : shutting down or rearming the device, so this + * doesn't really matter and this avoids waiting too long in IRQ ctx + */ + spin_lock(&dev->hardware_lock); + if (dev->device_busy) { + spin_unlock(&dev->hardware_lock); + return IRQ_RETVAL(IRQ_HANDLED); + } + + iir = ite8709_read(dev, ITE8709_IIR); + + switch (iir) { + case ITE8709_IIR_RFOI: + dprintk("fifo overrun, scheduling forced rearm just in case\n"); + dev->force_rearm = 1; + tasklet_schedule(&dev->tasklet); + spin_unlock(&dev->hardware_lock); + return IRQ_RETVAL(IRQ_HANDLED); + + case ITE8709_IIR_RDAI: + rfsr = ite8709_read(dev, ITE8709_RFSR); + fifo = rfsr & ITE8709_RFSR_MASK; + if (fifo > 32) + fifo = 32; + dprintk("iir: 0x%x rfsr: 0x%x fifo: %d\n", iir, rfsr, fifo); + + if (dev->rearmed) { + do_gettimeofday(&curr_tv); + dev->acc_space += 1000000ull + * (curr_tv.tv_sec - dev->last_tv.tv_sec) + + (curr_tv.tv_usec - dev->last_tv.tv_usec); + dev->rearmed = 0; + } + for (i = 0; i < fifo; i++) { + data = ite8709_read(dev, i+ITE8709_FIFO_START); + data = ~data; + /* Loop through */ + for (bit = 0; bit < 8; ++bit) { + if ((data >> bit) & 1) { + dev->acc_pulse += bit_duration; + if (dev->lastbit == 0) { + ite8709_add_read_queue(dev, 0, + dev->acc_space); + dev->acc_space = 0; + } + } else { + dev->acc_space += bit_duration; + if (dev->lastbit == 1) { + ite8709_add_read_queue(dev, 1, + dev->acc_pulse); + dev->acc_pulse = 0; + } + } + dev->lastbit = (data >> bit) & 1; + } + } + ite8709_write(dev, ITE8709_RFSR, 0); + + if (dev->acc_space > CFG_TIMEOUT) { + dprintk("scheduling rearm IRQ\n"); + do_gettimeofday(&dev->last_tv); + dev->force_rearm = 0; + tasklet_schedule(&dev->tasklet); + } + + spin_unlock(&dev->hardware_lock); + return IRQ_RETVAL(IRQ_HANDLED); + + default: + /* not our irq */ + dprintk("unknown IRQ (shouldn't happen) !!\n"); + spin_unlock(&dev->hardware_lock); + return IRQ_RETVAL(IRQ_NONE); + } +} + +static void ite8709_rearm_irq(unsigned long data) +{ + struct ite8709_device *dev; + unsigned long flags; + dev = (struct ite8709_device *) data; + + spin_lock_irqsave(&dev->hardware_lock, flags); + dev->device_busy = 1; + spin_unlock_irqrestore(&dev->hardware_lock, flags); + + if (dev->force_rearm || dev->acc_space > CFG_TIMEOUT) { + dprintk("rearming IRQ\n"); + ite8709_write_register(dev, IT8512_REG_RCR, + IT8512_RCR_RXACT | CFG_DCR); + ite8709_write_register(dev, IT8512_REG_MSTCR, + CFG_FIFOTL | IT8512_MSTCR_FIFOCLR); + ite8709_write_register(dev, IT8512_REG_RCR, + IT8512_RCR_RXEN | IT8512_RCR_RXACT | CFG_DCR); + if (!dev->force_rearm) + dev->rearmed = 1; + dev->force_rearm = 0; + } + + spin_lock_irqsave(&dev->hardware_lock, flags); + dev->device_busy = 0; + spin_unlock_irqrestore(&dev->hardware_lock, flags); +} + +static int ite8709_cleanup(struct ite8709_device *dev, int stage, int errno, + char *msg) +{ + if (msg != NULL) + printk(KERN_ERR LIRC_DRIVER_NAME ": %s\n", msg); + + switch (stage) { + case 6: + if (dev->use_count > 0) + ite8709_drop_hardware(dev); + case 5: + free_irq(dev->irq, dev); + case 4: + release_region(dev->io, 2); + case 3: + lirc_unregister_driver(dev->driver.minor); + case 2: + lirc_buffer_free(dev->driver.rbuf); + kfree(dev->driver.rbuf); + case 1: + kfree(dev); + case 0: + ; + } + + return errno; +} + +static int __devinit ite8709_pnp_probe(struct pnp_dev *dev, + const struct pnp_device_id *dev_id) +{ + struct lirc_driver *driver; + struct ite8709_device *ite8709_dev; + int ret; + + /* Check resources validity */ + if (!pnp_irq_valid(dev, 0)) + return ite8709_cleanup(NULL, 0, -ENODEV, "invalid IRQ"); + if (!pnp_port_valid(dev, 2)) + return ite8709_cleanup(NULL, 0, -ENODEV, "invalid IO port"); + + /* Allocate memory for device struct */ + ite8709_dev = kzalloc(sizeof(struct ite8709_device), GFP_KERNEL); + if (ite8709_dev == NULL) + return ite8709_cleanup(NULL, 0, -ENOMEM, "kzalloc failed"); + pnp_set_drvdata(dev, ite8709_dev); + + /* Initialize device struct */ + ite8709_dev->use_count = 0; + ite8709_dev->irq = pnp_irq(dev, 0); + ite8709_dev->io = pnp_port_start(dev, 2); + ite8709_dev->hardware_lock = + __SPIN_LOCK_UNLOCKED(ite8709_dev->hardware_lock); + ite8709_dev->acc_pulse = 0; + ite8709_dev->acc_space = 0; + ite8709_dev->lastbit = 0; + do_gettimeofday(&ite8709_dev->last_tv); + tasklet_init(&ite8709_dev->tasklet, ite8709_rearm_irq, + (long) ite8709_dev); + ite8709_dev->force_rearm = 0; + ite8709_dev->rearmed = 0; + ite8709_dev->device_busy = 0; + + /* Initialize driver struct */ + driver = &ite8709_dev->driver; + strcpy(driver->name, LIRC_DRIVER_NAME); + driver->minor = -1; + driver->code_length = sizeof(int) * 8; + driver->sample_rate = 0; + driver->features = LIRC_CAN_REC_MODE2; + driver->data = ite8709_dev; + driver->add_to_buf = NULL; + driver->set_use_inc = ite8709_set_use_inc; + driver->set_use_dec = ite8709_set_use_dec; + driver->dev = &dev->dev; + driver->owner = THIS_MODULE; + + /* Initialize LIRC buffer */ + driver->rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); + if (!driver->rbuf) + return ite8709_cleanup(ite8709_dev, 1, -ENOMEM, + "can't allocate lirc_buffer"); + if (lirc_buffer_init(driver->rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) + return ite8709_cleanup(ite8709_dev, 1, -ENOMEM, + "lirc_buffer_init() failed"); + + /* Register LIRC driver */ + ret = lirc_register_driver(driver); + if (ret < 0) + return ite8709_cleanup(ite8709_dev, 2, ret, + "lirc_register_driver() failed"); + + /* Reserve I/O port access */ + if (!request_region(ite8709_dev->io, 2, LIRC_DRIVER_NAME)) + return ite8709_cleanup(ite8709_dev, 3, -EBUSY, + "i/o port already in use"); + + /* Reserve IRQ line */ + ret = request_irq(ite8709_dev->irq, ite8709_interrupt, 0, + LIRC_DRIVER_NAME, ite8709_dev); + if (ret < 0) + return ite8709_cleanup(ite8709_dev, 4, ret, + "IRQ already in use"); + + /* Initialize hardware */ + ite8709_drop_hardware(ite8709_dev); /* Shutdown hw until first use */ + + printk(KERN_INFO LIRC_DRIVER_NAME ": device found : irq=%d io=0x%x\n", + ite8709_dev->irq, ite8709_dev->io); + + return 0; +} + +static void __devexit ite8709_pnp_remove(struct pnp_dev *dev) +{ + struct ite8709_device *ite8709_dev; + ite8709_dev = pnp_get_drvdata(dev); + + ite8709_cleanup(ite8709_dev, 6, 0, NULL); + + printk(KERN_INFO LIRC_DRIVER_NAME ": device removed\n"); +} + +#ifdef CONFIG_PM +static int ite8709_pnp_suspend(struct pnp_dev *dev, pm_message_t state) +{ + struct ite8709_device *ite8709_dev; + ite8709_dev = pnp_get_drvdata(dev); + + if (ite8709_dev->use_count > 0) + ite8709_drop_hardware(ite8709_dev); + + return 0; +} + +static int ite8709_pnp_resume(struct pnp_dev *dev) +{ + struct ite8709_device *ite8709_dev; + ite8709_dev = pnp_get_drvdata(dev); + + if (ite8709_dev->use_count > 0) + ite8709_init_hardware(ite8709_dev); + + return 0; +} +#else +#define ite8709_pnp_suspend NULL +#define ite8709_pnp_resume NULL +#endif + +static const struct pnp_device_id pnp_dev_table[] = { + {"ITE8709", 0}, + {} +}; + +MODULE_DEVICE_TABLE(pnp, pnp_dev_table); + +static struct pnp_driver ite8709_pnp_driver = { + .name = LIRC_DRIVER_NAME, + .probe = ite8709_pnp_probe, + .remove = __devexit_p(ite8709_pnp_remove), + .suspend = ite8709_pnp_suspend, + .resume = ite8709_pnp_resume, + .id_table = pnp_dev_table, +}; + +static int __init ite8709_init_module(void) +{ + return pnp_register_driver(&ite8709_pnp_driver); +} +module_init(ite8709_init_module); + +static void __exit ite8709_cleanup_module(void) +{ + pnp_unregister_driver(&ite8709_pnp_driver); +} +module_exit(ite8709_cleanup_module); + +MODULE_DESCRIPTION("LIRC driver for ITE8709 CIR port"); +MODULE_AUTHOR("Grégory Lardière"); +MODULE_LICENSE("GPL"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Enable debugging messages"); diff --git a/drivers/staging/lirc/lirc_parallel.c b/drivers/staging/lirc/lirc_parallel.c new file mode 100644 index 0000000..a1ebd07 --- /dev/null +++ b/drivers/staging/lirc/lirc_parallel.c @@ -0,0 +1,705 @@ +/* + * lirc_parallel.c + * + * lirc_parallel - device driver for infra-red signal receiving and + * transmitting unit built by the author + * + * Copyright (C) 1998 Christoph Bartelmus <lirc@bartelmus.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 + * + */ + +/*** Includes ***/ + +#ifdef CONFIG_SMP +#error "--- Sorry, this driver is not SMP safe. ---" +#endif + +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/time.h> +#include <linux/mm.h> +#include <linux/delay.h> + +#include <linux/io.h> +#include <linux/signal.h> +#include <linux/irq.h> +#include <linux/uaccess.h> +#include <asm/div64.h> + +#include <linux/poll.h> +#include <linux/parport.h> + +#include <media/lirc.h> +#include <media/lirc_dev.h> + +#include "lirc_parallel.h" + +#define LIRC_DRIVER_NAME "lirc_parallel" + +#ifndef LIRC_IRQ +#define LIRC_IRQ 7 +#endif +#ifndef LIRC_PORT +#define LIRC_PORT 0x378 +#endif +#ifndef LIRC_TIMER +#define LIRC_TIMER 65536 +#endif + +/*** Global Variables ***/ + +static int debug; +static int check_pselecd; + +unsigned int irq = LIRC_IRQ; +unsigned int io = LIRC_PORT; +#ifdef LIRC_TIMER +unsigned int timer; +unsigned int default_timer = LIRC_TIMER; +#endif + +#define RBUF_SIZE (256) /* this must be a power of 2 larger than 1 */ + +static int rbuf[RBUF_SIZE]; + +DECLARE_WAIT_QUEUE_HEAD(lirc_wait); + +unsigned int rptr; +unsigned int wptr; +unsigned int lost_irqs; +int is_open; + +struct parport *pport; +struct pardevice *ppdevice; +int is_claimed; + +unsigned int tx_mask = 1; + +/*** Internal Functions ***/ + +static unsigned int in(int offset) +{ + switch (offset) { + case LIRC_LP_BASE: + return parport_read_data(pport); + case LIRC_LP_STATUS: + return parport_read_status(pport); + case LIRC_LP_CONTROL: + return parport_read_control(pport); + } + return 0; /* make compiler happy */ +} + +static void out(int offset, int value) +{ + switch (offset) { + case LIRC_LP_BASE: + parport_write_data(pport, value); + break; + case LIRC_LP_CONTROL: + parport_write_control(pport, value); + break; + case LIRC_LP_STATUS: + printk(KERN_INFO "%s: attempt to write to status register\n", + LIRC_DRIVER_NAME); + break; + } +} + +static unsigned int lirc_get_timer(void) +{ + return in(LIRC_PORT_TIMER) & LIRC_PORT_TIMER_BIT; +} + +static unsigned int lirc_get_signal(void) +{ + return in(LIRC_PORT_SIGNAL) & LIRC_PORT_SIGNAL_BIT; +} + +static void lirc_on(void) +{ + out(LIRC_PORT_DATA, tx_mask); +} + +static void lirc_off(void) +{ + out(LIRC_PORT_DATA, 0); +} + +static unsigned int init_lirc_timer(void) +{ + struct timeval tv, now; + unsigned int level, newlevel, timeelapsed, newtimer; + int count = 0; + + do_gettimeofday(&tv); + tv.tv_sec++; /* wait max. 1 sec. */ + level = lirc_get_timer(); + do { + newlevel = lirc_get_timer(); + if (level == 0 && newlevel != 0) + count++; + level = newlevel; + do_gettimeofday(&now); + } while (count < 1000 && (now.tv_sec < tv.tv_sec + || (now.tv_sec == tv.tv_sec + && now.tv_usec < tv.tv_usec))); + + timeelapsed = ((now.tv_sec + 1 - tv.tv_sec)*1000000 + + (now.tv_usec - tv.tv_usec)); + if (count >= 1000 && timeelapsed > 0) { + if (default_timer == 0) { + /* autodetect timer */ + newtimer = (1000000*count)/timeelapsed; + printk(KERN_INFO "%s: %u Hz timer detected\n", + LIRC_DRIVER_NAME, newtimer); + return newtimer; + } else { + newtimer = (1000000*count)/timeelapsed; + if (abs(newtimer - default_timer) > default_timer/10) { + /* bad timer */ + printk(KERN_NOTICE "%s: bad timer: %u Hz\n", + LIRC_DRIVER_NAME, newtimer); + printk(KERN_NOTICE "%s: using default timer: " + "%u Hz\n", + LIRC_DRIVER_NAME, default_timer); + return default_timer; + } else { + printk(KERN_INFO "%s: %u Hz timer detected\n", + LIRC_DRIVER_NAME, newtimer); + return newtimer; /* use detected value */ + } + } + } else { + printk(KERN_NOTICE "%s: no timer detected\n", LIRC_DRIVER_NAME); + return 0; + } +} + +static int lirc_claim(void) +{ + if (parport_claim(ppdevice) != 0) { + printk(KERN_WARNING "%s: could not claim port\n", + LIRC_DRIVER_NAME); + printk(KERN_WARNING "%s: waiting for port becoming available" + "\n", LIRC_DRIVER_NAME); + if (parport_claim_or_block(ppdevice) < 0) { + printk(KERN_NOTICE "%s: could not claim port, giving" + " up\n", LIRC_DRIVER_NAME); + return 0; + } + } + out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP); + is_claimed = 1; + return 1; +} + +/*** interrupt handler ***/ + +static void rbuf_write(int signal) +{ + unsigned int nwptr; + + nwptr = (wptr + 1) & (RBUF_SIZE - 1); + if (nwptr == rptr) { + /* no new signals will be accepted */ + lost_irqs++; + printk(KERN_NOTICE "%s: buffer overrun\n", LIRC_DRIVER_NAME); + return; + } + rbuf[wptr] = signal; + wptr = nwptr; +} + +static void irq_handler(void *blah) +{ + struct timeval tv; + static struct timeval lasttv; + static int init; + long signal; + int data; + unsigned int level, newlevel; + unsigned int timeout; + + if (!module_refcount(THIS_MODULE)) + return; + + if (!is_claimed) + return; + +#if 0 + /* disable interrupt */ + disable_irq(irq); + out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ) & (~LP_PINTEN)); +#endif + if (check_pselecd && (in(1) & LP_PSELECD)) + return; + +#ifdef LIRC_TIMER + if (init) { + do_gettimeofday(&tv); + + signal = tv.tv_sec - lasttv.tv_sec; + if (signal > 15) + /* really long time */ + data = PULSE_MASK; + else + data = (int) (signal*1000000 + + tv.tv_usec - lasttv.tv_usec + + LIRC_SFH506_DELAY); + + rbuf_write(data); /* space */ + } else { + if (timer == 0) { + /* + * wake up; we'll lose this signal, but it will be + * garbage if the device is turned on anyway + */ + timer = init_lirc_timer(); + /* enable_irq(irq); */ + return; + } + init = 1; + } + + timeout = timer/10; /* timeout after 1/10 sec. */ + signal = 1; + level = lirc_get_timer(); + do { + newlevel = lirc_get_timer(); + if (level == 0 && newlevel != 0) + signal++; + level = newlevel; + + /* giving up */ + if (signal > timeout + || (check_pselecd && (in(1) & LP_PSELECD))) { + signal = 0; + printk(KERN_NOTICE "%s: timeout\n", LIRC_DRIVER_NAME); + break; + } + } while (lirc_get_signal()); + + if (signal != 0) { + /* ajust value to usecs */ + unsigned long long helper; + + helper = ((unsigned long long) signal)*1000000; + do_div(helper, timer); + signal = (long) helper; + + if (signal > LIRC_SFH506_DELAY) + data = signal - LIRC_SFH506_DELAY; + else + data = 1; + rbuf_write(PULSE_BIT|data); /* pulse */ + } + do_gettimeofday(&lasttv); +#else + /* add your code here */ +#endif + + wake_up_interruptible(&lirc_wait); + + /* enable interrupt */ + /* + enable_irq(irq); + out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ)|LP_PINTEN); + */ +} + +/*** file operations ***/ + +static loff_t lirc_lseek(struct file *filep, loff_t offset, int orig) +{ + return -ESPIPE; +} + +static ssize_t lirc_read(struct file *filep, char *buf, size_t n, loff_t *ppos) +{ + int result = 0; + int count = 0; + DECLARE_WAITQUEUE(wait, current); + + if (n % sizeof(int)) + return -EINVAL; + + add_wait_queue(&lirc_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + while (count < n) { + if (rptr != wptr) { + if (copy_to_user(buf+count, (char *) &rbuf[rptr], + sizeof(int))) { + result = -EFAULT; + break; + } + rptr = (rptr + 1) & (RBUF_SIZE - 1); + count += sizeof(int); + } else { + if (filep->f_flags & O_NONBLOCK) { + result = -EAGAIN; + break; + } + if (signal_pending(current)) { + result = -ERESTARTSYS; + break; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + } + remove_wait_queue(&lirc_wait, &wait); + set_current_state(TASK_RUNNING); + return count ? count : result; +} + +static ssize_t lirc_write(struct file *filep, const char *buf, size_t n, + loff_t *ppos) +{ + int count; + unsigned int i; + unsigned int level, newlevel; + unsigned long flags; + int counttimer; + int *wbuf; + + if (!is_claimed) + return -EBUSY; + + count = n / sizeof(int); + + if (n % sizeof(int) || count % 2 == 0) + return -EINVAL; + + wbuf = memdup_user(buf, n); + if (IS_ERR(wbuf)) + return PTR_ERR(wbuf); + +#ifdef LIRC_TIMER + if (timer == 0) { + /* try again if device is ready */ + timer = init_lirc_timer(); + if (timer == 0) + return -EIO; + } + + /* adjust values from usecs */ + for (i = 0; i < count; i++) { + unsigned long long helper; + + helper = ((unsigned long long) wbuf[i])*timer; + do_div(helper, 1000000); + wbuf[i] = (int) helper; + } + + local_irq_save(flags); + i = 0; + while (i < count) { + level = lirc_get_timer(); + counttimer = 0; + lirc_on(); + do { + newlevel = lirc_get_timer(); + if (level == 0 && newlevel != 0) + counttimer++; + level = newlevel; + if (check_pselecd && (in(1) & LP_PSELECD)) { + lirc_off(); + local_irq_restore(flags); + return -EIO; + } + } while (counttimer < wbuf[i]); + i++; + + lirc_off(); + if (i == count) + break; + counttimer = 0; + do { + newlevel = lirc_get_timer(); + if (level == 0 && newlevel != 0) + counttimer++; + level = newlevel; + if (check_pselecd && (in(1) & LP_PSELECD)) { + local_irq_restore(flags); + return -EIO; + } + } while (counttimer < wbuf[i]); + i++; + } + local_irq_restore(flags); +#else + /* place code that handles write without external timer here */ +#endif + return n; +} + +static unsigned int lirc_poll(struct file *file, poll_table *wait) +{ + poll_wait(file, &lirc_wait, wait); + if (rptr != wptr) + return POLLIN | POLLRDNORM; + return 0; +} + +static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + int result; + unsigned long features = LIRC_CAN_SET_TRANSMITTER_MASK | + LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2; + unsigned long mode; + unsigned int ivalue; + + switch (cmd) { + case LIRC_GET_FEATURES: + result = put_user(features, (unsigned long *) arg); + if (result) + return result; + break; + case LIRC_GET_SEND_MODE: + result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg); + if (result) + return result; + break; + case LIRC_GET_REC_MODE: + result = put_user(LIRC_MODE_MODE2, (unsigned long *) arg); + if (result) + return result; + break; + case LIRC_SET_SEND_MODE: + result = get_user(mode, (unsigned long *) arg); + if (result) + return result; + if (mode != LIRC_MODE_PULSE) + return -EINVAL; + break; + case LIRC_SET_REC_MODE: + result = get_user(mode, (unsigned long *) arg); + if (result) + return result; + if (mode != LIRC_MODE_MODE2) + return -ENOSYS; + break; + case LIRC_SET_TRANSMITTER_MASK: + result = get_user(ivalue, (unsigned int *) arg); + if (result) + return result; + if ((ivalue & LIRC_PARALLEL_TRANSMITTER_MASK) != ivalue) + return LIRC_PARALLEL_MAX_TRANSMITTERS; + tx_mask = ivalue; + break; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static int lirc_open(struct inode *node, struct file *filep) +{ + if (module_refcount(THIS_MODULE) || !lirc_claim()) + return -EBUSY; + + parport_enable_irq(pport); + + /* init read ptr */ + rptr = 0; + wptr = 0; + lost_irqs = 0; + + is_open = 1; + return 0; +} + +static int lirc_close(struct inode *node, struct file *filep) +{ + if (is_claimed) { + is_claimed = 0; + parport_release(ppdevice); + } + is_open = 0; + return 0; +} + +static const struct file_operations lirc_fops = { + .owner = THIS_MODULE, + .llseek = lirc_lseek, + .read = lirc_read, + .write = lirc_write, + .poll = lirc_poll, + .unlocked_ioctl = lirc_ioctl, + .open = lirc_open, + .release = lirc_close +}; + +static int set_use_inc(void *data) +{ + return 0; +} + +static void set_use_dec(void *data) +{ +} + +static struct lirc_driver driver = { + .name = LIRC_DRIVER_NAME, + .minor = -1, + .code_length = 1, + .sample_rate = 0, + .data = NULL, + .add_to_buf = NULL, + .set_use_inc = set_use_inc, + .set_use_dec = set_use_dec, + .fops = &lirc_fops, + .dev = NULL, + .owner = THIS_MODULE, +}; + +static int pf(void *handle); +static void kf(void *handle); + +static struct timer_list poll_timer; +static void poll_state(unsigned long ignored); + +static void poll_state(unsigned long ignored) +{ + printk(KERN_NOTICE "%s: time\n", + LIRC_DRIVER_NAME); + del_timer(&poll_timer); + if (is_claimed) + return; + kf(NULL); + if (!is_claimed) { + printk(KERN_NOTICE "%s: could not claim port, giving up\n", + LIRC_DRIVER_NAME); + init_timer(&poll_timer); + poll_timer.expires = jiffies + HZ; + poll_timer.data = (unsigned long)current; + poll_timer.function = poll_state; + add_timer(&poll_timer); + } +} + +static int pf(void *handle) +{ + parport_disable_irq(pport); + is_claimed = 0; + return 0; +} + +static void kf(void *handle) +{ + if (!is_open) + return; + if (!lirc_claim()) + return; + parport_enable_irq(pport); + lirc_off(); + /* this is a bit annoying when you actually print...*/ + /* + printk(KERN_INFO "%s: reclaimed port\n", LIRC_DRIVER_NAME); + */ +} + +/*** module initialization and cleanup ***/ + +static int __init lirc_parallel_init(void) +{ + pport = parport_find_base(io); + if (pport == NULL) { + printk(KERN_NOTICE "%s: no port at %x found\n", + LIRC_DRIVER_NAME, io); + return -ENXIO; + } + ppdevice = parport_register_device(pport, LIRC_DRIVER_NAME, + pf, kf, irq_handler, 0, NULL); + parport_put_port(pport); + if (ppdevice == NULL) { + printk(KERN_NOTICE "%s: parport_register_device() failed\n", + LIRC_DRIVER_NAME); + return -ENXIO; + } + if (parport_claim(ppdevice) != 0) + goto skip_init; + is_claimed = 1; + out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP); + +#ifdef LIRC_TIMER + if (debug) + out(LIRC_PORT_DATA, tx_mask); + + timer = init_lirc_timer(); + +#if 0 /* continue even if device is offline */ + if (timer == 0) { + is_claimed = 0; + parport_release(pport); + parport_unregister_device(ppdevice); + return -EIO; + } + +#endif + if (debug) + out(LIRC_PORT_DATA, 0); +#endif + + is_claimed = 0; + parport_release(ppdevice); + skip_init: + driver.minor = lirc_register_driver(&driver); + if (driver.minor < 0) { + printk(KERN_NOTICE "%s: register_chrdev() failed\n", + LIRC_DRIVER_NAME); + parport_unregister_device(ppdevice); + return -EIO; + } + printk(KERN_INFO "%s: installed using port 0x%04x irq %d\n", + LIRC_DRIVER_NAME, io, irq); + return 0; +} + +static void __exit lirc_parallel_exit(void) +{ + parport_unregister_device(ppdevice); + lirc_unregister_driver(driver.minor); +} + +module_init(lirc_parallel_init); +module_exit(lirc_parallel_exit); + +MODULE_DESCRIPTION("Infrared receiver driver for parallel ports."); +MODULE_AUTHOR("Christoph Bartelmus"); +MODULE_LICENSE("GPL"); + +module_param(io, int, S_IRUGO); +MODULE_PARM_DESC(io, "I/O address base (0x3bc, 0x378 or 0x278)"); + +module_param(irq, int, S_IRUGO); +MODULE_PARM_DESC(irq, "Interrupt (7 or 5)"); + +module_param(tx_mask, int, S_IRUGO); +MODULE_PARM_DESC(tx_maxk, "Transmitter mask (default: 0x01)"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Enable debugging messages"); + +module_param(check_pselecd, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Check for printer (default: 0)"); diff --git a/drivers/staging/lirc/lirc_parallel.h b/drivers/staging/lirc/lirc_parallel.h new file mode 100644 index 0000000..4bed6af --- /dev/null +++ b/drivers/staging/lirc/lirc_parallel.h @@ -0,0 +1,26 @@ +/* lirc_parallel.h */ + +#ifndef _LIRC_PARALLEL_H +#define _LIRC_PARALLEL_H + +#include <linux/lp.h> + +#define LIRC_PORT_LEN 3 + +#define LIRC_LP_BASE 0 +#define LIRC_LP_STATUS 1 +#define LIRC_LP_CONTROL 2 + +#define LIRC_PORT_DATA LIRC_LP_BASE /* base */ +#define LIRC_PORT_TIMER LIRC_LP_STATUS /* status port */ +#define LIRC_PORT_TIMER_BIT LP_PBUSY /* busy signal */ +#define LIRC_PORT_SIGNAL LIRC_LP_STATUS /* status port */ +#define LIRC_PORT_SIGNAL_BIT LP_PACK /* ack signal */ +#define LIRC_PORT_IRQ LIRC_LP_CONTROL /* control port */ + +#define LIRC_SFH506_DELAY 0 /* delay t_phl in usecs */ + +#define LIRC_PARALLEL_MAX_TRANSMITTERS 8 +#define LIRC_PARALLEL_TRANSMITTER_MASK ((1<<LIRC_PARALLEL_MAX_TRANSMITTERS) - 1) + +#endif diff --git a/drivers/staging/lirc/lirc_sasem.c b/drivers/staging/lirc/lirc_sasem.c new file mode 100644 index 0000000..73166c3 --- /dev/null +++ b/drivers/staging/lirc/lirc_sasem.c @@ -0,0 +1,933 @@ +/* + * lirc_sasem.c - USB remote support for LIRC + * Version 0.5 + * + * Copyright (C) 2004-2005 Oliver Stabel <oliver.stabel@gmx.de> + * Tim Davies <tim@opensystems.net.au> + * + * This driver was derived from: + * Venky Raju <dev@venky.ws> + * "lirc_imon - "LIRC/VFD driver for Ahanix/Soundgraph IMON IR/VFD" + * Paul Miller <pmiller9@users.sourceforge.net>'s 2003-2004 + * "lirc_atiusb - USB remote support for LIRC" + * Culver Consulting Services <henry@culcon.com>'s 2003 + * "Sasem OnAir VFD/IR USB driver" + * + * + * NOTE - The LCDproc iMon driver should work with this module. More info at + * http://www.frogstorm.info/sasem + */ + +/* + * 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/errno.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/usb.h> + +#include <media/lirc.h> +#include <media/lirc_dev.h> + + +#define MOD_AUTHOR "Oliver Stabel <oliver.stabel@gmx.de>, " \ + "Tim Davies <tim@opensystems.net.au>" +#define MOD_DESC "USB Driver for Sasem Remote Controller V1.1" +#define MOD_NAME "lirc_sasem" +#define MOD_VERSION "0.5" + +#define VFD_MINOR_BASE 144 /* Same as LCD */ +#define DEVICE_NAME "lcd%d" + +#define BUF_CHUNK_SIZE 8 +#define BUF_SIZE 128 + +#define IOCTL_LCD_CONTRAST 1 + +/*** P R O T O T Y P E S ***/ + +/* USB Callback prototypes */ +static int sasem_probe(struct usb_interface *interface, + const struct usb_device_id *id); +static void sasem_disconnect(struct usb_interface *interface); +static void usb_rx_callback(struct urb *urb); +static void usb_tx_callback(struct urb *urb); + +/* VFD file_operations function prototypes */ +static int vfd_open(struct inode *inode, struct file *file); +static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg); +static int vfd_close(struct inode *inode, struct file *file); +static ssize_t vfd_write(struct file *file, const char *buf, + size_t n_bytes, loff_t *pos); + +/* LIRC driver function prototypes */ +static int ir_open(void *data); +static void ir_close(void *data); + +/* Driver init/exit prototypes */ +static int __init sasem_init(void); +static void __exit sasem_exit(void); + +/*** G L O B A L S ***/ +#define SASEM_DATA_BUF_SZ 32 + +struct sasem_context { + + struct usb_device *dev; + int vfd_isopen; /* VFD port has been opened */ + unsigned int vfd_contrast; /* VFD contrast */ + int ir_isopen; /* IR port has been opened */ + int dev_present; /* USB device presence */ + struct mutex ctx_lock; /* to lock this object */ + wait_queue_head_t remove_ok; /* For unexpected USB disconnects */ + + struct lirc_driver *driver; + struct usb_endpoint_descriptor *rx_endpoint; + struct usb_endpoint_descriptor *tx_endpoint; + struct urb *rx_urb; + struct urb *tx_urb; + unsigned char usb_rx_buf[8]; + unsigned char usb_tx_buf[8]; + + struct tx_t { + unsigned char data_buf[SASEM_DATA_BUF_SZ]; /* user data buffer */ + struct completion finished; /* wait for write to finish */ + atomic_t busy; /* write in progress */ + int status; /* status of tx completion */ + } tx; + + /* for dealing with repeat codes (wish there was a toggle bit!) */ + struct timeval presstime; + char lastcode[8]; + int codesaved; +}; + +/* VFD file operations */ +static const struct file_operations vfd_fops = { + .owner = THIS_MODULE, + .open = &vfd_open, + .write = &vfd_write, + .unlocked_ioctl = &vfd_ioctl, + .release = &vfd_close, +}; + +/* USB Device ID for Sasem USB Control Board */ +static struct usb_device_id sasem_usb_id_table[] = { + /* Sasem USB Control Board */ + { USB_DEVICE(0x11ba, 0x0101) }, + /* Terminating entry */ + {} +}; + +/* USB Device data */ +static struct usb_driver sasem_driver = { + .name = MOD_NAME, + .probe = sasem_probe, + .disconnect = sasem_disconnect, + .id_table = sasem_usb_id_table, +}; + +static struct usb_class_driver sasem_class = { + .name = DEVICE_NAME, + .fops = &vfd_fops, + .minor_base = VFD_MINOR_BASE, +}; + +/* to prevent races between open() and disconnect() */ +static DEFINE_MUTEX(disconnect_lock); + +static int debug; + + +/*** M O D U L E C O D E ***/ + +MODULE_AUTHOR(MOD_AUTHOR); +MODULE_DESCRIPTION(MOD_DESC); +MODULE_LICENSE("GPL"); +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes (default: no)"); + +static void delete_context(struct sasem_context *context) +{ + usb_free_urb(context->tx_urb); /* VFD */ + usb_free_urb(context->rx_urb); /* IR */ + lirc_buffer_free(context->driver->rbuf); + kfree(context->driver->rbuf); + kfree(context->driver); + kfree(context); + + if (debug) + printk(KERN_INFO "%s: context deleted\n", __func__); +} + +static void deregister_from_lirc(struct sasem_context *context) +{ + int retval; + int minor = context->driver->minor; + + retval = lirc_unregister_driver(minor); + if (retval) + err("%s: unable to deregister from lirc (%d)", + __func__, retval); + else + printk(KERN_INFO "Deregistered Sasem driver (minor:%d)\n", + minor); + +} + +/** + * Called when the VFD device (e.g. /dev/usb/lcd) + * is opened by the application. + */ +static int vfd_open(struct inode *inode, struct file *file) +{ + struct usb_interface *interface; + struct sasem_context *context = NULL; + int subminor; + int retval = 0; + + /* prevent races with disconnect */ + mutex_lock(&disconnect_lock); + + subminor = iminor(inode); + interface = usb_find_interface(&sasem_driver, subminor); + if (!interface) { + err("%s: could not find interface for minor %d", + __func__, subminor); + retval = -ENODEV; + goto exit; + } + context = usb_get_intfdata(interface); + + if (!context) { + err("%s: no context found for minor %d", + __func__, subminor); + retval = -ENODEV; + goto exit; + } + + mutex_lock(&context->ctx_lock); + + if (context->vfd_isopen) { + err("%s: VFD port is already open", __func__); + retval = -EBUSY; + } else { + context->vfd_isopen = 1; + file->private_data = context; + printk(KERN_INFO "VFD port opened\n"); + } + + mutex_unlock(&context->ctx_lock); + +exit: + mutex_unlock(&disconnect_lock); + return retval; +} + +/** + * Called when the VFD device (e.g. /dev/usb/lcd) + * is closed by the application. + */ +static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg) +{ + struct sasem_context *context = NULL; + + context = (struct sasem_context *) file->private_data; + + if (!context) { + err("%s: no context for device", __func__); + return -ENODEV; + } + + mutex_lock(&context->ctx_lock); + + switch (cmd) { + case IOCTL_LCD_CONTRAST: + if (arg > 1000) + arg = 1000; + context->vfd_contrast = (unsigned int)arg; + break; + default: + printk(KERN_INFO "Unknown IOCTL command\n"); + mutex_unlock(&context->ctx_lock); + return -ENOIOCTLCMD; /* not supported */ + } + + mutex_unlock(&context->ctx_lock); + return 0; +} + +/** + * Called when the VFD device (e.g. /dev/usb/lcd) + * is closed by the application. + */ +static int vfd_close(struct inode *inode, struct file *file) +{ + struct sasem_context *context = NULL; + int retval = 0; + + context = (struct sasem_context *) file->private_data; + + if (!context) { + err("%s: no context for device", __func__); + return -ENODEV; + } + + mutex_lock(&context->ctx_lock); + + if (!context->vfd_isopen) { + err("%s: VFD is not open", __func__); + retval = -EIO; + } else { + context->vfd_isopen = 0; + printk(KERN_INFO "VFD port closed\n"); + if (!context->dev_present && !context->ir_isopen) { + + /* Device disconnected before close and IR port is + * not open. If IR port is open, context will be + * deleted by ir_close. */ + mutex_unlock(&context->ctx_lock); + delete_context(context); + return retval; + } + } + + mutex_unlock(&context->ctx_lock); + return retval; +} + +/** + * Sends a packet to the VFD. + */ +static int send_packet(struct sasem_context *context) +{ + unsigned int pipe; + int interval = 0; + int retval = 0; + + pipe = usb_sndintpipe(context->dev, + context->tx_endpoint->bEndpointAddress); + interval = context->tx_endpoint->bInterval; + + usb_fill_int_urb(context->tx_urb, context->dev, pipe, + context->usb_tx_buf, sizeof(context->usb_tx_buf), + usb_tx_callback, context, interval); + + context->tx_urb->actual_length = 0; + + init_completion(&context->tx.finished); + atomic_set(&(context->tx.busy), 1); + + retval = usb_submit_urb(context->tx_urb, GFP_KERNEL); + if (retval) { + atomic_set(&(context->tx.busy), 0); + err("%s: error submitting urb (%d)", __func__, retval); + } else { + /* Wait for transmission to complete (or abort) */ + mutex_unlock(&context->ctx_lock); + wait_for_completion(&context->tx.finished); + mutex_lock(&context->ctx_lock); + + retval = context->tx.status; + if (retval) + err("%s: packet tx failed (%d)", __func__, retval); + } + + return retval; +} + +/** + * Writes data to the VFD. The Sasem VFD is 2x16 characters + * and requires data in 9 consecutive USB interrupt packets, + * each packet carrying 8 bytes. + */ +static ssize_t vfd_write(struct file *file, const char *buf, + size_t n_bytes, loff_t *pos) +{ + int i; + int retval = 0; + struct sasem_context *context; + int *data_buf; + + context = (struct sasem_context *) file->private_data; + if (!context) { + err("%s: no context for device", __func__); + return -ENODEV; + } + + mutex_lock(&context->ctx_lock); + + if (!context->dev_present) { + err("%s: no Sasem device present", __func__); + retval = -ENODEV; + goto exit; + } + + if (n_bytes <= 0 || n_bytes > SASEM_DATA_BUF_SZ) { + err("%s: invalid payload size", __func__); + retval = -EINVAL; + goto exit; + } + + data_buf = memdup_user(buf, n_bytes); + if (PTR_ERR(data_buf)) + return PTR_ERR(data_buf); + + memcpy(context->tx.data_buf, data_buf, n_bytes); + + /* Pad with spaces */ + for (i = n_bytes; i < SASEM_DATA_BUF_SZ; ++i) + context->tx.data_buf[i] = ' '; + + /* Nine 8 byte packets to be sent */ + /* NOTE: "\x07\x01\0\0\0\0\0\0" or "\x0c\0\0\0\0\0\0\0" + * will clear the VFD */ + for (i = 0; i < 9; i++) { + switch (i) { + case 0: + memcpy(context->usb_tx_buf, "\x07\0\0\0\0\0\0\0", 8); + context->usb_tx_buf[1] = (context->vfd_contrast) ? + (0x2B - (context->vfd_contrast - 1) / 250) + : 0x2B; + break; + case 1: + memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8); + break; + case 2: + memcpy(context->usb_tx_buf, "\x0b\x01\0\0\0\0\0\0", 8); + break; + case 3: + memcpy(context->usb_tx_buf, context->tx.data_buf, 8); + break; + case 4: + memcpy(context->usb_tx_buf, + context->tx.data_buf + 8, 8); + break; + case 5: + memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8); + break; + case 6: + memcpy(context->usb_tx_buf, "\x0b\x02\0\0\0\0\0\0", 8); + break; + case 7: + memcpy(context->usb_tx_buf, + context->tx.data_buf + 16, 8); + break; + case 8: + memcpy(context->usb_tx_buf, + context->tx.data_buf + 24, 8); + break; + } + retval = send_packet(context); + if (retval) { + + err("%s: send packet failed for packet #%d", + __func__, i); + goto exit; + } + } +exit: + + mutex_unlock(&context->ctx_lock); + + return (!retval) ? n_bytes : retval; +} + +/** + * Callback function for USB core API: transmit data + */ +static void usb_tx_callback(struct urb *urb) +{ + struct sasem_context *context; + + if (!urb) + return; + context = (struct sasem_context *) urb->context; + if (!context) + return; + + context->tx.status = urb->status; + + /* notify waiters that write has finished */ + atomic_set(&context->tx.busy, 0); + complete(&context->tx.finished); + + return; +} + +/** + * Called by lirc_dev when the application opens /dev/lirc + */ +static int ir_open(void *data) +{ + int retval = 0; + struct sasem_context *context; + + /* prevent races with disconnect */ + mutex_lock(&disconnect_lock); + + context = (struct sasem_context *) data; + + mutex_lock(&context->ctx_lock); + + if (context->ir_isopen) { + err("%s: IR port is already open", __func__); + retval = -EBUSY; + goto exit; + } + + usb_fill_int_urb(context->rx_urb, context->dev, + usb_rcvintpipe(context->dev, + context->rx_endpoint->bEndpointAddress), + context->usb_rx_buf, sizeof(context->usb_rx_buf), + usb_rx_callback, context, context->rx_endpoint->bInterval); + + retval = usb_submit_urb(context->rx_urb, GFP_KERNEL); + + if (retval) + err("%s: usb_submit_urb failed for ir_open (%d)", + __func__, retval); + else { + context->ir_isopen = 1; + printk(KERN_INFO "IR port opened\n"); + } + +exit: + mutex_unlock(&context->ctx_lock); + + mutex_unlock(&disconnect_lock); + return 0; +} + +/** + * Called by lirc_dev when the application closes /dev/lirc + */ +static void ir_close(void *data) +{ + struct sasem_context *context; + + context = (struct sasem_context *)data; + if (!context) { + err("%s: no context for device", __func__); + return; + } + + mutex_lock(&context->ctx_lock); + + usb_kill_urb(context->rx_urb); + context->ir_isopen = 0; + printk(KERN_INFO "IR port closed\n"); + + if (!context->dev_present) { + + /* + * Device disconnected while IR port was + * still open. Driver was not deregistered + * at disconnect time, so do it now. + */ + deregister_from_lirc(context); + + if (!context->vfd_isopen) { + + mutex_unlock(&context->ctx_lock); + delete_context(context); + return; + } + /* If VFD port is open, context will be deleted by vfd_close */ + } + + mutex_unlock(&context->ctx_lock); + return; +} + +/** + * Process the incoming packet + */ +static void incoming_packet(struct sasem_context *context, + struct urb *urb) +{ + int len = urb->actual_length; + unsigned char *buf = urb->transfer_buffer; + long ms; + struct timeval tv; + + if (len != 8) { + printk(KERN_WARNING "%s: invalid incoming packet size (%d)\n", + __func__, len); + return; + } + +#ifdef DEBUG + int i; + for (i = 0; i < 8; ++i) + printk(KERN_INFO "%02x ", buf[i]); + printk(KERN_INFO "\n"); +#endif + + /* + * Lirc could deal with the repeat code, but we really need to block it + * if it arrives too late. Otherwise we could repeat the wrong code. + */ + + /* get the time since the last button press */ + do_gettimeofday(&tv); + ms = (tv.tv_sec - context->presstime.tv_sec) * 1000 + + (tv.tv_usec - context->presstime.tv_usec) / 1000; + + if (memcmp(buf, "\x08\0\0\0\0\0\0\0", 8) == 0) { + /* + * the repeat code is being sent, so we copy + * the old code to LIRC + */ + + /* + * NOTE: Only if the last code was less than 250ms ago + * - no one should be able to push another (undetected) button + * in that time and then get a false repeat of the previous + * press but it is long enough for a genuine repeat + */ + if ((ms < 250) && (context->codesaved != 0)) { + memcpy(buf, &context->lastcode, 8); + context->presstime.tv_sec = tv.tv_sec; + context->presstime.tv_usec = tv.tv_usec; + } + } else { + /* save the current valid code for repeats */ + memcpy(&context->lastcode, buf, 8); + /* + * set flag to signal a valid code was save; + * just for safety reasons + */ + context->codesaved = 1; + context->presstime.tv_sec = tv.tv_sec; + context->presstime.tv_usec = tv.tv_usec; + } + + lirc_buffer_write(context->driver->rbuf, buf); + wake_up(&context->driver->rbuf->wait_poll); +} + +/** + * Callback function for USB core API: receive data + */ +static void usb_rx_callback(struct urb *urb) +{ + struct sasem_context *context; + + if (!urb) + return; + context = (struct sasem_context *) urb->context; + if (!context) + return; + + switch (urb->status) { + + case -ENOENT: /* usbcore unlink successful! */ + return; + + case 0: + if (context->ir_isopen) + incoming_packet(context, urb); + break; + + default: + printk(KERN_WARNING "%s: status (%d): ignored", + __func__, urb->status); + break; + } + + usb_submit_urb(context->rx_urb, GFP_ATOMIC); + return; +} + + + +/** + * Callback function for USB core API: Probe + */ +static int sasem_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *dev = NULL; + struct usb_host_interface *iface_desc = NULL; + struct usb_endpoint_descriptor *rx_endpoint = NULL; + struct usb_endpoint_descriptor *tx_endpoint = NULL; + struct urb *rx_urb = NULL; + struct urb *tx_urb = NULL; + struct lirc_driver *driver = NULL; + struct lirc_buffer *rbuf = NULL; + int lirc_minor = 0; + int num_endpoints; + int retval = 0; + int vfd_ep_found; + int ir_ep_found; + int alloc_status; + struct sasem_context *context = NULL; + int i; + + printk(KERN_INFO "%s: found Sasem device\n", __func__); + + + dev = usb_get_dev(interface_to_usbdev(interface)); + iface_desc = interface->cur_altsetting; + num_endpoints = iface_desc->desc.bNumEndpoints; + + /* + * Scan the endpoint list and set: + * first input endpoint = IR endpoint + * first output endpoint = VFD endpoint + */ + + ir_ep_found = 0; + vfd_ep_found = 0; + + for (i = 0; i < num_endpoints && !(ir_ep_found && vfd_ep_found); ++i) { + + struct usb_endpoint_descriptor *ep; + int ep_dir; + int ep_type; + ep = &iface_desc->endpoint [i].desc; + ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK; + ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + + if (!ir_ep_found && + ep_dir == USB_DIR_IN && + ep_type == USB_ENDPOINT_XFER_INT) { + + rx_endpoint = ep; + ir_ep_found = 1; + if (debug) + printk(KERN_INFO "%s: found IR endpoint\n", + __func__); + + } else if (!vfd_ep_found && + ep_dir == USB_DIR_OUT && + ep_type == USB_ENDPOINT_XFER_INT) { + + tx_endpoint = ep; + vfd_ep_found = 1; + if (debug) + printk(KERN_INFO "%s: found VFD endpoint\n", + __func__); + } + } + + /* Input endpoint is mandatory */ + if (!ir_ep_found) { + + err("%s: no valid input (IR) endpoint found.", __func__); + retval = -ENODEV; + goto exit; + } + + if (!vfd_ep_found) + printk(KERN_INFO "%s: no valid output (VFD) endpoint found.\n", + __func__); + + + /* Allocate memory */ + alloc_status = 0; + + context = kzalloc(sizeof(struct sasem_context), GFP_KERNEL); + if (!context) { + err("%s: kzalloc failed for context", __func__); + alloc_status = 1; + goto alloc_status_switch; + } + driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); + if (!driver) { + err("%s: kzalloc failed for lirc_driver", __func__); + alloc_status = 2; + goto alloc_status_switch; + } + rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); + if (!rbuf) { + err("%s: kmalloc failed for lirc_buffer", __func__); + alloc_status = 3; + goto alloc_status_switch; + } + if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) { + err("%s: lirc_buffer_init failed", __func__); + alloc_status = 4; + goto alloc_status_switch; + } + rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!rx_urb) { + err("%s: usb_alloc_urb failed for IR urb", __func__); + alloc_status = 5; + goto alloc_status_switch; + } + if (vfd_ep_found) { + tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!tx_urb) { + err("%s: usb_alloc_urb failed for VFD urb", + __func__); + alloc_status = 6; + goto alloc_status_switch; + } + } + + mutex_init(&context->ctx_lock); + + strcpy(driver->name, MOD_NAME); + driver->minor = -1; + driver->code_length = 64; + driver->sample_rate = 0; + driver->features = LIRC_CAN_REC_LIRCCODE; + driver->data = context; + driver->rbuf = rbuf; + driver->set_use_inc = ir_open; + driver->set_use_dec = ir_close; + driver->dev = &interface->dev; + driver->owner = THIS_MODULE; + + mutex_lock(&context->ctx_lock); + + lirc_minor = lirc_register_driver(driver); + if (lirc_minor < 0) { + err("%s: lirc_register_driver failed", __func__); + alloc_status = 7; + mutex_unlock(&context->ctx_lock); + } else + printk(KERN_INFO "%s: Registered Sasem driver (minor:%d)\n", + __func__, lirc_minor); + +alloc_status_switch: + + switch (alloc_status) { + + case 7: + if (vfd_ep_found) + usb_free_urb(tx_urb); + case 6: + usb_free_urb(rx_urb); + case 5: + lirc_buffer_free(rbuf); + case 4: + kfree(rbuf); + case 3: + kfree(driver); + case 2: + kfree(context); + context = NULL; + case 1: + retval = -ENOMEM; + goto exit; + } + + /* Needed while unregistering! */ + driver->minor = lirc_minor; + + context->dev = dev; + context->dev_present = 1; + context->rx_endpoint = rx_endpoint; + context->rx_urb = rx_urb; + if (vfd_ep_found) { + context->tx_endpoint = tx_endpoint; + context->tx_urb = tx_urb; + context->vfd_contrast = 1000; /* range 0 - 1000 */ + } + context->driver = driver; + + usb_set_intfdata(interface, context); + + if (vfd_ep_found) { + + if (debug) + printk(KERN_INFO "Registering VFD with sysfs\n"); + if (usb_register_dev(interface, &sasem_class)) + /* Not a fatal error, so ignore */ + printk(KERN_INFO "%s: could not get a minor number " + "for VFD\n", __func__); + } + + printk(KERN_INFO "%s: Sasem device on usb<%d:%d> initialized\n", + __func__, dev->bus->busnum, dev->devnum); + + mutex_unlock(&context->ctx_lock); +exit: + return retval; +} + +/** + * Callback function for USB core API: disonnect + */ +static void sasem_disconnect(struct usb_interface *interface) +{ + struct sasem_context *context; + + /* prevent races with ir_open()/vfd_open() */ + mutex_lock(&disconnect_lock); + + context = usb_get_intfdata(interface); + mutex_lock(&context->ctx_lock); + + printk(KERN_INFO "%s: Sasem device disconnected\n", __func__); + + usb_set_intfdata(interface, NULL); + context->dev_present = 0; + + /* Stop reception */ + usb_kill_urb(context->rx_urb); + + /* Abort ongoing write */ + if (atomic_read(&context->tx.busy)) { + + usb_kill_urb(context->tx_urb); + wait_for_completion(&context->tx.finished); + } + + /* De-register from lirc_dev if IR port is not open */ + if (!context->ir_isopen) + deregister_from_lirc(context); + + usb_deregister_dev(interface, &sasem_class); + + mutex_unlock(&context->ctx_lock); + + if (!context->ir_isopen && !context->vfd_isopen) + delete_context(context); + + mutex_unlock(&disconnect_lock); +} + +static int __init sasem_init(void) +{ + int rc; + + printk(KERN_INFO MOD_DESC ", v" MOD_VERSION "\n"); + printk(KERN_INFO MOD_AUTHOR "\n"); + + rc = usb_register(&sasem_driver); + if (rc < 0) { + err("%s: usb register failed (%d)", __func__, rc); + return -ENODEV; + } + return 0; +} + +static void __exit sasem_exit(void) +{ + usb_deregister(&sasem_driver); + printk(KERN_INFO "module removed. Goodbye!\n"); +} + + +module_init(sasem_init); +module_exit(sasem_exit); diff --git a/drivers/staging/lirc/lirc_serial.c b/drivers/staging/lirc/lirc_serial.c new file mode 100644 index 0000000..9456f8e --- /dev/null +++ b/drivers/staging/lirc/lirc_serial.c @@ -0,0 +1,1313 @@ +/* + * lirc_serial.c + * + * lirc_serial - Device driver that records pulse- and pause-lengths + * (space-lengths) between DDCD event on a serial port. + * + * Copyright (C) 1996,97 Ralph Metzler <rjkm@thp.uni-koeln.de> + * Copyright (C) 1998 Trent Piepho <xyzzy@u.washington.edu> + * Copyright (C) 1998 Ben Pfaff <blp@gnu.org> + * Copyright (C) 1999 Christoph Bartelmus <lirc@bartelmus.de> + * Copyright (C) 2007 Andrei Tanas <andrei@tanas.ca> (suspend/resume support) + * 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 + * + */ + +/* + * Steve's changes to improve transmission fidelity: + * - for systems with the rdtsc instruction and the clock counter, a + * send_pule that times the pulses directly using the counter. + * This means that the LIRC_SERIAL_TRANSMITTER_LATENCY fudge is + * not needed. Measurement shows very stable waveform, even where + * PCI activity slows the access to the UART, which trips up other + * versions. + * - For other system, non-integer-microsecond pulse/space lengths, + * done using fixed point binary. So, much more accurate carrier + * frequency. + * - fine tuned transmitter latency, taking advantage of fractional + * microseconds in previous change + * - Fixed bug in the way transmitter latency was accounted for by + * tuning the pulse lengths down - the send_pulse routine ignored + * this overhead as it timed the overall pulse length - so the + * pulse frequency was right but overall pulse length was too + * long. Fixed by accounting for latency on each pulse/space + * iteration. + * + * Steve Davies <steve@daviesfam.org> July 2001 + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/serial_reg.h> +#include <linux/time.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <linux/mm.h> +#include <linux/delay.h> +#include <linux/poll.h> +#include <linux/platform_device.h> + +#include <asm/system.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/fcntl.h> +#include <linux/spinlock.h> + +#ifdef CONFIG_LIRC_SERIAL_NSLU2 +#include <asm/hardware.h> +#endif +/* From Intel IXP42X Developer's Manual (#252480-005): */ +/* ftp://download.intel.com/design/network/manuals/25248005.pdf */ +#define UART_IE_IXP42X_UUE 0x40 /* IXP42X UART Unit enable */ +#define UART_IE_IXP42X_RTOIE 0x10 /* IXP42X Receiver Data Timeout int.enable */ + +#include <media/lirc.h> +#include <media/lirc_dev.h> + +#define LIRC_DRIVER_NAME "lirc_serial" + +struct lirc_serial { + int signal_pin; + int signal_pin_change; + u8 on; + u8 off; + long (*send_pulse)(unsigned long length); + void (*send_space)(long length); + int features; + spinlock_t lock; +}; + +#define LIRC_HOMEBREW 0 +#define LIRC_IRDEO 1 +#define LIRC_IRDEO_REMOTE 2 +#define LIRC_ANIMAX 3 +#define LIRC_IGOR 4 +#define LIRC_NSLU2 5 + +/*** module parameters ***/ +static int type; +static int io; +static int irq; +static int iommap; +static int ioshift; +static int softcarrier = 1; +static int share_irq; +static int debug; +static int sense = -1; /* -1 = auto, 0 = active high, 1 = active low */ +static int txsense; /* 0 = active high, 1 = active low */ + +#define dprintk(fmt, args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \ + fmt, ## args); \ + } while (0) + +/* forward declarations */ +static long send_pulse_irdeo(unsigned long length); +static long send_pulse_homebrew(unsigned long length); +static void send_space_irdeo(long length); +static void send_space_homebrew(long length); + +static struct lirc_serial hardware[] = { + [LIRC_HOMEBREW] = { + .signal_pin = UART_MSR_DCD, + .signal_pin_change = UART_MSR_DDCD, + .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR), + .off = (UART_MCR_RTS | UART_MCR_OUT2), + .send_pulse = send_pulse_homebrew, + .send_space = send_space_homebrew, +#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER + .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE | + LIRC_CAN_SET_SEND_CARRIER | + LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2) +#else + .features = LIRC_CAN_REC_MODE2 +#endif + }, + + [LIRC_IRDEO] = { + .signal_pin = UART_MSR_DSR, + .signal_pin_change = UART_MSR_DDSR, + .on = UART_MCR_OUT2, + .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2), + .send_pulse = send_pulse_irdeo, + .send_space = send_space_irdeo, + .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE | + LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2) + }, + + [LIRC_IRDEO_REMOTE] = { + .signal_pin = UART_MSR_DSR, + .signal_pin_change = UART_MSR_DDSR, + .on = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2), + .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2), + .send_pulse = send_pulse_irdeo, + .send_space = send_space_irdeo, + .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE | + LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2) + }, + + [LIRC_ANIMAX] = { + .signal_pin = UART_MSR_DCD, + .signal_pin_change = UART_MSR_DDCD, + .on = 0, + .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2), + .send_pulse = NULL, + .send_space = NULL, + .features = LIRC_CAN_REC_MODE2 + }, + + [LIRC_IGOR] = { + .signal_pin = UART_MSR_DSR, + .signal_pin_change = UART_MSR_DDSR, + .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR), + .off = (UART_MCR_RTS | UART_MCR_OUT2), + .send_pulse = send_pulse_homebrew, + .send_space = send_space_homebrew, +#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER + .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE | + LIRC_CAN_SET_SEND_CARRIER | + LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2) +#else + .features = LIRC_CAN_REC_MODE2 +#endif + }, + +#ifdef CONFIG_LIRC_SERIAL_NSLU2 + /* + * Modified Linksys Network Storage Link USB 2.0 (NSLU2): + * We receive on CTS of the 2nd serial port (R142,LHS), we + * transmit with a IR diode between GPIO[1] (green status LED), + * and ground (Matthias Goebl <matthias.goebl@goebl.net>). + * See also http://www.nslu2-linux.org for this device + */ + [LIRC_NSLU2] = { + .signal_pin = UART_MSR_CTS, + .signal_pin_change = UART_MSR_DCTS, + .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR), + .off = (UART_MCR_RTS | UART_MCR_OUT2), + .send_pulse = send_pulse_homebrew, + .send_space = send_space_homebrew, +#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER + .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE | + LIRC_CAN_SET_SEND_CARRIER | + LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2) +#else + .features = LIRC_CAN_REC_MODE2 +#endif + }, +#endif + +}; + +#define RS_ISR_PASS_LIMIT 256 + +/* + * A long pulse code from a remote might take up to 300 bytes. The + * daemon should read the bytes as soon as they are generated, so take + * the number of keys you think you can push before the daemon runs + * and multiply by 300. The driver will warn you if you overrun this + * buffer. If you have a slow computer or non-busmastering IDE disks, + * maybe you will need to increase this. + */ + +/* This MUST be a power of two! It has to be larger than 1 as well. */ + +#define RBUF_LEN 256 + +static struct timeval lasttv = {0, 0}; + +static struct lirc_buffer rbuf; + +static unsigned int freq = 38000; +static unsigned int duty_cycle = 50; + +/* Initialized in init_timing_params() */ +static unsigned long period; +static unsigned long pulse_width; +static unsigned long space_width; + +#if defined(__i386__) +/* + * From: + * Linux I/O port programming mini-HOWTO + * Author: Riku Saikkonen <Riku.Saikkonen@hut.fi> + * v, 28 December 1997 + * + * [...] + * Actually, a port I/O instruction on most ports in the 0-0x3ff range + * takes almost exactly 1 microsecond, so if you're, for example, using + * the parallel port directly, just do additional inb()s from that port + * to delay. + * [...] + */ +/* transmitter latency 1.5625us 0x1.90 - this figure arrived at from + * comment above plus trimming to match actual measured frequency. + * This will be sensitive to cpu speed, though hopefully most of the 1.5us + * is spent in the uart access. Still - for reference test machine was a + * 1.13GHz Athlon system - Steve + */ + +/* + * changed from 400 to 450 as this works better on slower machines; + * faster machines will use the rdtsc code anyway + */ +#define LIRC_SERIAL_TRANSMITTER_LATENCY 450 + +#else + +/* does anybody have information on other platforms ? */ +/* 256 = 1<<8 */ +#define LIRC_SERIAL_TRANSMITTER_LATENCY 256 + +#endif /* __i386__ */ +/* + * FIXME: should we be using hrtimers instead of this + * LIRC_SERIAL_TRANSMITTER_LATENCY nonsense? + */ + +/* fetch serial input packet (1 byte) from register offset */ +static u8 sinp(int offset) +{ + if (iommap != 0) + /* the register is memory-mapped */ + offset <<= ioshift; + + return inb(io + offset); +} + +/* write serial output packet (1 byte) of value to register offset */ +static void soutp(int offset, u8 value) +{ + if (iommap != 0) + /* the register is memory-mapped */ + offset <<= ioshift; + + outb(value, io + offset); +} + +static void on(void) +{ +#ifdef CONFIG_LIRC_SERIAL_NSLU2 + /* + * On NSLU2, we put the transmit diode between the output of the green + * status LED and ground + */ + if (type == LIRC_NSLU2) { + gpio_line_set(NSLU2_LED_GRN, IXP4XX_GPIO_LOW); + return; + } +#endif + if (txsense) + soutp(UART_MCR, hardware[type].off); + else + soutp(UART_MCR, hardware[type].on); +} + +static void off(void) +{ +#ifdef CONFIG_LIRC_SERIAL_NSLU2 + if (type == LIRC_NSLU2) { + gpio_line_set(NSLU2_LED_GRN, IXP4XX_GPIO_HIGH); + return; + } +#endif + if (txsense) + soutp(UART_MCR, hardware[type].on); + else + soutp(UART_MCR, hardware[type].off); +} + +#ifndef MAX_UDELAY_MS +#define MAX_UDELAY_US 5000 +#else +#define MAX_UDELAY_US (MAX_UDELAY_MS*1000) +#endif + +static void safe_udelay(unsigned long usecs) +{ + while (usecs > MAX_UDELAY_US) { + udelay(MAX_UDELAY_US); + usecs -= MAX_UDELAY_US; + } + udelay(usecs); +} + +#ifdef USE_RDTSC +/* + * This is an overflow/precision juggle, complicated in that we can't + * do long long divide in the kernel + */ + +/* + * When we use the rdtsc instruction to measure clocks, we keep the + * pulse and space widths as clock cycles. As this is CPU speed + * dependent, the widths must be calculated in init_port and ioctl + * time + */ + +/* So send_pulse can quickly convert microseconds to clocks */ +static unsigned long conv_us_to_clocks; + +static int init_timing_params(unsigned int new_duty_cycle, + unsigned int new_freq) +{ + unsigned long long loops_per_sec, work; + + duty_cycle = new_duty_cycle; + freq = new_freq; + + loops_per_sec = current_cpu_data.loops_per_jiffy; + loops_per_sec *= HZ; + + /* How many clocks in a microsecond?, avoiding long long divide */ + work = loops_per_sec; + work *= 4295; /* 4295 = 2^32 / 1e6 */ + conv_us_to_clocks = (work >> 32); + + /* + * Carrier period in clocks, approach good up to 32GHz clock, + * gets carrier frequency within 8Hz + */ + period = loops_per_sec >> 3; + period /= (freq >> 3); + + /* Derive pulse and space from the period */ + pulse_width = period * duty_cycle / 100; + space_width = period - pulse_width; + dprintk("in init_timing_params, freq=%d, duty_cycle=%d, " + "clk/jiffy=%ld, pulse=%ld, space=%ld, " + "conv_us_to_clocks=%ld\n", + freq, duty_cycle, current_cpu_data.loops_per_jiffy, + pulse_width, space_width, conv_us_to_clocks); + return 0; +} +#else /* ! USE_RDTSC */ +static int init_timing_params(unsigned int new_duty_cycle, + unsigned int new_freq) +{ +/* + * period, pulse/space width are kept with 8 binary places - + * IE multiplied by 256. + */ + if (256 * 1000000L / new_freq * new_duty_cycle / 100 <= + LIRC_SERIAL_TRANSMITTER_LATENCY) + return -EINVAL; + if (256 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <= + LIRC_SERIAL_TRANSMITTER_LATENCY) + return -EINVAL; + duty_cycle = new_duty_cycle; + freq = new_freq; + period = 256 * 1000000L / freq; + pulse_width = period * duty_cycle / 100; + space_width = period - pulse_width; + dprintk("in init_timing_params, freq=%d pulse=%ld, " + "space=%ld\n", freq, pulse_width, space_width); + return 0; +} +#endif /* USE_RDTSC */ + + +/* return value: space length delta */ + +static long send_pulse_irdeo(unsigned long length) +{ + long rawbits, ret; + int i; + unsigned char output; + unsigned char chunk, shifted; + + /* how many bits have to be sent ? */ + rawbits = length * 1152 / 10000; + if (duty_cycle > 50) + chunk = 3; + else + chunk = 1; + for (i = 0, output = 0x7f; rawbits > 0; rawbits -= 3) { + shifted = chunk << (i * 3); + shifted >>= 1; + output &= (~shifted); + i++; + if (i == 3) { + soutp(UART_TX, output); + while (!(sinp(UART_LSR) & UART_LSR_THRE)) + ; + output = 0x7f; + i = 0; + } + } + if (i != 0) { + soutp(UART_TX, output); + while (!(sinp(UART_LSR) & UART_LSR_TEMT)) + ; + } + + if (i == 0) + ret = (-rawbits) * 10000 / 1152; + else + ret = (3 - i) * 3 * 10000 / 1152 + (-rawbits) * 10000 / 1152; + + return ret; +} + +#ifdef USE_RDTSC +/* Version that uses Pentium rdtsc instruction to measure clocks */ + +/* + * This version does sub-microsecond timing using rdtsc instruction, + * and does away with the fudged LIRC_SERIAL_TRANSMITTER_LATENCY + * Implicitly i586 architecture... - Steve + */ + +static long send_pulse_homebrew_softcarrier(unsigned long length) +{ + int flag; + unsigned long target, start, now; + + /* Get going quick as we can */ + rdtscl(start); + on(); + /* Convert length from microseconds to clocks */ + length *= conv_us_to_clocks; + /* And loop till time is up - flipping at right intervals */ + now = start; + target = pulse_width; + flag = 1; + /* + * FIXME: This looks like a hard busy wait, without even an occasional, + * polite, cpu_relax() call. There's got to be a better way? + * + * The i2c code has the result of a lot of bit-banging work, I wonder if + * there's something there which could be helpful here. + */ + while ((now - start) < length) { + /* Delay till flip time */ + do { + rdtscl(now); + } while ((now - start) < target); + + /* flip */ + if (flag) { + rdtscl(now); + off(); + target += space_width; + } else { + rdtscl(now); on(); + target += pulse_width; + } + flag = !flag; + } + rdtscl(now); + return ((now - start) - length) / conv_us_to_clocks; +} +#else /* ! USE_RDTSC */ +/* Version using udelay() */ + +/* + * here we use fixed point arithmetic, with 8 + * fractional bits. that gets us within 0.1% or so of the right average + * frequency, albeit with some jitter in pulse length - Steve + */ + +/* To match 8 fractional bits used for pulse/space length */ + +static long send_pulse_homebrew_softcarrier(unsigned long length) +{ + int flag; + unsigned long actual, target, d; + length <<= 8; + + actual = 0; target = 0; flag = 0; + while (actual < length) { + if (flag) { + off(); + target += space_width; + } else { + on(); + target += pulse_width; + } + d = (target - actual - + LIRC_SERIAL_TRANSMITTER_LATENCY + 128) >> 8; + /* + * Note - we've checked in ioctl that the pulse/space + * widths are big enough so that d is > 0 + */ + udelay(d); + actual += (d << 8) + LIRC_SERIAL_TRANSMITTER_LATENCY; + flag = !flag; + } + return (actual-length) >> 8; +} +#endif /* USE_RDTSC */ + +static long send_pulse_homebrew(unsigned long length) +{ + if (length <= 0) + return 0; + + if (softcarrier) + return send_pulse_homebrew_softcarrier(length); + else { + on(); + safe_udelay(length); + return 0; + } +} + +static void send_space_irdeo(long length) +{ + if (length <= 0) + return; + + safe_udelay(length); +} + +static void send_space_homebrew(long length) +{ + off(); + if (length <= 0) + return; + safe_udelay(length); +} + +static void rbwrite(int l) +{ + if (lirc_buffer_full(&rbuf)) { + /* no new signals will be accepted */ + dprintk("Buffer overrun\n"); + return; + } + lirc_buffer_write(&rbuf, (void *)&l); +} + +static void frbwrite(int l) +{ + /* simple noise filter */ + static int pulse, space; + static unsigned int ptr; + + if (ptr > 0 && (l & PULSE_BIT)) { + pulse += l & PULSE_MASK; + if (pulse > 250) { + rbwrite(space); + rbwrite(pulse | PULSE_BIT); + ptr = 0; + pulse = 0; + } + return; + } + if (!(l & PULSE_BIT)) { + if (ptr == 0) { + if (l > 20000) { + space = l; + ptr++; + return; + } + } else { + if (l > 20000) { + space += pulse; + if (space > PULSE_MASK) + space = PULSE_MASK; + space += l; + if (space > PULSE_MASK) + space = PULSE_MASK; + pulse = 0; + return; + } + rbwrite(space); + rbwrite(pulse | PULSE_BIT); + ptr = 0; + pulse = 0; + } + } + rbwrite(l); +} + +static irqreturn_t irq_handler(int i, void *blah) +{ + struct timeval tv; + int counter, dcd; + u8 status; + long deltv; + int data; + static int last_dcd = -1; + + if ((sinp(UART_IIR) & UART_IIR_NO_INT)) { + /* not our interrupt */ + return IRQ_NONE; + } + + counter = 0; + do { + counter++; + status = sinp(UART_MSR); + if (counter > RS_ISR_PASS_LIMIT) { + printk(KERN_WARNING LIRC_DRIVER_NAME ": AIEEEE: " + "We're caught!\n"); + break; + } + if ((status & hardware[type].signal_pin_change) + && sense != -1) { + /* get current time */ + do_gettimeofday(&tv); + + /* New mode, written by Trent Piepho + <xyzzy@u.washington.edu>. */ + + /* + * The old format was not very portable. + * We now use an int to pass pulses + * and spaces to user space. + * + * If PULSE_BIT is set a pulse has been + * received, otherwise a space has been + * received. The driver needs to know if your + * receiver is active high or active low, or + * the space/pulse sense could be + * inverted. The bits denoted by PULSE_MASK are + * the length in microseconds. Lengths greater + * than or equal to 16 seconds are clamped to + * PULSE_MASK. All other bits are unused. + * This is a much simpler interface for user + * programs, as well as eliminating "out of + * phase" errors with space/pulse + * autodetection. + */ + + /* calc time since last interrupt in microseconds */ + dcd = (status & hardware[type].signal_pin) ? 1 : 0; + + if (dcd == last_dcd) { + printk(KERN_WARNING LIRC_DRIVER_NAME + ": ignoring spike: %d %d %lx %lx %lx %lx\n", + dcd, sense, + tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); + continue; + } + + deltv = tv.tv_sec-lasttv.tv_sec; + if (tv.tv_sec < lasttv.tv_sec || + (tv.tv_sec == lasttv.tv_sec && + tv.tv_usec < lasttv.tv_usec)) { + printk(KERN_WARNING LIRC_DRIVER_NAME + ": AIEEEE: your clock just jumped " + "backwards\n"); + printk(KERN_WARNING LIRC_DRIVER_NAME + ": %d %d %lx %lx %lx %lx\n", + dcd, sense, + tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); + data = PULSE_MASK; + } else if (deltv > 15) { + data = PULSE_MASK; /* really long time */ + if (!(dcd^sense)) { + /* sanity check */ + printk(KERN_WARNING LIRC_DRIVER_NAME + ": AIEEEE: " + "%d %d %lx %lx %lx %lx\n", + dcd, sense, + tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); + /* + * detecting pulse while this + * MUST be a space! + */ + sense = sense ? 0 : 1; + } + } else + data = (int) (deltv*1000000 + + tv.tv_usec - + lasttv.tv_usec); + frbwrite(dcd^sense ? data : (data|PULSE_BIT)); + lasttv = tv; + last_dcd = dcd; + wake_up_interruptible(&rbuf.wait_poll); + } + } while (!(sinp(UART_IIR) & UART_IIR_NO_INT)); /* still pending ? */ + return IRQ_HANDLED; +} + + +static int hardware_init_port(void) +{ + u8 scratch, scratch2, scratch3; + + /* + * This is a simple port existence test, borrowed from the autoconfig + * function in drivers/serial/8250.c + */ + scratch = sinp(UART_IER); + soutp(UART_IER, 0); +#ifdef __i386__ + outb(0xff, 0x080); +#endif + scratch2 = sinp(UART_IER) & 0x0f; + soutp(UART_IER, 0x0f); +#ifdef __i386__ + outb(0x00, 0x080); +#endif + scratch3 = sinp(UART_IER) & 0x0f; + soutp(UART_IER, scratch); + if (scratch2 != 0 || scratch3 != 0x0f) { + /* we fail, there's nothing here */ + printk(KERN_ERR LIRC_DRIVER_NAME ": port existence test " + "failed, cannot continue\n"); + return -EINVAL; + } + + + + /* Set DLAB 0. */ + soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); + + /* First of all, disable all interrupts */ + soutp(UART_IER, sinp(UART_IER) & + (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI))); + + /* Clear registers. */ + sinp(UART_LSR); + sinp(UART_RX); + sinp(UART_IIR); + sinp(UART_MSR); + +#ifdef CONFIG_LIRC_SERIAL_NSLU2 + if (type == LIRC_NSLU2) { + /* Setup NSLU2 UART */ + + /* Enable UART */ + soutp(UART_IER, sinp(UART_IER) | UART_IE_IXP42X_UUE); + /* Disable Receiver data Time out interrupt */ + soutp(UART_IER, sinp(UART_IER) & ~UART_IE_IXP42X_RTOIE); + /* set out2 = interrupt unmask; off() doesn't set MCR + on NSLU2 */ + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2); + } +#endif + + /* Set line for power source */ + off(); + + /* Clear registers again to be sure. */ + sinp(UART_LSR); + sinp(UART_RX); + sinp(UART_IIR); + sinp(UART_MSR); + + switch (type) { + case LIRC_IRDEO: + case LIRC_IRDEO_REMOTE: + /* setup port to 7N1 @ 115200 Baud */ + /* 7N1+start = 9 bits at 115200 ~ 3 bits at 38kHz */ + + /* Set DLAB 1. */ + soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB); + /* Set divisor to 1 => 115200 Baud */ + soutp(UART_DLM, 0); + soutp(UART_DLL, 1); + /* Set DLAB 0 + 7N1 */ + soutp(UART_LCR, UART_LCR_WLEN7); + /* THR interrupt already disabled at this point */ + break; + default: + break; + } + + return 0; +} + +static int init_port(void) +{ + int i, nlow, nhigh; + + /* Reserve io region. */ + /* + * Future MMAP-Developers: Attention! + * For memory mapped I/O you *might* need to use ioremap() first, + * for the NSLU2 it's done in boot code. + */ + if (((iommap != 0) + && (request_mem_region(iommap, 8 << ioshift, + LIRC_DRIVER_NAME) == NULL)) + || ((iommap == 0) + && (request_region(io, 8, LIRC_DRIVER_NAME) == NULL))) { + printk(KERN_ERR LIRC_DRIVER_NAME + ": port %04x already in use\n", io); + printk(KERN_WARNING LIRC_DRIVER_NAME + ": use 'setserial /dev/ttySX uart none'\n"); + printk(KERN_WARNING LIRC_DRIVER_NAME + ": or compile the serial port driver as module and\n"); + printk(KERN_WARNING LIRC_DRIVER_NAME + ": make sure this module is loaded first\n"); + return -EBUSY; + } + + if (hardware_init_port() < 0) + return -EINVAL; + + /* Initialize pulse/space widths */ + init_timing_params(duty_cycle, freq); + + /* If pin is high, then this must be an active low receiver. */ + if (sense == -1) { + /* wait 1/2 sec for the power supply */ + msleep(500); + + /* + * probe 9 times every 0.04s, collect "votes" for + * active high/low + */ + nlow = 0; + nhigh = 0; + for (i = 0; i < 9; i++) { + if (sinp(UART_MSR) & hardware[type].signal_pin) + nlow++; + else + nhigh++; + msleep(40); + } + sense = (nlow >= nhigh ? 1 : 0); + printk(KERN_INFO LIRC_DRIVER_NAME ": auto-detected active " + "%s receiver\n", sense ? "low" : "high"); + } else + printk(KERN_INFO LIRC_DRIVER_NAME ": Manually using active " + "%s receiver\n", sense ? "low" : "high"); + + return 0; +} + +static int set_use_inc(void *data) +{ + int result; + unsigned long flags; + + /* initialize timestamp */ + do_gettimeofday(&lasttv); + + result = request_irq(irq, irq_handler, + IRQF_DISABLED | (share_irq ? IRQF_SHARED : 0), + LIRC_DRIVER_NAME, (void *)&hardware); + + switch (result) { + case -EBUSY: + printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", irq); + return -EBUSY; + case -EINVAL: + printk(KERN_ERR LIRC_DRIVER_NAME + ": Bad irq number or handler\n"); + return -EINVAL; + default: + dprintk("Interrupt %d, port %04x obtained\n", irq, io); + break; + }; + + spin_lock_irqsave(&hardware[type].lock, flags); + + /* Set DLAB 0. */ + soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); + + soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI); + + spin_unlock_irqrestore(&hardware[type].lock, flags); + + return 0; +} + +static void set_use_dec(void *data) +{ unsigned long flags; + + spin_lock_irqsave(&hardware[type].lock, flags); + + /* Set DLAB 0. */ + soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); + + /* First of all, disable all interrupts */ + soutp(UART_IER, sinp(UART_IER) & + (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI))); + spin_unlock_irqrestore(&hardware[type].lock, flags); + + free_irq(irq, (void *)&hardware); + + dprintk("freed IRQ %d\n", irq); +} + +static ssize_t lirc_write(struct file *file, const char *buf, + size_t n, loff_t *ppos) +{ + int i, count; + unsigned long flags; + long delta = 0; + int *wbuf; + + if (!(hardware[type].features & LIRC_CAN_SEND_PULSE)) + return -EBADF; + + count = n / sizeof(int); + if (n % sizeof(int) || count % 2 == 0) + return -EINVAL; + wbuf = memdup_user(buf, n); + if (PTR_ERR(wbuf)) + return PTR_ERR(wbuf); + spin_lock_irqsave(&hardware[type].lock, flags); + if (type == LIRC_IRDEO) { + /* DTR, RTS down */ + on(); + } + for (i = 0; i < count; i++) { + if (i%2) + hardware[type].send_space(wbuf[i] - delta); + else + delta = hardware[type].send_pulse(wbuf[i]); + } + off(); + spin_unlock_irqrestore(&hardware[type].lock, flags); + return n; +} + +static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + int result; + unsigned long value; + unsigned int ivalue; + + switch (cmd) { + case LIRC_GET_SEND_MODE: + if (!(hardware[type].features&LIRC_CAN_SEND_MASK)) + return -ENOIOCTLCMD; + + result = put_user(LIRC_SEND2MODE + (hardware[type].features&LIRC_CAN_SEND_MASK), + (unsigned long *) arg); + if (result) + return result; + break; + + case LIRC_SET_SEND_MODE: + if (!(hardware[type].features&LIRC_CAN_SEND_MASK)) + return -ENOIOCTLCMD; + + result = get_user(value, (unsigned long *) arg); + if (result) + return result; + /* only LIRC_MODE_PULSE supported */ + if (value != LIRC_MODE_PULSE) + return -ENOSYS; + break; + + case LIRC_GET_LENGTH: + return -ENOSYS; + break; + + case LIRC_SET_SEND_DUTY_CYCLE: + dprintk("SET_SEND_DUTY_CYCLE\n"); + if (!(hardware[type].features&LIRC_CAN_SET_SEND_DUTY_CYCLE)) + return -ENOIOCTLCMD; + + result = get_user(ivalue, (unsigned int *) arg); + if (result) + return result; + if (ivalue <= 0 || ivalue > 100) + return -EINVAL; + return init_timing_params(ivalue, freq); + break; + + case LIRC_SET_SEND_CARRIER: + dprintk("SET_SEND_CARRIER\n"); + if (!(hardware[type].features&LIRC_CAN_SET_SEND_CARRIER)) + return -ENOIOCTLCMD; + + result = get_user(ivalue, (unsigned int *) arg); + if (result) + return result; + if (ivalue > 500000 || ivalue < 20000) + return -EINVAL; + return init_timing_params(duty_cycle, ivalue); + break; + + default: + return lirc_dev_fop_ioctl(filep, cmd, arg); + } + return 0; +} + +static const struct file_operations lirc_fops = { + .owner = THIS_MODULE, + .write = lirc_write, + .unlocked_ioctl = lirc_ioctl, + .read = lirc_dev_fop_read, + .poll = lirc_dev_fop_poll, + .open = lirc_dev_fop_open, + .release = lirc_dev_fop_close, +}; + +static struct lirc_driver driver = { + .name = LIRC_DRIVER_NAME, + .minor = -1, + .code_length = 1, + .sample_rate = 0, + .data = NULL, + .add_to_buf = NULL, + .rbuf = &rbuf, + .set_use_inc = set_use_inc, + .set_use_dec = set_use_dec, + .fops = &lirc_fops, + .dev = NULL, + .owner = THIS_MODULE, +}; + +static struct platform_device *lirc_serial_dev; + +static int __devinit lirc_serial_probe(struct platform_device *dev) +{ + return 0; +} + +static int __devexit lirc_serial_remove(struct platform_device *dev) +{ + return 0; +} + +static int lirc_serial_suspend(struct platform_device *dev, + pm_message_t state) +{ + /* Set DLAB 0. */ + soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); + + /* Disable all interrupts */ + soutp(UART_IER, sinp(UART_IER) & + (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI))); + + /* Clear registers. */ + sinp(UART_LSR); + sinp(UART_RX); + sinp(UART_IIR); + sinp(UART_MSR); + + return 0; +} + +/* twisty maze... need a forward-declaration here... */ +static void lirc_serial_exit(void); + +static int lirc_serial_resume(struct platform_device *dev) +{ + unsigned long flags; + + if (hardware_init_port() < 0) { + lirc_serial_exit(); + return -EINVAL; + } + + spin_lock_irqsave(&hardware[type].lock, flags); + /* Enable Interrupt */ + do_gettimeofday(&lasttv); + soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI); + off(); + + lirc_buffer_clear(&rbuf); + + spin_unlock_irqrestore(&hardware[type].lock, flags); + + return 0; +} + +static struct platform_driver lirc_serial_driver = { + .probe = lirc_serial_probe, + .remove = __devexit_p(lirc_serial_remove), + .suspend = lirc_serial_suspend, + .resume = lirc_serial_resume, + .driver = { + .name = "lirc_serial", + .owner = THIS_MODULE, + }, +}; + +static int __init lirc_serial_init(void) +{ + int result; + + /* Init read buffer. */ + result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN); + if (result < 0) + return -ENOMEM; + + result = platform_driver_register(&lirc_serial_driver); + if (result) { + printk("lirc register returned %d\n", result); + goto exit_buffer_free; + } + + lirc_serial_dev = platform_device_alloc("lirc_serial", 0); + if (!lirc_serial_dev) { + result = -ENOMEM; + goto exit_driver_unregister; + } + + result = platform_device_add(lirc_serial_dev); + if (result) + goto exit_device_put; + + return 0; + +exit_device_put: + platform_device_put(lirc_serial_dev); +exit_driver_unregister: + platform_driver_unregister(&lirc_serial_driver); +exit_buffer_free: + lirc_buffer_free(&rbuf); + return result; +} + +static void lirc_serial_exit(void) +{ + platform_device_unregister(lirc_serial_dev); + platform_driver_unregister(&lirc_serial_driver); + lirc_buffer_free(&rbuf); +} + +static int __init lirc_serial_init_module(void) +{ + int result; + + result = lirc_serial_init(); + if (result) + return result; + + switch (type) { + case LIRC_HOMEBREW: + case LIRC_IRDEO: + case LIRC_IRDEO_REMOTE: + case LIRC_ANIMAX: + case LIRC_IGOR: + /* if nothing specified, use ttyS0/com1 and irq 4 */ + io = io ? io : 0x3f8; + irq = irq ? irq : 4; + break; +#ifdef CONFIG_LIRC_SERIAL_NSLU2 + case LIRC_NSLU2: + io = io ? io : IRQ_IXP4XX_UART2; + irq = irq ? irq : (IXP4XX_UART2_BASE_VIRT + REG_OFFSET); + iommap = iommap ? iommap : IXP4XX_UART2_BASE_PHYS; + ioshift = ioshift ? ioshift : 2; + break; +#endif + default: + result = -EINVAL; + goto exit_serial_exit; + } + if (!softcarrier) { + switch (type) { + case LIRC_HOMEBREW: + case LIRC_IGOR: +#ifdef CONFIG_LIRC_SERIAL_NSLU2 + case LIRC_NSLU2: +#endif + hardware[type].features &= + ~(LIRC_CAN_SET_SEND_DUTY_CYCLE| + LIRC_CAN_SET_SEND_CARRIER); + break; + } + } + + result = init_port(); + if (result < 0) + goto exit_serial_exit; + driver.features = hardware[type].features; + driver.dev = &lirc_serial_dev->dev; + driver.minor = lirc_register_driver(&driver); + if (driver.minor < 0) { + printk(KERN_ERR LIRC_DRIVER_NAME + ": register_chrdev failed!\n"); + result = -EIO; + goto exit_release; + } + return 0; +exit_release: + release_region(io, 8); +exit_serial_exit: + lirc_serial_exit(); + return result; +} + +static void __exit lirc_serial_exit_module(void) +{ + lirc_serial_exit(); + if (iommap != 0) + release_mem_region(iommap, 8 << ioshift); + else + release_region(io, 8); + lirc_unregister_driver(driver.minor); + dprintk("cleaned up module\n"); +} + + +module_init(lirc_serial_init_module); +module_exit(lirc_serial_exit_module); + +MODULE_DESCRIPTION("Infra-red receiver driver for serial ports."); +MODULE_AUTHOR("Ralph Metzler, Trent Piepho, Ben Pfaff, " + "Christoph Bartelmus, Andrei Tanas"); +MODULE_LICENSE("GPL"); + +module_param(type, int, S_IRUGO); +MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo," + " 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug," + " 5 = NSLU2 RX:CTS2/TX:GreenLED)"); + +module_param(io, int, S_IRUGO); +MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)"); + +/* some architectures (e.g. intel xscale) have memory mapped registers */ +module_param(iommap, bool, S_IRUGO); +MODULE_PARM_DESC(iommap, "physical base for memory mapped I/O" + " (0 = no memory mapped io)"); + +/* + * some architectures (e.g. intel xscale) align the 8bit serial registers + * on 32bit word boundaries. + * See linux-kernel/serial/8250.c serial_in()/out() + */ +module_param(ioshift, int, S_IRUGO); +MODULE_PARM_DESC(ioshift, "shift I/O register offset (0 = no shift)"); + +module_param(irq, int, S_IRUGO); +MODULE_PARM_DESC(irq, "Interrupt (4 or 3)"); + +module_param(share_irq, bool, S_IRUGO); +MODULE_PARM_DESC(share_irq, "Share interrupts (0 = off, 1 = on)"); + +module_param(sense, bool, S_IRUGO); +MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit" + " (0 = active high, 1 = active low )"); + +#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER +module_param(txsense, bool, S_IRUGO); +MODULE_PARM_DESC(txsense, "Sense of transmitter circuit" + " (0 = active high, 1 = active low )"); +#endif + +module_param(softcarrier, bool, S_IRUGO); +MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Enable debugging messages"); diff --git a/drivers/staging/lirc/lirc_sir.c b/drivers/staging/lirc/lirc_sir.c new file mode 100644 index 0000000..eb08fa7 --- /dev/null +++ b/drivers/staging/lirc/lirc_sir.c @@ -0,0 +1,1282 @@ +/* + * LIRC SIR driver, (C) 2000 Milan Pikula <www@fornax.sk> + * + * lirc_sir - Device driver for use with SIR (serial infra red) + * mode of IrDA on many notebooks. + * + * 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 + * + * + * 2000/09/16 Frank Przybylski <mail@frankprzybylski.de> : + * added timeout and relaxed pulse detection, removed gap bug + * + * 2000/12/15 Christoph Bartelmus <lirc@bartelmus.de> : + * added support for Tekram Irmate 210 (sending does not work yet, + * kind of disappointing that nobody was able to implement that + * before), + * major clean-up + * + * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> : + * added support for StrongARM SA1100 embedded microprocessor + * parts cut'n'pasted from sa1100_ir.c (C) 2000 Russell King + */ + +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/fs.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/serial_reg.h> +#include <linux/time.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/wait.h> +#include <linux/mm.h> +#include <linux/delay.h> +#include <linux/poll.h> +#include <asm/system.h> +#include <linux/io.h> +#include <asm/irq.h> +#include <linux/fcntl.h> +#ifdef LIRC_ON_SA1100 +#include <asm/hardware.h> +#ifdef CONFIG_SA1100_COLLIE +#include <asm/arch/tc35143.h> +#include <asm/ucb1200.h> +#endif +#endif + +#include <linux/timer.h> + +#include <media/lirc.h> +#include <media/lirc_dev.h> + +/* SECTION: Definitions */ + +/*** Tekram dongle ***/ +#ifdef LIRC_SIR_TEKRAM +/* stolen from kernel source */ +/* definitions for Tekram dongle */ +#define TEKRAM_115200 0x00 +#define TEKRAM_57600 0x01 +#define TEKRAM_38400 0x02 +#define TEKRAM_19200 0x03 +#define TEKRAM_9600 0x04 +#define TEKRAM_2400 0x08 + +#define TEKRAM_PW 0x10 /* Pulse select bit */ + +/* 10bit * 1s/115200bit in milliseconds = 87ms*/ +#define TIME_CONST (10000000ul/115200ul) + +#endif + +#ifdef LIRC_SIR_ACTISYS_ACT200L +static void init_act200(void); +#elif defined(LIRC_SIR_ACTISYS_ACT220L) +static void init_act220(void); +#endif + +/*** SA1100 ***/ +#ifdef LIRC_ON_SA1100 +struct sa1100_ser2_registers { + /* HSSP control register */ + unsigned char hscr0; + /* UART registers */ + unsigned char utcr0; + unsigned char utcr1; + unsigned char utcr2; + unsigned char utcr3; + unsigned char utcr4; + unsigned char utdr; + unsigned char utsr0; + unsigned char utsr1; +} sr; + +static int irq = IRQ_Ser2ICP; + +#define LIRC_ON_SA1100_TRANSMITTER_LATENCY 0 + +/* pulse/space ratio of 50/50 */ +static unsigned long pulse_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY); +/* 1000000/freq-pulse_width */ +static unsigned long space_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY); +static unsigned int freq = 38000; /* modulation frequency */ +static unsigned int duty_cycle = 50; /* duty cycle of 50% */ + +#endif + +#define RBUF_LEN 1024 +#define WBUF_LEN 1024 + +#define LIRC_DRIVER_NAME "lirc_sir" + +#define PULSE '[' + +#ifndef LIRC_SIR_TEKRAM +/* 9bit * 1s/115200bit in milli seconds = 78.125ms*/ +#define TIME_CONST (9000000ul/115200ul) +#endif + + +/* timeout for sequences in jiffies (=5/100s), must be longer than TIME_CONST */ +#define SIR_TIMEOUT (HZ*5/100) + +#ifndef LIRC_ON_SA1100 +#ifndef LIRC_IRQ +#define LIRC_IRQ 4 +#endif +#ifndef LIRC_PORT +/* for external dongles, default to com1 */ +#if defined(LIRC_SIR_ACTISYS_ACT200L) || \ + defined(LIRC_SIR_ACTISYS_ACT220L) || \ + defined(LIRC_SIR_TEKRAM) +#define LIRC_PORT 0x3f8 +#else +/* onboard sir ports are typically com3 */ +#define LIRC_PORT 0x3e8 +#endif +#endif + +static int io = LIRC_PORT; +static int irq = LIRC_IRQ; +static int threshold = 3; +#endif + +static DEFINE_SPINLOCK(timer_lock); +static struct timer_list timerlist; +/* time of last signal change detected */ +static struct timeval last_tv = {0, 0}; +/* time of last UART data ready interrupt */ +static struct timeval last_intr_tv = {0, 0}; +static int last_value; + +static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue); + +static DEFINE_SPINLOCK(hardware_lock); + +static int rx_buf[RBUF_LEN]; +static unsigned int rx_tail, rx_head; + +static int debug; +#define dprintk(fmt, args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \ + fmt, ## args); \ + } while (0) + +/* SECTION: Prototypes */ + +/* Communication with user-space */ +static unsigned int lirc_poll(struct file *file, poll_table *wait); +static ssize_t lirc_read(struct file *file, char *buf, size_t count, + loff_t *ppos); +static ssize_t lirc_write(struct file *file, const char *buf, size_t n, + loff_t *pos); +static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); +static void add_read_queue(int flag, unsigned long val); +static int init_chrdev(void); +static void drop_chrdev(void); +/* Hardware */ +static irqreturn_t sir_interrupt(int irq, void *dev_id); +static void send_space(unsigned long len); +static void send_pulse(unsigned long len); +static int init_hardware(void); +static void drop_hardware(void); +/* Initialisation */ +static int init_port(void); +static void drop_port(void); + +#ifdef LIRC_ON_SA1100 +static void on(void) +{ + PPSR |= PPC_TXD2; +} + +static void off(void) +{ + PPSR &= ~PPC_TXD2; +} +#else +static inline unsigned int sinp(int offset) +{ + return inb(io + offset); +} + +static inline void soutp(int offset, int value) +{ + outb(value, io + offset); +} +#endif + +#ifndef MAX_UDELAY_MS +#define MAX_UDELAY_US 5000 +#else +#define MAX_UDELAY_US (MAX_UDELAY_MS*1000) +#endif + +static void safe_udelay(unsigned long usecs) +{ + while (usecs > MAX_UDELAY_US) { + udelay(MAX_UDELAY_US); + usecs -= MAX_UDELAY_US; + } + udelay(usecs); +} + +/* SECTION: Communication with user-space */ + +static unsigned int lirc_poll(struct file *file, poll_table *wait) +{ + poll_wait(file, &lirc_read_queue, wait); + if (rx_head != rx_tail) + return POLLIN | POLLRDNORM; + return 0; +} + +static ssize_t lirc_read(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + int n = 0; + int retval = 0; + DECLARE_WAITQUEUE(wait, current); + + if (count % sizeof(int)) + return -EINVAL; + + add_wait_queue(&lirc_read_queue, &wait); + set_current_state(TASK_INTERRUPTIBLE); + while (n < count) { + if (rx_head != rx_tail) { + if (copy_to_user((void *) buf + n, + (void *) (rx_buf + rx_head), + sizeof(int))) { + retval = -EFAULT; + break; + } + rx_head = (rx_head + 1) & (RBUF_LEN - 1); + n += sizeof(int); + } else { + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + } + remove_wait_queue(&lirc_read_queue, &wait); + set_current_state(TASK_RUNNING); + return n ? n : retval; +} +static ssize_t lirc_write(struct file *file, const char *buf, size_t n, + loff_t *pos) +{ + unsigned long flags; + int i, count; + int *tx_buf; + + count = n / sizeof(int); + if (n % sizeof(int) || count % 2 == 0) + return -EINVAL; + tx_buf = memdup_user(buf, n); + if (IS_ERR(tx_buf)) + return PTR_ERR(tx_buf); + i = 0; +#ifdef LIRC_ON_SA1100 + /* disable receiver */ + Ser2UTCR3 = 0; +#endif + local_irq_save(flags); + while (1) { + if (i >= count) + break; + if (tx_buf[i]) + send_pulse(tx_buf[i]); + i++; + if (i >= count) + break; + if (tx_buf[i]) + send_space(tx_buf[i]); + i++; + } + local_irq_restore(flags); +#ifdef LIRC_ON_SA1100 + off(); + udelay(1000); /* wait 1ms for IR diode to recover */ + Ser2UTCR3 = 0; + /* clear status register to prevent unwanted interrupts */ + Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); + /* enable receiver */ + Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE; +#endif + return count; +} + +static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + int retval = 0; + unsigned long value = 0; +#ifdef LIRC_ON_SA1100 + unsigned int ivalue; + + if (cmd == LIRC_GET_FEATURES) + value = LIRC_CAN_SEND_PULSE | + LIRC_CAN_SET_SEND_DUTY_CYCLE | + LIRC_CAN_SET_SEND_CARRIER | + LIRC_CAN_REC_MODE2; + else if (cmd == LIRC_GET_SEND_MODE) + value = LIRC_MODE_PULSE; + else if (cmd == LIRC_GET_REC_MODE) + value = LIRC_MODE_MODE2; +#else + if (cmd == LIRC_GET_FEATURES) + value = LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2; + else if (cmd == LIRC_GET_SEND_MODE) + value = LIRC_MODE_PULSE; + else if (cmd == LIRC_GET_REC_MODE) + value = LIRC_MODE_MODE2; +#endif + + switch (cmd) { + case LIRC_GET_FEATURES: + case LIRC_GET_SEND_MODE: + case LIRC_GET_REC_MODE: + retval = put_user(value, (unsigned long *) arg); + break; + + case LIRC_SET_SEND_MODE: + case LIRC_SET_REC_MODE: + retval = get_user(value, (unsigned long *) arg); + break; +#ifdef LIRC_ON_SA1100 + case LIRC_SET_SEND_DUTY_CYCLE: + retval = get_user(ivalue, (unsigned int *) arg); + if (retval) + return retval; + if (ivalue <= 0 || ivalue > 100) + return -EINVAL; + /* (ivalue/100)*(1000000/freq) */ + duty_cycle = ivalue; + pulse_width = (unsigned long) duty_cycle*10000/freq; + space_width = (unsigned long) 1000000L/freq-pulse_width; + if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY) + pulse_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY; + if (space_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY) + space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY; + break; + case LIRC_SET_SEND_CARRIER: + retval = get_user(ivalue, (unsigned int *) arg); + if (retval) + return retval; + if (ivalue > 500000 || ivalue < 20000) + return -EINVAL; + freq = ivalue; + pulse_width = (unsigned long) duty_cycle*10000/freq; + space_width = (unsigned long) 1000000L/freq-pulse_width; + if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY) + pulse_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY; + if (space_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY) + space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY; + break; +#endif + default: + retval = -ENOIOCTLCMD; + + } + + if (retval) + return retval; + if (cmd == LIRC_SET_REC_MODE) { + if (value != LIRC_MODE_MODE2) + retval = -ENOSYS; + } else if (cmd == LIRC_SET_SEND_MODE) { + if (value != LIRC_MODE_PULSE) + retval = -ENOSYS; + } + + return retval; +} + +static void add_read_queue(int flag, unsigned long val) +{ + unsigned int new_rx_tail; + int newval; + + dprintk("add flag %d with val %lu\n", flag, val); + + newval = val & PULSE_MASK; + + /* + * statistically, pulses are ~TIME_CONST/2 too long. we could + * maybe make this more exact, but this is good enough + */ + if (flag) { + /* pulse */ + if (newval > TIME_CONST/2) + newval -= TIME_CONST/2; + else /* should not ever happen */ + newval = 1; + newval |= PULSE_BIT; + } else { + newval += TIME_CONST/2; + } + new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1); + if (new_rx_tail == rx_head) { + dprintk("Buffer overrun.\n"); + return; + } + rx_buf[rx_tail] = newval; + rx_tail = new_rx_tail; + wake_up_interruptible(&lirc_read_queue); +} + +static const struct file_operations lirc_fops = { + .owner = THIS_MODULE, + .read = lirc_read, + .write = lirc_write, + .poll = lirc_poll, + .unlocked_ioctl = lirc_ioctl, + .open = lirc_dev_fop_open, + .release = lirc_dev_fop_close, +}; + +static int set_use_inc(void *data) +{ + return 0; +} + +static void set_use_dec(void *data) +{ +} + +static struct lirc_driver driver = { + .name = LIRC_DRIVER_NAME, + .minor = -1, + .code_length = 1, + .sample_rate = 0, + .data = NULL, + .add_to_buf = NULL, + .set_use_inc = set_use_inc, + .set_use_dec = set_use_dec, + .fops = &lirc_fops, + .dev = NULL, + .owner = THIS_MODULE, +}; + + +static int init_chrdev(void) +{ + driver.minor = lirc_register_driver(&driver); + if (driver.minor < 0) { + printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n"); + return -EIO; + } + return 0; +} + +static void drop_chrdev(void) +{ + lirc_unregister_driver(driver.minor); +} + +/* SECTION: Hardware */ +static long delta(struct timeval *tv1, struct timeval *tv2) +{ + unsigned long deltv; + + deltv = tv2->tv_sec - tv1->tv_sec; + if (deltv > 15) + deltv = 0xFFFFFF; + else + deltv = deltv*1000000 + + tv2->tv_usec - + tv1->tv_usec; + return deltv; +} + +static void sir_timeout(unsigned long data) +{ + /* + * if last received signal was a pulse, but receiving stopped + * within the 9 bit frame, we need to finish this pulse and + * simulate a signal change to from pulse to space. Otherwise + * upper layers will receive two sequences next time. + */ + + unsigned long flags; + unsigned long pulse_end; + + /* avoid interference with interrupt */ + spin_lock_irqsave(&timer_lock, flags); + if (last_value) { +#ifndef LIRC_ON_SA1100 + /* clear unread bits in UART and restart */ + outb(UART_FCR_CLEAR_RCVR, io + UART_FCR); +#endif + /* determine 'virtual' pulse end: */ + pulse_end = delta(&last_tv, &last_intr_tv); + dprintk("timeout add %d for %lu usec\n", last_value, pulse_end); + add_read_queue(last_value, pulse_end); + last_value = 0; + last_tv = last_intr_tv; + } + spin_unlock_irqrestore(&timer_lock, flags); +} + +static irqreturn_t sir_interrupt(int irq, void *dev_id) +{ + unsigned char data; + struct timeval curr_tv; + static unsigned long deltv; +#ifdef LIRC_ON_SA1100 + int status; + static int n; + + status = Ser2UTSR0; + /* + * Deal with any receive errors first. The bytes in error may be + * the only bytes in the receive FIFO, so we do this first. + */ + while (status & UTSR0_EIF) { + int bstat; + + if (debug) { + dprintk("EIF\n"); + bstat = Ser2UTSR1; + + if (bstat & UTSR1_FRE) + dprintk("frame error\n"); + if (bstat & UTSR1_ROR) + dprintk("receive fifo overrun\n"); + if (bstat & UTSR1_PRE) + dprintk("parity error\n"); + } + + bstat = Ser2UTDR; + n++; + status = Ser2UTSR0; + } + + if (status & (UTSR0_RFS | UTSR0_RID)) { + do_gettimeofday(&curr_tv); + deltv = delta(&last_tv, &curr_tv); + do { + data = Ser2UTDR; + dprintk("%d data: %u\n", n, (unsigned int) data); + n++; + } while (status & UTSR0_RID && /* do not empty fifo in order to + * get UTSR0_RID in any case */ + Ser2UTSR1 & UTSR1_RNE); /* data ready */ + + if (status&UTSR0_RID) { + add_read_queue(0 , deltv - n * TIME_CONST); /*space*/ + add_read_queue(1, n * TIME_CONST); /*pulse*/ + n = 0; + last_tv = curr_tv; + } + } + + if (status & UTSR0_TFS) + printk(KERN_ERR "transmit fifo not full, shouldn't happen\n"); + + /* We must clear certain bits. */ + status &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); + if (status) + Ser2UTSR0 = status; +#else + unsigned long deltintrtv; + unsigned long flags; + int iir, lsr; + + while ((iir = inb(io + UART_IIR) & UART_IIR_ID)) { + switch (iir&UART_IIR_ID) { /* FIXME toto treba preriedit */ + case UART_IIR_MSI: + (void) inb(io + UART_MSR); + break; + case UART_IIR_RLSI: + (void) inb(io + UART_LSR); + break; + case UART_IIR_THRI: +#if 0 + if (lsr & UART_LSR_THRE) /* FIFO is empty */ + outb(data, io + UART_TX) +#endif + break; + case UART_IIR_RDI: + /* avoid interference with timer */ + spin_lock_irqsave(&timer_lock, flags); + do { + del_timer(&timerlist); + data = inb(io + UART_RX); + do_gettimeofday(&curr_tv); + deltv = delta(&last_tv, &curr_tv); + deltintrtv = delta(&last_intr_tv, &curr_tv); + dprintk("t %lu, d %d\n", deltintrtv, (int)data); + /* + * if nothing came in last X cycles, + * it was gap + */ + if (deltintrtv > TIME_CONST * threshold) { + if (last_value) { + dprintk("GAP\n"); + /* simulate signal change */ + add_read_queue(last_value, + deltv - + deltintrtv); + last_value = 0; + last_tv.tv_sec = + last_intr_tv.tv_sec; + last_tv.tv_usec = + last_intr_tv.tv_usec; + deltv = deltintrtv; + } + } + data = 1; + if (data ^ last_value) { + /* + * deltintrtv > 2*TIME_CONST, remember? + * the other case is timeout + */ + add_read_queue(last_value, + deltv-TIME_CONST); + last_value = data; + last_tv = curr_tv; + if (last_tv.tv_usec >= TIME_CONST) { + last_tv.tv_usec -= TIME_CONST; + } else { + last_tv.tv_sec--; + last_tv.tv_usec += 1000000 - + TIME_CONST; + } + } + last_intr_tv = curr_tv; + if (data) { + /* + * start timer for end of + * sequence detection + */ + timerlist.expires = jiffies + + SIR_TIMEOUT; + add_timer(&timerlist); + } + + lsr = inb(io + UART_LSR); + } while (lsr & UART_LSR_DR); /* data ready */ + spin_unlock_irqrestore(&timer_lock, flags); + break; + default: + break; + } + } +#endif + return IRQ_RETVAL(IRQ_HANDLED); +} + +#ifdef LIRC_ON_SA1100 +static void send_pulse(unsigned long length) +{ + unsigned long k, delay; + int flag; + + if (length == 0) + return; + /* + * this won't give us the carrier frequency we really want + * due to integer arithmetic, but we can accept this inaccuracy + */ + + for (k = flag = 0; k < length; k += delay, flag = !flag) { + if (flag) { + off(); + delay = space_width; + } else { + on(); + delay = pulse_width; + } + safe_udelay(delay); + } + off(); +} + +static void send_space(unsigned long length) +{ + if (length == 0) + return; + off(); + safe_udelay(length); +} +#else +static void send_space(unsigned long len) +{ + safe_udelay(len); +} + +static void send_pulse(unsigned long len) +{ + long bytes_out = len / TIME_CONST; + long time_left; + + time_left = (long)len - (long)bytes_out * (long)TIME_CONST; + if (bytes_out == 0) { + bytes_out++; + time_left = 0; + } + while (bytes_out--) { + outb(PULSE, io + UART_TX); + /* FIXME treba seriozne cakanie z char/serial.c */ + while (!(inb(io + UART_LSR) & UART_LSR_THRE)) + ; + } +#if 0 + if (time_left > 0) + safe_udelay(time_left); +#endif +} +#endif + +#ifdef CONFIG_SA1100_COLLIE +static int sa1100_irda_set_power_collie(int state) +{ + if (state) { + /* + * 0 - off + * 1 - short range, lowest power + * 2 - medium range, medium power + * 3 - maximum range, high power + */ + ucb1200_set_io_direction(TC35143_GPIO_IR_ON, + TC35143_IODIR_OUTPUT); + ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_LOW); + udelay(100); + } else { + /* OFF */ + ucb1200_set_io_direction(TC35143_GPIO_IR_ON, + TC35143_IODIR_OUTPUT); + ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_HIGH); + } + return 0; +} +#endif + +static int init_hardware(void) +{ + unsigned long flags; + + spin_lock_irqsave(&hardware_lock, flags); + /* reset UART */ +#ifdef LIRC_ON_SA1100 +#ifdef CONFIG_SA1100_BITSY + if (machine_is_bitsy()) { + printk(KERN_INFO "Power on IR module\n"); + set_bitsy_egpio(EGPIO_BITSY_IR_ON); + } +#endif +#ifdef CONFIG_SA1100_COLLIE + sa1100_irda_set_power_collie(3); /* power on */ +#endif + sr.hscr0 = Ser2HSCR0; + + sr.utcr0 = Ser2UTCR0; + sr.utcr1 = Ser2UTCR1; + sr.utcr2 = Ser2UTCR2; + sr.utcr3 = Ser2UTCR3; + sr.utcr4 = Ser2UTCR4; + + sr.utdr = Ser2UTDR; + sr.utsr0 = Ser2UTSR0; + sr.utsr1 = Ser2UTSR1; + + /* configure GPIO */ + /* output */ + PPDR |= PPC_TXD2; + PSDR |= PPC_TXD2; + /* set output to 0 */ + off(); + + /* Enable HP-SIR modulation, and ensure that the port is disabled. */ + Ser2UTCR3 = 0; + Ser2HSCR0 = sr.hscr0 & (~HSCR0_HSSP); + + /* clear status register to prevent unwanted interrupts */ + Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); + + /* 7N1 */ + Ser2UTCR0 = UTCR0_1StpBit|UTCR0_7BitData; + /* 115200 */ + Ser2UTCR1 = 0; + Ser2UTCR2 = 1; + /* use HPSIR, 1.6 usec pulses */ + Ser2UTCR4 = UTCR4_HPSIR|UTCR4_Z1_6us; + + /* enable receiver, receive fifo interrupt */ + Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE; + + /* clear status register to prevent unwanted interrupts */ + Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); + +#elif defined(LIRC_SIR_TEKRAM) + /* disable FIFO */ + soutp(UART_FCR, + UART_FCR_CLEAR_RCVR| + UART_FCR_CLEAR_XMIT| + UART_FCR_TRIGGER_1); + + /* Set DLAB 0. */ + soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); + + /* First of all, disable all interrupts */ + soutp(UART_IER, sinp(UART_IER) & + (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI))); + + /* Set DLAB 1. */ + soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB); + + /* Set divisor to 12 => 9600 Baud */ + soutp(UART_DLM, 0); + soutp(UART_DLL, 12); + + /* Set DLAB 0. */ + soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); + + /* power supply */ + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); + safe_udelay(50*1000); + + /* -DTR low -> reset PIC */ + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2); + udelay(1*1000); + + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); + udelay(100); + + + /* -RTS low -> send control byte */ + soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2); + udelay(7); + soutp(UART_TX, TEKRAM_115200|TEKRAM_PW); + + /* one byte takes ~1042 usec to transmit at 9600,8N1 */ + udelay(1500); + + /* back to normal operation */ + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); + udelay(50); + + udelay(1500); + + /* read previous control byte */ + printk(KERN_INFO LIRC_DRIVER_NAME + ": 0x%02x\n", sinp(UART_RX)); + + /* Set DLAB 1. */ + soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB); + + /* Set divisor to 1 => 115200 Baud */ + soutp(UART_DLM, 0); + soutp(UART_DLL, 1); + + /* Set DLAB 0, 8 Bit */ + soutp(UART_LCR, UART_LCR_WLEN8); + /* enable interrupts */ + soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI); +#else + outb(0, io + UART_MCR); + outb(0, io + UART_IER); + /* init UART */ + /* set DLAB, speed = 115200 */ + outb(UART_LCR_DLAB | UART_LCR_WLEN7, io + UART_LCR); + outb(1, io + UART_DLL); outb(0, io + UART_DLM); + /* 7N1+start = 9 bits at 115200 ~ 3 bits at 44000 */ + outb(UART_LCR_WLEN7, io + UART_LCR); + /* FIFO operation */ + outb(UART_FCR_ENABLE_FIFO, io + UART_FCR); + /* interrupts */ + /* outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, io + UART_IER); */ + outb(UART_IER_RDI, io + UART_IER); + /* turn on UART */ + outb(UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2, io + UART_MCR); +#ifdef LIRC_SIR_ACTISYS_ACT200L + init_act200(); +#elif defined(LIRC_SIR_ACTISYS_ACT220L) + init_act220(); +#endif +#endif + spin_unlock_irqrestore(&hardware_lock, flags); + return 0; +} + +static void drop_hardware(void) +{ + unsigned long flags; + + spin_lock_irqsave(&hardware_lock, flags); + +#ifdef LIRC_ON_SA1100 + Ser2UTCR3 = 0; + + Ser2UTCR0 = sr.utcr0; + Ser2UTCR1 = sr.utcr1; + Ser2UTCR2 = sr.utcr2; + Ser2UTCR4 = sr.utcr4; + Ser2UTCR3 = sr.utcr3; + + Ser2HSCR0 = sr.hscr0; +#ifdef CONFIG_SA1100_BITSY + if (machine_is_bitsy()) + clr_bitsy_egpio(EGPIO_BITSY_IR_ON); +#endif +#ifdef CONFIG_SA1100_COLLIE + sa1100_irda_set_power_collie(0); /* power off */ +#endif +#else + /* turn off interrupts */ + outb(0, io + UART_IER); +#endif + spin_unlock_irqrestore(&hardware_lock, flags); +} + +/* SECTION: Initialisation */ + +static int init_port(void) +{ + int retval; + + /* get I/O port access and IRQ line */ +#ifndef LIRC_ON_SA1100 + if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) { + printk(KERN_ERR LIRC_DRIVER_NAME + ": i/o port 0x%.4x already in use.\n", io); + return -EBUSY; + } +#endif + retval = request_irq(irq, sir_interrupt, IRQF_DISABLED, + LIRC_DRIVER_NAME, NULL); + if (retval < 0) { +# ifndef LIRC_ON_SA1100 + release_region(io, 8); +# endif + printk(KERN_ERR LIRC_DRIVER_NAME + ": IRQ %d already in use.\n", + irq); + return retval; + } +#ifndef LIRC_ON_SA1100 + printk(KERN_INFO LIRC_DRIVER_NAME + ": I/O port 0x%.4x, IRQ %d.\n", + io, irq); +#endif + + init_timer(&timerlist); + timerlist.function = sir_timeout; + timerlist.data = 0xabadcafe; + + return 0; +} + +static void drop_port(void) +{ + free_irq(irq, NULL); + del_timer_sync(&timerlist); +#ifndef LIRC_ON_SA1100 + release_region(io, 8); +#endif +} + +#ifdef LIRC_SIR_ACTISYS_ACT200L +/* Crystal/Cirrus CS8130 IR transceiver, used in Actisys Act200L dongle */ +/* some code borrowed from Linux IRDA driver */ + +/* Register 0: Control register #1 */ +#define ACT200L_REG0 0x00 +#define ACT200L_TXEN 0x01 /* Enable transmitter */ +#define ACT200L_RXEN 0x02 /* Enable receiver */ +#define ACT200L_ECHO 0x08 /* Echo control chars */ + +/* Register 1: Control register #2 */ +#define ACT200L_REG1 0x10 +#define ACT200L_LODB 0x01 /* Load new baud rate count value */ +#define ACT200L_WIDE 0x04 /* Expand the maximum allowable pulse */ + +/* Register 3: Transmit mode register #2 */ +#define ACT200L_REG3 0x30 +#define ACT200L_B0 0x01 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P) */ +#define ACT200L_B1 0x02 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P) */ +#define ACT200L_CHSY 0x04 /* StartBit Synced 0=bittime, 1=startbit */ + +/* Register 4: Output Power register */ +#define ACT200L_REG4 0x40 +#define ACT200L_OP0 0x01 /* Enable LED1C output */ +#define ACT200L_OP1 0x02 /* Enable LED2C output */ +#define ACT200L_BLKR 0x04 + +/* Register 5: Receive Mode register */ +#define ACT200L_REG5 0x50 +#define ACT200L_RWIDL 0x01 /* fixed 1.6us pulse mode */ + /*.. other various IRDA bit modes, and TV remote modes..*/ + +/* Register 6: Receive Sensitivity register #1 */ +#define ACT200L_REG6 0x60 +#define ACT200L_RS0 0x01 /* receive threshold bit 0 */ +#define ACT200L_RS1 0x02 /* receive threshold bit 1 */ + +/* Register 7: Receive Sensitivity register #2 */ +#define ACT200L_REG7 0x70 +#define ACT200L_ENPOS 0x04 /* Ignore the falling edge */ + +/* Register 8,9: Baud Rate Divider register #1,#2 */ +#define ACT200L_REG8 0x80 +#define ACT200L_REG9 0x90 + +#define ACT200L_2400 0x5f +#define ACT200L_9600 0x17 +#define ACT200L_19200 0x0b +#define ACT200L_38400 0x05 +#define ACT200L_57600 0x03 +#define ACT200L_115200 0x01 + +/* Register 13: Control register #3 */ +#define ACT200L_REG13 0xd0 +#define ACT200L_SHDW 0x01 /* Enable access to shadow registers */ + +/* Register 15: Status register */ +#define ACT200L_REG15 0xf0 + +/* Register 21: Control register #4 */ +#define ACT200L_REG21 0x50 +#define ACT200L_EXCK 0x02 /* Disable clock output driver */ +#define ACT200L_OSCL 0x04 /* oscillator in low power, medium accuracy mode */ + +static void init_act200(void) +{ + int i; + __u8 control[] = { + ACT200L_REG15, + ACT200L_REG13 | ACT200L_SHDW, + ACT200L_REG21 | ACT200L_EXCK | ACT200L_OSCL, + ACT200L_REG13, + ACT200L_REG7 | ACT200L_ENPOS, + ACT200L_REG6 | ACT200L_RS0 | ACT200L_RS1, + ACT200L_REG5 | ACT200L_RWIDL, + ACT200L_REG4 | ACT200L_OP0 | ACT200L_OP1 | ACT200L_BLKR, + ACT200L_REG3 | ACT200L_B0, + ACT200L_REG0 | ACT200L_TXEN | ACT200L_RXEN, + ACT200L_REG8 | (ACT200L_115200 & 0x0f), + ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f), + ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE + }; + + /* Set DLAB 1. */ + soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8); + + /* Set divisor to 12 => 9600 Baud */ + soutp(UART_DLM, 0); + soutp(UART_DLL, 12); + + /* Set DLAB 0. */ + soutp(UART_LCR, UART_LCR_WLEN8); + /* Set divisor to 12 => 9600 Baud */ + + /* power supply */ + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); + for (i = 0; i < 50; i++) + safe_udelay(1000); + + /* Reset the dongle : set RTS low for 25 ms */ + soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2); + for (i = 0; i < 25; i++) + udelay(1000); + + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); + udelay(100); + + /* Clear DTR and set RTS to enter command mode */ + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2); + udelay(7); + + /* send out the control register settings for 115K 7N1 SIR operation */ + for (i = 0; i < sizeof(control); i++) { + soutp(UART_TX, control[i]); + /* one byte takes ~1042 usec to transmit at 9600,8N1 */ + udelay(1500); + } + + /* back to normal operation */ + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); + udelay(50); + + udelay(1500); + soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB); + + /* Set DLAB 1. */ + soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7); + + /* Set divisor to 1 => 115200 Baud */ + soutp(UART_DLM, 0); + soutp(UART_DLL, 1); + + /* Set DLAB 0. */ + soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); + + /* Set DLAB 0, 7 Bit */ + soutp(UART_LCR, UART_LCR_WLEN7); + + /* enable interrupts */ + soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI); +} +#endif + +#ifdef LIRC_SIR_ACTISYS_ACT220L +/* + * Derived from linux IrDA driver (net/irda/actisys.c) + * Drop me a mail for any kind of comment: maxx@spaceboyz.net + */ + +void init_act220(void) +{ + int i; + + /* DLAB 1 */ + soutp(UART_LCR, UART_LCR_DLAB|UART_LCR_WLEN7); + + /* 9600 baud */ + soutp(UART_DLM, 0); + soutp(UART_DLL, 12); + + /* DLAB 0 */ + soutp(UART_LCR, UART_LCR_WLEN7); + + /* reset the dongle, set DTR low for 10us */ + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2); + udelay(10); + + /* back to normal (still 9600) */ + soutp(UART_MCR, UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2); + + /* + * send RTS pulses until we reach 115200 + * i hope this is really the same for act220l/act220l+ + */ + for (i = 0; i < 3; i++) { + udelay(10); + /* set RTS low for 10 us */ + soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2); + udelay(10); + /* set RTS high for 10 us */ + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); + } + + /* back to normal operation */ + udelay(1500); /* better safe than sorry ;) */ + + /* Set DLAB 1. */ + soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7); + + /* Set divisor to 1 => 115200 Baud */ + soutp(UART_DLM, 0); + soutp(UART_DLL, 1); + + /* Set DLAB 0, 7 Bit */ + /* The dongle doesn't seem to have any problems with operation at 7N1 */ + soutp(UART_LCR, UART_LCR_WLEN7); + + /* enable interrupts */ + soutp(UART_IER, UART_IER_RDI); +} +#endif + +static int init_lirc_sir(void) +{ + int retval; + + init_waitqueue_head(&lirc_read_queue); + retval = init_port(); + if (retval < 0) + return retval; + init_hardware(); + printk(KERN_INFO LIRC_DRIVER_NAME + ": Installed.\n"); + return 0; +} + + +static int __init lirc_sir_init(void) +{ + int retval; + + retval = init_chrdev(); + if (retval < 0) + return retval; + retval = init_lirc_sir(); + if (retval) { + drop_chrdev(); + return retval; + } + return 0; +} + +static void __exit lirc_sir_exit(void) +{ + drop_hardware(); + drop_chrdev(); + drop_port(); + printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n"); +} + +module_init(lirc_sir_init); +module_exit(lirc_sir_exit); + +#ifdef LIRC_SIR_TEKRAM +MODULE_DESCRIPTION("Infrared receiver driver for Tekram Irmate 210"); +MODULE_AUTHOR("Christoph Bartelmus"); +#elif defined(LIRC_ON_SA1100) +MODULE_DESCRIPTION("LIRC driver for StrongARM SA1100 embedded microprocessor"); +MODULE_AUTHOR("Christoph Bartelmus"); +#elif defined(LIRC_SIR_ACTISYS_ACT200L) +MODULE_DESCRIPTION("LIRC driver for Actisys Act200L"); +MODULE_AUTHOR("Karl Bongers"); +#elif defined(LIRC_SIR_ACTISYS_ACT220L) +MODULE_DESCRIPTION("LIRC driver for Actisys Act220L(+)"); +MODULE_AUTHOR("Jan Roemisch"); +#else +MODULE_DESCRIPTION("Infrared receiver driver for SIR type serial ports"); +MODULE_AUTHOR("Milan Pikula"); +#endif +MODULE_LICENSE("GPL"); + +#ifdef LIRC_ON_SA1100 +module_param(irq, int, S_IRUGO); +MODULE_PARM_DESC(irq, "Interrupt (16)"); +#else +module_param(io, int, S_IRUGO); +MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)"); + +module_param(irq, int, S_IRUGO); +MODULE_PARM_DESC(irq, "Interrupt (4 or 3)"); + +module_param(threshold, int, S_IRUGO); +MODULE_PARM_DESC(threshold, "space detection threshold (3)"); +#endif + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Enable debugging messages"); diff --git a/drivers/staging/lirc/lirc_streamzap.c b/drivers/staging/lirc/lirc_streamzap.c new file mode 100644 index 0000000..be09c10 --- /dev/null +++ b/drivers/staging/lirc/lirc_streamzap.c @@ -0,0 +1,821 @@ +/* + * Streamzap Remote Control driver + * + * Copyright (c) 2005 Christoph Bartelmus <lirc@bartelmus.de> + * + * This driver was based on the work of Greg Wickham and Adrian + * Dewhurst. It was substantially rewritten to support correct signal + * gaps and now maintains a delay buffer, which is used to present + * consistent timing behaviour to user space applications. Without the + * delay buffer an ugly hack would be required in lircd, which can + * cause sluggish signal decoding in certain situations. + * + * This driver is based on the USB skeleton driver packaged with the + * kernel; copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/smp_lock.h> +#include <linux/completion.h> +#include <linux/uaccess.h> +#include <linux/usb.h> + +#include <media/lirc.h> +#include <media/lirc_dev.h> + +#define DRIVER_VERSION "1.28" +#define DRIVER_NAME "lirc_streamzap" +#define DRIVER_DESC "Streamzap Remote Control driver" + +static int debug; + +#define USB_STREAMZAP_VENDOR_ID 0x0e9c +#define USB_STREAMZAP_PRODUCT_ID 0x0000 + +/* Use our own dbg macro */ +#define dprintk(fmt, args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG DRIVER_NAME "[%d]: " \ + fmt "\n", ## args); \ + } while (0) + +/* table of devices that work with this driver */ +static struct usb_device_id streamzap_table[] = { + /* Streamzap Remote Control */ + { USB_DEVICE(USB_STREAMZAP_VENDOR_ID, USB_STREAMZAP_PRODUCT_ID) }, + /* Terminating entry */ + { } +}; + +MODULE_DEVICE_TABLE(usb, streamzap_table); + +#define STREAMZAP_PULSE_MASK 0xf0 +#define STREAMZAP_SPACE_MASK 0x0f +#define STREAMZAP_TIMEOUT 0xff +#define STREAMZAP_RESOLUTION 256 + +/* number of samples buffered */ +#define STREAMZAP_BUF_LEN 128 + +enum StreamzapDecoderState { + PulseSpace, + FullPulse, + FullSpace, + IgnorePulse +}; + +/* Structure to hold all of our device specific stuff + * + * some remarks regarding locking: + * theoretically this struct can be accessed from three threads: + * + * - from lirc_dev through set_use_inc/set_use_dec + * + * - from the USB layer throuh probe/disconnect/irq + * + * Careful placement of lirc_register_driver/lirc_unregister_driver + * calls will prevent conflicts. lirc_dev makes sure that + * set_use_inc/set_use_dec are not being executed and will not be + * called after lirc_unregister_driver returns. + * + * - by the timer callback + * + * The timer is only running when the device is connected and the + * LIRC device is open. Making sure the timer is deleted by + * set_use_dec will make conflicts impossible. + */ +struct usb_streamzap { + + /* usb */ + /* save off the usb device pointer */ + struct usb_device *udev; + /* the interface for this device */ + struct usb_interface *interface; + + /* buffer & dma */ + unsigned char *buf_in; + dma_addr_t dma_in; + unsigned int buf_in_len; + + struct usb_endpoint_descriptor *endpoint; + + /* IRQ */ + struct urb *urb_in; + + /* lirc */ + struct lirc_driver *driver; + struct lirc_buffer *delay_buf; + + /* timer used to support delay buffering */ + struct timer_list delay_timer; + int timer_running; + spinlock_t timer_lock; + + /* tracks whether we are currently receiving some signal */ + int idle; + /* sum of signal lengths received since signal start */ + unsigned long sum; + /* start time of signal; necessary for gap tracking */ + struct timeval signal_last; + struct timeval signal_start; + enum StreamzapDecoderState decoder_state; + struct timer_list flush_timer; + int flush; + int in_use; + int timeout_enabled; +}; + + +/* local function prototypes */ +static int streamzap_probe(struct usb_interface *interface, + const struct usb_device_id *id); +static void streamzap_disconnect(struct usb_interface *interface); +static void usb_streamzap_irq(struct urb *urb); +static int streamzap_use_inc(void *data); +static void streamzap_use_dec(void *data); +static long streamzap_ioctl(struct file *filep, unsigned int cmd, + unsigned long arg); +static int streamzap_suspend(struct usb_interface *intf, pm_message_t message); +static int streamzap_resume(struct usb_interface *intf); + +/* usb specific object needed to register this driver with the usb subsystem */ + +static struct usb_driver streamzap_driver = { + .name = DRIVER_NAME, + .probe = streamzap_probe, + .disconnect = streamzap_disconnect, + .suspend = streamzap_suspend, + .resume = streamzap_resume, + .id_table = streamzap_table, +}; + +static void stop_timer(struct usb_streamzap *sz) +{ + unsigned long flags; + + spin_lock_irqsave(&sz->timer_lock, flags); + if (sz->timer_running) { + sz->timer_running = 0; + spin_unlock_irqrestore(&sz->timer_lock, flags); + del_timer_sync(&sz->delay_timer); + } else { + spin_unlock_irqrestore(&sz->timer_lock, flags); + } +} + +static void flush_timeout(unsigned long arg) +{ + struct usb_streamzap *sz = (struct usb_streamzap *) arg; + + /* finally start accepting data */ + sz->flush = 0; +} +static void delay_timeout(unsigned long arg) +{ + unsigned long flags; + /* deliver data every 10 ms */ + static unsigned long timer_inc = + (10000/(1000000/HZ)) == 0 ? 1 : (10000/(1000000/HZ)); + struct usb_streamzap *sz = (struct usb_streamzap *) arg; + int data; + + spin_lock_irqsave(&sz->timer_lock, flags); + + if (!lirc_buffer_empty(sz->delay_buf) && + !lirc_buffer_full(sz->driver->rbuf)) { + lirc_buffer_read(sz->delay_buf, (unsigned char *) &data); + lirc_buffer_write(sz->driver->rbuf, (unsigned char *) &data); + } + if (!lirc_buffer_empty(sz->delay_buf)) { + while (lirc_buffer_available(sz->delay_buf) < + STREAMZAP_BUF_LEN / 2 && + !lirc_buffer_full(sz->driver->rbuf)) { + lirc_buffer_read(sz->delay_buf, + (unsigned char *) &data); + lirc_buffer_write(sz->driver->rbuf, + (unsigned char *) &data); + } + if (sz->timer_running) { + sz->delay_timer.expires = jiffies + timer_inc; + add_timer(&sz->delay_timer); + } + } else { + sz->timer_running = 0; + } + + if (!lirc_buffer_empty(sz->driver->rbuf)) + wake_up(&sz->driver->rbuf->wait_poll); + + spin_unlock_irqrestore(&sz->timer_lock, flags); +} + +static void flush_delay_buffer(struct usb_streamzap *sz) +{ + int data; + int empty = 1; + + while (!lirc_buffer_empty(sz->delay_buf)) { + empty = 0; + lirc_buffer_read(sz->delay_buf, (unsigned char *) &data); + if (!lirc_buffer_full(sz->driver->rbuf)) { + lirc_buffer_write(sz->driver->rbuf, + (unsigned char *) &data); + } else { + dprintk("buffer overflow", sz->driver->minor); + } + } + if (!empty) + wake_up(&sz->driver->rbuf->wait_poll); +} + +static void push(struct usb_streamzap *sz, unsigned char *data) +{ + unsigned long flags; + + spin_lock_irqsave(&sz->timer_lock, flags); + if (lirc_buffer_full(sz->delay_buf)) { + int read_data; + + lirc_buffer_read(sz->delay_buf, + (unsigned char *) &read_data); + if (!lirc_buffer_full(sz->driver->rbuf)) { + lirc_buffer_write(sz->driver->rbuf, + (unsigned char *) &read_data); + } else { + dprintk("buffer overflow", sz->driver->minor); + } + } + + lirc_buffer_write(sz->delay_buf, data); + + if (!sz->timer_running) { + sz->delay_timer.expires = jiffies + HZ/10; + add_timer(&sz->delay_timer); + sz->timer_running = 1; + } + + spin_unlock_irqrestore(&sz->timer_lock, flags); +} + +static void push_full_pulse(struct usb_streamzap *sz, + unsigned char value) +{ + int pulse; + + if (sz->idle) { + long deltv; + int tmp; + + sz->signal_last = sz->signal_start; + do_gettimeofday(&sz->signal_start); + + deltv = sz->signal_start.tv_sec-sz->signal_last.tv_sec; + if (deltv > 15) { + /* really long time */ + tmp = LIRC_SPACE(LIRC_VALUE_MASK); + } else { + tmp = (int) (deltv*1000000+ + sz->signal_start.tv_usec - + sz->signal_last.tv_usec); + tmp -= sz->sum; + tmp = LIRC_SPACE(tmp); + } + dprintk("ls %u", sz->driver->minor, tmp); + push(sz, (char *)&tmp); + + sz->idle = 0; + sz->sum = 0; + } + + pulse = ((int) value) * STREAMZAP_RESOLUTION; + pulse += STREAMZAP_RESOLUTION / 2; + sz->sum += pulse; + pulse = LIRC_PULSE(pulse); + + dprintk("p %u", sz->driver->minor, pulse & PULSE_MASK); + push(sz, (char *)&pulse); +} + +static void push_half_pulse(struct usb_streamzap *sz, + unsigned char value) +{ + push_full_pulse(sz, (value & STREAMZAP_PULSE_MASK)>>4); +} + +static void push_full_space(struct usb_streamzap *sz, + unsigned char value) +{ + int space; + + space = ((int) value)*STREAMZAP_RESOLUTION; + space += STREAMZAP_RESOLUTION/2; + sz->sum += space; + space = LIRC_SPACE(space); + dprintk("s %u", sz->driver->minor, space); + push(sz, (char *)&space); +} + +static void push_half_space(struct usb_streamzap *sz, + unsigned char value) +{ + push_full_space(sz, value & STREAMZAP_SPACE_MASK); +} + +/** + * usb_streamzap_irq - IRQ handler + * + * This procedure is invoked on reception of data from + * the usb remote. + */ +static void usb_streamzap_irq(struct urb *urb) +{ + struct usb_streamzap *sz; + int len; + unsigned int i = 0; + + if (!urb) + return; + + sz = urb->context; + len = urb->actual_length; + + switch (urb->status) { + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* + * this urb is terminated, clean up. + * sz might already be invalid at this point + */ + dprintk("urb status: %d", -1, urb->status); + return; + default: + break; + } + + dprintk("received %d", sz->driver->minor, urb->actual_length); + if (!sz->flush) { + for (i = 0; i < urb->actual_length; i++) { + dprintk("%d: %x", sz->driver->minor, + i, (unsigned char) sz->buf_in[i]); + switch (sz->decoder_state) { + case PulseSpace: + if ((sz->buf_in[i]&STREAMZAP_PULSE_MASK) == + STREAMZAP_PULSE_MASK) { + sz->decoder_state = FullPulse; + continue; + } else if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK) + == STREAMZAP_SPACE_MASK) { + push_half_pulse(sz, sz->buf_in[i]); + sz->decoder_state = FullSpace; + continue; + } else { + push_half_pulse(sz, sz->buf_in[i]); + push_half_space(sz, sz->buf_in[i]); + } + break; + case FullPulse: + push_full_pulse(sz, sz->buf_in[i]); + sz->decoder_state = IgnorePulse; + break; + case FullSpace: + if (sz->buf_in[i] == STREAMZAP_TIMEOUT) { + sz->idle = 1; + stop_timer(sz); + if (sz->timeout_enabled) { + int timeout = + LIRC_TIMEOUT + (STREAMZAP_TIMEOUT * + STREAMZAP_RESOLUTION); + push(sz, (char *)&timeout); + } + flush_delay_buffer(sz); + } else + push_full_space(sz, sz->buf_in[i]); + sz->decoder_state = PulseSpace; + break; + case IgnorePulse: + if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK) == + STREAMZAP_SPACE_MASK) { + sz->decoder_state = FullSpace; + continue; + } + push_half_space(sz, sz->buf_in[i]); + sz->decoder_state = PulseSpace; + break; + } + } + } + + usb_submit_urb(urb, GFP_ATOMIC); + + return; +} + +static const struct file_operations streamzap_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = streamzap_ioctl, + .read = lirc_dev_fop_read, + .write = lirc_dev_fop_write, + .poll = lirc_dev_fop_poll, + .open = lirc_dev_fop_open, + .release = lirc_dev_fop_close, +}; + + +/** + * streamzap_probe + * + * Called by usb-core to associated with a candidate device + * On any failure the return value is the ERROR + * On success return 0 + */ +static int streamzap_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct usb_host_interface *iface_host; + struct usb_streamzap *sz; + struct lirc_driver *driver; + struct lirc_buffer *lirc_buf; + struct lirc_buffer *delay_buf; + char buf[63], name[128] = ""; + int retval = -ENOMEM; + int minor = 0; + + /* Allocate space for device driver specific data */ + sz = kzalloc(sizeof(struct usb_streamzap), GFP_KERNEL); + if (sz == NULL) + return -ENOMEM; + + sz->udev = udev; + sz->interface = interface; + + /* Check to ensure endpoint information matches requirements */ + iface_host = interface->cur_altsetting; + + if (iface_host->desc.bNumEndpoints != 1) { + err("%s: Unexpected desc.bNumEndpoints (%d)", __func__, + iface_host->desc.bNumEndpoints); + retval = -ENODEV; + goto free_sz; + } + + sz->endpoint = &(iface_host->endpoint[0].desc); + if ((sz->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + != USB_DIR_IN) { + err("%s: endpoint doesn't match input device 02%02x", + __func__, sz->endpoint->bEndpointAddress); + retval = -ENODEV; + goto free_sz; + } + + if ((sz->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + != USB_ENDPOINT_XFER_INT) { + err("%s: endpoint attributes don't match xfer 02%02x", + __func__, sz->endpoint->bmAttributes); + retval = -ENODEV; + goto free_sz; + } + + if (sz->endpoint->wMaxPacketSize == 0) { + err("%s: endpoint message size==0? ", __func__); + retval = -ENODEV; + goto free_sz; + } + + /* Allocate the USB buffer and IRQ URB */ + + sz->buf_in_len = sz->endpoint->wMaxPacketSize; + sz->buf_in = usb_alloc_coherent(sz->udev, sz->buf_in_len, + GFP_ATOMIC, &sz->dma_in); + if (sz->buf_in == NULL) + goto free_sz; + + sz->urb_in = usb_alloc_urb(0, GFP_KERNEL); + if (sz->urb_in == NULL) + goto free_sz; + + /* Connect this device to the LIRC sub-system */ + driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); + if (!driver) + goto free_sz; + + lirc_buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); + if (!lirc_buf) + goto free_driver; + if (lirc_buffer_init(lirc_buf, sizeof(int), STREAMZAP_BUF_LEN)) + goto kfree_lirc_buf; + + delay_buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); + if (!delay_buf) + goto free_lirc_buf; + if (lirc_buffer_init(delay_buf, sizeof(int), STREAMZAP_BUF_LEN)) + goto kfree_delay_buf; + + sz->driver = driver; + strcpy(sz->driver->name, DRIVER_NAME); + sz->driver->minor = -1; + sz->driver->sample_rate = 0; + sz->driver->code_length = sizeof(int) * 8; + sz->driver->features = LIRC_CAN_REC_MODE2 | + LIRC_CAN_GET_REC_RESOLUTION | + LIRC_CAN_SET_REC_TIMEOUT; + sz->driver->data = sz; + sz->driver->min_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION; + sz->driver->max_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION; + sz->driver->rbuf = lirc_buf; + sz->delay_buf = delay_buf; + sz->driver->set_use_inc = &streamzap_use_inc; + sz->driver->set_use_dec = &streamzap_use_dec; + sz->driver->fops = &streamzap_fops; + sz->driver->dev = &interface->dev; + sz->driver->owner = THIS_MODULE; + + sz->idle = 1; + sz->decoder_state = PulseSpace; + init_timer(&sz->delay_timer); + sz->delay_timer.function = delay_timeout; + sz->delay_timer.data = (unsigned long) sz; + sz->timer_running = 0; + spin_lock_init(&sz->timer_lock); + + init_timer(&sz->flush_timer); + sz->flush_timer.function = flush_timeout; + sz->flush_timer.data = (unsigned long) sz; + /* Complete final initialisations */ + + usb_fill_int_urb(sz->urb_in, udev, + usb_rcvintpipe(udev, sz->endpoint->bEndpointAddress), + sz->buf_in, sz->buf_in_len, usb_streamzap_irq, sz, + sz->endpoint->bInterval); + sz->urb_in->transfer_dma = sz->dma_in; + sz->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + if (udev->descriptor.iManufacturer + && usb_string(udev, udev->descriptor.iManufacturer, + buf, sizeof(buf)) > 0) + strlcpy(name, buf, sizeof(name)); + + if (udev->descriptor.iProduct + && usb_string(udev, udev->descriptor.iProduct, + buf, sizeof(buf)) > 0) + snprintf(name + strlen(name), sizeof(name) - strlen(name), + " %s", buf); + + minor = lirc_register_driver(driver); + + if (minor < 0) + goto free_delay_buf; + + sz->driver->minor = minor; + + usb_set_intfdata(interface, sz); + + printk(KERN_INFO DRIVER_NAME "[%d]: %s on usb%d:%d attached\n", + sz->driver->minor, name, + udev->bus->busnum, sz->udev->devnum); + + return 0; + +free_delay_buf: + lirc_buffer_free(sz->delay_buf); +kfree_delay_buf: + kfree(delay_buf); +free_lirc_buf: + lirc_buffer_free(sz->driver->rbuf); +kfree_lirc_buf: + kfree(lirc_buf); +free_driver: + kfree(driver); +free_sz: + if (retval == -ENOMEM) + err("Out of memory"); + + if (sz) { + usb_free_urb(sz->urb_in); + usb_free_coherent(udev, sz->buf_in_len, sz->buf_in, sz->dma_in); + kfree(sz); + } + + return retval; +} + +static int streamzap_use_inc(void *data) +{ + struct usb_streamzap *sz = data; + + if (!sz) { + dprintk("%s called with no context", -1, __func__); + return -EINVAL; + } + dprintk("set use inc", sz->driver->minor); + + lirc_buffer_clear(sz->driver->rbuf); + lirc_buffer_clear(sz->delay_buf); + + sz->flush_timer.expires = jiffies + HZ; + sz->flush = 1; + add_timer(&sz->flush_timer); + + sz->urb_in->dev = sz->udev; + if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) { + dprintk("open result = -EIO error submitting urb", + sz->driver->minor); + return -EIO; + } + sz->in_use++; + + return 0; +} + +static void streamzap_use_dec(void *data) +{ + struct usb_streamzap *sz = data; + + if (!sz) { + dprintk("%s called with no context", -1, __func__); + return; + } + dprintk("set use dec", sz->driver->minor); + + if (sz->flush) { + sz->flush = 0; + del_timer_sync(&sz->flush_timer); + } + + usb_kill_urb(sz->urb_in); + + stop_timer(sz); + + sz->in_use--; +} + +static long streamzap_ioctl(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + int result = 0; + int val; + struct usb_streamzap *sz = lirc_get_pdata(filep); + + switch (cmd) { + case LIRC_GET_REC_RESOLUTION: + result = put_user(STREAMZAP_RESOLUTION, (unsigned int *) arg); + break; + case LIRC_SET_REC_TIMEOUT: + result = get_user(val, (int *)arg); + if (result == 0) { + if (val == STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION) + sz->timeout_enabled = 1; + else if (val == 0) + sz->timeout_enabled = 0; + else + result = -EINVAL; + } + break; + default: + return lirc_dev_fop_ioctl(filep, cmd, arg); + } + return result; +} + +/** + * streamzap_disconnect + * + * Called by the usb core when the device is removed from the system. + * + * This routine guarantees that the driver will not submit any more urbs + * by clearing dev->udev. It is also supposed to terminate any currently + * active urbs. Unfortunately, usb_bulk_msg(), used in streamzap_read(), + * does not provide any way to do this. + */ +static void streamzap_disconnect(struct usb_interface *interface) +{ + struct usb_streamzap *sz; + int errnum; + int minor; + + sz = usb_get_intfdata(interface); + + /* unregister from the LIRC sub-system */ + + errnum = lirc_unregister_driver(sz->driver->minor); + if (errnum != 0) + dprintk("error in lirc_unregister: (returned %d)", + sz->driver->minor, errnum); + + lirc_buffer_free(sz->delay_buf); + lirc_buffer_free(sz->driver->rbuf); + + /* unregister from the USB sub-system */ + + usb_free_urb(sz->urb_in); + + usb_free_coherent(sz->udev, sz->buf_in_len, sz->buf_in, sz->dma_in); + + minor = sz->driver->minor; + kfree(sz->driver->rbuf); + kfree(sz->driver); + kfree(sz->delay_buf); + kfree(sz); + + printk(KERN_INFO DRIVER_NAME "[%d]: disconnected\n", minor); +} + +static int streamzap_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct usb_streamzap *sz = usb_get_intfdata(intf); + + printk(KERN_INFO DRIVER_NAME "[%d]: suspend\n", sz->driver->minor); + if (sz->in_use) { + if (sz->flush) { + sz->flush = 0; + del_timer_sync(&sz->flush_timer); + } + + stop_timer(sz); + + usb_kill_urb(sz->urb_in); + } + return 0; +} + +static int streamzap_resume(struct usb_interface *intf) +{ + struct usb_streamzap *sz = usb_get_intfdata(intf); + + lirc_buffer_clear(sz->driver->rbuf); + lirc_buffer_clear(sz->delay_buf); + + if (sz->in_use) { + sz->flush_timer.expires = jiffies + HZ; + sz->flush = 1; + add_timer(&sz->flush_timer); + + sz->urb_in->dev = sz->udev; + if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) { + dprintk("open result = -EIO error submitting urb", + sz->driver->minor); + return -EIO; + } + } + return 0; +} + +/** + * usb_streamzap_init + */ +static int __init usb_streamzap_init(void) +{ + int result; + + /* register this driver with the USB subsystem */ + result = usb_register(&streamzap_driver); + + if (result) { + err("usb_register failed. Error number %d", + result); + return result; + } + + printk(KERN_INFO DRIVER_NAME " " DRIVER_VERSION " registered\n"); + return 0; +} + +/** + * usb_streamzap_exit + */ +static void __exit usb_streamzap_exit(void) +{ + usb_deregister(&streamzap_driver); +} + + +module_init(usb_streamzap_init); +module_exit(usb_streamzap_exit); + +MODULE_AUTHOR("Christoph Bartelmus, Greg Wickham, Adrian Dewhurst"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Enable debugging messages"); diff --git a/drivers/staging/lirc/lirc_ttusbir.c b/drivers/staging/lirc/lirc_ttusbir.c new file mode 100644 index 0000000..e345ab9 --- /dev/null +++ b/drivers/staging/lirc/lirc_ttusbir.c @@ -0,0 +1,396 @@ +/* + * lirc_ttusbir.c + * + * lirc_ttusbir - LIRC device driver for the TechnoTrend USB IR Receiver + * + * Copyright (C) 2007 Stefan Macher <st_maker-lirc@yahoo.de> + * + * This LIRC driver provides access to the TechnoTrend USB IR Receiver. + * The receiver delivers the IR signal as raw sampled true/false data in + * isochronous USB packets each of size 128 byte. + * Currently the driver reduces the sampling rate by factor of 8 as this + * is still more than enough to decode RC-5 - others should be analyzed. + * But the driver does not rely on RC-5 it should be able to decode every + * IR signal that is not too fast. + */ + +/* + * 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/version.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/usb.h> + +#include <media/lirc.h> +#include <media/lirc_dev.h> + +MODULE_DESCRIPTION("TechnoTrend USB IR device driver for LIRC"); +MODULE_AUTHOR("Stefan Macher (st_maker-lirc@yahoo.de)"); +MODULE_LICENSE("GPL"); + +/* #define DEBUG */ +#ifdef DEBUG +#define DPRINTK printk +#else +#define DPRINTK(_x_, a...) +#endif + +/* function declarations */ +static int probe(struct usb_interface *intf, const struct usb_device_id *id); +static void disconnect(struct usb_interface *intf); +static void urb_complete(struct urb *urb); +static int set_use_inc(void *data); +static void set_use_dec(void *data); + +static int num_urbs = 2; +module_param(num_urbs, int, S_IRUGO); +MODULE_PARM_DESC(num_urbs, + "Number of URBs in queue. Try to increase to 4 in case " + "of problems (default: 2; minimum: 2)"); + +/* table of devices that work with this driver */ +static struct usb_device_id device_id_table[] = { + /* TechnoTrend USB IR Receiver */ + { USB_DEVICE(0x0B48, 0x2003) }, + /* Terminating entry */ + { } +}; +MODULE_DEVICE_TABLE(usb, device_id_table); + +/* USB driver definition */ +static struct usb_driver usb_driver = { + .name = "TTUSBIR", + .id_table = &(device_id_table[0]), + .probe = probe, + .disconnect = disconnect, +}; + +/* USB device definition */ +struct ttusbir_device { + struct usb_driver *usb_driver; + struct usb_device *udev; + struct usb_interface *interf; + struct usb_class_driver class_driver; + unsigned int ifnum; /* Interface number to use */ + unsigned int alt_setting; /* alternate setting to use */ + unsigned int endpoint; /* Endpoint to use */ + struct urb **urb; /* num_urb URB pointers*/ + char **buffer; /* 128 byte buffer for each URB */ + struct lirc_buffer rbuf; /* Buffer towards LIRC */ + struct lirc_driver driver; + int minor; + int last_pulse; /* remembers if last received byte was pulse or space */ + int last_num; /* remembers how many last bytes appeared */ + int opened; +}; + +/*** LIRC specific functions ***/ +static int set_use_inc(void *data) +{ + int i, retval; + struct ttusbir_device *ttusbir = data; + + DPRINTK("Sending first URBs\n"); + /* @TODO Do I need to check if I am already opened */ + ttusbir->opened = 1; + + for (i = 0; i < num_urbs; i++) { + retval = usb_submit_urb(ttusbir->urb[i], GFP_KERNEL); + if (retval) { + err("%s: usb_submit_urb failed on urb %d", + __func__, i); + return retval; + } + } + return 0; +} + +static void set_use_dec(void *data) +{ + struct ttusbir_device *ttusbir = data; + + DPRINTK("Device closed\n"); + + ttusbir->opened = 0; +} + +/*** USB specific functions ***/ + +/* + * This mapping table is used to do a very simple filtering of the + * input signal. + * For a value with at least 4 bits set it returns 0xFF otherwise + * 0x00. For faster IR signals this can not be used. But for RC-5 we + * still have about 14 samples per pulse/space, i.e. we sample with 14 + * times higher frequency than the signal frequency + */ +const unsigned char map_table[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +static void urb_complete(struct urb *urb) +{ + struct ttusbir_device *ttusbir; + unsigned char *buf; + int i; + int l; + + ttusbir = urb->context; + + if (!ttusbir->opened) + return; + + buf = (unsigned char *)urb->transfer_buffer; + + for (i = 0; i < 128; i++) { + /* Here we do the filtering and some kind of down sampling */ + buf[i] = ~map_table[buf[i]]; + if (ttusbir->last_pulse == buf[i]) { + if (ttusbir->last_num < PULSE_MASK/63) + ttusbir->last_num++; + /* + * else we are in a idle period and do not need to + * increment any longer + */ + } else { + l = ttusbir->last_num * 62; /* about 62 = us/byte */ + if (ttusbir->last_pulse) /* pulse or space? */ + l |= PULSE_BIT; + if (!lirc_buffer_full(&ttusbir->rbuf)) { + lirc_buffer_write(&ttusbir->rbuf, (void *)&l); + wake_up_interruptible(&ttusbir->rbuf.wait_poll); + } + ttusbir->last_num = 0; + ttusbir->last_pulse = buf[i]; + } + } + usb_submit_urb(urb, GFP_ATOMIC); /* keep data rolling :-) */ +} + +/* + * Called whenever the USB subsystem thinks we could be the right driver + * to handle this device + */ +static int probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + int alt_set, endp; + int found = 0; + int i, j; + int struct_size; + struct usb_host_interface *host_interf; + struct usb_interface_descriptor *interf_desc; + struct usb_host_endpoint *host_endpoint; + struct ttusbir_device *ttusbir; + + DPRINTK("Module ttusbir probe\n"); + + /* To reduce memory fragmentation we use only one allocation */ + struct_size = sizeof(struct ttusbir_device) + + (sizeof(struct urb *) * num_urbs) + + (sizeof(char *) * num_urbs) + + (num_urbs * 128); + ttusbir = kzalloc(struct_size, GFP_KERNEL); + if (!ttusbir) + return -ENOMEM; + + ttusbir->urb = (struct urb **)((char *)ttusbir + + sizeof(struct ttusbir_device)); + ttusbir->buffer = (char **)((char *)ttusbir->urb + + (sizeof(struct urb *) * num_urbs)); + for (i = 0; i < num_urbs; i++) + ttusbir->buffer[i] = (char *)ttusbir->buffer + + (sizeof(char *)*num_urbs) + (i * 128); + + ttusbir->usb_driver = &usb_driver; + ttusbir->alt_setting = -1; + /* @TODO check if error can be returned */ + ttusbir->udev = usb_get_dev(interface_to_usbdev(intf)); + ttusbir->interf = intf; + ttusbir->last_pulse = 0x00; + ttusbir->last_num = 0; + + /* + * Now look for interface setting we can handle + * We are searching for the alt setting where end point + * 0x82 has max packet size 16 + */ + for (alt_set = 0; alt_set < intf->num_altsetting && !found; alt_set++) { + host_interf = &intf->altsetting[alt_set]; + interf_desc = &host_interf->desc; + for (endp = 0; endp < interf_desc->bNumEndpoints; endp++) { + host_endpoint = &host_interf->endpoint[endp]; + if ((host_endpoint->desc.bEndpointAddress == 0x82) && + (host_endpoint->desc.wMaxPacketSize == 0x10)) { + ttusbir->alt_setting = alt_set; + ttusbir->endpoint = endp; + found = 1; + break; + } + } + } + if (ttusbir->alt_setting != -1) + DPRINTK("alt setting: %d\n", ttusbir->alt_setting); + else { + err("Could not find alternate setting\n"); + kfree(ttusbir); + return -EINVAL; + } + + /* OK lets setup this interface setting */ + usb_set_interface(ttusbir->udev, 0, ttusbir->alt_setting); + + /* Store device info in interface structure */ + usb_set_intfdata(intf, ttusbir); + + /* Register as a LIRC driver */ + if (lirc_buffer_init(&ttusbir->rbuf, sizeof(int), 256) < 0) { + err("Could not get memory for LIRC data buffer\n"); + usb_set_intfdata(intf, NULL); + kfree(ttusbir); + return -ENOMEM; + } + strcpy(ttusbir->driver.name, "TTUSBIR"); + ttusbir->driver.minor = -1; + ttusbir->driver.code_length = 1; + ttusbir->driver.sample_rate = 0; + ttusbir->driver.data = ttusbir; + ttusbir->driver.add_to_buf = NULL; + ttusbir->driver.rbuf = &ttusbir->rbuf; + ttusbir->driver.set_use_inc = set_use_inc; + ttusbir->driver.set_use_dec = set_use_dec; + ttusbir->driver.dev = &intf->dev; + ttusbir->driver.owner = THIS_MODULE; + ttusbir->driver.features = LIRC_CAN_REC_MODE2; + ttusbir->minor = lirc_register_driver(&ttusbir->driver); + if (ttusbir->minor < 0) { + err("Error registering as LIRC driver\n"); + usb_set_intfdata(intf, NULL); + lirc_buffer_free(&ttusbir->rbuf); + kfree(ttusbir); + return -EIO; + } + + /* Allocate and setup the URB that we will use to talk to the device */ + for (i = 0; i < num_urbs; i++) { + ttusbir->urb[i] = usb_alloc_urb(8, GFP_KERNEL); + if (!ttusbir->urb[i]) { + err("Could not allocate memory for the URB\n"); + for (j = i - 1; j >= 0; j--) + kfree(ttusbir->urb[j]); + lirc_buffer_free(&ttusbir->rbuf); + lirc_unregister_driver(ttusbir->minor); + kfree(ttusbir); + usb_set_intfdata(intf, NULL); + return -ENOMEM; + } + ttusbir->urb[i]->dev = ttusbir->udev; + ttusbir->urb[i]->context = ttusbir; + ttusbir->urb[i]->pipe = usb_rcvisocpipe(ttusbir->udev, + ttusbir->endpoint); + ttusbir->urb[i]->interval = 1; + ttusbir->urb[i]->transfer_flags = URB_ISO_ASAP; + ttusbir->urb[i]->transfer_buffer = &ttusbir->buffer[i][0]; + ttusbir->urb[i]->complete = urb_complete; + ttusbir->urb[i]->number_of_packets = 8; + ttusbir->urb[i]->transfer_buffer_length = 128; + for (j = 0; j < 8; j++) { + ttusbir->urb[i]->iso_frame_desc[j].offset = j*16; + ttusbir->urb[i]->iso_frame_desc[j].length = 16; + } + } + return 0; +} + +/** + * Called when the driver is unloaded or the device is unplugged + */ +static void disconnect(struct usb_interface *intf) +{ + int i; + struct ttusbir_device *ttusbir; + + DPRINTK("Module ttusbir disconnect\n"); + + ttusbir = (struct ttusbir_device *) usb_get_intfdata(intf); + usb_set_intfdata(intf, NULL); + lirc_unregister_driver(ttusbir->minor); + DPRINTK("unregistered\n"); + + for (i = 0; i < num_urbs; i++) { + usb_kill_urb(ttusbir->urb[i]); + usb_free_urb(ttusbir->urb[i]); + } + DPRINTK("URBs killed\n"); + lirc_buffer_free(&ttusbir->rbuf); + kfree(ttusbir); +} + +static int ttusbir_init_module(void) +{ + int result; + + DPRINTK(KERN_DEBUG "Module ttusbir init\n"); + + /* register this driver with the USB subsystem */ + result = usb_register(&usb_driver); + if (result) + err("usb_register failed. Error number %d", result); + return result; +} + +static void ttusbir_exit_module(void) +{ + printk(KERN_DEBUG "Module ttusbir exit\n"); + usb_deregister(&usb_driver); +} + +module_init(ttusbir_init_module); +module_exit(ttusbir_exit_module); diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c new file mode 100644 index 0000000..100caab1 --- /dev/null +++ b/drivers/staging/lirc/lirc_zilog.c @@ -0,0 +1,1387 @@ +/* + * i2c IR lirc driver for devices with zilog IR processors + * + * Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de> + * modified for PixelView (BT878P+W/FM) by + * Michal Kochanowicz <mkochano@pld.org.pl> + * Christoph Bartelmus <lirc@bartelmus.de> + * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by + * Ulrich Mueller <ulrich.mueller42@web.de> + * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by + * Stefan Jahn <stefan@lkcc.org> + * modified for inclusion into kernel sources by + * Jerome Brock <jbrock@users.sourceforge.net> + * modified for Leadtek Winfast PVR2000 by + * Thomas Reitmayr (treitmayr@yahoo.com) + * modified for Hauppauge PVR-150 IR TX device by + * Mark Weaver <mark@npsl.co.uk> + * changed name from lirc_pvr150 to lirc_zilog, works on more than pvr-150 + * Jarod Wilson <jarod@redhat.com> + * + * parts are cut&pasted from the lirc_i2c.c 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/kmod.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/poll.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/delay.h> +#include <linux/completion.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/firmware.h> +#include <linux/vmalloc.h> + +#include <linux/mutex.h> +#include <linux/kthread.h> + +#include <media/lirc_dev.h> +#include <media/lirc.h> + +struct IR { + struct lirc_driver l; + + /* Device info */ + struct mutex ir_lock; + int open; + + /* RX device */ + struct i2c_client c_rx; + int have_rx; + + /* RX device buffer & lock */ + struct lirc_buffer buf; + struct mutex buf_lock; + + /* RX polling thread data */ + struct completion *t_notify; + struct completion *t_notify2; + int shutdown; + struct task_struct *task; + + /* RX read data */ + unsigned char b[3]; + + /* TX device */ + struct i2c_client c_tx; + int need_boot; + int have_tx; +}; + +/* Minor -> data mapping */ +static struct IR *ir_devices[MAX_IRCTL_DEVICES]; + +/* Block size for IR transmitter */ +#define TX_BLOCK_SIZE 99 + +/* Hauppauge IR transmitter data */ +struct tx_data_struct { + /* Boot block */ + unsigned char *boot_data; + + /* Start of binary data block */ + unsigned char *datap; + + /* End of binary data block */ + unsigned char *endp; + + /* Number of installed codesets */ + unsigned int num_code_sets; + + /* Pointers to codesets */ + unsigned char **code_sets; + + /* Global fixed data template */ + int fixed[TX_BLOCK_SIZE]; +}; + +static struct tx_data_struct *tx_data; +static struct mutex tx_data_lock; + +#define zilog_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, \ + ## args) +#define zilog_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args) + +#define ZILOG_HAUPPAUGE_IR_RX_NAME "Zilog/Hauppauge IR RX" +#define ZILOG_HAUPPAUGE_IR_TX_NAME "Zilog/Hauppauge IR TX" + +/* module parameters */ +static int debug; /* debug output */ +static int disable_rx; /* disable RX device */ +static int disable_tx; /* disable TX device */ +static int minor = -1; /* minor number */ + +#define dprintk(fmt, args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG KBUILD_MODNAME ": " fmt, \ + ## args); \ + } while (0) + +static int add_to_buf(struct IR *ir) +{ + __u16 code; + unsigned char codes[2]; + unsigned char keybuf[6]; + int got_data = 0; + int ret; + int failures = 0; + unsigned char sendbuf[1] = { 0 }; + + if (lirc_buffer_full(&ir->buf)) { + dprintk("buffer overflow\n"); + return -EOVERFLOW; + } + + /* + * service the device as long as it is returning + * data and we have space + */ + do { + /* + * Lock i2c bus for the duration. RX/TX chips interfere so + * this is worth it + */ + mutex_lock(&ir->ir_lock); + + /* + * Send random "poll command" (?) Windows driver does this + * and it is a good point to detect chip failure. + */ + ret = i2c_master_send(&ir->c_rx, sendbuf, 1); + if (ret != 1) { + zilog_error("i2c_master_send failed with %d\n", ret); + if (failures >= 3) { + mutex_unlock(&ir->ir_lock); + zilog_error("unable to read from the IR chip " + "after 3 resets, giving up\n"); + return ret; + } + + /* Looks like the chip crashed, reset it */ + zilog_error("polling the IR receiver chip failed, " + "trying reset\n"); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((100 * HZ + 999) / 1000); + ir->need_boot = 1; + + ++failures; + mutex_unlock(&ir->ir_lock); + continue; + } + + ret = i2c_master_recv(&ir->c_rx, keybuf, sizeof(keybuf)); + mutex_unlock(&ir->ir_lock); + if (ret != sizeof(keybuf)) { + zilog_error("i2c_master_recv failed with %d -- " + "keeping last read buffer\n", ret); + } else { + ir->b[0] = keybuf[3]; + ir->b[1] = keybuf[4]; + ir->b[2] = keybuf[5]; + dprintk("key (0x%02x/0x%02x)\n", ir->b[0], ir->b[1]); + } + + /* key pressed ? */ +#ifdef I2C_HW_B_HDPVR + if (ir->c_rx.adapter->id == I2C_HW_B_HDPVR) { + if (got_data && (keybuf[0] == 0x80)) + return 0; + else if (got_data && (keybuf[0] == 0x00)) + return -ENODATA; + } else if ((ir->b[0] & 0x80) == 0) +#else + if ((ir->b[0] & 0x80) == 0) +#endif + return got_data ? 0 : -ENODATA; + + /* look what we have */ + code = (((__u16)ir->b[0] & 0x7f) << 6) | (ir->b[1] >> 2); + + codes[0] = (code >> 8) & 0xff; + codes[1] = code & 0xff; + + /* return it */ + lirc_buffer_write(&ir->buf, codes); + ++got_data; + } while (!lirc_buffer_full(&ir->buf)); + + return 0; +} + +/* + * Main function of the polling thread -- from lirc_dev. + * We don't fit the LIRC model at all anymore. This is horrible, but + * basically we have a single RX/TX device with a nasty failure mode + * that needs to be accounted for across the pair. lirc lets us provide + * fops, but prevents us from using the internal polling, etc. if we do + * so. Hence the replication. Might be neater to extend the LIRC model + * to account for this but I'd think it's a very special case of seriously + * messed up hardware. + */ +static int lirc_thread(void *arg) +{ + struct IR *ir = arg; + + if (ir->t_notify != NULL) + complete(ir->t_notify); + + dprintk("poll thread started\n"); + + do { + if (ir->open) { + set_current_state(TASK_INTERRUPTIBLE); + + /* + * This is ~113*2 + 24 + jitter (2*repeat gap + + * code length). We use this interval as the chip + * resets every time you poll it (bad!). This is + * therefore just sufficient to catch all of the + * button presses. It makes the remote much more + * responsive. You can see the difference by + * running irw and holding down a button. With + * 100ms, the old polling interval, you'll notice + * breaks in the repeat sequence corresponding to + * lost keypresses. + */ + schedule_timeout((260 * HZ) / 1000); + if (ir->shutdown) + break; + if (!add_to_buf(ir)) + wake_up_interruptible(&ir->buf.wait_poll); + } else { + /* if device not opened so we can sleep half a second */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/2); + } + } while (!ir->shutdown); + + if (ir->t_notify2 != NULL) + wait_for_completion(ir->t_notify2); + + ir->task = NULL; + if (ir->t_notify != NULL) + complete(ir->t_notify); + + dprintk("poll thread ended\n"); + return 0; +} + +static int set_use_inc(void *data) +{ + struct IR *ir = data; + + if (ir->l.owner == NULL || try_module_get(ir->l.owner) == 0) + return -ENODEV; + + /* lock bttv in memory while /dev/lirc is in use */ + /* + * this is completely broken code. lirc_unregister_driver() + * must be possible even when the device is open + */ + if (ir->c_rx.addr) + i2c_use_client(&ir->c_rx); + if (ir->c_tx.addr) + i2c_use_client(&ir->c_tx); + + return 0; +} + +static void set_use_dec(void *data) +{ + struct IR *ir = data; + + if (ir->c_rx.addr) + i2c_release_client(&ir->c_rx); + if (ir->c_tx.addr) + i2c_release_client(&ir->c_tx); + if (ir->l.owner != NULL) + module_put(ir->l.owner); +} + +/* safe read of a uint32 (always network byte order) */ +static int read_uint32(unsigned char **data, + unsigned char *endp, unsigned int *val) +{ + if (*data + 4 > endp) + return 0; + *val = ((*data)[0] << 24) | ((*data)[1] << 16) | + ((*data)[2] << 8) | (*data)[3]; + *data += 4; + return 1; +} + +/* safe read of a uint8 */ +static int read_uint8(unsigned char **data, + unsigned char *endp, unsigned char *val) +{ + if (*data + 1 > endp) + return 0; + *val = *((*data)++); + return 1; +} + +/* safe skipping of N bytes */ +static int skip(unsigned char **data, + unsigned char *endp, unsigned int distance) +{ + if (*data + distance > endp) + return 0; + *data += distance; + return 1; +} + +/* decompress key data into the given buffer */ +static int get_key_data(unsigned char *buf, + unsigned int codeset, unsigned int key) +{ + unsigned char *data, *endp, *diffs, *key_block; + unsigned char keys, ndiffs, id; + unsigned int base, lim, pos, i; + + /* Binary search for the codeset */ + for (base = 0, lim = tx_data->num_code_sets; lim; lim >>= 1) { + pos = base + (lim >> 1); + data = tx_data->code_sets[pos]; + + if (!read_uint32(&data, tx_data->endp, &i)) + goto corrupt; + + if (i == codeset) + break; + else if (codeset > i) { + base = pos + 1; + --lim; + } + } + /* Not found? */ + if (!lim) + return -EPROTO; + + /* Set end of data block */ + endp = pos < tx_data->num_code_sets - 1 ? + tx_data->code_sets[pos + 1] : tx_data->endp; + + /* Read the block header */ + if (!read_uint8(&data, endp, &keys) || + !read_uint8(&data, endp, &ndiffs) || + ndiffs > TX_BLOCK_SIZE || keys == 0) + goto corrupt; + + /* Save diffs & skip */ + diffs = data; + if (!skip(&data, endp, ndiffs)) + goto corrupt; + + /* Read the id of the first key */ + if (!read_uint8(&data, endp, &id)) + goto corrupt; + + /* Unpack the first key's data */ + for (i = 0; i < TX_BLOCK_SIZE; ++i) { + if (tx_data->fixed[i] == -1) { + if (!read_uint8(&data, endp, &buf[i])) + goto corrupt; + } else { + buf[i] = (unsigned char)tx_data->fixed[i]; + } + } + + /* Early out key found/not found */ + if (key == id) + return 0; + if (keys == 1) + return -EPROTO; + + /* Sanity check */ + key_block = data; + if (!skip(&data, endp, (keys - 1) * (ndiffs + 1))) + goto corrupt; + + /* Binary search for the key */ + for (base = 0, lim = keys - 1; lim; lim >>= 1) { + /* Seek to block */ + unsigned char *key_data; + pos = base + (lim >> 1); + key_data = key_block + (ndiffs + 1) * pos; + + if (*key_data == key) { + /* skip key id */ + ++key_data; + + /* found, so unpack the diffs */ + for (i = 0; i < ndiffs; ++i) { + unsigned char val; + if (!read_uint8(&key_data, endp, &val) || + diffs[i] >= TX_BLOCK_SIZE) + goto corrupt; + buf[diffs[i]] = val; + } + + return 0; + } else if (key > *key_data) { + base = pos + 1; + --lim; + } + } + /* Key not found */ + return -EPROTO; + +corrupt: + zilog_error("firmware is corrupt\n"); + return -EFAULT; +} + +/* send a block of data to the IR TX device */ +static int send_data_block(struct IR *ir, unsigned char *data_block) +{ + int i, j, ret; + unsigned char buf[5]; + + for (i = 0; i < TX_BLOCK_SIZE;) { + int tosend = TX_BLOCK_SIZE - i; + if (tosend > 4) + tosend = 4; + buf[0] = (unsigned char)(i + 1); + for (j = 0; j < tosend; ++j) + buf[1 + j] = data_block[i + j]; + dprintk("%02x %02x %02x %02x %02x", + buf[0], buf[1], buf[2], buf[3], buf[4]); + ret = i2c_master_send(&ir->c_tx, buf, tosend + 1); + if (ret != tosend + 1) { + zilog_error("i2c_master_send failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + i += tosend; + } + return 0; +} + +/* send boot data to the IR TX device */ +static int send_boot_data(struct IR *ir) +{ + int ret; + unsigned char buf[4]; + + /* send the boot block */ + ret = send_data_block(ir, tx_data->boot_data); + if (ret != 0) + return ret; + + /* kick it off? */ + buf[0] = 0x00; + buf[1] = 0x20; + ret = i2c_master_send(&ir->c_tx, buf, 2); + if (ret != 2) { + zilog_error("i2c_master_send failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + ret = i2c_master_send(&ir->c_tx, buf, 1); + if (ret != 1) { + zilog_error("i2c_master_send failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + + /* Here comes the firmware version... (hopefully) */ + ret = i2c_master_recv(&ir->c_tx, buf, 4); + if (ret != 4) { + zilog_error("i2c_master_recv failed with %d\n", ret); + return 0; + } + if (buf[0] != 0x80) { + zilog_error("unexpected IR TX response: %02x\n", buf[0]); + return 0; + } + zilog_notify("Zilog/Hauppauge IR blaster firmware version " + "%d.%d.%d loaded\n", buf[1], buf[2], buf[3]); + + return 0; +} + +/* unload "firmware", lock held */ +static void fw_unload_locked(void) +{ + if (tx_data) { + if (tx_data->code_sets) + vfree(tx_data->code_sets); + + if (tx_data->datap) + vfree(tx_data->datap); + + vfree(tx_data); + tx_data = NULL; + dprintk("successfully unloaded IR blaster firmware\n"); + } +} + +/* unload "firmware" for the IR TX device */ +static void fw_unload(void) +{ + mutex_lock(&tx_data_lock); + fw_unload_locked(); + mutex_unlock(&tx_data_lock); +} + +/* load "firmware" for the IR TX device */ +static int fw_load(struct IR *ir) +{ + int ret; + unsigned int i; + unsigned char *data, version, num_global_fixed; + const struct firmware *fw_entry; + + /* Already loaded? */ + mutex_lock(&tx_data_lock); + if (tx_data) { + ret = 0; + goto out; + } + + /* Request codeset data file */ + ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", &ir->c_tx.dev); + if (ret != 0) { + zilog_error("firmware haup-ir-blaster.bin not available " + "(%d)\n", ret); + ret = ret < 0 ? ret : -EFAULT; + goto out; + } + dprintk("firmware of size %zu loaded\n", fw_entry->size); + + /* Parse the file */ + tx_data = vmalloc(sizeof(*tx_data)); + if (tx_data == NULL) { + zilog_error("out of memory\n"); + release_firmware(fw_entry); + ret = -ENOMEM; + goto out; + } + tx_data->code_sets = NULL; + + /* Copy the data so hotplug doesn't get confused and timeout */ + tx_data->datap = vmalloc(fw_entry->size); + if (tx_data->datap == NULL) { + zilog_error("out of memory\n"); + release_firmware(fw_entry); + vfree(tx_data); + ret = -ENOMEM; + goto out; + } + memcpy(tx_data->datap, fw_entry->data, fw_entry->size); + tx_data->endp = tx_data->datap + fw_entry->size; + release_firmware(fw_entry); fw_entry = NULL; + + /* Check version */ + data = tx_data->datap; + if (!read_uint8(&data, tx_data->endp, &version)) + goto corrupt; + if (version != 1) { + zilog_error("unsupported code set file version (%u, expected" + "1) -- please upgrade to a newer driver", + version); + fw_unload_locked(); + ret = -EFAULT; + goto out; + } + + /* Save boot block for later */ + tx_data->boot_data = data; + if (!skip(&data, tx_data->endp, TX_BLOCK_SIZE)) + goto corrupt; + + if (!read_uint32(&data, tx_data->endp, + &tx_data->num_code_sets)) + goto corrupt; + + dprintk("%u IR blaster codesets loaded\n", tx_data->num_code_sets); + + tx_data->code_sets = vmalloc( + tx_data->num_code_sets * sizeof(char *)); + if (tx_data->code_sets == NULL) { + fw_unload_locked(); + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < TX_BLOCK_SIZE; ++i) + tx_data->fixed[i] = -1; + + /* Read global fixed data template */ + if (!read_uint8(&data, tx_data->endp, &num_global_fixed) || + num_global_fixed > TX_BLOCK_SIZE) + goto corrupt; + for (i = 0; i < num_global_fixed; ++i) { + unsigned char pos, val; + if (!read_uint8(&data, tx_data->endp, &pos) || + !read_uint8(&data, tx_data->endp, &val) || + pos >= TX_BLOCK_SIZE) + goto corrupt; + tx_data->fixed[pos] = (int)val; + } + + /* Filch out the position of each code set */ + for (i = 0; i < tx_data->num_code_sets; ++i) { + unsigned int id; + unsigned char keys; + unsigned char ndiffs; + + /* Save the codeset position */ + tx_data->code_sets[i] = data; + + /* Read header */ + if (!read_uint32(&data, tx_data->endp, &id) || + !read_uint8(&data, tx_data->endp, &keys) || + !read_uint8(&data, tx_data->endp, &ndiffs) || + ndiffs > TX_BLOCK_SIZE || keys == 0) + goto corrupt; + + /* skip diff positions */ + if (!skip(&data, tx_data->endp, ndiffs)) + goto corrupt; + + /* + * After the diffs we have the first key id + data - + * global fixed + */ + if (!skip(&data, tx_data->endp, + 1 + TX_BLOCK_SIZE - num_global_fixed)) + goto corrupt; + + /* Then we have keys-1 blocks of key id+diffs */ + if (!skip(&data, tx_data->endp, + (ndiffs + 1) * (keys - 1))) + goto corrupt; + } + ret = 0; + goto out; + +corrupt: + zilog_error("firmware is corrupt\n"); + fw_unload_locked(); + ret = -EFAULT; + +out: + mutex_unlock(&tx_data_lock); + return ret; +} + +/* initialise the IR TX device */ +static int tx_init(struct IR *ir) +{ + int ret; + + /* Load 'firmware' */ + ret = fw_load(ir); + if (ret != 0) + return ret; + + /* Send boot block */ + ret = send_boot_data(ir); + if (ret != 0) + return ret; + ir->need_boot = 0; + + /* Looks good */ + return 0; +} + +/* do nothing stub to make LIRC happy */ +static loff_t lseek(struct file *filep, loff_t offset, int orig) +{ + return -ESPIPE; +} + +/* copied from lirc_dev */ +static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) +{ + struct IR *ir = (struct IR *)filep->private_data; + unsigned char buf[ir->buf.chunk_size]; + int ret = 0, written = 0; + DECLARE_WAITQUEUE(wait, current); + + dprintk("read called\n"); + if (ir->c_rx.addr == 0) + return -ENODEV; + + if (mutex_lock_interruptible(&ir->buf_lock)) + return -ERESTARTSYS; + + if (n % ir->buf.chunk_size) { + dprintk("read result = -EINVAL\n"); + mutex_unlock(&ir->buf_lock); + return -EINVAL; + } + + /* + * we add ourselves to the task queue before buffer check + * to avoid losing scan code (in case when queue is awaken somewhere + * between while condition checking and scheduling) + */ + add_wait_queue(&ir->buf.wait_poll, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + /* + * while we didn't provide 'length' bytes, device is opened in blocking + * mode and 'copy_to_user' is happy, wait for data. + */ + while (written < n && ret == 0) { + if (lirc_buffer_empty(&ir->buf)) { + /* + * According to the read(2) man page, 'written' can be + * returned as less than 'n', instead of blocking + * again, returning -EWOULDBLOCK, or returning + * -ERESTARTSYS + */ + if (written) + break; + if (filep->f_flags & O_NONBLOCK) { + ret = -EWOULDBLOCK; + break; + } + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } else { + lirc_buffer_read(&ir->buf, buf); + ret = copy_to_user((void *)outbuf+written, buf, + ir->buf.chunk_size); + written += ir->buf.chunk_size; + } + } + + remove_wait_queue(&ir->buf.wait_poll, &wait); + set_current_state(TASK_RUNNING); + mutex_unlock(&ir->buf_lock); + + dprintk("read result = %s (%d)\n", + ret ? "-EFAULT" : "OK", ret); + + return ret ? ret : written; +} + +/* send a keypress to the IR TX device */ +static int send_code(struct IR *ir, unsigned int code, unsigned int key) +{ + unsigned char data_block[TX_BLOCK_SIZE]; + unsigned char buf[2]; + int i, ret; + + /* Get data for the codeset/key */ + ret = get_key_data(data_block, code, key); + + if (ret == -EPROTO) { + zilog_error("failed to get data for code %u, key %u -- check " + "lircd.conf entries\n", code, key); + return ret; + } else if (ret != 0) + return ret; + + /* Send the data block */ + ret = send_data_block(ir, data_block); + if (ret != 0) + return ret; + + /* Send data block length? */ + buf[0] = 0x00; + buf[1] = 0x40; + ret = i2c_master_send(&ir->c_tx, buf, 2); + if (ret != 2) { + zilog_error("i2c_master_send failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + ret = i2c_master_send(&ir->c_tx, buf, 1); + if (ret != 1) { + zilog_error("i2c_master_send failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + + /* Send finished download? */ + ret = i2c_master_recv(&ir->c_tx, buf, 1); + if (ret != 1) { + zilog_error("i2c_master_recv failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + if (buf[0] != 0xA0) { + zilog_error("unexpected IR TX response #1: %02x\n", + buf[0]); + return -EFAULT; + } + + /* Send prepare command? */ + buf[0] = 0x00; + buf[1] = 0x80; + ret = i2c_master_send(&ir->c_tx, buf, 2); + if (ret != 2) { + zilog_error("i2c_master_send failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + +#ifdef I2C_HW_B_HDPVR + /* + * The sleep bits aren't necessary on the HD PVR, and in fact, the + * last i2c_master_recv always fails with a -5, so for now, we're + * going to skip this whole mess and say we're done on the HD PVR + */ + if (ir->c_rx.adapter->id == I2C_HW_B_HDPVR) + goto done; +#endif + + /* + * This bit NAKs until the device is ready, so we retry it + * sleeping a bit each time. This seems to be what the windows + * driver does, approximately. + * Try for up to 1s. + */ + for (i = 0; i < 20; ++i) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((50 * HZ + 999) / 1000); + ret = i2c_master_send(&ir->c_tx, buf, 1); + if (ret == 1) + break; + dprintk("NAK expected: i2c_master_send " + "failed with %d (try %d)\n", ret, i+1); + } + if (ret != 1) { + zilog_error("IR TX chip never got ready: last i2c_master_send " + "failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + + /* Seems to be an 'ok' response */ + i = i2c_master_recv(&ir->c_tx, buf, 1); + if (i != 1) { + zilog_error("i2c_master_recv failed with %d\n", ret); + return -EFAULT; + } + if (buf[0] != 0x80) { + zilog_error("unexpected IR TX response #2: %02x\n", buf[0]); + return -EFAULT; + } + +done: + /* Oh good, it worked */ + dprintk("sent code %u, key %u\n", code, key); + return 0; +} + +/* + * Write a code to the device. We take in a 32-bit number (an int) and then + * decode this to a codeset/key index. The key data is then decompressed and + * sent to the device. We have a spin lock as per i2c documentation to prevent + * multiple concurrent sends which would probably cause the device to explode. + */ +static ssize_t write(struct file *filep, const char *buf, size_t n, + loff_t *ppos) +{ + struct IR *ir = (struct IR *)filep->private_data; + size_t i; + int failures = 0; + + if (ir->c_tx.addr == 0) + return -ENODEV; + + /* Validate user parameters */ + if (n % sizeof(int)) + return -EINVAL; + + /* Lock i2c bus for the duration */ + mutex_lock(&ir->ir_lock); + + /* Send each keypress */ + for (i = 0; i < n;) { + int ret = 0; + int command; + + if (copy_from_user(&command, buf + i, sizeof(command))) { + mutex_unlock(&ir->ir_lock); + return -EFAULT; + } + + /* Send boot data first if required */ + if (ir->need_boot == 1) { + ret = send_boot_data(ir); + if (ret == 0) + ir->need_boot = 0; + } + + /* Send the code */ + if (ret == 0) { + ret = send_code(ir, (unsigned)command >> 16, + (unsigned)command & 0xFFFF); + if (ret == -EPROTO) { + mutex_unlock(&ir->ir_lock); + return ret; + } + } + + /* + * Hmm, a failure. If we've had a few then give up, otherwise + * try a reset + */ + if (ret != 0) { + /* Looks like the chip crashed, reset it */ + zilog_error("sending to the IR transmitter chip " + "failed, trying reset\n"); + + if (failures >= 3) { + zilog_error("unable to send to the IR chip " + "after 3 resets, giving up\n"); + mutex_unlock(&ir->ir_lock); + return ret; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((100 * HZ + 999) / 1000); + ir->need_boot = 1; + ++failures; + } else + i += sizeof(int); + } + + /* Release i2c bus */ + mutex_unlock(&ir->ir_lock); + + /* All looks good */ + return n; +} + +/* copied from lirc_dev */ +static unsigned int poll(struct file *filep, poll_table *wait) +{ + struct IR *ir = (struct IR *)filep->private_data; + unsigned int ret; + + dprintk("poll called\n"); + if (ir->c_rx.addr == 0) + return -ENODEV; + + mutex_lock(&ir->buf_lock); + + poll_wait(filep, &ir->buf.wait_poll, wait); + + dprintk("poll result = %s\n", + lirc_buffer_empty(&ir->buf) ? "0" : "POLLIN|POLLRDNORM"); + + ret = lirc_buffer_empty(&ir->buf) ? 0 : (POLLIN|POLLRDNORM); + + mutex_unlock(&ir->buf_lock); + return ret; +} + +static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + struct IR *ir = (struct IR *)filep->private_data; + int result; + unsigned long mode, features = 0; + + if (ir->c_rx.addr != 0) + features |= LIRC_CAN_REC_LIRCCODE; + if (ir->c_tx.addr != 0) + features |= LIRC_CAN_SEND_PULSE; + + switch (cmd) { + case LIRC_GET_LENGTH: + result = put_user((unsigned long)13, + (unsigned long *)arg); + break; + case LIRC_GET_FEATURES: + result = put_user(features, (unsigned long *) arg); + break; + case LIRC_GET_REC_MODE: + if (!(features&LIRC_CAN_REC_MASK)) + return -ENOSYS; + + result = put_user(LIRC_REC2MODE + (features&LIRC_CAN_REC_MASK), + (unsigned long *)arg); + break; + case LIRC_SET_REC_MODE: + if (!(features&LIRC_CAN_REC_MASK)) + return -ENOSYS; + + result = get_user(mode, (unsigned long *)arg); + if (!result && !(LIRC_MODE2REC(mode) & features)) + result = -EINVAL; + break; + case LIRC_GET_SEND_MODE: + if (!(features&LIRC_CAN_SEND_MASK)) + return -ENOSYS; + + result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg); + break; + case LIRC_SET_SEND_MODE: + if (!(features&LIRC_CAN_SEND_MASK)) + return -ENOSYS; + + result = get_user(mode, (unsigned long *) arg); + if (!result && mode != LIRC_MODE_PULSE) + return -EINVAL; + break; + default: + return -EINVAL; + } + return result; +} + +/* + * Open the IR device. Get hold of our IR structure and + * stash it in private_data for the file + */ +static int open(struct inode *node, struct file *filep) +{ + struct IR *ir; + int ret; + + /* find our IR struct */ + unsigned minor = MINOR(node->i_rdev); + if (minor >= MAX_IRCTL_DEVICES) { + dprintk("minor %d: open result = -ENODEV\n", + minor); + return -ENODEV; + } + ir = ir_devices[minor]; + + /* increment in use count */ + mutex_lock(&ir->ir_lock); + ++ir->open; + ret = set_use_inc(ir); + if (ret != 0) { + --ir->open; + mutex_unlock(&ir->ir_lock); + return ret; + } + mutex_unlock(&ir->ir_lock); + + /* stash our IR struct */ + filep->private_data = ir; + + return 0; +} + +/* Close the IR device */ +static int close(struct inode *node, struct file *filep) +{ + /* find our IR struct */ + struct IR *ir = (struct IR *)filep->private_data; + if (ir == NULL) { + zilog_error("close: no private_data attached to the file!\n"); + return -ENODEV; + } + + /* decrement in use count */ + mutex_lock(&ir->ir_lock); + --ir->open; + set_use_dec(ir); + mutex_unlock(&ir->ir_lock); + + return 0; +} + +static struct lirc_driver lirc_template = { + .name = "lirc_zilog", + .set_use_inc = set_use_inc, + .set_use_dec = set_use_dec, + .owner = THIS_MODULE +}; + +static int ir_remove(struct i2c_client *client); +static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id); +static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg); + +static const struct i2c_device_id ir_transceiver_id[] = { + /* Generic entry for any IR transceiver */ + { "ir_video", 0 }, + /* IR device specific entries should be added here */ + { "ir_tx_z8f0811_haup", 0 }, + { "ir_rx_z8f0811_haup", 0 }, + { } +}; + +static struct i2c_driver driver = { + .driver = { + .owner = THIS_MODULE, + .name = "Zilog/Hauppauge i2c IR", + }, + .probe = ir_probe, + .remove = ir_remove, + .command = ir_command, + .id_table = ir_transceiver_id, +}; + +static const struct file_operations lirc_fops = { + .owner = THIS_MODULE, + .llseek = lseek, + .read = read, + .write = write, + .poll = poll, + .unlocked_ioctl = ioctl, + .open = open, + .release = close +}; + +static int ir_remove(struct i2c_client *client) +{ + struct IR *ir = i2c_get_clientdata(client); + + mutex_lock(&ir->ir_lock); + + if (ir->have_rx || ir->have_tx) { + DECLARE_COMPLETION(tn); + DECLARE_COMPLETION(tn2); + + /* end up polling thread */ + if (ir->task && !IS_ERR(ir->task)) { + ir->t_notify = &tn; + ir->t_notify2 = &tn2; + ir->shutdown = 1; + wake_up_process(ir->task); + complete(&tn2); + wait_for_completion(&tn); + ir->t_notify = NULL; + ir->t_notify2 = NULL; + } + + } else { + mutex_unlock(&ir->ir_lock); + zilog_error("%s: detached from something we didn't " + "attach to\n", __func__); + return -ENODEV; + } + + /* unregister lirc driver */ + if (ir->l.minor >= 0 && ir->l.minor < MAX_IRCTL_DEVICES) { + lirc_unregister_driver(ir->l.minor); + ir_devices[ir->l.minor] = NULL; + } + + /* free memory */ + lirc_buffer_free(&ir->buf); + mutex_unlock(&ir->ir_lock); + kfree(ir); + + return 0; +} + +static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct IR *ir = NULL; + struct i2c_adapter *adap = client->adapter; + char buf; + int ret; + int have_rx = 0, have_tx = 0; + + dprintk("%s: adapter id=0x%x, client addr=0x%02x\n", + __func__, adap->id, client->addr); + + /* + * The external IR receiver is at i2c address 0x71. + * The IR transmitter is at 0x70. + */ + client->addr = 0x70; + + if (!disable_tx) { + if (i2c_master_recv(client, &buf, 1) == 1) + have_tx = 1; + dprintk("probe 0x70 @ %s: %s\n", + adap->name, have_tx ? "success" : "failed"); + } + + if (!disable_rx) { + client->addr = 0x71; + if (i2c_master_recv(client, &buf, 1) == 1) + have_rx = 1; + dprintk("probe 0x71 @ %s: %s\n", + adap->name, have_rx ? "success" : "failed"); + } + + if (!(have_rx || have_tx)) { + zilog_error("%s: no devices found\n", adap->name); + goto out_nodev; + } + + printk(KERN_INFO "lirc_zilog: chip found with %s\n", + have_rx && have_tx ? "RX and TX" : + have_rx ? "RX only" : "TX only"); + + ir = kzalloc(sizeof(struct IR), GFP_KERNEL); + + if (!ir) + goto out_nomem; + + ret = lirc_buffer_init(&ir->buf, 2, BUFLEN / 2); + if (ret) + goto out_nomem; + + mutex_init(&ir->ir_lock); + mutex_init(&ir->buf_lock); + ir->need_boot = 1; + + memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver)); + ir->l.minor = -1; + + /* I2C attach to device */ + i2c_set_clientdata(client, ir); + + /* initialise RX device */ + if (have_rx) { + DECLARE_COMPLETION(tn); + memcpy(&ir->c_rx, client, sizeof(struct i2c_client)); + + ir->c_rx.addr = 0x71; + strlcpy(ir->c_rx.name, ZILOG_HAUPPAUGE_IR_RX_NAME, + I2C_NAME_SIZE); + + /* try to fire up polling thread */ + ir->t_notify = &tn; + ir->task = kthread_run(lirc_thread, ir, "lirc_zilog"); + if (IS_ERR(ir->task)) { + ret = PTR_ERR(ir->task); + zilog_error("lirc_register_driver: cannot run " + "poll thread %d\n", ret); + goto err; + } + wait_for_completion(&tn); + ir->t_notify = NULL; + ir->have_rx = 1; + } + + /* initialise TX device */ + if (have_tx) { + memcpy(&ir->c_tx, client, sizeof(struct i2c_client)); + ir->c_tx.addr = 0x70; + strlcpy(ir->c_tx.name, ZILOG_HAUPPAUGE_IR_TX_NAME, + I2C_NAME_SIZE); + ir->have_tx = 1; + } + + /* set lirc_dev stuff */ + ir->l.code_length = 13; + ir->l.rbuf = &ir->buf; + ir->l.fops = &lirc_fops; + ir->l.data = ir; + ir->l.minor = minor; + ir->l.dev = &adap->dev; + ir->l.sample_rate = 0; + + /* register with lirc */ + ir->l.minor = lirc_register_driver(&ir->l); + if (ir->l.minor < 0 || ir->l.minor >= MAX_IRCTL_DEVICES) { + zilog_error("ir_attach: \"minor\" must be between 0 and %d " + "(%d)!\n", MAX_IRCTL_DEVICES-1, ir->l.minor); + ret = -EBADRQC; + goto err; + } + + /* store this for getting back in open() later on */ + ir_devices[ir->l.minor] = ir; + + /* + * if we have the tx device, load the 'firmware'. We do this + * after registering with lirc as otherwise hotplug seems to take + * 10s to create the lirc device. + */ + if (have_tx) { + /* Special TX init */ + ret = tx_init(ir); + if (ret != 0) + goto err; + } + + return 0; + +err: + /* undo everything, hopefully... */ + if (ir->c_rx.addr) + ir_remove(&ir->c_rx); + if (ir->c_tx.addr) + ir_remove(&ir->c_tx); + return ret; + +out_nodev: + zilog_error("no device found\n"); + return -ENODEV; + +out_nomem: + zilog_error("memory allocation failure\n"); + kfree(ir); + return -ENOMEM; +} + +static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + /* nothing */ + return 0; +} + +static int __init zilog_init(void) +{ + int ret; + + zilog_notify("Zilog/Hauppauge IR driver initializing\n"); + + mutex_init(&tx_data_lock); + + request_module("firmware_class"); + + ret = i2c_add_driver(&driver); + if (ret) + zilog_error("initialization failed\n"); + else + zilog_notify("initialization complete\n"); + + return ret; +} + +static void __exit zilog_exit(void) +{ + i2c_del_driver(&driver); + /* if loaded */ + fw_unload(); + zilog_notify("Zilog/Hauppauge IR driver unloaded\n"); +} + +module_init(zilog_init); +module_exit(zilog_exit); + +MODULE_DESCRIPTION("Zilog/Hauppauge infrared transmitter driver (i2c stack)"); +MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, " + "Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver"); +MODULE_LICENSE("GPL"); +/* for compat with old name, which isn't all that accurate anymore */ +MODULE_ALIAS("lirc_pvr150"); + +module_param(minor, int, 0444); +MODULE_PARM_DESC(minor, "Preferred minor device number"); + +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Enable debugging messages"); + +module_param(disable_rx, bool, 0644); +MODULE_PARM_DESC(disable_rx, "Disable the IR receiver device"); + +module_param(disable_tx, bool, 0644); +MODULE_PARM_DESC(disable_tx, "Disable the IR transmitter device"); diff --git a/drivers/staging/tm6000/Kconfig b/drivers/staging/tm6000/Kconfig index 3657e33..c725356 100644 --- a/drivers/staging/tm6000/Kconfig +++ b/drivers/staging/tm6000/Kconfig @@ -26,8 +26,8 @@ config VIDEO_TM6000_ALSA module will be called tm6000-alsa. config VIDEO_TM6000_DVB - bool "DVB Support for tm6000 based TV cards" - depends on VIDEO_TM6000 && DVB_CORE && EXPERIMENTAL + tristate "DVB Support for tm6000 based TV cards" + depends on VIDEO_TM6000 && DVB_CORE && USB && EXPERIMENTAL select DVB_ZL10353 ---help--- This adds support for DVB cards based on the tm5600/tm6000 chip. diff --git a/drivers/staging/tm6000/Makefile b/drivers/staging/tm6000/Makefile index 93370fc..77e06bf 100644 --- a/drivers/staging/tm6000/Makefile +++ b/drivers/staging/tm6000/Makefile @@ -2,14 +2,12 @@ tm6000-objs := tm6000-cards.o \ tm6000-core.o \ tm6000-i2c.o \ tm6000-video.o \ - tm6000-stds.o - -ifeq ($(CONFIG_VIDEO_TM6000_DVB),y) -tm6000-objs += tm6000-dvb.o -endif + tm6000-stds.o \ + tm6000-input.o obj-$(CONFIG_VIDEO_TM6000) += tm6000.o obj-$(CONFIG_VIDEO_TM6000_ALSA) += tm6000-alsa.o +obj-$(CONFIG_VIDEO_TM6000_DVB) += tm6000-dvb.o EXTRA_CFLAGS = -Idrivers/media/video EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/drivers/staging/tm6000/tm6000-alsa.c b/drivers/staging/tm6000/tm6000-alsa.c index 273e26e..087137d 100644 --- a/drivers/staging/tm6000/tm6000-alsa.c +++ b/drivers/staging/tm6000/tm6000-alsa.c @@ -16,6 +16,7 @@ #include <linux/interrupt.h> #include <linux/usb.h> #include <linux/slab.h> +#include <linux/vmalloc.h> #include <asm/delay.h> #include <sound/core.h> @@ -36,34 +37,11 @@ } while (0) /**************************************************************************** - Data type declarations - Can be moded to a header file later - ****************************************************************************/ - -struct snd_tm6000_card { - struct snd_card *card; - - spinlock_t reg_lock; - - atomic_t count; - - unsigned int period_size; - unsigned int num_periods; - - struct tm6000_core *core; - struct tm6000_buffer *buf; - - int bufsize; - - struct snd_pcm_substream *substream; -}; - - -/**************************************************************************** Module global static vars ****************************************************************************/ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ -static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ + static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1}; module_param_array(enable, bool, NULL, 0444); @@ -100,11 +78,15 @@ static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip) struct tm6000_core *core = chip->core; int val; + dprintk(1, "Starting audio DMA\n"); + /* Enables audio */ val = tm6000_get_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, 0x0); val |= 0x20; tm6000_set_reg(core, TM6010_REQ07_RCC_ACTIVE_VIDEO_IF, val); + tm6000_set_audio_bitrate(core, 48000); + tm6000_set_reg(core, TM6010_REQ08_R01_A_INIT, 0x80); return 0; @@ -129,19 +111,39 @@ static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip) return 0; } -static int dsp_buffer_free(struct snd_tm6000_card *chip) +static void dsp_buffer_free(struct snd_pcm_substream *substream) { - BUG_ON(!chip->bufsize); + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); dprintk(2, "Freeing buffer\n"); - /* FIXME: Frees buffer */ + vfree(substream->runtime->dma_area); + substream->runtime->dma_area = NULL; + substream->runtime->dma_bytes = 0; +} - chip->bufsize = 0; +static int dsp_buffer_alloc(struct snd_pcm_substream *substream, int size) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + + dprintk(2, "Allocating buffer\n"); + + if (substream->runtime->dma_area) { + if (substream->runtime->dma_bytes > size) + return 0; + dsp_buffer_free(substream); + } - return 0; + substream->runtime->dma_area = vmalloc(size); + if (!substream->runtime->dma_area) + return -ENOMEM; + + substream->runtime->dma_bytes = size; + + return 0; } + /**************************************************************************** ALSA PCM Interface ****************************************************************************/ @@ -158,16 +160,16 @@ static struct snd_pcm_hardware snd_tm6000_digital_hw = { SNDRV_PCM_INFO_MMAP_VALID, .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, - .rate_min = 44100, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, .rate_max = 48000, .channels_min = 2, .channels_max = 2, - .period_bytes_min = DEFAULT_FIFO_SIZE/4, - .period_bytes_max = DEFAULT_FIFO_SIZE/4, + .period_bytes_min = 62720, + .period_bytes_max = 62720, .periods_min = 1, .periods_max = 1024, - .buffer_bytes_max = (1024*1024), + .buffer_bytes_max = 62720 * 8, }; /* @@ -202,29 +204,64 @@ static int snd_tm6000_close(struct snd_pcm_substream *substream) return 0; } +static int tm6000_fillbuf(struct tm6000_core *core, char *buf, int size) +{ + struct snd_tm6000_card *chip = core->adev; + struct snd_pcm_substream *substream = chip->substream; + struct snd_pcm_runtime *runtime; + int period_elapsed = 0; + unsigned int stride, buf_pos; + + if (!size || !substream) + return -EINVAL; + + runtime = substream->runtime; + if (!runtime || !runtime->dma_area) + return -EINVAL; + + buf_pos = chip->buf_pos; + stride = runtime->frame_bits >> 3; + + dprintk(1, "Copying %d bytes at %p[%d] - buf size=%d x %d\n", size, + runtime->dma_area, buf_pos, + (unsigned int)runtime->buffer_size, stride); + + if (buf_pos + size >= runtime->buffer_size * stride) { + unsigned int cnt = runtime->buffer_size * stride - buf_pos; + memcpy(runtime->dma_area + buf_pos, buf, cnt); + memcpy(runtime->dma_area, buf + cnt, size - cnt); + } else + memcpy(runtime->dma_area + buf_pos, buf, size); + + chip->buf_pos += size; + if (chip->buf_pos >= runtime->buffer_size * stride) + chip->buf_pos -= runtime->buffer_size * stride; + + chip->period_pos += size; + if (chip->period_pos >= runtime->period_size) { + chip->period_pos -= runtime->period_size; + period_elapsed = 1; + } + + if (period_elapsed) + snd_pcm_period_elapsed(substream); + + return 0; +} + /* * hw_params callback */ static int snd_tm6000_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); - - if (substream->runtime->dma_area) { - dsp_buffer_free(chip); - substream->runtime->dma_area = NULL; - } - - chip->period_size = params_period_bytes(hw_params); - chip->num_periods = params_periods(hw_params); - chip->bufsize = chip->period_size * params_periods(hw_params); - - BUG_ON(!chip->bufsize); + int size, rc; - dprintk(1, "Setting buffer\n"); - - /* FIXME: Allocate buffer for audio */ + size = params_period_bytes(hw_params) * params_periods(hw_params); + rc = dsp_buffer_alloc(substream, size); + if (rc < 0) + return rc; return 0; } @@ -234,13 +271,9 @@ static int snd_tm6000_hw_params(struct snd_pcm_substream *substream, */ static int snd_tm6000_hw_free(struct snd_pcm_substream *substream) { - struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); - if (substream->runtime->dma_area) { - dsp_buffer_free(chip); - substream->runtime->dma_area = NULL; - } + _tm6000_stop_audio_dma(chip); return 0; } @@ -250,6 +283,11 @@ static int snd_tm6000_hw_free(struct snd_pcm_substream *substream) */ static int snd_tm6000_prepare(struct snd_pcm_substream *substream) { + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + + chip->buf_pos = 0; + chip->period_pos = 0; + return 0; } @@ -287,12 +325,8 @@ static int snd_tm6000_card_trigger(struct snd_pcm_substream *substream, int cmd) static snd_pcm_uframes_t snd_tm6000_pointer(struct snd_pcm_substream *substream) { struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - u16 count; - - count = atomic_read(&chip->count); - return runtime->period_size * (count & (runtime->periods-1)); + return chip->buf_pos; } /* @@ -312,21 +346,6 @@ static struct snd_pcm_ops snd_tm6000_pcm_ops = { /* * create a PCM device */ -static int __devinit snd_tm6000_pcm(struct snd_tm6000_card *chip, - int device, char *name) -{ - int err; - struct snd_pcm *pcm; - - err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm); - if (err < 0) - return err; - pcm->private_data = chip; - strcpy(pcm->name, name); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_tm6000_pcm_ops); - - return 0; -} /* FIXME: Control interface - How to control volume/mute? */ @@ -337,27 +356,41 @@ static int __devinit snd_tm6000_pcm(struct snd_tm6000_card *chip, /* * Alsa Constructor - Component probe */ - -int tm6000_audio_init(struct tm6000_core *dev, int idx) +int tm6000_audio_init(struct tm6000_core *dev) { - struct snd_card *card; - struct snd_tm6000_card *chip; - int rc, len; - char component[14]; + struct snd_card *card; + struct snd_tm6000_card *chip; + int rc; + static int devnr; + char component[14]; + struct snd_pcm *pcm; + + if (!dev) + return 0; - if (idx >= SNDRV_CARDS) + if (devnr >= SNDRV_CARDS) return -ENODEV; - if (!enable[idx]) + if (!enable[devnr]) return -ENOENT; - rc = snd_card_create(index[idx], id[idx], THIS_MODULE, 0, &card); + rc = snd_card_create(index[devnr], "tm6000", THIS_MODULE, 0, &card); if (rc < 0) { - snd_printk(KERN_ERR "cannot create card instance %d\n", idx); + snd_printk(KERN_ERR "cannot create card instance %d\n", devnr); return rc; } + strcpy(card->driver, "tm6000-alsa"); + strcpy(card->shortname, "TM5600/60x0"); + sprintf(card->longname, "TM5600/60x0 Audio at bus %d device %d", + dev->udev->bus->busnum, dev->udev->devnum); + + sprintf(component, "USB%04x:%04x", + le16_to_cpu(dev->udev->descriptor.idVendor), + le16_to_cpu(dev->udev->descriptor.idProduct)); + snd_component_add(card, component); + snd_card_set_dev(card, &dev->udev->dev); - chip = kzalloc(sizeof(*chip), GFP_KERNEL); + chip = kzalloc(sizeof(struct snd_tm6000_card), GFP_KERNEL); if (!chip) { rc = -ENOMEM; goto error; @@ -365,45 +398,24 @@ int tm6000_audio_init(struct tm6000_core *dev, int idx) chip->core = dev; chip->card = card; + dev->adev = chip; + spin_lock_init(&chip->reg_lock); - strcpy(card->driver, "tm6000-alsa"); - sprintf(component, "USB%04x:%04x", - le16_to_cpu(dev->udev->descriptor.idVendor), - le16_to_cpu(dev->udev->descriptor.idProduct)); - snd_component_add(card, component); - - if (dev->udev->descriptor.iManufacturer) - len = usb_string(dev->udev, - dev->udev->descriptor.iManufacturer, - card->longname, sizeof(card->longname)); - else - len = 0; - - if (len > 0) - strlcat(card->longname, " ", sizeof(card->longname)); - - strlcat(card->longname, card->shortname, sizeof(card->longname)); - - len = strlcat(card->longname, " at ", sizeof(card->longname)); - - if (len < sizeof(card->longname)) - usb_make_path(dev->udev, card->longname + len, - sizeof(card->longname) - len); - - strlcat(card->longname, - dev->udev->speed == USB_SPEED_LOW ? ", low speed" : - dev->udev->speed == USB_SPEED_FULL ? ", full speed" : - ", high speed", - sizeof(card->longname)); - - rc = snd_tm6000_pcm(chip, 0, "tm6000 Digital"); + rc = snd_pcm_new(card, "TM6000 Audio", 0, 0, 1, &pcm); if (rc < 0) goto error; + pcm->info_flags = 0; + pcm->private_data = chip; + strcpy(pcm->name, "Trident TM5600/60x0"); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_tm6000_pcm_ops); + rc = snd_card_register(card); if (rc < 0) goto error; + dprintk(1,"Registered audio driver for %s\n", card->longname); return 0; @@ -414,14 +426,31 @@ error: static int tm6000_audio_fini(struct tm6000_core *dev) { + struct snd_tm6000_card *chip = dev->adev; + + if (!dev) + return 0; + + if (!chip) + return 0; + + if (!chip->card) + return 0; + + snd_card_free(chip->card); + chip->card = NULL; + kfree(chip); + dev->adev = NULL; + return 0; } struct tm6000_ops audio_ops = { - .id = TM6000_AUDIO, + .type = TM6000_AUDIO, .name = "TM6000 Audio Extension", .init = tm6000_audio_init, .fini = tm6000_audio_fini, + .fillbuf = tm6000_fillbuf, }; static int __init tm6000_alsa_register(void) diff --git a/drivers/staging/tm6000/tm6000-cards.c b/drivers/staging/tm6000/tm6000-cards.c index 6a9ae40..9d091c3 100644 --- a/drivers/staging/tm6000/tm6000-cards.c +++ b/drivers/staging/tm6000/tm6000-cards.c @@ -29,6 +29,7 @@ #include <media/tuner.h> #include <media/tvaudio.h> #include <media/i2c-addr.h> +#include <media/rc-map.h> #include "tm6000.h" #include "tm6000-regs.h" @@ -69,6 +70,8 @@ struct tm6000_board { int demod_addr; /* demodulator address */ struct tm6000_gpio gpio; + + char *ir_codes; }; struct tm6000_board tm6000_boards[] = { @@ -276,6 +279,7 @@ struct tm6000_board tm6000_boards[] = { .dvb_led = TM6010_GPIO_5, .ir = TM6010_GPIO_0, }, + .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS, }, [TM6010_BOARD_TWINHAN_TU501] = { .name = "Twinhan TU501(704D1)", @@ -347,7 +351,7 @@ int tm6000_xc5000_callback(void *ptr, int component, int command, int arg) } return (rc); } - +EXPORT_SYMBOL_GPL(tm6000_xc5000_callback); /* Tuner callback to provide the proper gpio changes needed for xc2028 */ @@ -361,6 +365,8 @@ int tm6000_tuner_callback(void *ptr, int component, int command, int arg) switch (command) { case XC2028_RESET_CLK: + tm6000_ir_wait(dev, 0); + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x02, arg); msleep(10); @@ -410,13 +416,14 @@ int tm6000_tuner_callback(void *ptr, int component, int command, int arg) msleep(130); break; } + + tm6000_ir_wait(dev, 1); break; case 1: tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x02, 0x01); msleep(10); break; - case 2: rc = tm6000_i2c_reset(dev, 100); break; @@ -424,6 +431,7 @@ int tm6000_tuner_callback(void *ptr, int component, int command, int arg) } return rc; } +EXPORT_SYMBOL_GPL(tm6000_tuner_callback); int tm6000_cards_setup(struct tm6000_core *dev) { @@ -635,6 +643,8 @@ static int tm6000_init_dev(struct tm6000_core *dev) dev->gpio = tm6000_boards[dev->model].gpio; + dev->ir_codes = tm6000_boards[dev->model].ir_codes; + dev->demod_addr = tm6000_boards[dev->model].demod_addr; dev->caps = tm6000_boards[dev->model].caps; @@ -681,31 +691,13 @@ static int tm6000_init_dev(struct tm6000_core *dev) goto err; tm6000_add_into_devlist(dev); - tm6000_init_extension(dev); - if (dev->caps.has_dvb) { - dev->dvb = kzalloc(sizeof(*(dev->dvb)), GFP_KERNEL); - if (!dev->dvb) { - rc = -ENOMEM; - goto err2; - } + tm6000_ir_init(dev); -#ifdef CONFIG_VIDEO_TM6000_DVB - rc = tm6000_dvb_register(dev); - if (rc < 0) { - kfree(dev->dvb); - dev->dvb = NULL; - goto err2; - } -#endif - } mutex_unlock(&dev->lock); return 0; -err2: - v4l2_device_unregister(&dev->v4l2_dev); - err: mutex_unlock(&dev->lock); return rc; @@ -724,7 +716,7 @@ static void get_max_endpoint(struct usb_device *udev, unsigned int size = tmp & 0x7ff; if (udev->speed == USB_SPEED_HIGH) - size = size * hb_mult (tmp); + size = size * hb_mult(tmp); if (size > tm_ep->maxsize) { tm_ep->endp = curr_e; @@ -848,6 +840,19 @@ static int tm6000_usb_probe(struct usb_interface *interface, &dev->isoc_out); } break; + case USB_ENDPOINT_XFER_INT: + if (!dir_out) { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "INT IN", e, + &dev->int_in); + } else { + get_max_endpoint(usbdev, + &interface->altsetting[i], + "INT OUT", e, + &dev->int_out); + } + break; } } } @@ -906,12 +911,7 @@ static void tm6000_usb_disconnect(struct usb_interface *interface) mutex_lock(&dev->lock); -#ifdef CONFIG_VIDEO_TM6000_DVB - if (dev->dvb) { - tm6000_dvb_unregister(dev); - kfree(dev->dvb); - } -#endif + tm6000_ir_fini(dev); if (dev->gpio.power_led) { switch (dev->model) { @@ -942,8 +942,8 @@ static void tm6000_usb_disconnect(struct usb_interface *interface) usb_put_dev(dev->udev); - tm6000_remove_from_devlist(dev); tm6000_close_extension(dev); + tm6000_remove_from_devlist(dev); mutex_unlock(&dev->lock); kfree(dev); diff --git a/drivers/staging/tm6000/tm6000-core.c b/drivers/staging/tm6000/tm6000-core.c index c3690e3..cded411 100644 --- a/drivers/staging/tm6000/tm6000-core.c +++ b/drivers/staging/tm6000/tm6000-core.c @@ -32,66 +32,64 @@ #define USB_TIMEOUT 5*HZ /* ms */ -int tm6000_read_write_usb (struct tm6000_core *dev, u8 req_type, u8 req, - u16 value, u16 index, u8 *buf, u16 len) +int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req, + u16 value, u16 index, u8 *buf, u16 len) { int ret, i; unsigned int pipe; - static int ini=0, last=0, n=0; - u8 *data=NULL; + static int ini = 0, last = 0, n = 0; + u8 *data = NULL; if (len) data = kzalloc(len, GFP_KERNEL); if (req_type & USB_DIR_IN) - pipe=usb_rcvctrlpipe(dev->udev, 0); + pipe = usb_rcvctrlpipe(dev->udev, 0); else { - pipe=usb_sndctrlpipe(dev->udev, 0); + pipe = usb_sndctrlpipe(dev->udev, 0); memcpy(data, buf, len); } if (tm6000_debug & V4L2_DEBUG_I2C) { if (!ini) - last=ini=jiffies; + last = ini = jiffies; printk("%06i (dev %p, pipe %08x): ", n, dev->udev, pipe); - printk( "%s: %06u ms %06u ms %02x %02x %02x %02x %02x %02x %02x %02x ", - (req_type & USB_DIR_IN)?" IN":"OUT", + printk("%s: %06u ms %06u ms %02x %02x %02x %02x %02x %02x %02x %02x ", + (req_type & USB_DIR_IN) ? " IN" : "OUT", jiffies_to_msecs(jiffies-last), jiffies_to_msecs(jiffies-ini), - req_type, req,value&0xff,value>>8, index&0xff, index>>8, - len&0xff, len>>8); - last=jiffies; + req_type, req, value&0xff, value>>8, index&0xff, + index>>8, len&0xff, len>>8); + last = jiffies; n++; - if ( !(req_type & USB_DIR_IN) ) { + if (!(req_type & USB_DIR_IN)) { printk(">>> "); - for (i=0;i<len;i++) { - printk(" %02x",buf[i]); - } + for (i = 0; i < len; i++) + printk(" %02x", buf[i]); printk("\n"); } } - ret = usb_control_msg(dev->udev, pipe, req, req_type, value, index, data, - len, USB_TIMEOUT); + ret = usb_control_msg(dev->udev, pipe, req, req_type, value, index, + data, len, USB_TIMEOUT); if (req_type & USB_DIR_IN) memcpy(buf, data, len); if (tm6000_debug & V4L2_DEBUG_I2C) { - if (ret<0) { + if (ret < 0) { if (req_type & USB_DIR_IN) - printk("<<< (len=%d)\n",len); + printk("<<< (len=%d)\n", len); printk("%s: Error #%d\n", __FUNCTION__, ret); } else if (req_type & USB_DIR_IN) { printk("<<< "); - for (i=0;i<len;i++) { - printk(" %02x",buf[i]); - } + for (i = 0; i < len; i++) + printk(" %02x", buf[i]); printk("\n"); } } @@ -103,52 +101,52 @@ int tm6000_read_write_usb (struct tm6000_core *dev, u8 req_type, u8 req, return ret; } -int tm6000_set_reg (struct tm6000_core *dev, u8 req, u16 value, u16 index) +int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index) { return - tm6000_read_write_usb (dev, USB_DIR_OUT | USB_TYPE_VENDOR, - req, value, index, NULL, 0); + tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR, + req, value, index, NULL, 0); } EXPORT_SYMBOL_GPL(tm6000_set_reg); -int tm6000_get_reg (struct tm6000_core *dev, u8 req, u16 value, u16 index) +int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index) { int rc; u8 buf[1]; - rc=tm6000_read_write_usb (dev, USB_DIR_IN | USB_TYPE_VENDOR, req, - value, index, buf, 1); + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 1); - if (rc<0) + if (rc < 0) return rc; return *buf; } EXPORT_SYMBOL_GPL(tm6000_get_reg); -int tm6000_get_reg16 (struct tm6000_core *dev, u8 req, u16 value, u16 index) +int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index) { int rc; u8 buf[2]; - rc=tm6000_read_write_usb (dev, USB_DIR_IN | USB_TYPE_VENDOR, req, - value, index, buf, 2); + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 2); - if (rc<0) + if (rc < 0) return rc; return buf[1]|buf[0]<<8; } -int tm6000_get_reg32 (struct tm6000_core *dev, u8 req, u16 value, u16 index) +int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index) { int rc; u8 buf[4]; - rc=tm6000_read_write_usb (dev, USB_DIR_IN | USB_TYPE_VENDOR, req, - value, index, buf, 4); + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 4); - if (rc<0) + if (rc < 0) return rc; return buf[3] | buf[2] << 8 | buf[1] << 16 | buf[0] << 24; @@ -188,7 +186,7 @@ void tm6000_set_fourcc_format(struct tm6000_core *dev) } } -int tm6000_init_analog_mode (struct tm6000_core *dev) +int tm6000_init_analog_mode(struct tm6000_core *dev) { if (dev->dev_type == TM6010) { int val; @@ -294,12 +292,10 @@ int tm6000_init_analog_mode (struct tm6000_core *dev) /* Enables soft reset */ tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01); - if (dev->scaler) { + if (dev->scaler) tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x20); - } else { - /* Enable Hfilter and disable TS Drop err */ + else /* Enable Hfilter and disable TS Drop err */ tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x80); - } tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x88); tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0x23); @@ -332,13 +328,13 @@ int tm6000_init_analog_mode (struct tm6000_core *dev) /*FIXME: Hack!!! */ struct v4l2_frequency f; mutex_lock(&dev->lock); - f.frequency=dev->freq; + f.frequency = dev->freq; v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); mutex_unlock(&dev->lock); msleep(100); - tm6000_set_standard (dev, &dev->norm); - tm6000_set_audio_bitrate (dev,48000); + tm6000_set_standard(dev, &dev->norm); + tm6000_set_audio_bitrate(dev, 48000); /* switch dvb led off */ if (dev->gpio.dvb_led) { @@ -349,7 +345,7 @@ int tm6000_init_analog_mode (struct tm6000_core *dev) return 0; } -int tm6000_init_digital_mode (struct tm6000_core *dev) +int tm6000_init_digital_mode(struct tm6000_core *dev) { if (dev->dev_type == TM6010) { int val; @@ -366,10 +362,8 @@ int tm6000_init_digital_mode (struct tm6000_core *dev) tm6000_set_reg(dev, TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xfc); tm6000_set_reg(dev, TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0xff); tm6000_set_reg(dev, TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfe); - tm6000_read_write_usb (dev, 0xc0, 0x0e, 0x00c2, 0x0008, buf, 2); - printk (KERN_INFO "buf %#x %#x \n", buf[0], buf[1]); - - + tm6000_read_write_usb(dev, 0xc0, 0x0e, 0x00c2, 0x0008, buf, 2); + printk(KERN_INFO"buf %#x %#x\n", buf[0], buf[1]); } else { tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x08); tm6000_set_reg(dev, TM6010_REQ07_RFF_SOFT_RESET, 0x00); @@ -377,7 +371,7 @@ int tm6000_init_digital_mode (struct tm6000_core *dev) tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_PULSE_CNT0, 0x08); tm6000_set_reg(dev, TM6010_REQ07_RE2_OUT_SEL2, 0x0c); tm6000_set_reg(dev, TM6010_REQ07_RE8_TYPESEL_MOS_I2S, 0xff); - tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00eb, 0xd8); + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0x00eb, 0xd8); tm6000_set_reg(dev, TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, 0x40); tm6000_set_reg(dev, TM6010_REQ07_RC1_TRESHOLD, 0xd0); tm6000_set_reg(dev, TM6010_REQ07_RC3_HSTART1, 0x09); @@ -388,14 +382,14 @@ int tm6000_init_digital_mode (struct tm6000_core *dev) tm6000_set_reg(dev, TM6010_REQ07_RE2_OUT_SEL2, 0x0c); tm6000_set_reg(dev, TM6010_REQ07_RE8_TYPESEL_MOS_I2S, 0xff); - tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00eb, 0x08); + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0x00eb, 0x08); msleep(50); - tm6000_set_reg (dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); msleep(50); - tm6000_set_reg (dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x01); + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x01); msleep(50); - tm6000_set_reg (dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); msleep(100); } @@ -407,6 +401,7 @@ int tm6000_init_digital_mode (struct tm6000_core *dev) return 0; } +EXPORT_SYMBOL(tm6000_init_digital_mode); struct reg_init { u8 req; @@ -566,9 +561,9 @@ struct reg_init tm6010_init_tab[] = { { TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0xff }, }; -int tm6000_init (struct tm6000_core *dev) +int tm6000_init(struct tm6000_core *dev) { - int board, rc=0, i, size; + int board, rc = 0, i, size; struct reg_init *tab; if (dev->dev_type == TM6010) { @@ -580,12 +575,12 @@ int tm6000_init (struct tm6000_core *dev) } /* Load board's initialization table */ - for (i=0; i< size; i++) { - rc= tm6000_set_reg (dev, tab[i].req, tab[i].reg, tab[i].val); - if (rc<0) { - printk (KERN_ERR "Error %i while setting req %d, " - "reg %d to value %d\n", rc, - tab[i].req,tab[i].reg, tab[i].val); + for (i = 0; i < size; i++) { + rc = tm6000_set_reg(dev, tab[i].req, tab[i].reg, tab[i].val); + if (rc < 0) { + printk(KERN_ERR "Error %i while setting req %d, " + "reg %d to value %d\n", rc, + tab[i].req, tab[i].reg, tab[i].val); return rc; } } @@ -593,12 +588,11 @@ int tm6000_init (struct tm6000_core *dev) msleep(5); /* Just to be conservative */ /* Check board version - maybe 10Moons specific */ - board=tm6000_get_reg32 (dev, REQ_40_GET_VERSION, 0, 0); - if (board >=0) { - printk (KERN_INFO "Board version = 0x%08x\n",board); - } else { - printk (KERN_ERR "Error %i while retrieving board version\n",board); - } + board = tm6000_get_reg32(dev, REQ_40_GET_VERSION, 0, 0); + if (board >= 0) + printk(KERN_INFO "Board version = 0x%08x\n", board); + else + printk(KERN_ERR "Error %i while retrieving board version\n", board); rc = tm6000_cards_setup(dev); @@ -609,23 +603,32 @@ int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate) { int val; - val=tm6000_get_reg (dev, REQ_07_SET_GET_AVREG, 0xeb, 0x0); -printk("Original value=%d\n",val); - if (val<0) + if (dev->dev_type == TM6010) { + val = tm6000_get_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, 0); + if (val < 0) + return val; + val = (val & 0xf0) | 0x1; /* 48 kHz, not muted */ + val = tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD, val); + if (val < 0) + return val; + } + + val = tm6000_get_reg(dev, REQ_07_SET_GET_AVREG, 0xeb, 0x0); + if (val < 0) return val; val &= 0x0f; /* Preserve the audio input control bits */ switch (bitrate) { case 44100: - val|=0xd0; - dev->audio_bitrate=bitrate; + val |= 0xd0; + dev->audio_bitrate = bitrate; break; case 48000: - val|=0x60; - dev->audio_bitrate=bitrate; + val |= 0x60; + dev->audio_bitrate = bitrate; break; } - val=tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xeb, val); + val = tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xeb, val); return val; } @@ -659,6 +662,23 @@ void tm6000_add_into_devlist(struct tm6000_core *dev) static LIST_HEAD(tm6000_extension_devlist); static DEFINE_MUTEX(tm6000_extension_devlist_lock); +int tm6000_call_fillbuf(struct tm6000_core *dev, enum tm6000_ops_type type, + char *buf, int size) +{ + struct tm6000_ops *ops = NULL; + + /* FIXME: tm6000_extension_devlist_lock should be a spinlock */ + + if (!list_empty(&tm6000_extension_devlist)) { + list_for_each_entry(ops, &tm6000_extension_devlist, next) { + if (ops->fillbuf && ops->type == type) + ops->fillbuf(dev, buf, size); + } + } + + return 0; +} + int tm6000_register_extension(struct tm6000_ops *ops) { struct tm6000_core *dev = NULL; @@ -667,10 +687,10 @@ int tm6000_register_extension(struct tm6000_ops *ops) mutex_lock(&tm6000_extension_devlist_lock); list_add_tail(&ops->next, &tm6000_extension_devlist); list_for_each_entry(dev, &tm6000_devlist, devlist) { - if (dev) - ops->init(dev); + ops->init(dev); + printk(KERN_INFO "%s: Initialized (%s) extension\n", + dev->name, ops->name); } - printk(KERN_INFO "tm6000: Initialized (%s) extension\n", ops->name); mutex_unlock(&tm6000_extension_devlist_lock); mutex_unlock(&tm6000_devlist_mutex); return 0; diff --git a/drivers/staging/tm6000/tm6000-dvb.c b/drivers/staging/tm6000/tm6000-dvb.c index 86c1c8b..f501edc 100644 --- a/drivers/staging/tm6000/tm6000-dvb.c +++ b/drivers/staging/tm6000/tm6000-dvb.c @@ -31,12 +31,25 @@ #include "tuner-xc2028.h" #include "xc5000.h" -static void inline print_err_status (struct tm6000_core *dev, - int packet, int status) +MODULE_DESCRIPTION("DVB driver extension module for tm5600/6000/6010 based TV cards"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); +MODULE_LICENSE("GPL"); + +MODULE_SUPPORTED_DEVICE("{{Trident, tm5600}," + "{{Trident, tm6000}," + "{{Trident, tm6010}"); + +static int debug; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug message"); + +static inline void print_err_status(struct tm6000_core *dev, + int packet, int status) { char *errmsg = "Unknown"; - switch(status) { + switch (status) { case -ENOENT: errmsg = "unlinked synchronuously"; break; @@ -62,7 +75,7 @@ static void inline print_err_status (struct tm6000_core *dev, errmsg = "Device does not respond"; break; } - if (packet<0) { + if (packet < 0) { dprintk(dev, 1, "URB status %d [%s].\n", status, errmsg); } else { @@ -74,19 +87,17 @@ static void inline print_err_status (struct tm6000_core *dev, static void tm6000_urb_received(struct urb *urb) { int ret; - struct tm6000_core* dev = urb->context; + struct tm6000_core *dev = urb->context; - if(urb->status != 0) { - print_err_status (dev,0,urb->status); - } - else if(urb->actual_length>0){ + if (urb->status != 0) + print_err_status(dev, 0, urb->status); + else if (urb->actual_length > 0) dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer, urb->actual_length); - } - if(dev->dvb->streams > 0) { + if (dev->dvb->streams > 0) { ret = usb_submit_urb(urb, GFP_ATOMIC); - if(ret < 0) { + if (ret < 0) { printk(KERN_ERR "tm6000: error %s\n", __FUNCTION__); kfree(urb->transfer_buffer); usb_free_urb(urb); @@ -100,7 +111,7 @@ int tm6000_start_stream(struct tm6000_core *dev) unsigned int pipe, size; struct tm6000_dvb *dvb = dev->dvb; - printk(KERN_INFO "tm6000: got start stream request %s\n",__FUNCTION__); + printk(KERN_INFO "tm6000: got start stream request %s\n", __FUNCTION__); if (dev->mode != TM6000_MODE_DIGITAL) { tm6000_init_digital_mode(dev); @@ -108,7 +119,7 @@ int tm6000_start_stream(struct tm6000_core *dev) } dvb->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); - if(dvb->bulk_urb == NULL) { + if (dvb->bulk_urb == NULL) { printk(KERN_ERR "tm6000: couldn't allocate urb\n"); return -ENOMEM; } @@ -120,7 +131,7 @@ int tm6000_start_stream(struct tm6000_core *dev) size = size * 15; /* 512 x 8 or 12 or 15 */ dvb->bulk_urb->transfer_buffer = kzalloc(size, GFP_KERNEL); - if(dvb->bulk_urb->transfer_buffer == NULL) { + if (dvb->bulk_urb->transfer_buffer == NULL) { usb_free_urb(dvb->bulk_urb); printk(KERN_ERR "tm6000: couldn't allocate transfer buffer!\n"); return -ENOMEM; @@ -132,20 +143,20 @@ int tm6000_start_stream(struct tm6000_core *dev) tm6000_urb_received, dev); ret = usb_clear_halt(dev->udev, pipe); - if(ret < 0) { - printk(KERN_ERR "tm6000: error %i in %s during pipe reset\n",ret,__FUNCTION__); + if (ret < 0) { + printk(KERN_ERR "tm6000: error %i in %s during pipe reset\n", + ret, __FUNCTION__); return ret; - } - else { + } else printk(KERN_ERR "tm6000: pipe resetted\n"); - } /* mutex_lock(&tm6000_driver.open_close_mutex); */ ret = usb_submit_urb(dvb->bulk_urb, GFP_KERNEL); /* mutex_unlock(&tm6000_driver.open_close_mutex); */ if (ret) { - printk(KERN_ERR "tm6000: submit of urb failed (error=%i)\n",ret); + printk(KERN_ERR "tm6000: submit of urb failed (error=%i)\n", + ret); kfree(dvb->bulk_urb->transfer_buffer); usb_free_urb(dvb->bulk_urb); @@ -159,10 +170,10 @@ void tm6000_stop_stream(struct tm6000_core *dev) { struct tm6000_dvb *dvb = dev->dvb; - if(dvb->bulk_urb) { - printk (KERN_INFO "urb killing\n"); + if (dvb->bulk_urb) { + printk(KERN_INFO "urb killing\n"); usb_kill_urb(dvb->bulk_urb); - printk (KERN_INFO "urb buffer free\n"); + printk(KERN_INFO "urb buffer free\n"); kfree(dvb->bulk_urb->transfer_buffer); usb_free_urb(dvb->bulk_urb); dvb->bulk_urb = NULL; @@ -174,35 +185,34 @@ int tm6000_start_feed(struct dvb_demux_feed *feed) struct dvb_demux *demux = feed->demux; struct tm6000_core *dev = demux->priv; struct tm6000_dvb *dvb = dev->dvb; - printk(KERN_INFO "tm6000: got start feed request %s\n",__FUNCTION__); + printk(KERN_INFO "tm6000: got start feed request %s\n", __FUNCTION__); mutex_lock(&dvb->mutex); - if(dvb->streams == 0) { + if (dvb->streams == 0) { dvb->streams = 1; /* mutex_init(&tm6000_dev->streming_mutex); */ tm6000_start_stream(dev); - } - else { + } else ++(dvb->streams); - } mutex_unlock(&dvb->mutex); return 0; } -int tm6000_stop_feed(struct dvb_demux_feed *feed) { +int tm6000_stop_feed(struct dvb_demux_feed *feed) +{ struct dvb_demux *demux = feed->demux; struct tm6000_core *dev = demux->priv; struct tm6000_dvb *dvb = dev->dvb; - printk(KERN_INFO "tm6000: got stop feed request %s\n",__FUNCTION__); + printk(KERN_INFO "tm6000: got stop feed request %s\n", __FUNCTION__); mutex_lock(&dvb->mutex); - printk (KERN_INFO "stream %#x\n", dvb->streams); + printk(KERN_INFO "stream %#x\n", dvb->streams); --(dvb->streams); - if(dvb->streams == 0) { - printk (KERN_INFO "stop stream\n"); + if (dvb->streams == 0) { + printk(KERN_INFO "stop stream\n"); tm6000_stop_stream(dev); /* mutex_destroy(&tm6000_dev->streaming_mutex); */ } @@ -216,9 +226,9 @@ int tm6000_dvb_attach_frontend(struct tm6000_core *dev) { struct tm6000_dvb *dvb = dev->dvb; - if(dev->caps.has_zl10353) { - struct zl10353_config config = - {.demod_address = dev->demod_addr, + if (dev->caps.has_zl10353) { + struct zl10353_config config = { + .demod_address = dev->demod_addr, .no_tuner = 1, .parallel_ts = 1, .if2 = 45700, @@ -227,8 +237,7 @@ int tm6000_dvb_attach_frontend(struct tm6000_core *dev) dvb->frontend = dvb_attach(zl10353_attach, &config, &dev->i2c_adap); - } - else { + } else { printk(KERN_ERR "tm6000: no frontend defined for the device!\n"); return -1; } @@ -238,7 +247,7 @@ int tm6000_dvb_attach_frontend(struct tm6000_core *dev) DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); -int tm6000_dvb_register(struct tm6000_core *dev) +int register_dvb(struct tm6000_core *dev) { int ret = -1; struct tm6000_dvb *dvb = dev->dvb; @@ -249,13 +258,13 @@ int tm6000_dvb_register(struct tm6000_core *dev) /* attach the frontend */ ret = tm6000_dvb_attach_frontend(dev); - if(ret < 0) { + if (ret < 0) { printk(KERN_ERR "tm6000: couldn't attach the frontend!\n"); goto err; } ret = dvb_register_adapter(&dvb->adapter, "Trident TVMaster 6000 DVB-T", - THIS_MODULE, &dev->udev->dev, adapter_nr); + THIS_MODULE, &dev->udev->dev, adapter_nr); dvb->adapter.priv = dev; if (dvb->frontend) { @@ -308,9 +317,8 @@ int tm6000_dvb_register(struct tm6000_core *dev) break; } } - } else { + } else printk(KERN_ERR "tm6000: no frontend found\n"); - } dvb->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING; @@ -321,7 +329,7 @@ int tm6000_dvb_register(struct tm6000_core *dev) dvb->demux.stop_feed = tm6000_stop_feed; dvb->demux.write_to_decoder = NULL; ret = dvb_dmx_init(&dvb->demux); - if(ret < 0) { + if (ret < 0) { printk("tm6000: dvb_dmx_init failed (errno = %d)\n", ret); goto frontend_err; } @@ -331,7 +339,7 @@ int tm6000_dvb_register(struct tm6000_core *dev) dvb->dmxdev.capabilities = 0; ret = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); - if(ret < 0) { + if (ret < 0) { printk("tm6000: dvb_dmxdev_init failed (errno = %d)\n", ret); goto dvb_dmx_err; } @@ -341,7 +349,7 @@ int tm6000_dvb_register(struct tm6000_core *dev) dvb_dmx_err: dvb_dmx_release(&dvb->demux); frontend_err: - if(dvb->frontend) { + if (dvb->frontend) { dvb_frontend_detach(dvb->frontend); dvb_unregister_frontend(dvb->frontend); } @@ -351,11 +359,11 @@ err: return ret; } -void tm6000_dvb_unregister(struct tm6000_core *dev) +void unregister_dvb(struct tm6000_core *dev) { struct tm6000_dvb *dvb = dev->dvb; - if(dvb->bulk_urb != NULL) { + if (dvb->bulk_urb != NULL) { struct urb *bulk_urb = dvb->bulk_urb; kfree(bulk_urb->transfer_buffer); @@ -365,7 +373,7 @@ void tm6000_dvb_unregister(struct tm6000_core *dev) } /* mutex_lock(&tm6000_driver.open_close_mutex); */ - if(dvb->frontend) { + if (dvb->frontend) { dvb_frontend_detach(dvb->frontend); dvb_unregister_frontend(dvb->frontend); } @@ -375,5 +383,70 @@ void tm6000_dvb_unregister(struct tm6000_core *dev) dvb_unregister_adapter(&dvb->adapter); mutex_destroy(&dvb->mutex); /* mutex_unlock(&tm6000_driver.open_close_mutex); */ +} +static int dvb_init(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb; + int rc; + + if (!dev) + return 0; + + if (!dev->caps.has_dvb) + return 0; + + dvb = kzalloc(sizeof(struct tm6000_dvb), GFP_KERNEL); + if (!dvb) { + printk(KERN_INFO "Cannot allocate memory\n"); + return -ENOMEM; + } + + dev->dvb = dvb; + + rc = register_dvb(dev); + if (rc < 0) { + kfree(dvb); + dev->dvb = NULL; + return 0; + } + + return 0; +} + +static int dvb_fini(struct tm6000_core *dev) +{ + if (!dev) + return 0; + + if (!dev->caps.has_dvb) + return 0; + + if (dev->dvb) { + unregister_dvb(dev); + kfree(dev->dvb); + dev->dvb = NULL; + } + + return 0; } + +static struct tm6000_ops dvb_ops = { + .type = TM6000_DVB, + .name = "TM6000 dvb Extension", + .init = dvb_init, + .fini = dvb_fini, +}; + +static int __init tm6000_dvb_register(void) +{ + return tm6000_register_extension(&dvb_ops); +} + +static void __exit tm6000_dvb_unregister(void) +{ + tm6000_unregister_extension(&dvb_ops); +} + +module_init(tm6000_dvb_register); +module_exit(tm6000_dvb_unregister); diff --git a/drivers/staging/tm6000/tm6000-i2c.c b/drivers/staging/tm6000/tm6000-i2c.c index 94ff489..79bc67f 100644 --- a/drivers/staging/tm6000/tm6000-i2c.c +++ b/drivers/staging/tm6000/tm6000-i2c.c @@ -40,7 +40,7 @@ static unsigned int i2c_debug = 0; module_param(i2c_debug, int, 0644); MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); -#define i2c_dprintk(lvl,fmt, args...) if (i2c_debug>=lvl) do{ \ +#define i2c_dprintk(lvl, fmt, args...) if (i2c_debug >= lvl) do { \ printk(KERN_DEBUG "%s at %s: " fmt, \ dev->name, __FUNCTION__ , ##args); } while (0) @@ -171,7 +171,7 @@ static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap, return 0; for (i = 0; i < num; i++) { addr = (msgs[i].addr << 1) & 0xff; - i2c_dprintk(2,"%s %s addr=0x%x len=%d:", + i2c_dprintk(2, "%s %s addr=0x%x len=%d:", (msgs[i].flags & I2C_M_RD) ? "read" : "write", i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); if (msgs[i].flags & I2C_M_RD) { @@ -235,7 +235,7 @@ static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap, return num; err: - i2c_dprintk(2," ERROR: %i\n", rc); + i2c_dprintk(2, " ERROR: %i\n", rc); return rc; } @@ -266,11 +266,10 @@ static int tm6000_i2c_eeprom(struct tm6000_core *dev, if (0 == (i % 16)) printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i); printk(" %02x", eedata[i]); - if ((eedata[i] >= ' ') && (eedata[i] <= 'z')) { + if ((eedata[i] >= ' ') && (eedata[i] <= 'z')) bytes[i%16] = eedata[i]; - } else { - bytes[i%16]='.'; - } + else + bytes[i%16] = '.'; i++; @@ -305,15 +304,15 @@ static u32 functionality(struct i2c_adapter *adap) } #define mass_write(addr, reg, data...) \ - { const static u8 _val[] = data; \ - rc=tm6000_read_write_usb(dev,USB_DIR_OUT | USB_TYPE_VENDOR, \ - REQ_16_SET_GET_I2C_WR1_RDN,(reg<<8)+addr, 0x00, (u8 *) _val, \ + { static const u8 _val[] = data; \ + rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR, \ + REQ_16_SET_GET_I2C_WR1_RDN, (reg<<8)+addr, 0x00, (u8 *) _val, \ ARRAY_SIZE(_val)); \ - if (rc<0) { \ - printk(KERN_ERR "Error on line %d: %d\n",__LINE__,rc); \ + if (rc < 0) { \ + printk(KERN_ERR "Error on line %d: %d\n", __LINE__, rc); \ return rc; \ } \ - msleep (10); \ + msleep(10); \ } static struct i2c_algorithm tm6000_algo = { diff --git a/drivers/staging/tm6000/tm6000-input.c b/drivers/staging/tm6000/tm6000-input.c new file mode 100644 index 0000000..32f7a0a --- /dev/null +++ b/drivers/staging/tm6000/tm6000-input.c @@ -0,0 +1,364 @@ +/* + tm6000-input.c - driver for TM5600/TM6000/TM6010 USB video capture devices + + Copyright (C) 2010 Stefan Ringel <stefan.ringel@arcor.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 version 2 + + 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/input.h> +#include <linux/usb.h> + +#include <media/ir-core.h> +#include <media/ir-common.h> + +#include "tm6000.h" +#include "tm6000-regs.h" + +static unsigned int ir_debug; +module_param(ir_debug, int, 0644); +MODULE_PARM_DESC(ir_debug, "enable debug message [IR]"); + +static unsigned int enable_ir = 1; +module_param(enable_ir, int, 0644); +MODULE_PARM_DESC(enable_ir, "enable ir (default is enable"); + +#undef dprintk + +#define dprintk(fmt, arg...) \ + if (ir_debug) { \ + printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ + } + +struct tm6000_ir_poll_result { + u8 rc_data[4]; +}; + +struct tm6000_IR { + struct tm6000_core *dev; + struct ir_input_dev *input; + struct ir_input_state ir; + char name[32]; + char phys[32]; + + /* poll expernal decoder */ + int polling; + struct delayed_work work; + u8 wait:1; + struct urb *int_urb; + u8 *urb_data; + u8 key:1; + + int (*get_key) (struct tm6000_IR *, struct tm6000_ir_poll_result *); + + /* IR device properties */ + struct ir_dev_props props; +}; + + +void tm6000_ir_wait(struct tm6000_core *dev, u8 state) +{ + struct tm6000_IR *ir = dev->ir; + + if (!dev->ir) + return; + + if (state) + ir->wait = 1; + else + ir->wait = 0; +} + + +static int tm6000_ir_config(struct tm6000_IR *ir) +{ + struct tm6000_core *dev = ir->dev; + u8 buf[10]; + int rc; + + /* hack */ + buf[0] = 0xff; + buf[1] = 0xff; + buf[2] = 0xf2; + buf[3] = 0x2b; + buf[4] = 0x20; + buf[5] = 0x35; + buf[6] = 0x60; + buf[7] = 0x04; + buf[8] = 0xc0; + buf[9] = 0x08; + + rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_00_SET_IR_VALUE, 0, 0, buf, 0x0a); + msleep(100); + + if (rc < 0) { + printk(KERN_INFO "IR configuration failed"); + return rc; + } + return 0; +} + +static void tm6000_ir_urb_received(struct urb *urb) +{ + struct tm6000_core *dev = urb->context; + struct tm6000_IR *ir = dev->ir; + int rc; + + if (urb->status != 0) + printk(KERN_INFO "not ready\n"); + else if (urb->actual_length > 0) + memcpy(ir->urb_data, urb->transfer_buffer, urb->actual_length); + + dprintk("data %02x %02x %02x %02x\n", ir->urb_data[0], + ir->urb_data[1], ir->urb_data[2], ir->urb_data[3]); + + ir->key = 1; + + rc = usb_submit_urb(urb, GFP_ATOMIC); +} + +static int default_polling_getkey(struct tm6000_IR *ir, + struct tm6000_ir_poll_result *poll_result) +{ + struct tm6000_core *dev = ir->dev; + int rc; + u8 buf[2]; + + if (ir->wait && !&dev->int_in) { + poll_result->rc_data[0] = 0xff; + return 0; + } + + if (&dev->int_in) { + poll_result->rc_data[0] = ir->urb_data[0]; + poll_result->rc_data[1] = ir->urb_data[1]; + } else { + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0); + msleep(10); + tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 1); + msleep(10); + + rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, REQ_02_GET_IR_CODE, 0, 0, buf, 1); + + msleep(10); + + dprintk("read data=%02x\n", buf[0]); + if (rc < 0) + return rc; + + poll_result->rc_data[0] = buf[0]; + } + return 0; +} + +static void tm6000_ir_handle_key(struct tm6000_IR *ir) +{ + int result; + struct tm6000_ir_poll_result poll_result; + + /* read the registers containing the IR status */ + result = ir->get_key(ir, &poll_result); + if (result < 0) { + printk(KERN_INFO "ir->get_key() failed %d\n", result); + return; + } + + dprintk("ir->get_key result data=%02x %02x\n", + poll_result.rc_data[0], poll_result.rc_data[1]); + + if (poll_result.rc_data[0] != 0xff && ir->key == 1) { + ir_input_keydown(ir->input->input_dev, &ir->ir, + poll_result.rc_data[0] | poll_result.rc_data[1] << 8); + + ir_input_nokey(ir->input->input_dev, &ir->ir); + ir->key = 0; + } + return; +} + +static void tm6000_ir_work(struct work_struct *work) +{ + struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work); + + tm6000_ir_handle_key(ir); + schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); +} + +static int tm6000_ir_start(void *priv) +{ + struct tm6000_IR *ir = priv; + + INIT_DELAYED_WORK(&ir->work, tm6000_ir_work); + schedule_delayed_work(&ir->work, 0); + + return 0; +} + +static void tm6000_ir_stop(void *priv) +{ + struct tm6000_IR *ir = priv; + + cancel_delayed_work_sync(&ir->work); +} + +int tm6000_ir_change_protocol(void *priv, u64 ir_type) +{ + struct tm6000_IR *ir = priv; + + ir->get_key = default_polling_getkey; + + tm6000_ir_config(ir); + /* TODO */ + return 0; +} + +int tm6000_ir_init(struct tm6000_core *dev) +{ + struct tm6000_IR *ir; + struct ir_input_dev *ir_input_dev; + int err = -ENOMEM; + int pipe, size, rc; + + if (!enable_ir) + return -ENODEV; + + if (!dev->caps.has_remote) + return 0; + + if (!dev->ir_codes) + return 0; + + ir = kzalloc(sizeof(*ir), GFP_KERNEL); + ir_input_dev = kzalloc(sizeof(*ir_input_dev), GFP_KERNEL); + ir_input_dev->input_dev = input_allocate_device(); + if (!ir || !ir_input_dev || !ir_input_dev->input_dev) + goto err_out_free; + + /* record handles to ourself */ + ir->dev = dev; + dev->ir = ir; + + ir->input = ir_input_dev; + + /* input einrichten */ + ir->props.allowed_protos = IR_TYPE_RC5 | IR_TYPE_NEC; + ir->props.priv = ir; + ir->props.change_protocol = tm6000_ir_change_protocol; + ir->props.open = tm6000_ir_start; + ir->props.close = tm6000_ir_stop; + ir->props.driver_type = RC_DRIVER_SCANCODE; + + ir->polling = 50; + + snprintf(ir->name, sizeof(ir->name), "tm5600/60x0 IR (%s)", + dev->name); + + usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); + strlcat(ir->phys, "/input0", sizeof(ir->phys)); + + tm6000_ir_change_protocol(ir, IR_TYPE_UNKNOWN); + err = ir_input_init(ir_input_dev->input_dev, &ir->ir, IR_TYPE_OTHER); + if (err < 0) + goto err_out_free; + + ir_input_dev->input_dev->name = ir->name; + ir_input_dev->input_dev->phys = ir->phys; + ir_input_dev->input_dev->id.bustype = BUS_USB; + ir_input_dev->input_dev->id.version = 1; + ir_input_dev->input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); + ir_input_dev->input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct); + + ir_input_dev->input_dev->dev.parent = &dev->udev->dev; + + if (&dev->int_in) { + dprintk("IR over int\n"); + + ir->int_urb = usb_alloc_urb(0, GFP_KERNEL); + + pipe = usb_rcvintpipe(dev->udev, + dev->int_in.endp->desc.bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK); + + size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe)); + dprintk("IR max size: %d\n", size); + + ir->int_urb->transfer_buffer = kzalloc(size, GFP_KERNEL); + if (ir->int_urb->transfer_buffer == NULL) { + usb_free_urb(ir->int_urb); + goto err_out_stop; + } + dprintk("int interval: %d\n", dev->int_in.endp->desc.bInterval); + usb_fill_int_urb(ir->int_urb, dev->udev, pipe, + ir->int_urb->transfer_buffer, size, + tm6000_ir_urb_received, dev, + dev->int_in.endp->desc.bInterval); + rc = usb_submit_urb(ir->int_urb, GFP_KERNEL); + if (rc) { + kfree(ir->int_urb->transfer_buffer); + usb_free_urb(ir->int_urb); + err = rc; + goto err_out_stop; + } + ir->urb_data = kzalloc(size, GFP_KERNEL); + } + + /* ir register */ + err = ir_input_register(ir->input->input_dev, dev->ir_codes, + &ir->props, "tm6000"); + if (err) + goto err_out_stop; + + return 0; + +err_out_stop: + dev->ir = NULL; +err_out_free: + kfree(ir_input_dev); + kfree(ir); + return err; +} + +int tm6000_ir_fini(struct tm6000_core *dev) +{ + struct tm6000_IR *ir = dev->ir; + + /* skip detach on non attached board */ + + if (!ir) + return 0; + + ir_input_unregister(ir->input->input_dev); + + if (ir->int_urb) { + usb_kill_urb(ir->int_urb); + kfree(ir->int_urb->transfer_buffer); + usb_free_urb(ir->int_urb); + ir->int_urb = NULL; + kfree(ir->urb_data); + ir->urb_data = NULL; + } + + kfree(ir->input); + ir->input = NULL; + kfree(ir); + dev->ir = NULL; + + return 0; +} diff --git a/drivers/staging/tm6000/tm6000-stds.c b/drivers/staging/tm6000/tm6000-stds.c index b3564f6..6bf4a73 100644 --- a/drivers/staging/tm6000/tm6000-stds.c +++ b/drivers/staging/tm6000/tm6000-stds.c @@ -77,7 +77,7 @@ static struct tm6000_std_tv_settings tv_stds[] = { {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00}, - {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31}, {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e}, {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83}, {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a}, @@ -135,7 +135,7 @@ static struct tm6000_std_tv_settings tv_stds[] = { {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02}, - {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31}, {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e}, {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91}, {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f}, @@ -193,7 +193,7 @@ static struct tm6000_std_tv_settings tv_stds[] = { {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02}, - {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31}, {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25}, {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5}, {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63}, @@ -251,7 +251,7 @@ static struct tm6000_std_tv_settings tv_stds[] = { {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02}, - {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31}, {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24}, {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92}, {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8}, @@ -308,7 +308,7 @@ static struct tm6000_std_tv_settings tv_stds[] = { {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f}, {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00}, - {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31}, {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e}, {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b}, {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2}, @@ -354,7 +354,7 @@ static struct tm6000_std_settings composite_stds[] = { {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00}, - {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31}, {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e}, {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83}, {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a}, @@ -396,7 +396,7 @@ static struct tm6000_std_settings composite_stds[] = { {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02}, - {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31}, {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e}, {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91}, {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f}, @@ -438,7 +438,7 @@ static struct tm6000_std_settings composite_stds[] = { {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02}, - {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31}, {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25}, {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5}, {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63}, @@ -480,7 +480,7 @@ static struct tm6000_std_settings composite_stds[] = { {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x02}, - {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31}, {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24}, {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92}, {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8}, @@ -521,7 +521,7 @@ static struct tm6000_std_settings composite_stds[] = { {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f}, {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x00}, - {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31}, {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e}, {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b}, {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xa2}, @@ -567,7 +567,7 @@ static struct tm6000_std_settings svideo_stds[] = { {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04}, - {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31}, {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e}, {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x83}, {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x0a}, @@ -609,7 +609,7 @@ static struct tm6000_std_settings svideo_stds[] = { {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04}, - {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31}, {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e}, {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x91}, {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x1f}, @@ -651,7 +651,7 @@ static struct tm6000_std_settings svideo_stds[] = { {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x04}, - {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x00}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30}, {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x25}, {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0xd5}, {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0x63}, @@ -693,7 +693,7 @@ static struct tm6000_std_settings svideo_stds[] = { {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0e}, {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03}, - {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x01}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x31}, {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x24}, {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x92}, {TM6010_REQ07_R1A_CHROMA_DTO_INCREMENT1, 0xe8}, @@ -734,7 +734,7 @@ static struct tm6000_std_settings svideo_stds[] = { {TM6010_REQ07_R01_VIDEO_CONTROL1, 0x0f}, {TM6010_REQ07_R02_VIDEO_CONTROL2, 0x5f}, {TM6010_REQ07_R03_YC_SEP_CONTROL, 0x03}, - {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x00}, + {TM6010_REQ07_R07_OUTPUT_CONTROL, 0x30}, {TM6010_REQ07_R17_HLOOP_MAXSTATE, 0x8b}, {TM6010_REQ07_R18_CHROMA_DTO_INCREMENT3, 0x1e}, {TM6010_REQ07_R19_CHROMA_DTO_INCREMENT2, 0x8b}, @@ -763,11 +763,11 @@ static struct tm6000_std_settings svideo_stds[] = { void tm6000_get_std_res(struct tm6000_core *dev) { /* Currently, those are the only supported resoltions */ - if (dev->norm & V4L2_STD_525_60) { + if (dev->norm & V4L2_STD_525_60) dev->height = 480; - } else { + else dev->height = 576; - } + dev->width = 720; } diff --git a/drivers/staging/tm6000/tm6000-usb-isoc.h b/drivers/staging/tm6000/tm6000-usb-isoc.h index 5a5049a..138716a 100644 --- a/drivers/staging/tm6000/tm6000-usb-isoc.h +++ b/drivers/staging/tm6000/tm6000-usb-isoc.h @@ -39,7 +39,7 @@ struct usb_isoc_ctl { int pos, size, pktsize; /* Last field: ODD or EVEN? */ - int field; + int vfield; /* Stores incomplete commands */ u32 tmp_buf; @@ -47,7 +47,4 @@ struct usb_isoc_ctl { /* Stores already requested buffers */ struct tm6000_buffer *buf; - - /* Stores the number of received fields */ - int nfields; }; diff --git a/drivers/staging/tm6000/tm6000-video.c b/drivers/staging/tm6000/tm6000-video.c index 56fa371..ce0a089 100644 --- a/drivers/staging/tm6000/tm6000-video.c +++ b/drivers/staging/tm6000/tm6000-video.c @@ -56,6 +56,7 @@ static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ /* Debug level */ int tm6000_debug; +EXPORT_SYMBOL_GPL(tm6000_debug); /* supported controls */ static struct v4l2_queryctrl tm6000_qctrl[] = { @@ -149,8 +150,6 @@ static inline void get_next_buf(struct tm6000_dmaqueue *dma_q, /* Cleans up buffer - Usefull for testing for frame/URB loss */ outp = videobuf_to_vmalloc(&(*buf)->vb); -// if (outp) -// memset(outp, 0, (*buf)->vb.size); return; } @@ -186,236 +185,152 @@ const char *tm6000_msg_type[] = { /* * Identify the tm5600/6000 buffer header type and properly handles */ -static int copy_packet(struct urb *urb, u32 header, u8 **ptr, u8 *endp, - u8 *out_p, struct tm6000_buffer **buf) -{ - struct tm6000_dmaqueue *dma_q = urb->context; - struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); - u8 c; - unsigned int cmd, cpysize, pktsize, size, field, block, line, pos = 0; - int rc = 0; - /* FIXME: move to tm6000-isoc */ - static int last_line = -2, start_line = -2, last_field = -2; - - /* FIXME: this is the hardcoded window size - */ - unsigned int linewidth = (*buf)->vb.width << 1; - - if (!dev->isoc_ctl.cmd) { - c = (header >> 24) & 0xff; - - /* split the header fields */ - size = ((header & 0x7e) << 1); - - if (size > 0) - size -= 4; - - block = (header >> 7) & 0xf; - field = (header >> 11) & 0x1; - line = (header >> 12) & 0x1ff; - cmd = (header >> 21) & 0x7; - - /* Validates header fields */ - if(size > TM6000_URB_MSG_LEN) - size = TM6000_URB_MSG_LEN; - - if (cmd == TM6000_URB_MSG_VIDEO) { - if ((block+1)*TM6000_URB_MSG_LEN>linewidth) - cmd = TM6000_URB_MSG_ERR; - - /* FIXME: Mounts the image as field0+field1 - * It should, instead, check if the user selected - * entrelaced or non-entrelaced mode - */ - pos = ((line << 1) - field - 1) * linewidth + - block * TM6000_URB_MSG_LEN; - - /* Don't allow to write out of the buffer */ - if (pos+TM6000_URB_MSG_LEN > (*buf)->vb.size) { - dprintk(dev, V4L2_DEBUG_ISOC, - "ERR: size=%d, num=%d, line=%d, " - "field=%d\n", - size, block, line, field); - - cmd = TM6000_URB_MSG_ERR; - } - } else { - pos=0; - } - - /* Prints debug info */ - dprintk(dev, V4L2_DEBUG_ISOC, "size=%d, num=%d, " - " line=%d, field=%d\n", - size, block, line, field); - - if ((last_line!=line)&&(last_line+1!=line) && - (cmd != TM6000_URB_MSG_ERR) ) { - if (cmd != TM6000_URB_MSG_VIDEO) { - dprintk(dev, V4L2_DEBUG_ISOC, "cmd=%d, " - "size=%d, num=%d, line=%d, field=%d\n", - cmd, size, block, line, field); - } - if (start_line<0) - start_line=last_line; - /* Prints debug info */ - dprintk(dev, V4L2_DEBUG_ISOC, "lines= %d-%d, " - "field=%d\n", - start_line, last_line, field); - - if ((start_line<6 && last_line>200) && - (last_field != field) ) { - - dev->isoc_ctl.nfields++; - if (dev->isoc_ctl.nfields>=2) { - dev->isoc_ctl.nfields=0; - - /* Announces that a new buffer were filled */ - buffer_filled (dev, dma_q, *buf); - dprintk(dev, V4L2_DEBUG_ISOC, - "new buffer filled\n"); - get_next_buf (dma_q, buf); - if (!*buf) - return rc; - out_p = videobuf_to_vmalloc(&((*buf)->vb)); - if (!out_p) - return rc; - - pos = dev->isoc_ctl.pos = 0; - } - } - - start_line=line; - last_field=field; - } - if (cmd == TM6000_URB_MSG_VIDEO) - last_line = line; - - pktsize = TM6000_URB_MSG_LEN; - } else { - /* Continue the last copy */ - cmd = dev->isoc_ctl.cmd; - size= dev->isoc_ctl.size; - pos = dev->isoc_ctl.pos; - pktsize = dev->isoc_ctl.pktsize; - } - - cpysize = (endp-(*ptr) > size) ? size : endp - *ptr; - - if (cpysize) { - /* handles each different URB message */ - switch(cmd) { - case TM6000_URB_MSG_VIDEO: - /* Fills video buffer */ - memcpy(&out_p[pos], *ptr, cpysize); - break; - case TM6000_URB_MSG_PTS: - break; - case TM6000_URB_MSG_AUDIO: - /* Need some code to process audio */ - printk ("%ld: cmd=%s, size=%d\n", jiffies, - tm6000_msg_type[cmd],size); - break; - case TM6000_URB_MSG_VBI: - break; - default: - dprintk (dev, V4L2_DEBUG_ISOC, "cmd=%s, size=%d\n", - tm6000_msg_type[cmd],size); - } - } - if (cpysize<size) { - /* End of URB packet, but cmd processing is not - * complete. Preserve the state for a next packet - */ - dev->isoc_ctl.pos = pos+cpysize; - dev->isoc_ctl.size= size-cpysize; - dev->isoc_ctl.cmd = cmd; - dev->isoc_ctl.pktsize = pktsize-cpysize; - (*ptr)+=cpysize; - } else { - dev->isoc_ctl.cmd = 0; - (*ptr)+=pktsize; - } - - return rc; -} - static int copy_streams(u8 *data, unsigned long len, struct urb *urb) { struct tm6000_dmaqueue *dma_q = urb->context; struct tm6000_core *dev= container_of(dma_q,struct tm6000_core,vidq); - u8 *ptr=data, *endp=data+len; + u8 *ptr=data, *endp=data+len, c; unsigned long header=0; int rc=0; - struct tm6000_buffer *buf; - char *outp = NULL; - - get_next_buf(dma_q, &buf); - if (buf) - outp = videobuf_to_vmalloc(&buf->vb); + unsigned int cmd, cpysize, pktsize, size, field, block, line, pos = 0; + struct tm6000_buffer *vbuf; + char *voutp = NULL; + unsigned int linewidth; - if (!outp) + /* get video buffer */ + get_next_buf (dma_q, &vbuf); + if (!vbuf) + return rc; + voutp = videobuf_to_vmalloc(&vbuf->vb); + if (!voutp) return 0; - for (ptr=data; ptr<endp;) { + for (ptr = data; ptr < endp;) { if (!dev->isoc_ctl.cmd) { - u8 *p=(u8 *)&dev->isoc_ctl.tmp_buf; - /* FIXME: This seems very complex - * It just recovers up to 3 bytes of the header that - * might be at the previous packet - */ - if (dev->isoc_ctl.tmp_buf_len) { - while (dev->isoc_ctl.tmp_buf_len) { - if ( *(ptr+3-dev->isoc_ctl.tmp_buf_len) == 0x47) { - break; - } - p++; - dev->isoc_ctl.tmp_buf_len--; - } - if (dev->isoc_ctl.tmp_buf_len) { - memcpy(&header, p, - dev->isoc_ctl.tmp_buf_len); - memcpy((u8 *)&header + + /* Header */ + if (dev->isoc_ctl.tmp_buf_len > 0) { + /* from last urb or packet */ + header = dev->isoc_ctl.tmp_buf; + if (4 - dev->isoc_ctl.tmp_buf_len > 0) { + memcpy ((u8 *)&header + dev->isoc_ctl.tmp_buf_len, ptr, 4 - dev->isoc_ctl.tmp_buf_len); ptr += 4 - dev->isoc_ctl.tmp_buf_len; - goto HEADER; } - } - /* Seek for sync */ - for (;ptr<endp-3;ptr++) { - if (*(ptr+3)==0x47) - break; + dev->isoc_ctl.tmp_buf_len = 0; + } else { + if (ptr + 3 >= endp) { + /* have incomplete header */ + dev->isoc_ctl.tmp_buf_len = endp - ptr; + memcpy (&dev->isoc_ctl.tmp_buf, ptr, + dev->isoc_ctl.tmp_buf_len); + return rc; + } + /* Seek for sync */ + for (; ptr < endp - 3; ptr++) { + if (*(ptr + 3) == 0x47) + break; + } + /* Get message header */ + header = *(unsigned long *)ptr; + ptr += 4; } - if (ptr+3>=endp) { - dev->isoc_ctl.tmp_buf_len=endp-ptr; - memcpy (&dev->isoc_ctl.tmp_buf,ptr, - dev->isoc_ctl.tmp_buf_len); - dev->isoc_ctl.cmd=0; - return rc; + /* split the header fields */ + c = (header >> 24) & 0xff; + size = ((header & 0x7e) << 1); + if (size > 0) + size -= 4; + block = (header >> 7) & 0xf; + field = (header >> 11) & 0x1; + line = (header >> 12) & 0x1ff; + cmd = (header >> 21) & 0x7; + /* Validates haeder fields */ + if (size > TM6000_URB_MSG_LEN) + size = TM6000_URB_MSG_LEN; + pktsize = TM6000_URB_MSG_LEN; + /* calculate position in buffer + * and change the buffer + */ + switch (cmd) { + case TM6000_URB_MSG_VIDEO: + if ((dev->isoc_ctl.vfield != field) && + (field == 1)) { + /* Announces that a new buffer + * were filled + */ + buffer_filled (dev, dma_q, vbuf); + dprintk (dev, V4L2_DEBUG_ISOC, + "new buffer filled\n"); + get_next_buf (dma_q, &vbuf); + if (!vbuf) + return rc; + voutp = videobuf_to_vmalloc (&vbuf->vb); + if (!voutp) + return rc; + memset(voutp, 0, vbuf->vb.size); + } + linewidth = vbuf->vb.width << 1; + pos = ((line << 1) - field - 1) * linewidth + + block * TM6000_URB_MSG_LEN; + /* Don't allow to write out of the buffer */ + if (pos + size > vbuf->vb.size) + cmd = TM6000_URB_MSG_ERR; + dev->isoc_ctl.vfield = field; + break; + case TM6000_URB_MSG_VBI: + break; + case TM6000_URB_MSG_AUDIO: + case TM6000_URB_MSG_PTS: + size = pktsize; /* Size is always 180 bytes */ + break; } - - /* Get message header */ - header=*(unsigned long *)ptr; - ptr+=4; + } else { + /* Continue the last copy */ + cmd = dev->isoc_ctl.cmd; + size = dev->isoc_ctl.size; + pos = dev->isoc_ctl.pos; + pktsize = dev->isoc_ctl.pktsize; } -HEADER: - /* Copy or continue last copy */ - rc=copy_packet(urb,header,&ptr,endp,outp,&buf); - if (rc<0) { - buf=NULL; - printk(KERN_ERR "tm6000: buffer underrun at %ld\n", - jiffies); - return rc; + cpysize = (endp - ptr > size) ? size : endp - ptr; + if (cpysize) { + /* copy data in different buffers */ + switch (cmd) { + case TM6000_URB_MSG_VIDEO: + /* Fills video buffer */ + if (vbuf) + memcpy (&voutp[pos], ptr, cpysize); + break; + case TM6000_URB_MSG_AUDIO: + tm6000_call_fillbuf(dev, TM6000_AUDIO, ptr, cpysize); + break; + case TM6000_URB_MSG_VBI: + /* Need some code to copy vbi buffer */ + break; + case TM6000_URB_MSG_PTS: + /* Need some code to copy pts */ + break; + } + } + if (ptr + pktsize > endp) { + /* End of URB packet, but cmd processing is not + * complete. Preserve the state for a next packet + */ + dev->isoc_ctl.pos = pos + cpysize; + dev->isoc_ctl.size = size - cpysize; + dev->isoc_ctl.cmd = cmd; + dev->isoc_ctl.pktsize = pktsize - (endp - ptr); + ptr += endp - ptr; + } else { + dev->isoc_ctl.cmd = 0; + ptr += pktsize; } - if (!buf) - return 0; } - return 0; } + /* * Identify the tm5600/6000 buffer header type and properly handles */ @@ -510,7 +425,6 @@ static inline int tm6000_isoc_copy(struct urb *urb) { struct tm6000_dmaqueue *dma_q = urb->context; struct tm6000_core *dev= container_of(dma_q,struct tm6000_core,vidq); - struct tm6000_buffer *buf; int i, len=0, rc=1, status; char *p; @@ -585,7 +499,6 @@ static void tm6000_uninit_isoc(struct tm6000_core *dev) struct urb *urb; int i; - dev->isoc_ctl.nfields = -1; dev->isoc_ctl.buf = NULL; for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { urb=dev->isoc_ctl.urb[i]; @@ -610,8 +523,6 @@ static void tm6000_uninit_isoc(struct tm6000_core *dev) dev->isoc_ctl.urb=NULL; dev->isoc_ctl.transfer_buffer=NULL; dev->isoc_ctl.num_bufs = 0; - - dev->isoc_ctl.num_bufs=0; } /* diff --git a/drivers/staging/tm6000/tm6000.h b/drivers/staging/tm6000/tm6000.h index 7bbaf26..1ec1bff 100644 --- a/drivers/staging/tm6000/tm6000.h +++ b/drivers/staging/tm6000/tm6000.h @@ -20,8 +20,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -// Use the tm6000-hack, instead of the proper initialization code -//#define HACK 1 +/* Use the tm6000-hack, instead of the proper initialization code i*/ +/* #define HACK 1 */ #include <linux/videodev2.h> #include <media/v4l2-common.h> @@ -98,7 +98,7 @@ enum tm6000_io_method { }; enum tm6000_mode { - TM6000_MODE_UNKNOWN=0, + TM6000_MODE_UNKNOWN = 0, TM6000_MODE_ANALOG, TM6000_MODE_DIGITAL, }; @@ -128,10 +128,21 @@ struct tm6000_dvb { struct dvb_frontend *frontend; struct dmxdev dmxdev; unsigned int streams; - struct urb *bulk_urb; + struct urb *bulk_urb; struct mutex mutex; }; +struct snd_tm6000_card { + struct snd_card *card; + spinlock_t reg_lock; + struct tm6000_core *core; + struct snd_pcm_substream *substream; + + /* temporary data for buffer fill processing */ + unsigned buf_pos; + unsigned period_pos; +}; + struct tm6000_endpoint { struct usb_host_endpoint *endp; __u8 bInterfaceNumber; @@ -147,7 +158,7 @@ struct tm6000_core { enum tm6000_devtype dev_type; /* type of device */ v4l2_std_id norm; /* Current norm */ - int width,height; /* Selected resolution */ + int width, height; /* Selected resolution */ enum tm6000_core_state state; @@ -160,6 +171,8 @@ struct tm6000_core { struct tm6000_gpio gpio; + char *ir_codes; + /* Demodulator configuration */ int demod_addr; /* demodulator address */ @@ -190,6 +203,11 @@ struct tm6000_core { /* DVB-T support */ struct tm6000_dvb *dvb; + /* audio support */ + struct snd_tm6000_card *adev; + + struct tm6000_IR *ir; + /* locks */ struct mutex lock; @@ -197,6 +215,7 @@ struct tm6000_core { struct usb_device *udev; /* the usb device */ struct tm6000_endpoint bulk_in, bulk_out, isoc_in, isoc_out; + struct tm6000_endpoint int_in, int_out; /* scaler!=0 if scaler is active*/ int scaler; @@ -207,14 +226,18 @@ struct tm6000_core { spinlock_t slock; }; -#define TM6000_AUDIO 0x10 +enum tm6000_ops_type { + TM6000_AUDIO = 0x10, + TM6000_DVB = 0x20, +}; struct tm6000_ops { struct list_head next; char *name; - int id; + enum tm6000_ops_type type; int (*init)(struct tm6000_core *); int (*fini)(struct tm6000_core *); + int (*fillbuf)(struct tm6000_core *, char *buf, int size); }; struct tm6000_fh { @@ -222,7 +245,7 @@ struct tm6000_fh { /* video capture */ struct tm6000_fmt *fmt; - unsigned int width,height; + unsigned int width, height; struct videobuf_queue vb_vidq; enum v4l2_buf_type type; @@ -234,28 +257,24 @@ struct tm6000_fh { /* In tm6000-cards.c */ -int tm6000_tuner_callback (void *ptr, int component, int command, int arg); -int tm6000_xc5000_callback (void *ptr, int component, int command, int arg); +int tm6000_tuner_callback(void *ptr, int component, int command, int arg); +int tm6000_xc5000_callback(void *ptr, int component, int command, int arg); int tm6000_cards_setup(struct tm6000_core *dev); /* In tm6000-core.c */ -int tm6000_read_write_usb (struct tm6000_core *dev, u8 reqtype, u8 req, +int tm6000_read_write_usb(struct tm6000_core *dev, u8 reqtype, u8 req, u16 value, u16 index, u8 *buf, u16 len); -int tm6000_get_reg (struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_get_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index); int tm6000_get_reg16(struct tm6000_core *dev, u8 req, u16 value, u16 index); int tm6000_get_reg32(struct tm6000_core *dev, u8 req, u16 value, u16 index); -int tm6000_set_reg (struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_set_reg(struct tm6000_core *dev, u8 req, u16 value, u16 index); int tm6000_i2c_reset(struct tm6000_core *dev, u16 tsleep); +int tm6000_init(struct tm6000_core *dev); -int tm6000_init (struct tm6000_core *dev); - -int tm6000_init_analog_mode (struct tm6000_core *dev); -int tm6000_init_digital_mode (struct tm6000_core *dev); -int tm6000_set_audio_bitrate (struct tm6000_core *dev, int bitrate); - -int tm6000_dvb_register(struct tm6000_core *dev); -void tm6000_dvb_unregister(struct tm6000_core *dev); +int tm6000_init_analog_mode(struct tm6000_core *dev); +int tm6000_init_digital_mode(struct tm6000_core *dev); +int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate); int tm6000_v4l2_register(struct tm6000_core *dev); int tm6000_v4l2_unregister(struct tm6000_core *dev); @@ -268,10 +287,13 @@ int tm6000_register_extension(struct tm6000_ops *ops); void tm6000_unregister_extension(struct tm6000_ops *ops); void tm6000_init_extension(struct tm6000_core *dev); void tm6000_close_extension(struct tm6000_core *dev); +int tm6000_call_fillbuf(struct tm6000_core *dev, enum tm6000_ops_type type, + char *buf, int size); + /* In tm6000-stds.c */ void tm6000_get_std_res(struct tm6000_core *dev); -int tm6000_set_standard (struct tm6000_core *dev, v4l2_std_id *norm); +int tm6000_set_standard(struct tm6000_core *dev, v4l2_std_id *norm); /* In tm6000-i2c.c */ int tm6000_i2c_register(struct tm6000_core *dev); @@ -285,14 +307,14 @@ int tm6000_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i); int tm6000_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i); -int tm6000_vidioc_reqbufs (struct file *file, void *priv, - struct v4l2_requestbuffers *rb); -int tm6000_vidioc_querybuf (struct file *file, void *priv, - struct v4l2_buffer *b); -int tm6000_vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *b); -int tm6000_vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *b); +int tm6000_vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb); +int tm6000_vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *b); +int tm6000_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b); +int tm6000_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b); ssize_t tm6000_v4l2_read(struct file *filp, char __user * buf, size_t count, - loff_t * f_pos); + loff_t *f_pos); unsigned int tm6000_v4l2_poll(struct file *file, struct poll_table_struct *wait); int tm6000_queue_init(struct tm6000_core *dev); @@ -300,6 +322,10 @@ int tm6000_queue_init(struct tm6000_core *dev); /* In tm6000-alsa.c */ /*int tm6000_audio_init(struct tm6000_core *dev, int idx);*/ +/* In tm6000-input.c */ +int tm6000_ir_init(struct tm6000_core *dev); +int tm6000_ir_fini(struct tm6000_core *dev); +void tm6000_ir_wait(struct tm6000_core *dev, u8 state); /* Debug stuff */ @@ -307,7 +333,7 @@ extern int tm6000_debug; #define dprintk(dev, level, fmt, arg...) do {\ if (tm6000_debug & level) \ - printk(KERN_INFO "(%lu) %s %s :"fmt, jiffies, \ + printk(KERN_INFO "(%lu) %s %s :"fmt, jiffies, \ dev->name, __FUNCTION__ , ##arg); } while (0) #define V4L2_DEBUG_REG 0x0004 @@ -320,5 +346,3 @@ extern int tm6000_debug; #define tm6000_err(fmt, arg...) do {\ printk(KERN_ERR "tm6000 %s :"fmt, \ __FUNCTION__ , ##arg); } while (0) - - diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c index dbe6db0..be446b7 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/f_uvc.c @@ -61,12 +61,12 @@ static struct usb_gadget_strings *uvc_function_strings[] = { #define UVC_INTF_VIDEO_STREAMING 1 static struct usb_interface_assoc_descriptor uvc_iad __initdata = { - .bLength = USB_DT_INTERFACE_ASSOCIATION_SIZE, + .bLength = sizeof(uvc_iad), .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, .bFirstInterface = 0, .bInterfaceCount = 2, .bFunctionClass = USB_CLASS_VIDEO, - .bFunctionSubClass = 0x03, + .bFunctionSubClass = UVC_SC_VIDEO_INTERFACE_COLLECTION, .bFunctionProtocol = 0x00, .iFunction = 0, }; @@ -78,7 +78,7 @@ static struct usb_interface_descriptor uvc_control_intf __initdata = { .bAlternateSetting = 0, .bNumEndpoints = 1, .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 0x01, + .bInterfaceSubClass = UVC_SC_VIDEOCONTROL, .bInterfaceProtocol = 0x00, .iInterface = 0, }; @@ -106,7 +106,7 @@ static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = { .bAlternateSetting = 0, .bNumEndpoints = 0, .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 0x02, + .bInterfaceSubClass = UVC_SC_VIDEOSTREAMING, .bInterfaceProtocol = 0x00, .iInterface = 0, }; @@ -118,7 +118,7 @@ static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = { .bAlternateSetting = 1, .bNumEndpoints = 1, .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 0x02, + .bInterfaceSubClass = UVC_SC_VIDEOSTREAMING, .bInterfaceProtocol = 0x00, .iInterface = 0, }; @@ -603,15 +603,15 @@ uvc_bind_config(struct usb_configuration *c, /* Validate the descriptors. */ if (control == NULL || control[0] == NULL || - control[0]->bDescriptorSubType != UVC_DT_HEADER) + control[0]->bDescriptorSubType != UVC_VC_HEADER) goto error; if (fs_streaming == NULL || fs_streaming[0] == NULL || - fs_streaming[0]->bDescriptorSubType != UVC_DT_INPUT_HEADER) + fs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) goto error; if (hs_streaming == NULL || hs_streaming[0] == NULL || - hs_streaming[0]->bDescriptorSubType != UVC_DT_INPUT_HEADER) + hs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) goto error; uvc->desc.control = control; diff --git a/drivers/usb/gadget/f_uvc.h b/drivers/usb/gadget/f_uvc.h index 8a5db7c..e18a663 100644 --- a/drivers/usb/gadget/f_uvc.h +++ b/drivers/usb/gadget/f_uvc.h @@ -15,357 +15,7 @@ #define _F_UVC_H_ #include <linux/usb/composite.h> - -#define USB_CLASS_VIDEO_CONTROL 1 -#define USB_CLASS_VIDEO_STREAMING 2 - -struct uvc_descriptor_header { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; -} __attribute__ ((packed)); - -struct uvc_header_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - __u16 bcdUVC; - __u16 wTotalLength; - __u32 dwClockFrequency; - __u8 bInCollection; - __u8 baInterfaceNr[]; -} __attribute__((__packed__)); - -#define UVC_HEADER_DESCRIPTOR(n) uvc_header_descriptor_##n - -#define DECLARE_UVC_HEADER_DESCRIPTOR(n) \ -struct UVC_HEADER_DESCRIPTOR(n) { \ - __u8 bLength; \ - __u8 bDescriptorType; \ - __u8 bDescriptorSubType; \ - __u16 bcdUVC; \ - __u16 wTotalLength; \ - __u32 dwClockFrequency; \ - __u8 bInCollection; \ - __u8 baInterfaceNr[n]; \ -} __attribute__ ((packed)) - -struct uvc_input_terminal_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - __u8 bTerminalID; - __u16 wTerminalType; - __u8 bAssocTerminal; - __u8 iTerminal; -} __attribute__((__packed__)); - -struct uvc_output_terminal_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - __u8 bTerminalID; - __u16 wTerminalType; - __u8 bAssocTerminal; - __u8 bSourceID; - __u8 iTerminal; -} __attribute__((__packed__)); - -struct uvc_camera_terminal_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - __u8 bTerminalID; - __u16 wTerminalType; - __u8 bAssocTerminal; - __u8 iTerminal; - __u16 wObjectiveFocalLengthMin; - __u16 wObjectiveFocalLengthMax; - __u16 wOcularFocalLength; - __u8 bControlSize; - __u8 bmControls[3]; -} __attribute__((__packed__)); - -struct uvc_selector_unit_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - __u8 bUnitID; - __u8 bNrInPins; - __u8 baSourceID[0]; - __u8 iSelector; -} __attribute__((__packed__)); - -#define UVC_SELECTOR_UNIT_DESCRIPTOR(n) \ - uvc_selector_unit_descriptor_##n - -#define DECLARE_UVC_SELECTOR_UNIT_DESCRIPTOR(n) \ -struct UVC_SELECTOR_UNIT_DESCRIPTOR(n) { \ - __u8 bLength; \ - __u8 bDescriptorType; \ - __u8 bDescriptorSubType; \ - __u8 bUnitID; \ - __u8 bNrInPins; \ - __u8 baSourceID[n]; \ - __u8 iSelector; \ -} __attribute__ ((packed)) - -struct uvc_processing_unit_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - __u8 bUnitID; - __u8 bSourceID; - __u16 wMaxMultiplier; - __u8 bControlSize; - __u8 bmControls[2]; - __u8 iProcessing; -} __attribute__((__packed__)); - -struct uvc_extension_unit_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - __u8 bUnitID; - __u8 guidExtensionCode[16]; - __u8 bNumControls; - __u8 bNrInPins; - __u8 baSourceID[0]; - __u8 bControlSize; - __u8 bmControls[0]; - __u8 iExtension; -} __attribute__((__packed__)); - -#define UVC_EXTENSION_UNIT_DESCRIPTOR(p, n) \ - uvc_extension_unit_descriptor_##p_##n - -#define DECLARE_UVC_EXTENSION_UNIT_DESCRIPTOR(p, n) \ -struct UVC_EXTENSION_UNIT_DESCRIPTOR(p, n) { \ - __u8 bLength; \ - __u8 bDescriptorType; \ - __u8 bDescriptorSubType; \ - __u8 bUnitID; \ - __u8 guidExtensionCode[16]; \ - __u8 bNumControls; \ - __u8 bNrInPins; \ - __u8 baSourceID[p]; \ - __u8 bControlSize; \ - __u8 bmControls[n]; \ - __u8 iExtension; \ -} __attribute__ ((packed)) - -struct uvc_control_endpoint_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - __u16 wMaxTransferSize; -} __attribute__((__packed__)); - -#define UVC_DT_HEADER 1 -#define UVC_DT_INPUT_TERMINAL 2 -#define UVC_DT_OUTPUT_TERMINAL 3 -#define UVC_DT_SELECTOR_UNIT 4 -#define UVC_DT_PROCESSING_UNIT 5 -#define UVC_DT_EXTENSION_UNIT 6 - -#define UVC_DT_HEADER_SIZE(n) (12+(n)) -#define UVC_DT_INPUT_TERMINAL_SIZE 8 -#define UVC_DT_OUTPUT_TERMINAL_SIZE 9 -#define UVC_DT_CAMERA_TERMINAL_SIZE(n) (15+(n)) -#define UVC_DT_SELECTOR_UNIT_SIZE(n) (6+(n)) -#define UVC_DT_PROCESSING_UNIT_SIZE(n) (9+(n)) -#define UVC_DT_EXTENSION_UNIT_SIZE(p,n) (24+(p)+(n)) -#define UVC_DT_CONTROL_ENDPOINT_SIZE 5 - -struct uvc_input_header_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - __u8 bNumFormats; - __u16 wTotalLength; - __u8 bEndpointAddress; - __u8 bmInfo; - __u8 bTerminalLink; - __u8 bStillCaptureMethod; - __u8 bTriggerSupport; - __u8 bTriggerUsage; - __u8 bControlSize; - __u8 bmaControls[]; -} __attribute__((__packed__)); - -#define UVC_INPUT_HEADER_DESCRIPTOR(n, p) \ - uvc_input_header_descriptor_##n_##p - -#define DECLARE_UVC_INPUT_HEADER_DESCRIPTOR(n, p) \ -struct UVC_INPUT_HEADER_DESCRIPTOR(n, p) { \ - __u8 bLength; \ - __u8 bDescriptorType; \ - __u8 bDescriptorSubType; \ - __u8 bNumFormats; \ - __u16 wTotalLength; \ - __u8 bEndpointAddress; \ - __u8 bmInfo; \ - __u8 bTerminalLink; \ - __u8 bStillCaptureMethod; \ - __u8 bTriggerSupport; \ - __u8 bTriggerUsage; \ - __u8 bControlSize; \ - __u8 bmaControls[p][n]; \ -} __attribute__ ((packed)) - -struct uvc_output_header_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - __u8 bNumFormats; - __u16 wTotalLength; - __u8 bEndpointAddress; - __u8 bTerminalLink; - __u8 bControlSize; - __u8 bmaControls[]; -} __attribute__((__packed__)); - -#define UVC_OUTPUT_HEADER_DESCRIPTOR(n, p) \ - uvc_output_header_descriptor_##n_##p - -#define DECLARE_UVC_OUTPUT_HEADER_DESCRIPTOR(n, p) \ -struct UVC_OUTPUT_HEADER_DESCRIPTOR(n, p) { \ - __u8 bLength; \ - __u8 bDescriptorType; \ - __u8 bDescriptorSubType; \ - __u8 bNumFormats; \ - __u16 wTotalLength; \ - __u8 bEndpointAddress; \ - __u8 bTerminalLink; \ - __u8 bControlSize; \ - __u8 bmaControls[p][n]; \ -} __attribute__ ((packed)) - -struct uvc_format_uncompressed { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - __u8 bFormatIndex; - __u8 bNumFrameDescriptors; - __u8 guidFormat[16]; - __u8 bBitsPerPixel; - __u8 bDefaultFrameIndex; - __u8 bAspectRatioX; - __u8 bAspectRatioY; - __u8 bmInterfaceFlags; - __u8 bCopyProtect; -} __attribute__((__packed__)); - -struct uvc_frame_uncompressed { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - __u8 bFrameIndex; - __u8 bmCapabilities; - __u16 wWidth; - __u16 wHeight; - __u32 dwMinBitRate; - __u32 dwMaxBitRate; - __u32 dwMaxVideoFrameBufferSize; - __u32 dwDefaultFrameInterval; - __u8 bFrameIntervalType; - __u32 dwFrameInterval[]; -} __attribute__((__packed__)); - -#define UVC_FRAME_UNCOMPRESSED(n) \ - uvc_frame_uncompressed_##n - -#define DECLARE_UVC_FRAME_UNCOMPRESSED(n) \ -struct UVC_FRAME_UNCOMPRESSED(n) { \ - __u8 bLength; \ - __u8 bDescriptorType; \ - __u8 bDescriptorSubType; \ - __u8 bFrameIndex; \ - __u8 bmCapabilities; \ - __u16 wWidth; \ - __u16 wHeight; \ - __u32 dwMinBitRate; \ - __u32 dwMaxBitRate; \ - __u32 dwMaxVideoFrameBufferSize; \ - __u32 dwDefaultFrameInterval; \ - __u8 bFrameIntervalType; \ - __u32 dwFrameInterval[n]; \ -} __attribute__ ((packed)) - -struct uvc_format_mjpeg { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - __u8 bFormatIndex; - __u8 bNumFrameDescriptors; - __u8 bmFlags; - __u8 bDefaultFrameIndex; - __u8 bAspectRatioX; - __u8 bAspectRatioY; - __u8 bmInterfaceFlags; - __u8 bCopyProtect; -} __attribute__((__packed__)); - -struct uvc_frame_mjpeg { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - __u8 bFrameIndex; - __u8 bmCapabilities; - __u16 wWidth; - __u16 wHeight; - __u32 dwMinBitRate; - __u32 dwMaxBitRate; - __u32 dwMaxVideoFrameBufferSize; - __u32 dwDefaultFrameInterval; - __u8 bFrameIntervalType; - __u32 dwFrameInterval[]; -} __attribute__((__packed__)); - -#define UVC_FRAME_MJPEG(n) \ - uvc_frame_mjpeg_##n - -#define DECLARE_UVC_FRAME_MJPEG(n) \ -struct UVC_FRAME_MJPEG(n) { \ - __u8 bLength; \ - __u8 bDescriptorType; \ - __u8 bDescriptorSubType; \ - __u8 bFrameIndex; \ - __u8 bmCapabilities; \ - __u16 wWidth; \ - __u16 wHeight; \ - __u32 dwMinBitRate; \ - __u32 dwMaxBitRate; \ - __u32 dwMaxVideoFrameBufferSize; \ - __u32 dwDefaultFrameInterval; \ - __u8 bFrameIntervalType; \ - __u32 dwFrameInterval[n]; \ -} __attribute__ ((packed)) - -struct uvc_color_matching_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - __u8 bColorPrimaries; - __u8 bTransferCharacteristics; - __u8 bMatrixCoefficients; -} __attribute__((__packed__)); - -#define UVC_DT_INPUT_HEADER 1 -#define UVC_DT_OUTPUT_HEADER 2 -#define UVC_DT_FORMAT_UNCOMPRESSED 4 -#define UVC_DT_FRAME_UNCOMPRESSED 5 -#define UVC_DT_FORMAT_MJPEG 6 -#define UVC_DT_FRAME_MJPEG 7 -#define UVC_DT_COLOR_MATCHING 13 - -#define UVC_DT_INPUT_HEADER_SIZE(n, p) (13+(n*p)) -#define UVC_DT_OUTPUT_HEADER_SIZE(n, p) (9+(n*p)) -#define UVC_DT_FORMAT_UNCOMPRESSED_SIZE 27 -#define UVC_DT_FRAME_UNCOMPRESSED_SIZE(n) (26+4*(n)) -#define UVC_DT_FORMAT_MJPEG_SIZE 11 -#define UVC_DT_FRAME_MJPEG_SIZE(n) (26+4*(n)) -#define UVC_DT_COLOR_MATCHING_SIZE 6 +#include <linux/usb/video.h> extern int uvc_bind_config(struct usb_configuration *c, const struct uvc_descriptor_header * const *control, diff --git a/drivers/usb/gadget/uvc.h b/drivers/usb/gadget/uvc.h index e92454c..5b79194 100644 --- a/drivers/usb/gadget/uvc.h +++ b/drivers/usb/gadget/uvc.h @@ -48,39 +48,6 @@ struct uvc_event #define UVC_INTF_STREAMING 1 /* ------------------------------------------------------------------------ - * UVC constants & structures - */ - -/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */ -#define UVC_STREAM_EOH (1 << 7) -#define UVC_STREAM_ERR (1 << 6) -#define UVC_STREAM_STI (1 << 5) -#define UVC_STREAM_RES (1 << 4) -#define UVC_STREAM_SCR (1 << 3) -#define UVC_STREAM_PTS (1 << 2) -#define UVC_STREAM_EOF (1 << 1) -#define UVC_STREAM_FID (1 << 0) - -struct uvc_streaming_control { - __u16 bmHint; - __u8 bFormatIndex; - __u8 bFrameIndex; - __u32 dwFrameInterval; - __u16 wKeyFrameRate; - __u16 wPFrameRate; - __u16 wCompQuality; - __u16 wCompWindowSize; - __u16 wDelay; - __u32 dwMaxVideoFrameSize; - __u32 dwMaxPayloadTransferSize; - __u32 dwClockFrequency; - __u8 bmFramingInfo; - __u8 bPreferedVersion; - __u8 bMinVersion; - __u8 bMaxVersion; -} __attribute__((__packed__)); - -/* ------------------------------------------------------------------------ * Debugging, printing and logging */ @@ -137,9 +104,6 @@ extern unsigned int uvc_gadget_trace_param; #define UVC_MAX_REQUEST_SIZE 64 #define UVC_MAX_EVENTS 4 -#define USB_DT_INTERFACE_ASSOCIATION_SIZE 8 -#define USB_CLASS_MISC 0xef - /* ------------------------------------------------------------------------ * Structures */ diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/webcam.c index f5f3030..288d211 100644 --- a/drivers/usb/gadget/webcam.c +++ b/drivers/usb/gadget/webcam.c @@ -90,7 +90,7 @@ DECLARE_UVC_HEADER_DESCRIPTOR(1); static const struct UVC_HEADER_DESCRIPTOR(1) uvc_control_header = { .bLength = UVC_DT_HEADER_SIZE(1), .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = UVC_DT_HEADER, + .bDescriptorSubType = UVC_VC_HEADER, .bcdUVC = cpu_to_le16(0x0100), .wTotalLength = 0, /* dynamic */ .dwClockFrequency = cpu_to_le32(48000000), @@ -101,7 +101,7 @@ static const struct UVC_HEADER_DESCRIPTOR(1) uvc_control_header = { static const struct uvc_camera_terminal_descriptor uvc_camera_terminal = { .bLength = UVC_DT_CAMERA_TERMINAL_SIZE(3), .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = UVC_DT_INPUT_TERMINAL, + .bDescriptorSubType = UVC_VC_INPUT_TERMINAL, .bTerminalID = 1, .wTerminalType = cpu_to_le16(0x0201), .bAssocTerminal = 0, @@ -118,7 +118,7 @@ static const struct uvc_camera_terminal_descriptor uvc_camera_terminal = { static const struct uvc_processing_unit_descriptor uvc_processing = { .bLength = UVC_DT_PROCESSING_UNIT_SIZE(2), .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = UVC_DT_PROCESSING_UNIT, + .bDescriptorSubType = UVC_VC_PROCESSING_UNIT, .bUnitID = 2, .bSourceID = 1, .wMaxMultiplier = cpu_to_le16(16*1024), @@ -131,7 +131,7 @@ static const struct uvc_processing_unit_descriptor uvc_processing = { static const struct uvc_output_terminal_descriptor uvc_output_terminal = { .bLength = UVC_DT_OUTPUT_TERMINAL_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = UVC_DT_OUTPUT_TERMINAL, + .bDescriptorSubType = UVC_VC_OUTPUT_TERMINAL, .bTerminalID = 3, .wTerminalType = cpu_to_le16(0x0101), .bAssocTerminal = 0, @@ -144,7 +144,7 @@ DECLARE_UVC_INPUT_HEADER_DESCRIPTOR(1, 2); static const struct UVC_INPUT_HEADER_DESCRIPTOR(1, 2) uvc_input_header = { .bLength = UVC_DT_INPUT_HEADER_SIZE(1, 2), .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = UVC_DT_INPUT_HEADER, + .bDescriptorSubType = UVC_VS_INPUT_HEADER, .bNumFormats = 2, .wTotalLength = 0, /* dynamic */ .bEndpointAddress = 0, /* dynamic */ @@ -161,7 +161,7 @@ static const struct UVC_INPUT_HEADER_DESCRIPTOR(1, 2) uvc_input_header = { static const struct uvc_format_uncompressed uvc_format_yuv = { .bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = UVC_DT_FORMAT_UNCOMPRESSED, + .bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED, .bFormatIndex = 1, .bNumFrameDescriptors = 2, .guidFormat = @@ -181,7 +181,7 @@ DECLARE_UVC_FRAME_UNCOMPRESSED(3); static const struct UVC_FRAME_UNCOMPRESSED(3) uvc_frame_yuv_360p = { .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(3), .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = UVC_DT_FRAME_UNCOMPRESSED, + .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, .bFrameIndex = 1, .bmCapabilities = 0, .wWidth = cpu_to_le16(640), @@ -199,7 +199,7 @@ static const struct UVC_FRAME_UNCOMPRESSED(3) uvc_frame_yuv_360p = { static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = { .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1), .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = UVC_DT_FRAME_UNCOMPRESSED, + .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, .bFrameIndex = 2, .bmCapabilities = 0, .wWidth = cpu_to_le16(1280), @@ -215,7 +215,7 @@ static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = { static const struct uvc_format_mjpeg uvc_format_mjpg = { .bLength = UVC_DT_FORMAT_MJPEG_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = UVC_DT_FORMAT_MJPEG, + .bDescriptorSubType = UVC_VS_FORMAT_MJPEG, .bFormatIndex = 2, .bNumFrameDescriptors = 2, .bmFlags = 0, @@ -232,7 +232,7 @@ DECLARE_UVC_FRAME_MJPEG(3); static const struct UVC_FRAME_MJPEG(3) uvc_frame_mjpg_360p = { .bLength = UVC_DT_FRAME_MJPEG_SIZE(3), .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = UVC_DT_FRAME_MJPEG, + .bDescriptorSubType = UVC_VS_FRAME_MJPEG, .bFrameIndex = 1, .bmCapabilities = 0, .wWidth = cpu_to_le16(640), @@ -250,7 +250,7 @@ static const struct UVC_FRAME_MJPEG(3) uvc_frame_mjpg_360p = { static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = { .bLength = UVC_DT_FRAME_MJPEG_SIZE(1), .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = UVC_DT_FRAME_MJPEG, + .bDescriptorSubType = UVC_VS_FRAME_MJPEG, .bFrameIndex = 2, .bmCapabilities = 0, .wWidth = cpu_to_le16(1280), @@ -266,7 +266,7 @@ static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = { static const struct uvc_color_matching_descriptor uvc_color_matching = { .bLength = UVC_DT_COLOR_MATCHING_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = UVC_DT_COLOR_MATCHING, + .bDescriptorSubType = UVC_VS_COLORFORMAT, .bColorPrimaries = 1, .bTransferCharacteristics = 1, .bMatrixCoefficients = 4, diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c index 1f8eb70..07fbb8a 100644 --- a/drivers/video/omap2/displays/panel-acx565akm.c +++ b/drivers/video/omap2/displays/panel-acx565akm.c @@ -592,7 +592,7 @@ static int acx_panel_power_on(struct omap_dss_device *dssdev) r = omapdss_sdi_display_enable(dssdev); if (r) { pr_err("%s sdi enable failed\n", __func__); - return r; + goto fail_unlock; } /*FIXME tweak me */ @@ -633,6 +633,8 @@ static int acx_panel_power_on(struct omap_dss_device *dssdev) return acx565akm_bl_update_status(md->bl_dev); fail: omapdss_sdi_display_disable(dssdev); +fail_unlock: + mutex_unlock(&md->mutex); return r; } diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index b6cb542..493a2bf 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -62,6 +62,7 @@ typedef enum fe_caps { FE_CAN_8VSB = 0x200000, FE_CAN_16VSB = 0x400000, FE_HAS_EXTENDED_CAPS = 0x800000, /* We need more bitspace for newer APIs, indicate this. */ + FE_CAN_TURBO_FEC = 0x8000000, /* frontend supports "turbo fec modulation" */ FE_CAN_2G_MODULATION = 0x10000000, /* frontend supports "2nd generation modulation" (DVB-S2) */ FE_NEEDS_BENDING = 0x20000000, /* not supported anymore, don't use (frontend requires frequency bending) */ FE_CAN_RECOVER = 0x40000000, /* frontend can recover from a cable unplug automatically */ diff --git a/include/linux/dvb/version.h b/include/linux/dvb/version.h index 540b058..5a7546c 100644 --- a/include/linux/dvb/version.h +++ b/include/linux/dvb/version.h @@ -24,6 +24,6 @@ #define _DVBVERSION_H_ #define DVB_API_VERSION 5 -#define DVB_API_VERSION_MINOR 1 +#define DVB_API_VERSION_MINOR 2 #endif /*_DVBVERSION_H_*/ diff --git a/include/linux/usb/video.h b/include/linux/usb/video.h index be436d9..3b3b95e 100644 --- a/include/linux/usb/video.h +++ b/include/linux/usb/video.h @@ -160,5 +160,409 @@ #define UVC_STATUS_TYPE_CONTROL 1 #define UVC_STATUS_TYPE_STREAMING 2 +/* 2.4.3.3. Payload Header Information */ +#define UVC_STREAM_EOH (1 << 7) +#define UVC_STREAM_ERR (1 << 6) +#define UVC_STREAM_STI (1 << 5) +#define UVC_STREAM_RES (1 << 4) +#define UVC_STREAM_SCR (1 << 3) +#define UVC_STREAM_PTS (1 << 2) +#define UVC_STREAM_EOF (1 << 1) +#define UVC_STREAM_FID (1 << 0) + +/* 4.1.2. Control Capabilities */ +#define UVC_CONTROL_CAP_GET (1 << 0) +#define UVC_CONTROL_CAP_SET (1 << 1) +#define UVC_CONTROL_CAP_DISABLED (1 << 2) +#define UVC_CONTROL_CAP_AUTOUPDATE (1 << 3) +#define UVC_CONTROL_CAP_ASYNCHRONOUS (1 << 4) + +/* ------------------------------------------------------------------------ + * UVC structures + */ + +/* All UVC descriptors have these 3 fields at the beginning */ +struct uvc_descriptor_header { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; +} __attribute__((packed)); + +/* 3.7.2. Video Control Interface Header Descriptor */ +struct uvc_header_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u16 bcdUVC; + __u16 wTotalLength; + __u32 dwClockFrequency; + __u8 bInCollection; + __u8 baInterfaceNr[]; +} __attribute__((__packed__)); + +#define UVC_DT_HEADER_SIZE(n) (12+(n)) + +#define UVC_HEADER_DESCRIPTOR(n) \ + uvc_header_descriptor_##n + +#define DECLARE_UVC_HEADER_DESCRIPTOR(n) \ +struct UVC_HEADER_DESCRIPTOR(n) { \ + __u8 bLength; \ + __u8 bDescriptorType; \ + __u8 bDescriptorSubType; \ + __u16 bcdUVC; \ + __u16 wTotalLength; \ + __u32 dwClockFrequency; \ + __u8 bInCollection; \ + __u8 baInterfaceNr[n]; \ +} __attribute__ ((packed)) + +/* 3.7.2.1. Input Terminal Descriptor */ +struct uvc_input_terminal_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u8 bTerminalID; + __u16 wTerminalType; + __u8 bAssocTerminal; + __u8 iTerminal; +} __attribute__((__packed__)); + +#define UVC_DT_INPUT_TERMINAL_SIZE 8 + +/* 3.7.2.2. Output Terminal Descriptor */ +struct uvc_output_terminal_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u8 bTerminalID; + __u16 wTerminalType; + __u8 bAssocTerminal; + __u8 bSourceID; + __u8 iTerminal; +} __attribute__((__packed__)); + +#define UVC_DT_OUTPUT_TERMINAL_SIZE 9 + +/* 3.7.2.3. Camera Terminal Descriptor */ +struct uvc_camera_terminal_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u8 bTerminalID; + __u16 wTerminalType; + __u8 bAssocTerminal; + __u8 iTerminal; + __u16 wObjectiveFocalLengthMin; + __u16 wObjectiveFocalLengthMax; + __u16 wOcularFocalLength; + __u8 bControlSize; + __u8 bmControls[3]; +} __attribute__((__packed__)); + +#define UVC_DT_CAMERA_TERMINAL_SIZE(n) (15+(n)) + +/* 3.7.2.4. Selector Unit Descriptor */ +struct uvc_selector_unit_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u8 bUnitID; + __u8 bNrInPins; + __u8 baSourceID[0]; + __u8 iSelector; +} __attribute__((__packed__)); + +#define UVC_DT_SELECTOR_UNIT_SIZE(n) (6+(n)) + +#define UVC_SELECTOR_UNIT_DESCRIPTOR(n) \ + uvc_selector_unit_descriptor_##n + +#define DECLARE_UVC_SELECTOR_UNIT_DESCRIPTOR(n) \ +struct UVC_SELECTOR_UNIT_DESCRIPTOR(n) { \ + __u8 bLength; \ + __u8 bDescriptorType; \ + __u8 bDescriptorSubType; \ + __u8 bUnitID; \ + __u8 bNrInPins; \ + __u8 baSourceID[n]; \ + __u8 iSelector; \ +} __attribute__ ((packed)) + +/* 3.7.2.5. Processing Unit Descriptor */ +struct uvc_processing_unit_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u8 bUnitID; + __u8 bSourceID; + __u16 wMaxMultiplier; + __u8 bControlSize; + __u8 bmControls[2]; + __u8 iProcessing; +} __attribute__((__packed__)); + +#define UVC_DT_PROCESSING_UNIT_SIZE(n) (9+(n)) + +/* 3.7.2.6. Extension Unit Descriptor */ +struct uvc_extension_unit_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u8 bUnitID; + __u8 guidExtensionCode[16]; + __u8 bNumControls; + __u8 bNrInPins; + __u8 baSourceID[0]; + __u8 bControlSize; + __u8 bmControls[0]; + __u8 iExtension; +} __attribute__((__packed__)); + +#define UVC_DT_EXTENSION_UNIT_SIZE(p, n) (24+(p)+(n)) + +#define UVC_EXTENSION_UNIT_DESCRIPTOR(p, n) \ + uvc_extension_unit_descriptor_##p_##n + +#define DECLARE_UVC_EXTENSION_UNIT_DESCRIPTOR(p, n) \ +struct UVC_EXTENSION_UNIT_DESCRIPTOR(p, n) { \ + __u8 bLength; \ + __u8 bDescriptorType; \ + __u8 bDescriptorSubType; \ + __u8 bUnitID; \ + __u8 guidExtensionCode[16]; \ + __u8 bNumControls; \ + __u8 bNrInPins; \ + __u8 baSourceID[p]; \ + __u8 bControlSize; \ + __u8 bmControls[n]; \ + __u8 iExtension; \ +} __attribute__ ((packed)) + +/* 3.8.2.2. Video Control Interrupt Endpoint Descriptor */ +struct uvc_control_endpoint_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u16 wMaxTransferSize; +} __attribute__((__packed__)); + +#define UVC_DT_CONTROL_ENDPOINT_SIZE 5 + +/* 3.9.2.1. Input Header Descriptor */ +struct uvc_input_header_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u8 bNumFormats; + __u16 wTotalLength; + __u8 bEndpointAddress; + __u8 bmInfo; + __u8 bTerminalLink; + __u8 bStillCaptureMethod; + __u8 bTriggerSupport; + __u8 bTriggerUsage; + __u8 bControlSize; + __u8 bmaControls[]; +} __attribute__((__packed__)); + +#define UVC_DT_INPUT_HEADER_SIZE(n, p) (13+(n*p)) + +#define UVC_INPUT_HEADER_DESCRIPTOR(n, p) \ + uvc_input_header_descriptor_##n_##p + +#define DECLARE_UVC_INPUT_HEADER_DESCRIPTOR(n, p) \ +struct UVC_INPUT_HEADER_DESCRIPTOR(n, p) { \ + __u8 bLength; \ + __u8 bDescriptorType; \ + __u8 bDescriptorSubType; \ + __u8 bNumFormats; \ + __u16 wTotalLength; \ + __u8 bEndpointAddress; \ + __u8 bmInfo; \ + __u8 bTerminalLink; \ + __u8 bStillCaptureMethod; \ + __u8 bTriggerSupport; \ + __u8 bTriggerUsage; \ + __u8 bControlSize; \ + __u8 bmaControls[p][n]; \ +} __attribute__ ((packed)) + +/* 3.9.2.2. Output Header Descriptor */ +struct uvc_output_header_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u8 bNumFormats; + __u16 wTotalLength; + __u8 bEndpointAddress; + __u8 bTerminalLink; + __u8 bControlSize; + __u8 bmaControls[]; +} __attribute__((__packed__)); + +#define UVC_DT_OUTPUT_HEADER_SIZE(n, p) (9+(n*p)) + +#define UVC_OUTPUT_HEADER_DESCRIPTOR(n, p) \ + uvc_output_header_descriptor_##n_##p + +#define DECLARE_UVC_OUTPUT_HEADER_DESCRIPTOR(n, p) \ +struct UVC_OUTPUT_HEADER_DESCRIPTOR(n, p) { \ + __u8 bLength; \ + __u8 bDescriptorType; \ + __u8 bDescriptorSubType; \ + __u8 bNumFormats; \ + __u16 wTotalLength; \ + __u8 bEndpointAddress; \ + __u8 bTerminalLink; \ + __u8 bControlSize; \ + __u8 bmaControls[p][n]; \ +} __attribute__ ((packed)) + +/* 3.9.2.6. Color matching descriptor */ +struct uvc_color_matching_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u8 bColorPrimaries; + __u8 bTransferCharacteristics; + __u8 bMatrixCoefficients; +} __attribute__((__packed__)); + +#define UVC_DT_COLOR_MATCHING_SIZE 6 + +/* 4.3.1.1. Video Probe and Commit Controls */ +struct uvc_streaming_control { + __u16 bmHint; + __u8 bFormatIndex; + __u8 bFrameIndex; + __u32 dwFrameInterval; + __u16 wKeyFrameRate; + __u16 wPFrameRate; + __u16 wCompQuality; + __u16 wCompWindowSize; + __u16 wDelay; + __u32 dwMaxVideoFrameSize; + __u32 dwMaxPayloadTransferSize; + __u32 dwClockFrequency; + __u8 bmFramingInfo; + __u8 bPreferedVersion; + __u8 bMinVersion; + __u8 bMaxVersion; +} __attribute__((__packed__)); + +/* Uncompressed Payload - 3.1.1. Uncompressed Video Format Descriptor */ +struct uvc_format_uncompressed { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u8 bFormatIndex; + __u8 bNumFrameDescriptors; + __u8 guidFormat[16]; + __u8 bBitsPerPixel; + __u8 bDefaultFrameIndex; + __u8 bAspectRatioX; + __u8 bAspectRatioY; + __u8 bmInterfaceFlags; + __u8 bCopyProtect; +} __attribute__((__packed__)); + +#define UVC_DT_FORMAT_UNCOMPRESSED_SIZE 27 + +/* Uncompressed Payload - 3.1.2. Uncompressed Video Frame Descriptor */ +struct uvc_frame_uncompressed { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u8 bFrameIndex; + __u8 bmCapabilities; + __u16 wWidth; + __u16 wHeight; + __u32 dwMinBitRate; + __u32 dwMaxBitRate; + __u32 dwMaxVideoFrameBufferSize; + __u32 dwDefaultFrameInterval; + __u8 bFrameIntervalType; + __u32 dwFrameInterval[]; +} __attribute__((__packed__)); + +#define UVC_DT_FRAME_UNCOMPRESSED_SIZE(n) (26+4*(n)) + +#define UVC_FRAME_UNCOMPRESSED(n) \ + uvc_frame_uncompressed_##n + +#define DECLARE_UVC_FRAME_UNCOMPRESSED(n) \ +struct UVC_FRAME_UNCOMPRESSED(n) { \ + __u8 bLength; \ + __u8 bDescriptorType; \ + __u8 bDescriptorSubType; \ + __u8 bFrameIndex; \ + __u8 bmCapabilities; \ + __u16 wWidth; \ + __u16 wHeight; \ + __u32 dwMinBitRate; \ + __u32 dwMaxBitRate; \ + __u32 dwMaxVideoFrameBufferSize; \ + __u32 dwDefaultFrameInterval; \ + __u8 bFrameIntervalType; \ + __u32 dwFrameInterval[n]; \ +} __attribute__ ((packed)) + +/* MJPEG Payload - 3.1.1. MJPEG Video Format Descriptor */ +struct uvc_format_mjpeg { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u8 bFormatIndex; + __u8 bNumFrameDescriptors; + __u8 bmFlags; + __u8 bDefaultFrameIndex; + __u8 bAspectRatioX; + __u8 bAspectRatioY; + __u8 bmInterfaceFlags; + __u8 bCopyProtect; +} __attribute__((__packed__)); + +#define UVC_DT_FORMAT_MJPEG_SIZE 11 + +/* MJPEG Payload - 3.1.2. MJPEG Video Frame Descriptor */ +struct uvc_frame_mjpeg { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + __u8 bFrameIndex; + __u8 bmCapabilities; + __u16 wWidth; + __u16 wHeight; + __u32 dwMinBitRate; + __u32 dwMaxBitRate; + __u32 dwMaxVideoFrameBufferSize; + __u32 dwDefaultFrameInterval; + __u8 bFrameIntervalType; + __u32 dwFrameInterval[]; +} __attribute__((__packed__)); + +#define UVC_DT_FRAME_MJPEG_SIZE(n) (26+4*(n)) + +#define UVC_FRAME_MJPEG(n) \ + uvc_frame_mjpeg_##n + +#define DECLARE_UVC_FRAME_MJPEG(n) \ +struct UVC_FRAME_MJPEG(n) { \ + __u8 bLength; \ + __u8 bDescriptorType; \ + __u8 bDescriptorSubType; \ + __u8 bFrameIndex; \ + __u8 bmCapabilities; \ + __u16 wWidth; \ + __u16 wHeight; \ + __u32 dwMinBitRate; \ + __u32 dwMaxBitRate; \ + __u32 dwMaxVideoFrameBufferSize; \ + __u32 dwDefaultFrameInterval; \ + __u8 bFrameIntervalType; \ + __u32 dwFrameInterval[n]; \ +} __attribute__ ((packed)) + #endif /* __LINUX_USB_VIDEO_H */ diff --git a/include/media/ir-core.h b/include/media/ir-core.h index ad1303f..513e60d 100644 --- a/include/media/ir-core.h +++ b/include/media/ir-core.h @@ -47,15 +47,21 @@ enum rc_driver_type { * is opened. * @close: callback to allow drivers to disable polling/irq when IR input device * is opened. + * @s_tx_mask: set transmitter mask (for devices with multiple tx outputs) + * @s_tx_carrier: set transmit carrier frequency + * @tx_ir: transmit IR */ struct ir_dev_props { enum rc_driver_type driver_type; unsigned long allowed_protos; u32 scanmask; - void *priv; + void *priv; int (*change_protocol)(void *priv, u64 ir_type); int (*open)(void *priv); void (*close)(void *priv); + int (*s_tx_mask)(void *priv, u32 mask); + int (*s_tx_carrier)(void *priv, u32 carrier); + int (*tx_ir)(void *priv, int *txbuf, u32 n); }; struct ir_input_dev { diff --git a/include/media/ir-kbd-i2c.h b/include/media/ir-kbd-i2c.h index 0506e45..5e96d7a 100644 --- a/include/media/ir-kbd-i2c.h +++ b/include/media/ir-kbd-i2c.h @@ -11,7 +11,7 @@ struct IR_i2c { struct i2c_client *c; struct input_dev *input; struct ir_input_state ir; - + u64 ir_type; /* Used to avoid fast repeating */ unsigned char old; diff --git a/include/media/lirc.h b/include/media/lirc.h new file mode 100644 index 0000000..42c467c --- /dev/null +++ b/include/media/lirc.h @@ -0,0 +1,165 @@ +/* + * lirc.h - linux infrared remote control header file + * last modified 2010/07/13 by Jarod Wilson + */ + +#ifndef _LINUX_LIRC_H +#define _LINUX_LIRC_H + +#include <linux/types.h> +#include <linux/ioctl.h> + +#define PULSE_BIT 0x01000000 +#define PULSE_MASK 0x00FFFFFF + +#define LIRC_MODE2_SPACE 0x00000000 +#define LIRC_MODE2_PULSE 0x01000000 +#define LIRC_MODE2_FREQUENCY 0x02000000 +#define LIRC_MODE2_TIMEOUT 0x03000000 + +#define LIRC_VALUE_MASK 0x00FFFFFF +#define LIRC_MODE2_MASK 0xFF000000 + +#define LIRC_SPACE(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_SPACE) +#define LIRC_PULSE(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_PULSE) +#define LIRC_FREQUENCY(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_FREQUENCY) +#define LIRC_TIMEOUT(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_TIMEOUT) + +#define LIRC_VALUE(val) ((val)&LIRC_VALUE_MASK) +#define LIRC_MODE2(val) ((val)&LIRC_MODE2_MASK) + +#define LIRC_IS_SPACE(val) (LIRC_MODE2(val) == LIRC_MODE2_SPACE) +#define LIRC_IS_PULSE(val) (LIRC_MODE2(val) == LIRC_MODE2_PULSE) +#define LIRC_IS_FREQUENCY(val) (LIRC_MODE2(val) == LIRC_MODE2_FREQUENCY) +#define LIRC_IS_TIMEOUT(val) (LIRC_MODE2(val) == LIRC_MODE2_TIMEOUT) + +/* used heavily by lirc userspace */ +#define lirc_t int + +/*** lirc compatible hardware features ***/ + +#define LIRC_MODE2SEND(x) (x) +#define LIRC_SEND2MODE(x) (x) +#define LIRC_MODE2REC(x) ((x) << 16) +#define LIRC_REC2MODE(x) ((x) >> 16) + +#define LIRC_MODE_RAW 0x00000001 +#define LIRC_MODE_PULSE 0x00000002 +#define LIRC_MODE_MODE2 0x00000004 +#define LIRC_MODE_LIRCCODE 0x00000010 + + +#define LIRC_CAN_SEND_RAW LIRC_MODE2SEND(LIRC_MODE_RAW) +#define LIRC_CAN_SEND_PULSE LIRC_MODE2SEND(LIRC_MODE_PULSE) +#define LIRC_CAN_SEND_MODE2 LIRC_MODE2SEND(LIRC_MODE_MODE2) +#define LIRC_CAN_SEND_LIRCCODE LIRC_MODE2SEND(LIRC_MODE_LIRCCODE) + +#define LIRC_CAN_SEND_MASK 0x0000003f + +#define LIRC_CAN_SET_SEND_CARRIER 0x00000100 +#define LIRC_CAN_SET_SEND_DUTY_CYCLE 0x00000200 +#define LIRC_CAN_SET_TRANSMITTER_MASK 0x00000400 + +#define LIRC_CAN_REC_RAW LIRC_MODE2REC(LIRC_MODE_RAW) +#define LIRC_CAN_REC_PULSE LIRC_MODE2REC(LIRC_MODE_PULSE) +#define LIRC_CAN_REC_MODE2 LIRC_MODE2REC(LIRC_MODE_MODE2) +#define LIRC_CAN_REC_LIRCCODE LIRC_MODE2REC(LIRC_MODE_LIRCCODE) + +#define LIRC_CAN_REC_MASK LIRC_MODE2REC(LIRC_CAN_SEND_MASK) + +#define LIRC_CAN_SET_REC_CARRIER (LIRC_CAN_SET_SEND_CARRIER << 16) +#define LIRC_CAN_SET_REC_DUTY_CYCLE (LIRC_CAN_SET_SEND_DUTY_CYCLE << 16) + +#define LIRC_CAN_SET_REC_DUTY_CYCLE_RANGE 0x40000000 +#define LIRC_CAN_SET_REC_CARRIER_RANGE 0x80000000 +#define LIRC_CAN_GET_REC_RESOLUTION 0x20000000 +#define LIRC_CAN_SET_REC_TIMEOUT 0x10000000 +#define LIRC_CAN_SET_REC_FILTER 0x08000000 + +#define LIRC_CAN_MEASURE_CARRIER 0x02000000 + +#define LIRC_CAN_SEND(x) ((x)&LIRC_CAN_SEND_MASK) +#define LIRC_CAN_REC(x) ((x)&LIRC_CAN_REC_MASK) + +#define LIRC_CAN_NOTIFY_DECODE 0x01000000 + +/*** IOCTL commands for lirc driver ***/ + +#define LIRC_GET_FEATURES _IOR('i', 0x00000000, __u32) + +#define LIRC_GET_SEND_MODE _IOR('i', 0x00000001, __u32) +#define LIRC_GET_REC_MODE _IOR('i', 0x00000002, __u32) +#define LIRC_GET_SEND_CARRIER _IOR('i', 0x00000003, __u32) +#define LIRC_GET_REC_CARRIER _IOR('i', 0x00000004, __u32) +#define LIRC_GET_SEND_DUTY_CYCLE _IOR('i', 0x00000005, __u32) +#define LIRC_GET_REC_DUTY_CYCLE _IOR('i', 0x00000006, __u32) +#define LIRC_GET_REC_RESOLUTION _IOR('i', 0x00000007, __u32) + +#define LIRC_GET_MIN_TIMEOUT _IOR('i', 0x00000008, __u32) +#define LIRC_GET_MAX_TIMEOUT _IOR('i', 0x00000009, __u32) + +#define LIRC_GET_MIN_FILTER_PULSE _IOR('i', 0x0000000a, __u32) +#define LIRC_GET_MAX_FILTER_PULSE _IOR('i', 0x0000000b, __u32) +#define LIRC_GET_MIN_FILTER_SPACE _IOR('i', 0x0000000c, __u32) +#define LIRC_GET_MAX_FILTER_SPACE _IOR('i', 0x0000000d, __u32) + +/* code length in bits, currently only for LIRC_MODE_LIRCCODE */ +#define LIRC_GET_LENGTH _IOR('i', 0x0000000f, __u32) + +#define LIRC_SET_SEND_MODE _IOW('i', 0x00000011, __u32) +#define LIRC_SET_REC_MODE _IOW('i', 0x00000012, __u32) +/* Note: these can reset the according pulse_width */ +#define LIRC_SET_SEND_CARRIER _IOW('i', 0x00000013, __u32) +#define LIRC_SET_REC_CARRIER _IOW('i', 0x00000014, __u32) +#define LIRC_SET_SEND_DUTY_CYCLE _IOW('i', 0x00000015, __u32) +#define LIRC_SET_REC_DUTY_CYCLE _IOW('i', 0x00000016, __u32) +#define LIRC_SET_TRANSMITTER_MASK _IOW('i', 0x00000017, __u32) + +/* + * when a timeout != 0 is set the driver will send a + * LIRC_MODE2_TIMEOUT data packet, otherwise LIRC_MODE2_TIMEOUT is + * never sent, timeout is disabled by default + */ +#define LIRC_SET_REC_TIMEOUT _IOW('i', 0x00000018, __u32) + +/* 1 enables, 0 disables timeout reports in MODE2 */ +#define LIRC_SET_REC_TIMEOUT_REPORTS _IOW('i', 0x00000019, __u32) + +/* + * pulses shorter than this are filtered out by hardware (software + * emulation in lirc_dev?) + */ +#define LIRC_SET_REC_FILTER_PULSE _IOW('i', 0x0000001a, __u32) +/* + * spaces shorter than this are filtered out by hardware (software + * emulation in lirc_dev?) + */ +#define LIRC_SET_REC_FILTER_SPACE _IOW('i', 0x0000001b, __u32) +/* + * if filter cannot be set independantly for pulse/space, this should + * be used + */ +#define LIRC_SET_REC_FILTER _IOW('i', 0x0000001c, __u32) + +/* + * if enabled from the next key press on the driver will send + * LIRC_MODE2_FREQUENCY packets + */ +#define LIRC_SET_MEASURE_CARRIER_MODE _IOW('i', 0x0000001d, __u32) + +/* + * to set a range use + * LIRC_SET_REC_DUTY_CYCLE_RANGE/LIRC_SET_REC_CARRIER_RANGE with the + * lower bound first and later + * LIRC_SET_REC_DUTY_CYCLE/LIRC_SET_REC_CARRIER with the upper bound + */ + +#define LIRC_SET_REC_DUTY_CYCLE_RANGE _IOW('i', 0x0000001e, __u32) +#define LIRC_SET_REC_CARRIER_RANGE _IOW('i', 0x0000001f, __u32) + +#define LIRC_NOTIFY_DECODE _IO('i', 0x00000020) + +#define LIRC_SETUP_START _IO('i', 0x00000021) +#define LIRC_SETUP_END _IO('i', 0x00000022) + +#endif diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h new file mode 100644 index 0000000..b1f6066 --- /dev/null +++ b/include/media/lirc_dev.h @@ -0,0 +1,225 @@ +/* + * LIRC base driver + * + * by Artur Lipowski <alipowski@interia.pl> + * This code is licensed under GNU GPL + * + */ + +#ifndef _LINUX_LIRC_DEV_H +#define _LINUX_LIRC_DEV_H + +#define MAX_IRCTL_DEVICES 4 +#define BUFLEN 16 + +#define mod(n, div) ((n) % (div)) + +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/ioctl.h> +#include <linux/poll.h> +#include <linux/kfifo.h> +#include <media/lirc.h> + +struct lirc_buffer { + wait_queue_head_t wait_poll; + spinlock_t fifo_lock; + unsigned int chunk_size; + unsigned int size; /* in chunks */ + /* Using chunks instead of bytes pretends to simplify boundary checking + * And should allow for some performance fine tunning later */ + struct kfifo fifo; + u8 fifo_initialized; +}; + +static inline void lirc_buffer_clear(struct lirc_buffer *buf) +{ + unsigned long flags; + + if (buf->fifo_initialized) { + spin_lock_irqsave(&buf->fifo_lock, flags); + kfifo_reset(&buf->fifo); + spin_unlock_irqrestore(&buf->fifo_lock, flags); + } else + WARN(1, "calling %s on an uninitialized lirc_buffer\n", + __func__); +} + +static inline int lirc_buffer_init(struct lirc_buffer *buf, + unsigned int chunk_size, + unsigned int size) +{ + int ret; + + init_waitqueue_head(&buf->wait_poll); + spin_lock_init(&buf->fifo_lock); + buf->chunk_size = chunk_size; + buf->size = size; + ret = kfifo_alloc(&buf->fifo, size * chunk_size, GFP_KERNEL); + if (ret == 0) + buf->fifo_initialized = 1; + + return ret; +} + +static inline void lirc_buffer_free(struct lirc_buffer *buf) +{ + if (buf->fifo_initialized) { + kfifo_free(&buf->fifo); + buf->fifo_initialized = 0; + } else + WARN(1, "calling %s on an uninitialized lirc_buffer\n", + __func__); +} + +static inline int lirc_buffer_len(struct lirc_buffer *buf) +{ + int len; + unsigned long flags; + + spin_lock_irqsave(&buf->fifo_lock, flags); + len = kfifo_len(&buf->fifo); + spin_unlock_irqrestore(&buf->fifo_lock, flags); + + return len; +} + +static inline int lirc_buffer_full(struct lirc_buffer *buf) +{ + return lirc_buffer_len(buf) == buf->size * buf->chunk_size; +} + +static inline int lirc_buffer_empty(struct lirc_buffer *buf) +{ + return !lirc_buffer_len(buf); +} + +static inline int lirc_buffer_available(struct lirc_buffer *buf) +{ + return buf->size - (lirc_buffer_len(buf) / buf->chunk_size); +} + +static inline unsigned int lirc_buffer_read(struct lirc_buffer *buf, + unsigned char *dest) +{ + unsigned int ret = 0; + + if (lirc_buffer_len(buf) >= buf->chunk_size) + ret = kfifo_out_locked(&buf->fifo, dest, buf->chunk_size, + &buf->fifo_lock); + return ret; + +} + +static inline unsigned int lirc_buffer_write(struct lirc_buffer *buf, + unsigned char *orig) +{ + unsigned int ret; + + ret = kfifo_in_locked(&buf->fifo, orig, buf->chunk_size, + &buf->fifo_lock); + + return ret; +} + +struct lirc_driver { + char name[40]; + int minor; + unsigned long code_length; + unsigned int buffer_size; /* in chunks holding one code each */ + int sample_rate; + unsigned long features; + + unsigned int chunk_size; + + void *data; + int min_timeout; + int max_timeout; + int (*add_to_buf) (void *data, struct lirc_buffer *buf); + struct lirc_buffer *rbuf; + int (*set_use_inc) (void *data); + void (*set_use_dec) (void *data); + struct file_operations *fops; + struct device *dev; + struct module *owner; +}; + +/* name: + * this string will be used for logs + * + * minor: + * indicates minor device (/dev/lirc) number for registered driver + * if caller fills it with negative value, then the first free minor + * number will be used (if available) + * + * code_length: + * length of the remote control key code expressed in bits + * + * sample_rate: + * + * data: + * it may point to any driver data and this pointer will be passed to + * all callback functions + * + * add_to_buf: + * add_to_buf will be called after specified period of the time or + * triggered by the external event, this behavior depends on value of + * the sample_rate this function will be called in user context. This + * routine should return 0 if data was added to the buffer and + * -ENODATA if none was available. This should add some number of bits + * evenly divisible by code_length to the buffer + * + * rbuf: + * if not NULL, it will be used as a read buffer, you will have to + * write to the buffer by other means, like irq's (see also + * lirc_serial.c). + * + * set_use_inc: + * set_use_inc will be called after device is opened + * + * set_use_dec: + * set_use_dec will be called after device is closed + * + * fops: + * file_operations for drivers which don't fit the current driver model. + * + * Some ioctl's can be directly handled by lirc_dev if the driver's + * ioctl function is NULL or if it returns -ENOIOCTLCMD (see also + * lirc_serial.c). + * + * owner: + * the module owning this struct + * + */ + + +/* following functions can be called ONLY from user context + * + * returns negative value on error or minor number + * of the registered device if success + * contents of the structure pointed by p is copied + */ +extern int lirc_register_driver(struct lirc_driver *d); + +/* returns negative value on error or 0 if success +*/ +extern int lirc_unregister_driver(int minor); + +/* Returns the private data stored in the lirc_driver + * associated with the given device file pointer. + */ +void *lirc_get_pdata(struct file *file); + +/* default file operations + * used by drivers if they override only some operations + */ +int lirc_dev_fop_open(struct inode *inode, struct file *file); +int lirc_dev_fop_close(struct inode *inode, struct file *file); +unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait); +long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +ssize_t lirc_dev_fop_read(struct file *file, char *buffer, size_t length, + loff_t *ppos); +ssize_t lirc_dev_fop_write(struct file *file, const char *buffer, size_t length, + loff_t *ppos); + +#endif diff --git a/include/media/rc-map.h b/include/media/rc-map.h index c78e99a..9569d08 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -17,8 +17,13 @@ #define IR_TYPE_RC6 (1 << 2) /* Philips RC6 protocol */ #define IR_TYPE_JVC (1 << 3) /* JVC protocol */ #define IR_TYPE_SONY (1 << 4) /* Sony12/15/20 protocol */ +#define IR_TYPE_LIRC (1 << 30) /* Pass raw IR to lirc userspace */ #define IR_TYPE_OTHER (1u << 31) +#define IR_TYPE_ALL (IR_TYPE_RC5 | IR_TYPE_NEC | IR_TYPE_RC6 | \ + IR_TYPE_JVC | IR_TYPE_SONY | IR_TYPE_LIRC | \ + IR_TYPE_OTHER) + struct ir_scancode { u32 scancode; u32 keycode; @@ -64,6 +69,8 @@ void rc_map_init(void); #define RC_MAP_BUDGET_CI_OLD "rc-budget-ci-old" #define RC_MAP_CINERGY_1400 "rc-cinergy-1400" #define RC_MAP_CINERGY "rc-cinergy" +#define RC_MAP_DIB0700_NEC_TABLE "rc-dib0700-nec" +#define RC_MAP_DIB0700_RC5_TABLE "rc-dib0700-rc5" #define RC_MAP_DM1105_NEC "rc-dm1105-nec" #define RC_MAP_DNTV_LIVE_DVBT_PRO "rc-dntv-live-dvbt-pro" #define RC_MAP_DNTV_LIVE_DVB_T "rc-dntv-live-dvb-t" @@ -87,6 +94,7 @@ void rc_map_init(void); #define RC_MAP_KAIOMY "rc-kaiomy" #define RC_MAP_KWORLD_315U "rc-kworld-315u" #define RC_MAP_KWORLD_PLUS_TV_ANALOG "rc-kworld-plus-tv-analog" +#define RC_MAP_LIRC "rc-lirc" #define RC_MAP_MANLI "rc-manli" #define RC_MAP_MSI_TVANYWHERE_PLUS "rc-msi-tvanywhere-plus" #define RC_MAP_MSI_TVANYWHERE "rc-msi-tvanywhere" @@ -107,6 +115,7 @@ void rc_map_init(void); #define RC_MAP_PV951 "rc-pv951" #define RC_MAP_RC5_HAUPPAUGE_NEW "rc-rc5-hauppauge-new" #define RC_MAP_RC5_TV "rc-rc5-tv" +#define RC_MAP_RC6_MCE "rc-rc6-mce" #define RC_MAP_REAL_AUDIO_220_32_KEYS "rc-real-audio-220-32-keys" #define RC_MAP_TBS_NEC "rc-tbs-nec" #define RC_MAP_TERRATEC_CINERGY_XS "rc-terratec-cinergy-xs" @@ -116,6 +125,7 @@ void rc_map_init(void); #define RC_MAP_VIDEOMATE_TV_PVR "rc-videomate-tv-pvr" #define RC_MAP_WINFAST "rc-winfast" #define RC_MAP_WINFAST_USBII_DELUXE "rc-winfast-usbii-deluxe" + /* * Please, do not just append newer Remote Controller names at the end. * The names should be ordered in alphabetical order diff --git a/include/media/sh_mobile_ceu.h b/include/media/sh_mobile_ceu.h index b677478..80346a6 100644 --- a/include/media/sh_mobile_ceu.h +++ b/include/media/sh_mobile_ceu.h @@ -6,8 +6,11 @@ #define SH_CEU_FLAG_HSYNC_LOW (1 << 2) /* default High if possible */ #define SH_CEU_FLAG_VSYNC_LOW (1 << 3) /* default High if possible */ +struct device; + struct sh_mobile_ceu_info { unsigned long flags; + struct device *csi2_dev; }; #endif /* __ASM_SH_MOBILE_CEU_H__ */ diff --git a/include/media/sh_mobile_csi2.h b/include/media/sh_mobile_csi2.h new file mode 100644 index 0000000..4d26151 --- /dev/null +++ b/include/media/sh_mobile_csi2.h @@ -0,0 +1,46 @@ +/* + * Driver header for the SH-Mobile MIPI CSI-2 unit + * + * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * 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 SH_MIPI_CSI +#define SH_MIPI_CSI + +enum sh_csi2_phy { + SH_CSI2_PHY_MAIN, + SH_CSI2_PHY_SUB, +}; + +enum sh_csi2_type { + SH_CSI2C, + SH_CSI2I, +}; + +#define SH_CSI2_CRC (1 << 0) +#define SH_CSI2_ECC (1 << 1) + +struct platform_device; + +struct sh_csi2_client_config { + enum sh_csi2_phy phy; + unsigned char lanes; /* bitmask[3:0] */ + unsigned char channel; /* 0..3 */ + struct platform_device *pdev; /* client platform device */ +}; + +struct sh_csi2_pdata { + enum sh_csi2_type type; + unsigned int flags; + struct sh_csi2_client_config *clients; + int num_clients; +}; + +struct device; +struct v4l2_device; + +#endif diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index b8289c2..2ce9573 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -12,12 +12,15 @@ #ifndef SOC_CAMERA_H #define SOC_CAMERA_H +#include <linux/device.h> #include <linux/mutex.h> #include <linux/pm.h> #include <linux/videodev2.h> #include <media/videobuf-core.h> #include <media/v4l2-device.h> +extern struct bus_type soc_camera_bus_type; + struct soc_camera_device { struct list_head list; struct device dev; diff --git a/include/media/v4l2-mediabus.h b/include/media/v4l2-mediabus.h index 865cda7..f0cf2e7 100644 --- a/include/media/v4l2-mediabus.h +++ b/include/media/v4l2-mediabus.h @@ -24,10 +24,10 @@ */ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_FIXED = 1, - V4L2_MBUS_FMT_YUYV8_2X8_LE, - V4L2_MBUS_FMT_YVYU8_2X8_LE, - V4L2_MBUS_FMT_YUYV8_2X8_BE, - V4L2_MBUS_FMT_YVYU8_2X8_BE, + V4L2_MBUS_FMT_YUYV8_2X8, + V4L2_MBUS_FMT_YVYU8_2X8, + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_VYUY8_2X8, V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, V4L2_MBUS_FMT_RGB565_2X8_LE, @@ -41,6 +41,11 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE, V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE, V4L2_MBUS_FMT_SGRBG8_1X8, + V4L2_MBUS_FMT_SBGGR12_1X12, + V4L2_MBUS_FMT_YUYV8_1_5X8, + V4L2_MBUS_FMT_YVYU8_1_5X8, + V4L2_MBUS_FMT_UYVY8_1_5X8, + V4L2_MBUS_FMT_VYUY8_1_5X8, }; /** diff --git a/include/media/videobuf-core.h b/include/media/videobuf-core.h index f91a736..f2c41ce 100644 --- a/include/media/videobuf-core.h +++ b/include/media/videobuf-core.h @@ -54,8 +54,6 @@ struct videobuf_queue; struct videobuf_mapping { unsigned int count; - unsigned long start; - unsigned long end; struct videobuf_queue *q; }; @@ -127,7 +125,7 @@ struct videobuf_queue_ops { struct videobuf_qtype_ops { u32 magic; - struct videobuf_buffer *(*alloc)(size_t size); + struct videobuf_buffer *(*alloc_vb)(size_t size); void *(*vaddr) (struct videobuf_buffer *buf); int (*iolock) (struct videobuf_queue *q, struct videobuf_buffer *vb, @@ -173,7 +171,7 @@ int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr); int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb, struct v4l2_framebuffer *fbuf); -struct videobuf_buffer *videobuf_alloc(struct videobuf_queue *q); +struct videobuf_buffer *videobuf_alloc_vb(struct videobuf_queue *q); /* Used on videobuf-dvb */ void *videobuf_queue_to_vaddr(struct videobuf_queue *q, diff --git a/include/media/videobuf-dma-sg.h b/include/media/videobuf-dma-sg.h index a195f3b..97e07f4 100644 --- a/include/media/videobuf-dma-sg.h +++ b/include/media/videobuf-dma-sg.h @@ -25,23 +25,6 @@ /* --------------------------------------------------------------------- */ /* - * Return a scatterlist for some page-aligned vmalloc()'ed memory - * block (NULL on errors). Memory for the scatterlist is allocated - * using kmalloc. The caller must free the memory. - */ -struct scatterlist *videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages); - -/* - * Return a scatterlist for a an array of userpages (NULL on errors). - * Memory for the scatterlist is allocated using kmalloc. The caller - * must free the memory. - */ -struct scatterlist *videobuf_pages_to_sg(struct page **pages, int nr_pages, - int offset); - -/* --------------------------------------------------------------------- */ - -/* * A small set of helper functions to manage buffers (both userland * and kernel) for DMA. * @@ -68,7 +51,7 @@ struct videobuf_dmabuf { struct page **pages; /* for kernel buffers */ - void *vmalloc; + void *vaddr; /* for overlay buffers (pci-pci dma) */ dma_addr_t bus_addr; @@ -87,6 +70,16 @@ struct videobuf_dma_sg_memory { struct videobuf_dmabuf dma; }; +/* + * Scatter-gather DMA buffer API. + * + * These functions provide a simple way to create a page list and a + * scatter-gather list from a kernel, userspace of physical address and map the + * memory for DMA operation. + * + * Despite the name, this is totally unrelated to videobuf, except that + * videobuf-dma-sg uses the same API internally. + */ void videobuf_dma_init(struct videobuf_dmabuf *dma); int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, unsigned long data, unsigned long size); @@ -96,8 +89,8 @@ int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction, dma_addr_t addr, int nr_pages); int videobuf_dma_free(struct videobuf_dmabuf *dma); -int videobuf_dma_map(struct videobuf_queue *q, struct videobuf_dmabuf *dma); -int videobuf_dma_unmap(struct videobuf_queue *q, struct videobuf_dmabuf *dma); +int videobuf_dma_map(struct device *dev, struct videobuf_dmabuf *dma); +int videobuf_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma); struct videobuf_dmabuf *videobuf_to_dma(struct videobuf_buffer *buf); void *videobuf_sg_alloc(size_t size); @@ -111,11 +104,5 @@ void videobuf_queue_sg_init(struct videobuf_queue *q, unsigned int msize, void *priv); -/*FIXME: these variants are used only on *-alsa code, where videobuf is - * used without queue - */ -int videobuf_sg_dma_map(struct device *dev, struct videobuf_dmabuf *dma); -int videobuf_sg_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma); - #endif /* _VIDEOBUF_DMA_SG_H */ diff --git a/include/media/videobuf-vmalloc.h b/include/media/videobuf-vmalloc.h index 851eb1a..e19403c 100644 --- a/include/media/videobuf-vmalloc.h +++ b/include/media/videobuf-vmalloc.h @@ -22,7 +22,7 @@ struct videobuf_vmalloc_memory { u32 magic; - void *vmalloc; + void *vaddr; /* remap_vmalloc_range seems to need to run * after mmap() on some cases */ |