Переглянути джерело

Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6

* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6: (313 commits)
  V4L/DVB (9186): Added support for Prof 7300 DVB-S/S2 cards
  V4L/DVB (9185): S2API: Ensure we have a reasonable ROLLOFF default
  V4L/DVB (9184): cx24116: Change the default SNR units back to percentage by default.
  V4L/DVB (9183): S2API: Return error of the caller provides 0 commands.
  V4L/DVB (9182): S2API: Added support for DTV_HIERARCHY
  V4L/DVB (9181): S2API: Add support fot DTV_GUARD_INTERVAL and DTV_TRANSMISSION_MODE
  V4L/DVB (9180): S2API: Added support for DTV_CODE_RATE_HP/LP
  V4L/DVB (9179): S2API: frontend.h cleanup
  V4L/DVB (9178): cx24116: Add module parameter to return SNR as ESNO.
  V4L/DVB (9177): S2API: Change _8PSK / _16APSK to PSK_8 and APSK_16
  V4L/DVB (9176): Add support for DvbWorld USB cards with STV0288 demodulator.
  V4L/DVB (9175): Remove NULL pointer in stb6000 driver.
  V4L/DVB (9174): Allow custom inittab for ST STV0288 demodulator.
  V4L/DVB (9173): S2API: Remove the hardcoded command limit during validation
  V4L/DVB (9172): S2API: Bugfix related to DVB-S / DVB-S2 tuning for the legacy API.
  V4L/DVB (9171): S2API: Stop an OOPS if illegal commands are dumped in S2API.
  V4L/DVB (9170): cx24116: Sanity checking to data input via S2API to the cx24116 demod.
  V4L/DVB (9169): uvcvideo: Support two new Bison Electronics webcams.
  V4L/DVB (9168): Add support for MSI TV@nywhere Plus remote
  V4L/DVB: v4l2-dev: remove duplicated #include
  ...
Linus Torvalds 16 роки тому
батько
коміт
cf2fa66055
100 змінених файлів з 14089 додано та 1681 видалено
  1. 1 0
      Documentation/video4linux/CARDLIST.bttv
  2. 2 0
      Documentation/video4linux/CARDLIST.cx23885
  3. 8 0
      Documentation/video4linux/CARDLIST.cx88
  4. 2 2
      Documentation/video4linux/CARDLIST.em28xx
  5. 7 1
      Documentation/video4linux/CARDLIST.saa7134
  6. 1 0
      Documentation/video4linux/CARDLIST.tuner
  7. 27 1
      Documentation/video4linux/gspca.txt
  8. 12 0
      Documentation/video4linux/m5602.txt
  9. 120 0
      Documentation/video4linux/soc-camera.txt
  10. 0 2
      arch/arm/mach-pxa/include/mach/camera.h
  11. 276 4
      drivers/media/common/ir-keymaps.c
  12. 1 1
      drivers/media/common/saa7146_core.c
  13. 1 1
      drivers/media/common/saa7146_fops.c
  14. 36 2
      drivers/media/common/tuners/mt2060.c
  15. 0 1
      drivers/media/common/tuners/mxl5007t.c
  16. 0 1
      drivers/media/common/tuners/tda18271-fe.c
  17. 7 5
      drivers/media/common/tuners/tda827x.c
  18. 0 1
      drivers/media/common/tuners/tda827x.h
  19. 1 3
      drivers/media/common/tuners/tda8290.c
  20. 0 1
      drivers/media/common/tuners/tda8290.h
  21. 0 1
      drivers/media/common/tuners/tda9887.c
  22. 2 1
      drivers/media/common/tuners/tuner-simple.c
  23. 22 0
      drivers/media/common/tuners/tuner-types.c
  24. 45 29
      drivers/media/common/tuners/tuner-xc2028.c
  25. 7 3
      drivers/media/common/tuners/tuner-xc2028.h
  26. 77 32
      drivers/media/common/tuners/xc5000.c
  27. 2 6
      drivers/media/common/tuners/xc5000.h
  28. 0 37
      drivers/media/common/tuners/xc5000_priv.h
  29. 4 1
      drivers/media/dvb/Kconfig
  30. 1 1
      drivers/media/dvb/Makefile
  31. 1 1
      drivers/media/dvb/b2c2/flexcop-dma.c
  32. 1 1
      drivers/media/dvb/bt8xx/dvb-bt8xx.c
  33. 0 85
      drivers/media/dvb/cinergyT2/Kconfig
  34. 0 3
      drivers/media/dvb/cinergyT2/Makefile
  35. 0 1105
      drivers/media/dvb/cinergyT2/cinergyT2.c
  36. 18 0
      drivers/media/dvb/dm1105/Kconfig
  37. 3 0
      drivers/media/dvb/dm1105/Makefile
  38. 911 0
      drivers/media/dvb/dm1105/dm1105.c
  39. 659 7
      drivers/media/dvb/dvb-core/dvb_frontend.c
  40. 32 0
      drivers/media/dvb/dvb-core/dvb_frontend.h
  41. 39 3
      drivers/media/dvb/dvb-usb/Kconfig
  42. 10 0
      drivers/media/dvb/dvb-usb/Makefile
  43. 1 1
      drivers/media/dvb/dvb-usb/af9005-remote.c
  44. 1 1
      drivers/media/dvb/dvb-usb/af9005-script.h
  45. 9 14
      drivers/media/dvb/dvb-usb/af9005.c
  46. 1474 0
      drivers/media/dvb/dvb-usb/af9015.c
  47. 524 0
      drivers/media/dvb/dvb-usb/af9015.h
  48. 25 5
      drivers/media/dvb/dvb-usb/anysee.c
  49. 268 0
      drivers/media/dvb/dvb-usb/cinergyT2-core.c
  50. 351 0
      drivers/media/dvb/dvb-usb/cinergyT2-fe.c
  51. 95 0
      drivers/media/dvb/dvb-usb/cinergyT2.h
  52. 501 3
      drivers/media/dvb/dvb-usb/cxusb.c
  53. 4 0
      drivers/media/dvb/dvb-usb/dib0700.h
  54. 112 3
      drivers/media/dvb/dvb-usb/dib0700_core.c
  55. 189 6
      drivers/media/dvb/dvb-usb/dib0700_devices.c
  56. 240 0
      drivers/media/dvb/dvb-usb/dtv5100.c
  57. 51 0
      drivers/media/dvb/dvb-usb/dtv5100.h
  58. 28 1
      drivers/media/dvb/dvb-usb/dvb-usb-ids.h
  59. 482 90
      drivers/media/dvb/dvb-usb/dw2102.c
  60. 0 1
      drivers/media/dvb/dvb-usb/dw2102.h
  61. 47 0
      drivers/media/dvb/frontends/Kconfig
  62. 7 0
      drivers/media/dvb/frontends/Makefile
  63. 1685 0
      drivers/media/dvb/frontends/af9013.c
  64. 107 0
      drivers/media/dvb/frontends/af9013.h
  65. 869 0
      drivers/media/dvb/frontends/af9013_priv.h
  66. 133 0
      drivers/media/dvb/frontends/au8522.c
  67. 17 0
      drivers/media/dvb/frontends/au8522.h
  68. 10 5
      drivers/media/dvb/frontends/cx24110.h
  69. 1423 0
      drivers/media/dvb/frontends/cx24116.c
  70. 53 0
      drivers/media/dvb/frontends/cx24116.h
  71. 7 1
      drivers/media/dvb/frontends/dib0070.h
  72. 5 1
      drivers/media/dvb/frontends/dib7000m.c
  73. 2 1
      drivers/media/dvb/frontends/dib7000p.c
  74. 36 5
      drivers/media/dvb/frontends/dib7000p.h
  75. 147 141
      drivers/media/dvb/frontends/drx397xD.c
  76. 3 3
      drivers/media/dvb/frontends/drx397xD.h
  77. 6 5
      drivers/media/dvb/frontends/dvb_dummy_fe.c
  78. 133 0
      drivers/media/dvb/frontends/eds1547.h
  79. 454 0
      drivers/media/dvb/frontends/lgs8gl5.c
  80. 45 0
      drivers/media/dvb/frontends/lgs8gl5.h
  81. 2 2
      drivers/media/dvb/frontends/nxt200x.c
  82. 1 1
      drivers/media/dvb/frontends/or51211.c
  83. 974 0
      drivers/media/dvb/frontends/si21xx.c
  84. 37 0
      drivers/media/dvb/frontends/si21xx.h
  85. 2 1
      drivers/media/dvb/frontends/sp887x.c
  86. 255 0
      drivers/media/dvb/frontends/stb6000.c
  87. 51 0
      drivers/media/dvb/frontends/stb6000.h
  88. 618 0
      drivers/media/dvb/frontends/stv0288.c
  89. 67 0
      drivers/media/dvb/frontends/stv0288.h
  90. 2 0
      drivers/media/dvb/frontends/stv0299.c
  91. 8 5
      drivers/media/dvb/frontends/stv0299.h
  92. 73 0
      drivers/media/dvb/frontends/tdhd1.h
  93. 1 0
      drivers/media/dvb/ttpci/Kconfig
  94. 95 32
      drivers/media/dvb/ttpci/av7110.c
  95. 1 0
      drivers/media/dvb/ttpci/av7110.h
  96. 3 0
      drivers/media/dvb/ttpci/av7110_av.c
  97. 6 2
      drivers/media/dvb/ttpci/budget-av.c
  98. 5 2
      drivers/media/dvb/ttpci/budget-ci.c
  99. 2 4
      drivers/media/dvb/ttpci/budget-core.c
  100. 6 3
      drivers/media/dvb/ttpci/budget-patch.c

+ 1 - 0
Documentation/video4linux/CARDLIST.bttv

@@ -150,3 +150,4 @@
 149 -> Typhoon TV-Tuner PCI (50684)
 150 -> Geovision GV-600                                    [008a:763c]
 151 -> Kozumi KTV-01C
+152 -> Encore ENL TV-FM-2                                  [1000:1801]

+ 2 - 0
Documentation/video4linux/CARDLIST.cx23885

@@ -9,3 +9,5 @@
   8 -> Hauppauge WinTV-HVR1700                             [0070:8101]
   9 -> Hauppauge WinTV-HVR1400                             [0070:8010]
  10 -> DViCO FusionHDTV7 Dual Express                      [18ac:d618]
+ 11 -> DViCO FusionHDTV DVB-T Dual Express                 [18ac:db78]
+ 12 -> Leadtek Winfast PxDVR3200 H                         [107d:6681]

+ 8 - 0
Documentation/video4linux/CARDLIST.cx88

@@ -66,3 +66,11 @@
  65 -> DViCO FusionHDTV 7 Gold                             [18ac:d610]
  66 -> Prolink Pixelview MPEG 8000GT                       [1554:4935]
  67 -> Kworld PlusTV HD PCI 120 (ATSC 120)                 [17de:08c1]
+ 68 -> Hauppauge WinTV-HVR4000 DVB-S/S2/T/Hybrid           [0070:6900,0070:6904,0070:6902]
+ 69 -> Hauppauge WinTV-HVR4000(Lite) DVB-S/S2              [0070:6905,0070:6906]
+ 70 -> TeVii S460 DVB-S/S2                                 [d460:9022]
+ 71 -> Omicom SS4 DVB-S/S2 PCI                             [A044:2011]
+ 72 -> TBS 8920 DVB-S/S2                                   [8920:8888]
+ 73 -> TeVii S420 DVB-S                                    [d420:9022]
+ 74 -> Prolink Pixelview Global Extreme                    [1554:4976]
+ 75 -> PROF 7300 DVB-S/S2                                  [B033:3033]

+ 2 - 2
Documentation/video4linux/CARDLIST.em28xx

@@ -1,5 +1,5 @@
   0 -> Unknown EM2800 video grabber             (em2800)        [eb1a:2800]
-  1 -> Unknown EM2750/28xx video grabber        (em2820/em2840) [eb1a:2820,eb1a:2821,eb1a:2860,eb1a:2861,eb1a:2870,eb1a:2881,eb1a:2883]
+  1 -> Unknown EM2750/28xx video grabber        (em2820/em2840) [eb1a:2820,eb1a:2860,eb1a:2861,eb1a:2870,eb1a:2881,eb1a:2883]
   2 -> Terratec Cinergy 250 USB                 (em2820/em2840) [0ccd:0036]
   3 -> Pinnacle PCTV USB 2                      (em2820/em2840) [2304:0208]
   4 -> Hauppauge WinTV USB 2                    (em2820/em2840) [2040:4200,2040:4201]
@@ -12,7 +12,7 @@
  11 -> Terratec Hybrid XS                       (em2880)        [0ccd:0042]
  12 -> Kworld PVR TV 2800 RF                    (em2820/em2840)
  13 -> Terratec Prodigy XS                      (em2880)        [0ccd:0047]
- 14 -> Pixelview Prolink PlayTV USB 2.0         (em2820/em2840)
+ 14 -> Pixelview Prolink PlayTV USB 2.0         (em2820/em2840) [eb1a:2821]
  15 -> V-Gear PocketTV                          (em2800)
  16 -> Hauppauge WinTV HVR 950                  (em2883)        [2040:6513,2040:6517,2040:651b,2040:651f]
  17 -> Pinnacle PCTV HD Pro Stick               (em2880)        [2304:0227]

+ 7 - 1
Documentation/video4linux/CARDLIST.saa7134

@@ -76,7 +76,7 @@
  75 -> AVerMedia AVerTVHD MCE A180              [1461:1044]
  76 -> SKNet MonsterTV Mobile                   [1131:4ee9]
  77 -> Pinnacle PCTV 40i/50i/110i (saa7133)     [11bd:002e]
- 78 -> ASUSTeK P7131 Dual                       [1043:4862,1043:4857]
+ 78 -> ASUSTeK P7131 Dual                       [1043:4862]
  79 -> Sedna/MuchTV PC TV Cardbus TV/Radio (ITO25 Rev:2B)
  80 -> ASUS Digimatrix TV                       [1043:0210]
  81 -> Philips Tiger reference design           [1131:2018]
@@ -145,3 +145,9 @@
 144 -> Beholder BeholdTV M6 Extra               [5ace:6193]
 145 -> AVerMedia MiniPCI DVB-T Hybrid M103      [1461:f636]
 146 -> ASUSTeK P7131 Analog
+147 -> Asus Tiger 3in1                          [1043:4878]
+148 -> Encore ENLTV-FM v5.3                     [1a7f:2008]
+149 -> Avermedia PCI pure analog (M135A)        [1461:f11d]
+150 -> Zogis Real Angel 220
+151 -> ADS Tech Instant HDTV                    [1421:0380]
+152 -> Asus Tiger Rev:1.00                      [1043:4857]

+ 1 - 0
Documentation/video4linux/CARDLIST.tuner

@@ -74,3 +74,4 @@ tuner=72 - Thomson FE6600
 tuner=73 - Samsung TCPG 6121P30A
 tuner=75 - Philips TEA5761 FM Radio
 tuner=76 - Xceive 5000 tuner
+tuner=77 - TCL tuner MF02GIP-5N-E

+ 27 - 1
Documentation/video4linux/gspca.txt

@@ -7,6 +7,7 @@ The modules are:
 xxxx		vend:prod
 ----
 spca501		0000:0000	MystFromOri Unknow Camera
+m5602		0402:5602	ALi Video Camera Controller
 spca501		040a:0002	Kodak DVC-325
 spca500		040a:0300	Kodak EZ200
 zc3xx		041e:041e	Creative WebCam Live!
@@ -42,6 +43,7 @@ zc3xx		0458:7007	Genius VideoCam V2
 zc3xx		0458:700c	Genius VideoCam V3
 zc3xx		0458:700f	Genius VideoCam Web V2
 sonixj		0458:7025	Genius Eye 311Q
+sonixj		0458:702e	Genius Slim 310 NB
 sonixj		045e:00f5	MicroSoft VX3000
 sonixj		045e:00f7	MicroSoft VX1000
 ov519		045e:028c	Micro$oft xbox cam
@@ -81,7 +83,7 @@ spca561		046d:092b	Labtec Webcam Plus
 spca561		046d:092c	Logitech QC chat Elch2
 spca561		046d:092d	Logitech QC Elch2
 spca561		046d:092e	Logitech QC Elch2
-spca561		046d:092f	Logitech QC Elch2
+spca561		046d:092f	Logitech  QuickCam Express Plus
 sunplus		046d:0960	Logitech ClickSmart 420
 sunplus		0471:0322	Philips DMVC1300K
 zc3xx		0471:0325	Philips SPC 200 NC
@@ -96,6 +98,29 @@ sunplus		04a5:3003	Benq DC 1300
 sunplus		04a5:3008	Benq DC 1500
 sunplus		04a5:300a	Benq DC 3410
 spca500		04a5:300c	Benq DC 1016
+finepix		04cb:0104	Fujifilm FinePix 4800
+finepix		04cb:0109	Fujifilm FinePix A202
+finepix		04cb:010b	Fujifilm FinePix A203
+finepix		04cb:010f	Fujifilm FinePix A204
+finepix		04cb:0111	Fujifilm FinePix A205
+finepix		04cb:0113	Fujifilm FinePix A210
+finepix		04cb:0115	Fujifilm FinePix A303
+finepix		04cb:0117	Fujifilm FinePix A310
+finepix		04cb:0119	Fujifilm FinePix F401
+finepix		04cb:011b	Fujifilm FinePix F402
+finepix		04cb:011d	Fujifilm FinePix F410
+finepix		04cb:0121	Fujifilm FinePix F601
+finepix		04cb:0123	Fujifilm FinePix F700
+finepix		04cb:0125	Fujifilm FinePix M603
+finepix		04cb:0127	Fujifilm FinePix S300
+finepix		04cb:0129	Fujifilm FinePix S304
+finepix		04cb:012b	Fujifilm FinePix S500
+finepix		04cb:012d	Fujifilm FinePix S602
+finepix		04cb:012f	Fujifilm FinePix S700
+finepix		04cb:0131	Fujifilm FinePix unknown model
+finepix		04cb:013b	Fujifilm FinePix unknown model
+finepix		04cb:013d	Fujifilm FinePix unknown model
+finepix		04cb:013f	Fujifilm FinePix F420
 sunplus		04f1:1001	JVC GC A50
 spca561		04fc:0561	Flexcam 100
 sunplus		04fc:500c	Sunplus CA500C
@@ -181,6 +206,7 @@ pac207		093a:2468	PAC207
 pac207		093a:2470	Genius GF112
 pac207		093a:2471	Genius VideoCam ge111
 pac207		093a:2472	Genius VideoCam ge110
+pac207		093a:2476	Genius e-Messenger 112
 pac7311		093a:2600	PAC7311 Typhoon
 pac7311		093a:2601	Philips SPC 610 NC
 pac7311		093a:2603	PAC7312

+ 12 - 0
Documentation/video4linux/m5602.txt

@@ -0,0 +1,12 @@
+This document describes the ALi m5602 bridge connected
+to the following supported sensors:
+OmniVision OV9650,
+Samsung s5k83a,
+Samsung s5k4aa,
+Micron mt9m111,
+Pixel plus PO1030
+
+This driver mimics the windows drivers, which have a braindead implementation sending bayer-encoded frames at VGA resolution.
+In a perfect world we should be able to reprogram the m5602 and the connected sensor in hardware instead, supporting a range of resolutions and pixelformats
+
+Anyway, have fun and please report any bugs to m560x-driver-devel@lists.sourceforge.net

+ 120 - 0
Documentation/video4linux/soc-camera.txt

@@ -0,0 +1,120 @@
+			Soc-Camera Subsystem
+			====================
+
+Terminology
+-----------
+
+The following terms are used in this document:
+ - camera / camera device / camera sensor - a video-camera sensor chip, capable
+   of connecting to a variety of systems and interfaces, typically uses i2c for
+   control and configuration, and a parallel or a serial bus for data.
+ - camera host - an interface, to which a camera is connected. Typically a
+   specialised interface, present on many SoCs, e.g., PXA27x and PXA3xx, SuperH,
+   AVR32, i.MX27, i.MX31.
+ - camera host bus - a connection between a camera host and a camera. Can be
+   parallel or serial, consists of data and control lines, e.g., clock, vertical
+   and horizontal synchronization signals.
+
+Purpose of the soc-camera subsystem
+-----------------------------------
+
+The soc-camera subsystem provides a unified API between camera host drivers and
+camera sensor drivers. It implements a V4L2 interface to the user, currently
+only the mmap method is supported.
+
+This subsystem has been written to connect drivers for System-on-Chip (SoC)
+video capture interfaces with drivers for CMOS camera sensor chips to enable
+the reuse of sensor drivers with various hosts. The subsystem has been designed
+to support multiple camera host interfaces and multiple cameras per interface,
+although most applications have only one camera sensor.
+
+Existing drivers
+----------------
+
+As of 2.6.27-rc4 there are two host drivers in the mainline: pxa_camera.c for
+PXA27x SoCs and sh_mobile_ceu_camera.c for SuperH SoCs, and four sensor drivers:
+mt9m001.c, mt9m111.c, mt9v022.c and a generic soc_camera_platform.c driver. This
+list is not supposed to be updated, look for more examples in your tree.
+
+Camera host API
+---------------
+
+A host camera driver is registered using the
+
+soc_camera_host_register(struct soc_camera_host *);
+
+function. The host object can be initialized as follows:
+
+static struct soc_camera_host pxa_soc_camera_host = {
+	.drv_name	= PXA_CAM_DRV_NAME,
+	.ops		= &pxa_soc_camera_host_ops,
+};
+
+All camera host methods are passed in a struct soc_camera_host_ops:
+
+static struct soc_camera_host_ops pxa_soc_camera_host_ops = {
+	.owner		= THIS_MODULE,
+	.add		= pxa_camera_add_device,
+	.remove		= pxa_camera_remove_device,
+	.suspend	= pxa_camera_suspend,
+	.resume		= pxa_camera_resume,
+	.set_fmt_cap	= pxa_camera_set_fmt_cap,
+	.try_fmt_cap	= pxa_camera_try_fmt_cap,
+	.init_videobuf	= pxa_camera_init_videobuf,
+	.reqbufs	= pxa_camera_reqbufs,
+	.poll		= pxa_camera_poll,
+	.querycap	= pxa_camera_querycap,
+	.try_bus_param	= pxa_camera_try_bus_param,
+	.set_bus_param	= pxa_camera_set_bus_param,
+};
+
+.add and .remove methods are called when a sensor is attached to or detached
+from the host, apart from performing host-internal tasks they shall also call
+sensor driver's .init and .release methods respectively. .suspend and .resume
+methods implement host's power-management functionality and its their
+responsibility to call respective sensor's methods. .try_bus_param and
+.set_bus_param are used to negotiate physical connection parameters between the
+host and the sensor. .init_videobuf is called by soc-camera core when a
+video-device is opened, further video-buffer management is implemented completely
+by the specific camera host driver. The rest of the methods are called from
+respective V4L2 operations.
+
+Camera API
+----------
+
+Sensor drivers can use struct soc_camera_link, typically provided by the
+platform, and used to specify to which camera host bus the sensor is connected,
+and arbitrarily provide platform .power and .reset methods for the camera.
+soc_camera_device_register() and soc_camera_device_unregister() functions are
+used to add a sensor driver to or remove one from the system. The registration
+function takes a pointer to struct soc_camera_device as the only parameter.
+This struct can be initialized as follows:
+
+	/* link to driver operations */
+	icd->ops	= &mt9m001_ops;
+	/* link to the underlying physical (e.g., i2c) device */
+	icd->control	= &client->dev;
+	/* window geometry */
+	icd->x_min	= 20;
+	icd->y_min	= 12;
+	icd->x_current	= 20;
+	icd->y_current	= 12;
+	icd->width_min	= 48;
+	icd->width_max	= 1280;
+	icd->height_min	= 32;
+	icd->height_max	= 1024;
+	icd->y_skip_top	= 1;
+	/* camera bus ID, typically obtained from platform data */
+	icd->iface	= icl->bus_id;
+
+struct soc_camera_ops provides .probe and .remove methods, which are called by
+the soc-camera core, when a camera is matched against or removed from a camera
+host bus, .init, .release, .suspend, and .resume are called from the camera host
+driver as discussed above. Other members of this struct provide respective V4L2
+functionality.
+
+struct soc_camera_device also links to an array of struct soc_camera_data_format,
+listing pixel formats, supported by the camera.
+
+--
+Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de>

+ 0 - 2
arch/arm/mach-pxa/include/mach/camera.h

@@ -36,8 +36,6 @@
 
 struct pxacamera_platform_data {
 	int (*init)(struct device *);
-	int (*power)(struct device *, int);
-	int (*reset)(struct device *, int);
 
 	unsigned long flags;
 	unsigned long mclk_10khz;

+ 276 - 4
drivers/media/common/ir-keymaps.c

@@ -103,6 +103,56 @@ IR_KEYTAB_TYPE ir_codes_avermedia_dvbt[IR_KEYTAB_SIZE] = {
 
 EXPORT_SYMBOL_GPL(ir_codes_avermedia_dvbt);
 
+/* Mauro Carvalho Chehab <mchehab@infradead.org> */
+IR_KEYTAB_TYPE ir_codes_avermedia_m135a[IR_KEYTAB_SIZE] = {
+	[0x00] = KEY_POWER2,
+	[0x2e] = KEY_DOT,		/* '.' */
+	[0x01] = KEY_MODE,		/* TV/FM */
+
+	[0x05] = KEY_1,
+	[0x06] = KEY_2,
+	[0x07] = KEY_3,
+	[0x09] = KEY_4,
+	[0x0a] = KEY_5,
+	[0x0b] = KEY_6,
+	[0x0d] = KEY_7,
+	[0x0e] = KEY_8,
+	[0x0f] = KEY_9,
+	[0x11] = KEY_0,
+
+	[0x13] = KEY_RIGHT,		/* -> */
+	[0x12] = KEY_LEFT,		/* <- */
+
+	[0x17] = KEY_SLEEP,		/* Capturar Imagem */
+	[0x10] = KEY_SHUFFLE,		/* Amostra */
+
+	/* FIXME: The keys bellow aren't ok */
+
+	[0x43] = KEY_CHANNELUP,
+	[0x42] = KEY_CHANNELDOWN,
+	[0x1f] = KEY_VOLUMEUP,
+	[0x1e] = KEY_VOLUMEDOWN,
+	[0x0c] = KEY_ENTER,
+
+	[0x14] = KEY_MUTE,
+	[0x08] = KEY_AUDIO,
+
+	[0x03] = KEY_TEXT,
+	[0x04] = KEY_EPG,
+	[0x2b] = KEY_TV2,		/* TV2 */
+
+	[0x1d] = KEY_RED,
+	[0x1c] = KEY_YELLOW,
+	[0x41] = KEY_GREEN,
+	[0x40] = KEY_BLUE,
+
+	[0x1a] = KEY_PLAYPAUSE,
+	[0x19] = KEY_RECORD,
+	[0x18] = KEY_PLAY,
+	[0x1b] = KEY_STOP,
+};
+EXPORT_SYMBOL_GPL(ir_codes_avermedia_m135a);
+
 /* Attila Kondoros <attila.kondoros@chello.hu> */
 IR_KEYTAB_TYPE ir_codes_apac_viewcomp[IR_KEYTAB_SIZE] = {
 
@@ -467,7 +517,8 @@ EXPORT_SYMBOL_GPL(ir_codes_adstech_dvb_t_pci);
 
 /* ---------------------------------------------------------------------- */
 
-/* MSI TV@nywhere remote */
+/* MSI TV@nywhere MASTER remote */
+
 IR_KEYTAB_TYPE ir_codes_msi_tvanywhere[IR_KEYTAB_SIZE] = {
 	/* Keys 0 to 9 */
 	[ 0x00 ] = KEY_0,
@@ -501,6 +552,95 @@ EXPORT_SYMBOL_GPL(ir_codes_msi_tvanywhere);
 
 /* ---------------------------------------------------------------------- */
 
+/*
+  Keycodes for remote on the MSI TV@nywhere Plus. The controller IC on the card
+  is marked "KS003". The controller is I2C at address 0x30, but does not seem
+  to respond to probes until a read is performed from a valid device.
+  I don't know why...
+
+  Note: This remote may be of similar or identical design to the
+  Pixelview remote (?).  The raw codes and duplicate button codes
+  appear to be the same.
+
+  Henry Wong <henry@stuffedcow.net>
+  Some changes to formatting and keycodes by Mark Schultz <n9xmj@yahoo.com>
+
+*/
+
+IR_KEYTAB_TYPE ir_codes_msi_tvanywhere_plus[IR_KEYTAB_SIZE] = {
+
+/*  ---- Remote Button Layout ----
+
+    POWER   SOURCE  SCAN    MUTE
+    TV/FM   1       2       3
+    |>      4       5       6
+    <|      7       8       9
+    ^^UP    0       +       RECALL
+    vvDN    RECORD  STOP    PLAY
+
+	MINIMIZE          ZOOM
+
+		  CH+
+      VOL-                   VOL+
+		  CH-
+
+	SNAPSHOT           MTS
+
+     <<      FUNC    >>     RESET
+*/
+
+	[0x01] = KEY_KP1,             /* 1 */
+	[0x0b] = KEY_KP2,             /* 2 */
+	[0x1b] = KEY_KP3,             /* 3 */
+	[0x05] = KEY_KP4,             /* 4 */
+	[0x09] = KEY_KP5,             /* 5 */
+	[0x15] = KEY_KP6,             /* 6 */
+	[0x06] = KEY_KP7,             /* 7 */
+	[0x0a] = KEY_KP8,             /* 8 */
+	[0x12] = KEY_KP9,             /* 9 */
+	[0x02] = KEY_KP0,             /* 0 */
+	[0x10] = KEY_KPPLUS,          /* + */
+	[0x13] = KEY_AGAIN,           /* Recall */
+
+	[0x1e] = KEY_POWER,           /* Power */
+	[0x07] = KEY_TUNER,           /* Source */
+	[0x1c] = KEY_SEARCH,          /* Scan */
+	[0x18] = KEY_MUTE,            /* Mute */
+
+	[0x03] = KEY_RADIO,           /* TV/FM */
+	/* The next four keys are duplicates that appear to send the
+	   same IR code as Ch+, Ch-, >>, and << .  The raw code assigned
+	   to them is the actual code + 0x20 - they will never be
+	   detected as such unless some way is discovered to distinguish
+	   these buttons from those that have the same code. */
+	[0x3f] = KEY_RIGHT,           /* |> and Ch+ */
+	[0x37] = KEY_LEFT,            /* <| and Ch- */
+	[0x2c] = KEY_UP,              /* ^^Up and >> */
+	[0x24] = KEY_DOWN,            /* vvDn and << */
+
+	[0x00] = KEY_RECORD,          /* Record */
+	[0x08] = KEY_STOP,            /* Stop */
+	[0x11] = KEY_PLAY,            /* Play */
+
+	[0x0f] = KEY_CLOSE,           /* Minimize */
+	[0x19] = KEY_ZOOM,            /* Zoom */
+	[0x1a] = KEY_SHUFFLE,         /* Snapshot */
+	[0x0d] = KEY_LANGUAGE,        /* MTS */
+
+	[0x14] = KEY_VOLUMEDOWN,      /* Vol- */
+	[0x16] = KEY_VOLUMEUP,        /* Vol+ */
+	[0x17] = KEY_CHANNELDOWN,     /* Ch- */
+	[0x1f] = KEY_CHANNELUP,       /* Ch+ */
+
+	[0x04] = KEY_REWIND,          /* << */
+	[0x0e] = KEY_MENU,            /* Function */
+	[0x0c] = KEY_FASTFORWARD,     /* >> */
+	[0x1d] = KEY_RESTART,         /* Reset */
+};
+EXPORT_SYMBOL_GPL(ir_codes_msi_tvanywhere_plus);
+
+/* ---------------------------------------------------------------------- */
+
 /* Cinergy 1400 DVB-T */
 IR_KEYTAB_TYPE ir_codes_cinergy_1400[IR_KEYTAB_SIZE] = {
 	[ 0x01 ] = KEY_POWER,
@@ -1792,12 +1932,61 @@ IR_KEYTAB_TYPE ir_codes_encore_enltv[IR_KEYTAB_SIZE] = {
 	[ 0x41 ] = KEY_GREEN,		/* AP2 */
 	[ 0x47 ] = KEY_YELLOW,		/* AP3 */
 	[ 0x57 ] = KEY_BLUE,		/* AP4 */
-
-
 };
-
 EXPORT_SYMBOL_GPL(ir_codes_encore_enltv);
 
+/* Encore ENLTV2-FM  - silver plastic - "Wand Media" written at the botton
+    Mauro Carvalho Chehab <mchehab@infradead.org> */
+IR_KEYTAB_TYPE ir_codes_encore_enltv2[IR_KEYTAB_SIZE] = {
+	[0x4c] = KEY_POWER2,
+	[0x4a] = KEY_TUNER,
+	[0x40] = KEY_1,
+	[0x60] = KEY_2,
+	[0x50] = KEY_3,
+	[0x70] = KEY_4,
+	[0x48] = KEY_5,
+	[0x68] = KEY_6,
+	[0x58] = KEY_7,
+	[0x78] = KEY_8,
+	[0x44] = KEY_9,
+	[0x54] = KEY_0,
+
+	[0x64] = KEY_LAST,		/* +100 */
+	[0x4e] = KEY_AGAIN,		/* Recall */
+
+	[0x6c] = KEY_SWITCHVIDEOMODE,	/* Video Source */
+	[0x5e] = KEY_MENU,
+	[0x56] = KEY_SCREEN,
+	[0x7a] = KEY_SETUP,
+
+	[0x46] = KEY_MUTE,
+	[0x5c] = KEY_MODE,		/* Stereo */
+	[0x74] = KEY_INFO,
+	[0x7c] = KEY_CLEAR,
+
+	[0x55] = KEY_UP,
+	[0x49] = KEY_DOWN,
+	[0x7e] = KEY_LEFT,
+	[0x59] = KEY_RIGHT,
+	[0x6a] = KEY_ENTER,
+
+	[0x42] = KEY_VOLUMEUP,
+	[0x62] = KEY_VOLUMEDOWN,
+	[0x52] = KEY_CHANNELUP,
+	[0x72] = KEY_CHANNELDOWN,
+
+	[0x41] = KEY_RECORD,
+	[0x51] = KEY_SHUFFLE,	/* Snapshot */
+	[0x75] = KEY_TIME,	/* Timeshift */
+	[0x71] = KEY_TV2,	/* PIP */
+
+	[0x45] = KEY_REWIND,
+	[0x6f] = KEY_PAUSE,
+	[0x7d] = KEY_FORWARD,
+	[0x79] = KEY_STOP,
+};
+EXPORT_SYMBOL_GPL(ir_codes_encore_enltv2);
+
 /* for the Technotrend 1500 bundled remotes (grey and black): */
 IR_KEYTAB_TYPE ir_codes_tt_1500[IR_KEYTAB_SIZE] = {
 	[ 0x01 ] = KEY_POWER,
@@ -2239,3 +2428,86 @@ IR_KEYTAB_TYPE ir_codes_avermedia_a16d[IR_KEYTAB_SIZE] = {
 	[0x2a] = KEY_MENU,
 };
 EXPORT_SYMBOL_GPL(ir_codes_avermedia_a16d);
+
+/* Encore ENLTV-FM v5.3
+   Mauro Carvalho Chehab <mchehab@infradead.org>
+ */
+IR_KEYTAB_TYPE ir_codes_encore_enltv_fm53[IR_KEYTAB_SIZE] = {
+	[0x10] = KEY_POWER2,
+	[0x06] = KEY_MUTE,
+
+	[0x09] = KEY_1,
+	[0x1d] = KEY_2,
+	[0x1f] = KEY_3,
+	[0x19] = KEY_4,
+	[0x1b] = KEY_5,
+	[0x11] = KEY_6,
+	[0x17] = KEY_7,
+	[0x12] = KEY_8,
+	[0x16] = KEY_9,
+	[0x48] = KEY_0,
+
+	[0x04] = KEY_LIST,		/* -/-- */
+	[0x40] = KEY_LAST,		/* recall */
+
+	[0x02] = KEY_MODE,		/* TV/AV */
+	[0x05] = KEY_SHUFFLE,		/* SNAPSHOT */
+
+	[0x4c] = KEY_CHANNELUP,		/* UP */
+	[0x00] = KEY_CHANNELDOWN,	/* DOWN */
+	[0x0d] = KEY_VOLUMEUP,		/* RIGHT */
+	[0x15] = KEY_VOLUMEDOWN,	/* LEFT */
+	[0x49] = KEY_ENTER,		/* OK */
+
+	[0x54] = KEY_RECORD,
+	[0x4d] = KEY_PLAY,		/* pause */
+
+	[0x1e] = KEY_UP,		/* video setting */
+	[0x0e] = KEY_RIGHT,		/* <- */
+	[0x1a] = KEY_LEFT,		/* -> */
+
+	[0x0a] = KEY_DOWN,		/* video default */
+	[0x0c] = KEY_ZOOM,		/* hide pannel */
+	[0x47] = KEY_SLEEP,		/* shutdown */
+};
+EXPORT_SYMBOL_GPL(ir_codes_encore_enltv_fm53);
+
+/* Zogis Real Audio 220 - 32 keys IR */
+IR_KEYTAB_TYPE ir_codes_real_audio_220_32_keys[IR_KEYTAB_SIZE] = {
+	[0x1c] = KEY_RADIO,
+	[0x12] = KEY_POWER2,
+
+	[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,
+	[0x00] = KEY_0,
+
+	[0x0c] = KEY_VOLUMEUP,
+	[0x18] = KEY_VOLUMEDOWN,
+	[0x0b] = KEY_CHANNELUP,
+	[0x15] = KEY_CHANNELDOWN,
+	[0x16] = KEY_ENTER,
+
+	[0x11] = KEY_LIST,		/* Source */
+	[0x0d] = KEY_AUDIO,		/* stereo */
+
+	[0x0f] = KEY_PREVIOUS,		/* Prev */
+	[0x1b] = KEY_PAUSE,		/* Timeshift */
+	[0x1a] = KEY_NEXT,		/* Next */
+
+	[0x0e] = KEY_STOP,
+	[0x1f] = KEY_PLAY,
+	[0x1e] = KEY_PLAYPAUSE,		/* Pause */
+
+	[0x1d] = KEY_RECORD,
+	[0x13] = KEY_MUTE,
+	[0x19] = KEY_SHUFFLE,		/* Snapshot */
+
+};
+EXPORT_SYMBOL_GPL(ir_codes_real_audio_220_32_keys);

+ 1 - 1
drivers/media/common/saa7146_core.c

@@ -234,7 +234,7 @@ void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt)
 int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt)
 {
 	__le32       *cpu;
-	dma_addr_t   dma_addr;
+	dma_addr_t   dma_addr = 0;
 
 	cpu = pci_alloc_consistent(pci, PAGE_SIZE, &dma_addr);
 	if (NULL == cpu) {

+ 1 - 1
drivers/media/common/saa7146_fops.c

@@ -533,7 +533,7 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,
 	memcpy(vfd, &device_template, sizeof(struct video_device));
 	strlcpy(vfd->name, name, sizeof(vfd->name));
 	vfd->release = video_device_release;
-	vfd->priv = dev;
+	video_set_drvdata(vfd, dev);
 
 	// fixme: -1 should be an insmod parameter *for the extension* (like "video_nr");
 	if (video_register_device(vfd, type, -1) < 0) {

+ 36 - 2
drivers/media/common/tuners/mt2060.c

@@ -170,6 +170,9 @@ static int mt2060_set_params(struct dvb_frontend *fe, struct dvb_frontend_parame
 	b[0] = REG_LO1B1;
 	b[1] = 0xFF;
 
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
 	mt2060_writeregs(priv,b,2);
 
 	freq = params->frequency / 1000; // Hz -> kHz
@@ -233,6 +236,9 @@ static int mt2060_set_params(struct dvb_frontend *fe, struct dvb_frontend_parame
 		i++;
 	} while (i<10);
 
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
 	return ret;
 }
 
@@ -296,13 +302,35 @@ static int mt2060_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
 static int mt2060_init(struct dvb_frontend *fe)
 {
 	struct mt2060_priv *priv = fe->tuner_priv;
-	return mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x33);
+	int ret;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
+	ret = mt2060_writereg(priv, REG_VGAG,
+			      (priv->cfg->clock_out << 6) | 0x33);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
+	return ret;
 }
 
 static int mt2060_sleep(struct dvb_frontend *fe)
 {
 	struct mt2060_priv *priv = fe->tuner_priv;
-	return mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x30);
+	int ret;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
+	ret = mt2060_writereg(priv, REG_VGAG,
+			      (priv->cfg->clock_out << 6) | 0x30);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
+	return ret;
 }
 
 static int mt2060_release(struct dvb_frontend *fe)
@@ -344,6 +372,9 @@ struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter
 	priv->i2c      = i2c;
 	priv->if1_freq = if1;
 
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
 	if (mt2060_readreg(priv,REG_PART_REV,&id) != 0) {
 		kfree(priv);
 		return NULL;
@@ -360,6 +391,9 @@ struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter
 
 	mt2060_calibrate(priv);
 
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
 	return fe;
 }
 EXPORT_SYMBOL(mt2060_attach);

+ 0 - 1
drivers/media/common/tuners/mxl5007t.c

@@ -979,7 +979,6 @@ struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe,
 	switch (instance) {
 	case 0:
 		goto fail;
-		break;
 	case 1:
 		/* new tuner instance */
 		state->config = cfg;

+ 0 - 1
drivers/media/common/tuners/tda18271-fe.c

@@ -1155,7 +1155,6 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
 	switch (instance) {
 	case 0:
 		goto fail;
-		break;
 	case 1:
 		/* new tuner instance */
 		priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO;

+ 7 - 5
drivers/media/common/tuners/tda827x.c

@@ -447,17 +447,19 @@ static void tda827xa_lna_gain(struct dvb_frontend *fe, int high,
 			else
 				arg = 0;
 		}
-		if (priv->cfg->tuner_callback)
-			priv->cfg->tuner_callback(priv->i2c_adap->algo_data,
-								gp_func, arg);
+		if (fe->callback)
+			fe->callback(priv->i2c_adap->algo_data,
+				     DVB_FRONTEND_COMPONENT_TUNER,
+				     gp_func, arg);
 		buf[1] = high ? 0 : 1;
 		if (priv->cfg->config == 2)
 			buf[1] = high ? 1 : 0;
 		i2c_transfer(priv->i2c_adap, &msg, 1);
 		break;
 	case 3: /* switch with GPIO of saa713x */
-		if (priv->cfg->tuner_callback)
-			priv->cfg->tuner_callback(priv->i2c_adap->algo_data, 0, high);
+		if (fe->callback)
+			fe->callback(priv->i2c_adap->algo_data,
+				     DVB_FRONTEND_COMPONENT_TUNER, 0, high);
 		break;
 	}
 }

+ 0 - 1
drivers/media/common/tuners/tda827x.h

@@ -36,7 +36,6 @@ struct tda827x_config
 	/* interface to tda829x driver */
 	unsigned int config;
 	int 	     switch_addr;
-	int (*tuner_callback) (void *dev, int command, int arg);
 
 	void (*agcf)(struct dvb_frontend *fe);
 };

+ 1 - 3
drivers/media/common/tuners/tda8290.c

@@ -672,10 +672,8 @@ struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe,
 	priv->i2c_props.addr     = i2c_addr;
 	priv->i2c_props.adap     = i2c_adap;
 	priv->i2c_props.name     = "tda829x";
-	if (cfg) {
+	if (cfg)
 		priv->cfg.config         = cfg->lna_cfg;
-		priv->cfg.tuner_callback = cfg->tuner_callback;
-	}
 
 	if (tda8290_probe(&priv->i2c_props) == 0) {
 		priv->ver = TDA8290;

+ 0 - 1
drivers/media/common/tuners/tda8290.h

@@ -22,7 +22,6 @@
 
 struct tda829x_config {
 	unsigned int lna_cfg;
-	int (*tuner_callback) (void *dev, int command, int arg);
 
 	unsigned int probe_tuner:1;
 #define TDA829X_PROBE_TUNER 0

+ 0 - 1
drivers/media/common/tuners/tda9887.c

@@ -686,7 +686,6 @@ struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe,
 	case 0:
 		mutex_unlock(&tda9887_list_mutex);
 		return NULL;
-		break;
 	case 1:
 		fe->analog_demod_priv = priv;
 		priv->mode = T_STANDBY;

+ 2 - 1
drivers/media/common/tuners/tuner-simple.c

@@ -142,6 +142,7 @@ static inline int tuner_stereo(const int type, const int status)
 	case TUNER_PHILIPS_FM1236_MK3:
 	case TUNER_PHILIPS_FM1256_IH3:
 	case TUNER_LG_NTSC_TAPE:
+	case TUNER_TCL_MF02GIP_5N:
 		return ((status & TUNER_SIGNAL) == TUNER_STEREO_MK3);
 	default:
 		return status & TUNER_STEREO;
@@ -494,6 +495,7 @@ static int simple_radio_bandswitch(struct dvb_frontend *fe, u8 *buffer)
 	case TUNER_PHILIPS_FMD1216ME_MK3:
 	case TUNER_LG_NTSC_TAPE:
 	case TUNER_PHILIPS_FM1256_IH3:
+	case TUNER_TCL_MF02GIP_5N:
 		buffer[3] = 0x19;
 		break;
 	case TUNER_TNF_5335MF:
@@ -1038,7 +1040,6 @@ struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe,
 	case 0:
 		mutex_unlock(&tuner_simple_list_mutex);
 		return NULL;
-		break;
 	case 1:
 		fe->tuner_priv = priv;
 

+ 22 - 0
drivers/media/common/tuners/tuner-types.c

@@ -1216,6 +1216,23 @@ static struct tuner_params tuner_samsung_tcpg_6121p30a_params[] = {
 	},
 };
 
+/* ------------ TUNER_TCL_MF02GIP-5N-E - TCL MF02GIP-5N ------------ */
+
+static struct tuner_range tuner_tcl_mf02gip_5n_ntsc_ranges[] = {
+	{ 16 * 172.00 /*MHz*/, 0x8e, 0x01, },
+	{ 16 * 448.00 /*MHz*/, 0x8e, 0x02, },
+	{ 16 * 999.99        , 0x8e, 0x04, },
+};
+
+static struct tuner_params tuner_tcl_mf02gip_5n_params[] = {
+	{
+		.type   = TUNER_PARAM_TYPE_NTSC,
+		.ranges = tuner_tcl_mf02gip_5n_ntsc_ranges,
+		.count  = ARRAY_SIZE(tuner_tcl_mf02gip_5n_ntsc_ranges),
+		.cb_first_if_lower_freq = 1,
+	},
+};
+
 /* --------------------------------------------------------------------- */
 
 struct tunertype tuners[] = {
@@ -1641,6 +1658,11 @@ struct tunertype tuners[] = {
 		.name   = "Xceive 5000 tuner",
 		/* see xc5000.c for details */
 	},
+	[TUNER_TCL_MF02GIP_5N] = { /* TCL tuner MF02GIP-5N-E */
+		.name   = "TCL tuner MF02GIP-5N-E",
+		.params = tuner_tcl_mf02gip_5n_params,
+		.count  = ARRAY_SIZE(tuner_tcl_mf02gip_5n_params),
+	},
 };
 EXPORT_SYMBOL(tuners);
 

+ 45 - 29
drivers/media/common/tuners/tuner-xc2028.c

@@ -71,9 +71,6 @@ struct firmware_properties {
 struct xc2028_data {
 	struct list_head        hybrid_tuner_instance_list;
 	struct tuner_i2c_props  i2c_props;
-	int                     (*tuner_callback) (void *dev,
-						   int command, int arg);
-	void			*video_dev;
 	__u32			frequency;
 
 	struct firmware_description *firm;
@@ -492,6 +489,23 @@ ret:
 	return i;
 }
 
+static inline int do_tuner_callback(struct dvb_frontend *fe, int cmd, int arg)
+{
+	struct xc2028_data *priv = fe->tuner_priv;
+
+	/* analog side (tuner-core) uses i2c_adap->algo_data.
+	 * digital side is not guaranteed to have algo_data defined.
+	 *
+	 * digital side will always have fe->dvb defined.
+	 * analog side (tuner-core) doesn't (yet) define fe->dvb.
+	 */
+
+	return (!fe->callback) ? -EINVAL :
+		fe->callback(((fe->dvb) && (fe->dvb->priv)) ?
+				fe->dvb->priv : priv->i2c_props.adap->algo_data,
+			     DVB_FRONTEND_COMPONENT_TUNER, cmd, arg);
+}
+
 static int load_firmware(struct dvb_frontend *fe, unsigned int type,
 			 v4l2_std_id *id)
 {
@@ -530,8 +544,7 @@ static int load_firmware(struct dvb_frontend *fe, unsigned int type,
 
 		if (!size) {
 			/* Special callback command received */
-			rc = priv->tuner_callback(priv->video_dev,
-						  XC2028_TUNER_RESET, 0);
+			rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0);
 			if (rc < 0) {
 				tuner_err("Error at RESET code %d\n",
 					   (*p) & 0x7f);
@@ -542,8 +555,7 @@ static int load_firmware(struct dvb_frontend *fe, unsigned int type,
 		if (size >= 0xff00) {
 			switch (size) {
 			case 0xff00:
-				rc = priv->tuner_callback(priv->video_dev,
-							XC2028_RESET_CLK, 0);
+				rc = do_tuner_callback(fe, XC2028_RESET_CLK, 0);
 				if (rc < 0) {
 					tuner_err("Error at RESET code %d\n",
 						  (*p) & 0x7f);
@@ -715,8 +727,7 @@ retry:
 	memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
 
 	/* Reset is needed before loading firmware */
-	rc = priv->tuner_callback(priv->video_dev,
-				  XC2028_TUNER_RESET, 0);
+	rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0);
 	if (rc < 0)
 		goto fail;
 
@@ -933,7 +944,7 @@ static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */,
 	   The reset CLK is needed only with tm6000.
 	   Driver should work fine even if this fails.
 	 */
-	priv->tuner_callback(priv->video_dev, XC2028_RESET_CLK, 1);
+	do_tuner_callback(fe, XC2028_RESET_CLK, 1);
 
 	msleep(10);
 
@@ -1002,11 +1013,6 @@ static int xc2028_set_params(struct dvb_frontend *fe,
 
 	tuner_dbg("%s called\n", __func__);
 
-	if (priv->ctrl.d2633)
-		type |= D2633;
-	else
-		type |= D2620;
-
 	switch(fe->ops.info.type) {
 	case FE_OFDM:
 		bw = p->u.ofdm.bandwidth;
@@ -1021,10 +1027,8 @@ static int xc2028_set_params(struct dvb_frontend *fe,
 		break;
 	case FE_ATSC:
 		bw = BANDWIDTH_6_MHZ;
-		/* The only ATSC firmware (at least on v2.7) is D2633,
-		   so overrides ctrl->d2633 */
-		type |= ATSC| D2633;
-		type &= ~D2620;
+		/* The only ATSC firmware (at least on v2.7) is D2633 */
+		type |= ATSC | D2633;
 		break;
 	/* DVB-S is not supported */
 	default:
@@ -1057,6 +1061,28 @@ static int xc2028_set_params(struct dvb_frontend *fe,
 		tuner_err("error: bandwidth not supported.\n");
 	};
 
+	/*
+	  Selects between D2633 or D2620 firmware.
+	  It doesn't make sense for ATSC, since it should be D2633 on all cases
+	 */
+	if (fe->ops.info.type != FE_ATSC) {
+		switch (priv->ctrl.type) {
+		case XC2028_D2633:
+			type |= D2633;
+			break;
+		case XC2028_D2620:
+			type |= D2620;
+			break;
+		case XC2028_AUTO:
+		default:
+			/* Zarlink seems to need D2633 */
+			if (priv->ctrl.demod == XC3028_FE_ZARLINK456)
+				type |= D2633;
+			else
+				type |= D2620;
+		}
+	}
+
 	/* All S-code tables need a 200kHz shift */
 	if (priv->ctrl.demod)
 		demod = priv->ctrl.demod + 200;
@@ -1177,20 +1203,10 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
 		break;
 	case 1:
 		/* new tuner instance */
-		priv->tuner_callback = cfg->callback;
 		priv->ctrl.max_len = 13;
 
 		mutex_init(&priv->lock);
 
-		/* analog side (tuner-core) uses i2c_adap->algo_data.
-		 * digital side is not guaranteed to have algo_data defined.
-		 *
-		 * digital side will always have fe->dvb defined.
-		 * analog side (tuner-core) doesn't (yet) define fe->dvb.
-		 */
-		priv->video_dev = ((fe->dvb) && (fe->dvb->priv)) ?
-				   fe->dvb->priv : cfg->i2c_adap->algo_data;
-
 		fe->tuner_priv = priv;
 		break;
 	case 2:

+ 7 - 3
drivers/media/common/tuners/tuner-xc2028.h

@@ -24,24 +24,28 @@
 #define	XC3028_FE_ZARLINK456	4560
 #define	XC3028_FE_CHINA		5200
 
+enum firmware_type {
+	XC2028_AUTO = 0,        /* By default, auto-detects */
+	XC2028_D2633,
+	XC2028_D2620,
+};
+
 struct xc2028_ctrl {
 	char			*fname;
 	int			max_len;
 	unsigned int		scode_table;
 	unsigned int		mts   :1;
-	unsigned int		d2633 :1;
 	unsigned int		input1:1;
 	unsigned int		vhfbw7:1;
 	unsigned int		uhfbw8:1;
 	unsigned int		demod;
+	enum firmware_type	type:2;
 };
 
 struct xc2028_config {
 	struct i2c_adapter *i2c_adap;
 	u8 		   i2c_addr;
-	void               *video_dev;
 	struct xc2028_ctrl *ctrl;
-	int                (*callback) (void *dev, int command, int arg);
 };
 
 /* xc2028 commands for callback */

+ 77 - 32
drivers/media/common/tuners/xc5000.c

@@ -30,7 +30,7 @@
 #include "dvb_frontend.h"
 
 #include "xc5000.h"
-#include "xc5000_priv.h"
+#include "tuner-i2c.h"
 
 static int debug;
 module_param(debug, int, 0644);
@@ -40,12 +40,26 @@ static int xc5000_load_fw_on_attach;
 module_param_named(init_fw, xc5000_load_fw_on_attach, int, 0644);
 MODULE_PARM_DESC(init_fw, "Load firmware during driver initialization.");
 
+static DEFINE_MUTEX(xc5000_list_mutex);
+static LIST_HEAD(hybrid_tuner_instance_list);
+
 #define dprintk(level,fmt, arg...) if (debug >= level) \
 	printk(KERN_INFO "%s: " fmt, "xc5000", ## arg)
 
 #define XC5000_DEFAULT_FIRMWARE "dvb-fe-xc5000-1.1.fw"
 #define XC5000_DEFAULT_FIRMWARE_SIZE 12332
 
+struct xc5000_priv {
+	struct tuner_i2c_props i2c_props;
+	struct list_head hybrid_tuner_instance_list;
+
+	u32 if_khz;
+	u32 freq_hz;
+	u32 bandwidth;
+	u8  video_standard;
+	u8  rf_mode;
+};
+
 /* Misc Defines */
 #define MAX_TV_STANDARD			23
 #define XC_MAX_I2C_WRITE_LENGTH		64
@@ -216,9 +230,12 @@ static void xc5000_TunerReset(struct dvb_frontend *fe)
 
 	dprintk(1, "%s()\n", __func__);
 
-	if (priv->cfg->tuner_callback) {
-		ret = priv->cfg->tuner_callback(priv->devptr,
-						XC5000_TUNER_RESET, 0);
+	if (fe->callback) {
+		ret = fe->callback(((fe->dvb) && (fe->dvb->priv)) ?
+					   fe->dvb->priv :
+					   priv->i2c_props.adap->algo_data,
+					   DVB_FRONTEND_COMPONENT_TUNER,
+					   XC5000_TUNER_RESET, 0);
 		if (ret)
 			printk(KERN_ERR "xc5000: reset failed\n");
 	} else
@@ -509,13 +526,13 @@ static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val)
 	u8 buf[2] = { reg >> 8, reg & 0xff };
 	u8 bval[2] = { 0, 0 };
 	struct i2c_msg msg[2] = {
-		{ .addr = priv->cfg->i2c_address,
+		{ .addr = priv->i2c_props.addr,
 			.flags = 0, .buf = &buf[0], .len = 2 },
-		{ .addr = priv->cfg->i2c_address,
+		{ .addr = priv->i2c_props.addr,
 			.flags = I2C_M_RD, .buf = &bval[0], .len = 2 },
 	};
 
-	if (i2c_transfer(priv->i2c, msg, 2) != 2) {
+	if (i2c_transfer(priv->i2c_props.adap, msg, 2) != 2) {
 		printk(KERN_WARNING "xc5000: I2C read failed\n");
 		return -EREMOTEIO;
 	}
@@ -526,10 +543,10 @@ static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val)
 
 static int xc5000_writeregs(struct xc5000_priv *priv, u8 *buf, u8 len)
 {
-	struct i2c_msg msg = { .addr = priv->cfg->i2c_address,
+	struct i2c_msg msg = { .addr = priv->i2c_props.addr,
 		.flags = 0, .buf = buf, .len = len };
 
-	if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+	if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) {
 		printk(KERN_ERR "xc5000: I2C write failed (len=%i)\n",
 			(int)len);
 		return -EREMOTEIO;
@@ -539,10 +556,10 @@ static int xc5000_writeregs(struct xc5000_priv *priv, u8 *buf, u8 len)
 
 static int xc5000_readregs(struct xc5000_priv *priv, u8 *buf, u8 len)
 {
-	struct i2c_msg msg = { .addr = priv->cfg->i2c_address,
+	struct i2c_msg msg = { .addr = priv->i2c_props.addr,
 		.flags = I2C_M_RD, .buf = buf, .len = len };
 
-	if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+	if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) {
 		printk(KERN_ERR "xc5000 I2C read failed (len=%i)\n",(int)len);
 		return -EREMOTEIO;
 	}
@@ -559,7 +576,7 @@ static int xc5000_fwupload(struct dvb_frontend* fe)
 	printk(KERN_INFO "xc5000: waiting for firmware upload (%s)...\n",
 		XC5000_DEFAULT_FIRMWARE);
 
-	ret = request_firmware(&fw, XC5000_DEFAULT_FIRMWARE, &priv->i2c->dev);
+	ret = request_firmware(&fw, XC5000_DEFAULT_FIRMWARE, &priv->i2c_props.adap->dev);
 	if (ret) {
 		printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n");
 		ret = XC_RESULT_RESET_FAILURE;
@@ -675,10 +692,10 @@ static int xc5000_set_params(struct dvb_frontend *fe,
 		return -EREMOTEIO;
 	}
 
-	ret = xc_set_IF_frequency(priv, priv->cfg->if_khz);
+	ret = xc_set_IF_frequency(priv, priv->if_khz);
 	if (ret != XC_RESULT_SUCCESS) {
 		printk(KERN_ERR "xc5000: xc_Set_IF_frequency(%d) failed\n",
-			priv->cfg->if_khz);
+		       priv->if_khz);
 		return -EIO;
 	}
 
@@ -897,9 +914,19 @@ static int xc5000_init(struct dvb_frontend *fe)
 
 static int xc5000_release(struct dvb_frontend *fe)
 {
+	struct xc5000_priv *priv = fe->tuner_priv;
+
 	dprintk(1, "%s()\n", __func__);
-	kfree(fe->tuner_priv);
+
+	mutex_lock(&xc5000_list_mutex);
+
+	if (priv)
+		hybrid_tuner_release_state(priv);
+
+	mutex_unlock(&xc5000_list_mutex);
+
 	fe->tuner_priv = NULL;
+
 	return 0;
 }
 
@@ -924,29 +951,43 @@ static const struct dvb_tuner_ops xc5000_tuner_ops = {
 
 struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
 				   struct i2c_adapter *i2c,
-				   struct xc5000_config *cfg, void *devptr)
+				   struct xc5000_config *cfg)
 {
 	struct xc5000_priv *priv = NULL;
+	int instance;
 	u16 id = 0;
 
-	dprintk(1, "%s()\n", __func__);
+	dprintk(1, "%s(%d-%04x)\n", __func__,
+		i2c ? i2c_adapter_id(i2c) : -1,
+		cfg ? cfg->i2c_address : -1);
 
-	priv = kzalloc(sizeof(struct xc5000_priv), GFP_KERNEL);
-	if (priv == NULL)
-		return NULL;
+	mutex_lock(&xc5000_list_mutex);
 
-	priv->cfg = cfg;
-	priv->bandwidth = BANDWIDTH_6_MHZ;
-	priv->i2c = i2c;
-	priv->devptr = devptr;
+	instance = hybrid_tuner_request_state(struct xc5000_priv, priv,
+					      hybrid_tuner_instance_list,
+					      i2c, cfg->i2c_address, "xc5000");
+	switch (instance) {
+	case 0:
+		goto fail;
+		break;
+	case 1:
+		/* new tuner instance */
+		priv->bandwidth = BANDWIDTH_6_MHZ;
+		priv->if_khz = cfg->if_khz;
+
+		fe->tuner_priv = priv;
+		break;
+	default:
+		/* existing tuner instance */
+		fe->tuner_priv = priv;
+		break;
+	}
 
 	/* Check if firmware has been loaded. It is possible that another
 	   instance of the driver has loaded the firmware.
 	 */
-	if (xc5000_readreg(priv, XREG_PRODUCT_ID, &id) != 0) {
-		kfree(priv);
-		return NULL;
-	}
+	if (xc5000_readreg(priv, XREG_PRODUCT_ID, &id) != 0)
+		goto fail;
 
 	switch(id) {
 	case XC_PRODUCT_ID_FW_LOADED:
@@ -967,19 +1008,23 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
 		printk(KERN_ERR
 			"xc5000: Device not found at addr 0x%02x (0x%x)\n",
 			cfg->i2c_address, id);
-		kfree(priv);
-		return NULL;
+		goto fail;
 	}
 
+	mutex_unlock(&xc5000_list_mutex);
+
 	memcpy(&fe->ops.tuner_ops, &xc5000_tuner_ops,
 		sizeof(struct dvb_tuner_ops));
 
-	fe->tuner_priv = priv;
-
 	if (xc5000_load_fw_on_attach)
 		xc5000_init(fe);
 
 	return fe;
+fail:
+	mutex_unlock(&xc5000_list_mutex);
+
+	xc5000_release(fe);
+	return NULL;
 }
 EXPORT_SYMBOL(xc5000_attach);
 

+ 2 - 6
drivers/media/common/tuners/xc5000.h

@@ -30,8 +30,6 @@ struct i2c_adapter;
 struct xc5000_config {
 	u8   i2c_address;
 	u32  if_khz;
-
-	int  (*tuner_callback) (void *priv, int command, int arg);
 };
 
 /* xc5000 callback command */
@@ -49,13 +47,11 @@ struct xc5000_config {
     (defined(CONFIG_MEDIA_TUNER_XC5000_MODULE) && defined(MODULE))
 extern struct dvb_frontend* xc5000_attach(struct dvb_frontend *fe,
 					  struct i2c_adapter *i2c,
-					  struct xc5000_config *cfg,
-					  void *devptr);
+					  struct xc5000_config *cfg);
 #else
 static inline struct dvb_frontend* xc5000_attach(struct dvb_frontend *fe,
 						 struct i2c_adapter *i2c,
-						 struct xc5000_config *cfg,
-						 void *devptr)
+						 struct xc5000_config *cfg)
 {
 	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
 	return NULL;

+ 0 - 37
drivers/media/common/tuners/xc5000_priv.h

@@ -1,37 +0,0 @@
-/*
- *  Driver for Xceive XC5000 "QAM/8VSB single chip tuner"
- *
- *  Copyright (c) 2007 Steven Toth <stoth@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; 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 XC5000_PRIV_H
-#define XC5000_PRIV_H
-
-struct xc5000_priv {
-	struct xc5000_config *cfg;
-	struct i2c_adapter   *i2c;
-
-	u32 freq_hz;
-	u32 bandwidth;
-	u8  video_standard;
-	u8  rf_mode;
-
-	void *devptr;
-};
-
-#endif

+ 4 - 1
drivers/media/dvb/Kconfig

@@ -20,7 +20,6 @@ comment "Supported USB Adapters"
 source "drivers/media/dvb/dvb-usb/Kconfig"
 source "drivers/media/dvb/ttusb-budget/Kconfig"
 source "drivers/media/dvb/ttusb-dec/Kconfig"
-source "drivers/media/dvb/cinergyT2/Kconfig"
 source "drivers/media/dvb/siano/Kconfig"
 
 comment "Supported FlexCopII (B2C2) Adapters"
@@ -35,6 +34,10 @@ comment "Supported Pluto2 Adapters"
 	depends on DVB_CORE && PCI && I2C
 source "drivers/media/dvb/pluto2/Kconfig"
 
+comment "Supported SDMC DM1105 Adapters"
+	depends on DVB_CORE && PCI && I2C
+source "drivers/media/dvb/dm1105/Kconfig"
+
 comment "Supported DVB Frontends"
 	depends on DVB_CORE
 source "drivers/media/dvb/frontends/Kconfig"

+ 1 - 1
drivers/media/dvb/Makefile

@@ -2,4 +2,4 @@
 # Makefile for the kernel multimedia device drivers.
 #
 
-obj-y        := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ cinergyT2/ dvb-usb/ pluto2/ siano/
+obj-y        := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ dvb-usb/ pluto2/ siano/ dm1105/

+ 1 - 1
drivers/media/dvb/b2c2/flexcop-dma.c

@@ -10,7 +10,7 @@
 int flexcop_dma_allocate(struct pci_dev *pdev, struct flexcop_dma *dma, u32 size)
 {
 	u8 *tcpu;
-	dma_addr_t tdma;
+	dma_addr_t tdma = 0;
 
 	if (size % 2) {
 		err("dma buffersize has to be even.");

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

@@ -702,7 +702,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
 	}
 
 	if (card->fe == NULL)
-		printk("dvb-bt8xx: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n",
+		printk("dvb-bt8xx: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
 		       card->bt->dev->vendor,
 		       card->bt->dev->device,
 		       card->bt->dev->subsystem_vendor,

+ 0 - 85
drivers/media/dvb/cinergyT2/Kconfig

@@ -1,85 +0,0 @@
-config DVB_CINERGYT2
-	tristate "Terratec CinergyT2/qanu USB2 DVB-T receiver"
-	depends on DVB_CORE && USB && INPUT
-	help
-	  Support for "TerraTec CinergyT2" USB2.0 Highspeed DVB Receivers
-
-	  Say Y if you own such a device and want to use it.
-
-
-config DVB_CINERGYT2_TUNING
-	bool "sophisticated fine-tuning for CinergyT2 cards"
-	depends on DVB_CINERGYT2
-	help
-	  Here you can fine-tune some parameters of the CinergyT2 driver.
-
-	  Normally you don't need to touch this, but in exotic setups you
-	  may fine-tune your setup and adjust e.g. DMA buffer sizes for
-	  a particular application.
-
-
-config DVB_CINERGYT2_STREAM_URB_COUNT
-	int "Number of queued USB Request Blocks for Highspeed Stream Transfers"
-	depends on DVB_CINERGYT2_TUNING
-	default "32"
-	help
-	  USB Request Blocks for Highspeed Stream transfers are scheduled in
-	  a queue for the Host Controller.
-
-	  Usually the default value is a safe choice.
-
-	  You may increase this number if you are using this device in a
-	  Server Environment with many high-traffic USB Highspeed devices
-	  sharing the same USB bus.
-
-
-config DVB_CINERGYT2_STREAM_BUF_SIZE
-	int "Size of URB Stream Buffers for Highspeed Transfers"
-	depends on DVB_CINERGYT2_TUNING
-	default "512"
-	help
-	  Should be a multiple of native buffer size of 512 bytes.
-	  Default value is a safe choice.
-
-	  You may increase this number if you are using this device in a
-	  Server Environment with many high-traffic USB Highspeed devices
-	  sharing the same USB bus.
-
-
-config DVB_CINERGYT2_QUERY_INTERVAL
-	int "Status update interval [milliseconds]"
-	depends on DVB_CINERGYT2_TUNING
-	default "250"
-	help
-	  This is the interval for status readouts from the demodulator.
-	  You may try lower values if you need more responsive signal quality
-	  measurements.
-
-	  Please keep in mind that these updates cause traffic on the tuner
-	  control bus and thus may or may not affect reception sensitivity.
-
-	  The default value should be a safe choice for common applications.
-
-
-config DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE
-	bool "Register the onboard IR Remote Control Receiver as Input Device"
-	depends on DVB_CINERGYT2_TUNING
-	default y
-	help
-	  Enable this option if you want to use the onboard Infrared Remote
-	  Control Receiver as Linux-Input device.
-
-	  Right now only the keycode table for the default Remote Control
-	  delivered with the device is supported, please see the driver
-	  source code to find out how to add support for other controls.
-
-
-config DVB_CINERGYT2_RC_QUERY_INTERVAL
-	int "Infrared Remote Controller update interval [milliseconds]"
-	depends on DVB_CINERGYT2_TUNING && DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE
-	default "50"
-	help
-	  If you have a very fast-repeating remote control you can try lower
-	  values, for normal consumer receivers the default value should be
-	  a safe choice.
-

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

@@ -1,3 +0,0 @@
-obj-$(CONFIG_DVB_CINERGYT2) += cinergyT2.o
-
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/

+ 0 - 1105
drivers/media/dvb/cinergyT2/cinergyT2.c

@@ -1,1105 +0,0 @@
-/*
- * TerraTec Cinergy T²/qanu USB2 DVB-T adapter.
- *
- * Copyright (C) 2004 Daniel Mack <daniel@qanu.de> and
- *		    Holger Waechtler <holger@qanu.de>
- *
- *  Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/usb.h>
-#include <linux/input.h>
-#include <linux/dvb/frontend.h>
-#include <linux/mutex.h>
-#include <linux/mm.h>
-#include <asm/io.h>
-
-#include "dmxdev.h"
-#include "dvb_demux.h"
-#include "dvb_net.h"
-
-#ifdef CONFIG_DVB_CINERGYT2_TUNING
-	#define STREAM_URB_COUNT (CONFIG_DVB_CINERGYT2_STREAM_URB_COUNT)
-	#define STREAM_BUF_SIZE (CONFIG_DVB_CINERGYT2_STREAM_BUF_SIZE)
-	#define QUERY_INTERVAL (CONFIG_DVB_CINERGYT2_QUERY_INTERVAL)
-	#ifdef CONFIG_DVB_CINERGYT2_ENABLE_RC_INPUT_DEVICE
-		#define RC_QUERY_INTERVAL (CONFIG_DVB_CINERGYT2_RC_QUERY_INTERVAL)
-		#define ENABLE_RC (1)
-	#endif
-#else
-	#define STREAM_URB_COUNT (32)
-	#define STREAM_BUF_SIZE (512)	/* bytes */
-	#define ENABLE_RC (1)
-	#define RC_QUERY_INTERVAL (50)	/* milliseconds */
-	#define QUERY_INTERVAL (333)	/* milliseconds */
-#endif
-
-#define DRIVER_NAME "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver"
-
-static int debug;
-module_param_named(debug, debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-#define dprintk(level, args...)						\
-do {									\
-	if ((debug & level)) {						\
-		printk("%s: %s(): ", KBUILD_MODNAME,			\
-		       __func__);					\
-		printk(args); }						\
-} while (0)
-
-enum cinergyt2_ep1_cmd {
-	CINERGYT2_EP1_PID_TABLE_RESET		= 0x01,
-	CINERGYT2_EP1_PID_SETUP			= 0x02,
-	CINERGYT2_EP1_CONTROL_STREAM_TRANSFER	= 0x03,
-	CINERGYT2_EP1_SET_TUNER_PARAMETERS	= 0x04,
-	CINERGYT2_EP1_GET_TUNER_STATUS		= 0x05,
-	CINERGYT2_EP1_START_SCAN		= 0x06,
-	CINERGYT2_EP1_CONTINUE_SCAN		= 0x07,
-	CINERGYT2_EP1_GET_RC_EVENTS		= 0x08,
-	CINERGYT2_EP1_SLEEP_MODE		= 0x09
-};
-
-struct dvbt_set_parameters_msg {
-	uint8_t cmd;
-	__le32 freq;
-	uint8_t bandwidth;
-	__le16 tps;
-	uint8_t flags;
-} __attribute__((packed));
-
-struct dvbt_get_status_msg {
-	__le32 freq;
-	uint8_t bandwidth;
-	__le16 tps;
-	uint8_t flags;
-	__le16 gain;
-	uint8_t snr;
-	__le32 viterbi_error_rate;
-	__le32 rs_error_rate;
-	__le32 uncorrected_block_count;
-	uint8_t lock_bits;
-	uint8_t prev_lock_bits;
-} __attribute__((packed));
-
-static struct dvb_frontend_info cinergyt2_fe_info = {
-	.name = DRIVER_NAME,
-	.type = FE_OFDM,
-	.frequency_min = 174000000,
-	.frequency_max = 862000000,
-	.frequency_stepsize = 166667,
-	.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_HIERARCHY_AUTO | FE_CAN_RECOVER | FE_CAN_MUTE_TS
-};
-
-struct cinergyt2 {
-	struct dvb_demux demux;
-	struct usb_device *udev;
-	struct mutex sem;
-	struct mutex wq_sem;
-	struct dvb_adapter adapter;
-	struct dvb_device *fedev;
-	struct dmxdev dmxdev;
-	struct dvb_net dvbnet;
-
-	int streaming;
-	int sleeping;
-
-	struct dvbt_set_parameters_msg param;
-	struct dvbt_get_status_msg status;
-	struct delayed_work query_work;
-
-	wait_queue_head_t poll_wq;
-	int pending_fe_events;
-	int disconnect_pending;
-	unsigned int uncorrected_block_count;
-	atomic_t inuse;
-
-	void *streambuf;
-	dma_addr_t streambuf_dmahandle;
-	struct urb *stream_urb [STREAM_URB_COUNT];
-
-#ifdef ENABLE_RC
-	struct input_dev *rc_input_dev;
-	char phys[64];
-	struct delayed_work rc_query_work;
-	int rc_input_event;
-	__le32 rc_last_code;
-	unsigned long last_event_jiffies;
-#endif
-};
-
-enum {
-	CINERGYT2_RC_EVENT_TYPE_NONE = 0x00,
-	CINERGYT2_RC_EVENT_TYPE_NEC  = 0x01,
-	CINERGYT2_RC_EVENT_TYPE_RC5  = 0x02
-};
-
-struct cinergyt2_rc_event {
-	char type;
-	__le32 value;
-} __attribute__((packed));
-
-static const uint32_t rc_keys[] = {
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xfe01eb04,	KEY_POWER,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xfd02eb04,	KEY_1,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xfc03eb04,	KEY_2,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xfb04eb04,	KEY_3,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xfa05eb04,	KEY_4,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xf906eb04,	KEY_5,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xf807eb04,	KEY_6,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xf708eb04,	KEY_7,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xf609eb04,	KEY_8,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xf50aeb04,	KEY_9,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xf30ceb04,	KEY_0,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xf40beb04,	KEY_VIDEO,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xf20deb04,	KEY_REFRESH,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xf10eeb04,	KEY_SELECT,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xf00feb04,	KEY_EPG,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xef10eb04,	KEY_UP,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xeb14eb04,	KEY_DOWN,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xee11eb04,	KEY_LEFT,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xec13eb04,	KEY_RIGHT,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xed12eb04,	KEY_OK,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xea15eb04,	KEY_TEXT,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xe916eb04,	KEY_INFO,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xe817eb04,	KEY_RED,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xe718eb04,	KEY_GREEN,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xe619eb04,	KEY_YELLOW,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xe51aeb04,	KEY_BLUE,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xe31ceb04,	KEY_VOLUMEUP,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xe11eeb04,	KEY_VOLUMEDOWN,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xe21deb04,	KEY_MUTE,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xe41beb04,	KEY_CHANNELUP,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xe01feb04,	KEY_CHANNELDOWN,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xbf40eb04,	KEY_PAUSE,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xb34ceb04,	KEY_PLAY,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xa758eb04,	KEY_RECORD,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xab54eb04,	KEY_PREVIOUS,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xb748eb04,	KEY_STOP,
-	CINERGYT2_RC_EVENT_TYPE_NEC,	0xa35ceb04,	KEY_NEXT
-};
-
-static int cinergyt2_command (struct cinergyt2 *cinergyt2,
-			      char *send_buf, int send_buf_len,
-			      char *recv_buf, int recv_buf_len)
-{
-	int actual_len;
-	char dummy;
-	int ret;
-
-	ret = usb_bulk_msg(cinergyt2->udev, usb_sndbulkpipe(cinergyt2->udev, 1),
-			   send_buf, send_buf_len, &actual_len, 1000);
-
-	if (ret)
-		dprintk(1, "usb_bulk_msg (send) failed, err %i\n", ret);
-
-	if (!recv_buf)
-		recv_buf = &dummy;
-
-	ret = usb_bulk_msg(cinergyt2->udev, usb_rcvbulkpipe(cinergyt2->udev, 1),
-			   recv_buf, recv_buf_len, &actual_len, 1000);
-
-	if (ret)
-		dprintk(1, "usb_bulk_msg (read) failed, err %i\n", ret);
-
-	return ret ? ret : actual_len;
-}
-
-static void cinergyt2_control_stream_transfer (struct cinergyt2 *cinergyt2, int enable)
-{
-	char buf [] = { CINERGYT2_EP1_CONTROL_STREAM_TRANSFER, enable ? 1 : 0 };
-	cinergyt2_command(cinergyt2, buf, sizeof(buf), NULL, 0);
-}
-
-static void cinergyt2_sleep (struct cinergyt2 *cinergyt2, int sleep)
-{
-	char buf [] = { CINERGYT2_EP1_SLEEP_MODE, sleep ? 1 : 0 };
-	cinergyt2_command(cinergyt2, buf, sizeof(buf), NULL, 0);
-	cinergyt2->sleeping = sleep;
-}
-
-static void cinergyt2_stream_irq (struct urb *urb);
-
-static int cinergyt2_submit_stream_urb (struct cinergyt2 *cinergyt2, struct urb *urb)
-{
-	int err;
-
-	usb_fill_bulk_urb(urb,
-			  cinergyt2->udev,
-			  usb_rcvbulkpipe(cinergyt2->udev, 0x2),
-			  urb->transfer_buffer,
-			  STREAM_BUF_SIZE,
-			  cinergyt2_stream_irq,
-			  cinergyt2);
-
-	if ((err = usb_submit_urb(urb, GFP_ATOMIC)))
-		dprintk(1, "urb submission failed (err = %i)!\n", err);
-
-	return err;
-}
-
-static void cinergyt2_stream_irq (struct urb *urb)
-{
-	struct cinergyt2 *cinergyt2 = urb->context;
-
-	if (urb->actual_length > 0)
-		dvb_dmx_swfilter(&cinergyt2->demux,
-				 urb->transfer_buffer, urb->actual_length);
-
-	if (cinergyt2->streaming)
-		cinergyt2_submit_stream_urb(cinergyt2, urb);
-}
-
-static void cinergyt2_free_stream_urbs (struct cinergyt2 *cinergyt2)
-{
-	int i;
-
-	for (i=0; i<STREAM_URB_COUNT; i++)
-		usb_free_urb(cinergyt2->stream_urb[i]);
-
-	usb_buffer_free(cinergyt2->udev, STREAM_URB_COUNT*STREAM_BUF_SIZE,
-			    cinergyt2->streambuf, cinergyt2->streambuf_dmahandle);
-}
-
-static int cinergyt2_alloc_stream_urbs (struct cinergyt2 *cinergyt2)
-{
-	int i;
-
-	cinergyt2->streambuf = usb_buffer_alloc(cinergyt2->udev, STREAM_URB_COUNT*STREAM_BUF_SIZE,
-					      GFP_KERNEL, &cinergyt2->streambuf_dmahandle);
-	if (!cinergyt2->streambuf) {
-		dprintk(1, "failed to alloc consistent stream memory area, bailing out!\n");
-		return -ENOMEM;
-	}
-
-	memset(cinergyt2->streambuf, 0, STREAM_URB_COUNT*STREAM_BUF_SIZE);
-
-	for (i=0; i<STREAM_URB_COUNT; i++) {
-		struct urb *urb;
-
-		if (!(urb = usb_alloc_urb(0, GFP_ATOMIC))) {
-			dprintk(1, "failed to alloc consistent stream urbs, bailing out!\n");
-			cinergyt2_free_stream_urbs(cinergyt2);
-			return -ENOMEM;
-		}
-
-		urb->transfer_buffer = cinergyt2->streambuf + i * STREAM_BUF_SIZE;
-		urb->transfer_buffer_length = STREAM_BUF_SIZE;
-
-		cinergyt2->stream_urb[i] = urb;
-	}
-
-	return 0;
-}
-
-static void cinergyt2_stop_stream_xfer (struct cinergyt2 *cinergyt2)
-{
-	int i;
-
-	cinergyt2_control_stream_transfer(cinergyt2, 0);
-
-	for (i=0; i<STREAM_URB_COUNT; i++)
-		usb_kill_urb(cinergyt2->stream_urb[i]);
-}
-
-static int cinergyt2_start_stream_xfer (struct cinergyt2 *cinergyt2)
-{
-	int i, err;
-
-	for (i=0; i<STREAM_URB_COUNT; i++) {
-		if ((err = cinergyt2_submit_stream_urb(cinergyt2, cinergyt2->stream_urb[i]))) {
-			cinergyt2_stop_stream_xfer(cinergyt2);
-			dprintk(1, "failed urb submission (%i: err = %i)!\n", i, err);
-			return err;
-		}
-	}
-
-	cinergyt2_control_stream_transfer(cinergyt2, 1);
-	return 0;
-}
-
-static int cinergyt2_start_feed(struct dvb_demux_feed *dvbdmxfeed)
-{
-	struct dvb_demux *demux = dvbdmxfeed->demux;
-	struct cinergyt2 *cinergyt2 = demux->priv;
-
-	if (cinergyt2->disconnect_pending)
-		return -EAGAIN;
-	if (mutex_lock_interruptible(&cinergyt2->sem))
-		return -ERESTARTSYS;
-
-	if (cinergyt2->streaming == 0)
-		cinergyt2_start_stream_xfer(cinergyt2);
-
-	cinergyt2->streaming++;
-	mutex_unlock(&cinergyt2->sem);
-	return 0;
-}
-
-static int cinergyt2_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
-{
-	struct dvb_demux *demux = dvbdmxfeed->demux;
-	struct cinergyt2 *cinergyt2 = demux->priv;
-
-	if (cinergyt2->disconnect_pending)
-		return -EAGAIN;
-	if (mutex_lock_interruptible(&cinergyt2->sem))
-		return -ERESTARTSYS;
-
-	if (--cinergyt2->streaming == 0)
-		cinergyt2_stop_stream_xfer(cinergyt2);
-
-	mutex_unlock(&cinergyt2->sem);
-	return 0;
-}
-
-/**
- *  convert linux-dvb frontend parameter set into TPS.
- *  See ETSI ETS-300744, section 4.6.2, table 9 for details.
- *
- *  This function is probably reusable and may better get placed in a support
- *  library.
- *
- *  We replace errornous fields by default TPS fields (the ones with value 0).
- */
-static uint16_t compute_tps (struct dvb_frontend_parameters *p)
-{
-	struct dvb_ofdm_parameters *op = &p->u.ofdm;
-	uint16_t tps = 0;
-
-	switch (op->code_rate_HP) {
-		case FEC_2_3:
-			tps |= (1 << 7);
-			break;
-		case FEC_3_4:
-			tps |= (2 << 7);
-			break;
-		case FEC_5_6:
-			tps |= (3 << 7);
-			break;
-		case FEC_7_8:
-			tps |= (4 << 7);
-			break;
-		case FEC_1_2:
-		case FEC_AUTO:
-		default:
-			/* tps |= (0 << 7) */;
-	}
-
-	switch (op->code_rate_LP) {
-		case FEC_2_3:
-			tps |= (1 << 4);
-			break;
-		case FEC_3_4:
-			tps |= (2 << 4);
-			break;
-		case FEC_5_6:
-			tps |= (3 << 4);
-			break;
-		case FEC_7_8:
-			tps |= (4 << 4);
-			break;
-		case FEC_1_2:
-		case FEC_AUTO:
-		default:
-			/* tps |= (0 << 4) */;
-	}
-
-	switch (op->constellation) {
-		case QAM_16:
-			tps |= (1 << 13);
-			break;
-		case QAM_64:
-			tps |= (2 << 13);
-			break;
-		case QPSK:
-		default:
-			/* tps |= (0 << 13) */;
-	}
-
-	switch (op->transmission_mode) {
-		case TRANSMISSION_MODE_8K:
-			tps |= (1 << 0);
-			break;
-		case TRANSMISSION_MODE_2K:
-		default:
-			/* tps |= (0 << 0) */;
-	}
-
-	switch (op->guard_interval) {
-		case GUARD_INTERVAL_1_16:
-			tps |= (1 << 2);
-			break;
-		case GUARD_INTERVAL_1_8:
-			tps |= (2 << 2);
-			break;
-		case GUARD_INTERVAL_1_4:
-			tps |= (3 << 2);
-			break;
-		case GUARD_INTERVAL_1_32:
-		default:
-			/* tps |= (0 << 2) */;
-	}
-
-	switch (op->hierarchy_information) {
-		case HIERARCHY_1:
-			tps |= (1 << 10);
-			break;
-		case HIERARCHY_2:
-			tps |= (2 << 10);
-			break;
-		case HIERARCHY_4:
-			tps |= (3 << 10);
-			break;
-		case HIERARCHY_NONE:
-		default:
-			/* tps |= (0 << 10) */;
-	}
-
-	return tps;
-}
-
-static int cinergyt2_open (struct inode *inode, struct file *file)
-{
-	struct dvb_device *dvbdev = file->private_data;
-	struct cinergyt2 *cinergyt2 = dvbdev->priv;
-	int err = -EAGAIN;
-
-	if (cinergyt2->disconnect_pending)
-		goto out;
-	err = mutex_lock_interruptible(&cinergyt2->wq_sem);
-	if (err)
-		goto out;
-
-	err = mutex_lock_interruptible(&cinergyt2->sem);
-	if (err)
-		goto out_unlock1;
-
-	if ((err = dvb_generic_open(inode, file)))
-		goto out_unlock2;
-
-	if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
-		cinergyt2_sleep(cinergyt2, 0);
-		schedule_delayed_work(&cinergyt2->query_work, HZ/2);
-	}
-
-	atomic_inc(&cinergyt2->inuse);
-
-out_unlock2:
-	mutex_unlock(&cinergyt2->sem);
-out_unlock1:
-	mutex_unlock(&cinergyt2->wq_sem);
-out:
-	return err;
-}
-
-static void cinergyt2_unregister(struct cinergyt2 *cinergyt2)
-{
-	dvb_net_release(&cinergyt2->dvbnet);
-	dvb_dmxdev_release(&cinergyt2->dmxdev);
-	dvb_dmx_release(&cinergyt2->demux);
-	dvb_unregister_device(cinergyt2->fedev);
-	dvb_unregister_adapter(&cinergyt2->adapter);
-
-	cinergyt2_free_stream_urbs(cinergyt2);
-	kfree(cinergyt2);
-}
-
-static int cinergyt2_release (struct inode *inode, struct file *file)
-{
-	struct dvb_device *dvbdev = file->private_data;
-	struct cinergyt2 *cinergyt2 = dvbdev->priv;
-
-	mutex_lock(&cinergyt2->wq_sem);
-
-	if (!cinergyt2->disconnect_pending && (file->f_flags & O_ACCMODE) != O_RDONLY) {
-		cancel_rearming_delayed_work(&cinergyt2->query_work);
-
-		mutex_lock(&cinergyt2->sem);
-		cinergyt2_sleep(cinergyt2, 1);
-		mutex_unlock(&cinergyt2->sem);
-	}
-
-	mutex_unlock(&cinergyt2->wq_sem);
-
-	if (atomic_dec_and_test(&cinergyt2->inuse) && cinergyt2->disconnect_pending) {
-		warn("delayed unregister in release");
-		cinergyt2_unregister(cinergyt2);
-	}
-
-	return dvb_generic_release(inode, file);
-}
-
-static unsigned int cinergyt2_poll (struct file *file, struct poll_table_struct *wait)
-{
-	struct dvb_device *dvbdev = file->private_data;
-	struct cinergyt2 *cinergyt2 = dvbdev->priv;
-	unsigned int mask = 0;
-
-	if (cinergyt2->disconnect_pending)
-		return -EAGAIN;
-	if (mutex_lock_interruptible(&cinergyt2->sem))
-		return -ERESTARTSYS;
-
-	poll_wait(file, &cinergyt2->poll_wq, wait);
-
-	if (cinergyt2->pending_fe_events != 0)
-		mask |= (POLLIN | POLLRDNORM | POLLPRI);
-
-	mutex_unlock(&cinergyt2->sem);
-
-	return mask;
-}
-
-
-static int cinergyt2_ioctl (struct inode *inode, struct file *file,
-		     unsigned cmd, unsigned long arg)
-{
-	struct dvb_device *dvbdev = file->private_data;
-	struct cinergyt2 *cinergyt2 = dvbdev->priv;
-	struct dvbt_get_status_msg *stat = &cinergyt2->status;
-	fe_status_t status = 0;
-
-	switch (cmd) {
-	case FE_GET_INFO:
-		return copy_to_user((void __user*) arg, &cinergyt2_fe_info,
-				    sizeof(struct dvb_frontend_info));
-
-	case FE_READ_STATUS:
-		if (0xffff - le16_to_cpu(stat->gain) > 30)
-			status |= FE_HAS_SIGNAL;
-		if (stat->lock_bits & (1 << 6))
-			status |= FE_HAS_LOCK;
-		if (stat->lock_bits & (1 << 5))
-			status |= FE_HAS_SYNC;
-		if (stat->lock_bits & (1 << 4))
-			status |= FE_HAS_CARRIER;
-		if (stat->lock_bits & (1 << 1))
-			status |= FE_HAS_VITERBI;
-
-		return copy_to_user((void  __user*) arg, &status, sizeof(status));
-
-	case FE_READ_BER:
-		return put_user(le32_to_cpu(stat->viterbi_error_rate),
-				(__u32 __user *) arg);
-
-	case FE_READ_SIGNAL_STRENGTH:
-		return put_user(0xffff - le16_to_cpu(stat->gain),
-				(__u16 __user *) arg);
-
-	case FE_READ_SNR:
-		return put_user((stat->snr << 8) | stat->snr,
-				(__u16 __user *) arg);
-
-	case FE_READ_UNCORRECTED_BLOCKS:
-	{
-		uint32_t unc_count;
-
-		if (mutex_lock_interruptible(&cinergyt2->sem))
-			return -ERESTARTSYS;
-		unc_count = cinergyt2->uncorrected_block_count;
-		cinergyt2->uncorrected_block_count = 0;
-		mutex_unlock(&cinergyt2->sem);
-
-		/* UNC are already converted to host byte order... */
-		return put_user(unc_count,(__u32 __user *) arg);
-	}
-	case FE_SET_FRONTEND:
-	{
-		struct dvbt_set_parameters_msg *param = &cinergyt2->param;
-		struct dvb_frontend_parameters p;
-		int err;
-
-		if ((file->f_flags & O_ACCMODE) == O_RDONLY)
-			return -EPERM;
-
-		if (copy_from_user(&p, (void  __user*) arg, sizeof(p)))
-			return -EFAULT;
-
-		if (cinergyt2->disconnect_pending)
-			return -EAGAIN;
-		if (mutex_lock_interruptible(&cinergyt2->sem))
-			return -ERESTARTSYS;
-
-		param->cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS;
-		param->tps = cpu_to_le16(compute_tps(&p));
-		param->freq = cpu_to_le32(p.frequency / 1000);
-		param->bandwidth = 8 - p.u.ofdm.bandwidth - BANDWIDTH_8_MHZ;
-
-		stat->lock_bits = 0;
-		cinergyt2->pending_fe_events++;
-		wake_up_interruptible(&cinergyt2->poll_wq);
-
-		err = cinergyt2_command(cinergyt2,
-					(char *) param, sizeof(*param),
-					NULL, 0);
-
-		mutex_unlock(&cinergyt2->sem);
-
-		return (err < 0) ? err : 0;
-	}
-
-	case FE_GET_FRONTEND:
-		/**
-		 *  trivial to implement (see struct dvbt_get_status_msg).
-		 *  equivalent to FE_READ ioctls, but needs
-		 *  TPS -> linux-dvb parameter set conversion. Feel free
-		 *  to implement this and send us a patch if you need this
-		 *  functionality.
-		 */
-		break;
-
-	case FE_GET_EVENT:
-	{
-		/**
-		 *  for now we only fill the status field. the parameters
-		 *  are trivial to fill as soon FE_GET_FRONTEND is done.
-		 */
-		struct dvb_frontend_event __user *e = (void __user *) arg;
-		if (cinergyt2->pending_fe_events == 0) {
-			if (file->f_flags & O_NONBLOCK)
-				return -EWOULDBLOCK;
-			wait_event_interruptible(cinergyt2->poll_wq,
-						 cinergyt2->pending_fe_events > 0);
-		}
-		cinergyt2->pending_fe_events = 0;
-		return cinergyt2_ioctl(inode, file, FE_READ_STATUS,
-					(unsigned long) &e->status);
-	}
-
-	default:
-		;
-	}
-
-	return -EINVAL;
-}
-
-static int cinergyt2_mmap(struct file *file, struct vm_area_struct *vma)
-{
-	struct dvb_device *dvbdev = file->private_data;
-	struct cinergyt2 *cinergyt2 = dvbdev->priv;
-	int ret = 0;
-
-	lock_kernel();
-
-	if (vma->vm_flags & (VM_WRITE | VM_EXEC)) {
-		ret = -EPERM;
-		goto bailout;
-	}
-
-	if (vma->vm_end > vma->vm_start + STREAM_URB_COUNT * STREAM_BUF_SIZE) {
-		ret = -EINVAL;
-		goto bailout;
-	}
-
-	vma->vm_flags |= (VM_IO | VM_DONTCOPY);
-	vma->vm_file = file;
-
-	ret = remap_pfn_range(vma, vma->vm_start,
-			      virt_to_phys(cinergyt2->streambuf) >> PAGE_SHIFT,
-			      vma->vm_end - vma->vm_start,
-			      vma->vm_page_prot) ? -EAGAIN : 0;
-bailout:
-	unlock_kernel();
-	return ret;
-}
-
-static struct file_operations cinergyt2_fops = {
-	.owner          = THIS_MODULE,
-	.ioctl		= cinergyt2_ioctl,
-	.poll           = cinergyt2_poll,
-	.open           = cinergyt2_open,
-	.release        = cinergyt2_release,
-	.mmap		= cinergyt2_mmap
-};
-
-static struct dvb_device cinergyt2_fe_template = {
-	.users = ~0,
-	.writers = 1,
-	.readers = (~0)-1,
-	.fops = &cinergyt2_fops
-};
-
-#ifdef ENABLE_RC
-
-static void cinergyt2_query_rc (struct work_struct *work)
-{
-	struct cinergyt2 *cinergyt2 =
-		container_of(work, struct cinergyt2, rc_query_work.work);
-	char buf[1] = { CINERGYT2_EP1_GET_RC_EVENTS };
-	struct cinergyt2_rc_event rc_events[12];
-	int n, len, i;
-
-	if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem))
-		return;
-
-	len = cinergyt2_command(cinergyt2, buf, sizeof(buf),
-				(char *) rc_events, sizeof(rc_events));
-	if (len < 0)
-		goto out;
-	if (len == 0) {
-		if (time_after(jiffies, cinergyt2->last_event_jiffies +
-			       msecs_to_jiffies(150))) {
-			/* stop key repeat */
-			if (cinergyt2->rc_input_event != KEY_MAX) {
-				dprintk(1, "rc_input_event=%d Up\n", cinergyt2->rc_input_event);
-				input_report_key(cinergyt2->rc_input_dev,
-						 cinergyt2->rc_input_event, 0);
-				input_sync(cinergyt2->rc_input_dev);
-				cinergyt2->rc_input_event = KEY_MAX;
-			}
-			cinergyt2->rc_last_code = cpu_to_le32(~0);
-		}
-		goto out;
-	}
-	cinergyt2->last_event_jiffies = jiffies;
-
-	for (n = 0; n < (len / sizeof(rc_events[0])); n++) {
-		dprintk(1, "rc_events[%d].value = %x, type=%x\n",
-			n, le32_to_cpu(rc_events[n].value), rc_events[n].type);
-
-		if (rc_events[n].type == CINERGYT2_RC_EVENT_TYPE_NEC &&
-		    rc_events[n].value == cpu_to_le32(~0)) {
-			/* keyrepeat bit -> just repeat last rc_input_event */
-		} else {
-			cinergyt2->rc_input_event = KEY_MAX;
-			for (i = 0; i < ARRAY_SIZE(rc_keys); i += 3) {
-				if (rc_keys[i + 0] == rc_events[n].type &&
-				    rc_keys[i + 1] == le32_to_cpu(rc_events[n].value)) {
-					cinergyt2->rc_input_event = rc_keys[i + 2];
-					break;
-				}
-			}
-		}
-
-		if (cinergyt2->rc_input_event != KEY_MAX) {
-			if (rc_events[n].value == cinergyt2->rc_last_code &&
-			    cinergyt2->rc_last_code != cpu_to_le32(~0)) {
-				/* emit a key-up so the double event is recognized */
-				dprintk(1, "rc_input_event=%d UP\n", cinergyt2->rc_input_event);
-				input_report_key(cinergyt2->rc_input_dev,
-						 cinergyt2->rc_input_event, 0);
-			}
-			dprintk(1, "rc_input_event=%d\n", cinergyt2->rc_input_event);
-			input_report_key(cinergyt2->rc_input_dev,
-					 cinergyt2->rc_input_event, 1);
-			input_sync(cinergyt2->rc_input_dev);
-			cinergyt2->rc_last_code = rc_events[n].value;
-		}
-	}
-
-out:
-	schedule_delayed_work(&cinergyt2->rc_query_work,
-			      msecs_to_jiffies(RC_QUERY_INTERVAL));
-
-	mutex_unlock(&cinergyt2->sem);
-}
-
-static int cinergyt2_register_rc(struct cinergyt2 *cinergyt2)
-{
-	struct input_dev *input_dev;
-	int i;
-	int err;
-
-	input_dev = input_allocate_device();
-	if (!input_dev)
-		return -ENOMEM;
-
-	usb_make_path(cinergyt2->udev, cinergyt2->phys, sizeof(cinergyt2->phys));
-	strlcat(cinergyt2->phys, "/input0", sizeof(cinergyt2->phys));
-	cinergyt2->rc_input_event = KEY_MAX;
-	cinergyt2->rc_last_code = cpu_to_le32(~0);
-	INIT_DELAYED_WORK(&cinergyt2->rc_query_work, cinergyt2_query_rc);
-
-	input_dev->name = DRIVER_NAME " remote control";
-	input_dev->phys = cinergyt2->phys;
-	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
-	for (i = 0; i < ARRAY_SIZE(rc_keys); i += 3)
-		set_bit(rc_keys[i + 2], input_dev->keybit);
-	input_dev->keycodesize = 0;
-	input_dev->keycodemax = 0;
-	input_dev->id.bustype = BUS_USB;
-	input_dev->id.vendor = le16_to_cpu(cinergyt2->udev->descriptor.idVendor);
-	input_dev->id.product = le16_to_cpu(cinergyt2->udev->descriptor.idProduct);
-	input_dev->id.version = 1;
-	input_dev->dev.parent = &cinergyt2->udev->dev;
-
-	err = input_register_device(input_dev);
-	if (err) {
-		input_free_device(input_dev);
-		return err;
-	}
-
-	cinergyt2->rc_input_dev = input_dev;
-	schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2);
-
-	return 0;
-}
-
-static void cinergyt2_unregister_rc(struct cinergyt2 *cinergyt2)
-{
-	cancel_rearming_delayed_work(&cinergyt2->rc_query_work);
-	input_unregister_device(cinergyt2->rc_input_dev);
-}
-
-static inline void cinergyt2_suspend_rc(struct cinergyt2 *cinergyt2)
-{
-	cancel_rearming_delayed_work(&cinergyt2->rc_query_work);
-}
-
-static inline void cinergyt2_resume_rc(struct cinergyt2 *cinergyt2)
-{
-	schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2);
-}
-
-#else
-
-static inline int cinergyt2_register_rc(struct cinergyt2 *cinergyt2) { return 0; }
-static inline void cinergyt2_unregister_rc(struct cinergyt2 *cinergyt2) { }
-static inline void cinergyt2_suspend_rc(struct cinergyt2 *cinergyt2) { }
-static inline void cinergyt2_resume_rc(struct cinergyt2 *cinergyt2) { }
-
-#endif /* ENABLE_RC */
-
-static void cinergyt2_query (struct work_struct *work)
-{
-	struct cinergyt2 *cinergyt2 =
-		container_of(work, struct cinergyt2, query_work.work);
-	char cmd [] = { CINERGYT2_EP1_GET_TUNER_STATUS };
-	struct dvbt_get_status_msg *s = &cinergyt2->status;
-	uint8_t lock_bits;
-
-	if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem))
-		return;
-
-	lock_bits = s->lock_bits;
-
-	cinergyt2_command(cinergyt2, cmd, sizeof(cmd), (char *) s, sizeof(*s));
-
-	cinergyt2->uncorrected_block_count +=
-		le32_to_cpu(s->uncorrected_block_count);
-
-	if (lock_bits != s->lock_bits) {
-		wake_up_interruptible(&cinergyt2->poll_wq);
-		cinergyt2->pending_fe_events++;
-	}
-
-	schedule_delayed_work(&cinergyt2->query_work,
-			      msecs_to_jiffies(QUERY_INTERVAL));
-
-	mutex_unlock(&cinergyt2->sem);
-}
-
-static int cinergyt2_probe (struct usb_interface *intf,
-		  const struct usb_device_id *id)
-{
-	struct cinergyt2 *cinergyt2;
-	int err;
-
-	if (!(cinergyt2 = kzalloc (sizeof(struct cinergyt2), GFP_KERNEL))) {
-		dprintk(1, "out of memory?!?\n");
-		return -ENOMEM;
-	}
-
-	usb_set_intfdata (intf, (void *) cinergyt2);
-
-	mutex_init(&cinergyt2->sem);
-	mutex_init(&cinergyt2->wq_sem);
-	init_waitqueue_head (&cinergyt2->poll_wq);
-	INIT_DELAYED_WORK(&cinergyt2->query_work, cinergyt2_query);
-
-	cinergyt2->udev = interface_to_usbdev(intf);
-	cinergyt2->param.cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS;
-
-	if (cinergyt2_alloc_stream_urbs (cinergyt2) < 0) {
-		dprintk(1, "unable to allocate stream urbs\n");
-		kfree(cinergyt2);
-		return -ENOMEM;
-	}
-
-	err = dvb_register_adapter(&cinergyt2->adapter, DRIVER_NAME,
-				   THIS_MODULE, &cinergyt2->udev->dev,
-				   adapter_nr);
-	if (err < 0) {
-		kfree(cinergyt2);
-		return err;
-	}
-
-	cinergyt2->demux.priv = cinergyt2;
-	cinergyt2->demux.filternum = 256;
-	cinergyt2->demux.feednum = 256;
-	cinergyt2->demux.start_feed = cinergyt2_start_feed;
-	cinergyt2->demux.stop_feed = cinergyt2_stop_feed;
-	cinergyt2->demux.dmx.capabilities = DMX_TS_FILTERING |
-					    DMX_SECTION_FILTERING |
-					    DMX_MEMORY_BASED_FILTERING;
-
-	if ((err = dvb_dmx_init(&cinergyt2->demux)) < 0) {
-		dprintk(1, "dvb_dmx_init() failed (err = %d)\n", err);
-		goto bailout;
-	}
-
-	cinergyt2->dmxdev.filternum = cinergyt2->demux.filternum;
-	cinergyt2->dmxdev.demux = &cinergyt2->demux.dmx;
-	cinergyt2->dmxdev.capabilities = 0;
-
-	if ((err = dvb_dmxdev_init(&cinergyt2->dmxdev, &cinergyt2->adapter)) < 0) {
-		dprintk(1, "dvb_dmxdev_init() failed (err = %d)\n", err);
-		goto bailout;
-	}
-
-	if (dvb_net_init(&cinergyt2->adapter, &cinergyt2->dvbnet, &cinergyt2->demux.dmx))
-		dprintk(1, "dvb_net_init() failed!\n");
-
-	dvb_register_device(&cinergyt2->adapter, &cinergyt2->fedev,
-			    &cinergyt2_fe_template, cinergyt2,
-			    DVB_DEVICE_FRONTEND);
-
-	err = cinergyt2_register_rc(cinergyt2);
-	if (err)
-		goto bailout;
-
-	return 0;
-
-bailout:
-	dvb_net_release(&cinergyt2->dvbnet);
-	dvb_dmxdev_release(&cinergyt2->dmxdev);
-	dvb_dmx_release(&cinergyt2->demux);
-	dvb_unregister_adapter(&cinergyt2->adapter);
-	cinergyt2_free_stream_urbs(cinergyt2);
-	kfree(cinergyt2);
-	return -ENOMEM;
-}
-
-static void cinergyt2_disconnect (struct usb_interface *intf)
-{
-	struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
-
-	cinergyt2_unregister_rc(cinergyt2);
-	cancel_rearming_delayed_work(&cinergyt2->query_work);
-	wake_up_interruptible(&cinergyt2->poll_wq);
-
-	cinergyt2->demux.dmx.close(&cinergyt2->demux.dmx);
-	cinergyt2->disconnect_pending = 1;
-
-	if (!atomic_read(&cinergyt2->inuse))
-		cinergyt2_unregister(cinergyt2);
-}
-
-static int cinergyt2_suspend (struct usb_interface *intf, pm_message_t state)
-{
-	struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
-
-	if (cinergyt2->disconnect_pending)
-		return -EAGAIN;
-	if (mutex_lock_interruptible(&cinergyt2->wq_sem))
-		return -ERESTARTSYS;
-
-	cinergyt2_suspend_rc(cinergyt2);
-	cancel_rearming_delayed_work(&cinergyt2->query_work);
-
-	mutex_lock(&cinergyt2->sem);
-	if (cinergyt2->streaming)
-		cinergyt2_stop_stream_xfer(cinergyt2);
-	cinergyt2_sleep(cinergyt2, 1);
-	mutex_unlock(&cinergyt2->sem);
-
-	mutex_unlock(&cinergyt2->wq_sem);
-
-	return 0;
-}
-
-static int cinergyt2_resume (struct usb_interface *intf)
-{
-	struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
-	struct dvbt_set_parameters_msg *param = &cinergyt2->param;
-	int err = -EAGAIN;
-
-	if (cinergyt2->disconnect_pending)
-		goto out;
-	err = mutex_lock_interruptible(&cinergyt2->wq_sem);
-	if (err)
-		goto out;
-
-	err = mutex_lock_interruptible(&cinergyt2->sem);
-	if (err)
-		goto out_unlock1;
-
-	if (!cinergyt2->sleeping) {
-		cinergyt2_sleep(cinergyt2, 0);
-		cinergyt2_command(cinergyt2, (char *) param, sizeof(*param), NULL, 0);
-		if (cinergyt2->streaming)
-			cinergyt2_start_stream_xfer(cinergyt2);
-		schedule_delayed_work(&cinergyt2->query_work, HZ/2);
-	}
-
-	cinergyt2_resume_rc(cinergyt2);
-
-	mutex_unlock(&cinergyt2->sem);
-out_unlock1:
-	mutex_unlock(&cinergyt2->wq_sem);
-out:
-	return err;
-}
-
-static const struct usb_device_id cinergyt2_table [] __devinitdata = {
-	{ USB_DEVICE(0x0ccd, 0x0038) },
-	{ 0 }
-};
-
-MODULE_DEVICE_TABLE(usb, cinergyt2_table);
-
-static struct usb_driver cinergyt2_driver = {
-	.name	= "cinergyT2",
-	.probe	= cinergyt2_probe,
-	.disconnect	= cinergyt2_disconnect,
-	.suspend	= cinergyt2_suspend,
-	.resume		= cinergyt2_resume,
-	.id_table	= cinergyt2_table
-};
-
-static int __init cinergyt2_init (void)
-{
-	int err;
-
-	if ((err = usb_register(&cinergyt2_driver)) < 0)
-		dprintk(1, "usb_register() failed! (err %i)\n", err);
-
-	return err;
-}
-
-static void __exit cinergyt2_exit (void)
-{
-	usb_deregister(&cinergyt2_driver);
-}
-
-module_init (cinergyt2_init);
-module_exit (cinergyt2_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Holger Waechtler, Daniel Mack");

+ 18 - 0
drivers/media/dvb/dm1105/Kconfig

@@ -0,0 +1,18 @@
+config DVB_DM1105
+	tristate "SDMC DM1105 based PCI cards"
+	depends on DVB_CORE && PCI && I2C
+	select DVB_PLL if !DVB_FE_CUSTOMISE
+	select DVB_STV0299 if !DVB_FE_CUSTOMISE
+	select DVB_STV0288 if !DVB_FE_CUSTOMISE
+	select DVB_STB6000 if !DVB_FE_CUSTOMISE
+	select DVB_CX24116 if !DVB_FE_CUSTOMISE
+	select DVB_SI21XX if !DVB_FE_CUSTOMISE
+	help
+	  Support for cards based on the SDMC DM1105 PCI chip like
+	  DvbWorld 2002
+
+	  Since these cards have no MPEG decoder onboard, they transmit
+	  only compressed MPEG data over the PCI bus, so you need
+	  an external software decoder to watch TV on your computer.
+
+	  Say Y or M if you own such a device and want to use it.

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

@@ -0,0 +1,3 @@
+obj-$(CONFIG_DVB_DM1105) += dm1105.o
+
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends

+ 911 - 0
drivers/media/dvb/dm1105/dm1105.c

@@ -0,0 +1,911 @@
+/*
+ * dm1105.c - driver for DVB cards based on SDMC DM1105 PCI chip
+ *
+ * Copyright (C) 2008 Igor M. Liplianin <liplianin@me.by>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/input.h>
+#include <media/ir-common.h>
+
+#include "demux.h"
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+#include "dvbdev.h"
+#include "dvb-pll.h"
+
+#include "stv0299.h"
+#include "stv0288.h"
+#include "stb6000.h"
+#include "si21xx.h"
+#include "cx24116.h"
+#include "z0194a.h"
+
+/* ----------------------------------------------- */
+/*
+ * PCI ID's
+ */
+#ifndef PCI_VENDOR_ID_TRIGEM
+#define PCI_VENDOR_ID_TRIGEM	0x109f
+#endif
+#ifndef PCI_DEVICE_ID_DM1105
+#define PCI_DEVICE_ID_DM1105	0x036f
+#endif
+#ifndef PCI_DEVICE_ID_DW2002
+#define PCI_DEVICE_ID_DW2002	0x2002
+#endif
+#ifndef PCI_DEVICE_ID_DW2004
+#define PCI_DEVICE_ID_DW2004	0x2004
+#endif
+/* ----------------------------------------------- */
+/* sdmc dm1105 registers */
+
+/* TS Control */
+#define DM1105_TSCTR				0x00
+#define DM1105_DTALENTH				0x04
+
+/* GPIO Interface */
+#define DM1105_GPIOVAL				0x08
+#define DM1105_GPIOCTR				0x0c
+
+/* PID serial number */
+#define DM1105_PIDN				0x10
+
+/* Odd-even secret key select */
+#define DM1105_CWSEL				0x14
+
+/* Host Command Interface */
+#define DM1105_HOST_CTR				0x18
+#define DM1105_HOST_AD				0x1c
+
+/* PCI Interface */
+#define DM1105_CR				0x30
+#define DM1105_RST				0x34
+#define DM1105_STADR				0x38
+#define DM1105_RLEN				0x3c
+#define DM1105_WRP				0x40
+#define DM1105_INTCNT				0x44
+#define DM1105_INTMAK				0x48
+#define DM1105_INTSTS				0x4c
+
+/* CW Value */
+#define DM1105_ODD				0x50
+#define DM1105_EVEN				0x58
+
+/* PID Value */
+#define DM1105_PID				0x60
+
+/* IR Control */
+#define DM1105_IRCTR				0x64
+#define DM1105_IRMODE				0x68
+#define DM1105_SYSTEMCODE			0x6c
+#define DM1105_IRCODE				0x70
+
+/* Unknown Values */
+#define DM1105_ENCRYPT				0x74
+#define DM1105_VER				0x7c
+
+/* I2C Interface */
+#define DM1105_I2CCTR				0x80
+#define DM1105_I2CSTS				0x81
+#define DM1105_I2CDAT				0x82
+#define DM1105_I2C_RA				0x83
+/* ----------------------------------------------- */
+/* Interrupt Mask Bits */
+
+#define INTMAK_TSIRQM				0x01
+#define INTMAK_HIRQM				0x04
+#define INTMAK_IRM				0x08
+#define INTMAK_ALLMASK				(INTMAK_TSIRQM | \
+						INTMAK_HIRQM | \
+						INTMAK_IRM)
+#define INTMAK_NONEMASK				0x00
+
+/* Interrupt Status Bits */
+#define INTSTS_TSIRQ				0x01
+#define INTSTS_HIRQ				0x04
+#define INTSTS_IR				0x08
+
+/* IR Control Bits */
+#define DM1105_IR_EN				0x01
+#define DM1105_SYS_CHK				0x02
+#define DM1105_REP_FLG				0x08
+
+/* EEPROM addr */
+#define IIC_24C01_addr				0xa0
+/* Max board count */
+#define DM1105_MAX				0x04
+
+#define DRIVER_NAME				"dm1105"
+
+#define DM1105_DMA_PACKETS			47
+#define DM1105_DMA_PACKET_LENGTH		(128*4)
+#define DM1105_DMA_BYTES			(128 * 4 * DM1105_DMA_PACKETS)
+
+/* GPIO's for LNB power control */
+#define DM1105_LNB_MASK				0x00000000
+#define DM1105_LNB_13V				0x00010100
+#define DM1105_LNB_18V				0x00000100
+
+static int ir_debug;
+module_param(ir_debug, int, 0644);
+MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static u16 ir_codes_dm1105_nec[128] = {
+	[0x0a] = KEY_Q,		/*power*/
+	[0x0c] = KEY_M,		/*mute*/
+	[0x11] = KEY_1,
+	[0x12] = KEY_2,
+	[0x13] = KEY_3,
+	[0x14] = KEY_4,
+	[0x15] = KEY_5,
+	[0x16] = KEY_6,
+	[0x17] = KEY_7,
+	[0x18] = KEY_8,
+	[0x19] = KEY_9,
+	[0x10] = KEY_0,
+	[0x1c] = KEY_PAGEUP,	/*ch+*/
+	[0x0f] = KEY_PAGEDOWN,	/*ch-*/
+	[0x1a] = KEY_O,		/*vol+*/
+	[0x0e] = KEY_Z,		/*vol-*/
+	[0x04] = KEY_R,		/*rec*/
+	[0x09] = KEY_D,		/*fav*/
+	[0x08] = KEY_BACKSPACE,	/*rewind*/
+	[0x07] = KEY_A,		/*fast*/
+	[0x0b] = KEY_P,		/*pause*/
+	[0x02] = KEY_ESC,	/*cancel*/
+	[0x03] = KEY_G,		/*tab*/
+	[0x00] = KEY_UP,	/*up*/
+	[0x1f] = KEY_ENTER,	/*ok*/
+	[0x01] = KEY_DOWN,	/*down*/
+	[0x05] = KEY_C,		/*cap*/
+	[0x06] = KEY_S,		/*stop*/
+	[0x40] = KEY_F,		/*full*/
+	[0x1e] = KEY_W,		/*tvmode*/
+	[0x1b] = KEY_B,		/*recall*/
+};
+
+/* infrared remote control */
+struct infrared {
+	u16	key_map[128];
+	struct input_dev	*input_dev;
+	char			input_phys[32];
+	struct tasklet_struct	ir_tasklet;
+	u32			ir_command;
+};
+
+struct dm1105dvb {
+	/* pci */
+	struct pci_dev *pdev;
+	u8 __iomem *io_mem;
+
+	/* ir */
+	struct infrared ir;
+
+	/* dvb */
+	struct dmx_frontend hw_frontend;
+	struct dmx_frontend mem_frontend;
+	struct dmxdev dmxdev;
+	struct dvb_adapter dvb_adapter;
+	struct dvb_demux demux;
+	struct dvb_frontend *fe;
+	struct dvb_net dvbnet;
+	unsigned int full_ts_users;
+
+	/* i2c */
+	struct i2c_adapter i2c_adap;
+
+	/* dma */
+	dma_addr_t dma_addr;
+	unsigned char *ts_buf;
+	u32 wrp;
+	u32 buffer_size;
+	unsigned int	PacketErrorCount;
+	unsigned int dmarst;
+	spinlock_t lock;
+
+};
+
+#define dm_io_mem(reg)	((unsigned long)(&dm1105dvb->io_mem[reg]))
+
+static struct dm1105dvb *dm1105dvb_local;
+
+static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap,
+			    struct i2c_msg *msgs, int num)
+{
+	struct dm1105dvb *dm1105dvb ;
+
+	int addr, rc, i, j, k, len, byte, data;
+	u8 status;
+
+	dm1105dvb = i2c_adap->algo_data;
+	for (i = 0; i < num; i++) {
+		outb(0x00, dm_io_mem(DM1105_I2CCTR));
+		if (msgs[i].flags & I2C_M_RD) {
+			/* read bytes */
+			addr  = msgs[i].addr << 1;
+			addr |= 1;
+			outb(addr, dm_io_mem(DM1105_I2CDAT));
+			for (byte = 0; byte < msgs[i].len; byte++)
+				outb(0, dm_io_mem(DM1105_I2CDAT + byte + 1));
+
+			outb(0x81 + msgs[i].len, dm_io_mem(DM1105_I2CCTR));
+			for (j = 0; j < 55; j++) {
+				mdelay(10);
+				status = inb(dm_io_mem(DM1105_I2CSTS));
+				if ((status & 0xc0) == 0x40)
+					break;
+			}
+			if (j >= 55)
+				return -1;
+
+			for (byte = 0; byte < msgs[i].len; byte++) {
+				rc = inb(dm_io_mem(DM1105_I2CDAT + byte + 1));
+				if (rc < 0)
+					goto err;
+				msgs[i].buf[byte] = rc;
+			}
+		} else {
+			if ((msgs[i].buf[0] == 0xf7) && (msgs[i].addr == 0x55)) {
+				/* prepaired for cx24116 firmware */
+				/* Write in small blocks */
+				len = msgs[i].len - 1;
+				k = 1;
+				do {
+					outb(msgs[i].addr << 1, dm_io_mem(DM1105_I2CDAT));
+					outb(0xf7, dm_io_mem(DM1105_I2CDAT + 1));
+					for (byte = 0; byte < (len > 48 ? 48 : len); byte++) {
+						data = msgs[i].buf[k+byte];
+						outb(data, dm_io_mem(DM1105_I2CDAT + byte + 2));
+					}
+					outb(0x82 + (len > 48 ? 48 : len), dm_io_mem(DM1105_I2CCTR));
+					for (j = 0; j < 25; j++) {
+						mdelay(10);
+						status = inb(dm_io_mem(DM1105_I2CSTS));
+						if ((status & 0xc0) == 0x40)
+							break;
+					}
+
+					if (j >= 25)
+						return -1;
+
+					k += 48;
+					len -= 48;
+				} while (len > 0);
+			} else {
+				/* write bytes */
+				outb(msgs[i].addr<<1, dm_io_mem(DM1105_I2CDAT));
+				for (byte = 0; byte < msgs[i].len; byte++) {
+					data = msgs[i].buf[byte];
+					outb(data, dm_io_mem(DM1105_I2CDAT + byte + 1));
+				}
+				outb(0x81 + msgs[i].len, dm_io_mem(DM1105_I2CCTR));
+				for (j = 0; j < 25; j++) {
+					mdelay(10);
+					status = inb(dm_io_mem(DM1105_I2CSTS));
+					if ((status & 0xc0) == 0x40)
+						break;
+				}
+
+				if (j >= 25)
+					return -1;
+			}
+		}
+	}
+	return num;
+ err:
+	return rc;
+}
+
+static u32 functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm dm1105_algo = {
+	.master_xfer   = dm1105_i2c_xfer,
+	.functionality = functionality,
+};
+
+static inline struct dm1105dvb *feed_to_dm1105dvb(struct dvb_demux_feed *feed)
+{
+	return container_of(feed->demux, struct dm1105dvb, demux);
+}
+
+static inline struct dm1105dvb *frontend_to_dm1105dvb(struct dvb_frontend *fe)
+{
+	return container_of(fe->dvb, struct dm1105dvb, dvb_adapter);
+}
+
+static int dm1105dvb_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+	struct dm1105dvb *dm1105dvb = frontend_to_dm1105dvb(fe);
+
+		if (voltage == SEC_VOLTAGE_18) {
+			outl(DM1105_LNB_MASK, dm_io_mem(DM1105_GPIOCTR));
+			outl(DM1105_LNB_18V, dm_io_mem(DM1105_GPIOVAL));
+		} else	{
+		/*LNB ON-13V by default!*/
+			outl(DM1105_LNB_MASK, dm_io_mem(DM1105_GPIOCTR));
+			outl(DM1105_LNB_13V, dm_io_mem(DM1105_GPIOVAL));
+		}
+
+	return 0;
+}
+
+static void dm1105dvb_set_dma_addr(struct dm1105dvb *dm1105dvb)
+{
+	outl(cpu_to_le32(dm1105dvb->dma_addr), dm_io_mem(DM1105_STADR));
+}
+
+static int __devinit dm1105dvb_dma_map(struct dm1105dvb *dm1105dvb)
+{
+	dm1105dvb->ts_buf = pci_alloc_consistent(dm1105dvb->pdev, 6*DM1105_DMA_BYTES, &dm1105dvb->dma_addr);
+
+	return pci_dma_mapping_error(dm1105dvb->pdev, dm1105dvb->dma_addr);
+}
+
+static void dm1105dvb_dma_unmap(struct dm1105dvb *dm1105dvb)
+{
+	pci_free_consistent(dm1105dvb->pdev, 6*DM1105_DMA_BYTES, dm1105dvb->ts_buf, dm1105dvb->dma_addr);
+}
+
+static void __devinit dm1105dvb_enable_irqs(struct dm1105dvb *dm1105dvb)
+{
+	outb(INTMAK_ALLMASK, dm_io_mem(DM1105_INTMAK));
+	outb(1, dm_io_mem(DM1105_CR));
+}
+
+static void dm1105dvb_disable_irqs(struct dm1105dvb *dm1105dvb)
+{
+	outb(INTMAK_IRM, dm_io_mem(DM1105_INTMAK));
+	outb(0, dm_io_mem(DM1105_CR));
+}
+
+static int dm1105dvb_start_feed(struct dvb_demux_feed *f)
+{
+	struct dm1105dvb *dm1105dvb = feed_to_dm1105dvb(f);
+
+	if (dm1105dvb->full_ts_users++ == 0)
+		dm1105dvb_enable_irqs(dm1105dvb);
+
+	return 0;
+}
+
+static int dm1105dvb_stop_feed(struct dvb_demux_feed *f)
+{
+	struct dm1105dvb *dm1105dvb = feed_to_dm1105dvb(f);
+
+	if (--dm1105dvb->full_ts_users == 0)
+		dm1105dvb_disable_irqs(dm1105dvb);
+
+	return 0;
+}
+
+/* ir tasklet */
+static void dm1105_emit_key(unsigned long parm)
+{
+	struct infrared *ir = (struct infrared *) parm;
+	u32 ircom = ir->ir_command;
+	u8 data;
+	u16 keycode;
+
+	data = (ircom >> 8) & 0x7f;
+
+	input_event(ir->input_dev, EV_MSC, MSC_RAW, (0x0000f8 << 16) | data);
+	input_event(ir->input_dev, EV_MSC, MSC_SCAN, data);
+	keycode = ir->key_map[data];
+
+	if (!keycode)
+		return;
+
+	input_event(ir->input_dev, EV_KEY, keycode, 1);
+	input_sync(ir->input_dev);
+	input_event(ir->input_dev, EV_KEY, keycode, 0);
+	input_sync(ir->input_dev);
+
+}
+
+static irqreturn_t dm1105dvb_irq(int irq, void *dev_id)
+{
+	struct dm1105dvb *dm1105dvb = dev_id;
+	unsigned int piece;
+	unsigned int nbpackets;
+	u32 command;
+	u32 nextwrp;
+	u32 oldwrp;
+
+	/* Read-Write INSTS Ack's Interrupt for DM1105 chip 16.03.2008 */
+	unsigned int intsts = inb(dm_io_mem(DM1105_INTSTS));
+	outb(intsts, dm_io_mem(DM1105_INTSTS));
+
+	switch (intsts) {
+	case INTSTS_TSIRQ:
+	case (INTSTS_TSIRQ | INTSTS_IR):
+		nextwrp = inl(dm_io_mem(DM1105_WRP)) -
+			inl(dm_io_mem(DM1105_STADR)) ;
+		oldwrp = dm1105dvb->wrp;
+		spin_lock(&dm1105dvb->lock);
+		if (!((dm1105dvb->ts_buf[oldwrp] == 0x47) &&
+				(dm1105dvb->ts_buf[oldwrp + 188] == 0x47) &&
+				(dm1105dvb->ts_buf[oldwrp + 188 * 2] == 0x47))) {
+			dm1105dvb->PacketErrorCount++;
+			/* bad packet found */
+			if ((dm1105dvb->PacketErrorCount >= 2) &&
+					(dm1105dvb->dmarst == 0)) {
+				outb(1, dm_io_mem(DM1105_RST));
+				dm1105dvb->wrp = 0;
+				dm1105dvb->PacketErrorCount = 0;
+				dm1105dvb->dmarst = 0;
+				spin_unlock(&dm1105dvb->lock);
+				return IRQ_HANDLED;
+			}
+		}
+		if (nextwrp < oldwrp) {
+			piece = dm1105dvb->buffer_size - oldwrp;
+			memcpy(dm1105dvb->ts_buf + dm1105dvb->buffer_size, dm1105dvb->ts_buf, nextwrp);
+			nbpackets = (piece + nextwrp)/188;
+		} else	{
+			nbpackets = (nextwrp - oldwrp)/188;
+		}
+		dvb_dmx_swfilter_packets(&dm1105dvb->demux, &dm1105dvb->ts_buf[oldwrp], nbpackets);
+		dm1105dvb->wrp = nextwrp;
+		spin_unlock(&dm1105dvb->lock);
+		break;
+	case INTSTS_IR:
+		command = inl(dm_io_mem(DM1105_IRCODE));
+		if (ir_debug)
+			printk("dm1105: received byte 0x%04x\n", command);
+
+		dm1105dvb->ir.ir_command = command;
+		tasklet_schedule(&dm1105dvb->ir.ir_tasklet);
+		break;
+	}
+	return IRQ_HANDLED;
+
+
+}
+
+/* register with input layer */
+static void input_register_keys(struct infrared *ir)
+{
+	int i;
+
+	memset(ir->input_dev->keybit, 0, sizeof(ir->input_dev->keybit));
+
+	for (i = 0; i < ARRAY_SIZE(ir->key_map); i++)
+			set_bit(ir->key_map[i], ir->input_dev->keybit);
+
+	ir->input_dev->keycode = ir->key_map;
+	ir->input_dev->keycodesize = sizeof(ir->key_map[0]);
+	ir->input_dev->keycodemax = ARRAY_SIZE(ir->key_map);
+}
+
+int __devinit dm1105_ir_init(struct dm1105dvb *dm1105)
+{
+	struct input_dev *input_dev;
+	int err;
+
+	dm1105dvb_local = dm1105;
+
+	input_dev = input_allocate_device();
+	if (!input_dev)
+		return -ENOMEM;
+
+	dm1105->ir.input_dev = input_dev;
+	snprintf(dm1105->ir.input_phys, sizeof(dm1105->ir.input_phys),
+		"pci-%s/ir0", pci_name(dm1105->pdev));
+
+	input_dev->evbit[0] = BIT(EV_KEY);
+	input_dev->name = "DVB on-card IR receiver";
+
+	input_dev->phys = dm1105->ir.input_phys;
+	input_dev->id.bustype = BUS_PCI;
+	input_dev->id.version = 2;
+	if (dm1105->pdev->subsystem_vendor) {
+		input_dev->id.vendor = dm1105->pdev->subsystem_vendor;
+		input_dev->id.product = dm1105->pdev->subsystem_device;
+	} else {
+		input_dev->id.vendor = dm1105->pdev->vendor;
+		input_dev->id.product = dm1105->pdev->device;
+	}
+	input_dev->dev.parent = &dm1105->pdev->dev;
+	/* initial keymap */
+	memcpy(dm1105->ir.key_map, ir_codes_dm1105_nec, sizeof dm1105->ir.key_map);
+	input_register_keys(&dm1105->ir);
+	err = input_register_device(input_dev);
+	if (err) {
+		input_free_device(input_dev);
+		return err;
+	}
+
+	tasklet_init(&dm1105->ir.ir_tasklet, dm1105_emit_key, (unsigned long) &dm1105->ir);
+
+	return 0;
+}
+
+
+void __devexit dm1105_ir_exit(struct dm1105dvb *dm1105)
+{
+	tasklet_kill(&dm1105->ir.ir_tasklet);
+	input_unregister_device(dm1105->ir.input_dev);
+
+}
+
+static int __devinit dm1105dvb_hw_init(struct dm1105dvb *dm1105dvb)
+{
+	dm1105dvb_disable_irqs(dm1105dvb);
+
+	outb(0, dm_io_mem(DM1105_HOST_CTR));
+
+	/*DATALEN 188,*/
+	outb(188, dm_io_mem(DM1105_DTALENTH));
+	/*TS_STRT TS_VALP MSBFIRST TS_MODE ALPAS TSPES*/
+	outw(0xc10a, dm_io_mem(DM1105_TSCTR));
+
+	/* map DMA and set address */
+	dm1105dvb_dma_map(dm1105dvb);
+	dm1105dvb_set_dma_addr(dm1105dvb);
+	/* big buffer */
+	outl(5*DM1105_DMA_BYTES, dm_io_mem(DM1105_RLEN));
+	outb(47, dm_io_mem(DM1105_INTCNT));
+
+	/* IR NEC mode enable */
+	outb((DM1105_IR_EN | DM1105_SYS_CHK), dm_io_mem(DM1105_IRCTR));
+	outb(0, dm_io_mem(DM1105_IRMODE));
+	outw(0, dm_io_mem(DM1105_SYSTEMCODE));
+
+	return 0;
+}
+
+static void dm1105dvb_hw_exit(struct dm1105dvb *dm1105dvb)
+{
+	dm1105dvb_disable_irqs(dm1105dvb);
+
+	/* IR disable */
+	outb(0, dm_io_mem(DM1105_IRCTR));
+	outb(INTMAK_NONEMASK, dm_io_mem(DM1105_INTMAK));
+
+	dm1105dvb_dma_unmap(dm1105dvb);
+}
+
+static struct stv0288_config earda_config = {
+	.demod_address = 0x68,
+	.min_delay_ms = 100,
+};
+
+static struct si21xx_config serit_config = {
+	.demod_address = 0x68,
+	.min_delay_ms = 100,
+
+};
+
+static struct cx24116_config serit_sp2633_config = {
+	.demod_address = 0x55,
+};
+
+static int __devinit frontend_init(struct dm1105dvb *dm1105dvb)
+{
+	int ret;
+
+	switch (dm1105dvb->pdev->subsystem_device) {
+	case PCI_DEVICE_ID_DW2002:
+		dm1105dvb->fe = dvb_attach(
+			stv0299_attach, &sharp_z0194a_config,
+			&dm1105dvb->i2c_adap);
+
+		if (dm1105dvb->fe) {
+			dm1105dvb->fe->ops.set_voltage =
+							dm1105dvb_set_voltage;
+			dvb_attach(dvb_pll_attach, dm1105dvb->fe, 0x60,
+					&dm1105dvb->i2c_adap, DVB_PLL_OPERA1);
+		}
+
+		if (!dm1105dvb->fe) {
+			dm1105dvb->fe = dvb_attach(
+				stv0288_attach, &earda_config,
+				&dm1105dvb->i2c_adap);
+			if (dm1105dvb->fe) {
+				dm1105dvb->fe->ops.set_voltage =
+							dm1105dvb_set_voltage;
+				dvb_attach(stb6000_attach, dm1105dvb->fe, 0x61,
+						&dm1105dvb->i2c_adap);
+			}
+		}
+
+		if (!dm1105dvb->fe) {
+			dm1105dvb->fe = dvb_attach(
+				si21xx_attach, &serit_config,
+				&dm1105dvb->i2c_adap);
+			if (dm1105dvb->fe)
+				dm1105dvb->fe->ops.set_voltage =
+							dm1105dvb_set_voltage;
+		}
+		break;
+	case PCI_DEVICE_ID_DW2004:
+		dm1105dvb->fe = dvb_attach(
+			cx24116_attach, &serit_sp2633_config,
+			&dm1105dvb->i2c_adap);
+		if (dm1105dvb->fe)
+			dm1105dvb->fe->ops.set_voltage = dm1105dvb_set_voltage;
+		break;
+	}
+
+	if (!dm1105dvb->fe) {
+		dev_err(&dm1105dvb->pdev->dev, "could not attach frontend\n");
+		return -ENODEV;
+	}
+
+	ret = dvb_register_frontend(&dm1105dvb->dvb_adapter, dm1105dvb->fe);
+	if (ret < 0) {
+		if (dm1105dvb->fe->ops.release)
+			dm1105dvb->fe->ops.release(dm1105dvb->fe);
+		dm1105dvb->fe = NULL;
+		return ret;
+	}
+
+	return 0;
+}
+
+static void __devinit dm1105dvb_read_mac(struct dm1105dvb *dm1105dvb, u8 *mac)
+{
+	static u8 command[1] = { 0x28 };
+
+	struct i2c_msg msg[] = {
+		{ .addr = IIC_24C01_addr >> 1, .flags = 0,
+				.buf = command, .len = 1 },
+		{ .addr = IIC_24C01_addr >> 1, .flags = I2C_M_RD,
+				.buf = mac, .len = 6 },
+	};
+
+	dm1105_i2c_xfer(&dm1105dvb->i2c_adap, msg , 2);
+	dev_info(&dm1105dvb->pdev->dev, "MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+			mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+}
+
+static int __devinit dm1105_probe(struct pci_dev *pdev,
+				  const struct pci_device_id *ent)
+{
+	struct dm1105dvb *dm1105dvb;
+	struct dvb_adapter *dvb_adapter;
+	struct dvb_demux *dvbdemux;
+	struct dmx_demux *dmx;
+	int ret = -ENOMEM;
+
+	dm1105dvb = kzalloc(sizeof(struct dm1105dvb), GFP_KERNEL);
+	if (!dm1105dvb)
+		goto out;
+
+	dm1105dvb->pdev = pdev;
+	dm1105dvb->buffer_size = 5 * DM1105_DMA_BYTES;
+	dm1105dvb->PacketErrorCount = 0;
+	dm1105dvb->dmarst = 0;
+
+	ret = pci_enable_device(pdev);
+	if (ret < 0)
+		goto err_kfree;
+
+	ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+	if (ret < 0)
+		goto err_pci_disable_device;
+
+	pci_set_master(pdev);
+
+	ret = pci_request_regions(pdev, DRIVER_NAME);
+	if (ret < 0)
+		goto err_pci_disable_device;
+
+	dm1105dvb->io_mem = pci_iomap(pdev, 0, pci_resource_len(pdev, 0));
+	if (!dm1105dvb->io_mem) {
+		ret = -EIO;
+		goto err_pci_release_regions;
+	}
+
+	spin_lock_init(&dm1105dvb->lock);
+	pci_set_drvdata(pdev, dm1105dvb);
+
+	ret = request_irq(pdev->irq, dm1105dvb_irq, IRQF_SHARED, DRIVER_NAME, dm1105dvb);
+	if (ret < 0)
+		goto err_pci_iounmap;
+
+	ret = dm1105dvb_hw_init(dm1105dvb);
+	if (ret < 0)
+		goto err_free_irq;
+
+	/* i2c */
+	i2c_set_adapdata(&dm1105dvb->i2c_adap, dm1105dvb);
+	strcpy(dm1105dvb->i2c_adap.name, DRIVER_NAME);
+	dm1105dvb->i2c_adap.owner = THIS_MODULE;
+	dm1105dvb->i2c_adap.class = I2C_CLASS_TV_DIGITAL;
+	dm1105dvb->i2c_adap.dev.parent = &pdev->dev;
+	dm1105dvb->i2c_adap.algo = &dm1105_algo;
+	dm1105dvb->i2c_adap.algo_data = dm1105dvb;
+	ret = i2c_add_adapter(&dm1105dvb->i2c_adap);
+
+	if (ret < 0)
+		goto err_dm1105dvb_hw_exit;
+
+	/* dvb */
+	ret = dvb_register_adapter(&dm1105dvb->dvb_adapter, DRIVER_NAME,
+					THIS_MODULE, &pdev->dev, adapter_nr);
+	if (ret < 0)
+		goto err_i2c_del_adapter;
+
+	dvb_adapter = &dm1105dvb->dvb_adapter;
+
+	dm1105dvb_read_mac(dm1105dvb, dvb_adapter->proposed_mac);
+
+	dvbdemux = &dm1105dvb->demux;
+	dvbdemux->filternum = 256;
+	dvbdemux->feednum = 256;
+	dvbdemux->start_feed = dm1105dvb_start_feed;
+	dvbdemux->stop_feed = dm1105dvb_stop_feed;
+	dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
+			DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING);
+	ret = dvb_dmx_init(dvbdemux);
+	if (ret < 0)
+		goto err_dvb_unregister_adapter;
+
+	dmx = &dvbdemux->dmx;
+	dm1105dvb->dmxdev.filternum = 256;
+	dm1105dvb->dmxdev.demux = dmx;
+	dm1105dvb->dmxdev.capabilities = 0;
+
+	ret = dvb_dmxdev_init(&dm1105dvb->dmxdev, dvb_adapter);
+	if (ret < 0)
+		goto err_dvb_dmx_release;
+
+	dm1105dvb->hw_frontend.source = DMX_FRONTEND_0;
+
+	ret = dmx->add_frontend(dmx, &dm1105dvb->hw_frontend);
+	if (ret < 0)
+		goto err_dvb_dmxdev_release;
+
+	dm1105dvb->mem_frontend.source = DMX_MEMORY_FE;
+
+	ret = dmx->add_frontend(dmx, &dm1105dvb->mem_frontend);
+	if (ret < 0)
+		goto err_remove_hw_frontend;
+
+	ret = dmx->connect_frontend(dmx, &dm1105dvb->hw_frontend);
+	if (ret < 0)
+		goto err_remove_mem_frontend;
+
+	ret = frontend_init(dm1105dvb);
+	if (ret < 0)
+		goto err_disconnect_frontend;
+
+	dvb_net_init(dvb_adapter, &dm1105dvb->dvbnet, dmx);
+	dm1105_ir_init(dm1105dvb);
+out:
+	return ret;
+
+err_disconnect_frontend:
+	dmx->disconnect_frontend(dmx);
+err_remove_mem_frontend:
+	dmx->remove_frontend(dmx, &dm1105dvb->mem_frontend);
+err_remove_hw_frontend:
+	dmx->remove_frontend(dmx, &dm1105dvb->hw_frontend);
+err_dvb_dmxdev_release:
+	dvb_dmxdev_release(&dm1105dvb->dmxdev);
+err_dvb_dmx_release:
+	dvb_dmx_release(dvbdemux);
+err_dvb_unregister_adapter:
+	dvb_unregister_adapter(dvb_adapter);
+err_i2c_del_adapter:
+	i2c_del_adapter(&dm1105dvb->i2c_adap);
+err_dm1105dvb_hw_exit:
+	dm1105dvb_hw_exit(dm1105dvb);
+err_free_irq:
+	free_irq(pdev->irq, dm1105dvb);
+err_pci_iounmap:
+	pci_iounmap(pdev, dm1105dvb->io_mem);
+err_pci_release_regions:
+	pci_release_regions(pdev);
+err_pci_disable_device:
+	pci_disable_device(pdev);
+err_kfree:
+	pci_set_drvdata(pdev, NULL);
+	kfree(dm1105dvb);
+	goto out;
+}
+
+static void __devexit dm1105_remove(struct pci_dev *pdev)
+{
+	struct dm1105dvb *dm1105dvb = pci_get_drvdata(pdev);
+	struct dvb_adapter *dvb_adapter = &dm1105dvb->dvb_adapter;
+	struct dvb_demux *dvbdemux = &dm1105dvb->demux;
+	struct dmx_demux *dmx = &dvbdemux->dmx;
+
+	dm1105_ir_exit(dm1105dvb);
+	dmx->close(dmx);
+	dvb_net_release(&dm1105dvb->dvbnet);
+	if (dm1105dvb->fe)
+		dvb_unregister_frontend(dm1105dvb->fe);
+
+	dmx->disconnect_frontend(dmx);
+	dmx->remove_frontend(dmx, &dm1105dvb->mem_frontend);
+	dmx->remove_frontend(dmx, &dm1105dvb->hw_frontend);
+	dvb_dmxdev_release(&dm1105dvb->dmxdev);
+	dvb_dmx_release(dvbdemux);
+	dvb_unregister_adapter(dvb_adapter);
+	if (&dm1105dvb->i2c_adap)
+		i2c_del_adapter(&dm1105dvb->i2c_adap);
+
+	dm1105dvb_hw_exit(dm1105dvb);
+	synchronize_irq(pdev->irq);
+	free_irq(pdev->irq, dm1105dvb);
+	pci_iounmap(pdev, dm1105dvb->io_mem);
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+	pci_set_drvdata(pdev, NULL);
+	kfree(dm1105dvb);
+}
+
+static struct pci_device_id dm1105_id_table[] __devinitdata = {
+	{
+		.vendor = PCI_VENDOR_ID_TRIGEM,
+		.device = PCI_DEVICE_ID_DM1105,
+		.subvendor = PCI_ANY_ID,
+		.subdevice = PCI_DEVICE_ID_DW2002,
+	}, {
+		.vendor = PCI_VENDOR_ID_TRIGEM,
+		.device = PCI_DEVICE_ID_DM1105,
+		.subvendor = PCI_ANY_ID,
+		.subdevice = PCI_DEVICE_ID_DW2004,
+	}, {
+		/* empty */
+	},
+};
+
+MODULE_DEVICE_TABLE(pci, dm1105_id_table);
+
+static struct pci_driver dm1105_driver = {
+	.name = DRIVER_NAME,
+	.id_table = dm1105_id_table,
+	.probe = dm1105_probe,
+	.remove = __devexit_p(dm1105_remove),
+};
+
+static int __init dm1105_init(void)
+{
+	return pci_register_driver(&dm1105_driver);
+}
+
+static void __exit dm1105_exit(void)
+{
+	pci_unregister_driver(&dm1105_driver);
+}
+
+module_init(dm1105_init);
+module_exit(dm1105_exit);
+
+MODULE_AUTHOR("Igor M. Liplianin <liplianin@me.by>");
+MODULE_DESCRIPTION("SDMC DM1105 DVB driver");
+MODULE_LICENSE("GPL");

+ 659 - 7
drivers/media/dvb/dvb-core/dvb_frontend.c

@@ -40,6 +40,7 @@
 
 #include "dvb_frontend.h"
 #include "dvbdev.h"
+#include <linux/dvb/version.h>
 
 static int dvb_frontend_debug;
 static int dvb_shutdown_timeout;
@@ -755,6 +756,539 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe,
 	return 0;
 }
 
+struct dtv_cmds_h dtv_cmds[] = {
+	[DTV_TUNE] = {
+		.name	= "DTV_TUNE",
+		.cmd	= DTV_TUNE,
+		.set	= 1,
+	},
+	[DTV_CLEAR] = {
+		.name	= "DTV_CLEAR",
+		.cmd	= DTV_CLEAR,
+		.set	= 1,
+	},
+
+	/* Set */
+	[DTV_FREQUENCY] = {
+		.name	= "DTV_FREQUENCY",
+		.cmd	= DTV_FREQUENCY,
+		.set	= 1,
+	},
+	[DTV_BANDWIDTH_HZ] = {
+		.name	= "DTV_BANDWIDTH_HZ",
+		.cmd	= DTV_BANDWIDTH_HZ,
+		.set	= 1,
+	},
+	[DTV_MODULATION] = {
+		.name	= "DTV_MODULATION",
+		.cmd	= DTV_MODULATION,
+		.set	= 1,
+	},
+	[DTV_INVERSION] = {
+		.name	= "DTV_INVERSION",
+		.cmd	= DTV_INVERSION,
+		.set	= 1,
+	},
+	[DTV_DISEQC_MASTER] = {
+		.name	= "DTV_DISEQC_MASTER",
+		.cmd	= DTV_DISEQC_MASTER,
+		.set	= 1,
+		.buffer	= 1,
+	},
+	[DTV_SYMBOL_RATE] = {
+		.name	= "DTV_SYMBOL_RATE",
+		.cmd	= DTV_SYMBOL_RATE,
+		.set	= 1,
+	},
+	[DTV_INNER_FEC] = {
+		.name	= "DTV_INNER_FEC",
+		.cmd	= DTV_INNER_FEC,
+		.set	= 1,
+	},
+	[DTV_VOLTAGE] = {
+		.name	= "DTV_VOLTAGE",
+		.cmd	= DTV_VOLTAGE,
+		.set	= 1,
+	},
+	[DTV_TONE] = {
+		.name	= "DTV_TONE",
+		.cmd	= DTV_TONE,
+		.set	= 1,
+	},
+	[DTV_PILOT] = {
+		.name	= "DTV_PILOT",
+		.cmd	= DTV_PILOT,
+		.set	= 1,
+	},
+	[DTV_ROLLOFF] = {
+		.name	= "DTV_ROLLOFF",
+		.cmd	= DTV_ROLLOFF,
+		.set	= 1,
+	},
+	[DTV_DELIVERY_SYSTEM] = {
+		.name	= "DTV_DELIVERY_SYSTEM",
+		.cmd	= DTV_DELIVERY_SYSTEM,
+		.set	= 1,
+	},
+	[DTV_HIERARCHY] = {
+		.name	= "DTV_HIERARCHY",
+		.cmd	= DTV_HIERARCHY,
+		.set	= 1,
+	},
+	[DTV_CODE_RATE_HP] = {
+		.name	= "DTV_CODE_RATE_HP",
+		.cmd	= DTV_CODE_RATE_HP,
+		.set	= 1,
+	},
+	[DTV_CODE_RATE_LP] = {
+		.name	= "DTV_CODE_RATE_LP",
+		.cmd	= DTV_CODE_RATE_LP,
+		.set	= 1,
+	},
+	[DTV_GUARD_INTERVAL] = {
+		.name	= "DTV_GUARD_INTERVAL",
+		.cmd	= DTV_GUARD_INTERVAL,
+		.set	= 1,
+	},
+	[DTV_TRANSMISSION_MODE] = {
+		.name	= "DTV_TRANSMISSION_MODE",
+		.cmd	= DTV_TRANSMISSION_MODE,
+		.set	= 1,
+	},
+	/* Get */
+	[DTV_DISEQC_SLAVE_REPLY] = {
+		.name	= "DTV_DISEQC_SLAVE_REPLY",
+		.cmd	= DTV_DISEQC_SLAVE_REPLY,
+		.set	= 0,
+		.buffer	= 1,
+	},
+	[DTV_API_VERSION] = {
+		.name	= "DTV_API_VERSION",
+		.cmd	= DTV_API_VERSION,
+		.set	= 0,
+	},
+	[DTV_CODE_RATE_HP] = {
+		.name	= "DTV_CODE_RATE_HP",
+		.cmd	= DTV_CODE_RATE_HP,
+		.set	= 0,
+	},
+	[DTV_CODE_RATE_LP] = {
+		.name	= "DTV_CODE_RATE_LP",
+		.cmd	= DTV_CODE_RATE_LP,
+		.set	= 0,
+	},
+	[DTV_GUARD_INTERVAL] = {
+		.name	= "DTV_GUARD_INTERVAL",
+		.cmd	= DTV_GUARD_INTERVAL,
+		.set	= 0,
+	},
+	[DTV_TRANSMISSION_MODE] = {
+		.name	= "DTV_TRANSMISSION_MODE",
+		.cmd	= DTV_TRANSMISSION_MODE,
+		.set	= 0,
+	},
+	[DTV_HIERARCHY] = {
+		.name	= "DTV_HIERARCHY",
+		.cmd	= DTV_HIERARCHY,
+		.set	= 0,
+	},
+};
+
+void dtv_property_dump(struct dtv_property *tvp)
+{
+	int i;
+
+	if (tvp->cmd <= 0 || tvp->cmd > DTV_MAX_COMMAND) {
+		printk("%s: tvp.cmd = 0x%08x (undefined/unknown/invalid)\n",
+			__func__, tvp->cmd);
+		return;
+	}
+
+	printk("%s() tvp.cmd    = 0x%08x (%s)\n"
+		,__FUNCTION__
+		,tvp->cmd
+		,dtv_cmds[ tvp->cmd ].name);
+
+	if(dtv_cmds[ tvp->cmd ].buffer) {
+
+		printk("%s() tvp.u.buffer.len = 0x%02x\n"
+			,__FUNCTION__
+			,tvp->u.buffer.len);
+
+		for(i = 0; i < tvp->u.buffer.len; i++)
+			printk("%s() tvp.u.buffer.data[0x%02x] = 0x%02x\n"
+				,__FUNCTION__
+				,i
+				,tvp->u.buffer.data[i]);
+
+	} else
+		printk("%s() tvp.u.data = 0x%08x\n", __FUNCTION__, tvp->u.data);
+}
+
+int is_legacy_delivery_system(fe_delivery_system_t s)
+{
+	if((s == SYS_UNDEFINED) || (s == SYS_DVBC_ANNEX_AC) ||
+		(s == SYS_DVBC_ANNEX_B) || (s == SYS_DVBT) || (s == SYS_DVBS))
+		return 1;
+
+	return 0;
+}
+
+/* Synchronise the legacy tuning parameters into the cache, so that demodulator
+ * drivers can use a single set_frontend tuning function, regardless of whether
+ * it's being used for the legacy or new API, reducing code and complexity.
+ */
+void dtv_property_cache_sync(struct dvb_frontend *fe, struct dvb_frontend_parameters *p)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+	printk("%s()\n", __FUNCTION__);
+
+	c->frequency = p->frequency;
+	c->inversion = p->inversion;
+
+	switch (fe->ops.info.type) {
+	case FE_QPSK:
+		c->modulation = QPSK;   /* implied for DVB-S in legacy API */
+		c->rolloff = ROLLOFF_35;/* implied for DVB-S */
+		c->symbol_rate = p->u.qpsk.symbol_rate;
+		c->fec_inner = p->u.qpsk.fec_inner;
+		c->delivery_system = SYS_DVBS;
+		break;
+	case FE_QAM:
+		c->symbol_rate = p->u.qam.symbol_rate;
+		c->fec_inner = p->u.qam.fec_inner;
+		c->modulation = p->u.qam.modulation;
+		c->delivery_system = SYS_DVBC_ANNEX_AC;
+		break;
+	case FE_OFDM:
+		if (p->u.ofdm.bandwidth == BANDWIDTH_6_MHZ)
+			c->bandwidth_hz = 6000000;
+		else if (p->u.ofdm.bandwidth == BANDWIDTH_7_MHZ)
+			c->bandwidth_hz = 7000000;
+		else if (p->u.ofdm.bandwidth == BANDWIDTH_8_MHZ)
+			c->bandwidth_hz = 8000000;
+		else
+			/* Including BANDWIDTH_AUTO */
+			c->bandwidth_hz = 0;
+		c->code_rate_HP = p->u.ofdm.code_rate_HP;
+		c->code_rate_LP = p->u.ofdm.code_rate_LP;
+		c->modulation = p->u.ofdm.constellation;
+		c->transmission_mode = p->u.ofdm.transmission_mode;
+		c->guard_interval = p->u.ofdm.guard_interval;
+		c->hierarchy = p->u.ofdm.hierarchy_information;
+		c->delivery_system = SYS_DVBT;
+		break;
+	case FE_ATSC:
+		c->modulation = p->u.vsb.modulation;
+		if ((c->modulation == VSB_8) || (c->modulation == VSB_16))
+			c->delivery_system = SYS_ATSC;
+		else
+			c->delivery_system = SYS_DVBC_ANNEX_B;
+		break;
+	}
+}
+
+/* Ensure the cached values are set correctly in the frontend
+ * legacy tuning structures, for the advanced tuning API.
+ */
+void dtv_property_legacy_params_sync(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct dvb_frontend_private *fepriv = fe->frontend_priv;
+	struct dvb_frontend_parameters *p = &fepriv->parameters;
+
+	printk("%s()\n", __FUNCTION__);
+
+	p->frequency = c->frequency;
+	p->inversion = c->inversion;
+
+	switch (fe->ops.info.type) {
+	case FE_QPSK:
+		printk("%s() Preparing QPSK req\n", __FUNCTION__);
+		p->u.qpsk.symbol_rate = c->symbol_rate;
+		p->u.qpsk.fec_inner = c->fec_inner;
+		c->delivery_system = SYS_DVBS;
+		break;
+	case FE_QAM:
+		printk("%s() Preparing QAM req\n", __FUNCTION__);
+		p->u.qam.symbol_rate = c->symbol_rate;
+		p->u.qam.fec_inner = c->fec_inner;
+		p->u.qam.modulation = c->modulation;
+		c->delivery_system = SYS_DVBC_ANNEX_AC;
+		break;
+	case FE_OFDM:
+		printk("%s() Preparing OFDM req\n", __FUNCTION__);
+		if (c->bandwidth_hz == 6000000)
+			p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
+		else if (c->bandwidth_hz == 7000000)
+			p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ;
+		else if (c->bandwidth_hz == 8000000)
+			p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
+		else
+			p->u.ofdm.bandwidth = BANDWIDTH_AUTO;
+		p->u.ofdm.code_rate_HP = c->code_rate_HP;
+		p->u.ofdm.code_rate_LP = c->code_rate_LP;
+		p->u.ofdm.constellation = c->modulation;
+		p->u.ofdm.transmission_mode = c->transmission_mode;
+		p->u.ofdm.guard_interval = c->guard_interval;
+		p->u.ofdm.hierarchy_information = c->hierarchy;
+		c->delivery_system = SYS_DVBT;
+		break;
+	case FE_ATSC:
+		printk("%s() Preparing VSB req\n", __FUNCTION__);
+		p->u.vsb.modulation = c->modulation;
+		if ((c->modulation == VSB_8) || (c->modulation == VSB_16))
+			c->delivery_system = SYS_ATSC;
+		else
+			c->delivery_system = SYS_DVBC_ANNEX_B;
+		break;
+	}
+}
+
+/* Ensure the cached values are set correctly in the frontend
+ * legacy tuning structures, for the legacy tuning API.
+ */
+void dtv_property_adv_params_sync(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct dvb_frontend_private *fepriv = fe->frontend_priv;
+	struct dvb_frontend_parameters *p = &fepriv->parameters;
+
+	printk("%s()\n", __FUNCTION__);
+
+	p->frequency = c->frequency;
+	p->inversion = c->inversion;
+
+	switch(c->modulation) {
+	case PSK_8:
+	case APSK_16:
+	case QPSK:
+		p->u.qpsk.symbol_rate = c->symbol_rate;
+		p->u.qpsk.fec_inner = c->fec_inner;
+		break;
+	default:
+		break;
+	}
+
+	if(c->delivery_system == SYS_ISDBT) {
+		/* Fake out a generic DVB-T request so we pass validation in the ioctl */
+		p->frequency = c->frequency;
+		p->inversion = INVERSION_AUTO;
+		p->u.ofdm.constellation = QAM_AUTO;
+		p->u.ofdm.code_rate_HP = FEC_AUTO;
+		p->u.ofdm.code_rate_LP = FEC_AUTO;
+		p->u.ofdm.bandwidth = BANDWIDTH_AUTO;
+		p->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO;
+		p->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO;
+		p->u.ofdm.hierarchy_information = HIERARCHY_AUTO;
+	}
+}
+
+void dtv_property_cache_submit(struct dvb_frontend *fe)
+{
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+	printk("%s()\n", __FUNCTION__);
+
+	/* For legacy delivery systems we don't need the delivery_system to
+	 * be specified, but we populate the older structures from the cache
+	 * so we can call set_frontend on older drivers.
+	 */
+	if(is_legacy_delivery_system(c->delivery_system)) {
+
+		printk("%s() legacy, modulation = %d\n", __FUNCTION__, c->modulation);
+		dtv_property_legacy_params_sync(fe);
+
+	} else {
+		printk("%s() adv, modulation = %d\n", __FUNCTION__, c->modulation);
+
+		/* For advanced delivery systems / modulation types ...
+		 * we seed the lecacy dvb_frontend_parameters structure
+		 * so that the sanity checking code later in the IOCTL processing
+		 * can validate our basic frequency ranges, symbolrates, modulation
+		 * etc.
+		 */
+		dtv_property_adv_params_sync(fe);
+	}
+}
+
+static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file,
+			unsigned int cmd, void *parg);
+static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file,
+			unsigned int cmd, void *parg);
+
+int dtv_property_process_get(struct dvb_frontend *fe, struct dtv_property *tvp,
+	struct inode *inode, struct file *file)
+{
+	int r = 0;
+
+	printk("%s()\n", __FUNCTION__);
+
+	dtv_property_dump(tvp);
+
+	/* Allow the frontend to validate incoming properties */
+	if (fe->ops.get_property)
+		r = fe->ops.get_property(fe, tvp);
+
+	if (r < 0)
+		return r;
+
+	switch(tvp->cmd) {
+	case DTV_FREQUENCY:
+		tvp->u.data = fe->dtv_property_cache.frequency;
+		break;
+	case DTV_MODULATION:
+		tvp->u.data = fe->dtv_property_cache.modulation;
+		break;
+	case DTV_BANDWIDTH_HZ:
+		tvp->u.data = fe->dtv_property_cache.bandwidth_hz;
+		break;
+	case DTV_INVERSION:
+		tvp->u.data = fe->dtv_property_cache.inversion;
+		break;
+	case DTV_SYMBOL_RATE:
+		tvp->u.data = fe->dtv_property_cache.symbol_rate;
+		break;
+	case DTV_INNER_FEC:
+		tvp->u.data = fe->dtv_property_cache.fec_inner;
+		break;
+	case DTV_PILOT:
+		tvp->u.data = fe->dtv_property_cache.pilot;
+		break;
+	case DTV_ROLLOFF:
+		tvp->u.data = fe->dtv_property_cache.rolloff;
+		break;
+	case DTV_DELIVERY_SYSTEM:
+		tvp->u.data = fe->dtv_property_cache.delivery_system;
+		break;
+	case DTV_VOLTAGE:
+		tvp->u.data = fe->dtv_property_cache.voltage;
+		break;
+	case DTV_TONE:
+		tvp->u.data = fe->dtv_property_cache.sectone;
+		break;
+	case DTV_API_VERSION:
+		tvp->u.data = (DVB_API_VERSION << 8) | DVB_API_VERSION_MINOR;
+		break;
+	case DTV_CODE_RATE_HP:
+		tvp->u.data = fe->dtv_property_cache.code_rate_HP;
+		break;
+	case DTV_CODE_RATE_LP:
+		tvp->u.data = fe->dtv_property_cache.code_rate_LP;
+		break;
+	case DTV_GUARD_INTERVAL:
+		tvp->u.data = fe->dtv_property_cache.guard_interval;
+		break;
+	case DTV_TRANSMISSION_MODE:
+		tvp->u.data = fe->dtv_property_cache.transmission_mode;
+		break;
+	case DTV_HIERARCHY:
+		tvp->u.data = fe->dtv_property_cache.hierarchy;
+		break;
+	default:
+		r = -1;
+	}
+
+	return r;
+}
+
+int dtv_property_process_set(struct dvb_frontend *fe, struct dtv_property *tvp,
+	struct inode *inode, struct file *file)
+{
+	int r = 0;
+	struct dvb_frontend_private *fepriv = fe->frontend_priv;
+	printk("%s()\n", __FUNCTION__);
+	dtv_property_dump(tvp);
+
+	/* Allow the frontend to validate incoming properties */
+	if (fe->ops.set_property)
+		r = fe->ops.set_property(fe, tvp);
+
+	if (r < 0)
+		return r;
+
+	switch(tvp->cmd) {
+	case DTV_CLEAR:
+		/* Reset a cache of data specific to the frontend here. This does
+		 * not effect hardware.
+		 */
+		printk("%s() Flushing property cache\n", __FUNCTION__);
+		memset(&fe->dtv_property_cache, 0, sizeof(struct dtv_frontend_properties));
+		fe->dtv_property_cache.state = tvp->cmd;
+		fe->dtv_property_cache.delivery_system = SYS_UNDEFINED;
+		break;
+	case DTV_TUNE:
+		/* interpret the cache of data, build either a traditional frontend
+		 * tunerequest so we can pass validation in the FE_SET_FRONTEND
+		 * ioctl.
+		 */
+		fe->dtv_property_cache.state = tvp->cmd;
+		printk("%s() Finalised property cache\n", __FUNCTION__);
+		dtv_property_cache_submit(fe);
+
+		r |= dvb_frontend_ioctl_legacy(inode, file, FE_SET_FRONTEND,
+			&fepriv->parameters);
+		break;
+	case DTV_FREQUENCY:
+		fe->dtv_property_cache.frequency = tvp->u.data;
+		break;
+	case DTV_MODULATION:
+		fe->dtv_property_cache.modulation = tvp->u.data;
+		break;
+	case DTV_BANDWIDTH_HZ:
+		fe->dtv_property_cache.bandwidth_hz = tvp->u.data;
+		break;
+	case DTV_INVERSION:
+		fe->dtv_property_cache.inversion = tvp->u.data;
+		break;
+	case DTV_SYMBOL_RATE:
+		fe->dtv_property_cache.symbol_rate = tvp->u.data;
+		break;
+	case DTV_INNER_FEC:
+		fe->dtv_property_cache.fec_inner = tvp->u.data;
+		break;
+	case DTV_PILOT:
+		fe->dtv_property_cache.pilot = tvp->u.data;
+		break;
+	case DTV_ROLLOFF:
+		fe->dtv_property_cache.rolloff = tvp->u.data;
+		break;
+	case DTV_DELIVERY_SYSTEM:
+		fe->dtv_property_cache.delivery_system = tvp->u.data;
+		break;
+	case DTV_VOLTAGE:
+		fe->dtv_property_cache.voltage = tvp->u.data;
+		r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_VOLTAGE,
+			(void *)fe->dtv_property_cache.voltage);
+		break;
+	case DTV_TONE:
+		fe->dtv_property_cache.sectone = tvp->u.data;
+		r = dvb_frontend_ioctl_legacy(inode, file, FE_SET_TONE,
+			(void *)fe->dtv_property_cache.sectone);
+		break;
+	case DTV_CODE_RATE_HP:
+		fe->dtv_property_cache.code_rate_HP = tvp->u.data;
+		break;
+	case DTV_CODE_RATE_LP:
+		fe->dtv_property_cache.code_rate_LP = tvp->u.data;
+		break;
+	case DTV_GUARD_INTERVAL:
+		fe->dtv_property_cache.guard_interval = tvp->u.data;
+		break;
+	case DTV_TRANSMISSION_MODE:
+		fe->dtv_property_cache.transmission_mode = tvp->u.data;
+		break;
+	case DTV_HIERARCHY:
+		fe->dtv_property_cache.hierarchy = tvp->u.data;
+		break;
+	default:
+		r = -1;
+	}
+
+	return r;
+}
+
 static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
 			unsigned int cmd, void *parg)
 {
@@ -776,6 +1310,116 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
 	if (down_interruptible (&fepriv->sem))
 		return -ERESTARTSYS;
 
+	if ((cmd == FE_SET_PROPERTY) || (cmd == FE_GET_PROPERTY))
+		err = dvb_frontend_ioctl_properties(inode, file, cmd, parg);
+	else {
+		fe->dtv_property_cache.state = DTV_UNDEFINED;
+		err = dvb_frontend_ioctl_legacy(inode, file, cmd, parg);
+	}
+
+	up(&fepriv->sem);
+	return err;
+}
+
+static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file,
+			unsigned int cmd, void *parg)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct dvb_frontend *fe = dvbdev->priv;
+	int err = 0;
+
+	struct dtv_properties *tvps = NULL;
+	struct dtv_property *tvp = NULL;
+	int i;
+
+	dprintk("%s\n", __func__);
+
+	if(cmd == FE_SET_PROPERTY) {
+		printk("%s() FE_SET_PROPERTY\n", __FUNCTION__);
+
+		tvps = (struct dtv_properties __user *)parg;
+
+		printk("%s() properties.num = %d\n", __FUNCTION__, tvps->num);
+		printk("%s() properties.props = %p\n", __FUNCTION__, tvps->props);
+
+		/* Put an arbitrary limit on the number of messages that can
+		 * be sent at once */
+		if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS))
+			return -EINVAL;
+
+		tvp = (struct dtv_property *) kmalloc(tvps->num *
+			sizeof(struct dtv_property), GFP_KERNEL);
+		if (!tvp) {
+			err = -ENOMEM;
+			goto out;
+		}
+
+		if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) {
+			err = -EFAULT;
+			goto out;
+		}
+
+		for (i = 0; i < tvps->num; i++) {
+			(tvp + i)->result = dtv_property_process_set(fe, tvp + i, inode, file);
+			err |= (tvp + i)->result;
+		}
+
+		if(fe->dtv_property_cache.state == DTV_TUNE) {
+			printk("%s() Property cache is full, tuning\n", __FUNCTION__);
+		}
+
+	} else
+	if(cmd == FE_GET_PROPERTY) {
+		printk("%s() FE_GET_PROPERTY\n", __FUNCTION__);
+
+		tvps = (struct dtv_properties __user *)parg;
+
+		printk("%s() properties.num = %d\n", __FUNCTION__, tvps->num);
+		printk("%s() properties.props = %p\n", __FUNCTION__, tvps->props);
+
+		/* Put an arbitrary limit on the number of messages that can
+		 * be sent at once */
+		if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS))
+			return -EINVAL;
+
+		tvp = (struct dtv_property *) kmalloc(tvps->num *
+			sizeof(struct dtv_property), GFP_KERNEL);
+		if (!tvp) {
+			err = -ENOMEM;
+			goto out;
+		}
+
+		if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) {
+			err = -EFAULT;
+			goto out;
+		}
+
+		for (i = 0; i < tvps->num; i++) {
+			(tvp + i)->result = dtv_property_process_get(fe, tvp + i, inode, file);
+			err |= (tvp + i)->result;
+		}
+
+		if (copy_to_user(tvps->props, tvp, tvps->num * sizeof(struct dtv_property))) {
+			err = -EFAULT;
+			goto out;
+		}
+
+	} else
+		err = -EOPNOTSUPP;
+
+out:
+	kfree(tvp);
+	return err;
+}
+
+static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file,
+			unsigned int cmd, void *parg)
+{
+	struct dvb_device *dvbdev = file->private_data;
+	struct dvb_frontend *fe = dvbdev->priv;
+	struct dvb_frontend_private *fepriv = fe->frontend_priv;
+	int err = -EOPNOTSUPP;
+
 	switch (cmd) {
 	case FE_GET_INFO: {
 		struct dvb_frontend_info* info = parg;
@@ -942,13 +1586,21 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
 	case FE_SET_FRONTEND: {
 		struct dvb_frontend_tune_settings fetunesettings;
 
-		if (dvb_frontend_check_parameters(fe, parg) < 0) {
-			err = -EINVAL;
-			break;
-		}
+		if(fe->dtv_property_cache.state == DTV_TUNE) {
+			if (dvb_frontend_check_parameters(fe, &fepriv->parameters) < 0) {
+				err = -EINVAL;
+				break;
+			}
+		} else {
+			if (dvb_frontend_check_parameters(fe, parg) < 0) {
+				err = -EINVAL;
+				break;
+			}
 
-		memcpy (&fepriv->parameters, parg,
-			sizeof (struct dvb_frontend_parameters));
+			memcpy (&fepriv->parameters, parg,
+				sizeof (struct dvb_frontend_parameters));
+			dtv_property_cache_sync(fe, &fepriv->parameters);
+		}
 
 		memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_settings));
 		memcpy(&fetunesettings.parameters, parg,
@@ -1027,10 +1679,10 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file,
 		break;
 	};
 
-	up (&fepriv->sem);
 	return err;
 }
 
+
 static unsigned int dvb_frontend_poll(struct file *file, struct poll_table_struct *wait)
 {
 	struct dvb_device *dvbdev = file->private_data;

+ 32 - 0
drivers/media/dvb/dvb-core/dvb_frontend.h

@@ -169,6 +169,9 @@ struct dvb_frontend_ops {
 
 	struct dvb_tuner_ops tuner_ops;
 	struct analog_demod_ops analog_ops;
+
+	int (*set_property)(struct dvb_frontend* fe, struct dtv_property* tvp);
+	int (*get_property)(struct dvb_frontend* fe, struct dtv_property* tvp);
 };
 
 #define MAX_EVENT 8
@@ -182,6 +185,32 @@ struct dvb_fe_events {
 	struct mutex		  mtx;
 };
 
+struct dtv_frontend_properties {
+
+	/* Cache State */
+	u32			state;
+
+	u32			frequency;
+	fe_modulation_t		modulation;
+
+	fe_sec_voltage_t	voltage;
+	fe_sec_tone_mode_t	sectone;
+	fe_spectral_inversion_t	inversion;
+	fe_code_rate_t		fec_inner;
+	fe_transmit_mode_t	transmission_mode;
+	u32			bandwidth_hz;	/* 0 = AUTO */
+	fe_guard_interval_t	guard_interval;
+	fe_hierarchy_t		hierarchy;
+	u32			symbol_rate;
+	fe_code_rate_t		code_rate_HP;
+	fe_code_rate_t		code_rate_LP;
+
+	fe_pilot_t		pilot;
+	fe_rolloff_t		rolloff;
+
+	fe_delivery_system_t	delivery_system;
+};
+
 struct dvb_frontend {
 	struct dvb_frontend_ops ops;
 	struct dvb_adapter *dvb;
@@ -190,6 +219,9 @@ struct dvb_frontend {
 	void *frontend_priv;
 	void *sec_priv;
 	void *analog_demod_priv;
+	struct dtv_frontend_properties dtv_property_cache;
+#define DVB_FRONTEND_COMPONENT_TUNER 0
+	int (*callback)(void *adapter_priv, int component, int cmd, int arg);
 };
 
 extern int dvb_register_frontend(struct dvb_adapter *dvb,

+ 39 - 3
drivers/media/dvb/dvb-usb/Kconfig

@@ -72,9 +72,11 @@ config DVB_USB_DIB0700
 	select DVB_DIB7000P
 	select DVB_DIB7000M
 	select DVB_DIB3000MC
+	select DVB_S5H1411 if !DVB_FE_CUSTOMISE
 	select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
 	select MEDIA_TUNER_MT2266 if !DVB_FE_CUSTOMISE
 	select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE
 	select DVB_TUNER_DIB0070
 	help
 	  Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The
@@ -108,6 +110,8 @@ config DVB_USB_CXUSB
 	select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
 	select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMISE
 	select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE
+	select DVB_DIB7000P if !DVB_FE_CUSTOMISE
+	select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE
 	help
 	  Say Y here to support the Conexant USB2.0 hybrid reference design.
 	  Currently, only DVB and ATSC modes are supported, analog mode
@@ -245,12 +249,25 @@ config DVB_USB_AF9005_REMOTE
 	  Afatech AF9005 based receiver.
 
 config DVB_USB_DW2102
-	tristate "DvbWorld 2102 DVB-S USB2.0 receiver"
+	tristate "DvbWorld DVB-S/S2 USB2.0 support"
 	depends on DVB_USB
-	select DVB_STV0299 if !DVB_FE_CUSTOMISE
 	select DVB_PLL if !DVB_FE_CUSTOMISE
+	select DVB_STV0299 if !DVB_FE_CUSTOMISE
+	select DVB_STV0288 if !DVB_FE_CUSTOMISE
+	select DVB_STB6000 if !DVB_FE_CUSTOMISE
+	select DVB_CX24116 if !DVB_FE_CUSTOMISE
+	select DVB_SI21XX if !DVB_FE_CUSTOMISE
 	help
-	   Say Y here to support the DvbWorld 2102 DVB-S USB2.0 receiver.
+	  Say Y here to support the DvbWorld DVB-S/S2 USB2.0 receivers
+	  and the TeVii S650.
+
+config 	DVB_USB_CINERGY_T2
+	tristate "Terratec CinergyT2/qanu USB 2.0 DVB-T receiver"
+	depends on DVB_USB
+	help
+	  Support for "TerraTec CinergyT2" USB2.0 Highspeed DVB Receivers
+
+	  Say Y if you own such a device and want to use it.
 
 config DVB_USB_ANYSEE
 	tristate "Anysee DVB-T/C USB2.0 support"
@@ -262,3 +279,22 @@ config DVB_USB_ANYSEE
 	help
 	  Say Y here to support the Anysee E30, Anysee E30 Plus or
 	  Anysee E30 C Plus DVB USB2.0 receiver.
+
+config DVB_USB_DTV5100
+	tristate "AME DTV-5100 USB2.0 DVB-T support"
+	depends on DVB_USB
+	select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
+	help
+	  Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver.
+
+config DVB_USB_AF9015
+	tristate "Afatech AF9015 DVB-T USB2.0 support"
+	depends on DVB_USB && EXPERIMENTAL
+	select DVB_AF9013
+	select DVB_PLL              if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_MT2060   if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_QT1010   if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMISE
+	select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE
+	help
+	  Say Y here to support the Afatech AF9015 based DVB-T USB2.0 receiver

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

@@ -67,6 +67,16 @@ obj-$(CONFIG_DVB_USB_ANYSEE) += dvb-usb-anysee.o
 dvb-usb-dw2102-objs = dw2102.o
 obj-$(CONFIG_DVB_USB_DW2102) += dvb-usb-dw2102.o
 
+dvb-usb-dtv5100-objs = dtv5100.o
+obj-$(CONFIG_DVB_USB_DTV5100) += dvb-usb-dtv5100.o
+
+dvb-usb-af9015-objs = af9015.o
+obj-$(CONFIG_DVB_USB_AF9015) += dvb-usb-af9015.o
+
+dvb-usb-cinergyT2-objs = cinergyT2-core.o cinergyT2-fe.o
+obj-$(CONFIG_DVB_USB_CINERGY_T2) += dvb-usb-cinergyT2.o
+
+
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
 # due to tuner-xc3028
 EXTRA_CFLAGS += -Idrivers/media/common/tuners

+ 1 - 1
drivers/media/dvb/dvb-usb/af9005-remote.c

@@ -25,7 +25,7 @@
  */
 #include "af9005.h"
 /* debug */
-int dvb_usb_af9005_remote_debug;
+static int dvb_usb_af9005_remote_debug;
 module_param_named(debug, dvb_usb_af9005_remote_debug, int, 0644);
 MODULE_PARM_DESC(debug,
 		 "enable (1) or disable (0) debug messages."

+ 1 - 1
drivers/media/dvb/dvb-usb/af9005-script.h

@@ -14,7 +14,7 @@ typedef struct {
 	u8 val;
 } RegDesc;
 
-RegDesc script[] = {
+static RegDesc script[] = {
 	{0xa180, 0x0, 0x8, 0xa},
 	{0xa181, 0x0, 0x8, 0xd7},
 	{0xa182, 0x0, 0x8, 0xa3},

+ 9 - 14
drivers/media/dvb/dvb-usb/af9005.c

@@ -35,17 +35,17 @@ module_param_named(led, dvb_usb_af9005_led, bool, 0644);
 MODULE_PARM_DESC(led, "enable led (default: 1).");
 
 /* eeprom dump */
-int dvb_usb_af9005_dump_eeprom = 0;
+static int dvb_usb_af9005_dump_eeprom;
 module_param_named(dump_eeprom, dvb_usb_af9005_dump_eeprom, int, 0);
 MODULE_PARM_DESC(dump_eeprom, "dump contents of the eeprom.");
 
 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 
 /* remote control decoder */
-int (*rc_decode) (struct dvb_usb_device * d, u8 * data, int len, u32 * event,
-		  int *state);
-void *rc_keys;
-int *rc_keys_size;
+static int (*rc_decode) (struct dvb_usb_device *d, u8 *data, int len,
+		u32 *event, int *state);
+static void *rc_keys;
+static int *rc_keys_size;
 
 u8 regmask[8] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
 
@@ -54,8 +54,8 @@ struct af9005_device_state {
 	int led_state;
 };
 
-int af9005_usb_generic_rw(struct dvb_usb_device *d, u8 * wbuf, u16 wlen,
-			  u8 * rbuf, u16 rlen, int delay_ms)
+static int af9005_usb_generic_rw(struct dvb_usb_device *d, u8 *wbuf, u16 wlen,
+			  u8 *rbuf, u16 rlen, int delay_ms)
 {
 	int actlen, ret = -ENOMEM;
 
@@ -98,12 +98,7 @@ int af9005_usb_generic_rw(struct dvb_usb_device *d, u8 * wbuf, u16 wlen,
 	return ret;
 }
 
-int af9005_usb_generic_write(struct dvb_usb_device *d, u8 * buf, u16 len)
-{
-	return af9005_usb_generic_rw(d, buf, len, NULL, 0, 0);
-}
-
-int af9005_generic_read_write(struct dvb_usb_device *d, u16 reg,
+static int af9005_generic_read_write(struct dvb_usb_device *d, u16 reg,
 			      int readwrite, int type, u8 * values, int len)
 {
 	struct af9005_device_state *st = d->priv;
@@ -765,7 +760,7 @@ static int af9005_boot_packet(struct usb_device *udev, int type, u8 * reply)
 	return 0;
 }
 
-int af9005_download_firmware(struct usb_device *udev, const struct firmware *fw)
+static int af9005_download_firmware(struct usb_device *udev, const struct firmware *fw)
 {
 	int i, packets, ret, act_len;
 

+ 1474 - 0
drivers/media/dvb/dvb-usb/af9015.c

@@ -0,0 +1,1474 @@
+/*
+ * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ *    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 "af9015.h"
+#include "af9013.h"
+#include "mt2060.h"
+#include "qt1010.h"
+#include "tda18271.h"
+#include "mxl5005s.h"
+#if 0
+#include "mc44s80x.h"
+#endif
+
+int dvb_usb_af9015_debug;
+module_param_named(debug, dvb_usb_af9015_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
+int dvb_usb_af9015_remote;
+module_param_named(remote, dvb_usb_af9015_remote, int, 0644);
+MODULE_PARM_DESC(remote, "select remote");
+int dvb_usb_af9015_dual_mode;
+module_param_named(dual_mode, dvb_usb_af9015_dual_mode, int, 0644);
+MODULE_PARM_DESC(dual_mode, "enable dual mode");
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static DEFINE_MUTEX(af9015_usb_mutex);
+
+static struct af9015_config af9015_config;
+static struct dvb_usb_device_properties af9015_properties[2];
+int af9015_properties_count = ARRAY_SIZE(af9015_properties);
+
+static struct af9013_config af9015_af9013_config[] = {
+	{
+		.demod_address = AF9015_I2C_DEMOD,
+		.output_mode = AF9013_OUTPUT_MODE_USB,
+		.api_version = { 0, 1, 9, 0 },
+		.gpio[0] = AF9013_GPIO_HI,
+		.gpio[3] = AF9013_GPIO_TUNER_ON,
+
+	}, {
+		.output_mode = AF9013_OUTPUT_MODE_SERIAL,
+		.api_version = { 0, 1, 9, 0 },
+		.gpio[0] = AF9013_GPIO_TUNER_ON,
+		.gpio[1] = AF9013_GPIO_LO,
+	}
+};
+
+static int af9015_rw_udev(struct usb_device *udev, struct req_t *req)
+{
+	int act_len, ret;
+	u8 buf[64];
+	u8 write = 1;
+	u8 msg_len = 8;
+	static u8 seq; /* packet sequence number */
+
+	if (mutex_lock_interruptible(&af9015_usb_mutex) < 0)
+		return -EAGAIN;
+
+	buf[0] = req->cmd;
+	buf[1] = seq++;
+	buf[2] = req->i2c_addr;
+	buf[3] = req->addr >> 8;
+	buf[4] = req->addr & 0xff;
+	buf[5] = req->mbox;
+	buf[6] = req->addr_len;
+	buf[7] = req->data_len;
+
+	switch (req->cmd) {
+	case GET_CONFIG:
+	case BOOT:
+	case READ_MEMORY:
+	case RECONNECT_USB:
+	case GET_IR_CODE:
+		write = 0;
+		break;
+	case READ_I2C:
+		write = 0;
+		buf[2] |= 0x01; /* set I2C direction */
+	case WRITE_I2C:
+		buf[0] = READ_WRITE_I2C;
+		break;
+	case WRITE_MEMORY:
+		if (((req->addr & 0xff00) == 0xff00) ||
+		    ((req->addr & 0xae00) == 0xae00))
+			buf[0] = WRITE_VIRTUAL_MEMORY;
+	case WRITE_VIRTUAL_MEMORY:
+	case COPY_FIRMWARE:
+	case DOWNLOAD_FIRMWARE:
+		break;
+	default:
+		err("unknown command:%d", req->cmd);
+		ret = -1;
+		goto error_unlock;
+	}
+
+	/* write requested */
+	if (write) {
+		memcpy(&buf[8], req->data, req->data_len);
+		msg_len += req->data_len;
+	}
+	deb_xfer(">>> ");
+	debug_dump(buf, msg_len, deb_xfer);
+
+	/* send req */
+	ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x02), buf, msg_len,
+	&act_len, AF9015_USB_TIMEOUT);
+	if (ret)
+		err("bulk message failed:%d (%d/%d)", ret, msg_len, act_len);
+	else
+		if (act_len != msg_len)
+			ret = -1; /* all data is not send */
+	if (ret)
+		goto error_unlock;
+
+	/* no ack for those packets */
+	if (req->cmd == DOWNLOAD_FIRMWARE || req->cmd == RECONNECT_USB)
+		goto exit_unlock;
+
+	/* receive ack and data if read req */
+	msg_len = 1 + 1 + req->data_len;  /* seq + status + data len */
+	ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, 0x81), buf, msg_len,
+			   &act_len, AF9015_USB_TIMEOUT);
+	if (ret) {
+		err("recv bulk message failed:%d", ret);
+		ret = -1;
+		goto error_unlock;
+	}
+
+	deb_xfer("<<< ");
+	debug_dump(buf, act_len, deb_xfer);
+
+	/* remote controller query status is 1 if remote code is not received */
+	if (req->cmd == GET_IR_CODE && buf[1] == 1) {
+		buf[1] = 0; /* clear command "error" status */
+		memset(&buf[2], 0, req->data_len);
+		buf[3] = 1; /* no remote code received mark */
+	}
+
+	/* check status */
+	if (buf[1]) {
+		err("command failed:%d", buf[1]);
+		ret = -1;
+		goto error_unlock;
+	}
+
+	/* read request, copy returned data to return buf */
+	if (!write)
+		memcpy(req->data, &buf[2], req->data_len);
+
+error_unlock:
+exit_unlock:
+	mutex_unlock(&af9015_usb_mutex);
+
+	return ret;
+}
+
+static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req)
+{
+	return af9015_rw_udev(d->udev, req);
+}
+
+static int af9015_write_regs(struct dvb_usb_device *d, u16 addr, u8 *val,
+	u8 len)
+{
+	struct req_t req = {WRITE_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, len,
+		val};
+	return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_write_reg(struct dvb_usb_device *d, u16 addr, u8 val)
+{
+	return af9015_write_regs(d, addr, &val, 1);
+}
+
+static int af9015_read_reg(struct dvb_usb_device *d, u16 addr, u8 *val)
+{
+	struct req_t req = {READ_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, 1, val};
+	return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_write_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg,
+	u8 val)
+{
+	struct req_t req = {WRITE_I2C, addr, reg, 1, 1, 1, &val};
+
+	if (addr == af9015_af9013_config[0].demod_address ||
+	    addr == af9015_af9013_config[1].demod_address)
+		req.addr_len = 3;
+
+	return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_read_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg,
+	u8 *val)
+{
+	struct req_t req = {READ_I2C, addr, reg, 0, 1, 1, val};
+
+	if (addr == af9015_af9013_config[0].demod_address ||
+	    addr == af9015_af9013_config[1].demod_address)
+		req.addr_len = 3;
+
+	return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+	int num)
+{
+	struct dvb_usb_device *d = i2c_get_adapdata(adap);
+	int ret = 0, i = 0;
+	u16 addr;
+	u8 mbox, addr_len;
+	struct req_t req;
+
+/* TODO: implement bus lock
+
+The bus lock is needed because there is two tuners both using same I2C-address.
+Due to that the only way to select correct tuner is use demodulator I2C-gate.
+
+................................................
+. AF9015 includes integrated AF9013 demodulator.
+. ____________                   ____________  .                ____________
+.|     uC     |                 |   demod    | .               |    tuner   |
+.|------------|                 |------------| .               |------------|
+.|   AF9015   |                 |  AF9013/5  | .               |   MXL5003  |
+.|            |--+----I2C-------|-----/ -----|-.-----I2C-------|            |
+.|            |  |              | addr 0x38  | .               |  addr 0xc6 |
+.|____________|  |              |____________| .               |____________|
+.................|..............................
+		 |               ____________                   ____________
+		 |              |   demod    |                 |    tuner   |
+		 |              |------------|                 |------------|
+		 |              |   AF9013   |                 |   MXL5003  |
+		 +----I2C-------|-----/ -----|-------I2C-------|            |
+				| addr 0x3a  |                 |  addr 0xc6 |
+				|____________|                 |____________|
+*/
+	if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+		return -EAGAIN;
+
+	while (i < num) {
+		if (msg[i].addr == af9015_af9013_config[0].demod_address ||
+		    msg[i].addr == af9015_af9013_config[1].demod_address) {
+			addr = msg[i].buf[0] << 8;
+			addr += msg[i].buf[1];
+			mbox = msg[i].buf[2];
+			addr_len = 3;
+		} else {
+			addr = msg[i].buf[0];
+			addr_len = 1;
+			mbox = 0;
+		}
+
+		if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
+			if (msg[i].addr ==
+				af9015_af9013_config[0].demod_address)
+				req.cmd = READ_MEMORY;
+			else
+				req.cmd = READ_I2C;
+			req.i2c_addr = msg[i].addr;
+			req.addr = addr;
+			req.mbox = mbox;
+			req.addr_len = addr_len;
+			req.data_len = msg[i+1].len;
+			req.data = &msg[i+1].buf[0];
+			ret = af9015_ctrl_msg(d, &req);
+			i += 2;
+		} else {
+			if (msg[i].addr ==
+				af9015_af9013_config[0].demod_address)
+				req.cmd = WRITE_MEMORY;
+			else
+				req.cmd = WRITE_I2C;
+			req.i2c_addr = msg[i].addr;
+			req.addr = addr;
+			req.mbox = mbox;
+			req.addr_len = addr_len;
+			req.data_len = msg[i].len-addr_len;
+			req.data = &msg[i].buf[addr_len];
+			ret = af9015_ctrl_msg(d, &req);
+			i += 1;
+		}
+		if (ret)
+			goto error;
+
+	}
+	ret = i;
+
+error:
+	mutex_unlock(&d->i2c_mutex);
+
+	return ret;
+}
+
+static u32 af9015_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm af9015_i2c_algo = {
+	.master_xfer = af9015_i2c_xfer,
+	.functionality = af9015_i2c_func,
+};
+
+static int af9015_do_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit, u8 op)
+{
+	int ret;
+	u8 val, mask = 0x01;
+
+	ret = af9015_read_reg(d, addr, &val);
+	if (ret)
+		return ret;
+
+	mask <<= bit;
+	if (op) {
+		/* set bit */
+		val |= mask;
+	} else {
+		/* clear bit */
+		mask ^= 0xff;
+		val &= mask;
+	}
+
+	return af9015_write_reg(d, addr, val);
+}
+
+static int af9015_set_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit)
+{
+	return af9015_do_reg_bit(d, addr, bit, 1);
+}
+
+static int af9015_clear_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit)
+{
+	return af9015_do_reg_bit(d, addr, bit, 0);
+}
+
+static int af9015_init_endpoint(struct dvb_usb_device *d)
+{
+	int ret;
+	u16 frame_size;
+	u8  packet_size;
+	deb_info("%s: USB speed:%d\n", __func__, d->udev->speed);
+
+#define TS_PACKET_SIZE            188
+
+#define TS_USB20_PACKET_COUNT     348
+#define TS_USB20_FRAME_SIZE       (TS_PACKET_SIZE*TS_USB20_PACKET_COUNT)
+
+#define TS_USB11_PACKET_COUNT      21
+#define TS_USB11_FRAME_SIZE       (TS_PACKET_SIZE*TS_USB11_PACKET_COUNT)
+
+#define TS_USB20_MAX_PACKET_SIZE  512
+#define TS_USB11_MAX_PACKET_SIZE   64
+
+	if (d->udev->speed == USB_SPEED_FULL) {
+		frame_size = TS_USB11_FRAME_SIZE/4;
+		packet_size = TS_USB11_MAX_PACKET_SIZE/4;
+	} else {
+		frame_size = TS_USB20_FRAME_SIZE/4;
+		packet_size = TS_USB20_MAX_PACKET_SIZE/4;
+	}
+
+	ret = af9015_set_reg_bit(d, 0xd507, 2); /* assert EP4 reset */
+	if (ret)
+		goto error;
+	ret = af9015_set_reg_bit(d, 0xd50b, 1); /* assert EP5 reset */
+	if (ret)
+		goto error;
+	ret = af9015_clear_reg_bit(d, 0xdd11, 5); /* disable EP4 */
+	if (ret)
+		goto error;
+	ret = af9015_clear_reg_bit(d, 0xdd11, 6); /* disable EP5 */
+	if (ret)
+		goto error;
+	ret = af9015_set_reg_bit(d, 0xdd11, 5); /* enable EP4 */
+	if (ret)
+		goto error;
+	if (af9015_config.dual_mode) {
+		ret = af9015_set_reg_bit(d, 0xdd11, 6); /* enable EP5 */
+		if (ret)
+			goto error;
+	}
+	ret = af9015_clear_reg_bit(d, 0xdd13, 5); /* disable EP4 NAK */
+	if (ret)
+		goto error;
+	if (af9015_config.dual_mode) {
+		ret = af9015_clear_reg_bit(d, 0xdd13, 6); /* disable EP5 NAK */
+		if (ret)
+			goto error;
+	}
+	/* EP4 xfer length */
+	ret = af9015_write_reg(d, 0xdd88, frame_size & 0xff);
+	if (ret)
+		goto error;
+	ret = af9015_write_reg(d, 0xdd89, frame_size >> 8);
+	if (ret)
+		goto error;
+	/* EP5 xfer length */
+	ret = af9015_write_reg(d, 0xdd8a, frame_size & 0xff);
+	if (ret)
+		goto error;
+	ret = af9015_write_reg(d, 0xdd8b, frame_size >> 8);
+	if (ret)
+		goto error;
+	ret = af9015_write_reg(d, 0xdd0c, packet_size); /* EP4 packet size */
+	if (ret)
+		goto error;
+	ret = af9015_write_reg(d, 0xdd0d, packet_size); /* EP5 packet size */
+	if (ret)
+		goto error;
+	ret = af9015_clear_reg_bit(d, 0xd507, 2); /* negate EP4 reset */
+	if (ret)
+		goto error;
+	if (af9015_config.dual_mode) {
+		ret = af9015_clear_reg_bit(d, 0xd50b, 1); /* negate EP5 reset */
+		if (ret)
+			goto error;
+	}
+
+	/* enable / disable mp2if2 */
+	if (af9015_config.dual_mode)
+		ret = af9015_set_reg_bit(d, 0xd50b, 0);
+	else
+		ret = af9015_clear_reg_bit(d, 0xd50b, 0);
+error:
+	if (ret)
+		err("endpoint init failed:%d", ret);
+	return ret;
+}
+
+static int af9015_copy_firmware(struct dvb_usb_device *d)
+{
+	int ret;
+	u8 fw_params[4];
+	u8 val, i;
+	struct req_t req = {COPY_FIRMWARE, 0, 0x5100, 0, 0, sizeof(fw_params),
+		fw_params };
+	deb_info("%s:\n", __func__);
+
+	fw_params[0] = af9015_config.firmware_size >> 8;
+	fw_params[1] = af9015_config.firmware_size & 0xff;
+	fw_params[2] = af9015_config.firmware_checksum >> 8;
+	fw_params[3] = af9015_config.firmware_checksum & 0xff;
+
+	/* wait 2nd demodulator ready */
+	msleep(100);
+
+	ret = af9015_read_reg_i2c(d, 0x3a, 0x98be, &val);
+	if (ret)
+		goto error;
+	else
+		deb_info("%s: firmware status:%02x\n", __func__, val);
+
+	if (val == 0x0c) /* fw is running, no need for download */
+		goto exit;
+
+	/* set I2C master clock to fast (to speed up firmware copy) */
+	ret = af9015_write_reg(d, 0xd416, 0x04); /* 0x04 * 400ns */
+	if (ret)
+		goto error;
+
+	msleep(50);
+
+	/* copy firmware */
+	ret = af9015_ctrl_msg(d, &req);
+	if (ret)
+		err("firmware copy cmd failed:%d", ret);
+	deb_info("%s: firmware copy done\n", __func__);
+
+	/* set I2C master clock back to normal */
+	ret = af9015_write_reg(d, 0xd416, 0x14); /* 0x14 * 400ns */
+	if (ret)
+		goto error;
+
+	/* request boot firmware */
+	ret = af9015_write_reg_i2c(d, af9015_af9013_config[1].demod_address,
+		0xe205, 1);
+	deb_info("%s: firmware boot cmd status:%d\n", __func__, ret);
+	if (ret)
+		goto error;
+
+	for (i = 0; i < 15; i++) {
+		msleep(100);
+
+		/* check firmware status */
+		ret = af9015_read_reg_i2c(d,
+			af9015_af9013_config[1].demod_address, 0x98be, &val);
+		deb_info("%s: firmware status cmd status:%d fw status:%02x\n",
+			__func__, ret, val);
+		if (ret)
+			goto error;
+
+		if (val == 0x0c || val == 0x04) /* success or fail */
+			break;
+	}
+
+	if (val == 0x04) {
+		err("firmware did not run");
+		ret = -1;
+	} else if (val != 0x0c) {
+		err("firmware boot timeout");
+		ret = -1;
+	}
+
+error:
+exit:
+	return ret;
+}
+
+/* dump eeprom */
+static int af9015_eeprom_dump(struct dvb_usb_device *d)
+{
+	char buf[52], buf2[4];
+	u8 reg, val;
+
+	for (reg = 0; ; reg++) {
+		if (reg % 16 == 0) {
+			if (reg)
+				deb_info("%s\n", buf);
+			sprintf(buf, "%02x: ", reg);
+		}
+		if (af9015_read_reg_i2c(d, AF9015_I2C_EEPROM, reg, &val) == 0)
+			sprintf(buf2, "%02x ", val);
+		else
+			strcpy(buf2, "-- ");
+		strcat(buf, buf2);
+		if (reg == 0xff)
+			break;
+	}
+	deb_info("%s\n", buf);
+	return 0;
+}
+
+int af9015_download_ir_table(struct dvb_usb_device *d)
+{
+	int i, packets = 0, ret;
+	u16 addr = 0x9a56; /* ir-table start address */
+	struct req_t req = {WRITE_MEMORY, 0, 0, 0, 0, 1, NULL};
+	u8 *data = NULL;
+	deb_info("%s:\n", __func__);
+
+	data = af9015_config.ir_table;
+	packets = af9015_config.ir_table_size;
+
+	/* no remote */
+	if (!packets)
+		goto exit;
+
+	/* load remote ir-table */
+	for (i = 0; i < packets; i++) {
+		req.addr = addr + i;
+		req.data = &data[i];
+		ret = af9015_ctrl_msg(d, &req);
+		if (ret) {
+			err("ir-table download failed at packet %d with " \
+				"code %d", i, ret);
+			return ret;
+		}
+	}
+
+exit:
+	return 0;
+}
+
+static int af9015_init(struct dvb_usb_device *d)
+{
+	int ret;
+	deb_info("%s:\n", __func__);
+
+	ret = af9015_init_endpoint(d);
+	if (ret)
+		goto error;
+
+	ret = af9015_download_ir_table(d);
+	if (ret)
+		goto error;
+
+error:
+	return ret;
+}
+
+static int af9015_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+	int ret;
+	deb_info("%s: onoff:%d\n", __func__, onoff);
+
+	if (onoff)
+		ret = af9015_set_reg_bit(adap->dev, 0xd503, 0);
+	else
+		ret = af9015_clear_reg_bit(adap->dev, 0xd503, 0);
+
+	return ret;
+}
+
+static int af9015_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid,
+	int onoff)
+{
+	int ret;
+	u8 idx;
+
+	deb_info("%s: set pid filter, index %d, pid %x, onoff %d\n",
+		__func__, index, pid, onoff);
+
+	ret = af9015_write_reg(adap->dev, 0xd505, (pid & 0xff));
+	if (ret)
+		goto error;
+
+	ret = af9015_write_reg(adap->dev, 0xd506, (pid >> 8));
+	if (ret)
+		goto error;
+
+	idx = ((index & 0x1f) | (1 << 5));
+	ret = af9015_write_reg(adap->dev, 0xd504, idx);
+
+error:
+	return ret;
+}
+
+static int af9015_download_firmware(struct usb_device *udev,
+	const struct firmware *fw)
+{
+	int i, len, packets, remainder, ret;
+	struct req_t req = {DOWNLOAD_FIRMWARE, 0, 0, 0, 0, 0, NULL};
+	u16 addr = 0x5100; /* firmware start address */
+	u16 checksum = 0;
+
+	deb_info("%s:\n", __func__);
+
+	/* calc checksum */
+	for (i = 0; i < fw->size; i++)
+		checksum += fw->data[i];
+
+	af9015_config.firmware_size = fw->size;
+	af9015_config.firmware_checksum = checksum;
+
+	#define FW_PACKET_MAX_DATA  55
+
+	packets = fw->size / FW_PACKET_MAX_DATA;
+	remainder = fw->size % FW_PACKET_MAX_DATA;
+	len = FW_PACKET_MAX_DATA;
+	for (i = 0; i <= packets; i++) {
+		if (i == packets)  /* set size of the last packet */
+			len = remainder;
+
+		req.data_len = len;
+		req.data = (u8 *)(fw->data + i * FW_PACKET_MAX_DATA);
+		req.addr = addr;
+		addr += FW_PACKET_MAX_DATA;
+
+		ret = af9015_rw_udev(udev, &req);
+		if (ret) {
+			err("firmware download failed at packet %d with " \
+				"code %d", i, ret);
+			goto error;
+		}
+	}
+
+	/* firmware loaded, request boot */
+	req.cmd = BOOT;
+	ret = af9015_rw_udev(udev, &req);
+	if (ret) {
+		err("firmware boot failed:%d", ret);
+		goto error;
+	}
+
+	/* firmware is running, reconnect device in the usb bus */
+	req.cmd = RECONNECT_USB;
+	ret = af9015_rw_udev(udev, &req);
+	if (ret)
+		err("reconnect failed: %d", ret);
+
+error:
+	return ret;
+}
+
+static int af9015_read_config(struct usb_device *udev)
+{
+	int ret;
+	u8 val, i, offset = 0;
+	struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val};
+	char manufacturer[10];
+
+	/* IR remote controller */
+	req.addr = AF9015_EEPROM_IR_MODE;
+	ret = af9015_rw_udev(udev, &req);
+	if (ret)
+		goto error;
+	deb_info("%s: IR mode:%d\n", __func__, val);
+	for (i = 0; i < af9015_properties_count; i++) {
+		if (val == AF9015_IR_MODE_DISABLED || val == 0x04) {
+			af9015_properties[i].rc_key_map = NULL;
+			af9015_properties[i].rc_key_map_size  = 0;
+		} else if (dvb_usb_af9015_remote) {
+			/* load remote defined as module param */
+			switch (dvb_usb_af9015_remote) {
+			case AF9015_REMOTE_A_LINK_DTU_M:
+				af9015_properties[i].rc_key_map =
+				  af9015_rc_keys_a_link;
+				af9015_properties[i].rc_key_map_size =
+				  ARRAY_SIZE(af9015_rc_keys_a_link);
+				af9015_config.ir_table = af9015_ir_table_a_link;
+				af9015_config.ir_table_size =
+				  ARRAY_SIZE(af9015_ir_table_a_link);
+				break;
+			case AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3:
+				af9015_properties[i].rc_key_map =
+				  af9015_rc_keys_msi;
+				af9015_properties[i].rc_key_map_size =
+				  ARRAY_SIZE(af9015_rc_keys_msi);
+				af9015_config.ir_table = af9015_ir_table_msi;
+				af9015_config.ir_table_size =
+				  ARRAY_SIZE(af9015_ir_table_msi);
+				break;
+			case AF9015_REMOTE_MYGICTV_U718:
+				af9015_properties[i].rc_key_map =
+				  af9015_rc_keys_mygictv;
+				af9015_properties[i].rc_key_map_size =
+				  ARRAY_SIZE(af9015_rc_keys_mygictv);
+				af9015_config.ir_table =
+				  af9015_ir_table_mygictv;
+				af9015_config.ir_table_size =
+				  ARRAY_SIZE(af9015_ir_table_mygictv);
+				break;
+			}
+		} else {
+			switch (udev->descriptor.idVendor) {
+			case USB_VID_LEADTEK:
+				af9015_properties[i].rc_key_map =
+				  af9015_rc_keys_leadtek;
+				af9015_properties[i].rc_key_map_size =
+				  ARRAY_SIZE(af9015_rc_keys_leadtek);
+				af9015_config.ir_table =
+				  af9015_ir_table_leadtek;
+				af9015_config.ir_table_size =
+				  ARRAY_SIZE(af9015_ir_table_leadtek);
+				break;
+			case USB_VID_VISIONPLUS:
+				if (udev->descriptor.idProduct ==
+				USB_PID_AZUREWAVE_AD_TU700) {
+					af9015_properties[i].rc_key_map =
+					  af9015_rc_keys_twinhan;
+					af9015_properties[i].rc_key_map_size =
+					  ARRAY_SIZE(af9015_rc_keys_twinhan);
+					af9015_config.ir_table =
+					  af9015_ir_table_twinhan;
+					af9015_config.ir_table_size =
+					  ARRAY_SIZE(af9015_ir_table_twinhan);
+				}
+				break;
+			case USB_VID_KWORLD_2:
+				/* TODO: use correct rc keys */
+				af9015_properties[i].rc_key_map =
+				  af9015_rc_keys_twinhan;
+				af9015_properties[i].rc_key_map_size =
+				  ARRAY_SIZE(af9015_rc_keys_twinhan);
+				af9015_config.ir_table = af9015_ir_table_kworld;
+				af9015_config.ir_table_size =
+				  ARRAY_SIZE(af9015_ir_table_kworld);
+				break;
+			/* Check USB manufacturer and product strings and try
+			   to determine correct remote in case of chip vendor
+			   reference IDs are used. */
+			case USB_VID_AFATECH:
+				memset(manufacturer, 0, sizeof(manufacturer));
+				usb_string(udev, udev->descriptor.iManufacturer,
+					manufacturer, sizeof(manufacturer));
+				if (!strcmp("Geniatech", manufacturer)) {
+					/* iManufacturer 1 Geniatech
+					   iProduct      2 AF9015 */
+					af9015_properties[i].rc_key_map =
+					  af9015_rc_keys_mygictv;
+					af9015_properties[i].rc_key_map_size =
+					  ARRAY_SIZE(af9015_rc_keys_mygictv);
+					af9015_config.ir_table =
+					  af9015_ir_table_mygictv;
+					af9015_config.ir_table_size =
+					  ARRAY_SIZE(af9015_ir_table_mygictv);
+				} else if (!strcmp("MSI", manufacturer)) {
+					/* iManufacturer 1 MSI
+					   iProduct      2 MSI K-VOX */
+					af9015_properties[i].rc_key_map =
+					  af9015_rc_keys_msi;
+					af9015_properties[i].rc_key_map_size =
+					  ARRAY_SIZE(af9015_rc_keys_msi);
+					af9015_config.ir_table =
+					  af9015_ir_table_msi;
+					af9015_config.ir_table_size =
+					  ARRAY_SIZE(af9015_ir_table_msi);
+				}
+				break;
+			}
+		}
+	}
+
+	/* TS mode - one or two receivers */
+	req.addr = AF9015_EEPROM_TS_MODE;
+	ret = af9015_rw_udev(udev, &req);
+	if (ret)
+		goto error;
+	af9015_config.dual_mode = val;
+	deb_info("%s: TS mode:%d\n", __func__, af9015_config.dual_mode);
+	/* disable dual mode by default because it is buggy */
+	if (!dvb_usb_af9015_dual_mode)
+		af9015_config.dual_mode = 0;
+
+	/* set buffer size according to USB port speed */
+	for (i = 0; i < af9015_properties_count; i++) {
+		/* USB1.1 set smaller buffersize and disable 2nd adapter */
+		if (udev->speed == USB_SPEED_FULL) {
+			af9015_properties[i].adapter->stream.u.bulk.buffersize =
+				TS_USB11_MAX_PACKET_SIZE;
+			/* disable 2nd adapter because we don't have
+			   PID-filters */
+			af9015_config.dual_mode = 0;
+		} else {
+			af9015_properties[i].adapter->stream.u.bulk.buffersize =
+				TS_USB20_MAX_PACKET_SIZE;
+		}
+	}
+
+	if (af9015_config.dual_mode) {
+		/* read 2nd demodulator I2C address */
+		req.addr = AF9015_EEPROM_DEMOD2_I2C;
+		ret = af9015_rw_udev(udev, &req);
+		if (ret)
+			goto error;
+		af9015_af9013_config[1].demod_address = val;
+
+		/* enable 2nd adapter */
+		for (i = 0; i < af9015_properties_count; i++)
+			af9015_properties[i].num_adapters = 2;
+
+	} else {
+		 /* disable 2nd adapter */
+		for (i = 0; i < af9015_properties_count; i++)
+			af9015_properties[i].num_adapters = 1;
+	}
+
+	for (i = 0; i < af9015_properties[0].num_adapters; i++) {
+		if (i == 1)
+			offset = AF9015_EEPROM_OFFSET;
+		/* xtal */
+		req.addr = AF9015_EEPROM_XTAL_TYPE1 + offset;
+		ret = af9015_rw_udev(udev, &req);
+		if (ret)
+			goto error;
+		switch (val) {
+		case 0:
+			af9015_af9013_config[i].adc_clock = 28800;
+			break;
+		case 1:
+			af9015_af9013_config[i].adc_clock = 20480;
+			break;
+		case 2:
+			af9015_af9013_config[i].adc_clock = 28000;
+			break;
+		case 3:
+			af9015_af9013_config[i].adc_clock = 25000;
+			break;
+		};
+		deb_info("%s: [%d] xtal:%d set adc_clock:%d\n", __func__, i,
+			val, af9015_af9013_config[i].adc_clock);
+
+		/* tuner IF */
+		req.addr = AF9015_EEPROM_IF1H + offset;
+		ret = af9015_rw_udev(udev, &req);
+		if (ret)
+			goto error;
+		af9015_af9013_config[i].tuner_if = val << 8;
+		req.addr = AF9015_EEPROM_IF1L + offset;
+		ret = af9015_rw_udev(udev, &req);
+		if (ret)
+			goto error;
+		af9015_af9013_config[i].tuner_if += val;
+		deb_info("%s: [%d] IF1:%d\n", __func__, i,
+			af9015_af9013_config[0].tuner_if);
+
+		/* MT2060 IF1 */
+		req.addr = AF9015_EEPROM_MT2060_IF1H  + offset;
+		ret = af9015_rw_udev(udev, &req);
+		if (ret)
+			goto error;
+		af9015_config.mt2060_if1[i] = val << 8;
+		req.addr = AF9015_EEPROM_MT2060_IF1L + offset;
+		ret = af9015_rw_udev(udev, &req);
+		if (ret)
+			goto error;
+		af9015_config.mt2060_if1[i] += val;
+		deb_info("%s: [%d] MT2060 IF1:%d\n", __func__, i,
+			af9015_config.mt2060_if1[i]);
+
+		/* tuner */
+		req.addr =  AF9015_EEPROM_TUNER_ID1 + offset;
+		ret = af9015_rw_udev(udev, &req);
+		if (ret)
+			goto error;
+		switch (val) {
+		case AF9013_TUNER_ENV77H11D5:
+		case AF9013_TUNER_MT2060:
+		case AF9013_TUNER_MC44S803:
+		case AF9013_TUNER_QT1010:
+		case AF9013_TUNER_UNKNOWN:
+		case AF9013_TUNER_MT2060_2:
+		case AF9013_TUNER_TDA18271:
+		case AF9013_TUNER_QT1010A:
+			af9015_af9013_config[i].rf_spec_inv = 1;
+			break;
+		case AF9013_TUNER_MXL5003D:
+		case AF9013_TUNER_MXL5005D:
+		case AF9013_TUNER_MXL5005R:
+			af9015_af9013_config[i].rf_spec_inv = 0;
+			break;
+		default:
+			warn("tuner id:%d not supported, please report!", val);
+			return -ENODEV;
+		};
+
+		af9015_af9013_config[i].tuner = val;
+		deb_info("%s: [%d] tuner id:%d\n", __func__, i, val);
+	}
+
+error:
+	if (ret)
+		err("eeprom read failed:%d", ret);
+
+	return ret;
+}
+
+static int af9015_identify_state(struct usb_device *udev,
+				 struct dvb_usb_device_properties *props,
+				 struct dvb_usb_device_description **desc,
+				 int *cold)
+{
+	int ret;
+	u8 reply;
+	struct req_t req = {GET_CONFIG, 0, 0, 0, 0, 1, &reply};
+
+	ret = af9015_rw_udev(udev, &req);
+	if (ret)
+		return ret;
+
+	deb_info("%s: reply:%02x\n", __func__, reply);
+	if (reply == 0x02)
+		*cold = 0;
+	else
+		*cold = 1;
+
+	return ret;
+}
+
+static int af9015_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
+{
+	u8 buf[8];
+	struct req_t req = {GET_IR_CODE, 0, 0, 0, 0, sizeof(buf), buf};
+	struct dvb_usb_rc_key *keymap = d->props.rc_key_map;
+	int i, ret;
+
+	memset(buf, 0, sizeof(buf));
+
+	ret = af9015_ctrl_msg(d, &req);
+	if (ret)
+		return ret;
+
+	*event = 0;
+	*state = REMOTE_NO_KEY_PRESSED;
+
+	for (i = 0; i < d->props.rc_key_map_size; i++) {
+		if (!buf[1] && keymap[i].custom == buf[0] &&
+		    keymap[i].data == buf[2]) {
+			*event = keymap[i].event;
+			*state = REMOTE_KEY_PRESSED;
+			break;
+		}
+	}
+	if (!buf[1])
+		deb_rc("%s: %02x %02x %02x %02x %02x %02x %02x %02x\n",
+			__func__, buf[0], buf[1], buf[2], buf[3], buf[4],
+			buf[5], buf[6], buf[7]);
+
+	return 0;
+}
+
+/* init 2nd I2C adapter */
+int af9015_i2c_init(struct dvb_usb_device *d)
+{
+	int ret;
+	struct af9015_state *state = d->priv;
+	deb_info("%s:\n", __func__);
+
+	strncpy(state->i2c_adap.name, d->desc->name,
+		sizeof(state->i2c_adap.name));
+#ifdef I2C_ADAP_CLASS_TV_DIGITAL
+	state->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL,
+#else
+	state->i2c_adap.class = I2C_CLASS_TV_DIGITAL,
+#endif
+	state->i2c_adap.algo      = d->props.i2c_algo;
+	state->i2c_adap.algo_data = NULL;
+	state->i2c_adap.dev.parent = &d->udev->dev;
+
+	i2c_set_adapdata(&state->i2c_adap, d);
+
+	ret = i2c_add_adapter(&state->i2c_adap);
+	if (ret < 0)
+		err("could not add i2c adapter");
+
+	return ret;
+}
+
+static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
+{
+	int ret;
+	struct af9015_state *state = adap->dev->priv;
+	struct i2c_adapter *i2c_adap;
+
+	if (adap->id == 0) {
+		/* select I2C adapter */
+		i2c_adap = &adap->dev->i2c_adap;
+
+		deb_info("%s: init I2C\n", __func__);
+		ret = af9015_i2c_init(adap->dev);
+
+		/* dump eeprom (debug) */
+		ret = af9015_eeprom_dump(adap->dev);
+		if (ret)
+			return ret;
+	} else {
+		/* select I2C adapter */
+		i2c_adap = &state->i2c_adap;
+
+		/* copy firmware to 2nd demodulator */
+		if (af9015_config.dual_mode) {
+			ret = af9015_copy_firmware(adap->dev);
+			if (ret) {
+				err("firmware copy to 2nd frontend " \
+					"failed, will disable it");
+				af9015_config.dual_mode = 0;
+				return -ENODEV;
+			}
+		} else {
+			return -ENODEV;
+		}
+	}
+
+	/* attach demodulator */
+	adap->fe = dvb_attach(af9013_attach, &af9015_af9013_config[adap->id],
+		i2c_adap);
+
+	return adap->fe == NULL ? -ENODEV : 0;
+}
+
+static struct mt2060_config af9015_mt2060_config = {
+	.i2c_address = 0xc0,
+	.clock_out = 0,
+};
+
+static struct qt1010_config af9015_qt1010_config = {
+	.i2c_address = 0xc4,
+};
+
+static struct tda18271_config af9015_tda18271_config = {
+	.gate = TDA18271_GATE_DIGITAL,
+	.small_i2c = 1,
+};
+
+static struct mxl5005s_config af9015_mxl5003_config = {
+	.i2c_address     = 0xc6,
+	.if_freq         = IF_FREQ_4570000HZ,
+	.xtal_freq       = CRYSTAL_FREQ_16000000HZ,
+	.agc_mode        = MXL_SINGLE_AGC,
+	.tracking_filter = MXL_TF_DEFAULT,
+	.rssi_enable     = MXL_RSSI_ENABLE,
+	.cap_select      = MXL_CAP_SEL_ENABLE,
+	.div_out         = MXL_DIV_OUT_4,
+	.clock_out       = MXL_CLOCK_OUT_DISABLE,
+	.output_load     = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+	.top		 = MXL5005S_TOP_25P2,
+	.mod_mode        = MXL_DIGITAL_MODE,
+	.if_mode         = MXL_ZERO_IF,
+	.AgcMasterByte   = 0x00,
+};
+
+static struct mxl5005s_config af9015_mxl5005_config = {
+	.i2c_address     = 0xc6,
+	.if_freq         = IF_FREQ_4570000HZ,
+	.xtal_freq       = CRYSTAL_FREQ_16000000HZ,
+	.agc_mode        = MXL_SINGLE_AGC,
+	.tracking_filter = MXL_TF_OFF,
+	.rssi_enable     = MXL_RSSI_ENABLE,
+	.cap_select      = MXL_CAP_SEL_ENABLE,
+	.div_out         = MXL_DIV_OUT_4,
+	.clock_out       = MXL_CLOCK_OUT_DISABLE,
+	.output_load     = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+	.top		 = MXL5005S_TOP_25P2,
+	.mod_mode        = MXL_DIGITAL_MODE,
+	.if_mode         = MXL_ZERO_IF,
+	.AgcMasterByte   = 0x00,
+};
+
+static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
+{
+	struct af9015_state *state = adap->dev->priv;
+	struct i2c_adapter *i2c_adap;
+	int ret;
+	deb_info("%s: \n", __func__);
+
+	/* select I2C adapter */
+	if (adap->id == 0)
+		i2c_adap = &adap->dev->i2c_adap;
+	else
+		i2c_adap = &state->i2c_adap;
+
+	switch (af9015_af9013_config[adap->id].tuner) {
+	case AF9013_TUNER_MT2060:
+	case AF9013_TUNER_MT2060_2:
+		ret = dvb_attach(mt2060_attach, adap->fe, i2c_adap,
+			&af9015_mt2060_config,
+			af9015_config.mt2060_if1[adap->id])
+			== NULL ? -ENODEV : 0;
+		break;
+	case AF9013_TUNER_QT1010:
+	case AF9013_TUNER_QT1010A:
+		ret = dvb_attach(qt1010_attach, adap->fe, i2c_adap,
+			&af9015_qt1010_config) == NULL ? -ENODEV : 0;
+		break;
+	case AF9013_TUNER_TDA18271:
+		ret = dvb_attach(tda18271_attach, adap->fe, 0xc0, i2c_adap,
+			&af9015_tda18271_config) == NULL ? -ENODEV : 0;
+		break;
+	case AF9013_TUNER_MXL5003D:
+		ret = dvb_attach(mxl5005s_attach, adap->fe, i2c_adap,
+			&af9015_mxl5003_config) == NULL ? -ENODEV : 0;
+		break;
+	case AF9013_TUNER_MXL5005D:
+	case AF9013_TUNER_MXL5005R:
+		ret = dvb_attach(mxl5005s_attach, adap->fe, i2c_adap,
+			&af9015_mxl5005_config) == NULL ? -ENODEV : 0;
+		break;
+	case AF9013_TUNER_ENV77H11D5:
+		ret = dvb_attach(dvb_pll_attach, adap->fe, 0xc0, i2c_adap,
+			DVB_PLL_TDA665X) == NULL ? -ENODEV : 0;
+		break;
+	case AF9013_TUNER_MC44S803:
+#if 0
+		ret = dvb_attach(mc44s80x_attach, adap->fe, i2c_adap)
+			== NULL ? -ENODEV : 0;
+#else
+		ret = -ENODEV;
+		info("Freescale MC44S803 tuner found but no driver for that" \
+			"tuner. Look at the Linuxtv.org for tuner driver" \
+			"status.");
+#endif
+		break;
+	case AF9013_TUNER_UNKNOWN:
+	default:
+		ret = -ENODEV;
+		err("Unknown tuner id:%d",
+			af9015_af9013_config[adap->id].tuner);
+	}
+	return ret;
+}
+
+static struct usb_device_id af9015_usb_table[] = {
+/*  0 */{USB_DEVICE(USB_VID_AFATECH,   USB_PID_AFATECH_AF9015_9015)},
+	{USB_DEVICE(USB_VID_AFATECH,   USB_PID_AFATECH_AF9015_9016)},
+	{USB_DEVICE(USB_VID_LEADTEK,   USB_PID_WINFAST_DTV_DONGLE_GOLD)},
+	{USB_DEVICE(USB_VID_PINNACLE,  USB_PID_PINNACLE_PCTV71E)},
+	{USB_DEVICE(USB_VID_KWORLD_2,  USB_PID_KWORLD_399U)},
+/*  5 */{USB_DEVICE(USB_VID_VISIONPLUS,
+		USB_PID_TINYTWIN)},
+	{USB_DEVICE(USB_VID_VISIONPLUS,
+		USB_PID_AZUREWAVE_AD_TU700)},
+	{USB_DEVICE(USB_VID_TERRATEC,  USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2)},
+	{USB_DEVICE(USB_VID_KWORLD_2,  USB_PID_KWORLD_PC160_2T)},
+	{USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X)},
+/* 10 */{USB_DEVICE(USB_VID_XTENSIONS, USB_PID_XTENSIONS_XD_380)},
+	{USB_DEVICE(USB_VID_MSI_2,     USB_PID_MSI_DIGIVOX_DUO)},
+	{USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X_2)},
+	{USB_DEVICE(USB_VID_TELESTAR,  USB_PID_TELESTAR_STARSTICK_2)},
+	{USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A309)},
+/* 15 */{USB_DEVICE(USB_VID_MSI_2,     USB_PID_MSI_DIGI_VOX_MINI_III)},
+	{0},
+};
+MODULE_DEVICE_TABLE(usb, af9015_usb_table);
+
+static struct dvb_usb_device_properties af9015_properties[] = {
+	{
+		.caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+		.usb_ctrl = DEVICE_SPECIFIC,
+		.download_firmware = af9015_download_firmware,
+		.firmware = "dvb-usb-af9015.fw",
+
+		.size_of_priv = sizeof(struct af9015_state), \
+
+		.num_adapters = 2,
+		.adapter = {
+			{
+				.caps = DVB_USB_ADAP_HAS_PID_FILTER |
+				DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+
+				.pid_filter_count = 32,
+				.pid_filter       = af9015_pid_filter,
+				.pid_filter_ctrl  = af9015_pid_filter_ctrl,
+
+				.frontend_attach =
+					af9015_af9013_frontend_attach,
+				.tuner_attach    = af9015_tuner_attach,
+				.stream = {
+					.type = USB_BULK,
+					.count = 6,
+					.endpoint = 0x84,
+				},
+			},
+			{
+				.frontend_attach =
+					af9015_af9013_frontend_attach,
+				.tuner_attach    = af9015_tuner_attach,
+				.stream = {
+					.type = USB_BULK,
+					.count = 6,
+					.endpoint = 0x85,
+				},
+			}
+		},
+
+		.identify_state = af9015_identify_state,
+
+		.rc_query         = af9015_rc_query,
+		.rc_interval      = 150,
+
+		.i2c_algo = &af9015_i2c_algo,
+
+		.num_device_descs = 9,
+		.devices = {
+			{
+				.name = "Afatech AF9015 DVB-T USB2.0 stick",
+				.cold_ids = {&af9015_usb_table[0],
+					     &af9015_usb_table[1], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "Leadtek WinFast DTV Dongle Gold",
+				.cold_ids = {&af9015_usb_table[2], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "Pinnacle PCTV 71e",
+				.cold_ids = {&af9015_usb_table[3], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "KWorld PlusTV Dual DVB-T Stick " \
+					"(DVB-T 399U)",
+				.cold_ids = {&af9015_usb_table[4], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "DigitalNow TinyTwin DVB-T Receiver",
+				.cold_ids = {&af9015_usb_table[5], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "TwinHan AzureWave AD-TU700(704J)",
+				.cold_ids = {&af9015_usb_table[6], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "TerraTec Cinergy T USB XE",
+				.cold_ids = {&af9015_usb_table[7], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "KWorld PlusTV Dual DVB-T PCI " \
+					"(DVB-T PC160-2T)",
+				.cold_ids = {&af9015_usb_table[8], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "AVerMedia AVerTV DVB-T Volar X",
+				.cold_ids = {&af9015_usb_table[9], NULL},
+				.warm_ids = {NULL},
+			},
+		}
+	}, {
+		.caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+		.usb_ctrl = DEVICE_SPECIFIC,
+		.download_firmware = af9015_download_firmware,
+		.firmware = "dvb-usb-af9015.fw",
+
+		.size_of_priv = sizeof(struct af9015_state), \
+
+		.num_adapters = 2,
+		.adapter = {
+			{
+				.caps = DVB_USB_ADAP_HAS_PID_FILTER |
+				DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+
+				.pid_filter_count = 32,
+				.pid_filter       = af9015_pid_filter,
+				.pid_filter_ctrl  = af9015_pid_filter_ctrl,
+
+				.frontend_attach =
+					af9015_af9013_frontend_attach,
+				.tuner_attach    = af9015_tuner_attach,
+				.stream = {
+					.type = USB_BULK,
+					.count = 6,
+					.endpoint = 0x84,
+				},
+			},
+			{
+				.frontend_attach =
+					af9015_af9013_frontend_attach,
+				.tuner_attach    = af9015_tuner_attach,
+				.stream = {
+					.type = USB_BULK,
+					.count = 6,
+					.endpoint = 0x85,
+				},
+			}
+		},
+
+		.identify_state = af9015_identify_state,
+
+		.rc_query         = af9015_rc_query,
+		.rc_interval      = 150,
+
+		.i2c_algo = &af9015_i2c_algo,
+
+		.num_device_descs = 6,
+		.devices = {
+			{
+				.name = "Xtensions XD-380",
+				.cold_ids = {&af9015_usb_table[10], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "MSI DIGIVOX Duo",
+				.cold_ids = {&af9015_usb_table[11], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "Fujitsu-Siemens Slim Mobile USB DVB-T",
+				.cold_ids = {&af9015_usb_table[12], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "Telestar Starstick 2",
+				.cold_ids = {&af9015_usb_table[13], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "AVerMedia A309",
+				.cold_ids = {&af9015_usb_table[14], NULL},
+				.warm_ids = {NULL},
+			},
+			{
+				.name = "MSI Digi VOX mini III",
+				.cold_ids = {&af9015_usb_table[15], NULL},
+				.warm_ids = {NULL},
+			},
+		}
+	}
+};
+
+static int af9015_usb_probe(struct usb_interface *intf,
+			    const struct usb_device_id *id)
+{
+	int ret = 0;
+	struct dvb_usb_device *d = NULL;
+	struct usb_device *udev = interface_to_usbdev(intf);
+	u8 i;
+
+	deb_info("%s: interface:%d\n", __func__,
+		intf->cur_altsetting->desc.bInterfaceNumber);
+
+	/* interface 0 is used by DVB-T receiver and
+	   interface 1 is for remote controller (HID) */
+	if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
+		ret = af9015_read_config(udev);
+		if (ret)
+			return ret;
+
+		for (i = 0; i < af9015_properties_count; i++) {
+			ret = dvb_usb_device_init(intf, &af9015_properties[i],
+				THIS_MODULE, &d, adapter_nr);
+			if (!ret)
+				break;
+			if (ret != -ENODEV)
+				return ret;
+		}
+		if (ret)
+			return ret;
+
+		if (d)
+			ret = af9015_init(d);
+	}
+
+	return ret;
+}
+
+void af9015_i2c_exit(struct dvb_usb_device *d)
+{
+	struct af9015_state *state = d->priv;
+	deb_info("%s: \n", __func__);
+
+	/* remove 2nd I2C adapter */
+	if (d->state & DVB_USB_STATE_I2C)
+		i2c_del_adapter(&state->i2c_adap);
+}
+
+static void af9015_usb_device_exit(struct usb_interface *intf)
+{
+	struct dvb_usb_device *d = usb_get_intfdata(intf);
+	deb_info("%s: \n", __func__);
+
+	/* remove 2nd I2C adapter */
+	if (d != NULL && d->desc != NULL)
+		af9015_i2c_exit(d);
+
+	dvb_usb_device_exit(intf);
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver af9015_usb_driver = {
+	.name = "dvb_usb_af9015",
+	.probe = af9015_usb_probe,
+	.disconnect = af9015_usb_device_exit,
+	.id_table = af9015_usb_table,
+};
+
+/* module stuff */
+static int __init af9015_usb_module_init(void)
+{
+	int ret;
+	ret = usb_register(&af9015_usb_driver);
+	if (ret)
+		err("module init failed:%d", ret);
+
+	return ret;
+}
+
+static void __exit af9015_usb_module_exit(void)
+{
+	/* deregister this driver from the USB subsystem */
+	usb_deregister(&af9015_usb_driver);
+}
+
+module_init(af9015_usb_module_init);
+module_exit(af9015_usb_module_exit);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Driver for Afatech AF9015 DVB-T");
+MODULE_LICENSE("GPL");

+ 524 - 0
drivers/media/dvb/dvb-usb/af9015.h

@@ -0,0 +1,524 @@
+/*
+ * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ *    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 _DVB_USB_AF9015_H_
+#define _DVB_USB_AF9015_H_
+
+#define DVB_USB_LOG_PREFIX "af9015"
+#include "dvb-usb.h"
+
+extern int dvb_usb_af9015_debug;
+#define deb_info(args...) dprintk(dvb_usb_af9015_debug, 0x01, args)
+#define deb_rc(args...)   dprintk(dvb_usb_af9015_debug, 0x02, args)
+#define deb_xfer(args...) dprintk(dvb_usb_af9015_debug, 0x04, args)
+#define deb_reg(args...)  dprintk(dvb_usb_af9015_debug, 0x08, args)
+#define deb_i2c(args...)  dprintk(dvb_usb_af9015_debug, 0x10, args)
+#define deb_fw(args...)   dprintk(dvb_usb_af9015_debug, 0x20, args)
+
+#define AF9015_I2C_EEPROM  0xa0
+#define AF9015_I2C_DEMOD   0x38
+#define AF9015_USB_TIMEOUT 2000
+
+/* EEPROM locations */
+#define AF9015_EEPROM_IR_MODE        0x18
+#define AF9015_EEPROM_IR_REMOTE_TYPE 0x34
+#define AF9015_EEPROM_TS_MODE        0x31
+#define AF9015_EEPROM_DEMOD2_I2C     0x32
+
+#define AF9015_EEPROM_SAW_BW1        0x35
+#define AF9015_EEPROM_XTAL_TYPE1     0x36
+#define AF9015_EEPROM_SPEC_INV1      0x37
+#define AF9015_EEPROM_IF1L           0x38
+#define AF9015_EEPROM_IF1H           0x39
+#define AF9015_EEPROM_MT2060_IF1L    0x3a
+#define AF9015_EEPROM_MT2060_IF1H    0x3b
+#define AF9015_EEPROM_TUNER_ID1      0x3c
+
+#define AF9015_EEPROM_SAW_BW2        0x45
+#define AF9015_EEPROM_XTAL_TYPE2     0x46
+#define AF9015_EEPROM_SPEC_INV2      0x47
+#define AF9015_EEPROM_IF2L           0x48
+#define AF9015_EEPROM_IF2H           0x49
+#define AF9015_EEPROM_MT2060_IF2L    0x4a
+#define AF9015_EEPROM_MT2060_IF2H    0x4b
+#define AF9015_EEPROM_TUNER_ID2      0x4c
+
+#define AF9015_EEPROM_OFFSET (AF9015_EEPROM_SAW_BW2 - AF9015_EEPROM_SAW_BW1)
+
+#define AF9015_GPIO_ON (1 << 0)
+#define AF9015_GPIO_EN (1 << 1)
+#define AF9015_GPIO_O  (1 << 2)
+#define AF9015_GPIO_I  (1 << 3)
+
+#define AF9015_GPIO_TUNER_ON  (AF9015_GPIO_ON|AF9015_GPIO_EN)
+#define AF9015_GPIO_TUNER_OFF (AF9015_GPIO_ON|AF9015_GPIO_EN|AF9015_GPIO_O)
+
+struct req_t {
+	u8  cmd;       /* [0] */
+	/*  seq */     /* [1] */
+	u8  i2c_addr;  /* [2] */
+	u16 addr;      /* [3|4] */
+	u8  mbox;      /* [5] */
+	u8  addr_len;  /* [6] */
+	u8  data_len;  /* [7] */
+	u8  *data;
+};
+
+enum af9015_cmd {
+	GET_CONFIG           = 0x10,
+	DOWNLOAD_FIRMWARE    = 0x11,
+	BOOT                 = 0x13,
+	READ_MEMORY          = 0x20,
+	WRITE_MEMORY         = 0x21,
+	READ_WRITE_I2C       = 0x22,
+	COPY_FIRMWARE        = 0x23,
+	RECONNECT_USB        = 0x5a,
+	WRITE_VIRTUAL_MEMORY = 0x26,
+	GET_IR_CODE          = 0x27,
+	READ_I2C,
+	WRITE_I2C,
+};
+
+enum af9015_ir_mode {
+	AF9015_IR_MODE_DISABLED = 0,
+	AF9015_IR_MODE_HID,
+	AF9015_IR_MODE_RLC,
+	AF9015_IR_MODE_RC6,
+};
+
+struct af9015_state {
+	struct i2c_adapter i2c_adap; /* I2C adapter for 2nd FE */
+};
+
+struct af9015_config {
+	u8  dual_mode:1;
+	u16 mt2060_if1[2];
+	u16 firmware_size;
+	u16 firmware_checksum;
+	u8  *ir_table;
+	u16 ir_table_size;
+};
+
+enum af9015_remote {
+	AF9015_REMOTE_NONE                    = 0,
+	AF9015_REMOTE_A_LINK_DTU_M,
+	AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
+	AF9015_REMOTE_MYGICTV_U718,
+};
+
+/* Leadtek WinFast DTV Dongle Gold */
+static struct dvb_usb_rc_key af9015_rc_keys_leadtek[] = {
+	{ 0x00, 0x1e, KEY_1 },
+	{ 0x00, 0x1f, KEY_2 },
+	{ 0x00, 0x20, KEY_3 },
+	{ 0x00, 0x21, KEY_4 },
+	{ 0x00, 0x22, KEY_5 },
+	{ 0x00, 0x23, KEY_6 },
+	{ 0x00, 0x24, KEY_7 },
+	{ 0x00, 0x25, KEY_8 },
+	{ 0x00, 0x26, KEY_9 },
+	{ 0x00, 0x27, KEY_0 },
+	{ 0x00, 0x28, KEY_ENTER },
+	{ 0x00, 0x4f, KEY_VOLUMEUP },
+	{ 0x00, 0x50, KEY_VOLUMEDOWN },
+	{ 0x00, 0x51, KEY_CHANNELDOWN },
+	{ 0x00, 0x52, KEY_CHANNELUP },
+};
+
+static u8 af9015_ir_table_leadtek[] = {
+	0x03, 0xfc, 0x00, 0xff, 0x1a, 0x01, 0x00,
+	0x03, 0xfc, 0x56, 0xa9, 0x00, 0x00, 0x00,
+	0x03, 0xfc, 0x4b, 0xb4, 0x00, 0x00, 0x00,
+	0x03, 0xfc, 0x4c, 0xb3, 0xb2, 0x04, 0x00,
+	0x03, 0xfc, 0x4d, 0xb2, 0x00, 0x00, 0x00,
+	0x03, 0xfc, 0x4e, 0xb1, 0x00, 0x00, 0x00,
+	0x03, 0xfc, 0x1f, 0xe0, 0x3d, 0x00, 0x00,
+	0x03, 0xfc, 0x40, 0xbf, 0x13, 0x01, 0x00,
+	0x03, 0xfc, 0x14, 0xeb, 0x10, 0x00, 0x00,
+	0x03, 0xfc, 0x49, 0xb6, 0x05, 0x01, 0x00,
+	0x03, 0xfc, 0x50, 0xaf, 0x29, 0x00, 0x00,
+	0x03, 0xfc, 0x0c, 0xf3, 0x52, 0x00, 0x00,
+	0x03, 0xfc, 0x03, 0xfc, 0x09, 0x00, 0x00,
+	0x03, 0xfc, 0x08, 0xf7, 0x50, 0x00, 0x00,
+	0x03, 0xfc, 0x13, 0xec, 0x28, 0x00, 0x00,
+	0x03, 0xfc, 0x04, 0xfb, 0x4f, 0x00, 0x00,
+	0x03, 0xfc, 0x4f, 0xb0, 0x0f, 0x01, 0x00,
+	0x03, 0xfc, 0x10, 0xef, 0x51, 0x00, 0x00,
+	0x03, 0xfc, 0x51, 0xae, 0x3f, 0x00, 0x00,
+	0x03, 0xfc, 0x42, 0xbd, 0x13, 0x00, 0x00,
+	0x03, 0xfc, 0x43, 0xbc, 0x00, 0x00, 0x00,
+	0x03, 0xfc, 0x44, 0xbb, 0x11, 0x00, 0x00,
+	0x03, 0xfc, 0x52, 0xad, 0x19, 0x00, 0x00,
+	0x03, 0xfc, 0x54, 0xab, 0x05, 0x00, 0x00,
+	0x03, 0xfc, 0x46, 0xb9, 0x29, 0x00, 0x00,
+	0x03, 0xfc, 0x55, 0xaa, 0x2b, 0x00, 0x00,
+	0x03, 0xfc, 0x53, 0xac, 0x41, 0x00, 0x00,
+	0x03, 0xfc, 0x05, 0xfa, 0x1e, 0x00, 0x00,
+	0x03, 0xfc, 0x06, 0xf9, 0x1f, 0x00, 0x00,
+	0x03, 0xfc, 0x07, 0xf8, 0x20, 0x00, 0x00,
+	0x03, 0xfc, 0x1e, 0xe1, 0x19, 0x00, 0x00,
+	0x03, 0xfc, 0x09, 0xf6, 0x21, 0x00, 0x00,
+	0x03, 0xfc, 0x0a, 0xf5, 0x22, 0x00, 0x00,
+	0x03, 0xfc, 0x0b, 0xf4, 0x23, 0x00, 0x00,
+	0x03, 0xfc, 0x1b, 0xe4, 0x16, 0x00, 0x00,
+	0x03, 0xfc, 0x0d, 0xf2, 0x24, 0x00, 0x00,
+	0x03, 0xfc, 0x0e, 0xf1, 0x25, 0x00, 0x00,
+	0x03, 0xfc, 0x0f, 0xf0, 0x26, 0x00, 0x00,
+	0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00,
+	0x03, 0xfc, 0x41, 0xbe, 0x37, 0x00, 0x00,
+	0x03, 0xfc, 0x12, 0xed, 0x27, 0x00, 0x00,
+	0x03, 0xfc, 0x11, 0xee, 0x2a, 0x00, 0x00,
+	0x03, 0xfc, 0x48, 0xb7, 0x2c, 0x00, 0x00,
+	0x03, 0xfc, 0x4a, 0xb5, 0x3c, 0x00, 0x00,
+	0x03, 0xfc, 0x47, 0xb8, 0x15, 0x01, 0x00,
+	0x03, 0xfc, 0x45, 0xba, 0x0b, 0x01, 0x00,
+	0x03, 0xfc, 0x5e, 0xa1, 0x43, 0x00, 0x00,
+	0x03, 0xfc, 0x5a, 0xa5, 0x42, 0x00, 0x00,
+	0x03, 0xfc, 0x5b, 0xa4, 0x4b, 0x00, 0x00,
+	0x03, 0xfc, 0x5f, 0xa0, 0x4e, 0x00, 0x00,
+};
+
+/* TwinHan AzureWave AD-TU700(704J) */
+static struct dvb_usb_rc_key af9015_rc_keys_twinhan[] = {
+	{ 0x05, 0x3f, KEY_POWER },
+	{ 0x00, 0x19, KEY_FAVORITES },    /* Favorite List */
+	{ 0x00, 0x04, KEY_TEXT },         /* Teletext */
+	{ 0x00, 0x0e, KEY_POWER },
+	{ 0x00, 0x0e, KEY_INFO },         /* Preview */
+	{ 0x00, 0x08, KEY_EPG },          /* Info/EPG */
+	{ 0x00, 0x0f, KEY_LIST },         /* Record List */
+	{ 0x00, 0x1e, KEY_1 },
+	{ 0x00, 0x1f, KEY_2 },
+	{ 0x00, 0x20, KEY_3 },
+	{ 0x00, 0x21, KEY_4 },
+	{ 0x00, 0x22, KEY_5 },
+	{ 0x00, 0x23, KEY_6 },
+	{ 0x00, 0x24, KEY_7 },
+	{ 0x00, 0x25, KEY_8 },
+	{ 0x00, 0x26, KEY_9 },
+	{ 0x00, 0x27, KEY_0 },
+	{ 0x00, 0x29, KEY_CANCEL },       /* Cancel */
+	{ 0x00, 0x4c, KEY_CLEAR },        /* Clear */
+	{ 0x00, 0x2a, KEY_BACK },         /* Back */
+	{ 0x00, 0x2b, KEY_TAB },          /* Tab */
+	{ 0x00, 0x52, KEY_UP },           /* up arrow */
+	{ 0x00, 0x51, KEY_DOWN },         /* down arrow */
+	{ 0x00, 0x4f, KEY_RIGHT },        /* right arrow */
+	{ 0x00, 0x50, KEY_LEFT },         /* left arrow */
+	{ 0x00, 0x28, KEY_ENTER },        /* Enter / ok */
+	{ 0x02, 0x52, KEY_VOLUMEUP },
+	{ 0x02, 0x51, KEY_VOLUMEDOWN },
+	{ 0x00, 0x4e, KEY_CHANNELDOWN },
+	{ 0x00, 0x4b, KEY_CHANNELUP },
+	{ 0x00, 0x4a, KEY_RECORD },
+	{ 0x01, 0x11, KEY_PLAY },
+	{ 0x00, 0x17, KEY_PAUSE },
+	{ 0x00, 0x0c, KEY_REWIND },       /* FR << */
+	{ 0x00, 0x11, KEY_FASTFORWARD },  /* FF >> */
+	{ 0x01, 0x15, KEY_PREVIOUS },     /* Replay */
+	{ 0x01, 0x0e, KEY_NEXT },         /* Skip */
+	{ 0x00, 0x13, KEY_CAMERA },       /* Capture */
+	{ 0x01, 0x0f, KEY_LANGUAGE },     /* SAP */
+	{ 0x01, 0x13, KEY_TV2 },          /* PIP */
+	{ 0x00, 0x1d, KEY_ZOOM },         /* Full Screen */
+	{ 0x01, 0x17, KEY_SUBTITLE },     /* Subtitle / CC */
+	{ 0x00, 0x10, KEY_MUTE },
+	{ 0x01, 0x19, KEY_AUDIO },        /* L/R */ /* TODO better event */
+	{ 0x01, 0x16, KEY_SLEEP },        /* Hibernate */
+	{ 0x01, 0x16, KEY_SWITCHVIDEOMODE },
+					  /* A/V */ /* TODO does not work */
+	{ 0x00, 0x06, KEY_AGAIN },        /* Recall */
+	{ 0x01, 0x16, KEY_KPPLUS },       /* Zoom+ */ /* TODO does not work */
+	{ 0x01, 0x16, KEY_KPMINUS },      /* Zoom- */ /* TODO does not work */
+	{ 0x02, 0x15, KEY_RED },
+	{ 0x02, 0x0a, KEY_GREEN },
+	{ 0x02, 0x1c, KEY_YELLOW },
+	{ 0x02, 0x05, KEY_BLUE },
+};
+
+static u8 af9015_ir_table_twinhan[] = {
+	0x00, 0xff, 0x16, 0xe9, 0x3f, 0x05, 0x00,
+	0x00, 0xff, 0x07, 0xf8, 0x16, 0x01, 0x00,
+	0x00, 0xff, 0x14, 0xeb, 0x11, 0x01, 0x00,
+	0x00, 0xff, 0x1a, 0xe5, 0x4d, 0x00, 0x00,
+	0x00, 0xff, 0x4c, 0xb3, 0x17, 0x00, 0x00,
+	0x00, 0xff, 0x12, 0xed, 0x11, 0x00, 0x00,
+	0x00, 0xff, 0x40, 0xbf, 0x0c, 0x00, 0x00,
+	0x00, 0xff, 0x11, 0xee, 0x4a, 0x00, 0x00,
+	0x00, 0xff, 0x54, 0xab, 0x13, 0x00, 0x00,
+	0x00, 0xff, 0x41, 0xbe, 0x15, 0x01, 0x00,
+	0x00, 0xff, 0x42, 0xbd, 0x0e, 0x01, 0x00,
+	0x00, 0xff, 0x43, 0xbc, 0x17, 0x01, 0x00,
+	0x00, 0xff, 0x50, 0xaf, 0x0f, 0x01, 0x00,
+	0x00, 0xff, 0x4d, 0xb2, 0x1d, 0x00, 0x00,
+	0x00, 0xff, 0x47, 0xb8, 0x13, 0x01, 0x00,
+	0x00, 0xff, 0x05, 0xfa, 0x4b, 0x00, 0x00,
+	0x00, 0xff, 0x02, 0xfd, 0x4e, 0x00, 0x00,
+	0x00, 0xff, 0x0e, 0xf1, 0x06, 0x00, 0x00,
+	0x00, 0xff, 0x1e, 0xe1, 0x52, 0x02, 0x00,
+	0x00, 0xff, 0x0a, 0xf5, 0x51, 0x02, 0x00,
+	0x00, 0xff, 0x10, 0xef, 0x10, 0x00, 0x00,
+	0x00, 0xff, 0x49, 0xb6, 0x19, 0x01, 0x00,
+	0x00, 0xff, 0x15, 0xea, 0x27, 0x00, 0x00,
+	0x00, 0xff, 0x03, 0xfc, 0x1e, 0x00, 0x00,
+	0x00, 0xff, 0x01, 0xfe, 0x1f, 0x00, 0x00,
+	0x00, 0xff, 0x06, 0xf9, 0x20, 0x00, 0x00,
+	0x00, 0xff, 0x09, 0xf6, 0x21, 0x00, 0x00,
+	0x00, 0xff, 0x1d, 0xe2, 0x22, 0x00, 0x00,
+	0x00, 0xff, 0x1f, 0xe0, 0x23, 0x00, 0x00,
+	0x00, 0xff, 0x0d, 0xf2, 0x24, 0x00, 0x00,
+	0x00, 0xff, 0x19, 0xe6, 0x25, 0x00, 0x00,
+	0x00, 0xff, 0x1b, 0xe4, 0x26, 0x00, 0x00,
+	0x00, 0xff, 0x00, 0xff, 0x2b, 0x00, 0x00,
+	0x00, 0xff, 0x4a, 0xb5, 0x4c, 0x00, 0x00,
+	0x00, 0xff, 0x4b, 0xb4, 0x52, 0x00, 0x00,
+	0x00, 0xff, 0x51, 0xae, 0x51, 0x00, 0x00,
+	0x00, 0xff, 0x52, 0xad, 0x4f, 0x00, 0x00,
+	0x00, 0xff, 0x4e, 0xb1, 0x50, 0x00, 0x00,
+	0x00, 0xff, 0x0c, 0xf3, 0x29, 0x00, 0x00,
+	0x00, 0xff, 0x4f, 0xb0, 0x28, 0x00, 0x00,
+	0x00, 0xff, 0x13, 0xec, 0x2a, 0x00, 0x00,
+	0x00, 0xff, 0x17, 0xe8, 0x19, 0x00, 0x00,
+	0x00, 0xff, 0x04, 0xfb, 0x0f, 0x00, 0x00,
+	0x00, 0xff, 0x48, 0xb7, 0x0e, 0x00, 0x00,
+	0x00, 0xff, 0x0f, 0xf0, 0x04, 0x00, 0x00,
+	0x00, 0xff, 0x1c, 0xe3, 0x08, 0x00, 0x00,
+	0x00, 0xff, 0x18, 0xe7, 0x15, 0x02, 0x00,
+	0x00, 0xff, 0x53, 0xac, 0x0a, 0x02, 0x00,
+	0x00, 0xff, 0x5e, 0xa1, 0x1c, 0x02, 0x00,
+	0x00, 0xff, 0x5f, 0xa0, 0x05, 0x02, 0x00,
+};
+
+/* A-Link DTU(m) */
+static struct dvb_usb_rc_key af9015_rc_keys_a_link[] = {
+	{ 0x00, 0x1e, KEY_1 },
+	{ 0x00, 0x1f, KEY_2 },
+	{ 0x00, 0x20, KEY_3 },
+	{ 0x00, 0x21, KEY_4 },
+	{ 0x00, 0x22, KEY_5 },
+	{ 0x00, 0x23, KEY_6 },
+	{ 0x00, 0x24, KEY_7 },
+	{ 0x00, 0x25, KEY_8 },
+	{ 0x00, 0x26, KEY_9 },
+	{ 0x00, 0x27, KEY_0 },
+	{ 0x00, 0x2e, KEY_CHANNELUP },
+	{ 0x00, 0x2d, KEY_CHANNELDOWN },
+	{ 0x04, 0x28, KEY_ZOOM },
+	{ 0x00, 0x41, KEY_MUTE },
+	{ 0x00, 0x42, KEY_VOLUMEDOWN },
+	{ 0x00, 0x43, KEY_VOLUMEUP },
+	{ 0x00, 0x44, KEY_GOTO },         /* jump */
+	{ 0x05, 0x45, KEY_POWER },
+};
+
+static u8 af9015_ir_table_a_link[] = {
+	0x08, 0xf7, 0x12, 0xed, 0x45, 0x05, 0x00, /* power */
+	0x08, 0xf7, 0x1a, 0xe5, 0x41, 0x00, 0x00, /* mute */
+	0x08, 0xf7, 0x01, 0xfe, 0x1e, 0x00, 0x00, /* 1 */
+	0x08, 0xf7, 0x1c, 0xe3, 0x21, 0x00, 0x00, /* 4 */
+	0x08, 0xf7, 0x03, 0xfc, 0x24, 0x00, 0x00, /* 7 */
+	0x08, 0xf7, 0x05, 0xfa, 0x28, 0x04, 0x00, /* zoom */
+	0x08, 0xf7, 0x00, 0xff, 0x43, 0x00, 0x00, /* volume up */
+	0x08, 0xf7, 0x16, 0xe9, 0x42, 0x00, 0x00, /* volume down */
+	0x08, 0xf7, 0x0f, 0xf0, 0x1f, 0x00, 0x00, /* 2 */
+	0x08, 0xf7, 0x0d, 0xf2, 0x22, 0x00, 0x00, /* 5 */
+	0x08, 0xf7, 0x1b, 0xe4, 0x25, 0x00, 0x00, /* 8 */
+	0x08, 0xf7, 0x06, 0xf9, 0x27, 0x00, 0x00, /* 0 */
+	0x08, 0xf7, 0x14, 0xeb, 0x2e, 0x00, 0x00, /* channel up */
+	0x08, 0xf7, 0x1d, 0xe2, 0x2d, 0x00, 0x00, /* channel down */
+	0x08, 0xf7, 0x02, 0xfd, 0x20, 0x00, 0x00, /* 3 */
+	0x08, 0xf7, 0x18, 0xe7, 0x23, 0x00, 0x00, /* 6 */
+	0x08, 0xf7, 0x04, 0xfb, 0x26, 0x00, 0x00, /* 9 */
+	0x08, 0xf7, 0x07, 0xf8, 0x44, 0x00, 0x00, /* jump */
+};
+
+/* MSI DIGIVOX mini II V3.0 */
+static struct dvb_usb_rc_key af9015_rc_keys_msi[] = {
+	{ 0x00, 0x1e, KEY_1 },
+	{ 0x00, 0x1f, KEY_2 },
+	{ 0x00, 0x20, KEY_3 },
+	{ 0x00, 0x21, KEY_4 },
+	{ 0x00, 0x22, KEY_5 },
+	{ 0x00, 0x23, KEY_6 },
+	{ 0x00, 0x24, KEY_7 },
+	{ 0x00, 0x25, KEY_8 },
+	{ 0x00, 0x26, KEY_9 },
+	{ 0x00, 0x27, KEY_0 },
+	{ 0x03, 0x0f, KEY_CHANNELUP },
+	{ 0x03, 0x0e, KEY_CHANNELDOWN },
+	{ 0x00, 0x42, KEY_VOLUMEDOWN },
+	{ 0x00, 0x43, KEY_VOLUMEUP },
+	{ 0x05, 0x45, KEY_POWER },
+	{ 0x00, 0x52, KEY_UP },           /* up */
+	{ 0x00, 0x51, KEY_DOWN },         /* down */
+	{ 0x00, 0x28, KEY_ENTER },
+};
+
+static u8 af9015_ir_table_msi[] = {
+	0x03, 0xfc, 0x17, 0xe8, 0x45, 0x05, 0x00, /* power */
+	0x03, 0xfc, 0x0d, 0xf2, 0x51, 0x00, 0x00, /* down */
+	0x03, 0xfc, 0x03, 0xfc, 0x52, 0x00, 0x00, /* up */
+	0x03, 0xfc, 0x1a, 0xe5, 0x1e, 0x00, 0x00, /* 1 */
+	0x03, 0xfc, 0x02, 0xfd, 0x1f, 0x00, 0x00, /* 2 */
+	0x03, 0xfc, 0x04, 0xfb, 0x20, 0x00, 0x00, /* 3 */
+	0x03, 0xfc, 0x1c, 0xe3, 0x21, 0x00, 0x00, /* 4 */
+	0x03, 0xfc, 0x08, 0xf7, 0x22, 0x00, 0x00, /* 5 */
+	0x03, 0xfc, 0x1d, 0xe2, 0x23, 0x00, 0x00, /* 6 */
+	0x03, 0xfc, 0x11, 0xee, 0x24, 0x00, 0x00, /* 7 */
+	0x03, 0xfc, 0x0b, 0xf4, 0x25, 0x00, 0x00, /* 8 */
+	0x03, 0xfc, 0x10, 0xef, 0x26, 0x00, 0x00, /* 9 */
+	0x03, 0xfc, 0x09, 0xf6, 0x27, 0x00, 0x00, /* 0 */
+	0x03, 0xfc, 0x14, 0xeb, 0x43, 0x00, 0x00, /* volume up */
+	0x03, 0xfc, 0x1f, 0xe0, 0x42, 0x00, 0x00, /* volume down */
+	0x03, 0xfc, 0x15, 0xea, 0x0f, 0x03, 0x00, /* channel up */
+	0x03, 0xfc, 0x05, 0xfa, 0x0e, 0x03, 0x00, /* channel down */
+	0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00, /* enter */
+};
+
+/* MYGICTV U718 */
+static struct dvb_usb_rc_key af9015_rc_keys_mygictv[] = {
+	{ 0x00, 0x3d, KEY_SWITCHVIDEOMODE },
+					  /* TV / AV */
+	{ 0x05, 0x45, KEY_POWER },
+	{ 0x00, 0x1e, KEY_1 },
+	{ 0x00, 0x1f, KEY_2 },
+	{ 0x00, 0x20, KEY_3 },
+	{ 0x00, 0x21, KEY_4 },
+	{ 0x00, 0x22, KEY_5 },
+	{ 0x00, 0x23, KEY_6 },
+	{ 0x00, 0x24, KEY_7 },
+	{ 0x00, 0x25, KEY_8 },
+	{ 0x00, 0x26, KEY_9 },
+	{ 0x00, 0x27, KEY_0 },
+	{ 0x00, 0x41, KEY_MUTE },
+	{ 0x00, 0x2a, KEY_ESC },          /* Esc */
+	{ 0x00, 0x2e, KEY_CHANNELUP },
+	{ 0x00, 0x2d, KEY_CHANNELDOWN },
+	{ 0x00, 0x42, KEY_VOLUMEDOWN },
+	{ 0x00, 0x43, KEY_VOLUMEUP },
+	{ 0x00, 0x52, KEY_UP },           /* up arrow */
+	{ 0x00, 0x51, KEY_DOWN },         /* down arrow */
+	{ 0x00, 0x4f, KEY_RIGHT },        /* right arrow */
+	{ 0x00, 0x50, KEY_LEFT },         /* left arrow */
+	{ 0x00, 0x28, KEY_ENTER },        /* ok */
+	{ 0x01, 0x15, KEY_RECORD },
+	{ 0x03, 0x13, KEY_PLAY },
+	{ 0x01, 0x13, KEY_PAUSE },
+	{ 0x01, 0x16, KEY_STOP },
+	{ 0x03, 0x07, KEY_REWIND },       /* FR << */
+	{ 0x03, 0x09, KEY_FASTFORWARD },  /* FF >> */
+	{ 0x00, 0x3b, KEY_TIME },         /* TimeShift */
+	{ 0x00, 0x3e, KEY_CAMERA },       /* Snapshot */
+	{ 0x03, 0x16, KEY_CYCLEWINDOWS }, /* yellow, min / max */
+	{ 0x00, 0x00, KEY_ZOOM },         /* 'select' (?) */
+	{ 0x03, 0x16, KEY_SHUFFLE },      /* Shuffle */
+	{ 0x03, 0x45, KEY_POWER },
+};
+
+static u8 af9015_ir_table_mygictv[] = {
+	0x02, 0xbd, 0x0c, 0xf3, 0x3d, 0x00, 0x00, /* TV / AV */
+	0x02, 0xbd, 0x14, 0xeb, 0x45, 0x05, 0x00, /* power */
+	0x02, 0xbd, 0x00, 0xff, 0x1e, 0x00, 0x00, /* 1 */
+	0x02, 0xbd, 0x01, 0xfe, 0x1f, 0x00, 0x00, /* 2 */
+	0x02, 0xbd, 0x02, 0xfd, 0x20, 0x00, 0x00, /* 3 */
+	0x02, 0xbd, 0x03, 0xfc, 0x21, 0x00, 0x00, /* 4 */
+	0x02, 0xbd, 0x04, 0xfb, 0x22, 0x00, 0x00, /* 5 */
+	0x02, 0xbd, 0x05, 0xfa, 0x23, 0x00, 0x00, /* 6 */
+	0x02, 0xbd, 0x06, 0xf9, 0x24, 0x00, 0x00, /* 7 */
+	0x02, 0xbd, 0x07, 0xf8, 0x25, 0x00, 0x00, /* 8 */
+	0x02, 0xbd, 0x08, 0xf7, 0x26, 0x00, 0x00, /* 9 */
+	0x02, 0xbd, 0x09, 0xf6, 0x27, 0x00, 0x00, /* 0 */
+	0x02, 0xbd, 0x0a, 0xf5, 0x41, 0x00, 0x00, /* mute */
+	0x02, 0xbd, 0x1c, 0xe3, 0x2a, 0x00, 0x00, /* esc */
+	0x02, 0xbd, 0x1f, 0xe0, 0x43, 0x00, 0x00, /* volume up */
+	0x02, 0xbd, 0x12, 0xed, 0x52, 0x00, 0x00, /* up arrow */
+	0x02, 0xbd, 0x11, 0xee, 0x50, 0x00, 0x00, /* left arrow */
+	0x02, 0xbd, 0x15, 0xea, 0x28, 0x00, 0x00, /* ok */
+	0x02, 0xbd, 0x10, 0xef, 0x4f, 0x00, 0x00, /* right arrow */
+	0x02, 0xbd, 0x13, 0xec, 0x51, 0x00, 0x00, /* down arrow */
+	0x02, 0xbd, 0x0e, 0xf1, 0x42, 0x00, 0x00, /* volume down */
+	0x02, 0xbd, 0x19, 0xe6, 0x15, 0x01, 0x00, /* record */
+	0x02, 0xbd, 0x1e, 0xe1, 0x13, 0x03, 0x00, /* play */
+	0x02, 0xbd, 0x16, 0xe9, 0x16, 0x01, 0x00, /* stop */
+	0x02, 0xbd, 0x0b, 0xf4, 0x28, 0x04, 0x00, /* yellow, min / max */
+	0x02, 0xbd, 0x0f, 0xf0, 0x3b, 0x00, 0x00, /* time shift */
+	0x02, 0xbd, 0x18, 0xe7, 0x2e, 0x00, 0x00, /* channel up */
+	0x02, 0xbd, 0x1a, 0xe5, 0x2d, 0x00, 0x00, /* channel down */
+	0x02, 0xbd, 0x17, 0xe8, 0x3e, 0x00, 0x00, /* snapshot */
+	0x02, 0xbd, 0x40, 0xbf, 0x13, 0x01, 0x00, /* pause */
+	0x02, 0xbd, 0x41, 0xbe, 0x09, 0x03, 0x00, /* FF >> */
+	0x02, 0xbd, 0x42, 0xbd, 0x07, 0x03, 0x00, /* FR << */
+	0x02, 0xbd, 0x43, 0xbc, 0x00, 0x00, 0x00, /* 'select' (?) */
+	0x02, 0xbd, 0x44, 0xbb, 0x16, 0x03, 0x00, /* shuffle */
+	0x02, 0xbd, 0x45, 0xba, 0x45, 0x03, 0x00, /* power */
+};
+
+/* KWorld PlusTV Dual DVB-T Stick (DVB-T 399U) */
+static u8 af9015_ir_table_kworld[] = {
+	0x86, 0x6b, 0x0c, 0xf3, 0x2e, 0x07, 0x00,
+	0x86, 0x6b, 0x16, 0xe9, 0x2d, 0x07, 0x00,
+	0x86, 0x6b, 0x1d, 0xe2, 0x37, 0x07, 0x00,
+	0x86, 0x6b, 0x00, 0xff, 0x1e, 0x07, 0x00,
+	0x86, 0x6b, 0x01, 0xfe, 0x1f, 0x07, 0x00,
+	0x86, 0x6b, 0x02, 0xfd, 0x20, 0x07, 0x00,
+	0x86, 0x6b, 0x03, 0xfc, 0x21, 0x07, 0x00,
+	0x86, 0x6b, 0x04, 0xfb, 0x22, 0x07, 0x00,
+	0x86, 0x6b, 0x05, 0xfa, 0x23, 0x07, 0x00,
+	0x86, 0x6b, 0x06, 0xf9, 0x24, 0x07, 0x00,
+	0x86, 0x6b, 0x07, 0xf8, 0x25, 0x07, 0x00,
+	0x86, 0x6b, 0x08, 0xf7, 0x26, 0x07, 0x00,
+	0x86, 0x6b, 0x09, 0xf6, 0x4d, 0x07, 0x00,
+	0x86, 0x6b, 0x0a, 0xf5, 0x4e, 0x07, 0x00,
+	0x86, 0x6b, 0x14, 0xeb, 0x4f, 0x07, 0x00,
+	0x86, 0x6b, 0x1e, 0xe1, 0x50, 0x07, 0x00,
+	0x86, 0x6b, 0x17, 0xe8, 0x52, 0x07, 0x00,
+	0x86, 0x6b, 0x1f, 0xe0, 0x51, 0x07, 0x00,
+	0x86, 0x6b, 0x0e, 0xf1, 0x0b, 0x07, 0x00,
+	0x86, 0x6b, 0x20, 0xdf, 0x0c, 0x07, 0x00,
+	0x86, 0x6b, 0x42, 0xbd, 0x0d, 0x07, 0x00,
+	0x86, 0x6b, 0x0b, 0xf4, 0x0e, 0x07, 0x00,
+	0x86, 0x6b, 0x43, 0xbc, 0x0f, 0x07, 0x00,
+	0x86, 0x6b, 0x10, 0xef, 0x10, 0x07, 0x00,
+	0x86, 0x6b, 0x21, 0xde, 0x11, 0x07, 0x00,
+	0x86, 0x6b, 0x13, 0xec, 0x12, 0x07, 0x00,
+	0x86, 0x6b, 0x11, 0xee, 0x13, 0x07, 0x00,
+	0x86, 0x6b, 0x12, 0xed, 0x14, 0x07, 0x00,
+	0x86, 0x6b, 0x19, 0xe6, 0x15, 0x07, 0x00,
+	0x86, 0x6b, 0x1a, 0xe5, 0x16, 0x07, 0x00,
+	0x86, 0x6b, 0x1b, 0xe4, 0x17, 0x07, 0x00,
+	0x86, 0x6b, 0x4b, 0xb4, 0x18, 0x07, 0x00,
+	0x86, 0x6b, 0x40, 0xbf, 0x19, 0x07, 0x00,
+	0x86, 0x6b, 0x44, 0xbb, 0x1a, 0x07, 0x00,
+	0x86, 0x6b, 0x41, 0xbe, 0x1b, 0x07, 0x00,
+	0x86, 0x6b, 0x22, 0xdd, 0x1c, 0x07, 0x00,
+	0x86, 0x6b, 0x15, 0xea, 0x1d, 0x07, 0x00,
+	0x86, 0x6b, 0x0f, 0xf0, 0x3f, 0x07, 0x00,
+	0x86, 0x6b, 0x1c, 0xe3, 0x40, 0x07, 0x00,
+	0x86, 0x6b, 0x4a, 0xb5, 0x41, 0x07, 0x00,
+	0x86, 0x6b, 0x48, 0xb7, 0x42, 0x07, 0x00,
+	0x86, 0x6b, 0x49, 0xb6, 0x43, 0x07, 0x00,
+	0x86, 0x6b, 0x18, 0xe7, 0x44, 0x07, 0x00,
+	0x86, 0x6b, 0x23, 0xdc, 0x45, 0x07, 0x00,
+};
+
+#endif

+ 25 - 5
drivers/media/dvb/dvb-usb/anysee.c

@@ -41,6 +41,9 @@
 static int dvb_usb_anysee_debug;
 module_param_named(debug, dvb_usb_anysee_debug, int, 0644);
 MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
+int dvb_usb_anysee_delsys;
+module_param_named(delsys, dvb_usb_anysee_delsys, int, 0644);
+MODULE_PARM_DESC(delsys, "select delivery mode (0=DVB-C, 1=DVB-T)");
 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 
 static struct mutex anysee_usb_mutex;
@@ -178,14 +181,14 @@ static int anysee_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
 			inc = 1;
 		}
 		if (ret)
-			return ret;
+			break;
 
 		i += inc;
 	}
 
 	mutex_unlock(&d->i2c_mutex);
 
-	return i;
+	return ret ? ret : i;
 }
 
 static u32 anysee_i2c_func(struct i2c_adapter *adapter)
@@ -272,9 +275,11 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
 	      model      demod     hw  firmware
 	   1. E30        MT352     02  0.2.1
 	   2. E30        ZL10353   02  0.2.1
-	   3. E30 Plus   ZL10353   06  0.1.0
-	   4. E30C Plus  TDA10023  0a  0.1.0    rev 0.2
-	   4. E30C Plus  TDA10023  0f  0.1.2    rev 0.4
+	   3. E30 Combo  ZL10353   0f  0.1.2    DVB-T/C combo
+	   4. E30 Plus   ZL10353   06  0.1.0
+	   5. E30C Plus  TDA10023  0a  0.1.0    rev 0.2
+	      E30C Plus  TDA10023  0f  0.1.2    rev 0.4
+	      E30 Combo  TDA10023  0f  0.1.2    DVB-T/C combo
 	*/
 
 	/* Zarlink MT352 DVB-T demod inside of Samsung DNOS404ZH102A NIM */
@@ -293,6 +298,21 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
 		return 0;
 	}
 
+	/* for E30 Combo Plus DVB-T demodulator */
+	if (dvb_usb_anysee_delsys) {
+		ret = anysee_write_reg(adap->dev, 0xb0, 0x01);
+		if (ret)
+			return ret;
+
+		/* Zarlink ZL10353 DVB-T demod */
+		adap->fe = dvb_attach(zl10353_attach, &anysee_zl10353_config,
+				      &adap->dev->i2c_adap);
+		if (adap->fe != NULL) {
+			state->tuner = DVB_PLL_SAMSUNG_DTOS403IH102A;
+			return 0;
+		}
+	}
+
 	/* connect demod on IO port D for TDA10023 & ZL10353 */
 	ret = anysee_write_reg(adap->dev, 0xb0, 0x25);
 	if (ret)

+ 268 - 0
drivers/media/dvb/dvb-usb/cinergyT2-core.c

@@ -0,0 +1,268 @@
+/*
+ * TerraTec Cinergy T2/qanu USB2 DVB-T adapter.
+ *
+ * Copyright (C) 2007 Tomi Orava (tomimo@ncircle.nullnet.fi)
+ *
+ * Based on the dvb-usb-framework code and the
+ * original Terratec Cinergy T2 driver by:
+ *
+ * Copyright (C) 2004 Daniel Mack <daniel@qanu.de> and
+ *		    Holger Waechtler <holger@qanu.de>
+ *
+ *  Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf
+ *
+ * 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 "cinergyT2.h"
+
+
+/* debug */
+int dvb_usb_cinergyt2_debug;
+int disable_remote;
+
+module_param_named(debug, dvb_usb_cinergyt2_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info, xfer=2, rc=4 "
+		"(or-able)).");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+struct cinergyt2_state {
+	u8 rc_counter;
+};
+
+/* We are missing a release hook with usb_device data */
+struct dvb_usb_device *cinergyt2_usb_device;
+
+static struct dvb_usb_device_properties cinergyt2_properties;
+
+static int cinergyt2_streaming_ctrl(struct dvb_usb_adapter *adap, int enable)
+{
+	char buf[] = { CINERGYT2_EP1_CONTROL_STREAM_TRANSFER, enable ? 1 : 0 };
+	char result[64];
+	return dvb_usb_generic_rw(adap->dev, buf, sizeof(buf), result,
+				sizeof(result), 0);
+}
+
+static int cinergyt2_power_ctrl(struct dvb_usb_device *d, int enable)
+{
+	char buf[] = { CINERGYT2_EP1_SLEEP_MODE, enable ? 0 : 1 };
+	char state[3];
+	return dvb_usb_generic_rw(d, buf, sizeof(buf), state, sizeof(state), 0);
+}
+
+static int cinergyt2_frontend_attach(struct dvb_usb_adapter *adap)
+{
+	char query[] = { CINERGYT2_EP1_GET_FIRMWARE_VERSION };
+	char state[3];
+	int ret;
+
+	adap->fe = cinergyt2_fe_attach(adap->dev);
+
+	ret = dvb_usb_generic_rw(adap->dev, query, sizeof(query), state,
+				sizeof(state), 0);
+	if (ret < 0) {
+		deb_rc("cinergyt2_power_ctrl() Failed to retrieve sleep "
+			"state info\n");
+	}
+
+	/* Copy this pointer as we are gonna need it in the release phase */
+	cinergyt2_usb_device = adap->dev;
+
+	return 0;
+}
+
+static struct dvb_usb_rc_key cinergyt2_rc_keys[] = {
+	{ 0x04,	0x01,	KEY_POWER },
+	{ 0x04,	0x02,	KEY_1 },
+	{ 0x04,	0x03,	KEY_2 },
+	{ 0x04,	0x04,	KEY_3 },
+	{ 0x04,	0x05,	KEY_4 },
+	{ 0x04,	0x06,	KEY_5 },
+	{ 0x04,	0x07,	KEY_6 },
+	{ 0x04,	0x08,	KEY_7 },
+	{ 0x04,	0x09,	KEY_8 },
+	{ 0x04,	0x0a,	KEY_9 },
+	{ 0x04,	0x0c,	KEY_0 },
+	{ 0x04,	0x0b,	KEY_VIDEO },
+	{ 0x04,	0x0d,	KEY_REFRESH },
+	{ 0x04,	0x0e,	KEY_SELECT },
+	{ 0x04,	0x0f,	KEY_EPG },
+	{ 0x04,	0x10,	KEY_UP },
+	{ 0x04,	0x14,	KEY_DOWN },
+	{ 0x04,	0x11,	KEY_LEFT },
+	{ 0x04,	0x13,	KEY_RIGHT },
+	{ 0x04,	0x12,	KEY_OK },
+	{ 0x04,	0x15,	KEY_TEXT },
+	{ 0x04,	0x16,	KEY_INFO },
+	{ 0x04,	0x17,	KEY_RED },
+	{ 0x04,	0x18,	KEY_GREEN },
+	{ 0x04,	0x19,	KEY_YELLOW },
+	{ 0x04,	0x1a,	KEY_BLUE },
+	{ 0x04,	0x1c,	KEY_VOLUMEUP },
+	{ 0x04,	0x1e,	KEY_VOLUMEDOWN },
+	{ 0x04,	0x1d,	KEY_MUTE },
+	{ 0x04,	0x1b,	KEY_CHANNELUP },
+	{ 0x04,	0x1f,	KEY_CHANNELDOWN },
+	{ 0x04,	0x40,	KEY_PAUSE },
+	{ 0x04,	0x4c,	KEY_PLAY },
+	{ 0x04,	0x58,	KEY_RECORD },
+	{ 0x04,	0x54,	KEY_PREVIOUS },
+	{ 0x04,	0x48,	KEY_STOP },
+	{ 0x04,	0x5c,	KEY_NEXT }
+};
+
+/* Number of keypresses to ignore before detect repeating */
+#define RC_REPEAT_DELAY 3
+
+static int repeatable_keys[] = {
+	KEY_UP,
+	KEY_DOWN,
+	KEY_LEFT,
+	KEY_RIGHT,
+	KEY_VOLUMEUP,
+	KEY_VOLUMEDOWN,
+	KEY_CHANNELUP,
+	KEY_CHANNELDOWN
+};
+
+static int cinergyt2_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
+{
+	struct cinergyt2_state *st = d->priv;
+	u8 key[5] = {0, 0, 0, 0, 0}, cmd = CINERGYT2_EP1_GET_RC_EVENTS;
+	int i;
+
+	*state = REMOTE_NO_KEY_PRESSED;
+
+	dvb_usb_generic_rw(d, &cmd, 1, key, sizeof(key), 0);
+	if (key[4] == 0xff) {
+		/* key repeat */
+		st->rc_counter++;
+		if (st->rc_counter > RC_REPEAT_DELAY) {
+			for (i = 0; i < ARRAY_SIZE(repeatable_keys); i++) {
+				if (d->last_event == repeatable_keys[i]) {
+					*state = REMOTE_KEY_REPEAT;
+					*event = d->last_event;
+					deb_rc("repeat key, event %x\n",
+						   *event);
+					return 0;
+				}
+			}
+			deb_rc("repeated key (non repeatable)\n");
+		}
+		return 0;
+	}
+
+	/* hack to pass checksum on the custom field */
+	key[2] = ~key[1];
+	dvb_usb_nec_rc_key_to_event(d, key, event, state);
+	if (key[0] != 0) {
+		if (*event != d->last_event)
+			st->rc_counter = 0;
+
+		deb_rc("key: %x %x %x %x %x\n",
+		       key[0], key[1], key[2], key[3], key[4]);
+	}
+	return 0;
+}
+
+static int cinergyt2_usb_probe(struct usb_interface *intf,
+				const struct usb_device_id *id)
+{
+	return dvb_usb_device_init(intf, &cinergyt2_properties,
+					THIS_MODULE, NULL, adapter_nr);
+}
+
+
+static struct usb_device_id cinergyt2_usb_table[] = {
+	{ USB_DEVICE(USB_VID_TERRATEC, 0x0038) },
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(usb, cinergyt2_usb_table);
+
+static struct dvb_usb_device_properties cinergyt2_properties = {
+	.size_of_priv = sizeof(struct cinergyt2_state),
+	.num_adapters = 1,
+	.adapter = {
+		{
+			.streaming_ctrl   = cinergyt2_streaming_ctrl,
+			.frontend_attach  = cinergyt2_frontend_attach,
+
+			/* parameter for the MPEG2-data transfer */
+			.stream = {
+				.type = USB_BULK,
+				.count = 5,
+				.endpoint = 0x02,
+				.u = {
+					.bulk = {
+						.buffersize = 512,
+					}
+				}
+			},
+		}
+	},
+
+	.power_ctrl       = cinergyt2_power_ctrl,
+
+	.rc_interval      = 50,
+	.rc_key_map       = cinergyt2_rc_keys,
+	.rc_key_map_size  = ARRAY_SIZE(cinergyt2_rc_keys),
+	.rc_query         = cinergyt2_rc_query,
+
+	.generic_bulk_ctrl_endpoint = 1,
+
+	.num_device_descs = 1,
+	.devices = {
+		{ .name = "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver",
+		  .cold_ids = {NULL},
+		  .warm_ids = { &cinergyt2_usb_table[0], NULL },
+		},
+		{ NULL },
+	}
+};
+
+
+static struct usb_driver cinergyt2_driver = {
+	.name		= "cinergyT2",
+	.probe		= cinergyt2_usb_probe,
+	.disconnect	= dvb_usb_device_exit,
+	.id_table	= cinergyt2_usb_table
+};
+
+static int __init cinergyt2_usb_init(void)
+{
+	int err;
+
+	err = usb_register(&cinergyt2_driver);
+	if (err) {
+		err("usb_register() failed! (err %i)\n", err);
+		return err;
+	}
+	return 0;
+}
+
+static void __exit cinergyt2_usb_exit(void)
+{
+	usb_deregister(&cinergyt2_driver);
+}
+
+module_init(cinergyt2_usb_init);
+module_exit(cinergyt2_usb_exit);
+
+MODULE_DESCRIPTION("Terratec Cinergy T2 DVB-T driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tomi Orava");

+ 351 - 0
drivers/media/dvb/dvb-usb/cinergyT2-fe.c

@@ -0,0 +1,351 @@
+/*
+ * TerraTec Cinergy T2/qanu USB2 DVB-T adapter.
+ *
+ * Copyright (C) 2007 Tomi Orava (tomimo@ncircle.nullnet.fi)
+ *
+ * Based on the dvb-usb-framework code and the
+ * original Terratec Cinergy T2 driver by:
+ *
+ * Copyright (C) 2004 Daniel Mack <daniel@qanu.de> and
+ *                  Holger Waechtler <holger@qanu.de>
+ *
+ *  Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf
+ *
+ * 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 "cinergyT2.h"
+
+
+/**
+ *  convert linux-dvb frontend parameter set into TPS.
+ *  See ETSI ETS-300744, section 4.6.2, table 9 for details.
+ *
+ *  This function is probably reusable and may better get placed in a support
+ *  library.
+ *
+ *  We replace errornous fields by default TPS fields (the ones with value 0).
+ */
+
+static uint16_t compute_tps(struct dvb_frontend_parameters *p)
+{
+	struct dvb_ofdm_parameters *op = &p->u.ofdm;
+	uint16_t tps = 0;
+
+	switch (op->code_rate_HP) {
+	case FEC_2_3:
+		tps |= (1 << 7);
+		break;
+	case FEC_3_4:
+		tps |= (2 << 7);
+		break;
+	case FEC_5_6:
+		tps |= (3 << 7);
+		break;
+	case FEC_7_8:
+		tps |= (4 << 7);
+		break;
+	case FEC_1_2:
+	case FEC_AUTO:
+	default:
+		/* tps |= (0 << 7) */;
+	}
+
+	switch (op->code_rate_LP) {
+	case FEC_2_3:
+		tps |= (1 << 4);
+		break;
+	case FEC_3_4:
+		tps |= (2 << 4);
+		break;
+	case FEC_5_6:
+		tps |= (3 << 4);
+		break;
+	case FEC_7_8:
+		tps |= (4 << 4);
+		break;
+	case FEC_1_2:
+	case FEC_AUTO:
+	default:
+		/* tps |= (0 << 4) */;
+	}
+
+	switch (op->constellation) {
+	case QAM_16:
+		tps |= (1 << 13);
+		break;
+	case QAM_64:
+		tps |= (2 << 13);
+		break;
+	case QPSK:
+	default:
+		/* tps |= (0 << 13) */;
+	}
+
+	switch (op->transmission_mode) {
+	case TRANSMISSION_MODE_8K:
+		tps |= (1 << 0);
+		break;
+	case TRANSMISSION_MODE_2K:
+	default:
+		/* tps |= (0 << 0) */;
+	}
+
+	switch (op->guard_interval) {
+	case GUARD_INTERVAL_1_16:
+		tps |= (1 << 2);
+		break;
+	case GUARD_INTERVAL_1_8:
+		tps |= (2 << 2);
+		break;
+	case GUARD_INTERVAL_1_4:
+		tps |= (3 << 2);
+		break;
+	case GUARD_INTERVAL_1_32:
+	default:
+		/* tps |= (0 << 2) */;
+	}
+
+	switch (op->hierarchy_information) {
+	case HIERARCHY_1:
+		tps |= (1 << 10);
+		break;
+	case HIERARCHY_2:
+		tps |= (2 << 10);
+		break;
+	case HIERARCHY_4:
+		tps |= (3 << 10);
+		break;
+	case HIERARCHY_NONE:
+	default:
+		/* tps |= (0 << 10) */;
+	}
+
+	return tps;
+}
+
+struct cinergyt2_fe_state {
+	struct dvb_frontend fe;
+	struct dvb_usb_device *d;
+};
+
+static int cinergyt2_fe_read_status(struct dvb_frontend *fe,
+					fe_status_t *status)
+{
+	struct cinergyt2_fe_state *state = fe->demodulator_priv;
+	struct dvbt_get_status_msg result;
+	u8 cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS };
+	int ret;
+
+	ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (u8 *)&result,
+			sizeof(result), 0);
+	if (ret < 0)
+		return ret;
+
+	*status = 0;
+
+	if (0xffff - le16_to_cpu(result.gain) > 30)
+		*status |= FE_HAS_SIGNAL;
+	if (result.lock_bits & (1 << 6))
+		*status |= FE_HAS_LOCK;
+	if (result.lock_bits & (1 << 5))
+		*status |= FE_HAS_SYNC;
+	if (result.lock_bits & (1 << 4))
+		*status |= FE_HAS_CARRIER;
+	if (result.lock_bits & (1 << 1))
+		*status |= FE_HAS_VITERBI;
+
+	if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) !=
+			(FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC))
+		*status &= ~FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int cinergyt2_fe_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+	struct cinergyt2_fe_state *state = fe->demodulator_priv;
+	struct dvbt_get_status_msg status;
+	char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS };
+	int ret;
+
+	ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status,
+				sizeof(status), 0);
+	if (ret < 0)
+		return ret;
+
+	*ber = le32_to_cpu(status.viterbi_error_rate);
+	return 0;
+}
+
+static int cinergyt2_fe_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
+{
+	struct cinergyt2_fe_state *state = fe->demodulator_priv;
+	struct dvbt_get_status_msg status;
+	u8 cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS };
+	int ret;
+
+	ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (u8 *)&status,
+				sizeof(status), 0);
+	if (ret < 0) {
+		err("cinergyt2_fe_read_unc_blocks() Failed! (Error=%d)\n",
+			ret);
+		return ret;
+	}
+	*unc = le32_to_cpu(status.uncorrected_block_count);
+	return 0;
+}
+
+static int cinergyt2_fe_read_signal_strength(struct dvb_frontend *fe,
+						u16 *strength)
+{
+	struct cinergyt2_fe_state *state = fe->demodulator_priv;
+	struct dvbt_get_status_msg status;
+	char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS };
+	int ret;
+
+	ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status,
+				sizeof(status), 0);
+	if (ret < 0) {
+		err("cinergyt2_fe_read_signal_strength() Failed!"
+			" (Error=%d)\n", ret);
+		return ret;
+	}
+	*strength = (0xffff - le16_to_cpu(status.gain));
+	return 0;
+}
+
+static int cinergyt2_fe_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+	struct cinergyt2_fe_state *state = fe->demodulator_priv;
+	struct dvbt_get_status_msg status;
+	char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS };
+	int ret;
+
+	ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status,
+				sizeof(status), 0);
+	if (ret < 0) {
+		err("cinergyt2_fe_read_snr() Failed! (Error=%d)\n", ret);
+		return ret;
+	}
+	*snr = (status.snr << 8) | status.snr;
+	return 0;
+}
+
+static int cinergyt2_fe_init(struct dvb_frontend *fe)
+{
+	return 0;
+}
+
+static int cinergyt2_fe_sleep(struct dvb_frontend *fe)
+{
+	deb_info("cinergyt2_fe_sleep() Called\n");
+	return 0;
+}
+
+static int cinergyt2_fe_get_tune_settings(struct dvb_frontend *fe,
+				struct dvb_frontend_tune_settings *tune)
+{
+	tune->min_delay_ms = 800;
+	return 0;
+}
+
+static int cinergyt2_fe_set_frontend(struct dvb_frontend *fe,
+				  struct dvb_frontend_parameters *fep)
+{
+	struct cinergyt2_fe_state *state = fe->demodulator_priv;
+	struct dvbt_set_parameters_msg param;
+	char result[2];
+	int err;
+
+	param.cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS;
+	param.tps = cpu_to_le16(compute_tps(fep));
+	param.freq = cpu_to_le32(fep->frequency / 1000);
+	param.bandwidth = 8 - fep->u.ofdm.bandwidth - BANDWIDTH_8_MHZ;
+
+	err = dvb_usb_generic_rw(state->d,
+			(char *)&param, sizeof(param),
+			result, sizeof(result), 0);
+	if (err < 0)
+		err("cinergyt2_fe_set_frontend() Failed! err=%d\n", err);
+
+	return (err < 0) ? err : 0;
+}
+
+static int cinergyt2_fe_get_frontend(struct dvb_frontend *fe,
+				  struct dvb_frontend_parameters *fep)
+{
+	return 0;
+}
+
+static void cinergyt2_fe_release(struct dvb_frontend *fe)
+{
+	struct cinergyt2_fe_state *state = fe->demodulator_priv;
+	if (state != NULL)
+		kfree(state);
+}
+
+static struct dvb_frontend_ops cinergyt2_fe_ops;
+
+struct dvb_frontend *cinergyt2_fe_attach(struct dvb_usb_device *d)
+{
+	struct cinergyt2_fe_state *s = kzalloc(sizeof(
+					struct cinergyt2_fe_state), GFP_KERNEL);
+	if (s == NULL)
+		return NULL;
+
+	s->d = d;
+	memcpy(&s->fe.ops, &cinergyt2_fe_ops, sizeof(struct dvb_frontend_ops));
+	s->fe.demodulator_priv = s;
+	return &s->fe;
+}
+
+
+static struct dvb_frontend_ops cinergyt2_fe_ops = {
+	.info = {
+		.name			= DRIVER_NAME,
+		.type			= FE_OFDM,
+		.frequency_min		= 174000000,
+		.frequency_max		= 862000000,
+		.frequency_stepsize	= 166667,
+		.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_HIERARCHY_AUTO
+			| FE_CAN_RECOVER
+			| FE_CAN_MUTE_TS
+	},
+
+	.release		= cinergyt2_fe_release,
+
+	.init			= cinergyt2_fe_init,
+	.sleep			= cinergyt2_fe_sleep,
+
+	.set_frontend		= cinergyt2_fe_set_frontend,
+	.get_frontend		= cinergyt2_fe_get_frontend,
+	.get_tune_settings	= cinergyt2_fe_get_tune_settings,
+
+	.read_status		= cinergyt2_fe_read_status,
+	.read_ber		= cinergyt2_fe_read_ber,
+	.read_signal_strength	= cinergyt2_fe_read_signal_strength,
+	.read_snr		= cinergyt2_fe_read_snr,
+	.read_ucblocks		= cinergyt2_fe_read_unc_blocks,
+};

+ 95 - 0
drivers/media/dvb/dvb-usb/cinergyT2.h

@@ -0,0 +1,95 @@
+/*
+ * TerraTec Cinergy T2/qanu USB2 DVB-T adapter.
+ *
+ * Copyright (C) 2007 Tomi Orava (tomimo@ncircle.nullnet.fi)
+ *
+ * Based on the dvb-usb-framework code and the
+ * original Terratec Cinergy T2 driver by:
+ *
+ * Copyright (C) 2004 Daniel Mack <daniel@qanu.de> and
+ *                  Holger Waechtler <holger@qanu.de>
+ *
+ *  Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf
+ *
+ * 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 _DVB_USB_CINERGYT2_H_
+#define _DVB_USB_CINERGYT2_H_
+
+#include <linux/usb/input.h>
+
+#define DVB_USB_LOG_PREFIX "cinergyT2"
+#include "dvb-usb.h"
+
+#define DRIVER_NAME "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver"
+
+extern int dvb_usb_cinergyt2_debug;
+
+#define deb_info(args...)  dprintk(dvb_usb_cinergyt2_debug,  0x001, args)
+#define deb_xfer(args...)  dprintk(dvb_usb_cinergyt2_debug,  0x002, args)
+#define deb_pll(args...)   dprintk(dvb_usb_cinergyt2_debug,  0x004, args)
+#define deb_ts(args...)    dprintk(dvb_usb_cinergyt2_debug,  0x008, args)
+#define deb_err(args...)   dprintk(dvb_usb_cinergyt2_debug,  0x010, args)
+#define deb_rc(args...)    dprintk(dvb_usb_cinergyt2_debug,  0x020, args)
+#define deb_fw(args...)    dprintk(dvb_usb_cinergyt2_debug,  0x040, args)
+#define deb_mem(args...)   dprintk(dvb_usb_cinergyt2_debug,  0x080, args)
+#define deb_uxfer(args...) dprintk(dvb_usb_cinergyt2_debug,  0x100, args)
+
+
+
+enum cinergyt2_ep1_cmd {
+	CINERGYT2_EP1_PID_TABLE_RESET		= 0x01,
+	CINERGYT2_EP1_PID_SETUP			= 0x02,
+	CINERGYT2_EP1_CONTROL_STREAM_TRANSFER	= 0x03,
+	CINERGYT2_EP1_SET_TUNER_PARAMETERS	= 0x04,
+	CINERGYT2_EP1_GET_TUNER_STATUS		= 0x05,
+	CINERGYT2_EP1_START_SCAN		= 0x06,
+	CINERGYT2_EP1_CONTINUE_SCAN		= 0x07,
+	CINERGYT2_EP1_GET_RC_EVENTS		= 0x08,
+	CINERGYT2_EP1_SLEEP_MODE		= 0x09,
+	CINERGYT2_EP1_GET_FIRMWARE_VERSION	= 0x0A
+};
+
+
+struct dvbt_get_status_msg {
+	uint32_t freq;
+	uint8_t bandwidth;
+	uint16_t tps;
+	uint8_t flags;
+	uint16_t gain;
+	uint8_t snr;
+	uint32_t viterbi_error_rate;
+	uint32_t rs_error_rate;
+	uint32_t uncorrected_block_count;
+	uint8_t lock_bits;
+	uint8_t prev_lock_bits;
+} __attribute__((packed));
+
+
+struct dvbt_set_parameters_msg {
+	uint8_t cmd;
+	uint32_t freq;
+	uint8_t bandwidth;
+	uint16_t tps;
+	uint8_t flags;
+} __attribute__((packed));
+
+
+extern struct dvb_frontend *cinergyt2_fe_attach(struct dvb_usb_device *d);
+
+#endif /* _DVB_USB_CINERGYT2_H_ */
+

+ 501 - 3
drivers/media/dvb/dvb-usb/cxusb.c

@@ -36,6 +36,9 @@
 #include "tuner-xc2028.h"
 #include "tuner-simple.h"
 #include "mxl5005s.h"
+#include "dib7000p.h"
+#include "dib0070.h"
+#include "lgs8gl5.h"
 
 /* debug */
 static int dvb_usb_cxusb_debug;
@@ -109,6 +112,25 @@ static void cxusb_nano2_led(struct dvb_usb_device *d, int onoff)
 	cxusb_bluebird_gpio_rw(d, 0x40, onoff ? 0 : 0x40);
 }
 
+static int cxusb_d680_dmb_gpio_tuner(struct dvb_usb_device *d,
+		u8 addr, int onoff)
+{
+	u8  o[2] = {addr, onoff};
+	u8  i;
+	int rc;
+
+	rc = cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1);
+
+	if (rc < 0)
+		return rc;
+	if (i == 0x01)
+		return 0;
+	else {
+		deb_info("gpio_write failed.\n");
+		return -EIO;
+	}
+}
+
 /* I2C */
 static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
 			  int num)
@@ -262,6 +284,20 @@ static int cxusb_nano2_power_ctrl(struct dvb_usb_device *d, int onoff)
 	return rc;
 }
 
+static int cxusb_d680_dmb_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+	int ret;
+	u8  b;
+	ret = cxusb_power_ctrl(d, onoff);
+	if (!onoff)
+		return ret;
+
+	msleep(128);
+	cxusb_ctrl_msg(d, CMD_DIGITAL, NULL, 0, &b, 1);
+	msleep(100);
+	return ret;
+}
+
 static int cxusb_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
 {
 	u8 buf[2] = { 0x03, 0x00 };
@@ -283,6 +319,67 @@ static int cxusb_aver_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
 	return 0;
 }
 
+static void cxusb_d680_dmb_drain_message(struct dvb_usb_device *d)
+{
+	int       ep = d->props.generic_bulk_ctrl_endpoint;
+	const int timeout = 100;
+	const int junk_len = 32;
+	u8        *junk;
+	int       rd_count;
+
+	/* Discard remaining data in video pipe */
+	junk = kmalloc(junk_len, GFP_KERNEL);
+	if (!junk)
+		return;
+	while (1) {
+		if (usb_bulk_msg(d->udev,
+			usb_rcvbulkpipe(d->udev, ep),
+			junk, junk_len, &rd_count, timeout) < 0)
+			break;
+		if (!rd_count)
+			break;
+	}
+	kfree(junk);
+}
+
+static void cxusb_d680_dmb_drain_video(struct dvb_usb_device *d)
+{
+	struct usb_data_stream_properties *p = &d->props.adapter[0].stream;
+	const int timeout = 100;
+	const int junk_len = p->u.bulk.buffersize;
+	u8        *junk;
+	int       rd_count;
+
+	/* Discard remaining data in video pipe */
+	junk = kmalloc(junk_len, GFP_KERNEL);
+	if (!junk)
+		return;
+	while (1) {
+		if (usb_bulk_msg(d->udev,
+			usb_rcvbulkpipe(d->udev, p->endpoint),
+			junk, junk_len, &rd_count, timeout) < 0)
+			break;
+		if (!rd_count)
+			break;
+	}
+	kfree(junk);
+}
+
+static int cxusb_d680_dmb_streaming_ctrl(
+		struct dvb_usb_adapter *adap, int onoff)
+{
+	if (onoff) {
+		u8 buf[2] = { 0x03, 0x00 };
+		cxusb_d680_dmb_drain_video(adap->dev);
+		return cxusb_ctrl_msg(adap->dev, CMD_STREAMING_ON,
+			buf, sizeof(buf), NULL, 0);
+	} else {
+		int ret = cxusb_ctrl_msg(adap->dev,
+			CMD_STREAMING_OFF, NULL, 0, NULL, 0);
+		return ret;
+	}
+}
+
 static int cxusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
 {
 	struct dvb_usb_rc_key *keymap = d->props.rc_key_map;
@@ -335,6 +432,32 @@ static int cxusb_bluebird2_rc_query(struct dvb_usb_device *d, u32 *event,
 	return 0;
 }
 
+static int cxusb_d680_dmb_rc_query(struct dvb_usb_device *d, u32 *event,
+		int *state)
+{
+	struct dvb_usb_rc_key *keymap = d->props.rc_key_map;
+	u8 ircode[2];
+	int i;
+
+	*event = 0;
+	*state = REMOTE_NO_KEY_PRESSED;
+
+	if (cxusb_ctrl_msg(d, 0x10, NULL, 0, ircode, 2) < 0)
+		return 0;
+
+	for (i = 0; i < d->props.rc_key_map_size; i++) {
+		if (keymap[i].custom == ircode[0] &&
+		    keymap[i].data == ircode[1]) {
+			*event = keymap[i].event;
+			*state = REMOTE_KEY_PRESSED;
+
+			return 0;
+		}
+	}
+
+	return 0;
+}
+
 static struct dvb_usb_rc_key dvico_mce_rc_keys[] = {
 	{ 0xfe, 0x02, KEY_TV },
 	{ 0xfe, 0x0e, KEY_MP3 },
@@ -422,6 +545,44 @@ static struct dvb_usb_rc_key dvico_portable_rc_keys[] = {
 	{ 0xfc, 0x00, KEY_UNKNOWN },    /* HD */
 };
 
+static struct dvb_usb_rc_key d680_dmb_rc_keys[] = {
+	{ 0x00, 0x38, KEY_UNKNOWN },	/* TV/AV */
+	{ 0x08, 0x0c, KEY_ZOOM },
+	{ 0x08, 0x00, KEY_0 },
+	{ 0x00, 0x01, KEY_1 },
+	{ 0x08, 0x02, KEY_2 },
+	{ 0x00, 0x03, KEY_3 },
+	{ 0x08, 0x04, KEY_4 },
+	{ 0x00, 0x05, KEY_5 },
+	{ 0x08, 0x06, KEY_6 },
+	{ 0x00, 0x07, KEY_7 },
+	{ 0x08, 0x08, KEY_8 },
+	{ 0x00, 0x09, KEY_9 },
+	{ 0x00, 0x0a, KEY_MUTE },
+	{ 0x08, 0x29, KEY_BACK },
+	{ 0x00, 0x12, KEY_CHANNELUP },
+	{ 0x08, 0x13, KEY_CHANNELDOWN },
+	{ 0x00, 0x2b, KEY_VOLUMEUP },
+	{ 0x08, 0x2c, KEY_VOLUMEDOWN },
+	{ 0x00, 0x20, KEY_UP },
+	{ 0x08, 0x21, KEY_DOWN },
+	{ 0x00, 0x11, KEY_LEFT },
+	{ 0x08, 0x10, KEY_RIGHT },
+	{ 0x00, 0x0d, KEY_OK },
+	{ 0x08, 0x1f, KEY_RECORD },
+	{ 0x00, 0x17, KEY_PLAYPAUSE },
+	{ 0x08, 0x16, KEY_PLAYPAUSE },
+	{ 0x00, 0x0b, KEY_STOP },
+	{ 0x08, 0x27, KEY_FASTFORWARD },
+	{ 0x00, 0x26, KEY_REWIND },
+	{ 0x08, 0x1e, KEY_UNKNOWN },    /* Time Shift */
+	{ 0x00, 0x0e, KEY_UNKNOWN },    /* Snapshot */
+	{ 0x08, 0x2d, KEY_UNKNOWN },    /* Mouse Cursor */
+	{ 0x00, 0x0f, KEY_UNKNOWN },    /* Minimize/Maximize */
+	{ 0x08, 0x14, KEY_UNKNOWN },    /* Shuffle */
+	{ 0x00, 0x25, KEY_POWER },
+};
+
 static int cxusb_dee1601_demod_init(struct dvb_frontend* fe)
 {
 	static u8 clock_config []  = { CLOCK_CTL,  0x38, 0x28 };
@@ -527,6 +688,24 @@ static struct mxl5005s_config aver_a868r_tuner = {
 	.AgcMasterByte   = 0x00,
 };
 
+/* FIXME: needs tweaking */
+static struct mxl5005s_config d680_dmb_tuner = {
+	.i2c_address     = 0x63,
+	.if_freq         = 36125000UL,
+	.xtal_freq       = CRYSTAL_FREQ_16000000HZ,
+	.agc_mode        = MXL_SINGLE_AGC,
+	.tracking_filter = MXL_TF_C,
+	.rssi_enable     = MXL_RSSI_ENABLE,
+	.cap_select      = MXL_CAP_SEL_ENABLE,
+	.div_out         = MXL_DIV_OUT_4,
+	.clock_out       = MXL_CLOCK_OUT_DISABLE,
+	.output_load     = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+	.top		 = MXL5005S_TOP_25P2,
+	.mod_mode        = MXL_DIGITAL_MODE,
+	.if_mode         = MXL_ZERO_IF,
+	.AgcMasterByte   = 0x00,
+};
+
 /* Callbacks for DVB USB */
 static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap)
 {
@@ -563,7 +742,8 @@ static int cxusb_lgh064f_tuner_attach(struct dvb_usb_adapter *adap)
 	return 0;
 }
 
-static int dvico_bluebird_xc2028_callback(void *ptr, int command, int arg)
+static int dvico_bluebird_xc2028_callback(void *ptr, int component,
+					  int command, int arg)
 {
 	struct dvb_usb_adapter *adap = ptr;
 	struct dvb_usb_device *d = adap->dev;
@@ -591,14 +771,16 @@ static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap)
 	struct xc2028_config	  cfg = {
 		.i2c_adap  = &adap->dev->i2c_adap,
 		.i2c_addr  = 0x61,
-		.callback  = dvico_bluebird_xc2028_callback,
 	};
 	static struct xc2028_ctrl ctl = {
-		.fname       = "xc3028-v27.fw",
+		.fname       = XC2028_DEFAULT_FIRMWARE,
 		.max_len     = 64,
 		.demod       = XC3028_FE_ZARLINK456,
 	};
 
+	/* FIXME: generalize & move to common area */
+	adap->fe->callback = dvico_bluebird_xc2028_callback;
+
 	fe = dvb_attach(xc2028_attach, adap->fe, &cfg);
 	if (fe == NULL || fe->ops.tuner_ops.set_config == NULL)
 		return -EIO;
@@ -615,6 +797,14 @@ static int cxusb_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
 	return 0;
 }
 
+static int cxusb_d680_dmb_tuner_attach(struct dvb_usb_adapter *adap)
+{
+	struct dvb_frontend *fe;
+	fe = dvb_attach(mxl5005s_attach, adap->fe,
+			&adap->dev->i2c_adap, &d680_dmb_tuner);
+	return (fe == NULL) ? -EIO : 0;
+}
+
 static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap)
 {
 	u8 b;
@@ -726,6 +916,159 @@ no_IR:
 	return 0;
 }
 
+static struct dibx000_agc_config dib7070_agc_config = {
+	.band_caps = BAND_UHF | BAND_VHF | BAND_LBAND | BAND_SBAND,
+
+	/*
+	 * 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=5, P_agc_write=0
+	 */
+	.setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) |
+		 (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0),
+	.inv_gain = 600,
+	.time_stabiliz = 10,
+	.alpha_level = 0,
+	.thlock = 118,
+	.wbd_inv = 0,
+	.wbd_ref = 3530,
+	.wbd_sel = 1,
+	.wbd_alpha = 5,
+	.agc1_max = 65535,
+	.agc1_min = 0,
+	.agc2_max = 65535,
+	.agc2_min = 0,
+	.agc1_pt1 = 0,
+	.agc1_pt2 = 40,
+	.agc1_pt3 = 183,
+	.agc1_slope1 = 206,
+	.agc1_slope2 = 255,
+	.agc2_pt1 = 72,
+	.agc2_pt2 = 152,
+	.agc2_slope1 = 88,
+	.agc2_slope2 = 90,
+	.alpha_mant = 17,
+	.alpha_exp = 27,
+	.beta_mant = 23,
+	.beta_exp = 51,
+	.perform_agc_softsplit = 0,
+};
+
+static struct dibx000_bandwidth_config dib7070_bw_config_12_mhz = {
+	.internal = 60000,
+	.sampling = 15000,
+	.pll_prediv = 1,
+	.pll_ratio = 20,
+	.pll_range = 3,
+	.pll_reset = 1,
+	.pll_bypass = 0,
+	.enable_refdiv = 0,
+	.bypclk_div = 0,
+	.IO_CLK_en_core = 1,
+	.ADClkSrc = 1,
+	.modulo = 2,
+	/* refsel, sel, freq_15k */
+	.sad_cfg = (3 << 14) | (1 << 12) | (524 << 0),
+	.ifreq = (0 << 25) | 0,
+	.timf = 20452225,
+	.xtal_hz = 12000000,
+};
+
+static struct dib7000p_config cxusb_dualdig4_rev2_config = {
+	.output_mode = OUTMODE_MPEG2_PAR_GATED_CLK,
+	.output_mpeg2_in_188_bytes = 1,
+
+	.agc_config_count = 1,
+	.agc = &dib7070_agc_config,
+	.bw  = &dib7070_bw_config_12_mhz,
+	.tuner_is_baseband = 1,
+	.spur_protect = 1,
+
+	.gpio_dir = 0xfcef,
+	.gpio_val = 0x0110,
+
+	.gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS,
+
+	.hostbus_diversity = 1,
+};
+
+static int cxusb_dualdig4_rev2_frontend_attach(struct dvb_usb_adapter *adap)
+{
+	if (usb_set_interface(adap->dev->udev, 0, 1) < 0)
+		err("set interface failed");
+
+	cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0);
+
+	cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1);
+
+	dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18,
+				 &cxusb_dualdig4_rev2_config);
+
+	adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80,
+			      &cxusb_dualdig4_rev2_config);
+	if (adap->fe == NULL)
+		return -EIO;
+
+	return 0;
+}
+
+static int dib7070_tuner_reset(struct dvb_frontend *fe, int onoff)
+{
+	return dib7000p_set_gpio(fe, 8, 0, !onoff);
+}
+
+static int dib7070_tuner_sleep(struct dvb_frontend *fe, int onoff)
+{
+	return 0;
+}
+
+static struct dib0070_config dib7070p_dib0070_config = {
+	.i2c_address = DEFAULT_DIB0070_I2C_ADDRESS,
+	.reset = dib7070_tuner_reset,
+	.sleep = dib7070_tuner_sleep,
+	.clock_khz = 12000,
+};
+
+struct dib0700_adapter_state {
+	int (*set_param_save) (struct dvb_frontend *,
+			       struct dvb_frontend_parameters *);
+};
+
+static int dib7070_set_param_override(struct dvb_frontend *fe,
+				      struct dvb_frontend_parameters *fep)
+{
+	struct dvb_usb_adapter *adap = fe->dvb->priv;
+	struct dib0700_adapter_state *state = adap->priv;
+
+	u16 offset;
+	u8 band = BAND_OF_FREQUENCY(fep->frequency/1000);
+	switch (band) {
+	case BAND_VHF: offset = 950; break;
+	default:
+	case BAND_UHF: offset = 550; break;
+	}
+
+	dib7000p_set_wbd_ref(fe, offset + dib0070_wbd_offset(fe));
+
+	return state->set_param_save(fe, fep);
+}
+
+static int cxusb_dualdig4_rev2_tuner_attach(struct dvb_usb_adapter *adap)
+{
+	struct dib0700_adapter_state *st = adap->priv;
+	struct i2c_adapter *tun_i2c =
+		dib7000p_get_i2c_master(adap->fe,
+					DIBX000_I2C_INTERFACE_TUNER, 1);
+
+	if (dvb_attach(dib0070_attach, adap->fe, tun_i2c,
+	    &dib7070p_dib0070_config) == NULL)
+		return -ENODEV;
+
+	st->set_param_save = adap->fe->ops.tuner_ops.set_params;
+	adap->fe->ops.tuner_ops.set_params = dib7070_set_param_override;
+	return 0;
+}
+
 static int cxusb_nano2_frontend_attach(struct dvb_usb_adapter *adap)
 {
 	if (usb_set_interface(adap->dev->udev, 0, 1) < 0)
@@ -751,6 +1094,54 @@ static int cxusb_nano2_frontend_attach(struct dvb_usb_adapter *adap)
 	return -EIO;
 }
 
+static struct lgs8gl5_config lgs8gl5_cfg = {
+	.demod_address = 0x19,
+};
+
+static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap)
+{
+	struct dvb_usb_device *d = adap->dev;
+	int n;
+
+	/* Select required USB configuration */
+	if (usb_set_interface(d->udev, 0, 0) < 0)
+		err("set interface failed");
+
+	/* Unblock all USB pipes */
+	usb_clear_halt(d->udev,
+		usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
+	usb_clear_halt(d->udev,
+		usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
+	usb_clear_halt(d->udev,
+		usb_rcvbulkpipe(d->udev, d->props.adapter[0].stream.endpoint));
+
+	/* Drain USB pipes to avoid hang after reboot */
+	for (n = 0;  n < 5;  n++) {
+		cxusb_d680_dmb_drain_message(d);
+		cxusb_d680_dmb_drain_video(d);
+		msleep(200);
+	}
+
+	/* Reset the tuner */
+	if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 0) < 0) {
+		err("clear tuner gpio failed");
+		return -EIO;
+	}
+	msleep(100);
+	if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 1) < 0) {
+		err("set tuner gpio failed");
+		return -EIO;
+	}
+	msleep(100);
+
+	/* Attach frontend */
+	adap->fe = dvb_attach(lgs8gl5_attach, &lgs8gl5_cfg, &d->i2c_adap);
+	if (adap->fe == NULL)
+		return -EIO;
+
+	return 0;
+}
+
 /*
  * DViCO has shipped two devices with the same USB ID, but only one of them
  * needs a firmware download.  Check the device class details to see if they
@@ -826,9 +1217,11 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties;
 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_dualdig4_properties;
+static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties;
 static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties;
 static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_properties;
 static struct dvb_usb_device_properties cxusb_aver_a868r_properties;
+static struct dvb_usb_device_properties cxusb_d680_dmb_properties;
 
 static int cxusb_probe(struct usb_interface *intf,
 		       const struct usb_device_id *id)
@@ -852,6 +1245,11 @@ static int cxusb_probe(struct usb_interface *intf,
 				     THIS_MODULE, NULL, adapter_nr) ||
 	    0 == dvb_usb_device_init(intf, &cxusb_aver_a868r_properties,
 				     THIS_MODULE, NULL, adapter_nr) ||
+	    0 == dvb_usb_device_init(intf,
+				     &cxusb_bluebird_dualdig4_rev2_properties,
+				     THIS_MODULE, NULL, adapter_nr) ||
+	    0 == dvb_usb_device_init(intf, &cxusb_d680_dmb_properties,
+				     THIS_MODULE, NULL, adapter_nr) ||
 	    0)
 		return 0;
 
@@ -876,6 +1274,8 @@ static struct usb_device_id cxusb_table [] = {
 	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2) },
 	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM) },
 	{ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_A868R) },
+	{ USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2) },
+	{ USB_DEVICE(USB_VID_CONEXANT, USB_PID_CONEXANT_D680_DMB) },
 	{}		/* Terminating entry */
 };
 MODULE_DEVICE_TABLE (usb, cxusb_table);
@@ -1321,6 +1721,104 @@ static struct dvb_usb_device_properties cxusb_aver_a868r_properties = {
 	}
 };
 
+static
+struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties = {
+	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+	.usb_ctrl         = CYPRESS_FX2,
+
+	.size_of_priv     = sizeof(struct cxusb_state),
+
+	.num_adapters = 1,
+	.adapter = {
+		{
+			.streaming_ctrl  = cxusb_streaming_ctrl,
+			.frontend_attach = cxusb_dualdig4_rev2_frontend_attach,
+			.tuner_attach    = cxusb_dualdig4_rev2_tuner_attach,
+			.size_of_priv    = sizeof(struct dib0700_adapter_state),
+			/* parameter for the MPEG2-data transfer */
+			.stream = {
+				.type = USB_BULK,
+				.count = 7,
+				.endpoint = 0x02,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			},
+		},
+	},
+
+	.power_ctrl       = cxusb_bluebird_power_ctrl,
+
+	.i2c_algo         = &cxusb_i2c_algo,
+
+	.generic_bulk_ctrl_endpoint = 0x01,
+
+	.rc_interval      = 100,
+	.rc_key_map       = dvico_mce_rc_keys,
+	.rc_key_map_size  = ARRAY_SIZE(dvico_mce_rc_keys),
+	.rc_query         = cxusb_rc_query,
+
+	.num_device_descs = 1,
+	.devices = {
+		{   "DViCO FusionHDTV DVB-T Dual Digital 4 (rev 2)",
+			{ NULL },
+			{ &cxusb_table[17], NULL },
+		},
+	}
+};
+
+static struct dvb_usb_device_properties cxusb_d680_dmb_properties = {
+	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+	.usb_ctrl         = CYPRESS_FX2,
+
+	.size_of_priv     = sizeof(struct cxusb_state),
+
+	.num_adapters = 1,
+	.adapter = {
+		{
+			.streaming_ctrl   = cxusb_d680_dmb_streaming_ctrl,
+			.frontend_attach  = cxusb_d680_dmb_frontend_attach,
+			.tuner_attach     = cxusb_d680_dmb_tuner_attach,
+
+			/* parameter for the MPEG2-data transfer */
+			.stream = {
+				.type = USB_BULK,
+				.count = 5,
+				.endpoint = 0x02,
+				.u = {
+					.bulk = {
+						.buffersize = 8192,
+					}
+				}
+			},
+		},
+	},
+
+	.power_ctrl       = cxusb_d680_dmb_power_ctrl,
+
+	.i2c_algo         = &cxusb_i2c_algo,
+
+	.generic_bulk_ctrl_endpoint = 0x01,
+
+	.rc_interval      = 100,
+	.rc_key_map       = d680_dmb_rc_keys,
+	.rc_key_map_size  = ARRAY_SIZE(d680_dmb_rc_keys),
+	.rc_query         = cxusb_d680_dmb_rc_query,
+
+	.num_device_descs = 1,
+	.devices = {
+		{
+			"Conexant DMB-TH Stick",
+			{ NULL },
+			{ &cxusb_table[18], NULL },
+		},
+	}
+};
+
 static struct usb_driver cxusb_driver = {
 	.name		= "dvb_usb_cxusb",
 	.probe		= cxusb_probe,

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

@@ -31,6 +31,8 @@ extern int dvb_usb_dib0700_debug;
 	// 2 Byte: MPEG2 mode:  4MSB(1 = Master Mode, 0 = Slave Mode) 4LSB(Channel 1 = bit0, Channel 2 = bit1)
 	// 2 Byte: Analog mode: 4MSB(0 = 625 lines, 1 = 525 lines)    4LSB(     "                "           )
 #define REQUEST_SET_RC       0x11
+#define REQUEST_NEW_I2C_READ 0x12
+#define REQUEST_NEW_I2C_WRITE 0x13
 #define REQUEST_GET_VERSION  0x15
 
 struct dib0700_state {
@@ -39,6 +41,8 @@ struct dib0700_state {
 	u8 rc_toggle;
 	u8 rc_counter;
 	u8 is_dib7000pc;
+	u8 fw_use_new_i2c_api;
+	u8 disable_streaming_master_mode;
 };
 
 extern int dib0700_set_gpio(struct dvb_usb_device *, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val);

+ 112 - 3
drivers/media/dvb/dvb-usb/dib0700_core.c

@@ -82,9 +82,98 @@ int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, u8 gpio_
 }
 
 /*
- * I2C master xfer function
+ * I2C master xfer function (supported in 1.20 firmware)
  */
-static int dib0700_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msg,int num)
+static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
+				int num)
+{
+	/* The new i2c firmware messages are more reliable and in particular
+	   properly support i2c read calls not preceded by a write */
+
+	struct dvb_usb_device *d = i2c_get_adapdata(adap);
+	uint8_t bus_mode = 1;  /* 0=eeprom bus, 1=frontend bus */
+	uint8_t gen_mode = 0; /* 0=master i2c, 1=gpio i2c */
+	uint8_t en_start = 0;
+	uint8_t en_stop = 0;
+	uint8_t buf[255]; /* TBV: malloc ? */
+	int result, i;
+
+	/* Ensure nobody else hits the i2c bus while we're sending our
+	   sequence of messages, (such as the remote control thread) */
+	if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+		return -EAGAIN;
+
+	for (i = 0; i < num; i++) {
+		if (i == 0) {
+			/* First message in the transaction */
+			en_start = 1;
+		} else if (!(msg[i].flags & I2C_M_NOSTART)) {
+			/* Device supports repeated-start */
+			en_start = 1;
+		} else {
+			/* Not the first packet and device doesn't support
+			   repeated start */
+			en_start = 0;
+		}
+		if (i == (num - 1)) {
+			/* Last message in the transaction */
+			en_stop = 1;
+		}
+
+		if (msg[i].flags & I2C_M_RD) {
+			/* Read request */
+			u16 index, value;
+			uint8_t i2c_dest;
+
+			i2c_dest = (msg[i].addr << 1);
+			value = ((en_start << 7) | (en_stop << 6) |
+				 (msg[i].len & 0x3F)) << 8 | i2c_dest;
+			/* I2C ctrl + FE bus; */
+			index = ((gen_mode<<6)&0xC0) | ((bus_mode<<4)&0x30);
+
+			result = usb_control_msg(d->udev,
+						 usb_rcvctrlpipe(d->udev, 0),
+						 REQUEST_NEW_I2C_READ,
+						 USB_TYPE_VENDOR | USB_DIR_IN,
+						 value, index, msg[i].buf,
+						 msg[i].len,
+						 USB_CTRL_GET_TIMEOUT);
+			if (result < 0) {
+				err("i2c read error (status = %d)\n", result);
+				break;
+			}
+		} else {
+			/* Write request */
+			buf[0] = REQUEST_NEW_I2C_WRITE;
+			buf[1] = (msg[i].addr << 1);
+			buf[2] = (en_start << 7) | (en_stop << 6) |
+				(msg[i].len & 0x3F);
+			/* I2C ctrl + FE bus; */
+			buf[3] = ((gen_mode<<6)&0xC0) | ((bus_mode<<4)&0x30);
+			/* The Actual i2c payload */
+			memcpy(&buf[4], msg[i].buf, msg[i].len);
+
+			result = usb_control_msg(d->udev,
+						 usb_sndctrlpipe(d->udev, 0),
+						 REQUEST_NEW_I2C_WRITE,
+						 USB_TYPE_VENDOR | USB_DIR_OUT,
+						 0, 0, buf, msg[i].len + 4,
+						 USB_CTRL_GET_TIMEOUT);
+			if (result < 0) {
+				err("i2c write error (status = %d)\n", result);
+				break;
+			}
+		}
+	}
+	mutex_unlock(&d->i2c_mutex);
+	return i;
+}
+
+/*
+ * I2C master xfer function (pre-1.20 firmware)
+ */
+static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap,
+				   struct i2c_msg *msg, int num)
 {
 	struct dvb_usb_device *d = i2c_get_adapdata(adap);
 	int i,len;
@@ -124,6 +213,21 @@ static int dib0700_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msg,int num
 	return i;
 }
 
+static int dib0700_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
+			    int num)
+{
+	struct dvb_usb_device *d = i2c_get_adapdata(adap);
+	struct dib0700_state *st = d->priv;
+
+	if (st->fw_use_new_i2c_api == 1) {
+		/* User running at least fw 1.20 */
+		return dib0700_i2c_xfer_new(adap, msg, num);
+	} else {
+		/* Use legacy calls */
+		return dib0700_i2c_xfer_legacy(adap, msg, num);
+	}
+}
+
 static u32 dib0700_i2c_func(struct i2c_adapter *adapter)
 {
 	return I2C_FUNC_I2C;
@@ -246,7 +350,12 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
 
 	b[0] = REQUEST_ENABLE_VIDEO;
 	b[1] = (onoff << 4) | 0x00; /* this bit gives a kind of command, rather than enabling something or not */
-	b[2] = (0x01 << 4); /* Master mode */
+
+	if (st->disable_streaming_master_mode == 1)
+		b[2] = 0x00;
+	else
+		b[2] = (0x01 << 4); /* Master mode */
+
 	b[3] = 0x00;
 
 	deb_info("modifying (%d) streaming state for %d\n", onoff, adap->id);

+ 189 - 6
drivers/media/dvb/dvb-usb/dib0700_devices.c

@@ -14,6 +14,8 @@
 #include "mt2060.h"
 #include "mt2266.h"
 #include "tuner-xc2028.h"
+#include "xc5000.h"
+#include "s5h1411.h"
 #include "dib0070.h"
 
 static int force_lna_activation;
@@ -366,7 +368,8 @@ static struct dib7000p_config stk7700ph_dib7700_xc3028_config = {
 	.gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS,
 };
 
-static int stk7700ph_xc3028_callback(void *ptr, int command, int arg)
+static int stk7700ph_xc3028_callback(void *ptr, int component,
+				     int command, int arg)
 {
 	struct dvb_usb_adapter *adap = ptr;
 
@@ -394,7 +397,6 @@ static struct xc2028_ctrl stk7700ph_xc3028_ctrl = {
 
 static struct xc2028_config stk7700ph_xc3028_config = {
 	.i2c_addr = 0x61,
-	.callback = stk7700ph_xc3028_callback,
 	.ctrl = &stk7700ph_xc3028_ctrl,
 };
 
@@ -435,7 +437,9 @@ static int stk7700ph_tuner_attach(struct dvb_usb_adapter *adap)
 		DIBX000_I2C_INTERFACE_TUNER, 1);
 
 	stk7700ph_xc3028_config.i2c_adap = tun_i2c;
-	stk7700ph_xc3028_config.video_dev = adap;
+
+	/* FIXME: generalize & move to common area */
+	adap->fe->callback = stk7700ph_xc3028_callback;
 
 	return dvb_attach(xc2028_attach, adap->fe, &stk7700ph_xc3028_config)
 		== NULL ? -ENODEV : 0;
@@ -677,6 +681,43 @@ static struct dvb_usb_rc_key dib0700_rc_keys[] = {
 	{ 0x01, 0x7d, KEY_VOLUMEDOWN },
 	{ 0x02, 0x42, KEY_CHANNELUP },
 	{ 0x00, 0x7d, KEY_CHANNELDOWN },
+
+	/* Key codes for Nova-TD "credit card" remote control. */
+	{ 0x1d, 0x00, KEY_0 },
+	{ 0x1d, 0x01, KEY_1 },
+	{ 0x1d, 0x02, KEY_2 },
+	{ 0x1d, 0x03, KEY_3 },
+	{ 0x1d, 0x04, KEY_4 },
+	{ 0x1d, 0x05, KEY_5 },
+	{ 0x1d, 0x06, KEY_6 },
+	{ 0x1d, 0x07, KEY_7 },
+	{ 0x1d, 0x08, KEY_8 },
+	{ 0x1d, 0x09, KEY_9 },
+	{ 0x1d, 0x0a, KEY_TEXT },
+	{ 0x1d, 0x0d, KEY_MENU },
+	{ 0x1d, 0x0f, KEY_MUTE },
+	{ 0x1d, 0x10, KEY_VOLUMEUP },
+	{ 0x1d, 0x11, KEY_VOLUMEDOWN },
+	{ 0x1d, 0x12, KEY_CHANNEL },
+	{ 0x1d, 0x14, KEY_UP },
+	{ 0x1d, 0x15, KEY_DOWN },
+	{ 0x1d, 0x16, KEY_LEFT },
+	{ 0x1d, 0x17, KEY_RIGHT },
+	{ 0x1d, 0x1c, KEY_TV },
+	{ 0x1d, 0x1e, KEY_NEXT },
+	{ 0x1d, 0x1f, KEY_BACK },
+	{ 0x1d, 0x20, KEY_CHANNELUP },
+	{ 0x1d, 0x21, KEY_CHANNELDOWN },
+	{ 0x1d, 0x24, KEY_LAST },
+	{ 0x1d, 0x25, KEY_OK },
+	{ 0x1d, 0x30, KEY_PAUSE },
+	{ 0x1d, 0x32, KEY_REWIND },
+	{ 0x1d, 0x34, KEY_FASTFORWARD },
+	{ 0x1d, 0x35, KEY_PLAY },
+	{ 0x1d, 0x36, KEY_STOP },
+	{ 0x1d, 0x37, KEY_RECORD },
+	{ 0x1d, 0x3b, KEY_GOTO },
+	{ 0x1d, 0x3d, KEY_POWER },
 };
 
 /* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */
@@ -1078,6 +1119,97 @@ static int stk7070pd_frontend_attach1(struct dvb_usb_adapter *adap)
 	return adap->fe == NULL ? -ENODEV : 0;
 }
 
+/* S5H1411 */
+static struct s5h1411_config pinnacle_801e_config = {
+	.output_mode   = S5H1411_PARALLEL_OUTPUT,
+	.gpio          = S5H1411_GPIO_OFF,
+	.mpeg_timing   = S5H1411_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK,
+	.qam_if        = S5H1411_IF_44000,
+	.vsb_if        = S5H1411_IF_44000,
+	.inversion     = S5H1411_INVERSION_OFF,
+	.status_mode   = S5H1411_DEMODLOCKING
+};
+
+/* Pinnacle PCTV HD Pro 801e GPIOs map:
+   GPIO0  - currently unknown
+   GPIO1  - xc5000 tuner reset
+   GPIO2  - CX25843 sleep
+   GPIO3  - currently unknown
+   GPIO4  - currently unknown
+   GPIO6  - currently unknown
+   GPIO7  - currently unknown
+   GPIO9  - currently unknown
+   GPIO10 - CX25843 reset
+ */
+static int s5h1411_frontend_attach(struct dvb_usb_adapter *adap)
+{
+	struct dib0700_state *st = adap->dev->priv;
+
+	/* Make use of the new i2c functions from FW 1.20 */
+	st->fw_use_new_i2c_api = 1;
+
+	/* The s5h1411 requires the dib0700 to not be in master mode */
+	st->disable_streaming_master_mode = 1;
+
+	/* All msleep values taken from Windows USB trace */
+	dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 0);
+	dib0700_set_gpio(adap->dev, GPIO3, GPIO_OUT, 0);
+	dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1);
+	msleep(400);
+	dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0);
+	msleep(60);
+	dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1);
+	msleep(30);
+	dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1);
+	dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1);
+	dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1);
+	dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1);
+	dib0700_set_gpio(adap->dev, GPIO2, GPIO_OUT, 0);
+	msleep(30);
+
+	/* Put the CX25843 to sleep for now since we're in digital mode */
+	dib0700_set_gpio(adap->dev, GPIO2, GPIO_OUT, 1);
+
+	/* GPIOs are initialized, do the attach */
+	adap->fe = dvb_attach(s5h1411_attach, &pinnacle_801e_config,
+			      &adap->dev->i2c_adap);
+	return adap->fe == NULL ? -ENODEV : 0;
+}
+
+static int dib0700_xc5000_tuner_callback(void *priv, int component,
+					 int command, int arg)
+{
+	struct dvb_usb_adapter *adap = priv;
+
+	if (command == XC5000_TUNER_RESET) {
+		/* Reset the tuner */
+		dib0700_set_gpio(adap->dev, GPIO1, GPIO_OUT, 0);
+		msleep(330); /* from Windows USB trace */
+		dib0700_set_gpio(adap->dev, GPIO1, GPIO_OUT, 1);
+		msleep(330); /* from Windows USB trace */
+	} else {
+		err("xc5000: unknown tuner callback command: %d\n", command);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct xc5000_config s5h1411_xc5000_tunerconfig = {
+	.i2c_address      = 0x64,
+	.if_khz           = 5380,
+};
+
+static int xc5000_tuner_attach(struct dvb_usb_adapter *adap)
+{
+	/* FIXME: generalize & move to common area */
+	adap->fe->callback = dib0700_xc5000_tuner_callback;
+
+	return dvb_attach(xc5000_attach, adap->fe, &adap->dev->i2c_adap,
+			  &s5h1411_xc5000_tunerconfig)
+		== NULL ? -ENODEV : 0;
+}
+
 /* DVB-USB and USB stuff follows */
 struct usb_device_id dib0700_usb_id_table[] = {
 /* 0 */	{ USB_DEVICE(USB_VID_DIBCOM,    USB_PID_DIBCOM_STK7700P) },
@@ -1119,6 +1251,11 @@ struct usb_device_id dib0700_usb_id_table[] = {
 	{ USB_DEVICE(USB_VID_LEADTEK,   USB_PID_WINFAST_DTV_DONGLE_STK7700P_2) },
 /* 35 */{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_TD_STICK_52009) },
 	{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500_3) },
+	{ USB_DEVICE(USB_VID_GIGABYTE,  USB_PID_GIGABYTE_U8000) },
+	{ USB_DEVICE(USB_VID_YUAN,      USB_PID_YUAN_STK7700PH) },
+	{ USB_DEVICE(USB_VID_ASUS,	USB_PID_ASUS_U3000H) },
+/* 40 */{ USB_DEVICE(USB_VID_PINNACLE,  USB_PID_PINNACLE_PCTV801E) },
+	{ USB_DEVICE(USB_VID_PINNACLE,  USB_PID_PINNACLE_PCTV801E_SE) },
 	{ 0 }		/* Terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
@@ -1126,7 +1263,7 @@ MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
 #define DIB0700_DEFAULT_DEVICE_PROPERTIES \
 	.caps              = DVB_USB_IS_AN_I2C_ADAPTER, \
 	.usb_ctrl          = DEVICE_SPECIFIC, \
-	.firmware          = "dvb-usb-dib0700-1.10.fw", \
+	.firmware          = "dvb-usb-dib0700-1.20.fw", \
 	.download_firmware = dib0700_download_firmware, \
 	.no_reconnect      = 1, \
 	.size_of_priv      = sizeof(struct dib0700_state), \
@@ -1293,7 +1430,12 @@ struct dvb_usb_device_properties dib0700_devices[] = {
 				{ &dib0700_usb_id_table[31], NULL },
 				{ NULL },
 			}
-		}
+		},
+
+		.rc_interval      = DEFAULT_RC_INTERVAL,
+		.rc_key_map       = dib0700_rc_keys,
+		.rc_key_map_size  = ARRAY_SIZE(dib0700_rc_keys),
+		.rc_query         = dib0700_rc_query
 	}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
 
 		.num_adapters = 1,
@@ -1408,7 +1550,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
 			},
 		},
 
-		.num_device_descs = 3,
+		.num_device_descs = 5,
 		.devices = {
 			{   "Terratec Cinergy HT USB XE",
 				{ &dib0700_usb_id_table[27], NULL },
@@ -1422,6 +1564,47 @@ struct dvb_usb_device_properties dib0700_devices[] = {
 				{ &dib0700_usb_id_table[32], NULL },
 				{ NULL },
 			},
+			{   "Gigabyte U8000-RH",
+				{ &dib0700_usb_id_table[37], NULL },
+				{ NULL },
+			},
+			{   "YUAN High-Tech STK7700PH",
+				{ &dib0700_usb_id_table[38], NULL },
+				{ NULL },
+			},
+			{   "Asus My Cinema-U3000Hybrid",
+				{ &dib0700_usb_id_table[39], NULL },
+				{ NULL },
+			},
+		},
+		.rc_interval      = DEFAULT_RC_INTERVAL,
+		.rc_key_map       = dib0700_rc_keys,
+		.rc_key_map_size  = ARRAY_SIZE(dib0700_rc_keys),
+		.rc_query         = dib0700_rc_query
+	}, { DIB0700_DEFAULT_DEVICE_PROPERTIES,
+		.num_adapters = 1,
+		.adapter = {
+			{
+				.frontend_attach  = s5h1411_frontend_attach,
+				.tuner_attach     = xc5000_tuner_attach,
+
+				DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
+
+				.size_of_priv = sizeof(struct
+						dib0700_adapter_state),
+			},
+		},
+
+		.num_device_descs = 2,
+		.devices = {
+			{   "Pinnacle PCTV HD Pro USB Stick",
+				{ &dib0700_usb_id_table[40], NULL },
+				{ NULL },
+			},
+			{   "Pinnacle PCTV HD USB Stick",
+				{ &dib0700_usb_id_table[41], NULL },
+				{ NULL },
+			},
 		},
 		.rc_interval      = DEFAULT_RC_INTERVAL,
 		.rc_key_map       = dib0700_rc_keys,

+ 240 - 0
drivers/media/dvb/dvb-usb/dtv5100.c

@@ -0,0 +1,240 @@
+/*
+ * DVB USB Linux driver for AME DTV-5100 USB2.0 DVB-T
+ *
+ * Copyright (C) 2008  Antoine Jacquet <royale@zerezo.com>
+ * http://royale.zerezo.com/dtv5100/
+ *
+ * Inspired by gl861.c and au6610.c drivers
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "dtv5100.h"
+#include "zl10353.h"
+#include "qt1010.h"
+
+/* debug */
+static int dvb_usb_dtv5100_debug;
+module_param_named(debug, dvb_usb_dtv5100_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static int dtv5100_i2c_msg(struct dvb_usb_device *d, u8 addr,
+			   u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
+{
+	u8 request;
+	u8 type;
+	u16 value;
+	u16 index;
+
+	switch (wlen) {
+	case 1:
+		/* write { reg }, read { value } */
+		request = (addr == DTV5100_DEMOD_ADDR ? DTV5100_DEMOD_READ :
+							DTV5100_TUNER_READ);
+		type = USB_TYPE_VENDOR | USB_DIR_IN;
+		value = 0;
+		break;
+	case 2:
+		/* write { reg, value } */
+		request = (addr == DTV5100_DEMOD_ADDR ? DTV5100_DEMOD_WRITE :
+							DTV5100_TUNER_WRITE);
+		type = USB_TYPE_VENDOR | USB_DIR_OUT;
+		value = wbuf[1];
+		break;
+	default:
+		warn("wlen = %x, aborting.", wlen);
+		return -EINVAL;
+	}
+	index = (addr << 8) + wbuf[0];
+
+	msleep(1); /* avoid I2C errors */
+	return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), request,
+			       type, value, index, rbuf, rlen,
+			       DTV5100_USB_TIMEOUT);
+}
+
+/* I2C */
+static int dtv5100_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+			    int num)
+{
+	struct dvb_usb_device *d = i2c_get_adapdata(adap);
+	int i;
+
+	if (num > 2)
+		return -EINVAL;
+
+	if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+		return -EAGAIN;
+
+	for (i = 0; i < num; i++) {
+		/* write/read request */
+		if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
+			if (dtv5100_i2c_msg(d, msg[i].addr, msg[i].buf,
+					    msg[i].len, msg[i+1].buf,
+					    msg[i+1].len) < 0)
+				break;
+			i++;
+		} else if (dtv5100_i2c_msg(d, msg[i].addr, msg[i].buf,
+					   msg[i].len, NULL, 0) < 0)
+				break;
+	}
+
+	mutex_unlock(&d->i2c_mutex);
+	return i;
+}
+
+static u32 dtv5100_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm dtv5100_i2c_algo = {
+	.master_xfer   = dtv5100_i2c_xfer,
+	.functionality = dtv5100_i2c_func,
+};
+
+/* Callbacks for DVB USB */
+static struct zl10353_config dtv5100_zl10353_config = {
+	.demod_address = DTV5100_DEMOD_ADDR,
+	.no_tuner = 1,
+	.parallel_ts = 1,
+};
+
+static int dtv5100_frontend_attach(struct dvb_usb_adapter *adap)
+{
+	adap->fe = dvb_attach(zl10353_attach, &dtv5100_zl10353_config,
+			      &adap->dev->i2c_adap);
+	if (adap->fe == NULL)
+		return -EIO;
+
+	/* disable i2c gate, or it won't work... is this safe? */
+	adap->fe->ops.i2c_gate_ctrl = NULL;
+
+	return 0;
+}
+
+static struct qt1010_config dtv5100_qt1010_config = {
+	.i2c_address = DTV5100_TUNER_ADDR
+};
+
+static int dtv5100_tuner_attach(struct dvb_usb_adapter *adap)
+{
+	return dvb_attach(qt1010_attach,
+			  adap->fe, &adap->dev->i2c_adap,
+			  &dtv5100_qt1010_config) == NULL ? -ENODEV : 0;
+}
+
+/* DVB USB Driver stuff */
+static struct dvb_usb_device_properties dtv5100_properties;
+
+static int dtv5100_probe(struct usb_interface *intf,
+			 const struct usb_device_id *id)
+{
+	int i, ret;
+	struct usb_device *udev = interface_to_usbdev(intf);
+
+	/* initialize non qt1010/zl10353 part? */
+	for (i = 0; dtv5100_init[i].request; i++) {
+		ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+				      dtv5100_init[i].request,
+				      USB_TYPE_VENDOR | USB_DIR_OUT,
+				      dtv5100_init[i].value,
+				      dtv5100_init[i].index, NULL, 0,
+				      DTV5100_USB_TIMEOUT);
+		if (ret)
+			return ret;
+	}
+
+	ret = dvb_usb_device_init(intf, &dtv5100_properties,
+				  THIS_MODULE, NULL, adapter_nr);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static struct usb_device_id dtv5100_table[] = {
+	{ USB_DEVICE(0x06be, 0xa232) },
+	{ }		/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, dtv5100_table);
+
+static struct dvb_usb_device_properties dtv5100_properties = {
+	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
+	.usb_ctrl = DEVICE_SPECIFIC,
+
+	.size_of_priv = 0,
+
+	.num_adapters = 1,
+	.adapter = {{
+		.frontend_attach = dtv5100_frontend_attach,
+		.tuner_attach    = dtv5100_tuner_attach,
+
+		.stream = {
+			.type = USB_BULK,
+			.count = 8,
+			.endpoint = 0x82,
+			.u = {
+				.bulk = {
+					.buffersize = 4096,
+				}
+			}
+		},
+	} },
+
+	.i2c_algo = &dtv5100_i2c_algo,
+
+	.num_device_descs = 1,
+	.devices = {
+		{
+			.name = "AME DTV-5100 USB2.0 DVB-T",
+			.cold_ids = { NULL },
+			.warm_ids = { &dtv5100_table[0], NULL },
+		},
+	}
+};
+
+static struct usb_driver dtv5100_driver = {
+	.name		= "dvb_usb_dtv5100",
+	.probe		= dtv5100_probe,
+	.disconnect	= dvb_usb_device_exit,
+	.id_table	= dtv5100_table,
+};
+
+/* module stuff */
+static int __init dtv5100_module_init(void)
+{
+	int ret;
+
+	ret = usb_register(&dtv5100_driver);
+	if (ret)
+		err("usb_register failed. Error number %d", ret);
+
+	return ret;
+}
+
+static void __exit dtv5100_module_exit(void)
+{
+	/* deregister this driver from the USB subsystem */
+	usb_deregister(&dtv5100_driver);
+}
+
+module_init(dtv5100_module_init);
+module_exit(dtv5100_module_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");

+ 51 - 0
drivers/media/dvb/dvb-usb/dtv5100.h

@@ -0,0 +1,51 @@
+/*
+ * DVB USB Linux driver for AME DTV-5100 USB2.0 DVB-T
+ *
+ * Copyright (C) 2008  Antoine Jacquet <royale@zerezo.com>
+ * http://royale.zerezo.com/dtv5100/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _DVB_USB_DTV5100_H_
+#define _DVB_USB_DTV5100_H_
+
+#define DVB_USB_LOG_PREFIX "dtv5100"
+#include "dvb-usb.h"
+
+#define DTV5100_USB_TIMEOUT 500
+
+#define DTV5100_DEMOD_ADDR	0x00
+#define DTV5100_DEMOD_WRITE	0xc0
+#define DTV5100_DEMOD_READ	0xc1
+
+#define DTV5100_TUNER_ADDR	0xc4
+#define DTV5100_TUNER_WRITE	0xc7
+#define DTV5100_TUNER_READ	0xc8
+
+#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/"
+#define DRIVER_DESC "AME DTV-5100 USB2.0 DVB-T"
+
+static struct {
+	u8 request;
+	u8 value;
+	u16 index;
+} dtv5100_init[] = {
+	{ 0x000000c5, 0x00000000, 0x00000001 },
+	{ 0x000000c5, 0x00000001, 0x00000001 },
+	{ }		/* Terminating entry */
+};
+
+#endif

+ 28 - 1
drivers/media/dvb/dvb-usb/dvb-usb-ids.h

@@ -22,6 +22,7 @@
 #define USB_VID_AVERMEDIA			0x07ca
 #define USB_VID_COMPRO				0x185b
 #define USB_VID_COMPRO_UNK			0x145f
+#define USB_VID_CONEXANT			0x0572
 #define USB_VID_CYPRESS				0x04b4
 #define USB_VID_DIBCOM				0x10b8
 #define USB_VID_DPOSH				0x1498
@@ -33,16 +34,19 @@
 #define USB_VID_HAUPPAUGE			0x2040
 #define USB_VID_HYPER_PALTEK			0x1025
 #define USB_VID_KWORLD				0xeb2a
+#define USB_VID_KWORLD_2			0x1b80
 #define USB_VID_KYE				0x0458
 #define USB_VID_LEADTEK				0x0413
 #define USB_VID_LITEON				0x04ca
 #define USB_VID_MEDION				0x1660
 #define USB_VID_MIGLIA				0x18f3
 #define USB_VID_MSI				0x0db0
+#define USB_VID_MSI_2				0x1462
 #define USB_VID_OPERA1				0x695c
 #define USB_VID_PINNACLE			0x2304
 #define USB_VID_TECHNOTREND			0x0b48
 #define USB_VID_TERRATEC			0x0ccd
+#define USB_VID_TELESTAR			0x10b9
 #define USB_VID_VISIONPLUS			0x13d3
 #define USB_VID_TWINHAN				0x1822
 #define USB_VID_ULTIMA_ELECTRONIC		0x05d8
@@ -50,15 +54,18 @@
 #define USB_VID_WIDEVIEW			0x14aa
 #define USB_VID_GIGABYTE			0x1044
 #define USB_VID_YUAN				0x1164
-
+#define USB_VID_XTENSIONS			0x1ae7
 
 /* Product IDs */
 #define USB_PID_ADSTECH_USB2_COLD			0xa333
 #define USB_PID_ADSTECH_USB2_WARM			0xa334
 #define USB_PID_AFATECH_AF9005				0x9020
+#define USB_PID_AFATECH_AF9015_9015			0x9015
+#define USB_PID_AFATECH_AF9015_9016			0x9016
 #define USB_VID_ALINK_DTU				0xf170
 #define USB_PID_ANSONIC_DVBT_USB			0x6000
 #define USB_PID_ANYSEE					0x861f
+#define USB_PID_AZUREWAVE_AD_TU700			0x3237
 #define USB_PID_AVERMEDIA_DVBT_USB_COLD			0x0001
 #define USB_PID_AVERMEDIA_DVBT_USB_WARM			0x0002
 #define USB_PID_AVERMEDIA_DVBT_USB2_COLD		0xa800
@@ -69,6 +76,7 @@
 #define USB_PID_COMPRO_DVBU2000_UNK_WARM		0x010d
 #define USB_PID_COMPRO_VIDEOMATE_U500			0x1e78
 #define USB_PID_COMPRO_VIDEOMATE_U500_PC		0x1e80
+#define USB_PID_CONEXANT_D680_DMB			0x86d6
 #define USB_PID_DIBCOM_HOOK_DEFAULT			0x0064
 #define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM		0x0065
 #define USB_PID_DIBCOM_MOD3000_COLD			0x0bb8
@@ -87,9 +95,12 @@
 #define USB_PID_UNIWILL_STK7700P			0x6003
 #define USB_PID_GRANDTEC_DVBT_USB_COLD			0x0fa0
 #define USB_PID_GRANDTEC_DVBT_USB_WARM			0x0fa1
+#define USB_PID_KWORLD_399U				0xe399
+#define USB_PID_KWORLD_PC160_2T				0xc160
 #define USB_PID_KWORLD_VSTREAM_COLD			0x17de
 #define USB_PID_KWORLD_VSTREAM_WARM			0x17df
 #define USB_PID_TERRATEC_CINERGY_T_USB_XE		0x0055
+#define USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2		0x0069
 #define USB_PID_TWINHAN_VP7041_COLD			0x3201
 #define USB_PID_TWINHAN_VP7041_WARM			0x3202
 #define USB_PID_TWINHAN_VP7020_COLD			0x3203
@@ -98,6 +109,7 @@
 #define USB_PID_TWINHAN_VP7045_WARM			0x3206
 #define USB_PID_TWINHAN_VP7021_COLD			0x3207
 #define USB_PID_TWINHAN_VP7021_WARM			0x3208
+#define USB_PID_TINYTWIN				0x3226
 #define USB_PID_DNTV_TINYUSB2_COLD			0x3223
 #define USB_PID_DNTV_TINYUSB2_WARM			0x3224
 #define USB_PID_ULTIMA_TVBOX_COLD			0x8105
@@ -144,6 +156,9 @@
 #define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R	0x0039
 #define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_ATSC	0x1039
 #define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_DVBT	0x2039
+#define USB_PID_AVERMEDIA_VOLAR_X			0xa815
+#define USB_PID_AVERMEDIA_VOLAR_X_2			0x8150
+#define USB_PID_AVERMEDIA_A309				0xa309
 #define USB_PID_TECHNOTREND_CONNECT_S2400               0x3006
 #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY	0x005a
 #define USB_PID_TERRATEC_CINERGY_HT_USB_XE		0x0058
@@ -153,8 +168,11 @@
 #define USB_PID_PINNACLE_PCTV2000E			0x022c
 #define USB_PID_PINNACLE_PCTV_DVB_T_FLASH		0x0228
 #define USB_PID_PINNACLE_PCTV_DUAL_DIVERSITY_DVB_T	0x0229
+#define USB_PID_PINNACLE_PCTV71E			0x022b
 #define USB_PID_PINNACLE_PCTV72E			0x0236
 #define USB_PID_PINNACLE_PCTV73E			0x0237
+#define USB_PID_PINNACLE_PCTV801E			0x023a
+#define USB_PID_PINNACLE_PCTV801E_SE			0x023b
 #define USB_PID_PCTV_200E				0x020e
 #define USB_PID_PCTV_400E				0x020f
 #define USB_PID_PCTV_450E				0x0222
@@ -171,6 +189,7 @@
 #define USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD		0xdb58
 #define USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM		0xdb59
 #define USB_PID_DVICO_BLUEBIRD_DUAL_4			0xdb78
+#define USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2		0xdb98
 #define USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2		0xdb70
 #define USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM	0xdb71
 #define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD		0xdb54
@@ -190,6 +209,7 @@
 #define USB_PID_WINFAST_DTV_DONGLE_WARM			0x6026
 #define USB_PID_WINFAST_DTV_DONGLE_STK7700P		0x6f00
 #define USB_PID_WINFAST_DTV_DONGLE_STK7700P_2		0x6f01
+#define USB_PID_WINFAST_DTV_DONGLE_GOLD			0x6029
 #define USB_PID_GENPIX_8PSK_REV_1_COLD			0x0200
 #define USB_PID_GENPIX_8PSK_REV_1_WARM			0x0201
 #define USB_PID_GENPIX_8PSK_REV_2			0x0202
@@ -197,14 +217,21 @@
 #define USB_PID_GENPIX_SKYWALKER_CW3K			0x0204
 #define USB_PID_SIGMATEK_DVB_110			0x6610
 #define USB_PID_MSI_DIGI_VOX_MINI_II			0x1513
+#define USB_PID_MSI_DIGIVOX_DUO				0x8801
 #define USB_PID_OPERA1_COLD				0x2830
 #define USB_PID_OPERA1_WARM				0x3829
 #define USB_PID_LIFEVIEW_TV_WALKER_TWIN_COLD		0x0514
 #define USB_PID_LIFEVIEW_TV_WALKER_TWIN_WARM		0x0513
 #define USB_PID_GIGABYTE_U7000				0x7001
+#define USB_PID_GIGABYTE_U8000				0x7002
 #define USB_PID_ASUS_U3000				0x171f
+#define USB_PID_ASUS_U3000H				0x1736
 #define USB_PID_ASUS_U3100				0x173f
 #define USB_PID_YUAN_EC372S				0x1edc
+#define USB_PID_YUAN_STK7700PH				0x1f08
 #define USB_PID_DW2102					0x2102
+#define USB_PID_XTENSIONS_XD_380			0x0381
+#define USB_PID_TELESTAR_STARSTICK_2			0x8000
+#define USB_PID_MSI_DIGI_VOX_MINI_III                   0x8807
 
 #endif

+ 482 - 90
drivers/media/dvb/dvb-usb/dw2102.c

@@ -1,4 +1,5 @@
-/* DVB USB framework compliant Linux driver for the DVBWorld DVB-S 2102 Card
+/* DVB USB framework compliant Linux driver for the
+*	DVBWorld DVB-S 2101, 2102, DVB-S2 2104 Card
 *
 * Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by)
 *
@@ -10,62 +11,74 @@
 */
 #include <linux/version.h>
 #include "dw2102.h"
+#include "si21xx.h"
 #include "stv0299.h"
 #include "z0194a.h"
+#include "stv0288.h"
+#include "stb6000.h"
+#include "eds1547.h"
+#include "cx24116.h"
 
 #ifndef USB_PID_DW2102
 #define USB_PID_DW2102 0x2102
 #endif
 
-#define DW2102_READ_MSG 0
-#define DW2102_WRITE_MSG 1
+#ifndef USB_PID_DW2104
+#define USB_PID_DW2104 0x2104
+#endif
+
+#define DW210X_READ_MSG 0
+#define DW210X_WRITE_MSG 1
 
 #define REG_1F_SYMBOLRATE_BYTE0 0x1f
 #define REG_20_SYMBOLRATE_BYTE1 0x20
 #define REG_21_SYMBOLRATE_BYTE2 0x21
-
+/* on my own*/
 #define DW2102_VOLTAGE_CTRL (0x1800)
 #define DW2102_RC_QUERY (0x1a00)
 
-struct dw2102_state {
+struct dw210x_state {
 	u32 last_key_pressed;
 };
-struct dw2102_rc_keys {
+struct dw210x_rc_keys {
 	u32 keycode;
 	u32 event;
 };
 
+/* debug */
+static int dvb_usb_dw2102_debug;
+module_param_named(debug, dvb_usb_dw2102_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info 2=xfer (or-able))." DVB_USB_DEBUG_STATUS);
+
 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 
-static int dw2102_op_rw(struct usb_device *dev, u8 request, u16 value,
-		u8 *data, u16 len, int flags)
+static int dw210x_op_rw(struct usb_device *dev, u8 request, u16 value,
+			u16 index, u8 * data, u16 len, int flags)
 {
 	int ret;
 	u8 u8buf[len];
 
-	unsigned int pipe = (flags == DW2102_READ_MSG) ?
-		usb_rcvctrlpipe(dev, 0) : usb_sndctrlpipe(dev, 0);
-	u8 request_type = (flags == DW2102_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT;
+	unsigned int pipe = (flags == DW210X_READ_MSG) ?
+				usb_rcvctrlpipe(dev, 0) : usb_sndctrlpipe(dev, 0);
+	u8 request_type = (flags == DW210X_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT;
 
-	if (flags == DW2102_WRITE_MSG)
+	if (flags == DW210X_WRITE_MSG)
 		memcpy(u8buf, data, len);
-	ret = usb_control_msg(dev, pipe, request,
-		request_type | USB_TYPE_VENDOR, value, 0 , u8buf, len, 2000);
+	ret = usb_control_msg(dev, pipe, request, request_type | USB_TYPE_VENDOR,
+				value, index , u8buf, len, 2000);
 
-	if (flags == DW2102_READ_MSG)
+	if (flags == DW210X_READ_MSG)
 		memcpy(data, u8buf, len);
 	return ret;
 }
 
 /* I2C */
-
 static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
 		int num)
 {
 struct dvb_usb_device *d = i2c_get_adapdata(adap);
 	int i = 0, ret = 0;
 	u8 buf6[] = {0x2c, 0x05, 0xc0, 0, 0, 0, 0};
-	u8 request;
 	u16 value;
 
 	if (!d)
@@ -76,14 +89,12 @@ struct dvb_usb_device *d = i2c_get_adapdata(adap);
 	switch (num) {
 	case 2:
 		/* read stv0299 register */
-		request = 0xb5;
 		value = msg[0].buf[0];/* register */
 		for (i = 0; i < msg[1].len; i++) {
 			value = value + i;
-			ret = dw2102_op_rw(d->udev, 0xb5,
-				value, buf6, 2, DW2102_READ_MSG);
+			ret = dw210x_op_rw(d->udev, 0xb5, value, 0,
+					buf6, 2, DW210X_READ_MSG);
 			msg[1].buf[i] = buf6[0];
-
 		}
 		break;
 	case 1:
@@ -93,8 +104,8 @@ struct dvb_usb_device *d = i2c_get_adapdata(adap);
 			buf6[0] = 0x2a;
 			buf6[1] = msg[0].buf[0];
 			buf6[2] = msg[0].buf[1];
-			ret = dw2102_op_rw(d->udev, 0xb2,
-				0, buf6, 3, DW2102_WRITE_MSG);
+			ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+					buf6, 3, DW210X_WRITE_MSG);
 			break;
 		case 0x60:
 			if (msg[0].flags == 0) {
@@ -106,26 +117,26 @@ struct dvb_usb_device *d = i2c_get_adapdata(adap);
 				buf6[4] = msg[0].buf[1];
 				buf6[5] = msg[0].buf[2];
 				buf6[6] = msg[0].buf[3];
-				ret = dw2102_op_rw(d->udev, 0xb2,
-				0, buf6, 7, DW2102_WRITE_MSG);
+				ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+						buf6, 7, DW210X_WRITE_MSG);
 			} else {
-			/* write to tuner pll */
-				ret = dw2102_op_rw(d->udev, 0xb5,
-				0, buf6, 1, DW2102_READ_MSG);
+			/* read from tuner */
+				ret = dw210x_op_rw(d->udev, 0xb5, 0, 0,
+						buf6, 1, DW210X_READ_MSG);
 				msg[0].buf[0] = buf6[0];
 			}
 			break;
 		case (DW2102_RC_QUERY):
-			ret  = dw2102_op_rw(d->udev, 0xb8,
-				0, buf6, 2, DW2102_READ_MSG);
+			ret  = dw210x_op_rw(d->udev, 0xb8, 0, 0,
+					buf6, 2, DW210X_READ_MSG);
 			msg[0].buf[0] = buf6[0];
 			msg[0].buf[1] = buf6[1];
 			break;
 		case (DW2102_VOLTAGE_CTRL):
 			buf6[0] = 0x30;
 			buf6[1] = msg[0].buf[0];
-			ret = dw2102_op_rw(d->udev, 0xb2,
-				0, buf6, 2, DW2102_WRITE_MSG);
+			ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+					buf6, 2, DW210X_WRITE_MSG);
 			break;
 		}
 
@@ -136,17 +147,265 @@ struct dvb_usb_device *d = i2c_get_adapdata(adap);
 	return num;
 }
 
-static u32 dw2102_i2c_func(struct i2c_adapter *adapter)
+static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap,
+						struct i2c_msg msg[], int num)
+{
+	struct dvb_usb_device *d = i2c_get_adapdata(adap);
+	int ret = 0;
+	u8 buf6[] = {0, 0, 0, 0, 0, 0, 0};
+
+	if (!d)
+		return -ENODEV;
+	if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+		return -EAGAIN;
+
+	switch (num) {
+	case 2:
+		/* read si2109 register by number */
+		buf6[0] = 0xd0;
+		buf6[1] = msg[0].len;
+		buf6[2] = msg[0].buf[0];
+		ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+				buf6, msg[0].len + 2, DW210X_WRITE_MSG);
+		/* read si2109 register */
+		ret = dw210x_op_rw(d->udev, 0xc3, 0xd0, 0,
+				buf6, msg[1].len + 2, DW210X_READ_MSG);
+		memcpy(msg[1].buf, buf6 + 2, msg[1].len);
+
+		break;
+	case 1:
+		switch (msg[0].addr) {
+		case 0x68:
+			/* write to si2109 register */
+			buf6[0] = 0xd0;
+			buf6[1] = msg[0].len;
+			memcpy(buf6 + 2, msg[0].buf, msg[0].len);
+			ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, buf6,
+					msg[0].len + 2, DW210X_WRITE_MSG);
+			break;
+		case(DW2102_RC_QUERY):
+			ret  = dw210x_op_rw(d->udev, 0xb8, 0, 0,
+					buf6, 2, DW210X_READ_MSG);
+			msg[0].buf[0] = buf6[0];
+			msg[0].buf[1] = buf6[1];
+			break;
+		case(DW2102_VOLTAGE_CTRL):
+			buf6[0] = 0x30;
+			buf6[1] = msg[0].buf[0];
+			ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+					buf6, 2, DW210X_WRITE_MSG);
+			break;
+		}
+		break;
+	}
+
+	mutex_unlock(&d->i2c_mutex);
+	return num;
+}
+static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num)
+{
+	struct dvb_usb_device *d = i2c_get_adapdata(adap);
+	int ret = 0;
+
+	if (!d)
+		return -ENODEV;
+	if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+		return -EAGAIN;
+
+	switch (num) {
+	case 2: {
+		/* read */
+		/* first write first register number */
+		u8 ibuf [msg[1].len + 2], obuf[3];
+		obuf[0] = 0xd0;
+		obuf[1] = msg[0].len;
+		obuf[2] = msg[0].buf[0];
+		ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+				obuf, msg[0].len + 2, DW210X_WRITE_MSG);
+		/* second read registers */
+		ret = dw210x_op_rw(d->udev, 0xc3, 0xd1 , 0,
+				ibuf, msg[1].len + 2, DW210X_READ_MSG);
+		memcpy(msg[1].buf, ibuf + 2, msg[1].len);
+
+		break;
+	}
+	case 1:
+		switch (msg[0].addr) {
+		case 0x68: {
+			/* write to register */
+			u8 obuf[msg[0].len + 2];
+			obuf[0] = 0xd0;
+			obuf[1] = msg[0].len;
+			memcpy(obuf + 2, msg[0].buf, msg[0].len);
+			ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+					obuf, msg[0].len + 2, DW210X_WRITE_MSG);
+			break;
+		}
+		case 0x61: {
+			/* write to tuner */
+			u8 obuf[msg[0].len + 2];
+			obuf[0] = 0xc2;
+			obuf[1] = msg[0].len;
+			memcpy(obuf + 2, msg[0].buf, msg[0].len);
+			ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+					obuf, msg[0].len + 2, DW210X_WRITE_MSG);
+			break;
+		}
+		case(DW2102_RC_QUERY): {
+			u8 ibuf[2];
+			ret  = dw210x_op_rw(d->udev, 0xb8, 0, 0,
+					ibuf, 2, DW210X_READ_MSG);
+			memcpy(msg[0].buf, ibuf , 2);
+			break;
+		}
+		case(DW2102_VOLTAGE_CTRL): {
+			u8 obuf[2];
+			obuf[0] = 0x30;
+			obuf[1] = msg[0].buf[0];
+			ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+					obuf, 2, DW210X_WRITE_MSG);
+			break;
+		}
+		}
+
+		break;
+	}
+
+	mutex_unlock(&d->i2c_mutex);
+	return num;
+}
+
+static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num)
+{
+	struct dvb_usb_device *d = i2c_get_adapdata(adap);
+	int ret = 0;
+	int len, i;
+
+	if (!d)
+		return -ENODEV;
+	if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+		return -EAGAIN;
+
+	switch (num) {
+	case 2: {
+		/* read */
+		/* first write first register number */
+		u8 ibuf [msg[1].len + 2], obuf[3];
+		obuf[0] = 0xaa;
+		obuf[1] = msg[0].len;
+		obuf[2] = msg[0].buf[0];
+		ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+				obuf, msg[0].len + 2, DW210X_WRITE_MSG);
+		/* second read registers */
+		ret = dw210x_op_rw(d->udev, 0xc3, 0xab , 0,
+				ibuf, msg[1].len + 2, DW210X_READ_MSG);
+		memcpy(msg[1].buf, ibuf + 2, msg[1].len);
+
+		break;
+	}
+	case 1:
+		switch (msg[0].addr) {
+		case 0x55: {
+			if (msg[0].buf[0] == 0xf7) {
+				/* firmware */
+				/* Write in small blocks */
+				u8 obuf[19];
+				obuf[0] = 0xaa;
+				obuf[1] = 0x11;
+				obuf[2] = 0xf7;
+				len = msg[0].len - 1;
+				i = 1;
+				do {
+					memcpy(obuf + 3, msg[0].buf + i, (len > 16 ? 16 : len));
+					ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+						obuf, (len > 16 ? 16 : len) + 3, DW210X_WRITE_MSG);
+					i += 16;
+					len -= 16;
+				} while (len > 0);
+			} else {
+				/* write to register */
+				u8 obuf[msg[0].len + 2];
+				obuf[0] = 0xaa;
+				obuf[1] = msg[0].len;
+				memcpy(obuf + 2, msg[0].buf, msg[0].len);
+				ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+						obuf, msg[0].len + 2, DW210X_WRITE_MSG);
+			}
+			break;
+		}
+		case(DW2102_RC_QUERY): {
+			u8 ibuf[2];
+			ret  = dw210x_op_rw(d->udev, 0xb8, 0, 0,
+					ibuf, 2, DW210X_READ_MSG);
+			memcpy(msg[0].buf, ibuf , 2);
+			break;
+		}
+		case(DW2102_VOLTAGE_CTRL): {
+			u8 obuf[2];
+			obuf[0] = 0x30;
+			obuf[1] = msg[0].buf[0];
+			ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+					obuf, 2, DW210X_WRITE_MSG);
+			break;
+		}
+		}
+
+		break;
+	}
+
+	mutex_unlock(&d->i2c_mutex);
+	return num;
+}
+
+static u32 dw210x_i2c_func(struct i2c_adapter *adapter)
 {
 	return I2C_FUNC_I2C;
 }
 
 static struct i2c_algorithm dw2102_i2c_algo = {
 	.master_xfer = dw2102_i2c_transfer,
-	.functionality = dw2102_i2c_func,
+	.functionality = dw210x_i2c_func,
+};
+
+static struct i2c_algorithm dw2102_serit_i2c_algo = {
+	.master_xfer = dw2102_serit_i2c_transfer,
+	.functionality = dw210x_i2c_func,
+};
+
+static struct i2c_algorithm dw2102_earda_i2c_algo = {
+	.master_xfer = dw2102_earda_i2c_transfer,
+	.functionality = dw210x_i2c_func,
+};
+
+static struct i2c_algorithm dw2104_i2c_algo = {
+	.master_xfer = dw2104_i2c_transfer,
+	.functionality = dw210x_i2c_func,
 };
 
-static int dw2102_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+static int dw210x_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
+{
+	int i;
+	u8 ibuf[] = {0, 0};
+	u8 eeprom[256], eepromline[16];
+
+	for (i = 0; i < 256; i++) {
+		if (dw210x_op_rw(d->udev, 0xb6, 0xa0 , i, ibuf, 2, DW210X_READ_MSG) < 0) {
+			err("read eeprom failed.");
+			return -1;
+		} else {
+			eepromline[i%16] = ibuf[0];
+			eeprom[i] = ibuf[0];
+		}
+		if ((i % 16) == 15) {
+			deb_xfer("%02x: ", i - 15);
+			debug_dump(eepromline, 16, deb_xfer);
+		}
+	}
+	memcpy(mac, eeprom + 8, 6);
+	return 0;
+};
+
+static int dw210x_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
 {
 	static u8 command_13v[1] = {0x00};
 	static u8 command_18v[1] = {0x01};
@@ -163,18 +422,66 @@ static int dw2102_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
 	return 0;
 }
 
-static int dw2102_frontend_attach(struct dvb_usb_adapter *d)
+static struct cx24116_config dw2104_config = {
+	.demod_address = 0x55,
+	.mpg_clk_pos_pol = 0x01,
+};
+
+static struct si21xx_config serit_sp1511lhb_config = {
+	.demod_address = 0x68,
+	.min_delay_ms = 100,
+
+};
+
+static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
 {
-	d->fe = dvb_attach(stv0299_attach, &sharp_z0194a_config,
-		&d->dev->i2c_adap);
-	if (d->fe != NULL) {
-		d->fe->ops.set_voltage = dw2102_set_voltage;
-		info("Attached stv0299!\n");
+	if ((d->fe = dvb_attach(cx24116_attach, &dw2104_config,
+			&d->dev->i2c_adap)) != NULL) {
+		d->fe->ops.set_voltage = dw210x_set_voltage;
+		info("Attached cx24116!\n");
 		return 0;
 	}
 	return -EIO;
 }
 
+static struct dvb_usb_device_properties dw2102_properties;
+
+static int dw2102_frontend_attach(struct dvb_usb_adapter *d)
+{
+	if (dw2102_properties.i2c_algo == &dw2102_serit_i2c_algo) {
+		/*dw2102_properties.adapter->tuner_attach = NULL;*/
+		d->fe = dvb_attach(si21xx_attach, &serit_sp1511lhb_config,
+					&d->dev->i2c_adap);
+		if (d->fe != NULL) {
+			d->fe->ops.set_voltage = dw210x_set_voltage;
+			info("Attached si21xx!\n");
+			return 0;
+		}
+	}
+	if (dw2102_properties.i2c_algo == &dw2102_earda_i2c_algo) {
+		/*dw2102_properties.adapter->tuner_attach = dw2102_tuner_attach;*/
+		d->fe = dvb_attach(stv0288_attach, &earda_config,
+					&d->dev->i2c_adap);
+		if (d->fe != NULL) {
+			d->fe->ops.set_voltage = dw210x_set_voltage;
+			info("Attached stv0288!\n");
+			return 0;
+		}
+	}
+
+	if (dw2102_properties.i2c_algo == &dw2102_i2c_algo) {
+		/*dw2102_properties.adapter->tuner_attach = dw2102_tuner_attach;*/
+		d->fe = dvb_attach(stv0299_attach, &sharp_z0194a_config,
+					&d->dev->i2c_adap);
+		if (d->fe != NULL) {
+			d->fe->ops.set_voltage = dw210x_set_voltage;
+			info("Attached stv0299!\n");
+			return 0;
+		}
+	}
+	return -EIO;
+}
+
 static int dw2102_tuner_attach(struct dvb_usb_adapter *adap)
 {
 	dvb_attach(dvb_pll_attach, adap->fe, 0x60,
@@ -182,7 +489,15 @@ static int dw2102_tuner_attach(struct dvb_usb_adapter *adap)
 	return 0;
 }
 
-static struct dvb_usb_rc_key dw2102_rc_keys[] = {
+static int dw2102_earda_tuner_attach(struct dvb_usb_adapter *adap)
+{
+	dvb_attach(stb6000_attach, adap->fe, 0x61,
+		&adap->dev->i2c_adap);
+
+	return 0;
+}
+
+static struct dvb_usb_rc_key dw210x_rc_keys[] = {
 	{ 0xf8,	0x0a, KEY_Q },		/*power*/
 	{ 0xf8,	0x0c, KEY_M },		/*mute*/
 	{ 0xf8,	0x11, KEY_1 },
@@ -221,7 +536,7 @@ static struct dvb_usb_rc_key dw2102_rc_keys[] = {
 
 static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
 {
-	struct dw2102_state *st = d->priv;
+	struct dw210x_state *st = d->priv;
 	u8 key[2];
 	struct i2c_msg msg[] = {
 		{.addr = DW2102_RC_QUERY, .flags = I2C_M_RD, .buf = key,
@@ -231,12 +546,12 @@ static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
 
 	*state = REMOTE_NO_KEY_PRESSED;
 	if (dw2102_i2c_transfer(&d->i2c_adap, msg, 1) == 1) {
-		for (i = 0; i < ARRAY_SIZE(dw2102_rc_keys); i++) {
-			if (dw2102_rc_keys[i].data == msg[0].buf[0]) {
+		for (i = 0; i < ARRAY_SIZE(dw210x_rc_keys); i++) {
+			if (dw210x_rc_keys[i].data == msg[0].buf[0]) {
 				*state = REMOTE_KEY_PRESSED;
-				*event = dw2102_rc_keys[i].event;
+				*event = dw210x_rc_keys[i].event;
 				st->last_key_pressed =
-					dw2102_rc_keys[i].event;
+					dw210x_rc_keys[i].event;
 				break;
 			}
 		st->last_key_pressed = 0;
@@ -249,6 +564,8 @@ static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
 static struct usb_device_id dw2102_table[] = {
 	{USB_DEVICE(USB_VID_CYPRESS, USB_PID_DW2102)},
 	{USB_DEVICE(USB_VID_CYPRESS, 0x2101)},
+	{USB_DEVICE(USB_VID_CYPRESS, 0x2104)},
+	{USB_DEVICE(0x9022, 0xd650)},
 	{ }
 };
 
@@ -260,7 +577,7 @@ static int dw2102_load_firmware(struct usb_device *dev,
 	u8 *b, *p;
 	int ret = 0, i;
 	u8 reset;
-	u8 reset16 [] = {0, 0, 0, 0, 0, 0, 0};
+	u8 reset16[] = {0, 0, 0, 0, 0, 0, 0};
 	const struct firmware *fw;
 	const char *filename = "dvb-usb-dw2101.fw";
 	switch (dev->descriptor.idProduct) {
@@ -273,25 +590,23 @@ static int dw2102_load_firmware(struct usb_device *dev,
 			return ret;
 		}
 		break;
-	case USB_PID_DW2102:
+	default:
 		fw = frmwr;
 		break;
 	}
-	info("start downloading DW2102 firmware");
+	info("start downloading DW210X firmware");
 	p = kmalloc(fw->size, GFP_KERNEL);
 	reset = 1;
 	/*stop the CPU*/
-	dw2102_op_rw(dev, 0xa0, 0x7f92, &reset, 1, DW2102_WRITE_MSG);
-	dw2102_op_rw(dev, 0xa0, 0xe600, &reset, 1, DW2102_WRITE_MSG);
+	dw210x_op_rw(dev, 0xa0, 0x7f92, 0, &reset, 1, DW210X_WRITE_MSG);
+	dw210x_op_rw(dev, 0xa0, 0xe600, 0, &reset, 1, DW210X_WRITE_MSG);
 
 	if (p != NULL) {
 		memcpy(p, fw->data, fw->size);
 		for (i = 0; i < fw->size; i += 0x40) {
 			b = (u8 *) p + i;
-			if (dw2102_op_rw
-				(dev, 0xa0, i, b , 0x40,
-					DW2102_WRITE_MSG) != 0x40
-				) {
+			if (dw210x_op_rw(dev, 0xa0, i, 0, b , 0x40,
+					DW210X_WRITE_MSG) != 0x40) {
 				err("error while transferring firmware");
 				ret = -EINVAL;
 				break;
@@ -299,43 +614,66 @@ static int dw2102_load_firmware(struct usb_device *dev,
 		}
 		/* restart the CPU */
 		reset = 0;
-		if (ret || dw2102_op_rw
-			(dev, 0xa0, 0x7f92, &reset, 1,
-			DW2102_WRITE_MSG) != 1) {
+		if (ret || dw210x_op_rw(dev, 0xa0, 0x7f92, 0, &reset, 1,
+					DW210X_WRITE_MSG) != 1) {
 			err("could not restart the USB controller CPU.");
 			ret = -EINVAL;
 		}
-		if (ret || dw2102_op_rw
-			(dev, 0xa0, 0xe600, &reset, 1,
-			DW2102_WRITE_MSG) != 1) {
+		if (ret || dw210x_op_rw(dev, 0xa0, 0xe600, 0, &reset, 1,
+					DW210X_WRITE_MSG) != 1) {
 			err("could not restart the USB controller CPU.");
 			ret = -EINVAL;
 		}
 		/* init registers */
 		switch (dev->descriptor.idProduct) {
-		case USB_PID_DW2102:
-			dw2102_op_rw
-				(dev, 0xbf, 0x0040, &reset, 0,
-				DW2102_WRITE_MSG);
-			dw2102_op_rw
-				(dev, 0xb9, 0x0000, &reset16[0], 2,
-				DW2102_READ_MSG);
+		case USB_PID_DW2104:
+		case 0xd650:
+			reset = 1;
+			dw210x_op_rw(dev, 0xc4, 0x0000, 0, &reset, 1,
+					DW210X_WRITE_MSG);
+			reset = 0;
+			dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0,
+					DW210X_WRITE_MSG);
 			break;
+		case USB_PID_DW2102:
+			dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0,
+					DW210X_WRITE_MSG);
+			dw210x_op_rw(dev, 0xb9, 0x0000, 0, &reset16[0], 2,
+					DW210X_READ_MSG);
+			/* check STV0299 frontend  */
+			dw210x_op_rw(dev, 0xb5, 0, 0, &reset16[0], 2,
+					DW210X_READ_MSG);
+			if (reset16[0] == 0xa1) {
+				dw2102_properties.i2c_algo = &dw2102_i2c_algo;
+				dw2102_properties.adapter->tuner_attach = &dw2102_tuner_attach;
+				break;
+			} else {
+				/* check STV0288 frontend  */
+				reset16[0] = 0xd0;
+				reset16[1] = 1;
+				reset16[2] = 0;
+				dw210x_op_rw(dev, 0xc2, 0, 0, &reset16[0], 3,
+						DW210X_WRITE_MSG);
+				dw210x_op_rw(dev, 0xc3, 0xd1, 0, &reset16[0], 3,
+						DW210X_READ_MSG);
+				if (reset16[2] == 0x11) {
+					dw2102_properties.i2c_algo = &dw2102_earda_i2c_algo;
+					dw2102_properties.adapter->tuner_attach = &dw2102_earda_tuner_attach;
+					break;
+				}
+			}
 		case 0x2101:
-			dw2102_op_rw
-				(dev, 0xbc, 0x0030, &reset16[0], 2,
-				DW2102_READ_MSG);
-			dw2102_op_rw
-				(dev, 0xba, 0x0000, &reset16[0], 7,
-				DW2102_READ_MSG);
-			dw2102_op_rw
-				(dev, 0xba, 0x0000, &reset16[0], 7,
-				DW2102_READ_MSG);
-			dw2102_op_rw
-				(dev, 0xb9, 0x0000, &reset16[0], 2,
-				DW2102_READ_MSG);
+			dw210x_op_rw(dev, 0xbc, 0x0030, 0, &reset16[0], 2,
+					DW210X_READ_MSG);
+			dw210x_op_rw(dev, 0xba, 0x0000, 0, &reset16[0], 7,
+					DW210X_READ_MSG);
+			dw210x_op_rw(dev, 0xba, 0x0000, 0, &reset16[0], 7,
+					DW210X_READ_MSG);
+			dw210x_op_rw(dev, 0xb9, 0x0000, 0, &reset16[0], 2,
+					DW210X_READ_MSG);
 			break;
 		}
+		msleep(100);
 		kfree(p);
 	}
 	return ret;
@@ -345,12 +683,12 @@ static struct dvb_usb_device_properties dw2102_properties = {
 	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
 	.usb_ctrl = DEVICE_SPECIFIC,
 	.firmware = "dvb-usb-dw2102.fw",
-	.size_of_priv = sizeof(struct dw2102_state),
+	.size_of_priv = sizeof(struct dw210x_state),
 	.no_reconnect = 1,
 
-	.i2c_algo = &dw2102_i2c_algo,
-	.rc_key_map = dw2102_rc_keys,
-	.rc_key_map_size = ARRAY_SIZE(dw2102_rc_keys),
+	.i2c_algo = &dw2102_serit_i2c_algo,
+	.rc_key_map = dw210x_rc_keys,
+	.rc_key_map_size = ARRAY_SIZE(dw210x_rc_keys),
 	.rc_interval = 150,
 	.rc_query = dw2102_rc_query,
 
@@ -358,11 +696,12 @@ static struct dvb_usb_device_properties dw2102_properties = {
 	/* parameter for the MPEG2-data transfer */
 	.num_adapters = 1,
 	.download_firmware = dw2102_load_firmware,
-	.adapter = {
+	.read_mac_address = dw210x_read_mac_address,
+		.adapter = {
 		{
 			.frontend_attach = dw2102_frontend_attach,
 			.streaming_ctrl = NULL,
-			.tuner_attach = dw2102_tuner_attach,
+			.tuner_attach = NULL,
 			.stream = {
 				.type = USB_BULK,
 				.count = 8,
@@ -388,11 +727,64 @@ static struct dvb_usb_device_properties dw2102_properties = {
 	}
 };
 
+static struct dvb_usb_device_properties dw2104_properties = {
+	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
+	.usb_ctrl = DEVICE_SPECIFIC,
+	.firmware = "dvb-usb-dw2104.fw",
+	.size_of_priv = sizeof(struct dw210x_state),
+	.no_reconnect = 1,
+
+	.i2c_algo = &dw2104_i2c_algo,
+	.rc_key_map = dw210x_rc_keys,
+	.rc_key_map_size = ARRAY_SIZE(dw210x_rc_keys),
+	.rc_interval = 150,
+	.rc_query = dw2102_rc_query,
+
+	.generic_bulk_ctrl_endpoint = 0x81,
+	/* parameter for the MPEG2-data transfer */
+	.num_adapters = 1,
+	.download_firmware = dw2102_load_firmware,
+	.read_mac_address = dw210x_read_mac_address,
+	.adapter = {
+		{
+			.frontend_attach = dw2104_frontend_attach,
+			.streaming_ctrl = NULL,
+			/*.tuner_attach = dw2104_tuner_attach,*/
+			.stream = {
+				.type = USB_BULK,
+				.count = 8,
+				.endpoint = 0x82,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			},
+		}
+	},
+	.num_device_descs = 2,
+	.devices = {
+		{ "DVBWorld DW2104 USB2.0",
+			{&dw2102_table[2], NULL},
+			{NULL},
+		},
+		{ "TeVii S650 USB2.0",
+			{&dw2102_table[3], NULL},
+			{NULL},
+		},
+	}
+};
+
 static int dw2102_probe(struct usb_interface *intf,
 		const struct usb_device_id *id)
 {
-	return dvb_usb_device_init(intf, &dw2102_properties,
-		THIS_MODULE, NULL, adapter_nr);
+	if (0 == dvb_usb_device_init(intf, &dw2102_properties,
+			THIS_MODULE, NULL, adapter_nr) ||
+	    0 == dvb_usb_device_init(intf, &dw2104_properties,
+			THIS_MODULE, NULL, adapter_nr)) {
+		return 0;
+	}
+	return -ENODEV;
 }
 
 static struct usb_driver dw2102_driver = {
@@ -420,6 +812,6 @@ module_init(dw2102_module_init);
 module_exit(dw2102_module_exit);
 
 MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by");
-MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101 2102 USB2.0 device");
+MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104 USB2.0 device");
 MODULE_VERSION("0.1");
 MODULE_LICENSE("GPL");

+ 0 - 1
drivers/media/dvb/dvb-usb/dw2102.h

@@ -4,6 +4,5 @@
 #define DVB_USB_LOG_PREFIX "dw2102"
 #include "dvb-usb.h"
 
-extern int dvb_usb_dw2102_debug;
 #define deb_xfer(args...) dprintk(dvb_usb_dw2102_debug, 0x02, args)
 #endif

+ 47 - 0
drivers/media/dvb/frontends/Kconfig

@@ -43,6 +43,20 @@ config DVB_S5H1420
 	help
 	  A DVB-S tuner module. Say Y when you want to support this frontend.
 
+config DVB_STV0288
+	tristate "ST STV0288 based"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_STB6000
+	tristate "ST STB6000 silicon tuner"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	  help
+	  A DVB-S silicon tuner module. Say Y when you want to support this tuner.
+
 config DVB_STV0299
 	tristate "ST STV0299 based"
 	depends on DVB_CORE && I2C
@@ -92,6 +106,20 @@ config DVB_TUA6100
 	help
 	  A DVB-S PLL chip.
 
+config DVB_CX24116
+	tristate "Conexant CX24116 based"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A DVB-S/S2 tuner module. Say Y when you want to support this frontend.
+
+config DVB_SI21XX
+	tristate "Silicon Labs SI21XX based"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A DVB-S tuner module. Say Y when you want to support this frontend.
+
 comment "DVB-T (terrestrial) frontends"
 	depends on DVB_CORE
 
@@ -385,4 +413,23 @@ config DVB_ISL6421
 	help
 	  An SEC control chip.
 
+config DVB_LGS8GL5
+	tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  A DMB-TH tuner module. Say Y when you want to support this frontend.
+
+comment "Tools to develop new frontends"
+
+config DVB_DUMMY_FE
+	tristate "Dummy frontend driver"
+	default n
+
+config DVB_AF9013
+	tristate "Afatech AF9013 demodulator"
+	depends on DVB_CORE && I2C
+	default m if DVB_FE_CUSTOMISE
+	help
+	  Say Y when you want to support this frontend.
 endmenu

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

@@ -48,3 +48,10 @@ obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o
 obj-$(CONFIG_DVB_AU8522) += au8522.o
 obj-$(CONFIG_DVB_TDA10048) += tda10048.o
 obj-$(CONFIG_DVB_S5H1411) += s5h1411.o
+obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o
+obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o
+obj-$(CONFIG_DVB_AF9013) += af9013.o
+obj-$(CONFIG_DVB_CX24116) += cx24116.o
+obj-$(CONFIG_DVB_SI21XX) += si21xx.o
+obj-$(CONFIG_DVB_STV0288) += stv0288.o
+obj-$(CONFIG_DVB_STB6000) += stb6000.o

+ 1685 - 0
drivers/media/dvb/frontends/af9013.c

@@ -0,0 +1,1685 @@
+/*
+ * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+
+#include "dvb_frontend.h"
+#include "af9013_priv.h"
+#include "af9013.h"
+
+int af9013_debug;
+
+struct af9013_state {
+	struct i2c_adapter *i2c;
+	struct dvb_frontend frontend;
+
+	struct af9013_config config;
+
+	u16 signal_strength;
+	u32 ber;
+	u32 ucblocks;
+	u16 snr;
+	u32 frequency;
+	unsigned long next_statistics_check;
+};
+
+static u8 regmask[8] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
+
+static int af9013_write_regs(struct af9013_state *state, u8 mbox, u16 reg,
+	u8 *val, u8 len)
+{
+	u8 buf[3+len];
+	struct i2c_msg msg = {
+		.addr = state->config.demod_address,
+		.flags = 0,
+		.len = sizeof(buf),
+		.buf = buf };
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
+	buf[2] = mbox;
+	memcpy(&buf[3], val, len);
+
+	if (i2c_transfer(state->i2c, &msg, 1) != 1) {
+		warn("I2C write failed reg:%04x len:%d", reg, len);
+		return -EREMOTEIO;
+	}
+	return 0;
+}
+
+static int af9013_write_ofdm_regs(struct af9013_state *state, u16 reg, u8 *val,
+	u8 len)
+{
+	u8 mbox = (1 << 0)|(1 << 1)|((len - 1) << 2)|(0 << 6)|(0 << 7);
+	return af9013_write_regs(state, mbox, reg, val, len);
+}
+
+static int af9013_write_ofsm_regs(struct af9013_state *state, u16 reg, u8 *val,
+	u8 len)
+{
+	u8 mbox = (1 << 0)|(1 << 1)|((len - 1) << 2)|(1 << 6)|(1 << 7);
+	return af9013_write_regs(state, mbox, reg, val, len);
+}
+
+/* write single register */
+static int af9013_write_reg(struct af9013_state *state, u16 reg, u8 val)
+{
+	return af9013_write_ofdm_regs(state, reg, &val, 1);
+}
+
+/* read single register */
+static int af9013_read_reg(struct af9013_state *state, u16 reg, u8 *val)
+{
+	u8 obuf[3] = { reg >> 8, reg & 0xff, 0 };
+	u8 ibuf[1];
+	struct i2c_msg msg[2] = {
+		{
+			.addr = state->config.demod_address,
+			.flags = 0,
+			.len = sizeof(obuf),
+			.buf = obuf
+		}, {
+			.addr = state->config.demod_address,
+			.flags = I2C_M_RD,
+			.len = sizeof(ibuf),
+			.buf = ibuf
+		}
+	};
+
+	if (i2c_transfer(state->i2c, msg, 2) != 2) {
+		warn("I2C read failed reg:%04x", reg);
+		return -EREMOTEIO;
+	}
+	*val = ibuf[0];
+	return 0;
+}
+
+static int af9013_write_reg_bits(struct af9013_state *state, u16 reg, u8 pos,
+	u8 len, u8 val)
+{
+	int ret;
+	u8 tmp, mask;
+
+	ret = af9013_read_reg(state, reg, &tmp);
+	if (ret)
+		return ret;
+
+	mask = regmask[len - 1] << pos;
+	tmp = (tmp & ~mask) | ((val << pos) & mask);
+
+	return af9013_write_reg(state, reg, tmp);
+}
+
+static int af9013_read_reg_bits(struct af9013_state *state, u16 reg, u8 pos,
+	u8 len, u8 *val)
+{
+	int ret;
+	u8 tmp;
+
+	ret = af9013_read_reg(state, reg, &tmp);
+	if (ret)
+		return ret;
+	*val = (tmp >> pos) & regmask[len - 1];
+	return 0;
+}
+
+static int af9013_set_gpio(struct af9013_state *state, u8 gpio, u8 gpioval)
+{
+	int ret;
+	u8 pos;
+	u16 addr;
+	deb_info("%s: gpio:%d gpioval:%02x\n", __func__, gpio, gpioval);
+
+/* GPIO0 & GPIO1 0xd735
+   GPIO2 & GPIO3 0xd736 */
+
+	switch (gpio) {
+	case 0:
+	case 1:
+		addr = 0xd735;
+		break;
+	case 2:
+	case 3:
+		addr = 0xd736;
+		break;
+
+	default:
+		err("invalid gpio:%d\n", gpio);
+		ret = -EINVAL;
+		goto error;
+	};
+
+	switch (gpio) {
+	case 0:
+	case 2:
+		pos = 0;
+		break;
+	case 1:
+	case 3:
+	default:
+		pos = 4;
+		break;
+	};
+
+	ret = af9013_write_reg_bits(state, addr, pos, 4, gpioval);
+
+error:
+	return ret;
+}
+
+static u32 af913_div(u32 a, u32 b, u32 x)
+{
+	u32 r = 0, c = 0, i;
+	deb_info("%s: a:%d b:%d x:%d\n", __func__, a, b, x);
+
+	if (a > b) {
+		c = a / b;
+		a = a - c * b;
+	}
+
+	for (i = 0; i < x; i++) {
+		if (a >= b) {
+			r += 1;
+			a -= b;
+		}
+		a <<= 1;
+		r <<= 1;
+	}
+	r = (c << (u32)x) + r;
+
+	deb_info("%s: a:%d b:%d x:%d r:%d r:%x\n", __func__, a, b, x, r, r);
+	return r;
+}
+
+static int af9013_set_coeff(struct af9013_state *state, fe_bandwidth_t bw)
+{
+	int ret = 0;
+	u8 i = 0;
+	u8 buf[24];
+	u32 ns_coeff1_2048nu;
+	u32 ns_coeff1_8191nu;
+	u32 ns_coeff1_8192nu;
+	u32 ns_coeff1_8193nu;
+	u32 ns_coeff2_2k;
+	u32 ns_coeff2_8k;
+
+	deb_info("%s: adc_clock:%d bw:%d\n", __func__,
+		state->config.adc_clock, bw);
+
+	switch (state->config.adc_clock) {
+	case 28800: /* 28.800 MHz */
+		switch (bw) {
+		case BANDWIDTH_6_MHZ:
+			ns_coeff1_2048nu = 0x01e79e7a;
+			ns_coeff1_8191nu = 0x0079eb6e;
+			ns_coeff1_8192nu = 0x0079e79e;
+			ns_coeff1_8193nu = 0x0079e3cf;
+			ns_coeff2_2k     = 0x00f3cf3d;
+			ns_coeff2_8k     = 0x003cf3cf;
+			break;
+		case BANDWIDTH_7_MHZ:
+			ns_coeff1_2048nu = 0x0238e38e;
+			ns_coeff1_8191nu = 0x008e3d55;
+			ns_coeff1_8192nu = 0x008e38e4;
+			ns_coeff1_8193nu = 0x008e3472;
+			ns_coeff2_2k     = 0x011c71c7;
+			ns_coeff2_8k     = 0x00471c72;
+			break;
+		case BANDWIDTH_8_MHZ:
+			ns_coeff1_2048nu = 0x028a28a3;
+			ns_coeff1_8191nu = 0x00a28f3d;
+			ns_coeff1_8192nu = 0x00a28a29;
+			ns_coeff1_8193nu = 0x00a28514;
+			ns_coeff2_2k     = 0x01451451;
+			ns_coeff2_8k     = 0x00514514;
+			break;
+		default:
+			ret = -EINVAL;
+		}
+		break;
+	case 20480: /* 20.480 MHz */
+		switch (bw) {
+		case BANDWIDTH_6_MHZ:
+			ns_coeff1_2048nu = 0x02adb6dc;
+			ns_coeff1_8191nu = 0x00ab7313;
+			ns_coeff1_8192nu = 0x00ab6db7;
+			ns_coeff1_8193nu = 0x00ab685c;
+			ns_coeff2_2k     = 0x0156db6e;
+			ns_coeff2_8k     = 0x0055b6dc;
+			break;
+		case BANDWIDTH_7_MHZ:
+			ns_coeff1_2048nu = 0x03200001;
+			ns_coeff1_8191nu = 0x00c80640;
+			ns_coeff1_8192nu = 0x00c80000;
+			ns_coeff1_8193nu = 0x00c7f9c0;
+			ns_coeff2_2k     = 0x01900000;
+			ns_coeff2_8k     = 0x00640000;
+			break;
+		case BANDWIDTH_8_MHZ:
+			ns_coeff1_2048nu = 0x03924926;
+			ns_coeff1_8191nu = 0x00e4996e;
+			ns_coeff1_8192nu = 0x00e49249;
+			ns_coeff1_8193nu = 0x00e48b25;
+			ns_coeff2_2k     = 0x01c92493;
+			ns_coeff2_8k     = 0x00724925;
+			break;
+		default:
+			ret = -EINVAL;
+		}
+		break;
+	case 28000: /* 28.000 MHz */
+		switch (bw) {
+		case BANDWIDTH_6_MHZ:
+			ns_coeff1_2048nu = 0x01f58d10;
+			ns_coeff1_8191nu = 0x007d672f;
+			ns_coeff1_8192nu = 0x007d6344;
+			ns_coeff1_8193nu = 0x007d5f59;
+			ns_coeff2_2k     = 0x00fac688;
+			ns_coeff2_8k     = 0x003eb1a2;
+			break;
+		case BANDWIDTH_7_MHZ:
+			ns_coeff1_2048nu = 0x02492492;
+			ns_coeff1_8191nu = 0x00924db7;
+			ns_coeff1_8192nu = 0x00924925;
+			ns_coeff1_8193nu = 0x00924492;
+			ns_coeff2_2k     = 0x01249249;
+			ns_coeff2_8k     = 0x00492492;
+			break;
+		case BANDWIDTH_8_MHZ:
+			ns_coeff1_2048nu = 0x029cbc15;
+			ns_coeff1_8191nu = 0x00a7343f;
+			ns_coeff1_8192nu = 0x00a72f05;
+			ns_coeff1_8193nu = 0x00a729cc;
+			ns_coeff2_2k     = 0x014e5e0a;
+			ns_coeff2_8k     = 0x00539783;
+			break;
+		default:
+			ret = -EINVAL;
+		}
+		break;
+	case 25000: /* 25.000 MHz */
+		switch (bw) {
+		case BANDWIDTH_6_MHZ:
+			ns_coeff1_2048nu = 0x0231bcb5;
+			ns_coeff1_8191nu = 0x008c7391;
+			ns_coeff1_8192nu = 0x008c6f2d;
+			ns_coeff1_8193nu = 0x008c6aca;
+			ns_coeff2_2k     = 0x0118de5b;
+			ns_coeff2_8k     = 0x00463797;
+			break;
+		case BANDWIDTH_7_MHZ:
+			ns_coeff1_2048nu = 0x028f5c29;
+			ns_coeff1_8191nu = 0x00a3dc29;
+			ns_coeff1_8192nu = 0x00a3d70a;
+			ns_coeff1_8193nu = 0x00a3d1ec;
+			ns_coeff2_2k     = 0x0147ae14;
+			ns_coeff2_8k     = 0x0051eb85;
+			break;
+		case BANDWIDTH_8_MHZ:
+			ns_coeff1_2048nu = 0x02ecfb9d;
+			ns_coeff1_8191nu = 0x00bb44c1;
+			ns_coeff1_8192nu = 0x00bb3ee7;
+			ns_coeff1_8193nu = 0x00bb390d;
+			ns_coeff2_2k     = 0x01767dce;
+			ns_coeff2_8k     = 0x005d9f74;
+			break;
+		default:
+			ret = -EINVAL;
+		}
+		break;
+	default:
+		err("invalid xtal");
+		return -EINVAL;
+	}
+	if (ret) {
+		err("invalid bandwidth");
+		return ret;
+	}
+
+	buf[i++] = (u8) ((ns_coeff1_2048nu & 0x03000000) >> 24);
+	buf[i++] = (u8) ((ns_coeff1_2048nu & 0x00ff0000) >> 16);
+	buf[i++] = (u8) ((ns_coeff1_2048nu & 0x0000ff00) >> 8);
+	buf[i++] = (u8) ((ns_coeff1_2048nu & 0x000000ff));
+	buf[i++] = (u8) ((ns_coeff2_2k     & 0x01c00000) >> 22);
+	buf[i++] = (u8) ((ns_coeff2_2k     & 0x003fc000) >> 14);
+	buf[i++] = (u8) ((ns_coeff2_2k     & 0x00003fc0) >> 6);
+	buf[i++] = (u8) ((ns_coeff2_2k     & 0x0000003f));
+	buf[i++] = (u8) ((ns_coeff1_8191nu & 0x03000000) >> 24);
+	buf[i++] = (u8) ((ns_coeff1_8191nu & 0x00ffc000) >> 16);
+	buf[i++] = (u8) ((ns_coeff1_8191nu & 0x0000ff00) >> 8);
+	buf[i++] = (u8) ((ns_coeff1_8191nu & 0x000000ff));
+	buf[i++] = (u8) ((ns_coeff1_8192nu & 0x03000000) >> 24);
+	buf[i++] = (u8) ((ns_coeff1_8192nu & 0x00ffc000) >> 16);
+	buf[i++] = (u8) ((ns_coeff1_8192nu & 0x0000ff00) >> 8);
+	buf[i++] = (u8) ((ns_coeff1_8192nu & 0x000000ff));
+	buf[i++] = (u8) ((ns_coeff1_8193nu & 0x03000000) >> 24);
+	buf[i++] = (u8) ((ns_coeff1_8193nu & 0x00ffc000) >> 16);
+	buf[i++] = (u8) ((ns_coeff1_8193nu & 0x0000ff00) >> 8);
+	buf[i++] = (u8) ((ns_coeff1_8193nu & 0x000000ff));
+	buf[i++] = (u8) ((ns_coeff2_8k     & 0x01c00000) >> 22);
+	buf[i++] = (u8) ((ns_coeff2_8k     & 0x003fc000) >> 14);
+	buf[i++] = (u8) ((ns_coeff2_8k     & 0x00003fc0) >> 6);
+	buf[i++] = (u8) ((ns_coeff2_8k     & 0x0000003f));
+
+	deb_info("%s: coeff:", __func__);
+	debug_dump(buf, sizeof(buf), deb_info);
+
+	/* program */
+	for (i = 0; i < sizeof(buf); i++) {
+		ret = af9013_write_reg(state, 0xae00 + i, buf[i]);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
+static int af9013_set_adc_ctrl(struct af9013_state *state)
+{
+	int ret;
+	u8 buf[3], tmp, i;
+	u32 adc_cw;
+
+	deb_info("%s: adc_clock:%d\n", __func__, state->config.adc_clock);
+
+	/* adc frequency type */
+	switch (state->config.adc_clock) {
+	case 28800: /* 28.800 MHz */
+		tmp = 0;
+		break;
+	case 20480: /* 20.480 MHz */
+		tmp = 1;
+		break;
+	case 28000: /* 28.000 MHz */
+		tmp = 2;
+		break;
+	case 25000: /* 25.000 MHz */
+		tmp = 3;
+		break;
+	default:
+		err("invalid xtal");
+		return -EINVAL;
+	}
+
+	adc_cw = af913_div(state->config.adc_clock*1000, 1000000ul, 19ul);
+
+	buf[0] = (u8) ((adc_cw & 0x000000ff));
+	buf[1] = (u8) ((adc_cw & 0x0000ff00) >> 8);
+	buf[2] = (u8) ((adc_cw & 0x00ff0000) >> 16);
+
+	deb_info("%s: adc_cw:", __func__);
+	debug_dump(buf, sizeof(buf), deb_info);
+
+	/* program */
+	for (i = 0; i < sizeof(buf); i++) {
+		ret = af9013_write_reg(state, 0xd180 + i, buf[i]);
+		if (ret)
+			goto error;
+	}
+	ret = af9013_write_reg_bits(state, 0x9bd2, 0, 4, tmp);
+error:
+	return ret;
+}
+
+static int af9013_set_freq_ctrl(struct af9013_state *state, fe_bandwidth_t bw)
+{
+	int ret;
+	u16 addr;
+	u8 buf[3], i, j;
+	u32 adc_freq, freq_cw;
+	s8 bfs_spec_inv;
+	int if_sample_freq;
+
+	for (j = 0; j < 3; j++) {
+		if (j == 0) {
+			addr = 0xd140; /* fcw normal */
+			bfs_spec_inv = state->config.rf_spec_inv ? -1 : 1;
+		} else if (j == 1) {
+			addr = 0x9be7; /* fcw dummy ram */
+			bfs_spec_inv = state->config.rf_spec_inv ? -1 : 1;
+		} else {
+			addr = 0x9bea; /* fcw inverted */
+			bfs_spec_inv = state->config.rf_spec_inv ? 1 : -1;
+		}
+
+		adc_freq       = state->config.adc_clock * 1000;
+		if_sample_freq = state->config.tuner_if * 1000;
+
+		/* TDA18271 uses different sampling freq for every bw */
+		if (state->config.tuner == AF9013_TUNER_TDA18271) {
+			switch (bw) {
+			case BANDWIDTH_6_MHZ:
+				if_sample_freq = 3300000; /* 3.3 MHz */
+				break;
+			case BANDWIDTH_7_MHZ:
+				if_sample_freq = 3800000; /* 3.8 MHz */
+				break;
+			case BANDWIDTH_8_MHZ:
+			default:
+				if_sample_freq = 4300000; /* 4.3 MHz */
+				break;
+			}
+		}
+
+		while (if_sample_freq > (adc_freq / 2))
+			if_sample_freq = if_sample_freq - adc_freq;
+
+		if (if_sample_freq >= 0)
+			bfs_spec_inv = bfs_spec_inv * (-1);
+		else
+			if_sample_freq = if_sample_freq * (-1);
+
+		freq_cw = af913_div(if_sample_freq, adc_freq, 23ul);
+
+		if (bfs_spec_inv == -1)
+			freq_cw = 0x00800000 - freq_cw;
+
+		buf[0] = (u8) ((freq_cw & 0x000000ff));
+		buf[1] = (u8) ((freq_cw & 0x0000ff00) >> 8);
+		buf[2] = (u8) ((freq_cw & 0x007f0000) >> 16);
+
+
+		deb_info("%s: freq_cw:", __func__);
+		debug_dump(buf, sizeof(buf), deb_info);
+
+		/* program */
+		for (i = 0; i < sizeof(buf); i++) {
+			ret = af9013_write_reg(state, addr++, buf[i]);
+			if (ret)
+				goto error;
+		}
+	}
+error:
+	return ret;
+}
+
+static int af9013_set_ofdm_params(struct af9013_state *state,
+	struct dvb_ofdm_parameters *params, u8 *auto_mode)
+{
+	int ret;
+	u8 i, buf[3] = {0, 0, 0};
+	*auto_mode = 0; /* set if parameters are requested to auto set */
+
+	switch (params->transmission_mode) {
+	case TRANSMISSION_MODE_AUTO:
+		*auto_mode = 1;
+	case TRANSMISSION_MODE_2K:
+		break;
+	case TRANSMISSION_MODE_8K:
+		buf[0] |= (1 << 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (params->guard_interval) {
+	case GUARD_INTERVAL_AUTO:
+		*auto_mode = 1;
+	case GUARD_INTERVAL_1_32:
+		break;
+	case GUARD_INTERVAL_1_16:
+		buf[0] |= (1 << 2);
+		break;
+	case GUARD_INTERVAL_1_8:
+		buf[0] |= (2 << 2);
+		break;
+	case GUARD_INTERVAL_1_4:
+		buf[0] |= (3 << 2);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (params->hierarchy_information) {
+	case HIERARCHY_AUTO:
+		*auto_mode = 1;
+	case HIERARCHY_NONE:
+		break;
+	case HIERARCHY_1:
+		buf[0] |= (1 << 4);
+		break;
+	case HIERARCHY_2:
+		buf[0] |= (2 << 4);
+		break;
+	case HIERARCHY_4:
+		buf[0] |= (3 << 4);
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	switch (params->constellation) {
+	case QAM_AUTO:
+		*auto_mode = 1;
+	case QPSK:
+		break;
+	case QAM_16:
+		buf[1] |= (1 << 6);
+		break;
+	case QAM_64:
+		buf[1] |= (2 << 6);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Use HP. How and which case we can switch to LP? */
+	buf[1] |= (1 << 4);
+
+	switch (params->code_rate_HP) {
+	case FEC_AUTO:
+		*auto_mode = 1;
+	case FEC_1_2:
+		break;
+	case FEC_2_3:
+		buf[2] |= (1 << 0);
+		break;
+	case FEC_3_4:
+		buf[2] |= (2 << 0);
+		break;
+	case FEC_5_6:
+		buf[2] |= (3 << 0);
+		break;
+	case FEC_7_8:
+		buf[2] |= (4 << 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (params->code_rate_LP) {
+	case FEC_AUTO:
+	/* if HIERARCHY_NONE and FEC_NONE then LP FEC is set to FEC_AUTO
+	   by dvb_frontend.c for compatibility */
+		if (params->hierarchy_information != HIERARCHY_NONE)
+			*auto_mode = 1;
+	case FEC_1_2:
+		break;
+	case FEC_2_3:
+		buf[2] |= (1 << 3);
+		break;
+	case FEC_3_4:
+		buf[2] |= (2 << 3);
+		break;
+	case FEC_5_6:
+		buf[2] |= (3 << 3);
+		break;
+	case FEC_7_8:
+		buf[2] |= (4 << 3);
+		break;
+	case FEC_NONE:
+		if (params->hierarchy_information == HIERARCHY_AUTO)
+			break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (params->bandwidth) {
+	case BANDWIDTH_6_MHZ:
+		break;
+	case BANDWIDTH_7_MHZ:
+		buf[1] |= (1 << 2);
+		break;
+	case BANDWIDTH_8_MHZ:
+		buf[1] |= (2 << 2);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* program */
+	for (i = 0; i < sizeof(buf); i++) {
+		ret = af9013_write_reg(state, 0xd3c0 + i, buf[i]);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
+static int af9013_reset(struct af9013_state *state, u8 sleep)
+{
+	int ret;
+	u8 tmp, i;
+	deb_info("%s\n", __func__);
+
+	/* enable OFDM reset */
+	ret = af9013_write_reg_bits(state, 0xd417, 4, 1, 1);
+	if (ret)
+		goto error;
+
+	/* start reset mechanism */
+	ret = af9013_write_reg(state, 0xaeff, 1);
+	if (ret)
+		goto error;
+
+	/* reset is done when bit 1 is set */
+	for (i = 0; i < 150; i++) {
+		ret = af9013_read_reg_bits(state, 0xd417, 1, 1, &tmp);
+		if (ret)
+			goto error;
+		if (tmp)
+			break; /* reset done */
+		msleep(10);
+	}
+	if (!tmp)
+		return -ETIMEDOUT;
+
+	/* don't clear reset when going to sleep */
+	if (!sleep) {
+		/* clear OFDM reset */
+		ret = af9013_write_reg_bits(state, 0xd417, 1, 1, 0);
+		if (ret)
+			goto error;
+
+		/* disable OFDM reset */
+		ret = af9013_write_reg_bits(state, 0xd417, 4, 1, 0);
+	}
+error:
+	return ret;
+}
+
+static int af9013_power_ctrl(struct af9013_state *state, u8 onoff)
+{
+	int ret;
+	deb_info("%s: onoff:%d\n", __func__, onoff);
+
+	if (onoff) {
+		/* power on */
+		ret = af9013_write_reg_bits(state, 0xd73a, 3, 1, 0);
+		if (ret)
+			goto error;
+		ret = af9013_write_reg_bits(state, 0xd417, 1, 1, 0);
+		if (ret)
+			goto error;
+		ret = af9013_write_reg_bits(state, 0xd417, 4, 1, 0);
+	} else {
+		/* power off */
+		ret = af9013_reset(state, 1);
+		if (ret)
+			goto error;
+		ret = af9013_write_reg_bits(state, 0xd73a, 3, 1, 1);
+	}
+error:
+	return ret;
+}
+
+static int af9013_lock_led(struct af9013_state *state, u8 onoff)
+{
+	deb_info("%s: onoff:%d\n", __func__, onoff);
+
+	return af9013_write_reg_bits(state, 0xd730, 0, 1, onoff);
+}
+
+static int af9013_set_frontend(struct dvb_frontend *fe,
+	struct dvb_frontend_parameters *params)
+{
+	struct af9013_state *state = fe->demodulator_priv;
+	int ret;
+	u8 auto_mode; /* auto set TPS */
+
+	deb_info("%s: freq:%d bw:%d\n", __func__, params->frequency,
+		params->u.ofdm.bandwidth);
+
+	state->frequency = params->frequency;
+
+	/* program CFOE coefficients */
+	ret = af9013_set_coeff(state, params->u.ofdm.bandwidth);
+	if (ret)
+		goto error;
+
+	/* program frequency control */
+	ret = af9013_set_freq_ctrl(state, params->u.ofdm.bandwidth);
+	if (ret)
+		goto error;
+
+	/* clear TPS lock flag (inverted flag) */
+	ret = af9013_write_reg_bits(state, 0xd330, 3, 1, 1);
+	if (ret)
+		goto error;
+
+	/* clear MPEG2 lock flag */
+	ret = af9013_write_reg_bits(state, 0xd507, 6, 1, 0);
+	if (ret)
+		goto error;
+
+	/* empty channel function */
+	ret = af9013_write_reg_bits(state, 0x9bfe, 0, 1, 0);
+	if (ret)
+		goto error;
+
+	/* empty DVB-T channel function */
+	ret = af9013_write_reg_bits(state, 0x9bc2, 0, 1, 0);
+	if (ret)
+		goto error;
+
+	/* program tuner */
+	if (fe->ops.tuner_ops.set_params)
+		fe->ops.tuner_ops.set_params(fe, params);
+
+	/* program TPS and bandwidth, check if auto mode needed */
+	ret = af9013_set_ofdm_params(state, &params->u.ofdm, &auto_mode);
+	if (ret)
+		goto error;
+
+	if (auto_mode) {
+		/* clear easy mode flag */
+		ret = af9013_write_reg(state, 0xaefd, 0);
+		deb_info("%s: auto TPS\n", __func__);
+	} else {
+		/* set easy mode flag */
+		ret = af9013_write_reg(state, 0xaefd, 1);
+		if (ret)
+			goto error;
+		ret = af9013_write_reg(state, 0xaefe, 0);
+		deb_info("%s: manual TPS\n", __func__);
+	}
+	if (ret)
+		goto error;
+
+	/* everything is set, lets try to receive channel - OFSM GO! */
+	ret = af9013_write_reg(state, 0xffff, 0);
+	if (ret)
+		goto error;
+
+error:
+	return ret;
+}
+
+static int af9013_get_frontend(struct dvb_frontend *fe,
+	struct dvb_frontend_parameters *p)
+{
+	struct af9013_state *state = fe->demodulator_priv;
+	int ret;
+	u8 i, buf[3];
+	deb_info("%s\n", __func__);
+
+	/* read TPS registers */
+	for (i = 0; i < 3; i++) {
+		ret = af9013_read_reg(state, 0xd3c0 + i, &buf[i]);
+		if (ret)
+			goto error;
+	}
+
+	switch ((buf[1] >> 6) & 3) {
+	case 0:
+		p->u.ofdm.constellation = QPSK;
+		break;
+	case 1:
+		p->u.ofdm.constellation = QAM_16;
+		break;
+	case 2:
+		p->u.ofdm.constellation = QAM_64;
+		break;
+	}
+
+	switch ((buf[0] >> 0) & 3) {
+	case 0:
+		p->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K;
+		break;
+	case 1:
+		p->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K;
+	}
+
+	switch ((buf[0] >> 2) & 3) {
+	case 0:
+		p->u.ofdm.guard_interval = GUARD_INTERVAL_1_32;
+		break;
+	case 1:
+		p->u.ofdm.guard_interval = GUARD_INTERVAL_1_16;
+		break;
+	case 2:
+		p->u.ofdm.guard_interval = GUARD_INTERVAL_1_8;
+		break;
+	case 3:
+		p->u.ofdm.guard_interval = GUARD_INTERVAL_1_4;
+		break;
+	}
+
+	switch ((buf[0] >> 4) & 7) {
+	case 0:
+		p->u.ofdm.hierarchy_information = HIERARCHY_NONE;
+		break;
+	case 1:
+		p->u.ofdm.hierarchy_information = HIERARCHY_1;
+		break;
+	case 2:
+		p->u.ofdm.hierarchy_information = HIERARCHY_2;
+		break;
+	case 3:
+		p->u.ofdm.hierarchy_information = HIERARCHY_4;
+		break;
+	}
+
+	switch ((buf[2] >> 0) & 7) {
+	case 0:
+		p->u.ofdm.code_rate_HP = FEC_1_2;
+		break;
+	case 1:
+		p->u.ofdm.code_rate_HP = FEC_2_3;
+		break;
+	case 2:
+		p->u.ofdm.code_rate_HP = FEC_3_4;
+		break;
+	case 3:
+		p->u.ofdm.code_rate_HP = FEC_5_6;
+		break;
+	case 4:
+		p->u.ofdm.code_rate_HP = FEC_7_8;
+		break;
+	}
+
+	switch ((buf[2] >> 3) & 7) {
+	case 0:
+		p->u.ofdm.code_rate_LP = FEC_1_2;
+		break;
+	case 1:
+		p->u.ofdm.code_rate_LP = FEC_2_3;
+		break;
+	case 2:
+		p->u.ofdm.code_rate_LP = FEC_3_4;
+		break;
+	case 3:
+		p->u.ofdm.code_rate_LP = FEC_5_6;
+		break;
+	case 4:
+		p->u.ofdm.code_rate_LP = FEC_7_8;
+		break;
+	}
+
+	switch ((buf[1] >> 2) & 3) {
+	case 0:
+		p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
+		break;
+	case 1:
+		p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ;
+		break;
+	case 2:
+		p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
+		break;
+	}
+
+	p->inversion = INVERSION_AUTO;
+	p->frequency = state->frequency;
+
+error:
+	return ret;
+}
+
+static int af9013_update_ber_unc(struct dvb_frontend *fe)
+{
+	struct af9013_state *state = fe->demodulator_priv;
+	int ret;
+	u8 buf[3], i;
+	u32 error_bit_count = 0;
+	u32 total_bit_count = 0;
+	u32 abort_packet_count = 0;
+
+	state->ber = 0;
+
+	/* check if error bit count is ready */
+	ret = af9013_read_reg_bits(state, 0xd391, 4, 1, &buf[0]);
+	if (ret)
+		goto error;
+	if (!buf[0])
+		goto exit;
+
+	/* get RSD packet abort count */
+	for (i = 0; i < 2; i++) {
+		ret = af9013_read_reg(state, 0xd38a + i, &buf[i]);
+		if (ret)
+			goto error;
+	}
+	abort_packet_count = (buf[1] << 8) + buf[0];
+
+	/* get error bit count */
+	for (i = 0; i < 3; i++) {
+		ret = af9013_read_reg(state, 0xd387 + i, &buf[i]);
+		if (ret)
+			goto error;
+	}
+	error_bit_count = (buf[2] << 16) + (buf[1] << 8) + buf[0];
+	error_bit_count = error_bit_count - abort_packet_count * 8 * 8;
+
+	/* get used RSD counting period (10000 RSD packets used) */
+	for (i = 0; i < 2; i++) {
+		ret = af9013_read_reg(state, 0xd385 + i, &buf[i]);
+		if (ret)
+			goto error;
+	}
+	total_bit_count = (buf[1] << 8) + buf[0];
+	total_bit_count = total_bit_count - abort_packet_count;
+	total_bit_count = total_bit_count * 204 * 8;
+
+	if (total_bit_count)
+		state->ber = error_bit_count * 1000000000 / total_bit_count;
+
+	state->ucblocks += abort_packet_count;
+
+	deb_info("%s: err bits:%d total bits:%d abort count:%d\n", __func__,
+		error_bit_count, total_bit_count, abort_packet_count);
+
+	/* set BER counting range */
+	ret = af9013_write_reg(state, 0xd385, 10000 & 0xff);
+	if (ret)
+		goto error;
+	ret = af9013_write_reg(state, 0xd386, 10000 >> 8);
+	if (ret)
+		goto error;
+	/* reset and start BER counter */
+	ret = af9013_write_reg_bits(state, 0xd391, 4, 1, 1);
+	if (ret)
+		goto error;
+
+exit:
+error:
+	return ret;
+}
+
+static int af9013_update_snr(struct dvb_frontend *fe)
+{
+	struct af9013_state *state = fe->demodulator_priv;
+	int ret;
+	u8 buf[3], i, len;
+	u32 quant = 0;
+	struct snr_table *snr_table;
+
+	/* check if quantizer ready (for snr) */
+	ret = af9013_read_reg_bits(state, 0xd2e1, 3, 1, &buf[0]);
+	if (ret)
+		goto error;
+	if (buf[0]) {
+		/* quantizer ready - read it */
+		for (i = 0; i < 3; i++) {
+			ret = af9013_read_reg(state, 0xd2e3 + i, &buf[i]);
+			if (ret)
+				goto error;
+		}
+		quant = (buf[2] << 16) + (buf[1] << 8) + buf[0];
+
+		/* read current constellation */
+		ret = af9013_read_reg(state, 0xd3c1, &buf[0]);
+		if (ret)
+			goto error;
+
+		switch ((buf[0] >> 6) & 3) {
+		case 0:
+			len = ARRAY_SIZE(qpsk_snr_table);
+			snr_table = qpsk_snr_table;
+			break;
+		case 1:
+			len = ARRAY_SIZE(qam16_snr_table);
+			snr_table = qam16_snr_table;
+			break;
+		case 2:
+			len = ARRAY_SIZE(qam64_snr_table);
+			snr_table = qam64_snr_table;
+			break;
+		default:
+			len = 0;
+			break;
+		}
+
+		if (len) {
+			for (i = 0; i < len; i++) {
+				if (quant < snr_table[i].val) {
+					state->snr = snr_table[i].snr * 10;
+					break;
+				}
+			}
+		}
+
+		/* set quantizer super frame count */
+		ret = af9013_write_reg(state, 0xd2e2, 1);
+		if (ret)
+			goto error;
+
+		/* check quantizer availability */
+		for (i = 0; i < 10; i++) {
+			msleep(10);
+			ret = af9013_read_reg_bits(state, 0xd2e6, 0, 1,
+				&buf[0]);
+			if (ret)
+				goto error;
+			if (!buf[0])
+				break;
+		}
+
+		/* reset quantizer */
+		ret = af9013_write_reg_bits(state, 0xd2e1, 3, 1, 1);
+		if (ret)
+			goto error;
+	}
+
+error:
+	return ret;
+}
+
+static int af9013_update_signal_strength(struct dvb_frontend *fe)
+{
+	struct af9013_state *state = fe->demodulator_priv;
+	int ret;
+	u8 tmp0;
+	u8 rf_gain, rf_50, rf_80, if_gain, if_50, if_80;
+	int signal_strength;
+
+	deb_info("%s\n", __func__);
+
+	state->signal_strength = 0;
+
+	ret = af9013_read_reg_bits(state, 0x9bee, 0, 1, &tmp0);
+	if (ret)
+		goto error;
+	if (tmp0) {
+		ret = af9013_read_reg(state, 0x9bbd, &rf_50);
+		if (ret)
+			goto error;
+		ret = af9013_read_reg(state, 0x9bd0, &rf_80);
+		if (ret)
+			goto error;
+		ret = af9013_read_reg(state, 0x9be2, &if_50);
+		if (ret)
+			goto error;
+		ret = af9013_read_reg(state, 0x9be4, &if_80);
+		if (ret)
+			goto error;
+		ret = af9013_read_reg(state, 0xd07c, &rf_gain);
+		if (ret)
+			goto error;
+		ret = af9013_read_reg(state, 0xd07d, &if_gain);
+		if (ret)
+			goto error;
+		signal_strength = (0xffff / (9 * (rf_50 + if_50) - \
+			11 * (rf_80 + if_80))) * (10 * (rf_gain + if_gain) - \
+			11 * (rf_80 + if_80));
+		if (signal_strength < 0)
+			signal_strength = 0;
+		else if (signal_strength > 0xffff)
+			signal_strength = 0xffff;
+
+		state->signal_strength = signal_strength;
+	}
+
+error:
+	return ret;
+}
+
+static int af9013_update_statistics(struct dvb_frontend *fe)
+{
+	struct af9013_state *state = fe->demodulator_priv;
+	int ret;
+
+	if (time_before(jiffies, state->next_statistics_check))
+		return 0;
+
+	/* set minimum statistic update interval */
+	state->next_statistics_check = jiffies + msecs_to_jiffies(1200);
+
+	ret = af9013_update_signal_strength(fe);
+	if (ret)
+		goto error;
+	ret = af9013_update_snr(fe);
+	if (ret)
+		goto error;
+	ret = af9013_update_ber_unc(fe);
+	if (ret)
+		goto error;
+
+error:
+	return ret;
+}
+
+static int af9013_get_tune_settings(struct dvb_frontend *fe,
+	struct dvb_frontend_tune_settings *fesettings)
+{
+	fesettings->min_delay_ms = 800;
+	fesettings->step_size = 0;
+	fesettings->max_drift = 0;
+
+	return 0;
+}
+
+static int af9013_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+	struct af9013_state *state = fe->demodulator_priv;
+	int ret = 0;
+	u8 tmp;
+	*status = 0;
+
+	/* TPS lock */
+	ret = af9013_read_reg_bits(state, 0xd330, 3, 1, &tmp);
+	if (ret)
+		goto error;
+	if (tmp)
+		*status |= FE_HAS_VITERBI | FE_HAS_CARRIER | FE_HAS_SIGNAL;
+
+	/* MPEG2 lock */
+	ret = af9013_read_reg_bits(state, 0xd507, 6, 1, &tmp);
+	if (ret)
+		goto error;
+	if (tmp)
+		*status |= FE_HAS_SYNC | FE_HAS_LOCK;
+
+	if (!*status & FE_HAS_SIGNAL) {
+		/* AGC lock */
+		ret = af9013_read_reg_bits(state, 0xd1a0, 6, 1, &tmp);
+		if (ret)
+			goto error;
+		if (tmp)
+			*status |= FE_HAS_SIGNAL;
+	}
+
+	if (!*status & FE_HAS_CARRIER) {
+		/* CFO lock */
+		ret = af9013_read_reg_bits(state, 0xd333, 7, 1, &tmp);
+		if (ret)
+			goto error;
+		if (tmp)
+			*status |= FE_HAS_CARRIER;
+	}
+
+	if (!*status & FE_HAS_CARRIER) {
+		/* SFOE lock */
+		ret = af9013_read_reg_bits(state, 0xd334, 6, 1, &tmp);
+		if (ret)
+			goto error;
+		if (tmp)
+			*status |= FE_HAS_CARRIER;
+	}
+
+	ret = af9013_update_statistics(fe);
+
+error:
+	return ret;
+}
+
+
+static int af9013_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+	struct af9013_state *state = fe->demodulator_priv;
+	int ret;
+	ret = af9013_update_statistics(fe);
+	*ber = state->ber;
+	return ret;
+}
+
+static int af9013_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+	struct af9013_state *state = fe->demodulator_priv;
+	int ret;
+	ret = af9013_update_statistics(fe);
+	*strength = state->signal_strength;
+	return ret;
+}
+
+static int af9013_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+	struct af9013_state *state = fe->demodulator_priv;
+	int ret;
+	ret = af9013_update_statistics(fe);
+	*snr = state->snr;
+	return ret;
+}
+
+static int af9013_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+	struct af9013_state *state = fe->demodulator_priv;
+	int ret;
+	ret = af9013_update_statistics(fe);
+	*ucblocks = state->ucblocks;
+	return ret;
+}
+
+static int af9013_sleep(struct dvb_frontend *fe)
+{
+	struct af9013_state *state = fe->demodulator_priv;
+	int ret;
+	deb_info("%s\n", __func__);
+
+	ret = af9013_lock_led(state, 0);
+	if (ret)
+		goto error;
+
+	ret = af9013_power_ctrl(state, 0);
+error:
+	return ret;
+}
+
+static int af9013_init(struct dvb_frontend *fe)
+{
+	struct af9013_state *state = fe->demodulator_priv;
+	int ret, i, len;
+	u8 tmp0, tmp1;
+	struct regdesc *init;
+	deb_info("%s\n", __func__);
+
+	/* reset OFDM */
+	ret = af9013_reset(state, 0);
+	if (ret)
+		goto error;
+
+	/* power on */
+	ret = af9013_power_ctrl(state, 1);
+	if (ret)
+		goto error;
+
+	/* enable ADC */
+	ret = af9013_write_reg(state, 0xd73a, 0xa4);
+	if (ret)
+		goto error;
+
+	/* write API version to firmware */
+	for (i = 0; i < sizeof(state->config.api_version); i++) {
+		ret = af9013_write_reg(state, 0x9bf2 + i,
+			state->config.api_version[i]);
+		if (ret)
+			goto error;
+	}
+
+	/* program ADC control */
+	ret = af9013_set_adc_ctrl(state);
+	if (ret)
+		goto error;
+
+	/* set I2C master clock */
+	ret = af9013_write_reg(state, 0xd416, 0x14);
+	if (ret)
+		goto error;
+
+	/* set 16 embx */
+	ret = af9013_write_reg_bits(state, 0xd700, 1, 1, 1);
+	if (ret)
+		goto error;
+
+	/* set no trigger */
+	ret = af9013_write_reg_bits(state, 0xd700, 2, 1, 0);
+	if (ret)
+		goto error;
+
+	/* set read-update bit for constellation */
+	ret = af9013_write_reg_bits(state, 0xd371, 1, 1, 1);
+	if (ret)
+		goto error;
+
+	/* enable FEC monitor */
+	ret = af9013_write_reg_bits(state, 0xd392, 1, 1, 1);
+	if (ret)
+		goto error;
+
+	/* load OFSM settings */
+	deb_info("%s: load ofsm settings\n", __func__);
+	len = ARRAY_SIZE(ofsm_init);
+	init = ofsm_init;
+	for (i = 0; i < len; i++) {
+		ret = af9013_write_reg_bits(state, init[i].addr, init[i].pos,
+			init[i].len, init[i].val);
+		if (ret)
+			goto error;
+	}
+
+	/* load tuner specific settings */
+	deb_info("%s: load tuner specific settings\n", __func__);
+	switch (state->config.tuner) {
+	case AF9013_TUNER_MXL5003D:
+		len = ARRAY_SIZE(tuner_init_mxl5003d);
+		init = tuner_init_mxl5003d;
+		break;
+	case AF9013_TUNER_MXL5005D:
+	case AF9013_TUNER_MXL5005R:
+		len = ARRAY_SIZE(tuner_init_mxl5005);
+		init = tuner_init_mxl5005;
+		break;
+	case AF9013_TUNER_ENV77H11D5:
+		len = ARRAY_SIZE(tuner_init_env77h11d5);
+		init = tuner_init_env77h11d5;
+		break;
+	case AF9013_TUNER_MT2060:
+		len = ARRAY_SIZE(tuner_init_mt2060);
+		init = tuner_init_mt2060;
+		break;
+	case AF9013_TUNER_MC44S803:
+		len = ARRAY_SIZE(tuner_init_mc44s803);
+		init = tuner_init_mc44s803;
+		break;
+	case AF9013_TUNER_QT1010:
+	case AF9013_TUNER_QT1010A:
+		len = ARRAY_SIZE(tuner_init_qt1010);
+		init = tuner_init_qt1010;
+		break;
+	case AF9013_TUNER_MT2060_2:
+		len = ARRAY_SIZE(tuner_init_mt2060_2);
+		init = tuner_init_mt2060_2;
+		break;
+	case AF9013_TUNER_TDA18271:
+		len = ARRAY_SIZE(tuner_init_tda18271);
+		init = tuner_init_tda18271;
+		break;
+	case AF9013_TUNER_UNKNOWN:
+	default:
+		len = ARRAY_SIZE(tuner_init_unknown);
+		init = tuner_init_unknown;
+		break;
+	}
+
+	for (i = 0; i < len; i++) {
+		ret = af9013_write_reg_bits(state, init[i].addr, init[i].pos,
+			init[i].len, init[i].val);
+		if (ret)
+			goto error;
+	}
+
+	/* set TS mode */
+	deb_info("%s: setting ts mode\n", __func__);
+	tmp0 = 0; /* parallel mode */
+	tmp1 = 0; /* serial mode */
+	switch (state->config.output_mode) {
+	case AF9013_OUTPUT_MODE_PARALLEL:
+		tmp0 = 1;
+		break;
+	case AF9013_OUTPUT_MODE_SERIAL:
+		tmp1 = 1;
+		break;
+	case AF9013_OUTPUT_MODE_USB:
+		/* usb mode for AF9015 */
+	default:
+		break;
+	}
+	ret = af9013_write_reg_bits(state, 0xd500, 1, 1, tmp0); /* parallel */
+	if (ret)
+		goto error;
+	ret = af9013_write_reg_bits(state, 0xd500, 2, 1, tmp1); /* serial */
+	if (ret)
+		goto error;
+
+	/* enable lock led */
+	ret = af9013_lock_led(state, 1);
+	if (ret)
+		goto error;
+
+error:
+	return ret;
+}
+
+static struct dvb_frontend_ops af9013_ops;
+
+static int af9013_download_firmware(struct af9013_state *state)
+{
+	int i, len, packets, remainder, ret;
+	const struct firmware *fw;
+	u16 addr = 0x5100; /* firmware start address */
+	u16 checksum = 0;
+	u8 val;
+	u8 fw_params[4];
+	u8 *data;
+	u8 *fw_file = AF9013_DEFAULT_FIRMWARE;
+
+	msleep(100);
+	/* check whether firmware is already running */
+	ret = af9013_read_reg(state, 0x98be, &val);
+	if (ret)
+		goto error;
+	else
+		deb_info("%s: firmware status:%02x\n", __func__, val);
+
+	if (val == 0x0c) /* fw is running, no need for download */
+		goto exit;
+
+	info("found a '%s' in cold state, will try to load a firmware",
+		af9013_ops.info.name);
+
+	/* request the firmware, this will block and timeout */
+	ret = request_firmware(&fw, fw_file,  &state->i2c->dev);
+	if (ret) {
+		err("did not find the firmware file. (%s) "
+			"Please see linux/Documentation/dvb/ for more details" \
+			" on firmware-problems. (%d)",
+			fw_file, ret);
+		goto error;
+	}
+
+	info("downloading firmware from file '%s'", fw_file);
+
+	/* calc checksum */
+	for (i = 0; i < fw->size; i++)
+		checksum += fw->data[i];
+
+	fw_params[0] = checksum >> 8;
+	fw_params[1] = checksum & 0xff;
+	fw_params[2] = fw->size >> 8;
+	fw_params[3] = fw->size & 0xff;
+
+	/* write fw checksum & size */
+	ret = af9013_write_ofsm_regs(state, 0x50fc,
+		fw_params, sizeof(fw_params));
+	if (ret)
+		goto error_release;
+
+	#define FW_PACKET_MAX_DATA  16
+
+	packets = fw->size / FW_PACKET_MAX_DATA;
+	remainder = fw->size % FW_PACKET_MAX_DATA;
+	len = FW_PACKET_MAX_DATA;
+	for (i = 0; i <= packets; i++) {
+		if (i == packets)  /* set size of the last packet */
+			len = remainder;
+
+		data = (u8 *)(fw->data + i * FW_PACKET_MAX_DATA);
+		ret = af9013_write_ofsm_regs(state, addr, data, len);
+		addr += FW_PACKET_MAX_DATA;
+
+		if (ret) {
+			err("firmware download failed at %d with %d", i, ret);
+			goto error_release;
+		}
+	}
+
+	/* request boot firmware */
+	ret = af9013_write_reg(state, 0xe205, 1);
+	if (ret)
+		goto error_release;
+
+	for (i = 0; i < 15; i++) {
+		msleep(100);
+
+		/* check firmware status */
+		ret = af9013_read_reg(state, 0x98be, &val);
+		if (ret)
+			goto error_release;
+
+		deb_info("%s: firmware status:%02x\n", __func__, val);
+
+		if (val == 0x0c || val == 0x04) /* success or fail */
+			break;
+	}
+
+	if (val == 0x04) {
+		err("firmware did not run");
+		ret = -1;
+	} else if (val != 0x0c) {
+		err("firmware boot timeout");
+		ret = -1;
+	}
+
+error_release:
+	release_firmware(fw);
+error:
+exit:
+	if (!ret)
+		info("found a '%s' in warm state.", af9013_ops.info.name);
+	return ret;
+}
+
+static int af9013_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+	int ret;
+	struct af9013_state *state = fe->demodulator_priv;
+	deb_info("%s: enable:%d\n", __func__, enable);
+
+	if (state->config.output_mode == AF9013_OUTPUT_MODE_USB)
+		ret = af9013_write_reg_bits(state, 0xd417, 3, 1, enable);
+	else
+		ret = af9013_write_reg_bits(state, 0xd607, 2, 1, enable);
+
+	return ret;
+}
+
+static void af9013_release(struct dvb_frontend *fe)
+{
+	struct af9013_state *state = fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops af9013_ops;
+
+struct dvb_frontend *af9013_attach(const struct af9013_config *config,
+	struct i2c_adapter *i2c)
+{
+	int ret;
+	struct af9013_state *state = NULL;
+	u8 buf[3], i;
+
+	/* allocate memory for the internal state */
+	state = kzalloc(sizeof(struct af9013_state), GFP_KERNEL);
+	if (state == NULL)
+		goto error;
+
+	/* setup the state */
+	state->i2c = i2c;
+	memcpy(&state->config, config, sizeof(struct af9013_config));
+
+	/* chip version */
+	ret = af9013_read_reg_bits(state, 0xd733, 4, 4, &buf[2]);
+	if (ret)
+		goto error;
+
+	/* ROM version */
+	for (i = 0; i < 2; i++) {
+		ret = af9013_read_reg(state, 0x116b + i, &buf[i]);
+		if (ret)
+			goto error;
+	}
+	deb_info("%s: chip version:%d ROM version:%d.%d\n", __func__,
+		buf[2], buf[0], buf[1]);
+
+	/* download firmware */
+	if (state->config.output_mode != AF9013_OUTPUT_MODE_USB) {
+		ret = af9013_download_firmware(state);
+		if (ret)
+			goto error;
+	}
+
+	/* firmware version */
+	for (i = 0; i < 3; i++) {
+		ret = af9013_read_reg(state, 0x5103 + i, &buf[i]);
+		if (ret)
+			goto error;
+	}
+	info("firmware version:%d.%d.%d", buf[0], buf[1], buf[2]);
+
+	/* settings for mp2if */
+	if (state->config.output_mode == AF9013_OUTPUT_MODE_USB) {
+		/* AF9015 split PSB to 1.5k + 0.5k */
+		ret = af9013_write_reg_bits(state, 0xd50b, 2, 1, 1);
+	} else {
+		/* AF9013 change the output bit to data7 */
+		ret = af9013_write_reg_bits(state, 0xd500, 3, 1, 1);
+		if (ret)
+			goto error;
+		/* AF9013 set mpeg to full speed */
+		ret = af9013_write_reg_bits(state, 0xd502, 4, 1, 1);
+	}
+	if (ret)
+		goto error;
+	ret = af9013_write_reg_bits(state, 0xd520, 4, 1, 1);
+	if (ret)
+		goto error;
+
+	/* set GPIOs */
+	for (i = 0; i < sizeof(state->config.gpio); i++) {
+		ret = af9013_set_gpio(state, i, state->config.gpio[i]);
+		if (ret)
+			goto error;
+	}
+
+	/* create dvb_frontend */
+	memcpy(&state->frontend.ops, &af9013_ops,
+		sizeof(struct dvb_frontend_ops));
+	state->frontend.demodulator_priv = state;
+
+	return &state->frontend;
+error:
+	kfree(state);
+	return NULL;
+}
+EXPORT_SYMBOL(af9013_attach);
+
+static struct dvb_frontend_ops af9013_ops = {
+	.info = {
+		.name = "Afatech AF9013 DVB-T",
+		.type = FE_OFDM,
+		.frequency_min = 174000000,
+		.frequency_max = 862000000,
+		.frequency_stepsize = 250000,
+		.frequency_tolerance = 0,
+		.caps =
+			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_HIERARCHY_AUTO |
+			FE_CAN_RECOVER |
+			FE_CAN_MUTE_TS
+	},
+
+	.release = af9013_release,
+	.init = af9013_init,
+	.sleep = af9013_sleep,
+	.i2c_gate_ctrl = af9013_i2c_gate_ctrl,
+
+	.set_frontend = af9013_set_frontend,
+	.get_frontend = af9013_get_frontend,
+
+	.get_tune_settings = af9013_get_tune_settings,
+
+	.read_status = af9013_read_status,
+	.read_ber = af9013_read_ber,
+	.read_signal_strength = af9013_read_signal_strength,
+	.read_snr = af9013_read_snr,
+	.read_ucblocks = af9013_read_ucblocks,
+};
+
+module_param_named(debug, af9013_debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Afatech AF9013 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");

+ 107 - 0
drivers/media/dvb/frontends/af9013.h

@@ -0,0 +1,107 @@
+/*
+ * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ *    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 _AF9013_H_
+#define _AF9013_H_
+
+#include <linux/dvb/frontend.h>
+
+enum af9013_ts_mode {
+	AF9013_OUTPUT_MODE_PARALLEL,
+	AF9013_OUTPUT_MODE_SERIAL,
+	AF9013_OUTPUT_MODE_USB, /* only for AF9015 */
+};
+
+enum af9013_tuner {
+	AF9013_TUNER_MXL5003D   =   3, /* MaxLinear */
+	AF9013_TUNER_MXL5005D   =  13, /* MaxLinear */
+	AF9013_TUNER_MXL5005R   =  30, /* MaxLinear */
+	AF9013_TUNER_ENV77H11D5 = 129, /* Panasonic */
+	AF9013_TUNER_MT2060     = 130, /* Microtune */
+	AF9013_TUNER_MC44S803   = 133, /* Freescale */
+	AF9013_TUNER_QT1010     = 134, /* Quantek */
+	AF9013_TUNER_UNKNOWN    = 140, /* for can tuners ? */
+	AF9013_TUNER_MT2060_2   = 147, /* Microtune */
+	AF9013_TUNER_TDA18271   = 156, /* NXP */
+	AF9013_TUNER_QT1010A    = 162, /* Quantek */
+};
+
+/* AF9013/5 GPIOs (mostly guessed)
+   demod#1-gpio#0 - set demod#2 i2c-addr for dual devices
+   demod#1-gpio#1 - xtal setting (?)
+   demod#1-gpio#3 - tuner#1
+   demod#2-gpio#0 - tuner#2
+   demod#2-gpio#1 - xtal setting (?)
+*/
+#define AF9013_GPIO_ON (1 << 0)
+#define AF9013_GPIO_EN (1 << 1)
+#define AF9013_GPIO_O  (1 << 2)
+#define AF9013_GPIO_I  (1 << 3)
+
+#define AF9013_GPIO_LO (AF9013_GPIO_ON|AF9013_GPIO_EN)
+#define AF9013_GPIO_HI (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O)
+
+#define AF9013_GPIO_TUNER_ON  (AF9013_GPIO_ON|AF9013_GPIO_EN)
+#define AF9013_GPIO_TUNER_OFF (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O)
+
+struct af9013_config {
+	/* demodulator's I2C address */
+	u8 demod_address;
+
+	/* frequencies in kHz */
+	u32 adc_clock;
+
+	/* tuner ID */
+	u8 tuner;
+
+	/* tuner IF */
+	u16 tuner_if;
+
+	/* TS data output mode */
+	u8 output_mode:2;
+
+	/* RF spectrum inversion */
+	u8 rf_spec_inv:1;
+
+	/* API version */
+	u8 api_version[4];
+
+	/* GPIOs */
+	u8 gpio[4];
+};
+
+
+#if defined(CONFIG_DVB_AF9013) || \
+	(defined(CONFIG_DVB_AF9013_MODULE) && defined(MODULE))
+extern struct dvb_frontend *af9013_attach(const struct af9013_config *config,
+	struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *af9013_attach(
+const struct af9013_config *config, struct i2c_adapter *i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif /* CONFIG_DVB_AF9013 */
+
+#endif /* _AF9013_H_ */

+ 869 - 0
drivers/media/dvb/frontends/af9013_priv.h

@@ -0,0 +1,869 @@
+/*
+ * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ *    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 _AF9013_PRIV_
+#define _AF9013_PRIV_
+
+#define LOG_PREFIX "af9013"
+extern int af9013_debug;
+
+#define dprintk(var, level, args...) \
+	    do { if ((var & level)) printk(args); } while (0)
+
+#define debug_dump(b, l, func) {\
+	int loop_; \
+	for (loop_ = 0; loop_ < l; loop_++) \
+		func("%02x ", b[loop_]); \
+	func("\n");\
+}
+
+#define deb_info(args...) dprintk(af9013_debug, 0x01, args)
+
+#undef err
+#define err(f, arg...)  printk(KERN_ERR     LOG_PREFIX": " f "\n" , ## arg)
+#undef info
+#define info(f, arg...) printk(KERN_INFO    LOG_PREFIX": " f "\n" , ## arg)
+#undef warn
+#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
+
+#define AF9013_DEFAULT_FIRMWARE     "dvb-fe-af9013.fw"
+
+struct regdesc {
+	u16 addr;
+	u8  pos:4;
+	u8  len:4;
+	u8  val;
+};
+
+struct snr_table {
+	u32 val;
+	u8 snr;
+};
+
+/* QPSK SNR lookup table */
+static struct snr_table qpsk_snr_table[] = {
+	{ 0x0b4771,  0 },
+	{ 0x0c1aed,  1 },
+	{ 0x0d0d27,  2 },
+	{ 0x0e4d19,  3 },
+	{ 0x0e5da8,  4 },
+	{ 0x107097,  5 },
+	{ 0x116975,  6 },
+	{ 0x1252d9,  7 },
+	{ 0x131fa4,  8 },
+	{ 0x13d5e1,  9 },
+	{ 0x148e53, 10 },
+	{ 0x15358b, 11 },
+	{ 0x15dd29, 12 },
+	{ 0x168112, 13 },
+	{ 0x170b61, 14 },
+	{ 0xffffff, 15 },
+};
+
+/* QAM16 SNR lookup table */
+static struct snr_table qam16_snr_table[] = {
+	{ 0x05eb62,  5 },
+	{ 0x05fecf,  6 },
+	{ 0x060b80,  7 },
+	{ 0x062501,  8 },
+	{ 0x064865,  9 },
+	{ 0x069604, 10 },
+	{ 0x06f356, 11 },
+	{ 0x07706a, 12 },
+	{ 0x0804d3, 13 },
+	{ 0x089d1a, 14 },
+	{ 0x093e3d, 15 },
+	{ 0x09e35d, 16 },
+	{ 0x0a7c3c, 17 },
+	{ 0x0afaf8, 18 },
+	{ 0x0b719d, 19 },
+	{ 0xffffff, 20 },
+};
+
+/* QAM64 SNR lookup table */
+static struct snr_table qam64_snr_table[] = {
+	{ 0x03109b, 12 },
+	{ 0x0310d4, 13 },
+	{ 0x031920, 14 },
+	{ 0x0322d0, 15 },
+	{ 0x0339fc, 16 },
+	{ 0x0364a1, 17 },
+	{ 0x038bcc, 18 },
+	{ 0x03c7d3, 19 },
+	{ 0x0408cc, 20 },
+	{ 0x043bed, 21 },
+	{ 0x048061, 22 },
+	{ 0x04be95, 23 },
+	{ 0x04fa7d, 24 },
+	{ 0x052405, 25 },
+	{ 0x05570d, 26 },
+	{ 0xffffff, 27 },
+};
+
+static struct regdesc ofsm_init[] = {
+	{ 0xd73a, 0, 8, 0xa1 },
+	{ 0xd73b, 0, 8, 0x1f },
+	{ 0xd73c, 4, 4, 0x0a },
+	{ 0xd732, 3, 1, 0x00 },
+	{ 0xd731, 4, 2, 0x03 },
+	{ 0xd73d, 7, 1, 0x01 },
+	{ 0xd740, 0, 1, 0x00 },
+	{ 0xd740, 1, 1, 0x00 },
+	{ 0xd740, 2, 1, 0x00 },
+	{ 0xd740, 3, 1, 0x01 },
+	{ 0xd3c1, 4, 1, 0x01 },
+	{ 0xd3a2, 0, 8, 0x00 },
+	{ 0xd3a3, 0, 8, 0x04 },
+	{ 0xd305, 0, 8, 0x32 },
+	{ 0xd306, 0, 8, 0x10 },
+	{ 0xd304, 0, 8, 0x04 },
+	{ 0x9112, 0, 1, 0x01 },
+	{ 0x911d, 0, 1, 0x01 },
+	{ 0x911a, 0, 1, 0x01 },
+	{ 0x911b, 0, 1, 0x01 },
+	{ 0x9bce, 0, 4, 0x02 },
+	{ 0x9116, 0, 1, 0x01 },
+	{ 0x9bd1, 0, 1, 0x01 },
+	{ 0xd2e0, 0, 8, 0xd0 },
+	{ 0xd2e9, 0, 4, 0x0d },
+	{ 0xd38c, 0, 8, 0xfc },
+	{ 0xd38d, 0, 8, 0x00 },
+	{ 0xd38e, 0, 8, 0x7e },
+	{ 0xd38f, 0, 8, 0x00 },
+	{ 0xd390, 0, 8, 0x2f },
+	{ 0xd145, 4, 1, 0x01 },
+	{ 0xd1a9, 4, 1, 0x01 },
+	{ 0xd158, 5, 3, 0x01 },
+	{ 0xd159, 0, 6, 0x06 },
+	{ 0xd167, 0, 8, 0x00 },
+	{ 0xd168, 0, 4, 0x07 },
+	{ 0xd1c3, 5, 3, 0x00 },
+	{ 0xd1c4, 0, 6, 0x00 },
+	{ 0xd1c5, 0, 7, 0x10 },
+	{ 0xd1c6, 0, 3, 0x02 },
+	{ 0xd080, 2, 5, 0x03 },
+	{ 0xd081, 4, 4, 0x09 },
+	{ 0xd098, 4, 4, 0x0f },
+	{ 0xd098, 0, 4, 0x03 },
+	{ 0xdbc0, 3, 1, 0x01 },
+	{ 0xdbc0, 4, 1, 0x01 },
+	{ 0xdbc7, 0, 8, 0x08 },
+	{ 0xdbc8, 4, 4, 0x00 },
+	{ 0xdbc9, 0, 5, 0x01 },
+	{ 0xd280, 0, 8, 0xe0 },
+	{ 0xd281, 0, 8, 0xff },
+	{ 0xd282, 0, 8, 0xff },
+	{ 0xd283, 0, 8, 0xc3 },
+	{ 0xd284, 0, 8, 0xff },
+	{ 0xd285, 0, 4, 0x01 },
+	{ 0xd0f0, 0, 7, 0x1a },
+	{ 0xd0f1, 4, 1, 0x01 },
+	{ 0xd0f2, 0, 8, 0x0c },
+	{ 0xd103, 0, 4, 0x08 },
+	{ 0xd0f8, 0, 7, 0x20 },
+	{ 0xd111, 5, 1, 0x00 },
+	{ 0xd111, 6, 1, 0x00 },
+	{ 0x910b, 0, 8, 0x0a },
+	{ 0x9115, 0, 8, 0x02 },
+	{ 0x910c, 0, 8, 0x02 },
+	{ 0x910d, 0, 8, 0x08 },
+	{ 0x910e, 0, 8, 0x0a },
+	{ 0x9bf6, 0, 8, 0x06 },
+	{ 0x9bf8, 0, 8, 0x02 },
+	{ 0x9bf7, 0, 8, 0x05 },
+	{ 0x9bf9, 0, 8, 0x0f },
+	{ 0x9bfc, 0, 8, 0x13 },
+	{ 0x9bd3, 0, 8, 0xff },
+	{ 0x9bbe, 0, 1, 0x01 },
+	{ 0x9bcc, 0, 1, 0x01 },
+};
+
+/* Panasonic ENV77H11D5 tuner init
+   AF9013_TUNER_ENV77H11D5 = 129 */
+static struct regdesc tuner_init_env77h11d5[] = {
+	{ 0x9bd5, 0, 8, 0x01 },
+	{ 0x9bd6, 0, 8, 0x03 },
+	{ 0x9bbe, 0, 8, 0x01 },
+	{ 0xd1a0, 1, 1, 0x01 },
+	{ 0xd000, 0, 1, 0x01 },
+	{ 0xd000, 1, 1, 0x00 },
+	{ 0xd001, 1, 1, 0x01 },
+	{ 0xd001, 0, 1, 0x00 },
+	{ 0xd001, 5, 1, 0x00 },
+	{ 0xd002, 0, 5, 0x19 },
+	{ 0xd003, 0, 5, 0x1a },
+	{ 0xd004, 0, 5, 0x19 },
+	{ 0xd005, 0, 5, 0x1a },
+	{ 0xd00e, 0, 5, 0x10 },
+	{ 0xd00f, 0, 3, 0x04 },
+	{ 0xd00f, 3, 3, 0x05 },
+	{ 0xd010, 0, 3, 0x04 },
+	{ 0xd010, 3, 3, 0x05 },
+	{ 0xd016, 4, 4, 0x03 },
+	{ 0xd01f, 0, 6, 0x0a },
+	{ 0xd020, 0, 6, 0x0a },
+	{ 0x9bda, 0, 8, 0x00 },
+	{ 0x9be3, 0, 8, 0x00 },
+	{ 0xd015, 0, 8, 0x50 },
+	{ 0xd016, 0, 1, 0x00 },
+	{ 0xd044, 0, 8, 0x46 },
+	{ 0xd045, 0, 1, 0x00 },
+	{ 0xd008, 0, 8, 0xdf },
+	{ 0xd009, 0, 2, 0x02 },
+	{ 0xd006, 0, 8, 0x44 },
+	{ 0xd007, 0, 2, 0x01 },
+	{ 0xd00c, 0, 8, 0xeb },
+	{ 0xd00d, 0, 2, 0x02 },
+	{ 0xd00a, 0, 8, 0xf4 },
+	{ 0xd00b, 0, 2, 0x01 },
+	{ 0x9bba, 0, 8, 0xf9 },
+	{ 0x9bc3, 0, 8, 0xdf },
+	{ 0x9bc4, 0, 8, 0x02 },
+	{ 0x9bc5, 0, 8, 0xeb },
+	{ 0x9bc6, 0, 8, 0x02 },
+	{ 0x9bc9, 0, 8, 0x52 },
+	{ 0xd011, 0, 8, 0x3c },
+	{ 0xd012, 0, 2, 0x01 },
+	{ 0xd013, 0, 8, 0xf7 },
+	{ 0xd014, 0, 2, 0x02 },
+	{ 0xd040, 0, 8, 0x0b },
+	{ 0xd041, 0, 2, 0x02 },
+	{ 0xd042, 0, 8, 0x4d },
+	{ 0xd043, 0, 2, 0x00 },
+	{ 0xd045, 1, 1, 0x00 },
+	{ 0x9bcf, 0, 1, 0x01 },
+	{ 0xd045, 2, 1, 0x01 },
+	{ 0xd04f, 0, 8, 0x9a },
+	{ 0xd050, 0, 1, 0x01 },
+	{ 0xd051, 0, 8, 0x5a },
+	{ 0xd052, 0, 1, 0x01 },
+	{ 0xd053, 0, 8, 0x50 },
+	{ 0xd054, 0, 8, 0x46 },
+	{ 0x9bd7, 0, 8, 0x0a },
+	{ 0x9bd8, 0, 8, 0x14 },
+	{ 0x9bd9, 0, 8, 0x08 },
+};
+
+/* Microtune MT2060 tuner init
+   AF9013_TUNER_MT2060     = 130 */
+static struct regdesc tuner_init_mt2060[] = {
+	{ 0x9bd5, 0, 8, 0x01 },
+	{ 0x9bd6, 0, 8, 0x07 },
+	{ 0xd1a0, 1, 1, 0x01 },
+	{ 0xd000, 0, 1, 0x01 },
+	{ 0xd000, 1, 1, 0x00 },
+	{ 0xd001, 1, 1, 0x01 },
+	{ 0xd001, 0, 1, 0x00 },
+	{ 0xd001, 5, 1, 0x00 },
+	{ 0xd002, 0, 5, 0x19 },
+	{ 0xd003, 0, 5, 0x1a },
+	{ 0xd004, 0, 5, 0x19 },
+	{ 0xd005, 0, 5, 0x1a },
+	{ 0xd00e, 0, 5, 0x10 },
+	{ 0xd00f, 0, 3, 0x04 },
+	{ 0xd00f, 3, 3, 0x05 },
+	{ 0xd010, 0, 3, 0x04 },
+	{ 0xd010, 3, 3, 0x05 },
+	{ 0xd016, 4, 4, 0x03 },
+	{ 0xd01f, 0, 6, 0x0a },
+	{ 0xd020, 0, 6, 0x0a },
+	{ 0x9bda, 0, 8, 0x00 },
+	{ 0x9be3, 0, 8, 0x00 },
+	{ 0x9bbe, 0, 1, 0x00 },
+	{ 0x9bcc, 0, 1, 0x00 },
+	{ 0x9bb9, 0, 8, 0x75 },
+	{ 0x9bcd, 0, 8, 0x24 },
+	{ 0x9bff, 0, 8, 0x30 },
+	{ 0xd015, 0, 8, 0x46 },
+	{ 0xd016, 0, 1, 0x00 },
+	{ 0xd044, 0, 8, 0x46 },
+	{ 0xd045, 0, 1, 0x00 },
+	{ 0xd008, 0, 8, 0x0f },
+	{ 0xd009, 0, 2, 0x02 },
+	{ 0xd006, 0, 8, 0x32 },
+	{ 0xd007, 0, 2, 0x01 },
+	{ 0xd00c, 0, 8, 0x36 },
+	{ 0xd00d, 0, 2, 0x03 },
+	{ 0xd00a, 0, 8, 0x35 },
+	{ 0xd00b, 0, 2, 0x01 },
+	{ 0x9bc7, 0, 8, 0x07 },
+	{ 0x9bc8, 0, 8, 0x90 },
+	{ 0x9bc3, 0, 8, 0x0f },
+	{ 0x9bc4, 0, 8, 0x02 },
+	{ 0x9bc5, 0, 8, 0x36 },
+	{ 0x9bc6, 0, 8, 0x03 },
+	{ 0x9bba, 0, 8, 0xc9 },
+	{ 0x9bc9, 0, 8, 0x79 },
+	{ 0xd011, 0, 8, 0x10 },
+	{ 0xd012, 0, 2, 0x01 },
+	{ 0xd013, 0, 8, 0x45 },
+	{ 0xd014, 0, 2, 0x03 },
+	{ 0xd040, 0, 8, 0x98 },
+	{ 0xd041, 0, 2, 0x00 },
+	{ 0xd042, 0, 8, 0xcf },
+	{ 0xd043, 0, 2, 0x03 },
+	{ 0xd045, 1, 1, 0x00 },
+	{ 0x9bcf, 0, 1, 0x01 },
+	{ 0xd045, 2, 1, 0x01 },
+	{ 0xd04f, 0, 8, 0x9a },
+	{ 0xd050, 0, 1, 0x01 },
+	{ 0xd051, 0, 8, 0x5a },
+	{ 0xd052, 0, 1, 0x01 },
+	{ 0xd053, 0, 8, 0x50 },
+	{ 0xd054, 0, 8, 0x46 },
+	{ 0x9bd7, 0, 8, 0x0a },
+	{ 0x9bd8, 0, 8, 0x14 },
+	{ 0x9bd9, 0, 8, 0x08 },
+	{ 0x9bd0, 0, 8, 0xcc },
+	{ 0x9be4, 0, 8, 0xa0 },
+	{ 0x9bbd, 0, 8, 0x8e },
+	{ 0x9be2, 0, 8, 0x4d },
+	{ 0x9bee, 0, 1, 0x01 },
+};
+
+/* Microtune MT2060 tuner init
+   AF9013_TUNER_MT2060_2   = 147 */
+static struct regdesc tuner_init_mt2060_2[] = {
+	{ 0x9bd5, 0, 8, 0x01 },
+	{ 0x9bd6, 0, 8, 0x06 },
+	{ 0x9bbe, 0, 8, 0x01 },
+	{ 0xd1a0, 1, 1, 0x01 },
+	{ 0xd000, 0, 1, 0x01 },
+	{ 0xd000, 1, 1, 0x00 },
+	{ 0xd001, 1, 1, 0x01 },
+	{ 0xd001, 0, 1, 0x00 },
+	{ 0xd001, 5, 1, 0x00 },
+	{ 0xd002, 0, 5, 0x19 },
+	{ 0xd003, 0, 5, 0x1a },
+	{ 0xd004, 0, 5, 0x19 },
+	{ 0xd005, 0, 5, 0x1a },
+	{ 0xd00e, 0, 5, 0x10 },
+	{ 0xd00f, 0, 3, 0x04 },
+	{ 0xd00f, 3, 3, 0x05 },
+	{ 0xd010, 0, 3, 0x04 },
+	{ 0xd010, 3, 3, 0x05 },
+	{ 0xd016, 4, 4, 0x03 },
+	{ 0xd01f, 0, 6, 0x0a },
+	{ 0xd020, 0, 6, 0x0a },
+	{ 0xd015, 0, 8, 0x46 },
+	{ 0xd016, 0, 1, 0x00 },
+	{ 0xd044, 0, 8, 0x46 },
+	{ 0xd045, 0, 1, 0x00 },
+	{ 0xd008, 0, 8, 0x0f },
+	{ 0xd009, 0, 2, 0x02 },
+	{ 0xd006, 0, 8, 0x32 },
+	{ 0xd007, 0, 2, 0x01 },
+	{ 0xd00c, 0, 8, 0x36 },
+	{ 0xd00d, 0, 2, 0x03 },
+	{ 0xd00a, 0, 8, 0x35 },
+	{ 0xd00b, 0, 2, 0x01 },
+	{ 0x9bc7, 0, 8, 0x07 },
+	{ 0x9bc8, 0, 8, 0x90 },
+	{ 0x9bc3, 0, 8, 0x0f },
+	{ 0x9bc4, 0, 8, 0x02 },
+	{ 0x9bc5, 0, 8, 0x36 },
+	{ 0x9bc6, 0, 8, 0x03 },
+	{ 0x9bba, 0, 8, 0xc9 },
+	{ 0x9bc9, 0, 8, 0x79 },
+	{ 0xd011, 0, 8, 0x10 },
+	{ 0xd012, 0, 2, 0x01 },
+	{ 0xd013, 0, 8, 0x45 },
+	{ 0xd014, 0, 2, 0x03 },
+	{ 0xd040, 0, 8, 0x98 },
+	{ 0xd041, 0, 2, 0x00 },
+	{ 0xd042, 0, 8, 0xcf },
+	{ 0xd043, 0, 2, 0x03 },
+	{ 0xd045, 1, 1, 0x00 },
+	{ 0x9bcf, 0, 8, 0x01 },
+	{ 0xd045, 2, 1, 0x01 },
+	{ 0xd04f, 0, 8, 0x9a },
+	{ 0xd050, 0, 1, 0x01 },
+	{ 0xd051, 0, 8, 0x5a },
+	{ 0xd052, 0, 1, 0x01 },
+	{ 0xd053, 0, 8, 0x96 },
+	{ 0xd054, 0, 8, 0x46 },
+	{ 0xd045, 7, 1, 0x00 },
+	{ 0x9bd7, 0, 8, 0x0a },
+	{ 0x9bd8, 0, 8, 0x14 },
+	{ 0x9bd9, 0, 8, 0x08 },
+};
+
+/* MaxLinear MXL5003 tuner init
+   AF9013_TUNER_MXL5003D   =   3 */
+static struct regdesc tuner_init_mxl5003d[] = {
+	{ 0x9bd5, 0, 8, 0x01 },
+	{ 0x9bd6, 0, 8, 0x09 },
+	{ 0xd1a0, 1, 1, 0x01 },
+	{ 0xd000, 0, 1, 0x01 },
+	{ 0xd000, 1, 1, 0x00 },
+	{ 0xd001, 1, 1, 0x01 },
+	{ 0xd001, 0, 1, 0x00 },
+	{ 0xd001, 5, 1, 0x00 },
+	{ 0xd002, 0, 5, 0x19 },
+	{ 0xd003, 0, 5, 0x1a },
+	{ 0xd004, 0, 5, 0x19 },
+	{ 0xd005, 0, 5, 0x1a },
+	{ 0xd00e, 0, 5, 0x10 },
+	{ 0xd00f, 0, 3, 0x04 },
+	{ 0xd00f, 3, 3, 0x05 },
+	{ 0xd010, 0, 3, 0x04 },
+	{ 0xd010, 3, 3, 0x05 },
+	{ 0xd016, 4, 4, 0x03 },
+	{ 0xd01f, 0, 6, 0x0a },
+	{ 0xd020, 0, 6, 0x0a },
+	{ 0x9bda, 0, 8, 0x00 },
+	{ 0x9be3, 0, 8, 0x00 },
+	{ 0x9bfc, 0, 8, 0x0f },
+	{ 0x9bf6, 0, 8, 0x01 },
+	{ 0x9bbe, 0, 1, 0x01 },
+	{ 0xd015, 0, 8, 0x33 },
+	{ 0xd016, 0, 1, 0x00 },
+	{ 0xd044, 0, 8, 0x40 },
+	{ 0xd045, 0, 1, 0x00 },
+	{ 0xd008, 0, 8, 0x0f },
+	{ 0xd009, 0, 2, 0x02 },
+	{ 0xd006, 0, 8, 0x6c },
+	{ 0xd007, 0, 2, 0x00 },
+	{ 0xd00c, 0, 8, 0x3d },
+	{ 0xd00d, 0, 2, 0x00 },
+	{ 0xd00a, 0, 8, 0x45 },
+	{ 0xd00b, 0, 2, 0x01 },
+	{ 0x9bc7, 0, 8, 0x07 },
+	{ 0x9bc8, 0, 8, 0x52 },
+	{ 0x9bc3, 0, 8, 0x0f },
+	{ 0x9bc4, 0, 8, 0x02 },
+	{ 0x9bc5, 0, 8, 0x3d },
+	{ 0x9bc6, 0, 8, 0x00 },
+	{ 0x9bba, 0, 8, 0xa2 },
+	{ 0x9bc9, 0, 8, 0xa0 },
+	{ 0xd011, 0, 8, 0x56 },
+	{ 0xd012, 0, 2, 0x00 },
+	{ 0xd013, 0, 8, 0x50 },
+	{ 0xd014, 0, 2, 0x00 },
+	{ 0xd040, 0, 8, 0x56 },
+	{ 0xd041, 0, 2, 0x00 },
+	{ 0xd042, 0, 8, 0x50 },
+	{ 0xd043, 0, 2, 0x00 },
+	{ 0xd045, 1, 1, 0x00 },
+	{ 0x9bcf, 0, 8, 0x01 },
+	{ 0xd045, 2, 1, 0x01 },
+	{ 0xd04f, 0, 8, 0x9a },
+	{ 0xd050, 0, 1, 0x01 },
+	{ 0xd051, 0, 8, 0x5a },
+	{ 0xd052, 0, 1, 0x01 },
+	{ 0xd053, 0, 8, 0x50 },
+	{ 0xd054, 0, 8, 0x46 },
+	{ 0x9bd7, 0, 8, 0x0a },
+	{ 0x9bd8, 0, 8, 0x14 },
+	{ 0x9bd9, 0, 8, 0x08 },
+};
+
+/* MaxLinear MXL5005 tuner init
+   AF9013_TUNER_MXL5005D   =  13
+   AF9013_TUNER_MXL5005R   =  30 */
+static struct regdesc tuner_init_mxl5005[] = {
+	{ 0x9bd5, 0, 8, 0x01 },
+	{ 0x9bd6, 0, 8, 0x07 },
+	{ 0xd1a0, 1, 1, 0x01 },
+	{ 0xd000, 0, 1, 0x01 },
+	{ 0xd000, 1, 1, 0x00 },
+	{ 0xd001, 1, 1, 0x01 },
+	{ 0xd001, 0, 1, 0x00 },
+	{ 0xd001, 5, 1, 0x00 },
+	{ 0xd002, 0, 5, 0x19 },
+	{ 0xd003, 0, 5, 0x1a },
+	{ 0xd004, 0, 5, 0x19 },
+	{ 0xd005, 0, 5, 0x1a },
+	{ 0xd00e, 0, 5, 0x10 },
+	{ 0xd00f, 0, 3, 0x04 },
+	{ 0xd00f, 3, 3, 0x05 },
+	{ 0xd010, 0, 3, 0x04 },
+	{ 0xd010, 3, 3, 0x05 },
+	{ 0xd016, 4, 4, 0x03 },
+	{ 0xd01f, 0, 6, 0x0a },
+	{ 0xd020, 0, 6, 0x0a },
+	{ 0x9bda, 0, 8, 0x01 },
+	{ 0x9be3, 0, 8, 0x01 },
+	{ 0x9bbe, 0, 1, 0x01 },
+	{ 0x9bcc, 0, 1, 0x01 },
+	{ 0x9bb9, 0, 8, 0x00 },
+	{ 0x9bcd, 0, 8, 0x28 },
+	{ 0x9bff, 0, 8, 0x24 },
+	{ 0xd015, 0, 8, 0x40 },
+	{ 0xd016, 0, 1, 0x00 },
+	{ 0xd044, 0, 8, 0x40 },
+	{ 0xd045, 0, 1, 0x00 },
+	{ 0xd008, 0, 8, 0x0f },
+	{ 0xd009, 0, 2, 0x02 },
+	{ 0xd006, 0, 8, 0x73 },
+	{ 0xd007, 0, 2, 0x01 },
+	{ 0xd00c, 0, 8, 0xfa },
+	{ 0xd00d, 0, 2, 0x01 },
+	{ 0xd00a, 0, 8, 0xff },
+	{ 0xd00b, 0, 2, 0x01 },
+	{ 0x9bc7, 0, 8, 0x23 },
+	{ 0x9bc8, 0, 8, 0x55 },
+	{ 0x9bc3, 0, 8, 0x01 },
+	{ 0x9bc4, 0, 8, 0x02 },
+	{ 0x9bc5, 0, 8, 0xfa },
+	{ 0x9bc6, 0, 8, 0x01 },
+	{ 0x9bba, 0, 8, 0xff },
+	{ 0x9bc9, 0, 8, 0xff },
+	{ 0x9bd3, 0, 8, 0x95 },
+	{ 0xd011, 0, 8, 0x70 },
+	{ 0xd012, 0, 2, 0x01 },
+	{ 0xd013, 0, 8, 0xfb },
+	{ 0xd014, 0, 2, 0x01 },
+	{ 0xd040, 0, 8, 0x70 },
+	{ 0xd041, 0, 2, 0x01 },
+	{ 0xd042, 0, 8, 0xfb },
+	{ 0xd043, 0, 2, 0x01 },
+	{ 0xd045, 1, 1, 0x00 },
+	{ 0x9bcf, 0, 1, 0x01 },
+	{ 0xd045, 2, 1, 0x01 },
+	{ 0xd04f, 0, 8, 0x9a },
+	{ 0xd050, 0, 1, 0x01 },
+	{ 0xd051, 0, 8, 0x5a },
+	{ 0xd052, 0, 1, 0x01 },
+	{ 0xd053, 0, 8, 0x50 },
+	{ 0xd054, 0, 8, 0x46 },
+	{ 0x9bd7, 0, 8, 0x0a },
+	{ 0x9bd8, 0, 8, 0x14 },
+	{ 0x9bd9, 0, 8, 0x08 },
+	{ 0x9bd0, 0, 8, 0x93 },
+	{ 0x9be4, 0, 8, 0xfe },
+	{ 0x9bbd, 0, 8, 0x63 },
+	{ 0x9be2, 0, 8, 0xfe },
+	{ 0x9bee, 0, 1, 0x01 },
+};
+
+/* Quantek QT1010 tuner init
+   AF9013_TUNER_QT1010     = 134
+   AF9013_TUNER_QT1010A    = 162 */
+static struct regdesc tuner_init_qt1010[] = {
+	{ 0x9bd5, 0, 8, 0x01 },
+	{ 0x9bd6, 0, 8, 0x09 },
+	{ 0xd1a0, 1, 1, 0x01 },
+	{ 0xd000, 0, 1, 0x01 },
+	{ 0xd000, 1, 1, 0x00 },
+	{ 0xd001, 1, 1, 0x01 },
+	{ 0xd001, 0, 1, 0x00 },
+	{ 0xd001, 5, 1, 0x00 },
+	{ 0xd002, 0, 5, 0x19 },
+	{ 0xd003, 0, 5, 0x1a },
+	{ 0xd004, 0, 5, 0x19 },
+	{ 0xd005, 0, 5, 0x1a },
+	{ 0xd00e, 0, 5, 0x10 },
+	{ 0xd00f, 0, 3, 0x04 },
+	{ 0xd00f, 3, 3, 0x05 },
+	{ 0xd010, 0, 3, 0x04 },
+	{ 0xd010, 3, 3, 0x05 },
+	{ 0xd016, 4, 4, 0x03 },
+	{ 0xd01f, 0, 6, 0x0a },
+	{ 0xd020, 0, 6, 0x0a },
+	{ 0x9bda, 0, 8, 0x01 },
+	{ 0x9be3, 0, 8, 0x01 },
+	{ 0xd015, 0, 8, 0x46 },
+	{ 0xd016, 0, 1, 0x00 },
+	{ 0xd044, 0, 8, 0x46 },
+	{ 0xd045, 0, 1, 0x00 },
+	{ 0x9bbe, 0, 1, 0x01 },
+	{ 0x9bcc, 0, 1, 0x01 },
+	{ 0x9bb9, 0, 8, 0x00 },
+	{ 0x9bcd, 0, 8, 0x28 },
+	{ 0x9bff, 0, 8, 0x20 },
+	{ 0xd008, 0, 8, 0x0f },
+	{ 0xd009, 0, 2, 0x02 },
+	{ 0xd006, 0, 8, 0x99 },
+	{ 0xd007, 0, 2, 0x01 },
+	{ 0xd00c, 0, 8, 0x0f },
+	{ 0xd00d, 0, 2, 0x02 },
+	{ 0xd00a, 0, 8, 0x50 },
+	{ 0xd00b, 0, 2, 0x01 },
+	{ 0x9bc7, 0, 8, 0x00 },
+	{ 0x9bc8, 0, 8, 0x00 },
+	{ 0x9bc3, 0, 8, 0x0f },
+	{ 0x9bc4, 0, 8, 0x02 },
+	{ 0x9bc5, 0, 8, 0x0f },
+	{ 0x9bc6, 0, 8, 0x02 },
+	{ 0x9bba, 0, 8, 0xc5 },
+	{ 0x9bc9, 0, 8, 0xff },
+	{ 0xd011, 0, 8, 0x58 },
+	{ 0xd012, 0, 2, 0x02 },
+	{ 0xd013, 0, 8, 0x89 },
+	{ 0xd014, 0, 2, 0x01 },
+	{ 0xd040, 0, 8, 0x58 },
+	{ 0xd041, 0, 2, 0x02 },
+	{ 0xd042, 0, 8, 0x89 },
+	{ 0xd043, 0, 2, 0x01 },
+	{ 0xd045, 1, 1, 0x00 },
+	{ 0x9bcf, 0, 1, 0x01 },
+	{ 0xd045, 2, 1, 0x01 },
+	{ 0xd04f, 0, 8, 0x9a },
+	{ 0xd050, 0, 1, 0x01 },
+	{ 0xd051, 0, 8, 0x5a },
+	{ 0xd052, 0, 1, 0x01 },
+	{ 0xd053, 0, 8, 0x50 },
+	{ 0xd054, 0, 8, 0x46 },
+	{ 0x9bd7, 0, 8, 0x0a },
+	{ 0x9bd8, 0, 8, 0x14 },
+	{ 0x9bd9, 0, 8, 0x08 },
+	{ 0x9bd0, 0, 8, 0xcd },
+	{ 0x9be4, 0, 8, 0xbb },
+	{ 0x9bbd, 0, 8, 0x93 },
+	{ 0x9be2, 0, 8, 0x80 },
+	{ 0x9bee, 0, 1, 0x01 },
+};
+
+/* Freescale MC44S803 tuner init
+   AF9013_TUNER_MC44S803   = 133 */
+static struct regdesc tuner_init_mc44s803[] = {
+	{ 0x9bd5, 0, 8, 0x01 },
+	{ 0x9bd6, 0, 8, 0x06 },
+	{ 0xd1a0, 1, 1, 0x01 },
+	{ 0xd000, 0, 1, 0x01 },
+	{ 0xd000, 1, 1, 0x00 },
+	{ 0xd001, 1, 1, 0x01 },
+	{ 0xd001, 0, 1, 0x00 },
+	{ 0xd001, 5, 1, 0x00 },
+	{ 0xd002, 0, 5, 0x19 },
+	{ 0xd003, 0, 5, 0x1a },
+	{ 0xd004, 0, 5, 0x19 },
+	{ 0xd005, 0, 5, 0x1a },
+	{ 0xd00e, 0, 5, 0x10 },
+	{ 0xd00f, 0, 3, 0x04 },
+	{ 0xd00f, 3, 3, 0x05 },
+	{ 0xd010, 0, 3, 0x04 },
+	{ 0xd010, 3, 3, 0x05 },
+	{ 0xd016, 4, 4, 0x03 },
+	{ 0xd01f, 0, 6, 0x0a },
+	{ 0xd020, 0, 6, 0x0a },
+	{ 0x9bda, 0, 8, 0x00 },
+	{ 0x9be3, 0, 8, 0x00 },
+	{ 0x9bf6, 0, 8, 0x01 },
+	{ 0x9bf8, 0, 8, 0x02 },
+	{ 0x9bf9, 0, 8, 0x02 },
+	{ 0x9bfc, 0, 8, 0x1f },
+	{ 0x9bbe, 0, 1, 0x01 },
+	{ 0x9bcc, 0, 1, 0x01 },
+	{ 0x9bb9, 0, 8, 0x00 },
+	{ 0x9bcd, 0, 8, 0x24 },
+	{ 0x9bff, 0, 8, 0x24 },
+	{ 0xd015, 0, 8, 0x46 },
+	{ 0xd016, 0, 1, 0x00 },
+	{ 0xd044, 0, 8, 0x46 },
+	{ 0xd045, 0, 1, 0x00 },
+	{ 0xd008, 0, 8, 0x01 },
+	{ 0xd009, 0, 2, 0x02 },
+	{ 0xd006, 0, 8, 0x7b },
+	{ 0xd007, 0, 2, 0x00 },
+	{ 0xd00c, 0, 8, 0x7c },
+	{ 0xd00d, 0, 2, 0x02 },
+	{ 0xd00a, 0, 8, 0xfe },
+	{ 0xd00b, 0, 2, 0x01 },
+	{ 0x9bc7, 0, 8, 0x08 },
+	{ 0x9bc8, 0, 8, 0x9a },
+	{ 0x9bc3, 0, 8, 0x01 },
+	{ 0x9bc4, 0, 8, 0x02 },
+	{ 0x9bc5, 0, 8, 0x7c },
+	{ 0x9bc6, 0, 8, 0x02 },
+	{ 0x9bba, 0, 8, 0xfc },
+	{ 0x9bc9, 0, 8, 0xaa },
+	{ 0xd011, 0, 8, 0x6b },
+	{ 0xd012, 0, 2, 0x00 },
+	{ 0xd013, 0, 8, 0x88 },
+	{ 0xd014, 0, 2, 0x02 },
+	{ 0xd040, 0, 8, 0x6b },
+	{ 0xd041, 0, 2, 0x00 },
+	{ 0xd042, 0, 8, 0x7c },
+	{ 0xd043, 0, 2, 0x02 },
+	{ 0xd045, 1, 1, 0x00 },
+	{ 0x9bcf, 0, 1, 0x01 },
+	{ 0xd045, 2, 1, 0x01 },
+	{ 0xd04f, 0, 8, 0x9a },
+	{ 0xd050, 0, 1, 0x01 },
+	{ 0xd051, 0, 8, 0x5a },
+	{ 0xd052, 0, 1, 0x01 },
+	{ 0xd053, 0, 8, 0x50 },
+	{ 0xd054, 0, 8, 0x46 },
+	{ 0x9bd7, 0, 8, 0x0a },
+	{ 0x9bd8, 0, 8, 0x14 },
+	{ 0x9bd9, 0, 8, 0x08 },
+	{ 0x9bd0, 0, 8, 0x9e },
+	{ 0x9be4, 0, 8, 0xff },
+	{ 0x9bbd, 0, 8, 0x9e },
+	{ 0x9be2, 0, 8, 0x25 },
+	{ 0x9bee, 0, 1, 0x01 },
+	{ 0xd73b, 3, 1, 0x00 },
+};
+
+/* unknown, probably for tin can tuner, tuner init
+   AF9013_TUNER_UNKNOWN   = 140 */
+static struct regdesc tuner_init_unknown[] = {
+	{ 0x9bd5, 0, 8, 0x01 },
+	{ 0x9bd6, 0, 8, 0x02 },
+	{ 0xd1a0, 1, 1, 0x01 },
+	{ 0xd000, 0, 1, 0x01 },
+	{ 0xd000, 1, 1, 0x00 },
+	{ 0xd001, 1, 1, 0x01 },
+	{ 0xd001, 0, 1, 0x00 },
+	{ 0xd001, 5, 1, 0x00 },
+	{ 0xd002, 0, 5, 0x19 },
+	{ 0xd003, 0, 5, 0x1a },
+	{ 0xd004, 0, 5, 0x19 },
+	{ 0xd005, 0, 5, 0x1a },
+	{ 0xd00e, 0, 5, 0x10 },
+	{ 0xd00f, 0, 3, 0x04 },
+	{ 0xd00f, 3, 3, 0x05 },
+	{ 0xd010, 0, 3, 0x04 },
+	{ 0xd010, 3, 3, 0x05 },
+	{ 0xd016, 4, 4, 0x03 },
+	{ 0xd01f, 0, 6, 0x0a },
+	{ 0xd020, 0, 6, 0x0a },
+	{ 0x9bda, 0, 8, 0x01 },
+	{ 0x9be3, 0, 8, 0x01 },
+	{ 0xd1a0, 1, 1, 0x00 },
+	{ 0x9bbe, 0, 1, 0x01 },
+	{ 0x9bcc, 0, 1, 0x01 },
+	{ 0x9bb9, 0, 8, 0x00 },
+	{ 0x9bcd, 0, 8, 0x18 },
+	{ 0x9bff, 0, 8, 0x2c },
+	{ 0xd015, 0, 8, 0x46 },
+	{ 0xd016, 0, 1, 0x00 },
+	{ 0xd044, 0, 8, 0x46 },
+	{ 0xd045, 0, 1, 0x00 },
+	{ 0xd008, 0, 8, 0xdf },
+	{ 0xd009, 0, 2, 0x02 },
+	{ 0xd006, 0, 8, 0x44 },
+	{ 0xd007, 0, 2, 0x01 },
+	{ 0xd00c, 0, 8, 0x00 },
+	{ 0xd00d, 0, 2, 0x02 },
+	{ 0xd00a, 0, 8, 0xf6 },
+	{ 0xd00b, 0, 2, 0x01 },
+	{ 0x9bba, 0, 8, 0xf9 },
+	{ 0x9bc8, 0, 8, 0xaa },
+	{ 0x9bc3, 0, 8, 0xdf },
+	{ 0x9bc4, 0, 8, 0x02 },
+	{ 0x9bc5, 0, 8, 0x00 },
+	{ 0x9bc6, 0, 8, 0x02 },
+	{ 0x9bc9, 0, 8, 0xf0 },
+	{ 0xd011, 0, 8, 0x3c },
+	{ 0xd012, 0, 2, 0x01 },
+	{ 0xd013, 0, 8, 0xf7 },
+	{ 0xd014, 0, 2, 0x02 },
+	{ 0xd040, 0, 8, 0x0b },
+	{ 0xd041, 0, 2, 0x02 },
+	{ 0xd042, 0, 8, 0x4d },
+	{ 0xd043, 0, 2, 0x00 },
+	{ 0xd045, 1, 1, 0x00 },
+	{ 0x9bcf, 0, 1, 0x01 },
+	{ 0xd045, 2, 1, 0x01 },
+	{ 0xd04f, 0, 8, 0x9a },
+	{ 0xd050, 0, 1, 0x01 },
+	{ 0xd051, 0, 8, 0x5a },
+	{ 0xd052, 0, 1, 0x01 },
+	{ 0xd053, 0, 8, 0x50 },
+	{ 0xd054, 0, 8, 0x46 },
+	{ 0x9bd7, 0, 8, 0x0a },
+	{ 0x9bd8, 0, 8, 0x14 },
+	{ 0x9bd9, 0, 8, 0x08 },
+};
+
+/* NXP TDA18271 tuner init
+   AF9013_TUNER_TDA18271   = 156 */
+static struct regdesc tuner_init_tda18271[] = {
+	{ 0x9bd5, 0, 8, 0x01 },
+	{ 0x9bd6, 0, 8, 0x04 },
+	{ 0xd1a0, 1, 1, 0x01 },
+	{ 0xd000, 0, 1, 0x01 },
+	{ 0xd000, 1, 1, 0x00 },
+	{ 0xd001, 1, 1, 0x01 },
+	{ 0xd001, 0, 1, 0x00 },
+	{ 0xd001, 5, 1, 0x00 },
+	{ 0xd002, 0, 5, 0x19 },
+	{ 0xd003, 0, 5, 0x1a },
+	{ 0xd004, 0, 5, 0x19 },
+	{ 0xd005, 0, 5, 0x1a },
+	{ 0xd00e, 0, 5, 0x10 },
+	{ 0xd00f, 0, 3, 0x04 },
+	{ 0xd00f, 3, 3, 0x05 },
+	{ 0xd010, 0, 3, 0x04 },
+	{ 0xd010, 3, 3, 0x05 },
+	{ 0xd016, 4, 4, 0x03 },
+	{ 0xd01f, 0, 6, 0x0a },
+	{ 0xd020, 0, 6, 0x0a },
+	{ 0x9bda, 0, 8, 0x01 },
+	{ 0x9be3, 0, 8, 0x01 },
+	{ 0xd1a0, 1, 1, 0x00 },
+	{ 0x9bbe, 0, 1, 0x01 },
+	{ 0x9bcc, 0, 1, 0x01 },
+	{ 0x9bb9, 0, 8, 0x00 },
+	{ 0x9bcd, 0, 8, 0x18 },
+	{ 0x9bff, 0, 8, 0x2c },
+	{ 0xd015, 0, 8, 0x46 },
+	{ 0xd016, 0, 1, 0x00 },
+	{ 0xd044, 0, 8, 0x46 },
+	{ 0xd045, 0, 1, 0x00 },
+	{ 0xd008, 0, 8, 0xdf },
+	{ 0xd009, 0, 2, 0x02 },
+	{ 0xd006, 0, 8, 0x44 },
+	{ 0xd007, 0, 2, 0x01 },
+	{ 0xd00c, 0, 8, 0x00 },
+	{ 0xd00d, 0, 2, 0x02 },
+	{ 0xd00a, 0, 8, 0xf6 },
+	{ 0xd00b, 0, 2, 0x01 },
+	{ 0x9bba, 0, 8, 0xf9 },
+	{ 0x9bc8, 0, 8, 0xaa },
+	{ 0x9bc3, 0, 8, 0xdf },
+	{ 0x9bc4, 0, 8, 0x02 },
+	{ 0x9bc5, 0, 8, 0x00 },
+	{ 0x9bc6, 0, 8, 0x02 },
+	{ 0x9bc9, 0, 8, 0xf0 },
+	{ 0xd011, 0, 8, 0x3c },
+	{ 0xd012, 0, 2, 0x01 },
+	{ 0xd013, 0, 8, 0xf7 },
+	{ 0xd014, 0, 2, 0x02 },
+	{ 0xd040, 0, 8, 0x0b },
+	{ 0xd041, 0, 2, 0x02 },
+	{ 0xd042, 0, 8, 0x4d },
+	{ 0xd043, 0, 2, 0x00 },
+	{ 0xd045, 1, 1, 0x00 },
+	{ 0x9bcf, 0, 1, 0x01 },
+	{ 0xd045, 2, 1, 0x01 },
+	{ 0xd04f, 0, 8, 0x9a },
+	{ 0xd050, 0, 1, 0x01 },
+	{ 0xd051, 0, 8, 0x5a },
+	{ 0xd052, 0, 1, 0x01 },
+	{ 0xd053, 0, 8, 0x50 },
+	{ 0xd054, 0, 8, 0x46 },
+	{ 0x9bd7, 0, 8, 0x0a },
+	{ 0x9bd8, 0, 8, 0x14 },
+	{ 0x9bd9, 0, 8, 0x08 },
+	{ 0x9bd0, 0, 8, 0xa8 },
+	{ 0x9be4, 0, 8, 0x7f },
+	{ 0x9bbd, 0, 8, 0xa8 },
+	{ 0x9be2, 0, 8, 0x20 },
+	{ 0x9bee, 0, 1, 0x01 },
+};
+
+#endif /* _AF9013_PRIV_ */

+ 133 - 0
drivers/media/dvb/frontends/au8522.c

@@ -40,6 +40,8 @@ struct au8522_state {
 	u32 current_frequency;
 	fe_modulation_t current_modulation;
 
+	u32 fe_status;
+	unsigned int led_state;
 };
 
 static int debug;
@@ -538,11 +540,98 @@ static int au8522_init(struct dvb_frontend *fe)
 	return 0;
 }
 
+static int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
+{
+	struct au8522_led_config *led_config = state->config->led_cfg;
+	u8 val;
+
+	/* bail out if we cant control an LED */
+	if (!led_config || !led_config->gpio_output ||
+	    !led_config->gpio_output_enable || !led_config->gpio_output_disable)
+		return 0;
+
+	val = au8522_readreg(state, 0x4000 |
+			     (led_config->gpio_output & ~0xc000));
+	if (onoff) {
+		/* enable GPIO output */
+		val &= ~((led_config->gpio_output_enable >> 8) & 0xff);
+		val |=  (led_config->gpio_output_enable & 0xff);
+	} else {
+		/* disable GPIO output */
+		val &= ~((led_config->gpio_output_disable >> 8) & 0xff);
+		val |=  (led_config->gpio_output_disable & 0xff);
+	}
+	return au8522_writereg(state, 0x8000 |
+			       (led_config->gpio_output & ~0xc000), val);
+}
+
+/* led = 0 | off
+ * led = 1 | signal ok
+ * led = 2 | signal strong
+ * led < 0 | only light led if leds are currently off
+ */
+static int au8522_led_ctrl(struct au8522_state *state, int led)
+{
+	struct au8522_led_config *led_config = state->config->led_cfg;
+	int i, ret = 0;
+
+	/* bail out if we cant control an LED */
+	if (!led_config || !led_config->gpio_leds ||
+	    !led_config->num_led_states || !led_config->led_states)
+		return 0;
+
+	if (led < 0) {
+		/* if LED is already lit, then leave it as-is */
+		if (state->led_state)
+			return 0;
+		else
+			led *= -1;
+	}
+
+	/* toggle LED if changing state */
+	if (state->led_state != led) {
+		u8 val;
+
+		dprintk("%s: %d\n", __func__, led);
+
+		au8522_led_gpio_enable(state, 1);
+
+		val = au8522_readreg(state, 0x4000 |
+				     (led_config->gpio_leds & ~0xc000));
+
+		/* start with all leds off */
+		for (i = 0; i < led_config->num_led_states; i++)
+			val &= ~led_config->led_states[i];
+
+		/* set selected LED state */
+		if (led < led_config->num_led_states)
+			val |= led_config->led_states[led];
+		else if (led_config->num_led_states)
+			val |=
+			led_config->led_states[led_config->num_led_states - 1];
+
+		ret = au8522_writereg(state, 0x8000 |
+				      (led_config->gpio_leds & ~0xc000), val);
+		if (ret < 0)
+			return ret;
+
+		state->led_state = led;
+
+		if (led == 0)
+			au8522_led_gpio_enable(state, 0);
+	}
+
+	return 0;
+}
+
 static int au8522_sleep(struct dvb_frontend *fe)
 {
 	struct au8522_state *state = fe->demodulator_priv;
 	dprintk("%s()\n", __func__);
 
+	/* turn off led */
+	au8522_led_ctrl(state, 0);
+
 	state->current_frequency = 0;
 
 	return 0;
@@ -592,12 +681,53 @@ static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status)
 			*status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
 		break;
 	}
+	state->fe_status = *status;
+
+	if (*status & FE_HAS_LOCK)
+		/* turn on LED, if it isn't on already */
+		au8522_led_ctrl(state, -1);
+	else
+		/* turn off LED */
+		au8522_led_ctrl(state, 0);
 
 	dprintk("%s() status 0x%08x\n", __func__, *status);
 
 	return 0;
 }
 
+static int au8522_led_status(struct au8522_state *state, const u16 *snr)
+{
+	struct au8522_led_config *led_config = state->config->led_cfg;
+	int led;
+	u16 strong;
+
+	/* bail out if we cant control an LED */
+	if (!led_config)
+		return 0;
+
+	if (0 == (state->fe_status & FE_HAS_LOCK))
+		return au8522_led_ctrl(state, 0);
+	else if (state->current_modulation == QAM_256)
+		strong = led_config->qam256_strong;
+	else if (state->current_modulation == QAM_64)
+		strong = led_config->qam64_strong;
+	else /* (state->current_modulation == VSB_8) */
+		strong = led_config->vsb8_strong;
+
+	if (*snr >= strong)
+		led = 2;
+	else
+		led = 1;
+
+	if ((state->led_state) &&
+	    (((strong < *snr) ? (*snr - strong) : (strong - *snr)) <= 10))
+		/* snr didn't change enough to bother
+		 * changing the color of the led */
+		return 0;
+
+	return au8522_led_ctrl(state, led);
+}
+
 static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr)
 {
 	struct au8522_state *state = fe->demodulator_priv;
@@ -621,6 +751,9 @@ static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr)
 					    au8522_readreg(state, 0x4311),
 					    snr);
 
+	if (state->config->led_cfg)
+		au8522_led_status(state, snr);
+
 	return ret;
 }
 

+ 17 - 0
drivers/media/dvb/frontends/au8522.h

@@ -30,6 +30,21 @@ enum au8522_if_freq {
 	AU8522_IF_3_25MHZ,
 };
 
+struct au8522_led_config {
+	u16 vsb8_strong;
+	u16 qam64_strong;
+	u16 qam256_strong;
+
+	u16 gpio_output;
+	/* unset hi bits, set low bits */
+	u16 gpio_output_enable;
+	u16 gpio_output_disable;
+
+	u16 gpio_leds;
+	u8 *led_states;
+	unsigned int num_led_states;
+};
+
 struct au8522_config {
 	/* the demodulator's i2c address */
 	u8 demod_address;
@@ -39,6 +54,8 @@ struct au8522_config {
 #define AU8522_DEMODLOCKING 1
 	u8 status_mode;
 
+	struct au8522_led_config *led_cfg;
+
 	enum au8522_if_freq vsb_if;
 	enum au8522_if_freq qam_if;
 };

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

@@ -33,12 +33,17 @@ struct cx24110_config
 	u8 demod_address;
 };
 
-static inline int cx24110_pll_write(struct dvb_frontend *fe, u32 val) {
-	int r = 0;
-	u8 buf[] = {(u8) (val>>24), (u8) (val>>16), (u8) (val>>8)};
+static inline int cx24110_pll_write(struct dvb_frontend *fe, u32 val)
+{
+	u8 buf[] = {
+		(u8)((val >> 24) & 0xff),
+		(u8)((val >> 16) & 0xff),
+		(u8)((val >> 8) & 0xff)
+	};
+
 	if (fe->ops.write)
-		r = fe->ops.write(fe, buf, 3);
-	return r;
+		return fe->ops.write(fe, buf, 3);
+	return 0;
 }
 
 #if defined(CONFIG_DVB_CX24110) || (defined(CONFIG_DVB_CX24110_MODULE) && defined(MODULE))

+ 1423 - 0
drivers/media/dvb/frontends/cx24116.c

@@ -0,0 +1,1423 @@
+/*
+    Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver
+
+    Copyright (C) 2006-2008 Steven Toth <stoth@hauppauge.com>
+    Copyright (C) 2006-2007 Georg Acher
+    Copyright (C) 2007-2008 Darron Broad
+	March 2007
+	    Fixed some bugs.
+	    Added diseqc support.
+	    Added corrected signal strength support.
+	August 2007
+	    Sync with legacy version.
+	    Some clean ups.
+    Copyright (C) 2008 Igor Liplianin
+	September, 9th 2008
+	    Fixed locking on high symbol rates (>30000).
+	    Implement MPEG initialization parameter.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+
+#include "dvb_frontend.h"
+#include "cx24116.h"
+
+static int debug = 0;
+#define dprintk(args...) \
+	do { \
+		if (debug) printk ("cx24116: " args); \
+	} while (0)
+
+#define CX24116_DEFAULT_FIRMWARE "dvb-fe-cx24116.fw"
+#define CX24116_SEARCH_RANGE_KHZ 5000
+
+/* known registers */
+#define CX24116_REG_COMMAND (0x00)      /* command args 0x00..0x1e */
+#define CX24116_REG_EXECUTE (0x1f)      /* execute command */
+#define CX24116_REG_MAILBOX (0x96)      /* FW or multipurpose mailbox? */
+#define CX24116_REG_RESET   (0x20)      /* reset status > 0     */
+#define CX24116_REG_SIGNAL  (0x9e)      /* signal low           */
+#define CX24116_REG_SSTATUS (0x9d)      /* signal high / status */
+#define CX24116_REG_QUALITY8 (0xa3)
+#define CX24116_REG_QSTATUS (0xbc)
+#define CX24116_REG_QUALITY0 (0xd5)
+#define CX24116_REG_BER0    (0xc9)
+#define CX24116_REG_BER8    (0xc8)
+#define CX24116_REG_BER16   (0xc7)
+#define CX24116_REG_BER24   (0xc6)
+#define CX24116_REG_UCB0    (0xcb)
+#define CX24116_REG_UCB8    (0xca)
+#define CX24116_REG_CLKDIV  (0xf3)
+#define CX24116_REG_RATEDIV (0xf9)
+#define CX24116_REG_FECSTATUS (0x9c)    /* configured fec (not tuned) or actual FEC (tuned) 1=1/2 2=2/3 etc */
+
+/* FECSTATUS bits */
+#define CX24116_FEC_FECMASK   (0x1f)    /* mask to determine configured fec (not tuned) or actual fec (tuned) */
+#define CX24116_FEC_DVBS      (0x20)    /* Select DVB-S demodulator, else DVB-S2 */
+#define CX24116_FEC_UNKNOWN   (0x40)    /* Unknown/unused */
+#define CX24116_FEC_PILOT     (0x80)    /* Pilot mode requested when tuning else always reset when tuned */
+
+/* arg buffer size */
+#define CX24116_ARGLEN (0x1e)
+
+/* rolloff */
+#define CX24116_ROLLOFF_020 (0x00)
+#define CX24116_ROLLOFF_025 (0x01)
+#define CX24116_ROLLOFF_035 (0x02)
+
+/* pilot bit */
+#define CX24116_PILOT_OFF (0x00)
+#define CX24116_PILOT_ON (0x40)
+
+/* signal status */
+#define CX24116_HAS_SIGNAL   (0x01)
+#define CX24116_HAS_CARRIER  (0x02)
+#define CX24116_HAS_VITERBI  (0x04)
+#define CX24116_HAS_SYNCLOCK (0x08)
+#define CX24116_HAS_UNKNOWN1 (0x10)
+#define CX24116_HAS_UNKNOWN2 (0x20)
+#define CX24116_STATUS_MASK  (0x3f)
+#define CX24116_SIGNAL_MASK  (0xc0)
+
+#define CX24116_DISEQC_TONEOFF   (0)    /* toneburst never sent */
+#define CX24116_DISEQC_TONECACHE (1)    /* toneburst cached     */
+#define CX24116_DISEQC_MESGCACHE (2)    /* message cached       */
+
+/* arg offset for DiSEqC */
+#define CX24116_DISEQC_BURST  (1)
+#define CX24116_DISEQC_ARG2_2 (2)   /* unknown value=2 */
+#define CX24116_DISEQC_ARG3_0 (3)   /* unknown value=0 */
+#define CX24116_DISEQC_ARG4_0 (4)   /* unknown value=0 */
+#define CX24116_DISEQC_MSGLEN (5)
+#define CX24116_DISEQC_MSGOFS (6)
+
+/* DiSEqC burst */
+#define CX24116_DISEQC_MINI_A (0)
+#define CX24116_DISEQC_MINI_B (1)
+
+/* DiSEqC tone burst */
+static int toneburst = 1;
+
+/* SNR measurements */
+static int esno_snr = 0;
+
+enum cmds
+{
+	CMD_SET_VCO     = 0x10,
+	CMD_TUNEREQUEST = 0x11,
+	CMD_MPEGCONFIG  = 0x13,
+	CMD_TUNERINIT   = 0x14,
+	CMD_BANDWIDTH   = 0x15,
+	CMD_GETAGC      = 0x19,
+	CMD_LNBCONFIG   = 0x20,
+	CMD_LNBSEND     = 0x21, /* Formerly CMD_SEND_DISEQC */
+	CMD_SET_TONEPRE = 0x22,
+	CMD_SET_TONE    = 0x23,
+	CMD_UPDFWVERS   = 0x35,
+	CMD_TUNERSLEEP  = 0x36,
+	CMD_AGCCONTROL  = 0x3b, /* Unknown */
+};
+
+/* The Demod/Tuner can't easily provide these, we cache them */
+struct cx24116_tuning
+{
+	u32 frequency;
+	u32 symbol_rate;
+	fe_spectral_inversion_t inversion;
+	fe_code_rate_t fec;
+
+	fe_modulation_t modulation;
+	fe_pilot_t pilot;
+	fe_rolloff_t rolloff;
+
+	/* Demod values */
+	u8 fec_val;
+	u8 fec_mask;
+	u8 inversion_val;
+	u8 pilot_val;
+	u8 rolloff_val;
+};
+
+/* Basic commands that are sent to the firmware */
+struct cx24116_cmd
+{
+	u8 len;
+	u8 args[CX24116_ARGLEN];
+};
+
+struct cx24116_state
+{
+	struct i2c_adapter* i2c;
+	const struct cx24116_config* config;
+
+	struct dvb_frontend frontend;
+
+	struct cx24116_tuning dcur;
+	struct cx24116_tuning dnxt;
+
+	u8 skip_fw_load;
+	u8 burst;
+	struct cx24116_cmd dsec_cmd;
+};
+
+static int cx24116_writereg(struct cx24116_state* state, int reg, int data)
+{
+	u8 buf[] = { reg, data };
+	struct i2c_msg msg = { .addr = state->config->demod_address,
+		.flags = 0, .buf = buf, .len = 2 };
+	int err;
+
+	if (debug>1)
+		printk("cx24116: %s: write reg 0x%02x, value 0x%02x\n",
+						__func__,reg, data);
+
+	if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) {
+		printk("%s: writereg error(err == %i, reg == 0x%02x,"
+			 " value == 0x%02x)\n", __func__, err, reg, data);
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+/* Bulk byte writes to a single I2C address, for 32k firmware load */
+static int cx24116_writeregN(struct cx24116_state* state, int reg, u8 *data, u16 len)
+{
+	int ret = -EREMOTEIO;
+	struct i2c_msg msg;
+	u8 *buf;
+
+	buf = kmalloc(len + 1, GFP_KERNEL);
+	if (buf == NULL) {
+		printk("Unable to kmalloc\n");
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	*(buf) = reg;
+	memcpy(buf + 1, data, len);
+
+	msg.addr = state->config->demod_address;
+	msg.flags = 0;
+	msg.buf = buf;
+	msg.len = len + 1;
+
+	if (debug>1)
+		printk("cx24116: %s:  write regN 0x%02x, len = %d\n",
+						__func__,reg, len);
+
+	if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) {
+		printk("%s: writereg error(err == %i, reg == 0x%02x\n",
+			 __func__, ret, reg);
+		ret = -EREMOTEIO;
+	}
+
+error:
+	kfree(buf);
+
+	return ret;
+}
+
+static int cx24116_readreg(struct cx24116_state* state, u8 reg)
+{
+	int ret;
+	u8 b0[] = { reg };
+	u8 b1[] = { 0 };
+	struct i2c_msg msg[] = {
+		{ .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 },
+		{ .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 }
+	};
+
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2) {
+		printk("%s: reg=0x%x (error=%d)\n", __func__, reg, ret);
+		return ret;
+	}
+
+	if (debug>1)
+		printk("cx24116: read reg 0x%02x, value 0x%02x\n",reg, b1[0]);
+
+	return b1[0];
+}
+
+static int cx24116_set_inversion(struct cx24116_state* state, fe_spectral_inversion_t inversion)
+{
+	dprintk("%s(%d)\n", __func__, inversion);
+
+	switch (inversion) {
+	case INVERSION_OFF:
+		state->dnxt.inversion_val = 0x00;
+		break;
+	case INVERSION_ON:
+		state->dnxt.inversion_val = 0x04;
+		break;
+	case INVERSION_AUTO:
+		state->dnxt.inversion_val = 0x0C;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	state->dnxt.inversion = inversion;
+
+	return 0;
+}
+
+/*
+ * modfec (modulation and FEC)
+ * ===========================
+ *
+ * MOD          FEC             mask/val    standard
+ * ----         --------        ----------- --------
+ * QPSK         FEC_1_2         0x02 0x02+X DVB-S
+ * QPSK         FEC_2_3         0x04 0x02+X DVB-S
+ * QPSK         FEC_3_4         0x08 0x02+X DVB-S
+ * QPSK         FEC_4_5         0x10 0x02+X DVB-S (?)
+ * QPSK         FEC_5_6         0x20 0x02+X DVB-S
+ * QPSK         FEC_6_7         0x40 0x02+X DVB-S
+ * QPSK         FEC_7_8         0x80 0x02+X DVB-S
+ * QPSK         FEC_8_9         0x01 0x02+X DVB-S (?) (NOT SUPPORTED?)
+ * QPSK         AUTO            0xff 0x02+X DVB-S
+ *
+ * For DVB-S high byte probably represents FEC
+ * and low byte selects the modulator. The high
+ * byte is search range mask. Bit 5 may turn
+ * on DVB-S and remaining bits represent some
+ * kind of calibration (how/what i do not know).
+ *
+ * Eg.(2/3) szap "Zone Horror"
+ *
+ * mask/val = 0x04, 0x20
+ * status 1f | signal c3c0 | snr a333 | ber 00000098 | unc 00000000 | FE_HAS_LOCK
+ *
+ * mask/val = 0x04, 0x30
+ * status 1f | signal c3c0 | snr a333 | ber 00000000 | unc 00000000 | FE_HAS_LOCK
+ *
+ * After tuning FECSTATUS contains actual FEC
+ * in use numbered 1 through to 8 for 1/2 .. 2/3 etc
+ *
+ * NBC=NOT/NON BACKWARD COMPATIBLE WITH DVB-S (DVB-S2 only)
+ *
+ * NBC-QPSK     FEC_1_2         0x00, 0x04      DVB-S2
+ * NBC-QPSK     FEC_3_5         0x00, 0x05      DVB-S2
+ * NBC-QPSK     FEC_2_3         0x00, 0x06      DVB-S2
+ * NBC-QPSK     FEC_3_4         0x00, 0x07      DVB-S2
+ * NBC-QPSK     FEC_4_5         0x00, 0x08      DVB-S2
+ * NBC-QPSK     FEC_5_6         0x00, 0x09      DVB-S2
+ * NBC-QPSK     FEC_8_9         0x00, 0x0a      DVB-S2
+ * NBC-QPSK     FEC_9_10        0x00, 0x0b      DVB-S2
+ *
+ * NBC-8PSK     FEC_3_5         0x00, 0x0c      DVB-S2
+ * NBC-8PSK     FEC_2_3         0x00, 0x0d      DVB-S2
+ * NBC-8PSK     FEC_3_4         0x00, 0x0e      DVB-S2
+ * NBC-8PSK     FEC_5_6         0x00, 0x0f      DVB-S2
+ * NBC-8PSK     FEC_8_9         0x00, 0x10      DVB-S2
+ * NBC-8PSK     FEC_9_10        0x00, 0x11      DVB-S2
+ *
+ * For DVB-S2 low bytes selects both modulator
+ * and FEC. High byte is meaningless here. To
+ * set pilot, bit 6 (0x40) is set. When inspecting
+ * FECSTATUS bit 7 (0x80) represents the pilot
+ * selection whilst not tuned. When tuned, actual FEC
+ * in use is found in FECSTATUS as per above. Pilot
+ * value is reset.
+ */
+
+/* A table of modulation, fec and configuration bytes for the demod.
+ * Not all S2 mmodulation schemes are support and not all rates with
+ * a scheme are support. Especially, no auto detect when in S2 mode.
+ */
+struct cx24116_modfec {
+	fe_delivery_system_t delivery_system;
+	fe_modulation_t modulation;
+	fe_code_rate_t fec;
+	u8 mask;	/* In DVBS mode this is used to autodetect */
+	u8 val;		/* Passed to the firmware to indicate mode selection */
+} CX24116_MODFEC_MODES[] = {
+ /* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */
+
+ /*mod   fec       mask  val */
+ { SYS_DVBS, QPSK, FEC_NONE, 0xfe, 0x30 },
+ { SYS_DVBS, QPSK, FEC_1_2,  0x02, 0x2e }, /* 00000010 00101110 */
+ { SYS_DVBS, QPSK, FEC_2_3,  0x04, 0x2f }, /* 00000100 00101111 */
+ { SYS_DVBS, QPSK, FEC_3_4,  0x08, 0x30 }, /* 00001000 00110000 */
+ { SYS_DVBS, QPSK, FEC_4_5,  0xfe, 0x30 }, /* 000?0000 ?        */
+ { SYS_DVBS, QPSK, FEC_5_6,  0x20, 0x31 }, /* 00100000 00110001 */
+ { SYS_DVBS, QPSK, FEC_6_7,  0xfe, 0x30 }, /* 0?000000 ?        */
+ { SYS_DVBS, QPSK, FEC_7_8,  0x80, 0x32 }, /* 10000000 00110010 */
+ { SYS_DVBS, QPSK, FEC_8_9,  0xfe, 0x30 }, /* 0000000? ?        */
+ { SYS_DVBS, QPSK, FEC_AUTO, 0xfe, 0x30 },
+ /* NBC-QPSK */
+ { SYS_DVBS2, QPSK, FEC_1_2,  0x00, 0x04 },
+ { SYS_DVBS2, QPSK, FEC_3_5,  0x00, 0x05 },
+ { SYS_DVBS2, QPSK, FEC_2_3,  0x00, 0x06 },
+ { SYS_DVBS2, QPSK, FEC_3_4,  0x00, 0x07 },
+ { SYS_DVBS2, QPSK, FEC_4_5,  0x00, 0x08 },
+ { SYS_DVBS2, QPSK, FEC_5_6,  0x00, 0x09 },
+ { SYS_DVBS2, QPSK, FEC_8_9,  0x00, 0x0a },
+ { SYS_DVBS2, QPSK, FEC_9_10, 0x00, 0x0b },
+ /* 8PSK */
+ { SYS_DVBS2, PSK_8, FEC_3_5,  0x00, 0x0c },
+ { SYS_DVBS2, PSK_8, FEC_2_3,  0x00, 0x0d },
+ { SYS_DVBS2, PSK_8, FEC_3_4,  0x00, 0x0e },
+ { SYS_DVBS2, PSK_8, FEC_5_6,  0x00, 0x0f },
+ { SYS_DVBS2, PSK_8, FEC_8_9,  0x00, 0x10 },
+ { SYS_DVBS2, PSK_8, FEC_9_10, 0x00, 0x11 },
+ /*
+  * `val' can be found in the FECSTATUS register when tuning.
+  * FECSTATUS will give the actual FEC in use if tuning was successful.
+  */
+};
+
+static int cx24116_lookup_fecmod(struct cx24116_state* state,
+	fe_modulation_t m, fe_code_rate_t f)
+{
+	int i, ret = -EOPNOTSUPP;
+
+	dprintk("%s(0x%02x,0x%02x)\n", __func__, m, f);
+
+	for(i=0 ; i < sizeof(CX24116_MODFEC_MODES) / sizeof(struct cx24116_modfec) ; i++)
+	{
+		if( (m == CX24116_MODFEC_MODES[i].modulation) &&
+			(f == CX24116_MODFEC_MODES[i].fec) )
+			{
+				ret = i;
+				break;
+			}
+	}
+
+	return ret;
+}
+
+static int cx24116_set_fec(struct cx24116_state* state, fe_modulation_t mod, fe_code_rate_t fec)
+{
+	int ret = 0;
+
+	dprintk("%s(0x%02x,0x%02x)\n", __func__, mod, fec);
+
+	ret = cx24116_lookup_fecmod(state, mod, fec);
+
+	if(ret < 0)
+		return ret;
+
+	state->dnxt.fec = fec;
+	state->dnxt.fec_val = CX24116_MODFEC_MODES[ret].val;
+	state->dnxt.fec_mask = CX24116_MODFEC_MODES[ret].mask;
+	dprintk("%s() mask/val = 0x%02x/0x%02x\n", __func__,
+		state->dnxt.fec_mask, state->dnxt.fec_val);
+
+	return 0;
+}
+
+static int cx24116_set_symbolrate(struct cx24116_state* state, u32 rate)
+{
+	dprintk("%s(%d)\n", __func__, rate);
+
+	/*  check if symbol rate is within limits */
+	if ((rate > state->frontend.ops.info.symbol_rate_max) ||
+	    (rate < state->frontend.ops.info.symbol_rate_min)) {
+		dprintk("%s() unsupported symbol_rate = %d\n", __func__, rate);
+		return -EOPNOTSUPP;
+	}
+
+	state->dnxt.symbol_rate = rate;
+	dprintk("%s() symbol_rate = %d\n", __func__, rate);
+
+	return 0;
+}
+
+static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware *fw);
+
+static int cx24116_firmware_ondemand(struct dvb_frontend* fe)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	const struct firmware *fw;
+	int ret = 0;
+
+	dprintk("%s()\n",__func__);
+
+	if (cx24116_readreg(state, 0x20) > 0)
+	{
+
+		if (state->skip_fw_load)
+			return 0;
+
+		/* Load firmware */
+		/* request the firmware, this will block until someone uploads it */
+		printk("%s: Waiting for firmware upload (%s)...\n", __func__, CX24116_DEFAULT_FIRMWARE);
+		ret = request_firmware(&fw, CX24116_DEFAULT_FIRMWARE, &state->i2c->dev);
+		printk("%s: Waiting for firmware upload(2)...\n", __func__);
+		if (ret) {
+			printk("%s: No firmware uploaded (timeout or file not found?)\n", __func__);
+			return ret;
+		}
+
+		/* Make sure we don't recurse back through here during loading */
+		state->skip_fw_load = 1;
+
+		ret = cx24116_load_firmware(fe, fw);
+		if (ret)
+			printk("%s: Writing firmware to device failed\n", __func__);
+
+		release_firmware(fw);
+
+		printk("%s: Firmware upload %s\n", __func__, ret == 0 ? "complete" : "failed");
+
+		/* Ensure firmware is always loaded if required */
+		state->skip_fw_load = 0;
+	}
+
+	return ret;
+}
+
+/* Take a basic firmware command structure, format it and forward it for processing */
+static int cx24116_cmd_execute(struct dvb_frontend* fe, struct cx24116_cmd *cmd)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	int i, ret;
+
+	dprintk("%s()\n", __func__);
+
+	/* Load the firmware if required */
+	if ( (ret = cx24116_firmware_ondemand(fe)) != 0)
+	{
+		printk("%s(): Unable initialise the firmware\n", __func__);
+		return ret;
+	}
+
+	/* Write the command */
+	for(i = 0; i < cmd->len ; i++)
+	{
+		dprintk("%s: 0x%02x == 0x%02x\n", __func__, i, cmd->args[i]);
+		cx24116_writereg(state, i, cmd->args[i]);
+	}
+
+	/* Start execution and wait for cmd to terminate */
+	cx24116_writereg(state, CX24116_REG_EXECUTE, 0x01);
+	while( cx24116_readreg(state, CX24116_REG_EXECUTE) )
+	{
+		msleep(10);
+		if(i++ > 64)
+		{
+			/* Avoid looping forever if the firmware does no respond */
+			printk("%s() Firmware not responding\n", __func__);
+			return -EREMOTEIO;
+		}
+	}
+	return 0;
+}
+
+static int cx24116_load_firmware (struct dvb_frontend* fe, const struct firmware *fw)
+{
+	struct cx24116_state* state = fe->demodulator_priv;
+	struct cx24116_cmd cmd;
+	int i, ret;
+	unsigned char vers[4];
+
+	dprintk("%s\n", __func__);
+	dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n"
+			,fw->size
+			,fw->data[0]
+			,fw->data[1]
+			,fw->data[ fw->size-2 ]
+			,fw->data[ fw->size-1 ]
+			);
+
+	/* Toggle 88x SRST pin to reset demod */
+	if (state->config->reset_device)
+		state->config->reset_device(fe);
+
+	/* Begin the firmware load process */
+	/* Prepare the demod, load the firmware, cleanup after load */
+
+	/* Init PLL */
+	cx24116_writereg(state, 0xE5, 0x00);
+	cx24116_writereg(state, 0xF1, 0x08);
+	cx24116_writereg(state, 0xF2, 0x13);
+
+	/* Start PLL */
+	cx24116_writereg(state, 0xe0, 0x03);
+	cx24116_writereg(state, 0xe0, 0x00);
+
+	/* Unknown */
+	cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46);
+	cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00);
+
+	/* Unknown */
+	cx24116_writereg(state, 0xF0, 0x03);
+	cx24116_writereg(state, 0xF4, 0x81);
+	cx24116_writereg(state, 0xF5, 0x00);
+	cx24116_writereg(state, 0xF6, 0x00);
+
+	/* write the entire firmware as one transaction */
+	cx24116_writeregN(state, 0xF7, fw->data, fw->size);
+
+	cx24116_writereg(state, 0xF4, 0x10);
+	cx24116_writereg(state, 0xF0, 0x00);
+	cx24116_writereg(state, 0xF8, 0x06);
+
+	/* Firmware CMD 10: VCO config */
+	cmd.args[0x00] = CMD_SET_VCO;
+	cmd.args[0x01] = 0x05;
+	cmd.args[0x02] = 0xdc;
+	cmd.args[0x03] = 0xda;
+	cmd.args[0x04] = 0xae;
+	cmd.args[0x05] = 0xaa;
+	cmd.args[0x06] = 0x04;
+	cmd.args[0x07] = 0x9d;
+	cmd.args[0x08] = 0xfc;
+	cmd.args[0x09] = 0x06;
+	cmd.len= 0x0a;
+	ret = cx24116_cmd_execute(fe, &cmd);
+	if (ret != 0)
+		return ret;
+
+	cx24116_writereg(state, CX24116_REG_SSTATUS, 0x00);
+
+	/* Firmware CMD 14: Tuner config */
+	cmd.args[0x00] = CMD_TUNERINIT;
+	cmd.args[0x01] = 0x00;
+	cmd.args[0x02] = 0x00;
+	cmd.len= 0x03;
+	ret = cx24116_cmd_execute(fe, &cmd);
+	if (ret != 0)
+		return ret;
+
+	cx24116_writereg(state, 0xe5, 0x00);
+
+	/* Firmware CMD 13: MPEG config */
+	cmd.args[0x00] = CMD_MPEGCONFIG;
+	cmd.args[0x01] = 0x01;
+	cmd.args[0x02] = 0x75;
+	cmd.args[0x03] = 0x00;
+	if (state->config->mpg_clk_pos_pol)
+		cmd.args[0x04] = state->config->mpg_clk_pos_pol;
+	else
+		cmd.args[0x04] = 0x02;
+	cmd.args[0x05] = 0x00;
+	cmd.len= 0x06;
+	ret = cx24116_cmd_execute(fe, &cmd);
+	if (ret != 0)
+		return ret;
+
+	/* Firmware CMD 35: Get firmware version */
+	cmd.args[0x00] = CMD_UPDFWVERS;
+	cmd.len= 0x02;
+	for(i=0; i<4; i++) {
+		cmd.args[0x01] = i;
+		ret = cx24116_cmd_execute(fe, &cmd);
+		if (ret != 0)
+			return ret;
+		vers[i]= cx24116_readreg(state, CX24116_REG_MAILBOX);
+	}
+	printk("%s: FW version %i.%i.%i.%i\n", __func__,
+		vers[0], vers[1], vers[2], vers[3]);
+
+	return 0;
+}
+
+static int cx24116_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+	/* The isl6421 module will override this function in the fops. */
+	dprintk("%s() This should never appear if the isl6421 module is loaded correctly\n",__func__);
+
+	return -EOPNOTSUPP;
+}
+
+static int cx24116_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+
+	int lock = cx24116_readreg(state, CX24116_REG_SSTATUS);
+
+	dprintk("%s: status = 0x%02x\n", __func__, lock);
+
+	*status = 0;
+
+	if (lock & CX24116_HAS_SIGNAL)
+		*status |= FE_HAS_SIGNAL;
+	if (lock & CX24116_HAS_CARRIER)
+		*status |= FE_HAS_CARRIER;
+	if (lock & CX24116_HAS_VITERBI)
+		*status |= FE_HAS_VITERBI;
+	if (lock & CX24116_HAS_SYNCLOCK)
+		*status |= FE_HAS_SYNC | FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int cx24116_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+
+	dprintk("%s()\n", __func__);
+
+	*ber =  ( cx24116_readreg(state, CX24116_REG_BER24) << 24 ) |
+		( cx24116_readreg(state, CX24116_REG_BER16) << 16 ) |
+		( cx24116_readreg(state, CX24116_REG_BER8 ) << 8  ) |
+		  cx24116_readreg(state, CX24116_REG_BER0 );
+
+	return 0;
+}
+
+/* TODO Determine function and scale appropriately */
+static int cx24116_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	struct cx24116_cmd cmd;
+	int ret;
+	u16 sig_reading;
+
+	dprintk("%s()\n", __func__);
+
+	/* Firmware CMD 19: Get AGC */
+	cmd.args[0x00] = CMD_GETAGC;
+	cmd.len= 0x01;
+	ret = cx24116_cmd_execute(fe, &cmd);
+	if (ret != 0)
+		return ret;
+
+	sig_reading = ( cx24116_readreg(state, CX24116_REG_SSTATUS) & CX24116_SIGNAL_MASK ) |
+		( cx24116_readreg(state, CX24116_REG_SIGNAL) << 6 );
+	*signal_strength= 0 - sig_reading;
+
+	dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n", __func__, sig_reading, *signal_strength);
+
+	return 0;
+}
+
+/* SNR (0..100)% = (sig & 0xf0) * 10 + (sig & 0x0f) * 10 / 16 */
+static int cx24116_read_snr_pct(struct dvb_frontend* fe, u16* snr)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	u8 snr_reading;
+	static const u32 snr_tab[] = { /* 10 x Table (rounded up) */
+		0x00000,0x0199A,0x03333,0x04ccD,0x06667,
+			0x08000,0x0999A,0x0b333,0x0cccD,0x0e667,
+		0x10000,0x1199A,0x13333,0x14ccD,0x16667,0x18000 };
+
+	dprintk("%s()\n", __func__);
+
+	snr_reading = cx24116_readreg(state, CX24116_REG_QUALITY0);
+
+	if(snr_reading >= 0xa0 /* 100% */)
+		*snr = 0xffff;
+	else
+		*snr = snr_tab [ ( snr_reading & 0xf0 )   >> 4 ] +
+			( snr_tab [ ( snr_reading & 0x0f ) ] >> 4 );
+
+	dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__,
+		snr_reading, *snr);
+
+	return 0;
+}
+
+/* The reelbox patches show the value in the registers represents
+ * ESNO, from 0->30db (values 0->300). We provide this value by
+ * default.
+ */
+static int cx24116_read_snr_esno(struct dvb_frontend* fe, u16* snr)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+
+	dprintk("%s()\n", __func__);
+
+	*snr = cx24116_readreg(state, CX24116_REG_QUALITY8) << 8 |
+		cx24116_readreg(state, CX24116_REG_QUALITY0);
+
+	dprintk("%s: raw 0x%04x\n", __func__, *snr);
+
+	return 0;
+}
+
+static int cx24116_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+	if (esno_snr == 1)
+		return cx24116_read_snr_esno(fe, snr);
+	else
+		return cx24116_read_snr_pct(fe, snr);
+}
+
+static int cx24116_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+
+	dprintk("%s()\n", __func__);
+
+	*ucblocks = ( cx24116_readreg(state, CX24116_REG_UCB8) << 8 ) |
+		cx24116_readreg(state, CX24116_REG_UCB0);
+
+	return 0;
+}
+
+/* Overwrite the current tuning params, we are about to tune */
+static void cx24116_clone_params(struct dvb_frontend* fe)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	memcpy(&state->dcur, &state->dnxt, sizeof(state->dcur));
+}
+
+/* Wait for LNB */
+static int cx24116_wait_for_lnb(struct dvb_frontend* fe)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	int i;
+
+	dprintk("%s() qstatus = 0x%02x\n", __func__,
+		cx24116_readreg(state, CX24116_REG_QSTATUS));
+
+	/* Wait for up to 300 ms */
+	for(i = 0; i < 30 ; i++) {
+		if (cx24116_readreg(state, CX24116_REG_QSTATUS) & 0x20)
+			return 0;
+		msleep(10);
+	}
+
+	dprintk("%s(): LNB not ready\n", __func__);
+
+	return -ETIMEDOUT; /* -EBUSY ? */
+}
+
+static int cx24116_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+	struct cx24116_cmd cmd;
+	int ret;
+
+	dprintk("%s(%d)\n", __func__, tone);
+	if ( (tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF) ) {
+		printk("%s: Invalid, tone=%d\n", __func__, tone);
+		return -EINVAL;
+	}
+
+	/* Wait for LNB ready */
+	ret = cx24116_wait_for_lnb(fe);
+	if(ret != 0)
+		return ret;
+
+	/* Min delay time after DiSEqC send */
+	msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */
+
+	/* This is always done before the tone is set */
+	cmd.args[0x00] = CMD_SET_TONEPRE;
+	cmd.args[0x01] = 0x00;
+	cmd.len= 0x02;
+	ret = cx24116_cmd_execute(fe, &cmd);
+	if (ret != 0)
+		return ret;
+
+	/* Now we set the tone */
+	cmd.args[0x00] = CMD_SET_TONE;
+	cmd.args[0x01] = 0x00;
+	cmd.args[0x02] = 0x00;
+
+	switch (tone) {
+	case SEC_TONE_ON:
+		dprintk("%s: setting tone on\n", __func__);
+		cmd.args[0x03] = 0x01;
+		break;
+	case SEC_TONE_OFF:
+		dprintk("%s: setting tone off\n",__func__);
+		cmd.args[0x03] = 0x00;
+		break;
+	}
+	cmd.len= 0x04;
+
+	/* Min delay time before DiSEqC send */
+	msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */
+
+	return cx24116_cmd_execute(fe, &cmd);
+}
+
+/* Initialise DiSEqC */
+static int cx24116_diseqc_init(struct dvb_frontend* fe)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	struct cx24116_cmd cmd;
+	int ret;
+
+	/* Firmware CMD 20: LNB/DiSEqC config */
+	cmd.args[0x00] = CMD_LNBCONFIG;
+	cmd.args[0x01] = 0x00;
+	cmd.args[0x02] = 0x10;
+	cmd.args[0x03] = 0x00;
+	cmd.args[0x04] = 0x8f;
+	cmd.args[0x05] = 0x28;
+	cmd.args[0x06] = (toneburst == CX24116_DISEQC_TONEOFF) ? 0x00 : 0x01;
+	cmd.args[0x07] = 0x01;
+	cmd.len= 0x08;
+	ret = cx24116_cmd_execute(fe, &cmd);
+	if (ret != 0)
+		return ret;
+
+	/* Prepare a DiSEqC command */
+	state->dsec_cmd.args[0x00] = CMD_LNBSEND;
+
+	/* DiSEqC burst */
+	state->dsec_cmd.args[CX24116_DISEQC_BURST]  = CX24116_DISEQC_MINI_A;
+
+	/* Unknown */
+	state->dsec_cmd.args[CX24116_DISEQC_ARG2_2] = 0x02;
+	state->dsec_cmd.args[CX24116_DISEQC_ARG3_0] = 0x00;
+	state->dsec_cmd.args[CX24116_DISEQC_ARG4_0] = 0x00; /* Continuation flag? */
+
+	/* DiSEqC message length */
+	state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = 0x00;
+
+	/* Command length */
+	state->dsec_cmd.len= CX24116_DISEQC_MSGOFS;
+
+	return 0;
+}
+
+/* Send DiSEqC message with derived burst (hack) || previous burst */
+static int cx24116_send_diseqc_msg(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *d)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	int i, ret;
+
+	/* Dump DiSEqC message */
+	if (debug) {
+		printk("cx24116: %s(", __func__);
+		for(i = 0 ; i < d->msg_len ;) {
+			printk("0x%02x", d->msg[i]);
+			if(++i < d->msg_len)
+				printk(", ");
+			}
+		printk(") toneburst=%d\n", toneburst);
+	}
+
+	/* Validate length */
+	if(d->msg_len > (CX24116_ARGLEN - CX24116_DISEQC_MSGOFS))
+		return -EINVAL;
+
+	/* DiSEqC message */
+	for (i = 0; i < d->msg_len; i++)
+		state->dsec_cmd.args[CX24116_DISEQC_MSGOFS + i] = d->msg[i];
+
+	/* DiSEqC message length */
+	state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = d->msg_len;
+
+	/* Command length */
+	state->dsec_cmd.len= CX24116_DISEQC_MSGOFS + state->dsec_cmd.args[CX24116_DISEQC_MSGLEN];
+
+	/* DiSEqC toneburst */
+	if(toneburst == CX24116_DISEQC_MESGCACHE)
+		/* Message is cached */
+		return 0;
+
+	else if(toneburst == CX24116_DISEQC_TONEOFF)
+		/* Message is sent without burst */
+		state->dsec_cmd.args[CX24116_DISEQC_BURST] = 0;
+
+	else if(toneburst == CX24116_DISEQC_TONECACHE) {
+		/*
+		 * Message is sent with derived else cached burst
+		 *
+		 * WRITE PORT GROUP COMMAND 38
+		 *
+		 * 0/A/A: E0 10 38 F0..F3
+		 * 1/B/B: E0 10 38 F4..F7
+		 * 2/C/A: E0 10 38 F8..FB
+		 * 3/D/B: E0 10 38 FC..FF
+		 *
+		 * databyte[3]= 8421:8421
+		 *              ABCD:WXYZ
+		 *              CLR :SET
+		 *
+		 *              WX= PORT SELECT 0..3    (X=TONEBURST)
+		 *              Y = VOLTAGE             (0=13V, 1=18V)
+		 *              Z = BAND                (0=LOW, 1=HIGH(22K))
+		 */
+		if(d->msg_len >= 4 && d->msg[2] == 0x38)
+			state->dsec_cmd.args[CX24116_DISEQC_BURST] = ((d->msg[3] & 4) >> 2);
+		if(debug)
+			dprintk("%s burst=%d\n", __func__, state->dsec_cmd.args[CX24116_DISEQC_BURST]);
+	}
+
+	/* Wait for LNB ready */
+	ret = cx24116_wait_for_lnb(fe);
+	if(ret != 0)
+		return ret;
+
+	/* Wait for voltage/min repeat delay */
+	msleep(100);
+
+	/* Command */
+	ret = cx24116_cmd_execute(fe, &state->dsec_cmd);
+	if(ret != 0)
+		return ret;
+	/*
+	 * Wait for send
+	 *
+	 * Eutelsat spec:
+	 * >15ms delay          + (XXX determine if FW does this, see set_tone)
+	 *  13.5ms per byte     +
+	 * >15ms delay          +
+	 *  12.5ms burst        +
+	 * >15ms delay            (XXX determine if FW does this, see set_tone)
+	 */
+	msleep( (state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + ((toneburst == CX24116_DISEQC_TONEOFF) ? 30 : 60) );
+
+	return 0;
+}
+
+/* Send DiSEqC burst */
+static int cx24116_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	int ret;
+
+	dprintk("%s(%d) toneburst=%d\n",__func__, burst, toneburst);
+
+	/* DiSEqC burst */
+	if (burst == SEC_MINI_A)
+		state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_A;
+	else if(burst == SEC_MINI_B)
+		state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_B;
+	else
+		return -EINVAL;
+
+	/* DiSEqC toneburst */
+	if(toneburst != CX24116_DISEQC_MESGCACHE)
+		/* Burst is cached */
+		return 0;
+
+	/* Burst is to be sent with cached message */
+
+	/* Wait for LNB ready */
+	ret = cx24116_wait_for_lnb(fe);
+	if(ret != 0)
+		return ret;
+
+	/* Wait for voltage/min repeat delay */
+	msleep(100);
+
+	/* Command */
+	ret = cx24116_cmd_execute(fe, &state->dsec_cmd);
+	if(ret != 0)
+		return ret;
+
+	/*
+	 * Wait for send
+	 *
+	 * Eutelsat spec:
+	 * >15ms delay          + (XXX determine if FW does this, see set_tone)
+	 *  13.5ms per byte     +
+	 * >15ms delay          +
+	 *  12.5ms burst        +
+	 * >15ms delay            (XXX determine if FW does this, see set_tone)
+	 */
+	msleep( (state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + 60 );
+
+	return 0;
+}
+
+static void cx24116_release(struct dvb_frontend* fe)
+{
+	struct cx24116_state* state = fe->demodulator_priv;
+	dprintk("%s\n",__func__);
+	kfree(state);
+}
+
+static struct dvb_frontend_ops cx24116_ops;
+
+struct dvb_frontend* cx24116_attach(const struct cx24116_config* config,
+				    struct i2c_adapter* i2c)
+{
+	struct cx24116_state* state = NULL;
+	int ret;
+
+	dprintk("%s\n",__func__);
+
+	/* allocate memory for the internal state */
+	state = kmalloc(sizeof(struct cx24116_state), GFP_KERNEL);
+	if (state == NULL) {
+		printk("Unable to kmalloc\n");
+		goto error1;
+	}
+
+	/* setup the state */
+	memset(state, 0, sizeof(struct cx24116_state));
+
+	state->config = config;
+	state->i2c = i2c;
+
+	/* check if the demod is present */
+	ret = (cx24116_readreg(state, 0xFF) << 8) | cx24116_readreg(state, 0xFE);
+	if (ret != 0x0501) {
+		printk("Invalid probe, probably not a CX24116 device\n");
+		goto error2;
+	}
+
+	/* create dvb_frontend */
+	memcpy(&state->frontend.ops, &cx24116_ops, sizeof(struct dvb_frontend_ops));
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error2: kfree(state);
+error1: return NULL;
+}
+/*
+ * Initialise or wake up device
+ *
+ * Power config will reset and load initial firmware if required
+ */
+static int cx24116_initfe(struct dvb_frontend* fe)
+{
+	struct cx24116_state* state = fe->demodulator_priv;
+	struct cx24116_cmd cmd;
+	int ret;
+
+	dprintk("%s()\n",__func__);
+
+	/* Power on */
+	cx24116_writereg(state, 0xe0, 0);
+	cx24116_writereg(state, 0xe1, 0);
+	cx24116_writereg(state, 0xea, 0);
+
+	/* Firmware CMD 36: Power config */
+	cmd.args[0x00] = CMD_TUNERSLEEP;
+	cmd.args[0x01] = 0;
+	cmd.len= 0x02;
+	ret = cx24116_cmd_execute(fe, &cmd);
+	if(ret != 0)
+		return ret;
+
+	return cx24116_diseqc_init(fe);
+}
+
+/*
+ * Put device to sleep
+ */
+static int cx24116_sleep(struct dvb_frontend* fe)
+{
+	struct cx24116_state* state = fe->demodulator_priv;
+	struct cx24116_cmd cmd;
+	int ret;
+
+	dprintk("%s()\n",__func__);
+
+	/* Firmware CMD 36: Power config */
+	cmd.args[0x00] = CMD_TUNERSLEEP;
+	cmd.args[0x01] = 1;
+	cmd.len= 0x02;
+	ret = cx24116_cmd_execute(fe, &cmd);
+	if(ret != 0)
+		return ret;
+
+	/* Power off (Shutdown clocks) */
+	cx24116_writereg(state, 0xea, 0xff);
+	cx24116_writereg(state, 0xe1, 1);
+	cx24116_writereg(state, 0xe0, 1);
+
+	return 0;
+}
+
+static int cx24116_set_property(struct dvb_frontend *fe, struct dtv_property* tvp)
+{
+	dprintk("%s(..)\n", __func__);
+	return 0;
+}
+
+static int cx24116_get_property(struct dvb_frontend *fe, struct dtv_property* tvp)
+{
+	dprintk("%s(..)\n", __func__);
+	return 0;
+}
+
+/* dvb-core told us to tune, the tv property cache will be complete,
+ * it's safe for is to pull values and use them for tuning purposes.
+ */
+static int cx24116_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
+{
+	struct cx24116_state *state = fe->demodulator_priv;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+	struct cx24116_cmd cmd;
+	fe_status_t tunerstat;
+	int i, status, ret, retune;
+
+	dprintk("%s()\n",__func__);
+
+	switch(c->delivery_system) {
+		case SYS_DVBS:
+			dprintk("%s: DVB-S delivery system selected\n",__func__);
+
+			/* Only QPSK is supported for DVB-S */
+			if(c->modulation != QPSK) {
+				dprintk("%s: unsupported modulation selected (%d)\n",
+					__func__, c->modulation);
+				return -EOPNOTSUPP;
+			}
+
+			/* Pilot doesn't exist in DVB-S, turn bit off */
+			state->dnxt.pilot_val = CX24116_PILOT_OFF;
+			retune = 1;
+
+			/* DVB-S only supports 0.35 */
+			if(c->rolloff != ROLLOFF_35) {
+				dprintk("%s: unsupported rolloff selected (%d)\n",
+					__func__, c->rolloff);
+				return -EOPNOTSUPP;
+			}
+			state->dnxt.rolloff_val = CX24116_ROLLOFF_035;
+			break;
+
+		case SYS_DVBS2:
+			dprintk("%s: DVB-S2 delivery system selected\n",__func__);
+
+			/*
+			 * NBC 8PSK/QPSK with DVB-S is supported for DVB-S2,
+			 * but not hardware auto detection
+			 */
+			if(c->modulation != PSK_8 && c->modulation != QPSK) {
+				dprintk("%s: unsupported modulation selected (%d)\n",
+					__func__, c->modulation);
+				return -EOPNOTSUPP;
+			}
+
+			switch(c->pilot) {
+				case PILOT_AUTO:	/* Not supported but emulated */
+					retune = 2;	/* Fall-through */
+				case PILOT_OFF:
+					state->dnxt.pilot_val = CX24116_PILOT_OFF;
+					break;
+				case PILOT_ON:
+					state->dnxt.pilot_val = CX24116_PILOT_ON;
+					break;
+				default:
+					dprintk("%s: unsupported pilot mode selected (%d)\n",
+						__func__, c->pilot);
+					return -EOPNOTSUPP;
+			}
+
+			switch(c->rolloff) {
+				case ROLLOFF_20:
+					state->dnxt.rolloff_val= CX24116_ROLLOFF_020;
+					break;
+				case ROLLOFF_25:
+					state->dnxt.rolloff_val= CX24116_ROLLOFF_025;
+					break;
+				case ROLLOFF_35:
+					state->dnxt.rolloff_val= CX24116_ROLLOFF_035;
+					break;
+				case ROLLOFF_AUTO:	/* Rolloff must be explicit */
+				default:
+					dprintk("%s: unsupported rolloff selected (%d)\n",
+						__func__, c->rolloff);
+					return -EOPNOTSUPP;
+			}
+			break;
+
+		default:
+			dprintk("%s: unsupported delivery system selected (%d)\n",
+				__func__, c->delivery_system);
+			return -EOPNOTSUPP;
+	}
+	state->dnxt.modulation = c->modulation;
+	state->dnxt.frequency = c->frequency;
+	state->dnxt.pilot = c->pilot;
+	state->dnxt.rolloff = c->rolloff;
+
+	if ((ret = cx24116_set_inversion(state, c->inversion)) !=  0)
+		return ret;
+
+	/* FEC_NONE/AUTO for DVB-S2 is not supported and detected here */
+	if ((ret = cx24116_set_fec(state, c->modulation, c->fec_inner)) !=  0)
+		return ret;
+
+	if ((ret = cx24116_set_symbolrate(state, c->symbol_rate)) !=  0)
+		return ret;
+
+	/* discard the 'current' tuning parameters and prepare to tune */
+	cx24116_clone_params(fe);
+
+	dprintk("%s:   modulation  = %d\n", __func__, state->dcur.modulation);
+	dprintk("%s:   frequency   = %d\n", __func__, state->dcur.frequency);
+	dprintk("%s:   pilot       = %d (val = 0x%02x)\n", __func__,
+		state->dcur.pilot, state->dcur.pilot_val);
+	dprintk("%s:   retune      = %d\n", __func__, retune);
+	dprintk("%s:   rolloff     = %d (val = 0x%02x)\n", __func__,
+		state->dcur.rolloff, state->dcur.rolloff_val);
+	dprintk("%s:   symbol_rate = %d\n", __func__, state->dcur.symbol_rate);
+	dprintk("%s:   FEC         = %d (mask/val = 0x%02x/0x%02x)\n", __func__,
+		state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val);
+	dprintk("%s:   Inversion   = %d (val = 0x%02x)\n", __func__,
+		state->dcur.inversion, state->dcur.inversion_val);
+
+	/* This is also done in advise/acquire on HVR4000 but not on LITE */
+	if (state->config->set_ts_params)
+		state->config->set_ts_params(fe, 0);
+
+	/* Set/Reset B/W */
+	cmd.args[0x00] = CMD_BANDWIDTH;
+	cmd.args[0x01] = 0x01;
+	cmd.len= 0x02;
+	ret = cx24116_cmd_execute(fe, &cmd);
+	if (ret != 0)
+		return ret;
+
+	/* Prepare a tune request */
+	cmd.args[0x00] = CMD_TUNEREQUEST;
+
+	/* Frequency */
+	cmd.args[0x01] = (state->dcur.frequency & 0xff0000) >> 16;
+	cmd.args[0x02] = (state->dcur.frequency & 0x00ff00) >> 8;
+	cmd.args[0x03] = (state->dcur.frequency & 0x0000ff);
+
+	/* Symbol Rate */
+	cmd.args[0x04] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8;
+	cmd.args[0x05] = ((state->dcur.symbol_rate / 1000) & 0x00ff);
+
+	/* Automatic Inversion */
+	cmd.args[0x06] = state->dcur.inversion_val;
+
+	/* Modulation / FEC / Pilot */
+	cmd.args[0x07] = state->dcur.fec_val | state->dcur.pilot_val;
+
+	cmd.args[0x08] = CX24116_SEARCH_RANGE_KHZ >> 8;
+	cmd.args[0x09] = CX24116_SEARCH_RANGE_KHZ & 0xff;
+	cmd.args[0x0a] = 0x00;
+	cmd.args[0x0b] = 0x00;
+	cmd.args[0x0c] = state->dcur.rolloff_val;
+	cmd.args[0x0d] = state->dcur.fec_mask;
+
+	if (state->dcur.symbol_rate > 30000000) {
+		cmd.args[0x0e] = 0x04;
+		cmd.args[0x0f] = 0x00;
+		cmd.args[0x10] = 0x01;
+		cmd.args[0x11] = 0x77;
+		cmd.args[0x12] = 0x36;
+		cx24116_writereg(state, CX24116_REG_CLKDIV, 0x44);
+		cx24116_writereg(state, CX24116_REG_RATEDIV, 0x01);
+	} else {
+		cmd.args[0x0e] = 0x06;
+		cmd.args[0x0f] = 0x00;
+		cmd.args[0x10] = 0x00;
+		cmd.args[0x11] = 0xFA;
+		cmd.args[0x12] = 0x24;
+		cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46);
+		cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00);
+	}
+
+	cmd.len= 0x13;
+
+	/* We need to support pilot and non-pilot tuning in the
+	 * driver automatically. This is a workaround for because
+	 * the demod does not support autodetect.
+	 */
+	do {
+		/* Reset status register */
+		status = cx24116_readreg(state, CX24116_REG_SSTATUS) & CX24116_SIGNAL_MASK;
+		cx24116_writereg(state, CX24116_REG_SSTATUS, status);
+
+		/* Tune */
+		ret = cx24116_cmd_execute(fe, &cmd);
+		if( ret != 0 )
+			break;
+
+		/*
+		 * Wait for up to 500 ms before retrying
+		 *
+		 * If we are able to tune then generally it occurs within 100ms.
+		 * If it takes longer, try a different toneburst setting.
+		 */
+		for(i = 0; i < 50 ; i++) {
+			cx24116_read_status(fe, &tunerstat);
+			status = tunerstat & (FE_HAS_SIGNAL | FE_HAS_SYNC);
+			if(status == (FE_HAS_SIGNAL | FE_HAS_SYNC)) {
+				dprintk("%s: Tuned\n",__func__);
+				goto tuned;
+			}
+			msleep(10);
+		}
+
+		dprintk("%s: Not tuned\n",__func__);
+
+		/* Toggle pilot bit when in auto-pilot */
+		if(state->dcur.pilot == PILOT_AUTO)
+			cmd.args[0x07] ^= CX24116_PILOT_ON;
+	}
+	while(--retune);
+
+tuned:  /* Set/Reset B/W */
+	cmd.args[0x00] = CMD_BANDWIDTH;
+	cmd.args[0x01] = 0x00;
+	cmd.len= 0x02;
+	ret = cx24116_cmd_execute(fe, &cmd);
+	if (ret != 0)
+		return ret;
+
+	return ret;
+}
+
+static struct dvb_frontend_ops cx24116_ops = {
+
+	.info = {
+		.name = "Conexant CX24116/CX24118",
+		.type = FE_QPSK,
+		.frequency_min = 950000,
+		.frequency_max = 2150000,
+		.frequency_stepsize = 1011, /* kHz for QPSK frontends */
+		.frequency_tolerance = 5000,
+		.symbol_rate_min = 1000000,
+		.symbol_rate_max = 45000000,
+		.caps = FE_CAN_INVERSION_AUTO |
+			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+			FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+			FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+			FE_CAN_QPSK | FE_CAN_RECOVER
+	},
+
+	.release = cx24116_release,
+
+	.init = cx24116_initfe,
+	.sleep = cx24116_sleep,
+	.read_status = cx24116_read_status,
+	.read_ber = cx24116_read_ber,
+	.read_signal_strength = cx24116_read_signal_strength,
+	.read_snr = cx24116_read_snr,
+	.read_ucblocks = cx24116_read_ucblocks,
+	.set_tone = cx24116_set_tone,
+	.set_voltage = cx24116_set_voltage,
+	.diseqc_send_master_cmd = cx24116_send_diseqc_msg,
+	.diseqc_send_burst = cx24116_diseqc_send_burst,
+
+	.set_property = cx24116_set_property,
+	.get_property = cx24116_get_property,
+	.set_frontend = cx24116_set_frontend,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
+
+module_param(toneburst, int, 0644);
+MODULE_PARM_DESC(toneburst, "DiSEqC toneburst 0=OFF, 1=TONE CACHE, 2=MESSAGE CACHE (default:1)");
+
+module_param(esno_snr, int, 0644);
+MODULE_PARM_DESC(debug, "SNR return units, 0=PERCENTAGE 0-100, 1=ESNO(db * 10) (default:0)");
+
+MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24116/cx24118 hardware");
+MODULE_AUTHOR("Steven Toth");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(cx24116_attach);

+ 53 - 0
drivers/media/dvb/frontends/cx24116.h

@@ -0,0 +1,53 @@
+/*
+    Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver
+
+    Copyright (C) 2006 Steven Toth <stoth@linuxtv.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef CX24116_H
+#define CX24116_H
+
+#include <linux/dvb/frontend.h>
+
+struct cx24116_config
+{
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* Need to set device param for start_dma */
+	int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured);
+
+	/* Need to reset device during firmware loading */
+	int (*reset_device)(struct dvb_frontend* fe);
+
+	/* Need to set MPEG parameters */
+	u8 mpg_clk_pos_pol:0x02;
+};
+
+#if defined(CONFIG_DVB_CX24116) || defined(CONFIG_DVB_CX24116_MODULE)
+extern struct dvb_frontend* cx24116_attach(const struct cx24116_config* config,
+					   struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* cx24116_attach(const struct cx24116_config* config,
+						  struct i2c_adapter* i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+	return NULL;
+}
+#endif // CONFIG_DVB_CX24116
+
+#endif /* CX24116_H */

+ 7 - 1
drivers/media/dvb/frontends/dib0070.h

@@ -41,6 +41,7 @@ struct dib0070_config {
 extern struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe,
 					   struct i2c_adapter *i2c,
 					   struct dib0070_config *cfg);
+extern u16 dib0070_wbd_offset(struct dvb_frontend *);
 #else
 static inline struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe,
 						  struct i2c_adapter *i2c,
@@ -49,9 +50,14 @@ static inline struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe,
 	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
 	return NULL;
 }
+
+static inline u16 dib0070_wbd_offset(struct dvb_frontend *fe)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return -ENODEV;
+}
 #endif
 
 extern void dib0070_ctrl_agc_filter(struct dvb_frontend *, uint8_t open);
-extern u16 dib0070_wbd_offset(struct dvb_frontend *);
 
 #endif

+ 5 - 1
drivers/media/dvb/frontends/dib7000m.c

@@ -1284,7 +1284,10 @@ struct i2c_adapter * dib7000m_get_i2c_master(struct dvb_frontend *demod, enum di
 }
 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[])
+#if 0
+/* used with some prototype boards */
+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;
@@ -1329,6 +1332,7 @@ int dib7000m_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defau
 	return 0;
 }
 EXPORT_SYMBOL(dib7000m_i2c_enumeration);
+#endif
 
 static struct dvb_frontend_ops dib7000m_ops;
 struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000m_config *cfg)

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

@@ -1333,7 +1333,8 @@ struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr,
 	/* Ensure the output mode remains at the previous default if it's
 	 * not specifically set by the caller.
 	 */
-	if (st->cfg.output_mode != OUTMODE_MPEG2_SERIAL)
+	if ((st->cfg.output_mode != OUTMODE_MPEG2_SERIAL) &&
+	    (st->cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK))
 		st->cfg.output_mode = OUTMODE_MPEG2_FIFO;
 
 	demod                   = &st->demod;

+ 36 - 5
drivers/media/dvb/frontends/dib7000p.h

@@ -41,6 +41,14 @@ struct dib7000p_config {
 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 dib7000p_i2c_enumeration(struct i2c_adapter *i2c,
+				    int no_of_demods, u8 default_addr,
+				    struct dib7000p_config cfg[]);
+extern int dib7000p_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val);
+extern int dib7000p_set_wbd_ref(struct dvb_frontend *, u16 value);
 #else
 static inline struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap,
 						   u8 i2c_addr,
@@ -49,13 +57,36 @@ static inline struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap,
 	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
 	return NULL;
 }
-#endif
 
-extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]);
+static inline
+struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *fe,
+					    enum dibx000_i2c_interface i, int x)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+
+extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c,
+				    int no_of_demods, u8 default_addr,
+				    struct dib7000p_config cfg[])
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return -ENODEV;
+}
+
+extern int dib7000p_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return -ENODEV;
+}
+
+extern int dib7000p_set_wbd_ref(struct dvb_frontend *fe, u16 value)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return -ENODEV;
+}
+#endif
 
-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);
-extern int dib7000p_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val);
-extern int dib7000p_set_wbd_ref(struct dvb_frontend *, u16 value);
 
 #endif

+ 147 - 141
drivers/media/dvb/frontends/drx397xD.c

@@ -38,35 +38,32 @@ static const char mod_name[] = "drx397xD";
 #define F_SET_0D0h	1
 #define F_SET_0D4h	2
 
-typedef enum fw_ix {
+enum fw_ix {
 #define _FW_ENTRY(a, b)		b
 #include "drx397xD_fw.h"
-} fw_ix_t;
+};
 
 /* chip specifics */
 struct drx397xD_state {
 	struct i2c_adapter *i2c;
 	struct dvb_frontend frontend;
 	struct drx397xD_config config;
-	fw_ix_t chip_rev;
+	enum fw_ix chip_rev;
 	int flags;
 	u32 bandwidth_parm;	/* internal bandwidth conversions */
 	u32 f_osc;		/* w90: actual osc frequency [Hz] */
 };
 
-/*******************************************************************************
- * Firmware
- ******************************************************************************/
-
+/* Firmware */
 static const char *blob_name[] = {
 #define _BLOB_ENTRY(a, b)		a
 #include "drx397xD_fw.h"
 };
 
-typedef enum blob_ix {
+enum blob_ix {
 #define _BLOB_ENTRY(a, b)		b
 #include "drx397xD_fw.h"
-} blob_ix_t;
+};
 
 static struct {
 	const char *name;
@@ -85,7 +82,7 @@ static struct {
 };
 
 /* use only with writer lock aquired */
-static void _drx_release_fw(struct drx397xD_state *s, fw_ix_t ix)
+static void _drx_release_fw(struct drx397xD_state *s, enum fw_ix ix)
 {
 	memset(&fw[ix].data[0], 0, sizeof(fw[0].data));
 	if (fw[ix].file)
@@ -94,9 +91,9 @@ static void _drx_release_fw(struct drx397xD_state *s, fw_ix_t ix)
 
 static void drx_release_fw(struct drx397xD_state *s)
 {
-	fw_ix_t ix = s->chip_rev;
+	enum fw_ix ix = s->chip_rev;
 
-	pr_debug("%s\n", __FUNCTION__);
+	pr_debug("%s\n", __func__);
 
 	write_lock(&fw[ix].lock);
 	if (fw[ix].refcnt) {
@@ -107,13 +104,13 @@ static void drx_release_fw(struct drx397xD_state *s)
 	write_unlock(&fw[ix].lock);
 }
 
-static int drx_load_fw(struct drx397xD_state *s, fw_ix_t ix)
+static int drx_load_fw(struct drx397xD_state *s, enum fw_ix ix)
 {
 	const u8 *data;
 	size_t size, len;
 	int i = 0, j, rc = -EINVAL;
 
-	pr_debug("%s\n", __FUNCTION__);
+	pr_debug("%s\n", __func__);
 
 	if (ix < 0 || ix >= ARRAY_SIZE(fw))
 		return -EINVAL;
@@ -175,32 +172,34 @@ static int drx_load_fw(struct drx397xD_state *s, fw_ix_t ix)
 			goto exit_corrupt;
 		}
 	} while (i < size);
-      exit_corrupt:
+
+exit_corrupt:
 	printk(KERN_ERR "%s: Firmware is corrupt\n", mod_name);
-      exit_err:
+exit_err:
 	_drx_release_fw(s, ix);
 	fw[ix].refcnt--;
-      exit_ok:
+exit_ok:
 	fw[ix].refcnt++;
 	write_unlock(&fw[ix].lock);
+
 	return rc;
 }
 
-/*******************************************************************************
- * i2c bus IO
- ******************************************************************************/
-
-static int write_fw(struct drx397xD_state *s, blob_ix_t ix)
+/* i2c bus IO */
+static int write_fw(struct drx397xD_state *s, enum blob_ix ix)
 {
-	struct i2c_msg msg = {.addr = s->config.demod_address,.flags = 0 };
 	const u8 *data;
 	int len, rc = 0, i = 0;
+	struct i2c_msg msg = {
+		.addr = s->config.demod_address,
+		.flags = 0
+	};
 
 	if (ix < 0 || ix >= ARRAY_SIZE(blob_name)) {
-		pr_debug("%s drx_fw_ix_t out of range\n", __FUNCTION__);
+		pr_debug("%s drx_fw_ix_t out of range\n", __func__);
 		return -EINVAL;
 	}
-	pr_debug("%s %s\n", __FUNCTION__, blob_name[ix]);
+	pr_debug("%s %s\n", __func__, blob_name[ix]);
 
 	read_lock(&fw[s->chip_rev].lock);
 	data = fw[s->chip_rev].data[ix];
@@ -229,33 +228,33 @@ static int write_fw(struct drx397xD_state *s, blob_ix_t ix)
 			goto exit_rc;
 		}
 	}
-      exit_rc:
+exit_rc:
 	read_unlock(&fw[s->chip_rev].lock);
+
 	return 0;
 }
 
 /* Function is not endian safe, use the RD16 wrapper below */
-static int _read16(struct drx397xD_state *s, u32 i2c_adr)
+static int _read16(struct drx397xD_state *s, __le32 i2c_adr)
 {
 	int rc;
 	u8 a[4];
-	u16 v;
+	__le16 v;
 	struct i2c_msg msg[2] = {
 		{
-		 .addr = s->config.demod_address,
-		 .flags = 0,
-		 .buf = a,
-		 .len = sizeof(a)
-		 }
-		, {
-		   .addr = s->config.demod_address,
-		   .flags = I2C_M_RD,
-		   .buf = (u8 *) & v,
-		   .len = sizeof(v)
-		   }
+			.addr = s->config.demod_address,
+			.flags = 0,
+			.buf = a,
+			.len = sizeof(a)
+		}, {
+			.addr = s->config.demod_address,
+			.flags = I2C_M_RD,
+			.buf = (u8 *)&v,
+			.len = sizeof(v)
+		}
 	};
 
-	*(u32 *) a = i2c_adr;
+	*(__le32 *) a = i2c_adr;
 
 	rc = i2c_transfer(s->i2c, msg, 2);
 	if (rc != 2)
@@ -265,7 +264,7 @@ static int _read16(struct drx397xD_state *s, u32 i2c_adr)
 }
 
 /* Function is not endian safe, use the WR16.. wrappers below */
-static int _write16(struct drx397xD_state *s, u32 i2c_adr, u16 val)
+static int _write16(struct drx397xD_state *s, __le32 i2c_adr, __le16 val)
 {
 	u8 a[6];
 	int rc;
@@ -276,28 +275,28 @@ static int _write16(struct drx397xD_state *s, u32 i2c_adr, u16 val)
 		.len = sizeof(a)
 	};
 
-	*(u32 *) a = i2c_adr;
-	*(u16 *) & a[4] = val;
+	*(__le32 *)a = i2c_adr;
+	*(__le16 *)&a[4] = val;
 
 	rc = i2c_transfer(s->i2c, &msg, 1);
 	if (rc != 1)
 		return -EIO;
+
 	return 0;
 }
 
-#define WR16(ss,adr, val) \
+#define WR16(ss, adr, val) \
 		_write16(ss, I2C_ADR_C0(adr), cpu_to_le16(val))
-#define WR16_E0(ss,adr, val) \
+#define WR16_E0(ss, adr, val) \
 		_write16(ss, I2C_ADR_E0(adr), cpu_to_le16(val))
-#define RD16(ss,adr) \
+#define RD16(ss, adr) \
 		_read16(ss, I2C_ADR_C0(adr))
 
-#define EXIT_RC( cmd )	if ( (rc = (cmd)) < 0) goto exit_rc
-
-/*******************************************************************************
- * Tuner callback
- ******************************************************************************/
+#define EXIT_RC(cmd)	\
+	if ((rc = (cmd)) < 0)	\
+		goto exit_rc
 
+/* Tuner callback */
 static int PLL_Set(struct drx397xD_state *s,
 		   struct dvb_frontend_parameters *fep, int *df_tuner)
 {
@@ -305,7 +304,7 @@ static int PLL_Set(struct drx397xD_state *s,
 	u32 f_tuner, f = fep->frequency;
 	int rc;
 
-	pr_debug("%s\n", __FUNCTION__);
+	pr_debug("%s\n", __func__);
 
 	if ((f > s->frontend.ops.tuner_ops.info.frequency_max) ||
 	    (f < s->frontend.ops.tuner_ops.info.frequency_min))
@@ -325,28 +324,26 @@ static int PLL_Set(struct drx397xD_state *s,
 		return rc;
 
 	*df_tuner = f_tuner - f;
-	pr_debug("%s requested %d [Hz] tuner %d [Hz]\n", __FUNCTION__, f,
+	pr_debug("%s requested %d [Hz] tuner %d [Hz]\n", __func__, f,
 		 f_tuner);
 
 	return 0;
 }
 
-/*******************************************************************************
- * Demodulator helper functions
- ******************************************************************************/
-
+/* Demodulator helper functions */
 static int SC_WaitForReady(struct drx397xD_state *s)
 {
 	int cnt = 1000;
 	int rc;
 
-	pr_debug("%s\n", __FUNCTION__);
+	pr_debug("%s\n", __func__);
 
 	while (cnt--) {
 		rc = RD16(s, 0x820043);
 		if (rc == 0)
 			return 0;
 	}
+
 	return -1;
 }
 
@@ -354,13 +351,14 @@ static int SC_SendCommand(struct drx397xD_state *s, int cmd)
 {
 	int rc;
 
-	pr_debug("%s\n", __FUNCTION__);
+	pr_debug("%s\n", __func__);
 
 	WR16(s, 0x820043, cmd);
 	SC_WaitForReady(s);
 	rc = RD16(s, 0x820042);
 	if ((rc & 0xffff) == 0xffff)
 		return -1;
+
 	return 0;
 }
 
@@ -368,7 +366,7 @@ static int HI_Command(struct drx397xD_state *s, u16 cmd)
 {
 	int rc, cnt = 1000;
 
-	pr_debug("%s\n", __FUNCTION__);
+	pr_debug("%s\n", __func__);
 
 	rc = WR16(s, 0x420032, cmd);
 	if (rc < 0)
@@ -383,22 +381,24 @@ static int HI_Command(struct drx397xD_state *s, u16 cmd)
 		if (rc < 0)
 			return rc;
 	} while (--cnt);
+
 	return rc;
 }
 
 static int HI_CfgCommand(struct drx397xD_state *s)
 {
 
-	pr_debug("%s\n", __FUNCTION__);
+	pr_debug("%s\n", __func__);
 
 	WR16(s, 0x420033, 0x3973);
-	WR16(s, 0x420034, s->config.w50);	// code 4, log 4
-	WR16(s, 0x420035, s->config.w52);	// code 15,  log 9
+	WR16(s, 0x420034, s->config.w50);	/* code 4, log 4 */
+	WR16(s, 0x420035, s->config.w52);	/* code 15,  log 9 */
 	WR16(s, 0x420036, s->config.demod_address << 1);
-	WR16(s, 0x420037, s->config.w56);	// code (set_i2c ??  initX 1 ), log 1
-//      WR16(s, 0x420033, 0x3973);
+	WR16(s, 0x420037, s->config.w56);	/* code (set_i2c ??  initX 1 ), log 1 */
+	/* WR16(s, 0x420033, 0x3973); */
 	if ((s->config.w56 & 8) == 0)
 		return HI_Command(s, 3);
+
 	return WR16(s, 0x420032, 0x3);
 }
 
@@ -419,7 +419,7 @@ static int SetCfgIfAgc(struct drx397xD_state *s, struct drx397xD_CfgIfAgc *agc)
 	u16 w0C = agc->w0C;
 	int quot, rem, i, rc = -EINVAL;
 
-	pr_debug("%s\n", __FUNCTION__);
+	pr_debug("%s\n", __func__);
 
 	if (agc->w04 > 0x3ff)
 		goto exit_rc;
@@ -468,7 +468,7 @@ static int SetCfgIfAgc(struct drx397xD_state *s, struct drx397xD_CfgIfAgc *agc)
 	i = slowIncrDecLUT_15272[rem / 28];
 	EXIT_RC(WR16(s, 0x0c2002b, i));
 	rc = WR16(s, 0x0c2002c, i);
-      exit_rc:
+exit_rc:
 	return rc;
 }
 
@@ -478,7 +478,7 @@ static int SetCfgRfAgc(struct drx397xD_state *s, struct drx397xD_CfgRfAgc *agc)
 	u16 w06 = agc->w06;
 	int rc = -1;
 
-	pr_debug("%s %d 0x%x 0x%x\n", __FUNCTION__, agc->d00, w04, w06);
+	pr_debug("%s %d 0x%x 0x%x\n", __func__, agc->d00, w04, w06);
 
 	if (w04 > 0x3ff)
 		goto exit_rc;
@@ -498,7 +498,7 @@ static int SetCfgRfAgc(struct drx397xD_state *s, struct drx397xD_CfgRfAgc *agc)
 		rc &= ~2;
 		break;
 	case 0:
-		// loc_8000659
+		/* loc_8000659 */
 		s->config.w9C &= ~2;
 		EXIT_RC(WR16(s, 0x0c20015, s->config.w9C));
 		EXIT_RC(RD16(s, 0x0c20010));
@@ -522,7 +522,8 @@ static int SetCfgRfAgc(struct drx397xD_state *s, struct drx397xD_CfgRfAgc *agc)
 		rc |= 2;
 	}
 	rc = WR16(s, 0x0c20013, rc);
-      exit_rc:
+
+exit_rc:
 	return rc;
 }
 
@@ -554,7 +555,7 @@ static int CorrectSysClockDeviation(struct drx397xD_state *s)
 	int lockstat;
 	u32 clk, clk_limit;
 
-	pr_debug("%s\n", __FUNCTION__);
+	pr_debug("%s\n", __func__);
 
 	if (s->config.d5C == 0) {
 		EXIT_RC(WR16(s, 0x08200e8, 0x010));
@@ -598,11 +599,12 @@ static int CorrectSysClockDeviation(struct drx397xD_state *s)
 
 	if (clk - s->config.f_osc * 1000 + clk_limit <= 2 * clk_limit) {
 		s->f_osc = clk;
-		pr_debug("%s: osc %d %d [Hz]\n", __FUNCTION__,
+		pr_debug("%s: osc %d %d [Hz]\n", __func__,
 			 s->config.f_osc * 1000, clk - s->config.f_osc * 1000);
 	}
 	rc = WR16(s, 0x08200e8, 0);
-      exit_rc:
+
+exit_rc:
 	return rc;
 }
 
@@ -610,7 +612,7 @@ static int ConfigureMPEGOutput(struct drx397xD_state *s, int type)
 {
 	int rc, si, bp;
 
-	pr_debug("%s\n", __FUNCTION__);
+	pr_debug("%s\n", __func__);
 
 	si = s->config.wA0;
 	if (s->config.w98 == 0) {
@@ -620,17 +622,17 @@ static int ConfigureMPEGOutput(struct drx397xD_state *s, int type)
 		si &= ~1;
 		bp = 0x200;
 	}
-	if (s->config.w9A == 0) {
+	if (s->config.w9A == 0)
 		si |= 0x80;
-	} else {
+	else
 		si &= ~0x80;
-	}
 
 	EXIT_RC(WR16(s, 0x2150045, 0));
 	EXIT_RC(WR16(s, 0x2150010, si));
 	EXIT_RC(WR16(s, 0x2150011, bp));
 	rc = WR16(s, 0x2150012, (type == 0 ? 0xfff : 0));
-      exit_rc:
+
+exit_rc:
 	return rc;
 }
 
@@ -646,7 +648,7 @@ static int drx_tune(struct drx397xD_state *s,
 
 	int rc, df_tuner;
 	int a, b, c, d;
-	pr_debug("%s %d\n", __FUNCTION__, s->config.d60);
+	pr_debug("%s %d\n", __func__, s->config.d60);
 
 	if (s->config.d60 != 2)
 		goto set_tuner;
@@ -658,7 +660,7 @@ static int drx_tune(struct drx397xD_state *s,
 	rc = ConfigureMPEGOutput(s, 0);
 	if (rc < 0)
 		goto set_tuner;
-      set_tuner:
+set_tuner:
 
 	rc = PLL_Set(s, fep, &df_tuner);
 	if (rc < 0) {
@@ -835,16 +837,16 @@ static int drx_tune(struct drx397xD_state *s,
 		rc = WR16(s, 0x2010012, 0);
 		if (rc < 0)
 			goto exit_rc;
-		//              QPSK    QAM16   QAM64
-		ebx = 0x19f;	//                 62
-		ebp = 0x1fb;	//                 15
-		v20 = 0x16a;	//  62
-		v1E = 0x195;	//         62
-		v16 = 0x1bb;	//  15
-		v14 = 0x1ef;	//         15
-		v12 = 5;	//  16
-		v10 = 5;	//         16
-		v0E = 5;	//                 16
+				/* QPSK    QAM16  QAM64	*/
+		ebx = 0x19f;	/*                 62	*/
+		ebp = 0x1fb;	/*                 15	*/
+		v20 = 0x16a;	/*  62			*/
+		v1E = 0x195;	/*         62		*/
+		v16 = 0x1bb;	/*  15			*/
+		v14 = 0x1ef;	/*         15		*/
+		v12 = 5;	/*  16			*/
+		v10 = 5;	/*         16		*/
+		v0E = 5;	/*                 16	*/
 	}
 
 	switch (fep->u.ofdm.constellation) {
@@ -997,17 +999,17 @@ static int drx_tune(struct drx397xD_state *s,
 	case BANDWIDTH_8_MHZ:	/* 0 */
 	case BANDWIDTH_AUTO:
 		rc = WR16(s, 0x0c2003f, 0x32);
-		s->bandwidth_parm = ebx = 0x8b8249;	// 9142857
+		s->bandwidth_parm = ebx = 0x8b8249;
 		edx = 0;
 		break;
 	case BANDWIDTH_7_MHZ:
 		rc = WR16(s, 0x0c2003f, 0x3b);
-		s->bandwidth_parm = ebx = 0x7a1200;	// 8000000
+		s->bandwidth_parm = ebx = 0x7a1200;
 		edx = 0x4807;
 		break;
 	case BANDWIDTH_6_MHZ:
 		rc = WR16(s, 0x0c2003f, 0x47);
-		s->bandwidth_parm = ebx = 0x68a1b6;	// 6857142
+		s->bandwidth_parm = ebx = 0x68a1b6;
 		edx = 0x0f07;
 		break;
 	};
@@ -1060,8 +1062,6 @@ static int drx_tune(struct drx397xD_state *s,
 	WR16(s, 0x0820040, 1);
 	SC_SendCommand(s, 1);
 
-//      rc = WR16(s, 0x2150000, 1);
-//      if (rc < 0) goto exit_rc;
 
 	rc = WR16(s, 0x2150000, 2);
 	rc = WR16(s, 0x2150016, a);
@@ -1069,7 +1069,8 @@ static int drx_tune(struct drx397xD_state *s,
 	rc = WR16(s, 0x2150036, 0);
 	rc = WR16(s, 0x2150000, 1);
 	s->config.d60 = 2;
-      exit_rc:
+
+exit_rc:
 	return rc;
 }
 
@@ -1082,7 +1083,7 @@ static int drx397x_init(struct dvb_frontend *fe)
 	struct drx397xD_state *s = fe->demodulator_priv;
 	int rc;
 
-	pr_debug("%s\n", __FUNCTION__);
+	pr_debug("%s\n", __func__);
 
 	s->config.rfagc.d00 = 2;	/* 0x7c */
 	s->config.rfagc.w04 = 0;
@@ -1102,18 +1103,18 @@ static int drx397x_init(struct dvb_frontend *fe)
 
 	/* HI_CfgCommand */
 	s->config.w50 = 4;
-	s->config.w52 = 9;	// 0xf;
+	s->config.w52 = 9;
 
-	s->config.f_if = 42800000;	/* d14: intermediate frequency [Hz]     */
-	s->config.f_osc = 48000;	/* s66 : oscillator frequency [kHz]     */
-	s->config.w92 = 12000;	// 20000;
+	s->config.f_if = 42800000;	/* d14: intermediate frequency [Hz] */
+	s->config.f_osc = 48000;	/* s66 : oscillator frequency [kHz] */
+	s->config.w92 = 12000;
 
 	s->config.w9C = 0x000e;
 	s->config.w9E = 0x0000;
 
 	/* ConfigureMPEGOutput params */
 	s->config.wA0 = 4;
-	s->config.w98 = 1;	// 0;
+	s->config.w98 = 1;
 	s->config.w9A = 1;
 
 	/* get chip revision */
@@ -1248,7 +1249,7 @@ static int drx397x_init(struct dvb_frontend *fe)
 		rc = WR16(s, 0x0c20012, 1);
 	}
 
-      write_DRXD_InitFE_1:
+write_DRXD_InitFE_1:
 
 	rc = write_fw(s, DRXD_InitFE_1);
 	if (rc < 0)
@@ -1311,7 +1312,8 @@ static int drx397x_init(struct dvb_frontend *fe)
 	s->config.d5C = 0;
 	s->config.d60 = 1;
 	s->config.d48 = 1;
-      error:
+
+error:
 	return rc;
 }
 
@@ -1326,7 +1328,8 @@ static int drx397x_set_frontend(struct dvb_frontend *fe,
 {
 	struct drx397xD_state *s = fe->demodulator_priv;
 
-	s->config.s20d24 = 1;	// 0;
+	s->config.s20d24 = 1;
+
 	return drx_tune(s, params);
 }
 
@@ -1337,18 +1340,16 @@ static int drx397x_get_tune_settings(struct dvb_frontend *fe,
 	fe_tune_settings->min_delay_ms = 10000;
 	fe_tune_settings->step_size = 0;
 	fe_tune_settings->max_drift = 0;
+
 	return 0;
 }
 
-static int drx397x_read_status(struct dvb_frontend *fe, fe_status_t * status)
+static int drx397x_read_status(struct dvb_frontend *fe, fe_status_t *status)
 {
 	struct drx397xD_state *s = fe->demodulator_priv;
 	int lockstat;
 
 	GetLockStatus(s, &lockstat);
-	/* TODO */
-//      if (lockstat & 1)
-//      CorrectSysClockDeviation(s);
 
 	*status = 0;
 	if (lockstat & 2) {
@@ -1356,9 +1357,8 @@ static int drx397x_read_status(struct dvb_frontend *fe, fe_status_t * status)
 		ConfigureMPEGOutput(s, 1);
 		*status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI;
 	}
-	if (lockstat & 4) {
+	if (lockstat & 4)
 		*status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
-	}
 
 	return 0;
 }
@@ -1366,16 +1366,18 @@ static int drx397x_read_status(struct dvb_frontend *fe, fe_status_t * status)
 static int drx397x_read_ber(struct dvb_frontend *fe, unsigned int *ber)
 {
 	*ber = 0;
+
 	return 0;
 }
 
-static int drx397x_read_snr(struct dvb_frontend *fe, u16 * snr)
+static int drx397x_read_snr(struct dvb_frontend *fe, u16 *snr)
 {
 	*snr = 0;
+
 	return 0;
 }
 
-static int drx397x_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
+static int drx397x_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
 {
 	struct drx397xD_state *s = fe->demodulator_priv;
 	int rc;
@@ -1401,6 +1403,7 @@ static int drx397x_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
 	 * The following does the same but with less rounding errors:
 	 */
 	*strength = ~(7720 + (rc * 30744 >> 10));
+
 	return 0;
 }
 
@@ -1408,6 +1411,7 @@ static int drx397x_read_ucblocks(struct dvb_frontend *fe,
 				 unsigned int *ucblocks)
 {
 	*ucblocks = 0;
+
 	return 0;
 }
 
@@ -1436,22 +1440,22 @@ static struct dvb_frontend_ops drx397x_ops = {
 		 .frequency_max		= 855250000,
 		 .frequency_stepsize	= 166667,
 		 .frequency_tolerance	= 0,
-		 .caps =					/* 0x0C01B2EAE */
-			 FE_CAN_FEC_1_2			|	// = 0x2,
-			 FE_CAN_FEC_2_3			|	// = 0x4,
-			 FE_CAN_FEC_3_4			|	// = 0x8,
-			 FE_CAN_FEC_5_6			|	// = 0x20,
-			 FE_CAN_FEC_7_8			|	// = 0x80,
-			 FE_CAN_FEC_AUTO		|	// = 0x200,
-			 FE_CAN_QPSK			|	// = 0x400,
-			 FE_CAN_QAM_16			|	// = 0x800,
-			 FE_CAN_QAM_64			|	// = 0x2000,
-			 FE_CAN_QAM_AUTO		|	// = 0x10000,
-			 FE_CAN_TRANSMISSION_MODE_AUTO	|	// = 0x20000,
-			 FE_CAN_GUARD_INTERVAL_AUTO	|	// = 0x80000,
-			 FE_CAN_HIERARCHY_AUTO		|	// = 0x100000,
-			 FE_CAN_RECOVER			|	// = 0x40000000,
-			 FE_CAN_MUTE_TS				// = 0x80000000
+		 .caps =				  /* 0x0C01B2EAE */
+			 FE_CAN_FEC_1_2			| /* = 0x2, */
+			 FE_CAN_FEC_2_3			| /* = 0x4, */
+			 FE_CAN_FEC_3_4			| /* = 0x8, */
+			 FE_CAN_FEC_5_6			| /* = 0x20, */
+			 FE_CAN_FEC_7_8			| /* = 0x80, */
+			 FE_CAN_FEC_AUTO		| /* = 0x200, */
+			 FE_CAN_QPSK			| /* = 0x400, */
+			 FE_CAN_QAM_16			| /* = 0x800, */
+			 FE_CAN_QAM_64			| /* = 0x2000, */
+			 FE_CAN_QAM_AUTO		| /* = 0x10000, */
+			 FE_CAN_TRANSMISSION_MODE_AUTO	| /* = 0x20000, */
+			 FE_CAN_GUARD_INTERVAL_AUTO	| /* = 0x80000, */
+			 FE_CAN_HIERARCHY_AUTO		| /* = 0x100000, */
+			 FE_CAN_RECOVER			| /* = 0x40000000, */
+			 FE_CAN_MUTE_TS			  /* = 0x80000000 */
 	 },
 
 	.release = drx397x_release,
@@ -1472,33 +1476,35 @@ static struct dvb_frontend_ops drx397x_ops = {
 struct dvb_frontend *drx397xD_attach(const struct drx397xD_config *config,
 				     struct i2c_adapter *i2c)
 {
-	struct drx397xD_state *s = NULL;
+	struct drx397xD_state *state;
 
 	/* allocate memory for the internal state */
-	s = kzalloc(sizeof(struct drx397xD_state), GFP_KERNEL);
-	if (s == NULL)
+	state = kzalloc(sizeof(struct drx397xD_state), GFP_KERNEL);
+	if (!state)
 		goto error;
 
 	/* setup the state */
-	s->i2c = i2c;
-	memcpy(&s->config, config, sizeof(struct drx397xD_config));
+	state->i2c = i2c;
+	memcpy(&state->config, config, sizeof(struct drx397xD_config));
 
 	/* check if the demod is there */
-	if (RD16(s, 0x2410019) < 0)
+	if (RD16(state, 0x2410019) < 0)
 		goto error;
 
 	/* create dvb_frontend */
-	memcpy(&s->frontend.ops, &drx397x_ops, sizeof(struct dvb_frontend_ops));
-	s->frontend.demodulator_priv = s;
+	memcpy(&state->frontend.ops, &drx397x_ops,
+			sizeof(struct dvb_frontend_ops));
+	state->frontend.demodulator_priv = state;
+
+	return &state->frontend;
+error:
+	kfree(state);
 
-	return &s->frontend;
-      error:
-	kfree(s);
 	return NULL;
 }
+EXPORT_SYMBOL(drx397xD_attach);
 
 MODULE_DESCRIPTION("Micronas DRX397xD DVB-T Frontend");
 MODULE_AUTHOR("Henk Vergonet");
 MODULE_LICENSE("GPL");
 
-EXPORT_SYMBOL(drx397xD_attach);

+ 3 - 3
drivers/media/dvb/frontends/drx397xD.h

@@ -28,7 +28,7 @@
 #define DRX_F_OFFSET	36000000
 
 #define I2C_ADR_C0(x) \
-(	(u32)cpu_to_le32( \
+(	cpu_to_le32( \
 		(u32)( \
 			(((u32)(x) & (u32)0x000000ffUL)      ) | \
 			(((u32)(x) & (u32)0x0000ff00UL) << 16) | \
@@ -38,7 +38,7 @@
 )
 
 #define I2C_ADR_E0(x) \
-(	(u32)cpu_to_le32( \
+(	cpu_to_le32( \
 		(u32)( \
 			(((u32)(x) & (u32)0x000000ffUL)      ) | \
 			(((u32)(x) & (u32)0x0000ff00UL) << 16) | \
@@ -122,7 +122,7 @@ extern struct dvb_frontend* drx397xD_attach(const struct drx397xD_config *config
 static inline struct dvb_frontend* drx397xD_attach(const struct drx397xD_config *config,
 					   struct i2c_adapter *i2c)
 {
-	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
 	return NULL;
 }
 #endif /* CONFIG_DVB_DRX397XD */

+ 6 - 5
drivers/media/dvb/frontends/dvb_dummy_fe.c

@@ -75,9 +75,10 @@ static int dvb_dummy_fe_get_frontend(struct dvb_frontend* fe, struct dvb_fronten
 
 static int dvb_dummy_fe_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p)
 {
-	if (fe->ops->tuner_ops->set_params) {
-		fe->ops->tuner_ops->set_params(fe, p);
-		if (fe->ops->i2c_gate_ctrl) fe->ops->i2c_gate_ctrl(fe, 0);
+	if (fe->ops.tuner_ops.set_params) {
+		fe->ops.tuner_ops.set_params(fe, p);
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 0);
 	}
 
 	return 0;
@@ -131,7 +132,7 @@ error:
 
 static struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops;
 
-struct dvb_frontend* dvb_dummy_fe_qpsk_attach()
+struct dvb_frontend *dvb_dummy_fe_qpsk_attach(void)
 {
 	struct dvb_dummy_fe_state* state = NULL;
 
@@ -151,7 +152,7 @@ error:
 
 static struct dvb_frontend_ops dvb_dummy_fe_qam_ops;
 
-struct dvb_frontend* dvb_dummy_fe_qam_attach()
+struct dvb_frontend *dvb_dummy_fe_qam_attach(void)
 {
 	struct dvb_dummy_fe_state* state = NULL;
 

+ 133 - 0
drivers/media/dvb/frontends/eds1547.h

@@ -0,0 +1,133 @@
+/* eds1547.h Earda EDS-1547 tuner support
+*
+* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by)
+*
+*	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 EDS1547
+#define EDS1547
+
+static u8 stv0288_earda_inittab[] = {
+	0x01, 0x57,
+	0x02, 0x20,
+	0x03, 0x8e,
+	0x04, 0x8e,
+	0x05, 0x12,
+	0x06, 0x00,
+	0x07, 0x00,
+	0x09, 0x00,
+	0x0a, 0x04,
+	0x0b, 0x00,
+	0x0c, 0x00,
+	0x0d, 0x00,
+	0x0e, 0xd4,
+	0x0f, 0x30,
+	0x11, 0x44,
+	0x12, 0x03,
+	0x13, 0x48,
+	0x14, 0x84,
+	0x15, 0x45,
+	0x16, 0xb7,
+	0x17, 0x9c,
+	0x18, 0x00,
+	0x19, 0xa6,
+	0x1a, 0x88,
+	0x1b, 0x8f,
+	0x1c, 0xf0,
+	0x20, 0x0b,
+	0x21, 0x54,
+	0x22, 0x00,
+	0x23, 0x00,
+	0x2b, 0xff,
+	0x2c, 0xf7,
+	0x30, 0x00,
+	0x31, 0x1e,
+	0x32, 0x14,
+	0x33, 0x0f,
+	0x34, 0x09,
+	0x35, 0x0c,
+	0x36, 0x05,
+	0x37, 0x2f,
+	0x38, 0x16,
+	0x39, 0xbd,
+	0x3a, 0x00,
+	0x3b, 0x13,
+	0x3c, 0x11,
+	0x3d, 0x30,
+	0x40, 0x63,
+	0x41, 0x04,
+	0x42, 0x60,
+	0x43, 0x00,
+	0x44, 0x00,
+	0x45, 0x00,
+	0x46, 0x00,
+	0x47, 0x00,
+	0x4a, 0x00,
+	0x50, 0x10,
+	0x51, 0x36,
+	0x52, 0x09,
+	0x53, 0x94,
+	0x54, 0x62,
+	0x55, 0x29,
+	0x56, 0x64,
+	0x57, 0x2b,
+	0x58, 0x54,
+	0x59, 0x86,
+	0x5a, 0x00,
+	0x5b, 0x9b,
+	0x5c, 0x08,
+	0x5d, 0x7f,
+	0x5e, 0x00,
+	0x5f, 0xff,
+	0x70, 0x00,
+	0x71, 0x00,
+	0x72, 0x00,
+	0x74, 0x00,
+	0x75, 0x00,
+	0x76, 0x00,
+	0x81, 0x00,
+	0x82, 0x3f,
+	0x83, 0x3f,
+	0x84, 0x00,
+	0x85, 0x00,
+	0x88, 0x00,
+	0x89, 0x00,
+	0x8a, 0x00,
+	0x8b, 0x00,
+	0x8c, 0x00,
+	0x90, 0x00,
+	0x91, 0x00,
+	0x92, 0x00,
+	0x93, 0x00,
+	0x94, 0x1c,
+	0x97, 0x00,
+	0xa0, 0x48,
+	0xa1, 0x00,
+	0xb0, 0xb8,
+	0xb1, 0x3a,
+	0xb2, 0x10,
+	0xb3, 0x82,
+	0xb4, 0x80,
+	0xb5, 0x82,
+	0xb6, 0x82,
+	0xb7, 0x82,
+	0xb8, 0x20,
+	0xb9, 0x00,
+	0xf0, 0x00,
+	0xf1, 0x00,
+	0xf2, 0xc0,
+	0xff,0xff,
+};
+
+static struct stv0288_config earda_config = {
+	.demod_address = 0x68,
+	.min_delay_ms = 100,
+	.inittab = stv0288_earda_inittab,
+};
+
+#endif

+ 454 - 0
drivers/media/dvb/frontends/lgs8gl5.c

@@ -0,0 +1,454 @@
+/*
+    Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver
+
+    Copyright (C) 2008 Sirius International (Hong Kong) Limited
+	Timothy Lee <timothy.lee@siriushk.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "dvb_frontend.h"
+#include "lgs8gl5.h"
+
+
+#define REG_RESET		0x02
+#define REG_RESET_OFF			0x01
+#define REG_03			0x03
+#define REG_04			0x04
+#define REG_07			0x07
+#define REG_09			0x09
+#define REG_0A			0x0a
+#define REG_0B			0x0b
+#define REG_0C			0x0c
+#define REG_37			0x37
+#define REG_STRENGTH		0x4b
+#define REG_STRENGTH_MASK		0x7f
+#define REG_STRENGTH_CARRIER		0x80
+#define REG_INVERSION		0x7c
+#define REG_INVERSION_ON		0x80
+#define REG_7D			0x7d
+#define REG_7E			0x7e
+#define REG_A2			0xa2
+#define REG_STATUS		0xa4
+#define REG_STATUS_SYNC		0x04
+#define REG_STATUS_LOCK		0x01
+
+
+struct lgs8gl5_state {
+	struct i2c_adapter *i2c;
+	const struct lgs8gl5_config *config;
+	struct dvb_frontend frontend;
+};
+
+
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) \
+			printk(KERN_DEBUG "lgs8gl5: " args); \
+	} while (0)
+
+
+/* Writes into demod's register */
+static int
+lgs8gl5_write_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
+{
+	int ret;
+	u8 buf[] = {reg, data};
+	struct i2c_msg msg = {
+		.addr  = state->config->demod_address,
+		.flags = 0,
+		.buf   = buf,
+		.len   = 2
+	};
+
+	ret = i2c_transfer(state->i2c, &msg, 1);
+	if (ret != 1)
+		dprintk("%s: error (reg=0x%02x, val=0x%02x, ret=%i)\n",
+			__func__, reg, data, ret);
+	return (ret != 1) ? -1 : 0;
+}
+
+
+/* Reads from demod's register */
+static int
+lgs8gl5_read_reg(struct lgs8gl5_state *state, u8 reg)
+{
+	int ret;
+	u8 b0[] = {reg};
+	u8 b1[] = {0};
+	struct i2c_msg msg[2] = {
+		{
+			.addr  = state->config->demod_address,
+			.flags = 0,
+			.buf   = b0,
+			.len   = 1
+		},
+		{
+			.addr  = state->config->demod_address,
+			.flags = I2C_M_RD,
+			.buf   = b1,
+			.len   = 1
+		}
+	};
+
+	ret = i2c_transfer(state->i2c, msg, 2);
+	if (ret != 2)
+		return -EIO;
+
+	return b1[0];
+}
+
+
+static int
+lgs8gl5_update_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
+{
+	lgs8gl5_read_reg(state, reg);
+	lgs8gl5_write_reg(state, reg, data);
+	return 0;
+}
+
+
+/* Writes into alternate device's register */
+/* TODO:  Find out what that device is for! */
+static int
+lgs8gl5_update_alt_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
+{
+	int ret;
+	u8 b0[] = {reg};
+	u8 b1[] = {0};
+	u8 b2[] = {reg, data};
+	struct i2c_msg msg[3] = {
+		{
+			.addr  = state->config->demod_address + 2,
+			.flags = 0,
+			.buf   = b0,
+			.len   = 1
+		},
+		{
+			.addr  = state->config->demod_address + 2,
+			.flags = I2C_M_RD,
+			.buf   = b1,
+			.len   = 1
+		},
+		{
+			.addr  = state->config->demod_address + 2,
+			.flags = 0,
+			.buf   = b2,
+			.len   = 2
+		},
+	};
+
+	ret = i2c_transfer(state->i2c, msg, 3);
+	return (ret != 3) ? -1 : 0;
+}
+
+
+static void
+lgs8gl5_soft_reset(struct lgs8gl5_state *state)
+{
+	u8 val;
+
+	dprintk("%s\n", __func__);
+
+	val = lgs8gl5_read_reg(state, REG_RESET);
+	lgs8gl5_write_reg(state, REG_RESET, val & ~REG_RESET_OFF);
+	lgs8gl5_write_reg(state, REG_RESET, val | REG_RESET_OFF);
+	msleep(5);
+}
+
+
+/* Starts demodulation */
+static void
+lgs8gl5_start_demod(struct lgs8gl5_state *state)
+{
+	u8  val;
+	int n;
+
+	dprintk("%s\n", __func__);
+
+	lgs8gl5_update_alt_reg(state, 0xc2, 0x28);
+	lgs8gl5_soft_reset(state);
+	lgs8gl5_update_reg(state, REG_07, 0x10);
+	lgs8gl5_update_reg(state, REG_07, 0x10);
+	lgs8gl5_write_reg(state, REG_09, 0x0e);
+	lgs8gl5_write_reg(state, REG_0A, 0xe5);
+	lgs8gl5_write_reg(state, REG_0B, 0x35);
+	lgs8gl5_write_reg(state, REG_0C, 0x30);
+
+	lgs8gl5_update_reg(state, REG_03, 0x00);
+	lgs8gl5_update_reg(state, REG_7E, 0x01);
+	lgs8gl5_update_alt_reg(state, 0xc5, 0x00);
+	lgs8gl5_update_reg(state, REG_04, 0x02);
+	lgs8gl5_update_reg(state, REG_37, 0x01);
+	lgs8gl5_soft_reset(state);
+
+	/* Wait for carrier */
+	for (n = 0;  n < 10;  n++) {
+		val = lgs8gl5_read_reg(state, REG_STRENGTH);
+		dprintk("Wait for carrier[%d] 0x%02X\n", n, val);
+		if (val & REG_STRENGTH_CARRIER)
+			break;
+		msleep(4);
+	}
+	if (!(val & REG_STRENGTH_CARRIER))
+		return;
+
+	/* Wait for lock */
+	for (n = 0;  n < 20;  n++) {
+		val = lgs8gl5_read_reg(state, REG_STATUS);
+		dprintk("Wait for lock[%d] 0x%02X\n", n, val);
+		if (val & REG_STATUS_LOCK)
+			break;
+		msleep(12);
+	}
+	if (!(val & REG_STATUS_LOCK))
+		return;
+
+	lgs8gl5_write_reg(state, REG_7D, lgs8gl5_read_reg(state, REG_A2));
+	lgs8gl5_soft_reset(state);
+}
+
+
+static int
+lgs8gl5_init(struct dvb_frontend *fe)
+{
+	struct lgs8gl5_state *state = fe->demodulator_priv;
+
+	dprintk("%s\n", __func__);
+
+	lgs8gl5_update_alt_reg(state, 0xc2, 0x28);
+	lgs8gl5_soft_reset(state);
+	lgs8gl5_update_reg(state, REG_07, 0x10);
+	lgs8gl5_update_reg(state, REG_07, 0x10);
+	lgs8gl5_write_reg(state, REG_09, 0x0e);
+	lgs8gl5_write_reg(state, REG_0A, 0xe5);
+	lgs8gl5_write_reg(state, REG_0B, 0x35);
+	lgs8gl5_write_reg(state, REG_0C, 0x30);
+
+	return 0;
+}
+
+
+static int
+lgs8gl5_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+	struct lgs8gl5_state *state = fe->demodulator_priv;
+	u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
+	u8 flags = lgs8gl5_read_reg(state, REG_STATUS);
+
+	*status = 0;
+
+	if ((level & REG_STRENGTH_MASK) > 0)
+		*status |= FE_HAS_SIGNAL;
+	if (level & REG_STRENGTH_CARRIER)
+		*status |= FE_HAS_CARRIER;
+	if (flags & REG_STATUS_SYNC)
+		*status |= FE_HAS_SYNC;
+	if (flags & REG_STATUS_LOCK)
+		*status |= FE_HAS_LOCK;
+
+	return 0;
+}
+
+
+static int
+lgs8gl5_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+	*ber = 0;
+
+	return 0;
+}
+
+
+static int
+lgs8gl5_read_signal_strength(struct dvb_frontend *fe, u16 *signal_strength)
+{
+	struct lgs8gl5_state *state = fe->demodulator_priv;
+	u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
+	*signal_strength = (level & REG_STRENGTH_MASK) << 8;
+
+	return 0;
+}
+
+
+static int
+lgs8gl5_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+	struct lgs8gl5_state *state = fe->demodulator_priv;
+	u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
+	*snr = (level & REG_STRENGTH_MASK) << 8;
+
+	return 0;
+}
+
+
+static int
+lgs8gl5_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+	*ucblocks = 0;
+
+	return 0;
+}
+
+
+static int
+lgs8gl5_set_frontend(struct dvb_frontend *fe,
+		struct dvb_frontend_parameters *p)
+{
+	struct lgs8gl5_state *state = fe->demodulator_priv;
+
+	dprintk("%s\n", __func__);
+
+	if (p->u.ofdm.bandwidth != BANDWIDTH_8_MHZ)
+		return -EINVAL;
+
+	if (fe->ops.tuner_ops.set_params) {
+		fe->ops.tuner_ops.set_params(fe, p);
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 0);
+	}
+
+	/* lgs8gl5_set_inversion(state, p->inversion); */
+
+	lgs8gl5_start_demod(state);
+
+	return 0;
+}
+
+
+static int
+lgs8gl5_get_frontend(struct dvb_frontend *fe,
+		struct dvb_frontend_parameters *p)
+{
+	struct lgs8gl5_state *state = fe->demodulator_priv;
+	u8 inv = lgs8gl5_read_reg(state, REG_INVERSION);
+	struct dvb_ofdm_parameters *o = &p->u.ofdm;
+
+	p->inversion = (inv & REG_INVERSION_ON) ? INVERSION_ON : INVERSION_OFF;
+
+	o->code_rate_HP = FEC_1_2;
+	o->code_rate_LP = FEC_7_8;
+	o->guard_interval = GUARD_INTERVAL_1_32;
+	o->transmission_mode = TRANSMISSION_MODE_2K;
+	o->constellation = QAM_64;
+	o->hierarchy_information = HIERARCHY_NONE;
+	o->bandwidth = BANDWIDTH_8_MHZ;
+
+	return 0;
+}
+
+
+static int
+lgs8gl5_get_tune_settings(struct dvb_frontend *fe,
+		struct dvb_frontend_tune_settings *fesettings)
+{
+	fesettings->min_delay_ms = 240;
+	fesettings->step_size    = 0;
+	fesettings->max_drift    = 0;
+	return 0;
+}
+
+
+static void
+lgs8gl5_release(struct dvb_frontend *fe)
+{
+	struct lgs8gl5_state *state = fe->demodulator_priv;
+	kfree(state);
+}
+
+
+static struct dvb_frontend_ops lgs8gl5_ops;
+
+
+struct dvb_frontend*
+lgs8gl5_attach(const struct lgs8gl5_config *config, struct i2c_adapter *i2c)
+{
+	struct lgs8gl5_state *state = NULL;
+
+	dprintk("%s\n", __func__);
+
+	/* Allocate memory for the internal state */
+	state = kmalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL);
+	if (state == NULL)
+		goto error;
+
+	/* Setup the state */
+	state->config = config;
+	state->i2c    = i2c;
+
+	/* Check if the demod is there */
+	if (lgs8gl5_read_reg(state, REG_RESET) < 0)
+		goto error;
+
+	/* Create dvb_frontend */
+	memcpy(&state->frontend.ops, &lgs8gl5_ops,
+		sizeof(struct dvb_frontend_ops));
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+EXPORT_SYMBOL(lgs8gl5_attach);
+
+
+static struct dvb_frontend_ops lgs8gl5_ops = {
+	.info = {
+		.name			= "Legend Silicon LGS-8GL5 DMB-TH",
+		.type			= FE_OFDM,
+		.frequency_min		= 474000000,
+		.frequency_max		= 858000000,
+		.frequency_stepsize	= 10000,
+		.frequency_tolerance	= 0,
+		.caps = FE_CAN_FEC_AUTO |
+			FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_32 |
+			FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+			FE_CAN_TRANSMISSION_MODE_AUTO |
+			FE_CAN_BANDWIDTH_AUTO |
+			FE_CAN_GUARD_INTERVAL_AUTO |
+			FE_CAN_HIERARCHY_AUTO |
+			FE_CAN_RECOVER
+	},
+
+	.release = lgs8gl5_release,
+
+	.init = lgs8gl5_init,
+
+	.set_frontend = lgs8gl5_set_frontend,
+	.get_frontend = lgs8gl5_get_frontend,
+	.get_tune_settings = lgs8gl5_get_tune_settings,
+
+	.read_status = lgs8gl5_read_status,
+	.read_ber = lgs8gl5_read_ber,
+	.read_signal_strength = lgs8gl5_read_signal_strength,
+	.read_snr = lgs8gl5_read_snr,
+	.read_ucblocks = lgs8gl5_read_ucblocks,
+};
+
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Legend Silicon LGS-8GL5 DMB-TH Demodulator driver");
+MODULE_AUTHOR("Timothy Lee");
+MODULE_LICENSE("GPL");

+ 45 - 0
drivers/media/dvb/frontends/lgs8gl5.h

@@ -0,0 +1,45 @@
+/*
+    Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver
+
+    Copyright (C) 2008 Sirius International (Hong Kong) Limited
+	Timothy Lee <timothy.lee@siriushk.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef LGS8GL5_H
+#define LGS8GL5_H
+
+#include <linux/dvb/frontend.h>
+
+struct lgs8gl5_config {
+	/* the demodulator's i2c address */
+	u8 demod_address;
+};
+
+#if defined(CONFIG_DVB_LGS8GL5) || \
+	(defined(CONFIG_DVB_LGS8GL5_MODULE) && defined(MODULE))
+extern struct dvb_frontend *lgs8gl5_attach(
+	const struct lgs8gl5_config *config, struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *lgs8gl5_attach(
+	const struct lgs8gl5_config *config, struct i2c_adapter *i2c) {
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif /* CONFIG_DVB_LGS8GL5 */
+
+#endif /* LGS8GL5_H */

+ 2 - 2
drivers/media/dvb/frontends/nxt200x.c

@@ -80,7 +80,7 @@ static int i2c_writebytes (struct nxt200x_state* state, u8 addr, u8 *buf, u8 len
 	return 0;
 }
 
-static u8 i2c_readbytes (struct nxt200x_state* state, u8 addr, u8* buf, u8 len)
+static int i2c_readbytes(struct nxt200x_state *state, u8 addr, u8 *buf, u8 len)
 {
 	int err;
 	struct i2c_msg msg = { .addr = addr, .flags = I2C_M_RD, .buf = buf, .len = len };
@@ -111,7 +111,7 @@ static int nxt200x_writebytes (struct nxt200x_state* state, u8 reg,
 	return 0;
 }
 
-static u8 nxt200x_readbytes (struct nxt200x_state* state, u8 reg, u8* buf, u8 len)
+static int nxt200x_readbytes(struct nxt200x_state *state, u8 reg, u8 *buf, u8 len)
 {
 	u8 reg2 [] = { reg };
 

+ 1 - 1
drivers/media/dvb/frontends/or51211.c

@@ -88,7 +88,7 @@ static int i2c_writebytes (struct or51211_state* state, u8 reg, const u8 *buf,
 	return 0;
 }
 
-static u8 i2c_readbytes (struct or51211_state* state, u8 reg, u8* buf, int len)
+static int i2c_readbytes(struct or51211_state *state, u8 reg, u8 *buf, int len)
 {
 	int err;
 	struct i2c_msg msg;

+ 974 - 0
drivers/media/dvb/frontends/si21xx.c

@@ -0,0 +1,974 @@
+/* DVB compliant Linux driver for the DVB-S si2109/2110 demodulator
+*
+* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by)
+*
+*	This program is free software; you can redistribute it and/or modify
+*	it under the terms of the GNU General Public License as published by
+*	the Free Software Foundation; either version 2 of the License, or
+*	(at your option) any later version.
+*
+*/
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "si21xx.h"
+
+#define	REVISION_REG			0x00
+#define	SYSTEM_MODE_REG			0x01
+#define	TS_CTRL_REG_1			0x02
+#define	TS_CTRL_REG_2			0x03
+#define	PIN_CTRL_REG_1			0x04
+#define	PIN_CTRL_REG_2			0x05
+#define	LOCK_STATUS_REG_1		0x0f
+#define	LOCK_STATUS_REG_2		0x10
+#define	ACQ_STATUS_REG			0x11
+#define	ACQ_CTRL_REG_1			0x13
+#define	ACQ_CTRL_REG_2			0x14
+#define	PLL_DIVISOR_REG			0x15
+#define	COARSE_TUNE_REG			0x16
+#define	FINE_TUNE_REG_L			0x17
+#define	FINE_TUNE_REG_H			0x18
+
+#define	ANALOG_AGC_POWER_LEVEL_REG	0x28
+#define	CFO_ESTIMATOR_CTRL_REG_1	0x29
+#define	CFO_ESTIMATOR_CTRL_REG_2	0x2a
+#define	CFO_ESTIMATOR_CTRL_REG_3	0x2b
+
+#define	SYM_RATE_ESTIMATE_REG_L		0x31
+#define	SYM_RATE_ESTIMATE_REG_M		0x32
+#define	SYM_RATE_ESTIMATE_REG_H		0x33
+
+#define	CFO_ESTIMATOR_OFFSET_REG_L	0x36
+#define	CFO_ESTIMATOR_OFFSET_REG_H	0x37
+#define	CFO_ERROR_REG_L			0x38
+#define	CFO_ERROR_REG_H			0x39
+#define	SYM_RATE_ESTIMATOR_CTRL_REG	0x3a
+
+#define	SYM_RATE_REG_L			0x3f
+#define	SYM_RATE_REG_M			0x40
+#define	SYM_RATE_REG_H			0x41
+#define	SYM_RATE_ESTIMATOR_MAXIMUM_REG	0x42
+#define	SYM_RATE_ESTIMATOR_MINIMUM_REG	0x43
+
+#define	C_N_ESTIMATOR_CTRL_REG		0x7c
+#define	C_N_ESTIMATOR_THRSHLD_REG	0x7d
+#define	C_N_ESTIMATOR_LEVEL_REG_L	0x7e
+#define	C_N_ESTIMATOR_LEVEL_REG_H	0x7f
+
+#define	BLIND_SCAN_CTRL_REG		0x80
+
+#define	LSA_CTRL_REG_1			0x8D
+#define	SPCTRM_TILT_CORR_THRSHLD_REG	0x8f
+#define	ONE_DB_BNDWDTH_THRSHLD_REG	0x90
+#define	TWO_DB_BNDWDTH_THRSHLD_REG	0x91
+#define	THREE_DB_BNDWDTH_THRSHLD_REG	0x92
+#define	INBAND_POWER_THRSHLD_REG	0x93
+#define	REF_NOISE_LVL_MRGN_THRSHLD_REG	0x94
+
+#define	VIT_SRCH_CTRL_REG_1		0xa0
+#define	VIT_SRCH_CTRL_REG_2		0xa1
+#define	VIT_SRCH_CTRL_REG_3		0xa2
+#define	VIT_SRCH_STATUS_REG		0xa3
+#define	VITERBI_BER_COUNT_REG_L		0xab
+#define	REED_SOLOMON_CTRL_REG		0xb0
+#define	REED_SOLOMON_ERROR_COUNT_REG_L	0xb1
+#define	PRBS_CTRL_REG			0xb5
+
+#define	LNB_CTRL_REG_1			0xc0
+#define	LNB_CTRL_REG_2			0xc1
+#define	LNB_CTRL_REG_3			0xc2
+#define	LNB_CTRL_REG_4			0xc3
+#define	LNB_CTRL_STATUS_REG		0xc4
+#define	LNB_FIFO_REGS_0			0xc5
+#define	LNB_FIFO_REGS_1			0xc6
+#define	LNB_FIFO_REGS_2			0xc7
+#define	LNB_FIFO_REGS_3			0xc8
+#define	LNB_FIFO_REGS_4			0xc9
+#define	LNB_FIFO_REGS_5			0xca
+#define	LNB_SUPPLY_CTRL_REG_1		0xcb
+#define	LNB_SUPPLY_CTRL_REG_2		0xcc
+#define	LNB_SUPPLY_CTRL_REG_3		0xcd
+#define	LNB_SUPPLY_CTRL_REG_4		0xce
+#define	LNB_SUPPLY_STATUS_REG		0xcf
+
+#define FALSE	0
+#define TRUE	1
+#define FAIL	-1
+#define PASS	0
+
+#define ALLOWABLE_FS_COUNT	10
+#define STATUS_BER		0
+#define STATUS_UCBLOCKS		1
+
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) \
+			printk(KERN_DEBUG "si21xx: " args); \
+	} while (0)
+
+enum {
+	ACTIVE_HIGH,
+	ACTIVE_LOW
+};
+enum {
+	BYTE_WIDE,
+	BIT_WIDE
+};
+enum {
+	CLK_GAPPED_MODE,
+	CLK_CONTINUOUS_MODE
+};
+enum {
+	RISING_EDGE,
+	FALLING_EDGE
+};
+enum {
+	MSB_FIRST,
+	LSB_FIRST
+};
+enum {
+	SERIAL,
+	PARALLEL
+};
+
+struct si21xx_state {
+	struct i2c_adapter *i2c;
+	const struct si21xx_config *config;
+	struct dvb_frontend frontend;
+	u8 initialised:1;
+	int errmode;
+	int fs;			/*Sampling rate of the ADC in MHz*/
+};
+
+/*	register default initialization */
+static u8 serit_sp1511lhb_inittab[] = {
+	0x01, 0x28,	/* set i2c_inc_disable */
+	0x20, 0x03,
+	0x27, 0x20,
+	0xe0, 0x45,
+	0xe1, 0x08,
+	0xfe, 0x01,
+	0x01, 0x28,
+	0x89, 0x09,
+	0x04, 0x80,
+	0x05, 0x01,
+	0x06, 0x00,
+	0x20, 0x03,
+	0x24, 0x88,
+	0x29, 0x09,
+	0x2a, 0x0f,
+	0x2c, 0x10,
+	0x2d, 0x19,
+	0x2e, 0x08,
+	0x2f, 0x10,
+	0x30, 0x19,
+	0x34, 0x20,
+	0x35, 0x03,
+	0x45, 0x02,
+	0x46, 0x45,
+	0x47, 0xd0,
+	0x48, 0x00,
+	0x49, 0x40,
+	0x4a, 0x03,
+	0x4c, 0xfd,
+	0x4f, 0x2e,
+	0x50, 0x2e,
+	0x51, 0x10,
+	0x52, 0x10,
+	0x56, 0x92,
+	0x59, 0x00,
+	0x5a, 0x2d,
+	0x5b, 0x33,
+	0x5c, 0x1f,
+	0x5f, 0x76,
+	0x62, 0xc0,
+	0x63, 0xc0,
+	0x64, 0xf3,
+	0x65, 0xf3,
+	0x79, 0x40,
+	0x6a, 0x40,
+	0x6b, 0x0a,
+	0x6c, 0x80,
+	0x6d, 0x27,
+	0x71, 0x06,
+	0x75, 0x60,
+	0x78, 0x00,
+	0x79, 0xb5,
+	0x7c, 0x05,
+	0x7d, 0x1a,
+	0x87, 0x55,
+	0x88, 0x72,
+	0x8f, 0x08,
+	0x90, 0xe0,
+	0x94, 0x40,
+	0xa0, 0x3f,
+	0xa1, 0xc0,
+	0xa4, 0xcc,
+	0xa5, 0x66,
+	0xa6, 0x66,
+	0xa7, 0x7b,
+	0xa8, 0x7b,
+	0xa9, 0x7b,
+	0xaa, 0x9a,
+	0xed, 0x04,
+	0xad, 0x00,
+	0xae, 0x03,
+	0xcc, 0xab,
+	0x01, 0x08,
+	0xff, 0xff
+};
+
+/*	low level read/writes */
+static int si21_writeregs(struct si21xx_state *state, u8 reg1,
+							u8 *data, int len)
+{
+	int ret;
+	u8 buf[60];/* = { reg1, data };*/
+	struct i2c_msg msg = {
+				.addr = state->config->demod_address,
+				.flags = 0,
+				.buf = buf,
+				.len = len + 1
+	};
+
+	msg.buf[0] =  reg1;
+	memcpy(msg.buf + 1, data, len);
+
+	ret = i2c_transfer(state->i2c, &msg, 1);
+
+	if (ret != 1)
+		dprintk("%s: writereg error (reg1 == 0x%02x, data == 0x%02x, "
+			"ret == %i)\n", __func__, reg1, data[0], ret);
+
+	return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+static int si21_writereg(struct si21xx_state *state, u8 reg, u8 data)
+{
+	int ret;
+	u8 buf[] = { reg, data };
+	struct i2c_msg msg = {
+				.addr = state->config->demod_address,
+				.flags = 0,
+				.buf = buf,
+				.len = 2
+	};
+
+	ret = i2c_transfer(state->i2c, &msg, 1);
+
+	if (ret != 1)
+		dprintk("%s: writereg error (reg == 0x%02x, data == 0x%02x, "
+			"ret == %i)\n", __func__, reg, data, ret);
+
+	return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+static int si21_write(struct dvb_frontend *fe, u8 *buf, int len)
+{
+	struct si21xx_state *state = fe->demodulator_priv;
+
+	if (len != 2)
+		return -EINVAL;
+
+	return si21_writereg(state, buf[0], buf[1]);
+}
+
+static u8 si21_readreg(struct si21xx_state *state, u8 reg)
+{
+	int ret;
+	u8 b0[] = { reg };
+	u8 b1[] = { 0 };
+	struct i2c_msg msg[] = {
+		{
+			.addr = state->config->demod_address,
+			.flags = 0,
+			.buf = b0,
+			.len = 1
+		}, {
+			.addr = state->config->demod_address,
+			.flags = I2C_M_RD,
+			.buf = b1,
+			.len = 1
+		}
+	};
+
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2)
+		dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n",
+			__func__, reg, ret);
+
+	return b1[0];
+}
+
+static int si21_readregs(struct si21xx_state *state, u8 reg1, u8 *b, u8 len)
+{
+	int ret;
+	struct i2c_msg msg[] = {
+		{
+			.addr = state->config->demod_address,
+			.flags = 0,
+			.buf = &reg1,
+			.len = 1
+		}, {
+			.addr = state->config->demod_address,
+			.flags = I2C_M_RD,
+			.buf = b,
+			.len = len
+		}
+	};
+
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2)
+		dprintk("%s: readreg error (ret == %i)\n", __func__, ret);
+
+	return ret == 2 ? 0 : -1;
+}
+
+static int si21xx_wait_diseqc_idle(struct si21xx_state *state, int timeout)
+{
+	unsigned long start = jiffies;
+
+	dprintk("%s\n", __func__);
+
+	while ((si21_readreg(state, LNB_CTRL_REG_1) & 0x8) == 8) {
+		if (jiffies - start > timeout) {
+			dprintk("%s: timeout!!\n", __func__);
+			return -ETIMEDOUT;
+		}
+		msleep(10);
+	};
+
+	return 0;
+}
+
+static int si21xx_set_symbolrate(struct dvb_frontend *fe, u32 srate)
+{
+	struct si21xx_state *state = fe->demodulator_priv;
+	u32 sym_rate, data_rate;
+	int i;
+	u8 sym_rate_bytes[3];
+
+	dprintk("%s : srate = %i\n", __func__ , srate);
+
+	if ((srate < 1000000) || (srate > 45000000))
+		return -EINVAL;
+
+	data_rate = srate;
+	sym_rate = 0;
+
+	for (i = 0; i < 4; ++i) {
+		sym_rate /= 100;
+		sym_rate = sym_rate + ((data_rate % 100) * 0x800000) /
+								state->fs;
+		data_rate /= 100;
+	}
+	for (i = 0; i < 3; ++i)
+		sym_rate_bytes[i] = (u8)((sym_rate >> (i * 8)) & 0xff);
+
+	si21_writeregs(state, SYM_RATE_REG_L, sym_rate_bytes, 0x03);
+
+	return 0;
+}
+
+static int si21xx_send_diseqc_msg(struct dvb_frontend *fe,
+					struct dvb_diseqc_master_cmd *m)
+{
+	struct si21xx_state *state = fe->demodulator_priv;
+	u8 lnb_status;
+	u8 LNB_CTRL_1;
+	int status;
+
+	dprintk("%s\n", __func__);
+
+	status = PASS;
+	LNB_CTRL_1 = 0;
+
+	status |= si21_readregs(state, LNB_CTRL_STATUS_REG, &lnb_status, 0x01);
+	status |= si21_readregs(state, LNB_CTRL_REG_1, &lnb_status, 0x01);
+
+	/*fill the FIFO*/
+	status |= si21_writeregs(state, LNB_FIFO_REGS_0, m->msg, m->msg_len);
+
+	LNB_CTRL_1 = (lnb_status & 0x70);
+	LNB_CTRL_1 |= m->msg_len;
+
+	LNB_CTRL_1 |= 0x80;	/* begin LNB signaling */
+
+	status |= si21_writeregs(state, LNB_CTRL_REG_1, &LNB_CTRL_1, 0x01);
+
+	return status;
+}
+
+static int si21xx_send_diseqc_burst(struct dvb_frontend *fe,
+						fe_sec_mini_cmd_t burst)
+{
+	struct si21xx_state *state = fe->demodulator_priv;
+	u8 val;
+
+	dprintk("%s\n", __func__);
+
+	if (si21xx_wait_diseqc_idle(state, 100) < 0)
+		return -ETIMEDOUT;
+
+	val = (0x80 | si21_readreg(state, 0xc1));
+	if (si21_writereg(state, LNB_CTRL_REG_1,
+			burst == SEC_MINI_A ? (val & ~0x10) : (val | 0x10)))
+		return -EREMOTEIO;
+
+	if (si21xx_wait_diseqc_idle(state, 100) < 0)
+		return -ETIMEDOUT;
+
+	if (si21_writereg(state, LNB_CTRL_REG_1, val))
+		return -EREMOTEIO;
+
+	return 0;
+}
+/*	30.06.2008 */
+static int si21xx_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+	struct si21xx_state *state = fe->demodulator_priv;
+	u8 val;
+
+	dprintk("%s\n", __func__);
+	val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1));
+
+	switch (tone) {
+	case SEC_TONE_ON:
+		return si21_writereg(state, LNB_CTRL_REG_1, val | 0x20);
+
+	case SEC_TONE_OFF:
+		return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x20));
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int si21xx_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt)
+{
+	struct si21xx_state *state = fe->demodulator_priv;
+
+	u8 val;
+	dprintk("%s: %s\n", __func__,
+		volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
+		volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??");
+
+
+	val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1));
+
+	switch (volt) {
+	case SEC_VOLTAGE_18:
+		return si21_writereg(state, LNB_CTRL_REG_1, val | 0x40);
+		break;
+	case SEC_VOLTAGE_13:
+		return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x40));
+		break;
+	default:
+		return -EINVAL;
+	};
+}
+
+static int si21xx_init(struct dvb_frontend *fe)
+{
+	struct si21xx_state *state = fe->demodulator_priv;
+	int i;
+	int status = 0;
+	u8 reg1;
+	u8 val;
+	u8 reg2[2];
+
+	dprintk("%s\n", __func__);
+
+	for (i = 0; ; i += 2) {
+		reg1 = serit_sp1511lhb_inittab[i];
+		val = serit_sp1511lhb_inittab[i+1];
+		if (reg1 == 0xff && val == 0xff)
+			break;
+		si21_writeregs(state, reg1, &val, 1);
+	}
+
+	/*DVB QPSK SYSTEM MODE REG*/
+	reg1 = 0x08;
+	si21_writeregs(state, SYSTEM_MODE_REG, &reg1, 0x01);
+
+	/*transport stream config*/
+	/*
+	mode = PARALLEL;
+	sdata_form = LSB_FIRST;
+	clk_edge = FALLING_EDGE;
+	clk_mode = CLK_GAPPED_MODE;
+	strt_len = BYTE_WIDE;
+	sync_pol = ACTIVE_HIGH;
+	val_pol = ACTIVE_HIGH;
+	err_pol = ACTIVE_HIGH;
+	sclk_rate = 0x00;
+	parity = 0x00 ;
+	data_delay = 0x00;
+	clk_delay = 0x00;
+	pclk_smooth = 0x00;
+	*/
+	reg2[0] =
+		PARALLEL + (LSB_FIRST << 1)
+		+ (FALLING_EDGE << 2) + (CLK_GAPPED_MODE << 3)
+		+ (BYTE_WIDE << 4) + (ACTIVE_HIGH << 5)
+		+ (ACTIVE_HIGH << 6) + (ACTIVE_HIGH << 7);
+
+	reg2[1] = 0;
+	/*	sclk_rate + (parity << 2)
+		+ (data_delay << 3) + (clk_delay << 4)
+		+ (pclk_smooth << 5);
+	*/
+	status |= si21_writeregs(state, TS_CTRL_REG_1, reg2, 0x02);
+	if (status != 0)
+		dprintk(" %s : TS Set Error\n", __func__);
+
+	return 0;
+
+}
+
+static int si21_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+	struct si21xx_state *state = fe->demodulator_priv;
+	u8 regs_read[2];
+	u8 reg_read;
+	u8 i;
+	u8 lock;
+	u8 signal = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG);
+
+	si21_readregs(state, LOCK_STATUS_REG_1, regs_read, 0x02);
+	reg_read = 0;
+
+	for (i = 0; i < 7; ++i)
+		reg_read |= ((regs_read[0] >> i) & 0x01) << (6 - i);
+
+	lock = ((reg_read & 0x7f) | (regs_read[1] & 0x80));
+
+	dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, lock);
+	*status = 0;
+
+	if (signal > 10)
+		*status |= FE_HAS_SIGNAL;
+
+	if (lock & 0x2)
+		*status |= FE_HAS_CARRIER;
+
+	if (lock & 0x20)
+		*status |= FE_HAS_VITERBI;
+
+	if (lock & 0x40)
+		*status |= FE_HAS_SYNC;
+
+	if ((lock & 0x7b) == 0x7b)
+		*status |= FE_HAS_LOCK;
+
+	return 0;
+}
+
+static int si21_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+	struct si21xx_state *state = fe->demodulator_priv;
+
+	/*status = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG,
+						(u8*)agclevel, 0x01);*/
+
+	u16 signal = (3 * si21_readreg(state, 0x27) *
+					si21_readreg(state, 0x28));
+
+	dprintk("%s : AGCPWR: 0x%02x%02x, signal=0x%04x\n", __func__,
+		si21_readreg(state, 0x27),
+		si21_readreg(state, 0x28), (int) signal);
+
+	signal  <<= 4;
+	*strength = signal;
+
+	return 0;
+}
+
+static int si21_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+	struct si21xx_state *state = fe->demodulator_priv;
+
+	dprintk("%s\n", __func__);
+
+	if (state->errmode != STATUS_BER)
+		return 0;
+
+	*ber = (si21_readreg(state, 0x1d) << 8) |
+				si21_readreg(state, 0x1e);
+
+	return 0;
+}
+
+static int si21_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+	struct si21xx_state *state = fe->demodulator_priv;
+
+	s32 xsnr = 0xffff - ((si21_readreg(state, 0x24) << 8) |
+					si21_readreg(state, 0x25));
+	xsnr = 3 * (xsnr - 0xa100);
+	*snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr;
+
+	dprintk("%s\n", __func__);
+
+	return 0;
+}
+
+static int si21_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+	struct si21xx_state *state = fe->demodulator_priv;
+
+	dprintk("%s\n", __func__);
+
+	if (state->errmode != STATUS_UCBLOCKS)
+		*ucblocks = 0;
+	else
+		*ucblocks = (si21_readreg(state, 0x1d) << 8) |
+					si21_readreg(state, 0x1e);
+
+	return 0;
+}
+
+/*	initiates a channel acquisition sequence
+	using the specified symbol rate and code rate */
+static int si21xx_setacquire(struct dvb_frontend *fe, int symbrate,
+						fe_code_rate_t crate)
+{
+
+	struct si21xx_state *state = fe->demodulator_priv;
+	u8 coderates[] = {
+				0x0, 0x01, 0x02, 0x04, 0x00,
+				0x8, 0x10, 0x20, 0x00, 0x3f
+	};
+
+	u8 coderate_ptr;
+	int status;
+	u8 start_acq = 0x80;
+	u8 reg, regs[3];
+
+	dprintk("%s\n", __func__);
+
+	status = PASS;
+	coderate_ptr = coderates[crate];
+
+	si21xx_set_symbolrate(fe, symbrate);
+
+	/* write code rates to use in the Viterbi search */
+	status |= si21_writeregs(state,
+				VIT_SRCH_CTRL_REG_1,
+				&coderate_ptr, 0x01);
+
+	/* clear acq_start bit */
+	status |= si21_readregs(state, ACQ_CTRL_REG_2, &reg, 0x01);
+	reg &= ~start_acq;
+	status |= si21_writeregs(state, ACQ_CTRL_REG_2, &reg, 0x01);
+
+	/* use new Carrier Frequency Offset Estimator (QuickLock) */
+	regs[0] = 0xCB;
+	regs[1] = 0x40;
+	regs[2] = 0xCB;
+
+	status |= si21_writeregs(state,
+				TWO_DB_BNDWDTH_THRSHLD_REG,
+				&regs[0], 0x03);
+	reg = 0x56;
+	status |= si21_writeregs(state,
+				LSA_CTRL_REG_1, &reg, 1);
+	reg = 0x05;
+	status |= si21_writeregs(state,
+				BLIND_SCAN_CTRL_REG, &reg, 1);
+	/* start automatic acq */
+	status |= si21_writeregs(state,
+				ACQ_CTRL_REG_2, &start_acq, 0x01);
+
+	return status;
+}
+
+static int si21xx_set_property(struct dvb_frontend *fe, struct dtv_property *p)
+{
+	dprintk("%s(..)\n", __func__);
+	return 0;
+}
+
+static int si21xx_get_property(struct dvb_frontend *fe, struct dtv_property *p)
+{
+	dprintk("%s(..)\n", __func__);
+	return 0;
+}
+
+static int si21xx_set_frontend(struct dvb_frontend *fe,
+					struct dvb_frontend_parameters *dfp)
+{
+	struct si21xx_state *state = fe->demodulator_priv;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+	/* freq		Channel carrier frequency in KHz (i.e. 1550000 KHz)
+	 datarate	Channel symbol rate in Sps (i.e. 22500000 Sps)*/
+
+	/* in MHz */
+	unsigned char coarse_tune_freq;
+	int fine_tune_freq;
+	unsigned char sample_rate = 0;
+	/* boolean */
+	unsigned int inband_interferer_ind;
+
+	/* INTERMEDIATE VALUES */
+	int icoarse_tune_freq; /* MHz */
+	int ifine_tune_freq; /* MHz */
+	unsigned int band_high;
+	unsigned int band_low;
+	unsigned int x1;
+	unsigned int x2;
+	int i;
+	unsigned int inband_interferer_div2[ALLOWABLE_FS_COUNT] = {
+			FALSE, FALSE, FALSE, FALSE, FALSE,
+			FALSE, FALSE, FALSE, FALSE, FALSE
+	};
+	unsigned int inband_interferer_div4[ALLOWABLE_FS_COUNT] = {
+			FALSE, FALSE, FALSE, FALSE, FALSE,
+			FALSE, FALSE, FALSE, FALSE, FALSE
+	};
+
+	int status;
+
+	/* allowable sample rates for ADC in MHz */
+	int afs[ALLOWABLE_FS_COUNT] = { 200, 192, 193, 194, 195,
+					196, 204, 205, 206, 207
+	};
+	/* in MHz */
+	int if_limit_high;
+	int if_limit_low;
+	int lnb_lo;
+	int lnb_uncertanity;
+
+	int rf_freq;
+	int data_rate;
+	unsigned char regs[4];
+
+	dprintk("%s : FE_SET_FRONTEND\n", __func__);
+
+	if (c->delivery_system != SYS_DVBS) {
+			dprintk("%s: unsupported delivery system selected (%d)\n",
+				__func__, c->delivery_system);
+			return -EOPNOTSUPP;
+	}
+
+	for (i = 0; i < ALLOWABLE_FS_COUNT; ++i)
+		inband_interferer_div2[i] = inband_interferer_div4[i] = FALSE;
+
+	if_limit_high = -700000;
+	if_limit_low = -100000;
+	/* in MHz */
+	lnb_lo = 0;
+	lnb_uncertanity = 0;
+
+	rf_freq = 10 * c->frequency ;
+	data_rate = c->symbol_rate / 100;
+
+	status = PASS;
+
+	band_low = (rf_freq - lnb_lo) - ((lnb_uncertanity * 200)
+					+ (data_rate * 135)) / 200;
+
+	band_high = (rf_freq - lnb_lo) + ((lnb_uncertanity * 200)
+					+ (data_rate * 135)) / 200;
+
+
+	icoarse_tune_freq = 100000 *
+				(((rf_freq - lnb_lo) -
+					(if_limit_low + if_limit_high) / 2)
+								/ 100000);
+
+	ifine_tune_freq = (rf_freq - lnb_lo) - icoarse_tune_freq ;
+
+	for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
+		x1 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) *
+					(afs[i] * 2500) + afs[i] * 2500;
+
+		x2 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) *
+							(afs[i] * 2500);
+
+		if (((band_low < x1) && (x1 < band_high)) ||
+					((band_low < x2) && (x2 < band_high)))
+					inband_interferer_div4[i] = TRUE;
+
+	}
+
+	for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
+		x1 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) *
+					(afs[i] * 5000) + afs[i] * 5000;
+
+		x2 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) *
+					(afs[i] * 5000);
+
+		if (((band_low < x1) && (x1 < band_high)) ||
+					((band_low < x2) && (x2 < band_high)))
+					inband_interferer_div2[i] = TRUE;
+	}
+
+	inband_interferer_ind = TRUE;
+	for (i = 0; i < ALLOWABLE_FS_COUNT; ++i)
+		inband_interferer_ind &= inband_interferer_div2[i] |
+						inband_interferer_div4[i];
+
+	if (inband_interferer_ind) {
+		for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
+			if (inband_interferer_div2[i] == FALSE) {
+				sample_rate = (u8) afs[i];
+				break;
+			}
+		}
+	} else {
+		for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) {
+			if ((inband_interferer_div2[i] |
+					inband_interferer_div4[i]) == FALSE) {
+				sample_rate = (u8) afs[i];
+				break;
+			}
+		}
+
+	}
+
+	if (sample_rate > 207 || sample_rate < 192)
+		sample_rate = 200;
+
+	fine_tune_freq = ((0x4000 * (ifine_tune_freq / 10)) /
+					((sample_rate) * 1000));
+
+	coarse_tune_freq = (u8)(icoarse_tune_freq / 100000);
+
+	regs[0] = sample_rate;
+	regs[1] = coarse_tune_freq;
+	regs[2] = fine_tune_freq & 0xFF;
+	regs[3] = fine_tune_freq >> 8 & 0xFF;
+
+	status |= si21_writeregs(state, PLL_DIVISOR_REG, &regs[0], 0x04);
+
+	state->fs = sample_rate;/*ADC MHz*/
+	si21xx_setacquire(fe, c->symbol_rate, c->fec_inner);
+
+	return 0;
+}
+
+static int si21xx_sleep(struct dvb_frontend *fe)
+{
+	struct si21xx_state *state = fe->demodulator_priv;
+	u8 regdata;
+
+	dprintk("%s\n", __func__);
+
+	si21_readregs(state, SYSTEM_MODE_REG, &regdata, 0x01);
+	regdata |= 1 << 6;
+	si21_writeregs(state, SYSTEM_MODE_REG, &regdata, 0x01);
+	state->initialised = 0;
+
+	return 0;
+}
+
+static void si21xx_release(struct dvb_frontend *fe)
+{
+	struct si21xx_state *state = fe->demodulator_priv;
+
+	dprintk("%s\n", __func__);
+
+	kfree(state);
+}
+
+static struct dvb_frontend_ops si21xx_ops = {
+
+	.info = {
+		.name			= "SL SI21XX DVB-S",
+		.type			= FE_QPSK,
+		.frequency_min		= 950000,
+		.frequency_max		= 2150000,
+		.frequency_stepsize	= 125,	 /* kHz for QPSK frontends */
+		.frequency_tolerance	= 0,
+		.symbol_rate_min	= 1000000,
+		.symbol_rate_max	= 45000000,
+		.symbol_rate_tolerance	= 500,	/* ppm */
+		.caps = 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_QPSK |
+		FE_CAN_FEC_AUTO
+	},
+
+	.release = si21xx_release,
+	.init = si21xx_init,
+	.sleep = si21xx_sleep,
+	.write = si21_write,
+	.read_status = si21_read_status,
+	.read_ber = si21_read_ber,
+	.read_signal_strength = si21_read_signal_strength,
+	.read_snr = si21_read_snr,
+	.read_ucblocks = si21_read_ucblocks,
+	.diseqc_send_master_cmd = si21xx_send_diseqc_msg,
+	.diseqc_send_burst = si21xx_send_diseqc_burst,
+	.set_tone = si21xx_set_tone,
+	.set_voltage = si21xx_set_voltage,
+
+	.set_property = si21xx_set_property,
+	.get_property = si21xx_get_property,
+	.set_frontend = si21xx_set_frontend,
+};
+
+struct dvb_frontend *si21xx_attach(const struct si21xx_config *config,
+						struct i2c_adapter *i2c)
+{
+	struct si21xx_state *state = NULL;
+	int id;
+
+	dprintk("%s\n", __func__);
+
+	/* allocate memory for the internal state */
+	state = kmalloc(sizeof(struct si21xx_state), GFP_KERNEL);
+	if (state == NULL)
+		goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	state->initialised = 0;
+	state->errmode = STATUS_BER;
+
+	/* check if the demod is there */
+	id = si21_readreg(state, SYSTEM_MODE_REG);
+	si21_writereg(state, SYSTEM_MODE_REG, id | 0x40); /* standby off */
+	msleep(200);
+	id = si21_readreg(state, 0x00);
+
+	/* register 0x00 contains:
+		0x34 for SI2107
+		0x24 for SI2108
+		0x14 for SI2109
+		0x04 for SI2110
+	*/
+	if (id != 0x04 && id != 0x14)
+		goto error;
+
+	/* create dvb_frontend */
+	memcpy(&state->frontend.ops, &si21xx_ops,
+					sizeof(struct dvb_frontend_ops));
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+	return NULL;
+}
+EXPORT_SYMBOL(si21xx_attach);
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("SL SI21XX DVB Demodulator driver");
+MODULE_AUTHOR("Igor M. Liplianin");
+MODULE_LICENSE("GPL");

+ 37 - 0
drivers/media/dvb/frontends/si21xx.h

@@ -0,0 +1,37 @@
+#ifndef SI21XX_H
+#define SI21XX_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+struct si21xx_config {
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	/* minimum delay before retuning */
+	int min_delay_ms;
+};
+
+#if defined(CONFIG_DVB_SI21XX) || \
+		(defined(CONFIG_DVB_SI21XX_MODULE) && defined(MODULE))
+extern struct dvb_frontend *si21xx_attach(const struct si21xx_config *config,
+						struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *si21xx_attach(
+		const struct si21xx_config *config, struct i2c_adapter *i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif
+
+static inline int si21xx_writeregister(struct dvb_frontend *fe, u8 reg, u8 val)
+{
+	int r = 0;
+	u8 buf[] = {reg, val};
+	if (fe->ops.write)
+		r = fe->ops.write(fe, buf, 2);
+	return r;
+}
+
+#endif

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

@@ -337,7 +337,8 @@ static int sp887x_setup_frontend_parameters (struct dvb_frontend* fe,
 					     struct dvb_frontend_parameters *p)
 {
 	struct sp887x_state* state = fe->demodulator_priv;
-	int actual_freq, err;
+	unsigned actual_freq;
+	int err;
 	u16 val, reg0xc05;
 
 	if (p->u.ofdm.bandwidth != BANDWIDTH_8_MHZ &&

+ 255 - 0
drivers/media/dvb/frontends/stb6000.c

@@ -0,0 +1,255 @@
+  /*
+     Driver for ST STB6000 DVBS Silicon tuner
+
+     Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by)
+
+     This program is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published by
+     the Free Software Foundation; either version 2 of the License, or
+     (at your option) any later version.
+
+     This program is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+
+     GNU General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with this program; if not, write to the Free Software
+     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+  */
+
+#include <linux/module.h>
+#include <linux/dvb/frontend.h>
+#include <asm/types.h>
+
+#include "stb6000.h"
+
+static int debug;
+#define dprintk(args...) \
+	do { \
+		if (debug) \
+			printk(KERN_DEBUG "stb6000: " args); \
+	} while (0)
+
+struct stb6000_priv {
+	/* i2c details */
+	int i2c_address;
+	struct i2c_adapter *i2c;
+	u32 frequency;
+};
+
+static int stb6000_release(struct dvb_frontend *fe)
+{
+	kfree(fe->tuner_priv);
+	fe->tuner_priv = NULL;
+	return 0;
+}
+
+static int stb6000_sleep(struct dvb_frontend *fe)
+{
+	struct stb6000_priv *priv = fe->tuner_priv;
+	int ret;
+	u8 buf[] = { 10, 0 };
+	struct i2c_msg msg = {
+		.addr = priv->i2c_address,
+		.flags = 0,
+		.buf = buf,
+		.len = 2
+	};
+
+	dprintk("%s:\n", __func__);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+
+	ret = i2c_transfer(priv->i2c, &msg, 1);
+	if (ret != 1)
+		dprintk("%s: i2c error\n", __func__);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	return (ret == 1) ? 0 : ret;
+}
+
+static int stb6000_set_params(struct dvb_frontend *fe,
+				struct dvb_frontend_parameters *params)
+{
+	struct stb6000_priv *priv = fe->tuner_priv;
+	unsigned int n, m;
+	int ret;
+	u32 freq_mhz;
+	int bandwidth;
+	u8 buf[12];
+	struct i2c_msg msg = {
+		.addr = priv->i2c_address,
+		.flags = 0,
+		.buf = buf,
+		.len = 12
+	};
+
+	dprintk("%s:\n", __func__);
+
+	freq_mhz = params->frequency / 1000;
+	bandwidth = params->u.qpsk.symbol_rate / 1000000;
+
+	if (bandwidth > 31)
+		bandwidth = 31;
+
+	if ((freq_mhz > 949) && (freq_mhz < 2151)) {
+		buf[0] = 0x01;
+		buf[1] = 0xac;
+		if (freq_mhz < 1950)
+			buf[1] = 0xaa;
+		if (freq_mhz < 1800)
+			buf[1] = 0xa8;
+		if (freq_mhz < 1650)
+			buf[1] = 0xa6;
+		if (freq_mhz < 1530)
+			buf[1] = 0xa5;
+		if (freq_mhz < 1470)
+			buf[1] = 0xa4;
+		if (freq_mhz < 1370)
+			buf[1] = 0xa2;
+		if (freq_mhz < 1300)
+			buf[1] = 0xa1;
+		if (freq_mhz < 1200)
+			buf[1] = 0xa0;
+		if (freq_mhz < 1075)
+			buf[1] = 0xbc;
+		if (freq_mhz < 1000)
+			buf[1] = 0xba;
+		if (freq_mhz < 1075) {
+			n = freq_mhz / 8; /* vco=lo*4 */
+			m = 2;
+		} else {
+			n = freq_mhz / 16; /* vco=lo*2 */
+			m = 1;
+		}
+		buf[2] = n >> 1;
+		buf[3] = (unsigned char)(((n & 1) << 7) |
+					(m * freq_mhz - n * 16) | 0x60);
+		buf[4] = 0x04;
+		buf[5] = 0x0e;
+
+		buf[6] = (unsigned char)(bandwidth);
+
+		buf[7] = 0xd8;
+		buf[8] = 0xd0;
+		buf[9] = 0x50;
+		buf[10] = 0xeb;
+		buf[11] = 0x4f;
+
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 1);
+
+		ret = i2c_transfer(priv->i2c, &msg, 1);
+		if (ret != 1)
+			dprintk("%s: i2c error\n", __func__);
+
+		udelay(10);
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 0);
+
+		buf[0] = 0x07;
+		buf[1] = 0xdf;
+		buf[2] = 0xd0;
+		buf[3] = 0x50;
+		buf[4] = 0xfb;
+		msg.len = 5;
+
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 1);
+
+		ret = i2c_transfer(priv->i2c, &msg, 1);
+		if (ret != 1)
+			dprintk("%s: i2c error\n", __func__);
+
+		udelay(10);
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 0);
+
+		priv->frequency = freq_mhz * 1000;
+
+		return (ret == 1) ? 0 : ret;
+	}
+	return -1;
+}
+
+static int stb6000_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+	struct stb6000_priv *priv = fe->tuner_priv;
+	*frequency = priv->frequency;
+	return 0;
+}
+
+static struct dvb_tuner_ops stb6000_tuner_ops = {
+	.info = {
+		.name = "ST STB6000",
+		.frequency_min = 950000,
+		.frequency_max = 2150000
+	},
+	.release = stb6000_release,
+	.sleep = stb6000_sleep,
+	.set_params = stb6000_set_params,
+	.get_frequency = stb6000_get_frequency,
+};
+
+struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, int addr,
+						struct i2c_adapter *i2c)
+{
+	struct stb6000_priv *priv = NULL;
+	u8 b0[] = { 0 };
+	u8 b1[] = { 0, 0 };
+	struct i2c_msg msg[2] = {
+		{
+			.addr = addr,
+			.flags = 0,
+			.buf = b0,
+			.len = 0
+		}, {
+			.addr = addr,
+			.flags = I2C_M_RD,
+			.buf = b1,
+			.len = 2
+		}
+	};
+	int ret;
+
+	dprintk("%s:\n", __func__);
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+
+	/* is some i2c device here ? */
+	ret = i2c_transfer(i2c, msg, 2);
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	if (ret != 2)
+		return NULL;
+
+	priv = kzalloc(sizeof(struct stb6000_priv), GFP_KERNEL);
+	if (priv == NULL)
+		return NULL;
+
+	priv->i2c_address = addr;
+	priv->i2c = i2c;
+
+	memcpy(&fe->ops.tuner_ops, &stb6000_tuner_ops,
+				sizeof(struct dvb_tuner_ops));
+
+	fe->tuner_priv = priv;
+
+	return fe;
+}
+EXPORT_SYMBOL(stb6000_attach);
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("DVB STB6000 driver");
+MODULE_AUTHOR("Igor M. Liplianin <liplianin@me.by>");
+MODULE_LICENSE("GPL");

+ 51 - 0
drivers/media/dvb/frontends/stb6000.h

@@ -0,0 +1,51 @@
+  /*
+     Driver for ST stb6000 DVBS Silicon tuner
+
+     Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by)
+
+     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 __DVB_STB6000_H__
+#define __DVB_STB6000_H__
+
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+
+/**
+ * Attach a stb6000 tuner to the supplied frontend structure.
+ *
+ * @param fe Frontend to attach to.
+ * @param addr i2c address of the tuner.
+ * @param i2c i2c adapter to use.
+ * @return FE pointer on success, NULL on failure.
+ */
+#if defined(CONFIG_DVB_STB6000) || (defined(CONFIG_DVB_STB6000_MODULE) \
+							&& defined(MODULE))
+extern struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, int addr,
+					   struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe,
+						  int addr,
+						  struct i2c_adapter *i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif /* CONFIG_DVB_STB6000 */
+
+#endif /* __DVB_STB6000_H__ */

+ 618 - 0
drivers/media/dvb/frontends/stv0288.c

@@ -0,0 +1,618 @@
+/*
+	Driver for ST STV0288 demodulator
+	Copyright (C) 2006 Georg Acher, BayCom GmbH, acher (at) baycom (dot) de
+		for Reel Multimedia
+	Copyright (C) 2008 TurboSight.com, Bob Liu <bob@turbosight.com>
+	Copyright (C) 2008 Igor M. Liplianin <liplianin@me.by>
+		Removed stb6000 specific tuner code and revised some
+		procedures.
+
+	This program is free software; you can redistribute it and/or modify
+	it under the terms of the GNU General Public License as published by
+	the Free Software Foundation; either version 2 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with this program; if not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "stv0288.h"
+
+struct stv0288_state {
+	struct i2c_adapter *i2c;
+	const struct stv0288_config *config;
+	struct dvb_frontend frontend;
+
+	u8 initialised:1;
+	u32 tuner_frequency;
+	u32 symbol_rate;
+	fe_code_rate_t fec_inner;
+	int errmode;
+};
+
+#define STATUS_BER 0
+#define STATUS_UCBLOCKS 1
+
+static int debug;
+static int debug_legacy_dish_switch;
+#define dprintk(args...) \
+	do { \
+		if (debug) \
+			printk(KERN_DEBUG "stv0288: " args); \
+	} while (0)
+
+
+static int stv0288_writeregI(struct stv0288_state *state, u8 reg, u8 data)
+{
+	int ret;
+	u8 buf[] = { reg, data };
+	struct i2c_msg msg = {
+		.addr = state->config->demod_address,
+		.flags = 0,
+		.buf = buf,
+		.len = 2
+	};
+
+	ret = i2c_transfer(state->i2c, &msg, 1);
+
+	if (ret != 1)
+		dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, "
+			"ret == %i)\n", __func__, reg, data, ret);
+
+	return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+static int stv0288_write(struct dvb_frontend *fe, u8 *buf, int len)
+{
+	struct stv0288_state *state = fe->demodulator_priv;
+
+	if (len != 2)
+		return -EINVAL;
+
+	return stv0288_writeregI(state, buf[0], buf[1]);
+}
+
+static u8 stv0288_readreg(struct stv0288_state *state, u8 reg)
+{
+	int ret;
+	u8 b0[] = { reg };
+	u8 b1[] = { 0 };
+	struct i2c_msg msg[] = {
+		{
+			.addr = state->config->demod_address,
+			.flags = 0,
+			.buf = b0,
+			.len = 1
+		}, {
+			.addr = state->config->demod_address,
+			.flags = I2C_M_RD,
+			.buf = b1,
+			.len = 1
+		}
+	};
+
+	ret = i2c_transfer(state->i2c, msg, 2);
+
+	if (ret != 2)
+		dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n",
+				__func__, reg, ret);
+
+	return b1[0];
+}
+
+static int stv0288_set_symbolrate(struct dvb_frontend *fe, u32 srate)
+{
+	struct stv0288_state *state = fe->demodulator_priv;
+	unsigned int temp;
+	unsigned char b[3];
+
+	if ((srate < 1000000) || (srate > 45000000))
+		return -EINVAL;
+
+	temp = (unsigned int)srate / 1000;
+
+		temp = temp * 32768;
+		temp = temp / 25;
+		temp = temp / 125;
+		b[0] = (unsigned char)((temp >> 12) & 0xff);
+		b[1] = (unsigned char)((temp >> 4) & 0xff);
+		b[2] = (unsigned char)((temp << 4) & 0xf0);
+		stv0288_writeregI(state, 0x28, 0x80); /* SFRH */
+		stv0288_writeregI(state, 0x29, 0); /* SFRM */
+		stv0288_writeregI(state, 0x2a, 0); /* SFRL */
+
+		stv0288_writeregI(state, 0x28, b[0]);
+		stv0288_writeregI(state, 0x29, b[1]);
+		stv0288_writeregI(state, 0x2a, b[2]);
+		dprintk("stv0288: stv0288_set_symbolrate\n");
+
+	return 0;
+}
+
+static int stv0288_send_diseqc_msg(struct dvb_frontend *fe,
+				    struct dvb_diseqc_master_cmd *m)
+{
+	struct stv0288_state *state = fe->demodulator_priv;
+
+	int i;
+
+	dprintk("%s\n", __func__);
+
+	stv0288_writeregI(state, 0x09, 0);
+	msleep(30);
+	stv0288_writeregI(state, 0x05, 0x16);
+
+	for (i = 0; i < m->msg_len; i++) {
+		if (stv0288_writeregI(state, 0x06, m->msg[i]))
+			return -EREMOTEIO;
+		msleep(12);
+	}
+
+	return 0;
+}
+
+static int stv0288_send_diseqc_burst(struct dvb_frontend *fe,
+						fe_sec_mini_cmd_t burst)
+{
+	struct stv0288_state *state = fe->demodulator_priv;
+
+	dprintk("%s\n", __func__);
+
+	if (stv0288_writeregI(state, 0x05, 0x16))/* burst mode */
+		return -EREMOTEIO;
+
+	if (stv0288_writeregI(state, 0x06, burst == SEC_MINI_A ? 0x00 : 0xff))
+		return -EREMOTEIO;
+
+	if (stv0288_writeregI(state, 0x06, 0x12))
+		return -EREMOTEIO;
+
+	return 0;
+}
+
+static int stv0288_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+	struct stv0288_state *state = fe->demodulator_priv;
+
+	switch (tone) {
+	case SEC_TONE_ON:
+		if (stv0288_writeregI(state, 0x05, 0x10))/* burst mode */
+			return -EREMOTEIO;
+		return stv0288_writeregI(state, 0x06, 0xff);
+
+	case SEC_TONE_OFF:
+		if (stv0288_writeregI(state, 0x05, 0x13))/* burst mode */
+			return -EREMOTEIO;
+		return stv0288_writeregI(state, 0x06, 0x00);
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static u8 stv0288_inittab[] = {
+	0x01, 0x15,
+	0x02, 0x20,
+	0x09, 0x0,
+	0x0a, 0x4,
+	0x0b, 0x0,
+	0x0c, 0x0,
+	0x0d, 0x0,
+	0x0e, 0xd4,
+	0x0f, 0x30,
+	0x11, 0x80,
+	0x12, 0x03,
+	0x13, 0x48,
+	0x14, 0x84,
+	0x15, 0x45,
+	0x16, 0xb7,
+	0x17, 0x9c,
+	0x18, 0x0,
+	0x19, 0xa6,
+	0x1a, 0x88,
+	0x1b, 0x8f,
+	0x1c, 0xf0,
+	0x20, 0x0b,
+	0x21, 0x54,
+	0x22, 0x0,
+	0x23, 0x0,
+	0x2b, 0xff,
+	0x2c, 0xf7,
+	0x30, 0x0,
+	0x31, 0x1e,
+	0x32, 0x14,
+	0x33, 0x0f,
+	0x34, 0x09,
+	0x35, 0x0c,
+	0x36, 0x05,
+	0x37, 0x2f,
+	0x38, 0x16,
+	0x39, 0xbe,
+	0x3a, 0x0,
+	0x3b, 0x13,
+	0x3c, 0x11,
+	0x3d, 0x30,
+	0x40, 0x63,
+	0x41, 0x04,
+	0x42, 0x60,
+	0x43, 0x00,
+	0x44, 0x00,
+	0x45, 0x00,
+	0x46, 0x00,
+	0x47, 0x00,
+	0x4a, 0x00,
+	0x50, 0x10,
+	0x51, 0x38,
+	0x52, 0x21,
+	0x58, 0x54,
+	0x59, 0x86,
+	0x5a, 0x0,
+	0x5b, 0x9b,
+	0x5c, 0x08,
+	0x5d, 0x7f,
+	0x5e, 0x0,
+	0x5f, 0xff,
+	0x70, 0x0,
+	0x71, 0x0,
+	0x72, 0x0,
+	0x74, 0x0,
+	0x75, 0x0,
+	0x76, 0x0,
+	0x81, 0x0,
+	0x82, 0x3f,
+	0x83, 0x3f,
+	0x84, 0x0,
+	0x85, 0x0,
+	0x88, 0x0,
+	0x89, 0x0,
+	0x8a, 0x0,
+	0x8b, 0x0,
+	0x8c, 0x0,
+	0x90, 0x0,
+	0x91, 0x0,
+	0x92, 0x0,
+	0x93, 0x0,
+	0x94, 0x1c,
+	0x97, 0x0,
+	0xa0, 0x48,
+	0xa1, 0x0,
+	0xb0, 0xb8,
+	0xb1, 0x3a,
+	0xb2, 0x10,
+	0xb3, 0x82,
+	0xb4, 0x80,
+	0xb5, 0x82,
+	0xb6, 0x82,
+	0xb7, 0x82,
+	0xb8, 0x20,
+	0xb9, 0x0,
+	0xf0, 0x0,
+	0xf1, 0x0,
+	0xf2, 0xc0,
+	0x51, 0x36,
+	0x52, 0x09,
+	0x53, 0x94,
+	0x54, 0x62,
+	0x55, 0x29,
+	0x56, 0x64,
+	0x57, 0x2b,
+	0xff, 0xff,
+};
+
+static int stv0288_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt)
+{
+	dprintk("%s: %s\n", __func__,
+		volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
+		volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??");
+
+	return 0;
+}
+
+static int stv0288_init(struct dvb_frontend *fe)
+{
+	struct stv0288_state *state = fe->demodulator_priv;
+	int i;
+	u8 reg;
+	u8 val;
+
+	dprintk("stv0288: init chip\n");
+	stv0288_writeregI(state, 0x41, 0x04);
+	msleep(50);
+
+	/* we have default inittab */
+	if (state->config->inittab == NULL) {
+		for (i = 0; !(stv0288_inittab[i] == 0xff &&
+				stv0288_inittab[i + 1] == 0xff); i += 2)
+			stv0288_writeregI(state, stv0288_inittab[i],
+					stv0288_inittab[i + 1]);
+	} else {
+		for (i = 0; ; i += 2)  {
+			reg = state->config->inittab[i];
+			val = state->config->inittab[i+1];
+			if (reg == 0xff && val == 0xff)
+				break;
+			stv0288_writeregI(state, reg, val);
+		}
+	}
+	return 0;
+}
+
+static int stv0288_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+	struct stv0288_state *state = fe->demodulator_priv;
+
+	u8 sync = stv0288_readreg(state, 0x24);
+	if (sync == 255)
+		sync = 0;
+
+	dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, sync);
+
+	*status = 0;
+
+	if ((sync & 0x08) == 0x08) {
+		*status |= FE_HAS_LOCK;
+		dprintk("stv0288 has locked\n");
+	}
+
+	return 0;
+}
+
+static int stv0288_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+	struct stv0288_state *state = fe->demodulator_priv;
+
+	if (state->errmode != STATUS_BER)
+		return 0;
+	*ber = (stv0288_readreg(state, 0x26) << 8) |
+					stv0288_readreg(state, 0x27);
+	dprintk("stv0288_read_ber %d\n", *ber);
+
+	return 0;
+}
+
+
+static int stv0288_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+	struct stv0288_state *state = fe->demodulator_priv;
+
+	s32 signal =  0xffff - ((stv0288_readreg(state, 0x10) << 8));
+
+
+	signal = signal * 5 / 4;
+	*strength = (signal > 0xffff) ? 0xffff : (signal < 0) ? 0 : signal;
+	dprintk("stv0288_read_signal_strength %d\n", *strength);
+
+	return 0;
+}
+static int stv0288_sleep(struct dvb_frontend *fe)
+{
+	struct stv0288_state *state = fe->demodulator_priv;
+
+	stv0288_writeregI(state, 0x41, 0x84);
+	state->initialised = 0;
+
+	return 0;
+}
+static int stv0288_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+	struct stv0288_state *state = fe->demodulator_priv;
+
+	s32 xsnr = 0xffff - ((stv0288_readreg(state, 0x2d) << 8)
+			   | stv0288_readreg(state, 0x2e));
+	xsnr = 3 * (xsnr - 0xa100);
+	*snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr;
+	dprintk("stv0288_read_snr %d\n", *snr);
+
+	return 0;
+}
+
+static int stv0288_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+	struct stv0288_state *state = fe->demodulator_priv;
+
+	if (state->errmode != STATUS_BER)
+		return 0;
+	*ucblocks = (stv0288_readreg(state, 0x26) << 8) |
+					stv0288_readreg(state, 0x27);
+	dprintk("stv0288_read_ber %d\n", *ucblocks);
+
+	return 0;
+}
+
+static int stv0288_set_property(struct dvb_frontend *fe, struct dtv_property *p)
+{
+	dprintk("%s(..)\n", __func__);
+	return 0;
+}
+
+static int stv0288_get_property(struct dvb_frontend *fe, struct dtv_property *p)
+{
+	dprintk("%s(..)\n", __func__);
+	return 0;
+}
+
+static int stv0288_set_frontend(struct dvb_frontend *fe,
+					struct dvb_frontend_parameters *dfp)
+{
+	struct stv0288_state *state = fe->demodulator_priv;
+	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+	char tm;
+	unsigned char tda[3];
+
+	dprintk("%s : FE_SET_FRONTEND\n", __func__);
+
+	if (c->delivery_system != SYS_DVBS) {
+			dprintk("%s: unsupported delivery "
+				"system selected (%d)\n",
+				__func__, c->delivery_system);
+			return -EOPNOTSUPP;
+	}
+
+	if (state->config->set_ts_params)
+		state->config->set_ts_params(fe, 0);
+
+	/* only frequency & symbol_rate are used for tuner*/
+	dfp->frequency = c->frequency;
+	dfp->u.qpsk.symbol_rate = c->symbol_rate;
+	if (fe->ops.tuner_ops.set_params) {
+		fe->ops.tuner_ops.set_params(fe, dfp);
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 0);
+	}
+
+	udelay(10);
+	stv0288_set_symbolrate(fe, c->symbol_rate);
+	/* Carrier lock control register */
+	stv0288_writeregI(state, 0x15, 0xc5);
+
+	tda[0] = 0x2b; /* CFRM */
+	tda[2] = 0x0; /* CFRL */
+	for (tm = -6; tm < 7;) {
+		/* Viterbi status */
+		if (stv0288_readreg(state, 0x24) & 0x80)
+			break;
+
+		tda[2] += 40;
+		if (tda[2] < 40)
+			tm++;
+		tda[1] = (unsigned char)tm;
+		stv0288_writeregI(state, 0x2b, tda[1]);
+		stv0288_writeregI(state, 0x2c, tda[2]);
+		udelay(30);
+	}
+
+	state->tuner_frequency = c->frequency;
+	state->fec_inner = FEC_AUTO;
+	state->symbol_rate = c->symbol_rate;
+
+	return 0;
+}
+
+static int stv0288_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+	struct stv0288_state *state = fe->demodulator_priv;
+
+	if (enable)
+		stv0288_writeregI(state, 0x01, 0xb5);
+	else
+		stv0288_writeregI(state, 0x01, 0x35);
+
+	udelay(1);
+
+	return 0;
+}
+
+static void stv0288_release(struct dvb_frontend *fe)
+{
+	struct stv0288_state *state = fe->demodulator_priv;
+	kfree(state);
+}
+
+static struct dvb_frontend_ops stv0288_ops = {
+
+	.info = {
+		.name			= "ST STV0288 DVB-S",
+		.type			= FE_QPSK,
+		.frequency_min		= 950000,
+		.frequency_max		= 2150000,
+		.frequency_stepsize	= 1000,	 /* kHz for QPSK frontends */
+		.frequency_tolerance	= 0,
+		.symbol_rate_min	= 1000000,
+		.symbol_rate_max	= 45000000,
+		.symbol_rate_tolerance	= 500,	/* ppm */
+		.caps = 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_QPSK |
+		      FE_CAN_FEC_AUTO
+	},
+
+	.release = stv0288_release,
+	.init = stv0288_init,
+	.sleep = stv0288_sleep,
+	.write = stv0288_write,
+	.i2c_gate_ctrl = stv0288_i2c_gate_ctrl,
+	.read_status = stv0288_read_status,
+	.read_ber = stv0288_read_ber,
+	.read_signal_strength = stv0288_read_signal_strength,
+	.read_snr = stv0288_read_snr,
+	.read_ucblocks = stv0288_read_ucblocks,
+	.diseqc_send_master_cmd = stv0288_send_diseqc_msg,
+	.diseqc_send_burst = stv0288_send_diseqc_burst,
+	.set_tone = stv0288_set_tone,
+	.set_voltage = stv0288_set_voltage,
+
+	.set_property = stv0288_set_property,
+	.get_property = stv0288_get_property,
+	.set_frontend = stv0288_set_frontend,
+};
+
+struct dvb_frontend *stv0288_attach(const struct stv0288_config *config,
+				    struct i2c_adapter *i2c)
+{
+	struct stv0288_state *state = NULL;
+	int id;
+
+	/* allocate memory for the internal state */
+	state = kmalloc(sizeof(struct stv0288_state), GFP_KERNEL);
+	if (state == NULL)
+		goto error;
+
+	/* setup the state */
+	state->config = config;
+	state->i2c = i2c;
+	state->initialised = 0;
+	state->tuner_frequency = 0;
+	state->symbol_rate = 0;
+	state->fec_inner = 0;
+	state->errmode = STATUS_BER;
+
+	stv0288_writeregI(state, 0x41, 0x04);
+	msleep(200);
+	id = stv0288_readreg(state, 0x00);
+	dprintk("stv0288 id %x\n", id);
+
+	/* register 0x00 contains 0x11 for STV0288  */
+	if (id != 0x11)
+		goto error;
+
+	/* create dvb_frontend */
+	memcpy(&state->frontend.ops, &stv0288_ops,
+			sizeof(struct dvb_frontend_ops));
+	state->frontend.demodulator_priv = state;
+	return &state->frontend;
+
+error:
+	kfree(state);
+
+	return NULL;
+}
+EXPORT_SYMBOL(stv0288_attach);
+
+module_param(debug_legacy_dish_switch, int, 0444);
+MODULE_PARM_DESC(debug_legacy_dish_switch,
+		"Enable timing analysis for Dish Network legacy switches");
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("ST STV0288 DVB Demodulator driver");
+MODULE_AUTHOR("Georg Acher, Bob Liu, Igor liplianin");
+MODULE_LICENSE("GPL");
+

+ 67 - 0
drivers/media/dvb/frontends/stv0288.h

@@ -0,0 +1,67 @@
+/*
+	Driver for ST STV0288 demodulator
+
+	Copyright (C) 2006 Georg Acher, BayCom GmbH, acher (at) baycom (dot) de
+		for Reel Multimedia
+	Copyright (C) 2008 TurboSight.com, <bob@turbosight.com>
+	Copyright (C) 2008 Igor M. Liplianin <liplianin@me.by>
+		Removed stb6000 specific tuner code and revised some
+		procedures.
+
+	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 STV0288_H
+#define STV0288_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+struct stv0288_config {
+	/* the demodulator's i2c address */
+	u8 demod_address;
+
+	u8* inittab;
+
+	/* minimum delay before retuning */
+	int min_delay_ms;
+
+	int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
+};
+
+#if defined(CONFIG_DVB_STV0288) || (defined(CONFIG_DVB_STV0288_MODULE) && \
+							defined(MODULE))
+extern struct dvb_frontend *stv0288_attach(const struct stv0288_config *config,
+					   struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *stv0288_attach(const struct stv0288_config *config,
+					   struct i2c_adapter *i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif /* CONFIG_DVB_STV0288 */
+
+static inline int stv0288_writereg(struct dvb_frontend *fe, u8 reg, u8 val)
+{
+	int r = 0;
+	u8 buf[] = { reg, val };
+	if (fe->ops.write)
+		r = fe->ops.write(fe, buf, 2);
+	return r;
+}
+
+#endif /* STV0288_H */

+ 2 - 0
drivers/media/dvb/frontends/stv0299.c

@@ -559,6 +559,8 @@ static int stv0299_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_par
 	int invval = 0;
 
 	dprintk ("%s : FE_SET_FRONTEND\n", __func__);
+	if (state->config->set_ts_params)
+		state->config->set_ts_params(fe, 0);
 
 	// set the inversion
 	if (p->inversion == INVERSION_OFF) invval = 0;

+ 8 - 5
drivers/media/dvb/frontends/stv0299.h

@@ -89,15 +89,18 @@ struct stv0299_config
 	int min_delay_ms;
 
 	/* Set the symbol rate */
-	int (*set_symbol_rate)(struct dvb_frontend* fe, u32 srate, u32 ratio);
+	int (*set_symbol_rate)(struct dvb_frontend *fe, u32 srate, u32 ratio);
+
+	/* Set device param to start dma */
+	int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
 };
 
 #if defined(CONFIG_DVB_STV0299) || (defined(CONFIG_DVB_STV0299_MODULE) && defined(MODULE))
-extern struct dvb_frontend* stv0299_attach(const struct stv0299_config* config,
-					   struct i2c_adapter* i2c);
+extern struct dvb_frontend *stv0299_attach(const struct stv0299_config *config,
+					   struct i2c_adapter *i2c);
 #else
-static inline struct dvb_frontend* stv0299_attach(const struct stv0299_config* config,
-					   struct i2c_adapter* i2c)
+static inline struct dvb_frontend *stv0299_attach(const struct stv0299_config *config,
+					   struct i2c_adapter *i2c)
 {
 	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
 	return NULL;

+ 73 - 0
drivers/media/dvb/frontends/tdhd1.h

@@ -0,0 +1,73 @@
+/*
+ * tdhd1.h - ALPS TDHD1-204A tuner support
+ *
+ * Copyright (C) 2008 Oliver Endriss <o.endriss@gmx.de>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * The project's page is at http://www.linuxtv.org
+ */
+
+#ifndef TDHD1_H
+#define TDHD1_H
+
+#include "tda1004x.h"
+
+static int alps_tdhd1_204_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name);
+
+static struct tda1004x_config alps_tdhd1_204a_config = {
+	.demod_address = 0x8,
+	.invert = 1,
+	.invert_oclk = 0,
+	.xtal_freq = TDA10046_XTAL_4M,
+	.agc_config = TDA10046_AGC_DEFAULT,
+	.if_freq = TDA10046_FREQ_3617,
+	.request_firmware = alps_tdhd1_204_request_firmware
+};
+
+static int alps_tdhd1_204a_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+{
+	struct i2c_adapter *i2c = fe->tuner_priv;
+	u8 data[4];
+	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+	u32 div;
+
+	div = (params->frequency + 36166666) / 166666;
+
+	data[0] = (div >> 8) & 0x7f;
+	data[1] = div & 0xff;
+	data[2] = 0x85;
+
+	if (params->frequency >= 174000000 && params->frequency <= 230000000)
+		data[3] = 0x02;
+	else if (params->frequency >= 470000000 && params->frequency <= 823000000)
+		data[3] = 0x0C;
+	else if (params->frequency > 823000000 && params->frequency <= 862000000)
+		data[3] = 0x8C;
+	else
+		return -EINVAL;
+
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	if (i2c_transfer(i2c, &msg, 1) != 1)
+		return -EIO;
+
+	return 0;
+}
+
+#endif /* TDHD1_H */

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

@@ -86,6 +86,7 @@ config DVB_BUDGET
 	select DVB_TDA10086 if !DVB_FE_CUSTOMISE
 	select DVB_TDA826X if !DVB_FE_CUSTOMISE
 	select DVB_LNBP21 if !DVB_FE_CUSTOMISE
+	select DVB_TDA1004X if !DVB_FE_CUSTOMISE
 	help
 	  Support for simple SAA7146 based DVB cards (so called Budget-
 	  or Nova-PCI cards) without onboard MPEG2 decoder, and without

+ 95 - 32
drivers/media/dvb/ttpci/av7110.c

@@ -88,6 +88,7 @@ static int budgetpatch;
 static int wss_cfg_4_3 = 0x4008;
 static int wss_cfg_16_9 = 0x0007;
 static int tv_standard;
+static int full_ts;
 
 module_param_named(debug, av7110_debug, int, 0644);
 MODULE_PARM_DESC(debug, "debug level (bitmask, default 0)");
@@ -106,6 +107,8 @@ module_param(volume, int, 0444);
 MODULE_PARM_DESC(volume, "initial volume: default 255 (range 0-255)");
 module_param(budgetpatch, int, 0444);
 MODULE_PARM_DESC(budgetpatch, "use budget-patch hardware modification: default 0 (0 no, 1 autodetect, 2 always)");
+module_param(full_ts, int, 0444);
+MODULE_PARM_DESC(full_ts, "enable code for full-ts hardware modification: 0 disable (default), 1 enable");
 module_param(wss_cfg_4_3, int, 0444);
 MODULE_PARM_DESC(wss_cfg_4_3, "WSS 4:3 - default 0x4008 - bit 15: disable, 14: burst mode, 13..0: wss data");
 module_param(wss_cfg_16_9, int, 0444);
@@ -116,6 +119,8 @@ MODULE_PARM_DESC(tv_standard, "TV standard: 0 PAL (default), 1 NTSC");
 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 
 static void restart_feeds(struct av7110 *av7110);
+static int budget_start_feed(struct dvb_demux_feed *feed);
+static int budget_stop_feed(struct dvb_demux_feed *feed);
 
 static int av7110_num;
 
@@ -376,9 +381,9 @@ static inline void start_debi_dma(struct av7110 *av7110, int dir,
 		irdebi(av7110, DEBISWAB, addr, 0, len);
 }
 
-static void debiirq(unsigned long data)
+static void debiirq(unsigned long cookie)
 {
-	struct av7110 *av7110 = (struct av7110 *) data;
+	struct av7110 *av7110 = (struct av7110 *)cookie;
 	int type = av7110->debitype;
 	int handle = (type >> 8) & 0x1f;
 	unsigned int xfer = 0;
@@ -487,9 +492,9 @@ debi_done:
 }
 
 /* irq from av7110 firmware writing the mailbox register in the DPRAM */
-static void gpioirq(unsigned long data)
+static void gpioirq(unsigned long cookie)
 {
-	struct av7110 *av7110 = (struct av7110 *) data;
+	struct av7110 *av7110 = (struct av7110 *)cookie;
 	u32 rxbuf, txbuf;
 	int len;
 
@@ -806,6 +811,9 @@ static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter)
 
 	dprintk(4, "%p\n", av7110);
 
+	if (av7110->full_ts)
+		return 0;
+
 	if (dvbdmxfilter->type == DMX_TYPE_SEC) {
 		if (hw_sections) {
 			buf[4] = (dvbdmxfilter->filter.filter_value[0] << 8) |
@@ -854,6 +862,9 @@ static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter)
 
 	dprintk(4, "%p\n", av7110);
 
+	if (av7110->full_ts)
+		return 0;
+
 	handle = dvbdmxfilter->hw_handle;
 	if (handle >= 32) {
 		printk("%s tried to stop invalid filter %04x, filter type = %x\n",
@@ -913,7 +924,7 @@ static int dvb_feed_start_pid(struct dvb_demux_feed *dvbdmxfeed)
 				return ret;
 		}
 
-	if ((dvbdmxfeed->ts_type & TS_PACKET)) {
+	if ((dvbdmxfeed->ts_type & TS_PACKET) && !av7110->full_ts) {
 		if (dvbdmxfeed->pes_type == 0 && !(dvbdmx->pids[0] & 0x8000))
 			ret = av7110_av_start_record(av7110, RP_AUDIO, dvbdmxfeed);
 		if (dvbdmxfeed->pes_type == 1 && !(dvbdmx->pids[1] & 0x8000))
@@ -974,7 +985,7 @@ static int av7110_start_feed(struct dvb_demux_feed *feed)
 	if (!demux->dmx.frontend)
 		return -EINVAL;
 
-	if (feed->pid > 0x1fff)
+	if (!av7110->full_ts && feed->pid > 0x1fff)
 		return -EINVAL;
 
 	if (feed->type == DMX_TYPE_TS) {
@@ -1003,7 +1014,12 @@ static int av7110_start_feed(struct dvb_demux_feed *feed)
 		}
 	}
 
-	else if (feed->type == DMX_TYPE_SEC) {
+	if (av7110->full_ts) {
+		budget_start_feed(feed);
+		return ret;
+	}
+
+	if (feed->type == DMX_TYPE_SEC) {
 		int i;
 
 		for (i = 0; i < demux->filternum; i++) {
@@ -1050,7 +1066,12 @@ static int av7110_stop_feed(struct dvb_demux_feed *feed)
 				ret = StopHWFilter(feed->filter);
 	}
 
-	if (!ret && feed->type == DMX_TYPE_SEC) {
+	if (av7110->full_ts) {
+		budget_stop_feed(feed);
+		return ret;
+	}
+
+	if (feed->type == DMX_TYPE_SEC) {
 		for (i = 0; i<demux->filternum; i++) {
 			if (demux->filter[i].state == DMX_STATE_GO &&
 			    demux->filter[i].filter.parent == &feed->feed.sec) {
@@ -1074,6 +1095,7 @@ static void restart_feeds(struct av7110 *av7110)
 	struct dvb_demux *dvbdmx = &av7110->demux;
 	struct dvb_demux_feed *feed;
 	int mode;
+	int feeding;
 	int i, j;
 
 	dprintk(4, "%p\n", av7110);
@@ -1082,6 +1104,8 @@ static void restart_feeds(struct av7110 *av7110)
 	av7110->playing = 0;
 	av7110->rec_mode = 0;
 
+	feeding = av7110->feeding1; /* full_ts mod */
+
 	for (i = 0; i < dvbdmx->feednum; i++) {
 		feed = &dvbdmx->feed[i];
 		if (feed->state == DMX_STATE_GO) {
@@ -1099,6 +1123,8 @@ static void restart_feeds(struct av7110 *av7110)
 		}
 	}
 
+	av7110->feeding1 = feeding; /* full_ts mod */
+
 	if (mode)
 		av7110_av_start_play(av7110, mode);
 }
@@ -1197,8 +1223,9 @@ static int start_ts_capture(struct av7110 *budget)
 
 	if (budget->feeding1)
 		return ++budget->feeding1;
-	memset(budget->grabbing, 0x00, TS_HEIGHT * TS_WIDTH);
+	memset(budget->grabbing, 0x00, TS_BUFLEN);
 	budget->ttbp = 0;
+	SAA7146_ISR_CLEAR(budget->dev, MASK_10);  /* VPE */
 	SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */
 	saa7146_write(budget->dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */
 	return ++budget->feeding1;
@@ -1233,18 +1260,14 @@ static int budget_stop_feed(struct dvb_demux_feed *feed)
 	return status;
 }
 
-static void vpeirq(unsigned long data)
+static void vpeirq(unsigned long cookie)
 {
-	struct av7110 *budget = (struct av7110 *) data;
+	struct av7110 *budget = (struct av7110 *)cookie;
 	u8 *mem = (u8 *) (budget->grabbing);
 	u32 olddma = budget->ttbp;
 	u32 newdma = saa7146_read(budget->dev, PCI_VDP3);
+	struct dvb_demux *demux = budget->full_ts ? &budget->demux : &budget->demux1;
 
-	if (!budgetpatch) {
-		printk("av7110.c: vpeirq() called while budgetpatch disabled!"
-		       " check saa7146 IER register\n");
-		BUG();
-	}
 	/* nearest lower position divisible by 188 */
 	newdma -= newdma % 188;
 
@@ -1268,11 +1291,11 @@ static void vpeirq(unsigned long data)
 
 	if (newdma > olddma)
 		/* no wraparound, dump olddma..newdma */
-		dvb_dmx_swfilter_packets(&budget->demux1, mem + olddma, (newdma - olddma) / 188);
+		dvb_dmx_swfilter_packets(demux, mem + olddma, (newdma - olddma) / 188);
 	else {
 		/* wraparound, dump olddma..buflen and 0..newdma */
-		dvb_dmx_swfilter_packets(&budget->demux1, mem + olddma, (TS_BUFLEN - olddma) / 188);
-		dvb_dmx_swfilter_packets(&budget->demux1, mem, newdma / 188);
+		dvb_dmx_swfilter_packets(demux, mem + olddma, (TS_BUFLEN - olddma) / 188);
+		dvb_dmx_swfilter_packets(demux, mem, newdma / 188);
 	}
 }
 
@@ -1294,8 +1317,8 @@ static int av7110_register(struct av7110 *av7110)
 	for (i = 0; i < 32; i++)
 		av7110->handle2filter[i] = NULL;
 
-	dvbdemux->filternum = 32;
-	dvbdemux->feednum = 32;
+	dvbdemux->filternum = (av7110->full_ts) ? 256 : 32;
+	dvbdemux->feednum = (av7110->full_ts) ? 256 : 32;
 	dvbdemux->start_feed = av7110_start_feed;
 	dvbdemux->stop_feed = av7110_stop_feed;
 	dvbdemux->write_to_decoder = av7110_write_to_decoder;
@@ -1305,7 +1328,7 @@ static int av7110_register(struct av7110 *av7110)
 	dvb_dmx_init(&av7110->demux);
 	av7110->demux.dmx.get_stc = dvb_get_stc;
 
-	av7110->dmxdev.filternum = 32;
+	av7110->dmxdev.filternum = (av7110->full_ts) ? 256 : 32;
 	av7110->dmxdev.demux = &dvbdemux->dmx;
 	av7110->dmxdev.capabilities = 0;
 
@@ -1422,7 +1445,6 @@ int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val)
 	return i2c_transfer(&av7110->i2c_adap, &msgs, 1);
 }
 
-#if 0
 u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg)
 {
 	u8 mm1[] = {0x00};
@@ -1439,7 +1461,6 @@ u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg)
 
 	return mm2[0];
 }
-#endif
 
 /****************************************************************************
  * INITIALIZATION
@@ -2256,7 +2277,7 @@ static int frontend_init(struct av7110 *av7110)
 	if (!av7110->fe) {
 		/* FIXME: propagate the failure code from the lower layers */
 		ret = -ENOMEM;
-		printk("dvb-ttpci: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n",
+		printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
 		       av7110->dev->pci->vendor,
 		       av7110->dev->pci->device,
 		       av7110->dev->pci->subsystem_vendor,
@@ -2484,7 +2505,47 @@ static int __devinit av7110_attach(struct saa7146_dev* dev,
 			       av7110->dvb_adapter.proposed_mac);
 	ret = -ENOMEM;
 
-	if (budgetpatch) {
+	/* full-ts mod? */
+	if (full_ts)
+		av7110->full_ts = true;
+
+	/* check for full-ts flag in eeprom */
+	if (i2c_readreg(av7110, 0xaa, 0) == 0x4f && i2c_readreg(av7110, 0xaa, 1) == 0x45) {
+		u8 flags = i2c_readreg(av7110, 0xaa, 2);
+		if (flags != 0xff && (flags & 0x01))
+			av7110->full_ts = true;
+	}
+
+	if (av7110->full_ts) {
+		printk(KERN_INFO "dvb-ttpci: full-ts mode enabled for saa7146 port B\n");
+		spin_lock_init(&av7110->feedlock1);
+		av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length,
+								 &av7110->pt);
+		if (!av7110->grabbing)
+			goto err_i2c_del_3;
+
+		saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+		saa7146_write(dev, MC2, (MASK_10 | MASK_26));
+
+		saa7146_write(dev, DD1_INIT, 0x00000600);
+		saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+		saa7146_write(dev, BRS_CTRL, 0x60000000);
+		saa7146_write(dev, MC2, MASK_08 | MASK_24);
+
+		/* dma3 */
+		saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000));
+		saa7146_write(dev, BASE_ODD3, 0);
+		saa7146_write(dev, BASE_EVEN3, 0);
+		saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT);
+		saa7146_write(dev, PITCH3, TS_WIDTH);
+		saa7146_write(dev, BASE_PAGE3, av7110->pt.dma | ME1 | 0x90);
+		saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH);
+		saa7146_write(dev, MC2, MASK_04 | MASK_20);
+
+		tasklet_init(&av7110->vpe_tasklet, vpeirq, (unsigned long) av7110);
+
+	} else if (budgetpatch) {
 		spin_lock_init(&av7110->feedlock1);
 		av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length,
 								 &av7110->pt);
@@ -2710,11 +2771,13 @@ static int __devexit av7110_detach(struct saa7146_dev* saa)
 #if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE)
 	av7110_ir_exit(av7110);
 #endif
-	if (budgetpatch) {
-		/* Disable RPS1 */
-		saa7146_write(saa, MC1, MASK_29);
-		/* VSYNC LOW (inactive) */
-		saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
+	if (budgetpatch || av7110->full_ts) {
+		if (budgetpatch) {
+			/* Disable RPS1 */
+			saa7146_write(saa, MC1, MASK_29);
+			/* VSYNC LOW (inactive) */
+			saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO);
+		}
 		saa7146_write(saa, MC1, MASK_20);	/* DMA3 off */
 		SAA7146_IER_DISABLE(saa, MASK_10);
 		SAA7146_ISR_CLEAR(saa, MASK_10);
@@ -2794,7 +2857,7 @@ static void av7110_irq(struct saa7146_dev* dev, u32 *isr)
 		tasklet_schedule(&av7110->gpio_tasklet);
 	}
 
-	if ((*isr & MASK_10) && budgetpatch)
+	if (*isr & MASK_10)
 		tasklet_schedule(&av7110->vpe_tasklet);
 }
 

+ 1 - 0
drivers/media/dvb/ttpci/av7110.h

@@ -192,6 +192,7 @@ struct av7110 {
 	unsigned char           *grabbing;
 	struct saa7146_pgtable  pt;
 	struct tasklet_struct   vpe_tasklet;
+	bool			full_ts;
 
 	int			fe_synced;
 	struct mutex		pid_mutex;

+ 3 - 0
drivers/media/dvb/ttpci/av7110_av.c

@@ -788,6 +788,9 @@ int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t l
 
 	dprintk(2, "av7110:%p, \n", av7110);
 
+	if (av7110->full_ts && demux->dmx.frontend->source != DMX_MEMORY_FE)
+		return 0;
+
 	switch (feed->pes_type) {
 	case 0:
 		if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY)

+ 6 - 2
drivers/media/dvb/ttpci/budget-av.c

@@ -57,6 +57,8 @@
 #define SLOTSTATUS_READY        8
 #define SLOTSTATUS_OCCUPIED     (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY)
 
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
 struct budget_av {
 	struct budget budget;
 	struct video_device *vd;
@@ -1049,7 +1051,7 @@ static void frontend_init(struct budget_av *budget_av)
 
 	if (fe == NULL) {
 		printk(KERN_ERR "budget-av: A frontend driver was not found "
-				"for device %04x/%04x subsystem %04x/%04x\n",
+				"for device [%04x:%04x] subsystem [%04x:%04x]\n",
 		       saa->pci->vendor,
 		       saa->pci->device,
 		       saa->pci->subsystem_vendor,
@@ -1127,7 +1129,9 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio
 
 	dev->ext_priv = budget_av;
 
-	if ((err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE))) {
+	err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE,
+				adapter_nr);
+	if (err) {
 		kfree(budget_av);
 		return err;
 	}

+ 5 - 2
drivers/media/dvb/ttpci/budget-ci.c

@@ -92,6 +92,8 @@ static int ir_debug;
 module_param(ir_debug, int, 0644);
 MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding");
 
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
 struct budget_ci_ir {
 	struct input_dev *dev;
 	struct tasklet_struct msp430_irq_tasklet;
@@ -1153,7 +1155,7 @@ static void frontend_init(struct budget_ci *budget_ci)
 	}
 
 	if (budget_ci->budget.dvb_frontend == NULL) {
-		printk("budget-ci: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n",
+		printk("budget-ci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
 		       budget_ci->budget.dev->pci->vendor,
 		       budget_ci->budget.dev->pci->device,
 		       budget_ci->budget.dev->pci->subsystem_vendor,
@@ -1183,7 +1185,8 @@ static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio
 
 	dev->ext_priv = budget_ci;
 
-	err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE);
+	err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE,
+				adapter_nr);
 	if (err)
 		goto out2;
 

+ 2 - 4
drivers/media/dvb/ttpci/budget-core.c

@@ -57,8 +57,6 @@ module_param_named(bufsize, dma_buffer_size, int, 0444);
 MODULE_PARM_DESC(debug, "Turn on/off budget debugging (default:off).");
 MODULE_PARM_DESC(bufsize, "DMA buffer size in KB, default: 188, min: 188, max: 1410 (Activy: 564)");
 
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
 /****************************************************************************
  * TT budget / WinTV Nova
  ****************************************************************************/
@@ -411,7 +409,7 @@ static void budget_unregister(struct budget *budget)
 
 int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
 		      struct saa7146_pci_extension_data *info,
-		      struct module *owner)
+		      struct module *owner, short *adapter_nums)
 {
 	int ret = 0;
 	struct budget_info *bi = info->ext_priv;
@@ -474,7 +472,7 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev,
 	printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size);
 
 	ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name,
-				   owner, &budget->dev->pci->dev, adapter_nr);
+				   owner, &budget->dev->pci->dev, adapter_nums);
 	if (ret < 0)
 		return ret;
 

+ 6 - 3
drivers/media/dvb/ttpci/budget-patch.c

@@ -39,6 +39,8 @@
 
 #include "bsru6.h"
 
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
 #define budget_patch budget
 
 static struct saa7146_extension budget_extension;
@@ -360,7 +362,7 @@ static void frontend_init(struct budget_patch* budget)
 	}
 
 	if (budget->dvb_frontend == NULL) {
-		printk("dvb-ttpci: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n",
+		printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
 		       budget->dev->pci->vendor,
 		       budget->dev->pci->device,
 		       budget->dev->pci->subsystem_vendor,
@@ -592,8 +594,9 @@ static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_exte
 
 	dprintk(2, "budget: %p\n", budget);
 
-	if ((err = ttpci_budget_init (budget, dev, info, THIS_MODULE))) {
-		kfree (budget);
+	err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr);
+	if (err) {
+		kfree(budget);
 		return err;
 	}
 

Деякі файли не було показано, через те що забагато файлів було змінено