Explorar o código

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 %!s(int64=16) %!d(string=hai) anos
pai
achega
043fe50f80
Modificáronse 100 ficheiros con 11082 adicións e 5916 borrados
  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. 2698 2324
      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);

+ 2698 - 2324
drivers/media/common/ir-keymaps.c

@@ -1,8 +1,6 @@
 /*
-
-
-    Keytables for supported remote controls. This file is part of
-    video4linux.
+    Keytables for supported remote controls, used on drivers/media
+    devices.
 
     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
@@ -17,7 +15,13 @@
     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.
+*/
 
+/*
+ * NOTICE FOR DEVELOPERS:
+ *   The IR mappings should be as close as possible to what's
+ *   specified at:
+ *      http://linuxtv.org/wiki/index.php/Remote_Controllers
  */
 #include <linux/module.h>
 
@@ -25,589 +29,627 @@
 #include <media/ir-common.h>
 
 /* empty keytable, can be used as placeholder for not-yet created keytables */
-IR_KEYTAB_TYPE ir_codes_empty[IR_KEYTAB_SIZE] = {
-	[ 0x2a ] = KEY_COFFEE,
+static struct ir_scancode ir_codes_empty[] = {
+	{ 0x2a, KEY_COFFEE },
 };
 
-EXPORT_SYMBOL_GPL(ir_codes_empty);
+struct ir_scancode_table ir_codes_empty_table = {
+	.scan = ir_codes_empty,
+	.size = ARRAY_SIZE(ir_codes_empty),
+};
+EXPORT_SYMBOL_GPL(ir_codes_empty_table);
 
 /* Michal Majchrowicz <mmajchrowicz@gmail.com> */
-IR_KEYTAB_TYPE ir_codes_proteus_2309[IR_KEYTAB_SIZE] = {
+static struct ir_scancode ir_codes_proteus_2309[] = {
 	/* numeric */
-	[ 0x00 ] = KEY_0,
-	[ 0x01 ] = KEY_1,
-	[ 0x02 ] = KEY_2,
-	[ 0x03 ] = KEY_3,
-	[ 0x04 ] = KEY_4,
-	[ 0x05 ] = KEY_5,
-	[ 0x06 ] = KEY_6,
-	[ 0x07 ] = KEY_7,
-	[ 0x08 ] = KEY_8,
-	[ 0x09 ] = KEY_9,
-
-	[ 0x5c ] = KEY_POWER,     /* power       */
-	[ 0x20 ] = KEY_F,         /* full screen */
-	[ 0x0f ] = KEY_BACKSPACE, /* recall      */
-	[ 0x1b ] = KEY_ENTER,     /* mute        */
-	[ 0x41 ] = KEY_RECORD,    /* record      */
-	[ 0x43 ] = KEY_STOP,      /* stop        */
-	[ 0x16 ] = KEY_S,
-	[ 0x1a ] = KEY_Q,         /* off         */
-	[ 0x2e ] = KEY_RED,
-	[ 0x1f ] = KEY_DOWN,      /* channel -   */
-	[ 0x1c ] = KEY_UP,        /* channel +   */
-	[ 0x10 ] = KEY_LEFT,      /* volume -    */
-	[ 0x1e ] = KEY_RIGHT,     /* volume +    */
-	[ 0x14 ] = KEY_F1,
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_proteus_2309);
+	{ 0x00, KEY_0 },
+	{ 0x01, KEY_1 },
+	{ 0x02, KEY_2 },
+	{ 0x03, KEY_3 },
+	{ 0x04, KEY_4 },
+	{ 0x05, KEY_5 },
+	{ 0x06, KEY_6 },
+	{ 0x07, KEY_7 },
+	{ 0x08, KEY_8 },
+	{ 0x09, KEY_9 },
+
+	{ 0x5c, KEY_POWER },		/* power       */
+	{ 0x20, KEY_ZOOM },		/* full screen */
+	{ 0x0f, KEY_BACKSPACE },	/* recall      */
+	{ 0x1b, KEY_ENTER },		/* mute        */
+	{ 0x41, KEY_RECORD },		/* record      */
+	{ 0x43, KEY_STOP },		/* stop        */
+	{ 0x16, KEY_S },
+	{ 0x1a, KEY_POWER2 },		/* off         */
+	{ 0x2e, KEY_RED },
+	{ 0x1f, KEY_CHANNELDOWN },	/* channel -   */
+	{ 0x1c, KEY_CHANNELUP },	/* channel +   */
+	{ 0x10, KEY_VOLUMEDOWN },	/* volume -    */
+	{ 0x1e, KEY_VOLUMEUP },		/* volume +    */
+	{ 0x14, KEY_F1 },
+};
+
+struct ir_scancode_table ir_codes_proteus_2309_table = {
+	.scan = ir_codes_proteus_2309,
+	.size = ARRAY_SIZE(ir_codes_proteus_2309),
+};
+EXPORT_SYMBOL_GPL(ir_codes_proteus_2309_table);
+
 /* Matt Jesson <dvb@jesson.eclipse.co.uk */
-IR_KEYTAB_TYPE ir_codes_avermedia_dvbt[IR_KEYTAB_SIZE] = {
-	[ 0x28 ] = KEY_0,         //'0' / 'enter'
-	[ 0x22 ] = KEY_1,         //'1'
-	[ 0x12 ] = KEY_2,         //'2' / 'up arrow'
-	[ 0x32 ] = KEY_3,         //'3'
-	[ 0x24 ] = KEY_4,         //'4' / 'left arrow'
-	[ 0x14 ] = KEY_5,         //'5'
-	[ 0x34 ] = KEY_6,         //'6' / 'right arrow'
-	[ 0x26 ] = KEY_7,         //'7'
-	[ 0x16 ] = KEY_8,         //'8' / 'down arrow'
-	[ 0x36 ] = KEY_9,         //'9'
-
-	[ 0x20 ] = KEY_LIST,        // 'source'
-	[ 0x10 ] = KEY_TEXT,        // 'teletext'
-	[ 0x00 ] = KEY_POWER,       // 'power'
-	[ 0x04 ] = KEY_AUDIO,       // 'audio'
-	[ 0x06 ] = KEY_ZOOM,        // 'full screen'
-	[ 0x18 ] = KEY_VIDEO,       // 'display'
-	[ 0x38 ] = KEY_SEARCH,      // 'loop'
-	[ 0x08 ] = KEY_INFO,        // 'preview'
-	[ 0x2a ] = KEY_REWIND,      // 'backward <<'
-	[ 0x1a ] = KEY_FASTFORWARD, // 'forward >>'
-	[ 0x3a ] = KEY_RECORD,      // 'capture'
-	[ 0x0a ] = KEY_MUTE,        // 'mute'
-	[ 0x2c ] = KEY_RECORD,      // 'record'
-	[ 0x1c ] = KEY_PAUSE,       // 'pause'
-	[ 0x3c ] = KEY_STOP,        // 'stop'
-	[ 0x0c ] = KEY_PLAY,        // 'play'
-	[ 0x2e ] = KEY_RED,         // 'red'
-	[ 0x01 ] = KEY_BLUE,        // 'blue' / 'cancel'
-	[ 0x0e ] = KEY_YELLOW,      // 'yellow' / 'ok'
-	[ 0x21 ] = KEY_GREEN,       // 'green'
-	[ 0x11 ] = KEY_CHANNELDOWN, // 'channel -'
-	[ 0x31 ] = KEY_CHANNELUP,   // 'channel +'
-	[ 0x1e ] = KEY_VOLUMEDOWN,  // 'volume -'
-	[ 0x3e ] = KEY_VOLUMEUP,    // 'volume +'
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_avermedia_dvbt);
+static struct ir_scancode ir_codes_avermedia_dvbt[] = {
+	{ 0x28, KEY_0 },		/* '0' / 'enter' */
+	{ 0x22, KEY_1 },		/* '1' */
+	{ 0x12, KEY_2 },		/* '2' / 'up arrow' */
+	{ 0x32, KEY_3 },		/* '3' */
+	{ 0x24, KEY_4 },		/* '4' / 'left arrow' */
+	{ 0x14, KEY_5 },		/* '5' */
+	{ 0x34, KEY_6 },		/* '6' / 'right arrow' */
+	{ 0x26, KEY_7 },		/* '7' */
+	{ 0x16, KEY_8 },		/* '8' / 'down arrow' */
+	{ 0x36, KEY_9 },		/* '9' */
+
+	{ 0x20, KEY_LIST },		/* 'source' */
+	{ 0x10, KEY_TEXT },		/* 'teletext' */
+	{ 0x00, KEY_POWER },		/* 'power' */
+	{ 0x04, KEY_AUDIO },		/* 'audio' */
+	{ 0x06, KEY_ZOOM },		/* 'full screen' */
+	{ 0x18, KEY_VIDEO },		/* 'display' */
+	{ 0x38, KEY_SEARCH },		/* 'loop' */
+	{ 0x08, KEY_INFO },		/* 'preview' */
+	{ 0x2a, KEY_REWIND },		/* 'backward <<' */
+	{ 0x1a, KEY_FASTFORWARD },	/* 'forward >>' */
+	{ 0x3a, KEY_RECORD },		/* 'capture' */
+	{ 0x0a, KEY_MUTE },		/* 'mute' */
+	{ 0x2c, KEY_RECORD },		/* 'record' */
+	{ 0x1c, KEY_PAUSE },		/* 'pause' */
+	{ 0x3c, KEY_STOP },		/* 'stop' */
+	{ 0x0c, KEY_PLAY },		/* 'play' */
+	{ 0x2e, KEY_RED },		/* 'red' */
+	{ 0x01, KEY_BLUE },		/* 'blue' / 'cancel' */
+	{ 0x0e, KEY_YELLOW },		/* 'yellow' / 'ok' */
+	{ 0x21, KEY_GREEN },		/* 'green' */
+	{ 0x11, KEY_CHANNELDOWN },	/* 'channel -' */
+	{ 0x31, KEY_CHANNELUP },	/* 'channel +' */
+	{ 0x1e, KEY_VOLUMEDOWN },	/* 'volume -' */
+	{ 0x3e, KEY_VOLUMEUP },		/* 'volume +' */
+};
+
+struct ir_scancode_table ir_codes_avermedia_dvbt_table = {
+	.scan = ir_codes_avermedia_dvbt,
+	.size = ARRAY_SIZE(ir_codes_avermedia_dvbt),
+};
+EXPORT_SYMBOL_GPL(ir_codes_avermedia_dvbt_table);
 
 /* Mauro Carvalho Chehab <mchehab@infradead.org> */
-IR_KEYTAB_TYPE ir_codes_avermedia_m135a[IR_KEYTAB_SIZE] = {
-	[0x00] = KEY_POWER2,
-	[0x2e] = KEY_DOT,		/* '.' */
-	[0x01] = KEY_MODE,		/* TV/FM */
-
-	[0x05] = KEY_1,
-	[0x06] = KEY_2,
-	[0x07] = KEY_3,
-	[0x09] = KEY_4,
-	[0x0a] = KEY_5,
-	[0x0b] = KEY_6,
-	[0x0d] = KEY_7,
-	[0x0e] = KEY_8,
-	[0x0f] = KEY_9,
-	[0x11] = KEY_0,
-
-	[0x13] = KEY_RIGHT,		/* -> */
-	[0x12] = KEY_LEFT,		/* <- */
-
-	[0x17] = KEY_SLEEP,		/* Capturar Imagem */
-	[0x10] = KEY_SHUFFLE,		/* Amostra */
+static struct ir_scancode ir_codes_avermedia_m135a[] = {
+	{ 0x00, KEY_POWER2 },
+	{ 0x2e, KEY_DOT },		/* '.' */
+	{ 0x01, KEY_MODE },		/* TV/FM */
+
+	{ 0x05, KEY_1 },
+	{ 0x06, KEY_2 },
+	{ 0x07, KEY_3 },
+	{ 0x09, KEY_4 },
+	{ 0x0a, KEY_5 },
+	{ 0x0b, KEY_6 },
+	{ 0x0d, KEY_7 },
+	{ 0x0e, KEY_8 },
+	{ 0x0f, KEY_9 },
+	{ 0x11, KEY_0 },
+
+	{ 0x13, KEY_RIGHT },		/* -> */
+	{ 0x12, KEY_LEFT },		/* <- */
+
+	{ 0x17, KEY_SLEEP },		/* Capturar Imagem */
+	{ 0x10, KEY_SHUFFLE },		/* Amostra */
 
 	/* FIXME: The keys bellow aren't ok */
 
-	[0x43] = KEY_CHANNELUP,
-	[0x42] = KEY_CHANNELDOWN,
-	[0x1f] = KEY_VOLUMEUP,
-	[0x1e] = KEY_VOLUMEDOWN,
-	[0x0c] = KEY_ENTER,
+	{ 0x43, KEY_CHANNELUP },
+	{ 0x42, KEY_CHANNELDOWN },
+	{ 0x1f, KEY_VOLUMEUP },
+	{ 0x1e, KEY_VOLUMEDOWN },
+	{ 0x0c, KEY_ENTER },
 
-	[0x14] = KEY_MUTE,
-	[0x08] = KEY_AUDIO,
+	{ 0x14, KEY_MUTE },
+	{ 0x08, KEY_AUDIO },
 
-	[0x03] = KEY_TEXT,
-	[0x04] = KEY_EPG,
-	[0x2b] = KEY_TV2,		/* TV2 */
+	{ 0x03, KEY_TEXT },
+	{ 0x04, KEY_EPG },
+	{ 0x2b, KEY_TV2 },		/* TV2 */
 
-	[0x1d] = KEY_RED,
-	[0x1c] = KEY_YELLOW,
-	[0x41] = KEY_GREEN,
-	[0x40] = KEY_BLUE,
+	{ 0x1d, KEY_RED },
+	{ 0x1c, KEY_YELLOW },
+	{ 0x41, KEY_GREEN },
+	{ 0x40, KEY_BLUE },
 
-	[0x1a] = KEY_PLAYPAUSE,
-	[0x19] = KEY_RECORD,
-	[0x18] = KEY_PLAY,
-	[0x1b] = KEY_STOP,
+	{ 0x1a, KEY_PLAYPAUSE },
+	{ 0x19, KEY_RECORD },
+	{ 0x18, KEY_PLAY },
+	{ 0x1b, KEY_STOP },
 };
-EXPORT_SYMBOL_GPL(ir_codes_avermedia_m135a);
+
+struct ir_scancode_table ir_codes_avermedia_m135a_table = {
+	.scan = ir_codes_avermedia_m135a,
+	.size = ARRAY_SIZE(ir_codes_avermedia_m135a),
+};
+EXPORT_SYMBOL_GPL(ir_codes_avermedia_m135a_table);
 
 /* Oldrich Jedlicka <oldium.pro@seznam.cz> */
-IR_KEYTAB_TYPE ir_codes_avermedia_cardbus[IR_KEYTAB_SIZE] = {
-	[0x00] = KEY_POWER,
-	[0x01] = KEY_TUNER,		/* TV/FM */
-	[0x03] = KEY_TEXT,		/* Teletext */
-	[0x04] = KEY_EPG,
-	[0x05] = KEY_1,
-	[0x06] = KEY_2,
-	[0x07] = KEY_3,
-	[0x08] = KEY_AUDIO,
-	[0x09] = KEY_4,
-	[0x0a] = KEY_5,
-	[0x0b] = KEY_6,
-	[0x0c] = KEY_ZOOM,		/* Full screen */
-	[0x0d] = KEY_7,
-	[0x0e] = KEY_8,
-	[0x0f] = KEY_9,
-	[0x10] = KEY_PAGEUP,		/* 16-CH PREV */
-	[0x11] = KEY_0,
-	[0x12] = KEY_INFO,
-	[0x13] = KEY_AGAIN,		/* CH RTN - channel return */
-	[0x14] = KEY_MUTE,
-	[0x15] = KEY_EDIT,		/* Autoscan */
-	[0x17] = KEY_SAVE,		/* Screenshot */
-	[0x18] = KEY_PLAYPAUSE,
-	[0x19] = KEY_RECORD,
-	[0x1a] = KEY_PLAY,
-	[0x1b] = KEY_STOP,
-	[0x1c] = KEY_FASTFORWARD,
-	[0x1d] = KEY_REWIND,
-	[0x1e] = KEY_VOLUMEDOWN,
-	[0x1f] = KEY_VOLUMEUP,
-	[0x22] = KEY_SLEEP,		/* Sleep */
-	[0x23] = KEY_ZOOM,		/* Aspect */
-	[0x26] = KEY_SCREEN,		/* Pos */
-	[0x27] = KEY_ANGLE,		/* Size */
-	[0x28] = KEY_SELECT,		/* Select */
-	[0x29] = KEY_BLUE,		/* Blue/Picture */
-	[0x2a] = KEY_BACKSPACE,	/* Back */
-	[0x2b] = KEY_MEDIA,		/* PIP (Picture-in-picture) */
-	[0x2c] = KEY_DOWN,
-	[0x2e] = KEY_DOT,
-	[0x2f] = KEY_TV,		/* Live TV */
-	[0x32] = KEY_LEFT,
-	[0x33] = KEY_CLEAR,		/* Clear */
-	[0x35] = KEY_RED,		/* Red/TV */
-	[0x36] = KEY_UP,
-	[0x37] = KEY_HOME,		/* Home */
-	[0x39] = KEY_GREEN,		/* Green/Video */
-	[0x3d] = KEY_YELLOW,		/* Yellow/Music */
-	[0x3e] = KEY_OK,		/* Ok */
-	[0x3f] = KEY_RIGHT,
-	[0x40] = KEY_NEXT,		/* Next */
-	[0x41] = KEY_PREVIOUS,	/* Previous */
-	[0x42] = KEY_CHANNELDOWN,	/* Channel down */
-	[0x43] = KEY_CHANNELUP	/* Channel up */
-};
-EXPORT_SYMBOL_GPL(ir_codes_avermedia_cardbus);
+static struct ir_scancode ir_codes_avermedia_cardbus[] = {
+	{ 0x00, KEY_POWER },
+	{ 0x01, KEY_TUNER },		/* TV/FM */
+	{ 0x03, KEY_TEXT },		/* Teletext */
+	{ 0x04, KEY_EPG },
+	{ 0x05, KEY_1 },
+	{ 0x06, KEY_2 },
+	{ 0x07, KEY_3 },
+	{ 0x08, KEY_AUDIO },
+	{ 0x09, KEY_4 },
+	{ 0x0a, KEY_5 },
+	{ 0x0b, KEY_6 },
+	{ 0x0c, KEY_ZOOM },		/* Full screen */
+	{ 0x0d, KEY_7 },
+	{ 0x0e, KEY_8 },
+	{ 0x0f, KEY_9 },
+	{ 0x10, KEY_PAGEUP },		/* 16-CH PREV */
+	{ 0x11, KEY_0 },
+	{ 0x12, KEY_INFO },
+	{ 0x13, KEY_AGAIN },		/* CH RTN - channel return */
+	{ 0x14, KEY_MUTE },
+	{ 0x15, KEY_EDIT },		/* Autoscan */
+	{ 0x17, KEY_SAVE },		/* Screenshot */
+	{ 0x18, KEY_PLAYPAUSE },
+	{ 0x19, KEY_RECORD },
+	{ 0x1a, KEY_PLAY },
+	{ 0x1b, KEY_STOP },
+	{ 0x1c, KEY_FASTFORWARD },
+	{ 0x1d, KEY_REWIND },
+	{ 0x1e, KEY_VOLUMEDOWN },
+	{ 0x1f, KEY_VOLUMEUP },
+	{ 0x22, KEY_SLEEP },		/* Sleep */
+	{ 0x23, KEY_ZOOM },		/* Aspect */
+	{ 0x26, KEY_SCREEN },		/* Pos */
+	{ 0x27, KEY_ANGLE },		/* Size */
+	{ 0x28, KEY_SELECT },		/* Select */
+	{ 0x29, KEY_BLUE },		/* Blue/Picture */
+	{ 0x2a, KEY_BACKSPACE },	/* Back */
+	{ 0x2b, KEY_MEDIA },		/* PIP (Picture-in-picture) */
+	{ 0x2c, KEY_DOWN },
+	{ 0x2e, KEY_DOT },
+	{ 0x2f, KEY_TV },		/* Live TV */
+	{ 0x32, KEY_LEFT },
+	{ 0x33, KEY_CLEAR },		/* Clear */
+	{ 0x35, KEY_RED },		/* Red/TV */
+	{ 0x36, KEY_UP },
+	{ 0x37, KEY_HOME },		/* Home */
+	{ 0x39, KEY_GREEN },		/* Green/Video */
+	{ 0x3d, KEY_YELLOW },		/* Yellow/Music */
+	{ 0x3e, KEY_OK },		/* Ok */
+	{ 0x3f, KEY_RIGHT },
+	{ 0x40, KEY_NEXT },		/* Next */
+	{ 0x41, KEY_PREVIOUS },		/* Previous */
+	{ 0x42, KEY_CHANNELDOWN },	/* Channel down */
+	{ 0x43, KEY_CHANNELUP },	/* Channel up */
+};
+
+struct ir_scancode_table ir_codes_avermedia_cardbus_table = {
+	.scan = ir_codes_avermedia_cardbus,
+	.size = ARRAY_SIZE(ir_codes_avermedia_cardbus),
+};
+EXPORT_SYMBOL_GPL(ir_codes_avermedia_cardbus_table);
 
 /* Attila Kondoros <attila.kondoros@chello.hu> */
-IR_KEYTAB_TYPE ir_codes_apac_viewcomp[IR_KEYTAB_SIZE] = {
-
-	[ 0x01 ] = KEY_1,
-	[ 0x02 ] = KEY_2,
-	[ 0x03 ] = KEY_3,
-	[ 0x04 ] = KEY_4,
-	[ 0x05 ] = KEY_5,
-	[ 0x06 ] = KEY_6,
-	[ 0x07 ] = KEY_7,
-	[ 0x08 ] = KEY_8,
-	[ 0x09 ] = KEY_9,
-	[ 0x00 ] = KEY_0,
-	[ 0x17 ] = KEY_LAST,        // +100
-	[ 0x0a ] = KEY_LIST,        // recall
-
-
-	[ 0x1c ] = KEY_TUNER,       // TV/FM
-	[ 0x15 ] = KEY_SEARCH,      // scan
-	[ 0x12 ] = KEY_POWER,       // power
-	[ 0x1f ] = KEY_VOLUMEDOWN,  // vol up
-	[ 0x1b ] = KEY_VOLUMEUP,    // vol down
-	[ 0x1e ] = KEY_CHANNELDOWN, // chn up
-	[ 0x1a ] = KEY_CHANNELUP,   // chn down
-
-	[ 0x11 ] = KEY_VIDEO,       // video
-	[ 0x0f ] = KEY_ZOOM,        // full screen
-	[ 0x13 ] = KEY_MUTE,        // mute/unmute
-	[ 0x10 ] = KEY_TEXT,        // min
-
-	[ 0x0d ] = KEY_STOP,        // freeze
-	[ 0x0e ] = KEY_RECORD,      // record
-	[ 0x1d ] = KEY_PLAYPAUSE,   // stop
-	[ 0x19 ] = KEY_PLAY,        // play
-
-	[ 0x16 ] = KEY_GOTO,        // osd
-	[ 0x14 ] = KEY_REFRESH,     // default
-	[ 0x0c ] = KEY_KPPLUS,      // fine tune >>>>
-	[ 0x18 ] = KEY_KPMINUS      // fine tune <<<<
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_apac_viewcomp);
+static struct ir_scancode ir_codes_apac_viewcomp[] = {
+
+	{ 0x01, KEY_1 },
+	{ 0x02, KEY_2 },
+	{ 0x03, KEY_3 },
+	{ 0x04, KEY_4 },
+	{ 0x05, KEY_5 },
+	{ 0x06, KEY_6 },
+	{ 0x07, KEY_7 },
+	{ 0x08, KEY_8 },
+	{ 0x09, KEY_9 },
+	{ 0x00, KEY_0 },
+	{ 0x17, KEY_LAST },		/* +100 */
+	{ 0x0a, KEY_LIST },		/* recall */
+
+
+	{ 0x1c, KEY_TUNER },		/* TV/FM */
+	{ 0x15, KEY_SEARCH },		/* scan */
+	{ 0x12, KEY_POWER },		/* power */
+	{ 0x1f, KEY_VOLUMEDOWN },	/* vol up */
+	{ 0x1b, KEY_VOLUMEUP },		/* vol down */
+	{ 0x1e, KEY_CHANNELDOWN },	/* chn up */
+	{ 0x1a, KEY_CHANNELUP },	/* chn down */
+
+	{ 0x11, KEY_VIDEO },		/* video */
+	{ 0x0f, KEY_ZOOM },		/* full screen */
+	{ 0x13, KEY_MUTE },		/* mute/unmute */
+	{ 0x10, KEY_TEXT },		/* min */
+
+	{ 0x0d, KEY_STOP },		/* freeze */
+	{ 0x0e, KEY_RECORD },		/* record */
+	{ 0x1d, KEY_PLAYPAUSE },	/* stop */
+	{ 0x19, KEY_PLAY },		/* play */
+
+	{ 0x16, KEY_GOTO },		/* osd */
+	{ 0x14, KEY_REFRESH },		/* default */
+	{ 0x0c, KEY_KPPLUS },		/* fine tune >>>> */
+	{ 0x18, KEY_KPMINUS },		/* fine tune <<<< */
+};
+
+struct ir_scancode_table ir_codes_apac_viewcomp_table = {
+	.scan = ir_codes_apac_viewcomp,
+	.size = ARRAY_SIZE(ir_codes_apac_viewcomp),
+};
+EXPORT_SYMBOL_GPL(ir_codes_apac_viewcomp_table);
 
 /* ---------------------------------------------------------------------- */
 
-IR_KEYTAB_TYPE ir_codes_pixelview[IR_KEYTAB_SIZE] = {
+static struct ir_scancode ir_codes_pixelview[] = {
 
-	[ 0x1e ] = KEY_POWER,       // power
-	[ 0x07 ] = KEY_MEDIA,       // source
-	[ 0x1c ] = KEY_SEARCH,      // scan
+	{ 0x1e, KEY_POWER },	/* power */
+	{ 0x07, KEY_MEDIA },	/* source */
+	{ 0x1c, KEY_SEARCH },	/* scan */
 
-/* FIXME: duplicate keycodes?
- *
- * These four keys seem to share the same GPIO as CH+, CH-, <<< and >>>
- * The GPIO values are
- * 6397fb for both "Scan <" and "CH -",
- * 639ffb for "Scan >" and "CH+",
- * 6384fb for "Tune <" and "<<<",
- * 638cfb for "Tune >" and ">>>", regardless of the mask.
- *
- *	[ 0x17 ] = KEY_BACK,        // fm scan <<
- *	[ 0x1f ] = KEY_FORWARD,     // fm scan >>
- *
- *	[ 0x04 ] = KEY_LEFT,        // fm tuning <
- *	[ 0x0c ] = KEY_RIGHT,       // fm tuning >
- *
- * For now, these four keys are disabled. Pressing them will generate
- * the CH+/CH-/<<</>>> events
- */
 
-	[ 0x03 ] = KEY_TUNER,       // TV/FM
+	{ 0x03, KEY_TUNER },		/* TV/FM */
 
-	[ 0x00 ] = KEY_RECORD,
-	[ 0x08 ] = KEY_STOP,
-	[ 0x11 ] = KEY_PLAY,
+	{ 0x00, KEY_RECORD },
+	{ 0x08, KEY_STOP },
+	{ 0x11, KEY_PLAY },
 
-	[ 0x1a ] = KEY_PLAYPAUSE,   // freeze
-	[ 0x19 ] = KEY_ZOOM,        // zoom
-	[ 0x0f ] = KEY_TEXT,        // min
+	{ 0x1a, KEY_PLAYPAUSE },	/* freeze */
+	{ 0x19, KEY_ZOOM },		/* zoom */
+	{ 0x0f, KEY_TEXT },		/* min */
 
-	[ 0x01 ] = KEY_1,
-	[ 0x0b ] = KEY_2,
-	[ 0x1b ] = KEY_3,
-	[ 0x05 ] = KEY_4,
-	[ 0x09 ] = KEY_5,
-	[ 0x15 ] = KEY_6,
-	[ 0x06 ] = KEY_7,
-	[ 0x0a ] = KEY_8,
-	[ 0x12 ] = KEY_9,
-	[ 0x02 ] = KEY_0,
-	[ 0x10 ] = KEY_LAST,        // +100
-	[ 0x13 ] = KEY_LIST,        // recall
+	{ 0x01, KEY_1 },
+	{ 0x0b, KEY_2 },
+	{ 0x1b, KEY_3 },
+	{ 0x05, KEY_4 },
+	{ 0x09, KEY_5 },
+	{ 0x15, KEY_6 },
+	{ 0x06, KEY_7 },
+	{ 0x0a, KEY_8 },
+	{ 0x12, KEY_9 },
+	{ 0x02, KEY_0 },
+	{ 0x10, KEY_LAST },		/* +100 */
+	{ 0x13, KEY_LIST },		/* recall */
 
-	[ 0x1f ] = KEY_CHANNELUP,   // chn down
-	[ 0x17 ] = KEY_CHANNELDOWN, // chn up
-	[ 0x16 ] = KEY_VOLUMEUP,    // vol down
-	[ 0x14 ] = KEY_VOLUMEDOWN,  // vol up
+	{ 0x1f, KEY_CHANNELUP },	/* chn down */
+	{ 0x17, KEY_CHANNELDOWN },	/* chn up */
+	{ 0x16, KEY_VOLUMEUP },		/* vol down */
+	{ 0x14, KEY_VOLUMEDOWN },	/* vol up */
 
-	[ 0x04 ] = KEY_KPMINUS,     // <<<
-	[ 0x0e ] = KEY_SETUP,       // function
-	[ 0x0c ] = KEY_KPPLUS,      // >>>
+	{ 0x04, KEY_KPMINUS },		/* <<< */
+	{ 0x0e, KEY_SETUP },		/* function */
+	{ 0x0c, KEY_KPPLUS },		/* >>> */
 
-	[ 0x0d ] = KEY_GOTO,        // mts
-	[ 0x1d ] = KEY_REFRESH,     // reset
-	[ 0x18 ] = KEY_MUTE         // mute/unmute
+	{ 0x0d, KEY_GOTO },		/* mts */
+	{ 0x1d, KEY_REFRESH },		/* reset */
+	{ 0x18, KEY_MUTE },		/* mute/unmute */
 };
 
-EXPORT_SYMBOL_GPL(ir_codes_pixelview);
+struct ir_scancode_table ir_codes_pixelview_table = {
+	.scan = ir_codes_pixelview,
+	.size = ARRAY_SIZE(ir_codes_pixelview),
+};
+EXPORT_SYMBOL_GPL(ir_codes_pixelview_table);
 
 /*
    Mauro Carvalho Chehab <mchehab@infradead.org>
    present on PV MPEG 8000GT
  */
-IR_KEYTAB_TYPE ir_codes_pixelview_new[IR_KEYTAB_SIZE] = {
-	[0x3c] = KEY_PAUSE,		/* Timeshift */
-	[0x12] = KEY_POWER,
-
-	[0x3d] = KEY_1,
-	[0x38] = KEY_2,
-	[0x18] = KEY_3,
-	[0x35] = KEY_4,
-	[0x39] = KEY_5,
-	[0x15] = KEY_6,
-	[0x36] = KEY_7,
-	[0x3a] = KEY_8,
-	[0x1e] = KEY_9,
-	[0x3e] = KEY_0,
-
-	[0x1c] = KEY_AGAIN,		/* LOOP	*/
-	[0x3f] = KEY_MEDIA,		/* Source */
-	[0x1f] = KEY_LAST,		/* +100 */
-	[0x1b] = KEY_MUTE,
-
-	[0x17] = KEY_CHANNELDOWN,
-	[0x16] = KEY_CHANNELUP,
-	[0x10] = KEY_VOLUMEUP,
-	[0x14] = KEY_VOLUMEDOWN,
-	[0x13] = KEY_ZOOM,
-
-	[0x19] = KEY_SHUFFLE,		/* SNAPSHOT */
-	[0x1a] = KEY_SEARCH,		/* scan */
-
-	[0x37] = KEY_REWIND,		/* << */
-	[0x32] = KEY_RECORD,		/* o (red) */
-	[0x33] = KEY_FORWARD,		/* >> */
-	[0x11] = KEY_STOP,		/* square */
-	[0x3b] = KEY_PLAY,		/* > */
-	[0x30] = KEY_PLAYPAUSE,		/* || */
-
-	[0x31] = KEY_TV,
-	[0x34] = KEY_RADIO,
-};
-EXPORT_SYMBOL_GPL(ir_codes_pixelview_new);
-
-IR_KEYTAB_TYPE ir_codes_nebula[IR_KEYTAB_SIZE] = {
-	[ 0x00 ] = KEY_0,
-	[ 0x01 ] = KEY_1,
-	[ 0x02 ] = KEY_2,
-	[ 0x03 ] = KEY_3,
-	[ 0x04 ] = KEY_4,
-	[ 0x05 ] = KEY_5,
-	[ 0x06 ] = KEY_6,
-	[ 0x07 ] = KEY_7,
-	[ 0x08 ] = KEY_8,
-	[ 0x09 ] = KEY_9,
-	[ 0x0a ] = KEY_TV,
-	[ 0x0b ] = KEY_AUX,
-	[ 0x0c ] = KEY_DVD,
-	[ 0x0d ] = KEY_POWER,
-	[ 0x0e ] = KEY_MHP,	/* labelled 'Picture' */
-	[ 0x0f ] = KEY_AUDIO,
-	[ 0x10 ] = KEY_INFO,
-	[ 0x11 ] = KEY_F13,	/* 16:9 */
-	[ 0x12 ] = KEY_F14,	/* 14:9 */
-	[ 0x13 ] = KEY_EPG,
-	[ 0x14 ] = KEY_EXIT,
-	[ 0x15 ] = KEY_MENU,
-	[ 0x16 ] = KEY_UP,
-	[ 0x17 ] = KEY_DOWN,
-	[ 0x18 ] = KEY_LEFT,
-	[ 0x19 ] = KEY_RIGHT,
-	[ 0x1a ] = KEY_ENTER,
-	[ 0x1b ] = KEY_CHANNELUP,
-	[ 0x1c ] = KEY_CHANNELDOWN,
-	[ 0x1d ] = KEY_VOLUMEUP,
-	[ 0x1e ] = KEY_VOLUMEDOWN,
-	[ 0x1f ] = KEY_RED,
-	[ 0x20 ] = KEY_GREEN,
-	[ 0x21 ] = KEY_YELLOW,
-	[ 0x22 ] = KEY_BLUE,
-	[ 0x23 ] = KEY_SUBTITLE,
-	[ 0x24 ] = KEY_F15,	/* AD */
-	[ 0x25 ] = KEY_TEXT,
-	[ 0x26 ] = KEY_MUTE,
-	[ 0x27 ] = KEY_REWIND,
-	[ 0x28 ] = KEY_STOP,
-	[ 0x29 ] = KEY_PLAY,
-	[ 0x2a ] = KEY_FASTFORWARD,
-	[ 0x2b ] = KEY_F16,	/* chapter */
-	[ 0x2c ] = KEY_PAUSE,
-	[ 0x2d ] = KEY_PLAY,
-	[ 0x2e ] = KEY_RECORD,
-	[ 0x2f ] = KEY_F17,	/* picture in picture */
-	[ 0x30 ] = KEY_KPPLUS,	/* zoom in */
-	[ 0x31 ] = KEY_KPMINUS,	/* zoom out */
-	[ 0x32 ] = KEY_F18,	/* capture */
-	[ 0x33 ] = KEY_F19,	/* web */
-	[ 0x34 ] = KEY_EMAIL,
-	[ 0x35 ] = KEY_PHONE,
-	[ 0x36 ] = KEY_PC
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_nebula);
+static struct ir_scancode ir_codes_pixelview_new[] = {
+	{ 0x3c, KEY_TIME },		/* Timeshift */
+	{ 0x12, KEY_POWER },
+
+	{ 0x3d, KEY_1 },
+	{ 0x38, KEY_2 },
+	{ 0x18, KEY_3 },
+	{ 0x35, KEY_4 },
+	{ 0x39, KEY_5 },
+	{ 0x15, KEY_6 },
+	{ 0x36, KEY_7 },
+	{ 0x3a, KEY_8 },
+	{ 0x1e, KEY_9 },
+	{ 0x3e, KEY_0 },
+
+	{ 0x1c, KEY_AGAIN },		/* LOOP	*/
+	{ 0x3f, KEY_MEDIA },		/* Source */
+	{ 0x1f, KEY_LAST },		/* +100 */
+	{ 0x1b, KEY_MUTE },
+
+	{ 0x17, KEY_CHANNELDOWN },
+	{ 0x16, KEY_CHANNELUP },
+	{ 0x10, KEY_VOLUMEUP },
+	{ 0x14, KEY_VOLUMEDOWN },
+	{ 0x13, KEY_ZOOM },
+
+	{ 0x19, KEY_CAMERA },		/* SNAPSHOT */
+	{ 0x1a, KEY_SEARCH },		/* scan */
+
+	{ 0x37, KEY_REWIND },		/* << */
+	{ 0x32, KEY_RECORD },		/* o (red) */
+	{ 0x33, KEY_FORWARD },		/* >> */
+	{ 0x11, KEY_STOP },		/* square */
+	{ 0x3b, KEY_PLAY },		/* > */
+	{ 0x30, KEY_PLAYPAUSE },	/* || */
+
+	{ 0x31, KEY_TV },
+	{ 0x34, KEY_RADIO },
+};
+
+struct ir_scancode_table ir_codes_pixelview_new_table = {
+	.scan = ir_codes_pixelview_new,
+	.size = ARRAY_SIZE(ir_codes_pixelview_new),
+};
+EXPORT_SYMBOL_GPL(ir_codes_pixelview_new_table);
+
+static struct ir_scancode ir_codes_nebula[] = {
+	{ 0x00, KEY_0 },
+	{ 0x01, KEY_1 },
+	{ 0x02, KEY_2 },
+	{ 0x03, KEY_3 },
+	{ 0x04, KEY_4 },
+	{ 0x05, KEY_5 },
+	{ 0x06, KEY_6 },
+	{ 0x07, KEY_7 },
+	{ 0x08, KEY_8 },
+	{ 0x09, KEY_9 },
+	{ 0x0a, KEY_TV },
+	{ 0x0b, KEY_AUX },
+	{ 0x0c, KEY_DVD },
+	{ 0x0d, KEY_POWER },
+	{ 0x0e, KEY_MHP },	/* labelled 'Picture' */
+	{ 0x0f, KEY_AUDIO },
+	{ 0x10, KEY_INFO },
+	{ 0x11, KEY_F13 },	/* 16:9 */
+	{ 0x12, KEY_F14 },	/* 14:9 */
+	{ 0x13, KEY_EPG },
+	{ 0x14, KEY_EXIT },
+	{ 0x15, KEY_MENU },
+	{ 0x16, KEY_UP },
+	{ 0x17, KEY_DOWN },
+	{ 0x18, KEY_LEFT },
+	{ 0x19, KEY_RIGHT },
+	{ 0x1a, KEY_ENTER },
+	{ 0x1b, KEY_CHANNELUP },
+	{ 0x1c, KEY_CHANNELDOWN },
+	{ 0x1d, KEY_VOLUMEUP },
+	{ 0x1e, KEY_VOLUMEDOWN },
+	{ 0x1f, KEY_RED },
+	{ 0x20, KEY_GREEN },
+	{ 0x21, KEY_YELLOW },
+	{ 0x22, KEY_BLUE },
+	{ 0x23, KEY_SUBTITLE },
+	{ 0x24, KEY_F15 },	/* AD */
+	{ 0x25, KEY_TEXT },
+	{ 0x26, KEY_MUTE },
+	{ 0x27, KEY_REWIND },
+	{ 0x28, KEY_STOP },
+	{ 0x29, KEY_PLAY },
+	{ 0x2a, KEY_FASTFORWARD },
+	{ 0x2b, KEY_F16 },	/* chapter */
+	{ 0x2c, KEY_PAUSE },
+	{ 0x2d, KEY_PLAY },
+	{ 0x2e, KEY_RECORD },
+	{ 0x2f, KEY_F17 },	/* picture in picture */
+	{ 0x30, KEY_KPPLUS },	/* zoom in */
+	{ 0x31, KEY_KPMINUS },	/* zoom out */
+	{ 0x32, KEY_F18 },	/* capture */
+	{ 0x33, KEY_F19 },	/* web */
+	{ 0x34, KEY_EMAIL },
+	{ 0x35, KEY_PHONE },
+	{ 0x36, KEY_PC },
+};
+
+struct ir_scancode_table ir_codes_nebula_table = {
+	.scan = ir_codes_nebula,
+	.size = ARRAY_SIZE(ir_codes_nebula),
+};
+EXPORT_SYMBOL_GPL(ir_codes_nebula_table);
 
 /* DigitalNow DNTV Live DVB-T Remote */
-IR_KEYTAB_TYPE ir_codes_dntv_live_dvb_t[IR_KEYTAB_SIZE] = {
-	[ 0x00 ] = KEY_ESC,		/* 'go up a level?' */
+static struct ir_scancode ir_codes_dntv_live_dvb_t[] = {
+	{ 0x00, KEY_ESC },		/* 'go up a level?' */
 	/* Keys 0 to 9 */
-	[ 0x0a ] = KEY_0,
-	[ 0x01 ] = KEY_1,
-	[ 0x02 ] = KEY_2,
-	[ 0x03 ] = KEY_3,
-	[ 0x04 ] = KEY_4,
-	[ 0x05 ] = KEY_5,
-	[ 0x06 ] = KEY_6,
-	[ 0x07 ] = KEY_7,
-	[ 0x08 ] = KEY_8,
-	[ 0x09 ] = KEY_9,
-
-	[ 0x0b ] = KEY_TUNER,		/* tv/fm */
-	[ 0x0c ] = KEY_SEARCH,		/* scan */
-	[ 0x0d ] = KEY_STOP,
-	[ 0x0e ] = KEY_PAUSE,
-	[ 0x0f ] = KEY_LIST,		/* source */
-
-	[ 0x10 ] = KEY_MUTE,
-	[ 0x11 ] = KEY_REWIND,		/* backward << */
-	[ 0x12 ] = KEY_POWER,
-	[ 0x13 ] = KEY_S,			/* snap */
-	[ 0x14 ] = KEY_AUDIO,		/* stereo */
-	[ 0x15 ] = KEY_CLEAR,		/* reset */
-	[ 0x16 ] = KEY_PLAY,
-	[ 0x17 ] = KEY_ENTER,
-	[ 0x18 ] = KEY_ZOOM,		/* full screen */
-	[ 0x19 ] = KEY_FASTFORWARD,	/* forward >> */
-	[ 0x1a ] = KEY_CHANNELUP,
-	[ 0x1b ] = KEY_VOLUMEUP,
-	[ 0x1c ] = KEY_INFO,		/* preview */
-	[ 0x1d ] = KEY_RECORD,		/* record */
-	[ 0x1e ] = KEY_CHANNELDOWN,
-	[ 0x1f ] = KEY_VOLUMEDOWN,
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_dntv_live_dvb_t);
+	{ 0x0a, KEY_0 },
+	{ 0x01, KEY_1 },
+	{ 0x02, KEY_2 },
+	{ 0x03, KEY_3 },
+	{ 0x04, KEY_4 },
+	{ 0x05, KEY_5 },
+	{ 0x06, KEY_6 },
+	{ 0x07, KEY_7 },
+	{ 0x08, KEY_8 },
+	{ 0x09, KEY_9 },
+
+	{ 0x0b, KEY_TUNER },		/* tv/fm */
+	{ 0x0c, KEY_SEARCH },		/* scan */
+	{ 0x0d, KEY_STOP },
+	{ 0x0e, KEY_PAUSE },
+	{ 0x0f, KEY_LIST },		/* source */
+
+	{ 0x10, KEY_MUTE },
+	{ 0x11, KEY_REWIND },		/* backward << */
+	{ 0x12, KEY_POWER },
+	{ 0x13, KEY_CAMERA },		/* snap */
+	{ 0x14, KEY_AUDIO },		/* stereo */
+	{ 0x15, KEY_CLEAR },		/* reset */
+	{ 0x16, KEY_PLAY },
+	{ 0x17, KEY_ENTER },
+	{ 0x18, KEY_ZOOM },		/* full screen */
+	{ 0x19, KEY_FASTFORWARD },	/* forward >> */
+	{ 0x1a, KEY_CHANNELUP },
+	{ 0x1b, KEY_VOLUMEUP },
+	{ 0x1c, KEY_INFO },		/* preview */
+	{ 0x1d, KEY_RECORD },		/* record */
+	{ 0x1e, KEY_CHANNELDOWN },
+	{ 0x1f, KEY_VOLUMEDOWN },
+};
+
+struct ir_scancode_table ir_codes_dntv_live_dvb_t_table = {
+	.scan = ir_codes_dntv_live_dvb_t,
+	.size = ARRAY_SIZE(ir_codes_dntv_live_dvb_t),
+};
+EXPORT_SYMBOL_GPL(ir_codes_dntv_live_dvb_t_table);
 
 /* ---------------------------------------------------------------------- */
 
 /* IO-DATA BCTV7E Remote */
-IR_KEYTAB_TYPE ir_codes_iodata_bctv7e[IR_KEYTAB_SIZE] = {
-	[ 0x40 ] = KEY_TV,
-	[ 0x20 ] = KEY_RADIO,		/* FM */
-	[ 0x60 ] = KEY_EPG,
-	[ 0x00 ] = KEY_POWER,
+static struct ir_scancode ir_codes_iodata_bctv7e[] = {
+	{ 0x40, KEY_TV },
+	{ 0x20, KEY_RADIO },		/* FM */
+	{ 0x60, KEY_EPG },
+	{ 0x00, KEY_POWER },
 
 	/* Keys 0 to 9 */
-	[ 0x44 ] = KEY_0,		/* 10 */
-	[ 0x50 ] = KEY_1,
-	[ 0x30 ] = KEY_2,
-	[ 0x70 ] = KEY_3,
-	[ 0x48 ] = KEY_4,
-	[ 0x28 ] = KEY_5,
-	[ 0x68 ] = KEY_6,
-	[ 0x58 ] = KEY_7,
-	[ 0x38 ] = KEY_8,
-	[ 0x78 ] = KEY_9,
-
-	[ 0x10 ] = KEY_L,			/* Live */
-	[ 0x08 ] = KEY_T,			/* Time Shift */
-
-	[ 0x18 ] = KEY_PLAYPAUSE,		/* Play */
-
-	[ 0x24 ] = KEY_ENTER,		/* 11 */
-	[ 0x64 ] = KEY_ESC,		/* 12 */
-	[ 0x04 ] = KEY_M,			/* Multi */
-
-	[ 0x54 ] = KEY_VIDEO,
-	[ 0x34 ] = KEY_CHANNELUP,
-	[ 0x74 ] = KEY_VOLUMEUP,
-	[ 0x14 ] = KEY_MUTE,
-
-	[ 0x4c ] = KEY_S,			/* SVIDEO */
-	[ 0x2c ] = KEY_CHANNELDOWN,
-	[ 0x6c ] = KEY_VOLUMEDOWN,
-	[ 0x0c ] = KEY_ZOOM,
-
-	[ 0x5c ] = KEY_PAUSE,
-	[ 0x3c ] = KEY_C,			/* || (red) */
-	[ 0x7c ] = KEY_RECORD,		/* recording */
-	[ 0x1c ] = KEY_STOP,
-
-	[ 0x41 ] = KEY_REWIND,		/* backward << */
-	[ 0x21 ] = KEY_PLAY,
-	[ 0x61 ] = KEY_FASTFORWARD,	/* forward >> */
-	[ 0x01 ] = KEY_NEXT,		/* skip >| */
+	{ 0x44, KEY_0 },		/* 10 */
+	{ 0x50, KEY_1 },
+	{ 0x30, KEY_2 },
+	{ 0x70, KEY_3 },
+	{ 0x48, KEY_4 },
+	{ 0x28, KEY_5 },
+	{ 0x68, KEY_6 },
+	{ 0x58, KEY_7 },
+	{ 0x38, KEY_8 },
+	{ 0x78, KEY_9 },
+
+	{ 0x10, KEY_L },		/* Live */
+	{ 0x08, KEY_TIME },		/* Time Shift */
+
+	{ 0x18, KEY_PLAYPAUSE },	/* Play */
+
+	{ 0x24, KEY_ENTER },		/* 11 */
+	{ 0x64, KEY_ESC },		/* 12 */
+	{ 0x04, KEY_M },		/* Multi */
+
+	{ 0x54, KEY_VIDEO },
+	{ 0x34, KEY_CHANNELUP },
+	{ 0x74, KEY_VOLUMEUP },
+	{ 0x14, KEY_MUTE },
+
+	{ 0x4c, KEY_VCR },		/* SVIDEO */
+	{ 0x2c, KEY_CHANNELDOWN },
+	{ 0x6c, KEY_VOLUMEDOWN },
+	{ 0x0c, KEY_ZOOM },
+
+	{ 0x5c, KEY_PAUSE },
+	{ 0x3c, KEY_RED },		/* || (red) */
+	{ 0x7c, KEY_RECORD },		/* recording */
+	{ 0x1c, KEY_STOP },
+
+	{ 0x41, KEY_REWIND },		/* backward << */
+	{ 0x21, KEY_PLAY },
+	{ 0x61, KEY_FASTFORWARD },	/* forward >> */
+	{ 0x01, KEY_NEXT },		/* skip >| */
 };
 
-EXPORT_SYMBOL_GPL(ir_codes_iodata_bctv7e);
+struct ir_scancode_table ir_codes_iodata_bctv7e_table = {
+	.scan = ir_codes_iodata_bctv7e,
+	.size = ARRAY_SIZE(ir_codes_iodata_bctv7e),
+};
+EXPORT_SYMBOL_GPL(ir_codes_iodata_bctv7e_table);
 
 /* ---------------------------------------------------------------------- */
 
 /* ADS Tech Instant TV DVB-T PCI Remote */
-IR_KEYTAB_TYPE ir_codes_adstech_dvb_t_pci[IR_KEYTAB_SIZE] = {
+static struct ir_scancode ir_codes_adstech_dvb_t_pci[] = {
 	/* Keys 0 to 9 */
-	[ 0x4d ] = KEY_0,
-	[ 0x57 ] = KEY_1,
-	[ 0x4f ] = KEY_2,
-	[ 0x53 ] = KEY_3,
-	[ 0x56 ] = KEY_4,
-	[ 0x4e ] = KEY_5,
-	[ 0x5e ] = KEY_6,
-	[ 0x54 ] = KEY_7,
-	[ 0x4c ] = KEY_8,
-	[ 0x5c ] = KEY_9,
-
-	[ 0x5b ] = KEY_POWER,
-	[ 0x5f ] = KEY_MUTE,
-	[ 0x55 ] = KEY_GOTO,
-	[ 0x5d ] = KEY_SEARCH,
-	[ 0x17 ] = KEY_EPG,		/* Guide */
-	[ 0x1f ] = KEY_MENU,
-	[ 0x0f ] = KEY_UP,
-	[ 0x46 ] = KEY_DOWN,
-	[ 0x16 ] = KEY_LEFT,
-	[ 0x1e ] = KEY_RIGHT,
-	[ 0x0e ] = KEY_SELECT,		/* Enter */
-	[ 0x5a ] = KEY_INFO,
-	[ 0x52 ] = KEY_EXIT,
-	[ 0x59 ] = KEY_PREVIOUS,
-	[ 0x51 ] = KEY_NEXT,
-	[ 0x58 ] = KEY_REWIND,
-	[ 0x50 ] = KEY_FORWARD,
-	[ 0x44 ] = KEY_PLAYPAUSE,
-	[ 0x07 ] = KEY_STOP,
-	[ 0x1b ] = KEY_RECORD,
-	[ 0x13 ] = KEY_TUNER,		/* Live */
-	[ 0x0a ] = KEY_A,
-	[ 0x12 ] = KEY_B,
-	[ 0x03 ] = KEY_PROG1,		/* 1 */
-	[ 0x01 ] = KEY_PROG2,		/* 2 */
-	[ 0x00 ] = KEY_PROG3,		/* 3 */
-	[ 0x06 ] = KEY_DVD,
-	[ 0x48 ] = KEY_AUX,		/* Photo */
-	[ 0x40 ] = KEY_VIDEO,
-	[ 0x19 ] = KEY_AUDIO,		/* Music */
-	[ 0x0b ] = KEY_CHANNELUP,
-	[ 0x08 ] = KEY_CHANNELDOWN,
-	[ 0x15 ] = KEY_VOLUMEUP,
-	[ 0x1c ] = KEY_VOLUMEDOWN,
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_adstech_dvb_t_pci);
+	{ 0x4d, KEY_0 },
+	{ 0x57, KEY_1 },
+	{ 0x4f, KEY_2 },
+	{ 0x53, KEY_3 },
+	{ 0x56, KEY_4 },
+	{ 0x4e, KEY_5 },
+	{ 0x5e, KEY_6 },
+	{ 0x54, KEY_7 },
+	{ 0x4c, KEY_8 },
+	{ 0x5c, KEY_9 },
+
+	{ 0x5b, KEY_POWER },
+	{ 0x5f, KEY_MUTE },
+	{ 0x55, KEY_GOTO },
+	{ 0x5d, KEY_SEARCH },
+	{ 0x17, KEY_EPG },		/* Guide */
+	{ 0x1f, KEY_MENU },
+	{ 0x0f, KEY_UP },
+	{ 0x46, KEY_DOWN },
+	{ 0x16, KEY_LEFT },
+	{ 0x1e, KEY_RIGHT },
+	{ 0x0e, KEY_SELECT },		/* Enter */
+	{ 0x5a, KEY_INFO },
+	{ 0x52, KEY_EXIT },
+	{ 0x59, KEY_PREVIOUS },
+	{ 0x51, KEY_NEXT },
+	{ 0x58, KEY_REWIND },
+	{ 0x50, KEY_FORWARD },
+	{ 0x44, KEY_PLAYPAUSE },
+	{ 0x07, KEY_STOP },
+	{ 0x1b, KEY_RECORD },
+	{ 0x13, KEY_TUNER },		/* Live */
+	{ 0x0a, KEY_A },
+	{ 0x12, KEY_B },
+	{ 0x03, KEY_PROG1 },		/* 1 */
+	{ 0x01, KEY_PROG2 },		/* 2 */
+	{ 0x00, KEY_PROG3 },		/* 3 */
+	{ 0x06, KEY_DVD },
+	{ 0x48, KEY_AUX },		/* Photo */
+	{ 0x40, KEY_VIDEO },
+	{ 0x19, KEY_AUDIO },		/* Music */
+	{ 0x0b, KEY_CHANNELUP },
+	{ 0x08, KEY_CHANNELDOWN },
+	{ 0x15, KEY_VOLUMEUP },
+	{ 0x1c, KEY_VOLUMEDOWN },
+};
+
+struct ir_scancode_table ir_codes_adstech_dvb_t_pci_table = {
+	.scan = ir_codes_adstech_dvb_t_pci,
+	.size = ARRAY_SIZE(ir_codes_adstech_dvb_t_pci),
+};
+EXPORT_SYMBOL_GPL(ir_codes_adstech_dvb_t_pci_table);
 
 /* ---------------------------------------------------------------------- */
 
 /* MSI TV@nywhere MASTER remote */
 
-IR_KEYTAB_TYPE ir_codes_msi_tvanywhere[IR_KEYTAB_SIZE] = {
+static struct ir_scancode ir_codes_msi_tvanywhere[] = {
 	/* Keys 0 to 9 */
-	[ 0x00 ] = KEY_0,
-	[ 0x01 ] = KEY_1,
-	[ 0x02 ] = KEY_2,
-	[ 0x03 ] = KEY_3,
-	[ 0x04 ] = KEY_4,
-	[ 0x05 ] = KEY_5,
-	[ 0x06 ] = KEY_6,
-	[ 0x07 ] = KEY_7,
-	[ 0x08 ] = KEY_8,
-	[ 0x09 ] = KEY_9,
-
-	[ 0x0c ] = KEY_MUTE,
-	[ 0x0f ] = KEY_SCREEN,		/* Full Screen */
-	[ 0x10 ] = KEY_F,			/* Funtion */
-	[ 0x11 ] = KEY_T,			/* Time shift */
-	[ 0x12 ] = KEY_POWER,
-	[ 0x13 ] = KEY_MEDIA,		/* MTS */
-	[ 0x14 ] = KEY_SLOW,
-	[ 0x16 ] = KEY_REWIND,		/* backward << */
-	[ 0x17 ] = KEY_ENTER,		/* Return */
-	[ 0x18 ] = KEY_FASTFORWARD,	/* forward >> */
-	[ 0x1a ] = KEY_CHANNELUP,
-	[ 0x1b ] = KEY_VOLUMEUP,
-	[ 0x1e ] = KEY_CHANNELDOWN,
-	[ 0x1f ] = KEY_VOLUMEDOWN,
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_msi_tvanywhere);
+	{ 0x00, KEY_0 },
+	{ 0x01, KEY_1 },
+	{ 0x02, KEY_2 },
+	{ 0x03, KEY_3 },
+	{ 0x04, KEY_4 },
+	{ 0x05, KEY_5 },
+	{ 0x06, KEY_6 },
+	{ 0x07, KEY_7 },
+	{ 0x08, KEY_8 },
+	{ 0x09, KEY_9 },
+
+	{ 0x0c, KEY_MUTE },
+	{ 0x0f, KEY_SCREEN },		/* Full Screen */
+	{ 0x10, KEY_FN },		/* Funtion */
+	{ 0x11, KEY_TIME },		/* Time shift */
+	{ 0x12, KEY_POWER },
+	{ 0x13, KEY_MEDIA },		/* MTS */
+	{ 0x14, KEY_SLOW },
+	{ 0x16, KEY_REWIND },		/* backward << */
+	{ 0x17, KEY_ENTER },		/* Return */
+	{ 0x18, KEY_FASTFORWARD },	/* forward >> */
+	{ 0x1a, KEY_CHANNELUP },
+	{ 0x1b, KEY_VOLUMEUP },
+	{ 0x1e, KEY_CHANNELDOWN },
+	{ 0x1f, KEY_VOLUMEDOWN },
+};
+
+struct ir_scancode_table ir_codes_msi_tvanywhere_table = {
+	.scan = ir_codes_msi_tvanywhere,
+	.size = ARRAY_SIZE(ir_codes_msi_tvanywhere),
+};
+EXPORT_SYMBOL_GPL(ir_codes_msi_tvanywhere_table);
 
 /* ---------------------------------------------------------------------- */
 
@@ -626,7 +668,7 @@ EXPORT_SYMBOL_GPL(ir_codes_msi_tvanywhere);
 
 */
 
-IR_KEYTAB_TYPE ir_codes_msi_tvanywhere_plus[IR_KEYTAB_SIZE] = {
+static struct ir_scancode ir_codes_msi_tvanywhere_plus[] = {
 
 /*  ---- Remote Button Layout ----
 
@@ -648,596 +690,645 @@ IR_KEYTAB_TYPE ir_codes_msi_tvanywhere_plus[IR_KEYTAB_SIZE] = {
      <<      FUNC    >>     RESET
 */
 
-	[0x01] = KEY_KP1,             /* 1 */
-	[0x0b] = KEY_KP2,             /* 2 */
-	[0x1b] = KEY_KP3,             /* 3 */
-	[0x05] = KEY_KP4,             /* 4 */
-	[0x09] = KEY_KP5,             /* 5 */
-	[0x15] = KEY_KP6,             /* 6 */
-	[0x06] = KEY_KP7,             /* 7 */
-	[0x0a] = KEY_KP8,             /* 8 */
-	[0x12] = KEY_KP9,             /* 9 */
-	[0x02] = KEY_KP0,             /* 0 */
-	[0x10] = KEY_KPPLUS,          /* + */
-	[0x13] = KEY_AGAIN,           /* Recall */
-
-	[0x1e] = KEY_POWER,           /* Power */
-	[0x07] = KEY_TUNER,           /* Source */
-	[0x1c] = KEY_SEARCH,          /* Scan */
-	[0x18] = KEY_MUTE,            /* Mute */
-
-	[0x03] = KEY_RADIO,           /* TV/FM */
+	{ 0x01, KEY_1 },		/* 1 */
+	{ 0x0b, KEY_2 },		/* 2 */
+	{ 0x1b, KEY_3 },		/* 3 */
+	{ 0x05, KEY_4 },		/* 4 */
+	{ 0x09, KEY_5 },		/* 5 */
+	{ 0x15, KEY_6 },		/* 6 */
+	{ 0x06, KEY_7 },		/* 7 */
+	{ 0x0a, KEY_8 },		/* 8 */
+	{ 0x12, KEY_9 },		/* 9 */
+	{ 0x02, KEY_0 },		/* 0 */
+	{ 0x10, KEY_KPPLUS },		/* + */
+	{ 0x13, KEY_AGAIN },		/* Recall */
+
+	{ 0x1e, KEY_POWER },		/* Power */
+	{ 0x07, KEY_TUNER },		/* Source */
+	{ 0x1c, KEY_SEARCH },		/* Scan */
+	{ 0x18, KEY_MUTE },		/* Mute */
+
+	{ 0x03, KEY_RADIO },		/* TV/FM */
 	/* The next four keys are duplicates that appear to send the
 	   same IR code as Ch+, Ch-, >>, and << .  The raw code assigned
 	   to them is the actual code + 0x20 - they will never be
 	   detected as such unless some way is discovered to distinguish
 	   these buttons from those that have the same code. */
-	[0x3f] = KEY_RIGHT,           /* |> and Ch+ */
-	[0x37] = KEY_LEFT,            /* <| and Ch- */
-	[0x2c] = KEY_UP,              /* ^^Up and >> */
-	[0x24] = KEY_DOWN,            /* vvDn and << */
-
-	[0x00] = KEY_RECORD,          /* Record */
-	[0x08] = KEY_STOP,            /* Stop */
-	[0x11] = KEY_PLAY,            /* Play */
-
-	[0x0f] = KEY_CLOSE,           /* Minimize */
-	[0x19] = KEY_ZOOM,            /* Zoom */
-	[0x1a] = KEY_SHUFFLE,         /* Snapshot */
-	[0x0d] = KEY_LANGUAGE,        /* MTS */
-
-	[0x14] = KEY_VOLUMEDOWN,      /* Vol- */
-	[0x16] = KEY_VOLUMEUP,        /* Vol+ */
-	[0x17] = KEY_CHANNELDOWN,     /* Ch- */
-	[0x1f] = KEY_CHANNELUP,       /* Ch+ */
+	{ 0x3f, KEY_RIGHT },		/* |> and Ch+ */
+	{ 0x37, KEY_LEFT },		/* <| and Ch- */
+	{ 0x2c, KEY_UP },		/* ^^Up and >> */
+	{ 0x24, KEY_DOWN },		/* vvDn and << */
+
+	{ 0x00, KEY_RECORD },		/* Record */
+	{ 0x08, KEY_STOP },		/* Stop */
+	{ 0x11, KEY_PLAY },		/* Play */
+
+	{ 0x0f, KEY_CLOSE },		/* Minimize */
+	{ 0x19, KEY_ZOOM },		/* Zoom */
+	{ 0x1a, KEY_CAMERA },		/* Snapshot */
+	{ 0x0d, KEY_LANGUAGE },		/* MTS */
+
+	{ 0x14, KEY_VOLUMEDOWN },	/* Vol- */
+	{ 0x16, KEY_VOLUMEUP },		/* Vol+ */
+	{ 0x17, KEY_CHANNELDOWN },	/* Ch- */
+	{ 0x1f, KEY_CHANNELUP },	/* Ch+ */
+
+	{ 0x04, KEY_REWIND },		/* << */
+	{ 0x0e, KEY_MENU },		/* Function */
+	{ 0x0c, KEY_FASTFORWARD },	/* >> */
+	{ 0x1d, KEY_RESTART },		/* Reset */
+};
 
-	[0x04] = KEY_REWIND,          /* << */
-	[0x0e] = KEY_MENU,            /* Function */
-	[0x0c] = KEY_FASTFORWARD,     /* >> */
-	[0x1d] = KEY_RESTART,         /* Reset */
+struct ir_scancode_table ir_codes_msi_tvanywhere_plus_table = {
+	.scan = ir_codes_msi_tvanywhere_plus,
+	.size = ARRAY_SIZE(ir_codes_msi_tvanywhere_plus),
 };
-EXPORT_SYMBOL_GPL(ir_codes_msi_tvanywhere_plus);
+EXPORT_SYMBOL_GPL(ir_codes_msi_tvanywhere_plus_table);
 
 /* ---------------------------------------------------------------------- */
 
 /* Cinergy 1400 DVB-T */
-IR_KEYTAB_TYPE ir_codes_cinergy_1400[IR_KEYTAB_SIZE] = {
-	[ 0x01 ] = KEY_POWER,
-	[ 0x02 ] = KEY_1,
-	[ 0x03 ] = KEY_2,
-	[ 0x04 ] = KEY_3,
-	[ 0x05 ] = KEY_4,
-	[ 0x06 ] = KEY_5,
-	[ 0x07 ] = KEY_6,
-	[ 0x08 ] = KEY_7,
-	[ 0x09 ] = KEY_8,
-	[ 0x0a ] = KEY_9,
-	[ 0x0c ] = KEY_0,
-
-	[ 0x0b ] = KEY_VIDEO,
-	[ 0x0d ] = KEY_REFRESH,
-	[ 0x0e ] = KEY_SELECT,
-	[ 0x0f ] = KEY_EPG,
-	[ 0x10 ] = KEY_UP,
-	[ 0x11 ] = KEY_LEFT,
-	[ 0x12 ] = KEY_OK,
-	[ 0x13 ] = KEY_RIGHT,
-	[ 0x14 ] = KEY_DOWN,
-	[ 0x15 ] = KEY_TEXT,
-	[ 0x16 ] = KEY_INFO,
-
-	[ 0x17 ] = KEY_RED,
-	[ 0x18 ] = KEY_GREEN,
-	[ 0x19 ] = KEY_YELLOW,
-	[ 0x1a ] = KEY_BLUE,
-
-	[ 0x1b ] = KEY_CHANNELUP,
-	[ 0x1c ] = KEY_VOLUMEUP,
-	[ 0x1d ] = KEY_MUTE,
-	[ 0x1e ] = KEY_VOLUMEDOWN,
-	[ 0x1f ] = KEY_CHANNELDOWN,
-
-	[ 0x40 ] = KEY_PAUSE,
-	[ 0x4c ] = KEY_PLAY,
-	[ 0x58 ] = KEY_RECORD,
-	[ 0x54 ] = KEY_PREVIOUS,
-	[ 0x48 ] = KEY_STOP,
-	[ 0x5c ] = KEY_NEXT,
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_cinergy_1400);
+static struct ir_scancode ir_codes_cinergy_1400[] = {
+	{ 0x01, KEY_POWER },
+	{ 0x02, KEY_1 },
+	{ 0x03, KEY_2 },
+	{ 0x04, KEY_3 },
+	{ 0x05, KEY_4 },
+	{ 0x06, KEY_5 },
+	{ 0x07, KEY_6 },
+	{ 0x08, KEY_7 },
+	{ 0x09, KEY_8 },
+	{ 0x0a, KEY_9 },
+	{ 0x0c, KEY_0 },
+
+	{ 0x0b, KEY_VIDEO },
+	{ 0x0d, KEY_REFRESH },
+	{ 0x0e, KEY_SELECT },
+	{ 0x0f, KEY_EPG },
+	{ 0x10, KEY_UP },
+	{ 0x11, KEY_LEFT },
+	{ 0x12, KEY_OK },
+	{ 0x13, KEY_RIGHT },
+	{ 0x14, KEY_DOWN },
+	{ 0x15, KEY_TEXT },
+	{ 0x16, KEY_INFO },
+
+	{ 0x17, KEY_RED },
+	{ 0x18, KEY_GREEN },
+	{ 0x19, KEY_YELLOW },
+	{ 0x1a, KEY_BLUE },
+
+	{ 0x1b, KEY_CHANNELUP },
+	{ 0x1c, KEY_VOLUMEUP },
+	{ 0x1d, KEY_MUTE },
+	{ 0x1e, KEY_VOLUMEDOWN },
+	{ 0x1f, KEY_CHANNELDOWN },
+
+	{ 0x40, KEY_PAUSE },
+	{ 0x4c, KEY_PLAY },
+	{ 0x58, KEY_RECORD },
+	{ 0x54, KEY_PREVIOUS },
+	{ 0x48, KEY_STOP },
+	{ 0x5c, KEY_NEXT },
+};
+
+struct ir_scancode_table ir_codes_cinergy_1400_table = {
+	.scan = ir_codes_cinergy_1400,
+	.size = ARRAY_SIZE(ir_codes_cinergy_1400),
+};
+EXPORT_SYMBOL_GPL(ir_codes_cinergy_1400_table);
 
 /* ---------------------------------------------------------------------- */
 
 /* AVERTV STUDIO 303 Remote */
-IR_KEYTAB_TYPE ir_codes_avertv_303[IR_KEYTAB_SIZE] = {
-	[ 0x2a ] = KEY_1,
-	[ 0x32 ] = KEY_2,
-	[ 0x3a ] = KEY_3,
-	[ 0x4a ] = KEY_4,
-	[ 0x52 ] = KEY_5,
-	[ 0x5a ] = KEY_6,
-	[ 0x6a ] = KEY_7,
-	[ 0x72 ] = KEY_8,
-	[ 0x7a ] = KEY_9,
-	[ 0x0e ] = KEY_0,
-
-	[ 0x02 ] = KEY_POWER,
-	[ 0x22 ] = KEY_VIDEO,
-	[ 0x42 ] = KEY_AUDIO,
-	[ 0x62 ] = KEY_ZOOM,
-	[ 0x0a ] = KEY_TV,
-	[ 0x12 ] = KEY_CD,
-	[ 0x1a ] = KEY_TEXT,
-
-	[ 0x16 ] = KEY_SUBTITLE,
-	[ 0x1e ] = KEY_REWIND,
-	[ 0x06 ] = KEY_PRINT,
-
-	[ 0x2e ] = KEY_SEARCH,
-	[ 0x36 ] = KEY_SLEEP,
-	[ 0x3e ] = KEY_SHUFFLE,
-	[ 0x26 ] = KEY_MUTE,
-
-	[ 0x4e ] = KEY_RECORD,
-	[ 0x56 ] = KEY_PAUSE,
-	[ 0x5e ] = KEY_STOP,
-	[ 0x46 ] = KEY_PLAY,
-
-	[ 0x6e ] = KEY_RED,
-	[ 0x0b ] = KEY_GREEN,
-	[ 0x66 ] = KEY_YELLOW,
-	[ 0x03 ] = KEY_BLUE,
-
-	[ 0x76 ] = KEY_LEFT,
-	[ 0x7e ] = KEY_RIGHT,
-	[ 0x13 ] = KEY_DOWN,
-	[ 0x1b ] = KEY_UP,
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_avertv_303);
+static struct ir_scancode ir_codes_avertv_303[] = {
+	{ 0x2a, KEY_1 },
+	{ 0x32, KEY_2 },
+	{ 0x3a, KEY_3 },
+	{ 0x4a, KEY_4 },
+	{ 0x52, KEY_5 },
+	{ 0x5a, KEY_6 },
+	{ 0x6a, KEY_7 },
+	{ 0x72, KEY_8 },
+	{ 0x7a, KEY_9 },
+	{ 0x0e, KEY_0 },
+
+	{ 0x02, KEY_POWER },
+	{ 0x22, KEY_VIDEO },
+	{ 0x42, KEY_AUDIO },
+	{ 0x62, KEY_ZOOM },
+	{ 0x0a, KEY_TV },
+	{ 0x12, KEY_CD },
+	{ 0x1a, KEY_TEXT },
+
+	{ 0x16, KEY_SUBTITLE },
+	{ 0x1e, KEY_REWIND },
+	{ 0x06, KEY_PRINT },
+
+	{ 0x2e, KEY_SEARCH },
+	{ 0x36, KEY_SLEEP },
+	{ 0x3e, KEY_SHUFFLE },
+	{ 0x26, KEY_MUTE },
+
+	{ 0x4e, KEY_RECORD },
+	{ 0x56, KEY_PAUSE },
+	{ 0x5e, KEY_STOP },
+	{ 0x46, KEY_PLAY },
+
+	{ 0x6e, KEY_RED },
+	{ 0x0b, KEY_GREEN },
+	{ 0x66, KEY_YELLOW },
+	{ 0x03, KEY_BLUE },
+
+	{ 0x76, KEY_LEFT },
+	{ 0x7e, KEY_RIGHT },
+	{ 0x13, KEY_DOWN },
+	{ 0x1b, KEY_UP },
+};
+
+struct ir_scancode_table ir_codes_avertv_303_table = {
+	.scan = ir_codes_avertv_303,
+	.size = ARRAY_SIZE(ir_codes_avertv_303),
+};
+EXPORT_SYMBOL_GPL(ir_codes_avertv_303_table);
 
 /* ---------------------------------------------------------------------- */
 
 /* DigitalNow DNTV Live! DVB-T Pro Remote */
-IR_KEYTAB_TYPE ir_codes_dntv_live_dvbt_pro[IR_KEYTAB_SIZE] = {
-	[ 0x16 ] = KEY_POWER,
-	[ 0x5b ] = KEY_HOME,
-
-	[ 0x55 ] = KEY_TV,		/* live tv */
-	[ 0x58 ] = KEY_TUNER,		/* digital Radio */
-	[ 0x5a ] = KEY_RADIO,		/* FM radio */
-	[ 0x59 ] = KEY_DVD,		/* dvd menu */
-	[ 0x03 ] = KEY_1,
-	[ 0x01 ] = KEY_2,
-	[ 0x06 ] = KEY_3,
-	[ 0x09 ] = KEY_4,
-	[ 0x1d ] = KEY_5,
-	[ 0x1f ] = KEY_6,
-	[ 0x0d ] = KEY_7,
-	[ 0x19 ] = KEY_8,
-	[ 0x1b ] = KEY_9,
-	[ 0x0c ] = KEY_CANCEL,
-	[ 0x15 ] = KEY_0,
-	[ 0x4a ] = KEY_CLEAR,
-	[ 0x13 ] = KEY_BACK,
-	[ 0x00 ] = KEY_TAB,
-	[ 0x4b ] = KEY_UP,
-	[ 0x4e ] = KEY_LEFT,
-	[ 0x4f ] = KEY_OK,
-	[ 0x52 ] = KEY_RIGHT,
-	[ 0x51 ] = KEY_DOWN,
-	[ 0x1e ] = KEY_VOLUMEUP,
-	[ 0x0a ] = KEY_VOLUMEDOWN,
-	[ 0x02 ] = KEY_CHANNELDOWN,
-	[ 0x05 ] = KEY_CHANNELUP,
-	[ 0x11 ] = KEY_RECORD,
-	[ 0x14 ] = KEY_PLAY,
-	[ 0x4c ] = KEY_PAUSE,
-	[ 0x1a ] = KEY_STOP,
-	[ 0x40 ] = KEY_REWIND,
-	[ 0x12 ] = KEY_FASTFORWARD,
-	[ 0x41 ] = KEY_PREVIOUSSONG,	/* replay |< */
-	[ 0x42 ] = KEY_NEXTSONG,	/* skip >| */
-	[ 0x54 ] = KEY_CAMERA,		/* capture */
-	[ 0x50 ] = KEY_LANGUAGE,	/* sap */
-	[ 0x47 ] = KEY_TV2,		/* pip */
-	[ 0x4d ] = KEY_SCREEN,
-	[ 0x43 ] = KEY_SUBTITLE,
-	[ 0x10 ] = KEY_MUTE,
-	[ 0x49 ] = KEY_AUDIO,		/* l/r */
-	[ 0x07 ] = KEY_SLEEP,
-	[ 0x08 ] = KEY_VIDEO,		/* a/v */
-	[ 0x0e ] = KEY_PREVIOUS,	/* recall */
-	[ 0x45 ] = KEY_ZOOM,		/* zoom + */
-	[ 0x46 ] = KEY_ANGLE,		/* zoom - */
-	[ 0x56 ] = KEY_RED,
-	[ 0x57 ] = KEY_GREEN,
-	[ 0x5c ] = KEY_YELLOW,
-	[ 0x5d ] = KEY_BLUE,
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_dntv_live_dvbt_pro);
-
-IR_KEYTAB_TYPE ir_codes_em_terratec[IR_KEYTAB_SIZE] = {
-	[ 0x01 ] = KEY_CHANNEL,
-	[ 0x02 ] = KEY_SELECT,
-	[ 0x03 ] = KEY_MUTE,
-	[ 0x04 ] = KEY_POWER,
-	[ 0x05 ] = KEY_1,
-	[ 0x06 ] = KEY_2,
-	[ 0x07 ] = KEY_3,
-	[ 0x08 ] = KEY_CHANNELUP,
-	[ 0x09 ] = KEY_4,
-	[ 0x0a ] = KEY_5,
-	[ 0x0b ] = KEY_6,
-	[ 0x0c ] = KEY_CHANNELDOWN,
-	[ 0x0d ] = KEY_7,
-	[ 0x0e ] = KEY_8,
-	[ 0x0f ] = KEY_9,
-	[ 0x10 ] = KEY_VOLUMEUP,
-	[ 0x11 ] = KEY_0,
-	[ 0x12 ] = KEY_MENU,
-	[ 0x13 ] = KEY_PRINT,
-	[ 0x14 ] = KEY_VOLUMEDOWN,
-	[ 0x16 ] = KEY_PAUSE,
-	[ 0x18 ] = KEY_RECORD,
-	[ 0x19 ] = KEY_REWIND,
-	[ 0x1a ] = KEY_PLAY,
-	[ 0x1b ] = KEY_FORWARD,
-	[ 0x1c ] = KEY_BACKSPACE,
-	[ 0x1e ] = KEY_STOP,
-	[ 0x40 ] = KEY_ZOOM,
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_em_terratec);
-
-IR_KEYTAB_TYPE ir_codes_pinnacle_grey[IR_KEYTAB_SIZE] = {
-	[ 0x3a ] = KEY_0,
-	[ 0x31 ] = KEY_1,
-	[ 0x32 ] = KEY_2,
-	[ 0x33 ] = KEY_3,
-	[ 0x34 ] = KEY_4,
-	[ 0x35 ] = KEY_5,
-	[ 0x36 ] = KEY_6,
-	[ 0x37 ] = KEY_7,
-	[ 0x38 ] = KEY_8,
-	[ 0x39 ] = KEY_9,
-
-	[ 0x2f ] = KEY_POWER,
-
-	[ 0x2e ] = KEY_P,
-	[ 0x1f ] = KEY_L,
-	[ 0x2b ] = KEY_I,
-
-	[ 0x2d ] = KEY_SCREEN,
-	[ 0x1e ] = KEY_ZOOM,
-	[ 0x1b ] = KEY_VOLUMEUP,
-	[ 0x0f ] = KEY_VOLUMEDOWN,
-	[ 0x17 ] = KEY_CHANNELUP,
-	[ 0x1c ] = KEY_CHANNELDOWN,
-	[ 0x25 ] = KEY_INFO,
-
-	[ 0x3c ] = KEY_MUTE,
-
-	[ 0x3d ] = KEY_LEFT,
-	[ 0x3b ] = KEY_RIGHT,
-
-	[ 0x3f ] = KEY_UP,
-	[ 0x3e ] = KEY_DOWN,
-	[ 0x1a ] = KEY_ENTER,
-
-	[ 0x1d ] = KEY_MENU,
-	[ 0x19 ] = KEY_AGAIN,
-	[ 0x16 ] = KEY_PREVIOUSSONG,
-	[ 0x13 ] = KEY_NEXTSONG,
-	[ 0x15 ] = KEY_PAUSE,
-	[ 0x0e ] = KEY_REWIND,
-	[ 0x0d ] = KEY_PLAY,
-	[ 0x0b ] = KEY_STOP,
-	[ 0x07 ] = KEY_FORWARD,
-	[ 0x27 ] = KEY_RECORD,
-	[ 0x26 ] = KEY_TUNER,
-	[ 0x29 ] = KEY_TEXT,
-	[ 0x2a ] = KEY_MEDIA,
-	[ 0x18 ] = KEY_EPG,
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_pinnacle_grey);
-
-IR_KEYTAB_TYPE ir_codes_flyvideo[IR_KEYTAB_SIZE] = {
-	[ 0x0f ] = KEY_0,
-	[ 0x03 ] = KEY_1,
-	[ 0x04 ] = KEY_2,
-	[ 0x05 ] = KEY_3,
-	[ 0x07 ] = KEY_4,
-	[ 0x08 ] = KEY_5,
-	[ 0x09 ] = KEY_6,
-	[ 0x0b ] = KEY_7,
-	[ 0x0c ] = KEY_8,
-	[ 0x0d ] = KEY_9,
-
-	[ 0x0e ] = KEY_MODE,         // Air/Cable
-	[ 0x11 ] = KEY_VIDEO,        // Video
-	[ 0x15 ] = KEY_AUDIO,        // Audio
-	[ 0x00 ] = KEY_POWER,        // Power
-	[ 0x18 ] = KEY_TUNER,        // AV Source
-	[ 0x02 ] = KEY_ZOOM,         // Fullscreen
-	[ 0x1a ] = KEY_LANGUAGE,     // Stereo
-	[ 0x1b ] = KEY_MUTE,         // Mute
-	[ 0x14 ] = KEY_VOLUMEUP,     // Volume +
-	[ 0x17 ] = KEY_VOLUMEDOWN,   // Volume -
-	[ 0x12 ] = KEY_CHANNELUP,    // Channel +
-	[ 0x13 ] = KEY_CHANNELDOWN,  // Channel -
-	[ 0x06 ] = KEY_AGAIN,        // Recall
-	[ 0x10 ] = KEY_ENTER,        // Enter
-
-	[ 0x19 ] = KEY_BACK,         // Rewind  ( <<< )
-	[ 0x1f ] = KEY_FORWARD,      // Forward ( >>> )
-	[ 0x0a ] = KEY_ANGLE,        // (no label, may be used as the PAUSE button)
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_flyvideo);
-
-IR_KEYTAB_TYPE ir_codes_flydvb[IR_KEYTAB_SIZE] = {
-	[ 0x01 ] = KEY_ZOOM,		// Full Screen
-	[ 0x00 ] = KEY_POWER,		// Power
-
-	[ 0x03 ] = KEY_1,
-	[ 0x04 ] = KEY_2,
-	[ 0x05 ] = KEY_3,
-	[ 0x07 ] = KEY_4,
-	[ 0x08 ] = KEY_5,
-	[ 0x09 ] = KEY_6,
-	[ 0x0b ] = KEY_7,
-	[ 0x0c ] = KEY_8,
-	[ 0x0d ] = KEY_9,
-	[ 0x06 ] = KEY_AGAIN,		// Recall
-	[ 0x0f ] = KEY_0,
-	[ 0x10 ] = KEY_MUTE,		// Mute
-	[ 0x02 ] = KEY_RADIO,		// TV/Radio
-	[ 0x1b ] = KEY_LANGUAGE,		// SAP (Second Audio Program)
-
-	[ 0x14 ] = KEY_VOLUMEUP,		// VOL+
-	[ 0x17 ] = KEY_VOLUMEDOWN,	// VOL-
-	[ 0x12 ] = KEY_CHANNELUP,		// CH+
-	[ 0x13 ] = KEY_CHANNELDOWN,	// CH-
-	[ 0x1d ] = KEY_ENTER,		// Enter
-
-	[ 0x1a ] = KEY_MODE,		// PIP
-	[ 0x18 ] = KEY_TUNER,		// Source
-
-	[ 0x1e ] = KEY_RECORD,		// Record/Pause
-	[ 0x15 ] = KEY_ANGLE,		// Swap (no label on key)
-	[ 0x1c ] = KEY_PAUSE,		// Timeshift/Pause
-	[ 0x19 ] = KEY_BACK,		// Rewind <<
-	[ 0x0a ] = KEY_PLAYPAUSE,		// Play/Pause
-	[ 0x1f ] = KEY_FORWARD,		// Forward >>
-	[ 0x16 ] = KEY_PREVIOUS,		// Back |<<
-	[ 0x11 ] = KEY_STOP,		// Stop
-	[ 0x0e ] = KEY_NEXT,		// End >>|
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_flydvb);
-
-IR_KEYTAB_TYPE ir_codes_cinergy[IR_KEYTAB_SIZE] = {
-	[ 0x00 ] = KEY_0,
-	[ 0x01 ] = KEY_1,
-	[ 0x02 ] = KEY_2,
-	[ 0x03 ] = KEY_3,
-	[ 0x04 ] = KEY_4,
-	[ 0x05 ] = KEY_5,
-	[ 0x06 ] = KEY_6,
-	[ 0x07 ] = KEY_7,
-	[ 0x08 ] = KEY_8,
-	[ 0x09 ] = KEY_9,
-
-	[ 0x0a ] = KEY_POWER,
-	[ 0x0b ] = KEY_PROG1,           // app
-	[ 0x0c ] = KEY_ZOOM,            // zoom/fullscreen
-	[ 0x0d ] = KEY_CHANNELUP,       // channel
-	[ 0x0e ] = KEY_CHANNELDOWN,     // channel-
-	[ 0x0f ] = KEY_VOLUMEUP,
-	[ 0x10 ] = KEY_VOLUMEDOWN,
-	[ 0x11 ] = KEY_TUNER,           // AV
-	[ 0x12 ] = KEY_NUMLOCK,         // -/--
-	[ 0x13 ] = KEY_AUDIO,           // audio
-	[ 0x14 ] = KEY_MUTE,
-	[ 0x15 ] = KEY_UP,
-	[ 0x16 ] = KEY_DOWN,
-	[ 0x17 ] = KEY_LEFT,
-	[ 0x18 ] = KEY_RIGHT,
-	[ 0x19 ] = BTN_LEFT,
-	[ 0x1a ] = BTN_RIGHT,
-	[ 0x1b ] = KEY_WWW,             // text
-	[ 0x1c ] = KEY_REWIND,
-	[ 0x1d ] = KEY_FORWARD,
-	[ 0x1e ] = KEY_RECORD,
-	[ 0x1f ] = KEY_PLAY,
-	[ 0x20 ] = KEY_PREVIOUSSONG,
-	[ 0x21 ] = KEY_NEXTSONG,
-	[ 0x22 ] = KEY_PAUSE,
-	[ 0x23 ] = KEY_STOP,
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_cinergy);
+static struct ir_scancode ir_codes_dntv_live_dvbt_pro[] = {
+	{ 0x16, KEY_POWER },
+	{ 0x5b, KEY_HOME },
+
+	{ 0x55, KEY_TV },		/* live tv */
+	{ 0x58, KEY_TUNER },		/* digital Radio */
+	{ 0x5a, KEY_RADIO },		/* FM radio */
+	{ 0x59, KEY_DVD },		/* dvd menu */
+	{ 0x03, KEY_1 },
+	{ 0x01, KEY_2 },
+	{ 0x06, KEY_3 },
+	{ 0x09, KEY_4 },
+	{ 0x1d, KEY_5 },
+	{ 0x1f, KEY_6 },
+	{ 0x0d, KEY_7 },
+	{ 0x19, KEY_8 },
+	{ 0x1b, KEY_9 },
+	{ 0x0c, KEY_CANCEL },
+	{ 0x15, KEY_0 },
+	{ 0x4a, KEY_CLEAR },
+	{ 0x13, KEY_BACK },
+	{ 0x00, KEY_TAB },
+	{ 0x4b, KEY_UP },
+	{ 0x4e, KEY_LEFT },
+	{ 0x4f, KEY_OK },
+	{ 0x52, KEY_RIGHT },
+	{ 0x51, KEY_DOWN },
+	{ 0x1e, KEY_VOLUMEUP },
+	{ 0x0a, KEY_VOLUMEDOWN },
+	{ 0x02, KEY_CHANNELDOWN },
+	{ 0x05, KEY_CHANNELUP },
+	{ 0x11, KEY_RECORD },
+	{ 0x14, KEY_PLAY },
+	{ 0x4c, KEY_PAUSE },
+	{ 0x1a, KEY_STOP },
+	{ 0x40, KEY_REWIND },
+	{ 0x12, KEY_FASTFORWARD },
+	{ 0x41, KEY_PREVIOUSSONG },	/* replay |< */
+	{ 0x42, KEY_NEXTSONG },		/* skip >| */
+	{ 0x54, KEY_CAMERA },		/* capture */
+	{ 0x50, KEY_LANGUAGE },		/* sap */
+	{ 0x47, KEY_TV2 },		/* pip */
+	{ 0x4d, KEY_SCREEN },
+	{ 0x43, KEY_SUBTITLE },
+	{ 0x10, KEY_MUTE },
+	{ 0x49, KEY_AUDIO },		/* l/r */
+	{ 0x07, KEY_SLEEP },
+	{ 0x08, KEY_VIDEO },		/* a/v */
+	{ 0x0e, KEY_PREVIOUS },		/* recall */
+	{ 0x45, KEY_ZOOM },		/* zoom + */
+	{ 0x46, KEY_ANGLE },		/* zoom - */
+	{ 0x56, KEY_RED },
+	{ 0x57, KEY_GREEN },
+	{ 0x5c, KEY_YELLOW },
+	{ 0x5d, KEY_BLUE },
+};
+
+struct ir_scancode_table ir_codes_dntv_live_dvbt_pro_table = {
+	.scan = ir_codes_dntv_live_dvbt_pro,
+	.size = ARRAY_SIZE(ir_codes_dntv_live_dvbt_pro),
+};
+EXPORT_SYMBOL_GPL(ir_codes_dntv_live_dvbt_pro_table);
+
+static struct ir_scancode ir_codes_em_terratec[] = {
+	{ 0x01, KEY_CHANNEL },
+	{ 0x02, KEY_SELECT },
+	{ 0x03, KEY_MUTE },
+	{ 0x04, KEY_POWER },
+	{ 0x05, KEY_1 },
+	{ 0x06, KEY_2 },
+	{ 0x07, KEY_3 },
+	{ 0x08, KEY_CHANNELUP },
+	{ 0x09, KEY_4 },
+	{ 0x0a, KEY_5 },
+	{ 0x0b, KEY_6 },
+	{ 0x0c, KEY_CHANNELDOWN },
+	{ 0x0d, KEY_7 },
+	{ 0x0e, KEY_8 },
+	{ 0x0f, KEY_9 },
+	{ 0x10, KEY_VOLUMEUP },
+	{ 0x11, KEY_0 },
+	{ 0x12, KEY_MENU },
+	{ 0x13, KEY_PRINT },
+	{ 0x14, KEY_VOLUMEDOWN },
+	{ 0x16, KEY_PAUSE },
+	{ 0x18, KEY_RECORD },
+	{ 0x19, KEY_REWIND },
+	{ 0x1a, KEY_PLAY },
+	{ 0x1b, KEY_FORWARD },
+	{ 0x1c, KEY_BACKSPACE },
+	{ 0x1e, KEY_STOP },
+	{ 0x40, KEY_ZOOM },
+};
+
+struct ir_scancode_table ir_codes_em_terratec_table = {
+	.scan = ir_codes_em_terratec,
+	.size = ARRAY_SIZE(ir_codes_em_terratec),
+};
+EXPORT_SYMBOL_GPL(ir_codes_em_terratec_table);
+
+static struct ir_scancode ir_codes_pinnacle_grey[] = {
+	{ 0x3a, KEY_0 },
+	{ 0x31, KEY_1 },
+	{ 0x32, KEY_2 },
+	{ 0x33, KEY_3 },
+	{ 0x34, KEY_4 },
+	{ 0x35, KEY_5 },
+	{ 0x36, KEY_6 },
+	{ 0x37, KEY_7 },
+	{ 0x38, KEY_8 },
+	{ 0x39, KEY_9 },
+
+	{ 0x2f, KEY_POWER },
+
+	{ 0x2e, KEY_P },
+	{ 0x1f, KEY_L },
+	{ 0x2b, KEY_I },
+
+	{ 0x2d, KEY_SCREEN },
+	{ 0x1e, KEY_ZOOM },
+	{ 0x1b, KEY_VOLUMEUP },
+	{ 0x0f, KEY_VOLUMEDOWN },
+	{ 0x17, KEY_CHANNELUP },
+	{ 0x1c, KEY_CHANNELDOWN },
+	{ 0x25, KEY_INFO },
+
+	{ 0x3c, KEY_MUTE },
+
+	{ 0x3d, KEY_LEFT },
+	{ 0x3b, KEY_RIGHT },
+
+	{ 0x3f, KEY_UP },
+	{ 0x3e, KEY_DOWN },
+	{ 0x1a, KEY_ENTER },
+
+	{ 0x1d, KEY_MENU },
+	{ 0x19, KEY_AGAIN },
+	{ 0x16, KEY_PREVIOUSSONG },
+	{ 0x13, KEY_NEXTSONG },
+	{ 0x15, KEY_PAUSE },
+	{ 0x0e, KEY_REWIND },
+	{ 0x0d, KEY_PLAY },
+	{ 0x0b, KEY_STOP },
+	{ 0x07, KEY_FORWARD },
+	{ 0x27, KEY_RECORD },
+	{ 0x26, KEY_TUNER },
+	{ 0x29, KEY_TEXT },
+	{ 0x2a, KEY_MEDIA },
+	{ 0x18, KEY_EPG },
+};
+
+struct ir_scancode_table ir_codes_pinnacle_grey_table = {
+	.scan = ir_codes_pinnacle_grey,
+	.size = ARRAY_SIZE(ir_codes_pinnacle_grey),
+};
+EXPORT_SYMBOL_GPL(ir_codes_pinnacle_grey_table);
+
+static struct ir_scancode ir_codes_flyvideo[] = {
+	{ 0x0f, KEY_0 },
+	{ 0x03, KEY_1 },
+	{ 0x04, KEY_2 },
+	{ 0x05, KEY_3 },
+	{ 0x07, KEY_4 },
+	{ 0x08, KEY_5 },
+	{ 0x09, KEY_6 },
+	{ 0x0b, KEY_7 },
+	{ 0x0c, KEY_8 },
+	{ 0x0d, KEY_9 },
+
+	{ 0x0e, KEY_MODE },	/* Air/Cable */
+	{ 0x11, KEY_VIDEO },	/* Video */
+	{ 0x15, KEY_AUDIO },	/* Audio */
+	{ 0x00, KEY_POWER },	/* Power */
+	{ 0x18, KEY_TUNER },	/* AV Source */
+	{ 0x02, KEY_ZOOM },	/* Fullscreen */
+	{ 0x1a, KEY_LANGUAGE },	/* Stereo */
+	{ 0x1b, KEY_MUTE },	/* Mute */
+	{ 0x14, KEY_VOLUMEUP },	/* Volume + */
+	{ 0x17, KEY_VOLUMEDOWN },/* Volume - */
+	{ 0x12, KEY_CHANNELUP },/* Channel + */
+	{ 0x13, KEY_CHANNELDOWN },/* Channel - */
+	{ 0x06, KEY_AGAIN },	/* Recall */
+	{ 0x10, KEY_ENTER },	/* Enter */
+
+	{ 0x19, KEY_BACK },	/* Rewind  ( <<< ) */
+	{ 0x1f, KEY_FORWARD },	/* Forward ( >>> ) */
+	{ 0x0a, KEY_ANGLE },	/* no label, may be used as the PAUSE button */
+};
+
+struct ir_scancode_table ir_codes_flyvideo_table = {
+	.scan = ir_codes_flyvideo,
+	.size = ARRAY_SIZE(ir_codes_flyvideo),
+};
+EXPORT_SYMBOL_GPL(ir_codes_flyvideo_table);
+
+static struct ir_scancode ir_codes_flydvb[] = {
+	{ 0x01, KEY_ZOOM },		/* Full Screen */
+	{ 0x00, KEY_POWER },		/* Power */
+
+	{ 0x03, KEY_1 },
+	{ 0x04, KEY_2 },
+	{ 0x05, KEY_3 },
+	{ 0x07, KEY_4 },
+	{ 0x08, KEY_5 },
+	{ 0x09, KEY_6 },
+	{ 0x0b, KEY_7 },
+	{ 0x0c, KEY_8 },
+	{ 0x0d, KEY_9 },
+	{ 0x06, KEY_AGAIN },		/* Recall */
+	{ 0x0f, KEY_0 },
+	{ 0x10, KEY_MUTE },		/* Mute */
+	{ 0x02, KEY_RADIO },		/* TV/Radio */
+	{ 0x1b, KEY_LANGUAGE },		/* SAP (Second Audio Program) */
+
+	{ 0x14, KEY_VOLUMEUP },		/* VOL+ */
+	{ 0x17, KEY_VOLUMEDOWN },	/* VOL- */
+	{ 0x12, KEY_CHANNELUP },	/* CH+ */
+	{ 0x13, KEY_CHANNELDOWN },	/* CH- */
+	{ 0x1d, KEY_ENTER },		/* Enter */
+
+	{ 0x1a, KEY_MODE },		/* PIP */
+	{ 0x18, KEY_TUNER },		/* Source */
+
+	{ 0x1e, KEY_RECORD },		/* Record/Pause */
+	{ 0x15, KEY_ANGLE },		/* Swap (no label on key) */
+	{ 0x1c, KEY_PAUSE },		/* Timeshift/Pause */
+	{ 0x19, KEY_BACK },		/* Rewind << */
+	{ 0x0a, KEY_PLAYPAUSE },	/* Play/Pause */
+	{ 0x1f, KEY_FORWARD },		/* Forward >> */
+	{ 0x16, KEY_PREVIOUS },		/* Back |<< */
+	{ 0x11, KEY_STOP },		/* Stop */
+	{ 0x0e, KEY_NEXT },		/* End >>| */
+};
+
+struct ir_scancode_table ir_codes_flydvb_table = {
+	.scan = ir_codes_flydvb,
+	.size = ARRAY_SIZE(ir_codes_flydvb),
+};
+EXPORT_SYMBOL_GPL(ir_codes_flydvb_table);
+
+static struct ir_scancode ir_codes_cinergy[] = {
+	{ 0x00, KEY_0 },
+	{ 0x01, KEY_1 },
+	{ 0x02, KEY_2 },
+	{ 0x03, KEY_3 },
+	{ 0x04, KEY_4 },
+	{ 0x05, KEY_5 },
+	{ 0x06, KEY_6 },
+	{ 0x07, KEY_7 },
+	{ 0x08, KEY_8 },
+	{ 0x09, KEY_9 },
+
+	{ 0x0a, KEY_POWER },
+	{ 0x0b, KEY_PROG1 },		/* app */
+	{ 0x0c, KEY_ZOOM },		/* zoom/fullscreen */
+	{ 0x0d, KEY_CHANNELUP },	/* channel */
+	{ 0x0e, KEY_CHANNELDOWN },	/* channel- */
+	{ 0x0f, KEY_VOLUMEUP },
+	{ 0x10, KEY_VOLUMEDOWN },
+	{ 0x11, KEY_TUNER },		/* AV */
+	{ 0x12, KEY_NUMLOCK },		/* -/-- */
+	{ 0x13, KEY_AUDIO },		/* audio */
+	{ 0x14, KEY_MUTE },
+	{ 0x15, KEY_UP },
+	{ 0x16, KEY_DOWN },
+	{ 0x17, KEY_LEFT },
+	{ 0x18, KEY_RIGHT },
+	{ 0x19, BTN_LEFT, },
+	{ 0x1a, BTN_RIGHT, },
+	{ 0x1b, KEY_WWW },		/* text */
+	{ 0x1c, KEY_REWIND },
+	{ 0x1d, KEY_FORWARD },
+	{ 0x1e, KEY_RECORD },
+	{ 0x1f, KEY_PLAY },
+	{ 0x20, KEY_PREVIOUSSONG },
+	{ 0x21, KEY_NEXTSONG },
+	{ 0x22, KEY_PAUSE },
+	{ 0x23, KEY_STOP },
+};
+
+struct ir_scancode_table ir_codes_cinergy_table = {
+	.scan = ir_codes_cinergy,
+	.size = ARRAY_SIZE(ir_codes_cinergy),
+};
+EXPORT_SYMBOL_GPL(ir_codes_cinergy_table);
 
 /* Alfons Geser <a.geser@cox.net>
  * updates from Job D. R. Borges <jobdrb@ig.com.br> */
-IR_KEYTAB_TYPE ir_codes_eztv[IR_KEYTAB_SIZE] = {
-	[ 0x12 ] = KEY_POWER,
-	[ 0x01 ] = KEY_TV,             // DVR
-	[ 0x15 ] = KEY_DVD,            // DVD
-	[ 0x17 ] = KEY_AUDIO,          // music
-				     // DVR mode / DVD mode / music mode
-
-	[ 0x1b ] = KEY_MUTE,           // mute
-	[ 0x02 ] = KEY_LANGUAGE,       // MTS/SAP / audio / autoseek
-	[ 0x1e ] = KEY_SUBTITLE,       // closed captioning / subtitle / seek
-	[ 0x16 ] = KEY_ZOOM,           // full screen
-	[ 0x1c ] = KEY_VIDEO,          // video source / eject / delall
-	[ 0x1d ] = KEY_RESTART,        // playback / angle / del
-	[ 0x2f ] = KEY_SEARCH,         // scan / menu / playlist
-	[ 0x30 ] = KEY_CHANNEL,        // CH surfing / bookmark / memo
-
-	[ 0x31 ] = KEY_HELP,           // help
-	[ 0x32 ] = KEY_MODE,           // num/memo
-	[ 0x33 ] = KEY_ESC,            // cancel
-
-	[ 0x0c ] = KEY_UP,             // up
-	[ 0x10 ] = KEY_DOWN,           // down
-	[ 0x08 ] = KEY_LEFT,           // left
-	[ 0x04 ] = KEY_RIGHT,          // right
-	[ 0x03 ] = KEY_SELECT,         // select
-
-	[ 0x1f ] = KEY_REWIND,         // rewind
-	[ 0x20 ] = KEY_PLAYPAUSE,      // play/pause
-	[ 0x29 ] = KEY_FORWARD,        // forward
-	[ 0x14 ] = KEY_AGAIN,          // repeat
-	[ 0x2b ] = KEY_RECORD,         // recording
-	[ 0x2c ] = KEY_STOP,           // stop
-	[ 0x2d ] = KEY_PLAY,           // play
-	[ 0x2e ] = KEY_SHUFFLE,        // snapshot / shuffle
-
-	[ 0x00 ] = KEY_0,
-	[ 0x05 ] = KEY_1,
-	[ 0x06 ] = KEY_2,
-	[ 0x07 ] = KEY_3,
-	[ 0x09 ] = KEY_4,
-	[ 0x0a ] = KEY_5,
-	[ 0x0b ] = KEY_6,
-	[ 0x0d ] = KEY_7,
-	[ 0x0e ] = KEY_8,
-	[ 0x0f ] = KEY_9,
-
-	[ 0x2a ] = KEY_VOLUMEUP,
-	[ 0x11 ] = KEY_VOLUMEDOWN,
-	[ 0x18 ] = KEY_CHANNELUP,      // CH.tracking up
-	[ 0x19 ] = KEY_CHANNELDOWN,    // CH.tracking down
-
-	[ 0x13 ] = KEY_ENTER,        // enter
-	[ 0x21 ] = KEY_DOT,          // . (decimal dot)
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_eztv);
+static struct ir_scancode ir_codes_eztv[] = {
+	{ 0x12, KEY_POWER },
+	{ 0x01, KEY_TV },	/* DVR */
+	{ 0x15, KEY_DVD },	/* DVD */
+	{ 0x17, KEY_AUDIO },	/* music */
+				/* DVR mode / DVD mode / music mode */
+
+	{ 0x1b, KEY_MUTE },	/* mute */
+	{ 0x02, KEY_LANGUAGE },	/* MTS/SAP / audio / autoseek */
+	{ 0x1e, KEY_SUBTITLE },	/* closed captioning / subtitle / seek */
+	{ 0x16, KEY_ZOOM },	/* full screen */
+	{ 0x1c, KEY_VIDEO },	/* video source / eject / delall */
+	{ 0x1d, KEY_RESTART },	/* playback / angle / del */
+	{ 0x2f, KEY_SEARCH },	/* scan / menu / playlist */
+	{ 0x30, KEY_CHANNEL },	/* CH surfing / bookmark / memo */
+
+	{ 0x31, KEY_HELP },	/* help */
+	{ 0x32, KEY_MODE },	/* num/memo */
+	{ 0x33, KEY_ESC },	/* cancel */
+
+	{ 0x0c, KEY_UP },	/* up */
+	{ 0x10, KEY_DOWN },	/* down */
+	{ 0x08, KEY_LEFT },	/* left */
+	{ 0x04, KEY_RIGHT },	/* right */
+	{ 0x03, KEY_SELECT },	/* select */
+
+	{ 0x1f, KEY_REWIND },	/* rewind */
+	{ 0x20, KEY_PLAYPAUSE },/* play/pause */
+	{ 0x29, KEY_FORWARD },	/* forward */
+	{ 0x14, KEY_AGAIN },	/* repeat */
+	{ 0x2b, KEY_RECORD },	/* recording */
+	{ 0x2c, KEY_STOP },	/* stop */
+	{ 0x2d, KEY_PLAY },	/* play */
+	{ 0x2e, KEY_CAMERA },	/* snapshot / shuffle */
+
+	{ 0x00, KEY_0 },
+	{ 0x05, KEY_1 },
+	{ 0x06, KEY_2 },
+	{ 0x07, KEY_3 },
+	{ 0x09, KEY_4 },
+	{ 0x0a, KEY_5 },
+	{ 0x0b, KEY_6 },
+	{ 0x0d, KEY_7 },
+	{ 0x0e, KEY_8 },
+	{ 0x0f, KEY_9 },
+
+	{ 0x2a, KEY_VOLUMEUP },
+	{ 0x11, KEY_VOLUMEDOWN },
+	{ 0x18, KEY_CHANNELUP },/* CH.tracking up */
+	{ 0x19, KEY_CHANNELDOWN },/* CH.tracking down */
+
+	{ 0x13, KEY_ENTER },	/* enter */
+	{ 0x21, KEY_DOT },	/* . (decimal dot) */
+};
+
+struct ir_scancode_table ir_codes_eztv_table = {
+	.scan = ir_codes_eztv,
+	.size = ARRAY_SIZE(ir_codes_eztv),
+};
+EXPORT_SYMBOL_GPL(ir_codes_eztv_table);
 
 /* Alex Hermann <gaaf@gmx.net> */
-IR_KEYTAB_TYPE ir_codes_avermedia[IR_KEYTAB_SIZE] = {
-	[ 0x28 ] = KEY_1,
-	[ 0x18 ] = KEY_2,
-	[ 0x38 ] = KEY_3,
-	[ 0x24 ] = KEY_4,
-	[ 0x14 ] = KEY_5,
-	[ 0x34 ] = KEY_6,
-	[ 0x2c ] = KEY_7,
-	[ 0x1c ] = KEY_8,
-	[ 0x3c ] = KEY_9,
-	[ 0x22 ] = KEY_0,
-
-	[ 0x20 ] = KEY_TV,		/* TV/FM */
-	[ 0x10 ] = KEY_CD,		/* CD */
-	[ 0x30 ] = KEY_TEXT,		/* TELETEXT */
-	[ 0x00 ] = KEY_POWER,		/* POWER */
-
-	[ 0x08 ] = KEY_VIDEO,		/* VIDEO */
-	[ 0x04 ] = KEY_AUDIO,		/* AUDIO */
-	[ 0x0c ] = KEY_ZOOM,		/* FULL SCREEN */
-
-	[ 0x12 ] = KEY_SUBTITLE,	/* DISPLAY */
-	[ 0x32 ] = KEY_REWIND,		/* LOOP	*/
-	[ 0x02 ] = KEY_PRINT,		/* PREVIEW */
-
-	[ 0x2a ] = KEY_SEARCH,		/* AUTOSCAN */
-	[ 0x1a ] = KEY_SLEEP,		/* FREEZE */
-	[ 0x3a ] = KEY_SHUFFLE,		/* SNAPSHOT */
-	[ 0x0a ] = KEY_MUTE,		/* MUTE */
-
-	[ 0x26 ] = KEY_RECORD,		/* RECORD */
-	[ 0x16 ] = KEY_PAUSE,		/* PAUSE */
-	[ 0x36 ] = KEY_STOP,		/* STOP */
-	[ 0x06 ] = KEY_PLAY,		/* PLAY */
-
-	[ 0x2e ] = KEY_RED,		/* RED */
-	[ 0x21 ] = KEY_GREEN,		/* GREEN */
-	[ 0x0e ] = KEY_YELLOW,		/* YELLOW */
-	[ 0x01 ] = KEY_BLUE,		/* BLUE */
-
-	[ 0x1e ] = KEY_VOLUMEDOWN,	/* VOLUME- */
-	[ 0x3e ] = KEY_VOLUMEUP,	/* VOLUME+ */
-	[ 0x11 ] = KEY_CHANNELDOWN,	/* CHANNEL/PAGE- */
-	[ 0x31 ] = KEY_CHANNELUP	/* CHANNEL/PAGE+ */
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_avermedia);
-
-IR_KEYTAB_TYPE ir_codes_videomate_tv_pvr[IR_KEYTAB_SIZE] = {
-	[ 0x14 ] = KEY_MUTE,
-	[ 0x24 ] = KEY_ZOOM,
-
-	[ 0x01 ] = KEY_DVD,
-	[ 0x23 ] = KEY_RADIO,
-	[ 0x00 ] = KEY_TV,
-
-	[ 0x0a ] = KEY_REWIND,
-	[ 0x08 ] = KEY_PLAYPAUSE,
-	[ 0x0f ] = KEY_FORWARD,
-
-	[ 0x02 ] = KEY_PREVIOUS,
-	[ 0x07 ] = KEY_STOP,
-	[ 0x06 ] = KEY_NEXT,
-
-	[ 0x0c ] = KEY_UP,
-	[ 0x0e ] = KEY_DOWN,
-	[ 0x0b ] = KEY_LEFT,
-	[ 0x0d ] = KEY_RIGHT,
-	[ 0x11 ] = KEY_OK,
-
-	[ 0x03 ] = KEY_MENU,
-	[ 0x09 ] = KEY_SETUP,
-	[ 0x05 ] = KEY_VIDEO,
-	[ 0x22 ] = KEY_CHANNEL,
-
-	[ 0x12 ] = KEY_VOLUMEUP,
-	[ 0x15 ] = KEY_VOLUMEDOWN,
-	[ 0x10 ] = KEY_CHANNELUP,
-	[ 0x13 ] = KEY_CHANNELDOWN,
-
-	[ 0x04 ] = KEY_RECORD,
-
-	[ 0x16 ] = KEY_1,
-	[ 0x17 ] = KEY_2,
-	[ 0x18 ] = KEY_3,
-	[ 0x19 ] = KEY_4,
-	[ 0x1a ] = KEY_5,
-	[ 0x1b ] = KEY_6,
-	[ 0x1c ] = KEY_7,
-	[ 0x1d ] = KEY_8,
-	[ 0x1e ] = KEY_9,
-	[ 0x1f ] = KEY_0,
-
-	[ 0x20 ] = KEY_LANGUAGE,
-	[ 0x21 ] = KEY_SLEEP,
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_videomate_tv_pvr);
+static struct ir_scancode ir_codes_avermedia[] = {
+	{ 0x28, KEY_1 },
+	{ 0x18, KEY_2 },
+	{ 0x38, KEY_3 },
+	{ 0x24, KEY_4 },
+	{ 0x14, KEY_5 },
+	{ 0x34, KEY_6 },
+	{ 0x2c, KEY_7 },
+	{ 0x1c, KEY_8 },
+	{ 0x3c, KEY_9 },
+	{ 0x22, KEY_0 },
+
+	{ 0x20, KEY_TV },		/* TV/FM */
+	{ 0x10, KEY_CD },		/* CD */
+	{ 0x30, KEY_TEXT },		/* TELETEXT */
+	{ 0x00, KEY_POWER },		/* POWER */
+
+	{ 0x08, KEY_VIDEO },		/* VIDEO */
+	{ 0x04, KEY_AUDIO },		/* AUDIO */
+	{ 0x0c, KEY_ZOOM },		/* FULL SCREEN */
+
+	{ 0x12, KEY_SUBTITLE },		/* DISPLAY */
+	{ 0x32, KEY_REWIND },		/* LOOP	*/
+	{ 0x02, KEY_PRINT },		/* PREVIEW */
+
+	{ 0x2a, KEY_SEARCH },		/* AUTOSCAN */
+	{ 0x1a, KEY_SLEEP },		/* FREEZE */
+	{ 0x3a, KEY_CAMERA },		/* SNAPSHOT */
+	{ 0x0a, KEY_MUTE },		/* MUTE */
+
+	{ 0x26, KEY_RECORD },		/* RECORD */
+	{ 0x16, KEY_PAUSE },		/* PAUSE */
+	{ 0x36, KEY_STOP },		/* STOP */
+	{ 0x06, KEY_PLAY },		/* PLAY */
+
+	{ 0x2e, KEY_RED },		/* RED */
+	{ 0x21, KEY_GREEN },		/* GREEN */
+	{ 0x0e, KEY_YELLOW },		/* YELLOW */
+	{ 0x01, KEY_BLUE },		/* BLUE */
+
+	{ 0x1e, KEY_VOLUMEDOWN },	/* VOLUME- */
+	{ 0x3e, KEY_VOLUMEUP },		/* VOLUME+ */
+	{ 0x11, KEY_CHANNELDOWN },	/* CHANNEL/PAGE- */
+	{ 0x31, KEY_CHANNELUP }		/* CHANNEL/PAGE+ */
+};
+
+struct ir_scancode_table ir_codes_avermedia_table = {
+	.scan = ir_codes_avermedia,
+	.size = ARRAY_SIZE(ir_codes_avermedia),
+};
+EXPORT_SYMBOL_GPL(ir_codes_avermedia_table);
+
+static struct ir_scancode ir_codes_videomate_tv_pvr[] = {
+	{ 0x14, KEY_MUTE },
+	{ 0x24, KEY_ZOOM },
+
+	{ 0x01, KEY_DVD },
+	{ 0x23, KEY_RADIO },
+	{ 0x00, KEY_TV },
+
+	{ 0x0a, KEY_REWIND },
+	{ 0x08, KEY_PLAYPAUSE },
+	{ 0x0f, KEY_FORWARD },
+
+	{ 0x02, KEY_PREVIOUS },
+	{ 0x07, KEY_STOP },
+	{ 0x06, KEY_NEXT },
+
+	{ 0x0c, KEY_UP },
+	{ 0x0e, KEY_DOWN },
+	{ 0x0b, KEY_LEFT },
+	{ 0x0d, KEY_RIGHT },
+	{ 0x11, KEY_OK },
+
+	{ 0x03, KEY_MENU },
+	{ 0x09, KEY_SETUP },
+	{ 0x05, KEY_VIDEO },
+	{ 0x22, KEY_CHANNEL },
+
+	{ 0x12, KEY_VOLUMEUP },
+	{ 0x15, KEY_VOLUMEDOWN },
+	{ 0x10, KEY_CHANNELUP },
+	{ 0x13, KEY_CHANNELDOWN },
+
+	{ 0x04, KEY_RECORD },
+
+	{ 0x16, KEY_1 },
+	{ 0x17, KEY_2 },
+	{ 0x18, KEY_3 },
+	{ 0x19, KEY_4 },
+	{ 0x1a, KEY_5 },
+	{ 0x1b, KEY_6 },
+	{ 0x1c, KEY_7 },
+	{ 0x1d, KEY_8 },
+	{ 0x1e, KEY_9 },
+	{ 0x1f, KEY_0 },
+
+	{ 0x20, KEY_LANGUAGE },
+	{ 0x21, KEY_SLEEP },
+};
+
+struct ir_scancode_table ir_codes_videomate_tv_pvr_table = {
+	.scan = ir_codes_videomate_tv_pvr,
+	.size = ARRAY_SIZE(ir_codes_videomate_tv_pvr),
+};
+EXPORT_SYMBOL_GPL(ir_codes_videomate_tv_pvr_table);
 
 /* Michael Tokarev <mjt@tls.msk.ru>
    http://www.corpit.ru/mjt/beholdTV/remote_control.jpg
-   keytable is used by MANLI MTV00[ 0x0c ] and BeholdTV 40[13] at
+   keytable is used by MANLI MTV00[0x0c] and BeholdTV 40[13] at
    least, and probably other cards too.
    The "ascii-art picture" below (in comments, first row
    is the keycode in hex, and subsequent row(s) shows
    the button labels (several variants when appropriate)
    helps to descide which keycodes to assign to the buttons.
  */
-IR_KEYTAB_TYPE ir_codes_manli[IR_KEYTAB_SIZE] = {
+static struct ir_scancode ir_codes_manli[] = {
 
 	/*  0x1c            0x12  *
 	 * FUNCTION         POWER *
 	 *   FM              (|)  *
 	 *                        */
-	[ 0x1c ] = KEY_RADIO,	/*XXX*/
-	[ 0x12 ] = KEY_POWER,
+	{ 0x1c, KEY_RADIO },	/*XXX*/
+	{ 0x12, KEY_POWER },
 
 	/*  0x01    0x02    0x03  *
 	 *   1       2       3    *
@@ -1248,29 +1339,29 @@ IR_KEYTAB_TYPE ir_codes_manli[IR_KEYTAB_SIZE] = {
 	 *  0x07    0x08    0x09  *
 	 *   7       8       9    *
 	 *                        */
-	[ 0x01 ] = KEY_1,
-	[ 0x02 ] = KEY_2,
-	[ 0x03 ] = KEY_3,
-	[ 0x04 ] = KEY_4,
-	[ 0x05 ] = KEY_5,
-	[ 0x06 ] = KEY_6,
-	[ 0x07 ] = KEY_7,
-	[ 0x08 ] = KEY_8,
-	[ 0x09 ] = KEY_9,
+	{ 0x01, KEY_1 },
+	{ 0x02, KEY_2 },
+	{ 0x03, KEY_3 },
+	{ 0x04, KEY_4 },
+	{ 0x05, KEY_5 },
+	{ 0x06, KEY_6 },
+	{ 0x07, KEY_7 },
+	{ 0x08, KEY_8 },
+	{ 0x09, KEY_9 },
 
 	/*  0x0a    0x00    0x17  *
 	 * RECALL    0      +100  *
 	 *                  PLUS  *
 	 *                        */
-	[ 0x0a ] = KEY_AGAIN,	/*XXX KEY_REWIND? */
-	[ 0x00 ] = KEY_0,
-	[ 0x17 ] = KEY_DIGITS,	/*XXX*/
+	{ 0x0a, KEY_AGAIN },	/*XXX KEY_REWIND? */
+	{ 0x00, KEY_0 },
+	{ 0x17, KEY_DIGITS },	/*XXX*/
 
 	/*  0x14            0x10  *
 	 *  MENU            INFO  *
 	 *  OSD                   */
-	[ 0x14 ] = KEY_MENU,
-	[ 0x10 ] = KEY_INFO,
+	{ 0x14, KEY_MENU },
+	{ 0x10, KEY_INFO },
 
 	/*          0x0b          *
 	 *           Up           *
@@ -1281,18 +1372,18 @@ IR_KEYTAB_TYPE ir_codes_manli[IR_KEYTAB_SIZE] = {
 	 *         0x015          *
 	 *         Down           *
 	 *                        */
-	[ 0x0b ] = KEY_UP,	/*XXX KEY_SCROLLUP? */
-	[ 0x18 ] = KEY_LEFT,	/*XXX KEY_BACK? */
-	[ 0x16 ] = KEY_OK,	/*XXX KEY_SELECT? KEY_ENTER? */
-	[ 0x0c ] = KEY_RIGHT,	/*XXX KEY_FORWARD? */
-	[ 0x15 ] = KEY_DOWN,	/*XXX KEY_SCROLLDOWN? */
+	{ 0x0b, KEY_UP },
+	{ 0x18, KEY_LEFT },
+	{ 0x16, KEY_OK },	/*XXX KEY_SELECT? KEY_ENTER? */
+	{ 0x0c, KEY_RIGHT },
+	{ 0x15, KEY_DOWN },
 
 	/*  0x11            0x0d  *
 	 *  TV/AV           MODE  *
 	 *  SOURCE         STEREO *
 	 *                        */
-	[ 0x11 ] = KEY_TV,	/*XXX*/
-	[ 0x0d ] = KEY_MODE,	/*XXX there's no KEY_STEREO */
+	{ 0x11, KEY_TV },	/*XXX*/
+	{ 0x0d, KEY_MODE },	/*XXX there's no KEY_STEREO	*/
 
 	/*  0x0f    0x1b    0x1a  *
 	 *  AUDIO   Vol+    Chan+ *
@@ -1301,891 +1392,967 @@ IR_KEYTAB_TYPE ir_codes_manli[IR_KEYTAB_SIZE] = {
 	 *  0x0e    0x1f    0x1e  *
 	 *  SLEEP   Vol-    Chan- *
 	 *                        */
-	[ 0x0f ] = KEY_AUDIO,
-	[ 0x1b ] = KEY_VOLUMEUP,
-	[ 0x1a ] = KEY_CHANNELUP,
-	[ 0x0e ] = KEY_SLEEP,	/*XXX maybe KEY_PAUSE */
-	[ 0x1f ] = KEY_VOLUMEDOWN,
-	[ 0x1e ] = KEY_CHANNELDOWN,
+	{ 0x0f, KEY_AUDIO },
+	{ 0x1b, KEY_VOLUMEUP },
+	{ 0x1a, KEY_CHANNELUP },
+	{ 0x0e, KEY_TIME },
+	{ 0x1f, KEY_VOLUMEDOWN },
+	{ 0x1e, KEY_CHANNELDOWN },
 
 	/*         0x13     0x19  *
 	 *         MUTE   SNAPSHOT*
 	 *                        */
-	[ 0x13 ] = KEY_MUTE,
-	[ 0x19 ] = KEY_RECORD,	/*XXX*/
+	{ 0x13, KEY_MUTE },
+	{ 0x19, KEY_CAMERA },
 
-	// 0x1d unused ?
+	/* 0x1d unused ? */
 };
 
-EXPORT_SYMBOL_GPL(ir_codes_manli);
+struct ir_scancode_table ir_codes_manli_table = {
+	.scan = ir_codes_manli,
+	.size = ARRAY_SIZE(ir_codes_manli),
+};
+EXPORT_SYMBOL_GPL(ir_codes_manli_table);
 
 /* Mike Baikov <mike@baikov.com> */
-IR_KEYTAB_TYPE ir_codes_gotview7135[IR_KEYTAB_SIZE] = {
-
-	[ 0x11 ] = KEY_POWER,
-	[ 0x35 ] = KEY_TV,
-	[ 0x1b ] = KEY_0,
-	[ 0x29 ] = KEY_1,
-	[ 0x19 ] = KEY_2,
-	[ 0x39 ] = KEY_3,
-	[ 0x1f ] = KEY_4,
-	[ 0x2c ] = KEY_5,
-	[ 0x21 ] = KEY_6,
-	[ 0x24 ] = KEY_7,
-	[ 0x18 ] = KEY_8,
-	[ 0x2b ] = KEY_9,
-	[ 0x3b ] = KEY_AGAIN, /* LOOP */
-	[ 0x06 ] = KEY_AUDIO,
-	[ 0x31 ] = KEY_PRINT, /* PREVIEW */
-	[ 0x3e ] = KEY_VIDEO,
-	[ 0x10 ] = KEY_CHANNELUP,
-	[ 0x20 ] = KEY_CHANNELDOWN,
-	[ 0x0c ] = KEY_VOLUMEDOWN,
-	[ 0x28 ] = KEY_VOLUMEUP,
-	[ 0x08 ] = KEY_MUTE,
-	[ 0x26 ] = KEY_SEARCH, /*SCAN*/
-	[ 0x3f ] = KEY_SHUFFLE, /* SNAPSHOT */
-	[ 0x12 ] = KEY_RECORD,
-	[ 0x32 ] = KEY_STOP,
-	[ 0x3c ] = KEY_PLAY,
-	[ 0x1d ] = KEY_REWIND,
-	[ 0x2d ] = KEY_PAUSE,
-	[ 0x0d ] = KEY_FORWARD,
-	[ 0x05 ] = KEY_ZOOM,  /*FULL*/
-
-	[ 0x2a ] = KEY_F21, /* LIVE TIMESHIFT */
-	[ 0x0e ] = KEY_F22, /* MIN TIMESHIFT */
-	[ 0x1e ] = KEY_F23, /* TIMESHIFT */
-	[ 0x38 ] = KEY_F24, /* NORMAL TIMESHIFT */
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_gotview7135);
-
-IR_KEYTAB_TYPE ir_codes_purpletv[IR_KEYTAB_SIZE] = {
-	[ 0x03 ] = KEY_POWER,
-	[ 0x6f ] = KEY_MUTE,
-	[ 0x10 ] = KEY_BACKSPACE,       /* Recall */
-
-	[ 0x11 ] = KEY_0,
-	[ 0x04 ] = KEY_1,
-	[ 0x05 ] = KEY_2,
-	[ 0x06 ] = KEY_3,
-	[ 0x08 ] = KEY_4,
-	[ 0x09 ] = KEY_5,
-	[ 0x0a ] = KEY_6,
-	[ 0x0c ] = KEY_7,
-	[ 0x0d ] = KEY_8,
-	[ 0x0e ] = KEY_9,
-	[ 0x12 ] = KEY_DOT,           /* 100+ */
-
-	[ 0x07 ] = KEY_VOLUMEUP,
-	[ 0x0b ] = KEY_VOLUMEDOWN,
-	[ 0x1a ] = KEY_KPPLUS,
-	[ 0x18 ] = KEY_KPMINUS,
-	[ 0x15 ] = KEY_UP,
-	[ 0x1d ] = KEY_DOWN,
-	[ 0x0f ] = KEY_CHANNELUP,
-	[ 0x13 ] = KEY_CHANNELDOWN,
-	[ 0x48 ] = KEY_ZOOM,
-
-	[ 0x1b ] = KEY_VIDEO,           /* Video source */
-	[ 0x49 ] = KEY_LANGUAGE,        /* MTS Select */
-	[ 0x19 ] = KEY_SEARCH,          /* Auto Scan */
-
-	[ 0x4b ] = KEY_RECORD,
-	[ 0x46 ] = KEY_PLAY,
-	[ 0x45 ] = KEY_PAUSE,           /* Pause */
-	[ 0x44 ] = KEY_STOP,
-	[ 0x40 ] = KEY_FORWARD,         /* Forward ? */
-	[ 0x42 ] = KEY_REWIND,          /* Backward ? */
-
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_purpletv);
+static struct ir_scancode ir_codes_gotview7135[] = {
+
+	{ 0x11, KEY_POWER },
+	{ 0x35, KEY_TV },
+	{ 0x1b, KEY_0 },
+	{ 0x29, KEY_1 },
+	{ 0x19, KEY_2 },
+	{ 0x39, KEY_3 },
+	{ 0x1f, KEY_4 },
+	{ 0x2c, KEY_5 },
+	{ 0x21, KEY_6 },
+	{ 0x24, KEY_7 },
+	{ 0x18, KEY_8 },
+	{ 0x2b, KEY_9 },
+	{ 0x3b, KEY_AGAIN },	/* LOOP */
+	{ 0x06, KEY_AUDIO },
+	{ 0x31, KEY_PRINT },	/* PREVIEW */
+	{ 0x3e, KEY_VIDEO },
+	{ 0x10, KEY_CHANNELUP },
+	{ 0x20, KEY_CHANNELDOWN },
+	{ 0x0c, KEY_VOLUMEDOWN },
+	{ 0x28, KEY_VOLUMEUP },
+	{ 0x08, KEY_MUTE },
+	{ 0x26, KEY_SEARCH },	/* SCAN */
+	{ 0x3f, KEY_CAMERA },	/* SNAPSHOT */
+	{ 0x12, KEY_RECORD },
+	{ 0x32, KEY_STOP },
+	{ 0x3c, KEY_PLAY },
+	{ 0x1d, KEY_REWIND },
+	{ 0x2d, KEY_PAUSE },
+	{ 0x0d, KEY_FORWARD },
+	{ 0x05, KEY_ZOOM },	/*FULL*/
+
+	{ 0x2a, KEY_F21 },	/* LIVE TIMESHIFT */
+	{ 0x0e, KEY_F22 },	/* MIN TIMESHIFT */
+	{ 0x1e, KEY_TIME },	/* TIMESHIFT */
+	{ 0x38, KEY_F24 },	/* NORMAL TIMESHIFT */
+};
+
+struct ir_scancode_table ir_codes_gotview7135_table = {
+	.scan = ir_codes_gotview7135,
+	.size = ARRAY_SIZE(ir_codes_gotview7135),
+};
+EXPORT_SYMBOL_GPL(ir_codes_gotview7135_table);
+
+static struct ir_scancode ir_codes_purpletv[] = {
+	{ 0x03, KEY_POWER },
+	{ 0x6f, KEY_MUTE },
+	{ 0x10, KEY_BACKSPACE },	/* Recall */
+
+	{ 0x11, KEY_0 },
+	{ 0x04, KEY_1 },
+	{ 0x05, KEY_2 },
+	{ 0x06, KEY_3 },
+	{ 0x08, KEY_4 },
+	{ 0x09, KEY_5 },
+	{ 0x0a, KEY_6 },
+	{ 0x0c, KEY_7 },
+	{ 0x0d, KEY_8 },
+	{ 0x0e, KEY_9 },
+	{ 0x12, KEY_DOT },	/* 100+ */
+
+	{ 0x07, KEY_VOLUMEUP },
+	{ 0x0b, KEY_VOLUMEDOWN },
+	{ 0x1a, KEY_KPPLUS },
+	{ 0x18, KEY_KPMINUS },
+	{ 0x15, KEY_UP },
+	{ 0x1d, KEY_DOWN },
+	{ 0x0f, KEY_CHANNELUP },
+	{ 0x13, KEY_CHANNELDOWN },
+	{ 0x48, KEY_ZOOM },
+
+	{ 0x1b, KEY_VIDEO },	/* Video source */
+	{ 0x1f, KEY_CAMERA },	/* Snapshot */
+	{ 0x49, KEY_LANGUAGE },	/* MTS Select */
+	{ 0x19, KEY_SEARCH },	/* Auto Scan */
+
+	{ 0x4b, KEY_RECORD },
+	{ 0x46, KEY_PLAY },
+	{ 0x45, KEY_PAUSE },	/* Pause */
+	{ 0x44, KEY_STOP },
+	{ 0x43, KEY_TIME },	/* Time Shift */
+	{ 0x17, KEY_CHANNEL },	/* SURF CH */
+	{ 0x40, KEY_FORWARD },	/* Forward ? */
+	{ 0x42, KEY_REWIND },	/* Backward ? */
+
+};
+
+struct ir_scancode_table ir_codes_purpletv_table = {
+	.scan = ir_codes_purpletv,
+	.size = ARRAY_SIZE(ir_codes_purpletv),
+};
+EXPORT_SYMBOL_GPL(ir_codes_purpletv_table);
 
 /* Mapping for the 28 key remote control as seen at
    http://www.sednacomputer.com/photo/cardbus-tv.jpg
    Pavel Mihaylov <bin@bash.info>
    Also for the remote bundled with Kozumi KTV-01C card */
-IR_KEYTAB_TYPE ir_codes_pctv_sedna[IR_KEYTAB_SIZE] = {
-	[ 0x00 ] = KEY_0,
-	[ 0x01 ] = KEY_1,
-	[ 0x02 ] = KEY_2,
-	[ 0x03 ] = KEY_3,
-	[ 0x04 ] = KEY_4,
-	[ 0x05 ] = KEY_5,
-	[ 0x06 ] = KEY_6,
-	[ 0x07 ] = KEY_7,
-	[ 0x08 ] = KEY_8,
-	[ 0x09 ] = KEY_9,
-
-	[ 0x0a ] = KEY_AGAIN,          /* Recall */
-	[ 0x0b ] = KEY_CHANNELUP,
-	[ 0x0c ] = KEY_VOLUMEUP,
-	[ 0x0d ] = KEY_MODE,           /* Stereo */
-	[ 0x0e ] = KEY_STOP,
-	[ 0x0f ] = KEY_PREVIOUSSONG,
-	[ 0x10 ] = KEY_ZOOM,
-	[ 0x11 ] = KEY_TUNER,          /* Source */
-	[ 0x12 ] = KEY_POWER,
-	[ 0x13 ] = KEY_MUTE,
-	[ 0x15 ] = KEY_CHANNELDOWN,
-	[ 0x18 ] = KEY_VOLUMEDOWN,
-	[ 0x19 ] = KEY_SHUFFLE,        /* Snapshot */
-	[ 0x1a ] = KEY_NEXTSONG,
-	[ 0x1b ] = KEY_TEXT,           /* Time Shift */
-	[ 0x1c ] = KEY_RADIO,          /* FM Radio */
-	[ 0x1d ] = KEY_RECORD,
-	[ 0x1e ] = KEY_PAUSE,
+static struct ir_scancode ir_codes_pctv_sedna[] = {
+	{ 0x00, KEY_0 },
+	{ 0x01, KEY_1 },
+	{ 0x02, KEY_2 },
+	{ 0x03, KEY_3 },
+	{ 0x04, KEY_4 },
+	{ 0x05, KEY_5 },
+	{ 0x06, KEY_6 },
+	{ 0x07, KEY_7 },
+	{ 0x08, KEY_8 },
+	{ 0x09, KEY_9 },
+
+	{ 0x0a, KEY_AGAIN },	/* Recall */
+	{ 0x0b, KEY_CHANNELUP },
+	{ 0x0c, KEY_VOLUMEUP },
+	{ 0x0d, KEY_MODE },	/* Stereo */
+	{ 0x0e, KEY_STOP },
+	{ 0x0f, KEY_PREVIOUSSONG },
+	{ 0x10, KEY_ZOOM },
+	{ 0x11, KEY_TUNER },	/* Source */
+	{ 0x12, KEY_POWER },
+	{ 0x13, KEY_MUTE },
+	{ 0x15, KEY_CHANNELDOWN },
+	{ 0x18, KEY_VOLUMEDOWN },
+	{ 0x19, KEY_CAMERA },	/* Snapshot */
+	{ 0x1a, KEY_NEXTSONG },
+	{ 0x1b, KEY_TIME },	/* Time Shift */
+	{ 0x1c, KEY_RADIO },	/* FM Radio */
+	{ 0x1d, KEY_RECORD },
+	{ 0x1e, KEY_PAUSE },
 	/* additional codes for Kozumi's remote */
-	[0x14] = KEY_INFO,        /* OSD */
-	[0x16] = KEY_OK,          /* OK */
-	[0x17] = KEY_DIGITS,      /* Plus */
-	[0x1f] = KEY_PLAY,        /* Play */
+	{ 0x14, KEY_INFO },	/* OSD */
+	{ 0x16, KEY_OK },	/* OK */
+	{ 0x17, KEY_DIGITS },	/* Plus */
+	{ 0x1f, KEY_PLAY },	/* Play */
 };
 
-EXPORT_SYMBOL_GPL(ir_codes_pctv_sedna);
+struct ir_scancode_table ir_codes_pctv_sedna_table = {
+	.scan = ir_codes_pctv_sedna,
+	.size = ARRAY_SIZE(ir_codes_pctv_sedna),
+};
+EXPORT_SYMBOL_GPL(ir_codes_pctv_sedna_table);
 
 /* Mark Phalan <phalanm@o2.ie> */
-IR_KEYTAB_TYPE ir_codes_pv951[IR_KEYTAB_SIZE] = {
-	[ 0x00 ] = KEY_0,
-	[ 0x01 ] = KEY_1,
-	[ 0x02 ] = KEY_2,
-	[ 0x03 ] = KEY_3,
-	[ 0x04 ] = KEY_4,
-	[ 0x05 ] = KEY_5,
-	[ 0x06 ] = KEY_6,
-	[ 0x07 ] = KEY_7,
-	[ 0x08 ] = KEY_8,
-	[ 0x09 ] = KEY_9,
-
-	[ 0x12 ] = KEY_POWER,
-	[ 0x10 ] = KEY_MUTE,
-	[ 0x1f ] = KEY_VOLUMEDOWN,
-	[ 0x1b ] = KEY_VOLUMEUP,
-	[ 0x1a ] = KEY_CHANNELUP,
-	[ 0x1e ] = KEY_CHANNELDOWN,
-	[ 0x0e ] = KEY_PAGEUP,
-	[ 0x1d ] = KEY_PAGEDOWN,
-	[ 0x13 ] = KEY_SOUND,
-
-	[ 0x18 ] = KEY_KPPLUSMINUS,	/* CH +/- */
-	[ 0x16 ] = KEY_SUBTITLE,		/* CC */
-	[ 0x0d ] = KEY_TEXT,		/* TTX */
-	[ 0x0b ] = KEY_TV,		/* AIR/CBL */
-	[ 0x11 ] = KEY_PC,		/* PC/TV */
-	[ 0x17 ] = KEY_OK,		/* CH RTN */
-	[ 0x19 ] = KEY_MODE, 		/* FUNC */
-	[ 0x0c ] = KEY_SEARCH, 		/* AUTOSCAN */
+static struct ir_scancode ir_codes_pv951[] = {
+	{ 0x00, KEY_0 },
+	{ 0x01, KEY_1 },
+	{ 0x02, KEY_2 },
+	{ 0x03, KEY_3 },
+	{ 0x04, KEY_4 },
+	{ 0x05, KEY_5 },
+	{ 0x06, KEY_6 },
+	{ 0x07, KEY_7 },
+	{ 0x08, KEY_8 },
+	{ 0x09, KEY_9 },
+
+	{ 0x12, KEY_POWER },
+	{ 0x10, KEY_MUTE },
+	{ 0x1f, KEY_VOLUMEDOWN },
+	{ 0x1b, KEY_VOLUMEUP },
+	{ 0x1a, KEY_CHANNELUP },
+	{ 0x1e, KEY_CHANNELDOWN },
+	{ 0x0e, KEY_PAGEUP },
+	{ 0x1d, KEY_PAGEDOWN },
+	{ 0x13, KEY_SOUND },
+
+	{ 0x18, KEY_KPPLUSMINUS },	/* CH +/- */
+	{ 0x16, KEY_SUBTITLE },		/* CC */
+	{ 0x0d, KEY_TEXT },		/* TTX */
+	{ 0x0b, KEY_TV },		/* AIR/CBL */
+	{ 0x11, KEY_PC },		/* PC/TV */
+	{ 0x17, KEY_OK },		/* CH RTN */
+	{ 0x19, KEY_MODE },		/* FUNC */
+	{ 0x0c, KEY_SEARCH },		/* AUTOSCAN */
 
 	/* Not sure what to do with these ones! */
-	[ 0x0f ] = KEY_SELECT, 		/* SOURCE */
-	[ 0x0a ] = KEY_KPPLUS,		/* +100 */
-	[ 0x14 ] = KEY_EQUAL,		/* SYNC */
-	[ 0x1c ] = KEY_MEDIA,             /* PC/TV */
+	{ 0x0f, KEY_SELECT },		/* SOURCE */
+	{ 0x0a, KEY_KPPLUS },		/* +100 */
+	{ 0x14, KEY_EQUAL },		/* SYNC */
+	{ 0x1c, KEY_MEDIA },		/* PC/TV */
 };
 
-EXPORT_SYMBOL_GPL(ir_codes_pv951);
+struct ir_scancode_table ir_codes_pv951_table = {
+	.scan = ir_codes_pv951,
+	.size = ARRAY_SIZE(ir_codes_pv951),
+};
+EXPORT_SYMBOL_GPL(ir_codes_pv951_table);
 
 /* generic RC5 keytable                                          */
 /* see http://users.pandora.be/nenya/electronics/rc5/codes00.htm */
 /* used by old (black) Hauppauge remotes                         */
-IR_KEYTAB_TYPE ir_codes_rc5_tv[IR_KEYTAB_SIZE] = {
+static struct ir_scancode ir_codes_rc5_tv[] = {
 	/* Keys 0 to 9 */
-	[ 0x00 ] = KEY_0,
-	[ 0x01 ] = KEY_1,
-	[ 0x02 ] = KEY_2,
-	[ 0x03 ] = KEY_3,
-	[ 0x04 ] = KEY_4,
-	[ 0x05 ] = KEY_5,
-	[ 0x06 ] = KEY_6,
-	[ 0x07 ] = KEY_7,
-	[ 0x08 ] = KEY_8,
-	[ 0x09 ] = KEY_9,
-
-	[ 0x0b ] = KEY_CHANNEL,		/* channel / program (japan: 11) */
-	[ 0x0c ] = KEY_POWER,		/* standby */
-	[ 0x0d ] = KEY_MUTE,		/* mute / demute */
-	[ 0x0f ] = KEY_TV,		/* display */
-	[ 0x10 ] = KEY_VOLUMEUP,
-	[ 0x11 ] = KEY_VOLUMEDOWN,
-	[ 0x12 ] = KEY_BRIGHTNESSUP,
-	[ 0x13 ] = KEY_BRIGHTNESSDOWN,
-	[ 0x1e ] = KEY_SEARCH,		/* search + */
-	[ 0x20 ] = KEY_CHANNELUP,	/* channel / program + */
-	[ 0x21 ] = KEY_CHANNELDOWN,	/* channel / program - */
-	[ 0x22 ] = KEY_CHANNEL,		/* alt / channel */
-	[ 0x23 ] = KEY_LANGUAGE,	/* 1st / 2nd language */
-	[ 0x26 ] = KEY_SLEEP,		/* sleeptimer */
-	[ 0x2e ] = KEY_MENU,		/* 2nd controls (USA: menu) */
-	[ 0x30 ] = KEY_PAUSE,
-	[ 0x32 ] = KEY_REWIND,
-	[ 0x33 ] = KEY_GOTO,
-	[ 0x35 ] = KEY_PLAY,
-	[ 0x36 ] = KEY_STOP,
-	[ 0x37 ] = KEY_RECORD,		/* recording */
-	[ 0x3c ] = KEY_TEXT,    	/* teletext submode (Japan: 12) */
-	[ 0x3d ] = KEY_SUSPEND,		/* system standby */
-
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_rc5_tv);
+	{ 0x00, KEY_0 },
+	{ 0x01, KEY_1 },
+	{ 0x02, KEY_2 },
+	{ 0x03, KEY_3 },
+	{ 0x04, KEY_4 },
+	{ 0x05, KEY_5 },
+	{ 0x06, KEY_6 },
+	{ 0x07, KEY_7 },
+	{ 0x08, KEY_8 },
+	{ 0x09, KEY_9 },
+
+	{ 0x0b, KEY_CHANNEL },		/* channel / program (japan: 11) */
+	{ 0x0c, KEY_POWER },		/* standby */
+	{ 0x0d, KEY_MUTE },		/* mute / demute */
+	{ 0x0f, KEY_TV },		/* display */
+	{ 0x10, KEY_VOLUMEUP },
+	{ 0x11, KEY_VOLUMEDOWN },
+	{ 0x12, KEY_BRIGHTNESSUP },
+	{ 0x13, KEY_BRIGHTNESSDOWN },
+	{ 0x1e, KEY_SEARCH },		/* search + */
+	{ 0x20, KEY_CHANNELUP },	/* channel / program + */
+	{ 0x21, KEY_CHANNELDOWN },	/* channel / program - */
+	{ 0x22, KEY_CHANNEL },		/* alt / channel */
+	{ 0x23, KEY_LANGUAGE },		/* 1st / 2nd language */
+	{ 0x26, KEY_SLEEP },		/* sleeptimer */
+	{ 0x2e, KEY_MENU },		/* 2nd controls (USA: menu) */
+	{ 0x30, KEY_PAUSE },
+	{ 0x32, KEY_REWIND },
+	{ 0x33, KEY_GOTO },
+	{ 0x35, KEY_PLAY },
+	{ 0x36, KEY_STOP },
+	{ 0x37, KEY_RECORD },		/* recording */
+	{ 0x3c, KEY_TEXT },		/* teletext submode (Japan: 12) */
+	{ 0x3d, KEY_SUSPEND },		/* system standby */
+
+};
+
+struct ir_scancode_table ir_codes_rc5_tv_table = {
+	.scan = ir_codes_rc5_tv,
+	.size = ARRAY_SIZE(ir_codes_rc5_tv),
+};
+EXPORT_SYMBOL_GPL(ir_codes_rc5_tv_table);
 
 /* Table for Leadtek Winfast Remote Controls - used by both bttv and cx88 */
-IR_KEYTAB_TYPE ir_codes_winfast[IR_KEYTAB_SIZE] = {
+static struct ir_scancode ir_codes_winfast[] = {
 	/* Keys 0 to 9 */
-	[ 0x12 ] = KEY_0,
-	[ 0x05 ] = KEY_1,
-	[ 0x06 ] = KEY_2,
-	[ 0x07 ] = KEY_3,
-	[ 0x09 ] = KEY_4,
-	[ 0x0a ] = KEY_5,
-	[ 0x0b ] = KEY_6,
-	[ 0x0d ] = KEY_7,
-	[ 0x0e ] = KEY_8,
-	[ 0x0f ] = KEY_9,
-
-	[ 0x00 ] = KEY_POWER,
-	[ 0x1b ] = KEY_AUDIO,           /* Audio Source */
-	[ 0x02 ] = KEY_TUNER,		/* TV/FM, not on Y0400052 */
-	[ 0x1e ] = KEY_VIDEO,           /* Video Source */
-	[ 0x16 ] = KEY_INFO,            /* Display information */
-	[ 0x04 ] = KEY_VOLUMEUP,
-	[ 0x08 ] = KEY_VOLUMEDOWN,
-	[ 0x0c ] = KEY_CHANNELUP,
-	[ 0x10 ] = KEY_CHANNELDOWN,
-	[ 0x03 ] = KEY_ZOOM,		/* fullscreen */
-	[ 0x1f ] = KEY_TEXT,		/* closed caption/teletext */
-	[ 0x20 ] = KEY_SLEEP,
-	[ 0x29 ] = KEY_CLEAR,           /* boss key */
-	[ 0x14 ] = KEY_MUTE,
-	[ 0x2b ] = KEY_RED,
-	[ 0x2c ] = KEY_GREEN,
-	[ 0x2d ] = KEY_YELLOW,
-	[ 0x2e ] = KEY_BLUE,
-	[ 0x18 ] = KEY_KPPLUS,		/* fine tune + , not on Y040052 */
-	[ 0x19 ] = KEY_KPMINUS,		/* fine tune - , not on Y040052 */
-	[ 0x2a ] = KEY_MEDIA,           /* PIP (Picture in picture */
-	[ 0x21 ] = KEY_DOT,
-	[ 0x13 ] = KEY_ENTER,
-	[ 0x11 ] = KEY_LAST,            /* Recall (last channel */
-	[ 0x22 ] = KEY_PREVIOUS,
-	[ 0x23 ] = KEY_PLAYPAUSE,
-	[ 0x24 ] = KEY_NEXT,
-	[ 0x25 ] = KEY_ARCHIVE,       /* Time Shifting */
-	[ 0x26 ] = KEY_STOP,
-	[ 0x27 ] = KEY_RECORD,
-	[ 0x28 ] = KEY_SAVE,          /* Screenshot */
-	[ 0x2f ] = KEY_MENU,
-	[ 0x30 ] = KEY_CANCEL,
-	[ 0x31 ] = KEY_CHANNEL,       /* Channel Surf */
-	[ 0x32 ] = KEY_SUBTITLE,
-	[ 0x33 ] = KEY_LANGUAGE,
-	[ 0x34 ] = KEY_REWIND,
-	[ 0x35 ] = KEY_FASTFORWARD,
-	[ 0x36 ] = KEY_TV,
-	[ 0x37 ] = KEY_RADIO,         /* FM */
-	[ 0x38 ] = KEY_DVD,
-
-	[ 0x3e ] = KEY_F21,           /* MCE +VOL, on Y04G0033 */
-	[ 0x3a ] = KEY_F22,           /* MCE -VOL, on Y04G0033 */
-	[ 0x3b ] = KEY_F23,           /* MCE +CH,  on Y04G0033 */
-	[ 0x3f ] = KEY_F24            /* MCE -CH,  on Y04G0033 */
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_winfast);
-
-IR_KEYTAB_TYPE ir_codes_pinnacle_color[IR_KEYTAB_SIZE] = {
-	[ 0x59 ] = KEY_MUTE,
-	[ 0x4a ] = KEY_POWER,
-
-	[ 0x18 ] = KEY_TEXT,
-	[ 0x26 ] = KEY_TV,
-	[ 0x3d ] = KEY_PRINT,
-
-	[ 0x48 ] = KEY_RED,
-	[ 0x04 ] = KEY_GREEN,
-	[ 0x11 ] = KEY_YELLOW,
-	[ 0x00 ] = KEY_BLUE,
-
-	[ 0x2d ] = KEY_VOLUMEUP,
-	[ 0x1e ] = KEY_VOLUMEDOWN,
-
-	[ 0x49 ] = KEY_MENU,
-
-	[ 0x16 ] = KEY_CHANNELUP,
-	[ 0x17 ] = KEY_CHANNELDOWN,
-
-	[ 0x20 ] = KEY_UP,
-	[ 0x21 ] = KEY_DOWN,
-	[ 0x22 ] = KEY_LEFT,
-	[ 0x23 ] = KEY_RIGHT,
-	[ 0x0d ] = KEY_SELECT,
-
-
-
-	[ 0x08 ] = KEY_BACK,
-	[ 0x07 ] = KEY_REFRESH,
-
-	[ 0x2f ] = KEY_ZOOM,
-	[ 0x29 ] = KEY_RECORD,
-
-	[ 0x4b ] = KEY_PAUSE,
-	[ 0x4d ] = KEY_REWIND,
-	[ 0x2e ] = KEY_PLAY,
-	[ 0x4e ] = KEY_FORWARD,
-	[ 0x53 ] = KEY_PREVIOUS,
-	[ 0x4c ] = KEY_STOP,
-	[ 0x54 ] = KEY_NEXT,
-
-	[ 0x69 ] = KEY_0,
-	[ 0x6a ] = KEY_1,
-	[ 0x6b ] = KEY_2,
-	[ 0x6c ] = KEY_3,
-	[ 0x6d ] = KEY_4,
-	[ 0x6e ] = KEY_5,
-	[ 0x6f ] = KEY_6,
-	[ 0x70 ] = KEY_7,
-	[ 0x71 ] = KEY_8,
-	[ 0x72 ] = KEY_9,
-
-	[ 0x74 ] = KEY_CHANNEL,
-	[ 0x0a ] = KEY_BACKSPACE,
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_pinnacle_color);
+	{ 0x12, KEY_0 },
+	{ 0x05, KEY_1 },
+	{ 0x06, KEY_2 },
+	{ 0x07, KEY_3 },
+	{ 0x09, KEY_4 },
+	{ 0x0a, KEY_5 },
+	{ 0x0b, KEY_6 },
+	{ 0x0d, KEY_7 },
+	{ 0x0e, KEY_8 },
+	{ 0x0f, KEY_9 },
+
+	{ 0x00, KEY_POWER },
+	{ 0x1b, KEY_AUDIO },		/* Audio Source */
+	{ 0x02, KEY_TUNER },		/* TV/FM, not on Y0400052 */
+	{ 0x1e, KEY_VIDEO },		/* Video Source */
+	{ 0x16, KEY_INFO },		/* Display information */
+	{ 0x04, KEY_VOLUMEUP },
+	{ 0x08, KEY_VOLUMEDOWN },
+	{ 0x0c, KEY_CHANNELUP },
+	{ 0x10, KEY_CHANNELDOWN },
+	{ 0x03, KEY_ZOOM },		/* fullscreen */
+	{ 0x1f, KEY_TEXT },		/* closed caption/teletext */
+	{ 0x20, KEY_SLEEP },
+	{ 0x29, KEY_CLEAR },		/* boss key */
+	{ 0x14, KEY_MUTE },
+	{ 0x2b, KEY_RED },
+	{ 0x2c, KEY_GREEN },
+	{ 0x2d, KEY_YELLOW },
+	{ 0x2e, KEY_BLUE },
+	{ 0x18, KEY_KPPLUS },		/* fine tune + , not on Y040052 */
+	{ 0x19, KEY_KPMINUS },		/* fine tune - , not on Y040052 */
+	{ 0x2a, KEY_MEDIA },		/* PIP (Picture in picture */
+	{ 0x21, KEY_DOT },
+	{ 0x13, KEY_ENTER },
+	{ 0x11, KEY_LAST },		/* Recall (last channel */
+	{ 0x22, KEY_PREVIOUS },
+	{ 0x23, KEY_PLAYPAUSE },
+	{ 0x24, KEY_NEXT },
+	{ 0x25, KEY_TIME },		/* Time Shifting */
+	{ 0x26, KEY_STOP },
+	{ 0x27, KEY_RECORD },
+	{ 0x28, KEY_SAVE },		/* Screenshot */
+	{ 0x2f, KEY_MENU },
+	{ 0x30, KEY_CANCEL },
+	{ 0x31, KEY_CHANNEL },		/* Channel Surf */
+	{ 0x32, KEY_SUBTITLE },
+	{ 0x33, KEY_LANGUAGE },
+	{ 0x34, KEY_REWIND },
+	{ 0x35, KEY_FASTFORWARD },
+	{ 0x36, KEY_TV },
+	{ 0x37, KEY_RADIO },		/* FM */
+	{ 0x38, KEY_DVD },
+
+	{ 0x3e, KEY_F21 },		/* MCE +VOL, on Y04G0033 */
+	{ 0x3a, KEY_F22 },		/* MCE -VOL, on Y04G0033 */
+	{ 0x3b, KEY_F23 },		/* MCE +CH,  on Y04G0033 */
+	{ 0x3f, KEY_F24 }		/* MCE -CH,  on Y04G0033 */
+};
+
+struct ir_scancode_table ir_codes_winfast_table = {
+	.scan = ir_codes_winfast,
+	.size = ARRAY_SIZE(ir_codes_winfast),
+};
+EXPORT_SYMBOL_GPL(ir_codes_winfast_table);
+
+static struct ir_scancode ir_codes_pinnacle_color[] = {
+	{ 0x59, KEY_MUTE },
+	{ 0x4a, KEY_POWER },
+
+	{ 0x18, KEY_TEXT },
+	{ 0x26, KEY_TV },
+	{ 0x3d, KEY_PRINT },
+
+	{ 0x48, KEY_RED },
+	{ 0x04, KEY_GREEN },
+	{ 0x11, KEY_YELLOW },
+	{ 0x00, KEY_BLUE },
+
+	{ 0x2d, KEY_VOLUMEUP },
+	{ 0x1e, KEY_VOLUMEDOWN },
+
+	{ 0x49, KEY_MENU },
+
+	{ 0x16, KEY_CHANNELUP },
+	{ 0x17, KEY_CHANNELDOWN },
+
+	{ 0x20, KEY_UP },
+	{ 0x21, KEY_DOWN },
+	{ 0x22, KEY_LEFT },
+	{ 0x23, KEY_RIGHT },
+	{ 0x0d, KEY_SELECT },
+
+	{ 0x08, KEY_BACK },
+	{ 0x07, KEY_REFRESH },
+
+	{ 0x2f, KEY_ZOOM },
+	{ 0x29, KEY_RECORD },
+
+	{ 0x4b, KEY_PAUSE },
+	{ 0x4d, KEY_REWIND },
+	{ 0x2e, KEY_PLAY },
+	{ 0x4e, KEY_FORWARD },
+	{ 0x53, KEY_PREVIOUS },
+	{ 0x4c, KEY_STOP },
+	{ 0x54, KEY_NEXT },
+
+	{ 0x69, KEY_0 },
+	{ 0x6a, KEY_1 },
+	{ 0x6b, KEY_2 },
+	{ 0x6c, KEY_3 },
+	{ 0x6d, KEY_4 },
+	{ 0x6e, KEY_5 },
+	{ 0x6f, KEY_6 },
+	{ 0x70, KEY_7 },
+	{ 0x71, KEY_8 },
+	{ 0x72, KEY_9 },
+
+	{ 0x74, KEY_CHANNEL },
+	{ 0x0a, KEY_BACKSPACE },
+};
+
+struct ir_scancode_table ir_codes_pinnacle_color_table = {
+	.scan = ir_codes_pinnacle_color,
+	.size = ARRAY_SIZE(ir_codes_pinnacle_color),
+};
+EXPORT_SYMBOL_GPL(ir_codes_pinnacle_color_table);
 
 /* Hauppauge: the newer, gray remotes (seems there are multiple
  * slightly different versions), shipped with cx88+ivtv cards.
  * almost rc5 coding, but some non-standard keys */
-IR_KEYTAB_TYPE ir_codes_hauppauge_new[IR_KEYTAB_SIZE] = {
+static struct ir_scancode ir_codes_hauppauge_new[] = {
 	/* Keys 0 to 9 */
-	[ 0x00 ] = KEY_0,
-	[ 0x01 ] = KEY_1,
-	[ 0x02 ] = KEY_2,
-	[ 0x03 ] = KEY_3,
-	[ 0x04 ] = KEY_4,
-	[ 0x05 ] = KEY_5,
-	[ 0x06 ] = KEY_6,
-	[ 0x07 ] = KEY_7,
-	[ 0x08 ] = KEY_8,
-	[ 0x09 ] = KEY_9,
-
-	[ 0x0a ] = KEY_TEXT,      	/* keypad asterisk as well */
-	[ 0x0b ] = KEY_RED,		/* red button */
-	[ 0x0c ] = KEY_RADIO,
-	[ 0x0d ] = KEY_MENU,
-	[ 0x0e ] = KEY_SUBTITLE,	/* also the # key */
-	[ 0x0f ] = KEY_MUTE,
-	[ 0x10 ] = KEY_VOLUMEUP,
-	[ 0x11 ] = KEY_VOLUMEDOWN,
-	[ 0x12 ] = KEY_PREVIOUS,	/* previous channel */
-	[ 0x14 ] = KEY_UP,
-	[ 0x15 ] = KEY_DOWN,
-	[ 0x16 ] = KEY_LEFT,
-	[ 0x17 ] = KEY_RIGHT,
-	[ 0x18 ] = KEY_VIDEO,		/* Videos */
-	[ 0x19 ] = KEY_AUDIO,		/* Music */
+	{ 0x00, KEY_0 },
+	{ 0x01, KEY_1 },
+	{ 0x02, KEY_2 },
+	{ 0x03, KEY_3 },
+	{ 0x04, KEY_4 },
+	{ 0x05, KEY_5 },
+	{ 0x06, KEY_6 },
+	{ 0x07, KEY_7 },
+	{ 0x08, KEY_8 },
+	{ 0x09, KEY_9 },
+
+	{ 0x0a, KEY_TEXT },		/* keypad asterisk as well */
+	{ 0x0b, KEY_RED },		/* red button */
+	{ 0x0c, KEY_RADIO },
+	{ 0x0d, KEY_MENU },
+	{ 0x0e, KEY_SUBTITLE },		/* also the # key */
+	{ 0x0f, KEY_MUTE },
+	{ 0x10, KEY_VOLUMEUP },
+	{ 0x11, KEY_VOLUMEDOWN },
+	{ 0x12, KEY_PREVIOUS },		/* previous channel */
+	{ 0x14, KEY_UP },
+	{ 0x15, KEY_DOWN },
+	{ 0x16, KEY_LEFT },
+	{ 0x17, KEY_RIGHT },
+	{ 0x18, KEY_VIDEO },		/* Videos */
+	{ 0x19, KEY_AUDIO },		/* Music */
 	/* 0x1a: Pictures - presume this means
 	   "Multimedia Home Platform" -
 	   no "PICTURES" key in input.h
 	 */
-	[ 0x1a ] = KEY_MHP,
-
-	[ 0x1b ] = KEY_EPG,		/* Guide */
-	[ 0x1c ] = KEY_TV,
-	[ 0x1e ] = KEY_NEXTSONG,	/* skip >| */
-	[ 0x1f ] = KEY_EXIT,		/* back/exit */
-	[ 0x20 ] = KEY_CHANNELUP,	/* channel / program + */
-	[ 0x21 ] = KEY_CHANNELDOWN,	/* channel / program - */
-	[ 0x22 ] = KEY_CHANNEL,		/* source (old black remote) */
-	[ 0x24 ] = KEY_PREVIOUSSONG,	/* replay |< */
-	[ 0x25 ] = KEY_ENTER,		/* OK */
-	[ 0x26 ] = KEY_SLEEP,		/* minimize (old black remote) */
-	[ 0x29 ] = KEY_BLUE,		/* blue key */
-	[ 0x2e ] = KEY_GREEN,		/* green button */
-	[ 0x30 ] = KEY_PAUSE,		/* pause */
-	[ 0x32 ] = KEY_REWIND,		/* backward << */
-	[ 0x34 ] = KEY_FASTFORWARD,	/* forward >> */
-	[ 0x35 ] = KEY_PLAY,
-	[ 0x36 ] = KEY_STOP,
-	[ 0x37 ] = KEY_RECORD,		/* recording */
-	[ 0x38 ] = KEY_YELLOW,		/* yellow key */
-	[ 0x3b ] = KEY_SELECT,		/* top right button */
-	[ 0x3c ] = KEY_ZOOM,		/* full */
-	[ 0x3d ] = KEY_POWER,		/* system power (green button) */
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_hauppauge_new);
-
-IR_KEYTAB_TYPE ir_codes_npgtech[IR_KEYTAB_SIZE] = {
-	[ 0x1d ] = KEY_SWITCHVIDEOMODE, /* switch inputs */
-	[ 0x2a ] = KEY_FRONT,
-
-	[ 0x3e ] = KEY_1,
-	[ 0x02 ] = KEY_2,
-	[ 0x06 ] = KEY_3,
-	[ 0x0a ] = KEY_4,
-	[ 0x0e ] = KEY_5,
-	[ 0x12 ] = KEY_6,
-	[ 0x16 ] = KEY_7,
-	[ 0x1a ] = KEY_8,
-	[ 0x1e ] = KEY_9,
-	[ 0x3a ] = KEY_0,
-	[ 0x22 ] = KEY_NUMLOCK,         /* -/-- */
-	[ 0x20 ] = KEY_REFRESH,
-
-	[ 0x03 ] = KEY_BRIGHTNESSDOWN,
-	[ 0x28 ] = KEY_AUDIO,
-	[ 0x3c ] = KEY_UP,
-	[ 0x3f ] = KEY_LEFT,
-	[ 0x2e ] = KEY_MUTE,
-	[ 0x3b ] = KEY_RIGHT,
-	[ 0x00 ] = KEY_DOWN,
-	[ 0x07 ] = KEY_BRIGHTNESSUP,
-	[ 0x2c ] = KEY_TEXT,
-
-	[ 0x37 ] = KEY_RECORD,
-	[ 0x17 ] = KEY_PLAY,
-	[ 0x13 ] = KEY_PAUSE,
-	[ 0x26 ] = KEY_STOP,
-	[ 0x18 ] = KEY_FASTFORWARD,
-	[ 0x14 ] = KEY_REWIND,
-	[ 0x33 ] = KEY_ZOOM,
-	[ 0x32 ] = KEY_KEYBOARD,
-	[ 0x30 ] = KEY_GOTO,            /* Pointing arrow */
-	[ 0x36 ] = KEY_MACRO,           /* Maximize/Minimize (yellow) */
-	[ 0x0b ] = KEY_RADIO,
-	[ 0x10 ] = KEY_POWER,
-
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_npgtech);
+	{ 0x1a, KEY_MHP },
+
+	{ 0x1b, KEY_EPG },		/* Guide */
+	{ 0x1c, KEY_TV },
+	{ 0x1e, KEY_NEXTSONG },		/* skip >| */
+	{ 0x1f, KEY_EXIT },		/* back/exit */
+	{ 0x20, KEY_CHANNELUP },	/* channel / program + */
+	{ 0x21, KEY_CHANNELDOWN },	/* channel / program - */
+	{ 0x22, KEY_CHANNEL },		/* source (old black remote) */
+	{ 0x24, KEY_PREVIOUSSONG },	/* replay |< */
+	{ 0x25, KEY_ENTER },		/* OK */
+	{ 0x26, KEY_SLEEP },		/* minimize (old black remote) */
+	{ 0x29, KEY_BLUE },		/* blue key */
+	{ 0x2e, KEY_GREEN },		/* green button */
+	{ 0x30, KEY_PAUSE },		/* pause */
+	{ 0x32, KEY_REWIND },		/* backward << */
+	{ 0x34, KEY_FASTFORWARD },	/* forward >> */
+	{ 0x35, KEY_PLAY },
+	{ 0x36, KEY_STOP },
+	{ 0x37, KEY_RECORD },		/* recording */
+	{ 0x38, KEY_YELLOW },		/* yellow key */
+	{ 0x3b, KEY_SELECT },		/* top right button */
+	{ 0x3c, KEY_ZOOM },		/* full */
+	{ 0x3d, KEY_POWER },		/* system power (green button) */
+};
+
+struct ir_scancode_table ir_codes_hauppauge_new_table = {
+	.scan = ir_codes_hauppauge_new,
+	.size = ARRAY_SIZE(ir_codes_hauppauge_new),
+};
+EXPORT_SYMBOL_GPL(ir_codes_hauppauge_new_table);
+
+static struct ir_scancode ir_codes_npgtech[] = {
+	{ 0x1d, KEY_SWITCHVIDEOMODE },	/* switch inputs */
+	{ 0x2a, KEY_FRONT },
+
+	{ 0x3e, KEY_1 },
+	{ 0x02, KEY_2 },
+	{ 0x06, KEY_3 },
+	{ 0x0a, KEY_4 },
+	{ 0x0e, KEY_5 },
+	{ 0x12, KEY_6 },
+	{ 0x16, KEY_7 },
+	{ 0x1a, KEY_8 },
+	{ 0x1e, KEY_9 },
+	{ 0x3a, KEY_0 },
+	{ 0x22, KEY_NUMLOCK },		/* -/-- */
+	{ 0x20, KEY_REFRESH },
+
+	{ 0x03, KEY_BRIGHTNESSDOWN },
+	{ 0x28, KEY_AUDIO },
+	{ 0x3c, KEY_CHANNELUP },
+	{ 0x3f, KEY_VOLUMEDOWN },
+	{ 0x2e, KEY_MUTE },
+	{ 0x3b, KEY_VOLUMEUP },
+	{ 0x00, KEY_CHANNELDOWN },
+	{ 0x07, KEY_BRIGHTNESSUP },
+	{ 0x2c, KEY_TEXT },
+
+	{ 0x37, KEY_RECORD },
+	{ 0x17, KEY_PLAY },
+	{ 0x13, KEY_PAUSE },
+	{ 0x26, KEY_STOP },
+	{ 0x18, KEY_FASTFORWARD },
+	{ 0x14, KEY_REWIND },
+	{ 0x33, KEY_ZOOM },
+	{ 0x32, KEY_KEYBOARD },
+	{ 0x30, KEY_GOTO },		/* Pointing arrow */
+	{ 0x36, KEY_MACRO },		/* Maximize/Minimize (yellow) */
+	{ 0x0b, KEY_RADIO },
+	{ 0x10, KEY_POWER },
+
+};
+
+struct ir_scancode_table ir_codes_npgtech_table = {
+	.scan = ir_codes_npgtech,
+	.size = ARRAY_SIZE(ir_codes_npgtech),
+};
+EXPORT_SYMBOL_GPL(ir_codes_npgtech_table);
 
 /* Norwood Micro (non-Pro) TV Tuner
    By Peter Naulls <peter@chocky.org>
    Key comments are the functions given in the manual */
-IR_KEYTAB_TYPE ir_codes_norwood[IR_KEYTAB_SIZE] = {
+static struct ir_scancode ir_codes_norwood[] = {
 	/* Keys 0 to 9 */
-	[ 0x20 ] = KEY_0,
-	[ 0x21 ] = KEY_1,
-	[ 0x22 ] = KEY_2,
-	[ 0x23 ] = KEY_3,
-	[ 0x24 ] = KEY_4,
-	[ 0x25 ] = KEY_5,
-	[ 0x26 ] = KEY_6,
-	[ 0x27 ] = KEY_7,
-	[ 0x28 ] = KEY_8,
-	[ 0x29 ] = KEY_9,
-
-	[ 0x78 ] = KEY_TUNER,             /* Video Source        */
-	[ 0x2c ] = KEY_EXIT,              /* Open/Close software */
-	[ 0x2a ] = KEY_SELECT,            /* 2 Digit Select      */
-	[ 0x69 ] = KEY_AGAIN,             /* Recall              */
-
-	[ 0x32 ] = KEY_BRIGHTNESSUP,      /* Brightness increase */
-	[ 0x33 ] = KEY_BRIGHTNESSDOWN,    /* Brightness decrease */
-	[ 0x6b ] = KEY_KPPLUS,            /* (not named >>>>>)   */
-	[ 0x6c ] = KEY_KPMINUS,           /* (not named <<<<<)   */
-
-	[ 0x2d ] = KEY_MUTE,              /* Mute                */
-	[ 0x30 ] = KEY_VOLUMEUP,          /* Volume up           */
-	[ 0x31 ] = KEY_VOLUMEDOWN,        /* Volume down         */
-	[ 0x60 ] = KEY_CHANNELUP,         /* Channel up          */
-	[ 0x61 ] = KEY_CHANNELDOWN,       /* Channel down        */
-
-	[ 0x3f ] = KEY_RECORD,            /* Record              */
-	[ 0x37 ] = KEY_PLAY,              /* Play                */
-	[ 0x36 ] = KEY_PAUSE,             /* Pause               */
-	[ 0x2b ] = KEY_STOP,              /* Stop                */
-	[ 0x67 ] = KEY_FASTFORWARD,       /* Foward              */
-	[ 0x66 ] = KEY_REWIND,            /* Rewind              */
-	[ 0x3e ] = KEY_SEARCH,            /* Auto Scan           */
-	[ 0x2e ] = KEY_CAMERA,            /* Capture Video       */
-	[ 0x6d ] = KEY_MENU,              /* Show/Hide Control   */
-	[ 0x2f ] = KEY_ZOOM,              /* Full Screen         */
-	[ 0x34 ] = KEY_RADIO,             /* FM                  */
-	[ 0x65 ] = KEY_POWER,             /* Computer power      */
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_norwood);
+	{ 0x20, KEY_0 },
+	{ 0x21, KEY_1 },
+	{ 0x22, KEY_2 },
+	{ 0x23, KEY_3 },
+	{ 0x24, KEY_4 },
+	{ 0x25, KEY_5 },
+	{ 0x26, KEY_6 },
+	{ 0x27, KEY_7 },
+	{ 0x28, KEY_8 },
+	{ 0x29, KEY_9 },
+
+	{ 0x78, KEY_TUNER },		/* Video Source        */
+	{ 0x2c, KEY_EXIT },		/* Open/Close software */
+	{ 0x2a, KEY_SELECT },		/* 2 Digit Select      */
+	{ 0x69, KEY_AGAIN },		/* Recall              */
+
+	{ 0x32, KEY_BRIGHTNESSUP },	/* Brightness increase */
+	{ 0x33, KEY_BRIGHTNESSDOWN },	/* Brightness decrease */
+	{ 0x6b, KEY_KPPLUS },		/* (not named >>>>>)   */
+	{ 0x6c, KEY_KPMINUS },		/* (not named <<<<<)   */
+
+	{ 0x2d, KEY_MUTE },		/* Mute                */
+	{ 0x30, KEY_VOLUMEUP },		/* Volume up           */
+	{ 0x31, KEY_VOLUMEDOWN },	/* Volume down         */
+	{ 0x60, KEY_CHANNELUP },	/* Channel up          */
+	{ 0x61, KEY_CHANNELDOWN },	/* Channel down        */
+
+	{ 0x3f, KEY_RECORD },		/* Record              */
+	{ 0x37, KEY_PLAY },		/* Play                */
+	{ 0x36, KEY_PAUSE },		/* Pause               */
+	{ 0x2b, KEY_STOP },		/* Stop                */
+	{ 0x67, KEY_FASTFORWARD },	/* Foward              */
+	{ 0x66, KEY_REWIND },		/* Rewind              */
+	{ 0x3e, KEY_SEARCH },		/* Auto Scan           */
+	{ 0x2e, KEY_CAMERA },		/* Capture Video       */
+	{ 0x6d, KEY_MENU },		/* Show/Hide Control   */
+	{ 0x2f, KEY_ZOOM },		/* Full Screen         */
+	{ 0x34, KEY_RADIO },		/* FM                  */
+	{ 0x65, KEY_POWER },		/* Computer power      */
+};
+
+struct ir_scancode_table ir_codes_norwood_table = {
+	.scan = ir_codes_norwood,
+	.size = ARRAY_SIZE(ir_codes_norwood),
+};
+EXPORT_SYMBOL_GPL(ir_codes_norwood_table);
 
 /* From reading the following remotes:
  * Zenith Universal 7 / TV Mode 807 / VCR Mode 837
  * Hauppauge (from NOVA-CI-s box product)
  * This is a "middle of the road" approach, differences are noted
  */
-IR_KEYTAB_TYPE ir_codes_budget_ci_old[IR_KEYTAB_SIZE] = {
-	[ 0x00 ] = KEY_0,
-	[ 0x01 ] = KEY_1,
-	[ 0x02 ] = KEY_2,
-	[ 0x03 ] = KEY_3,
-	[ 0x04 ] = KEY_4,
-	[ 0x05 ] = KEY_5,
-	[ 0x06 ] = KEY_6,
-	[ 0x07 ] = KEY_7,
-	[ 0x08 ] = KEY_8,
-	[ 0x09 ] = KEY_9,
-	[ 0x0a ] = KEY_ENTER,
-	[ 0x0b ] = KEY_RED,
-	[ 0x0c ] = KEY_POWER,             /* RADIO on Hauppauge */
-	[ 0x0d ] = KEY_MUTE,
-	[ 0x0f ] = KEY_A,                 /* TV on Hauppauge */
-	[ 0x10 ] = KEY_VOLUMEUP,
-	[ 0x11 ] = KEY_VOLUMEDOWN,
-	[ 0x14 ] = KEY_B,
-	[ 0x1c ] = KEY_UP,
-	[ 0x1d ] = KEY_DOWN,
-	[ 0x1e ] = KEY_OPTION,            /* RESERVED on Hauppauge */
-	[ 0x1f ] = KEY_BREAK,
-	[ 0x20 ] = KEY_CHANNELUP,
-	[ 0x21 ] = KEY_CHANNELDOWN,
-	[ 0x22 ] = KEY_PREVIOUS,          /* Prev. Ch on Zenith, SOURCE on Hauppauge */
-	[ 0x24 ] = KEY_RESTART,
-	[ 0x25 ] = KEY_OK,
-	[ 0x26 ] = KEY_CYCLEWINDOWS,      /* MINIMIZE on Hauppauge */
-	[ 0x28 ] = KEY_ENTER,             /* VCR mode on Zenith */
-	[ 0x29 ] = KEY_PAUSE,
-	[ 0x2b ] = KEY_RIGHT,
-	[ 0x2c ] = KEY_LEFT,
-	[ 0x2e ] = KEY_MENU,              /* FULL SCREEN on Hauppauge */
-	[ 0x30 ] = KEY_SLOW,
-	[ 0x31 ] = KEY_PREVIOUS,          /* VCR mode on Zenith */
-	[ 0x32 ] = KEY_REWIND,
-	[ 0x34 ] = KEY_FASTFORWARD,
-	[ 0x35 ] = KEY_PLAY,
-	[ 0x36 ] = KEY_STOP,
-	[ 0x37 ] = KEY_RECORD,
-	[ 0x38 ] = KEY_TUNER,             /* TV/VCR on Zenith */
-	[ 0x3a ] = KEY_C,
-	[ 0x3c ] = KEY_EXIT,
-	[ 0x3d ] = KEY_POWER2,
-	[ 0x3e ] = KEY_TUNER,
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_budget_ci_old);
+static struct ir_scancode ir_codes_budget_ci_old[] = {
+	{ 0x00, KEY_0 },
+	{ 0x01, KEY_1 },
+	{ 0x02, KEY_2 },
+	{ 0x03, KEY_3 },
+	{ 0x04, KEY_4 },
+	{ 0x05, KEY_5 },
+	{ 0x06, KEY_6 },
+	{ 0x07, KEY_7 },
+	{ 0x08, KEY_8 },
+	{ 0x09, KEY_9 },
+	{ 0x0a, KEY_ENTER },
+	{ 0x0b, KEY_RED },
+	{ 0x0c, KEY_POWER },		/* RADIO on Hauppauge */
+	{ 0x0d, KEY_MUTE },
+	{ 0x0f, KEY_A },		/* TV on Hauppauge */
+	{ 0x10, KEY_VOLUMEUP },
+	{ 0x11, KEY_VOLUMEDOWN },
+	{ 0x14, KEY_B },
+	{ 0x1c, KEY_UP },
+	{ 0x1d, KEY_DOWN },
+	{ 0x1e, KEY_OPTION },		/* RESERVED on Hauppauge */
+	{ 0x1f, KEY_BREAK },
+	{ 0x20, KEY_CHANNELUP },
+	{ 0x21, KEY_CHANNELDOWN },
+	{ 0x22, KEY_PREVIOUS },		/* Prev Ch on Zenith, SOURCE on Hauppauge */
+	{ 0x24, KEY_RESTART },
+	{ 0x25, KEY_OK },
+	{ 0x26, KEY_CYCLEWINDOWS },	/* MINIMIZE on Hauppauge */
+	{ 0x28, KEY_ENTER },		/* VCR mode on Zenith */
+	{ 0x29, KEY_PAUSE },
+	{ 0x2b, KEY_RIGHT },
+	{ 0x2c, KEY_LEFT },
+	{ 0x2e, KEY_MENU },		/* FULL SCREEN on Hauppauge */
+	{ 0x30, KEY_SLOW },
+	{ 0x31, KEY_PREVIOUS },		/* VCR mode on Zenith */
+	{ 0x32, KEY_REWIND },
+	{ 0x34, KEY_FASTFORWARD },
+	{ 0x35, KEY_PLAY },
+	{ 0x36, KEY_STOP },
+	{ 0x37, KEY_RECORD },
+	{ 0x38, KEY_TUNER },		/* TV/VCR on Zenith */
+	{ 0x3a, KEY_C },
+	{ 0x3c, KEY_EXIT },
+	{ 0x3d, KEY_POWER2 },
+	{ 0x3e, KEY_TUNER },
+};
+
+struct ir_scancode_table ir_codes_budget_ci_old_table = {
+	.scan = ir_codes_budget_ci_old,
+	.size = ARRAY_SIZE(ir_codes_budget_ci_old),
+};
+EXPORT_SYMBOL_GPL(ir_codes_budget_ci_old_table);
 
 /*
  * Marc Fargas <telenieko@telenieko.com>
  * this is the remote control that comes with the asus p7131
  * which has a label saying is "Model PC-39"
  */
-IR_KEYTAB_TYPE ir_codes_asus_pc39[IR_KEYTAB_SIZE] = {
+static struct ir_scancode ir_codes_asus_pc39[] = {
 	/* Keys 0 to 9 */
-	[ 0x15 ] = KEY_0,
-	[ 0x29 ] = KEY_1,
-	[ 0x2d ] = KEY_2,
-	[ 0x2b ] = KEY_3,
-	[ 0x09 ] = KEY_4,
-	[ 0x0d ] = KEY_5,
-	[ 0x0b ] = KEY_6,
-	[ 0x31 ] = KEY_7,
-	[ 0x35 ] = KEY_8,
-	[ 0x33 ] = KEY_9,
-
-	[ 0x3e ] = KEY_RADIO,		/* radio */
-	[ 0x03 ] = KEY_MENU,		/* dvd/menu */
-	[ 0x2a ] = KEY_VOLUMEUP,
-	[ 0x19 ] = KEY_VOLUMEDOWN,
-	[ 0x37 ] = KEY_UP,
-	[ 0x3b ] = KEY_DOWN,
-	[ 0x27 ] = KEY_LEFT,
-	[ 0x2f ] = KEY_RIGHT,
-	[ 0x25 ] = KEY_VIDEO,		/* video */
-	[ 0x39 ] = KEY_AUDIO,		/* music */
-
-	[ 0x21 ] = KEY_TV,		/* tv */
-	[ 0x1d ] = KEY_EXIT,		/* back */
-	[ 0x0a ] = KEY_CHANNELUP,	/* channel / program + */
-	[ 0x1b ] = KEY_CHANNELDOWN,	/* channel / program - */
-	[ 0x1a ] = KEY_ENTER,		/* enter */
-
-	[ 0x06 ] = KEY_PAUSE,		/* play/pause */
-	[ 0x1e ] = KEY_PREVIOUS,	/* rew */
-	[ 0x26 ] = KEY_NEXT,		/* forward */
-	[ 0x0e ] = KEY_REWIND,		/* backward << */
-	[ 0x3a ] = KEY_FASTFORWARD,	/* forward >> */
-	[ 0x36 ] = KEY_STOP,
-	[ 0x2e ] = KEY_RECORD,		/* recording */
-	[ 0x16 ] = KEY_POWER,		/* the button that reads "close" */
-
-	[ 0x11 ] = KEY_ZOOM,		/* full screen */
-	[ 0x13 ] = KEY_MACRO,		/* recall */
-	[ 0x23 ] = KEY_HOME,		/* home */
-	[ 0x05 ] = KEY_PVR,		/* picture */
-	[ 0x3d ] = KEY_MUTE,		/* mute */
-	[ 0x01 ] = KEY_DVD,		/* dvd */
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_asus_pc39);
+	{ 0x15, KEY_0 },
+	{ 0x29, KEY_1 },
+	{ 0x2d, KEY_2 },
+	{ 0x2b, KEY_3 },
+	{ 0x09, KEY_4 },
+	{ 0x0d, KEY_5 },
+	{ 0x0b, KEY_6 },
+	{ 0x31, KEY_7 },
+	{ 0x35, KEY_8 },
+	{ 0x33, KEY_9 },
+
+	{ 0x3e, KEY_RADIO },		/* radio */
+	{ 0x03, KEY_MENU },		/* dvd/menu */
+	{ 0x2a, KEY_VOLUMEUP },
+	{ 0x19, KEY_VOLUMEDOWN },
+	{ 0x37, KEY_UP },
+	{ 0x3b, KEY_DOWN },
+	{ 0x27, KEY_LEFT },
+	{ 0x2f, KEY_RIGHT },
+	{ 0x25, KEY_VIDEO },		/* video */
+	{ 0x39, KEY_AUDIO },		/* music */
+
+	{ 0x21, KEY_TV },		/* tv */
+	{ 0x1d, KEY_EXIT },		/* back */
+	{ 0x0a, KEY_CHANNELUP },	/* channel / program + */
+	{ 0x1b, KEY_CHANNELDOWN },	/* channel / program - */
+	{ 0x1a, KEY_ENTER },		/* enter */
+
+	{ 0x06, KEY_PAUSE },		/* play/pause */
+	{ 0x1e, KEY_PREVIOUS },		/* rew */
+	{ 0x26, KEY_NEXT },		/* forward */
+	{ 0x0e, KEY_REWIND },		/* backward << */
+	{ 0x3a, KEY_FASTFORWARD },	/* forward >> */
+	{ 0x36, KEY_STOP },
+	{ 0x2e, KEY_RECORD },		/* recording */
+	{ 0x16, KEY_POWER },		/* the button that reads "close" */
+
+	{ 0x11, KEY_ZOOM },		/* full screen */
+	{ 0x13, KEY_MACRO },		/* recall */
+	{ 0x23, KEY_HOME },		/* home */
+	{ 0x05, KEY_PVR },		/* picture */
+	{ 0x3d, KEY_MUTE },		/* mute */
+	{ 0x01, KEY_DVD },		/* dvd */
+};
+
+struct ir_scancode_table ir_codes_asus_pc39_table = {
+	.scan = ir_codes_asus_pc39,
+	.size = ARRAY_SIZE(ir_codes_asus_pc39),
+};
+EXPORT_SYMBOL_GPL(ir_codes_asus_pc39_table);
 
 
 /* Encore ENLTV-FM  - black plastic, white front cover with white glowing buttons
     Juan Pablo Sormani <sorman@gmail.com> */
-IR_KEYTAB_TYPE ir_codes_encore_enltv[IR_KEYTAB_SIZE] = {
+static struct ir_scancode ir_codes_encore_enltv[] = {
 
 	/* Power button does nothing, neither in Windows app,
 	 although it sends data (used for BIOS wakeup?) */
-	[ 0x0d ] = KEY_MUTE,
-
-	[ 0x1e ] = KEY_TV,
-	[ 0x00 ] = KEY_VIDEO,
-	[ 0x01 ] = KEY_AUDIO,		/* music */
-	[ 0x02 ] = KEY_MHP,		/* picture */
-
-	[ 0x1f ] = KEY_1,
-	[ 0x03 ] = KEY_2,
-	[ 0x04 ] = KEY_3,
-	[ 0x05 ] = KEY_4,
-	[ 0x1c ] = KEY_5,
-	[ 0x06 ] = KEY_6,
-	[ 0x07 ] = KEY_7,
-	[ 0x08 ] = KEY_8,
-	[ 0x1d ] = KEY_9,
-	[ 0x0a ] = KEY_0,
-
-	[ 0x09 ] = KEY_LIST,        /* -/-- */
-	[ 0x0b ] = KEY_LAST,        /* recall */
-
-	[ 0x14 ] = KEY_HOME,		/* win start menu */
-	[ 0x15 ] = KEY_EXIT,		/* exit */
-	[ 0x16 ] = KEY_UP,
-	[ 0x12 ] = KEY_DOWN,
-	[ 0x0c ] = KEY_RIGHT,
-	[ 0x17 ] = KEY_LEFT,
-
-	[ 0x18 ] = KEY_ENTER,		/* OK */
-
-	[ 0x0e ] = KEY_ESC,
-	[ 0x13 ] = KEY_D,		/* desktop */
-	[ 0x11 ] = KEY_TAB,
-	[ 0x19 ] = KEY_SWITCHVIDEOMODE,	/* switch */
-
-	[ 0x1a ] = KEY_MENU,
-	[ 0x1b ] = KEY_ZOOM,		/* fullscreen */
-	[ 0x44 ] = KEY_TIME,		/* time shift */
-	[ 0x40 ] = KEY_MODE,		/* source */
-
-	[ 0x5a ] = KEY_RECORD,
-	[ 0x42 ] = KEY_PLAY,		/* play/pause */
-	[ 0x45 ] = KEY_STOP,
-	[ 0x43 ] = KEY_CAMERA,		/* camera icon */
-
-	[ 0x48 ] = KEY_REWIND,
-	[ 0x4a ] = KEY_FASTFORWARD,
-	[ 0x49 ] = KEY_PREVIOUS,
-	[ 0x4b ] = KEY_NEXT,
-
-	[ 0x4c ] = KEY_FAVORITES,	/* tv wall */
-	[ 0x4d ] = KEY_SOUND,		/* DVD sound */
-	[ 0x4e ] = KEY_LANGUAGE,	/* DVD lang */
-	[ 0x4f ] = KEY_TEXT,		/* DVD text */
-
-	[ 0x50 ] = KEY_SLEEP,		/* shutdown */
-	[ 0x51 ] = KEY_MODE,		/* stereo > main */
-	[ 0x52 ] = KEY_SELECT,		/* stereo > sap */
-	[ 0x53 ] = KEY_PROG1,		/* teletext */
-
-
-	[ 0x59 ] = KEY_RED,		/* AP1 */
-	[ 0x41 ] = KEY_GREEN,		/* AP2 */
-	[ 0x47 ] = KEY_YELLOW,		/* AP3 */
-	[ 0x57 ] = KEY_BLUE,		/* AP4 */
-};
-EXPORT_SYMBOL_GPL(ir_codes_encore_enltv);
+	{ 0x0d, KEY_MUTE },
+
+	{ 0x1e, KEY_TV },
+	{ 0x00, KEY_VIDEO },
+	{ 0x01, KEY_AUDIO },		/* music */
+	{ 0x02, KEY_MHP },		/* picture */
+
+	{ 0x1f, KEY_1 },
+	{ 0x03, KEY_2 },
+	{ 0x04, KEY_3 },
+	{ 0x05, KEY_4 },
+	{ 0x1c, KEY_5 },
+	{ 0x06, KEY_6 },
+	{ 0x07, KEY_7 },
+	{ 0x08, KEY_8 },
+	{ 0x1d, KEY_9 },
+	{ 0x0a, KEY_0 },
+
+	{ 0x09, KEY_LIST },		/* -/-- */
+	{ 0x0b, KEY_LAST },		/* recall */
+
+	{ 0x14, KEY_HOME },		/* win start menu */
+	{ 0x15, KEY_EXIT },		/* exit */
+	{ 0x16, KEY_CHANNELUP },	/* UP */
+	{ 0x12, KEY_CHANNELDOWN },	/* DOWN */
+	{ 0x0c, KEY_VOLUMEUP },		/* RIGHT */
+	{ 0x17, KEY_VOLUMEDOWN },	/* LEFT */
+
+	{ 0x18, KEY_ENTER },		/* OK */
+
+	{ 0x0e, KEY_ESC },
+	{ 0x13, KEY_CYCLEWINDOWS },	/* desktop */
+	{ 0x11, KEY_TAB },
+	{ 0x19, KEY_SWITCHVIDEOMODE },	/* switch */
+
+	{ 0x1a, KEY_MENU },
+	{ 0x1b, KEY_ZOOM },		/* fullscreen */
+	{ 0x44, KEY_TIME },		/* time shift */
+	{ 0x40, KEY_MODE },		/* source */
+
+	{ 0x5a, KEY_RECORD },
+	{ 0x42, KEY_PLAY },		/* play/pause */
+	{ 0x45, KEY_STOP },
+	{ 0x43, KEY_CAMERA },		/* camera icon */
+
+	{ 0x48, KEY_REWIND },
+	{ 0x4a, KEY_FASTFORWARD },
+	{ 0x49, KEY_PREVIOUS },
+	{ 0x4b, KEY_NEXT },
+
+	{ 0x4c, KEY_FAVORITES },	/* tv wall */
+	{ 0x4d, KEY_SOUND },		/* DVD sound */
+	{ 0x4e, KEY_LANGUAGE },		/* DVD lang */
+	{ 0x4f, KEY_TEXT },		/* DVD text */
+
+	{ 0x50, KEY_SLEEP },		/* shutdown */
+	{ 0x51, KEY_MODE },		/* stereo > main */
+	{ 0x52, KEY_SELECT },		/* stereo > sap */
+	{ 0x53, KEY_PROG1 },		/* teletext */
+
+
+	{ 0x59, KEY_RED },		/* AP1 */
+	{ 0x41, KEY_GREEN },		/* AP2 */
+	{ 0x47, KEY_YELLOW },		/* AP3 */
+	{ 0x57, KEY_BLUE },		/* AP4 */
+};
+
+struct ir_scancode_table ir_codes_encore_enltv_table = {
+	.scan = ir_codes_encore_enltv,
+	.size = ARRAY_SIZE(ir_codes_encore_enltv),
+};
+EXPORT_SYMBOL_GPL(ir_codes_encore_enltv_table);
 
 /* Encore ENLTV2-FM  - silver plastic - "Wand Media" written at the botton
     Mauro Carvalho Chehab <mchehab@infradead.org> */
-IR_KEYTAB_TYPE ir_codes_encore_enltv2[IR_KEYTAB_SIZE] = {
-	[0x4c] = KEY_POWER2,
-	[0x4a] = KEY_TUNER,
-	[0x40] = KEY_1,
-	[0x60] = KEY_2,
-	[0x50] = KEY_3,
-	[0x70] = KEY_4,
-	[0x48] = KEY_5,
-	[0x68] = KEY_6,
-	[0x58] = KEY_7,
-	[0x78] = KEY_8,
-	[0x44] = KEY_9,
-	[0x54] = KEY_0,
-
-	[0x64] = KEY_LAST,		/* +100 */
-	[0x4e] = KEY_AGAIN,		/* Recall */
-
-	[0x6c] = KEY_SWITCHVIDEOMODE,	/* Video Source */
-	[0x5e] = KEY_MENU,
-	[0x56] = KEY_SCREEN,
-	[0x7a] = KEY_SETUP,
-
-	[0x46] = KEY_MUTE,
-	[0x5c] = KEY_MODE,		/* Stereo */
-	[0x74] = KEY_INFO,
-	[0x7c] = KEY_CLEAR,
-
-	[0x55] = KEY_UP,
-	[0x49] = KEY_DOWN,
-	[0x7e] = KEY_LEFT,
-	[0x59] = KEY_RIGHT,
-	[0x6a] = KEY_ENTER,
-
-	[0x42] = KEY_VOLUMEUP,
-	[0x62] = KEY_VOLUMEDOWN,
-	[0x52] = KEY_CHANNELUP,
-	[0x72] = KEY_CHANNELDOWN,
-
-	[0x41] = KEY_RECORD,
-	[0x51] = KEY_SHUFFLE,	/* Snapshot */
-	[0x75] = KEY_TIME,	/* Timeshift */
-	[0x71] = KEY_TV2,	/* PIP */
-
-	[0x45] = KEY_REWIND,
-	[0x6f] = KEY_PAUSE,
-	[0x7d] = KEY_FORWARD,
-	[0x79] = KEY_STOP,
-};
-EXPORT_SYMBOL_GPL(ir_codes_encore_enltv2);
+static struct ir_scancode ir_codes_encore_enltv2[] = {
+	{ 0x4c, KEY_POWER2 },
+	{ 0x4a, KEY_TUNER },
+	{ 0x40, KEY_1 },
+	{ 0x60, KEY_2 },
+	{ 0x50, KEY_3 },
+	{ 0x70, KEY_4 },
+	{ 0x48, KEY_5 },
+	{ 0x68, KEY_6 },
+	{ 0x58, KEY_7 },
+	{ 0x78, KEY_8 },
+	{ 0x44, KEY_9 },
+	{ 0x54, KEY_0 },
+
+	{ 0x64, KEY_LAST },		/* +100 */
+	{ 0x4e, KEY_AGAIN },		/* Recall */
+
+	{ 0x6c, KEY_SWITCHVIDEOMODE },	/* Video Source */
+	{ 0x5e, KEY_MENU },
+	{ 0x56, KEY_SCREEN },
+	{ 0x7a, KEY_SETUP },
+
+	{ 0x46, KEY_MUTE },
+	{ 0x5c, KEY_MODE },		/* Stereo */
+	{ 0x74, KEY_INFO },
+	{ 0x7c, KEY_CLEAR },
+
+	{ 0x55, KEY_UP },
+	{ 0x49, KEY_DOWN },
+	{ 0x7e, KEY_LEFT },
+	{ 0x59, KEY_RIGHT },
+	{ 0x6a, KEY_ENTER },
+
+	{ 0x42, KEY_VOLUMEUP },
+	{ 0x62, KEY_VOLUMEDOWN },
+	{ 0x52, KEY_CHANNELUP },
+	{ 0x72, KEY_CHANNELDOWN },
+
+	{ 0x41, KEY_RECORD },
+	{ 0x51, KEY_CAMERA },		/* Snapshot */
+	{ 0x75, KEY_TIME },		/* Timeshift */
+	{ 0x71, KEY_TV2 },		/* PIP */
+
+	{ 0x45, KEY_REWIND },
+	{ 0x6f, KEY_PAUSE },
+	{ 0x7d, KEY_FORWARD },
+	{ 0x79, KEY_STOP },
+};
+
+struct ir_scancode_table ir_codes_encore_enltv2_table = {
+	.scan = ir_codes_encore_enltv2,
+	.size = ARRAY_SIZE(ir_codes_encore_enltv2),
+};
+EXPORT_SYMBOL_GPL(ir_codes_encore_enltv2_table);
 
 /* for the Technotrend 1500 bundled remotes (grey and black): */
-IR_KEYTAB_TYPE ir_codes_tt_1500[IR_KEYTAB_SIZE] = {
-	[ 0x01 ] = KEY_POWER,
-	[ 0x02 ] = KEY_SHUFFLE,	/* ? double-arrow key */
-	[ 0x03 ] = KEY_1,
-	[ 0x04 ] = KEY_2,
-	[ 0x05 ] = KEY_3,
-	[ 0x06 ] = KEY_4,
-	[ 0x07 ] = KEY_5,
-	[ 0x08 ] = KEY_6,
-	[ 0x09 ] = KEY_7,
-	[ 0x0a ] = KEY_8,
-	[ 0x0b ] = KEY_9,
-	[ 0x0c ] = KEY_0,
-	[ 0x0d ] = KEY_UP,
-	[ 0x0e ] = KEY_LEFT,
-	[ 0x0f ] = KEY_OK,
-	[ 0x10 ] = KEY_RIGHT,
-	[ 0x11 ] = KEY_DOWN,
-	[ 0x12 ] = KEY_INFO,
-	[ 0x13 ] = KEY_EXIT,
-	[ 0x14 ] = KEY_RED,
-	[ 0x15 ] = KEY_GREEN,
-	[ 0x16 ] = KEY_YELLOW,
-	[ 0x17 ] = KEY_BLUE,
-	[ 0x18 ] = KEY_MUTE,
-	[ 0x19 ] = KEY_TEXT,
-	[ 0x1a ] = KEY_MODE,	/* ? TV/Radio */
-	[ 0x21 ] = KEY_OPTION,
-	[ 0x22 ] = KEY_EPG,
-	[ 0x23 ] = KEY_CHANNELUP,
-	[ 0x24 ] = KEY_CHANNELDOWN,
-	[ 0x25 ] = KEY_VOLUMEUP,
-	[ 0x26 ] = KEY_VOLUMEDOWN,
-	[ 0x27 ] = KEY_SETUP,
-	[ 0x3a ] = KEY_RECORD, /* these keys are only in the black remote */
-	[ 0x3b ] = KEY_PLAY,
-	[ 0x3c ] = KEY_STOP,
-	[ 0x3d ] = KEY_REWIND,
-	[ 0x3e ] = KEY_PAUSE,
-	[ 0x3f ] = KEY_FORWARD,
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_tt_1500);
+static struct ir_scancode ir_codes_tt_1500[] = {
+	{ 0x01, KEY_POWER },
+	{ 0x02, KEY_SHUFFLE },		/* ? double-arrow key */
+	{ 0x03, KEY_1 },
+	{ 0x04, KEY_2 },
+	{ 0x05, KEY_3 },
+	{ 0x06, KEY_4 },
+	{ 0x07, KEY_5 },
+	{ 0x08, KEY_6 },
+	{ 0x09, KEY_7 },
+	{ 0x0a, KEY_8 },
+	{ 0x0b, KEY_9 },
+	{ 0x0c, KEY_0 },
+	{ 0x0d, KEY_UP },
+	{ 0x0e, KEY_LEFT },
+	{ 0x0f, KEY_OK },
+	{ 0x10, KEY_RIGHT },
+	{ 0x11, KEY_DOWN },
+	{ 0x12, KEY_INFO },
+	{ 0x13, KEY_EXIT },
+	{ 0x14, KEY_RED },
+	{ 0x15, KEY_GREEN },
+	{ 0x16, KEY_YELLOW },
+	{ 0x17, KEY_BLUE },
+	{ 0x18, KEY_MUTE },
+	{ 0x19, KEY_TEXT },
+	{ 0x1a, KEY_MODE },		/* ? TV/Radio */
+	{ 0x21, KEY_OPTION },
+	{ 0x22, KEY_EPG },
+	{ 0x23, KEY_CHANNELUP },
+	{ 0x24, KEY_CHANNELDOWN },
+	{ 0x25, KEY_VOLUMEUP },
+	{ 0x26, KEY_VOLUMEDOWN },
+	{ 0x27, KEY_SETUP },
+	{ 0x3a, KEY_RECORD },		/* these keys are only in the black remote */
+	{ 0x3b, KEY_PLAY },
+	{ 0x3c, KEY_STOP },
+	{ 0x3d, KEY_REWIND },
+	{ 0x3e, KEY_PAUSE },
+	{ 0x3f, KEY_FORWARD },
+};
+
+struct ir_scancode_table ir_codes_tt_1500_table = {
+	.scan = ir_codes_tt_1500,
+	.size = ARRAY_SIZE(ir_codes_tt_1500),
+};
+EXPORT_SYMBOL_GPL(ir_codes_tt_1500_table);
 
 /* DViCO FUSION HDTV MCE remote */
-IR_KEYTAB_TYPE ir_codes_fusionhdtv_mce[IR_KEYTAB_SIZE] = {
-
-	[ 0x0b ] = KEY_1,
-	[ 0x17 ] = KEY_2,
-	[ 0x1b ] = KEY_3,
-	[ 0x07 ] = KEY_4,
-	[ 0x50 ] = KEY_5,
-	[ 0x54 ] = KEY_6,
-	[ 0x48 ] = KEY_7,
-	[ 0x4c ] = KEY_8,
-	[ 0x58 ] = KEY_9,
-	[ 0x03 ] = KEY_0,
-
-	[ 0x5e ] = KEY_OK,
-	[ 0x51 ] = KEY_UP,
-	[ 0x53 ] = KEY_DOWN,
-	[ 0x5b ] = KEY_LEFT,
-	[ 0x5f ] = KEY_RIGHT,
-
-	[ 0x02 ] = KEY_TV,		/* Labeled DTV on remote */
-	[ 0x0e ] = KEY_MP3,
-	[ 0x1a ] = KEY_DVD,
-	[ 0x1e ] = KEY_FAVORITES,	/* Labeled CPF on remote */
-	[ 0x16 ] = KEY_SETUP,
-	[ 0x46 ] = KEY_POWER2,		/* TV On/Off button on remote */
-	[ 0x0a ] = KEY_EPG,		/* Labeled Guide on remote */
-
-	[ 0x49 ] = KEY_BACK,
-	[ 0x59 ] = KEY_INFO,		/* Labeled MORE on remote */
-	[ 0x4d ] = KEY_MENU,		/* Labeled DVDMENU on remote */
-	[ 0x55 ] = KEY_CYCLEWINDOWS,	/* Labeled ALT-TAB on remote */
-
-	[ 0x0f ] = KEY_PREVIOUSSONG,	/* Labeled |<< REPLAY on remote */
-	[ 0x12 ] = KEY_NEXTSONG,	/* Labeled >>| SKIP on remote */
-	[ 0x42 ] = KEY_ENTER, 		/* Labeled START with a green
-					 * MS windows logo on remote */
-
-	[ 0x15 ] = KEY_VOLUMEUP,
-	[ 0x05 ] = KEY_VOLUMEDOWN,
-	[ 0x11 ] = KEY_CHANNELUP,
-	[ 0x09 ] = KEY_CHANNELDOWN,
-
-	[ 0x52 ] = KEY_CAMERA,
-	[ 0x5a ] = KEY_TUNER,
-	[ 0x19 ] = KEY_OPEN,
-
-	[ 0x13 ] = KEY_MODE,		/* 4:3 16:9 select */
-	[ 0x1f ] = KEY_ZOOM,
-
-	[ 0x43 ] = KEY_REWIND,
-	[ 0x47 ] = KEY_PLAYPAUSE,
-	[ 0x4f ] = KEY_FASTFORWARD,
-	[ 0x57 ] = KEY_MUTE,
-	[ 0x0d ] = KEY_STOP,
-	[ 0x01 ] = KEY_RECORD,
-	[ 0x4e ] = KEY_POWER,
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_fusionhdtv_mce);
+static struct ir_scancode ir_codes_fusionhdtv_mce[] = {
+
+	{ 0x0b, KEY_1 },
+	{ 0x17, KEY_2 },
+	{ 0x1b, KEY_3 },
+	{ 0x07, KEY_4 },
+	{ 0x50, KEY_5 },
+	{ 0x54, KEY_6 },
+	{ 0x48, KEY_7 },
+	{ 0x4c, KEY_8 },
+	{ 0x58, KEY_9 },
+	{ 0x03, KEY_0 },
+
+	{ 0x5e, KEY_OK },
+	{ 0x51, KEY_UP },
+	{ 0x53, KEY_DOWN },
+	{ 0x5b, KEY_LEFT },
+	{ 0x5f, KEY_RIGHT },
+
+	{ 0x02, KEY_TV },		/* Labeled DTV on remote */
+	{ 0x0e, KEY_MP3 },
+	{ 0x1a, KEY_DVD },
+	{ 0x1e, KEY_FAVORITES },	/* Labeled CPF on remote */
+	{ 0x16, KEY_SETUP },
+	{ 0x46, KEY_POWER2 },		/* TV On/Off button on remote */
+	{ 0x0a, KEY_EPG },		/* Labeled Guide on remote */
+
+	{ 0x49, KEY_BACK },
+	{ 0x59, KEY_INFO },		/* Labeled MORE on remote */
+	{ 0x4d, KEY_MENU },		/* Labeled DVDMENU on remote */
+	{ 0x55, KEY_CYCLEWINDOWS },	/* Labeled ALT-TAB on remote */
+
+	{ 0x0f, KEY_PREVIOUSSONG },	/* Labeled |<< REPLAY on remote */
+	{ 0x12, KEY_NEXTSONG },		/* Labeled >>| SKIP on remote */
+	{ 0x42, KEY_ENTER },		/* Labeled START with a green
+					   MS windows logo on remote */
+
+	{ 0x15, KEY_VOLUMEUP },
+	{ 0x05, KEY_VOLUMEDOWN },
+	{ 0x11, KEY_CHANNELUP },
+	{ 0x09, KEY_CHANNELDOWN },
+
+	{ 0x52, KEY_CAMERA },
+	{ 0x5a, KEY_TUNER },
+	{ 0x19, KEY_OPEN },
+
+	{ 0x13, KEY_MODE },		/* 4:3 16:9 select */
+	{ 0x1f, KEY_ZOOM },
+
+	{ 0x43, KEY_REWIND },
+	{ 0x47, KEY_PLAYPAUSE },
+	{ 0x4f, KEY_FASTFORWARD },
+	{ 0x57, KEY_MUTE },
+	{ 0x0d, KEY_STOP },
+	{ 0x01, KEY_RECORD },
+	{ 0x4e, KEY_POWER },
+};
+
+struct ir_scancode_table ir_codes_fusionhdtv_mce_table = {
+	.scan = ir_codes_fusionhdtv_mce,
+	.size = ARRAY_SIZE(ir_codes_fusionhdtv_mce),
+};
+EXPORT_SYMBOL_GPL(ir_codes_fusionhdtv_mce_table);
 
 /* Pinnacle PCTV HD 800i mini remote */
-IR_KEYTAB_TYPE ir_codes_pinnacle_pctv_hd[IR_KEYTAB_SIZE] = {
-
-	[0x0f] = KEY_1,
-	[0x15] = KEY_2,
-	[0x10] = KEY_3,
-	[0x18] = KEY_4,
-	[0x1b] = KEY_5,
-	[0x1e] = KEY_6,
-	[0x11] = KEY_7,
-	[0x21] = KEY_8,
-	[0x12] = KEY_9,
-	[0x27] = KEY_0,
-
-	[0x24] = KEY_ZOOM,
-	[0x2a] = KEY_SUBTITLE,
-
-	[0x00] = KEY_MUTE,
-	[0x01] = KEY_ENTER,	/* Pinnacle Logo */
-	[0x39] = KEY_POWER,
-
-	[0x03] = KEY_VOLUMEUP,
-	[0x09] = KEY_VOLUMEDOWN,
-	[0x06] = KEY_CHANNELUP,
-	[0x0c] = KEY_CHANNELDOWN,
-
-	[0x2d] = KEY_REWIND,
-	[0x30] = KEY_PLAYPAUSE,
-	[0x33] = KEY_FASTFORWARD,
-	[0x3c] = KEY_STOP,
-	[0x36] = KEY_RECORD,
-	[0x3f] = KEY_EPG,	/* Labeled "?" */
-};
-EXPORT_SYMBOL_GPL(ir_codes_pinnacle_pctv_hd);
+static struct ir_scancode ir_codes_pinnacle_pctv_hd[] = {
+
+	{ 0x0f, KEY_1 },
+	{ 0x15, KEY_2 },
+	{ 0x10, KEY_3 },
+	{ 0x18, KEY_4 },
+	{ 0x1b, KEY_5 },
+	{ 0x1e, KEY_6 },
+	{ 0x11, KEY_7 },
+	{ 0x21, KEY_8 },
+	{ 0x12, KEY_9 },
+	{ 0x27, KEY_0 },
+
+	{ 0x24, KEY_ZOOM },
+	{ 0x2a, KEY_SUBTITLE },
+
+	{ 0x00, KEY_MUTE },
+	{ 0x01, KEY_ENTER },	/* Pinnacle Logo */
+	{ 0x39, KEY_POWER },
+
+	{ 0x03, KEY_VOLUMEUP },
+	{ 0x09, KEY_VOLUMEDOWN },
+	{ 0x06, KEY_CHANNELUP },
+	{ 0x0c, KEY_CHANNELDOWN },
+
+	{ 0x2d, KEY_REWIND },
+	{ 0x30, KEY_PLAYPAUSE },
+	{ 0x33, KEY_FASTFORWARD },
+	{ 0x3c, KEY_STOP },
+	{ 0x36, KEY_RECORD },
+	{ 0x3f, KEY_EPG },	/* Labeled "?" */
+};
+
+struct ir_scancode_table ir_codes_pinnacle_pctv_hd_table = {
+	.scan = ir_codes_pinnacle_pctv_hd,
+	.size = ARRAY_SIZE(ir_codes_pinnacle_pctv_hd),
+};
+EXPORT_SYMBOL_GPL(ir_codes_pinnacle_pctv_hd_table);
 
 /*
  * Igor Kuznetsov <igk72@ya.ru>
@@ -2198,13 +2365,13 @@ EXPORT_SYMBOL_GPL(ir_codes_pinnacle_pctv_hd);
  * the button labels (several variants when appropriate)
  * helps to descide which keycodes to assign to the buttons.
  */
-IR_KEYTAB_TYPE ir_codes_behold[IR_KEYTAB_SIZE] = {
+static struct ir_scancode ir_codes_behold[] = {
 
 	/*  0x1c            0x12  *
 	 *  TV/FM          POWER  *
 	 *                        */
-	[ 0x1c ] = KEY_TUNER,	/*XXX KEY_TV KEY_RADIO */
-	[ 0x12 ] = KEY_POWER,
+	{ 0x1c, KEY_TUNER },	/* XXX KEY_TV / KEY_RADIO */
+	{ 0x12, KEY_POWER },
 
 	/*  0x01    0x02    0x03  *
 	 *   1       2       3    *
@@ -2215,28 +2382,28 @@ IR_KEYTAB_TYPE ir_codes_behold[IR_KEYTAB_SIZE] = {
 	 *  0x07    0x08    0x09  *
 	 *   7       8       9    *
 	 *                        */
-	[ 0x01 ] = KEY_1,
-	[ 0x02 ] = KEY_2,
-	[ 0x03 ] = KEY_3,
-	[ 0x04 ] = KEY_4,
-	[ 0x05 ] = KEY_5,
-	[ 0x06 ] = KEY_6,
-	[ 0x07 ] = KEY_7,
-	[ 0x08 ] = KEY_8,
-	[ 0x09 ] = KEY_9,
+	{ 0x01, KEY_1 },
+	{ 0x02, KEY_2 },
+	{ 0x03, KEY_3 },
+	{ 0x04, KEY_4 },
+	{ 0x05, KEY_5 },
+	{ 0x06, KEY_6 },
+	{ 0x07, KEY_7 },
+	{ 0x08, KEY_8 },
+	{ 0x09, KEY_9 },
 
 	/*  0x0a    0x00    0x17  *
 	 * RECALL    0      MODE  *
 	 *                        */
-	[ 0x0a ] = KEY_AGAIN,
-	[ 0x00 ] = KEY_0,
-	[ 0x17 ] = KEY_MODE,
+	{ 0x0a, KEY_AGAIN },
+	{ 0x00, KEY_0 },
+	{ 0x17, KEY_MODE },
 
 	/*  0x14          0x10    *
 	 * ASPECT      FULLSCREEN *
 	 *                        */
-	[ 0x14 ] = KEY_SCREEN,
-	[ 0x10 ] = KEY_ZOOM,
+	{ 0x14, KEY_SCREEN },
+	{ 0x10, KEY_ZOOM },
 
 	/*          0x0b          *
 	 *           Up           *
@@ -2247,17 +2414,17 @@ IR_KEYTAB_TYPE ir_codes_behold[IR_KEYTAB_SIZE] = {
 	 *         0x015          *
 	 *         Down           *
 	 *                        */
-	[ 0x0b ] = KEY_CHANNELUP,	/*XXX KEY_UP */
-	[ 0x18 ] = KEY_VOLUMEDOWN,	/*XXX KEY_LEFT */
-	[ 0x16 ] = KEY_OK,		/*XXX KEY_ENTER */
-	[ 0x0c ] = KEY_VOLUMEUP,	/*XXX KEY_RIGHT */
-	[ 0x15 ] = KEY_CHANNELDOWN,	/*XXX KEY_DOWN */
+	{ 0x0b, KEY_CHANNELUP },
+	{ 0x18, KEY_VOLUMEDOWN },
+	{ 0x16, KEY_OK },		/* XXX KEY_ENTER */
+	{ 0x0c, KEY_VOLUMEUP },
+	{ 0x15, KEY_CHANNELDOWN },
 
 	/*  0x11            0x0d  *
 	 *  MUTE            INFO  *
 	 *                        */
-	[ 0x11 ] = KEY_MUTE,
-	[ 0x0d ] = KEY_INFO,
+	{ 0x11, KEY_MUTE },
+	{ 0x0d, KEY_INFO },
 
 	/*  0x0f    0x1b    0x1a  *
 	 * RECORD PLAY/PAUSE STOP *
@@ -2266,30 +2433,34 @@ IR_KEYTAB_TYPE ir_codes_behold[IR_KEYTAB_SIZE] = {
 	 *TELETEXT  AUDIO  SOURCE *
 	 *           RED   YELLOW *
 	 *                        */
-	[ 0x0f ] = KEY_RECORD,
-	[ 0x1b ] = KEY_PLAYPAUSE,
-	[ 0x1a ] = KEY_STOP,
-	[ 0x0e ] = KEY_TEXT,
-	[ 0x1f ] = KEY_RED,	/*XXX KEY_AUDIO */
-	[ 0x1e ] = KEY_YELLOW,	/*XXX KEY_SOURCE */
+	{ 0x0f, KEY_RECORD },
+	{ 0x1b, KEY_PLAYPAUSE },
+	{ 0x1a, KEY_STOP },
+	{ 0x0e, KEY_TEXT },
+	{ 0x1f, KEY_RED },	/*XXX KEY_AUDIO	*/
+	{ 0x1e, KEY_YELLOW },	/*XXX KEY_SOURCE	*/
 
 	/*  0x1d   0x13     0x19  *
 	 * SLEEP  PREVIEW   DVB   *
 	 *         GREEN    BLUE  *
 	 *                        */
-	[ 0x1d ] = KEY_SLEEP,
-	[ 0x13 ] = KEY_GREEN,
-	[ 0x19 ] = KEY_BLUE,	/*XXX KEY_SAT */
+	{ 0x1d, KEY_SLEEP },
+	{ 0x13, KEY_GREEN },
+	{ 0x19, KEY_BLUE },	/* XXX KEY_SAT	*/
 
 	/*  0x58           0x5c   *
 	 * FREEZE        SNAPSHOT *
 	 *                        */
-	[ 0x58 ] = KEY_SLOW,
-	[ 0x5c ] = KEY_SAVE,
+	{ 0x58, KEY_SLOW },
+	{ 0x5c, KEY_CAMERA },
 
 };
 
-EXPORT_SYMBOL_GPL(ir_codes_behold);
+struct ir_scancode_table ir_codes_behold_table = {
+	.scan = ir_codes_behold,
+	.size = ARRAY_SIZE(ir_codes_behold),
+};
+EXPORT_SYMBOL_GPL(ir_codes_behold_table);
 
 /* Beholder Intl. Ltd. 2008
  * Dmitry Belimov d.belimov@google.com
@@ -2299,16 +2470,16 @@ EXPORT_SYMBOL_GPL(ir_codes_behold);
  * the button labels (several variants when appropriate)
  * helps to descide which keycodes to assign to the buttons.
  */
-IR_KEYTAB_TYPE ir_codes_behold_columbus[IR_KEYTAB_SIZE] = {
+static struct ir_scancode ir_codes_behold_columbus[] = {
 
 	/*  0x13   0x11   0x1C   0x12  *
 	 *  Mute  Source  TV/FM  Power *
 	 *                             */
 
-	[0x13] = KEY_MUTE,
-	[0x11] = KEY_PROPS,
-	[0x1C] = KEY_TUNER,	/* KEY_TV/KEY_RADIO */
-	[0x12] = KEY_POWER,
+	{ 0x13, KEY_MUTE },
+	{ 0x11, KEY_PROPS },
+	{ 0x1C, KEY_TUNER },	/* KEY_TV/KEY_RADIO	*/
+	{ 0x12, KEY_POWER },
 
 	/*  0x01    0x02    0x03  0x0D    *
 	 *   1       2       3   Stereo   *
@@ -2319,173 +2490,188 @@ IR_KEYTAB_TYPE ir_codes_behold_columbus[IR_KEYTAB_SIZE] = {
 	 *  0x07    0x08    0x09  0x10    *
 	 *   7       8       9    Zoom 	  *
 	 *                                */
-	[0x01] = KEY_1,
-	[0x02] = KEY_2,
-	[0x03] = KEY_3,
-	[0x0D] = KEY_SETUP,	  /* Setup key */
-	[0x04] = KEY_4,
-	[0x05] = KEY_5,
-	[0x06] = KEY_6,
-	[0x19] = KEY_BOOKMARKS, /* Snapshot key */
-	[0x07] = KEY_7,
-	[0x08] = KEY_8,
-	[0x09] = KEY_9,
-	[0x10] = KEY_ZOOM,
+	{ 0x01, KEY_1 },
+	{ 0x02, KEY_2 },
+	{ 0x03, KEY_3 },
+	{ 0x0D, KEY_SETUP },	  /* Setup key */
+	{ 0x04, KEY_4 },
+	{ 0x05, KEY_5 },
+	{ 0x06, KEY_6 },
+	{ 0x19, KEY_CAMERA },	/* Snapshot key */
+	{ 0x07, KEY_7 },
+	{ 0x08, KEY_8 },
+	{ 0x09, KEY_9 },
+	{ 0x10, KEY_ZOOM },
 
 	/*  0x0A    0x00    0x0B       0x0C   *
 	 * RECALL    0    ChannelUp  VolumeUp *
 	 *                                    */
-	[0x0A] = KEY_AGAIN,
-	[0x00] = KEY_0,
-	[0x0B] = KEY_CHANNELUP,
-	[0x0C] = KEY_VOLUMEUP,
+	{ 0x0A, KEY_AGAIN },
+	{ 0x00, KEY_0 },
+	{ 0x0B, KEY_CHANNELUP },
+	{ 0x0C, KEY_VOLUMEUP },
 
 	/*   0x1B      0x1D      0x15        0x18     *
 	 * Timeshift  Record  ChannelDown  VolumeDown *
 	 *                                            */
 
-	[0x1B] = KEY_REWIND,
-	[0x1D] = KEY_RECORD,
-	[0x15] = KEY_CHANNELDOWN,
-	[0x18] = KEY_VOLUMEDOWN,
+	{ 0x1B, KEY_TIME },
+	{ 0x1D, KEY_RECORD },
+	{ 0x15, KEY_CHANNELDOWN },
+	{ 0x18, KEY_VOLUMEDOWN },
 
 	/*   0x0E   0x1E     0x0F     0x1A  *
 	 *   Stop   Pause  Previouse  Next  *
 	 *                                  */
 
-	[0x0E] = KEY_STOP,
-	[0x1E] = KEY_PAUSE,
-	[0x0F] = KEY_PREVIOUS,
-	[0x1A] = KEY_NEXT,
+	{ 0x0E, KEY_STOP },
+	{ 0x1E, KEY_PAUSE },
+	{ 0x0F, KEY_PREVIOUS },
+	{ 0x1A, KEY_NEXT },
+
+};
 
+struct ir_scancode_table ir_codes_behold_columbus_table = {
+	.scan = ir_codes_behold_columbus,
+	.size = ARRAY_SIZE(ir_codes_behold_columbus),
 };
-EXPORT_SYMBOL_GPL(ir_codes_behold_columbus);
+EXPORT_SYMBOL_GPL(ir_codes_behold_columbus_table);
 
 /*
  * Remote control for the Genius TVGO A11MCE
  * Adrian Pardini <pardo.bsso@gmail.com>
  */
-IR_KEYTAB_TYPE ir_codes_genius_tvgo_a11mce[IR_KEYTAB_SIZE] = {
+static struct ir_scancode ir_codes_genius_tvgo_a11mce[] = {
 	/* Keys 0 to 9 */
-	[0x48] = KEY_0,
-	[0x09] = KEY_1,
-	[0x1d] = KEY_2,
-	[0x1f] = KEY_3,
-	[0x19] = KEY_4,
-	[0x1b] = KEY_5,
-	[0x11] = KEY_6,
-	[0x17] = KEY_7,
-	[0x12] = KEY_8,
-	[0x16] = KEY_9,
-
-	[0x54] = KEY_RECORD,		/* recording */
-	[0x06] = KEY_MUTE,		/* mute */
-	[0x10] = KEY_POWER,
-	[0x40] = KEY_LAST,		/* recall */
-	[0x4c] = KEY_CHANNELUP,		/* channel / program + */
-	[0x00] = KEY_CHANNELDOWN,	/* channel / program - */
-	[0x0d] = KEY_VOLUMEUP,
-	[0x15] = KEY_VOLUMEDOWN,
-	[0x4d] = KEY_OK,		/* also labeled as Pause */
-	[0x1c] = KEY_ZOOM,		/* full screen and Stop*/
-	[0x02] = KEY_MODE,		/* AV Source or Rewind*/
-	[0x04] = KEY_LIST,		/* -/-- */
+	{ 0x48, KEY_0 },
+	{ 0x09, KEY_1 },
+	{ 0x1d, KEY_2 },
+	{ 0x1f, KEY_3 },
+	{ 0x19, KEY_4 },
+	{ 0x1b, KEY_5 },
+	{ 0x11, KEY_6 },
+	{ 0x17, KEY_7 },
+	{ 0x12, KEY_8 },
+	{ 0x16, KEY_9 },
+
+	{ 0x54, KEY_RECORD },		/* recording */
+	{ 0x06, KEY_MUTE },		/* mute */
+	{ 0x10, KEY_POWER },
+	{ 0x40, KEY_LAST },		/* recall */
+	{ 0x4c, KEY_CHANNELUP },	/* channel / program + */
+	{ 0x00, KEY_CHANNELDOWN },	/* channel / program - */
+	{ 0x0d, KEY_VOLUMEUP },
+	{ 0x15, KEY_VOLUMEDOWN },
+	{ 0x4d, KEY_OK },		/* also labeled as Pause */
+	{ 0x1c, KEY_ZOOM },		/* full screen and Stop*/
+	{ 0x02, KEY_MODE },		/* AV Source or Rewind*/
+	{ 0x04, KEY_LIST },		/* -/-- */
 	/* small arrows above numbers */
-	[0x1a] = KEY_NEXT,		/* also Fast Forward */
-	[0x0e] = KEY_PREVIOUS,	/* also Rewind */
+	{ 0x1a, KEY_NEXT },		/* also Fast Forward */
+	{ 0x0e, KEY_PREVIOUS },		/* also Rewind */
 	/* these are in a rather non standard layout and have
 	an alternate name written */
-	[0x1e] = KEY_UP,		/* Video Setting */
-	[0x0a] = KEY_DOWN,		/* Video Default */
-	[0x05] = KEY_LEFT,		/* Snapshot */
-	[0x0c] = KEY_RIGHT,		/* Hide Panel */
+	{ 0x1e, KEY_UP },		/* Video Setting */
+	{ 0x0a, KEY_DOWN },		/* Video Default */
+	{ 0x05, KEY_CAMERA },		/* Snapshot */
+	{ 0x0c, KEY_RIGHT },		/* Hide Panel */
 	/* Four buttons without label */
-	[0x49] = KEY_RED,
-	[0x0b] = KEY_GREEN,
-	[0x13] = KEY_YELLOW,
-	[0x50] = KEY_BLUE,
+	{ 0x49, KEY_RED },
+	{ 0x0b, KEY_GREEN },
+	{ 0x13, KEY_YELLOW },
+	{ 0x50, KEY_BLUE },
+};
+
+struct ir_scancode_table ir_codes_genius_tvgo_a11mce_table = {
+	.scan = ir_codes_genius_tvgo_a11mce,
+	.size = ARRAY_SIZE(ir_codes_genius_tvgo_a11mce),
 };
-EXPORT_SYMBOL_GPL(ir_codes_genius_tvgo_a11mce);
+EXPORT_SYMBOL_GPL(ir_codes_genius_tvgo_a11mce_table);
 
 /*
  * Remote control for Powercolor Real Angel 330
  * Daniel Fraga <fragabr@gmail.com>
  */
-IR_KEYTAB_TYPE ir_codes_powercolor_real_angel[IR_KEYTAB_SIZE] = {
-	[0x38] = KEY_SWITCHVIDEOMODE,	/* switch inputs */
-	[0x0c] = KEY_MEDIA,		/* Turn ON/OFF App */
-	[0x00] = KEY_0,
-	[0x01] = KEY_1,
-	[0x02] = KEY_2,
-	[0x03] = KEY_3,
-	[0x04] = KEY_4,
-	[0x05] = KEY_5,
-	[0x06] = KEY_6,
-	[0x07] = KEY_7,
-	[0x08] = KEY_8,
-	[0x09] = KEY_9,
-	[0x0a] = KEY_DIGITS,		/* single, double, tripple digit */
-	[0x29] = KEY_PREVIOUS,		/* previous channel */
-	[0x12] = KEY_BRIGHTNESSUP,
-	[0x13] = KEY_BRIGHTNESSDOWN,
-	[0x2b] = KEY_MODE,		/* stereo/mono */
-	[0x2c] = KEY_TEXT,		/* teletext */
-	[0x20] = KEY_UP,		/* channel up */
-	[0x21] = KEY_DOWN,		/* channel down */
-	[0x10] = KEY_RIGHT,		/* volume up */
-	[0x11] = KEY_LEFT,		/* volume down */
-	[0x0d] = KEY_MUTE,
-	[0x1f] = KEY_RECORD,
-	[0x17] = KEY_PLAY,
-	[0x16] = KEY_PAUSE,
-	[0x0b] = KEY_STOP,
-	[0x27] = KEY_FASTFORWARD,
-	[0x26] = KEY_REWIND,
-	[0x1e] = KEY_SEARCH,		/* autoscan */
-	[0x0e] = KEY_SHUFFLE,		/* snapshot */
-	[0x2d] = KEY_SETUP,
-	[0x0f] = KEY_SCREEN,		/* full screen */
-	[0x14] = KEY_RADIO,		/* FM radio */
-	[0x25] = KEY_POWER,		/* power */
-};
-EXPORT_SYMBOL_GPL(ir_codes_powercolor_real_angel);
+static struct ir_scancode ir_codes_powercolor_real_angel[] = {
+	{ 0x38, KEY_SWITCHVIDEOMODE },	/* switch inputs */
+	{ 0x0c, KEY_MEDIA },		/* Turn ON/OFF App */
+	{ 0x00, KEY_0 },
+	{ 0x01, KEY_1 },
+	{ 0x02, KEY_2 },
+	{ 0x03, KEY_3 },
+	{ 0x04, KEY_4 },
+	{ 0x05, KEY_5 },
+	{ 0x06, KEY_6 },
+	{ 0x07, KEY_7 },
+	{ 0x08, KEY_8 },
+	{ 0x09, KEY_9 },
+	{ 0x0a, KEY_DIGITS },		/* single, double, tripple digit */
+	{ 0x29, KEY_PREVIOUS },		/* previous channel */
+	{ 0x12, KEY_BRIGHTNESSUP },
+	{ 0x13, KEY_BRIGHTNESSDOWN },
+	{ 0x2b, KEY_MODE },		/* stereo/mono */
+	{ 0x2c, KEY_TEXT },		/* teletext */
+	{ 0x20, KEY_CHANNELUP },	/* channel up */
+	{ 0x21, KEY_CHANNELDOWN },	/* channel down */
+	{ 0x10, KEY_VOLUMEUP },		/* volume up */
+	{ 0x11, KEY_VOLUMEDOWN },	/* volume down */
+	{ 0x0d, KEY_MUTE },
+	{ 0x1f, KEY_RECORD },
+	{ 0x17, KEY_PLAY },
+	{ 0x16, KEY_PAUSE },
+	{ 0x0b, KEY_STOP },
+	{ 0x27, KEY_FASTFORWARD },
+	{ 0x26, KEY_REWIND },
+	{ 0x1e, KEY_SEARCH },		/* autoscan */
+	{ 0x0e, KEY_CAMERA },		/* snapshot */
+	{ 0x2d, KEY_SETUP },
+	{ 0x0f, KEY_SCREEN },		/* full screen */
+	{ 0x14, KEY_RADIO },		/* FM radio */
+	{ 0x25, KEY_POWER },		/* power */
+};
+
+struct ir_scancode_table ir_codes_powercolor_real_angel_table = {
+	.scan = ir_codes_powercolor_real_angel,
+	.size = ARRAY_SIZE(ir_codes_powercolor_real_angel),
+};
+EXPORT_SYMBOL_GPL(ir_codes_powercolor_real_angel_table);
 
 /* Kworld Plus TV Analog Lite PCI IR
    Mauro Carvalho Chehab <mchehab@infradead.org>
  */
-IR_KEYTAB_TYPE ir_codes_kworld_plus_tv_analog[IR_KEYTAB_SIZE] = {
-	[0x0c] = KEY_PROG1,		/* Kworld key */
-	[0x16] = KEY_CLOSECD,		/* -> ) */
-	[0x1d] = KEY_POWER2,
-
-	[0x00] = KEY_1,
-	[0x01] = KEY_2,
-	[0x02] = KEY_3,			/* Two keys have the same code: 3 and left */
-	[0x03] = KEY_4,			/* Two keys have the same code: 3 and right */
-	[0x04] = KEY_5,
-	[0x05] = KEY_6,
-	[0x06] = KEY_7,
-	[0x07] = KEY_8,
-	[0x08] = KEY_9,
-	[0x0a] = KEY_0,
-
-	[0x09] = KEY_AGAIN,
-	[0x14] = KEY_MUTE,
-
-	[0x20] = KEY_UP,
-	[0x21] = KEY_DOWN,
-	[0x0b] = KEY_ENTER,
-
-	[0x10] = KEY_CHANNELUP,
-	[0x11] = KEY_CHANNELDOWN,
+static struct ir_scancode ir_codes_kworld_plus_tv_analog[] = {
+	{ 0x0c, KEY_PROG1 },		/* Kworld key */
+	{ 0x16, KEY_CLOSECD },		/* -> ) */
+	{ 0x1d, KEY_POWER2 },
+
+	{ 0x00, KEY_1 },
+	{ 0x01, KEY_2 },
+	{ 0x02, KEY_3 },		/* Two keys have the same code: 3 and left */
+	{ 0x03, KEY_4 },		/* Two keys have the same code: 3 and right */
+	{ 0x04, KEY_5 },
+	{ 0x05, KEY_6 },
+	{ 0x06, KEY_7 },
+	{ 0x07, KEY_8 },
+	{ 0x08, KEY_9 },
+	{ 0x0a, KEY_0 },
+
+	{ 0x09, KEY_AGAIN },
+	{ 0x14, KEY_MUTE },
+
+	{ 0x20, KEY_UP },
+	{ 0x21, KEY_DOWN },
+	{ 0x0b, KEY_ENTER },
+
+	{ 0x10, KEY_CHANNELUP },
+	{ 0x11, KEY_CHANNELDOWN },
 
 	/* Couldn't map key left/key right since those
 	   conflict with '3' and '4' scancodes
 	   I dunno what the original driver does
 	 */
 
-	[0x13] = KEY_VOLUMEUP,
-	[0x12] = KEY_VOLUMEDOWN,
+	{ 0x13, KEY_VOLUMEUP },
+	{ 0x12, KEY_VOLUMEDOWN },
 
 	/* The lower part of the IR
 	   There are several duplicated keycodes there.
@@ -2496,280 +2682,468 @@ IR_KEYTAB_TYPE ir_codes_kworld_plus_tv_analog[IR_KEYTAB_SIZE] = {
 	   Also, it is not related to the time between keyup
 	   and keydown.
 	 */
-	[0x19] = KEY_PAUSE,		/* Timeshift */
-	[0x1a] = KEY_STOP,
-	[0x1b] = KEY_RECORD,
+	{ 0x19, KEY_TIME},		/* Timeshift */
+	{ 0x1a, KEY_STOP},
+	{ 0x1b, KEY_RECORD},
 
-	[0x22] = KEY_TEXT,
+	{ 0x22, KEY_TEXT},
 
-	[0x15] = KEY_AUDIO,		/* ((*)) */
-	[0x0f] = KEY_ZOOM,
-	[0x1c] = KEY_SHUFFLE,		/* snapshot */
+	{ 0x15, KEY_AUDIO},		/* ((*)) */
+	{ 0x0f, KEY_ZOOM},
+	{ 0x1c, KEY_CAMERA},		/* snapshot */
 
-	[0x18] = KEY_RED,		/* B */
-	[0x23] = KEY_GREEN,		/* C */
+	{ 0x18, KEY_RED},		/* B */
+	{ 0x23, KEY_GREEN},		/* C */
 };
-EXPORT_SYMBOL_GPL(ir_codes_kworld_plus_tv_analog);
+struct ir_scancode_table ir_codes_kworld_plus_tv_analog_table = {
+	.scan = ir_codes_kworld_plus_tv_analog,
+	.size = ARRAY_SIZE(ir_codes_kworld_plus_tv_analog),
+};
+EXPORT_SYMBOL_GPL(ir_codes_kworld_plus_tv_analog_table);
 
 /* Kaiomy TVnPC U2
    Mauro Carvalho Chehab <mchehab@infradead.org>
  */
-IR_KEYTAB_TYPE ir_codes_kaiomy[IR_KEYTAB_SIZE] = {
-	[0x43] = KEY_POWER2,
-	[0x01] = KEY_LIST,
-	[0x0b] = KEY_ZOOM,
-	[0x03] = KEY_POWER,
-
-	[0x04] = KEY_1,
-	[0x08] = KEY_2,
-	[0x02] = KEY_3,
-
-	[0x0f] = KEY_4,
-	[0x05] = KEY_5,
-	[0x06] = KEY_6,
-
-	[0x0c] = KEY_7,
-	[0x0d] = KEY_8,
-	[0x0a] = KEY_9,
-
-	[0x11] = KEY_0,
-
-	[0x09] = KEY_CHANNELUP,
-	[0x07] = KEY_CHANNELDOWN,
-
-	[0x0e] = KEY_VOLUMEUP,
-	[0x13] = KEY_VOLUMEDOWN,
-
-	[0x10] = KEY_HOME,
-	[0x12] = KEY_ENTER,
-
-	[0x14] = KEY_RECORD,
-	[0x15] = KEY_STOP,
-	[0x16] = KEY_PLAY,
-	[0x17] = KEY_MUTE,
-
-	[0x18] = KEY_UP,
-	[0x19] = KEY_DOWN,
-	[0x1a] = KEY_LEFT,
-	[0x1b] = KEY_RIGHT,
-
-	[0x1c] = KEY_RED,
-	[0x1d] = KEY_GREEN,
-	[0x1e] = KEY_YELLOW,
-	[0x1f] = KEY_BLUE,
-};
-EXPORT_SYMBOL_GPL(ir_codes_kaiomy);
-
-IR_KEYTAB_TYPE ir_codes_avermedia_a16d[IR_KEYTAB_SIZE] = {
-	[0x20] = KEY_LIST,
-	[0x00] = KEY_POWER,
-	[0x28] = KEY_1,
-	[0x18] = KEY_2,
-	[0x38] = KEY_3,
-	[0x24] = KEY_4,
-	[0x14] = KEY_5,
-	[0x34] = KEY_6,
-	[0x2c] = KEY_7,
-	[0x1c] = KEY_8,
-	[0x3c] = KEY_9,
-	[0x12] = KEY_SUBTITLE,
-	[0x22] = KEY_0,
-	[0x32] = KEY_REWIND,
-	[0x3a] = KEY_SHUFFLE,
-	[0x02] = KEY_PRINT,
-	[0x11] = KEY_CHANNELDOWN,
-	[0x31] = KEY_CHANNELUP,
-	[0x0c] = KEY_ZOOM,
-	[0x1e] = KEY_VOLUMEDOWN,
-	[0x3e] = KEY_VOLUMEUP,
-	[0x0a] = KEY_MUTE,
-	[0x04] = KEY_AUDIO,
-	[0x26] = KEY_RECORD,
-	[0x06] = KEY_PLAY,
-	[0x36] = KEY_STOP,
-	[0x16] = KEY_PAUSE,
-	[0x2e] = KEY_REWIND,
-	[0x0e] = KEY_FASTFORWARD,
-	[0x30] = KEY_TEXT,
-	[0x21] = KEY_GREEN,
-	[0x01] = KEY_BLUE,
-	[0x08] = KEY_EPG,
-	[0x2a] = KEY_MENU,
-};
-EXPORT_SYMBOL_GPL(ir_codes_avermedia_a16d);
+static struct ir_scancode ir_codes_kaiomy[] = {
+	{ 0x43, KEY_POWER2},
+	{ 0x01, KEY_LIST},
+	{ 0x0b, KEY_ZOOM},
+	{ 0x03, KEY_POWER},
 
-/* Encore ENLTV-FM v5.3
-   Mauro Carvalho Chehab <mchehab@infradead.org>
- */
-IR_KEYTAB_TYPE ir_codes_encore_enltv_fm53[IR_KEYTAB_SIZE] = {
-	[0x10] = KEY_POWER2,
-	[0x06] = KEY_MUTE,
-
-	[0x09] = KEY_1,
-	[0x1d] = KEY_2,
-	[0x1f] = KEY_3,
-	[0x19] = KEY_4,
-	[0x1b] = KEY_5,
-	[0x11] = KEY_6,
-	[0x17] = KEY_7,
-	[0x12] = KEY_8,
-	[0x16] = KEY_9,
-	[0x48] = KEY_0,
-
-	[0x04] = KEY_LIST,		/* -/-- */
-	[0x40] = KEY_LAST,		/* recall */
-
-	[0x02] = KEY_MODE,		/* TV/AV */
-	[0x05] = KEY_SHUFFLE,		/* SNAPSHOT */
-
-	[0x4c] = KEY_CHANNELUP,		/* UP */
-	[0x00] = KEY_CHANNELDOWN,	/* DOWN */
-	[0x0d] = KEY_VOLUMEUP,		/* RIGHT */
-	[0x15] = KEY_VOLUMEDOWN,	/* LEFT */
-	[0x49] = KEY_ENTER,		/* OK */
-
-	[0x54] = KEY_RECORD,
-	[0x4d] = KEY_PLAY,		/* pause */
-
-	[0x1e] = KEY_UP,		/* video setting */
-	[0x0e] = KEY_RIGHT,		/* <- */
-	[0x1a] = KEY_LEFT,		/* -> */
-
-	[0x0a] = KEY_DOWN,		/* video default */
-	[0x0c] = KEY_ZOOM,		/* hide pannel */
-	[0x47] = KEY_SLEEP,		/* shutdown */
-};
-EXPORT_SYMBOL_GPL(ir_codes_encore_enltv_fm53);
+	{ 0x04, KEY_1},
+	{ 0x08, KEY_2},
+	{ 0x02, KEY_3},
 
-/* Zogis Real Audio 220 - 32 keys IR */
-IR_KEYTAB_TYPE ir_codes_real_audio_220_32_keys[IR_KEYTAB_SIZE] = {
-	[0x1c] = KEY_RADIO,
-	[0x12] = KEY_POWER2,
+	{ 0x0f, KEY_4},
+	{ 0x05, KEY_5},
+	{ 0x06, KEY_6},
+
+	{ 0x0c, KEY_7},
+	{ 0x0d, KEY_8},
+	{ 0x0a, KEY_9},
 
-	[0x01] = KEY_1,
-	[0x02] = KEY_2,
-	[0x03] = KEY_3,
-	[0x04] = KEY_4,
-	[0x05] = KEY_5,
-	[0x06] = KEY_6,
-	[0x07] = KEY_7,
-	[0x08] = KEY_8,
-	[0x09] = KEY_9,
-	[0x00] = KEY_0,
+	{ 0x11, KEY_0},
 
-	[0x0c] = KEY_VOLUMEUP,
-	[0x18] = KEY_VOLUMEDOWN,
-	[0x0b] = KEY_CHANNELUP,
-	[0x15] = KEY_CHANNELDOWN,
-	[0x16] = KEY_ENTER,
+	{ 0x09, KEY_CHANNELUP},
+	{ 0x07, KEY_CHANNELDOWN},
 
-	[0x11] = KEY_LIST,		/* Source */
-	[0x0d] = KEY_AUDIO,		/* stereo */
+	{ 0x0e, KEY_VOLUMEUP},
+	{ 0x13, KEY_VOLUMEDOWN},
 
-	[0x0f] = KEY_PREVIOUS,		/* Prev */
-	[0x1b] = KEY_PAUSE,		/* Timeshift */
-	[0x1a] = KEY_NEXT,		/* Next */
+	{ 0x10, KEY_HOME},
+	{ 0x12, KEY_ENTER},
 
-	[0x0e] = KEY_STOP,
-	[0x1f] = KEY_PLAY,
-	[0x1e] = KEY_PLAYPAUSE,		/* Pause */
+	{ 0x14, KEY_RECORD},
+	{ 0x15, KEY_STOP},
+	{ 0x16, KEY_PLAY},
+	{ 0x17, KEY_MUTE},
 
-	[0x1d] = KEY_RECORD,
-	[0x13] = KEY_MUTE,
-	[0x19] = KEY_SHUFFLE,		/* Snapshot */
+	{ 0x18, KEY_UP},
+	{ 0x19, KEY_DOWN},
+	{ 0x1a, KEY_LEFT},
+	{ 0x1b, KEY_RIGHT},
 
+	{ 0x1c, KEY_RED},
+	{ 0x1d, KEY_GREEN},
+	{ 0x1e, KEY_YELLOW},
+	{ 0x1f, KEY_BLUE},
+};
+struct ir_scancode_table ir_codes_kaiomy_table = {
+	.scan = ir_codes_kaiomy,
+	.size = ARRAY_SIZE(ir_codes_kaiomy),
+};
+EXPORT_SYMBOL_GPL(ir_codes_kaiomy_table);
+
+static struct ir_scancode ir_codes_avermedia_a16d[] = {
+	{ 0x20, KEY_LIST},
+	{ 0x00, KEY_POWER},
+	{ 0x28, KEY_1},
+	{ 0x18, KEY_2},
+	{ 0x38, KEY_3},
+	{ 0x24, KEY_4},
+	{ 0x14, KEY_5},
+	{ 0x34, KEY_6},
+	{ 0x2c, KEY_7},
+	{ 0x1c, KEY_8},
+	{ 0x3c, KEY_9},
+	{ 0x12, KEY_SUBTITLE},
+	{ 0x22, KEY_0},
+	{ 0x32, KEY_REWIND},
+	{ 0x3a, KEY_SHUFFLE},
+	{ 0x02, KEY_PRINT},
+	{ 0x11, KEY_CHANNELDOWN},
+	{ 0x31, KEY_CHANNELUP},
+	{ 0x0c, KEY_ZOOM},
+	{ 0x1e, KEY_VOLUMEDOWN},
+	{ 0x3e, KEY_VOLUMEUP},
+	{ 0x0a, KEY_MUTE},
+	{ 0x04, KEY_AUDIO},
+	{ 0x26, KEY_RECORD},
+	{ 0x06, KEY_PLAY},
+	{ 0x36, KEY_STOP},
+	{ 0x16, KEY_PAUSE},
+	{ 0x2e, KEY_REWIND},
+	{ 0x0e, KEY_FASTFORWARD},
+	{ 0x30, KEY_TEXT},
+	{ 0x21, KEY_GREEN},
+	{ 0x01, KEY_BLUE},
+	{ 0x08, KEY_EPG},
+	{ 0x2a, KEY_MENU},
+};
+struct ir_scancode_table ir_codes_avermedia_a16d_table = {
+	.scan = ir_codes_avermedia_a16d,
+	.size = ARRAY_SIZE(ir_codes_avermedia_a16d),
 };
-EXPORT_SYMBOL_GPL(ir_codes_real_audio_220_32_keys);
+EXPORT_SYMBOL_GPL(ir_codes_avermedia_a16d_table);
+
+/* Encore ENLTV-FM v5.3
+   Mauro Carvalho Chehab <mchehab@infradead.org>
+ */
+static struct ir_scancode ir_codes_encore_enltv_fm53[] = {
+	{ 0x10, KEY_POWER2},
+	{ 0x06, KEY_MUTE},
+
+	{ 0x09, KEY_1},
+	{ 0x1d, KEY_2},
+	{ 0x1f, KEY_3},
+	{ 0x19, KEY_4},
+	{ 0x1b, KEY_5},
+	{ 0x11, KEY_6},
+	{ 0x17, KEY_7},
+	{ 0x12, KEY_8},
+	{ 0x16, KEY_9},
+	{ 0x48, KEY_0},
+
+	{ 0x04, KEY_LIST},		/* -/-- */
+	{ 0x40, KEY_LAST},		/* recall */
+
+	{ 0x02, KEY_MODE},		/* TV/AV */
+	{ 0x05, KEY_CAMERA},		/* SNAPSHOT */
+
+	{ 0x4c, KEY_CHANNELUP},		/* UP */
+	{ 0x00, KEY_CHANNELDOWN},	/* DOWN */
+	{ 0x0d, KEY_VOLUMEUP},		/* RIGHT */
+	{ 0x15, KEY_VOLUMEDOWN},	/* LEFT */
+	{ 0x49, KEY_ENTER},		/* OK */
+
+	{ 0x54, KEY_RECORD},
+	{ 0x4d, KEY_PLAY},		/* pause */
+
+	{ 0x1e, KEY_MENU},		/* video setting */
+	{ 0x0e, KEY_RIGHT},		/* <- */
+	{ 0x1a, KEY_LEFT},		/* -> */
+
+	{ 0x0a, KEY_CLEAR},		/* video default */
+	{ 0x0c, KEY_ZOOM},		/* hide pannel */
+	{ 0x47, KEY_SLEEP},		/* shutdown */
+};
+struct ir_scancode_table ir_codes_encore_enltv_fm53_table = {
+	.scan = ir_codes_encore_enltv_fm53,
+	.size = ARRAY_SIZE(ir_codes_encore_enltv_fm53),
+};
+EXPORT_SYMBOL_GPL(ir_codes_encore_enltv_fm53_table);
+
+/* Zogis Real Audio 220 - 32 keys IR */
+static struct ir_scancode ir_codes_real_audio_220_32_keys[] = {
+	{ 0x1c, KEY_RADIO},
+	{ 0x12, KEY_POWER2},
+
+	{ 0x01, KEY_1},
+	{ 0x02, KEY_2},
+	{ 0x03, KEY_3},
+	{ 0x04, KEY_4},
+	{ 0x05, KEY_5},
+	{ 0x06, KEY_6},
+	{ 0x07, KEY_7},
+	{ 0x08, KEY_8},
+	{ 0x09, KEY_9},
+	{ 0x00, KEY_0},
+
+	{ 0x0c, KEY_VOLUMEUP},
+	{ 0x18, KEY_VOLUMEDOWN},
+	{ 0x0b, KEY_CHANNELUP},
+	{ 0x15, KEY_CHANNELDOWN},
+	{ 0x16, KEY_ENTER},
+
+	{ 0x11, KEY_LIST},		/* Source */
+	{ 0x0d, KEY_AUDIO},		/* stereo */
+
+	{ 0x0f, KEY_PREVIOUS},		/* Prev */
+	{ 0x1b, KEY_TIME},		/* Timeshift */
+	{ 0x1a, KEY_NEXT},		/* Next */
+
+	{ 0x0e, KEY_STOP},
+	{ 0x1f, KEY_PLAY},
+	{ 0x1e, KEY_PLAYPAUSE},		/* Pause */
+
+	{ 0x1d, KEY_RECORD},
+	{ 0x13, KEY_MUTE},
+	{ 0x19, KEY_CAMERA},		/* Snapshot */
+
+};
+struct ir_scancode_table ir_codes_real_audio_220_32_keys_table = {
+	.scan = ir_codes_real_audio_220_32_keys,
+	.size = ARRAY_SIZE(ir_codes_real_audio_220_32_keys),
+};
+EXPORT_SYMBOL_GPL(ir_codes_real_audio_220_32_keys_table);
 
 /* ATI TV Wonder HD 600 USB
    Devin Heitmueller <devin.heitmueller@gmail.com>
  */
-IR_KEYTAB_TYPE ir_codes_ati_tv_wonder_hd_600[IR_KEYTAB_SIZE] = {
-	[0x00] = KEY_RECORD,		/* Row 1 */
-	[0x01] = KEY_PLAYPAUSE,
-	[0x02] = KEY_STOP,
-	[0x03] = KEY_POWER,
-	[0x04] = KEY_PREVIOUS,	/* Row 2 */
-	[0x05] = KEY_REWIND,
-	[0x06] = KEY_FORWARD,
-	[0x07] = KEY_NEXT,
-	[0x08] = KEY_EPG,		/* Row 3 */
-	[0x09] = KEY_HOME,
-	[0x0a] = KEY_MENU,
-	[0x0b] = KEY_CHANNELUP,
-	[0x0c] = KEY_BACK,		/* Row 4 */
-	[0x0d] = KEY_UP,
-	[0x0e] = KEY_INFO,
-	[0x0f] = KEY_CHANNELDOWN,
-	[0x10] = KEY_LEFT,		/* Row 5 */
-	[0x11] = KEY_SELECT,
-	[0x12] = KEY_RIGHT,
-	[0x13] = KEY_VOLUMEUP,
-	[0x14] = KEY_LAST,		/* Row 6 */
-	[0x15] = KEY_DOWN,
-	[0x16] = KEY_MUTE,
-	[0x17] = KEY_VOLUMEDOWN,
-};
-
-EXPORT_SYMBOL_GPL(ir_codes_ati_tv_wonder_hd_600);
+static struct ir_scancode ir_codes_ati_tv_wonder_hd_600[] = {
+	{ 0x00, KEY_RECORD},		/* Row 1 */
+	{ 0x01, KEY_PLAYPAUSE},
+	{ 0x02, KEY_STOP},
+	{ 0x03, KEY_POWER},
+	{ 0x04, KEY_PREVIOUS},	/* Row 2 */
+	{ 0x05, KEY_REWIND},
+	{ 0x06, KEY_FORWARD},
+	{ 0x07, KEY_NEXT},
+	{ 0x08, KEY_EPG},		/* Row 3 */
+	{ 0x09, KEY_HOME},
+	{ 0x0a, KEY_MENU},
+	{ 0x0b, KEY_CHANNELUP},
+	{ 0x0c, KEY_BACK},		/* Row 4 */
+	{ 0x0d, KEY_UP},
+	{ 0x0e, KEY_INFO},
+	{ 0x0f, KEY_CHANNELDOWN},
+	{ 0x10, KEY_LEFT},		/* Row 5 */
+	{ 0x11, KEY_SELECT},
+	{ 0x12, KEY_RIGHT},
+	{ 0x13, KEY_VOLUMEUP},
+	{ 0x14, KEY_LAST},		/* Row 6 */
+	{ 0x15, KEY_DOWN},
+	{ 0x16, KEY_MUTE},
+	{ 0x17, KEY_VOLUMEDOWN},
+};
+struct ir_scancode_table ir_codes_ati_tv_wonder_hd_600_table = {
+	.scan = ir_codes_ati_tv_wonder_hd_600,
+	.size = ARRAY_SIZE(ir_codes_ati_tv_wonder_hd_600),
+};
+EXPORT_SYMBOL_GPL(ir_codes_ati_tv_wonder_hd_600_table);
 
 /* DVBWorld remotes
    Igor M. Liplianin <liplianin@me.by>
  */
-IR_KEYTAB_TYPE ir_codes_dm1105_nec[IR_KEYTAB_SIZE] = {
-	[0x0a] = KEY_Q,		/*power*/
-	[0x0c] = KEY_M,		/*mute*/
-	[0x11] = KEY_1,
-	[0x12] = KEY_2,
-	[0x13] = KEY_3,
-	[0x14] = KEY_4,
-	[0x15] = KEY_5,
-	[0x16] = KEY_6,
-	[0x17] = KEY_7,
-	[0x18] = KEY_8,
-	[0x19] = KEY_9,
-	[0x10] = KEY_0,
-	[0x1c] = KEY_PAGEUP,	/*ch+*/
-	[0x0f] = KEY_PAGEDOWN,	/*ch-*/
-	[0x1a] = KEY_O,		/*vol+*/
-	[0x0e] = KEY_Z,		/*vol-*/
-	[0x04] = KEY_R,		/*rec*/
-	[0x09] = KEY_D,		/*fav*/
-	[0x08] = KEY_BACKSPACE,	/*rewind*/
-	[0x07] = KEY_A,		/*fast*/
-	[0x0b] = KEY_P,		/*pause*/
-	[0x02] = KEY_ESC,	/*cancel*/
-	[0x03] = KEY_G,		/*tab*/
-	[0x00] = KEY_UP,	/*up*/
-	[0x1f] = KEY_ENTER,	/*ok*/
-	[0x01] = KEY_DOWN,	/*down*/
-	[0x05] = KEY_C,		/*cap*/
-	[0x06] = KEY_S,		/*stop*/
-	[0x40] = KEY_F,		/*full*/
-	[0x1e] = KEY_W,		/*tvmode*/
-	[0x1b] = KEY_B,		/*recall*/
-};
-EXPORT_SYMBOL_GPL(ir_codes_dm1105_nec);
+static struct ir_scancode ir_codes_dm1105_nec[] = {
+	{ 0x0a, KEY_POWER2},		/* power */
+	{ 0x0c, KEY_MUTE},		/* mute */
+	{ 0x11, KEY_1},
+	{ 0x12, KEY_2},
+	{ 0x13, KEY_3},
+	{ 0x14, KEY_4},
+	{ 0x15, KEY_5},
+	{ 0x16, KEY_6},
+	{ 0x17, KEY_7},
+	{ 0x18, KEY_8},
+	{ 0x19, KEY_9},
+	{ 0x10, KEY_0},
+	{ 0x1c, KEY_CHANNELUP},		/* ch+ */
+	{ 0x0f, KEY_CHANNELDOWN},	/* ch- */
+	{ 0x1a, KEY_VOLUMEUP},		/* vol+ */
+	{ 0x0e, KEY_VOLUMEDOWN},	/* vol- */
+	{ 0x04, KEY_RECORD},		/* rec */
+	{ 0x09, KEY_CHANNEL},		/* fav */
+	{ 0x08, KEY_BACKSPACE},		/* rewind */
+	{ 0x07, KEY_FASTFORWARD},	/* fast */
+	{ 0x0b, KEY_PAUSE},		/* pause */
+	{ 0x02, KEY_ESC},		/* cancel */
+	{ 0x03, KEY_TAB},		/* tab */
+	{ 0x00, KEY_UP},		/* up */
+	{ 0x1f, KEY_ENTER},		/* ok */
+	{ 0x01, KEY_DOWN},		/* down */
+	{ 0x05, KEY_RECORD},		/* cap */
+	{ 0x06, KEY_STOP},		/* stop */
+	{ 0x40, KEY_ZOOM},		/* full */
+	{ 0x1e, KEY_TV},		/* tvmode */
+	{ 0x1b, KEY_B},			/* recall */
+};
+struct ir_scancode_table ir_codes_dm1105_nec_table = {
+	.scan = ir_codes_dm1105_nec,
+	.size = ARRAY_SIZE(ir_codes_dm1105_nec),
+};
+EXPORT_SYMBOL_GPL(ir_codes_dm1105_nec_table);
+
+/* Terratec Cinergy Hybrid T USB XS
+   Devin Heitmueller <dheitmueller@linuxtv.org>
+ */
+static struct ir_scancode ir_codes_terratec_cinergy_xs[] = {
+	{ 0x41, KEY_HOME},
+	{ 0x01, KEY_POWER},
+	{ 0x42, KEY_MENU},
+	{ 0x02, KEY_1},
+	{ 0x03, KEY_2},
+	{ 0x04, KEY_3},
+	{ 0x43, KEY_SUBTITLE},
+	{ 0x05, KEY_4},
+	{ 0x06, KEY_5},
+	{ 0x07, KEY_6},
+	{ 0x44, KEY_TEXT},
+	{ 0x08, KEY_7},
+	{ 0x09, KEY_8},
+	{ 0x0a, KEY_9},
+	{ 0x45, KEY_DELETE},
+	{ 0x0b, KEY_TUNER},
+	{ 0x0c, KEY_0},
+	{ 0x0d, KEY_MODE},
+	{ 0x46, KEY_TV},
+	{ 0x47, KEY_DVD},
+	{ 0x49, KEY_VIDEO},
+	{ 0x4b, KEY_AUX},
+	{ 0x10, KEY_UP},
+	{ 0x11, KEY_LEFT},
+	{ 0x12, KEY_OK},
+	{ 0x13, KEY_RIGHT},
+	{ 0x14, KEY_DOWN},
+	{ 0x0f, KEY_EPG},
+	{ 0x16, KEY_INFO},
+	{ 0x4d, KEY_BACKSPACE},
+	{ 0x1c, KEY_VOLUMEUP},
+	{ 0x4c, KEY_PLAY},
+	{ 0x1b, KEY_CHANNELUP},
+	{ 0x1e, KEY_VOLUMEDOWN},
+	{ 0x1d, KEY_MUTE},
+	{ 0x1f, KEY_CHANNELDOWN},
+	{ 0x17, KEY_RED},
+	{ 0x18, KEY_GREEN},
+	{ 0x19, KEY_YELLOW},
+	{ 0x1a, KEY_BLUE},
+	{ 0x58, KEY_RECORD},
+	{ 0x48, KEY_STOP},
+	{ 0x40, KEY_PAUSE},
+	{ 0x54, KEY_LAST},
+	{ 0x4e, KEY_REWIND},
+	{ 0x4f, KEY_FASTFORWARD},
+	{ 0x5c, KEY_NEXT},
+};
+struct ir_scancode_table ir_codes_terratec_cinergy_xs_table = {
+	.scan = ir_codes_terratec_cinergy_xs,
+	.size = ARRAY_SIZE(ir_codes_terratec_cinergy_xs),
+};
+EXPORT_SYMBOL_GPL(ir_codes_terratec_cinergy_xs_table);
 
 /* EVGA inDtube
    Devin Heitmueller <devin.heitmueller@gmail.com>
  */
-IR_KEYTAB_TYPE ir_codes_evga_indtube[IR_KEYTAB_SIZE] = {
-	[0x12] = KEY_POWER,
-	[0x02] = KEY_MODE,	/* TV */
-	[0x14] = KEY_MUTE,
-	[0x1a] = KEY_CHANNELUP,
-	[0x16] = KEY_TV2,	/* PIP */
-	[0x1d] = KEY_VOLUMEUP,
-	[0x05] = KEY_CHANNELDOWN,
-	[0x0f] = KEY_PLAYPAUSE,
-	[0x19] = KEY_VOLUMEDOWN,
-	[0x1c] = KEY_REWIND,
-	[0x0d] = KEY_RECORD,
-	[0x18] = KEY_FORWARD,
-	[0x1e] = KEY_PREVIOUS,
-	[0x1b] = KEY_STOP,
-	[0x1f] = KEY_NEXT,
-	[0x13] = KEY_CAMERA,
-};
-EXPORT_SYMBOL_GPL(ir_codes_evga_indtube);
+static struct ir_scancode ir_codes_evga_indtube[] = {
+	{ 0x12, KEY_POWER},
+	{ 0x02, KEY_MODE},	/* TV */
+	{ 0x14, KEY_MUTE},
+	{ 0x1a, KEY_CHANNELUP},
+	{ 0x16, KEY_TV2},	/* PIP */
+	{ 0x1d, KEY_VOLUMEUP},
+	{ 0x05, KEY_CHANNELDOWN},
+	{ 0x0f, KEY_PLAYPAUSE},
+	{ 0x19, KEY_VOLUMEDOWN},
+	{ 0x1c, KEY_REWIND},
+	{ 0x0d, KEY_RECORD},
+	{ 0x18, KEY_FORWARD},
+	{ 0x1e, KEY_PREVIOUS},
+	{ 0x1b, KEY_STOP},
+	{ 0x1f, KEY_NEXT},
+	{ 0x13, KEY_CAMERA},
+};
+struct ir_scancode_table ir_codes_evga_indtube_table = {
+	.scan = ir_codes_evga_indtube,
+	.size = ARRAY_SIZE(ir_codes_evga_indtube),
+};
+EXPORT_SYMBOL_GPL(ir_codes_evga_indtube_table);
+
+static struct ir_scancode ir_codes_videomate_s350[] = {
+	{ 0x00, KEY_TV},
+	{ 0x01, KEY_DVD},
+	{ 0x04, KEY_RECORD},
+	{ 0x05, KEY_VIDEO},	/* TV/Video */
+	{ 0x07, KEY_STOP},
+	{ 0x08, KEY_PLAYPAUSE},
+	{ 0x0a, KEY_REWIND},
+	{ 0x0f, KEY_FASTFORWARD},
+	{ 0x10, KEY_CHANNELUP},
+	{ 0x12, KEY_VOLUMEUP},
+	{ 0x13, KEY_CHANNELDOWN},
+	{ 0x14, KEY_MUTE},
+	{ 0x15, KEY_VOLUMEDOWN},
+	{ 0x16, KEY_1},
+	{ 0x17, KEY_2},
+	{ 0x18, KEY_3},
+	{ 0x19, KEY_4},
+	{ 0x1a, KEY_5},
+	{ 0x1b, KEY_6},
+	{ 0x1c, KEY_7},
+	{ 0x1d, KEY_8},
+	{ 0x1e, KEY_9},
+	{ 0x1f, KEY_0},
+	{ 0x21, KEY_SLEEP},
+	{ 0x24, KEY_ZOOM},
+	{ 0x25, KEY_LAST},	/* Recall */
+	{ 0x26, KEY_SUBTITLE},	/* CC */
+	{ 0x27, KEY_LANGUAGE},	/* MTS */
+	{ 0x29, KEY_CHANNEL},	/* SURF */
+	{ 0x2b, KEY_A},
+	{ 0x2c, KEY_B},
+	{ 0x2f, KEY_CAMERA},	/* Snapshot */
+	{ 0x23, KEY_RADIO},
+	{ 0x02, KEY_PREVIOUSSONG},
+	{ 0x06, KEY_NEXTSONG},
+	{ 0x03, KEY_EPG},
+	{ 0x09, KEY_SETUP},
+	{ 0x22, KEY_BACKSPACE},
+	{ 0x0c, KEY_UP},
+	{ 0x0e, KEY_DOWN},
+	{ 0x0b, KEY_LEFT},
+	{ 0x0d, KEY_RIGHT},
+	{ 0x11, KEY_ENTER},
+	{ 0x20, KEY_TEXT},
+};
+struct ir_scancode_table ir_codes_videomate_s350_table = {
+	.scan = ir_codes_videomate_s350,
+	.size = ARRAY_SIZE(ir_codes_videomate_s350),
+};
+EXPORT_SYMBOL_GPL(ir_codes_videomate_s350_table);
+
+/* GADMEI UTV330+ RM008Z remote
+   Shine Liu <shinel@foxmail.com>
+ */
+static struct ir_scancode ir_codes_gadmei_rm008z[] = {
+	{ 0x14, KEY_POWER2},		/* POWER OFF */
+	{ 0x0c, KEY_MUTE},		/* MUTE */
+
+	{ 0x18, KEY_TV},		/* TV */
+	{ 0x0e, KEY_VIDEO},		/* AV */
+	{ 0x0b, KEY_AUDIO},		/* SV */
+	{ 0x0f, KEY_RADIO},		/* FM */
+
+	{ 0x00, KEY_1},
+	{ 0x01, KEY_2},
+	{ 0x02, KEY_3},
+	{ 0x03, KEY_4},
+	{ 0x04, KEY_5},
+	{ 0x05, KEY_6},
+	{ 0x06, KEY_7},
+	{ 0x07, KEY_8},
+	{ 0x08, KEY_9},
+	{ 0x09, KEY_0},
+	{ 0x0a, KEY_INFO},		/* OSD */
+	{ 0x1c, KEY_BACKSPACE},		/* LAST */
+
+	{ 0x0d, KEY_PLAY},		/* PLAY */
+	{ 0x1e, KEY_CAMERA},		/* SNAPSHOT */
+	{ 0x1a, KEY_RECORD},		/* RECORD */
+	{ 0x17, KEY_STOP},		/* STOP */
+
+	{ 0x1f, KEY_UP},		/* UP */
+	{ 0x44, KEY_DOWN},		/* DOWN */
+	{ 0x46, KEY_TAB},		/* BACK */
+	{ 0x4a, KEY_ZOOM},		/* FULLSECREEN */
+
+	{ 0x10, KEY_VOLUMEUP},		/* VOLUMEUP */
+	{ 0x11, KEY_VOLUMEDOWN},	/* VOLUMEDOWN */
+	{ 0x12, KEY_CHANNELUP},		/* CHANNELUP */
+	{ 0x13, KEY_CHANNELDOWN},	/* CHANNELDOWN */
+	{ 0x15, KEY_ENTER},		/* OK */
+};
+struct ir_scancode_table ir_codes_gadmei_rm008z_table = {
+	.scan = ir_codes_gadmei_rm008z,
+	.size = ARRAY_SIZE(ir_codes_gadmei_rm008z),
+};
+EXPORT_SYMBOL_GPL(ir_codes_gadmei_rm008z_table);

+ 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

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