Explorar o código

Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/mchehab/v4l-dvb

* 'master' of master.kernel.org:/pub/scm/linux/kernel/git/mchehab/v4l-dvb: (132 commits)
  V4L/DVB 4949b: Fix container_of pointer retreival
  V4L/DVB (4949a): Fix INIT_WORK
  V4L/DVB (4949): Cxusb: codingstyle cleanups
  V4L/DVB (4948): Cxusb: Convert tuner functions to use dvb_pll_attach
  V4L/DVB (4947): Cx88: trivial cleanups
  V4L/DVB (4946): Cx88: Move cx88_dvb_bus_ctrl out of the card-specific area
  V4L/DVB (4945): Cx88: consolidate cx22702_config structs
  V4L/DVB (4944): Cx88: Convert DViCO FusionHDTV Hybrid to use dvb_pll_attach
  V4L/DVB (4943): Cx88: cleanup dvb_pll_attach for lgdt3302 tuners
  V4L/DVB (4953): Usbvision minor fixes
  V4L/DVB (4951): Add version.h, since it is required for VIDIOC_QUERYCAP
  V4L/DVB (4940): Or51211: Changed SNR and signal strength calculations
  V4L/DVB (4939): Or51132: Changed SNR and signal strength reporting
  V4L/DVB (4938): Cx88: Convert lgdt3302 tuning function to use dvb_pll_attach
  V4L/DVB (4941): Remove LINUX_VERSION_CODE and fix identations
  V4L/DVB (4942): Whitespace cleanups
  V4L/DVB (4937): Usbvision cleanup and code reorganization
  V4L/DVB (4936): Make MT4049FM5 tuner to set FM Gain to Normal
  V4L/DVB (4935): Added the capability of selecting fm gain by tuner
  V4L/DVB (4934): Usbvision radio requires GainNormal at e register
  ...
Linus Torvalds %!s(int64=19) %!d(string=hai) anos
pai
achega
bb7320d1d9
Modificáronse 100 ficheiros con 9502 adicións e 1671 borrados
  1. 2 2
      Documentation/dvb/cards.txt
  2. 1 1
      Documentation/video4linux/CARDLIST.cx88
  3. 6 1
      Documentation/video4linux/CARDLIST.saa7134
  4. 54 0
      Documentation/video4linux/cafe_ccic
  5. 0 162
      Documentation/video4linux/zr36120.txt
  6. 2 0
      drivers/media/Kconfig
  7. 55 0
      drivers/media/common/ir-keymaps.c
  8. 12 4
      drivers/media/common/saa7146_i2c.c
  9. 1 0
      drivers/media/dvb/b2c2/Kconfig
  10. 2 8
      drivers/media/dvb/b2c2/flexcop-fe-tuner.c
  11. 1 1
      drivers/media/dvb/bt8xx/Kconfig
  12. 1 8
      drivers/media/dvb/bt8xx/dvb-bt8xx.c
  13. 1 1
      drivers/media/dvb/bt8xx/dvb-bt8xx.h
  14. 11 2
      drivers/media/dvb/cinergyT2/cinergyT2.c
  15. 14 0
      drivers/media/dvb/dvb-usb/Kconfig
  16. 3 0
      drivers/media/dvb/dvb-usb/Makefile
  17. 18 18
      drivers/media/dvb/dvb-usb/a800.c
  18. 135 136
      drivers/media/dvb/dvb-usb/cxusb.c
  19. 5 0
      drivers/media/dvb/dvb-usb/dib0700.h
  20. 36 4
      drivers/media/dvb/dvb-usb/dib0700_core.c
  21. 183 17
      drivers/media/dvb/dvb-usb/dib0700_devices.c
  22. 57 56
      drivers/media/dvb/dvb-usb/dibusb-mb.c
  23. 13 13
      drivers/media/dvb/dvb-usb/dibusb-mc.c
  24. 11 11
      drivers/media/dvb/dvb-usb/digitv.c
  25. 12 12
      drivers/media/dvb/dvb-usb/dtt200u.c
  26. 11 3
      drivers/media/dvb/dvb-usb/dvb-usb-ids.h
  27. 23 14
      drivers/media/dvb/dvb-usb/dvb-usb-remote.c
  28. 11 11
      drivers/media/dvb/dvb-usb/gp8psk.c
  29. 17 17
      drivers/media/dvb/dvb-usb/nova-t-usb2.c
  30. 270 0
      drivers/media/dvb/dvb-usb/ttusb2.c
  31. 70 0
      drivers/media/dvb/dvb-usb/ttusb2.h
  32. 12 12
      drivers/media/dvb/dvb-usb/umt-010.c
  33. 10 10
      drivers/media/dvb/dvb-usb/vp702x.c
  34. 29 11
      drivers/media/dvb/dvb-usb/vp7045.c
  35. 24 0
      drivers/media/dvb/frontends/Kconfig
  36. 3 0
      drivers/media/dvb/frontends/Makefile
  37. 1 6
      drivers/media/dvb/frontends/dib3000mc.c
  38. 1191 0
      drivers/media/dvb/frontends/dib7000m.c
  39. 51 0
      drivers/media/dvb/frontends/dib7000m.h
  40. 1019 0
      drivers/media/dvb/frontends/dib7000p.c
  41. 46 0
      drivers/media/dvb/frontends/dib7000p.h
  42. 12 1
      drivers/media/dvb/frontends/dibx000_common.h
  43. 33 34
      drivers/media/dvb/frontends/dvb-pll.c
  44. 5 2
      drivers/media/dvb/frontends/dvb-pll.h
  45. 0 64
      drivers/media/dvb/frontends/lg_h06xf.h
  46. 126 131
      drivers/media/dvb/frontends/lgdt330x.c
  47. 10 5
      drivers/media/dvb/frontends/lgdt330x_priv.h
  48. 134 0
      drivers/media/dvb/frontends/lgh06xf.c
  49. 35 0
      drivers/media/dvb/frontends/lgh06xf.h
  50. 91 85
      drivers/media/dvb/frontends/or51132.c
  51. 50 74
      drivers/media/dvb/frontends/or51211.c
  52. 8 2
      drivers/media/dvb/frontends/tda1004x.c
  53. 3 2
      drivers/media/dvb/frontends/tda1004x.h
  54. 30 0
      drivers/media/dvb/frontends/tda8083.c
  55. 7 5
      drivers/media/dvb/frontends/tda826x.c
  56. 1 2
      drivers/media/dvb/frontends/tua6100.c
  57. 1 0
      drivers/media/dvb/ttpci/Kconfig
  58. 1 1
      drivers/media/dvb/ttpci/av7110.c
  59. 17 8
      drivers/media/dvb/ttpci/av7110_ir.c
  60. 21 5
      drivers/media/dvb/ttpci/budget-av.c
  61. 197 137
      drivers/media/dvb/ttpci/budget-ci.c
  62. 1 1
      drivers/media/dvb/ttpci/budget.c
  63. 9 2
      drivers/media/dvb/ttusb-dec/ttusb_dec.c
  64. 19 12
      drivers/media/video/Kconfig
  65. 4 2
      drivers/media/video/Makefile
  66. 1 1
      drivers/media/video/bt8xx/bttv-driver.c
  67. 57 44
      drivers/media/video/bt8xx/bttv-input.c
  68. 160 0
      drivers/media/video/cafe_ccic-regs.h
  69. 2228 0
      drivers/media/video/cafe_ccic.c
  70. 1 0
      drivers/media/video/cx88/Kconfig
  71. 110 69
      drivers/media/video/cx88/cx88-blackbird.c
  72. 54 32
      drivers/media/video/cx88/cx88-cards.c
  73. 132 201
      drivers/media/video/cx88/cx88-dvb.c
  74. 47 30
      drivers/media/video/cx88/cx88-input.c
  75. 331 17
      drivers/media/video/cx88/cx88-mpeg.c
  76. 0 13
      drivers/media/video/cx88/cx88-tvaudio.c
  77. 32 0
      drivers/media/video/cx88/cx88-video.c
  78. 47 0
      drivers/media/video/cx88/cx88.h
  79. 28 18
      drivers/media/video/ir-kbd-i2c.c
  80. 4 4
      drivers/media/video/mxb.c
  81. 1333 0
      drivers/media/video/ov7670.c
  82. 16 0
      drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
  83. 0 26
      drivers/media/video/pvrusb2/pvrusb2-hdw.c
  84. 77 4
      drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
  85. 9 9
      drivers/media/video/saa7115.c
  86. 54 9
      drivers/media/video/saa7134/saa7134-alsa.c
  87. 222 0
      drivers/media/video/saa7134/saa7134-cards.c
  88. 6 5
      drivers/media/video/saa7134/saa7134-core.c
  89. 183 39
      drivers/media/video/saa7134/saa7134-dvb.c
  90. 1 0
      drivers/media/video/saa7134/saa7134-i2c.c
  91. 62 14
      drivers/media/video/saa7134/saa7134-input.c
  92. 8 0
      drivers/media/video/saa7134/saa7134.h
  93. 18 3
      drivers/media/video/stv680.c
  94. 6 0
      drivers/media/video/tda9887.c
  95. 4 0
      drivers/media/video/tuner-core.c
  96. 4 0
      drivers/media/video/tuner-simple.c
  97. 3 12
      drivers/media/video/tuner-types.c
  98. 5 4
      drivers/media/video/tveeprom.c
  99. 8 1
      drivers/media/video/usbvideo/quickcam_messenger.c
  100. 12 0
      drivers/media/video/usbvision/Kconfig

+ 2 - 2
Documentation/dvb/cards.txt

@@ -22,10 +22,10 @@ o Frontends drivers:
    - ves1x93		: Alps BSRV2 (ves1893 demodulator) and dbox2 (ves1993)
    - ves1x93		: Alps BSRV2 (ves1893 demodulator) and dbox2 (ves1993)
    - cx24110		: Conexant HM1221/HM1811 (cx24110 or cx24106 demod, cx24108 PLL)
    - cx24110		: Conexant HM1221/HM1811 (cx24110 or cx24106 demod, cx24108 PLL)
    - grundig_29504-491	: Grundig 29504-491 (Philips TDA8083 demodulator), tsa5522 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),
    - stv0299		: Alps BSRU6 (tsa5059 PLL), LG TDQB-S00x (tsa5059 PLL),
 			  LG TDQF-S001F (sl1935 PLL), Philips SU1278 (tua6100 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:
   DVB-C:
    - ves1820		: various (ves1820 demodulator, sp5659c or spXXXX PLL)
    - ves1820		: various (ves1820 demodulator, sp5659c or spXXXX PLL)
    - at76c651		: Atmel AT76c651(B) with DAT7021 PLL
    - at76c651		: Atmel AT76c651(B) with DAT7021 PLL

+ 1 - 1
Documentation/video4linux/CARDLIST.cx88

@@ -43,7 +43,7 @@
  42 -> digitalnow DNTV Live! DVB-T Pro                     [1822:0025,1822:0019]
  42 -> digitalnow DNTV Live! DVB-T Pro                     [1822:0025,1822:0019]
  43 -> KWorld/VStream XPert DVB-T with cx22702             [17de:08a1,12ab:2300]
  43 -> KWorld/VStream XPert DVB-T with cx22702             [17de:08a1,12ab:2300]
  44 -> DViCO FusionHDTV DVB-T Dual Digital                 [18ac:db50,18ac:db54]
  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]
  46 -> DViCO FusionHDTV DVB-T Hybrid                       [18ac:db40,18ac:db44]
  47 -> pcHDTV HD5500 HDTV                                  [7063:5500]
  47 -> pcHDTV HD5500 HDTV                                  [7063:5500]
  48 -> Kworld MCE 200 Deluxe                               [17de:0841]
  48 -> Kworld MCE 200 Deluxe                               [17de:0841]

+ 6 - 1
Documentation/video4linux/CARDLIST.saa7134

@@ -76,7 +76,7 @@
  75 -> AVerMedia AVerTVHD MCE A180              [1461:1044]
  75 -> AVerMedia AVerTVHD MCE A180              [1461:1044]
  76 -> SKNet MonsterTV Mobile                   [1131:4ee9]
  76 -> SKNet MonsterTV Mobile                   [1131:4ee9]
  77 -> Pinnacle PCTV 40i/50i/110i (saa7133)     [11bd:002e]
  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)
  79 -> Sedna/MuchTV PC TV Cardbus TV/Radio (ITO25 Rev:2B)
  80 -> ASUS Digimatrix TV                       [1043:0210]
  80 -> ASUS Digimatrix TV                       [1043:0210]
  81 -> Philips Tiger reference design           [1131:2018]
  81 -> Philips Tiger reference design           [1131:2018]
@@ -99,3 +99,8 @@
  98 -> Proteus Pro 2309                         [0919:2003]
  98 -> Proteus Pro 2309                         [0919:2003]
  99 -> AVerMedia TV Hybrid A16AR                [1461:2c00]
  99 -> AVerMedia TV Hybrid A16AR                [1461:2c00]
 100 -> Asus Europa2 OEM                         [1043:4860]
 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]

+ 54 - 0
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

+ 0 - 162
Documentation/video4linux/zr36120.txt

@@ -1,162 +0,0 @@
-Driver for Trust Computer Products Framegrabber, version 0.6.1
------- --- ----- -------- -------- ------------  ------- - - -
-
-- ZORAN ------------------------------------------------------
- Author: Pauline Middelink <middelin@polyware.nl>
-   Date: 18 September 1999
-Version: 0.6.1
-
-- Description ------------------------------------------------
-
-Video4Linux compatible driver for an unknown brand framegrabber
-(Sold in the Netherlands by TRUST Computer Products) and various
-other zoran zr36120 based framegrabbers.
-
-The card contains a ZR36120 Multimedia PCI Interface and a Philips
-SAA7110 Onechip Frontend videodecoder. There is also an DSP of
-which I have forgotten the number, since i will never get that thing
-to work without specs from the vendor itself.
-
-The SAA711x are capable of processing 6 different video inputs,
-CVBS1..6 and Y1+C1, Y2+C2, Y3+C3. All in 50/60Hz, NTSC, PAL or
-SECAM and delivering a YUV datastream.  On my card the input
-'CVBS-0' corresponds to channel CVBS2 and 'S-Video' to Y2+C2.
-
-I have some reports of other cards working with the mentioned
-chip sets. For a list of other working cards please have a look
-at the cards named in the tvcards struct in the beginning of
-zr36120.c
-
-After some testing, I discovered that the carddesigner messed up
-on the I2C interface. The Zoran chip includes 2 lines SDA and SCL
-which (s)he connected reversely. So we have to clock on the SDA
-and r/w data on the SCL pin. Life is fun... Each cardtype now has
-a bit which signifies if you have a card with the same deficiency.
-
-Oh, for the completeness of this story I must mention that my
-card delivers the VSYNC pulse of the SAA chip to GIRQ1, not
-GIRQ0 as some other cards have. This is also incorporated in
-the driver be clearing/setting the 'useirq1' bit in the tvcard
-description.
-
-Another problems of continuous capturing data with a Zoran chip
-is something nasty inside the chip. It effectively halves the
-fps we ought to get... Here is the scenario: capturing frames
-to memory is done in the so-called snapshot mode. In this mode
-the Zoran stops after capturing a frame worth of data and wait
-till the application set GRAB bit to indicate readiness for the
-next frame. After detecting a set bit, the chip neatly waits
-till the start of a frame, captures it and it goes back to off.
-Smart ppl will notice the problem here. Its the waiting on the
-_next_ frame each time we set the GRAB bit... Oh well, 12,5 fps
-is still plenty fast for me.
--- update 28/7/1999 --
-Don't believe a word I just said... Proof is the output
-of `streamer -t 300 -r 25 -f avi15 -o /dev/null`
-	++--+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25
-	+-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+-
-	syncer: done
-	writer: done
-(note the /dev/null is prudent here, my system is not able to
- grab /and/ write 25 fps to a file... gifts welcome :) )
-The technical reasoning follows: The zoran completed the last
-frame, the VSYNC goes low, and GRAB is cleared. The interrupt
-routine starts to work since its VSYNC driven, and again
-activates the GRAB bit. A few ms later the VSYNC (re-)rises and
-the zoran starts to work on a new and freshly broadcasted frame....
-
-For pointers I used the specs of both chips. Below are the URLs:
-  http://www.zoran.com/ftp/download/devices/pci/ZR36120/36120data.pdf
-  http://www-us.semiconductor.philips.com/acrobat/datasheets/SAA_7110_A_1.pdf
-Some alternatives for the Philips SAA 7110 datasheet are:
-  http://www.datasheetcatalog.com/datasheets_pdf/S/A/A/7/SAA7110.shtml
-  http://www.datasheetarchive.com/search.php?search=SAA7110&sType=part
-
-The documentation has very little on absolute numbers or timings
-needed for the various modes/resolutions, but there are other
-programs you can borrow those from.
-
------- Install --------------------------------------------
-Read the file called TODO.  Note its long list of limitations.
-
-Build a kernel with VIDEO4LINUX enabled. Activate the
-BT848 driver; we need this because we have need for the
-other modules (i2c and videodev) it enables.
-
-To install this software, extract it into a suitable directory.
-Examine the makefile and change anything you don't like.  Type "make".
-
-After making the modules check if you have the much needed
-/dev/video devices. If not, execute the following 4 lines:
-	mknod /dev/video c 81 0
-	mknod /dev/video1 c 81 1
-	mknod /dev/video2 c 81 2
-	mknod /dev/video3 c 81 3
-	mknod /dev/video4 c 81 4
-
-After making/checking the devices do:
-	modprobe i2c
-	modprobe videodev
-	modprobe saa7110	(optional)
-	modprobe saa7111	(optional)
-	modprobe tuner		(optional)
-	insmod zoran cardtype=<n>
-
-<n> is the cardtype of the card you have. The cardnumber can
-be found in the source of zr36120. Look for tvcards. If your
-card is not there, please try if any other card gives some
-response, and mail me if you got a working tvcard addition.
-
-PS. <TVCard editors behold!)
-    Don't forget to set video_input to the number of inputs
-    you defined in the video_mux part of the tvcard definition.
-    It's a common error to add a channel but not incrementing
-    video_input and getting angry with me/v4l/linux/linus :(
-
-You are now ready to test the framegrabber with your favorite
-video4linux compatible tool
-
------- Application ----------------------------------------
-
-This device works with all Video4Linux compatible applications,
-given the limitations in the TODO file.
-
------- API ------------------------------------------------
-
-This uses the V4L interface as of kernel release 2.1.116, and in
-fact has not been tested on any lower version.  There are a couple
-of minor differences due to the fact that the amount of data returned
-with each frame varies, and no doubt there are discrepancies due to my
-misunderstanding of the API.  I intend to convert this driver to the
-new V4L2 API when it has stabilized more.
-
------- Current state --------------------------------------
-
-The driver is capable of overlaying a video image in screen, and
-even capable of grabbing frames. It uses the BIGPHYSAREA patch
-to allocate lots of large memory blocks when tis patch is
-found in the kernel, but it doesn't need it.
-The consequence is that, when loading the driver as a module,
-the module may tell you it's out of memory, but 'free' says
-otherwise. The reason is simple; the modules wants its memory
-contiguous, not fragmented, and after a long uptime there
-probably isn't a fragment of memory large enough...
-
-The driver uses a double buffering scheme, which should really
-be an n-way buffer, depending on the size of allocated framebuffer
-and the requested grab-size/format.
-This current version also fixes a dead-lock situation during irq
-time, which really, really froze my system... :)
-
-Good luck.
-  Pauline

+ 2 - 0
drivers/media/Kconfig

@@ -67,6 +67,7 @@ source "drivers/media/common/Kconfig"
 
 
 config VIDEO_TUNER
 config VIDEO_TUNER
 	tristate
 	tristate
+	depends on I2C
 
 
 config VIDEO_BUF
 config VIDEO_BUF
 	tristate
 	tristate
@@ -82,6 +83,7 @@ config VIDEO_IR
 
 
 config VIDEO_TVEEPROM
 config VIDEO_TVEEPROM
 	tristate
 	tristate
+	depends on I2C
 
 
 config USB_DABUSB
 config USB_DABUSB
 	tristate "DABUSB driver"
 	tristate "DABUSB driver"

+ 55 - 0
drivers/media/common/ir-keymaps.c

@@ -1552,3 +1552,58 @@ IR_KEYTAB_TYPE ir_codes_norwood[IR_KEYTAB_SIZE] = {
 };
 };
 
 
 EXPORT_SYMBOL_GPL(ir_codes_norwood);
 EXPORT_SYMBOL_GPL(ir_codes_norwood);
+
+/* From reading the following remotes:
+ * Zenith Universal 7 / TV Mode 807 / VCR Mode 837
+ * Hauppauge (from NOVA-CI-s box product)
+ * This is a "middle of the road" approach, differences are noted
+ */
+IR_KEYTAB_TYPE ir_codes_budget_ci_old[IR_KEYTAB_SIZE] = {
+	[ 0x00 ] = KEY_0,
+	[ 0x01 ] = KEY_1,
+	[ 0x02 ] = KEY_2,
+	[ 0x03 ] = KEY_3,
+	[ 0x04 ] = KEY_4,
+	[ 0x05 ] = KEY_5,
+	[ 0x06 ] = KEY_6,
+	[ 0x07 ] = KEY_7,
+	[ 0x08 ] = KEY_8,
+	[ 0x09 ] = KEY_9,
+	[ 0x0a ] = KEY_ENTER,
+	[ 0x0b ] = KEY_RED,
+	[ 0x0c ] = KEY_POWER,             /* RADIO on Hauppauge */
+	[ 0x0d ] = KEY_MUTE,
+	[ 0x0f ] = KEY_A,                 /* TV on Hauppauge */
+	[ 0x10 ] = KEY_VOLUMEUP,
+	[ 0x11 ] = KEY_VOLUMEDOWN,
+	[ 0x14 ] = KEY_B,
+	[ 0x1c ] = KEY_UP,
+	[ 0x1d ] = KEY_DOWN,
+	[ 0x1e ] = KEY_OPTION,            /* RESERVED on Hauppauge */
+	[ 0x1f ] = KEY_BREAK,
+	[ 0x20 ] = KEY_CHANNELUP,
+	[ 0x21 ] = KEY_CHANNELDOWN,
+	[ 0x22 ] = KEY_PREVIOUS,          /* Prev. Ch on Zenith, SOURCE on Hauppauge */
+	[ 0x24 ] = KEY_RESTART,
+	[ 0x25 ] = KEY_OK,
+	[ 0x26 ] = KEY_CYCLEWINDOWS,      /* MINIMIZE on Hauppauge */
+	[ 0x28 ] = KEY_ENTER,             /* VCR mode on Zenith */
+	[ 0x29 ] = KEY_PAUSE,
+	[ 0x2b ] = KEY_RIGHT,
+	[ 0x2c ] = KEY_LEFT,
+	[ 0x2e ] = KEY_MENU,              /* FULL SCREEN on Hauppauge */
+	[ 0x30 ] = KEY_SLOW,
+	[ 0x31 ] = KEY_PREVIOUS,          /* VCR mode on Zenith */
+	[ 0x32 ] = KEY_REWIND,
+	[ 0x34 ] = KEY_FASTFORWARD,
+	[ 0x35 ] = KEY_PLAY,
+	[ 0x36 ] = KEY_STOP,
+	[ 0x37 ] = KEY_RECORD,
+	[ 0x38 ] = KEY_TUNER,             /* TV/VCR on Zenith */
+	[ 0x3a ] = KEY_C,
+	[ 0x3c ] = KEY_EXIT,
+	[ 0x3d ] = KEY_POWER2,
+	[ 0x3e ] = KEY_TUNER,
+};
+
+EXPORT_SYMBOL_GPL(ir_codes_budget_ci_old);

+ 12 - 4
drivers/media/common/saa7146_i2c.c

@@ -189,13 +189,21 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, u32* dword, int short_d
 		saa7146_write(dev, I2C_TRANSFER, *dword);
 		saa7146_write(dev, I2C_TRANSFER, *dword);
 
 
 		dev->i2c_op = 1;
 		dev->i2c_op = 1;
+		SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17);
 		SAA7146_IER_ENABLE(dev, MASK_16|MASK_17);
 		SAA7146_IER_ENABLE(dev, MASK_16|MASK_17);
 		saa7146_write(dev, MC2, (MASK_00 | MASK_16));
 		saa7146_write(dev, MC2, (MASK_00 | MASK_16));
 
 
-		wait_event_interruptible(dev->i2c_wq, dev->i2c_op == 0);
-		if (signal_pending (current)) {
-			/* a signal arrived */
-			return -ERESTARTSYS;
+		timeout = HZ/100 + 1; /* 10ms */
+		timeout = wait_event_interruptible_timeout(dev->i2c_wq, dev->i2c_op == 0, timeout);
+		if (timeout == -ERESTARTSYS || dev->i2c_op) {
+			SAA7146_IER_DISABLE(dev, MASK_16|MASK_17);
+			SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17);
+			if (timeout == -ERESTARTSYS)
+				/* a signal arrived */
+				return -ERESTARTSYS;
+
+			printk(KERN_WARNING "saa7146_i2c_writeout: timed out waiting for end of xfer\n");
+			return -EIO;
 		}
 		}
 		status = saa7146_read(dev, I2C_STATUS);
 		status = saa7146_read(dev, I2C_STATUS);
 	} else {
 	} else {

+ 1 - 0
drivers/media/dvb/b2c2/Kconfig

@@ -9,6 +9,7 @@ config DVB_B2C2_FLEXCOP
 	select DVB_STV0297 if !DVB_FE_CUSTOMISE
 	select DVB_STV0297 if !DVB_FE_CUSTOMISE
 	select DVB_BCM3510 if !DVB_FE_CUSTOMISE
 	select DVB_BCM3510 if !DVB_FE_CUSTOMISE
 	select DVB_LGDT330X if !DVB_FE_CUSTOMISE
 	select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+	select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE
 	help
 	help
 	  Support for the digital TV receiver chip made by B2C2 Inc. included in
 	  Support for the digital TV receiver chip made by B2C2 Inc. included in
 	  Technisats PCI cards and USB boxes.
 	  Technisats PCI cards and USB boxes.

+ 2 - 8
drivers/media/dvb/b2c2/flexcop-fe-tuner.c

@@ -14,7 +14,7 @@
 #include "stv0297.h"
 #include "stv0297.h"
 #include "mt312.h"
 #include "mt312.h"
 #include "lgdt330x.h"
 #include "lgdt330x.h"
-#include "lg_h06xf.h"
+#include "lgh06xf.h"
 #include "dvb-pll.h"
 #include "dvb-pll.h"
 
 
 /* lnb control */
 /* lnb control */
@@ -303,12 +303,6 @@ static int flexcop_fe_request_firmware(struct dvb_frontend* fe, const struct fir
 	return request_firmware(fw, name, fc->dev);
 	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 = {
 static struct lgdt330x_config air2pc_atsc_hd5000_config = {
 	.demod_address       = 0x59,
 	.demod_address       = 0x59,
 	.demod_chip          = LGDT3303,
 	.demod_chip          = LGDT3303,
@@ -533,7 +527,7 @@ int flexcop_frontend_init(struct flexcop_device *fc)
 	/* try the air atsc 3nd generation (lgdt3303) */
 	/* try the air atsc 3nd generation (lgdt3303) */
 	if ((fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, &fc->i2c_adap)) != NULL) {
 	if ((fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, &fc->i2c_adap)) != NULL) {
 		fc->dev_type          = FC_AIR_ATSC3;
 		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);
 		info("found the lgdt3303 at i2c address: 0x%02x",air2pc_atsc_hd5000_config.demod_address);
 	} else
 	} else
 	/* try the air atsc 1nd generation (bcm3510)/panasonic ct10s */
 	/* try the air atsc 1nd generation (bcm3510)/panasonic ct10s */

+ 1 - 1
drivers/media/dvb/bt8xx/Kconfig

@@ -1,13 +1,13 @@
 config DVB_BT8XX
 config DVB_BT8XX
 	tristate "BT8xx based PCI cards"
 	tristate "BT8xx based PCI cards"
 	depends on DVB_CORE && PCI && I2C && VIDEO_BT848
 	depends on DVB_CORE && PCI && I2C && VIDEO_BT848
-	select DVB_PLL
 	select DVB_MT352 if !DVB_FE_CUSTOMISE
 	select DVB_MT352 if !DVB_FE_CUSTOMISE
 	select DVB_SP887X if !DVB_FE_CUSTOMISE
 	select DVB_SP887X if !DVB_FE_CUSTOMISE
 	select DVB_NXT6000 if !DVB_FE_CUSTOMISE
 	select DVB_NXT6000 if !DVB_FE_CUSTOMISE
 	select DVB_CX24110 if !DVB_FE_CUSTOMISE
 	select DVB_CX24110 if !DVB_FE_CUSTOMISE
 	select DVB_OR51211 if !DVB_FE_CUSTOMISE
 	select DVB_OR51211 if !DVB_FE_CUSTOMISE
 	select DVB_LGDT330X 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 DVB_ZL10353 if !DVB_FE_CUSTOMISE
 	select FW_LOADER
 	select FW_LOADER
 	help
 	help

+ 1 - 8
drivers/media/dvb/bt8xx/dvb-bt8xx.c

@@ -34,7 +34,6 @@
 #include "dvb_frontend.h"
 #include "dvb_frontend.h"
 #include "dvb-bt8xx.h"
 #include "dvb-bt8xx.h"
 #include "bt878.h"
 #include "bt878.h"
-#include "dvb-pll.h"
 
 
 static int debug;
 static int debug;
 
 
@@ -568,12 +567,6 @@ static struct mt352_config digitv_alps_tded4_config = {
 	.demod_init = digitv_alps_tded4_demod_init,
 	.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 = {
 static struct lgdt330x_config tdvs_tua6034_config = {
 	.demod_address    = 0x0e,
 	.demod_address    = 0x0e,
 	.demod_chip       = LGDT3303,
 	.demod_chip       = LGDT3303,
@@ -616,7 +609,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
 		lgdt330x_reset(card);
 		lgdt330x_reset(card);
 		card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter);
 		card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter);
 		if (card->fe != NULL) {
 		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");
 			dprintk ("dvb_bt8xx: lgdt330x detected\n");
 		}
 		}
 		break;
 		break;

+ 1 - 1
drivers/media/dvb/bt8xx/dvb-bt8xx.h

@@ -37,7 +37,7 @@
 #include "cx24110.h"
 #include "cx24110.h"
 #include "or51211.h"
 #include "or51211.h"
 #include "lgdt330x.h"
 #include "lgdt330x.h"
-#include "lg_h06xf.h"
+#include "lgh06xf.h"
 #include "zl10353.h"
 #include "zl10353.h"
 
 
 struct dvb_bt8xx_card {
 struct dvb_bt8xx_card {

+ 11 - 2
drivers/media/dvb/cinergyT2/cinergyT2.c

@@ -746,6 +746,7 @@ static void cinergyt2_query_rc (struct work_struct *work)
 				dprintk(1, "rc_input_event=%d Up\n", cinergyt2->rc_input_event);
 				dprintk(1, "rc_input_event=%d Up\n", cinergyt2->rc_input_event);
 				input_report_key(cinergyt2->rc_input_dev,
 				input_report_key(cinergyt2->rc_input_dev,
 						 cinergyt2->rc_input_event, 0);
 						 cinergyt2->rc_input_event, 0);
+				input_sync(cinergyt2->rc_input_dev);
 				cinergyt2->rc_input_event = KEY_MAX;
 				cinergyt2->rc_input_event = KEY_MAX;
 			}
 			}
 			cinergyt2->rc_last_code = ~0;
 			cinergyt2->rc_last_code = ~0;
@@ -783,6 +784,7 @@ static void cinergyt2_query_rc (struct work_struct *work)
 			dprintk(1, "rc_input_event=%d\n", cinergyt2->rc_input_event);
 			dprintk(1, "rc_input_event=%d\n", cinergyt2->rc_input_event);
 			input_report_key(cinergyt2->rc_input_dev,
 			input_report_key(cinergyt2->rc_input_dev,
 					 cinergyt2->rc_input_event, 1);
 					 cinergyt2->rc_input_event, 1);
+			input_sync(cinergyt2->rc_input_dev);
 			cinergyt2->rc_last_code = rc_events[n].value;
 			cinergyt2->rc_last_code = rc_events[n].value;
 		}
 		}
 	}
 	}
@@ -798,8 +800,9 @@ static int cinergyt2_register_rc(struct cinergyt2 *cinergyt2)
 {
 {
 	struct input_dev *input_dev;
 	struct input_dev *input_dev;
 	int i;
 	int i;
+	int err;
 
 
-	cinergyt2->rc_input_dev = input_dev = input_allocate_device();
+	input_dev = input_allocate_device();
 	if (!input_dev)
 	if (!input_dev)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
@@ -817,7 +820,13 @@ static int cinergyt2_register_rc(struct cinergyt2 *cinergyt2)
 	input_dev->keycodesize = 0;
 	input_dev->keycodesize = 0;
 	input_dev->keycodemax = 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);
 	schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2);
 
 
 	return 0;
 	return 0;

+ 14 - 0
drivers/media/dvb/dvb-usb/Kconfig

@@ -69,6 +69,8 @@ config DVB_USB_DIBUSB_MC
 config DVB_USB_DIB0700
 config DVB_USB_DIB0700
 	tristate "DiBcom DiB0700 USB DVB devices (see help for supported devices)"
 	tristate "DiBcom DiB0700 USB DVB devices (see help for supported devices)"
 	depends on DVB_USB
 	depends on DVB_USB
+	select DVB_DIB7000P
+	select DVB_DIB7000M
 	select DVB_DIB3000MC
 	select DVB_DIB3000MC
 	select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
 	select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
 	help
 	help
@@ -96,6 +98,7 @@ config DVB_USB_CXUSB
 	depends on DVB_USB
 	depends on DVB_USB
 	select DVB_CX22702 if !DVB_FE_CUSTOMISE
 	select DVB_CX22702 if !DVB_FE_CUSTOMISE
 	select DVB_LGDT330X 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_MT352 if !DVB_FE_CUSTOMISE
 	select DVB_ZL10353 if !DVB_FE_CUSTOMISE
 	select DVB_ZL10353 if !DVB_FE_CUSTOMISE
 	help
 	help
@@ -157,6 +160,17 @@ config DVB_USB_NOVA_T_USB2
 	help
 	help
 	  Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver.
 	  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
 config DVB_USB_DTT200U
 	tristate "WideView WT-200U and WT-220U (pen) DVB-T USB2.0 support (Yakumo/Hama/Typhoon/Yuan)"
 	tristate "WideView WT-200U and WT-220U (pen) DVB-T USB2.0 support (Yakumo/Hama/Typhoon/Yuan)"
 	depends on DVB_USB
 	depends on DVB_USB

+ 3 - 0
drivers/media/dvb/dvb-usb/Makefile

@@ -36,6 +36,9 @@ obj-$(CONFIG_DVB_USB_DIGITV) += dvb-usb-digitv.o
 dvb-usb-cxusb-objs = cxusb.o
 dvb-usb-cxusb-objs = cxusb.o
 obj-$(CONFIG_DVB_USB_CXUSB) += dvb-usb-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
 dvb-usb-dib0700-objs = dib0700_core.o dib0700_devices.o
 obj-$(CONFIG_DVB_USB_DIB0700) += dvb-usb-dib0700.o
 obj-$(CONFIG_DVB_USB_DIB0700) += dvb-usb-dib0700.o
 
 

+ 18 - 18
drivers/media/dvb/dvb-usb/a800.c

@@ -116,24 +116,24 @@ static struct dvb_usb_device_properties a800_properties = {
 		{
 		{
 			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
 			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
 			.pid_filter_count = 32,
 			.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),
 			.size_of_priv     = sizeof(struct dibusb_state),
 		},
 		},

+ 135 - 136
drivers/media/dvb/dvb-usb/cxusb.c

@@ -14,12 +14,12 @@
  * TODO: Use the cx25840-driver for the analogue part
  * TODO: Use the cx25840-driver for the analogue part
  *
  *
  * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de)
  * 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)
  * Copyright (C) 2006 Chris Pascoe (c.pascoe@itee.uq.edu.au)
  *
  *
- *	This program is free software; you can redistribute it and/or modify it
- *	under the terms of the GNU General Public License as published by the Free
- *	Software Foundation, version 2.
+ *   This program is 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
  * see Documentation/dvb/README.dvb-usb for more information
  */
  */
@@ -27,29 +27,29 @@
 
 
 #include "cx22702.h"
 #include "cx22702.h"
 #include "lgdt330x.h"
 #include "lgdt330x.h"
-#include "lg_h06xf.h"
+#include "lgh06xf.h"
 #include "mt352.h"
 #include "mt352.h"
 #include "mt352_priv.h"
 #include "mt352_priv.h"
 #include "zl10353.h"
 #include "zl10353.h"
 
 
 /* debug */
 /* debug */
 int dvb_usb_cxusb_debug;
 int dvb_usb_cxusb_debug;
-module_param_named(debug,dvb_usb_cxusb_debug, int, 0644);
+module_param_named(debug, dvb_usb_cxusb_debug, int, 0644);
 MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
 MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
 
 
 static int cxusb_ctrl_msg(struct dvb_usb_device *d,
 static int cxusb_ctrl_msg(struct dvb_usb_device *d,
-		u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
+			  u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
 {
 {
 	int wo = (rbuf == NULL || rlen == 0); /* write-only */
 	int wo = (rbuf == NULL || rlen == 0); /* write-only */
 	u8 sndbuf[1+wlen];
 	u8 sndbuf[1+wlen];
-	memset(sndbuf,0,1+wlen);
+	memset(sndbuf, 0, 1+wlen);
 
 
 	sndbuf[0] = cmd;
 	sndbuf[0] = cmd;
-	memcpy(&sndbuf[1],wbuf,wlen);
+	memcpy(&sndbuf[1], wbuf, wlen);
 	if (wo)
 	if (wo)
-		dvb_usb_generic_write(d,sndbuf,1+wlen);
+		dvb_usb_generic_write(d, sndbuf, 1+wlen);
 	else
 	else
-		dvb_usb_generic_rw(d,sndbuf,1+wlen,rbuf,rlen,0);
+		dvb_usb_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen, 0);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -58,14 +58,14 @@ static int cxusb_ctrl_msg(struct dvb_usb_device *d,
 static void cxusb_gpio_tuner(struct dvb_usb_device *d, int onoff)
 static void cxusb_gpio_tuner(struct dvb_usb_device *d, int onoff)
 {
 {
 	struct cxusb_state *st = d->priv;
 	struct cxusb_state *st = d->priv;
-	u8 o[2],i;
+	u8 o[2], i;
 
 
 	if (st->gpio_write_state[GPIO_TUNER] == onoff)
 	if (st->gpio_write_state[GPIO_TUNER] == onoff)
 		return;
 		return;
 
 
 	o[0] = GPIO_TUNER;
 	o[0] = GPIO_TUNER;
 	o[1] = onoff;
 	o[1] = onoff;
-	cxusb_ctrl_msg(d,CMD_GPIO_WRITE,o,2,&i,1);
+	cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1);
 
 
 	if (i != 0x01)
 	if (i != 0x01)
 		deb_info("gpio_write failed.\n");
 		deb_info("gpio_write failed.\n");
@@ -74,7 +74,8 @@ static void cxusb_gpio_tuner(struct dvb_usb_device *d, int onoff)
 }
 }
 
 
 /* I2C */
 /* I2C */
-static int cxusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num)
+static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+			  int num)
 {
 {
 	struct dvb_usb_device *d = i2c_get_adapdata(adap);
 	struct dvb_usb_device *d = i2c_get_adapdata(adap);
 	int i;
 	int i;
@@ -89,12 +90,12 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num)
 
 
 		if (d->udev->descriptor.idVendor == USB_VID_MEDION)
 		if (d->udev->descriptor.idVendor == USB_VID_MEDION)
 			switch (msg[i].addr) {
 			switch (msg[i].addr) {
-				case 0x63:
-					cxusb_gpio_tuner(d,0);
-					break;
-				default:
-					cxusb_gpio_tuner(d,1);
-					break;
+			case 0x63:
+				cxusb_gpio_tuner(d, 0);
+				break;
+			default:
+				cxusb_gpio_tuner(d, 1);
+				break;
 			}
 			}
 
 
 		/* read request */
 		/* read request */
@@ -103,26 +104,27 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num)
 			obuf[0] = msg[i].len;
 			obuf[0] = msg[i].len;
 			obuf[1] = msg[i+1].len;
 			obuf[1] = msg[i+1].len;
 			obuf[2] = msg[i].addr;
 			obuf[2] = msg[i].addr;
-			memcpy(&obuf[3],msg[i].buf,msg[i].len);
+			memcpy(&obuf[3], msg[i].buf, msg[i].len);
 
 
 			if (cxusb_ctrl_msg(d, CMD_I2C_READ,
 			if (cxusb_ctrl_msg(d, CMD_I2C_READ,
-						obuf, 3+msg[i].len,
-						ibuf, 1+msg[i+1].len) < 0)
+					   obuf, 3+msg[i].len,
+					   ibuf, 1+msg[i+1].len) < 0)
 				break;
 				break;
 
 
 			if (ibuf[0] != 0x08)
 			if (ibuf[0] != 0x08)
 				deb_i2c("i2c read may have failed\n");
 				deb_i2c("i2c read may have failed\n");
 
 
-			memcpy(msg[i+1].buf,&ibuf[1],msg[i+1].len);
+			memcpy(msg[i+1].buf, &ibuf[1], msg[i+1].len);
 
 
 			i++;
 			i++;
 		} else { /* write */
 		} else { /* write */
 			u8 obuf[2+msg[i].len], ibuf;
 			u8 obuf[2+msg[i].len], ibuf;
 			obuf[0] = msg[i].addr;
 			obuf[0] = msg[i].addr;
 			obuf[1] = msg[i].len;
 			obuf[1] = msg[i].len;
-			memcpy(&obuf[2],msg[i].buf,msg[i].len);
+			memcpy(&obuf[2], msg[i].buf, msg[i].len);
 
 
-			if (cxusb_ctrl_msg(d,CMD_I2C_WRITE, obuf, 2+msg[i].len, &ibuf,1) < 0)
+			if (cxusb_ctrl_msg(d, CMD_I2C_WRITE, obuf,
+					   2+msg[i].len, &ibuf,1) < 0)
 				break;
 				break;
 			if (ibuf != 0x08)
 			if (ibuf != 0x08)
 				deb_i2c("i2c write may have failed\n");
 				deb_i2c("i2c write may have failed\n");
@@ -324,16 +326,8 @@ static int cxusb_mt352_demod_init(struct dvb_frontend* fe)
 	return 0;
 	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 = {
 static struct cx22702_config cxusb_cx22702_config = {
 	.demod_address = 0x63,
 	.demod_address = 0x63,
-
 	.output_mode = CX22702_PARALLEL_OUTPUT,
 	.output_mode = CX22702_PARALLEL_OUTPUT,
 };
 };
 
 
@@ -374,31 +368,27 @@ static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap)
 
 
 static int cxusb_dee1601_tuner_attach(struct dvb_usb_adapter *adap)
 static int cxusb_dee1601_tuner_attach(struct dvb_usb_adapter *adap)
 {
 {
-	adap->pll_addr = 0x61;
-	adap->pll_desc = &dvb_pll_thomson_dtt7579;
-	adap->fe->ops.tuner_ops.calc_regs = dvb_usb_tuner_calc_regs;
+	dvb_attach(dvb_pll_attach, adap->fe, 0x61,
+		   NULL, &dvb_pll_thomson_dtt7579);
 	return 0;
 	return 0;
 }
 }
 
 
 static int cxusb_lgz201_tuner_attach(struct dvb_usb_adapter *adap)
 static int cxusb_lgz201_tuner_attach(struct dvb_usb_adapter *adap)
 {
 {
-	adap->pll_addr = 0x61;
-	adap->pll_desc = &dvb_pll_lg_z201;
-	adap->fe->ops.tuner_ops.calc_regs = dvb_usb_tuner_calc_regs;
+	dvb_attach(dvb_pll_attach, adap->fe, 0x61, NULL, &dvb_pll_lg_z201);
 	return 0;
 	return 0;
 }
 }
 
 
 static int cxusb_dtt7579_tuner_attach(struct dvb_usb_adapter *adap)
 static int cxusb_dtt7579_tuner_attach(struct dvb_usb_adapter *adap)
 {
 {
-	adap->pll_addr = 0x60;
-	adap->pll_desc = &dvb_pll_thomson_dtt7579;
-	adap->fe->ops.tuner_ops.calc_regs = dvb_usb_tuner_calc_regs;
+	dvb_attach(dvb_pll_attach, adap->fe, 0x60,
+		   NULL, &dvb_pll_thomson_dtt7579);
 	return 0;
 	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;
 	return 0;
 }
 }
 
 
@@ -410,7 +400,8 @@ static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap)
 
 
 	cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, &b, 1);
 	cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, &b, 1);
 
 
-	if ((adap->fe = dvb_attach(cx22702_attach, &cxusb_cx22702_config, &adap->dev->i2c_adap)) != NULL)
+	if ((adap->fe = dvb_attach(cx22702_attach, &cxusb_cx22702_config,
+				   &adap->dev->i2c_adap)) != NULL)
 		return 0;
 		return 0;
 
 
 	return -EIO;
 	return -EIO;
@@ -423,7 +414,8 @@ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap)
 
 
 	cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
 	cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
 
 
-	if ((adap->fe = dvb_attach(lgdt330x_attach, &cxusb_lgdt3303_config, &adap->dev->i2c_adap)) != NULL)
+	if ((adap->fe = dvb_attach(lgdt330x_attach, &cxusb_lgdt3303_config,
+				   &adap->dev->i2c_adap)) != NULL)
 		return 0;
 		return 0;
 
 
 	return -EIO;
 	return -EIO;
@@ -437,7 +429,8 @@ static int cxusb_mt352_frontend_attach(struct dvb_usb_adapter *adap)
 
 
 	cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
 	cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
 
 
-	if ((adap->fe = dvb_attach(mt352_attach, &cxusb_mt352_config, &adap->dev->i2c_adap)) != NULL)
+	if ((adap->fe = dvb_attach(mt352_attach, &cxusb_mt352_config,
+				   &adap->dev->i2c_adap)) != NULL)
 		return 0;
 		return 0;
 
 
 	return -EIO;
 	return -EIO;
@@ -450,8 +443,11 @@ static int cxusb_dee1601_frontend_attach(struct dvb_usb_adapter *adap)
 
 
 	cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
 	cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
 
 
-	if (((adap->fe = dvb_attach(mt352_attach, &cxusb_dee1601_config, &adap->dev->i2c_adap)) != NULL) ||
-		((adap->fe = dvb_attach(zl10353_attach, &cxusb_zl10353_dee1601_config, &adap->dev->i2c_adap)) != NULL))
+	if (((adap->fe = dvb_attach(mt352_attach, &cxusb_dee1601_config,
+				    &adap->dev->i2c_adap)) != NULL) ||
+		((adap->fe = dvb_attach(zl10353_attach,
+					&cxusb_zl10353_dee1601_config,
+					&adap->dev->i2c_adap)) != NULL))
 		return 0;
 		return 0;
 
 
 	return -EIO;
 	return -EIO;
@@ -463,7 +459,8 @@ static int cxusb_dee1601_frontend_attach(struct dvb_usb_adapter *adap)
  */
  */
 
 
 #define BLUEBIRD_01_ID_OFFSET 6638
 #define BLUEBIRD_01_ID_OFFSET 6638
-static int bluebird_patch_dvico_firmware_download(struct usb_device *udev, const struct firmware *fw)
+static int bluebird_patch_dvico_firmware_download(struct usb_device *udev,
+						  const struct firmware *fw)
 {
 {
 	if (fw->size < BLUEBIRD_01_ID_OFFSET + 4)
 	if (fw->size < BLUEBIRD_01_ID_OFFSET + 4)
 		return -EINVAL;
 		return -EINVAL;
@@ -471,10 +468,12 @@ static int bluebird_patch_dvico_firmware_download(struct usb_device *udev, const
 	if (fw->data[BLUEBIRD_01_ID_OFFSET] == (USB_VID_DVICO & 0xff) &&
 	if (fw->data[BLUEBIRD_01_ID_OFFSET] == (USB_VID_DVICO & 0xff) &&
 	    fw->data[BLUEBIRD_01_ID_OFFSET + 1] == USB_VID_DVICO >> 8) {
 	    fw->data[BLUEBIRD_01_ID_OFFSET + 1] == USB_VID_DVICO >> 8) {
 
 
-		fw->data[BLUEBIRD_01_ID_OFFSET + 2] = udev->descriptor.idProduct + 1;
-		fw->data[BLUEBIRD_01_ID_OFFSET + 3] = udev->descriptor.idProduct >> 8;
+		fw->data[BLUEBIRD_01_ID_OFFSET + 2] =
+			udev->descriptor.idProduct + 1;
+		fw->data[BLUEBIRD_01_ID_OFFSET + 3] =
+			udev->descriptor.idProduct >> 8;
 
 
-		return usb_cypress_load_firmware(udev,fw,CYPRESS_FX2);
+		return usb_cypress_load_firmware(udev, fw, CYPRESS_FX2);
 	}
 	}
 
 
 	return -EINVAL;
 	return -EINVAL;
@@ -488,7 +487,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties;
 static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties;
 static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties;
 
 
 static int cxusb_probe(struct usb_interface *intf,
 static int cxusb_probe(struct usb_interface *intf,
-		const struct usb_device_id *id)
+		       const struct usb_device_id *id)
 {
 {
 	if (dvb_usb_device_init(intf,&cxusb_medion_properties,THIS_MODULE,NULL) == 0 ||
 	if (dvb_usb_device_init(intf,&cxusb_medion_properties,THIS_MODULE,NULL) == 0 ||
 		dvb_usb_device_init(intf,&cxusb_bluebird_lgh064f_properties,THIS_MODULE,NULL) == 0 ||
 		dvb_usb_device_init(intf,&cxusb_bluebird_lgh064f_properties,THIS_MODULE,NULL) == 0 ||
@@ -502,20 +501,20 @@ static int cxusb_probe(struct usb_interface *intf,
 }
 }
 
 
 static struct usb_device_id cxusb_table [] = {
 static struct usb_device_id cxusb_table [] = {
-		{ USB_DEVICE(USB_VID_MEDION, USB_PID_MEDION_MD95700) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_COLD) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_WARM) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_COLD) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_COLD) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_WARM) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_COLD) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_WARM) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD) },
-		{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM) },
-		{}		/* Terminating entry */
+	{ USB_DEVICE(USB_VID_MEDION, USB_PID_MEDION_MD95700) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_COLD) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_WARM) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_COLD) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_COLD) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_WARM) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_COLD) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_WARM) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM) },
+	{}		/* Terminating entry */
 };
 };
 MODULE_DEVICE_TABLE (usb, cxusb_table);
 MODULE_DEVICE_TABLE (usb, cxusb_table);
 
 
@@ -529,20 +528,20 @@ static struct dvb_usb_device_properties cxusb_medion_properties = {
 	.num_adapters = 1,
 	.num_adapters = 1,
 	.adapter = {
 	.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 +574,21 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = {
 	.num_adapters = 1,
 	.num_adapters = 1,
 	.adapter = {
 	.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 +626,20 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = {
 	.num_adapters = 1,
 	.num_adapters = 1,
 	.adapter = {
 	.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 = {
 			.stream = {
 				.type = USB_BULK,
 				.type = USB_BULK,
-		.count = 5,
-		.endpoint = 0x04,
-		.u = {
-			.bulk = {
-				.buffersize = 8192,
-			}
-		}
-	},
+				.count = 5,
+				.endpoint = 0x04,
+				.u = {
+					.bulk = {
+						.buffersize = 8192,
+					}
+				}
+			},
 		},
 		},
 	},
 	},
 
 
@@ -686,21 +685,21 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties = {
 	.num_adapters = 2,
 	.num_adapters = 2,
 	.adapter = {
 	.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 = {
 			.stream = {
 				.type = USB_BULK,
 				.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,
 	.power_ctrl       = cxusb_bluebird_power_ctrl,
@@ -736,21 +735,21 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = {
 	.num_adapters = 1,
 	.num_adapters = 1,
 	.adapter = {
 	.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 = {
 			.stream = {
 				.type = USB_BULK,
 				.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,
 	.power_ctrl       = cxusb_bluebird_power_ctrl,
@@ -776,7 +775,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = {
 static struct usb_driver cxusb_driver = {
 static struct usb_driver cxusb_driver = {
 	.name		= "dvb_usb_cxusb",
 	.name		= "dvb_usb_cxusb",
 	.probe		= cxusb_probe,
 	.probe		= cxusb_probe,
-	.disconnect = dvb_usb_device_exit,
+	.disconnect     = dvb_usb_device_exit,
 	.id_table	= cxusb_table,
 	.id_table	= cxusb_table,
 };
 };
 
 
@@ -802,7 +801,7 @@ module_init (cxusb_module_init);
 module_exit (cxusb_module_exit);
 module_exit (cxusb_module_exit);
 
 
 MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
 MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
-MODULE_AUTHOR("Michael Krufky <mkrufky@m1k.net>");
+MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
 MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
 MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
 MODULE_DESCRIPTION("Driver for Conexant USB2.0 hybrid reference design");
 MODULE_DESCRIPTION("Driver for Conexant USB2.0 hybrid reference design");
 MODULE_VERSION("1.0-alpha");
 MODULE_VERSION("1.0-alpha");

+ 5 - 0
drivers/media/dvb/dvb-usb/dib0700.h

@@ -24,18 +24,23 @@ extern int dvb_usb_dib0700_debug;
 #define REQUEST_I2C_WRITE    0x3
 #define REQUEST_I2C_WRITE    0x3
 #define REQUEST_POLL_RC      0x4
 #define REQUEST_POLL_RC      0x4
 #define REQUEST_JUMPRAM      0x8
 #define REQUEST_JUMPRAM      0x8
+#define REQUEST_SET_CLOCK    0xB
 #define REQUEST_SET_GPIO     0xC
 #define REQUEST_SET_GPIO     0xC
 #define REQUEST_ENABLE_VIDEO 0xF
 #define REQUEST_ENABLE_VIDEO 0xF
 	// 1 Byte: 4MSB(1 = enable streaming, 0 = disable streaming) 4LSB(Video Mode: 0 = MPEG2 188Bytes, 1 = Analog)
 	// 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: 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(     "                "           )
 	// 2 Byte: Analog mode: 4MSB(0 = 625 lines, 1 = 525 lines)    4LSB(     "                "           )
+#define REQUEST_GET_VERSION  0x15
 
 
 struct dib0700_state {
 struct dib0700_state {
 	u8 channel_state;
 	u8 channel_state;
 	u16 mt2060_if1[2];
 	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_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_download_firmware(struct usb_device *udev, const struct firmware *fw);
 extern int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff);
 extern int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff);
 extern struct i2c_algorithm dib0700_i2c_algo;
 extern struct i2c_algorithm dib0700_i2c_algo;

+ 36 - 4
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,
 int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props,
 			struct dvb_usb_device_description **desc, int *cold)
 			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);
 	deb_info("cold: %d\n", *cold);
 	return 0;
 	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)
 static int dib0700_jumpram(struct usb_device *udev, u32 address)
 {
 {
 	int ret, actlen;
 	int ret, actlen;
@@ -197,7 +229,7 @@ int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw
 		/* start the firmware */
 		/* start the firmware */
 		if ((ret = dib0700_jumpram(udev, 0x70000000)) == 0) {
 		if ((ret = dib0700_jumpram(udev, 0x70000000)) == 0) {
 			info("firmware started successfully.");
 			info("firmware started successfully.");
-			msleep(100);
+			msleep(500);
 		}
 		}
 	} else
 	} else
 		ret = -EIO;
 		ret = -EIO;

+ 183 - 17
drivers/media/dvb/dvb-usb/dib0700_devices.c

@@ -9,6 +9,8 @@
 #include "dib0700.h"
 #include "dib0700.h"
 
 
 #include "dib3000mc.h"
 #include "dib3000mc.h"
+#include "dib7000m.h"
+#include "dib7000p.h"
 #include "mt2060.h"
 #include "mt2060.h"
 
 
 static int force_lna_activation;
 static int force_lna_activation;
@@ -95,37 +97,189 @@ static int bristol_tuner_attach(struct dvb_usb_adapter *adap)
 }
 }
 
 
 /* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */
 /* 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)
 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 */
 	/* 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_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)
 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[] = {
 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) },
+		{ 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) },
 		{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500_2) },
 		{ 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_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK) },
 		{ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR) },
 		{ 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 */
 		{ }		/* Terminating entry */
 };
 };
 MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
 MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
@@ -167,20 +321,32 @@ struct dvb_usb_device_properties dib0700_devices[] = {
 			},
 			},
 		},
 		},
 
 
-		.num_device_descs = 3,
+		.num_device_descs = 6,
 		.devices = {
 		.devices = {
 			{   "DiBcom STK7700P reference design",
 			{   "DiBcom STK7700P reference design",
-				{ &dib0700_usb_id_table[0], NULL },
+				{ &dib0700_usb_id_table[0], &dib0700_usb_id_table[1] },
 				{ NULL },
 				{ NULL },
 			},
 			},
 			{   "Hauppauge Nova-T Stick",
 			{   "Hauppauge Nova-T Stick",
-				{ &dib0700_usb_id_table[3], NULL },
+				{ &dib0700_usb_id_table[4], &dib0700_usb_id_table[9], NULL },
 				{ NULL },
 				{ NULL },
 			},
 			},
 			{   "AVerMedia AVerTV DVB-T Volar",
 			{   "AVerMedia AVerTV DVB-T Volar",
-				{ &dib0700_usb_id_table[4], NULL },
+				{ &dib0700_usb_id_table[5], &dib0700_usb_id_table[10] },
 				{ NULL },
 				{ 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,
 	}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
 
 
@@ -202,7 +368,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
 		.num_device_descs = 1,
 		.num_device_descs = 1,
 		.devices = {
 		.devices = {
 			{   "Hauppauge Nova-T 500 Dual DVB-T",
 			{   "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 },
 				{ NULL },
 			},
 			},
 		}
 		}

+ 57 - 56
drivers/media/dvb/dvb-usb/dibusb-mb.c

@@ -163,23 +163,23 @@ static struct dvb_usb_device_properties dibusb1_1_properties = {
 			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
 			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
 			.pid_filter_count = 16,
 			.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 = {
 			.stream = {
 				.type = USB_BULK,
 				.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),
 			.size_of_priv     = sizeof(struct dibusb_state),
 		}
 		}
 	},
 	},
@@ -248,23 +248,23 @@ static struct dvb_usb_device_properties dibusb1_1_an2235_properties = {
 			.caps = DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF | DVB_USB_ADAP_HAS_PID_FILTER,
 			.caps = DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF | DVB_USB_ADAP_HAS_PID_FILTER,
 			.pid_filter_count = 16,
 			.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 = {
 			.stream = {
 				.type = USB_BULK,
 				.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),
 			.size_of_priv     = sizeof(struct dibusb_state),
 		},
 		},
 	},
 	},
@@ -312,22 +312,23 @@ static struct dvb_usb_device_properties dibusb2_0b_properties = {
 			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
 			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
 			.pid_filter_count = 16,
 			.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 = {
 			.stream = {
 				.type = USB_BULK,
 				.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),
 			.size_of_priv     = sizeof(struct dibusb_state),
 		}
 		}
 	},
 	},
@@ -369,22 +370,22 @@ static struct dvb_usb_device_properties artec_t1_usb2_properties = {
 			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
 			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
 			.pid_filter_count = 16,
 			.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 = {
 			.stream = {
 				.type = USB_BULK,
 				.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),
 			.size_of_priv     = sizeof(struct dibusb_state),
 		}
 		}
 	},
 	},

+ 13 - 13
drivers/media/dvb/dvb-usb/dibusb-mc.c

@@ -54,23 +54,23 @@ static struct dvb_usb_device_properties dibusb_mc_properties = {
 		{
 		{
 			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
 			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
 			.pid_filter_count = 32,
 			.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 */
 	/* parameter for the MPEG2-data transfer */
 			.stream = {
 			.stream = {
 				.type = USB_BULK,
 				.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),
 			.size_of_priv     = sizeof(struct dibusb_state),
 		}
 		}
 	},
 	},

+ 11 - 11
drivers/media/dvb/dvb-usb/digitv.c

@@ -274,20 +274,20 @@ static struct dvb_usb_device_properties digitv_properties = {
 	.num_adapters = 1,
 	.num_adapters = 1,
 	.adapter = {
 	.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 = {
 			.stream = {
 				.type = USB_BULK,
 				.type = USB_BULK,
-		.count = 7,
-		.endpoint = 0x02,
-		.u = {
-			.bulk = {
-				.buffersize = 4096,
-			}
-		}
-	},
+				.count = 7,
+				.endpoint = 0x02,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			},
 		}
 		}
 	},
 	},
 	.identify_state   = digitv_identify_state,
 	.identify_state   = digitv_identify_state,

+ 12 - 12
drivers/media/dvb/dvb-usb/dtt200u.c

@@ -268,20 +268,20 @@ static struct dvb_usb_device_properties wt220u_zl0353_properties = {
 			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING,
 			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING,
 			.pid_filter_count = 15,
 			.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 = {
 			.stream = {
 				.type = USB_BULK,
 				.type = USB_BULK,
-		.count = 7,
-		.endpoint = 0x02,
-		.u = {
-			.bulk = {
-				.buffersize = 4096,
-			}
-		}
-	},
+				.count = 7,
+				.endpoint = 0x02,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			},
 		}
 		}
 	},
 	},
 	.power_ctrl      = dtt200u_power_ctrl,
 	.power_ctrl      = dtt200u_power_ctrl,

+ 11 - 3
drivers/media/dvb/dvb-usb/dvb-usb-ids.h

@@ -33,6 +33,7 @@
 #define USB_VID_VISIONPLUS			0x13d3
 #define USB_VID_VISIONPLUS			0x13d3
 #define USB_VID_TWINHAN				0x1822
 #define USB_VID_TWINHAN				0x1822
 #define USB_VID_ULTIMA_ELECTRONIC		0x05d8
 #define USB_VID_ULTIMA_ELECTRONIC		0x05d8
+#define USB_VID_UNIWILL				0x1584
 #define USB_VID_WIDEVIEW			0x14aa
 #define USB_VID_WIDEVIEW			0x14aa
 
 
 /* Product IDs */
 /* Product IDs */
@@ -46,6 +47,7 @@
 #define USB_PID_COMPRO_DVBU2000_WARM			0xd001
 #define USB_PID_COMPRO_DVBU2000_WARM			0xd001
 #define USB_PID_COMPRO_DVBU2000_UNK_COLD		0x010c
 #define USB_PID_COMPRO_DVBU2000_UNK_COLD		0x010c
 #define USB_PID_COMPRO_DVBU2000_UNK_WARM		0x010d
 #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			0x0064
 #define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM		0x0065
 #define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM		0x0065
 #define USB_PID_DIBCOM_MOD3000_COLD			0x0bb8
 #define USB_PID_DIBCOM_MOD3000_COLD			0x0bb8
@@ -53,7 +55,9 @@
 #define USB_PID_DIBCOM_MOD3001_COLD			0x0bc6
 #define USB_PID_DIBCOM_MOD3001_COLD			0x0bc6
 #define USB_PID_DIBCOM_MOD3001_WARM			0x0bc7
 #define USB_PID_DIBCOM_MOD3001_WARM			0x0bc7
 #define USB_PID_DIBCOM_STK7700P				0x1e14
 #define USB_PID_DIBCOM_STK7700P				0x1e14
+#define USB_PID_DIBCOM_STK7700P_PC			0x1e78
 #define USB_PID_DIBCOM_ANCHOR_2135_COLD			0x2131
 #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_COLD			0x0fa0
 #define USB_PID_GRANDTEC_DVBT_USB_WARM			0x0fa1
 #define USB_PID_GRANDTEC_DVBT_USB_WARM			0x0fa1
 #define USB_PID_KWORLD_VSTREAM_COLD			0x17de
 #define USB_PID_KWORLD_VSTREAM_COLD			0x17de
@@ -97,7 +101,9 @@
 #define USB_PID_HAUPPAUGE_NOVA_T_500			0x9941
 #define USB_PID_HAUPPAUGE_NOVA_T_500			0x9941
 #define USB_PID_HAUPPAUGE_NOVA_T_500_2			0x9950
 #define USB_PID_HAUPPAUGE_NOVA_T_500_2			0x9950
 #define USB_PID_HAUPPAUGE_NOVA_T_STICK			0x7050
 #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_NEBULA_DIGITV				0x0201
 #define USB_PID_DVICO_BLUEBIRD_LGDT			0xd820
 #define USB_PID_DVICO_BLUEBIRD_LGDT			0xd820
 #define USB_PID_DVICO_BLUEBIRD_LG064F_COLD		0xd500
 #define USB_PID_DVICO_BLUEBIRD_LG064F_COLD		0xd500
@@ -110,8 +116,8 @@
 #define USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM		0xdb51
 #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_COLD		0xdb58
 #define USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM		0xdb59
 #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_MEDION_MD95700				0x0932
 #define USB_PID_KYE_DVB_T_COLD				0x701e
 #define USB_PID_KYE_DVB_T_COLD				0x701e
 #define USB_PID_KYE_DVB_T_WARM				0x701f
 #define USB_PID_KYE_DVB_T_WARM				0x701f
@@ -125,7 +131,9 @@
 #define USB_PID_GRANDTEC_DVBT_USB2_WARM			0x0bc7
 #define USB_PID_GRANDTEC_DVBT_USB2_WARM			0x0bc7
 #define USB_PID_WINFAST_DTV_DONGLE_COLD			0x6025
 #define USB_PID_WINFAST_DTV_DONGLE_COLD			0x6025
 #define USB_PID_WINFAST_DTV_DONGLE_WARM			0x6026
 #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_COLD			0x0200
 #define USB_PID_GENPIX_8PSK_WARM			0x0201
 #define USB_PID_GENPIX_8PSK_WARM			0x0201
 
 
+
 #endif
 #endif

+ 23 - 14
drivers/media/dvb/dvb-usb/dvb-usb-remote.c

@@ -90,7 +90,9 @@ schedule:
 
 
 int dvb_usb_remote_init(struct dvb_usb_device *d)
 int dvb_usb_remote_init(struct dvb_usb_device *d)
 {
 {
+	struct input_dev *input_dev;
 	int i;
 	int i;
+	int err;
 
 
 	if (d->props.rc_key_map == NULL ||
 	if (d->props.rc_key_map == NULL ||
 		d->props.rc_query == NULL ||
 		d->props.rc_query == NULL ||
@@ -100,23 +102,24 @@ int dvb_usb_remote_init(struct dvb_usb_device *d)
 	usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys));
 	usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys));
 	strlcat(d->rc_phys, "/ir0", 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;
 		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 */
 	/* set the bits for the keys */
 	deb_rc("key map size: %d\n", d->props.rc_key_map_size);
 	deb_rc("key map size: %d\n", d->props.rc_key_map_size);
 	for (i = 0; i < d->props.rc_key_map_size; i++) {
 	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. */
 	/* Start the remote-control polling. */
@@ -124,10 +127,16 @@ int dvb_usb_remote_init(struct dvb_usb_device *d)
 		d->props.rc_interval = 100; /* default */
 		d->props.rc_interval = 100; /* default */
 
 
 	/* setting these two values to non-zero, we have to manage key repeats */
 	/* 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_DELAYED_WORK(&d->rc_query_work, dvb_usb_read_remote_control);
 	INIT_DELAYED_WORK(&d->rc_query_work, dvb_usb_read_remote_control);
 
 

+ 11 - 11
drivers/media/dvb/dvb-usb/gp8psk.c

@@ -194,19 +194,19 @@ static struct dvb_usb_device_properties gp8psk_properties = {
 	.num_adapters = 1,
 	.num_adapters = 1,
 	.adapter = {
 	.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 = {
 			.stream = {
 				.type = USB_BULK,
 				.type = USB_BULK,
-		.count = 7,
-		.endpoint = 0x82,
-		.u = {
-			.bulk = {
-				.buffersize = 8192,
-			}
-		}
-	},
+				.count = 7,
+				.endpoint = 0x82,
+				.u = {
+					.bulk = {
+						.buffersize = 8192,
+					}
+				}
+			},
 		}
 		}
 	},
 	},
 	.power_ctrl       = gp8psk_power_ctrl,
 	.power_ctrl       = gp8psk_power_ctrl,

+ 17 - 17
drivers/media/dvb/dvb-usb/nova-t-usb2.c

@@ -163,23 +163,23 @@ static struct dvb_usb_device_properties nova_t_properties = {
 			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
 			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
 			.pid_filter_count = 32,
 			.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),
 			.size_of_priv     = sizeof(struct dibusb_state),
 		}
 		}

+ 270 - 0
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 <holger@convergence.de>
+ * Copyright (c) 2003 Felix Domke <tmbinc@elitedvb.net>
+ * Copyright (C) 2005-6 Patrick Boettcher <pb@linuxtv.org>
+ *
+ *	This program is free software; you can redistribute it and/or modify it
+ *	under the terms of the GNU General Public License as published by 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 <patrick.boettcher@desy.de>");
+MODULE_DESCRIPTION("Driver for Pinnacle PCTV 400e DVB-S USB2.0");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL");

+ 70 - 0
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 <holger@convergence.de>
+ * Copyright (c) 2003 Felix Domke <tmbinc@elitedvb.net>
+ * Copyright (C) 2005-6 Patrick Boettcher <pb@linuxtv.de>
+ *
+ *	This program is free software; you can redistribute it and/or modify it
+ *	under the terms of the GNU General Public License as published by the Free
+ *	Software Foundation, version 2.
+ *
+ * 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 <id> <cmdbyte> <datalen> <data...>
+ *
+ * in message (complete block is always 0x40 bytes long)
+ * 0x55 <id> <cmdbyte> <datalen> <data...>
+ *
+ * id is incremented for each transaction
+ */
+
+#define CMD_DSP_DOWNLOAD    0x13
+/* out data: <byte>[28]
+ * last block must be empty */
+
+#define CMD_DSP_BOOT        0x14
+/* out data: nothing */
+
+#define CMD_POWER           0x15
+/* out data: <on=1/off=0> */
+
+#define CMD_LNB             0x16
+/* out data: <power=1> <18V=0,13V=1> <tone> <??=1> <??=1> */
+
+#define CMD_GET_VERSION     0x17
+/* in  data: <version_byte>[5] */
+
+#define CMD_DISEQC          0x18
+/* out data: <master=0xff/burst=??> <cmdlen> <cmdbytes>[cmdlen] */
+
+#define CMD_PID_ENABLE      0x22
+/* out data: <index> <type: ts=1/sec=2> <pid msb> <pid lsb> */
+
+#define CMD_PID_DISABLE     0x23
+/* out data: <index> */
+
+#define CMD_FILTER_ENABLE   0x24
+/* out data: <index> <pid_idx> <filter>[12] <mask>[12] */
+
+#define CMD_FILTER_DISABLE  0x25
+/* out data: <index> */
+
+#define CMD_GET_DSP_VERSION 0x26
+/* in  data: <version_byte>[28] */
+
+#define CMD_I2C_XFER        0x31
+/* out data: <addr << 1> <sndlen> <rcvlen> <data>[sndlen]
+ * in  data: <addr << 1> <sndlen> <rcvlen> <data>[rcvlen] */
+
+#define CMD_I2C_BITRATE     0x32
+/* out data: <default=0> */
+
+#endif

+ 12 - 12
drivers/media/dvb/dvb-usb/umt-010.c

@@ -99,21 +99,21 @@ static struct dvb_usb_device_properties umt_properties = {
 	.num_adapters = 1,
 	.num_adapters = 1,
 	.adapter = {
 	.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 = {
 			.stream = {
 				.type = USB_BULK,
 				.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),
 			.size_of_priv     = sizeof(struct dibusb_state),
 		}
 		}

+ 10 - 10
drivers/media/dvb/dvb-usb/vp702x.c

@@ -275,22 +275,22 @@ static struct dvb_usb_device_properties vp702x_properties = {
 			.caps             = DVB_USB_ADAP_RECEIVES_204_BYTE_TS,
 			.caps             = DVB_USB_ADAP_RECEIVES_204_BYTE_TS,
 
 
 			.streaming_ctrl   = vp702x_streaming_ctrl,
 			.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 = {
 			.stream = {
 				.type = USB_BULK,
 				.type = USB_BULK,
 				.count = 10,
 				.count = 10,
-		.endpoint = 0x02,
-		.u = {
-			.bulk = {
-				.buffersize = 4096,
-			}
-		}
-	},
+				.endpoint = 0x02,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			},
 			.size_of_priv     = sizeof(struct vp702x_state),
 			.size_of_priv     = sizeof(struct vp702x_state),
 		}
 		}
-		},
+	},
 	.read_mac_address = vp702x_read_mac_addr,
 	.read_mac_address = vp702x_read_mac_addr,
 
 
 	.rc_key_map       = vp702x_rc_keys,
 	.rc_key_map       = vp702x_rc_keys,

+ 29 - 11
drivers/media/dvb/dvb-usb/vp7045.c

@@ -125,7 +125,25 @@ static struct dvb_usb_rc_key vp7045_rc_keys[] = {
 	{ 0x00, 0x00, KEY_TAB }, /* Tab */
 	{ 0x00, 0x00, KEY_TAB }, /* Tab */
 	{ 0x00, 0x48, KEY_INFO }, /* Preview */
 	{ 0x00, 0x48, KEY_INFO }, /* Preview */
 	{ 0x00, 0x04, KEY_LIST }, /* RecordList */
 	{ 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)
 static int vp7045_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
@@ -217,18 +235,18 @@ static struct dvb_usb_device_properties vp7045_properties = {
 	.num_adapters = 1,
 	.num_adapters = 1,
 	.adapter = {
 	.adapter = {
 		{
 		{
-	.frontend_attach  = vp7045_frontend_attach,
-	/* parameter for the MPEG2-data transfer */
+			.frontend_attach  = vp7045_frontend_attach,
+			/* parameter for the MPEG2-data transfer */
 			.stream = {
 			.stream = {
 				.type = USB_BULK,
 				.type = USB_BULK,
-		.count = 7,
-		.endpoint = 0x02,
-		.u = {
-			.bulk = {
-				.buffersize = 4096,
-			}
-		}
-	},
+				.count = 7,
+				.endpoint = 0x02,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			},
 		}
 		}
 	},
 	},
 	.power_ctrl       = vp7045_power_ctrl,
 	.power_ctrl       = vp7045_power_ctrl,

+ 24 - 0
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
 	  A DVB-T tuner module. Designed for mobile usage. Say Y when you want
 	  to support this frontend.
 	  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"
 comment "DVB-C (cable) frontends"
 	depends on DVB_CORE
 	depends on DVB_CORE
 
 
@@ -281,6 +297,14 @@ config DVB_TUNER_MT2060
 	help
 	help
 	  A driver for the silicon IF tuner MT2060 from Microtune.
 	  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"
 comment "Miscellaneous devices"
 	depends on DVB_CORE
 	depends on DVB_CORE
 
 

+ 3 - 0
drivers/media/dvb/frontends/Makefile

@@ -13,6 +13,8 @@ obj-$(CONFIG_DVB_TDA8083) += tda8083.o
 obj-$(CONFIG_DVB_L64781) += l64781.o
 obj-$(CONFIG_DVB_L64781) += l64781.o
 obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o
 obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o
 obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dibx000_common.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_MT312) += mt312.o
 obj-$(CONFIG_DVB_VES1820) += ves1820.o
 obj-$(CONFIG_DVB_VES1820) += ves1820.o
 obj-$(CONFIG_DVB_VES1X93) += ves1x93.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_TDA826X) += tda826x.o
 obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o
 obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o
 obj-$(CONFIG_DVB_TUA6100) += tua6100.o
 obj-$(CONFIG_DVB_TUA6100) += tua6100.o
+obj-$(CONFIG_DVB_TUNER_LGH06XF) += lgh06xf.o

+ 1 - 6
drivers/media/dvb/frontends/dib3000mc.c

@@ -511,16 +511,11 @@ static int dib3000mc_autosearch_start(struct dvb_frontend *demod, struct dibx000
 
 
 
 
 	/* a channel for autosearch */
 	/* 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.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_alpha = 1; fchan.vit_code_rate_hp = 2; fchan.vit_code_rate_lp = 2;
 	fchan.vit_hrch = 0; fchan.vit_select_hp = 1;
 	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);
 	reg = dib3000mc_read_word(state, 0);
 	dib3000mc_write_word(state, 0, reg | (1 << 8));
 	dib3000mc_write_word(state, 0, reg | (1 << 8));

+ 1191 - 0
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 <linux/kernel.h>
+#include <linux/i2c.h>
+
+#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 <pboettcher@dibcom.fr>");
+MODULE_DESCRIPTION("Driver for the DiBcom 7000MA/MB/PA/PB/MC COFDM demodulator");
+MODULE_LICENSE("GPL");

+ 51 - 0
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

+ 1019 - 0
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 <linux/kernel.h>
+#include <linux/i2c.h>
+
+#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 <pboettcher@dibcom.fr>");
+MODULE_DESCRIPTION("Driver for the DiBcom 7000PC COFDM demodulator");
+MODULE_LICENSE("GPL");

+ 46 - 0
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

+ 12 - 1
drivers/media/dvb/frontends/dibx000_common.h

@@ -32,6 +32,13 @@ extern void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst);
 #define BAND_LBAND 0x01
 #define BAND_LBAND 0x01
 #define BAND_UHF   0x02
 #define BAND_UHF   0x02
 #define BAND_VHF   0x04
 #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 {
 struct dibx000_agc_config {
 	/* defines the capabilities of this AGC-setting - using the BAND_-defines*/
 	/* defines the capabilities of this AGC-setting - using the BAND_-defines*/
@@ -129,6 +136,7 @@ enum dibx000_adc_states {
 
 
 /* I hope I can get rid of the following kludge in the near future */
 /* I hope I can get rid of the following kludge in the near future */
 struct dibx000_ofdm_channel {
 struct dibx000_ofdm_channel {
+	u32 RF_kHz;
 	u8  Bw;
 	u8  Bw;
 	s16 nfft;
 	s16 nfft;
 	s16 guard;
 	s16 guard;
@@ -138,9 +146,11 @@ struct dibx000_ofdm_channel {
 	s16 vit_alpha;
 	s16 vit_alpha;
 	s16 vit_code_rate_hp;
 	s16 vit_code_rate_hp;
 	s16 vit_code_rate_lp;
 	s16 vit_code_rate_lp;
+	u8  intlv_native;
 };
 };
 
 
 #define FEP2DIB(fep,ch) \
 #define FEP2DIB(fep,ch) \
+	(ch)->RF_kHz           = (fep)->frequency / 1000; \
 	(ch)->Bw               = (fep)->u.ofdm.bandwidth; \
 	(ch)->Bw               = (fep)->u.ofdm.bandwidth; \
 	(ch)->nfft             = (fep)->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO ? -1 : (fep)->u.ofdm.transmission_mode; \
 	(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; \
 	(ch)->guard            = (fep)->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO ? -1 : (fep)->u.ofdm.guard_interval; \
@@ -149,7 +159,8 @@ struct dibx000_ofdm_channel {
 	(ch)->vit_select_hp    = 1; \
 	(ch)->vit_select_hp    = 1; \
 	(ch)->vit_alpha        = 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_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 {\
 #define INIT_OFDM_CHANNEL(ch) do {\
 	(ch)->Bw               = 0;  \
 	(ch)->Bw               = 0;  \

+ 33 - 34
drivers/media/dvb/frontends/dvb-pll.c

@@ -472,14 +472,14 @@ int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
 		printk("pll: %s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n",
 		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]);
 		       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);
 EXPORT_SYMBOL(dvb_pll_configure);
 
 
 static int dvb_pll_release(struct dvb_frontend *fe)
 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;
 	fe->tuner_priv = NULL;
 	return 0;
 	return 0;
 }
 }
@@ -489,7 +489,8 @@ static int dvb_pll_sleep(struct dvb_frontend *fe)
 	struct dvb_pll_priv *priv = fe->tuner_priv;
 	struct dvb_pll_priv *priv = fe->tuner_priv;
 	u8 buf[4];
 	u8 buf[4];
 	struct i2c_msg msg =
 	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 i;
 	int result;
 	int result;
 
 
@@ -517,16 +518,16 @@ static int dvb_pll_sleep(struct dvb_frontend *fe)
 	return 0;
 	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;
 	struct dvb_pll_priv *priv = fe->tuner_priv;
 	u8 buf[4];
 	u8 buf[4];
 	struct i2c_msg msg =
 	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;
 	int result;
-	u32 div;
-	int i;
-	u32 bandwidth = 0;
+	u32 bandwidth = 0, frequency = 0;
 
 
 	if (priv->i2c == NULL)
 	if (priv->i2c == NULL)
 		return -EINVAL;
 		return -EINVAL;
@@ -536,8 +537,11 @@ static int dvb_pll_set_params(struct dvb_frontend *fe, struct dvb_frontend_param
 		bandwidth = params->u.ofdm.bandwidth;
 		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;
 		return result;
+	else
+		frequency = result;
 
 
 	if (fe->ops.i2c_gate_ctrl)
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 1);
 		fe->ops.i2c_gate_ctrl(fe, 1);
@@ -545,26 +549,19 @@ static int dvb_pll_set_params(struct dvb_frontend *fe, struct dvb_frontend_param
 		return result;
 		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;
 	priv->bandwidth = bandwidth;
 
 
 	return 0;
 	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;
 	struct dvb_pll_priv *priv = fe->tuner_priv;
 	int result;
 	int result;
-	u32 div;
-	int i;
-	u32 bandwidth = 0;
+	u32 bandwidth = 0, frequency = 0;
 
 
 	if (buf_len < 5)
 	if (buf_len < 5)
 		return -EINVAL;
 		return -EINVAL;
@@ -574,18 +571,15 @@ static int dvb_pll_calc_regs(struct dvb_frontend *fe, struct dvb_frontend_parame
 		bandwidth = params->u.ofdm.bandwidth;
 		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;
 		return result;
+	else
+		frequency = result;
+
 	buf[0] = priv->pll_i2c_address;
 	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;
 	priv->bandwidth = bandwidth;
 
 
 	return 5;
 	return 5;
@@ -614,10 +608,13 @@ static struct dvb_tuner_ops dvb_pll_tuner_ops = {
 	.get_bandwidth = dvb_pll_get_bandwidth,
 	.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 };
 	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;
 	struct dvb_pll_priv *priv = NULL;
 	int ret;
 	int ret;
 
 
@@ -640,7 +637,9 @@ struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struc
 	priv->i2c = i2c;
 	priv->i2c = i2c;
 	priv->pll_desc = desc;
 	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);
 	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->min;
 	fe->ops.tuner_ops.info.frequency_min = desc->max;
 	fe->ops.tuner_ops.info.frequency_min = desc->max;

+ 5 - 2
drivers/media/dvb/frontends/dvb-pll.h

@@ -48,7 +48,7 @@ extern struct dvb_pll_desc dvb_pll_philips_td1316;
 extern struct dvb_pll_desc dvb_pll_thomson_fe6600;
 extern struct dvb_pll_desc dvb_pll_thomson_fe6600;
 
 
 extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
 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.
  * Attach a dvb-pll to the supplied frontend structure.
@@ -59,6 +59,9 @@ extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
  * @param desc dvb_pll_desc to use.
  * @param desc dvb_pll_desc to use.
  * @return Frontend pointer on success, NULL on failure
  * @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
 #endif

+ 0 - 64
drivers/media/dvb/frontends/lg_h06xf.h

@@ -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

+ 126 - 131
drivers/media/dvb/frontends/lgdt330x.c

@@ -31,9 +31,6 @@
  *   Air2PC/AirStar 2 ATSC 3rd generation (HD5000)
  *   Air2PC/AirStar 2 ATSC 3rd generation (HD5000)
  *   pcHDTV HD5500
  *   pcHDTV HD5500
  *
  *
- * TODO:
- * signal strength always returns 0.
- *
  */
  */
 
 
 #include <linux/kernel.h>
 #include <linux/kernel.h>
@@ -46,9 +43,13 @@
 #include <asm/byteorder.h>
 #include <asm/byteorder.h>
 
 
 #include "dvb_frontend.h"
 #include "dvb_frontend.h"
+#include "dvb_math.h"
 #include "lgdt330x_priv.h"
 #include "lgdt330x_priv.h"
 #include "lgdt330x.h"
 #include "lgdt330x.h"
 
 
+/* Use Equalizer Mean Squared Error instead of Phaser Tracker MSE */
+/* #define USE_EQMSE */
+
 static int debug = 0;
 static int debug = 0;
 module_param(debug, int, 0644);
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug,"Turn on/off lgdt330x frontend debugging (default:off).");
 MODULE_PARM_DESC(debug,"Turn on/off lgdt330x frontend debugging (default:off).");
@@ -68,6 +69,7 @@ struct lgdt330x_state
 
 
 	/* Demodulator private data */
 	/* Demodulator private data */
 	fe_modulation_t current_modulation;
 	fe_modulation_t current_modulation;
+	u32 snr; /* Result of last SNR calculation */
 
 
 	/* Tuner private data */
 	/* Tuner private data */
 	u32 current_frequency;
 	u32 current_frequency;
@@ -302,10 +304,10 @@ static int lgdt330x_set_parameters(struct dvb_frontend* fe,
 	static u8 lgdt3303_8vsb_44_data[] = {
 	static u8 lgdt3303_8vsb_44_data[] = {
 		0x04, 0x00,
 		0x04, 0x00,
 		0x0d, 0x40,
 		0x0d, 0x40,
-	0x0e, 0x87,
-	0x0f, 0x8e,
-	0x10, 0x01,
-	0x47, 0x8b };
+		0x0e, 0x87,
+		0x0f, 0x8e,
+		0x10, 0x01,
+		0x47, 0x8b };
 
 
 	/*
 	/*
 	 * Array of byte pairs <address, value>
 	 * Array of byte pairs <address, value>
@@ -435,9 +437,6 @@ static int lgdt3302_read_status(struct dvb_frontend* fe, fe_status_t* status)
 		/* Test signal does not exist flag */
 		/* Test signal does not exist flag */
 		/* as well as the AGC lock flag.   */
 		/* as well as the AGC lock flag.   */
 		*status |= FE_HAS_SIGNAL;
 		*status |= FE_HAS_SIGNAL;
-	} else {
-		/* Without a signal all other status bits are meaningless */
-		return 0;
 	}
 	}
 
 
 	/*
 	/*
@@ -500,9 +499,6 @@ static int lgdt3303_read_status(struct dvb_frontend* fe, fe_status_t* status)
 		/* Test input signal does not exist flag */
 		/* Test input signal does not exist flag */
 		/* as well as the AGC lock flag.   */
 		/* as well as the AGC lock flag.   */
 		*status |= FE_HAS_SIGNAL;
 		*status |= FE_HAS_SIGNAL;
-	} else {
-		/* Without a signal all other status bits are meaningless */
-		return 0;
 	}
 	}
 
 
 	/* Carrier Recovery Lock Status Register */
 	/* Carrier Recovery Lock Status Register */
@@ -543,151 +539,150 @@ static int lgdt3303_read_status(struct dvb_frontend* fe, fe_status_t* status)
 	return 0;
 	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)
 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;
 	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];
 		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
 #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];
 		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];
 		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;
 	return 0;
 }
 }
 
 
 static int lgdt3303_read_snr(struct dvb_frontend* fe, u16* snr)
 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;
 	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];
 		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];
 		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;
 	return 0;
 }
 }

+ 10 - 5
drivers/media/dvb/frontends/lgdt330x_priv.h

@@ -51,14 +51,19 @@ enum I2C_REG {
 	AGC_RFIF_ACC2= 0x3b,
 	AGC_RFIF_ACC2= 0x3b,
 	AGC_STATUS= 0x3f,
 	AGC_STATUS= 0x3f,
 	SYNC_STATUS_VSB= 0x43,
 	SYNC_STATUS_VSB= 0x43,
-	EQPH_ERR0= 0x47,
-	EQ_ERR1= 0x48,
-	EQ_ERR2= 0x49,
-	PH_ERR1= 0x4a,
-	PH_ERR2= 0x4b,
 	DEMUX_CONTROL= 0x66,
 	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_COUNTER1= 0x6a,
 	LGDT3302_PACKET_ERR_COUNTER2= 0x6b,
 	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_COUNTER1= 0x8b,
 	LGDT3303_PACKET_ERR_COUNTER2= 0x8c,
 	LGDT3303_PACKET_ERR_COUNTER2= 0x8c,
 };
 };

+ 134 - 0
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:
+ */

+ 35 - 0
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_ */

+ 91 - 85
drivers/media/dvb/frontends/or51132.c

@@ -40,6 +40,7 @@
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <asm/byteorder.h>
 #include <asm/byteorder.h>
 
 
+#include "dvb_math.h"
 #include "dvb_frontend.h"
 #include "dvb_frontend.h"
 #include "dvb-pll.h"
 #include "dvb-pll.h"
 #include "or51132.h"
 #include "or51132.h"
@@ -62,6 +63,7 @@ struct or51132_state
 
 
 	/* Demodulator private data */
 	/* Demodulator private data */
 	fe_modulation_t current_modulation;
 	fe_modulation_t current_modulation;
+	u32 snr; /* Result of last SNR calculation */
 
 
 	/* Tuner private data */
 	/* Tuner private data */
 	u32 current_frequency;
 	u32 current_frequency;
@@ -465,124 +467,128 @@ static int or51132_read_status(struct dvb_frontend* fe, fe_status_t* status)
 	return 0;
 	return 0;
 }
 }
 
 
-/* log10-1 table at .5 increments from 1 to 100.5 */
-static unsigned int i100x20log10[] = {
-     0,  352,  602,  795,  954, 1088, 1204, 1306, 1397, 1480,
-  1556, 1625, 1690, 1750, 1806, 1858, 1908, 1955, 2000, 2042,
-  2082, 2121, 2158, 2193, 2227, 2260, 2292, 2322, 2352, 2380,
-  2408, 2434, 2460, 2486, 2510, 2534, 2557, 2580, 2602, 2623,
-  2644, 2664, 2684, 2704, 2723, 2742, 2760, 2778, 2795, 2813,
-  2829, 2846, 2862, 2878, 2894, 2909, 2924, 2939, 2954, 2968,
-  2982, 2996, 3010, 3023, 3037, 3050, 3062, 3075, 3088, 3100,
-  3112, 3124, 3136, 3148, 3159, 3170, 3182, 3193, 3204, 3214,
-  3225, 3236, 3246, 3256, 3266, 3276, 3286, 3296, 3306, 3316,
-  3325, 3334, 3344, 3353, 3362, 3371, 3380, 3389, 3397, 3406,
-  3415, 3423, 3432, 3440, 3448, 3456, 3464, 3472, 3480, 3488,
-  3496, 3504, 3511, 3519, 3526, 3534, 3541, 3549, 3556, 3563,
-  3570, 3577, 3584, 3591, 3598, 3605, 3612, 3619, 3625, 3632,
-  3639, 3645, 3652, 3658, 3665, 3671, 3677, 3683, 3690, 3696,
-  3702, 3708, 3714, 3720, 3726, 3732, 3738, 3744, 3750, 3755,
-  3761, 3767, 3772, 3778, 3784, 3789, 3795, 3800, 3806, 3811,
-  3816, 3822, 3827, 3832, 3838, 3843, 3848, 3853, 3858, 3863,
-  3868, 3874, 3879, 3884, 3888, 3893, 3898, 3903, 3908, 3913,
-  3918, 3922, 3927, 3932, 3936, 3941, 3946, 3950, 3955, 3960,
-  3964, 3969, 3973, 3978, 3982, 3986, 3991, 3995, 4000, 4004,
-};
+/* Calculate SNR estimation (scaled by 2^24)
 
 
-static unsigned int denom[] = {1,1,100,1000,10000,100000,1000000,10000000,100000000};
+   8-VSB SNR and QAM equations from Oren datasheets
 
 
-static unsigned int i20Log10(unsigned short val)
-{
-	unsigned int rntval = 100;
-	unsigned int tmp = val;
-	unsigned int exp = 1;
+   For 8-VSB:
+     SNR[dB] = 10 * log10(897152044.8282 / MSE^2 ) - K
+
+     Where K = 0 if NTSC rejection filter is OFF; and
+	   K = 3 if NTSC rejection filter is ON
+
+   For QAM64:
+     SNR[dB] = 10 * log10(897152044.8282 / MSE^2 )
 
 
-	while(tmp > 100) {tmp /= 100; exp++;}
+   For QAM256:
+     SNR[dB] = 10 * log10(907832426.314266  / MSE^2 )
 
 
-	val = (2 * val)/denom[exp];
-	if (exp > 1) rntval = 2000*exp;
+   We re-write the snr equation as:
+     SNR * 2^24 = 10*(c - 2*intlog10(MSE))
+   Where for QAM256, c = log10(907832426.314266) * 2^24
+   and for 8-VSB and QAM64, c = log10(897152044.8282) * 2^24 */
 
 
-	rntval += i100x20log10[val];
-	return rntval;
+static u32 calculate_snr(u32 mse, u32 c)
+{
+	if (mse == 0) /* No signal */
+		return 0;
+
+	mse = 2*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 or51132_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+static int or51132_read_snr(struct dvb_frontend* fe, u16* snr)
 {
 {
 	struct or51132_state* state = fe->demodulator_priv;
 	struct or51132_state* state = fe->demodulator_priv;
-	unsigned char rec_buf[2];
-	unsigned char snd_buf[2];
-	u8 rcvr_stat;
-	u16 snr_equ;
-	u32 signal_strength;
-	int usK;
+	u8 rec_buf[2];
+	u8 snd_buf[2];
+	u32 noise;
+	u32 c;
+	u32 usK;
 
 
+	/* Register is same for VSB or QAM firmware */
 	snd_buf[0]=0x04;
 	snd_buf[0]=0x04;
 	snd_buf[1]=0x02; /* SNR after Equalizer */
 	snd_buf[1]=0x02; /* SNR after Equalizer */
 	msleep(30); /* 30ms */
 	msleep(30); /* 30ms */
 	if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
 	if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
-		printk(KERN_WARNING "or51132: read_status write error\n");
-		return -1;
+		printk(KERN_WARNING "or51132: snr write error\n");
+		return -EREMOTEIO;
 	}
 	}
 	msleep(30); /* 30ms */
 	msleep(30); /* 30ms */
 	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
 	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
-		printk(KERN_WARNING "or51132: read_status read error\n");
-		return -1;
+		printk(KERN_WARNING "or51132: snr read error\n");
+		return -EREMOTEIO;
 	}
 	}
-	snr_equ = rec_buf[0] | (rec_buf[1] << 8);
-	dprintk("read_signal_strength snr_equ %x %x (%i)\n",rec_buf[0],rec_buf[1],snr_equ);
+	noise = rec_buf[0] | (rec_buf[1] << 8);
+	dprintk("read_snr noise %x %x (%i)\n",rec_buf[0],rec_buf[1],noise);
 
 
-	/* Receiver Status */
+	/* Read status, contains modulation type for QAM_AUTO and
+	   NTSC filter for VSB */
 	snd_buf[0]=0x04;
 	snd_buf[0]=0x04;
-	snd_buf[1]=0x00;
+	snd_buf[1]=0x00; /* Status register */
 	msleep(30); /* 30ms */
 	msleep(30); /* 30ms */
 	if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
 	if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
-		printk(KERN_WARNING "or51132: read_signal_strength read_status write error\n");
-		return -1;
+		printk(KERN_WARNING "or51132: status write error\n");
+		return -EREMOTEIO;
 	}
 	}
 	msleep(30); /* 30ms */
 	msleep(30); /* 30ms */
 	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
 	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
-		printk(KERN_WARNING "or51132: read_signal_strength read_status read error\n");
-		return -1;
+		printk(KERN_WARNING "or51132: status read error\n");
+		return -EREMOTEIO;
 	}
 	}
-	dprintk("read_signal_strength read_status %x %x\n",rec_buf[0],rec_buf[1]);
-	rcvr_stat = rec_buf[1];
-	usK = (rcvr_stat & 0x10) ? 3 : 0;
 
 
-	/* The value reported back from the frontend will be FFFF=100% 0000=0% */
-	signal_strength = (((8952 - i20Log10(snr_equ) - usK*100)/3+5)*65535)/1000;
-	if (signal_strength > 0xffff)
-		*strength = 0xffff;
-	else
-		*strength = signal_strength;
-	dprintk("read_signal_strength %i\n",*strength);
+	usK = 0;
+	switch (rec_buf[0]) {
+	case 0x06:
+		usK = (rec_buf[1] & 0x10) ? 0x03000000 : 0;
+		/* Fall through to QAM64 case */
+	case 0x43:
+		c = 150204167;
+		break;
+	case 0x45:
+		c = 150290396;
+		break;
+	default:
+		printk(KERN_ERR "or51132: unknown status 0x%02x\n", rec_buf[0]);
+		return -EREMOTEIO;
+	}
+	dprintk("%s: modulation %02x, NTSC rej O%s\n", __FUNCTION__,
+		rec_buf[0], rec_buf[1]&0x10?"n":"ff");
+
+	/* Calculate SNR using noise, c, and NTSC rejection correction */
+	state->snr = calculate_snr(noise, c) - usK;
+	*snr = (state->snr) >> 16;
+
+	dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __FUNCTION__, noise,
+		state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16);
 
 
 	return 0;
 	return 0;
 }
 }
 
 
-static int or51132_read_snr(struct dvb_frontend* fe, u16* snr)
+static int or51132_read_signal_strength(struct dvb_frontend* fe, u16* strength)
 {
 {
-	struct or51132_state* state = fe->demodulator_priv;
-	unsigned char rec_buf[2];
-	unsigned char snd_buf[2];
-	u16 snr_equ;
-
-	snd_buf[0]=0x04;
-	snd_buf[1]=0x02; /* SNR after Equalizer */
-	msleep(30); /* 30ms */
-	if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) {
-		printk(KERN_WARNING "or51132: read_snr write error\n");
-		return -1;
-	}
-	msleep(30); /* 30ms */
-	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
-		printk(KERN_WARNING "or51132: read_snr dvr read error\n");
-		return -1;
-	}
-	snr_equ = rec_buf[0] | (rec_buf[1] << 8);
-	dprintk("read_snr snr_equ %x %x (%i)\n",rec_buf[0],rec_buf[1],snr_equ);
+	/* 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 or51132_state* state = (struct or51132_state*) fe->demodulator_priv;
+	u16 snr;
+	int ret;
 
 
-	*snr = 0xFFFF - snr_equ;
-	dprintk("read_snr %i\n",*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;
 	return 0;
 }
 }

+ 50 - 74
drivers/media/dvb/frontends/or51211.c

@@ -39,6 +39,7 @@
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <asm/byteorder.h>
 #include <asm/byteorder.h>
 
 
+#include "dvb_math.h"
 #include "dvb_frontend.h"
 #include "dvb_frontend.h"
 #include "or51211.h"
 #include "or51211.h"
 
 
@@ -63,6 +64,7 @@ struct or51211_state {
 
 
 	/* Demodulator private data */
 	/* Demodulator private data */
 	u8 initialized:1;
 	u8 initialized:1;
+	u32 snr; /* Result of last SNR claculation */
 
 
 	/* Tuner private data */
 	/* Tuner private data */
 	u32 current_frequency;
 	u32 current_frequency;
@@ -292,107 +294,81 @@ static int or51211_read_status(struct dvb_frontend* fe, fe_status_t* status)
 	return 0;
 	return 0;
 }
 }
 
 
-/* log10-1 table at .5 increments from 1 to 100.5 */
-static unsigned int i100x20log10[] = {
-		0,  352,  602,  795,  954, 1088, 1204, 1306, 1397, 1480,
-	 1556, 1625, 1690, 1750, 1806, 1858, 1908, 1955, 2000, 2042,
-	 2082, 2121, 2158, 2193, 2227, 2260, 2292, 2322, 2352, 2380,
-	 2408, 2434, 2460, 2486, 2510, 2534, 2557, 2580, 2602, 2623,
-	 2644, 2664, 2684, 2704, 2723, 2742, 2760, 2778, 2795, 2813,
-	 2829, 2846, 2862, 2878, 2894, 2909, 2924, 2939, 2954, 2968,
-	 2982, 2996, 3010, 3023, 3037, 3050, 3062, 3075, 3088, 3100,
-	 3112, 3124, 3136, 3148, 3159, 3170, 3182, 3193, 3204, 3214,
-	 3225, 3236, 3246, 3256, 3266, 3276, 3286, 3296, 3306, 3316,
-	 3325, 3334, 3344, 3353, 3362, 3371, 3380, 3389, 3397, 3406,
-	 3415, 3423, 3432, 3440, 3448, 3456, 3464, 3472, 3480, 3488,
-	 3496, 3504, 3511, 3519, 3526, 3534, 3541, 3549, 3556, 3563,
-	 3570, 3577, 3584, 3591, 3598, 3605, 3612, 3619, 3625, 3632,
-	 3639, 3645, 3652, 3658, 3665, 3671, 3677, 3683, 3690, 3696,
-	 3702, 3708, 3714, 3720, 3726, 3732, 3738, 3744, 3750, 3755,
-	 3761, 3767, 3772, 3778, 3784, 3789, 3795, 3800, 3806, 3811,
-	 3816, 3822, 3827, 3832, 3838, 3843, 3848, 3853, 3858, 3863,
-	 3868, 3874, 3879, 3884, 3888, 3893, 3898, 3903, 3908, 3913,
-	 3918, 3922, 3927, 3932, 3936, 3941, 3946, 3950, 3955, 3960,
-	 3964, 3969, 3973, 3978, 3982, 3986, 3991, 3995, 4000, 4004,
-};
-
-static unsigned int denom[] = {1,1,100,1000,10000,100000,1000000,10000000,100000000};
+/* Calculate SNR estimation (scaled by 2^24)
 
 
-static unsigned int i20Log10(unsigned short val)
-{
-	unsigned int rntval = 100;
-	unsigned int tmp = val;
-	unsigned int exp = 1;
+   8-VSB SNR equation from Oren datasheets
 
 
-	while(tmp > 100) {tmp /= 100; exp++;}
+   For 8-VSB:
+     SNR[dB] = 10 * log10(219037.9454 / MSE^2 )
 
 
-	val = (2 * val)/denom[exp];
-	if (exp > 1) rntval = 2000*exp;
+   We re-write the snr equation as:
+     SNR * 2^24 = 10*(c - 2*intlog10(MSE))
+   Where for 8-VSB, c = log10(219037.9454) * 2^24 */
 
 
-	rntval += i100x20log10[val];
-	return rntval;
+static u32 calculate_snr(u32 mse, u32 c)
+{
+	if (mse == 0) /* No signal */
+		return 0;
+
+	mse = 2*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 or51211_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+static int or51211_read_snr(struct dvb_frontend* fe, u16* snr)
 {
 {
 	struct or51211_state* state = fe->demodulator_priv;
 	struct or51211_state* state = fe->demodulator_priv;
 	u8 rec_buf[2];
 	u8 rec_buf[2];
-	u8 snd_buf[4];
-	u8 snr_equ;
-	u32 signal_strength;
+	u8 snd_buf[3];
 
 
 	/* SNR after Equalizer */
 	/* SNR after Equalizer */
 	snd_buf[0] = 0x04;
 	snd_buf[0] = 0x04;
 	snd_buf[1] = 0x00;
 	snd_buf[1] = 0x00;
 	snd_buf[2] = 0x04;
 	snd_buf[2] = 0x04;
-	snd_buf[3] = 0x00;
 
 
 	if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) {
 	if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) {
-		printk(KERN_WARNING "or51211: read_status write error\n");
+		printk(KERN_WARNING "%s: error writing snr reg\n",
+		       __FUNCTION__);
 		return -1;
 		return -1;
 	}
 	}
-	msleep(3);
 	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
 	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
-		printk(KERN_WARNING "or51211: read_status read error\n");
+		printk(KERN_WARNING "%s: read_status read error\n",
+		       __FUNCTION__);
 		return -1;
 		return -1;
 	}
 	}
-	snr_equ = rec_buf[0] & 0xff;
 
 
-	/* The value reported back from the frontend will be FFFF=100% 0000=0% */
-	signal_strength = (((5334 - i20Log10(snr_equ))/3+5)*65535)/1000;
-	if (signal_strength > 0xffff)
-		*strength = 0xffff;
-	else
-		*strength = signal_strength;
-	dprintk("read_signal_strength %i\n",*strength);
+	state->snr = calculate_snr(rec_buf[0], 89599047);
+	*snr = (state->snr) >> 16;
+
+	dprintk("%s: noise = 0x%02x, snr = %d.%02d dB\n", __FUNCTION__, rec_buf[0],
+		state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16);
 
 
 	return 0;
 	return 0;
 }
 }
 
 
-static int or51211_read_snr(struct dvb_frontend* fe, u16* snr)
+static int or51211_read_signal_strength(struct dvb_frontend* fe, u16* strength)
 {
 {
-	struct or51211_state* state = fe->demodulator_priv;
-	u8 rec_buf[2];
-	u8 snd_buf[4];
-
-	/* SNR after Equalizer */
-	snd_buf[0] = 0x04;
-	snd_buf[1] = 0x00;
-	snd_buf[2] = 0x04;
-	snd_buf[3] = 0x00;
-
-	if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) {
-		printk(KERN_WARNING "or51211: read_status write error\n");
-		return -1;
-	}
-	msleep(3);
-	if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) {
-		printk(KERN_WARNING "or51211: read_status read error\n");
-		return -1;
-	}
-	*snr = rec_buf[0] & 0xff;
-
-	dprintk("read_snr %i\n",*snr);
+	/* 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 or51211_state* state = (struct or51211_state*)fe->demodulator_priv;
+	u16 snr;
+	int ret;
+
+	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;
 	return 0;
 }
 }

+ 8 - 2
drivers/media/dvb/frontends/tda1004x.c

@@ -648,18 +648,24 @@ static int tda10046_init(struct dvb_frontend* fe)
 		tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup
 		tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup
 		tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x00); // set AGC polarities
 		tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x00); // set AGC polarities
 		break;
 		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_CONF, 0x02);   // AGC setup
 		tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70);    // AGC Threshold
 		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_AGC_RENORM, 0x08); // Gain Renormalize
 		tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x6a); // set AGC polarities
 		tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x6a); // set AGC polarities
 		break;
 		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_CONF, 0x02);   // AGC setup
 		tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70);    // AGC Threshold
 		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_AGC_RENORM, 0x08); // Gain Renormalize
 		tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities
 		tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities
 		break;
 		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, TDA1004X_CONFADC2, 0x38);
 	tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0x61); // Turn both AGC outputs on
 	tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0x61); // Turn both AGC outputs on

+ 3 - 2
drivers/media/dvb/frontends/tda1004x.h

@@ -35,8 +35,9 @@ enum tda10046_agc {
 	TDA10046_AGC_DEFAULT,		/* original configuration */
 	TDA10046_AGC_DEFAULT,		/* original configuration */
 	TDA10046_AGC_IFO_AUTO_NEG,	/* IF AGC only, automatic, negtive */
 	TDA10046_AGC_IFO_AUTO_NEG,	/* IF AGC only, automatic, negtive */
 	TDA10046_AGC_IFO_AUTO_POS,	/* IF AGC only, automatic, positive */
 	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 {
 enum tda10046_if {

+ 30 - 0
drivers/media/dvb/frontends/tda8083.c

@@ -262,12 +262,29 @@ static int tda8083_read_status(struct dvb_frontend* fe, fe_status_t* status)
 	if (sync & 0x10)
 	if (sync & 0x10)
 		*status |= FE_HAS_SYNC;
 		*status |= FE_HAS_SYNC;
 
 
+	if (sync & 0x20) /* frontend can not lock */
+		*status |= FE_TIMEDOUT;
+
 	if ((sync & 0x1f) == 0x1f)
 	if ((sync & 0x1f) == 0x1f)
 		*status |= FE_HAS_LOCK;
 		*status |= FE_HAS_LOCK;
 
 
 	return 0;
 	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)
 static int tda8083_read_signal_strength(struct dvb_frontend* fe, u16* strength)
 {
 {
 	struct tda8083_state* state = fe->demodulator_priv;
 	struct tda8083_state* state = fe->demodulator_priv;
@@ -288,6 +305,17 @@ static int tda8083_read_snr(struct dvb_frontend* fe, u16* snr)
 	return 0;
 	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)
 static int tda8083_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
 {
 {
 	struct tda8083_state* state = fe->demodulator_priv;
 	struct tda8083_state* state = fe->demodulator_priv;
@@ -440,6 +468,8 @@ static struct dvb_frontend_ops tda8083_ops = {
 	.read_status = tda8083_read_status,
 	.read_status = tda8083_read_status,
 	.read_signal_strength = tda8083_read_signal_strength,
 	.read_signal_strength = tda8083_read_signal_strength,
 	.read_snr = tda8083_read_snr,
 	.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_master_cmd = tda8083_send_diseqc_msg,
 	.diseqc_send_burst = tda8083_diseqc_send_burst,
 	.diseqc_send_burst = tda8083_diseqc_send_burst,

+ 7 - 5
drivers/media/dvb/frontends/tda826x.c

@@ -42,8 +42,7 @@ struct tda826x_priv {
 
 
 static int tda826x_release(struct dvb_frontend *fe)
 static int tda826x_release(struct dvb_frontend *fe)
 {
 {
-	if (fe->tuner_priv)
-		kfree(fe->tuner_priv);
+	kfree(fe->tuner_priv);
 	fe->tuner_priv = NULL;
 	fe->tuner_priv = NULL;
 	return 0;
 	return 0;
 }
 }
@@ -133,18 +132,21 @@ struct dvb_frontend *tda826x_attach(struct dvb_frontend *fe, int addr, struct i2
 {
 {
 	struct tda826x_priv *priv = NULL;
 	struct tda826x_priv *priv = NULL;
 	u8 b1 [] = { 0, 0 };
 	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;
 	int ret;
 
 
 	dprintk("%s:\n", __FUNCTION__);
 	dprintk("%s:\n", __FUNCTION__);
 
 
 	if (fe->ops.i2c_gate_ctrl)
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 1);
 		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)
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 0);
 		fe->ops.i2c_gate_ctrl(fe, 0);
 
 
-	if (ret != 1)
+	if (ret != 2)
 		return NULL;
 		return NULL;
 	if (!(b1[1] & 0x80))
 	if (!(b1[1] & 0x80))
 		return NULL;
 		return NULL;

+ 1 - 2
drivers/media/dvb/frontends/tua6100.c

@@ -43,8 +43,7 @@ struct tua6100_priv {
 
 
 static int tua6100_release(struct dvb_frontend *fe)
 static int tua6100_release(struct dvb_frontend *fe)
 {
 {
-	if (fe->tuner_priv)
-		kfree(fe->tuner_priv);
+	kfree(fe->tuner_priv);
 	fe->tuner_priv = NULL;
 	fe->tuner_priv = NULL;
 	return 0;
 	return 0;
 }
 }

+ 1 - 0
drivers/media/dvb/ttpci/Kconfig

@@ -92,6 +92,7 @@ config DVB_BUDGET_CI
 	select DVB_STV0299 if !DVB_FE_CUSTOMISE
 	select DVB_STV0299 if !DVB_FE_CUSTOMISE
 	select DVB_TDA1004X if !DVB_FE_CUSTOMISE
 	select DVB_TDA1004X if !DVB_FE_CUSTOMISE
 	select DVB_LNBP21 if !DVB_FE_CUSTOMISE
 	select DVB_LNBP21 if !DVB_FE_CUSTOMISE
+	select VIDEO_IR
 	help
 	help
 	  Support for simple SAA7146 based DVB cards
 	  Support for simple SAA7146 based DVB cards
 	  (so called Budget- or Nova-PCI cards) without onboard
 	  (so called Budget- or Nova-PCI cards) without onboard

+ 1 - 1
drivers/media/dvb/ttpci/av7110.c

@@ -2828,7 +2828,7 @@ MODULE_DEVICE_TABLE(pci, pci_tbl);
 
 
 static struct saa7146_extension av7110_extension = {
 static struct saa7146_extension av7110_extension = {
 	.name		= "dvb",
 	.name		= "dvb",
-	.flags		= SAA7146_I2C_SHORT_DELAY,
+	.flags		= SAA7146_USE_I2C_IRQ,
 
 
 	.module		= THIS_MODULE,
 	.module		= THIS_MODULE,
 	.pci_tbl	= &pci_tbl[0],
 	.pci_tbl	= &pci_tbl[0],

+ 17 - 8
drivers/media/dvb/ttpci/av7110_ir.c

@@ -48,7 +48,8 @@ static void av7110_emit_keyup(unsigned long data)
 	if (!data || !test_bit(data, input_dev->key))
 	if (!data || !test_bit(data, input_dev->key))
 		return;
 		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 long parm)
 		del_timer(&keyup_timer);
 		del_timer(&keyup_timer);
 		if (keyup_timer.data != keycode || new_toggle != old_toggle) {
 		if (keyup_timer.data != keycode || new_toggle != old_toggle) {
 			delay_timer_finished = 0;
 			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 {
 	} else {
 		delay_timer_finished = 0;
 		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;
 	keyup_timer.expires = jiffies + UP_TIMEOUT;
@@ -211,6 +215,7 @@ static void ir_handler(struct av7110 *av7110, u32 ircom)
 int __devinit av7110_ir_init(struct av7110 *av7110)
 int __devinit av7110_ir_init(struct av7110 *av7110)
 {
 {
 	static struct proc_dir_entry *e;
 	static struct proc_dir_entry *e;
+	int err;
 
 
 	if (av_cnt >= sizeof av_list/sizeof av_list[0])
 	if (av_cnt >= sizeof av_list/sizeof av_list[0])
 		return -ENOSPC;
 		return -ENOSPC;
@@ -231,7 +236,11 @@ int __devinit av7110_ir_init(struct av7110 *av7110)
 		set_bit(EV_KEY, input_dev->evbit);
 		set_bit(EV_KEY, input_dev->evbit);
 		set_bit(EV_REP, input_dev->evbit);
 		set_bit(EV_REP, input_dev->evbit);
 		input_register_keys();
 		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;
 		input_dev->timer.function = input_repeat_key;
 
 
 		e = create_proc_entry("av7110_ir", S_IFREG | S_IRUGO | S_IWUSR, NULL);
 		e = create_proc_entry("av7110_ir", S_IFREG | S_IRUGO | S_IWUSR, NULL);

+ 21 - 5
drivers/media/dvb/ttpci/budget-av.c

@@ -360,7 +360,7 @@ static int ciintf_init(struct budget_av *budget_av)
 	saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
 	saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
 
 
 	/* Enable DEBI pins */
 	/* Enable DEBI pins */
-	saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16) | 0x800);
+	saa7146_write(saa, MC1, MASK_27 | MASK_11);
 
 
 	/* register CI interface */
 	/* register CI interface */
 	budget_av->ca.owner = THIS_MODULE;
 	budget_av->ca.owner = THIS_MODULE;
@@ -386,7 +386,7 @@ static int ciintf_init(struct budget_av *budget_av)
 	return 0;
 	return 0;
 
 
 error:
 error:
-	saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
+	saa7146_write(saa, MC1, MASK_27);
 	return result;
 	return result;
 }
 }
 
 
@@ -403,7 +403,7 @@ static void ciintf_deinit(struct budget_av *budget_av)
 	dvb_ca_en50221_release(&budget_av->ca);
 	dvb_ca_en50221_release(&budget_av->ca);
 
 
 	/* disable DEBI pins */
 	/* disable DEBI pins */
-	saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
+	saa7146_write(saa, MC1, MASK_27);
 }
 }
 
 
 
 
@@ -655,6 +655,10 @@ static struct tda10021_config philips_cu1216_config = {
 	.demod_address = 0x0c,
 	.demod_address = 0x0c,
 };
 };
 
 
+static struct tda10021_config philips_cu1216_config_altaddress = {
+	.demod_address = 0x0d,
+};
+
 
 
 
 
 
 
@@ -831,7 +835,7 @@ static int philips_sd1878_tda8261_tuner_set_params(struct dvb_frontend *fe,
 		return -EINVAL;
 		return -EINVAL;
 
 
 	rc=dvb_pll_configure(&dvb_pll_philips_sd1878_tda8261, buf,
 	rc=dvb_pll_configure(&dvb_pll_philips_sd1878_tda8261, buf,
-			params->frequency, 0);
+			     params->frequency, 0);
 	if(rc < 0) return rc;
 	if(rc < 0) return rc;
 
 
 	if (fe->ops.i2c_gate_ctrl)
 	if (fe->ops.i2c_gate_ctrl)
@@ -914,6 +918,7 @@ static u8 read_pwm(struct budget_av *budget_av)
 #define SUBID_DVBS_TV_STAR_CI	0x0016
 #define SUBID_DVBS_TV_STAR_CI	0x0016
 #define SUBID_DVBS_EASYWATCH_1  0x001a
 #define SUBID_DVBS_EASYWATCH_1  0x001a
 #define SUBID_DVBS_EASYWATCH	0x001e
 #define SUBID_DVBS_EASYWATCH	0x001e
+#define SUBID_DVBC_EASYWATCH	0x002a
 #define SUBID_DVBC_KNC1		0x0020
 #define SUBID_DVBC_KNC1		0x0020
 #define SUBID_DVBC_KNC1_PLUS	0x0021
 #define SUBID_DVBC_KNC1_PLUS	0x0021
 #define SUBID_DVBC_CINERGY1200	0x1156
 #define SUBID_DVBC_CINERGY1200	0x1156
@@ -947,11 +952,15 @@ static void frontend_init(struct budget_av *budget_av)
 	/* Enable / PowerON Frontend */
 	/* Enable / PowerON Frontend */
 	saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO);
 	saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO);
 
 
+	/* Wait for PowerON */
+	msleep(100);
+
 	/* additional setup necessary for the PLUS cards */
 	/* additional setup necessary for the PLUS cards */
 	switch (saa->pci->subsystem_device) {
 	switch (saa->pci->subsystem_device) {
 		case SUBID_DVBS_KNC1_PLUS:
 		case SUBID_DVBS_KNC1_PLUS:
 		case SUBID_DVBC_KNC1_PLUS:
 		case SUBID_DVBC_KNC1_PLUS:
 		case SUBID_DVBT_KNC1_PLUS:
 		case SUBID_DVBT_KNC1_PLUS:
+		case SUBID_DVBC_EASYWATCH:
 			saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI);
 			saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI);
 			break;
 			break;
 	}
 	}
@@ -1006,10 +1015,15 @@ static void frontend_init(struct budget_av *budget_av)
 	case SUBID_DVBC_KNC1:
 	case SUBID_DVBC_KNC1:
 	case SUBID_DVBC_KNC1_PLUS:
 	case SUBID_DVBC_KNC1_PLUS:
 	case SUBID_DVBC_CINERGY1200:
 	case SUBID_DVBC_CINERGY1200:
+	case SUBID_DVBC_EASYWATCH:
 		budget_av->reinitialise_demod = 1;
 		budget_av->reinitialise_demod = 1;
 		fe = dvb_attach(tda10021_attach, &philips_cu1216_config,
 		fe = dvb_attach(tda10021_attach, &philips_cu1216_config,
 				     &budget_av->budget.i2c_adap,
 				     &budget_av->budget.i2c_adap,
 				     read_pwm(budget_av));
 				     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) {
 		if (fe) {
 			budget_av->tda10021_poclkp = 1;
 			budget_av->tda10021_poclkp = 1;
 			budget_av->tda10021_set_frontend = fe->ops.set_frontend;
 			budget_av->tda10021_set_frontend = fe->ops.set_frontend;
@@ -1242,6 +1256,7 @@ MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BUDGET_KNC1T);
 MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR);
 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(satewpls, "Satelco EasyWatch DVB-S light", BUDGET_TVSTAR);
 MAKE_BUDGET_INFO(satewpls1, "Satelco EasyWatch DVB-S light", BUDGET_KNC1S);
 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(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP);
 MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP);
 MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP);
 MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP);
 MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP);
@@ -1260,6 +1275,7 @@ static struct pci_device_id pci_tbl[] = {
 	MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016),
 	MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016),
 	MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e),
 	MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e),
 	MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a),
 	MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a),
+	MAKE_EXTENSION_PCI(satewplc, 0x1894, 0x002a),
 	MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020),
 	MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020),
 	MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021),
 	MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021),
 	MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030),
 	MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030),
@@ -1277,7 +1293,7 @@ MODULE_DEVICE_TABLE(pci, pci_tbl);
 
 
 static struct saa7146_extension budget_extension = {
 static struct saa7146_extension budget_extension = {
 	.name = "budget_av",
 	.name = "budget_av",
-	.flags = SAA7146_I2C_SHORT_DELAY,
+	.flags = SAA7146_USE_I2C_IRQ,
 
 
 	.pci_tbl = pci_tbl,
 	.pci_tbl = pci_tbl,
 
 

+ 197 - 137
drivers/media/dvb/ttpci/budget-ci.c

@@ -37,6 +37,7 @@
 #include <linux/interrupt.h>
 #include <linux/interrupt.h>
 #include <linux/input.h>
 #include <linux/input.h>
 #include <linux/spinlock.h>
 #include <linux/spinlock.h>
+#include <media/ir-common.h>
 
 
 #include "dvb_ca_en50221.h"
 #include "dvb_ca_en50221.h"
 #include "stv0299.h"
 #include "stv0299.h"
@@ -72,162 +73,218 @@
 #define SLOTSTATUS_READY	8
 #define SLOTSTATUS_READY	8
 #define SLOTSTATUS_OCCUPIED	(SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY)
 #define SLOTSTATUS_OCCUPIED	(SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY)
 
 
+/* Milliseconds during which key presses are regarded as key repeat and during
+ * which the debounce logic is active
+ */
+#define IR_REPEAT_TIMEOUT	350
+
+/* RC5 device wildcard */
+#define IR_DEVICE_ANY		255
+
+/* Some remotes sends multiple sequences per keypress (e.g. Zenith sends two),
+ * this setting allows the superflous sequences to be ignored
+ */
+static int debounce = 0;
+module_param(debounce, int, 0644);
+MODULE_PARM_DESC(debounce, "ignore repeated IR sequences (default: 0 = ignore no sequences)");
+
+static int rc5_device = -1;
+module_param(rc5_device, int, 0644);
+MODULE_PARM_DESC(rc5_device, "only IR commands to given RC5 device (device = 0 - 31, any device = 255, default: autodetect)");
+
+static int ir_debug = 0;
+module_param(ir_debug, int, 0644);
+MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding");
+
+struct budget_ci_ir {
+	struct input_dev *dev;
+	struct tasklet_struct msp430_irq_tasklet;
+	char name[72]; /* 40 + 32 for (struct saa7146_dev).name */
+	char phys[32];
+	struct ir_input_state state;
+	int rc5_device;
+};
+
 struct budget_ci {
 struct budget_ci {
 	struct budget budget;
 	struct budget budget;
-	struct input_dev *input_dev;
-	struct tasklet_struct msp430_irq_tasklet;
 	struct tasklet_struct ciintf_irq_tasklet;
 	struct tasklet_struct ciintf_irq_tasklet;
 	int slot_status;
 	int slot_status;
 	int ci_irq;
 	int ci_irq;
 	struct dvb_ca_en50221 ca;
 	struct dvb_ca_en50221 ca;
-	char ir_dev_name[50];
+	struct budget_ci_ir ir;
 	u8 tuner_pll_address; /* used for philips_tdm1316l configs */
 	u8 tuner_pll_address; /* used for philips_tdm1316l configs */
 };
 };
 
 
-/* from reading the following remotes:
-   Zenith Universal 7 / TV Mode 807 / VCR Mode 837
-   Hauppauge (from NOVA-CI-s box product)
-   i've taken a "middle of the road" approach and note the differences
-*/
-static u16 key_map[64] = {
-	/* 0x0X */
-	KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8,
-	KEY_9,
-	KEY_ENTER,
-	KEY_RED,
-	KEY_POWER,		/* RADIO on Hauppauge */
-	KEY_MUTE,
-	0,
-	KEY_A,			/* TV on Hauppauge */
-	/* 0x1X */
-	KEY_VOLUMEUP, KEY_VOLUMEDOWN,
-	0, 0,
-	KEY_B,
-	0, 0, 0, 0, 0, 0, 0,
-	KEY_UP, KEY_DOWN,
-	KEY_OPTION,		/* RESERVED on Hauppauge */
-	KEY_BREAK,
-	/* 0x2X */
-	KEY_CHANNELUP, KEY_CHANNELDOWN,
-	KEY_PREVIOUS,		/* Prev. Ch on Zenith, SOURCE on Hauppauge */
-	0, KEY_RESTART, KEY_OK,
-	KEY_CYCLEWINDOWS,	/* MINIMIZE on Hauppauge */
-	0,
-	KEY_ENTER,		/* VCR mode on Zenith */
-	KEY_PAUSE,
-	0,
-	KEY_RIGHT, KEY_LEFT,
-	0,
-	KEY_MENU,		/* FULL SCREEN on Hauppauge */
-	0,
-	/* 0x3X */
-	KEY_SLOW,
-	KEY_PREVIOUS,		/* VCR mode on Zenith */
-	KEY_REWIND,
-	0,
-	KEY_FASTFORWARD,
-	KEY_PLAY, KEY_STOP,
-	KEY_RECORD,
-	KEY_TUNER,		/* TV/VCR on Zenith */
-	0,
-	KEY_C,
-	0,
-	KEY_EXIT,
-	KEY_POWER2,
-	KEY_TUNER,		/* VCR mode on Zenith */
-	0,
-};
-
-static void msp430_ir_debounce(unsigned long data)
+static void msp430_ir_keyup(unsigned long data)
 {
 {
-	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;
-	}
-
-	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 */
+	struct budget_ci_ir *ir = (struct budget_ci_ir *) data;
+	ir_input_nokey(ir->dev, &ir->state);
 }
 }
 
 
 static void msp430_ir_interrupt(unsigned long data)
 static void msp430_ir_interrupt(unsigned long data)
 {
 {
 	struct budget_ci *budget_ci = (struct budget_ci *) data;
 	struct budget_ci *budget_ci = (struct budget_ci *) data;
-	struct input_dev *dev = budget_ci->input_dev;
-	unsigned int code =
-		ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8;
+	struct input_dev *dev = budget_ci->ir.dev;
+	static int bounces = 0;
+	int device;
+	int toggle;
+	static int prev_toggle = -1;
+	static u32 ir_key;
+	u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8;
+
+	/*
+	 * The msp430 chip can generate two different bytes, command and device
+	 *
+	 * type1: X1CCCCCC, C = command bits (0 - 63)
+	 * type2: X0TDDDDD, D = device bits (0 - 31), T = RC5 toggle bit
+	 *
+	 * More than one command byte may be generated before the device byte
+	 * Only when we have both, a correct keypress is generated
+	 */
+
+	/* Is this a RC5 command byte? */
+	if (command & 0x40) {
+		if (ir_debug)
+			printk("budget_ci: received command byte 0x%02x\n", command);
+		ir_key = command & 0x3f;
+		return;
+	}
 
 
-	if (code & 0x40) {
-		code &= 0x3f;
+	/* It's a RC5 device byte */
+	if (ir_debug)
+		printk("budget_ci: received device byte 0x%02x\n", command);
+	device = command & 0x1f;
+	toggle = command & 0x20;
 
 
-		if (timer_pending(&dev->timer)) {
-			if (code == dev->repeat_key) {
-				++dev->rep[0];
-				return;
-			}
-			del_timer(&dev->timer);
-			input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0);
-		}
+	if (budget_ci->ir.rc5_device != IR_DEVICE_ANY && budget_ci->ir.rc5_device != device)
+		return;
 
 
-		if (!key_map[code]) {
-			printk("DVB (%s): no key for %02x!\n", __FUNCTION__, code);
-			return;
-		}
+	/* Ignore repeated key sequences if requested */
+	if (toggle == prev_toggle && ir_key == dev->repeat_key &&
+	    bounces > 0 && timer_pending(&dev->timer)) {
+		if (ir_debug)
+			printk("budget_ci: debounce logic ignored IR command\n");
+		bounces--;
+		return;
+	}
+	prev_toggle = toggle;
 
 
-		/* 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);
+	/* Are we still waiting for a keyup event? */
+	if (del_timer(&dev->timer))
+		ir_input_nokey(dev, &budget_ci->ir.state);
+
+	/* Generate keypress */
+	if (ir_debug)
+		printk("budget_ci: generating keypress 0x%02x\n", ir_key);
+	ir_input_keydown(dev, &budget_ci->ir.state, ir_key, (ir_key & (command << 8)));
+
+	/* Do we want to delay the keyup event? */
+	if (debounce) {
+		bounces = debounce;
+		mod_timer(&dev->timer, jiffies + msecs_to_jiffies(IR_REPEAT_TIMEOUT));
+	} else {
+		ir_input_nokey(dev, &budget_ci->ir.state);
 	}
 	}
 }
 }
 
 
 static int msp430_ir_init(struct budget_ci *budget_ci)
 static int msp430_ir_init(struct budget_ci *budget_ci)
 {
 {
 	struct saa7146_dev *saa = budget_ci->budget.dev;
 	struct saa7146_dev *saa = budget_ci->budget.dev;
-	struct input_dev *input_dev;
-	int i;
+	struct input_dev *input_dev = budget_ci->ir.dev;
+	int error;
+
+	budget_ci->ir.dev = input_dev = input_allocate_device();
+	if (!input_dev) {
+		printk(KERN_ERR "budget_ci: IR interface initialisation failed\n");
+		error = -ENOMEM;
+		goto out1;
+	}
+
+	snprintf(budget_ci->ir.name, sizeof(budget_ci->ir.name),
+		 "Budget-CI dvb ir receiver %s", saa->name);
+	snprintf(budget_ci->ir.phys, sizeof(budget_ci->ir.phys),
+		 "pci-%s/ir0", pci_name(saa->pci));
 
 
-	budget_ci->input_dev = input_dev = input_allocate_device();
-	if (!input_dev)
-		return -ENOMEM;
+	input_dev->name = budget_ci->ir.name;
 
 
-	sprintf(budget_ci->ir_dev_name, "Budget-CI dvb ir receiver %s", saa->name);
+	input_dev->phys = budget_ci->ir.phys;
+	input_dev->id.bustype = BUS_PCI;
+	input_dev->id.version = 1;
+	if (saa->pci->subsystem_vendor) {
+		input_dev->id.vendor = saa->pci->subsystem_vendor;
+		input_dev->id.product = saa->pci->subsystem_device;
+	} else {
+		input_dev->id.vendor = saa->pci->vendor;
+		input_dev->id.product = saa->pci->device;
+	}
+	input_dev->cdev.dev = &saa->pci->dev;
 
 
-	input_dev->name = budget_ci->ir_dev_name;
+	/* Select keymap and address */
+	switch (budget_ci->budget.dev->pci->subsystem_device) {
+	case 0x100c:
+	case 0x100f:
+	case 0x1010:
+	case 0x1011:
+	case 0x1012:
+	case 0x1017:
+		/* The hauppauge keymap is a superset of these remotes */
+		ir_input_init(input_dev, &budget_ci->ir.state,
+			      IR_TYPE_RC5, ir_codes_hauppauge_new);
+
+		if (rc5_device < 0)
+			budget_ci->ir.rc5_device = 0x1f;
+		else
+			budget_ci->ir.rc5_device = rc5_device;
+		break;
+	default:
+		/* unknown remote */
+		ir_input_init(input_dev, &budget_ci->ir.state,
+			      IR_TYPE_RC5, ir_codes_budget_ci_old);
+
+		if (rc5_device < 0)
+			budget_ci->ir.rc5_device = IR_DEVICE_ANY;
+		else
+			budget_ci->ir.rc5_device = rc5_device;
+		break;
+	}
 
 
-	set_bit(EV_KEY, input_dev->evbit);
-	for (i = 0; i < ARRAY_SIZE(key_map); i++)
-		if (key_map[i])
-			set_bit(key_map[i], input_dev->keybit);
+	/* initialise the key-up debounce timeout handler */
+	input_dev->timer.function = msp430_ir_keyup;
+	input_dev->timer.data = (unsigned long) &budget_ci->ir;
 
 
-	input_register_device(budget_ci->input_dev);
+	error = input_register_device(input_dev);
+	if (error) {
+		printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error);
+		goto out2;
+	}
 
 
-	input_dev->timer.function = msp430_ir_debounce;
+	tasklet_init(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt,
+		     (unsigned long) budget_ci);
 
 
-	saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_06);
+	SAA7146_IER_ENABLE(saa, MASK_06);
 	saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI);
 	saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI);
 
 
 	return 0;
 	return 0;
+
+out2:
+	input_free_device(input_dev);
+out1:
+	return error;
 }
 }
 
 
 static void msp430_ir_deinit(struct budget_ci *budget_ci)
 static void msp430_ir_deinit(struct budget_ci *budget_ci)
 {
 {
 	struct saa7146_dev *saa = budget_ci->budget.dev;
 	struct saa7146_dev *saa = budget_ci->budget.dev;
-	struct input_dev *dev = budget_ci->input_dev;
+	struct input_dev *dev = budget_ci->ir.dev;
 
 
-	saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_06);
+	SAA7146_IER_DISABLE(saa, MASK_06);
 	saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT);
 	saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT);
+	tasklet_kill(&budget_ci->ir.msp430_irq_tasklet);
 
 
-	if (del_timer(&dev->timer))
-		input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0);
+	if (del_timer(&dev->timer)) {
+		ir_input_nokey(dev, &budget_ci->ir.state);
+		input_sync(dev);
+	}
 
 
 	input_unregister_device(dev);
 	input_unregister_device(dev);
 }
 }
@@ -428,7 +485,7 @@ static int ciintf_init(struct budget_ci *budget_ci)
 	memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221));
 	memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221));
 
 
 	// enable DEBI pins
 	// enable DEBI pins
-	saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16) | 0x800);
+	saa7146_write(saa, MC1, MASK_27 | MASK_11);
 
 
 	// test if it is there
 	// test if it is there
 	ci_version = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0);
 	ci_version = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0);
@@ -480,7 +537,7 @@ static int ciintf_init(struct budget_ci *budget_ci)
 		} else {
 		} else {
 			saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
 			saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
 		}
 		}
-		saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_03);
+		SAA7146_IER_ENABLE(saa, MASK_03);
 	}
 	}
 
 
 	// enable interface
 	// enable interface
@@ -502,7 +559,7 @@ static int ciintf_init(struct budget_ci *budget_ci)
 	return 0;
 	return 0;
 
 
 error:
 error:
-	saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
+	saa7146_write(saa, MC1, MASK_27);
 	return result;
 	return result;
 }
 }
 
 
@@ -512,7 +569,7 @@ static void ciintf_deinit(struct budget_ci *budget_ci)
 
 
 	// disable CI interrupts
 	// disable CI interrupts
 	if (budget_ci->ci_irq) {
 	if (budget_ci->ci_irq) {
-		saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_03);
+		SAA7146_IER_DISABLE(saa, MASK_03);
 		saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT);
 		saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT);
 		tasklet_kill(&budget_ci->ciintf_irq_tasklet);
 		tasklet_kill(&budget_ci->ciintf_irq_tasklet);
 	}
 	}
@@ -530,7 +587,7 @@ static void ciintf_deinit(struct budget_ci *budget_ci)
 	dvb_ca_en50221_release(&budget_ci->ca);
 	dvb_ca_en50221_release(&budget_ci->ca);
 
 
 	// disable DEBI pins
 	// disable DEBI pins
-	saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
+	saa7146_write(saa, MC1, MASK_27);
 }
 }
 
 
 static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr)
 static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr)
@@ -540,7 +597,7 @@ static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr)
 	dprintk(8, "dev: %p, budget_ci: %p\n", dev, budget_ci);
 	dprintk(8, "dev: %p, budget_ci: %p\n", dev, budget_ci);
 
 
 	if (*isr & MASK_06)
 	if (*isr & MASK_06)
-		tasklet_schedule(&budget_ci->msp430_irq_tasklet);
+		tasklet_schedule(&budget_ci->ir.msp430_irq_tasklet);
 
 
 	if (*isr & MASK_10)
 	if (*isr & MASK_10)
 		ttpci_budget_irq10_handler(dev, isr);
 		ttpci_budget_irq10_handler(dev, isr);
@@ -835,7 +892,7 @@ static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe, struc
 		band = 1;
 		band = 1;
 	} else if (tuner_frequency < 200000000) {
 	} else if (tuner_frequency < 200000000) {
 		cp = 6;
 		cp = 6;
-		band = 1;
+		band = 2;
 	} else if (tuner_frequency < 290000000) {
 	} else if (tuner_frequency < 290000000) {
 		cp = 3;
 		cp = 3;
 		band = 2;
 		band = 2;
@@ -1083,24 +1140,23 @@ static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio
 	struct budget_ci *budget_ci;
 	struct budget_ci *budget_ci;
 	int err;
 	int err;
 
 
-	if (!(budget_ci = kmalloc(sizeof(struct budget_ci), GFP_KERNEL)))
-		return -ENOMEM;
+	budget_ci = kzalloc(sizeof(struct budget_ci), GFP_KERNEL);
+	if (!budget_ci) {
+		err = -ENOMEM;
+		goto out1;
+	}
 
 
 	dprintk(2, "budget_ci: %p\n", budget_ci);
 	dprintk(2, "budget_ci: %p\n", budget_ci);
 
 
-	budget_ci->budget.ci_present = 0;
-
 	dev->ext_priv = budget_ci;
 	dev->ext_priv = budget_ci;
 
 
-	if ((err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE))) {
-		kfree(budget_ci);
-		return err;
-	}
-
-	tasklet_init(&budget_ci->msp430_irq_tasklet, msp430_ir_interrupt,
-		     (unsigned long) budget_ci);
+	err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE);
+	if (err)
+		goto out2;
 
 
-	msp430_ir_init(budget_ci);
+	err = msp430_ir_init(budget_ci);
+	if (err)
+		goto out3;
 
 
 	ciintf_init(budget_ci);
 	ciintf_init(budget_ci);
 
 
@@ -1110,6 +1166,13 @@ static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio
 	ttpci_budget_init_hooks(&budget_ci->budget);
 	ttpci_budget_init_hooks(&budget_ci->budget);
 
 
 	return 0;
 	return 0;
+
+out3:
+	ttpci_budget_deinit(&budget_ci->budget);
+out2:
+	kfree(budget_ci);
+out1:
+	return err;
 }
 }
 
 
 static int budget_ci_detach(struct saa7146_dev *dev)
 static int budget_ci_detach(struct saa7146_dev *dev)
@@ -1120,16 +1183,13 @@ static int budget_ci_detach(struct saa7146_dev *dev)
 
 
 	if (budget_ci->budget.ci_present)
 	if (budget_ci->budget.ci_present)
 		ciintf_deinit(budget_ci);
 		ciintf_deinit(budget_ci);
+	msp430_ir_deinit(budget_ci);
 	if (budget_ci->budget.dvb_frontend) {
 	if (budget_ci->budget.dvb_frontend) {
 		dvb_unregister_frontend(budget_ci->budget.dvb_frontend);
 		dvb_unregister_frontend(budget_ci->budget.dvb_frontend);
 		dvb_frontend_detach(budget_ci->budget.dvb_frontend);
 		dvb_frontend_detach(budget_ci->budget.dvb_frontend);
 	}
 	}
 	err = ttpci_budget_deinit(&budget_ci->budget);
 	err = ttpci_budget_deinit(&budget_ci->budget);
 
 
-	tasklet_kill(&budget_ci->msp430_irq_tasklet);
-
-	msp430_ir_deinit(budget_ci);
-
 	// disable frontend and CI interface
 	// disable frontend and CI interface
 	saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT);
 	saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT);
 
 
@@ -1162,7 +1222,7 @@ MODULE_DEVICE_TABLE(pci, pci_tbl);
 
 
 static struct saa7146_extension budget_extension = {
 static struct saa7146_extension budget_extension = {
 	.name = "budget_ci dvb",
 	.name = "budget_ci dvb",
-	.flags = SAA7146_I2C_SHORT_DELAY,
+	.flags = SAA7146_USE_I2C_IRQ,
 
 
 	.module = THIS_MODULE,
 	.module = THIS_MODULE,
 	.pci_tbl = &pci_tbl[0],
 	.pci_tbl = &pci_tbl[0],

+ 1 - 1
drivers/media/dvb/ttpci/budget.c

@@ -555,7 +555,7 @@ MODULE_DEVICE_TABLE(pci, pci_tbl);
 
 
 static struct saa7146_extension budget_extension = {
 static struct saa7146_extension budget_extension = {
 	.name		= "budget dvb",
 	.name		= "budget dvb",
-	.flags		= SAA7146_I2C_SHORT_DELAY,
+	.flags		= SAA7146_USE_I2C_IRQ,
 
 
 	.module		= THIS_MODULE,
 	.module		= THIS_MODULE,
 	.pci_tbl	= pci_tbl,
 	.pci_tbl	= pci_tbl,

+ 9 - 2
drivers/media/dvb/ttusb-dec/ttusb_dec.c

@@ -238,6 +238,7 @@ static void ttusb_dec_handle_irq( struct urb *urb)
 		 * for now lets report each signal as a key down and up*/
 		 * for now lets report each signal as a key down and up*/
 		dprintk("%s:rc signal:%d\n", __FUNCTION__, buffer[4]);
 		dprintk("%s:rc signal:%d\n", __FUNCTION__, buffer[4]);
 		input_report_key(dec->rc_input_dev, rc_keys[buffer[4] - 1], 1);
 		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_report_key(dec->rc_input_dev, rc_keys[buffer[4] - 1], 0);
 		input_sync(dec->rc_input_dev);
 		input_sync(dec->rc_input_dev);
 	}
 	}
@@ -1187,11 +1188,12 @@ static int ttusb_init_rc( struct ttusb_dec *dec)
 	struct input_dev *input_dev;
 	struct input_dev *input_dev;
 	u8 b[] = { 0x00, 0x01 };
 	u8 b[] = { 0x00, 0x01 };
 	int i;
 	int i;
+	int err;
 
 
 	usb_make_path(dec->udev, dec->rc_phys, sizeof(dec->rc_phys));
 	usb_make_path(dec->udev, dec->rc_phys, sizeof(dec->rc_phys));
 	strlcpy(dec->rc_phys, "/input0", 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)
 	if (!input_dev)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
@@ -1205,8 +1207,13 @@ static int ttusb_init_rc( struct ttusb_dec *dec)
 	for (i = 0; i < ARRAY_SIZE(rc_keys); i++)
 	for (i = 0; i < ARRAY_SIZE(rc_keys); i++)
 		  set_bit(rc_keys[i], input_dev->keybit);
 		  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))
 	if (usb_submit_urb(dec->irq_urb, GFP_KERNEL))
 		printk("%s: usb_submit_urb failed\n",__FUNCTION__);
 		printk("%s: usb_submit_urb failed\n",__FUNCTION__);
 	/* enable irq pipe */
 	/* enable irq pipe */

+ 19 - 12
drivers/media/video/Kconfig

@@ -184,6 +184,14 @@ config VIDEO_KS0127
 	  To compile this driver as a module, choose M here: the
 	  To compile this driver as a module, choose M here: the
 	  module will be called ks0127.
 	  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
 config VIDEO_SAA7110
 	tristate "Philips SAA7110 video decoder"
 	tristate "Philips SAA7110 video decoder"
 	depends on VIDEO_V4L1 && I2C
 	depends on VIDEO_V4L1 && I2C
@@ -567,18 +575,6 @@ config VIDEO_ZORAN_AVS6EYES
 	help
 	help
 	  Support for the AverMedia 6 Eyes video surveillance card.
 	  Support for the AverMedia 6 Eyes video surveillance card.
 
 
-config VIDEO_ZR36120
-	tristate "Zoran ZR36120/36125 Video For Linux"
-	depends on PCI && I2C && VIDEO_V4L1 && BROKEN
-	help
-	  Support for ZR36120/ZR36125 based frame grabber/overlay boards.
-	  This includes the Victor II, WaveWatcher, Video Wonder, Maxi-TV,
-	  and Buster boards. Please read the material in
-	  <file:Documentation/video4linux/zr36120.txt> for more information.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called zr36120.
-
 config VIDEO_MEYE
 config VIDEO_MEYE
 	tristate "Sony Vaio Picturebook Motion Eye Video For Linux"
 	tristate "Sony Vaio Picturebook Motion Eye Video For Linux"
 	depends on PCI && SONYPI && VIDEO_V4L1
 	depends on PCI && SONYPI && VIDEO_V4L1
@@ -670,6 +666,15 @@ config VIDEO_M32R_AR_M64278
 	  To compile this driver as a module, choose M here: the
 	  To compile this driver as a module, choose M here: the
 	  module will be called arv.
 	  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
 # USB Multimedia device configuration
 #
 #
@@ -681,6 +686,8 @@ source "drivers/media/video/pvrusb2/Kconfig"
 
 
 source "drivers/media/video/em28xx/Kconfig"
 source "drivers/media/video/em28xx/Kconfig"
 
 
+source "drivers/media/video/usbvision/Kconfig"
+
 source "drivers/media/video/usbvideo/Kconfig"
 source "drivers/media/video/usbvideo/Kconfig"
 
 
 source "drivers/media/video/et61x251/Kconfig"
 source "drivers/media/video/et61x251/Kconfig"

+ 4 - 2
drivers/media/video/Makefile

@@ -2,7 +2,6 @@
 # Makefile for the video capture/playback device drivers.
 # Makefile for the video capture/playback device drivers.
 #
 #
 
 
-zoran-objs      :=	zr36120.o zr36120_i2c.o zr36120_mem.o
 zr36067-objs	:=	zoran_procfs.o zoran_device.o \
 zr36067-objs	:=	zoran_procfs.o zoran_device.o \
 			zoran_driver.o zoran_card.o
 			zoran_driver.o zoran_card.o
 tuner-objs	:=	tuner-core.o tuner-types.o tuner-simple.o \
 tuner-objs	:=	tuner-core.o tuner-types.o tuner-simple.o \
@@ -23,7 +22,6 @@ obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o
 obj-$(CONFIG_VIDEO_TDA9875) += tda9875.o
 obj-$(CONFIG_VIDEO_TDA9875) += tda9875.o
 obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o
 obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o
 
 
-obj-$(CONFIG_VIDEO_ZR36120) += zoran.o
 obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o
 obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o
 obj-$(CONFIG_VIDEO_SAA5246A) += saa5246a.o
 obj-$(CONFIG_VIDEO_SAA5246A) += saa5246a.o
 obj-$(CONFIG_VIDEO_SAA5249) += saa5249.o
 obj-$(CONFIG_VIDEO_SAA5249) += saa5249.o
@@ -64,6 +62,7 @@ obj-$(CONFIG_VIDEO_MEYE) += meye.o
 obj-$(CONFIG_VIDEO_SAA7134) += ir-kbd-i2c.o saa7134/
 obj-$(CONFIG_VIDEO_SAA7134) += ir-kbd-i2c.o saa7134/
 obj-$(CONFIG_VIDEO_CX88) += cx88/
 obj-$(CONFIG_VIDEO_CX88) += cx88/
 obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
 obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
+obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
 obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
 obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
 obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
 obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
 obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
 obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
@@ -92,6 +91,9 @@ obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
 obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
 obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
 obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.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_DABUSB)        += dabusb.o
 obj-$(CONFIG_USB_OV511)         += ov511.o
 obj-$(CONFIG_USB_OV511)         += ov511.o
 obj-$(CONFIG_USB_SE401)         += se401.o
 obj-$(CONFIG_USB_SE401)         += se401.o

+ 1 - 1
drivers/media/video/bt8xx/bttv-driver.c

@@ -1793,7 +1793,7 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
 		memset(i,0,sizeof(*i));
 		memset(i,0,sizeof(*i));
 		i->index    = n;
 		i->index    = n;
 		i->type     = V4L2_INPUT_TYPE_CAMERA;
 		i->type     = V4L2_INPUT_TYPE_CAMERA;
-		i->audioset = 0;
+		i->audioset = 1;
 		if (i->index == bttv_tvcards[btv->c.type].tuner) {
 		if (i->index == bttv_tvcards[btv->c.type].tuner) {
 			sprintf(i->name, "Television");
 			sprintf(i->name, "Television");
 			i->type  = V4L2_INPUT_TYPE_TUNER;
 			i->type  = V4L2_INPUT_TYPE_TUNER;

+ 57 - 44
drivers/media/video/bt8xx/bttv-input.c

@@ -259,24 +259,59 @@ static void bttv_rc5_timer_keyup(unsigned long data)
 
 
 /* ---------------------------------------------------------------------- */
 /* ---------------------------------------------------------------------- */
 
 
+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)
 int bttv_input_init(struct bttv *btv)
 {
 {
 	struct bttv_ir *ir;
 	struct bttv_ir *ir;
 	IR_KEYTAB_TYPE *ir_codes = NULL;
 	IR_KEYTAB_TYPE *ir_codes = NULL;
 	struct input_dev *input_dev;
 	struct input_dev *input_dev;
 	int ir_type = IR_TYPE_OTHER;
 	int ir_type = IR_TYPE_OTHER;
+	int err = -ENOMEM;
 
 
 	if (!btv->has_remote)
 	if (!btv->has_remote)
 		return -ENODEV;
 		return -ENODEV;
 
 
 	ir = kzalloc(sizeof(*ir),GFP_KERNEL);
 	ir = kzalloc(sizeof(*ir),GFP_KERNEL);
 	input_dev = input_allocate_device();
 	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 */
 	/* detect & configure */
 	switch (btv->c.type) {
 	switch (btv->c.type) {
@@ -348,10 +383,9 @@ int bttv_input_init(struct bttv *btv)
 		break;
 		break;
 	}
 	}
 	if (NULL == ir_codes) {
 	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) {
 	if (ir->rc5_gpio) {
@@ -389,32 +423,26 @@ int bttv_input_init(struct bttv *btv)
 	input_dev->cdev.dev = &btv->c.pci->dev;
 	input_dev->cdev.dev = &btv->c.pci->dev;
 
 
 	btv->remote = ir;
 	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 */
 	/* 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 */
 	/* the remote isn't as bouncy as a keyboard */
 	ir->dev->rep[REP_DELAY] = repeat_delay;
 	ir->dev->rep[REP_DELAY] = repeat_delay;
 	ir->dev->rep[REP_PERIOD] = repeat_period;
 	ir->dev->rep[REP_PERIOD] = repeat_period;
 
 
 	return 0;
 	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)
 void bttv_input_fini(struct bttv *btv)
@@ -422,22 +450,7 @@ void bttv_input_fini(struct bttv *btv)
 	if (btv->remote == NULL)
 	if (btv->remote == NULL)
 		return;
 		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);
 	input_unregister_device(btv->remote->dev);
 	kfree(btv->remote);
 	kfree(btv->remote);
 	btv->remote = NULL;
 	btv->remote = NULL;

+ 160 - 0
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

+ 2228 - 0
drivers/media/video/cafe_ccic.c

@@ -0,0 +1,2228 @@
+/*
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/jiffies.h>
+#include <linux/vmalloc.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#include "cafe_ccic-regs.h"
+
+#define CAFE_VERSION 0x000001
+
+
+/*
+ * Parameters.
+ */
+MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
+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;
+
+	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 (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;
+}
+
+/*
+ * G/S_PARM.  Most of this is done by the sensor, but we are
+ * the level which controls the number of read buffers.
+ */
+static int cafe_vidioc_g_parm(struct file *filp, void *priv,
+		struct v4l2_streamparm *parms)
+{
+	struct cafe_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = __cafe_cam_cmd(cam, VIDIOC_G_PARM, parms);
+	mutex_unlock(&cam->s_mutex);
+	parms->parm.capture.readbuffers = n_dma_bufs;
+	return ret;
+}
+
+static int cafe_vidioc_s_parm(struct file *filp, void *priv,
+		struct v4l2_streamparm *parms)
+{
+	struct cafe_camera *cam = priv;
+	int ret;
+
+	mutex_lock(&cam->s_mutex);
+	ret = __cafe_cam_cmd(cam, VIDIOC_S_PARM, parms);
+	mutex_unlock(&cam->s_mutex);
+	parms->parm.capture.readbuffers = n_dma_bufs;
+	return ret;
+}
+
+
+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,
+	.vidioc_g_parm		= cafe_vidioc_g_parm,
+	.vidioc_s_parm		= cafe_vidioc_s_parm,
+};
+
+
+
+
+
+
+
+/* ---------------------------------------------------------------------- */
+/*
+ * Interrupt handler stuff
+ */
+
+
+
+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);
+		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);

+ 1 - 0
drivers/media/video/cx88/Kconfig

@@ -53,6 +53,7 @@ config VIDEO_CX88_DVB
 	select DVB_OR51132 if !DVB_FE_CUSTOMISE
 	select DVB_OR51132 if !DVB_FE_CUSTOMISE
 	select DVB_CX22702 if !DVB_FE_CUSTOMISE
 	select DVB_CX22702 if !DVB_FE_CUSTOMISE
 	select DVB_LGDT330X 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_NXT200X if !DVB_FE_CUSTOMISE
 	select DVB_CX24123 if !DVB_FE_CUSTOMISE
 	select DVB_CX24123 if !DVB_FE_CUSTOMISE
 	select DVB_ISL6421 if !DVB_FE_CUSTOMISE
 	select DVB_ISL6421 if !DVB_FE_CUSTOMISE

+ 110 - 69
drivers/media/video/cx88/cx88-blackbird.c

@@ -50,7 +50,6 @@ MODULE_PARM_DESC(debug,"enable debug messages [blackbird]");
 #define dprintk(level,fmt, arg...)	if (debug >= level) \
 #define dprintk(level,fmt, arg...)	if (debug >= level) \
 	printk(KERN_DEBUG "%s/2-bb: " fmt, dev->core->name , ## arg)
 	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 *inode, struct file *file,
 				  BLACKBIRD_MPEG_CAPTURE,
 				  BLACKBIRD_MPEG_CAPTURE,
 				  BLACKBIRD_RAW_BITS_NONE);
 				  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);
 		blackbird_initialize_codec(dev);
 		cx88_set_scale(dev->core, dev->width, dev->height,
 		cx88_set_scale(dev->core, dev->width, dev->height,
@@ -914,11 +913,15 @@ static int mpeg_do_ioctl(struct inode *inode, struct file *file,
 	}
 	}
 
 
 	default:
 	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;
 	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)
 static unsigned int mpeg_translate_ioctl(unsigned int cmd)
 {
 {
 	return cmd;
 	return cmd;
@@ -927,33 +930,49 @@ static unsigned int mpeg_translate_ioctl(unsigned int cmd)
 static int mpeg_ioctl(struct inode *inode, struct file *file,
 static int mpeg_ioctl(struct inode *inode, struct file *file,
 			unsigned int cmd, unsigned long arg)
 			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)
 static int mpeg_open(struct inode *inode, struct file *file)
 {
 {
 	int minor = iminor(inode);
 	int minor = iminor(inode);
-	struct cx8802_dev *h,*dev = NULL;
+	struct cx8802_dev *dev = NULL;
 	struct cx8802_fh *fh;
 	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;
 		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;
 		return -EINVAL;
+	}
 	dprintk(1,"open minor=%d\n",minor);
 	dprintk(1,"open minor=%d\n",minor);
 
 
 	/* allocate + initialize per filehandle data */
 	/* allocate + initialize per filehandle data */
 	fh = kzalloc(sizeof(*fh),GFP_KERNEL);
 	fh = kzalloc(sizeof(*fh),GFP_KERNEL);
-	if (NULL == fh)
+	if (NULL == fh) {
+		if (drv)
+			drv->request_release(drv);
 		return -ENOMEM;
 		return -ENOMEM;
+	}
 	file->private_data = fh;
 	file->private_data = fh;
 	fh->dev      = dev;
 	fh->dev      = dev;
 
 
@@ -974,6 +993,8 @@ static int mpeg_open(struct inode *inode, struct file *file)
 static int mpeg_release(struct inode *inode, struct file *file)
 static int mpeg_release(struct inode *inode, struct file *file)
 {
 {
 	struct cx8802_fh  *fh  = file->private_data;
 	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, BLACKBIRD_END_NOW, 0, 0x13); */
 	blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
 	blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
@@ -992,6 +1013,16 @@ static int mpeg_release(struct inode *inode, struct file *file)
 	videobuf_mmap_free(&fh->mpegq);
 	videobuf_mmap_free(&fh->mpegq);
 	file->private_data = NULL;
 	file->private_data = NULL;
 	kfree(fh);
 	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;
 	return 0;
 }
 }
 
 
@@ -1043,6 +1074,44 @@ static struct video_device cx8802_mpeg_template =
 
 
 /* ------------------------------------------------------------------ */
 /* ------------------------------------------------------------------ */
 
 
+/* 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)
 static void blackbird_unregister_video(struct cx8802_dev *dev)
 {
 {
 	if (dev->mpeg_dev) {
 	if (dev->mpeg_dev) {
@@ -1073,28 +1142,23 @@ static int blackbird_register_video(struct cx8802_dev *dev)
 
 
 /* ----------------------------------------------------------- */
 /* ----------------------------------------------------------- */
 
 
-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;
 	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;
 	err = -ENODEV;
 	if (!(cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD))
 	if (!(cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD))
 		goto fail_core;
 		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->width = 720;
 	dev->height = 576;
 	dev->height = 576;
 	cx2341x_fill_defaults(&dev->params);
 	cx2341x_fill_defaults(&dev->params);
@@ -1106,64 +1170,36 @@ static int __devinit blackbird_probe(struct pci_dev *pci_dev,
 		dev->height = 576;
 		dev->height = 576;
 	}
 	}
 
 
-	err = cx8802_init_common(dev);
-	if (0 != err)
-		goto fail_free;
-
 	/* blackbird stuff */
 	/* blackbird stuff */
 	printk("%s/2: cx23416 based mpeg encoder (blackbird reference design)\n",
 	printk("%s/2: cx23416 based mpeg encoder (blackbird reference design)\n",
 	       core->name);
 	       core->name);
 	host_setup(dev->core);
 	host_setup(dev->core);
 
 
-	list_add_tail(&dev->devlist,&cx8802_devlist);
 	blackbird_register_video(dev);
 	blackbird_register_video(dev);
 
 
 	/* initial device configuration: needed ? */
 	/* initial device configuration: needed ? */
 
 
 	return 0;
 	return 0;
 
 
- fail_free:
-	kfree(dev);
  fail_core:
  fail_core:
-	cx88_core_put(core,pci_dev);
 	return err;
 	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 */
-	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)
 static int blackbird_init(void)
@@ -1176,17 +1212,22 @@ static int blackbird_init(void)
 	printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
 	printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
 	       SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
 	       SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
 #endif
 #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)
 static void blackbird_fini(void)
 {
 {
-	pci_unregister_driver(&blackbird_pci_driver);
+	cx8802_unregister_driver(&cx8802_blackbird_driver);
 }
 }
 
 
 module_init(blackbird_init);
 module_init(blackbird_init);
 module_exit(blackbird_fini);
 module_exit(blackbird_fini);
 
 
+EXPORT_SYMBOL(cx88_ioctl_hook);
+EXPORT_SYMBOL(cx88_ioctl_translator);
+
 /* ----------------------------------------------------------- */
 /* ----------------------------------------------------------- */
 /*
 /*
  * Local variables:
  * Local variables:

+ 54 - 32
drivers/media/video/cx88/cx88-cards.c

@@ -281,18 +281,22 @@ struct cx88_board cx88_boards[] = {
 			.type   = CX88_VMUX_TELEVISION,
 			.type   = CX88_VMUX_TELEVISION,
 			.vmux   = 0,
 			.vmux   = 0,
 			.gpio0  = 0x0000bde2,
 			.gpio0  = 0x0000bde2,
+			.extadc = 1,
 		},{
 		},{
 			.type   = CX88_VMUX_COMPOSITE1,
 			.type   = CX88_VMUX_COMPOSITE1,
 			.vmux   = 1,
 			.vmux   = 1,
 			.gpio0  = 0x0000bde6,
 			.gpio0  = 0x0000bde6,
+			.extadc = 1,
 		},{
 		},{
 			.type   = CX88_VMUX_SVIDEO,
 			.type   = CX88_VMUX_SVIDEO,
 			.vmux   = 2,
 			.vmux   = 2,
 			.gpio0  = 0x0000bde6,
 			.gpio0  = 0x0000bde6,
+			.extadc = 1,
 		}},
 		}},
 		.radio = {
 		.radio = {
 			.type   = CX88_RADIO,
 			.type   = CX88_RADIO,
 			.gpio0  = 0x0000bd62,
 			.gpio0  = 0x0000bd62,
+			.extadc = 1,
 		},
 		},
 		.mpeg           = CX88_MPEG_BLACKBIRD,
 		.mpeg           = CX88_MPEG_BLACKBIRD,
 	},
 	},
@@ -353,6 +357,7 @@ struct cx88_board cx88_boards[] = {
 			.type   = CX88_VMUX_SVIDEO,
 			.type   = CX88_VMUX_SVIDEO,
 			.vmux   = 2,
 			.vmux   = 2,
 			.gpio0  = 0x0000fde6, // 0x0000fda6 L,R RCA audio in?
 			.gpio0  = 0x0000fde6, // 0x0000fda6 L,R RCA audio in?
+			.extadc = 1,
 		}},
 		}},
 		.radio = {
 		.radio = {
 			.type   = CX88_RADIO,
 			.type   = CX88_RADIO,
@@ -523,6 +528,7 @@ struct cx88_board cx88_boards[] = {
 		.input          = {{
 		.input          = {{
 			.type   = CX88_VMUX_TELEVISION,
 			.type   = CX88_VMUX_TELEVISION,
 			.vmux   = 0,
 			.vmux   = 0,
+			.extadc = 1,
 		}},
 		}},
 		.mpeg           = CX88_MPEG_BLACKBIRD,
 		.mpeg           = CX88_MPEG_BLACKBIRD,
 	},
 	},
@@ -646,18 +652,22 @@ struct cx88_board cx88_boards[] = {
 			.type   = CX88_VMUX_TELEVISION,
 			.type   = CX88_VMUX_TELEVISION,
 			.vmux   = 0,
 			.vmux   = 0,
 			.gpio0  = 0x00009d80,
 			.gpio0  = 0x00009d80,
+			.extadc = 1,
 		},{
 		},{
 			.type   = CX88_VMUX_COMPOSITE1,
 			.type   = CX88_VMUX_COMPOSITE1,
 			.vmux   = 1,
 			.vmux   = 1,
 			.gpio0  = 0x00009d76,
 			.gpio0  = 0x00009d76,
+			.extadc = 1,
 		},{
 		},{
 			.type   = CX88_VMUX_SVIDEO,
 			.type   = CX88_VMUX_SVIDEO,
 			.vmux   = 2,
 			.vmux   = 2,
 			.gpio0  = 0x00009d76,
 			.gpio0  = 0x00009d76,
+			.extadc = 1,
 		}},
 		}},
 		.radio = {
 		.radio = {
 			.type   = CX88_RADIO,
 			.type   = CX88_RADIO,
 			.gpio0  = 0x00009d00,
 			.gpio0  = 0x00009d00,
+			.extadc = 1,
 		},
 		},
 		.mpeg           = CX88_MPEG_BLACKBIRD,
 		.mpeg           = CX88_MPEG_BLACKBIRD,
 	},
 	},
@@ -786,25 +796,29 @@ struct cx88_board cx88_boards[] = {
 		.tuner_addr     = ADDR_UNSET,
 		.tuner_addr     = ADDR_UNSET,
 		.radio_addr     = ADDR_UNSET,
 		.radio_addr     = ADDR_UNSET,
 		.tda9887_conf   = TDA9887_PRESENT,
 		.tda9887_conf   = TDA9887_PRESENT,
-		.mpeg           = CX88_MPEG_BLACKBIRD,
 		.input          = {{
 		.input          = {{
 			.type   = CX88_VMUX_COMPOSITE1,
 			.type   = CX88_VMUX_COMPOSITE1,
 			.vmux   = 0,
 			.vmux   = 0,
 			.gpio0  = 0x0000cd73,
 			.gpio0  = 0x0000cd73,
+			.extadc = 1,
 		},{
 		},{
 			.type   = CX88_VMUX_SVIDEO,
 			.type   = CX88_VMUX_SVIDEO,
 			.vmux   = 1,
 			.vmux   = 1,
 			.gpio0  = 0x0000cd73,
 			.gpio0  = 0x0000cd73,
+			.extadc = 1,
 		},{
 		},{
 			.type   = CX88_VMUX_TELEVISION,
 			.type   = CX88_VMUX_TELEVISION,
 			.vmux   = 3,
 			.vmux   = 3,
 			.gpio0  = 0x0000cdb3,
 			.gpio0  = 0x0000cdb3,
+			.extadc = 1,
 		}},
 		}},
 		.radio = {
 		.radio = {
 			.type   = CX88_RADIO,
 			.type   = CX88_RADIO,
 			.vmux   = 2,
 			.vmux   = 2,
 			.gpio0  = 0x0000cdf3,
 			.gpio0  = 0x0000cdf3,
+			.extadc = 1,
 		},
 		},
+		.mpeg           = CX88_MPEG_BLACKBIRD,
 	},
 	},
 	[CX88_BOARD_KWORLD_VSTREAM_EXPERT_DVD] = {
 	[CX88_BOARD_KWORLD_VSTREAM_EXPERT_DVD] = {
 		 /* Alexander Wold <awold@bigfoot.com> */
 		 /* Alexander Wold <awold@bigfoot.com> */
@@ -1050,7 +1064,6 @@ struct cx88_board cx88_boards[] = {
 		.mpeg           = CX88_MPEG_DVB,
 		.mpeg           = CX88_MPEG_DVB,
 	},
 	},
 	[CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT] = {
 	[CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT] = {
-		/* FIXME: Audio not working for s-video / composite inputs. */
 		.name           = "KWorld HardwareMpegTV XPert",
 		.name           = "KWorld HardwareMpegTV XPert",
 		.tuner_type     = TUNER_PHILIPS_TDA8290,
 		.tuner_type     = TUNER_PHILIPS_TDA8290,
 		.radio_type     = UNSET,
 		.radio_type     = UNSET,
@@ -1065,10 +1078,12 @@ struct cx88_board cx88_boards[] = {
 			.type   = CX88_VMUX_COMPOSITE1,
 			.type   = CX88_VMUX_COMPOSITE1,
 			.vmux   = 1,
 			.vmux   = 1,
 			.gpio0  = 0x3de6,
 			.gpio0  = 0x3de6,
+			.extadc = 1,
 		},{
 		},{
 			.type   = CX88_VMUX_SVIDEO,
 			.type   = CX88_VMUX_SVIDEO,
 			.vmux   = 2,
 			.vmux   = 2,
 			.gpio0  = 0x3de6,
 			.gpio0  = 0x3de6,
+			.extadc = 1,
 		}},
 		}},
 		.radio = {
 		.radio = {
 			.type   = CX88_RADIO,
 			.type   = CX88_RADIO,
@@ -1252,35 +1267,35 @@ struct cx88_board cx88_boards[] = {
 			.gpio0  = 0x070b,
 			.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] = {
 	[CX88_BOARD_HAUPPAUGE_HVR1300] = {
 		.name		= "Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder",
 		.name		= "Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder",
 		.tuner_type     = TUNER_PHILIPS_FMD1216ME_MK3,
 		.tuner_type     = TUNER_PHILIPS_FMD1216ME_MK3,
@@ -1293,17 +1308,20 @@ struct cx88_board cx88_boards[] = {
 			.type   = CX88_VMUX_TELEVISION,
 			.type   = CX88_VMUX_TELEVISION,
 			.vmux   = 0,
 			.vmux   = 0,
 			.gpio0	= 0xe780,
 			.gpio0	= 0xe780,
+			.extadc = 1,
 		},{
 		},{
 			.type	= CX88_VMUX_COMPOSITE1,
 			.type	= CX88_VMUX_COMPOSITE1,
 			.vmux	= 1,
 			.vmux	= 1,
 			.gpio0	= 0xe780,
 			.gpio0	= 0xe780,
+			.extadc = 1,
 		},{
 		},{
 			.type	= CX88_VMUX_SVIDEO,
 			.type	= CX88_VMUX_SVIDEO,
 			.vmux	= 2,
 			.vmux	= 2,
 			.gpio0	= 0xe780,
 			.gpio0	= 0xe780,
+			.extadc = 1,
 		}},
 		}},
 		/* fixme: Add radio support */
 		/* fixme: Add radio support */
-		.mpeg           = CX88_MPEG_DVB,
+		.mpeg           = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD,
 	},
 	},
 };
 };
 const unsigned int cx88_bcount = ARRAY_SIZE(cx88_boards);
 const unsigned int cx88_bcount = ARRAY_SIZE(cx88_boards);
@@ -1513,6 +1531,10 @@ struct cx88_subid cx88_subids[] = {
 	},{
 	},{
 		.subvendor = 0x17de,
 		.subvendor = 0x17de,
 		.subdevice = 0x0840,
 		.subdevice = 0x0840,
+	       .card      = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
+       },{
+	       .subvendor = 0x1421,
+	       .subdevice = 0x0305,
 		.card      = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
 		.card      = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
 	},{
 	},{
 		.subvendor = 0x18ac,
 		.subvendor = 0x18ac,

+ 132 - 201
drivers/media/video/cx88/cx88-dvb.c

@@ -42,7 +42,7 @@
 #include "cx22702.h"
 #include "cx22702.h"
 #include "or51132.h"
 #include "or51132.h"
 #include "lgdt330x.h"
 #include "lgdt330x.h"
-#include "lg_h06xf.h"
+#include "lgh06xf.h"
 #include "nxt200x.h"
 #include "nxt200x.h"
 #include "cx24123.h"
 #include "cx24123.h"
 #include "isl6421.h"
 #include "isl6421.h"
@@ -57,7 +57,7 @@ module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug,"enable debug messages [dvb]");
 MODULE_PARM_DESC(debug,"enable debug messages [dvb]");
 
 
 #define dprintk(level,fmt, arg...)	if (debug >= level) \
 #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)
 
 
 /* ------------------------------------------------------------------ */
 /* ------------------------------------------------------------------ */
 
 
@@ -74,8 +74,8 @@ static int dvb_buf_setup(struct videobuf_queue *q,
 	return 0;
 	return 0;
 }
 }
 
 
-static int dvb_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
-			   enum v4l2_field field)
+static int dvb_buf_prepare(struct videobuf_queue *q,
+			   struct videobuf_buffer *vb, enum v4l2_field field)
 {
 {
 	struct cx8802_dev *dev = q->priv_data;
 	struct cx8802_dev *dev = q->priv_data;
 	return cx8802_buf_prepare(q, dev, (struct cx88_buffer*)vb,field);
 	return cx8802_buf_prepare(q, dev, (struct cx88_buffer*)vb,field);
@@ -87,7 +87,8 @@ static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
 	cx8802_buf_queue(dev, (struct cx88_buffer*)vb);
 	cx8802_buf_queue(dev, (struct cx88_buffer*)vb);
 }
 }
 
 
-static void dvb_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+static void dvb_buf_release(struct videobuf_queue *q,
+			    struct videobuf_buffer *vb)
 {
 {
 	cx88_free_buffer(q, (struct cx88_buffer*)vb);
 	cx88_free_buffer(q, (struct cx88_buffer*)vb);
 }
 }
@@ -100,6 +101,26 @@ static struct videobuf_queue_ops dvb_qops = {
 };
 };
 
 
 /* ------------------------------------------------------------------ */
 /* ------------------------------------------------------------------ */
+
+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 int dvico_fusionhdtv_demod_init(struct dvb_frontend* fe)
 static int dvico_fusionhdtv_demod_init(struct dvb_frontend* fe)
 {
 {
 	static u8 clock_config []  = { CLOCK_CTL,  0x38, 0x39 };
 	static u8 clock_config []  = { CLOCK_CTL,  0x38, 0x39 };
@@ -268,35 +289,6 @@ static struct mt352_config dntv_live_dvbt_pro_config = {
 };
 };
 #endif
 #endif
 
 
-static int dvico_hybrid_tuner_set_params(struct dvb_frontend *fe,
-					 struct dvb_frontend_parameters *params)
-{
-	u8 pllbuf[4];
-	struct cx8802_dev *dev= fe->dvb->priv;
-	struct i2c_msg msg =
-		{ .addr = dev->core->pll_addr, .flags = 0,
-		  .buf = pllbuf, .len = 4 };
-	int err;
-
-	dvb_pll_configure(dev->core->pll_desc, pllbuf,
-			  params->frequency,
-			  params->u.ofdm.bandwidth);
-
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-	if ((err = i2c_transfer(&dev->core->i2c_adap, &msg, 1)) != 1) {
-		printk(KERN_WARNING "cx88-dvb: %s error "
-		       "(addr %02x <- %02x, err = %i)\n",
-		       __FUNCTION__, pllbuf[0], pllbuf[1], err);
-		if (err < 0)
-			return err;
-		else
-			return -EREMOTEIO;
-	}
-
-	return 0;
-}
-
 static struct zl10353_config dvico_fusionhdtv_hybrid = {
 static struct zl10353_config dvico_fusionhdtv_hybrid = {
 	.demod_address = 0x0f,
 	.demod_address = 0x0f,
 	.no_tuner      = 1,
 	.no_tuner      = 1,
@@ -311,28 +303,12 @@ static struct cx22702_config connexant_refboard_config = {
 	.output_mode   = CX22702_SERIAL_OUTPUT,
 	.output_mode   = CX22702_SERIAL_OUTPUT,
 };
 };
 
 
-static struct cx22702_config hauppauge_novat_config = {
-	.demod_address = 0x43,
-	.output_mode   = CX22702_SERIAL_OUTPUT,
-};
-
-static struct cx22702_config hauppauge_hvr1100_config = {
+static struct cx22702_config hauppauge_hvr_config = {
 	.demod_address = 0x63,
 	.demod_address = 0x63,
 	.output_mode   = CX22702_SERIAL_OUTPUT,
 	.output_mode   = CX22702_SERIAL_OUTPUT,
 };
 };
 
 
-static struct cx22702_config hauppauge_hvr1300_config = {
-	.demod_address = 0x63,
-	.output_mode   = CX22702_SERIAL_OUTPUT,
-};
-
-static struct cx22702_config hauppauge_hvr3000_config = {
-	.demod_address = 0x63,
-	.output_mode = CX22702_SERIAL_OUTPUT,
-};
-
-static int or51132_set_ts_param(struct dvb_frontend* fe,
-				int is_punctured)
+static int or51132_set_ts_param(struct dvb_frontend* fe, int is_punctured)
 {
 {
 	struct cx8802_dev *dev= fe->dvb->priv;
 	struct cx8802_dev *dev= fe->dvb->priv;
 	dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00;
 	dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00;
@@ -344,50 +320,6 @@ static struct or51132_config pchdtv_hd3000 = {
 	.set_ts_params = or51132_set_ts_param,
 	.set_ts_params = or51132_set_ts_param,
 };
 };
 
 
-static int lgdt3302_tuner_set_params(struct dvb_frontend* fe,
-				     struct dvb_frontend_parameters* params)
-{
-	/* FIXME make this routine use the tuner-simple code.
-	 * It could probably be shared with a number of ATSC
-	 * frontends. Many share the same tuner with analog TV. */
-
-	struct cx8802_dev *dev= fe->dvb->priv;
-	struct cx88_core *core = dev->core;
-	u8 buf[4];
-	struct i2c_msg msg =
-		{ .addr = dev->core->pll_addr, .flags = 0, .buf = buf, .len = 4 };
-	int err;
-
-	dvb_pll_configure(core->pll_desc, buf, params->frequency, 0);
-	dprintk(1, "%s: tuner at 0x%02x bytes: 0x%02x 0x%02x 0x%02x 0x%02x\n",
-		__FUNCTION__, msg.addr, buf[0],buf[1],buf[2],buf[3]);
-
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-	if ((err = i2c_transfer(&core->i2c_adap, &msg, 1)) != 1) {
-		printk(KERN_WARNING "cx88-dvb: %s error "
-		       "(addr %02x <- %02x, err = %i)\n",
-		       __FUNCTION__, buf[0], buf[1], err);
-		if (err < 0)
-			return err;
-		else
-			return -EREMOTEIO;
-	}
-	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)
 static int lgdt330x_pll_rf_set(struct dvb_frontend* fe, int index)
 {
 {
 	struct cx8802_dev *dev= fe->dvb->priv;
 	struct cx8802_dev *dev= fe->dvb->priv;
@@ -432,8 +364,7 @@ static struct lgdt330x_config pchdtv_hd5500 = {
 	.set_ts_params = lgdt330x_set_ts_param,
 	.set_ts_params = lgdt330x_set_ts_param,
 };
 };
 
 
-static int nxt200x_set_ts_param(struct dvb_frontend* fe,
-				int is_punctured)
+static int nxt200x_set_ts_param(struct dvb_frontend* fe, int is_punctured)
 {
 {
 	struct cx8802_dev *dev= fe->dvb->priv;
 	struct cx8802_dev *dev= fe->dvb->priv;
 	dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00;
 	dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00;
@@ -469,11 +400,10 @@ static int kworld_dvbs_100_set_voltage(struct dvb_frontend* fe,
 	struct cx8802_dev *dev= fe->dvb->priv;
 	struct cx8802_dev *dev= fe->dvb->priv;
 	struct cx88_core *core = dev->core;
 	struct cx88_core *core = dev->core;
 
 
-	if (voltage == SEC_VOLTAGE_OFF) {
+	if (voltage == SEC_VOLTAGE_OFF)
 		cx_write(MO_GP0_IO, 0x000006fb);
 		cx_write(MO_GP0_IO, 0x000006fb);
-	} else {
+	else
 		cx_write(MO_GP0_IO, 0x000006f9);
 		cx_write(MO_GP0_IO, 0x000006f9);
-	}
 
 
 	if (core->prev_set_voltage)
 	if (core->prev_set_voltage)
 		return core->prev_set_voltage(fe, voltage);
 		return core->prev_set_voltage(fe, voltage);
@@ -522,7 +452,7 @@ static int dvb_register(struct cx8802_dev *dev)
 	switch (dev->core->board) {
 	switch (dev->core->board) {
 	case CX88_BOARD_HAUPPAUGE_DVB_T1:
 	case CX88_BOARD_HAUPPAUGE_DVB_T1:
 		dev->dvb.frontend = dvb_attach(cx22702_attach,
 		dev->dvb.frontend = dvb_attach(cx22702_attach,
-					       &hauppauge_novat_config,
+					       &connexant_refboard_config,
 					       &dev->core->i2c_adap);
 					       &dev->core->i2c_adap);
 		if (dev->dvb.frontend != NULL) {
 		if (dev->dvb.frontend != NULL) {
 			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
 			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
@@ -547,32 +477,11 @@ static int dvb_register(struct cx8802_dev *dev)
 	case CX88_BOARD_HAUPPAUGE_HVR1100:
 	case CX88_BOARD_HAUPPAUGE_HVR1100:
 	case CX88_BOARD_HAUPPAUGE_HVR1100LP:
 	case CX88_BOARD_HAUPPAUGE_HVR1100LP:
 		dev->dvb.frontend = dvb_attach(cx22702_attach,
 		dev->dvb.frontend = dvb_attach(cx22702_attach,
-					       &hauppauge_hvr1100_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_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,
+					       &hauppauge_hvr_config,
 					       &dev->core->i2c_adap);
 					       &dev->core->i2c_adap);
 		if (dev->dvb.frontend != NULL) {
 		if (dev->dvb.frontend != NULL) {
 			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
 			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
-				   &dev->core->i2c_adap,
-				   &dvb_pll_fmd1216me);
+				   &dev->core->i2c_adap, &dvb_pll_fmd1216me);
 		}
 		}
 		break;
 		break;
 	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS:
 	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS:
@@ -647,18 +556,17 @@ static int dvb_register(struct cx8802_dev *dev)
 #endif
 #endif
 		break;
 		break;
 	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID:
 	case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID:
-		dev->core->pll_addr = 0x61;
-		dev->core->pll_desc = &dvb_pll_thomson_fe6600;
 		dev->dvb.frontend = dvb_attach(zl10353_attach,
 		dev->dvb.frontend = dvb_attach(zl10353_attach,
 					       &dvico_fusionhdtv_hybrid,
 					       &dvico_fusionhdtv_hybrid,
 					       &dev->core->i2c_adap);
 					       &dev->core->i2c_adap);
 		if (dev->dvb.frontend != NULL) {
 		if (dev->dvb.frontend != NULL) {
-			dev->dvb.frontend->ops.tuner_ops.set_params = dvico_hybrid_tuner_set_params;
+			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+				   &dev->core->i2c_adap,
+				   &dvb_pll_thomson_fe6600);
 		}
 		}
 		break;
 		break;
 	case CX88_BOARD_PCHDTV_HD3000:
 	case CX88_BOARD_PCHDTV_HD3000:
-		dev->dvb.frontend = dvb_attach(or51132_attach,
-					       &pchdtv_hd3000,
+		dev->dvb.frontend = dvb_attach(or51132_attach, &pchdtv_hd3000,
 					       &dev->core->i2c_adap);
 					       &dev->core->i2c_adap);
 		if (dev->dvb.frontend != NULL) {
 		if (dev->dvb.frontend != NULL) {
 			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
 			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
@@ -679,13 +587,13 @@ static int dvb_register(struct cx8802_dev *dev)
 
 
 		/* Select RF connector callback */
 		/* Select RF connector callback */
 		fusionhdtv_3_gold.pll_rf_set = lgdt330x_pll_rf_set;
 		fusionhdtv_3_gold.pll_rf_set = lgdt330x_pll_rf_set;
-		dev->core->pll_addr = 0x61;
-		dev->core->pll_desc = &dvb_pll_microtune_4042;
 		dev->dvb.frontend = dvb_attach(lgdt330x_attach,
 		dev->dvb.frontend = dvb_attach(lgdt330x_attach,
 					       &fusionhdtv_3_gold,
 					       &fusionhdtv_3_gold,
 					       &dev->core->i2c_adap);
 					       &dev->core->i2c_adap);
 		if (dev->dvb.frontend != NULL) {
 		if (dev->dvb.frontend != NULL) {
-			dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3302_tuner_set_params;
+			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+				   &dev->core->i2c_adap,
+				   &dvb_pll_microtune_4042);
 		}
 		}
 		}
 		}
 		break;
 		break;
@@ -699,13 +607,13 @@ static int dvb_register(struct cx8802_dev *dev)
 		mdelay(100);
 		mdelay(100);
 		cx_set(MO_GP0_IO, 9);
 		cx_set(MO_GP0_IO, 9);
 		mdelay(200);
 		mdelay(200);
-		dev->core->pll_addr = 0x61;
-		dev->core->pll_desc = &dvb_pll_thomson_dtt761x;
 		dev->dvb.frontend = dvb_attach(lgdt330x_attach,
 		dev->dvb.frontend = dvb_attach(lgdt330x_attach,
 					       &fusionhdtv_3_gold,
 					       &fusionhdtv_3_gold,
 					       &dev->core->i2c_adap);
 					       &dev->core->i2c_adap);
 		if (dev->dvb.frontend != NULL) {
 		if (dev->dvb.frontend != NULL) {
-			dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3302_tuner_set_params;
+			dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+				   &dev->core->i2c_adap,
+				   &dvb_pll_thomson_dtt761x);
 		}
 		}
 		}
 		}
 		break;
 		break;
@@ -723,7 +631,8 @@ static int dvb_register(struct cx8802_dev *dev)
 					       &fusionhdtv_5_gold,
 					       &fusionhdtv_5_gold,
 					       &dev->core->i2c_adap);
 					       &dev->core->i2c_adap);
 		if (dev->dvb.frontend != NULL) {
 		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;
 		break;
@@ -741,7 +650,8 @@ static int dvb_register(struct cx8802_dev *dev)
 					       &pchdtv_hd5500,
 					       &pchdtv_hd5500,
 					       &dev->core->i2c_adap);
 					       &dev->core->i2c_adap);
 		if (dev->dvb.frontend != NULL) {
 		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;
 		break;
@@ -782,6 +692,24 @@ static int dvb_register(struct cx8802_dev *dev)
 			dev->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage;
 			dev->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage;
 		}
 		}
 		break;
 		break;
+	case CX88_BOARD_HAUPPAUGE_HVR1300:
+		dev->dvb.frontend = dvb_attach(cx22702_attach,
+					       &hauppauge_hvr_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_hvr_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:
 	default:
 		printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n",
 		printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n",
 		       dev->core->name);
 		       dev->core->name);
@@ -796,6 +724,8 @@ static int dvb_register(struct cx8802_dev *dev)
 		dev->dvb.frontend->ops.info.frequency_min = dev->core->pll_desc->min;
 		dev->dvb.frontend->ops.info.frequency_min = dev->core->pll_desc->min;
 		dev->dvb.frontend->ops.info.frequency_max = dev->core->pll_desc->max;
 		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 */
 	/* Put the analog decoder in standby to keep it quiet */
 	cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL);
 	cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL);
@@ -806,37 +736,67 @@ static int dvb_register(struct cx8802_dev *dev)
 
 
 /* ----------------------------------------------------------- */
 /* ----------------------------------------------------------- */
 
 
-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 cx8802_dev *dev;
-	struct cx88_core  *core;
+	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 cx88_core *core = drv->core;
+	struct cx8802_dev *dev = drv->core->dvbdev;
 	int err;
 	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;
 	err = -ENODEV;
 	if (!(cx88_boards[core->board].mpeg & CX88_MPEG_DVB))
 	if (!(cx88_boards[core->board].mpeg & CX88_MPEG_DVB))
 		goto fail_core;
 		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
 #ifdef HAVE_VP3054_I2C
 	err = vp3054_i2c_probe(dev);
 	err = vp3054_i2c_probe(dev);
 	if (0 != err)
 	if (0 != err)
-		goto fail_free;
+		goto fail_core;
 #endif
 #endif
 
 
 	/* dvb stuff */
 	/* dvb stuff */
@@ -848,28 +808,16 @@ static int __devinit dvb_probe(struct pci_dev *pci_dev,
 			    sizeof(struct cx88_buffer),
 			    sizeof(struct cx88_buffer),
 			    dev);
 			    dev);
 	err = dvb_register(dev);
 	err = dvb_register(dev);
-	if (0 != err)
-		goto fail_fini;
+	if (err != 0)
+		printk("%s dvb_register failed err = %d\n", __FUNCTION__, err);
 
 
-	/* Maintain a reference to cx88-video can query the 8802 device. */
-	core->dvbdev = dev;
-	return 0;
-
- fail_fini:
-	cx8802_fini_common(dev);
- fail_free:
-	kfree(dev);
  fail_core:
  fail_core:
-	cx88_core_put(core,pci_dev);
 	return err;
 	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 */
 	/* dvb */
 	videobuf_dvb_unregister(&dev->dvb);
 	videobuf_dvb_unregister(&dev->dvb);
@@ -878,33 +826,16 @@ static void __devexit dvb_remove(struct pci_dev *pci_dev)
 	vp3054_i2c_remove(dev);
 	vp3054_i2c_remove(dev);
 #endif
 #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)
 static int dvb_init(void)
@@ -917,12 +848,12 @@ static int dvb_init(void)
 	printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
 	printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
 	       SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
 	       SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
 #endif
 #endif
-	return pci_register_driver(&dvb_pci_driver);
+	return cx8802_register_driver(&cx8802_dvb_driver);
 }
 }
 
 
 static void dvb_fini(void)
 static void dvb_fini(void)
 {
 {
-	pci_unregister_driver(&dvb_pci_driver);
+	cx8802_unregister_driver(&cx8802_dvb_driver);
 }
 }
 
 
 module_init(dvb_init);
 module_init(dvb_init);

+ 47 - 30
drivers/media/video/cx88/cx88-input.c

@@ -155,6 +155,35 @@ static void cx88_ir_work(struct work_struct *work)
 	mod_timer(&ir->timer, timeout);
 	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);
+		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)
 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 pci_dev *pci)
 	struct input_dev *input_dev;
 	struct input_dev *input_dev;
 	IR_KEYTAB_TYPE *ir_codes = NULL;
 	IR_KEYTAB_TYPE *ir_codes = NULL;
 	int ir_type = IR_TYPE_OTHER;
 	int ir_type = IR_TYPE_OTHER;
+	int err = -ENOMEM;
 
 
 	ir = kzalloc(sizeof(*ir), GFP_KERNEL);
 	ir = kzalloc(sizeof(*ir), GFP_KERNEL);
 	input_dev = input_allocate_device();
 	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;
 	ir->input = input_dev;
 
 
@@ -280,9 +307,8 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
 	}
 	}
 
 
 	if (NULL == ir_codes) {
 	if (NULL == ir_codes) {
-		kfree(ir);
-		input_free_device(input_dev);
-		return -ENODEV;
+		err = -ENODEV;
+		goto err_out_free;
 	}
 	}
 
 
 	/* init input device */
 	/* init input device */
@@ -307,23 +333,22 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
 	ir->core = core;
 	ir->core = core;
 	core->ir = ir;
 	core->ir = ir;
 
 
-	if (ir->polling) {
-		INIT_WORK(&ir->work, cx88_ir_work);
-		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 */
 	/* all done */
-	input_register_device(ir->input);
+	err = input_register_device(ir->input);
+	if (err)
+		goto err_out_stop;
 
 
 	return 0;
 	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)
 int cx88_ir_fini(struct cx88_core *core)
@@ -334,15 +359,7 @@ int cx88_ir_fini(struct cx88_core *core)
 	if (NULL == ir)
 	if (NULL == ir)
 		return 0;
 		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);
 	input_unregister_device(ir->input);
 	kfree(ir);
 	kfree(ir);
 
 

+ 331 - 17
drivers/media/video/cx88/cx88-mpeg.c

@@ -44,8 +44,12 @@ module_param(debug,int,0644);
 MODULE_PARM_DESC(debug,"enable debug messages [mpeg]");
 MODULE_PARM_DESC(debug,"enable debug messages [mpeg]");
 
 
 #define dprintk(level,fmt, arg...)	if (debug >= level) \
 #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,
 static int cx8802_start_dma(struct cx8802_dev    *dev,
@@ -65,17 +69,13 @@ static int cx8802_start_dma(struct cx8802_dev    *dev,
 
 
 	/* FIXME: this needs a review.
 	/* FIXME: this needs a review.
 	 * also: move to cx88-blackbird + cx88-dvb source files? */
 	 * 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 */
 		/* negedge driven & software reset */
 		cx_write(TS_GEN_CNTRL, 0x0040 | dev->ts_gen_cntrl);
 		cx_write(TS_GEN_CNTRL, 0x0040 | dev->ts_gen_cntrl);
 		udelay(100);
 		udelay(100);
@@ -93,15 +93,17 @@ static int cx8802_start_dma(struct cx8802_dev    *dev,
 			cx_write(MO_PINMUX_IO, 0x88); /* Enable MPEG parallel IO and video signal pins */
 			cx_write(MO_PINMUX_IO, 0x88); /* Enable MPEG parallel IO and video signal pins */
 			udelay(100);
 			udelay(100);
 			break;
 			break;
+		case CX88_BOARD_HAUPPAUGE_HVR1300:
+			break;
 		default:
 		default:
 			cx_write(TS_SOP_STAT, 0x00);
 			cx_write(TS_SOP_STAT, 0x00);
 			break;
 			break;
 		}
 		}
 		cx_write(TS_GEN_CNTRL, dev->ts_gen_cntrl);
 		cx_write(TS_GEN_CNTRL, dev->ts_gen_cntrl);
 		udelay(100);
 		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(MO_PINMUX_IO, 0x88); /* enable MPEG parallel IO */
 
 
 		cx_write(TS_GEN_CNTRL, 0x46); /* punctured clock TS & posedge driven & software reset */
 		cx_write(TS_GEN_CNTRL, 0x46); /* punctured clock TS & posedge driven & software reset */
@@ -112,6 +114,10 @@ static int cx8802_start_dma(struct cx8802_dev    *dev,
 
 
 		cx_write(TS_GEN_CNTRL, 0x06); /* punctured clock TS & posedge driven */
 		cx_write(TS_GEN_CNTRL, 0x06); /* punctured clock TS & posedge driven */
 		udelay(100);
 		udelay(100);
+	} else {
+		printk( "%s() Failed. Unsupported value in .mpeg (0x%08x)\n", __FUNCTION__,
+			cx88_boards[core->board].mpeg );
+		return -EINVAL;
 	}
 	}
 
 
 	/* reset counter */
 	/* reset counter */
@@ -542,8 +548,315 @@ int cx8802_resume_common(struct pci_dev *pci_dev)
 	return 0;
 	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_prepare);
 EXPORT_SYMBOL(cx8802_buf_queue);
 EXPORT_SYMBOL(cx8802_buf_queue);
 EXPORT_SYMBOL(cx8802_cancel_buffers);
 EXPORT_SYMBOL(cx8802_cancel_buffers);
@@ -551,9 +864,10 @@ EXPORT_SYMBOL(cx8802_cancel_buffers);
 EXPORT_SYMBOL(cx8802_init_common);
 EXPORT_SYMBOL(cx8802_init_common);
 EXPORT_SYMBOL(cx8802_fini_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:
  * Local variables:

+ 0 - 13
drivers/media/video/cx88/cx88-tvaudio.c

@@ -143,19 +143,6 @@ static void set_audio_finish(struct cx88_core *core, u32 ctl)
 	cx88_start_audio_dma(core);
 	cx88_start_audio_dma(core);
 
 
 	if (cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD) {
 	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_I2SINPUTCNTL, 4);
 		cx_write(AUD_BAUDRATE, 1);
 		cx_write(AUD_BAUDRATE, 1);
 		/* 'pass-thru mode': this enables the i2s output to the mpeg encoder */
 		/* 'pass-thru mode': this enables the i2s output to the mpeg encoder */

+ 32 - 0
drivers/media/video/cx88/cx88-video.c

@@ -454,6 +454,14 @@ static int video_mux(struct cx88_core *core, unsigned int input)
 		cx_clear(MO_FILTER_ODD,   0x00002020);
 		cx_clear(MO_FILTER_ODD,   0x00002020);
 		break;
 		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;
 	return 0;
 }
 }
 
 
@@ -1490,6 +1498,30 @@ int cx88_do_ioctl(struct inode *inode, struct file *file, int radio,
 		mutex_unlock(&core->lock);
 		mutex_unlock(&core->lock);
 		return 0;
 		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:
 	default:
 		return v4l_compat_translate_ioctl(inode,file,cmd,arg,
 		return v4l_compat_translate_ioctl(inode,file,cmd,arg,

+ 47 - 0
drivers/media/video/cx88/cx88.h

@@ -74,6 +74,11 @@ enum cx88_board_type {
 	CX88_MPEG_BLACKBIRD
 	CX88_MPEG_BLACKBIRD
 };
 };
 
 
+enum cx8802_board_access {
+	CX8802_DRVCTL_SHARED    = 1,
+	CX8802_DRVCTL_EXCLUSIVE = 2,
+};
+
 /* ----------------------------------------------------------- */
 /* ----------------------------------------------------------- */
 /* tv norms                                                    */
 /* tv norms                                                    */
 
 
@@ -220,6 +225,7 @@ struct cx88_input {
 	enum cx88_itype type;
 	enum cx88_itype type;
 	unsigned int    vmux;
 	unsigned int    vmux;
 	u32             gpio0, gpio1, gpio2, gpio3;
 	u32             gpio0, gpio1, gpio2, gpio3;
+	unsigned int    extadc:1;
 };
 };
 
 
 struct cx88_board {
 struct cx88_board {
@@ -330,6 +336,7 @@ struct cx88_core {
 
 
 	/* cx88-video needs to access cx8802 for hybrid tuner pll access. */
 	/* cx88-video needs to access cx8802 for hybrid tuner pll access. */
 	struct cx8802_dev          *dvbdev;
 	struct cx8802_dev          *dvbdev;
+	enum cx88_board_type       active_type_id;
 };
 };
 
 
 struct cx8800_dev;
 struct cx8800_dev;
@@ -405,6 +412,31 @@ struct cx8802_suspend_state {
 	int                        disabled;
 	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 cx8802_dev {
 	struct cx88_core           *core;
 	struct cx88_core           *core;
 	spinlock_t                 slock;
 	spinlock_t                 slock;
@@ -439,6 +471,9 @@ struct cx8802_dev {
 
 
 	/* mpeg params */
 	/* mpeg params */
 	struct cx2341x_mpeg_params 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 *core, struct v4l2_tuner *t);
 void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual);
 void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual);
 int cx88_audio_thread(void *data);
 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                                                */
 /* cx88-input.c                                                */
 
 
@@ -600,6 +640,13 @@ extern int cx88_do_ioctl(struct inode *inode, struct file *file, int radio,
 extern const u32 cx88_user_ctrls[];
 extern const u32 cx88_user_ctrls[];
 extern int cx8800_ctrl_query(struct v4l2_queryctrl *qctrl);
 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:
  * Local variables:
  * c-basic-offset: 8
  * c-basic-offset: 8

+ 28 - 18
drivers/media/video/ir-kbd-i2c.c

@@ -305,15 +305,14 @@ static int ir_attach(struct i2c_adapter *adap, int addr,
 	int ir_type;
 	int ir_type;
 	struct IR_i2c *ir;
 	struct IR_i2c *ir;
 	struct input_dev *input_dev;
 	struct input_dev *input_dev;
+	int err;
 
 
 	ir = kzalloc(sizeof(struct IR_i2c),GFP_KERNEL);
 	ir = kzalloc(sizeof(struct IR_i2c),GFP_KERNEL);
 	input_dev = input_allocate_device();
 	input_dev = input_allocate_device();
 	if (!ir || !input_dev) {
 	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->c = client_template;
 	ir->input = input_dev;
 	ir->input = input_dev;
@@ -355,32 +354,34 @@ static int ir_attach(struct i2c_adapter *adap, int addr,
 		break;
 		break;
 	case 0x7a:
 	case 0x7a:
 	case 0x47:
 	case 0x47:
+	case 0x71:
 		/* Handled by saa7134-input */
 		/* Handled by saa7134-input */
 		name        = "SAA713x remote";
 		name        = "SAA713x remote";
 		ir_type     = IR_TYPE_OTHER;
 		ir_type     = IR_TYPE_OTHER;
 		break;
 		break;
 	default:
 	default:
 		/* shouldn't happen */
 		/* 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 */
 	/* Sets name */
 	snprintf(ir->c.name, sizeof(ir->c.name), "i2c IR (%s)", 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
 	/* register i2c device
 	 * At device register, IR codes may be changed to be
 	 * At device register, IR codes may be changed to be
 	 * board dependent.
 	 * 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 not supported or disabled, unregisters driver */
 	if (ir->get_key == NULL) {
 	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) */
 	/* Phys addr can only be set after attaching (for ir->c.dev.bus_id) */
@@ -389,15 +390,17 @@ static int ir_attach(struct i2c_adapter *adap, int addr,
 		 ir->c.dev.bus_id);
 		 ir->c.dev.bus_id);
 
 
 	/* init + register input device */
 	/* 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->id.bustype = BUS_I2C;
 	input_dev->name       = ir->c.name;
 	input_dev->name       = ir->c.name;
 	input_dev->phys       = ir->phys;
 	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",
 	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 */
 	/* start polling via eventd */
 	INIT_WORK(&ir->work, ir_work);
 	INIT_WORK(&ir->work, ir_work);
@@ -407,6 +410,13 @@ static int ir_attach(struct i2c_adapter *adap, int addr,
 	schedule_work(&ir->work);
 	schedule_work(&ir->work);
 
 
 	return 0;
 	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)
 static int ir_detach(struct i2c_client *client)
@@ -414,7 +424,7 @@ static int ir_detach(struct i2c_client *client)
 	struct IR_i2c *ir = i2c_get_clientdata(client);
 	struct IR_i2c *ir = i2c_get_clientdata(client);
 
 
 	/* kill outstanding polls */
 	/* kill outstanding polls */
-	del_timer(&ir->timer);
+	del_timer_sync(&ir->timer);
 	flush_scheduled_work();
 	flush_scheduled_work();
 
 
 	/* unregister devices */
 	/* unregister devices */
@@ -439,7 +449,7 @@ static int ir_probe(struct i2c_adapter *adap)
 	*/
 	*/
 
 
 	static const int probe_bttv[] = { 0x1a, 0x18, 0x4b, 0x64, 0x30, -1};
 	static const int probe_bttv[] = { 0x1a, 0x18, 0x4b, 0x64, 0x30, -1};
-	static const int probe_saa7134[] = { 0x7a, 0x47, -1 };
+	static const int probe_saa7134[] = { 0x7a, 0x47, 0x71, -1 };
 	static const int probe_em28XX[] = { 0x30, 0x47, -1 };
 	static const int probe_em28XX[] = { 0x30, 0x47, -1 };
 	const int *probe = NULL;
 	const int *probe = NULL;
 	struct i2c_client c;
 	struct i2c_client c;

+ 4 - 4
drivers/media/video/mxb.c

@@ -160,10 +160,6 @@ static int mxb_probe(struct saa7146_dev* dev)
 		printk("mxb: saa7111 i2c module not available.\n");
 		printk("mxb: saa7111 i2c module not available.\n");
 		return -ENODEV;
 		return -ENODEV;
 	}
 	}
-	if ((result = request_module("tuner")) < 0) {
-		printk("mxb: tuner i2c module not available.\n");
-		return -ENODEV;
-	}
 	if ((result = request_module("tea6420")) < 0) {
 	if ((result = request_module("tea6420")) < 0) {
 		printk("mxb: tea6420 i2c module not available.\n");
 		printk("mxb: tea6420 i2c module not available.\n");
 		return -ENODEV;
 		return -ENODEV;
@@ -176,6 +172,10 @@ static int mxb_probe(struct saa7146_dev* dev)
 		printk("mxb: tda9840 i2c module not available.\n");
 		printk("mxb: tda9840 i2c module not available.\n");
 		return -ENODEV;
 		return -ENODEV;
 	}
 	}
+	if ((result = request_module("tuner")) < 0) {
+		printk("mxb: tuner i2c module not available.\n");
+		return -ENODEV;
+	}
 
 
 	mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL);
 	mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL);
 	if( NULL == mxb ) {
 	if( NULL == mxb ) {

+ 1333 - 0
drivers/media/video/ov7670.c

@@ -0,0 +1,1333 @@
+/*
+ * 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 <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/videodev.h>
+#include <media/v4l2-common.h>
+#include <linux/i2c.h>
+
+
+MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net.");
+MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors");
+MODULE_LICENSE("GPL");
+
+/*
+ * Basic window sizes.  These probably belong somewhere more globally
+ * useful.
+ */
+#define VGA_WIDTH	640
+#define VGA_HEIGHT	480
+#define QVGA_WIDTH	320
+#define QVGA_HEIGHT	240
+#define CIF_WIDTH	352
+#define CIF_HEIGHT	288
+#define QCIF_WIDTH	176
+#define	QCIF_HEIGHT	144
+
+/*
+ * Our nominal (default) frame rate.
+ */
+#define OV7670_FRAME_RATE 30
+
+/*
+ * The 7670 sits on i2c with ID 0x42
+ */
+#define OV7670_I2C_ADDR 0x42
+
+/* Registers */
+#define REG_GAIN	0x00	/* Gain lower 8 bits (rest in vref) */
+#define REG_BLUE	0x01	/* blue gain */
+#define REG_RED		0x02	/* red gain */
+#define REG_VREF	0x03	/* Pieces of GAIN, VSTART, VSTOP */
+#define REG_COM1	0x04	/* Control 1 */
+#define  COM1_CCIR656	  0x40  /* CCIR656 enable */
+#define REG_BAVE	0x05	/* U/B Average level */
+#define REG_GbAVE	0x06	/* Y/Gb Average level */
+#define REG_AECHH	0x07	/* AEC MS 5 bits */
+#define REG_RAVE	0x08	/* V/R Average level */
+#define REG_COM2	0x09	/* Control 2 */
+#define  COM2_SSLEEP	  0x10	/* Soft sleep mode */
+#define REG_PID		0x0a	/* Product ID MSB */
+#define REG_VER		0x0b	/* Product ID LSB */
+#define REG_COM3	0x0c	/* Control 3 */
+#define  COM3_SWAP	  0x40	  /* Byte swap */
+#define  COM3_SCALEEN	  0x08	  /* Enable scaling */
+#define  COM3_DCWEN	  0x04	  /* Enable downsamp/crop/window */
+#define REG_COM4	0x0d	/* Control 4 */
+#define REG_COM5	0x0e	/* All "reserved" */
+#define REG_COM6	0x0f	/* Control 6 */
+#define REG_AECH	0x10	/* More bits of AEC value */
+#define REG_CLKRC	0x11	/* Clocl control */
+#define   CLK_EXT	  0x40	  /* Use external clock directly */
+#define   CLK_SCALE	  0x3f	  /* Mask for internal clock scale */
+#define REG_COM7	0x12	/* Control 7 */
+#define   COM7_RESET	  0x80	  /* Register reset */
+#define   COM7_FMT_MASK	  0x38
+#define   COM7_FMT_VGA	  0x00
+#define	  COM7_FMT_CIF	  0x20	  /* CIF format */
+#define   COM7_FMT_QVGA	  0x10	  /* QVGA format */
+#define   COM7_FMT_QCIF	  0x08	  /* QCIF format */
+#define	  COM7_RGB	  0x04	  /* bits 0 and 2 - RGB format */
+#define	  COM7_YUV	  0x00	  /* YUV */
+#define	  COM7_BAYER	  0x01	  /* Bayer format */
+#define	  COM7_PBAYER	  0x05	  /* "Processed bayer" */
+#define REG_COM8	0x13	/* Control 8 */
+#define   COM8_FASTAEC	  0x80	  /* Enable fast AGC/AEC */
+#define   COM8_AECSTEP	  0x40	  /* Unlimited AEC step size */
+#define   COM8_BFILT	  0x20	  /* Band filter enable */
+#define   COM8_AGC	  0x04	  /* Auto gain enable */
+#define   COM8_AWB	  0x02	  /* White balance enable */
+#define   COM8_AEC	  0x01	  /* Auto exposure enable */
+#define REG_COM9	0x14	/* Control 9  - gain ceiling */
+#define REG_COM10	0x15	/* Control 10 */
+#define   COM10_HSYNC	  0x40	  /* HSYNC instead of HREF */
+#define   COM10_PCLK_HB	  0x20	  /* Suppress PCLK on horiz blank */
+#define   COM10_HREF_REV  0x08	  /* Reverse HREF */
+#define   COM10_VS_LEAD	  0x04	  /* VSYNC on clock leading edge */
+#define   COM10_VS_NEG	  0x02	  /* VSYNC negative */
+#define   COM10_HS_NEG	  0x01	  /* HSYNC negative */
+#define REG_HSTART	0x17	/* Horiz start high bits */
+#define REG_HSTOP	0x18	/* Horiz stop high bits */
+#define REG_VSTART	0x19	/* Vert start high bits */
+#define REG_VSTOP	0x1a	/* Vert stop high bits */
+#define REG_PSHFT	0x1b	/* Pixel delay after HREF */
+#define REG_MIDH	0x1c	/* Manuf. ID high */
+#define REG_MIDL	0x1d	/* Manuf. ID low */
+#define REG_MVFP	0x1e	/* Mirror / vflip */
+#define   MVFP_MIRROR	  0x20	  /* Mirror image */
+#define   MVFP_FLIP	  0x10	  /* Vertical flip */
+
+#define REG_AEW		0x24	/* AGC upper limit */
+#define REG_AEB		0x25	/* AGC lower limit */
+#define REG_VPT		0x26	/* AGC/AEC fast mode op region */
+#define REG_HSYST	0x30	/* HSYNC rising edge delay */
+#define REG_HSYEN	0x31	/* HSYNC falling edge delay */
+#define REG_HREF	0x32	/* HREF pieces */
+#define REG_TSLB	0x3a	/* lots of stuff */
+#define   TSLB_YLAST	  0x04	  /* UYVY or VYUY - see com13 */
+#define REG_COM11	0x3b	/* Control 11 */
+#define   COM11_NIGHT	  0x80	  /* NIght mode enable */
+#define   COM11_NMFR	  0x60	  /* Two bit NM frame rate */
+#define   COM11_HZAUTO	  0x10	  /* Auto detect 50/60 Hz */
+#define	  COM11_50HZ	  0x08	  /* Manual 50Hz select */
+#define   COM11_EXP	  0x02
+#define REG_COM12	0x3c	/* Control 12 */
+#define   COM12_HREF	  0x80	  /* HREF always */
+#define REG_COM13	0x3d	/* Control 13 */
+#define   COM13_GAMMA	  0x80	  /* Gamma enable */
+#define	  COM13_UVSAT	  0x40	  /* UV saturation auto adjustment */
+#define   COM13_UVSWAP	  0x01	  /* V before U - w/TSLB */
+#define REG_COM14	0x3e	/* Control 14 */
+#define   COM14_DCWEN	  0x10	  /* DCW/PCLK-scale enable */
+#define REG_EDGE	0x3f	/* Edge enhancement factor */
+#define REG_COM15	0x40	/* Control 15 */
+#define   COM15_R10F0	  0x00	  /* Data range 10 to F0 */
+#define	  COM15_R01FE	  0x80	  /*            01 to FE */
+#define   COM15_R00FF	  0xc0	  /*            00 to FF */
+#define   COM15_RGB565	  0x10	  /* RGB565 output */
+#define   COM15_RGB555	  0x30	  /* RGB555 output */
+#define REG_COM16	0x41	/* Control 16 */
+#define   COM16_AWBGAIN   0x08	  /* AWB gain enable */
+#define REG_COM17	0x42	/* Control 17 */
+#define   COM17_AECWIN	  0xc0	  /* AEC window - must match COM4 */
+#define   COM17_CBAR	  0x08	  /* DSP Color bar */
+
+/*
+ * This matrix defines how the colors are generated, must be
+ * tweaked to adjust hue and saturation.
+ *
+ * Order: v-red, v-green, v-blue, u-red, u-green, u-blue
+ *
+ * They are nine-bit signed quantities, with the sign bit
+ * stored in 0x58.  Sign for v-red is bit 0, and up from there.
+ */
+#define	REG_CMATRIX_BASE 0x4f
+#define   CMATRIX_LEN 6
+#define REG_CMATRIX_SIGN 0x58
+
+
+#define REG_BRIGHT	0x55	/* Brightness */
+#define REG_CONTRAS	0x56	/* Contrast control */
+
+#define REG_GFIX	0x69	/* Fix gain control */
+
+#define REG_RGB444	0x8c	/* RGB 444 control */
+#define   R444_ENABLE	  0x02	  /* Turn on RGB444, overrides 5x5 */
+#define   R444_RGBX	  0x01	  /* Empty nibble at end */
+
+#define REG_HAECC1	0x9f	/* Hist AEC/AGC control 1 */
+#define REG_HAECC2	0xa0	/* Hist AEC/AGC control 2 */
+
+#define REG_BD50MAX	0xa5	/* 50hz banding step limit */
+#define REG_HAECC3	0xa6	/* Hist AEC/AGC control 3 */
+#define REG_HAECC4	0xa7	/* Hist AEC/AGC control 4 */
+#define REG_HAECC5	0xa8	/* Hist AEC/AGC control 5 */
+#define REG_HAECC6	0xa9	/* Hist AEC/AGC control 6 */
+#define REG_HAECC7	0xaa	/* Hist AEC/AGC control 7 */
+#define REG_BD60MAX	0xab	/* 60hz banding step limit */
+
+
+/*
+ * Information we maintain about a known sensor.
+ */
+struct ov7670_format_struct;  /* coming later */
+struct ov7670_info {
+	struct ov7670_format_struct *fmt;  /* Current format */
+	unsigned char sat;		/* Saturation value */
+	int hue;			/* Hue value */
+};
+
+
+
+
+/*
+ * The default register settings, as obtained from OmniVision.  There
+ * is really no making sense of most of these - lots of "reserved" values
+ * and such.
+ *
+ * These settings give VGA YUYV.
+ */
+
+struct regval_list {
+	unsigned char reg_num;
+	unsigned char value;
+};
+
+static struct regval_list ov7670_default_regs[] = {
+	{ REG_COM7, COM7_RESET },
+/*
+ * Clock scale: 3 = 15fps
+ *              2 = 20fps
+ *              1 = 30fps
+ */
+	{ REG_CLKRC, 0x1 },	/* OV: clock scale (30 fps) */
+	{ REG_TSLB,  0x04 },	/* OV */
+	{ REG_COM7, 0 },	/* VGA */
+	/*
+	 * Set the hardware window.  These values from OV don't entirely
+	 * make sense - hstop is less than hstart.  But they work...
+	 */
+	{ REG_HSTART, 0x13 },	{ REG_HSTOP, 0x01 },
+	{ REG_HREF, 0xb6 },	{ REG_VSTART, 0x02 },
+	{ REG_VSTOP, 0x7a },	{ REG_VREF, 0x0a },
+
+	{ REG_COM3, 0 },	{ REG_COM14, 0 },
+	/* Mystery scaling numbers */
+	{ 0x70, 0x3a },		{ 0x71, 0x35 },
+	{ 0x72, 0x11 },		{ 0x73, 0xf0 },
+	{ 0xa2, 0x02 },		{ REG_COM10, 0x0 },
+
+	/* Gamma curve values */
+	{ 0x7a, 0x20 },		{ 0x7b, 0x10 },
+	{ 0x7c, 0x1e },		{ 0x7d, 0x35 },
+	{ 0x7e, 0x5a },		{ 0x7f, 0x69 },
+	{ 0x80, 0x76 },		{ 0x81, 0x80 },
+	{ 0x82, 0x88 },		{ 0x83, 0x8f },
+	{ 0x84, 0x96 },		{ 0x85, 0xa3 },
+	{ 0x86, 0xaf },		{ 0x87, 0xc4 },
+	{ 0x88, 0xd7 },		{ 0x89, 0xe8 },
+
+	/* AGC and AEC parameters.  Note we start by disabling those features,
+	   then turn them only after tweaking the values. */
+	{ REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT },
+	{ REG_GAIN, 0 },	{ REG_AECH, 0 },
+	{ REG_COM4, 0x40 }, /* magic reserved bit */
+	{ REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */
+	{ REG_BD50MAX, 0x05 },	{ REG_BD60MAX, 0x07 },
+	{ REG_AEW, 0x95 },	{ REG_AEB, 0x33 },
+	{ REG_VPT, 0xe3 },	{ REG_HAECC1, 0x78 },
+	{ REG_HAECC2, 0x68 },	{ 0xa1, 0x03 }, /* magic */
+	{ REG_HAECC3, 0xd8 },	{ REG_HAECC4, 0xd8 },
+	{ REG_HAECC5, 0xf0 },	{ REG_HAECC6, 0x90 },
+	{ REG_HAECC7, 0x94 },
+	{ REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC },
+
+	/* Almost all of these are magic "reserved" values.  */
+	{ REG_COM5, 0x61 },	{ REG_COM6, 0x4b },
+	{ 0x16, 0x02 },		{ REG_MVFP, 0x07|MVFP_MIRROR },
+	{ 0x21, 0x02 },		{ 0x22, 0x91 },
+	{ 0x29, 0x07 },		{ 0x33, 0x0b },
+	{ 0x35, 0x0b },		{ 0x37, 0x1d },
+	{ 0x38, 0x71 },		{ 0x39, 0x2a },
+	{ REG_COM12, 0x78 },	{ 0x4d, 0x40 },
+	{ 0x4e, 0x20 },		{ REG_GFIX, 0 },
+	{ 0x6b, 0x4a },		{ 0x74, 0x10 },
+	{ 0x8d, 0x4f },		{ 0x8e, 0 },
+	{ 0x8f, 0 },		{ 0x90, 0 },
+	{ 0x91, 0 },		{ 0x96, 0 },
+	{ 0x9a, 0 },		{ 0xb0, 0x84 },
+	{ 0xb1, 0x0c },		{ 0xb2, 0x0e },
+	{ 0xb3, 0x82 },		{ 0xb8, 0x0a },
+
+	/* More reserved magic, some of which tweaks white balance */
+	{ 0x43, 0x0a },		{ 0x44, 0xf0 },
+	{ 0x45, 0x34 },		{ 0x46, 0x58 },
+	{ 0x47, 0x28 },		{ 0x48, 0x3a },
+	{ 0x59, 0x88 },		{ 0x5a, 0x88 },
+	{ 0x5b, 0x44 },		{ 0x5c, 0x67 },
+	{ 0x5d, 0x49 },		{ 0x5e, 0x0e },
+	{ 0x6c, 0x0a },		{ 0x6d, 0x55 },
+	{ 0x6e, 0x11 },		{ 0x6f, 0x9f }, /* "9e for advance AWB" */
+	{ 0x6a, 0x40 },		{ REG_BLUE, 0x40 },
+	{ REG_RED, 0x60 },
+	{ REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB },
+
+	/* Matrix coefficients */
+	{ 0x4f, 0x80 },		{ 0x50, 0x80 },
+	{ 0x51, 0 },		{ 0x52, 0x22 },
+	{ 0x53, 0x5e },		{ 0x54, 0x80 },
+	{ 0x58, 0x9e },
+
+	{ REG_COM16, COM16_AWBGAIN },	{ REG_EDGE, 0 },
+	{ 0x75, 0x05 },		{ 0x76, 0xe1 },
+	{ 0x4c, 0 },		{ 0x77, 0x01 },
+	{ REG_COM13, 0xc3 },	{ 0x4b, 0x09 },
+	{ 0xc9, 0x60 },		{ REG_COM16, 0x38 },
+	{ 0x56, 0x40 },
+
+	{ 0x34, 0x11 },		{ REG_COM11, COM11_EXP|COM11_HZAUTO },
+	{ 0xa4, 0x88 },		{ 0x96, 0 },
+	{ 0x97, 0x30 },		{ 0x98, 0x20 },
+	{ 0x99, 0x30 },		{ 0x9a, 0x84 },
+	{ 0x9b, 0x29 },		{ 0x9c, 0x03 },
+	{ 0x9d, 0x4c },		{ 0x9e, 0x3f },
+	{ 0x78, 0x04 },
+
+	/* Extra-weird stuff.  Some sort of multiplexor register */
+	{ 0x79, 0x01 },		{ 0xc8, 0xf0 },
+	{ 0x79, 0x0f },		{ 0xc8, 0x00 },
+	{ 0x79, 0x10 },		{ 0xc8, 0x7e },
+	{ 0x79, 0x0a },		{ 0xc8, 0x80 },
+	{ 0x79, 0x0b },		{ 0xc8, 0x01 },
+	{ 0x79, 0x0c },		{ 0xc8, 0x0f },
+	{ 0x79, 0x0d },		{ 0xc8, 0x20 },
+	{ 0x79, 0x09 },		{ 0xc8, 0x80 },
+	{ 0x79, 0x02 },		{ 0xc8, 0xc0 },
+	{ 0x79, 0x03 },		{ 0xc8, 0x40 },
+	{ 0x79, 0x05 },		{ 0xc8, 0x30 },
+	{ 0x79, 0x26 },
+
+	{ 0xff, 0xff },	/* END MARKER */
+};
+
+
+/*
+ * Here we'll try to encapsulate the changes for just the output
+ * video format.
+ *
+ * RGB656 and YUV422 come from OV; RGB444 is homebrewed.
+ *
+ * IMPORTANT RULE: the first entry must be for COM7, see ov7670_s_fmt for why.
+ */
+
+
+static struct regval_list ov7670_fmt_yuv422[] = {
+	{ REG_COM7, 0x0 },  /* Selects YUV mode */
+	{ REG_RGB444, 0 },	/* No RGB444 please */
+	{ REG_COM1, 0 },
+	{ REG_COM15, COM15_R00FF },
+	{ REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */
+	{ 0x4f, 0x80 }, 	/* "matrix coefficient 1" */
+	{ 0x50, 0x80 }, 	/* "matrix coefficient 2" */
+	{ 0x51, 0    },		/* vb */
+	{ 0x52, 0x22 }, 	/* "matrix coefficient 4" */
+	{ 0x53, 0x5e }, 	/* "matrix coefficient 5" */
+	{ 0x54, 0x80 }, 	/* "matrix coefficient 6" */
+	{ REG_COM13, COM13_GAMMA|COM13_UVSAT },
+	{ 0xff, 0xff },
+};
+
+static struct regval_list ov7670_fmt_rgb565[] = {
+	{ REG_COM7, COM7_RGB },	/* Selects RGB mode */
+	{ REG_RGB444, 0 },	/* No RGB444 please */
+	{ REG_COM1, 0x0 },
+	{ REG_COM15, COM15_RGB565 },
+	{ REG_COM9, 0x38 }, 	/* 16x gain ceiling; 0x8 is reserved bit */
+	{ 0x4f, 0xb3 }, 	/* "matrix coefficient 1" */
+	{ 0x50, 0xb3 }, 	/* "matrix coefficient 2" */
+	{ 0x51, 0    },		/* vb */
+	{ 0x52, 0x3d }, 	/* "matrix coefficient 4" */
+	{ 0x53, 0xa7 }, 	/* "matrix coefficient 5" */
+	{ 0x54, 0xe4 }, 	/* "matrix coefficient 6" */
+	{ REG_COM13, COM13_GAMMA|COM13_UVSAT },
+	{ 0xff, 0xff },
+};
+
+static struct regval_list ov7670_fmt_rgb444[] = {
+	{ REG_COM7, COM7_RGB },	/* Selects RGB mode */
+	{ REG_RGB444, R444_ENABLE },	/* Enable xxxxrrrr ggggbbbb */
+	{ REG_COM1, 0x40 },	/* Magic reserved bit */
+	{ REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */
+	{ REG_COM9, 0x38 }, 	/* 16x gain ceiling; 0x8 is reserved bit */
+	{ 0x4f, 0xb3 }, 	/* "matrix coefficient 1" */
+	{ 0x50, 0xb3 }, 	/* "matrix coefficient 2" */
+	{ 0x51, 0    },		/* vb */
+	{ 0x52, 0x3d }, 	/* "matrix coefficient 4" */
+	{ 0x53, 0xa7 }, 	/* "matrix coefficient 5" */
+	{ 0x54, 0xe4 }, 	/* "matrix coefficient 6" */
+	{ REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 },  /* Magic rsvd bit */
+	{ 0xff, 0xff },
+};
+
+
+
+
+/*
+ * Low-level register I/O.
+ */
+
+static int ov7670_read(struct i2c_client *c, unsigned char reg,
+		unsigned char *value)
+{
+	int ret;
+
+	ret = i2c_smbus_read_byte_data(c, reg);
+	if (ret >= 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 },
+	},
+};
+#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;
+	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;
+}
+
+/*
+ * Implement G/S_PARM.  There is a "high quality" mode we could try
+ * to do someday; for now, we just do the frame rate tweak.
+ */
+static int ov7670_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
+{
+	struct v4l2_captureparm *cp = &parms->parm.capture;
+	unsigned char clkrc;
+	int ret;
+
+	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	ret = ov7670_read(c, REG_CLKRC, &clkrc);
+	if (ret < 0)
+		return ret;
+	memset(cp, 0, sizeof(struct v4l2_captureparm));
+	cp->capability = V4L2_CAP_TIMEPERFRAME;
+	cp->timeperframe.numerator = 1;
+	cp->timeperframe.denominator = OV7670_FRAME_RATE;
+	if ((clkrc & CLK_EXT) == 0 && (clkrc & CLK_SCALE) > 1)
+		cp->timeperframe.denominator /= (clkrc & CLK_SCALE);
+	return 0;
+}
+
+static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
+{
+	struct v4l2_captureparm *cp = &parms->parm.capture;
+	struct v4l2_fract *tpf = &cp->timeperframe;
+	unsigned char clkrc;
+	int ret, div;
+
+	if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	if (cp->extendedmode != 0)
+		return -EINVAL;
+	/*
+	 * CLKRC has a reserved bit, so let's preserve it.
+	 */
+	ret = ov7670_read(c, REG_CLKRC, &clkrc);
+	if (ret < 0)
+		return ret;
+	if (tpf->numerator == 0 || tpf->denominator == 0)
+		div = 1;  /* Reset to full rate */
+	else
+		div = (tpf->numerator*OV7670_FRAME_RATE)/tpf->denominator;
+	if (div == 0)
+		div = 1;
+	else if (div > CLK_SCALE)
+		div = CLK_SCALE;
+	clkrc = (clkrc & 0x80) | div;
+	tpf->numerator = 1;
+	tpf->denominator = OV7670_FRAME_RATE/div;
+	return ov7670_write(c, REG_CLKRC, clkrc);
+}
+
+
+
+/*
+ * 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);
+	case VIDIOC_S_PARM:
+		return ov7670_s_parm(client, (struct v4l2_streamparm *) arg);
+	case VIDIOC_G_PARM:
+		return ov7670_g_parm(client, (struct v4l2_streamparm *) arg);
+	}
+	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);

+ 16 - 0
drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c

@@ -260,6 +260,22 @@ int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *hdw,
 				  sizeof(decoder_ops[0]))) - 1;
 				  sizeof(decoder_ops[0]))) - 1;
 	hdw->decoder_ctrl = &ctxt->ctrl;
 	hdw->decoder_ctrl = &ctxt->ctrl;
 	cp->handler = &ctxt->handler;
 	cp->handler = &ctxt->handler;
+	{
+		/*
+		  Mike Isely <isely@pobox.com> 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",
 	pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x cx2584x V4L2 handler set up",
 		   cp->client->addr);
 		   cp->client->addr);
 	return !0;
 	return !0;

+ 0 - 26
drivers/media/video/pvrusb2/pvrusb2-hdw.c

@@ -356,28 +356,6 @@ static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v)
 	return 0;
 	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)
 static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp)
 {
 {
 	/* Actual maximum depends on the video standard in effect. */
 	/* Actual maximum depends on the video standard in effect. */
@@ -758,10 +736,6 @@ static const struct pvr2_ctl_info control_defs[] = {
 		.default_value = 720,
 		.default_value = 720,
 		DEFREF(res_hor),
 		DEFREF(res_hor),
 		DEFINT(19,720),
 		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",
 		.desc = "Vertical capture resolution",
 		.name = "resolution_ver",
 		.name = "resolution_ver",

+ 77 - 4
drivers/media/video/pvrusb2/pvrusb2-i2c-core.c

@@ -185,6 +185,79 @@ static int pvr2_i2c_basic_op(struct pvr2_hdw *hdw,
 	}
 	}
 }
 }
 
 
+
+/* 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 <isely@pobox.com> 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
 /* 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
    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
    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 *hdw)
 {
 {
 	unsigned int idx;
 	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++) {
 	for (idx = 0; idx < PVR2_I2C_FUNC_CNT; idx++) {
 		hdw->i2c_func[idx] = pvr2_i2c_basic_op;
 		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) {
 	if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) {
 		hdw->i2c_func[0x1b] = i2c_hack_wm8775;
 		hdw->i2c_func[0x1b] = i2c_hack_wm8775;
 		hdw->i2c_func[0x44] = i2c_hack_cx25840;
 		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.
 	// Configure the adapter and set up everything else related to it.

+ 9 - 9
drivers/media/video/saa7115.c

@@ -851,7 +851,7 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height)
 
 
 	/* On 60Hz, it is using a higher Vertical Output Size */
 	/* On 60Hz, it is using a higher Vertical Output Size */
 	if (!is_50hz)
 	if (!is_50hz)
-		res+=(VRES_60HZ-480)>>1;
+		res += (VRES_60HZ - 480) >> 1;
 
 
 		/* height */
 		/* height */
 	saa711x_write(client, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,
 	saa711x_write(client, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,
@@ -907,7 +907,7 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height)
 
 
 	/* Activates task "B" */
 	/* Activates task "B" */
 	saa711x_write(client, R_80_GLOBAL_CNTL_1,
 	saa711x_write(client, R_80_GLOBAL_CNTL_1,
-				saa711x_read(client,R_80_GLOBAL_CNTL_1)|0x20);
+				saa711x_read(client,R_80_GLOBAL_CNTL_1) | 0x20);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -932,11 +932,11 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
 	if (std & V4L2_STD_525_60) {
 	if (std & V4L2_STD_525_60) {
 		v4l_dbg(1, debug, client, "decoder set standard 60 Hz\n");
 		v4l_dbg(1, debug, client, "decoder set standard 60 Hz\n");
 		saa711x_writeregs(client, saa7115_cfg_60hz_video);
 		saa711x_writeregs(client, saa7115_cfg_60hz_video);
-		saa711x_set_size(client,720,480);
+		saa711x_set_size(client, 720, 480);
 	} else {
 	} else {
 		v4l_dbg(1, debug, client, "decoder set standard 50 Hz\n");
 		v4l_dbg(1, debug, client, "decoder set standard 50 Hz\n");
 		saa711x_writeregs(client, saa7115_cfg_50hz_video);
 		saa711x_writeregs(client, saa7115_cfg_50hz_video);
-		saa711x_set_size(client,720,576);
+		saa711x_set_size(client, 720, 576);
 	}
 	}
 
 
 	/* Register 0E - Bits D6-D4 on NO-AUTO mode
 	/* Register 0E - Bits D6-D4 on NO-AUTO mode
@@ -1464,13 +1464,13 @@ static int saa711x_attach(struct i2c_adapter *adapter, int address, int kind)
 	client->driver = &i2c_driver_saa711x;
 	client->driver = &i2c_driver_saa711x;
 	snprintf(client->name, sizeof(client->name) - 1, "saa7115");
 	snprintf(client->name, sizeof(client->name) - 1, "saa7115");
 
 
-	for (i=0;i<0x0f;i++) {
+	for (i = 0; i < 0x0f; i++) {
 		saa711x_write(client, 0, i);
 		saa711x_write(client, 0, i);
-		name[i] = (saa711x_read(client, 0) &0x0f) +'0';
-		if (name[i]>'9')
-			name[i]+='a'-'9'-1;
+		name[i] = (saa711x_read(client, 0) & 0x0f) + '0';
+		if (name[i] > '9')
+			name[i] += 'a' - '9' - 1;
 	}
 	}
-	name[i]='\0';
+	name[i] = '\0';
 
 
 	saa711x_write(client, 0, 5);
 	saa711x_write(client, 0, 5);
 	chip_id = saa711x_read(client, 0) & 0x0f;
 	chip_id = saa711x_read(client, 0) & 0x0f;

+ 54 - 9
drivers/media/video/saa7134/saa7134-alsa.c

@@ -1,10 +1,6 @@
 /*
 /*
  *   SAA713x ALSA support for V4L
  *   SAA713x ALSA support for V4L
  *
  *
- *
- *   Caveats:
- *        - Volume doesn't work (it's always at max)
- *
  *   This program is free software; you can redistribute it and/or modify
  *   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
  *   it under the terms of the GNU General Public License as published by
  *   the Free Software Foundation, version 2
  *   the Free Software Foundation, version 2
@@ -614,13 +610,18 @@ static int snd_card_saa7134_capture_open(struct snd_pcm_substream * substream)
 	snd_card_saa7134_pcm_t *pcm;
 	snd_card_saa7134_pcm_t *pcm;
 	snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream);
 	snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream);
 	struct saa7134_dev *dev = saa7134->dev;
 	struct saa7134_dev *dev = saa7134->dev;
-	int err;
+	int amux, err;
 
 
 	mutex_lock(&dev->dmasound.lock);
 	mutex_lock(&dev->dmasound.lock);
 
 
 	dev->dmasound.read_count  = 0;
 	dev->dmasound.read_count  = 0;
 	dev->dmasound.read_offset = 0;
 	dev->dmasound.read_offset = 0;
 
 
+	amux = dev->input->amux;
+	if ((amux < 1) || (amux > 3))
+		amux = 1;
+	dev->dmasound.input  =  amux - 1;
+
 	mutex_unlock(&dev->dmasound.lock);
 	mutex_unlock(&dev->dmasound.lock);
 
 
 	pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
 	pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
@@ -713,6 +714,8 @@ static int snd_saa7134_volume_put(struct snd_kcontrol * kcontrol,
 				  struct snd_ctl_elem_value * ucontrol)
 				  struct snd_ctl_elem_value * ucontrol)
 {
 {
 	snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol);
 	snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol);
+	struct saa7134_dev *dev = chip->dev;
+
 	int change, addr = kcontrol->private_value;
 	int change, addr = kcontrol->private_value;
 	int left, right;
 	int left, right;
 
 
@@ -727,10 +730,52 @@ static int snd_saa7134_volume_put(struct snd_kcontrol * kcontrol,
 	if (right > 20)
 	if (right > 20)
 		right = 20;
 		right = 20;
 	spin_lock_irq(&chip->mixer_lock);
 	spin_lock_irq(&chip->mixer_lock);
-	change = chip->mixer_volume[addr][0] != left ||
-		 chip->mixer_volume[addr][1] != right;
-	chip->mixer_volume[addr][0] = left;
-	chip->mixer_volume[addr][1] = right;
+	change = 0;
+	if (chip->mixer_volume[addr][0] != left) {
+		change = 1;
+		right = left;
+	}
+	if (chip->mixer_volume[addr][1] != right) {
+		change = 1;
+		left = right;
+	}
+	if (change) {
+		switch (dev->pci->device) {
+			case PCI_DEVICE_ID_PHILIPS_SAA7134:
+				switch (addr) {
+					case MIXER_ADDR_TVTUNER:
+						left = 20;
+						break;
+					case MIXER_ADDR_LINE1:
+						saa_andorb(SAA7134_ANALOG_IO_SELECT,  0x10,
+							   (left > 10) ? 0x00 : 0x10);
+						break;
+					case MIXER_ADDR_LINE2:
+						saa_andorb(SAA7134_ANALOG_IO_SELECT,  0x20,
+							   (left > 10) ? 0x00 : 0x20);
+						break;
+				}
+				break;
+			case PCI_DEVICE_ID_PHILIPS_SAA7133:
+			case PCI_DEVICE_ID_PHILIPS_SAA7135:
+				switch (addr) {
+					case MIXER_ADDR_TVTUNER:
+						left = 20;
+						break;
+					case MIXER_ADDR_LINE1:
+						saa_andorb(0x0594,  0x10,
+							   (left > 10) ? 0x00 : 0x10);
+						break;
+					case MIXER_ADDR_LINE2:
+						saa_andorb(0x0594,  0x20,
+							   (left > 10) ? 0x00 : 0x20);
+						break;
+				}
+				break;
+		}
+		chip->mixer_volume[addr][0] = left;
+		chip->mixer_volume[addr][1] = right;
+	}
 	spin_unlock_irq(&chip->mixer_lock);
 	spin_unlock_irq(&chip->mixer_lock);
 	return change;
 	return change;
 }
 }

+ 222 - 0
drivers/media/video/saa7134/saa7134-cards.c

@@ -2462,14 +2462,17 @@ struct saa7134_board saa7134_boards[] = {
 			.vmux = 1,
 			.vmux = 1,
 			.amux = TV,
 			.amux = TV,
 			.tv   = 1,
 			.tv   = 1,
+			.gpio = 0x0000000,
 		},{
 		},{
 			.name = name_comp1,
 			.name = name_comp1,
 			.vmux = 3,
 			.vmux = 3,
 			.amux = LINE2,
 			.amux = LINE2,
+			.gpio = 0x0200000,
 		},{
 		},{
 			.name = name_svideo,
 			.name = name_svideo,
 			.vmux = 8,
 			.vmux = 8,
 			.amux = LINE2,
 			.amux = LINE2,
+			.gpio = 0x0200000,
 		}},
 		}},
 		.radio = {
 		.radio = {
 			.name = name_radio,
 			.name = name_radio,
@@ -3022,6 +3025,158 @@ struct saa7134_board saa7134_boards[] = {
 			.amux   = LINE1,
 			.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 <mo_fedotov@mail.ru> */
+		.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 <fedora@barber-family.id.au> */
+		.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 <tomlohave@gmail.com> */
+		.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,
+		.inputs         = {{
+			.name = name_tv,
+			.vmux = 1,
+			.amux = TV,
+			.tv   = 1,
+		},{
+			.name   = name_comp1,
+			.vmux   = 3,
+			.amux   = LINE2, /* FIXME: audio doesn't work on svideo/composite */
+		},{
+			.name   = name_svideo,
+			.vmux   = 8,
+			.amux   = LINE2, /* FIXME: audio doesn't work on svideo/composite */
+		}},
+		.radio = {
+			.name = name_radio,
+			.amux   = TV,
+		},
+	},
+	[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);
 const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
@@ -3630,6 +3785,36 @@ struct pci_device_id saa7134_pci_tbl[] = {
 		.subvendor    = 0x1043,
 		.subvendor    = 0x1043,
 		.subdevice    = 0x4860,
 		.subdevice    = 0x4860,
 		.driver_data  = SAA7134_BOARD_ASUS_EUROPA2_HYBRID,
 		.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 --- */
 		/* --- boards without eeprom + subsystem ID --- */
 		.vendor       = PCI_VENDOR_ID_PHILIPS,
 		.vendor       = PCI_VENDOR_ID_PHILIPS,
@@ -3717,6 +3902,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
 	case SAA7134_BOARD_AVERMEDIA_305:
 	case SAA7134_BOARD_AVERMEDIA_305:
 	case SAA7134_BOARD_AVERMEDIA_STUDIO_307:
 	case SAA7134_BOARD_AVERMEDIA_STUDIO_307:
 	case SAA7134_BOARD_AVERMEDIA_307:
 	case SAA7134_BOARD_AVERMEDIA_307:
+	case SAA7134_BOARD_AVERMEDIA_STUDIO_507:
 	case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
 	case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
 	case SAA7134_BOARD_AVERMEDIA_777:
 	case SAA7134_BOARD_AVERMEDIA_777:
 /*      case SAA7134_BOARD_SABRENT_SBTTVFM:  */ /* not finished yet */
 /*      case SAA7134_BOARD_SABRENT_SBTTVFM:  */ /* not finished yet */
@@ -3725,6 +3911,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
 	case SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII:
 	case SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII:
 	case SAA7134_BOARD_VIDEOMATE_DVBT_300:
 	case SAA7134_BOARD_VIDEOMATE_DVBT_300:
 	case SAA7134_BOARD_VIDEOMATE_DVBT_200:
 	case SAA7134_BOARD_VIDEOMATE_DVBT_200:
+	case SAA7134_BOARD_VIDEOMATE_DVBT_200A:
 	case SAA7134_BOARD_MANLI_MTV001:
 	case SAA7134_BOARD_MANLI_MTV001:
 	case SAA7134_BOARD_MANLI_MTV002:
 	case SAA7134_BOARD_MANLI_MTV002:
 	case SAA7134_BOARD_BEHOLD_409FM:
 	case SAA7134_BOARD_BEHOLD_409FM:
@@ -3793,7 +3980,9 @@ int saa7134_board_init1(struct saa7134_dev *dev)
 		break;
 		break;
 	/* i2c remotes */
 	/* i2c remotes */
 	case SAA7134_BOARD_PINNACLE_PCTV_110i:
 	case SAA7134_BOARD_PINNACLE_PCTV_110i:
+	case SAA7134_BOARD_PINNACLE_PCTV_310i:
 	case SAA7134_BOARD_UPMOST_PURPLE_TV:
 	case SAA7134_BOARD_UPMOST_PURPLE_TV:
+	case SAA7134_BOARD_HAUPPAUGE_HVR1110:
 		dev->has_remote = SAA7134_REMOTE_I2C;
 		dev->has_remote = SAA7134_REMOTE_I2C;
 		break;
 		break;
 	case SAA7134_BOARD_AVERMEDIA_A169_B:
 	case SAA7134_BOARD_AVERMEDIA_A169_B:
@@ -3924,9 +4113,11 @@ int saa7134_board_init2(struct saa7134_dev *dev)
 		}
 		}
 		break;
 		break;
 	case SAA7134_BOARD_PHILIPS_TIGER:
 	case SAA7134_BOARD_PHILIPS_TIGER:
+	case SAA7134_BOARD_PINNACLE_PCTV_310i:
 	case SAA7134_BOARD_TEVION_DVBT_220RF:
 	case SAA7134_BOARD_TEVION_DVBT_220RF:
 	case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
 	case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
 	case SAA7134_BOARD_MEDION_MD8800_QUADRO:
 	case SAA7134_BOARD_MEDION_MD8800_QUADRO:
+	case SAA7134_BOARD_HAUPPAUGE_HVR1110:
 		/* this is a hybrid board, initialize to analog mode
 		/* this is a hybrid board, initialize to analog mode
 		 * and configure firmware eeprom address
 		 * and configure firmware eeprom address
 		 */
 		 */
@@ -3952,6 +4143,14 @@ int saa7134_board_init2(struct saa7134_dev *dev)
 		i2c_transfer(&dev->i2c_adap, &msg, 1);
 		i2c_transfer(&dev->i2c_adap, &msg, 1);
 		}
 		}
 		break;
 		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:
 	case SAA7134_BOARD_KWORLD_ATSC110:
 		{
 		{
 			/* enable tuner */
 			/* enable tuner */
@@ -3964,6 +4163,29 @@ int saa7134_board_init2(struct saa7134_dev *dev)
 					       dev->name, i);
 					       dev->name, i);
 		}
 		}
 		break;
 		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;
 	return 0;
 }
 }

+ 6 - 5
drivers/media/video/saa7134/saa7134-core.c

@@ -889,15 +889,16 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
 		must_configure_manually();
 		must_configure_manually();
 		dev->board = SAA7134_BOARD_UNKNOWN;
 		dev->board = SAA7134_BOARD_UNKNOWN;
 	}
 	}
+	dev->autodetected = card[dev->nr] != dev->board;
 	dev->tuner_type   = saa7134_boards[dev->board].tuner_type;
 	dev->tuner_type   = saa7134_boards[dev->board].tuner_type;
 	dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf;
 	dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf;
 	if (UNSET != tuner[dev->nr])
 	if (UNSET != tuner[dev->nr])
 		dev->tuner_type = 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 */
 	/* get mmio */
 	if (!request_mem_region(pci_resource_start(pci_dev,0),
 	if (!request_mem_region(pci_resource_start(pci_dev,0),

+ 183 - 39
drivers/media/video/saa7134/saa7134-dvb.c

@@ -50,6 +50,10 @@ static unsigned int antenna_pwr = 0;
 module_param(antenna_pwr, int, 0444);
 module_param(antenna_pwr, int, 0444);
 MODULE_PARM_DESC(antenna_pwr,"enable antenna power (Pinnacle 300i)");
 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)
 static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on)
 {
 {
@@ -293,7 +297,7 @@ static int philips_tu1216_tuner_60_set_params(struct dvb_frontend *fe, struct dv
 	return philips_tda6651_pll_set(0x60, fe, params);
 	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)
 					   const struct firmware **fw, char *name)
 {
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
 	struct saa7134_dev *dev = fe->dvb->priv;
@@ -308,7 +312,7 @@ static struct tda1004x_config philips_tu1216_60_config = {
 	.xtal_freq     = TDA10046_XTAL_4M,
 	.xtal_freq     = TDA10046_XTAL_4M,
 	.agc_config    = TDA10046_AGC_DEFAULT,
 	.agc_config    = TDA10046_AGC_DEFAULT,
 	.if_freq       = TDA10046_FREQ_3617,
 	.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_tu1216_61_config = {
 	.xtal_freq     = TDA10046_XTAL_4M,
 	.xtal_freq     = TDA10046_XTAL_4M,
 	.agc_config    = TDA10046_AGC_DEFAULT,
 	.agc_config    = TDA10046_AGC_DEFAULT,
 	.if_freq       = TDA10046_FREQ_3617,
 	.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;
 	struct saa7134_dev *dev = fe->dvb->priv;
 	static u8 msg[] = { 0x0b, 0xf5, 0x86, 0xab };
 	static u8 msg[] = { 0x0b, 0xf5, 0x86, 0xab };
@@ -347,18 +351,8 @@ static int philips_europa_tuner_init(struct dvb_frontend *fe)
 		fe->ops.i2c_gate_ctrl(fe, 1);
 		fe->ops.i2c_gate_ctrl(fe, 1);
 	if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1)
 	if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1)
 		return -EIO;
 		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)
 	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;
 	return 0;
 }
 }
 
 
@@ -367,6 +361,22 @@ static int philips_td1316_tuner_set_params(struct dvb_frontend *fe, struct dvb_f
 	return philips_tda6651_pll_set(0x61, fe, params);
 	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)
 static int philips_europa_tuner_sleep(struct dvb_frontend *fe)
 {
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
 	struct saa7134_dev *dev = fe->dvb->priv;
@@ -671,7 +681,7 @@ static struct tda1004x_config tda827x_lifeview_config = {
 	.invert        = 1,
 	.invert        = 1,
 	.invert_oclk   = 0,
 	.invert_oclk   = 0,
 	.xtal_freq     = TDA10046_XTAL_16M,
 	.xtal_freq     = TDA10046_XTAL_16M,
-	.agc_config    = TDA10046_AGC_TDA827X,
+	.agc_config    = TDA10046_AGC_TDA827X_GP11,
 	.if_freq       = TDA10046_FREQ_045,
 	.if_freq       = TDA10046_FREQ_045,
 	.request_firmware = NULL,
 	.request_firmware = NULL,
 };
 };
@@ -812,32 +822,40 @@ static int philips_tda827xa_tuner_sleep(u8 addr, struct dvb_frontend *fe)
 	if (fe->ops.i2c_gate_ctrl)
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 1);
 		fe->ops.i2c_gate_ctrl(fe, 1);
 	i2c_transfer(&dev->i2c_adap, &tuner_msg, 1);
 	i2c_transfer(&dev->i2c_adap, &tuner_msg, 1);
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
 	return 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;
 	struct saa7134_dev *dev = fe->dvb->priv;
 	static u8 tda8290_close[] = { 0x21, 0xc0};
 	static u8 tda8290_close[] = { 0x21, 0xc0};
 	static u8 tda8290_open[]  = { 0x21, 0x80};
 	static u8 tda8290_open[]  = { 0x21, 0x80};
 	struct i2c_msg tda8290_msg = {.addr = 0x4b,.flags = 0, .len = 2};
 	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;
 		return -EIO;
 	msleep(20);
 	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);
 	ret = philips_tda827xa_pll_set(0x61, fe, params);
 	if (ret != 0)
 	if (ret != 0)
 		return ret;
 		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)
 static int philips_tiger_tuner_init(struct dvb_frontend *fe)
@@ -867,13 +885,80 @@ static struct tda1004x_config philips_tiger_config = {
 	.invert        = 1,
 	.invert        = 1,
 	.invert_oclk   = 0,
 	.invert_oclk   = 0,
 	.xtal_freq     = TDA10046_XTAL_16M,
 	.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,
 	.if_freq       = TDA10046_FREQ_045,
 	.request_firmware = NULL,
 	.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)
 static int asus_p7131_dual_tuner_init(struct dvb_frontend *fe)
 {
 {
 	struct saa7134_dev *dev = fe->dvb->priv;
 	struct saa7134_dev *dev = fe->dvb->priv;
@@ -921,7 +1006,7 @@ static struct tda1004x_config lifeview_trio_config = {
 	.invert        = 1,
 	.invert        = 1,
 	.invert_oclk   = 0,
 	.invert_oclk   = 0,
 	.xtal_freq     = TDA10046_XTAL_16M,
 	.xtal_freq     = TDA10046_XTAL_16M,
-	.agc_config    = TDA10046_AGC_TDA827X_GPL,
+	.agc_config    = TDA10046_AGC_TDA827X_GP00,
 	.if_freq       = TDA10046_FREQ_045,
 	.if_freq       = TDA10046_FREQ_045,
 	.request_firmware = NULL,
 	.request_firmware = NULL,
 };
 };
@@ -958,7 +1043,7 @@ static struct tda1004x_config ads_tech_duo_config = {
 	.invert        = 1,
 	.invert        = 1,
 	.invert_oclk   = 0,
 	.invert_oclk   = 0,
 	.xtal_freq     = TDA10046_XTAL_16M,
 	.xtal_freq     = TDA10046_XTAL_16M,
-	.agc_config    = TDA10046_AGC_TDA827X_GPL,
+	.agc_config    = TDA10046_AGC_TDA827X_GP00,
 	.if_freq       = TDA10046_FREQ_045,
 	.if_freq       = TDA10046_FREQ_045,
 	.request_firmware = NULL,
 	.request_firmware = NULL,
 };
 };
@@ -983,7 +1068,7 @@ static struct tda1004x_config tevion_dvbt220rf_config = {
 	.invert        = 1,
 	.invert        = 1,
 	.invert_oclk   = 0,
 	.invert_oclk   = 0,
 	.xtal_freq     = TDA10046_XTAL_16M,
 	.xtal_freq     = TDA10046_XTAL_16M,
-	.agc_config    = TDA10046_AGC_TDA827X,
+	.agc_config    = TDA10046_AGC_TDA827X_GP11,
 	.if_freq       = TDA10046_FREQ_045,
 	.if_freq       = TDA10046_FREQ_045,
 	.request_firmware = NULL,
 	.request_firmware = NULL,
 };
 };
@@ -1028,7 +1113,7 @@ static struct tda1004x_config md8800_dvbt_config = {
 	.invert        = 1,
 	.invert        = 1,
 	.invert_oclk   = 0,
 	.invert_oclk   = 0,
 	.xtal_freq     = TDA10046_XTAL_16M,
 	.xtal_freq     = TDA10046_XTAL_16M,
-	.agc_config    = TDA10046_AGC_TDA827X,
+	.agc_config    = TDA10046_AGC_TDA827X_GP11,
 	.if_freq       = TDA10046_FREQ_045,
 	.if_freq       = TDA10046_FREQ_045,
 	.request_firmware = NULL,
 	.request_firmware = NULL,
 };
 };
@@ -1168,6 +1253,29 @@ static int dvb_init(struct saa7134_dev *dev)
 					       &philips_tiger_config,
 					       &philips_tiger_config,
 					       &dev->i2c_adap);
 					       &dev->i2c_adap);
 		if (dev->dvb.frontend) {
 		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.init = philips_tiger_tuner_init;
 			dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep;
 			dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep;
 			dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
 			dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
@@ -1175,9 +1283,10 @@ static int dvb_init(struct saa7134_dev *dev)
 		break;
 		break;
 	case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
 	case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
 		dev->dvb.frontend = dvb_attach(tda10046_attach,
 		dev->dvb.frontend = dvb_attach(tda10046_attach,
-					       &philips_tiger_config,
+					       &asus_p7131_dual_config,
 					       &dev->i2c_adap);
 					       &dev->i2c_adap);
 		if (dev->dvb.frontend) {
 		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.init = asus_p7131_dual_tuner_init;
 			dev->dvb.frontend->ops.tuner_ops.sleep = asus_p7131_dual_tuner_sleep;
 			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;
 			dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params;
@@ -1194,12 +1303,27 @@ static int dvb_init(struct saa7134_dev *dev)
 		}
 		}
 		break;
 		break;
 	case SAA7134_BOARD_FLYDVB_TRIO:
 	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;
 		break;
 	case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
 	case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
@@ -1281,7 +1405,27 @@ static int dvb_init(struct saa7134_dev *dev)
 			dev->dvb.frontend->ops.tuner_ops.set_params = philips_fmd1216_tuner_set_params;
 			dev->dvb.frontend->ops.tuner_ops.set_params = philips_fmd1216_tuner_set_params;
 		}
 		}
 		break;
 		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:
 	default:
 		printk("%s: Huh? unknown DVB card?\n",dev->name);
 		printk("%s: Huh? unknown DVB card?\n",dev->name);
 		break;
 		break;

+ 1 - 0
drivers/media/video/saa7134/saa7134-i2c.c

@@ -341,6 +341,7 @@ static int attach_inform(struct i2c_client *client)
 	switch (client->addr) {
 	switch (client->addr) {
 		case 0x7a:
 		case 0x7a:
 		case 0x47:
 		case 0x47:
+		case 0x71:
 		{
 		{
 			struct IR_i2c *ir = i2c_get_clientdata(client);
 			struct IR_i2c *ir = i2c_get_clientdata(client);
 			d1printk("%s i2c IR detected (%s).\n",
 			d1printk("%s i2c IR detected (%s).\n",

+ 62 - 14
drivers/media/video/saa7134/saa7134-input.c

@@ -112,6 +112,27 @@ static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
 	return 1;
 	return 1;
 }
 }
 
 
+static int get_key_hvr1110(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+	unsigned char buf[5], cod4, code3, code4;
+
+	/* poll IR chip */
+	if (5 != i2c_master_recv(&ir->c,buf,5))
+		return -EIO;
+
+	cod4	= buf[4];
+	code4	= (cod4 >> 2);
+	code3	= buf[3];
+	if (code3 == 0)
+		/* no key pressed */
+		return 0;
+
+	/* return key */
+	*ir_key = code4;
+	*ir_raw = code4;
+	return 1;
+}
+
 void saa7134_input_irq(struct saa7134_dev *dev)
 void saa7134_input_irq(struct saa7134_dev *dev)
 {
 {
 	struct saa7134_ir *ir = dev->remote;
 	struct saa7134_ir *ir = dev->remote;
@@ -131,6 +152,23 @@ static void saa7134_input_timer(unsigned long data)
 	mod_timer(&ir->timer, timeout);
 	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)
 int saa7134_input_init1(struct saa7134_dev *dev)
 {
 {
 	struct saa7134_ir *ir;
 	struct saa7134_ir *ir;
@@ -141,6 +179,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
 	u32 mask_keyup   = 0;
 	u32 mask_keyup   = 0;
 	int polling      = 0;
 	int polling      = 0;
 	int ir_type      = IR_TYPE_OTHER;
 	int ir_type      = IR_TYPE_OTHER;
+	int err;
 
 
 	if (dev->has_remote != SAA7134_REMOTE_GPIO)
 	if (dev->has_remote != SAA7134_REMOTE_GPIO)
 		return -ENODEV;
 		return -ENODEV;
@@ -184,6 +223,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
 	case SAA7134_BOARD_AVERMEDIA_307:
 	case SAA7134_BOARD_AVERMEDIA_307:
 	case SAA7134_BOARD_AVERMEDIA_STUDIO_305:
 	case SAA7134_BOARD_AVERMEDIA_STUDIO_305:
 	case SAA7134_BOARD_AVERMEDIA_STUDIO_307:
 	case SAA7134_BOARD_AVERMEDIA_STUDIO_307:
+	case SAA7134_BOARD_AVERMEDIA_STUDIO_507:
 	case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
 	case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
 		ir_codes     = ir_codes_avermedia;
 		ir_codes     = ir_codes_avermedia;
 		mask_keycode = 0x0007C8;
 		mask_keycode = 0x0007C8;
@@ -266,9 +306,8 @@ int saa7134_input_init1(struct saa7134_dev *dev)
 	ir = kzalloc(sizeof(*ir), GFP_KERNEL);
 	ir = kzalloc(sizeof(*ir), GFP_KERNEL);
 	input_dev = input_allocate_device();
 	input_dev = input_allocate_device();
 	if (!ir || !input_dev) {
 	if (!ir || !input_dev) {
-		kfree(ir);
-		input_free_device(input_dev);
-		return -ENOMEM;
+		err = -ENOMEM;
+		goto err_out_free;
 	}
 	}
 
 
 	ir->dev = input_dev;
 	ir->dev = input_dev;
@@ -299,18 +338,22 @@ int saa7134_input_init1(struct saa7134_dev *dev)
 	}
 	}
 	input_dev->cdev.dev = &dev->pci->dev;
 	input_dev->cdev.dev = &dev->pci->dev;
 
 
-	/* all done */
 	dev->remote = ir;
 	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;
 	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)
 void saa7134_input_fini(struct saa7134_dev *dev)
@@ -318,8 +361,7 @@ void saa7134_input_fini(struct saa7134_dev *dev)
 	if (NULL == dev->remote)
 	if (NULL == dev->remote)
 		return;
 		return;
 
 
-	if (dev->remote->polling)
-		del_timer_sync(&dev->remote->timer);
+	saa7134_ir_stop(dev);
 	input_unregister_device(dev->remote->dev);
 	input_unregister_device(dev->remote->dev);
 	kfree(dev->remote);
 	kfree(dev->remote);
 	dev->remote = NULL;
 	dev->remote = NULL;
@@ -335,6 +377,7 @@ void saa7134_set_i2c_ir(struct saa7134_dev *dev, struct IR_i2c *ir)
 
 
 	switch (dev->board) {
 	switch (dev->board) {
 	case SAA7134_BOARD_PINNACLE_PCTV_110i:
 	case SAA7134_BOARD_PINNACLE_PCTV_110i:
+	case SAA7134_BOARD_PINNACLE_PCTV_310i:
 		snprintf(ir->c.name, sizeof(ir->c.name), "Pinnacle PCTV");
 		snprintf(ir->c.name, sizeof(ir->c.name), "Pinnacle PCTV");
 		if (pinnacle_remote == 0) {
 		if (pinnacle_remote == 0) {
 			ir->get_key   = get_key_pinnacle_color;
 			ir->get_key   = get_key_pinnacle_color;
@@ -349,6 +392,11 @@ void saa7134_set_i2c_ir(struct saa7134_dev *dev, struct IR_i2c *ir)
 		ir->get_key   = get_key_purpletv;
 		ir->get_key   = get_key_purpletv;
 		ir->ir_codes  = ir_codes_purpletv;
 		ir->ir_codes  = ir_codes_purpletv;
 		break;
 		break;
+	case SAA7134_BOARD_HAUPPAUGE_HVR1110:
+		snprintf(ir->c.name, sizeof(ir->c.name), "HVR 1110");
+		ir->get_key   = get_key_hvr1110;
+		ir->ir_codes  = ir_codes_hauppauge_new;
+		break;
 	default:
 	default:
 		dprintk("Shouldn't get here: Unknown board %x for I2C IR?\n",dev->board);
 		dprintk("Shouldn't get here: Unknown board %x for I2C IR?\n",dev->board);
 		break;
 		break;

+ 8 - 0
drivers/media/video/saa7134/saa7134.h

@@ -227,6 +227,11 @@ struct saa7134_format {
 #define SAA7134_BOARD_PROTEUS_2309 98
 #define SAA7134_BOARD_PROTEUS_2309 98
 #define SAA7134_BOARD_AVERMEDIA_A16AR   99
 #define SAA7134_BOARD_AVERMEDIA_A16AR   99
 #define SAA7134_BOARD_ASUS_EUROPA2_HYBRID 100
 #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_MAXBOARDS 8
 #define SAA7134_INPUT_MAX 8
 #define SAA7134_INPUT_MAX 8
@@ -446,6 +451,9 @@ struct saa7134_dev {
 	struct v4l2_prio_state     prio;
 	struct v4l2_prio_state     prio;
 #endif
 #endif
 
 
+	/* insmod option/autodetected */
+	int                        autodetected;
+
 	/* various device info */
 	/* various device info */
 	unsigned int               resources;
 	unsigned int               resources;
 	struct video_device        *video_dev;
 	struct video_device        *video_dev;

+ 18 - 3
drivers/media/video/stv680.c

@@ -687,7 +687,7 @@ static int stv680_start_stream (struct usb_stv *stv680)
 		stv680->sbuf[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL);
 		stv680->sbuf[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL);
 		if (stv680->sbuf[i].data == NULL) {
 		if (stv680->sbuf[i].data == NULL) {
 			PDEBUG (0, "STV(e): Could not kmalloc raw data buffer %i", i);
 			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 usb_stv *stv680)
 		stv680->scratch[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL);
 		stv680->scratch[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL);
 		if (stv680->scratch[i].data == NULL) {
 		if (stv680->scratch[i].data == NULL) {
 			PDEBUG (0, "STV(e): Could not kmalloc raw scratch buffer %i", i);
 			PDEBUG (0, "STV(e): Could not kmalloc raw scratch buffer %i", i);
-			return -1;
+			goto nomem_err;
 		}
 		}
 		stv680->scratch[i].state = BUFFER_UNUSED;
 		stv680->scratch[i].state = BUFFER_UNUSED;
 	}
 	}
@@ -706,7 +706,7 @@ static int stv680_start_stream (struct usb_stv *stv680)
 	for (i = 0; i < STV680_NUMSBUF; i++) {
 	for (i = 0; i < STV680_NUMSBUF; i++) {
 		urb = usb_alloc_urb (0, GFP_KERNEL);
 		urb = usb_alloc_urb (0, GFP_KERNEL);
 		if (!urb)
 		if (!urb)
-			return -ENOMEM;
+			goto nomem_err;
 
 
 		/* sbuf is urb->transfer_buffer, later gets memcpyed to scratch */
 		/* sbuf is urb->transfer_buffer, later gets memcpyed to scratch */
 		usb_fill_bulk_urb (urb, stv680->udev,
 		usb_fill_bulk_urb (urb, stv680->udev,
@@ -721,6 +721,21 @@ static int stv680_start_stream (struct usb_stv *stv680)
 
 
 	stv680->framecount = 0;
 	stv680->framecount = 0;
 	return 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)
 static int stv680_stop_stream (struct usb_stv *stv680)

+ 6 - 0
drivers/media/video/tda9887.c

@@ -482,6 +482,12 @@ static int tda9887_set_config(struct tuner *t, char *buf)
 		buf[1] &= ~cQSS;
 		buf[1] &= ~cQSS;
 	if (t->tda9887_config & TDA9887_GATING_18)
 	if (t->tda9887_config & TDA9887_GATING_18)
 		buf[3] &= ~cGating_36;
 		buf[3] &= ~cGating_36;
+
+	if (t->tda9887_config & TDA9887_GAIN_NORMAL) {
+		radio_stereo.e &= ~cTunerGainLow;
+		radio_mono.e &= ~cTunerGainLow;
+	}
+
 	return 0;
 	return 0;
 }
 }
 
 

+ 4 - 0
drivers/media/video/tuner-core.c

@@ -443,6 +443,10 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
 			printk("%02x ",buffer[i]);
 			printk("%02x ",buffer[i]);
 		printk("\n");
 		printk("\n");
 	}
 	}
+	/* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */
+	if (adap->id == I2C_HW_SAA7146 && addr < 0x4a)
+		return -ENODEV;
+
 	/* autodetection code based on the i2c addr */
 	/* autodetection code based on the i2c addr */
 	if (!no_autodetect) {
 	if (!no_autodetect) {
 		switch (addr) {
 		switch (addr) {

+ 4 - 0
drivers/media/video/tuner-simple.c

@@ -108,6 +108,7 @@ static int tuner_stereo(struct i2c_client *c)
 		case TUNER_PHILIPS_FM1216ME_MK3:
 		case TUNER_PHILIPS_FM1216ME_MK3:
 		case TUNER_PHILIPS_FM1236_MK3:
 		case TUNER_PHILIPS_FM1236_MK3:
 		case TUNER_PHILIPS_FM1256_IH3:
 		case TUNER_PHILIPS_FM1256_IH3:
+		case TUNER_LG_NTSC_TAPE:
 			stereo = ((status & TUNER_SIGNAL) == TUNER_STEREO_MK3);
 			stereo = ((status & TUNER_SIGNAL) == TUNER_STEREO_MK3);
 			break;
 			break;
 		default:
 		default:
@@ -421,6 +422,7 @@ static void default_set_radio_freq(struct i2c_client *c, unsigned int freq)
 	case TUNER_PHILIPS_FM1216ME_MK3:
 	case TUNER_PHILIPS_FM1216ME_MK3:
 	case TUNER_PHILIPS_FM1236_MK3:
 	case TUNER_PHILIPS_FM1236_MK3:
 	case TUNER_PHILIPS_FMD1216ME_MK3:
 	case TUNER_PHILIPS_FMD1216ME_MK3:
+	case TUNER_LG_NTSC_TAPE:
 		buffer[3] = 0x19;
 		buffer[3] = 0x19;
 		break;
 		break;
 	case TUNER_TNF_5335MF:
 	case TUNER_TNF_5335MF:
@@ -465,6 +467,8 @@ static void default_set_radio_freq(struct i2c_client *c, unsigned int freq)
 			config |= TDA9887_INTERCARRIER;
 			config |= TDA9887_INTERCARRIER;
 /*		if (params->port1_set_for_fm_mono)
 /*		if (params->port1_set_for_fm_mono)
 			config &= ~TDA9887_PORT1_ACTIVE;*/
 			config &= ~TDA9887_PORT1_ACTIVE;*/
+		if (params->fm_gain_normal)
+			config |= TDA9887_GAIN_NORMAL;
 		i2c_clients_command(c->adapter, TDA9887_SET_CONFIG, &config);
 		i2c_clients_command(c->adapter, TDA9887_SET_CONFIG, &config);
 	}
 	}
 	if (4 != (rc = i2c_master_send(c,buffer,4)))
 	if (4 != (rc = i2c_master_send(c,buffer,4)))

+ 3 - 12
drivers/media/video/tuner-types.c

@@ -651,6 +651,7 @@ static struct tuner_params tuner_microtune_4049_fm5_params[] = {
 		.has_tda9887 = 1,
 		.has_tda9887 = 1,
 		.port1_invert_for_secam_lc = 1,
 		.port1_invert_for_secam_lc = 1,
 		.default_pll_gating_18 = 1,
 		.default_pll_gating_18 = 1,
+		.fm_gain_normal=1,
 	},
 	},
 };
 };
 
 
@@ -672,16 +673,6 @@ static struct tuner_params tuner_panasonic_vp27_params[] = {
 	},
 	},
 };
 };
 
 
-/* ------------ TUNER_LG_NTSC_TAPE - LGINNOTEK NTSC ------------ */
-
-static struct tuner_params tuner_lg_ntsc_tape_params[] = {
-	{
-		.type   = TUNER_PARAM_TYPE_NTSC,
-		.ranges = tuner_fm1236_mk3_ntsc_ranges,
-		.count  = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges),
-	},
-};
-
 /* ------------ TUNER_TNF_8831BGFF - Philips PAL ------------ */
 /* ------------ TUNER_TNF_8831BGFF - Philips PAL ------------ */
 
 
 static struct tuner_range tuner_tnf_8831bgff_pal_ranges[] = {
 static struct tuner_range tuner_tnf_8831bgff_pal_ranges[] = {
@@ -1331,8 +1322,8 @@ struct tunertype tuners[] = {
 	},
 	},
 	[TUNER_LG_NTSC_TAPE] = { /* LGINNOTEK NTSC */
 	[TUNER_LG_NTSC_TAPE] = { /* LGINNOTEK NTSC */
 		.name   = "LG NTSC (TAPE series)",
 		.name   = "LG NTSC (TAPE series)",
-		.params = tuner_lg_ntsc_tape_params,
-		.count  = ARRAY_SIZE(tuner_lg_ntsc_tape_params),
+		.params = tuner_fm1236_mk3_params,
+		.count  = ARRAY_SIZE(tuner_fm1236_mk3_params),
 	},
 	},
 	[TUNER_TNF_8831BGFF] = { /* Philips PAL */
 	[TUNER_TNF_8831BGFF] = { /* Philips PAL */
 		.name   = "Tenna TNF 8831 BGFF)",
 		.name   = "Tenna TNF 8831 BGFF)",

+ 5 - 4
drivers/media/video/tveeprom.c

@@ -430,7 +430,7 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee,
 			tvee->has_radio = eeprom_data[i+len-1];
 			tvee->has_radio = eeprom_data[i+len-1];
 			/* old style tag, don't know how to detect
 			/* old style tag, don't know how to detect
 			IR presence, mark as unknown. */
 			IR presence, mark as unknown. */
-			tvee->has_ir = 2;
+			tvee->has_ir = -1;
 			tvee->model =
 			tvee->model =
 				eeprom_data[i+8] +
 				eeprom_data[i+8] +
 				(eeprom_data[i+9] << 8);
 				(eeprom_data[i+9] << 8);
@@ -653,13 +653,14 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee,
 			STRM(decoderIC, tvee->decoder_processor),
 			STRM(decoderIC, tvee->decoder_processor),
 			tvee->decoder_processor);
 			tvee->decoder_processor);
 	}
 	}
-	if (tvee->has_ir == 2)
+	if (tvee->has_ir == -1)
 		tveeprom_info("has %sradio\n",
 		tveeprom_info("has %sradio\n",
 				tvee->has_radio ? "" : "no ");
 				tvee->has_radio ? "" : "no ");
 	else
 	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_radio ? "" : "no ",
-				tvee->has_ir ? "" : "no ");
+				(tvee->has_ir & 1) ? "" : "no ",
+				(tvee->has_ir & 2) ? "" : "no ");
 }
 }
 EXPORT_SYMBOL(tveeprom_hauppauge_analog);
 EXPORT_SYMBOL(tveeprom_hauppauge_analog);
 
 

+ 8 - 1
drivers/media/video/usbvideo/quickcam_messenger.c

@@ -86,6 +86,7 @@ MODULE_DEVICE_TABLE(usb, qcm_table);
 static void qcm_register_input(struct qcm *cam, struct usb_device *dev)
 static void qcm_register_input(struct qcm *cam, struct usb_device *dev)
 {
 {
 	struct input_dev *input_dev;
 	struct input_dev *input_dev;
+	int error;
 
 
 	usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname));
 	usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname));
 	strncat(cam->input_physname, "/input0", sizeof(cam->input_physname));
 	strncat(cam->input_physname, "/input0", sizeof(cam->input_physname));
@@ -106,7 +107,13 @@ static void qcm_register_input(struct qcm *cam, struct usb_device *dev)
 
 
 	input_dev->private = cam;
 	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)
 static void qcm_unregister_input(struct qcm *cam)

+ 12 - 0
drivers/media/video/usbvision/Kconfig

@@ -0,0 +1,12 @@
+config VIDEO_USBVISION
+	tristate "USB video devices based on Nogatech NT1003/1004/1005"
+	depends on I2C && VIDEO_V4L2
+	select VIDEO_TUNER
+	select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
+	---help---
+	  There are more than 50 different USB video devices based on
+	  NT1003/1004/1005 USB Bridges. This driver enables using those
+	  devices.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called usbvision.

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio