Browse Source

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: (213 commits)
  V4L/DVB (12720): em28xx-cards: Add vendor/product id for Kworld DVD Maker 2
  V4L/DVB (12713): em28xx: Cleanups at ir_i2c handler
  V4L/DVB (12712): em28xx: properly load ir-kbd-i2c when needed
  V4L/DVB (12701): saa7134: ir-kbd-i2c init data needs a persistent object
  V4L/DVB (12699): cx18: ir-kbd-i2c initialization data should point to a persistent object
  V4L/DVB (12698): em28xx: ir-kbd-i2c init data needs a persistent object
  V4L/DVB (12707): gspca - sn9c20x: Add SXGA support to MT9M111
  V4L/DVB (12706): gspca - sn9c20x: disable exposure/gain controls for MT9M111 sensors.
  V4L/DVB (12705): gspca - sn9c20x: Add SXGA support to SOI968
  V4L/DVB (12703): gspca - sn9c20x: Reduces size of object
  V4L/DVB (12704): gspca - sn9c20x: Fix exposure on SOI968 sensors
  V4L/DVB (12696): gspca - sonixj / sn9c102: Two drivers for 0c45:60fc and 0c45:613e.
  V4L/DVB (12695): gspca - vc032x: Do the LED work with the sensor hv7131r.
  V4L/DVB (12694): gspca - vc032x: Change the start exchanges of the sensor hv7131r.
  V4L/DVB (12693): gspca - sunplus: The brightness is signed.
  V4L/DVB (12692): gspca - sunplus: Optimize code.
  V4L/DVB (12691): gspca - sonixj: Don't use mdelay().
  V4L/DVB (12690): gspca - pac7311: Webcam 06f8:3009 added.
  V4L/DVB (12686): dvb-core: check supported QAM modulations
  V4L/DVB (12685): dvb-core: check fe->ops.set_frontend return value
  ...
Linus Torvalds 16 years ago
parent
commit
043fe50f80
100 changed files with 8952 additions and 4118 deletions
  1. 2 0
      Documentation/video4linux/CARDLIST.cx23885
  2. 1 0
      Documentation/video4linux/CARDLIST.cx88
  3. 3 2
      Documentation/video4linux/CARDLIST.em28xx
  4. 4 0
      Documentation/video4linux/CARDLIST.saa7134
  5. 1 0
      Documentation/video4linux/CARDLIST.tuner
  6. 2 2
      Documentation/video4linux/CQcam.txt
  7. 6 0
      Documentation/video4linux/gspca.txt
  8. 176 0
      Documentation/video4linux/si4713.txt
  9. 13 2
      drivers/media/common/ir-functions.c
  10. 568 526
      drivers/media/common/ir-keymaps.c
  11. 18 2
      drivers/media/common/tuners/tda18271-fe.c
  12. 10 10
      drivers/media/common/tuners/tda18271-priv.h
  13. 3 0
      drivers/media/common/tuners/tda18271.h
  14. 6 0
      drivers/media/common/tuners/tuner-simple.c
  15. 25 0
      drivers/media/common/tuners/tuner-types.c
  16. 13 0
      drivers/media/dvb/Kconfig
  17. 57 161
      drivers/media/dvb/b2c2/flexcop-fe-tuner.c
  18. 1 1
      drivers/media/dvb/bt8xx/dst.c
  19. 134 16
      drivers/media/dvb/dm1105/dm1105.c
  20. 174 57
      drivers/media/dvb/dvb-core/dmxdev.c
  21. 8 1
      drivers/media/dvb/dvb-core/dmxdev.h
  22. 2 6
      drivers/media/dvb/dvb-core/dvb_demux.c
  23. 30 5
      drivers/media/dvb/dvb-core/dvb_frontend.c
  24. 5 0
      drivers/media/dvb/dvb-core/dvbdev.h
  25. 4 2
      drivers/media/dvb/dvb-usb/Kconfig
  26. 35 35
      drivers/media/dvb/dvb-usb/a800.c
  27. 38 38
      drivers/media/dvb/dvb-usb/af9005-remote.c
  28. 10 10
      drivers/media/dvb/dvb-usb/af9015.c
  29. 230 230
      drivers/media/dvb/dvb-usb/af9015.h
  30. 46 46
      drivers/media/dvb/dvb-usb/anysee.c
  31. 37 37
      drivers/media/dvb/dvb-usb/cinergyT2-core.c
  32. 1 0
      drivers/media/dvb/dvb-usb/cinergyT2-fe.c
  33. 135 125
      drivers/media/dvb/dvb-usb/cxusb.c
  34. 199 188
      drivers/media/dvb/dvb-usb/dib0700_devices.c
  35. 122 122
      drivers/media/dvb/dvb-usb/dibusb-common.c
  36. 8 2
      drivers/media/dvb/dvb-usb/dibusb-mc.c
  37. 57 57
      drivers/media/dvb/dvb-usb/digitv.c
  38. 18 18
      drivers/media/dvb/dvb-usb/dtt200u.c
  39. 1 1
      drivers/media/dvb/dvb-usb/dvb-usb-i2c.c
  40. 6 0
      drivers/media/dvb/dvb-usb/dvb-usb-ids.h
  41. 71 2
      drivers/media/dvb/dvb-usb/dvb-usb-remote.c
  42. 16 1
      drivers/media/dvb/dvb-usb/dvb-usb.h
  43. 289 114
      drivers/media/dvb/dvb-usb/dw2102.c
  44. 34 34
      drivers/media/dvb/dvb-usb/m920x.c
  45. 49 48
      drivers/media/dvb/dvb-usb/nova-t-usb2.c
  46. 27 28
      drivers/media/dvb/dvb-usb/opera1.c
  47. 3 3
      drivers/media/dvb/dvb-usb/vp702x.c
  48. 51 51
      drivers/media/dvb/dvb-usb/vp7045.c
  49. 103 40
      drivers/media/dvb/firewire/firedtv-avc.c
  50. 7 0
      drivers/media/dvb/frontends/Kconfig
  51. 1 0
      drivers/media/dvb/frontends/Makefile
  52. 1 1
      drivers/media/dvb/frontends/cx22700.c
  53. 5 1
      drivers/media/dvb/frontends/cx24113.c
  54. 1 1
      drivers/media/dvb/frontends/cx24123.c
  55. 1 1
      drivers/media/dvb/frontends/dib0070.c
  56. 1 1
      drivers/media/dvb/frontends/dib7000p.c
  57. 75 0
      drivers/media/dvb/frontends/dvb-pll.c
  58. 4 0
      drivers/media/dvb/frontends/dvb-pll.h
  59. 387 97
      drivers/media/dvb/frontends/lgs8gxx.c
  60. 8 3
      drivers/media/dvb/frontends/lgs8gxx.h
  61. 6 6
      drivers/media/dvb/frontends/lgs8gxx_priv.h
  62. 4 3
      drivers/media/dvb/frontends/mt312.c
  63. 3 1
      drivers/media/dvb/frontends/stb6100.c
  64. 4 4
      drivers/media/dvb/frontends/stv0900_core.c
  65. 1 1
      drivers/media/dvb/frontends/stv0900_sw.c
  66. 24 24
      drivers/media/dvb/frontends/stv6110.c
  67. 1 1
      drivers/media/dvb/frontends/stv6110.h
  68. 1 1
      drivers/media/dvb/frontends/tda10021.c
  69. 2 2
      drivers/media/dvb/frontends/tda8261.c
  70. 1 1
      drivers/media/dvb/frontends/ves1820.c
  71. 1 1
      drivers/media/dvb/frontends/zl10036.c
  72. 308 0
      drivers/media/dvb/frontends/zl10039.c
  73. 40 0
      drivers/media/dvb/frontends/zl10039.h
  74. 11 3
      drivers/media/dvb/frontends/zl10353.c
  75. 1 1
      drivers/media/dvb/pluto2/pluto2.c
  76. 1 1
      drivers/media/dvb/ttpci/av7110_v4l.c
  77. 3 3
      drivers/media/dvb/ttpci/budget-ci.c
  78. 27 32
      drivers/media/radio/Kconfig
  79. 3 1
      drivers/media/radio/Makefile
  80. 4 2
      drivers/media/radio/radio-cadet.c
  81. 0 1863
      drivers/media/radio/radio-si470x.c
  82. 367 0
      drivers/media/radio/radio-si4713.c
  83. 37 0
      drivers/media/radio/si470x/Kconfig
  84. 9 0
      drivers/media/radio/si470x/Makefile
  85. 798 0
      drivers/media/radio/si470x/radio-si470x-common.c
  86. 401 0
      drivers/media/radio/si470x/radio-si470x-i2c.c
  87. 988 0
      drivers/media/radio/si470x/radio-si470x-usb.c
  88. 225 0
      drivers/media/radio/si470x/radio-si470x.h
  89. 2060 0
      drivers/media/radio/si4713-i2c.c
  90. 237 0
      drivers/media/radio/si4713-i2c.h
  91. 3 3
      drivers/media/video/Kconfig
  92. 1 1
      drivers/media/video/au0828/au0828-dvb.c
  93. 0 1
      drivers/media/video/au0828/au0828-i2c.c
  94. 1 0
      drivers/media/video/bt8xx/bttv-cards.c
  95. 6 8
      drivers/media/video/bt8xx/bttv-driver.c
  96. 0 2
      drivers/media/video/bt8xx/bttv-i2c.c
  97. 14 13
      drivers/media/video/bt8xx/bttv-input.c
  98. 0 1
      drivers/media/video/cafe_ccic.c
  99. 5 3
      drivers/media/video/cx18/cx18-cards.c
  100. 11 7
      drivers/media/video/cx18/cx18-cards.h

+ 2 - 0
Documentation/video4linux/CARDLIST.cx23885

@@ -21,3 +21,5 @@
  20 -> Hauppauge WinTV-HVR1255                             [0070:2251]
  21 -> Hauppauge WinTV-HVR1210                             [0070:2291,0070:2295]
  22 -> Mygica X8506 DMB-TH                                 [14f1:8651]
+ 23 -> Magic-Pro ProHDTV Extreme 2                         [14f1:8657]
+ 24 -> Hauppauge WinTV-HVR1850                             [0070:8541]

+ 1 - 0
Documentation/video4linux/CARDLIST.cx88

@@ -80,3 +80,4 @@
  79 -> Terratec Cinergy HT PCI MKII                        [153b:1177]
  80 -> Hauppauge WinTV-IR Only                             [0070:9290]
  81 -> Leadtek WinFast DTV1800 Hybrid                      [107d:6654]
+ 82 -> WinFast DTV2000 H rev. J                            [107d:6f2b]

+ 3 - 2
Documentation/video4linux/CARDLIST.em28xx

@@ -7,7 +7,7 @@
   6 -> Terratec Cinergy 200 USB                 (em2800)
   7 -> Leadtek Winfast USB II                   (em2800)        [0413:6023]
   8 -> Kworld USB2800                           (em2800)
-  9 -> Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker (em2820/em2840) [1b80:e302,2304:0207,2304:021a]
+  9 -> Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker  (em2820/em2840) [1b80:e302,1b80:e304,2304:0207,2304:021a]
  10 -> Hauppauge WinTV HVR 900                  (em2880)        [2040:6500]
  11 -> Terratec Hybrid XS                       (em2880)        [0ccd:0042]
  12 -> Kworld PVR TV 2800 RF                    (em2820/em2840)
@@ -33,7 +33,7 @@
  34 -> Terratec Cinergy A Hybrid XS             (em2860)        [0ccd:004f]
  35 -> Typhoon DVD Maker                        (em2860)
  36 -> NetGMBH Cam                              (em2860)
- 37 -> Gadmei UTV330                            (em2860)
+ 37 -> Gadmei UTV330                            (em2860)        [eb1a:50a6]
  38 -> Yakumo MovieMixer                        (em2861)
  39 -> KWorld PVRTV 300U                        (em2861)        [eb1a:e300]
  40 -> Plextor ConvertX PX-TV100U               (em2861)        [093b:a005]
@@ -67,3 +67,4 @@
  69 -> KWorld ATSC 315U HDTV TV Box             (em2882)        [eb1a:a313]
  70 -> Evga inDtube                             (em2882)
  71 -> Silvercrest Webcam 1.3mpix               (em2820/em2840)
+ 72 -> Gadmei UTV330+                           (em2861)

+ 4 - 0
Documentation/video4linux/CARDLIST.saa7134

@@ -167,3 +167,7 @@
 166 -> Beholder BeholdTV 607 RDS                [5ace:6073]
 167 -> Beholder BeholdTV 609 RDS                [5ace:6092]
 168 -> Beholder BeholdTV 609 RDS                [5ace:6093]
+169 -> Compro VideoMate S350/S300               [185b:c900]
+170 -> AverMedia AverTV Studio 505              [1461:a115]
+171 -> Beholder BeholdTV X7                     [5ace:7595]
+172 -> RoverMedia TV Link Pro FM                [19d1:0138]

+ 1 - 0
Documentation/video4linux/CARDLIST.tuner

@@ -78,3 +78,4 @@ tuner=77 - TCL tuner MF02GIP-5N-E
 tuner=78 - Philips FMD1216MEX MK3 Hybrid Tuner
 tuner=79 - Philips PAL/SECAM multi (FM1216 MK5)
 tuner=80 - Philips FQ1216LME MK3 PAL/SECAM w/active loopthrough
+tuner=81 - Partsnic (Daewoo) PTI-5NF05

+ 2 - 2
Documentation/video4linux/CQcam.txt

@@ -18,8 +18,8 @@ Table of Contents
 
 1.0 Introduction
 
-  The file ../drivers/char/c-qcam.c is a device driver for the
-Logitech (nee Connectix) parallel port interface color CCD camera.
+  The file ../../drivers/media/video/c-qcam.c is a device driver for
+the Logitech (nee Connectix) parallel port interface color CCD camera.
 This is a fairly inexpensive device for capturing images.  Logitech
 does not currently provide information for developers, but many people
 have engineered several solutions for non-Microsoft use of the Color

+ 6 - 0
Documentation/video4linux/gspca.txt

@@ -140,6 +140,7 @@ spca500		04fc:7333	PalmPixDC85
 sunplus		04fc:ffff	Pure DigitalDakota
 spca501		0506:00df	3Com HomeConnect Lite
 sunplus		052b:1513	Megapix V4
+sunplus		052b:1803	MegaImage VI
 tv8532		0545:808b	Veo Stingray
 tv8532		0545:8333	Veo Stingray
 sunplus		0546:3155	Polaroid PDC3070
@@ -182,6 +183,7 @@ ov534		06f8:3002	Hercules Blog Webcam
 ov534		06f8:3003	Hercules Dualpix HD Weblog
 sonixj		06f8:3004	Hercules Classic Silver
 sonixj		06f8:3008	Hercules Deluxe Optical Glass
+pac7311		06f8:3009	Hercules Classic Link
 spca508		0733:0110	ViewQuest VQ110
 spca508		0130:0130	Clone Digital Webcam 11043
 spca501		0733:0401	Intel Create and Share
@@ -235,8 +237,10 @@ pac7311		093a:2621	PAC731x
 pac7311		093a:2622	Genius Eye 312
 pac7311		093a:2624	PAC7302
 pac7311		093a:2626	Labtec 2200
+pac7311		093a:2629	Genious iSlim 300
 pac7311		093a:262a	Webcam 300k
 pac7311		093a:262c	Philips SPC 230 NC
+jeilinj		0979:0280	Sakar 57379
 zc3xx		0ac8:0302	Z-star Vimicro zc0302
 vc032x		0ac8:0321	Vimicro generic vc0321
 vc032x		0ac8:0323	Vimicro Vc0323
@@ -247,6 +251,7 @@ zc3xx		0ac8:305b	Z-star Vimicro zc0305b
 zc3xx		0ac8:307b	Ldlc VC302+Ov7620
 vc032x		0ac8:c001	Sony embedded vimicro
 vc032x		0ac8:c002	Sony embedded vimicro
+vc032x		0ac8:c301	Samsung Q1 Ultra Premium
 spca508		0af9:0010	Hama USB Sightcam 100
 spca508		0af9:0011	Hama USB Sightcam 100
 sonixb		0c45:6001	Genius VideoCAM NB
@@ -284,6 +289,7 @@ sonixj		0c45:613a	Microdia Sonix PC Camera
 sonixj		0c45:613b	Surfer SN-206
 sonixj		0c45:613c	Sonix Pccam168
 sonixj		0c45:6143	Sonix Pccam168
+sonixj		0c45:6148	Digitus DA-70811/ZSMC USB PC Camera ZS211/Microdia
 sn9c20x		0c45:6240	PC Camera (SN9C201 + MT9M001)
 sn9c20x		0c45:6242	PC Camera (SN9C201 + MT9M111)
 sn9c20x		0c45:6248	PC Camera (SN9C201 + OV9655)

+ 176 - 0
Documentation/video4linux/si4713.txt

@@ -0,0 +1,176 @@
+Driver for I2C radios for the Silicon Labs Si4713 FM Radio Transmitters
+
+Copyright (c) 2009 Nokia Corporation
+Contact: Eduardo Valentin <eduardo.valentin@nokia.com>
+
+
+Information about the Device
+============================
+This chip is a Silicon Labs product. It is a I2C device, currently on 0x63 address.
+Basically, it has transmission and signal noise level measurement features.
+
+The Si4713 integrates transmit functions for FM broadcast stereo transmission.
+The chip also allows integrated receive power scanning to identify low signal
+power FM channels.
+
+The chip is programmed using commands and responses. There are also several
+properties which can change the behavior of this chip.
+
+Users must comply with local regulations on radio frequency (RF) transmission.
+
+Device driver description
+=========================
+There are two modules to handle this device. One is a I2C device driver
+and the other is a platform driver.
+
+The I2C device driver exports a v4l2-subdev interface to the kernel.
+All properties can also be accessed by v4l2 extended controls interface, by
+using the v4l2-subdev calls (g_ext_ctrls, s_ext_ctrls).
+
+The platform device driver exports a v4l2 radio device interface to user land.
+So, it uses the I2C device driver as a sub device in order to send the user
+commands to the actual device. Basically it is a wrapper to the I2C device driver.
+
+Applications can use v4l2 radio API to specify frequency of operation, mute state,
+etc. But mostly of its properties will be present in the extended controls.
+
+When the v4l2 mute property is set to 1 (true), the driver will turn the chip off.
+
+Properties description
+======================
+
+The properties can be accessed using v4l2 extended controls.
+Here is an output from v4l2-ctl util:
+/ # v4l2-ctl -d /dev/radio0 --all -L
+Driver Info:
+	Driver name   : radio-si4713
+	Card type     : Silicon Labs Si4713 Modulator
+	Bus info      :
+	Driver version: 0
+	Capabilities  : 0x00080800
+		RDS Output
+		Modulator
+Audio output: 0 (FM Modulator Audio Out)
+Frequency: 1408000 (88.000000 MHz)
+Video Standard = 0x00000000
+Modulator:
+	Name                 : FM Modulator
+	Capabilities         : 62.5 Hz stereo rds
+	Frequency range      : 76.0 MHz - 108.0 MHz
+	Subchannel modulation: stereo+rds
+
+User Controls
+
+			   mute (bool) : default=1 value=0
+
+FM Radio Modulator Controls
+
+	   rds_signal_deviation (int)  : min=0 max=90000 step=10 default=200 value=200 flags=slider
+		 rds_program_id (int)  : min=0 max=65535 step=1 default=0 value=0
+	       rds_program_type (int)  : min=0 max=31 step=1 default=0 value=0
+		    rds_ps_name (str)  : min=0 max=96 step=8 value='si4713  '
+		 rds_radio_text (str)  : min=0 max=384 step=32 value=''
+  audio_limiter_feature_enabled (bool) : default=1 value=1
+     audio_limiter_release_time (int)  : min=250 max=102390 step=50 default=5010 value=5010 flags=slider
+	audio_limiter_deviation (int)  : min=0 max=90000 step=10 default=66250 value=66250 flags=slider
+audio_compression_feature_enabl (bool) : default=1 value=1
+	 audio_compression_gain (int)  : min=0 max=20 step=1 default=15 value=15 flags=slider
+    audio_compression_threshold (int)  : min=-40 max=0 step=1 default=-40 value=-40 flags=slider
+  audio_compression_attack_time (int)  : min=0 max=5000 step=500 default=0 value=0 flags=slider
+ audio_compression_release_time (int)  : min=100000 max=1000000 step=100000 default=1000000 value=1000000 flags=slider
+     pilot_tone_feature_enabled (bool) : default=1 value=1
+	   pilot_tone_deviation (int)  : min=0 max=90000 step=10 default=6750 value=6750 flags=slider
+	   pilot_tone_frequency (int)  : min=0 max=19000 step=1 default=19000 value=19000 flags=slider
+	  pre_emphasis_settings (menu) : min=0 max=2 default=1 value=1
+	       tune_power_level (int)  : min=0 max=120 step=1 default=88 value=88 flags=slider
+	 tune_antenna_capacitor (int)  : min=0 max=191 step=1 default=0 value=110 flags=slider
+/ #
+
+Here is a summary of them:
+
+* Pilot is an audible tone sent by the device.
+
+pilot_frequency - Configures the frequency of the stereo pilot tone.
+pilot_deviation - Configures pilot tone frequency deviation level.
+pilot_enabled - Enables or disables the pilot tone feature.
+
+* The si4713 device is capable of applying audio compression to the transmitted signal.
+
+acomp_enabled - Enables or disables the audio dynamic range control feature.
+acomp_gain - Sets the gain for audio dynamic range control.
+acomp_threshold - Sets the threshold level for audio dynamic range control.
+acomp_attack_time - Sets the attack time for audio dynamic range control.
+acomp_release_time - Sets the release time for audio dynamic range control.
+
+* Limiter setups audio deviation limiter feature. Once a over deviation occurs,
+it is possible to adjust the front-end gain of the audio input and always
+prevent over deviation.
+
+limiter_enabled - Enables or disables the limiter feature.
+limiter_deviation - Configures audio frequency deviation level.
+limiter_release_time - Sets the limiter release time.
+
+* Tuning power
+
+power_level - Sets the output power level for signal transmission.
+antenna_capacitor - This selects the value of antenna tuning capacitor manually
+or automatically if set to zero.
+
+* RDS related
+
+rds_ps_name - Sets the RDS ps name field for transmission.
+rds_radio_text - Sets the RDS radio text for transmission.
+rds_pi - Sets the RDS PI field for transmission.
+rds_pty - Sets the RDS PTY field for transmission.
+
+* Region related
+
+preemphasis - sets the preemphasis to be applied for transmission.
+
+RNL
+===
+
+This device also has an interface to measure received noise level. To do that, you should
+ioctl the device node. Here is an code of example:
+
+int main (int argc, char *argv[])
+{
+	struct si4713_rnl rnl;
+	int fd = open("/dev/radio0", O_RDWR);
+	int rval;
+
+	if (argc < 2)
+		return -EINVAL;
+
+	if (fd < 0)
+		return fd;
+
+	sscanf(argv[1], "%d", &rnl.frequency);
+
+	rval = ioctl(fd, SI4713_IOC_MEASURE_RNL, &rnl);
+	if (rval < 0)
+		return rval;
+
+	printf("received noise level: %d\n", rnl.rnl);
+
+	close(fd);
+}
+
+The struct si4713_rnl and SI4713_IOC_MEASURE_RNL are defined under
+include/media/si4713.h.
+
+Stereo/Mono and RDS subchannels
+===============================
+
+The device can also be configured using the available sub channels for
+transmission. To do that use S/G_MODULATOR ioctl and configure txsubchans properly.
+Refer to v4l2-spec for proper use of this ioctl.
+
+Testing
+=======
+Testing is usually done with v4l2-ctl utility for managing FM tuner cards.
+The tool can be found in v4l-dvb repository under v4l2-apps/util directory.
+
+Example for setting rds ps name:
+# v4l2-ctl -d /dev/radio0 --set-ctrl=rds_ps_name="Dummy"
+

+ 13 - 2
drivers/media/common/ir-functions.c

@@ -58,13 +58,24 @@ static void ir_input_key_event(struct input_dev *dev, struct ir_input_state *ir)
 /* -------------------------------------------------------------------------- */
 
 void ir_input_init(struct input_dev *dev, struct ir_input_state *ir,
-		   int ir_type, IR_KEYTAB_TYPE *ir_codes)
+		   int ir_type, struct ir_scancode_table *ir_codes)
 {
 	int i;
 
 	ir->ir_type = ir_type;
+
+	memset(ir->ir_codes, sizeof(ir->ir_codes), 0);
+
+	/*
+	 * FIXME: This is a temporary workaround to use the new IR tables
+	 * with the old approach. Later patches will replace this to a
+	 * proper method
+	 */
+
 	if (ir_codes)
-		memcpy(ir->ir_codes, ir_codes, sizeof(ir->ir_codes));
+		for (i = 0; i < ir_codes->size; i++)
+			if (ir_codes->scan[i].scancode < IR_KEYTAB_SIZE)
+				ir->ir_codes[ir_codes->scan[i].scancode] = ir_codes->scan[i].keycode;
 
 	dev->keycode     = ir->ir_codes;
 	dev->keycodesize = sizeof(IR_KEYTAB_TYPE);

File diff suppressed because it is too large
+ 568 - 526
drivers/media/common/ir-keymaps.c


+ 18 - 2
drivers/media/common/tuners/tda18271-fe.c

@@ -27,7 +27,7 @@ module_param_named(debug, tda18271_debug, int, 0644);
 MODULE_PARM_DESC(debug, "set debug level "
 		 "(info=1, map=2, reg=4, adv=8, cal=16 (or-able))");
 
-static int tda18271_cal_on_startup;
+static int tda18271_cal_on_startup = -1;
 module_param_named(cal, tda18271_cal_on_startup, int, 0644);
 MODULE_PARM_DESC(cal, "perform RF tracking filter calibration on startup");
 
@@ -1192,10 +1192,25 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
 	case 0:
 		goto fail;
 	case 1:
+	{
 		/* new tuner instance */
+		int rf_cal_on_startup;
+
 		priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO;
 		priv->role = (cfg) ? cfg->role : TDA18271_MASTER;
 		priv->config = (cfg) ? cfg->config : 0;
+
+		/* tda18271_cal_on_startup == -1 when cal
+		 * module option is unset */
+		if (tda18271_cal_on_startup == -1) {
+			/* honor attach-time configuration */
+			rf_cal_on_startup =
+				((cfg) && (cfg->rf_cal_on_startup)) ? 1 : 0;
+		} else {
+			/* module option overrides attach configuration */
+			rf_cal_on_startup = tda18271_cal_on_startup;
+		}
+
 		priv->cal_initialized = false;
 		mutex_init(&priv->lock);
 
@@ -1213,11 +1228,12 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
 		mutex_lock(&priv->lock);
 		tda18271_init_regs(fe);
 
-		if ((tda18271_cal_on_startup) && (priv->id == TDA18271HDC2))
+		if ((rf_cal_on_startup) && (priv->id == TDA18271HDC2))
 			tda18271c2_rf_cal_init(fe);
 
 		mutex_unlock(&priv->lock);
 		break;
+	}
 	default:
 		/* existing tuner instance */
 		fe->tuner_priv = priv;

+ 10 - 10
drivers/media/common/tuners/tda18271-priv.h

@@ -137,17 +137,17 @@ extern int tda18271_debug;
 #define tda_printk(kern, fmt, arg...) \
 	printk(kern "%s: " fmt, __func__, ##arg)
 
-#define dprintk(kern, lvl, fmt, arg...) do {\
+#define tda_dprintk(lvl, fmt, arg...) do {\
 	if (tda18271_debug & lvl) \
-		tda_printk(kern, fmt, ##arg); } while (0)
-
-#define tda_info(fmt, arg...) printk(KERN_INFO              fmt, ##arg)
-#define tda_warn(fmt, arg...) tda_printk(KERN_WARNING,      fmt, ##arg)
-#define tda_err(fmt, arg...)  tda_printk(KERN_ERR,          fmt, ##arg)
-#define tda_dbg(fmt, arg...)  dprintk(KERN_DEBUG, DBG_INFO, fmt, ##arg)
-#define tda_map(fmt, arg...)  dprintk(KERN_DEBUG, DBG_MAP,  fmt, ##arg)
-#define tda_reg(fmt, arg...)  dprintk(KERN_DEBUG, DBG_REG,  fmt, ##arg)
-#define tda_cal(fmt, arg...)  dprintk(KERN_DEBUG, DBG_CAL,  fmt, ##arg)
+		tda_printk(KERN_DEBUG, fmt, ##arg); } while (0)
+
+#define tda_info(fmt, arg...)     printk(KERN_INFO     fmt, ##arg)
+#define tda_warn(fmt, arg...) tda_printk(KERN_WARNING, fmt, ##arg)
+#define tda_err(fmt, arg...)  tda_printk(KERN_ERR,     fmt, ##arg)
+#define tda_dbg(fmt, arg...)  tda_dprintk(DBG_INFO,    fmt, ##arg)
+#define tda_map(fmt, arg...)  tda_dprintk(DBG_MAP,     fmt, ##arg)
+#define tda_reg(fmt, arg...)  tda_dprintk(DBG_REG,     fmt, ##arg)
+#define tda_cal(fmt, arg...)  tda_dprintk(DBG_CAL,     fmt, ##arg)
 
 #define tda_fail(ret)							     \
 ({									     \

+ 3 - 0
drivers/media/common/tuners/tda18271.h

@@ -77,6 +77,9 @@ struct tda18271_config {
 	/* use i2c gate provided by analog or digital demod */
 	enum tda18271_i2c_gate gate;
 
+	/* force rf tracking filter calibration on startup */
+	unsigned int rf_cal_on_startup:1;
+
 	/* some i2c providers cant write all 39 registers at once */
 	unsigned int small_i2c:1;
 

+ 6 - 0
drivers/media/common/tuners/tuner-simple.c

@@ -144,6 +144,8 @@ static inline int tuner_stereo(const int type, const int status)
 	case TUNER_LG_NTSC_TAPE:
 	case TUNER_TCL_MF02GIP_5N:
 		return ((status & TUNER_SIGNAL) == TUNER_STEREO_MK3);
+	case TUNER_PHILIPS_FM1216MK5:
+		return status | TUNER_STEREO;
 	default:
 		return status & TUNER_STEREO;
 	}
@@ -508,6 +510,10 @@ static int simple_radio_bandswitch(struct dvb_frontend *fe, u8 *buffer)
 	case TUNER_TCL_MF02GIP_5N:
 		buffer[3] = 0x19;
 		break;
+	case TUNER_PHILIPS_FM1216MK5:
+		buffer[2] = 0x88;
+		buffer[3] = 0x09;
+		break;
 	case TUNER_TNF_5335MF:
 		buffer[3] = 0x11;
 		break;

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

@@ -1301,6 +1301,25 @@ static struct tuner_params tuner_fq1216lme_mk3_params[] = {
 	},
 };
 
+/* ----- TUNER_PARTSNIC_PTI_5NF05 - Partsnic (Daewoo) PTI-5NF05 NTSC ----- */
+
+static struct tuner_range tuner_partsnic_pti_5nf05_ranges[] = {
+	/* The datasheet specified channel ranges and the bandswitch byte */
+	/* The control byte value of 0x8e is just a guess */
+	{ 16 * 133.25 /*MHz*/, 0x8e, 0x01, }, /* Channels    2 -    B */
+	{ 16 * 367.25 /*MHz*/, 0x8e, 0x02, }, /* Channels    C - W+11 */
+	{ 16 * 999.99        , 0x8e, 0x08, }, /* Channels W+12 -   69 */
+};
+
+static struct tuner_params tuner_partsnic_pti_5nf05_params[] = {
+	{
+		.type   = TUNER_PARAM_TYPE_NTSC,
+		.ranges = tuner_partsnic_pti_5nf05_ranges,
+		.count  = ARRAY_SIZE(tuner_partsnic_pti_5nf05_ranges),
+		.cb_first_if_lower_freq = 1, /* not specified but safe to do */
+	},
+};
+
 /* --------------------------------------------------------------------- */
 
 struct tunertype tuners[] = {
@@ -1753,6 +1772,12 @@ struct tunertype tuners[] = {
 		.params = tuner_fq1216lme_mk3_params,
 		.count  = ARRAY_SIZE(tuner_fq1216lme_mk3_params),
 	},
+
+	[TUNER_PARTSNIC_PTI_5NF05] = {
+		.name = "Partsnic (Daewoo) PTI-5NF05",
+		.params = tuner_partsnic_pti_5nf05_params,
+		.count  = ARRAY_SIZE(tuner_partsnic_pti_5nf05_params),
+	},
 };
 EXPORT_SYMBOL(tuners);
 

+ 13 - 0
drivers/media/dvb/Kconfig

@@ -2,6 +2,19 @@
 # DVB device configuration
 #
 
+config DVB_MAX_ADAPTERS
+	int "maximum number of DVB/ATSC adapters"
+	depends on DVB_CORE
+	default 8
+	range 1 255
+	help
+	  Maximum number of DVB/ATSC adapters. Increasing this number
+	  increases the memory consumption of the DVB subsystem even
+	  if a much lower number of DVB/ATSC adapters is present.
+	  Only values in the range 4-32 are tested.
+
+	  If you are unsure about this, use the default value 8
+
 config DVB_DYNAMIC_MINORS
 	bool "Dynamic DVB minor allocation"
 	depends on DVB_CORE

+ 57 - 161
drivers/media/dvb/b2c2/flexcop-fe-tuner.c

@@ -66,7 +66,7 @@ static int flexcop_sleep(struct dvb_frontend* fe)
 #endif
 
 /* SkyStar2 DVB-S rev 2.3 */
-#if FE_SUPPORTED(MT312)
+#if FE_SUPPORTED(MT312) && FE_SUPPORTED(PLL)
 static int flexcop_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
 {
 /* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */
@@ -155,55 +155,34 @@ static struct mt312_config skystar23_samsung_tbdu18132_config = {
 	.demod_address = 0x0e,
 };
 
-static int skystar23_samsung_tbdu18132_tuner_set_params(struct dvb_frontend *fe,
-	struct dvb_frontend_parameters *params)
-{
-	u8 buf[4];
-	u32 div;
-	struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf,
-	.len = sizeof(buf) };
-	struct flexcop_device *fc = fe->dvb->priv;
-	div = (params->frequency + (125/2)) / 125;
-
-	buf[0] = (div >> 8) & 0x7f;
-	buf[1] = (div >> 0) & 0xff;
-	buf[2] = 0x84 | ((div >> 10) & 0x60);
-	buf[3] = 0x80;
-
-	if (params->frequency < 1550000)
-		buf[3] |= 0x02;
-
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-	if (i2c_transfer(&fc->fc_i2c_adap[0].i2c_adap, &msg, 1) != 1)
-		return -EIO;
-	return 0;
-}
-
 static int skystar2_rev23_attach(struct flexcop_device *fc,
 	struct i2c_adapter *i2c)
 {
+	struct dvb_frontend_ops *ops;
+
 	fc->fe = dvb_attach(mt312_attach, &skystar23_samsung_tbdu18132_config, i2c);
-	if (fc->fe != NULL) {
-		struct dvb_frontend_ops *ops = &fc->fe->ops;
-		ops->tuner_ops.set_params   =
-			skystar23_samsung_tbdu18132_tuner_set_params;
-		ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd;
-		ops->diseqc_send_burst      = flexcop_diseqc_send_burst;
-		ops->set_tone               = flexcop_set_tone;
-		ops->set_voltage            = flexcop_set_voltage;
-		fc->fe_sleep                = ops->sleep;
-		ops->sleep                  = flexcop_sleep;
-		return 1;
-	}
-	return 0;
+	if (!fc->fe)
+		return 0;
+
+	if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c,
+			DVB_PLL_SAMSUNG_TBDU18132))
+		return 0;
+
+	ops = &fc->fe->ops;
+	ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd;
+	ops->diseqc_send_burst      = flexcop_diseqc_send_burst;
+	ops->set_tone               = flexcop_set_tone;
+	ops->set_voltage            = flexcop_set_voltage;
+	fc->fe_sleep                = ops->sleep;
+	ops->sleep                  = flexcop_sleep;
+	return 1;
 }
 #else
 #define skystar2_rev23_attach NULL
 #endif
 
 /* SkyStar2 DVB-S rev 2.6 */
-#if FE_SUPPORTED(STV0299)
+#if FE_SUPPORTED(STV0299) && FE_SUPPORTED(PLL)
 static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend *fe,
 	u32 srate, u32 ratio)
 {
@@ -232,31 +211,6 @@ static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend *fe,
 	return 0;
 }
 
-static int samsung_tbmu24112_tuner_set_params(struct dvb_frontend *fe,
-	struct dvb_frontend_parameters *params)
-{
-	u8 buf[4];
-	u32 div;
-	struct i2c_msg msg = {
-	.addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
-	struct flexcop_device *fc = fe->dvb->priv;
-	div = params->frequency / 125;
-
-	buf[0] = (div >> 8) & 0x7f;
-	buf[1] = div & 0xff;
-	buf[2] = 0x84; /* 0xC4 */
-	buf[3] = 0x08;
-
-	if (params->frequency < 1500000)
-		buf[3] |= 0x10;
-
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-	if (i2c_transfer(&fc->fc_i2c_adap[0].i2c_adap, &msg, 1) != 1)
-		return -EIO;
-	return 0;
-}
-
 static u8 samsung_tbmu24112_inittab[] = {
 	0x01, 0x15,
 	0x02, 0x30,
@@ -318,15 +272,18 @@ static int skystar2_rev26_attach(struct flexcop_device *fc,
 	struct i2c_adapter *i2c)
 {
 	fc->fe = dvb_attach(stv0299_attach, &samsung_tbmu24112_config, i2c);
-	if (fc->fe != NULL) {
-		struct dvb_frontend_ops *ops  = &fc->fe->ops;
-		ops->tuner_ops.set_params = samsung_tbmu24112_tuner_set_params;
-		ops->set_voltage = flexcop_set_voltage;
-		fc->fe_sleep = ops->sleep;
-		ops->sleep = flexcop_sleep;
-		return 1;
-	}
-	return 0;
+	if (!fc->fe)
+		return 0;
+
+	if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c,
+			DVB_PLL_SAMSUNG_TBMU24112))
+		return 0;
+
+	fc->fe->ops.set_voltage = flexcop_set_voltage;
+	fc->fe_sleep = fc->fe->ops.sleep;
+	fc->fe->ops.sleep = flexcop_sleep;
+	return 1;
+
 }
 #else
 #define skystar2_rev26_attach NULL
@@ -421,7 +378,7 @@ static int skystar2_rev28_attach(struct flexcop_device *fc,
 	if (!fc->fe)
 		return 0;
 
-	i2c_tuner = cx24123_get_tuner_i2c_adapter(fc->fe);;
+	i2c_tuner = cx24123_get_tuner_i2c_adapter(fc->fe);
 	if (!i2c_tuner)
 		return 0;
 
@@ -449,7 +406,7 @@ static int skystar2_rev28_attach(struct flexcop_device *fc,
 #endif
 
 /* AirStar DVB-T */
-#if FE_SUPPORTED(MT352)
+#if FE_SUPPORTED(MT352) && FE_SUPPORTED(PLL)
 static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend *fe)
 {
 	static u8 mt352_clock_config[] = { 0x89, 0x18, 0x2d };
@@ -467,32 +424,6 @@ static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend *fe)
 	return 0;
 }
 
-static int samsung_tdtc9251dh0_calc_regs(struct dvb_frontend *fe,
-	struct dvb_frontend_parameters *params, u8* pllbuf, int buf_len)
-{
-	u32 div;
-	unsigned char bs = 0;
-
-	if (buf_len < 5)
-		return -EINVAL;
-
-#define IF_FREQUENCYx6 217    /* 6 * 36.16666666667MHz */
-	div = (((params->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6;
-	if (params->frequency >= 48000000 && params->frequency <= 154000000) \
-		bs = 0x09;
-	if (params->frequency >= 161000000 && params->frequency <= 439000000) \
-		bs = 0x0a;
-	if (params->frequency >= 447000000 && params->frequency <= 863000000) \
-		bs = 0x08;
-
-	pllbuf[0] = 0x61;
-	pllbuf[1] = div >> 8;
-	pllbuf[2] = div & 0xff;
-	pllbuf[3] = 0xcc;
-	pllbuf[4] = bs;
-	return 5;
-}
-
 static struct mt352_config samsung_tdtc9251dh0_config = {
 	.demod_address = 0x0f,
 	.demod_init    = samsung_tdtc9251dh0_demod_init,
@@ -502,11 +433,11 @@ static int airstar_dvbt_attach(struct flexcop_device *fc,
 	struct i2c_adapter *i2c)
 {
 	fc->fe = dvb_attach(mt352_attach, &samsung_tdtc9251dh0_config, i2c);
-	if (fc->fe != NULL) {
-		fc->fe->ops.tuner_ops.calc_regs = samsung_tdtc9251dh0_calc_regs;
-		return 1;
-	}
-	return 0;
+	if (!fc->fe)
+		return 0;
+
+	return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL,
+			    DVB_PLL_SAMSUNG_TDTC9251DH0);
 }
 #else
 #define airstar_dvbt_attach NULL
@@ -580,54 +511,7 @@ static int airstar_atsc3_attach(struct flexcop_device *fc,
 #endif
 
 /* CableStar2 DVB-C */
-#if FE_SUPPORTED(STV0297)
-static int alps_tdee4_stv0297_tuner_set_params(struct dvb_frontend* fe,
-		struct dvb_frontend_parameters *fep)
-{
-	struct flexcop_device *fc = fe->dvb->priv;
-	u8 buf[4];
-	u16 div;
-	int ret;
-
-/* 62.5 kHz * 10 */
-#define REF_FREQ    625
-#define FREQ_OFFSET 36125
-
-	div = ((fep->frequency/1000 + FREQ_OFFSET) * 10) / REF_FREQ;
-/* 4 MHz = 4000 KHz */
-
-	buf[0] = (u8)( div >> 8) & 0x7f;
-	buf[1] = (u8)        div & 0xff;
-
-/* F(osc) = N * Reference Freq. (62.5 kHz)
- * byte 2 :  0 N14 N13 N12 N11 N10 N9  N8
- * byte 3 : N7 N6  N5  N4  N3  N2  N1  N0
- * byte 4 : 1  *   *   AGD R3  R2  R1  R0
- * byte 5 : C1 *   RE  RTS BS4 BS3 BS2 BS1
- * AGD = 1, R3 R2 R1 R0 = 0 1 0 1 => byte 4 = 1**10101 = 0x95 */
-	buf[2] = 0x95;
-
-/* Range(MHz)  C1 *  RE RTS BS4 BS3 BS2 BS1  Byte 5
- *  47 - 153   0  *  0   0   0   0   0   1   0x01
- * 153 - 430   0  *  0   0   0   0   1   0   0x02
- * 430 - 822   0  *  0   0   1   0   0   0   0x08
- * 822 - 862   1  *  0   0   1   0   0   0   0x88 */
-
-	     if (fep->frequency <= 153000000) buf[3] = 0x01;
-	else if (fep->frequency <= 430000000) buf[3] = 0x02;
-	else if (fep->frequency <= 822000000) buf[3] = 0x08;
-	else buf[3] = 0x88;
-
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 0);
-	deb_tuner("tuner buffer for %d Hz: %x %x %x %x\n", fep->frequency,
-	buf[0], buf[1], buf[2], buf[3]);
-	ret = fc->i2c_request(&fc->fc_i2c_adap[2],
-			FC_WRITE, 0x61, buf[0], &buf[1], 3);
-	deb_tuner("tuner write returned: %d\n",ret);
-	return ret;
-}
-
+#if FE_SUPPORTED(STV0297) && FE_SUPPORTED(PLL)
 static u8 alps_tdee4_stv0297_inittab[] = {
 	0x80, 0x01,
 	0x80, 0x00,
@@ -711,13 +595,25 @@ static int cablestar2_attach(struct flexcop_device *fc,
 {
 	fc->fc_i2c_adap[0].no_base_addr = 1;
 	fc->fe = dvb_attach(stv0297_attach, &alps_tdee4_stv0297_config, i2c);
-	if (!fc->fe) {
-		/* Reset for next frontend to try */
-		fc->fc_i2c_adap[0].no_base_addr = 0;
-		return 0;
-	}
-	fc->fe->ops.tuner_ops.set_params = alps_tdee4_stv0297_tuner_set_params;
+	if (!fc->fe)
+		goto fail;
+
+	/* This tuner doesn't use the stv0297's I2C gate, but instead the
+	 * tuner is connected to a different flexcop I2C adapter.  */
+	if (fc->fe->ops.i2c_gate_ctrl)
+		fc->fe->ops.i2c_gate_ctrl(fc->fe, 0);
+	fc->fe->ops.i2c_gate_ctrl = NULL;
+
+	if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61,
+			&fc->fc_i2c_adap[2].i2c_adap, DVB_PLL_TDEE4))
+		goto fail;
+
 	return 1;
+
+fail:
+	/* Reset for next frontend to try */
+	fc->fc_i2c_adap[0].no_base_addr = 0;
+	return 0;
 }
 #else
 #define cablestar2_attach NULL

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

@@ -1059,7 +1059,7 @@ static int dst_get_tuner_info(struct dst_state *state)
 		dprintk(verbose, DST_ERROR, 1, "DST type has TS=188");
 	}
 	if (state->board_info[0] == 0xbc) {
-		if (state->type_flags != DST_TYPE_IS_ATSC)
+		if (state->dst_type != DST_TYPE_IS_ATSC)
 			state->type_flags |= DST_TYPE_HAS_TS188;
 		else
 			state->type_flags |= DST_TYPE_HAS_NEWTUNE_2;

+ 134 - 16
drivers/media/dvb/dm1105/dm1105.c

@@ -44,6 +44,14 @@
 #include "cx24116.h"
 #include "z0194a.h"
 
+#define UNSET (-1U)
+
+#define DM1105_BOARD_NOAUTO		UNSET
+#define DM1105_BOARD_UNKNOWN		0
+#define DM1105_BOARD_DVBWORLD_2002	1
+#define DM1105_BOARD_DVBWORLD_2004	2
+#define DM1105_BOARD_AXESS_DM05		3
+
 /* ----------------------------------------------- */
 /*
  * PCI ID's
@@ -153,20 +161,105 @@
 
 /* GPIO's for LNB power control */
 #define DM1105_LNB_MASK				0x00000000
+#define DM1105_LNB_OFF				0x00020000
 #define DM1105_LNB_13V				0x00010100
 #define DM1105_LNB_18V				0x00000100
 
 /* GPIO's for LNB power control for Axess DM05 */
 #define DM05_LNB_MASK				0x00000000
+#define DM05_LNB_OFF				0x00020000/* actually 13v */
 #define DM05_LNB_13V				0x00020000
 #define DM05_LNB_18V				0x00030000
 
+static unsigned int card[]  = {[0 ... 3] = UNSET };
+module_param_array(card,  int, NULL, 0444);
+MODULE_PARM_DESC(card, "card type");
+
 static int ir_debug;
 module_param(ir_debug, int, 0644);
 MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding");
 
+static unsigned int dm1105_devcount;
+
 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 
+struct dm1105_board {
+	char                    *name;
+};
+
+struct dm1105_subid {
+	u16     subvendor;
+	u16     subdevice;
+	u32     card;
+};
+
+static const struct dm1105_board dm1105_boards[] = {
+	[DM1105_BOARD_UNKNOWN] = {
+		.name		= "UNKNOWN/GENERIC",
+	},
+	[DM1105_BOARD_DVBWORLD_2002] = {
+		.name		= "DVBWorld PCI 2002",
+	},
+	[DM1105_BOARD_DVBWORLD_2004] = {
+		.name		= "DVBWorld PCI 2004",
+	},
+	[DM1105_BOARD_AXESS_DM05] = {
+		.name		= "Axess/EasyTv DM05",
+	},
+};
+
+static const struct dm1105_subid dm1105_subids[] = {
+	{
+		.subvendor = 0x0000,
+		.subdevice = 0x2002,
+		.card      = DM1105_BOARD_DVBWORLD_2002,
+	}, {
+		.subvendor = 0x0001,
+		.subdevice = 0x2002,
+		.card      = DM1105_BOARD_DVBWORLD_2002,
+	}, {
+		.subvendor = 0x0000,
+		.subdevice = 0x2004,
+		.card      = DM1105_BOARD_DVBWORLD_2004,
+	}, {
+		.subvendor = 0x0001,
+		.subdevice = 0x2004,
+		.card      = DM1105_BOARD_DVBWORLD_2004,
+	}, {
+		.subvendor = 0x195d,
+		.subdevice = 0x1105,
+		.card      = DM1105_BOARD_AXESS_DM05,
+	},
+};
+
+static void dm1105_card_list(struct pci_dev *pci)
+{
+	int i;
+
+	if (0 == pci->subsystem_vendor &&
+			0 == pci->subsystem_device) {
+		printk(KERN_ERR
+			"dm1105: Your board has no valid PCI Subsystem ID\n"
+			"dm1105: and thus can't be autodetected\n"
+			"dm1105: Please pass card=<n> insmod option to\n"
+			"dm1105: workaround that.  Redirect complaints to\n"
+			"dm1105: the vendor of the TV card.  Best regards,\n"
+			"dm1105: -- tux\n");
+	} else {
+		printk(KERN_ERR
+			"dm1105: Your board isn't known (yet) to the driver.\n"
+			"dm1105: You can try to pick one of the existing\n"
+			"dm1105: card configs via card=<n> insmod option.\n"
+			"dm1105: Updating to the latest version might help\n"
+			"dm1105: as well.\n");
+	}
+	printk(KERN_ERR "Here is a list of valid choices for the card=<n> "
+		   "insmod option:\n");
+	for (i = 0; i < ARRAY_SIZE(dm1105_boards); i++)
+		printk(KERN_ERR "dm1105:    card=%d -> %s\n",
+				i, dm1105_boards[i].name);
+}
+
 /* infrared remote control */
 struct infrared {
 	struct input_dev	*input_dev;
@@ -193,6 +286,8 @@ struct dm1105dvb {
 	struct dvb_frontend *fe;
 	struct dvb_net dvbnet;
 	unsigned int full_ts_users;
+	unsigned int boardnr;
+	int nr;
 
 	/* i2c */
 	struct i2c_adapter i2c_adap;
@@ -211,7 +306,6 @@ struct dm1105dvb {
 	unsigned int	PacketErrorCount;
 	unsigned int dmarst;
 	spinlock_t lock;
-
 };
 
 #define dm_io_mem(reg)	((unsigned long)(&dm1105dvb->io_mem[reg]))
@@ -326,16 +420,20 @@ static inline struct dm1105dvb *frontend_to_dm1105dvb(struct dvb_frontend *fe)
 static int dm1105dvb_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
 {
 	struct dm1105dvb *dm1105dvb = frontend_to_dm1105dvb(fe);
-	u32 lnb_mask, lnb_13v, lnb_18v;
+	u32 lnb_mask, lnb_13v, lnb_18v, lnb_off;
 
-	switch (dm1105dvb->pdev->subsystem_device) {
-	case PCI_DEVICE_ID_DM05:
+	switch (dm1105dvb->boardnr) {
+	case DM1105_BOARD_AXESS_DM05:
 		lnb_mask = DM05_LNB_MASK;
+		lnb_off = DM05_LNB_OFF;
 		lnb_13v = DM05_LNB_13V;
 		lnb_18v = DM05_LNB_18V;
 		break;
+	case DM1105_BOARD_DVBWORLD_2002:
+	case DM1105_BOARD_DVBWORLD_2004:
 	default:
 		lnb_mask = DM1105_LNB_MASK;
+		lnb_off = DM1105_LNB_OFF;
 		lnb_13v = DM1105_LNB_13V;
 		lnb_18v = DM1105_LNB_18V;
 	}
@@ -343,8 +441,10 @@ static int dm1105dvb_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volta
 	outl(lnb_mask, dm_io_mem(DM1105_GPIOCTR));
 	if (voltage == SEC_VOLTAGE_18)
 		outl(lnb_18v , dm_io_mem(DM1105_GPIOVAL));
-	else
+	else if (voltage == SEC_VOLTAGE_13)
 		outl(lnb_13v, dm_io_mem(DM1105_GPIOVAL));
+	else
+		outl(lnb_off, dm_io_mem(DM1105_GPIOVAL));
 
 	return 0;
 }
@@ -477,7 +577,7 @@ static irqreturn_t dm1105dvb_irq(int irq, void *dev_id)
 int __devinit dm1105_ir_init(struct dm1105dvb *dm1105)
 {
 	struct input_dev *input_dev;
-	IR_KEYTAB_TYPE *ir_codes = ir_codes_dm1105_nec;
+	struct ir_scancode_table *ir_codes = &ir_codes_dm1105_nec_table;
 	int ir_type = IR_TYPE_OTHER;
 	int err = -ENOMEM;
 
@@ -589,8 +689,8 @@ static int __devinit frontend_init(struct dm1105dvb *dm1105dvb)
 {
 	int ret;
 
-	switch (dm1105dvb->pdev->subsystem_device) {
-	case PCI_DEVICE_ID_DW2004:
+	switch (dm1105dvb->boardnr) {
+	case DM1105_BOARD_DVBWORLD_2004:
 		dm1105dvb->fe = dvb_attach(
 			cx24116_attach, &serit_sp2633_config,
 			&dm1105dvb->i2c_adap);
@@ -598,6 +698,8 @@ static int __devinit frontend_init(struct dm1105dvb *dm1105dvb)
 			dm1105dvb->fe->ops.set_voltage = dm1105dvb_set_voltage;
 
 		break;
+	case DM1105_BOARD_DVBWORLD_2002:
+	case DM1105_BOARD_AXESS_DM05:
 	default:
 		dm1105dvb->fe = dvb_attach(
 			stv0299_attach, &sharp_z0194a_config,
@@ -676,11 +778,31 @@ static int __devinit dm1105_probe(struct pci_dev *pdev,
 	struct dvb_demux *dvbdemux;
 	struct dmx_demux *dmx;
 	int ret = -ENOMEM;
+	int i;
 
 	dm1105dvb = kzalloc(sizeof(struct dm1105dvb), GFP_KERNEL);
 	if (!dm1105dvb)
 		return -ENOMEM;
 
+	/* board config */
+	dm1105dvb->nr = dm1105_devcount;
+	dm1105dvb->boardnr = UNSET;
+	if (card[dm1105dvb->nr] < ARRAY_SIZE(dm1105_boards))
+		dm1105dvb->boardnr = card[dm1105dvb->nr];
+	for (i = 0; UNSET == dm1105dvb->boardnr &&
+				i < ARRAY_SIZE(dm1105_subids); i++)
+		if (pdev->subsystem_vendor ==
+			dm1105_subids[i].subvendor &&
+				pdev->subsystem_device ==
+					dm1105_subids[i].subdevice)
+			dm1105dvb->boardnr = dm1105_subids[i].card;
+
+	if (UNSET == dm1105dvb->boardnr) {
+		dm1105dvb->boardnr = DM1105_BOARD_UNKNOWN;
+		dm1105_card_list(pdev);
+	}
+
+	dm1105_devcount++;
 	dm1105dvb->pdev = pdev;
 	dm1105dvb->buffer_size = 5 * DM1105_DMA_BYTES;
 	dm1105dvb->PacketErrorCount = 0;
@@ -853,6 +975,7 @@ static void __devexit dm1105_remove(struct pci_dev *pdev)
 	pci_release_regions(pdev);
 	pci_disable_device(pdev);
 	pci_set_drvdata(pdev, NULL);
+	dm1105_devcount--;
 	kfree(dm1105dvb);
 }
 
@@ -861,17 +984,12 @@ 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,
+		.subdevice = PCI_ANY_ID,
 	}, {
 		.vendor = PCI_VENDOR_ID_AXESS,
 		.device = PCI_DEVICE_ID_DM05,
-		.subvendor = PCI_VENDOR_ID_AXESS,
-		.subdevice = PCI_DEVICE_ID_DM05,
+		.subvendor = PCI_ANY_ID,
+		.subdevice = PCI_ANY_ID,
 	}, {
 		/* empty */
 	},

+ 174 - 57
drivers/media/dvb/dvb-core/dmxdev.c

@@ -430,6 +430,8 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
 /* stop feed but only mark the specified filter as stopped (state set) */
 static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter)
 {
+	struct dmxdev_feed *feed;
+
 	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
 
 	switch (dmxdevfilter->type) {
@@ -438,7 +440,8 @@ static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter)
 		dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec);
 		break;
 	case DMXDEV_TYPE_PES:
-		dmxdevfilter->feed.ts->stop_filtering(dmxdevfilter->feed.ts);
+		list_for_each_entry(feed, &dmxdevfilter->feed.ts, next)
+			feed->ts->stop_filtering(feed->ts);
 		break;
 	default:
 		return -EINVAL;
@@ -449,13 +452,23 @@ static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter)
 /* start feed associated with the specified filter */
 static int dvb_dmxdev_feed_start(struct dmxdev_filter *filter)
 {
+	struct dmxdev_feed *feed;
+	int ret;
+
 	dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO);
 
 	switch (filter->type) {
 	case DMXDEV_TYPE_SEC:
 		return filter->feed.sec->start_filtering(filter->feed.sec);
 	case DMXDEV_TYPE_PES:
-		return filter->feed.ts->start_filtering(filter->feed.ts);
+		list_for_each_entry(feed, &filter->feed.ts, next) {
+			ret = feed->ts->start_filtering(feed->ts);
+			if (ret < 0) {
+				dvb_dmxdev_feed_stop(filter);
+				return ret;
+			}
+		}
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -487,6 +500,9 @@ static int dvb_dmxdev_feed_restart(struct dmxdev_filter *filter)
 
 static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter)
 {
+	struct dmxdev_feed *feed;
+	struct dmx_demux *demux;
+
 	if (dmxdevfilter->state < DMXDEV_STATE_GO)
 		return 0;
 
@@ -503,13 +519,12 @@ static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter)
 		dmxdevfilter->feed.sec = NULL;
 		break;
 	case DMXDEV_TYPE_PES:
-		if (!dmxdevfilter->feed.ts)
-			break;
 		dvb_dmxdev_feed_stop(dmxdevfilter);
-		dmxdevfilter->dev->demux->
-		    release_ts_feed(dmxdevfilter->dev->demux,
-				    dmxdevfilter->feed.ts);
-		dmxdevfilter->feed.ts = NULL;
+		demux = dmxdevfilter->dev->demux;
+		list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) {
+			demux->release_ts_feed(demux, feed->ts);
+			feed->ts = NULL;
+		}
 		break;
 	default:
 		if (dmxdevfilter->state == DMXDEV_STATE_ALLOCATED)
@@ -521,19 +536,88 @@ static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter)
 	return 0;
 }
 
+static void dvb_dmxdev_delete_pids(struct dmxdev_filter *dmxdevfilter)
+{
+	struct dmxdev_feed *feed, *tmp;
+
+	/* delete all PIDs */
+	list_for_each_entry_safe(feed, tmp, &dmxdevfilter->feed.ts, next) {
+		list_del(&feed->next);
+		kfree(feed);
+	}
+
+	BUG_ON(!list_empty(&dmxdevfilter->feed.ts));
+}
+
 static inline int dvb_dmxdev_filter_reset(struct dmxdev_filter *dmxdevfilter)
 {
 	if (dmxdevfilter->state < DMXDEV_STATE_SET)
 		return 0;
 
+	if (dmxdevfilter->type == DMXDEV_TYPE_PES)
+		dvb_dmxdev_delete_pids(dmxdevfilter);
+
 	dmxdevfilter->type = DMXDEV_TYPE_NONE;
 	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
 	return 0;
 }
 
+static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev,
+				 struct dmxdev_filter *filter,
+				 struct dmxdev_feed *feed)
+{
+	struct timespec timeout = { 0 };
+	struct dmx_pes_filter_params *para = &filter->params.pes;
+	dmx_output_t otype;
+	int ret;
+	int ts_type;
+	enum dmx_ts_pes ts_pes;
+	struct dmx_ts_feed *tsfeed;
+
+	feed->ts = NULL;
+	otype = para->output;
+
+	ts_pes = (enum dmx_ts_pes)para->pes_type;
+
+	if (ts_pes < DMX_PES_OTHER)
+		ts_type = TS_DECODER;
+	else
+		ts_type = 0;
+
+	if (otype == DMX_OUT_TS_TAP)
+		ts_type |= TS_PACKET;
+	else if (otype == DMX_OUT_TSDEMUX_TAP)
+		ts_type |= TS_PACKET | TS_DEMUX;
+	else if (otype == DMX_OUT_TAP)
+		ts_type |= TS_PACKET | TS_DEMUX | TS_PAYLOAD_ONLY;
+
+	ret = dmxdev->demux->allocate_ts_feed(dmxdev->demux, &feed->ts,
+					      dvb_dmxdev_ts_callback);
+	if (ret < 0)
+		return ret;
+
+	tsfeed = feed->ts;
+	tsfeed->priv = filter;
+
+	ret = tsfeed->set(tsfeed, feed->pid, ts_type, ts_pes, 32768, timeout);
+	if (ret < 0) {
+		dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
+		return ret;
+	}
+
+	ret = tsfeed->start_filtering(tsfeed);
+	if (ret < 0) {
+		dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
+		return ret;
+	}
+
+	return 0;
+}
+
 static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
 {
 	struct dmxdev *dmxdev = filter->dev;
+	struct dmxdev_feed *feed;
 	void *mem;
 	int ret, i;
 
@@ -631,56 +715,14 @@ static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
 		break;
 	}
 	case DMXDEV_TYPE_PES:
-	{
-		struct timespec timeout = { 0 };
-		struct dmx_pes_filter_params *para = &filter->params.pes;
-		dmx_output_t otype;
-		int ts_type;
-		enum dmx_ts_pes ts_pes;
-		struct dmx_ts_feed **tsfeed = &filter->feed.ts;
-
-		filter->feed.ts = NULL;
-		otype = para->output;
-
-		ts_pes = (enum dmx_ts_pes)para->pes_type;
-
-		if (ts_pes < DMX_PES_OTHER)
-			ts_type = TS_DECODER;
-		else
-			ts_type = 0;
-
-		if (otype == DMX_OUT_TS_TAP)
-			ts_type |= TS_PACKET;
-		else if (otype == DMX_OUT_TSDEMUX_TAP)
-			ts_type |= TS_PACKET | TS_DEMUX;
-		else if (otype == DMX_OUT_TAP)
-			ts_type |= TS_PACKET | TS_DEMUX | TS_PAYLOAD_ONLY;
-
-		ret = dmxdev->demux->allocate_ts_feed(dmxdev->demux,
-						      tsfeed,
-						      dvb_dmxdev_ts_callback);
-		if (ret < 0)
-			return ret;
-
-		(*tsfeed)->priv = filter;
-
-		ret = (*tsfeed)->set(*tsfeed, para->pid, ts_type, ts_pes,
-				     32768, timeout);
-		if (ret < 0) {
-			dmxdev->demux->release_ts_feed(dmxdev->demux,
-						       *tsfeed);
-			return ret;
-		}
-
-		ret = filter->feed.ts->start_filtering(filter->feed.ts);
-		if (ret < 0) {
-			dmxdev->demux->release_ts_feed(dmxdev->demux,
-						       *tsfeed);
-			return ret;
+		list_for_each_entry(feed, &filter->feed.ts, next) {
+			ret = dvb_dmxdev_start_feed(dmxdev, filter, feed);
+			if (ret < 0) {
+				dvb_dmxdev_filter_stop(filter);
+				return ret;
+			}
 		}
-
 		break;
-	}
 	default:
 		return -EINVAL;
 	}
@@ -718,7 +760,7 @@ static int dvb_demux_open(struct inode *inode, struct file *file)
 	dvb_ringbuffer_init(&dmxdevfilter->buffer, NULL, 8192);
 	dmxdevfilter->type = DMXDEV_TYPE_NONE;
 	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
-	dmxdevfilter->feed.ts = NULL;
+	INIT_LIST_HEAD(&dmxdevfilter->feed.ts);
 	init_timer(&dmxdevfilter->timer);
 
 	dvbdev->users++;
@@ -760,6 +802,55 @@ static inline void invert_mode(dmx_filter_t *filter)
 		filter->mode[i] ^= 0xff;
 }
 
+static int dvb_dmxdev_add_pid(struct dmxdev *dmxdev,
+			      struct dmxdev_filter *filter, u16 pid)
+{
+	struct dmxdev_feed *feed;
+
+	if ((filter->type != DMXDEV_TYPE_PES) ||
+	    (filter->state < DMXDEV_STATE_SET))
+		return -EINVAL;
+
+	/* only TS packet filters may have multiple PIDs */
+	if ((filter->params.pes.output != DMX_OUT_TSDEMUX_TAP) &&
+	    (!list_empty(&filter->feed.ts)))
+		return -EINVAL;
+
+	feed = kzalloc(sizeof(struct dmxdev_feed), GFP_KERNEL);
+	if (feed == NULL)
+		return -ENOMEM;
+
+	feed->pid = pid;
+	list_add(&feed->next, &filter->feed.ts);
+
+	if (filter->state >= DMXDEV_STATE_GO)
+		return dvb_dmxdev_start_feed(dmxdev, filter, feed);
+
+	return 0;
+}
+
+static int dvb_dmxdev_remove_pid(struct dmxdev *dmxdev,
+				  struct dmxdev_filter *filter, u16 pid)
+{
+	struct dmxdev_feed *feed, *tmp;
+
+	if ((filter->type != DMXDEV_TYPE_PES) ||
+	    (filter->state < DMXDEV_STATE_SET))
+		return -EINVAL;
+
+	list_for_each_entry_safe(feed, tmp, &filter->feed.ts, next) {
+		if ((feed->pid == pid) && (feed->ts != NULL)) {
+			feed->ts->stop_filtering(feed->ts);
+			filter->dev->demux->release_ts_feed(filter->dev->demux,
+							    feed->ts);
+			list_del(&feed->next);
+			kfree(feed);
+		}
+	}
+
+	return 0;
+}
+
 static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev,
 				 struct dmxdev_filter *dmxdevfilter,
 				 struct dmx_sct_filter_params *params)
@@ -784,7 +875,10 @@ static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev,
 				     struct dmxdev_filter *dmxdevfilter,
 				     struct dmx_pes_filter_params *params)
 {
+	int ret;
+
 	dvb_dmxdev_filter_stop(dmxdevfilter);
+	dvb_dmxdev_filter_reset(dmxdevfilter);
 
 	if (params->pes_type > DMX_PES_OTHER || params->pes_type < 0)
 		return -EINVAL;
@@ -795,6 +889,11 @@ static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev,
 
 	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
 
+	ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter,
+				 dmxdevfilter->params.pes.pid);
+	if (ret < 0)
+		return ret;
+
 	if (params->flags & DMX_IMMEDIATE_START)
 		return dvb_dmxdev_filter_start(dmxdevfilter);
 
@@ -958,6 +1057,24 @@ static int dvb_demux_do_ioctl(struct inode *inode, struct file *file,
 					     &((struct dmx_stc *)parg)->base);
 		break;
 
+	case DMX_ADD_PID:
+		if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+			ret = -ERESTARTSYS;
+			break;
+		}
+		ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter, *(u16 *)parg);
+		mutex_unlock(&dmxdevfilter->mutex);
+		break;
+
+	case DMX_REMOVE_PID:
+		if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+			ret = -ERESTARTSYS;
+			break;
+		}
+		ret = dvb_dmxdev_remove_pid(dmxdev, dmxdevfilter, *(u16 *)parg);
+		mutex_unlock(&dmxdevfilter->mutex);
+		break;
+
 	default:
 		ret = -EINVAL;
 		break;

+ 8 - 1
drivers/media/dvb/dvb-core/dmxdev.h

@@ -53,13 +53,20 @@ enum dmxdev_state {
 	DMXDEV_STATE_TIMEDOUT
 };
 
+struct dmxdev_feed {
+	u16 pid;
+	struct dmx_ts_feed *ts;
+	struct list_head next;
+};
+
 struct dmxdev_filter {
 	union {
 		struct dmx_section_filter *sec;
 	} filter;
 
 	union {
-		struct dmx_ts_feed *ts;
+		/* list of TS and PES feeds (struct dmxdev_feed) */
+		struct list_head ts;
 		struct dmx_section_feed *sec;
 	} feed;
 

+ 2 - 6
drivers/media/dvb/dvb-core/dvb_demux.c

@@ -425,13 +425,9 @@ no_dvb_demux_tscheck:
 		if ((DVR_FEED(feed)) && (dvr_done++))
 			continue;
 
-		if (feed->pid == pid) {
+		if (feed->pid == pid)
 			dvb_dmx_swfilter_packet_type(feed, buf);
-			if (DVR_FEED(feed))
-				continue;
-		}
-
-		if (feed->pid == 0x2000)
+		else if (feed->pid == 0x2000)
 			feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
 	}
 }

+ 30 - 5
drivers/media/dvb/dvb-core/dvb_frontend.c

@@ -72,6 +72,7 @@ MODULE_PARM_DESC(dvb_mfe_wait_time, "Wait up to <mfe_wait_time> seconds on open(
 #define FESTATE_ZIGZAG_FAST 32
 #define FESTATE_ZIGZAG_SLOW 64
 #define FESTATE_DISEQC 128
+#define FESTATE_ERROR 256
 #define FESTATE_WAITFORLOCK (FESTATE_TUNING_FAST | FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW | FESTATE_DISEQC)
 #define FESTATE_SEARCHING_FAST (FESTATE_TUNING_FAST | FESTATE_ZIGZAG_FAST)
 #define FESTATE_SEARCHING_SLOW (FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_SLOW)
@@ -269,6 +270,7 @@ static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wra
 {
 	int autoinversion;
 	int ready = 0;
+	int fe_set_err = 0;
 	struct dvb_frontend_private *fepriv = fe->frontend_priv;
 	int original_inversion = fepriv->parameters.inversion;
 	u32 original_frequency = fepriv->parameters.frequency;
@@ -345,7 +347,11 @@ static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wra
 	if (autoinversion)
 		fepriv->parameters.inversion = fepriv->inversion;
 	if (fe->ops.set_frontend)
-		fe->ops.set_frontend(fe, &fepriv->parameters);
+		fe_set_err = fe->ops.set_frontend(fe, &fepriv->parameters);
+	if (fe_set_err < 0) {
+		fepriv->state = FESTATE_ERROR;
+		return fe_set_err;
+	}
 
 	fepriv->parameters.frequency = original_frequency;
 	fepriv->parameters.inversion = original_inversion;
@@ -357,6 +363,7 @@ static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wra
 static void dvb_frontend_swzigzag(struct dvb_frontend *fe)
 {
 	fe_status_t s = 0;
+	int retval = 0;
 	struct dvb_frontend_private *fepriv = fe->frontend_priv;
 
 	/* if we've got no parameters, just keep idling */
@@ -370,8 +377,12 @@ static void dvb_frontend_swzigzag(struct dvb_frontend *fe)
 	if (fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT) {
 		if (fepriv->state & FESTATE_RETUNE) {
 			if (fe->ops.set_frontend)
-				fe->ops.set_frontend(fe, &fepriv->parameters);
-			fepriv->state = FESTATE_TUNED;
+				retval = fe->ops.set_frontend(fe,
+							&fepriv->parameters);
+			if (retval < 0)
+				fepriv->state = FESTATE_ERROR;
+			else
+				fepriv->state = FESTATE_TUNED;
 		}
 		fepriv->delay = 3*HZ;
 		fepriv->quality = 0;
@@ -449,7 +460,11 @@ static void dvb_frontend_swzigzag(struct dvb_frontend *fe)
 		fepriv->delay = fepriv->min_delay;
 
 		/* peform a tune */
-		if (dvb_frontend_swzigzag_autotune(fe, fepriv->check_wrapped)) {
+		retval = dvb_frontend_swzigzag_autotune(fe,
+							fepriv->check_wrapped);
+		if (retval < 0) {
+			return;
+		} else if (retval) {
 			/* OK, if we've run out of trials at the fast speed.
 			 * Drop back to slow for the _next_ attempt */
 			fepriv->state = FESTATE_SEARCHING_SLOW;
@@ -823,6 +838,15 @@ static int dvb_frontend_check_parameters(struct dvb_frontend *fe,
 		}
 	}
 
+	/* check for supported modulation */
+	if (fe->ops.info.type == FE_QAM &&
+	    (parms->u.qam.modulation > QAM_AUTO ||
+	     !((1 << (parms->u.qam.modulation + 10)) & fe->ops.info.caps))) {
+		printk(KERN_WARNING "DVB: adapter %i frontend %i modulation %u not supported\n",
+		       fe->dvb->num, fe->id, parms->u.qam.modulation);
+			return -EINVAL;
+	}
+
 	return 0;
 }
 
@@ -1499,7 +1523,8 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file,
 
 		/* if retune was requested but hasn't occured yet, prevent
 		 * that user get signal state from previous tuning */
-		if(fepriv->state == FESTATE_RETUNE) {
+		if (fepriv->state == FESTATE_RETUNE ||
+		    fepriv->state == FESTATE_ERROR) {
 			err=0;
 			*status = 0;
 			break;

+ 5 - 0
drivers/media/dvb/dvb-core/dvbdev.h

@@ -30,7 +30,12 @@
 
 #define DVB_MAJOR 212
 
+#if defined(CONFIG_DVB_MAX_ADAPTERS) && CONFIG_DVB_MAX_ADAPTERS > 0
+#define DVB_MAX_ADAPTERS CONFIG_DVB_MAX_ADAPTERS
+#else
+#warning invalid CONFIG_DVB_MAX_ADAPTERS value
 #define DVB_MAX_ADAPTERS 8
+#endif
 
 #define DVB_UNSET (-1)
 

+ 4 - 2
drivers/media/dvb/dvb-usb/Kconfig

@@ -253,7 +253,7 @@ config DVB_USB_AF9005_REMOTE
 	  Afatech AF9005 based receiver.
 
 config DVB_USB_DW2102
-	tristate "DvbWorld DVB-S/S2 USB2.0 support"
+	tristate "DvbWorld & TeVii DVB-S/S2 USB2.0 support"
 	depends on DVB_USB
 	select DVB_PLL if !DVB_FE_CUSTOMISE
 	select DVB_STV0299 if !DVB_FE_CUSTOMISE
@@ -262,9 +262,11 @@ config DVB_USB_DW2102
 	select DVB_CX24116 if !DVB_FE_CUSTOMISE
 	select DVB_SI21XX if !DVB_FE_CUSTOMISE
 	select DVB_TDA10021 if !DVB_FE_CUSTOMISE
+	select DVB_MT312 if !DVB_FE_CUSTOMISE
+	select DVB_ZL10039 if !DVB_FE_CUSTOMISE
 	help
 	  Say Y here to support the DvbWorld DVB-S/S2 USB2.0 receivers
-	  and the TeVii S650.
+	  and the TeVii S650, S630.
 
 config DVB_USB_CINERGY_T2
 	tristate "Terratec CinergyT2/qanu USB 2.0 DVB-T receiver"

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

@@ -38,41 +38,41 @@ static int a800_identify_state(struct usb_device *udev, struct dvb_usb_device_pr
 }
 
 static struct dvb_usb_rc_key a800_rc_keys[] = {
-	{ 0x02, 0x01, KEY_PROG1 },       /* SOURCE */
-	{ 0x02, 0x00, KEY_POWER },       /* POWER */
-	{ 0x02, 0x05, KEY_1 },           /* 1 */
-	{ 0x02, 0x06, KEY_2 },           /* 2 */
-	{ 0x02, 0x07, KEY_3 },           /* 3 */
-	{ 0x02, 0x09, KEY_4 },           /* 4 */
-	{ 0x02, 0x0a, KEY_5 },           /* 5 */
-	{ 0x02, 0x0b, KEY_6 },           /* 6 */
-	{ 0x02, 0x0d, KEY_7 },           /* 7 */
-	{ 0x02, 0x0e, KEY_8 },           /* 8 */
-	{ 0x02, 0x0f, KEY_9 },           /* 9 */
-	{ 0x02, 0x12, KEY_LEFT },        /* L / DISPLAY */
-	{ 0x02, 0x11, KEY_0 },           /* 0 */
-	{ 0x02, 0x13, KEY_RIGHT },       /* R / CH RTN */
-	{ 0x02, 0x17, KEY_PROG2 },       /* SNAP SHOT */
-	{ 0x02, 0x10, KEY_PROG3 },       /* 16-CH PREV */
-	{ 0x02, 0x1e, KEY_VOLUMEDOWN },  /* VOL DOWN */
-	{ 0x02, 0x0c, KEY_ZOOM },        /* FULL SCREEN */
-	{ 0x02, 0x1f, KEY_VOLUMEUP },    /* VOL UP */
-	{ 0x02, 0x14, KEY_MUTE },        /* MUTE */
-	{ 0x02, 0x08, KEY_AUDIO },       /* AUDIO */
-	{ 0x02, 0x19, KEY_RECORD },      /* RECORD */
-	{ 0x02, 0x18, KEY_PLAY },        /* PLAY */
-	{ 0x02, 0x1b, KEY_STOP },        /* STOP */
-	{ 0x02, 0x1a, KEY_PLAYPAUSE },   /* TIMESHIFT / PAUSE */
-	{ 0x02, 0x1d, KEY_BACK },        /* << / RED */
-	{ 0x02, 0x1c, KEY_FORWARD },     /* >> / YELLOW */
-	{ 0x02, 0x03, KEY_TEXT },        /* TELETEXT */
-	{ 0x02, 0x04, KEY_EPG },         /* EPG */
-	{ 0x02, 0x15, KEY_MENU },        /* MENU */
-
-	{ 0x03, 0x03, KEY_CHANNELUP },   /* CH UP */
-	{ 0x03, 0x02, KEY_CHANNELDOWN }, /* CH DOWN */
-	{ 0x03, 0x01, KEY_FIRST },       /* |<< / GREEN */
-	{ 0x03, 0x00, KEY_LAST },        /* >>| / BLUE */
+	{ 0x0201, KEY_PROG1 },       /* SOURCE */
+	{ 0x0200, KEY_POWER },       /* POWER */
+	{ 0x0205, KEY_1 },           /* 1 */
+	{ 0x0206, KEY_2 },           /* 2 */
+	{ 0x0207, KEY_3 },           /* 3 */
+	{ 0x0209, KEY_4 },           /* 4 */
+	{ 0x020a, KEY_5 },           /* 5 */
+	{ 0x020b, KEY_6 },           /* 6 */
+	{ 0x020d, KEY_7 },           /* 7 */
+	{ 0x020e, KEY_8 },           /* 8 */
+	{ 0x020f, KEY_9 },           /* 9 */
+	{ 0x0212, KEY_LEFT },        /* L / DISPLAY */
+	{ 0x0211, KEY_0 },           /* 0 */
+	{ 0x0213, KEY_RIGHT },       /* R / CH RTN */
+	{ 0x0217, KEY_PROG2 },       /* SNAP SHOT */
+	{ 0x0210, KEY_PROG3 },       /* 16-CH PREV */
+	{ 0x021e, KEY_VOLUMEDOWN },  /* VOL DOWN */
+	{ 0x020c, KEY_ZOOM },        /* FULL SCREEN */
+	{ 0x021f, KEY_VOLUMEUP },    /* VOL UP */
+	{ 0x0214, KEY_MUTE },        /* MUTE */
+	{ 0x0208, KEY_AUDIO },       /* AUDIO */
+	{ 0x0219, KEY_RECORD },      /* RECORD */
+	{ 0x0218, KEY_PLAY },        /* PLAY */
+	{ 0x021b, KEY_STOP },        /* STOP */
+	{ 0x021a, KEY_PLAYPAUSE },   /* TIMESHIFT / PAUSE */
+	{ 0x021d, KEY_BACK },        /* << / RED */
+	{ 0x021c, KEY_FORWARD },     /* >> / YELLOW */
+	{ 0x0203, KEY_TEXT },        /* TELETEXT */
+	{ 0x0204, KEY_EPG },         /* EPG */
+	{ 0x0215, KEY_MENU },        /* MENU */
+
+	{ 0x0303, KEY_CHANNELUP },   /* CH UP */
+	{ 0x0302, KEY_CHANNELDOWN }, /* CH DOWN */
+	{ 0x0301, KEY_FIRST },       /* |<< / GREEN */
+	{ 0x0300, KEY_LAST },        /* >>| / BLUE */
 
 };
 

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

@@ -35,43 +35,43 @@ MODULE_PARM_DESC(debug,
 
 struct dvb_usb_rc_key af9005_rc_keys[] = {
 
-	{0x01, 0xb7, KEY_POWER},
-	{0x01, 0xa7, KEY_VOLUMEUP},
-	{0x01, 0x87, KEY_CHANNELUP},
-	{0x01, 0x7f, KEY_MUTE},
-	{0x01, 0xbf, KEY_VOLUMEDOWN},
-	{0x01, 0x3f, KEY_CHANNELDOWN},
-	{0x01, 0xdf, KEY_1},
-	{0x01, 0x5f, KEY_2},
-	{0x01, 0x9f, KEY_3},
-	{0x01, 0x1f, KEY_4},
-	{0x01, 0xef, KEY_5},
-	{0x01, 0x6f, KEY_6},
-	{0x01, 0xaf, KEY_7},
-	{0x01, 0x27, KEY_8},
-	{0x01, 0x07, KEY_9},
-	{0x01, 0xcf, KEY_ZOOM},
-	{0x01, 0x4f, KEY_0},
-	{0x01, 0x8f, KEY_GOTO},	/* marked jump on the remote */
+	{0x01b7, KEY_POWER},
+	{0x01a7, KEY_VOLUMEUP},
+	{0x0187, KEY_CHANNELUP},
+	{0x017f, KEY_MUTE},
+	{0x01bf, KEY_VOLUMEDOWN},
+	{0x013f, KEY_CHANNELDOWN},
+	{0x01df, KEY_1},
+	{0x015f, KEY_2},
+	{0x019f, KEY_3},
+	{0x011f, KEY_4},
+	{0x01ef, KEY_5},
+	{0x016f, KEY_6},
+	{0x01af, KEY_7},
+	{0x0127, KEY_8},
+	{0x0107, KEY_9},
+	{0x01cf, KEY_ZOOM},
+	{0x014f, KEY_0},
+	{0x018f, KEY_GOTO},	/* marked jump on the remote */
 
-	{0x00, 0xbd, KEY_POWER},
-	{0x00, 0x7d, KEY_VOLUMEUP},
-	{0x00, 0xfd, KEY_CHANNELUP},
-	{0x00, 0x9d, KEY_MUTE},
-	{0x00, 0x5d, KEY_VOLUMEDOWN},
-	{0x00, 0xdd, KEY_CHANNELDOWN},
-	{0x00, 0xad, KEY_1},
-	{0x00, 0x6d, KEY_2},
-	{0x00, 0xed, KEY_3},
-	{0x00, 0x8d, KEY_4},
-	{0x00, 0x4d, KEY_5},
-	{0x00, 0xcd, KEY_6},
-	{0x00, 0xb5, KEY_7},
-	{0x00, 0x75, KEY_8},
-	{0x00, 0xf5, KEY_9},
-	{0x00, 0x95, KEY_ZOOM},
-	{0x00, 0x55, KEY_0},
-	{0x00, 0xd5, KEY_GOTO},	/* marked jump on the remote */
+	{0x00bd, KEY_POWER},
+	{0x007d, KEY_VOLUMEUP},
+	{0x00fd, KEY_CHANNELUP},
+	{0x009d, KEY_MUTE},
+	{0x005d, KEY_VOLUMEDOWN},
+	{0x00dd, KEY_CHANNELDOWN},
+	{0x00ad, KEY_1},
+	{0x006d, KEY_2},
+	{0x00ed, KEY_3},
+	{0x008d, KEY_4},
+	{0x004d, KEY_5},
+	{0x00cd, KEY_6},
+	{0x00b5, KEY_7},
+	{0x0075, KEY_8},
+	{0x00f5, KEY_9},
+	{0x0095, KEY_ZOOM},
+	{0x0055, KEY_0},
+	{0x00d5, KEY_GOTO},	/* marked jump on the remote */
 };
 
 int af9005_rc_keys_size = ARRAY_SIZE(af9005_rc_keys);
@@ -131,8 +131,8 @@ int af9005_rc_decode(struct dvb_usb_device *d, u8 * data, int len, u32 * event,
 				return 0;
 			}
 			for (i = 0; i < af9005_rc_keys_size; i++) {
-				if (af9005_rc_keys[i].custom == cust
-				    && af9005_rc_keys[i].data == dat) {
+				if (rc5_custom(&af9005_rc_keys[i]) == cust
+				    && rc5_data(&af9005_rc_keys[i]) == dat) {
 					*event = af9005_rc_keys[i].event;
 					*state = REMOTE_KEY_PRESSED;
 					deb_decode

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

@@ -538,24 +538,22 @@ exit:
 /* dump eeprom */
 static int af9015_eeprom_dump(struct dvb_usb_device *d)
 {
-	char buf[4+3*16+1], buf2[4];
 	u8 reg, val;
 
 	for (reg = 0; ; reg++) {
 		if (reg % 16 == 0) {
 			if (reg)
-				deb_info("%s\n", buf);
-			sprintf(buf, "%02x: ", reg);
+				deb_info(KERN_CONT "\n");
+			deb_info(KERN_DEBUG "%02x:", reg);
 		}
 		if (af9015_read_reg_i2c(d, AF9015_I2C_EEPROM, reg, &val) == 0)
-			sprintf(buf2, "%02x ", val);
+			deb_info(KERN_CONT " %02x", val);
 		else
-			strcpy(buf2, "-- ");
-		strcat(buf, buf2);
+			deb_info(KERN_CONT " --");
 		if (reg == 0xff)
 			break;
 	}
-	deb_info("%s\n", buf);
+	deb_info(KERN_CONT "\n");
 	return 0;
 }
 
@@ -1045,8 +1043,8 @@ static int af9015_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
 	*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]) {
+		if (!buf[1] && rc5_custom(&keymap[i]) == buf[0] &&
+		    rc5_data(&keymap[i]) == buf[2]) {
 			*event = keymap[i].event;
 			*state = REMOTE_KEY_PRESSED;
 			break;
@@ -1266,6 +1264,7 @@ static struct usb_device_id af9015_usb_table[] = {
 	{USB_DEVICE(USB_VID_KWORLD_2,  USB_PID_CONCEPTRONIC_CTVDIGRCU)},
 	{USB_DEVICE(USB_VID_KWORLD_2,  USB_PID_KWORLD_MC810)},
 	{USB_DEVICE(USB_VID_KYE,       USB_PID_GENIUS_TVGO_DVB_T03)},
+/* 25 */{USB_DEVICE(USB_VID_KWORLD_2,  USB_PID_KWORLD_399U_2)},
 	{0},
 };
 MODULE_DEVICE_TABLE(usb, af9015_usb_table);
@@ -1346,7 +1345,8 @@ static struct dvb_usb_device_properties af9015_properties[] = {
 			{
 				.name = "KWorld PlusTV Dual DVB-T Stick " \
 					"(DVB-T 399U)",
-				.cold_ids = {&af9015_usb_table[4], NULL},
+				.cold_ids = {&af9015_usb_table[4],
+					     &af9015_usb_table[25], NULL},
 				.warm_ids = {NULL},
 			},
 			{

+ 230 - 230
drivers/media/dvb/dvb-usb/af9015.h

@@ -121,21 +121,21 @@ enum af9015_remote {
 
 /* 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 },
+	{ 0x001e, KEY_1 },
+	{ 0x001f, KEY_2 },
+	{ 0x0020, KEY_3 },
+	{ 0x0021, KEY_4 },
+	{ 0x0022, KEY_5 },
+	{ 0x0023, KEY_6 },
+	{ 0x0024, KEY_7 },
+	{ 0x0025, KEY_8 },
+	{ 0x0026, KEY_9 },
+	{ 0x0027, KEY_0 },
+	{ 0x0028, KEY_ENTER },
+	{ 0x004f, KEY_VOLUMEUP },
+	{ 0x0050, KEY_VOLUMEDOWN },
+	{ 0x0051, KEY_CHANNELDOWN },
+	{ 0x0052, KEY_CHANNELUP },
 };
 
 static u8 af9015_ir_table_leadtek[] = {
@@ -193,60 +193,60 @@ static u8 af9015_ir_table_leadtek[] = {
 
 /* 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 },
+	{ 0x053f, KEY_POWER },
+	{ 0x0019, KEY_FAVORITES },    /* Favorite List */
+	{ 0x0004, KEY_TEXT },         /* Teletext */
+	{ 0x000e, KEY_POWER },
+	{ 0x000e, KEY_INFO },         /* Preview */
+	{ 0x0008, KEY_EPG },          /* Info/EPG */
+	{ 0x000f, KEY_LIST },         /* Record List */
+	{ 0x001e, KEY_1 },
+	{ 0x001f, KEY_2 },
+	{ 0x0020, KEY_3 },
+	{ 0x0021, KEY_4 },
+	{ 0x0022, KEY_5 },
+	{ 0x0023, KEY_6 },
+	{ 0x0024, KEY_7 },
+	{ 0x0025, KEY_8 },
+	{ 0x0026, KEY_9 },
+	{ 0x0027, KEY_0 },
+	{ 0x0029, KEY_CANCEL },       /* Cancel */
+	{ 0x004c, KEY_CLEAR },        /* Clear */
+	{ 0x002a, KEY_BACK },         /* Back */
+	{ 0x002b, KEY_TAB },          /* Tab */
+	{ 0x0052, KEY_UP },           /* up arrow */
+	{ 0x0051, KEY_DOWN },         /* down arrow */
+	{ 0x004f, KEY_RIGHT },        /* right arrow */
+	{ 0x0050, KEY_LEFT },         /* left arrow */
+	{ 0x0028, KEY_ENTER },        /* Enter / ok */
+	{ 0x0252, KEY_VOLUMEUP },
+	{ 0x0251, KEY_VOLUMEDOWN },
+	{ 0x004e, KEY_CHANNELDOWN },
+	{ 0x004b, KEY_CHANNELUP },
+	{ 0x004a, KEY_RECORD },
+	{ 0x0111, KEY_PLAY },
+	{ 0x0017, KEY_PAUSE },
+	{ 0x000c, KEY_REWIND },       /* FR << */
+	{ 0x0011, KEY_FASTFORWARD },  /* FF >> */
+	{ 0x0115, KEY_PREVIOUS },     /* Replay */
+	{ 0x010e, KEY_NEXT },         /* Skip */
+	{ 0x0013, KEY_CAMERA },       /* Capture */
+	{ 0x010f, KEY_LANGUAGE },     /* SAP */
+	{ 0x0113, KEY_TV2 },          /* PIP */
+	{ 0x001d, KEY_ZOOM },         /* Full Screen */
+	{ 0x0117, KEY_SUBTITLE },     /* Subtitle / CC */
+	{ 0x0010, KEY_MUTE },
+	{ 0x0119, KEY_AUDIO },        /* L/R */ /* TODO better event */
+	{ 0x0116, KEY_SLEEP },        /* Hibernate */
+	{ 0x0116, 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 },
+	{ 0x0006, KEY_AGAIN },        /* Recall */
+	{ 0x0116, KEY_KPPLUS },       /* Zoom+ */ /* TODO does not work */
+	{ 0x0116, KEY_KPMINUS },      /* Zoom- */ /* TODO does not work */
+	{ 0x0215, KEY_RED },
+	{ 0x020a, KEY_GREEN },
+	{ 0x021c, KEY_YELLOW },
+	{ 0x0205, KEY_BLUE },
 };
 
 static u8 af9015_ir_table_twinhan[] = {
@@ -304,24 +304,24 @@ static u8 af9015_ir_table_twinhan[] = {
 
 /* 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 },
+	{ 0x001e, KEY_1 },
+	{ 0x001f, KEY_2 },
+	{ 0x0020, KEY_3 },
+	{ 0x0021, KEY_4 },
+	{ 0x0022, KEY_5 },
+	{ 0x0023, KEY_6 },
+	{ 0x0024, KEY_7 },
+	{ 0x0025, KEY_8 },
+	{ 0x0026, KEY_9 },
+	{ 0x0027, KEY_0 },
+	{ 0x002e, KEY_CHANNELUP },
+	{ 0x002d, KEY_CHANNELDOWN },
+	{ 0x0428, KEY_ZOOM },
+	{ 0x0041, KEY_MUTE },
+	{ 0x0042, KEY_VOLUMEDOWN },
+	{ 0x0043, KEY_VOLUMEUP },
+	{ 0x0044, KEY_GOTO },         /* jump */
+	{ 0x0545, KEY_POWER },
 };
 
 static u8 af9015_ir_table_a_link[] = {
@@ -347,24 +347,24 @@ static u8 af9015_ir_table_a_link[] = {
 
 /* 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 },
+	{ 0x001e, KEY_1 },
+	{ 0x001f, KEY_2 },
+	{ 0x0020, KEY_3 },
+	{ 0x0021, KEY_4 },
+	{ 0x0022, KEY_5 },
+	{ 0x0023, KEY_6 },
+	{ 0x0024, KEY_7 },
+	{ 0x0025, KEY_8 },
+	{ 0x0026, KEY_9 },
+	{ 0x0027, KEY_0 },
+	{ 0x030f, KEY_CHANNELUP },
+	{ 0x030e, KEY_CHANNELDOWN },
+	{ 0x0042, KEY_VOLUMEDOWN },
+	{ 0x0043, KEY_VOLUMEUP },
+	{ 0x0545, KEY_POWER },
+	{ 0x0052, KEY_UP },           /* up */
+	{ 0x0051, KEY_DOWN },         /* down */
+	{ 0x0028, KEY_ENTER },
 };
 
 static u8 af9015_ir_table_msi[] = {
@@ -390,42 +390,42 @@ static u8 af9015_ir_table_msi[] = {
 
 /* MYGICTV U718 */
 static struct dvb_usb_rc_key af9015_rc_keys_mygictv[] = {
-	{ 0x00, 0x3d, KEY_SWITCHVIDEOMODE },
+	{ 0x003d, 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 },
+	{ 0x0545, KEY_POWER },
+	{ 0x001e, KEY_1 },
+	{ 0x001f, KEY_2 },
+	{ 0x0020, KEY_3 },
+	{ 0x0021, KEY_4 },
+	{ 0x0022, KEY_5 },
+	{ 0x0023, KEY_6 },
+	{ 0x0024, KEY_7 },
+	{ 0x0025, KEY_8 },
+	{ 0x0026, KEY_9 },
+	{ 0x0027, KEY_0 },
+	{ 0x0041, KEY_MUTE },
+	{ 0x002a, KEY_ESC },          /* Esc */
+	{ 0x002e, KEY_CHANNELUP },
+	{ 0x002d, KEY_CHANNELDOWN },
+	{ 0x0042, KEY_VOLUMEDOWN },
+	{ 0x0043, KEY_VOLUMEUP },
+	{ 0x0052, KEY_UP },           /* up arrow */
+	{ 0x0051, KEY_DOWN },         /* down arrow */
+	{ 0x004f, KEY_RIGHT },        /* right arrow */
+	{ 0x0050, KEY_LEFT },         /* left arrow */
+	{ 0x0028, KEY_ENTER },        /* ok */
+	{ 0x0115, KEY_RECORD },
+	{ 0x0313, KEY_PLAY },
+	{ 0x0113, KEY_PAUSE },
+	{ 0x0116, KEY_STOP },
+	{ 0x0307, KEY_REWIND },       /* FR << */
+	{ 0x0309, KEY_FASTFORWARD },  /* FF >> */
+	{ 0x003b, KEY_TIME },         /* TimeShift */
+	{ 0x003e, KEY_CAMERA },       /* Snapshot */
+	{ 0x0316, KEY_CYCLEWINDOWS }, /* yellow, min / max */
+	{ 0x0000, KEY_ZOOM },         /* 'select' (?) */
+	{ 0x0316, KEY_SHUFFLE },      /* Shuffle */
+	{ 0x0345, KEY_POWER },
 };
 
 static u8 af9015_ir_table_mygictv[] = {
@@ -516,41 +516,41 @@ static u8 af9015_ir_table_kworld[] = {
 
 /* AverMedia Volar X */
 static struct dvb_usb_rc_key af9015_rc_keys_avermedia[] = {
-	{ 0x05, 0x3d, KEY_PROG1 },       /* SOURCE */
-	{ 0x05, 0x12, KEY_POWER },       /* POWER */
-	{ 0x05, 0x1e, KEY_1 },           /* 1 */
-	{ 0x05, 0x1f, KEY_2 },           /* 2 */
-	{ 0x05, 0x20, KEY_3 },           /* 3 */
-	{ 0x05, 0x21, KEY_4 },           /* 4 */
-	{ 0x05, 0x22, KEY_5 },           /* 5 */
-	{ 0x05, 0x23, KEY_6 },           /* 6 */
-	{ 0x05, 0x24, KEY_7 },           /* 7 */
-	{ 0x05, 0x25, KEY_8 },           /* 8 */
-	{ 0x05, 0x26, KEY_9 },           /* 9 */
-	{ 0x05, 0x3f, KEY_LEFT },        /* L / DISPLAY */
-	{ 0x05, 0x27, KEY_0 },           /* 0 */
-	{ 0x05, 0x0f, KEY_RIGHT },       /* R / CH RTN */
-	{ 0x05, 0x18, KEY_PROG2 },       /* SNAP SHOT */
-	{ 0x05, 0x1c, KEY_PROG3 },       /* 16-CH PREV */
-	{ 0x05, 0x2d, KEY_VOLUMEDOWN },  /* VOL DOWN */
-	{ 0x05, 0x3e, KEY_ZOOM },        /* FULL SCREEN */
-	{ 0x05, 0x2e, KEY_VOLUMEUP },    /* VOL UP */
-	{ 0x05, 0x10, KEY_MUTE },        /* MUTE */
-	{ 0x05, 0x04, KEY_AUDIO },       /* AUDIO */
-	{ 0x05, 0x15, KEY_RECORD },      /* RECORD */
-	{ 0x05, 0x11, KEY_PLAY },        /* PLAY */
-	{ 0x05, 0x16, KEY_STOP },        /* STOP */
-	{ 0x05, 0x0c, KEY_PLAYPAUSE },   /* TIMESHIFT / PAUSE */
-	{ 0x05, 0x05, KEY_BACK },        /* << / RED */
-	{ 0x05, 0x09, KEY_FORWARD },     /* >> / YELLOW */
-	{ 0x05, 0x17, KEY_TEXT },        /* TELETEXT */
-	{ 0x05, 0x0a, KEY_EPG },         /* EPG */
-	{ 0x05, 0x13, KEY_MENU },        /* MENU */
-
-	{ 0x05, 0x0e, KEY_CHANNELUP },   /* CH UP */
-	{ 0x05, 0x0d, KEY_CHANNELDOWN }, /* CH DOWN */
-	{ 0x05, 0x19, KEY_FIRST },       /* |<< / GREEN */
-	{ 0x05, 0x08, KEY_LAST },        /* >>| / BLUE */
+	{ 0x053d, KEY_PROG1 },       /* SOURCE */
+	{ 0x0512, KEY_POWER },       /* POWER */
+	{ 0x051e, KEY_1 },           /* 1 */
+	{ 0x051f, KEY_2 },           /* 2 */
+	{ 0x0520, KEY_3 },           /* 3 */
+	{ 0x0521, KEY_4 },           /* 4 */
+	{ 0x0522, KEY_5 },           /* 5 */
+	{ 0x0523, KEY_6 },           /* 6 */
+	{ 0x0524, KEY_7 },           /* 7 */
+	{ 0x0525, KEY_8 },           /* 8 */
+	{ 0x0526, KEY_9 },           /* 9 */
+	{ 0x053f, KEY_LEFT },        /* L / DISPLAY */
+	{ 0x0527, KEY_0 },           /* 0 */
+	{ 0x050f, KEY_RIGHT },       /* R / CH RTN */
+	{ 0x0518, KEY_PROG2 },       /* SNAP SHOT */
+	{ 0x051c, KEY_PROG3 },       /* 16-CH PREV */
+	{ 0x052d, KEY_VOLUMEDOWN },  /* VOL DOWN */
+	{ 0x053e, KEY_ZOOM },        /* FULL SCREEN */
+	{ 0x052e, KEY_VOLUMEUP },    /* VOL UP */
+	{ 0x0510, KEY_MUTE },        /* MUTE */
+	{ 0x0504, KEY_AUDIO },       /* AUDIO */
+	{ 0x0515, KEY_RECORD },      /* RECORD */
+	{ 0x0511, KEY_PLAY },        /* PLAY */
+	{ 0x0516, KEY_STOP },        /* STOP */
+	{ 0x050c, KEY_PLAYPAUSE },   /* TIMESHIFT / PAUSE */
+	{ 0x0505, KEY_BACK },        /* << / RED */
+	{ 0x0509, KEY_FORWARD },     /* >> / YELLOW */
+	{ 0x0517, KEY_TEXT },        /* TELETEXT */
+	{ 0x050a, KEY_EPG },         /* EPG */
+	{ 0x0513, KEY_MENU },        /* MENU */
+
+	{ 0x050e, KEY_CHANNELUP },   /* CH UP */
+	{ 0x050d, KEY_CHANNELDOWN }, /* CH DOWN */
+	{ 0x0519, KEY_FIRST },       /* |<< / GREEN */
+	{ 0x0508, KEY_LAST },        /* >>| / BLUE */
 };
 
 static u8 af9015_ir_table_avermedia[] = {
@@ -622,34 +622,34 @@ static u8 af9015_ir_table_avermedia_ks[] = {
 
 /* Digittrade DVB-T USB Stick */
 static struct dvb_usb_rc_key af9015_rc_keys_digittrade[] = {
-	{ 0x01, 0x0f, KEY_LAST },	/* RETURN */
-	{ 0x05, 0x17, KEY_TEXT },	/* TELETEXT */
-	{ 0x01, 0x08, KEY_EPG },	/* EPG */
-	{ 0x05, 0x13, KEY_POWER },	/* POWER */
-	{ 0x01, 0x09, KEY_ZOOM },	/* FULLSCREEN */
-	{ 0x00, 0x40, KEY_AUDIO },	/* DUAL SOUND */
-	{ 0x00, 0x2c, KEY_PRINT },	/* SNAPSHOT */
-	{ 0x05, 0x16, KEY_SUBTITLE },	/* SUBTITLE */
-	{ 0x00, 0x52, KEY_CHANNELUP },	/* CH Up */
-	{ 0x00, 0x51, KEY_CHANNELDOWN },/* Ch Dn */
-	{ 0x00, 0x57, KEY_VOLUMEUP },	/* Vol Up */
-	{ 0x00, 0x56, KEY_VOLUMEDOWN },	/* Vol Dn */
-	{ 0x01, 0x10, KEY_MUTE },	/* MUTE */
-	{ 0x00, 0x27, KEY_0 },
-	{ 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 },
-	{ 0x01, 0x17, KEY_PLAYPAUSE },	/* TIMESHIFT */
-	{ 0x01, 0x15, KEY_RECORD },	/* RECORD */
-	{ 0x03, 0x13, KEY_PLAY },	/* PLAY */
-	{ 0x01, 0x16, KEY_STOP },	/* STOP */
-	{ 0x01, 0x13, KEY_PAUSE },	/* PAUSE */
+	{ 0x010f, KEY_LAST },	/* RETURN */
+	{ 0x0517, KEY_TEXT },	/* TELETEXT */
+	{ 0x0108, KEY_EPG },	/* EPG */
+	{ 0x0513, KEY_POWER },	/* POWER */
+	{ 0x0109, KEY_ZOOM },	/* FULLSCREEN */
+	{ 0x0040, KEY_AUDIO },	/* DUAL SOUND */
+	{ 0x002c, KEY_PRINT },	/* SNAPSHOT */
+	{ 0x0516, KEY_SUBTITLE },	/* SUBTITLE */
+	{ 0x0052, KEY_CHANNELUP },	/* CH Up */
+	{ 0x0051, KEY_CHANNELDOWN },/* Ch Dn */
+	{ 0x0057, KEY_VOLUMEUP },	/* Vol Up */
+	{ 0x0056, KEY_VOLUMEDOWN },	/* Vol Dn */
+	{ 0x0110, KEY_MUTE },	/* MUTE */
+	{ 0x0027, KEY_0 },
+	{ 0x001e, KEY_1 },
+	{ 0x001f, KEY_2 },
+	{ 0x0020, KEY_3 },
+	{ 0x0021, KEY_4 },
+	{ 0x0022, KEY_5 },
+	{ 0x0023, KEY_6 },
+	{ 0x0024, KEY_7 },
+	{ 0x0025, KEY_8 },
+	{ 0x0026, KEY_9 },
+	{ 0x0117, KEY_PLAYPAUSE },	/* TIMESHIFT */
+	{ 0x0115, KEY_RECORD },	/* RECORD */
+	{ 0x0313, KEY_PLAY },	/* PLAY */
+	{ 0x0116, KEY_STOP },	/* STOP */
+	{ 0x0113, KEY_PAUSE },	/* PAUSE */
 };
 
 static u8 af9015_ir_table_digittrade[] = {
@@ -685,34 +685,34 @@ static u8 af9015_ir_table_digittrade[] = {
 
 /* TREKSTOR DVB-T USB Stick */
 static struct dvb_usb_rc_key af9015_rc_keys_trekstor[] = {
-	{ 0x07, 0x04, KEY_AGAIN },		/* Home */
-	{ 0x07, 0x05, KEY_MUTE },		/* Mute */
-	{ 0x07, 0x06, KEY_UP },			/* Up */
-	{ 0x07, 0x07, KEY_DOWN },		/* Down */
-	{ 0x07, 0x09, KEY_RIGHT },		/* Right */
-	{ 0x07, 0x0a, KEY_ENTER },		/* OK */
-	{ 0x07, 0x0b, KEY_FASTFORWARD },	/* Fast forward */
-	{ 0x07, 0x0c, KEY_REWIND },		/* Rewind */
-	{ 0x07, 0x0d, KEY_PLAY },		/* Play/Pause */
-	{ 0x07, 0x0e, KEY_VOLUMEUP },		/* Volume + */
-	{ 0x07, 0x0f, KEY_VOLUMEDOWN },		/* Volume - */
-	{ 0x07, 0x10, KEY_RECORD },		/* Record */
-	{ 0x07, 0x11, KEY_STOP },		/* Stop */
-	{ 0x07, 0x12, KEY_ZOOM },		/* TV */
-	{ 0x07, 0x13, KEY_EPG },		/* Info/EPG */
-	{ 0x07, 0x14, KEY_CHANNELDOWN },	/* Channel - */
-	{ 0x07, 0x15, KEY_CHANNELUP },		/* Channel + */
-	{ 0x07, 0x1e, KEY_1 },
-	{ 0x07, 0x1f, KEY_2 },
-	{ 0x07, 0x20, KEY_3 },
-	{ 0x07, 0x21, KEY_4 },
-	{ 0x07, 0x22, KEY_5 },
-	{ 0x07, 0x23, KEY_6 },
-	{ 0x07, 0x24, KEY_7 },
-	{ 0x07, 0x25, KEY_8 },
-	{ 0x07, 0x26, KEY_9 },
-	{ 0x07, 0x08, KEY_LEFT },		/* LEFT */
-	{ 0x07, 0x27, KEY_0 },
+	{ 0x0704, KEY_AGAIN },		/* Home */
+	{ 0x0705, KEY_MUTE },		/* Mute */
+	{ 0x0706, KEY_UP },			/* Up */
+	{ 0x0707, KEY_DOWN },		/* Down */
+	{ 0x0709, KEY_RIGHT },		/* Right */
+	{ 0x070a, KEY_ENTER },		/* OK */
+	{ 0x070b, KEY_FASTFORWARD },	/* Fast forward */
+	{ 0x070c, KEY_REWIND },		/* Rewind */
+	{ 0x070d, KEY_PLAY },		/* Play/Pause */
+	{ 0x070e, KEY_VOLUMEUP },		/* Volume + */
+	{ 0x070f, KEY_VOLUMEDOWN },		/* Volume - */
+	{ 0x0710, KEY_RECORD },		/* Record */
+	{ 0x0711, KEY_STOP },		/* Stop */
+	{ 0x0712, KEY_ZOOM },		/* TV */
+	{ 0x0713, KEY_EPG },		/* Info/EPG */
+	{ 0x0714, KEY_CHANNELDOWN },	/* Channel - */
+	{ 0x0715, KEY_CHANNELUP },		/* Channel + */
+	{ 0x071e, KEY_1 },
+	{ 0x071f, KEY_2 },
+	{ 0x0720, KEY_3 },
+	{ 0x0721, KEY_4 },
+	{ 0x0722, KEY_5 },
+	{ 0x0723, KEY_6 },
+	{ 0x0724, KEY_7 },
+	{ 0x0725, KEY_8 },
+	{ 0x0726, KEY_9 },
+	{ 0x0708, KEY_LEFT },		/* LEFT */
+	{ 0x0727, KEY_0 },
 };
 
 static u8 af9015_ir_table_trekstor[] = {

+ 46 - 46
drivers/media/dvb/dvb-usb/anysee.c

@@ -389,8 +389,8 @@ static int anysee_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
 	*state = REMOTE_NO_KEY_PRESSED;
 
 	for (i = 0; i < d->props.rc_key_map_size; i++) {
-		if (keymap[i].custom == ircode[0] &&
-		    keymap[i].data == ircode[1]) {
+		if (rc5_custom(&keymap[i]) == ircode[0] &&
+		    rc5_data(&keymap[i]) == ircode[1]) {
 			*event = keymap[i].event;
 			*state = REMOTE_KEY_PRESSED;
 			return 0;
@@ -400,50 +400,50 @@ static int anysee_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
 }
 
 static struct dvb_usb_rc_key anysee_rc_keys[] = {
-	{ 0x01, 0x00, KEY_0 },
-	{ 0x01, 0x01, KEY_1 },
-	{ 0x01, 0x02, KEY_2 },
-	{ 0x01, 0x03, KEY_3 },
-	{ 0x01, 0x04, KEY_4 },
-	{ 0x01, 0x05, KEY_5 },
-	{ 0x01, 0x06, KEY_6 },
-	{ 0x01, 0x07, KEY_7 },
-	{ 0x01, 0x08, KEY_8 },
-	{ 0x01, 0x09, KEY_9 },
-	{ 0x01, 0x0a, KEY_POWER },
-	{ 0x01, 0x0b, KEY_DOCUMENTS },    /* * */
-	{ 0x01, 0x19, KEY_FAVORITES },
-	{ 0x01, 0x20, KEY_SLEEP },
-	{ 0x01, 0x21, KEY_MODE },         /* 4:3 / 16:9 select */
-	{ 0x01, 0x22, KEY_ZOOM },
-	{ 0x01, 0x47, KEY_TEXT },
-	{ 0x01, 0x16, KEY_TV },           /* TV / radio select */
-	{ 0x01, 0x1e, KEY_LANGUAGE },     /* Second Audio Program */
-	{ 0x01, 0x1a, KEY_SUBTITLE },
-	{ 0x01, 0x1b, KEY_CAMERA },       /* screenshot */
-	{ 0x01, 0x42, KEY_MUTE },
-	{ 0x01, 0x0e, KEY_MENU },
-	{ 0x01, 0x0f, KEY_EPG },
-	{ 0x01, 0x17, KEY_INFO },
-	{ 0x01, 0x10, KEY_EXIT },
-	{ 0x01, 0x13, KEY_VOLUMEUP },
-	{ 0x01, 0x12, KEY_VOLUMEDOWN },
-	{ 0x01, 0x11, KEY_CHANNELUP },
-	{ 0x01, 0x14, KEY_CHANNELDOWN },
-	{ 0x01, 0x15, KEY_OK },
-	{ 0x01, 0x1d, KEY_RED },
-	{ 0x01, 0x1f, KEY_GREEN },
-	{ 0x01, 0x1c, KEY_YELLOW },
-	{ 0x01, 0x44, KEY_BLUE },
-	{ 0x01, 0x0c, KEY_SHUFFLE },      /* snapshot */
-	{ 0x01, 0x48, KEY_STOP },
-	{ 0x01, 0x50, KEY_PLAY },
-	{ 0x01, 0x51, KEY_PAUSE },
-	{ 0x01, 0x49, KEY_RECORD },
-	{ 0x01, 0x18, KEY_PREVIOUS },     /* |<< */
-	{ 0x01, 0x0d, KEY_NEXT },         /* >>| */
-	{ 0x01, 0x24, KEY_PROG1 },        /* F1 */
-	{ 0x01, 0x25, KEY_PROG2 },        /* F2 */
+	{ 0x0100, KEY_0 },
+	{ 0x0101, KEY_1 },
+	{ 0x0102, KEY_2 },
+	{ 0x0103, KEY_3 },
+	{ 0x0104, KEY_4 },
+	{ 0x0105, KEY_5 },
+	{ 0x0106, KEY_6 },
+	{ 0x0107, KEY_7 },
+	{ 0x0108, KEY_8 },
+	{ 0x0109, KEY_9 },
+	{ 0x010a, KEY_POWER },
+	{ 0x010b, KEY_DOCUMENTS },    /* * */
+	{ 0x0119, KEY_FAVORITES },
+	{ 0x0120, KEY_SLEEP },
+	{ 0x0121, KEY_MODE },         /* 4:3 / 16:9 select */
+	{ 0x0122, KEY_ZOOM },
+	{ 0x0147, KEY_TEXT },
+	{ 0x0116, KEY_TV },           /* TV / radio select */
+	{ 0x011e, KEY_LANGUAGE },     /* Second Audio Program */
+	{ 0x011a, KEY_SUBTITLE },
+	{ 0x011b, KEY_CAMERA },       /* screenshot */
+	{ 0x0142, KEY_MUTE },
+	{ 0x010e, KEY_MENU },
+	{ 0x010f, KEY_EPG },
+	{ 0x0117, KEY_INFO },
+	{ 0x0110, KEY_EXIT },
+	{ 0x0113, KEY_VOLUMEUP },
+	{ 0x0112, KEY_VOLUMEDOWN },
+	{ 0x0111, KEY_CHANNELUP },
+	{ 0x0114, KEY_CHANNELDOWN },
+	{ 0x0115, KEY_OK },
+	{ 0x011d, KEY_RED },
+	{ 0x011f, KEY_GREEN },
+	{ 0x011c, KEY_YELLOW },
+	{ 0x0144, KEY_BLUE },
+	{ 0x010c, KEY_SHUFFLE },      /* snapshot */
+	{ 0x0148, KEY_STOP },
+	{ 0x0150, KEY_PLAY },
+	{ 0x0151, KEY_PAUSE },
+	{ 0x0149, KEY_RECORD },
+	{ 0x0118, KEY_PREVIOUS },     /* |<< */
+	{ 0x010d, KEY_NEXT },         /* >>| */
+	{ 0x0124, KEY_PROG1 },        /* F1 */
+	{ 0x0125, KEY_PROG2 },        /* F2 */
 };
 
 /* DVB USB Driver stuff */

+ 37 - 37
drivers/media/dvb/dvb-usb/cinergyT2-core.c

@@ -85,43 +85,43 @@ static int cinergyt2_frontend_attach(struct dvb_usb_adapter *adap)
 }
 
 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 }
+	{ 0x0401, KEY_POWER },
+	{ 0x0402, KEY_1 },
+	{ 0x0403, KEY_2 },
+	{ 0x0404, KEY_3 },
+	{ 0x0405, KEY_4 },
+	{ 0x0406, KEY_5 },
+	{ 0x0407, KEY_6 },
+	{ 0x0408, KEY_7 },
+	{ 0x0409, KEY_8 },
+	{ 0x040a, KEY_9 },
+	{ 0x040c, KEY_0 },
+	{ 0x040b, KEY_VIDEO },
+	{ 0x040d, KEY_REFRESH },
+	{ 0x040e, KEY_SELECT },
+	{ 0x040f, KEY_EPG },
+	{ 0x0410, KEY_UP },
+	{ 0x0414, KEY_DOWN },
+	{ 0x0411, KEY_LEFT },
+	{ 0x0413, KEY_RIGHT },
+	{ 0x0412, KEY_OK },
+	{ 0x0415, KEY_TEXT },
+	{ 0x0416, KEY_INFO },
+	{ 0x0417, KEY_RED },
+	{ 0x0418, KEY_GREEN },
+	{ 0x0419, KEY_YELLOW },
+	{ 0x041a, KEY_BLUE },
+	{ 0x041c, KEY_VOLUMEUP },
+	{ 0x041e, KEY_VOLUMEDOWN },
+	{ 0x041d, KEY_MUTE },
+	{ 0x041b, KEY_CHANNELUP },
+	{ 0x041f, KEY_CHANNELDOWN },
+	{ 0x0440, KEY_PAUSE },
+	{ 0x044c, KEY_PLAY },
+	{ 0x0458, KEY_RECORD },
+	{ 0x0454, KEY_PREVIOUS },
+	{ 0x0448, KEY_STOP },
+	{ 0x045c, KEY_NEXT }
 };
 
 /* Number of keypresses to ignore before detect repeating */

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

@@ -275,6 +275,7 @@ static int cinergyt2_fe_set_frontend(struct dvb_frontend *fe,
 	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;
+	param.flags = 0;
 
 	err = dvb_usb_generic_rw(state->d,
 			(char *)&param, sizeof(param),

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

@@ -38,7 +38,7 @@
 #include "mxl5005s.h"
 #include "dib7000p.h"
 #include "dib0070.h"
-#include "lgs8gl5.h"
+#include "lgs8gxx.h"
 
 /* debug */
 static int dvb_usb_cxusb_debug;
@@ -392,8 +392,8 @@ static int cxusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
 	*state = REMOTE_NO_KEY_PRESSED;
 
 	for (i = 0; i < d->props.rc_key_map_size; i++) {
-		if (keymap[i].custom == ircode[2] &&
-		    keymap[i].data == ircode[3]) {
+		if (rc5_custom(&keymap[i]) == ircode[2] &&
+		    rc5_data(&keymap[i]) == ircode[3]) {
 			*event = keymap[i].event;
 			*state = REMOTE_KEY_PRESSED;
 
@@ -420,8 +420,8 @@ static int cxusb_bluebird2_rc_query(struct dvb_usb_device *d, u32 *event,
 		return 0;
 
 	for (i = 0; i < d->props.rc_key_map_size; i++) {
-		if (keymap[i].custom == ircode[1] &&
-		    keymap[i].data == ircode[2]) {
+		if (rc5_custom(&keymap[i]) == ircode[1] &&
+		    rc5_data(&keymap[i]) == ircode[2]) {
 			*event = keymap[i].event;
 			*state = REMOTE_KEY_PRESSED;
 
@@ -446,8 +446,8 @@ static int cxusb_d680_dmb_rc_query(struct dvb_usb_device *d, u32 *event,
 		return 0;
 
 	for (i = 0; i < d->props.rc_key_map_size; i++) {
-		if (keymap[i].custom == ircode[0] &&
-		    keymap[i].data == ircode[1]) {
+		if (rc5_custom(&keymap[i]) == ircode[0] &&
+		    rc5_data(&keymap[i]) == ircode[1]) {
 			*event = keymap[i].event;
 			*state = REMOTE_KEY_PRESSED;
 
@@ -459,128 +459,128 @@ static int cxusb_d680_dmb_rc_query(struct dvb_usb_device *d, u32 *event,
 }
 
 static struct dvb_usb_rc_key dvico_mce_rc_keys[] = {
-	{ 0xfe, 0x02, KEY_TV },
-	{ 0xfe, 0x0e, KEY_MP3 },
-	{ 0xfe, 0x1a, KEY_DVD },
-	{ 0xfe, 0x1e, KEY_FAVORITES },
-	{ 0xfe, 0x16, KEY_SETUP },
-	{ 0xfe, 0x46, KEY_POWER2 },
-	{ 0xfe, 0x0a, KEY_EPG },
-	{ 0xfe, 0x49, KEY_BACK },
-	{ 0xfe, 0x4d, KEY_MENU },
-	{ 0xfe, 0x51, KEY_UP },
-	{ 0xfe, 0x5b, KEY_LEFT },
-	{ 0xfe, 0x5f, KEY_RIGHT },
-	{ 0xfe, 0x53, KEY_DOWN },
-	{ 0xfe, 0x5e, KEY_OK },
-	{ 0xfe, 0x59, KEY_INFO },
-	{ 0xfe, 0x55, KEY_TAB },
-	{ 0xfe, 0x0f, KEY_PREVIOUSSONG },/* Replay */
-	{ 0xfe, 0x12, KEY_NEXTSONG },	/* Skip */
-	{ 0xfe, 0x42, KEY_ENTER	 },	/* Windows/Start */
-	{ 0xfe, 0x15, KEY_VOLUMEUP },
-	{ 0xfe, 0x05, KEY_VOLUMEDOWN },
-	{ 0xfe, 0x11, KEY_CHANNELUP },
-	{ 0xfe, 0x09, KEY_CHANNELDOWN },
-	{ 0xfe, 0x52, KEY_CAMERA },
-	{ 0xfe, 0x5a, KEY_TUNER },	/* Live */
-	{ 0xfe, 0x19, KEY_OPEN },
-	{ 0xfe, 0x0b, KEY_1 },
-	{ 0xfe, 0x17, KEY_2 },
-	{ 0xfe, 0x1b, KEY_3 },
-	{ 0xfe, 0x07, KEY_4 },
-	{ 0xfe, 0x50, KEY_5 },
-	{ 0xfe, 0x54, KEY_6 },
-	{ 0xfe, 0x48, KEY_7 },
-	{ 0xfe, 0x4c, KEY_8 },
-	{ 0xfe, 0x58, KEY_9 },
-	{ 0xfe, 0x13, KEY_ANGLE },	/* Aspect */
-	{ 0xfe, 0x03, KEY_0 },
-	{ 0xfe, 0x1f, KEY_ZOOM },
-	{ 0xfe, 0x43, KEY_REWIND },
-	{ 0xfe, 0x47, KEY_PLAYPAUSE },
-	{ 0xfe, 0x4f, KEY_FASTFORWARD },
-	{ 0xfe, 0x57, KEY_MUTE },
-	{ 0xfe, 0x0d, KEY_STOP },
-	{ 0xfe, 0x01, KEY_RECORD },
-	{ 0xfe, 0x4e, KEY_POWER },
+	{ 0xfe02, KEY_TV },
+	{ 0xfe0e, KEY_MP3 },
+	{ 0xfe1a, KEY_DVD },
+	{ 0xfe1e, KEY_FAVORITES },
+	{ 0xfe16, KEY_SETUP },
+	{ 0xfe46, KEY_POWER2 },
+	{ 0xfe0a, KEY_EPG },
+	{ 0xfe49, KEY_BACK },
+	{ 0xfe4d, KEY_MENU },
+	{ 0xfe51, KEY_UP },
+	{ 0xfe5b, KEY_LEFT },
+	{ 0xfe5f, KEY_RIGHT },
+	{ 0xfe53, KEY_DOWN },
+	{ 0xfe5e, KEY_OK },
+	{ 0xfe59, KEY_INFO },
+	{ 0xfe55, KEY_TAB },
+	{ 0xfe0f, KEY_PREVIOUSSONG },/* Replay */
+	{ 0xfe12, KEY_NEXTSONG },	/* Skip */
+	{ 0xfe42, KEY_ENTER	 },	/* Windows/Start */
+	{ 0xfe15, KEY_VOLUMEUP },
+	{ 0xfe05, KEY_VOLUMEDOWN },
+	{ 0xfe11, KEY_CHANNELUP },
+	{ 0xfe09, KEY_CHANNELDOWN },
+	{ 0xfe52, KEY_CAMERA },
+	{ 0xfe5a, KEY_TUNER },	/* Live */
+	{ 0xfe19, KEY_OPEN },
+	{ 0xfe0b, KEY_1 },
+	{ 0xfe17, KEY_2 },
+	{ 0xfe1b, KEY_3 },
+	{ 0xfe07, KEY_4 },
+	{ 0xfe50, KEY_5 },
+	{ 0xfe54, KEY_6 },
+	{ 0xfe48, KEY_7 },
+	{ 0xfe4c, KEY_8 },
+	{ 0xfe58, KEY_9 },
+	{ 0xfe13, KEY_ANGLE },	/* Aspect */
+	{ 0xfe03, KEY_0 },
+	{ 0xfe1f, KEY_ZOOM },
+	{ 0xfe43, KEY_REWIND },
+	{ 0xfe47, KEY_PLAYPAUSE },
+	{ 0xfe4f, KEY_FASTFORWARD },
+	{ 0xfe57, KEY_MUTE },
+	{ 0xfe0d, KEY_STOP },
+	{ 0xfe01, KEY_RECORD },
+	{ 0xfe4e, KEY_POWER },
 };
 
 static struct dvb_usb_rc_key dvico_portable_rc_keys[] = {
-	{ 0xfc, 0x02, KEY_SETUP },       /* Profile */
-	{ 0xfc, 0x43, KEY_POWER2 },
-	{ 0xfc, 0x06, KEY_EPG },
-	{ 0xfc, 0x5a, KEY_BACK },
-	{ 0xfc, 0x05, KEY_MENU },
-	{ 0xfc, 0x47, KEY_INFO },
-	{ 0xfc, 0x01, KEY_TAB },
-	{ 0xfc, 0x42, KEY_PREVIOUSSONG },/* Replay */
-	{ 0xfc, 0x49, KEY_VOLUMEUP },
-	{ 0xfc, 0x09, KEY_VOLUMEDOWN },
-	{ 0xfc, 0x54, KEY_CHANNELUP },
-	{ 0xfc, 0x0b, KEY_CHANNELDOWN },
-	{ 0xfc, 0x16, KEY_CAMERA },
-	{ 0xfc, 0x40, KEY_TUNER },	/* ATV/DTV */
-	{ 0xfc, 0x45, KEY_OPEN },
-	{ 0xfc, 0x19, KEY_1 },
-	{ 0xfc, 0x18, KEY_2 },
-	{ 0xfc, 0x1b, KEY_3 },
-	{ 0xfc, 0x1a, KEY_4 },
-	{ 0xfc, 0x58, KEY_5 },
-	{ 0xfc, 0x59, KEY_6 },
-	{ 0xfc, 0x15, KEY_7 },
-	{ 0xfc, 0x14, KEY_8 },
-	{ 0xfc, 0x17, KEY_9 },
-	{ 0xfc, 0x44, KEY_ANGLE },	/* Aspect */
-	{ 0xfc, 0x55, KEY_0 },
-	{ 0xfc, 0x07, KEY_ZOOM },
-	{ 0xfc, 0x0a, KEY_REWIND },
-	{ 0xfc, 0x08, KEY_PLAYPAUSE },
-	{ 0xfc, 0x4b, KEY_FASTFORWARD },
-	{ 0xfc, 0x5b, KEY_MUTE },
-	{ 0xfc, 0x04, KEY_STOP },
-	{ 0xfc, 0x56, KEY_RECORD },
-	{ 0xfc, 0x57, KEY_POWER },
-	{ 0xfc, 0x41, KEY_UNKNOWN },    /* INPUT */
-	{ 0xfc, 0x00, KEY_UNKNOWN },    /* HD */
+	{ 0xfc02, KEY_SETUP },       /* Profile */
+	{ 0xfc43, KEY_POWER2 },
+	{ 0xfc06, KEY_EPG },
+	{ 0xfc5a, KEY_BACK },
+	{ 0xfc05, KEY_MENU },
+	{ 0xfc47, KEY_INFO },
+	{ 0xfc01, KEY_TAB },
+	{ 0xfc42, KEY_PREVIOUSSONG },/* Replay */
+	{ 0xfc49, KEY_VOLUMEUP },
+	{ 0xfc09, KEY_VOLUMEDOWN },
+	{ 0xfc54, KEY_CHANNELUP },
+	{ 0xfc0b, KEY_CHANNELDOWN },
+	{ 0xfc16, KEY_CAMERA },
+	{ 0xfc40, KEY_TUNER },	/* ATV/DTV */
+	{ 0xfc45, KEY_OPEN },
+	{ 0xfc19, KEY_1 },
+	{ 0xfc18, KEY_2 },
+	{ 0xfc1b, KEY_3 },
+	{ 0xfc1a, KEY_4 },
+	{ 0xfc58, KEY_5 },
+	{ 0xfc59, KEY_6 },
+	{ 0xfc15, KEY_7 },
+	{ 0xfc14, KEY_8 },
+	{ 0xfc17, KEY_9 },
+	{ 0xfc44, KEY_ANGLE },	/* Aspect */
+	{ 0xfc55, KEY_0 },
+	{ 0xfc07, KEY_ZOOM },
+	{ 0xfc0a, KEY_REWIND },
+	{ 0xfc08, KEY_PLAYPAUSE },
+	{ 0xfc4b, KEY_FASTFORWARD },
+	{ 0xfc5b, KEY_MUTE },
+	{ 0xfc04, KEY_STOP },
+	{ 0xfc56, KEY_RECORD },
+	{ 0xfc57, KEY_POWER },
+	{ 0xfc41, KEY_UNKNOWN },    /* INPUT */
+	{ 0xfc00, 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 },
+	{ 0x0038, KEY_UNKNOWN },	/* TV/AV */
+	{ 0x080c, KEY_ZOOM },
+	{ 0x0800, KEY_0 },
+	{ 0x0001, KEY_1 },
+	{ 0x0802, KEY_2 },
+	{ 0x0003, KEY_3 },
+	{ 0x0804, KEY_4 },
+	{ 0x0005, KEY_5 },
+	{ 0x0806, KEY_6 },
+	{ 0x0007, KEY_7 },
+	{ 0x0808, KEY_8 },
+	{ 0x0009, KEY_9 },
+	{ 0x000a, KEY_MUTE },
+	{ 0x0829, KEY_BACK },
+	{ 0x0012, KEY_CHANNELUP },
+	{ 0x0813, KEY_CHANNELDOWN },
+	{ 0x002b, KEY_VOLUMEUP },
+	{ 0x082c, KEY_VOLUMEDOWN },
+	{ 0x0020, KEY_UP },
+	{ 0x0821, KEY_DOWN },
+	{ 0x0011, KEY_LEFT },
+	{ 0x0810, KEY_RIGHT },
+	{ 0x000d, KEY_OK },
+	{ 0x081f, KEY_RECORD },
+	{ 0x0017, KEY_PLAYPAUSE },
+	{ 0x0816, KEY_PLAYPAUSE },
+	{ 0x000b, KEY_STOP },
+	{ 0x0827, KEY_FASTFORWARD },
+	{ 0x0026, KEY_REWIND },
+	{ 0x081e, KEY_UNKNOWN },    /* Time Shift */
+	{ 0x000e, KEY_UNKNOWN },    /* Snapshot */
+	{ 0x082d, KEY_UNKNOWN },    /* Mouse Cursor */
+	{ 0x000f, KEY_UNKNOWN },    /* Minimize/Maximize */
+	{ 0x0814, KEY_UNKNOWN },    /* Shuffle */
+	{ 0x0025, KEY_POWER },
 };
 
 static int cxusb_dee1601_demod_init(struct dvb_frontend* fe)
@@ -1094,8 +1094,18 @@ static int cxusb_nano2_frontend_attach(struct dvb_usb_adapter *adap)
 	return -EIO;
 }
 
-static struct lgs8gl5_config lgs8gl5_cfg = {
+static struct lgs8gxx_config d680_lgs8gl5_cfg = {
+	.prod = LGS8GXX_PROD_LGS8GL5,
 	.demod_address = 0x19,
+	.serial_ts = 0,
+	.ts_clk_pol = 0,
+	.ts_clk_gated = 1,
+	.if_clk_freq = 30400, /* 30.4 MHz */
+	.if_freq = 5725, /* 5.725 MHz */
+	.if_neg_center = 0,
+	.ext_adc = 0,
+	.adc_signed = 0,
+	.if_neg_edge = 0,
 };
 
 static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap)
@@ -1135,7 +1145,7 @@ static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap)
 	msleep(100);
 
 	/* Attach frontend */
-	adap->fe = dvb_attach(lgs8gl5_attach, &lgs8gl5_cfg, &d->i2c_adap);
+	adap->fe = dvb_attach(lgs8gxx_attach, &d680_lgs8gl5_cfg, &d->i2c_adap);
 	if (adap->fe == NULL)
 		return -EIO;
 

+ 199 - 188
drivers/media/dvb/dvb-usb/dib0700_devices.c

@@ -310,7 +310,7 @@ static int stk7700d_tuner_attach(struct dvb_usb_adapter *adap)
 	struct i2c_adapter *tun_i2c;
 	tun_i2c = dib7000p_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1);
 	return dvb_attach(mt2266_attach, adap->fe, tun_i2c,
-		&stk7700d_mt2266_config[adap->id]) == NULL ? -ENODEV : 0;;
+		&stk7700d_mt2266_config[adap->id]) == NULL ? -ENODEV : 0;
 }
 
 /* STK7700-PH: Digital/Analog Hybrid Tuner, e.h. Cinergy HT USB HE */
@@ -509,7 +509,8 @@ static int dib0700_rc_query_legacy(struct dvb_usb_device *d, u32 *event,
 			return 0;
 		}
 		for (i=0;i<d->props.rc_key_map_size; i++) {
-			if (keymap[i].custom == key[3-2] && keymap[i].data == key[3-3]) {
+			if (rc5_custom(&keymap[i]) == key[3-2] &&
+			    rc5_data(&keymap[i]) == key[3-3]) {
 				st->rc_counter = 0;
 				*event = keymap[i].event;
 				*state = REMOTE_KEY_PRESSED;
@@ -522,7 +523,8 @@ static int dib0700_rc_query_legacy(struct dvb_usb_device *d, u32 *event,
 	default: {
 		/* RC-5 protocol changes toggle bit on new keypress */
 		for (i = 0; i < d->props.rc_key_map_size; i++) {
-			if (keymap[i].custom == key[3-2] && keymap[i].data == key[3-3]) {
+			if (rc5_custom(&keymap[i]) == key[3-2] &&
+			    rc5_data(&keymap[i]) == key[3-3]) {
 				if (d->last_event == keymap[i].event &&
 					key[3-1] == st->rc_toggle) {
 					st->rc_counter++;
@@ -616,8 +618,8 @@ static int dib0700_rc_query_v1_20(struct dvb_usb_device *d, u32 *event,
 
 	/* Find the key in the map */
 	for (i = 0; i < d->props.rc_key_map_size; i++) {
-		if (keymap[i].custom == poll_reply.system_lsb &&
-		    keymap[i].data == poll_reply.data) {
+		if (rc5_custom(&keymap[i]) == poll_reply.system_lsb &&
+		    rc5_data(&keymap[i]) == poll_reply.data) {
 			*event = keymap[i].event;
 			found = 1;
 			break;
@@ -684,193 +686,193 @@ static int dib0700_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
 
 static struct dvb_usb_rc_key dib0700_rc_keys[] = {
 	/* Key codes for the tiny Pinnacle remote*/
-	{ 0x07, 0x00, KEY_MUTE },
-	{ 0x07, 0x01, KEY_MENU }, // Pinnacle logo
-	{ 0x07, 0x39, KEY_POWER },
-	{ 0x07, 0x03, KEY_VOLUMEUP },
-	{ 0x07, 0x09, KEY_VOLUMEDOWN },
-	{ 0x07, 0x06, KEY_CHANNELUP },
-	{ 0x07, 0x0c, KEY_CHANNELDOWN },
-	{ 0x07, 0x0f, KEY_1 },
-	{ 0x07, 0x15, KEY_2 },
-	{ 0x07, 0x10, KEY_3 },
-	{ 0x07, 0x18, KEY_4 },
-	{ 0x07, 0x1b, KEY_5 },
-	{ 0x07, 0x1e, KEY_6 },
-	{ 0x07, 0x11, KEY_7 },
-	{ 0x07, 0x21, KEY_8 },
-	{ 0x07, 0x12, KEY_9 },
-	{ 0x07, 0x27, KEY_0 },
-	{ 0x07, 0x24, KEY_SCREEN }, // 'Square' key
-	{ 0x07, 0x2a, KEY_TEXT },   // 'T' key
-	{ 0x07, 0x2d, KEY_REWIND },
-	{ 0x07, 0x30, KEY_PLAY },
-	{ 0x07, 0x33, KEY_FASTFORWARD },
-	{ 0x07, 0x36, KEY_RECORD },
-	{ 0x07, 0x3c, KEY_STOP },
-	{ 0x07, 0x3f, KEY_CANCEL }, // '?' key
+	{ 0x0700, KEY_MUTE },
+	{ 0x0701, KEY_MENU }, /* Pinnacle logo */
+	{ 0x0739, KEY_POWER },
+	{ 0x0703, KEY_VOLUMEUP },
+	{ 0x0709, KEY_VOLUMEDOWN },
+	{ 0x0706, KEY_CHANNELUP },
+	{ 0x070c, KEY_CHANNELDOWN },
+	{ 0x070f, KEY_1 },
+	{ 0x0715, KEY_2 },
+	{ 0x0710, KEY_3 },
+	{ 0x0718, KEY_4 },
+	{ 0x071b, KEY_5 },
+	{ 0x071e, KEY_6 },
+	{ 0x0711, KEY_7 },
+	{ 0x0721, KEY_8 },
+	{ 0x0712, KEY_9 },
+	{ 0x0727, KEY_0 },
+	{ 0x0724, KEY_SCREEN }, /* 'Square' key */
+	{ 0x072a, KEY_TEXT },   /* 'T' key */
+	{ 0x072d, KEY_REWIND },
+	{ 0x0730, KEY_PLAY },
+	{ 0x0733, KEY_FASTFORWARD },
+	{ 0x0736, KEY_RECORD },
+	{ 0x073c, KEY_STOP },
+	{ 0x073f, KEY_CANCEL }, /* '?' key */
 	/* Key codes for the Terratec Cinergy DT XS Diversity, similar to cinergyT2.c */
-	{ 0xeb, 0x01, KEY_POWER },
-	{ 0xeb, 0x02, KEY_1 },
-	{ 0xeb, 0x03, KEY_2 },
-	{ 0xeb, 0x04, KEY_3 },
-	{ 0xeb, 0x05, KEY_4 },
-	{ 0xeb, 0x06, KEY_5 },
-	{ 0xeb, 0x07, KEY_6 },
-	{ 0xeb, 0x08, KEY_7 },
-	{ 0xeb, 0x09, KEY_8 },
-	{ 0xeb, 0x0a, KEY_9 },
-	{ 0xeb, 0x0b, KEY_VIDEO },
-	{ 0xeb, 0x0c, KEY_0 },
-	{ 0xeb, 0x0d, KEY_REFRESH },
-	{ 0xeb, 0x0f, KEY_EPG },
-	{ 0xeb, 0x10, KEY_UP },
-	{ 0xeb, 0x11, KEY_LEFT },
-	{ 0xeb, 0x12, KEY_OK },
-	{ 0xeb, 0x13, KEY_RIGHT },
-	{ 0xeb, 0x14, KEY_DOWN },
-	{ 0xeb, 0x16, KEY_INFO },
-	{ 0xeb, 0x17, KEY_RED },
-	{ 0xeb, 0x18, KEY_GREEN },
-	{ 0xeb, 0x19, KEY_YELLOW },
-	{ 0xeb, 0x1a, KEY_BLUE },
-	{ 0xeb, 0x1b, KEY_CHANNELUP },
-	{ 0xeb, 0x1c, KEY_VOLUMEUP },
-	{ 0xeb, 0x1d, KEY_MUTE },
-	{ 0xeb, 0x1e, KEY_VOLUMEDOWN },
-	{ 0xeb, 0x1f, KEY_CHANNELDOWN },
-	{ 0xeb, 0x40, KEY_PAUSE },
-	{ 0xeb, 0x41, KEY_HOME },
-	{ 0xeb, 0x42, KEY_MENU }, /* DVD Menu */
-	{ 0xeb, 0x43, KEY_SUBTITLE },
-	{ 0xeb, 0x44, KEY_TEXT }, /* Teletext */
-	{ 0xeb, 0x45, KEY_DELETE },
-	{ 0xeb, 0x46, KEY_TV },
-	{ 0xeb, 0x47, KEY_DVD },
-	{ 0xeb, 0x48, KEY_STOP },
-	{ 0xeb, 0x49, KEY_VIDEO },
-	{ 0xeb, 0x4a, KEY_AUDIO }, /* Music */
-	{ 0xeb, 0x4b, KEY_SCREEN }, /* Pic */
-	{ 0xeb, 0x4c, KEY_PLAY },
-	{ 0xeb, 0x4d, KEY_BACK },
-	{ 0xeb, 0x4e, KEY_REWIND },
-	{ 0xeb, 0x4f, KEY_FASTFORWARD },
-	{ 0xeb, 0x54, KEY_PREVIOUS },
-	{ 0xeb, 0x58, KEY_RECORD },
-	{ 0xeb, 0x5c, KEY_NEXT },
+	{ 0xeb01, KEY_POWER },
+	{ 0xeb02, KEY_1 },
+	{ 0xeb03, KEY_2 },
+	{ 0xeb04, KEY_3 },
+	{ 0xeb05, KEY_4 },
+	{ 0xeb06, KEY_5 },
+	{ 0xeb07, KEY_6 },
+	{ 0xeb08, KEY_7 },
+	{ 0xeb09, KEY_8 },
+	{ 0xeb0a, KEY_9 },
+	{ 0xeb0b, KEY_VIDEO },
+	{ 0xeb0c, KEY_0 },
+	{ 0xeb0d, KEY_REFRESH },
+	{ 0xeb0f, KEY_EPG },
+	{ 0xeb10, KEY_UP },
+	{ 0xeb11, KEY_LEFT },
+	{ 0xeb12, KEY_OK },
+	{ 0xeb13, KEY_RIGHT },
+	{ 0xeb14, KEY_DOWN },
+	{ 0xeb16, KEY_INFO },
+	{ 0xeb17, KEY_RED },
+	{ 0xeb18, KEY_GREEN },
+	{ 0xeb19, KEY_YELLOW },
+	{ 0xeb1a, KEY_BLUE },
+	{ 0xeb1b, KEY_CHANNELUP },
+	{ 0xeb1c, KEY_VOLUMEUP },
+	{ 0xeb1d, KEY_MUTE },
+	{ 0xeb1e, KEY_VOLUMEDOWN },
+	{ 0xeb1f, KEY_CHANNELDOWN },
+	{ 0xeb40, KEY_PAUSE },
+	{ 0xeb41, KEY_HOME },
+	{ 0xeb42, KEY_MENU }, /* DVD Menu */
+	{ 0xeb43, KEY_SUBTITLE },
+	{ 0xeb44, KEY_TEXT }, /* Teletext */
+	{ 0xeb45, KEY_DELETE },
+	{ 0xeb46, KEY_TV },
+	{ 0xeb47, KEY_DVD },
+	{ 0xeb48, KEY_STOP },
+	{ 0xeb49, KEY_VIDEO },
+	{ 0xeb4a, KEY_AUDIO }, /* Music */
+	{ 0xeb4b, KEY_SCREEN }, /* Pic */
+	{ 0xeb4c, KEY_PLAY },
+	{ 0xeb4d, KEY_BACK },
+	{ 0xeb4e, KEY_REWIND },
+	{ 0xeb4f, KEY_FASTFORWARD },
+	{ 0xeb54, KEY_PREVIOUS },
+	{ 0xeb58, KEY_RECORD },
+	{ 0xeb5c, KEY_NEXT },
 
 	/* Key codes for the Haupauge WinTV Nova-TD, copied from nova-t-usb2.c (Nova-T USB2) */
-	{ 0x1e, 0x00, KEY_0 },
-	{ 0x1e, 0x01, KEY_1 },
-	{ 0x1e, 0x02, KEY_2 },
-	{ 0x1e, 0x03, KEY_3 },
-	{ 0x1e, 0x04, KEY_4 },
-	{ 0x1e, 0x05, KEY_5 },
-	{ 0x1e, 0x06, KEY_6 },
-	{ 0x1e, 0x07, KEY_7 },
-	{ 0x1e, 0x08, KEY_8 },
-	{ 0x1e, 0x09, KEY_9 },
-	{ 0x1e, 0x0a, KEY_KPASTERISK },
-	{ 0x1e, 0x0b, KEY_RED },
-	{ 0x1e, 0x0c, KEY_RADIO },
-	{ 0x1e, 0x0d, KEY_MENU },
-	{ 0x1e, 0x0e, KEY_GRAVE }, /* # */
-	{ 0x1e, 0x0f, KEY_MUTE },
-	{ 0x1e, 0x10, KEY_VOLUMEUP },
-	{ 0x1e, 0x11, KEY_VOLUMEDOWN },
-	{ 0x1e, 0x12, KEY_CHANNEL },
-	{ 0x1e, 0x14, KEY_UP },
-	{ 0x1e, 0x15, KEY_DOWN },
-	{ 0x1e, 0x16, KEY_LEFT },
-	{ 0x1e, 0x17, KEY_RIGHT },
-	{ 0x1e, 0x18, KEY_VIDEO },
-	{ 0x1e, 0x19, KEY_AUDIO },
-	{ 0x1e, 0x1a, KEY_MEDIA },
-	{ 0x1e, 0x1b, KEY_EPG },
-	{ 0x1e, 0x1c, KEY_TV },
-	{ 0x1e, 0x1e, KEY_NEXT },
-	{ 0x1e, 0x1f, KEY_BACK },
-	{ 0x1e, 0x20, KEY_CHANNELUP },
-	{ 0x1e, 0x21, KEY_CHANNELDOWN },
-	{ 0x1e, 0x24, KEY_LAST }, /* Skip backwards */
-	{ 0x1e, 0x25, KEY_OK },
-	{ 0x1e, 0x29, KEY_BLUE},
-	{ 0x1e, 0x2e, KEY_GREEN },
-	{ 0x1e, 0x30, KEY_PAUSE },
-	{ 0x1e, 0x32, KEY_REWIND },
-	{ 0x1e, 0x34, KEY_FASTFORWARD },
-	{ 0x1e, 0x35, KEY_PLAY },
-	{ 0x1e, 0x36, KEY_STOP },
-	{ 0x1e, 0x37, KEY_RECORD },
-	{ 0x1e, 0x38, KEY_YELLOW },
-	{ 0x1e, 0x3b, KEY_GOTO },
-	{ 0x1e, 0x3d, KEY_POWER },
+	{ 0x1e00, KEY_0 },
+	{ 0x1e01, KEY_1 },
+	{ 0x1e02, KEY_2 },
+	{ 0x1e03, KEY_3 },
+	{ 0x1e04, KEY_4 },
+	{ 0x1e05, KEY_5 },
+	{ 0x1e06, KEY_6 },
+	{ 0x1e07, KEY_7 },
+	{ 0x1e08, KEY_8 },
+	{ 0x1e09, KEY_9 },
+	{ 0x1e0a, KEY_KPASTERISK },
+	{ 0x1e0b, KEY_RED },
+	{ 0x1e0c, KEY_RADIO },
+	{ 0x1e0d, KEY_MENU },
+	{ 0x1e0e, KEY_GRAVE }, /* # */
+	{ 0x1e0f, KEY_MUTE },
+	{ 0x1e10, KEY_VOLUMEUP },
+	{ 0x1e11, KEY_VOLUMEDOWN },
+	{ 0x1e12, KEY_CHANNEL },
+	{ 0x1e14, KEY_UP },
+	{ 0x1e15, KEY_DOWN },
+	{ 0x1e16, KEY_LEFT },
+	{ 0x1e17, KEY_RIGHT },
+	{ 0x1e18, KEY_VIDEO },
+	{ 0x1e19, KEY_AUDIO },
+	{ 0x1e1a, KEY_MEDIA },
+	{ 0x1e1b, KEY_EPG },
+	{ 0x1e1c, KEY_TV },
+	{ 0x1e1e, KEY_NEXT },
+	{ 0x1e1f, KEY_BACK },
+	{ 0x1e20, KEY_CHANNELUP },
+	{ 0x1e21, KEY_CHANNELDOWN },
+	{ 0x1e24, KEY_LAST }, /* Skip backwards */
+	{ 0x1e25, KEY_OK },
+	{ 0x1e29, KEY_BLUE},
+	{ 0x1e2e, KEY_GREEN },
+	{ 0x1e30, KEY_PAUSE },
+	{ 0x1e32, KEY_REWIND },
+	{ 0x1e34, KEY_FASTFORWARD },
+	{ 0x1e35, KEY_PLAY },
+	{ 0x1e36, KEY_STOP },
+	{ 0x1e37, KEY_RECORD },
+	{ 0x1e38, KEY_YELLOW },
+	{ 0x1e3b, KEY_GOTO },
+	{ 0x1e3d, KEY_POWER },
 
 	/* Key codes for the Leadtek Winfast DTV Dongle */
-	{ 0x00, 0x42, KEY_POWER },
-	{ 0x07, 0x7c, KEY_TUNER },
-	{ 0x0f, 0x4e, KEY_PRINT }, /* PREVIEW */
-	{ 0x08, 0x40, KEY_SCREEN }, /* full screen toggle*/
-	{ 0x0f, 0x71, KEY_DOT }, /* frequency */
-	{ 0x07, 0x43, KEY_0 },
-	{ 0x0c, 0x41, KEY_1 },
-	{ 0x04, 0x43, KEY_2 },
-	{ 0x0b, 0x7f, KEY_3 },
-	{ 0x0e, 0x41, KEY_4 },
-	{ 0x06, 0x43, KEY_5 },
-	{ 0x09, 0x7f, KEY_6 },
-	{ 0x0d, 0x7e, KEY_7 },
-	{ 0x05, 0x7c, KEY_8 },
-	{ 0x0a, 0x40, KEY_9 },
-	{ 0x0e, 0x4e, KEY_CLEAR },
-	{ 0x04, 0x7c, KEY_CHANNEL }, /* show channel number */
-	{ 0x0f, 0x41, KEY_LAST }, /* recall */
-	{ 0x03, 0x42, KEY_MUTE },
-	{ 0x06, 0x4c, KEY_RESERVED }, /* PIP button*/
-	{ 0x01, 0x72, KEY_SHUFFLE }, /* SNAPSHOT */
-	{ 0x0c, 0x4e, KEY_PLAYPAUSE }, /* TIMESHIFT */
-	{ 0x0b, 0x70, KEY_RECORD },
-	{ 0x03, 0x7d, KEY_VOLUMEUP },
-	{ 0x01, 0x7d, KEY_VOLUMEDOWN },
-	{ 0x02, 0x42, KEY_CHANNELUP },
-	{ 0x00, 0x7d, KEY_CHANNELDOWN },
+	{ 0x0042, KEY_POWER },
+	{ 0x077c, KEY_TUNER },
+	{ 0x0f4e, KEY_PRINT }, /* PREVIEW */
+	{ 0x0840, KEY_SCREEN }, /* full screen toggle*/
+	{ 0x0f71, KEY_DOT }, /* frequency */
+	{ 0x0743, KEY_0 },
+	{ 0x0c41, KEY_1 },
+	{ 0x0443, KEY_2 },
+	{ 0x0b7f, KEY_3 },
+	{ 0x0e41, KEY_4 },
+	{ 0x0643, KEY_5 },
+	{ 0x097f, KEY_6 },
+	{ 0x0d7e, KEY_7 },
+	{ 0x057c, KEY_8 },
+	{ 0x0a40, KEY_9 },
+	{ 0x0e4e, KEY_CLEAR },
+	{ 0x047c, KEY_CHANNEL }, /* show channel number */
+	{ 0x0f41, KEY_LAST }, /* recall */
+	{ 0x0342, KEY_MUTE },
+	{ 0x064c, KEY_RESERVED }, /* PIP button*/
+	{ 0x0172, KEY_SHUFFLE }, /* SNAPSHOT */
+	{ 0x0c4e, KEY_PLAYPAUSE }, /* TIMESHIFT */
+	{ 0x0b70, KEY_RECORD },
+	{ 0x037d, KEY_VOLUMEUP },
+	{ 0x017d, KEY_VOLUMEDOWN },
+	{ 0x0242, KEY_CHANNELUP },
+	{ 0x007d, KEY_CHANNELDOWN },
 
 	/* Key codes for Nova-TD "credit card" remote control. */
-	{ 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 },
+	{ 0x1d00, KEY_0 },
+	{ 0x1d01, KEY_1 },
+	{ 0x1d02, KEY_2 },
+	{ 0x1d03, KEY_3 },
+	{ 0x1d04, KEY_4 },
+	{ 0x1d05, KEY_5 },
+	{ 0x1d06, KEY_6 },
+	{ 0x1d07, KEY_7 },
+	{ 0x1d08, KEY_8 },
+	{ 0x1d09, KEY_9 },
+	{ 0x1d0a, KEY_TEXT },
+	{ 0x1d0d, KEY_MENU },
+	{ 0x1d0f, KEY_MUTE },
+	{ 0x1d10, KEY_VOLUMEUP },
+	{ 0x1d11, KEY_VOLUMEDOWN },
+	{ 0x1d12, KEY_CHANNEL },
+	{ 0x1d14, KEY_UP },
+	{ 0x1d15, KEY_DOWN },
+	{ 0x1d16, KEY_LEFT },
+	{ 0x1d17, KEY_RIGHT },
+	{ 0x1d1c, KEY_TV },
+	{ 0x1d1e, KEY_NEXT },
+	{ 0x1d1f, KEY_BACK },
+	{ 0x1d20, KEY_CHANNELUP },
+	{ 0x1d21, KEY_CHANNELDOWN },
+	{ 0x1d24, KEY_LAST },
+	{ 0x1d25, KEY_OK },
+	{ 0x1d30, KEY_PAUSE },
+	{ 0x1d32, KEY_REWIND },
+	{ 0x1d34, KEY_FASTFORWARD },
+	{ 0x1d35, KEY_PLAY },
+	{ 0x1d36, KEY_STOP },
+	{ 0x1d37, KEY_RECORD },
+	{ 0x1d3b, KEY_GOTO },
+	{ 0x1d3d, KEY_POWER },
 };
 
 /* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */
@@ -1497,6 +1499,8 @@ struct usb_device_id dib0700_usb_id_table[] = {
 	{ USB_DEVICE(USB_VID_LEADTEK,   USB_PID_WINFAST_DTV_DONGLE_H) },
 	{ USB_DEVICE(USB_VID_TERRATEC,	USB_PID_TERRATEC_T3) },
 	{ USB_DEVICE(USB_VID_TERRATEC,	USB_PID_TERRATEC_T5) },
+	{ USB_DEVICE(USB_VID_YUAN,      USB_PID_YUAN_STK7700D) },
+	{ USB_DEVICE(USB_VID_YUAN,	USB_PID_YUAN_STK7700D_2) },
 	{ 0 }		/* Terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
@@ -1624,7 +1628,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
 			}
 		},
 
-		.num_device_descs = 4,
+		.num_device_descs = 5,
 		.devices = {
 			{   "Pinnacle PCTV 2000e",
 				{ &dib0700_usb_id_table[11], NULL },
@@ -1642,6 +1646,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
 				{ &dib0700_usb_id_table[14], NULL },
 				{ NULL },
 			},
+			{   "YUAN High-Tech DiBcom STK7700D",
+				{ &dib0700_usb_id_table[55], NULL },
+				{ NULL },
+			},
 
 		},
 
@@ -1822,7 +1830,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
 			},
 		},
 
-		.num_device_descs = 8,
+		.num_device_descs = 9,
 		.devices = {
 			{   "Terratec Cinergy HT USB XE",
 				{ &dib0700_usb_id_table[27], NULL },
@@ -1856,7 +1864,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
 				{ &dib0700_usb_id_table[51], NULL },
 				{ NULL },
 			},
-
+			{   "YUAN High-Tech STK7700D",
+				{ &dib0700_usb_id_table[54], NULL },
+				{ NULL },
+			},
 		},
 		.rc_interval      = DEFAULT_RC_INTERVAL,
 		.rc_key_map       = dib0700_rc_keys,

+ 122 - 122
drivers/media/dvb/dvb-usb/dibusb-common.c

@@ -318,132 +318,132 @@ EXPORT_SYMBOL(dibusb_dib3000mc_tuner_attach);
  */
 struct dvb_usb_rc_key dibusb_rc_keys[] = {
 	/* Key codes for the little Artec T1/Twinhan/HAMA/ remote. */
-	{ 0x00, 0x16, KEY_POWER },
-	{ 0x00, 0x10, KEY_MUTE },
-	{ 0x00, 0x03, KEY_1 },
-	{ 0x00, 0x01, KEY_2 },
-	{ 0x00, 0x06, KEY_3 },
-	{ 0x00, 0x09, KEY_4 },
-	{ 0x00, 0x1d, KEY_5 },
-	{ 0x00, 0x1f, KEY_6 },
-	{ 0x00, 0x0d, KEY_7 },
-	{ 0x00, 0x19, KEY_8 },
-	{ 0x00, 0x1b, KEY_9 },
-	{ 0x00, 0x15, KEY_0 },
-	{ 0x00, 0x05, KEY_CHANNELUP },
-	{ 0x00, 0x02, KEY_CHANNELDOWN },
-	{ 0x00, 0x1e, KEY_VOLUMEUP },
-	{ 0x00, 0x0a, KEY_VOLUMEDOWN },
-	{ 0x00, 0x11, KEY_RECORD },
-	{ 0x00, 0x17, KEY_FAVORITES }, /* Heart symbol - Channel list. */
-	{ 0x00, 0x14, KEY_PLAY },
-	{ 0x00, 0x1a, KEY_STOP },
-	{ 0x00, 0x40, KEY_REWIND },
-	{ 0x00, 0x12, KEY_FASTFORWARD },
-	{ 0x00, 0x0e, KEY_PREVIOUS }, /* Recall - Previous channel. */
-	{ 0x00, 0x4c, KEY_PAUSE },
-	{ 0x00, 0x4d, KEY_SCREEN }, /* Full screen mode. */
-	{ 0x00, 0x54, KEY_AUDIO }, /* MTS - Switch to secondary audio. */
+	{ 0x0016, KEY_POWER },
+	{ 0x0010, KEY_MUTE },
+	{ 0x0003, KEY_1 },
+	{ 0x0001, KEY_2 },
+	{ 0x0006, KEY_3 },
+	{ 0x0009, KEY_4 },
+	{ 0x001d, KEY_5 },
+	{ 0x001f, KEY_6 },
+	{ 0x000d, KEY_7 },
+	{ 0x0019, KEY_8 },
+	{ 0x001b, KEY_9 },
+	{ 0x0015, KEY_0 },
+	{ 0x0005, KEY_CHANNELUP },
+	{ 0x0002, KEY_CHANNELDOWN },
+	{ 0x001e, KEY_VOLUMEUP },
+	{ 0x000a, KEY_VOLUMEDOWN },
+	{ 0x0011, KEY_RECORD },
+	{ 0x0017, KEY_FAVORITES }, /* Heart symbol - Channel list. */
+	{ 0x0014, KEY_PLAY },
+	{ 0x001a, KEY_STOP },
+	{ 0x0040, KEY_REWIND },
+	{ 0x0012, KEY_FASTFORWARD },
+	{ 0x000e, KEY_PREVIOUS }, /* Recall - Previous channel. */
+	{ 0x004c, KEY_PAUSE },
+	{ 0x004d, KEY_SCREEN }, /* Full screen mode. */
+	{ 0x0054, KEY_AUDIO }, /* MTS - Switch to secondary audio. */
 	/* additional keys TwinHan VisionPlus, the Artec seemingly not have */
-	{ 0x00, 0x0c, KEY_CANCEL }, /* Cancel */
-	{ 0x00, 0x1c, KEY_EPG }, /* EPG */
-	{ 0x00, 0x00, KEY_TAB }, /* Tab */
-	{ 0x00, 0x48, KEY_INFO }, /* Preview */
-	{ 0x00, 0x04, KEY_LIST }, /* RecordList */
-	{ 0x00, 0x0f, KEY_TEXT }, /* Teletext */
+	{ 0x000c, KEY_CANCEL }, /* Cancel */
+	{ 0x001c, KEY_EPG }, /* EPG */
+	{ 0x0000, KEY_TAB }, /* Tab */
+	{ 0x0048, KEY_INFO }, /* Preview */
+	{ 0x0004, KEY_LIST }, /* RecordList */
+	{ 0x000f, KEY_TEXT }, /* Teletext */
 	/* Key codes for the KWorld/ADSTech/JetWay remote. */
-	{ 0x86, 0x12, KEY_POWER },
-	{ 0x86, 0x0f, KEY_SELECT }, /* source */
-	{ 0x86, 0x0c, KEY_UNKNOWN }, /* scan */
-	{ 0x86, 0x0b, KEY_EPG },
-	{ 0x86, 0x10, KEY_MUTE },
-	{ 0x86, 0x01, KEY_1 },
-	{ 0x86, 0x02, KEY_2 },
-	{ 0x86, 0x03, KEY_3 },
-	{ 0x86, 0x04, KEY_4 },
-	{ 0x86, 0x05, KEY_5 },
-	{ 0x86, 0x06, KEY_6 },
-	{ 0x86, 0x07, KEY_7 },
-	{ 0x86, 0x08, KEY_8 },
-	{ 0x86, 0x09, KEY_9 },
-	{ 0x86, 0x0a, KEY_0 },
-	{ 0x86, 0x18, KEY_ZOOM },
-	{ 0x86, 0x1c, KEY_UNKNOWN }, /* preview */
-	{ 0x86, 0x13, KEY_UNKNOWN }, /* snap */
-	{ 0x86, 0x00, KEY_UNDO },
-	{ 0x86, 0x1d, KEY_RECORD },
-	{ 0x86, 0x0d, KEY_STOP },
-	{ 0x86, 0x0e, KEY_PAUSE },
-	{ 0x86, 0x16, KEY_PLAY },
-	{ 0x86, 0x11, KEY_BACK },
-	{ 0x86, 0x19, KEY_FORWARD },
-	{ 0x86, 0x14, KEY_UNKNOWN }, /* pip */
-	{ 0x86, 0x15, KEY_ESC },
-	{ 0x86, 0x1a, KEY_UP },
-	{ 0x86, 0x1e, KEY_DOWN },
-	{ 0x86, 0x1f, KEY_LEFT },
-	{ 0x86, 0x1b, KEY_RIGHT },
+	{ 0x8612, KEY_POWER },
+	{ 0x860f, KEY_SELECT }, /* source */
+	{ 0x860c, KEY_UNKNOWN }, /* scan */
+	{ 0x860b, KEY_EPG },
+	{ 0x8610, KEY_MUTE },
+	{ 0x8601, KEY_1 },
+	{ 0x8602, KEY_2 },
+	{ 0x8603, KEY_3 },
+	{ 0x8604, KEY_4 },
+	{ 0x8605, KEY_5 },
+	{ 0x8606, KEY_6 },
+	{ 0x8607, KEY_7 },
+	{ 0x8608, KEY_8 },
+	{ 0x8609, KEY_9 },
+	{ 0x860a, KEY_0 },
+	{ 0x8618, KEY_ZOOM },
+	{ 0x861c, KEY_UNKNOWN }, /* preview */
+	{ 0x8613, KEY_UNKNOWN }, /* snap */
+	{ 0x8600, KEY_UNDO },
+	{ 0x861d, KEY_RECORD },
+	{ 0x860d, KEY_STOP },
+	{ 0x860e, KEY_PAUSE },
+	{ 0x8616, KEY_PLAY },
+	{ 0x8611, KEY_BACK },
+	{ 0x8619, KEY_FORWARD },
+	{ 0x8614, KEY_UNKNOWN }, /* pip */
+	{ 0x8615, KEY_ESC },
+	{ 0x861a, KEY_UP },
+	{ 0x861e, KEY_DOWN },
+	{ 0x861f, KEY_LEFT },
+	{ 0x861b, KEY_RIGHT },
 
 	/* Key codes for the DiBcom MOD3000 remote. */
-	{ 0x80, 0x00, KEY_MUTE },
-	{ 0x80, 0x01, KEY_TEXT },
-	{ 0x80, 0x02, KEY_HOME },
-	{ 0x80, 0x03, KEY_POWER },
-
-	{ 0x80, 0x04, KEY_RED },
-	{ 0x80, 0x05, KEY_GREEN },
-	{ 0x80, 0x06, KEY_YELLOW },
-	{ 0x80, 0x07, KEY_BLUE },
-
-	{ 0x80, 0x08, KEY_DVD },
-	{ 0x80, 0x09, KEY_AUDIO },
-	{ 0x80, 0x0a, KEY_MEDIA },      /* Pictures */
-	{ 0x80, 0x0b, KEY_VIDEO },
-
-	{ 0x80, 0x0c, KEY_BACK },
-	{ 0x80, 0x0d, KEY_UP },
-	{ 0x80, 0x0e, KEY_RADIO },
-	{ 0x80, 0x0f, KEY_EPG },
-
-	{ 0x80, 0x10, KEY_LEFT },
-	{ 0x80, 0x11, KEY_OK },
-	{ 0x80, 0x12, KEY_RIGHT },
-	{ 0x80, 0x13, KEY_UNKNOWN },    /* SAP */
-
-	{ 0x80, 0x14, KEY_TV },
-	{ 0x80, 0x15, KEY_DOWN },
-	{ 0x80, 0x16, KEY_MENU },       /* DVD Menu */
-	{ 0x80, 0x17, KEY_LAST },
-
-	{ 0x80, 0x18, KEY_RECORD },
-	{ 0x80, 0x19, KEY_STOP },
-	{ 0x80, 0x1a, KEY_PAUSE },
-	{ 0x80, 0x1b, KEY_PLAY },
-
-	{ 0x80, 0x1c, KEY_PREVIOUS },
-	{ 0x80, 0x1d, KEY_REWIND },
-	{ 0x80, 0x1e, KEY_FASTFORWARD },
-	{ 0x80, 0x1f, KEY_NEXT},
-
-	{ 0x80, 0x40, KEY_1 },
-	{ 0x80, 0x41, KEY_2 },
-	{ 0x80, 0x42, KEY_3 },
-	{ 0x80, 0x43, KEY_CHANNELUP },
-
-	{ 0x80, 0x44, KEY_4 },
-	{ 0x80, 0x45, KEY_5 },
-	{ 0x80, 0x46, KEY_6 },
-	{ 0x80, 0x47, KEY_CHANNELDOWN },
-
-	{ 0x80, 0x48, KEY_7 },
-	{ 0x80, 0x49, KEY_8 },
-	{ 0x80, 0x4a, KEY_9 },
-	{ 0x80, 0x4b, KEY_VOLUMEUP },
-
-	{ 0x80, 0x4c, KEY_CLEAR },
-	{ 0x80, 0x4d, KEY_0 },
-	{ 0x80, 0x4e, KEY_ENTER },
-	{ 0x80, 0x4f, KEY_VOLUMEDOWN },
+	{ 0x8000, KEY_MUTE },
+	{ 0x8001, KEY_TEXT },
+	{ 0x8002, KEY_HOME },
+	{ 0x8003, KEY_POWER },
+
+	{ 0x8004, KEY_RED },
+	{ 0x8005, KEY_GREEN },
+	{ 0x8006, KEY_YELLOW },
+	{ 0x8007, KEY_BLUE },
+
+	{ 0x8008, KEY_DVD },
+	{ 0x8009, KEY_AUDIO },
+	{ 0x800a, KEY_MEDIA },      /* Pictures */
+	{ 0x800b, KEY_VIDEO },
+
+	{ 0x800c, KEY_BACK },
+	{ 0x800d, KEY_UP },
+	{ 0x800e, KEY_RADIO },
+	{ 0x800f, KEY_EPG },
+
+	{ 0x8010, KEY_LEFT },
+	{ 0x8011, KEY_OK },
+	{ 0x8012, KEY_RIGHT },
+	{ 0x8013, KEY_UNKNOWN },    /* SAP */
+
+	{ 0x8014, KEY_TV },
+	{ 0x8015, KEY_DOWN },
+	{ 0x8016, KEY_MENU },       /* DVD Menu */
+	{ 0x8017, KEY_LAST },
+
+	{ 0x8018, KEY_RECORD },
+	{ 0x8019, KEY_STOP },
+	{ 0x801a, KEY_PAUSE },
+	{ 0x801b, KEY_PLAY },
+
+	{ 0x801c, KEY_PREVIOUS },
+	{ 0x801d, KEY_REWIND },
+	{ 0x801e, KEY_FASTFORWARD },
+	{ 0x801f, KEY_NEXT},
+
+	{ 0x8040, KEY_1 },
+	{ 0x8041, KEY_2 },
+	{ 0x8042, KEY_3 },
+	{ 0x8043, KEY_CHANNELUP },
+
+	{ 0x8044, KEY_4 },
+	{ 0x8045, KEY_5 },
+	{ 0x8046, KEY_6 },
+	{ 0x8047, KEY_CHANNELDOWN },
+
+	{ 0x8048, KEY_7 },
+	{ 0x8049, KEY_8 },
+	{ 0x804a, KEY_9 },
+	{ 0x804b, KEY_VOLUMEUP },
+
+	{ 0x804c, KEY_CLEAR },
+	{ 0x804d, KEY_0 },
+	{ 0x804e, KEY_ENTER },
+	{ 0x804f, KEY_VOLUMEDOWN },
 };
 EXPORT_SYMBOL(dibusb_rc_keys);
 

+ 8 - 2
drivers/media/dvb/dvb-usb/dibusb-mc.c

@@ -42,6 +42,8 @@ static struct usb_device_id dibusb_dib3000mc_table [] = {
 /* 11 */	{ USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC,	USB_PID_ARTEC_T14_WARM) },
 /* 12 */	{ USB_DEVICE(USB_VID_LEADTEK,		USB_PID_WINFAST_DTV_DONGLE_COLD) },
 /* 13 */	{ USB_DEVICE(USB_VID_LEADTEK,		USB_PID_WINFAST_DTV_DONGLE_WARM) },
+/* 14 */	{ USB_DEVICE(USB_VID_HUMAX_COEX,	USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD) },
+/* 15 */	{ USB_DEVICE(USB_VID_HUMAX_COEX,	USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM) },
 			{ }		/* Terminating entry */
 };
 MODULE_DEVICE_TABLE (usb, dibusb_dib3000mc_table);
@@ -66,7 +68,7 @@ static struct dvb_usb_device_properties dibusb_mc_properties = {
 	/* parameter for the MPEG2-data transfer */
 			.stream = {
 				.type = USB_BULK,
-				.count = 7,
+				.count = 8,
 				.endpoint = 0x06,
 				.u = {
 					.bulk = {
@@ -88,7 +90,7 @@ static struct dvb_usb_device_properties dibusb_mc_properties = {
 
 	.generic_bulk_ctrl_endpoint = 0x01,
 
-	.num_device_descs = 7,
+	.num_device_descs = 8,
 	.devices = {
 		{   "DiBcom USB2.0 DVB-T reference design (MOD3000P)",
 			{ &dibusb_dib3000mc_table[0], NULL },
@@ -119,6 +121,10 @@ static struct dvb_usb_device_properties dibusb_mc_properties = {
 			{ &dibusb_dib3000mc_table[12], NULL },
 			{ &dibusb_dib3000mc_table[13], NULL },
 		},
+		{   "Humax/Coex DVB-T USB Stick 2.0 High Speed",
+			{ &dibusb_dib3000mc_table[14], NULL },
+			{ &dibusb_dib3000mc_table[15], NULL },
+		},
 		{ NULL },
 	}
 };

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

@@ -162,61 +162,61 @@ static int digitv_tuner_attach(struct dvb_usb_adapter *adap)
 }
 
 static struct dvb_usb_rc_key digitv_rc_keys[] = {
-	{ 0x5f, 0x55, KEY_0 },
-	{ 0x6f, 0x55, KEY_1 },
-	{ 0x9f, 0x55, KEY_2 },
-	{ 0xaf, 0x55, KEY_3 },
-	{ 0x5f, 0x56, KEY_4 },
-	{ 0x6f, 0x56, KEY_5 },
-	{ 0x9f, 0x56, KEY_6 },
-	{ 0xaf, 0x56, KEY_7 },
-	{ 0x5f, 0x59, KEY_8 },
-	{ 0x6f, 0x59, KEY_9 },
-	{ 0x9f, 0x59, KEY_TV },
-	{ 0xaf, 0x59, KEY_AUX },
-	{ 0x5f, 0x5a, KEY_DVD },
-	{ 0x6f, 0x5a, KEY_POWER },
-	{ 0x9f, 0x5a, KEY_MHP },     /* labelled 'Picture' */
-	{ 0xaf, 0x5a, KEY_AUDIO },
-	{ 0x5f, 0x65, KEY_INFO },
-	{ 0x6f, 0x65, KEY_F13 },     /* 16:9 */
-	{ 0x9f, 0x65, KEY_F14 },     /* 14:9 */
-	{ 0xaf, 0x65, KEY_EPG },
-	{ 0x5f, 0x66, KEY_EXIT },
-	{ 0x6f, 0x66, KEY_MENU },
-	{ 0x9f, 0x66, KEY_UP },
-	{ 0xaf, 0x66, KEY_DOWN },
-	{ 0x5f, 0x69, KEY_LEFT },
-	{ 0x6f, 0x69, KEY_RIGHT },
-	{ 0x9f, 0x69, KEY_ENTER },
-	{ 0xaf, 0x69, KEY_CHANNELUP },
-	{ 0x5f, 0x6a, KEY_CHANNELDOWN },
-	{ 0x6f, 0x6a, KEY_VOLUMEUP },
-	{ 0x9f, 0x6a, KEY_VOLUMEDOWN },
-	{ 0xaf, 0x6a, KEY_RED },
-	{ 0x5f, 0x95, KEY_GREEN },
-	{ 0x6f, 0x95, KEY_YELLOW },
-	{ 0x9f, 0x95, KEY_BLUE },
-	{ 0xaf, 0x95, KEY_SUBTITLE },
-	{ 0x5f, 0x96, KEY_F15 },     /* AD */
-	{ 0x6f, 0x96, KEY_TEXT },
-	{ 0x9f, 0x96, KEY_MUTE },
-	{ 0xaf, 0x96, KEY_REWIND },
-	{ 0x5f, 0x99, KEY_STOP },
-	{ 0x6f, 0x99, KEY_PLAY },
-	{ 0x9f, 0x99, KEY_FASTFORWARD },
-	{ 0xaf, 0x99, KEY_F16 },     /* chapter */
-	{ 0x5f, 0x9a, KEY_PAUSE },
-	{ 0x6f, 0x9a, KEY_PLAY },
-	{ 0x9f, 0x9a, KEY_RECORD },
-	{ 0xaf, 0x9a, KEY_F17 },     /* picture in picture */
-	{ 0x5f, 0xa5, KEY_KPPLUS },  /* zoom in */
-	{ 0x6f, 0xa5, KEY_KPMINUS }, /* zoom out */
-	{ 0x9f, 0xa5, KEY_F18 },     /* capture */
-	{ 0xaf, 0xa5, KEY_F19 },     /* web */
-	{ 0x5f, 0xa6, KEY_EMAIL },
-	{ 0x6f, 0xa6, KEY_PHONE },
-	{ 0x9f, 0xa6, KEY_PC },
+	{ 0x5f55, KEY_0 },
+	{ 0x6f55, KEY_1 },
+	{ 0x9f55, KEY_2 },
+	{ 0xaf55, KEY_3 },
+	{ 0x5f56, KEY_4 },
+	{ 0x6f56, KEY_5 },
+	{ 0x9f56, KEY_6 },
+	{ 0xaf56, KEY_7 },
+	{ 0x5f59, KEY_8 },
+	{ 0x6f59, KEY_9 },
+	{ 0x9f59, KEY_TV },
+	{ 0xaf59, KEY_AUX },
+	{ 0x5f5a, KEY_DVD },
+	{ 0x6f5a, KEY_POWER },
+	{ 0x9f5a, KEY_MHP },     /* labelled 'Picture' */
+	{ 0xaf5a, KEY_AUDIO },
+	{ 0x5f65, KEY_INFO },
+	{ 0x6f65, KEY_F13 },     /* 16:9 */
+	{ 0x9f65, KEY_F14 },     /* 14:9 */
+	{ 0xaf65, KEY_EPG },
+	{ 0x5f66, KEY_EXIT },
+	{ 0x6f66, KEY_MENU },
+	{ 0x9f66, KEY_UP },
+	{ 0xaf66, KEY_DOWN },
+	{ 0x5f69, KEY_LEFT },
+	{ 0x6f69, KEY_RIGHT },
+	{ 0x9f69, KEY_ENTER },
+	{ 0xaf69, KEY_CHANNELUP },
+	{ 0x5f6a, KEY_CHANNELDOWN },
+	{ 0x6f6a, KEY_VOLUMEUP },
+	{ 0x9f6a, KEY_VOLUMEDOWN },
+	{ 0xaf6a, KEY_RED },
+	{ 0x5f95, KEY_GREEN },
+	{ 0x6f95, KEY_YELLOW },
+	{ 0x9f95, KEY_BLUE },
+	{ 0xaf95, KEY_SUBTITLE },
+	{ 0x5f96, KEY_F15 },     /* AD */
+	{ 0x6f96, KEY_TEXT },
+	{ 0x9f96, KEY_MUTE },
+	{ 0xaf96, KEY_REWIND },
+	{ 0x5f99, KEY_STOP },
+	{ 0x6f99, KEY_PLAY },
+	{ 0x9f99, KEY_FASTFORWARD },
+	{ 0xaf99, KEY_F16 },     /* chapter */
+	{ 0x5f9a, KEY_PAUSE },
+	{ 0x6f9a, KEY_PLAY },
+	{ 0x9f9a, KEY_RECORD },
+	{ 0xaf9a, KEY_F17 },     /* picture in picture */
+	{ 0x5fa5, KEY_KPPLUS },  /* zoom in */
+	{ 0x6fa5, KEY_KPMINUS }, /* zoom out */
+	{ 0x9fa5, KEY_F18 },     /* capture */
+	{ 0xafa5, KEY_F19 },     /* web */
+	{ 0x5fa6, KEY_EMAIL },
+	{ 0x6fa6, KEY_PHONE },
+	{ 0x9fa6, KEY_PC },
 };
 
 static int digitv_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
@@ -238,8 +238,8 @@ static int digitv_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
 	if (key[1] != 0)
 	{
 		  for (i = 0; i < d->props.rc_key_map_size; i++) {
-			if (d->props.rc_key_map[i].custom == key[1] &&
-			    d->props.rc_key_map[i].data == key[2]) {
+			if (rc5_custom(&d->props.rc_key_map[i]) == key[1] &&
+			    rc5_data(&d->props.rc_key_map[i]) == key[2]) {
 				*event = d->props.rc_key_map[i].event;
 				*state = REMOTE_KEY_PRESSED;
 				return 0;

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

@@ -58,24 +58,24 @@ static int dtt200u_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid,
 /* remote control */
 /* key list for the tiny remote control (Yakumo, don't know about the others) */
 static struct dvb_usb_rc_key dtt200u_rc_keys[] = {
-	{ 0x80, 0x01, KEY_MUTE },
-	{ 0x80, 0x02, KEY_CHANNELDOWN },
-	{ 0x80, 0x03, KEY_VOLUMEDOWN },
-	{ 0x80, 0x04, KEY_1 },
-	{ 0x80, 0x05, KEY_2 },
-	{ 0x80, 0x06, KEY_3 },
-	{ 0x80, 0x07, KEY_4 },
-	{ 0x80, 0x08, KEY_5 },
-	{ 0x80, 0x09, KEY_6 },
-	{ 0x80, 0x0a, KEY_7 },
-	{ 0x80, 0x0c, KEY_ZOOM },
-	{ 0x80, 0x0d, KEY_0 },
-	{ 0x80, 0x0e, KEY_SELECT },
-	{ 0x80, 0x12, KEY_POWER },
-	{ 0x80, 0x1a, KEY_CHANNELUP },
-	{ 0x80, 0x1b, KEY_8 },
-	{ 0x80, 0x1e, KEY_VOLUMEUP },
-	{ 0x80, 0x1f, KEY_9 },
+	{ 0x8001, KEY_MUTE },
+	{ 0x8002, KEY_CHANNELDOWN },
+	{ 0x8003, KEY_VOLUMEDOWN },
+	{ 0x8004, KEY_1 },
+	{ 0x8005, KEY_2 },
+	{ 0x8006, KEY_3 },
+	{ 0x8007, KEY_4 },
+	{ 0x8008, KEY_5 },
+	{ 0x8009, KEY_6 },
+	{ 0x800a, KEY_7 },
+	{ 0x800c, KEY_ZOOM },
+	{ 0x800d, KEY_0 },
+	{ 0x800e, KEY_SELECT },
+	{ 0x8012, KEY_POWER },
+	{ 0x801a, KEY_CHANNELUP },
+	{ 0x801b, KEY_8 },
+	{ 0x801e, KEY_VOLUMEUP },
+	{ 0x801f, KEY_9 },
 };
 
 static int dtt200u_rc_query(struct dvb_usb_device *d, u32 *event, int *state)

+ 1 - 1
drivers/media/dvb/dvb-usb/dvb-usb-i2c.c

@@ -19,7 +19,7 @@ int dvb_usb_i2c_init(struct dvb_usb_device *d)
 		return -EINVAL;
 	}
 
-	strncpy(d->i2c_adap.name, d->desc->name, sizeof(d->i2c_adap.name));
+	strlcpy(d->i2c_adap.name, d->desc->name, sizeof(d->i2c_adap.name));
 	d->i2c_adap.class = I2C_CLASS_TV_DIGITAL,
 	d->i2c_adap.algo      = d->props.i2c_algo;
 	d->i2c_adap.algo_data = NULL;

+ 6 - 0
drivers/media/dvb/dvb-usb/dvb-usb-ids.h

@@ -58,6 +58,7 @@
 #define USB_VID_GIGABYTE			0x1044
 #define USB_VID_YUAN				0x1164
 #define USB_VID_XTENSIONS			0x1ae7
+#define USB_VID_HUMAX_COEX			0x10b9
 
 /* Product IDs */
 #define USB_PID_ADSTECH_USB2_COLD			0xa333
@@ -103,6 +104,7 @@
 #define USB_PID_GRANDTEC_DVBT_USB_WARM			0x0fa1
 #define USB_PID_INTEL_CE9500				0x9500
 #define USB_PID_KWORLD_399U				0xe399
+#define USB_PID_KWORLD_399U_2				0xe400
 #define USB_PID_KWORLD_395U				0xe396
 #define USB_PID_KWORLD_395U_2				0xe39b
 #define USB_PID_KWORLD_395U_3				0xe395
@@ -252,6 +254,8 @@
 #define USB_PID_YUAN_STK7700PH				0x1f08
 #define USB_PID_YUAN_PD378S				0x2edc
 #define USB_PID_YUAN_MC770				0x0871
+#define USB_PID_YUAN_STK7700D				0x1efc
+#define USB_PID_YUAN_STK7700D_2				0x1e8c
 #define USB_PID_DW2102					0x2102
 #define USB_PID_XTENSIONS_XD_380			0x0381
 #define USB_PID_TELESTAR_STARSTICK_2			0x8000
@@ -259,5 +263,7 @@
 #define USB_PID_SONY_PLAYTV				0x0003
 #define USB_PID_ELGATO_EYETV_DTT			0x0021
 #define USB_PID_ELGATO_EYETV_DTT_Dlx			0x0020
+#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD		0x5000
+#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM		0x5001
 
 #endif

+ 71 - 2
drivers/media/dvb/dvb-usb/dvb-usb-remote.c

@@ -8,6 +8,71 @@
 #include "dvb-usb-common.h"
 #include <linux/usb/input.h>
 
+static int dvb_usb_getkeycode(struct input_dev *dev,
+				    int scancode, int *keycode)
+{
+	struct dvb_usb_device *d = input_get_drvdata(dev);
+
+	struct dvb_usb_rc_key *keymap = d->props.rc_key_map;
+	int i;
+
+	/* See if we can match the raw key code. */
+	for (i = 0; i < d->props.rc_key_map_size; i++)
+		if (keymap[i].scan == scancode) {
+			*keycode = keymap[i].event;
+			return 0;
+		}
+
+	/*
+	 * If is there extra space, returns KEY_RESERVED,
+	 * otherwise, input core won't let dvb_usb_setkeycode
+	 * to work
+	 */
+	for (i = 0; i < d->props.rc_key_map_size; i++)
+		if (keymap[i].event == KEY_RESERVED ||
+		    keymap[i].event == KEY_UNKNOWN) {
+			*keycode = KEY_RESERVED;
+			return 0;
+		}
+
+	return -EINVAL;
+}
+
+static int dvb_usb_setkeycode(struct input_dev *dev,
+				    int scancode, int keycode)
+{
+	struct dvb_usb_device *d = input_get_drvdata(dev);
+
+	struct dvb_usb_rc_key *keymap = d->props.rc_key_map;
+	int i;
+
+	/* Search if it is replacing an existing keycode */
+	for (i = 0; i < d->props.rc_key_map_size; i++)
+		if (keymap[i].scan == scancode) {
+			keymap[i].event = keycode;
+			return 0;
+		}
+
+	/* Search if is there a clean entry. If so, use it */
+	for (i = 0; i < d->props.rc_key_map_size; i++)
+		if (keymap[i].event == KEY_RESERVED ||
+		    keymap[i].event == KEY_UNKNOWN) {
+			keymap[i].scan = scancode;
+			keymap[i].event = keycode;
+			return 0;
+		}
+
+	/*
+	 * FIXME: Currently, it is not possible to increase the size of
+	 * scancode table. For it to happen, one possibility
+	 * would be to allocate a table with key_map_size + 1,
+	 * copying data, appending the new key on it, and freeing
+	 * the old one - or maybe just allocating some spare space
+	 */
+
+	return -EINVAL;
+}
+
 /* Remote-control poll function - called every dib->rc_query_interval ms to see
  * whether the remote control has received anything.
  *
@@ -111,6 +176,8 @@ int dvb_usb_remote_init(struct dvb_usb_device *d)
 	input_dev->phys = d->rc_phys;
 	usb_to_input_id(d->udev, &input_dev->id);
 	input_dev->dev.parent = &d->udev->dev;
+	input_dev->getkeycode = dvb_usb_getkeycode;
+	input_dev->setkeycode = dvb_usb_setkeycode;
 
 	/* set the bits for the keys */
 	deb_rc("key map size: %d\n", d->props.rc_key_map_size);
@@ -128,6 +195,8 @@ int dvb_usb_remote_init(struct dvb_usb_device *d)
 	input_dev->rep[REP_PERIOD] = d->props.rc_interval;
 	input_dev->rep[REP_DELAY]  = d->props.rc_interval + 150;
 
+	input_set_drvdata(input_dev, d);
+
 	err = input_register_device(input_dev);
 	if (err) {
 		input_free_device(input_dev);
@@ -178,8 +247,8 @@ int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *d,
 			}
 			/* See if we can match the raw key code. */
 			for (i = 0; i < d->props.rc_key_map_size; i++)
-				if (keymap[i].custom == keybuf[1] &&
-					keymap[i].data == keybuf[3]) {
+				if (rc5_custom(&keymap[i]) == keybuf[1] &&
+					rc5_data(&keymap[i]) == keybuf[3]) {
 					*event = keymap[i].event;
 					*state = REMOTE_KEY_PRESSED;
 					return 0;

+ 16 - 1
drivers/media/dvb/dvb-usb/dvb-usb.h

@@ -81,10 +81,25 @@ struct dvb_usb_device_description {
  * @event: the input event assigned to key identified by custom and data
  */
 struct dvb_usb_rc_key {
-	u8 custom,data;
+	u16 scan;
 	u32 event;
 };
 
+static inline u8 rc5_custom(struct dvb_usb_rc_key *key)
+{
+	return (key->scan >> 8) & 0xff;
+}
+
+static inline u8 rc5_data(struct dvb_usb_rc_key *key)
+{
+	return key->scan & 0xff;
+}
+
+static inline u8 rc5_scan(struct dvb_usb_rc_key *key)
+{
+	return key->scan & 0xffff;
+}
+
 struct dvb_usb_device;
 struct dvb_usb_adapter;
 struct usb_data_stream;

+ 289 - 114
drivers/media/dvb/dvb-usb/dw2102.c

@@ -1,6 +1,6 @@
 /* DVB USB framework compliant Linux driver for the
 *	DVBWorld DVB-S 2101, 2102, DVB-S2 2104, DVB-C 3101,
-*	TeVii S600, S650 Cards
+*	TeVii S600, S630, S650 Cards
 * Copyright (C) 2008,2009 Igor M. Liplianin (liplianin@me.by)
 *
 *	This program is free software; you can redistribute it and/or modify it
@@ -18,6 +18,8 @@
 #include "eds1547.h"
 #include "cx24116.h"
 #include "tda1002x.h"
+#include "mt312.h"
+#include "zl10039.h"
 
 #ifndef USB_PID_DW2102
 #define USB_PID_DW2102 0x2102
@@ -39,6 +41,10 @@
 #define USB_PID_TEVII_S650 0xd650
 #endif
 
+#ifndef USB_PID_TEVII_S630
+#define USB_PID_TEVII_S630 0xd630
+#endif
+
 #define DW210X_READ_MSG 0
 #define DW210X_WRITE_MSG 1
 
@@ -436,6 +442,69 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
 	return num;
 }
 
+static int s630_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 */
+		u8 ibuf[msg[1].len], obuf[3];
+		obuf[0] = msg[1].len;
+		obuf[1] = (msg[0].addr << 1);
+		obuf[2] = msg[0].buf[0];
+
+		ret = dw210x_op_rw(d->udev, 0x90, 0, 0,
+					obuf, 3, DW210X_WRITE_MSG);
+		msleep(5);
+		ret = dw210x_op_rw(d->udev, 0x91, 0, 0,
+					ibuf, msg[1].len, DW210X_READ_MSG);
+		memcpy(msg[1].buf, ibuf, msg[1].len);
+		break;
+	}
+	case 1:
+		switch (msg[0].addr) {
+		case 0x60:
+		case 0x0e: {
+			/* write to zl10313, zl10039 register, */
+			u8 obuf[msg[0].len + 2];
+			obuf[0] = msg[0].len + 1;
+			obuf[1] = (msg[0].addr << 1);
+			memcpy(obuf + 2, msg[0].buf, msg[0].len);
+			ret = dw210x_op_rw(d->udev, 0x80, 0, 0,
+					obuf, msg[0].len + 2, DW210X_WRITE_MSG);
+			break;
+		}
+		case (DW2102_RC_QUERY): {
+			u8 ibuf[4];
+			ret  = dw210x_op_rw(d->udev, 0xb8, 0, 0,
+					ibuf, 4, DW210X_READ_MSG);
+			msg[0].buf[0] = ibuf[3];
+			break;
+		}
+		case (DW2102_VOLTAGE_CTRL): {
+			u8 obuf[2];
+			obuf[0] = 0x03;
+			obuf[1] = msg[0].buf[0];
+			ret = dw210x_op_rw(d->udev, 0x8a, 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;
@@ -466,6 +535,11 @@ static struct i2c_algorithm dw3101_i2c_algo = {
 	.functionality = dw210x_i2c_func,
 };
 
+static struct i2c_algorithm s630_i2c_algo = {
+	.master_xfer = s630_i2c_transfer,
+	.functionality = dw210x_i2c_func,
+};
+
 static int dw210x_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
 {
 	int i;
@@ -490,6 +564,37 @@ static int dw210x_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
 	return 0;
 };
 
+static int s630_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
+{
+	int i, ret;
+	u8 buf[3], eeprom[256], eepromline[16];
+
+	for (i = 0; i < 256; i++) {
+		buf[0] = 1;
+		buf[1] = 0xa0;
+		buf[2] = i;
+		ret = dw210x_op_rw(d->udev, 0x90, 0, 0,
+					buf, 3, DW210X_WRITE_MSG);
+		ret = dw210x_op_rw(d->udev, 0x91, 0, 0,
+					buf, 1, DW210X_READ_MSG);
+		if (ret < 0) {
+			err("read eeprom failed.");
+			return -1;
+		} else {
+			eepromline[i % 16] = buf[0];
+			eeprom[i] = buf[0];
+		}
+
+		if ((i % 16) == 15) {
+			deb_xfer("%02x: ", i - 15);
+			debug_dump(eepromline, 16, deb_xfer);
+		}
+	}
+
+	memcpy(mac, eeprom + 16, 6);
+	return 0;
+};
+
 static int dw210x_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
 {
 	static u8 command_13v[1] = {0x00};
@@ -535,6 +640,10 @@ static struct tda10023_config dw3101_tda10023_config = {
 	.invert = 1,
 };
 
+static struct mt312_config zl313_config = {
+	.demod_address = 0x0e,
+};
+
 static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
 {
 	if ((d->fe = dvb_attach(cx24116_attach, &dw2104_config,
@@ -596,6 +705,18 @@ static int dw3101_frontend_attach(struct dvb_usb_adapter *d)
 	return -EIO;
 }
 
+static int s630_frontend_attach(struct dvb_usb_adapter *d)
+{
+	d->fe = dvb_attach(mt312_attach, &zl313_config,
+				&d->dev->i2c_adap);
+	if (d->fe != NULL) {
+		d->fe->ops.set_voltage = dw210x_set_voltage;
+		info("Attached zl10313!\n");
+		return 0;
+	}
+	return -EIO;
+}
+
 static int dw2102_tuner_attach(struct dvb_usb_adapter *adap)
 {
 	dvb_attach(dvb_pll_attach, adap->fe, 0x60,
@@ -619,123 +740,131 @@ static int dw3101_tuner_attach(struct dvb_usb_adapter *adap)
 	return 0;
 }
 
+static int s630_zl10039_tuner_attach(struct dvb_usb_adapter *adap)
+{
+	dvb_attach(zl10039_attach, adap->fe, 0x60,
+		&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 },
-	{ 0xf8,	0x12, KEY_2 },
-	{ 0xf8,	0x13, KEY_3 },
-	{ 0xf8,	0x14, KEY_4 },
-	{ 0xf8,	0x15, KEY_5 },
-	{ 0xf8,	0x16, KEY_6 },
-	{ 0xf8,	0x17, KEY_7 },
-	{ 0xf8,	0x18, KEY_8 },
-	{ 0xf8,	0x19, KEY_9 },
-	{ 0xf8, 0x10, KEY_0 },
-	{ 0xf8, 0x1c, KEY_PAGEUP },	/*ch+*/
-	{ 0xf8, 0x0f, KEY_PAGEDOWN },	/*ch-*/
-	{ 0xf8, 0x1a, KEY_O },		/*vol+*/
-	{ 0xf8, 0x0e, KEY_Z },		/*vol-*/
-	{ 0xf8, 0x04, KEY_R },		/*rec*/
-	{ 0xf8, 0x09, KEY_D },		/*fav*/
-	{ 0xf8, 0x08, KEY_BACKSPACE },	/*rewind*/
-	{ 0xf8, 0x07, KEY_A },		/*fast*/
-	{ 0xf8, 0x0b, KEY_P },		/*pause*/
-	{ 0xf8, 0x02, KEY_ESC },	/*cancel*/
-	{ 0xf8, 0x03, KEY_G },		/*tab*/
-	{ 0xf8, 0x00, KEY_UP },		/*up*/
-	{ 0xf8, 0x1f, KEY_ENTER },	/*ok*/
-	{ 0xf8, 0x01, KEY_DOWN },	/*down*/
-	{ 0xf8, 0x05, KEY_C },		/*cap*/
-	{ 0xf8, 0x06, KEY_S },		/*stop*/
-	{ 0xf8, 0x40, KEY_F },		/*full*/
-	{ 0xf8, 0x1e, KEY_W },		/*tvmode*/
-	{ 0xf8, 0x1b, KEY_B },		/*recall*/
+	{ 0xf80a, KEY_Q },		/*power*/
+	{ 0xf80c, KEY_M },		/*mute*/
+	{ 0xf811, KEY_1 },
+	{ 0xf812, KEY_2 },
+	{ 0xf813, KEY_3 },
+	{ 0xf814, KEY_4 },
+	{ 0xf815, KEY_5 },
+	{ 0xf816, KEY_6 },
+	{ 0xf817, KEY_7 },
+	{ 0xf818, KEY_8 },
+	{ 0xf819, KEY_9 },
+	{ 0xf810, KEY_0 },
+	{ 0xf81c, KEY_PAGEUP },	/*ch+*/
+	{ 0xf80f, KEY_PAGEDOWN },	/*ch-*/
+	{ 0xf81a, KEY_O },		/*vol+*/
+	{ 0xf80e, KEY_Z },		/*vol-*/
+	{ 0xf804, KEY_R },		/*rec*/
+	{ 0xf809, KEY_D },		/*fav*/
+	{ 0xf808, KEY_BACKSPACE },	/*rewind*/
+	{ 0xf807, KEY_A },		/*fast*/
+	{ 0xf80b, KEY_P },		/*pause*/
+	{ 0xf802, KEY_ESC },	/*cancel*/
+	{ 0xf803, KEY_G },		/*tab*/
+	{ 0xf800, KEY_UP },		/*up*/
+	{ 0xf81f, KEY_ENTER },	/*ok*/
+	{ 0xf801, KEY_DOWN },	/*down*/
+	{ 0xf805, KEY_C },		/*cap*/
+	{ 0xf806, KEY_S },		/*stop*/
+	{ 0xf840, KEY_F },		/*full*/
+	{ 0xf81e, KEY_W },		/*tvmode*/
+	{ 0xf81b, KEY_B },		/*recall*/
 };
 
 static struct dvb_usb_rc_key tevii_rc_keys[] = {
-	{ 0xf8, 0x0a, KEY_POWER },
-	{ 0xf8, 0x0c, KEY_MUTE },
-	{ 0xf8, 0x11, KEY_1 },
-	{ 0xf8, 0x12, KEY_2 },
-	{ 0xf8, 0x13, KEY_3 },
-	{ 0xf8, 0x14, KEY_4 },
-	{ 0xf8, 0x15, KEY_5 },
-	{ 0xf8, 0x16, KEY_6 },
-	{ 0xf8, 0x17, KEY_7 },
-	{ 0xf8, 0x18, KEY_8 },
-	{ 0xf8, 0x19, KEY_9 },
-	{ 0xf8, 0x10, KEY_0 },
-	{ 0xf8, 0x1c, KEY_MENU },
-	{ 0xf8, 0x0f, KEY_VOLUMEDOWN },
-	{ 0xf8, 0x1a, KEY_LAST },
-	{ 0xf8, 0x0e, KEY_OPEN },
-	{ 0xf8, 0x04, KEY_RECORD },
-	{ 0xf8, 0x09, KEY_VOLUMEUP },
-	{ 0xf8, 0x08, KEY_CHANNELUP },
-	{ 0xf8, 0x07, KEY_PVR },
-	{ 0xf8, 0x0b, KEY_TIME },
-	{ 0xf8, 0x02, KEY_RIGHT },
-	{ 0xf8, 0x03, KEY_LEFT },
-	{ 0xf8, 0x00, KEY_UP },
-	{ 0xf8, 0x1f, KEY_OK },
-	{ 0xf8, 0x01, KEY_DOWN },
-	{ 0xf8, 0x05, KEY_TUNER },
-	{ 0xf8, 0x06, KEY_CHANNELDOWN },
-	{ 0xf8, 0x40, KEY_PLAYPAUSE },
-	{ 0xf8, 0x1e, KEY_REWIND },
-	{ 0xf8, 0x1b, KEY_FAVORITES },
-	{ 0xf8, 0x1d, KEY_BACK },
-	{ 0xf8, 0x4d, KEY_FASTFORWARD },
-	{ 0xf8, 0x44, KEY_EPG },
-	{ 0xf8, 0x4c, KEY_INFO },
-	{ 0xf8, 0x41, KEY_AB },
-	{ 0xf8, 0x43, KEY_AUDIO },
-	{ 0xf8, 0x45, KEY_SUBTITLE },
-	{ 0xf8, 0x4a, KEY_LIST },
-	{ 0xf8, 0x46, KEY_F1 },
-	{ 0xf8, 0x47, KEY_F2 },
-	{ 0xf8, 0x5e, KEY_F3 },
-	{ 0xf8, 0x5c, KEY_F4 },
-	{ 0xf8, 0x52, KEY_F5 },
-	{ 0xf8, 0x5a, KEY_F6 },
-	{ 0xf8, 0x56, KEY_MODE },
-	{ 0xf8, 0x58, KEY_SWITCHVIDEOMODE },
+	{ 0xf80a, KEY_POWER },
+	{ 0xf80c, KEY_MUTE },
+	{ 0xf811, KEY_1 },
+	{ 0xf812, KEY_2 },
+	{ 0xf813, KEY_3 },
+	{ 0xf814, KEY_4 },
+	{ 0xf815, KEY_5 },
+	{ 0xf816, KEY_6 },
+	{ 0xf817, KEY_7 },
+	{ 0xf818, KEY_8 },
+	{ 0xf819, KEY_9 },
+	{ 0xf810, KEY_0 },
+	{ 0xf81c, KEY_MENU },
+	{ 0xf80f, KEY_VOLUMEDOWN },
+	{ 0xf81a, KEY_LAST },
+	{ 0xf80e, KEY_OPEN },
+	{ 0xf804, KEY_RECORD },
+	{ 0xf809, KEY_VOLUMEUP },
+	{ 0xf808, KEY_CHANNELUP },
+	{ 0xf807, KEY_PVR },
+	{ 0xf80b, KEY_TIME },
+	{ 0xf802, KEY_RIGHT },
+	{ 0xf803, KEY_LEFT },
+	{ 0xf800, KEY_UP },
+	{ 0xf81f, KEY_OK },
+	{ 0xf801, KEY_DOWN },
+	{ 0xf805, KEY_TUNER },
+	{ 0xf806, KEY_CHANNELDOWN },
+	{ 0xf840, KEY_PLAYPAUSE },
+	{ 0xf81e, KEY_REWIND },
+	{ 0xf81b, KEY_FAVORITES },
+	{ 0xf81d, KEY_BACK },
+	{ 0xf84d, KEY_FASTFORWARD },
+	{ 0xf844, KEY_EPG },
+	{ 0xf84c, KEY_INFO },
+	{ 0xf841, KEY_AB },
+	{ 0xf843, KEY_AUDIO },
+	{ 0xf845, KEY_SUBTITLE },
+	{ 0xf84a, KEY_LIST },
+	{ 0xf846, KEY_F1 },
+	{ 0xf847, KEY_F2 },
+	{ 0xf85e, KEY_F3 },
+	{ 0xf85c, KEY_F4 },
+	{ 0xf852, KEY_F5 },
+	{ 0xf85a, KEY_F6 },
+	{ 0xf856, KEY_MODE },
+	{ 0xf858, KEY_SWITCHVIDEOMODE },
 };
 
 static struct dvb_usb_rc_key tbs_rc_keys[] = {
-	{ 0xf8,	0x84, KEY_POWER },
-	{ 0xf8,	0x94, KEY_MUTE },
-	{ 0xf8,	0x87, KEY_1 },
-	{ 0xf8,	0x86, KEY_2 },
-	{ 0xf8,	0x85, KEY_3 },
-	{ 0xf8,	0x8b, KEY_4 },
-	{ 0xf8,	0x8a, KEY_5 },
-	{ 0xf8,	0x89, KEY_6 },
-	{ 0xf8,	0x8f, KEY_7 },
-	{ 0xf8,	0x8e, KEY_8 },
-	{ 0xf8,	0x8d, KEY_9 },
-	{ 0xf8, 0x92, KEY_0 },
-	{ 0xf8, 0x96, KEY_CHANNELUP },
-	{ 0xf8, 0x91, KEY_CHANNELDOWN },
-	{ 0xf8, 0x93, KEY_VOLUMEUP },
-	{ 0xf8, 0x8c, KEY_VOLUMEDOWN },
-	{ 0xf8, 0x83, KEY_RECORD },
-	{ 0xf8, 0x98, KEY_PAUSE  },
-	{ 0xf8, 0x99, KEY_OK },
-	{ 0xf8, 0x9a, KEY_SHUFFLE },
-	{ 0xf8, 0x81, KEY_UP },
-	{ 0xf8, 0x90, KEY_LEFT },
-	{ 0xf8, 0x82, KEY_RIGHT },
-	{ 0xf8, 0x88, KEY_DOWN },
-	{ 0xf8, 0x95, KEY_FAVORITES },
-	{ 0xf8, 0x97, KEY_SUBTITLE },
-	{ 0xf8, 0x9d, KEY_ZOOM },
-	{ 0xf8, 0x9f, KEY_EXIT },
-	{ 0xf8, 0x9e, KEY_MENU },
-	{ 0xf8, 0x9c, KEY_EPG },
-	{ 0xf8, 0x80, KEY_PREVIOUS },
-	{ 0xf8, 0x9b, KEY_MODE }
+	{ 0xf884, KEY_POWER },
+	{ 0xf894, KEY_MUTE },
+	{ 0xf887, KEY_1 },
+	{ 0xf886, KEY_2 },
+	{ 0xf885, KEY_3 },
+	{ 0xf88b, KEY_4 },
+	{ 0xf88a, KEY_5 },
+	{ 0xf889, KEY_6 },
+	{ 0xf88f, KEY_7 },
+	{ 0xf88e, KEY_8 },
+	{ 0xf88d, KEY_9 },
+	{ 0xf892, KEY_0 },
+	{ 0xf896, KEY_CHANNELUP },
+	{ 0xf891, KEY_CHANNELDOWN },
+	{ 0xf893, KEY_VOLUMEUP },
+	{ 0xf88c, KEY_VOLUMEDOWN },
+	{ 0xf883, KEY_RECORD },
+	{ 0xf898, KEY_PAUSE  },
+	{ 0xf899, KEY_OK },
+	{ 0xf89a, KEY_SHUFFLE },
+	{ 0xf881, KEY_UP },
+	{ 0xf890, KEY_LEFT },
+	{ 0xf882, KEY_RIGHT },
+	{ 0xf888, KEY_DOWN },
+	{ 0xf895, KEY_FAVORITES },
+	{ 0xf897, KEY_SUBTITLE },
+	{ 0xf89d, KEY_ZOOM },
+	{ 0xf89f, KEY_EXIT },
+	{ 0xf89e, KEY_MENU },
+	{ 0xf89c, KEY_EPG },
+	{ 0xf880, KEY_PREVIOUS },
+	{ 0xf89b, KEY_MODE }
 };
 
 static struct dvb_usb_rc_keys_table keys_tables[] = {
@@ -763,9 +892,9 @@ 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) {
+	if (d->props.i2c_algo->master_xfer(&d->i2c_adap, &msg, 1) == 1) {
 		for (i = 0; i < keymap_size ; i++) {
-			if (keymap[i].data == msg.buf[0]) {
+			if (rc5_data(&keymap[i]) == msg.buf[0]) {
 				*state = REMOTE_KEY_PRESSED;
 				*event = keymap[i].event;
 				break;
@@ -792,6 +921,7 @@ static struct usb_device_id dw2102_table[] = {
 	{USB_DEVICE(0x9022, USB_PID_TEVII_S650)},
 	{USB_DEVICE(USB_VID_TERRATEC, USB_PID_CINERGY_S)},
 	{USB_DEVICE(USB_VID_CYPRESS, USB_PID_DW3101)},
+	{USB_DEVICE(0x9022, USB_PID_TEVII_S630)},
 	{ }
 };
 
@@ -806,6 +936,7 @@ static int dw2102_load_firmware(struct usb_device *dev,
 	u8 reset16[] = {0, 0, 0, 0, 0, 0, 0};
 	const struct firmware *fw;
 	const char *filename = "dvb-usb-dw2101.fw";
+
 	switch (dev->descriptor.idProduct) {
 	case 0x2101:
 		ret = request_firmware(&fw, filename, &dev->dev);
@@ -1053,6 +1184,48 @@ static struct dvb_usb_device_properties dw3101_properties = {
 	}
 };
 
+static struct dvb_usb_device_properties s630_properties = {
+	.caps = DVB_USB_IS_AN_I2C_ADAPTER,
+	.usb_ctrl = DEVICE_SPECIFIC,
+	.firmware = "dvb-usb-s630.fw",
+	.no_reconnect = 1,
+
+	.i2c_algo = &s630_i2c_algo,
+	.rc_key_map = tevii_rc_keys,
+	.rc_key_map_size = ARRAY_SIZE(tevii_rc_keys),
+	.rc_interval = 150,
+	.rc_query = dw2102_rc_query,
+
+	.generic_bulk_ctrl_endpoint = 0x81,
+	.num_adapters = 1,
+	.download_firmware = dw2102_load_firmware,
+	.read_mac_address = s630_read_mac_address,
+	.adapter = {
+		{
+			.frontend_attach = s630_frontend_attach,
+			.streaming_ctrl = NULL,
+			.tuner_attach = s630_zl10039_tuner_attach,
+			.stream = {
+				.type = USB_BULK,
+				.count = 8,
+				.endpoint = 0x82,
+				.u = {
+					.bulk = {
+						.buffersize = 4096,
+					}
+				}
+			},
+		}
+	},
+	.num_device_descs = 1,
+	.devices = {
+		{"TeVii S630 USB",
+			{&dw2102_table[6], NULL},
+			{NULL},
+		},
+	}
+};
+
 static int dw2102_probe(struct usb_interface *intf,
 		const struct usb_device_id *id)
 {
@@ -1061,6 +1234,8 @@ static int dw2102_probe(struct usb_interface *intf,
 	    0 == dvb_usb_device_init(intf, &dw2104_properties,
 			THIS_MODULE, NULL, adapter_nr) ||
 	    0 == dvb_usb_device_init(intf, &dw3101_properties,
+			THIS_MODULE, NULL, adapter_nr) ||
+	    0 == dvb_usb_device_init(intf, &s630_properties,
 			THIS_MODULE, NULL, adapter_nr)) {
 		return 0;
 	}
@@ -1094,6 +1269,6 @@ module_exit(dw2102_module_exit);
 MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by");
 MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104,"
 				" DVB-C 3101 USB2.0,"
-				" TeVii S600, S650 USB2.0 devices");
+				" TeVii S600, S630, S650 USB2.0 devices");
 MODULE_VERSION("0.1");
 MODULE_LICENSE("GPL");

+ 34 - 34
drivers/media/dvb/dvb-usb/m920x.c

@@ -140,7 +140,7 @@ static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
 		goto unlock;
 
 	for (i = 0; i < d->props.rc_key_map_size; i++)
-		if (d->props.rc_key_map[i].data == rc_state[1]) {
+		if (rc5_data(&d->props.rc_key_map[i]) == rc_state[1]) {
 			*event = d->props.rc_key_map[i].event;
 
 			switch(rc_state[0]) {
@@ -562,42 +562,42 @@ static struct m920x_inits tvwalkertwin_rc_init [] = {
 
 /* ir keymaps */
 static struct dvb_usb_rc_key megasky_rc_keys [] = {
-	{ 0x0, 0x12, KEY_POWER },
-	{ 0x0, 0x1e, KEY_CYCLEWINDOWS }, /* min/max */
-	{ 0x0, 0x02, KEY_CHANNELUP },
-	{ 0x0, 0x05, KEY_CHANNELDOWN },
-	{ 0x0, 0x03, KEY_VOLUMEUP },
-	{ 0x0, 0x06, KEY_VOLUMEDOWN },
-	{ 0x0, 0x04, KEY_MUTE },
-	{ 0x0, 0x07, KEY_OK }, /* TS */
-	{ 0x0, 0x08, KEY_STOP },
-	{ 0x0, 0x09, KEY_MENU }, /* swap */
-	{ 0x0, 0x0a, KEY_REWIND },
-	{ 0x0, 0x1b, KEY_PAUSE },
-	{ 0x0, 0x1f, KEY_FASTFORWARD },
-	{ 0x0, 0x0c, KEY_RECORD },
-	{ 0x0, 0x0d, KEY_CAMERA }, /* screenshot */
-	{ 0x0, 0x0e, KEY_COFFEE }, /* "MTS" */
+	{ 0x0012, KEY_POWER },
+	{ 0x001e, KEY_CYCLEWINDOWS }, /* min/max */
+	{ 0x0002, KEY_CHANNELUP },
+	{ 0x0005, KEY_CHANNELDOWN },
+	{ 0x0003, KEY_VOLUMEUP },
+	{ 0x0006, KEY_VOLUMEDOWN },
+	{ 0x0004, KEY_MUTE },
+	{ 0x0007, KEY_OK }, /* TS */
+	{ 0x0008, KEY_STOP },
+	{ 0x0009, KEY_MENU }, /* swap */
+	{ 0x000a, KEY_REWIND },
+	{ 0x001b, KEY_PAUSE },
+	{ 0x001f, KEY_FASTFORWARD },
+	{ 0x000c, KEY_RECORD },
+	{ 0x000d, KEY_CAMERA }, /* screenshot */
+	{ 0x000e, KEY_COFFEE }, /* "MTS" */
 };
 
 static struct dvb_usb_rc_key tvwalkertwin_rc_keys [] = {
-	{ 0x0, 0x01, KEY_ZOOM }, /* Full Screen */
-	{ 0x0, 0x02, KEY_CAMERA }, /* snapshot */
-	{ 0x0, 0x03, KEY_MUTE },
-	{ 0x0, 0x04, KEY_REWIND },
-	{ 0x0, 0x05, KEY_PLAYPAUSE }, /* Play/Pause */
-	{ 0x0, 0x06, KEY_FASTFORWARD },
-	{ 0x0, 0x07, KEY_RECORD },
-	{ 0x0, 0x08, KEY_STOP },
-	{ 0x0, 0x09, KEY_TIME }, /* Timeshift */
-	{ 0x0, 0x0c, KEY_COFFEE }, /* Recall */
-	{ 0x0, 0x0e, KEY_CHANNELUP },
-	{ 0x0, 0x12, KEY_POWER },
-	{ 0x0, 0x15, KEY_MENU }, /* source */
-	{ 0x0, 0x18, KEY_CYCLEWINDOWS }, /* TWIN PIP */
-	{ 0x0, 0x1a, KEY_CHANNELDOWN },
-	{ 0x0, 0x1b, KEY_VOLUMEDOWN },
-	{ 0x0, 0x1e, KEY_VOLUMEUP },
+	{ 0x0001, KEY_ZOOM }, /* Full Screen */
+	{ 0x0002, KEY_CAMERA }, /* snapshot */
+	{ 0x0003, KEY_MUTE },
+	{ 0x0004, KEY_REWIND },
+	{ 0x0005, KEY_PLAYPAUSE }, /* Play/Pause */
+	{ 0x0006, KEY_FASTFORWARD },
+	{ 0x0007, KEY_RECORD },
+	{ 0x0008, KEY_STOP },
+	{ 0x0009, KEY_TIME }, /* Timeshift */
+	{ 0x000c, KEY_COFFEE }, /* Recall */
+	{ 0x000e, KEY_CHANNELUP },
+	{ 0x0012, KEY_POWER },
+	{ 0x0015, KEY_MENU }, /* source */
+	{ 0x0018, KEY_CYCLEWINDOWS }, /* TWIN PIP */
+	{ 0x001a, KEY_CHANNELDOWN },
+	{ 0x001b, KEY_VOLUMEDOWN },
+	{ 0x001e, KEY_VOLUMEUP },
 };
 
 /* DVB USB Driver stuff */

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

@@ -22,51 +22,51 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 
 /* Hauppauge NOVA-T USB2 keys */
 static struct dvb_usb_rc_key haupp_rc_keys [] = {
-	{ 0x1e, 0x00, KEY_0 },
-	{ 0x1e, 0x01, KEY_1 },
-	{ 0x1e, 0x02, KEY_2 },
-	{ 0x1e, 0x03, KEY_3 },
-	{ 0x1e, 0x04, KEY_4 },
-	{ 0x1e, 0x05, KEY_5 },
-	{ 0x1e, 0x06, KEY_6 },
-	{ 0x1e, 0x07, KEY_7 },
-	{ 0x1e, 0x08, KEY_8 },
-	{ 0x1e, 0x09, KEY_9 },
-	{ 0x1e, 0x0a, KEY_KPASTERISK },
-	{ 0x1e, 0x0b, KEY_RED },
-	{ 0x1e, 0x0c, KEY_RADIO },
-	{ 0x1e, 0x0d, KEY_MENU },
-	{ 0x1e, 0x0e, KEY_GRAVE }, /* # */
-	{ 0x1e, 0x0f, KEY_MUTE },
-	{ 0x1e, 0x10, KEY_VOLUMEUP },
-	{ 0x1e, 0x11, KEY_VOLUMEDOWN },
-	{ 0x1e, 0x12, KEY_CHANNEL },
-	{ 0x1e, 0x14, KEY_UP },
-	{ 0x1e, 0x15, KEY_DOWN },
-	{ 0x1e, 0x16, KEY_LEFT },
-	{ 0x1e, 0x17, KEY_RIGHT },
-	{ 0x1e, 0x18, KEY_VIDEO },
-	{ 0x1e, 0x19, KEY_AUDIO },
-	{ 0x1e, 0x1a, KEY_MEDIA },
-	{ 0x1e, 0x1b, KEY_EPG },
-	{ 0x1e, 0x1c, KEY_TV },
-	{ 0x1e, 0x1e, KEY_NEXT },
-	{ 0x1e, 0x1f, KEY_BACK },
-	{ 0x1e, 0x20, KEY_CHANNELUP },
-	{ 0x1e, 0x21, KEY_CHANNELDOWN },
-	{ 0x1e, 0x24, KEY_LAST }, /* Skip backwards */
-	{ 0x1e, 0x25, KEY_OK },
-	{ 0x1e, 0x29, KEY_BLUE},
-	{ 0x1e, 0x2e, KEY_GREEN },
-	{ 0x1e, 0x30, KEY_PAUSE },
-	{ 0x1e, 0x32, KEY_REWIND },
-	{ 0x1e, 0x34, KEY_FASTFORWARD },
-	{ 0x1e, 0x35, KEY_PLAY },
-	{ 0x1e, 0x36, KEY_STOP },
-	{ 0x1e, 0x37, KEY_RECORD },
-	{ 0x1e, 0x38, KEY_YELLOW },
-	{ 0x1e, 0x3b, KEY_GOTO },
-	{ 0x1e, 0x3d, KEY_POWER },
+	{ 0x1e00, KEY_0 },
+	{ 0x1e01, KEY_1 },
+	{ 0x1e02, KEY_2 },
+	{ 0x1e03, KEY_3 },
+	{ 0x1e04, KEY_4 },
+	{ 0x1e05, KEY_5 },
+	{ 0x1e06, KEY_6 },
+	{ 0x1e07, KEY_7 },
+	{ 0x1e08, KEY_8 },
+	{ 0x1e09, KEY_9 },
+	{ 0x1e0a, KEY_KPASTERISK },
+	{ 0x1e0b, KEY_RED },
+	{ 0x1e0c, KEY_RADIO },
+	{ 0x1e0d, KEY_MENU },
+	{ 0x1e0e, KEY_GRAVE }, /* # */
+	{ 0x1e0f, KEY_MUTE },
+	{ 0x1e10, KEY_VOLUMEUP },
+	{ 0x1e11, KEY_VOLUMEDOWN },
+	{ 0x1e12, KEY_CHANNEL },
+	{ 0x1e14, KEY_UP },
+	{ 0x1e15, KEY_DOWN },
+	{ 0x1e16, KEY_LEFT },
+	{ 0x1e17, KEY_RIGHT },
+	{ 0x1e18, KEY_VIDEO },
+	{ 0x1e19, KEY_AUDIO },
+	{ 0x1e1a, KEY_MEDIA },
+	{ 0x1e1b, KEY_EPG },
+	{ 0x1e1c, KEY_TV },
+	{ 0x1e1e, KEY_NEXT },
+	{ 0x1e1f, KEY_BACK },
+	{ 0x1e20, KEY_CHANNELUP },
+	{ 0x1e21, KEY_CHANNELDOWN },
+	{ 0x1e24, KEY_LAST }, /* Skip backwards */
+	{ 0x1e25, KEY_OK },
+	{ 0x1e29, KEY_BLUE},
+	{ 0x1e2e, KEY_GREEN },
+	{ 0x1e30, KEY_PAUSE },
+	{ 0x1e32, KEY_REWIND },
+	{ 0x1e34, KEY_FASTFORWARD },
+	{ 0x1e35, KEY_PLAY },
+	{ 0x1e36, KEY_STOP },
+	{ 0x1e37, KEY_RECORD },
+	{ 0x1e38, KEY_YELLOW },
+	{ 0x1e3b, KEY_GOTO },
+	{ 0x1e3d, KEY_POWER },
 };
 
 /* Firmware bug? sometimes, when a new key is pressed, the previous pressed key
@@ -92,10 +92,11 @@ static int nova_t_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
 			deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x to c: %02x d: %02x toggle: %d\n",key[1],key[2],key[3],custom,data,toggle);
 
 			for (i = 0; i < ARRAY_SIZE(haupp_rc_keys); i++) {
-				if (haupp_rc_keys[i].data == data &&
-					haupp_rc_keys[i].custom == custom) {
+				if (rc5_data(&haupp_rc_keys[i]) == data &&
+					rc5_custom(&haupp_rc_keys[i]) == custom) {
 
-					deb_rc("c: %x, d: %x\n",haupp_rc_keys[i].data,haupp_rc_keys[i].custom);
+					deb_rc("c: %x, d: %x\n", rc5_data(&haupp_rc_keys[i]),
+								 rc5_custom(&haupp_rc_keys[i]));
 
 					*event = haupp_rc_keys[i].event;
 					*state = REMOTE_KEY_PRESSED;

+ 27 - 28
drivers/media/dvb/dvb-usb/opera1.c

@@ -332,32 +332,32 @@ static int opera1_pid_filter_control(struct dvb_usb_adapter *adap, int onoff)
 }
 
 static struct dvb_usb_rc_key opera1_rc_keys[] = {
-	{0x5f, 0xa0, KEY_1},
-	{0x51, 0xaf, KEY_2},
-	{0x5d, 0xa2, KEY_3},
-	{0x41, 0xbe, KEY_4},
-	{0x0b, 0xf5, KEY_5},
-	{0x43, 0xbd, KEY_6},
-	{0x47, 0xb8, KEY_7},
-	{0x49, 0xb6, KEY_8},
-	{0x05, 0xfa, KEY_9},
-	{0x45, 0xba, KEY_0},
-	{0x09, 0xf6, KEY_UP},	/*chanup */
-	{0x1b, 0xe5, KEY_DOWN},	/*chandown */
-	{0x5d, 0xa3, KEY_LEFT},	/*voldown */
-	{0x5f, 0xa1, KEY_RIGHT},	/*volup */
-	{0x07, 0xf8, KEY_SPACE},	/*tab */
-	{0x1f, 0xe1, KEY_ENTER},	/*play ok */
-	{0x1b, 0xe4, KEY_Z},	/*zoom */
-	{0x59, 0xa6, KEY_M},	/*mute */
-	{0x5b, 0xa5, KEY_F},	/*tv/f */
-	{0x19, 0xe7, KEY_R},	/*rec */
-	{0x01, 0xfe, KEY_S},	/*Stop */
-	{0x03, 0xfd, KEY_P},	/*pause */
-	{0x03, 0xfc, KEY_W},	/*<- -> */
-	{0x07, 0xf9, KEY_C},	/*capture */
-	{0x47, 0xb9, KEY_Q},	/*exit */
-	{0x43, 0xbc, KEY_O},	/*power */
+	{0x5fa0, KEY_1},
+	{0x51af, KEY_2},
+	{0x5da2, KEY_3},
+	{0x41be, KEY_4},
+	{0x0bf5, KEY_5},
+	{0x43bd, KEY_6},
+	{0x47b8, KEY_7},
+	{0x49b6, KEY_8},
+	{0x05fa, KEY_9},
+	{0x45ba, KEY_0},
+	{0x09f6, KEY_UP},	/*chanup */
+	{0x1be5, KEY_DOWN},	/*chandown */
+	{0x5da3, KEY_LEFT},	/*voldown */
+	{0x5fa1, KEY_RIGHT},	/*volup */
+	{0x07f8, KEY_SPACE},	/*tab */
+	{0x1fe1, KEY_ENTER},	/*play ok */
+	{0x1be4, KEY_Z},	/*zoom */
+	{0x59a6, KEY_M},	/*mute */
+	{0x5ba5, KEY_F},	/*tv/f */
+	{0x19e7, KEY_R},	/*rec */
+	{0x01fe, KEY_S},	/*Stop */
+	{0x03fd, KEY_P},	/*pause */
+	{0x03fc, KEY_W},	/*<- -> */
+	{0x07f9, KEY_C},	/*capture */
+	{0x47b9, KEY_Q},	/*exit */
+	{0x43bc, KEY_O},	/*power */
 
 };
 
@@ -405,8 +405,7 @@ static int opera1_rc_query(struct dvb_usb_device *dev, u32 * event, int *state)
 		send_key = (send_key & 0xffff) | 0x0100;
 
 		for (i = 0; i < ARRAY_SIZE(opera1_rc_keys); i++) {
-			if ((opera1_rc_keys[i].custom * 256 +
-					opera1_rc_keys[i].data) == (send_key & 0xffff)) {
+			if (rc5_scan(&opera1_rc_keys[i]) == (send_key & 0xffff)) {
 				*state = REMOTE_KEY_PRESSED;
 				*event = opera1_rc_keys[i].event;
 				opst->last_key_pressed =

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

@@ -175,8 +175,8 @@ static int vp702x_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
 
 /* keys for the enclosed remote control */
 static struct dvb_usb_rc_key vp702x_rc_keys[] = {
-	{ 0x00, 0x01, KEY_1 },
-	{ 0x00, 0x02, KEY_2 },
+	{ 0x0001, KEY_1 },
+	{ 0x0002, KEY_2 },
 };
 
 /* remote control stuff (does not work with my box) */
@@ -198,7 +198,7 @@ static int vp702x_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
 	}
 
 	for (i = 0; i < ARRAY_SIZE(vp702x_rc_keys); i++)
-		if (vp702x_rc_keys[i].custom == key[1]) {
+		if (rc5_custom(&vp702x_rc_keys[i]) == key[1]) {
 			*state = REMOTE_KEY_PRESSED;
 			*event = vp702x_rc_keys[i].event;
 			break;

+ 51 - 51
drivers/media/dvb/dvb-usb/vp7045.c

@@ -100,56 +100,56 @@ static int vp7045_power_ctrl(struct dvb_usb_device *d, int onoff)
 /* The keymapping struct. Somehow this should be loaded to the driver, but
  * currently it is hardcoded. */
 static struct dvb_usb_rc_key vp7045_rc_keys[] = {
-	{ 0x00, 0x16, KEY_POWER },
-	{ 0x00, 0x10, KEY_MUTE },
-	{ 0x00, 0x03, KEY_1 },
-	{ 0x00, 0x01, KEY_2 },
-	{ 0x00, 0x06, KEY_3 },
-	{ 0x00, 0x09, KEY_4 },
-	{ 0x00, 0x1d, KEY_5 },
-	{ 0x00, 0x1f, KEY_6 },
-	{ 0x00, 0x0d, KEY_7 },
-	{ 0x00, 0x19, KEY_8 },
-	{ 0x00, 0x1b, KEY_9 },
-	{ 0x00, 0x15, KEY_0 },
-	{ 0x00, 0x05, KEY_CHANNELUP },
-	{ 0x00, 0x02, KEY_CHANNELDOWN },
-	{ 0x00, 0x1e, KEY_VOLUMEUP },
-	{ 0x00, 0x0a, KEY_VOLUMEDOWN },
-	{ 0x00, 0x11, KEY_RECORD },
-	{ 0x00, 0x17, KEY_FAVORITES }, /* Heart symbol - Channel list. */
-	{ 0x00, 0x14, KEY_PLAY },
-	{ 0x00, 0x1a, KEY_STOP },
-	{ 0x00, 0x40, KEY_REWIND },
-	{ 0x00, 0x12, KEY_FASTFORWARD },
-	{ 0x00, 0x0e, KEY_PREVIOUS }, /* Recall - Previous channel. */
-	{ 0x00, 0x4c, KEY_PAUSE },
-	{ 0x00, 0x4d, KEY_SCREEN }, /* Full screen mode. */
-	{ 0x00, 0x54, KEY_AUDIO }, /* MTS - Switch to secondary audio. */
-	{ 0x00, 0x0c, KEY_CANCEL }, /* Cancel */
-	{ 0x00, 0x1c, KEY_EPG }, /* EPG */
-	{ 0x00, 0x00, KEY_TAB }, /* Tab */
-	{ 0x00, 0x48, KEY_INFO }, /* Preview */
-	{ 0x00, 0x04, KEY_LIST }, /* RecordList */
-	{ 0x00, 0x0f, KEY_TEXT }, /* Teletext */
-	{ 0x00, 0x41, KEY_PREVIOUSSONG },
-	{ 0x00, 0x42, KEY_NEXTSONG },
-	{ 0x00, 0x4b, KEY_UP },
-	{ 0x00, 0x51, KEY_DOWN },
-	{ 0x00, 0x4e, KEY_LEFT },
-	{ 0x00, 0x52, KEY_RIGHT },
-	{ 0x00, 0x4f, KEY_ENTER },
-	{ 0x00, 0x13, KEY_CANCEL },
-	{ 0x00, 0x4a, KEY_CLEAR },
-	{ 0x00, 0x54, KEY_PRINT }, /* Capture */
-	{ 0x00, 0x43, KEY_SUBTITLE }, /* Subtitle/CC */
-	{ 0x00, 0x08, KEY_VIDEO }, /* A/V */
-	{ 0x00, 0x07, KEY_SLEEP }, /* Hibernate */
-	{ 0x00, 0x45, KEY_ZOOM }, /* Zoom+ */
-	{ 0x00, 0x18, KEY_RED},
-	{ 0x00, 0x53, KEY_GREEN},
-	{ 0x00, 0x5e, KEY_YELLOW},
-	{ 0x00, 0x5f, KEY_BLUE}
+	{ 0x0016, KEY_POWER },
+	{ 0x0010, KEY_MUTE },
+	{ 0x0003, KEY_1 },
+	{ 0x0001, KEY_2 },
+	{ 0x0006, KEY_3 },
+	{ 0x0009, KEY_4 },
+	{ 0x001d, KEY_5 },
+	{ 0x001f, KEY_6 },
+	{ 0x000d, KEY_7 },
+	{ 0x0019, KEY_8 },
+	{ 0x001b, KEY_9 },
+	{ 0x0015, KEY_0 },
+	{ 0x0005, KEY_CHANNELUP },
+	{ 0x0002, KEY_CHANNELDOWN },
+	{ 0x001e, KEY_VOLUMEUP },
+	{ 0x000a, KEY_VOLUMEDOWN },
+	{ 0x0011, KEY_RECORD },
+	{ 0x0017, KEY_FAVORITES }, /* Heart symbol - Channel list. */
+	{ 0x0014, KEY_PLAY },
+	{ 0x001a, KEY_STOP },
+	{ 0x0040, KEY_REWIND },
+	{ 0x0012, KEY_FASTFORWARD },
+	{ 0x000e, KEY_PREVIOUS }, /* Recall - Previous channel. */
+	{ 0x004c, KEY_PAUSE },
+	{ 0x004d, KEY_SCREEN }, /* Full screen mode. */
+	{ 0x0054, KEY_AUDIO }, /* MTS - Switch to secondary audio. */
+	{ 0x000c, KEY_CANCEL }, /* Cancel */
+	{ 0x001c, KEY_EPG }, /* EPG */
+	{ 0x0000, KEY_TAB }, /* Tab */
+	{ 0x0048, KEY_INFO }, /* Preview */
+	{ 0x0004, KEY_LIST }, /* RecordList */
+	{ 0x000f, KEY_TEXT }, /* Teletext */
+	{ 0x0041, KEY_PREVIOUSSONG },
+	{ 0x0042, KEY_NEXTSONG },
+	{ 0x004b, KEY_UP },
+	{ 0x0051, KEY_DOWN },
+	{ 0x004e, KEY_LEFT },
+	{ 0x0052, KEY_RIGHT },
+	{ 0x004f, KEY_ENTER },
+	{ 0x0013, KEY_CANCEL },
+	{ 0x004a, KEY_CLEAR },
+	{ 0x0054, KEY_PRINT }, /* Capture */
+	{ 0x0043, KEY_SUBTITLE }, /* Subtitle/CC */
+	{ 0x0008, KEY_VIDEO }, /* A/V */
+	{ 0x0007, KEY_SLEEP }, /* Hibernate */
+	{ 0x0045, KEY_ZOOM }, /* Zoom+ */
+	{ 0x0018, KEY_RED},
+	{ 0x0053, KEY_GREEN},
+	{ 0x005e, KEY_YELLOW},
+	{ 0x005f, KEY_BLUE}
 };
 
 static int vp7045_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
@@ -166,7 +166,7 @@ static int vp7045_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
 	}
 
 	for (i = 0; i < ARRAY_SIZE(vp7045_rc_keys); i++)
-		if (vp7045_rc_keys[i].data == key) {
+		if (rc5_data(&vp7045_rc_keys[i]) == key) {
 			*state = REMOTE_KEY_PRESSED;
 			*event = vp7045_rc_keys[i].event;
 			break;

+ 103 - 40
drivers/media/dvb/firewire/firedtv-avc.c

@@ -89,15 +89,33 @@ struct avc_response_frame {
 	u8 operand[509];
 };
 
-#define AVC_DEBUG_FCP_SUBACTIONS	1
-#define AVC_DEBUG_FCP_PAYLOADS		2
+#define AVC_DEBUG_READ_DESCRIPTOR              0x0001
+#define AVC_DEBUG_DSIT                         0x0002
+#define AVC_DEBUG_DSD                          0x0004
+#define AVC_DEBUG_REGISTER_REMOTE_CONTROL      0x0008
+#define AVC_DEBUG_LNB_CONTROL                  0x0010
+#define AVC_DEBUG_TUNE_QPSK                    0x0020
+#define AVC_DEBUG_TUNE_QPSK2                   0x0040
+#define AVC_DEBUG_HOST2CA                      0x0080
+#define AVC_DEBUG_CA2HOST                      0x0100
+#define AVC_DEBUG_APPLICATION_PMT              0x4000
+#define AVC_DEBUG_FCP_PAYLOADS                 0x8000
 
 static int avc_debug;
 module_param_named(debug, avc_debug, int, 0644);
-MODULE_PARM_DESC(debug, "Verbose logging (default = 0"
-	", FCP subactions = "	__stringify(AVC_DEBUG_FCP_SUBACTIONS)
-	", FCP payloads = "	__stringify(AVC_DEBUG_FCP_PAYLOADS)
-	", or all = -1)");
+MODULE_PARM_DESC(debug, "Verbose logging (none = 0"
+	", FCP subactions"
+	": READ DESCRIPTOR = "		__stringify(AVC_DEBUG_READ_DESCRIPTOR)
+	", DSIT = "			__stringify(AVC_DEBUG_DSIT)
+	", REGISTER_REMOTE_CONTROL = "	__stringify(AVC_DEBUG_REGISTER_REMOTE_CONTROL)
+	", LNB CONTROL = "		__stringify(AVC_DEBUG_LNB_CONTROL)
+	", TUNE QPSK = "		__stringify(AVC_DEBUG_TUNE_QPSK)
+	", TUNE QPSK2 = "		__stringify(AVC_DEBUG_TUNE_QPSK2)
+	", HOST2CA = "			__stringify(AVC_DEBUG_HOST2CA)
+	", CA2HOST = "			__stringify(AVC_DEBUG_CA2HOST)
+	"; Application sent PMT = "	__stringify(AVC_DEBUG_APPLICATION_PMT)
+	", FCP payloads = "		__stringify(AVC_DEBUG_FCP_PAYLOADS)
+	", or a combination, or all = -1)");
 
 static const char *debug_fcp_ctype(unsigned int ctype)
 {
@@ -118,48 +136,70 @@ static const char *debug_fcp_opcode(unsigned int opcode,
 				    const u8 *data, int length)
 {
 	switch (opcode) {
-	case AVC_OPCODE_VENDOR:			break;
-	case AVC_OPCODE_READ_DESCRIPTOR:	return "ReadDescriptor";
-	case AVC_OPCODE_DSIT:			return "DirectSelectInfo.Type";
-	case AVC_OPCODE_DSD:			return "DirectSelectData";
-	default:				return "?";
+	case AVC_OPCODE_VENDOR:
+		break;
+	case AVC_OPCODE_READ_DESCRIPTOR:
+		return avc_debug & AVC_DEBUG_READ_DESCRIPTOR ?
+				"ReadDescriptor" : NULL;
+	case AVC_OPCODE_DSIT:
+		return avc_debug & AVC_DEBUG_DSIT ?
+				"DirectSelectInfo.Type" : NULL;
+	case AVC_OPCODE_DSD:
+		return avc_debug & AVC_DEBUG_DSD ? "DirectSelectData" : NULL;
+	default:
+		return "Unknown";
 	}
 
 	if (length < 7 ||
 	    data[3] != SFE_VENDOR_DE_COMPANYID_0 ||
 	    data[4] != SFE_VENDOR_DE_COMPANYID_1 ||
 	    data[5] != SFE_VENDOR_DE_COMPANYID_2)
-		return "Vendor";
+		return "Vendor/Unknown";
 
 	switch (data[6]) {
-	case SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL:	return "RegisterRC";
-	case SFE_VENDOR_OPCODE_LNB_CONTROL:		return "LNBControl";
-	case SFE_VENDOR_OPCODE_TUNE_QPSK:		return "TuneQPSK";
-	case SFE_VENDOR_OPCODE_TUNE_QPSK2:		return "TuneQPSK2";
-	case SFE_VENDOR_OPCODE_HOST2CA:			return "Host2CA";
-	case SFE_VENDOR_OPCODE_CA2HOST:			return "CA2Host";
+	case SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL:
+		return avc_debug & AVC_DEBUG_REGISTER_REMOTE_CONTROL ?
+				"RegisterRC" : NULL;
+	case SFE_VENDOR_OPCODE_LNB_CONTROL:
+		return avc_debug & AVC_DEBUG_LNB_CONTROL ? "LNBControl" : NULL;
+	case SFE_VENDOR_OPCODE_TUNE_QPSK:
+		return avc_debug & AVC_DEBUG_TUNE_QPSK ? "TuneQPSK" : NULL;
+	case SFE_VENDOR_OPCODE_TUNE_QPSK2:
+		return avc_debug & AVC_DEBUG_TUNE_QPSK2 ? "TuneQPSK2" : NULL;
+	case SFE_VENDOR_OPCODE_HOST2CA:
+		return avc_debug & AVC_DEBUG_HOST2CA ? "Host2CA" : NULL;
+	case SFE_VENDOR_OPCODE_CA2HOST:
+		return avc_debug & AVC_DEBUG_CA2HOST ? "CA2Host" : NULL;
 	}
-	return "Vendor";
+	return "Vendor/Unknown";
 }
 
 static void debug_fcp(const u8 *data, int length)
 {
-	unsigned int subunit_type, subunit_id, op;
-	const char *prefix = data[0] > 7 ? "FCP <- " : "FCP -> ";
+	unsigned int subunit_type, subunit_id, opcode;
+	const char *op, *prefix;
+
+	prefix       = data[0] > 7 ? "FCP <- " : "FCP -> ";
+	subunit_type = data[1] >> 3;
+	subunit_id   = data[1] & 7;
+	opcode       = subunit_type == 0x1e || subunit_id == 5 ? ~0 : data[2];
+	op           = debug_fcp_opcode(opcode, data, length);
 
-	if (avc_debug & AVC_DEBUG_FCP_SUBACTIONS) {
-		subunit_type = data[1] >> 3;
-		subunit_id = data[1] & 7;
-		op = subunit_type == 0x1e || subunit_id == 5 ? ~0 : data[2];
+	if (op) {
 		printk(KERN_INFO "%ssu=%x.%x l=%d: %-8s - %s\n",
 		       prefix, subunit_type, subunit_id, length,
-		       debug_fcp_ctype(data[0]),
-		       debug_fcp_opcode(op, data, length));
+		       debug_fcp_ctype(data[0]), op);
+		if (avc_debug & AVC_DEBUG_FCP_PAYLOADS)
+			print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_NONE,
+				       16, 1, data, length, false);
 	}
+}
 
-	if (avc_debug & AVC_DEBUG_FCP_PAYLOADS)
-		print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_NONE, 16, 1,
-			       data, length, false);
+static void debug_pmt(char *msg, int length)
+{
+	printk(KERN_INFO "APP PMT -> l=%d\n", length);
+	print_hex_dump(KERN_INFO, "APP PMT -> ", DUMP_PREFIX_NONE,
+		       16, 1, msg, length, false);
 }
 
 static int __avc_write(struct firedtv *fdtv,
@@ -254,6 +294,26 @@ int avc_recv(struct firedtv *fdtv, void *data, size_t length)
 	return 0;
 }
 
+static int add_pid_filter(struct firedtv *fdtv, u8 *operand)
+{
+	int i, n, pos = 1;
+
+	for (i = 0, n = 0; i < 16; i++) {
+		if (test_bit(i, &fdtv->channel_active)) {
+			operand[pos++] = 0x13; /* flowfunction relay */
+			operand[pos++] = 0x80; /* dsd_sel_spec_valid_flags -> PID */
+			operand[pos++] = (fdtv->channel_pid[i] >> 8) & 0x1f;
+			operand[pos++] = fdtv->channel_pid[i] & 0xff;
+			operand[pos++] = 0x00; /* tableID */
+			operand[pos++] = 0x00; /* filter_length */
+			n++;
+		}
+	}
+	operand[0] = n;
+
+	return pos;
+}
+
 /*
  * tuning command for setting the relative LNB frequency
  * (not supported by the AVC standard)
@@ -316,7 +376,8 @@ static void avc_tuner_tuneqpsk(struct firedtv *fdtv,
 	}
 }
 
-static void avc_tuner_dsd_dvb_c(struct dvb_frontend_parameters *params,
+static void avc_tuner_dsd_dvb_c(struct firedtv *fdtv,
+				struct dvb_frontend_parameters *params,
 				struct avc_command_frame *c)
 {
 	c->opcode = AVC_OPCODE_DSD;
@@ -378,13 +439,13 @@ static void avc_tuner_dsd_dvb_c(struct dvb_frontend_parameters *params,
 
 	c->operand[20] = 0x00;
 	c->operand[21] = 0x00;
-	/* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */
-	c->operand[22] = 0x00;
 
-	c->length = 28;
+	/* Add PIDs to filter */
+	c->length = ALIGN(22 + add_pid_filter(fdtv, &c->operand[22]) + 3, 4);
 }
 
-static void avc_tuner_dsd_dvb_t(struct dvb_frontend_parameters *params,
+static void avc_tuner_dsd_dvb_t(struct firedtv *fdtv,
+				struct dvb_frontend_parameters *params,
 				struct avc_command_frame *c)
 {
 	struct dvb_ofdm_parameters *ofdm = &params->u.ofdm;
@@ -481,10 +542,9 @@ static void avc_tuner_dsd_dvb_t(struct dvb_frontend_parameters *params,
 
 	c->operand[15] = 0x00; /* network_ID[0] */
 	c->operand[16] = 0x00; /* network_ID[1] */
-	/* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */
-	c->operand[17] = 0x00;
 
-	c->length = 24;
+	/* Add PIDs to filter */
+	c->length = ALIGN(17 + add_pid_filter(fdtv, &c->operand[17]) + 3, 4);
 }
 
 int avc_tuner_dsd(struct firedtv *fdtv,
@@ -502,8 +562,8 @@ int avc_tuner_dsd(struct firedtv *fdtv,
 	switch (fdtv->type) {
 	case FIREDTV_DVB_S:
 	case FIREDTV_DVB_S2: avc_tuner_tuneqpsk(fdtv, params, c); break;
-	case FIREDTV_DVB_C: avc_tuner_dsd_dvb_c(params, c); break;
-	case FIREDTV_DVB_T: avc_tuner_dsd_dvb_t(params, c); break;
+	case FIREDTV_DVB_C: avc_tuner_dsd_dvb_c(fdtv, params, c); break;
+	case FIREDTV_DVB_T: avc_tuner_dsd_dvb_t(fdtv, params, c); break;
 	default:
 		BUG();
 	}
@@ -963,6 +1023,9 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
 	int es_info_length;
 	int crc32_csum;
 
+	if (unlikely(avc_debug & AVC_DEBUG_APPLICATION_PMT))
+		debug_pmt(msg, length);
+
 	memset(c, 0, sizeof(*c));
 
 	c->ctype   = AVC_CTYPE_CONTROL;

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

@@ -81,6 +81,13 @@ config DVB_ZL10036
 	help
 	  A DVB-S tuner module. Say Y when you want to support this frontend.
 
+config DVB_ZL10039
+	tristate "Zarlink ZL10039 silicon tuner"
+	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_S5H1420
 	tristate "Samsung S5H1420 based"
 	depends on DVB_CORE && I2C

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

@@ -31,6 +31,7 @@ obj-$(CONFIG_DVB_SP887X) += sp887x.o
 obj-$(CONFIG_DVB_NXT6000) += nxt6000.o
 obj-$(CONFIG_DVB_MT352) += mt352.o
 obj-$(CONFIG_DVB_ZL10036) += zl10036.o
+obj-$(CONFIG_DVB_ZL10039) += zl10039.o
 obj-$(CONFIG_DVB_ZL10353) += zl10353.o
 obj-$(CONFIG_DVB_CX22702) += cx22702.o
 obj-$(CONFIG_DVB_DRX397XD) += drx397xD.o

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

@@ -155,7 +155,7 @@ static int cx22700_set_tps (struct cx22700_state *state, struct dvb_ofdm_paramet
 	    p->hierarchy_information > HIERARCHY_4)
 		return -EINVAL;
 
-	if (p->bandwidth < BANDWIDTH_8_MHZ && p->bandwidth > BANDWIDTH_6_MHZ)
+	if (p->bandwidth < BANDWIDTH_8_MHZ || p->bandwidth > BANDWIDTH_6_MHZ)
 		return -EINVAL;
 
 	if (p->bandwidth == BANDWIDTH_7_MHZ)

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

@@ -303,6 +303,7 @@ static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f)
 {
 	s32 N;
 	s64 F;
+	u64 dividend;
 	u8 R, r;
 	u8 vcodiv;
 	u8 factor;
@@ -346,7 +347,10 @@ static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f)
 	F = freq_hz;
 	F *= (u64) (R * vcodiv * 262144);
 	dprintk("1 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
-	do_div(F, state->config->xtal_khz*1000 * factor * 2);
+	/* do_div needs an u64 as first argument */
+	dividend = F;
+	do_div(dividend, state->config->xtal_khz * 1000 * factor * 2);
+	F = dividend;
 	dprintk("2 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
 	F -= (N + 32) * 262144;
 

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

@@ -458,7 +458,7 @@ static int cx24123_set_symbolrate(struct cx24123_state *state, u32 srate)
 	/*  check if symbol rate is within limits */
 	if ((srate > state->frontend.ops.info.symbol_rate_max) ||
 	    (srate < state->frontend.ops.info.symbol_rate_min))
-		return -EOPNOTSUPP;;
+		return -EOPNOTSUPP;
 
 	/* choose the sampling rate high enough for the required operation,
 	   while optimizing the power consumed by the demodulator */

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

@@ -167,7 +167,7 @@ static int dib0070_tune_digital(struct dvb_frontend *fe, struct dvb_frontend_par
 					break;
 				case BAND_SBAND:
 					LO4_SET_VCO_HFDIV(lo4, 0, 0);
-					LO4_SET_CTRIM(lo4, 1);;
+					LO4_SET_CTRIM(lo4, 1);
 					c = 1;
 					break;
 				case BAND_UHF:

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

@@ -883,7 +883,7 @@ static void dib7000p_spur_protect(struct dib7000p_state *state, u32 rf_khz, u32
 	255, 255, 255, 255, 255, 255};
 
 	u32 xtal = state->cfg.bw->xtal_hz / 1000;
-	int f_rel = ( (rf_khz + xtal/2) / xtal) * xtal - rf_khz;
+	int f_rel = DIV_ROUND_CLOSEST(rf_khz, xtal) * xtal - rf_khz;
 	int k;
 	int coef_re[8],coef_im[8];
 	int bw_khz = bw;

+ 75 - 0
drivers/media/dvb/frontends/dvb-pll.c

@@ -389,6 +389,77 @@ static struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = {
 	}
 };
 
+/* Samsung TDTC9251DH0 DVB-T NIM, as used on AirStar 2 */
+static struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = {
+	.name	= "Samsung TDTC9251DH0",
+	.min	=  48000000,
+	.max	= 863000000,
+	.iffreq	=  36166667,
+	.count	= 3,
+	.entries = {
+		{ 157500000, 166667, 0xcc, 0x09 },
+		{ 443000000, 166667, 0xcc, 0x0a },
+		{ 863000000, 166667, 0xcc, 0x08 },
+	}
+};
+
+/* Samsung TBDU18132 DVB-S NIM with TSA5059 PLL, used in SkyStar2 DVB-S 2.3 */
+static struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = {
+	.name = "Samsung TBDU18132",
+	.min	=  950000,
+	.max	= 2150000, /* guesses */
+	.iffreq = 0,
+	.count = 2,
+	.entries = {
+		{ 1550000, 125, 0x84, 0x82 },
+		{ 4095937, 125, 0x84, 0x80 },
+	}
+	/* TSA5059 PLL has a 17 bit divisor rather than the 15 bits supported
+	 * by this driver.  The two extra bits are 0x60 in the third byte.  15
+	 * bits is enough for over 4 GHz, which is enough to cover the range
+	 * of this tuner.  We could use the additional divisor bits by adding
+	 * more entries, e.g.
+	 { 0x0ffff * 125 + 125/2, 125, 0x84 | 0x20, },
+	 { 0x17fff * 125 + 125/2, 125, 0x84 | 0x40, },
+	 { 0x1ffff * 125 + 125/2, 125, 0x84 | 0x60, }, */
+};
+
+/* Samsung TBMU24112 DVB-S NIM with SL1935 zero-IF tuner */
+static struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = {
+	.name = "Samsung TBMU24112",
+	.min	=  950000,
+	.max	= 2150000, /* guesses */
+	.iffreq = 0,
+	.count = 2,
+	.entries = {
+		{ 1500000, 125, 0x84, 0x18 },
+		{ 9999999, 125, 0x84, 0x08 },
+	}
+};
+
+/* Alps TDEE4 DVB-C NIM, used on Cablestar 2 */
+/* byte 4 : 1  *   *   AGD R3  R2  R1  R0
+ * byte 5 : C1 *   RE  RTS BS4 BS3 BS2 BS1
+ * AGD = 1, R3 R2 R1 R0 = 0 1 0 1 => byte 4 = 1**10101 = 0x95
+ * Range(MHz)  C1 *  RE RTS BS4 BS3 BS2 BS1  Byte 5
+ *  47 - 153   0  *  0   0   0   0   0   1   0x01
+ * 153 - 430   0  *  0   0   0   0   1   0   0x02
+ * 430 - 822   0  *  0   0   1   0   0   0   0x08
+ * 822 - 862   1  *  0   0   1   0   0   0   0x88 */
+static struct dvb_pll_desc dvb_pll_alps_tdee4 = {
+	.name = "ALPS TDEE4",
+	.min	=  47000000,
+	.max	= 862000000,
+	.iffreq	=  36125000,
+	.count = 4,
+	.entries = {
+		{ 153000000, 62500, 0x95, 0x01 },
+		{ 430000000, 62500, 0x95, 0x02 },
+		{ 822000000, 62500, 0x95, 0x08 },
+		{ 999999999, 62500, 0x95, 0x88 },
+	}
+};
+
 /* ----------------------------------------------------------- */
 
 static struct dvb_pll_desc *pll_list[] = {
@@ -402,11 +473,15 @@ static struct dvb_pll_desc *pll_list[] = {
 	[DVB_PLL_TUA6034]                = &dvb_pll_tua6034,
 	[DVB_PLL_TDA665X]                = &dvb_pll_tda665x,
 	[DVB_PLL_TDED4]                  = &dvb_pll_tded4,
+	[DVB_PLL_TDEE4]                  = &dvb_pll_alps_tdee4,
 	[DVB_PLL_TDHU2]                  = &dvb_pll_tdhu2,
 	[DVB_PLL_SAMSUNG_TBMV]           = &dvb_pll_samsung_tbmv,
 	[DVB_PLL_PHILIPS_SD1878_TDA8261] = &dvb_pll_philips_sd1878_tda8261,
 	[DVB_PLL_OPERA1]                 = &dvb_pll_opera1,
 	[DVB_PLL_SAMSUNG_DTOS403IH102A]  = &dvb_pll_samsung_dtos403ih102a,
+	[DVB_PLL_SAMSUNG_TDTC9251DH0]    = &dvb_pll_samsung_tdtc9251dh0,
+	[DVB_PLL_SAMSUNG_TBDU18132]	 = &dvb_pll_samsung_tbdu18132,
+	[DVB_PLL_SAMSUNG_TBMU24112]      = &dvb_pll_samsung_tbmu24112,
 };
 
 /* ----------------------------------------------------------- */

+ 4 - 0
drivers/media/dvb/frontends/dvb-pll.h

@@ -23,6 +23,10 @@
 #define DVB_PLL_PHILIPS_SD1878_TDA8261 12
 #define DVB_PLL_OPERA1                 13
 #define DVB_PLL_SAMSUNG_DTOS403IH102A  14
+#define DVB_PLL_SAMSUNG_TDTC9251DH0    15
+#define DVB_PLL_SAMSUNG_TBDU18132      16
+#define DVB_PLL_SAMSUNG_TBMU24112      17
+#define DVB_PLL_TDEE4		       18
 
 /**
  * Attach a dvb-pll to the supplied frontend structure.

+ 387 - 97
drivers/media/dvb/frontends/lgs8gxx.c

@@ -1,9 +1,9 @@
 /*
- *    Support for Legend Silicon DMB-TH demodulator
- *    LGS8913, LGS8GL5
+ *    Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator
+ *    LGS8913, LGS8GL5, LGS8G75
  *    experimental support LGS8G42, LGS8G52
  *
- *    Copyright (C) 2007,2008 David T.L. Wong <davidtlwong@gmail.com>
+ *    Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com>
  *    Copyright (C) 2008 Sirius International (Hong Kong) Limited
  *    Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5)
  *
@@ -46,6 +46,42 @@ module_param(fake_signal_str, int, 0644);
 MODULE_PARM_DESC(fake_signal_str, "fake signal strength for LGS8913."
 "Signal strength calculation is slow.(default:on).");
 
+static const u8 lgs8g75_initdat[] = {
+	0x01, 0x30, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xE4, 0xF5, 0xA8, 0xF5, 0xB8, 0xF5, 0x88, 0xF5,
+	0x89, 0xF5, 0x87, 0x75, 0xD0, 0x00, 0x11, 0x50,
+	0x11, 0x50, 0xF4, 0xF5, 0x80, 0xF5, 0x90, 0xF5,
+	0xA0, 0xF5, 0xB0, 0x75, 0x81, 0x30, 0x80, 0x01,
+	0x32, 0x90, 0x80, 0x12, 0x74, 0xFF, 0xF0, 0x90,
+	0x80, 0x13, 0x74, 0x1F, 0xF0, 0x90, 0x80, 0x23,
+	0x74, 0x01, 0xF0, 0x90, 0x80, 0x22, 0xF0, 0x90,
+	0x00, 0x48, 0x74, 0x00, 0xF0, 0x90, 0x80, 0x4D,
+	0x74, 0x05, 0xF0, 0x90, 0x80, 0x09, 0xE0, 0x60,
+	0x21, 0x12, 0x00, 0xDD, 0x14, 0x60, 0x1B, 0x12,
+	0x00, 0xDD, 0x14, 0x60, 0x15, 0x12, 0x00, 0xDD,
+	0x14, 0x60, 0x0F, 0x12, 0x00, 0xDD, 0x14, 0x60,
+	0x09, 0x12, 0x00, 0xDD, 0x14, 0x60, 0x03, 0x12,
+	0x00, 0xDD, 0x90, 0x80, 0x42, 0xE0, 0x60, 0x0B,
+	0x14, 0x60, 0x0C, 0x14, 0x60, 0x0D, 0x14, 0x60,
+	0x0E, 0x01, 0xB3, 0x74, 0x04, 0x01, 0xB9, 0x74,
+	0x05, 0x01, 0xB9, 0x74, 0x07, 0x01, 0xB9, 0x74,
+	0x0A, 0xC0, 0xE0, 0x74, 0xC8, 0x12, 0x00, 0xE2,
+	0xD0, 0xE0, 0x14, 0x70, 0xF4, 0x90, 0x80, 0x09,
+	0xE0, 0x70, 0xAE, 0x12, 0x00, 0xF6, 0x12, 0x00,
+	0xFE, 0x90, 0x00, 0x48, 0xE0, 0x04, 0xF0, 0x90,
+	0x80, 0x4E, 0xF0, 0x01, 0x73, 0x90, 0x80, 0x08,
+	0xF0, 0x22, 0xF8, 0x7A, 0x0C, 0x79, 0xFD, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9,
+	0xF6, 0xDA, 0xF2, 0xD8, 0xEE, 0x22, 0x90, 0x80,
+	0x65, 0xE0, 0x54, 0xFD, 0xF0, 0x22, 0x90, 0x80,
+	0x65, 0xE0, 0x44, 0xC2, 0xF0, 0x22
+};
+
 /* LGS8GXX internal helper functions */
 
 static int lgs8gxx_write_reg(struct lgs8gxx_state *priv, u8 reg, u8 data)
@@ -55,7 +91,7 @@ static int lgs8gxx_write_reg(struct lgs8gxx_state *priv, u8 reg, u8 data)
 	struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 };
 
 	msg.addr = priv->config->demod_address;
-	if (reg >= 0xC0)
+	if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0)
 		msg.addr += 0x02;
 
 	if (debug >= 2)
@@ -84,7 +120,7 @@ static int lgs8gxx_read_reg(struct lgs8gxx_state *priv, u8 reg, u8 *p_data)
 	};
 
 	dev_addr = priv->config->demod_address;
-	if (reg >= 0xC0)
+	if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0)
 		dev_addr += 0x02;
 	msg[1].addr =  msg[0].addr = dev_addr;
 
@@ -112,19 +148,36 @@ static int lgs8gxx_soft_reset(struct lgs8gxx_state *priv)
 	return 0;
 }
 
+static int wait_reg_mask(struct lgs8gxx_state *priv, u8 reg, u8 mask,
+	u8 val, u8 delay, u8 tries)
+{
+	u8 t;
+	int i;
+
+	for (i = 0; i < tries; i++) {
+		lgs8gxx_read_reg(priv, reg, &t);
+
+		if ((t & mask) == val)
+			return 0;
+		msleep(delay);
+	}
+
+	return 1;
+}
+
 static int lgs8gxx_set_ad_mode(struct lgs8gxx_state *priv)
 {
 	const struct lgs8gxx_config *config = priv->config;
 	u8 if_conf;
 
-	if_conf = 0x10; /* AGC output on; */
+	if_conf = 0x10; /* AGC output on, RF_AGC output off; */
 
 	if_conf |=
 		((config->ext_adc) ? 0x80 : 0x00) |
 		((config->if_neg_center) ? 0x04 : 0x00) |
 		((config->if_freq == 0) ? 0x08 : 0x00) | /* Baseband */
-		((config->ext_adc && config->adc_signed) ? 0x02 : 0x00) |
-		((config->ext_adc && config->if_neg_edge) ? 0x01 : 0x00);
+		((config->adc_signed) ? 0x02 : 0x00) |
+		((config->if_neg_edge) ? 0x01 : 0x00);
 
 	if (config->ext_adc &&
 		(config->prod == LGS8GXX_PROD_LGS8G52)) {
@@ -157,39 +210,82 @@ static int lgs8gxx_set_if_freq(struct lgs8gxx_state *priv, u32 freq /*in kHz*/)
 	}
 	dprintk("AFC_INIT_FREQ = 0x%08X\n", v32);
 
-	lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32));
-	lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 8));
-	lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 16));
-	lgs8gxx_write_reg(priv, 0x0C, 0xFF & (v32 >> 24));
+	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+		lgs8gxx_write_reg(priv, 0x08, 0xFF & (v32));
+		lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32 >> 8));
+		lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 16));
+		lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 24));
+	} else {
+		lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32));
+		lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 8));
+		lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 16));
+		lgs8gxx_write_reg(priv, 0x0C, 0xFF & (v32 >> 24));
+	}
+
+	return 0;
+}
+
+static int lgs8gxx_get_afc_phase(struct lgs8gxx_state *priv)
+{
+	u64 val;
+	u32 v32 = 0;
+	u8 reg_addr, t;
+	int i;
+
+	if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
+		reg_addr = 0x23;
+	else
+		reg_addr = 0x48;
+
+	for (i = 0; i < 4; i++) {
+		lgs8gxx_read_reg(priv, reg_addr, &t);
+		v32 <<= 8;
+		v32 |= t;
+		reg_addr--;
+	}
 
+	val = v32;
+	val *= priv->config->if_clk_freq;
+	val /= (u64)1 << 32;
+	dprintk("AFC = %u kHz\n", (u32)val);
 	return 0;
 }
 
 static int lgs8gxx_set_mode_auto(struct lgs8gxx_state *priv)
 {
 	u8 t;
+	u8 prod = priv->config->prod;
 
-	if (priv->config->prod == LGS8GXX_PROD_LGS8913)
+	if (prod == LGS8GXX_PROD_LGS8913)
 		lgs8gxx_write_reg(priv, 0xC6, 0x01);
 
-	lgs8gxx_read_reg(priv, 0x7E, &t);
-	lgs8gxx_write_reg(priv, 0x7E, t | 0x01);
-
-	/* clear FEC self reset */
-	lgs8gxx_read_reg(priv, 0xC5, &t);
-	lgs8gxx_write_reg(priv, 0xC5, t & 0xE0);
+	if (prod == LGS8GXX_PROD_LGS8G75) {
+		lgs8gxx_read_reg(priv, 0x0C, &t);
+		t &= (~0x04);
+		lgs8gxx_write_reg(priv, 0x0C, t | 0x80);
+		lgs8gxx_write_reg(priv, 0x39, 0x00);
+		lgs8gxx_write_reg(priv, 0x3D, 0x04);
+	} else if (prod == LGS8GXX_PROD_LGS8913 ||
+		prod == LGS8GXX_PROD_LGS8GL5 ||
+		prod == LGS8GXX_PROD_LGS8G42 ||
+		prod == LGS8GXX_PROD_LGS8G52 ||
+		prod == LGS8GXX_PROD_LGS8G54) {
+		lgs8gxx_read_reg(priv, 0x7E, &t);
+		lgs8gxx_write_reg(priv, 0x7E, t | 0x01);
+
+		/* clear FEC self reset */
+		lgs8gxx_read_reg(priv, 0xC5, &t);
+		lgs8gxx_write_reg(priv, 0xC5, t & 0xE0);
+	}
 
-	if (priv->config->prod == LGS8GXX_PROD_LGS8913) {
+	if (prod == LGS8GXX_PROD_LGS8913) {
 		/* FEC auto detect */
 		lgs8gxx_write_reg(priv, 0xC1, 0x03);
 
 		lgs8gxx_read_reg(priv, 0x7C, &t);
 		t = (t & 0x8C) | 0x03;
 		lgs8gxx_write_reg(priv, 0x7C, t);
-	}
-
 
-	if (priv->config->prod == LGS8GXX_PROD_LGS8913) {
 		/* BER test mode */
 		lgs8gxx_read_reg(priv, 0xC3, &t);
 		t = (t & 0xEF) |  0x10;
@@ -207,6 +303,32 @@ static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv)
 	int ret = 0;
 	u8 t;
 
+	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+		u8 t2;
+		lgs8gxx_read_reg(priv, 0x0C, &t);
+		t &= (~0x80);
+		lgs8gxx_write_reg(priv, 0x0C, t);
+
+		lgs8gxx_read_reg(priv, 0x0C, &t);
+		lgs8gxx_read_reg(priv, 0x19, &t2);
+
+		if (((t&0x03) == 0x01) && (t2&0x01)) {
+			lgs8gxx_write_reg(priv, 0x6E, 0x05);
+			lgs8gxx_write_reg(priv, 0x39, 0x02);
+			lgs8gxx_write_reg(priv, 0x39, 0x03);
+			lgs8gxx_write_reg(priv, 0x3D, 0x05);
+			lgs8gxx_write_reg(priv, 0x3E, 0x28);
+			lgs8gxx_write_reg(priv, 0x53, 0x80);
+		} else {
+			lgs8gxx_write_reg(priv, 0x6E, 0x3F);
+			lgs8gxx_write_reg(priv, 0x39, 0x00);
+			lgs8gxx_write_reg(priv, 0x3D, 0x04);
+		}
+
+		lgs8gxx_soft_reset(priv);
+		return 0;
+	}
+
 	/* turn off auto-detect; manual settings */
 	lgs8gxx_write_reg(priv, 0x7E, 0);
 	if (priv->config->prod == LGS8GXX_PROD_LGS8913)
@@ -226,11 +348,39 @@ static int lgs8gxx_is_locked(struct lgs8gxx_state *priv, u8 *locked)
 	int ret = 0;
 	u8 t;
 
-	ret = lgs8gxx_read_reg(priv, 0x4B, &t);
+	if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
+		ret = lgs8gxx_read_reg(priv, 0x13, &t);
+	else
+		ret = lgs8gxx_read_reg(priv, 0x4B, &t);
 	if (ret != 0)
 		return ret;
 
-	*locked = ((t & 0xC0) == 0xC0) ? 1 : 0;
+	if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
+		*locked = ((t & 0x80) == 0x80) ? 1 : 0;
+	else
+		*locked = ((t & 0xC0) == 0xC0) ? 1 : 0;
+	return 0;
+}
+
+/* Wait for Code Acquisition Lock */
+static int lgs8gxx_wait_ca_lock(struct lgs8gxx_state *priv, u8 *locked)
+{
+	int ret = 0;
+	u8 reg, mask, val;
+
+	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+		reg = 0x13;
+		mask = 0x80;
+		val = 0x80;
+	} else {
+		reg = 0x4B;
+		mask = 0xC0;
+		val = 0xC0;
+	}
+
+	ret = wait_reg_mask(priv, reg, mask, val, 50, 40);
+	*locked = (ret == 0) ? 1 : 0;
+
 	return 0;
 }
 
@@ -238,21 +388,30 @@ static int lgs8gxx_is_autodetect_finished(struct lgs8gxx_state *priv,
 					  u8 *finished)
 {
 	int ret = 0;
-	u8 t;
+	u8 reg, mask, val;
 
-	ret = lgs8gxx_read_reg(priv, 0xA4, &t);
-	if (ret != 0)
-		return ret;
+	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+		reg = 0x1f;
+		mask = 0xC0;
+		val = 0x80;
+	} else {
+		reg = 0xA4;
+		mask = 0x03;
+		val = 0x01;
+	}
 
-	*finished = ((t & 0x3) == 0x1) ? 1 : 0;
+	ret = wait_reg_mask(priv, reg, mask, val, 10, 20);
+	*finished = (ret == 0) ? 1 : 0;
 
 	return 0;
 }
 
-static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 *locked)
+static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 cpn,
+	u8 *locked)
 {
-	int err;
+	int err = 0;
 	u8 ad_fini = 0;
+	u8 t1, t2;
 
 	if (gi == GI_945)
 		dprintk("try GI 945\n");
@@ -260,17 +419,29 @@ static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 *locked)
 		dprintk("try GI 595\n");
 	else if (gi == GI_420)
 		dprintk("try GI 420\n");
-	lgs8gxx_write_reg(priv, 0x04, gi);
+	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+		lgs8gxx_read_reg(priv, 0x0C, &t1);
+		lgs8gxx_read_reg(priv, 0x18, &t2);
+		t1 &= ~(GI_MASK);
+		t1 |= gi;
+		t2 &= 0xFE;
+		t2 |= cpn ? 0x01 : 0x00;
+		lgs8gxx_write_reg(priv, 0x0C, t1);
+		lgs8gxx_write_reg(priv, 0x18, t2);
+	} else {
+		lgs8gxx_write_reg(priv, 0x04, gi);
+	}
 	lgs8gxx_soft_reset(priv);
-	msleep(50);
+	err = lgs8gxx_wait_ca_lock(priv, locked);
+	if (err || !(*locked))
+		return err;
 	err = lgs8gxx_is_autodetect_finished(priv, &ad_fini);
 	if (err != 0)
 		return err;
 	if (ad_fini) {
-		err = lgs8gxx_is_locked(priv, locked);
-		if (err != 0)
-			return err;
-	}
+		dprintk("auto detect finished\n");
+	} else
+		*locked = 0;
 
 	return 0;
 }
@@ -285,13 +456,18 @@ static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv,
 	dprintk("%s\n", __func__);
 
 	lgs8gxx_set_mode_auto(priv);
-	/* Guard Interval */
-	lgs8gxx_write_reg(priv, 0x03, 00);
+	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+		lgs8gxx_write_reg(priv, 0x67, 0xAA);
+		lgs8gxx_write_reg(priv, 0x6E, 0x3F);
+	} else {
+		/* Guard Interval */
+		lgs8gxx_write_reg(priv, 0x03, 00);
+	}
 
 	for (i = 0; i < 2; i++) {
 		for (j = 0; j < 2; j++) {
 			tmp_gi = GI_945;
-			err = lgs8gxx_autolock_gi(priv, GI_945, &locked);
+			err = lgs8gxx_autolock_gi(priv, GI_945, j, &locked);
 			if (err)
 				goto out;
 			if (locked)
@@ -299,14 +475,14 @@ static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv,
 		}
 		for (j = 0; j < 2; j++) {
 			tmp_gi = GI_420;
-			err = lgs8gxx_autolock_gi(priv, GI_420, &locked);
+			err = lgs8gxx_autolock_gi(priv, GI_420, j, &locked);
 			if (err)
 				goto out;
 			if (locked)
 				goto locked;
 		}
 		tmp_gi = GI_595;
-		err = lgs8gxx_autolock_gi(priv, GI_595, &locked);
+		err = lgs8gxx_autolock_gi(priv, GI_595, 1, &locked);
 		if (err)
 			goto out;
 		if (locked)
@@ -317,8 +493,13 @@ locked:
 	if ((err == 0) && (locked == 1)) {
 		u8 t;
 
-		lgs8gxx_read_reg(priv, 0xA2, &t);
-		*detected_param = t;
+		if (priv->config->prod != LGS8GXX_PROD_LGS8G75) {
+			lgs8gxx_read_reg(priv, 0xA2, &t);
+			*detected_param = t;
+		} else {
+			lgs8gxx_read_reg(priv, 0x1F, &t);
+			*detected_param = t & 0x3F;
+		}
 
 		if (tmp_gi == GI_945)
 			dprintk("GI 945 locked\n");
@@ -345,18 +526,28 @@ static void lgs8gxx_auto_lock(struct lgs8gxx_state *priv)
 
 	if (err != 0) {
 		dprintk("lgs8gxx_auto_detect failed\n");
-	}
+	} else
+		dprintk("detected param = 0x%02X\n", detected_param);
 
 	/* Apply detected parameters */
 	if (priv->config->prod == LGS8GXX_PROD_LGS8913) {
 		u8 inter_leave_len = detected_param & TIM_MASK ;
-		inter_leave_len = (inter_leave_len == TIM_LONG) ? 0x60 : 0x40;
+		/* Fix 8913 time interleaver detection bug */
+		inter_leave_len = (inter_leave_len == TIM_MIDDLE) ? 0x60 : 0x40;
 		detected_param &= CF_MASK | SC_MASK  | LGS_FEC_MASK;
 		detected_param |= inter_leave_len;
 	}
-	lgs8gxx_write_reg(priv, 0x7D, detected_param);
-	if (priv->config->prod == LGS8GXX_PROD_LGS8913)
-		lgs8gxx_write_reg(priv, 0xC0, detected_param);
+	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+		u8 t;
+		lgs8gxx_read_reg(priv, 0x19, &t);
+		t &= 0x81;
+		t |= detected_param << 1;
+		lgs8gxx_write_reg(priv, 0x19, t);
+	} else {
+		lgs8gxx_write_reg(priv, 0x7D, detected_param);
+		if (priv->config->prod == LGS8GXX_PROD_LGS8913)
+			lgs8gxx_write_reg(priv, 0xC0, detected_param);
+	}
 	/* lgs8gxx_soft_reset(priv); */
 
 	/* Enter manual mode */
@@ -378,9 +569,10 @@ static int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv,
 	u8 serial, u8 clk_pol, u8 clk_gated)
 {
 	int ret = 0;
-	u8 t;
+	u8 t, reg_addr;
 
-	ret = lgs8gxx_read_reg(priv, 0xC2, &t);
+	reg_addr = (priv->config->prod == LGS8GXX_PROD_LGS8G75) ? 0x30 : 0xC2;
+	ret = lgs8gxx_read_reg(priv, reg_addr, &t);
 	if (ret != 0)
 		return ret;
 
@@ -389,13 +581,29 @@ static int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv,
 	t |= clk_pol ? TS_CLK_INVERTED : TS_CLK_NORMAL;
 	t |= clk_gated ? TS_CLK_GATED : TS_CLK_FREERUN;
 
-	ret = lgs8gxx_write_reg(priv, 0xC2, t);
+	ret = lgs8gxx_write_reg(priv, reg_addr, t);
 	if (ret != 0)
 		return ret;
 
 	return 0;
 }
 
+/* A/D input peak-to-peak voltage range */
+static int lgs8g75_set_adc_vpp(struct lgs8gxx_state *priv,
+	u8 sel)
+{
+	u8 r26 = 0x73, r27 = 0x90;
+
+	if (priv->config->prod != LGS8GXX_PROD_LGS8G75)
+		return 0;
+
+	r26 |= (sel & 0x01) << 7;
+	r27 |= (sel & 0x02) >> 1;
+	lgs8gxx_write_reg(priv, 0x26, r26);
+	lgs8gxx_write_reg(priv, 0x27, r27);
+
+	return 0;
+}
 
 /* LGS8913 demod frontend functions */
 
@@ -417,6 +625,34 @@ static int lgs8913_init(struct lgs8gxx_state *priv)
 	return 0;
 }
 
+static int lgs8g75_init_data(struct lgs8gxx_state *priv)
+{
+	const u8 *p = lgs8g75_initdat;
+	int i;
+
+	lgs8gxx_write_reg(priv, 0xC6, 0x40);
+
+	lgs8gxx_write_reg(priv, 0x3D, 0x04);
+	lgs8gxx_write_reg(priv, 0x39, 0x00);
+
+	lgs8gxx_write_reg(priv, 0x3A, 0x00);
+	lgs8gxx_write_reg(priv, 0x38, 0x00);
+	lgs8gxx_write_reg(priv, 0x3B, 0x00);
+	lgs8gxx_write_reg(priv, 0x38, 0x00);
+
+	for (i = 0; i < sizeof(lgs8g75_initdat); i++) {
+		lgs8gxx_write_reg(priv, 0x38, 0x00);
+		lgs8gxx_write_reg(priv, 0x3A, (u8)(i&0xff));
+		lgs8gxx_write_reg(priv, 0x3B, (u8)(i>>8));
+		lgs8gxx_write_reg(priv, 0x3C, *p);
+		p++;
+	}
+
+	lgs8gxx_write_reg(priv, 0x38, 0x00);
+
+	return 0;
+}
+
 static int lgs8gxx_init(struct dvb_frontend *fe)
 {
 	struct lgs8gxx_state *priv =
@@ -429,6 +665,9 @@ static int lgs8gxx_init(struct dvb_frontend *fe)
 	lgs8gxx_read_reg(priv, 0, &data);
 	dprintk("reg 0 = 0x%02X\n", data);
 
+	if (config->prod == LGS8GXX_PROD_LGS8G75)
+		lgs8g75_set_adc_vpp(priv, config->adc_vpp);
+
 	/* Setup MPEG output format */
 	err = lgs8gxx_set_mpeg_mode(priv, config->serial_ts,
 				    config->ts_clk_pol,
@@ -439,8 +678,7 @@ static int lgs8gxx_init(struct dvb_frontend *fe)
 	if (config->prod == LGS8GXX_PROD_LGS8913)
 		lgs8913_init(priv);
 	lgs8gxx_set_if_freq(priv, priv->config->if_freq);
-	if (config->prod != LGS8GXX_PROD_LGS8913)
-		lgs8gxx_set_ad_mode(priv);
+	lgs8gxx_set_ad_mode(priv);
 
 	return 0;
 }
@@ -489,9 +727,6 @@ static int lgs8gxx_set_fe(struct dvb_frontend *fe,
 static int lgs8gxx_get_fe(struct dvb_frontend *fe,
 			  struct dvb_frontend_parameters *fe_params)
 {
-	struct lgs8gxx_state *priv = fe->demodulator_priv;
-	u8 t;
-
 	dprintk("%s\n", __func__);
 
 	/* TODO: get real readings from device */
@@ -501,29 +736,10 @@ static int lgs8gxx_get_fe(struct dvb_frontend *fe,
 	/* bandwidth */
 	fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
 
-
-	lgs8gxx_read_reg(priv, 0x7D, &t);
 	fe_params->u.ofdm.code_rate_HP = FEC_AUTO;
 	fe_params->u.ofdm.code_rate_LP = FEC_AUTO;
 
-	/* constellation */
-	switch (t & SC_MASK) {
-	case SC_QAM64:
-		fe_params->u.ofdm.constellation = QAM_64;
-		break;
-	case SC_QAM32:
-		fe_params->u.ofdm.constellation = QAM_32;
-		break;
-	case SC_QAM16:
-		fe_params->u.ofdm.constellation = QAM_16;
-		break;
-	case SC_QAM4:
-	case SC_QAM4NR:
-		fe_params->u.ofdm.constellation = QPSK;
-		break;
-	default:
-		fe_params->u.ofdm.constellation = QAM_64;
-	}
+	fe_params->u.ofdm.constellation = QAM_AUTO;
 
 	/* transmission mode */
 	fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO;
@@ -552,9 +768,19 @@ static int lgs8gxx_read_status(struct dvb_frontend *fe, fe_status_t *fe_status)
 {
 	struct lgs8gxx_state *priv = fe->demodulator_priv;
 	s8 ret;
-	u8 t;
+	u8 t, locked = 0;
 
 	dprintk("%s\n", __func__);
+	*fe_status = 0;
+
+	lgs8gxx_get_afc_phase(priv);
+	lgs8gxx_is_locked(priv, &locked);
+	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+		if (locked)
+			*fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+				FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+		return 0;
+	}
 
 	ret = lgs8gxx_read_reg(priv, 0x4B, &t);
 	if (ret != 0)
@@ -658,12 +884,33 @@ static int lgs8913_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal)
 	return 0;
 }
 
+static int lgs8g75_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal)
+{
+	u8 t;
+	s16 v = 0;
+
+	dprintk("%s\n", __func__);
+
+	lgs8gxx_read_reg(priv, 0xB1, &t);
+	v |= t;
+	v <<= 8;
+	lgs8gxx_read_reg(priv, 0xB0, &t);
+	v |= t;
+
+	*signal = v;
+	dprintk("%s: signal=0x%02X\n", __func__, *signal);
+
+	return 0;
+}
+
 static int lgs8gxx_read_signal_strength(struct dvb_frontend *fe, u16 *signal)
 {
 	struct lgs8gxx_state *priv = fe->demodulator_priv;
 
 	if (priv->config->prod == LGS8GXX_PROD_LGS8913)
 		return lgs8913_read_signal_strength(priv, signal);
+	else if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
+		return lgs8g75_read_signal_strength(priv, signal);
 	else
 		return lgs8gxx_read_signal_agc(priv, signal);
 }
@@ -674,7 +921,10 @@ static int lgs8gxx_read_snr(struct dvb_frontend *fe, u16 *snr)
 	u8 t;
 	*snr = 0;
 
-	lgs8gxx_read_reg(priv, 0x95, &t);
+	if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
+		lgs8gxx_read_reg(priv, 0x34, &t);
+	else
+		lgs8gxx_read_reg(priv, 0x95, &t);
 	dprintk("AVG Noise=0x%02X\n", t);
 	*snr = 256 - t;
 	*snr <<= 8;
@@ -690,31 +940,68 @@ static int lgs8gxx_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
 	return 0;
 }
 
+static void packet_counter_start(struct lgs8gxx_state *priv)
+{
+	u8 orig, t;
+
+	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+		lgs8gxx_read_reg(priv, 0x30, &orig);
+		orig &= 0xE7;
+		t = orig | 0x10;
+		lgs8gxx_write_reg(priv, 0x30, t);
+		t = orig | 0x18;
+		lgs8gxx_write_reg(priv, 0x30, t);
+		t = orig | 0x10;
+		lgs8gxx_write_reg(priv, 0x30, t);
+	} else {
+		lgs8gxx_write_reg(priv, 0xC6, 0x01);
+		lgs8gxx_write_reg(priv, 0xC6, 0x41);
+		lgs8gxx_write_reg(priv, 0xC6, 0x01);
+	}
+}
+
+static void packet_counter_stop(struct lgs8gxx_state *priv)
+{
+	u8 t;
+
+	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+		lgs8gxx_read_reg(priv, 0x30, &t);
+		t &= 0xE7;
+		lgs8gxx_write_reg(priv, 0x30, t);
+	} else {
+		lgs8gxx_write_reg(priv, 0xC6, 0x81);
+	}
+}
+
 static int lgs8gxx_read_ber(struct dvb_frontend *fe, u32 *ber)
 {
 	struct lgs8gxx_state *priv = fe->demodulator_priv;
-	u8 r0, r1, r2, r3;
-	u32 total_cnt, err_cnt;
+	u8 reg_err, reg_total, t;
+	u32 total_cnt = 0, err_cnt = 0;
+	int i;
 
 	dprintk("%s\n", __func__);
 
-	lgs8gxx_write_reg(priv, 0xc6, 0x01);
-	lgs8gxx_write_reg(priv, 0xc6, 0x41);
-	lgs8gxx_write_reg(priv, 0xc6, 0x01);
-
+	packet_counter_start(priv);
 	msleep(200);
+	packet_counter_stop(priv);
+
+	if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+		reg_total = 0x28; reg_err = 0x2C;
+	} else {
+		reg_total = 0xD0; reg_err = 0xD4;
+	}
 
-	lgs8gxx_write_reg(priv, 0xc6, 0x81);
-	lgs8gxx_read_reg(priv, 0xd0, &r0);
-	lgs8gxx_read_reg(priv, 0xd1, &r1);
-	lgs8gxx_read_reg(priv, 0xd2, &r2);
-	lgs8gxx_read_reg(priv, 0xd3, &r3);
-	total_cnt = (r3 << 24) | (r2 << 16) | (r1 << 8) | (r0);
-	lgs8gxx_read_reg(priv, 0xd4, &r0);
-	lgs8gxx_read_reg(priv, 0xd5, &r1);
-	lgs8gxx_read_reg(priv, 0xd6, &r2);
-	lgs8gxx_read_reg(priv, 0xd7, &r3);
-	err_cnt = (r3 << 24) | (r2 << 16) | (r1 << 8) | (r0);
+	for (i = 0; i < 4; i++) {
+		total_cnt <<= 8;
+		lgs8gxx_read_reg(priv, reg_total+3-i, &t);
+		total_cnt |= t;
+	}
+	for (i = 0; i < 4; i++) {
+		err_cnt <<= 8;
+		lgs8gxx_read_reg(priv, reg_err+3-i, &t);
+		err_cnt |= t;
+	}
 	dprintk("error=%d total=%d\n", err_cnt, total_cnt);
 
 	if (total_cnt == 0)
@@ -801,6 +1088,9 @@ struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config,
 	       sizeof(struct dvb_frontend_ops));
 	priv->frontend.demodulator_priv = priv;
 
+	if (config->prod == LGS8GXX_PROD_LGS8G75)
+		lgs8g75_init_data(priv);
+
 	return &priv->frontend;
 
 error_out:

+ 8 - 3
drivers/media/dvb/frontends/lgs8gxx.h

@@ -1,9 +1,9 @@
 /*
- *    Support for Legend Silicon DMB-TH demodulator
- *    LGS8913, LGS8GL5
+ *    Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator
+ *    LGS8913, LGS8GL5, LGS8G75
  *    experimental support LGS8G42, LGS8G52
  *
- *    Copyright (C) 2007,2008 David T.L. Wong <davidtlwong@gmail.com>
+ *    Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com>
  *    Copyright (C) 2008 Sirius International (Hong Kong) Limited
  *    Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5)
  *
@@ -34,6 +34,7 @@
 #define LGS8GXX_PROD_LGS8G42 3
 #define LGS8GXX_PROD_LGS8G52 4
 #define LGS8GXX_PROD_LGS8G54 5
+#define LGS8GXX_PROD_LGS8G75 6
 
 struct lgs8gxx_config {
 
@@ -70,6 +71,10 @@ struct lgs8gxx_config {
 	/*IF use Negative center frequency*/
 	u8 if_neg_center;
 
+	/*8G75 internal ADC input range selection*/
+	/*0: 0.8Vpp, 1: 1.0Vpp, 2: 1.6Vpp, 3: 2.0Vpp*/
+	u8 adc_vpp;
+
 	/* slave address and configuration of the tuner */
 	u8 tuner_address;
 };

+ 6 - 6
drivers/media/dvb/frontends/lgs8gxx_priv.h

@@ -1,9 +1,9 @@
 /*
- *    Support for Legend Silicon DMB-TH demodulator
- *    LGS8913, LGS8GL5
+ *    Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator
+ *    LGS8913, LGS8GL5, LGS8G75
  *    experimental support LGS8G42, LGS8G52
  *
- *    Copyright (C) 2007,2008 David T.L. Wong <davidtlwong@gmail.com>
+ *    Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com>
  *    Copyright (C) 2008 Sirius International (Hong Kong) Limited
  *    Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5)
  *
@@ -38,7 +38,7 @@ struct lgs8gxx_state {
 #define SC_QAM64	0x10	/* 64QAM modulation */
 #define SC_QAM32	0x0C	/* 32QAM modulation */
 #define SC_QAM16	0x08	/* 16QAM modulation */
-#define SC_QAM4NR	0x04	/* 4QAM modulation */
+#define SC_QAM4NR	0x04	/* 4QAM-NR modulation */
 #define SC_QAM4		0x00	/* 4QAM modulation */
 
 #define LGS_FEC_MASK	0x03	/* FEC Rate Mask */
@@ -47,8 +47,8 @@ struct lgs8gxx_state {
 #define LGS_FEC_0_8	0x02	/* FEC Rate 0.8 */
 
 #define TIM_MASK	  0x20	/* Time Interleave Length Mask */
-#define TIM_LONG	  0x00	/* Time Interleave Length = 720 */
-#define TIM_MIDDLE     0x20   /* Time Interleave Length = 240 */
+#define TIM_LONG	  0x20	/* Time Interleave Length = 720 */
+#define TIM_MIDDLE     0x00   /* Time Interleave Length = 240 */
 
 #define CF_MASK	0x80	/* Control Frame Mask */
 #define CF_EN	0x80	/* Control Frame On */

+ 4 - 3
drivers/media/dvb/frontends/mt312.c

@@ -85,7 +85,7 @@ static int mt312_read(struct mt312_state *state, const enum mt312_reg_addr reg,
 		int i;
 		dprintk("R(%d):", reg & 0x7f);
 		for (i = 0; i < count; i++)
-			printk(" %02x", buf[i]);
+			printk(KERN_CONT " %02x", buf[i]);
 		printk("\n");
 	}
 
@@ -103,7 +103,7 @@ static int mt312_write(struct mt312_state *state, const enum mt312_reg_addr reg,
 		int i;
 		dprintk("W(%d):", reg & 0x7f);
 		for (i = 0; i < count; i++)
-			printk(" %02x", src[i]);
+			printk(KERN_CONT " %02x", src[i]);
 		printk("\n");
 	}
 
@@ -744,7 +744,8 @@ static struct dvb_frontend_ops mt312_ops = {
 		.type = FE_QPSK,
 		.frequency_min = 950000,
 		.frequency_max = 2150000,
-		.frequency_stepsize = (MT312_PLL_CLK / 1000) / 128, /* FIXME: adjust freq to real used xtal */
+		/* FIXME: adjust freq to real used xtal */
+		.frequency_stepsize = (MT312_PLL_CLK / 1000) / 128,
 		.symbol_rate_min = MT312_SYS_CLK / 128, /* FIXME as above */
 		.symbol_rate_max = MT312_SYS_CLK / 2,
 		.caps =

+ 3 - 1
drivers/media/dvb/frontends/stb6100.c

@@ -367,7 +367,9 @@ static int stb6100_set_frequency(struct dvb_frontend *fe, u32 frequency)
 	/* N(I) = floor(f(VCO) / (f(XTAL) * (PSD2 ? 2 : 1)))	*/
 	nint = fvco / (state->reference << psd2);
 	/* N(F) = round(f(VCO) / f(XTAL) * (PSD2 ? 2 : 1) - N(I)) * 2 ^ 9	*/
-	nfrac = (((fvco - (nint * state->reference << psd2)) << (9 - psd2)) + state->reference / 2) / state->reference;
+	nfrac = DIV_ROUND_CLOSEST((fvco - (nint * state->reference << psd2))
+					 << (9 - psd2),
+				  state->reference);
 	dprintk(verbose, FE_DEBUG, 1,
 		"frequency = %u, srate = %u, g = %u, odiv = %u, psd2 = %u, fxtal = %u, osm = %u, fvco = %u, N(I) = %u, N(F) = %u",
 		frequency, srate, (unsigned int)g, (unsigned int)odiv,

+ 4 - 4
drivers/media/dvb/frontends/stv0900_core.c

@@ -230,8 +230,8 @@ enum fe_stv0900_error stv0900_initialize(struct stv0900_internal *i_params)
 			stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5c);
 			stv0900_write_reg(i_params, R0900_P1_TNRCFG, 0x6c);
 			stv0900_write_reg(i_params, R0900_P2_TNRCFG, 0x6f);
-			stv0900_write_reg(i_params, R0900_P1_I2CRPT, 0x24);
-			stv0900_write_reg(i_params, R0900_P2_I2CRPT, 0x24);
+			stv0900_write_reg(i_params, R0900_P1_I2CRPT, 0x20);
+			stv0900_write_reg(i_params, R0900_P2_I2CRPT, 0x20);
 			stv0900_write_reg(i_params, R0900_NCOARSE, 0x13);
 			msleep(3);
 			stv0900_write_reg(i_params, R0900_I2CCFG, 0x08);
@@ -370,8 +370,8 @@ static int stv0900_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
 	u32 fi2c;
 
 	dmd_reg(fi2c, F0900_P1_I2CT_ON, F0900_P2_I2CT_ON);
-	if (enable)
-		stv0900_write_bits(i_params, fi2c, 1);
+
+	stv0900_write_bits(i_params, fi2c, enable);
 
 	return 0;
 }

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

@@ -1721,7 +1721,7 @@ static enum fe_stv0900_signal_type stv0900_dvbs1_acq_workaround(struct dvb_front
 
 	s32 srate, demod_timeout,
 		fec_timeout, freq1, freq0;
-	enum fe_stv0900_signal_type signal_type = STV0900_NODATA;;
+	enum fe_stv0900_signal_type signal_type = STV0900_NODATA;
 
 	switch (demod) {
 	case STV0900_DEMOD_1:

+ 24 - 24
drivers/media/dvb/frontends/stv6110.c

@@ -36,6 +36,7 @@ struct stv6110_priv {
 	struct i2c_adapter *i2c;
 
 	u32 mclk;
+	u8 clk_div;
 	u8 regs[8];
 };
 
@@ -100,35 +101,25 @@ static int stv6110_read_regs(struct dvb_frontend *fe, u8 regs[],
 	struct stv6110_priv *priv = fe->tuner_priv;
 	int rc;
 	u8 reg[] = { start };
-	struct i2c_msg msg_wr = {
-		.addr	= priv->i2c_address,
-		.flags	= 0,
-		.buf	= reg,
-		.len	= 1,
+	struct i2c_msg msg[] = {
+		{
+			.addr	= priv->i2c_address,
+			.flags	= 0,
+			.buf	= reg,
+			.len	= 1,
+		}, {
+			.addr	= priv->i2c_address,
+			.flags	= I2C_M_RD,
+			.buf	= regs,
+			.len	= len,
+		},
 	};
 
-	struct i2c_msg msg_rd = {
-		.addr	= priv->i2c_address,
-		.flags	= I2C_M_RD,
-		.buf	= regs,
-		.len	= len,
-	};
-	/* write subaddr */
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 1);
 
-	rc = i2c_transfer(priv->i2c, &msg_wr, 1);
-	if (rc != 1)
-		dprintk("%s: i2c error\n", __func__);
-
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 0);
-	/* read registers */
-	if (fe->ops.i2c_gate_ctrl)
-		fe->ops.i2c_gate_ctrl(fe, 1);
-
-	rc = i2c_transfer(priv->i2c, &msg_rd, 1);
-	if (rc != 1)
+	rc = i2c_transfer(priv->i2c, msg, 2);
+	if (rc != 2)
 		dprintk("%s: i2c error\n", __func__);
 
 	if (fe->ops.i2c_gate_ctrl)
@@ -221,6 +212,10 @@ static int stv6110_init(struct dvb_frontend *fe)
 	priv->regs[RSTV6110_CTRL1] |=
 				((((priv->mclk / 1000000) - 16) & 0x1f) << 3);
 
+	/* divisor value for the output clock */
+	priv->regs[RSTV6110_CTRL2] &= ~0xc0;
+	priv->regs[RSTV6110_CTRL2] |= (priv->clk_div << 6);
+
 	stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL1], RSTV6110_CTRL1, 8);
 	msleep(1);
 	stv6110_set_bandwidth(fe, 72000000);
@@ -418,6 +413,10 @@ struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe,
 	};
 	int ret;
 
+	/* divisor value for the output clock */
+	reg0[2] &= ~0xc0;
+	reg0[2] |= (config->clk_div << 6);
+
 	if (fe->ops.i2c_gate_ctrl)
 		fe->ops.i2c_gate_ctrl(fe, 1);
 
@@ -436,6 +435,7 @@ struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe,
 	priv->i2c_address = config->i2c_address;
 	priv->i2c = i2c;
 	priv->mclk = config->mclk;
+	priv->clk_div = config->clk_div;
 
 	memcpy(&priv->regs, &reg0[1], 8);
 

+ 1 - 1
drivers/media/dvb/frontends/stv6110.h

@@ -41,7 +41,7 @@
 struct stv6110_config {
 	u8 i2c_address;
 	u32 mclk;
-	int iq_wiring;
+	u8 clk_div;	/* divisor value for the output clock */
 };
 
 #if defined(CONFIG_DVB_STV6110) || (defined(CONFIG_DVB_STV6110_MODULE) \

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

@@ -176,7 +176,7 @@ static int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate
 	tmp =  ((symbolrate << 4) % FIN) << 8;
 	ratio = (ratio << 8) + tmp / FIN;
 	tmp = (tmp % FIN) << 8;
-	ratio = (ratio << 8) + (tmp + FIN/2) / FIN;
+	ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, FIN);
 
 	BDR = ratio;
 	BDRI = (((XIN << 5) / symbolrate) + 1) / 2;

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

@@ -136,9 +136,9 @@ static int tda8261_set_state(struct dvb_frontend *fe,
 
 		if (frequency < 1450000)
 			buf[3] = 0x00;
-		if (frequency < 2000000)
+		else if (frequency < 2000000)
 			buf[3] = 0x40;
-		if (frequency < 2150000)
+		else if (frequency < 2150000)
 			buf[3] = 0x80;
 
 		/* Set params */

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

@@ -165,7 +165,7 @@ static int ves1820_set_symbolrate(struct ves1820_state *state, u32 symbolrate)
 	tmp = ((symbolrate << 4) % fin) << 8;
 	ratio = (ratio << 8) + tmp / fin;
 	tmp = (tmp % fin) << 8;
-	ratio = (ratio << 8) + (tmp + fin / 2) / fin;
+	ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, fin);
 
 	BDR = ratio;
 	BDRI = (((state->config->xin << 5) / symbolrate) + 1) / 2;

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

@@ -29,7 +29,7 @@
 
 #include <linux/module.h>
 #include <linux/dvb/frontend.h>
-#include <asm/types.h>
+#include <linux/types.h>
 
 #include "zl10036.h"
 

+ 308 - 0
drivers/media/dvb/frontends/zl10039.c

@@ -0,0 +1,308 @@
+/*
+ *  Driver for Zarlink ZL10039 DVB-S tuner
+ *
+ *  Copyright 2007 Jan D. Louw <jd.louw@mweb.co.za>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/dvb/frontend.h>
+
+#include "dvb_frontend.h"
+#include "zl10039.h"
+
+static int debug;
+
+#define dprintk(args...) \
+	do { \
+		if (debug) \
+			printk(KERN_DEBUG args); \
+	} while (0)
+
+enum zl10039_model_id {
+	ID_ZL10039 = 1
+};
+
+struct zl10039_state {
+	struct i2c_adapter *i2c;
+	u8 i2c_addr;
+	u8 id;
+};
+
+enum zl10039_reg_addr {
+	PLL0 = 0,
+	PLL1,
+	PLL2,
+	PLL3,
+	RFFE,
+	BASE0,
+	BASE1,
+	BASE2,
+	LO0,
+	LO1,
+	LO2,
+	LO3,
+	LO4,
+	LO5,
+	LO6,
+	GENERAL
+};
+
+static int zl10039_read(const struct zl10039_state *state,
+			const enum zl10039_reg_addr reg, u8 *buf,
+			const size_t count)
+{
+	u8 regbuf[] = { reg };
+	struct i2c_msg msg[] = {
+		{/* Write register address */
+			.addr = state->i2c_addr,
+			.flags = 0,
+			.buf = regbuf,
+			.len = 1,
+		}, {/* Read count bytes */
+			.addr = state->i2c_addr,
+			.flags = I2C_M_RD,
+			.buf = buf,
+			.len = count,
+		},
+	};
+
+	dprintk("%s\n", __func__);
+
+	if (i2c_transfer(state->i2c, msg, 2) != 2) {
+		dprintk("%s: i2c read error\n", __func__);
+		return -EREMOTEIO;
+	}
+
+	return 0; /* Success */
+}
+
+static int zl10039_write(struct zl10039_state *state,
+			const enum zl10039_reg_addr reg, const u8 *src,
+			const size_t count)
+{
+	u8 buf[count + 1];
+	struct i2c_msg msg = {
+		.addr = state->i2c_addr,
+		.flags = 0,
+		.buf = buf,
+		.len = count + 1,
+	};
+
+	dprintk("%s\n", __func__);
+	/* Write register address and data in one go */
+	buf[0] = reg;
+	memcpy(&buf[1], src, count);
+	if (i2c_transfer(state->i2c, &msg, 1) != 1) {
+		dprintk("%s: i2c write error\n", __func__);
+		return -EREMOTEIO;
+	}
+
+	return 0; /* Success */
+}
+
+static inline int zl10039_readreg(struct zl10039_state *state,
+				const enum zl10039_reg_addr reg, u8 *val)
+{
+	return zl10039_read(state, reg, val, 1);
+}
+
+static inline int zl10039_writereg(struct zl10039_state *state,
+				const enum zl10039_reg_addr reg,
+				const u8 val)
+{
+	return zl10039_write(state, reg, &val, 1);
+}
+
+static int zl10039_init(struct dvb_frontend *fe)
+{
+	struct zl10039_state *state = fe->tuner_priv;
+	int ret;
+
+	dprintk("%s\n", __func__);
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	/* Reset logic */
+	ret = zl10039_writereg(state, GENERAL, 0x40);
+	if (ret < 0) {
+		dprintk("Note: i2c write error normal when resetting the "
+			"tuner\n");
+	}
+	/* Wake up */
+	ret = zl10039_writereg(state, GENERAL, 0x01);
+	if (ret < 0) {
+		dprintk("Tuner power up failed\n");
+		return ret;
+	}
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	return 0;
+}
+
+static int zl10039_sleep(struct dvb_frontend *fe)
+{
+	struct zl10039_state *state = fe->tuner_priv;
+	int ret;
+
+	dprintk("%s\n", __func__);
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	ret = zl10039_writereg(state, GENERAL, 0x80);
+	if (ret < 0) {
+		dprintk("Tuner sleep failed\n");
+		return ret;
+	}
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	return 0;
+}
+
+static int zl10039_set_params(struct dvb_frontend *fe,
+			struct dvb_frontend_parameters *params)
+{
+	struct zl10039_state *state = fe->tuner_priv;
+	u8 buf[6];
+	u8 bf;
+	u32 fbw;
+	u32 div;
+	int ret;
+
+	dprintk("%s\n", __func__);
+	dprintk("Set frequency = %d, symbol rate = %d\n",
+			params->frequency, params->u.qpsk.symbol_rate);
+
+	/* Assumed 10.111 MHz crystal oscillator */
+	/* Cancelled num/den 80 to prevent overflow */
+	div = (params->frequency * 1000) / 126387;
+	fbw = (params->u.qpsk.symbol_rate * 27) / 32000;
+	/* Cancelled num/den 10 to prevent overflow */
+	bf = ((fbw * 5088) / 1011100) - 1;
+
+	/*PLL divider*/
+	buf[0] = (div >> 8) & 0x7f;
+	buf[1] = (div >> 0) & 0xff;
+	/*Reference divider*/
+	/* Select reference ratio of 80 */
+	buf[2] = 0x1D;
+	/*PLL test modes*/
+	buf[3] = 0x40;
+	/*RF Control register*/
+	buf[4] = 0x6E; /* Bypass enable */
+	/*Baseband filter cutoff */
+	buf[5] = bf;
+
+	/* Open i2c gate */
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	/* BR = 10, Enable filter adjustment */
+	ret = zl10039_writereg(state, BASE1, 0x0A);
+	if (ret < 0)
+		goto error;
+	/* Write new config values */
+	ret = zl10039_write(state, PLL0, buf, sizeof(buf));
+	if (ret < 0)
+		goto error;
+	/* BR = 10, Disable filter adjustment */
+	ret = zl10039_writereg(state, BASE1, 0x6A);
+	if (ret < 0)
+		goto error;
+
+	/* Close i2c gate */
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+	return 0;
+error:
+	dprintk("Error setting tuner\n");
+	return ret;
+}
+
+static int zl10039_release(struct dvb_frontend *fe)
+{
+	struct zl10039_state *state = fe->tuner_priv;
+
+	dprintk("%s\n", __func__);
+	kfree(state);
+	fe->tuner_priv = NULL;
+	return 0;
+}
+
+static struct dvb_tuner_ops zl10039_ops = {
+	.release = zl10039_release,
+	.init = zl10039_init,
+	.sleep = zl10039_sleep,
+	.set_params = zl10039_set_params,
+};
+
+struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe,
+		u8 i2c_addr, struct i2c_adapter *i2c)
+{
+	struct zl10039_state *state = NULL;
+
+	dprintk("%s\n", __func__);
+	state = kmalloc(sizeof(struct zl10039_state), GFP_KERNEL);
+	if (state == NULL)
+		goto error;
+
+	state->i2c = i2c;
+	state->i2c_addr = i2c_addr;
+
+	/* Open i2c gate */
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 1);
+	/* check if this is a valid tuner */
+	if (zl10039_readreg(state, GENERAL, &state->id) < 0) {
+		/* Close i2c gate */
+		if (fe->ops.i2c_gate_ctrl)
+			fe->ops.i2c_gate_ctrl(fe, 0);
+		goto error;
+	}
+	/* Close i2c gate */
+	if (fe->ops.i2c_gate_ctrl)
+		fe->ops.i2c_gate_ctrl(fe, 0);
+
+	state->id = state->id & 0x0f;
+	switch (state->id) {
+	case ID_ZL10039:
+		strcpy(fe->ops.tuner_ops.info.name,
+			"Zarlink ZL10039 DVB-S tuner");
+		break;
+	default:
+		dprintk("Chip ID=%x does not match a known type\n", state->id);
+		break;
+		goto error;
+	}
+
+	memcpy(&fe->ops.tuner_ops, &zl10039_ops, sizeof(struct dvb_tuner_ops));
+	fe->tuner_priv = state;
+	dprintk("Tuner attached @ i2c address 0x%02x\n", i2c_addr);
+	return fe;
+error:
+	kfree(state);
+	return NULL;
+}
+EXPORT_SYMBOL(zl10039_attach);
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+MODULE_DESCRIPTION("Zarlink ZL10039 DVB-S tuner driver");
+MODULE_AUTHOR("Jan D. Louw <jd.louw@mweb.co.za>");
+MODULE_LICENSE("GPL");

+ 40 - 0
drivers/media/dvb/frontends/zl10039.h

@@ -0,0 +1,40 @@
+/*
+    Driver for Zarlink ZL10039 DVB-S tuner
+
+    Copyright (C) 2007 Jan D. Louw <jd.louw@mweb.co.za>
+
+    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 ZL10039_H
+#define ZL10039_H
+
+#if defined(CONFIG_DVB_ZL10039) || (defined(CONFIG_DVB_ZL10039_MODULE) \
+	    && defined(MODULE))
+struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe,
+					u8 i2c_addr,
+					struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe,
+					u8 i2c_addr,
+					struct i2c_adapter *i2c)
+{
+	printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+	return NULL;
+}
+#endif /* CONFIG_DVB_ZL10039 */
+
+#endif /* ZL10039_H */

+ 11 - 3
drivers/media/dvb/frontends/zl10353.c

@@ -38,6 +38,8 @@ struct zl10353_state {
 	struct zl10353_config config;
 
 	enum fe_bandwidth bandwidth;
+       u32 ucblocks;
+       u32 frequency;
 };
 
 static int debug;
@@ -199,6 +201,8 @@ static int zl10353_set_parameters(struct dvb_frontend *fe,
 	u16 tps = 0;
 	struct dvb_ofdm_parameters *op = &param->u.ofdm;
 
+       state->frequency = param->frequency;
+
 	zl10353_single_write(fe, RESET, 0x80);
 	udelay(200);
 	zl10353_single_write(fe, 0xEA, 0x01);
@@ -464,7 +468,7 @@ static int zl10353_get_parameters(struct dvb_frontend *fe,
 		break;
 	}
 
-	param->frequency = 0;
+       param->frequency = state->frequency;
 	op->bandwidth = state->bandwidth;
 	param->inversion = INVERSION_AUTO;
 
@@ -542,9 +546,13 @@ static int zl10353_read_snr(struct dvb_frontend *fe, u16 *snr)
 static int zl10353_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
 {
 	struct zl10353_state *state = fe->demodulator_priv;
+       u32 ubl = 0;
+
+       ubl = zl10353_read_register(state, RS_UBC_1) << 8 |
+	     zl10353_read_register(state, RS_UBC_0);
 
-	*ucblocks = zl10353_read_register(state, RS_UBC_1) << 8 |
-		    zl10353_read_register(state, RS_UBC_0);
+       state->ucblocks += ubl;
+       *ucblocks = state->ucblocks;
 
 	return 0;
 }

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

@@ -439,7 +439,7 @@ static inline u32 divide(u32 numerator, u32 denominator)
 	if (denominator == 0)
 		return ~0;
 
-	return (numerator + denominator / 2) / denominator;
+	return DIV_ROUND_CLOSEST(numerator, denominator);
 }
 
 /* LG Innotek TDTE-E001P (Infineon TUA6034) */

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

@@ -490,7 +490,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
 	if (!av7110->analog_tuner_flags)
 		return 0;
 
-	if (input < 0 || input >= 4)
+	if (input >= 4)
 		return -EINVAL;
 
 	av7110->current_input = input;

+ 3 - 3
drivers/media/dvb/ttpci/budget-ci.c

@@ -225,7 +225,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci)
 	case 0x1012:
 		/* The hauppauge keymap is a superset of these remotes */
 		ir_input_init(input_dev, &budget_ci->ir.state,
-			      IR_TYPE_RC5, ir_codes_hauppauge_new);
+			      IR_TYPE_RC5, &ir_codes_hauppauge_new_table);
 
 		if (rc5_device < 0)
 			budget_ci->ir.rc5_device = 0x1f;
@@ -237,7 +237,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci)
 	case 0x101a:
 		/* for the Technotrend 1500 bundled remote */
 		ir_input_init(input_dev, &budget_ci->ir.state,
-			      IR_TYPE_RC5, ir_codes_tt_1500);
+			      IR_TYPE_RC5, &ir_codes_tt_1500_table);
 
 		if (rc5_device < 0)
 			budget_ci->ir.rc5_device = IR_DEVICE_ANY;
@@ -247,7 +247,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci)
 	default:
 		/* unknown remote */
 		ir_input_init(input_dev, &budget_ci->ir.state,
-			      IR_TYPE_RC5, ir_codes_budget_ci_old);
+			      IR_TYPE_RC5, &ir_codes_budget_ci_old_table);
 
 		if (rc5_device < 0)
 			budget_ci->ir.rc5_device = IR_DEVICE_ANY;

+ 27 - 32
drivers/media/radio/Kconfig

@@ -288,16 +288,6 @@ config RADIO_TYPHOON
 	  To compile this driver as a module, choose M here: the
 	  module will be called radio-typhoon.
 
-config RADIO_TYPHOON_PROC_FS
-	bool "Support for /proc/radio-typhoon"
-	depends on PROC_FS && RADIO_TYPHOON
-	help
-	  Say Y here if you want the typhoon radio card driver to write
-	  status information (frequency, volume, muted, mute frequency,
-	  base address) to /proc/radio-typhoon. The file can be viewed with
-	  your favorite pager (i.e. use "more /proc/radio-typhoon" or "less
-	  /proc/radio-typhoon" or simply "cat /proc/radio-typhoon").
-
 config RADIO_TYPHOON_PORT
 	hex "Typhoon I/O port (0x316 or 0x336)"
 	depends on RADIO_TYPHOON=y
@@ -339,6 +329,29 @@ config RADIO_ZOLTRIX_PORT
 	help
 	  Enter the I/O port of your Zoltrix radio card.
 
+config I2C_SI4713
+	tristate "I2C driver for Silicon Labs Si4713 device"
+	depends on I2C && VIDEO_V4L2
+	---help---
+	  Say Y here if you want support to Si4713 I2C device.
+	  This device driver supports only i2c bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called si4713.
+
+config RADIO_SI4713
+	tristate "Silicon Labs Si4713 FM Radio Transmitter support"
+	depends on I2C && VIDEO_V4L2
+	select I2C_SI4713
+	---help---
+	  Say Y here if you want support to Si4713 FM Radio Transmitter.
+	  This device can transmit audio through FM. It can transmit
+	  EDS and EBDS signals as well. This module is the v4l2 radio
+	  interface for the i2c driver of this device.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called radio-si4713.
+
 config USB_DSBR
 	tristate "D-Link/GemTek USB FM radio support"
 	depends on USB && VIDEO_V4L2
@@ -351,29 +364,11 @@ config USB_DSBR
 	  To compile this driver as a module, choose M here: the
 	  module will be called dsbr100.
 
-config USB_SI470X
-	tristate "Silicon Labs Si470x FM Radio Receiver support"
-	depends on USB && VIDEO_V4L2
-	---help---
-	  This is a driver for USB devices with the Silicon Labs SI470x
-	  chip. Currently these devices are known to work:
-	  - 10c4:818a: Silicon Labs USB FM Radio Reference Design
-	  - 06e1:a155: ADS/Tech FM Radio Receiver (formerly Instant FM Music)
-	  - 1b80:d700: KWorld USB FM Radio SnapMusic Mobile 700 (FM700)
-
-	  Sound is provided by the ALSA USB Audio/MIDI driver. Therefore
-	  if you don't want to use the device solely for RDS receiving,
-	  it is recommended to also select SND_USB_AUDIO.
-
-	  Please have a look at the documentation, especially on how
-	  to redirect the audio stream from the radio to your sound device:
-	  Documentation/video4linux/si470x.txt
-
-	  Say Y here if you want to connect this type of radio to your
-	  computer's USB port.
+config RADIO_SI470X
+	bool "Silicon Labs Si470x FM Radio Receiver support"
+	depends on VIDEO_V4L2
 
-	  To compile this driver as a module, choose M here: the
-	  module will be called radio-si470x.
+source "drivers/media/radio/si470x/Kconfig"
 
 config USB_MR800
 	tristate "AverMedia MR 800 USB FM radio support"

+ 3 - 1
drivers/media/radio/Makefile

@@ -15,9 +15,11 @@ obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o
 obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o
 obj-$(CONFIG_RADIO_GEMTEK_PCI) += radio-gemtek-pci.o
 obj-$(CONFIG_RADIO_TRUST) += radio-trust.o
+obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o
+obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o
 obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o
 obj-$(CONFIG_USB_DSBR) += dsbr100.o
-obj-$(CONFIG_USB_SI470X) += radio-si470x.o
+obj-$(CONFIG_RADIO_SI470X) += si470x/
 obj-$(CONFIG_USB_MR800) += radio-mr800.o
 obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o
 

+ 4 - 2
drivers/media/radio/radio-cadet.c

@@ -359,7 +359,8 @@ static int vidioc_querycap(struct file *file, void *priv,
 	strlcpy(v->card, "ADS Cadet", sizeof(v->card));
 	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
 	v->version = CADET_VERSION;
-	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_READWRITE;
+	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
+			  V4L2_CAP_READWRITE | V4L2_CAP_RDS_CAPTURE;
 	return 0;
 }
 
@@ -372,7 +373,7 @@ static int vidioc_g_tuner(struct file *file, void *priv,
 	switch (v->index) {
 	case 0:
 		strlcpy(v->name, "FM", sizeof(v->name));
-		v->capability = V4L2_TUNER_CAP_STEREO;
+		v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS;
 		v->rangelow = 1400;     /* 87.5 MHz */
 		v->rangehigh = 1728;    /* 108.0 MHz */
 		v->rxsubchans = cadet_getstereo(dev);
@@ -386,6 +387,7 @@ static int vidioc_g_tuner(struct file *file, void *priv,
 		default:
 			break;
 		}
+		v->rxsubchans |= V4L2_TUNER_SUB_RDS;
 		break;
 	case 1:
 		strlcpy(v->name, "AM", sizeof(v->name));

+ 0 - 1863
drivers/media/radio/radio-si470x.c

@@ -1,1863 +0,0 @@
-/*
- *  drivers/media/radio/radio-si470x.c
- *
- *  Driver for USB radios for the Silicon Labs Si470x FM Radio Receivers:
- *   - Silicon Labs USB FM Radio Reference Design
- *   - ADS/Tech FM Radio Receiver (formerly Instant FM Music) (RDX-155-EF)
- *   - KWorld USB FM Radio SnapMusic Mobile 700 (FM700)
- *   - Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear)
- *
- *  Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net>
- *
- * 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
- */
-
-
-/*
- * History:
- * 2008-01-12	Tobias Lorenz <tobias.lorenz@gmx.net>
- *		Version 1.0.0
- *		- First working version
- * 2008-01-13	Tobias Lorenz <tobias.lorenz@gmx.net>
- *		Version 1.0.1
- *		- Improved error handling, every function now returns errno
- *		- Improved multi user access (start/mute/stop)
- *		- Channel doesn't get lost anymore after start/mute/stop
- *		- RDS support added (polling mode via interrupt EP 1)
- *		- marked default module parameters with *value*
- *		- switched from bit structs to bit masks
- *		- header file cleaned and integrated
- * 2008-01-14	Tobias Lorenz <tobias.lorenz@gmx.net>
- * 		Version 1.0.2
- * 		- hex values are now lower case
- * 		- commented USB ID for ADS/Tech moved on todo list
- * 		- blacklisted si470x in hid-quirks.c
- * 		- rds buffer handling functions integrated into *_work, *_read
- * 		- rds_command in si470x_poll exchanged against simple retval
- * 		- check for firmware version 15
- * 		- code order and prototypes still remain the same
- * 		- spacing and bottom of band codes remain the same
- * 2008-01-16	Tobias Lorenz <tobias.lorenz@gmx.net>
- *		Version 1.0.3
- * 		- code reordered to avoid function prototypes
- *		- switch/case defaults are now more user-friendly
- *		- unified comment style
- *		- applied all checkpatch.pl v1.12 suggestions
- *		  except the warning about the too long lines with bit comments
- *		- renamed FMRADIO to RADIO to cut line length (checkpatch.pl)
- * 2008-01-22	Tobias Lorenz <tobias.lorenz@gmx.net>
- *		Version 1.0.4
- *		- avoid poss. locking when doing copy_to_user which may sleep
- *		- RDS is automatically activated on read now
- *		- code cleaned of unnecessary rds_commands
- *		- USB Vendor/Product ID for ADS/Tech FM Radio Receiver verified
- *		  (thanks to Guillaume RAMOUSSE)
- * 2008-01-27	Tobias Lorenz <tobias.lorenz@gmx.net>
- *		Version 1.0.5
- *		- number of seek_retries changed to tune_timeout
- *		- fixed problem with incomplete tune operations by own buffers
- *		- optimization of variables and printf types
- *		- improved error logging
- * 2008-01-31	Tobias Lorenz <tobias.lorenz@gmx.net>
- *		Oliver Neukum <oliver@neukum.org>
- *		Version 1.0.6
- *		- fixed coverity checker warnings in *_usb_driver_disconnect
- *		- probe()/open() race by correct ordering in probe()
- *		- DMA coherency rules by separate allocation of all buffers
- *		- use of endianness macros
- *		- abuse of spinlock, replaced by mutex
- *		- racy handling of timer in disconnect,
- *		  replaced by delayed_work
- *		- racy interruptible_sleep_on(),
- *		  replaced with wait_event_interruptible()
- *		- handle signals in read()
- * 2008-02-08	Tobias Lorenz <tobias.lorenz@gmx.net>
- *		Oliver Neukum <oliver@neukum.org>
- *		Version 1.0.7
- *		- usb autosuspend support
- *		- unplugging fixed
- * 2008-05-07	Tobias Lorenz <tobias.lorenz@gmx.net>
- *		Version 1.0.8
- *		- hardware frequency seek support
- *		- afc indication
- *		- more safety checks, let si470x_get_freq return errno
- *		- vidioc behavior corrected according to v4l2 spec
- * 2008-10-20	Alexey Klimov <klimov.linux@gmail.com>
- * 		- add support for KWorld USB FM Radio FM700
- * 		- blacklisted KWorld radio in hid-core.c and hid-ids.h
- * 2008-12-03	Mark Lord <mlord@pobox.com>
- *		- add support for DealExtreme USB Radio
- * 2009-01-31	Bob Ross <pigiron@gmx.com>
- *		- correction of stereo detection/setting
- *		- correction of signal strength indicator scaling
- * 2009-01-31	Rick Bronson <rick@efn.org>
- *		Tobias Lorenz <tobias.lorenz@gmx.net>
- *		- add LED status output
- *		- get HW/SW version from scratchpad
- *
- * ToDo:
- * - add firmware download/update support
- * - RDS support: interrupt mode, instead of polling
- */
-
-
-/* driver definitions */
-#define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>"
-#define DRIVER_NAME "radio-si470x"
-#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 9)
-#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
-#define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers"
-#define DRIVER_VERSION "1.0.9"
-
-
-/* kernel includes */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/smp_lock.h>
-#include <linux/input.h>
-#include <linux/usb.h>
-#include <linux/hid.h>
-#include <linux/version.h>
-#include <linux/videodev2.h>
-#include <linux/mutex.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/rds.h>
-#include <asm/unaligned.h>
-
-
-/* USB Device ID List */
-static struct usb_device_id si470x_usb_driver_id_table[] = {
-	/* Silicon Labs USB FM Radio Reference Design */
-	{ USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) },
-	/* ADS/Tech FM Radio Receiver (formerly Instant FM Music) */
-	{ USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) },
-	/* KWorld USB FM Radio SnapMusic Mobile 700 (FM700) */
-	{ USB_DEVICE_AND_INTERFACE_INFO(0x1b80, 0xd700, USB_CLASS_HID, 0, 0) },
-	/* Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) */
-	{ USB_DEVICE_AND_INTERFACE_INFO(0x10c5, 0x819a, USB_CLASS_HID, 0, 0) },
-	/* Terminating entry */
-	{ }
-};
-MODULE_DEVICE_TABLE(usb, si470x_usb_driver_id_table);
-
-
-
-/**************************************************************************
- * Module Parameters
- **************************************************************************/
-
-/* Radio Nr */
-static int radio_nr = -1;
-module_param(radio_nr, int, 0444);
-MODULE_PARM_DESC(radio_nr, "Radio Nr");
-
-/* Spacing (kHz) */
-/* 0: 200 kHz (USA, Australia) */
-/* 1: 100 kHz (Europe, Japan) */
-/* 2:  50 kHz */
-static unsigned short space = 2;
-module_param(space, ushort, 0444);
-MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*");
-
-/* Bottom of Band (MHz) */
-/* 0: 87.5 - 108 MHz (USA, Europe)*/
-/* 1: 76   - 108 MHz (Japan wide band) */
-/* 2: 76   -  90 MHz (Japan) */
-static unsigned short band = 1;
-module_param(band, ushort, 0444);
-MODULE_PARM_DESC(band, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz");
-
-/* De-emphasis */
-/* 0: 75 us (USA) */
-/* 1: 50 us (Europe, Australia, Japan) */
-static unsigned short de = 1;
-module_param(de, ushort, 0444);
-MODULE_PARM_DESC(de, "De-emphasis: 0=75us *1=50us*");
-
-/* USB timeout */
-static unsigned int usb_timeout = 500;
-module_param(usb_timeout, uint, 0644);
-MODULE_PARM_DESC(usb_timeout, "USB timeout (ms): *500*");
-
-/* Tune timeout */
-static unsigned int tune_timeout = 3000;
-module_param(tune_timeout, uint, 0644);
-MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*");
-
-/* Seek timeout */
-static unsigned int seek_timeout = 5000;
-module_param(seek_timeout, uint, 0644);
-MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*");
-
-/* RDS buffer blocks */
-static unsigned int rds_buf = 100;
-module_param(rds_buf, uint, 0444);
-MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");
-
-/* RDS maximum block errors */
-static unsigned short max_rds_errors = 1;
-/* 0 means   0  errors requiring correction */
-/* 1 means 1-2  errors requiring correction (used by original USBRadio.exe) */
-/* 2 means 3-5  errors requiring correction */
-/* 3 means   6+ errors or errors in checkword, correction not possible */
-module_param(max_rds_errors, ushort, 0644);
-MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*");
-
-/* RDS poll frequency */
-static unsigned int rds_poll_time = 40;
-/* 40 is used by the original USBRadio.exe */
-/* 50 is used by radio-cadet */
-/* 75 should be okay */
-/* 80 is the usual RDS receive interval */
-module_param(rds_poll_time, uint, 0644);
-MODULE_PARM_DESC(rds_poll_time, "RDS poll time (ms): *40*");
-
-
-
-/**************************************************************************
- * Register Definitions
- **************************************************************************/
-#define RADIO_REGISTER_SIZE	2	/* 16 register bit width */
-#define RADIO_REGISTER_NUM	16	/* DEVICEID   ... RDSD */
-#define RDS_REGISTER_NUM	6	/* STATUSRSSI ... RDSD */
-
-#define DEVICEID		0	/* Device ID */
-#define DEVICEID_PN		0xf000	/* bits 15..12: Part Number */
-#define DEVICEID_MFGID		0x0fff	/* bits 11..00: Manufacturer ID */
-
-#define CHIPID			1	/* Chip ID */
-#define CHIPID_REV		0xfc00	/* bits 15..10: Chip Version */
-#define CHIPID_DEV		0x0200	/* bits 09..09: Device */
-#define CHIPID_FIRMWARE		0x01ff	/* bits 08..00: Firmware Version */
-
-#define POWERCFG		2	/* Power Configuration */
-#define POWERCFG_DSMUTE		0x8000	/* bits 15..15: Softmute Disable */
-#define POWERCFG_DMUTE		0x4000	/* bits 14..14: Mute Disable */
-#define POWERCFG_MONO		0x2000	/* bits 13..13: Mono Select */
-#define POWERCFG_RDSM		0x0800	/* bits 11..11: RDS Mode (Si4701 only) */
-#define POWERCFG_SKMODE		0x0400	/* bits 10..10: Seek Mode */
-#define POWERCFG_SEEKUP		0x0200	/* bits 09..09: Seek Direction */
-#define POWERCFG_SEEK		0x0100	/* bits 08..08: Seek */
-#define POWERCFG_DISABLE	0x0040	/* bits 06..06: Powerup Disable */
-#define POWERCFG_ENABLE		0x0001	/* bits 00..00: Powerup Enable */
-
-#define CHANNEL			3	/* Channel */
-#define CHANNEL_TUNE		0x8000	/* bits 15..15: Tune */
-#define CHANNEL_CHAN		0x03ff	/* bits 09..00: Channel Select */
-
-#define SYSCONFIG1		4	/* System Configuration 1 */
-#define SYSCONFIG1_RDSIEN	0x8000	/* bits 15..15: RDS Interrupt Enable (Si4701 only) */
-#define SYSCONFIG1_STCIEN	0x4000	/* bits 14..14: Seek/Tune Complete Interrupt Enable */
-#define SYSCONFIG1_RDS		0x1000	/* bits 12..12: RDS Enable (Si4701 only) */
-#define SYSCONFIG1_DE		0x0800	/* bits 11..11: De-emphasis (0=75us 1=50us) */
-#define SYSCONFIG1_AGCD		0x0400	/* bits 10..10: AGC Disable */
-#define SYSCONFIG1_BLNDADJ	0x00c0	/* bits 07..06: Stereo/Mono Blend Level Adjustment */
-#define SYSCONFIG1_GPIO3	0x0030	/* bits 05..04: General Purpose I/O 3 */
-#define SYSCONFIG1_GPIO2	0x000c	/* bits 03..02: General Purpose I/O 2 */
-#define SYSCONFIG1_GPIO1	0x0003	/* bits 01..00: General Purpose I/O 1 */
-
-#define SYSCONFIG2		5	/* System Configuration 2 */
-#define SYSCONFIG2_SEEKTH	0xff00	/* bits 15..08: RSSI Seek Threshold */
-#define SYSCONFIG2_BAND		0x0080	/* bits 07..06: Band Select */
-#define SYSCONFIG2_SPACE	0x0030	/* bits 05..04: Channel Spacing */
-#define SYSCONFIG2_VOLUME	0x000f	/* bits 03..00: Volume */
-
-#define SYSCONFIG3		6	/* System Configuration 3 */
-#define SYSCONFIG3_SMUTER	0xc000	/* bits 15..14: Softmute Attack/Recover Rate */
-#define SYSCONFIG3_SMUTEA	0x3000	/* bits 13..12: Softmute Attenuation */
-#define SYSCONFIG3_SKSNR	0x00f0	/* bits 07..04: Seek SNR Threshold */
-#define SYSCONFIG3_SKCNT	0x000f	/* bits 03..00: Seek FM Impulse Detection Threshold */
-
-#define TEST1			7	/* Test 1 */
-#define TEST1_AHIZEN		0x4000	/* bits 14..14: Audio High-Z Enable */
-
-#define TEST2			8	/* Test 2 */
-/* TEST2 only contains reserved bits */
-
-#define BOOTCONFIG		9	/* Boot Configuration */
-/* BOOTCONFIG only contains reserved bits */
-
-#define STATUSRSSI		10	/* Status RSSI */
-#define STATUSRSSI_RDSR		0x8000	/* bits 15..15: RDS Ready (Si4701 only) */
-#define STATUSRSSI_STC		0x4000	/* bits 14..14: Seek/Tune Complete */
-#define STATUSRSSI_SF		0x2000	/* bits 13..13: Seek Fail/Band Limit */
-#define STATUSRSSI_AFCRL	0x1000	/* bits 12..12: AFC Rail */
-#define STATUSRSSI_RDSS		0x0800	/* bits 11..11: RDS Synchronized (Si4701 only) */
-#define STATUSRSSI_BLERA	0x0600	/* bits 10..09: RDS Block A Errors (Si4701 only) */
-#define STATUSRSSI_ST		0x0100	/* bits 08..08: Stereo Indicator */
-#define STATUSRSSI_RSSI		0x00ff	/* bits 07..00: RSSI (Received Signal Strength Indicator) */
-
-#define READCHAN		11	/* Read Channel */
-#define READCHAN_BLERB		0xc000	/* bits 15..14: RDS Block D Errors (Si4701 only) */
-#define READCHAN_BLERC		0x3000	/* bits 13..12: RDS Block C Errors (Si4701 only) */
-#define READCHAN_BLERD		0x0c00	/* bits 11..10: RDS Block B Errors (Si4701 only) */
-#define READCHAN_READCHAN	0x03ff	/* bits 09..00: Read Channel */
-
-#define RDSA			12	/* RDSA */
-#define RDSA_RDSA		0xffff	/* bits 15..00: RDS Block A Data (Si4701 only) */
-
-#define RDSB			13	/* RDSB */
-#define RDSB_RDSB		0xffff	/* bits 15..00: RDS Block B Data (Si4701 only) */
-
-#define RDSC			14	/* RDSC */
-#define RDSC_RDSC		0xffff	/* bits 15..00: RDS Block C Data (Si4701 only) */
-
-#define RDSD			15	/* RDSD */
-#define RDSD_RDSD		0xffff	/* bits 15..00: RDS Block D Data (Si4701 only) */
-
-
-
-/**************************************************************************
- * USB HID Reports
- **************************************************************************/
-
-/* Reports 1-16 give direct read/write access to the 16 Si470x registers */
-/* with the (REPORT_ID - 1) corresponding to the register address across USB */
-/* endpoint 0 using GET_REPORT and SET_REPORT */
-#define REGISTER_REPORT_SIZE	(RADIO_REGISTER_SIZE + 1)
-#define REGISTER_REPORT(reg)	((reg) + 1)
-
-/* Report 17 gives direct read/write access to the entire Si470x register */
-/* map across endpoint 0 using GET_REPORT and SET_REPORT */
-#define ENTIRE_REPORT_SIZE	(RADIO_REGISTER_NUM * RADIO_REGISTER_SIZE + 1)
-#define ENTIRE_REPORT		17
-
-/* Report 18 is used to send the lowest 6 Si470x registers up the HID */
-/* interrupt endpoint 1 to Windows every 20 milliseconds for status */
-#define RDS_REPORT_SIZE		(RDS_REGISTER_NUM * RADIO_REGISTER_SIZE + 1)
-#define RDS_REPORT		18
-
-/* Report 19: LED state */
-#define LED_REPORT_SIZE		3
-#define LED_REPORT		19
-
-/* Report 19: stream */
-#define STREAM_REPORT_SIZE	3
-#define STREAM_REPORT		19
-
-/* Report 20: scratch */
-#define SCRATCH_PAGE_SIZE	63
-#define SCRATCH_REPORT_SIZE	(SCRATCH_PAGE_SIZE + 1)
-#define SCRATCH_REPORT		20
-
-/* Reports 19-22: flash upgrade of the C8051F321 */
-#define WRITE_REPORT_SIZE	4
-#define WRITE_REPORT		19
-#define FLASH_REPORT_SIZE	64
-#define FLASH_REPORT		20
-#define CRC_REPORT_SIZE		3
-#define CRC_REPORT		21
-#define RESPONSE_REPORT_SIZE	2
-#define RESPONSE_REPORT		22
-
-/* Report 23: currently unused, but can accept 60 byte reports on the HID */
-/* interrupt out endpoint 2 every 1 millisecond */
-#define UNUSED_REPORT		23
-
-
-
-/**************************************************************************
- * Software/Hardware Versions
- **************************************************************************/
-#define RADIO_SW_VERSION_NOT_BOOTLOADABLE	6
-#define RADIO_SW_VERSION			7
-#define RADIO_SW_VERSION_CURRENT		15
-#define RADIO_HW_VERSION			1
-
-#define SCRATCH_PAGE_SW_VERSION	1
-#define SCRATCH_PAGE_HW_VERSION	2
-
-
-
-/**************************************************************************
- * LED State Definitions
- **************************************************************************/
-#define LED_COMMAND		0x35
-
-#define NO_CHANGE_LED		0x00
-#define ALL_COLOR_LED		0x01	/* streaming state */
-#define BLINK_GREEN_LED		0x02	/* connect state */
-#define BLINK_RED_LED		0x04
-#define BLINK_ORANGE_LED	0x10	/* disconnect state */
-#define SOLID_GREEN_LED		0x20	/* tuning/seeking state */
-#define SOLID_RED_LED		0x40	/* bootload state */
-#define SOLID_ORANGE_LED	0x80
-
-
-
-/**************************************************************************
- * Stream State Definitions
- **************************************************************************/
-#define STREAM_COMMAND	0x36
-#define STREAM_VIDPID	0x00
-#define STREAM_AUDIO	0xff
-
-
-
-/**************************************************************************
- * Bootloader / Flash Commands
- **************************************************************************/
-
-/* unique id sent to bootloader and required to put into a bootload state */
-#define UNIQUE_BL_ID		0x34
-
-/* mask for the flash data */
-#define FLASH_DATA_MASK		0x55
-
-/* bootloader commands */
-#define GET_SW_VERSION_COMMAND	0x00
-#define SET_PAGE_COMMAND	0x01
-#define ERASE_PAGE_COMMAND	0x02
-#define WRITE_PAGE_COMMAND	0x03
-#define CRC_ON_PAGE_COMMAND	0x04
-#define READ_FLASH_BYTE_COMMAND	0x05
-#define RESET_DEVICE_COMMAND	0x06
-#define GET_HW_VERSION_COMMAND	0x07
-#define BLANK			0xff
-
-/* bootloader command responses */
-#define COMMAND_OK		0x01
-#define COMMAND_FAILED		0x02
-#define COMMAND_PENDING		0x03
-
-
-
-/**************************************************************************
- * General Driver Definitions
- **************************************************************************/
-
-/*
- * si470x_device - private data
- */
-struct si470x_device {
-	/* reference to USB and video device */
-	struct usb_device *usbdev;
-	struct usb_interface *intf;
-	struct video_device *videodev;
-
-	/* driver management */
-	unsigned int users;
-	unsigned char disconnected;
-	struct mutex disconnect_lock;
-
-	/* Silabs internal registers (0..15) */
-	unsigned short registers[RADIO_REGISTER_NUM];
-
-	/* RDS receive buffer */
-	struct delayed_work work;
-	wait_queue_head_t read_queue;
-	struct mutex lock;		/* buffer locking */
-	unsigned char *buffer;		/* size is always multiple of three */
-	unsigned int buf_size;
-	unsigned int rd_index;
-	unsigned int wr_index;
-
-	/* scratch page */
-	unsigned char software_version;
-	unsigned char hardware_version;
-};
-
-
-/*
- * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW,
- * 62.5 kHz otherwise.
- * The tuner is able to have a channel spacing of 50, 100 or 200 kHz.
- * tuner->capability is therefore set to V4L2_TUNER_CAP_LOW
- * The FREQ_MUL is then: 1 MHz / 62.5 Hz = 16000
- */
-#define FREQ_MUL (1000000 / 62.5)
-
-
-
-/**************************************************************************
- * General Driver Functions - REGISTER_REPORTs
- **************************************************************************/
-
-/*
- * si470x_get_report - receive a HID report
- */
-static int si470x_get_report(struct si470x_device *radio, void *buf, int size)
-{
-	unsigned char *report = (unsigned char *) buf;
-	int retval;
-
-	retval = usb_control_msg(radio->usbdev,
-		usb_rcvctrlpipe(radio->usbdev, 0),
-		HID_REQ_GET_REPORT,
-		USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
-		report[0], 2,
-		buf, size, usb_timeout);
-
-	if (retval < 0)
-		printk(KERN_WARNING DRIVER_NAME
-			": si470x_get_report: usb_control_msg returned %d\n",
-			retval);
-	return retval;
-}
-
-
-/*
- * si470x_set_report - send a HID report
- */
-static int si470x_set_report(struct si470x_device *radio, void *buf, int size)
-{
-	unsigned char *report = (unsigned char *) buf;
-	int retval;
-
-	retval = usb_control_msg(radio->usbdev,
-		usb_sndctrlpipe(radio->usbdev, 0),
-		HID_REQ_SET_REPORT,
-		USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
-		report[0], 2,
-		buf, size, usb_timeout);
-
-	if (retval < 0)
-		printk(KERN_WARNING DRIVER_NAME
-			": si470x_set_report: usb_control_msg returned %d\n",
-			retval);
-	return retval;
-}
-
-
-/*
- * si470x_get_register - read register
- */
-static int si470x_get_register(struct si470x_device *radio, int regnr)
-{
-	unsigned char buf[REGISTER_REPORT_SIZE];
-	int retval;
-
-	buf[0] = REGISTER_REPORT(regnr);
-
-	retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
-
-	if (retval >= 0)
-		radio->registers[regnr] = get_unaligned_be16(&buf[1]);
-
-	return (retval < 0) ? -EINVAL : 0;
-}
-
-
-/*
- * si470x_set_register - write register
- */
-static int si470x_set_register(struct si470x_device *radio, int regnr)
-{
-	unsigned char buf[REGISTER_REPORT_SIZE];
-	int retval;
-
-	buf[0] = REGISTER_REPORT(regnr);
-	put_unaligned_be16(radio->registers[regnr], &buf[1]);
-
-	retval = si470x_set_report(radio, (void *) &buf, sizeof(buf));
-
-	return (retval < 0) ? -EINVAL : 0;
-}
-
-
-/*
- * si470x_set_chan - set the channel
- */
-static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
-{
-	int retval;
-	unsigned long timeout;
-	bool timed_out = 0;
-
-	/* start tuning */
-	radio->registers[CHANNEL] &= ~CHANNEL_CHAN;
-	radio->registers[CHANNEL] |= CHANNEL_TUNE | chan;
-	retval = si470x_set_register(radio, CHANNEL);
-	if (retval < 0)
-		goto done;
-
-	/* wait till tune operation has completed */
-	timeout = jiffies + msecs_to_jiffies(tune_timeout);
-	do {
-		retval = si470x_get_register(radio, STATUSRSSI);
-		if (retval < 0)
-			goto stop;
-		timed_out = time_after(jiffies, timeout);
-	} while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) &&
-		(!timed_out));
-	if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
-		printk(KERN_WARNING DRIVER_NAME ": tune does not complete\n");
-	if (timed_out)
-		printk(KERN_WARNING DRIVER_NAME
-			": tune timed out after %u ms\n", tune_timeout);
-
-stop:
-	/* stop tuning */
-	radio->registers[CHANNEL] &= ~CHANNEL_TUNE;
-	retval = si470x_set_register(radio, CHANNEL);
-
-done:
-	return retval;
-}
-
-
-/*
- * si470x_get_freq - get the frequency
- */
-static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
-{
-	unsigned int spacing, band_bottom;
-	unsigned short chan;
-	int retval;
-
-	/* Spacing (kHz) */
-	switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
-	/* 0: 200 kHz (USA, Australia) */
-	case 0:
-		spacing = 0.200 * FREQ_MUL; break;
-	/* 1: 100 kHz (Europe, Japan) */
-	case 1:
-		spacing = 0.100 * FREQ_MUL; break;
-	/* 2:  50 kHz */
-	default:
-		spacing = 0.050 * FREQ_MUL; break;
-	};
-
-	/* Bottom of Band (MHz) */
-	switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
-	/* 0: 87.5 - 108 MHz (USA, Europe) */
-	case 0:
-		band_bottom = 87.5 * FREQ_MUL; break;
-	/* 1: 76   - 108 MHz (Japan wide band) */
-	default:
-		band_bottom = 76   * FREQ_MUL; break;
-	/* 2: 76   -  90 MHz (Japan) */
-	case 2:
-		band_bottom = 76   * FREQ_MUL; break;
-	};
-
-	/* read channel */
-	retval = si470x_get_register(radio, READCHAN);
-	chan = radio->registers[READCHAN] & READCHAN_READCHAN;
-
-	/* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */
-	*freq = chan * spacing + band_bottom;
-
-	return retval;
-}
-
-
-/*
- * si470x_set_freq - set the frequency
- */
-static int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
-{
-	unsigned int spacing, band_bottom;
-	unsigned short chan;
-
-	/* Spacing (kHz) */
-	switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
-	/* 0: 200 kHz (USA, Australia) */
-	case 0:
-		spacing = 0.200 * FREQ_MUL; break;
-	/* 1: 100 kHz (Europe, Japan) */
-	case 1:
-		spacing = 0.100 * FREQ_MUL; break;
-	/* 2:  50 kHz */
-	default:
-		spacing = 0.050 * FREQ_MUL; break;
-	};
-
-	/* Bottom of Band (MHz) */
-	switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
-	/* 0: 87.5 - 108 MHz (USA, Europe) */
-	case 0:
-		band_bottom = 87.5 * FREQ_MUL; break;
-	/* 1: 76   - 108 MHz (Japan wide band) */
-	default:
-		band_bottom = 76   * FREQ_MUL; break;
-	/* 2: 76   -  90 MHz (Japan) */
-	case 2:
-		band_bottom = 76   * FREQ_MUL; break;
-	};
-
-	/* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */
-	chan = (freq - band_bottom) / spacing;
-
-	return si470x_set_chan(radio, chan);
-}
-
-
-/*
- * si470x_set_seek - set seek
- */
-static int si470x_set_seek(struct si470x_device *radio,
-		unsigned int wrap_around, unsigned int seek_upward)
-{
-	int retval = 0;
-	unsigned long timeout;
-	bool timed_out = 0;
-
-	/* start seeking */
-	radio->registers[POWERCFG] |= POWERCFG_SEEK;
-	if (wrap_around == 1)
-		radio->registers[POWERCFG] &= ~POWERCFG_SKMODE;
-	else
-		radio->registers[POWERCFG] |= POWERCFG_SKMODE;
-	if (seek_upward == 1)
-		radio->registers[POWERCFG] |= POWERCFG_SEEKUP;
-	else
-		radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP;
-	retval = si470x_set_register(radio, POWERCFG);
-	if (retval < 0)
-		goto done;
-
-	/* wait till seek operation has completed */
-	timeout = jiffies + msecs_to_jiffies(seek_timeout);
-	do {
-		retval = si470x_get_register(radio, STATUSRSSI);
-		if (retval < 0)
-			goto stop;
-		timed_out = time_after(jiffies, timeout);
-	} while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) &&
-		(!timed_out));
-	if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
-		printk(KERN_WARNING DRIVER_NAME ": seek does not complete\n");
-	if (radio->registers[STATUSRSSI] & STATUSRSSI_SF)
-		printk(KERN_WARNING DRIVER_NAME
-			": seek failed / band limit reached\n");
-	if (timed_out)
-		printk(KERN_WARNING DRIVER_NAME
-			": seek timed out after %u ms\n", seek_timeout);
-
-stop:
-	/* stop seeking */
-	radio->registers[POWERCFG] &= ~POWERCFG_SEEK;
-	retval = si470x_set_register(radio, POWERCFG);
-
-done:
-	/* try again, if timed out */
-	if ((retval == 0) && timed_out)
-		retval = -EAGAIN;
-
-	return retval;
-}
-
-
-/*
- * si470x_start - switch on radio
- */
-static int si470x_start(struct si470x_device *radio)
-{
-	int retval;
-
-	/* powercfg */
-	radio->registers[POWERCFG] =
-		POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM;
-	retval = si470x_set_register(radio, POWERCFG);
-	if (retval < 0)
-		goto done;
-
-	/* sysconfig 1 */
-	radio->registers[SYSCONFIG1] = SYSCONFIG1_DE;
-	retval = si470x_set_register(radio, SYSCONFIG1);
-	if (retval < 0)
-		goto done;
-
-	/* sysconfig 2 */
-	radio->registers[SYSCONFIG2] =
-		(0x3f  << 8) |				/* SEEKTH */
-		((band  << 6) & SYSCONFIG2_BAND)  |	/* BAND */
-		((space << 4) & SYSCONFIG2_SPACE) |	/* SPACE */
-		15;					/* VOLUME (max) */
-	retval = si470x_set_register(radio, SYSCONFIG2);
-	if (retval < 0)
-		goto done;
-
-	/* reset last channel */
-	retval = si470x_set_chan(radio,
-		radio->registers[CHANNEL] & CHANNEL_CHAN);
-
-done:
-	return retval;
-}
-
-
-/*
- * si470x_stop - switch off radio
- */
-static int si470x_stop(struct si470x_device *radio)
-{
-	int retval;
-
-	/* sysconfig 1 */
-	radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
-	retval = si470x_set_register(radio, SYSCONFIG1);
-	if (retval < 0)
-		goto done;
-
-	/* powercfg */
-	radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
-	/* POWERCFG_ENABLE has to automatically go low */
-	radio->registers[POWERCFG] |= POWERCFG_ENABLE |	POWERCFG_DISABLE;
-	retval = si470x_set_register(radio, POWERCFG);
-
-done:
-	return retval;
-}
-
-
-/*
- * si470x_rds_on - switch on rds reception
- */
-static int si470x_rds_on(struct si470x_device *radio)
-{
-	int retval;
-
-	/* sysconfig 1 */
-	mutex_lock(&radio->lock);
-	radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS;
-	retval = si470x_set_register(radio, SYSCONFIG1);
-	if (retval < 0)
-		radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
-	mutex_unlock(&radio->lock);
-
-	return retval;
-}
-
-
-
-/**************************************************************************
- * General Driver Functions - ENTIRE_REPORT
- **************************************************************************/
-
-/*
- * si470x_get_all_registers - read entire registers
- */
-static int si470x_get_all_registers(struct si470x_device *radio)
-{
-	unsigned char buf[ENTIRE_REPORT_SIZE];
-	int retval;
-	unsigned char regnr;
-
-	buf[0] = ENTIRE_REPORT;
-
-	retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
-
-	if (retval >= 0)
-		for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++)
-			radio->registers[regnr] = get_unaligned_be16(
-				&buf[regnr * RADIO_REGISTER_SIZE + 1]);
-
-	return (retval < 0) ? -EINVAL : 0;
-}
-
-
-
-/**************************************************************************
- * General Driver Functions - RDS_REPORT
- **************************************************************************/
-
-/*
- * si470x_get_rds_registers - read rds registers
- */
-static int si470x_get_rds_registers(struct si470x_device *radio)
-{
-	unsigned char buf[RDS_REPORT_SIZE];
-	int retval;
-	int size;
-	unsigned char regnr;
-
-	buf[0] = RDS_REPORT;
-
-	retval = usb_interrupt_msg(radio->usbdev,
-		usb_rcvintpipe(radio->usbdev, 1),
-		(void *) &buf, sizeof(buf), &size, usb_timeout);
-	if (size != sizeof(buf))
-		printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: "
-			"return size differs: %d != %zu\n", size, sizeof(buf));
-	if (retval < 0)
-		printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: "
-			"usb_interrupt_msg returned %d\n", retval);
-
-	if (retval >= 0)
-		for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++)
-			radio->registers[STATUSRSSI + regnr] =
-				get_unaligned_be16(
-				&buf[regnr * RADIO_REGISTER_SIZE + 1]);
-
-	return (retval < 0) ? -EINVAL : 0;
-}
-
-
-
-/**************************************************************************
- * General Driver Functions - LED_REPORT
- **************************************************************************/
-
-/*
- * si470x_set_led_state - sets the led state
- */
-static int si470x_set_led_state(struct si470x_device *radio,
-		unsigned char led_state)
-{
-	unsigned char buf[LED_REPORT_SIZE];
-	int retval;
-
-	buf[0] = LED_REPORT;
-	buf[1] = LED_COMMAND;
-	buf[2] = led_state;
-
-	retval = si470x_set_report(radio, (void *) &buf, sizeof(buf));
-
-	return (retval < 0) ? -EINVAL : 0;
-}
-
-
-
-/**************************************************************************
- * General Driver Functions - SCRATCH_REPORT
- **************************************************************************/
-
-/*
- * si470x_get_scratch_versions - gets the scratch page and version infos
- */
-static int si470x_get_scratch_page_versions(struct si470x_device *radio)
-{
-	unsigned char buf[SCRATCH_REPORT_SIZE];
-	int retval;
-
-	buf[0] = SCRATCH_REPORT;
-
-	retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
-
-	if (retval < 0)
-		printk(KERN_WARNING DRIVER_NAME ": si470x_get_scratch: "
-			"si470x_get_report returned %d\n", retval);
-	else {
-		radio->software_version = buf[1];
-		radio->hardware_version = buf[2];
-	}
-
-	return (retval < 0) ? -EINVAL : 0;
-}
-
-
-
-/**************************************************************************
- * RDS Driver Functions
- **************************************************************************/
-
-/*
- * si470x_rds - rds processing function
- */
-static void si470x_rds(struct si470x_device *radio)
-{
-	unsigned char blocknum;
-	unsigned short bler; /* rds block errors */
-	unsigned short rds;
-	unsigned char tmpbuf[3];
-
-	/* get rds blocks */
-	if (si470x_get_rds_registers(radio) < 0)
-		return;
-	if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) {
-		/* No RDS group ready */
-		return;
-	}
-	if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSS) == 0) {
-		/* RDS decoder not synchronized */
-		return;
-	}
-
-	/* copy all four RDS blocks to internal buffer */
-	mutex_lock(&radio->lock);
-	for (blocknum = 0; blocknum < 4; blocknum++) {
-		switch (blocknum) {
-		default:
-			bler = (radio->registers[STATUSRSSI] &
-					STATUSRSSI_BLERA) >> 9;
-			rds = radio->registers[RDSA];
-			break;
-		case 1:
-			bler = (radio->registers[READCHAN] &
-					READCHAN_BLERB) >> 14;
-			rds = radio->registers[RDSB];
-			break;
-		case 2:
-			bler = (radio->registers[READCHAN] &
-					READCHAN_BLERC) >> 12;
-			rds = radio->registers[RDSC];
-			break;
-		case 3:
-			bler = (radio->registers[READCHAN] &
-					READCHAN_BLERD) >> 10;
-			rds = radio->registers[RDSD];
-			break;
-		};
-
-		/* Fill the V4L2 RDS buffer */
-		put_unaligned_le16(rds, &tmpbuf);
-		tmpbuf[2] = blocknum;		/* offset name */
-		tmpbuf[2] |= blocknum << 3;	/* received offset */
-		if (bler > max_rds_errors)
-			tmpbuf[2] |= 0x80; /* uncorrectable errors */
-		else if (bler > 0)
-			tmpbuf[2] |= 0x40; /* corrected error(s) */
-
-		/* copy RDS block to internal buffer */
-		memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3);
-		radio->wr_index += 3;
-
-		/* wrap write pointer */
-		if (radio->wr_index >= radio->buf_size)
-			radio->wr_index = 0;
-
-		/* check for overflow */
-		if (radio->wr_index == radio->rd_index) {
-			/* increment and wrap read pointer */
-			radio->rd_index += 3;
-			if (radio->rd_index >= radio->buf_size)
-				radio->rd_index = 0;
-		}
-	}
-	mutex_unlock(&radio->lock);
-
-	/* wake up read queue */
-	if (radio->wr_index != radio->rd_index)
-		wake_up_interruptible(&radio->read_queue);
-}
-
-
-/*
- * si470x_work - rds work function
- */
-static void si470x_work(struct work_struct *work)
-{
-	struct si470x_device *radio = container_of(work, struct si470x_device,
-		work.work);
-
-	/* safety checks */
-	if (radio->disconnected)
-		return;
-	if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
-		return;
-
-	si470x_rds(radio);
-	schedule_delayed_work(&radio->work, msecs_to_jiffies(rds_poll_time));
-}
-
-
-
-/**************************************************************************
- * File Operations Interface
- **************************************************************************/
-
-/*
- * si470x_fops_read - read RDS data
- */
-static ssize_t si470x_fops_read(struct file *file, char __user *buf,
-		size_t count, loff_t *ppos)
-{
-	struct si470x_device *radio = video_drvdata(file);
-	int retval = 0;
-	unsigned int block_count = 0;
-
-	/* switch on rds reception */
-	if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) {
-		si470x_rds_on(radio);
-		schedule_delayed_work(&radio->work,
-			msecs_to_jiffies(rds_poll_time));
-	}
-
-	/* block if no new data available */
-	while (radio->wr_index == radio->rd_index) {
-		if (file->f_flags & O_NONBLOCK) {
-			retval = -EWOULDBLOCK;
-			goto done;
-		}
-		if (wait_event_interruptible(radio->read_queue,
-			radio->wr_index != radio->rd_index) < 0) {
-			retval = -EINTR;
-			goto done;
-		}
-	}
-
-	/* calculate block count from byte count */
-	count /= 3;
-
-	/* copy RDS block out of internal buffer and to user buffer */
-	mutex_lock(&radio->lock);
-	while (block_count < count) {
-		if (radio->rd_index == radio->wr_index)
-			break;
-
-		/* always transfer rds complete blocks */
-		if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3))
-			/* retval = -EFAULT; */
-			break;
-
-		/* increment and wrap read pointer */
-		radio->rd_index += 3;
-		if (radio->rd_index >= radio->buf_size)
-			radio->rd_index = 0;
-
-		/* increment counters */
-		block_count++;
-		buf += 3;
-		retval += 3;
-	}
-	mutex_unlock(&radio->lock);
-
-done:
-	return retval;
-}
-
-
-/*
- * si470x_fops_poll - poll RDS data
- */
-static unsigned int si470x_fops_poll(struct file *file,
-		struct poll_table_struct *pts)
-{
-	struct si470x_device *radio = video_drvdata(file);
-	int retval = 0;
-
-	/* switch on rds reception */
-	if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) {
-		si470x_rds_on(radio);
-		schedule_delayed_work(&radio->work,
-			msecs_to_jiffies(rds_poll_time));
-	}
-
-	poll_wait(file, &radio->read_queue, pts);
-
-	if (radio->rd_index != radio->wr_index)
-		retval = POLLIN | POLLRDNORM;
-
-	return retval;
-}
-
-
-/*
- * si470x_fops_open - file open
- */
-static int si470x_fops_open(struct file *file)
-{
-	struct si470x_device *radio = video_drvdata(file);
-	int retval;
-
-	lock_kernel();
-	radio->users++;
-
-	retval = usb_autopm_get_interface(radio->intf);
-	if (retval < 0) {
-		radio->users--;
-		retval = -EIO;
-		goto done;
-	}
-
-	if (radio->users == 1) {
-		/* start radio */
-		retval = si470x_start(radio);
-		if (retval < 0)
-			usb_autopm_put_interface(radio->intf);
-	}
-
-done:
-	unlock_kernel();
-	return retval;
-}
-
-
-/*
- * si470x_fops_release - file release
- */
-static int si470x_fops_release(struct file *file)
-{
-	struct si470x_device *radio = video_drvdata(file);
-	int retval = 0;
-
-	/* safety check */
-	if (!radio) {
-		retval = -ENODEV;
-		goto done;
-	}
-
-	mutex_lock(&radio->disconnect_lock);
-	radio->users--;
-	if (radio->users == 0) {
-		if (radio->disconnected) {
-			video_unregister_device(radio->videodev);
-			kfree(radio->buffer);
-			kfree(radio);
-			goto unlock;
-		}
-
-		/* stop rds reception */
-		cancel_delayed_work_sync(&radio->work);
-
-		/* cancel read processes */
-		wake_up_interruptible(&radio->read_queue);
-
-		/* stop radio */
-		retval = si470x_stop(radio);
-		usb_autopm_put_interface(radio->intf);
-	}
-unlock:
-	mutex_unlock(&radio->disconnect_lock);
-done:
-	return retval;
-}
-
-
-/*
- * si470x_fops - file operations interface
- */
-static const struct v4l2_file_operations si470x_fops = {
-	.owner		= THIS_MODULE,
-	.read		= si470x_fops_read,
-	.poll		= si470x_fops_poll,
-	.ioctl		= video_ioctl2,
-	.open		= si470x_fops_open,
-	.release	= si470x_fops_release,
-};
-
-
-
-/**************************************************************************
- * Video4Linux Interface
- **************************************************************************/
-
-/*
- * si470x_v4l2_queryctrl - query control
- */
-static struct v4l2_queryctrl si470x_v4l2_queryctrl[] = {
-	{
-		.id		= V4L2_CID_AUDIO_VOLUME,
-		.type		= V4L2_CTRL_TYPE_INTEGER,
-		.name		= "Volume",
-		.minimum	= 0,
-		.maximum	= 15,
-		.step		= 1,
-		.default_value	= 15,
-	},
-	{
-		.id		= V4L2_CID_AUDIO_MUTE,
-		.type		= V4L2_CTRL_TYPE_BOOLEAN,
-		.name		= "Mute",
-		.minimum	= 0,
-		.maximum	= 1,
-		.step		= 1,
-		.default_value	= 1,
-	},
-};
-
-
-/*
- * si470x_vidioc_querycap - query device capabilities
- */
-static int si470x_vidioc_querycap(struct file *file, void *priv,
-		struct v4l2_capability *capability)
-{
-	struct si470x_device *radio = video_drvdata(file);
-
-	strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
-	strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
-	usb_make_path(radio->usbdev, capability->bus_info, sizeof(capability->bus_info));
-	capability->version = DRIVER_KERNEL_VERSION;
-	capability->capabilities = V4L2_CAP_HW_FREQ_SEEK |
-		V4L2_CAP_TUNER | V4L2_CAP_RADIO;
-
-	return 0;
-}
-
-
-/*
- * si470x_vidioc_queryctrl - enumerate control items
- */
-static int si470x_vidioc_queryctrl(struct file *file, void *priv,
-		struct v4l2_queryctrl *qc)
-{
-	unsigned char i = 0;
-	int retval = -EINVAL;
-
-	/* abort if qc->id is below V4L2_CID_BASE */
-	if (qc->id < V4L2_CID_BASE)
-		goto done;
-
-	/* search video control */
-	for (i = 0; i < ARRAY_SIZE(si470x_v4l2_queryctrl); i++) {
-		if (qc->id == si470x_v4l2_queryctrl[i].id) {
-			memcpy(qc, &(si470x_v4l2_queryctrl[i]), sizeof(*qc));
-			retval = 0; /* found */
-			break;
-		}
-	}
-
-	/* disable unsupported base controls */
-	/* to satisfy kradio and such apps */
-	if ((retval == -EINVAL) && (qc->id < V4L2_CID_LASTP1)) {
-		qc->flags = V4L2_CTRL_FLAG_DISABLED;
-		retval = 0;
-	}
-
-done:
-	if (retval < 0)
-		printk(KERN_WARNING DRIVER_NAME
-			": query controls failed with %d\n", retval);
-	return retval;
-}
-
-
-/*
- * si470x_vidioc_g_ctrl - get the value of a control
- */
-static int si470x_vidioc_g_ctrl(struct file *file, void *priv,
-		struct v4l2_control *ctrl)
-{
-	struct si470x_device *radio = video_drvdata(file);
-	int retval = 0;
-
-	/* safety checks */
-	if (radio->disconnected) {
-		retval = -EIO;
-		goto done;
-	}
-
-	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_VOLUME:
-		ctrl->value = radio->registers[SYSCONFIG2] &
-				SYSCONFIG2_VOLUME;
-		break;
-	case V4L2_CID_AUDIO_MUTE:
-		ctrl->value = ((radio->registers[POWERCFG] &
-				POWERCFG_DMUTE) == 0) ? 1 : 0;
-		break;
-	default:
-		retval = -EINVAL;
-	}
-
-done:
-	if (retval < 0)
-		printk(KERN_WARNING DRIVER_NAME
-			": get control failed with %d\n", retval);
-	return retval;
-}
-
-
-/*
- * si470x_vidioc_s_ctrl - set the value of a control
- */
-static int si470x_vidioc_s_ctrl(struct file *file, void *priv,
-		struct v4l2_control *ctrl)
-{
-	struct si470x_device *radio = video_drvdata(file);
-	int retval = 0;
-
-	/* safety checks */
-	if (radio->disconnected) {
-		retval = -EIO;
-		goto done;
-	}
-
-	switch (ctrl->id) {
-	case V4L2_CID_AUDIO_VOLUME:
-		radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME;
-		radio->registers[SYSCONFIG2] |= ctrl->value;
-		retval = si470x_set_register(radio, SYSCONFIG2);
-		break;
-	case V4L2_CID_AUDIO_MUTE:
-		if (ctrl->value == 1)
-			radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
-		else
-			radio->registers[POWERCFG] |= POWERCFG_DMUTE;
-		retval = si470x_set_register(radio, POWERCFG);
-		break;
-	default:
-		retval = -EINVAL;
-	}
-
-done:
-	if (retval < 0)
-		printk(KERN_WARNING DRIVER_NAME
-			": set control failed with %d\n", retval);
-	return retval;
-}
-
-
-/*
- * si470x_vidioc_g_audio - get audio attributes
- */
-static int si470x_vidioc_g_audio(struct file *file, void *priv,
-		struct v4l2_audio *audio)
-{
-	/* driver constants */
-	audio->index = 0;
-	strcpy(audio->name, "Radio");
-	audio->capability = V4L2_AUDCAP_STEREO;
-	audio->mode = 0;
-
-	return 0;
-}
-
-
-/*
- * si470x_vidioc_g_tuner - get tuner attributes
- */
-static int si470x_vidioc_g_tuner(struct file *file, void *priv,
-		struct v4l2_tuner *tuner)
-{
-	struct si470x_device *radio = video_drvdata(file);
-	int retval = 0;
-
-	/* safety checks */
-	if (radio->disconnected) {
-		retval = -EIO;
-		goto done;
-	}
-	if (tuner->index != 0) {
-		retval = -EINVAL;
-		goto done;
-	}
-
-	retval = si470x_get_register(radio, STATUSRSSI);
-	if (retval < 0)
-		goto done;
-
-	/* driver constants */
-	strcpy(tuner->name, "FM");
-	tuner->type = V4L2_TUNER_RADIO;
-	tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
-
-	/* range limits */
-	switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
-	/* 0: 87.5 - 108 MHz (USA, Europe, default) */
-	default:
-		tuner->rangelow  =  87.5 * FREQ_MUL;
-		tuner->rangehigh = 108   * FREQ_MUL;
-		break;
-	/* 1: 76   - 108 MHz (Japan wide band) */
-	case 1 :
-		tuner->rangelow  =  76   * FREQ_MUL;
-		tuner->rangehigh = 108   * FREQ_MUL;
-		break;
-	/* 2: 76   -  90 MHz (Japan) */
-	case 2 :
-		tuner->rangelow  =  76   * FREQ_MUL;
-		tuner->rangehigh =  90   * FREQ_MUL;
-		break;
-	};
-
-	/* stereo indicator == stereo (instead of mono) */
-	if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0)
-		tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
-	else
-		tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
-
-	/* mono/stereo selector */
-	if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0)
-		tuner->audmode = V4L2_TUNER_MODE_STEREO;
-	else
-		tuner->audmode = V4L2_TUNER_MODE_MONO;
-
-	/* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */
-	/* measured in units of dbµV in 1 db increments (max at ~75 dbµV) */
-	tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI);
-	/* the ideal factor is 0xffff/75 = 873,8 */
-	tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10);
-
-	/* automatic frequency control: -1: freq to low, 1 freq to high */
-	/* AFCRL does only indicate that freq. differs, not if too low/high */
-	tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0;
-
-done:
-	if (retval < 0)
-		printk(KERN_WARNING DRIVER_NAME
-			": get tuner failed with %d\n", retval);
-	return retval;
-}
-
-
-/*
- * si470x_vidioc_s_tuner - set tuner attributes
- */
-static int si470x_vidioc_s_tuner(struct file *file, void *priv,
-		struct v4l2_tuner *tuner)
-{
-	struct si470x_device *radio = video_drvdata(file);
-	int retval = -EINVAL;
-
-	/* safety checks */
-	if (radio->disconnected) {
-		retval = -EIO;
-		goto done;
-	}
-	if (tuner->index != 0)
-		goto done;
-
-	/* mono/stereo selector */
-	switch (tuner->audmode) {
-	case V4L2_TUNER_MODE_MONO:
-		radio->registers[POWERCFG] |= POWERCFG_MONO;  /* force mono */
-		break;
-	case V4L2_TUNER_MODE_STEREO:
-		radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */
-		break;
-	default:
-		goto done;
-	}
-
-	retval = si470x_set_register(radio, POWERCFG);
-
-done:
-	if (retval < 0)
-		printk(KERN_WARNING DRIVER_NAME
-			": set tuner failed with %d\n", retval);
-	return retval;
-}
-
-
-/*
- * si470x_vidioc_g_frequency - get tuner or modulator radio frequency
- */
-static int si470x_vidioc_g_frequency(struct file *file, void *priv,
-		struct v4l2_frequency *freq)
-{
-	struct si470x_device *radio = video_drvdata(file);
-	int retval = 0;
-
-	/* safety checks */
-	if (radio->disconnected) {
-		retval = -EIO;
-		goto done;
-	}
-	if (freq->tuner != 0) {
-		retval = -EINVAL;
-		goto done;
-	}
-
-	freq->type = V4L2_TUNER_RADIO;
-	retval = si470x_get_freq(radio, &freq->frequency);
-
-done:
-	if (retval < 0)
-		printk(KERN_WARNING DRIVER_NAME
-			": get frequency failed with %d\n", retval);
-	return retval;
-}
-
-
-/*
- * si470x_vidioc_s_frequency - set tuner or modulator radio frequency
- */
-static int si470x_vidioc_s_frequency(struct file *file, void *priv,
-		struct v4l2_frequency *freq)
-{
-	struct si470x_device *radio = video_drvdata(file);
-	int retval = 0;
-
-	/* safety checks */
-	if (radio->disconnected) {
-		retval = -EIO;
-		goto done;
-	}
-	if (freq->tuner != 0) {
-		retval = -EINVAL;
-		goto done;
-	}
-
-	retval = si470x_set_freq(radio, freq->frequency);
-
-done:
-	if (retval < 0)
-		printk(KERN_WARNING DRIVER_NAME
-			": set frequency failed with %d\n", retval);
-	return retval;
-}
-
-
-/*
- * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek
- */
-static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
-		struct v4l2_hw_freq_seek *seek)
-{
-	struct si470x_device *radio = video_drvdata(file);
-	int retval = 0;
-
-	/* safety checks */
-	if (radio->disconnected) {
-		retval = -EIO;
-		goto done;
-	}
-	if (seek->tuner != 0) {
-		retval = -EINVAL;
-		goto done;
-	}
-
-	retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward);
-
-done:
-	if (retval < 0)
-		printk(KERN_WARNING DRIVER_NAME
-			": set hardware frequency seek failed with %d\n",
-			retval);
-	return retval;
-}
-
-
-/*
- * si470x_ioctl_ops - video device ioctl operations
- */
-static const struct v4l2_ioctl_ops si470x_ioctl_ops = {
-	.vidioc_querycap	= si470x_vidioc_querycap,
-	.vidioc_queryctrl	= si470x_vidioc_queryctrl,
-	.vidioc_g_ctrl		= si470x_vidioc_g_ctrl,
-	.vidioc_s_ctrl		= si470x_vidioc_s_ctrl,
-	.vidioc_g_audio		= si470x_vidioc_g_audio,
-	.vidioc_g_tuner		= si470x_vidioc_g_tuner,
-	.vidioc_s_tuner		= si470x_vidioc_s_tuner,
-	.vidioc_g_frequency	= si470x_vidioc_g_frequency,
-	.vidioc_s_frequency	= si470x_vidioc_s_frequency,
-	.vidioc_s_hw_freq_seek	= si470x_vidioc_s_hw_freq_seek,
-};
-
-
-/*
- * si470x_viddev_template - video device interface
- */
-static struct video_device si470x_viddev_template = {
-	.fops			= &si470x_fops,
-	.name			= DRIVER_NAME,
-	.release		= video_device_release,
-	.ioctl_ops		= &si470x_ioctl_ops,
-};
-
-
-
-/**************************************************************************
- * USB Interface
- **************************************************************************/
-
-/*
- * si470x_usb_driver_probe - probe for the device
- */
-static int si470x_usb_driver_probe(struct usb_interface *intf,
-		const struct usb_device_id *id)
-{
-	struct si470x_device *radio;
-	int retval = 0;
-
-	/* private data allocation and initialization */
-	radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL);
-	if (!radio) {
-		retval = -ENOMEM;
-		goto err_initial;
-	}
-	radio->users = 0;
-	radio->disconnected = 0;
-	radio->usbdev = interface_to_usbdev(intf);
-	radio->intf = intf;
-	mutex_init(&radio->disconnect_lock);
-	mutex_init(&radio->lock);
-
-	/* video device allocation and initialization */
-	radio->videodev = video_device_alloc();
-	if (!radio->videodev) {
-		retval = -ENOMEM;
-		goto err_radio;
-	}
-	memcpy(radio->videodev, &si470x_viddev_template,
-			sizeof(si470x_viddev_template));
-	video_set_drvdata(radio->videodev, radio);
-
-	/* show some infos about the specific si470x device */
-	if (si470x_get_all_registers(radio) < 0) {
-		retval = -EIO;
-		goto err_video;
-	}
-	printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4hx ChipID=0x%4.4hx\n",
-			radio->registers[DEVICEID], radio->registers[CHIPID]);
-
-	/* get software and hardware versions */
-	if (si470x_get_scratch_page_versions(radio) < 0) {
-		retval = -EIO;
-		goto err_video;
-	}
-	printk(KERN_INFO DRIVER_NAME
-			": software version %d, hardware version %d\n",
-			radio->software_version, radio->hardware_version);
-
-	/* check if device and firmware is current */
-	if ((radio->registers[CHIPID] & CHIPID_FIRMWARE)
-			< RADIO_SW_VERSION_CURRENT) {
-		printk(KERN_WARNING DRIVER_NAME
-			": This driver is known to work with "
-			"firmware version %hu,\n", RADIO_SW_VERSION_CURRENT);
-		printk(KERN_WARNING DRIVER_NAME
-			": but the device has firmware version %hu.\n",
-			radio->registers[CHIPID] & CHIPID_FIRMWARE);
-		printk(KERN_WARNING DRIVER_NAME
-			": If you have some trouble using this driver,\n");
-		printk(KERN_WARNING DRIVER_NAME
-			": please report to V4L ML at "
-			"linux-media@vger.kernel.org\n");
-	}
-
-	/* set initial frequency */
-	si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
-
-	/* set led to connect state */
-	si470x_set_led_state(radio, BLINK_GREEN_LED);
-
-	/* rds buffer allocation */
-	radio->buf_size = rds_buf * 3;
-	radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
-	if (!radio->buffer) {
-		retval = -EIO;
-		goto err_video;
-	}
-
-	/* rds buffer configuration */
-	radio->wr_index = 0;
-	radio->rd_index = 0;
-	init_waitqueue_head(&radio->read_queue);
-
-	/* prepare rds work function */
-	INIT_DELAYED_WORK(&radio->work, si470x_work);
-
-	/* register video device */
-	retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr);
-	if (retval) {
-		printk(KERN_WARNING DRIVER_NAME
-				": Could not register video device\n");
-		goto err_all;
-	}
-	usb_set_intfdata(intf, radio);
-
-	return 0;
-err_all:
-	kfree(radio->buffer);
-err_video:
-	video_device_release(radio->videodev);
-err_radio:
-	kfree(radio);
-err_initial:
-	return retval;
-}
-
-
-/*
- * si470x_usb_driver_suspend - suspend the device
- */
-static int si470x_usb_driver_suspend(struct usb_interface *intf,
-		pm_message_t message)
-{
-	struct si470x_device *radio = usb_get_intfdata(intf);
-
-	printk(KERN_INFO DRIVER_NAME ": suspending now...\n");
-
-	cancel_delayed_work_sync(&radio->work);
-
-	return 0;
-}
-
-
-/*
- * si470x_usb_driver_resume - resume the device
- */
-static int si470x_usb_driver_resume(struct usb_interface *intf)
-{
-	struct si470x_device *radio = usb_get_intfdata(intf);
-
-	printk(KERN_INFO DRIVER_NAME ": resuming now...\n");
-
-	mutex_lock(&radio->lock);
-	if (radio->users && radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS)
-		schedule_delayed_work(&radio->work,
-			msecs_to_jiffies(rds_poll_time));
-	mutex_unlock(&radio->lock);
-
-	return 0;
-}
-
-
-/*
- * si470x_usb_driver_disconnect - disconnect the device
- */
-static void si470x_usb_driver_disconnect(struct usb_interface *intf)
-{
-	struct si470x_device *radio = usb_get_intfdata(intf);
-
-	mutex_lock(&radio->disconnect_lock);
-	radio->disconnected = 1;
-	cancel_delayed_work_sync(&radio->work);
-	usb_set_intfdata(intf, NULL);
-	if (radio->users == 0) {
-		/* set led to disconnect state */
-		si470x_set_led_state(radio, BLINK_ORANGE_LED);
-
-		video_unregister_device(radio->videodev);
-		kfree(radio->buffer);
-		kfree(radio);
-	}
-	mutex_unlock(&radio->disconnect_lock);
-}
-
-
-/*
- * si470x_usb_driver - usb driver interface
- */
-static struct usb_driver si470x_usb_driver = {
-	.name			= DRIVER_NAME,
-	.probe			= si470x_usb_driver_probe,
-	.disconnect		= si470x_usb_driver_disconnect,
-	.suspend		= si470x_usb_driver_suspend,
-	.resume			= si470x_usb_driver_resume,
-	.id_table		= si470x_usb_driver_id_table,
-	.supports_autosuspend	= 1,
-};
-
-
-
-/**************************************************************************
- * Module Interface
- **************************************************************************/
-
-/*
- * si470x_module_init - module init
- */
-static int __init si470x_module_init(void)
-{
-	printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n");
-	return usb_register(&si470x_usb_driver);
-}
-
-
-/*
- * si470x_module_exit - module exit
- */
-static void __exit si470x_module_exit(void)
-{
-	usb_deregister(&si470x_usb_driver);
-}
-
-
-module_init(si470x_module_init);
-module_exit(si470x_module_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_VERSION(DRIVER_VERSION);

+ 367 - 0
drivers/media/radio/radio-si4713.c

@@ -0,0 +1,367 @@
+/*
+ * drivers/media/radio/radio-si4713.c
+ *
+ * Platform Driver for Silicon Labs Si4713 FM Radio Transmitter:
+ *
+ * Copyright (c) 2008 Instituto Nokia de Tecnologia - INdT
+ * Contact: Eduardo Valentin <eduardo.valentin@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/radio-si4713.h>
+
+/* module parameters */
+static int radio_nr = -1;	/* radio device minor (-1 ==> auto assign) */
+module_param(radio_nr, int, 0);
+MODULE_PARM_DESC(radio_nr,
+		 "Minor number for radio device (-1 ==> auto assign)");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Eduardo Valentin <eduardo.valentin@nokia.com>");
+MODULE_DESCRIPTION("Platform driver for Si4713 FM Radio Transmitter");
+MODULE_VERSION("0.0.1");
+
+/* Driver state struct */
+struct radio_si4713_device {
+	struct v4l2_device		v4l2_dev;
+	struct video_device		*radio_dev;
+};
+
+/* radio_si4713_fops - file operations interface */
+static const struct v4l2_file_operations radio_si4713_fops = {
+	.owner		= THIS_MODULE,
+	.ioctl		= video_ioctl2,
+};
+
+/* Video4Linux Interface */
+static int radio_si4713_fill_audout(struct v4l2_audioout *vao)
+{
+	/* TODO: check presence of audio output */
+	strlcpy(vao->name, "FM Modulator Audio Out", 32);
+
+	return 0;
+}
+
+static int radio_si4713_enumaudout(struct file *file, void *priv,
+						struct v4l2_audioout *vao)
+{
+	return radio_si4713_fill_audout(vao);
+}
+
+static int radio_si4713_g_audout(struct file *file, void *priv,
+					struct v4l2_audioout *vao)
+{
+	int rval = radio_si4713_fill_audout(vao);
+
+	vao->index = 0;
+
+	return rval;
+}
+
+static int radio_si4713_s_audout(struct file *file, void *priv,
+					struct v4l2_audioout *vao)
+{
+	return vao->index ? -EINVAL : 0;
+}
+
+/* radio_si4713_querycap - query device capabilities */
+static int radio_si4713_querycap(struct file *file, void *priv,
+					struct v4l2_capability *capability)
+{
+	struct radio_si4713_device *rsdev;
+
+	rsdev = video_get_drvdata(video_devdata(file));
+
+	strlcpy(capability->driver, "radio-si4713", sizeof(capability->driver));
+	strlcpy(capability->card, "Silicon Labs Si4713 Modulator",
+				sizeof(capability->card));
+	capability->capabilities = V4L2_CAP_MODULATOR | V4L2_CAP_RDS_OUTPUT;
+
+	return 0;
+}
+
+/* radio_si4713_queryctrl - enumerate control items */
+static int radio_si4713_queryctrl(struct file *file, void *priv,
+						struct v4l2_queryctrl *qc)
+{
+	/* Must be sorted from low to high control ID! */
+	static const u32 user_ctrls[] = {
+		V4L2_CID_USER_CLASS,
+		V4L2_CID_AUDIO_MUTE,
+		0
+	};
+
+	/* Must be sorted from low to high control ID! */
+	static const u32 fmtx_ctrls[] = {
+		V4L2_CID_FM_TX_CLASS,
+		V4L2_CID_RDS_TX_DEVIATION,
+		V4L2_CID_RDS_TX_PI,
+		V4L2_CID_RDS_TX_PTY,
+		V4L2_CID_RDS_TX_PS_NAME,
+		V4L2_CID_RDS_TX_RADIO_TEXT,
+		V4L2_CID_AUDIO_LIMITER_ENABLED,
+		V4L2_CID_AUDIO_LIMITER_RELEASE_TIME,
+		V4L2_CID_AUDIO_LIMITER_DEVIATION,
+		V4L2_CID_AUDIO_COMPRESSION_ENABLED,
+		V4L2_CID_AUDIO_COMPRESSION_GAIN,
+		V4L2_CID_AUDIO_COMPRESSION_THRESHOLD,
+		V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME,
+		V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME,
+		V4L2_CID_PILOT_TONE_ENABLED,
+		V4L2_CID_PILOT_TONE_DEVIATION,
+		V4L2_CID_PILOT_TONE_FREQUENCY,
+		V4L2_CID_TUNE_PREEMPHASIS,
+		V4L2_CID_TUNE_POWER_LEVEL,
+		V4L2_CID_TUNE_ANTENNA_CAPACITOR,
+		0
+	};
+	static const u32 *ctrl_classes[] = {
+		user_ctrls,
+		fmtx_ctrls,
+		NULL
+	};
+	struct radio_si4713_device *rsdev;
+
+	rsdev = video_get_drvdata(video_devdata(file));
+
+	qc->id = v4l2_ctrl_next(ctrl_classes, qc->id);
+	if (qc->id == 0)
+		return -EINVAL;
+
+	if (qc->id == V4L2_CID_USER_CLASS || qc->id == V4L2_CID_FM_TX_CLASS)
+		return v4l2_ctrl_query_fill(qc, 0, 0, 0, 0);
+
+	return v4l2_device_call_until_err(&rsdev->v4l2_dev, 0, core,
+						queryctrl, qc);
+}
+
+/*
+ * v4l2 ioctl call backs.
+ * we are just a wrapper for v4l2_sub_devs.
+ */
+static inline struct v4l2_device *get_v4l2_dev(struct file *file)
+{
+	return &((struct radio_si4713_device *)video_drvdata(file))->v4l2_dev;
+}
+
+static int radio_si4713_g_ext_ctrls(struct file *file, void *p,
+						struct v4l2_ext_controls *vecs)
+{
+	return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core,
+							g_ext_ctrls, vecs);
+}
+
+static int radio_si4713_s_ext_ctrls(struct file *file, void *p,
+						struct v4l2_ext_controls *vecs)
+{
+	return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core,
+							s_ext_ctrls, vecs);
+}
+
+static int radio_si4713_g_ctrl(struct file *file, void *p,
+						struct v4l2_control *vc)
+{
+	return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core,
+							g_ctrl, vc);
+}
+
+static int radio_si4713_s_ctrl(struct file *file, void *p,
+						struct v4l2_control *vc)
+{
+	return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core,
+							s_ctrl, vc);
+}
+
+static int radio_si4713_g_modulator(struct file *file, void *p,
+						struct v4l2_modulator *vm)
+{
+	return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner,
+							g_modulator, vm);
+}
+
+static int radio_si4713_s_modulator(struct file *file, void *p,
+						struct v4l2_modulator *vm)
+{
+	return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner,
+							s_modulator, vm);
+}
+
+static int radio_si4713_g_frequency(struct file *file, void *p,
+						struct v4l2_frequency *vf)
+{
+	return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner,
+							g_frequency, vf);
+}
+
+static int radio_si4713_s_frequency(struct file *file, void *p,
+						struct v4l2_frequency *vf)
+{
+	return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner,
+							s_frequency, vf);
+}
+
+static long radio_si4713_default(struct file *file, void *p, int cmd, void *arg)
+{
+	return v4l2_device_call_until_err(get_v4l2_dev(file), 0, core,
+							ioctl, cmd, arg);
+}
+
+static struct v4l2_ioctl_ops radio_si4713_ioctl_ops = {
+	.vidioc_enumaudout	= radio_si4713_enumaudout,
+	.vidioc_g_audout	= radio_si4713_g_audout,
+	.vidioc_s_audout	= radio_si4713_s_audout,
+	.vidioc_querycap	= radio_si4713_querycap,
+	.vidioc_queryctrl	= radio_si4713_queryctrl,
+	.vidioc_g_ext_ctrls	= radio_si4713_g_ext_ctrls,
+	.vidioc_s_ext_ctrls	= radio_si4713_s_ext_ctrls,
+	.vidioc_g_ctrl		= radio_si4713_g_ctrl,
+	.vidioc_s_ctrl		= radio_si4713_s_ctrl,
+	.vidioc_g_modulator	= radio_si4713_g_modulator,
+	.vidioc_s_modulator	= radio_si4713_s_modulator,
+	.vidioc_g_frequency	= radio_si4713_g_frequency,
+	.vidioc_s_frequency	= radio_si4713_s_frequency,
+	.vidioc_default		= radio_si4713_default,
+};
+
+/* radio_si4713_vdev_template - video device interface */
+static struct video_device radio_si4713_vdev_template = {
+	.fops			= &radio_si4713_fops,
+	.name			= "radio-si4713",
+	.release		= video_device_release,
+	.ioctl_ops		= &radio_si4713_ioctl_ops,
+};
+
+/* Platform driver interface */
+/* radio_si4713_pdriver_probe - probe for the device */
+static int radio_si4713_pdriver_probe(struct platform_device *pdev)
+{
+	struct radio_si4713_platform_data *pdata = pdev->dev.platform_data;
+	struct radio_si4713_device *rsdev;
+	struct i2c_adapter *adapter;
+	struct v4l2_subdev *sd;
+	int rval = 0;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "Cannot proceed without platform data.\n");
+		rval = -EINVAL;
+		goto exit;
+	}
+
+	rsdev = kzalloc(sizeof *rsdev, GFP_KERNEL);
+	if (!rsdev) {
+		dev_err(&pdev->dev, "Failed to alloc video device.\n");
+		rval = -ENOMEM;
+		goto exit;
+	}
+
+	rval = v4l2_device_register(&pdev->dev, &rsdev->v4l2_dev);
+	if (rval) {
+		dev_err(&pdev->dev, "Failed to register v4l2 device.\n");
+		goto free_rsdev;
+	}
+
+	adapter = i2c_get_adapter(pdata->i2c_bus);
+	if (!adapter) {
+		dev_err(&pdev->dev, "Cannot get i2c adapter %d\n",
+							pdata->i2c_bus);
+		rval = -ENODEV;
+		goto unregister_v4l2_dev;
+	}
+
+	sd = v4l2_i2c_new_subdev_board(&rsdev->v4l2_dev, adapter, "si4713_i2c",
+					pdata->subdev_board_info, NULL);
+	if (!sd) {
+		dev_err(&pdev->dev, "Cannot get v4l2 subdevice\n");
+		rval = -ENODEV;
+		goto unregister_v4l2_dev;
+	}
+
+	rsdev->radio_dev = video_device_alloc();
+	if (!rsdev->radio_dev) {
+		dev_err(&pdev->dev, "Failed to alloc video device.\n");
+		rval = -ENOMEM;
+		goto unregister_v4l2_dev;
+	}
+
+	memcpy(rsdev->radio_dev, &radio_si4713_vdev_template,
+			sizeof(radio_si4713_vdev_template));
+	video_set_drvdata(rsdev->radio_dev, rsdev);
+	if (video_register_device(rsdev->radio_dev, VFL_TYPE_RADIO, radio_nr)) {
+		dev_err(&pdev->dev, "Could not register video device.\n");
+		rval = -EIO;
+		goto free_vdev;
+	}
+	dev_info(&pdev->dev, "New device successfully probed\n");
+
+	goto exit;
+
+free_vdev:
+	video_device_release(rsdev->radio_dev);
+unregister_v4l2_dev:
+	v4l2_device_unregister(&rsdev->v4l2_dev);
+free_rsdev:
+	kfree(rsdev);
+exit:
+	return rval;
+}
+
+/* radio_si4713_pdriver_remove - remove the device */
+static int __exit radio_si4713_pdriver_remove(struct platform_device *pdev)
+{
+	struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+	struct radio_si4713_device *rsdev = container_of(v4l2_dev,
+						struct radio_si4713_device,
+						v4l2_dev);
+
+	video_unregister_device(rsdev->radio_dev);
+	v4l2_device_unregister(&rsdev->v4l2_dev);
+	kfree(rsdev);
+
+	return 0;
+}
+
+static struct platform_driver radio_si4713_pdriver = {
+	.driver		= {
+		.name	= "radio-si4713",
+	},
+	.probe		= radio_si4713_pdriver_probe,
+	.remove         = __exit_p(radio_si4713_pdriver_remove),
+};
+
+/* Module Interface */
+static int __init radio_si4713_module_init(void)
+{
+	return platform_driver_register(&radio_si4713_pdriver);
+}
+
+static void __exit radio_si4713_module_exit(void)
+{
+	platform_driver_unregister(&radio_si4713_pdriver);
+}
+
+module_init(radio_si4713_module_init);
+module_exit(radio_si4713_module_exit);
+

+ 37 - 0
drivers/media/radio/si470x/Kconfig

@@ -0,0 +1,37 @@
+config USB_SI470X
+	tristate "Silicon Labs Si470x FM Radio Receiver support with USB"
+	depends on USB && RADIO_SI470X
+	---help---
+	  This is a driver for USB devices with the Silicon Labs SI470x
+	  chip. Currently these devices are known to work:
+	  - 10c4:818a: Silicon Labs USB FM Radio Reference Design
+	  - 06e1:a155: ADS/Tech FM Radio Receiver (formerly Instant FM Music)
+	  - 1b80:d700: KWorld USB FM Radio SnapMusic Mobile 700 (FM700)
+	  - 10c5:819a: Sanei Electric FM USB Radio (aka DealExtreme.com PCear)
+
+	  Sound is provided by the ALSA USB Audio/MIDI driver. Therefore
+	  if you don't want to use the device solely for RDS receiving,
+	  it is recommended to also select SND_USB_AUDIO.
+
+	  Please have a look at the documentation, especially on how
+	  to redirect the audio stream from the radio to your sound device:
+	  Documentation/video4linux/si470x.txt
+
+	  Say Y here if you want to connect this type of radio to your
+	  computer's USB port.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called radio-usb-si470x.
+
+config I2C_SI470X
+	tristate "Silicon Labs Si470x FM Radio Receiver support with I2C"
+	depends on I2C && RADIO_SI470X && !USB_SI470X
+	---help---
+	  This is a driver for I2C devices with the Silicon Labs SI470x
+	  chip.
+
+	  Say Y here if you want to connect this type of radio to your
+	  computer's I2C port.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called radio-i2c-si470x.

+ 9 - 0
drivers/media/radio/si470x/Makefile

@@ -0,0 +1,9 @@
+#
+# Makefile for radios with Silicon Labs Si470x FM Radio Receivers
+#
+
+radio-usb-si470x-objs	:= radio-si470x-usb.o radio-si470x-common.o
+radio-i2c-si470x-objs	:= radio-si470x-i2c.o radio-si470x-common.o
+
+obj-$(CONFIG_USB_SI470X) += radio-usb-si470x.o
+obj-$(CONFIG_I2C_SI470X) += radio-i2c-si470x.o

+ 798 - 0
drivers/media/radio/si470x/radio-si470x-common.c

@@ -0,0 +1,798 @@
+/*
+ *  drivers/media/radio/si470x/radio-si470x-common.c
+ *
+ *  Driver for radios with Silicon Labs Si470x FM Radio Receivers
+ *
+ *  Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net>
+ *
+ * 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
+ */
+
+
+/*
+ * History:
+ * 2008-01-12	Tobias Lorenz <tobias.lorenz@gmx.net>
+ *		Version 1.0.0
+ *		- First working version
+ * 2008-01-13	Tobias Lorenz <tobias.lorenz@gmx.net>
+ *		Version 1.0.1
+ *		- Improved error handling, every function now returns errno
+ *		- Improved multi user access (start/mute/stop)
+ *		- Channel doesn't get lost anymore after start/mute/stop
+ *		- RDS support added (polling mode via interrupt EP 1)
+ *		- marked default module parameters with *value*
+ *		- switched from bit structs to bit masks
+ *		- header file cleaned and integrated
+ * 2008-01-14	Tobias Lorenz <tobias.lorenz@gmx.net>
+ * 		Version 1.0.2
+ * 		- hex values are now lower case
+ * 		- commented USB ID for ADS/Tech moved on todo list
+ * 		- blacklisted si470x in hid-quirks.c
+ * 		- rds buffer handling functions integrated into *_work, *_read
+ * 		- rds_command in si470x_poll exchanged against simple retval
+ * 		- check for firmware version 15
+ * 		- code order and prototypes still remain the same
+ * 		- spacing and bottom of band codes remain the same
+ * 2008-01-16	Tobias Lorenz <tobias.lorenz@gmx.net>
+ *		Version 1.0.3
+ * 		- code reordered to avoid function prototypes
+ *		- switch/case defaults are now more user-friendly
+ *		- unified comment style
+ *		- applied all checkpatch.pl v1.12 suggestions
+ *		  except the warning about the too long lines with bit comments
+ *		- renamed FMRADIO to RADIO to cut line length (checkpatch.pl)
+ * 2008-01-22	Tobias Lorenz <tobias.lorenz@gmx.net>
+ *		Version 1.0.4
+ *		- avoid poss. locking when doing copy_to_user which may sleep
+ *		- RDS is automatically activated on read now
+ *		- code cleaned of unnecessary rds_commands
+ *		- USB Vendor/Product ID for ADS/Tech FM Radio Receiver verified
+ *		  (thanks to Guillaume RAMOUSSE)
+ * 2008-01-27	Tobias Lorenz <tobias.lorenz@gmx.net>
+ *		Version 1.0.5
+ *		- number of seek_retries changed to tune_timeout
+ *		- fixed problem with incomplete tune operations by own buffers
+ *		- optimization of variables and printf types
+ *		- improved error logging
+ * 2008-01-31	Tobias Lorenz <tobias.lorenz@gmx.net>
+ *		Oliver Neukum <oliver@neukum.org>
+ *		Version 1.0.6
+ *		- fixed coverity checker warnings in *_usb_driver_disconnect
+ *		- probe()/open() race by correct ordering in probe()
+ *		- DMA coherency rules by separate allocation of all buffers
+ *		- use of endianness macros
+ *		- abuse of spinlock, replaced by mutex
+ *		- racy handling of timer in disconnect,
+ *		  replaced by delayed_work
+ *		- racy interruptible_sleep_on(),
+ *		  replaced with wait_event_interruptible()
+ *		- handle signals in read()
+ * 2008-02-08	Tobias Lorenz <tobias.lorenz@gmx.net>
+ *		Oliver Neukum <oliver@neukum.org>
+ *		Version 1.0.7
+ *		- usb autosuspend support
+ *		- unplugging fixed
+ * 2008-05-07	Tobias Lorenz <tobias.lorenz@gmx.net>
+ *		Version 1.0.8
+ *		- hardware frequency seek support
+ *		- afc indication
+ *		- more safety checks, let si470x_get_freq return errno
+ *		- vidioc behavior corrected according to v4l2 spec
+ * 2008-10-20	Alexey Klimov <klimov.linux@gmail.com>
+ * 		- add support for KWorld USB FM Radio FM700
+ * 		- blacklisted KWorld radio in hid-core.c and hid-ids.h
+ * 2008-12-03	Mark Lord <mlord@pobox.com>
+ *		- add support for DealExtreme USB Radio
+ * 2009-01-31	Bob Ross <pigiron@gmx.com>
+ *		- correction of stereo detection/setting
+ *		- correction of signal strength indicator scaling
+ * 2009-01-31	Rick Bronson <rick@efn.org>
+ *		Tobias Lorenz <tobias.lorenz@gmx.net>
+ *		- add LED status output
+ *		- get HW/SW version from scratchpad
+ * 2009-06-16   Edouard Lafargue <edouard@lafargue.name>
+ *		Version 1.0.10
+ *		- add support for interrupt mode for RDS endpoint,
+ *                instead of polling.
+ *                Improves RDS reception significantly
+ */
+
+
+/* kernel includes */
+#include "radio-si470x.h"
+
+
+
+/**************************************************************************
+ * Module Parameters
+ **************************************************************************/
+
+/* Spacing (kHz) */
+/* 0: 200 kHz (USA, Australia) */
+/* 1: 100 kHz (Europe, Japan) */
+/* 2:  50 kHz */
+static unsigned short space = 2;
+module_param(space, ushort, 0444);
+MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*");
+
+/* Bottom of Band (MHz) */
+/* 0: 87.5 - 108 MHz (USA, Europe)*/
+/* 1: 76   - 108 MHz (Japan wide band) */
+/* 2: 76   -  90 MHz (Japan) */
+static unsigned short band = 1;
+module_param(band, ushort, 0444);
+MODULE_PARM_DESC(band, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz");
+
+/* De-emphasis */
+/* 0: 75 us (USA) */
+/* 1: 50 us (Europe, Australia, Japan) */
+static unsigned short de = 1;
+module_param(de, ushort, 0444);
+MODULE_PARM_DESC(de, "De-emphasis: 0=75us *1=50us*");
+
+/* Tune timeout */
+static unsigned int tune_timeout = 3000;
+module_param(tune_timeout, uint, 0644);
+MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*");
+
+/* Seek timeout */
+static unsigned int seek_timeout = 5000;
+module_param(seek_timeout, uint, 0644);
+MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*");
+
+
+
+/**************************************************************************
+ * Generic Functions
+ **************************************************************************/
+
+/*
+ * si470x_set_chan - set the channel
+ */
+static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
+{
+	int retval;
+	unsigned long timeout;
+	bool timed_out = 0;
+
+	/* start tuning */
+	radio->registers[CHANNEL] &= ~CHANNEL_CHAN;
+	radio->registers[CHANNEL] |= CHANNEL_TUNE | chan;
+	retval = si470x_set_register(radio, CHANNEL);
+	if (retval < 0)
+		goto done;
+
+	/* wait till tune operation has completed */
+	timeout = jiffies + msecs_to_jiffies(tune_timeout);
+	do {
+		retval = si470x_get_register(radio, STATUSRSSI);
+		if (retval < 0)
+			goto stop;
+		timed_out = time_after(jiffies, timeout);
+	} while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) &&
+		(!timed_out));
+	if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
+		dev_warn(&radio->videodev->dev, "tune does not complete\n");
+	if (timed_out)
+		dev_warn(&radio->videodev->dev,
+			"tune timed out after %u ms\n", tune_timeout);
+
+stop:
+	/* stop tuning */
+	radio->registers[CHANNEL] &= ~CHANNEL_TUNE;
+	retval = si470x_set_register(radio, CHANNEL);
+
+done:
+	return retval;
+}
+
+
+/*
+ * si470x_get_freq - get the frequency
+ */
+static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
+{
+	unsigned int spacing, band_bottom;
+	unsigned short chan;
+	int retval;
+
+	/* Spacing (kHz) */
+	switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
+	/* 0: 200 kHz (USA, Australia) */
+	case 0:
+		spacing = 0.200 * FREQ_MUL; break;
+	/* 1: 100 kHz (Europe, Japan) */
+	case 1:
+		spacing = 0.100 * FREQ_MUL; break;
+	/* 2:  50 kHz */
+	default:
+		spacing = 0.050 * FREQ_MUL; break;
+	};
+
+	/* Bottom of Band (MHz) */
+	switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
+	/* 0: 87.5 - 108 MHz (USA, Europe) */
+	case 0:
+		band_bottom = 87.5 * FREQ_MUL; break;
+	/* 1: 76   - 108 MHz (Japan wide band) */
+	default:
+		band_bottom = 76   * FREQ_MUL; break;
+	/* 2: 76   -  90 MHz (Japan) */
+	case 2:
+		band_bottom = 76   * FREQ_MUL; break;
+	};
+
+	/* read channel */
+	retval = si470x_get_register(radio, READCHAN);
+	chan = radio->registers[READCHAN] & READCHAN_READCHAN;
+
+	/* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */
+	*freq = chan * spacing + band_bottom;
+
+	return retval;
+}
+
+
+/*
+ * si470x_set_freq - set the frequency
+ */
+int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
+{
+	unsigned int spacing, band_bottom;
+	unsigned short chan;
+
+	/* Spacing (kHz) */
+	switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
+	/* 0: 200 kHz (USA, Australia) */
+	case 0:
+		spacing = 0.200 * FREQ_MUL; break;
+	/* 1: 100 kHz (Europe, Japan) */
+	case 1:
+		spacing = 0.100 * FREQ_MUL; break;
+	/* 2:  50 kHz */
+	default:
+		spacing = 0.050 * FREQ_MUL; break;
+	};
+
+	/* Bottom of Band (MHz) */
+	switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
+	/* 0: 87.5 - 108 MHz (USA, Europe) */
+	case 0:
+		band_bottom = 87.5 * FREQ_MUL; break;
+	/* 1: 76   - 108 MHz (Japan wide band) */
+	default:
+		band_bottom = 76   * FREQ_MUL; break;
+	/* 2: 76   -  90 MHz (Japan) */
+	case 2:
+		band_bottom = 76   * FREQ_MUL; break;
+	};
+
+	/* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */
+	chan = (freq - band_bottom) / spacing;
+
+	return si470x_set_chan(radio, chan);
+}
+
+
+/*
+ * si470x_set_seek - set seek
+ */
+static int si470x_set_seek(struct si470x_device *radio,
+		unsigned int wrap_around, unsigned int seek_upward)
+{
+	int retval = 0;
+	unsigned long timeout;
+	bool timed_out = 0;
+
+	/* start seeking */
+	radio->registers[POWERCFG] |= POWERCFG_SEEK;
+	if (wrap_around == 1)
+		radio->registers[POWERCFG] &= ~POWERCFG_SKMODE;
+	else
+		radio->registers[POWERCFG] |= POWERCFG_SKMODE;
+	if (seek_upward == 1)
+		radio->registers[POWERCFG] |= POWERCFG_SEEKUP;
+	else
+		radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP;
+	retval = si470x_set_register(radio, POWERCFG);
+	if (retval < 0)
+		goto done;
+
+	/* wait till seek operation has completed */
+	timeout = jiffies + msecs_to_jiffies(seek_timeout);
+	do {
+		retval = si470x_get_register(radio, STATUSRSSI);
+		if (retval < 0)
+			goto stop;
+		timed_out = time_after(jiffies, timeout);
+	} while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) &&
+		(!timed_out));
+	if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
+		dev_warn(&radio->videodev->dev, "seek does not complete\n");
+	if (radio->registers[STATUSRSSI] & STATUSRSSI_SF)
+		dev_warn(&radio->videodev->dev,
+			"seek failed / band limit reached\n");
+	if (timed_out)
+		dev_warn(&radio->videodev->dev,
+			"seek timed out after %u ms\n", seek_timeout);
+
+stop:
+	/* stop seeking */
+	radio->registers[POWERCFG] &= ~POWERCFG_SEEK;
+	retval = si470x_set_register(radio, POWERCFG);
+
+done:
+	/* try again, if timed out */
+	if ((retval == 0) && timed_out)
+		retval = -EAGAIN;
+
+	return retval;
+}
+
+
+/*
+ * si470x_start - switch on radio
+ */
+int si470x_start(struct si470x_device *radio)
+{
+	int retval;
+
+	/* powercfg */
+	radio->registers[POWERCFG] =
+		POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM;
+	retval = si470x_set_register(radio, POWERCFG);
+	if (retval < 0)
+		goto done;
+
+	/* sysconfig 1 */
+	radio->registers[SYSCONFIG1] = SYSCONFIG1_DE;
+	retval = si470x_set_register(radio, SYSCONFIG1);
+	if (retval < 0)
+		goto done;
+
+	/* sysconfig 2 */
+	radio->registers[SYSCONFIG2] =
+		(0x3f  << 8) |				/* SEEKTH */
+		((band  << 6) & SYSCONFIG2_BAND)  |	/* BAND */
+		((space << 4) & SYSCONFIG2_SPACE) |	/* SPACE */
+		15;					/* VOLUME (max) */
+	retval = si470x_set_register(radio, SYSCONFIG2);
+	if (retval < 0)
+		goto done;
+
+	/* reset last channel */
+	retval = si470x_set_chan(radio,
+		radio->registers[CHANNEL] & CHANNEL_CHAN);
+
+done:
+	return retval;
+}
+
+
+/*
+ * si470x_stop - switch off radio
+ */
+int si470x_stop(struct si470x_device *radio)
+{
+	int retval;
+
+	/* sysconfig 1 */
+	radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
+	retval = si470x_set_register(radio, SYSCONFIG1);
+	if (retval < 0)
+		goto done;
+
+	/* powercfg */
+	radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
+	/* POWERCFG_ENABLE has to automatically go low */
+	radio->registers[POWERCFG] |= POWERCFG_ENABLE |	POWERCFG_DISABLE;
+	retval = si470x_set_register(radio, POWERCFG);
+
+done:
+	return retval;
+}
+
+
+/*
+ * si470x_rds_on - switch on rds reception
+ */
+int si470x_rds_on(struct si470x_device *radio)
+{
+	int retval;
+
+	/* sysconfig 1 */
+	mutex_lock(&radio->lock);
+	radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS;
+	retval = si470x_set_register(radio, SYSCONFIG1);
+	if (retval < 0)
+		radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
+	mutex_unlock(&radio->lock);
+
+	return retval;
+}
+
+
+
+/**************************************************************************
+ * Video4Linux Interface
+ **************************************************************************/
+
+/*
+ * si470x_vidioc_queryctrl - enumerate control items
+ */
+static int si470x_vidioc_queryctrl(struct file *file, void *priv,
+		struct v4l2_queryctrl *qc)
+{
+	struct si470x_device *radio = video_drvdata(file);
+	int retval = -EINVAL;
+
+	/* abort if qc->id is below V4L2_CID_BASE */
+	if (qc->id < V4L2_CID_BASE)
+		goto done;
+
+	/* search video control */
+	switch (qc->id) {
+	case V4L2_CID_AUDIO_VOLUME:
+		return v4l2_ctrl_query_fill(qc, 0, 15, 1, 15);
+	case V4L2_CID_AUDIO_MUTE:
+		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
+	}
+
+	/* disable unsupported base controls */
+	/* to satisfy kradio and such apps */
+	if ((retval == -EINVAL) && (qc->id < V4L2_CID_LASTP1)) {
+		qc->flags = V4L2_CTRL_FLAG_DISABLED;
+		retval = 0;
+	}
+
+done:
+	if (retval < 0)
+		dev_warn(&radio->videodev->dev,
+			"query controls failed with %d\n", retval);
+	return retval;
+}
+
+
+/*
+ * si470x_vidioc_g_ctrl - get the value of a control
+ */
+static int si470x_vidioc_g_ctrl(struct file *file, void *priv,
+		struct v4l2_control *ctrl)
+{
+	struct si470x_device *radio = video_drvdata(file);
+	int retval = 0;
+
+	/* safety checks */
+	retval = si470x_disconnect_check(radio);
+	if (retval)
+		goto done;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_VOLUME:
+		ctrl->value = radio->registers[SYSCONFIG2] &
+				SYSCONFIG2_VOLUME;
+		break;
+	case V4L2_CID_AUDIO_MUTE:
+		ctrl->value = ((radio->registers[POWERCFG] &
+				POWERCFG_DMUTE) == 0) ? 1 : 0;
+		break;
+	default:
+		retval = -EINVAL;
+	}
+
+done:
+	if (retval < 0)
+		dev_warn(&radio->videodev->dev,
+			"get control failed with %d\n", retval);
+	return retval;
+}
+
+
+/*
+ * si470x_vidioc_s_ctrl - set the value of a control
+ */
+static int si470x_vidioc_s_ctrl(struct file *file, void *priv,
+		struct v4l2_control *ctrl)
+{
+	struct si470x_device *radio = video_drvdata(file);
+	int retval = 0;
+
+	/* safety checks */
+	retval = si470x_disconnect_check(radio);
+	if (retval)
+		goto done;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_VOLUME:
+		radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME;
+		radio->registers[SYSCONFIG2] |= ctrl->value;
+		retval = si470x_set_register(radio, SYSCONFIG2);
+		break;
+	case V4L2_CID_AUDIO_MUTE:
+		if (ctrl->value == 1)
+			radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
+		else
+			radio->registers[POWERCFG] |= POWERCFG_DMUTE;
+		retval = si470x_set_register(radio, POWERCFG);
+		break;
+	default:
+		retval = -EINVAL;
+	}
+
+done:
+	if (retval < 0)
+		dev_warn(&radio->videodev->dev,
+			"set control failed with %d\n", retval);
+	return retval;
+}
+
+
+/*
+ * si470x_vidioc_g_audio - get audio attributes
+ */
+static int si470x_vidioc_g_audio(struct file *file, void *priv,
+		struct v4l2_audio *audio)
+{
+	/* driver constants */
+	audio->index = 0;
+	strcpy(audio->name, "Radio");
+	audio->capability = V4L2_AUDCAP_STEREO;
+	audio->mode = 0;
+
+	return 0;
+}
+
+
+/*
+ * si470x_vidioc_g_tuner - get tuner attributes
+ */
+static int si470x_vidioc_g_tuner(struct file *file, void *priv,
+		struct v4l2_tuner *tuner)
+{
+	struct si470x_device *radio = video_drvdata(file);
+	int retval = 0;
+
+	/* safety checks */
+	retval = si470x_disconnect_check(radio);
+	if (retval)
+		goto done;
+
+	if (tuner->index != 0) {
+		retval = -EINVAL;
+		goto done;
+	}
+
+	retval = si470x_get_register(radio, STATUSRSSI);
+	if (retval < 0)
+		goto done;
+
+	/* driver constants */
+	strcpy(tuner->name, "FM");
+	tuner->type = V4L2_TUNER_RADIO;
+#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE)
+	tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+			    V4L2_TUNER_CAP_RDS;
+#else
+	tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
+#endif
+
+	/* range limits */
+	switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
+	/* 0: 87.5 - 108 MHz (USA, Europe, default) */
+	default:
+		tuner->rangelow  =  87.5 * FREQ_MUL;
+		tuner->rangehigh = 108   * FREQ_MUL;
+		break;
+	/* 1: 76   - 108 MHz (Japan wide band) */
+	case 1:
+		tuner->rangelow  =  76   * FREQ_MUL;
+		tuner->rangehigh = 108   * FREQ_MUL;
+		break;
+	/* 2: 76   -  90 MHz (Japan) */
+	case 2:
+		tuner->rangelow  =  76   * FREQ_MUL;
+		tuner->rangehigh =  90   * FREQ_MUL;
+		break;
+	};
+
+	/* stereo indicator == stereo (instead of mono) */
+	if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0)
+		tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
+	else
+		tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE)
+	/* If there is a reliable method of detecting an RDS channel,
+	   then this code should check for that before setting this
+	   RDS subchannel. */
+	tuner->rxsubchans |= V4L2_TUNER_SUB_RDS;
+#endif
+
+	/* mono/stereo selector */
+	if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0)
+		tuner->audmode = V4L2_TUNER_MODE_STEREO;
+	else
+		tuner->audmode = V4L2_TUNER_MODE_MONO;
+
+	/* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */
+	/* measured in units of db쨉V in 1 db increments (max at ~75 db쨉V) */
+	tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI);
+	/* the ideal factor is 0xffff/75 = 873,8 */
+	tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10);
+
+	/* automatic frequency control: -1: freq to low, 1 freq to high */
+	/* AFCRL does only indicate that freq. differs, not if too low/high */
+	tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0;
+
+done:
+	if (retval < 0)
+		dev_warn(&radio->videodev->dev,
+			"get tuner failed with %d\n", retval);
+	return retval;
+}
+
+
+/*
+ * si470x_vidioc_s_tuner - set tuner attributes
+ */
+static int si470x_vidioc_s_tuner(struct file *file, void *priv,
+		struct v4l2_tuner *tuner)
+{
+	struct si470x_device *radio = video_drvdata(file);
+	int retval = -EINVAL;
+
+	/* safety checks */
+	retval = si470x_disconnect_check(radio);
+	if (retval)
+		goto done;
+
+	if (tuner->index != 0)
+		goto done;
+
+	/* mono/stereo selector */
+	switch (tuner->audmode) {
+	case V4L2_TUNER_MODE_MONO:
+		radio->registers[POWERCFG] |= POWERCFG_MONO;  /* force mono */
+		break;
+	case V4L2_TUNER_MODE_STEREO:
+		radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */
+		break;
+	default:
+		goto done;
+	}
+
+	retval = si470x_set_register(radio, POWERCFG);
+
+done:
+	if (retval < 0)
+		dev_warn(&radio->videodev->dev,
+			"set tuner failed with %d\n", retval);
+	return retval;
+}
+
+
+/*
+ * si470x_vidioc_g_frequency - get tuner or modulator radio frequency
+ */
+static int si470x_vidioc_g_frequency(struct file *file, void *priv,
+		struct v4l2_frequency *freq)
+{
+	struct si470x_device *radio = video_drvdata(file);
+	int retval = 0;
+
+	/* safety checks */
+	retval = si470x_disconnect_check(radio);
+	if (retval)
+		goto done;
+
+	if (freq->tuner != 0) {
+		retval = -EINVAL;
+		goto done;
+	}
+
+	freq->type = V4L2_TUNER_RADIO;
+	retval = si470x_get_freq(radio, &freq->frequency);
+
+done:
+	if (retval < 0)
+		dev_warn(&radio->videodev->dev,
+			"get frequency failed with %d\n", retval);
+	return retval;
+}
+
+
+/*
+ * si470x_vidioc_s_frequency - set tuner or modulator radio frequency
+ */
+static int si470x_vidioc_s_frequency(struct file *file, void *priv,
+		struct v4l2_frequency *freq)
+{
+	struct si470x_device *radio = video_drvdata(file);
+	int retval = 0;
+
+	/* safety checks */
+	retval = si470x_disconnect_check(radio);
+	if (retval)
+		goto done;
+
+	if (freq->tuner != 0) {
+		retval = -EINVAL;
+		goto done;
+	}
+
+	retval = si470x_set_freq(radio, freq->frequency);
+
+done:
+	if (retval < 0)
+		dev_warn(&radio->videodev->dev,
+			"set frequency failed with %d\n", retval);
+	return retval;
+}
+
+
+/*
+ * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek
+ */
+static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
+		struct v4l2_hw_freq_seek *seek)
+{
+	struct si470x_device *radio = video_drvdata(file);
+	int retval = 0;
+
+	/* safety checks */
+	retval = si470x_disconnect_check(radio);
+	if (retval)
+		goto done;
+
+	if (seek->tuner != 0) {
+		retval = -EINVAL;
+		goto done;
+	}
+
+	retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward);
+
+done:
+	if (retval < 0)
+		dev_warn(&radio->videodev->dev,
+			"set hardware frequency seek failed with %d\n", retval);
+	return retval;
+}
+
+
+/*
+ * si470x_ioctl_ops - video device ioctl operations
+ */
+static const struct v4l2_ioctl_ops si470x_ioctl_ops = {
+	.vidioc_querycap	= si470x_vidioc_querycap,
+	.vidioc_queryctrl	= si470x_vidioc_queryctrl,
+	.vidioc_g_ctrl		= si470x_vidioc_g_ctrl,
+	.vidioc_s_ctrl		= si470x_vidioc_s_ctrl,
+	.vidioc_g_audio		= si470x_vidioc_g_audio,
+	.vidioc_g_tuner		= si470x_vidioc_g_tuner,
+	.vidioc_s_tuner		= si470x_vidioc_s_tuner,
+	.vidioc_g_frequency	= si470x_vidioc_g_frequency,
+	.vidioc_s_frequency	= si470x_vidioc_s_frequency,
+	.vidioc_s_hw_freq_seek	= si470x_vidioc_s_hw_freq_seek,
+};
+
+
+/*
+ * si470x_viddev_template - video device interface
+ */
+struct video_device si470x_viddev_template = {
+	.fops			= &si470x_fops,
+	.name			= DRIVER_NAME,
+	.release		= video_device_release,
+	.ioctl_ops		= &si470x_ioctl_ops,
+};

+ 401 - 0
drivers/media/radio/si470x/radio-si470x-i2c.c

@@ -0,0 +1,401 @@
+/*
+ * drivers/media/radio/si470x/radio-si470x-i2c.c
+ *
+ * I2C driver for radios with Silicon Labs Si470x FM Radio Receivers
+ *
+ * Copyright (c) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+/*
+ * ToDo:
+ * - RDS support
+ */
+
+
+/* driver definitions */
+#define DRIVER_AUTHOR "Joonyoung Shim <jy0922.shim@samsung.com>";
+#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 0)
+#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
+#define DRIVER_DESC "I2C radio driver for Si470x FM Radio Receivers"
+#define DRIVER_VERSION "1.0.0"
+
+/* kernel includes */
+#include <linux/i2c.h>
+#include <linux/delay.h>
+
+#include "radio-si470x.h"
+
+
+/* I2C Device ID List */
+static const struct i2c_device_id si470x_i2c_id[] = {
+	/* Generic Entry */
+	{ "si470x", 0 },
+	/* Terminating entry */
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, si470x_i2c_id);
+
+
+
+/**************************************************************************
+ * Module Parameters
+ **************************************************************************/
+
+/* Radio Nr */
+static int radio_nr = -1;
+module_param(radio_nr, int, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio Nr");
+
+
+
+/**************************************************************************
+ * I2C Definitions
+ **************************************************************************/
+
+/* Write starts with the upper byte of register 0x02 */
+#define WRITE_REG_NUM		8
+#define WRITE_INDEX(i)		(i + 0x02)
+
+/* Read starts with the upper byte of register 0x0a */
+#define READ_REG_NUM		RADIO_REGISTER_NUM
+#define READ_INDEX(i)		((i + RADIO_REGISTER_NUM - 0x0a) % READ_REG_NUM)
+
+
+
+/**************************************************************************
+ * General Driver Functions - REGISTERs
+ **************************************************************************/
+
+/*
+ * si470x_get_register - read register
+ */
+int si470x_get_register(struct si470x_device *radio, int regnr)
+{
+	u16 buf[READ_REG_NUM];
+	struct i2c_msg msgs[1] = {
+		{ radio->client->addr, I2C_M_RD, sizeof(u16) * READ_REG_NUM,
+			(void *)buf },
+	};
+
+	if (i2c_transfer(radio->client->adapter, msgs, 1) != 1)
+		return -EIO;
+
+	radio->registers[regnr] = __be16_to_cpu(buf[READ_INDEX(regnr)]);
+
+	return 0;
+}
+
+
+/*
+ * si470x_set_register - write register
+ */
+int si470x_set_register(struct si470x_device *radio, int regnr)
+{
+	int i;
+	u16 buf[WRITE_REG_NUM];
+	struct i2c_msg msgs[1] = {
+		{ radio->client->addr, 0, sizeof(u16) * WRITE_REG_NUM,
+			(void *)buf },
+	};
+
+	for (i = 0; i < WRITE_REG_NUM; i++)
+		buf[i] = __cpu_to_be16(radio->registers[WRITE_INDEX(i)]);
+
+	if (i2c_transfer(radio->client->adapter, msgs, 1) != 1)
+		return -EIO;
+
+	return 0;
+}
+
+
+
+/**************************************************************************
+ * General Driver Functions - ENTIRE REGISTERS
+ **************************************************************************/
+
+/*
+ * si470x_get_all_registers - read entire registers
+ */
+static int si470x_get_all_registers(struct si470x_device *radio)
+{
+	int i;
+	u16 buf[READ_REG_NUM];
+	struct i2c_msg msgs[1] = {
+		{ radio->client->addr, I2C_M_RD, sizeof(u16) * READ_REG_NUM,
+			(void *)buf },
+	};
+
+	if (i2c_transfer(radio->client->adapter, msgs, 1) != 1)
+		return -EIO;
+
+	for (i = 0; i < READ_REG_NUM; i++)
+		radio->registers[i] = __be16_to_cpu(buf[READ_INDEX(i)]);
+
+	return 0;
+}
+
+
+
+/**************************************************************************
+ * General Driver Functions - DISCONNECT_CHECK
+ **************************************************************************/
+
+/*
+ * si470x_disconnect_check - check whether radio disconnects
+ */
+int si470x_disconnect_check(struct si470x_device *radio)
+{
+	return 0;
+}
+
+
+
+/**************************************************************************
+ * File Operations Interface
+ **************************************************************************/
+
+/*
+ * si470x_fops_open - file open
+ */
+static int si470x_fops_open(struct file *file)
+{
+	struct si470x_device *radio = video_drvdata(file);
+	int retval = 0;
+
+	mutex_lock(&radio->lock);
+	radio->users++;
+
+	if (radio->users == 1)
+		/* start radio */
+		retval = si470x_start(radio);
+
+	mutex_unlock(&radio->lock);
+
+	return retval;
+}
+
+
+/*
+ * si470x_fops_release - file release
+ */
+static int si470x_fops_release(struct file *file)
+{
+	struct si470x_device *radio = video_drvdata(file);
+	int retval = 0;
+
+	/* safety check */
+	if (!radio)
+		return -ENODEV;
+
+	mutex_lock(&radio->lock);
+	radio->users--;
+	if (radio->users == 0)
+		/* stop radio */
+		retval = si470x_stop(radio);
+
+	mutex_unlock(&radio->lock);
+
+	return retval;
+}
+
+
+/*
+ * si470x_fops - file operations interface
+ */
+const struct v4l2_file_operations si470x_fops = {
+	.owner		= THIS_MODULE,
+	.ioctl		= video_ioctl2,
+	.open		= si470x_fops_open,
+	.release	= si470x_fops_release,
+};
+
+
+
+/**************************************************************************
+ * Video4Linux Interface
+ **************************************************************************/
+
+/*
+ * si470x_vidioc_querycap - query device capabilities
+ */
+int si470x_vidioc_querycap(struct file *file, void *priv,
+		struct v4l2_capability *capability)
+{
+	strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
+	strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
+	capability->version = DRIVER_KERNEL_VERSION;
+	capability->capabilities = V4L2_CAP_HW_FREQ_SEEK |
+		V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+
+	return 0;
+}
+
+
+
+/**************************************************************************
+ * I2C Interface
+ **************************************************************************/
+
+/*
+ * si470x_i2c_probe - probe for the device
+ */
+static int __devinit si470x_i2c_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct si470x_device *radio;
+	int retval = 0;
+	unsigned char version_warning = 0;
+
+	/* private data allocation and initialization */
+	radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL);
+	if (!radio) {
+		retval = -ENOMEM;
+		goto err_initial;
+	}
+	radio->users = 0;
+	radio->client = client;
+	mutex_init(&radio->lock);
+
+	/* video device allocation and initialization */
+	radio->videodev = video_device_alloc();
+	if (!radio->videodev) {
+		retval = -ENOMEM;
+		goto err_radio;
+	}
+	memcpy(radio->videodev, &si470x_viddev_template,
+			sizeof(si470x_viddev_template));
+	video_set_drvdata(radio->videodev, radio);
+
+	/* power up : need 110ms */
+	radio->registers[POWERCFG] = POWERCFG_ENABLE;
+	if (si470x_set_register(radio, POWERCFG) < 0) {
+		retval = -EIO;
+		goto err_all;
+	}
+	msleep(110);
+
+	/* get device and chip versions */
+	if (si470x_get_all_registers(radio) < 0) {
+		retval = -EIO;
+		goto err_video;
+	}
+	dev_info(&client->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n",
+			radio->registers[DEVICEID], radio->registers[CHIPID]);
+	if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) < RADIO_FW_VERSION) {
+		dev_warn(&client->dev,
+			"This driver is known to work with "
+			"firmware version %hu,\n", RADIO_FW_VERSION);
+		dev_warn(&client->dev,
+			"but the device has firmware version %hu.\n",
+			radio->registers[CHIPID] & CHIPID_FIRMWARE);
+		version_warning = 1;
+	}
+
+	/* give out version warning */
+	if (version_warning == 1) {
+		dev_warn(&client->dev,
+			"If you have some trouble using this driver,\n");
+		dev_warn(&client->dev,
+			"please report to V4L ML at "
+			"linux-media@vger.kernel.org\n");
+	}
+
+	/* set initial frequency */
+	si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
+
+	/* register video device */
+	retval = video_register_device(radio->videodev, VFL_TYPE_RADIO,
+			radio_nr);
+	if (retval) {
+		dev_warn(&client->dev, "Could not register video device\n");
+		goto err_all;
+	}
+	i2c_set_clientdata(client, radio);
+
+	return 0;
+err_all:
+err_video:
+	video_device_release(radio->videodev);
+err_radio:
+	kfree(radio);
+err_initial:
+	return retval;
+}
+
+
+/*
+ * si470x_i2c_remove - remove the device
+ */
+static __devexit int si470x_i2c_remove(struct i2c_client *client)
+{
+	struct si470x_device *radio = i2c_get_clientdata(client);
+
+	video_unregister_device(radio->videodev);
+	kfree(radio);
+	i2c_set_clientdata(client, NULL);
+
+	return 0;
+}
+
+
+/*
+ * si470x_i2c_driver - i2c driver interface
+ */
+static struct i2c_driver si470x_i2c_driver = {
+	.driver = {
+		.name		= "si470x",
+		.owner		= THIS_MODULE,
+	},
+	.probe			= si470x_i2c_probe,
+	.remove			= __devexit_p(si470x_i2c_remove),
+	.id_table		= si470x_i2c_id,
+};
+
+
+
+/**************************************************************************
+ * Module Interface
+ **************************************************************************/
+
+/*
+ * si470x_i2c_init - module init
+ */
+static int __init si470x_i2c_init(void)
+{
+	printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n");
+	return i2c_add_driver(&si470x_i2c_driver);
+}
+
+
+/*
+ * si470x_i2c_exit - module exit
+ */
+static void __exit si470x_i2c_exit(void)
+{
+	i2c_del_driver(&si470x_i2c_driver);
+}
+
+
+module_init(si470x_i2c_init);
+module_exit(si470x_i2c_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);

+ 988 - 0
drivers/media/radio/si470x/radio-si470x-usb.c

@@ -0,0 +1,988 @@
+/*
+ *  drivers/media/radio/si470x/radio-si470x-usb.c
+ *
+ *  USB driver for radios with Silicon Labs Si470x FM Radio Receivers
+ *
+ *  Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net>
+ *
+ * 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
+ */
+
+
+/*
+ * ToDo:
+ * - add firmware download/update support
+ */
+
+
+/* driver definitions */
+#define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>"
+#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 10)
+#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
+#define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers"
+#define DRIVER_VERSION "1.0.10"
+
+/* kernel includes */
+#include <linux/usb.h>
+#include <linux/hid.h>
+
+#include "radio-si470x.h"
+
+
+/* USB Device ID List */
+static struct usb_device_id si470x_usb_driver_id_table[] = {
+	/* Silicon Labs USB FM Radio Reference Design */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) },
+	/* ADS/Tech FM Radio Receiver (formerly Instant FM Music) */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) },
+	/* KWorld USB FM Radio SnapMusic Mobile 700 (FM700) */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x1b80, 0xd700, USB_CLASS_HID, 0, 0) },
+	/* Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) */
+	{ USB_DEVICE_AND_INTERFACE_INFO(0x10c5, 0x819a, USB_CLASS_HID, 0, 0) },
+	/* Terminating entry */
+	{ }
+};
+MODULE_DEVICE_TABLE(usb, si470x_usb_driver_id_table);
+
+
+
+/**************************************************************************
+ * Module Parameters
+ **************************************************************************/
+
+/* Radio Nr */
+static int radio_nr = -1;
+module_param(radio_nr, int, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio Nr");
+
+/* USB timeout */
+static unsigned int usb_timeout = 500;
+module_param(usb_timeout, uint, 0644);
+MODULE_PARM_DESC(usb_timeout, "USB timeout (ms): *500*");
+
+/* RDS buffer blocks */
+static unsigned int rds_buf = 100;
+module_param(rds_buf, uint, 0444);
+MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");
+
+/* RDS maximum block errors */
+static unsigned short max_rds_errors = 1;
+/* 0 means   0  errors requiring correction */
+/* 1 means 1-2  errors requiring correction (used by original USBRadio.exe) */
+/* 2 means 3-5  errors requiring correction */
+/* 3 means   6+ errors or errors in checkword, correction not possible */
+module_param(max_rds_errors, ushort, 0644);
+MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*");
+
+
+
+/**************************************************************************
+ * USB HID Reports
+ **************************************************************************/
+
+/* Reports 1-16 give direct read/write access to the 16 Si470x registers */
+/* with the (REPORT_ID - 1) corresponding to the register address across USB */
+/* endpoint 0 using GET_REPORT and SET_REPORT */
+#define REGISTER_REPORT_SIZE	(RADIO_REGISTER_SIZE + 1)
+#define REGISTER_REPORT(reg)	((reg) + 1)
+
+/* Report 17 gives direct read/write access to the entire Si470x register */
+/* map across endpoint 0 using GET_REPORT and SET_REPORT */
+#define ENTIRE_REPORT_SIZE	(RADIO_REGISTER_NUM * RADIO_REGISTER_SIZE + 1)
+#define ENTIRE_REPORT		17
+
+/* Report 18 is used to send the lowest 6 Si470x registers up the HID */
+/* interrupt endpoint 1 to Windows every 20 milliseconds for status */
+#define RDS_REPORT_SIZE		(RDS_REGISTER_NUM * RADIO_REGISTER_SIZE + 1)
+#define RDS_REPORT		18
+
+/* Report 19: LED state */
+#define LED_REPORT_SIZE		3
+#define LED_REPORT		19
+
+/* Report 19: stream */
+#define STREAM_REPORT_SIZE	3
+#define STREAM_REPORT		19
+
+/* Report 20: scratch */
+#define SCRATCH_PAGE_SIZE	63
+#define SCRATCH_REPORT_SIZE	(SCRATCH_PAGE_SIZE + 1)
+#define SCRATCH_REPORT		20
+
+/* Reports 19-22: flash upgrade of the C8051F321 */
+#define WRITE_REPORT_SIZE	4
+#define WRITE_REPORT		19
+#define FLASH_REPORT_SIZE	64
+#define FLASH_REPORT		20
+#define CRC_REPORT_SIZE		3
+#define CRC_REPORT		21
+#define RESPONSE_REPORT_SIZE	2
+#define RESPONSE_REPORT		22
+
+/* Report 23: currently unused, but can accept 60 byte reports on the HID */
+/* interrupt out endpoint 2 every 1 millisecond */
+#define UNUSED_REPORT		23
+
+
+
+/**************************************************************************
+ * Software/Hardware Versions from Scratch Page
+ **************************************************************************/
+#define RADIO_SW_VERSION_NOT_BOOTLOADABLE	6
+#define RADIO_SW_VERSION			7
+#define RADIO_HW_VERSION			1
+
+
+
+/**************************************************************************
+ * LED State Definitions
+ **************************************************************************/
+#define LED_COMMAND		0x35
+
+#define NO_CHANGE_LED		0x00
+#define ALL_COLOR_LED		0x01	/* streaming state */
+#define BLINK_GREEN_LED		0x02	/* connect state */
+#define BLINK_RED_LED		0x04
+#define BLINK_ORANGE_LED	0x10	/* disconnect state */
+#define SOLID_GREEN_LED		0x20	/* tuning/seeking state */
+#define SOLID_RED_LED		0x40	/* bootload state */
+#define SOLID_ORANGE_LED	0x80
+
+
+
+/**************************************************************************
+ * Stream State Definitions
+ **************************************************************************/
+#define STREAM_COMMAND	0x36
+#define STREAM_VIDPID	0x00
+#define STREAM_AUDIO	0xff
+
+
+
+/**************************************************************************
+ * Bootloader / Flash Commands
+ **************************************************************************/
+
+/* unique id sent to bootloader and required to put into a bootload state */
+#define UNIQUE_BL_ID		0x34
+
+/* mask for the flash data */
+#define FLASH_DATA_MASK		0x55
+
+/* bootloader commands */
+#define GET_SW_VERSION_COMMAND	0x00
+#define SET_PAGE_COMMAND	0x01
+#define ERASE_PAGE_COMMAND	0x02
+#define WRITE_PAGE_COMMAND	0x03
+#define CRC_ON_PAGE_COMMAND	0x04
+#define READ_FLASH_BYTE_COMMAND	0x05
+#define RESET_DEVICE_COMMAND	0x06
+#define GET_HW_VERSION_COMMAND	0x07
+#define BLANK			0xff
+
+/* bootloader command responses */
+#define COMMAND_OK		0x01
+#define COMMAND_FAILED		0x02
+#define COMMAND_PENDING		0x03
+
+
+
+/**************************************************************************
+ * General Driver Functions - REGISTER_REPORTs
+ **************************************************************************/
+
+/*
+ * si470x_get_report - receive a HID report
+ */
+static int si470x_get_report(struct si470x_device *radio, void *buf, int size)
+{
+	unsigned char *report = (unsigned char *) buf;
+	int retval;
+
+	retval = usb_control_msg(radio->usbdev,
+		usb_rcvctrlpipe(radio->usbdev, 0),
+		HID_REQ_GET_REPORT,
+		USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+		report[0], 2,
+		buf, size, usb_timeout);
+
+	if (retval < 0)
+		dev_warn(&radio->intf->dev,
+			"si470x_get_report: usb_control_msg returned %d\n",
+			retval);
+	return retval;
+}
+
+
+/*
+ * si470x_set_report - send a HID report
+ */
+static int si470x_set_report(struct si470x_device *radio, void *buf, int size)
+{
+	unsigned char *report = (unsigned char *) buf;
+	int retval;
+
+	retval = usb_control_msg(radio->usbdev,
+		usb_sndctrlpipe(radio->usbdev, 0),
+		HID_REQ_SET_REPORT,
+		USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
+		report[0], 2,
+		buf, size, usb_timeout);
+
+	if (retval < 0)
+		dev_warn(&radio->intf->dev,
+			"si470x_set_report: usb_control_msg returned %d\n",
+			retval);
+	return retval;
+}
+
+
+/*
+ * si470x_get_register - read register
+ */
+int si470x_get_register(struct si470x_device *radio, int regnr)
+{
+	unsigned char buf[REGISTER_REPORT_SIZE];
+	int retval;
+
+	buf[0] = REGISTER_REPORT(regnr);
+
+	retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
+
+	if (retval >= 0)
+		radio->registers[regnr] = get_unaligned_be16(&buf[1]);
+
+	return (retval < 0) ? -EINVAL : 0;
+}
+
+
+/*
+ * si470x_set_register - write register
+ */
+int si470x_set_register(struct si470x_device *radio, int regnr)
+{
+	unsigned char buf[REGISTER_REPORT_SIZE];
+	int retval;
+
+	buf[0] = REGISTER_REPORT(regnr);
+	put_unaligned_be16(radio->registers[regnr], &buf[1]);
+
+	retval = si470x_set_report(radio, (void *) &buf, sizeof(buf));
+
+	return (retval < 0) ? -EINVAL : 0;
+}
+
+
+
+/**************************************************************************
+ * General Driver Functions - ENTIRE_REPORT
+ **************************************************************************/
+
+/*
+ * si470x_get_all_registers - read entire registers
+ */
+static int si470x_get_all_registers(struct si470x_device *radio)
+{
+	unsigned char buf[ENTIRE_REPORT_SIZE];
+	int retval;
+	unsigned char regnr;
+
+	buf[0] = ENTIRE_REPORT;
+
+	retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
+
+	if (retval >= 0)
+		for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++)
+			radio->registers[regnr] = get_unaligned_be16(
+				&buf[regnr * RADIO_REGISTER_SIZE + 1]);
+
+	return (retval < 0) ? -EINVAL : 0;
+}
+
+
+
+/**************************************************************************
+ * General Driver Functions - LED_REPORT
+ **************************************************************************/
+
+/*
+ * si470x_set_led_state - sets the led state
+ */
+static int si470x_set_led_state(struct si470x_device *radio,
+		unsigned char led_state)
+{
+	unsigned char buf[LED_REPORT_SIZE];
+	int retval;
+
+	buf[0] = LED_REPORT;
+	buf[1] = LED_COMMAND;
+	buf[2] = led_state;
+
+	retval = si470x_set_report(radio, (void *) &buf, sizeof(buf));
+
+	return (retval < 0) ? -EINVAL : 0;
+}
+
+
+
+/**************************************************************************
+ * General Driver Functions - SCRATCH_REPORT
+ **************************************************************************/
+
+/*
+ * si470x_get_scratch_versions - gets the scratch page and version infos
+ */
+static int si470x_get_scratch_page_versions(struct si470x_device *radio)
+{
+	unsigned char buf[SCRATCH_REPORT_SIZE];
+	int retval;
+
+	buf[0] = SCRATCH_REPORT;
+
+	retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
+
+	if (retval < 0)
+		dev_warn(&radio->intf->dev, "si470x_get_scratch: "
+			"si470x_get_report returned %d\n", retval);
+	else {
+		radio->software_version = buf[1];
+		radio->hardware_version = buf[2];
+	}
+
+	return (retval < 0) ? -EINVAL : 0;
+}
+
+
+
+/**************************************************************************
+ * General Driver Functions - DISCONNECT_CHECK
+ **************************************************************************/
+
+/*
+ * si470x_disconnect_check - check whether radio disconnects
+ */
+int si470x_disconnect_check(struct si470x_device *radio)
+{
+	if (radio->disconnected)
+		return -EIO;
+	else
+		return 0;
+}
+
+
+
+/**************************************************************************
+ * RDS Driver Functions
+ **************************************************************************/
+
+/*
+ * si470x_int_in_callback - rds callback and processing function
+ *
+ * TODO: do we need to use mutex locks in some sections?
+ */
+static void si470x_int_in_callback(struct urb *urb)
+{
+	struct si470x_device *radio = urb->context;
+	unsigned char buf[RDS_REPORT_SIZE];
+	int retval;
+	unsigned char regnr;
+	unsigned char blocknum;
+	unsigned short bler; /* rds block errors */
+	unsigned short rds;
+	unsigned char tmpbuf[3];
+
+	if (urb->status) {
+		if (urb->status == -ENOENT ||
+				urb->status == -ECONNRESET ||
+				urb->status == -ESHUTDOWN) {
+			return;
+		} else {
+			dev_warn(&radio->intf->dev,
+			 "non-zero urb status (%d)\n", urb->status);
+			goto resubmit; /* Maybe we can recover. */
+		}
+	}
+
+	/* safety checks */
+	if (radio->disconnected)
+		return;
+	if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
+		goto resubmit;
+
+	if (urb->actual_length > 0) {
+		/* Update RDS registers with URB data */
+		buf[0] = RDS_REPORT;
+		for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++)
+			radio->registers[STATUSRSSI + regnr] =
+			    get_unaligned_be16(&radio->int_in_buffer[
+				regnr * RADIO_REGISTER_SIZE + 1]);
+		/* get rds blocks */
+		if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) {
+			/* No RDS group ready, better luck next time */
+			goto resubmit;
+		}
+		if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSS) == 0) {
+			/* RDS decoder not synchronized */
+			goto resubmit;
+		}
+		for (blocknum = 0; blocknum < 4; blocknum++) {
+			switch (blocknum) {
+			default:
+				bler = (radio->registers[STATUSRSSI] &
+						STATUSRSSI_BLERA) >> 9;
+				rds = radio->registers[RDSA];
+				break;
+			case 1:
+				bler = (radio->registers[READCHAN] &
+						READCHAN_BLERB) >> 14;
+				rds = radio->registers[RDSB];
+				break;
+			case 2:
+				bler = (radio->registers[READCHAN] &
+						READCHAN_BLERC) >> 12;
+				rds = radio->registers[RDSC];
+				break;
+			case 3:
+				bler = (radio->registers[READCHAN] &
+						READCHAN_BLERD) >> 10;
+				rds = radio->registers[RDSD];
+				break;
+			};
+
+			/* Fill the V4L2 RDS buffer */
+			put_unaligned_le16(rds, &tmpbuf);
+			tmpbuf[2] = blocknum;		/* offset name */
+			tmpbuf[2] |= blocknum << 3;	/* received offset */
+			if (bler > max_rds_errors)
+				tmpbuf[2] |= 0x80; /* uncorrectable errors */
+			else if (bler > 0)
+				tmpbuf[2] |= 0x40; /* corrected error(s) */
+
+			/* copy RDS block to internal buffer */
+			memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3);
+			radio->wr_index += 3;
+
+			/* wrap write pointer */
+			if (radio->wr_index >= radio->buf_size)
+				radio->wr_index = 0;
+
+			/* check for overflow */
+			if (radio->wr_index == radio->rd_index) {
+				/* increment and wrap read pointer */
+				radio->rd_index += 3;
+				if (radio->rd_index >= radio->buf_size)
+					radio->rd_index = 0;
+			}
+		}
+		if (radio->wr_index != radio->rd_index)
+			wake_up_interruptible(&radio->read_queue);
+	}
+
+resubmit:
+	/* Resubmit if we're still running. */
+	if (radio->int_in_running && radio->usbdev) {
+		retval = usb_submit_urb(radio->int_in_urb, GFP_ATOMIC);
+		if (retval) {
+			dev_warn(&radio->intf->dev,
+			       "resubmitting urb failed (%d)", retval);
+			radio->int_in_running = 0;
+		}
+	}
+}
+
+
+
+/**************************************************************************
+ * File Operations Interface
+ **************************************************************************/
+
+/*
+ * si470x_fops_read - read RDS data
+ */
+static ssize_t si470x_fops_read(struct file *file, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct si470x_device *radio = video_drvdata(file);
+	int retval = 0;
+	unsigned int block_count = 0;
+
+	/* switch on rds reception */
+	if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
+		si470x_rds_on(radio);
+
+	/* block if no new data available */
+	while (radio->wr_index == radio->rd_index) {
+		if (file->f_flags & O_NONBLOCK) {
+			retval = -EWOULDBLOCK;
+			goto done;
+		}
+		if (wait_event_interruptible(radio->read_queue,
+			radio->wr_index != radio->rd_index) < 0) {
+			retval = -EINTR;
+			goto done;
+		}
+	}
+
+	/* calculate block count from byte count */
+	count /= 3;
+
+	/* copy RDS block out of internal buffer and to user buffer */
+	mutex_lock(&radio->lock);
+	while (block_count < count) {
+		if (radio->rd_index == radio->wr_index)
+			break;
+
+		/* always transfer rds complete blocks */
+		if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3))
+			/* retval = -EFAULT; */
+			break;
+
+		/* increment and wrap read pointer */
+		radio->rd_index += 3;
+		if (radio->rd_index >= radio->buf_size)
+			radio->rd_index = 0;
+
+		/* increment counters */
+		block_count++;
+		buf += 3;
+		retval += 3;
+	}
+	mutex_unlock(&radio->lock);
+
+done:
+	return retval;
+}
+
+
+/*
+ * si470x_fops_poll - poll RDS data
+ */
+static unsigned int si470x_fops_poll(struct file *file,
+		struct poll_table_struct *pts)
+{
+	struct si470x_device *radio = video_drvdata(file);
+	int retval = 0;
+
+	/* switch on rds reception */
+	if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
+		si470x_rds_on(radio);
+
+	poll_wait(file, &radio->read_queue, pts);
+
+	if (radio->rd_index != radio->wr_index)
+		retval = POLLIN | POLLRDNORM;
+
+	return retval;
+}
+
+
+/*
+ * si470x_fops_open - file open
+ */
+static int si470x_fops_open(struct file *file)
+{
+	struct si470x_device *radio = video_drvdata(file);
+	int retval;
+
+	lock_kernel();
+	radio->users++;
+
+	retval = usb_autopm_get_interface(radio->intf);
+	if (retval < 0) {
+		radio->users--;
+		retval = -EIO;
+		goto done;
+	}
+
+	if (radio->users == 1) {
+		/* start radio */
+		retval = si470x_start(radio);
+		if (retval < 0) {
+			usb_autopm_put_interface(radio->intf);
+			goto done;
+		}
+
+		/* initialize interrupt urb */
+		usb_fill_int_urb(radio->int_in_urb, radio->usbdev,
+			usb_rcvintpipe(radio->usbdev,
+			radio->int_in_endpoint->bEndpointAddress),
+			radio->int_in_buffer,
+			le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize),
+			si470x_int_in_callback,
+			radio,
+			radio->int_in_endpoint->bInterval);
+
+		radio->int_in_running = 1;
+		mb();
+
+		retval = usb_submit_urb(radio->int_in_urb, GFP_KERNEL);
+		if (retval) {
+			dev_info(&radio->intf->dev,
+				 "submitting int urb failed (%d)\n", retval);
+			radio->int_in_running = 0;
+			usb_autopm_put_interface(radio->intf);
+		}
+	}
+
+done:
+	unlock_kernel();
+	return retval;
+}
+
+
+/*
+ * si470x_fops_release - file release
+ */
+static int si470x_fops_release(struct file *file)
+{
+	struct si470x_device *radio = video_drvdata(file);
+	int retval = 0;
+
+	/* safety check */
+	if (!radio) {
+		retval = -ENODEV;
+		goto done;
+	}
+
+	mutex_lock(&radio->disconnect_lock);
+	radio->users--;
+	if (radio->users == 0) {
+		/* shutdown interrupt handler */
+		if (radio->int_in_running) {
+			radio->int_in_running = 0;
+		if (radio->int_in_urb)
+			usb_kill_urb(radio->int_in_urb);
+		}
+
+		if (radio->disconnected) {
+			video_unregister_device(radio->videodev);
+			kfree(radio->int_in_buffer);
+			kfree(radio->buffer);
+			kfree(radio);
+			goto unlock;
+		}
+
+		/* cancel read processes */
+		wake_up_interruptible(&radio->read_queue);
+
+		/* stop radio */
+		retval = si470x_stop(radio);
+		usb_autopm_put_interface(radio->intf);
+	}
+unlock:
+	mutex_unlock(&radio->disconnect_lock);
+done:
+	return retval;
+}
+
+
+/*
+ * si470x_fops - file operations interface
+ */
+const struct v4l2_file_operations si470x_fops = {
+	.owner		= THIS_MODULE,
+	.read		= si470x_fops_read,
+	.poll		= si470x_fops_poll,
+	.ioctl		= video_ioctl2,
+	.open		= si470x_fops_open,
+	.release	= si470x_fops_release,
+};
+
+
+
+/**************************************************************************
+ * Video4Linux Interface
+ **************************************************************************/
+
+/*
+ * si470x_vidioc_querycap - query device capabilities
+ */
+int si470x_vidioc_querycap(struct file *file, void *priv,
+		struct v4l2_capability *capability)
+{
+	struct si470x_device *radio = video_drvdata(file);
+
+	strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
+	strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
+	usb_make_path(radio->usbdev, capability->bus_info,
+			sizeof(capability->bus_info));
+	capability->version = DRIVER_KERNEL_VERSION;
+	capability->capabilities = V4L2_CAP_HW_FREQ_SEEK |
+		V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE;
+
+	return 0;
+}
+
+
+
+/**************************************************************************
+ * USB Interface
+ **************************************************************************/
+
+/*
+ * si470x_usb_driver_probe - probe for the device
+ */
+static int si470x_usb_driver_probe(struct usb_interface *intf,
+		const struct usb_device_id *id)
+{
+	struct si470x_device *radio;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	int i, int_end_size, retval = 0;
+	unsigned char version_warning = 0;
+
+	/* private data allocation and initialization */
+	radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL);
+	if (!radio) {
+		retval = -ENOMEM;
+		goto err_initial;
+	}
+	radio->users = 0;
+	radio->disconnected = 0;
+	radio->usbdev = interface_to_usbdev(intf);
+	radio->intf = intf;
+	mutex_init(&radio->disconnect_lock);
+	mutex_init(&radio->lock);
+
+	iface_desc = intf->cur_altsetting;
+
+	/* Set up interrupt endpoint information. */
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+		if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==
+		 USB_DIR_IN) && ((endpoint->bmAttributes &
+		 USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT))
+			radio->int_in_endpoint = endpoint;
+	}
+	if (!radio->int_in_endpoint) {
+		dev_info(&intf->dev, "could not find interrupt in endpoint\n");
+		retval = -EIO;
+		goto err_radio;
+	}
+
+	int_end_size = le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize);
+
+	radio->int_in_buffer = kmalloc(int_end_size, GFP_KERNEL);
+	if (!radio->int_in_buffer) {
+		dev_info(&intf->dev, "could not allocate int_in_buffer");
+		retval = -ENOMEM;
+		goto err_radio;
+	}
+
+	radio->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!radio->int_in_urb) {
+		dev_info(&intf->dev, "could not allocate int_in_urb");
+		retval = -ENOMEM;
+		goto err_intbuffer;
+	}
+
+	/* video device allocation and initialization */
+	radio->videodev = video_device_alloc();
+	if (!radio->videodev) {
+		retval = -ENOMEM;
+		goto err_intbuffer;
+	}
+	memcpy(radio->videodev, &si470x_viddev_template,
+			sizeof(si470x_viddev_template));
+	video_set_drvdata(radio->videodev, radio);
+
+	/* get device and chip versions */
+	if (si470x_get_all_registers(radio) < 0) {
+		retval = -EIO;
+		goto err_video;
+	}
+	dev_info(&intf->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n",
+			radio->registers[DEVICEID], radio->registers[CHIPID]);
+	if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) < RADIO_FW_VERSION) {
+		dev_warn(&intf->dev,
+			"This driver is known to work with "
+			"firmware version %hu,\n", RADIO_FW_VERSION);
+		dev_warn(&intf->dev,
+			"but the device has firmware version %hu.\n",
+			radio->registers[CHIPID] & CHIPID_FIRMWARE);
+		version_warning = 1;
+	}
+
+	/* get software and hardware versions */
+	if (si470x_get_scratch_page_versions(radio) < 0) {
+		retval = -EIO;
+		goto err_video;
+	}
+	dev_info(&intf->dev, "software version %d, hardware version %d\n",
+			radio->software_version, radio->hardware_version);
+	if (radio->software_version < RADIO_SW_VERSION) {
+		dev_warn(&intf->dev,
+			"This driver is known to work with "
+			"software version %hu,\n", RADIO_SW_VERSION);
+		dev_warn(&intf->dev,
+			"but the device has software version %hu.\n",
+			radio->software_version);
+		version_warning = 1;
+	}
+	if (radio->hardware_version < RADIO_HW_VERSION) {
+		dev_warn(&intf->dev,
+			"This driver is known to work with "
+			"hardware version %hu,\n", RADIO_HW_VERSION);
+		dev_warn(&intf->dev,
+			"but the device has hardware version %hu.\n",
+			radio->hardware_version);
+		version_warning = 1;
+	}
+
+	/* give out version warning */
+	if (version_warning == 1) {
+		dev_warn(&intf->dev,
+			"If you have some trouble using this driver,\n");
+		dev_warn(&intf->dev,
+			"please report to V4L ML at "
+			"linux-media@vger.kernel.org\n");
+	}
+
+	/* set initial frequency */
+	si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
+
+	/* set led to connect state */
+	si470x_set_led_state(radio, BLINK_GREEN_LED);
+
+	/* rds buffer allocation */
+	radio->buf_size = rds_buf * 3;
+	radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
+	if (!radio->buffer) {
+		retval = -EIO;
+		goto err_video;
+	}
+
+	/* rds buffer configuration */
+	radio->wr_index = 0;
+	radio->rd_index = 0;
+	init_waitqueue_head(&radio->read_queue);
+
+	/* register video device */
+	retval = video_register_device(radio->videodev, VFL_TYPE_RADIO,
+			radio_nr);
+	if (retval) {
+		dev_warn(&intf->dev, "Could not register video device\n");
+		goto err_all;
+	}
+	usb_set_intfdata(intf, radio);
+
+	return 0;
+err_all:
+	kfree(radio->buffer);
+err_video:
+	video_device_release(radio->videodev);
+err_intbuffer:
+	kfree(radio->int_in_buffer);
+err_radio:
+	kfree(radio);
+err_initial:
+	return retval;
+}
+
+
+/*
+ * si470x_usb_driver_suspend - suspend the device
+ */
+static int si470x_usb_driver_suspend(struct usb_interface *intf,
+		pm_message_t message)
+{
+	dev_info(&intf->dev, "suspending now...\n");
+
+	return 0;
+}
+
+
+/*
+ * si470x_usb_driver_resume - resume the device
+ */
+static int si470x_usb_driver_resume(struct usb_interface *intf)
+{
+	dev_info(&intf->dev, "resuming now...\n");
+
+	return 0;
+}
+
+
+/*
+ * si470x_usb_driver_disconnect - disconnect the device
+ */
+static void si470x_usb_driver_disconnect(struct usb_interface *intf)
+{
+	struct si470x_device *radio = usb_get_intfdata(intf);
+
+	mutex_lock(&radio->disconnect_lock);
+	radio->disconnected = 1;
+	usb_set_intfdata(intf, NULL);
+	if (radio->users == 0) {
+		/* set led to disconnect state */
+		si470x_set_led_state(radio, BLINK_ORANGE_LED);
+
+		/* Free data structures. */
+		usb_free_urb(radio->int_in_urb);
+
+		kfree(radio->int_in_buffer);
+		video_unregister_device(radio->videodev);
+		kfree(radio->buffer);
+		kfree(radio);
+	}
+	mutex_unlock(&radio->disconnect_lock);
+}
+
+
+/*
+ * si470x_usb_driver - usb driver interface
+ */
+static struct usb_driver si470x_usb_driver = {
+	.name			= DRIVER_NAME,
+	.probe			= si470x_usb_driver_probe,
+	.disconnect		= si470x_usb_driver_disconnect,
+	.suspend		= si470x_usb_driver_suspend,
+	.resume			= si470x_usb_driver_resume,
+	.id_table		= si470x_usb_driver_id_table,
+	.supports_autosuspend	= 1,
+};
+
+
+
+/**************************************************************************
+ * Module Interface
+ **************************************************************************/
+
+/*
+ * si470x_module_init - module init
+ */
+static int __init si470x_module_init(void)
+{
+	printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n");
+	return usb_register(&si470x_usb_driver);
+}
+
+
+/*
+ * si470x_module_exit - module exit
+ */
+static void __exit si470x_module_exit(void)
+{
+	usb_deregister(&si470x_usb_driver);
+}
+
+
+module_init(si470x_module_init);
+module_exit(si470x_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);

+ 225 - 0
drivers/media/radio/si470x/radio-si470x.h

@@ -0,0 +1,225 @@
+/*
+ *  drivers/media/radio/si470x/radio-si470x.h
+ *
+ *  Driver for radios with Silicon Labs Si470x FM Radio Receivers
+ *
+ *  Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net>
+ *
+ * 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
+ */
+
+
+/* driver definitions */
+#define DRIVER_NAME "radio-si470x"
+
+
+/* kernel includes */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/input.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+#include <linux/mutex.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/rds.h>
+#include <asm/unaligned.h>
+
+
+
+/**************************************************************************
+ * Register Definitions
+ **************************************************************************/
+#define RADIO_REGISTER_SIZE	2	/* 16 register bit width */
+#define RADIO_REGISTER_NUM	16	/* DEVICEID   ... RDSD */
+#define RDS_REGISTER_NUM	6	/* STATUSRSSI ... RDSD */
+
+#define DEVICEID		0	/* Device ID */
+#define DEVICEID_PN		0xf000	/* bits 15..12: Part Number */
+#define DEVICEID_MFGID		0x0fff	/* bits 11..00: Manufacturer ID */
+
+#define CHIPID			1	/* Chip ID */
+#define CHIPID_REV		0xfc00	/* bits 15..10: Chip Version */
+#define CHIPID_DEV		0x0200	/* bits 09..09: Device */
+#define CHIPID_FIRMWARE		0x01ff	/* bits 08..00: Firmware Version */
+
+#define POWERCFG		2	/* Power Configuration */
+#define POWERCFG_DSMUTE		0x8000	/* bits 15..15: Softmute Disable */
+#define POWERCFG_DMUTE		0x4000	/* bits 14..14: Mute Disable */
+#define POWERCFG_MONO		0x2000	/* bits 13..13: Mono Select */
+#define POWERCFG_RDSM		0x0800	/* bits 11..11: RDS Mode (Si4701 only) */
+#define POWERCFG_SKMODE		0x0400	/* bits 10..10: Seek Mode */
+#define POWERCFG_SEEKUP		0x0200	/* bits 09..09: Seek Direction */
+#define POWERCFG_SEEK		0x0100	/* bits 08..08: Seek */
+#define POWERCFG_DISABLE	0x0040	/* bits 06..06: Powerup Disable */
+#define POWERCFG_ENABLE		0x0001	/* bits 00..00: Powerup Enable */
+
+#define CHANNEL			3	/* Channel */
+#define CHANNEL_TUNE		0x8000	/* bits 15..15: Tune */
+#define CHANNEL_CHAN		0x03ff	/* bits 09..00: Channel Select */
+
+#define SYSCONFIG1		4	/* System Configuration 1 */
+#define SYSCONFIG1_RDSIEN	0x8000	/* bits 15..15: RDS Interrupt Enable (Si4701 only) */
+#define SYSCONFIG1_STCIEN	0x4000	/* bits 14..14: Seek/Tune Complete Interrupt Enable */
+#define SYSCONFIG1_RDS		0x1000	/* bits 12..12: RDS Enable (Si4701 only) */
+#define SYSCONFIG1_DE		0x0800	/* bits 11..11: De-emphasis (0=75us 1=50us) */
+#define SYSCONFIG1_AGCD		0x0400	/* bits 10..10: AGC Disable */
+#define SYSCONFIG1_BLNDADJ	0x00c0	/* bits 07..06: Stereo/Mono Blend Level Adjustment */
+#define SYSCONFIG1_GPIO3	0x0030	/* bits 05..04: General Purpose I/O 3 */
+#define SYSCONFIG1_GPIO2	0x000c	/* bits 03..02: General Purpose I/O 2 */
+#define SYSCONFIG1_GPIO1	0x0003	/* bits 01..00: General Purpose I/O 1 */
+
+#define SYSCONFIG2		5	/* System Configuration 2 */
+#define SYSCONFIG2_SEEKTH	0xff00	/* bits 15..08: RSSI Seek Threshold */
+#define SYSCONFIG2_BAND		0x0080	/* bits 07..06: Band Select */
+#define SYSCONFIG2_SPACE	0x0030	/* bits 05..04: Channel Spacing */
+#define SYSCONFIG2_VOLUME	0x000f	/* bits 03..00: Volume */
+
+#define SYSCONFIG3		6	/* System Configuration 3 */
+#define SYSCONFIG3_SMUTER	0xc000	/* bits 15..14: Softmute Attack/Recover Rate */
+#define SYSCONFIG3_SMUTEA	0x3000	/* bits 13..12: Softmute Attenuation */
+#define SYSCONFIG3_SKSNR	0x00f0	/* bits 07..04: Seek SNR Threshold */
+#define SYSCONFIG3_SKCNT	0x000f	/* bits 03..00: Seek FM Impulse Detection Threshold */
+
+#define TEST1			7	/* Test 1 */
+#define TEST1_AHIZEN		0x4000	/* bits 14..14: Audio High-Z Enable */
+
+#define TEST2			8	/* Test 2 */
+/* TEST2 only contains reserved bits */
+
+#define BOOTCONFIG		9	/* Boot Configuration */
+/* BOOTCONFIG only contains reserved bits */
+
+#define STATUSRSSI		10	/* Status RSSI */
+#define STATUSRSSI_RDSR		0x8000	/* bits 15..15: RDS Ready (Si4701 only) */
+#define STATUSRSSI_STC		0x4000	/* bits 14..14: Seek/Tune Complete */
+#define STATUSRSSI_SF		0x2000	/* bits 13..13: Seek Fail/Band Limit */
+#define STATUSRSSI_AFCRL	0x1000	/* bits 12..12: AFC Rail */
+#define STATUSRSSI_RDSS		0x0800	/* bits 11..11: RDS Synchronized (Si4701 only) */
+#define STATUSRSSI_BLERA	0x0600	/* bits 10..09: RDS Block A Errors (Si4701 only) */
+#define STATUSRSSI_ST		0x0100	/* bits 08..08: Stereo Indicator */
+#define STATUSRSSI_RSSI		0x00ff	/* bits 07..00: RSSI (Received Signal Strength Indicator) */
+
+#define READCHAN		11	/* Read Channel */
+#define READCHAN_BLERB		0xc000	/* bits 15..14: RDS Block D Errors (Si4701 only) */
+#define READCHAN_BLERC		0x3000	/* bits 13..12: RDS Block C Errors (Si4701 only) */
+#define READCHAN_BLERD		0x0c00	/* bits 11..10: RDS Block B Errors (Si4701 only) */
+#define READCHAN_READCHAN	0x03ff	/* bits 09..00: Read Channel */
+
+#define RDSA			12	/* RDSA */
+#define RDSA_RDSA		0xffff	/* bits 15..00: RDS Block A Data (Si4701 only) */
+
+#define RDSB			13	/* RDSB */
+#define RDSB_RDSB		0xffff	/* bits 15..00: RDS Block B Data (Si4701 only) */
+
+#define RDSC			14	/* RDSC */
+#define RDSC_RDSC		0xffff	/* bits 15..00: RDS Block C Data (Si4701 only) */
+
+#define RDSD			15	/* RDSD */
+#define RDSD_RDSD		0xffff	/* bits 15..00: RDS Block D Data (Si4701 only) */
+
+
+
+/**************************************************************************
+ * General Driver Definitions
+ **************************************************************************/
+
+/*
+ * si470x_device - private data
+ */
+struct si470x_device {
+	struct video_device *videodev;
+
+	/* driver management */
+	unsigned int users;
+
+	/* Silabs internal registers (0..15) */
+	unsigned short registers[RADIO_REGISTER_NUM];
+
+	/* RDS receive buffer */
+	wait_queue_head_t read_queue;
+	struct mutex lock;		/* buffer locking */
+	unsigned char *buffer;		/* size is always multiple of three */
+	unsigned int buf_size;
+	unsigned int rd_index;
+	unsigned int wr_index;
+
+#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE)
+	/* reference to USB and video device */
+	struct usb_device *usbdev;
+	struct usb_interface *intf;
+
+	/* Interrupt endpoint handling */
+	char *int_in_buffer;
+	struct usb_endpoint_descriptor *int_in_endpoint;
+	struct urb *int_in_urb;
+	int int_in_running;
+
+	/* scratch page */
+	unsigned char software_version;
+	unsigned char hardware_version;
+
+	/* driver management */
+	unsigned char disconnected;
+	struct mutex disconnect_lock;
+#endif
+
+#if defined(CONFIG_I2C_SI470X) || defined(CONFIG_I2C_SI470X_MODULE)
+	struct i2c_client *client;
+#endif
+};
+
+
+
+/**************************************************************************
+ * Firmware Versions
+ **************************************************************************/
+
+#define RADIO_FW_VERSION	15
+
+
+
+/**************************************************************************
+ * Frequency Multiplicator
+ **************************************************************************/
+
+/*
+ * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW,
+ * 62.5 kHz otherwise.
+ * The tuner is able to have a channel spacing of 50, 100 or 200 kHz.
+ * tuner->capability is therefore set to V4L2_TUNER_CAP_LOW
+ * The FREQ_MUL is then: 1 MHz / 62.5 Hz = 16000
+ */
+#define FREQ_MUL (1000000 / 62.5)
+
+
+
+/**************************************************************************
+ * Common Functions
+ **************************************************************************/
+extern const struct v4l2_file_operations si470x_fops;
+extern struct video_device si470x_viddev_template;
+int si470x_get_register(struct si470x_device *radio, int regnr);
+int si470x_set_register(struct si470x_device *radio, int regnr);
+int si470x_disconnect_check(struct si470x_device *radio);
+int si470x_set_freq(struct si470x_device *radio, unsigned int freq);
+int si470x_start(struct si470x_device *radio);
+int si470x_stop(struct si470x_device *radio);
+int si470x_rds_on(struct si470x_device *radio);
+int si470x_vidioc_querycap(struct file *file, void *priv,
+		struct v4l2_capability *capability);

+ 2060 - 0
drivers/media/radio/si4713-i2c.c

@@ -0,0 +1,2060 @@
+/*
+ * drivers/media/radio/si4713-i2c.c
+ *
+ * Silicon Labs Si4713 FM Radio Transmitter I2C commands.
+ *
+ * Copyright (c) 2009 Nokia Corporation
+ * Contact: Eduardo Valentin <eduardo.valentin@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/mutex.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-common.h>
+
+#include "si4713-i2c.h"
+
+/* module parameters */
+static int debug;
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug level (0 - 2)");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Eduardo Valentin <eduardo.valentin@nokia.com>");
+MODULE_DESCRIPTION("I2C driver for Si4713 FM Radio Transmitter");
+MODULE_VERSION("0.0.1");
+
+#define DEFAULT_RDS_PI			0x00
+#define DEFAULT_RDS_PTY			0x00
+#define DEFAULT_RDS_PS_NAME		""
+#define DEFAULT_RDS_RADIO_TEXT		DEFAULT_RDS_PS_NAME
+#define DEFAULT_RDS_DEVIATION		0x00C8
+#define DEFAULT_RDS_PS_REPEAT_COUNT	0x0003
+#define DEFAULT_LIMITER_RTIME		0x1392
+#define DEFAULT_LIMITER_DEV		0x102CA
+#define DEFAULT_PILOT_FREQUENCY 	0x4A38
+#define DEFAULT_PILOT_DEVIATION		0x1A5E
+#define DEFAULT_ACOMP_ATIME		0x0000
+#define DEFAULT_ACOMP_RTIME		0xF4240L
+#define DEFAULT_ACOMP_GAIN		0x0F
+#define DEFAULT_ACOMP_THRESHOLD 	(-0x28)
+#define DEFAULT_MUTE			0x01
+#define DEFAULT_POWER_LEVEL		88
+#define DEFAULT_FREQUENCY		8800
+#define DEFAULT_PREEMPHASIS		FMPE_EU
+#define DEFAULT_TUNE_RNL		0xFF
+
+#define to_si4713_device(sd)	container_of(sd, struct si4713_device, sd)
+
+/* frequency domain transformation (using times 10 to avoid floats) */
+#define FREQDEV_UNIT	100000
+#define FREQV4L2_MULTI	625
+#define si4713_to_v4l2(f)	((f * FREQDEV_UNIT) / FREQV4L2_MULTI)
+#define v4l2_to_si4713(f)	((f * FREQV4L2_MULTI) / FREQDEV_UNIT)
+#define FREQ_RANGE_LOW			7600
+#define FREQ_RANGE_HIGH			10800
+
+#define MAX_ARGS 7
+
+#define RDS_BLOCK			8
+#define RDS_BLOCK_CLEAR			0x03
+#define RDS_BLOCK_LOAD			0x04
+#define RDS_RADIOTEXT_2A		0x20
+#define RDS_RADIOTEXT_BLK_SIZE		4
+#define RDS_RADIOTEXT_INDEX_MAX		0x0F
+#define RDS_CARRIAGE_RETURN		0x0D
+
+#define rds_ps_nblocks(len)	((len / RDS_BLOCK) + (len % RDS_BLOCK ? 1 : 0))
+
+#define get_status_bit(p, b, m)	(((p) & (m)) >> (b))
+#define set_bits(p, v, b, m)	(((p) & ~(m)) | ((v) << (b)))
+
+#define ATTACK_TIME_UNIT	500
+
+#define POWER_OFF			0x00
+#define POWER_ON			0x01
+
+#define msb(x)                  ((u8)((u16) x >> 8))
+#define lsb(x)                  ((u8)((u16) x &  0x00FF))
+#define compose_u16(msb, lsb)	(((u16)msb << 8) | lsb)
+#define check_command_failed(status)	(!(status & SI4713_CTS) || \
+					(status & SI4713_ERR))
+/* mute definition */
+#define set_mute(p)	((p & 1) | ((p & 1) << 1));
+#define get_mute(p)	(p & 0x01)
+
+#ifdef DEBUG
+#define DBG_BUFFER(device, message, buffer, size)			\
+	{								\
+		int i;							\
+		char str[(size)*5];					\
+		for (i = 0; i < size; i++)				\
+			sprintf(str + i * 5, " 0x%02x", buffer[i]);	\
+		v4l2_dbg(2, debug, device, "%s:%s\n", message, str);	\
+	}
+#else
+#define DBG_BUFFER(device, message, buffer, size)
+#endif
+
+/*
+ * Values for limiter release time (sorted by second column)
+ *	device	release
+ *	value	time (us)
+ */
+static long limiter_times[] = {
+	2000,	250,
+	1000,	500,
+	510,	1000,
+	255,	2000,
+	170,	3000,
+	127,	4020,
+	102,	5010,
+	85,	6020,
+	73,	7010,
+	64,	7990,
+	57,	8970,
+	51,	10030,
+	25,	20470,
+	17,	30110,
+	13,	39380,
+	10,	51190,
+	8,	63690,
+	7,	73140,
+	6,	85330,
+	5,	102390,
+};
+
+/*
+ * Values for audio compression release time (sorted by second column)
+ *	device	release
+ *	value	time (us)
+ */
+static unsigned long acomp_rtimes[] = {
+	0,	100000,
+	1,	200000,
+	2,	350000,
+	3,	525000,
+	4,	1000000,
+};
+
+/*
+ * Values for preemphasis (sorted by second column)
+ *	device	preemphasis
+ *	value	value (v4l2)
+ */
+static unsigned long preemphasis_values[] = {
+	FMPE_DISABLED,	V4L2_PREEMPHASIS_DISABLED,
+	FMPE_EU,	V4L2_PREEMPHASIS_50_uS,
+	FMPE_USA,	V4L2_PREEMPHASIS_75_uS,
+};
+
+static int usecs_to_dev(unsigned long usecs, unsigned long const array[],
+			int size)
+{
+	int i;
+	int rval = -EINVAL;
+
+	for (i = 0; i < size / 2; i++)
+		if (array[(i * 2) + 1] >= usecs) {
+			rval = array[i * 2];
+			break;
+		}
+
+	return rval;
+}
+
+static unsigned long dev_to_usecs(int value, unsigned long const array[],
+			int size)
+{
+	int i;
+	int rval = -EINVAL;
+
+	for (i = 0; i < size / 2; i++)
+		if (array[i * 2] == value) {
+			rval = array[(i * 2) + 1];
+			break;
+		}
+
+	return rval;
+}
+
+/* si4713_handler: IRQ handler, just complete work */
+static irqreturn_t si4713_handler(int irq, void *dev)
+{
+	struct si4713_device *sdev = dev;
+
+	v4l2_dbg(2, debug, &sdev->sd,
+			"%s: sending signal to completion work.\n", __func__);
+	complete(&sdev->work);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * si4713_send_command - sends a command to si4713 and waits its response
+ * @sdev: si4713_device structure for the device we are communicating
+ * @command: command id
+ * @args: command arguments we are sending (up to 7)
+ * @argn: actual size of @args
+ * @response: buffer to place the expected response from the device (up to 15)
+ * @respn: actual size of @response
+ * @usecs: amount of time to wait before reading the response (in usecs)
+ */
+static int si4713_send_command(struct si4713_device *sdev, const u8 command,
+				const u8 args[], const int argn,
+				u8 response[], const int respn, const int usecs)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&sdev->sd);
+	u8 data1[MAX_ARGS + 1];
+	int err;
+
+	if (!client->adapter)
+		return -ENODEV;
+
+	/* First send the command and its arguments */
+	data1[0] = command;
+	memcpy(data1 + 1, args, argn);
+	DBG_BUFFER(&sdev->sd, "Parameters", data1, argn + 1);
+
+	err = i2c_master_send(client, data1, argn + 1);
+	if (err != argn + 1) {
+		v4l2_err(&sdev->sd, "Error while sending command 0x%02x\n",
+			command);
+		return (err > 0) ? -EIO : err;
+	}
+
+	/* Wait response from interrupt */
+	if (!wait_for_completion_timeout(&sdev->work,
+				usecs_to_jiffies(usecs) + 1))
+		v4l2_warn(&sdev->sd,
+				"(%s) Device took too much time to answer.\n",
+				__func__);
+
+	/* Then get the response */
+	err = i2c_master_recv(client, response, respn);
+	if (err != respn) {
+		v4l2_err(&sdev->sd,
+			"Error while reading response for command 0x%02x\n",
+			command);
+		return (err > 0) ? -EIO : err;
+	}
+
+	DBG_BUFFER(&sdev->sd, "Response", response, respn);
+	if (check_command_failed(response[0]))
+		return -EBUSY;
+
+	return 0;
+}
+
+/*
+ * si4713_read_property - reads a si4713 property
+ * @sdev: si4713_device structure for the device we are communicating
+ * @prop: property identification number
+ * @pv: property value to be returned on success
+ */
+static int si4713_read_property(struct si4713_device *sdev, u16 prop, u32 *pv)
+{
+	int err;
+	u8 val[SI4713_GET_PROP_NRESP];
+	/*
+	 * 	.First byte = 0
+	 * 	.Second byte = property's MSB
+	 * 	.Third byte = property's LSB
+	 */
+	const u8 args[SI4713_GET_PROP_NARGS] = {
+		0x00,
+		msb(prop),
+		lsb(prop),
+	};
+
+	err = si4713_send_command(sdev, SI4713_CMD_GET_PROPERTY,
+				  args, ARRAY_SIZE(args), val,
+				  ARRAY_SIZE(val), DEFAULT_TIMEOUT);
+
+	if (err < 0)
+		return err;
+
+	*pv = compose_u16(val[2], val[3]);
+
+	v4l2_dbg(1, debug, &sdev->sd,
+			"%s: property=0x%02x value=0x%02x status=0x%02x\n",
+			__func__, prop, *pv, val[0]);
+
+	return err;
+}
+
+/*
+ * si4713_write_property - modifies a si4713 property
+ * @sdev: si4713_device structure for the device we are communicating
+ * @prop: property identification number
+ * @val: new value for that property
+ */
+static int si4713_write_property(struct si4713_device *sdev, u16 prop, u16 val)
+{
+	int rval;
+	u8 resp[SI4713_SET_PROP_NRESP];
+	/*
+	 * 	.First byte = 0
+	 * 	.Second byte = property's MSB
+	 * 	.Third byte = property's LSB
+	 * 	.Fourth byte = value's MSB
+	 * 	.Fifth byte = value's LSB
+	 */
+	const u8 args[SI4713_SET_PROP_NARGS] = {
+		0x00,
+		msb(prop),
+		lsb(prop),
+		msb(val),
+		lsb(val),
+	};
+
+	rval = si4713_send_command(sdev, SI4713_CMD_SET_PROPERTY,
+					args, ARRAY_SIZE(args),
+					resp, ARRAY_SIZE(resp),
+					DEFAULT_TIMEOUT);
+
+	if (rval < 0)
+		return rval;
+
+	v4l2_dbg(1, debug, &sdev->sd,
+			"%s: property=0x%02x value=0x%02x status=0x%02x\n",
+			__func__, prop, val, resp[0]);
+
+	/*
+	 * As there is no command response for SET_PROPERTY,
+	 * wait Tcomp time to finish before proceed, in order
+	 * to have property properly set.
+	 */
+	msleep(TIMEOUT_SET_PROPERTY);
+
+	return rval;
+}
+
+/*
+ * si4713_powerup - Powers the device up
+ * @sdev: si4713_device structure for the device we are communicating
+ */
+static int si4713_powerup(struct si4713_device *sdev)
+{
+	int err;
+	u8 resp[SI4713_PWUP_NRESP];
+	/*
+	 * 	.First byte = Enabled interrupts and boot function
+	 * 	.Second byte = Input operation mode
+	 */
+	const u8 args[SI4713_PWUP_NARGS] = {
+		SI4713_PWUP_CTSIEN | SI4713_PWUP_GPO2OEN | SI4713_PWUP_FUNC_TX,
+		SI4713_PWUP_OPMOD_ANALOG,
+	};
+
+	if (sdev->power_state)
+		return 0;
+
+	sdev->platform_data->set_power(1);
+	err = si4713_send_command(sdev, SI4713_CMD_POWER_UP,
+					args, ARRAY_SIZE(args),
+					resp, ARRAY_SIZE(resp),
+					TIMEOUT_POWER_UP);
+
+	if (!err) {
+		v4l2_dbg(1, debug, &sdev->sd, "Powerup response: 0x%02x\n",
+				resp[0]);
+		v4l2_dbg(1, debug, &sdev->sd, "Device in power up mode\n");
+		sdev->power_state = POWER_ON;
+
+		err = si4713_write_property(sdev, SI4713_GPO_IEN,
+						SI4713_STC_INT | SI4713_CTS);
+	} else {
+		sdev->platform_data->set_power(0);
+	}
+
+	return err;
+}
+
+/*
+ * si4713_powerdown - Powers the device down
+ * @sdev: si4713_device structure for the device we are communicating
+ */
+static int si4713_powerdown(struct si4713_device *sdev)
+{
+	int err;
+	u8 resp[SI4713_PWDN_NRESP];
+
+	if (!sdev->power_state)
+		return 0;
+
+	err = si4713_send_command(sdev, SI4713_CMD_POWER_DOWN,
+					NULL, 0,
+					resp, ARRAY_SIZE(resp),
+					DEFAULT_TIMEOUT);
+
+	if (!err) {
+		v4l2_dbg(1, debug, &sdev->sd, "Power down response: 0x%02x\n",
+				resp[0]);
+		v4l2_dbg(1, debug, &sdev->sd, "Device in reset mode\n");
+		sdev->platform_data->set_power(0);
+		sdev->power_state = POWER_OFF;
+	}
+
+	return err;
+}
+
+/*
+ * si4713_checkrev - Checks if we are treating a device with the correct rev.
+ * @sdev: si4713_device structure for the device we are communicating
+ */
+static int si4713_checkrev(struct si4713_device *sdev)
+{
+	struct i2c_client *client = v4l2_get_subdevdata(&sdev->sd);
+	int rval;
+	u8 resp[SI4713_GETREV_NRESP];
+
+	mutex_lock(&sdev->mutex);
+
+	rval = si4713_send_command(sdev, SI4713_CMD_GET_REV,
+					NULL, 0,
+					resp, ARRAY_SIZE(resp),
+					DEFAULT_TIMEOUT);
+
+	if (rval < 0)
+		goto unlock;
+
+	if (resp[1] == SI4713_PRODUCT_NUMBER) {
+		v4l2_info(&sdev->sd, "chip found @ 0x%02x (%s)\n",
+				client->addr << 1, client->adapter->name);
+	} else {
+		v4l2_err(&sdev->sd, "Invalid product number\n");
+		rval = -EINVAL;
+	}
+
+unlock:
+	mutex_unlock(&sdev->mutex);
+	return rval;
+}
+
+/*
+ * si4713_wait_stc - Waits STC interrupt and clears status bits. Usefull
+ *		     for TX_TUNE_POWER, TX_TUNE_FREQ and TX_TUNE_MEAS
+ * @sdev: si4713_device structure for the device we are communicating
+ * @usecs: timeout to wait for STC interrupt signal
+ */
+static int si4713_wait_stc(struct si4713_device *sdev, const int usecs)
+{
+	int err;
+	u8 resp[SI4713_GET_STATUS_NRESP];
+
+	/* Wait response from STC interrupt */
+	if (!wait_for_completion_timeout(&sdev->work,
+			usecs_to_jiffies(usecs) + 1))
+		v4l2_warn(&sdev->sd,
+			"%s: device took too much time to answer (%d usec).\n",
+				__func__, usecs);
+
+	/* Clear status bits */
+	err = si4713_send_command(sdev, SI4713_CMD_GET_INT_STATUS,
+					NULL, 0,
+					resp, ARRAY_SIZE(resp),
+					DEFAULT_TIMEOUT);
+
+	if (err < 0)
+		goto exit;
+
+	v4l2_dbg(1, debug, &sdev->sd,
+			"%s: status bits: 0x%02x\n", __func__, resp[0]);
+
+	if (!(resp[0] & SI4713_STC_INT))
+		err = -EIO;
+
+exit:
+	return err;
+}
+
+/*
+ * si4713_tx_tune_freq - Sets the state of the RF carrier and sets the tuning
+ * 			frequency between 76 and 108 MHz in 10 kHz units and
+ * 			steps of 50 kHz.
+ * @sdev: si4713_device structure for the device we are communicating
+ * @frequency: desired frequency (76 - 108 MHz, unit 10 KHz, step 50 kHz)
+ */
+static int si4713_tx_tune_freq(struct si4713_device *sdev, u16 frequency)
+{
+	int err;
+	u8 val[SI4713_TXFREQ_NRESP];
+	/*
+	 * 	.First byte = 0
+	 * 	.Second byte = frequency's MSB
+	 * 	.Third byte = frequency's LSB
+	 */
+	const u8 args[SI4713_TXFREQ_NARGS] = {
+		0x00,
+		msb(frequency),
+		lsb(frequency),
+	};
+
+	err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_FREQ,
+				  args, ARRAY_SIZE(args), val,
+				  ARRAY_SIZE(val), DEFAULT_TIMEOUT);
+
+	if (err < 0)
+		return err;
+
+	v4l2_dbg(1, debug, &sdev->sd,
+			"%s: frequency=0x%02x status=0x%02x\n", __func__,
+			frequency, val[0]);
+
+	err = si4713_wait_stc(sdev, TIMEOUT_TX_TUNE);
+	if (err < 0)
+		return err;
+
+	return compose_u16(args[1], args[2]);
+}
+
+/*
+ * si4713_tx_tune_power - Sets the RF voltage level between 88 and 115 dBuV in
+ * 			1 dB units. A value of 0x00 indicates off. The command
+ * 			also sets the antenna tuning capacitance. A value of 0
+ * 			indicates autotuning, and a value of 1 - 191 indicates
+ * 			a manual override, which results in a tuning
+ * 			capacitance of 0.25 pF x @antcap.
+ * @sdev: si4713_device structure for the device we are communicating
+ * @power: tuning power (88 - 115 dBuV, unit/step 1 dB)
+ * @antcap: value of antenna tuning capacitor (0 - 191)
+ */
+static int si4713_tx_tune_power(struct si4713_device *sdev, u8 power,
+				u8 antcap)
+{
+	int err;
+	u8 val[SI4713_TXPWR_NRESP];
+	/*
+	 * 	.First byte = 0
+	 * 	.Second byte = 0
+	 * 	.Third byte = power
+	 * 	.Fourth byte = antcap
+	 */
+	const u8 args[SI4713_TXPWR_NARGS] = {
+		0x00,
+		0x00,
+		power,
+		antcap,
+	};
+
+	if (((power > 0) && (power < SI4713_MIN_POWER)) ||
+		power > SI4713_MAX_POWER || antcap > SI4713_MAX_ANTCAP)
+		return -EDOM;
+
+	err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_POWER,
+				  args, ARRAY_SIZE(args), val,
+				  ARRAY_SIZE(val), DEFAULT_TIMEOUT);
+
+	if (err < 0)
+		return err;
+
+	v4l2_dbg(1, debug, &sdev->sd,
+			"%s: power=0x%02x antcap=0x%02x status=0x%02x\n",
+			__func__, power, antcap, val[0]);
+
+	return si4713_wait_stc(sdev, TIMEOUT_TX_TUNE_POWER);
+}
+
+/*
+ * si4713_tx_tune_measure - Enters receive mode and measures the received noise
+ * 			level in units of dBuV on the selected frequency.
+ * 			The Frequency must be between 76 and 108 MHz in 10 kHz
+ * 			units and steps of 50 kHz. The command also sets the
+ * 			antenna	tuning capacitance. A value of 0 means
+ * 			autotuning, and a value of 1 to 191 indicates manual
+ * 			override.
+ * @sdev: si4713_device structure for the device we are communicating
+ * @frequency: desired frequency (76 - 108 MHz, unit 10 KHz, step 50 kHz)
+ * @antcap: value of antenna tuning capacitor (0 - 191)
+ */
+static int si4713_tx_tune_measure(struct si4713_device *sdev, u16 frequency,
+					u8 antcap)
+{
+	int err;
+	u8 val[SI4713_TXMEA_NRESP];
+	/*
+	 * 	.First byte = 0
+	 * 	.Second byte = frequency's MSB
+	 * 	.Third byte = frequency's LSB
+	 * 	.Fourth byte = antcap
+	 */
+	const u8 args[SI4713_TXMEA_NARGS] = {
+		0x00,
+		msb(frequency),
+		lsb(frequency),
+		antcap,
+	};
+
+	sdev->tune_rnl = DEFAULT_TUNE_RNL;
+
+	if (antcap > SI4713_MAX_ANTCAP)
+		return -EDOM;
+
+	err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_MEASURE,
+				  args, ARRAY_SIZE(args), val,
+				  ARRAY_SIZE(val), DEFAULT_TIMEOUT);
+
+	if (err < 0)
+		return err;
+
+	v4l2_dbg(1, debug, &sdev->sd,
+			"%s: frequency=0x%02x antcap=0x%02x status=0x%02x\n",
+			__func__, frequency, antcap, val[0]);
+
+	return si4713_wait_stc(sdev, TIMEOUT_TX_TUNE);
+}
+
+/*
+ * si4713_tx_tune_status- Returns the status of the tx_tune_freq, tx_tune_mea or
+ * 			tx_tune_power commands. This command return the current
+ * 			frequency, output voltage in dBuV, the antenna tunning
+ * 			capacitance value and the received noise level. The
+ * 			command also clears the stcint interrupt bit when the
+ * 			first bit of its arguments is high.
+ * @sdev: si4713_device structure for the device we are communicating
+ * @intack: 0x01 to clear the seek/tune complete interrupt status indicator.
+ * @frequency: returned frequency
+ * @power: returned power
+ * @antcap: returned antenna capacitance
+ * @noise: returned noise level
+ */
+static int si4713_tx_tune_status(struct si4713_device *sdev, u8 intack,
+					u16 *frequency,	u8 *power,
+					u8 *antcap, u8 *noise)
+{
+	int err;
+	u8 val[SI4713_TXSTATUS_NRESP];
+	/*
+	 * 	.First byte = intack bit
+	 */
+	const u8 args[SI4713_TXSTATUS_NARGS] = {
+		intack & SI4713_INTACK_MASK,
+	};
+
+	err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_STATUS,
+				  args, ARRAY_SIZE(args), val,
+				  ARRAY_SIZE(val), DEFAULT_TIMEOUT);
+
+	if (!err) {
+		v4l2_dbg(1, debug, &sdev->sd,
+			"%s: status=0x%02x\n", __func__, val[0]);
+		*frequency = compose_u16(val[2], val[3]);
+		sdev->frequency = *frequency;
+		*power = val[5];
+		*antcap = val[6];
+		*noise = val[7];
+		v4l2_dbg(1, debug, &sdev->sd, "%s: response: %d x 10 kHz "
+				"(power %d, antcap %d, rnl %d)\n", __func__,
+				*frequency, *power, *antcap, *noise);
+	}
+
+	return err;
+}
+
+/*
+ * si4713_tx_rds_buff - Loads the RDS group buffer FIFO or circular buffer.
+ * @sdev: si4713_device structure for the device we are communicating
+ * @mode: the buffer operation mode.
+ * @rdsb: RDS Block B
+ * @rdsc: RDS Block C
+ * @rdsd: RDS Block D
+ * @cbleft: returns the number of available circular buffer blocks minus the
+ *          number of used circular buffer blocks.
+ */
+static int si4713_tx_rds_buff(struct si4713_device *sdev, u8 mode, u16 rdsb,
+				u16 rdsc, u16 rdsd, s8 *cbleft)
+{
+	int err;
+	u8 val[SI4713_RDSBUFF_NRESP];
+
+	const u8 args[SI4713_RDSBUFF_NARGS] = {
+		mode & SI4713_RDSBUFF_MODE_MASK,
+		msb(rdsb),
+		lsb(rdsb),
+		msb(rdsc),
+		lsb(rdsc),
+		msb(rdsd),
+		lsb(rdsd),
+	};
+
+	err = si4713_send_command(sdev, SI4713_CMD_TX_RDS_BUFF,
+				  args, ARRAY_SIZE(args), val,
+				  ARRAY_SIZE(val), DEFAULT_TIMEOUT);
+
+	if (!err) {
+		v4l2_dbg(1, debug, &sdev->sd,
+			"%s: status=0x%02x\n", __func__, val[0]);
+		*cbleft = (s8)val[2] - val[3];
+		v4l2_dbg(1, debug, &sdev->sd, "%s: response: interrupts"
+				" 0x%02x cb avail: %d cb used %d fifo avail"
+				" %d fifo used %d\n", __func__, val[1],
+				val[2], val[3], val[4], val[5]);
+	}
+
+	return err;
+}
+
+/*
+ * si4713_tx_rds_ps - Loads the program service buffer.
+ * @sdev: si4713_device structure for the device we are communicating
+ * @psid: program service id to be loaded.
+ * @pschar: assumed 4 size char array to be loaded into the program service
+ */
+static int si4713_tx_rds_ps(struct si4713_device *sdev, u8 psid,
+				unsigned char *pschar)
+{
+	int err;
+	u8 val[SI4713_RDSPS_NRESP];
+
+	const u8 args[SI4713_RDSPS_NARGS] = {
+		psid & SI4713_RDSPS_PSID_MASK,
+		pschar[0],
+		pschar[1],
+		pschar[2],
+		pschar[3],
+	};
+
+	err = si4713_send_command(sdev, SI4713_CMD_TX_RDS_PS,
+				  args, ARRAY_SIZE(args), val,
+				  ARRAY_SIZE(val), DEFAULT_TIMEOUT);
+
+	if (err < 0)
+		return err;
+
+	v4l2_dbg(1, debug, &sdev->sd, "%s: status=0x%02x\n", __func__, val[0]);
+
+	return err;
+}
+
+static int si4713_set_power_state(struct si4713_device *sdev, u8 value)
+{
+	int rval;
+
+	mutex_lock(&sdev->mutex);
+
+	if (value)
+		rval = si4713_powerup(sdev);
+	else
+		rval = si4713_powerdown(sdev);
+
+	mutex_unlock(&sdev->mutex);
+	return rval;
+}
+
+static int si4713_set_mute(struct si4713_device *sdev, u16 mute)
+{
+	int rval = 0;
+
+	mute = set_mute(mute);
+
+	mutex_lock(&sdev->mutex);
+
+	if (sdev->power_state)
+		rval = si4713_write_property(sdev,
+				SI4713_TX_LINE_INPUT_MUTE, mute);
+
+	if (rval >= 0)
+		sdev->mute = get_mute(mute);
+
+	mutex_unlock(&sdev->mutex);
+
+	return rval;
+}
+
+static int si4713_set_rds_ps_name(struct si4713_device *sdev, char *ps_name)
+{
+	int rval = 0, i;
+	u8 len = 0;
+
+	/* We want to clear the whole thing */
+	if (!strlen(ps_name))
+		memset(ps_name, 0, MAX_RDS_PS_NAME + 1);
+
+	mutex_lock(&sdev->mutex);
+
+	if (sdev->power_state) {
+		/* Write the new ps name and clear the padding */
+		for (i = 0; i < MAX_RDS_PS_NAME; i += (RDS_BLOCK / 2)) {
+			rval = si4713_tx_rds_ps(sdev, (i / (RDS_BLOCK / 2)),
+						ps_name + i);
+			if (rval < 0)
+				goto unlock;
+		}
+
+		/* Setup the size to be sent */
+		if (strlen(ps_name))
+			len = strlen(ps_name) - 1;
+		else
+			len = 1;
+
+		rval = si4713_write_property(sdev,
+				SI4713_TX_RDS_PS_MESSAGE_COUNT,
+				rds_ps_nblocks(len));
+		if (rval < 0)
+			goto unlock;
+
+		rval = si4713_write_property(sdev,
+				SI4713_TX_RDS_PS_REPEAT_COUNT,
+				DEFAULT_RDS_PS_REPEAT_COUNT * 2);
+		if (rval < 0)
+			goto unlock;
+	}
+
+	strncpy(sdev->rds_info.ps_name, ps_name, MAX_RDS_PS_NAME);
+
+unlock:
+	mutex_unlock(&sdev->mutex);
+	return rval;
+}
+
+static int si4713_set_rds_radio_text(struct si4713_device *sdev, char *rt)
+{
+	int rval = 0, i;
+	u16 t_index = 0;
+	u8 b_index = 0, cr_inserted = 0;
+	s8 left;
+
+	mutex_lock(&sdev->mutex);
+
+	if (!sdev->power_state)
+		goto copy;
+
+	rval = si4713_tx_rds_buff(sdev, RDS_BLOCK_CLEAR, 0, 0, 0, &left);
+	if (rval < 0)
+		goto unlock;
+
+	if (!strlen(rt))
+		goto copy;
+
+	do {
+		/* RDS spec says that if the last block isn't used,
+		 * then apply a carriage return
+		 */
+		if (t_index < (RDS_RADIOTEXT_INDEX_MAX *
+			RDS_RADIOTEXT_BLK_SIZE)) {
+			for (i = 0; i < RDS_RADIOTEXT_BLK_SIZE; i++) {
+				if (!rt[t_index + i] || rt[t_index + i] ==
+					RDS_CARRIAGE_RETURN) {
+					rt[t_index + i] = RDS_CARRIAGE_RETURN;
+					cr_inserted = 1;
+					break;
+				}
+			}
+		}
+
+		rval = si4713_tx_rds_buff(sdev, RDS_BLOCK_LOAD,
+				compose_u16(RDS_RADIOTEXT_2A, b_index++),
+				compose_u16(rt[t_index], rt[t_index + 1]),
+				compose_u16(rt[t_index + 2], rt[t_index + 3]),
+				&left);
+		if (rval < 0)
+			goto unlock;
+
+		t_index += RDS_RADIOTEXT_BLK_SIZE;
+
+		if (cr_inserted)
+			break;
+	} while (left > 0);
+
+copy:
+	strncpy(sdev->rds_info.radio_text, rt, MAX_RDS_RADIO_TEXT);
+
+unlock:
+	mutex_unlock(&sdev->mutex);
+	return rval;
+}
+
+static int si4713_choose_econtrol_action(struct si4713_device *sdev, u32 id,
+		u32 **shadow, s32 *bit, s32 *mask, u16 *property, int *mul,
+		unsigned long **table, int *size)
+{
+	s32 rval = 0;
+
+	switch (id) {
+	/* FM_TX class controls */
+	case V4L2_CID_RDS_TX_PI:
+		*property = SI4713_TX_RDS_PI;
+		*mul = 1;
+		*shadow = &sdev->rds_info.pi;
+		break;
+	case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD:
+		*property = SI4713_TX_ACOMP_THRESHOLD;
+		*mul = 1;
+		*shadow = &sdev->acomp_info.threshold;
+		break;
+	case V4L2_CID_AUDIO_COMPRESSION_GAIN:
+		*property = SI4713_TX_ACOMP_GAIN;
+		*mul = 1;
+		*shadow = &sdev->acomp_info.gain;
+		break;
+	case V4L2_CID_PILOT_TONE_FREQUENCY:
+		*property = SI4713_TX_PILOT_FREQUENCY;
+		*mul = 1;
+		*shadow = &sdev->pilot_info.frequency;
+		break;
+	case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME:
+		*property = SI4713_TX_ACOMP_ATTACK_TIME;
+		*mul = ATTACK_TIME_UNIT;
+		*shadow = &sdev->acomp_info.attack_time;
+		break;
+	case V4L2_CID_PILOT_TONE_DEVIATION:
+		*property = SI4713_TX_PILOT_DEVIATION;
+		*mul = 10;
+		*shadow = &sdev->pilot_info.deviation;
+		break;
+	case V4L2_CID_AUDIO_LIMITER_DEVIATION:
+		*property = SI4713_TX_AUDIO_DEVIATION;
+		*mul = 10;
+		*shadow = &sdev->limiter_info.deviation;
+		break;
+	case V4L2_CID_RDS_TX_DEVIATION:
+		*property = SI4713_TX_RDS_DEVIATION;
+		*mul = 1;
+		*shadow = &sdev->rds_info.deviation;
+		break;
+
+	case V4L2_CID_RDS_TX_PTY:
+		*property = SI4713_TX_RDS_PS_MISC;
+		*bit = 5;
+		*mask = 0x1F << 5;
+		*shadow = &sdev->rds_info.pty;
+		break;
+	case V4L2_CID_AUDIO_LIMITER_ENABLED:
+		*property = SI4713_TX_ACOMP_ENABLE;
+		*bit = 1;
+		*mask = 1 << 1;
+		*shadow = &sdev->limiter_info.enabled;
+		break;
+	case V4L2_CID_AUDIO_COMPRESSION_ENABLED:
+		*property = SI4713_TX_ACOMP_ENABLE;
+		*bit = 0;
+		*mask = 1 << 0;
+		*shadow = &sdev->acomp_info.enabled;
+		break;
+	case V4L2_CID_PILOT_TONE_ENABLED:
+		*property = SI4713_TX_COMPONENT_ENABLE;
+		*bit = 0;
+		*mask = 1 << 0;
+		*shadow = &sdev->pilot_info.enabled;
+		break;
+
+	case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME:
+		*property = SI4713_TX_LIMITER_RELEASE_TIME;
+		*table = limiter_times;
+		*size = ARRAY_SIZE(limiter_times);
+		*shadow = &sdev->limiter_info.release_time;
+		break;
+	case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME:
+		*property = SI4713_TX_ACOMP_RELEASE_TIME;
+		*table = acomp_rtimes;
+		*size = ARRAY_SIZE(acomp_rtimes);
+		*shadow = &sdev->acomp_info.release_time;
+		break;
+	case V4L2_CID_TUNE_PREEMPHASIS:
+		*property = SI4713_TX_PREEMPHASIS;
+		*table = preemphasis_values;
+		*size = ARRAY_SIZE(preemphasis_values);
+		*shadow = &sdev->preemphasis;
+		break;
+
+	default:
+		rval = -EINVAL;
+	};
+
+	return rval;
+}
+
+static int si4713_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc);
+
+/* write string property */
+static int si4713_write_econtrol_string(struct si4713_device *sdev,
+				struct v4l2_ext_control *control)
+{
+	struct v4l2_queryctrl vqc;
+	int len;
+	s32 rval = 0;
+
+	vqc.id = control->id;
+	rval = si4713_queryctrl(&sdev->sd, &vqc);
+	if (rval < 0)
+		goto exit;
+
+	switch (control->id) {
+	case V4L2_CID_RDS_TX_PS_NAME: {
+		char ps_name[MAX_RDS_PS_NAME + 1];
+
+		len = control->size - 1;
+		if (len > MAX_RDS_PS_NAME) {
+			rval = -ERANGE;
+			goto exit;
+		}
+		rval = copy_from_user(ps_name, control->string, len);
+		if (rval < 0)
+			goto exit;
+		ps_name[len] = '\0';
+
+		if (strlen(ps_name) % vqc.step) {
+			rval = -ERANGE;
+			goto exit;
+		}
+
+		rval = si4713_set_rds_ps_name(sdev, ps_name);
+	}
+		break;
+
+	case V4L2_CID_RDS_TX_RADIO_TEXT: {
+		char radio_text[MAX_RDS_RADIO_TEXT + 1];
+
+		len = control->size - 1;
+		if (len > MAX_RDS_RADIO_TEXT) {
+			rval = -ERANGE;
+			goto exit;
+		}
+		rval = copy_from_user(radio_text, control->string, len);
+		if (rval < 0)
+			goto exit;
+		radio_text[len] = '\0';
+
+		if (strlen(radio_text) % vqc.step) {
+			rval = -ERANGE;
+			goto exit;
+		}
+
+		rval = si4713_set_rds_radio_text(sdev, radio_text);
+	}
+		break;
+
+	default:
+		rval = -EINVAL;
+		break;
+	};
+
+exit:
+	return rval;
+}
+
+static int validate_range(struct v4l2_subdev *sd,
+					struct v4l2_ext_control *control)
+{
+	struct v4l2_queryctrl vqc;
+	int rval;
+
+	vqc.id = control->id;
+	rval = si4713_queryctrl(sd, &vqc);
+	if (rval < 0)
+		goto exit;
+
+	if (control->value < vqc.minimum || control->value > vqc.maximum)
+		rval = -ERANGE;
+
+exit:
+	return rval;
+}
+
+/* properties which use tx_tune_power*/
+static int si4713_write_econtrol_tune(struct si4713_device *sdev,
+				struct v4l2_ext_control *control)
+{
+	s32 rval = 0;
+	u8 power, antcap;
+
+	rval = validate_range(&sdev->sd, control);
+	if (rval < 0)
+		goto exit;
+
+	mutex_lock(&sdev->mutex);
+
+	switch (control->id) {
+	case V4L2_CID_TUNE_POWER_LEVEL:
+		power = control->value;
+		antcap = sdev->antenna_capacitor;
+		break;
+	case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
+		power = sdev->power_level;
+		antcap = control->value;
+		break;
+	default:
+		rval = -EINVAL;
+		goto unlock;
+	};
+
+	if (sdev->power_state)
+		rval = si4713_tx_tune_power(sdev, power, antcap);
+
+	if (rval == 0) {
+		sdev->power_level = power;
+		sdev->antenna_capacitor = antcap;
+	}
+
+unlock:
+	mutex_unlock(&sdev->mutex);
+exit:
+	return rval;
+}
+
+static int si4713_write_econtrol_integers(struct si4713_device *sdev,
+					struct v4l2_ext_control *control)
+{
+	s32 rval;
+	u32 *shadow = NULL, val = 0;
+	s32 bit = 0, mask = 0;
+	u16 property = 0;
+	int mul = 0;
+	unsigned long *table = NULL;
+	int size = 0;
+
+	rval = validate_range(&sdev->sd, control);
+	if (rval < 0)
+		goto exit;
+
+	rval = si4713_choose_econtrol_action(sdev, control->id, &shadow, &bit,
+			&mask, &property, &mul, &table, &size);
+	if (rval < 0)
+		goto exit;
+
+	val = control->value;
+	if (mul) {
+		val = control->value / mul;
+	} else if (table) {
+		rval = usecs_to_dev(control->value, table, size);
+		if (rval < 0)
+			goto exit;
+		val = rval;
+		rval = 0;
+	}
+
+	mutex_lock(&sdev->mutex);
+
+	if (sdev->power_state) {
+		if (mask) {
+			rval = si4713_read_property(sdev, property, &val);
+			if (rval < 0)
+				goto unlock;
+			val = set_bits(val, control->value, bit, mask);
+		}
+
+		rval = si4713_write_property(sdev, property, val);
+		if (rval < 0)
+			goto unlock;
+		if (mask)
+			val = control->value;
+	}
+
+	if (mul) {
+		*shadow = val * mul;
+	} else if (table) {
+		rval = dev_to_usecs(val, table, size);
+		if (rval < 0)
+			goto unlock;
+		*shadow = rval;
+		rval = 0;
+	} else {
+		*shadow = val;
+	}
+
+unlock:
+	mutex_unlock(&sdev->mutex);
+exit:
+	return rval;
+}
+
+static int si4713_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f);
+static int si4713_s_modulator(struct v4l2_subdev *sd, struct v4l2_modulator *);
+/*
+ * si4713_setup - Sets the device up with current configuration.
+ * @sdev: si4713_device structure for the device we are communicating
+ */
+static int si4713_setup(struct si4713_device *sdev)
+{
+	struct v4l2_ext_control ctrl;
+	struct v4l2_frequency f;
+	struct v4l2_modulator vm;
+	struct si4713_device *tmp;
+	int rval = 0;
+
+	tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
+	if (!tmp)
+		return -ENOMEM;
+
+	/* Get a local copy to avoid race */
+	mutex_lock(&sdev->mutex);
+	memcpy(tmp, sdev, sizeof(*sdev));
+	mutex_unlock(&sdev->mutex);
+
+	ctrl.id = V4L2_CID_RDS_TX_PI;
+	ctrl.value = tmp->rds_info.pi;
+	rval |= si4713_write_econtrol_integers(sdev, &ctrl);
+
+	ctrl.id = V4L2_CID_AUDIO_COMPRESSION_THRESHOLD;
+	ctrl.value = tmp->acomp_info.threshold;
+	rval |= si4713_write_econtrol_integers(sdev, &ctrl);
+
+	ctrl.id = V4L2_CID_AUDIO_COMPRESSION_GAIN;
+	ctrl.value = tmp->acomp_info.gain;
+	rval |= si4713_write_econtrol_integers(sdev, &ctrl);
+
+	ctrl.id = V4L2_CID_PILOT_TONE_FREQUENCY;
+	ctrl.value = tmp->pilot_info.frequency;
+	rval |= si4713_write_econtrol_integers(sdev, &ctrl);
+
+	ctrl.id = V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME;
+	ctrl.value = tmp->acomp_info.attack_time;
+	rval |= si4713_write_econtrol_integers(sdev, &ctrl);
+
+	ctrl.id = V4L2_CID_PILOT_TONE_DEVIATION;
+	ctrl.value = tmp->pilot_info.deviation;
+	rval |= si4713_write_econtrol_integers(sdev, &ctrl);
+
+	ctrl.id = V4L2_CID_AUDIO_LIMITER_DEVIATION;
+	ctrl.value = tmp->limiter_info.deviation;
+	rval |= si4713_write_econtrol_integers(sdev, &ctrl);
+
+	ctrl.id = V4L2_CID_RDS_TX_DEVIATION;
+	ctrl.value = tmp->rds_info.deviation;
+	rval |= si4713_write_econtrol_integers(sdev, &ctrl);
+
+	ctrl.id = V4L2_CID_RDS_TX_PTY;
+	ctrl.value = tmp->rds_info.pty;
+	rval |= si4713_write_econtrol_integers(sdev, &ctrl);
+
+	ctrl.id = V4L2_CID_AUDIO_LIMITER_ENABLED;
+	ctrl.value = tmp->limiter_info.enabled;
+	rval |= si4713_write_econtrol_integers(sdev, &ctrl);
+
+	ctrl.id = V4L2_CID_AUDIO_COMPRESSION_ENABLED;
+	ctrl.value = tmp->acomp_info.enabled;
+	rval |= si4713_write_econtrol_integers(sdev, &ctrl);
+
+	ctrl.id = V4L2_CID_PILOT_TONE_ENABLED;
+	ctrl.value = tmp->pilot_info.enabled;
+	rval |= si4713_write_econtrol_integers(sdev, &ctrl);
+
+	ctrl.id = V4L2_CID_AUDIO_LIMITER_RELEASE_TIME;
+	ctrl.value = tmp->limiter_info.release_time;
+	rval |= si4713_write_econtrol_integers(sdev, &ctrl);
+
+	ctrl.id = V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME;
+	ctrl.value = tmp->acomp_info.release_time;
+	rval |= si4713_write_econtrol_integers(sdev, &ctrl);
+
+	ctrl.id = V4L2_CID_TUNE_PREEMPHASIS;
+	ctrl.value = tmp->preemphasis;
+	rval |= si4713_write_econtrol_integers(sdev, &ctrl);
+
+	ctrl.id = V4L2_CID_RDS_TX_PS_NAME;
+	rval |= si4713_set_rds_ps_name(sdev, tmp->rds_info.ps_name);
+
+	ctrl.id = V4L2_CID_RDS_TX_RADIO_TEXT;
+	rval |= si4713_set_rds_radio_text(sdev, tmp->rds_info.radio_text);
+
+	/* Device procedure needs to set frequency first */
+	f.frequency = tmp->frequency ? tmp->frequency : DEFAULT_FREQUENCY;
+	f.frequency = si4713_to_v4l2(f.frequency);
+	rval |= si4713_s_frequency(&sdev->sd, &f);
+
+	ctrl.id = V4L2_CID_TUNE_POWER_LEVEL;
+	ctrl.value = tmp->power_level;
+	rval |= si4713_write_econtrol_tune(sdev, &ctrl);
+
+	ctrl.id = V4L2_CID_TUNE_ANTENNA_CAPACITOR;
+	ctrl.value = tmp->antenna_capacitor;
+	rval |= si4713_write_econtrol_tune(sdev, &ctrl);
+
+	vm.index = 0;
+	if (tmp->stereo)
+		vm.txsubchans = V4L2_TUNER_SUB_STEREO;
+	else
+		vm.txsubchans = V4L2_TUNER_SUB_MONO;
+	if (tmp->rds_info.enabled)
+		vm.txsubchans |= V4L2_TUNER_SUB_RDS;
+	si4713_s_modulator(&sdev->sd, &vm);
+
+	kfree(tmp);
+
+	return rval;
+}
+
+/*
+ * si4713_initialize - Sets the device up with default configuration.
+ * @sdev: si4713_device structure for the device we are communicating
+ */
+static int si4713_initialize(struct si4713_device *sdev)
+{
+	int rval;
+
+	rval = si4713_set_power_state(sdev, POWER_ON);
+	if (rval < 0)
+		goto exit;
+
+	rval = si4713_checkrev(sdev);
+	if (rval < 0)
+		goto exit;
+
+	rval = si4713_set_power_state(sdev, POWER_OFF);
+	if (rval < 0)
+		goto exit;
+
+	mutex_lock(&sdev->mutex);
+
+	sdev->rds_info.pi = DEFAULT_RDS_PI;
+	sdev->rds_info.pty = DEFAULT_RDS_PTY;
+	sdev->rds_info.deviation = DEFAULT_RDS_DEVIATION;
+	strlcpy(sdev->rds_info.ps_name, DEFAULT_RDS_PS_NAME, MAX_RDS_PS_NAME);
+	strlcpy(sdev->rds_info.radio_text, DEFAULT_RDS_RADIO_TEXT,
+							MAX_RDS_RADIO_TEXT);
+	sdev->rds_info.enabled = 1;
+
+	sdev->limiter_info.release_time = DEFAULT_LIMITER_RTIME;
+	sdev->limiter_info.deviation = DEFAULT_LIMITER_DEV;
+	sdev->limiter_info.enabled = 1;
+
+	sdev->pilot_info.deviation = DEFAULT_PILOT_DEVIATION;
+	sdev->pilot_info.frequency = DEFAULT_PILOT_FREQUENCY;
+	sdev->pilot_info.enabled = 1;
+
+	sdev->acomp_info.release_time = DEFAULT_ACOMP_RTIME;
+	sdev->acomp_info.attack_time = DEFAULT_ACOMP_ATIME;
+	sdev->acomp_info.threshold = DEFAULT_ACOMP_THRESHOLD;
+	sdev->acomp_info.gain = DEFAULT_ACOMP_GAIN;
+	sdev->acomp_info.enabled = 1;
+
+	sdev->frequency = DEFAULT_FREQUENCY;
+	sdev->preemphasis = DEFAULT_PREEMPHASIS;
+	sdev->mute = DEFAULT_MUTE;
+	sdev->power_level = DEFAULT_POWER_LEVEL;
+	sdev->antenna_capacitor = 0;
+	sdev->stereo = 1;
+	sdev->tune_rnl = DEFAULT_TUNE_RNL;
+
+	mutex_unlock(&sdev->mutex);
+
+exit:
+	return rval;
+}
+
+/* read string property */
+static int si4713_read_econtrol_string(struct si4713_device *sdev,
+				struct v4l2_ext_control *control)
+{
+	s32 rval = 0;
+
+	switch (control->id) {
+	case V4L2_CID_RDS_TX_PS_NAME:
+		if (strlen(sdev->rds_info.ps_name) + 1 > control->size) {
+			control->size = MAX_RDS_PS_NAME + 1;
+			rval = -ENOSPC;
+			goto exit;
+		}
+		rval = copy_to_user(control->string, sdev->rds_info.ps_name,
+					strlen(sdev->rds_info.ps_name) + 1);
+		break;
+
+	case V4L2_CID_RDS_TX_RADIO_TEXT:
+		if (strlen(sdev->rds_info.radio_text) + 1 > control->size) {
+			control->size = MAX_RDS_RADIO_TEXT + 1;
+			rval = -ENOSPC;
+			goto exit;
+		}
+		rval = copy_to_user(control->string, sdev->rds_info.radio_text,
+					strlen(sdev->rds_info.radio_text) + 1);
+		break;
+
+	default:
+		rval = -EINVAL;
+		break;
+	};
+
+exit:
+	return rval;
+}
+
+/*
+ * si4713_update_tune_status - update properties from tx_tune_status
+ * command. Must be called with sdev->mutex held.
+ * @sdev: si4713_device structure for the device we are communicating
+ */
+static int si4713_update_tune_status(struct si4713_device *sdev)
+{
+	int rval;
+	u16 f = 0;
+	u8 p = 0, a = 0, n = 0;
+
+	rval = si4713_tx_tune_status(sdev, 0x00, &f, &p, &a, &n);
+
+	if (rval < 0)
+		goto exit;
+
+	sdev->power_level = p;
+	sdev->antenna_capacitor = a;
+	sdev->tune_rnl = n;
+
+exit:
+	return rval;
+}
+
+/* properties which use tx_tune_status */
+static int si4713_read_econtrol_tune(struct si4713_device *sdev,
+				struct v4l2_ext_control *control)
+{
+	s32 rval = 0;
+
+	mutex_lock(&sdev->mutex);
+
+	if (sdev->power_state) {
+		rval = si4713_update_tune_status(sdev);
+		if (rval < 0)
+			goto unlock;
+	}
+
+	switch (control->id) {
+	case V4L2_CID_TUNE_POWER_LEVEL:
+		control->value = sdev->power_level;
+		break;
+	case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
+		control->value = sdev->antenna_capacitor;
+		break;
+	default:
+		rval = -EINVAL;
+	};
+
+unlock:
+	mutex_unlock(&sdev->mutex);
+	return rval;
+}
+
+static int si4713_read_econtrol_integers(struct si4713_device *sdev,
+				struct v4l2_ext_control *control)
+{
+	s32 rval;
+	u32 *shadow = NULL, val = 0;
+	s32 bit = 0, mask = 0;
+	u16 property = 0;
+	int mul = 0;
+	unsigned long *table = NULL;
+	int size = 0;
+
+	rval = si4713_choose_econtrol_action(sdev, control->id, &shadow, &bit,
+			&mask, &property, &mul, &table, &size);
+	if (rval < 0)
+		goto exit;
+
+	mutex_lock(&sdev->mutex);
+
+	if (sdev->power_state) {
+		rval = si4713_read_property(sdev, property, &val);
+		if (rval < 0)
+			goto unlock;
+
+		/* Keep negative values for threshold */
+		if (control->id == V4L2_CID_AUDIO_COMPRESSION_THRESHOLD)
+			*shadow = (s16)val;
+		else if (mask)
+			*shadow = get_status_bit(val, bit, mask);
+		else if (mul)
+			*shadow = val * mul;
+		else
+			*shadow = dev_to_usecs(val, table, size);
+	}
+
+	control->value = *shadow;
+
+unlock:
+	mutex_unlock(&sdev->mutex);
+exit:
+	return rval;
+}
+
+/*
+ * Video4Linux Subdev Interface
+ */
+/* si4713_s_ext_ctrls - set extended controls value */
+static int si4713_s_ext_ctrls(struct v4l2_subdev *sd,
+				struct v4l2_ext_controls *ctrls)
+{
+	struct si4713_device *sdev = to_si4713_device(sd);
+	int i;
+
+	if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX)
+		return -EINVAL;
+
+	for (i = 0; i < ctrls->count; i++) {
+		int err;
+
+		switch ((ctrls->controls + i)->id) {
+		case V4L2_CID_RDS_TX_PS_NAME:
+		case V4L2_CID_RDS_TX_RADIO_TEXT:
+			err = si4713_write_econtrol_string(sdev,
+							ctrls->controls + i);
+			break;
+		case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
+		case V4L2_CID_TUNE_POWER_LEVEL:
+			err = si4713_write_econtrol_tune(sdev,
+							ctrls->controls + i);
+			break;
+		default:
+			err = si4713_write_econtrol_integers(sdev,
+							ctrls->controls + i);
+		}
+
+		if (err < 0) {
+			ctrls->error_idx = i;
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+/* si4713_g_ext_ctrls - get extended controls value */
+static int si4713_g_ext_ctrls(struct v4l2_subdev *sd,
+				struct v4l2_ext_controls *ctrls)
+{
+	struct si4713_device *sdev = to_si4713_device(sd);
+	int i;
+
+	if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX)
+		return -EINVAL;
+
+	for (i = 0; i < ctrls->count; i++) {
+		int err;
+
+		switch ((ctrls->controls + i)->id) {
+		case V4L2_CID_RDS_TX_PS_NAME:
+		case V4L2_CID_RDS_TX_RADIO_TEXT:
+			err = si4713_read_econtrol_string(sdev,
+							ctrls->controls + i);
+			break;
+		case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
+		case V4L2_CID_TUNE_POWER_LEVEL:
+			err = si4713_read_econtrol_tune(sdev,
+							ctrls->controls + i);
+			break;
+		default:
+			err = si4713_read_econtrol_integers(sdev,
+							ctrls->controls + i);
+		}
+
+		if (err < 0) {
+			ctrls->error_idx = i;
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+/* si4713_queryctrl - enumerate control items */
+static int si4713_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+	int rval = 0;
+
+	switch (qc->id) {
+	/* User class controls */
+	case V4L2_CID_AUDIO_MUTE:
+		rval = v4l2_ctrl_query_fill(qc, 0, 1, 1, DEFAULT_MUTE);
+		break;
+	/* FM_TX class controls */
+	case V4L2_CID_RDS_TX_PI:
+		rval = v4l2_ctrl_query_fill(qc, 0, 0xFFFF, 1, DEFAULT_RDS_PI);
+		break;
+	case V4L2_CID_RDS_TX_PTY:
+		rval = v4l2_ctrl_query_fill(qc, 0, 31, 1, DEFAULT_RDS_PTY);
+		break;
+	case V4L2_CID_RDS_TX_DEVIATION:
+		rval = v4l2_ctrl_query_fill(qc, 0, MAX_RDS_DEVIATION,
+						10, DEFAULT_RDS_DEVIATION);
+		break;
+	case V4L2_CID_RDS_TX_PS_NAME:
+		/*
+		 * Report step as 8. From RDS spec, psname
+		 * should be 8. But there are receivers which scroll strings
+		 * sized as 8xN.
+		 */
+		rval = v4l2_ctrl_query_fill(qc, 0, MAX_RDS_PS_NAME, 8, 0);
+		break;
+	case V4L2_CID_RDS_TX_RADIO_TEXT:
+		/*
+		 * Report step as 32 (2A block). From RDS spec,
+		 * radio text should be 32 for 2A block. But there are receivers
+		 * which scroll strings sized as 32xN. Setting default to 32.
+		 */
+		rval = v4l2_ctrl_query_fill(qc, 0, MAX_RDS_RADIO_TEXT, 32, 0);
+		break;
+
+	case V4L2_CID_AUDIO_LIMITER_ENABLED:
+		rval = v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
+		break;
+	case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME:
+		rval = v4l2_ctrl_query_fill(qc, 250, MAX_LIMITER_RELEASE_TIME,
+						50, DEFAULT_LIMITER_RTIME);
+		break;
+	case V4L2_CID_AUDIO_LIMITER_DEVIATION:
+		rval = v4l2_ctrl_query_fill(qc, 0, MAX_LIMITER_DEVIATION,
+						10, DEFAULT_LIMITER_DEV);
+		break;
+
+	case V4L2_CID_AUDIO_COMPRESSION_ENABLED:
+		rval = v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
+		break;
+	case V4L2_CID_AUDIO_COMPRESSION_GAIN:
+		rval = v4l2_ctrl_query_fill(qc, 0, MAX_ACOMP_GAIN, 1,
+						DEFAULT_ACOMP_GAIN);
+		break;
+	case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD:
+		rval = v4l2_ctrl_query_fill(qc, MIN_ACOMP_THRESHOLD,
+						MAX_ACOMP_THRESHOLD, 1,
+						DEFAULT_ACOMP_THRESHOLD);
+		break;
+	case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME:
+		rval = v4l2_ctrl_query_fill(qc, 0, MAX_ACOMP_ATTACK_TIME,
+						500, DEFAULT_ACOMP_ATIME);
+		break;
+	case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME:
+		rval = v4l2_ctrl_query_fill(qc, 100000, MAX_ACOMP_RELEASE_TIME,
+						100000, DEFAULT_ACOMP_RTIME);
+		break;
+
+	case V4L2_CID_PILOT_TONE_ENABLED:
+		rval = v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
+		break;
+	case V4L2_CID_PILOT_TONE_DEVIATION:
+		rval = v4l2_ctrl_query_fill(qc, 0, MAX_PILOT_DEVIATION,
+						10, DEFAULT_PILOT_DEVIATION);
+		break;
+	case V4L2_CID_PILOT_TONE_FREQUENCY:
+		rval = v4l2_ctrl_query_fill(qc, 0, MAX_PILOT_FREQUENCY,
+						1, DEFAULT_PILOT_FREQUENCY);
+		break;
+
+	case V4L2_CID_TUNE_PREEMPHASIS:
+		rval = v4l2_ctrl_query_fill(qc, V4L2_PREEMPHASIS_DISABLED,
+						V4L2_PREEMPHASIS_75_uS, 1,
+						V4L2_PREEMPHASIS_50_uS);
+		break;
+	case V4L2_CID_TUNE_POWER_LEVEL:
+		rval = v4l2_ctrl_query_fill(qc, 0, 120, 1, DEFAULT_POWER_LEVEL);
+		break;
+	case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
+		rval = v4l2_ctrl_query_fill(qc, 0, 191, 1, 0);
+		break;
+	default:
+		rval = -EINVAL;
+		break;
+	};
+
+	return rval;
+}
+
+/* si4713_g_ctrl - get the value of a control */
+static int si4713_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+	struct si4713_device *sdev = to_si4713_device(sd);
+	int rval = 0;
+
+	if (!sdev)
+		return -ENODEV;
+
+	mutex_lock(&sdev->mutex);
+
+	if (sdev->power_state) {
+		rval = si4713_read_property(sdev, SI4713_TX_LINE_INPUT_MUTE,
+						&sdev->mute);
+
+		if (rval < 0)
+			goto unlock;
+	}
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		ctrl->value = get_mute(sdev->mute);
+		break;
+	}
+
+unlock:
+	mutex_unlock(&sdev->mutex);
+	return rval;
+}
+
+/* si4713_s_ctrl - set the value of a control */
+static int si4713_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+	struct si4713_device *sdev = to_si4713_device(sd);
+	int rval = 0;
+
+	if (!sdev)
+		return -ENODEV;
+
+	switch (ctrl->id) {
+	case V4L2_CID_AUDIO_MUTE:
+		if (ctrl->value) {
+			rval = si4713_set_mute(sdev, ctrl->value);
+			if (rval < 0)
+				goto exit;
+
+			rval = si4713_set_power_state(sdev, POWER_DOWN);
+		} else {
+			rval = si4713_set_power_state(sdev, POWER_UP);
+			if (rval < 0)
+				goto exit;
+
+			rval = si4713_setup(sdev);
+			if (rval < 0)
+				goto exit;
+
+			rval = si4713_set_mute(sdev, ctrl->value);
+		}
+		break;
+	}
+
+exit:
+	return rval;
+}
+
+/* si4713_ioctl - deal with private ioctls (only rnl for now) */
+long si4713_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+	struct si4713_device *sdev = to_si4713_device(sd);
+	struct si4713_rnl *rnl = arg;
+	u16 frequency;
+	int rval = 0;
+
+	if (!arg)
+		return -EINVAL;
+
+	mutex_lock(&sdev->mutex);
+	switch (cmd) {
+	case SI4713_IOC_MEASURE_RNL:
+		frequency = v4l2_to_si4713(rnl->frequency);
+
+		if (sdev->power_state) {
+			/* Set desired measurement frequency */
+			rval = si4713_tx_tune_measure(sdev, frequency, 0);
+			if (rval < 0)
+				goto unlock;
+			/* get results from tune status */
+			rval = si4713_update_tune_status(sdev);
+			if (rval < 0)
+				goto unlock;
+		}
+		rnl->rnl = sdev->tune_rnl;
+		break;
+
+	default:
+		/* nothing */
+		rval = -ENOIOCTLCMD;
+	}
+
+unlock:
+	mutex_unlock(&sdev->mutex);
+	return rval;
+}
+
+static const struct v4l2_subdev_core_ops si4713_subdev_core_ops = {
+	.queryctrl	= si4713_queryctrl,
+	.g_ext_ctrls	= si4713_g_ext_ctrls,
+	.s_ext_ctrls	= si4713_s_ext_ctrls,
+	.g_ctrl		= si4713_g_ctrl,
+	.s_ctrl		= si4713_s_ctrl,
+	.ioctl		= si4713_ioctl,
+};
+
+/* si4713_g_modulator - get modulator attributes */
+static int si4713_g_modulator(struct v4l2_subdev *sd, struct v4l2_modulator *vm)
+{
+	struct si4713_device *sdev = to_si4713_device(sd);
+	int rval = 0;
+
+	if (!sdev) {
+		rval = -ENODEV;
+		goto exit;
+	}
+
+	if (vm->index > 0) {
+		rval = -EINVAL;
+		goto exit;
+	}
+
+	strncpy(vm->name, "FM Modulator", 32);
+	vm->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW |
+						V4L2_TUNER_CAP_RDS;
+
+	/* Report current frequency range limits */
+	vm->rangelow = si4713_to_v4l2(FREQ_RANGE_LOW);
+	vm->rangehigh = si4713_to_v4l2(FREQ_RANGE_HIGH);
+
+	mutex_lock(&sdev->mutex);
+
+	if (sdev->power_state) {
+		u32 comp_en = 0;
+
+		rval = si4713_read_property(sdev, SI4713_TX_COMPONENT_ENABLE,
+						&comp_en);
+		if (rval < 0)
+			goto unlock;
+
+		sdev->stereo = get_status_bit(comp_en, 1, 1 << 1);
+		sdev->rds_info.enabled = get_status_bit(comp_en, 2, 1 << 2);
+	}
+
+	/* Report current audio mode: mono or stereo */
+	if (sdev->stereo)
+		vm->txsubchans = V4L2_TUNER_SUB_STEREO;
+	else
+		vm->txsubchans = V4L2_TUNER_SUB_MONO;
+
+	/* Report rds feature status */
+	if (sdev->rds_info.enabled)
+		vm->txsubchans |= V4L2_TUNER_SUB_RDS;
+	else
+		vm->txsubchans &= ~V4L2_TUNER_SUB_RDS;
+
+unlock:
+	mutex_unlock(&sdev->mutex);
+exit:
+	return rval;
+}
+
+/* si4713_s_modulator - set modulator attributes */
+static int si4713_s_modulator(struct v4l2_subdev *sd, struct v4l2_modulator *vm)
+{
+	struct si4713_device *sdev = to_si4713_device(sd);
+	int rval = 0;
+	u16 stereo, rds;
+	u32 p;
+
+	if (!sdev)
+		return -ENODEV;
+
+	if (vm->index > 0)
+		return -EINVAL;
+
+	/* Set audio mode: mono or stereo */
+	if (vm->txsubchans & V4L2_TUNER_SUB_STEREO)
+		stereo = 1;
+	else if (vm->txsubchans & V4L2_TUNER_SUB_MONO)
+		stereo = 0;
+	else
+		return -EINVAL;
+
+	rds = !!(vm->txsubchans & V4L2_TUNER_SUB_RDS);
+
+	mutex_lock(&sdev->mutex);
+
+	if (sdev->power_state) {
+		rval = si4713_read_property(sdev,
+						SI4713_TX_COMPONENT_ENABLE, &p);
+		if (rval < 0)
+			goto unlock;
+
+		p = set_bits(p, stereo, 1, 1 << 1);
+		p = set_bits(p, rds, 2, 1 << 2);
+
+		rval = si4713_write_property(sdev,
+						SI4713_TX_COMPONENT_ENABLE, p);
+		if (rval < 0)
+			goto unlock;
+	}
+
+	sdev->stereo = stereo;
+	sdev->rds_info.enabled = rds;
+
+unlock:
+	mutex_unlock(&sdev->mutex);
+	return rval;
+}
+
+/* si4713_g_frequency - get tuner or modulator radio frequency */
+static int si4713_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
+{
+	struct si4713_device *sdev = to_si4713_device(sd);
+	int rval = 0;
+
+	f->type = V4L2_TUNER_RADIO;
+
+	mutex_lock(&sdev->mutex);
+
+	if (sdev->power_state) {
+		u16 freq;
+		u8 p, a, n;
+
+		rval = si4713_tx_tune_status(sdev, 0x00, &freq, &p, &a, &n);
+		if (rval < 0)
+			goto unlock;
+
+		sdev->frequency = freq;
+	}
+
+	f->frequency = si4713_to_v4l2(sdev->frequency);
+
+unlock:
+	mutex_unlock(&sdev->mutex);
+	return rval;
+}
+
+/* si4713_s_frequency - set tuner or modulator radio frequency */
+static int si4713_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
+{
+	struct si4713_device *sdev = to_si4713_device(sd);
+	int rval = 0;
+	u16 frequency = v4l2_to_si4713(f->frequency);
+
+	/* Check frequency range */
+	if (frequency < FREQ_RANGE_LOW || frequency > FREQ_RANGE_HIGH)
+		return -EDOM;
+
+	mutex_lock(&sdev->mutex);
+
+	if (sdev->power_state) {
+		rval = si4713_tx_tune_freq(sdev, frequency);
+		if (rval < 0)
+			goto unlock;
+		frequency = rval;
+		rval = 0;
+	}
+	sdev->frequency = frequency;
+	f->frequency = si4713_to_v4l2(frequency);
+
+unlock:
+	mutex_unlock(&sdev->mutex);
+	return rval;
+}
+
+static const struct v4l2_subdev_tuner_ops si4713_subdev_tuner_ops = {
+	.g_frequency	= si4713_g_frequency,
+	.s_frequency	= si4713_s_frequency,
+	.g_modulator	= si4713_g_modulator,
+	.s_modulator	= si4713_s_modulator,
+};
+
+static const struct v4l2_subdev_ops si4713_subdev_ops = {
+	.core		= &si4713_subdev_core_ops,
+	.tuner		= &si4713_subdev_tuner_ops,
+};
+
+/*
+ * I2C driver interface
+ */
+/* si4713_probe - probe for the device */
+static int si4713_probe(struct i2c_client *client,
+					const struct i2c_device_id *id)
+{
+	struct si4713_device *sdev;
+	int rval;
+
+	sdev = kzalloc(sizeof *sdev, GFP_KERNEL);
+	if (!sdev) {
+		dev_err(&client->dev, "Failed to alloc video device.\n");
+		rval = -ENOMEM;
+		goto exit;
+	}
+
+	sdev->platform_data = client->dev.platform_data;
+	if (!sdev->platform_data) {
+		v4l2_err(&sdev->sd, "No platform data registered.\n");
+		rval = -ENODEV;
+		goto free_sdev;
+	}
+
+	v4l2_i2c_subdev_init(&sdev->sd, client, &si4713_subdev_ops);
+
+	mutex_init(&sdev->mutex);
+	init_completion(&sdev->work);
+
+	if (client->irq) {
+		rval = request_irq(client->irq,
+			si4713_handler, IRQF_TRIGGER_FALLING | IRQF_DISABLED,
+			client->name, sdev);
+		if (rval < 0) {
+			v4l2_err(&sdev->sd, "Could not request IRQ\n");
+			goto free_sdev;
+		}
+		v4l2_dbg(1, debug, &sdev->sd, "IRQ requested.\n");
+	} else {
+		v4l2_warn(&sdev->sd, "IRQ not configured. Using timeouts.\n");
+	}
+
+	rval = si4713_initialize(sdev);
+	if (rval < 0) {
+		v4l2_err(&sdev->sd, "Failed to probe device information.\n");
+		goto free_irq;
+	}
+
+	return 0;
+
+free_irq:
+	if (client->irq)
+		free_irq(client->irq, sdev);
+free_sdev:
+	kfree(sdev);
+exit:
+	return rval;
+}
+
+/* si4713_remove - remove the device */
+static int si4713_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct si4713_device *sdev = to_si4713_device(sd);
+
+	if (sdev->power_state)
+		si4713_set_power_state(sdev, POWER_DOWN);
+
+	if (client->irq > 0)
+		free_irq(client->irq, sdev);
+
+	v4l2_device_unregister_subdev(sd);
+
+	kfree(sdev);
+
+	return 0;
+}
+
+/* si4713_i2c_driver - i2c driver interface */
+static const struct i2c_device_id si4713_id[] = {
+	{ "si4713" , 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, si4713_id);
+
+static struct i2c_driver si4713_i2c_driver = {
+	.driver		= {
+		.name	= "si4713",
+	},
+	.probe		= si4713_probe,
+	.remove         = si4713_remove,
+	.id_table       = si4713_id,
+};
+
+/* Module Interface */
+static int __init si4713_module_init(void)
+{
+	return i2c_add_driver(&si4713_i2c_driver);
+}
+
+static void __exit si4713_module_exit(void)
+{
+	i2c_del_driver(&si4713_i2c_driver);
+}
+
+module_init(si4713_module_init);
+module_exit(si4713_module_exit);
+

+ 237 - 0
drivers/media/radio/si4713-i2c.h

@@ -0,0 +1,237 @@
+/*
+ * drivers/media/radio/si4713-i2c.h
+ *
+ * Property and commands definitions for Si4713 radio transmitter chip.
+ *
+ * Copyright (c) 2008 Instituto Nokia de Tecnologia - INdT
+ * Contact: Eduardo Valentin <eduardo.valentin@nokia.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+
+#ifndef SI4713_I2C_H
+#define SI4713_I2C_H
+
+#include <media/v4l2-subdev.h>
+#include <media/si4713.h>
+
+#define SI4713_PRODUCT_NUMBER		0x0D
+
+/* Command Timeouts */
+#define DEFAULT_TIMEOUT			500
+#define TIMEOUT_SET_PROPERTY		20
+#define TIMEOUT_TX_TUNE_POWER		30000
+#define TIMEOUT_TX_TUNE			110000
+#define TIMEOUT_POWER_UP		200000
+
+/*
+ * Command and its arguments definitions
+ */
+#define SI4713_PWUP_CTSIEN		(1<<7)
+#define SI4713_PWUP_GPO2OEN		(1<<6)
+#define SI4713_PWUP_PATCH		(1<<5)
+#define SI4713_PWUP_XOSCEN		(1<<4)
+#define SI4713_PWUP_FUNC_TX		0x02
+#define SI4713_PWUP_FUNC_PATCH		0x0F
+#define SI4713_PWUP_OPMOD_ANALOG	0x50
+#define SI4713_PWUP_OPMOD_DIGITAL	0x0F
+#define SI4713_PWUP_NARGS		2
+#define SI4713_PWUP_NRESP		1
+#define SI4713_CMD_POWER_UP		0x01
+
+#define SI4713_GETREV_NRESP		9
+#define SI4713_CMD_GET_REV		0x10
+
+#define SI4713_PWDN_NRESP		1
+#define SI4713_CMD_POWER_DOWN		0x11
+
+#define SI4713_SET_PROP_NARGS		5
+#define SI4713_SET_PROP_NRESP		1
+#define SI4713_CMD_SET_PROPERTY		0x12
+
+#define SI4713_GET_PROP_NARGS		3
+#define SI4713_GET_PROP_NRESP		4
+#define SI4713_CMD_GET_PROPERTY		0x13
+
+#define SI4713_GET_STATUS_NRESP		1
+#define SI4713_CMD_GET_INT_STATUS	0x14
+
+#define SI4713_CMD_PATCH_ARGS		0x15
+#define SI4713_CMD_PATCH_DATA		0x16
+
+#define SI4713_MAX_FREQ			10800
+#define SI4713_MIN_FREQ			7600
+#define SI4713_TXFREQ_NARGS		3
+#define SI4713_TXFREQ_NRESP		1
+#define SI4713_CMD_TX_TUNE_FREQ		0x30
+
+#define SI4713_MAX_POWER		120
+#define SI4713_MIN_POWER		88
+#define SI4713_MAX_ANTCAP		191
+#define SI4713_MIN_ANTCAP		0
+#define SI4713_TXPWR_NARGS		4
+#define SI4713_TXPWR_NRESP		1
+#define SI4713_CMD_TX_TUNE_POWER	0x31
+
+#define SI4713_TXMEA_NARGS		4
+#define SI4713_TXMEA_NRESP		1
+#define SI4713_CMD_TX_TUNE_MEASURE	0x32
+
+#define SI4713_INTACK_MASK		0x01
+#define SI4713_TXSTATUS_NARGS		1
+#define SI4713_TXSTATUS_NRESP		8
+#define SI4713_CMD_TX_TUNE_STATUS	0x33
+
+#define SI4713_OVERMOD_BIT		(1 << 2)
+#define SI4713_IALH_BIT			(1 << 1)
+#define SI4713_IALL_BIT			(1 << 0)
+#define SI4713_ASQSTATUS_NARGS		1
+#define SI4713_ASQSTATUS_NRESP		5
+#define SI4713_CMD_TX_ASQ_STATUS	0x34
+
+#define SI4713_RDSBUFF_MODE_MASK	0x87
+#define SI4713_RDSBUFF_NARGS		7
+#define SI4713_RDSBUFF_NRESP		6
+#define SI4713_CMD_TX_RDS_BUFF		0x35
+
+#define SI4713_RDSPS_PSID_MASK		0x1F
+#define SI4713_RDSPS_NARGS		5
+#define SI4713_RDSPS_NRESP		1
+#define SI4713_CMD_TX_RDS_PS		0x36
+
+#define SI4713_CMD_GPO_CTL		0x80
+#define SI4713_CMD_GPO_SET		0x81
+
+/*
+ * Bits from status response
+ */
+#define SI4713_CTS			(1<<7)
+#define SI4713_ERR			(1<<6)
+#define SI4713_RDS_INT			(1<<2)
+#define SI4713_ASQ_INT			(1<<1)
+#define SI4713_STC_INT			(1<<0)
+
+/*
+ * Property definitions
+ */
+#define SI4713_GPO_IEN			0x0001
+#define SI4713_DIG_INPUT_FORMAT		0x0101
+#define SI4713_DIG_INPUT_SAMPLE_RATE	0x0103
+#define SI4713_REFCLK_FREQ		0x0201
+#define SI4713_REFCLK_PRESCALE		0x0202
+#define SI4713_TX_COMPONENT_ENABLE	0x2100
+#define SI4713_TX_AUDIO_DEVIATION	0x2101
+#define SI4713_TX_PILOT_DEVIATION	0x2102
+#define SI4713_TX_RDS_DEVIATION		0x2103
+#define SI4713_TX_LINE_INPUT_LEVEL	0x2104
+#define SI4713_TX_LINE_INPUT_MUTE	0x2105
+#define SI4713_TX_PREEMPHASIS		0x2106
+#define SI4713_TX_PILOT_FREQUENCY	0x2107
+#define SI4713_TX_ACOMP_ENABLE		0x2200
+#define SI4713_TX_ACOMP_THRESHOLD	0x2201
+#define SI4713_TX_ACOMP_ATTACK_TIME	0x2202
+#define SI4713_TX_ACOMP_RELEASE_TIME	0x2203
+#define SI4713_TX_ACOMP_GAIN		0x2204
+#define SI4713_TX_LIMITER_RELEASE_TIME	0x2205
+#define SI4713_TX_ASQ_INTERRUPT_SOURCE	0x2300
+#define SI4713_TX_ASQ_LEVEL_LOW		0x2301
+#define SI4713_TX_ASQ_DURATION_LOW	0x2302
+#define SI4713_TX_ASQ_LEVEL_HIGH	0x2303
+#define SI4713_TX_ASQ_DURATION_HIGH	0x2304
+#define SI4713_TX_RDS_INTERRUPT_SOURCE	0x2C00
+#define SI4713_TX_RDS_PI		0x2C01
+#define SI4713_TX_RDS_PS_MIX		0x2C02
+#define SI4713_TX_RDS_PS_MISC		0x2C03
+#define SI4713_TX_RDS_PS_REPEAT_COUNT	0x2C04
+#define SI4713_TX_RDS_PS_MESSAGE_COUNT	0x2C05
+#define SI4713_TX_RDS_PS_AF		0x2C06
+#define SI4713_TX_RDS_FIFO_SIZE		0x2C07
+
+#define PREEMPHASIS_USA			75
+#define PREEMPHASIS_EU			50
+#define PREEMPHASIS_DISABLED		0
+#define FMPE_USA			0x00
+#define FMPE_EU				0x01
+#define FMPE_DISABLED			0x02
+
+#define POWER_UP			0x01
+#define POWER_DOWN			0x00
+
+struct rds_info {
+	u32 pi;
+#define MAX_RDS_PTY			31
+	u32 pty;
+#define MAX_RDS_DEVIATION		90000
+	u32 deviation;
+/*
+ * PSNAME is known to be defined as 8 character sized (RDS Spec).
+ * However, there is receivers which scroll PSNAME 8xN sized.
+ */
+#define MAX_RDS_PS_NAME			96
+	u8 ps_name[MAX_RDS_PS_NAME + 1];
+/*
+ * MAX_RDS_RADIO_TEXT is known to be defined as 32 (2A group) or 64 (2B group)
+ * character sized (RDS Spec).
+ * However, there is receivers which scroll them as well.
+ */
+#define MAX_RDS_RADIO_TEXT		384
+	u8 radio_text[MAX_RDS_RADIO_TEXT + 1];
+	u32 enabled;
+};
+
+struct limiter_info {
+#define MAX_LIMITER_RELEASE_TIME	102390
+	u32 release_time;
+#define MAX_LIMITER_DEVIATION		90000
+	u32 deviation;
+	u32 enabled;
+};
+
+struct pilot_info {
+#define MAX_PILOT_DEVIATION		90000
+	u32 deviation;
+#define MAX_PILOT_FREQUENCY		19000
+	u32 frequency;
+	u32 enabled;
+};
+
+struct acomp_info {
+#define MAX_ACOMP_RELEASE_TIME		1000000
+	u32 release_time;
+#define MAX_ACOMP_ATTACK_TIME		5000
+	u32 attack_time;
+#define MAX_ACOMP_THRESHOLD		0
+#define MIN_ACOMP_THRESHOLD		(-40)
+	s32 threshold;
+#define MAX_ACOMP_GAIN			20
+	u32 gain;
+	u32 enabled;
+};
+
+/*
+ * si4713_device - private data
+ */
+struct si4713_device {
+	/* v4l2_subdev and i2c reference (v4l2_subdev priv data) */
+	struct v4l2_subdev sd;
+	/* private data structures */
+	struct mutex mutex;
+	struct completion work;
+	struct si4713_platform_data *platform_data;
+	struct rds_info rds_info;
+	struct limiter_info limiter_info;
+	struct pilot_info pilot_info;
+	struct acomp_info acomp_info;
+	u32 frequency;
+	u32 preemphasis;
+	u32 mute;
+	u32 power_level;
+	u32 power_state;
+	u32 antenna_capacitor;
+	u32 stereo;
+	u32 tune_rnl;
+};
+#endif /* ifndef SI4713_I2C_H */

+ 3 - 3
drivers/media/video/Kconfig

@@ -203,9 +203,9 @@ config VIDEO_CS53L32A
 	  module will be called cs53l32a.
 
 config VIDEO_M52790
-       tristate "Mitsubishi M52790 A/V switch"
-       depends on VIDEO_V4L2 && I2C
-       ---help---
+	tristate "Mitsubishi M52790 A/V switch"
+	depends on VIDEO_V4L2 && I2C
+	---help---
 	 Support for the Mitsubishi M52790 A/V switch.
 
 	 To compile this driver as a module, choose M here: the

+ 1 - 1
drivers/media/video/au0828/au0828-dvb.c

@@ -151,7 +151,7 @@ static int start_urb_transfer(struct au0828_dev *dev)
 	dprintk(2, "%s()\n", __func__);
 
 	if (dev->urb_streaming) {
-		dprintk(2, "%s: iso xfer already running!\n", __func__);
+		dprintk(2, "%s: bulk xfer already running!\n", __func__);
 		return 0;
 	}
 

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

@@ -320,7 +320,6 @@ static struct i2c_algorithm au0828_i2c_algo_template = {
 static struct i2c_adapter au0828_i2c_adap_template = {
 	.name              = DRIVER_NAME,
 	.owner             = THIS_MODULE,
-	.id                = I2C_HW_B_AU0828,
 	.algo              = &au0828_i2c_algo_template,
 };
 

+ 1 - 0
drivers/media/video/bt8xx/bttv-cards.c

@@ -1274,6 +1274,7 @@ struct tvcard bttv_tvcards[] = {
 		.pll            = PLL_28,
 		.tuner_type	= TUNER_TEMIC_PAL,
 		.tuner_addr	= ADDR_UNSET,
+		.has_remote	= 1,
 	},
 
 	/* ---- card 0x3c ---------------------------------- */

+ 6 - 8
drivers/media/video/bt8xx/bttv-driver.c

@@ -2652,6 +2652,8 @@ static int bttv_querycap(struct file *file, void  *priv,
 		V4L2_CAP_VBI_CAPTURE |
 		V4L2_CAP_READWRITE |
 		V4L2_CAP_STREAMING;
+	if (btv->has_saa6588)
+		cap->capabilities |= V4L2_CAP_RDS_CAPTURE;
 	if (no_overlay <= 0)
 		cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
 
@@ -4593,14 +4595,10 @@ static int bttv_resume(struct pci_dev *pci_dev)
 #endif
 
 static struct pci_device_id bttv_pci_tbl[] = {
-	{PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848,
-	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
-	{PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849,
-	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
-	{PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878,
-	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
-	{PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879,
-	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT848), 0},
+	{PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT849), 0},
+	{PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT878), 0},
+	{PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT879), 0},
 	{0,}
 };
 

+ 0 - 2
drivers/media/video/bt8xx/bttv-i2c.c

@@ -352,7 +352,6 @@ int __devinit init_bttv_i2c(struct bttv *btv)
 		/* bt878 */
 		strlcpy(btv->c.i2c_adap.name, "bt878",
 			sizeof(btv->c.i2c_adap.name));
-		btv->c.i2c_adap.id = I2C_HW_B_BT848;	/* FIXME */
 		btv->c.i2c_adap.algo = &bttv_algo;
 	} else {
 		/* bt848 */
@@ -362,7 +361,6 @@ int __devinit init_bttv_i2c(struct bttv *btv)
 
 		strlcpy(btv->c.i2c_adap.name, "bttv",
 			sizeof(btv->c.i2c_adap.name));
-		btv->c.i2c_adap.id = I2C_HW_B_BT848;
 		memcpy(&btv->i2c_algo, &bttv_i2c_algo_bit_template,
 		       sizeof(bttv_i2c_algo_bit_template));
 		btv->i2c_algo.udelay = i2c_udelay;

+ 14 - 13
drivers/media/video/bt8xx/bttv-input.c

@@ -245,7 +245,7 @@ static void bttv_ir_stop(struct bttv *btv)
 int bttv_input_init(struct bttv *btv)
 {
 	struct card_ir *ir;
-	IR_KEYTAB_TYPE *ir_codes = NULL;
+	struct ir_scancode_table *ir_codes = NULL;
 	struct input_dev *input_dev;
 	int ir_type = IR_TYPE_OTHER;
 	int err = -ENOMEM;
@@ -263,7 +263,7 @@ int bttv_input_init(struct bttv *btv)
 	case BTTV_BOARD_AVERMEDIA:
 	case BTTV_BOARD_AVPHONE98:
 	case BTTV_BOARD_AVERMEDIA98:
-		ir_codes         = ir_codes_avermedia;
+		ir_codes         = &ir_codes_avermedia_table;
 		ir->mask_keycode = 0xf88000;
 		ir->mask_keydown = 0x010000;
 		ir->polling      = 50; // ms
@@ -271,14 +271,14 @@ int bttv_input_init(struct bttv *btv)
 
 	case BTTV_BOARD_AVDVBT_761:
 	case BTTV_BOARD_AVDVBT_771:
-		ir_codes         = ir_codes_avermedia_dvbt;
+		ir_codes         = &ir_codes_avermedia_dvbt_table;
 		ir->mask_keycode = 0x0f00c0;
 		ir->mask_keydown = 0x000020;
 		ir->polling      = 50; // ms
 		break;
 
 	case BTTV_BOARD_PXELVWPLTVPAK:
-		ir_codes         = ir_codes_pixelview;
+		ir_codes         = &ir_codes_pixelview_table;
 		ir->mask_keycode = 0x003e00;
 		ir->mask_keyup   = 0x010000;
 		ir->polling      = 50; // ms
@@ -286,54 +286,55 @@ int bttv_input_init(struct bttv *btv)
 	case BTTV_BOARD_PV_M4900:
 	case BTTV_BOARD_PV_BT878P_9B:
 	case BTTV_BOARD_PV_BT878P_PLUS:
-		ir_codes         = ir_codes_pixelview;
+		ir_codes         = &ir_codes_pixelview_table;
 		ir->mask_keycode = 0x001f00;
 		ir->mask_keyup   = 0x008000;
 		ir->polling      = 50; // ms
 		break;
 
 	case BTTV_BOARD_WINFAST2000:
-		ir_codes         = ir_codes_winfast;
+		ir_codes         = &ir_codes_winfast_table;
 		ir->mask_keycode = 0x1f8;
 		break;
 	case BTTV_BOARD_MAGICTVIEW061:
 	case BTTV_BOARD_MAGICTVIEW063:
-		ir_codes         = ir_codes_winfast;
+		ir_codes         = &ir_codes_winfast_table;
 		ir->mask_keycode = 0x0008e000;
 		ir->mask_keydown = 0x00200000;
 		break;
 	case BTTV_BOARD_APAC_VIEWCOMP:
-		ir_codes         = ir_codes_apac_viewcomp;
+		ir_codes         = &ir_codes_apac_viewcomp_table;
 		ir->mask_keycode = 0x001f00;
 		ir->mask_keyup   = 0x008000;
 		ir->polling      = 50; // ms
 		break;
+	case BTTV_BOARD_ASKEY_CPH03X:
 	case BTTV_BOARD_CONCEPTRONIC_CTVFMI2:
 	case BTTV_BOARD_CONTVFMI:
-		ir_codes         = ir_codes_pixelview;
+		ir_codes         = &ir_codes_pixelview_table;
 		ir->mask_keycode = 0x001F00;
 		ir->mask_keyup   = 0x006000;
 		ir->polling      = 50; // ms
 		break;
 	case BTTV_BOARD_NEBULA_DIGITV:
-		ir_codes = ir_codes_nebula;
+		ir_codes = &ir_codes_nebula_table;
 		btv->custom_irq = bttv_rc5_irq;
 		ir->rc5_gpio = 1;
 		break;
 	case BTTV_BOARD_MACHTV_MAGICTV:
-		ir_codes         = ir_codes_apac_viewcomp;
+		ir_codes         = &ir_codes_apac_viewcomp_table;
 		ir->mask_keycode = 0x001F00;
 		ir->mask_keyup   = 0x004000;
 		ir->polling      = 50; /* ms */
 		break;
 	case BTTV_BOARD_KOZUMI_KTV_01C:
-		ir_codes         = ir_codes_pctv_sedna;
+		ir_codes         = &ir_codes_pctv_sedna_table;
 		ir->mask_keycode = 0x001f00;
 		ir->mask_keyup   = 0x006000;
 		ir->polling      = 50; /* ms */
 		break;
 	case BTTV_BOARD_ENLTV_FM_2:
-		ir_codes         = ir_codes_encore_enltv2;
+		ir_codes         = &ir_codes_encore_enltv2_table;
 		ir->mask_keycode = 0x00fd00;
 		ir->mask_keyup   = 0x000080;
 		ir->polling      = 1; /* ms */

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

@@ -490,7 +490,6 @@ static int cafe_smbus_setup(struct cafe_camera *cam)
 	int ret;
 
 	cafe_smbus_enable_irq(cam);
-	adap->id = I2C_HW_SMBUS_CAFE;
 	adap->owner = THIS_MODULE;
 	adap->algo = &cafe_smbus_algo;
 	strcpy(adap->name, "cafe_ccic");

+ 5 - 3
drivers/media/video/cx18/cx18-cards.c

@@ -56,7 +56,8 @@ static const struct cx18_card cx18_card_hvr1600_esmt = {
 	.hw_audio_ctrl = CX18_HW_418_AV,
 	.hw_muxer = CX18_HW_CS5345,
 	.hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
-		  CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL,
+		  CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL |
+		  CX18_HW_Z8F0811_IR_HAUP,
 	.video_inputs = {
 		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE7 },
 		{ CX18_CARD_INPUT_SVIDEO1,    1, CX18_AV_SVIDEO1    },
@@ -102,7 +103,8 @@ static const struct cx18_card cx18_card_hvr1600_samsung = {
 	.hw_audio_ctrl = CX18_HW_418_AV,
 	.hw_muxer = CX18_HW_CS5345,
 	.hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
-		  CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL,
+		  CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL |
+		  CX18_HW_Z8F0811_IR_HAUP,
 	.video_inputs = {
 		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE7 },
 		{ CX18_CARD_INPUT_SVIDEO1,    1, CX18_AV_SVIDEO1    },
@@ -204,7 +206,7 @@ static const struct cx18_card cx18_card_mpc718 = {
 	.v4l2_capabilities = CX18_CAP_ENCODER,
 	.hw_audio_ctrl = CX18_HW_418_AV,
 	.hw_muxer = CX18_HW_GPIO_MUX,
-	.hw_all = CX18_HW_418_AV | CX18_HW_TUNER |
+	.hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
 		  CX18_HW_GPIO_MUX | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL,
 	.video_inputs = {
 		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE2 },

+ 11 - 7
drivers/media/video/cx18/cx18-cards.h

@@ -22,13 +22,17 @@
  */
 
 /* hardware flags */
-#define CX18_HW_TUNER		(1 << 0)
-#define CX18_HW_TVEEPROM	(1 << 1)
-#define CX18_HW_CS5345		(1 << 2)
-#define CX18_HW_DVB		(1 << 3)
-#define CX18_HW_418_AV		(1 << 4)
-#define CX18_HW_GPIO_MUX	(1 << 5)
-#define CX18_HW_GPIO_RESET_CTRL	(1 << 6)
+#define CX18_HW_TUNER			(1 << 0)
+#define CX18_HW_TVEEPROM		(1 << 1)
+#define CX18_HW_CS5345			(1 << 2)
+#define CX18_HW_DVB			(1 << 3)
+#define CX18_HW_418_AV			(1 << 4)
+#define CX18_HW_GPIO_MUX		(1 << 5)
+#define CX18_HW_GPIO_RESET_CTRL		(1 << 6)
+#define CX18_HW_Z8F0811_IR_TX_HAUP	(1 << 7)
+#define CX18_HW_Z8F0811_IR_RX_HAUP	(1 << 8)
+#define CX18_HW_Z8F0811_IR_HAUP	(CX18_HW_Z8F0811_IR_RX_HAUP | \
+				 CX18_HW_Z8F0811_IR_TX_HAUP)
 
 /* video inputs */
 #define	CX18_CARD_INPUT_VID_TUNER	1

Some files were not shown because too many files changed in this diff