GIT ce69c38b89688acb1850d07da246b5a566489f4e git+ssh://master.kernel.org/pub/scm/linux/kernel/git/mchehab/v4l-dvb.git#devel commit Author: Mauro Carvalho Chehab Date: Tue Nov 21 11:32:12 2006 -0300 V4L/DVB (4871): Fix i2c dependencies of VIDEO_TVEEPROM and VIDEO_TUNER Signed-off-by: Mauro Carvalho Chehab commit a6358b33ad9d81d8a1d405daa4f3479f8cf5c6ea Author: Michael Krufky Date: Mon Nov 20 17:03:48 2006 -0300 V4L/DVB (4869): Lgh06xf: use return value of dvb_pll_configure() Use return value of dvb_pll_configure() to fill priv->frequency In lgh06xf_set_params: Rename int variable "err" to "result". Remove needless calculation of the set frequency, since this value is now being returned by dvb_pll_configure(). Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 08fdf2136626a0580c742069ca4dc6c2c5038448 Author: Michael Krufky Date: Mon Nov 20 16:45:29 2006 -0300 V4L/DVB (4868): Dvb-pll: return frequency set by dvb_pll_configure() This patch removes some duplicated code by returning the frequency set by dvb_pll_configure(), instead of recalculating it again in dvb_pll_set_params() and dvb_pll_calc_regs(). If the return value of dvb_pll_configure is less than zero, it is an error code. Otherwise, the return value is the frequency actually set by the function. Signed-off-by: Michael Krufky Acked-by: Andrew de Quincey Signed-off-by: Mauro Carvalho Chehab commit 8e68a5d5bac71e0910e696a6c83b85c189e2a4fd Author: Michael Krufky Date: Mon Nov 20 16:38:42 2006 -0300 V4L/DVB (4867): Whitespace cleanup - adhere to 80-column limit - replace some spaces with tabs Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 1b04e63f92791a7d78f5b833abcd78cc7bc90b60 Author: Patrick Boettcher Date: Tue Nov 21 10:47:38 2006 -0200 V4L/DVB (4863): Adding support for Pinnacle PCTV 400e DVB-S Adding support for Pinnacle PCTV 400e DVB-S. The module name is called ttusb2, because it this device (and other Pinnacle devices) is using the USB-protocol originally used by Technotrend device. I'm suspecting Technotrend as the device-designer. Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab commit 463c0699fa0e6ab7862706ea2810bf7693bb9d75 Author: Patrick Boettcher Date: Tue Nov 21 05:35:10 2006 -0300 V4L/DVB (4864): TDA826x I2C read with 2 messages Added a dump I2C message to the TDA826x-driver to fix I2C read for identification with ttusb2-driver. Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab commit 30b676f8fc0fd507facc9150e96280d134c93eea Author: audetto@tiscali.it Date: Mon Nov 20 18:27:44 2006 -0300 V4L/DVB (4862): Fixes uninitialized variables passed to VIDIOC_G_FBUF. Signed-off-by: Andrea A Odetti Signed-off-by: Mauro Carvalho Chehab commit 3cebc7888a8a982c18c7506f16c07541715deb8c Author: Mauro Carvalho Chehab Date: Mon Nov 20 13:19:20 2006 -0300 V4L/DVB (4861): Remove the need of a STD array for drivers using video_ioctl2 video_ioctl2 will auto-generate standard entries at ENUM_FMT. Also, now, a driver may return a subset of the video array at the return, to be stored as the current_norm. For example, a driver may ask for V4L2_STD_PAL. At return, driver may change it to V4L2_STD_PAL_B. This way, a futher call to G_STD will return the exact detected video std. Signed-off-by: Mauro Carvalho Chehab commit a98eb9dd08d1f5e2d434212ace41e8905f35d6da Author: Mauro Carvalho Chehab Date: Mon Nov 20 12:13:25 2006 -0300 V4L/DVB (4860): Optimization of v4l1 handling Signed-off-by: Mauro Carvalho Chehab commit 3d3e01c5d32abbd340c604dd752434d080450353 Author: Mauro Carvalho Chehab Date: Mon Nov 20 12:10:43 2006 -0300 V4L/DVB (4859): Fix initializations on some video_ioctl2 handlers Signed-off-by: Mauro Carvalho Chehab commit 525839970e0eb7563bc16594a5261be469b22b3a Author: Mauro Carvalho Chehab Date: Mon Nov 20 12:10:04 2006 -0300 V4L/DVB (4858): Fix: implement missing VIDIOCSTUNER on v4l1-compat module Signed-off-by: Mauro Carvalho Chehab commit 416119078e519375e9088a1503980412cfb49ea1 Author: Mauro Carvalho Chehab Date: Mon Nov 20 12:06:54 2006 -0300 V4L/DVB (4857): Cleans some ioctl structs before calling V4L2 counterpart Signed-off-by: Mauro Carvalho Chehab commit 11e457685b6ef9f2298b03cc84a5807eb3f61247 Author: Andrew de Quincey Date: Sun Nov 19 14:10:59 2006 -0300 V4L/DVB (4855): Support KNC1 DVBC cards with alternative tda10021 i2c address For some reason, some of these cards have the tda10021 configured to a different address. This adds support for such cards Signed-off-by: Andrew de Quincey Signed-off-by: Mauro Carvalho Chehab commit f7e4131522c626707f2823406fe2262b709033b0 Author: Dmitry Torokhov Date: Mon Nov 20 10:23:04 2006 -0300 V4L/DVB (4854): Handle errors from input_register_device() Also sprinkled some input_sync() throughout the code. Acked-by: Ricardo Cerqueira Acked-by: Oliver Endriss Acked-by: Andrew de Quincey Signed-off-by: Dmitry Torokhov Signed-off-by: Mauro Carvalho Chehab commit ec18c2f1ab4ed997e14f9cfe6c053bc28a26056c Author: Mike Isely Date: Sun Nov 19 20:50:31 2006 -0300 V4L/DVB (4852): Pvrusb2: Fix horizontal resolution setting problem for 24xxx devices Fix long-standing issue which prevented use of 24xxx devices at any horizontal resolution other an 720. The problem is in the cx25840 module, if no attempt is made to initialize VBI there. Such a thing should not be needed, but the pvrusb2 driver is updated now to deal with this. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab commit 32ee0506ef7c62e63121fc42c8233a97a09b70b7 Author: Michael Krufky Date: Sun Nov 19 22:58:19 2006 -0300 V4L/DVB (4850): Lgh06xf: fix compiler error when not selected fix build error: lgh06xf.h:29: error: syntax error before '{' token Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 627ba7ed8e5c42e085c1891c634f3c4541df702e Author: Ira Snyder Date: Mon Nov 20 07:20:48 2006 -0300 V4L/DVB (4849): Add missing spin_unlock to saa6588 decoder driver Sparse noticed a lock imbalance in read_from_buf(). Further inspection shows that the lock should not be held when the function exits. This adds a spin_unlock_irqrestore(), so that every exit path of the read_from_buf() function is consistent. The unlock was missing on an error path. Signed-off-by: Ira W. Snyder Signed-off-by: Hans J. Koch Signed-off-by: Mauro Carvalho Chehab commit 2482d0887f0d669864df64bd3dcb9e2d639c9163 Author: Michael Krufky Date: Sun Nov 19 19:49:11 2006 -0300 V4L/DVB (4847): Drivers/media/dvb/frontends: kfree() cleanups We don't have to check for NULL before kfree() Acked-by: Andrew de Quincey Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit c6409d14accb24271fa8dc529a07bd44cb900ccd Author: Michael Krufky Date: Sun Nov 19 19:45:26 2006 -0300 V4L/DVB (4846): Create new lgh06xf atsc tuner module This patch creates a new atsc tuner module for the LG TDVS-H06xF ATSC tuners, called lgh06xf. The purpose of this change is to reduce some duplicated code, and to allow the lgh06xf tuner code to take advantage of dvb_attach(). As a side effect, the dependency of dvb-bt8xx on dvb-pll has been removed, since the lgh06xf module itself will use dvb-pll, while remaining optional for the dvb-bt8xx driver through the use of DVB_FE_CUSTOMISE Acked-by: Andrew de Quincey Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 75591cfd806ae999bbb5363da81f44eca5316c09 Author: Jonathan Corbet Date: Sun Nov 19 19:04:55 2006 -0300 V4L/DVB (4842): Updated camera driver A couple of Cafe driver fixes, and support for the hue and saturation controls. Signed-off-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab commit 4c9277a125eead06bac6718b07adbf000d36cfb7 Author: Mauro Carvalho Chehab Date: Sun Nov 19 05:45:52 2006 -0300 V4L/DVB (4841): Added a newer PCI ID to CARDLIST.cx88 Signed-off-by: Mauro Carvalho Chehab commit 194dd7b9dfd6d43c29f0eabe5b84628511c5a125 Author: Oliver Endriss Date: Sun Nov 19 02:15:37 2006 -0300 V4L/DVB (4840): Budget: diseqc_method module parameter for cards with subsystem-id 13c2:1003 New module parameter diseqc_method for cards with subsystem-id 13c2:1003. - 0: unreliable method, can be used by all board revisions (default) - 1: reliable method, works for newer board layouts only The parameter has no effect for cards with other subsystem-ids. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab commit 90506c453300edf2bc87006bb90cb53880db947b Author: Jelle Foks Date: Sat Nov 18 15:47:15 2006 -0300 V4L/DVB (4839): Add support ptv-305 Add subvendor/device of the ADSTech Instant TV Deluxe PTV-305 to the card list as card=45 (KWORLD_HARDWARE_MPEG_TV_XPERT). Signed-off-by: Jelle Foks Signed-off-by: Mauro Carvalho Chehab commit 0dad4a770b762a9d044ad92a85b5078ba3f64290 Author: Jelle Foks Date: Sat Nov 18 15:47:11 2006 -0300 V4L/DVB (4838): Fix cx88-blackbird null pointer Allows 'debug=1' for cx88-blackbird module (dev needs to be valid for dprintk). Fixes a null-pointer dereference when using debug=1. Signed-off-by: Jelle Foks Signed-off-by: Mauro Carvalho Chehab commit 9f15efa01b3b39df894ef6dff220901857289194 Author: Nico Sabbi Date: Wed Nov 15 22:06:56 2006 -0300 V4L/DVB (4836): Added support for both DVB frontends of the Lifeview Trio This card (like some others) supports both, DVB-T and a DVB-S. The patch adds an insmod option to select the frontend: use_frontend=0 -> DVB-T use_frontend=1 -> DVB-S Signed-off-by: Nico Sabbi Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit ac8e0c3f960eff291e6c73ad3624ec3a0abea0d6 Author: Hartmut Hackmann Date: Wed Nov 15 21:31:54 2006 -0300 V4L/DVB (4835): Added support for the Terratec Cinergy HT PCMCIA module This is a hybrid cardbus module. Besides the card support, i modified the definition names for AGC and GPIO of the tda10046. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 3c9f19367809c6e6fabd6bdfa33a4daa5273cd36 Author: Adrian Bunk Date: Fri Nov 17 11:59:22 2006 -0300 V4L/DVB (4834): Cafe_ccic.c: make a function static This patch makes the needlessly global cafe_v4l_dev_release() static. Signed-off-by: Adrian Bunk Signed-off-by: Mauro Carvalho Chehab commit 09597bb7b9546997b1037c14c7cf2422204941df Author: Thomas Hamm Date: Fri Nov 17 07:12:58 2006 -0300 V4L/DVB (4833): Patch for SATELCO EasyWatch PCI (DVB-C) Add support for Satelco EasyWatch PCI DVBC cards Signed-off-by: Thomas Hamm Signed-off-by: Andrew de Quincey Signed-off-by: Mauro Carvalho Chehab commit b0765e7ece26978f5f44a5007ac27af2a5c78c77 Author: Andrew de Quincey Date: Thu Nov 16 22:12:40 2006 -0300 V4L/DVB (4832): Fix uninitialised variable in dvb_frontend_swzigzag Spotted by coverity/Adrian Bunk. Signed-off-by: Andrew de Quincey Signed-off-by: Mauro Carvalho Chehab commit fb3871361ce8ea3e46a42d3d05829a9b70f07739 Author: Andrew de Quincey Date: Thu Nov 16 18:31:04 2006 -0300 V4L/DVB (4831): Fix tuning on older budget DVBS cards. Fixes to DISEQC on these cards inadvertently broke normal tone/voltage signalling. This restores the necessary function. Signed-off-by: Andrew de Quincey Signed-off-by: Mauro Carvalho Chehab commit 76d60d5f2c454b4bbd9d9fb8ec4e10e8d677db30 Author: Mauro Carvalho Chehab Date: Thu Nov 16 11:02:28 2006 -0300 V4L/DVB (4829): v4l2_type_names table is incomplete Sliced VBI types are missing at the tables. Fixing it. Signed-off-by: Mauro Carvalho Chehab commit d4034724444b7982441a20462c1b1608b09f30d0 Author: Mauro Carvalho Chehab Date: Tue Nov 14 12:40:07 2006 -0300 V4L/DVB (4825): FIX bug 5760: audio were not working on some bttv drivers This fixes a bug introduced by the -git commit: bbf7871e1cd58b89f77b1152f457250c6e94b614 It seems that some bttv apps can't work fine when audioset=0. Thanks to Christian Casteyde for pointing this. Signed-off-by: Mauro Carvalho Chehab commit e3ba34bccd2fbff262ae097c2ec10fb1f365e8b9 Author: Jose Carlos Garcia Sogo Date: Tue Nov 14 05:01:47 2006 -0300 V4L/DVB (4823): Add alternative device ID (0xb808) for AverMedia AverTV Volar dongles. Add alternative device ID (0xb808) for AverMedia AverTV Volar dongles. Signed-off-by: Jose Carlos Garcia Sogo Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab commit 8d9bb9445aac3622096cd53df6dd62ec30fac052 Author: Stefan Traby Date: Sun Nov 12 13:02:51 2006 -0300 V4L/DVB (4819): Dib0700: Add support for new revision of Nova-T Stick Added support for Nova-T Stick with USB-pid: 0x7060 Signed-off-by: Stefan Traby Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 522cd9533174310e3c2374b9d764c8200b0330cc Author: Paolo Ciarrocchi Date: Sun Nov 12 14:19:15 2006 -0300 V4L/DVB (4813): Added information about Technisat Sky2Pc cards Acked-by: Daniele Vallini Signed-off-by: Paolo Ciarrocchi Signed-off-by: Mauro Carvalho Chehab commit 78ec9e64f57f5ffe4ed40272733cccf2df30fa7d Author: Hans Verkuil Date: Sun Nov 12 09:28:46 2006 -0300 V4L/DVB (4812): Detect presence of IR receiver/IR transmitter in tveeprom Thanks to input from Steven Toth from Hauppauge the tveeprom module has been extended to detect the presence of an IR transmitter (aka IR-blaster). Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab commit 08bddc049390edbaf09ce349d5bdf4103c3954c3 Author: Thomas Genty Date: Sun Nov 5 14:17:30 2006 -0300 V4L/DVB (4806): Saa7134: add support for Hauppauge WinTV-HVR1110 DVB-T/Hybrid This patch adds support for the Hauppauge WinTV-HVR1110 DVB-T/Hybrid Signed-off-by: Thomas Genty Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit bba18872bdbb72f7f06dcbcd9a8e4ee32483b0e7 Author: Michael Krufky Date: Wed Nov 8 16:47:32 2006 -0300 V4L/DVB (4805): Dib0700: Add support for Leadtek Winfast DTV Dongle (STK7700P based) This patch adds support for the new, STK7700-based revision of the Leadtek Winfast DTV Dongle. Signed-off-by: Michal CIJOML Semler Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit dd6970b9e89cad5c73fcd1c44f52f82d90389303 Author: Jonathan Corbet Date: Sat Nov 4 09:26:00 2006 -0300 V4L/DVB (4798): OmniVision OV7670 driver This patch adds a V4L2 driver for the OmniVision OV7670 camera. Signed-off-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab commit ee9a16d645fe6e377bf298c2fe1d4430842f31aa Author: Jonathan Corbet Date: Sat Nov 4 09:25:53 2006 -0300 V4L/DVB (4797): Marvell 88ALP01 "cafe" driver A driver for the Marvell M88ALP01 "CAFE" CMOS integrated camera controller. This driver has been renamed "cafe_ccic" since my previous patch set. Signed-off-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab commit 0d6ec7cf4d2bec6545aa6680be68aa4df44fc58e Author: Jonathan Corbet Date: Sat Nov 4 09:22:27 2006 -0300 V4L/DVB (4796): A couple of V4L2 defines needed by Cafe Camara driver Two defines for V4L2, needed by the Cafe camera driver: 1) Add the RGB444 image format 2) Add the "init" internal command which is separate from "reset". Signed-off-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab commit 35c40bfad1859540543e6789f01e556b7bbada61 Author: Andrew Morton Date: Thu Nov 2 19:45:13 2006 -0300 V4L/DVB (4794): Dvb: dibx000_common-fix Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab commit b1686db8b26867fbf3ac46b6abbb95598001f21c Author: Hartmut Hackmann Date: Mon Oct 30 20:00:16 2006 -0300 V4L/DVB (4792): Add support for the Compro Videomate DVB-T200A This board has the same PCI ID as the T200, so the exact board type is determined from the eeprom. The original patch was provided by Francis Barber Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 7a96e83f26cfb69549542895637a1be4082f7213 Author: Hartmut Hackmann Date: Mon Oct 30 19:56:59 2006 -0300 V4L/DVB (4791): Added autodetected flag to the saa7134_dev structure In case the exact board type needs to be determined by probing or evaluating the eeprom, this flag allows to still set the board type via the card=xx insmod option. This is an extract of a patch by Francis Barber. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 23e8899618fc5d874d15450224413203f7a12e88 Author: Trent Piepho Date: Sun Oct 29 13:35:39 2006 -0300 V4L/DVB (4789): Lgdt330x: SNR and signal strength reporting Update the SNR calculations to use the new dvb_math log function, and add SNR calculations for all supported modulations for both lg dt3302 and dt3303. The QAM equations don't appear in the dt3302 datasheet, so the ones from the dt3303 datasheet were used. SNR returned is the actual value in dB as 8.8 fixed point. Reporting of real signal strength isn't supported, so rather than return 0, which confuses some software and users, a re-scaled SNR value is returned. Code originally by Rusty Scott. Signed-off-by: Trent Piepho Signed-off-by: Rusty Scott Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit ba7220d3afcf47623111e008c900119e334d3eb4 Author: Christoph Haubrich Date: Tue Oct 31 00:29:30 2006 -0300 V4L/DVB (4788): Tda8083: support for uncorrectable blocks and bit error rate Copied routines for uc blocks and BER from the removed tda80xx.c into tda8083.c. Signed-off-by: Christoph Haubrich Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab commit 309019165ab56df8258cda6bcca2b764fcbd1255 Author: Patrick Boettcher Date: Thu Oct 19 08:17:43 2006 -0300 V4L/DVB (4778): Fixed device count Forgot to increase the device count for the STK7700P-devices Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab commit cab9722d2d07a1efa84b592288500d05a0b86e7f Author: Henning Schroeer Date: Thu Oct 19 07:58:22 2006 -0300 V4L/DVB (4776): Adding USB IDs for Uniwill STK7700P-ref-design Adding USB IDs for Uniwill STK7700P-ref-design Signed-off-by: Henning Schroeer Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab commit 61c10412ecdadf87ae87950ea9657fa52d58301a Author: Tomi Koivulahti Date: Thu Oct 19 07:27:19 2006 -0300 V4L/DVB (4775): [patch] Add Compro USB IDs This patch adds Compro Videomate U500 to supported devices. Signed-off-by: Tomi Koivulahti Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab commit 1c717a9ba015442464f11ad15ae38752effa4d5c Author: Patrick Boettcher Date: Wed Oct 18 08:35:16 2006 -0300 V4L/DVB (4773): Misc fixes for DiB3000 and DiB7000 This patch contains several fixes for the autosearch algorithm and other small ones. Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab commit 374bf8b6881a90cdf36785e5cc33bf00470cf477 Author: Patrick Boettcher Date: Wed Oct 18 08:34:16 2006 -0300 V4L/DVB (4772): Add support for DiBcom DiB7000PC This patch contains support for the DiB7000PC-driver. Signed-off-by: Francois KANOUNNIKOFF Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab commit aeb8b262708261f4abb9e7578763e6613eba2a25 Author: Hartmut Hackmann Date: Thu Oct 12 20:38:51 2006 -0300 V4L/DVB (4769): Added support for a ASUSTEK P7131 Dual DVB-T variant This card has no firmware eeprom. The old version still should not need a firmware file due to an undocumented feature of the TDA10046. The patch also includes Hermann Pitton's proposal for improved antenna switch handling Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 70b1bbc033d2625a58beb0062baee12a8d7ecc3e Author: Hermann Pitton Date: Thu Oct 12 20:00:56 2006 -0300 V4L/DVB (4768): Add the Asus P7131 Dual hybrid to the new tda8290_i2c_gate_ctrl The gate control was moved to a separate function. Signed-off-by: Hermann Pitton Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 4a18a8b780bff3de69c178c5a066c5a120fd5ef5 Author: Hartmut Hackmann Date: Thu Oct 12 19:46:16 2006 -0300 V4L/DVB (4767): Make Remote control of the Pinnacle PCTV 310i work The remote control works, but we still need a better keymap. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 00da4c01e2c41c44392d43eb982646180f772ab1 Author: Patrick Boettcher Date: Tue Oct 17 18:28:14 2006 -0300 V4L/DVB (4766): Add working dib7000m-module First working version of the dib7000m-driver. This commit also makes the Hauppauge NOVA-T Stick working. Signed-off-by: Francois Kanounnikoff Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab commit 31ded9454777096e8663d3579aff9cd006042d68 Author: Luke Deller Date: Tue Oct 17 18:28:10 2006 -0300 V4L/DVB (4765): Dvb-usb/vp7045.c patch for extra key Add support for more keys on the remote control included with the DigitalNow tinyUSB2 DVB-T Receiver. Signed-off-by: Luke Deller Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab commit 459e96a1f7502f758240a9663f43eaeb1dc23c4d Author: Mike Isely Date: Sun Oct 15 21:35:14 2006 -0300 V4L/DVB (4763): Pvrusb2: Implement IR reception for 24xxx devices Unlike 29xxx devices, the 24xxx model series does not have a dedicated I2C device for reception of IR codes. Instead IR is handled directly by the FX2 microcontroller and the results are communicated via commands to the FX2. Rather than implement a whole new IR reception pathway for 24xxx devices, this changeset instead emulates the presence of the 29xxx device's I2C based IR receiver by intercepting commands to that chip and issuing appropriate FX2 commands to do the needed action. This has the result of allowing all the usual IR frameworks (ir-kbd-i2c or lirc) to continue working unmodified for 24xxx devices. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab commit b413c0f694cd3ae6688f00fc81e47bb197b0f0b2 Author: Amit Choudhary Date: Tue Oct 17 11:39:06 2006 -0300 V4L/DVB (4761): Stv680.c: check kmalloc() return value. Signed-off-by: Amit Choudhary Signed-off-by: Mauro Carvalho Chehab commit 503050fb5f024dde2444ae122908254cd07719a8 Author: Michael Krufky Date: Mon Oct 16 20:47:14 2006 -0300 V4L/DVB (4759): Cx88: use external adc for rca audio inputs on the ASUS PVR-416 For the ASUS PVR-416, the external adc must be used for the rca audio inputs, but television / radio inputs use the internal adc. Thanks to Alex Deucher for lending his card to me. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 973cc0ea262443b221257b080b7d0eb88d525e50 Author: Michael Krufky Date: Mon Oct 16 16:53:01 2006 -0300 V4L/DVB (4758): Cx88: use external adc for svideo/composite For the KWorld HardwareMpegTV XPert, the external adc must be used for svideo / composite inputs, but television / radio inputs use the internal adc. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit e12b501d265cf8f6cd41b05fd839c696acd8eb23 Author: Michael Krufky Date: Mon Oct 16 16:51:11 2006 -0300 V4L/DVB (4757): Cx88: determine whether or not to use external adc Some cx88-blackbird boards use an external adc, but not necessarily for all inputs. Thus, this needs to be configurable on the card level for each input. This patch allows for the usage of the external adc to be determined by a bit setting in the cx88_input struct for cards based on the cx88 blackbird design. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 8608fe1b9b4ad000c285c7004c8ea8470023a8a8 Author: Michael Krufky Date: Mon Oct 16 16:07:51 2006 -0300 V4L/DVB (4756): Cx88: cleanups - fixed whitespace, replaced leading spaces with tabs - moved .mpeg descriptor below input settings Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit f87e4631e57bc6b25b3c136b0394dfabfd9d36d3 Author: Michael Krufky Date: Sun Oct 15 14:51:08 2006 -0300 V4L/DVB (4754): Cxusb: update copyright and author email address Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 0debd89225ff6089e37b887594a1066b6b3d9c30 Author: Michael Krufky Date: Fri Oct 13 21:55:57 2006 -0300 V4L/DVB (4753): Cxusb: rename cxusb_lgdt3303_tuner_attach cxusb_lgdt3303_tuner_attach were renamed to cxusb_lgh064f_tuner_attach Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab commit 862b3a78a9e25e74e9dd03733eca3ed2cfcddad9 Author: Patrick Boettcher Date: Fri Oct 13 11:34:46 2006 -0300 V4L/DVB (4749): Fixed DVB-USB-Adapter indention While converting everything to DVB-USB-Adapter, there was a wrong indention. Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab commit 6d5588d65e7cfd55c4d971b4f74876658a13a0ec Author: Steven Toth Date: Fri Oct 6 21:29:25 2006 -0300 V4L/DVB (4736): Cx88-blackbird module is rejected during probe. If the last cx88 board probed is not backbird based, and a previous board was, the entire module is unloaded leading to an oops during mpeg_open on the first /dev/videoN device. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab commit 7eb0ae010c610f6b783b862130c2ee612e184e41 Author: Mikhail Fedotov Date: Fri Oct 6 20:23:47 2006 -0300 V4L/DVB (4728): Add support for AverMedia AverTV Studio 507 This is just an additional analog board configuration. Signed-off-by: Mikhail Fedotov Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit ee1c48ec91fe32503e2a68e5fece642737d2e0a8 Author: Hartmut Hackmann Date: Fri Oct 6 19:13:50 2006 -0300 V4L/DVB (4726): Add support for Pinnacle 310i The driver supports analog TV, radio and DVB-T. It is based on the preliminary patch by Pierluigi Rolando. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab commit 4bf66ce409bec3e59482ce9e66527294e46d0613 Author: Steven Toth Date: Thu Oct 5 21:28:24 2006 -0300 V4L/DVB (4723): Bugfix: Select the correct cx8802_dev when enumerating by CX88_MPEG_type A bug in cx8802_get_driver() meant that in multiboard environments, when testing frontends on the non primary board, the incorrect device was returned resulting in "Unsupported value in .mpeg.." messages. Depending on the electrical design of the hardware (serial, parallel, rising/falling edge detect), transport would still be delivered and the problem went unnoticed. This patch ensures the correct instance of cx8802_dev is returned. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab commit b16e157e9d55a19f76212d4bd4bc2d64ddffde3b Author: Steven Toth Date: Tue Nov 21 09:09:31 2006 -0200 V4L/DVB (4676): Dynamic cx88 mpeg port management for HVR1300 MPEG2/DVB-T support. A series of patches to change the cx88 framework to allow the PCI mpeg port to be shared dynamically between different types of drivers or applications. This patch changes the cx88-dvb and cx88-blackbird drivers to become 'sub drivers' of a higher single cx88-mpeg driver. The cx88-mpeg driver is a superset of the previous cx88-mpeg/blackbird drivers and now owns the IRQ. cx88-dvb/blackbird now become mini drivers, registering themselves with cx88-mpeg through a standard interface with callbacks. Sub drivers request access to hardware via the cx88-mpeg driver. In turn the cx88-mpeg driver determines whether the hardware is busy and accepts or refuses the request, grant access using callbacks into the sub drivers. The net effect is that you are no longer able to tamper with the mpeg port from multiple different applications at the same time, potentially breaking a live mpeg2 hardware encoding or dvb stream. The mechanism extends to enable multiple dvb frontends to be registered and share the single resource. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab commit 7611b2a3d2b79545445ace75194ec08909c94bb2 Author: Patrick Boettcher Date: Tue Nov 21 09:09:31 2006 -0200 V4L/DVB (4524): Initial commit for the DiB7000M-demod Initial commit for the driver for the DiB7000M COFDM demodulator. Signed-off-by: Francois KANOUNNIKOFF Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab commit 946136184f7acf515b2d8decc7a429fb8b587efb Author: Trent Piepho Date: Wed Oct 4 20:33:51 2006 -0300 V4L/DVB (4722): Cx88: Add support for VIDIOC_INT_[SR]_REGISTER ioctls Add support for the advanced debugging ioctls, to allow access to the cx88 registers from userspace. Only i2c_id == 0 is supported, for access to the cx88 adapter itself. There isn't any support for access to I2C clients of the adapter. Most of them don't have R/W registers anyway, and its necessary to use i2c-dev to talk to them from userspace. Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab commit 3701ad074b38a011d77c459226d446c97ee8a41d Author: Patrick Boettcher Date: Thu Oct 19 08:15:40 2006 -0300 V4L/DVB (4777): Correct AVerMedia Volar USB ID correct AVerMedia Volar USB ID Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab Documentation/dvb/cards.txt | 4 Documentation/video4linux/CARDLIST.cx88 | 2 Documentation/video4linux/CARDLIST.saa7134 | 7 Documentation/video4linux/cafe_ccic | 54 + drivers/media/Kconfig | 2 drivers/media/dvb/b2c2/Kconfig | 1 drivers/media/dvb/b2c2/flexcop-fe-tuner.c | 10 drivers/media/dvb/bt8xx/Kconfig | 2 drivers/media/dvb/bt8xx/dvb-bt8xx.c | 9 drivers/media/dvb/bt8xx/dvb-bt8xx.h | 2 drivers/media/dvb/cinergyT2/cinergyT2.c | 13 drivers/media/dvb/dvb-core/dvb_frontend.c | 2 drivers/media/dvb/dvb-usb/Kconfig | 14 drivers/media/dvb/dvb-usb/Makefile | 3 drivers/media/dvb/dvb-usb/a800.c | 36 drivers/media/dvb/dvb-usb/cxusb.c | 147 + drivers/media/dvb/dvb-usb/dib0700.h | 5 drivers/media/dvb/dvb-usb/dib0700_core.c | 40 drivers/media/dvb/dvb-usb/dib0700_devices.c | 200 ++ drivers/media/dvb/dvb-usb/dibusb-mb.c | 113 + drivers/media/dvb/dvb-usb/dibusb-mc.c | 26 drivers/media/dvb/dvb-usb/digitv.c | 22 drivers/media/dvb/dvb-usb/dtt200u.c | 24 drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 14 drivers/media/dvb/dvb-usb/dvb-usb-remote.c | 37 drivers/media/dvb/dvb-usb/gp8psk.c | 22 drivers/media/dvb/dvb-usb/nova-t-usb2.c | 34 drivers/media/dvb/dvb-usb/ttusb2.c | 270 +++ drivers/media/dvb/dvb-usb/ttusb2.h | 70 + drivers/media/dvb/dvb-usb/umt-010.c | 24 drivers/media/dvb/dvb-usb/vp702x.c | 20 drivers/media/dvb/dvb-usb/vp7045.c | 40 drivers/media/dvb/frontends/Kconfig | 24 drivers/media/dvb/frontends/Makefile | 3 drivers/media/dvb/frontends/dib3000mc.c | 7 drivers/media/dvb/frontends/dib7000m.c | 1191 +++++++++++ drivers/media/dvb/frontends/dib7000m.h | 51 drivers/media/dvb/frontends/dib7000p.c | 1019 ++++++++++ drivers/media/dvb/frontends/dib7000p.h | 46 drivers/media/dvb/frontends/dibx000_common.h | 13 drivers/media/dvb/frontends/dvb-pll.c | 67 - drivers/media/dvb/frontends/dvb-pll.h | 7 drivers/media/dvb/frontends/lg_h06xf.h | 64 - drivers/media/dvb/frontends/lgdt330x.c | 243 +- drivers/media/dvb/frontends/lgdt330x_priv.h | 15 drivers/media/dvb/frontends/lgh06xf.c | 134 + drivers/media/dvb/frontends/lgh06xf.h | 35 drivers/media/dvb/frontends/tda1004x.c | 10 drivers/media/dvb/frontends/tda1004x.h | 5 drivers/media/dvb/frontends/tda8083.c | 30 drivers/media/dvb/frontends/tda826x.c | 12 drivers/media/dvb/frontends/tua6100.c | 3 drivers/media/dvb/ttpci/av7110_ir.c | 25 drivers/media/dvb/ttpci/budget-av.c | 15 drivers/media/dvb/ttpci/budget-ci.c | 42 drivers/media/dvb/ttpci/budget.c | 9 drivers/media/dvb/ttusb-dec/ttusb_dec.c | 11 drivers/media/video/Kconfig | 17 drivers/media/video/Makefile | 3 drivers/media/video/bt8xx/bttv-driver.c | 2 drivers/media/video/bt8xx/bttv-input.c | 101 + drivers/media/video/cafe_ccic-regs.h | 160 ++ drivers/media/video/cafe_ccic.c | 2232 +++++++++++++++++++++ drivers/media/video/cx88/Kconfig | 1 drivers/media/video/cx88/cx88-blackbird.c | 179 +- drivers/media/video/cx88/cx88-cards.c | 86 + drivers/media/video/cx88/cx88-dvb.c | 217 +- drivers/media/video/cx88/cx88-input.c | 77 - drivers/media/video/cx88/cx88-mpeg.c | 348 +++ drivers/media/video/cx88/cx88-tvaudio.c | 13 drivers/media/video/cx88/cx88-video.c | 32 drivers/media/video/cx88/cx88.h | 47 drivers/media/video/ir-kbd-i2c.c | 43 drivers/media/video/ov7670.c | 1286 ++++++++++++ drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c | 16 drivers/media/video/pvrusb2/pvrusb2-hdw.c | 26 drivers/media/video/pvrusb2/pvrusb2-i2c-core.c | 81 + drivers/media/video/saa6588.c | 4 drivers/media/video/saa7134/saa7134-cards.c | 215 ++ drivers/media/video/saa7134/saa7134-core.c | 11 drivers/media/video/saa7134/saa7134-dvb.c | 222 ++ drivers/media/video/saa7134/saa7134-input.c | 50 drivers/media/video/saa7134/saa7134.h | 8 drivers/media/video/stv680.c | 21 drivers/media/video/tveeprom.c | 9 drivers/media/video/usbvideo/quickcam_messenger.c | 9 drivers/media/video/v4l1-compat.c | 18 drivers/media/video/v4l2-common.c | 85 + drivers/media/video/videodev.c | 150 + drivers/media/video/vivi.c | 16 include/linux/i2c-id.h | 2 include/linux/videodev2.h | 1 include/media/tveeprom.h | 2 include/media/v4l2-common.h | 7 include/media/v4l2-dev.h | 14 95 files changed, 9072 insertions(+), 1089 deletions(-) diff --git a/Documentation/dvb/cards.txt b/Documentation/dvb/cards.txt index ca58e33..cc09187 100644 --- a/Documentation/dvb/cards.txt +++ b/Documentation/dvb/cards.txt @@ -22,10 +22,10 @@ o Frontends drivers: - ves1x93 : Alps BSRV2 (ves1893 demodulator) and dbox2 (ves1993) - cx24110 : Conexant HM1221/HM1811 (cx24110 or cx24106 demod, cx24108 PLL) - grundig_29504-491 : Grundig 29504-491 (Philips TDA8083 demodulator), tsa5522 PLL - - mt312 : Zarlink mt312 or Mitel vp310 demodulator, sl1935 or tsa5059 PLL + - mt312 : Zarlink mt312 or Mitel vp310 demodulator, sl1935 or tsa5059 PLLi, Technisat Sky2Pc with bios Rev. 2.3 - stv0299 : Alps BSRU6 (tsa5059 PLL), LG TDQB-S00x (tsa5059 PLL), LG TDQF-S001F (sl1935 PLL), Philips SU1278 (tua6100 PLL), - Philips SU1278SH (tsa5059 PLL), Samsung TBMU24112IMB + Philips SU1278SH (tsa5059 PLL), Samsung TBMU24112IMB, Technisat Sky2Pc with bios Rev. 2.6 DVB-C: - ves1820 : various (ves1820 demodulator, sp5659c or spXXXX PLL) - at76c651 : Atmel AT76c651(B) with DAT7021 PLL diff --git a/Documentation/video4linux/CARDLIST.cx88 b/Documentation/video4linux/CARDLIST.cx88 index 8755b3e..62e32b4 100644 --- a/Documentation/video4linux/CARDLIST.cx88 +++ b/Documentation/video4linux/CARDLIST.cx88 @@ -43,7 +43,7 @@ 42 -> digitalnow DNTV Live! DVB-T Pro [1822:0025,1822:0019] 43 -> KWorld/VStream XPert DVB-T with cx22702 [17de:08a1,12ab:2300] 44 -> DViCO FusionHDTV DVB-T Dual Digital [18ac:db50,18ac:db54] - 45 -> KWorld HardwareMpegTV XPert [17de:0840] + 45 -> KWorld HardwareMpegTV XPert [17de:0840,1421:0305] 46 -> DViCO FusionHDTV DVB-T Hybrid [18ac:db40,18ac:db44] 47 -> pcHDTV HD5500 HDTV [7063:5500] 48 -> Kworld MCE 200 Deluxe [17de:0841] diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index 53ce6a3..f6201cc 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -76,7 +76,7 @@ 75 -> AVerMedia AVerTVHD MCE A180 [1461:1044] 76 -> SKNet MonsterTV Mobile [1131:4ee9] 77 -> Pinnacle PCTV 40i/50i/110i (saa7133) [11bd:002e] - 78 -> ASUSTeK P7131 Dual [1043:4862] + 78 -> ASUSTeK P7131 Dual [1043:4862,1043:4876] 79 -> Sedna/MuchTV PC TV Cardbus TV/Radio (ITO25 Rev:2B) 80 -> ASUS Digimatrix TV [1043:0210] 81 -> Philips Tiger reference design [1131:2018] @@ -99,3 +99,8 @@ 98 -> Proteus Pro 2309 [0919:2003] 99 -> AVerMedia TV Hybrid A16AR [1461:2c00] 100 -> Asus Europa2 OEM [1043:4860] +101 -> Pinnacle PCTV 310i [11bd:002f] +102 -> Avermedia AVerTV Studio 507 [1461:9715] +103 -> Compro Videomate DVB-T200A +104 -> Hauppauge WinTV-HVR1110 DVB-T/Hybrid [0070:6701] +105 -> Terratec Cinergy HT PCMCIA [153b:1172] diff --git a/Documentation/video4linux/cafe_ccic b/Documentation/video4linux/cafe_ccic new file mode 100644 index 0000000..8882102 --- /dev/null +++ b/Documentation/video4linux/cafe_ccic @@ -0,0 +1,54 @@ +"cafe_ccic" is a driver for the Marvell 88ALP01 "cafe" CMOS camera +controller. This is the controller found in first-generation OLPC systems, +and this driver was written with support from the OLPC project. + +Current status: the core driver works. It can generate data in YUV422, +RGB565, and RGB444 formats. (Anybody looking at the code will see RGB32 as +well, but that is a debugging aid which will be removed shortly). VGA and +QVGA modes work; CIF is there but the colors remain funky. Only the OV7670 +sensor is known to work with this controller at this time. + +To try it out: either of these commands will work: + + mplayer tv:// -tv driver=v4l2:width=640:height=480 -nosound + mplayer tv:// -tv driver=v4l2:width=640:height=480:outfmt=bgr16 -nosound + +The "xawtv" utility also works; gqcam does not, for unknown reasons. + +There are a few load-time options, most of which can be changed after +loading via sysfs as well: + + - alloc_bufs_at_load: Normally, the driver will not allocate any DMA + buffers until the time comes to transfer data. If this option is set, + then worst-case-sized buffers will be allocated at module load time. + This option nails down the memory for the life of the module, but + perhaps decreases the chances of an allocation failure later on. + + - dma_buf_size: The size of DMA buffers to allocate. Note that this + option is only consulted for load-time allocation; when buffers are + allocated at run time, they will be sized appropriately for the current + camera settings. + + - n_dma_bufs: The controller can cycle through either two or three DMA + buffers. Normally, the driver tries to use three buffers; on faster + systems, however, it will work well with only two. + + - min_buffers: The minimum number of streaming I/O buffers that the driver + will consent to work with. Default is one, but, on slower systems, + better behavior with mplayer can be achieved by setting to a higher + value (like six). + + - max_buffers: The maximum number of streaming I/O buffers; default is + ten. That number was carefully picked out of a hat and should not be + assumed to actually mean much of anything. + + - flip: If this boolean parameter is set, the sensor will be instructed to + invert the video image. Whether it makes sense is determined by how + your particular camera is mounted. + +Work is ongoing with this driver, stay tuned. + +jon + +Jonathan Corbet +corbet@lwn.net diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 9f7e1fe..87410db 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -67,6 +67,7 @@ source "drivers/media/common/Kconfig" config VIDEO_TUNER tristate + depends on I2C config VIDEO_BUF tristate @@ -82,6 +83,7 @@ config VIDEO_IR config VIDEO_TVEEPROM tristate + depends on I2C config USB_DABUSB tristate "DABUSB driver" diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig index a0dcd59..7987595 100644 --- a/drivers/media/dvb/b2c2/Kconfig +++ b/drivers/media/dvb/b2c2/Kconfig @@ -9,6 +9,7 @@ config DVB_B2C2_FLEXCOP select DVB_STV0297 if !DVB_FE_CUSTOMISE select DVB_BCM3510 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE help Support for the digital TV receiver chip made by B2C2 Inc. included in Technisats PCI cards and USB boxes. diff --git a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index b8ba878..c2b35e3 100644 --- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -14,7 +14,7 @@ #include "bcm3510.h" #include "stv0297.h" #include "mt312.h" #include "lgdt330x.h" -#include "lg_h06xf.h" +#include "lgh06xf.h" #include "dvb-pll.h" /* lnb control */ @@ -303,12 +303,6 @@ static int flexcop_fe_request_firmware(s return request_firmware(fw, name, fc->dev); } -static int lgdt3303_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) -{ - struct flexcop_device *fc = fe->dvb->priv; - return lg_h06xf_pll_set(fe, &fc->i2c_adap, params); -} - static struct lgdt330x_config air2pc_atsc_hd5000_config = { .demod_address = 0x59, .demod_chip = LGDT3303, @@ -533,7 +527,7 @@ int flexcop_frontend_init(struct flexcop /* try the air atsc 3nd generation (lgdt3303) */ if ((fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, &fc->i2c_adap)) != NULL) { fc->dev_type = FC_AIR_ATSC3; - fc->fe->ops.tuner_ops.set_params = lgdt3303_tuner_set_params; + dvb_attach(lgh06xf_attach, fc->fe, &fc->i2c_adap); info("found the lgdt3303 at i2c address: 0x%02x",air2pc_atsc_hd5000_config.demod_address); } else /* try the air atsc 1nd generation (bcm3510)/panasonic ct10s */ diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig index ae2ff5d..dd66b60 100644 --- a/drivers/media/dvb/bt8xx/Kconfig +++ b/drivers/media/dvb/bt8xx/Kconfig @@ -1,13 +1,13 @@ config DVB_BT8XX tristate "BT8xx based PCI cards" depends on DVB_CORE && PCI && I2C && VIDEO_BT848 - select DVB_PLL select DVB_MT352 if !DVB_FE_CUSTOMISE select DVB_SP887X if !DVB_FE_CUSTOMISE select DVB_NXT6000 if !DVB_FE_CUSTOMISE select DVB_CX24110 if !DVB_FE_CUSTOMISE select DVB_OR51211 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE select FW_LOADER help diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c index 14e69a7..80a85cb 100644 --- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c +++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c @@ -34,7 +34,6 @@ #include "dvb_demux.h" #include "dvb_frontend.h" #include "dvb-bt8xx.h" #include "bt878.h" -#include "dvb-pll.h" static int debug; @@ -568,12 +567,6 @@ static struct mt352_config digitv_alps_t .demod_init = digitv_alps_tded4_demod_init, }; -static int tdvs_tua6034_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) -{ - struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv; - return lg_h06xf_pll_set(fe, card->i2c_adapter, params); -} - static struct lgdt330x_config tdvs_tua6034_config = { .demod_address = 0x0e, .demod_chip = LGDT3303, @@ -616,7 +609,7 @@ static void frontend_init(struct dvb_bt8 lgdt330x_reset(card); card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter); if (card->fe != NULL) { - card->fe->ops.tuner_ops.set_params = tdvs_tua6034_tuner_set_params; + dvb_attach(lgh06xf_attach, card->fe, card->i2c_adapter); dprintk ("dvb_bt8xx: lgdt330x detected\n"); } break; diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.h b/drivers/media/dvb/bt8xx/dvb-bt8xx.h index 4745a90..e75f417 100644 --- a/drivers/media/dvb/bt8xx/dvb-bt8xx.h +++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.h @@ -37,7 +37,7 @@ #include "nxt6000.h" #include "cx24110.h" #include "or51211.h" #include "lgdt330x.h" -#include "lg_h06xf.h" +#include "lgh06xf.h" #include "zl10353.h" struct dvb_bt8xx_card { diff --git a/drivers/media/dvb/cinergyT2/cinergyT2.c b/drivers/media/dvb/cinergyT2/cinergyT2.c index ff7d4f5..daa48e4 100644 --- a/drivers/media/dvb/cinergyT2/cinergyT2.c +++ b/drivers/media/dvb/cinergyT2/cinergyT2.c @@ -746,6 +746,7 @@ static void cinergyt2_query_rc (void *da dprintk(1, "rc_input_event=%d Up\n", cinergyt2->rc_input_event); input_report_key(cinergyt2->rc_input_dev, cinergyt2->rc_input_event, 0); + input_sync(cinergyt2->rc_input_dev); cinergyt2->rc_input_event = KEY_MAX; } cinergyt2->rc_last_code = ~0; @@ -783,6 +784,7 @@ static void cinergyt2_query_rc (void *da dprintk(1, "rc_input_event=%d\n", cinergyt2->rc_input_event); input_report_key(cinergyt2->rc_input_dev, cinergyt2->rc_input_event, 1); + input_sync(cinergyt2->rc_input_dev); cinergyt2->rc_last_code = rc_events[n].value; } } @@ -798,8 +800,9 @@ static int cinergyt2_register_rc(struct { struct input_dev *input_dev; int i; + int err; - cinergyt2->rc_input_dev = input_dev = input_allocate_device(); + input_dev = input_allocate_device(); if (!input_dev) return -ENOMEM; @@ -817,7 +820,13 @@ static int cinergyt2_register_rc(struct input_dev->keycodesize = 0; input_dev->keycodemax = 0; - input_register_device(cinergyt2->rc_input_dev); + err = input_register_device(input_dev); + if (err) { + input_free_device(input_dev); + return err; + } + + cinergyt2->rc_input_dev = input_dev; schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2); return 0; diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 53304e6..a2ab2ee 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -348,7 +348,7 @@ static int dvb_frontend_swzigzag_autotun static void dvb_frontend_swzigzag(struct dvb_frontend *fe) { - fe_status_t s; + fe_status_t s = 0; struct dvb_frontend_private *fepriv = fe->frontend_priv; /* if we've got no parameters, just keep idling */ diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index a263b3f..ad52143 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -69,6 +69,8 @@ config DVB_USB_DIBUSB_MC config DVB_USB_DIB0700 tristate "DiBcom DiB0700 USB DVB devices (see help for supported devices)" depends on DVB_USB + select DVB_DIB7000P + select DVB_DIB7000M select DVB_DIB3000MC select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE help @@ -96,6 +98,7 @@ config DVB_USB_CXUSB depends on DVB_USB select DVB_CX22702 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE select DVB_MT352 if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE help @@ -157,6 +160,17 @@ config DVB_USB_NOVA_T_USB2 help Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver. +config DVB_USB_TTUSB2 + tristate "Pinnacle 400e DVB-S USB2.0 support" + depends on DVB_USB + select DVB_TDA10086 if !DVB_FE_CUSTOMISE + select DVB_LNBP21 if !DVB_FE_CUSTOMISE + select DVB_TDA826X if !DVB_FE_CUSTOMISE + help + Say Y here to support the Pinnacle 400e DVB-S USB2.0 receiver. The + firmware protocol used by this module is similar to the one used by the + old ttusb-driver - that's why the module is called dvb-usb-ttusb2.ko. + config DVB_USB_DTT200U tristate "WideView WT-200U and WT-220U (pen) DVB-T USB2.0 support (Yakumo/Hama/Typhoon/Yuan)" depends on DVB_USB diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index e239107..154d593 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -36,6 +36,9 @@ obj-$(CONFIG_DVB_USB_DIGITV) += dvb-usb- dvb-usb-cxusb-objs = cxusb.o obj-$(CONFIG_DVB_USB_CXUSB) += dvb-usb-cxusb.o +dvb-usb-ttusb2-objs = ttusb2.o +obj-$(CONFIG_DVB_USB_TTUSB2) += dvb-usb-ttusb2.o + dvb-usb-dib0700-objs = dib0700_core.o dib0700_devices.o obj-$(CONFIG_DVB_USB_DIB0700) += dvb-usb-dib0700.o diff --git a/drivers/media/dvb/dvb-usb/a800.c b/drivers/media/dvb/dvb-usb/a800.c index 2ed3eb6..a6c5f19 100644 --- a/drivers/media/dvb/dvb-usb/a800.c +++ b/drivers/media/dvb/dvb-usb/a800.c @@ -116,24 +116,24 @@ static struct dvb_usb_device_properties { .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, - .streaming_ctrl = dibusb2_0_streaming_ctrl, - .pid_filter = dibusb_pid_filter, - .pid_filter_ctrl = dibusb_pid_filter_ctrl, - - .frontend_attach = dibusb_dib3000mc_frontend_attach, - .tuner_attach = dibusb_dib3000mc_tuner_attach, - - /* parameter for the MPEG2-data transfer */ - .stream = { - .type = USB_BULK, - .count = 7, - .endpoint = 0x06, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + + .frontend_attach = dibusb_dib3000mc_frontend_attach, + .tuner_attach = dibusb_dib3000mc_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 7, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, .size_of_priv = sizeof(struct dibusb_state), }, diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index 43f3906..ae039b9 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/drivers/media/dvb/dvb-usb/cxusb.c @@ -14,7 +14,7 @@ * TODO: Use the cx25840-driver for the analogue part * * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de) - * Copyright (C) 2005 Michael Krufky (mkrufky@m1k.net) + * Copyright (C) 2006 Michael Krufky (mkrufky@linuxtv.org) * Copyright (C) 2006 Chris Pascoe (c.pascoe@itee.uq.edu.au) * * This program is free software; you can redistribute it and/or modify it @@ -27,7 +27,7 @@ #include "cxusb.h" #include "cx22702.h" #include "lgdt330x.h" -#include "lg_h06xf.h" +#include "lgh06xf.h" #include "mt352.h" #include "mt352_priv.h" #include "zl10353.h" @@ -324,13 +324,6 @@ static int cxusb_mt352_demod_init(struct return 0; } -static int cxusb_lgh064f_tuner_set_params(struct dvb_frontend *fe, - struct dvb_frontend_parameters *fep) -{ - struct dvb_usb_adapter *adap = fe->dvb->priv; - return lg_h06xf_pll_set(fe, &adap->dev->i2c_adap, fep); -} - static struct cx22702_config cxusb_cx22702_config = { .demod_address = 0x63, @@ -396,9 +389,9 @@ static int cxusb_dtt7579_tuner_attach(st return 0; } -static int cxusb_lgdt3303_tuner_attach(struct dvb_usb_adapter *adap) +static int cxusb_lgh064f_tuner_attach(struct dvb_usb_adapter *adap) { - adap->fe->ops.tuner_ops.set_params = cxusb_lgh064f_tuner_set_params; + dvb_attach(lgh06xf_attach, adap->fe, &adap->dev->i2c_adap); return 0; } @@ -529,20 +522,20 @@ static struct dvb_usb_device_properties .num_adapters = 1, .adapter = { { - .streaming_ctrl = cxusb_streaming_ctrl, - .frontend_attach = cxusb_cx22702_frontend_attach, - .tuner_attach = cxusb_fmd1216me_tuner_attach, - /* parameter for the MPEG2-data transfer */ - .stream = { - .type = USB_BULK, - .count = 5, - .endpoint = 0x02, - .u = { - .bulk = { - .buffersize = 8192, - } - } - }, + .streaming_ctrl = cxusb_streaming_ctrl, + .frontend_attach = cxusb_cx22702_frontend_attach, + .tuner_attach = cxusb_fmd1216me_tuner_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 5, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, }, }, @@ -575,21 +568,21 @@ static struct dvb_usb_device_properties .num_adapters = 1, .adapter = { { - .streaming_ctrl = cxusb_streaming_ctrl, - .frontend_attach = cxusb_lgdt3303_frontend_attach, - .tuner_attach = cxusb_lgdt3303_tuner_attach, - - /* parameter for the MPEG2-data transfer */ - .stream = { - .type = USB_BULK, - .count = 5, - .endpoint = 0x02, - .u = { - .bulk = { - .buffersize = 8192, - } - } - }, + .streaming_ctrl = cxusb_streaming_ctrl, + .frontend_attach = cxusb_lgdt3303_frontend_attach, + .tuner_attach = cxusb_lgh064f_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 5, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, }, }, @@ -627,20 +620,20 @@ static struct dvb_usb_device_properties .num_adapters = 1, .adapter = { { - .streaming_ctrl = cxusb_streaming_ctrl, - .frontend_attach = cxusb_dee1601_frontend_attach, - .tuner_attach = cxusb_dee1601_tuner_attach, - /* parameter for the MPEG2-data transfer */ + .streaming_ctrl = cxusb_streaming_ctrl, + .frontend_attach = cxusb_dee1601_frontend_attach, + .tuner_attach = cxusb_dee1601_tuner_attach, + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 5, - .endpoint = 0x04, - .u = { - .bulk = { - .buffersize = 8192, - } - } - }, + .count = 5, + .endpoint = 0x04, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, }, }, @@ -686,21 +679,21 @@ static struct dvb_usb_device_properties .num_adapters = 2, .adapter = { { - .streaming_ctrl = cxusb_streaming_ctrl, - .frontend_attach = cxusb_mt352_frontend_attach, - .tuner_attach = cxusb_lgz201_tuner_attach, + .streaming_ctrl = cxusb_streaming_ctrl, + .frontend_attach = cxusb_mt352_frontend_attach, + .tuner_attach = cxusb_lgz201_tuner_attach, - /* parameter for the MPEG2-data transfer */ + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 5, - .endpoint = 0x04, - .u = { - .bulk = { - .buffersize = 8192, - } - } - }, + .count = 5, + .endpoint = 0x04, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, }, }, .power_ctrl = cxusb_bluebird_power_ctrl, @@ -736,21 +729,21 @@ static struct dvb_usb_device_properties .num_adapters = 1, .adapter = { { - .streaming_ctrl = cxusb_streaming_ctrl, - .frontend_attach = cxusb_mt352_frontend_attach, - .tuner_attach = cxusb_dtt7579_tuner_attach, + .streaming_ctrl = cxusb_streaming_ctrl, + .frontend_attach = cxusb_mt352_frontend_attach, + .tuner_attach = cxusb_dtt7579_tuner_attach, - /* parameter for the MPEG2-data transfer */ + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 5, - .endpoint = 0x04, - .u = { - .bulk = { - .buffersize = 8192, - } - } - }, + .count = 5, + .endpoint = 0x04, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, }, }, .power_ctrl = cxusb_bluebird_power_ctrl, @@ -802,7 +795,7 @@ module_init (cxusb_module_init); module_exit (cxusb_module_exit); MODULE_AUTHOR("Patrick Boettcher "); -MODULE_AUTHOR("Michael Krufky "); +MODULE_AUTHOR("Michael Krufky "); MODULE_AUTHOR("Chris Pascoe "); MODULE_DESCRIPTION("Driver for Conexant USB2.0 hybrid reference design"); MODULE_VERSION("1.0-alpha"); diff --git a/drivers/media/dvb/dvb-usb/dib0700.h b/drivers/media/dvb/dvb-usb/dib0700.h index ac84347..cda3ade 100644 --- a/drivers/media/dvb/dvb-usb/dib0700.h +++ b/drivers/media/dvb/dvb-usb/dib0700.h @@ -24,18 +24,23 @@ #define REQUEST_I2C_READ 0x2 #define REQUEST_I2C_WRITE 0x3 #define REQUEST_POLL_RC 0x4 #define REQUEST_JUMPRAM 0x8 +#define REQUEST_SET_CLOCK 0xB #define REQUEST_SET_GPIO 0xC #define REQUEST_ENABLE_VIDEO 0xF // 1 Byte: 4MSB(1 = enable streaming, 0 = disable streaming) 4LSB(Video Mode: 0 = MPEG2 188Bytes, 1 = Analog) // 2 Byte: MPEG2 mode: 4MSB(1 = Master Mode, 0 = Slave Mode) 4LSB(Channel 1 = bit0, Channel 2 = bit1) // 2 Byte: Analog mode: 4MSB(0 = 625 lines, 1 = 525 lines) 4LSB( " " ) +#define REQUEST_GET_VERSION 0x15 struct dib0700_state { u8 channel_state; u16 mt2060_if1[2]; + + u8 is_dib7000pc; }; extern int dib0700_set_gpio(struct dvb_usb_device *, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val); +extern int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3); extern int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw); extern int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff); extern struct i2c_algorithm dib0700_i2c_algo; diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c index dca6c69..6a4d150 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_core.c +++ b/drivers/media/dvb/dvb-usb/dib0700_core.c @@ -135,14 +135,46 @@ struct i2c_algorithm dib0700_i2c_algo = int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props, struct dvb_usb_device_description **desc, int *cold) { - u8 buf[3] = { REQUEST_SET_GPIO, 4, (GPIO_IN << 7) | (0 << 6) }; // GPIO4 is save - used for I2C - *cold = usb_control_msg(udev, usb_sndctrlpipe(udev,0), - buf[0], USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, buf, 3, USB_CTRL_GET_TIMEOUT) != 3; + u8 b[16]; + s16 ret = usb_control_msg(udev, usb_rcvctrlpipe(udev,0), + REQUEST_GET_VERSION, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, b, 16, USB_CTRL_GET_TIMEOUT); + + deb_info("FW GET_VERSION length: %d\n",ret); + + *cold = ret <= 0; deb_info("cold: %d\n", *cold); return 0; } +static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll, + u8 pll_src, u8 pll_range, u8 clock_gpio3, u16 pll_prediv, + u16 pll_loopdiv, u16 free_div, u16 dsuScaler) +{ + u8 b[10]; + b[0] = REQUEST_SET_CLOCK; + b[1] = (en_pll << 7) | (pll_src << 6) | (pll_range << 5) | (clock_gpio3 << 4); + b[2] = (pll_prediv >> 8) & 0xff; // MSB + b[3] = pll_prediv & 0xff; // LSB + b[4] = (pll_loopdiv >> 8) & 0xff; // MSB + b[5] = pll_loopdiv & 0xff; // LSB + b[6] = (free_div >> 8) & 0xff; // MSB + b[7] = free_div & 0xff; // LSB + b[8] = (dsuScaler >> 8) & 0xff; // MSB + b[9] = dsuScaler & 0xff; // LSB + + return dib0700_ctrl_wr(d, b, 10); +} + +int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3) +{ + switch (clk_MHz) { + case 72: dib0700_set_clock(d, 1, 0, 1, clock_out_gp3, 2, 24, 0, 0x4c); break; + default: return -EINVAL; + } + return 0; +} + static int dib0700_jumpram(struct usb_device *udev, u32 address) { int ret, actlen; @@ -197,7 +229,7 @@ int dib0700_download_firmware(struct usb /* start the firmware */ if ((ret = dib0700_jumpram(udev, 0x70000000)) == 0) { info("firmware started successfully."); - msleep(100); + msleep(500); } } else ret = -EIO; diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index e473bfe..2208757 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -9,6 +9,8 @@ #include "dib0700.h" #include "dib3000mc.h" +#include "dib7000m.h" +#include "dib7000p.h" #include "mt2060.h" static int force_lna_activation; @@ -95,37 +97,189 @@ static int bristol_tuner_attach(struct d } /* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */ -/* -static struct mt2060_config stk7000p_mt2060_config = { - 0x60 +static struct dibx000_agc_config stk7700p_7000m_mt2060_agc_config = { + BAND_UHF | BAND_VHF, // band_caps + + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, + * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */ + (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), // setup + + 712, // inv_gain + 41, // time_stabiliz + + 0, // alpha_level + 118, // thlock + + 0, // wbd_inv + 4095, // wbd_ref + 0, // wbd_sel + 0, // wbd_alpha + + 42598, // agc1_max + 17694, // agc1_min + 45875, // agc2_max + 2621, // agc2_min + 0, // agc1_pt1 + 76, // agc1_pt2 + 139, // agc1_pt3 + 52, // agc1_slope1 + 59, // agc1_slope2 + 107, // agc2_pt1 + 172, // agc2_pt2 + 57, // agc2_slope1 + 70, // agc2_slope2 + + 21, // alpha_mant + 25, // alpha_exp + 28, // beta_mant + 48, // beta_exp + + 1, // perform_agc_softsplit + { 0, // split_min + 107, // split_max + 51800, // global_split_min + 24700 // global_split_max + }, +}; + +static struct dibx000_agc_config stk7700p_7000p_mt2060_agc_config = { + BAND_UHF | BAND_VHF, + + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, + * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */ + (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), // setup + + 712, // inv_gain + 41, // time_stabiliz + + 0, // alpha_level + 118, // thlock + + 0, // wbd_inv + 4095, // wbd_ref + 0, // wbd_sel + 0, // wbd_alpha + + 42598, // agc1_max + 16384, // agc1_min + 42598, // agc2_max + 0, // agc2_min + + 0, // agc1_pt1 + 137, // agc1_pt2 + 255, // agc1_pt3 + + 0, // agc1_slope1 + 255, // agc1_slope2 + + 0, // agc2_pt1 + 0, // agc2_pt2 + + 0, // agc2_slope1 + 41, // agc2_slope2 + + 15, // alpha_mant + 25, // alpha_exp + + 28, // beta_mant + 48, // beta_exp + + 0, // perform_agc_softsplit +}; + +static struct dibx000_bandwidth_config stk7700p_pll_config = { + 60000, 30000, // internal, sampling + 1, 8, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass + 0, 0, 1, 1, 0, // misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, modulo + (3 << 14) | (1 << 12) | (524 << 0), // sad_cfg: refsel, sel, freq_15k + 60258167, // ifreq + 20452225, // timf +}; + +static struct dib7000m_config stk7700p_dib7000m_config = { + .dvbt_mode = 1, + .output_mpeg2_in_188_bytes = 1, + .quartz_direct = 1, + + .agc_config_count = 1, + .agc = &stk7700p_7000m_mt2060_agc_config, + .bw = &stk7700p_pll_config, + + .gpio_dir = DIB7000M_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000M_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000M_GPIO_DEFAULT_PWM_POS, +}; + +static struct dib7000p_config stk7700p_dib7000p_config = { + .output_mpeg2_in_188_bytes = 1, + + .agc = &stk7700p_7000p_mt2060_agc_config, + .bw = &stk7700p_pll_config, + + .gpio_dir = DIB7000M_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000M_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000M_GPIO_DEFAULT_PWM_POS, }; -*/ static int stk7700p_frontend_attach(struct dvb_usb_adapter *adap) { + struct dib0700_state *st = adap->dev->priv; /* unless there is no real power management in DVB - we leave the device on GPIO6 */ - dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); msleep(10); - dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(10); - dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); msleep(10); + + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); msleep(50); + + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(10); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); msleep(10); + dib0700_ctrl_clock(adap->dev, 72, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); msleep(100); + + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); + + st->mt2060_if1[0] = 1220; + + if (dib7000pc_detection(&adap->dev->i2c_adap)) { + adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000p_config); + st->is_dib7000pc = 1; + } else + adap->fe = dvb_attach(dib7000m_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000m_config); -// adap->fe = dib7000m_attach(&adap->dev->i2c_adap, &stk7700p_dib7000m_config, 18); - return 0; + return adap->fe == NULL ? -ENODEV : 0; } +static struct mt2060_config stk7700p_mt2060_config = { + 0x60 +}; + static int stk7700p_tuner_attach(struct dvb_usb_adapter *adap) { -// tun_i2c = dib7000m_get_tuner_i2c_master(adap->fe, 1); -// return mt2060_attach(adap->fe, tun_i2c, &stk3000p_mt2060_config, if1); - return 0; + struct dib0700_state *st = adap->dev->priv; + struct i2c_adapter *tun_i2c; + + if (st->is_dib7000pc) + tun_i2c = dib7000p_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1); + else + tun_i2c = dib7000m_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1); + + return dvb_attach(mt2060_attach, adap->fe, tun_i2c, &stk7700p_mt2060_config, + st->mt2060_if1[0]) == NULL ? -ENODEV : 0; } struct usb_device_id dib0700_usb_id_table[] = { { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7700P) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7700P_PC) }, + { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500) }, { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500_2) }, { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK) }, { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR) }, + { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_VIDEOMATE_U500) }, + { USB_DEVICE(USB_VID_UNIWILL, USB_PID_UNIWILL_STK7700P) }, + { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_STK7700P) }, + { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK_2) }, + { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_2) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -167,20 +321,32 @@ struct dvb_usb_device_properties dib0700 }, }, - .num_device_descs = 3, + .num_device_descs = 6, .devices = { { "DiBcom STK7700P reference design", - { &dib0700_usb_id_table[0], NULL }, + { &dib0700_usb_id_table[0], &dib0700_usb_id_table[1] }, { NULL }, }, { "Hauppauge Nova-T Stick", - { &dib0700_usb_id_table[3], NULL }, + { &dib0700_usb_id_table[4], &dib0700_usb_id_table[9], NULL }, { NULL }, }, { "AVerMedia AVerTV DVB-T Volar", - { &dib0700_usb_id_table[4], NULL }, + { &dib0700_usb_id_table[5], &dib0700_usb_id_table[10] }, { NULL }, }, + { "Compro Videomate U500", + { &dib0700_usb_id_table[6], NULL }, + { NULL }, + }, + { "Uniwill STK7700P based (Hama and others)", + { &dib0700_usb_id_table[7], NULL }, + { NULL }, + }, + { "Leadtek Winfast DTV Dongle (STK7700P based)", + { &dib0700_usb_id_table[8], NULL }, + { NULL }, + } } }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, @@ -202,7 +368,7 @@ struct dvb_usb_device_properties dib0700 .num_device_descs = 1, .devices = { { "Hauppauge Nova-T 500 Dual DVB-T", - { &dib0700_usb_id_table[1], &dib0700_usb_id_table[2], NULL }, + { &dib0700_usb_id_table[2], &dib0700_usb_id_table[3], NULL }, { NULL }, }, } diff --git a/drivers/media/dvb/dvb-usb/dibusb-mb.c b/drivers/media/dvb/dvb-usb/dibusb-mb.c index 4fe363e..7a6ae8f 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-mb.c +++ b/drivers/media/dvb/dvb-usb/dibusb-mb.c @@ -163,23 +163,23 @@ static struct dvb_usb_device_properties .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 16, - .streaming_ctrl = dibusb_streaming_ctrl, - .pid_filter = dibusb_pid_filter, - .pid_filter_ctrl = dibusb_pid_filter_ctrl, - .frontend_attach = dibusb_dib3000mb_frontend_attach, - .tuner_attach = dibusb_tuner_probe_and_attach, + .streaming_ctrl = dibusb_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .frontend_attach = dibusb_dib3000mb_frontend_attach, + .tuner_attach = dibusb_tuner_probe_and_attach, - /* parameter for the MPEG2-data transfer */ + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 7, - .endpoint = 0x02, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, .size_of_priv = sizeof(struct dibusb_state), } }, @@ -248,23 +248,23 @@ static struct dvb_usb_device_properties .caps = DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF | DVB_USB_ADAP_HAS_PID_FILTER, .pid_filter_count = 16, - .streaming_ctrl = dibusb_streaming_ctrl, - .pid_filter = dibusb_pid_filter, - .pid_filter_ctrl = dibusb_pid_filter_ctrl, - .frontend_attach = dibusb_dib3000mb_frontend_attach, - .tuner_attach = dibusb_tuner_probe_and_attach, + .streaming_ctrl = dibusb_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .frontend_attach = dibusb_dib3000mb_frontend_attach, + .tuner_attach = dibusb_tuner_probe_and_attach, - /* parameter for the MPEG2-data transfer */ + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 7, - .endpoint = 0x02, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, .size_of_priv = sizeof(struct dibusb_state), }, }, @@ -312,22 +312,23 @@ static struct dvb_usb_device_properties .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 16, - .streaming_ctrl = dibusb2_0_streaming_ctrl, - .pid_filter = dibusb_pid_filter, - .pid_filter_ctrl = dibusb_pid_filter_ctrl, - .frontend_attach = dibusb_dib3000mb_frontend_attach, - .tuner_attach = dibusb_thomson_tuner_attach, - /* parameter for the MPEG2-data transfer */ + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .frontend_attach = dibusb_dib3000mb_frontend_attach, + .tuner_attach = dibusb_thomson_tuner_attach, + + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 7, - .endpoint = 0x06, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .count = 7, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, .size_of_priv = sizeof(struct dibusb_state), } }, @@ -369,22 +370,22 @@ static struct dvb_usb_device_properties .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 16, - .streaming_ctrl = dibusb2_0_streaming_ctrl, - .pid_filter = dibusb_pid_filter, - .pid_filter_ctrl = dibusb_pid_filter_ctrl, - .frontend_attach = dibusb_dib3000mb_frontend_attach, - .tuner_attach = dibusb_tuner_probe_and_attach, - /* parameter for the MPEG2-data transfer */ + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .frontend_attach = dibusb_dib3000mb_frontend_attach, + .tuner_attach = dibusb_tuner_probe_and_attach, + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 7, - .endpoint = 0x06, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .count = 7, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, .size_of_priv = sizeof(struct dibusb_state), } }, diff --git a/drivers/media/dvb/dvb-usb/dibusb-mc.c b/drivers/media/dvb/dvb-usb/dibusb-mc.c index a0fd37e..e7ea3e7 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-mc.c +++ b/drivers/media/dvb/dvb-usb/dibusb-mc.c @@ -54,23 +54,23 @@ static struct dvb_usb_device_properties { .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, - .streaming_ctrl = dibusb2_0_streaming_ctrl, - .pid_filter = dibusb_pid_filter, - .pid_filter_ctrl = dibusb_pid_filter_ctrl, - .frontend_attach = dibusb_dib3000mc_frontend_attach, - .tuner_attach = dibusb_dib3000mc_tuner_attach, + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .frontend_attach = dibusb_dib3000mc_frontend_attach, + .tuner_attach = dibusb_dib3000mc_tuner_attach, /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 7, - .endpoint = 0x06, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .count = 7, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, .size_of_priv = sizeof(struct dibusb_state), } }, diff --git a/drivers/media/dvb/dvb-usb/digitv.c b/drivers/media/dvb/dvb-usb/digitv.c index 8fb3437..4a198d4 100644 --- a/drivers/media/dvb/dvb-usb/digitv.c +++ b/drivers/media/dvb/dvb-usb/digitv.c @@ -274,20 +274,20 @@ static struct dvb_usb_device_properties .num_adapters = 1, .adapter = { { - .frontend_attach = digitv_frontend_attach, - .tuner_attach = digitv_tuner_attach, + .frontend_attach = digitv_frontend_attach, + .tuner_attach = digitv_tuner_attach, - /* parameter for the MPEG2-data transfer */ + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 7, - .endpoint = 0x02, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, } }, .identify_state = digitv_identify_state, diff --git a/drivers/media/dvb/dvb-usb/dtt200u.c b/drivers/media/dvb/dvb-usb/dtt200u.c index fa43a41..7dbe143 100644 --- a/drivers/media/dvb/dvb-usb/dtt200u.c +++ b/drivers/media/dvb/dvb-usb/dtt200u.c @@ -268,20 +268,20 @@ static struct dvb_usb_device_properties .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING, .pid_filter_count = 15, - .streaming_ctrl = dtt200u_streaming_ctrl, - .pid_filter = dtt200u_pid_filter, - .frontend_attach = dtt200u_frontend_attach, - /* parameter for the MPEG2-data transfer */ + .streaming_ctrl = dtt200u_streaming_ctrl, + .pid_filter = dtt200u_pid_filter, + .frontend_attach = dtt200u_frontend_attach, + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 7, - .endpoint = 0x02, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, } }, .power_ctrl = dtt200u_power_ctrl, diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 4d6b069..6a55ea2 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -32,6 +32,7 @@ #define USB_VID_MEDION 0x1660 #define USB_VID_PINNACLE 0x2304 #define USB_VID_VISIONPLUS 0x13d3 #define USB_VID_TWINHAN 0x1822 +#define USB_VID_UNIWILL 0x1584 #define USB_VID_ULTIMA_ELECTRONIC 0x05d8 #define USB_VID_WIDEVIEW 0x14aa @@ -46,6 +47,7 @@ #define USB_PID_COMPRO_DVBU2000_COLD 0 #define USB_PID_COMPRO_DVBU2000_WARM 0xd001 #define USB_PID_COMPRO_DVBU2000_UNK_COLD 0x010c #define USB_PID_COMPRO_DVBU2000_UNK_WARM 0x010d +#define USB_PID_COMPRO_VIDEOMATE_U500 0x1e78 #define USB_PID_DIBCOM_HOOK_DEFAULT 0x0064 #define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM 0x0065 #define USB_PID_DIBCOM_MOD3000_COLD 0x0bb8 @@ -53,7 +55,9 @@ #define USB_PID_DIBCOM_MOD3000_WARM 0x #define USB_PID_DIBCOM_MOD3001_COLD 0x0bc6 #define USB_PID_DIBCOM_MOD3001_WARM 0x0bc7 #define USB_PID_DIBCOM_STK7700P 0x1e14 +#define USB_PID_DIBCOM_STK7700P_PC 0x1e78 #define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131 +#define USB_PID_UNIWILL_STK7700P 0x6003 #define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0 #define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1 #define USB_PID_KWORLD_VSTREAM_COLD 0x17de @@ -97,7 +101,9 @@ #define USB_PID_WINTV_NOVA_T_USB2_WARM #define USB_PID_HAUPPAUGE_NOVA_T_500 0x9941 #define USB_PID_HAUPPAUGE_NOVA_T_500_2 0x9950 #define USB_PID_HAUPPAUGE_NOVA_T_STICK 0x7050 -#define USB_PID_AVERMEDIA_VOLAR 0x1234 +#define USB_PID_HAUPPAUGE_NOVA_T_STICK_2 0x7060 +#define USB_PID_AVERMEDIA_VOLAR 0xa807 +#define USB_PID_AVERMEDIA_VOLAR_2 0xb808 #define USB_PID_NEBULA_DIGITV 0x0201 #define USB_PID_DVICO_BLUEBIRD_LGDT 0xd820 #define USB_PID_DVICO_BLUEBIRD_LG064F_COLD 0xd500 @@ -110,8 +116,8 @@ #define USB_PID_DVICO_BLUEBIRD_DUAL_1_CO #define USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM 0xdb51 #define USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD 0xdb58 #define USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM 0xdb59 -#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD 0xdb54 -#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM 0xdb55 +#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD 0xdb54 +#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM 0xdb55 #define USB_PID_MEDION_MD95700 0x0932 #define USB_PID_KYE_DVB_T_COLD 0x701e #define USB_PID_KYE_DVB_T_WARM 0x701f @@ -125,7 +131,9 @@ #define USB_PID_GRANDTEC_DVBT_USB2_COLD #define USB_PID_GRANDTEC_DVBT_USB2_WARM 0x0bc7 #define USB_PID_WINFAST_DTV_DONGLE_COLD 0x6025 #define USB_PID_WINFAST_DTV_DONGLE_WARM 0x6026 +#define USB_PID_WINFAST_DTV_DONGLE_STK7700P 0x6f00 #define USB_PID_GENPIX_8PSK_COLD 0x0200 #define USB_PID_GENPIX_8PSK_WARM 0x0201 + #endif diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c index 0a3a0b6..fac0596 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c @@ -89,7 +89,9 @@ schedule: int dvb_usb_remote_init(struct dvb_usb_device *d) { + struct input_dev *input_dev; int i; + int err; if (d->props.rc_key_map == NULL || d->props.rc_query == NULL || @@ -99,23 +101,24 @@ int dvb_usb_remote_init(struct dvb_usb_d usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys)); strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys)); - d->rc_input_dev = input_allocate_device(); - if (!d->rc_input_dev) + input_dev = input_allocate_device(); + if (!input_dev) return -ENOMEM; - d->rc_input_dev->evbit[0] = BIT(EV_KEY); - d->rc_input_dev->keycodesize = sizeof(unsigned char); - d->rc_input_dev->keycodemax = KEY_MAX; - d->rc_input_dev->name = "IR-receiver inside an USB DVB receiver"; - d->rc_input_dev->phys = d->rc_phys; - usb_to_input_id(d->udev, &d->rc_input_dev->id); - d->rc_input_dev->cdev.dev = &d->udev->dev; + input_dev->evbit[0] = BIT(EV_KEY); + input_dev->keycodesize = sizeof(unsigned char); + input_dev->keycodemax = KEY_MAX; + input_dev->name = "IR-receiver inside an USB DVB receiver"; + input_dev->phys = d->rc_phys; + usb_to_input_id(d->udev, &input_dev->id); + input_dev->cdev.dev = &d->udev->dev; /* set the bits for the keys */ deb_rc("key map size: %d\n", d->props.rc_key_map_size); for (i = 0; i < d->props.rc_key_map_size; i++) { - deb_rc("setting bit for event %d item %d\n",d->props.rc_key_map[i].event, i); - set_bit(d->props.rc_key_map[i].event, d->rc_input_dev->keybit); + deb_rc("setting bit for event %d item %d\n", + d->props.rc_key_map[i].event, i); + set_bit(d->props.rc_key_map[i].event, input_dev->keybit); } /* Start the remote-control polling. */ @@ -123,10 +126,16 @@ int dvb_usb_remote_init(struct dvb_usb_d d->props.rc_interval = 100; /* default */ /* setting these two values to non-zero, we have to manage key repeats */ - d->rc_input_dev->rep[REP_PERIOD] = d->props.rc_interval; - d->rc_input_dev->rep[REP_DELAY] = d->props.rc_interval + 150; + input_dev->rep[REP_PERIOD] = d->props.rc_interval; + input_dev->rep[REP_DELAY] = d->props.rc_interval + 150; - input_register_device(d->rc_input_dev); + err = input_register_device(input_dev); + if (err) { + input_free_device(input_dev); + return err; + } + + d->rc_input_dev = input_dev; INIT_WORK(&d->rc_query_work, dvb_usb_read_remote_control, d); diff --git a/drivers/media/dvb/dvb-usb/gp8psk.c b/drivers/media/dvb/dvb-usb/gp8psk.c index 7375eb2..518d67f 100644 --- a/drivers/media/dvb/dvb-usb/gp8psk.c +++ b/drivers/media/dvb/dvb-usb/gp8psk.c @@ -194,19 +194,19 @@ static struct dvb_usb_device_properties .num_adapters = 1, .adapter = { { - .streaming_ctrl = gp8psk_streaming_ctrl, - .frontend_attach = gp8psk_frontend_attach, - /* parameter for the MPEG2-data transfer */ + .streaming_ctrl = gp8psk_streaming_ctrl, + .frontend_attach = gp8psk_frontend_attach, + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 7, - .endpoint = 0x82, - .u = { - .bulk = { - .buffersize = 8192, - } - } - }, + .count = 7, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, } }, .power_ctrl = gp8psk_power_ctrl, diff --git a/drivers/media/dvb/dvb-usb/nova-t-usb2.c b/drivers/media/dvb/dvb-usb/nova-t-usb2.c index a58874c..d48622e 100644 --- a/drivers/media/dvb/dvb-usb/nova-t-usb2.c +++ b/drivers/media/dvb/dvb-usb/nova-t-usb2.c @@ -163,23 +163,23 @@ static struct dvb_usb_device_properties .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, - .streaming_ctrl = dibusb2_0_streaming_ctrl, - .pid_filter = dibusb_pid_filter, - .pid_filter_ctrl = dibusb_pid_filter_ctrl, - .frontend_attach = dibusb_dib3000mc_frontend_attach, - .tuner_attach = dibusb_dib3000mc_tuner_attach, - - /* parameter for the MPEG2-data transfer */ - .stream = { - .type = USB_BULK, - .count = 7, - .endpoint = 0x06, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .frontend_attach = dibusb_dib3000mc_frontend_attach, + .tuner_attach = dibusb_dib3000mc_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 7, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, .size_of_priv = sizeof(struct dibusb_state), } diff --git a/drivers/media/dvb/dvb-usb/ttusb2.c b/drivers/media/dvb/dvb-usb/ttusb2.c new file mode 100644 index 0000000..95d2997 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/ttusb2.c @@ -0,0 +1,270 @@ +/* DVB USB compliant linux driver for Technotrend DVB USB boxes and clones + * (e.g. Pinnacle 400e DVB-S USB2.0). + * + * The Pinnacle 400e uses the same protocol as the Technotrend USB1.1 boxes. + * + * TDA8263 + TDA10086 + * + * I2C addresses: + * 0x08 - LNBP21PD - LNB power supply + * 0x0e - TDA10086 - Demodulator + * 0x50 - FX2 eeprom + * 0x60 - TDA8263 - Tuner + * 0x78 ??? + * + * Copyright (c) 2002 Holger Waechtler + * Copyright (c) 2003 Felix Domke + * Copyright (C) 2005-6 Patrick Boettcher + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#define DVB_USB_LOG_PREFIX "ttusb2" +#include "dvb-usb.h" + +#include "ttusb2.h" + +#include "tda826x.h" +#include "tda10086.h" +#include "lnbp21.h" + +/* debug */ +static int dvb_usb_ttusb2_debug; +#define deb_info(args...) dprintk(dvb_usb_ttusb2_debug,0x01,args) +module_param_named(debug,dvb_usb_ttusb2_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))." DVB_USB_DEBUG_STATUS); + +struct ttusb2_state { + u8 id; +}; + +static int ttusb2_msg(struct dvb_usb_device *d, u8 cmd, + u8 *wbuf, int wlen, u8 *rbuf, int rlen) +{ + struct ttusb2_state *st = d->priv; + u8 s[wlen+4],r[64] = { 0 }; + int ret = 0; + + memset(s,0,wlen+4); + + s[0] = 0xaa; + s[1] = ++st->id; + s[2] = cmd; + s[3] = wlen; + memcpy(&s[4],wbuf,wlen); + + ret = dvb_usb_generic_rw(d, s, wlen+4, r, 64, 0); + + if (ret != 0 || + r[0] != 0x55 || + r[1] != s[1] || + r[2] != cmd || + (rlen > 0 && r[3] != rlen)) { + warn("there might have been an error during control message transfer. (rlen = %d, was %d)",rlen,r[3]); + return -EIO; + } + + if (rlen > 0) + memcpy(rbuf, &r[4], rlen); + + return 0; +} + +static int ttusb2_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + static u8 obuf[60], ibuf[60]; + int i,read; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + if (num > 2) + warn("more than 2 i2c messages at a time is not handled yet. TODO."); + + for (i = 0; i < num; i++) { + read = i+1 < num && (msg[i+1].flags & I2C_M_RD); + + obuf[0] = (msg[i].addr << 1) | read; + obuf[1] = msg[i].len; + + /* read request */ + if (read) + obuf[2] = msg[i+1].len; + else + obuf[2] = 0; + + memcpy(&obuf[3],msg[i].buf,msg[i].len); + + if (ttusb2_msg(d, CMD_I2C_XFER, obuf, msg[i].len+3, ibuf, obuf[2] + 3) < 0) { + err("i2c transfer failed."); + break; + } + + if (read) { + memcpy(msg[i+1].buf,&ibuf[3],msg[i+1].len); + i++; + } + } + + mutex_unlock(&d->i2c_mutex); + return i; +} + +static u32 ttusb2_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm ttusb2_i2c_algo = { + .master_xfer = ttusb2_i2c_xfer, + .functionality = ttusb2_i2c_func, +}; + +/* Callbacks for DVB USB */ +static int ttusb2_identify_state (struct usb_device *udev, struct + dvb_usb_device_properties *props, struct dvb_usb_device_description **desc, + int *cold) +{ + *cold = udev->descriptor.iManufacturer == 0 && udev->descriptor.iProduct == 0; + return 0; +} + +static int ttusb2_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + u8 b = onoff; + ttusb2_msg(d, CMD_POWER, &b, 0, NULL, 0); + return ttusb2_msg(d, CMD_POWER, &b, 1, NULL, 0); +} + + +static struct tda10086_config tda10086_config = { + .demod_address = 0x0e, + .invert = 0, +}; + +static int ttusb2_frontend_attach(struct dvb_usb_adapter *adap) +{ + if (usb_set_interface(adap->dev->udev,0,3) < 0) + err("set interface to alts=3 failed"); + + if ((adap->fe = dvb_attach(tda10086_attach, &tda10086_config, &adap->dev->i2c_adap)) == NULL) { + deb_info("TDA10086 attach failed\n"); + return -ENODEV; + } + + return 0; +} + +static int ttusb2_tuner_attach(struct dvb_usb_adapter *adap) +{ + if (dvb_attach(tda826x_attach, adap->fe, 0x60, &adap->dev->i2c_adap, 0) == NULL) { + deb_info("TDA8263 attach failed\n"); + return -ENODEV; + } + + if (dvb_attach(lnbp21_attach, adap->fe, &adap->dev->i2c_adap, 0, 0) == NULL) { + deb_info("LNBP21 attach failed\n"); + return -ENODEV; + } + return 0; +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties ttusb2_properties; + +static int ttusb2_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return dvb_usb_device_init(intf,&ttusb2_properties,THIS_MODULE,NULL); +} + +static struct usb_device_id ttusb2_table [] = { + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_400E) }, + {} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, ttusb2_table); + +static struct dvb_usb_device_properties ttusb2_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-pctv-400e-01.fw", + + .size_of_priv = sizeof(struct ttusb2_state), + + .num_adapters = 1, + .adapter = { + { + .streaming_ctrl = NULL, // ttusb2_streaming_ctrl, + + .frontend_attach = ttusb2_frontend_attach, + .tuner_attach = ttusb2_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_ISOC, + .count = 5, + .endpoint = 0x02, + .u = { + .isoc = { + .framesperurb = 4, + .framesize = 940, + .interval = 1, + } + } + } + } + }, + + .power_ctrl = ttusb2_power_ctrl, + .identify_state = ttusb2_identify_state, + + .i2c_algo = &ttusb2_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 1, + .devices = { + { "Pinnacle 400e DVB-S USB2.0", + { &ttusb2_table[0], NULL }, + { NULL }, + }, + } +}; + +static struct usb_driver ttusb2_driver = { + .name = "dvb_usb_ttusb2", + .probe = ttusb2_probe, + .disconnect = dvb_usb_device_exit, + .id_table = ttusb2_table, +}; + +/* module stuff */ +static int __init ttusb2_module_init(void) +{ + int result; + if ((result = usb_register(&ttusb2_driver))) { + err("usb_register failed. Error number %d",result); + return result; + } + + return 0; +} + +static void __exit ttusb2_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&ttusb2_driver); +} + +module_init (ttusb2_module_init); +module_exit (ttusb2_module_exit); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for Pinnacle PCTV 400e DVB-S USB2.0"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/ttusb2.h b/drivers/media/dvb/dvb-usb/ttusb2.h new file mode 100644 index 0000000..52a63af --- /dev/null +++ b/drivers/media/dvb/dvb-usb/ttusb2.h @@ -0,0 +1,70 @@ +/* DVB USB compliant linux driver for Technotrend DVB USB boxes and clones + * (e.g. Pinnacle 400e DVB-S USB2.0). + * + * Copyright (c) 2002 Holger Waechtler + * Copyright (c) 2003 Felix Domke + * Copyright (C) 2005-6 Patrick Boettcher + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#ifndef _DVB_USB_TTUSB2_H_ +#define _DVB_USB_TTUSB2_H_ + +/* TTUSB protocol + * + * always to messages (out/in) + * out message: + * 0xaa + * + * in message (complete block is always 0x40 bytes long) + * 0x55 + * + * id is incremented for each transaction + */ + +#define CMD_DSP_DOWNLOAD 0x13 +/* out data: [28] + * last block must be empty */ + +#define CMD_DSP_BOOT 0x14 +/* out data: nothing */ + +#define CMD_POWER 0x15 +/* out data: */ + +#define CMD_LNB 0x16 +/* out data: <18V=0,13V=1> */ + +#define CMD_GET_VERSION 0x17 +/* in data: [5] */ + +#define CMD_DISEQC 0x18 +/* out data: [cmdlen] */ + +#define CMD_PID_ENABLE 0x22 +/* out data: */ + +#define CMD_PID_DISABLE 0x23 +/* out data: */ + +#define CMD_FILTER_ENABLE 0x24 +/* out data: [12] [12] */ + +#define CMD_FILTER_DISABLE 0x25 +/* out data: */ + +#define CMD_GET_DSP_VERSION 0x26 +/* in data: [28] */ + +#define CMD_I2C_XFER 0x31 +/* out data: [sndlen] + * in data: [rcvlen] */ + +#define CMD_I2C_BITRATE 0x32 +/* out data: */ + +#endif diff --git a/drivers/media/dvb/dvb-usb/umt-010.c b/drivers/media/dvb/dvb-usb/umt-010.c index f9941ea..f77b48f 100644 --- a/drivers/media/dvb/dvb-usb/umt-010.c +++ b/drivers/media/dvb/dvb-usb/umt-010.c @@ -99,21 +99,21 @@ static struct dvb_usb_device_properties .num_adapters = 1, .adapter = { { - .streaming_ctrl = dibusb2_0_streaming_ctrl, - .frontend_attach = umt_mt352_frontend_attach, - .tuner_attach = umt_tuner_attach, + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .frontend_attach = umt_mt352_frontend_attach, + .tuner_attach = umt_tuner_attach, - /* parameter for the MPEG2-data transfer */ + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 20, - .endpoint = 0x06, - .u = { - .bulk = { - .buffersize = 512, - } - } - }, + .count = 20, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 512, + } + } + }, .size_of_priv = sizeof(struct dibusb_state), } diff --git a/drivers/media/dvb/dvb-usb/vp702x.c b/drivers/media/dvb/dvb-usb/vp702x.c index 02bd61a..16533b3 100644 --- a/drivers/media/dvb/dvb-usb/vp702x.c +++ b/drivers/media/dvb/dvb-usb/vp702x.c @@ -275,22 +275,22 @@ static struct dvb_usb_device_properties .caps = DVB_USB_ADAP_RECEIVES_204_BYTE_TS, .streaming_ctrl = vp702x_streaming_ctrl, - .frontend_attach = vp702x_frontend_attach, + .frontend_attach = vp702x_frontend_attach, - /* parameter for the MPEG2-data transfer */ + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, .count = 10, - .endpoint = 0x02, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, .size_of_priv = sizeof(struct vp702x_state), } - }, + }, .read_mac_address = vp702x_read_mac_addr, .rc_key_map = vp702x_rc_keys, diff --git a/drivers/media/dvb/dvb-usb/vp7045.c b/drivers/media/dvb/dvb-usb/vp7045.c index b4cf002..69a46b3 100644 --- a/drivers/media/dvb/dvb-usb/vp7045.c +++ b/drivers/media/dvb/dvb-usb/vp7045.c @@ -125,7 +125,25 @@ static struct dvb_usb_rc_key vp7045_rc_k { 0x00, 0x00, KEY_TAB }, /* Tab */ { 0x00, 0x48, KEY_INFO }, /* Preview */ { 0x00, 0x04, KEY_LIST }, /* RecordList */ - { 0x00, 0x0f, KEY_TEXT } /* Teletext */ + { 0x00, 0x0f, KEY_TEXT }, /* Teletext */ + { 0x00, 0x41, KEY_PREVIOUSSONG }, + { 0x00, 0x42, KEY_NEXTSONG }, + { 0x00, 0x4b, KEY_UP }, + { 0x00, 0x51, KEY_DOWN }, + { 0x00, 0x4e, KEY_LEFT }, + { 0x00, 0x52, KEY_RIGHT }, + { 0x00, 0x4f, KEY_ENTER }, + { 0x00, 0x13, KEY_CANCEL }, + { 0x00, 0x4a, KEY_CLEAR }, + { 0x00, 0x54, KEY_PRINT }, /* Capture */ + { 0x00, 0x43, KEY_SUBTITLE }, /* Subtitle/CC */ + { 0x00, 0x08, KEY_VIDEO }, /* A/V */ + { 0x00, 0x07, KEY_SLEEP }, /* Hibernate */ + { 0x00, 0x45, KEY_ZOOM }, /* Zoom+ */ + { 0x00, 0x18, KEY_RED}, + { 0x00, 0x53, KEY_GREEN}, + { 0x00, 0x5e, KEY_YELLOW}, + { 0x00, 0x5f, KEY_BLUE} }; static int vp7045_rc_query(struct dvb_usb_device *d, u32 *event, int *state) @@ -217,18 +235,18 @@ static struct dvb_usb_device_properties .num_adapters = 1, .adapter = { { - .frontend_attach = vp7045_frontend_attach, - /* parameter for the MPEG2-data transfer */ + .frontend_attach = vp7045_frontend_attach, + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 7, - .endpoint = 0x02, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, } }, .power_ctrl = vp7045_power_ctrl, diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index aebb8d6..af314bb 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -172,6 +172,22 @@ config DVB_DIB3000MC A DVB-T tuner module. Designed for mobile usage. Say Y when you want to support this frontend. +config DVB_DIB7000M + tristate "DiBcom 7000MA/MB/PA/PB/MC" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-T tuner module. Designed for mobile usage. Say Y when you want + to support this frontend. + +config DVB_DIB7000P + tristate "DiBcom 7000PC" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-T tuner module. Designed for mobile usage. Say Y when you want + to support this frontend. + comment "DVB-C (cable) frontends" depends on DVB_CORE @@ -281,6 +297,14 @@ config DVB_TUNER_MT2060 help A driver for the silicon IF tuner MT2060 from Microtune. +config DVB_TUNER_LGH06XF + tristate "LG TDVS-H06xF ATSC tuner" + depends on DVB_CORE && I2C + select DVB_PLL + default m if DVB_FE_CUSTOMISE + help + A driver for the LG TDVS-H06xF ATSC tuner family. + comment "Miscellaneous devices" depends on DVB_CORE diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index dce9cf0..3fa6e5d 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -13,6 +13,8 @@ obj-$(CONFIG_DVB_TDA8083) += tda8083.o obj-$(CONFIG_DVB_L64781) += l64781.o obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dibx000_common.o +obj-$(CONFIG_DVB_DIB7000M) += dib7000m.o dibx000_common.o +obj-$(CONFIG_DVB_DIB7000P) += dib7000p.o dibx000_common.o obj-$(CONFIG_DVB_MT312) += mt312.o obj-$(CONFIG_DVB_VES1820) += ves1820.o obj-$(CONFIG_DVB_VES1X93) += ves1x93.o @@ -37,3 +39,4 @@ obj-$(CONFIG_DVB_TDA10086) += tda10086.o obj-$(CONFIG_DVB_TDA826X) += tda826x.o obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o obj-$(CONFIG_DVB_TUA6100) += tua6100.o +obj-$(CONFIG_DVB_TUNER_LGH06XF) += lgh06xf.o diff --git a/drivers/media/dvb/frontends/dib3000mc.c b/drivers/media/dvb/frontends/dib3000mc.c index 3561a77..5da6617 100644 --- a/drivers/media/dvb/frontends/dib3000mc.c +++ b/drivers/media/dvb/frontends/dib3000mc.c @@ -511,16 +511,11 @@ static int dib3000mc_autosearch_start(st /* a channel for autosearch */ - reg = 0; - if (chan->nfft == -1 && chan->guard == -1) reg = 7; - if (chan->nfft == -1 && chan->guard != -1) reg = 2; - if (chan->nfft != -1 && chan->guard == -1) reg = 3; - fchan.nfft = 1; fchan.guard = 0; fchan.nqam = 2; fchan.vit_alpha = 1; fchan.vit_code_rate_hp = 2; fchan.vit_code_rate_lp = 2; fchan.vit_hrch = 0; fchan.vit_select_hp = 1; - dib3000mc_set_channel_cfg(state, &fchan, reg); + dib3000mc_set_channel_cfg(state, &fchan, 7); reg = dib3000mc_read_word(state, 0); dib3000mc_write_word(state, 0, reg | (1 << 8)); diff --git a/drivers/media/dvb/frontends/dib7000m.c b/drivers/media/dvb/frontends/dib7000m.c new file mode 100644 index 0000000..f5d40aa --- /dev/null +++ b/drivers/media/dvb/frontends/dib7000m.c @@ -0,0 +1,1191 @@ +/* + * Linux-DVB Driver for DiBcom's DiB7000M and + * first generation DiB7000P-demodulator-family. + * + * Copyright (C) 2005-6 DiBcom (http://www.dibcom.fr/) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + */ +#include +#include + +#include "dvb_frontend.h" + +#include "dib7000m.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); + +#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB7000M:"); printk(args); } } while (0) + +struct dib7000m_state { + struct dvb_frontend demod; + struct dib7000m_config cfg; + + u8 i2c_addr; + struct i2c_adapter *i2c_adap; + + struct dibx000_i2c_master i2c_master; + +/* offset is 1 in case of the 7000MC */ + u8 reg_offs; + + u16 wbd_ref; + + u8 current_band; + fe_bandwidth_t current_bandwidth; + struct dibx000_agc_config *current_agc; + u32 timf; + + u16 revision; +}; + +enum dib7000m_power_mode { + DIB7000M_POWER_ALL = 0, + + DIB7000M_POWER_NO, + DIB7000M_POWER_INTERF_ANALOG_AGC, + DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD, + DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD, + DIB7000M_POWER_INTERFACE_ONLY, +}; + +static u16 dib7000m_read_word(struct dib7000m_state *state, u16 reg) +{ + u8 wb[2] = { (reg >> 8) | 0x80, reg & 0xff }; + u8 rb[2]; + struct i2c_msg msg[2] = { + { .addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2 }, + { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 }, + }; + + if (i2c_transfer(state->i2c_adap, msg, 2) != 2) + dprintk("i2c read error on %d\n",reg); + + return (rb[0] << 8) | rb[1]; +} + +static int dib7000m_write_word(struct dib7000m_state *state, u16 reg, u16 val) +{ + u8 b[4] = { + (reg >> 8) & 0xff, reg & 0xff, + (val >> 8) & 0xff, val & 0xff, + }; + struct i2c_msg msg = { + .addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4 + }; + return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; +} +static int dib7000m_set_output_mode(struct dib7000m_state *state, int mode) +{ + int ret = 0; + u16 outreg, fifo_threshold, smo_mode, + sram = 0x0005; /* by default SRAM output is disabled */ + + outreg = 0; + fifo_threshold = 1792; + smo_mode = (dib7000m_read_word(state, 294 + state->reg_offs) & 0x0010) | (1 << 1); + + dprintk("-I- Setting output mode for demod %p to %d\n", + &state->demod, mode); + + switch (mode) { + case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock + outreg = (1 << 10); /* 0x0400 */ + break; + case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock + outreg = (1 << 10) | (1 << 6); /* 0x0440 */ + break; + case OUTMODE_MPEG2_SERIAL: // STBs with serial input + outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0482 */ + break; + case OUTMODE_DIVERSITY: + if (state->cfg.hostbus_diversity) + outreg = (1 << 10) | (4 << 6); /* 0x0500 */ + else + sram |= 0x0c00; + break; + case OUTMODE_MPEG2_FIFO: // e.g. USB feeding + smo_mode |= (3 << 1); + fifo_threshold = 512; + outreg = (1 << 10) | (5 << 6); + break; + case OUTMODE_HIGH_Z: // disable + outreg = 0; + break; + default: + dprintk("Unhandled output_mode passed to be set for demod %p\n",&state->demod); + break; + } + + if (state->cfg.output_mpeg2_in_188_bytes) + smo_mode |= (1 << 5) ; + + ret |= dib7000m_write_word(state, 294 + state->reg_offs, smo_mode); + ret |= dib7000m_write_word(state, 295 + state->reg_offs, fifo_threshold); /* synchronous fread */ + ret |= dib7000m_write_word(state, 1795, outreg); + ret |= dib7000m_write_word(state, 1805, sram); + + return ret; +} + +static int dib7000m_set_power_mode(struct dib7000m_state *state, enum dib7000m_power_mode mode) +{ + /* by default everything is going to be powered off */ + u16 reg_903 = 0xffff, reg_904 = 0xffff, reg_905 = 0xffff, reg_906 = 0x3fff; + + /* now, depending on the requested mode, we power on */ + switch (mode) { + /* power up everything in the demod */ + case DIB7000M_POWER_ALL: + reg_903 = 0x0000; reg_904 = 0x0000; reg_905 = 0x0000; reg_906 = 0x0000; + break; + + /* just leave power on the control-interfaces: GPIO and (I2C or SDIO or SRAM) */ + case DIB7000M_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C or SRAM */ + reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 2)); + break; + + case DIB7000M_POWER_INTERF_ANALOG_AGC: + reg_903 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10)); + reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 2)); + reg_906 &= ~((1 << 0)); + break; + + case DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD: + reg_903 = 0x0000; reg_904 = 0x801f; reg_905 = 0x0000; reg_906 = 0x0000; + break; + + case DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD: + reg_903 = 0x0000; reg_904 = 0x8000; reg_905 = 0x010b; reg_906 = 0x0000; + break; + case DIB7000M_POWER_NO: + break; + } + + /* always power down unused parts */ + if (!state->cfg.mobile_mode) + reg_904 |= (1 << 7) | (1 << 6) | (1 << 4) | (1 << 2) | (1 << 1); + + /* P_sdio_select_clk = 0 on MC */ + if (state->revision != 0x4000) + reg_906 <<= 1; + + dib7000m_write_word(state, 903, reg_903); + dib7000m_write_word(state, 904, reg_904); + dib7000m_write_word(state, 905, reg_905); + dib7000m_write_word(state, 906, reg_906); + + return 0; +} + +static int dib7000m_set_adc_state(struct dib7000m_state *state, enum dibx000_adc_states no) +{ + int ret = 0; + u16 reg_913 = dib7000m_read_word(state, 913), + reg_914 = dib7000m_read_word(state, 914); + + switch (no) { + case DIBX000_SLOW_ADC_ON: + reg_914 |= (1 << 1) | (1 << 0); + ret |= dib7000m_write_word(state, 914, reg_914); + reg_914 &= ~(1 << 1); + break; + + case DIBX000_SLOW_ADC_OFF: + reg_914 |= (1 << 1) | (1 << 0); + break; + + case DIBX000_ADC_ON: + if (state->revision == 0x4000) { // workaround for PA/MA + // power-up ADC + dib7000m_write_word(state, 913, 0); + dib7000m_write_word(state, 914, reg_914 & 0x3); + // power-down bandgag + dib7000m_write_word(state, 913, (1 << 15)); + dib7000m_write_word(state, 914, reg_914 & 0x3); + } + + reg_913 &= 0x0fff; + reg_914 &= 0x0003; + break; + + case DIBX000_ADC_OFF: // leave the VBG voltage on + reg_913 |= (1 << 14) | (1 << 13) | (1 << 12); + reg_914 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2); + break; + + case DIBX000_VBG_ENABLE: + reg_913 &= ~(1 << 15); + break; + + case DIBX000_VBG_DISABLE: + reg_913 |= (1 << 15); + break; + + default: + break; + } + +// dprintk("-D- 913: %x, 914: %x\n", reg_913, reg_914); + + ret |= dib7000m_write_word(state, 913, reg_913); + ret |= dib7000m_write_word(state, 914, reg_914); + + return ret; +} + +static int dib7000m_set_bandwidth(struct dvb_frontend *demod, u8 bw_idx) +{ + struct dib7000m_state *state = demod->demodulator_priv; + u32 timf; + + // store the current bandwidth for later use + state->current_bandwidth = bw_idx; + + if (state->timf == 0) { + dprintk("-D- Using default timf\n"); + timf = state->cfg.bw->timf; + } else { + dprintk("-D- Using updated timf\n"); + timf = state->timf; + } + + timf = timf * (BW_INDEX_TO_KHZ(bw_idx) / 100) / 80; + + dib7000m_write_word(state, 23, (timf >> 16) & 0xffff); + dib7000m_write_word(state, 24, (timf ) & 0xffff); + + return 0; +} + +static int dib7000m_sad_calib(struct dib7000m_state *state) +{ + +/* internal */ +// dib7000m_write_word(state, 928, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writting in set_bandwidth + dib7000m_write_word(state, 929, (0 << 1) | (0 << 0)); + dib7000m_write_word(state, 930, 776); // 0.625*3.3 / 4096 + + /* do the calibration */ + dib7000m_write_word(state, 929, (1 << 0)); + dib7000m_write_word(state, 929, (0 << 0)); + + msleep(1); + + return 0; +} + +static void dib7000m_reset_pll_common(struct dib7000m_state *state, const struct dibx000_bandwidth_config *bw) +{ + dib7000m_write_word(state, 18, ((bw->internal*1000) >> 16) & 0xffff); + dib7000m_write_word(state, 19, (bw->internal*1000) & 0xffff); + dib7000m_write_word(state, 21, (bw->ifreq >> 16) & 0xffff); + dib7000m_write_word(state, 22, bw->ifreq & 0xffff); + + dib7000m_write_word(state, 928, bw->sad_cfg); +} + +static void dib7000m_reset_pll(struct dib7000m_state *state) +{ + const struct dibx000_bandwidth_config *bw = state->cfg.bw; + u16 reg_907,reg_910; + + /* default */ + reg_907 = (bw->pll_bypass << 15) | (bw->modulo << 7) | + (bw->ADClkSrc << 6) | (bw->IO_CLK_en_core << 5) | (bw->bypclk_div << 2) | + (bw->enable_refdiv << 1) | (0 << 0); + reg_910 = (((bw->pll_ratio >> 6) & 0x3) << 3) | (bw->pll_range << 1) | bw->pll_reset; + + // for this oscillator frequency should be 30 MHz for the Master (default values in the board_parameters give that value) + // this is only working only for 30 MHz crystals + if (!state->cfg.quartz_direct) { + reg_910 |= (1 << 5); // forcing the predivider to 1 + + // if the previous front-end is baseband, its output frequency is 15 MHz (prev freq divided by 2) + if(state->cfg.input_clk_is_div_2) + reg_907 |= (16 << 9); + else // otherwise the previous front-end puts out its input (default 30MHz) - no extra division necessary + reg_907 |= (8 << 9); + } else { + reg_907 |= (bw->pll_ratio & 0x3f) << 9; + reg_910 |= (bw->pll_prediv << 5); + } + + dib7000m_write_word(state, 910, reg_910); // pll cfg + dib7000m_write_word(state, 907, reg_907); // clk cfg0 + dib7000m_write_word(state, 908, 0x0006); // clk_cfg1 + + dib7000m_reset_pll_common(state, bw); +} + +static void dib7000mc_reset_pll(struct dib7000m_state *state) +{ + const struct dibx000_bandwidth_config *bw = state->cfg.bw; + + // clk_cfg0 + dib7000m_write_word(state, 907, (bw->pll_prediv << 8) | (bw->pll_ratio << 0)); + + // clk_cfg1 + //dib7000m_write_word(state, 908, (1 << 14) | (3 << 12) |(0 << 11) | + dib7000m_write_word(state, 908, (0 << 14) | (3 << 12) |(0 << 11) | + (bw->IO_CLK_en_core << 10) | (bw->bypclk_div << 5) | (bw->enable_refdiv << 4) | + (bw->pll_bypass << 3) | (bw->pll_range << 1) | (bw->pll_reset << 0)); + + // smpl_cfg + dib7000m_write_word(state, 910, (1 << 12) | (2 << 10) | (bw->modulo << 8) | (bw->ADClkSrc << 7)); + + dib7000m_reset_pll_common(state, bw); +} + +static int dib7000m_reset_gpio(struct dib7000m_state *st) +{ + /* reset the GPIOs */ + dprintk("-D- gpio dir: %x: gpio val: %x, gpio pwm pos: %x\n", + st->cfg.gpio_dir, st->cfg.gpio_val,st->cfg.gpio_pwm_pos); + + dib7000m_write_word(st, 773, st->cfg.gpio_dir); + dib7000m_write_word(st, 774, st->cfg.gpio_val); + + /* TODO 782 is P_gpio_od */ + + dib7000m_write_word(st, 775, st->cfg.gpio_pwm_pos); + + dib7000m_write_word(st, 780, st->cfg.pwm_freq_div); + return 0; +} + +static int dib7000m_demod_reset(struct dib7000m_state *state) +{ + dib7000m_set_power_mode(state, DIB7000M_POWER_ALL); + + /* always leave the VBG voltage on - it consumes almost nothing but takes a long time to start */ + dib7000m_set_adc_state(state, DIBX000_VBG_ENABLE); + + /* restart all parts */ + dib7000m_write_word(state, 898, 0xffff); + dib7000m_write_word(state, 899, 0xffff); + dib7000m_write_word(state, 900, 0xff0f); + dib7000m_write_word(state, 901, 0xfffc); + + dib7000m_write_word(state, 898, 0); + dib7000m_write_word(state, 899, 0); + dib7000m_write_word(state, 900, 0); + dib7000m_write_word(state, 901, 0); + + if (state->revision == 0x4000) + dib7000m_reset_pll(state); + else + dib7000mc_reset_pll(state); + + if (dib7000m_reset_gpio(state) != 0) + dprintk("-E- GPIO reset was not successful.\n"); + + if (dib7000m_set_output_mode(state, OUTMODE_HIGH_Z) != 0) + dprintk("-E- OUTPUT_MODE could not be resetted.\n"); + + /* unforce divstr regardless whether i2c enumeration was done or not */ + dib7000m_write_word(state, 1794, dib7000m_read_word(state, 1794) & ~(1 << 1) ); + + dib7000m_set_bandwidth(&state->demod, BANDWIDTH_8_MHZ); + + dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_ON); + dib7000m_sad_calib(state); + dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_OFF); + + dib7000m_set_power_mode(state, DIB7000M_POWER_INTERFACE_ONLY); + + return 0; +} + +static void dib7000m_restart_agc(struct dib7000m_state *state) +{ + // P_restart_iqc & P_restart_agc + dib7000m_write_word(state, 898, 0x0c00); + dib7000m_write_word(state, 898, 0x0000); +} + +static int dib7000m_agc_soft_split(struct dib7000m_state *state) +{ + u16 agc,split_offset; + + if(!state->current_agc || !state->current_agc->perform_agc_softsplit || state->current_agc->split.max == 0) + return 0; + + // n_agc_global + agc = dib7000m_read_word(state, 390); + + if (agc > state->current_agc->split.min_thres) + split_offset = state->current_agc->split.min; + else if (agc < state->current_agc->split.max_thres) + split_offset = state->current_agc->split.max; + else + split_offset = state->current_agc->split.max * + (agc - state->current_agc->split.min_thres) / + (state->current_agc->split.max_thres - state->current_agc->split.min_thres); + + dprintk("AGC split_offset: %d\n",split_offset); + + // P_agc_force_split and P_agc_split_offset + return dib7000m_write_word(state, 103, (dib7000m_read_word(state, 103) & 0xff00) | split_offset); +} + +static int dib7000m_update_lna(struct dib7000m_state *state) +{ + int i; + u16 dyn_gain; + + // when there is no LNA to program return immediatly + if (state->cfg.update_lna == NULL) + return 0; + + msleep(60); + for (i = 0; i < 20; i++) { + // read dyn_gain here (because it is demod-dependent and not tuner) + dyn_gain = dib7000m_read_word(state, 390); + + dprintk("agc global: %d\n", dyn_gain); + + if (state->cfg.update_lna(&state->demod,dyn_gain)) { // LNA has changed + dib7000m_restart_agc(state); + msleep(60); + } else + break; + } + return 0; +} + +static void dib7000m_set_agc_config(struct dib7000m_state *state, u8 band) +{ + struct dibx000_agc_config *agc = NULL; + int i; + if (state->current_band == band) + return; + state->current_band = band; + + for (i = 0; i < state->cfg.agc_config_count; i++) + if (state->cfg.agc[i].band_caps & band) { + agc = &state->cfg.agc[i]; + break; + } + + if (agc == NULL) { + dprintk("-E- No valid AGC configuration found for band 0x%02x\n",band); + return; + } + + state->current_agc = agc; + + /* AGC */ + dib7000m_write_word(state, 72 , agc->setup); + dib7000m_write_word(state, 73 , agc->inv_gain); + dib7000m_write_word(state, 74 , agc->time_stabiliz); + dib7000m_write_word(state, 97 , (agc->alpha_level << 12) | agc->thlock); + + // Demod AGC loop configuration + dib7000m_write_word(state, 98, (agc->alpha_mant << 5) | agc->alpha_exp); + dib7000m_write_word(state, 99, (agc->beta_mant << 6) | agc->beta_exp); + + dprintk("-D- WBD: ref: %d, sel: %d, active: %d, alpha: %d\n", + state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel); + + /* AGC continued */ + if (state->wbd_ref != 0) + dib7000m_write_word(state, 102, state->wbd_ref); + else // use default + dib7000m_write_word(state, 102, agc->wbd_ref); + + dib7000m_write_word(state, 103, (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8) ); + dib7000m_write_word(state, 104, agc->agc1_max); + dib7000m_write_word(state, 105, agc->agc1_min); + dib7000m_write_word(state, 106, agc->agc2_max); + dib7000m_write_word(state, 107, agc->agc2_min); + dib7000m_write_word(state, 108, (agc->agc1_pt1 << 8) | agc->agc1_pt2 ); + dib7000m_write_word(state, 109, (agc->agc1_slope1 << 8) | agc->agc1_slope2); + dib7000m_write_word(state, 110, (agc->agc2_pt1 << 8) | agc->agc2_pt2); + dib7000m_write_word(state, 111, (agc->agc2_slope1 << 8) | agc->agc2_slope2); + + if (state->revision > 0x4000) { // settings for the MC + dib7000m_write_word(state, 71, agc->agc1_pt3); +// dprintk("-D- 929: %x %d %d\n", +// (dib7000m_read_word(state, 929) & 0xffe3) | (agc->wbd_inv << 4) | (agc->wbd_sel << 2), agc->wbd_inv, agc->wbd_sel); + dib7000m_write_word(state, 929, (dib7000m_read_word(state, 929) & 0xffe3) | (agc->wbd_inv << 4) | (agc->wbd_sel << 2)); + } else { + // wrong default values + u16 b[9] = { 676, 696, 717, 737, 758, 778, 799, 819, 840 }; + for (i = 0; i < 9; i++) + dib7000m_write_word(state, 88 + i, b[i]); + } +} + +static void dib7000m_update_timf_freq(struct dib7000m_state *state) +{ + u32 timf = (dib7000m_read_word(state, 436) << 16) | dib7000m_read_word(state, 437); + state->timf = timf * 80 / (BW_INDEX_TO_KHZ(state->current_bandwidth) / 100); + dib7000m_write_word(state, 23, (u16) (timf >> 16)); + dib7000m_write_word(state, 24, (u16) (timf & 0xffff)); + dprintk("-D- Updated timf_frequency: %d (default: %d)\n",state->timf, state->cfg.bw->timf); +} + +static void dib7000m_set_channel(struct dib7000m_state *state, struct dibx000_ofdm_channel *ch, u8 seq) +{ + u16 value, est[4]; + + dib7000m_set_agc_config(state, BAND_OF_FREQUENCY(ch->RF_kHz)); + + /* nfft, guard, qam, alpha */ + dib7000m_write_word(state, 0, (ch->nfft << 7) | (ch->guard << 5) | (ch->nqam << 3) | (ch->vit_alpha)); + dib7000m_write_word(state, 5, (seq << 4)); + + /* P_dintl_native, P_dintlv_inv, P_vit_hrch, P_vit_code_rate, P_vit_select_hp */ + value = (ch->intlv_native << 6) | (ch->vit_hrch << 4) | (ch->vit_select_hp & 0x1); + if (ch->vit_hrch == 0 || ch->vit_select_hp == 1) + value |= (ch->vit_code_rate_hp << 1); + else + value |= (ch->vit_code_rate_lp << 1); + dib7000m_write_word(state, 267 + state->reg_offs, value); + + /* offset loop parameters */ + + /* P_timf_alpha = 6, P_corm_alpha=6, P_corm_thres=0x80 */ + dib7000m_write_word(state, 26, (6 << 12) | (6 << 8) | 0x80); + + /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=1, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */ + dib7000m_write_word(state, 29, (0 << 14) | (4 << 10) | (1 << 9) | (3 << 5) | (1 << 4) | (0x3)); + + /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max=3 */ + dib7000m_write_word(state, 32, (0 << 4) | 0x3); + + /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step=5 */ + dib7000m_write_word(state, 33, (0 << 4) | 0x5); + + /* P_dvsy_sync_wait */ + switch (ch->nfft) { + case 1: value = 256; break; + case 2: value = 128; break; + case 0: + default: value = 64; break; + } + value *= ((1 << (ch->guard)) * 3 / 2); // add 50% SFN margin + value <<= 4; + + /* deactive the possibility of diversity reception if extended interleave - not for 7000MC */ + /* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */ + if (ch->intlv_native || state->revision > 0x4000) + value |= (1 << 2) | (2 << 0); + else + value |= 0; + dib7000m_write_word(state, 266 + state->reg_offs, value); + + /* channel estimation fine configuration */ + switch (ch->nqam) { + case 2: + est[0] = 0x0148; /* P_adp_regul_cnt 0.04 */ + est[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */ + est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ + est[3] = 0xfff8; /* P_adp_noise_ext -0.001 */ + break; + case 1: + est[0] = 0x023d; /* P_adp_regul_cnt 0.07 */ + est[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */ + est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ + est[3] = 0xfff0; /* P_adp_noise_ext -0.002 */ + break; + default: + est[0] = 0x099a; /* P_adp_regul_cnt 0.3 */ + est[1] = 0xffae; /* P_adp_noise_cnt -0.01 */ + est[2] = 0x0333; /* P_adp_regul_ext 0.1 */ + est[3] = 0xfff8; /* P_adp_noise_ext -0.002 */ + break; + } + for (value = 0; value < 4; value++) + dib7000m_write_word(state, 214 + value + state->reg_offs, est[value]); + + // set power-up level: interf+analog+AGC + dib7000m_set_power_mode(state, DIB7000M_POWER_INTERF_ANALOG_AGC); + dib7000m_set_adc_state(state, DIBX000_ADC_ON); + + msleep(7); + + //AGC initialization + if (state->cfg.agc_control) + state->cfg.agc_control(&state->demod, 1); + + dib7000m_restart_agc(state); + + // wait AGC rough lock time + msleep(5); + + dib7000m_update_lna(state); + dib7000m_agc_soft_split(state); + + // wait AGC accurate lock time + msleep(7); + + if (state->cfg.agc_control) + state->cfg.agc_control(&state->demod, 0); + + // set power-up level: autosearch + dib7000m_set_power_mode(state, DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD); +} + +static int dib7000m_autosearch_start(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch) +{ + struct dib7000m_state *state = demod->demodulator_priv; + struct dibx000_ofdm_channel auto_ch; + int ret = 0; + u32 value; + + INIT_OFDM_CHANNEL(&auto_ch); + auto_ch.RF_kHz = ch->RF_kHz; + auto_ch.Bw = ch->Bw; + auto_ch.nqam = 2; + auto_ch.guard = 0; + auto_ch.nfft = 1; + auto_ch.vit_alpha = 1; + auto_ch.vit_select_hp = 1; + auto_ch.vit_code_rate_hp = 2; + auto_ch.vit_code_rate_lp = 3; + auto_ch.vit_hrch = 0; + auto_ch.intlv_native = 1; + + dib7000m_set_channel(state, &auto_ch, 7); + + // always use the setting for 8MHz here lock_time for 7,6 MHz are longer + value = 30 * state->cfg.bw->internal; + ret |= dib7000m_write_word(state, 6, (u16) ((value >> 16) & 0xffff)); // lock0 wait time + ret |= dib7000m_write_word(state, 7, (u16) (value & 0xffff)); // lock0 wait time + value = 100 * state->cfg.bw->internal; + ret |= dib7000m_write_word(state, 8, (u16) ((value >> 16) & 0xffff)); // lock1 wait time + ret |= dib7000m_write_word(state, 9, (u16) (value & 0xffff)); // lock1 wait time + value = 500 * state->cfg.bw->internal; + ret |= dib7000m_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); // lock2 wait time + ret |= dib7000m_write_word(state, 11, (u16) (value & 0xffff)); // lock2 wait time + + // start search + value = dib7000m_read_word(state, 0); + ret |= dib7000m_write_word(state, 0, value | (1 << 9)); + + /* clear n_irq_pending */ + if (state->revision == 0x4000) + dib7000m_write_word(state, 1793, 0); + else + dib7000m_read_word(state, 537); + + ret |= dib7000m_write_word(state, 0, (u16) value); + + return ret; +} + +static int dib7000m_autosearch_irq(struct dib7000m_state *state, u16 reg) +{ + u16 irq_pending = dib7000m_read_word(state, reg); + + if (irq_pending & 0x1) { // failed + dprintk("#\n"); + return 1; + } + + if (irq_pending & 0x2) { // succeeded + dprintk("!\n"); + return 2; + } + return 0; // still pending +} + +static int dib7000m_autosearch_is_irq(struct dvb_frontend *demod) +{ + struct dib7000m_state *state = demod->demodulator_priv; + if (state->revision == 0x4000) + return dib7000m_autosearch_irq(state, 1793); + else + return dib7000m_autosearch_irq(state, 537); +} + +static int dib7000m_tune(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch) +{ + struct dib7000m_state *state = demod->demodulator_priv; + int ret = 0; + u16 value; + + // we are already tuned - just resuming from suspend + if (ch != NULL) + dib7000m_set_channel(state, ch, 0); + else + return -EINVAL; + + // restart demod + ret |= dib7000m_write_word(state, 898, 0x4000); + ret |= dib7000m_write_word(state, 898, 0x0000); + msleep(45); + + ret |= dib7000m_set_power_mode(state, DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD); + /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */ + ret |= dib7000m_write_word(state, 29, (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3)); + + // never achieved a lock with that bandwidth so far - wait for timfreq to update + if (state->timf == 0) + msleep(200); + + //dump_reg(state); + /* P_timf_alpha, P_corm_alpha=6, P_corm_thres=0x80 */ + value = (6 << 8) | 0x80; + switch (ch->nfft) { + case 0: value |= (7 << 12); break; + case 1: value |= (9 << 12); break; + case 2: value |= (8 << 12); break; + } + ret |= dib7000m_write_word(state, 26, value); + + /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max */ + value = (0 << 4); + switch (ch->nfft) { + case 0: value |= 0x6; break; + case 1: value |= 0x8; break; + case 2: value |= 0x7; break; + } + ret |= dib7000m_write_word(state, 32, value); + + /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step */ + value = (0 << 4); + switch (ch->nfft) { + case 0: value |= 0x6; break; + case 1: value |= 0x8; break; + case 2: value |= 0x7; break; + } + ret |= dib7000m_write_word(state, 33, value); + + // we achieved a lock - it's time to update the osc freq + if ((dib7000m_read_word(state, 535) >> 6) & 0x1) + dib7000m_update_timf_freq(state); + + return ret; +} + +static int dib7000m_init(struct dvb_frontend *demod) +{ + struct dib7000m_state *state = demod->demodulator_priv; + int ret = 0; + u8 o = state->reg_offs; + + dib7000m_set_power_mode(state, DIB7000M_POWER_ALL); + + if (dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_ON) != 0) + dprintk("-E- could not start Slow ADC\n"); + + if (state->cfg.dvbt_mode) + dib7000m_write_word(state, 1796, 0x0); // select DVB-T output + + if (state->cfg.mobile_mode) + ret |= dib7000m_write_word(state, 261 + o, 2); + else + ret |= dib7000m_write_word(state, 224 + o, 1); + + ret |= dib7000m_write_word(state, 173 + o, 0); + ret |= dib7000m_write_word(state, 174 + o, 0); + ret |= dib7000m_write_word(state, 175 + o, 0); + ret |= dib7000m_write_word(state, 176 + o, 0); + ret |= dib7000m_write_word(state, 177 + o, 0); + ret |= dib7000m_write_word(state, 178 + o, 0); + ret |= dib7000m_write_word(state, 179 + o, 0); + ret |= dib7000m_write_word(state, 180 + o, 0); + + // P_corm_thres Lock algorithms configuration + ret |= dib7000m_write_word(state, 26, 0x6680); + + // P_palf_alpha_regul, P_palf_filter_freeze, P_palf_filter_on + ret |= dib7000m_write_word(state, 170 + o, 0x0410); + // P_fft_nb_to_cut + ret |= dib7000m_write_word(state, 182 + o, 8192); + // P_pha3_thres + ret |= dib7000m_write_word(state, 195 + o, 0x0ccd); + // P_cti_use_cpe, P_cti_use_prog + ret |= dib7000m_write_word(state, 196 + o, 0); + // P_cspu_regul, P_cspu_win_cut + ret |= dib7000m_write_word(state, 205 + o, 0x200f); + // P_adp_regul_cnt + ret |= dib7000m_write_word(state, 214 + o, 0x023d); + // P_adp_noise_cnt + ret |= dib7000m_write_word(state, 215 + o, 0x00a4); + // P_adp_regul_ext + ret |= dib7000m_write_word(state, 216 + o, 0x00a4); + // P_adp_noise_ext + ret |= dib7000m_write_word(state, 217 + o, 0x7ff0); + // P_adp_fil + ret |= dib7000m_write_word(state, 218 + o, 0x3ccc); + + // P_2d_byp_ti_num + ret |= dib7000m_write_word(state, 226 + o, 0); + + // P_fec_* + ret |= dib7000m_write_word(state, 281 + o, 0x0010); + // P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard + ret |= dib7000m_write_word(state, 294 + o,0x0062); + + // P_iqc_alpha_pha, P_iqc_alpha_amp, P_iqc_dcc_alpha, ... + if(state->cfg.tuner_is_baseband) + ret |= dib7000m_write_word(state, 36, 0x0755); + else + ret |= dib7000m_write_word(state, 36, 0x1f55); + + // auto search configuration + ret |= dib7000m_write_word(state, 2, 0x0004); + ret |= dib7000m_write_word(state, 3, 0x1000); + ret |= dib7000m_write_word(state, 4, 0x0814); + ret |= dib7000m_write_word(state, 6, 0x001b); + ret |= dib7000m_write_word(state, 7, 0x7740); + ret |= dib7000m_write_word(state, 8, 0x005b); + ret |= dib7000m_write_word(state, 9, 0x8d80); + ret |= dib7000m_write_word(state, 10, 0x01c9); + ret |= dib7000m_write_word(state, 11, 0xc380); + ret |= dib7000m_write_word(state, 12, 0x0000); + ret |= dib7000m_write_word(state, 13, 0x0080); + ret |= dib7000m_write_word(state, 14, 0x0000); + ret |= dib7000m_write_word(state, 15, 0x0090); + ret |= dib7000m_write_word(state, 16, 0x0001); + ret |= dib7000m_write_word(state, 17, 0xd4c0); + ret |= dib7000m_write_word(state, 263 + o,0x0001); + + // P_divclksel=3 P_divbitsel=1 + if (state->revision == 0x4000) + dib7000m_write_word(state, 909, (3 << 10) | (1 << 6)); + else + dib7000m_write_word(state, 909, (3 << 4) | 1); + + // Tuner IO bank: max drive (14mA) + ret |= dib7000m_write_word(state, 912 ,0x2c8a); + + ret |= dib7000m_write_word(state, 1817, 1); + + return ret; +} + +static int dib7000m_sleep(struct dvb_frontend *demod) +{ + struct dib7000m_state *st = demod->demodulator_priv; + dib7000m_set_output_mode(st, OUTMODE_HIGH_Z); + return dib7000m_set_power_mode(st, DIB7000M_POWER_INTERFACE_ONLY) | + dib7000m_set_adc_state(st, DIBX000_SLOW_ADC_OFF) | + dib7000m_set_adc_state(st, DIBX000_ADC_OFF); +} + +static int dib7000m_identify(struct dib7000m_state *state) +{ + u16 value; + if ((value = dib7000m_read_word(state, 896)) != 0x01b3) { + dprintk("-E- DiB7000M: wrong Vendor ID (read=0x%x)\n",value); + return -EREMOTEIO; + } + + state->revision = dib7000m_read_word(state, 897); + if (state->revision != 0x4000 && + state->revision != 0x4001 && + state->revision != 0x4002) { + dprintk("-E- DiB7000M: wrong Device ID (%x)\n",value); + return -EREMOTEIO; + } + + /* protect this driver to be used with 7000PC */ + if (state->revision == 0x4000 && dib7000m_read_word(state, 769) == 0x4000) { + dprintk("-E- DiB7000M: this driver does not work with DiB7000PC\n"); + return -EREMOTEIO; + } + + switch (state->revision) { + case 0x4000: dprintk("-I- found DiB7000MA/PA/MB/PB\n"); break; + case 0x4001: state->reg_offs = 1; dprintk("-I- found DiB7000HC\n"); break; + case 0x4002: state->reg_offs = 1; dprintk("-I- found DiB7000MC\n"); break; + } + + return 0; +} + + +static int dib7000m_get_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep) +{ + struct dib7000m_state *state = fe->demodulator_priv; + u16 tps = dib7000m_read_word(state,480); + + fep->inversion = INVERSION_AUTO; + + fep->u.ofdm.bandwidth = state->current_bandwidth; + + switch ((tps >> 8) & 0x3) { + case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break; + case 1: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; break; + /* case 2: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_4K; break; */ + } + + switch (tps & 0x3) { + case 0: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; break; + case 1: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; break; + case 2: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; break; + case 3: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; break; + } + + switch ((tps >> 14) & 0x3) { + case 0: fep->u.ofdm.constellation = QPSK; break; + case 1: fep->u.ofdm.constellation = QAM_16; break; + case 2: + default: fep->u.ofdm.constellation = QAM_64; break; + } + + /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */ + /* (tps >> 13) & 0x1 == hrch is used, (tps >> 10) & 0x7 == alpha */ + + fep->u.ofdm.hierarchy_information = HIERARCHY_NONE; + switch ((tps >> 5) & 0x7) { + case 1: fep->u.ofdm.code_rate_HP = FEC_1_2; break; + case 2: fep->u.ofdm.code_rate_HP = FEC_2_3; break; + case 3: fep->u.ofdm.code_rate_HP = FEC_3_4; break; + case 5: fep->u.ofdm.code_rate_HP = FEC_5_6; break; + case 7: + default: fep->u.ofdm.code_rate_HP = FEC_7_8; break; + + } + + switch ((tps >> 2) & 0x7) { + case 1: fep->u.ofdm.code_rate_LP = FEC_1_2; break; + case 2: fep->u.ofdm.code_rate_LP = FEC_2_3; break; + case 3: fep->u.ofdm.code_rate_LP = FEC_3_4; break; + case 5: fep->u.ofdm.code_rate_LP = FEC_5_6; break; + case 7: + default: fep->u.ofdm.code_rate_LP = FEC_7_8; break; + } + + /* native interleaver: (dib7000m_read_word(state, 481) >> 5) & 0x1 */ + + return 0; +} + +static int dib7000m_set_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep) +{ + struct dib7000m_state *state = fe->demodulator_priv; + struct dibx000_ofdm_channel ch; + + INIT_OFDM_CHANNEL(&ch); + FEP2DIB(fep,&ch); + + state->current_bandwidth = fep->u.ofdm.bandwidth; + dib7000m_set_bandwidth(fe, fep->u.ofdm.bandwidth); + + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe, fep); + + if (fep->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO || + fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO || + fep->u.ofdm.constellation == QAM_AUTO || + fep->u.ofdm.code_rate_HP == FEC_AUTO) { + int i = 800, found; + + dib7000m_autosearch_start(fe, &ch); + do { + msleep(1); + found = dib7000m_autosearch_is_irq(fe); + } while (found == 0 && i--); + + dprintk("autosearch returns: %d\n",found); + if (found == 0 || found == 1) + return 0; // no channel found + + dib7000m_get_frontend(fe, fep); + FEP2DIB(fep, &ch); + } + + /* make this a config parameter */ + dib7000m_set_output_mode(state, OUTMODE_MPEG2_FIFO); + + return dib7000m_tune(fe, &ch); +} + +static int dib7000m_read_status(struct dvb_frontend *fe, fe_status_t *stat) +{ + struct dib7000m_state *state = fe->demodulator_priv; + u16 lock = dib7000m_read_word(state, 535); + + *stat = 0; + + if (lock & 0x8000) + *stat |= FE_HAS_SIGNAL; + if (lock & 0x3000) + *stat |= FE_HAS_CARRIER; + if (lock & 0x0100) + *stat |= FE_HAS_VITERBI; + if (lock & 0x0010) + *stat |= FE_HAS_SYNC; + if (lock & 0x0008) + *stat |= FE_HAS_LOCK; + + return 0; +} + +static int dib7000m_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct dib7000m_state *state = fe->demodulator_priv; + *ber = (dib7000m_read_word(state, 526) << 16) | dib7000m_read_word(state, 527); + return 0; +} + +static int dib7000m_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) +{ + struct dib7000m_state *state = fe->demodulator_priv; + *unc = dib7000m_read_word(state, 534); + return 0; +} + +static int dib7000m_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct dib7000m_state *state = fe->demodulator_priv; + u16 val = dib7000m_read_word(state, 390); + *strength = 65535 - val; + return 0; +} + +static int dib7000m_read_snr(struct dvb_frontend* fe, u16 *snr) +{ + *snr = 0x0000; + return 0; +} + +static int dib7000m_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static void dib7000m_release(struct dvb_frontend *demod) +{ + struct dib7000m_state *st = demod->demodulator_priv; + dibx000_exit_i2c_master(&st->i2c_master); + kfree(st); +} + +struct i2c_adapter * dib7000m_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating) +{ + struct dib7000m_state *st = demod->demodulator_priv; + return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); +} +EXPORT_SYMBOL(dib7000m_get_i2c_master); + +int dib7000m_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000m_config cfg[]) +{ + struct dib7000m_state st = { .i2c_adap = i2c }; + int k = 0; + u8 new_addr = 0; + + for (k = no_of_demods-1; k >= 0; k--) { + st.cfg = cfg[k]; + + /* designated i2c address */ + new_addr = (0x40 + k) << 1; + st.i2c_addr = new_addr; + if (dib7000m_identify(&st) != 0) { + st.i2c_addr = default_addr; + if (dib7000m_identify(&st) != 0) { + dprintk("DiB7000M #%d: not identified\n", k); + return -EIO; + } + } + + /* start diversity to pull_down div_str - just for i2c-enumeration */ + dib7000m_set_output_mode(&st, OUTMODE_DIVERSITY); + + dib7000m_write_word(&st, 1796, 0x0); // select DVB-T output + + /* set new i2c address and force divstart */ + dib7000m_write_word(&st, 1794, (new_addr << 2) | 0x2); + + dprintk("IC %d initialized (to i2c_address 0x%x)\n", k, new_addr); + } + + for (k = 0; k < no_of_demods; k++) { + st.cfg = cfg[k]; + st.i2c_addr = (0x40 + k) << 1; + + // unforce divstr + dib7000m_write_word(&st,1794, st.i2c_addr << 2); + + /* deactivate div - it was just for i2c-enumeration */ + dib7000m_set_output_mode(&st, OUTMODE_HIGH_Z); + } + + return 0; +} +EXPORT_SYMBOL(dib7000m_i2c_enumeration); + +static struct dvb_frontend_ops dib7000m_ops; +struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000m_config *cfg) +{ + struct dvb_frontend *demod; + struct dib7000m_state *st; + st = kzalloc(sizeof(struct dib7000m_state), GFP_KERNEL); + if (st == NULL) + return NULL; + + memcpy(&st->cfg, cfg, sizeof(struct dib7000m_config)); + st->i2c_adap = i2c_adap; + st->i2c_addr = i2c_addr; + + demod = &st->demod; + demod->demodulator_priv = st; + memcpy(&st->demod.ops, &dib7000m_ops, sizeof(struct dvb_frontend_ops)); + + if (dib7000m_identify(st) != 0) + goto error; + + if (st->revision == 0x4000) + dibx000_init_i2c_master(&st->i2c_master, DIB7000, st->i2c_adap, st->i2c_addr); + else + dibx000_init_i2c_master(&st->i2c_master, DIB7000MC, st->i2c_adap, st->i2c_addr); + + dib7000m_demod_reset(st); + + return demod; + +error: + kfree(st); + return NULL; +} +EXPORT_SYMBOL(dib7000m_attach); + +static struct dvb_frontend_ops dib7000m_ops = { + .info = { + .name = "DiBcom 7000MA/MB/PA/PB/MC", + .type = FE_OFDM, + .frequency_min = 44250000, + .frequency_max = 867250000, + .frequency_stepsize = 62500, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_RECOVER | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = dib7000m_release, + + .init = dib7000m_init, + .sleep = dib7000m_sleep, + + .set_frontend = dib7000m_set_frontend, + .get_tune_settings = dib7000m_fe_get_tune_settings, + .get_frontend = dib7000m_get_frontend, + + .read_status = dib7000m_read_status, + .read_ber = dib7000m_read_ber, + .read_signal_strength = dib7000m_read_signal_strength, + .read_snr = dib7000m_read_snr, + .read_ucblocks = dib7000m_read_unc_blocks, +}; + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for the DiBcom 7000MA/MB/PA/PB/MC COFDM demodulator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/dib7000m.h b/drivers/media/dvb/frontends/dib7000m.h new file mode 100644 index 0000000..597e9cc --- /dev/null +++ b/drivers/media/dvb/frontends/dib7000m.h @@ -0,0 +1,51 @@ +#ifndef DIB7000M_H +#define DIB7000M_H + +#include "dibx000_common.h" + +struct dib7000m_config { + u8 dvbt_mode; + u8 output_mpeg2_in_188_bytes; + u8 hostbus_diversity; + u8 tuner_is_baseband; + u8 mobile_mode; + int (*update_lna) (struct dvb_frontend *, u16 agc_global); + + u8 agc_config_count; + struct dibx000_agc_config *agc; + + struct dibx000_bandwidth_config *bw; + +#define DIB7000M_GPIO_DEFAULT_DIRECTIONS 0xffff + u16 gpio_dir; +#define DIB7000M_GPIO_DEFAULT_VALUES 0x0000 + u16 gpio_val; +#define DIB7000M_GPIO_PWM_POS0(v) ((v & 0xf) << 12) +#define DIB7000M_GPIO_PWM_POS1(v) ((v & 0xf) << 8 ) +#define DIB7000M_GPIO_PWM_POS2(v) ((v & 0xf) << 4 ) +#define DIB7000M_GPIO_PWM_POS3(v) (v & 0xf) +#define DIB7000M_GPIO_DEFAULT_PWM_POS 0xffff + u16 gpio_pwm_pos; + + u16 pwm_freq_div; + + u8 quartz_direct; + + u8 input_clk_is_div_2; + + int (*agc_control) (struct dvb_frontend *, u8 before); +}; + +#define DEFAULT_DIB7000M_I2C_ADDRESS 18 + +extern struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000m_config *cfg); +extern struct i2c_adapter * dib7000m_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); + +/* TODO +extern INT dib7000m_set_gpio(struct dibDemod *demod, UCHAR num, UCHAR dir, UCHAR val); +extern INT dib7000m_enable_vbg_voltage(struct dibDemod *demod); +extern void dib7000m_set_hostbus_diversity(struct dibDemod *demod, UCHAR onoff); +extern USHORT dib7000m_get_current_agc_global(struct dibDemod *demod); +*/ + +#endif diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c new file mode 100644 index 0000000..0349a4b --- /dev/null +++ b/drivers/media/dvb/frontends/dib7000p.c @@ -0,0 +1,1019 @@ +/* + * Linux-DVB Driver for DiBcom's second generation DiB7000P (PC). + * + * Copyright (C) 2005-6 DiBcom (http://www.dibcom.fr/) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + */ +#include +#include + +#include "dvb_frontend.h" + +#include "dib7000p.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); + +#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB7000P:"); printk(args); } } while (0) + +struct dib7000p_state { + struct dvb_frontend demod; + struct dib7000p_config cfg; + + u8 i2c_addr; + struct i2c_adapter *i2c_adap; + + struct dibx000_i2c_master i2c_master; + + u16 wbd_ref; + + u8 current_band; + fe_bandwidth_t current_bandwidth; + struct dibx000_agc_config *current_agc; + u32 timf; + + u16 gpio_dir; + u16 gpio_val; +}; + +enum dib7000p_power_mode { + DIB7000P_POWER_ALL = 0, + DIB7000P_POWER_INTERFACE_ONLY, +}; + +static u16 dib7000p_read_word(struct dib7000p_state *state, u16 reg) +{ + u8 wb[2] = { reg >> 8, reg & 0xff }; + u8 rb[2]; + struct i2c_msg msg[2] = { + { .addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2 }, + { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 }, + }; + + if (i2c_transfer(state->i2c_adap, msg, 2) != 2) + dprintk("i2c read error on %d\n",reg); + + return (rb[0] << 8) | rb[1]; +} + +static int dib7000p_write_word(struct dib7000p_state *state, u16 reg, u16 val) +{ + u8 b[4] = { + (reg >> 8) & 0xff, reg & 0xff, + (val >> 8) & 0xff, val & 0xff, + }; + struct i2c_msg msg = { + .addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4 + }; + return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; +} +static int dib7000p_set_output_mode(struct dib7000p_state *state, int mode) +{ + int ret = 0; + u16 outreg, fifo_threshold, smo_mode; + + outreg = 0; + fifo_threshold = 1792; + smo_mode = (dib7000p_read_word(state, 235) & 0x0010) | (1 << 1); + + dprintk("-I- Setting output mode for demod %p to %d\n", + &state->demod, mode); + + switch (mode) { + case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock + outreg = (1 << 10); /* 0x0400 */ + break; + case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock + outreg = (1 << 10) | (1 << 6); /* 0x0440 */ + break; + case OUTMODE_MPEG2_SERIAL: // STBs with serial input + outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0480 */ + break; + case OUTMODE_DIVERSITY: + if (state->cfg.hostbus_diversity) + outreg = (1 << 10) | (4 << 6); /* 0x0500 */ + else + outreg = (1 << 11); + break; + case OUTMODE_MPEG2_FIFO: // e.g. USB feeding + smo_mode |= (3 << 1); + fifo_threshold = 512; + outreg = (1 << 10) | (5 << 6); + break; + case OUTMODE_HIGH_Z: // disable + outreg = 0; + break; + default: + dprintk("Unhandled output_mode passed to be set for demod %p\n",&state->demod); + break; + } + + if (state->cfg.output_mpeg2_in_188_bytes) + smo_mode |= (1 << 5) ; + + ret |= dib7000p_write_word(state, 235, smo_mode); + ret |= dib7000p_write_word(state, 236, fifo_threshold); /* synchronous fread */ + ret |= dib7000p_write_word(state, 1286, outreg); /* P_Div_active */ + + return ret; +} + +static int dib7000p_set_power_mode(struct dib7000p_state *state, enum dib7000p_power_mode mode) +{ + /* by default everything is powered off */ + u16 reg_774 = 0xffff, reg_775 = 0xffff, reg_776 = 0x0007, reg_899 = 0x0003, + reg_1280 = (0xfe00) | (dib7000p_read_word(state, 1280) & 0x01ff); + + /* now, depending on the requested mode, we power on */ + switch (mode) { + /* power up everything in the demod */ + case DIB7000P_POWER_ALL: + reg_774 = 0x0000; reg_775 = 0x0000; reg_776 = 0x0; reg_899 = 0x0; reg_1280 &= 0x01ff; + break; + /* just leave power on the control-interfaces: GPIO and (I2C or SDIO) */ + case DIB7000P_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C */ + reg_1280 &= ~((1 << 14) | (1 << 13) | (1 << 12) | (1 << 10)); + break; +/* TODO following stuff is just converted from the dib7000-driver - check when is used what */ + } + + dib7000p_write_word(state, 774, reg_774); + dib7000p_write_word(state, 775, reg_775); + dib7000p_write_word(state, 776, reg_776); + dib7000p_write_word(state, 899, reg_899); + dib7000p_write_word(state, 1280, reg_1280); + + return 0; +} + +static void dib7000p_set_adc_state(struct dib7000p_state *state, enum dibx000_adc_states no) +{ + u16 reg_908 = dib7000p_read_word(state, 908), + reg_909 = dib7000p_read_word(state, 909); + + switch (no) { + case DIBX000_SLOW_ADC_ON: + reg_909 |= (1 << 1) | (1 << 0); + dib7000p_write_word(state, 909, reg_909); + reg_909 &= ~(1 << 1); + break; + + case DIBX000_SLOW_ADC_OFF: + reg_909 |= (1 << 1) | (1 << 0); + break; + + case DIBX000_ADC_ON: + reg_908 &= 0x0fff; + reg_909 &= 0x0003; + break; + + case DIBX000_ADC_OFF: // leave the VBG voltage on + reg_908 |= (1 << 14) | (1 << 13) | (1 << 12); + reg_909 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2); + break; + + case DIBX000_VBG_ENABLE: + reg_908 &= ~(1 << 15); + break; + + case DIBX000_VBG_DISABLE: + reg_908 |= (1 << 15); + break; + + default: + break; + } + +// dprintk("908: %x, 909: %x\n", reg_908, reg_909); + + dib7000p_write_word(state, 908, reg_908); + dib7000p_write_word(state, 909, reg_909); +} + +static int dib7000p_set_bandwidth(struct dvb_frontend *demod, u8 BW_Idx) +{ + struct dib7000p_state *state = demod->demodulator_priv; + u32 timf; + + // store the current bandwidth for later use + state->current_bandwidth = BW_Idx; + + if (state->timf == 0) { + dprintk("-D- Using default timf\n"); + timf = state->cfg.bw->timf; + } else { + dprintk("-D- Using updated timf\n"); + timf = state->timf; + } + + timf = timf * (BW_INDEX_TO_KHZ(BW_Idx) / 100) / 80; + + dprintk("timf: %d\n",timf); + + dib7000p_write_word(state, 23, (timf >> 16) & 0xffff); + dib7000p_write_word(state, 24, (timf ) & 0xffff); + + return 0; +} + +static int dib7000p_sad_calib(struct dib7000p_state *state) +{ +/* internal */ +// dib7000p_write_word(state, 72, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writting in set_bandwidth + dib7000p_write_word(state, 73, (0 << 1) | (0 << 0)); + dib7000p_write_word(state, 74, 776); // 0.625*3.3 / 4096 + + /* do the calibration */ + dib7000p_write_word(state, 73, (1 << 0)); + dib7000p_write_word(state, 73, (0 << 0)); + + msleep(1); + + return 0; +} + +static void dib7000p_reset_pll(struct dib7000p_state *state) +{ + struct dibx000_bandwidth_config *bw = &state->cfg.bw[0]; + + dib7000p_write_word(state, 903, (bw->pll_prediv << 5) | (((bw->pll_ratio >> 6) & 0x3) << 3) | (bw->pll_range << 1) | bw->pll_reset); + dib7000p_write_word(state, 900, ((bw->pll_ratio & 0x3f) << 9) | (bw->pll_bypass << 15) | (bw->modulo << 7) | (bw->ADClkSrc << 6) | + (bw->IO_CLK_en_core << 5) | (bw->bypclk_div << 2) | (bw->enable_refdiv << 1) | (0 << 0)); + + dib7000p_write_word(state, 18, ((bw->internal*1000) >> 16) & 0xffff); + dib7000p_write_word(state, 19, (bw->internal*1000 ) & 0xffff); + dib7000p_write_word(state, 21, (bw->ifreq >> 16) & 0xffff); + dib7000p_write_word(state, 22, (bw->ifreq ) & 0xffff); + + dib7000p_write_word(state, 72, bw->sad_cfg); +} + +static int dib7000p_reset_gpio(struct dib7000p_state *st) +{ + /* reset the GPIOs */ + dprintk("-D- gpio dir: %x: gpio val: %x, gpio pwm pos: %x\n",st->gpio_dir, st->gpio_val,st->cfg.gpio_pwm_pos); + + dib7000p_write_word(st, 1029, st->gpio_dir); + dib7000p_write_word(st, 1030, st->gpio_val); + + /* TODO 1031 is P_gpio_od */ + + dib7000p_write_word(st, 1032, st->cfg.gpio_pwm_pos); + + dib7000p_write_word(st, 1037, st->cfg.pwm_freq_div); + return 0; +} + +static int dib7000p_demod_reset(struct dib7000p_state *state) +{ + dib7000p_set_power_mode(state, DIB7000P_POWER_ALL); + + dib7000p_set_adc_state(state, DIBX000_VBG_ENABLE); + + /* restart all parts */ + dib7000p_write_word(state, 770, 0xffff); + dib7000p_write_word(state, 771, 0xffff); + dib7000p_write_word(state, 772, 0x001f); + dib7000p_write_word(state, 898, 0x0003); + /* except i2c, sdio, gpio - control interfaces */ + dib7000p_write_word(state, 1280, 0x01fc - ((1 << 7) | (1 << 6) | (1 << 5)) ); + + dib7000p_write_word(state, 770, 0); + dib7000p_write_word(state, 771, 0); + dib7000p_write_word(state, 772, 0); + dib7000p_write_word(state, 898, 0); + dib7000p_write_word(state, 1280, 0); + + /* default */ + dib7000p_reset_pll(state); + + if (dib7000p_reset_gpio(state) != 0) + dprintk("-E- GPIO reset was not successful.\n"); + + if (dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) != 0) + dprintk("-E- OUTPUT_MODE could not be resetted.\n"); + + /* unforce divstr regardless whether i2c enumeration was done or not */ + dib7000p_write_word(state, 1285, dib7000p_read_word(state, 1285) & ~(1 << 1) ); + + dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY); + + return 0; +} + +static void dib7000p_restart_agc(struct dib7000p_state *state) +{ + // P_restart_iqc & P_restart_agc + dib7000p_write_word(state, 770, 0x0c00); + dib7000p_write_word(state, 770, 0x0000); +} + +static void dib7000p_update_lna(struct dib7000p_state *state) +{ + int i; + u16 dyn_gain; + + // when there is no LNA to program return immediatly + if (state->cfg.update_lna == NULL) + return; + + for (i = 0; i < 5; i++) { + // read dyn_gain here (because it is demod-dependent and not tuner) + dyn_gain = dib7000p_read_word(state, 394); + + if (state->cfg.update_lna(&state->demod,dyn_gain)) { // LNA has changed + dib7000p_restart_agc(state); + msleep(5); + } else + break; + } +} + +static void dib7000p_pll_clk_cfg(struct dib7000p_state *state) +{ + u16 tmp = 0; + tmp = dib7000p_read_word(state, 903); + dib7000p_write_word(state, 903, (tmp | 0x1)); //pwr-up pll + tmp = dib7000p_read_word(state, 900); + dib7000p_write_word(state, 900, (tmp & 0x7fff) | (1 << 6)); //use High freq clock +} + +static void dib7000p_update_timf_freq(struct dib7000p_state *state) +{ + u32 timf = (dib7000p_read_word(state, 427) << 16) | dib7000p_read_word(state, 428); + state->timf = timf * 80 / (BW_INDEX_TO_KHZ(state->current_bandwidth) / 100); + dib7000p_write_word(state, 23, (u16) (timf >> 16)); + dib7000p_write_word(state, 24, (u16) (timf & 0xffff)); + dprintk("-D- Updated timf_frequency: %d (default: %d)\n",state->timf, state->cfg.bw->timf); +} + +static void dib7000p_set_channel(struct dib7000p_state *state, struct dibx000_ofdm_channel *ch, u8 seq) +{ + u16 tmp, est[4]; // reg_26, reg_32, reg_33, reg_187, reg_188, reg_189, reg_190, reg_207, reg_208; + + /* nfft, guard, qam, alpha */ + dib7000p_write_word(state, 0, (ch->nfft << 7) | (ch->guard << 5) | (ch->nqam << 3) | (ch->vit_alpha)); + dib7000p_write_word(state, 5, (seq << 4) | 1); /* do not force tps, search list 0 */ + + /* P_dintl_native, P_dintlv_inv, P_vit_hrch, P_vit_code_rate, P_vit_select_hp */ + tmp = (ch->intlv_native << 6) | (ch->vit_hrch << 4) | (ch->vit_select_hp & 0x1); + if (ch->vit_hrch == 0 || ch->vit_select_hp == 1) + tmp |= (ch->vit_code_rate_hp << 1); + else + tmp |= (ch->vit_code_rate_lp << 1); + dib7000p_write_word(state, 208, tmp); + + /* P_dvsy_sync_wait */ + switch (ch->nfft) { + case 1: tmp = 256; break; + case 2: tmp = 128; break; + case 0: + default: tmp = 64; break; + } + tmp *= ((1 << (ch->guard)) * 3 / 2); // add 50% SFN margin + tmp <<= 4; + + /* deactive the possibility of diversity reception if extended interleave */ + /* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */ + if (ch->intlv_native || ch->nfft == 1) + tmp |= (1 << 2) | (2 << 0); + dib7000p_write_word(state, 207, tmp); + + dib7000p_write_word(state, 26, 0x6680); // timf(6xxx) + dib7000p_write_word(state, 29, 0x1273); // isi inh1273 on1073 + dib7000p_write_word(state, 32, 0x0003); // pha_off_max(xxx3) + dib7000p_write_word(state, 33, 0x0005); // sfreq(xxx5) + + /* channel estimation fine configuration */ + switch (ch->nqam) { + case 2: + est[0] = 0x0148; /* P_adp_regul_cnt 0.04 */ + est[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */ + est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ + est[3] = 0xfff8; /* P_adp_noise_ext -0.001 */ + break; + case 1: + est[0] = 0x023d; /* P_adp_regul_cnt 0.07 */ + est[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */ + est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ + est[3] = 0xfff0; /* P_adp_noise_ext -0.002 */ + break; + default: + est[0] = 0x099a; /* P_adp_regul_cnt 0.3 */ + est[1] = 0xffae; /* P_adp_noise_cnt -0.01 */ + est[2] = 0x0333; /* P_adp_regul_ext 0.1 */ + est[3] = 0xfff8; /* P_adp_noise_ext -0.002 */ + break; + } + for (tmp = 0; tmp < 4; tmp++) + dib7000p_write_word(state, 187 + tmp, est[tmp]); + + // set power-up level: interf+analog+AGC + dib7000p_set_power_mode(state, DIB7000P_POWER_ALL); + dib7000p_set_adc_state(state, DIBX000_ADC_ON); + dib7000p_pll_clk_cfg(state); + msleep(7); + + // AGC initialization + if (state->cfg.agc_control) + state->cfg.agc_control(&state->demod, 1); + + dib7000p_restart_agc(state); + + // wait AGC rough lock time + msleep(5); + + dib7000p_update_lna(state); + + // wait AGC accurate lock time + msleep(7); + if (state->cfg.agc_control) + state->cfg.agc_control(&state->demod, 0); +} + +static int dib7000p_autosearch_start(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch) +{ + struct dib7000p_state *state = demod->demodulator_priv; + struct dibx000_ofdm_channel auto_ch; + u32 value; + + INIT_OFDM_CHANNEL(&auto_ch); + auto_ch.RF_kHz = ch->RF_kHz; + auto_ch.Bw = ch->Bw; + auto_ch.nqam = 2; + auto_ch.guard = 0; + auto_ch.nfft = 1; + auto_ch.vit_alpha = 1; + auto_ch.vit_select_hp = 1; + auto_ch.vit_code_rate_hp = 2; + auto_ch.vit_code_rate_lp = 3; + auto_ch.vit_hrch = 0; + auto_ch.intlv_native = 1; + + dib7000p_set_channel(state, &auto_ch, 7); + + // always use the setting for 8MHz here lock_time for 7,6 MHz are longer + value = 30 * state->cfg.bw->internal; + dib7000p_write_word(state, 6, (u16) ((value >> 16) & 0xffff)); // lock0 wait time + dib7000p_write_word(state, 7, (u16) (value & 0xffff)); // lock0 wait time + value = 100 * state->cfg.bw->internal; + dib7000p_write_word(state, 8, (u16) ((value >> 16) & 0xffff)); // lock1 wait time + dib7000p_write_word(state, 9, (u16) (value & 0xffff)); // lock1 wait time + value = 500 * state->cfg.bw->internal; + dib7000p_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); // lock2 wait time + dib7000p_write_word(state, 11, (u16) (value & 0xffff)); // lock2 wait time + + value = dib7000p_read_word(state, 0); + dib7000p_write_word(state, 0, (1 << 9) | value); + dib7000p_read_word(state, 1284); + dib7000p_write_word(state, 0, (u16) value); + + return 0; +} + +static int dib7000p_autosearch_is_irq(struct dvb_frontend *demod) +{ + struct dib7000p_state *state = demod->demodulator_priv; + u16 irq_pending = dib7000p_read_word(state, 1284); + + if (irq_pending & 0x1) // failed + return 1; + + if (irq_pending & 0x2) // succeeded + return 2; + + return 0; // still pending +} + +static int dib7000p_tune(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch) +{ + struct dib7000p_state *state = demod->demodulator_priv; + u16 tmp = 0; + + if (ch != NULL) + dib7000p_set_channel(state, ch, 0); + else + return -EINVAL; + + // restart demod + dib7000p_write_word(state, 770, 0x4000); + dib7000p_write_word(state, 770, 0x0000); + msleep(45); + + /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */ + dib7000p_write_word(state, 29, (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3)); + + // never achieved a lock with that bandwidth so far - wait for osc-freq to update + if (state->timf == 0) + msleep(200); + + /* offset loop parameters */ + + /* P_timf_alpha, P_corm_alpha=6, P_corm_thres=0x80 */ + tmp = (6 << 8) | 0x80; + switch (ch->nfft) { + case 0: tmp |= (7 << 12); break; + case 1: tmp |= (9 << 12); break; + case 2: tmp |= (8 << 12); break; + } + dib7000p_write_word(state, 26, tmp); /* timf_a(6xxx) */ + + /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max */ + tmp = (0 << 4); + switch (ch->nfft) { + case 0: tmp |= 0x6; break; + case 1: tmp |= 0x8; break; + case 2: tmp |= 0x7; break; + } + dib7000p_write_word(state, 32, tmp); + + /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step */ + tmp = (0 << 4); + switch (ch->nfft) { + case 0: tmp |= 0x6; break; + case 1: tmp |= 0x8; break; + case 2: tmp |= 0x7; break; + } + dib7000p_write_word(state, 33, tmp); + + tmp = dib7000p_read_word(state,509); + if (!((tmp >> 6) & 0x1)) { + /* restart the fec */ + tmp = dib7000p_read_word(state,771); + dib7000p_write_word(state, 771, tmp | (1 << 1)); + dib7000p_write_word(state, 771, tmp); + msleep(10); + tmp = dib7000p_read_word(state,509); + } + + // we achieved a lock - it's time to update the osc freq + if ((tmp >> 6) & 0x1) + dib7000p_update_timf_freq(state); + + return 0; +} + +static int dib7000p_init(struct dvb_frontend *demod) +{ + struct dibx000_agc_config *agc; + struct dib7000p_state *state = demod->demodulator_priv; + int ret = 0; + + // Demodulator default configuration + agc = state->cfg.agc; + + dib7000p_set_power_mode(state, DIB7000P_POWER_ALL); + dib7000p_set_adc_state(state, DIBX000_SLOW_ADC_ON); + + /* AGC */ + ret |= dib7000p_write_word(state, 75 , agc->setup ); + ret |= dib7000p_write_word(state, 76 , agc->inv_gain ); + ret |= dib7000p_write_word(state, 77 , agc->time_stabiliz ); + ret |= dib7000p_write_word(state, 100, (agc->alpha_level << 12) | agc->thlock); + + // Demod AGC loop configuration + ret |= dib7000p_write_word(state, 101, (agc->alpha_mant << 5) | agc->alpha_exp); + ret |= dib7000p_write_word(state, 102, (agc->beta_mant << 6) | agc->beta_exp); + + /* AGC continued */ + dprintk("-D- WBD: ref: %d, sel: %d, active: %d, alpha: %d\n", + state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel); + + if (state->wbd_ref != 0) + ret |= dib7000p_write_word(state, 105, (agc->wbd_inv << 12) | state->wbd_ref); + else + ret |= dib7000p_write_word(state, 105, (agc->wbd_inv << 12) | agc->wbd_ref); + + ret |= dib7000p_write_word(state, 106, (agc->wbd_sel << 13) | (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8) ); + + ret |= dib7000p_write_word(state, 107, agc->agc1_max); + ret |= dib7000p_write_word(state, 108, agc->agc1_min); + ret |= dib7000p_write_word(state, 109, agc->agc2_max); + ret |= dib7000p_write_word(state, 110, agc->agc2_min); + ret |= dib7000p_write_word(state, 111, (agc->agc1_pt1 << 8) | agc->agc1_pt2 ); + ret |= dib7000p_write_word(state, 112, agc->agc1_pt3); + ret |= dib7000p_write_word(state, 113, (agc->agc1_slope1 << 8) | agc->agc1_slope2); + ret |= dib7000p_write_word(state, 114, (agc->agc2_pt1 << 8) | agc->agc2_pt2); + ret |= dib7000p_write_word(state, 115, (agc->agc2_slope1 << 8) | agc->agc2_slope2); + + /* disable power smoothing */ + ret |= dib7000p_write_word(state, 145, 0); + ret |= dib7000p_write_word(state, 146, 0); + ret |= dib7000p_write_word(state, 147, 0); + ret |= dib7000p_write_word(state, 148, 0); + ret |= dib7000p_write_word(state, 149, 0); + ret |= dib7000p_write_word(state, 150, 0); + ret |= dib7000p_write_word(state, 151, 0); + ret |= dib7000p_write_word(state, 152, 0); + + // P_timf_alpha=6, P_corm_alpha=6, P_corm_thres=128 default: 6,4,26 + ret |= dib7000p_write_word(state, 26 ,0x6680); + + // P_palf_filter_on=1, P_palf_filter_freeze=0, P_palf_alpha_regul=16 + ret |= dib7000p_write_word(state, 142,0x0410); + // P_fft_freq_dir=1, P_fft_nb_to_cut=0 + ret |= dib7000p_write_word(state, 154,1 << 13); + // P_pha3_thres, default 0x3000 + ret |= dib7000p_write_word(state, 168,0x0ccd); + // P_cti_use_cpe=0, P_cti_use_prog=0, P_cti_win_len=16, default: 0x0010 + //ret |= dib7000p_write_word(state, 169,0x0010); + // P_cspu_regul=512, P_cspu_win_cut=15, default: 0x2005 + ret |= dib7000p_write_word(state, 183,0x200f); + // P_adp_regul_cnt=573, default: 410 + ret |= dib7000p_write_word(state, 187,0x023d); + // P_adp_noise_cnt= + ret |= dib7000p_write_word(state, 188,0x00a4); + // P_adp_regul_ext + ret |= dib7000p_write_word(state, 189,0x00a4); + // P_adp_noise_ext + ret |= dib7000p_write_word(state, 190,0x7ff0); + // P_adp_fil + ret |= dib7000p_write_word(state, 191,0x3ccc); + + ret |= dib7000p_write_word(state, 222,0x0010); + // P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard + ret |= dib7000p_write_word(state, 235,0x0062); + + // P_iqc_alpha_pha, P_iqc_alpha_amp_dcc_alpha, ... + if(state->cfg.tuner_is_baseband) + ret |= dib7000p_write_word(state, 36,0x0755); + else + ret |= dib7000p_write_word(state, 36,0x1f55); + + // auto search configuration + ret |= dib7000p_write_word(state, 2 ,0x0004); + ret |= dib7000p_write_word(state, 3 ,0x1000); + + /* Equal Lock */ + ret |= dib7000p_write_word(state, 4 ,0x0814); + + ret |= dib7000p_write_word(state, 6 ,0x001b); + ret |= dib7000p_write_word(state, 7 ,0x7740); + ret |= dib7000p_write_word(state, 8 ,0x005b); + ret |= dib7000p_write_word(state, 9 ,0x8d80); + ret |= dib7000p_write_word(state, 10 ,0x01c9); + ret |= dib7000p_write_word(state, 11 ,0xc380); + ret |= dib7000p_write_word(state, 12 ,0x0000); + ret |= dib7000p_write_word(state, 13 ,0x0080); + ret |= dib7000p_write_word(state, 14 ,0x0000); + ret |= dib7000p_write_word(state, 15 ,0x0090); + ret |= dib7000p_write_word(state, 16 ,0x0001); + ret |= dib7000p_write_word(state, 17 ,0xd4c0); + + // P_clk_cfg1 + ret |= dib7000p_write_word(state, 901, 0x0006); + + // P_divclksel=3 P_divbitsel=1 + ret |= dib7000p_write_word(state, 902, (3 << 10) | (1 << 6)); + + // Tuner IO bank: max drive (14mA) + divout pads max drive + ret |= dib7000p_write_word(state, 905, 0x2c8e); + + ret |= dib7000p_set_bandwidth(&state->demod, BANDWIDTH_8_MHZ); + dib7000p_sad_calib(state); + + return ret; +} + +static int dib7000p_sleep(struct dvb_frontend *demod) +{ + struct dib7000p_state *state = demod->demodulator_priv; + return dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) | dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY); +} + +static int dib7000p_identify(struct dib7000p_state *st) +{ + u16 value; + dprintk("-I- DiB7000PC: checking demod on I2C address: %d (%x)\n", + st->i2c_addr, st->i2c_addr); + + if ((value = dib7000p_read_word(st, 768)) != 0x01b3) { + dprintk("-E- DiB7000PC: wrong Vendor ID (read=0x%x)\n",value); + return -EREMOTEIO; + } + + if ((value = dib7000p_read_word(st, 769)) != 0x4000) { + dprintk("-E- DiB7000PC: wrong Device ID (%x)\n",value); + return -EREMOTEIO; + } + + return 0; +} + + +static int dib7000p_get_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u16 tps = dib7000p_read_word(state,463); + + fep->inversion = INVERSION_AUTO; + + fep->u.ofdm.bandwidth = state->current_bandwidth; + + switch ((tps >> 8) & 0x3) { + case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break; + case 1: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; break; + /* case 2: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_4K; break; */ + } + + switch (tps & 0x3) { + case 0: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; break; + case 1: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; break; + case 2: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; break; + case 3: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; break; + } + + switch ((tps >> 14) & 0x3) { + case 0: fep->u.ofdm.constellation = QPSK; break; + case 1: fep->u.ofdm.constellation = QAM_16; break; + case 2: + default: fep->u.ofdm.constellation = QAM_64; break; + } + + /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */ + /* (tps >> 13) & 0x1 == hrch is used, (tps >> 10) & 0x7 == alpha */ + + fep->u.ofdm.hierarchy_information = HIERARCHY_NONE; + switch ((tps >> 5) & 0x7) { + case 1: fep->u.ofdm.code_rate_HP = FEC_1_2; break; + case 2: fep->u.ofdm.code_rate_HP = FEC_2_3; break; + case 3: fep->u.ofdm.code_rate_HP = FEC_3_4; break; + case 5: fep->u.ofdm.code_rate_HP = FEC_5_6; break; + case 7: + default: fep->u.ofdm.code_rate_HP = FEC_7_8; break; + + } + + switch ((tps >> 2) & 0x7) { + case 1: fep->u.ofdm.code_rate_LP = FEC_1_2; break; + case 2: fep->u.ofdm.code_rate_LP = FEC_2_3; break; + case 3: fep->u.ofdm.code_rate_LP = FEC_3_4; break; + case 5: fep->u.ofdm.code_rate_LP = FEC_5_6; break; + case 7: + default: fep->u.ofdm.code_rate_LP = FEC_7_8; break; + } + + /* native interleaver: (dib7000p_read_word(state, 464) >> 5) & 0x1 */ + + return 0; +} + +static int dib7000p_set_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep) +{ + struct dib7000p_state *state = fe->demodulator_priv; + struct dibx000_ofdm_channel ch; + + INIT_OFDM_CHANNEL(&ch); + FEP2DIB(fep,&ch); + + state->current_bandwidth = fep->u.ofdm.bandwidth; + dib7000p_set_bandwidth(fe, fep->u.ofdm.bandwidth); + + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe, fep); + + if (fep->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO || + fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO || + fep->u.ofdm.constellation == QAM_AUTO || + fep->u.ofdm.code_rate_HP == FEC_AUTO) { + int i = 800, found; + + dib7000p_autosearch_start(fe, &ch); + do { + msleep(1); + found = dib7000p_autosearch_is_irq(fe); + } while (found == 0 && i--); + + dprintk("autosearch returns: %d\n",found); + if (found == 0 || found == 1) + return 0; // no channel found + + dib7000p_get_frontend(fe, fep); + FEP2DIB(fep, &ch); + } + + /* make this a config parameter */ + dib7000p_set_output_mode(state, OUTMODE_MPEG2_FIFO); + + return dib7000p_tune(fe, &ch); +} + +static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t *stat) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u16 lock = dib7000p_read_word(state, 509); + + *stat = 0; + + if (lock & 0x8000) + *stat |= FE_HAS_SIGNAL; + if (lock & 0x3000) + *stat |= FE_HAS_CARRIER; + if (lock & 0x0100) + *stat |= FE_HAS_VITERBI; + if (lock & 0x0010) + *stat |= FE_HAS_SYNC; + if (lock & 0x0008) + *stat |= FE_HAS_LOCK; + + return 0; +} + +static int dib7000p_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct dib7000p_state *state = fe->demodulator_priv; + *ber = (dib7000p_read_word(state, 500) << 16) | dib7000p_read_word(state, 501); + return 0; +} + +static int dib7000p_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) +{ + struct dib7000p_state *state = fe->demodulator_priv; + *unc = dib7000p_read_word(state, 506); + return 0; +} + +static int dib7000p_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u16 val = dib7000p_read_word(state, 394); + *strength = 65535 - val; + return 0; +} + +static int dib7000p_read_snr(struct dvb_frontend* fe, u16 *snr) +{ + *snr = 0x0000; + return 0; +} + +static int dib7000p_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static void dib7000p_release(struct dvb_frontend *demod) +{ + struct dib7000p_state *st = demod->demodulator_priv; + dibx000_exit_i2c_master(&st->i2c_master); + kfree(st); +} + +int dib7000pc_detection(struct i2c_adapter *i2c_adap) +{ + u8 tx[2], rx[2]; + struct i2c_msg msg[2] = { + { .addr = 18 >> 1, .flags = 0, .buf = tx, .len = 2 }, + { .addr = 18 >> 1, .flags = I2C_M_RD, .buf = rx, .len = 2 }, + }; + + tx[0] = 0x03; + tx[1] = 0x00; + + if (i2c_transfer(i2c_adap, msg, 2) == 2) + if (rx[0] == 0x01 && rx[1] == 0xb3) { + dprintk("-D- DiB7000PC detected\n"); + return 1; + } + + msg[0].addr = msg[1].addr = 0x40; + + if (i2c_transfer(i2c_adap, msg, 2) == 2) + if (rx[0] == 0x01 && rx[1] == 0xb3) { + dprintk("-D- DiB7000PC detected\n"); + return 1; + } + + dprintk("-D- DiB7000PC not detected\n"); + return 0; +} +EXPORT_SYMBOL(dib7000pc_detection); + +struct i2c_adapter * dib7000p_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating) +{ + struct dib7000p_state *st = demod->demodulator_priv; + return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); +} +EXPORT_SYMBOL(dib7000p_get_i2c_master); + +int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]) +{ + struct dib7000p_state st = { .i2c_adap = i2c }; + int k = 0; + u8 new_addr = 0; + + for (k = no_of_demods-1; k >= 0; k--) { + st.cfg = cfg[k]; + + /* designated i2c address */ + new_addr = (0x40 + k) << 1; + st.i2c_addr = new_addr; + if (dib7000p_identify(&st) != 0) { + st.i2c_addr = default_addr; + if (dib7000p_identify(&st) != 0) { + dprintk("DiB7000P #%d: not identified\n", k); + return -EIO; + } + } + + /* start diversity to pull_down div_str - just for i2c-enumeration */ + dib7000p_set_output_mode(&st, OUTMODE_DIVERSITY); + + /* set new i2c address and force divstart */ + dib7000p_write_word(&st, 1285, (new_addr << 2) | 0x2); + + dprintk("IC %d initialized (to i2c_address 0x%x)\n", k, new_addr); + } + + for (k = 0; k < no_of_demods; k++) { + st.cfg = cfg[k]; + st.i2c_addr = (0x40 + k) << 1; + + // unforce divstr + dib7000p_write_word(&st, 1285, st.i2c_addr << 2); + + /* deactivate div - it was just for i2c-enumeration */ + dib7000p_set_output_mode(&st, OUTMODE_HIGH_Z); + } + + return 0; +} +EXPORT_SYMBOL(dib7000p_i2c_enumeration); + +static struct dvb_frontend_ops dib7000p_ops; +struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg) +{ + struct dvb_frontend *demod; + struct dib7000p_state *st; + st = kzalloc(sizeof(struct dib7000p_state), GFP_KERNEL); + if (st == NULL) + return NULL; + + memcpy(&st->cfg, cfg, sizeof(struct dib7000p_config)); + st->i2c_adap = i2c_adap; + st->i2c_addr = i2c_addr; + st->gpio_val = cfg->gpio_val; + st->gpio_dir = cfg->gpio_dir; + + demod = &st->demod; + demod->demodulator_priv = st; + memcpy(&st->demod.ops, &dib7000p_ops, sizeof(struct dvb_frontend_ops)); + + if (dib7000p_identify(st) != 0) + goto error; + + dibx000_init_i2c_master(&st->i2c_master, DIB7000P, st->i2c_adap, st->i2c_addr); + + dib7000p_demod_reset(st); + + return demod; + +error: + kfree(st); + return NULL; +} +EXPORT_SYMBOL(dib7000p_attach); + +static struct dvb_frontend_ops dib7000p_ops = { + .info = { + .name = "DiBcom 7000PC", + .type = FE_OFDM, + .frequency_min = 44250000, + .frequency_max = 867250000, + .frequency_stepsize = 62500, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_RECOVER | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = dib7000p_release, + + .init = dib7000p_init, + .sleep = dib7000p_sleep, + + .set_frontend = dib7000p_set_frontend, + .get_tune_settings = dib7000p_fe_get_tune_settings, + .get_frontend = dib7000p_get_frontend, + + .read_status = dib7000p_read_status, + .read_ber = dib7000p_read_ber, + .read_signal_strength = dib7000p_read_signal_strength, + .read_snr = dib7000p_read_snr, + .read_ucblocks = dib7000p_read_unc_blocks, +}; + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for the DiBcom 7000PC COFDM demodulator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/dib7000p.h b/drivers/media/dvb/frontends/dib7000p.h new file mode 100644 index 0000000..79465cf --- /dev/null +++ b/drivers/media/dvb/frontends/dib7000p.h @@ -0,0 +1,46 @@ +#ifndef DIB7000P_H +#define DIB7000P_H + +#include "dibx000_common.h" + +struct dib7000p_config { + u8 output_mpeg2_in_188_bytes; + u8 hostbus_diversity; + u8 tuner_is_baseband; + int (*update_lna) (struct dvb_frontend *, u16 agc_global); + + struct dibx000_agc_config *agc; + struct dibx000_bandwidth_config *bw; + +#define DIB7000P_GPIO_DEFAULT_DIRECTIONS 0xffff + u16 gpio_dir; +#define DIB7000P_GPIO_DEFAULT_VALUES 0x0000 + u16 gpio_val; +#define DIB7000P_GPIO_PWM_POS0(v) ((v & 0xf) << 12) +#define DIB7000P_GPIO_PWM_POS1(v) ((v & 0xf) << 8 ) +#define DIB7000P_GPIO_PWM_POS2(v) ((v & 0xf) << 4 ) +#define DIB7000P_GPIO_PWM_POS3(v) (v & 0xf) +#define DIB7000P_GPIO_DEFAULT_PWM_POS 0xffff + u16 gpio_pwm_pos; + + u16 pwm_freq_div; + + u8 quartz_direct; + + int (*agc_control) (struct dvb_frontend *, u8 before); +}; + +#define DEFAULT_DIB7000P_I2C_ADDRESS 18 + +extern struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg); +extern struct i2c_adapter * dib7000p_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); +extern int dib7000pc_detection(struct i2c_adapter *i2c_adap); + +/* TODO +extern INT dib7000p_set_gpio(struct dibDemod *demod, UCHAR num, UCHAR dir, UCHAR val); +extern INT dib7000p_enable_vbg_voltage(struct dibDemod *demod); +extern void dib7000p_set_hostbus_diversity(struct dibDemod *demod, UCHAR onoff); +extern USHORT dib7000p_get_current_agc_global(struct dibDemod *demod); +*/ + +#endif diff --git a/drivers/media/dvb/frontends/dibx000_common.h b/drivers/media/dvb/frontends/dibx000_common.h index bb0c65f..a1df604 100644 --- a/drivers/media/dvb/frontends/dibx000_common.h +++ b/drivers/media/dvb/frontends/dibx000_common.h @@ -32,6 +32,13 @@ extern void dibx000_exit_i2c_master(stru #define BAND_LBAND 0x01 #define BAND_UHF 0x02 #define BAND_VHF 0x04 +#define BAND_SBAND 0x08 +#define BAND_FM 0x10 + +#define BAND_OF_FREQUENCY(freq_kHz) ( (freq_kHz) <= 115000 ? BAND_FM : \ + (freq_kHz) <= 250000 ? BAND_VHF : \ + (freq_kHz) <= 863000 ? BAND_UHF : \ + (freq_kHz) <= 2000000 ? BAND_LBAND : BAND_SBAND ) struct dibx000_agc_config { /* defines the capabilities of this AGC-setting - using the BAND_-defines*/ @@ -129,6 +136,7 @@ #define OUTMODE_MPEG2_FIFO /* I hope I can get rid of the following kludge in the near future */ struct dibx000_ofdm_channel { + u32 RF_kHz; u8 Bw; s16 nfft; s16 guard; @@ -138,9 +146,11 @@ struct dibx000_ofdm_channel { s16 vit_alpha; s16 vit_code_rate_hp; s16 vit_code_rate_lp; + u8 intlv_native; }; #define FEP2DIB(fep,ch) \ + (ch)->RF_kHz = (fep)->frequency / 1000; \ (ch)->Bw = (fep)->u.ofdm.bandwidth; \ (ch)->nfft = (fep)->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO ? -1 : (fep)->u.ofdm.transmission_mode; \ (ch)->guard = (fep)->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO ? -1 : (fep)->u.ofdm.guard_interval; \ @@ -149,7 +159,8 @@ #define FEP2DIB(fep,ch) \ (ch)->vit_select_hp = 1; \ (ch)->vit_alpha = 1; \ (ch)->vit_code_rate_hp = (fep)->u.ofdm.code_rate_HP == FEC_AUTO ? -1 : (fep)->u.ofdm.code_rate_HP; \ - (ch)->vit_code_rate_lp = (fep)->u.ofdm.code_rate_LP == FEC_AUTO ? -1 : (fep)->u.ofdm.code_rate_LP; + (ch)->vit_code_rate_lp = (fep)->u.ofdm.code_rate_LP == FEC_AUTO ? -1 : (fep)->u.ofdm.code_rate_LP; \ + (ch)->intlv_native = 1; #define INIT_OFDM_CHANNEL(ch) do {\ (ch)->Bw = 0; \ diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c index b7e7108..62de760 100644 --- a/drivers/media/dvb/frontends/dvb-pll.c +++ b/drivers/media/dvb/frontends/dvb-pll.c @@ -472,14 +472,14 @@ int dvb_pll_configure(struct dvb_pll_des printk("pll: %s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n", desc->name, div, buf[0], buf[1], buf[2], buf[3]); - return 0; + // calculate the frequency we set it to + return (div * desc->entries[i].stepsize) - desc->entries[i].offset; } EXPORT_SYMBOL(dvb_pll_configure); static int dvb_pll_release(struct dvb_frontend *fe) { - if (fe->tuner_priv) - kfree(fe->tuner_priv); + kfree(fe->tuner_priv); fe->tuner_priv = NULL; return 0; } @@ -489,7 +489,8 @@ static int dvb_pll_sleep(struct dvb_fron struct dvb_pll_priv *priv = fe->tuner_priv; u8 buf[4]; struct i2c_msg msg = - { .addr = priv->pll_i2c_address, .flags = 0, .buf = buf, .len = sizeof(buf) }; + { .addr = priv->pll_i2c_address, .flags = 0, + .buf = buf, .len = sizeof(buf) }; int i; int result; @@ -517,16 +518,16 @@ static int dvb_pll_sleep(struct dvb_fron return 0; } -static int dvb_pll_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int dvb_pll_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) { struct dvb_pll_priv *priv = fe->tuner_priv; u8 buf[4]; struct i2c_msg msg = - { .addr = priv->pll_i2c_address, .flags = 0, .buf = buf, .len = sizeof(buf) }; + { .addr = priv->pll_i2c_address, .flags = 0, + .buf = buf, .len = sizeof(buf) }; int result; - u32 div; - int i; - u32 bandwidth = 0; + u32 bandwidth = 0, frequency = 0; if (priv->i2c == NULL) return -EINVAL; @@ -536,8 +537,11 @@ static int dvb_pll_set_params(struct dvb bandwidth = params->u.ofdm.bandwidth; } - if ((result = dvb_pll_configure(priv->pll_desc, buf, params->frequency, bandwidth)) != 0) + if ((result = dvb_pll_configure(priv->pll_desc, buf, + params->frequency, bandwidth)) < 0) return result; + else + frequency = result; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -545,26 +549,19 @@ static int dvb_pll_set_params(struct dvb return result; } - // calculate the frequency we set it to - for (i = 0; i < priv->pll_desc->count; i++) { - if (params->frequency > priv->pll_desc->entries[i].limit) - continue; - break; - } - div = (params->frequency + priv->pll_desc->entries[i].offset) / priv->pll_desc->entries[i].stepsize; - priv->frequency = (div * priv->pll_desc->entries[i].stepsize) - priv->pll_desc->entries[i].offset; + priv->frequency = frequency; priv->bandwidth = bandwidth; return 0; } -static int dvb_pll_calc_regs(struct dvb_frontend *fe, struct dvb_frontend_parameters *params, u8 *buf, int buf_len) +static int dvb_pll_calc_regs(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params, + u8 *buf, int buf_len) { struct dvb_pll_priv *priv = fe->tuner_priv; int result; - u32 div; - int i; - u32 bandwidth = 0; + u32 bandwidth = 0, frequency = 0; if (buf_len < 5) return -EINVAL; @@ -574,18 +571,15 @@ static int dvb_pll_calc_regs(struct dvb_ bandwidth = params->u.ofdm.bandwidth; } - if ((result = dvb_pll_configure(priv->pll_desc, buf+1, params->frequency, bandwidth)) != 0) + if ((result = dvb_pll_configure(priv->pll_desc, buf+1, + params->frequency, bandwidth)) < 0) return result; + else + frequency = result; + buf[0] = priv->pll_i2c_address; - // calculate the frequency we set it to - for (i = 0; i < priv->pll_desc->count; i++) { - if (params->frequency > priv->pll_desc->entries[i].limit) - continue; - break; - } - div = (params->frequency + priv->pll_desc->entries[i].offset) / priv->pll_desc->entries[i].stepsize; - priv->frequency = (div * priv->pll_desc->entries[i].stepsize) - priv->pll_desc->entries[i].offset; + priv->frequency = frequency; priv->bandwidth = bandwidth; return 5; @@ -614,10 +608,13 @@ static struct dvb_tuner_ops dvb_pll_tune .get_bandwidth = dvb_pll_get_bandwidth, }; -struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc) +struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, + struct i2c_adapter *i2c, + struct dvb_pll_desc *desc) { u8 b1 [] = { 0 }; - struct i2c_msg msg = { .addr = pll_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 }; + struct i2c_msg msg = { .addr = pll_addr, .flags = I2C_M_RD, + .buf = b1, .len = 1 }; struct dvb_pll_priv *priv = NULL; int ret; @@ -640,7 +637,9 @@ struct dvb_frontend *dvb_pll_attach(stru priv->i2c = i2c; priv->pll_desc = desc; - memcpy(&fe->ops.tuner_ops, &dvb_pll_tuner_ops, sizeof(struct dvb_tuner_ops)); + memcpy(&fe->ops.tuner_ops, &dvb_pll_tuner_ops, + sizeof(struct dvb_tuner_ops)); + strncpy(fe->ops.tuner_ops.info.name, desc->name, 128); fe->ops.tuner_ops.info.frequency_min = desc->min; fe->ops.tuner_ops.info.frequency_min = desc->max; diff --git a/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h index ed5ac5a..681186a 100644 --- a/drivers/media/dvb/frontends/dvb-pll.h +++ b/drivers/media/dvb/frontends/dvb-pll.h @@ -48,7 +48,7 @@ extern struct dvb_pll_desc dvb_pll_phili extern struct dvb_pll_desc dvb_pll_thomson_fe6600; extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, - u32 freq, int bandwidth); + u32 freq, int bandwidth); /** * Attach a dvb-pll to the supplied frontend structure. @@ -59,6 +59,9 @@ extern int dvb_pll_configure(struct dvb_ * @param desc dvb_pll_desc to use. * @return Frontend pointer on success, NULL on failure */ -extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc); +extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, + int pll_addr, + struct i2c_adapter *i2c, + struct dvb_pll_desc *desc); #endif diff --git a/drivers/media/dvb/frontends/lg_h06xf.h b/drivers/media/dvb/frontends/lg_h06xf.h deleted file mode 100644 index 754d51d..0000000 --- a/drivers/media/dvb/frontends/lg_h06xf.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * lg_h06xf.h - ATSC Tuner support for LG TDVS-H06xF - * - * 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 _LG_H06XF_H_ -#define _LG_H06XF_H_ -#include "dvb-pll.h" - -static int lg_h06xf_pll_set(struct dvb_frontend* fe, struct i2c_adapter* i2c_adap, - struct dvb_frontend_parameters* params) -{ - u8 buf[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, - .buf = buf, .len = sizeof(buf) }; - int err; - - dvb_pll_configure(&dvb_pll_lg_tdvs_h06xf, buf, params->frequency, 0); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if ((err = i2c_transfer(i2c_adap, &msg, 1)) != 1) { - printk(KERN_WARNING "lg_h06xf: %s error " - "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, buf[0], buf[1], err); - if (err < 0) - return err; - else - return -EREMOTEIO; - } - - /* Set the Auxiliary Byte. */ - buf[0] = buf[2]; - buf[0] &= ~0x20; - buf[0] |= 0x18; - buf[1] = 0x50; - msg.len = 2; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if ((err = i2c_transfer(i2c_adap, &msg, 1)) != 1) { - printk(KERN_WARNING "lg_h06xf: %s error " - "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, buf[0], buf[1], err); - if (err < 0) - return err; - else - return -EREMOTEIO; - } - - return 0; -} -#endif diff --git a/drivers/media/dvb/frontends/lgdt330x.c b/drivers/media/dvb/frontends/lgdt330x.c index 9a35470..db3ef58 100644 --- a/drivers/media/dvb/frontends/lgdt330x.c +++ b/drivers/media/dvb/frontends/lgdt330x.c @@ -31,9 +31,6 @@ * Air2PC/AirStar 2 ATSC 3rd generation (HD5000) * pcHDTV HD5500 * - * TODO: - * signal strength always returns 0. - * */ #include @@ -46,9 +43,13 @@ #include #include #include "dvb_frontend.h" +#include "dvb_math.h" #include "lgdt330x_priv.h" #include "lgdt330x.h" +/* Use Equalizer Mean Squared Error instead of Phaser Tracker MSE */ +/* #define USE_EQMSE */ + static int debug = 0; module_param(debug, int, 0644); MODULE_PARM_DESC(debug,"Turn on/off lgdt330x frontend debugging (default:off)."); @@ -68,6 +69,7 @@ struct lgdt330x_state /* Demodulator private data */ fe_modulation_t current_modulation; + u32 snr; /* Result of last SNR calculation */ /* Tuner private data */ u32 current_frequency; @@ -543,151 +545,150 @@ static int lgdt3303_read_status(struct d return 0; } -static int lgdt330x_read_signal_strength(struct dvb_frontend* fe, u16* strength) +/* Calculate SNR estimation (scaled by 2^24) + + 8-VSB SNR equations from LGDT3302 and LGDT3303 datasheets, QAM + equations from LGDT3303 datasheet. VSB is the same between the '02 + and '03, so maybe QAM is too? Perhaps someone with a newer datasheet + that has QAM information could verify? + + For 8-VSB: (two ways, take your pick) + LGDT3302: + SNR_EQ = 10 * log10(25 * 24^2 / EQ_MSE) + LGDT3303: + SNR_EQ = 10 * log10(25 * 32^2 / EQ_MSE) + LGDT3302 & LGDT3303: + SNR_PT = 10 * log10(25 * 32^2 / PT_MSE) (we use this one) + For 64-QAM: + SNR = 10 * log10( 688128 / MSEQAM) + For 256-QAM: + SNR = 10 * log10( 696320 / MSEQAM) + + We re-write the snr equation as: + SNR * 2^24 = 10*(c - intlog10(MSE)) + Where for 256-QAM, c = log10(696320) * 2^24, and so on. */ + +static u32 calculate_snr(u32 mse, u32 c) { - /* not directly available. */ - *strength = 0; - return 0; + if (mse == 0) /* No signal */ + return 0; + + mse = intlog10(mse); + if (mse > c) { + /* Negative SNR, which is possible, but realisticly the + demod will lose lock before the signal gets this bad. The + API only allows for unsigned values, so just return 0 */ + return 0; + } + return 10*(c - mse); } static int lgdt3302_read_snr(struct dvb_frontend* fe, u16* snr) { -#ifdef SNR_IN_DB - /* - * Spec sheet shows formula for SNR_EQ = 10 log10(25 * 24**2 / noise) - * and SNR_PH = 10 log10(25 * 32**2 / noise) for equalizer and phase tracker - * respectively. The following tables are built on these formulas. - * The usual definition is SNR = 20 log10(signal/noise) - * If the specification is wrong the value retuned is 1/2 the actual SNR in db. - * - * This table is a an ordered list of noise values computed by the - * formula from the spec sheet such that the index into the table - * starting at 43 or 45 is the SNR value in db. There are duplicate noise - * value entries at the beginning because the SNR varies more than - * 1 db for a change of 1 digit in noise at very small values of noise. - * - * Examples from SNR_EQ table: - * noise SNR - * 0 43 - * 1 42 - * 2 39 - * 3 37 - * 4 36 - * 5 35 - * 6 34 - * 7 33 - * 8 33 - * 9 32 - * 10 32 - * 11 31 - * 12 31 - * 13 30 - */ - - static const u32 SNR_EQ[] = - { 1, 2, 2, 2, 3, 3, 4, 4, 5, 7, - 9, 11, 13, 17, 21, 26, 33, 41, 52, 65, - 81, 102, 129, 162, 204, 257, 323, 406, 511, 644, - 810, 1020, 1284, 1616, 2035, 2561, 3224, 4059, 5110, 6433, - 8098, 10195, 12835, 16158, 20341, 25608, 32238, 40585, 51094, 64323, - 80978, 101945, 128341, 161571, 203406, 256073, 0x40000 - }; - - static const u32 SNR_PH[] = - { 1, 2, 2, 2, 3, 3, 4, 5, 6, 8, - 10, 12, 15, 19, 23, 29, 37, 46, 58, 73, - 91, 115, 144, 182, 229, 288, 362, 456, 574, 722, - 909, 1144, 1440, 1813, 2282, 2873, 3617, 4553, 5732, 7216, - 9084, 11436, 14396, 18124, 22817, 28724, 36161, 45524, 57312, 72151, - 90833, 114351, 143960, 181235, 228161, 0x080000 - }; - - static u8 buf[5];/* read data buffer */ - static u32 noise; /* noise value */ - static u32 snr_db; /* index into SNR_EQ[] */ struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; + u8 buf[5]; /* read data buffer */ + u32 noise; /* noise value */ + u32 c; /* per-modulation SNR calculation constant */ - /* read both equalizer and phase tracker noise data */ - i2c_read_demod_bytes(state, EQPH_ERR0, buf, sizeof(buf)); - - if (state->current_modulation == VSB_8) { - /* Equalizer Mean-Square Error Register for VSB */ + switch(state->current_modulation) { + case VSB_8: + i2c_read_demod_bytes(state, LGDT3302_EQPH_ERR0, buf, 5); +#ifdef USE_EQMSE + /* Use Equalizer Mean-Square Error Register */ + /* SNR for ranges from -15.61 to +41.58 */ noise = ((buf[0] & 7) << 16) | (buf[1] << 8) | buf[2]; - - /* - * Look up noise value in table. - * A better search algorithm could be used... - * watch out there are duplicate entries. - */ - for (snr_db = 0; snr_db < sizeof(SNR_EQ); snr_db++) { - if (noise < SNR_EQ[snr_db]) { - *snr = 43 - snr_db; - break; - } - } - } else { - /* Phase Tracker Mean-Square Error Register for QAM */ - noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4]; - - /* Look up noise value in table. */ - for (snr_db = 0; snr_db < sizeof(SNR_PH); snr_db++) { - if (noise < SNR_PH[snr_db]) { - *snr = 45 - snr_db; - break; - } - } - } + c = 69765745; /* log10(25*24^2)*2^24 */ #else - /* Return the raw noise value */ - static u8 buf[5];/* read data buffer */ - static u32 noise; /* noise value */ - struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; - - /* read both equalizer and pase tracker noise data */ - i2c_read_demod_bytes(state, EQPH_ERR0, buf, sizeof(buf)); - - if (state->current_modulation == VSB_8) { - /* Phase Tracker Mean-Square Error Register for VSB */ + /* Use Phase Tracker Mean-Square Error Register */ + /* SNR for ranges from -13.11 to +44.08 */ noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4]; - } else { - - /* Carrier Recovery Mean-Square Error for QAM */ - i2c_read_demod_bytes(state, 0x1a, buf, 2); + c = 73957994; /* log10(25*32^2)*2^24 */ +#endif + break; + case QAM_64: + case QAM_256: + i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2); noise = ((buf[0] & 3) << 8) | buf[1]; + c = state->current_modulation == QAM_64 ? 97939837 : 98026066; + /* log10(688128)*2^24 and log10(696320)*2^24 */ + break; + default: + printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n", + __FUNCTION__); + return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */ } - /* Small values for noise mean signal is better so invert noise */ - *snr = ~noise; -#endif + state->snr = calculate_snr(noise, c); + *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */ - dprintk("%s: noise = 0x%05x, snr = %idb\n",__FUNCTION__, noise, *snr); + dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __FUNCTION__, noise, + state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16); return 0; } static int lgdt3303_read_snr(struct dvb_frontend* fe, u16* snr) { - /* Return the raw noise value */ - static u8 buf[5];/* read data buffer */ - static u32 noise; /* noise value */ struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; + u8 buf[5]; /* read data buffer */ + u32 noise; /* noise value */ + u32 c; /* per-modulation SNR calculation constant */ - if (state->current_modulation == VSB_8) { - - i2c_read_demod_bytes(state, 0x6e, buf, 5); - /* Phase Tracker Mean-Square Error Register for VSB */ + switch(state->current_modulation) { + case VSB_8: + i2c_read_demod_bytes(state, LGDT3303_EQPH_ERR0, buf, 5); +#ifdef USE_EQMSE + /* Use Equalizer Mean-Square Error Register */ + /* SNR for ranges from -16.12 to +44.08 */ + noise = ((buf[0] & 0x78) << 13) | (buf[1] << 8) | buf[2]; + c = 73957994; /* log10(25*32^2)*2^24 */ +#else + /* Use Phase Tracker Mean-Square Error Register */ + /* SNR for ranges from -13.11 to +44.08 */ noise = ((buf[0] & 7) << 16) | (buf[3] << 8) | buf[4]; - } else { - - /* Carrier Recovery Mean-Square Error for QAM */ - i2c_read_demod_bytes(state, 0x1a, buf, 2); + c = 73957994; /* log10(25*32^2)*2^24 */ +#endif + break; + case QAM_64: + case QAM_256: + i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2); noise = (buf[0] << 8) | buf[1]; + c = state->current_modulation == QAM_64 ? 97939837 : 98026066; + /* log10(688128)*2^24 and log10(696320)*2^24 */ + break; + default: + printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n", + __FUNCTION__); + return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */ } - /* Small values for noise mean signal is better so invert noise */ - *snr = ~noise; + state->snr = calculate_snr(noise, c); + *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */ + + dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __FUNCTION__, noise, + state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16); + + return 0; +} + +static int lgdt330x_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + /* Calculate Strength from SNR up to 35dB */ + /* Even though the SNR can go higher than 35dB, there is some comfort */ + /* factor in having a range of strong signals that can show at 100% */ + struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; + u16 snr; + int ret; - dprintk("%s: noise = 0x%05x, snr = %idb\n",__FUNCTION__, noise, *snr); + ret = fe->ops.read_snr(fe, &snr); + if (ret != 0) + return ret; + /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ + /* scale the range 0 - 35*2^24 into 0 - 65535 */ + if (state->snr >= 8960 * 0x10000) + *strength = 0xffff; + else + *strength = state->snr / 8960; return 0; } diff --git a/drivers/media/dvb/frontends/lgdt330x_priv.h b/drivers/media/dvb/frontends/lgdt330x_priv.h index 59b7c5b..38c7669 100644 --- a/drivers/media/dvb/frontends/lgdt330x_priv.h +++ b/drivers/media/dvb/frontends/lgdt330x_priv.h @@ -51,14 +51,19 @@ enum I2C_REG { AGC_RFIF_ACC2= 0x3b, AGC_STATUS= 0x3f, SYNC_STATUS_VSB= 0x43, - EQPH_ERR0= 0x47, - EQ_ERR1= 0x48, - EQ_ERR2= 0x49, - PH_ERR1= 0x4a, - PH_ERR2= 0x4b, DEMUX_CONTROL= 0x66, + LGDT3302_EQPH_ERR0= 0x47, + LGDT3302_EQ_ERR1= 0x48, + LGDT3302_EQ_ERR2= 0x49, + LGDT3302_PH_ERR1= 0x4a, + LGDT3302_PH_ERR2= 0x4b, LGDT3302_PACKET_ERR_COUNTER1= 0x6a, LGDT3302_PACKET_ERR_COUNTER2= 0x6b, + LGDT3303_EQPH_ERR0= 0x6e, + LGDT3303_EQ_ERR1= 0x6f, + LGDT3303_EQ_ERR2= 0x70, + LGDT3303_PH_ERR1= 0x71, + LGDT3303_PH_ERR2= 0x72, LGDT3303_PACKET_ERR_COUNTER1= 0x8b, LGDT3303_PACKET_ERR_COUNTER2= 0x8c, }; diff --git a/drivers/media/dvb/frontends/lgh06xf.c b/drivers/media/dvb/frontends/lgh06xf.c new file mode 100644 index 0000000..2202d0c --- /dev/null +++ b/drivers/media/dvb/frontends/lgh06xf.c @@ -0,0 +1,134 @@ +/* + * lgh06xf.c - ATSC Tuner support for LG TDVS-H06xF + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dvb-pll.h" +#include "lgh06xf.h" + +#define LG_H06XF_PLL_I2C_ADDR 0x61 + +struct lgh06xf_priv { + struct i2c_adapter *i2c; + u32 frequency; +}; + +static int lgh06xf_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int lgh06xf_set_params(struct dvb_frontend* fe, + struct dvb_frontend_parameters* params) +{ + struct lgh06xf_priv *priv = fe->tuner_priv; + u8 buf[4]; + struct i2c_msg msg = { .addr = LG_H06XF_PLL_I2C_ADDR, .flags = 0, + .buf = buf, .len = sizeof(buf) }; + u32 frequency; + int result; + + if ((result = dvb_pll_configure(&dvb_pll_lg_tdvs_h06xf, buf, + params->frequency, 0)) < 0) + return result; + else + frequency = result; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { + printk(KERN_WARNING "lgh06xf: %s error " + "(addr %02x <- %02x, result = %i)\n", + __FUNCTION__, buf[0], buf[1], result); + if (result < 0) + return result; + else + return -EREMOTEIO; + } + + /* Set the Auxiliary Byte. */ + buf[0] = buf[2]; + buf[0] &= ~0x20; + buf[0] |= 0x18; + buf[1] = 0x50; + msg.len = 2; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { + printk(KERN_WARNING "lgh06xf: %s error " + "(addr %02x <- %02x, result = %i)\n", + __FUNCTION__, buf[0], buf[1], result); + if (result < 0) + return result; + else + return -EREMOTEIO; + } + + priv->frequency = frequency; + + return 0; +} + +static int lgh06xf_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct lgh06xf_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static struct dvb_tuner_ops lgh06xf_tuner_ops = { + .release = lgh06xf_release, + .set_params = lgh06xf_set_params, + .get_frequency = lgh06xf_get_frequency, +}; + +struct dvb_frontend* lgh06xf_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c) +{ + struct lgh06xf_priv *priv = NULL; + + priv = kzalloc(sizeof(struct lgh06xf_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->i2c = i2c; + + memcpy(&fe->ops.tuner_ops, &lgh06xf_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + strlcpy(fe->ops.tuner_ops.info.name, dvb_pll_lg_tdvs_h06xf.name, + sizeof(fe->ops.tuner_ops.info.name)); + + fe->ops.tuner_ops.info.frequency_min = dvb_pll_lg_tdvs_h06xf.min; + fe->ops.tuner_ops.info.frequency_max = dvb_pll_lg_tdvs_h06xf.max; + + fe->tuner_priv = priv; + return fe; +} + +EXPORT_SYMBOL(lgh06xf_attach); + +MODULE_DESCRIPTION("LG TDVS-H06xF ATSC Tuner support"); +MODULE_AUTHOR("Michael Krufky"); +MODULE_LICENSE("GPL"); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/dvb/frontends/lgh06xf.h b/drivers/media/dvb/frontends/lgh06xf.h new file mode 100644 index 0000000..510b4be --- /dev/null +++ b/drivers/media/dvb/frontends/lgh06xf.h @@ -0,0 +1,35 @@ +/* + * lgh06xf.h - ATSC Tuner support for LG TDVS-H06xF + * + * 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 _LGH06XF_H_ +#define _LGH06XF_H_ +#include "dvb_frontend.h" + +#if defined(CONFIG_DVB_TUNER_LGH06XF) || (defined(CONFIG_DVB_TUNER_LGH06XF_MODULE) && defined(MODULE)) +extern struct dvb_frontend* lgh06xf_attach(struct dvb_frontend* fe, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend* lgh06xf_attach(struct dvb_frontend* fe, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif /* CONFIG_DVB_TUNER_LGH06XF */ + +#endif /* _LGH06XF_H_ */ diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c index 11e0dca..00e4bcd 100644 --- a/drivers/media/dvb/frontends/tda1004x.c +++ b/drivers/media/dvb/frontends/tda1004x.c @@ -648,18 +648,24 @@ static int tda10046_init(struct dvb_fron tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x00); // set AGC polarities break; - case TDA10046_AGC_TDA827X: + case TDA10046_AGC_TDA827X_GP11: tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x6a); // set AGC polarities break; - case TDA10046_AGC_TDA827X_GPL: + case TDA10046_AGC_TDA827X_GP00: tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities break; + case TDA10046_AGC_TDA827X_GP01: + tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup + tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold + tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize + tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x62); // set AGC polarities + break; } tda1004x_write_byteI(state, TDA1004X_CONFADC2, 0x38); tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0x61); // Turn both AGC outputs on diff --git a/drivers/media/dvb/frontends/tda1004x.h b/drivers/media/dvb/frontends/tda1004x.h index 605ad2d..ec502d7 100644 --- a/drivers/media/dvb/frontends/tda1004x.h +++ b/drivers/media/dvb/frontends/tda1004x.h @@ -35,8 +35,9 @@ enum tda10046_agc { TDA10046_AGC_DEFAULT, /* original configuration */ TDA10046_AGC_IFO_AUTO_NEG, /* IF AGC only, automatic, negtive */ TDA10046_AGC_IFO_AUTO_POS, /* IF AGC only, automatic, positive */ - TDA10046_AGC_TDA827X, /* IF AGC only, special setup for tda827x */ - TDA10046_AGC_TDA827X_GPL, /* same as above, but GPIOs 0 */ + TDA10046_AGC_TDA827X_GP11, /* IF AGC only, special setup for tda827x */ + TDA10046_AGC_TDA827X_GP00, /* same as above, but GPIOs 0 */ + TDA10046_AGC_TDA827X_GP01, /* same as above, but GPIO3=0 GPIO1=1*/ }; enum tda10046_if { diff --git a/drivers/media/dvb/frontends/tda8083.c b/drivers/media/dvb/frontends/tda8083.c index 3aa45eb..67415c9 100644 --- a/drivers/media/dvb/frontends/tda8083.c +++ b/drivers/media/dvb/frontends/tda8083.c @@ -262,12 +262,29 @@ static int tda8083_read_status(struct dv if (sync & 0x10) *status |= FE_HAS_SYNC; + if (sync & 0x20) /* frontend can not lock */ + *status |= FE_TIMEDOUT; + if ((sync & 0x1f) == 0x1f) *status |= FE_HAS_LOCK; return 0; } +static int tda8083_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct tda8083_state* state = fe->demodulator_priv; + int ret; + u8 buf[3]; + + if ((ret = tda8083_readregs(state, 0x0b, buf, sizeof(buf)))) + return ret; + + *ber = ((buf[0] & 0x1f) << 16) | (buf[1] << 8) | buf[2]; + + return 0; +} + static int tda8083_read_signal_strength(struct dvb_frontend* fe, u16* strength) { struct tda8083_state* state = fe->demodulator_priv; @@ -288,6 +305,17 @@ static int tda8083_read_snr(struct dvb_f return 0; } +static int tda8083_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct tda8083_state* state = fe->demodulator_priv; + + *ucblocks = tda8083_readreg(state, 0x0f); + if (*ucblocks == 0xff) + *ucblocks = 0xffffffff; + + return 0; +} + static int tda8083_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { struct tda8083_state* state = fe->demodulator_priv; @@ -440,6 +468,8 @@ static struct dvb_frontend_ops tda8083_o .read_status = tda8083_read_status, .read_signal_strength = tda8083_read_signal_strength, .read_snr = tda8083_read_snr, + .read_ber = tda8083_read_ber, + .read_ucblocks = tda8083_read_ucblocks, .diseqc_send_master_cmd = tda8083_send_diseqc_msg, .diseqc_send_burst = tda8083_diseqc_send_burst, diff --git a/drivers/media/dvb/frontends/tda826x.c b/drivers/media/dvb/frontends/tda826x.c index 34815b0..79f971d 100644 --- a/drivers/media/dvb/frontends/tda826x.c +++ b/drivers/media/dvb/frontends/tda826x.c @@ -42,8 +42,7 @@ struct tda826x_priv { static int tda826x_release(struct dvb_frontend *fe) { - if (fe->tuner_priv) - kfree(fe->tuner_priv); + kfree(fe->tuner_priv); fe->tuner_priv = NULL; return 0; } @@ -133,18 +132,21 @@ struct dvb_frontend *tda826x_attach(stru { struct tda826x_priv *priv = NULL; u8 b1 [] = { 0, 0 }; - struct i2c_msg msg = { .addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 2 }; + struct i2c_msg msg[2] = { + { .addr = addr, .flags = 0, .buf = NULL, .len = 0 }, + { .addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 2 } + }; int ret; dprintk("%s:\n", __FUNCTION__); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - ret = i2c_transfer (i2c, &msg, 1); + ret = i2c_transfer (i2c, msg, 2); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); - if (ret != 1) + if (ret != 2) return NULL; if (!(b1[1] & 0x80)) return NULL; diff --git a/drivers/media/dvb/frontends/tua6100.c b/drivers/media/dvb/frontends/tua6100.c index 8855439..6ba0029 100644 --- a/drivers/media/dvb/frontends/tua6100.c +++ b/drivers/media/dvb/frontends/tua6100.c @@ -43,8 +43,7 @@ struct tua6100_priv { static int tua6100_release(struct dvb_frontend *fe) { - if (fe->tuner_priv) - kfree(fe->tuner_priv); + kfree(fe->tuner_priv); fe->tuner_priv = NULL; return 0; } diff --git a/drivers/media/dvb/ttpci/av7110_ir.c b/drivers/media/dvb/ttpci/av7110_ir.c index d54bbcd..e4544ea 100644 --- a/drivers/media/dvb/ttpci/av7110_ir.c +++ b/drivers/media/dvb/ttpci/av7110_ir.c @@ -48,7 +48,8 @@ static void av7110_emit_keyup(unsigned l if (!data || !test_bit(data, input_dev->key)) return; - input_event(input_dev, EV_KEY, data, !!0); + input_report_key(input_dev, data, 0); + input_sync(input_dev); } @@ -115,14 +116,17 @@ static void av7110_emit_key(unsigned lon del_timer(&keyup_timer); if (keyup_timer.data != keycode || new_toggle != old_toggle) { delay_timer_finished = 0; - input_event(input_dev, EV_KEY, keyup_timer.data, !!0); - input_event(input_dev, EV_KEY, keycode, !0); - } else - if (delay_timer_finished) - input_event(input_dev, EV_KEY, keycode, 2); + input_event(input_dev, EV_KEY, keyup_timer.data, 0); + input_event(input_dev, EV_KEY, keycode, 1); + input_sync(input_dev); + } else if (delay_timer_finished) { + input_event(input_dev, EV_KEY, keycode, 2); + input_sync(input_dev); + } } else { delay_timer_finished = 0; - input_event(input_dev, EV_KEY, keycode, !0); + input_event(input_dev, EV_KEY, keycode, 1); + input_sync(input_dev); } keyup_timer.expires = jiffies + UP_TIMEOUT; @@ -211,6 +215,7 @@ static void ir_handler(struct av7110 *av int __devinit av7110_ir_init(struct av7110 *av7110) { static struct proc_dir_entry *e; + int err; if (av_cnt >= sizeof av_list/sizeof av_list[0]) return -ENOSPC; @@ -231,7 +236,11 @@ int __devinit av7110_ir_init(struct av71 set_bit(EV_KEY, input_dev->evbit); set_bit(EV_REP, input_dev->evbit); input_register_keys(); - input_register_device(input_dev); + err = input_register_device(input_dev); + if (err) { + input_free_device(input_dev); + return err; + } input_dev->timer.function = input_repeat_key; e = create_proc_entry("av7110_ir", S_IFREG | S_IRUGO | S_IWUSR, NULL); diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c index 2235ff8..8bdc90a 100644 --- a/drivers/media/dvb/ttpci/budget-av.c +++ b/drivers/media/dvb/ttpci/budget-av.c @@ -655,6 +655,10 @@ static struct tda10021_config philips_cu .demod_address = 0x0c, }; +static struct tda10021_config philips_cu1216_config_altaddress = { + .demod_address = 0x0d, +}; + @@ -831,7 +835,7 @@ static int philips_sd1878_tda8261_tuner_ return -EINVAL; rc=dvb_pll_configure(&dvb_pll_philips_sd1878_tda8261, buf, - params->frequency, 0); + params->frequency, 0); if(rc < 0) return rc; if (fe->ops.i2c_gate_ctrl) @@ -914,6 +918,7 @@ #define SUBID_DVBS_TV_STAR 0x0014 #define SUBID_DVBS_TV_STAR_CI 0x0016 #define SUBID_DVBS_EASYWATCH_1 0x001a #define SUBID_DVBS_EASYWATCH 0x001e +#define SUBID_DVBC_EASYWATCH 0x002a #define SUBID_DVBC_KNC1 0x0020 #define SUBID_DVBC_KNC1_PLUS 0x0021 #define SUBID_DVBC_CINERGY1200 0x1156 @@ -952,6 +957,7 @@ static void frontend_init(struct budget_ case SUBID_DVBS_KNC1_PLUS: case SUBID_DVBC_KNC1_PLUS: case SUBID_DVBT_KNC1_PLUS: + case SUBID_DVBC_EASYWATCH: saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI); break; } @@ -1006,10 +1012,15 @@ static void frontend_init(struct budget_ case SUBID_DVBC_KNC1: case SUBID_DVBC_KNC1_PLUS: case SUBID_DVBC_CINERGY1200: + case SUBID_DVBC_EASYWATCH: budget_av->reinitialise_demod = 1; fe = dvb_attach(tda10021_attach, &philips_cu1216_config, &budget_av->budget.i2c_adap, read_pwm(budget_av)); + if (fe == NULL) + fe = dvb_attach(tda10021_attach, &philips_cu1216_config_altaddress, + &budget_av->budget.i2c_adap, + read_pwm(budget_av)); if (fe) { budget_av->tda10021_poclkp = 1; budget_av->tda10021_set_frontend = fe->ops.set_frontend; @@ -1242,6 +1253,7 @@ MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BU MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR); MAKE_BUDGET_INFO(satewpls, "Satelco EasyWatch DVB-S light", BUDGET_TVSTAR); MAKE_BUDGET_INFO(satewpls1, "Satelco EasyWatch DVB-S light", BUDGET_KNC1S); +MAKE_BUDGET_INFO(satewplc, "Satelco EasyWatch DVB-C", BUDGET_KNC1CP); MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP); MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP); MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP); @@ -1260,6 +1272,7 @@ static struct pci_device_id pci_tbl[] = MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016), MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e), MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a), + MAKE_EXTENSION_PCI(satewplc, 0x1894, 0x002a), MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020), MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021), MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030), diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index cd5ec48..25d0dfc 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -143,14 +143,14 @@ static void msp430_ir_debounce(unsigned struct input_dev *dev = (struct input_dev *) data; if (dev->rep[0] == 0 || dev->rep[0] == ~0) { - input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0); - return; + input_event(dev, EV_KEY, key_map[dev->repeat_key], 0); + } else { + dev->rep[0] = 0; + dev->timer.expires = jiffies + HZ * 350 / 1000; + add_timer(&dev->timer); + input_event(dev, EV_KEY, key_map[dev->repeat_key], 2); /* REPEAT */ } - - dev->rep[0] = 0; - dev->timer.expires = jiffies + HZ * 350 / 1000; - add_timer(&dev->timer); - input_event(dev, EV_KEY, key_map[dev->repeat_key], 2); /* REPEAT */ + input_sync(dev); } static void msp430_ir_interrupt(unsigned long data) @@ -169,7 +169,7 @@ static void msp430_ir_interrupt(unsigned return; } del_timer(&dev->timer); - input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0); + input_event(dev, EV_KEY, key_map[dev->repeat_key], 0); } if (!key_map[code]) { @@ -177,15 +177,14 @@ static void msp430_ir_interrupt(unsigned return; } + input_event(dev, EV_KEY, key_map[code], 1); + input_sync(dev); + /* initialize debounce and repeat */ dev->repeat_key = code; /* Zenith remote _always_ sends 2 sequences */ dev->rep[0] = ~0; - /* 350 milliseconds */ - dev->timer.expires = jiffies + HZ * 350 / 1000; - /* MAKE */ - input_event(dev, EV_KEY, key_map[code], !0); - add_timer(&dev->timer); + mod_timer(&dev->timer, jiffies + msecs_to_jiffies(350)); } } @@ -194,8 +193,9 @@ static int msp430_ir_init(struct budget_ struct saa7146_dev *saa = budget_ci->budget.dev; struct input_dev *input_dev; int i; + int err; - budget_ci->input_dev = input_dev = input_allocate_device(); + input_dev = input_allocate_device(); if (!input_dev) return -ENOMEM; @@ -208,10 +208,16 @@ static int msp430_ir_init(struct budget_ if (key_map[i]) set_bit(key_map[i], input_dev->keybit); - input_register_device(budget_ci->input_dev); + err = input_register_device(input_dev); + if (err) { + input_free_device(input_dev); + return err; + } input_dev->timer.function = msp430_ir_debounce; + budget_ci->input_dev = input_dev; + saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_06); saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI); @@ -226,8 +232,10 @@ static void msp430_ir_deinit(struct budg saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_06); saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); - if (del_timer(&dev->timer)) - input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0); + if (del_timer(&dev->timer)) { + input_event(dev, EV_KEY, key_map[dev->repeat_key], 0); + input_sync(dev); + } input_unregister_device(dev); } diff --git a/drivers/media/dvb/ttpci/budget.c b/drivers/media/dvb/ttpci/budget.c index e58f039..56f1c80 100644 --- a/drivers/media/dvb/ttpci/budget.c +++ b/drivers/media/dvb/ttpci/budget.c @@ -46,6 +46,10 @@ #include "tda826x.h" #include "lnbp21.h" #include "bsru6.h" +static int diseqc_method; +module_param(diseqc_method, int, 0444); +MODULE_PARM_DESC(diseqc_method, "Select DiSEqC method for subsystem id 13c2:1003, 0: default, 1: more reliable (for newer revisions only)"); + static void Set22K (struct budget *budget, int state) { struct saa7146_dev *dev=budget->dev; @@ -382,6 +386,11 @@ static void frontend_init(struct budget if (budget->dvb_frontend) { budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + if (budget->dev->pci->subsystem_device == 0x1003 && diseqc_method == 0) { + budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; + budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; + budget->dvb_frontend->ops.set_tone = budget_set_tone; + } break; } break; diff --git a/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/drivers/media/dvb/ttusb-dec/ttusb_dec.c index a1c9fa9..c1f4db0 100644 --- a/drivers/media/dvb/ttusb-dec/ttusb_dec.c +++ b/drivers/media/dvb/ttusb-dec/ttusb_dec.c @@ -238,6 +238,7 @@ static void ttusb_dec_handle_irq( struct * for now lets report each signal as a key down and up*/ dprintk("%s:rc signal:%d\n", __FUNCTION__, buffer[4]); input_report_key(dec->rc_input_dev, rc_keys[buffer[4] - 1], 1); + input_sync(dec->rc_input_dev); input_report_key(dec->rc_input_dev, rc_keys[buffer[4] - 1], 0); input_sync(dec->rc_input_dev); } @@ -1188,11 +1189,12 @@ static int ttusb_init_rc( struct ttusb_d struct input_dev *input_dev; u8 b[] = { 0x00, 0x01 }; int i; + int err; usb_make_path(dec->udev, dec->rc_phys, sizeof(dec->rc_phys)); strlcpy(dec->rc_phys, "/input0", sizeof(dec->rc_phys)); - dec->rc_input_dev = input_dev = input_allocate_device(); + input_dev = input_allocate_device(); if (!input_dev) return -ENOMEM; @@ -1206,8 +1208,13 @@ static int ttusb_init_rc( struct ttusb_d for (i = 0; i < ARRAY_SIZE(rc_keys); i++) set_bit(rc_keys[i], input_dev->keybit); - input_register_device(input_dev); + err = input_register_device(input_dev); + if (err) { + input_free_device(input_dev); + return err; + } + dec->rc_input_dev = input_dev; if (usb_submit_urb(dec->irq_urb, GFP_KERNEL)) printk("%s: usb_submit_urb failed\n",__FUNCTION__); /* enable irq pipe */ diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index bf26755..0a4274e 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -184,6 +184,14 @@ config VIDEO_KS0127 To compile this driver as a module, choose M here: the module will be called ks0127. +config VIDEO_OV7670 + tristate "OmniVision OV7670 sensor support" + depends on I2C && VIDEO_V4L2 + ---help--- + This is a Video4Linux2 sensor-level driver for the OmniVision + OV7670 VGA camera. It currently only works with the M88ALP01 + controller. + config VIDEO_SAA7110 tristate "Philips SAA7110 video decoder" depends on VIDEO_V4L1 && I2C @@ -670,6 +678,15 @@ config VIDEO_M32R_AR_M64278 To compile this driver as a module, choose M here: the module will be called arv. +config VIDEO_CAFE_CCIC + tristate "Marvell 88ALP01 (Cafe) CMOS Camera Controller support" + depends on I2C && VIDEO_V4L2 + select VIDEO_OV7670 + ---help--- + This is a video4linux2 driver for the Marvell 88ALP01 integrated + CMOS camera controller. This is the controller found on first- + generation OLPC systems. + # # USB Multimedia device configuration # diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index af57abc..2adb56d 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -92,6 +92,9 @@ obj-$(CONFIG_VIDEO_UPD64031A) += upd6403 obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o +obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o +obj-$(CONFIG_VIDEO_OV7670) += ov7670.o + obj-$(CONFIG_USB_DABUSB) += dabusb.o obj-$(CONFIG_USB_OV511) += ov511.o obj-$(CONFIG_USB_SE401) += se401.o diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 6e1ddad..3c8e474 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -1793,7 +1793,7 @@ static int bttv_common_ioctls(struct btt memset(i,0,sizeof(*i)); i->index = n; i->type = V4L2_INPUT_TYPE_CAMERA; - i->audioset = 0; + i->audioset = 1; if (i->index == bttv_tvcards[btv->c.type].tuner) { sprintf(i->name, "Television"); i->type = V4L2_INPUT_TYPE_TUNER; diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c index 933d6db..cbc012f 100644 --- a/drivers/media/video/bt8xx/bttv-input.c +++ b/drivers/media/video/bt8xx/bttv-input.c @@ -259,24 +259,59 @@ static void bttv_rc5_timer_keyup(unsigne /* ---------------------------------------------------------------------- */ +static void bttv_ir_start(struct bttv *btv, struct bttv_ir *ir) +{ + if (ir->polling) { + init_timer(&ir->timer); + ir->timer.function = bttv_input_timer; + ir->timer.data = (unsigned long)btv; + ir->timer.expires = jiffies + HZ; + add_timer(&ir->timer); + } else if (ir->rc5_gpio) { + /* set timer_end for code completion */ + init_timer(&ir->timer_end); + ir->timer_end.function = bttv_rc5_timer_end; + ir->timer_end.data = (unsigned long)ir; + + init_timer(&ir->timer_keyup); + ir->timer_keyup.function = bttv_rc5_timer_keyup; + ir->timer_keyup.data = (unsigned long)ir; + } +} + +static void bttv_ir_stop(struct bttv *btv) +{ + if (btv->remote->polling) { + del_timer_sync(&btv->remote->timer); + flush_scheduled_work(); + } + + if (btv->remote->rc5_gpio) { + u32 gpio; + + del_timer_sync(&btv->remote->timer_end); + flush_scheduled_work(); + + gpio = bttv_gpio_read(&btv->c); + bttv_gpio_write(&btv->c, gpio & ~(1 << 4)); + } +} + int bttv_input_init(struct bttv *btv) { struct bttv_ir *ir; IR_KEYTAB_TYPE *ir_codes = NULL; struct input_dev *input_dev; int ir_type = IR_TYPE_OTHER; + int err = -ENOMEM; if (!btv->has_remote) return -ENODEV; ir = kzalloc(sizeof(*ir),GFP_KERNEL); input_dev = input_allocate_device(); - if (!ir || !input_dev) { - kfree(ir); - input_free_device(input_dev); - return -ENOMEM; - } - memset(ir,0,sizeof(*ir)); + if (!ir || !input_dev) + goto err_out_free; /* detect & configure */ switch (btv->c.type) { @@ -348,10 +383,9 @@ int bttv_input_init(struct bttv *btv) break; } if (NULL == ir_codes) { - dprintk(KERN_INFO "Ooops: IR config error [card=%d]\n",btv->c.type); - kfree(ir); - input_free_device(input_dev); - return -ENODEV; + dprintk(KERN_INFO "Ooops: IR config error [card=%d]\n", btv->c.type); + err = -ENODEV; + goto err_out_free; } if (ir->rc5_gpio) { @@ -389,32 +423,26 @@ int bttv_input_init(struct bttv *btv) input_dev->cdev.dev = &btv->c.pci->dev; btv->remote = ir; - if (ir->polling) { - init_timer(&ir->timer); - ir->timer.function = bttv_input_timer; - ir->timer.data = (unsigned long)btv; - ir->timer.expires = jiffies + HZ; - add_timer(&ir->timer); - } else if (ir->rc5_gpio) { - /* set timer_end for code completion */ - init_timer(&ir->timer_end); - ir->timer_end.function = bttv_rc5_timer_end; - ir->timer_end.data = (unsigned long)ir; - - init_timer(&ir->timer_keyup); - ir->timer_keyup.function = bttv_rc5_timer_keyup; - ir->timer_keyup.data = (unsigned long)ir; - } + bttv_ir_start(btv, ir); /* all done */ - input_register_device(btv->remote->dev); - printk(DEVNAME ": %s detected at %s\n",ir->name,ir->phys); + err = input_register_device(btv->remote->dev); + if (err) + goto err_out_stop; /* the remote isn't as bouncy as a keyboard */ ir->dev->rep[REP_DELAY] = repeat_delay; ir->dev->rep[REP_PERIOD] = repeat_period; return 0; + + err_out_stop: + bttv_ir_stop(btv); + btv->remote = NULL; + err_out_free: + input_free_device(input_dev); + kfree(ir); + return err; } void bttv_input_fini(struct bttv *btv) @@ -422,22 +450,7 @@ void bttv_input_fini(struct bttv *btv) if (btv->remote == NULL) return; - if (btv->remote->polling) { - del_timer_sync(&btv->remote->timer); - flush_scheduled_work(); - } - - - if (btv->remote->rc5_gpio) { - u32 gpio; - - del_timer_sync(&btv->remote->timer_end); - flush_scheduled_work(); - - gpio = bttv_gpio_read(&btv->c); - bttv_gpio_write(&btv->c, gpio & ~(1 << 4)); - } - + bttv_ir_stop(btv); input_unregister_device(btv->remote->dev); kfree(btv->remote); btv->remote = NULL; diff --git a/drivers/media/video/cafe_ccic-regs.h b/drivers/media/video/cafe_ccic-regs.h new file mode 100644 index 0000000..b2c22a0 --- /dev/null +++ b/drivers/media/video/cafe_ccic-regs.h @@ -0,0 +1,160 @@ +/* + * Register definitions for the m88alp01 camera interface. Offsets in bytes + * as given in the spec. + * + * Copyright 2006 One Laptop Per Child Association, Inc. + * + * Written by Jonathan Corbet, corbet@lwn.net. + * + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + */ +#define REG_Y0BAR 0x00 +#define REG_Y1BAR 0x04 +#define REG_Y2BAR 0x08 +/* ... */ + +#define REG_IMGPITCH 0x24 /* Image pitch register */ +#define IMGP_YP_SHFT 2 /* Y pitch params */ +#define IMGP_YP_MASK 0x00003ffc /* Y pitch field */ +#define IMGP_UVP_SHFT 18 /* UV pitch (planar) */ +#define IMGP_UVP_MASK 0x3ffc0000 +#define REG_IRQSTATRAW 0x28 /* RAW IRQ Status */ +#define IRQ_EOF0 0x00000001 /* End of frame 0 */ +#define IRQ_EOF1 0x00000002 /* End of frame 1 */ +#define IRQ_EOF2 0x00000004 /* End of frame 2 */ +#define IRQ_SOF0 0x00000008 /* Start of frame 0 */ +#define IRQ_SOF1 0x00000010 /* Start of frame 1 */ +#define IRQ_SOF2 0x00000020 /* Start of frame 2 */ +#define IRQ_OVERFLOW 0x00000040 /* FIFO overflow */ +#define IRQ_TWSIW 0x00010000 /* TWSI (smbus) write */ +#define IRQ_TWSIR 0x00020000 /* TWSI read */ +#define IRQ_TWSIE 0x00040000 /* TWSI error */ +#define TWSIIRQS (IRQ_TWSIW|IRQ_TWSIR|IRQ_TWSIE) +#define FRAMEIRQS (IRQ_EOF0|IRQ_EOF1|IRQ_EOF2|IRQ_SOF0|IRQ_SOF1|IRQ_SOF2) +#define ALLIRQS (TWSIIRQS|FRAMEIRQS|IRQ_OVERFLOW) +#define REG_IRQMASK 0x2c /* IRQ mask - same bits as IRQSTAT */ +#define REG_IRQSTAT 0x30 /* IRQ status / clear */ + +#define REG_IMGSIZE 0x34 /* Image size */ +#define IMGSZ_V_MASK 0x1fff0000 +#define IMGSZ_V_SHIFT 16 +#define IMGSZ_H_MASK 0x00003fff +#define REG_IMGOFFSET 0x38 /* IMage offset */ + +#define REG_CTRL0 0x3c /* Control 0 */ +#define C0_ENABLE 0x00000001 /* Makes the whole thing go */ + +/* Mask for all the format bits */ +#define C0_DF_MASK 0x00fffffc /* Bits 2-23 */ + +/* RGB ordering */ +#define C0_RGB4_RGBX 0x00000000 +#define C0_RGB4_XRGB 0x00000004 +#define C0_RGB4_BGRX 0x00000008 +#define C0_RGB4_XBGR 0x0000000c +#define C0_RGB5_RGGB 0x00000000 +#define C0_RGB5_GRBG 0x00000004 +#define C0_RGB5_GBRG 0x00000008 +#define C0_RGB5_BGGR 0x0000000c + +/* Spec has two fields for DIN and DOUT, but they must match, so + combine them here. */ +#define C0_DF_YUV 0x00000000 /* Data is YUV */ +#define C0_DF_RGB 0x000000a0 /* ... RGB */ +#define C0_DF_BAYER 0x00000140 /* ... Bayer */ +/* 8-8-8 must be missing from the below - ask */ +#define C0_RGBF_565 0x00000000 +#define C0_RGBF_444 0x00000800 +#define C0_RGB_BGR 0x00001000 /* Blue comes first */ +#define C0_YUV_PLANAR 0x00000000 /* YUV 422 planar format */ +#define C0_YUV_PACKED 0x00008000 /* YUV 422 packed */ +#define C0_YUV_420PL 0x0000a000 /* YUV 420 planar */ +/* Think that 420 packed must be 111 - ask */ +#define C0_YUVE_YUYV 0x00000000 /* Y1CbY0Cr */ +#define C0_YUVE_YVYU 0x00010000 /* Y1CrY0Cb */ +#define C0_YUVE_VYUY 0x00020000 /* CrY1CbY0 */ +#define C0_YUVE_UYVY 0x00030000 /* CbY1CrY0 */ +#define C0_YUVE_XYUV 0x00000000 /* 420: .YUV */ +#define C0_YUVE_XYVU 0x00010000 /* 420: .YVU */ +#define C0_YUVE_XUVY 0x00020000 /* 420: .UVY */ +#define C0_YUVE_XVUY 0x00030000 /* 420: .VUY */ +/* Bayer bits 18,19 if needed */ +#define C0_HPOL_LOW 0x01000000 /* HSYNC polarity active low */ +#define C0_VPOL_LOW 0x02000000 /* VSYNC polarity active low */ +#define C0_VCLK_LOW 0x04000000 /* VCLK on falling edge */ +#define C0_DOWNSCALE 0x08000000 /* Enable downscaler */ +#define C0_SIFM_MASK 0xc0000000 /* SIF mode bits */ +#define C0_SIF_HVSYNC 0x00000000 /* Use H/VSYNC */ +#define CO_SOF_NOSYNC 0x40000000 /* Use inband active signaling */ + + +#define REG_CTRL1 0x40 /* Control 1 */ +#define C1_444ALPHA 0x00f00000 /* Alpha field in RGB444 */ +#define C1_ALPHA_SHFT 20 +#define C1_DMAB32 0x00000000 /* 32-byte DMA burst */ +#define C1_DMAB16 0x02000000 /* 16-byte DMA burst */ +#define C1_DMAB64 0x04000000 /* 64-byte DMA burst */ +#define C1_DMAB_MASK 0x06000000 +#define C1_TWOBUFS 0x08000000 /* Use only two DMA buffers */ +#define C1_PWRDWN 0x10000000 /* Power down */ + +#define REG_CLKCTRL 0x88 /* Clock control */ +#define CLK_DIV_MASK 0x0000ffff /* Upper bits RW "reserved" */ + +#define REG_GPR 0xb4 /* General purpose register. This + controls inputs to the power and reset + pins on the OV7670 used with OLPC; + other deployments could differ. */ +#define GPR_C1EN 0x00000020 /* Pad 1 (power down) enable */ +#define GPR_C0EN 0x00000010 /* Pad 0 (reset) enable */ +#define GPR_C1 0x00000002 /* Control 1 value */ +/* + * Control 0 is wired to reset on OLPC machines. For ov7x sensors, + * it is active low, for 0v6x, instead, it's active high. What + * fun. + */ +#define GPR_C0 0x00000001 /* Control 0 value */ + +#define REG_TWSIC0 0xb8 /* TWSI (smbus) control 0 */ +#define TWSIC0_EN 0x00000001 /* TWSI enable */ +#define TWSIC0_MODE 0x00000002 /* 1 = 16-bit, 0 = 8-bit */ +#define TWSIC0_SID 0x000003fc /* Slave ID */ +#define TWSIC0_SID_SHIFT 2 +#define TWSIC0_CLKDIV 0x0007fc00 /* Clock divider */ +#define TWSIC0_MASKACK 0x00400000 /* Mask ack from sensor */ +#define TWSIC0_OVMAGIC 0x00800000 /* Make it work on OV sensors */ + +#define REG_TWSIC1 0xbc /* TWSI control 1 */ +#define TWSIC1_DATA 0x0000ffff /* Data to/from camchip */ +#define TWSIC1_ADDR 0x00ff0000 /* Address (register) */ +#define TWSIC1_ADDR_SHIFT 16 +#define TWSIC1_READ 0x01000000 /* Set for read op */ +#define TWSIC1_WSTAT 0x02000000 /* Write status */ +#define TWSIC1_RVALID 0x04000000 /* Read data valid */ +#define TWSIC1_ERROR 0x08000000 /* Something screwed up */ + + +#define REG_UBAR 0xc4 /* Upper base address register */ + +/* + * Here's the weird global control registers which are said to live + * way up here. + */ +#define REG_GL_CSR 0x3004 /* Control/status register */ +#define GCSR_SRS 0x00000001 /* SW Reset set */ +#define GCSR_SRC 0x00000002 /* SW Reset clear */ +#define GCSR_MRS 0x00000004 /* Master reset set */ +#define GCSR_MRC 0x00000008 /* HW Reset clear */ +#define GCSR_CCIC_EN 0x00004000 /* CCIC Clock enable */ +#define REG_GL_IMASK 0x300c /* Interrupt mask register */ +#define GIMSK_CCIC_EN 0x00000004 /* CCIC Interrupt enable */ + +#define REG_LEN REG_GL_IMASK + 4 + + +/* + * Useful stuff that probably belongs somewhere global. + */ +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c new file mode 100644 index 0000000..30864da --- /dev/null +++ b/drivers/media/video/cafe_ccic.c @@ -0,0 +1,2232 @@ +/* + * A driver for the CMOS camera controller in the Marvell 88ALP01 "cafe" + * multifunction chip. Currently works with the Omnivision OV7670 + * sensor. + * + * Copyright 2006 One Laptop Per Child Association, Inc. + * + * Written by Jonathan Corbet, corbet@lwn.net. + * + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cafe_ccic-regs.h" + +#define CAFE_VERSION 0x000001 + + +/* + * Parameters. + */ +MODULE_AUTHOR("Jonathan Corbet "); +MODULE_DESCRIPTION("Marvell 88ALP01 CMOS Camera Controller driver"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("Video"); + +/* + * Internal DMA buffer management. Since the controller cannot do S/G I/O, + * we must have physically contiguous buffers to bring frames into. + * These parameters control how many buffers we use, whether we + * allocate them at load time (better chance of success, but nails down + * memory) or when somebody tries to use the camera (riskier), and, + * for load-time allocation, how big they should be. + * + * The controller can cycle through three buffers. We could use + * more by flipping pointers around, but it probably makes little + * sense. + */ + +#define MAX_DMA_BUFS 3 +static int alloc_bufs_at_load = 0; +module_param(alloc_bufs_at_load, bool, 0444); +MODULE_PARM_DESC(alloc_bufs_at_load, + "Non-zero value causes DMA buffers to be allocated at module " + "load time. This increases the chances of successfully getting " + "those buffers, but at the cost of nailing down the memory from " + "the outset."); + +static int n_dma_bufs = 3; +module_param(n_dma_bufs, uint, 0644); +MODULE_PARM_DESC(n_dma_bufs, + "The number of DMA buffers to allocate. Can be either two " + "(saves memory, makes timing tighter) or three."); + +static int dma_buf_size = VGA_WIDTH * VGA_HEIGHT * 2; /* Worst case */ +module_param(dma_buf_size, uint, 0444); +MODULE_PARM_DESC(dma_buf_size, + "The size of the allocated DMA buffers. If actual operating " + "parameters require larger buffers, an attempt to reallocate " + "will be made."); + +static int min_buffers = 1; +module_param(min_buffers, uint, 0644); +MODULE_PARM_DESC(min_buffers, + "The minimum number of streaming I/O buffers we are willing " + "to work with."); + +static int max_buffers = 10; +module_param(max_buffers, uint, 0644); +MODULE_PARM_DESC(max_buffers, + "The maximum number of streaming I/O buffers an application " + "will be allowed to allocate. These buffers are big and live " + "in vmalloc space."); + +static int flip = 0; +module_param(flip, bool, 0444); +MODULE_PARM_DESC(flip, + "If set, the sensor will be instructed to flip the image " + "vertically."); + + +enum cafe_state { + S_NOTREADY, /* Not yet initialized */ + S_IDLE, /* Just hanging around */ + S_FLAKED, /* Some sort of problem */ + S_SINGLEREAD, /* In read() */ + S_SPECREAD, /* Speculative read (for future read()) */ + S_STREAMING /* Streaming data */ +}; + +/* + * Tracking of streaming I/O buffers. + */ +struct cafe_sio_buffer { + struct list_head list; + struct v4l2_buffer v4lbuf; + char *buffer; /* Where it lives in kernel space */ + int mapcount; + struct cafe_camera *cam; +}; + +/* + * A description of one of our devices. + * Locking: controlled by s_mutex. Certain fields, however, require + * the dev_lock spinlock; they are marked as such by comments. + * dev_lock is also required for access to device registers. + */ +struct cafe_camera +{ + enum cafe_state state; + unsigned long flags; /* Buffer status, mainly (dev_lock) */ + int users; /* How many open FDs */ + struct file *owner; /* Who has data access (v4l2) */ + + /* + * Subsystem structures. + */ + struct pci_dev *pdev; + struct video_device v4ldev; + struct i2c_adapter i2c_adapter; + struct i2c_client *sensor; + + unsigned char __iomem *regs; + struct list_head dev_list; /* link to other devices */ + + /* DMA buffers */ + unsigned int nbufs; /* How many are alloc'd */ + int next_buf; /* Next to consume (dev_lock) */ + unsigned int dma_buf_size; /* allocated size */ + void *dma_bufs[MAX_DMA_BUFS]; /* Internal buffer addresses */ + dma_addr_t dma_handles[MAX_DMA_BUFS]; /* Buffer bus addresses */ + unsigned int specframes; /* Unconsumed spec frames (dev_lock) */ + unsigned int sequence; /* Frame sequence number */ + unsigned int buf_seq[MAX_DMA_BUFS]; /* Sequence for individual buffers */ + + /* Streaming buffers */ + unsigned int n_sbufs; /* How many we have */ + struct cafe_sio_buffer *sb_bufs; /* The array of housekeeping structs */ + struct list_head sb_avail; /* Available for data (we own) (dev_lock) */ + struct list_head sb_full; /* With data (user space owns) (dev_lock) */ + struct tasklet_struct s_tasklet; + + /* Current operating parameters */ + enum v4l2_chip_ident sensor_type; /* Currently ov7670 only */ + struct v4l2_pix_format pix_format; + + /* Locks */ + struct mutex s_mutex; /* Access to this structure */ + spinlock_t dev_lock; /* Access to device */ + + /* Misc */ + wait_queue_head_t smbus_wait; /* Waiting on i2c events */ + wait_queue_head_t iowait; /* Waiting on frame data */ +#ifdef CONFIG_VIDEO_ADV_DEBUG + struct dentry *dfs_regs; + struct dentry *dfs_cam_regs; +#endif +}; + +/* + * Status flags. Always manipulated with bit operations. + */ +#define CF_BUF0_VALID 0 /* Buffers valid - first three */ +#define CF_BUF1_VALID 1 +#define CF_BUF2_VALID 2 +#define CF_DMA_ACTIVE 3 /* A frame is incoming */ +#define CF_CONFIG_NEEDED 4 /* Must configure hardware */ + + + +/* + * Start over with DMA buffers - dev_lock needed. + */ +static void cafe_reset_buffers(struct cafe_camera *cam) +{ + int i; + + cam->next_buf = -1; + for (i = 0; i < cam->nbufs; i++) + clear_bit(i, &cam->flags); + cam->specframes = 0; +} + +static inline int cafe_needs_config(struct cafe_camera *cam) +{ + return test_bit(CF_CONFIG_NEEDED, &cam->flags); +} + +static void cafe_set_config_needed(struct cafe_camera *cam, int needed) +{ + if (needed) + set_bit(CF_CONFIG_NEEDED, &cam->flags); + else + clear_bit(CF_CONFIG_NEEDED, &cam->flags); +} + + + + +/* + * Debugging and related. + */ +#define cam_err(cam, fmt, arg...) \ + dev_err(&(cam)->pdev->dev, fmt, ##arg); +#define cam_warn(cam, fmt, arg...) \ + dev_warn(&(cam)->pdev->dev, fmt, ##arg); +#define cam_dbg(cam, fmt, arg...) \ + dev_dbg(&(cam)->pdev->dev, fmt, ##arg); + + +/* ---------------------------------------------------------------------*/ +/* + * We keep a simple list of known devices to search at open time. + */ +static LIST_HEAD(cafe_dev_list); +static DEFINE_MUTEX(cafe_dev_list_lock); + +static void cafe_add_dev(struct cafe_camera *cam) +{ + mutex_lock(&cafe_dev_list_lock); + list_add_tail(&cam->dev_list, &cafe_dev_list); + mutex_unlock(&cafe_dev_list_lock); +} + +static void cafe_remove_dev(struct cafe_camera *cam) +{ + mutex_lock(&cafe_dev_list_lock); + list_del(&cam->dev_list); + mutex_unlock(&cafe_dev_list_lock); +} + +static struct cafe_camera *cafe_find_dev(int minor) +{ + struct cafe_camera *cam; + + mutex_lock(&cafe_dev_list_lock); + list_for_each_entry(cam, &cafe_dev_list, dev_list) { + if (cam->v4ldev.minor == minor) + goto done; + } + cam = NULL; + done: + mutex_unlock(&cafe_dev_list_lock); + return cam; +} + + +static struct cafe_camera *cafe_find_by_pdev(struct pci_dev *pdev) +{ + struct cafe_camera *cam; + + mutex_lock(&cafe_dev_list_lock); + list_for_each_entry(cam, &cafe_dev_list, dev_list) { + if (cam->pdev == pdev) + goto done; + } + cam = NULL; + done: + mutex_unlock(&cafe_dev_list_lock); + return cam; +} + + +/* ------------------------------------------------------------------------ */ +/* + * Device register I/O + */ +static inline void cafe_reg_write(struct cafe_camera *cam, unsigned int reg, + unsigned int val) +{ + iowrite32(val, cam->regs + reg); +} + +static inline unsigned int cafe_reg_read(struct cafe_camera *cam, + unsigned int reg) +{ + return ioread32(cam->regs + reg); +} + + +static inline void cafe_reg_write_mask(struct cafe_camera *cam, unsigned int reg, + unsigned int val, unsigned int mask) +{ + unsigned int v = cafe_reg_read(cam, reg); + + v = (v & ~mask) | (val & mask); + cafe_reg_write(cam, reg, v); +} + +static inline void cafe_reg_clear_bit(struct cafe_camera *cam, + unsigned int reg, unsigned int val) +{ + cafe_reg_write_mask(cam, reg, 0, val); +} + +static inline void cafe_reg_set_bit(struct cafe_camera *cam, + unsigned int reg, unsigned int val) +{ + cafe_reg_write_mask(cam, reg, val, val); +} + + + +/* -------------------------------------------------------------------- */ +/* + * The I2C/SMBUS interface to the camera itself starts here. The + * controller handles SMBUS itself, presenting a relatively simple register + * interface; all we have to do is to tell it where to route the data. + */ +#define CAFE_SMBUS_TIMEOUT (HZ) /* generous */ + +static int cafe_smbus_write_done(struct cafe_camera *cam) +{ + unsigned long flags; + int c1; + + /* + * We must delay after the interrupt, or the controller gets confused + * and never does give us good status. Fortunately, we don't do this + * often. + */ + udelay(20); + spin_lock_irqsave(&cam->dev_lock, flags); + c1 = cafe_reg_read(cam, REG_TWSIC1); + spin_unlock_irqrestore(&cam->dev_lock, flags); + return (c1 & (TWSIC1_WSTAT|TWSIC1_ERROR)) != TWSIC1_WSTAT; +} + +static int cafe_smbus_write_data(struct cafe_camera *cam, + u16 addr, u8 command, u8 value) +{ + unsigned int rval; + unsigned long flags; + + spin_lock_irqsave(&cam->dev_lock, flags); + rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID); + rval |= TWSIC0_OVMAGIC; /* Make OV sensors work */ + /* + * Marvell sez set clkdiv to all 1's for now. + */ + rval |= TWSIC0_CLKDIV; + cafe_reg_write(cam, REG_TWSIC0, rval); + (void) cafe_reg_read(cam, REG_TWSIC1); /* force write */ + rval = value | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR); + cafe_reg_write(cam, REG_TWSIC1, rval); + spin_unlock_irqrestore(&cam->dev_lock, flags); + msleep(2); /* Required or things flake */ + + wait_event_timeout(cam->smbus_wait, cafe_smbus_write_done(cam), + CAFE_SMBUS_TIMEOUT); + spin_lock_irqsave(&cam->dev_lock, flags); + rval = cafe_reg_read(cam, REG_TWSIC1); + spin_unlock_irqrestore(&cam->dev_lock, flags); + + if (rval & TWSIC1_WSTAT) { + cam_err(cam, "SMBUS write (%02x/%02x/%02x) timed out\n", addr, + command, value); + return -EIO; + } + if (rval & TWSIC1_ERROR) { + cam_err(cam, "SMBUS write (%02x/%02x/%02x) error\n", addr, + command, value); + return -EIO; + } + return 0; +} + + + +static int cafe_smbus_read_done(struct cafe_camera *cam) +{ + unsigned long flags; + int c1; + + /* + * We must delay after the interrupt, or the controller gets confused + * and never does give us good status. Fortunately, we don't do this + * often. + */ + udelay(20); + spin_lock_irqsave(&cam->dev_lock, flags); + c1 = cafe_reg_read(cam, REG_TWSIC1); + spin_unlock_irqrestore(&cam->dev_lock, flags); + return c1 & (TWSIC1_RVALID|TWSIC1_ERROR); +} + + + +static int cafe_smbus_read_data(struct cafe_camera *cam, + u16 addr, u8 command, u8 *value) +{ + unsigned int rval; + unsigned long flags; + + spin_lock_irqsave(&cam->dev_lock, flags); + rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID); + rval |= TWSIC0_OVMAGIC; /* Make OV sensors work */ + /* + * Marvel sez set clkdiv to all 1's for now. + */ + rval |= TWSIC0_CLKDIV; + cafe_reg_write(cam, REG_TWSIC0, rval); + (void) cafe_reg_read(cam, REG_TWSIC1); /* force write */ + rval = TWSIC1_READ | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR); + cafe_reg_write(cam, REG_TWSIC1, rval); + spin_unlock_irqrestore(&cam->dev_lock, flags); + + wait_event_timeout(cam->smbus_wait, + cafe_smbus_read_done(cam), CAFE_SMBUS_TIMEOUT); + spin_lock_irqsave(&cam->dev_lock, flags); + rval = cafe_reg_read(cam, REG_TWSIC1); + spin_unlock_irqrestore(&cam->dev_lock, flags); + + if (rval & TWSIC1_ERROR) { + cam_err(cam, "SMBUS read (%02x/%02x) error\n", addr, command); + return -EIO; + } + if (! (rval & TWSIC1_RVALID)) { + cam_err(cam, "SMBUS read (%02x/%02x) timed out\n", addr, + command); + return -EIO; + } + *value = rval & 0xff; + return 0; +} + +/* + * Perform a transfer over SMBUS. This thing is called under + * the i2c bus lock, so we shouldn't race with ourselves... + */ +static int cafe_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char rw, u8 command, + int size, union i2c_smbus_data *data) +{ + struct cafe_camera *cam = i2c_get_adapdata(adapter); + int ret = -EINVAL; + + /* + * Refuse to talk to anything but OV cam chips. We should + * never even see an attempt to do so, but one never knows. + */ + if (cam->sensor && addr != cam->sensor->addr) { + cam_err(cam, "funky smbus addr %d\n", addr); + return -EINVAL; + } + /* + * This interface would appear to only do byte data ops. OK + * it can do word too, but the cam chip has no use for that. + */ + if (size != I2C_SMBUS_BYTE_DATA) { + cam_err(cam, "funky xfer size %d\n", size); + return -EINVAL; + } + + if (rw == I2C_SMBUS_WRITE) + ret = cafe_smbus_write_data(cam, addr, command, data->byte); + else if (rw == I2C_SMBUS_READ) + ret = cafe_smbus_read_data(cam, addr, command, &data->byte); + return ret; +} + + +static void cafe_smbus_enable_irq(struct cafe_camera *cam) +{ + unsigned long flags; + + spin_lock_irqsave(&cam->dev_lock, flags); + cafe_reg_set_bit(cam, REG_IRQMASK, TWSIIRQS); + spin_unlock_irqrestore(&cam->dev_lock, flags); +} + +static u32 cafe_smbus_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_READ_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA; +} + +static struct i2c_algorithm cafe_smbus_algo = { + .smbus_xfer = cafe_smbus_xfer, + .functionality = cafe_smbus_func +}; + +/* Somebody is on the bus */ +static int cafe_cam_init(struct cafe_camera *cam); +static void cafe_ctlr_stop_dma(struct cafe_camera *cam); +static void cafe_ctlr_power_down(struct cafe_camera *cam); + +static int cafe_smbus_attach(struct i2c_client *client) +{ + struct cafe_camera *cam = i2c_get_adapdata(client->adapter); + + /* + * Don't talk to chips we don't recognize. + */ + if (client->driver->id == I2C_DRIVERID_OV7670) { + cam->sensor = client; + return cafe_cam_init(cam); + } + return -EINVAL; +} + +static int cafe_smbus_detach(struct i2c_client *client) +{ + struct cafe_camera *cam = i2c_get_adapdata(client->adapter); + + if (cam->sensor == client) { + cafe_ctlr_stop_dma(cam); + cafe_ctlr_power_down(cam); + cam_err(cam, "lost the sensor!\n"); + cam->sensor = NULL; /* Bummer, no camera */ + cam->state = S_NOTREADY; + } + return 0; +} + +static int cafe_smbus_setup(struct cafe_camera *cam) +{ + struct i2c_adapter *adap = &cam->i2c_adapter; + int ret; + + cafe_smbus_enable_irq(cam); + adap->id = I2C_HW_SMBUS_CAFE; + adap->class = I2C_CLASS_CAM_DIGITAL; + adap->owner = THIS_MODULE; + adap->client_register = cafe_smbus_attach; + adap->client_unregister = cafe_smbus_detach; + adap->algo = &cafe_smbus_algo; + strcpy(adap->name, "cafe_ccic"); + i2c_set_adapdata(adap, cam); + ret = i2c_add_adapter(adap); + if (ret) + printk(KERN_ERR "Unable to register cafe i2c adapter\n"); + return ret; +} + +static void cafe_smbus_shutdown(struct cafe_camera *cam) +{ + i2c_del_adapter(&cam->i2c_adapter); +} + + +/* ------------------------------------------------------------------- */ +/* + * Deal with the controller. + */ + +/* + * Do everything we think we need to have the interface operating + * according to the desired format. + */ +static void cafe_ctlr_dma(struct cafe_camera *cam) +{ + /* + * Store the first two Y buffers (we aren't supporting + * planar formats for now, so no UV bufs). Then either + * set the third if it exists, or tell the controller + * to just use two. + */ + cafe_reg_write(cam, REG_Y0BAR, cam->dma_handles[0]); + cafe_reg_write(cam, REG_Y1BAR, cam->dma_handles[1]); + if (cam->nbufs > 2) { + cafe_reg_write(cam, REG_Y2BAR, cam->dma_handles[2]); + cafe_reg_clear_bit(cam, REG_CTRL1, C1_TWOBUFS); + } + else + cafe_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS); + cafe_reg_write(cam, REG_UBAR, 0); /* 32 bits only for now */ +} + +static void cafe_ctlr_image(struct cafe_camera *cam) +{ + int imgsz; + struct v4l2_pix_format *fmt = &cam->pix_format; + + imgsz = ((fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK) | + (fmt->bytesperline & IMGSZ_H_MASK); + cafe_reg_write(cam, REG_IMGSIZE, imgsz); + cafe_reg_write(cam, REG_IMGOFFSET, 0); + /* YPITCH just drops the last two bits */ + cafe_reg_write_mask(cam, REG_IMGPITCH, fmt->bytesperline, + IMGP_YP_MASK); + /* + * Tell the controller about the image format we are using. + */ + switch (cam->pix_format.pixelformat) { + case V4L2_PIX_FMT_YUYV: + cafe_reg_write_mask(cam, REG_CTRL0, + C0_DF_YUV|C0_YUV_PACKED|C0_YUVE_YUYV, + C0_DF_MASK); + break; + + /* + * For "fake rgb32" get the image pitch right. + */ + case V4L2_PIX_FMT_RGB32: + cafe_reg_write_mask(cam, REG_IMGPITCH, fmt->bytesperline/2, + IMGP_YP_MASK); + imgsz = ((fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK) | + ((fmt->bytesperline/2) & IMGSZ_H_MASK); + cafe_reg_write(cam, REG_IMGSIZE, imgsz); + /* fall into ... */ + case V4L2_PIX_FMT_RGB444: + cafe_reg_write_mask(cam, REG_CTRL0, + C0_DF_RGB|C0_RGBF_444|C0_RGB4_XRGB, + C0_DF_MASK); + /* Alpha value? */ + break; + + case V4L2_PIX_FMT_RGB565: + cafe_reg_write_mask(cam, REG_CTRL0, + C0_DF_RGB|C0_RGBF_565|C0_RGB5_BGGR, + C0_DF_MASK); + break; + + default: + cam_err(cam, "Unknown format %x\n", cam->pix_format.pixelformat); + break; + } + /* + * Make sure it knows we want to use hsync/vsync. + */ + cafe_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC, + C0_SIFM_MASK); +} + + +/* + * Configure the controller for operation; caller holds the + * device mutex. + */ +static int cafe_ctlr_configure(struct cafe_camera *cam) +{ + unsigned long flags; + + spin_lock_irqsave(&cam->dev_lock, flags); + cafe_ctlr_dma(cam); + cafe_ctlr_image(cam); + cafe_set_config_needed(cam, 0); + spin_unlock_irqrestore(&cam->dev_lock, flags); + return 0; +} + +static void cafe_ctlr_irq_enable(struct cafe_camera *cam) +{ + /* + * Clear any pending interrupts, since we do not + * expect to have I/O active prior to enabling. + */ + cafe_reg_write(cam, REG_IRQSTAT, FRAMEIRQS); + cafe_reg_set_bit(cam, REG_IRQMASK, FRAMEIRQS); +} + +static void cafe_ctlr_irq_disable(struct cafe_camera *cam) +{ + cafe_reg_clear_bit(cam, REG_IRQMASK, FRAMEIRQS); +} + +/* + * Make the controller start grabbing images. Everything must + * be set up before doing this. + */ +static void cafe_ctlr_start(struct cafe_camera *cam) +{ + /* set_bit performs a read, so no other barrier should be + needed here */ + cafe_reg_set_bit(cam, REG_CTRL0, C0_ENABLE); +} + +static void cafe_ctlr_stop(struct cafe_camera *cam) +{ + cafe_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE); +} + +static void cafe_ctlr_init(struct cafe_camera *cam) +{ + unsigned long flags; + + spin_lock_irqsave(&cam->dev_lock, flags); + /* + * Added magic to bring up the hardware on the B-Test board + */ + cafe_reg_write(cam, 0x3038, 0x8); + cafe_reg_write(cam, 0x315c, 0x80008); + /* + * Go through the dance needed to wake the device up. + * Note that these registers are global and shared + * with the NAND and SD devices. Interaction between the + * three still needs to be examined. + */ + cafe_reg_write(cam, REG_GL_CSR, GCSR_SRS|GCSR_MRS); /* Needed? */ + cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRC); + cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRS); + mdelay(5); /* FIXME revisit this */ + cafe_reg_write(cam, REG_GL_CSR, GCSR_CCIC_EN|GCSR_SRC|GCSR_MRC); + cafe_reg_set_bit(cam, REG_GL_IMASK, GIMSK_CCIC_EN); + /* + * Make sure it's not powered down. + */ + cafe_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN); + /* + * Turn off the enable bit. It sure should be off anyway, + * but it's good to be sure. + */ + cafe_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE); + /* + * Mask all interrupts. + */ + cafe_reg_write(cam, REG_IRQMASK, 0); + /* + * Clock the sensor appropriately. Controller clock should + * be 48MHz, sensor "typical" value is half that. + */ + cafe_reg_write_mask(cam, REG_CLKCTRL, 2, CLK_DIV_MASK); + spin_unlock_irqrestore(&cam->dev_lock, flags); +} + + +/* + * Stop the controller, and don't return until we're really sure that no + * further DMA is going on. + */ +static void cafe_ctlr_stop_dma(struct cafe_camera *cam) +{ + unsigned long flags; + + /* + * Theory: stop the camera controller (whether it is operating + * or not). Delay briefly just in case we race with the SOF + * interrupt, then wait until no DMA is active. + */ + spin_lock_irqsave(&cam->dev_lock, flags); + cafe_ctlr_stop(cam); + spin_unlock_irqrestore(&cam->dev_lock, flags); + mdelay(1); + wait_event_timeout(cam->iowait, + !test_bit(CF_DMA_ACTIVE, &cam->flags), HZ); + if (test_bit(CF_DMA_ACTIVE, &cam->flags)) + cam_err(cam, "Timeout waiting for DMA to end\n"); + /* This would be bad news - what now? */ + spin_lock_irqsave(&cam->dev_lock, flags); + cam->state = S_IDLE; + cafe_ctlr_irq_disable(cam); + spin_unlock_irqrestore(&cam->dev_lock, flags); +} + +/* + * Power up and down. + */ +static void cafe_ctlr_power_up(struct cafe_camera *cam) +{ + unsigned long flags; + + spin_lock_irqsave(&cam->dev_lock, flags); + cafe_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN); + /* + * Put the sensor into operational mode (assumes OLPC-style + * wiring). Control 0 is reset - set to 1 to operate. + * Control 1 is power down, set to 0 to operate. + */ + cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */ + mdelay(1); /* Marvell says 1ms will do it */ + cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0); + mdelay(1); /* Enough? */ + spin_unlock_irqrestore(&cam->dev_lock, flags); +} + +static void cafe_ctlr_power_down(struct cafe_camera *cam) +{ + unsigned long flags; + + spin_lock_irqsave(&cam->dev_lock, flags); + cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C1); + cafe_reg_set_bit(cam, REG_CTRL1, C1_PWRDWN); + spin_unlock_irqrestore(&cam->dev_lock, flags); +} + +/* -------------------------------------------------------------------- */ +/* + * Communications with the sensor. + */ + +static int __cafe_cam_cmd(struct cafe_camera *cam, int cmd, void *arg) +{ + struct i2c_client *sc = cam->sensor; + int ret; + + if (sc == NULL || sc->driver == NULL || sc->driver->command == NULL) + return -EINVAL; + ret = sc->driver->command(sc, cmd, arg); + if (ret == -EPERM) /* Unsupported command */ + return 0; + return ret; +} + +static int __cafe_cam_reset(struct cafe_camera *cam) +{ + int zero = 0; + return __cafe_cam_cmd(cam, VIDIOC_INT_RESET, &zero); +} + +/* + * We have found the sensor on the i2c. Let's try to have a + * conversation. + */ +static int cafe_cam_init(struct cafe_camera *cam) +{ + int ret; + + mutex_lock(&cam->s_mutex); + if (cam->state != S_NOTREADY) + cam_warn(cam, "Cam init with device in funky state %d", + cam->state); + ret = __cafe_cam_reset(cam); + if (ret) + goto out; + ret = __cafe_cam_cmd(cam, VIDIOC_INT_G_CHIP_IDENT, &cam->sensor_type); + if (ret) + goto out; +// if (cam->sensor->addr != OV7xx0_SID) { + if (cam->sensor_type != V4L2_IDENT_OV7670) { + cam_err(cam, "Unsupported sensor type %d", cam->sensor->addr); + ret = -EINVAL; + goto out; + } +/* Get/set parameters? */ + ret = 0; + cam->state = S_IDLE; + out: + mutex_unlock(&cam->s_mutex); + return ret; +} + +/* + * Configure the sensor to match the parameters we have. Caller should + * hold s_mutex + */ +static int cafe_cam_set_flip(struct cafe_camera *cam) +{ + struct v4l2_control ctrl; + + memset(&ctrl, 0, sizeof(ctrl)); + ctrl.id = V4L2_CID_VFLIP; + ctrl.value = flip; + return __cafe_cam_cmd(cam, VIDIOC_S_CTRL, &ctrl); +} + + +static int cafe_cam_configure(struct cafe_camera *cam) +{ + struct v4l2_format fmt; + int ret, zero = 0; + + if (cam->state != S_IDLE) + return -EINVAL; + fmt.fmt.pix = cam->pix_format; + ret = __cafe_cam_cmd(cam, VIDIOC_INT_INIT, &zero); + if (ret == 0) + ret = __cafe_cam_cmd(cam, VIDIOC_S_FMT, &fmt); + /* + * OV7670 does weird things if flip is set *before* format... + */ + ret += cafe_cam_set_flip(cam); + return ret; +} + +/* -------------------------------------------------------------------- */ +/* + * DMA buffer management. These functions need s_mutex held. + */ + +/* FIXME: this is inefficient as hell, since dma_alloc_coherent just + * does a get_free_pages() call, and we waste a good chunk of an orderN + * allocation. Should try to allocate the whole set in one chunk. + */ +static int cafe_alloc_dma_bufs(struct cafe_camera *cam, int loadtime) +{ + int i; + + cafe_set_config_needed(cam, 1); + if (loadtime) + cam->dma_buf_size = dma_buf_size; + else { + cam->dma_buf_size = cam->pix_format.sizeimage; + if (cam->pix_format.pixelformat == V4L2_PIX_FMT_RGB32) + cam->dma_buf_size /= 2; + } + if (n_dma_bufs > 3) + n_dma_bufs = 3; + + cam->nbufs = 0; + for (i = 0; i < n_dma_bufs; i++) { + cam->dma_bufs[i] = dma_alloc_coherent(&cam->pdev->dev, + cam->dma_buf_size, cam->dma_handles + i, + GFP_KERNEL); + if (cam->dma_bufs[i] == NULL) { + cam_warn(cam, "Failed to allocate DMA buffer\n"); + break; + } + /* For debug, remove eventually */ + memset(cam->dma_bufs[i], 0xcc, cam->dma_buf_size); + (cam->nbufs)++; + } + + switch (cam->nbufs) { + case 1: + dma_free_coherent(&cam->pdev->dev, cam->dma_buf_size, + cam->dma_bufs[0], cam->dma_handles[0]); + cam->nbufs = 0; + case 0: + cam_err(cam, "Insufficient DMA buffers, cannot operate\n"); + return -ENOMEM; + + case 2: + if (n_dma_bufs > 2) + cam_warn(cam, "Will limp along with only 2 buffers\n"); + break; + } + return 0; +} + +static void cafe_free_dma_bufs(struct cafe_camera *cam) +{ + int i; + + for (i = 0; i < cam->nbufs; i++) { + dma_free_coherent(&cam->pdev->dev, cam->dma_buf_size, + cam->dma_bufs[i], cam->dma_handles[i]); + cam->dma_bufs[i] = NULL; + } + cam->nbufs = 0; +} + + + + + +/* ----------------------------------------------------------------------- */ +/* + * Here starts the V4L2 interface code. + */ + +/* + * Read an image from the device. + */ +static ssize_t cafe_deliver_buffer(struct cafe_camera *cam, + char __user *buffer, size_t len, loff_t *pos) +{ + int bufno; + unsigned long flags; + + spin_lock_irqsave(&cam->dev_lock, flags); + if (cam->next_buf < 0) { + cam_err(cam, "deliver_buffer: No next buffer\n"); + spin_unlock_irqrestore(&cam->dev_lock, flags); + return -EIO; + } + bufno = cam->next_buf; + clear_bit(bufno, &cam->flags); + if (++(cam->next_buf) >= cam->nbufs) + cam->next_buf = 0; + if (! test_bit(cam->next_buf, &cam->flags)) + cam->next_buf = -1; + cam->specframes = 0; + spin_unlock_irqrestore(&cam->dev_lock, flags); + + if (len > cam->pix_format.sizeimage) + len = cam->pix_format.sizeimage; + if (copy_to_user(buffer, cam->dma_bufs[bufno], len)) + return -EFAULT; + (*pos) += len; + return len; +} + +/* + * Get everything ready, and start grabbing frames. + */ +static int cafe_read_setup(struct cafe_camera *cam, enum cafe_state state) +{ + int ret; + unsigned long flags; + + /* + * Configuration. If we still don't have DMA buffers, + * make one last, desperate attempt. + */ + if (cam->nbufs == 0) + if (cafe_alloc_dma_bufs(cam, 0)) + return -ENOMEM; + + if (cafe_needs_config(cam)) { + cafe_cam_configure(cam); + ret = cafe_ctlr_configure(cam); + if (ret) + return ret; + } + + /* + * Turn it loose. + */ + spin_lock_irqsave(&cam->dev_lock, flags); + cafe_reset_buffers(cam); + cafe_ctlr_irq_enable(cam); + cam->state = state; + cafe_ctlr_start(cam); + spin_unlock_irqrestore(&cam->dev_lock, flags); + return 0; +} + + +static ssize_t cafe_v4l_read(struct file *filp, + char __user *buffer, size_t len, loff_t *pos) +{ + struct cafe_camera *cam = filp->private_data; + int ret; + + /* + * Perhaps we're in speculative read mode and already + * have data? + */ + mutex_lock(&cam->s_mutex); + if (cam->state == S_SPECREAD) { + if (cam->next_buf >= 0) { + ret = cafe_deliver_buffer(cam, buffer, len, pos); + if (ret != 0) + goto out_unlock; + } + } else if (cam->state == S_FLAKED || cam->state == S_NOTREADY) { + ret = -EIO; + goto out_unlock; + } else if (cam->state != S_IDLE) { + ret = -EBUSY; + goto out_unlock; + } + + /* + * v4l2: multiple processes can open the device, but only + * one gets to grab data from it. + */ + if (cam->owner && cam->owner != filp) { + ret = -EBUSY; + goto out_unlock; + } + cam->owner = filp; + + /* + * Do setup if need be. + */ + if (cam->state != S_SPECREAD) { + ret = cafe_read_setup(cam, S_SINGLEREAD); + if (ret) + goto out_unlock; + } + /* + * Wait for something to happen. This should probably + * be interruptible (FIXME). + */ + wait_event_timeout(cam->iowait, cam->next_buf >= 0, HZ); + if (cam->next_buf < 0) { + cam_err(cam, "read() operation timed out\n"); + cafe_ctlr_stop_dma(cam); + ret = -EIO; + goto out_unlock; + } + /* + * Give them their data and we should be done. + */ + ret = cafe_deliver_buffer(cam, buffer, len, pos); + + out_unlock: + mutex_unlock(&cam->s_mutex); + return ret; +} + + + + + + + + +/* + * Streaming I/O support. + */ + + + +static int cafe_vidioc_streamon(struct file *filp, void *priv, + enum v4l2_buf_type type) +{ + struct cafe_camera *cam = filp->private_data; + int ret = -EINVAL; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + goto out; + mutex_lock(&cam->s_mutex); + if (cam->state != S_IDLE || cam->n_sbufs == 0) + goto out_unlock; + + cam->sequence = 0; + ret = cafe_read_setup(cam, S_STREAMING); + + out_unlock: + mutex_unlock(&cam->s_mutex); + out: + return ret; +} + + +static int cafe_vidioc_streamoff(struct file *filp, void *priv, + enum v4l2_buf_type type) +{ + struct cafe_camera *cam = filp->private_data; + int ret = -EINVAL; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + goto out; + mutex_lock(&cam->s_mutex); + if (cam->state != S_STREAMING) + goto out_unlock; + + cafe_ctlr_stop_dma(cam); + ret = 0; + + out_unlock: + mutex_unlock(&cam->s_mutex); + out: + return ret; +} + + + +static int cafe_setup_siobuf(struct cafe_camera *cam, int index) +{ + struct cafe_sio_buffer *buf = cam->sb_bufs + index; + + INIT_LIST_HEAD(&buf->list); + buf->v4lbuf.length = PAGE_ALIGN(cam->pix_format.sizeimage); + buf->buffer = vmalloc_user(buf->v4lbuf.length); + if (buf->buffer == NULL) + return -ENOMEM; + buf->mapcount = 0; + buf->cam = cam; + + buf->v4lbuf.index = index; + buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf->v4lbuf.field = V4L2_FIELD_NONE; + buf->v4lbuf.memory = V4L2_MEMORY_MMAP; + /* + * Offset: must be 32-bit even on a 64-bit system. video-buf + * just uses the length times the index, but the spec warns + * against doing just that - vma merging problems. So we + * leave a gap between each pair of buffers. + */ + buf->v4lbuf.m.offset = 2*index*buf->v4lbuf.length; + return 0; +} + +static int cafe_free_sio_buffers(struct cafe_camera *cam) +{ + int i; + + /* + * If any buffers are mapped, we cannot free them at all. + */ + for (i = 0; i < cam->n_sbufs; i++) + if (cam->sb_bufs[i].mapcount > 0) + return -EBUSY; + /* + * OK, let's do it. + */ + for (i = 0; i < cam->n_sbufs; i++) + vfree(cam->sb_bufs[i].buffer); + cam->n_sbufs = 0; + kfree(cam->sb_bufs); + cam->sb_bufs = NULL; + INIT_LIST_HEAD(&cam->sb_avail); + INIT_LIST_HEAD(&cam->sb_full); + return 0; +} + + + +static int cafe_vidioc_reqbufs(struct file *filp, void *priv, + struct v4l2_requestbuffers *req) +{ + struct cafe_camera *cam = filp->private_data; + int ret; + + /* + * Make sure it's something we can do. User pointers could be + * implemented without great pain, but that's not been done yet. + */ + if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (req->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + /* + * If they ask for zero buffers, they really want us to stop streaming + * (if it's happening) and free everything. Should we check owner? + */ + mutex_lock(&cam->s_mutex); + if (req->count == 0) { + if (cam->state == S_STREAMING) + cafe_ctlr_stop_dma(cam); + ret = cafe_free_sio_buffers (cam); + goto out; + } + /* + * Device needs to be idle and working. We *could* try to do the + * right thing in S_SPECREAD by shutting things down, but it + * probably doesn't matter. + */ + if (cam->state != S_IDLE || (cam->owner && cam->owner != filp)) { + ret = -EBUSY; + goto out; + } + cam->owner = filp; + + if (req->count < min_buffers) + req->count = min_buffers; + else if (req->count > max_buffers) + req->count = max_buffers; + if (cam->n_sbufs > 0) { + ret = cafe_free_sio_buffers(cam); + if (ret) + goto out; + } + + cam->sb_bufs = kzalloc(req->count*sizeof(struct cafe_sio_buffer), + GFP_KERNEL); + if (cam->sb_bufs == NULL) { + ret = -ENOMEM; + goto out; + } + for (cam->n_sbufs = 0; cam->n_sbufs < req->count; (cam->n_sbufs++)) { + ret = cafe_setup_siobuf(cam, cam->n_sbufs); + if (ret) + break; + } + + if (cam->n_sbufs == 0) /* no luck at all - ret already set */ + kfree(cam->sb_bufs); + else + ret = 0; + req->count = cam->n_sbufs; /* In case of partial success */ + + out: + mutex_unlock(&cam->s_mutex); + return ret; +} + + +static int cafe_vidioc_querybuf(struct file *filp, void *priv, + struct v4l2_buffer *buf) +{ + struct cafe_camera *cam = filp->private_data; + int ret = -EINVAL; + + mutex_lock(&cam->s_mutex); + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + goto out; + if (buf->index < 0 || buf->index >= cam->n_sbufs) + goto out; + *buf = cam->sb_bufs[buf->index].v4lbuf; + ret = 0; + out: + mutex_unlock(&cam->s_mutex); + return ret; +} + +static int cafe_vidioc_qbuf(struct file *filp, void *priv, + struct v4l2_buffer *buf) +{ + struct cafe_camera *cam = filp->private_data; + struct cafe_sio_buffer *sbuf; + int ret = -EINVAL; + unsigned long flags; + + mutex_lock(&cam->s_mutex); + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + goto out; + if (buf->index < 0 || buf->index >= cam->n_sbufs) + goto out; + sbuf = cam->sb_bufs + buf->index; + if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED) { + ret = 0; /* Already queued?? */ + goto out; + } + if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_DONE) { + /* Spec doesn't say anything, seems appropriate tho */ + ret = -EBUSY; + goto out; + } + sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_QUEUED; + spin_lock_irqsave(&cam->dev_lock, flags); + list_add(&sbuf->list, &cam->sb_avail); + spin_unlock_irqrestore(&cam->dev_lock, flags); + ret = 0; + out: + mutex_unlock(&cam->s_mutex); + return ret; +} + +static int cafe_vidioc_dqbuf(struct file *filp, void *priv, + struct v4l2_buffer *buf) +{ + struct cafe_camera *cam = filp->private_data; + struct cafe_sio_buffer *sbuf; + int ret = -EINVAL; + unsigned long flags; + + mutex_lock(&cam->s_mutex); + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + goto out_unlock; + if (cam->state != S_STREAMING) + goto out_unlock; + if (list_empty(&cam->sb_full) && filp->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + goto out_unlock; + } + + while (list_empty(&cam->sb_full) && cam->state == S_STREAMING) { + mutex_unlock(&cam->s_mutex); + if (wait_event_interruptible(cam->iowait, + !list_empty(&cam->sb_full))) { + ret = -ERESTARTSYS; + goto out; + } + mutex_lock(&cam->s_mutex); + } + + if (cam->state != S_STREAMING) + ret = -EINTR; + else { + spin_lock_irqsave(&cam->dev_lock, flags); + /* Should probably recheck !list_empty() here */ + sbuf = list_entry(cam->sb_full.next, + struct cafe_sio_buffer, list); + list_del_init(&sbuf->list); + spin_unlock_irqrestore(&cam->dev_lock, flags); + sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_DONE; + *buf = sbuf->v4lbuf; + ret = 0; + } + + out_unlock: + mutex_unlock(&cam->s_mutex); + out: + return ret; +} + + + +static void cafe_v4l_vm_open(struct vm_area_struct *vma) +{ + struct cafe_sio_buffer *sbuf = vma->vm_private_data; + /* + * Locking: done under mmap_sem, so we don't need to + * go back to the camera lock here. + */ + sbuf->mapcount++; +} + + +static void cafe_v4l_vm_close(struct vm_area_struct *vma) +{ + struct cafe_sio_buffer *sbuf = vma->vm_private_data; + + mutex_lock(&sbuf->cam->s_mutex); + sbuf->mapcount--; + /* Docs say we should stop I/O too... */ + if (sbuf->mapcount == 0) + sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_MAPPED; + mutex_unlock(&sbuf->cam->s_mutex); +} + +static struct vm_operations_struct cafe_v4l_vm_ops = { + .open = cafe_v4l_vm_open, + .close = cafe_v4l_vm_close +}; + + +static int cafe_v4l_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct cafe_camera *cam = filp->private_data; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + int ret = -EINVAL; + int i; + struct cafe_sio_buffer *sbuf = NULL; + + if (! (vma->vm_flags & VM_WRITE) || ! (vma->vm_flags & VM_SHARED)) + return -EINVAL; + /* + * Find the buffer they are looking for. + */ + mutex_lock(&cam->s_mutex); + for (i = 0; i < cam->n_sbufs; i++) + if (cam->sb_bufs[i].v4lbuf.m.offset == offset) { + sbuf = cam->sb_bufs + i; + break; + } + if (sbuf == NULL) + goto out; + + ret = remap_vmalloc_range(vma, sbuf->buffer, 0); + if (ret) + goto out; + vma->vm_flags |= VM_DONTEXPAND; + vma->vm_private_data = sbuf; + vma->vm_ops = &cafe_v4l_vm_ops; + sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_MAPPED; + cafe_v4l_vm_open(vma); + ret = 0; + out: + mutex_unlock(&cam->s_mutex); + return ret; +} + + + +static int cafe_v4l_open(struct inode *inode, struct file *filp) +{ + struct cafe_camera *cam; + + cam = cafe_find_dev(iminor(inode)); + if (cam == NULL) + return -ENODEV; + filp->private_data = cam; + + mutex_lock(&cam->s_mutex); + if (cam->users == 0) { + cafe_ctlr_power_up(cam); + __cafe_cam_reset(cam); + cafe_set_config_needed(cam, 1); + /* FIXME make sure this is complete */ + } + (cam->users)++; + mutex_unlock(&cam->s_mutex); + return 0; +} + + +static int cafe_v4l_release(struct inode *inode, struct file *filp) +{ + struct cafe_camera *cam = filp->private_data; + + mutex_lock(&cam->s_mutex); + (cam->users)--; + if (filp == cam->owner) { + cafe_ctlr_stop_dma(cam); + cafe_free_sio_buffers(cam); + cam->owner = NULL; + } + if (cam->users == 0) { + cafe_ctlr_power_down(cam); + if (! alloc_bufs_at_load) + cafe_free_dma_bufs(cam); + } + mutex_unlock(&cam->s_mutex); + return 0; +} + + + +static unsigned int cafe_v4l_poll(struct file *filp, + struct poll_table_struct *pt) +{ + struct cafe_camera *cam = filp->private_data; + + poll_wait(filp, &cam->iowait, pt); + if (cam->next_buf >= 0) + return POLLIN | POLLRDNORM; + return 0; +} + + + +static int cafe_vidioc_queryctrl(struct file *filp, void *priv, + struct v4l2_queryctrl *qc) +{ + struct cafe_camera *cam = filp->private_data; + int ret; + + mutex_lock(&cam->s_mutex); + ret = __cafe_cam_cmd(cam, VIDIOC_QUERYCTRL, qc); + mutex_unlock(&cam->s_mutex); + return ret; +} + + +static int cafe_vidioc_g_ctrl(struct file *filp, void *priv, + struct v4l2_control *ctrl) +{ + struct cafe_camera *cam = filp->private_data; + int ret; + + mutex_lock(&cam->s_mutex); + ret = __cafe_cam_cmd(cam, VIDIOC_G_CTRL, ctrl); + mutex_unlock(&cam->s_mutex); + return ret; +} + + +static int cafe_vidioc_s_ctrl(struct file *filp, void *priv, + struct v4l2_control *ctrl) +{ + struct cafe_camera *cam = filp->private_data; + int ret; + + mutex_lock(&cam->s_mutex); + ret = __cafe_cam_cmd(cam, VIDIOC_S_CTRL, ctrl); + mutex_unlock(&cam->s_mutex); + return ret; +} + + + + + +static int cafe_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strcpy(cap->driver, "cafe_ccic"); + strcpy(cap->card, "cafe_ccic"); + cap->version = CAFE_VERSION; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + return 0; +} + + +/* + * The default format we use until somebody says otherwise. + */ +static struct v4l2_pix_format cafe_def_pix_format = { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .pixelformat = V4L2_PIX_FMT_YUYV, + .field = V4L2_FIELD_NONE, + .bytesperline = VGA_WIDTH*2, + .sizeimage = VGA_WIDTH*VGA_HEIGHT*2, +}; + +static int cafe_vidioc_enum_fmt_cap(struct file *filp, + void *priv, struct v4l2_fmtdesc *fmt) +{ + struct cafe_camera *cam = priv; + int ret; + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + mutex_lock(&cam->s_mutex); + ret = __cafe_cam_cmd(cam, VIDIOC_ENUM_FMT, fmt); + mutex_unlock(&cam->s_mutex); + return ret; +} + + +static int cafe_vidioc_try_fmt_cap (struct file *filp, void *priv, + struct v4l2_format *fmt) +{ + struct cafe_camera *cam = priv; + int ret; + + mutex_lock(&cam->s_mutex); + ret = __cafe_cam_cmd(cam, VIDIOC_TRY_FMT, fmt); + mutex_unlock(&cam->s_mutex); + return ret; +} + +static int cafe_vidioc_s_fmt_cap(struct file *filp, void *priv, + struct v4l2_format *fmt) +{ + struct cafe_camera *cam = priv; + int ret; + + /* + * Can't do anything if the device is not idle + * Also can't if there are streaming buffers in place. + */ + if (cam->state != S_IDLE || cam->n_sbufs > 0) + return -EBUSY; + /* + * See if the formatting works in principle. + */ + ret = cafe_vidioc_try_fmt_cap(filp, priv, fmt); + if (ret) + return ret; + /* + * Now we start to change things for real, so let's do it + * under lock. + */ + mutex_lock(&cam->s_mutex); + cam->pix_format = fmt->fmt.pix; + /* + * Make sure we have appropriate DMA buffers. + */ + ret = -ENOMEM; + if (cam->nbufs > 0 && cam->dma_buf_size < cam->pix_format.sizeimage) + cafe_free_dma_bufs(cam); + if (cam->nbufs == 0) { + if (cafe_alloc_dma_bufs(cam, 0)) + goto out; + } + /* + * It looks like this might work, so let's program the sensor. + */ + ret = cafe_cam_configure(cam); + if (! ret) + ret = cafe_ctlr_configure(cam); + out: + mutex_unlock(&cam->s_mutex); + return ret; +} + +/* + * Return our stored notion of how the camera is/should be configured. + * The V4l2 spec wants us to be smarter, and actually get this from + * the camera (and not mess with it at open time). Someday. + */ +static int cafe_vidioc_g_fmt_cap(struct file *filp, void *priv, + struct v4l2_format *f) +{ + struct cafe_camera *cam = priv; + + f->fmt.pix = cam->pix_format; + return 0; +} + +/* + * We only have one input - the sensor - so minimize the nonsense here. + */ +static int cafe_vidioc_enum_input(struct file *filp, void *priv, + struct v4l2_input *input) +{ + if (input->index != 0) + return -EINVAL; + + input->type = V4L2_INPUT_TYPE_CAMERA; + input->std = V4L2_STD_ALL; /* Not sure what should go here */ + strcpy(input->name, "Camera"); + return 0; +} + +static int cafe_vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int cafe_vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +/* from vivi.c */ +static int cafe_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a) +{ + return 0; +} + +static void cafe_v4l_dev_release(struct video_device *vd) +{ + struct cafe_camera *cam = container_of(vd, struct cafe_camera, v4ldev); + + kfree(cam); +} + + +/* + * This template device holds all of those v4l2 methods; we + * clone it for specific real devices. + */ + +static struct file_operations cafe_v4l_fops = { + .owner = THIS_MODULE, + .open = cafe_v4l_open, + .release = cafe_v4l_release, + .read = cafe_v4l_read, + .poll = cafe_v4l_poll, + .mmap = cafe_v4l_mmap, + .ioctl = video_ioctl2, + .llseek = no_llseek, +}; + +static struct video_device cafe_v4l_template = { + .name = "cafe", + .type = VFL_TYPE_GRABBER, + .type2 = VID_TYPE_CAPTURE, + .minor = -1, /* Get one dynamically */ + .tvnorms = V4L2_STD_NTSC_M, + .current_norm = V4L2_STD_NTSC_M, /* make mplayer happy */ + + .fops = &cafe_v4l_fops, + .release = cafe_v4l_dev_release, + + .vidioc_querycap = cafe_vidioc_querycap, + .vidioc_enum_fmt_cap = cafe_vidioc_enum_fmt_cap, + .vidioc_try_fmt_cap = cafe_vidioc_try_fmt_cap, + .vidioc_s_fmt_cap = cafe_vidioc_s_fmt_cap, + .vidioc_g_fmt_cap = cafe_vidioc_g_fmt_cap, + .vidioc_enum_input = cafe_vidioc_enum_input, + .vidioc_g_input = cafe_vidioc_g_input, + .vidioc_s_input = cafe_vidioc_s_input, + .vidioc_s_std = cafe_vidioc_s_std, + .vidioc_reqbufs = cafe_vidioc_reqbufs, + .vidioc_querybuf = cafe_vidioc_querybuf, + .vidioc_qbuf = cafe_vidioc_qbuf, + .vidioc_dqbuf = cafe_vidioc_dqbuf, + .vidioc_streamon = cafe_vidioc_streamon, + .vidioc_streamoff = cafe_vidioc_streamoff, + .vidioc_queryctrl = cafe_vidioc_queryctrl, + .vidioc_g_ctrl = cafe_vidioc_g_ctrl, + .vidioc_s_ctrl = cafe_vidioc_s_ctrl, + /* Do cropping someday */ +}; + + + + + + + +/* ---------------------------------------------------------------------- */ +/* + * Interrupt handler stuff + */ + +/* + * Create RGB32 from RGB444 so it can be displayed before the applications + * know about the latter format. + */ +static void cafe_fake_rgb32(struct cafe_camera *cam, char *dest, char *src) +{ + int i; + u16 *ssrc = (u16 *) src; + + /* RGB444 version */ + for (i = 0; i < cam->pix_format.sizeimage; i += 4) { + // dest[0] = (*ssrc & 0xf000) >> 8; + dest[0] = (*ssrc & 0x000f) << 4; + dest[1] = (*ssrc & 0x00f0); + dest[2] = (*ssrc & 0x0f00) >> 4; + dest[3] = (*ssrc & 0xf000); /* Alpha */ + dest += 4; + ssrc++; + } +} + + +static void cafe_frame_tasklet(unsigned long data) +{ + struct cafe_camera *cam = (struct cafe_camera *) data; + int i; + unsigned long flags; + struct cafe_sio_buffer *sbuf; + + spin_lock_irqsave(&cam->dev_lock, flags); + for (i = 0; i < cam->nbufs; i++) { + int bufno = cam->next_buf; + if (bufno < 0) { /* "will never happen" */ + cam_err(cam, "No valid bufs in tasklet!\n"); + break; + } + if (++(cam->next_buf) >= cam->nbufs) + cam->next_buf = 0; + if (! test_bit(bufno, &cam->flags)) + continue; + if (list_empty(&cam->sb_avail)) + break; /* Leave it valid, hope for better later */ + clear_bit(bufno, &cam->flags); + /* + * We could perhaps drop the spinlock during this + * big copy. Something to consider. + */ + sbuf = list_entry(cam->sb_avail.next, + struct cafe_sio_buffer, list); + if (cam->pix_format.pixelformat == V4L2_PIX_FMT_RGB32) + cafe_fake_rgb32(cam, sbuf->buffer, cam->dma_bufs[bufno]); + else + memcpy(sbuf->buffer, cam->dma_bufs[bufno], + cam->pix_format.sizeimage); + sbuf->v4lbuf.bytesused = cam->pix_format.sizeimage; + sbuf->v4lbuf.sequence = cam->buf_seq[bufno]; + sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; + sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE; + list_move_tail(&sbuf->list, &cam->sb_full); + } + if (! list_empty(&cam->sb_full)) + wake_up(&cam->iowait); + spin_unlock_irqrestore(&cam->dev_lock, flags); +} + + + +static void cafe_frame_complete(struct cafe_camera *cam, int frame) +{ + /* + * Basic frame housekeeping. + */ + if (test_bit(frame, &cam->flags) && printk_ratelimit()) + cam_err(cam, "Frame overrun on %d, frames lost\n", frame); + set_bit(frame, &cam->flags); + clear_bit(CF_DMA_ACTIVE, &cam->flags); + if (cam->next_buf < 0) + cam->next_buf = frame; + cam->buf_seq[frame] = ++(cam->sequence); + + switch (cam->state) { + /* + * If in single read mode, try going speculative. + */ + case S_SINGLEREAD: + cam->state = S_SPECREAD; + cam->specframes = 0; + wake_up(&cam->iowait); + break; + + /* + * If we are already doing speculative reads, and nobody is + * reading them, just stop. + */ + case S_SPECREAD: + if (++(cam->specframes) >= cam->nbufs) { + cafe_ctlr_stop(cam); + cafe_ctlr_irq_disable(cam); + cam->state = S_IDLE; + } + wake_up(&cam->iowait); + break; + /* + * For the streaming case, we defer the real work to the + * camera tasklet. + * + * FIXME: if the application is not consuming the buffers, + * we should eventually put things on hold and restart in + * vidioc_dqbuf(). + */ + case S_STREAMING: + tasklet_schedule(&cam->s_tasklet); + break; + + default: + cam_err(cam, "Frame interrupt in non-operational state\n"); + break; + } +} + + + + +static void cafe_frame_irq(struct cafe_camera *cam, unsigned int irqs) +{ + unsigned int frame; + + cafe_reg_write(cam, REG_IRQSTAT, FRAMEIRQS); /* Clear'em all */ + /* + * Handle any frame completions. There really should + * not be more than one of these, or we have fallen + * far behind. + */ + for (frame = 0; frame < cam->nbufs; frame++) + if (irqs & (IRQ_EOF0 << frame)) + cafe_frame_complete(cam, frame); + /* + * If a frame starts, note that we have DMA active. This + * code assumes that we won't get multiple frame interrupts + * at once; may want to rethink that. + */ + if (irqs & (IRQ_SOF0 | IRQ_SOF1 | IRQ_SOF2)) + set_bit(CF_DMA_ACTIVE, &cam->flags); +} + + + +static irqreturn_t cafe_irq(int irq, void *data) +{ + struct cafe_camera *cam = data; + unsigned int irqs; + + spin_lock(&cam->dev_lock); + irqs = cafe_reg_read(cam, REG_IRQSTAT); + if ((irqs & ALLIRQS) == 0) { + spin_unlock(&cam->dev_lock); + return IRQ_NONE; + } + if (irqs & FRAMEIRQS) + cafe_frame_irq(cam, irqs); + if (irqs & TWSIIRQS) { + cafe_reg_write(cam, REG_IRQSTAT, TWSIIRQS); + wake_up(&cam->smbus_wait); + } + spin_unlock(&cam->dev_lock); + return IRQ_HANDLED; +} + + +/* -------------------------------------------------------------------------- */ +#ifdef CONFIG_VIDEO_ADV_DEBUG +/* + * Debugfs stuff. + */ + +static char cafe_debug_buf[1024]; +static struct dentry *cafe_dfs_root; + +static void cafe_dfs_setup(void) +{ + cafe_dfs_root = debugfs_create_dir("cafe_ccic", NULL); + if (IS_ERR(cafe_dfs_root)) { + cafe_dfs_root = NULL; /* Never mind */ + printk(KERN_NOTICE "cafe_ccic unable to set up debugfs\n"); + } +} + +static void cafe_dfs_shutdown(void) +{ + if (cafe_dfs_root) + debugfs_remove(cafe_dfs_root); +} + +static int cafe_dfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t cafe_dfs_read_regs(struct file *file, + char __user *buf, size_t count, loff_t *ppos) +{ + struct cafe_camera *cam = file->private_data; + char *s = cafe_debug_buf; + int offset; + + for (offset = 0; offset < 0x44; offset += 4) + s += sprintf(s, "%02x: %08x\n", offset, + cafe_reg_read(cam, offset)); + for (offset = 0x88; offset <= 0x90; offset += 4) + s += sprintf(s, "%02x: %08x\n", offset, + cafe_reg_read(cam, offset)); + for (offset = 0xb4; offset <= 0xbc; offset += 4) + s += sprintf(s, "%02x: %08x\n", offset, + cafe_reg_read(cam, offset)); + for (offset = 0x3000; offset <= 0x300c; offset += 4) + s += sprintf(s, "%04x: %08x\n", offset, + cafe_reg_read(cam, offset)); + return simple_read_from_buffer(buf, count, ppos, cafe_debug_buf, + s - cafe_debug_buf); +} + +static struct file_operations cafe_dfs_reg_ops = { + .owner = THIS_MODULE, + .read = cafe_dfs_read_regs, + .open = cafe_dfs_open +}; + +static ssize_t cafe_dfs_read_cam(struct file *file, + char __user *buf, size_t count, loff_t *ppos) +{ + struct cafe_camera *cam = file->private_data; + char *s = cafe_debug_buf; + int offset; + + if (! cam->sensor) + return -EINVAL; + for (offset = 0x0; offset < 0x8a; offset++) + { + u8 v; + + cafe_smbus_read_data(cam, cam->sensor->addr, offset, &v); + s += sprintf(s, "%02x: %02x\n", offset, v); + } + return simple_read_from_buffer(buf, count, ppos, cafe_debug_buf, + s - cafe_debug_buf); +} + +static struct file_operations cafe_dfs_cam_ops = { + .owner = THIS_MODULE, + .read = cafe_dfs_read_cam, + .open = cafe_dfs_open +}; + + + +static void cafe_dfs_cam_setup(struct cafe_camera *cam) +{ + char fname[40]; + + if (!cafe_dfs_root) + return; + sprintf(fname, "regs-%d", cam->v4ldev.minor); + cam->dfs_regs = debugfs_create_file(fname, 0444, cafe_dfs_root, + cam, &cafe_dfs_reg_ops); + sprintf(fname, "cam-%d", cam->v4ldev.minor); + cam->dfs_cam_regs = debugfs_create_file(fname, 0444, cafe_dfs_root, + cam, &cafe_dfs_cam_ops); +} + + +static void cafe_dfs_cam_shutdown(struct cafe_camera *cam) +{ + if (! IS_ERR(cam->dfs_regs)) + debugfs_remove(cam->dfs_regs); + if (! IS_ERR(cam->dfs_cam_regs)) + debugfs_remove(cam->dfs_cam_regs); +} + +#else + +#define cafe_dfs_setup() +#define cafe_dfs_shutdown() +#define cafe_dfs_cam_setup(cam) +#define cafe_dfs_cam_shutdown(cam) +#endif /* CONFIG_VIDEO_ADV_DEBUG */ + + + + +/* ------------------------------------------------------------------------*/ +/* + * PCI interface stuff. + */ + +static int cafe_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int ret; + u16 classword; + struct cafe_camera *cam; + /* + * Make sure we have a camera here - we'll get calls for + * the other cafe devices as well. + */ + pci_read_config_word(pdev, PCI_CLASS_DEVICE, &classword); + if (classword != PCI_CLASS_MULTIMEDIA_VIDEO) + return -ENODEV; + /* + * Start putting together one of our big camera structures. + */ + ret = -ENOMEM; + cam = kzalloc(sizeof(struct cafe_camera), GFP_KERNEL); + if (cam == NULL) + goto out; + mutex_init(&cam->s_mutex); + mutex_lock(&cam->s_mutex); + spin_lock_init(&cam->dev_lock); + cam->state = S_NOTREADY; + cafe_set_config_needed(cam, 1); + init_waitqueue_head(&cam->smbus_wait); + init_waitqueue_head(&cam->iowait); + cam->pdev = pdev; + cam->pix_format = cafe_def_pix_format; + INIT_LIST_HEAD(&cam->dev_list); + INIT_LIST_HEAD(&cam->sb_avail); + INIT_LIST_HEAD(&cam->sb_full); + tasklet_init(&cam->s_tasklet, cafe_frame_tasklet, (unsigned long) cam); + /* + * Get set up on the PCI bus. + */ + ret = pci_enable_device(pdev); + if (ret) + goto out_free; + pci_set_master(pdev); + + ret = -EIO; + cam->regs = pci_iomap(pdev, 0, 0); + if (! cam->regs) { + printk(KERN_ERR "Unable to ioremap cafe-ccic regs\n"); + goto out_free; + } + ret = request_irq(pdev->irq, cafe_irq, IRQF_SHARED, "cafe-ccic", cam); + if (ret) + goto out_iounmap; + cafe_ctlr_init(cam); + cafe_ctlr_power_up(cam); + /* + * Set up I2C/SMBUS communications + */ + mutex_unlock(&cam->s_mutex); /* attach can deadlock */ + ret = cafe_smbus_setup(cam); + if (ret) + goto out_freeirq; + /* + * Get the v4l2 setup done. + */ + mutex_lock(&cam->s_mutex); + cam->v4ldev = cafe_v4l_template; + cam->v4ldev.debug = 0; +// cam->v4ldev.debug = V4L2_DEBUG_IOCTL_ARG; + ret = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER, -1); + if (ret) + goto out_smbus; + /* + * If so requested, try to get our DMA buffers now. + */ + if (alloc_bufs_at_load) { + if (cafe_alloc_dma_bufs(cam, 1)) + cam_warn(cam, "Unable to alloc DMA buffers at load" + " will try again later."); + } + + cafe_dfs_cam_setup(cam); + mutex_unlock(&cam->s_mutex); + cafe_add_dev(cam); + return 0; + + out_smbus: + cafe_smbus_shutdown(cam); + out_freeirq: + cafe_ctlr_power_down(cam); + free_irq(pdev->irq, cam); + out_iounmap: + pci_iounmap(pdev, cam->regs); + out_free: + kfree(cam); + out: + return ret; +} + + +/* + * Shut down an initialized device + */ +static void cafe_shutdown(struct cafe_camera *cam) +{ +/* FIXME: Make sure we take care of everything here */ + cafe_dfs_cam_shutdown(cam); + if (cam->n_sbufs > 0) + /* What if they are still mapped? Shouldn't be, but... */ + cafe_free_sio_buffers(cam); + cafe_remove_dev(cam); + cafe_ctlr_stop_dma(cam); + cafe_ctlr_power_down(cam); + cafe_smbus_shutdown(cam); + cafe_free_dma_bufs(cam); + free_irq(cam->pdev->irq, cam); + pci_iounmap(cam->pdev, cam->regs); + video_unregister_device(&cam->v4ldev); + /* kfree(cam); done in v4l_release () */ +} + + +static void cafe_pci_remove(struct pci_dev *pdev) +{ + struct cafe_camera *cam = cafe_find_by_pdev(pdev); + + if (cam == NULL) { + cam_warn(cam, "pci_remove on unknown pdev %p\n", pdev); + return; + } + mutex_lock(&cam->s_mutex); + if (cam->users > 0) + cam_warn(cam, "Removing a device with users!\n"); + cafe_shutdown(cam); +/* No unlock - it no longer exists */ +} + + + + +static struct pci_device_id cafe_ids[] = { + { PCI_DEVICE(0x1148, 0x4340) }, /* Temporary ID on devel board */ + { PCI_DEVICE(0x11ab, 0x4100) }, /* Eventual real ID */ + { PCI_DEVICE(0x11ab, 0x4102) }, /* Really eventual real ID */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, cafe_ids); + +static struct pci_driver cafe_pci_driver = { + .name = "cafe1000-ccic", + .id_table = cafe_ids, + .probe = cafe_pci_probe, + .remove = cafe_pci_remove, +}; + + + + +static int __init cafe_init(void) +{ + int ret; + + printk(KERN_NOTICE "Marvell M88ALP01 'CAFE' Camera Controller version %d\n", + CAFE_VERSION); + cafe_dfs_setup(); + ret = pci_register_driver(&cafe_pci_driver); + if (ret) { + printk(KERN_ERR "Unable to register cafe_ccic driver\n"); + goto out; + } + request_module("ov7670"); /* FIXME want something more general */ + ret = 0; + + out: + return ret; +} + + +static void __exit cafe_exit(void) +{ + pci_unregister_driver(&cafe_pci_driver); + cafe_dfs_shutdown(); +} + +module_init(cafe_init); +module_exit(cafe_exit); diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig index 0f9d969..b2a66ba 100644 --- a/drivers/media/video/cx88/Kconfig +++ b/drivers/media/video/cx88/Kconfig @@ -53,6 +53,7 @@ config VIDEO_CX88_DVB select DVB_OR51132 if !DVB_FE_CUSTOMISE select DVB_CX22702 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE select DVB_NXT200X if !DVB_FE_CUSTOMISE select DVB_CX24123 if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index 4673832..0cf0360 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -50,7 +50,6 @@ MODULE_PARM_DESC(debug,"enable debug mes #define dprintk(level,fmt, arg...) if (debug >= level) \ printk(KERN_DEBUG "%s/2-bb: " fmt, dev->core->name , ## arg) -static LIST_HEAD(cx8802_devlist); /* ------------------------------------------------------------------ */ @@ -882,7 +881,7 @@ static int mpeg_do_ioctl(struct inode *i BLACKBIRD_MPEG_CAPTURE, BLACKBIRD_RAW_BITS_NONE); - cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, mpeg_do_ioctl); + cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, cx88_ioctl_hook); blackbird_initialize_codec(dev); cx88_set_scale(dev->core, dev->width, dev->height, @@ -914,11 +913,15 @@ static int mpeg_do_ioctl(struct inode *i } default: - return cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, mpeg_do_ioctl); + return cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, cx88_ioctl_hook); } return 0; } +int (*cx88_ioctl_hook)(struct inode *inode, struct file *file, + unsigned int cmd, void *arg); +unsigned int (*cx88_ioctl_translator)(unsigned int cmd); + static unsigned int mpeg_translate_ioctl(unsigned int cmd) { return cmd; @@ -927,33 +930,49 @@ static unsigned int mpeg_translate_ioctl static int mpeg_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - cmd = mpeg_translate_ioctl( cmd ); - return video_usercopy(inode, file, cmd, arg, mpeg_do_ioctl); + cmd = cx88_ioctl_translator( cmd ); + return video_usercopy(inode, file, cmd, arg, cx88_ioctl_hook); } static int mpeg_open(struct inode *inode, struct file *file) { int minor = iminor(inode); - struct cx8802_dev *h,*dev = NULL; + struct cx8802_dev *dev = NULL; struct cx8802_fh *fh; - struct list_head *list; + struct cx8802_driver *drv = NULL; + int err; - list_for_each(list,&cx8802_devlist) { - h = list_entry(list, struct cx8802_dev, devlist); - if (h->mpeg_dev->minor == minor) - dev = h; - } - if (NULL == dev) + dev = cx8802_get_device(inode); + + dprintk( 1, "%s\n", __FUNCTION__); + + if (dev == NULL) return -ENODEV; - if (blackbird_initialize_codec(dev) < 0) + /* Make sure we can acquire the hardware */ + drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD); + if (drv) { + err = drv->request_acquire(drv); + if(err != 0) { + dprintk(1,"%s: Unable to acquire hardware, %d\n", __FUNCTION__, err); + return err; + } + } + + if (blackbird_initialize_codec(dev) < 0) { + if (drv) + drv->request_release(drv); return -EINVAL; + } dprintk(1,"open minor=%d\n",minor); /* allocate + initialize per filehandle data */ fh = kzalloc(sizeof(*fh),GFP_KERNEL); - if (NULL == fh) + if (NULL == fh) { + if (drv) + drv->request_release(drv); return -ENOMEM; + } file->private_data = fh; fh->dev = dev; @@ -974,6 +993,8 @@ static int mpeg_open(struct inode *inode static int mpeg_release(struct inode *inode, struct file *file) { struct cx8802_fh *fh = file->private_data; + struct cx8802_dev *dev = NULL; + struct cx8802_driver *drv = NULL; /* blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, BLACKBIRD_END_NOW, 0, 0x13); */ blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, @@ -992,6 +1013,16 @@ static int mpeg_release(struct inode *in videobuf_mmap_free(&fh->mpegq); file->private_data = NULL; kfree(fh); + + /* Make sure we release the hardware */ + dev = cx8802_get_device(inode); + if (dev == NULL) + return -ENODEV; + + drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD); + if (drv) + drv->request_release(drv); + return 0; } @@ -1043,6 +1074,44 @@ static struct video_device cx8802_mpeg_t /* ------------------------------------------------------------------ */ +/* The CX8802 MPEG API will call this when we can use the hardware */ +static int cx8802_blackbird_advise_acquire(struct cx8802_driver *drv) +{ + struct cx88_core *core = drv->core; + int err = 0; + + switch (core->board) { + case CX88_BOARD_HAUPPAUGE_HVR1300: + /* By default, core setup will leave the cx22702 out of reset, on the bus. + * We left the hardware on power up with the cx22702 active. + * We're being given access to re-arrange the GPIOs. + * Take the bus off the cx22702 and put the cx23416 on it. + */ + cx_clear(MO_GP0_IO, 0x00000080); /* cx22702 in reset */ + cx_set(MO_GP0_IO, 0x00000004); /* Disable the cx22702 */ + break; + default: + err = -ENODEV; + } + return err; +} + +/* The CX8802 MPEG API will call this when we need to release the hardware */ +static int cx8802_blackbird_advise_release(struct cx8802_driver *drv) +{ + struct cx88_core *core = drv->core; + int err = 0; + + switch (core->board) { + case CX88_BOARD_HAUPPAUGE_HVR1300: + /* Exit leaving the cx23416 on the bus */ + break; + default: + err = -ENODEV; + } + return err; +} + static void blackbird_unregister_video(struct cx8802_dev *dev) { if (dev->mpeg_dev) { @@ -1073,28 +1142,23 @@ static int blackbird_register_video(stru /* ----------------------------------------------------------- */ -static int __devinit blackbird_probe(struct pci_dev *pci_dev, - const struct pci_device_id *pci_id) +static int cx8802_blackbird_probe(struct cx8802_driver *drv) { - struct cx8802_dev *dev; - struct cx88_core *core; + struct cx88_core *core = drv->core; + struct cx8802_dev *dev = core->dvbdev; int err; - /* general setup */ - core = cx88_core_get(pci_dev); - if (NULL == core) - return -EINVAL; + dprintk( 1, "%s\n", __FUNCTION__); + dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n", + core->board, + core->name, + core->pci_bus, + core->pci_slot); err = -ENODEV; if (!(cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD)) goto fail_core; - err = -ENOMEM; - dev = kzalloc(sizeof(*dev),GFP_KERNEL); - if (NULL == dev) - goto fail_core; - dev->pci = pci_dev; - dev->core = core; dev->width = 720; dev->height = 576; cx2341x_fill_defaults(&dev->params); @@ -1106,64 +1170,36 @@ static int __devinit blackbird_probe(str dev->height = 576; } - err = cx8802_init_common(dev); - if (0 != err) - goto fail_free; - /* blackbird stuff */ printk("%s/2: cx23416 based mpeg encoder (blackbird reference design)\n", core->name); host_setup(dev->core); - list_add_tail(&dev->devlist,&cx8802_devlist); blackbird_register_video(dev); /* initial device configuration: needed ? */ return 0; - fail_free: - kfree(dev); fail_core: - cx88_core_put(core,pci_dev); return err; } -static void __devexit blackbird_remove(struct pci_dev *pci_dev) +static int cx8802_blackbird_remove(struct cx8802_driver *drv) { - struct cx8802_dev *dev = pci_get_drvdata(pci_dev); - /* blackbird */ - blackbird_unregister_video(dev); - list_del(&dev->devlist); + blackbird_unregister_video(drv->core->dvbdev); - /* common */ - cx8802_fini_common(dev); - cx88_core_put(dev->core,dev->pci); - kfree(dev); + return 0; } -static struct pci_device_id cx8802_pci_tbl[] = { - { - .vendor = 0x14f1, - .device = 0x8802, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - },{ - /* --- end of list --- */ - } -}; -MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl); - -static struct pci_driver blackbird_pci_driver = { - .name = "cx88-blackbird", - .id_table = cx8802_pci_tbl, - .probe = blackbird_probe, - .remove = __devexit_p(blackbird_remove), -#ifdef CONFIG_PM - .suspend = cx8802_suspend_common, - .resume = cx8802_resume_common, -#endif +static struct cx8802_driver cx8802_blackbird_driver = { + .type_id = CX88_MPEG_BLACKBIRD, + .hw_access = CX8802_DRVCTL_SHARED, + .probe = cx8802_blackbird_probe, + .remove = cx8802_blackbird_remove, + .advise_acquire = cx8802_blackbird_advise_acquire, + .advise_release = cx8802_blackbird_advise_release, }; static int blackbird_init(void) @@ -1176,17 +1212,22 @@ #ifdef SNAPSHOT printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n", SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100); #endif - return pci_register_driver(&blackbird_pci_driver); + cx88_ioctl_hook = mpeg_do_ioctl; + cx88_ioctl_translator = mpeg_translate_ioctl; + return cx8802_register_driver(&cx8802_blackbird_driver); } static void blackbird_fini(void) { - pci_unregister_driver(&blackbird_pci_driver); + cx8802_unregister_driver(&cx8802_blackbird_driver); } module_init(blackbird_init); module_exit(blackbird_fini); +EXPORT_SYMBOL(cx88_ioctl_hook); +EXPORT_SYMBOL(cx88_ioctl_translator); + /* ----------------------------------------------------------- */ /* * Local variables: diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index f764a57..c791708 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -281,18 +281,22 @@ struct cx88_board cx88_boards[] = { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0000bde2, + .extadc = 1, },{ .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x0000bde6, + .extadc = 1, },{ .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x0000bde6, + .extadc = 1, }}, .radio = { .type = CX88_RADIO, .gpio0 = 0x0000bd62, + .extadc = 1, }, .mpeg = CX88_MPEG_BLACKBIRD, }, @@ -353,6 +357,7 @@ struct cx88_board cx88_boards[] = { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x0000fde6, // 0x0000fda6 L,R RCA audio in? + .extadc = 1, }}, .radio = { .type = CX88_RADIO, @@ -523,6 +528,7 @@ struct cx88_board cx88_boards[] = { .input = {{ .type = CX88_VMUX_TELEVISION, .vmux = 0, + .extadc = 1, }}, .mpeg = CX88_MPEG_BLACKBIRD, }, @@ -646,18 +652,22 @@ struct cx88_board cx88_boards[] = { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x00009d80, + .extadc = 1, },{ .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x00009d76, + .extadc = 1, },{ .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x00009d76, + .extadc = 1, }}, .radio = { .type = CX88_RADIO, .gpio0 = 0x00009d00, + .extadc = 1, }, .mpeg = CX88_MPEG_BLACKBIRD, }, @@ -786,25 +796,29 @@ struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .mpeg = CX88_MPEG_BLACKBIRD, .input = {{ .type = CX88_VMUX_COMPOSITE1, .vmux = 0, .gpio0 = 0x0000cd73, + .extadc = 1, },{ .type = CX88_VMUX_SVIDEO, .vmux = 1, .gpio0 = 0x0000cd73, + .extadc = 1, },{ .type = CX88_VMUX_TELEVISION, .vmux = 3, .gpio0 = 0x0000cdb3, + .extadc = 1, }}, .radio = { .type = CX88_RADIO, .vmux = 2, .gpio0 = 0x0000cdf3, + .extadc = 1, }, + .mpeg = CX88_MPEG_BLACKBIRD, }, [CX88_BOARD_KWORLD_VSTREAM_EXPERT_DVD] = { /* Alexander Wold */ @@ -1050,7 +1064,6 @@ struct cx88_board cx88_boards[] = { .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT] = { - /* FIXME: Audio not working for s-video / composite inputs. */ .name = "KWorld HardwareMpegTV XPert", .tuner_type = TUNER_PHILIPS_TDA8290, .radio_type = UNSET, @@ -1065,10 +1078,12 @@ struct cx88_board cx88_boards[] = { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x3de6, + .extadc = 1, },{ .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x3de6, + .extadc = 1, }}, .radio = { .type = CX88_RADIO, @@ -1252,35 +1267,35 @@ struct cx88_board cx88_boards[] = { .gpio0 = 0x070b, }}, }, - [CX88_BOARD_TE_DTV_250_OEM_SWANN] = { - .name = "Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM", - .tuner_type = TUNER_LG_PAL_NEW_TAPC, - .radio_type = UNSET, - .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, - .input = {{ - .type = CX88_VMUX_TELEVISION, - .vmux = 0, - .gpio0 = 0x003fffff, - .gpio1 = 0x00e00000, - .gpio2 = 0x003fffff, - .gpio3 = 0x02000000, - },{ - .type = CX88_VMUX_COMPOSITE1, - .vmux = 1, - .gpio0 = 0x003fffff, - .gpio1 = 0x00e00000, - .gpio2 = 0x003fffff, - .gpio3 = 0x02000000, - },{ - .type = CX88_VMUX_SVIDEO, - .vmux = 2, - .gpio0 = 0x003fffff, - .gpio1 = 0x00e00000, - .gpio2 = 0x003fffff, - .gpio3 = 0x02000000, - }}, - }, + [CX88_BOARD_TE_DTV_250_OEM_SWANN] = { + .name = "Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM", + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x003fffff, + .gpio1 = 0x00e00000, + .gpio2 = 0x003fffff, + .gpio3 = 0x02000000, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x003fffff, + .gpio1 = 0x00e00000, + .gpio2 = 0x003fffff, + .gpio3 = 0x02000000, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x003fffff, + .gpio1 = 0x00e00000, + .gpio2 = 0x003fffff, + .gpio3 = 0x02000000, + }}, + }, [CX88_BOARD_HAUPPAUGE_HVR1300] = { .name = "Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder", .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, @@ -1293,17 +1308,20 @@ struct cx88_board cx88_boards[] = { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0xe780, + .extadc = 1, },{ .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0xe780, + .extadc = 1, },{ .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0xe780, + .extadc = 1, }}, /* fixme: Add radio support */ - .mpeg = CX88_MPEG_DVB, + .mpeg = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD, }, }; const unsigned int cx88_bcount = ARRAY_SIZE(cx88_boards); @@ -1513,6 +1531,10 @@ struct cx88_subid cx88_subids[] = { },{ .subvendor = 0x17de, .subdevice = 0x0840, + .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT, + },{ + .subvendor = 0x1421, + .subdevice = 0x0305, .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT, },{ .subvendor = 0x18ac, diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 0ef13e7..0266a36 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -42,7 +42,7 @@ #include "zl10353.h" #include "cx22702.h" #include "or51132.h" #include "lgdt330x.h" -#include "lg_h06xf.h" +#include "lgh06xf.h" #include "nxt200x.h" #include "cx24123.h" #include "isl6421.h" @@ -57,7 +57,7 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug,"enable debug messages [dvb]"); #define dprintk(level,fmt, arg...) if (debug >= level) \ - printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->core->name , ## arg) + printk(KERN_DEBUG "%s/2-dvb: " fmt, core->name, ## arg) /* ------------------------------------------------------------------ */ @@ -315,20 +315,36 @@ static struct cx22702_config hauppauge_n .demod_address = 0x43, .output_mode = CX22702_SERIAL_OUTPUT, }; - static struct cx22702_config hauppauge_hvr1100_config = { .demod_address = 0x63, .output_mode = CX22702_SERIAL_OUTPUT, }; - -static struct cx22702_config hauppauge_hvr1300_config = { +static struct cx22702_config hauppauge_hvr3000_config = { .demod_address = 0x63, .output_mode = CX22702_SERIAL_OUTPUT, }; -static struct cx22702_config hauppauge_hvr3000_config = { +static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, + int acquire) +{ + struct cx8802_dev *dev= fe->dvb->priv; + struct cx8802_driver *drv = NULL; + int ret = 0; + + drv = cx8802_get_driver(dev, CX88_MPEG_DVB); + if (drv) { + if(acquire) + ret = drv->request_acquire(drv); + else + ret = drv->request_release(drv); + } + + return ret; +} + +static struct cx22702_config hauppauge_hvr1300_config = { .demod_address = 0x63, - .output_mode = CX22702_SERIAL_OUTPUT, + .output_mode = CX22702_SERIAL_OUTPUT, }; static int or51132_set_ts_param(struct dvb_frontend* fe, @@ -376,18 +392,6 @@ static int lgdt3302_tuner_set_params(str return 0; } -static int lgdt3303_tuner_set_params(struct dvb_frontend* fe, - struct dvb_frontend_parameters* params) -{ - struct cx8802_dev *dev= fe->dvb->priv; - struct cx88_core *core = dev->core; - - /* Put the analog decoder in standby to keep it quiet */ - cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); - - return lg_h06xf_pll_set(fe, &core->i2c_adap, params); -} - static int lgdt330x_pll_rf_set(struct dvb_frontend* fe, int index) { struct cx8802_dev *dev= fe->dvb->priv; @@ -555,26 +559,6 @@ static int dvb_register(struct cx8802_de &dvb_pll_fmd1216me); } break; - case CX88_BOARD_HAUPPAUGE_HVR1300: - dev->dvb.frontend = dvb_attach(cx22702_attach, - &hauppauge_hvr1300_config, - &dev->core->i2c_adap); - if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - &dvb_pll_fmd1216me); - } - break; - case CX88_BOARD_HAUPPAUGE_HVR3000: - dev->dvb.frontend = dvb_attach(cx22702_attach, - &hauppauge_hvr3000_config, - &dev->core->i2c_adap); - if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - &dvb_pll_fmd1216me); - } - break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: dev->dvb.frontend = dvb_attach(mt352_attach, &dvico_fusionhdtv, @@ -723,7 +707,8 @@ #endif &fusionhdtv_5_gold, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3303_tuner_set_params; + dvb_attach(lgh06xf_attach, dev->dvb.frontend, + &dev->core->i2c_adap); } } break; @@ -741,7 +726,8 @@ #endif &pchdtv_hd5500, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3303_tuner_set_params; + dvb_attach(lgh06xf_attach, dev->dvb.frontend, + &dev->core->i2c_adap); } } break; @@ -782,6 +768,26 @@ #endif dev->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage; } break; + case CX88_BOARD_HAUPPAUGE_HVR1300: + dev->dvb.frontend = dvb_attach(cx22702_attach, + &hauppauge_hvr1300_config, + &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + &dev->core->i2c_adap, + &dvb_pll_fmd1216me); + } + break; + case CX88_BOARD_HAUPPAUGE_HVR3000: + dev->dvb.frontend = dvb_attach(cx22702_attach, + &hauppauge_hvr3000_config, + &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + &dev->core->i2c_adap, + &dvb_pll_fmd1216me); + } + break; default: printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n", dev->core->name); @@ -796,6 +802,8 @@ #endif dev->dvb.frontend->ops.info.frequency_min = dev->core->pll_desc->min; dev->dvb.frontend->ops.info.frequency_max = dev->core->pll_desc->max; } + /* Ensure all frontends negotiate bus access */ + dev->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl; /* Put the analog decoder in standby to keep it quiet */ cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); @@ -806,37 +814,67 @@ #endif /* ----------------------------------------------------------- */ -static int __devinit dvb_probe(struct pci_dev *pci_dev, - const struct pci_device_id *pci_id) +/* CX8802 MPEG -> mini driver - We have been given the hardware */ +static int cx8802_dvb_advise_acquire(struct cx8802_driver *drv) +{ + struct cx88_core *core = drv->core; + int err = 0; + dprintk( 1, "%s\n", __FUNCTION__); + + switch (core->board) { + case CX88_BOARD_HAUPPAUGE_HVR1300: + /* We arrive here with either the cx23416 or the cx22702 + * on the bus. Take the bus from the cx23416 and enable the + * cx22702 demod + */ + cx_set(MO_GP0_IO, 0x00000080); /* cx22702 out of reset and enable */ + cx_clear(MO_GP0_IO, 0x00000004); + udelay(1000); + break; + default: + err = -ENODEV; + } + return err; +} + +/* CX8802 MPEG -> mini driver - We no longer have the hardware */ +static int cx8802_dvb_advise_release(struct cx8802_driver *drv) +{ + struct cx88_core *core = drv->core; + int err = 0; + dprintk( 1, "%s\n", __FUNCTION__); + + switch (core->board) { + case CX88_BOARD_HAUPPAUGE_HVR1300: + /* Do Nothing, leave the cx22702 on the bus. */ + break; + default: + err = -ENODEV; + } + return err; +} + +static int cx8802_dvb_probe(struct cx8802_driver *drv) { - struct cx8802_dev *dev; - struct cx88_core *core; + struct cx88_core *core = drv->core; + struct cx8802_dev *dev = drv->core->dvbdev; int err; - /* general setup */ - core = cx88_core_get(pci_dev); - if (NULL == core) - return -EINVAL; + dprintk( 1, "%s\n", __FUNCTION__); + dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n", + core->board, + core->name, + core->pci_bus, + core->pci_slot); err = -ENODEV; if (!(cx88_boards[core->board].mpeg & CX88_MPEG_DVB)) goto fail_core; - err = -ENOMEM; - dev = kzalloc(sizeof(*dev),GFP_KERNEL); - if (NULL == dev) - goto fail_core; - dev->pci = pci_dev; - dev->core = core; - - err = cx8802_init_common(dev); - if (0 != err) - goto fail_free; - #ifdef HAVE_VP3054_I2C err = vp3054_i2c_probe(dev); if (0 != err) - goto fail_free; + goto fail_core; #endif /* dvb stuff */ @@ -848,28 +886,16 @@ #endif sizeof(struct cx88_buffer), dev); err = dvb_register(dev); - if (0 != err) - goto fail_fini; - - /* Maintain a reference to cx88-video can query the 8802 device. */ - core->dvbdev = dev; - return 0; + if (err != 0) + printk("%s dvb_register failed err = %d\n", __FUNCTION__, err); - fail_fini: - cx8802_fini_common(dev); - fail_free: - kfree(dev); fail_core: - cx88_core_put(core,pci_dev); return err; } -static void __devexit dvb_remove(struct pci_dev *pci_dev) +static int cx8802_dvb_remove(struct cx8802_driver *drv) { - struct cx8802_dev *dev = pci_get_drvdata(pci_dev); - - /* Destroy any 8802 reference. */ - dev->core->dvbdev = NULL; + struct cx8802_dev *dev = drv->core->dvbdev; /* dvb */ videobuf_dvb_unregister(&dev->dvb); @@ -878,33 +904,16 @@ #ifdef HAVE_VP3054_I2C vp3054_i2c_remove(dev); #endif - /* common */ - cx8802_fini_common(dev); - cx88_core_put(dev->core,dev->pci); - kfree(dev); + return 0; } -static struct pci_device_id cx8802_pci_tbl[] = { - { - .vendor = 0x14f1, - .device = 0x8802, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - },{ - /* --- end of list --- */ - } -}; -MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl); - -static struct pci_driver dvb_pci_driver = { - .name = "cx88-dvb", - .id_table = cx8802_pci_tbl, - .probe = dvb_probe, - .remove = __devexit_p(dvb_remove), -#ifdef CONFIG_PM - .suspend = cx8802_suspend_common, - .resume = cx8802_resume_common, -#endif +static struct cx8802_driver cx8802_dvb_driver = { + .type_id = CX88_MPEG_DVB, + .hw_access = CX8802_DRVCTL_SHARED, + .probe = cx8802_dvb_probe, + .remove = cx8802_dvb_remove, + .advise_acquire = cx8802_dvb_advise_acquire, + .advise_release = cx8802_dvb_advise_release, }; static int dvb_init(void) @@ -917,12 +926,12 @@ #ifdef SNAPSHOT printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n", SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100); #endif - return pci_register_driver(&dvb_pci_driver); + return cx8802_register_driver(&cx8802_dvb_driver); } static void dvb_fini(void) { - pci_unregister_driver(&dvb_pci_driver); + cx8802_unregister_driver(&cx8802_dvb_driver); } module_init(dvb_init); diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index 57e1c02..0592ad4 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -155,6 +155,35 @@ static void cx88_ir_work(void *data) mod_timer(&ir->timer, timeout); } +static void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir) +{ + if (ir->polling) { + INIT_WORK(&ir->work, cx88_ir_work, ir); + init_timer(&ir->timer); + ir->timer.function = ir_timer; + ir->timer.data = (unsigned long)ir; + schedule_work(&ir->work); + } + if (ir->sampling) { + core->pci_irqmask |= (1 << 18); /* IR_SMP_INT */ + cx_write(MO_DDS_IO, 0xa80a80); /* 4 kHz sample rate */ + cx_write(MO_DDSCFG_IO, 0x5); /* enable */ + } +} + +static void cx88_ir_stop(struct cx88_core *core, struct cx88_IR *ir) +{ + if (ir->sampling) { + cx_write(MO_DDSCFG_IO, 0x0); + core->pci_irqmask &= ~(1 << 18); + } + + if (ir->polling) { + del_timer_sync(&ir->timer); + flush_scheduled_work(); + } +} + /* ---------------------------------------------------------------------- */ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) @@ -163,14 +192,12 @@ int cx88_ir_init(struct cx88_core *core, struct input_dev *input_dev; IR_KEYTAB_TYPE *ir_codes = NULL; int ir_type = IR_TYPE_OTHER; + int err = -ENOMEM; ir = kzalloc(sizeof(*ir), GFP_KERNEL); input_dev = input_allocate_device(); - if (!ir || !input_dev) { - kfree(ir); - input_free_device(input_dev); - return -ENOMEM; - } + if (!ir || !input_dev) + goto err_out_free; ir->input = input_dev; @@ -280,9 +307,8 @@ int cx88_ir_init(struct cx88_core *core, } if (NULL == ir_codes) { - kfree(ir); - input_free_device(input_dev); - return -ENODEV; + err = -ENODEV; + goto err_out_free; } /* init input device */ @@ -307,23 +333,22 @@ int cx88_ir_init(struct cx88_core *core, ir->core = core; core->ir = ir; - if (ir->polling) { - INIT_WORK(&ir->work, cx88_ir_work, ir); - init_timer(&ir->timer); - ir->timer.function = ir_timer; - ir->timer.data = (unsigned long)ir; - schedule_work(&ir->work); - } - if (ir->sampling) { - core->pci_irqmask |= (1 << 18); /* IR_SMP_INT */ - cx_write(MO_DDS_IO, 0xa80a80); /* 4 kHz sample rate */ - cx_write(MO_DDSCFG_IO, 0x5); /* enable */ - } + cx88_ir_start(core, ir); /* all done */ - input_register_device(ir->input); + err = input_register_device(ir->input); + if (err) + goto err_out_stop; return 0; + + err_out_stop: + cx88_ir_stop(core, ir); + core->ir = NULL; + err_out_free: + input_free_device(input_dev); + kfree(ir); + return err; } int cx88_ir_fini(struct cx88_core *core) @@ -334,15 +359,7 @@ int cx88_ir_fini(struct cx88_core *core) if (NULL == ir) return 0; - if (ir->sampling) { - cx_write(MO_DDSCFG_IO, 0x0); - core->pci_irqmask &= ~(1 << 18); - } - if (ir->polling) { - del_timer(&ir->timer); - flush_scheduled_work(); - } - + cx88_ir_stop(core, ir); input_unregister_device(ir->input); kfree(ir); diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index 6b23a4e..1fe1a83 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -44,8 +44,12 @@ module_param(debug,int,0644); MODULE_PARM_DESC(debug,"enable debug messages [mpeg]"); #define dprintk(level,fmt, arg...) if (debug >= level) \ - printk(KERN_DEBUG "%s/2: " fmt, dev->core->name , ## arg) + printk(KERN_DEBUG "%s/2-mpeg: " fmt, dev->core->name, ## arg) +#define mpeg_dbg(level,fmt, arg...) if (debug >= level) \ + printk(KERN_DEBUG "%s/2-mpeg: " fmt, core->name, ## arg) + +static LIST_HEAD(cx8802_devlist); /* ------------------------------------------------------------------ */ static int cx8802_start_dma(struct cx8802_dev *dev, @@ -65,17 +69,13 @@ static int cx8802_start_dma(struct cx880 /* FIXME: this needs a review. * also: move to cx88-blackbird + cx88-dvb source files? */ - if (cx88_boards[core->board].mpeg == (CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD) ) { - /* Report a warning until the mini driver patch is applied, - * else the following conditions will set the dma registers incorrectly. - * This will be removed in the next major patch and changes to the conditions - * will be made. - */ - printk(KERN_INFO "%s() board->(CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD) is invalid\n", __FUNCTION__); - return -EINVAL; - } - if (cx88_boards[core->board].mpeg & CX88_MPEG_DVB) { + dprintk( 1, "core->active_type_id = 0x%08x\n", core->active_type_id); + + if ( (core->active_type_id == CX88_MPEG_DVB) && + (cx88_boards[core->board].mpeg & CX88_MPEG_DVB) ) { + + dprintk( 1, "cx8802_start_dma doing .dvb\n"); /* negedge driven & software reset */ cx_write(TS_GEN_CNTRL, 0x0040 | dev->ts_gen_cntrl); udelay(100); @@ -93,15 +93,17 @@ static int cx8802_start_dma(struct cx880 cx_write(MO_PINMUX_IO, 0x88); /* Enable MPEG parallel IO and video signal pins */ udelay(100); break; + case CX88_BOARD_HAUPPAUGE_HVR1300: + break; default: cx_write(TS_SOP_STAT, 0x00); break; } cx_write(TS_GEN_CNTRL, dev->ts_gen_cntrl); udelay(100); - } - - if (cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD) { + } else if ( (core->active_type_id == CX88_MPEG_BLACKBIRD) && + (cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD) ) { + dprintk( 1, "cx8802_start_dma doing .blackbird\n"); cx_write(MO_PINMUX_IO, 0x88); /* enable MPEG parallel IO */ cx_write(TS_GEN_CNTRL, 0x46); /* punctured clock TS & posedge driven & software reset */ @@ -112,6 +114,10 @@ static int cx8802_start_dma(struct cx880 cx_write(TS_GEN_CNTRL, 0x06); /* punctured clock TS & posedge driven */ udelay(100); + } else { + printk( "%s() Failed. Unsupported value in .mpeg (0x%08x)\n", __FUNCTION__, + cx88_boards[core->board].mpeg ); + return -EINVAL; } /* reset counter */ @@ -542,8 +548,315 @@ int cx8802_resume_common(struct pci_dev return 0; } +struct cx8802_dev * cx8802_get_device(struct inode *inode) +{ + int minor = iminor(inode); + struct cx8802_dev *h = NULL; + struct list_head *list; + + list_for_each(list,&cx8802_devlist) { + h = list_entry(list, struct cx8802_dev, devlist); + if (h->mpeg_dev->minor == minor) + return h; + } + + return NULL; +} + +struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype) +{ + struct cx8802_dev *h = NULL; + struct cx8802_driver *d = NULL; + struct list_head *list; + struct list_head *list2; + + list_for_each(list,&cx8802_devlist) { + h = list_entry(list, struct cx8802_dev, devlist); + if (h != dev) + continue; + + list_for_each(list2, &h->drvlist.devlist) { + d = list_entry(list2, struct cx8802_driver, devlist); + + /* only unregister the correct driver type */ + if (d->type_id == btype) { + return d; + } + } + } + + return NULL; +} + +/* Driver asked for hardware access. */ +int cx8802_request_acquire(struct cx8802_driver *drv) +{ + struct cx88_core *core = drv->core; + + /* Fail a request for hardware if the device is busy. */ + if (core->active_type_id != CX88_BOARD_NONE) + return -EBUSY; + + if (drv->advise_acquire) + { + core->active_type_id = drv->type_id; + drv->advise_acquire(drv); + + mpeg_dbg(1,"%s() Post acquire GPIO=%x\n", __FUNCTION__, cx_read(MO_GP0_IO)); + } + + return 0; +} + +/* Driver asked to release hardware. */ +int cx8802_request_release(struct cx8802_driver *drv) +{ + struct cx88_core *core = drv->core; + + if (drv->advise_release) + { + drv->advise_release(drv); + core->active_type_id = CX88_BOARD_NONE; + mpeg_dbg(1,"%s() Post release GPIO=%x\n", __FUNCTION__, cx_read(MO_GP0_IO)); + } + + return 0; +} + +static int cx8802_check_driver(struct cx8802_driver *drv) +{ + if (drv == NULL) + return -ENODEV; + + if ((drv->type_id != CX88_MPEG_DVB) && + (drv->type_id != CX88_MPEG_BLACKBIRD)) + return -EINVAL; + + if ((drv->hw_access != CX8802_DRVCTL_SHARED) && + (drv->hw_access != CX8802_DRVCTL_EXCLUSIVE)) + return -EINVAL; + + if ((drv->probe == NULL) || + (drv->remove == NULL) || + (drv->advise_acquire == NULL) || + (drv->advise_release == NULL)) + return -EINVAL; + + return 0; +} + +int cx8802_register_driver(struct cx8802_driver *drv) +{ + struct cx8802_dev *h; + struct cx8802_driver *driver; + struct list_head *list; + int err = 0, i = 0; + + printk(KERN_INFO "%s() ->registering driver type=%s access=%s\n", __FUNCTION__ , + drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird", + drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive"); + + if ((err = cx8802_check_driver(drv)) != 0) { + printk(KERN_INFO "%s() cx8802_driver is invalid\n", __FUNCTION__ ); + return err; + } + + list_for_each(list,&cx8802_devlist) { + h = list_entry(list, struct cx8802_dev, devlist); + + printk(KERN_INFO "CORE %s: subsystem: %04x:%04x, board: %s [card=%d]\n", + h->core->name,h->pci->subsystem_vendor, + h->pci->subsystem_device,cx88_boards[h->core->board].name, + h->core->board); + + /* Bring up a new struct for each driver instance */ + driver = kzalloc(sizeof(*drv),GFP_KERNEL); + if (driver == NULL) + return -ENOMEM; + + /* Snapshot of the driver registration data */ + drv->core = h->core; + drv->suspend = cx8802_suspend_common; + drv->resume = cx8802_resume_common; + drv->request_acquire = cx8802_request_acquire; + drv->request_release = cx8802_request_release; + memcpy(driver, drv, sizeof(*driver)); + + err = drv->probe(driver); + if (err == 0) { + i++; + mutex_lock(&drv->core->lock); + list_add_tail(&driver->devlist,&h->drvlist.devlist); + mutex_unlock(&drv->core->lock); + } else { + printk(KERN_ERR "%s() ->probe failed err = %d\n", __FUNCTION__, err); + } + + } + if (i == 0) + err = -ENODEV; + else + err = 0; + + return err; +} + +int cx8802_unregister_driver(struct cx8802_driver *drv) +{ + struct cx8802_dev *h; + struct cx8802_driver *d; + struct list_head *list; + struct list_head *list2, *q; + int err = 0, i = 0; + + printk(KERN_INFO "%s() ->unregistering driver type=%s\n", __FUNCTION__ , + drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird"); + + list_for_each(list,&cx8802_devlist) { + i++; + h = list_entry(list, struct cx8802_dev, devlist); + + printk(KERN_INFO "CORE %s: subsystem: %04x:%04x, board: %s [card=%d]\n", + h->core->name,h->pci->subsystem_vendor, + h->pci->subsystem_device,cx88_boards[h->core->board].name, + h->core->board); + + list_for_each_safe(list2, q, &h->drvlist.devlist) { + d = list_entry(list2, struct cx8802_driver, devlist); + + /* only unregister the correct driver type */ + if (d->type_id != drv->type_id) + continue; + + err = d->remove(d); + if (err == 0) { + mutex_lock(&drv->core->lock); + list_del(list2); + mutex_unlock(&drv->core->lock); + } else + printk(KERN_ERR "%s() ->remove failed err = %d\n", __FUNCTION__, err); + + } + + } + + return err; +} + /* ----------------------------------------------------------- */ +static int __devinit cx8802_probe(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + struct cx8802_dev *dev; + struct cx88_core *core; + int err; + + /* general setup */ + core = cx88_core_get(pci_dev); + if (NULL == core) + return -EINVAL; + printk("%s/2: cx2388x 8802 Driver Manager\n", core->name); + + err = -ENODEV; + if (!cx88_boards[core->board].mpeg) + goto fail_core; + + err = -ENOMEM; + dev = kzalloc(sizeof(*dev),GFP_KERNEL); + if (NULL == dev) + goto fail_core; + dev->pci = pci_dev; + dev->core = core; + + err = cx8802_init_common(dev); + if (err != 0) + goto fail_free; + + INIT_LIST_HEAD(&dev->drvlist.devlist); + list_add_tail(&dev->devlist,&cx8802_devlist); + + /* Maintain a reference so cx88-video can query the 8802 device. */ + core->dvbdev = dev; + return 0; + + fail_free: + kfree(dev); + fail_core: + cx88_core_put(core,pci_dev); + return err; +} + +static void __devexit cx8802_remove(struct pci_dev *pci_dev) +{ + struct cx8802_dev *dev; + struct cx8802_driver *h; + struct list_head *list; + + dev = pci_get_drvdata(pci_dev); + + dprintk( 1, "%s\n", __FUNCTION__); + + list_for_each(list,&dev->drvlist.devlist) { + h = list_entry(list, struct cx8802_driver, devlist); + dprintk( 1, " ->driver\n"); + if (h->remove == NULL) { + printk(KERN_ERR "%s .. skipping driver, no probe function\n", __FUNCTION__); + continue; + } + printk(KERN_INFO "%s .. Removing driver type %d\n", __FUNCTION__, h->type_id); + cx8802_unregister_driver(h); + list_del(&dev->drvlist.devlist); + } + + /* Destroy any 8802 reference. */ + dev->core->dvbdev = NULL; + + /* common */ + cx8802_fini_common(dev); + cx88_core_put(dev->core,dev->pci); + kfree(dev); +} + +static struct pci_device_id cx8802_pci_tbl[] = { + { + .vendor = 0x14f1, + .device = 0x8802, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + },{ + /* --- end of list --- */ + } +}; +MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl); + +static struct pci_driver cx8802_pci_driver = { + .name = "cx88-mpeg driver manager", + .id_table = cx8802_pci_tbl, + .probe = cx8802_probe, + .remove = __devexit_p(cx8802_remove), +}; + +static int cx8802_init(void) +{ + printk(KERN_INFO "cx2388x cx88-mpeg Driver Manager version %d.%d.%d loaded\n", + (CX88_VERSION_CODE >> 16) & 0xff, + (CX88_VERSION_CODE >> 8) & 0xff, + CX88_VERSION_CODE & 0xff); +#ifdef SNAPSHOT + printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n", + SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100); +#endif + return pci_register_driver(&cx8802_pci_driver); +} + +static void cx8802_fini(void) +{ + pci_unregister_driver(&cx8802_pci_driver); +} + +module_init(cx8802_init); +module_exit(cx8802_fini); EXPORT_SYMBOL(cx8802_buf_prepare); EXPORT_SYMBOL(cx8802_buf_queue); EXPORT_SYMBOL(cx8802_cancel_buffers); @@ -551,9 +864,10 @@ EXPORT_SYMBOL(cx8802_cancel_buffers); EXPORT_SYMBOL(cx8802_init_common); EXPORT_SYMBOL(cx8802_fini_common); -EXPORT_SYMBOL(cx8802_suspend_common); -EXPORT_SYMBOL(cx8802_resume_common); - +EXPORT_SYMBOL(cx8802_register_driver); +EXPORT_SYMBOL(cx8802_unregister_driver); +EXPORT_SYMBOL(cx8802_get_device); +EXPORT_SYMBOL(cx8802_get_driver); /* ----------------------------------------------------------- */ /* * Local variables: diff --git a/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/video/cx88/cx88-tvaudio.c index 58ba9f7..3482e01 100644 --- a/drivers/media/video/cx88/cx88-tvaudio.c +++ b/drivers/media/video/cx88/cx88-tvaudio.c @@ -143,19 +143,6 @@ static void set_audio_finish(struct cx88 cx88_start_audio_dma(core); if (cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD) { - /* sets sound input from external adc */ - switch (core->board) { - case CX88_BOARD_HAUPPAUGE_ROSLYN: - case CX88_BOARD_KWORLD_MCE200_DELUXE: - case CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT: - case CX88_BOARD_PIXELVIEW_PLAYTV_P7000: - case CX88_BOARD_ASUS_PVR_416: - cx_clear(AUD_CTL, EN_I2SIN_ENABLE); - break; - default: - cx_set(AUD_CTL, EN_I2SIN_ENABLE); - } - cx_write(AUD_I2SINPUTCNTL, 4); cx_write(AUD_BAUDRATE, 1); /* 'pass-thru mode': this enables the i2s output to the mpeg encoder */ diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 90e298d..8613378 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -454,6 +454,14 @@ static int video_mux(struct cx88_core *c cx_clear(MO_FILTER_ODD, 0x00002020); break; } + + if (cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD) { + /* sets sound input from external adc */ + if (INPUT(input)->extadc) + cx_set(AUD_CTL, EN_I2SIN_ENABLE); + else + cx_clear(AUD_CTL, EN_I2SIN_ENABLE); + } return 0; } @@ -1490,6 +1498,30 @@ int cx88_do_ioctl(struct inode *inode, s mutex_unlock(&core->lock); return 0; } +#ifdef CONFIG_VIDEO_ADV_DEBUG + /* ioctls to allow direct acces to the cx2388x registers */ + case VIDIOC_INT_G_REGISTER: + { + struct v4l2_register *reg = arg; + + if (reg->i2c_id != 0) + return -EINVAL; + /* cx2388x has a 24-bit register space */ + reg->val = cx_read(reg->reg&0xffffff); + return 0; + } + case VIDIOC_INT_S_REGISTER: + { + struct v4l2_register *reg = arg; + + if (reg->i2c_id != 0) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + cx_write(reg->reg&0xffffff, reg->val); + return 0; + } +#endif default: return v4l_compat_translate_ioctl(inode,file,cmd,arg, diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index 3bc91aa..7054e94 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -74,6 +74,11 @@ enum cx88_board_type { CX88_MPEG_BLACKBIRD }; +enum cx8802_board_access { + CX8802_DRVCTL_SHARED = 1, + CX8802_DRVCTL_EXCLUSIVE = 2, +}; + /* ----------------------------------------------------------- */ /* tv norms */ @@ -220,6 +225,7 @@ struct cx88_input { enum cx88_itype type; unsigned int vmux; u32 gpio0, gpio1, gpio2, gpio3; + unsigned int extadc:1; }; struct cx88_board { @@ -330,6 +336,7 @@ struct cx88_core { /* cx88-video needs to access cx8802 for hybrid tuner pll access. */ struct cx8802_dev *dvbdev; + enum cx88_board_type active_type_id; }; struct cx8800_dev; @@ -405,6 +412,31 @@ struct cx8802_suspend_state { int disabled; }; +struct cx8802_driver { + struct cx88_core *core; + struct list_head devlist; + + /* Type of driver and access required */ + enum cx88_board_type type_id; + enum cx8802_board_access hw_access; + + /* MPEG 8802 internal only */ + int (*suspend)(struct pci_dev *pci_dev, pm_message_t state); + int (*resume)(struct pci_dev *pci_dev); + + /* MPEG 8802 -> mini driver - Driver probe and configuration */ + int (*probe)(struct cx8802_driver *drv); + int (*remove)(struct cx8802_driver *drv); + + /* MPEG 8802 -> mini driver - Access for hardware control */ + int (*advise_acquire)(struct cx8802_driver *drv); + int (*advise_release)(struct cx8802_driver *drv); + + /* MPEG 8802 <- mini driver - Access for hardware control */ + int (*request_acquire)(struct cx8802_driver *drv); + int (*request_release)(struct cx8802_driver *drv); +}; + struct cx8802_dev { struct cx88_core *core; spinlock_t slock; @@ -439,6 +471,9 @@ struct cx8802_dev { /* mpeg params */ struct cx2341x_mpeg_params params; + + /* List of attached drivers */ + struct cx8802_driver drvlist; }; /* ----------------------------------------------------------- */ @@ -571,6 +606,11 @@ void cx88_get_stereo(struct cx88_core *c void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual); int cx88_audio_thread(void *data); +int cx8802_register_driver(struct cx8802_driver *drv); +int cx8802_unregister_driver(struct cx8802_driver *drv); +struct cx8802_dev * cx8802_get_device(struct inode *inode); +struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype); + /* ----------------------------------------------------------- */ /* cx88-input.c */ @@ -600,6 +640,13 @@ extern int cx88_do_ioctl(struct inode *i extern const u32 cx88_user_ctrls[]; extern int cx8800_ctrl_query(struct v4l2_queryctrl *qctrl); +/* ----------------------------------------------------------- */ +/* cx88-blackbird.c */ +/* used by cx88-ivtv ioctl emulation layer */ +extern int (*cx88_ioctl_hook)(struct inode *inode, struct file *file, + unsigned int cmd, void *arg); +extern unsigned int (*cx88_ioctl_translator)(unsigned int cmd); + /* * Local variables: * c-basic-offset: 8 diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index 1457b16..9f18cd7 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -305,15 +305,14 @@ static int ir_attach(struct i2c_adapter int ir_type; struct IR_i2c *ir; struct input_dev *input_dev; + int err; ir = kzalloc(sizeof(struct IR_i2c),GFP_KERNEL); input_dev = input_allocate_device(); if (!ir || !input_dev) { - input_free_device(input_dev); - kfree(ir); - return -ENOMEM; + err = -ENOMEM; + goto err_out_free; } - memset(ir,0,sizeof(*ir)); ir->c = client_template; ir->input = input_dev; @@ -361,26 +360,27 @@ static int ir_attach(struct i2c_adapter break; default: /* shouldn't happen */ - printk(DEVNAME ": Huh? unknown i2c address (0x%02x)?\n",addr); - kfree(ir); - return -1; + printk(DEVNAME ": Huh? unknown i2c address (0x%02x)?\n", addr); + err = -ENODEV; + goto err_out_free; } /* Sets name */ snprintf(ir->c.name, sizeof(ir->c.name), "i2c IR (%s)", name); - ir->ir_codes=ir_codes; + ir->ir_codes = ir_codes; /* register i2c device * At device register, IR codes may be changed to be * board dependent. */ - i2c_attach_client(&ir->c); + err = i2c_attach_client(&ir->c); + if (err) + goto err_out_free; /* If IR not supported or disabled, unregisters driver */ if (ir->get_key == NULL) { - i2c_detach_client(&ir->c); - kfree(ir); - return -1; + err = -ENODEV; + goto err_out_detach; } /* Phys addr can only be set after attaching (for ir->c.dev.bus_id) */ @@ -389,15 +389,17 @@ static int ir_attach(struct i2c_adapter ir->c.dev.bus_id); /* init + register input device */ - ir_input_init(input_dev,&ir->ir,ir_type,ir->ir_codes); + ir_input_init(input_dev, &ir->ir, ir_type, ir->ir_codes); input_dev->id.bustype = BUS_I2C; input_dev->name = ir->c.name; input_dev->phys = ir->phys; - /* register event device */ - input_register_device(ir->input); + err = input_register_device(ir->input); + if (err) + goto err_out_detach; + printk(DEVNAME ": %s detected at %s [%s]\n", - ir->input->name,ir->input->phys,adap->name); + ir->input->name, ir->input->phys, adap->name); /* start polling via eventd */ INIT_WORK(&ir->work, ir_work, ir); @@ -407,6 +409,13 @@ static int ir_attach(struct i2c_adapter schedule_work(&ir->work); return 0; + + err_out_detach: + i2c_detach_client(&ir->c); + err_out_free: + input_free_device(input_dev); + kfree(ir); + return err; } static int ir_detach(struct i2c_client *client) @@ -414,7 +423,7 @@ static int ir_detach(struct i2c_client * struct IR_i2c *ir = i2c_get_clientdata(client); /* kill outstanding polls */ - del_timer(&ir->timer); + del_timer_sync(&ir->timer); flush_scheduled_work(); /* unregister devices */ diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c new file mode 100644 index 0000000..b7d824e --- /dev/null +++ b/drivers/media/video/ov7670.c @@ -0,0 +1,1286 @@ +/* + * A V4L2 driver for OmniVision OV7670 cameras. + * + * Copyright 2006 One Laptop Per Child Association, Inc. Written + * by Jonathan Corbet with substantial inspiration from Mark + * McClelland's ovcamchip code. + * + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + */ +#include +#include +#include +#include +#include +#include +#include +#include + + +MODULE_AUTHOR("Jonathan Corbet = 0) + *value = (unsigned char) ret; + return ret; +} + + +static int ov7670_write(struct i2c_client *c, unsigned char reg, + unsigned char value) +{ + return i2c_smbus_write_byte_data(c, reg, value); +} + + +/* + * Write a list of register settings; ff/ff stops the process. + */ +static int ov7670_write_array(struct i2c_client *c, struct regval_list *vals) +{ + while (vals->reg_num != 0xff || vals->value != 0xff) { + int ret = ov7670_write(c, vals->reg_num, vals->value); + if (ret < 0) + return ret; + vals++; + } + return 0; +} + + +/* + * Stuff that knows about the sensor. + */ +static void ov7670_reset(struct i2c_client *client) +{ + ov7670_write(client, REG_COM7, COM7_RESET); + msleep(1); +} + + +static int ov7670_init(struct i2c_client *client) +{ + return ov7670_write_array(client, ov7670_default_regs); +} + + + +static int ov7670_detect(struct i2c_client *client) +{ + unsigned char v; + int ret; + + ret = ov7670_init(client); + if (ret < 0) + return ret; + ret = ov7670_read(client, REG_MIDH, &v); + if (ret < 0) + return ret; + if (v != 0x7f) /* OV manuf. id. */ + return -ENODEV; + ret = ov7670_read(client, REG_MIDL, &v); + if (ret < 0) + return ret; + if (v != 0xa2) + return -ENODEV; + /* + * OK, we know we have an OmniVision chip...but which one? + */ + ret = ov7670_read(client, REG_PID, &v); + if (ret < 0) + return ret; + if (v != 0x76) /* PID + VER = 0x76 / 0x73 */ + return -ENODEV; + ret = ov7670_read(client, REG_VER, &v); + if (ret < 0) + return ret; + if (v != 0x73) /* PID + VER = 0x76 / 0x73 */ + return -ENODEV; + return 0; +} + + +/* + * Store information about the video data format. The color matrix + * is deeply tied into the format, so keep the relevant values here. + * The magic matrix nubmers come from OmniVision. + */ +static struct ov7670_format_struct { + __u8 *desc; + __u32 pixelformat; + struct regval_list *regs; + int cmatrix[CMATRIX_LEN]; +} ov7670_formats[] = { + { + .desc = "YUYV 4:2:2", + .pixelformat = V4L2_PIX_FMT_YUYV, + .regs = ov7670_fmt_yuv422, + .cmatrix = { 128, -128, 0, -34, -94, 128 }, + }, + { + .desc = "RGB 444", + .pixelformat = V4L2_PIX_FMT_RGB444, + .regs = ov7670_fmt_rgb444, + .cmatrix = { 179, -179, 0, -61, -176, 228 }, + }, + { + .desc = "RGB 565", + .pixelformat = V4L2_PIX_FMT_RGB565, + .regs = ov7670_fmt_rgb565, + .cmatrix = { 179, -179, 0, -61, -176, 228 }, + }, + /* + * Pretend we do RGB32. This is here on the assumption that the + * upper layer will reformat RGB444 appropriately. + * + * The entire purpose for this thing's existence is to enable easy + * display of RGB444 for debugging purposes. It will come out soon. + */ + { + .desc = "RGB32 (faked)", + .pixelformat = V4L2_PIX_FMT_RGB32, + .regs = ov7670_fmt_rgb444, + .cmatrix = { 179, -179, 0, -61, -176, 228 }, + }, +}; +#define N_OV7670_FMTS (sizeof(ov7670_formats)/sizeof(ov7670_formats[0])) + +/* + * All formats we support are 2 bytes/pixel. + */ +#define BYTES_PER_PIXEL 2 + +/* + * Then there is the issue of window sizes. Try to capture the info here. + */ + +/* + * QCIF mode is done (by OV) in a very strange way - it actually looks like + * VGA with weird scaling options - they do *not* use the canned QCIF mode + * which is allegedly provided by the sensor. So here's the weird register + * settings. + */ +static struct regval_list ov7670_qcif_regs[] = { + { REG_COM3, COM3_SCALEEN|COM3_DCWEN }, + { REG_COM3, COM3_DCWEN }, + { REG_COM14, COM14_DCWEN | 0x01}, + { 0x73, 0xf1 }, + { 0xa2, 0x52 }, + { 0x7b, 0x1c }, + { 0x7c, 0x28 }, + { 0x7d, 0x3c }, + { 0x7f, 0x69 }, + { REG_COM9, 0x38 }, + { 0xa1, 0x0b }, + { 0x74, 0x19 }, + { 0x9a, 0x80 }, + { 0x43, 0x14 }, + { REG_COM13, 0xc0 }, + { 0xff, 0xff }, +}; + +static struct ov7670_win_size { + int width; + int height; + unsigned char com7_bit; + int hstart; /* Start/stop values for the camera. Note */ + int hstop; /* that they do not always make complete */ + int vstart; /* sense to humans, but evidently the sensor */ + int vstop; /* will do the right thing... */ + struct regval_list *regs; /* Regs to tweak */ +/* h/vref stuff */ +} ov7670_win_sizes[] = { + /* VGA */ + { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .com7_bit = COM7_FMT_VGA, + .hstart = 158, /* These values from */ + .hstop = 14, /* Omnivision */ + .vstart = 10, + .vstop = 490, + .regs = NULL, + }, + /* CIF */ + { + .width = CIF_WIDTH, + .height = CIF_HEIGHT, + .com7_bit = COM7_FMT_CIF, + .hstart = 170, /* Empirically determined */ + .hstop = 90, + .vstart = 14, + .vstop = 494, + .regs = NULL, + }, + /* QVGA */ + { + .width = QVGA_WIDTH, + .height = QVGA_HEIGHT, + .com7_bit = COM7_FMT_QVGA, + .hstart = 164, /* Empirically determined */ + .hstop = 20, + .vstart = 14, + .vstop = 494, + .regs = NULL, + }, + /* QCIF */ + { + .width = QCIF_WIDTH, + .height = QCIF_HEIGHT, + .com7_bit = COM7_FMT_VGA, /* see comment above */ + .hstart = 456, /* Empirically determined */ + .hstop = 24, + .vstart = 14, + .vstop = 494, + .regs = ov7670_qcif_regs, + }, +}; + +#define N_WIN_SIZES (sizeof(ov7670_win_sizes)/sizeof(ov7670_win_sizes[0])) + + +/* + * Store a set of start/stop values into the camera. + */ +static int ov7670_set_hw(struct i2c_client *client, int hstart, int hstop, + int vstart, int vstop) +{ + int ret; + unsigned char v; +/* + * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of + * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is + * a mystery "edge offset" value in the top two bits of href. + */ + ret = ov7670_write(client, REG_HSTART, (hstart >> 3) & 0xff); + ret += ov7670_write(client, REG_HSTOP, (hstop >> 3) & 0xff); + ret += ov7670_read(client, REG_HREF, &v); + v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); + msleep(10); + ret += ov7670_write(client, REG_HREF, v); +/* + * Vertical: similar arrangement, but only 10 bits. + */ + ret += ov7670_write(client, REG_VSTART, (vstart >> 2) & 0xff); + ret += ov7670_write(client, REG_VSTOP, (vstop >> 2) & 0xff); + ret += ov7670_read(client, REG_VREF, &v); + v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3); + msleep(10); + ret += ov7670_write(client, REG_VREF, v); + return ret; +} + + +static int ov7670_enum_fmt(struct i2c_client *c, struct v4l2_fmtdesc *fmt) +{ + struct ov7670_format_struct *ofmt; + + if (fmt->index >= N_OV7670_FMTS) + return -EINVAL; + + ofmt = ov7670_formats + fmt->index; + fmt->flags = 0; + strcpy(fmt->description, ofmt->desc); + fmt->pixelformat = ofmt->pixelformat; + return 0; +} + + +static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt, + struct ov7670_format_struct **ret_fmt, + struct ov7670_win_size **ret_wsize) +{ + int index; + struct ov7670_win_size *wsize; + struct v4l2_pix_format *pix = &fmt->fmt.pix; + + for (index = 0; index < N_OV7670_FMTS; index++) + if (ov7670_formats[index].pixelformat == pix->pixelformat) + break; + if (index >= N_OV7670_FMTS) + return -EINVAL; + if (ret_fmt != NULL) + *ret_fmt = ov7670_formats + index; + /* + * Fields: the OV devices claim to be progressive. + */ + if (pix->field == V4L2_FIELD_ANY) + pix->field = V4L2_FIELD_NONE; + else if (pix->field != V4L2_FIELD_NONE) + return -EINVAL; + /* + * Round requested image size down to the nearest + * we support, but not below the smallest. + */ + for (wsize = ov7670_win_sizes; wsize < ov7670_win_sizes + N_WIN_SIZES; + wsize++) + if (pix->width >= wsize->width && pix->height >= wsize->height) + break; + if (wsize >= ov7670_win_sizes + N_WIN_SIZES) + wsize--; /* Take the smallest one */ + if (ret_wsize != NULL) + *ret_wsize = wsize; + /* + * Note the size we'll actually handle. + */ + pix->width = wsize->width; + pix->height = wsize->height; + pix->bytesperline = pix->width*BYTES_PER_PIXEL; + if (pix->pixelformat == V4L2_PIX_FMT_RGB32) + pix->bytesperline *= 2; + pix->sizeimage = pix->height*pix->bytesperline; + return 0; +} + +/* + * Set a format. + */ +static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) +{ + int ret; + struct ov7670_format_struct *ovfmt; + struct ov7670_win_size *wsize; + struct ov7670_info *info = i2c_get_clientdata(c); + unsigned char com7; + + ret = ov7670_try_fmt(c, fmt, &ovfmt, &wsize); + if (ret) + return ret; + /* + * COM7 is a pain in the ass, it doesn't like to be read then + * quickly written afterward. But we have everything we need + * to set it absolutely here, as long as the format-specific + * register sets list it first. + */ + com7 = ovfmt->regs[0].value; + com7 |= wsize->com7_bit; + ov7670_write(c, REG_COM7, com7); + /* + * Now write the rest of the array. Also store start/stops + */ + ov7670_write_array(c, ovfmt->regs + 1); + ov7670_set_hw(c, wsize->hstart, wsize->hstop, wsize->vstart, + wsize->vstop); + ret = 0; + if (wsize->regs) + ret = ov7670_write_array(c, wsize->regs); + info->fmt = ovfmt; + return 0; +} + +/* + * Code for dealing with controls. + */ + + + + + +static int ov7670_store_cmatrix(struct i2c_client *client, + int matrix[CMATRIX_LEN]) +{ + int i, ret; + unsigned char signbits; + + /* + * Weird crap seems to exist in the upper part of + * the sign bits register, so let's preserve it. + */ + ret = ov7670_read(client, REG_CMATRIX_SIGN, &signbits); + signbits &= 0xc0; + + for (i = 0; i < CMATRIX_LEN; i++) { + unsigned char raw; + + if (matrix[i] < 0) { + signbits |= (1 << i); + if (matrix[i] < -255) + raw = 0xff; + else + raw = (-1 * matrix[i]) & 0xff; + } + else { + if (matrix[i] > 255) + raw = 0xff; + else + raw = matrix[i] & 0xff; + } + ret += ov7670_write(client, REG_CMATRIX_BASE + i, raw); + } + ret += ov7670_write(client, REG_CMATRIX_SIGN, signbits); + return ret; +} + + +/* + * Hue also requires messing with the color matrix. It also requires + * trig functions, which tend not to be well supported in the kernel. + * So here is a simple table of sine values, 0-90 degrees, in steps + * of five degrees. Values are multiplied by 1000. + * + * The following naive approximate trig functions require an argument + * carefully limited to -180 <= theta <= 180. + */ +#define SIN_STEP 5 +static const int ov7670_sin_table[] = { + 0, 87, 173, 258, 342, 422, + 499, 573, 642, 707, 766, 819, + 866, 906, 939, 965, 984, 996, + 1000 +}; + +static int ov7670_sine(int theta) +{ + int chs = 1; + int sine; + + if (theta < 0) { + theta = -theta; + chs = -1; + } + if (theta <= 90) + sine = ov7670_sin_table[theta/SIN_STEP]; + else { + theta -= 90; + sine = 1000 - ov7670_sin_table[theta/SIN_STEP]; + } + return sine*chs; +} + +static int ov7670_cosine(int theta) +{ + theta = 90 - theta; + if (theta > 180) + theta -= 360; + else if (theta < -180) + theta += 360; + return ov7670_sine(theta); +} + + + + +static void ov7670_calc_cmatrix(struct ov7670_info *info, + int matrix[CMATRIX_LEN]) +{ + int i; + /* + * Apply the current saturation setting first. + */ + for (i = 0; i < CMATRIX_LEN; i++) + matrix[i] = (info->fmt->cmatrix[i]*info->sat) >> 7; + /* + * Then, if need be, rotate the hue value. + */ + if (info->hue != 0) { + int sinth, costh, tmpmatrix[CMATRIX_LEN]; + + memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int)); + sinth = ov7670_sine(info->hue); + costh = ov7670_cosine(info->hue); + + matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000; + matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000; + matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000; + matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000; + matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000; + matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000; + } +} + + + +static int ov7670_t_sat(struct i2c_client *client, int value) +{ + struct ov7670_info *info = i2c_get_clientdata(client); + int matrix[CMATRIX_LEN]; + int ret; + + info->sat = value; + ov7670_calc_cmatrix(info, matrix); + ret = ov7670_store_cmatrix(client, matrix); + return ret; +} + +static int ov7670_q_sat(struct i2c_client *client, __s32 *value) +{ + struct ov7670_info *info = i2c_get_clientdata(client); + + *value = info->sat; + return 0; +} + +static int ov7670_t_hue(struct i2c_client *client, int value) +{ + struct ov7670_info *info = i2c_get_clientdata(client); + int matrix[CMATRIX_LEN]; + int ret; + + if (value < -180 || value > 180) + return -EINVAL; + info->hue = value; + ov7670_calc_cmatrix(info, matrix); + ret = ov7670_store_cmatrix(client, matrix); + return ret; +} + + +static int ov7670_q_hue(struct i2c_client *client, __s32 *value) +{ + struct ov7670_info *info = i2c_get_clientdata(client); + + *value = info->hue; + return 0; +} + + +/* + * Some weird registers seem to store values in a sign/magnitude format! + */ +static unsigned char ov7670_sm_to_abs(unsigned char v) +{ + if ((v & 0x80) == 0) + return v + 128; + else + return 128 - (v & 0x7f); +} + + +static unsigned char ov7670_abs_to_sm(unsigned char v) +{ + if (v > 127) + return v & 0x7f; + else + return (128 - v) | 0x80; +} + +static int ov7670_t_brightness(struct i2c_client *client, int value) +{ + unsigned char com8, v; + int ret; + + ov7670_read(client, REG_COM8, &com8); + com8 &= ~COM8_AEC; + ov7670_write(client, REG_COM8, com8); + v = ov7670_abs_to_sm(value); + ret = ov7670_write(client, REG_BRIGHT, v); + return ret; +} + +static int ov7670_q_brightness(struct i2c_client *client, __s32 *value) +{ + unsigned char v; + int ret = ov7670_read(client, REG_BRIGHT, &v); + + *value = ov7670_sm_to_abs(v); + return ret; +} + +static int ov7670_t_contrast(struct i2c_client *client, int value) +{ + return ov7670_write(client, REG_CONTRAS, (unsigned char) value); +} + +static int ov7670_q_contrast(struct i2c_client *client, __s32 *value) +{ + unsigned char v; + int ret = ov7670_read(client, REG_CONTRAS, &v); + + *value = v; + return ret; +} + +static int ov7670_q_hflip(struct i2c_client *client, __s32 *value) +{ + int ret; + unsigned char v; + + ret = ov7670_read(client, REG_MVFP, &v); + *value = (v & MVFP_MIRROR) == MVFP_MIRROR; + return ret; +} + + +static int ov7670_t_hflip(struct i2c_client *client, int value) +{ + unsigned char v; + int ret; + + ret = ov7670_read(client, REG_MVFP, &v); + if (value) + v |= MVFP_MIRROR; + else + v &= ~MVFP_MIRROR; + msleep(10); /* FIXME */ + ret += ov7670_write(client, REG_MVFP, v); + return ret; +} + + + +static int ov7670_q_vflip(struct i2c_client *client, __s32 *value) +{ + int ret; + unsigned char v; + + ret = ov7670_read(client, REG_MVFP, &v); + *value = (v & MVFP_FLIP) == MVFP_FLIP; + return ret; +} + + +static int ov7670_t_vflip(struct i2c_client *client, int value) +{ + unsigned char v; + int ret; + + ret = ov7670_read(client, REG_MVFP, &v); + if (value) + v |= MVFP_FLIP; + else + v &= ~MVFP_FLIP; + msleep(10); /* FIXME */ + ret += ov7670_write(client, REG_MVFP, v); + return ret; +} + + +static struct ov7670_control { + struct v4l2_queryctrl qc; + int (*query)(struct i2c_client *c, __s32 *value); + int (*tweak)(struct i2c_client *c, int value); +} ov7670_controls[] = +{ + { + .qc = { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 0x80, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov7670_t_brightness, + .query = ov7670_q_brightness, + }, + { + .qc = { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 0x40, /* XXX ov7670 spec */ + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov7670_t_contrast, + .query = ov7670_q_contrast, + }, + { + .qc = { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 256, + .step = 1, + .default_value = 0x80, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov7670_t_sat, + .query = ov7670_q_sat, + }, + { + .qc = { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "HUE", + .minimum = -180, + .maximum = 180, + .step = 5, + .default_value = 0, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov7670_t_hue, + .query = ov7670_q_hue, + }, + { + .qc = { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .tweak = ov7670_t_vflip, + .query = ov7670_q_vflip, + }, + { + .qc = { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Horizontal mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .tweak = ov7670_t_hflip, + .query = ov7670_q_hflip, + }, +}; +#define N_CONTROLS (sizeof(ov7670_controls)/sizeof(ov7670_controls[0])) + +static struct ov7670_control *ov7670_find_control(__u32 id) +{ + int i; + + for (i = 0; i < N_CONTROLS; i++) + if (ov7670_controls[i].qc.id == id) + return ov7670_controls + i; + return NULL; +} + + +static int ov7670_queryctrl(struct i2c_client *client, + struct v4l2_queryctrl *qc) +{ + struct ov7670_control *ctrl = ov7670_find_control(qc->id); + + if (ctrl == NULL) + return -EINVAL; + *qc = ctrl->qc; + return 0; +} + +static int ov7670_g_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct ov7670_control *octrl = ov7670_find_control(ctrl->id); + int ret; + + if (octrl == NULL) + return -EINVAL; + ret = octrl->query(client, &ctrl->value); + if (ret >= 0) + return 0; + return ret; +} + +static int ov7670_s_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct ov7670_control *octrl = ov7670_find_control(ctrl->id); + int ret; + + if (octrl == NULL) + return -EINVAL; + ret = octrl->tweak(client, ctrl->value); + if (ret >= 0) + return 0; + return ret; +} + + + + + + +/* + * Basic i2c stuff. + */ +static struct i2c_driver ov7670_driver; + +static int ov7670_attach(struct i2c_adapter *adapter) +{ + int ret; + struct i2c_client *client; + struct ov7670_info *info; + + /* + * For now: only deal with adapters we recognize. + */ + if (adapter->id != I2C_HW_SMBUS_CAFE) + return -ENODEV; + + client = kzalloc(sizeof (struct i2c_client), GFP_KERNEL); + if (! client) + return -ENOMEM; + client->adapter = adapter; + client->addr = OV7670_I2C_ADDR; + client->driver = &ov7670_driver, + strcpy(client->name, "OV7670"); + /* + * Set up our info structure. + */ + info = kzalloc(sizeof (struct ov7670_info), GFP_KERNEL); + if (! info) { + ret = -ENOMEM; + goto out_free; + } + info->fmt = &ov7670_formats[0]; + info->sat = 128; /* Review this */ + i2c_set_clientdata(client, info); + + /* + * Make sure it's an ov7670 + */ + ret = ov7670_detect(client); + if (ret) + goto out_free_info; + i2c_attach_client(client); + return 0; + + out_free_info: + kfree(info); + out_free: + kfree(client); + return ret; +} + + +static int ov7670_detach(struct i2c_client *client) +{ + i2c_detach_client(client); + kfree(i2c_get_clientdata(client)); + kfree(client); + return 0; +} + + +static int ov7670_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ + switch (cmd) { + case VIDIOC_INT_G_CHIP_IDENT: + * (enum v4l2_chip_ident *) arg = V4L2_IDENT_OV7670; + return 0; + + case VIDIOC_INT_RESET: + ov7670_reset(client); + return 0; + + case VIDIOC_INT_INIT: + return ov7670_init(client); + + case VIDIOC_ENUM_FMT: + return ov7670_enum_fmt(client, (struct v4l2_fmtdesc *) arg); + case VIDIOC_TRY_FMT: + return ov7670_try_fmt(client, (struct v4l2_format *) arg, NULL, NULL); + case VIDIOC_S_FMT: + return ov7670_s_fmt(client, (struct v4l2_format *) arg); + case VIDIOC_QUERYCTRL: + return ov7670_queryctrl(client, (struct v4l2_queryctrl *) arg); + case VIDIOC_S_CTRL: + return ov7670_s_ctrl(client, (struct v4l2_control *) arg); + case VIDIOC_G_CTRL: + return ov7670_g_ctrl(client, (struct v4l2_control *) arg); + /* Todo: + g/s_parm + initialization + */ + } + return -EINVAL; +} + + + +static struct i2c_driver ov7670_driver = { + .driver = { + .name = "ov7670", + }, + .id = I2C_DRIVERID_OV7670, + .class = I2C_CLASS_CAM_DIGITAL, + .attach_adapter = ov7670_attach, + .detach_client = ov7670_detach, + .command = ov7670_command, +}; + + +/* + * Module initialization + */ +static int __init ov7670_mod_init(void) +{ + printk(KERN_NOTICE "OmniVision ov7670 sensor driver, at your service\n"); + return i2c_add_driver(&ov7670_driver); +} + +static void __exit ov7670_mod_exit(void) +{ + i2c_del_driver(&ov7670_driver); +} + +module_init(ov7670_mod_init); +module_exit(ov7670_mod_exit); diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index c80c26b..848fb23 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -260,6 +260,22 @@ int pvr2_i2c_cx2584x_v4l_setup(struct pv sizeof(decoder_ops[0]))) - 1; hdw->decoder_ctrl = &ctxt->ctrl; cp->handler = &ctxt->handler; + { + /* + Mike Isely 19-Nov-2006 - This bit + of nuttiness for cx25840 causes that module to + correctly set up its video scaling. This is really + a problem in the cx25840 module itself, but we work + around it here. The problem has not been seen in + ivtv because there VBI is supported and set up. We + don't do VBI here (at least not yet) and thus we + never attempted to even set it up. + */ + struct v4l2_format fmt; + memset(&fmt,0,sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_FMT,&fmt); + } pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x cx2584x V4L2 handler set up", cp->client->addr); return !0; diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index f920e0c..b395391 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -356,28 +356,6 @@ static int ctrl_freq_set(struct pvr2_ctr return 0; } -static int ctrl_hres_max_get(struct pvr2_ctrl *cptr,int *vp) -{ - /* If we're dealing with a 24xxx device, force the horizontal - maximum to be 720 no matter what, since we can't get the device - to work properly with any other value. Otherwise just return - the normal value. */ - *vp = cptr->info->def.type_int.max_value; - if (cptr->hdw->hdw_type == PVR2_HDW_TYPE_24XXX) *vp = 720; - return 0; -} - -static int ctrl_hres_min_get(struct pvr2_ctrl *cptr,int *vp) -{ - /* If we're dealing with a 24xxx device, force the horizontal - minimum to be 720 no matter what, since we can't get the device - to work properly with any other value. Otherwise just return - the normal value. */ - *vp = cptr->info->def.type_int.min_value; - if (cptr->hdw->hdw_type == PVR2_HDW_TYPE_24XXX) *vp = 720; - return 0; -} - static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp) { /* Actual maximum depends on the video standard in effect. */ @@ -758,10 +736,6 @@ static const struct pvr2_ctl_info contro .default_value = 720, DEFREF(res_hor), DEFINT(19,720), - /* Hook in check for clamp on horizontal resolution in - order to avoid unsolved problem involving cx25840. */ - .get_max_value = ctrl_hres_max_get, - .get_min_value = ctrl_hres_min_get, },{ .desc = "Vertical capture resolution", .name = "resolution_ver", diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 3b9012f..f9bb41d 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -185,6 +185,79 @@ static int pvr2_i2c_basic_op(struct pvr2 } } + +/* This is a special entry point for cases of I2C transaction attempts to + the IR receiver. The implementation here simulates the IR receiver by + issuing a command to the FX2 firmware and using that response to return + what the real I2C receiver would have returned. We use this for 24xxx + devices, where the IR receiver chip has been removed and replaced with + FX2 related logic. */ +static int i2c_24xxx_ir(struct pvr2_hdw *hdw, + u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) +{ + u8 dat[4]; + unsigned int stat; + + if (!(rlen || wlen)) { + /* This is a probe attempt. Just let it succeed. */ + return 0; + } + + /* We don't understand this kind of transaction */ + if ((wlen != 0) || (rlen == 0)) return -EIO; + + if (rlen < 3) { + /* Mike Isely Appears to be a probe + attempt from lirc. Just fill in zeroes and return. If + we try instead to do the full transaction here, then bad + things seem to happen within the lirc driver module + (version 0.8.0-7 sources from Debian, when run under + vanilla 2.6.17.6 kernel) - and I don't have the patience + to chase it down. */ + if (rlen > 0) rdata[0] = 0; + if (rlen > 1) rdata[1] = 0; + return 0; + } + + /* Issue a command to the FX2 to read the IR receiver. */ + LOCK_TAKE(hdw->ctl_lock); do { + hdw->cmd_buffer[0] = 0xec; + stat = pvr2_send_request(hdw, + hdw->cmd_buffer,1, + hdw->cmd_buffer,4); + dat[0] = hdw->cmd_buffer[0]; + dat[1] = hdw->cmd_buffer[1]; + dat[2] = hdw->cmd_buffer[2]; + dat[3] = hdw->cmd_buffer[3]; + } while (0); LOCK_GIVE(hdw->ctl_lock); + + /* Give up if that operation failed. */ + if (stat != 0) return stat; + + /* Mangle the results into something that looks like the real IR + receiver. */ + rdata[2] = 0xc1; + if (dat[0] != 1) { + /* No code received. */ + rdata[0] = 0; + rdata[1] = 0; + } else { + u16 val; + /* Mash the FX2 firmware-provided IR code into something + that the normal i2c chip-level driver expects. */ + val = dat[1]; + val <<= 8; + val |= dat[2]; + val >>= 1; + val &= ~0x0003; + val |= 0x8000; + rdata[0] = (val >> 8) & 0xffu; + rdata[1] = val & 0xffu; + } + + return 0; +} + /* This is a special entry point that is entered if an I2C operation is attempted to a wm8775 chip on model 24xxx hardware. Autodetect of this part doesn't work, but we know it is really there. So let's look for @@ -887,17 +960,17 @@ void pvr2_i2c_core_init(struct pvr2_hdw { unsigned int idx; - // The default action for all possible I2C addresses is just to do - // the transfer normally. + /* The default action for all possible I2C addresses is just to do + the transfer normally. */ for (idx = 0; idx < PVR2_I2C_FUNC_CNT; idx++) { hdw->i2c_func[idx] = pvr2_i2c_basic_op; } - // If however we're dealing with new hardware, insert some hacks in - // the I2C transfer stack to let things work better. + /* However, deal with various special cases for 24xxx hardware. */ if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) { hdw->i2c_func[0x1b] = i2c_hack_wm8775; hdw->i2c_func[0x44] = i2c_hack_cx25840; + hdw->i2c_func[0x18] = i2c_24xxx_ir; } // Configure the adapter and set up everything else related to it. diff --git a/drivers/media/video/saa6588.c b/drivers/media/video/saa6588.c index a81285c..7b9859c 100644 --- a/drivers/media/video/saa6588.c +++ b/drivers/media/video/saa6588.c @@ -212,8 +212,10 @@ static void read_from_buf(struct saa6588 if (rd_blocks > s->block_count) rd_blocks = s->block_count; - if (!rd_blocks) + if (!rd_blocks) { + spin_unlock_irqrestore(&s->lock, flags); return; + } for (i = 0; i < rd_blocks; i++) { if (block_to_user_buf(s, buf_ptr)) { diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 51f0cfd..d0ddc65 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -2462,14 +2462,17 @@ struct saa7134_board saa7134_boards[] = .vmux = 1, .amux = TV, .tv = 1, + .gpio = 0x0000000, },{ .name = name_comp1, .vmux = 3, .amux = LINE2, + .gpio = 0x0200000, },{ .name = name_svideo, .vmux = 8, .amux = LINE2, + .gpio = 0x0200000, }}, .radio = { .name = name_radio, @@ -3022,6 +3025,152 @@ struct saa7134_board saa7134_boards[] = .amux = LINE1, }, }, + [SAA7134_BOARD_PINNACLE_PCTV_310i] = { + .name = "Pinnacle PCTV 310i", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x000200000, + .inputs = {{ + .name = name_tv, + .vmux = 4, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 1, + .amux = LINE2, + },{ + .name = name_comp2, + .vmux = 0, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_AVERMEDIA_STUDIO_507] = { + /* Mikhail Fedotov */ + .name = "Avermedia AVerTV Studio 507", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1256_IH3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x03, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x00, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + .gpio = 0x00, + },{ + .name = name_comp2, + .vmux = 3, + .amux = LINE2, + .gpio = 0x00, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + .gpio = 0x00, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x01, + }, + .mute = { + .name = name_mute, + .amux = LINE1, + .gpio = 0x00, + }, + }, + [SAA7134_BOARD_VIDEOMATE_DVBT_200A] = { + /* Francis Barber */ + .name = "Compro Videomate DVB-T200A", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 1, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + }, + [SAA7134_BOARD_HAUPPAUGE_HVR1110] = { + /* Thomas Genty */ + .name = "Hauppauge WinTV-HVR1110 DVB-T/Hybrid", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x000200000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_CINERGY_HT_PCMCIA] = { + .name = "Terratec Cinergy HT PCMCIA", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 6, + .amux = LINE1, + }}, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -3631,6 +3780,36 @@ struct pci_device_id saa7134_pci_tbl[] = .subdevice = 0x4860, .driver_data = SAA7134_BOARD_ASUS_EUROPA2_HYBRID, },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x11bd, + .subdevice = 0x002f, + .driver_data = SAA7134_BOARD_PINNACLE_PCTV_310i, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0x9715, + .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_507, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1043, + .subdevice = 0x4876, + .driver_data = SAA7134_BOARD_ASUSTeK_P7131_DUAL, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6701, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x153b, + .subdevice = 0x1172, + .driver_data = SAA7134_BOARD_CINERGY_HT_PCMCIA, + },{ /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -3717,6 +3896,7 @@ int saa7134_board_init1(struct saa7134_d case SAA7134_BOARD_AVERMEDIA_305: case SAA7134_BOARD_AVERMEDIA_STUDIO_307: case SAA7134_BOARD_AVERMEDIA_307: + case SAA7134_BOARD_AVERMEDIA_STUDIO_507: case SAA7134_BOARD_AVERMEDIA_GO_007_FM: case SAA7134_BOARD_AVERMEDIA_777: /* case SAA7134_BOARD_SABRENT_SBTTVFM: */ /* not finished yet */ @@ -3725,6 +3905,7 @@ int saa7134_board_init1(struct saa7134_d case SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII: case SAA7134_BOARD_VIDEOMATE_DVBT_300: case SAA7134_BOARD_VIDEOMATE_DVBT_200: + case SAA7134_BOARD_VIDEOMATE_DVBT_200A: case SAA7134_BOARD_MANLI_MTV001: case SAA7134_BOARD_MANLI_MTV002: case SAA7134_BOARD_BEHOLD_409FM: @@ -3793,6 +3974,7 @@ int saa7134_board_init1(struct saa7134_d break; /* i2c remotes */ case SAA7134_BOARD_PINNACLE_PCTV_110i: + case SAA7134_BOARD_PINNACLE_PCTV_310i: case SAA7134_BOARD_UPMOST_PURPLE_TV: dev->has_remote = SAA7134_REMOTE_I2C; break; @@ -3924,9 +4106,11 @@ int saa7134_board_init2(struct saa7134_d } break; case SAA7134_BOARD_PHILIPS_TIGER: + case SAA7134_BOARD_PINNACLE_PCTV_310i: case SAA7134_BOARD_TEVION_DVBT_220RF: case SAA7134_BOARD_ASUSTeK_P7131_DUAL: case SAA7134_BOARD_MEDION_MD8800_QUADRO: + case SAA7134_BOARD_HAUPPAUGE_HVR1110: /* this is a hybrid board, initialize to analog mode * and configure firmware eeprom address */ @@ -3952,6 +4136,14 @@ int saa7134_board_init2(struct saa7134_d i2c_transfer(&dev->i2c_adap, &msg, 1); } break; + case SAA7134_BOARD_CINERGY_HT_PCMCIA: + /* make the tda10046 find its eeprom */ + { + u8 data[] = { 0x3c, 0x33, 0x60}; + struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; + i2c_transfer(&dev->i2c_adap, &msg, 1); + } + break; case SAA7134_BOARD_KWORLD_ATSC110: { /* enable tuner */ @@ -3964,6 +4156,29 @@ int saa7134_board_init2(struct saa7134_d dev->name, i); } break; + case SAA7134_BOARD_VIDEOMATE_DVBT_200: + case SAA7134_BOARD_VIDEOMATE_DVBT_200A: + /* The T200 and the T200A share the same pci id. Consequently, + * we are going to query eeprom to try to find out which one we + * are actually looking at. */ + + /* Don't do this if the board was specifically selected with an + * insmod option or if we have the default configuration T200*/ + if(!dev->autodetected || (dev->eedata[0x41] == 0xd0)) + break; + if(dev->eedata[0x41] == 0x02) { + /* Reconfigure board as T200A */ + dev->board = SAA7134_BOARD_VIDEOMATE_DVBT_200A; + dev->tuner_type = saa7134_boards[dev->board].tuner_type; + dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf; + printk(KERN_INFO "%s: Reconfigured board as %s\n", + dev->name, saa7134_boards[dev->board].name); + } else { + printk(KERN_WARNING "%s: Unexpected tuner type info: %x in eeprom\n", + dev->name, dev->eedata[0x41]); + break; + } + break; } return 0; } diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 5c9e63d..ed038ff 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -889,15 +889,16 @@ #endif must_configure_manually(); dev->board = SAA7134_BOARD_UNKNOWN; } + dev->autodetected = card[dev->nr] != dev->board; dev->tuner_type = saa7134_boards[dev->board].tuner_type; dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf; if (UNSET != tuner[dev->nr]) dev->tuner_type = tuner[dev->nr]; - printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", - dev->name,pci_dev->subsystem_vendor, - pci_dev->subsystem_device,saa7134_boards[dev->board].name, - dev->board, card[dev->nr] == dev->board ? - "insmod option" : "autodetected"); + printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", + dev->name,pci_dev->subsystem_vendor, + pci_dev->subsystem_device,saa7134_boards[dev->board].name, + dev->board, dev->autodetected ? + "autodetected" : "insmod option"); /* get mmio */ if (!request_mem_region(pci_resource_start(pci_dev,0), diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 6b61d9b..fa83398 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -50,6 +50,10 @@ static unsigned int antenna_pwr = 0; module_param(antenna_pwr, int, 0444); MODULE_PARM_DESC(antenna_pwr,"enable antenna power (Pinnacle 300i)"); +static int use_frontent = 0; +module_param(use_frontent, int, 0644); +MODULE_PARM_DESC(use_frontent,"for cards with multiple frontends (0: terrestrial, 1: satellite)"); + /* ------------------------------------------------------------------ */ static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on) { @@ -293,7 +297,7 @@ static int philips_tu1216_tuner_60_set_p return philips_tda6651_pll_set(0x60, fe, params); } -static int philips_tu1216_request_firmware(struct dvb_frontend *fe, +static int philips_tda1004x_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name) { struct saa7134_dev *dev = fe->dvb->priv; @@ -308,7 +312,7 @@ static struct tda1004x_config philips_tu .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_DEFAULT, .if_freq = TDA10046_FREQ_3617, - .request_firmware = philips_tu1216_request_firmware, + .request_firmware = philips_tda1004x_request_firmware, }; /* ------------------------------------------------------------------ */ @@ -331,12 +335,12 @@ static struct tda1004x_config philips_tu .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_DEFAULT, .if_freq = TDA10046_FREQ_3617, - .request_firmware = philips_tu1216_request_firmware, + .request_firmware = philips_tda1004x_request_firmware, }; /* ------------------------------------------------------------------ */ -static int philips_europa_tuner_init(struct dvb_frontend *fe) +static int philips_td1316_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; static u8 msg[] = { 0x0b, 0xf5, 0x86, 0xab }; @@ -347,18 +351,8 @@ static int philips_europa_tuner_init(str fe->ops.i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1) return -EIO; - msleep(1); - - /* switch the board to dvb mode */ - init_msg.addr = 0x43; - init_msg.len = 0x02; - msg[0] = 0x00; - msg[1] = 0x40; if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1) - return -EIO; - + fe->ops.i2c_gate_ctrl(fe, 0); return 0; } @@ -367,6 +361,22 @@ static int philips_td1316_tuner_set_para return philips_tda6651_pll_set(0x61, fe, params); } +static int philips_europa_tuner_init(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + static u8 msg[] = { 0x00, 0x40}; + struct i2c_msg init_msg = {.addr = 0x43,.flags = 0,.buf = msg,.len = sizeof(msg) }; + + + if (philips_td1316_tuner_init(fe)) + return -EIO; + msleep(1); + if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1) + return -EIO; + + return 0; +} + static int philips_europa_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; @@ -671,7 +681,7 @@ static struct tda1004x_config tda827x_li .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X, + .agc_config = TDA10046_AGC_TDA827X_GP11, .if_freq = TDA10046_FREQ_045, .request_firmware = NULL, }; @@ -812,32 +822,40 @@ static int philips_tda827xa_tuner_sleep( if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &tuner_msg, 1); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); return 0; } /* ------------------------------------------------------------------ */ -static int philips_tiger_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int tda8290_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) { - int ret; struct saa7134_dev *dev = fe->dvb->priv; static u8 tda8290_close[] = { 0x21, 0xc0}; static u8 tda8290_open[] = { 0x21, 0x80}; struct i2c_msg tda8290_msg = {.addr = 0x4b,.flags = 0, .len = 2}; - - /* close tda8290 i2c bridge */ - tda8290_msg.buf = tda8290_close; - ret = i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1); - if (ret != 1) + if (enable) { + tda8290_msg.buf = tda8290_close; + } else { + tda8290_msg.buf = tda8290_open; + } + if (i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1) != 1) return -EIO; msleep(20); + return 0; +} + +/* ------------------------------------------------------------------ */ + +static int philips_tiger_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +{ + int ret; + ret = philips_tda827xa_pll_set(0x61, fe, params); if (ret != 0) return ret; - /* open tda8290 i2c bridge */ - tda8290_msg.buf = tda8290_open; - i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1); - return ret; + return 0; } static int philips_tiger_tuner_init(struct dvb_frontend *fe) @@ -867,13 +885,80 @@ static struct tda1004x_config philips_ti .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X, + .agc_config = TDA10046_AGC_TDA827X_GP11, + .if_freq = TDA10046_FREQ_045, + .request_firmware = NULL, +}; +/* ------------------------------------------------------------------ */ + +static int cinergy_ht_tuner_init(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + static u8 data[] = { 0x3c, 0x33, 0x62}; + struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; + + if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static int cinergy_ht_tuner_sleep(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + static u8 data[] = { 0x3c, 0x33, 0x60}; + struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; + + i2c_transfer(&dev->i2c_adap, &msg, 1); + philips_tda827xa_tuner_sleep( 0x61, fe); + return 0; +} + +static struct tda1004x_config cinergy_ht_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X_GP01, .if_freq = TDA10046_FREQ_045, .request_firmware = NULL, }; /* ------------------------------------------------------------------ */ +static struct tda1004x_config pinnacle_pctv_310i_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X_GP11, + .if_freq = TDA10046_FREQ_045, + .request_firmware = philips_tda1004x_request_firmware, +}; + +/* ------------------------------------------------------------------ */ + +static struct tda1004x_config hauppauge_hvr_1110_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X_GP11, + .if_freq = TDA10046_FREQ_045, + .request_firmware = philips_tda1004x_request_firmware, +}; + +/* ------------------------------------------------------------------ */ + +static struct tda1004x_config asus_p7131_dual_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X_GP11, + .if_freq = TDA10046_FREQ_045, + .request_firmware = philips_tda1004x_request_firmware, +}; + static int asus_p7131_dual_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; @@ -921,7 +1006,7 @@ static struct tda1004x_config lifeview_t .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GPL, + .agc_config = TDA10046_AGC_TDA827X_GP00, .if_freq = TDA10046_FREQ_045, .request_firmware = NULL, }; @@ -958,7 +1043,7 @@ static struct tda1004x_config ads_tech_d .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GPL, + .agc_config = TDA10046_AGC_TDA827X_GP00, .if_freq = TDA10046_FREQ_045, .request_firmware = NULL, }; @@ -983,7 +1068,7 @@ static struct tda1004x_config tevion_dvb .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X, + .agc_config = TDA10046_AGC_TDA827X_GP11, .if_freq = TDA10046_FREQ_045, .request_firmware = NULL, }; @@ -1028,7 +1113,7 @@ static struct tda1004x_config md8800_dvb .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X, + .agc_config = TDA10046_AGC_TDA827X_GP11, .if_freq = TDA10046_FREQ_045, .request_firmware = NULL, }; @@ -1168,6 +1253,29 @@ static int dvb_init(struct saa7134_dev * &philips_tiger_config, &dev->i2c_adap); if (dev->dvb.frontend) { + dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; + dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init; + dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep; + dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; + } + break; + case SAA7134_BOARD_PINNACLE_PCTV_310i: + dev->dvb.frontend = dvb_attach(tda10046_attach, + &pinnacle_pctv_310i_config, + &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; + dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init; + dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep; + dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; + } + break; + case SAA7134_BOARD_HAUPPAUGE_HVR1110: + dev->dvb.frontend = dvb_attach(tda10046_attach, + &hauppauge_hvr_1110_config, + &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init; dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep; dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; @@ -1175,9 +1283,10 @@ static int dvb_init(struct saa7134_dev * break; case SAA7134_BOARD_ASUSTeK_P7131_DUAL: dev->dvb.frontend = dvb_attach(tda10046_attach, - &philips_tiger_config, + &asus_p7131_dual_config, &dev->i2c_adap); if (dev->dvb.frontend) { + dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; dev->dvb.frontend->ops.tuner_ops.init = asus_p7131_dual_tuner_init; dev->dvb.frontend->ops.tuner_ops.sleep = asus_p7131_dual_tuner_sleep; dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; @@ -1194,12 +1303,27 @@ static int dvb_init(struct saa7134_dev * } break; case SAA7134_BOARD_FLYDVB_TRIO: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &lifeview_trio_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.sleep = lifeview_trio_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = lifeview_trio_tuner_set_params; + if(! use_frontent) { //terrestrial + dev->dvb.frontend = dvb_attach(tda10046_attach, + &lifeview_trio_config, + &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops.tuner_ops.sleep = lifeview_trio_tuner_sleep; + dev->dvb.frontend->ops.tuner_ops.set_params = + lifeview_trio_tuner_set_params; + } + } else { //satellite + dev->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap); + if (dev->dvb.frontend) { + if (dvb_attach(tda826x_attach, dev->dvb.frontend, 0x63, + &dev->i2c_adap, 0) == NULL) { + printk("%s: Lifeview Trio, No tda826x found!\n", __FUNCTION__); + } + if (dvb_attach(isl6421_attach, dev->dvb.frontend, &dev->i2c_adap, + 0x08, 0, 0) == NULL) { + printk("%s: Lifeview Trio, No ISL6421 found!\n", __FUNCTION__); + } + } } break; case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: @@ -1281,7 +1405,27 @@ static int dvb_init(struct saa7134_dev * dev->dvb.frontend->ops.tuner_ops.set_params = philips_fmd1216_tuner_set_params; } break; + case SAA7134_BOARD_VIDEOMATE_DVBT_200A: + dev->dvb.frontend = dvb_attach(tda10046_attach, + &philips_europa_config, + &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops.tuner_ops.init = philips_td1316_tuner_init; + dev->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params; + } + break; + case SAA7134_BOARD_CINERGY_HT_PCMCIA: + dev->dvb.frontend = dvb_attach(tda10046_attach, + &cinergy_ht_config, + &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; + dev->dvb.frontend->ops.tuner_ops.init = cinergy_ht_tuner_init; + dev->dvb.frontend->ops.tuner_ops.sleep = cinergy_ht_tuner_sleep; + dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; + } + break; default: printk("%s: Huh? unknown DVB card?\n",dev->name); break; diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index dee8355..5d2bf15 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -131,6 +131,23 @@ static void saa7134_input_timer(unsigned mod_timer(&ir->timer, timeout); } +static void saa7134_ir_start(struct saa7134_dev *dev, struct saa7134_ir *ir) +{ + if (ir->polling) { + init_timer(&ir->timer); + ir->timer.function = saa7134_input_timer; + ir->timer.data = (unsigned long)dev; + ir->timer.expires = jiffies + HZ; + add_timer(&ir->timer); + } +} + +static void saa7134_ir_stop(struct saa7134_dev *dev) +{ + if (dev->remote->polling) + del_timer_sync(&dev->remote->timer); +} + int saa7134_input_init1(struct saa7134_dev *dev) { struct saa7134_ir *ir; @@ -141,6 +158,7 @@ int saa7134_input_init1(struct saa7134_d u32 mask_keyup = 0; int polling = 0; int ir_type = IR_TYPE_OTHER; + int err; if (dev->has_remote != SAA7134_REMOTE_GPIO) return -ENODEV; @@ -184,6 +202,7 @@ int saa7134_input_init1(struct saa7134_d case SAA7134_BOARD_AVERMEDIA_307: case SAA7134_BOARD_AVERMEDIA_STUDIO_305: case SAA7134_BOARD_AVERMEDIA_STUDIO_307: + case SAA7134_BOARD_AVERMEDIA_STUDIO_507: case SAA7134_BOARD_AVERMEDIA_GO_007_FM: ir_codes = ir_codes_avermedia; mask_keycode = 0x0007C8; @@ -266,9 +285,8 @@ int saa7134_input_init1(struct saa7134_d ir = kzalloc(sizeof(*ir), GFP_KERNEL); input_dev = input_allocate_device(); if (!ir || !input_dev) { - kfree(ir); - input_free_device(input_dev); - return -ENOMEM; + err = -ENOMEM; + goto err_out_free; } ir->dev = input_dev; @@ -299,18 +317,22 @@ int saa7134_input_init1(struct saa7134_d } input_dev->cdev.dev = &dev->pci->dev; - /* all done */ dev->remote = ir; - if (ir->polling) { - init_timer(&ir->timer); - ir->timer.function = saa7134_input_timer; - ir->timer.data = (unsigned long)dev; - ir->timer.expires = jiffies + HZ; - add_timer(&ir->timer); - } + saa7134_ir_start(dev, ir); + + err = input_register_device(ir->dev); + if (err) + goto err_out_stop; - input_register_device(ir->dev); return 0; + + err_out_stop: + saa7134_ir_stop(dev); + dev->remote = NULL; + err_out_free: + input_free_device(input_dev); + kfree(ir); + return err; } void saa7134_input_fini(struct saa7134_dev *dev) @@ -318,8 +340,7 @@ void saa7134_input_fini(struct saa7134_d if (NULL == dev->remote) return; - if (dev->remote->polling) - del_timer_sync(&dev->remote->timer); + saa7134_ir_stop(dev); input_unregister_device(dev->remote->dev); kfree(dev->remote); dev->remote = NULL; @@ -335,6 +356,7 @@ void saa7134_set_i2c_ir(struct saa7134_d switch (dev->board) { case SAA7134_BOARD_PINNACLE_PCTV_110i: + case SAA7134_BOARD_PINNACLE_PCTV_310i: snprintf(ir->c.name, sizeof(ir->c.name), "Pinnacle PCTV"); if (pinnacle_remote == 0) { ir->get_key = get_key_pinnacle_color; diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 7cf96b4..e88ad7b 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -227,6 +227,11 @@ #define SAA7134_BOARD_FLYDVBS_LR300 97 #define SAA7134_BOARD_PROTEUS_2309 98 #define SAA7134_BOARD_AVERMEDIA_A16AR 99 #define SAA7134_BOARD_ASUS_EUROPA2_HYBRID 100 +#define SAA7134_BOARD_PINNACLE_PCTV_310i 101 +#define SAA7134_BOARD_AVERMEDIA_STUDIO_507 102 +#define SAA7134_BOARD_VIDEOMATE_DVBT_200A 103 +#define SAA7134_BOARD_HAUPPAUGE_HVR1110 104 +#define SAA7134_BOARD_CINERGY_HT_PCMCIA 105 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 @@ -446,6 +451,9 @@ #ifdef VIDIOC_G_PRIORITY struct v4l2_prio_state prio; #endif + /* insmod option/autodetected */ + int autodetected; + /* various device info */ unsigned int resources; struct video_device *video_dev; diff --git a/drivers/media/video/stv680.c b/drivers/media/video/stv680.c index 6d1ef1e..a1ec3ac 100644 --- a/drivers/media/video/stv680.c +++ b/drivers/media/video/stv680.c @@ -687,7 +687,7 @@ static int stv680_start_stream (struct u stv680->sbuf[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL); if (stv680->sbuf[i].data == NULL) { PDEBUG (0, "STV(e): Could not kmalloc raw data buffer %i", i); - return -1; + goto nomem_err; } } @@ -698,7 +698,7 @@ static int stv680_start_stream (struct u stv680->scratch[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL); if (stv680->scratch[i].data == NULL) { PDEBUG (0, "STV(e): Could not kmalloc raw scratch buffer %i", i); - return -1; + goto nomem_err; } stv680->scratch[i].state = BUFFER_UNUSED; } @@ -706,7 +706,7 @@ static int stv680_start_stream (struct u for (i = 0; i < STV680_NUMSBUF; i++) { urb = usb_alloc_urb (0, GFP_KERNEL); if (!urb) - return -ENOMEM; + goto nomem_err; /* sbuf is urb->transfer_buffer, later gets memcpyed to scratch */ usb_fill_bulk_urb (urb, stv680->udev, @@ -721,6 +721,21 @@ static int stv680_start_stream (struct u stv680->framecount = 0; return 0; + + nomem_err: + for (i = 0; i < STV680_NUMSCRATCH; i++) { + kfree(stv680->scratch[i].data); + stv680->scratch[i].data = NULL; + } + for (i = 0; i < STV680_NUMSBUF; i++) { + usb_kill_urb(stv680->urb[i]); + usb_free_urb(stv680->urb[i]); + stv680->urb[i] = NULL; + kfree(stv680->sbuf[i].data); + stv680->sbuf[i].data = NULL; + } + return -ENOMEM; + } static int stv680_stop_stream (struct usb_stv *stv680) diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index 6b9ef73..2624e3f 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -430,7 +430,7 @@ void tveeprom_hauppauge_analog(struct i2 tvee->has_radio = eeprom_data[i+len-1]; /* old style tag, don't know how to detect IR presence, mark as unknown. */ - tvee->has_ir = 2; + tvee->has_ir = -1; tvee->model = eeprom_data[i+8] + (eeprom_data[i+9] << 8); @@ -653,13 +653,14 @@ void tveeprom_hauppauge_analog(struct i2 STRM(decoderIC, tvee->decoder_processor), tvee->decoder_processor); } - if (tvee->has_ir == 2) + if (tvee->has_ir == -1) tveeprom_info("has %sradio\n", tvee->has_radio ? "" : "no "); else - tveeprom_info("has %sradio, has %sIR remote\n", + tveeprom_info("has %sradio, has %sIR receiver, has %sIR transmitter\n", tvee->has_radio ? "" : "no ", - tvee->has_ir ? "" : "no "); + (tvee->has_ir & 1) ? "" : "no ", + (tvee->has_ir & 2) ? "" : "no "); } EXPORT_SYMBOL(tveeprom_hauppauge_analog); diff --git a/drivers/media/video/usbvideo/quickcam_messenger.c b/drivers/media/video/usbvideo/quickcam_messenger.c index 9a26b94..5ff6857 100644 --- a/drivers/media/video/usbvideo/quickcam_messenger.c +++ b/drivers/media/video/usbvideo/quickcam_messenger.c @@ -86,6 +86,7 @@ #ifdef CONFIG_INPUT static void qcm_register_input(struct qcm *cam, struct usb_device *dev) { struct input_dev *input_dev; + int error; usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname)); strncat(cam->input_physname, "/input0", sizeof(cam->input_physname)); @@ -106,7 +107,13 @@ static void qcm_register_input(struct qc input_dev->private = cam; - input_register_device(cam->input); + error = input_register_device(cam->input); + if (error) { + warn("Failed to register camera's input device, err: %d\n", + error); + input_free_device(cam->input); + cam->input = NULL; + } } static void qcm_unregister_input(struct qcm *cam) diff --git a/drivers/media/video/v4l1-compat.c b/drivers/media/video/v4l1-compat.c index 1d899e2..8a13e59 100644 --- a/drivers/media/video/v4l1-compat.c +++ b/drivers/media/video/v4l1-compat.c @@ -350,6 +350,7 @@ v4l_compat_translate_ioctl(struct inode struct video_buffer *buffer = arg; memset(buffer, 0, sizeof(*buffer)); + memset(&fbuf2, 0, sizeof(fbuf2)); err = drv(inode, file, VIDIOC_G_FBUF, &fbuf2); if (err < 0) { @@ -616,6 +617,7 @@ v4l_compat_translate_ioctl(struct inode case VIDIOCSPICT: /* set tone controls & partial capture format */ { struct video_picture *pict = arg; + memset(&fbuf2, 0, sizeof(fbuf2)); set_v4l_control(inode, file, V4L2_CID_BRIGHTNESS, pict->brightness, drv); @@ -708,12 +710,22 @@ v4l_compat_translate_ioctl(struct inode } case VIDIOCSTUNER: /* select a tuner input */ { - err = 0; + struct video_tuner *tun = arg; + struct v4l2_tuner t; + memset(&t,0,sizeof(t)); + + t.index=tun->tuner; + + err = drv(inode, file, VIDIOC_S_INPUT, &t); + if (err < 0) + dprintk("VIDIOCSTUNER / VIDIOC_S_INPUT: %d\n",err); + break; } case VIDIOCGFREQ: /* get frequency */ { unsigned long *freq = arg; + memset(&freq2,0,sizeof(freq2)); freq2.tuner = 0; err = drv(inode, file, VIDIOC_G_FREQUENCY, &freq2); @@ -726,8 +738,8 @@ v4l_compat_translate_ioctl(struct inode case VIDIOCSFREQ: /* set frequency */ { unsigned long *freq = arg; + memset(&freq2,0,sizeof(freq2)); - freq2.tuner = 0; drv(inode, file, VIDIOC_G_FREQUENCY, &freq2); freq2.frequency = *freq; err = drv(inode, file, VIDIOC_S_FREQUENCY, &freq2); @@ -738,6 +750,7 @@ v4l_compat_translate_ioctl(struct inode case VIDIOCGAUDIO: /* get audio properties/controls */ { struct video_audio *aud = arg; + memset(&aud2,0,sizeof(aud2)); err = drv(inode, file, VIDIOC_G_AUDIO, &aud2); if (err < 0) { @@ -898,6 +911,7 @@ v4l_compat_translate_ioctl(struct inode { int *i = arg; + memset(&buf2,0,sizeof(buf2)); buf2.index = *i; buf2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; err = drv(inode, file, VIDIOC_QUERYBUF, &buf2); diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 78d28b0..752c82c 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -87,6 +87,78 @@ MODULE_LICENSE("GPL"); */ +char *v4l2_norm_to_name(v4l2_std_id id) +{ + char *name; + + switch (id) { + case V4L2_STD_PAL: + name="PAL"; break; + case V4L2_STD_PAL_BG: + name="PAL-BG"; break; + case V4L2_STD_PAL_DK: + name="PAL-DK"; break; + case V4L2_STD_PAL_B: + name="PAL-B"; break; + case V4L2_STD_PAL_B1: + name="PAL-B1"; break; + case V4L2_STD_PAL_G: + name="PAL-G"; break; + case V4L2_STD_PAL_H: + name="PAL-H"; break; + case V4L2_STD_PAL_I: + name="PAL-I"; break; + case V4L2_STD_PAL_D: + name="PAL-D"; break; + case V4L2_STD_PAL_D1: + name="PAL-D1"; break; + case V4L2_STD_PAL_K: + name="PAL-K"; break; + case V4L2_STD_PAL_M: + name="PAL-M"; break; + case V4L2_STD_PAL_N: + name="PAL-N"; break; + case V4L2_STD_PAL_Nc: + name="PAL-Nc"; break; + case V4L2_STD_PAL_60: + name="PAL-60"; break; + case V4L2_STD_NTSC: + name="NTSC"; break; + case V4L2_STD_NTSC_M: + name="NTSC-M"; break; + case V4L2_STD_NTSC_M_JP: + name="NTSC-M-JP"; break; + case V4L2_STD_NTSC_443: + name="NTSC-443"; break; + case V4L2_STD_NTSC_M_KR: + name="NTSC-M-KR"; break; + case V4L2_STD_SECAM: + name="SECAM"; break; + case V4L2_STD_SECAM_DK: + name="SECAM-DK"; break; + case V4L2_STD_SECAM_B: + name="SECAM-B"; break; + case V4L2_STD_SECAM_D: + name="SECAM-D"; break; + case V4L2_STD_SECAM_G: + name="SECAM-G"; break; + case V4L2_STD_SECAM_H: + name="SECAM-H"; break; + case V4L2_STD_SECAM_K: + name="SECAM-K"; break; + case V4L2_STD_SECAM_K1: + name="SECAM-K1"; break; + case V4L2_STD_SECAM_L: + name="SECAM-L"; break; + case V4L2_STD_SECAM_LC: + name="SECAM-LC"; break; + default: + name="Unknown"; break; + } + + return name; +} + /* Fill in the fields of a v4l2_standard structure according to the 'id' and 'transmission' parameters. Returns negative on error. */ int v4l2_video_std_construct(struct v4l2_standard *vs, @@ -184,11 +256,13 @@ char *v4l2_field_names[] = { }; char *v4l2_type_names[] = { - [V4L2_BUF_TYPE_VIDEO_CAPTURE] = "video-cap", - [V4L2_BUF_TYPE_VIDEO_OVERLAY] = "video-over", - [V4L2_BUF_TYPE_VIDEO_OUTPUT] = "video-out", - [V4L2_BUF_TYPE_VBI_CAPTURE] = "vbi-cap", - [V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out", + [V4L2_BUF_TYPE_VIDEO_CAPTURE] = "video-cap", + [V4L2_BUF_TYPE_VIDEO_OVERLAY] = "video-over", + [V4L2_BUF_TYPE_VIDEO_OUTPUT] = "video-out", + [V4L2_BUF_TYPE_VBI_CAPTURE] = "vbi-cap", + [V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out", + [V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-cap", + [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "slicec-vbi-out", }; static char *v4l2_memory_names[] = { @@ -1451,6 +1525,7 @@ u32 v4l2_ctrl_next(const u32 * const * c /* ----------------------------------------------------------------- */ +EXPORT_SYMBOL(v4l2_norm_to_name); EXPORT_SYMBOL(v4l2_video_std_construct); EXPORT_SYMBOL(v4l2_prio_init); diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index d424a41..54de044 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -428,6 +428,10 @@ static int __video_do_ioctl(struct inode v4l_print_ioctl(vfd->name, cmd); } + if (_IOC_TYPE(cmd)=='v') + return v4l_compat_translate_ioctl(inode,file,cmd,arg, + __video_do_ioctl); + switch(cmd) { /* --- capabilities ------------------------------------------ */ case VIDIOC_QUERYCAP: @@ -829,20 +833,85 @@ #endif case VIDIOC_ENUMSTD: { struct v4l2_standard *p = arg; - unsigned int index = p->index; + v4l2_std_id id = vfd->tvnorms,curr_id=0; + unsigned int index = p->index,i; - if (!vfd->tvnormsize) { - printk (KERN_WARNING "%s: no TV norms defined!\n", - vfd->name); + if (index<0) { + ret=-EINVAL; break; } - if (index<0 || index >= vfd->tvnormsize) { - ret=-EINVAL; - break; + /* Return norm array on a canonical way */ + for (i=0;i<= index && id; i++) { + if ( (id & V4L2_STD_PAL) == V4L2_STD_PAL) { + curr_id = V4L2_STD_PAL; + } else if ( (id & V4L2_STD_PAL_BG) == V4L2_STD_PAL_BG) { + curr_id = V4L2_STD_PAL_BG; + } else if ( (id & V4L2_STD_PAL_DK) == V4L2_STD_PAL_DK) { + curr_id = V4L2_STD_PAL_DK; + } else if ( (id & V4L2_STD_PAL_B) == V4L2_STD_PAL_B) { + curr_id = V4L2_STD_PAL_B; + } else if ( (id & V4L2_STD_PAL_B1) == V4L2_STD_PAL_B1) { + curr_id = V4L2_STD_PAL_B1; + } else if ( (id & V4L2_STD_PAL_G) == V4L2_STD_PAL_G) { + curr_id = V4L2_STD_PAL_G; + } else if ( (id & V4L2_STD_PAL_H) == V4L2_STD_PAL_H) { + curr_id = V4L2_STD_PAL_H; + } else if ( (id & V4L2_STD_PAL_I) == V4L2_STD_PAL_I) { + curr_id = V4L2_STD_PAL_I; + } else if ( (id & V4L2_STD_PAL_D) == V4L2_STD_PAL_D) { + curr_id = V4L2_STD_PAL_D; + } else if ( (id & V4L2_STD_PAL_D1) == V4L2_STD_PAL_D1) { + curr_id = V4L2_STD_PAL_D1; + } else if ( (id & V4L2_STD_PAL_K) == V4L2_STD_PAL_K) { + curr_id = V4L2_STD_PAL_K; + } else if ( (id & V4L2_STD_PAL_M) == V4L2_STD_PAL_M) { + curr_id = V4L2_STD_PAL_M; + } else if ( (id & V4L2_STD_PAL_N) == V4L2_STD_PAL_N) { + curr_id = V4L2_STD_PAL_N; + } else if ( (id & V4L2_STD_PAL_Nc) == V4L2_STD_PAL_Nc) { + curr_id = V4L2_STD_PAL_Nc; + } else if ( (id & V4L2_STD_PAL_60) == V4L2_STD_PAL_60) { + curr_id = V4L2_STD_PAL_60; + } else if ( (id & V4L2_STD_NTSC) == V4L2_STD_NTSC) { + curr_id = V4L2_STD_NTSC; + } else if ( (id & V4L2_STD_NTSC_M) == V4L2_STD_NTSC_M) { + curr_id = V4L2_STD_NTSC_M; + } else if ( (id & V4L2_STD_NTSC_M_JP) == V4L2_STD_NTSC_M_JP) { + curr_id = V4L2_STD_NTSC_M_JP; + } else if ( (id & V4L2_STD_NTSC_443) == V4L2_STD_NTSC_443) { + curr_id = V4L2_STD_NTSC_443; + } else if ( (id & V4L2_STD_NTSC_M_KR) == V4L2_STD_NTSC_M_KR) { + curr_id = V4L2_STD_NTSC_M_KR; + } else if ( (id & V4L2_STD_SECAM) == V4L2_STD_SECAM) { + curr_id = V4L2_STD_SECAM; + } else if ( (id & V4L2_STD_SECAM_DK) == V4L2_STD_SECAM_DK) { + curr_id = V4L2_STD_SECAM_DK; + } else if ( (id & V4L2_STD_SECAM_B) == V4L2_STD_SECAM_B) { + curr_id = V4L2_STD_SECAM_B; + } else if ( (id & V4L2_STD_SECAM_D) == V4L2_STD_SECAM_D) { + curr_id = V4L2_STD_SECAM_D; + } else if ( (id & V4L2_STD_SECAM_G) == V4L2_STD_SECAM_G) { + curr_id = V4L2_STD_SECAM_G; + } else if ( (id & V4L2_STD_SECAM_H) == V4L2_STD_SECAM_H) { + curr_id = V4L2_STD_SECAM_H; + } else if ( (id & V4L2_STD_SECAM_K) == V4L2_STD_SECAM_K) { + curr_id = V4L2_STD_SECAM_K; + } else if ( (id & V4L2_STD_SECAM_K1) == V4L2_STD_SECAM_K1) { + curr_id = V4L2_STD_SECAM_K1; + } else if ( (id & V4L2_STD_SECAM_L) == V4L2_STD_SECAM_L) { + curr_id = V4L2_STD_SECAM_L; + } else if ( (id & V4L2_STD_SECAM_LC) == V4L2_STD_SECAM_LC) { + curr_id = V4L2_STD_SECAM_LC; + } else { + break; + } + id &= ~curr_id; } - v4l2_video_std_construct(p, vfd->tvnorms[p->index].id, - vfd->tvnorms[p->index].name); + if (i<=index) + return -EINVAL; + + v4l2_video_std_construct(p, curr_id,v4l2_norm_to_name(curr_id)); p->index = index; dbgarg (cmd, "index=%d, id=%Ld, name=%s, fps=%d/%d, " @@ -868,39 +937,23 @@ #endif } case VIDIOC_S_STD: { - v4l2_std_id *id = arg; - unsigned int i; - - if (!vfd->tvnormsize) { - printk (KERN_WARNING "%s: no TV norms defined!\n", - vfd->name); - break; - } + v4l2_std_id *id = arg,norm; dbgarg (cmd, "value=%Lu\n", (long long unsigned) *id); - /* First search for exact match */ - for (i = 0; i < vfd->tvnormsize; i++) - if (*id == vfd->tvnorms[i].id) - break; - /* Then for a generic video std that contains desired std */ - if (i == vfd->tvnormsize) - for (i = 0; i < vfd->tvnormsize; i++) - if (*id & vfd->tvnorms[i].id) - break; - if (i == vfd->tvnormsize) { + norm = (*id) & vfd->tvnorms; + if ( vfd->tvnorms && !norm) /* Check if std is supported */ break; - } /* Calls the specific handler */ if (vfd->vidioc_s_std) - ret=vfd->vidioc_s_std(file, fh, i); + ret=vfd->vidioc_s_std(file, fh, &norm); else ret=-EINVAL; /* Updates standard information */ - if (!ret) - vfd->current_norm=*id; + if (ret>=0) + vfd->current_norm=norm; break; } @@ -1088,9 +1141,13 @@ #endif case VIDIOC_G_AUDIO: { struct v4l2_audio *p=arg; + __u32 index=p->index; if (!vfd->vidioc_g_audio) break; + + memset(p,0,sizeof(*p)); + p->index=index; dbgarg(cmd, "Get for index=%d\n", p->index); ret=vfd->vidioc_g_audio(file, fh, p); if (!ret) @@ -1288,25 +1345,12 @@ #endif ret=vfd->vidioc_g_parm(file, fh, p); } else { struct v4l2_standard s; - int i; - - if (!vfd->tvnormsize) { - printk (KERN_WARNING "%s: no TV norms defined!\n", - vfd->name); - break; - } if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - for (i = 0; i < vfd->tvnormsize; i++) - if (vfd->tvnorms[i].id == vfd->current_norm) - break; - if (i >= vfd->tvnormsize) - return -EINVAL; - v4l2_video_std_construct(&s, vfd->current_norm, - vfd->tvnorms[i].name); + v4l2_norm_to_name(vfd->current_norm)); memset(p,0,sizeof(*p)); @@ -1329,8 +1373,14 @@ #endif case VIDIOC_G_TUNER: { struct v4l2_tuner *p=arg; + __u32 index=p->index; + if (!vfd->vidioc_g_tuner) break; + + memset(p,0,sizeof(*p)); + p->index=index; + ret=vfd->vidioc_g_tuner(file, fh, p); if (!ret) dbgarg (cmd, "index=%d, name=%s, type=%d, " @@ -1363,6 +1413,9 @@ #endif struct v4l2_frequency *p=arg; if (!vfd->vidioc_g_frequency) break; + + memset(p,0,sizeof(*p)); + ret=vfd->vidioc_g_frequency(file, fh, p); if (!ret) dbgarg (cmd, "tuner=%d, type=%d, frequency=%d\n", @@ -1396,12 +1449,7 @@ #endif ret=vfd->vidioc_log_status(file, fh); break; } - - /* --- Others --------------------------------------------- */ - - default: - ret=v4l_compat_translate_ioctl(inode,file,cmd,arg,__video_do_ioctl); - } + } /* switch */ if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { if (ret<0) { diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 3c8dc72..5789a10 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -1043,16 +1043,8 @@ static int vidioc_streamoff(struct file return (0); } -static struct v4l2_tvnorm tvnorms[] = { - { - .name = "NTSC-M", - .id = V4L2_STD_NTSC_M, - } -}; - -static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id a) +static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *i) { - return 0; } @@ -1332,8 +1324,8 @@ static struct video_device vivi = { #ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = vidiocgmbuf, #endif - .tvnorms = tvnorms, - .tvnormsize = ARRAY_SIZE(tvnorms), + .tvnorms = V4L2_STD_NTSC_M, + .current_norm = V4L2_STD_NTSC_M, }; /* ----------------------------------------------------------------- Initialization and module stuff @@ -1360,8 +1352,6 @@ static int __init vivi_init(void) dev->vidq.timeout.data = (unsigned long)dev; init_timer(&dev->vidq.timeout); - vivi.current_norm = tvnorms[0].id; - ret = video_register_device(&vivi, VFL_TYPE_GRABBER, video_nr); printk(KERN_INFO "Video Technology Magazine Virtual Video Capture Board (Load status: %d)\n", ret); return ret; diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h index 0a8f750..0f4cf34 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h @@ -159,6 +159,7 @@ #define I2C_DRIVERID_LM90 1042 #define I2C_DRIVERID_ASB100 1043 #define I2C_DRIVERID_FSCHER 1046 #define I2C_DRIVERID_W83L785TS 1047 +#define I2C_DRIVERID_OV7670 1048 /* Omnivision 7670 camera */ /* * ---- Adapter types ---------------------------------------------------- @@ -250,6 +251,7 @@ #define I2C_HW_SMBUS_OV511 0x04000e /* O #define I2C_HW_SMBUS_OV518 0x04000f /* OV518(+) USB 1.1 webcam ICs */ #define I2C_HW_SMBUS_OV519 0x040010 /* OV519 USB 1.1 webcam IC */ #define I2C_HW_SMBUS_OVFX2 0x040011 /* Cypress/OmniVision FX2 webcam */ +#define I2C_HW_SMBUS_CAFE 0x040012 /* Marvell 88ALP01 "CAFE" cam */ /* --- ISA pseudo-adapter */ #define I2C_HW_ISA 0x050000 diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index df5c465..5cb380a 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -244,6 +244,7 @@ #define V4L2_PIX_FMT_YUV420 v4l2_fourcc #define V4L2_PIX_FMT_YYUV v4l2_fourcc('Y','Y','U','V') /* 16 YUV 4:2:2 */ #define V4L2_PIX_FMT_HI240 v4l2_fourcc('H','I','2','4') /* 8 8-bit color */ #define V4L2_PIX_FMT_HM12 v4l2_fourcc('H','M','1','2') /* 8 YUV 4:2:0 16x16 macroblocks */ +#define V4L2_PIX_FMT_RGB444 v4l2_fourcc('R','4','4','4') /* 16 xxxxrrrr ggggbbbb */ /* see http://www.siliconimaging.com/RGB%20Bayer.htm */ #define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B','A','8','1') /* 8 BGBG.. GRGR.. */ diff --git a/include/media/tveeprom.h b/include/media/tveeprom.h index e9fc1a7..5660ea2 100644 --- a/include/media/tveeprom.h +++ b/include/media/tveeprom.h @@ -3,7 +3,7 @@ struct tveeprom { u32 has_radio; - u32 has_ir; /* 0: no IR, 1: IR present, 2: unknown */ + u32 has_ir; /* bit 0: IR receiver present, bit 1: IR transmitter (blaster) present. -1 == unknown */ u32 has_MAC_address; /* 0: no MAC, 1: MAC present, 2: unknown */ u32 tuner_type; diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index aecc946..91b1992 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -144,6 +144,9 @@ enum v4l2_chip_ident { V4L2_IDENT_CX25841 = 241, V4L2_IDENT_CX25842 = 242, V4L2_IDENT_CX25843 = 243, + + /* OmniVision sensors - range 250-299 */ + V4L2_IDENT_OV7670 = 250, }; /* audio ioctls */ @@ -251,4 +254,8 @@ struct v4l2_crystal_freq { If the frequency is not supported, then -EINVAL is returned. */ #define VIDIOC_INT_S_CRYSTAL_FREQ _IOW ('d', 113, struct v4l2_crystal_freq) +/* Initialize the sensor registors to some sort of reasonable + default values. */ +#define VIDIOC_INT_INIT _IOW ('d', 114, u32) + #endif /* V4L2_COMMON_H_ */ diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 6a11d77..fb96472 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -43,6 +43,7 @@ #define VFL_TYPE_VTX 3 /* Video standard functions */ extern unsigned int v4l2_video_std_fps(struct v4l2_standard *vs); +extern char *v4l2_norm_to_name(v4l2_std_id id); extern int v4l2_video_std_construct(struct v4l2_standard *vs, int id, char *name); @@ -81,12 +82,6 @@ extern long v4l_compat_ioctl32(struct fi * This version moves redundant code from video device code to * the common handler */ -struct v4l2_tvnorm { - char *name; - v4l2_std_id id; - - void *priv_data; -}; struct video_device { @@ -104,9 +99,8 @@ struct video_device int debug; /* Activates debug level*/ /* Video standard vars */ - int tvnormsize; /* Size of tvnorm array */ - v4l2_std_id current_norm; /* Current tvnorm */ - struct v4l2_tvnorm *tvnorms; + v4l2_std_id tvnorms; /* Supported tv norms */ + v4l2_std_id current_norm; /* Current tvnorm */ /* callbacks */ void (*release)(struct video_device *vfd); @@ -211,7 +205,7 @@ #endif /* Standard handling G_STD and ENUMSTD are handled by videodev.c */ - int (*vidioc_s_std) (struct file *file, void *fh, v4l2_std_id a); + int (*vidioc_s_std) (struct file *file, void *fh, v4l2_std_id *norm); int (*vidioc_querystd) (struct file *file, void *fh, v4l2_std_id *a); /* Input handling */