diff options
67 files changed, 3420 insertions, 934 deletions
@@ -45,6 +45,14 @@ firmware - Create the firmware files that are enclosed at the firmware_install- Install firmware files under /lib/firmware +====================== +Documentation rules: +v4l2-spec - Creates V4L2 API specification + +dvb-spec - Creates DVB API specification + +spec - Creates both V4L and DVB API specification + ======================= Module selection rules: (Those may require write access to kernel tree) diff --git a/README.patches b/README.patches index 28d697302..f989bc3f3 100644 --- a/README.patches +++ b/README.patches @@ -7,13 +7,13 @@ and by the v4l-dvb community. (*) This is just an aka for the main developers involved in V4L/DVB drivers. They are a volunteer and unremunerated group of people that have the common interest of providing a good support on Linux for -receiving and capturing video streams from Web cams, Analog TV, Digital +receiving and capturing video streams from webcams, analog TV, digital TV and radio broadcast AM/FM. CONTENTS ======== - 1. A Brief introduction about patch management + 1. A brief introduction about patch management 2. Git trees' relationships with v4l/dvb development 3. Mercurial trees used for v4l/dvb development 4. Community best practices @@ -23,7 +23,7 @@ TV and radio broadcast AM/FM. 8. Identifying regressions with Mercurial 9. Creating a newer driver -1. A Brief introduction about patch management +1. A brief introduction about patch management ========================================== Current V4L/DVB development is based on a modern SCM system that @@ -35,7 +35,7 @@ final users. That's the reason why hg was selected for V4L/DVB development. There are some tutorials, FAQs and other valuable information at -http://selenic.com/mercurial about hg usage. +http://selenic.com/mercurial/ about hg usage. Mercurial is a distributed SCM, which means every developer gets his own full copy of the repository (including the complete revision @@ -44,7 +44,7 @@ The resulting changesets can then be exchanged between repositories and finally merged into a common repository on linuxtv.org. A list of current available repositories is available at: - http://linuxtv.org/hg + http://linuxtv.org/hg/ 2. Git and Mercurial trees' relationships with v4l/dvb development =============================================================== @@ -58,12 +58,13 @@ The main kernel trees is owned by Linus Torvalds, being located at: The subsystem master tree is owned by the subsystem maintainer (Mauro Carvalho Chehab) being located at: http://git.kernel.org/?p=linux/kernel/git/mchehab/linux-2.6.git + A tree with development patches that aren't ready yet for upstream is handled at: http://git.kernel.org/?p=linux/kernel/git/mchehab/devel.git -There's also an experimental tree, that contains all experimental patches -from subsystem trees. It is called linux-next. Its purpose is to check in +There is also an experimental tree, that contains all experimental patches +from subsystem trees, called linux-next. Its purpose is to check in advance if patches from different trees would conflict. The main tree for linux-next is owned by Stephen Rothwell and it is located at: http://git.kernel.org/?p=linux/kernel/git/sfr/linux-next.git @@ -79,7 +80,7 @@ located at: Before committing into the master -git tree, the finished patches from each maintainers tree are added on a staging tree, owned by the subsystem maintainer, at: - http://linuxtv.org/hg/v4l-dvb + http://linuxtv.org/hg/v4l-dvb/ The main function of this tree is to merge patches from other repositories and to test the entire subsystem with the finished patches. @@ -87,7 +88,7 @@ This is also the recommended tree for users interested on testing newer V4L/DVB patches and drivers. Users are welcome to use, test and report any issues via the mailing -lists or via the Kernel's bugzilla, available at: +lists or via the Kernel Bug Tracker, available at: http://bugzilla.kernel.org Michael Krufky maintains a backport tree, containing a subset of the @@ -95,7 +96,7 @@ patches from the subsystem tree that are meant to be sent to kernel -stable team, at: http://git.kernel.org/http://git.kernel.org/?p=linux/kernel/git/mkrufky/v4l-dvb-2.6.x.y.git -3. Other mercurial trees used for v4l/dvb development +3. Other Mercurial trees used for v4l/dvb development ================================================== V4L/DVB driver development is hosted at http://linuxtv.org. There are a @@ -132,7 +133,7 @@ a) Every developer should follow the "rules of thumb" of kernel development Documentation/SubmitChecklist Documentation/CodingStyle -b) All commits at mercurial trees should have a consistent message, +b) All commits at Mercurial trees should have a consistent message, describing the patch. This is done by using: make commit @@ -157,7 +158,7 @@ b) All commits at mercurial trees should have a consistent message, Signed-off-by: nowhere <nowhere@noplace.org> All lines starting with # and all lines starting with HG: will be - removed from the mercurial commit log. + removed from the Mercurial commit log. *WARNING* Be careful not to leave the first line blank, otherwise hg will leave subject blank. @@ -212,9 +213,9 @@ d) For "make commit" to work properly, the HGUSER shell environment var It is strongly recommended to have those lines in .bashrc or .profile. -e) All patches shall have a Developers Certificate of Origin +e) All patches shall have a Developer's Certificate of Origin version 1.1 in the commit log or the email description, signed by the - patch authors, as postulated in the Linux Kernel source at: + patch authors, as postulated in the Linux kernel source at: Documentation/SubmittingPatches @@ -255,19 +256,19 @@ g) Another kernel's practice that is agreed to be good is that a Reviewed-by: My Name <myemail@mysite.com> This is particularly important for Kernel to userspace ABI changes. -h) If the patch also affects other parts of kernel (like Alsa +h) If the patch also affects other parts of kernel (like ALSA or i2c), it is required that, when submitting upstream, the patch also goes to the maintainers of that subsystem. To do this, the developer shall copy the interested parties. - At mercurial tree, this can be handled automatically by the LinuxTV + At Mercurial tree, this can be handled automatically by the LinuxTV scripts, by using the cc: meta tag, together with the Signed-off-by lines. Something like: CC: someotherkerneldeveloper@someplace Signed-off-by: nowhere <nowhere@noplace.org> - This way, when a patch arrives mercurial hg tree, a mailbomb script + This way, when a patch arrives Mercurial hg tree, a mailbomb script will copy the proper interested parties. When submitting a patch via e-mail, it is better to copy all interested @@ -283,8 +284,8 @@ i) If the patch modifies the include/linux/videodev2.h file, then you document still builds. Of course, any changes you make to the public V4L2 API must be documented anyway. -j) Sometimes, mainstream changes affect the v4l-dvb tree, and mast be - backported to the v4l-dvb tree. This kind of commit to the mercurial +j) Sometimes, mainstream changes affect the v4l-dvb tree, and must be + backported to the v4l-dvb tree. This kind of commit to the Mercurial tree should follow the rules above and should also have the line: kernel-sync: @@ -352,7 +353,7 @@ l) To import contributed stuff to a developer's, a script is provided. machine, since git has a gitimport script that is used by mailimport. There's also a helper script to make easier to retrieve patches from - other mercurial repositories. The syntax is: + other Mercurial repositories. The syntax is: ./hgimport <URL> Also, hg has a feature, called mqueue, that allows having several patches @@ -437,7 +438,7 @@ Those changes will require some manual sync between -git and -hg, it is better to avoid those circumstances. During the procedure of generating kernel patches, the maintainer uses -to do a diff between the kernel tree and v4l-dvb mercurial tree +to do a diff between the kernel tree and v4l-dvb Mercurial tree (without any backport code there). If there are discrepancies, a backport patch from mainstream to v4l-dvb is generally applied by the maintainer. @@ -498,9 +499,9 @@ c. Please include a brief description in the headers of your This is just a sample commit comment, just for reference purposes. This does nothing. - Signed-off-by nowhere <nowere@noplace.org> + Signed-off-by: nowhere <nowere@noplace.org> -d. Every patch shall have a Developers Certificate of Origin and should +d. Every patch shall have a Developer's Certificate of Origin and should be submitted by one of its authors. All the patch authors should sign it. @@ -522,7 +523,7 @@ g. If it is a newer driver (not yet in one of the development trees), The better way for you to identify regressions with Mercurial is to use hg bisect. This is an extension provided with the current - mercurial versions. For it to work, you need to have the proper setup + Mercurial versions. For it to work, you need to have the proper setup at an hgrc file. To test if bisect is working, you can do: hg bisect help diff --git a/dvb-spec/dvbapi/dvbapi.tex b/dvb-spec/dvbapi/dvbapi.tex index d75c2417f..74fc6510e 100644 --- a/dvb-spec/dvbapi/dvbapi.tex +++ b/dvb-spec/dvbapi/dvbapi.tex @@ -4,7 +4,7 @@ \usepackage{times} %\usepackage[hang]{caption} -\usepackage{fancyheadings} +\usepackage{fancyhdr} %\usepackage{lucidabr} %\usepackage{fancybox} \usepackage{array} @@ -24,82 +24,82 @@ \newcommand{\function}[5]{ \subsection{#1} - \noindent DESCRIPTION + \noindent DESCRIPTION \medskip - + \begin{tabular}[h]{p{11cm}} #3 \end{tabular} - + \medskip - \noindent SYNOPSIS + \noindent SYNOPSIS \medskip \begin{tabular}[h]{p{11cm}} {\tt #2} \end{tabular} - - %\item[] + + %\item[] \medskip -\noindent PARAMETERS +\noindent PARAMETERS \medskip - + \begin{tabular}[h]{p{3cm}p{8cm}} #4 \end{tabular} - - %\item[] + + %\item[] \medskip \noindent ERRORS \medskip - + \begin{tabular}[h]{p{3cm}p{8cm}} #5 \end{tabular} - + %\end{itemize} - + \medskip } \def\ifunction#1#2#3#4#5{\function{#1\index{#1}}{#2}{#3}{#4}{#5}} \newcommand{\kfunction}[5]{ \subsection{#1} - \noindent DESCRIPTION + \noindent DESCRIPTION \medskip \begin{tabular}[h]{p{11cm}} #3 \end{tabular} - + \medskip - \noindent SYNOPSIS + \noindent SYNOPSIS \medskip \begin{tabular}[h]{p{11cm}} {\tt #2} \end{tabular} - - %\item[] + + %\item[] \medskip -\noindent PARAMETERS +\noindent PARAMETERS \medskip - + \begin{tabular}[h]{p{3cm}p{8cm}} #4 \end{tabular} - - %\item[] + + %\item[] \medskip \noindent RETURNS \medskip - + \begin{tabular}[h]{p{3cm}p{8cm}} #5 \end{tabular} - + %\end{itemize} - + \medskip } \def\kifunction#1#2#3#4#5{\kfunction{#1\index{#1}}{#2}{#3}{#4}{#5}} @@ -164,7 +164,7 @@ \end{document} -%%% Local Variables: +%%% Local Variables: %%% mode: latex %%% TeX-master: t -%%% End: +%%% End: diff --git a/linux/Documentation/dvb/README.flexcop b/linux/Documentation/dvb/README.flexcop deleted file mode 100644 index 5515469de..000000000 --- a/linux/Documentation/dvb/README.flexcop +++ /dev/null @@ -1,205 +0,0 @@ -This README escorted the skystar2-driver rewriting procedure. It describes the -state of the new flexcop-driver set and some internals are written down here -too. - -This document hopefully describes things about the flexcop and its -device-offsprings. Goal was to write an easy-to-write and easy-to-read set of -drivers based on the skystar2.c and other information. - -Remark: flexcop-pci.c was a copy of skystar2.c, but every line has been -touched and rewritten. - -History & News -============== - 2005-04-01 - correct USB ISOC transfers (thanks to Vadim Catana) - - - - -General coding processing -========================= - -We should proceed as follows (as long as no one complains): - -0) Think before start writing code! - -1) rewriting the skystar2.c with the help of the flexcop register descriptions -and splitting up the files to a pci-bus-part and a flexcop-part. -The new driver will be called b2c2-flexcop-pci.ko/b2c2-flexcop-usb.ko for the -device-specific part and b2c2-flexcop.ko for the common flexcop-functions. - -2) Search for errors in the leftover of flexcop-pci.c (compare with pluto2.c -and other pci drivers) - -3) make some beautification (see 'Improvements when rewriting (refactoring) is -done') - -4) Testing the new driver and maybe substitute the skystar2.c with it, to reach -a wider tester audience. - -5) creating an usb-bus-part using the already written flexcop code for the pci -card. - -Idea: create a kernel-object for the flexcop and export all important -functions. This option saves kernel-memory, but maybe a lot of functions have -to be exported to kernel namespace. - - -Current situation -================= - -0) Done :) -1) Done (some minor issues left) -2) Done -3) Not ready yet, more information is necessary -4) next to be done (see the table below) -5) USB driver is working (yes, there are some minor issues) - -What seems to be ready? ------------------------ - -1) Rewriting -1a) i2c is cut off from the flexcop-pci.c and seems to work -1b) moved tuner and demod stuff from flexcop-pci.c to flexcop-tuner-fe.c -1c) moved lnb and diseqc stuff from flexcop-pci.c to flexcop-tuner-fe.c -1e) eeprom (reading MAC address) -1d) sram (no dynamic sll size detection (commented out) (using default as JJ told me)) -1f) misc. register accesses for reading parameters (e.g. resetting, revision) -1g) pid/mac filter (flexcop-hw-filter.c) -1i) dvb-stuff initialization in flexcop.c (done) -1h) dma stuff (now just using the size-irq, instead of all-together, to be done) -1j) remove flexcop initialization from flexcop-pci.c completely (done) -1l) use a well working dma IRQ method (done, see 'Known bugs and problems and TODO') -1k) cleanup flexcop-files (remove unused EXPORT_SYMBOLs, make static from -non-static where possible, moved code to proper places) - -2) Search for errors in the leftover of flexcop-pci.c (partially done) -5a) add MAC address reading -5c) feeding of ISOC data to the software demux (format of the isochronous data -and speed optimization, no real error) (thanks to Vadim Catana) - -What to do in the near future? --------------------------------------- -(no special order here) - -5) USB driver -5b) optimize isoc-transfer (submitting/killing isoc URBs when transfer is starting) - -Testing changes ---------------- - -O = item is working -P = item is partially working -X = item is not working -N = item does not apply here -<empty field> = item need to be examined - - | PCI | USB -item | mt352 | nxt2002 | stv0299 | mt312 | mt352 | nxt2002 | stv0299 | mt312 --------+-------+---------+---------+-------+-------+---------+---------+------- -1a) | O | | | | N | N | N | N -1b) | O | | | | | | O | -1c) | N | N | | | N | N | O | -1d) | O | O -1e) | O | O -1f) | P -1g) | O -1h) | P | -1i) | O | N -1j) | O | N -1l) | O | N -2) | O | N -5a) | N | O -5b)* | N | -5c) | N | O - -* - not done yet - -Known bugs and problems and TODO --------------------------------- - -1g/h/l) when pid filtering is enabled on the pci card - -DMA usage currently: - The DMA is splitted in 2 equal-sized subbuffers. The Flexcop writes to first - address and triggers an IRQ when it's full and starts writing to the second - address. When the second address is full, the IRQ is triggered again, and - the flexcop writes to first address again, and so on. - The buffersize of each address is currently 640*188 bytes. - - Problem is, when using hw-pid-filtering and doing some low-bandwidth - operation (like scanning) the buffers won't be filled enough to trigger - the IRQ. That's why: - - When PID filtering is activated, the timer IRQ is used. Every 1.97 ms the IRQ - is triggered. Is the current write address of DMA1 different to the one - during the last IRQ, then the data is passed to the demuxer. - - There is an additional DMA-IRQ-method: packet count IRQ. This isn't - implemented correctly yet. - - The solution is to disable HW PID filtering, but I don't know how the DVB - API software demux behaves on slow systems with 45MBit/s TS. - -Solved bugs :) --------------- -1g) pid-filtering (somehow pid index 4 and 5 (EMM_PID and ECM_PID) aren't -working) -SOLUTION: also index 0 was affected, because net_translation is done for -these indexes by default - -5b) isochronous transfer does only work in the first attempt (for the Sky2PC -USB, Air2PC is working) SOLUTION: the flexcop was going asleep and never really -woke up again (don't know if this need fixes, see -flexcop-fe-tuner.c:flexcop_sleep) - -NEWS: when the driver is loaded and unloaded and loaded again (w/o doing -anything in the while the driver is loaded the first time), no transfers take -place anymore. - -Improvements when rewriting (refactoring) is done -================================================= - -- split sleeping of the flexcop (misc_204.ACPI3_sig = 1;) from lnb_control - (enable sleeping for other demods than dvb-s) -- add support for CableStar (stv0297 Microtune 203x/ALPS) (almost done, incompatibilities with the Nexus-CA) - -Debugging ---------- -- add verbose debugging to skystar2.c (dump the reg_dw_data) and compare it - with this flexcop, this is important, because i2c is now using the - flexcop_ibi_value union from flexcop-reg.h (do you have a better idea for - that, please tell us so). - -Everything which is identical in the following table, can be put into a common -flexcop-module. - - PCI USB -------------------------------------------------------------------------------- -Different: -Register access: accessing IO memory USB control message -I2C bus: I2C bus of the FC USB control message -Data transfer: DMA isochronous transfer -EEPROM transfer: through i2c bus not clear yet - -Identical: -Streaming: accessing registers -PID Filtering: accessing registers -Sram destinations: accessing registers -Tuner/Demod: I2C bus -DVB-stuff: can be written for common use - -Acknowledgements (just for the rewriting part) -================ - -Bjarne Steinsbo thought a lot in the first place of the pci part for this code -sharing idea. - -Andreas Oberritter for providing a recent PCI initialization template -(pluto2.c). - -Boleslaw Ciesielski for pointing out a problem with firmware loader. - -Vadim Catana for correcting the USB transfer. - -comments, critics and ideas to linux-dvb@linuxtv.org. diff --git a/linux/Documentation/dvb/technisat.txt b/linux/Documentation/dvb/technisat.txt index cdf6ee4b2..3f435ffb2 100644 --- a/linux/Documentation/dvb/technisat.txt +++ b/linux/Documentation/dvb/technisat.txt @@ -1,5 +1,5 @@ -How to set up the Technisat devices -=================================== +How to set up the Technisat/B2C2 Flexcop devices +================================================ 1) Find out what device you have ================================ @@ -16,54 +16,60 @@ DVB: registering frontend 0 (Conexant CX24123/CX24109)... If the Technisat is the only TV device in your box get rid of unnecessary modules and check this one: "Multimedia devices" => "Customise analog and hybrid tuner modules to build" -In this directory uncheck every driver which is activated there. +In this directory uncheck every driver which is activated there (except "Simple tuner support" for case 9 only). Then please activate: 2a) Main module part: a.)"Multimedia devices" => "DVB/ATSC adapters" => "Technisat/B2C2 FlexcopII(b) and FlexCopIII adapters" -b.)"Multimedia devices" => "DVB/ATSC adapters" => "Technisat/B2C2 FlexcopII(b) and FlexCopIII adapters" => "Technisat/B2C2 Air/Sky/Cable2PC PCI" in case of a PCI card OR +b.)"Multimedia devices" => "DVB/ATSC adapters" => "Technisat/B2C2 FlexcopII(b) and FlexCopIII adapters" => "Technisat/B2C2 Air/Sky/Cable2PC PCI" in case of a PCI card +OR c.)"Multimedia devices" => "DVB/ATSC adapters" => "Technisat/B2C2 FlexcopII(b) and FlexCopIII adapters" => "Technisat/B2C2 Air/Sky/Cable2PC USB" in case of an USB 1.1 adapter d.)"Multimedia devices" => "DVB/ATSC adapters" => "Technisat/B2C2 FlexcopII(b) and FlexCopIII adapters" => "Enable debug for the B2C2 FlexCop drivers" Notice: d.) is helpful for troubleshooting 2b) Frontend module part: -1.) Revision 2.3: +1.) SkyStar DVB-S Revision 2.3: a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build" b.)"Multimedia devices" => "Customise DVB frontends" => "Zarlink VP310/MT312/ZL10313 based" -2.) Revision 2.6: +2.) SkyStar DVB-S Revision 2.6: a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build" b.)"Multimedia devices" => "Customise DVB frontends" => "ST STV0299 based" -3.) Revision 2.7: +3.) SkyStar DVB-S Revision 2.7: a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build" b.)"Multimedia devices" => "Customise DVB frontends" => "Samsung S5H1420 based" c.)"Multimedia devices" => "Customise DVB frontends" => "Integrant ITD1000 Zero IF tuner for DVB-S/DSS" d.)"Multimedia devices" => "Customise DVB frontends" => "ISL6421 SEC controller" -4.) Revision 2.8: +4.) SkyStar DVB-S Revision 2.8: a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build" b.)"Multimedia devices" => "Customise DVB frontends" => "Conexant CX24113/CX24128 tuner for DVB-S/DSS" c.)"Multimedia devices" => "Customise DVB frontends" => "Conexant CX24123 based" d.)"Multimedia devices" => "Customise DVB frontends" => "ISL6421 SEC controller" -5.) DVB-T card: +5.) AirStar DVB-T card: a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build" b.)"Multimedia devices" => "Customise DVB frontends" => "Zarlink MT352 based" -6.) DVB-C card: +6.) CableStar DVB-C card: a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build" b.)"Multimedia devices" => "Customise DVB frontends" => "ST STV0297 based" -7.) ATSC card 1st generation: +7.) AirStar ATSC card 1st generation: a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build" b.)"Multimedia devices" => "Customise DVB frontends" => "Broadcom BCM3510" -8.) ATSC card 2nd generation: +8.) AirStar ATSC card 2nd generation: a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build" b.)"Multimedia devices" => "Customise DVB frontends" => "NxtWave Communications NXT2002/NXT2004 based" -c.)"Multimedia devices" => "Customise DVB frontends" => "LG Electronics LGDT3302/LGDT3303 based" +c.)"Multimedia devices" => "Customise DVB frontends" => "Generic I2C PLL based tuners" -Author: Uwe Bugla <uwe.bugla@gmx.de> December 2008 +9.) AirStar ATSC card 3rd generation: +a.)"Multimedia devices" => "Customise DVB frontends" => "Customise the frontend modules to build" +b.)"Multimedia devices" => "Customise DVB frontends" => "LG Electronics LGDT3302/LGDT3303 based" +c.)"Multimedia devices" => "Customise analog and hybrid tuner modules to build" => "Simple tuner support" + +Author: Uwe Bugla <uwe.bugla@gmx.de> February 2009 diff --git a/linux/arch/arm/plat-mxc/include/mach/mx3_camera.h b/linux/arch/arm/plat-mxc/include/mach/mx3_camera.h new file mode 100644 index 000000000..36d7ff27b --- /dev/null +++ b/linux/arch/arm/plat-mxc/include/mach/mx3_camera.h @@ -0,0 +1,52 @@ +/* + * mx3_camera.h - i.MX3x camera driver header file + * + * Copyright (C) 2008, Guennadi Liakhovetski, DENX Software Engineering, <lg@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. + * + * 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 _MX3_CAMERA_H_ +#define _MX3_CAMERA_H_ + +#include <linux/device.h> + +#define MX3_CAMERA_CLK_SRC 1 +#define MX3_CAMERA_EXT_VSYNC 2 +#define MX3_CAMERA_DP 4 +#define MX3_CAMERA_PCP 8 +#define MX3_CAMERA_HSP 0x10 +#define MX3_CAMERA_VSP 0x20 +#define MX3_CAMERA_DATAWIDTH_4 0x40 +#define MX3_CAMERA_DATAWIDTH_8 0x80 +#define MX3_CAMERA_DATAWIDTH_10 0x100 +#define MX3_CAMERA_DATAWIDTH_15 0x200 + +#define MX3_CAMERA_DATAWIDTH_MASK (MX3_CAMERA_DATAWIDTH_4 | MX3_CAMERA_DATAWIDTH_8 | \ + MX3_CAMERA_DATAWIDTH_10 | MX3_CAMERA_DATAWIDTH_15) + +/** + * struct mx3_camera_pdata - i.MX3x camera platform data + * @flags: MX3_CAMERA_* flags + * @mclk_10khz: master clock frequency in 10kHz units + * @dma_dev: IPU DMA device to match against in channel allocation + */ +struct mx3_camera_pdata { + unsigned long flags; + unsigned long mclk_10khz; + struct device *dma_dev; +}; + +#endif diff --git a/linux/arch/sh/boards/board-ap325rxa.c b/linux/arch/sh/boards/board-ap325rxa.c new file mode 100644 index 000000000..78a9395fe --- /dev/null +++ b/linux/arch/sh/boards/board-ap325rxa.c @@ -0,0 +1,526 @@ +/* + * Renesas - AP-325RXA + * (Compatible with Algo System ., LTD. - AP-320A) + * + * Copyright (C) 2008 Renesas Solutions Corp. + * Author : Yusuke Goda <goda.yuske@renesas.com> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/init.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/mtd/physmap.h> +#include <linux/mtd/sh_flctl.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/smsc911x.h> +#include <linux/gpio.h> +#include <linux/spi/spi.h> +#include <linux/spi/spi_gpio.h> +#include <media/ov772x.h> +#include <media/soc_camera_platform.h> +#include <media/sh_mobile_ceu.h> +#include <video/sh_mobile_lcdc.h> +#include <asm/io.h> +#include <asm/clock.h> +#include <cpu/sh7723.h> + +static struct smsc911x_platform_config smsc911x_config = { + .phy_interface = PHY_INTERFACE_MODE_MII, + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, + .irq_type = SMSC911X_IRQ_TYPE_OPEN_DRAIN, + .flags = SMSC911X_USE_32BIT, +}; + +static struct resource smsc9118_resources[] = { + [0] = { + .start = 0xb6080000, + .end = 0xb60fffff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = 35, + .end = 35, + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device smsc9118_device = { + .name = "smsc911x", + .id = -1, + .num_resources = ARRAY_SIZE(smsc9118_resources), + .resource = smsc9118_resources, + .dev = { + .platform_data = &smsc911x_config, + }, +}; + +/* + * AP320 and AP325RXA has CPLD data in NOR Flash(0xA80000-0xABFFFF). + * If this area erased, this board can not boot. + */ +static struct mtd_partition ap325rxa_nor_flash_partitions[] = { + { + .name = "uboot", + .offset = 0, + .size = (1 * 1024 * 1024), + .mask_flags = MTD_WRITEABLE, /* Read-only */ + }, { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = (2 * 1024 * 1024), + }, { + .name = "free-area0", + .offset = MTDPART_OFS_APPEND, + .size = ((7 * 1024 * 1024) + (512 * 1024)), + }, { + .name = "CPLD-Data", + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE, /* Read-only */ + .size = (1024 * 128 * 2), + }, { + .name = "free-area1", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + }, +}; + +static struct physmap_flash_data ap325rxa_nor_flash_data = { + .width = 2, + .parts = ap325rxa_nor_flash_partitions, + .nr_parts = ARRAY_SIZE(ap325rxa_nor_flash_partitions), +}; + +static struct resource ap325rxa_nor_flash_resources[] = { + [0] = { + .name = "NOR Flash", + .start = 0x00000000, + .end = 0x00ffffff, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device ap325rxa_nor_flash_device = { + .name = "physmap-flash", + .resource = ap325rxa_nor_flash_resources, + .num_resources = ARRAY_SIZE(ap325rxa_nor_flash_resources), + .dev = { + .platform_data = &ap325rxa_nor_flash_data, + }, +}; + +static struct mtd_partition nand_partition_info[] = { + { + .name = "nand_data", + .offset = 0, + .size = MTDPART_SIZ_FULL, + }, +}; + +static struct resource nand_flash_resources[] = { + [0] = { + .start = 0xa4530000, + .end = 0xa45300ff, + .flags = IORESOURCE_MEM, + } +}; + +static struct sh_flctl_platform_data nand_flash_data = { + .parts = nand_partition_info, + .nr_parts = ARRAY_SIZE(nand_partition_info), + .flcmncr_val = FCKSEL_E | TYPESEL_SET | NANWF_E, + .has_hwecc = 1, +}; + +static struct platform_device nand_flash_device = { + .name = "sh_flctl", + .resource = nand_flash_resources, + .num_resources = ARRAY_SIZE(nand_flash_resources), + .dev = { + .platform_data = &nand_flash_data, + }, +}; + +#define FPGA_LCDREG 0xB4100180 +#define FPGA_BKLREG 0xB4100212 +#define FPGA_LCDREG_VAL 0x0018 +#define PORT_MSELCRB 0xA4050182 +#define PORT_HIZCRC 0xA405015C +#define PORT_DRVCRA 0xA405018A +#define PORT_DRVCRB 0xA405018C + +static void ap320_wvga_power_on(void *board_data) +{ + msleep(100); + + /* ASD AP-320/325 LCD ON */ + ctrl_outw(FPGA_LCDREG_VAL, FPGA_LCDREG); + + /* backlight */ + gpio_set_value(GPIO_PTS3, 0); + ctrl_outw(0x100, FPGA_BKLREG); +} + +static struct sh_mobile_lcdc_info lcdc_info = { + .clock_source = LCDC_CLK_EXTERNAL, + .ch[0] = { + .chan = LCDC_CHAN_MAINLCD, + .bpp = 16, + .interface_type = RGB18, + .clock_divider = 1, + .lcd_cfg = { + .name = "LB070WV1", + .xres = 800, + .yres = 480, + .left_margin = 40, + .right_margin = 160, + .hsync_len = 8, + .upper_margin = 63, + .lower_margin = 80, + .vsync_len = 1, + .sync = 0, /* hsync and vsync are active low */ + }, + .lcd_size_cfg = { /* 7.0 inch */ + .width = 152, + .height = 91, + }, + .board_cfg = { + .display_on = ap320_wvga_power_on, + }, + } +}; + +static struct resource lcdc_resources[] = { + [0] = { + .name = "LCDC", + .start = 0xfe940000, /* P4-only space */ + .end = 0xfe941fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = 28, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device lcdc_device = { + .name = "sh_mobile_lcdc_fb", + .num_resources = ARRAY_SIZE(lcdc_resources), + .resource = lcdc_resources, + .dev = { + .platform_data = &lcdc_info, + }, +}; + +static void camera_power(int val) +{ + gpio_set_value(GPIO_PTZ5, val); /* RST_CAM/RSTB */ + mdelay(10); +} + +#ifdef CONFIG_I2C +/* support for the old ncm03j camera */ +static unsigned char camera_ncm03j_magic[] = +{ + 0x87, 0x00, 0x88, 0x08, 0x89, 0x01, 0x8A, 0xE8, + 0x1D, 0x00, 0x1E, 0x8A, 0x21, 0x00, 0x33, 0x36, + 0x36, 0x60, 0x37, 0x08, 0x3B, 0x31, 0x44, 0x0F, + 0x46, 0xF0, 0x4B, 0x28, 0x4C, 0x21, 0x4D, 0x55, + 0x4E, 0x1B, 0x4F, 0xC7, 0x50, 0xFC, 0x51, 0x12, + 0x58, 0x02, 0x66, 0xC0, 0x67, 0x46, 0x6B, 0xA0, + 0x6C, 0x34, 0x7E, 0x25, 0x7F, 0x25, 0x8D, 0x0F, + 0x92, 0x40, 0x93, 0x04, 0x94, 0x26, 0x95, 0x0A, + 0x99, 0x03, 0x9A, 0xF0, 0x9B, 0x14, 0x9D, 0x7A, + 0xC5, 0x02, 0xD6, 0x07, 0x59, 0x00, 0x5A, 0x1A, + 0x5B, 0x2A, 0x5C, 0x37, 0x5D, 0x42, 0x5E, 0x56, + 0xC8, 0x00, 0xC9, 0x1A, 0xCA, 0x2A, 0xCB, 0x37, + 0xCC, 0x42, 0xCD, 0x56, 0xCE, 0x00, 0xCF, 0x1A, + 0xD0, 0x2A, 0xD1, 0x37, 0xD2, 0x42, 0xD3, 0x56, + 0x5F, 0x68, 0x60, 0x87, 0x61, 0xA3, 0x62, 0xBC, + 0x63, 0xD4, 0x64, 0xEA, 0xD6, 0x0F, +}; + +static int camera_probe(void) +{ + struct i2c_adapter *a = i2c_get_adapter(0); + struct i2c_msg msg; + int ret; + + camera_power(1); + msg.addr = 0x6e; + msg.buf = camera_ncm03j_magic; + msg.len = 2; + msg.flags = 0; + ret = i2c_transfer(a, &msg, 1); + camera_power(0); + + return ret; +} + +static int camera_set_capture(struct soc_camera_platform_info *info, + int enable) +{ + struct i2c_adapter *a = i2c_get_adapter(0); + struct i2c_msg msg; + int ret = 0; + int i; + + camera_power(0); + if (!enable) + return 0; /* no disable for now */ + + camera_power(1); + for (i = 0; i < ARRAY_SIZE(camera_ncm03j_magic); i += 2) { + u_int8_t buf[8]; + + msg.addr = 0x6e; + msg.buf = buf; + msg.len = 2; + msg.flags = 0; + + buf[0] = camera_ncm03j_magic[i]; + buf[1] = camera_ncm03j_magic[i + 1]; + + ret = (ret < 0) ? ret : i2c_transfer(a, &msg, 1); + } + + return ret; +} + +static struct soc_camera_platform_info camera_info = { + .iface = 0, + .format_name = "UYVY", + .format_depth = 16, + .format = { + .pixelformat = V4L2_PIX_FMT_UYVY, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + .width = 640, + .height = 480, + }, + .bus_param = SOCAM_PCLK_SAMPLE_RISING | SOCAM_HSYNC_ACTIVE_HIGH | + SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_MASTER | SOCAM_DATAWIDTH_8, + .set_capture = camera_set_capture, +}; + +static struct platform_device camera_device = { + .name = "soc_camera_platform", + .dev = { + .platform_data = &camera_info, + }, +}; + +static int __init camera_setup(void) +{ + if (camera_probe() > 0) + platform_device_register(&camera_device); + + return 0; +} +late_initcall(camera_setup); + +#endif /* CONFIG_I2C */ + +static int ov7725_power(struct device *dev, int mode) +{ + camera_power(0); + if (mode) + camera_power(1); + + return 0; +} + +static struct ov772x_camera_info ov7725_info = { + .buswidth = SOCAM_DATAWIDTH_8, + .flags = OV772X_FLAG_VFLIP | OV772X_FLAG_HFLIP, + .link = { + .power = ov7725_power, + }, +}; + +static struct sh_mobile_ceu_info sh_mobile_ceu_info = { + .flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_HSYNC_ACTIVE_HIGH | + SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH | SOCAM_MASTER | + SOCAM_DATAWIDTH_8, +}; + +static struct resource ceu_resources[] = { + [0] = { + .name = "CEU", + .start = 0xfe910000, + .end = 0xfe91009f, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = 52, + .flags = IORESOURCE_IRQ, + }, + [2] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device ceu_device = { + .name = "sh_mobile_ceu", + .id = 0, /* "ceu0" clock */ + .num_resources = ARRAY_SIZE(ceu_resources), + .resource = ceu_resources, + .dev = { + .platform_data = &sh_mobile_ceu_info, + }, +}; + +struct spi_gpio_platform_data sdcard_cn3_platform_data = { + .sck = GPIO_PTD0, + .mosi = GPIO_PTD1, + .miso = GPIO_PTD2, + .num_chipselect = 1, +}; + +static struct platform_device sdcard_cn3_device = { + .name = "spi_gpio", + .dev = { + .platform_data = &sdcard_cn3_platform_data, + }, +}; + +static struct platform_device *ap325rxa_devices[] __initdata = { + &smsc9118_device, + &ap325rxa_nor_flash_device, + &lcdc_device, + &ceu_device, + &nand_flash_device, + &sdcard_cn3_device, +}; + +static struct i2c_board_info __initdata ap325rxa_i2c_devices[] = { + { + I2C_BOARD_INFO("pcf8563", 0x51), + }, + { + I2C_BOARD_INFO("ov772x", 0x21), + .platform_data = &ov7725_info, + }, +}; + +static struct spi_board_info ap325rxa_spi_devices[] = { + { + .modalias = "mmc_spi", + .max_speed_hz = 5000000, + .chip_select = 0, + .controller_data = (void *) GPIO_PTD5, + }, +}; + +static int __init ap325rxa_devices_setup(void) +{ + /* LD3 and LD4 LEDs */ + gpio_request(GPIO_PTX5, NULL); /* RUN */ + gpio_direction_output(GPIO_PTX5, 1); + gpio_export(GPIO_PTX5, 0); + + gpio_request(GPIO_PTX4, NULL); /* INDICATOR */ + gpio_direction_output(GPIO_PTX4, 0); + gpio_export(GPIO_PTX4, 0); + + /* SW1 input */ + gpio_request(GPIO_PTF7, NULL); /* MODE */ + gpio_direction_input(GPIO_PTF7); + gpio_export(GPIO_PTF7, 0); + + /* LCDC */ + gpio_request(GPIO_FN_LCDD15, NULL); + gpio_request(GPIO_FN_LCDD14, NULL); + gpio_request(GPIO_FN_LCDD13, NULL); + gpio_request(GPIO_FN_LCDD12, NULL); + gpio_request(GPIO_FN_LCDD11, NULL); + gpio_request(GPIO_FN_LCDD10, NULL); + gpio_request(GPIO_FN_LCDD9, NULL); + gpio_request(GPIO_FN_LCDD8, NULL); + gpio_request(GPIO_FN_LCDD7, NULL); + gpio_request(GPIO_FN_LCDD6, NULL); + gpio_request(GPIO_FN_LCDD5, NULL); + gpio_request(GPIO_FN_LCDD4, NULL); + gpio_request(GPIO_FN_LCDD3, NULL); + gpio_request(GPIO_FN_LCDD2, NULL); + gpio_request(GPIO_FN_LCDD1, NULL); + gpio_request(GPIO_FN_LCDD0, NULL); + gpio_request(GPIO_FN_LCDLCLK_PTR, NULL); + gpio_request(GPIO_FN_LCDDCK, NULL); + gpio_request(GPIO_FN_LCDVEPWC, NULL); + gpio_request(GPIO_FN_LCDVCPWC, NULL); + gpio_request(GPIO_FN_LCDVSYN, NULL); + gpio_request(GPIO_FN_LCDHSYN, NULL); + gpio_request(GPIO_FN_LCDDISP, NULL); + gpio_request(GPIO_FN_LCDDON, NULL); + + /* LCD backlight */ + gpio_request(GPIO_PTS3, NULL); + gpio_direction_output(GPIO_PTS3, 1); + + /* CEU */ + gpio_request(GPIO_FN_VIO_CLK2, NULL); + gpio_request(GPIO_FN_VIO_VD2, NULL); + gpio_request(GPIO_FN_VIO_HD2, NULL); + gpio_request(GPIO_FN_VIO_FLD, NULL); + gpio_request(GPIO_FN_VIO_CKO, NULL); + gpio_request(GPIO_FN_VIO_D15, NULL); + gpio_request(GPIO_FN_VIO_D14, NULL); + gpio_request(GPIO_FN_VIO_D13, NULL); + gpio_request(GPIO_FN_VIO_D12, NULL); + gpio_request(GPIO_FN_VIO_D11, NULL); + gpio_request(GPIO_FN_VIO_D10, NULL); + gpio_request(GPIO_FN_VIO_D9, NULL); + gpio_request(GPIO_FN_VIO_D8, NULL); + + gpio_request(GPIO_PTZ7, NULL); + gpio_direction_output(GPIO_PTZ7, 0); /* OE_CAM */ + gpio_request(GPIO_PTZ6, NULL); + gpio_direction_output(GPIO_PTZ6, 0); /* STBY_CAM */ + gpio_request(GPIO_PTZ5, NULL); + gpio_direction_output(GPIO_PTZ5, 0); /* RST_CAM */ + gpio_request(GPIO_PTZ4, NULL); + gpio_direction_output(GPIO_PTZ4, 0); /* SADDR */ + + ctrl_outw(ctrl_inw(PORT_MSELCRB) & ~0x0001, PORT_MSELCRB); + + /* FLCTL */ + gpio_request(GPIO_FN_FCE, NULL); + gpio_request(GPIO_FN_NAF7, NULL); + gpio_request(GPIO_FN_NAF6, NULL); + gpio_request(GPIO_FN_NAF5, NULL); + gpio_request(GPIO_FN_NAF4, NULL); + gpio_request(GPIO_FN_NAF3, NULL); + gpio_request(GPIO_FN_NAF2, NULL); + gpio_request(GPIO_FN_NAF1, NULL); + gpio_request(GPIO_FN_NAF0, NULL); + gpio_request(GPIO_FN_FCDE, NULL); + gpio_request(GPIO_FN_FOE, NULL); + gpio_request(GPIO_FN_FSC, NULL); + gpio_request(GPIO_FN_FWE, NULL); + gpio_request(GPIO_FN_FRB, NULL); + + ctrl_outw(0, PORT_HIZCRC); + ctrl_outw(0xFFFF, PORT_DRVCRA); + ctrl_outw(0xFFFF, PORT_DRVCRB); + + platform_resource_setup_memory(&ceu_device, "ceu", 4 << 20); + + i2c_register_board_info(0, ap325rxa_i2c_devices, + ARRAY_SIZE(ap325rxa_i2c_devices)); + + spi_register_board_info(ap325rxa_spi_devices, + ARRAY_SIZE(ap325rxa_spi_devices)); + + return platform_add_devices(ap325rxa_devices, + ARRAY_SIZE(ap325rxa_devices)); +} +device_initcall(ap325rxa_devices_setup); + +static struct sh_machine_vector mv_ap325rxa __initmv = { + .mv_name = "AP-325RXA", +}; diff --git a/linux/arch/sh/boards/mach-migor/setup.c b/linux/arch/sh/boards/mach-migor/setup.c new file mode 100644 index 000000000..6d6395996 --- /dev/null +++ b/linux/arch/sh/boards/mach-migor/setup.c @@ -0,0 +1,579 @@ +/* + * Renesas System Solutions Asia Pte. Ltd - Migo-R + * + * Copyright (C) 2008 Magnus Damm + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/mtd/physmap.h> +#include <linux/mtd/nand.h> +#include <linux/i2c.h> +#include <linux/smc91x.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/gpio.h> +#include <linux/spi/spi.h> +#include <linux/spi/spi_gpio.h> +#include <video/sh_mobile_lcdc.h> +#include <media/sh_mobile_ceu.h> +#include <media/ov772x.h> +#include <media/tw9910.h> +#include <asm/clock.h> +#include <asm/machvec.h> +#include <asm/io.h> +#include <asm/sh_keysc.h> +#include <mach/migor.h> +#include <cpu/sh7722.h> + +/* Address IRQ Size Bus Description + * 0x00000000 64MB 16 NOR Flash (SP29PL256N) + * 0x0c000000 64MB 64 SDRAM (2xK4M563233G) + * 0x10000000 IRQ0 16 Ethernet (SMC91C111) + * 0x14000000 IRQ4 16 USB 2.0 Host Controller (M66596) + * 0x18000000 8GB 8 NAND Flash (K9K8G08U0A) + */ + +static struct smc91x_platdata smc91x_info = { + .flags = SMC91X_USE_16BIT | SMC91X_NOWAIT, +}; + +static struct resource smc91x_eth_resources[] = { + [0] = { + .name = "SMC91C111" , + .start = 0x10000300, + .end = 0x1000030f, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = 32, /* IRQ0 */ + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL, + }, +}; + +static struct platform_device smc91x_eth_device = { + .name = "smc91x", + .num_resources = ARRAY_SIZE(smc91x_eth_resources), + .resource = smc91x_eth_resources, + .dev = { + .platform_data = &smc91x_info, + }, +}; + +static struct sh_keysc_info sh_keysc_info = { + .mode = SH_KEYSC_MODE_2, /* KEYOUT0->4, KEYIN1->5 */ + .scan_timing = 3, + .delay = 5, + .keycodes = { + 0, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_ENTER, + 0, KEY_F, KEY_C, KEY_D, KEY_H, KEY_1, + 0, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, + 0, KEY_7, KEY_8, KEY_9, KEY_S, KEY_0, + 0, KEY_P, KEY_STOP, KEY_REWIND, KEY_PLAY, KEY_FASTFORWARD, + }, +}; + +static struct resource sh_keysc_resources[] = { + [0] = { + .start = 0x044b0000, + .end = 0x044b000f, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = 79, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device sh_keysc_device = { + .name = "sh_keysc", + .id = 0, /* "keysc0" clock */ + .num_resources = ARRAY_SIZE(sh_keysc_resources), + .resource = sh_keysc_resources, + .dev = { + .platform_data = &sh_keysc_info, + }, +}; + +static struct mtd_partition migor_nor_flash_partitions[] = +{ + { + .name = "uboot", + .offset = 0, + .size = (1 * 1024 * 1024), + .mask_flags = MTD_WRITEABLE, /* Read-only */ + }, + { + .name = "rootfs", + .offset = MTDPART_OFS_APPEND, + .size = (15 * 1024 * 1024), + }, + { + .name = "other", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + }, +}; + +static struct physmap_flash_data migor_nor_flash_data = { + .width = 2, + .parts = migor_nor_flash_partitions, + .nr_parts = ARRAY_SIZE(migor_nor_flash_partitions), +}; + +static struct resource migor_nor_flash_resources[] = { + [0] = { + .name = "NOR Flash", + .start = 0x00000000, + .end = 0x03ffffff, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device migor_nor_flash_device = { + .name = "physmap-flash", + .resource = migor_nor_flash_resources, + .num_resources = ARRAY_SIZE(migor_nor_flash_resources), + .dev = { + .platform_data = &migor_nor_flash_data, + }, +}; + +static struct mtd_partition migor_nand_flash_partitions[] = { + { + .name = "nanddata1", + .offset = 0x0, + .size = 512 * 1024 * 1024, + }, + { + .name = "nanddata2", + .offset = MTDPART_OFS_APPEND, + .size = 512 * 1024 * 1024, + }, +}; + +static void migor_nand_flash_cmd_ctl(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *chip = mtd->priv; + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + writeb(cmd, chip->IO_ADDR_W + 0x00400000); + else if (ctrl & NAND_ALE) + writeb(cmd, chip->IO_ADDR_W + 0x00800000); + else + writeb(cmd, chip->IO_ADDR_W); +} + +static int migor_nand_flash_ready(struct mtd_info *mtd) +{ + return gpio_get_value(GPIO_PTA1); /* NAND_RBn */ +} + +struct platform_nand_data migor_nand_flash_data = { + .chip = { + .nr_chips = 1, + .partitions = migor_nand_flash_partitions, + .nr_partitions = ARRAY_SIZE(migor_nand_flash_partitions), + .chip_delay = 20, + .part_probe_types = (const char *[]) { "cmdlinepart", NULL }, + }, + .ctrl = { + .dev_ready = migor_nand_flash_ready, + .cmd_ctrl = migor_nand_flash_cmd_ctl, + }, +}; + +static struct resource migor_nand_flash_resources[] = { + [0] = { + .name = "NAND Flash", + .start = 0x18000000, + .end = 0x18ffffff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device migor_nand_flash_device = { + .name = "gen_nand", + .resource = migor_nand_flash_resources, + .num_resources = ARRAY_SIZE(migor_nand_flash_resources), + .dev = { + .platform_data = &migor_nand_flash_data, + } +}; + +static struct sh_mobile_lcdc_info sh_mobile_lcdc_info = { +#ifdef CONFIG_SH_MIGOR_RTA_WVGA + .clock_source = LCDC_CLK_BUS, + .ch[0] = { + .chan = LCDC_CHAN_MAINLCD, + .bpp = 16, + .interface_type = RGB16, + .clock_divider = 2, + .lcd_cfg = { + .name = "LB070WV1", + .xres = 800, + .yres = 480, + .left_margin = 64, + .right_margin = 16, + .hsync_len = 120, + .upper_margin = 1, + .lower_margin = 17, + .vsync_len = 2, + .sync = 0, + }, + .lcd_size_cfg = { /* 7.0 inch */ + .width = 152, + .height = 91, + }, + } +#endif +#ifdef CONFIG_SH_MIGOR_QVGA + .clock_source = LCDC_CLK_PERIPHERAL, + .ch[0] = { + .chan = LCDC_CHAN_MAINLCD, + .bpp = 16, + .interface_type = SYS16A, + .clock_divider = 10, + .lcd_cfg = { + .name = "PH240320T", + .xres = 320, + .yres = 240, + .left_margin = 0, + .right_margin = 16, + .hsync_len = 8, + .upper_margin = 1, + .lower_margin = 17, + .vsync_len = 2, + .sync = FB_SYNC_HOR_HIGH_ACT, + }, + .lcd_size_cfg = { /* 2.4 inch */ + .width = 49, + .height = 37, + }, + .board_cfg = { + .setup_sys = migor_lcd_qvga_setup, + }, + .sys_bus_cfg = { + .ldmt2r = 0x06000a09, + .ldmt3r = 0x180e3418, + /* set 1s delay to encourage fsync() */ + .deferred_io_msec = 1000, + }, + } +#endif +}; + +static struct resource migor_lcdc_resources[] = { + [0] = { + .name = "LCDC", + .start = 0xfe940000, /* P4-only space */ + .end = 0xfe941fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = 28, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device migor_lcdc_device = { + .name = "sh_mobile_lcdc_fb", + .num_resources = ARRAY_SIZE(migor_lcdc_resources), + .resource = migor_lcdc_resources, + .dev = { + .platform_data = &sh_mobile_lcdc_info, + }, +}; + +static struct clk *camera_clk; +static DEFINE_MUTEX(camera_lock); + +static void camera_power_on(int is_tw) +{ + mutex_lock(&camera_lock); + + /* Use 10 MHz VIO_CKO instead of 24 MHz to work + * around signal quality issues on Panel Board V2.1. + */ + camera_clk = clk_get(NULL, "video_clk"); + clk_set_rate(camera_clk, 10000000); + clk_enable(camera_clk); /* start VIO_CKO */ + + /* use VIO_RST to take camera out of reset */ + mdelay(10); + if (is_tw) { + gpio_set_value(GPIO_PTT2, 0); + gpio_set_value(GPIO_PTT0, 0); + } else { + gpio_set_value(GPIO_PTT0, 1); + } + gpio_set_value(GPIO_PTT3, 0); + mdelay(10); + gpio_set_value(GPIO_PTT3, 1); + mdelay(10); /* wait to let chip come out of reset */ +} + +static void camera_power_off(void) +{ + clk_disable(camera_clk); /* stop VIO_CKO */ + clk_put(camera_clk); + + gpio_set_value(GPIO_PTT3, 0); + mutex_unlock(&camera_lock); +} + +static int ov7725_power(struct device *dev, int mode) +{ + if (mode) + camera_power_on(0); + else + camera_power_off(); + + return 0; +} + +static int tw9910_power(struct device *dev, int mode) +{ + if (mode) + camera_power_on(1); + else + camera_power_off(); + + return 0; +} + +static struct sh_mobile_ceu_info sh_mobile_ceu_info = { + .flags = SOCAM_MASTER | SOCAM_DATAWIDTH_8 | SOCAM_PCLK_SAMPLE_RISING + | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH + | SOCAM_DATA_ACTIVE_HIGH, +}; + +static struct resource migor_ceu_resources[] = { + [0] = { + .name = "CEU", + .start = 0xfe910000, + .end = 0xfe91009f, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = 52, + .flags = IORESOURCE_IRQ, + }, + [2] = { + /* place holder for contiguous memory */ + }, +}; + +static struct platform_device migor_ceu_device = { + .name = "sh_mobile_ceu", + .id = 0, /* "ceu0" clock */ + .num_resources = ARRAY_SIZE(migor_ceu_resources), + .resource = migor_ceu_resources, + .dev = { + .platform_data = &sh_mobile_ceu_info, + }, +}; + +static struct ov772x_camera_info ov7725_info = { + .buswidth = SOCAM_DATAWIDTH_8, + .link = { + .power = ov7725_power, + }, +}; + +static struct tw9910_video_info tw9910_info = { + .buswidth = SOCAM_DATAWIDTH_8, + .mpout = TW9910_MPO_FIELD, + .link = { + .power = tw9910_power, + } +}; + +struct spi_gpio_platform_data sdcard_cn9_platform_data = { + .sck = GPIO_PTD0, + .mosi = GPIO_PTD1, + .miso = GPIO_PTD2, + .num_chipselect = 1, +}; + +static struct platform_device sdcard_cn9_device = { + .name = "spi_gpio", + .dev = { + .platform_data = &sdcard_cn9_platform_data, + }, +}; + +static struct platform_device *migor_devices[] __initdata = { + &smc91x_eth_device, + &sh_keysc_device, + &migor_lcdc_device, + &migor_ceu_device, + &migor_nor_flash_device, + &migor_nand_flash_device, + &sdcard_cn9_device, +}; + +static struct i2c_board_info migor_i2c_devices[] = { + { + I2C_BOARD_INFO("rs5c372b", 0x32), + }, + { + I2C_BOARD_INFO("migor_ts", 0x51), + .irq = 38, /* IRQ6 */ + }, + { + I2C_BOARD_INFO("ov772x", 0x21), + .platform_data = &ov7725_info, + }, + { + I2C_BOARD_INFO("tw9910", 0x45), + .platform_data = &tw9910_info, + }, +}; + +static struct spi_board_info migor_spi_devices[] = { + { + .modalias = "mmc_spi", + .max_speed_hz = 5000000, + .chip_select = 0, + .controller_data = (void *) GPIO_PTD5, + }, +}; + +static int __init migor_devices_setup(void) +{ + /* Lit D11 LED */ + gpio_request(GPIO_PTJ7, NULL); + gpio_direction_output(GPIO_PTJ7, 1); + gpio_export(GPIO_PTJ7, 0); + + /* Lit D12 LED */ + gpio_request(GPIO_PTJ5, NULL); + gpio_direction_output(GPIO_PTJ5, 1); + gpio_export(GPIO_PTJ5, 0); + + /* SMC91C111 - Enable IRQ0, Setup CS4 for 16-bit fast access */ + gpio_request(GPIO_FN_IRQ0, NULL); + ctrl_outl(0x00003400, BSC_CS4BCR); + ctrl_outl(0x00110080, BSC_CS4WCR); + + /* KEYSC */ + gpio_request(GPIO_FN_KEYOUT0, NULL); + gpio_request(GPIO_FN_KEYOUT1, NULL); + gpio_request(GPIO_FN_KEYOUT2, NULL); + gpio_request(GPIO_FN_KEYOUT3, NULL); + gpio_request(GPIO_FN_KEYOUT4_IN6, NULL); + gpio_request(GPIO_FN_KEYIN1, NULL); + gpio_request(GPIO_FN_KEYIN2, NULL); + gpio_request(GPIO_FN_KEYIN3, NULL); + gpio_request(GPIO_FN_KEYIN4, NULL); + gpio_request(GPIO_FN_KEYOUT5_IN5, NULL); + + /* NAND Flash */ + gpio_request(GPIO_FN_CS6A_CE2B, NULL); + ctrl_outl((ctrl_inl(BSC_CS6ABCR) & ~0x0600) | 0x0200, BSC_CS6ABCR); + gpio_request(GPIO_PTA1, NULL); + gpio_direction_input(GPIO_PTA1); + + /* Touch Panel */ + gpio_request(GPIO_FN_IRQ6, NULL); + + /* LCD Panel */ +#ifdef CONFIG_SH_MIGOR_QVGA /* LCDC - QVGA - Enable SYS Interface signals */ + gpio_request(GPIO_FN_LCDD17, NULL); + gpio_request(GPIO_FN_LCDD16, NULL); + gpio_request(GPIO_FN_LCDD15, NULL); + gpio_request(GPIO_FN_LCDD14, NULL); + gpio_request(GPIO_FN_LCDD13, NULL); + gpio_request(GPIO_FN_LCDD12, NULL); + gpio_request(GPIO_FN_LCDD11, NULL); + gpio_request(GPIO_FN_LCDD10, NULL); + gpio_request(GPIO_FN_LCDD8, NULL); + gpio_request(GPIO_FN_LCDD7, NULL); + gpio_request(GPIO_FN_LCDD6, NULL); + gpio_request(GPIO_FN_LCDD5, NULL); + gpio_request(GPIO_FN_LCDD4, NULL); + gpio_request(GPIO_FN_LCDD3, NULL); + gpio_request(GPIO_FN_LCDD2, NULL); + gpio_request(GPIO_FN_LCDD1, NULL); + gpio_request(GPIO_FN_LCDRS, NULL); + gpio_request(GPIO_FN_LCDCS, NULL); + gpio_request(GPIO_FN_LCDRD, NULL); + gpio_request(GPIO_FN_LCDWR, NULL); + gpio_request(GPIO_PTH2, NULL); /* LCD_DON */ + gpio_direction_output(GPIO_PTH2, 1); +#endif +#ifdef CONFIG_SH_MIGOR_RTA_WVGA /* LCDC - WVGA - Enable RGB Interface signals */ + gpio_request(GPIO_FN_LCDD15, NULL); + gpio_request(GPIO_FN_LCDD14, NULL); + gpio_request(GPIO_FN_LCDD13, NULL); + gpio_request(GPIO_FN_LCDD12, NULL); + gpio_request(GPIO_FN_LCDD11, NULL); + gpio_request(GPIO_FN_LCDD10, NULL); + gpio_request(GPIO_FN_LCDD9, NULL); + gpio_request(GPIO_FN_LCDD8, NULL); + gpio_request(GPIO_FN_LCDD7, NULL); + gpio_request(GPIO_FN_LCDD6, NULL); + gpio_request(GPIO_FN_LCDD5, NULL); + gpio_request(GPIO_FN_LCDD4, NULL); + gpio_request(GPIO_FN_LCDD3, NULL); + gpio_request(GPIO_FN_LCDD2, NULL); + gpio_request(GPIO_FN_LCDD1, NULL); + gpio_request(GPIO_FN_LCDD0, NULL); + gpio_request(GPIO_FN_LCDLCLK, NULL); + gpio_request(GPIO_FN_LCDDCK, NULL); + gpio_request(GPIO_FN_LCDVEPWC, NULL); + gpio_request(GPIO_FN_LCDVCPWC, NULL); + gpio_request(GPIO_FN_LCDVSYN, NULL); + gpio_request(GPIO_FN_LCDHSYN, NULL); + gpio_request(GPIO_FN_LCDDISP, NULL); + gpio_request(GPIO_FN_LCDDON, NULL); +#endif + + /* CEU */ + gpio_request(GPIO_FN_VIO_CLK2, NULL); + gpio_request(GPIO_FN_VIO_VD2, NULL); + gpio_request(GPIO_FN_VIO_HD2, NULL); + gpio_request(GPIO_FN_VIO_FLD, NULL); + gpio_request(GPIO_FN_VIO_CKO, NULL); + gpio_request(GPIO_FN_VIO_D15, NULL); + gpio_request(GPIO_FN_VIO_D14, NULL); + gpio_request(GPIO_FN_VIO_D13, NULL); + gpio_request(GPIO_FN_VIO_D12, NULL); + gpio_request(GPIO_FN_VIO_D11, NULL); + gpio_request(GPIO_FN_VIO_D10, NULL); + gpio_request(GPIO_FN_VIO_D9, NULL); + gpio_request(GPIO_FN_VIO_D8, NULL); + + gpio_request(GPIO_PTT3, NULL); /* VIO_RST */ + gpio_direction_output(GPIO_PTT3, 0); + gpio_request(GPIO_PTT2, NULL); /* TV_IN_EN */ + gpio_direction_output(GPIO_PTT2, 1); + gpio_request(GPIO_PTT0, NULL); /* CAM_EN */ +#ifdef CONFIG_SH_MIGOR_RTA_WVGA + gpio_direction_output(GPIO_PTT0, 0); +#else + gpio_direction_output(GPIO_PTT0, 1); +#endif + ctrl_outw(ctrl_inw(PORT_MSELCRB) | 0x2000, PORT_MSELCRB); /* D15->D8 */ + + platform_resource_setup_memory(&migor_ceu_device, "ceu", 4 << 20); + + i2c_register_board_info(0, migor_i2c_devices, + ARRAY_SIZE(migor_i2c_devices)); + + spi_register_board_info(migor_spi_devices, + ARRAY_SIZE(migor_spi_devices)); + + return platform_add_devices(migor_devices, ARRAY_SIZE(migor_devices)); +} +__initcall(migor_devices_setup); diff --git a/linux/drivers/media/common/tuners/tda18271-common.c b/linux/drivers/media/common/tuners/tda18271-common.c index a8fb96698..a49facb7b 100644 --- a/linux/drivers/media/common/tuners/tda18271-common.c +++ b/linux/drivers/media/common/tuners/tda18271-common.c @@ -558,9 +558,9 @@ int tda18271_set_standby_mode(struct dvb_frontend *fe, tda_dbg("sm = %d, sm_lt = %d, sm_xt = %d\n", sm, sm_lt, sm_xt); regs[R_EP3] &= ~0xe0; /* clear sm, sm_lt, sm_xt */ - regs[R_EP3] |= sm ? (1 << 7) : 0 | - sm_lt ? (1 << 6) : 0 | - sm_xt ? (1 << 5) : 0; + regs[R_EP3] |= (sm ? (1 << 7) : 0) | + (sm_lt ? (1 << 6) : 0) | + (sm_xt ? (1 << 5) : 0); return tda18271_write_regs(fe, R_EP3, 1); } diff --git a/linux/drivers/media/dvb/b2c2/flexcop-hw-filter.c b/linux/drivers/media/dvb/b2c2/flexcop-hw-filter.c index b386cc66c..451974ba3 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-hw-filter.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-hw-filter.c @@ -192,6 +192,7 @@ int flexcop_pid_feed_control(struct flexcop_device *fc, struct dvb_demux_feed *d return 0; } +EXPORT_SYMBOL(flexcop_pid_feed_control); void flexcop_hw_filter_init(struct flexcop_device *fc) { diff --git a/linux/drivers/media/dvb/b2c2/flexcop-pci.c b/linux/drivers/media/dvb/b2c2/flexcop-pci.c index 853a3ccff..5baf9ded9 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-pci.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-pci.c @@ -13,9 +13,9 @@ static int enable_pid_filtering = 1; module_param(enable_pid_filtering, int, 0444); MODULE_PARM_DESC(enable_pid_filtering, "enable hardware pid filtering: supported values: 0 (fullts), 1"); -static int irq_chk_intv; +static int irq_chk_intv = 100; module_param(irq_chk_intv, int, 0644); -MODULE_PARM_DESC(irq_chk_intv, "set the interval for IRQ watchdog (currently just debugging)."); +MODULE_PARM_DESC(irq_chk_intv, "set the interval for IRQ streaming watchdog."); #ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG #define dprintk(level,args...) \ @@ -34,7 +34,9 @@ MODULE_PARM_DESC(irq_chk_intv, "set the interval for IRQ watchdog (currently jus static int debug; module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "set debug level (1=info,2=regs,4=TS,8=irqdma (|-able))." DEBSTATUS); +MODULE_PARM_DESC(debug, + "set debug level (1=info,2=regs,4=TS,8=irqdma,16=check (|-able))." + DEBSTATUS); #define DRIVER_VERSION "0.1" #define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV PCI Driver" @@ -58,6 +60,8 @@ struct flexcop_pci { int active_dma1_addr; /* 0 = addr0 of dma1; 1 = addr1 of dma1 */ u32 last_dma1_cur_pos; /* position of the pointer last time the timer/packet irq occured */ int count; + int count_prev; + int stream_problem; spinlock_t irq_lock; @@ -115,18 +119,44 @@ static void flexcop_pci_irq_check_work(struct work_struct *work) #endif struct flexcop_device *fc = fc_pci->fc_dev; - flexcop_ibi_value v = fc->read_ibi_reg(fc,sram_dest_reg_714); - - flexcop_dump_reg(fc_pci->fc_dev,dma1_000,4); + if (fc->feedcount) { +#if 0 + flexcop_ibi_value v = fc->read_ibi_reg(fc, sram_dest_reg_714); + deb_chk("net_ovflow_error: %d, media_ovflow_error: %d," + "cai_ovflow_error: %d, cao_ovflow_error: %d, " + "sram_dma: %d, maximumfill: %d\n", + v.sram_dest_reg_714.net_ovflow_error, + v.sram_dest_reg_714.media_ovflow_error, + v.sram_dest_reg_714.cai_ovflow_error, + v.sram_dest_reg_714.cao_ovflow_error, + v.sram_dest_reg_714.ctrl_sramdma, + v.sram_dest_reg_714.ctrl_maximumfill); +#endif - if (v.sram_dest_reg_714.net_ovflow_error) - deb_chk("sram net_ovflow_error\n"); - if (v.sram_dest_reg_714.media_ovflow_error) - deb_chk("sram media_ovflow_error\n"); - if (v.sram_dest_reg_714.cai_ovflow_error) - deb_chk("sram cai_ovflow_error\n"); - if (v.sram_dest_reg_714.cai_ovflow_error) - deb_chk("sram cai_ovflow_error\n"); + if (fc_pci->count == fc_pci->count_prev) { + deb_chk("no IRQ since the last check\n"); + if (fc_pci->stream_problem++ == 3) { + struct dvb_demux_feed *feed; + + spin_lock_irq(&fc->demux.lock); + list_for_each_entry(feed, &fc->demux.feed_list, + list_head) { + flexcop_pid_feed_control(fc, feed, 0); + } + + list_for_each_entry(feed, &fc->demux.feed_list, + list_head) { + flexcop_pid_feed_control(fc, feed, 1); + } + spin_unlock_irq(&fc->demux.lock); + + fc_pci->stream_problem = 0; + } + } else { + fc_pci->stream_problem = 0; + fc_pci->count_prev = fc_pci->count; + } + } schedule_delayed_work(&fc_pci->irq_check_work, msecs_to_jiffies(irq_chk_intv < 100 ? 100 : irq_chk_intv)); @@ -232,16 +262,12 @@ static int flexcop_pci_stream_control(struct flexcop_device *fc, int onoff) flexcop_dma_control_timer_irq(fc,FC_DMA_1,1); deb_irq("IRQ enabled\n"); + fc_pci->count_prev = fc_pci->count; + // fc_pci->active_dma1_addr = 0; // flexcop_dma_control_size_irq(fc,FC_DMA_1,1); - if (irq_chk_intv > 0) - schedule_delayed_work(&fc_pci->irq_check_work, - msecs_to_jiffies(irq_chk_intv < 100 ? 100 : irq_chk_intv)); } else { - if (irq_chk_intv > 0) - cancel_delayed_work(&fc_pci->irq_check_work); - flexcop_dma_control_timer_irq(fc,FC_DMA_1,0); deb_irq("IRQ disabled\n"); @@ -315,8 +341,6 @@ static int flexcop_pci_init(struct flexcop_pci *fc_pci) IRQF_SHARED, DRIVER_NAME, fc_pci)) != 0) goto err_pci_iounmap; - - fc_pci->init_state |= FC_PCI_INIT; return ret; @@ -395,6 +419,10 @@ static int flexcop_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e INIT_DELAYED_WORK(&fc_pci->irq_check_work, flexcop_pci_irq_check_work); #endif + if (irq_chk_intv > 0) + schedule_delayed_work(&fc_pci->irq_check_work, + msecs_to_jiffies(irq_chk_intv < 100 ? 100 : irq_chk_intv)); + return ret; err_fc_exit: @@ -413,6 +441,9 @@ static void flexcop_pci_remove(struct pci_dev *pdev) { struct flexcop_pci *fc_pci = pci_get_drvdata(pdev); + if (irq_chk_intv > 0) + cancel_delayed_work(&fc_pci->irq_check_work); + flexcop_pci_dma_exit(fc_pci); flexcop_device_exit(fc_pci->fc_dev); flexcop_pci_exit(fc_pci); diff --git a/linux/drivers/media/dvb/b2c2/flexcop.c b/linux/drivers/media/dvb/b2c2/flexcop.c index 676413a91..91068952b 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop.c +++ b/linux/drivers/media/dvb/b2c2/flexcop.c @@ -212,8 +212,7 @@ void flexcop_reset_block_300(struct flexcop_device *fc) v210.sw_reset_210.Block_reset_enable = 0xb2; fc->write_ibi_reg(fc,sw_reset_210,v210); - msleep(1); - + udelay(1000); fc->write_ibi_reg(fc,ctrl_208,v208_save); } diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.c b/linux/drivers/media/dvb/dvb-usb/af9015.c index 88a365ad9..ca7609583 100644 --- a/linux/drivers/media/dvb/dvb-usb/af9015.c +++ b/linux/drivers/media/dvb/dvb-usb/af9015.c @@ -1507,9 +1507,6 @@ static void af9015_usb_device_exit(struct usb_interface *intf) /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver af9015_usb_driver = { -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 15) - .owner = THIS_MODULE, -#endif .name = "dvb_usb_af9015", .probe = af9015_usb_probe, .disconnect = af9015_usb_device_exit, diff --git a/linux/drivers/media/dvb/frontends/cx24113.c b/linux/drivers/media/dvb/frontends/cx24113.c index f6e7b0380..e4fd533a4 100644 --- a/linux/drivers/media/dvb/frontends/cx24113.c +++ b/linux/drivers/media/dvb/frontends/cx24113.c @@ -559,7 +559,7 @@ struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe, kzalloc(sizeof(struct cx24113_state), GFP_KERNEL); int rc; if (state == NULL) { - err("Unable to kmalloc\n"); + err("Unable to kzalloc\n"); goto error; } diff --git a/linux/drivers/media/dvb/frontends/cx24116.c b/linux/drivers/media/dvb/frontends/cx24116.c index 8301a9865..b5ff0b6a8 100644 --- a/linux/drivers/media/dvb/frontends/cx24116.c +++ b/linux/drivers/media/dvb/frontends/cx24116.c @@ -1112,13 +1112,10 @@ struct dvb_frontend *cx24116_attach(const struct cx24116_config *config, dprintk("%s\n", __func__); /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct cx24116_state), GFP_KERNEL); + state = kzalloc(sizeof(struct cx24116_state), GFP_KERNEL); if (state == NULL) goto error1; - /* setup the state */ - memset(state, 0, sizeof(struct cx24116_state)); - state->config = config; state->i2c = i2c; diff --git a/linux/drivers/media/dvb/frontends/cx24123.c b/linux/drivers/media/dvb/frontends/cx24123.c index c9cfc8393..f431f0c56 100644 --- a/linux/drivers/media/dvb/frontends/cx24123.c +++ b/linux/drivers/media/dvb/frontends/cx24123.c @@ -1084,13 +1084,13 @@ static struct dvb_frontend_ops cx24123_ops; struct dvb_frontend *cx24123_attach(const struct cx24123_config *config, struct i2c_adapter *i2c) { + /* allocate memory for the internal state */ struct cx24123_state *state = kzalloc(sizeof(struct cx24123_state), GFP_KERNEL); dprintk("\n"); - /* allocate memory for the internal state */ if (state == NULL) { - err("Unable to kmalloc\n"); + err("Unable to kzalloc\n"); goto error; } diff --git a/linux/drivers/media/dvb/frontends/lgdt3304.c b/linux/drivers/media/dvb/frontends/lgdt3304.c index d56a799d3..3a4a18970 100644 --- a/linux/drivers/media/dvb/frontends/lgdt3304.c +++ b/linux/drivers/media/dvb/frontends/lgdt3304.c @@ -383,7 +383,6 @@ struct dvb_frontend* lgdt3304_attach(const struct lgdt3304_config *config, struct lgdt3304_state *state; state = kzalloc(sizeof(struct lgdt3304_state), GFP_KERNEL); - memset(state, 0x0, sizeof(struct lgdt3304_state)); state->addr = config->i2c_address; state->i2c = i2c; diff --git a/linux/drivers/media/dvb/frontends/s921_module.c b/linux/drivers/media/dvb/frontends/s921_module.c index 8ae0676a5..4a673c184 100644 --- a/linux/drivers/media/dvb/frontends/s921_module.c +++ b/linux/drivers/media/dvb/frontends/s921_module.c @@ -233,7 +233,6 @@ struct dvb_frontend* s921_attach(const struct s921_config *config, struct s921_state *state; state = kzalloc(sizeof(struct s921_state), GFP_KERNEL); - memset(state, 0x0, sizeof(struct s921_state)); state->addr = config->i2c_address; state->i2c = i2c; diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index e671beb59..bb7df8c18 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -759,6 +759,13 @@ config SOC_CAMERA_OV772X help This is a ov772x camera driver +config VIDEO_MX3 + tristate "i.MX3x Camera Sensor Interface driver" + depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA + select VIDEOBUF_DMA_CONTIG + ---help--- + This is a v4l2 driver for the i.MX3x Camera Sensor Interface + config VIDEO_PXA27x tristate "PXA27x Quick Capture Interface driver" depends on VIDEO_DEV && PXA27x && SOC_CAMERA diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index 263c2264d..307490ebc 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -132,10 +132,11 @@ obj-$(CONFIG_VIDEO_CX18) += cx18/ obj-$(CONFIG_VIDEO_VIVI) += vivi.o obj-$(CONFIG_VIDEO_CX23885) += cx23885/ -obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o +obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o +obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o -obj-$(CONFIG_SOC_CAMERA) += soc_camera.o +obj-$(CONFIG_SOC_CAMERA) += soc_camera.o obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o diff --git a/linux/drivers/media/video/cx18/cx18-av-core.c b/linux/drivers/media/video/cx18/cx18-av-core.c index a3bd2c95f..fc576cf1a 100644 --- a/linux/drivers/media/video/cx18/cx18-av-core.c +++ b/linux/drivers/media/video/cx18/cx18-av-core.c @@ -788,10 +788,12 @@ int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg) switch (qc->id) { case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); case V4L2_CID_CONTRAST: case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); case V4L2_CID_HUE: - return v4l2_ctrl_query_fill_std(qc); + return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); default: break; } @@ -801,10 +803,11 @@ int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg) return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, state->default_volume); case V4L2_CID_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill_std(qc); + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); default: return -EINVAL; } diff --git a/linux/drivers/media/video/cx2341x.c b/linux/drivers/media/video/cx2341x.c index 01c4d2d02..2cf0c5fee 100644 --- a/linux/drivers/media/video/cx2341x.c +++ b/linux/drivers/media/video/cx2341x.c @@ -501,6 +501,29 @@ int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, int err; switch (qctrl->id) { + case V4L2_CID_MPEG_STREAM_TYPE: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_STREAM_TYPE_MPEG2_PS, + V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, 1, + V4L2_MPEG_STREAM_TYPE_MPEG2_PS); + + case V4L2_CID_MPEG_STREAM_VBI_FMT: + if (params->capabilities & CX2341X_CAP_HAS_SLICED_VBI) + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_STREAM_VBI_FMT_NONE, + V4L2_MPEG_STREAM_VBI_FMT_IVTV, 1, + V4L2_MPEG_STREAM_VBI_FMT_NONE); + return cx2341x_ctrl_query_fill(qctrl, + V4L2_MPEG_STREAM_VBI_FMT_NONE, + V4L2_MPEG_STREAM_VBI_FMT_NONE, 1, + default_params.stream_vbi_fmt); + + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 1, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); + case V4L2_CID_MPEG_AUDIO_ENCODING: if (params->capabilities & CX2341X_CAP_HAS_AC3) { /* @@ -532,9 +555,36 @@ int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; return 0; - case V4L2_CID_MPEG_AUDIO_L1_BITRATE: - case V4L2_CID_MPEG_AUDIO_L3_BITRATE: - return -EINVAL; + case V4L2_CID_MPEG_AUDIO_MODE: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_MODE_STEREO, + V4L2_MPEG_AUDIO_MODE_MONO, 1, + V4L2_MPEG_AUDIO_MODE_STEREO); + + case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 1, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4); + if (err == 0 && + params->audio_mode != V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return err; + + case V4L2_CID_MPEG_AUDIO_EMPHASIS: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_EMPHASIS_NONE, + V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 1, + V4L2_MPEG_AUDIO_EMPHASIS_NONE); + + case V4L2_CID_MPEG_AUDIO_CRC: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_CRC_NONE, + V4L2_MPEG_AUDIO_CRC_CRC16, 1, + V4L2_MPEG_AUDIO_CRC_NONE); + + case V4L2_CID_MPEG_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: err = v4l2_ctrl_query_fill(qctrl, @@ -551,13 +601,6 @@ int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; return 0; - case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: - err = v4l2_ctrl_query_fill_std(qctrl); - if (err == 0 && - params->audio_mode != V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) - qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - return err; - case V4L2_CID_MPEG_VIDEO_ENCODING: /* this setting is read-only for the cx2341x since the V4L2_CID_MPEG_STREAM_TYPE really determines the @@ -570,32 +613,51 @@ int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; return err; + case V4L2_CID_MPEG_VIDEO_ASPECT: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_ASPECT_1x1, + V4L2_MPEG_VIDEO_ASPECT_221x100, 1, + V4L2_MPEG_VIDEO_ASPECT_4x3); + + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + return v4l2_ctrl_query_fill(qctrl, 0, 33, 1, 2); + + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + return v4l2_ctrl_query_fill(qctrl, 1, 34, 1, + params->is_50hz ? 12 : 15); + + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 1); + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - err = v4l2_ctrl_query_fill_std(qctrl); + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); if (err == 0 && params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; return err; + case V4L2_CID_MPEG_VIDEO_BITRATE: + return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000); + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - err = v4l2_ctrl_query_fill_std(qctrl); + err = v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000); if (err == 0 && params->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; return err; - case V4L2_CID_MPEG_STREAM_VBI_FMT: - if (params->capabilities & CX2341X_CAP_HAS_SLICED_VBI) - return v4l2_ctrl_query_fill_std(qctrl); - return cx2341x_ctrl_query_fill(qctrl, - V4L2_MPEG_STREAM_VBI_FMT_NONE, - V4L2_MPEG_STREAM_VBI_FMT_NONE, 1, - default_params.stream_vbi_fmt); + case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: + return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0); - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - return v4l2_ctrl_query_fill(qctrl, 1, 34, 1, - params->is_50hz ? 12 : 15); + case V4L2_CID_MPEG_VIDEO_MUTE: + return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); + + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: /* Init YUV (really YCbCr) to black */ + return v4l2_ctrl_query_fill(qctrl, 0, 0xffffff, 1, 0x008080); /* CX23415/6 specific */ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: @@ -697,7 +759,7 @@ int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, default_params.stream_insert_nav_packets); default: - return v4l2_ctrl_query_fill_std(qctrl); + return -EINVAL; } } diff --git a/linux/drivers/media/video/cx25840/cx25840-core.c b/linux/drivers/media/video/cx25840/cx25840-core.c index d70dcd028..921cc96fc 100644 --- a/linux/drivers/media/video/cx25840/cx25840-core.c +++ b/linux/drivers/media/video/cx25840/cx25840-core.c @@ -1224,10 +1224,12 @@ static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) switch (qc->id) { case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); case V4L2_CID_CONTRAST: case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); case V4L2_CID_HUE: - return v4l2_ctrl_query_fill_std(qc); + return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); default: break; } @@ -1239,10 +1241,11 @@ static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, state->default_volume); case V4L2_CID_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill_std(qc); + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); default: return -EINVAL; } diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c index bfa3986f5..eb49a1364 100644 --- a/linux/drivers/media/video/em28xx/em28xx-audio.c +++ b/linux/drivers/media/video/em28xx/em28xx-audio.c @@ -560,6 +560,8 @@ static int em28xx_audio_init(struct em28xx *dev) pcm->info_flags = 0; pcm->private_data = dev; strcpy(pcm->name, "Empia 28xx Capture"); + + snd_card_set_dev(card, &dev->udev->dev); strcpy(card->driver, "Empia Em28xx Audio"); strcpy(card->shortname, "Em28xx Audio"); strcpy(card->longname, "Empia Em28xx Audio"); diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 5af4c417a..bab6340da 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -1309,6 +1309,7 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_TVP5150, + .adecoder = EM28XX_TVAUDIO, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -1986,6 +1987,8 @@ void em28xx_card_setup(struct em28xx *dev) request_module("tvp5150"); if (dev->board.tuner_type != TUNER_ABSENT) request_module("tuner"); + if (dev->board.adecoder == EM28XX_TVAUDIO) + request_module("tvaudio"); #endif em28xx_config_tuner(dev); diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 4aebda6c7..e21f29b99 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -358,6 +358,11 @@ enum em28xx_decoder { EM28XX_SAA711X, }; +enum em28xx_adecoder { + EM28XX_NOADECODER = 0, + EM28XX_TVAUDIO, +}; + struct em28xx_board { char *name; int vchannels; @@ -383,6 +388,7 @@ struct em28xx_board { unsigned char xclk, i2c_speed; enum em28xx_decoder decoder; + enum em28xx_adecoder adecoder; struct em28xx_input input[MAX_EM28XX_INPUT]; struct em28xx_input radio; diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index 5cdb643b8..36d6f50be 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -2199,9 +2199,9 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x21)}, #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0c45, 0x613b), BSI(SN9C120, OV7660, 0x21)}, +#endif {USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)}, /* {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x??)}, */ -#endif {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, SP80708, 0x18)}, {} }; diff --git a/linux/drivers/media/video/gspca/zc3xx.c b/linux/drivers/media/video/gspca/zc3xx.c index b93573f66..ad385b9a0 100644 --- a/linux/drivers/media/video/gspca/zc3xx.c +++ b/linux/drivers/media/video/gspca/zc3xx.c @@ -7016,7 +7016,7 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) static int zcxx_probeSensor(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int sensor, sensor2; + int sensor; switch (sd->sensor) { case SENSOR_MC501CB: @@ -7031,16 +7031,9 @@ static int zcxx_probeSensor(struct gspca_dev *gspca_dev) break; } sensor = vga_2wr_probe(gspca_dev); - if (sensor >= 0) { - if (sensor < 0x7600) - return sensor; - /* next probe is needed for OmniVision ? */ - } - sensor2 = vga_3wr_probe(gspca_dev); - if (sensor2 >= 0 - && sensor >= 0) + if (sensor >= 0) return sensor; - return sensor2; + return vga_3wr_probe(gspca_dev); } /* this function is called at probe time */ @@ -7177,6 +7170,10 @@ static int sd_config(struct gspca_dev *gspca_dev, PDEBUG(D_PROBE, "Find Sensor OV7620"); sd->sensor = SENSOR_OV7620; break; + case 0x7631: + PDEBUG(D_PROBE, "Find Sensor OV7630C"); + sd->sensor = SENSOR_OV7630C; + break; case 0x7648: PDEBUG(D_PROBE, "Find Sensor OV7648"); sd->sensor = SENSOR_OV7620; /* same sensor (?) */ diff --git a/linux/drivers/media/video/msp3400-driver.c b/linux/drivers/media/video/msp3400-driver.c index 053f168bf..61b54317f 100644 --- a/linux/drivers/media/video/msp3400-driver.c +++ b/linux/drivers/media/video/msp3400-driver.c @@ -718,22 +718,24 @@ static int msp_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) struct msp_state *state = to_state(sd); switch (qc->id) { - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill_std(qc); - default: - break; + case V4L2_CID_AUDIO_VOLUME: + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); + case V4L2_CID_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); + default: + break; } if (!state->has_sound_processing) return -EINVAL; switch (qc->id) { - case V4L2_CID_AUDIO_LOUDNESS: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill_std(qc); - default: - return -EINVAL; + case V4L2_CID_AUDIO_LOUDNESS: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); + default: + return -EINVAL; } return 0; } diff --git a/linux/drivers/media/video/mt9m001.c b/linux/drivers/media/video/mt9m001.c index 3d8e6ed8d..c2e52100a 100644 --- a/linux/drivers/media/video/mt9m001.c +++ b/linux/drivers/media/video/mt9m001.c @@ -276,7 +276,7 @@ static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd) /* MT9M001 has all capture_format parameters fixed */ unsigned long flags = SOCAM_DATAWIDTH_10 | SOCAM_PCLK_SAMPLE_RISING | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | - SOCAM_MASTER; + SOCAM_DATA_ACTIVE_HIGH | SOCAM_MASTER; if (bus_switch_possible(mt9m001)) flags |= SOCAM_DATAWIDTH_8; diff --git a/linux/drivers/media/video/mt9m111.c b/linux/drivers/media/video/mt9m111.c index cdf3f38e2..3ae675a42 100644 --- a/linux/drivers/media/video/mt9m111.c +++ b/linux/drivers/media/video/mt9m111.c @@ -393,6 +393,8 @@ static int mt9m111_disable(struct soc_camera_device *icd) static int mt9m111_reset(struct soc_camera_device *icd) { + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct soc_camera_link *icl = mt9m111->client->dev.platform_data; int ret; ret = reg_set(RESET, MT9M111_RESET_RESET_MODE); @@ -401,6 +403,10 @@ static int mt9m111_reset(struct soc_camera_device *icd) if (!ret) ret = reg_clear(RESET, MT9M111_RESET_RESET_MODE | MT9M111_RESET_RESET_SOC); + + if (icl->reset) + icl->reset(&mt9m111->client->dev); + return ret; } @@ -420,7 +426,7 @@ static unsigned long mt9m111_query_bus_param(struct soc_camera_device *icd) struct soc_camera_link *icl = mt9m111->client->dev.platform_data; unsigned long flags = SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | - SOCAM_DATAWIDTH_8; + SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8; return soc_camera_apply_sensor_flags(icl, flags); } diff --git a/linux/drivers/media/video/mt9t031.c b/linux/drivers/media/video/mt9t031.c index dd5bd9dd0..aa0e8ec34 100644 --- a/linux/drivers/media/video/mt9t031.c +++ b/linux/drivers/media/video/mt9t031.c @@ -150,7 +150,7 @@ static int mt9t031_init(struct soc_camera_device *icd) if (ret >= 0) ret = reg_write(icd, MT9T031_RESET, 0); if (ret >= 0) - ret = reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3); + ret = reg_clear(icd, MT9T031_OUTPUT_CONTROL, 2); return ret >= 0 ? 0 : -EIO; } @@ -158,14 +158,14 @@ static int mt9t031_init(struct soc_camera_device *icd) static int mt9t031_release(struct soc_camera_device *icd) { /* Disable the chip */ - reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3); + reg_clear(icd, MT9T031_OUTPUT_CONTROL, 2); return 0; } static int mt9t031_start_capture(struct soc_camera_device *icd) { /* Switch to master "normal" mode */ - if (reg_set(icd, MT9T031_OUTPUT_CONTROL, 3) < 0) + if (reg_set(icd, MT9T031_OUTPUT_CONTROL, 2) < 0) return -EIO; return 0; } @@ -173,7 +173,7 @@ static int mt9t031_start_capture(struct soc_camera_device *icd) static int mt9t031_stop_capture(struct soc_camera_device *icd) { /* Stop sensor readout */ - if (reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3) < 0) + if (reg_clear(icd, MT9T031_OUTPUT_CONTROL, 2) < 0) return -EIO; return 0; } @@ -201,6 +201,18 @@ static unsigned long mt9t031_query_bus_param(struct soc_camera_device *icd) return soc_camera_apply_sensor_flags(icl, MT9T031_BUS_PARAM); } +/* Round up minima and round down maxima */ +static void recalculate_limits(struct soc_camera_device *icd, + u16 xskip, u16 yskip) +{ + icd->x_min = (MT9T031_COLUMN_SKIP + xskip - 1) / xskip; + icd->y_min = (MT9T031_ROW_SKIP + yskip - 1) / yskip; + icd->width_min = (MT9T031_MIN_WIDTH + xskip - 1) / xskip; + icd->height_min = (MT9T031_MIN_HEIGHT + yskip - 1) / yskip; + icd->width_max = MT9T031_MAX_WIDTH / xskip; + icd->height_max = MT9T031_MAX_HEIGHT / yskip; +} + static int mt9t031_set_fmt(struct soc_camera_device *icd, __u32 pixfmt, struct v4l2_rect *rect) { @@ -208,54 +220,70 @@ static int mt9t031_set_fmt(struct soc_camera_device *icd, int ret; const u16 hblank = MT9T031_HORIZONTAL_BLANK, vblank = MT9T031_VERTICAL_BLANK; - u16 xbin, xskip = mt9t031->xskip, ybin, yskip = mt9t031->yskip, - width = rect->width * xskip, height = rect->height * yskip; + u16 xbin, xskip, ybin, yskip, width, height, left, top; if (pixfmt) { - /* S_FMT - use binning and skipping for scaling, recalculate */ + /* + * try_fmt has put rectangle within limits. + * S_FMT - use binning and skipping for scaling, recalculate + * limits, used for cropping + */ /* Is this more optimal than just a division? */ for (xskip = 8; xskip > 1; xskip--) - if (rect->width * xskip <= icd->width_max) + if (rect->width * xskip <= MT9T031_MAX_WIDTH) break; for (yskip = 8; yskip > 1; yskip--) - if (rect->height * yskip <= icd->height_max) + if (rect->height * yskip <= MT9T031_MAX_HEIGHT) break; - width = rect->width * xskip; - height = rect->height * yskip; - - dev_dbg(&icd->dev, "xskip %u, width %u, yskip %u, height %u\n", - xskip, width, yskip, height); + recalculate_limits(icd, xskip, yskip); + } else { + /* CROP - no change in scaling, or in limits */ + xskip = mt9t031->xskip; + yskip = mt9t031->yskip; } + /* Make sure we don't exceed sensor limits */ + if (rect->left + rect->width > icd->width_max) + rect->left = (icd->width_max - rect->width) / 2 + icd->x_min; + + if (rect->top + rect->height > icd->height_max) + rect->top = (icd->height_max - rect->height) / 2 + icd->y_min; + + width = rect->width * xskip; + height = rect->height * yskip; + left = rect->left * xskip; + top = rect->top * yskip; + xbin = min(xskip, (u16)3); ybin = min(yskip, (u16)3); - /* Make sure we don't exceed frame limits */ - if (rect->left + width > icd->width_max) - rect->left = (icd->width_max - width) / 2; + dev_dbg(&icd->dev, "xskip %u, width %u/%u, yskip %u, height %u/%u\n", + xskip, width, rect->width, yskip, height, rect->height); - if (rect->top + height > icd->height_max) - rect->top = (icd->height_max - height) / 2; - - /* Could just do roundup(rect->left, [xy]bin); but this is cheaper */ + /* Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper */ switch (xbin) { case 2: - rect->left = (rect->left + 1) & ~1; + left = (left + 3) & ~3; break; case 3: - rect->left = roundup(rect->left, 3); + left = roundup(left, 6); } switch (ybin) { case 2: - rect->top = (rect->top + 1) & ~1; + top = (top + 3) & ~3; break; case 3: - rect->top = roundup(rect->top, 3); + top = roundup(top, 6); } + /* Disable register update, reconfigure atomically */ + ret = reg_set(icd, MT9T031_OUTPUT_CONTROL, 1); + if (ret < 0) + return ret; + /* Blanking and start values - default... */ ret = reg_write(icd, MT9T031_HORIZONTAL_BLANKING, hblank); if (ret >= 0) @@ -270,14 +298,14 @@ static int mt9t031_set_fmt(struct soc_camera_device *icd, ret = reg_write(icd, MT9T031_ROW_ADDRESS_MODE, ((ybin - 1) << 4) | (yskip - 1)); } - dev_dbg(&icd->dev, "new left %u, top %u\n", rect->left, rect->top); + dev_dbg(&icd->dev, "new physical left %u, top %u\n", left, top); /* The caller provides a supported format, as guaranteed by * icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap() */ if (ret >= 0) - ret = reg_write(icd, MT9T031_COLUMN_START, rect->left); + ret = reg_write(icd, MT9T031_COLUMN_START, left); if (ret >= 0) - ret = reg_write(icd, MT9T031_ROW_START, rect->top); + ret = reg_write(icd, MT9T031_ROW_START, top); if (ret >= 0) ret = reg_write(icd, MT9T031_WINDOW_WIDTH, width - 1); if (ret >= 0) @@ -302,6 +330,9 @@ static int mt9t031_set_fmt(struct soc_camera_device *icd, mt9t031->yskip = yskip; } + /* Re-enable register update, commit all changes */ + reg_clear(icd, MT9T031_OUTPUT_CONTROL, 1); + return ret < 0 ? ret : 0; } @@ -310,14 +341,14 @@ static int mt9t031_try_fmt(struct soc_camera_device *icd, { struct v4l2_pix_format *pix = &f->fmt.pix; - if (pix->height < icd->height_min) - pix->height = icd->height_min; - if (pix->height > icd->height_max) - pix->height = icd->height_max; - if (pix->width < icd->width_min) - pix->width = icd->width_min; - if (pix->width > icd->width_max) - pix->width = icd->width_max; + if (pix->height < MT9T031_MIN_HEIGHT) + pix->height = MT9T031_MIN_HEIGHT; + if (pix->height > MT9T031_MAX_HEIGHT) + pix->height = MT9T031_MAX_HEIGHT; + if (pix->width < MT9T031_MIN_WIDTH) + pix->width = MT9T031_MIN_WIDTH; + if (pix->width > MT9T031_MAX_WIDTH) + pix->width = MT9T031_MAX_WIDTH; pix->width &= ~0x01; /* has to be even */ pix->height &= ~0x01; /* has to be even */ @@ -390,6 +421,14 @@ static const struct v4l2_queryctrl mt9t031_controls[] = { .step = 1, .default_value = 0, }, { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Horizontally", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { .id = V4L2_CID_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Gain", @@ -513,21 +552,23 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro if (data < 0) return -EIO; } else { - /* Pack it into 1.125..15 variable step, register values 9..67 */ + /* Pack it into 1.125..128 variable step, register values 9..0x7860 */ /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ unsigned long range = qctrl->maximum - qctrl->default_value - 1; + /* calculated gain: map 65..127 to 9..1024 step 0.125 */ unsigned long gain = ((ctrl->value - qctrl->default_value - 1) * - 111 + range / 2) / range + 9; + 1015 + range / 2) / range + 9; - if (gain <= 32) + if (gain <= 32) /* calculated gain 9..32 -> 9..32 */ data = gain; - else if (gain <= 64) + else if (gain <= 64) /* calculated gain 33..64 -> 0x51..0x60 */ data = ((gain - 32) * 16 + 16) / 32 + 80; else - data = ((gain - 64) * 7 + 28) / 56 + 96; + /* calculated gain 65..1024 -> (1..120) << 8 + 0x60 */ + data = (((gain - 64 + 7) * 32) & 0xff00) | 0x60; - dev_dbg(&icd->dev, "Setting gain from %d to %d\n", - reg_read(icd, MT9T031_GLOBAL_GAIN), data); + dev_dbg(&icd->dev, "Setting gain from 0x%x to 0x%x\n", + reg_read(icd, MT9T031_GLOBAL_GAIN), data); data = reg_write(icd, MT9T031_GLOBAL_GAIN, data); if (data < 0) return -EIO; diff --git a/linux/drivers/media/video/mt9v022.c b/linux/drivers/media/video/mt9v022.c index 9601884ca..59efb8ff0 100644 --- a/linux/drivers/media/video/mt9v022.c +++ b/linux/drivers/media/video/mt9v022.c @@ -336,7 +336,7 @@ static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd) return SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW | - SOCAM_MASTER | SOCAM_SLAVE | + SOCAM_DATA_ACTIVE_HIGH | SOCAM_MASTER | SOCAM_SLAVE | width_flag; } diff --git a/linux/drivers/media/video/mx3_camera.c b/linux/drivers/media/video/mx3_camera.c new file mode 100644 index 000000000..f525dc48f --- /dev/null +++ b/linux/drivers/media/video/mx3_camera.c @@ -0,0 +1,1183 @@ +/* + * V4L2 Driver for i.MX3x camera host + * + * Copyright (C) 2008 + * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.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/init.h> +#include <linux/module.h> +#include <linux/version.h> +#include <linux/videodev2.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/vmalloc.h> +#include <linux/interrupt.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-dev.h> +#include <media/videobuf-dma-contig.h> +#include <media/soc_camera.h> + +#include <mach/ipu.h> +#include <mach/mx3_camera.h> + +#define MX3_CAM_DRV_NAME "mx3-camera" + +/* CMOS Sensor Interface Registers */ +#define CSI_REG_START 0x60 + +#define CSI_SENS_CONF (0x60 - CSI_REG_START) +#define CSI_SENS_FRM_SIZE (0x64 - CSI_REG_START) +#define CSI_ACT_FRM_SIZE (0x68 - CSI_REG_START) +#define CSI_OUT_FRM_CTRL (0x6C - CSI_REG_START) +#define CSI_TST_CTRL (0x70 - CSI_REG_START) +#define CSI_CCIR_CODE_1 (0x74 - CSI_REG_START) +#define CSI_CCIR_CODE_2 (0x78 - CSI_REG_START) +#define CSI_CCIR_CODE_3 (0x7C - CSI_REG_START) +#define CSI_FLASH_STROBE_1 (0x80 - CSI_REG_START) +#define CSI_FLASH_STROBE_2 (0x84 - CSI_REG_START) + +#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0 +#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1 +#define CSI_SENS_CONF_DATA_POL_SHIFT 2 +#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3 +#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4 +#define CSI_SENS_CONF_SENS_CLKSRC_SHIFT 7 +#define CSI_SENS_CONF_DATA_FMT_SHIFT 8 +#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 10 +#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15 +#define CSI_SENS_CONF_DIVRATIO_SHIFT 16 + +#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 (0UL << CSI_SENS_CONF_DATA_FMT_SHIFT) +#define CSI_SENS_CONF_DATA_FMT_YUV422 (2UL << CSI_SENS_CONF_DATA_FMT_SHIFT) +#define CSI_SENS_CONF_DATA_FMT_BAYER (3UL << CSI_SENS_CONF_DATA_FMT_SHIFT) + +#define MAX_VIDEO_MEM 16 + +struct mx3_camera_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + const struct soc_camera_data_format *fmt; + + /* One descriptot per scatterlist (per frame) */ + struct dma_async_tx_descriptor *txd; + + /* We have to "build" a scatterlist ourselves - one element per frame */ + struct scatterlist sg; +}; + +/** + * struct mx3_camera_dev - i.MX3x camera (CSI) object + * @dev: camera device, to which the coherent buffer is attached + * @icd: currently attached camera sensor + * @clk: pointer to clock + * @base: remapped register base address + * @pdata: platform data + * @platform_flags: platform flags + * @mclk: master clock frequency in Hz + * @capture: list of capture videobuffers + * @lock: protects video buffer lists + * @active: active video buffer + * @idmac_channel: array of pointers to IPU DMAC DMA channels + * @soc_host: embedded soc_host object + */ +struct mx3_camera_dev { + struct device *dev; + /* + * i.MX3x is only supposed to handle one camera on its Camera Sensor + * Interface. If anyone ever builds hardware to enable more than one + * camera _simultaneously_, they will have to modify this driver too + */ + struct soc_camera_device *icd; + struct clk *clk; + + void __iomem *base; + + struct mx3_camera_pdata *pdata; + + unsigned long platform_flags; + unsigned long mclk; + + struct list_head capture; + spinlock_t lock; /* Protects video buffer lists */ + struct mx3_camera_buffer *active; + + /* IDMAC / dmaengine interface */ + struct idmac_channel *idmac_channel[1]; /* We need one channel */ + + struct soc_camera_host soc_host; +}; + +struct dma_chan_request { + struct mx3_camera_dev *mx3_cam; + enum ipu_channel id; +}; + +static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt); + +static u32 csi_reg_read(struct mx3_camera_dev *mx3, off_t reg) +{ + return __raw_readl(mx3->base + reg); +} + +static void csi_reg_write(struct mx3_camera_dev *mx3, u32 value, off_t reg) +{ + __raw_writel(value, mx3->base + reg); +} + +/* Called from the IPU IDMAC ISR */ +static void mx3_cam_dma_done(void *arg) +{ + struct idmac_tx_desc *desc = to_tx_desc(arg); + struct dma_chan *chan = desc->txd.chan; + struct idmac_channel *ichannel = to_idmac_chan(chan); + struct mx3_camera_dev *mx3_cam = ichannel->client; + struct videobuf_buffer *vb; + + dev_dbg(chan->device->dev, "callback cookie %d, active DMA 0x%08x\n", + desc->txd.cookie, mx3_cam->active ? sg_dma_address(&mx3_cam->active->sg) : 0); + + spin_lock(&mx3_cam->lock); + if (mx3_cam->active) { + vb = &mx3_cam->active->vb; + + list_del_init(&vb->queue); + vb->state = VIDEOBUF_DONE; + do_gettimeofday(&vb->ts); + vb->field_count++; + wake_up(&vb->done); + } + + if (list_empty(&mx3_cam->capture)) { + mx3_cam->active = NULL; + spin_unlock(&mx3_cam->lock); + + /* + * stop capture - without further buffers IPU_CHA_BUF0_RDY will + * not get updated + */ + return; + } + + mx3_cam->active = list_entry(mx3_cam->capture.next, + struct mx3_camera_buffer, vb.queue); + mx3_cam->active->vb.state = VIDEOBUF_ACTIVE; + spin_unlock(&mx3_cam->lock); +} + +static void free_buffer(struct videobuf_queue *vq, struct mx3_camera_buffer *buf) +{ + struct soc_camera_device *icd = vq->priv_data; + struct videobuf_buffer *vb = &buf->vb; + struct dma_async_tx_descriptor *txd = buf->txd; + struct idmac_channel *ichan; + + BUG_ON(in_interrupt()); + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + vb, vb->baddr, vb->bsize); + + /* + * This waits until this buffer is out of danger, i.e., until it is no + * longer in STATE_QUEUED or STATE_ACTIVE + */ + videobuf_waiton(vb, 0, 0); + if (txd) { + ichan = to_idmac_chan(txd->chan); + async_tx_ack(txd); + } + videobuf_dma_contig_free(vq, vb); + buf->txd = NULL; + + vb->state = VIDEOBUF_NEEDS_INIT; +} + +/* + * Videobuf operations + */ + +/* + * Calculate the __buffer__ (not data) size and number of buffers. + * Called with .vb_lock held + */ +static int mx3_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx3_camera_dev *mx3_cam = ici->priv; + /* + * bits-per-pixel (depth) as specified in camera's pixel format does + * not necessarily match what the camera interface writes to RAM, but + * it should be good enough for now. + */ + unsigned int bpp = DIV_ROUND_UP(icd->current_fmt->depth, 8); + + if (!mx3_cam->idmac_channel[0]) + return -EINVAL; + + *size = icd->width * icd->height * bpp; + + if (!*count) + *count = 32; + + if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) + *count = MAX_VIDEO_MEM * 1024 * 1024 / *size; + + return 0; +} + +/* Called with .vb_lock held */ +static int mx3_videobuf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, enum v4l2_field field) +{ + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx3_camera_dev *mx3_cam = ici->priv; + struct mx3_camera_buffer *buf = + container_of(vb, struct mx3_camera_buffer, vb); + /* current_fmt _must_ always be set */ + size_t new_size = icd->width * icd->height * + ((icd->current_fmt->depth + 7) >> 3); + int ret; + + /* + * I think, in buf_prepare you only have to protect global data, + * the actual buffer is yours + */ + + if (buf->fmt != icd->current_fmt || + vb->width != icd->width || + vb->height != icd->height || + vb->field != field) { + buf->fmt = icd->current_fmt; + vb->width = icd->width; + vb->height = icd->height; + vb->field = field; + if (vb->state != VIDEOBUF_NEEDS_INIT) + free_buffer(vq, buf); + } + + if (vb->baddr && vb->bsize < new_size) { + /* User provided buffer, but it is too small */ + ret = -ENOMEM; + goto out; + } + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; + struct scatterlist *sg = &buf->sg; + + /* + * The total size of video-buffers that will be allocated / mapped. + * *size that we calculated in videobuf_setup gets assigned to + * vb->bsize, and now we use the same calculation to get vb->size. + */ + vb->size = new_size; + + /* This actually (allocates and) maps buffers */ + ret = videobuf_iolock(vq, vb, NULL); + if (ret) + goto fail; + + /* + * We will have to configure the IDMAC channel. It has two slots + * for DMA buffers, we shall enter the first two buffers there, + * and then submit new buffers in DMA-ready interrupts + */ + sg_init_table(sg, 1); + sg_dma_address(sg) = videobuf_to_dma_contig(vb); + sg_dma_len(sg) = vb->size; + + buf->txd = ichan->dma_chan.device->device_prep_slave_sg( + &ichan->dma_chan, sg, 1, DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT); + if (!buf->txd) { + ret = -EIO; + goto fail; + } + + buf->txd->callback_param = buf->txd; + buf->txd->callback = mx3_cam_dma_done; + + vb->state = VIDEOBUF_PREPARED; + } + + return 0; + +fail: + free_buffer(vq, buf); +out: + return ret; +} + +static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc) +{ + /* Add more formats as need arises and test possibilities appear... */ + switch (fourcc) { + case V4L2_PIX_FMT_RGB565: + return IPU_PIX_FMT_RGB565; + case V4L2_PIX_FMT_RGB24: + return IPU_PIX_FMT_RGB24; + case V4L2_PIX_FMT_RGB332: + return IPU_PIX_FMT_RGB332; + case V4L2_PIX_FMT_YUV422P: + return IPU_PIX_FMT_YVU422P; + default: + return IPU_PIX_FMT_GENERIC; + } +} + +/* Called with .vb_lock held */ +static void mx3_videobuf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx3_camera_dev *mx3_cam = ici->priv; + struct mx3_camera_buffer *buf = + container_of(vb, struct mx3_camera_buffer, vb); + struct dma_async_tx_descriptor *txd = buf->txd; + struct idmac_channel *ichan = to_idmac_chan(txd->chan); + struct idmac_video_param *video = &ichan->params.video; + const struct soc_camera_data_format *data_fmt = icd->current_fmt; + dma_cookie_t cookie; + unsigned long flags; + + /* This is the configuration of one sg-element */ + video->out_pixel_fmt = fourcc_to_ipu_pix(data_fmt->fourcc); + video->out_width = icd->width; + video->out_height = icd->height; + video->out_stride = icd->width; + +#ifdef DEBUG + /* helps to see what DMA actually has written */ + memset((void *)vb->baddr, 0xaa, vb->bsize); +#endif + + spin_lock_irqsave(&mx3_cam->lock, flags); + + list_add_tail(&vb->queue, &mx3_cam->capture); + + if (!mx3_cam->active) { + mx3_cam->active = buf; + vb->state = VIDEOBUF_ACTIVE; + } else { + vb->state = VIDEOBUF_QUEUED; + } + + spin_unlock_irqrestore(&mx3_cam->lock, flags); + + cookie = txd->tx_submit(txd); + dev_dbg(&icd->dev, "Submitted cookie %d DMA 0x%08x\n", cookie, sg_dma_address(&buf->sg)); + if (cookie >= 0) + return; + + /* Submit error */ + vb->state = VIDEOBUF_PREPARED; + + spin_lock_irqsave(&mx3_cam->lock, flags); + + list_del_init(&vb->queue); + + if (mx3_cam->active == buf) + mx3_cam->active = NULL; + + spin_unlock_irqrestore(&mx3_cam->lock, flags); +} + +/* Called with .vb_lock held */ +static void mx3_videobuf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx3_camera_dev *mx3_cam = ici->priv; + struct mx3_camera_buffer *buf = + container_of(vb, struct mx3_camera_buffer, vb); + unsigned long flags; + + dev_dbg(&icd->dev, "Release%s DMA 0x%08x (state %d), queue %sempty\n", + mx3_cam->active == buf ? " active" : "", sg_dma_address(&buf->sg), + vb->state, list_empty(&vb->queue) ? "" : "not "); + spin_lock_irqsave(&mx3_cam->lock, flags); + if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) && + !list_empty(&vb->queue)) { + vb->state = VIDEOBUF_ERROR; + + list_del_init(&vb->queue); + if (mx3_cam->active == buf) + mx3_cam->active = NULL; + } + spin_unlock_irqrestore(&mx3_cam->lock, flags); + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops mx3_videobuf_ops = { + .buf_setup = mx3_videobuf_setup, + .buf_prepare = mx3_videobuf_prepare, + .buf_queue = mx3_videobuf_queue, + .buf_release = mx3_videobuf_release, +}; + +static void mx3_camera_init_videobuf(struct videobuf_queue *q, + struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx3_camera_dev *mx3_cam = ici->priv; + + videobuf_queue_dma_contig_init(q, &mx3_videobuf_ops, mx3_cam->dev, + &mx3_cam->lock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_NONE, + sizeof(struct mx3_camera_buffer), icd); +} + +/* First part of ipu_csi_init_interface() */ +static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam, + struct soc_camera_device *icd) +{ + u32 conf; + long rate; + + /* Set default size: ipu_csi_set_window_size() */ + csi_reg_write(mx3_cam, (640 - 1) | ((480 - 1) << 16), CSI_ACT_FRM_SIZE); + /* ...and position to 0:0: ipu_csi_set_window_pos() */ + conf = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000; + csi_reg_write(mx3_cam, conf, CSI_OUT_FRM_CTRL); + + /* We use only gated clock synchronisation mode so far */ + conf = 0 << CSI_SENS_CONF_SENS_PRTCL_SHIFT; + + /* Set generic data, platform-biggest bus-width */ + conf |= CSI_SENS_CONF_DATA_FMT_BAYER; + + if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15) + conf |= 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; + else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10) + conf |= 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; + else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8) + conf |= 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; + else/* if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)*/ + conf |= 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; + + if (mx3_cam->platform_flags & MX3_CAMERA_CLK_SRC) + conf |= 1 << CSI_SENS_CONF_SENS_CLKSRC_SHIFT; + if (mx3_cam->platform_flags & MX3_CAMERA_EXT_VSYNC) + conf |= 1 << CSI_SENS_CONF_EXT_VSYNC_SHIFT; + if (mx3_cam->platform_flags & MX3_CAMERA_DP) + conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT; + if (mx3_cam->platform_flags & MX3_CAMERA_PCP) + conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT; + if (mx3_cam->platform_flags & MX3_CAMERA_HSP) + conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT; + if (mx3_cam->platform_flags & MX3_CAMERA_VSP) + conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT; + + /* ipu_csi_init_interface() */ + csi_reg_write(mx3_cam, conf, CSI_SENS_CONF); + + clk_enable(mx3_cam->clk); + rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk); + dev_dbg(&icd->dev, "Set SENS_CONF to %x, rate %ld\n", conf, rate); + if (rate) + clk_set_rate(mx3_cam->clk, rate); +} + +/* Called with .video_lock held */ +static int mx3_camera_add_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx3_camera_dev *mx3_cam = ici->priv; + int ret; + + if (mx3_cam->icd) { + ret = -EBUSY; + goto ebusy; + } + + mx3_camera_activate(mx3_cam, icd); + ret = icd->ops->init(icd); + if (ret < 0) { + clk_disable(mx3_cam->clk); + goto einit; + } + + mx3_cam->icd = icd; + +einit: +ebusy: + if (!ret) + dev_info(&icd->dev, "MX3 Camera driver attached to camera %d\n", + icd->devnum); + + return ret; +} + +/* Called with .video_lock held */ +static void mx3_camera_remove_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx3_camera_dev *mx3_cam = ici->priv; + struct idmac_channel **ichan = &mx3_cam->idmac_channel[0]; + + BUG_ON(icd != mx3_cam->icd); + + if (*ichan) { + dma_release_channel(&(*ichan)->dma_chan); + *ichan = NULL; + } + + icd->ops->release(icd); + + clk_disable(mx3_cam->clk); + + mx3_cam->icd = NULL; + + dev_info(&icd->dev, "MX3 Camera driver detached from camera %d\n", + icd->devnum); +} + +static bool channel_change_requested(struct soc_camera_device *icd, + const struct soc_camera_format_xlate *xlate, + __u32 pixfmt, struct v4l2_rect *rect) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx3_camera_dev *mx3_cam = ici->priv; + struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; + + /* So far only one configuration is supported */ + return pixfmt || (ichan && rect->width * rect->height > + icd->width * icd->height); +} + +static int test_platform_param(struct mx3_camera_dev *mx3_cam, + unsigned char buswidth, unsigned long *flags) +{ + /* + * Platform specified synchronization and pixel clock polarities are + * only a recommendation and are only used during probing. MX3x + * camera interface only works in master mode, i.e., uses HSYNC and + * VSYNC signals from the sensor + */ + *flags = SOCAM_MASTER | + SOCAM_HSYNC_ACTIVE_HIGH | + SOCAM_HSYNC_ACTIVE_LOW | + SOCAM_VSYNC_ACTIVE_HIGH | + SOCAM_VSYNC_ACTIVE_LOW | + SOCAM_PCLK_SAMPLE_RISING | + SOCAM_PCLK_SAMPLE_FALLING | + SOCAM_DATA_ACTIVE_HIGH | + SOCAM_DATA_ACTIVE_LOW; + + /* If requested data width is supported by the platform, use it or any + * possible lower value - i.MX31 is smart enough to schift bits */ + switch (buswidth) { + case 15: + if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)) + return -EINVAL; + *flags |= SOCAM_DATAWIDTH_15 | SOCAM_DATAWIDTH_10 | + SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4; + break; + case 10: + if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)) + return -EINVAL; + *flags |= SOCAM_DATAWIDTH_10 | SOCAM_DATAWIDTH_8 | + SOCAM_DATAWIDTH_4; + break; + case 8: + if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)) + return -EINVAL; + *flags |= SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4; + break; + case 4: + if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)) + return -EINVAL; + *flags |= SOCAM_DATAWIDTH_4; + break; + default: + dev_info(mx3_cam->dev, "Unsupported bus width %d\n", buswidth); + return -EINVAL; + } + + return 0; +} + +static int mx3_camera_try_bus_param(struct soc_camera_device *icd, + const unsigned int depth) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx3_camera_dev *mx3_cam = ici->priv; + unsigned long bus_flags, camera_flags; + int ret = test_platform_param(mx3_cam, depth, &bus_flags); + + dev_dbg(&ici->dev, "requested bus width %d bit: %d\n", depth, ret); + + if (ret < 0) + return ret; + + camera_flags = icd->ops->query_bus_param(icd); + + ret = soc_camera_bus_param_compatible(camera_flags, bus_flags); + if (ret < 0) + dev_warn(&icd->dev, "Flags incompatible: camera %lx, host %lx\n", + camera_flags, bus_flags); + + return ret; +} + +static bool chan_filter(struct dma_chan *chan, void *arg) +{ + struct dma_chan_request *rq = arg; + struct mx3_camera_pdata *pdata; + + if (!rq) + return false; + + pdata = rq->mx3_cam->dev->platform_data; + + return rq->id == chan->chan_id && + pdata->dma_dev == chan->device->dev; +} + +static const struct soc_camera_data_format mx3_camera_formats[] = { + { + .name = "Bayer (sRGB) 8 bit", + .depth = 8, + .fourcc = V4L2_PIX_FMT_SBGGR8, + .colorspace = V4L2_COLORSPACE_SRGB, + }, { + .name = "Monochrome 8 bit", + .depth = 8, + .fourcc = V4L2_PIX_FMT_GREY, + .colorspace = V4L2_COLORSPACE_JPEG, + }, +}; + +static bool buswidth_supported(struct soc_camera_host *ici, int depth) +{ + struct mx3_camera_dev *mx3_cam = ici->priv; + + switch (depth) { + case 4: + return !!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4); + case 8: + return !!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8); + case 10: + return !!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10); + case 15: + return !!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15); + } + return false; +} + +static int mx3_camera_get_formats(struct soc_camera_device *icd, int idx, + struct soc_camera_format_xlate *xlate) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + int formats = 0, buswidth, ret; + + buswidth = icd->formats[idx].depth; + + if (!buswidth_supported(ici, buswidth)) + return 0; + + ret = mx3_camera_try_bus_param(icd, buswidth); + if (ret < 0) + return 0; + + switch (icd->formats[idx].fourcc) { + case V4L2_PIX_FMT_SGRBG10: + formats++; + if (xlate) { + xlate->host_fmt = &mx3_camera_formats[0]; + xlate->cam_fmt = icd->formats + idx; + xlate->buswidth = buswidth; + xlate++; + dev_dbg(&ici->dev, "Providing format %s using %s\n", + mx3_camera_formats[0].name, + icd->formats[idx].name); + } + goto passthrough; + case V4L2_PIX_FMT_Y16: + formats++; + if (xlate) { + xlate->host_fmt = &mx3_camera_formats[1]; + xlate->cam_fmt = icd->formats + idx; + xlate->buswidth = buswidth; + xlate++; + dev_dbg(&ici->dev, "Providing format %s using %s\n", + mx3_camera_formats[0].name, + icd->formats[idx].name); + } + default: +passthrough: + /* Generic pass-through */ + formats++; + if (xlate) { + xlate->host_fmt = icd->formats + idx; + xlate->cam_fmt = icd->formats + idx; + xlate->buswidth = buswidth; + xlate++; + dev_dbg(&ici->dev, + "Providing format %s in pass-through mode\n", + icd->formats[idx].name); + } + } + + return formats; +} + +static int mx3_camera_set_fmt(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx3_camera_dev *mx3_cam = ici->priv; + const struct soc_camera_format_xlate *xlate; + u32 ctrl, width_field, height_field; + int ret; + + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (pixfmt && !xlate) { + dev_warn(&ici->dev, "Format %x not found\n", pixfmt); + return -EINVAL; + } + + /* + * We now know pixel formats and can decide upon DMA-channel(s) + * So far only direct camera-to-memory is supported + */ + if (channel_change_requested(icd, xlate, pixfmt, rect)) { + dma_cap_mask_t mask; + struct dma_chan *chan; + struct idmac_channel **ichan = &mx3_cam->idmac_channel[0]; + /* We have to use IDMAC_IC_7 for Bayer / generic data */ + struct dma_chan_request rq = {.mx3_cam = mx3_cam, + .id = IDMAC_IC_7}; + + if (*ichan) { + struct videobuf_buffer *vb, *_vb; + dma_release_channel(&(*ichan)->dma_chan); + *ichan = NULL; + mx3_cam->active = NULL; + list_for_each_entry_safe(vb, _vb, &mx3_cam->capture, queue) { + list_del_init(&vb->queue); + vb->state = VIDEOBUF_ERROR; + wake_up(&vb->done); + } + } + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_cap_set(DMA_PRIVATE, mask); + chan = dma_request_channel(mask, chan_filter, &rq); + if (!chan) + return -EBUSY; + + *ichan = to_idmac_chan(chan); + (*ichan)->client = mx3_cam; + } + + /* + * Might have to perform a complete interface initialisation like in + * ipu_csi_init_interface() in mxc_v4l2_s_param(). Also consider + * mxc_v4l2_s_fmt() + */ + + /* Setup frame size - this cannot be changed on-the-fly... */ + width_field = rect->width - 1; + height_field = rect->height - 1; + csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_SENS_FRM_SIZE); + + csi_reg_write(mx3_cam, width_field << 16, CSI_FLASH_STROBE_1); + csi_reg_write(mx3_cam, (height_field << 16) | 0x22, CSI_FLASH_STROBE_2); + + csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_ACT_FRM_SIZE); + + /* ...and position */ + ctrl = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000; + /* Sensor does the cropping */ + csi_reg_write(mx3_cam, ctrl | 0 | (0 << 8), CSI_OUT_FRM_CTRL); + + /* + * No need to free resources here if we fail, we'll see if we need to + * do this next time we are called + */ + + ret = icd->ops->set_fmt(icd, pixfmt ? xlate->cam_fmt->fourcc : 0, rect); + if (pixfmt && !ret) { + icd->buswidth = xlate->buswidth; + icd->current_fmt = xlate->host_fmt; + } + + return ret; +} + +static int mx3_camera_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + const struct soc_camera_format_xlate *xlate; + struct v4l2_pix_format *pix = &f->fmt.pix; + __u32 pixfmt = pix->pixelformat; + enum v4l2_field field; + int ret; + + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (pixfmt && !xlate) { + dev_warn(&ici->dev, "Format %x not found\n", pixfmt); + return -EINVAL; + } + + /* limit to MX3 hardware capabilities */ + if (pix->height > 4096) + pix->height = 4096; + if (pix->width > 4096) + pix->width = 4096; + + pix->bytesperline = pix->width * + DIV_ROUND_UP(xlate->host_fmt->depth, 8); + pix->sizeimage = pix->height * pix->bytesperline; + + /* camera has to see its format, but the user the original one */ + pix->pixelformat = xlate->cam_fmt->fourcc; + /* limit to sensor capabilities */ + ret = icd->ops->try_fmt(icd, f); + pix->pixelformat = xlate->host_fmt->fourcc; + + field = pix->field; + + if (field == V4L2_FIELD_ANY) { + pix->field = V4L2_FIELD_NONE; + } else if (field != V4L2_FIELD_NONE) { + dev_err(&icd->dev, "Field type %d unsupported.\n", field); + return -EINVAL; + } + + return ret; +} + +static int mx3_camera_reqbufs(struct soc_camera_file *icf, + struct v4l2_requestbuffers *p) +{ + return 0; +} + +static unsigned int mx3_camera_poll(struct file *file, poll_table *pt) +{ + struct soc_camera_file *icf = file->private_data; + + return videobuf_poll_stream(file, &icf->vb_vidq, pt); +} + +static int mx3_camera_querycap(struct soc_camera_host *ici, + struct v4l2_capability *cap) +{ + /* cap->name is set by the firendly caller:-> */ + strlcpy(cap->card, "i.MX3x Camera", sizeof(cap->card)); + cap->version = KERNEL_VERSION(0, 2, 2); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + + return 0; +} + +static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx3_camera_dev *mx3_cam = ici->priv; + unsigned long bus_flags, camera_flags, common_flags; + u32 dw, sens_conf; + int ret = test_platform_param(mx3_cam, icd->buswidth, &bus_flags); + const struct soc_camera_format_xlate *xlate; + + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (!xlate) { + dev_warn(&ici->dev, "Format %x not found\n", pixfmt); + return -EINVAL; + } + + dev_dbg(&ici->dev, "requested bus width %d bit: %d\n", + icd->buswidth, ret); + + if (ret < 0) + return ret; + + camera_flags = icd->ops->query_bus_param(icd); + + common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags); + if (!common_flags) { + dev_dbg(&ici->dev, "no common flags: camera %lx, host %lx\n", + camera_flags, bus_flags); + return -EINVAL; + } + + /* Make choices, based on platform preferences */ + if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) && + (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) { + if (mx3_cam->platform_flags & MX3_CAMERA_HSP) + common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH; + else + common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW; + } + + if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) && + (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) { + if (mx3_cam->platform_flags & MX3_CAMERA_VSP) + common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH; + else + common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW; + } + + if ((common_flags & SOCAM_DATA_ACTIVE_HIGH) && + (common_flags & SOCAM_DATA_ACTIVE_LOW)) { + if (mx3_cam->platform_flags & MX3_CAMERA_DP) + common_flags &= ~SOCAM_DATA_ACTIVE_HIGH; + else + common_flags &= ~SOCAM_DATA_ACTIVE_LOW; + } + + if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) && + (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) { + if (mx3_cam->platform_flags & MX3_CAMERA_PCP) + common_flags &= ~SOCAM_PCLK_SAMPLE_RISING; + else + common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING; + } + + /* Make the camera work in widest common mode, we'll take care of + * the rest */ + if (common_flags & SOCAM_DATAWIDTH_15) + common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) | + SOCAM_DATAWIDTH_15; + else if (common_flags & SOCAM_DATAWIDTH_10) + common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) | + SOCAM_DATAWIDTH_10; + else if (common_flags & SOCAM_DATAWIDTH_8) + common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) | + SOCAM_DATAWIDTH_8; + else + common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) | + SOCAM_DATAWIDTH_4; + + ret = icd->ops->set_bus_param(icd, common_flags); + if (ret < 0) + return ret; + + /* + * So far only gated clock mode is supported. Add a line + * (3 << CSI_SENS_CONF_SENS_PRTCL_SHIFT) | + * below and select the required mode when supporting other + * synchronisation protocols. + */ + sens_conf = csi_reg_read(mx3_cam, CSI_SENS_CONF) & + ~((1 << CSI_SENS_CONF_VSYNC_POL_SHIFT) | + (1 << CSI_SENS_CONF_HSYNC_POL_SHIFT) | + (1 << CSI_SENS_CONF_DATA_POL_SHIFT) | + (1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT) | + (3 << CSI_SENS_CONF_DATA_FMT_SHIFT) | + (3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT)); + + /* TODO: Support RGB and YUV formats */ + + /* This has been set in mx3_camera_activate(), but we clear it above */ + sens_conf |= CSI_SENS_CONF_DATA_FMT_BAYER; + + if (common_flags & SOCAM_PCLK_SAMPLE_FALLING) + sens_conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT; + if (common_flags & SOCAM_HSYNC_ACTIVE_LOW) + sens_conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT; + if (common_flags & SOCAM_VSYNC_ACTIVE_LOW) + sens_conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT; + if (common_flags & SOCAM_DATA_ACTIVE_LOW) + sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT; + + /* Just do what we're asked to do */ + switch (xlate->host_fmt->depth) { + case 4: + dw = 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; + break; + case 8: + dw = 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; + break; + case 10: + dw = 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; + break; + default: + /* + * Actually it can only be 15 now, default is just to silence + * compiler warnings + */ + case 15: + dw = 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; + } + + csi_reg_write(mx3_cam, sens_conf | dw, CSI_SENS_CONF); + + dev_dbg(&ici->dev, "Set SENS_CONF to %x\n", sens_conf | dw); + + return 0; +} + +static struct soc_camera_host_ops mx3_soc_camera_host_ops = { + .owner = THIS_MODULE, + .add = mx3_camera_add_device, + .remove = mx3_camera_remove_device, +#ifdef CONFIG_PM + .suspend = mx3_camera_suspend, + .resume = mx3_camera_resume, +#endif + .set_fmt = mx3_camera_set_fmt, + .try_fmt = mx3_camera_try_fmt, + .get_formats = mx3_camera_get_formats, + .init_videobuf = mx3_camera_init_videobuf, + .reqbufs = mx3_camera_reqbufs, + .poll = mx3_camera_poll, + .querycap = mx3_camera_querycap, + .set_bus_param = mx3_camera_set_bus_param, +}; + +static int mx3_camera_probe(struct platform_device *pdev) +{ + struct mx3_camera_dev *mx3_cam; + struct resource *res; + void __iomem *base; + int err = 0; + struct soc_camera_host *soc_host; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + err = -ENODEV; + goto egetres; + } + + mx3_cam = vmalloc(sizeof(*mx3_cam)); + if (!mx3_cam) { + dev_err(&pdev->dev, "Could not allocate mx3 camera object\n"); + err = -ENOMEM; + goto ealloc; + } + memset(mx3_cam, 0, sizeof(*mx3_cam)); + + mx3_cam->clk = clk_get(&pdev->dev, "csi_clk"); + if (IS_ERR(mx3_cam->clk)) { + err = PTR_ERR(mx3_cam->clk); + goto eclkget; + } + + dev_set_drvdata(&pdev->dev, mx3_cam); + + mx3_cam->pdata = pdev->dev.platform_data; + mx3_cam->platform_flags = mx3_cam->pdata->flags; + if (!(mx3_cam->platform_flags & (MX3_CAMERA_DATAWIDTH_4 | + MX3_CAMERA_DATAWIDTH_8 | MX3_CAMERA_DATAWIDTH_10 | + MX3_CAMERA_DATAWIDTH_15))) { + /* Platform hasn't set available data widths. This is bad. + * Warn and use a default. */ + dev_warn(&pdev->dev, "WARNING! Platform hasn't set available " + "data widths, using default 8 bit\n"); + mx3_cam->platform_flags |= MX3_CAMERA_DATAWIDTH_8; + } + + mx3_cam->mclk = mx3_cam->pdata->mclk_10khz * 10000; + if (!mx3_cam->mclk) { + dev_warn(&pdev->dev, + "mclk_10khz == 0! Please, fix your platform data. " + "Using default 20MHz\n"); + mx3_cam->mclk = 20000000; + } + + /* list of video-buffers */ + INIT_LIST_HEAD(&mx3_cam->capture); + spin_lock_init(&mx3_cam->lock); + + base = ioremap(res->start, res->end - res->start + 1); + if (!base) { + err = -ENOMEM; + goto eioremap; + } + + mx3_cam->base = base; + mx3_cam->dev = &pdev->dev; + + soc_host = &mx3_cam->soc_host; + soc_host->drv_name = MX3_CAM_DRV_NAME; + soc_host->ops = &mx3_soc_camera_host_ops; + soc_host->priv = mx3_cam; + soc_host->dev.parent = &pdev->dev; + soc_host->nr = pdev->id; + err = soc_camera_host_register(soc_host); + if (err) + goto ecamhostreg; + + /* IDMAC interface */ + dmaengine_get(); + + return 0; + +ecamhostreg: + iounmap(base); +eioremap: + clk_put(mx3_cam->clk); +eclkget: + vfree(mx3_cam); +ealloc: +egetres: + return err; +} + +static int __devexit mx3_camera_remove(struct platform_device *pdev) +{ + struct mx3_camera_dev *mx3_cam = platform_get_drvdata(pdev); + + clk_put(mx3_cam->clk); + + soc_camera_host_unregister(&mx3_cam->soc_host); + + iounmap(mx3_cam->base); + + /* + * The channel has either not been allocated, + * or should have been released + */ + if (WARN_ON(mx3_cam->idmac_channel[0])) + dma_release_channel(&mx3_cam->idmac_channel[0]->dma_chan); + + vfree(mx3_cam); + + dmaengine_put(); + + dev_info(&pdev->dev, "i.MX3x Camera driver unloaded\n"); + + return 0; +} + +static struct platform_driver mx3_camera_driver = { + .driver = { + .name = MX3_CAM_DRV_NAME, + }, + .probe = mx3_camera_probe, + .remove = __exit_p(mx3_camera_remove), +}; + + +static int __devinit mx3_camera_init(void) +{ + return platform_driver_register(&mx3_camera_driver); +} + +static void __exit mx3_camera_exit(void) +{ + platform_driver_unregister(&mx3_camera_driver); +} + +module_init(mx3_camera_init); +module_exit(mx3_camera_exit); + +MODULE_DESCRIPTION("i.MX3x SoC Camera Host driver"); +MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/linux/drivers/media/video/ov772x.c b/linux/drivers/media/video/ov772x.c index ce389cacf..2f3313524 100644 --- a/linux/drivers/media/video/ov772x.c +++ b/linux/drivers/media/video/ov772x.c @@ -217,10 +217,11 @@ #define OCAP_4x 0x03 /* 4x */ /* COM3 */ -#define SWAP_MASK 0x38 +#define SWAP_MASK (SWAP_RGB | SWAP_YUV | SWAP_ML) +#define IMG_MASK (VFLIP_IMG | HFLIP_IMG) -#define VFIMG_ON_OFF 0x80 /* Vertical flip image ON/OFF selection */ -#define HMIMG_ON_OFF 0x40 /* Horizontal mirror image ON/OFF selection */ +#define VFLIP_IMG 0x80 /* Vertical flip image ON/OFF selection */ +#define HFLIP_IMG 0x40 /* Horizontal mirror image ON/OFF selection */ #define SWAP_RGB 0x20 /* Swap B/R output sequence in RGB mode */ #define SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV mode */ #define SWAP_ML 0x08 /* Swap output MSB/LSB */ @@ -271,11 +272,13 @@ #define SLCT_QVGA 0x40 /* 1 : QVGA */ #define ITU656_ON_OFF 0x20 /* ITU656 protocol ON/OFF selection */ /* RGB output format control */ +#define FMT_MASK 0x0c /* Mask of color format */ #define FMT_GBR422 0x00 /* 00 : GBR 4:2:2 */ #define FMT_RGB565 0x04 /* 01 : RGB 565 */ #define FMT_RGB555 0x08 /* 10 : RGB 555 */ #define FMT_RGB444 0x0c /* 11 : RGB 444 */ /* Output format control */ +#define OFMT_MASK 0x03 /* Mask of output format */ #define OFMT_YUV 0x00 /* 00 : YUV */ #define OFMT_P_BRAW 0x01 /* 01 : Processed Bayer RAW */ #define OFMT_RGB 0x02 /* 10 : RGB */ @@ -299,7 +302,7 @@ #define GAIN_2x 0x00 /* 000 : 2x */ #define GAIN_4x 0x10 /* 001 : 4x */ #define GAIN_8x 0x20 /* 010 : 8x */ -#define GAIN_16x 0x30 /* 011 : 16x */ +#define GAIN_16x 0x30 /* 011 : 16x */ #define GAIN_32x 0x40 /* 100 : 32x */ #define GAIN_64x 0x50 /* 101 : 64x */ #define GAIN_128x 0x60 /* 110 : 128x */ @@ -356,13 +359,6 @@ #define VOSZ_QVGA 0x78 /* - * bit configure (32 bit) - * this is used in struct ov772x_color_format :: option - */ -#define OP_UV 0x00000001 -#define OP_SWAP_RGB 0x00000002 - -/* * ID */ #define OV7720 0x7720 @@ -380,8 +376,9 @@ struct regval_list { struct ov772x_color_format { char *name; __u32 fourcc; - const struct regval_list *regs; - unsigned int option; + u8 dsp3; + u8 com3; + u8 com7; }; struct ov772x_win_size { @@ -399,39 +396,13 @@ struct ov772x_priv { const struct ov772x_color_format *fmt; const struct ov772x_win_size *win; int model; + unsigned int flag_vflip:1; + unsigned int flag_hflip:1; }; #define ENDMARKER { 0xff, 0xff } /* - * register setting for color format - */ -static const struct regval_list ov772x_RGB555_regs[] = { - { COM3, 0x00 }, - { COM7, FMT_RGB555 | OFMT_RGB }, - ENDMARKER, -}; - -static const struct regval_list ov772x_RGB565_regs[] = { - { COM3, 0x00 }, - { COM7, FMT_RGB565 | OFMT_RGB }, - ENDMARKER, -}; - -static const struct regval_list ov772x_YYUV_regs[] = { - { COM3, SWAP_YUV }, - { COM7, OFMT_YUV }, - ENDMARKER, -}; - -static const struct regval_list ov772x_UVYY_regs[] = { - { COM3, 0x00 }, - { COM7, OFMT_YUV }, - ENDMARKER, -}; - - -/* * register setting for window size */ static const struct regval_list ov772x_qvga_regs[] = { @@ -500,38 +471,48 @@ static const struct soc_camera_data_format ov772x_fmt_lists[] = { /* * color format list */ -#define T_YUYV 0 static const struct ov772x_color_format ov772x_cfmts[] = { - [T_YUYV] = { + { SETFOURCC(YUYV), - .regs = ov772x_YYUV_regs, + .dsp3 = 0x0, + .com3 = SWAP_YUV, + .com7 = OFMT_YUV, }, { SETFOURCC(YVYU), - .regs = ov772x_YYUV_regs, - .option = OP_UV, + .dsp3 = UV_ON, + .com3 = SWAP_YUV, + .com7 = OFMT_YUV, }, { SETFOURCC(UYVY), - .regs = ov772x_UVYY_regs, + .dsp3 = 0x0, + .com3 = 0x0, + .com7 = OFMT_YUV, }, { SETFOURCC(RGB555), - .regs = ov772x_RGB555_regs, - .option = OP_SWAP_RGB, + .dsp3 = 0x0, + .com3 = SWAP_RGB, + .com7 = FMT_RGB555 | OFMT_RGB, }, { SETFOURCC(RGB555X), - .regs = ov772x_RGB555_regs, + .dsp3 = 0x0, + .com3 = 0x0, + .com7 = FMT_RGB555 | OFMT_RGB, }, { SETFOURCC(RGB565), - .regs = ov772x_RGB565_regs, - .option = OP_SWAP_RGB, + .dsp3 = 0x0, + .com3 = SWAP_RGB, + .com7 = FMT_RGB565 | OFMT_RGB, }, { SETFOURCC(RGB565X), - .regs = ov772x_RGB565_regs, + .dsp3 = 0x0, + .com3 = 0x0, + .com7 = FMT_RGB565 | OFMT_RGB, }, }; @@ -562,6 +543,27 @@ static const struct ov772x_win_size ov772x_win_qvga = { .regs = ov772x_qvga_regs, }; +static const struct v4l2_queryctrl ov772x_controls[] = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Vertically", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Horizontally", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, +}; + /* * general function @@ -587,8 +589,11 @@ static int ov772x_mask_set(struct i2c_client *client, u8 set) { s32 val = i2c_smbus_read_byte_data(client, command); + if (val < 0) + return val; + val &= ~mask; - val |= set; + val |= set & mask; return i2c_smbus_write_byte_data(client, command, val); } @@ -635,74 +640,20 @@ static int ov772x_release(struct soc_camera_device *icd) static int ov772x_start_capture(struct soc_camera_device *icd) { struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); - int ret; - - if (!priv->win) - priv->win = &ov772x_win_vga; - if (!priv->fmt) - priv->fmt = &ov772x_cfmts[T_YUYV]; - - /* - * reset hardware - */ - ov772x_reset(priv->client); - - /* - * set color format - */ - ret = ov772x_write_array(priv->client, priv->fmt->regs); - if (ret < 0) - goto start_end; - /* - * set size format - */ - ret = ov772x_write_array(priv->client, priv->win->regs); - if (ret < 0) - goto start_end; - - /* - * set COM7 bit ( QVGA or VGA ) - */ - ret = ov772x_mask_set(priv->client, - COM7, SLCT_MASK, priv->win->com7_bit); - if (ret < 0) - goto start_end; - - /* - * set UV setting - */ - if (priv->fmt->option & OP_UV) { - ret = ov772x_mask_set(priv->client, - DSP_CTRL3, UV_MASK, UV_ON); - if (ret < 0) - goto start_end; - } - - /* - * set SWAP setting - */ - if (priv->fmt->option & OP_SWAP_RGB) { - ret = ov772x_mask_set(priv->client, - COM3, SWAP_MASK, SWAP_RGB); - if (ret < 0) - goto start_end; + if (!priv->win || !priv->fmt) { + dev_err(&icd->dev, "norm or win select error\n"); + return -EPERM; } dev_dbg(&icd->dev, "format %s, win %s\n", priv->fmt->name, priv->win->name); -start_end: - priv->fmt = NULL; - priv->win = NULL; - - return ret; + return 0; } static int ov772x_stop_capture(struct soc_camera_device *icd) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); - ov772x_reset(priv->client); return 0; } @@ -718,11 +669,54 @@ static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd) struct soc_camera_link *icl = priv->client->dev.platform_data; unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | - priv->info->buswidth; + SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth; return soc_camera_apply_sensor_flags(icl, flags); } +static int ov772x_get_control(struct soc_camera_device *icd, + struct v4l2_control *ctrl) +{ + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + ctrl->value = priv->flag_vflip; + break; + case V4L2_CID_HFLIP: + ctrl->value = priv->flag_hflip; + break; + } + return 0; +} + +static int ov772x_set_control(struct soc_camera_device *icd, + struct v4l2_control *ctrl) +{ + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + int ret = 0; + u8 val; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + val = ctrl->value ? VFLIP_IMG : 0x00; + priv->flag_vflip = ctrl->value; + if (priv->info->flags & OV772X_FLAG_VFLIP) + val ^= VFLIP_IMG; + ret = ov772x_mask_set(priv->client, COM3, VFLIP_IMG, val); + break; + case V4L2_CID_HFLIP: + val = ctrl->value ? HFLIP_IMG : 0x00; + priv->flag_hflip = ctrl->value; + if (priv->info->flags & OV772X_FLAG_HFLIP) + val ^= HFLIP_IMG; + ret = ov772x_mask_set(priv->client, COM3, HFLIP_IMG, val); + break; + } + + return ret; +} + static int ov772x_get_chip_id(struct soc_camera_device *icd, struct v4l2_dbg_chip_ident *id) { @@ -787,13 +781,13 @@ ov772x_select_win(u32 width, u32 height) return win; } - static int ov772x_set_fmt(struct soc_camera_device *icd, __u32 pixfmt, struct v4l2_rect *rect) { struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); int ret = -EINVAL; + u8 val; int i; /* @@ -803,16 +797,76 @@ static int ov772x_set_fmt(struct soc_camera_device *icd, for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) { if (pixfmt == ov772x_cfmts[i].fourcc) { priv->fmt = ov772x_cfmts + i; - ret = 0; break; } } + if (!priv->fmt) + goto ov772x_set_fmt_error; /* * select win */ priv->win = ov772x_select_win(rect->width, rect->height); + /* + * reset hardware + */ + ov772x_reset(priv->client); + + /* + * set size format + */ + ret = ov772x_write_array(priv->client, priv->win->regs); + if (ret < 0) + goto ov772x_set_fmt_error; + + /* + * set DSP_CTRL3 + */ + val = priv->fmt->dsp3; + if (val) { + ret = ov772x_mask_set(priv->client, + DSP_CTRL3, UV_MASK, val); + if (ret < 0) + goto ov772x_set_fmt_error; + } + + /* + * set COM3 + */ + val = priv->fmt->com3; + if (priv->info->flags & OV772X_FLAG_VFLIP) + val |= VFLIP_IMG; + if (priv->info->flags & OV772X_FLAG_HFLIP) + val |= HFLIP_IMG; + if (priv->flag_vflip) + val ^= VFLIP_IMG; + if (priv->flag_hflip) + val ^= HFLIP_IMG; + + ret = ov772x_mask_set(priv->client, + COM3, SWAP_MASK | IMG_MASK, val); + if (ret < 0) + goto ov772x_set_fmt_error; + + /* + * set COM7 + */ + val = priv->win->com7_bit | priv->fmt->com7; + ret = ov772x_mask_set(priv->client, + COM7, (SLCT_MASK | FMT_MASK | OFMT_MASK), + val); + if (ret < 0) + goto ov772x_set_fmt_error; + + return ret; + +ov772x_set_fmt_error: + + ov772x_reset(priv->client); + priv->win = NULL; + priv->fmt = NULL; + return ret; } @@ -889,7 +943,6 @@ static int ov772x_video_probe(struct soc_camera_device *icd) i2c_smbus_read_byte_data(priv->client, MIDH), i2c_smbus_read_byte_data(priv->client, MIDL)); - return soc_camera_video_start(icd); } @@ -910,6 +963,10 @@ static struct soc_camera_ops ov772x_ops = { .try_fmt = ov772x_try_fmt, .set_bus_param = ov772x_set_bus_param, .query_bus_param = ov772x_query_bus_param, + .controls = ov772x_controls, + .num_controls = ARRAY_SIZE(ov772x_controls), + .get_control = ov772x_get_control, + .set_control = ov772x_set_control, .get_chip_id = ov772x_get_chip_id, #ifdef CONFIG_VIDEO_ADV_DEBUG .get_register = ov772x_get_register, diff --git a/linux/drivers/media/video/pxa_camera.c b/linux/drivers/media/video/pxa_camera.c index 2c283b82a..dd524b3c6 100644 --- a/linux/drivers/media/video/pxa_camera.c +++ b/linux/drivers/media/video/pxa_camera.c @@ -888,6 +888,7 @@ static int test_platform_param(struct pxa_camera_dev *pcdev, SOCAM_HSYNC_ACTIVE_LOW | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW | + SOCAM_DATA_ACTIVE_HIGH | SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING; @@ -1164,23 +1165,23 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; - const struct soc_camera_data_format *host_fmt, *cam_fmt = NULL; - const struct soc_camera_format_xlate *xlate; + const struct soc_camera_data_format *cam_fmt = NULL; + const struct soc_camera_format_xlate *xlate = NULL; struct soc_camera_sense sense = { .master_clock = pcdev->mclk, .pixel_clock_max = pcdev->ciclk / 4, }; - int ret, buswidth; + int ret; - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - if (!xlate) { - dev_warn(&ici->dev, "Format %x not found\n", pixfmt); - return -EINVAL; - } + if (pixfmt) { + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (!xlate) { + dev_warn(&ici->dev, "Format %x not found\n", pixfmt); + return -EINVAL; + } - buswidth = xlate->buswidth; - host_fmt = xlate->host_fmt; - cam_fmt = xlate->cam_fmt; + cam_fmt = xlate->cam_fmt; + } /* If PCLK is used to latch data from the sensor, check sense */ if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN) @@ -1210,8 +1211,8 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, } if (pixfmt && !ret) { - icd->buswidth = buswidth; - icd->current_fmt = host_fmt; + icd->buswidth = xlate->buswidth; + icd->current_fmt = xlate->host_fmt; } return ret; diff --git a/linux/drivers/media/video/saa7115.c b/linux/drivers/media/video/saa7115.c index 169b01f6d..2570ebf89 100644 --- a/linux/drivers/media/video/saa7115.c +++ b/linux/drivers/media/video/saa7115.c @@ -1207,10 +1207,12 @@ static int saa711x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { switch (qc->id) { case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); case V4L2_CID_CONTRAST: case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); case V4L2_CID_HUE: - return v4l2_ctrl_query_fill_std(qc); + return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); default: return -EINVAL; } diff --git a/linux/drivers/media/video/saa7134/saa6752hs.c b/linux/drivers/media/video/saa7134/saa6752hs.c index 2e2871de1..3af36d929 100644 --- a/linux/drivers/media/video/saa7134/saa6752hs.c +++ b/linux/drivers/media/video/saa7134/saa6752hs.c @@ -599,7 +599,7 @@ static int saa6752hs_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc V4L2_MPEG_VIDEO_ASPECT_4x3); case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - err = v4l2_ctrl_query_fill_std(qctrl); + err = v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000); if (err == 0 && params->vi_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) @@ -613,12 +613,20 @@ static int saa6752hs_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc V4L2_MPEG_STREAM_TYPE_MPEG2_TS); case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); case V4L2_CID_MPEG_VIDEO_BITRATE: + return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000); case V4L2_CID_MPEG_STREAM_PID_PMT: + return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 16); case V4L2_CID_MPEG_STREAM_PID_AUDIO: + return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 260); case V4L2_CID_MPEG_STREAM_PID_VIDEO: + return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 256); case V4L2_CID_MPEG_STREAM_PID_PCR: - return v4l2_ctrl_query_fill_std(qctrl); + return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 259); default: break; diff --git a/linux/drivers/media/video/saa7134/saa7134-empress.c b/linux/drivers/media/video/saa7134/saa7134-empress.c index 813f5f88d..609342128 100644 --- a/linux/drivers/media/video/saa7134/saa7134-empress.c +++ b/linux/drivers/media/video/saa7134/saa7134-empress.c @@ -390,7 +390,7 @@ static int empress_queryctrl(struct file *file, void *priv, if (c->id == 0) return -EINVAL; if (c->id == V4L2_CID_USER_CLASS || c->id == V4L2_CID_MPEG_CLASS) - return v4l2_ctrl_query_fill_std(c); + return v4l2_ctrl_query_fill(c, 0, 0, 0, 0); if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG) return saa7134_queryctrl(file, priv, c); return saa_call_empress(dev, core, queryctrl, c); diff --git a/linux/drivers/media/video/sh_mobile_ceu_camera.c b/linux/drivers/media/video/sh_mobile_ceu_camera.c index ae98eaf5f..29ff02802 100644 --- a/linux/drivers/media/video/sh_mobile_ceu_camera.c +++ b/linux/drivers/media/video/sh_mobile_ceu_camera.c @@ -102,6 +102,30 @@ struct sh_mobile_ceu_dev { const struct soc_camera_data_format *camera_fmt; }; +static unsigned long make_bus_param(struct sh_mobile_ceu_dev *pcdev) +{ + unsigned long flags; + + flags = SOCAM_MASTER | + SOCAM_PCLK_SAMPLE_RISING | + SOCAM_HSYNC_ACTIVE_HIGH | + SOCAM_HSYNC_ACTIVE_LOW | + SOCAM_VSYNC_ACTIVE_HIGH | + SOCAM_VSYNC_ACTIVE_LOW | + SOCAM_DATA_ACTIVE_HIGH; + + if (pcdev->pdata->flags & SH_CEU_FLAG_USE_8BIT_BUS) + flags |= SOCAM_DATAWIDTH_8; + + if (pcdev->pdata->flags & SH_CEU_FLAG_USE_16BIT_BUS) + flags |= SOCAM_DATAWIDTH_16; + + if (flags & SOCAM_DATAWIDTH_MASK) + return flags; + + return 0; +} + static void ceu_write(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs, u32 data) { @@ -397,7 +421,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, camera_flags = icd->ops->query_bus_param(icd); common_flags = soc_camera_bus_param_compatible(camera_flags, - pcdev->pdata->flags); + make_bus_param(pcdev)); if (!common_flags) return -EINVAL; @@ -518,7 +542,7 @@ static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd) camera_flags = icd->ops->query_bus_param(icd); common_flags = soc_camera_bus_param_compatible(camera_flags, - pcdev->pdata->flags); + make_bus_param(pcdev)); if (!common_flags) return -EINVAL; @@ -563,11 +587,29 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx, if (ret < 0) return 0; + /* Beginning of a pass */ + if (!idx) + icd->host_priv = NULL; + switch (icd->formats[idx].fourcc) { case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_VYUY: case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_YVYU: + if (icd->host_priv) + goto add_single_format; + + /* + * Our case is simple so far: for any of the above four camera + * formats we add all our four synthesized NV* formats, so, + * just marking the device with a single flag suffices. If + * the format generation rules are more complex, you would have + * to actually hang your already added / counted formats onto + * the host_priv pointer and check whether the format you're + * going to add now is already there. + */ + icd->host_priv = (void *)sh_mobile_ceu_formats; + n = ARRAY_SIZE(sh_mobile_ceu_formats); formats += n; for (k = 0; xlate && k < n; k++) { @@ -580,6 +622,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx, icd->formats[idx].name); } default: +add_single_format: /* Generic pass-through */ formats++; if (xlate) { @@ -604,21 +647,18 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, const struct soc_camera_format_xlate *xlate; int ret; + if (!pixfmt) + return icd->ops->set_fmt(icd, pixfmt, rect); + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (!xlate) { dev_warn(&ici->dev, "Format %x not found\n", pixfmt); return -EINVAL; } - switch (pixfmt) { - case 0: /* Only geometry change */ - ret = icd->ops->set_fmt(icd, pixfmt, rect); - break; - default: - ret = icd->ops->set_fmt(icd, xlate->cam_fmt->fourcc, rect); - } + ret = icd->ops->set_fmt(icd, xlate->cam_fmt->fourcc, rect); - if (pixfmt && !ret) { + if (!ret) { icd->buswidth = xlate->buswidth; icd->current_fmt = xlate->host_fmt; pcdev->camera_fmt = xlate->cam_fmt; diff --git a/linux/drivers/media/video/sn9c102/sn9c102_devtable.h b/linux/drivers/media/video/sn9c102/sn9c102_devtable.h index 8cb3457e7..41dfb60f4 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/linux/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -123,7 +123,9 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), }, #endif { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, +#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), }, +#endif { SN9C102_USB_DEVICE(0x0c45, 0x613e, BRIDGE_SN9C120), }, { } }; diff --git a/linux/drivers/media/video/tda7432.c b/linux/drivers/media/video/tda7432.c index 30f6f8a38..104fc4246 100644 --- a/linux/drivers/media/video/tda7432.c +++ b/linux/drivers/media/video/tda7432.c @@ -422,12 +422,14 @@ static int tda7432_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) static int tda7432_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: case V4L2_CID_AUDIO_VOLUME: + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); + case V4L2_CID_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill_std(qc); + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); } return -EINVAL; } diff --git a/linux/drivers/media/video/tda9875.c b/linux/drivers/media/video/tda9875.c index 892d25a40..938bafbbd 100644 --- a/linux/drivers/media/video/tda9875.c +++ b/linux/drivers/media/video/tda9875.c @@ -327,9 +327,10 @@ static int tda9875_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { switch (qc->id) { case V4L2_CID_AUDIO_VOLUME: + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill_std(qc); + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); } return -EINVAL; } diff --git a/linux/drivers/media/video/tvaudio.c b/linux/drivers/media/video/tvaudio.c index db03d1350..755418a31 100644 --- a/linux/drivers/media/video/tvaudio.c +++ b/linux/drivers/media/video/tvaudio.c @@ -1644,21 +1644,24 @@ static int tvaudio_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) switch (qc->id) { case V4L2_CID_AUDIO_MUTE: - break; + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); case V4L2_CID_AUDIO_VOLUME: + if (desc->flags & CHIP_HAS_VOLUME) + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); + break; case V4L2_CID_AUDIO_BALANCE: - if (!(desc->flags & CHIP_HAS_VOLUME)) - return -EINVAL; + if (desc->flags & CHIP_HAS_VOLUME) + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); break; case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: - if (!(desc->flags & CHIP_HAS_BASSTREBLE)) - return -EINVAL; + if (desc->flags & CHIP_HAS_BASSTREBLE) + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); break; default: - return -EINVAL; + break; } - return v4l2_ctrl_query_fill_std(qc); + return -EINVAL; } static int tvaudio_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *rt) diff --git a/linux/drivers/media/video/tvp514x.c b/linux/drivers/media/video/tvp514x.c index 5f4cbc2b2..4262e60b8 100644 --- a/linux/drivers/media/video/tvp514x.c +++ b/linux/drivers/media/video/tvp514x.c @@ -86,9 +86,12 @@ struct tvp514x_std_info { struct v4l2_standard standard; }; +static struct tvp514x_reg tvp514x_reg_list_default[0x40]; /** - * struct tvp514x_decoded - TVP5146/47 decoder object + * struct tvp514x_decoder - TVP5146/47 decoder object * @v4l2_int_device: Slave handle + * @tvp514x_slave: Slave pointer which is used by @v4l2_int_device + * @tvp514x_regs: copy of hw's regs with preset values. * @pdata: Board specific * @client: I2C client data * @id: Entry from I2C table @@ -103,7 +106,9 @@ struct tvp514x_std_info { * @route: input and output routing at chip level */ struct tvp514x_decoder { - struct v4l2_int_device *v4l2_int_device; + struct v4l2_int_device v4l2_int_device; + struct v4l2_int_slave tvp514x_slave; + struct tvp514x_reg tvp514x_regs[ARRAY_SIZE(tvp514x_reg_list_default)]; const struct tvp514x_platform_data *pdata; struct i2c_client *client; @@ -124,7 +129,7 @@ struct tvp514x_decoder { }; /* TVP514x default register values */ -static struct tvp514x_reg tvp514x_reg_list[] = { +static struct tvp514x_reg tvp514x_reg_list_default[] = { {TOK_WRITE, REG_INPUT_SEL, 0x05}, /* Composite selected */ {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F}, {TOK_WRITE, REG_VIDEO_STD, 0x00}, /* Auto mode */ @@ -422,7 +427,7 @@ static int tvp514x_configure(struct tvp514x_decoder *decoder) /* common register initialization */ err = - tvp514x_write_regs(decoder->client, tvp514x_reg_list); + tvp514x_write_regs(decoder->client, decoder->tvp514x_regs); if (err) return err; @@ -580,7 +585,8 @@ static int ioctl_s_std(struct v4l2_int_device *s, v4l2_std_id *std_id) return err; decoder->current_std = i; - tvp514x_reg_list[REG_VIDEO_STD].val = decoder->std_list[i].video_std; + decoder->tvp514x_regs[REG_VIDEO_STD].val = + decoder->std_list[i].video_std; v4l_dbg(1, debug, decoder->client, "Standard set to: %s", decoder->std_list[i].standard.name); @@ -625,8 +631,8 @@ static int ioctl_s_routing(struct v4l2_int_device *s, if (err) return err; - tvp514x_reg_list[REG_INPUT_SEL].val = input_sel; - tvp514x_reg_list[REG_OUTPUT_FORMATTER1].val = output_sel; + decoder->tvp514x_regs[REG_INPUT_SEL].val = input_sel; + decoder->tvp514x_regs[REG_OUTPUT_FORMATTER1].val = output_sel; /* Clear status */ msleep(LOCK_RETRY_DELAY); @@ -719,10 +725,9 @@ ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl) switch (qctrl->id) { case V4L2_CID_BRIGHTNESS: - /* Brightness supported is same as standard one (0-255), - * so make use of standard API provided. + /* Brightness supported is (0-255), */ - err = v4l2_ctrl_query_fill_std(qctrl); + err = v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128); break; case V4L2_CID_CONTRAST: case V4L2_CID_SATURATION: @@ -779,16 +784,16 @@ ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - ctrl->value = tvp514x_reg_list[REG_BRIGHTNESS].val; + ctrl->value = decoder->tvp514x_regs[REG_BRIGHTNESS].val; break; case V4L2_CID_CONTRAST: - ctrl->value = tvp514x_reg_list[REG_CONTRAST].val; + ctrl->value = decoder->tvp514x_regs[REG_CONTRAST].val; break; case V4L2_CID_SATURATION: - ctrl->value = tvp514x_reg_list[REG_SATURATION].val; + ctrl->value = decoder->tvp514x_regs[REG_SATURATION].val; break; case V4L2_CID_HUE: - ctrl->value = tvp514x_reg_list[REG_HUE].val; + ctrl->value = decoder->tvp514x_regs[REG_HUE].val; if (ctrl->value == 0x7F) ctrl->value = 180; else if (ctrl->value == 0x80) @@ -798,7 +803,7 @@ ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) break; case V4L2_CID_AUTOGAIN: - ctrl->value = tvp514x_reg_list[REG_AFE_GAIN_CTRL].val; + ctrl->value = decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val; if ((ctrl->value & 0x3) == 3) ctrl->value = 1; else @@ -848,7 +853,7 @@ ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) value); if (err) return err; - tvp514x_reg_list[REG_BRIGHTNESS].val = value; + decoder->tvp514x_regs[REG_BRIGHTNESS].val = value; break; case V4L2_CID_CONTRAST: if (ctrl->value < 0 || ctrl->value > 255) { @@ -861,7 +866,7 @@ ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) value); if (err) return err; - tvp514x_reg_list[REG_CONTRAST].val = value; + decoder->tvp514x_regs[REG_CONTRAST].val = value; break; case V4L2_CID_SATURATION: if (ctrl->value < 0 || ctrl->value > 255) { @@ -874,7 +879,7 @@ ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) value); if (err) return err; - tvp514x_reg_list[REG_SATURATION].val = value; + decoder->tvp514x_regs[REG_SATURATION].val = value; break; case V4L2_CID_HUE: if (value == 180) @@ -893,7 +898,7 @@ ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) value); if (err) return err; - tvp514x_reg_list[REG_HUE].val = value; + decoder->tvp514x_regs[REG_HUE].val = value; break; case V4L2_CID_AUTOGAIN: if (value == 1) @@ -910,7 +915,7 @@ ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) value); if (err) return err; - tvp514x_reg_list[REG_AFE_GAIN_CTRL].val = value; + decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val = value; break; default: v4l_err(decoder->client, @@ -1275,7 +1280,7 @@ static int ioctl_init(struct v4l2_int_device *s) struct tvp514x_decoder *decoder = s->priv; /* Set default standard to auto */ - tvp514x_reg_list[REG_VIDEO_STD].val = + decoder->tvp514x_regs[REG_VIDEO_STD].val = VIDEO_STD_AUTO_SWITCH_BIT; return tvp514x_configure(decoder); @@ -1344,11 +1349,6 @@ static struct v4l2_int_ioctl_desc tvp514x_ioctl_desc[] = { (v4l2_int_ioctl_func *) ioctl_s_routing}, }; -static struct v4l2_int_slave tvp514x_slave = { - .ioctls = tvp514x_ioctl_desc, - .num_ioctls = ARRAY_SIZE(tvp514x_ioctl_desc), -}; - static struct tvp514x_decoder tvp514x_dev = { .state = STATE_NOT_DETECTED, @@ -1369,17 +1369,15 @@ static struct tvp514x_decoder tvp514x_dev = { .current_std = STD_NTSC_MJ, .std_list = tvp514x_std_list, .num_stds = ARRAY_SIZE(tvp514x_std_list), - -}; - -static struct v4l2_int_device tvp514x_int_device = { - .module = THIS_MODULE, - .name = TVP514X_MODULE_NAME, - .priv = &tvp514x_dev, - .type = v4l2_int_type_slave, - .u = { - .slave = &tvp514x_slave, - }, + .v4l2_int_device = { + .module = THIS_MODULE, + .name = TVP514X_MODULE_NAME, + .type = v4l2_int_type_slave, + }, + .tvp514x_slave = { + .ioctls = tvp514x_ioctl_desc, + .num_ioctls = ARRAY_SIZE(tvp514x_ioctl_desc), + }, }; /** @@ -1392,26 +1390,37 @@ static struct v4l2_int_device tvp514x_int_device = { static int tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct tvp514x_decoder *decoder = &tvp514x_dev; + struct tvp514x_decoder *decoder; int err; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; - decoder->pdata = client->dev.platform_data; - if (!decoder->pdata) { + decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); + if (!decoder) + return -ENOMEM; + + if (!client->dev.platform_data) { v4l_err(client, "No platform data!!\n"); - return -ENODEV; + err = -ENODEV; + goto out_free; } + + *decoder = tvp514x_dev; + decoder->v4l2_int_device.priv = decoder; + decoder->pdata = client->dev.platform_data; + decoder->v4l2_int_device.u.slave = &decoder->tvp514x_slave; + memcpy(decoder->tvp514x_regs, tvp514x_reg_list_default, + sizeof(tvp514x_reg_list_default)); /* * Fetch platform specific data, and configure the * tvp514x_reg_list[] accordingly. Since this is one * time configuration, no need to preserve. */ - tvp514x_reg_list[REG_OUTPUT_FORMATTER2].val |= + decoder->tvp514x_regs[REG_OUTPUT_FORMATTER2].val |= (decoder->pdata->clk_polarity << 1); - tvp514x_reg_list[REG_SYNC_CONTROL].val |= + decoder->tvp514x_regs[REG_SYNC_CONTROL].val |= ((decoder->pdata->hs_polarity << 2) | (decoder->pdata->vs_polarity << 3)); /* @@ -1419,23 +1428,27 @@ tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) */ decoder->id = (struct i2c_device_id *)id; /* Attach to Master */ - strcpy(tvp514x_int_device.u.slave->attach_to, decoder->pdata->master); - decoder->v4l2_int_device = &tvp514x_int_device; + strcpy(decoder->v4l2_int_device.u.slave->attach_to, + decoder->pdata->master); decoder->client = client; i2c_set_clientdata(client, decoder); /* Register with V4L2 layer as slave device */ - err = v4l2_int_device_register(decoder->v4l2_int_device); + err = v4l2_int_device_register(&decoder->v4l2_int_device); if (err) { i2c_set_clientdata(client, NULL); v4l_err(client, "Unable to register to v4l2. Err[%d]\n", err); + goto out_free; } else v4l_info(client, "Registered to v4l2 master %s!!\n", decoder->pdata->master); - return 0; + +out_free: + kfree(decoder); + return err; } /** @@ -1452,9 +1465,9 @@ static int __exit tvp514x_remove(struct i2c_client *client) if (!client->adapter) return -ENODEV; /* our client isn't attached */ - v4l2_int_device_unregister(decoder->v4l2_int_device); + v4l2_int_device_unregister(&decoder->v4l2_int_device); i2c_set_clientdata(client, NULL); - + kfree(decoder); return 0; } /* diff --git a/linux/drivers/media/video/tw9910.c b/linux/drivers/media/video/tw9910.c index 4593cdea7..ed8dca3bf 100644 --- a/linux/drivers/media/video/tw9910.c +++ b/linux/drivers/media/video/tw9910.c @@ -460,9 +460,11 @@ static int tw9910_mask_set(struct i2c_client *client, u8 command, u8 mask, u8 set) { s32 val = i2c_smbus_read_byte_data(client, command); + if (val < 0) + return val; val &= ~mask; - val |= set; + val |= set & mask; return i2c_smbus_write_byte_data(client, command, val); } diff --git a/linux/drivers/media/video/usbvision/usbvision-core.c b/linux/drivers/media/video/usbvision/usbvision-core.c index 440f1b05a..412149bbe 100644 --- a/linux/drivers/media/video/usbvision/usbvision-core.c +++ b/linux/drivers/media/video/usbvision/usbvision-core.c @@ -2654,7 +2654,7 @@ int usbvision_muxsel(struct usb_usbvision *usbvision, int channel) } route.input = mode[channel]; route.output = 0; - call_i2c_clients(usbvision, VIDIOC_INT_S_VIDEO_ROUTING,&route); + call_all(usbvision, video, s_routing, &route); usbvision_set_audio(usbvision, audio[channel]); return 0; } diff --git a/linux/drivers/media/video/usbvision/usbvision-i2c.c b/linux/drivers/media/video/usbvision/usbvision-i2c.c index 4c74861f4..addc1716e 100644 --- a/linux/drivers/media/video/usbvision/usbvision-i2c.c +++ b/linux/drivers/media/video/usbvision/usbvision-i2c.c @@ -206,72 +206,78 @@ static struct i2c_algorithm usbvision_algo = { }; -/* - * registering functions to load algorithms at runtime - */ -static int usbvision_i2c_usb_add_bus(struct i2c_adapter *adap) -{ - PDEBUG(DBG_I2C, "I2C debugging is enabled [i2c]"); - PDEBUG(DBG_I2C, "ALGO debugging is enabled [i2c]"); - - /* register new adapter to i2c module... */ - - adap->algo = &usbvision_algo; - - adap->timeout = 100; /* default values, should */ - adap->retries = 3; /* be replaced by defines */ - - i2c_add_adapter(adap); - - PDEBUG(DBG_I2C,"i2c bus for %s registered", adap->name); - - return 0; -} - /* ----------------------------------------------------------------------- */ /* usbvision specific I2C functions */ /* ----------------------------------------------------------------------- */ static struct i2c_adapter i2c_adap_template; -static struct i2c_client i2c_client_template; int usbvision_i2c_register(struct usb_usbvision *usbvision) { + static unsigned short saa711x_addrs[] = { + 0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */ + 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */ + I2C_CLIENT_END }; + memcpy(&usbvision->i2c_adap, &i2c_adap_template, sizeof(struct i2c_adapter)); - memcpy(&usbvision->i2c_client, &i2c_client_template, - sizeof(struct i2c_client)); sprintf(usbvision->i2c_adap.name + strlen(usbvision->i2c_adap.name), " #%d", usbvision->vdev->num); PDEBUG(DBG_I2C,"Adaptername: %s", usbvision->i2c_adap.name); usbvision->i2c_adap.dev.parent = &usbvision->dev->dev; - i2c_set_adapdata(&usbvision->i2c_adap, usbvision); - i2c_set_clientdata(&usbvision->i2c_client, usbvision); - - usbvision->i2c_client.adapter = &usbvision->i2c_adap; + i2c_set_adapdata(&usbvision->i2c_adap, &usbvision->v4l2_dev); if (usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_IIC_LRNACK) < 0) { printk(KERN_ERR "usbvision_register: can't write reg\n"); return -EBUSY; } -#ifdef CONFIG_MODULES + PDEBUG(DBG_I2C, "I2C debugging is enabled [i2c]"); + PDEBUG(DBG_I2C, "ALGO debugging is enabled [i2c]"); + + /* register new adapter to i2c module... */ + + usbvision->i2c_adap.algo = &usbvision_algo; + + usbvision->i2c_adap.timeout = 100; /* default values, should */ + usbvision->i2c_adap.retries = 3; /* be replaced by defines */ + + i2c_add_adapter(&usbvision->i2c_adap); + + PDEBUG(DBG_I2C, "i2c bus for %s registered", usbvision->i2c_adap.name); + /* Request the load of the i2c modules we need */ switch (usbvision_device_data[usbvision->DevModel].Codec) { case CODEC_SAA7113: - request_module("saa7115"); - break; case CODEC_SAA7111: - request_module("saa7115"); + v4l2_i2c_new_probed_subdev(&usbvision->i2c_adap, "saa7115", + "saa7115_auto", saa711x_addrs); break; } if (usbvision_device_data[usbvision->DevModel].Tuner == 1) { - request_module("tuner"); + struct v4l2_subdev *sd; + enum v4l2_i2c_tuner_type type; + struct tuner_setup tun_setup; + + sd = v4l2_i2c_new_probed_subdev(&usbvision->i2c_adap, "tuner", + "tuner", v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + /* depending on whether we found a demod or not, select + the tuner type. */ + type = sd ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; + + sd = v4l2_i2c_new_probed_subdev(&usbvision->i2c_adap, "tuner", + "tuner", v4l2_i2c_tuner_addrs(type)); + + if (usbvision->tuner_type != -1) { + tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; + tun_setup.type = usbvision->tuner_type; + tun_setup.addr = v4l2_i2c_subdev_addr(sd); + call_all(usbvision, tuner, s_type_addr, &tun_setup); + } } -#endif - return usbvision_i2c_usb_add_bus(&usbvision->i2c_adap); + return 0; } int usbvision_i2c_unregister(struct usb_usbvision *usbvision) @@ -284,67 +290,6 @@ int usbvision_i2c_unregister(struct usb_usbvision *usbvision) return 0; } -void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd, - void *arg) -{ - i2c_clients_command(&usbvision->i2c_adap, cmd, arg); -} - -static int attach_inform(struct i2c_client *client) -{ - struct usb_usbvision *usbvision; - - usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter); - - switch (client->addr << 1) { - case 0x42 << 1: - case 0x43 << 1: - case 0x4a << 1: - case 0x4b << 1: - PDEBUG(DBG_I2C,"attach_inform: tda9887 detected."); - break; - case 0x42: - PDEBUG(DBG_I2C,"attach_inform: saa7114 detected."); - break; - case 0x4a: - PDEBUG(DBG_I2C,"attach_inform: saa7113 detected."); - break; - case 0x48: - PDEBUG(DBG_I2C,"attach_inform: saa7111 detected."); - break; - case 0xa0: - PDEBUG(DBG_I2C,"attach_inform: eeprom detected."); - break; - - default: - { - struct tuner_setup tun_setup; - - PDEBUG(DBG_I2C,"attach inform: detected I2C address %x", client->addr << 1); - usbvision->tuner_addr = client->addr; - - if ((usbvision->have_tuner) && (usbvision->tuner_type != -1)) { - tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; - tun_setup.type = usbvision->tuner_type; - tun_setup.addr = usbvision->tuner_addr; - call_i2c_clients(usbvision, TUNER_SET_TYPE_ADDR, &tun_setup); - } - } - break; - } - return 0; -} - -static int detach_inform(struct i2c_client *client) -{ - struct usb_usbvision *usbvision; - - usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter); - - PDEBUG(DBG_I2C,"usbvision[%d] detaches %s", usbvision->nr, client->name); - return 0; -} - static int usbvision_i2c_read_max4(struct usb_usbvision *usbvision, unsigned char addr, char *buf, short len) @@ -517,14 +462,6 @@ static int usbvision_i2c_read(struct usb_usbvision *usbvision, unsigned char add static struct i2c_adapter i2c_adap_template = { .owner = THIS_MODULE, .name = "usbvision", - .id = I2C_HW_B_BT848, /* FIXME */ - .client_register = attach_inform, - .client_unregister = detach_inform, - .class = I2C_CLASS_TV_ANALOG, -}; - -static struct i2c_client i2c_client_template = { - .name = "usbvision internal", }; /* diff --git a/linux/drivers/media/video/usbvision/usbvision-video.c b/linux/drivers/media/video/usbvision/usbvision-video.c index 334c77d91..cfa184d63 100644 --- a/linux/drivers/media/video/usbvision/usbvision-video.c +++ b/linux/drivers/media/video/usbvision/usbvision-video.c @@ -212,7 +212,7 @@ static ssize_t show_hue(struct device *cd, ctrl.id = V4L2_CID_HUE; ctrl.value = 0; if(usbvision->user) - call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl); + call_all(usbvision, core, g_ctrl, &ctrl); return sprintf(buf, "%d\n", ctrl.value); } static DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL); @@ -227,7 +227,7 @@ static ssize_t show_contrast(struct device *cd, ctrl.id = V4L2_CID_CONTRAST; ctrl.value = 0; if(usbvision->user) - call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl); + call_all(usbvision, core, g_ctrl, &ctrl); return sprintf(buf, "%d\n", ctrl.value); } static DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL); @@ -242,7 +242,7 @@ static ssize_t show_brightness(struct device *cd, ctrl.id = V4L2_CID_BRIGHTNESS; ctrl.value = 0; if(usbvision->user) - call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl); + call_all(usbvision, core, g_ctrl, &ctrl); return sprintf(buf, "%d\n", ctrl.value); } static DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL); @@ -257,7 +257,7 @@ static ssize_t show_saturation(struct device *cd, ctrl.id = V4L2_CID_SATURATION; ctrl.value = 0; if(usbvision->user) - call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl); + call_all(usbvision, core, g_ctrl, &ctrl); return sprintf(buf, "%d\n", ctrl.value); } static DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL); @@ -622,8 +622,7 @@ static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id) usbvision->tvnormId=*id; mutex_lock(&usbvision->lock); - call_i2c_clients(usbvision, VIDIOC_S_STD, - &usbvision->tvnormId); + call_all(usbvision, tuner, s_std, usbvision->tvnormId); mutex_unlock(&usbvision->lock); /* propagate the change to the decoder */ usbvision_muxsel(usbvision, usbvision->ctl_input); @@ -645,7 +644,7 @@ static int vidioc_g_tuner (struct file *file, void *priv, strcpy(vt->name, "Television"); } /* Let clients fill in the remainder of this struct */ - call_i2c_clients(usbvision,VIDIOC_G_TUNER,vt); + call_all(usbvision, tuner, g_tuner, vt); return 0; } @@ -659,7 +658,7 @@ static int vidioc_s_tuner (struct file *file, void *priv, if (!usbvision->have_tuner || vt->index) return -EINVAL; /* let clients handle this */ - call_i2c_clients(usbvision,VIDIOC_S_TUNER,vt); + call_all(usbvision, tuner, s_tuner, vt); return 0; } @@ -690,7 +689,7 @@ static int vidioc_s_frequency (struct file *file, void *priv, return -EINVAL; usbvision->freq = freq->frequency; - call_i2c_clients(usbvision, VIDIOC_S_FREQUENCY, freq); + call_all(usbvision, tuner, s_frequency, freq); return 0; } @@ -728,7 +727,7 @@ static int vidioc_queryctrl (struct file *file, void *priv, memset(ctrl,0,sizeof(*ctrl)); ctrl->id=id; - call_i2c_clients(usbvision, VIDIOC_QUERYCTRL, ctrl); + call_all(usbvision, core, queryctrl, ctrl); if (!ctrl->type) return -EINVAL; @@ -740,7 +739,7 @@ static int vidioc_g_ctrl (struct file *file, void *priv, struct v4l2_control *ctrl) { struct usb_usbvision *usbvision = video_drvdata(file); - call_i2c_clients(usbvision, VIDIOC_G_CTRL, ctrl); + call_all(usbvision, core, g_ctrl, ctrl); return 0; } @@ -749,7 +748,7 @@ static int vidioc_s_ctrl (struct file *file, void *priv, struct v4l2_control *ctrl) { struct usb_usbvision *usbvision = video_drvdata(file); - call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl); + call_all(usbvision, core, s_ctrl, ctrl); return 0; } @@ -900,10 +899,9 @@ static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *vb) static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { struct usb_usbvision *usbvision = video_drvdata(file); - int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; usbvision->streaming = Stream_On; - call_i2c_clients(usbvision,VIDIOC_STREAMON , &b); + call_all(usbvision, video, s_stream, 1); return 0; } @@ -912,7 +910,6 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { struct usb_usbvision *usbvision = video_drvdata(file); - int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -920,7 +917,7 @@ static int vidioc_streamoff(struct file *file, if(usbvision->streaming == Stream_On) { usbvision_stream_interrupt(usbvision); /* Stop all video streamings */ - call_i2c_clients(usbvision,VIDIOC_STREAMOFF , &b); + call_all(usbvision, video, s_stream, 0); } usbvision_empty_framequeues(usbvision); @@ -1043,7 +1040,7 @@ static ssize_t usbvision_v4l2_read(struct file *file, char __user *buf, if(usbvision->streaming != Stream_On) { /* no stream is running, make it running ! */ usbvision->streaming = Stream_On; - call_i2c_clients(usbvision,VIDIOC_STREAMON , NULL); + call_all(usbvision, video, s_stream, 1); } /* Then, enqueue as many frames as possible @@ -1214,7 +1211,7 @@ static int usbvision_radio_open(struct file *file) // If so far no errors then we shall start the radio usbvision->radio = 1; - call_i2c_clients(usbvision,AUDC_SET_RADIO,&usbvision->tuner_type); + call_all(usbvision, tuner, s_radio); usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO); usbvision->user++; } @@ -1427,7 +1424,7 @@ static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision, } *vdev = *vdev_template; // vdev->minor = -1; - vdev->parent = &usb_dev->dev; + vdev->v4l2_dev = &usbvision->v4l2_dev; snprintf(vdev->name, sizeof(vdev->name), "%s", name); video_set_drvdata(vdev, usbvision); return vdev; @@ -1548,33 +1545,30 @@ static struct usb_usbvision *usbvision_alloc(struct usb_device *dev) { struct usb_usbvision *usbvision; - if ((usbvision = kzalloc(sizeof(struct usb_usbvision), GFP_KERNEL)) == - NULL) { - goto err_exit; - } + usbvision = kzalloc(sizeof(struct usb_usbvision), GFP_KERNEL); + if (usbvision == NULL) + return NULL; usbvision->dev = dev; + if (v4l2_device_register(&dev->dev, &usbvision->v4l2_dev)) + goto err_free; mutex_init(&usbvision->lock); /* available */ // prepare control urb for control messages during interrupts usbvision->ctrlUrb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL); - if (usbvision->ctrlUrb == NULL) { - goto err_exit; - } + if (usbvision->ctrlUrb == NULL) + goto err_unreg; init_waitqueue_head(&usbvision->ctrlUrb_wq); usbvision_init_powerOffTimer(usbvision); return usbvision; -err_exit: - if (usbvision && usbvision->ctrlUrb) { - usb_free_urb(usbvision->ctrlUrb); - } - if (usbvision) { - kfree(usbvision); - } +err_unreg: + v4l2_device_unregister(&usbvision->v4l2_dev); +err_free: + kfree(usbvision); return NULL; } @@ -1604,6 +1598,7 @@ static void usbvision_release(struct usb_usbvision *usbvision) usb_free_urb(usbvision->ctrlUrb); } + v4l2_device_unregister(&usbvision->v4l2_dev); kfree(usbvision); PDEBUG(DBG_PROBE, "success"); @@ -1739,8 +1734,6 @@ static int __devinit usbvision_probe(struct usb_interface *intf, usbvision->tuner_type = usbvision_device_data[model].TunerType; } - usbvision->tuner_addr = ADDR_UNSET; - usbvision->DevModel = model; usbvision->remove_pending = 0; usbvision->iface = ifnum; diff --git a/linux/drivers/media/video/usbvision/usbvision.h b/linux/drivers/media/video/usbvision/usbvision.h index 590ff1e19..dc86c2139 100644 --- a/linux/drivers/media/video/usbvision/usbvision.h +++ b/linux/drivers/media/video/usbvision/usbvision.h @@ -35,7 +35,7 @@ #include <linux/usb.h> #include <linux/i2c.h> #include <linux/mutex.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/tuner.h> #include <linux/videodev2.h> #include "compat.h" @@ -358,13 +358,13 @@ extern struct usbvision_device_data_st usbvision_device_data[]; extern struct usb_device_id usbvision_table[]; struct usb_usbvision { + struct v4l2_device v4l2_dev; struct video_device *vdev; /* Video Device */ struct video_device *rdev; /* Radio Device */ struct video_device *vbi; /* VBI Device */ /* i2c Declaration Section*/ struct i2c_adapter i2c_adap; - struct i2c_client i2c_client; struct urb *ctrlUrb; unsigned char ctrlUrbBuffer[8]; @@ -375,7 +375,6 @@ struct usb_usbvision { /* configuration part */ int have_tuner; int tuner_type; - int tuner_addr; int bridgeType; // NT1003, NT1004, NT1005 int radio; int video_inputs; // # of inputs @@ -465,6 +464,8 @@ struct usb_usbvision { int ComprBlockTypes[4]; }; +#define call_all(usbvision, o, f, args...) \ + v4l2_device_call_all(&usbvision->v4l2_dev, 0, o, f, ##args) /* --------------------------------------------------------------- */ /* defined in usbvision-i2c.c */ @@ -476,7 +477,6 @@ struct usb_usbvision { /* ----------------------------------------------------------------------- */ int usbvision_i2c_register(struct usb_usbvision *usbvision); int usbvision_i2c_unregister(struct usb_usbvision *usbvision); -void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd,void *arg); /* defined in usbvision-core.c */ int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg); diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c index 2a41eb418..0d2d87198 100644 --- a/linux/drivers/media/video/uvc/uvc_driver.c +++ b/linux/drivers/media/video/uvc/uvc_driver.c @@ -1863,6 +1863,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* ViMicro */ + { .match_flags = USB_DEVICE_ID_MATCH_VENDOR + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0ac8, + .idProduct = 0x0000, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, /* MT6227 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, diff --git a/linux/drivers/media/video/uvc/uvc_video.c b/linux/drivers/media/video/uvc/uvc_video.c index 04c791213..82a9999b6 100644 --- a/linux/drivers/media/video/uvc/uvc_video.c +++ b/linux/drivers/media/video/uvc/uvc_video.c @@ -61,7 +61,7 @@ int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, return 0; } -static void uvc_fixup_buffer_size(struct uvc_video_device *video, +static void uvc_fixup_video_ctrl(struct uvc_video_device *video, struct uvc_streaming_control *ctrl) { struct uvc_format *format; @@ -84,6 +84,31 @@ static void uvc_fixup_buffer_size(struct uvc_video_device *video, video->dev->uvc_version < 0x0110)) ctrl->dwMaxVideoFrameSize = frame->dwMaxVideoFrameBufferSize; + + if (video->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH && + video->streaming->intf->num_altsetting > 1) { + u32 interval; + u32 bandwidth; + + interval = (ctrl->dwFrameInterval > 100000) + ? ctrl->dwFrameInterval + : frame->dwFrameInterval[0]; + + /* Compute a bandwidth estimation by multiplying the frame + * size by the number of video frames per second, divide the + * result by the number of USB frames (or micro-frames for + * high-speed devices) per second and add the UVC header size + * (assumed to be 12 bytes long). + */ + bandwidth = frame->wWidth * frame->wHeight / 8 * format->bpp; + bandwidth *= 10000000 / interval + 1; + bandwidth /= 1000; + if (video->dev->udev->speed == USB_SPEED_HIGH) + bandwidth /= 8; + bandwidth += 12; + + ctrl->dwMaxPayloadTransferSize = bandwidth; + } } static int uvc_get_video_ctrl(struct uvc_video_device *video, @@ -158,10 +183,11 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, ctrl->bMaxVersion = 0; } - /* Some broken devices return a null or wrong dwMaxVideoFrameSize. - * Try to get the value from the format and frame descriptors. + /* Some broken devices return null or wrong dwMaxVideoFrameSize and + * dwMaxPayloadTransferSize fields. Try to get the value from the + * format and frame descriptors. */ - uvc_fixup_buffer_size(video, ctrl); + uvc_fixup_video_ctrl(video, ctrl); ret = 0; out: @@ -540,6 +566,9 @@ static void uvc_video_decode_bulk(struct urb *urb, u8 *mem; int len, ret; + if (urb->actual_length == 0) + return; + mem = urb->transfer_buffer; len = urb->actual_length; video->bulk.payload_size += len; @@ -1030,11 +1059,20 @@ int uvc_video_init(struct uvc_video_device *video) */ usb_set_interface(video->dev->udev, video->streaming->intfnum, 0); - /* Some webcams don't suport GET_DEF requests on the probe control. We - * fall back to GET_CUR if GET_DEF fails. + /* Set the streaming probe control with default streaming parameters + * retrieved from the device. Webcams that don't suport GET_DEF + * requests on the probe control will just keep their current streaming + * parameters. + */ + if (uvc_get_video_ctrl(video, probe, 1, GET_DEF) == 0) + uvc_set_video_ctrl(video, probe, 1); + + /* Initialize the streaming parameters with the probe control current + * value. This makes sure SET_CUR requests on the streaming commit + * control will always use values retrieved from a successful GET_CUR + * request on the probe control, as required by the UVC specification. */ - if ((ret = uvc_get_video_ctrl(video, probe, 1, GET_DEF)) < 0 && - (ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR)) < 0) + if ((ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR)) < 0) return ret; /* Check if the default format descriptor exists. Use the first diff --git a/linux/drivers/media/video/uvc/uvcvideo.h b/linux/drivers/media/video/uvc/uvcvideo.h index 408b8b846..53d5c9e0c 100644 --- a/linux/drivers/media/video/uvc/uvcvideo.h +++ b/linux/drivers/media/video/uvc/uvcvideo.h @@ -315,6 +315,7 @@ struct uvc_xu_control { #define UVC_QUIRK_STREAM_NO_FID 0x00000010 #define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020 #define UVC_QUIRK_PRUNE_CONTROLS 0x00000040 +#define UVC_QUIRK_FIX_BANDWIDTH 0x00000080 /* Format flags */ #define UVC_FMT_FLAG_COMPRESSED 0x00000001 diff --git a/linux/drivers/media/video/v4l2-common.c b/linux/drivers/media/video/v4l2-common.c index 8ae8dd366..bc5b40012 100644 --- a/linux/drivers/media/video/v4l2-common.c +++ b/linux/drivers/media/video/v4l2-common.c @@ -335,6 +335,12 @@ const char **v4l2_ctrl_get_menu(u32 id) "Aperture Priority Mode", NULL }; + static const char *colorfx[] = { + "None", + "Black & White", + "Sepia", + NULL + }; switch (id) { case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: @@ -371,6 +377,8 @@ const char **v4l2_ctrl_get_menu(u32 id) return camera_power_line_frequency; case V4L2_CID_EXPOSURE_AUTO: return camera_exposure_auto; + case V4L2_CID_COLORFX: + return colorfx; default: return NULL; } @@ -383,16 +391,16 @@ const char *v4l2_ctrl_get_name(u32 id) switch (id) { /* USER controls */ case V4L2_CID_USER_CLASS: return "User Controls"; + case V4L2_CID_BRIGHTNESS: return "Brightness"; + case V4L2_CID_CONTRAST: return "Contrast"; + case V4L2_CID_SATURATION: return "Saturation"; + case V4L2_CID_HUE: return "Hue"; case V4L2_CID_AUDIO_VOLUME: return "Volume"; - case V4L2_CID_AUDIO_MUTE: return "Mute"; case V4L2_CID_AUDIO_BALANCE: return "Balance"; case V4L2_CID_AUDIO_BASS: return "Bass"; case V4L2_CID_AUDIO_TREBLE: return "Treble"; + case V4L2_CID_AUDIO_MUTE: return "Mute"; case V4L2_CID_AUDIO_LOUDNESS: return "Loudness"; - case V4L2_CID_BRIGHTNESS: return "Brightness"; - case V4L2_CID_CONTRAST: return "Contrast"; - case V4L2_CID_SATURATION: return "Saturation"; - case V4L2_CID_HUE: return "Hue"; case V4L2_CID_BLACK_LEVEL: return "Black Level"; case V4L2_CID_AUTO_WHITE_BALANCE: return "White Balance, Automatic"; case V4L2_CID_DO_WHITE_BALANCE: return "Do White Balance"; @@ -413,6 +421,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_BACKLIGHT_COMPENSATION: return "Backlight Compensation"; case V4L2_CID_CHROMA_AGC: return "Chroma AGC"; case V4L2_CID_COLOR_KILLER: return "Color Killer"; + case V4L2_CID_COLORFX: return "Color Effects"; /* MPEG controls */ case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls"; @@ -491,16 +500,25 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_HFLIP: case V4L2_CID_VFLIP: case V4L2_CID_HUE_AUTO: + case V4L2_CID_CHROMA_AGC: + case V4L2_CID_COLOR_KILLER: case V4L2_CID_MPEG_AUDIO_MUTE: case V4L2_CID_MPEG_VIDEO_MUTE: case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: case V4L2_CID_MPEG_VIDEO_PULLDOWN: case V4L2_CID_EXPOSURE_AUTO_PRIORITY: + case V4L2_CID_FOCUS_AUTO: case V4L2_CID_PRIVACY: qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; min = 0; max = step = 1; break; + case V4L2_CID_PAN_RESET: + case V4L2_CID_TILT_RESET: + qctrl->type = V4L2_CTRL_TYPE_BUTTON; + qctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY; + min = max = step = def = 0; + break; case V4L2_CID_POWER_LINE_FREQUENCY: case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: case V4L2_CID_MPEG_AUDIO_ENCODING: @@ -518,6 +536,7 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_MPEG_STREAM_TYPE: case V4L2_CID_MPEG_STREAM_VBI_FMT: case V4L2_CID_EXPOSURE_AUTO: + case V4L2_CID_COLORFX: qctrl->type = V4L2_CTRL_TYPE_MENU; step = 1; break; @@ -548,8 +567,17 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_CONTRAST: case V4L2_CID_SATURATION: case V4L2_CID_HUE: + case V4L2_CID_RED_BALANCE: + case V4L2_CID_BLUE_BALANCE: + case V4L2_CID_GAMMA: qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; break; + case V4L2_CID_PAN_RELATIVE: + case V4L2_CID_TILT_RELATIVE: + case V4L2_CID_FOCUS_RELATIVE: + case V4L2_CID_ZOOM_RELATIVE: + qctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY; + break; } qctrl->minimum = min; qctrl->maximum = max; @@ -561,148 +589,9 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste } EXPORT_SYMBOL(v4l2_ctrl_query_fill); -/* Fill in a struct v4l2_queryctrl with standard values based on - the control ID. */ -int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl) -{ - switch (qctrl->id) { - /* USER controls */ - case V4L2_CID_USER_CLASS: - case V4L2_CID_MPEG_CLASS: - return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0); - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qctrl, 0, 65535, 65535 / 100, 58880); - case V4L2_CID_AUDIO_MUTE: - case V4L2_CID_AUDIO_LOUDNESS: - return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill(qctrl, 0, 65535, 65535 / 100, 32768); - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128); - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(qctrl, 0, 127, 1, 64); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qctrl, -128, 127, 1, 0); - - /* MPEG controls */ - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 1, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); - case V4L2_CID_MPEG_AUDIO_ENCODING: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_ENCODING_LAYER_1, - V4L2_MPEG_AUDIO_ENCODING_AC3, 1, - V4L2_MPEG_AUDIO_ENCODING_LAYER_2); - case V4L2_CID_MPEG_AUDIO_L1_BITRATE: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_L1_BITRATE_32K, - V4L2_MPEG_AUDIO_L1_BITRATE_448K, 1, - V4L2_MPEG_AUDIO_L1_BITRATE_256K); - case V4L2_CID_MPEG_AUDIO_L2_BITRATE: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_L2_BITRATE_32K, - V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1, - V4L2_MPEG_AUDIO_L2_BITRATE_224K); - case V4L2_CID_MPEG_AUDIO_L3_BITRATE: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_L3_BITRATE_32K, - V4L2_MPEG_AUDIO_L3_BITRATE_320K, 1, - V4L2_MPEG_AUDIO_L3_BITRATE_192K); - case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: - return v4l2_ctrl_query_fill(qctrl, 0, 6400, 1, 3200000); - case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_AC3_BITRATE_32K, - V4L2_MPEG_AUDIO_AC3_BITRATE_640K, 1, - V4L2_MPEG_AUDIO_AC3_BITRATE_384K); - case V4L2_CID_MPEG_AUDIO_MODE: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_MODE_STEREO, - V4L2_MPEG_AUDIO_MODE_MONO, 1, - V4L2_MPEG_AUDIO_MODE_STEREO); - case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 1, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4); - case V4L2_CID_MPEG_AUDIO_EMPHASIS: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_EMPHASIS_NONE, - V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 1, - V4L2_MPEG_AUDIO_EMPHASIS_NONE); - case V4L2_CID_MPEG_AUDIO_CRC: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_CRC_NONE, - V4L2_MPEG_AUDIO_CRC_CRC16, 1, - V4L2_MPEG_AUDIO_CRC_NONE); - case V4L2_CID_MPEG_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); - case V4L2_CID_MPEG_VIDEO_ENCODING: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_VIDEO_ENCODING_MPEG_1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2); - case V4L2_CID_MPEG_VIDEO_ASPECT: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_VIDEO_ASPECT_1x1, - V4L2_MPEG_VIDEO_ASPECT_221x100, 1, - V4L2_MPEG_VIDEO_ASPECT_4x3); - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - return v4l2_ctrl_query_fill(qctrl, 0, 33, 1, 2); - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - return v4l2_ctrl_query_fill(qctrl, 1, 34, 1, 12); - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 1); - case V4L2_CID_MPEG_VIDEO_PULLDOWN: - return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1, - V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); - case V4L2_CID_MPEG_VIDEO_BITRATE: - return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000); - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000); - case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: - return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0); - case V4L2_CID_MPEG_VIDEO_MUTE: + case V4L2_CID_CAMERA_CLASS: + case V4L2_CID_COLORFX: return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); - case V4L2_CID_MPEG_VIDEO_MUTE_YUV: /* Init YUV (really YCbCr) to black */ - return v4l2_ctrl_query_fill(qctrl, 0, 0xffffff, 1, 0x008080); - case V4L2_CID_MPEG_STREAM_TYPE: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_STREAM_TYPE_MPEG2_PS, - V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, 1, - V4L2_MPEG_STREAM_TYPE_MPEG2_PS); - case V4L2_CID_MPEG_STREAM_PID_PMT: - return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 16); - case V4L2_CID_MPEG_STREAM_PID_AUDIO: - return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 260); - case V4L2_CID_MPEG_STREAM_PID_VIDEO: - return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 256); - case V4L2_CID_MPEG_STREAM_PID_PCR: - return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 259); - case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: - return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0); - case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: - return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0); - case V4L2_CID_MPEG_STREAM_VBI_FMT: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_STREAM_VBI_FMT_NONE, - V4L2_MPEG_STREAM_VBI_FMT_IVTV, 1, - V4L2_MPEG_STREAM_VBI_FMT_NONE); - default: - return -EINVAL; - } -} -EXPORT_SYMBOL(v4l2_ctrl_query_fill_std); - /* Fill in a struct v4l2_querymenu based on the struct v4l2_queryctrl and the menu. The qctrl pointer may be NULL, in which case it is ignored. If menu_items is NULL, then the menu items are retrieved using @@ -1049,6 +938,15 @@ struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter, } EXPORT_SYMBOL_GPL(v4l2_i2c_new_probed_subdev); +/* Return i2c client address of v4l2_subdev. */ +unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return client ? client->addr : I2C_CLIENT_END; +} +EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_addr); + /* Return a list of I2C tuner addresses to probe. Use only if the tuner addresses are unknown. */ const unsigned short *v4l2_i2c_tuner_addrs(enum v4l2_i2c_tuner_type type) diff --git a/linux/include/linux/videodev2.h b/linux/include/linux/videodev2.h index 6969c3086..4167a0b44 100644 --- a/linux/include/linux/videodev2.h +++ b/linux/include/linux/videodev2.h @@ -830,6 +830,7 @@ struct v4l2_querymenu { #define V4L2_CTRL_FLAG_UPDATE 0x0008 #define V4L2_CTRL_FLAG_INACTIVE 0x0010 #define V4L2_CTRL_FLAG_SLIDER 0x0020 +#define V4L2_CTRL_FLAG_WRITE_ONLY 0x0040 /* Query flag, to be ORed with the control ID */ #define V4L2_CTRL_FLAG_NEXT_CTRL 0x80000000 diff --git a/linux/include/media/ov772x.h b/linux/include/media/ov772x.h index e391d55ed..57db48dd8 100644 --- a/linux/include/media/ov772x.h +++ b/linux/include/media/ov772x.h @@ -13,8 +13,13 @@ #include <media/soc_camera.h> +/* for flags */ +#define OV772X_FLAG_VFLIP 0x00000001 /* Vertical flip image */ +#define OV772X_FLAG_HFLIP 0x00000002 /* Horizontal flip image */ + struct ov772x_camera_info { unsigned long buswidth; + unsigned long flags; struct soc_camera_link link; }; diff --git a/linux/include/media/sh_mobile_ceu.h b/linux/include/media/sh_mobile_ceu.h index b5dbefea3..0f3524cff 100644 --- a/linux/include/media/sh_mobile_ceu.h +++ b/linux/include/media/sh_mobile_ceu.h @@ -1,10 +1,11 @@ #ifndef __ASM_SH_MOBILE_CEU_H__ #define __ASM_SH_MOBILE_CEU_H__ -#include <media/soc_camera.h> +#define SH_CEU_FLAG_USE_8BIT_BUS (1 << 0) /* use 8bit bus width */ +#define SH_CEU_FLAG_USE_16BIT_BUS (1 << 1) /* use 16bit bus width */ struct sh_mobile_ceu_info { - unsigned long flags; /* SOCAM_... */ + unsigned long flags; }; #endif /* __ASM_SH_MOBILE_CEU_H__ */ diff --git a/linux/include/media/soc_camera.h b/linux/include/media/soc_camera.h index 7440d9250..c63a3409f 100644 --- a/linux/include/media/soc_camera.h +++ b/linux/include/media/soc_camera.h @@ -239,15 +239,19 @@ static inline struct v4l2_queryctrl const *soc_camera_find_qctrl( static inline unsigned long soc_camera_bus_param_compatible( unsigned long camera_flags, unsigned long bus_flags) { - unsigned long common_flags, hsync, vsync, pclk; + unsigned long common_flags, hsync, vsync, pclk, data, buswidth, mode; common_flags = camera_flags & bus_flags; hsync = common_flags & (SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW); vsync = common_flags & (SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW); pclk = common_flags & (SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING); + data = common_flags & (SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_LOW); + mode = common_flags & (SOCAM_MASTER | SOCAM_SLAVE); + buswidth = common_flags & SOCAM_DATAWIDTH_MASK; - return (!hsync || !vsync || !pclk) ? 0 : common_flags; + return (!hsync || !vsync || !pclk || !data || !mode || !buswidth) ? 0 : + common_flags; } extern unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl, diff --git a/linux/include/media/v4l2-common.h b/linux/include/media/v4l2-common.h index de785da45..3a6905615 100644 --- a/linux/include/media/v4l2-common.h +++ b/linux/include/media/v4l2-common.h @@ -102,7 +102,6 @@ int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl, const char *v4l2_ctrl_get_name(u32 id); const char **v4l2_ctrl_get_menu(u32 id); int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def); -int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl); int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu, struct v4l2_queryctrl *qctrl, const char **menu_items); #define V4L2_CTRL_MENU_IDS_END (0xffffffff) @@ -154,6 +153,8 @@ struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter, /* Initialize an v4l2_subdev with data from an i2c_client struct */ void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client, const struct v4l2_subdev_ops *ops); +/* Return i2c client address of v4l2_subdev. */ +unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd); enum v4l2_i2c_tuner_type { ADDRS_RADIO, /* Radio tuner addresses */ diff --git a/v4l/Makefile b/v4l/Makefile index da51bb50a..512b355f4 100644 --- a/v4l/Makefile +++ b/v4l/Makefile @@ -48,9 +48,14 @@ default:: config-compat.h Makefile.media links oss firmware:: $(MAKE) -C firmware -spec:: +v4l2-spec:: $(MAKE) -C ../v4l2-spec +dvb-spec:: + $(MAKE) -C ../dvb-spec/dvbapi + +spec:: v4l2-spec dvb-spec + apps:: $(MAKE) -C ../v4l2-apps @@ -288,6 +293,7 @@ clean:: config-compat.h Module.symvers Module.markers modules.order $(MAKE) -C firmware $@ $(MAKE) -C ../v4l2-spec $@ + $(MAKE) -C ../dvb-spec/dvbapi $@ $(MAKE) -C ../v4l2-apps $@ distclean:: clean diff --git a/v4l/scripts/make_config_compat.pl b/v4l/scripts/make_config_compat.pl index d24b5f718..423dcff77 100755 --- a/v4l/scripts/make_config_compat.pl +++ b/v4l/scripts/make_config_compat.pl @@ -277,6 +277,13 @@ open IN, "<$infile" or die "File not found: $infile"; $out.= "#ifndef __CONFIG_COMPAT_H__\n"; $out.= "#define __CONFIG_COMPAT_H__\n\n"; $out.= "#include <linux/autoconf.h>\n\n"; + +# mmdebug.h includes autoconf.h. So if this header exists, +# then include it before our config is set. +if (-f "$kdir/include/linux/mmdebug.h") { + $out.= "#include <linux/mmdebug.h>\n\n"; +} + while(<IN>) { next unless /^(\S+)\s*:= (\S+)$/; $out.= "#undef $1\n"; diff --git a/v4l/versions.txt b/v4l/versions.txt index ac0266047..8435046c5 100644 --- a/v4l/versions.txt +++ b/v4l/versions.txt @@ -37,6 +37,8 @@ USB_SI470X USB_GSPCA_FINEPIX # uses linux/hid.h and struct delayed_work USB_SI470X +# due to INIT_WORK changes +USB_GSPCA_SQ905 [2.6.19] #This driver were developed at kernel 2.6.19, requiring vmalloc_user/remap_vmalloc_range diff --git a/v4l2-apps/test/parse-sniffusb2.pl b/v4l2-apps/util/parse-sniffusb2.pl index 34406b0ea..34406b0ea 100644..100755 --- a/v4l2-apps/test/parse-sniffusb2.pl +++ b/v4l2-apps/util/parse-sniffusb2.pl diff --git a/v4l2-apps/util/v4l2-ctl.cpp b/v4l2-apps/util/v4l2-ctl.cpp index 7d5988571..dec9edd61 100644 --- a/v4l2-apps/util/v4l2-ctl.cpp +++ b/v4l2-apps/util/v4l2-ctl.cpp @@ -129,6 +129,8 @@ enum Option { OptGetOverlayCropCap, OptGetOutputOverlayCropCap, OptOverlay, + OptGetJpegComp, + OptSetJpegComp, OptListDevices, OptLast = 256 }; @@ -256,6 +258,8 @@ static struct option long_options[] = { {"get-cropcap-output-overlay", no_argument, 0, OptGetOutputOverlayCropCap}, {"get-crop-output-overlay", no_argument, 0, OptGetOutputOverlayCrop}, {"set-crop-output-overlay", required_argument, 0, OptSetOutputOverlayCrop}, + {"get-jpeg-comp", no_argument, 0, OptGetJpegComp}, + {"set-jpeg-comp", required_argument, 0, OptSetJpegComp}, {"overlay", required_argument, 0, OptOverlay}, {"list-devices", no_argument, 0, OptListDevices}, {0, 0, 0, 0} @@ -380,6 +384,16 @@ static void usage(void) " query the video output overlay crop window [VIDIOC_G_CROP]\n" " --set-crop-output-overlay=top=<x>,left=<y>,width=<w>,height=<h>\n" " set the video output overlay crop window [VIDIOC_S_CROP]\n" + " --get-jpeg-comp query the JPEG compression [VIDIOC_G_JPEGCOMP]\n" + " --set-jpeg-comp=quality=<q>,markers=<markers>,comment=<c>,app<n>=<a>\n" + " set the JPEG compression [VIDIOC_S_JPEGCOMP]\n" + " <n> is the app segment: 0-9 or a-f, <a> is the actual string.\n" + " <markers> is a colon separated list of:\n" + " dht: Define Huffman Tables\n" + " dqt: Define Quantization Tables\n" + " dri: Define Restart Interval\n" + " --set-audio-output=<num>\n" + " set the audio output to <num> [VIDIOC_S_AUDOUT]\n" " --get-audio-input query the audio input [VIDIOC_G_AUDIO]\n" " --set-audio-input=<num>\n" " set the audio input to <num> [VIDIOC_S_AUDIO]\n" @@ -576,11 +590,12 @@ static void print_qctrl(int fd, struct v4l2_queryctrl *queryctrl, } if (queryctrl->flags) { const flag_def def[] = { - { V4L2_CTRL_FLAG_GRABBED, "grabbed" }, - { V4L2_CTRL_FLAG_READ_ONLY, "readonly" }, - { V4L2_CTRL_FLAG_UPDATE, "update" }, - { V4L2_CTRL_FLAG_INACTIVE, "inactive" }, - { V4L2_CTRL_FLAG_SLIDER, "slider" }, + { V4L2_CTRL_FLAG_GRABBED, "grabbed" }, + { V4L2_CTRL_FLAG_READ_ONLY, "read-only" }, + { V4L2_CTRL_FLAG_UPDATE, "update" }, + { V4L2_CTRL_FLAG_INACTIVE, "inactive" }, + { V4L2_CTRL_FLAG_SLIDER, "slider" }, + { V4L2_CTRL_FLAG_WRITE_ONLY, "write-only" }, { 0, NULL } }; printf(" flags=%s", flags2s(queryctrl->flags, def).c_str()); @@ -750,6 +765,35 @@ static void printfbuf(const struct v4l2_framebuffer &fb) } } +static std::string markers2s(unsigned markers) +{ + std::string s; + + if (markers & V4L2_JPEG_MARKER_DHT) + s += "\t\tDefine Huffman Tables\n"; + if (markers & V4L2_JPEG_MARKER_DQT) + s += "\t\tDefine Quantization Tables\n"; + if (markers & V4L2_JPEG_MARKER_DRI) + s += "\t\tDefine Restart Interval\n"; + if (markers & V4L2_JPEG_MARKER_COM) + s += "\t\tDefine Comment\n"; + if (markers & V4L2_JPEG_MARKER_APP) + s += "\t\tDefine APP segment\n"; + return s; +} + +static void printjpegcomp(const struct v4l2_jpegcompression &jc) +{ + printf("JPEG compression:\n"); + printf("\tQuality: %d\n", jc.quality); + if (jc.COM_len) + printf("\tComment: '%s'\n", jc.COM_data); + if (jc.APP_len) + printf("\tAPP%x : '%s'\n", jc.APPn, jc.APP_data); + printf("\tMarkers: 0x%08lx\n", jc.jpeg_markers); + printf("%s", markers2s(jc.jpeg_markers).c_str()); +} + static void printcrop(const struct v4l2_crop &crop) { printf("Crop: Left %d, Top %d, Width %d, Height %d\n", @@ -1337,6 +1381,7 @@ int main(int argc, char **argv) struct v4l2_rect vcrop_overlay; /* crop rect */ struct v4l2_rect vcrop_out_overlay; /* crop rect */ struct v4l2_framebuffer fbuf; /* fbuf */ + struct v4l2_jpegcompression jpegcomp; /* jpeg compression */ int input; /* set_input/get_input */ int output; /* set_output/get_output */ v4l2_std_id std; /* get_std/set_std */ @@ -1371,6 +1416,7 @@ int main(int argc, char **argv) memset(&vf, 0, sizeof(vf)); memset(&vs, 0, sizeof(vs)); memset(&fbuf, 0, sizeof(fbuf)); + memset(&jpegcomp, 0, sizeof(jpegcomp)); if (argc == 1) { usage(); @@ -1700,6 +1746,62 @@ int main(int argc, char **argv) } break; } + case OptSetJpegComp: + { + subs = optarg; + while (*subs != '\0') { + static const char *const subopts[] = { + "app0", "app1", "app2", "app3", + "app4", "app5", "app6", "app7", + "app8", "app9", "appa", "appb", + "appc", "appd", "appe", "appf", + "quality", + "markers", + "comment", + NULL + }; + int len; + int opt = parse_subopt(&subs, subopts, &value); + + switch (opt) { + case 16: + jpegcomp.quality = strtol(value, 0L, 0); + break; + case 17: + if (strstr(value, "dht")) + jpegcomp.jpeg_markers |= V4L2_JPEG_MARKER_DHT; + if (strstr(value, "dqt")) + jpegcomp.jpeg_markers |= V4L2_JPEG_MARKER_DQT; + if (strstr(value, "dri")) + jpegcomp.jpeg_markers |= V4L2_JPEG_MARKER_DRI; + break; + case 18: + len = strlen(value); + if (len > sizeof(jpegcomp.COM_data) - 1) + len = sizeof(jpegcomp.COM_data) - 1; + jpegcomp.COM_len = len; + memcpy(jpegcomp.COM_data, value, len); + jpegcomp.COM_data[len] = '\0'; + break; + default: + if (opt < 0 || opt > 15) + break; + len = strlen(value); + if (len > sizeof(jpegcomp.APP_data) - 1) + len = sizeof(jpegcomp.APP_data) - 1; + if (jpegcomp.APP_len) { + fprintf(stderr, "Only one APP segment can be set\n"); + break; + } + jpegcomp.APP_len = len; + memcpy(jpegcomp.APP_data, value, len); + jpegcomp.APP_data[len] = '\0'; + jpegcomp.APPn = opt; + break; + } + } + break; + } case OptListDevices: list_devices(); break; @@ -1768,6 +1870,7 @@ int main(int argc, char **argv) options[OptGetFBuf] = 1; options[OptGetCropCap] = 1; options[OptGetOutputCropCap] = 1; + options[OptGetJpegComp] = 1; options[OptSilent] = 1; } @@ -1987,6 +2090,10 @@ set_vid_fmt_error: } } + if (options[OptSetJpegComp]) { + doioctl(fd, VIDIOC_S_JPEGCOMP, &jpegcomp, "VIDIOC_S_JPEGCOMP"); + } + if (options[OptOverlay]) { doioctl(fd, VIDIOC_OVERLAY, &overlay, "VIDIOC_OVERLAY"); } @@ -2110,6 +2217,12 @@ set_vid_fmt_error: printfbuf(fb); } + if (options[OptGetJpegComp]) { + struct v4l2_jpegcompression jc; + if (doioctl(fd, VIDIOC_G_JPEGCOMP, &jc, "VIDIOC_G_JPEGCOMP") == 0) + printjpegcomp(jc); + } + if (options[OptGetCropCap]) { struct v4l2_cropcap cropcap; diff --git a/v4l2-spec/compat.sgml b/v4l2-spec/compat.sgml index 8c3177b22..94713023d 100644 --- a/v4l2-spec/compat.sgml +++ b/v4l2-spec/compat.sgml @@ -2281,6 +2281,17 @@ was renamed to <structname id=v4l2-chip-ident-old>v4l2_chip_ident_old</structnam <constant>V4L2_CID_ZOOM_CONTINUOUS</constant> and <constant>V4L2_CID_PRIVACY</constant>.</para> </listitem> + </orderedlist> + </section> + <section> + <title>V4L2 in Linux 2.6.30</title> + <orderedlist> + <listitem> + <para>New control flag <constant>V4L2_CTRL_FLAG_WRITE_ONLY</constant> was added.</para> + </listitem> + <listitem> + <para>New control <constant>V4L2_CID_COLORFX</constant> was added.</para> + </listitem> </orderedlist> </section> </section> diff --git a/v4l2-spec/controls.sgml b/v4l2-spec/controls.sgml index 0df57dcf7..477a97077 100644 --- a/v4l2-spec/controls.sgml +++ b/v4l2-spec/controls.sgml @@ -271,11 +271,20 @@ minimum value disables backlight compensation.</entry> <entry>boolean</entry> <entry>Enable the color killer (&ie; force a black & white image in case of a weak video signal).</entry> </row> + <row id="v4l2-colorfx"> + <entry><constant>V4L2_CID_COLORFX</constant></entry> + <entry>enum</entry> + <entry>Selects a color effect. Possible values for +<constant>enum v4l2_colorfx</constant> are: +<constant>V4L2_COLORFX_NONE</constant> (0), +<constant>V4L2_COLORFX_BW</constant> (1) and +<constant>V4L2_COLORFX_SEPIA</constant> (2).</entry> + </row> <row> <entry><constant>V4L2_CID_LASTP1</constant></entry> <entry></entry> <entry>End of the predefined control IDs (currently -<constant>V4L2_CID_COLOR_KILLER</constant> + 1).</entry> +<constant>V4L2_CID_COLORFX</constant> + 1).</entry> </row> <row> <entry><constant>V4L2_CID_PRIVATE_BASE</constant></entry> @@ -1679,7 +1688,7 @@ remain constant.</entry> camera horizontally by the specified amount. The unit is undefined. A positive value moves the camera to the right (clockwise when viewed from above), a negative value to the left. A value of zero does not -cause motion.</entry> +cause motion. This is a write-only control.</entry> </row> <row><entry></entry></row> @@ -1689,25 +1698,23 @@ cause motion.</entry> </row><row><entry spanname="descr">This control turns the camera vertically by the specified amount. The unit is undefined. A positive value moves the camera up, a negative value down. A value of -zero does not cause motion.</entry> +zero does not cause motion. This is a write-only control.</entry> </row> <row><entry></entry></row> <row> <entry spanname="id"><constant>V4L2_CID_PAN_RESET</constant> </entry> - <entry>boolean</entry> - </row><row><entry spanname="descr">When this control is set -to <constant>TRUE</constant> (1), the camera moves horizontally to the -default position.</entry> + <entry>button</entry> + </row><row><entry spanname="descr">When this control is set, +the camera moves horizontally to the default position.</entry> </row> <row><entry></entry></row> <row> <entry spanname="id"><constant>V4L2_CID_TILT_RESET</constant> </entry> - <entry>boolean</entry> - </row><row><entry spanname="descr">When this control is set -to <constant>TRUE</constant> (1), the camera moves vertically to the -default position.</entry> + <entry>button</entry> + </row><row><entry spanname="descr">When this control is set, +the camera moves vertically to the default position.</entry> </row> <row><entry></entry></row> @@ -1750,7 +1757,7 @@ negative values towards infinity.</entry> </row><row><entry spanname="descr">This control moves the focal point of the camera by the specified amount. The unit is undefined. Positive values move the focus closer to the camera, -negative values towards infinity.</entry> +negative values towards infinity. This is a write-only control.</entry> </row> <row><entry></entry></row> @@ -1778,7 +1785,7 @@ value should be a positive integer.</entry> </row><row><entry spanname="descr">Specify the objective lens focal length relatively to the current value. Positive values move the zoom lens group towards the telephoto direction, negative values towards the -wide-angle direction. The zoom unit is driver-specific.</entry> +wide-angle direction. The zoom unit is driver-specific. This is a write-only control.</entry> </row> <row><entry></entry></row> diff --git a/v4l2-spec/vidioc-queryctrl.sgml b/v4l2-spec/vidioc-queryctrl.sgml index 20c4147eb..9908a964d 100644 --- a/v4l2-spec/vidioc-queryctrl.sgml +++ b/v4l2-spec/vidioc-queryctrl.sgml @@ -361,6 +361,15 @@ control.</entry> <entry>A hint that this control is best represented as a slider-like element in a user interface.</entry> </row> + <row> + <entry><constant>V4L2_CTRL_FLAG_WRITE_ONLY</constant></entry> + <entry>0x0040</entry> + <entry>This control is permanently writable only. Any +attempt to read the control will result in an &EACCES; error code. This +flag is typically present for relative controls or action controls where +writing a value will cause the device to carry out a given action +(⪚ motor control) but no meaningful value can be returned.</entry> + </row> </tbody> </tgroup> </table> @@ -378,6 +387,12 @@ is invalid. The &v4l2-querymenu; <structfield>id</structfield> or <structfield>index</structfield> is invalid.</para> </listitem> </varlistentry> + <varlistentry> + <term><errorcode>EACCES</errorcode></term> + <listitem> + <para>An attempt was made to read a write-only control.</para> + </listitem> + </varlistentry> </variablelist> </refsect1> </refentry> |