File-copy from v4.4.100

This is the result of 'cp' from a linux-stable tree with the 'v4.4.100'
tag checked out (commit 26d6298789e695c9f627ce49a7bbd2286405798a) on
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git

Please refer to that tree for all history prior to this point.

Change-Id: I8a9ee2aea93cd29c52c847d0ce33091a73ae6afe
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
new file mode 100644
index 0000000..b6e1311
--- /dev/null
+++ b/drivers/media/rc/Kconfig
@@ -0,0 +1,391 @@
+config RC_CORE
+	tristate
+	depends on MEDIA_RC_SUPPORT
+	depends on INPUT
+	default y
+
+source "drivers/media/rc/keymaps/Kconfig"
+
+menuconfig RC_DECODERS
+        bool "Remote controller decoders"
+	depends on RC_CORE
+	default y
+
+if RC_DECODERS
+config LIRC
+	tristate "LIRC interface driver"
+	depends on RC_CORE
+
+	---help---
+	   Enable this option to build the Linux Infrared Remote
+	   Control (LIRC) core device interface driver. The LIRC
+	   interface passes raw IR to and from userspace, where the
+	   LIRC daemon handles protocol decoding for IR reception and
+	   encoding for IR transmitting (aka "blasting").
+
+config IR_LIRC_CODEC
+	tristate "Enable IR to LIRC bridge"
+	depends on RC_CORE
+	depends on LIRC
+	default y
+
+	---help---
+	   Enable this option to pass raw IR to and from userspace via
+	   the LIRC interface.
+
+
+config IR_NEC_DECODER
+	tristate "Enable IR raw decoder for the NEC protocol"
+	depends on RC_CORE
+	select BITREVERSE
+	default y
+
+	---help---
+	   Enable this option if you have IR with NEC protocol, and
+	   if the IR is decoded in software
+
+config IR_RC5_DECODER
+	tristate "Enable IR raw decoder for the RC-5 protocol"
+	depends on RC_CORE
+	select BITREVERSE
+	default y
+
+	---help---
+	   Enable this option if you have IR with RC-5 protocol, and
+	   if the IR is decoded in software
+
+config IR_RC6_DECODER
+	tristate "Enable IR raw decoder for the RC6 protocol"
+	depends on RC_CORE
+	select BITREVERSE
+	default y
+
+	---help---
+	   Enable this option if you have an infrared remote control which
+	   uses the RC6 protocol, and you need software decoding support.
+
+config IR_JVC_DECODER
+	tristate "Enable IR raw decoder for the JVC protocol"
+	depends on RC_CORE
+	select BITREVERSE
+	default y
+
+	---help---
+	   Enable this option if you have an infrared remote control which
+	   uses the JVC protocol, and you need software decoding support.
+
+config IR_SONY_DECODER
+	tristate "Enable IR raw decoder for the Sony protocol"
+	depends on RC_CORE
+	select BITREVERSE
+	default y
+
+	---help---
+	   Enable this option if you have an infrared remote control which
+	   uses the Sony protocol, and you need software decoding support.
+
+config IR_SANYO_DECODER
+	tristate "Enable IR raw decoder for the Sanyo protocol"
+	depends on RC_CORE
+	default y
+
+	---help---
+	   Enable this option if you have an infrared remote control which
+	   uses the Sanyo protocol (Sanyo, Aiwa, Chinon remotes),
+	   and you need software decoding support.
+
+config IR_SHARP_DECODER
+	tristate "Enable IR raw decoder for the Sharp protocol"
+	depends on RC_CORE
+	default y
+
+	---help---
+	   Enable this option if you have an infrared remote control which
+	   uses the Sharp protocol, and you need software decoding support.
+
+config IR_MCE_KBD_DECODER
+	tristate "Enable IR raw decoder for the MCE keyboard/mouse protocol"
+	depends on RC_CORE
+	select BITREVERSE
+	default y
+
+	---help---
+	   Enable this option if you have a Microsoft Remote Keyboard for
+	   Windows Media Center Edition, which you would like to use with
+	   a raw IR receiver in your system.
+
+config IR_XMP_DECODER
+	tristate "Enable IR raw decoder for the XMP protocol"
+	depends on RC_CORE
+	select BITREVERSE
+	default y
+
+	---help---
+	   Enable this option if you have IR with XMP protocol, and
+	   if the IR is decoded in software
+endif #RC_DECODERS
+
+menuconfig RC_DEVICES
+	bool "Remote Controller devices"
+	depends on RC_CORE
+
+if RC_DEVICES
+
+config RC_ATI_REMOTE
+	tristate "ATI / X10 based USB RF remote controls"
+	depends on USB_ARCH_HAS_HCD
+	depends on RC_CORE
+	select USB
+	help
+	   Say Y here if you want to use an X10 based USB remote control.
+	   These are RF remotes with USB receivers.
+
+	   Such devices include the ATI remote that comes with many of ATI's
+	   All-In-Wonder video cards, the X10 "Lola" remote, NVIDIA RF remote,
+	   Medion RF remote, and SnapStream FireFly remote.
+
+	   This driver provides mouse pointer, left and right mouse buttons,
+	   and maps all the other remote buttons to keypress events.
+
+	   To compile this driver as a module, choose M here: the module will be
+	   called ati_remote.
+
+config IR_ENE
+	tristate "ENE eHome Receiver/Transceiver (pnp id: ENE0100/ENE02xxx)"
+	depends on PNP
+	depends on RC_CORE
+	---help---
+	   Say Y here to enable support for integrated infrared receiver
+	   /transceiver made by ENE.
+
+	   You can see if you have it by looking at lspnp output.
+	   Output should include ENE0100 ENE0200 or something similar.
+
+	   To compile this driver as a module, choose M here: the
+	   module will be called ene_ir.
+
+config IR_HIX5HD2
+	tristate "Hisilicon hix5hd2 IR remote control"
+	depends on RC_CORE
+	help
+	 Say Y here if you want to use hisilicon hix5hd2 remote control.
+	 To compile this driver as a module, choose M here: the module will be
+	 called ir-hix5hd2.
+
+	 If you're not sure, select N here
+
+config IR_IMON
+	tristate "SoundGraph iMON Receiver and Display"
+	depends on USB_ARCH_HAS_HCD
+	depends on RC_CORE
+	select USB
+	---help---
+	   Say Y here if you want to use a SoundGraph iMON (aka Antec Veris)
+	   IR Receiver and/or LCD/VFD/VGA display.
+
+	   To compile this driver as a module, choose M here: the
+	   module will be called imon.
+
+config IR_MCEUSB
+	tristate "Windows Media Center Ed. eHome Infrared Transceiver"
+	depends on USB_ARCH_HAS_HCD
+	depends on RC_CORE
+	select USB
+	---help---
+	   Say Y here if you want to use a Windows Media Center Edition
+	   eHome Infrared Transceiver.
+
+	   To compile this driver as a module, choose M here: the
+	   module will be called mceusb.
+
+config IR_ITE_CIR
+	tristate "ITE Tech Inc. IT8712/IT8512 Consumer Infrared Transceiver"
+	depends on PNP
+	depends on RC_CORE
+	---help---
+	   Say Y here to enable support for integrated infrared receivers
+	   /transceivers made by ITE Tech Inc. These are found in
+	   several ASUS devices, like the ASUS Digimatrix or the ASUS
+	   EEEBox 1501U.
+
+	   To compile this driver as a module, choose M here: the
+	   module will be called ite-cir.
+
+config IR_FINTEK
+	tristate "Fintek Consumer Infrared Transceiver"
+	depends on PNP
+	depends on RC_CORE
+	---help---
+	   Say Y here to enable support for integrated infrared receiver
+	   /transciever made by Fintek. This chip is found on assorted
+	   Jetway motherboards (and of course, possibly others).
+
+	   To compile this driver as a module, choose M here: the
+	   module will be called fintek-cir.
+
+config IR_MESON
+	tristate "Amlogic Meson IR remote receiver"
+	depends on RC_CORE
+	depends on ARCH_MESON || COMPILE_TEST
+	---help---
+	   Say Y if you want to use the IR remote receiver available
+	   on Amlogic Meson SoCs.
+
+	   To compile this driver as a module, choose M here: the
+	   module will be called meson-ir.
+
+config IR_NUVOTON
+	tristate "Nuvoton w836x7hg Consumer Infrared Transceiver"
+	depends on PNP
+	depends on RC_CORE
+	---help---
+	   Say Y here to enable support for integrated infrared receiver
+	   /transciever made by Nuvoton (formerly Winbond). This chip is
+	   found in the ASRock ION 330HT, as well as assorted Intel
+	   DP55-series motherboards (and of course, possibly others).
+
+	   To compile this driver as a module, choose M here: the
+	   module will be called nuvoton-cir.
+
+config IR_REDRAT3
+	tristate "RedRat3 IR Transceiver"
+	depends on USB_ARCH_HAS_HCD
+	depends on RC_CORE
+	select NEW_LEDS
+	select LEDS_CLASS
+	select USB
+	---help---
+	   Say Y here if you want to use a RedRat3 Infrared Transceiver.
+
+	   To compile this driver as a module, choose M here: the
+	   module will be called redrat3.
+
+config IR_STREAMZAP
+	tristate "Streamzap PC Remote IR Receiver"
+	depends on USB_ARCH_HAS_HCD
+	depends on RC_CORE
+	select USB
+	---help---
+	   Say Y here if you want to use a Streamzap PC Remote
+	   Infrared Receiver.
+
+	   To compile this driver as a module, choose M here: the
+	   module will be called streamzap.
+
+config IR_WINBOND_CIR
+	tristate "Winbond IR remote control"
+	depends on X86 && PNP
+	depends on RC_CORE
+	select NEW_LEDS
+	select LEDS_CLASS
+	select BITREVERSE
+	---help---
+	   Say Y here if you want to use the IR remote functionality found
+	   in some Winbond SuperI/O chips. Currently only the WPCD376I
+	   chip is supported (included in some Intel Media series
+	   motherboards).
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called winbond_cir.
+
+config IR_IGORPLUGUSB
+	tristate "IgorPlug-USB IR Receiver"
+	depends on USB_ARCH_HAS_HCD
+	depends on RC_CORE
+	select USB
+	---help---
+	   Say Y here if you want to use the IgorPlug-USB IR Receiver by
+	   Igor Cesko. This device is included on the Fit-PC2.
+
+	   Note that this device can only record bursts of 36 IR pulses and
+	   spaces, which is not enough for the NEC, Sanyo and RC-6 protocol.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called igorplugusb.
+
+config IR_IGUANA
+	tristate "IguanaWorks USB IR Transceiver"
+	depends on USB_ARCH_HAS_HCD
+	depends on RC_CORE
+	select USB
+	---help---
+	   Say Y here if you want to use the IguanaWorks USB IR Transceiver.
+	   Both infrared receive and send are supported. If you want to
+	   change the ID or the pin config, use the user space driver from
+	   IguanaWorks.
+
+	   Only firmware 0x0205 and later is supported.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called iguanair.
+
+config IR_TTUSBIR
+	tristate "TechnoTrend USB IR Receiver"
+	depends on USB_ARCH_HAS_HCD
+	depends on RC_CORE
+	select USB
+	select NEW_LEDS
+	select LEDS_CLASS
+	---help---
+	   Say Y here if you want to use the TechnoTrend USB IR Receiver. The
+	   driver can control the led.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called ttusbir.
+
+config IR_RX51
+	tristate "Nokia N900 IR transmitter diode"
+	depends on OMAP_DM_TIMER && ARCH_OMAP2PLUS && LIRC && !ARCH_MULTIPLATFORM
+	---help---
+	   Say Y or M here if you want to enable support for the IR
+	   transmitter diode built in the Nokia N900 (RX51) device.
+
+	   The driver uses omap DM timers for generating the carrier
+	   wave and pulses.
+
+source "drivers/media/rc/img-ir/Kconfig"
+
+config RC_LOOPBACK
+	tristate "Remote Control Loopback Driver"
+	depends on RC_CORE
+	---help---
+	   Say Y here if you want support for the remote control loopback
+	   driver which allows TX data to be sent back as RX data.
+	   This is mostly useful for debugging purposes.
+
+	   If you're not sure, select N here.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called rc_loopback.
+
+config IR_GPIO_CIR
+	tristate "GPIO IR remote control"
+	depends on RC_CORE
+	---help---
+	   Say Y if you want to use GPIO based IR Receiver.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called gpio-ir-recv.
+
+config RC_ST
+	tristate "ST remote control receiver"
+	depends on RC_CORE
+	depends on ARCH_STI || COMPILE_TEST
+	---help---
+	   Say Y here if you want support for ST remote control driver
+	   which allows both IR and UHF RX.
+	   The driver passes raw pulse and space information to the LIRC decoder.
+
+	   If you're not sure, select N here.
+
+config IR_SUNXI
+	tristate "SUNXI IR remote control"
+	depends on RC_CORE
+	depends on ARCH_SUNXI || COMPILE_TEST
+	---help---
+	   Say Y if you want to use sunXi internal IR Controller
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called sunxi-ir.
+
+endif #RC_DEVICES
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
new file mode 100644
index 0000000..379a5c0
--- /dev/null
+++ b/drivers/media/rc/Makefile
@@ -0,0 +1,39 @@
+rc-core-objs	:= rc-main.o rc-ir-raw.o
+
+obj-y += keymaps/
+
+obj-$(CONFIG_RC_CORE) += rc-core.o
+obj-$(CONFIG_LIRC) += lirc_dev.o
+obj-$(CONFIG_IR_NEC_DECODER) += ir-nec-decoder.o
+obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o
+obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o
+obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o
+obj-$(CONFIG_IR_SONY_DECODER) += ir-sony-decoder.o
+obj-$(CONFIG_IR_SANYO_DECODER) += ir-sanyo-decoder.o
+obj-$(CONFIG_IR_SHARP_DECODER) += ir-sharp-decoder.o
+obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o
+obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o
+obj-$(CONFIG_IR_XMP_DECODER) += ir-xmp-decoder.o
+
+# stand-alone IR receivers/transmitters
+obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o
+obj-$(CONFIG_IR_HIX5HD2) += ir-hix5hd2.o
+obj-$(CONFIG_IR_IMON) += imon.o
+obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o
+obj-$(CONFIG_IR_MCEUSB) += mceusb.o
+obj-$(CONFIG_IR_FINTEK) += fintek-cir.o
+obj-$(CONFIG_IR_MESON) += meson-ir.o
+obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o
+obj-$(CONFIG_IR_ENE) += ene_ir.o
+obj-$(CONFIG_IR_REDRAT3) += redrat3.o
+obj-$(CONFIG_IR_RX51) += ir-rx51.o
+obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
+obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o
+obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o
+obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o
+obj-$(CONFIG_IR_IGORPLUGUSB) += igorplugusb.o
+obj-$(CONFIG_IR_IGUANA) += iguanair.o
+obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o
+obj-$(CONFIG_RC_ST) += st_rc.o
+obj-$(CONFIG_IR_SUNXI) += sunxi-cir.o
+obj-$(CONFIG_IR_IMG) += img-ir/
diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c
new file mode 100644
index 0000000..a356318
--- /dev/null
+++ b/drivers/media/rc/ati_remote.c
@@ -0,0 +1,982 @@
+/*
+ *  USB ATI Remote support
+ *
+ *                Copyright (c) 2011, 2012 Anssi Hannula <anssi.hannula@iki.fi>
+ *  Version 2.2.0 Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net>
+ *  Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev
+ *
+ *  This 2.2.0 version is a rewrite / cleanup of the 2.1.1 driver, including
+ *  porting to the 2.6 kernel interfaces, along with other modification
+ *  to better match the style of the existing usb/input drivers.  However, the
+ *  protocol and hardware handling is essentially unchanged from 2.1.1.
+ *
+ *  The 2.1.1 driver was derived from the usbati_remote and usbkbd drivers by
+ *  Vojtech Pavlik.
+ *
+ *  Changes:
+ *
+ *  Feb 2004: Torrey Hoffman <thoffman@arnor.net>
+ *            Version 2.2.0
+ *  Jun 2004: Torrey Hoffman <thoffman@arnor.net>
+ *            Version 2.2.1
+ *            Added key repeat support contributed by:
+ *                Vincent Vanackere <vanackere@lif.univ-mrs.fr>
+ *            Added support for the "Lola" remote contributed by:
+ *                Seth Cohn <sethcohn@yahoo.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
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ * Hardware & software notes
+ *
+ * These remote controls are distributed by ATI as part of their
+ * "All-In-Wonder" video card packages.  The receiver self-identifies as a
+ * "USB Receiver" with manufacturer "X10 Wireless Technology Inc".
+ *
+ * The "Lola" remote is available from X10.  See:
+ *    http://www.x10.com/products/lola_sg1.htm
+ * The Lola is similar to the ATI remote but has no mouse support, and slightly
+ * different keys.
+ *
+ * It is possible to use multiple receivers and remotes on multiple computers
+ * simultaneously by configuring them to use specific channels.
+ *
+ * The RF protocol used by the remote supports 16 distinct channels, 1 to 16.
+ * Actually, it may even support more, at least in some revisions of the
+ * hardware.
+ *
+ * Each remote can be configured to transmit on one channel as follows:
+ *   - Press and hold the "hand icon" button.
+ *   - When the red LED starts to blink, let go of the "hand icon" button.
+ *   - When it stops blinking, input the channel code as two digits, from 01
+ *     to 16, and press the hand icon again.
+ *
+ * The timing can be a little tricky.  Try loading the module with debug=1
+ * to have the kernel print out messages about the remote control number
+ * and mask.  Note: debugging prints remote numbers as zero-based hexadecimal.
+ *
+ * The driver has a "channel_mask" parameter. This bitmask specifies which
+ * channels will be ignored by the module.  To mask out channels, just add
+ * all the 2^channel_number values together.
+ *
+ * For instance, set channel_mask = 2^4 = 16 (binary 10000) to make ati_remote
+ * ignore signals coming from remote controls transmitting on channel 4, but
+ * accept all other channels.
+ *
+ * Or, set channel_mask = 65533, (0xFFFD), and all channels except 1 will be
+ * ignored.
+ *
+ * The default is 0 (respond to all channels). Bit 0 and bits 17-32 of this
+ * parameter are unused.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/usb/input.h>
+#include <linux/wait.h>
+#include <linux/jiffies.h>
+#include <media/rc-core.h>
+
+/*
+ * Module and Version Information, Module Parameters
+ */
+
+#define ATI_REMOTE_VENDOR_ID		0x0bc7
+#define LOLA_REMOTE_PRODUCT_ID		0x0002
+#define LOLA2_REMOTE_PRODUCT_ID		0x0003
+#define ATI_REMOTE_PRODUCT_ID		0x0004
+#define NVIDIA_REMOTE_PRODUCT_ID	0x0005
+#define MEDION_REMOTE_PRODUCT_ID	0x0006
+#define FIREFLY_REMOTE_PRODUCT_ID	0x0008
+
+#define DRIVER_VERSION		"2.2.1"
+#define DRIVER_AUTHOR           "Torrey Hoffman <thoffman@arnor.net>"
+#define DRIVER_DESC             "ATI/X10 RF USB Remote Control"
+
+#define NAME_BUFSIZE      80    /* size of product name, path buffers */
+#define DATA_BUFSIZE      63    /* size of URB data buffers */
+
+/*
+ * Duplicate event filtering time.
+ * Sequential, identical KIND_FILTERED inputs with less than
+ * FILTER_TIME milliseconds between them are considered as repeat
+ * events. The hardware generates 5 events for the first keypress
+ * and we have to take this into account for an accurate repeat
+ * behaviour.
+ */
+#define FILTER_TIME	60 /* msec */
+#define REPEAT_DELAY	500 /* msec */
+
+static unsigned long channel_mask;
+module_param(channel_mask, ulong, 0644);
+MODULE_PARM_DESC(channel_mask, "Bitmask of remote control channels to ignore");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
+
+static int repeat_filter = FILTER_TIME;
+module_param(repeat_filter, int, 0644);
+MODULE_PARM_DESC(repeat_filter, "Repeat filter time, default = 60 msec");
+
+static int repeat_delay = REPEAT_DELAY;
+module_param(repeat_delay, int, 0644);
+MODULE_PARM_DESC(repeat_delay, "Delay before sending repeats, default = 500 msec");
+
+static bool mouse = true;
+module_param(mouse, bool, 0444);
+MODULE_PARM_DESC(mouse, "Enable mouse device, default = yes");
+
+#define dbginfo(dev, format, arg...) \
+	do { if (debug) dev_info(dev , format , ## arg); } while (0)
+#undef err
+#define err(format, arg...) printk(KERN_ERR format , ## arg)
+
+struct ati_receiver_type {
+	/* either default_keymap or get_default_keymap should be set */
+	const char *default_keymap;
+	const char *(*get_default_keymap)(struct usb_interface *interface);
+};
+
+static const char *get_medion_keymap(struct usb_interface *interface)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+
+	/*
+	 * There are many different Medion remotes shipped with a receiver
+	 * with the same usb id, but the receivers have subtle differences
+	 * in the USB descriptors allowing us to detect them.
+	 */
+
+	if (udev->manufacturer && udev->product) {
+		if (udev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_WAKEUP) {
+
+			if (!strcmp(udev->manufacturer, "X10 Wireless Technology Inc")
+			    && !strcmp(udev->product, "USB Receiver"))
+				return RC_MAP_MEDION_X10_DIGITAINER;
+
+			if (!strcmp(udev->manufacturer, "X10 WTI")
+			    && !strcmp(udev->product, "RF receiver"))
+				return RC_MAP_MEDION_X10_OR2X;
+		} else {
+
+			 if (!strcmp(udev->manufacturer, "X10 Wireless Technology Inc")
+			    && !strcmp(udev->product, "USB Receiver"))
+				return RC_MAP_MEDION_X10;
+		}
+	}
+
+	dev_info(&interface->dev,
+		 "Unknown Medion X10 receiver, using default ati_remote Medion keymap\n");
+
+	return RC_MAP_MEDION_X10;
+}
+
+static const struct ati_receiver_type type_ati		= {
+	.default_keymap = RC_MAP_ATI_X10
+};
+static const struct ati_receiver_type type_medion	= {
+	.get_default_keymap = get_medion_keymap
+};
+static const struct ati_receiver_type type_firefly	= {
+	.default_keymap = RC_MAP_SNAPSTREAM_FIREFLY
+};
+
+static struct usb_device_id ati_remote_table[] = {
+	{
+		USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID),
+		.driver_info = (unsigned long)&type_ati
+	},
+	{
+		USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID),
+		.driver_info = (unsigned long)&type_ati
+	},
+	{
+		USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID),
+		.driver_info = (unsigned long)&type_ati
+	},
+	{
+		USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID),
+		.driver_info = (unsigned long)&type_ati
+	},
+	{
+		USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID),
+		.driver_info = (unsigned long)&type_medion
+	},
+	{
+		USB_DEVICE(ATI_REMOTE_VENDOR_ID, FIREFLY_REMOTE_PRODUCT_ID),
+		.driver_info = (unsigned long)&type_firefly
+	},
+	{}	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ati_remote_table);
+
+/* Get hi and low bytes of a 16-bits int */
+#define HI(a)	((unsigned char)((a) >> 8))
+#define LO(a)	((unsigned char)((a) & 0xff))
+
+#define SEND_FLAG_IN_PROGRESS	1
+#define SEND_FLAG_COMPLETE	2
+
+/* Device initialization strings */
+static char init1[] = { 0x01, 0x00, 0x20, 0x14 };
+static char init2[] = { 0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20 };
+
+struct ati_remote {
+	struct input_dev *idev;
+	struct rc_dev *rdev;
+	struct usb_device *udev;
+	struct usb_interface *interface;
+
+	struct urb *irq_urb;
+	struct urb *out_urb;
+	struct usb_endpoint_descriptor *endpoint_in;
+	struct usb_endpoint_descriptor *endpoint_out;
+	unsigned char *inbuf;
+	unsigned char *outbuf;
+	dma_addr_t inbuf_dma;
+	dma_addr_t outbuf_dma;
+
+	unsigned char old_data;     /* Detect duplicate events */
+	unsigned long old_jiffies;
+	unsigned long acc_jiffies;  /* handle acceleration */
+	unsigned long first_jiffies;
+
+	unsigned int repeat_count;
+
+	char rc_name[NAME_BUFSIZE];
+	char rc_phys[NAME_BUFSIZE];
+	char mouse_name[NAME_BUFSIZE];
+	char mouse_phys[NAME_BUFSIZE];
+
+	wait_queue_head_t wait;
+	int send_flags;
+
+	int users; /* 0-2, users are rc and input */
+	struct mutex open_mutex;
+};
+
+/* "Kinds" of messages sent from the hardware to the driver. */
+#define KIND_END        0
+#define KIND_LITERAL    1   /* Simply pass to input system as EV_KEY */
+#define KIND_FILTERED   2   /* Add artificial key-up events, drop keyrepeats */
+#define KIND_ACCEL      3   /* Translate to EV_REL mouse-move events */
+
+/* Translation table from hardware messages to input events. */
+static const struct {
+	unsigned char kind;
+	unsigned char data;	/* Raw key code from remote */
+	unsigned short code;	/* Input layer translation */
+}  ati_remote_tbl[] = {
+	/* Directional control pad axes.  Code is xxyy */
+	{KIND_ACCEL,    0x70, 0xff00},	/* left */
+	{KIND_ACCEL,    0x71, 0x0100},	/* right */
+	{KIND_ACCEL,    0x72, 0x00ff},	/* up */
+	{KIND_ACCEL,    0x73, 0x0001},	/* down */
+
+	/* Directional control pad diagonals */
+	{KIND_ACCEL,    0x74, 0xffff},	/* left up */
+	{KIND_ACCEL,    0x75, 0x01ff},	/* right up */
+	{KIND_ACCEL,    0x77, 0xff01},	/* left down */
+	{KIND_ACCEL,    0x76, 0x0101},	/* right down */
+
+	/* "Mouse button" buttons.  The code below uses the fact that the
+	 * lsbit of the raw code is a down/up indicator. */
+	{KIND_LITERAL,  0x78, BTN_LEFT}, /* left btn down */
+	{KIND_LITERAL,  0x79, BTN_LEFT}, /* left btn up */
+	{KIND_LITERAL,  0x7c, BTN_RIGHT},/* right btn down */
+	{KIND_LITERAL,  0x7d, BTN_RIGHT},/* right btn up */
+
+	/* Artificial "doubleclick" events are generated by the hardware.
+	 * They are mapped to the "side" and "extra" mouse buttons here. */
+	{KIND_FILTERED, 0x7a, BTN_SIDE}, /* left dblclick */
+	{KIND_FILTERED, 0x7e, BTN_EXTRA},/* right dblclick */
+
+	/* Non-mouse events are handled by rc-core */
+	{KIND_END, 0x00, 0}
+};
+
+/*
+ * ati_remote_dump_input
+ */
+static void ati_remote_dump(struct device *dev, unsigned char *data,
+			    unsigned int len)
+{
+	if (len == 1) {
+		if (data[0] != (unsigned char)0xff && data[0] != 0x00)
+			dev_warn(dev, "Weird byte 0x%02x\n", data[0]);
+	} else if (len == 4)
+		dev_warn(dev, "Weird key %*ph\n", 4, data);
+	else
+		dev_warn(dev, "Weird data, len=%d %*ph ...\n", len, 6, data);
+}
+
+/*
+ * ati_remote_open
+ */
+static int ati_remote_open(struct ati_remote *ati_remote)
+{
+	int err = 0;
+
+	mutex_lock(&ati_remote->open_mutex);
+
+	if (ati_remote->users++ != 0)
+		goto out; /* one was already active */
+
+	/* On first open, submit the read urb which was set up previously. */
+	ati_remote->irq_urb->dev = ati_remote->udev;
+	if (usb_submit_urb(ati_remote->irq_urb, GFP_KERNEL)) {
+		dev_err(&ati_remote->interface->dev,
+			"%s: usb_submit_urb failed!\n", __func__);
+		err = -EIO;
+	}
+
+out:	mutex_unlock(&ati_remote->open_mutex);
+	return err;
+}
+
+/*
+ * ati_remote_close
+ */
+static void ati_remote_close(struct ati_remote *ati_remote)
+{
+	mutex_lock(&ati_remote->open_mutex);
+	if (--ati_remote->users == 0)
+		usb_kill_urb(ati_remote->irq_urb);
+	mutex_unlock(&ati_remote->open_mutex);
+}
+
+static int ati_remote_input_open(struct input_dev *inputdev)
+{
+	struct ati_remote *ati_remote = input_get_drvdata(inputdev);
+	return ati_remote_open(ati_remote);
+}
+
+static void ati_remote_input_close(struct input_dev *inputdev)
+{
+	struct ati_remote *ati_remote = input_get_drvdata(inputdev);
+	ati_remote_close(ati_remote);
+}
+
+static int ati_remote_rc_open(struct rc_dev *rdev)
+{
+	struct ati_remote *ati_remote = rdev->priv;
+	return ati_remote_open(ati_remote);
+}
+
+static void ati_remote_rc_close(struct rc_dev *rdev)
+{
+	struct ati_remote *ati_remote = rdev->priv;
+	ati_remote_close(ati_remote);
+}
+
+/*
+ * ati_remote_irq_out
+ */
+static void ati_remote_irq_out(struct urb *urb)
+{
+	struct ati_remote *ati_remote = urb->context;
+
+	if (urb->status) {
+		dev_dbg(&ati_remote->interface->dev, "%s: status %d\n",
+			__func__, urb->status);
+		return;
+	}
+
+	ati_remote->send_flags |= SEND_FLAG_COMPLETE;
+	wmb();
+	wake_up(&ati_remote->wait);
+}
+
+/*
+ * ati_remote_sendpacket
+ *
+ * Used to send device initialization strings
+ */
+static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd,
+	unsigned char *data)
+{
+	int retval = 0;
+
+	/* Set up out_urb */
+	memcpy(ati_remote->out_urb->transfer_buffer + 1, data, LO(cmd));
+	((char *) ati_remote->out_urb->transfer_buffer)[0] = HI(cmd);
+
+	ati_remote->out_urb->transfer_buffer_length = LO(cmd) + 1;
+	ati_remote->out_urb->dev = ati_remote->udev;
+	ati_remote->send_flags = SEND_FLAG_IN_PROGRESS;
+
+	retval = usb_submit_urb(ati_remote->out_urb, GFP_ATOMIC);
+	if (retval) {
+		dev_dbg(&ati_remote->interface->dev,
+			 "sendpacket: usb_submit_urb failed: %d\n", retval);
+		return retval;
+	}
+
+	wait_event_timeout(ati_remote->wait,
+		((ati_remote->out_urb->status != -EINPROGRESS) ||
+			(ati_remote->send_flags & SEND_FLAG_COMPLETE)),
+		HZ);
+	usb_kill_urb(ati_remote->out_urb);
+
+	return retval;
+}
+
+/*
+ * ati_remote_compute_accel
+ *
+ * Implements acceleration curve for directional control pad
+ * If elapsed time since last event is > 1/4 second, user "stopped",
+ * so reset acceleration. Otherwise, user is probably holding the control
+ * pad down, so we increase acceleration, ramping up over two seconds to
+ * a maximum speed.
+ */
+static int ati_remote_compute_accel(struct ati_remote *ati_remote)
+{
+	static const char accel[] = { 1, 2, 4, 6, 9, 13, 20 };
+	unsigned long now = jiffies;
+	int acc;
+
+	if (time_after(now, ati_remote->old_jiffies + msecs_to_jiffies(250))) {
+		acc = 1;
+		ati_remote->acc_jiffies = now;
+	}
+	else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(125)))
+		acc = accel[0];
+	else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(250)))
+		acc = accel[1];
+	else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(500)))
+		acc = accel[2];
+	else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1000)))
+		acc = accel[3];
+	else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1500)))
+		acc = accel[4];
+	else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(2000)))
+		acc = accel[5];
+	else
+		acc = accel[6];
+
+	return acc;
+}
+
+/*
+ * ati_remote_report_input
+ */
+static void ati_remote_input_report(struct urb *urb)
+{
+	struct ati_remote *ati_remote = urb->context;
+	unsigned char *data= ati_remote->inbuf;
+	struct input_dev *dev = ati_remote->idev;
+	int index = -1;
+	int remote_num;
+	unsigned char scancode;
+	u32 wheel_keycode = KEY_RESERVED;
+	int i;
+
+	/*
+	 * data[0] = 0x14
+	 * data[1] = data[2] + data[3] + 0xd5 (a checksum byte)
+	 * data[2] = the key code (with toggle bit in MSB with some models)
+	 * data[3] = channel << 4 (the low 4 bits must be zero)
+	 */
+
+	/* Deal with strange looking inputs */
+	if ( urb->actual_length != 4 || data[0] != 0x14 ||
+	     data[1] != (unsigned char)(data[2] + data[3] + 0xD5) ||
+	     (data[3] & 0x0f) != 0x00) {
+		ati_remote_dump(&urb->dev->dev, data, urb->actual_length);
+		return;
+	}
+
+	if (data[1] != ((data[2] + data[3] + 0xd5) & 0xff)) {
+		dbginfo(&ati_remote->interface->dev,
+			"wrong checksum in input: %*ph\n", 4, data);
+		return;
+	}
+
+	/* Mask unwanted remote channels.  */
+	/* note: remote_num is 0-based, channel 1 on remote == 0 here */
+	remote_num = (data[3] >> 4) & 0x0f;
+	if (channel_mask & (1 << (remote_num + 1))) {
+		dbginfo(&ati_remote->interface->dev,
+			"Masked input from channel 0x%02x: data %02x, "
+			"mask= 0x%02lx\n",
+			remote_num, data[2], channel_mask);
+		return;
+	}
+
+	/*
+	 * MSB is a toggle code, though only used by some devices
+	 * (e.g. SnapStream Firefly)
+	 */
+	scancode = data[2] & 0x7f;
+
+	dbginfo(&ati_remote->interface->dev,
+		"channel 0x%02x; key data %02x, scancode %02x\n",
+		remote_num, data[2], scancode);
+
+	if (scancode >= 0x70) {
+		/*
+		 * This is either a mouse or scrollwheel event, depending on
+		 * the remote/keymap.
+		 * Get the keycode assigned to scancode 0x78/0x70. If it is
+		 * set, assume this is a scrollwheel up/down event.
+		 */
+		wheel_keycode = rc_g_keycode_from_table(ati_remote->rdev,
+							scancode & 0x78);
+
+		if (wheel_keycode == KEY_RESERVED) {
+			/* scrollwheel was not mapped, assume mouse */
+
+			/* Look up event code index in the mouse translation
+			 * table.
+			 */
+			for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) {
+				if (scancode == ati_remote_tbl[i].data) {
+					index = i;
+					break;
+				}
+			}
+		}
+	}
+
+	if (index >= 0 && ati_remote_tbl[index].kind == KIND_LITERAL) {
+		/*
+		 * The lsbit of the raw key code is a down/up flag.
+		 * Invert it to match the input layer's conventions.
+		 */
+		input_event(dev, EV_KEY, ati_remote_tbl[index].code,
+			!(data[2] & 1));
+
+		ati_remote->old_jiffies = jiffies;
+
+	} else if (index < 0 || ati_remote_tbl[index].kind == KIND_FILTERED) {
+		unsigned long now = jiffies;
+
+		/* Filter duplicate events which happen "too close" together. */
+		if (ati_remote->old_data == data[2] &&
+		    time_before(now, ati_remote->old_jiffies +
+				     msecs_to_jiffies(repeat_filter))) {
+			ati_remote->repeat_count++;
+		} else {
+			ati_remote->repeat_count = 0;
+			ati_remote->first_jiffies = now;
+		}
+
+		ati_remote->old_jiffies = now;
+
+		/* Ensure we skip at least the 4 first duplicate events
+		 * (generated by a single keypress), and continue skipping
+		 * until repeat_delay msecs have passed.
+		 */
+		if (ati_remote->repeat_count > 0 &&
+		    (ati_remote->repeat_count < 5 ||
+		     time_before(now, ati_remote->first_jiffies +
+				      msecs_to_jiffies(repeat_delay))))
+			return;
+
+		if (index >= 0) {
+			input_event(dev, EV_KEY, ati_remote_tbl[index].code, 1);
+			input_event(dev, EV_KEY, ati_remote_tbl[index].code, 0);
+		} else {
+			/* Not a mouse event, hand it to rc-core. */
+			int count = 1;
+
+			if (wheel_keycode != KEY_RESERVED) {
+				/*
+				 * This is a scrollwheel event, send the
+				 * scroll up (0x78) / down (0x70) scancode
+				 * repeatedly as many times as indicated by
+				 * rest of the scancode.
+				 */
+				count = (scancode & 0x07) + 1;
+				scancode &= 0x78;
+			}
+
+			while (count--) {
+				/*
+				* We don't use the rc-core repeat handling yet as
+				* it would cause ghost repeats which would be a
+				* regression for this driver.
+				*/
+				rc_keydown_notimeout(ati_remote->rdev, RC_TYPE_OTHER,
+						     scancode, data[2]);
+				rc_keyup(ati_remote->rdev);
+			}
+			goto nosync;
+		}
+
+	} else if (ati_remote_tbl[index].kind == KIND_ACCEL) {
+		signed char dx = ati_remote_tbl[index].code >> 8;
+		signed char dy = ati_remote_tbl[index].code & 255;
+
+		/*
+		 * Other event kinds are from the directional control pad, and
+		 * have an acceleration factor applied to them.  Without this
+		 * acceleration, the control pad is mostly unusable.
+		 */
+		int acc = ati_remote_compute_accel(ati_remote);
+		if (dx)
+			input_report_rel(dev, REL_X, dx * acc);
+		if (dy)
+			input_report_rel(dev, REL_Y, dy * acc);
+		ati_remote->old_jiffies = jiffies;
+
+	} else {
+		dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n",
+			ati_remote_tbl[index].kind);
+		return;
+	}
+	input_sync(dev);
+nosync:
+	ati_remote->old_data = data[2];
+}
+
+/*
+ * ati_remote_irq_in
+ */
+static void ati_remote_irq_in(struct urb *urb)
+{
+	struct ati_remote *ati_remote = urb->context;
+	int retval;
+
+	switch (urb->status) {
+	case 0:			/* success */
+		ati_remote_input_report(urb);
+		break;
+	case -ECONNRESET:	/* unlink */
+	case -ENOENT:
+	case -ESHUTDOWN:
+		dev_dbg(&ati_remote->interface->dev,
+			"%s: urb error status, unlink?\n",
+			__func__);
+		return;
+	default:		/* error */
+		dev_dbg(&ati_remote->interface->dev,
+			"%s: Nonzero urb status %d\n",
+			__func__, urb->status);
+	}
+
+	retval = usb_submit_urb(urb, GFP_ATOMIC);
+	if (retval)
+		dev_err(&ati_remote->interface->dev,
+			"%s: usb_submit_urb()=%d\n",
+			__func__, retval);
+}
+
+/*
+ * ati_remote_alloc_buffers
+ */
+static int ati_remote_alloc_buffers(struct usb_device *udev,
+				    struct ati_remote *ati_remote)
+{
+	ati_remote->inbuf = usb_alloc_coherent(udev, DATA_BUFSIZE, GFP_ATOMIC,
+					       &ati_remote->inbuf_dma);
+	if (!ati_remote->inbuf)
+		return -1;
+
+	ati_remote->outbuf = usb_alloc_coherent(udev, DATA_BUFSIZE, GFP_ATOMIC,
+						&ati_remote->outbuf_dma);
+	if (!ati_remote->outbuf)
+		return -1;
+
+	ati_remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!ati_remote->irq_urb)
+		return -1;
+
+	ati_remote->out_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!ati_remote->out_urb)
+		return -1;
+
+	return 0;
+}
+
+/*
+ * ati_remote_free_buffers
+ */
+static void ati_remote_free_buffers(struct ati_remote *ati_remote)
+{
+	usb_free_urb(ati_remote->irq_urb);
+	usb_free_urb(ati_remote->out_urb);
+
+	usb_free_coherent(ati_remote->udev, DATA_BUFSIZE,
+		ati_remote->inbuf, ati_remote->inbuf_dma);
+
+	usb_free_coherent(ati_remote->udev, DATA_BUFSIZE,
+		ati_remote->outbuf, ati_remote->outbuf_dma);
+}
+
+static void ati_remote_input_init(struct ati_remote *ati_remote)
+{
+	struct input_dev *idev = ati_remote->idev;
+	int i;
+
+	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+	idev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
+		BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA);
+	idev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+	for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++)
+		if (ati_remote_tbl[i].kind == KIND_LITERAL ||
+		    ati_remote_tbl[i].kind == KIND_FILTERED)
+			__set_bit(ati_remote_tbl[i].code, idev->keybit);
+
+	input_set_drvdata(idev, ati_remote);
+
+	idev->open = ati_remote_input_open;
+	idev->close = ati_remote_input_close;
+
+	idev->name = ati_remote->mouse_name;
+	idev->phys = ati_remote->mouse_phys;
+
+	usb_to_input_id(ati_remote->udev, &idev->id);
+	idev->dev.parent = &ati_remote->interface->dev;
+}
+
+static void ati_remote_rc_init(struct ati_remote *ati_remote)
+{
+	struct rc_dev *rdev = ati_remote->rdev;
+
+	rdev->priv = ati_remote;
+	rdev->driver_type = RC_DRIVER_SCANCODE;
+	rdev->allowed_protocols = RC_BIT_OTHER;
+	rdev->driver_name = "ati_remote";
+
+	rdev->open = ati_remote_rc_open;
+	rdev->close = ati_remote_rc_close;
+
+	rdev->input_name = ati_remote->rc_name;
+	rdev->input_phys = ati_remote->rc_phys;
+
+	usb_to_input_id(ati_remote->udev, &rdev->input_id);
+	rdev->dev.parent = &ati_remote->interface->dev;
+}
+
+static int ati_remote_initialize(struct ati_remote *ati_remote)
+{
+	struct usb_device *udev = ati_remote->udev;
+	int pipe, maxp;
+
+	init_waitqueue_head(&ati_remote->wait);
+
+	/* Set up irq_urb */
+	pipe = usb_rcvintpipe(udev, ati_remote->endpoint_in->bEndpointAddress);
+	maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+	maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
+
+	usb_fill_int_urb(ati_remote->irq_urb, udev, pipe, ati_remote->inbuf,
+			 maxp, ati_remote_irq_in, ati_remote,
+			 ati_remote->endpoint_in->bInterval);
+	ati_remote->irq_urb->transfer_dma = ati_remote->inbuf_dma;
+	ati_remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	/* Set up out_urb */
+	pipe = usb_sndintpipe(udev, ati_remote->endpoint_out->bEndpointAddress);
+	maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+	maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
+
+	usb_fill_int_urb(ati_remote->out_urb, udev, pipe, ati_remote->outbuf,
+			 maxp, ati_remote_irq_out, ati_remote,
+			 ati_remote->endpoint_out->bInterval);
+	ati_remote->out_urb->transfer_dma = ati_remote->outbuf_dma;
+	ati_remote->out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	/* send initialization strings */
+	if ((ati_remote_sendpacket(ati_remote, 0x8004, init1)) ||
+	    (ati_remote_sendpacket(ati_remote, 0x8007, init2))) {
+		dev_err(&ati_remote->interface->dev,
+			 "Initializing ati_remote hardware failed.\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/*
+ * ati_remote_probe
+ */
+static int ati_remote_probe(struct usb_interface *interface,
+	const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(interface);
+	struct usb_host_interface *iface_host = interface->cur_altsetting;
+	struct usb_endpoint_descriptor *endpoint_in, *endpoint_out;
+	struct ati_receiver_type *type = (struct ati_receiver_type *)id->driver_info;
+	struct ati_remote *ati_remote;
+	struct input_dev *input_dev;
+	struct rc_dev *rc_dev;
+	int err = -ENOMEM;
+
+	if (iface_host->desc.bNumEndpoints != 2) {
+		err("%s: Unexpected desc.bNumEndpoints\n", __func__);
+		return -ENODEV;
+	}
+
+	endpoint_in = &iface_host->endpoint[0].desc;
+	endpoint_out = &iface_host->endpoint[1].desc;
+
+	if (!usb_endpoint_is_int_in(endpoint_in)) {
+		err("%s: Unexpected endpoint_in\n", __func__);
+		return -ENODEV;
+	}
+	if (le16_to_cpu(endpoint_in->wMaxPacketSize) == 0) {
+		err("%s: endpoint_in message size==0? \n", __func__);
+		return -ENODEV;
+	}
+
+	ati_remote = kzalloc(sizeof (struct ati_remote), GFP_KERNEL);
+	rc_dev = rc_allocate_device();
+	if (!ati_remote || !rc_dev)
+		goto exit_free_dev_rdev;
+
+	/* Allocate URB buffers, URBs */
+	if (ati_remote_alloc_buffers(udev, ati_remote))
+		goto exit_free_buffers;
+
+	ati_remote->endpoint_in = endpoint_in;
+	ati_remote->endpoint_out = endpoint_out;
+	ati_remote->udev = udev;
+	ati_remote->rdev = rc_dev;
+	ati_remote->interface = interface;
+
+	usb_make_path(udev, ati_remote->rc_phys, sizeof(ati_remote->rc_phys));
+	strlcpy(ati_remote->mouse_phys, ati_remote->rc_phys,
+		sizeof(ati_remote->mouse_phys));
+
+	strlcat(ati_remote->rc_phys, "/input0", sizeof(ati_remote->rc_phys));
+	strlcat(ati_remote->mouse_phys, "/input1", sizeof(ati_remote->mouse_phys));
+
+	if (udev->manufacturer)
+		strlcpy(ati_remote->rc_name, udev->manufacturer,
+			sizeof(ati_remote->rc_name));
+
+	if (udev->product)
+		snprintf(ati_remote->rc_name, sizeof(ati_remote->rc_name),
+			 "%s %s", ati_remote->rc_name, udev->product);
+
+	if (!strlen(ati_remote->rc_name))
+		snprintf(ati_remote->rc_name, sizeof(ati_remote->rc_name),
+			DRIVER_DESC "(%04x,%04x)",
+			le16_to_cpu(ati_remote->udev->descriptor.idVendor),
+			le16_to_cpu(ati_remote->udev->descriptor.idProduct));
+
+	snprintf(ati_remote->mouse_name, sizeof(ati_remote->mouse_name),
+		 "%s mouse", ati_remote->rc_name);
+
+	rc_dev->map_name = RC_MAP_ATI_X10; /* default map */
+
+	/* set default keymap according to receiver model */
+	if (type) {
+		if (type->default_keymap)
+			rc_dev->map_name = type->default_keymap;
+		else if (type->get_default_keymap)
+			rc_dev->map_name = type->get_default_keymap(interface);
+	}
+
+	ati_remote_rc_init(ati_remote);
+	mutex_init(&ati_remote->open_mutex);
+
+	/* Device Hardware Initialization - fills in ati_remote->idev from udev. */
+	err = ati_remote_initialize(ati_remote);
+	if (err)
+		goto exit_kill_urbs;
+
+	/* Set up and register rc device */
+	err = rc_register_device(ati_remote->rdev);
+	if (err)
+		goto exit_kill_urbs;
+
+	/* use our delay for rc_dev */
+	ati_remote->rdev->input_dev->rep[REP_DELAY] = repeat_delay;
+
+	/* Set up and register mouse input device */
+	if (mouse) {
+		input_dev = input_allocate_device();
+		if (!input_dev) {
+			err = -ENOMEM;
+			goto exit_unregister_device;
+		}
+
+		ati_remote->idev = input_dev;
+		ati_remote_input_init(ati_remote);
+		err = input_register_device(input_dev);
+
+		if (err)
+			goto exit_free_input_device;
+	}
+
+	usb_set_intfdata(interface, ati_remote);
+	return 0;
+
+ exit_free_input_device:
+	input_free_device(input_dev);
+ exit_unregister_device:
+	rc_unregister_device(rc_dev);
+	rc_dev = NULL;
+ exit_kill_urbs:
+	usb_kill_urb(ati_remote->irq_urb);
+	usb_kill_urb(ati_remote->out_urb);
+ exit_free_buffers:
+	ati_remote_free_buffers(ati_remote);
+ exit_free_dev_rdev:
+	 rc_free_device(rc_dev);
+	kfree(ati_remote);
+	return err;
+}
+
+/*
+ * ati_remote_disconnect
+ */
+static void ati_remote_disconnect(struct usb_interface *interface)
+{
+	struct ati_remote *ati_remote;
+
+	ati_remote = usb_get_intfdata(interface);
+	usb_set_intfdata(interface, NULL);
+	if (!ati_remote) {
+		dev_warn(&interface->dev, "%s - null device?\n", __func__);
+		return;
+	}
+
+	usb_kill_urb(ati_remote->irq_urb);
+	usb_kill_urb(ati_remote->out_urb);
+	if (ati_remote->idev)
+		input_unregister_device(ati_remote->idev);
+	rc_unregister_device(ati_remote->rdev);
+	ati_remote_free_buffers(ati_remote);
+	kfree(ati_remote);
+}
+
+/* usb specific object to register with the usb subsystem */
+static struct usb_driver ati_remote_driver = {
+	.name         = "ati_remote",
+	.probe        = ati_remote_probe,
+	.disconnect   = ati_remote_disconnect,
+	.id_table     = ati_remote_table,
+};
+
+module_usb_driver(ati_remote_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
new file mode 100644
index 0000000..8d77e1c
--- /dev/null
+++ b/drivers/media/rc/ene_ir.c
@@ -0,0 +1,1219 @@
+/*
+ * driver for ENE KB3926 B/C/D/E/F CIR (pnp id: ENE0XXX)
+ *
+ * Copyright (C) 2010 Maxim Levitsky <maximlevitsky@gmail.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
+ *
+ * Special thanks to:
+ *   Sami R. <maesesami@gmail.com> for lot of help in debugging and therefore
+ *    bringing to life support for transmission & learning mode.
+ *
+ *   Charlie Andrews <charliethepilot@googlemail.com> for lots of help in
+ *   bringing up the support of new firmware buffer that is popular
+ *   on latest notebooks
+ *
+ *   ENE for partial device documentation
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pnp.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <media/rc-core.h>
+#include "ene_ir.h"
+
+static int sample_period;
+static bool learning_mode_force;
+static int debug;
+static bool txsim;
+
+static void ene_set_reg_addr(struct ene_device *dev, u16 reg)
+{
+	outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
+	outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
+}
+
+/* read a hardware register */
+static u8 ene_read_reg(struct ene_device *dev, u16 reg)
+{
+	u8 retval;
+	ene_set_reg_addr(dev, reg);
+	retval = inb(dev->hw_io + ENE_IO);
+	dbg_regs("reg %04x == %02x", reg, retval);
+	return retval;
+}
+
+/* write a hardware register */
+static void ene_write_reg(struct ene_device *dev, u16 reg, u8 value)
+{
+	dbg_regs("reg %04x <- %02x", reg, value);
+	ene_set_reg_addr(dev, reg);
+	outb(value, dev->hw_io + ENE_IO);
+}
+
+/* Set bits in hardware register */
+static void ene_set_reg_mask(struct ene_device *dev, u16 reg, u8 mask)
+{
+	dbg_regs("reg %04x |= %02x", reg, mask);
+	ene_set_reg_addr(dev, reg);
+	outb(inb(dev->hw_io + ENE_IO) | mask, dev->hw_io + ENE_IO);
+}
+
+/* Clear bits in hardware register */
+static void ene_clear_reg_mask(struct ene_device *dev, u16 reg, u8 mask)
+{
+	dbg_regs("reg %04x &= ~%02x ", reg, mask);
+	ene_set_reg_addr(dev, reg);
+	outb(inb(dev->hw_io + ENE_IO) & ~mask, dev->hw_io + ENE_IO);
+}
+
+/* A helper to set/clear a bit in register according to boolean variable */
+static void ene_set_clear_reg_mask(struct ene_device *dev, u16 reg, u8 mask,
+								bool set)
+{
+	if (set)
+		ene_set_reg_mask(dev, reg, mask);
+	else
+		ene_clear_reg_mask(dev, reg, mask);
+}
+
+/* detect hardware features */
+static int ene_hw_detect(struct ene_device *dev)
+{
+	u8 chip_major, chip_minor;
+	u8 hw_revision, old_ver;
+	u8 fw_reg2, fw_reg1;
+
+	ene_clear_reg_mask(dev, ENE_ECSTS, ENE_ECSTS_RSRVD);
+	chip_major = ene_read_reg(dev, ENE_ECVER_MAJOR);
+	chip_minor = ene_read_reg(dev, ENE_ECVER_MINOR);
+	ene_set_reg_mask(dev, ENE_ECSTS, ENE_ECSTS_RSRVD);
+
+	hw_revision = ene_read_reg(dev, ENE_ECHV);
+	old_ver = ene_read_reg(dev, ENE_HW_VER_OLD);
+
+	dev->pll_freq = (ene_read_reg(dev, ENE_PLLFRH) << 4) +
+		(ene_read_reg(dev, ENE_PLLFRL) >> 4);
+
+	if (sample_period != ENE_DEFAULT_SAMPLE_PERIOD)
+		dev->rx_period_adjust =
+			dev->pll_freq == ENE_DEFAULT_PLL_FREQ ? 2 : 4;
+
+	if (hw_revision == 0xFF) {
+		pr_warn("device seems to be disabled\n");
+		pr_warn("send a mail to lirc-list@lists.sourceforge.net\n");
+		pr_warn("please attach output of acpidump and dmidecode\n");
+		return -ENODEV;
+	}
+
+	pr_notice("chip is 0x%02x%02x - kbver = 0x%02x, rev = 0x%02x\n",
+		  chip_major, chip_minor, old_ver, hw_revision);
+
+	pr_notice("PLL freq = %d\n", dev->pll_freq);
+
+	if (chip_major == 0x33) {
+		pr_warn("chips 0x33xx aren't supported\n");
+		return -ENODEV;
+	}
+
+	if (chip_major == 0x39 && chip_minor == 0x26 && hw_revision == 0xC0) {
+		dev->hw_revision = ENE_HW_C;
+		pr_notice("KB3926C detected\n");
+	} else if (old_ver == 0x24 && hw_revision == 0xC0) {
+		dev->hw_revision = ENE_HW_B;
+		pr_notice("KB3926B detected\n");
+	} else {
+		dev->hw_revision = ENE_HW_D;
+		pr_notice("KB3926D or higher detected\n");
+	}
+
+	/* detect features hardware supports */
+	if (dev->hw_revision < ENE_HW_C)
+		return 0;
+
+	fw_reg1 = ene_read_reg(dev, ENE_FW1);
+	fw_reg2 = ene_read_reg(dev, ENE_FW2);
+
+	pr_notice("Firmware regs: %02x %02x\n", fw_reg1, fw_reg2);
+
+	dev->hw_use_gpio_0a = !!(fw_reg2 & ENE_FW2_GP0A);
+	dev->hw_learning_and_tx_capable = !!(fw_reg2 & ENE_FW2_LEARNING);
+	dev->hw_extra_buffer = !!(fw_reg1 & ENE_FW1_HAS_EXTRA_BUF);
+
+	if (dev->hw_learning_and_tx_capable)
+		dev->hw_fan_input = !!(fw_reg2 & ENE_FW2_FAN_INPUT);
+
+	pr_notice("Hardware features:\n");
+
+	if (dev->hw_learning_and_tx_capable) {
+		pr_notice("* Supports transmitting & learning mode\n");
+		pr_notice("   This feature is rare and therefore,\n");
+		pr_notice("   you are welcome to test it,\n");
+		pr_notice("   and/or contact the author via:\n");
+		pr_notice("   lirc-list@lists.sourceforge.net\n");
+		pr_notice("   or maximlevitsky@gmail.com\n");
+
+		pr_notice("* Uses GPIO %s for IR raw input\n",
+			  dev->hw_use_gpio_0a ? "40" : "0A");
+
+		if (dev->hw_fan_input)
+			pr_notice("* Uses unused fan feedback input as source of demodulated IR data\n");
+	}
+
+	if (!dev->hw_fan_input)
+		pr_notice("* Uses GPIO %s for IR demodulated input\n",
+			  dev->hw_use_gpio_0a ? "0A" : "40");
+
+	if (dev->hw_extra_buffer)
+		pr_notice("* Uses new style input buffer\n");
+	return 0;
+}
+
+/* Read properities of hw sample buffer */
+static void ene_rx_setup_hw_buffer(struct ene_device *dev)
+{
+	u16 tmp;
+
+	ene_rx_read_hw_pointer(dev);
+	dev->r_pointer = dev->w_pointer;
+
+	if (!dev->hw_extra_buffer) {
+		dev->buffer_len = ENE_FW_PACKET_SIZE * 2;
+		return;
+	}
+
+	tmp = ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER);
+	tmp |= ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER+1) << 8;
+	dev->extra_buf1_address = tmp;
+
+	dev->extra_buf1_len = ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER + 2);
+
+	tmp = ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER + 3);
+	tmp |= ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER + 4) << 8;
+	dev->extra_buf2_address = tmp;
+
+	dev->extra_buf2_len = ene_read_reg(dev, ENE_FW_SAMPLE_BUFFER + 5);
+
+	dev->buffer_len = dev->extra_buf1_len + dev->extra_buf2_len + 8;
+
+	pr_notice("Hardware uses 2 extended buffers:\n");
+	pr_notice("  0x%04x - len : %d\n",
+		  dev->extra_buf1_address, dev->extra_buf1_len);
+	pr_notice("  0x%04x - len : %d\n",
+		  dev->extra_buf2_address, dev->extra_buf2_len);
+
+	pr_notice("Total buffer len = %d\n", dev->buffer_len);
+
+	if (dev->buffer_len > 64 || dev->buffer_len < 16)
+		goto error;
+
+	if (dev->extra_buf1_address > 0xFBFC ||
+					dev->extra_buf1_address < 0xEC00)
+		goto error;
+
+	if (dev->extra_buf2_address > 0xFBFC ||
+					dev->extra_buf2_address < 0xEC00)
+		goto error;
+
+	if (dev->r_pointer > dev->buffer_len)
+		goto error;
+
+	ene_set_reg_mask(dev, ENE_FW1, ENE_FW1_EXTRA_BUF_HND);
+	return;
+error:
+	pr_warn("Error validating extra buffers, device probably won't work\n");
+	dev->hw_extra_buffer = false;
+	ene_clear_reg_mask(dev, ENE_FW1, ENE_FW1_EXTRA_BUF_HND);
+}
+
+
+/* Restore the pointers to extra buffers - to make module reload work*/
+static void ene_rx_restore_hw_buffer(struct ene_device *dev)
+{
+	if (!dev->hw_extra_buffer)
+		return;
+
+	ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 0,
+				dev->extra_buf1_address & 0xFF);
+	ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 1,
+				dev->extra_buf1_address >> 8);
+	ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 2, dev->extra_buf1_len);
+
+	ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 3,
+				dev->extra_buf2_address & 0xFF);
+	ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 4,
+				dev->extra_buf2_address >> 8);
+	ene_write_reg(dev, ENE_FW_SAMPLE_BUFFER + 5,
+				dev->extra_buf2_len);
+	ene_clear_reg_mask(dev, ENE_FW1, ENE_FW1_EXTRA_BUF_HND);
+}
+
+/* Read hardware write pointer */
+static void ene_rx_read_hw_pointer(struct ene_device *dev)
+{
+	if (dev->hw_extra_buffer)
+		dev->w_pointer = ene_read_reg(dev, ENE_FW_RX_POINTER);
+	else
+		dev->w_pointer = ene_read_reg(dev, ENE_FW2)
+			& ENE_FW2_BUF_WPTR ? 0 : ENE_FW_PACKET_SIZE;
+
+	dbg_verbose("RB: HW write pointer: %02x, driver read pointer: %02x",
+		dev->w_pointer, dev->r_pointer);
+}
+
+/* Gets address of next sample from HW ring buffer */
+static int ene_rx_get_sample_reg(struct ene_device *dev)
+{
+	int r_pointer;
+
+	if (dev->r_pointer == dev->w_pointer) {
+		dbg_verbose("RB: hit end, try update w_pointer");
+		ene_rx_read_hw_pointer(dev);
+	}
+
+	if (dev->r_pointer == dev->w_pointer) {
+		dbg_verbose("RB: end of data at %d", dev->r_pointer);
+		return 0;
+	}
+
+	dbg_verbose("RB: reading at offset %d", dev->r_pointer);
+	r_pointer = dev->r_pointer;
+
+	dev->r_pointer++;
+	if (dev->r_pointer == dev->buffer_len)
+		dev->r_pointer = 0;
+
+	dbg_verbose("RB: next read will be from offset %d", dev->r_pointer);
+
+	if (r_pointer < 8) {
+		dbg_verbose("RB: read at main buffer at %d", r_pointer);
+		return ENE_FW_SAMPLE_BUFFER + r_pointer;
+	}
+
+	r_pointer -= 8;
+
+	if (r_pointer < dev->extra_buf1_len) {
+		dbg_verbose("RB: read at 1st extra buffer at %d", r_pointer);
+		return dev->extra_buf1_address + r_pointer;
+	}
+
+	r_pointer -= dev->extra_buf1_len;
+
+	if (r_pointer < dev->extra_buf2_len) {
+		dbg_verbose("RB: read at 2nd extra buffer at %d", r_pointer);
+		return dev->extra_buf2_address + r_pointer;
+	}
+
+	dbg("attempt to read beyond ring buffer end");
+	return 0;
+}
+
+/* Sense current received carrier */
+static void ene_rx_sense_carrier(struct ene_device *dev)
+{
+	DEFINE_IR_RAW_EVENT(ev);
+
+	int carrier, duty_cycle;
+	int period = ene_read_reg(dev, ENE_CIRCAR_PRD);
+	int hperiod = ene_read_reg(dev, ENE_CIRCAR_HPRD);
+
+	if (!(period & ENE_CIRCAR_PRD_VALID))
+		return;
+
+	period &= ~ENE_CIRCAR_PRD_VALID;
+
+	if (!period)
+		return;
+
+	dbg("RX: hardware carrier period = %02x", period);
+	dbg("RX: hardware carrier pulse period = %02x", hperiod);
+
+	carrier = 2000000 / period;
+	duty_cycle = (hperiod * 100) / period;
+	dbg("RX: sensed carrier = %d Hz, duty cycle %d%%",
+						carrier, duty_cycle);
+	if (dev->carrier_detect_enabled) {
+		ev.carrier_report = true;
+		ev.carrier = carrier;
+		ev.duty_cycle = duty_cycle;
+		ir_raw_event_store(dev->rdev, &ev);
+	}
+}
+
+/* this enables/disables the CIR RX engine */
+static void ene_rx_enable_cir_engine(struct ene_device *dev, bool enable)
+{
+	ene_set_clear_reg_mask(dev, ENE_CIRCFG,
+			ENE_CIRCFG_RX_EN | ENE_CIRCFG_RX_IRQ, enable);
+}
+
+/* this selects input for CIR engine. Ether GPIO 0A or GPIO40*/
+static void ene_rx_select_input(struct ene_device *dev, bool gpio_0a)
+{
+	ene_set_clear_reg_mask(dev, ENE_CIRCFG2, ENE_CIRCFG2_GPIO0A, gpio_0a);
+}
+
+/*
+ * this enables alternative input via fan tachometer sensor and bypasses
+ * the hw CIR engine
+ */
+static void ene_rx_enable_fan_input(struct ene_device *dev, bool enable)
+{
+	if (!dev->hw_fan_input)
+		return;
+
+	if (!enable)
+		ene_write_reg(dev, ENE_FAN_AS_IN1, 0);
+	else {
+		ene_write_reg(dev, ENE_FAN_AS_IN1, ENE_FAN_AS_IN1_EN);
+		ene_write_reg(dev, ENE_FAN_AS_IN2, ENE_FAN_AS_IN2_EN);
+	}
+}
+
+/* setup the receiver for RX*/
+static void ene_rx_setup(struct ene_device *dev)
+{
+	bool learning_mode = dev->learning_mode_enabled ||
+					dev->carrier_detect_enabled;
+	int sample_period_adjust = 0;
+
+	dbg("RX: setup receiver, learning mode = %d", learning_mode);
+
+
+	/* This selects RLC input and clears CFG2 settings */
+	ene_write_reg(dev, ENE_CIRCFG2, 0x00);
+
+	/* set sample period*/
+	if (sample_period == ENE_DEFAULT_SAMPLE_PERIOD)
+		sample_period_adjust =
+			dev->pll_freq == ENE_DEFAULT_PLL_FREQ ? 1 : 2;
+
+	ene_write_reg(dev, ENE_CIRRLC_CFG,
+			(sample_period + sample_period_adjust) |
+						ENE_CIRRLC_CFG_OVERFLOW);
+	/* revB doesn't support inputs */
+	if (dev->hw_revision < ENE_HW_C)
+		goto select_timeout;
+
+	if (learning_mode) {
+
+		WARN_ON(!dev->hw_learning_and_tx_capable);
+
+		/* Enable the opposite of the normal input
+		That means that if GPIO40 is normally used, use GPIO0A
+		and vice versa.
+		This input will carry non demodulated
+		signal, and we will tell the hw to demodulate it itself */
+		ene_rx_select_input(dev, !dev->hw_use_gpio_0a);
+		dev->rx_fan_input_inuse = false;
+
+		/* Enable carrier demodulation */
+		ene_set_reg_mask(dev, ENE_CIRCFG, ENE_CIRCFG_CARR_DEMOD);
+
+		/* Enable carrier detection */
+		ene_write_reg(dev, ENE_CIRCAR_PULS, 0x63);
+		ene_set_clear_reg_mask(dev, ENE_CIRCFG2, ENE_CIRCFG2_CARR_DETECT,
+			dev->carrier_detect_enabled || debug);
+	} else {
+		if (dev->hw_fan_input)
+			dev->rx_fan_input_inuse = true;
+		else
+			ene_rx_select_input(dev, dev->hw_use_gpio_0a);
+
+		/* Disable carrier detection & demodulation */
+		ene_clear_reg_mask(dev, ENE_CIRCFG, ENE_CIRCFG_CARR_DEMOD);
+		ene_clear_reg_mask(dev, ENE_CIRCFG2, ENE_CIRCFG2_CARR_DETECT);
+	}
+
+select_timeout:
+	if (dev->rx_fan_input_inuse) {
+		dev->rdev->rx_resolution = US_TO_NS(ENE_FW_SAMPLE_PERIOD_FAN);
+
+		/* Fan input doesn't support timeouts, it just ends the
+			input with a maximum sample */
+		dev->rdev->min_timeout = dev->rdev->max_timeout =
+			US_TO_NS(ENE_FW_SMPL_BUF_FAN_MSK *
+				ENE_FW_SAMPLE_PERIOD_FAN);
+	} else {
+		dev->rdev->rx_resolution = US_TO_NS(sample_period);
+
+		/* Theoreticly timeout is unlimited, but we cap it
+		 * because it was seen that on one device, it
+		 * would stop sending spaces after around 250 msec.
+		 * Besides, this is close to 2^32 anyway and timeout is u32.
+		 */
+		dev->rdev->min_timeout = US_TO_NS(127 * sample_period);
+		dev->rdev->max_timeout = US_TO_NS(200000);
+	}
+
+	if (dev->hw_learning_and_tx_capable)
+		dev->rdev->tx_resolution = US_TO_NS(sample_period);
+
+	if (dev->rdev->timeout > dev->rdev->max_timeout)
+		dev->rdev->timeout = dev->rdev->max_timeout;
+	if (dev->rdev->timeout < dev->rdev->min_timeout)
+		dev->rdev->timeout = dev->rdev->min_timeout;
+}
+
+/* Enable the device for receive */
+static void ene_rx_enable_hw(struct ene_device *dev)
+{
+	u8 reg_value;
+
+	/* Enable system interrupt */
+	if (dev->hw_revision < ENE_HW_C) {
+		ene_write_reg(dev, ENEB_IRQ, dev->irq << 1);
+		ene_write_reg(dev, ENEB_IRQ_UNK1, 0x01);
+	} else {
+		reg_value = ene_read_reg(dev, ENE_IRQ) & 0xF0;
+		reg_value |= ENE_IRQ_UNK_EN;
+		reg_value &= ~ENE_IRQ_STATUS;
+		reg_value |= (dev->irq & ENE_IRQ_MASK);
+		ene_write_reg(dev, ENE_IRQ, reg_value);
+	}
+
+	/* Enable inputs */
+	ene_rx_enable_fan_input(dev, dev->rx_fan_input_inuse);
+	ene_rx_enable_cir_engine(dev, !dev->rx_fan_input_inuse);
+
+	/* ack any pending irqs - just in case */
+	ene_irq_status(dev);
+
+	/* enable firmware bits */
+	ene_set_reg_mask(dev, ENE_FW1, ENE_FW1_ENABLE | ENE_FW1_IRQ);
+
+	/* enter idle mode */
+	ir_raw_event_set_idle(dev->rdev, true);
+}
+
+/* Enable the device for receive - wrapper to track the state*/
+static void ene_rx_enable(struct ene_device *dev)
+{
+	ene_rx_enable_hw(dev);
+	dev->rx_enabled = true;
+}
+
+/* Disable the device receiver */
+static void ene_rx_disable_hw(struct ene_device *dev)
+{
+	/* disable inputs */
+	ene_rx_enable_cir_engine(dev, false);
+	ene_rx_enable_fan_input(dev, false);
+
+	/* disable hardware IRQ and firmware flag */
+	ene_clear_reg_mask(dev, ENE_FW1, ENE_FW1_ENABLE | ENE_FW1_IRQ);
+	ir_raw_event_set_idle(dev->rdev, true);
+}
+
+/* Disable the device receiver - wrapper to track the state */
+static void ene_rx_disable(struct ene_device *dev)
+{
+	ene_rx_disable_hw(dev);
+	dev->rx_enabled = false;
+}
+
+/* This resets the receiver. Useful to stop stream of spaces at end of
+ * transmission
+ */
+static void ene_rx_reset(struct ene_device *dev)
+{
+	ene_clear_reg_mask(dev, ENE_CIRCFG, ENE_CIRCFG_RX_EN);
+	ene_set_reg_mask(dev, ENE_CIRCFG, ENE_CIRCFG_RX_EN);
+}
+
+/* Set up the TX carrier frequency and duty cycle */
+static void ene_tx_set_carrier(struct ene_device *dev)
+{
+	u8 tx_puls_width;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->hw_lock, flags);
+
+	ene_set_clear_reg_mask(dev, ENE_CIRCFG,
+		ENE_CIRCFG_TX_CARR, dev->tx_period > 0);
+
+	if (!dev->tx_period)
+		goto unlock;
+
+	BUG_ON(dev->tx_duty_cycle >= 100 || dev->tx_duty_cycle <= 0);
+
+	tx_puls_width = dev->tx_period / (100 / dev->tx_duty_cycle);
+
+	if (!tx_puls_width)
+		tx_puls_width = 1;
+
+	dbg("TX: pulse distance = %d * 500 ns", dev->tx_period);
+	dbg("TX: pulse width = %d * 500 ns", tx_puls_width);
+
+	ene_write_reg(dev, ENE_CIRMOD_PRD, dev->tx_period | ENE_CIRMOD_PRD_POL);
+	ene_write_reg(dev, ENE_CIRMOD_HPRD, tx_puls_width);
+unlock:
+	spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+/* Enable/disable transmitters */
+static void ene_tx_set_transmitters(struct ene_device *dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->hw_lock, flags);
+	ene_set_clear_reg_mask(dev, ENE_GPIOFS8, ENE_GPIOFS8_GPIO41,
+					!!(dev->transmitter_mask & 0x01));
+	ene_set_clear_reg_mask(dev, ENE_GPIOFS1, ENE_GPIOFS1_GPIO0D,
+					!!(dev->transmitter_mask & 0x02));
+	spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+/* prepare transmission */
+static void ene_tx_enable(struct ene_device *dev)
+{
+	u8 conf1 = ene_read_reg(dev, ENE_CIRCFG);
+	u8 fwreg2 = ene_read_reg(dev, ENE_FW2);
+
+	dev->saved_conf1 = conf1;
+
+	/* Show information about currently connected transmitter jacks */
+	if (fwreg2 & ENE_FW2_EMMITER1_CONN)
+		dbg("TX: Transmitter #1 is connected");
+
+	if (fwreg2 & ENE_FW2_EMMITER2_CONN)
+		dbg("TX: Transmitter #2 is connected");
+
+	if (!(fwreg2 & (ENE_FW2_EMMITER1_CONN | ENE_FW2_EMMITER2_CONN)))
+		pr_warn("TX: transmitter cable isn't connected!\n");
+
+	/* disable receive on revc */
+	if (dev->hw_revision == ENE_HW_C)
+		conf1 &= ~ENE_CIRCFG_RX_EN;
+
+	/* Enable TX engine */
+	conf1 |= ENE_CIRCFG_TX_EN | ENE_CIRCFG_TX_IRQ;
+	ene_write_reg(dev, ENE_CIRCFG, conf1);
+}
+
+/* end transmission */
+static void ene_tx_disable(struct ene_device *dev)
+{
+	ene_write_reg(dev, ENE_CIRCFG, dev->saved_conf1);
+	dev->tx_buffer = NULL;
+}
+
+
+/* TX one sample - must be called with dev->hw_lock*/
+static void ene_tx_sample(struct ene_device *dev)
+{
+	u8 raw_tx;
+	u32 sample;
+	bool pulse = dev->tx_sample_pulse;
+
+	if (!dev->tx_buffer) {
+		pr_warn("TX: BUG: attempt to transmit NULL buffer\n");
+		return;
+	}
+
+	/* Grab next TX sample */
+	if (!dev->tx_sample) {
+
+		if (dev->tx_pos == dev->tx_len) {
+			if (!dev->tx_done) {
+				dbg("TX: no more data to send");
+				dev->tx_done = true;
+				goto exit;
+			} else {
+				dbg("TX: last sample sent by hardware");
+				ene_tx_disable(dev);
+				complete(&dev->tx_complete);
+				return;
+			}
+		}
+
+		sample = dev->tx_buffer[dev->tx_pos++];
+		dev->tx_sample_pulse = !dev->tx_sample_pulse;
+
+		dev->tx_sample = DIV_ROUND_CLOSEST(sample, sample_period);
+
+		if (!dev->tx_sample)
+			dev->tx_sample = 1;
+	}
+
+	raw_tx = min(dev->tx_sample , (unsigned int)ENE_CIRRLC_OUT_MASK);
+	dev->tx_sample -= raw_tx;
+
+	dbg("TX: sample %8d (%s)", raw_tx * sample_period,
+						pulse ? "pulse" : "space");
+	if (pulse)
+		raw_tx |= ENE_CIRRLC_OUT_PULSE;
+
+	ene_write_reg(dev,
+		dev->tx_reg ? ENE_CIRRLC_OUT1 : ENE_CIRRLC_OUT0, raw_tx);
+
+	dev->tx_reg = !dev->tx_reg;
+exit:
+	/* simulate TX done interrupt */
+	if (txsim)
+		mod_timer(&dev->tx_sim_timer, jiffies + HZ / 500);
+}
+
+/* timer to simulate tx done interrupt */
+static void ene_tx_irqsim(unsigned long data)
+{
+	struct ene_device *dev = (struct ene_device *)data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->hw_lock, flags);
+	ene_tx_sample(dev);
+	spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+
+/* read irq status and ack it */
+static int ene_irq_status(struct ene_device *dev)
+{
+	u8 irq_status;
+	u8 fw_flags1, fw_flags2;
+	int retval = 0;
+
+	fw_flags2 = ene_read_reg(dev, ENE_FW2);
+
+	if (dev->hw_revision < ENE_HW_C) {
+		irq_status = ene_read_reg(dev, ENEB_IRQ_STATUS);
+
+		if (!(irq_status & ENEB_IRQ_STATUS_IR))
+			return 0;
+
+		ene_clear_reg_mask(dev, ENEB_IRQ_STATUS, ENEB_IRQ_STATUS_IR);
+		return ENE_IRQ_RX;
+	}
+
+	irq_status = ene_read_reg(dev, ENE_IRQ);
+	if (!(irq_status & ENE_IRQ_STATUS))
+		return 0;
+
+	/* original driver does that twice - a workaround ? */
+	ene_write_reg(dev, ENE_IRQ, irq_status & ~ENE_IRQ_STATUS);
+	ene_write_reg(dev, ENE_IRQ, irq_status & ~ENE_IRQ_STATUS);
+
+	/* check RX interrupt */
+	if (fw_flags2 & ENE_FW2_RXIRQ) {
+		retval |= ENE_IRQ_RX;
+		ene_write_reg(dev, ENE_FW2, fw_flags2 & ~ENE_FW2_RXIRQ);
+	}
+
+	/* check TX interrupt */
+	fw_flags1 = ene_read_reg(dev, ENE_FW1);
+	if (fw_flags1 & ENE_FW1_TXIRQ) {
+		ene_write_reg(dev, ENE_FW1, fw_flags1 & ~ENE_FW1_TXIRQ);
+		retval |= ENE_IRQ_TX;
+	}
+
+	return retval;
+}
+
+/* interrupt handler */
+static irqreturn_t ene_isr(int irq, void *data)
+{
+	u16 hw_value, reg;
+	int hw_sample, irq_status;
+	bool pulse;
+	unsigned long flags;
+	irqreturn_t retval = IRQ_NONE;
+	struct ene_device *dev = (struct ene_device *)data;
+	DEFINE_IR_RAW_EVENT(ev);
+
+	spin_lock_irqsave(&dev->hw_lock, flags);
+
+	dbg_verbose("ISR called");
+	ene_rx_read_hw_pointer(dev);
+	irq_status = ene_irq_status(dev);
+
+	if (!irq_status)
+		goto unlock;
+
+	retval = IRQ_HANDLED;
+
+	if (irq_status & ENE_IRQ_TX) {
+		dbg_verbose("TX interrupt");
+		if (!dev->hw_learning_and_tx_capable) {
+			dbg("TX interrupt on unsupported device!");
+			goto unlock;
+		}
+		ene_tx_sample(dev);
+	}
+
+	if (!(irq_status & ENE_IRQ_RX))
+		goto unlock;
+
+	dbg_verbose("RX interrupt");
+
+	if (dev->hw_learning_and_tx_capable)
+		ene_rx_sense_carrier(dev);
+
+	/* On hardware that don't support extra buffer we need to trust
+		the interrupt and not track the read pointer */
+	if (!dev->hw_extra_buffer)
+		dev->r_pointer = dev->w_pointer == 0 ? ENE_FW_PACKET_SIZE : 0;
+
+	while (1) {
+
+		reg = ene_rx_get_sample_reg(dev);
+
+		dbg_verbose("next sample to read at: %04x", reg);
+		if (!reg)
+			break;
+
+		hw_value = ene_read_reg(dev, reg);
+
+		if (dev->rx_fan_input_inuse) {
+
+			int offset = ENE_FW_SMPL_BUF_FAN - ENE_FW_SAMPLE_BUFFER;
+
+			/* read high part of the sample */
+			hw_value |= ene_read_reg(dev, reg + offset) << 8;
+			pulse = hw_value & ENE_FW_SMPL_BUF_FAN_PLS;
+
+			/* clear space bit, and other unused bits */
+			hw_value &= ENE_FW_SMPL_BUF_FAN_MSK;
+			hw_sample = hw_value * ENE_FW_SAMPLE_PERIOD_FAN;
+
+		} else {
+			pulse = !(hw_value & ENE_FW_SAMPLE_SPACE);
+			hw_value &= ~ENE_FW_SAMPLE_SPACE;
+			hw_sample = hw_value * sample_period;
+
+			if (dev->rx_period_adjust) {
+				hw_sample *= 100;
+				hw_sample /= (100 + dev->rx_period_adjust);
+			}
+		}
+
+		if (!dev->hw_extra_buffer && !hw_sample) {
+			dev->r_pointer = dev->w_pointer;
+			continue;
+		}
+
+		dbg("RX: %d (%s)", hw_sample, pulse ? "pulse" : "space");
+
+		ev.duration = US_TO_NS(hw_sample);
+		ev.pulse = pulse;
+		ir_raw_event_store_with_filter(dev->rdev, &ev);
+	}
+
+	ir_raw_event_handle(dev->rdev);
+unlock:
+	spin_unlock_irqrestore(&dev->hw_lock, flags);
+	return retval;
+}
+
+/* Initialize default settings */
+static void ene_setup_default_settings(struct ene_device *dev)
+{
+	dev->tx_period = 32;
+	dev->tx_duty_cycle = 50; /*%*/
+	dev->transmitter_mask = 0x03;
+	dev->learning_mode_enabled = learning_mode_force;
+
+	/* Set reasonable default timeout */
+	dev->rdev->timeout = US_TO_NS(150000);
+}
+
+/* Upload all hardware settings at once. Used at load and resume time */
+static void ene_setup_hw_settings(struct ene_device *dev)
+{
+	if (dev->hw_learning_and_tx_capable) {
+		ene_tx_set_carrier(dev);
+		ene_tx_set_transmitters(dev);
+	}
+
+	ene_rx_setup(dev);
+}
+
+/* outside interface: called on first open*/
+static int ene_open(struct rc_dev *rdev)
+{
+	struct ene_device *dev = rdev->priv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->hw_lock, flags);
+	ene_rx_enable(dev);
+	spin_unlock_irqrestore(&dev->hw_lock, flags);
+	return 0;
+}
+
+/* outside interface: called on device close*/
+static void ene_close(struct rc_dev *rdev)
+{
+	struct ene_device *dev = rdev->priv;
+	unsigned long flags;
+	spin_lock_irqsave(&dev->hw_lock, flags);
+
+	ene_rx_disable(dev);
+	spin_unlock_irqrestore(&dev->hw_lock, flags);
+}
+
+/* outside interface: set transmitter mask */
+static int ene_set_tx_mask(struct rc_dev *rdev, u32 tx_mask)
+{
+	struct ene_device *dev = rdev->priv;
+	dbg("TX: attempt to set transmitter mask %02x", tx_mask);
+
+	/* invalid txmask */
+	if (!tx_mask || tx_mask & ~0x03) {
+		dbg("TX: invalid mask");
+		/* return count of transmitters */
+		return 2;
+	}
+
+	dev->transmitter_mask = tx_mask;
+	ene_tx_set_transmitters(dev);
+	return 0;
+}
+
+/* outside interface : set tx carrier */
+static int ene_set_tx_carrier(struct rc_dev *rdev, u32 carrier)
+{
+	struct ene_device *dev = rdev->priv;
+	u32 period;
+
+	dbg("TX: attempt to set tx carrier to %d kHz", carrier);
+	if (carrier == 0)
+		return -EINVAL;
+
+	period = 2000000 / carrier;
+	if (period && (period > ENE_CIRMOD_PRD_MAX ||
+			period < ENE_CIRMOD_PRD_MIN)) {
+
+		dbg("TX: out of range %d-%d kHz carrier",
+			2000 / ENE_CIRMOD_PRD_MIN, 2000 / ENE_CIRMOD_PRD_MAX);
+		return -1;
+	}
+
+	dev->tx_period = period;
+	ene_tx_set_carrier(dev);
+	return 0;
+}
+
+/*outside interface : set tx duty cycle */
+static int ene_set_tx_duty_cycle(struct rc_dev *rdev, u32 duty_cycle)
+{
+	struct ene_device *dev = rdev->priv;
+	dbg("TX: setting duty cycle to %d%%", duty_cycle);
+	dev->tx_duty_cycle = duty_cycle;
+	ene_tx_set_carrier(dev);
+	return 0;
+}
+
+/* outside interface: enable learning mode */
+static int ene_set_learning_mode(struct rc_dev *rdev, int enable)
+{
+	struct ene_device *dev = rdev->priv;
+	unsigned long flags;
+	if (enable == dev->learning_mode_enabled)
+		return 0;
+
+	spin_lock_irqsave(&dev->hw_lock, flags);
+	dev->learning_mode_enabled = enable;
+	ene_rx_disable(dev);
+	ene_rx_setup(dev);
+	ene_rx_enable(dev);
+	spin_unlock_irqrestore(&dev->hw_lock, flags);
+	return 0;
+}
+
+static int ene_set_carrier_report(struct rc_dev *rdev, int enable)
+{
+	struct ene_device *dev = rdev->priv;
+	unsigned long flags;
+
+	if (enable == dev->carrier_detect_enabled)
+		return 0;
+
+	spin_lock_irqsave(&dev->hw_lock, flags);
+	dev->carrier_detect_enabled = enable;
+	ene_rx_disable(dev);
+	ene_rx_setup(dev);
+	ene_rx_enable(dev);
+	spin_unlock_irqrestore(&dev->hw_lock, flags);
+	return 0;
+}
+
+/* outside interface: enable or disable idle mode */
+static void ene_set_idle(struct rc_dev *rdev, bool idle)
+{
+	struct ene_device *dev = rdev->priv;
+
+	if (idle) {
+		ene_rx_reset(dev);
+		dbg("RX: end of data");
+	}
+}
+
+/* outside interface: transmit */
+static int ene_transmit(struct rc_dev *rdev, unsigned *buf, unsigned n)
+{
+	struct ene_device *dev = rdev->priv;
+	unsigned long flags;
+
+	dev->tx_buffer = buf;
+	dev->tx_len = n;
+	dev->tx_pos = 0;
+	dev->tx_reg = 0;
+	dev->tx_done = 0;
+	dev->tx_sample = 0;
+	dev->tx_sample_pulse = false;
+
+	dbg("TX: %d samples", dev->tx_len);
+
+	spin_lock_irqsave(&dev->hw_lock, flags);
+
+	ene_tx_enable(dev);
+
+	/* Transmit first two samples */
+	ene_tx_sample(dev);
+	ene_tx_sample(dev);
+
+	spin_unlock_irqrestore(&dev->hw_lock, flags);
+
+	if (wait_for_completion_timeout(&dev->tx_complete, 2 * HZ) == 0) {
+		dbg("TX: timeout");
+		spin_lock_irqsave(&dev->hw_lock, flags);
+		ene_tx_disable(dev);
+		spin_unlock_irqrestore(&dev->hw_lock, flags);
+	} else
+		dbg("TX: done");
+	return n;
+}
+
+/* probe entry */
+static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
+{
+	int error = -ENOMEM;
+	struct rc_dev *rdev;
+	struct ene_device *dev;
+
+	/* allocate memory */
+	dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL);
+	rdev = rc_allocate_device();
+	if (!dev || !rdev)
+		goto exit_free_dev_rdev;
+
+	/* validate resources */
+	error = -ENODEV;
+
+	/* init these to -1, as 0 is valid for both */
+	dev->hw_io = -1;
+	dev->irq = -1;
+
+	if (!pnp_port_valid(pnp_dev, 0) ||
+	    pnp_port_len(pnp_dev, 0) < ENE_IO_SIZE)
+		goto exit_free_dev_rdev;
+
+	if (!pnp_irq_valid(pnp_dev, 0))
+		goto exit_free_dev_rdev;
+
+	spin_lock_init(&dev->hw_lock);
+
+	dev->hw_io = pnp_port_start(pnp_dev, 0);
+	dev->irq = pnp_irq(pnp_dev, 0);
+
+
+	pnp_set_drvdata(pnp_dev, dev);
+	dev->pnp_dev = pnp_dev;
+
+	/* don't allow too short/long sample periods */
+	if (sample_period < 5 || sample_period > 0x7F)
+		sample_period = ENE_DEFAULT_SAMPLE_PERIOD;
+
+	/* detect hardware version and features */
+	error = ene_hw_detect(dev);
+	if (error)
+		goto exit_free_dev_rdev;
+
+	if (!dev->hw_learning_and_tx_capable && txsim) {
+		dev->hw_learning_and_tx_capable = true;
+		setup_timer(&dev->tx_sim_timer, ene_tx_irqsim,
+						(long unsigned int)dev);
+		pr_warn("Simulation of TX activated\n");
+	}
+
+	if (!dev->hw_learning_and_tx_capable)
+		learning_mode_force = false;
+
+	rdev->driver_type = RC_DRIVER_IR_RAW;
+	rdev->allowed_protocols = RC_BIT_ALL;
+	rdev->priv = dev;
+	rdev->open = ene_open;
+	rdev->close = ene_close;
+	rdev->s_idle = ene_set_idle;
+	rdev->driver_name = ENE_DRIVER_NAME;
+	rdev->map_name = RC_MAP_RC6_MCE;
+	rdev->input_name = "ENE eHome Infrared Remote Receiver";
+
+	if (dev->hw_learning_and_tx_capable) {
+		rdev->s_learning_mode = ene_set_learning_mode;
+		init_completion(&dev->tx_complete);
+		rdev->tx_ir = ene_transmit;
+		rdev->s_tx_mask = ene_set_tx_mask;
+		rdev->s_tx_carrier = ene_set_tx_carrier;
+		rdev->s_tx_duty_cycle = ene_set_tx_duty_cycle;
+		rdev->s_carrier_report = ene_set_carrier_report;
+		rdev->input_name = "ENE eHome Infrared Remote Transceiver";
+	}
+
+	dev->rdev = rdev;
+
+	ene_rx_setup_hw_buffer(dev);
+	ene_setup_default_settings(dev);
+	ene_setup_hw_settings(dev);
+
+	device_set_wakeup_capable(&pnp_dev->dev, true);
+	device_set_wakeup_enable(&pnp_dev->dev, true);
+
+	error = rc_register_device(rdev);
+	if (error < 0)
+		goto exit_free_dev_rdev;
+
+	/* claim the resources */
+	error = -EBUSY;
+	if (!request_region(dev->hw_io, ENE_IO_SIZE, ENE_DRIVER_NAME)) {
+		goto exit_unregister_device;
+	}
+
+	if (request_irq(dev->irq, ene_isr,
+			IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev)) {
+		goto exit_release_hw_io;
+	}
+
+	pr_notice("driver has been successfully loaded\n");
+	return 0;
+
+exit_release_hw_io:
+	release_region(dev->hw_io, ENE_IO_SIZE);
+exit_unregister_device:
+	rc_unregister_device(rdev);
+	rdev = NULL;
+exit_free_dev_rdev:
+	rc_free_device(rdev);
+	kfree(dev);
+	return error;
+}
+
+/* main unload function */
+static void ene_remove(struct pnp_dev *pnp_dev)
+{
+	struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->hw_lock, flags);
+	ene_rx_disable(dev);
+	ene_rx_restore_hw_buffer(dev);
+	spin_unlock_irqrestore(&dev->hw_lock, flags);
+
+	free_irq(dev->irq, dev);
+	release_region(dev->hw_io, ENE_IO_SIZE);
+	rc_unregister_device(dev->rdev);
+	kfree(dev);
+}
+
+/* enable wake on IR (wakes on specific button on original remote) */
+static void ene_enable_wake(struct ene_device *dev, bool enable)
+{
+	dbg("wake on IR %s", enable ? "enabled" : "disabled");
+	ene_set_clear_reg_mask(dev, ENE_FW1, ENE_FW1_WAKE, enable);
+}
+
+#ifdef CONFIG_PM
+static int ene_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
+{
+	struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+	bool wake = device_may_wakeup(&dev->pnp_dev->dev);
+
+	if (!wake && dev->rx_enabled)
+		ene_rx_disable_hw(dev);
+
+	ene_enable_wake(dev, wake);
+	return 0;
+}
+
+static int ene_resume(struct pnp_dev *pnp_dev)
+{
+	struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+	ene_setup_hw_settings(dev);
+
+	if (dev->rx_enabled)
+		ene_rx_enable(dev);
+
+	ene_enable_wake(dev, false);
+	return 0;
+}
+#endif
+
+static void ene_shutdown(struct pnp_dev *pnp_dev)
+{
+	struct ene_device *dev = pnp_get_drvdata(pnp_dev);
+	ene_enable_wake(dev, true);
+}
+
+static const struct pnp_device_id ene_ids[] = {
+	{.id = "ENE0100",},
+	{.id = "ENE0200",},
+	{.id = "ENE0201",},
+	{.id = "ENE0202",},
+	{},
+};
+
+static struct pnp_driver ene_driver = {
+	.name = ENE_DRIVER_NAME,
+	.id_table = ene_ids,
+	.flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
+
+	.probe = ene_probe,
+	.remove = ene_remove,
+#ifdef CONFIG_PM
+	.suspend = ene_suspend,
+	.resume = ene_resume,
+#endif
+	.shutdown = ene_shutdown,
+};
+
+module_param(sample_period, int, S_IRUGO);
+MODULE_PARM_DESC(sample_period, "Hardware sample period (50 us default)");
+
+module_param(learning_mode_force, bool, S_IRUGO);
+MODULE_PARM_DESC(learning_mode_force, "Enable learning mode by default");
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug level");
+
+module_param(txsim, bool, S_IRUGO);
+MODULE_PARM_DESC(txsim,
+	"Simulate TX features on unsupported hardware (dangerous)");
+
+MODULE_DEVICE_TABLE(pnp, ene_ids);
+MODULE_DESCRIPTION
+	("Infrared input driver for KB3926B/C/D/E/F "
+	"(aka ENE0100/ENE0200/ENE0201/ENE0202) CIR port");
+
+MODULE_AUTHOR("Maxim Levitsky");
+MODULE_LICENSE("GPL");
+
+module_pnp_driver(ene_driver);
diff --git a/drivers/media/rc/ene_ir.h b/drivers/media/rc/ene_ir.h
new file mode 100644
index 0000000..a7911e3
--- /dev/null
+++ b/drivers/media/rc/ene_ir.h
@@ -0,0 +1,250 @@
+/*
+ * driver for ENE KB3926 B/C/D/E/F CIR (also known as ENE0XXX)
+ *
+ * Copyright (C) 2010 Maxim Levitsky <maximlevitsky@gmail.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/spinlock.h>
+
+
+/* hardware address */
+#define ENE_STATUS		0	/* hardware status - unused */
+#define ENE_ADDR_HI		1	/* hi byte of register address */
+#define ENE_ADDR_LO		2	/* low byte of register address */
+#define ENE_IO			3	/* read/write window */
+#define ENE_IO_SIZE		4
+
+/* 8 bytes of samples, divided in 2 packets*/
+#define ENE_FW_SAMPLE_BUFFER	0xF8F0	/* sample buffer */
+#define ENE_FW_SAMPLE_SPACE	0x80	/* sample is space */
+#define ENE_FW_PACKET_SIZE	4
+
+/* first firmware flag register */
+#define ENE_FW1			0xF8F8  /* flagr */
+#define	ENE_FW1_ENABLE		0x01	/* enable fw processing */
+#define ENE_FW1_TXIRQ		0x02	/* TX interrupt pending */
+#define ENE_FW1_HAS_EXTRA_BUF	0x04	/* fw uses extra buffer*/
+#define ENE_FW1_EXTRA_BUF_HND	0x08	/* extra buffer handshake bit*/
+#define ENE_FW1_LED_ON		0x10	/* turn on a led */
+
+#define ENE_FW1_WPATTERN	0x20	/* enable wake pattern */
+#define ENE_FW1_WAKE		0x40	/* enable wake from S3 */
+#define ENE_FW1_IRQ		0x80	/* enable interrupt */
+
+/* second firmware flag register */
+#define ENE_FW2			0xF8F9  /* flagw */
+#define ENE_FW2_BUF_WPTR	0x01	/* which half of the buffer to read */
+#define ENE_FW2_RXIRQ		0x04	/* RX IRQ pending*/
+#define ENE_FW2_GP0A		0x08	/* Use GPIO0A for demodulated input */
+#define ENE_FW2_EMMITER1_CONN	0x10	/* TX emmiter 1 connected */
+#define ENE_FW2_EMMITER2_CONN	0x20	/* TX emmiter 2 connected */
+
+#define ENE_FW2_FAN_INPUT	0x40	/* fan input used for demodulated data*/
+#define ENE_FW2_LEARNING	0x80	/* hardware supports learning and TX */
+
+/* firmware RX pointer for new style buffer */
+#define ENE_FW_RX_POINTER	0xF8FA
+
+/* high parts of samples for fan input (8 samples)*/
+#define ENE_FW_SMPL_BUF_FAN	0xF8FB
+#define ENE_FW_SMPL_BUF_FAN_PLS	0x8000	/* combined sample is pulse */
+#define ENE_FW_SMPL_BUF_FAN_MSK	0x0FFF  /* combined sample maximum value */
+#define ENE_FW_SAMPLE_PERIOD_FAN 61	/* fan input has fixed sample period */
+
+/* transmitter ports */
+#define ENE_GPIOFS1		0xFC01
+#define ENE_GPIOFS1_GPIO0D	0x20	/* enable tx output on GPIO0D */
+#define ENE_GPIOFS8		0xFC08
+#define ENE_GPIOFS8_GPIO41	0x02	/* enable tx output on GPIO40 */
+
+/* IRQ registers block (for revision B) */
+#define ENEB_IRQ		0xFD09	/* IRQ number */
+#define ENEB_IRQ_UNK1		0xFD17	/* unknown setting = 1 */
+#define ENEB_IRQ_STATUS		0xFD80	/* irq status */
+#define ENEB_IRQ_STATUS_IR	0x20	/* IR irq */
+
+/* fan as input settings */
+#define ENE_FAN_AS_IN1		0xFE30  /* fan init reg 1 */
+#define ENE_FAN_AS_IN1_EN	0xCD
+#define ENE_FAN_AS_IN2		0xFE31  /* fan init reg 2 */
+#define ENE_FAN_AS_IN2_EN	0x03
+
+/* IRQ registers block (for revision C,D) */
+#define ENE_IRQ			0xFE9B	/* new irq settings register */
+#define ENE_IRQ_MASK		0x0F	/* irq number mask */
+#define ENE_IRQ_UNK_EN		0x10	/* always enabled */
+#define ENE_IRQ_STATUS		0x20	/* irq status and ACK */
+
+/* CIR Config register #1 */
+#define ENE_CIRCFG		0xFEC0
+#define ENE_CIRCFG_RX_EN	0x01	/* RX enable */
+#define ENE_CIRCFG_RX_IRQ	0x02	/* Enable hardware interrupt */
+#define ENE_CIRCFG_REV_POL	0x04	/* Input polarity reversed */
+#define ENE_CIRCFG_CARR_DEMOD	0x08	/* Enable carrier demodulator */
+
+#define ENE_CIRCFG_TX_EN	0x10	/* TX enable */
+#define ENE_CIRCFG_TX_IRQ	0x20	/* Send interrupt on TX done */
+#define ENE_CIRCFG_TX_POL_REV	0x40	/* TX polarity reversed */
+#define ENE_CIRCFG_TX_CARR	0x80	/* send TX carrier or not */
+
+/* CIR config register #2 */
+#define ENE_CIRCFG2		0xFEC1
+#define ENE_CIRCFG2_RLC		0x00
+#define ENE_CIRCFG2_RC5		0x01
+#define ENE_CIRCFG2_RC6		0x02
+#define ENE_CIRCFG2_NEC		0x03
+#define ENE_CIRCFG2_CARR_DETECT	0x10	/* Enable carrier detection */
+#define ENE_CIRCFG2_GPIO0A	0x20	/* Use GPIO0A instead of GPIO40 for input */
+#define ENE_CIRCFG2_FAST_SAMPL1	0x40	/* Fast leading pulse detection for RC6 */
+#define ENE_CIRCFG2_FAST_SAMPL2	0x80	/* Fast data detection for RC6 */
+
+/* Knobs for protocol decoding - will document when/if will use them */
+#define ENE_CIRPF		0xFEC2
+#define ENE_CIRHIGH		0xFEC3
+#define ENE_CIRBIT		0xFEC4
+#define ENE_CIRSTART		0xFEC5
+#define ENE_CIRSTART2		0xFEC6
+
+/* Actual register which contains RLC RX data - read by firmware */
+#define ENE_CIRDAT_IN		0xFEC7
+
+
+/* RLC configuration - sample period (1us resulution) + idle mode */
+#define ENE_CIRRLC_CFG		0xFEC8
+#define ENE_CIRRLC_CFG_OVERFLOW	0x80	/* interrupt on overflows if set */
+#define ENE_DEFAULT_SAMPLE_PERIOD 50
+
+/* Two byte RLC TX buffer */
+#define ENE_CIRRLC_OUT0		0xFEC9
+#define ENE_CIRRLC_OUT1		0xFECA
+#define ENE_CIRRLC_OUT_PULSE	0x80	/* Transmitted sample is pulse */
+#define ENE_CIRRLC_OUT_MASK	0x7F
+
+
+/* Carrier detect setting
+ * Low nibble  - number of carrier pulses to average
+ * High nibble - number of initial carrier pulses to discard
+ */
+#define ENE_CIRCAR_PULS		0xFECB
+
+/* detected RX carrier period (resolution: 500 ns) */
+#define ENE_CIRCAR_PRD		0xFECC
+#define ENE_CIRCAR_PRD_VALID	0x80	/* data valid content valid */
+
+/* detected RX carrier pulse width (resolution: 500 ns) */
+#define ENE_CIRCAR_HPRD		0xFECD
+
+/* TX period (resolution: 500 ns, minimum 2)*/
+#define ENE_CIRMOD_PRD		0xFECE
+#define ENE_CIRMOD_PRD_POL	0x80	/* TX carrier polarity*/
+
+#define ENE_CIRMOD_PRD_MAX	0x7F	/* 15.87 kHz */
+#define ENE_CIRMOD_PRD_MIN	0x02	/* 1 Mhz */
+
+/* TX pulse width (resolution: 500 ns)*/
+#define ENE_CIRMOD_HPRD		0xFECF
+
+/* Hardware versions */
+#define ENE_ECHV		0xFF00	/* hardware revision */
+#define ENE_PLLFRH		0xFF16
+#define ENE_PLLFRL		0xFF17
+#define ENE_DEFAULT_PLL_FREQ	1000
+
+#define ENE_ECSTS		0xFF1D
+#define ENE_ECSTS_RSRVD		0x04
+
+#define ENE_ECVER_MAJOR		0xFF1E	/* chip version */
+#define ENE_ECVER_MINOR		0xFF1F
+#define ENE_HW_VER_OLD		0xFD00
+
+/******************************************************************************/
+
+#define ENE_DRIVER_NAME		"ene_ir"
+
+#define ENE_IRQ_RX		1
+#define ENE_IRQ_TX		2
+
+#define  ENE_HW_B		1	/* 3926B */
+#define  ENE_HW_C		2	/* 3926C */
+#define  ENE_HW_D		3	/* 3926D or later */
+
+#define __dbg(level, format, ...)				\
+do {								\
+	if (debug >= level)					\
+		pr_info(format "\n", ## __VA_ARGS__);		\
+} while (0)
+
+#define dbg(format, ...)		__dbg(1, format, ## __VA_ARGS__)
+#define dbg_verbose(format, ...)	__dbg(2, format, ## __VA_ARGS__)
+#define dbg_regs(format, ...)		__dbg(3, format, ## __VA_ARGS__)
+
+struct ene_device {
+	struct pnp_dev *pnp_dev;
+	struct rc_dev *rdev;
+
+	/* hw IO settings */
+	long hw_io;
+	int irq;
+	spinlock_t hw_lock;
+
+	/* HW features */
+	int hw_revision;			/* hardware revision */
+	bool hw_use_gpio_0a;			/* gpio0a is demodulated input*/
+	bool hw_extra_buffer;			/* hardware has 'extra buffer' */
+	bool hw_fan_input;			/* fan input is IR data source */
+	bool hw_learning_and_tx_capable;	/* learning & tx capable */
+	int  pll_freq;
+	int buffer_len;
+
+	/* Extra RX buffer location */
+	int extra_buf1_address;
+	int extra_buf1_len;
+	int extra_buf2_address;
+	int extra_buf2_len;
+
+	/* HW state*/
+	int r_pointer;				/* pointer to next sample to read */
+	int w_pointer;				/* pointer to next sample hw will write */
+	bool rx_fan_input_inuse;		/* is fan input in use for rx*/
+	int tx_reg;				/* current reg used for TX */
+	u8  saved_conf1;			/* saved FEC0 reg */
+	unsigned int tx_sample;			/* current sample for TX */
+	bool tx_sample_pulse;			/* current sample is pulse */
+
+	/* TX buffer */
+	unsigned *tx_buffer;			/* input samples buffer*/
+	int tx_pos;				/* position in that buffer */
+	int tx_len;				/* current len of tx buffer */
+	int tx_done;				/* done transmitting */
+						/* one more sample pending*/
+	struct completion tx_complete;		/* TX completion */
+	struct timer_list tx_sim_timer;
+
+	/* TX settings */
+	int tx_period;
+	int tx_duty_cycle;
+	int transmitter_mask;
+
+	/* RX settings */
+	bool learning_mode_enabled;		/* learning input enabled */
+	bool carrier_detect_enabled;		/* carrier detect enabled */
+	int rx_period_adjust;
+	bool rx_enabled;
+};
+
+static int ene_irq_status(struct ene_device *dev);
+static void ene_rx_read_hw_pointer(struct ene_device *dev);
diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
new file mode 100644
index 0000000..bd7b3bd
--- /dev/null
+++ b/drivers/media/rc/fintek-cir.c
@@ -0,0 +1,695 @@
+/*
+ * Driver for Feature Integration Technology Inc. (aka Fintek) LPC CIR
+ *
+ * Copyright (C) 2011 Jarod Wilson <jarod@redhat.com>
+ *
+ * Special thanks to Fintek for providing hardware and spec sheets.
+ * This driver is based upon the nuvoton, ite and ene drivers for
+ * similar hardware.
+ *
+ * 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
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pnp.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <media/rc-core.h>
+
+#include "fintek-cir.h"
+
+/* write val to config reg */
+static inline void fintek_cr_write(struct fintek_dev *fintek, u8 val, u8 reg)
+{
+	fit_dbg("%s: reg 0x%02x, val 0x%02x  (ip/dp: %02x/%02x)",
+		__func__, reg, val, fintek->cr_ip, fintek->cr_dp);
+	outb(reg, fintek->cr_ip);
+	outb(val, fintek->cr_dp);
+}
+
+/* read val from config reg */
+static inline u8 fintek_cr_read(struct fintek_dev *fintek, u8 reg)
+{
+	u8 val;
+
+	outb(reg, fintek->cr_ip);
+	val = inb(fintek->cr_dp);
+
+	fit_dbg("%s: reg 0x%02x, val 0x%02x  (ip/dp: %02x/%02x)",
+		__func__, reg, val, fintek->cr_ip, fintek->cr_dp);
+	return val;
+}
+
+/* update config register bit without changing other bits */
+static inline void fintek_set_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg)
+{
+	u8 tmp = fintek_cr_read(fintek, reg) | val;
+	fintek_cr_write(fintek, tmp, reg);
+}
+
+/* clear config register bit without changing other bits */
+static inline void fintek_clear_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg)
+{
+	u8 tmp = fintek_cr_read(fintek, reg) & ~val;
+	fintek_cr_write(fintek, tmp, reg);
+}
+
+/* enter config mode */
+static inline void fintek_config_mode_enable(struct fintek_dev *fintek)
+{
+	/* Enabling Config Mode explicitly requires writing 2x */
+	outb(CONFIG_REG_ENABLE, fintek->cr_ip);
+	outb(CONFIG_REG_ENABLE, fintek->cr_ip);
+}
+
+/* exit config mode */
+static inline void fintek_config_mode_disable(struct fintek_dev *fintek)
+{
+	outb(CONFIG_REG_DISABLE, fintek->cr_ip);
+}
+
+/*
+ * When you want to address a specific logical device, write its logical
+ * device number to GCR_LOGICAL_DEV_NO
+ */
+static inline void fintek_select_logical_dev(struct fintek_dev *fintek, u8 ldev)
+{
+	fintek_cr_write(fintek, ldev, GCR_LOGICAL_DEV_NO);
+}
+
+/* write val to cir config register */
+static inline void fintek_cir_reg_write(struct fintek_dev *fintek, u8 val, u8 offset)
+{
+	outb(val, fintek->cir_addr + offset);
+}
+
+/* read val from cir config register */
+static u8 fintek_cir_reg_read(struct fintek_dev *fintek, u8 offset)
+{
+	u8 val;
+
+	val = inb(fintek->cir_addr + offset);
+
+	return val;
+}
+
+/* dump current cir register contents */
+static void cir_dump_regs(struct fintek_dev *fintek)
+{
+	fintek_config_mode_enable(fintek);
+	fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
+
+	pr_info("%s: Dump CIR logical device registers:\n", FINTEK_DRIVER_NAME);
+	pr_info(" * CR CIR BASE ADDR: 0x%x\n",
+		(fintek_cr_read(fintek, CIR_CR_BASE_ADDR_HI) << 8) |
+		fintek_cr_read(fintek, CIR_CR_BASE_ADDR_LO));
+	pr_info(" * CR CIR IRQ NUM:   0x%x\n",
+		fintek_cr_read(fintek, CIR_CR_IRQ_SEL));
+
+	fintek_config_mode_disable(fintek);
+
+	pr_info("%s: Dump CIR registers:\n", FINTEK_DRIVER_NAME);
+	pr_info(" * STATUS:     0x%x\n",
+		fintek_cir_reg_read(fintek, CIR_STATUS));
+	pr_info(" * CONTROL:    0x%x\n",
+		fintek_cir_reg_read(fintek, CIR_CONTROL));
+	pr_info(" * RX_DATA:    0x%x\n",
+		fintek_cir_reg_read(fintek, CIR_RX_DATA));
+	pr_info(" * TX_CONTROL: 0x%x\n",
+		fintek_cir_reg_read(fintek, CIR_TX_CONTROL));
+	pr_info(" * TX_DATA:    0x%x\n",
+		fintek_cir_reg_read(fintek, CIR_TX_DATA));
+}
+
+/* detect hardware features */
+static int fintek_hw_detect(struct fintek_dev *fintek)
+{
+	unsigned long flags;
+	u8 chip_major, chip_minor;
+	u8 vendor_major, vendor_minor;
+	u8 portsel, ir_class;
+	u16 vendor, chip;
+
+	fintek_config_mode_enable(fintek);
+
+	/* Check if we're using config port 0x4e or 0x2e */
+	portsel = fintek_cr_read(fintek, GCR_CONFIG_PORT_SEL);
+	if (portsel == 0xff) {
+		fit_pr(KERN_INFO, "first portsel read was bunk, trying alt");
+		fintek_config_mode_disable(fintek);
+		fintek->cr_ip = CR_INDEX_PORT2;
+		fintek->cr_dp = CR_DATA_PORT2;
+		fintek_config_mode_enable(fintek);
+		portsel = fintek_cr_read(fintek, GCR_CONFIG_PORT_SEL);
+	}
+	fit_dbg("portsel reg: 0x%02x", portsel);
+
+	ir_class = fintek_cir_reg_read(fintek, CIR_CR_CLASS);
+	fit_dbg("ir_class reg: 0x%02x", ir_class);
+
+	switch (ir_class) {
+	case CLASS_RX_2TX:
+	case CLASS_RX_1TX:
+		fintek->hw_tx_capable = true;
+		break;
+	case CLASS_RX_ONLY:
+	default:
+		fintek->hw_tx_capable = false;
+		break;
+	}
+
+	chip_major = fintek_cr_read(fintek, GCR_CHIP_ID_HI);
+	chip_minor = fintek_cr_read(fintek, GCR_CHIP_ID_LO);
+	chip  = chip_major << 8 | chip_minor;
+
+	vendor_major = fintek_cr_read(fintek, GCR_VENDOR_ID_HI);
+	vendor_minor = fintek_cr_read(fintek, GCR_VENDOR_ID_LO);
+	vendor = vendor_major << 8 | vendor_minor;
+
+	if (vendor != VENDOR_ID_FINTEK)
+		fit_pr(KERN_WARNING, "Unknown vendor ID: 0x%04x", vendor);
+	else
+		fit_dbg("Read Fintek vendor ID from chip");
+
+	fintek_config_mode_disable(fintek);
+
+	spin_lock_irqsave(&fintek->fintek_lock, flags);
+	fintek->chip_major  = chip_major;
+	fintek->chip_minor  = chip_minor;
+	fintek->chip_vendor = vendor;
+
+	/*
+	 * Newer reviews of this chipset uses port 8 instead of 5
+	 */
+	if ((chip != 0x0408) && (chip != 0x0804))
+		fintek->logical_dev_cir = LOGICAL_DEV_CIR_REV2;
+	else
+		fintek->logical_dev_cir = LOGICAL_DEV_CIR_REV1;
+
+	spin_unlock_irqrestore(&fintek->fintek_lock, flags);
+
+	return 0;
+}
+
+static void fintek_cir_ldev_init(struct fintek_dev *fintek)
+{
+	/* Select CIR logical device and enable */
+	fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
+	fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);
+
+	/* Write allocated CIR address and IRQ information to hardware */
+	fintek_cr_write(fintek, fintek->cir_addr >> 8, CIR_CR_BASE_ADDR_HI);
+	fintek_cr_write(fintek, fintek->cir_addr & 0xff, CIR_CR_BASE_ADDR_LO);
+
+	fintek_cr_write(fintek, fintek->cir_irq, CIR_CR_IRQ_SEL);
+
+	fit_dbg("CIR initialized, base io address: 0x%lx, irq: %d (len: %d)",
+		fintek->cir_addr, fintek->cir_irq, fintek->cir_port_len);
+}
+
+/* enable CIR interrupts */
+static void fintek_enable_cir_irq(struct fintek_dev *fintek)
+{
+	fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_EN, CIR_STATUS);
+}
+
+static void fintek_cir_regs_init(struct fintek_dev *fintek)
+{
+	/* clear any and all stray interrupts */
+	fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);
+
+	/* and finally, enable interrupts */
+	fintek_enable_cir_irq(fintek);
+}
+
+static void fintek_enable_wake(struct fintek_dev *fintek)
+{
+	fintek_config_mode_enable(fintek);
+	fintek_select_logical_dev(fintek, LOGICAL_DEV_ACPI);
+
+	/* Allow CIR PME's to wake system */
+	fintek_set_reg_bit(fintek, ACPI_WAKE_EN_CIR_BIT, LDEV_ACPI_WAKE_EN_REG);
+	/* Enable CIR PME's */
+	fintek_set_reg_bit(fintek, ACPI_PME_CIR_BIT, LDEV_ACPI_PME_EN_REG);
+	/* Clear CIR PME status register */
+	fintek_set_reg_bit(fintek, ACPI_PME_CIR_BIT, LDEV_ACPI_PME_CLR_REG);
+	/* Save state */
+	fintek_set_reg_bit(fintek, ACPI_STATE_CIR_BIT, LDEV_ACPI_STATE_REG);
+
+	fintek_config_mode_disable(fintek);
+}
+
+static int fintek_cmdsize(u8 cmd, u8 subcmd)
+{
+	int datasize = 0;
+
+	switch (cmd) {
+	case BUF_COMMAND_NULL:
+		if (subcmd == BUF_HW_CMD_HEADER)
+			datasize = 1;
+		break;
+	case BUF_HW_CMD_HEADER:
+		if (subcmd == BUF_CMD_G_REVISION)
+			datasize = 2;
+		break;
+	case BUF_COMMAND_HEADER:
+		switch (subcmd) {
+		case BUF_CMD_S_CARRIER:
+		case BUF_CMD_S_TIMEOUT:
+		case BUF_RSP_PULSE_COUNT:
+			datasize = 2;
+			break;
+		case BUF_CMD_SIG_END:
+		case BUF_CMD_S_TXMASK:
+		case BUF_CMD_S_RXSENSOR:
+			datasize = 1;
+			break;
+		}
+	}
+
+	return datasize;
+}
+
+/* process ir data stored in driver buffer */
+static void fintek_process_rx_ir_data(struct fintek_dev *fintek)
+{
+	DEFINE_IR_RAW_EVENT(rawir);
+	u8 sample;
+	bool event = false;
+	int i;
+
+	for (i = 0; i < fintek->pkts; i++) {
+		sample = fintek->buf[i];
+		switch (fintek->parser_state) {
+		case CMD_HEADER:
+			fintek->cmd = sample;
+			if ((fintek->cmd == BUF_COMMAND_HEADER) ||
+			    ((fintek->cmd & BUF_COMMAND_MASK) !=
+			     BUF_PULSE_BIT)) {
+				fintek->parser_state = SUBCMD;
+				continue;
+			}
+			fintek->rem = (fintek->cmd & BUF_LEN_MASK);
+			fit_dbg("%s: rem: 0x%02x", __func__, fintek->rem);
+			if (fintek->rem)
+				fintek->parser_state = PARSE_IRDATA;
+			else
+				ir_raw_event_reset(fintek->rdev);
+			break;
+		case SUBCMD:
+			fintek->rem = fintek_cmdsize(fintek->cmd, sample);
+			fintek->parser_state = CMD_DATA;
+			break;
+		case CMD_DATA:
+			fintek->rem--;
+			break;
+		case PARSE_IRDATA:
+			fintek->rem--;
+			init_ir_raw_event(&rawir);
+			rawir.pulse = ((sample & BUF_PULSE_BIT) != 0);
+			rawir.duration = US_TO_NS((sample & BUF_SAMPLE_MASK)
+					  * CIR_SAMPLE_PERIOD);
+
+			fit_dbg("Storing %s with duration %d",
+				rawir.pulse ? "pulse" : "space",
+				rawir.duration);
+			if (ir_raw_event_store_with_filter(fintek->rdev,
+									&rawir))
+				event = true;
+			break;
+		}
+
+		if ((fintek->parser_state != CMD_HEADER) && !fintek->rem)
+			fintek->parser_state = CMD_HEADER;
+	}
+
+	fintek->pkts = 0;
+
+	if (event) {
+		fit_dbg("Calling ir_raw_event_handle");
+		ir_raw_event_handle(fintek->rdev);
+	}
+}
+
+/* copy data from hardware rx register into driver buffer */
+static void fintek_get_rx_ir_data(struct fintek_dev *fintek, u8 rx_irqs)
+{
+	unsigned long flags;
+	u8 sample, status;
+
+	spin_lock_irqsave(&fintek->fintek_lock, flags);
+
+	/*
+	 * We must read data from CIR_RX_DATA until the hardware IR buffer
+	 * is empty and clears the RX_TIMEOUT and/or RX_RECEIVE flags in
+	 * the CIR_STATUS register
+	 */
+	do {
+		sample = fintek_cir_reg_read(fintek, CIR_RX_DATA);
+		fit_dbg("%s: sample: 0x%02x", __func__, sample);
+
+		fintek->buf[fintek->pkts] = sample;
+		fintek->pkts++;
+
+		status = fintek_cir_reg_read(fintek, CIR_STATUS);
+		if (!(status & CIR_STATUS_IRQ_EN))
+			break;
+	} while (status & rx_irqs);
+
+	fintek_process_rx_ir_data(fintek);
+
+	spin_unlock_irqrestore(&fintek->fintek_lock, flags);
+}
+
+static void fintek_cir_log_irqs(u8 status)
+{
+	fit_pr(KERN_INFO, "IRQ 0x%02x:%s%s%s%s%s", status,
+		status & CIR_STATUS_IRQ_EN	? " IRQEN"	: "",
+		status & CIR_STATUS_TX_FINISH	? " TXF"	: "",
+		status & CIR_STATUS_TX_UNDERRUN	? " TXU"	: "",
+		status & CIR_STATUS_RX_TIMEOUT	? " RXTO"	: "",
+		status & CIR_STATUS_RX_RECEIVE	? " RXOK"	: "");
+}
+
+/* interrupt service routine for incoming and outgoing CIR data */
+static irqreturn_t fintek_cir_isr(int irq, void *data)
+{
+	struct fintek_dev *fintek = data;
+	u8 status, rx_irqs;
+
+	fit_dbg_verbose("%s firing", __func__);
+
+	fintek_config_mode_enable(fintek);
+	fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
+	fintek_config_mode_disable(fintek);
+
+	/*
+	 * Get IR Status register contents. Write 1 to ack/clear
+	 *
+	 * bit: reg name    - description
+	 *   3: TX_FINISH   - TX is finished
+	 *   2: TX_UNDERRUN - TX underrun
+	 *   1: RX_TIMEOUT  - RX data timeout
+	 *   0: RX_RECEIVE  - RX data received
+	 */
+	status = fintek_cir_reg_read(fintek, CIR_STATUS);
+	if (!(status & CIR_STATUS_IRQ_MASK) || status == 0xff) {
+		fit_dbg_verbose("%s exiting, IRSTS 0x%02x", __func__, status);
+		fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);
+		return IRQ_RETVAL(IRQ_NONE);
+	}
+
+	if (debug)
+		fintek_cir_log_irqs(status);
+
+	rx_irqs = status & (CIR_STATUS_RX_RECEIVE | CIR_STATUS_RX_TIMEOUT);
+	if (rx_irqs)
+		fintek_get_rx_ir_data(fintek, rx_irqs);
+
+	/* ack/clear all irq flags we've got */
+	fintek_cir_reg_write(fintek, status, CIR_STATUS);
+
+	fit_dbg_verbose("%s done", __func__);
+	return IRQ_RETVAL(IRQ_HANDLED);
+}
+
+static void fintek_enable_cir(struct fintek_dev *fintek)
+{
+	/* set IRQ enabled */
+	fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_EN, CIR_STATUS);
+
+	fintek_config_mode_enable(fintek);
+
+	/* enable the CIR logical device */
+	fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
+	fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);
+
+	fintek_config_mode_disable(fintek);
+
+	/* clear all pending interrupts */
+	fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);
+
+	/* enable interrupts */
+	fintek_enable_cir_irq(fintek);
+}
+
+static void fintek_disable_cir(struct fintek_dev *fintek)
+{
+	fintek_config_mode_enable(fintek);
+
+	/* disable the CIR logical device */
+	fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
+	fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN);
+
+	fintek_config_mode_disable(fintek);
+}
+
+static int fintek_open(struct rc_dev *dev)
+{
+	struct fintek_dev *fintek = dev->priv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&fintek->fintek_lock, flags);
+	fintek_enable_cir(fintek);
+	spin_unlock_irqrestore(&fintek->fintek_lock, flags);
+
+	return 0;
+}
+
+static void fintek_close(struct rc_dev *dev)
+{
+	struct fintek_dev *fintek = dev->priv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&fintek->fintek_lock, flags);
+	fintek_disable_cir(fintek);
+	spin_unlock_irqrestore(&fintek->fintek_lock, flags);
+}
+
+/* Allocate memory, probe hardware, and initialize everything */
+static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
+{
+	struct fintek_dev *fintek;
+	struct rc_dev *rdev;
+	int ret = -ENOMEM;
+
+	fintek = kzalloc(sizeof(struct fintek_dev), GFP_KERNEL);
+	if (!fintek)
+		return ret;
+
+	/* input device for IR remote (and tx) */
+	rdev = rc_allocate_device();
+	if (!rdev)
+		goto exit_free_dev_rdev;
+
+	ret = -ENODEV;
+	/* validate pnp resources */
+	if (!pnp_port_valid(pdev, 0)) {
+		dev_err(&pdev->dev, "IR PNP Port not valid!\n");
+		goto exit_free_dev_rdev;
+	}
+
+	if (!pnp_irq_valid(pdev, 0)) {
+		dev_err(&pdev->dev, "IR PNP IRQ not valid!\n");
+		goto exit_free_dev_rdev;
+	}
+
+	fintek->cir_addr = pnp_port_start(pdev, 0);
+	fintek->cir_irq  = pnp_irq(pdev, 0);
+	fintek->cir_port_len = pnp_port_len(pdev, 0);
+
+	fintek->cr_ip = CR_INDEX_PORT;
+	fintek->cr_dp = CR_DATA_PORT;
+
+	spin_lock_init(&fintek->fintek_lock);
+
+	pnp_set_drvdata(pdev, fintek);
+	fintek->pdev = pdev;
+
+	ret = fintek_hw_detect(fintek);
+	if (ret)
+		goto exit_free_dev_rdev;
+
+	/* Initialize CIR & CIR Wake Logical Devices */
+	fintek_config_mode_enable(fintek);
+	fintek_cir_ldev_init(fintek);
+	fintek_config_mode_disable(fintek);
+
+	/* Initialize CIR & CIR Wake Config Registers */
+	fintek_cir_regs_init(fintek);
+
+	/* Set up the rc device */
+	rdev->priv = fintek;
+	rdev->driver_type = RC_DRIVER_IR_RAW;
+	rdev->allowed_protocols = RC_BIT_ALL;
+	rdev->open = fintek_open;
+	rdev->close = fintek_close;
+	rdev->input_name = FINTEK_DESCRIPTION;
+	rdev->input_phys = "fintek/cir0";
+	rdev->input_id.bustype = BUS_HOST;
+	rdev->input_id.vendor = VENDOR_ID_FINTEK;
+	rdev->input_id.product = fintek->chip_major;
+	rdev->input_id.version = fintek->chip_minor;
+	rdev->dev.parent = &pdev->dev;
+	rdev->driver_name = FINTEK_DRIVER_NAME;
+	rdev->map_name = RC_MAP_RC6_MCE;
+	rdev->timeout = US_TO_NS(1000);
+	/* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */
+	rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD);
+
+	fintek->rdev = rdev;
+
+	ret = -EBUSY;
+	/* now claim resources */
+	if (!request_region(fintek->cir_addr,
+			    fintek->cir_port_len, FINTEK_DRIVER_NAME))
+		goto exit_free_dev_rdev;
+
+	if (request_irq(fintek->cir_irq, fintek_cir_isr, IRQF_SHARED,
+			FINTEK_DRIVER_NAME, (void *)fintek))
+		goto exit_free_cir_addr;
+
+	ret = rc_register_device(rdev);
+	if (ret)
+		goto exit_free_irq;
+
+	device_init_wakeup(&pdev->dev, true);
+
+	fit_pr(KERN_NOTICE, "driver has been successfully loaded\n");
+	if (debug)
+		cir_dump_regs(fintek);
+
+	return 0;
+
+exit_free_irq:
+	free_irq(fintek->cir_irq, fintek);
+exit_free_cir_addr:
+	release_region(fintek->cir_addr, fintek->cir_port_len);
+exit_free_dev_rdev:
+	rc_free_device(rdev);
+	kfree(fintek);
+
+	return ret;
+}
+
+static void fintek_remove(struct pnp_dev *pdev)
+{
+	struct fintek_dev *fintek = pnp_get_drvdata(pdev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&fintek->fintek_lock, flags);
+	/* disable CIR */
+	fintek_disable_cir(fintek);
+	fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);
+	/* enable CIR Wake (for IR power-on) */
+	fintek_enable_wake(fintek);
+	spin_unlock_irqrestore(&fintek->fintek_lock, flags);
+
+	/* free resources */
+	free_irq(fintek->cir_irq, fintek);
+	release_region(fintek->cir_addr, fintek->cir_port_len);
+
+	rc_unregister_device(fintek->rdev);
+
+	kfree(fintek);
+}
+
+static int fintek_suspend(struct pnp_dev *pdev, pm_message_t state)
+{
+	struct fintek_dev *fintek = pnp_get_drvdata(pdev);
+	unsigned long flags;
+
+	fit_dbg("%s called", __func__);
+
+	spin_lock_irqsave(&fintek->fintek_lock, flags);
+
+	/* disable all CIR interrupts */
+	fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);
+
+	spin_unlock_irqrestore(&fintek->fintek_lock, flags);
+
+	fintek_config_mode_enable(fintek);
+
+	/* disable cir logical dev */
+	fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
+	fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN);
+
+	fintek_config_mode_disable(fintek);
+
+	/* make sure wake is enabled */
+	fintek_enable_wake(fintek);
+
+	return 0;
+}
+
+static int fintek_resume(struct pnp_dev *pdev)
+{
+	struct fintek_dev *fintek = pnp_get_drvdata(pdev);
+
+	fit_dbg("%s called", __func__);
+
+	/* open interrupt */
+	fintek_enable_cir_irq(fintek);
+
+	/* Enable CIR logical device */
+	fintek_config_mode_enable(fintek);
+	fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
+	fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);
+
+	fintek_config_mode_disable(fintek);
+
+	fintek_cir_regs_init(fintek);
+
+	return 0;
+}
+
+static void fintek_shutdown(struct pnp_dev *pdev)
+{
+	struct fintek_dev *fintek = pnp_get_drvdata(pdev);
+	fintek_enable_wake(fintek);
+}
+
+static const struct pnp_device_id fintek_ids[] = {
+	{ "FIT0002", 0 },   /* CIR */
+	{ "", 0 },
+};
+
+static struct pnp_driver fintek_driver = {
+	.name		= FINTEK_DRIVER_NAME,
+	.id_table	= fintek_ids,
+	.flags		= PNP_DRIVER_RES_DO_NOT_CHANGE,
+	.probe		= fintek_probe,
+	.remove		= fintek_remove,
+	.suspend	= fintek_suspend,
+	.resume		= fintek_resume,
+	.shutdown	= fintek_shutdown,
+};
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging output");
+
+MODULE_DEVICE_TABLE(pnp, fintek_ids);
+MODULE_DESCRIPTION(FINTEK_DESCRIPTION " driver");
+
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
+MODULE_LICENSE("GPL");
+
+module_pnp_driver(fintek_driver);
diff --git a/drivers/media/rc/fintek-cir.h b/drivers/media/rc/fintek-cir.h
new file mode 100644
index 0000000..b698f3d
--- /dev/null
+++ b/drivers/media/rc/fintek-cir.h
@@ -0,0 +1,245 @@
+/*
+ * Driver for Feature Integration Technology Inc. (aka Fintek) LPC CIR
+ *
+ * Copyright (C) 2011 Jarod Wilson <jarod@redhat.com>
+ *
+ * Special thanks to Fintek for providing hardware and spec sheets.
+ * This driver is based upon the nuvoton, ite and ene drivers for
+ * similar hardware.
+ *
+ * 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/spinlock.h>
+#include <linux/ioctl.h>
+
+/* platform driver name to register */
+#define FINTEK_DRIVER_NAME	"fintek-cir"
+#define FINTEK_DESCRIPTION	"Fintek LPC SuperIO Consumer IR Transceiver"
+#define VENDOR_ID_FINTEK	0x1934
+
+
+/* debugging module parameter */
+static int debug;
+
+#define fit_pr(level, text, ...) \
+	printk(level KBUILD_MODNAME ": " text, ## __VA_ARGS__)
+
+#define fit_dbg(text, ...) \
+	if (debug) \
+		printk(KERN_DEBUG \
+			KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__)
+
+#define fit_dbg_verbose(text, ...) \
+	if (debug > 1) \
+		printk(KERN_DEBUG \
+			KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__)
+
+#define fit_dbg_wake(text, ...) \
+	if (debug > 2) \
+		printk(KERN_DEBUG \
+			KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__)
+
+
+#define TX_BUF_LEN 256
+#define RX_BUF_LEN 32
+
+struct fintek_dev {
+	struct pnp_dev *pdev;
+	struct rc_dev *rdev;
+
+	spinlock_t fintek_lock;
+
+	/* for rx */
+	u8 buf[RX_BUF_LEN];
+	unsigned int pkts;
+
+	struct {
+		spinlock_t lock;
+		u8 buf[TX_BUF_LEN];
+		unsigned int buf_count;
+		unsigned int cur_buf_num;
+		wait_queue_head_t queue;
+	} tx;
+
+	/* Config register index/data port pair */
+	u32 cr_ip;
+	u32 cr_dp;
+
+	/* hardware I/O settings */
+	unsigned long cir_addr;
+	int cir_irq;
+	int cir_port_len;
+
+	/* hardware id */
+	u8 chip_major;
+	u8 chip_minor;
+	u16 chip_vendor;
+	u8 logical_dev_cir;
+
+	/* hardware features */
+	bool hw_learning_capable;
+	bool hw_tx_capable;
+
+	/* rx settings */
+	bool learning_enabled;
+	bool carrier_detect_enabled;
+
+	enum {
+		CMD_HEADER = 0,
+		SUBCMD,
+		CMD_DATA,
+		PARSE_IRDATA,
+	} parser_state;
+
+	u8 cmd, rem;
+
+	/* carrier period = 1 / frequency */
+	u32 carrier;
+};
+
+/* buffer packet constants, largely identical to mceusb.c */
+#define BUF_PULSE_BIT		0x80
+#define BUF_LEN_MASK		0x1f
+#define BUF_SAMPLE_MASK		0x7f
+
+#define BUF_COMMAND_HEADER	0x9f
+#define BUF_COMMAND_MASK	0xe0
+#define BUF_COMMAND_NULL	0x00
+#define BUF_HW_CMD_HEADER	0xff
+#define BUF_CMD_G_REVISION	0x0b
+#define BUF_CMD_S_CARRIER	0x06
+#define BUF_CMD_S_TIMEOUT	0x0c
+#define BUF_CMD_SIG_END		0x01
+#define BUF_CMD_S_TXMASK	0x08
+#define BUF_CMD_S_RXSENSOR	0x14
+#define BUF_RSP_PULSE_COUNT	0x15
+
+#define CIR_SAMPLE_PERIOD	50
+
+/*
+ * Configuration Register:
+ *  Index Port
+ *  Data Port
+ */
+#define CR_INDEX_PORT		0x2e
+#define CR_DATA_PORT		0x2f
+
+/* Possible alternate values, depends on how the chip is wired */
+#define CR_INDEX_PORT2		0x4e
+#define CR_DATA_PORT2		0x4f
+
+/*
+ * GCR_CONFIG_PORT_SEL bit 4 specifies which Index Port value is
+ * active. 1 = 0x4e, 0 = 0x2e
+ */
+#define PORT_SEL_PORT_4E_EN	0x10
+
+/* Extended Function Mode enable/disable magic values */
+#define CONFIG_REG_ENABLE	0x87
+#define CONFIG_REG_DISABLE	0xaa
+
+/* Chip IDs found in CR_CHIP_ID_{HI,LO} */
+#define CHIP_ID_HIGH_F71809U	0x04
+#define CHIP_ID_LOW_F71809U	0x08
+
+/*
+ * Global control regs we need to care about:
+ *      Global Control                  def.
+ *      Register name           addr    val. */
+#define GCR_SOFTWARE_RESET	0x02 /* 0x00 */
+#define GCR_LOGICAL_DEV_NO	0x07 /* 0x00 */
+#define GCR_CHIP_ID_HI		0x20 /* 0x04 */
+#define GCR_CHIP_ID_LO		0x21 /* 0x08 */
+#define GCR_VENDOR_ID_HI	0x23 /* 0x19 */
+#define GCR_VENDOR_ID_LO	0x24 /* 0x34 */
+#define GCR_CONFIG_PORT_SEL	0x25 /* 0x01 */
+#define GCR_KBMOUSE_WAKEUP	0x27
+
+#define LOGICAL_DEV_DISABLE	0x00
+#define LOGICAL_DEV_ENABLE	0x01
+
+/* Logical device number of the CIR function */
+#define LOGICAL_DEV_CIR_REV1	0x05
+#define LOGICAL_DEV_CIR_REV2	0x08
+
+/* CIR Logical Device (LDN 0x08) config registers */
+#define CIR_CR_COMMAND_INDEX	0x04
+#define CIR_CR_IRCS		0x05 /* Before host writes command to IR, host
+					must set to 1. When host finshes write
+					command to IR, host must clear to 0. */
+#define CIR_CR_COMMAND_DATA	0x06 /* Host read or write comand data */
+#define CIR_CR_CLASS		0x07 /* 0xff = rx-only, 0x66 = rx + 2 tx,
+					0x33 = rx + 1 tx */
+#define CIR_CR_DEV_EN		0x30 /* bit0 = 1 enables CIR */
+#define CIR_CR_BASE_ADDR_HI	0x60 /* MSB of CIR IO base addr */
+#define CIR_CR_BASE_ADDR_LO	0x61 /* LSB of CIR IO base addr */
+#define CIR_CR_IRQ_SEL		0x70 /* bits3-0 store CIR IRQ */
+#define CIR_CR_PSOUT_STATUS	0xf1
+#define CIR_CR_WAKE_KEY3_ADDR	0xf8
+#define CIR_CR_WAKE_KEY3_CODE	0xf9
+#define CIR_CR_WAKE_KEY3_DC	0xfa
+#define CIR_CR_WAKE_CONTROL	0xfb
+#define CIR_CR_WAKE_KEY12_ADDR	0xfc
+#define CIR_CR_WAKE_KEY4_ADDR	0xfd
+#define CIR_CR_WAKE_KEY5_ADDR	0xfe
+
+#define CLASS_RX_ONLY		0xff
+#define CLASS_RX_2TX		0x66
+#define CLASS_RX_1TX		0x33
+
+/* CIR device registers */
+#define CIR_STATUS		0x00
+#define CIR_RX_DATA		0x01
+#define CIR_TX_CONTROL		0x02
+#define CIR_TX_DATA		0x03
+#define CIR_CONTROL		0x04
+
+/* Bits to enable CIR wake */
+#define LOGICAL_DEV_ACPI	0x01
+#define LDEV_ACPI_WAKE_EN_REG	0xe8
+#define ACPI_WAKE_EN_CIR_BIT	0x04
+
+#define LDEV_ACPI_PME_EN_REG	0xf0
+#define LDEV_ACPI_PME_CLR_REG	0xf1
+#define ACPI_PME_CIR_BIT	0x02
+
+#define LDEV_ACPI_STATE_REG	0xf4
+#define ACPI_STATE_CIR_BIT	0x20
+
+/*
+ * CIR status register (0x00):
+ *   7 - CIR_IRQ_EN (1 = enable CIR IRQ, 0 = disable)
+ *   3 - TX_FINISH (1 when TX finished, write 1 to clear)
+ *   2 - TX_UNDERRUN (1 on TX underrun, write 1 to clear)
+ *   1 - RX_TIMEOUT (1 on RX timeout, write 1 to clear)
+ *   0 - RX_RECEIVE (1 on RX receive, write 1 to clear)
+ */
+#define CIR_STATUS_IRQ_EN	0x80
+#define CIR_STATUS_TX_FINISH	0x08
+#define CIR_STATUS_TX_UNDERRUN	0x04
+#define CIR_STATUS_RX_TIMEOUT	0x02
+#define CIR_STATUS_RX_RECEIVE	0x01
+#define CIR_STATUS_IRQ_MASK	0x0f
+
+/*
+ * CIR TX control register (0x02):
+ *   7 - TX_START (1 to indicate TX start, auto-cleared when done)
+ *   6 - TX_END (1 to indicate TX data written to TX fifo)
+ */
+#define CIR_TX_CONTROL_TX_START	0x80
+#define CIR_TX_CONTROL_TX_END	0x40
+
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
new file mode 100644
index 0000000..7dbc9ca
--- /dev/null
+++ b/drivers/media/rc/gpio-ir-recv.c
@@ -0,0 +1,252 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <media/rc-core.h>
+#include <media/gpio-ir-recv.h>
+
+#define GPIO_IR_DRIVER_NAME	"gpio-rc-recv"
+#define GPIO_IR_DEVICE_NAME	"gpio_ir_recv"
+
+struct gpio_rc_dev {
+	struct rc_dev *rcdev;
+	int gpio_nr;
+	bool active_low;
+};
+
+#ifdef CONFIG_OF
+/*
+ * Translate OpenFirmware node properties into platform_data
+ */
+static int gpio_ir_recv_get_devtree_pdata(struct device *dev,
+				  struct gpio_ir_recv_platform_data *pdata)
+{
+	struct device_node *np = dev->of_node;
+	enum of_gpio_flags flags;
+	int gpio;
+
+	gpio = of_get_gpio_flags(np, 0, &flags);
+	if (gpio < 0) {
+		if (gpio != -EPROBE_DEFER)
+			dev_err(dev, "Failed to get gpio flags (%d)\n", gpio);
+		return gpio;
+	}
+
+	pdata->gpio_nr = gpio;
+	pdata->active_low = (flags & OF_GPIO_ACTIVE_LOW);
+	/* probe() takes care of map_name == NULL or allowed_protos == 0 */
+	pdata->map_name = of_get_property(np, "linux,rc-map-name", NULL);
+	pdata->allowed_protos = 0;
+
+	return 0;
+}
+
+static const struct of_device_id gpio_ir_recv_of_match[] = {
+	{ .compatible = "gpio-ir-receiver", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, gpio_ir_recv_of_match);
+
+#else /* !CONFIG_OF */
+
+#define gpio_ir_recv_get_devtree_pdata(dev, pdata)	(-ENOSYS)
+
+#endif
+
+static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id)
+{
+	struct gpio_rc_dev *gpio_dev = dev_id;
+	int gval;
+	int rc = 0;
+	enum raw_event_type type = IR_SPACE;
+
+	gval = gpio_get_value(gpio_dev->gpio_nr);
+
+	if (gval < 0)
+		goto err_get_value;
+
+	if (gpio_dev->active_low)
+		gval = !gval;
+
+	if (gval == 1)
+		type = IR_PULSE;
+
+	rc = ir_raw_event_store_edge(gpio_dev->rcdev, type);
+	if (rc < 0)
+		goto err_get_value;
+
+	ir_raw_event_handle(gpio_dev->rcdev);
+
+err_get_value:
+	return IRQ_HANDLED;
+}
+
+static int gpio_ir_recv_probe(struct platform_device *pdev)
+{
+	struct gpio_rc_dev *gpio_dev;
+	struct rc_dev *rcdev;
+	const struct gpio_ir_recv_platform_data *pdata =
+					pdev->dev.platform_data;
+	int rc;
+
+	if (pdev->dev.of_node) {
+		struct gpio_ir_recv_platform_data *dtpdata =
+			devm_kzalloc(&pdev->dev, sizeof(*dtpdata), GFP_KERNEL);
+		if (!dtpdata)
+			return -ENOMEM;
+		rc = gpio_ir_recv_get_devtree_pdata(&pdev->dev, dtpdata);
+		if (rc)
+			return rc;
+		pdata = dtpdata;
+	}
+
+	if (!pdata)
+		return -EINVAL;
+
+	if (pdata->gpio_nr < 0)
+		return -EINVAL;
+
+	gpio_dev = kzalloc(sizeof(struct gpio_rc_dev), GFP_KERNEL);
+	if (!gpio_dev)
+		return -ENOMEM;
+
+	rcdev = rc_allocate_device();
+	if (!rcdev) {
+		rc = -ENOMEM;
+		goto err_allocate_device;
+	}
+
+	rcdev->priv = gpio_dev;
+	rcdev->driver_type = RC_DRIVER_IR_RAW;
+	rcdev->input_name = GPIO_IR_DEVICE_NAME;
+	rcdev->input_phys = GPIO_IR_DEVICE_NAME "/input0";
+	rcdev->input_id.bustype = BUS_HOST;
+	rcdev->input_id.vendor = 0x0001;
+	rcdev->input_id.product = 0x0001;
+	rcdev->input_id.version = 0x0100;
+	rcdev->dev.parent = &pdev->dev;
+	rcdev->driver_name = GPIO_IR_DRIVER_NAME;
+	if (pdata->allowed_protos)
+		rcdev->allowed_protocols = pdata->allowed_protos;
+	else
+		rcdev->allowed_protocols = RC_BIT_ALL;
+	rcdev->map_name = pdata->map_name ?: RC_MAP_EMPTY;
+
+	gpio_dev->rcdev = rcdev;
+	gpio_dev->gpio_nr = pdata->gpio_nr;
+	gpio_dev->active_low = pdata->active_low;
+
+	rc = gpio_request(pdata->gpio_nr, "gpio-ir-recv");
+	if (rc < 0)
+		goto err_gpio_request;
+	rc  = gpio_direction_input(pdata->gpio_nr);
+	if (rc < 0)
+		goto err_gpio_direction_input;
+
+	rc = rc_register_device(rcdev);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "failed to register rc device\n");
+		goto err_register_rc_device;
+	}
+
+	platform_set_drvdata(pdev, gpio_dev);
+
+	rc = request_any_context_irq(gpio_to_irq(pdata->gpio_nr),
+				gpio_ir_recv_irq,
+			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+					"gpio-ir-recv-irq", gpio_dev);
+	if (rc < 0)
+		goto err_request_irq;
+
+	return 0;
+
+err_request_irq:
+	rc_unregister_device(rcdev);
+	rcdev = NULL;
+err_register_rc_device:
+err_gpio_direction_input:
+	gpio_free(pdata->gpio_nr);
+err_gpio_request:
+	rc_free_device(rcdev);
+err_allocate_device:
+	kfree(gpio_dev);
+	return rc;
+}
+
+static int gpio_ir_recv_remove(struct platform_device *pdev)
+{
+	struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
+
+	free_irq(gpio_to_irq(gpio_dev->gpio_nr), gpio_dev);
+	rc_unregister_device(gpio_dev->rcdev);
+	gpio_free(gpio_dev->gpio_nr);
+	kfree(gpio_dev);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int gpio_ir_recv_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(gpio_to_irq(gpio_dev->gpio_nr));
+	else
+		disable_irq(gpio_to_irq(gpio_dev->gpio_nr));
+
+	return 0;
+}
+
+static int gpio_ir_recv_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(gpio_to_irq(gpio_dev->gpio_nr));
+	else
+		enable_irq(gpio_to_irq(gpio_dev->gpio_nr));
+
+	return 0;
+}
+
+static const struct dev_pm_ops gpio_ir_recv_pm_ops = {
+	.suspend        = gpio_ir_recv_suspend,
+	.resume         = gpio_ir_recv_resume,
+};
+#endif
+
+static struct platform_driver gpio_ir_recv_driver = {
+	.probe  = gpio_ir_recv_probe,
+	.remove = gpio_ir_recv_remove,
+	.driver = {
+		.name   = GPIO_IR_DRIVER_NAME,
+		.of_match_table = of_match_ptr(gpio_ir_recv_of_match),
+#ifdef CONFIG_PM
+		.pm	= &gpio_ir_recv_pm_ops,
+#endif
+	},
+};
+module_platform_driver(gpio_ir_recv_driver);
+
+MODULE_DESCRIPTION("GPIO IR Receiver driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/rc/igorplugusb.c b/drivers/media/rc/igorplugusb.c
new file mode 100644
index 0000000..b36e515
--- /dev/null
+++ b/drivers/media/rc/igorplugusb.c
@@ -0,0 +1,261 @@
+/*
+ * IgorPlug-USB IR Receiver
+ *
+ * Copyright (C) 2014 Sean Young <sean@mess.org>
+ *
+ * Supports the standard homebrew IgorPlugUSB receiver with Igor's firmware.
+ * See http://www.cesko.host.sk/IgorPlugUSB/IgorPlug-USB%20(AVR)_eng.htm
+ *
+ * Based on the lirc_igorplugusb.c driver:
+ *	Copyright (C) 2004 Jan M. Hochstein
+ *	<hochstein@algo.informatik.tu-darmstadt.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <media/rc-core.h>
+
+#define DRIVER_DESC		"IgorPlug-USB IR Receiver"
+#define DRIVER_NAME		"igorplugusb"
+
+#define HEADERLEN		3
+#define BUFLEN			36
+#define MAX_PACKET		(HEADERLEN + BUFLEN)
+
+#define SET_INFRABUFFER_EMPTY	1
+#define GET_INFRACODE		2
+
+
+struct igorplugusb {
+	struct rc_dev *rc;
+	struct device *dev;
+
+	struct urb *urb;
+	struct usb_ctrlrequest request;
+
+	struct timer_list timer;
+
+	uint8_t buf_in[MAX_PACKET];
+
+	char phys[64];
+};
+
+static void igorplugusb_cmd(struct igorplugusb *ir, int cmd);
+
+static void igorplugusb_irdata(struct igorplugusb *ir, unsigned len)
+{
+	DEFINE_IR_RAW_EVENT(rawir);
+	unsigned i, start, overflow;
+
+	dev_dbg(ir->dev, "irdata: %*ph (len=%u)", len, ir->buf_in, len);
+
+	/*
+	 * If more than 36 pulses and spaces follow each other, the igorplugusb
+	 * overwrites its buffer from the beginning. The overflow value is the
+	 * last offset which was not overwritten. Everything from this offset
+	 * onwards occurred before everything until this offset.
+	 */
+	overflow = ir->buf_in[2];
+	i = start = overflow + HEADERLEN;
+
+	if (start >= len) {
+		dev_err(ir->dev, "receive overflow invalid: %u", overflow);
+	} else {
+		if (overflow > 0)
+			dev_warn(ir->dev, "receive overflow, at least %u lost",
+								overflow);
+
+		do {
+			rawir.duration = ir->buf_in[i] * 85333;
+			rawir.pulse = i & 1;
+
+			ir_raw_event_store_with_filter(ir->rc, &rawir);
+
+			if (++i == len)
+				i = HEADERLEN;
+		} while (i != start);
+
+		/* add a trailing space */
+		rawir.duration = ir->rc->timeout;
+		rawir.pulse = false;
+		ir_raw_event_store_with_filter(ir->rc, &rawir);
+
+		ir_raw_event_handle(ir->rc);
+	}
+
+	igorplugusb_cmd(ir, SET_INFRABUFFER_EMPTY);
+}
+
+static void igorplugusb_callback(struct urb *urb)
+{
+	struct usb_ctrlrequest *req;
+	struct igorplugusb *ir = urb->context;
+
+	req = (struct usb_ctrlrequest *)urb->setup_packet;
+
+	switch (urb->status) {
+	case 0:
+		if (req->bRequest == GET_INFRACODE &&
+					urb->actual_length > HEADERLEN)
+			igorplugusb_irdata(ir, urb->actual_length);
+		else /* request IR */
+			mod_timer(&ir->timer, jiffies + msecs_to_jiffies(50));
+		break;
+	case -EPROTO:
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		usb_unlink_urb(urb);
+		return;
+	default:
+		dev_warn(ir->dev, "Error: urb status = %d\n", urb->status);
+		igorplugusb_cmd(ir, SET_INFRABUFFER_EMPTY);
+		break;
+	}
+}
+
+static void igorplugusb_cmd(struct igorplugusb *ir, int cmd)
+{
+	int ret;
+
+	ir->request.bRequest = cmd;
+	ir->urb->transfer_flags = 0;
+	ret = usb_submit_urb(ir->urb, GFP_ATOMIC);
+	if (ret)
+		dev_err(ir->dev, "submit urb failed: %d", ret);
+}
+
+static void igorplugusb_timer(unsigned long data)
+{
+	struct igorplugusb *ir = (struct igorplugusb *)data;
+
+	igorplugusb_cmd(ir, GET_INFRACODE);
+}
+
+static int igorplugusb_probe(struct usb_interface *intf,
+					const struct usb_device_id *id)
+{
+	struct usb_device *udev;
+	struct usb_host_interface *idesc;
+	struct usb_endpoint_descriptor *ep;
+	struct igorplugusb *ir;
+	struct rc_dev *rc;
+	int ret;
+
+	udev = interface_to_usbdev(intf);
+	idesc = intf->cur_altsetting;
+
+	if (idesc->desc.bNumEndpoints != 1) {
+		dev_err(&intf->dev, "incorrect number of endpoints");
+		return -ENODEV;
+	}
+
+	ep = &idesc->endpoint[0].desc;
+	if (!usb_endpoint_dir_in(ep) || !usb_endpoint_xfer_control(ep)) {
+		dev_err(&intf->dev, "endpoint incorrect");
+		return -ENODEV;
+	}
+
+	ir = devm_kzalloc(&intf->dev, sizeof(*ir), GFP_KERNEL);
+	if (!ir)
+		return -ENOMEM;
+
+	ir->dev = &intf->dev;
+
+	setup_timer(&ir->timer, igorplugusb_timer, (unsigned long)ir);
+
+	ir->request.bRequest = GET_INFRACODE;
+	ir->request.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN;
+	ir->request.wLength = cpu_to_le16(sizeof(ir->buf_in));
+
+	ir->urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!ir->urb)
+		return -ENOMEM;
+
+	usb_fill_control_urb(ir->urb, udev,
+		usb_rcvctrlpipe(udev, 0), (uint8_t *)&ir->request,
+		ir->buf_in, sizeof(ir->buf_in), igorplugusb_callback, ir);
+
+	usb_make_path(udev, ir->phys, sizeof(ir->phys));
+
+	rc = rc_allocate_device();
+	rc->input_name = DRIVER_DESC;
+	rc->input_phys = ir->phys;
+	usb_to_input_id(udev, &rc->input_id);
+	rc->dev.parent = &intf->dev;
+	rc->driver_type = RC_DRIVER_IR_RAW;
+	/*
+	 * This device can only store 36 pulses + spaces, which is not enough
+	 * for the NEC protocol and many others.
+	 */
+	rc->allowed_protocols = RC_BIT_ALL & ~(RC_BIT_NEC | RC_BIT_RC6_6A_20 |
+			RC_BIT_RC6_6A_24 | RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE |
+			RC_BIT_SONY20 | RC_BIT_MCE_KBD | RC_BIT_SANYO);
+
+	rc->priv = ir;
+	rc->driver_name = DRIVER_NAME;
+	rc->map_name = RC_MAP_HAUPPAUGE;
+	rc->timeout = MS_TO_NS(100);
+	rc->rx_resolution = 85333;
+
+	ir->rc = rc;
+	ret = rc_register_device(rc);
+	if (ret) {
+		dev_err(&intf->dev, "failed to register rc device: %d", ret);
+		rc_free_device(rc);
+		usb_free_urb(ir->urb);
+		return ret;
+	}
+
+	usb_set_intfdata(intf, ir);
+
+	igorplugusb_cmd(ir, SET_INFRABUFFER_EMPTY);
+
+	return 0;
+}
+
+static void igorplugusb_disconnect(struct usb_interface *intf)
+{
+	struct igorplugusb *ir = usb_get_intfdata(intf);
+
+	rc_unregister_device(ir->rc);
+	del_timer_sync(&ir->timer);
+	usb_set_intfdata(intf, NULL);
+	usb_kill_urb(ir->urb);
+	usb_free_urb(ir->urb);
+}
+
+static struct usb_device_id igorplugusb_table[] = {
+	/* Igor Plug USB (Atmel's Manufact. ID) */
+	{ USB_DEVICE(0x03eb, 0x0002) },
+	/* Fit PC2 Infrared Adapter */
+	{ USB_DEVICE(0x03eb, 0x21fe) },
+	/* Terminating entry */
+	{ }
+};
+
+static struct usb_driver igorplugusb_driver = {
+	.name =	DRIVER_NAME,
+	.probe = igorplugusb_probe,
+	.disconnect = igorplugusb_disconnect,
+	.id_table = igorplugusb_table
+};
+
+module_usb_driver(igorplugusb_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, igorplugusb_table);
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
new file mode 100644
index 0000000..ee60e17
--- /dev/null
+++ b/drivers/media/rc/iguanair.c
@@ -0,0 +1,618 @@
+/*
+ * IguanaWorks USB IR Transceiver support
+ *
+ * Copyright (C) 2012 Sean Young <sean@mess.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <media/rc-core.h>
+
+#define DRIVER_NAME "iguanair"
+#define BUF_SIZE 152
+
+struct iguanair {
+	struct rc_dev *rc;
+
+	struct device *dev;
+	struct usb_device *udev;
+
+	uint16_t version;
+	uint8_t bufsize;
+	uint8_t cycle_overhead;
+
+	struct mutex lock;
+
+	/* receiver support */
+	bool receiver_on;
+	dma_addr_t dma_in, dma_out;
+	uint8_t *buf_in;
+	struct urb *urb_in, *urb_out;
+	struct completion completion;
+
+	/* transmit support */
+	bool tx_overflow;
+	uint32_t carrier;
+	struct send_packet *packet;
+
+	char name[64];
+	char phys[64];
+};
+
+#define CMD_NOP			0x00
+#define CMD_GET_VERSION		0x01
+#define CMD_GET_BUFSIZE		0x11
+#define CMD_GET_FEATURES	0x10
+#define CMD_SEND		0x15
+#define CMD_EXECUTE		0x1f
+#define CMD_RX_OVERFLOW		0x31
+#define CMD_TX_OVERFLOW		0x32
+#define CMD_RECEIVER_ON		0x12
+#define CMD_RECEIVER_OFF	0x14
+
+#define DIR_IN			0xdc
+#define DIR_OUT			0xcd
+
+#define MAX_IN_PACKET		8u
+#define MAX_OUT_PACKET		(sizeof(struct send_packet) + BUF_SIZE)
+#define TIMEOUT			1000
+#define RX_RESOLUTION		21333
+
+struct packet {
+	uint16_t start;
+	uint8_t direction;
+	uint8_t cmd;
+};
+
+struct send_packet {
+	struct packet header;
+	uint8_t length;
+	uint8_t channels;
+	uint8_t busy7;
+	uint8_t busy4;
+	uint8_t payload[0];
+};
+
+static void process_ir_data(struct iguanair *ir, unsigned len)
+{
+	if (len >= 4 && ir->buf_in[0] == 0 && ir->buf_in[1] == 0) {
+		switch (ir->buf_in[3]) {
+		case CMD_GET_VERSION:
+			if (len == 6) {
+				ir->version = (ir->buf_in[5] << 8) |
+							ir->buf_in[4];
+				complete(&ir->completion);
+			}
+			break;
+		case CMD_GET_BUFSIZE:
+			if (len >= 5) {
+				ir->bufsize = ir->buf_in[4];
+				complete(&ir->completion);
+			}
+			break;
+		case CMD_GET_FEATURES:
+			if (len > 5) {
+				ir->cycle_overhead = ir->buf_in[5];
+				complete(&ir->completion);
+			}
+			break;
+		case CMD_TX_OVERFLOW:
+			ir->tx_overflow = true;
+		case CMD_RECEIVER_OFF:
+		case CMD_RECEIVER_ON:
+		case CMD_SEND:
+			complete(&ir->completion);
+			break;
+		case CMD_RX_OVERFLOW:
+			dev_warn(ir->dev, "receive overflow\n");
+			ir_raw_event_reset(ir->rc);
+			break;
+		default:
+			dev_warn(ir->dev, "control code %02x received\n",
+							ir->buf_in[3]);
+			break;
+		}
+	} else if (len >= 7) {
+		DEFINE_IR_RAW_EVENT(rawir);
+		unsigned i;
+		bool event = false;
+
+		init_ir_raw_event(&rawir);
+
+		for (i = 0; i < 7; i++) {
+			if (ir->buf_in[i] == 0x80) {
+				rawir.pulse = false;
+				rawir.duration = US_TO_NS(21845);
+			} else {
+				rawir.pulse = (ir->buf_in[i] & 0x80) == 0;
+				rawir.duration = ((ir->buf_in[i] & 0x7f) + 1) *
+								 RX_RESOLUTION;
+			}
+
+			if (ir_raw_event_store_with_filter(ir->rc, &rawir))
+				event = true;
+		}
+
+		if (event)
+			ir_raw_event_handle(ir->rc);
+	}
+}
+
+static void iguanair_rx(struct urb *urb)
+{
+	struct iguanair *ir;
+	int rc;
+
+	if (!urb)
+		return;
+
+	ir = urb->context;
+	if (!ir) {
+		usb_unlink_urb(urb);
+		return;
+	}
+
+	switch (urb->status) {
+	case 0:
+		process_ir_data(ir, urb->actual_length);
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		usb_unlink_urb(urb);
+		return;
+	case -EPIPE:
+	default:
+		dev_dbg(ir->dev, "Error: urb status = %d\n", urb->status);
+		break;
+	}
+
+	rc = usb_submit_urb(urb, GFP_ATOMIC);
+	if (rc && rc != -ENODEV)
+		dev_warn(ir->dev, "failed to resubmit urb: %d\n", rc);
+}
+
+static void iguanair_irq_out(struct urb *urb)
+{
+	struct iguanair *ir = urb->context;
+
+	if (urb->status)
+		dev_dbg(ir->dev, "Error: out urb status = %d\n", urb->status);
+
+	/* if we sent an nop packet, do not expect a response */
+	if (urb->status == 0 && ir->packet->header.cmd == CMD_NOP)
+		complete(&ir->completion);
+}
+
+static int iguanair_send(struct iguanair *ir, unsigned size)
+{
+	int rc;
+
+	reinit_completion(&ir->completion);
+
+	ir->urb_out->transfer_buffer_length = size;
+	rc = usb_submit_urb(ir->urb_out, GFP_KERNEL);
+	if (rc)
+		return rc;
+
+	if (wait_for_completion_timeout(&ir->completion, TIMEOUT) == 0)
+		return -ETIMEDOUT;
+
+	return rc;
+}
+
+static int iguanair_get_features(struct iguanair *ir)
+{
+	int rc;
+
+	/*
+	 * On cold boot, the iguanair initializes on the first packet
+	 * received but does not process that packet. Send an empty
+	 * packet.
+	 */
+	ir->packet->header.start = 0;
+	ir->packet->header.direction = DIR_OUT;
+	ir->packet->header.cmd = CMD_NOP;
+	iguanair_send(ir, sizeof(ir->packet->header));
+
+	ir->packet->header.cmd = CMD_GET_VERSION;
+	rc = iguanair_send(ir, sizeof(ir->packet->header));
+	if (rc) {
+		dev_info(ir->dev, "failed to get version\n");
+		goto out;
+	}
+
+	if (ir->version < 0x205) {
+		dev_err(ir->dev, "firmware 0x%04x is too old\n", ir->version);
+		rc = -ENODEV;
+		goto out;
+	}
+
+	ir->bufsize = 150;
+	ir->cycle_overhead = 65;
+
+	ir->packet->header.cmd = CMD_GET_BUFSIZE;
+
+	rc = iguanair_send(ir, sizeof(ir->packet->header));
+	if (rc) {
+		dev_info(ir->dev, "failed to get buffer size\n");
+		goto out;
+	}
+
+	if (ir->bufsize > BUF_SIZE) {
+		dev_info(ir->dev, "buffer size %u larger than expected\n",
+								ir->bufsize);
+		ir->bufsize = BUF_SIZE;
+	}
+
+	ir->packet->header.cmd = CMD_GET_FEATURES;
+
+	rc = iguanair_send(ir, sizeof(ir->packet->header));
+	if (rc)
+		dev_info(ir->dev, "failed to get features\n");
+out:
+	return rc;
+}
+
+static int iguanair_receiver(struct iguanair *ir, bool enable)
+{
+	ir->packet->header.start = 0;
+	ir->packet->header.direction = DIR_OUT;
+	ir->packet->header.cmd = enable ? CMD_RECEIVER_ON : CMD_RECEIVER_OFF;
+
+	if (enable)
+		ir_raw_event_reset(ir->rc);
+
+	return iguanair_send(ir, sizeof(ir->packet->header));
+}
+
+/*
+ * The iguanair creates the carrier by busy spinning after each half period.
+ * This is counted in CPU cycles, with the CPU running at 24MHz. It is
+ * broken down into 7-cycles and 4-cyles delays, with a preference for
+ * 4-cycle delays, minus the overhead of the loop itself (cycle_overhead).
+ */
+static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier)
+{
+	struct iguanair *ir = dev->priv;
+
+	if (carrier < 25000 || carrier > 150000)
+		return -EINVAL;
+
+	mutex_lock(&ir->lock);
+
+	if (carrier != ir->carrier) {
+		uint32_t cycles, fours, sevens;
+
+		ir->carrier = carrier;
+
+		cycles = DIV_ROUND_CLOSEST(24000000, carrier * 2) -
+							ir->cycle_overhead;
+
+		/*
+		 * Calculate minimum number of 7 cycles needed so
+		 * we are left with a multiple of 4; so we want to have
+		 * (sevens * 7) & 3 == cycles & 3
+		 */
+		sevens = (4 - cycles) & 3;
+		fours = (cycles - sevens * 7) / 4;
+
+		/*
+		 * The firmware interprets these values as a relative offset
+		 * for a branch. Immediately following the branches, there
+		 * 4 instructions of 7 cycles (2 bytes each) and 110
+		 * instructions of 4 cycles (1 byte each). A relative branch
+		 * of 0 will execute all of them, branch further for less
+		 * cycle burning.
+		 */
+		ir->packet->busy7 = (4 - sevens) * 2;
+		ir->packet->busy4 = 110 - fours;
+	}
+
+	mutex_unlock(&ir->lock);
+
+	return carrier;
+}
+
+static int iguanair_set_tx_mask(struct rc_dev *dev, uint32_t mask)
+{
+	struct iguanair *ir = dev->priv;
+
+	if (mask > 15)
+		return 4;
+
+	mutex_lock(&ir->lock);
+	ir->packet->channels = mask << 4;
+	mutex_unlock(&ir->lock);
+
+	return 0;
+}
+
+static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
+{
+	struct iguanair *ir = dev->priv;
+	uint8_t space;
+	unsigned i, size, periods, bytes;
+	int rc;
+
+	mutex_lock(&ir->lock);
+
+	/* convert from us to carrier periods */
+	for (i = space = size = 0; i < count; i++) {
+		periods = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000);
+		bytes = DIV_ROUND_UP(periods, 127);
+		if (size + bytes > ir->bufsize) {
+			rc = -EINVAL;
+			goto out;
+		}
+		while (periods) {
+			unsigned p = min(periods, 127u);
+			ir->packet->payload[size++] = p | space;
+			periods -= p;
+		}
+		space ^= 0x80;
+	}
+
+	ir->packet->header.start = 0;
+	ir->packet->header.direction = DIR_OUT;
+	ir->packet->header.cmd = CMD_SEND;
+	ir->packet->length = size;
+
+	ir->tx_overflow = false;
+
+	rc = iguanair_send(ir, sizeof(*ir->packet) + size);
+
+	if (rc == 0 && ir->tx_overflow)
+		rc = -EOVERFLOW;
+
+out:
+	mutex_unlock(&ir->lock);
+
+	return rc ? rc : count;
+}
+
+static int iguanair_open(struct rc_dev *rdev)
+{
+	struct iguanair *ir = rdev->priv;
+	int rc;
+
+	mutex_lock(&ir->lock);
+
+	rc = iguanair_receiver(ir, true);
+	if (rc == 0)
+		ir->receiver_on = true;
+
+	mutex_unlock(&ir->lock);
+
+	return rc;
+}
+
+static void iguanair_close(struct rc_dev *rdev)
+{
+	struct iguanair *ir = rdev->priv;
+	int rc;
+
+	mutex_lock(&ir->lock);
+
+	rc = iguanair_receiver(ir, false);
+	ir->receiver_on = false;
+	if (rc && rc != -ENODEV)
+		dev_warn(ir->dev, "failed to disable receiver: %d\n", rc);
+
+	mutex_unlock(&ir->lock);
+}
+
+static int iguanair_probe(struct usb_interface *intf,
+			  const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct iguanair *ir;
+	struct rc_dev *rc;
+	int ret, pipein, pipeout;
+	struct usb_host_interface *idesc;
+
+	ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+	rc = rc_allocate_device();
+	if (!ir || !rc) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ir->buf_in = usb_alloc_coherent(udev, MAX_IN_PACKET, GFP_KERNEL,
+								&ir->dma_in);
+	ir->packet = usb_alloc_coherent(udev, MAX_OUT_PACKET, GFP_KERNEL,
+								&ir->dma_out);
+	ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
+	ir->urb_out = usb_alloc_urb(0, GFP_KERNEL);
+
+	if (!ir->buf_in || !ir->packet || !ir->urb_in || !ir->urb_out) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	idesc = intf->altsetting;
+
+	if (idesc->desc.bNumEndpoints < 2) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	ir->rc = rc;
+	ir->dev = &intf->dev;
+	ir->udev = udev;
+	mutex_init(&ir->lock);
+
+	init_completion(&ir->completion);
+	pipeout = usb_sndintpipe(udev,
+				idesc->endpoint[1].desc.bEndpointAddress);
+	usb_fill_int_urb(ir->urb_out, udev, pipeout, ir->packet, MAX_OUT_PACKET,
+						iguanair_irq_out, ir, 1);
+	ir->urb_out->transfer_dma = ir->dma_out;
+	ir->urb_out->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	pipein = usb_rcvintpipe(udev, idesc->endpoint[0].desc.bEndpointAddress);
+	usb_fill_int_urb(ir->urb_in, udev, pipein, ir->buf_in, MAX_IN_PACKET,
+							 iguanair_rx, ir, 1);
+	ir->urb_in->transfer_dma = ir->dma_in;
+	ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	ret = usb_submit_urb(ir->urb_in, GFP_KERNEL);
+	if (ret) {
+		dev_warn(&intf->dev, "failed to submit urb: %d\n", ret);
+		goto out;
+	}
+
+	ret = iguanair_get_features(ir);
+	if (ret)
+		goto out2;
+
+	snprintf(ir->name, sizeof(ir->name),
+		"IguanaWorks USB IR Transceiver version 0x%04x", ir->version);
+
+	usb_make_path(ir->udev, ir->phys, sizeof(ir->phys));
+
+	rc->input_name = ir->name;
+	rc->input_phys = ir->phys;
+	usb_to_input_id(ir->udev, &rc->input_id);
+	rc->dev.parent = &intf->dev;
+	rc->driver_type = RC_DRIVER_IR_RAW;
+	rc->allowed_protocols = RC_BIT_ALL;
+	rc->priv = ir;
+	rc->open = iguanair_open;
+	rc->close = iguanair_close;
+	rc->s_tx_mask = iguanair_set_tx_mask;
+	rc->s_tx_carrier = iguanair_set_tx_carrier;
+	rc->tx_ir = iguanair_tx;
+	rc->driver_name = DRIVER_NAME;
+	rc->map_name = RC_MAP_RC6_MCE;
+	rc->timeout = MS_TO_NS(100);
+	rc->rx_resolution = RX_RESOLUTION;
+
+	iguanair_set_tx_carrier(rc, 38000);
+	iguanair_set_tx_mask(rc, 0);
+
+	ret = rc_register_device(rc);
+	if (ret < 0) {
+		dev_err(&intf->dev, "failed to register rc device %d", ret);
+		goto out2;
+	}
+
+	usb_set_intfdata(intf, ir);
+
+	return 0;
+out2:
+	usb_kill_urb(ir->urb_in);
+	usb_kill_urb(ir->urb_out);
+out:
+	if (ir) {
+		usb_free_urb(ir->urb_in);
+		usb_free_urb(ir->urb_out);
+		usb_free_coherent(udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in);
+		usb_free_coherent(udev, MAX_OUT_PACKET, ir->packet,
+								ir->dma_out);
+	}
+	rc_free_device(rc);
+	kfree(ir);
+	return ret;
+}
+
+static void iguanair_disconnect(struct usb_interface *intf)
+{
+	struct iguanair *ir = usb_get_intfdata(intf);
+
+	rc_unregister_device(ir->rc);
+	usb_set_intfdata(intf, NULL);
+	usb_kill_urb(ir->urb_in);
+	usb_kill_urb(ir->urb_out);
+	usb_free_urb(ir->urb_in);
+	usb_free_urb(ir->urb_out);
+	usb_free_coherent(ir->udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in);
+	usb_free_coherent(ir->udev, MAX_OUT_PACKET, ir->packet, ir->dma_out);
+	kfree(ir);
+}
+
+static int iguanair_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct iguanair *ir = usb_get_intfdata(intf);
+	int rc = 0;
+
+	mutex_lock(&ir->lock);
+
+	if (ir->receiver_on) {
+		rc = iguanair_receiver(ir, false);
+		if (rc)
+			dev_warn(ir->dev, "failed to disable receiver for suspend\n");
+	}
+
+	usb_kill_urb(ir->urb_in);
+	usb_kill_urb(ir->urb_out);
+
+	mutex_unlock(&ir->lock);
+
+	return rc;
+}
+
+static int iguanair_resume(struct usb_interface *intf)
+{
+	struct iguanair *ir = usb_get_intfdata(intf);
+	int rc = 0;
+
+	mutex_lock(&ir->lock);
+
+	rc = usb_submit_urb(ir->urb_in, GFP_KERNEL);
+	if (rc)
+		dev_warn(&intf->dev, "failed to submit urb: %d\n", rc);
+
+	if (ir->receiver_on) {
+		rc = iguanair_receiver(ir, true);
+		if (rc)
+			dev_warn(ir->dev, "failed to enable receiver after resume\n");
+	}
+
+	mutex_unlock(&ir->lock);
+
+	return rc;
+}
+
+static const struct usb_device_id iguanair_table[] = {
+	{ USB_DEVICE(0x1781, 0x0938) },
+	{ }
+};
+
+static struct usb_driver iguanair_driver = {
+	.name =	DRIVER_NAME,
+	.probe = iguanair_probe,
+	.disconnect = iguanair_disconnect,
+	.suspend = iguanair_suspend,
+	.resume = iguanair_resume,
+	.reset_resume = iguanair_resume,
+	.id_table = iguanair_table,
+	.soft_unbind = 1	/* we want to disable receiver on unbind */
+};
+
+module_usb_driver(iguanair_driver);
+
+MODULE_DESCRIPTION("IguanaWorks USB IR Transceiver");
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, iguanair_table);
+
diff --git a/drivers/media/rc/img-ir/Kconfig b/drivers/media/rc/img-ir/Kconfig
new file mode 100644
index 0000000..a896d3c
--- /dev/null
+++ b/drivers/media/rc/img-ir/Kconfig
@@ -0,0 +1,77 @@
+config IR_IMG
+	tristate "ImgTec IR Decoder"
+	depends on RC_CORE
+	depends on METAG || MIPS || COMPILE_TEST
+	select IR_IMG_HW if !IR_IMG_RAW
+	help
+	   Say Y or M here if you want to use the ImgTec infrared decoder
+	   functionality found in SoCs such as TZ1090.
+
+config IR_IMG_RAW
+	bool "Raw decoder"
+	depends on IR_IMG
+	help
+	   Say Y here to enable the raw mode driver which passes raw IR signal
+	   changes to the IR raw decoders for software decoding. This is much
+	   less reliable (due to lack of timestamps) and consumes more
+	   processing power than using hardware decode, but can be useful for
+	   testing, debug, and to make more protocols available.
+
+config IR_IMG_HW
+	bool "Hardware decoder"
+	depends on IR_IMG
+	help
+	   Say Y here to enable the hardware decode driver which decodes the IR
+	   signals in hardware. This is more reliable, consumes less processing
+	   power since only a single interrupt is received for each scancode,
+	   and allows an IR scancode to be used as a wake event.
+
+config IR_IMG_NEC
+	bool "NEC protocol support"
+	depends on IR_IMG_HW
+	help
+	   Say Y here to enable support for the NEC, extended NEC, and 32-bit
+	   NEC protocols in the ImgTec infrared decoder block.
+
+config IR_IMG_JVC
+	bool "JVC protocol support"
+	depends on IR_IMG_HW
+	help
+	   Say Y here to enable support for the JVC protocol in the ImgTec
+	   infrared decoder block.
+
+config IR_IMG_SONY
+	bool "Sony protocol support"
+	depends on IR_IMG_HW
+	help
+	   Say Y here to enable support for the Sony protocol in the ImgTec
+	   infrared decoder block.
+
+config IR_IMG_SHARP
+	bool "Sharp protocol support"
+	depends on IR_IMG_HW
+	help
+	   Say Y here to enable support for the Sharp protocol in the ImgTec
+	   infrared decoder block.
+
+config IR_IMG_SANYO
+	bool "Sanyo protocol support"
+	depends on IR_IMG_HW
+	help
+	   Say Y here to enable support for the Sanyo protocol (used by Sanyo,
+	   Aiwa, Chinon remotes) in the ImgTec infrared decoder block.
+
+config IR_IMG_RC5
+	bool "Philips RC5 protocol support"
+	depends on IR_IMG_HW
+	help
+	   Say Y here to enable support for the RC5 protocol in the ImgTec
+	   infrared decoder block.
+
+config IR_IMG_RC6
+	bool "Philips RC6 protocol support"
+	depends on IR_IMG_HW
+	help
+	   Say Y here to enable support for the RC6 protocol in the ImgTec
+	   infrared decoder block.
+	   Note: This version only supports mode 0.
diff --git a/drivers/media/rc/img-ir/Makefile b/drivers/media/rc/img-ir/Makefile
new file mode 100644
index 0000000..8e6d458
--- /dev/null
+++ b/drivers/media/rc/img-ir/Makefile
@@ -0,0 +1,13 @@
+img-ir-y			:= img-ir-core.o
+img-ir-$(CONFIG_IR_IMG_RAW)	+= img-ir-raw.o
+img-ir-$(CONFIG_IR_IMG_HW)	+= img-ir-hw.o
+img-ir-$(CONFIG_IR_IMG_NEC)	+= img-ir-nec.o
+img-ir-$(CONFIG_IR_IMG_JVC)	+= img-ir-jvc.o
+img-ir-$(CONFIG_IR_IMG_SONY)	+= img-ir-sony.o
+img-ir-$(CONFIG_IR_IMG_SHARP)	+= img-ir-sharp.o
+img-ir-$(CONFIG_IR_IMG_SANYO)	+= img-ir-sanyo.o
+img-ir-$(CONFIG_IR_IMG_RC5)	+= img-ir-rc5.o
+img-ir-$(CONFIG_IR_IMG_RC6)	+= img-ir-rc6.o
+img-ir-objs			:= $(img-ir-y)
+
+obj-$(CONFIG_IR_IMG)		+= img-ir.o
diff --git a/drivers/media/rc/img-ir/img-ir-core.c b/drivers/media/rc/img-ir/img-ir-core.c
new file mode 100644
index 0000000..03fe080
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-core.c
@@ -0,0 +1,201 @@
+/*
+ * ImgTec IR Decoder found in PowerDown Controller.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ *
+ * 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 contains core img-ir code for setting up the driver. The two interfaces
+ * (raw and hardware decode) are handled separately.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include "img-ir.h"
+
+static irqreturn_t img_ir_isr(int irq, void *dev_id)
+{
+	struct img_ir_priv *priv = dev_id;
+	u32 irq_status;
+
+	spin_lock(&priv->lock);
+	/* we have to clear irqs before reading */
+	irq_status = img_ir_read(priv, IMG_IR_IRQ_STATUS);
+	img_ir_write(priv, IMG_IR_IRQ_CLEAR, irq_status);
+
+	/* don't handle valid data irqs if we're only interested in matches */
+	irq_status &= img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+
+	/* hand off edge interrupts to raw decode handler */
+	if (irq_status & IMG_IR_IRQ_EDGE && img_ir_raw_enabled(&priv->raw))
+		img_ir_isr_raw(priv, irq_status);
+
+	/* hand off hardware match interrupts to hardware decode handler */
+	if (irq_status & (IMG_IR_IRQ_DATA_MATCH |
+			  IMG_IR_IRQ_DATA_VALID |
+			  IMG_IR_IRQ_DATA2_VALID) &&
+	    img_ir_hw_enabled(&priv->hw))
+		img_ir_isr_hw(priv, irq_status);
+
+	spin_unlock(&priv->lock);
+	return IRQ_HANDLED;
+}
+
+static void img_ir_setup(struct img_ir_priv *priv)
+{
+	/* start off with interrupts disabled */
+	img_ir_write(priv, IMG_IR_IRQ_ENABLE, 0);
+
+	img_ir_setup_raw(priv);
+	img_ir_setup_hw(priv);
+
+	if (!IS_ERR(priv->clk))
+		clk_prepare_enable(priv->clk);
+}
+
+static void img_ir_ident(struct img_ir_priv *priv)
+{
+	u32 core_rev = img_ir_read(priv, IMG_IR_CORE_REV);
+
+	dev_info(priv->dev,
+		 "IMG IR Decoder (%d.%d.%d.%d) probed successfully\n",
+		 (core_rev & IMG_IR_DESIGNER) >> IMG_IR_DESIGNER_SHIFT,
+		 (core_rev & IMG_IR_MAJOR_REV) >> IMG_IR_MAJOR_REV_SHIFT,
+		 (core_rev & IMG_IR_MINOR_REV) >> IMG_IR_MINOR_REV_SHIFT,
+		 (core_rev & IMG_IR_MAINT_REV) >> IMG_IR_MAINT_REV_SHIFT);
+	dev_info(priv->dev, "Modes:%s%s\n",
+		 img_ir_hw_enabled(&priv->hw) ? " hardware" : "",
+		 img_ir_raw_enabled(&priv->raw) ? " raw" : "");
+}
+
+static int img_ir_probe(struct platform_device *pdev)
+{
+	struct img_ir_priv *priv;
+	struct resource *res_regs;
+	int irq, error, error2;
+
+	/* Get resources from platform device */
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "cannot find IRQ resource\n");
+		return irq;
+	}
+
+	/* Private driver data */
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(&pdev->dev, "cannot allocate device data\n");
+		return -ENOMEM;
+	}
+	platform_set_drvdata(pdev, priv);
+	priv->dev = &pdev->dev;
+	spin_lock_init(&priv->lock);
+
+	/* Ioremap the registers */
+	res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->reg_base = devm_ioremap_resource(&pdev->dev, res_regs);
+	if (IS_ERR(priv->reg_base))
+		return PTR_ERR(priv->reg_base);
+
+	/* Get core clock */
+	priv->clk = devm_clk_get(&pdev->dev, "core");
+	if (IS_ERR(priv->clk))
+		dev_warn(&pdev->dev, "cannot get core clock resource\n");
+
+	/* Get sys clock */
+	priv->sys_clk = devm_clk_get(&pdev->dev, "sys");
+	if (IS_ERR(priv->sys_clk))
+		dev_warn(&pdev->dev, "cannot get sys clock resource\n");
+	/*
+	 * Enabling the system clock before the register interface is
+	 * accessed. ISR shouldn't get called with Sys Clock disabled,
+	 * hence exiting probe with an error.
+	 */
+	if (!IS_ERR(priv->sys_clk)) {
+		error = clk_prepare_enable(priv->sys_clk);
+		if (error) {
+			dev_err(&pdev->dev, "cannot enable sys clock\n");
+			return error;
+		}
+	}
+
+	/* Set up raw & hw decoder */
+	error = img_ir_probe_raw(priv);
+	error2 = img_ir_probe_hw(priv);
+	if (error && error2) {
+		if (error == -ENODEV)
+			error = error2;
+		goto err_probe;
+	}
+
+	/* Get the IRQ */
+	priv->irq = irq;
+	error = request_irq(priv->irq, img_ir_isr, 0, "img-ir", priv);
+	if (error) {
+		dev_err(&pdev->dev, "cannot register IRQ %u\n",
+			priv->irq);
+		error = -EIO;
+		goto err_irq;
+	}
+
+	img_ir_ident(priv);
+	img_ir_setup(priv);
+
+	return 0;
+
+err_irq:
+	img_ir_remove_hw(priv);
+	img_ir_remove_raw(priv);
+err_probe:
+	if (!IS_ERR(priv->sys_clk))
+		clk_disable_unprepare(priv->sys_clk);
+	return error;
+}
+
+static int img_ir_remove(struct platform_device *pdev)
+{
+	struct img_ir_priv *priv = platform_get_drvdata(pdev);
+
+	free_irq(priv->irq, priv);
+	img_ir_remove_hw(priv);
+	img_ir_remove_raw(priv);
+
+	if (!IS_ERR(priv->clk))
+		clk_disable_unprepare(priv->clk);
+	if (!IS_ERR(priv->sys_clk))
+		clk_disable_unprepare(priv->sys_clk);
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(img_ir_pmops, img_ir_suspend, img_ir_resume);
+
+static const struct of_device_id img_ir_match[] = {
+	{ .compatible = "img,ir-rev1" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, img_ir_match);
+
+static struct platform_driver img_ir_driver = {
+	.driver = {
+		.name = "img-ir",
+		.of_match_table	= img_ir_match,
+		.pm = &img_ir_pmops,
+	},
+	.probe = img_ir_probe,
+	.remove = img_ir_remove,
+};
+
+module_platform_driver(img_ir_driver);
+
+MODULE_AUTHOR("Imagination Technologies Ltd.");
+MODULE_DESCRIPTION("ImgTec IR");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c
new file mode 100644
index 0000000..7bb71bc
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-hw.c
@@ -0,0 +1,1149 @@
+/*
+ * ImgTec IR Hardware Decoder found in PowerDown Controller.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ *
+ * 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 ties into the input subsystem using the RC-core. Protocol support is
+ * provided in separate modules which provide the parameters and scancode
+ * translation functions to set up the hardware decoder and interpret the
+ * resulting input.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <media/rc-core.h>
+#include "img-ir.h"
+
+/* Decoders lock (only modified to preprocess them) */
+static DEFINE_SPINLOCK(img_ir_decoders_lock);
+
+static bool img_ir_decoders_preprocessed;
+static struct img_ir_decoder *img_ir_decoders[] = {
+#ifdef CONFIG_IR_IMG_NEC
+	&img_ir_nec,
+#endif
+#ifdef CONFIG_IR_IMG_JVC
+	&img_ir_jvc,
+#endif
+#ifdef CONFIG_IR_IMG_SONY
+	&img_ir_sony,
+#endif
+#ifdef CONFIG_IR_IMG_SHARP
+	&img_ir_sharp,
+#endif
+#ifdef CONFIG_IR_IMG_SANYO
+	&img_ir_sanyo,
+#endif
+#ifdef CONFIG_IR_IMG_RC5
+	&img_ir_rc5,
+#endif
+#ifdef CONFIG_IR_IMG_RC6
+	&img_ir_rc6,
+#endif
+	NULL
+};
+
+#define IMG_IR_F_FILTER		BIT(RC_FILTER_NORMAL)	/* enable filtering */
+#define IMG_IR_F_WAKE		BIT(RC_FILTER_WAKEUP)	/* enable waking */
+
+/* code type quirks */
+
+#define IMG_IR_QUIRK_CODE_BROKEN	0x1	/* Decode is broken */
+#define IMG_IR_QUIRK_CODE_LEN_INCR	0x2	/* Bit length needs increment */
+/*
+ * The decoder generates rapid interrupts without actually having
+ * received any new data after an incomplete IR code is decoded.
+ */
+#define IMG_IR_QUIRK_CODE_IRQ		0x4
+
+/* functions for preprocessing timings, ensuring max is set */
+
+static void img_ir_timing_preprocess(struct img_ir_timing_range *range,
+				     unsigned int unit)
+{
+	if (range->max < range->min)
+		range->max = range->min;
+	if (unit) {
+		/* multiply by unit and convert to microseconds */
+		range->min = (range->min*unit)/1000;
+		range->max = (range->max*unit + 999)/1000; /* round up */
+	}
+}
+
+static void img_ir_symbol_timing_preprocess(struct img_ir_symbol_timing *timing,
+					    unsigned int unit)
+{
+	img_ir_timing_preprocess(&timing->pulse, unit);
+	img_ir_timing_preprocess(&timing->space, unit);
+}
+
+static void img_ir_timings_preprocess(struct img_ir_timings *timings,
+				      unsigned int unit)
+{
+	img_ir_symbol_timing_preprocess(&timings->ldr, unit);
+	img_ir_symbol_timing_preprocess(&timings->s00, unit);
+	img_ir_symbol_timing_preprocess(&timings->s01, unit);
+	img_ir_symbol_timing_preprocess(&timings->s10, unit);
+	img_ir_symbol_timing_preprocess(&timings->s11, unit);
+	/* default s10 and s11 to s00 and s01 if no leader */
+	if (unit)
+		/* multiply by unit and convert to microseconds (round up) */
+		timings->ft.ft_min = (timings->ft.ft_min*unit + 999)/1000;
+}
+
+/* functions for filling empty fields with defaults */
+
+static void img_ir_timing_defaults(struct img_ir_timing_range *range,
+				   struct img_ir_timing_range *defaults)
+{
+	if (!range->min)
+		range->min = defaults->min;
+	if (!range->max)
+		range->max = defaults->max;
+}
+
+static void img_ir_symbol_timing_defaults(struct img_ir_symbol_timing *timing,
+					  struct img_ir_symbol_timing *defaults)
+{
+	img_ir_timing_defaults(&timing->pulse, &defaults->pulse);
+	img_ir_timing_defaults(&timing->space, &defaults->space);
+}
+
+static void img_ir_timings_defaults(struct img_ir_timings *timings,
+				    struct img_ir_timings *defaults)
+{
+	img_ir_symbol_timing_defaults(&timings->ldr, &defaults->ldr);
+	img_ir_symbol_timing_defaults(&timings->s00, &defaults->s00);
+	img_ir_symbol_timing_defaults(&timings->s01, &defaults->s01);
+	img_ir_symbol_timing_defaults(&timings->s10, &defaults->s10);
+	img_ir_symbol_timing_defaults(&timings->s11, &defaults->s11);
+	if (!timings->ft.ft_min)
+		timings->ft.ft_min = defaults->ft.ft_min;
+}
+
+/* functions for converting timings to register values */
+
+/**
+ * img_ir_control() - Convert control struct to control register value.
+ * @control:	Control data
+ *
+ * Returns:	The control register value equivalent of @control.
+ */
+static u32 img_ir_control(const struct img_ir_control *control)
+{
+	u32 ctrl = control->code_type << IMG_IR_CODETYPE_SHIFT;
+	if (control->decoden)
+		ctrl |= IMG_IR_DECODEN;
+	if (control->hdrtog)
+		ctrl |= IMG_IR_HDRTOG;
+	if (control->ldrdec)
+		ctrl |= IMG_IR_LDRDEC;
+	if (control->decodinpol)
+		ctrl |= IMG_IR_DECODINPOL;
+	if (control->bitorien)
+		ctrl |= IMG_IR_BITORIEN;
+	if (control->d1validsel)
+		ctrl |= IMG_IR_D1VALIDSEL;
+	if (control->bitinv)
+		ctrl |= IMG_IR_BITINV;
+	if (control->decodend2)
+		ctrl |= IMG_IR_DECODEND2;
+	if (control->bitoriend2)
+		ctrl |= IMG_IR_BITORIEND2;
+	if (control->bitinvd2)
+		ctrl |= IMG_IR_BITINVD2;
+	return ctrl;
+}
+
+/**
+ * img_ir_timing_range_convert() - Convert microsecond range.
+ * @out:	Output timing range in clock cycles with a shift.
+ * @in:		Input timing range in microseconds.
+ * @tolerance:	Tolerance as a fraction of 128 (roughly percent).
+ * @clock_hz:	IR clock rate in Hz.
+ * @shift:	Shift of output units.
+ *
+ * Converts min and max from microseconds to IR clock cycles, applies a
+ * tolerance, and shifts for the register, rounding in the right direction.
+ * Note that in and out can safely be the same object.
+ */
+static void img_ir_timing_range_convert(struct img_ir_timing_range *out,
+					const struct img_ir_timing_range *in,
+					unsigned int tolerance,
+					unsigned long clock_hz,
+					unsigned int shift)
+{
+	unsigned int min = in->min;
+	unsigned int max = in->max;
+	/* add a tolerance */
+	min = min - (min*tolerance >> 7);
+	max = max + (max*tolerance >> 7);
+	/* convert from microseconds into clock cycles */
+	min = min*clock_hz / 1000000;
+	max = (max*clock_hz + 999999) / 1000000; /* round up */
+	/* apply shift and copy to output */
+	out->min = min >> shift;
+	out->max = (max + ((1 << shift) - 1)) >> shift; /* round up */
+}
+
+/**
+ * img_ir_symbol_timing() - Convert symbol timing struct to register value.
+ * @timing:	Symbol timing data
+ * @tolerance:	Timing tolerance where 0-128 represents 0-100%
+ * @clock_hz:	Frequency of source clock in Hz
+ * @pd_shift:	Shift to apply to symbol period
+ * @w_shift:	Shift to apply to symbol width
+ *
+ * Returns:	Symbol timing register value based on arguments.
+ */
+static u32 img_ir_symbol_timing(const struct img_ir_symbol_timing *timing,
+				unsigned int tolerance,
+				unsigned long clock_hz,
+				unsigned int pd_shift,
+				unsigned int w_shift)
+{
+	struct img_ir_timing_range hw_pulse, hw_period;
+	/* we calculate period in hw_period, then convert in place */
+	hw_period.min = timing->pulse.min + timing->space.min;
+	hw_period.max = timing->pulse.max + timing->space.max;
+	img_ir_timing_range_convert(&hw_period, &hw_period,
+			tolerance, clock_hz, pd_shift);
+	img_ir_timing_range_convert(&hw_pulse, &timing->pulse,
+			tolerance, clock_hz, w_shift);
+	/* construct register value */
+	return	(hw_period.max	<< IMG_IR_PD_MAX_SHIFT)	|
+		(hw_period.min	<< IMG_IR_PD_MIN_SHIFT)	|
+		(hw_pulse.max	<< IMG_IR_W_MAX_SHIFT)	|
+		(hw_pulse.min	<< IMG_IR_W_MIN_SHIFT);
+}
+
+/**
+ * img_ir_free_timing() - Convert free time timing struct to register value.
+ * @timing:	Free symbol timing data
+ * @clock_hz:	Source clock frequency in Hz
+ *
+ * Returns:	Free symbol timing register value.
+ */
+static u32 img_ir_free_timing(const struct img_ir_free_timing *timing,
+			      unsigned long clock_hz)
+{
+	unsigned int minlen, maxlen, ft_min;
+	/* minlen is only 5 bits, and round minlen to multiple of 2 */
+	if (timing->minlen < 30)
+		minlen = timing->minlen & -2;
+	else
+		minlen = 30;
+	/* maxlen has maximum value of 48, and round maxlen to multiple of 2 */
+	if (timing->maxlen < 48)
+		maxlen = (timing->maxlen + 1) & -2;
+	else
+		maxlen = 48;
+	/* convert and shift ft_min, rounding upwards */
+	ft_min = (timing->ft_min*clock_hz + 999999) / 1000000;
+	ft_min = (ft_min + 7) >> 3;
+	/* construct register value */
+	return	(maxlen << IMG_IR_MAXLEN_SHIFT)	|
+		(minlen << IMG_IR_MINLEN_SHIFT)	|
+		(ft_min << IMG_IR_FT_MIN_SHIFT);
+}
+
+/**
+ * img_ir_free_timing_dynamic() - Update free time register value.
+ * @st_ft:	Static free time register value from img_ir_free_timing.
+ * @filter:	Current filter which may additionally restrict min/max len.
+ *
+ * Returns:	Updated free time register value based on the current filter.
+ */
+static u32 img_ir_free_timing_dynamic(u32 st_ft, struct img_ir_filter *filter)
+{
+	unsigned int minlen, maxlen, newminlen, newmaxlen;
+
+	/* round minlen, maxlen to multiple of 2 */
+	newminlen = filter->minlen & -2;
+	newmaxlen = (filter->maxlen + 1) & -2;
+	/* extract min/max len from register */
+	minlen = (st_ft & IMG_IR_MINLEN) >> IMG_IR_MINLEN_SHIFT;
+	maxlen = (st_ft & IMG_IR_MAXLEN) >> IMG_IR_MAXLEN_SHIFT;
+	/* if the new values are more restrictive, update the register value */
+	if (newminlen > minlen) {
+		st_ft &= ~IMG_IR_MINLEN;
+		st_ft |= newminlen << IMG_IR_MINLEN_SHIFT;
+	}
+	if (newmaxlen < maxlen) {
+		st_ft &= ~IMG_IR_MAXLEN;
+		st_ft |= newmaxlen << IMG_IR_MAXLEN_SHIFT;
+	}
+	return st_ft;
+}
+
+/**
+ * img_ir_timings_convert() - Convert timings to register values
+ * @regs:	Output timing register values
+ * @timings:	Input timing data
+ * @tolerance:	Timing tolerance where 0-128 represents 0-100%
+ * @clock_hz:	Source clock frequency in Hz
+ */
+static void img_ir_timings_convert(struct img_ir_timing_regvals *regs,
+				   const struct img_ir_timings *timings,
+				   unsigned int tolerance,
+				   unsigned int clock_hz)
+{
+	/* leader symbol timings are divided by 16 */
+	regs->ldr = img_ir_symbol_timing(&timings->ldr, tolerance, clock_hz,
+			4, 4);
+	/* other symbol timings, pd fields only are divided by 2 */
+	regs->s00 = img_ir_symbol_timing(&timings->s00, tolerance, clock_hz,
+			1, 0);
+	regs->s01 = img_ir_symbol_timing(&timings->s01, tolerance, clock_hz,
+			1, 0);
+	regs->s10 = img_ir_symbol_timing(&timings->s10, tolerance, clock_hz,
+			1, 0);
+	regs->s11 = img_ir_symbol_timing(&timings->s11, tolerance, clock_hz,
+			1, 0);
+	regs->ft = img_ir_free_timing(&timings->ft, clock_hz);
+}
+
+/**
+ * img_ir_decoder_preprocess() - Preprocess timings in decoder.
+ * @decoder:	Decoder to be preprocessed.
+ *
+ * Ensures that the symbol timing ranges are valid with respect to ordering, and
+ * does some fixed conversion on them.
+ */
+static void img_ir_decoder_preprocess(struct img_ir_decoder *decoder)
+{
+	/* default tolerance */
+	if (!decoder->tolerance)
+		decoder->tolerance = 10; /* percent */
+	/* and convert tolerance to fraction out of 128 */
+	decoder->tolerance = decoder->tolerance * 128 / 100;
+
+	/* fill in implicit fields */
+	img_ir_timings_preprocess(&decoder->timings, decoder->unit);
+
+	/* do the same for repeat timings if applicable */
+	if (decoder->repeat) {
+		img_ir_timings_preprocess(&decoder->rtimings, decoder->unit);
+		img_ir_timings_defaults(&decoder->rtimings, &decoder->timings);
+	}
+}
+
+/**
+ * img_ir_decoder_convert() - Generate internal timings in decoder.
+ * @decoder:	Decoder to be converted to internal timings.
+ * @timings:	Timing register values.
+ * @clock_hz:	IR clock rate in Hz.
+ *
+ * Fills out the repeat timings and timing register values for a specific clock
+ * rate.
+ */
+static void img_ir_decoder_convert(const struct img_ir_decoder *decoder,
+				   struct img_ir_reg_timings *reg_timings,
+				   unsigned int clock_hz)
+{
+	/* calculate control value */
+	reg_timings->ctrl = img_ir_control(&decoder->control);
+
+	/* fill in implicit fields and calculate register values */
+	img_ir_timings_convert(&reg_timings->timings, &decoder->timings,
+			       decoder->tolerance, clock_hz);
+
+	/* do the same for repeat timings if applicable */
+	if (decoder->repeat)
+		img_ir_timings_convert(&reg_timings->rtimings,
+				       &decoder->rtimings, decoder->tolerance,
+				       clock_hz);
+}
+
+/**
+ * img_ir_write_timings() - Write timings to the hardware now
+ * @priv:	IR private data
+ * @regs:	Timing register values to write
+ * @type:	RC filter type (RC_FILTER_*)
+ *
+ * Write timing register values @regs to the hardware, taking into account the
+ * current filter which may impose restrictions on the length of the expected
+ * data.
+ */
+static void img_ir_write_timings(struct img_ir_priv *priv,
+				 struct img_ir_timing_regvals *regs,
+				 enum rc_filter_type type)
+{
+	struct img_ir_priv_hw *hw = &priv->hw;
+
+	/* filter may be more restrictive to minlen, maxlen */
+	u32 ft = regs->ft;
+	if (hw->flags & BIT(type))
+		ft = img_ir_free_timing_dynamic(regs->ft, &hw->filters[type]);
+	/* write to registers */
+	img_ir_write(priv, IMG_IR_LEAD_SYMB_TIMING, regs->ldr);
+	img_ir_write(priv, IMG_IR_S00_SYMB_TIMING, regs->s00);
+	img_ir_write(priv, IMG_IR_S01_SYMB_TIMING, regs->s01);
+	img_ir_write(priv, IMG_IR_S10_SYMB_TIMING, regs->s10);
+	img_ir_write(priv, IMG_IR_S11_SYMB_TIMING, regs->s11);
+	img_ir_write(priv, IMG_IR_FREE_SYMB_TIMING, ft);
+	dev_dbg(priv->dev, "timings: ldr=%#x, s=[%#x, %#x, %#x, %#x], ft=%#x\n",
+		regs->ldr, regs->s00, regs->s01, regs->s10, regs->s11, ft);
+}
+
+static void img_ir_write_filter(struct img_ir_priv *priv,
+				struct img_ir_filter *filter)
+{
+	if (filter) {
+		dev_dbg(priv->dev, "IR filter=%016llx & %016llx\n",
+			(unsigned long long)filter->data,
+			(unsigned long long)filter->mask);
+		img_ir_write(priv, IMG_IR_IRQ_MSG_DATA_LW, (u32)filter->data);
+		img_ir_write(priv, IMG_IR_IRQ_MSG_DATA_UP, (u32)(filter->data
+									>> 32));
+		img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_LW, (u32)filter->mask);
+		img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_UP, (u32)(filter->mask
+									>> 32));
+	} else {
+		dev_dbg(priv->dev, "IR clearing filter\n");
+		img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_LW, 0);
+		img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_UP, 0);
+	}
+}
+
+/* caller must have lock */
+static void _img_ir_set_filter(struct img_ir_priv *priv,
+			       struct img_ir_filter *filter)
+{
+	struct img_ir_priv_hw *hw = &priv->hw;
+	u32 irq_en, irq_on;
+
+	irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+	if (filter) {
+		/* Only use the match interrupt */
+		hw->filters[RC_FILTER_NORMAL] = *filter;
+		hw->flags |= IMG_IR_F_FILTER;
+		irq_on = IMG_IR_IRQ_DATA_MATCH;
+		irq_en &= ~(IMG_IR_IRQ_DATA_VALID | IMG_IR_IRQ_DATA2_VALID);
+	} else {
+		/* Only use the valid interrupt */
+		hw->flags &= ~IMG_IR_F_FILTER;
+		irq_en &= ~IMG_IR_IRQ_DATA_MATCH;
+		irq_on = IMG_IR_IRQ_DATA_VALID | IMG_IR_IRQ_DATA2_VALID;
+	}
+	irq_en |= irq_on;
+
+	img_ir_write_filter(priv, filter);
+	/* clear any interrupts we're enabling so we don't handle old ones */
+	img_ir_write(priv, IMG_IR_IRQ_CLEAR, irq_on);
+	img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en);
+}
+
+/* caller must have lock */
+static void _img_ir_set_wake_filter(struct img_ir_priv *priv,
+				    struct img_ir_filter *filter)
+{
+	struct img_ir_priv_hw *hw = &priv->hw;
+	if (filter) {
+		/* Enable wake, and copy filter for later */
+		hw->filters[RC_FILTER_WAKEUP] = *filter;
+		hw->flags |= IMG_IR_F_WAKE;
+	} else {
+		/* Disable wake */
+		hw->flags &= ~IMG_IR_F_WAKE;
+	}
+}
+
+/* Callback for setting scancode filter */
+static int img_ir_set_filter(struct rc_dev *dev, enum rc_filter_type type,
+			     struct rc_scancode_filter *sc_filter)
+{
+	struct img_ir_priv *priv = dev->priv;
+	struct img_ir_priv_hw *hw = &priv->hw;
+	struct img_ir_filter filter, *filter_ptr = &filter;
+	int ret = 0;
+
+	dev_dbg(priv->dev, "IR scancode %sfilter=%08x & %08x\n",
+		type == RC_FILTER_WAKEUP ? "wake " : "",
+		sc_filter->data,
+		sc_filter->mask);
+
+	spin_lock_irq(&priv->lock);
+
+	/* filtering can always be disabled */
+	if (!sc_filter->mask) {
+		filter_ptr = NULL;
+		goto set_unlock;
+	}
+
+	/* current decoder must support scancode filtering */
+	if (!hw->decoder || !hw->decoder->filter) {
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	/* convert scancode filter to raw filter */
+	filter.minlen = 0;
+	filter.maxlen = ~0;
+	ret = hw->decoder->filter(sc_filter, &filter, hw->enabled_protocols);
+	if (ret)
+		goto unlock;
+	dev_dbg(priv->dev, "IR raw %sfilter=%016llx & %016llx\n",
+		type == RC_FILTER_WAKEUP ? "wake " : "",
+		(unsigned long long)filter.data,
+		(unsigned long long)filter.mask);
+
+set_unlock:
+	/* apply raw filters */
+	switch (type) {
+	case RC_FILTER_NORMAL:
+		_img_ir_set_filter(priv, filter_ptr);
+		break;
+	case RC_FILTER_WAKEUP:
+		_img_ir_set_wake_filter(priv, filter_ptr);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+unlock:
+	spin_unlock_irq(&priv->lock);
+	return ret;
+}
+
+static int img_ir_set_normal_filter(struct rc_dev *dev,
+				    struct rc_scancode_filter *sc_filter)
+{
+	return img_ir_set_filter(dev, RC_FILTER_NORMAL, sc_filter);
+}
+
+static int img_ir_set_wakeup_filter(struct rc_dev *dev,
+				    struct rc_scancode_filter *sc_filter)
+{
+	return img_ir_set_filter(dev, RC_FILTER_WAKEUP, sc_filter);
+}
+
+/**
+ * img_ir_set_decoder() - Set the current decoder.
+ * @priv:	IR private data.
+ * @decoder:	Decoder to use with immediate effect.
+ * @proto:	Protocol bitmap (or 0 to use decoder->type).
+ */
+static void img_ir_set_decoder(struct img_ir_priv *priv,
+			       const struct img_ir_decoder *decoder,
+			       u64 proto)
+{
+	struct img_ir_priv_hw *hw = &priv->hw;
+	struct rc_dev *rdev = hw->rdev;
+	u32 ir_status, irq_en;
+	spin_lock_irq(&priv->lock);
+
+	/*
+	 * First record that the protocol is being stopped so that the end timer
+	 * isn't restarted while we're trying to stop it.
+	 */
+	hw->stopping = true;
+
+	/*
+	 * Release the lock to stop the end timer, since the end timer handler
+	 * acquires the lock and we don't want to deadlock waiting for it.
+	 */
+	spin_unlock_irq(&priv->lock);
+	del_timer_sync(&hw->end_timer);
+	del_timer_sync(&hw->suspend_timer);
+	spin_lock_irq(&priv->lock);
+
+	hw->stopping = false;
+
+	/* switch off and disable interrupts */
+	img_ir_write(priv, IMG_IR_CONTROL, 0);
+	irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+	img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en & IMG_IR_IRQ_EDGE);
+	img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_ALL & ~IMG_IR_IRQ_EDGE);
+
+	/* ack any data already detected */
+	ir_status = img_ir_read(priv, IMG_IR_STATUS);
+	if (ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2)) {
+		ir_status &= ~(IMG_IR_RXDVAL | IMG_IR_RXDVALD2);
+		img_ir_write(priv, IMG_IR_STATUS, ir_status);
+	}
+
+	/* always read data to clear buffer if IR wakes the device */
+	img_ir_read(priv, IMG_IR_DATA_LW);
+	img_ir_read(priv, IMG_IR_DATA_UP);
+
+	/* switch back to normal mode */
+	hw->mode = IMG_IR_M_NORMAL;
+
+	/* clear the wakeup scancode filter */
+	rdev->scancode_wakeup_filter.data = 0;
+	rdev->scancode_wakeup_filter.mask = 0;
+
+	/* clear raw filters */
+	_img_ir_set_filter(priv, NULL);
+	_img_ir_set_wake_filter(priv, NULL);
+
+	/* clear the enabled protocols */
+	hw->enabled_protocols = 0;
+
+	/* switch decoder */
+	hw->decoder = decoder;
+	if (!decoder)
+		goto unlock;
+
+	/* set the enabled protocols */
+	if (!proto)
+		proto = decoder->type;
+	hw->enabled_protocols = proto;
+
+	/* write the new timings */
+	img_ir_decoder_convert(decoder, &hw->reg_timings, hw->clk_hz);
+	img_ir_write_timings(priv, &hw->reg_timings.timings, RC_FILTER_NORMAL);
+
+	/* set up and enable */
+	img_ir_write(priv, IMG_IR_CONTROL, hw->reg_timings.ctrl);
+
+
+unlock:
+	spin_unlock_irq(&priv->lock);
+}
+
+/**
+ * img_ir_decoder_compatable() - Find whether a decoder will work with a device.
+ * @priv:	IR private data.
+ * @dec:	Decoder to check.
+ *
+ * Returns:	true if @dec is compatible with the device @priv refers to.
+ */
+static bool img_ir_decoder_compatible(struct img_ir_priv *priv,
+				      const struct img_ir_decoder *dec)
+{
+	unsigned int ct;
+
+	/* don't accept decoders using code types which aren't supported */
+	ct = dec->control.code_type;
+	if (priv->hw.ct_quirks[ct] & IMG_IR_QUIRK_CODE_BROKEN)
+		return false;
+
+	return true;
+}
+
+/**
+ * img_ir_allowed_protos() - Get allowed protocols from global decoder list.
+ * @priv:	IR private data.
+ *
+ * Returns:	Mask of protocols supported by the device @priv refers to.
+ */
+static u64 img_ir_allowed_protos(struct img_ir_priv *priv)
+{
+	u64 protos = 0;
+	struct img_ir_decoder **decp;
+
+	for (decp = img_ir_decoders; *decp; ++decp) {
+		const struct img_ir_decoder *dec = *decp;
+		if (img_ir_decoder_compatible(priv, dec))
+			protos |= dec->type;
+	}
+	return protos;
+}
+
+/* Callback for changing protocol using sysfs */
+static int img_ir_change_protocol(struct rc_dev *dev, u64 *ir_type)
+{
+	struct img_ir_priv *priv = dev->priv;
+	struct img_ir_priv_hw *hw = &priv->hw;
+	struct rc_dev *rdev = hw->rdev;
+	struct img_ir_decoder **decp;
+	u64 wakeup_protocols;
+
+	if (!*ir_type) {
+		/* disable all protocols */
+		img_ir_set_decoder(priv, NULL, 0);
+		goto success;
+	}
+	for (decp = img_ir_decoders; *decp; ++decp) {
+		const struct img_ir_decoder *dec = *decp;
+		if (!img_ir_decoder_compatible(priv, dec))
+			continue;
+		if (*ir_type & dec->type) {
+			*ir_type &= dec->type;
+			img_ir_set_decoder(priv, dec, *ir_type);
+			goto success;
+		}
+	}
+	return -EINVAL;
+
+success:
+	/*
+	 * Only allow matching wakeup protocols for now, and only if filtering
+	 * is supported.
+	 */
+	wakeup_protocols = *ir_type;
+	if (!hw->decoder || !hw->decoder->filter)
+		wakeup_protocols = 0;
+	rdev->allowed_wakeup_protocols = wakeup_protocols;
+	rdev->enabled_wakeup_protocols = wakeup_protocols;
+	return 0;
+}
+
+/* Changes ir-core protocol device attribute */
+static void img_ir_set_protocol(struct img_ir_priv *priv, u64 proto)
+{
+	struct rc_dev *rdev = priv->hw.rdev;
+
+	spin_lock_irq(&rdev->rc_map.lock);
+	rdev->rc_map.rc_type = __ffs64(proto);
+	spin_unlock_irq(&rdev->rc_map.lock);
+
+	mutex_lock(&rdev->lock);
+	rdev->enabled_protocols = proto;
+	rdev->allowed_wakeup_protocols = proto;
+	rdev->enabled_wakeup_protocols = proto;
+	mutex_unlock(&rdev->lock);
+}
+
+/* Set up IR decoders */
+static void img_ir_init_decoders(void)
+{
+	struct img_ir_decoder **decp;
+
+	spin_lock(&img_ir_decoders_lock);
+	if (!img_ir_decoders_preprocessed) {
+		for (decp = img_ir_decoders; *decp; ++decp)
+			img_ir_decoder_preprocess(*decp);
+		img_ir_decoders_preprocessed = true;
+	}
+	spin_unlock(&img_ir_decoders_lock);
+}
+
+#ifdef CONFIG_PM_SLEEP
+/**
+ * img_ir_enable_wake() - Switch to wake mode.
+ * @priv:	IR private data.
+ *
+ * Returns:	non-zero if the IR can wake the system.
+ */
+static int img_ir_enable_wake(struct img_ir_priv *priv)
+{
+	struct img_ir_priv_hw *hw = &priv->hw;
+	int ret = 0;
+
+	spin_lock_irq(&priv->lock);
+	if (hw->flags & IMG_IR_F_WAKE) {
+		/* interrupt only on a match */
+		hw->suspend_irqen = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+		img_ir_write(priv, IMG_IR_IRQ_ENABLE, IMG_IR_IRQ_DATA_MATCH);
+		img_ir_write_filter(priv, &hw->filters[RC_FILTER_WAKEUP]);
+		img_ir_write_timings(priv, &hw->reg_timings.timings,
+				     RC_FILTER_WAKEUP);
+		hw->mode = IMG_IR_M_WAKE;
+		ret = 1;
+	}
+	spin_unlock_irq(&priv->lock);
+	return ret;
+}
+
+/**
+ * img_ir_disable_wake() - Switch out of wake mode.
+ * @priv:	IR private data
+ *
+ * Returns:	1 if the hardware should be allowed to wake from a sleep state.
+ *		0 otherwise.
+ */
+static int img_ir_disable_wake(struct img_ir_priv *priv)
+{
+	struct img_ir_priv_hw *hw = &priv->hw;
+	int ret = 0;
+
+	spin_lock_irq(&priv->lock);
+	if (hw->flags & IMG_IR_F_WAKE) {
+		/* restore normal filtering */
+		if (hw->flags & IMG_IR_F_FILTER) {
+			img_ir_write(priv, IMG_IR_IRQ_ENABLE,
+				     (hw->suspend_irqen & IMG_IR_IRQ_EDGE) |
+				     IMG_IR_IRQ_DATA_MATCH);
+			img_ir_write_filter(priv,
+					    &hw->filters[RC_FILTER_NORMAL]);
+		} else {
+			img_ir_write(priv, IMG_IR_IRQ_ENABLE,
+				     (hw->suspend_irqen & IMG_IR_IRQ_EDGE) |
+				     IMG_IR_IRQ_DATA_VALID |
+				     IMG_IR_IRQ_DATA2_VALID);
+			img_ir_write_filter(priv, NULL);
+		}
+		img_ir_write_timings(priv, &hw->reg_timings.timings,
+				     RC_FILTER_NORMAL);
+		hw->mode = IMG_IR_M_NORMAL;
+		ret = 1;
+	}
+	spin_unlock_irq(&priv->lock);
+	return ret;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+/* lock must be held */
+static void img_ir_begin_repeat(struct img_ir_priv *priv)
+{
+	struct img_ir_priv_hw *hw = &priv->hw;
+	if (hw->mode == IMG_IR_M_NORMAL) {
+		/* switch to repeat timings */
+		img_ir_write(priv, IMG_IR_CONTROL, 0);
+		hw->mode = IMG_IR_M_REPEATING;
+		img_ir_write_timings(priv, &hw->reg_timings.rtimings,
+				     RC_FILTER_NORMAL);
+		img_ir_write(priv, IMG_IR_CONTROL, hw->reg_timings.ctrl);
+	}
+}
+
+/* lock must be held */
+static void img_ir_end_repeat(struct img_ir_priv *priv)
+{
+	struct img_ir_priv_hw *hw = &priv->hw;
+	if (hw->mode == IMG_IR_M_REPEATING) {
+		/* switch to normal timings */
+		img_ir_write(priv, IMG_IR_CONTROL, 0);
+		hw->mode = IMG_IR_M_NORMAL;
+		img_ir_write_timings(priv, &hw->reg_timings.timings,
+				     RC_FILTER_NORMAL);
+		img_ir_write(priv, IMG_IR_CONTROL, hw->reg_timings.ctrl);
+	}
+}
+
+/* lock must be held */
+static void img_ir_handle_data(struct img_ir_priv *priv, u32 len, u64 raw)
+{
+	struct img_ir_priv_hw *hw = &priv->hw;
+	const struct img_ir_decoder *dec = hw->decoder;
+	int ret = IMG_IR_SCANCODE;
+	struct img_ir_scancode_req request;
+
+	request.protocol = RC_TYPE_UNKNOWN;
+	request.toggle   = 0;
+
+	if (dec->scancode)
+		ret = dec->scancode(len, raw, hw->enabled_protocols, &request);
+	else if (len >= 32)
+		request.scancode = (u32)raw;
+	else if (len < 32)
+		request.scancode = (u32)raw & ((1 << len)-1);
+	dev_dbg(priv->dev, "data (%u bits) = %#llx\n",
+		len, (unsigned long long)raw);
+	if (ret == IMG_IR_SCANCODE) {
+		dev_dbg(priv->dev, "decoded scan code %#x, toggle %u\n",
+			request.scancode, request.toggle);
+		rc_keydown(hw->rdev, request.protocol, request.scancode,
+			   request.toggle);
+		img_ir_end_repeat(priv);
+	} else if (ret == IMG_IR_REPEATCODE) {
+		if (hw->mode == IMG_IR_M_REPEATING) {
+			dev_dbg(priv->dev, "decoded repeat code\n");
+			rc_repeat(hw->rdev);
+		} else {
+			dev_dbg(priv->dev, "decoded unexpected repeat code, ignoring\n");
+		}
+	} else {
+		dev_dbg(priv->dev, "decode failed (%d)\n", ret);
+		return;
+	}
+
+
+	/* we mustn't update the end timer while trying to stop it */
+	if (dec->repeat && !hw->stopping) {
+		unsigned long interval;
+
+		img_ir_begin_repeat(priv);
+
+		/* update timer, but allowing for 1/8th tolerance */
+		interval = dec->repeat + (dec->repeat >> 3);
+		mod_timer(&hw->end_timer,
+			  jiffies + msecs_to_jiffies(interval));
+	}
+}
+
+/* timer function to end waiting for repeat. */
+static void img_ir_end_timer(unsigned long arg)
+{
+	struct img_ir_priv *priv = (struct img_ir_priv *)arg;
+
+	spin_lock_irq(&priv->lock);
+	img_ir_end_repeat(priv);
+	spin_unlock_irq(&priv->lock);
+}
+
+/*
+ * Timer function to re-enable the current protocol after it had been
+ * cleared when invalid interrupts were generated due to a quirk in the
+ * img-ir decoder.
+ */
+static void img_ir_suspend_timer(unsigned long arg)
+{
+	struct img_ir_priv *priv = (struct img_ir_priv *)arg;
+
+	spin_lock_irq(&priv->lock);
+	/*
+	 * Don't overwrite enabled valid/match IRQs if they have already been
+	 * changed by e.g. a filter change.
+	 */
+	if ((priv->hw.quirk_suspend_irq & IMG_IR_IRQ_EDGE) ==
+				img_ir_read(priv, IMG_IR_IRQ_ENABLE))
+		img_ir_write(priv, IMG_IR_IRQ_ENABLE,
+					priv->hw.quirk_suspend_irq);
+	/* enable */
+	img_ir_write(priv, IMG_IR_CONTROL, priv->hw.reg_timings.ctrl);
+	spin_unlock_irq(&priv->lock);
+}
+
+#ifdef CONFIG_COMMON_CLK
+static void img_ir_change_frequency(struct img_ir_priv *priv,
+				    struct clk_notifier_data *change)
+{
+	struct img_ir_priv_hw *hw = &priv->hw;
+
+	dev_dbg(priv->dev, "clk changed %lu HZ -> %lu HZ\n",
+		change->old_rate, change->new_rate);
+
+	spin_lock_irq(&priv->lock);
+	if (hw->clk_hz == change->new_rate)
+		goto unlock;
+	hw->clk_hz = change->new_rate;
+	/* refresh current timings */
+	if (hw->decoder) {
+		img_ir_decoder_convert(hw->decoder, &hw->reg_timings,
+				       hw->clk_hz);
+		switch (hw->mode) {
+		case IMG_IR_M_NORMAL:
+			img_ir_write_timings(priv, &hw->reg_timings.timings,
+					     RC_FILTER_NORMAL);
+			break;
+		case IMG_IR_M_REPEATING:
+			img_ir_write_timings(priv, &hw->reg_timings.rtimings,
+					     RC_FILTER_NORMAL);
+			break;
+#ifdef CONFIG_PM_SLEEP
+		case IMG_IR_M_WAKE:
+			img_ir_write_timings(priv, &hw->reg_timings.timings,
+					     RC_FILTER_WAKEUP);
+			break;
+#endif
+		}
+	}
+unlock:
+	spin_unlock_irq(&priv->lock);
+}
+
+static int img_ir_clk_notify(struct notifier_block *self, unsigned long action,
+			     void *data)
+{
+	struct img_ir_priv *priv = container_of(self, struct img_ir_priv,
+						hw.clk_nb);
+	switch (action) {
+	case POST_RATE_CHANGE:
+		img_ir_change_frequency(priv, data);
+		break;
+	default:
+		break;
+	}
+	return NOTIFY_OK;
+}
+#endif /* CONFIG_COMMON_CLK */
+
+/* called with priv->lock held */
+void img_ir_isr_hw(struct img_ir_priv *priv, u32 irq_status)
+{
+	struct img_ir_priv_hw *hw = &priv->hw;
+	u32 ir_status, len, lw, up;
+	unsigned int ct;
+
+	/* use the current decoder */
+	if (!hw->decoder)
+		return;
+
+	ct = hw->decoder->control.code_type;
+
+	ir_status = img_ir_read(priv, IMG_IR_STATUS);
+	if (!(ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2))) {
+		if (!(priv->hw.ct_quirks[ct] & IMG_IR_QUIRK_CODE_IRQ) ||
+				hw->stopping)
+			return;
+		/*
+		 * The below functionality is added as a work around to stop
+		 * multiple Interrupts generated when an incomplete IR code is
+		 * received by the decoder.
+		 * The decoder generates rapid interrupts without actually
+		 * having received any new data. After a single interrupt it's
+		 * expected to clear up, but instead multiple interrupts are
+		 * rapidly generated. only way to get out of this loop is to
+		 * reset the control register after a short delay.
+		 */
+		img_ir_write(priv, IMG_IR_CONTROL, 0);
+		hw->quirk_suspend_irq = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+		img_ir_write(priv, IMG_IR_IRQ_ENABLE,
+			     hw->quirk_suspend_irq & IMG_IR_IRQ_EDGE);
+
+		/* Timer activated to re-enable the protocol. */
+		mod_timer(&hw->suspend_timer,
+			  jiffies + msecs_to_jiffies(5));
+		return;
+	}
+	ir_status &= ~(IMG_IR_RXDVAL | IMG_IR_RXDVALD2);
+	img_ir_write(priv, IMG_IR_STATUS, ir_status);
+
+	len = (ir_status & IMG_IR_RXDLEN) >> IMG_IR_RXDLEN_SHIFT;
+	/* some versions report wrong length for certain code types */
+	if (hw->ct_quirks[ct] & IMG_IR_QUIRK_CODE_LEN_INCR)
+		++len;
+
+	lw = img_ir_read(priv, IMG_IR_DATA_LW);
+	up = img_ir_read(priv, IMG_IR_DATA_UP);
+	img_ir_handle_data(priv, len, (u64)up << 32 | lw);
+}
+
+void img_ir_setup_hw(struct img_ir_priv *priv)
+{
+	struct img_ir_decoder **decp;
+
+	if (!priv->hw.rdev)
+		return;
+
+	/* Use the first available decoder (or disable stuff if NULL) */
+	for (decp = img_ir_decoders; *decp; ++decp) {
+		const struct img_ir_decoder *dec = *decp;
+		if (img_ir_decoder_compatible(priv, dec)) {
+			img_ir_set_protocol(priv, dec->type);
+			img_ir_set_decoder(priv, dec, 0);
+			return;
+		}
+	}
+	img_ir_set_decoder(priv, NULL, 0);
+}
+
+/**
+ * img_ir_probe_hw_caps() - Probe capabilities of the hardware.
+ * @priv:	IR private data.
+ */
+static void img_ir_probe_hw_caps(struct img_ir_priv *priv)
+{
+	struct img_ir_priv_hw *hw = &priv->hw;
+	/*
+	 * When a version of the block becomes available without these quirks,
+	 * they'll have to depend on the core revision.
+	 */
+	hw->ct_quirks[IMG_IR_CODETYPE_PULSELEN]
+		|= IMG_IR_QUIRK_CODE_LEN_INCR;
+	hw->ct_quirks[IMG_IR_CODETYPE_BIPHASE]
+		|= IMG_IR_QUIRK_CODE_IRQ;
+	hw->ct_quirks[IMG_IR_CODETYPE_2BITPULSEPOS]
+		|= IMG_IR_QUIRK_CODE_BROKEN;
+}
+
+int img_ir_probe_hw(struct img_ir_priv *priv)
+{
+	struct img_ir_priv_hw *hw = &priv->hw;
+	struct rc_dev *rdev;
+	int error;
+
+	/* Ensure hardware decoders have been preprocessed */
+	img_ir_init_decoders();
+
+	/* Probe hardware capabilities */
+	img_ir_probe_hw_caps(priv);
+
+	/* Set up the end timer */
+	setup_timer(&hw->end_timer, img_ir_end_timer, (unsigned long)priv);
+	setup_timer(&hw->suspend_timer, img_ir_suspend_timer,
+				(unsigned long)priv);
+
+	/* Register a clock notifier */
+	if (!IS_ERR(priv->clk)) {
+		hw->clk_hz = clk_get_rate(priv->clk);
+#ifdef CONFIG_COMMON_CLK
+		hw->clk_nb.notifier_call = img_ir_clk_notify;
+		error = clk_notifier_register(priv->clk, &hw->clk_nb);
+		if (error)
+			dev_warn(priv->dev,
+				 "failed to register clock notifier\n");
+#endif
+	} else {
+		hw->clk_hz = 32768;
+	}
+
+	/* Allocate hardware decoder */
+	hw->rdev = rdev = rc_allocate_device();
+	if (!rdev) {
+		dev_err(priv->dev, "cannot allocate input device\n");
+		error = -ENOMEM;
+		goto err_alloc_rc;
+	}
+	rdev->priv = priv;
+	rdev->map_name = RC_MAP_EMPTY;
+	rdev->allowed_protocols = img_ir_allowed_protos(priv);
+	rdev->input_name = "IMG Infrared Decoder";
+	rdev->s_filter = img_ir_set_normal_filter;
+	rdev->s_wakeup_filter = img_ir_set_wakeup_filter;
+
+	/* Register hardware decoder */
+	error = rc_register_device(rdev);
+	if (error) {
+		dev_err(priv->dev, "failed to register IR input device\n");
+		goto err_register_rc;
+	}
+
+	/*
+	 * Set this after rc_register_device as no protocols have been
+	 * registered yet.
+	 */
+	rdev->change_protocol = img_ir_change_protocol;
+
+	device_init_wakeup(priv->dev, 1);
+
+	return 0;
+
+err_register_rc:
+	img_ir_set_decoder(priv, NULL, 0);
+	hw->rdev = NULL;
+	rc_free_device(rdev);
+err_alloc_rc:
+#ifdef CONFIG_COMMON_CLK
+	if (!IS_ERR(priv->clk))
+		clk_notifier_unregister(priv->clk, &hw->clk_nb);
+#endif
+	return error;
+}
+
+void img_ir_remove_hw(struct img_ir_priv *priv)
+{
+	struct img_ir_priv_hw *hw = &priv->hw;
+	struct rc_dev *rdev = hw->rdev;
+	if (!rdev)
+		return;
+	img_ir_set_decoder(priv, NULL, 0);
+	hw->rdev = NULL;
+	rc_unregister_device(rdev);
+#ifdef CONFIG_COMMON_CLK
+	if (!IS_ERR(priv->clk))
+		clk_notifier_unregister(priv->clk, &hw->clk_nb);
+#endif
+}
+
+#ifdef CONFIG_PM_SLEEP
+int img_ir_suspend(struct device *dev)
+{
+	struct img_ir_priv *priv = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev) && img_ir_enable_wake(priv))
+		enable_irq_wake(priv->irq);
+	return 0;
+}
+
+int img_ir_resume(struct device *dev)
+{
+	struct img_ir_priv *priv = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev) && img_ir_disable_wake(priv))
+		disable_irq_wake(priv->irq);
+	return 0;
+}
+#endif	/* CONFIG_PM_SLEEP */
diff --git a/drivers/media/rc/img-ir/img-ir-hw.h b/drivers/media/rc/img-ir/img-ir-hw.h
new file mode 100644
index 0000000..91a2977
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-hw.h
@@ -0,0 +1,301 @@
+/*
+ * ImgTec IR Hardware Decoder found in PowerDown Controller.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ *
+ * 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.
+ */
+
+#ifndef _IMG_IR_HW_H_
+#define _IMG_IR_HW_H_
+
+#include <linux/kernel.h>
+#include <media/rc-core.h>
+
+/* constants */
+
+#define IMG_IR_CODETYPE_PULSELEN	0x0	/* Sony */
+#define IMG_IR_CODETYPE_PULSEDIST	0x1	/* NEC, Toshiba, Micom, Sharp */
+#define IMG_IR_CODETYPE_BIPHASE		0x2	/* RC-5/6 */
+#define IMG_IR_CODETYPE_2BITPULSEPOS	0x3	/* RC-MM */
+
+
+/* Timing information */
+
+/**
+ * struct img_ir_control - Decoder control settings
+ * @decoden:	Primary decoder enable
+ * @code_type:	Decode type (see IMG_IR_CODETYPE_*)
+ * @hdrtog:	Detect header toggle symbol after leader symbol
+ * @ldrdec:	Don't discard leader if maximum width reached
+ * @decodinpol:	Decoder input polarity (1=active high)
+ * @bitorien:	Bit orientation (1=MSB first)
+ * @d1validsel:	Decoder 2 takes over if it detects valid data
+ * @bitinv:	Bit inversion switch (1=don't invert)
+ * @decodend2:	Secondary decoder enable (no leader symbol)
+ * @bitoriend2:	Bit orientation (1=MSB first)
+ * @bitinvd2:	Secondary decoder bit inversion switch (1=don't invert)
+ */
+struct img_ir_control {
+	unsigned decoden:1;
+	unsigned code_type:2;
+	unsigned hdrtog:1;
+	unsigned ldrdec:1;
+	unsigned decodinpol:1;
+	unsigned bitorien:1;
+	unsigned d1validsel:1;
+	unsigned bitinv:1;
+	unsigned decodend2:1;
+	unsigned bitoriend2:1;
+	unsigned bitinvd2:1;
+};
+
+/**
+ * struct img_ir_timing_range - range of timing values
+ * @min:	Minimum timing value
+ * @max:	Maximum timing value (if < @min, this will be set to @min during
+ *		preprocessing step, so it is normally not explicitly initialised
+ *		and is taken care of by the tolerance)
+ */
+struct img_ir_timing_range {
+	u16 min;
+	u16 max;
+};
+
+/**
+ * struct img_ir_symbol_timing - timing data for a symbol
+ * @pulse:	Timing range for the length of the pulse in this symbol
+ * @space:	Timing range for the length of the space in this symbol
+ */
+struct img_ir_symbol_timing {
+	struct img_ir_timing_range pulse;
+	struct img_ir_timing_range space;
+};
+
+/**
+ * struct img_ir_free_timing - timing data for free time symbol
+ * @minlen:	Minimum number of bits of data
+ * @maxlen:	Maximum number of bits of data
+ * @ft_min:	Minimum free time after message
+ */
+struct img_ir_free_timing {
+	/* measured in bits */
+	u8 minlen;
+	u8 maxlen;
+	u16 ft_min;
+};
+
+/**
+ * struct img_ir_timings - Timing values.
+ * @ldr:	Leader symbol timing data
+ * @s00:	Zero symbol timing data for primary decoder
+ * @s01:	One symbol timing data for primary decoder
+ * @s10:	Zero symbol timing data for secondary (no leader symbol) decoder
+ * @s11:	One symbol timing data for secondary (no leader symbol) decoder
+ * @ft:		Free time symbol timing data
+ */
+struct img_ir_timings {
+	struct img_ir_symbol_timing ldr, s00, s01, s10, s11;
+	struct img_ir_free_timing ft;
+};
+
+/**
+ * struct img_ir_filter - Filter IR events.
+ * @data:	Data to match.
+ * @mask:	Mask of bits to compare.
+ * @minlen:	Additional minimum number of bits.
+ * @maxlen:	Additional maximum number of bits.
+ */
+struct img_ir_filter {
+	u64 data;
+	u64 mask;
+	u8 minlen;
+	u8 maxlen;
+};
+
+/**
+ * struct img_ir_timing_regvals - Calculated timing register values.
+ * @ldr:	Leader symbol timing register value
+ * @s00:	Zero symbol timing register value for primary decoder
+ * @s01:	One symbol timing register value for primary decoder
+ * @s10:	Zero symbol timing register value for secondary decoder
+ * @s11:	One symbol timing register value for secondary decoder
+ * @ft:		Free time symbol timing register value
+ */
+struct img_ir_timing_regvals {
+	u32 ldr, s00, s01, s10, s11, ft;
+};
+
+#define IMG_IR_SCANCODE		0	/* new scancode */
+#define IMG_IR_REPEATCODE	1	/* repeat the previous code */
+
+/**
+ * struct img_ir_scancode_req - Scancode request data.
+ * @protocol:	Protocol code of received message (defaults to
+ *		RC_TYPE_UNKNOWN).
+ * @scancode:	Scan code of received message (must be written by
+ *		handler if IMG_IR_SCANCODE is returned).
+ * @toggle:	Toggle bit (defaults to 0).
+ */
+struct img_ir_scancode_req {
+	enum rc_type protocol;
+	u32 scancode;
+	u8 toggle;
+};
+
+/**
+ * struct img_ir_decoder - Decoder settings for an IR protocol.
+ * @type:	Protocol types bitmap.
+ * @tolerance:	Timing tolerance as a percentage (default 10%).
+ * @unit:	Unit of timings in nanoseconds (default 1 us).
+ * @timings:	Primary timings
+ * @rtimings:	Additional override timings while waiting for repeats.
+ * @repeat:	Maximum repeat interval (always in milliseconds).
+ * @control:	Control flags.
+ *
+ * @scancode:	Pointer to function to convert the IR data into a scancode (it
+ *		must be safe to execute in interrupt context).
+ *		Returns IMG_IR_SCANCODE to emit new scancode.
+ *		Returns IMG_IR_REPEATCODE to repeat previous code.
+ *		Returns -errno (e.g. -EINVAL) on error.
+ * @filter:	Pointer to function to convert scancode filter to raw hardware
+ *		filter. The minlen and maxlen fields will have been initialised
+ *		to the maximum range.
+ */
+struct img_ir_decoder {
+	/* core description */
+	u64				type;
+	unsigned int			tolerance;
+	unsigned int			unit;
+	struct img_ir_timings		timings;
+	struct img_ir_timings		rtimings;
+	unsigned int			repeat;
+	struct img_ir_control		control;
+
+	/* scancode logic */
+	int (*scancode)(int len, u64 raw, u64 enabled_protocols,
+			struct img_ir_scancode_req *request);
+	int (*filter)(const struct rc_scancode_filter *in,
+		      struct img_ir_filter *out, u64 protocols);
+};
+
+extern struct img_ir_decoder img_ir_nec;
+extern struct img_ir_decoder img_ir_jvc;
+extern struct img_ir_decoder img_ir_sony;
+extern struct img_ir_decoder img_ir_sharp;
+extern struct img_ir_decoder img_ir_sanyo;
+extern struct img_ir_decoder img_ir_rc5;
+extern struct img_ir_decoder img_ir_rc6;
+
+/**
+ * struct img_ir_reg_timings - Reg values for decoder timings at clock rate.
+ * @ctrl:	Processed control register value.
+ * @timings:	Processed primary timings.
+ * @rtimings:	Processed repeat timings.
+ */
+struct img_ir_reg_timings {
+	u32				ctrl;
+	struct img_ir_timing_regvals	timings;
+	struct img_ir_timing_regvals	rtimings;
+};
+
+struct img_ir_priv;
+
+#ifdef CONFIG_IR_IMG_HW
+
+enum img_ir_mode {
+	IMG_IR_M_NORMAL,
+	IMG_IR_M_REPEATING,
+#ifdef CONFIG_PM_SLEEP
+	IMG_IR_M_WAKE,
+#endif
+};
+
+/**
+ * struct img_ir_priv_hw - Private driver data for hardware decoder.
+ * @ct_quirks:		Quirk bits for each code type.
+ * @rdev:		Remote control device
+ * @clk_nb:		Notifier block for clock notify events.
+ * @end_timer:		Timer until repeat timeout.
+ * @suspend_timer:	Timer to re-enable protocol.
+ * @decoder:		Current decoder settings.
+ * @enabled_protocols:	Currently enabled protocols.
+ * @clk_hz:		Current core clock rate in Hz.
+ * @reg_timings:	Timing reg values for decoder at clock rate.
+ * @flags:		IMG_IR_F_*.
+ * @filters:		HW filters (derived from scancode filters).
+ * @mode:		Current decode mode.
+ * @stopping:		Indicates that decoder is being taken down and timers
+ *			should not be restarted.
+ * @suspend_irqen:	Saved IRQ enable mask over suspend.
+ * @quirk_suspend_irq:	Saved IRQ enable mask over quirk suspend timer.
+ */
+struct img_ir_priv_hw {
+	unsigned int			ct_quirks[4];
+	struct rc_dev			*rdev;
+	struct notifier_block		clk_nb;
+	struct timer_list		end_timer;
+	struct timer_list		suspend_timer;
+	const struct img_ir_decoder	*decoder;
+	u64				enabled_protocols;
+	unsigned long			clk_hz;
+	struct img_ir_reg_timings	reg_timings;
+	unsigned int			flags;
+	struct img_ir_filter		filters[RC_FILTER_MAX];
+
+	enum img_ir_mode		mode;
+	bool				stopping;
+	u32				suspend_irqen;
+	u32				quirk_suspend_irq;
+};
+
+static inline bool img_ir_hw_enabled(struct img_ir_priv_hw *hw)
+{
+	return hw->rdev;
+};
+
+void img_ir_isr_hw(struct img_ir_priv *priv, u32 irq_status);
+void img_ir_setup_hw(struct img_ir_priv *priv);
+int img_ir_probe_hw(struct img_ir_priv *priv);
+void img_ir_remove_hw(struct img_ir_priv *priv);
+
+#ifdef CONFIG_PM_SLEEP
+int img_ir_suspend(struct device *dev);
+int img_ir_resume(struct device *dev);
+#else
+#define img_ir_suspend NULL
+#define img_ir_resume NULL
+#endif
+
+#else
+
+struct img_ir_priv_hw {
+};
+
+static inline bool img_ir_hw_enabled(struct img_ir_priv_hw *hw)
+{
+	return false;
+};
+static inline void img_ir_isr_hw(struct img_ir_priv *priv, u32 irq_status)
+{
+}
+static inline void img_ir_setup_hw(struct img_ir_priv *priv)
+{
+}
+static inline int img_ir_probe_hw(struct img_ir_priv *priv)
+{
+	return -ENODEV;
+}
+static inline void img_ir_remove_hw(struct img_ir_priv *priv)
+{
+}
+
+#define img_ir_suspend NULL
+#define img_ir_resume NULL
+
+#endif /* CONFIG_IR_IMG_HW */
+
+#endif /* _IMG_IR_HW_H_ */
diff --git a/drivers/media/rc/img-ir/img-ir-jvc.c b/drivers/media/rc/img-ir/img-ir-jvc.c
new file mode 100644
index 0000000..d3e2fc0
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-jvc.c
@@ -0,0 +1,88 @@
+/*
+ * ImgTec IR Decoder setup for JVC protocol.
+ *
+ * Copyright 2012-2014 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include "img-ir-hw.h"
+
+/* Convert JVC data to a scancode */
+static int img_ir_jvc_scancode(int len, u64 raw, u64 enabled_protocols,
+			       struct img_ir_scancode_req *request)
+{
+	unsigned int cust, data;
+
+	if (len != 16)
+		return -EINVAL;
+
+	cust = (raw >> 0) & 0xff;
+	data = (raw >> 8) & 0xff;
+
+	request->protocol = RC_TYPE_JVC;
+	request->scancode = cust << 8 | data;
+	return IMG_IR_SCANCODE;
+}
+
+/* Convert JVC scancode to JVC data filter */
+static int img_ir_jvc_filter(const struct rc_scancode_filter *in,
+			     struct img_ir_filter *out, u64 protocols)
+{
+	unsigned int cust, data;
+	unsigned int cust_m, data_m;
+
+	cust   = (in->data >> 8) & 0xff;
+	cust_m = (in->mask >> 8) & 0xff;
+	data   = (in->data >> 0) & 0xff;
+	data_m = (in->mask >> 0) & 0xff;
+
+	out->data = cust   | data << 8;
+	out->mask = cust_m | data_m << 8;
+
+	return 0;
+}
+
+/*
+ * JVC decoder
+ * See also http://www.sbprojects.com/knowledge/ir/jvc.php
+ *          http://support.jvc.com/consumer/support/documents/RemoteCodes.pdf
+ */
+struct img_ir_decoder img_ir_jvc = {
+	.type = RC_BIT_JVC,
+	.control = {
+		.decoden = 1,
+		.code_type = IMG_IR_CODETYPE_PULSEDIST,
+	},
+	/* main timings */
+	.unit = 527500, /* 527.5 us */
+	.timings = {
+		/* leader symbol */
+		.ldr = {
+			.pulse = { 16	/* 8.44 ms */ },
+			.space = { 8	/* 4.22 ms */ },
+		},
+		/* 0 symbol */
+		.s00 = {
+			.pulse = { 1	/* 527.5 us +-60 us */ },
+			.space = { 1	/* 527.5 us */ },
+		},
+		/* 1 symbol */
+		.s01 = {
+			.pulse = { 1	/* 527.5 us +-60 us */ },
+			.space = { 3	/* 1.5825 ms +-40 us */ },
+		},
+		/* free time */
+		.ft = {
+			.minlen = 16,
+			.maxlen = 16,
+			.ft_min = 10,	/* 5.275 ms */
+		},
+	},
+	/* scancode logic */
+	.scancode = img_ir_jvc_scancode,
+	.filter = img_ir_jvc_filter,
+};
diff --git a/drivers/media/rc/img-ir/img-ir-nec.c b/drivers/media/rc/img-ir/img-ir-nec.c
new file mode 100644
index 0000000..27a7ea8
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-nec.c
@@ -0,0 +1,158 @@
+/*
+ * ImgTec IR Decoder setup for NEC protocol.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include "img-ir-hw.h"
+#include <linux/bitrev.h>
+
+/* Convert NEC data to a scancode */
+static int img_ir_nec_scancode(int len, u64 raw, u64 enabled_protocols,
+			       struct img_ir_scancode_req *request)
+{
+	unsigned int addr, addr_inv, data, data_inv;
+	/* a repeat code has no data */
+	if (!len)
+		return IMG_IR_REPEATCODE;
+	if (len != 32)
+		return -EINVAL;
+	/* raw encoding: ddDDaaAA */
+	addr     = (raw >>  0) & 0xff;
+	addr_inv = (raw >>  8) & 0xff;
+	data     = (raw >> 16) & 0xff;
+	data_inv = (raw >> 24) & 0xff;
+	if ((data_inv ^ data) != 0xff) {
+		/* 32-bit NEC (used by Apple and TiVo remotes) */
+		/* scan encoding: as transmitted, MSBit = first received bit */
+		request->scancode = bitrev8(addr)     << 24 |
+				bitrev8(addr_inv) << 16 |
+				bitrev8(data)     <<  8 |
+				bitrev8(data_inv);
+	} else if ((addr_inv ^ addr) != 0xff) {
+		/* Extended NEC */
+		/* scan encoding: AAaaDD */
+		request->scancode = addr     << 16 |
+				addr_inv <<  8 |
+				data;
+	} else {
+		/* Normal NEC */
+		/* scan encoding: AADD */
+		request->scancode = addr << 8 |
+				data;
+	}
+	request->protocol = RC_TYPE_NEC;
+	return IMG_IR_SCANCODE;
+}
+
+/* Convert NEC scancode to NEC data filter */
+static int img_ir_nec_filter(const struct rc_scancode_filter *in,
+			     struct img_ir_filter *out, u64 protocols)
+{
+	unsigned int addr, addr_inv, data, data_inv;
+	unsigned int addr_m, addr_inv_m, data_m, data_inv_m;
+
+	data       = in->data & 0xff;
+	data_m     = in->mask & 0xff;
+
+	if ((in->data | in->mask) & 0xff000000) {
+		/* 32-bit NEC (used by Apple and TiVo remotes) */
+		/* scan encoding: as transmitted, MSBit = first received bit */
+		addr       = bitrev8(in->data >> 24);
+		addr_m     = bitrev8(in->mask >> 24);
+		addr_inv   = bitrev8(in->data >> 16);
+		addr_inv_m = bitrev8(in->mask >> 16);
+		data       = bitrev8(in->data >>  8);
+		data_m     = bitrev8(in->mask >>  8);
+		data_inv   = bitrev8(in->data >>  0);
+		data_inv_m = bitrev8(in->mask >>  0);
+	} else if ((in->data | in->mask) & 0x00ff0000) {
+		/* Extended NEC */
+		/* scan encoding AAaaDD */
+		addr       = (in->data >> 16) & 0xff;
+		addr_m     = (in->mask >> 16) & 0xff;
+		addr_inv   = (in->data >>  8) & 0xff;
+		addr_inv_m = (in->mask >>  8) & 0xff;
+		data_inv   = data ^ 0xff;
+		data_inv_m = data_m;
+	} else {
+		/* Normal NEC */
+		/* scan encoding: AADD */
+		addr       = (in->data >>  8) & 0xff;
+		addr_m     = (in->mask >>  8) & 0xff;
+		addr_inv   = addr ^ 0xff;
+		addr_inv_m = addr_m;
+		data_inv   = data ^ 0xff;
+		data_inv_m = data_m;
+	}
+
+	/* raw encoding: ddDDaaAA */
+	out->data = data_inv << 24 |
+		    data     << 16 |
+		    addr_inv <<  8 |
+		    addr;
+	out->mask = data_inv_m << 24 |
+		    data_m     << 16 |
+		    addr_inv_m <<  8 |
+		    addr_m;
+	return 0;
+}
+
+/*
+ * NEC decoder
+ * See also http://www.sbprojects.com/knowledge/ir/nec.php
+ *        http://wiki.altium.com/display/ADOH/NEC+Infrared+Transmission+Protocol
+ */
+struct img_ir_decoder img_ir_nec = {
+	.type = RC_BIT_NEC,
+	.control = {
+		.decoden = 1,
+		.code_type = IMG_IR_CODETYPE_PULSEDIST,
+	},
+	/* main timings */
+	.unit = 562500, /* 562.5 us */
+	.timings = {
+		/* leader symbol */
+		.ldr = {
+			.pulse = { 16	/* 9ms */ },
+			.space = { 8	/* 4.5ms */ },
+		},
+		/* 0 symbol */
+		.s00 = {
+			.pulse = { 1	/* 562.5 us */ },
+			.space = { 1	/* 562.5 us */ },
+		},
+		/* 1 symbol */
+		.s01 = {
+			.pulse = { 1	/* 562.5 us */ },
+			.space = { 3	/* 1687.5 us */ },
+		},
+		/* free time */
+		.ft = {
+			.minlen = 32,
+			.maxlen = 32,
+			.ft_min = 10,	/* 5.625 ms */
+		},
+	},
+	/* repeat codes */
+	.repeat = 108,			/* 108 ms */
+	.rtimings = {
+		/* leader symbol */
+		.ldr = {
+			.space = { 4	/* 2.25 ms */ },
+		},
+		/* free time */
+		.ft = {
+			.minlen = 0,	/* repeat code has no data */
+			.maxlen = 0,
+		},
+	},
+	/* scancode logic */
+	.scancode = img_ir_nec_scancode,
+	.filter = img_ir_nec_filter,
+};
diff --git a/drivers/media/rc/img-ir/img-ir-raw.c b/drivers/media/rc/img-ir/img-ir-raw.c
new file mode 100644
index 0000000..33f37ed
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-raw.c
@@ -0,0 +1,156 @@
+/*
+ * ImgTec IR Raw Decoder found in PowerDown Controller.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ *
+ * 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 ties into the input subsystem using the RC-core in raw mode. Raw IR
+ * signal edges are reported and decoded by generic software decoders.
+ */
+
+#include <linux/spinlock.h>
+#include <media/rc-core.h>
+#include "img-ir.h"
+
+#define ECHO_TIMEOUT_MS 150	/* ms between echos */
+
+/* must be called with priv->lock held */
+static void img_ir_refresh_raw(struct img_ir_priv *priv, u32 irq_status)
+{
+	struct img_ir_priv_raw *raw = &priv->raw;
+	struct rc_dev *rc_dev = priv->raw.rdev;
+	int multiple;
+	u32 ir_status;
+
+	/* find whether both rise and fall was detected */
+	multiple = ((irq_status & IMG_IR_IRQ_EDGE) == IMG_IR_IRQ_EDGE);
+	/*
+	 * If so, we need to see if the level has actually changed.
+	 * If it's just noise that we didn't have time to process,
+	 * there's no point reporting it.
+	 */
+	ir_status = img_ir_read(priv, IMG_IR_STATUS) & IMG_IR_IRRXD;
+	if (multiple && ir_status == raw->last_status)
+		return;
+	raw->last_status = ir_status;
+
+	/* report the edge to the IR raw decoders */
+	if (ir_status) /* low */
+		ir_raw_event_store_edge(rc_dev, IR_SPACE);
+	else /* high */
+		ir_raw_event_store_edge(rc_dev, IR_PULSE);
+	ir_raw_event_handle(rc_dev);
+}
+
+/* called with priv->lock held */
+void img_ir_isr_raw(struct img_ir_priv *priv, u32 irq_status)
+{
+	struct img_ir_priv_raw *raw = &priv->raw;
+
+	/* check not removing */
+	if (!raw->rdev)
+		return;
+
+	img_ir_refresh_raw(priv, irq_status);
+
+	/* start / push back the echo timer */
+	mod_timer(&raw->timer, jiffies + msecs_to_jiffies(ECHO_TIMEOUT_MS));
+}
+
+/*
+ * Echo timer callback function.
+ * The raw decoders expect to get a final sample even if there are no edges, in
+ * order to be assured of the final space. If there are no edges for a certain
+ * time we use this timer to emit a final sample to satisfy them.
+ */
+static void img_ir_echo_timer(unsigned long arg)
+{
+	struct img_ir_priv *priv = (struct img_ir_priv *)arg;
+
+	spin_lock_irq(&priv->lock);
+
+	/* check not removing */
+	if (priv->raw.rdev)
+		/*
+		 * It's safe to pass irq_status=0 since it's only used to check
+		 * for double edges.
+		 */
+		img_ir_refresh_raw(priv, 0);
+
+	spin_unlock_irq(&priv->lock);
+}
+
+void img_ir_setup_raw(struct img_ir_priv *priv)
+{
+	u32 irq_en;
+
+	if (!priv->raw.rdev)
+		return;
+
+	/* clear and enable edge interrupts */
+	spin_lock_irq(&priv->lock);
+	irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+	irq_en |= IMG_IR_IRQ_EDGE;
+	img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE);
+	img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en);
+	spin_unlock_irq(&priv->lock);
+}
+
+int img_ir_probe_raw(struct img_ir_priv *priv)
+{
+	struct img_ir_priv_raw *raw = &priv->raw;
+	struct rc_dev *rdev;
+	int error;
+
+	/* Set up the echo timer */
+	setup_timer(&raw->timer, img_ir_echo_timer, (unsigned long)priv);
+
+	/* Allocate raw decoder */
+	raw->rdev = rdev = rc_allocate_device();
+	if (!rdev) {
+		dev_err(priv->dev, "cannot allocate raw input device\n");
+		return -ENOMEM;
+	}
+	rdev->priv = priv;
+	rdev->map_name = RC_MAP_EMPTY;
+	rdev->input_name = "IMG Infrared Decoder Raw";
+	rdev->driver_type = RC_DRIVER_IR_RAW;
+
+	/* Register raw decoder */
+	error = rc_register_device(rdev);
+	if (error) {
+		dev_err(priv->dev, "failed to register raw IR input device\n");
+		rc_free_device(rdev);
+		raw->rdev = NULL;
+		return error;
+	}
+
+	return 0;
+}
+
+void img_ir_remove_raw(struct img_ir_priv *priv)
+{
+	struct img_ir_priv_raw *raw = &priv->raw;
+	struct rc_dev *rdev = raw->rdev;
+	u32 irq_en;
+
+	if (!rdev)
+		return;
+
+	/* switch off and disable raw (edge) interrupts */
+	spin_lock_irq(&priv->lock);
+	raw->rdev = NULL;
+	irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
+	irq_en &= ~IMG_IR_IRQ_EDGE;
+	img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en);
+	img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE);
+	spin_unlock_irq(&priv->lock);
+
+	rc_unregister_device(rdev);
+
+	del_timer_sync(&raw->timer);
+}
diff --git a/drivers/media/rc/img-ir/img-ir-raw.h b/drivers/media/rc/img-ir/img-ir-raw.h
new file mode 100644
index 0000000..4c9b767
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-raw.h
@@ -0,0 +1,65 @@
+/*
+ * ImgTec IR Raw Decoder found in PowerDown Controller.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ *
+ * 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.
+ */
+
+#ifndef _IMG_IR_RAW_H_
+#define _IMG_IR_RAW_H_
+
+struct img_ir_priv;
+
+#ifdef CONFIG_IR_IMG_RAW
+
+/**
+ * struct img_ir_priv_raw - Private driver data for raw decoder.
+ * @rdev:		Raw remote control device
+ * @timer:		Timer to echo samples to keep soft decoders happy.
+ * @last_status:	Last raw status bits.
+ */
+struct img_ir_priv_raw {
+	struct rc_dev		*rdev;
+	struct timer_list	timer;
+	u32			last_status;
+};
+
+static inline bool img_ir_raw_enabled(struct img_ir_priv_raw *raw)
+{
+	return raw->rdev;
+};
+
+void img_ir_isr_raw(struct img_ir_priv *priv, u32 irq_status);
+void img_ir_setup_raw(struct img_ir_priv *priv);
+int img_ir_probe_raw(struct img_ir_priv *priv);
+void img_ir_remove_raw(struct img_ir_priv *priv);
+
+#else
+
+struct img_ir_priv_raw {
+};
+static inline bool img_ir_raw_enabled(struct img_ir_priv_raw *raw)
+{
+	return false;
+};
+static inline void img_ir_isr_raw(struct img_ir_priv *priv, u32 irq_status)
+{
+}
+static inline void img_ir_setup_raw(struct img_ir_priv *priv)
+{
+}
+static inline int img_ir_probe_raw(struct img_ir_priv *priv)
+{
+	return -ENODEV;
+}
+static inline void img_ir_remove_raw(struct img_ir_priv *priv)
+{
+}
+
+#endif /* CONFIG_IR_IMG_RAW */
+
+#endif /* _IMG_IR_RAW_H_ */
diff --git a/drivers/media/rc/img-ir/img-ir-rc5.c b/drivers/media/rc/img-ir/img-ir-rc5.c
new file mode 100644
index 0000000..a8a28a3
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-rc5.c
@@ -0,0 +1,88 @@
+/*
+ * ImgTec IR Decoder setup for Philips RC-5 protocol.
+ *
+ * Copyright 2012-2014 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include "img-ir-hw.h"
+
+/* Convert RC5 data to a scancode */
+static int img_ir_rc5_scancode(int len, u64 raw, u64 enabled_protocols,
+				struct img_ir_scancode_req *request)
+{
+	unsigned int addr, cmd, tgl, start;
+
+	/* Quirk in the decoder shifts everything by 2 to the left. */
+	raw   >>= 2;
+
+	start	=  (raw >> 13)	& 0x01;
+	tgl	=  (raw >> 11)	& 0x01;
+	addr	=  (raw >>  6)	& 0x1f;
+	cmd	=   raw		& 0x3f;
+	/*
+	 * 12th bit is used to extend the command in extended RC5 and has
+	 * no effect on standard RC5.
+	 */
+	cmd	+= ((raw >> 12) & 0x01) ? 0 : 0x40;
+
+	if (!start)
+		return -EINVAL;
+
+	request->protocol = RC_TYPE_RC5;
+	request->scancode = addr << 8 | cmd;
+	request->toggle   = tgl;
+	return IMG_IR_SCANCODE;
+}
+
+/* Convert RC5 scancode to RC5 data filter */
+static int img_ir_rc5_filter(const struct rc_scancode_filter *in,
+				 struct img_ir_filter *out, u64 protocols)
+{
+	/* Not supported by the hw. */
+	return -EINVAL;
+}
+
+/*
+ * RC-5 decoder
+ * see http://www.sbprojects.com/knowledge/ir/rc5.php
+ */
+struct img_ir_decoder img_ir_rc5 = {
+	.type      = RC_BIT_RC5,
+	.control   = {
+		.bitoriend2	= 1,
+		.code_type	= IMG_IR_CODETYPE_BIPHASE,
+		.decodend2	= 1,
+	},
+	/* main timings */
+	.tolerance	= 16,
+	.unit		= 888888, /* 1/36k*32=888.888microseconds */
+	.timings	= {
+		/* 10 symbol */
+		.s10 = {
+			.pulse	= { 1 },
+			.space	= { 1 },
+		},
+
+		/* 11 symbol */
+		.s11 = {
+			.pulse	= { 1 },
+			.space	= { 1 },
+		},
+
+		/* free time */
+		.ft  = {
+			.minlen = 14,
+			.maxlen = 14,
+			.ft_min = 5,
+		},
+	},
+
+	/* scancode logic */
+	.scancode	= img_ir_rc5_scancode,
+	.filter		= img_ir_rc5_filter,
+};
diff --git a/drivers/media/rc/img-ir/img-ir-rc6.c b/drivers/media/rc/img-ir/img-ir-rc6.c
new file mode 100644
index 0000000..de1e275
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-rc6.c
@@ -0,0 +1,117 @@
+/*
+ * ImgTec IR Decoder setup for Philips RC-6 protocol.
+ *
+ * Copyright 2012-2014 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include "img-ir-hw.h"
+
+/* Convert RC6 data to a scancode */
+static int img_ir_rc6_scancode(int len, u64 raw, u64 enabled_protocols,
+				struct img_ir_scancode_req *request)
+{
+	unsigned int addr, cmd, mode, trl1, trl2;
+
+	/*
+	 * Due to a side effect of the decoder handling the double length
+	 * Trailer bit, the header information is a bit scrambled, and the
+	 * raw data is shifted incorrectly.
+	 * This workaround effectively recovers the header bits.
+	 *
+	 * The Header field should look like this:
+	 *
+	 * StartBit ModeBit2 ModeBit1 ModeBit0 TrailerBit
+	 *
+	 * But what we get is:
+	 *
+	 * ModeBit2 ModeBit1 ModeBit0 TrailerBit1 TrailerBit2
+	 *
+	 * The start bit is not important to recover the scancode.
+	 */
+
+	raw	>>= 27;
+
+	trl1	= (raw >>  17)	& 0x01;
+	trl2	= (raw >>  16)	& 0x01;
+
+	mode	= (raw >>  18)	& 0x07;
+	addr	= (raw >>   8)	& 0xff;
+	cmd	=  raw		& 0xff;
+
+	/*
+	 * Due to the above explained irregularity the trailer bits cannot
+	 * have the same value.
+	 */
+	if (trl1 == trl2)
+		return -EINVAL;
+
+	/* Only mode 0 supported for now */
+	if (mode)
+		return -EINVAL;
+
+	request->protocol = RC_TYPE_RC6_0;
+	request->scancode = addr << 8 | cmd;
+	request->toggle	  = trl2;
+	return IMG_IR_SCANCODE;
+}
+
+/* Convert RC6 scancode to RC6 data filter */
+static int img_ir_rc6_filter(const struct rc_scancode_filter *in,
+				 struct img_ir_filter *out, u64 protocols)
+{
+	/* Not supported by the hw. */
+	return -EINVAL;
+}
+
+/*
+ * RC-6 decoder
+ * see http://www.sbprojects.com/knowledge/ir/rc6.php
+ */
+struct img_ir_decoder img_ir_rc6 = {
+	.type		= RC_BIT_RC6_0,
+	.control	= {
+		.bitorien	= 1,
+		.code_type	= IMG_IR_CODETYPE_BIPHASE,
+		.decoden	= 1,
+		.decodinpol	= 1,
+	},
+	/* main timings */
+	.tolerance	= 20,
+	/*
+	 * Due to a quirk in the img-ir decoder, default header values do
+	 * not work, the values described below were extracted from
+	 * successful RTL test cases.
+	 */
+	.timings	= {
+		/* leader symbol */
+		.ldr = {
+			.pulse	= { 650 },
+			.space	= { 660 },
+		},
+		/* 0 symbol */
+		.s00 = {
+			.pulse	= { 370 },
+			.space	= { 370 },
+		},
+		/* 01 symbol */
+		.s01 = {
+			.pulse	= { 370 },
+			.space	= { 370 },
+		},
+		/* free time */
+		.ft  = {
+			.minlen = 21,
+			.maxlen = 21,
+			.ft_min = 2666,	/* 2.666 ms */
+		},
+	},
+
+	/* scancode logic */
+	.scancode	= img_ir_rc6_scancode,
+	.filter		= img_ir_rc6_filter,
+};
diff --git a/drivers/media/rc/img-ir/img-ir-sanyo.c b/drivers/media/rc/img-ir/img-ir-sanyo.c
new file mode 100644
index 0000000..f394994
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-sanyo.c
@@ -0,0 +1,129 @@
+/*
+ * ImgTec IR Decoder setup for Sanyo protocol.
+ *
+ * Copyright 2012-2014 Imagination Technologies Ltd.
+ *
+ * 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.
+ *
+ * From ir-sanyo-decoder.c:
+ *
+ * This protocol uses the NEC protocol timings. However, data is formatted as:
+ *	13 bits Custom Code
+ *	13 bits NOT(Custom Code)
+ *	8 bits Key data
+ *	8 bits NOT(Key data)
+ *
+ * According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon
+ * Information for this protocol is available at the Sanyo LC7461 datasheet.
+ */
+
+#include "img-ir-hw.h"
+
+/* Convert Sanyo data to a scancode */
+static int img_ir_sanyo_scancode(int len, u64 raw, u64 enabled_protocols,
+				 struct img_ir_scancode_req *request)
+{
+	unsigned int addr, addr_inv, data, data_inv;
+	/* a repeat code has no data */
+	if (!len)
+		return IMG_IR_REPEATCODE;
+	if (len != 42)
+		return -EINVAL;
+	addr     = (raw >>  0) & 0x1fff;
+	addr_inv = (raw >> 13) & 0x1fff;
+	data     = (raw >> 26) & 0xff;
+	data_inv = (raw >> 34) & 0xff;
+	/* Validate data */
+	if ((data_inv ^ data) != 0xff)
+		return -EINVAL;
+	/* Validate address */
+	if ((addr_inv ^ addr) != 0x1fff)
+		return -EINVAL;
+
+	/* Normal Sanyo */
+	request->protocol = RC_TYPE_SANYO;
+	request->scancode = addr << 8 | data;
+	return IMG_IR_SCANCODE;
+}
+
+/* Convert Sanyo scancode to Sanyo data filter */
+static int img_ir_sanyo_filter(const struct rc_scancode_filter *in,
+			       struct img_ir_filter *out, u64 protocols)
+{
+	unsigned int addr, addr_inv, data, data_inv;
+	unsigned int addr_m, data_m;
+
+	data = in->data & 0xff;
+	data_m = in->mask & 0xff;
+	data_inv = data ^ 0xff;
+
+	if (in->data & 0xff700000)
+		return -EINVAL;
+
+	addr       = (in->data >> 8) & 0x1fff;
+	addr_m     = (in->mask >> 8) & 0x1fff;
+	addr_inv   = addr ^ 0x1fff;
+
+	out->data = (u64)data_inv << 34 |
+		    (u64)data     << 26 |
+			 addr_inv << 13 |
+			 addr;
+	out->mask = (u64)data_m << 34 |
+		    (u64)data_m << 26 |
+			 addr_m << 13 |
+			 addr_m;
+	return 0;
+}
+
+/* Sanyo decoder */
+struct img_ir_decoder img_ir_sanyo = {
+	.type = RC_BIT_SANYO,
+	.control = {
+		.decoden = 1,
+		.code_type = IMG_IR_CODETYPE_PULSEDIST,
+	},
+	/* main timings */
+	.unit = 562500, /* 562.5 us */
+	.timings = {
+		/* leader symbol */
+		.ldr = {
+			.pulse = { 16	/* 9ms */ },
+			.space = { 8	/* 4.5ms */ },
+		},
+		/* 0 symbol */
+		.s00 = {
+			.pulse = { 1	/* 562.5 us */ },
+			.space = { 1	/* 562.5 us */ },
+		},
+		/* 1 symbol */
+		.s01 = {
+			.pulse = { 1	/* 562.5 us */ },
+			.space = { 3	/* 1687.5 us */ },
+		},
+		/* free time */
+		.ft = {
+			.minlen = 42,
+			.maxlen = 42,
+			.ft_min = 10,	/* 5.625 ms */
+		},
+	},
+	/* repeat codes */
+	.repeat = 108,			/* 108 ms */
+	.rtimings = {
+		/* leader symbol */
+		.ldr = {
+			.space = { 4	/* 2.25 ms */ },
+		},
+		/* free time */
+		.ft = {
+			.minlen = 0,	/* repeat code has no data */
+			.maxlen = 0,
+		},
+	},
+	/* scancode logic */
+	.scancode = img_ir_sanyo_scancode,
+	.filter = img_ir_sanyo_filter,
+};
diff --git a/drivers/media/rc/img-ir/img-ir-sharp.c b/drivers/media/rc/img-ir/img-ir-sharp.c
new file mode 100644
index 0000000..fe5acc4
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-sharp.c
@@ -0,0 +1,106 @@
+/*
+ * ImgTec IR Decoder setup for Sharp protocol.
+ *
+ * Copyright 2012-2014 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include "img-ir-hw.h"
+
+/* Convert Sharp data to a scancode */
+static int img_ir_sharp_scancode(int len, u64 raw, u64 enabled_protocols,
+				 struct img_ir_scancode_req *request)
+{
+	unsigned int addr, cmd, exp, chk;
+
+	if (len != 15)
+		return -EINVAL;
+
+	addr = (raw >>   0) & 0x1f;
+	cmd  = (raw >>   5) & 0xff;
+	exp  = (raw >>  13) &  0x1;
+	chk  = (raw >>  14) &  0x1;
+
+	/* validate data */
+	if (!exp)
+		return -EINVAL;
+	if (chk)
+		/* probably the second half of the message */
+		return -EINVAL;
+
+	request->protocol = RC_TYPE_SHARP;
+	request->scancode = addr << 8 | cmd;
+	return IMG_IR_SCANCODE;
+}
+
+/* Convert Sharp scancode to Sharp data filter */
+static int img_ir_sharp_filter(const struct rc_scancode_filter *in,
+			       struct img_ir_filter *out, u64 protocols)
+{
+	unsigned int addr, cmd, exp = 0, chk = 0;
+	unsigned int addr_m, cmd_m, exp_m = 0, chk_m = 0;
+
+	addr   = (in->data >> 8) & 0x1f;
+	addr_m = (in->mask >> 8) & 0x1f;
+	cmd    = (in->data >> 0) & 0xff;
+	cmd_m  = (in->mask >> 0) & 0xff;
+	if (cmd_m) {
+		/* if filtering commands, we can only match the first part */
+		exp   = 1;
+		exp_m = 1;
+		chk   = 0;
+		chk_m = 1;
+	}
+
+	out->data = addr        |
+		    cmd   <<  5 |
+		    exp   << 13 |
+		    chk   << 14;
+	out->mask = addr_m      |
+		    cmd_m <<  5 |
+		    exp_m << 13 |
+		    chk_m << 14;
+
+	return 0;
+}
+
+/*
+ * Sharp decoder
+ * See also http://www.sbprojects.com/knowledge/ir/sharp.php
+ */
+struct img_ir_decoder img_ir_sharp = {
+	.type = RC_BIT_SHARP,
+	.control = {
+		.decoden = 0,
+		.decodend2 = 1,
+		.code_type = IMG_IR_CODETYPE_PULSEDIST,
+		.d1validsel = 1,
+	},
+	/* main timings */
+	.tolerance = 20,	/* 20% */
+	.timings = {
+		/* 0 symbol */
+		.s10 = {
+			.pulse = { 320	/* 320 us */ },
+			.space = { 680	/* 1 ms period */ },
+		},
+		/* 1 symbol */
+		.s11 = {
+			.pulse = { 320	/* 320 us */ },
+			.space = { 1680	/* 2 ms period */ },
+		},
+		/* free time */
+		.ft = {
+			.minlen = 15,
+			.maxlen = 15,
+			.ft_min = 5000,	/* 5 ms */
+		},
+	},
+	/* scancode logic */
+	.scancode = img_ir_sharp_scancode,
+	.filter = img_ir_sharp_filter,
+};
diff --git a/drivers/media/rc/img-ir/img-ir-sony.c b/drivers/media/rc/img-ir/img-ir-sony.c
new file mode 100644
index 0000000..7f7375f
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir-sony.c
@@ -0,0 +1,154 @@
+/*
+ * ImgTec IR Decoder setup for Sony (SIRC) protocol.
+ *
+ * Copyright 2012-2014 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include "img-ir-hw.h"
+
+/* Convert Sony data to a scancode */
+static int img_ir_sony_scancode(int len, u64 raw, u64 enabled_protocols,
+				struct img_ir_scancode_req *request)
+{
+	unsigned int dev, subdev, func;
+
+	switch (len) {
+	case 12:
+		if (!(enabled_protocols & RC_BIT_SONY12))
+			return -EINVAL;
+		func   = raw & 0x7f;	/* first 7 bits */
+		raw    >>= 7;
+		dev    = raw & 0x1f;	/* next 5 bits */
+		subdev = 0;
+		request->protocol = RC_TYPE_SONY12;
+		break;
+	case 15:
+		if (!(enabled_protocols & RC_BIT_SONY15))
+			return -EINVAL;
+		func   = raw & 0x7f;	/* first 7 bits */
+		raw    >>= 7;
+		dev    = raw & 0xff;	/* next 8 bits */
+		subdev = 0;
+		request->protocol = RC_TYPE_SONY15;
+		break;
+	case 20:
+		if (!(enabled_protocols & RC_BIT_SONY20))
+			return -EINVAL;
+		func   = raw & 0x7f;	/* first 7 bits */
+		raw    >>= 7;
+		dev    = raw & 0x1f;	/* next 5 bits */
+		raw    >>= 5;
+		subdev = raw & 0xff;	/* next 8 bits */
+		request->protocol = RC_TYPE_SONY20;
+		break;
+	default:
+		return -EINVAL;
+	}
+	request->scancode = dev << 16 | subdev << 8 | func;
+	return IMG_IR_SCANCODE;
+}
+
+/* Convert NEC scancode to NEC data filter */
+static int img_ir_sony_filter(const struct rc_scancode_filter *in,
+			      struct img_ir_filter *out, u64 protocols)
+{
+	unsigned int dev, subdev, func;
+	unsigned int dev_m, subdev_m, func_m;
+	unsigned int len = 0;
+
+	dev      = (in->data >> 16) & 0xff;
+	dev_m    = (in->mask >> 16) & 0xff;
+	subdev   = (in->data >> 8)  & 0xff;
+	subdev_m = (in->mask >> 8)  & 0xff;
+	func     = (in->data >> 0)  & 0x7f;
+	func_m   = (in->mask >> 0)  & 0x7f;
+
+	if (subdev & subdev_m) {
+		/* can't encode subdev and higher device bits */
+		if (dev & dev_m & 0xe0)
+			return -EINVAL;
+		/* subdevice (extended) bits only in 20 bit encoding */
+		if (!(protocols & RC_BIT_SONY20))
+			return -EINVAL;
+		len = 20;
+		dev_m &= 0x1f;
+	} else if (dev & dev_m & 0xe0) {
+		/* upper device bits only in 15 bit encoding */
+		if (!(protocols & RC_BIT_SONY15))
+			return -EINVAL;
+		len = 15;
+		subdev_m = 0;
+	} else {
+		/*
+		 * The hardware mask cannot distinguish high device bits and low
+		 * extended bits, so logically AND those bits of the masks
+		 * together.
+		 */
+		subdev_m &= (dev_m >> 5) | 0xf8;
+		dev_m &= 0x1f;
+	}
+
+	/* ensure there aren't any bits straying between fields */
+	dev &= dev_m;
+	subdev &= subdev_m;
+
+	/* write the hardware filter */
+	out->data = func          |
+		    dev      << 7 |
+		    subdev   << 15;
+	out->mask = func_m        |
+		    dev_m    << 7 |
+		    subdev_m << 15;
+
+	if (len) {
+		out->minlen = len;
+		out->maxlen = len;
+	}
+	return 0;
+}
+
+/*
+ * Sony SIRC decoder
+ * See also http://www.sbprojects.com/knowledge/ir/sirc.php
+ *          http://picprojects.org.uk/projects/sirc/sonysirc.pdf
+ */
+struct img_ir_decoder img_ir_sony = {
+	.type = RC_BIT_SONY12 | RC_BIT_SONY15 | RC_BIT_SONY20,
+	.control = {
+		.decoden = 1,
+		.code_type = IMG_IR_CODETYPE_PULSELEN,
+	},
+	/* main timings */
+	.unit = 600000, /* 600 us */
+	.timings = {
+		/* leader symbol */
+		.ldr = {
+			.pulse = { 4	/* 2.4 ms */ },
+			.space = { 1	/* 600 us */ },
+		},
+		/* 0 symbol */
+		.s00 = {
+			.pulse = { 1	/* 600 us */ },
+			.space = { 1	/* 600 us */ },
+		},
+		/* 1 symbol */
+		.s01 = {
+			.pulse = { 2	/* 1.2 ms */ },
+			.space = { 1	/* 600 us */ },
+		},
+		/* free time */
+		.ft = {
+			.minlen = 12,
+			.maxlen = 20,
+			.ft_min = 10,	/* 6 ms */
+		},
+	},
+	/* scancode logic */
+	.scancode = img_ir_sony_scancode,
+	.filter = img_ir_sony_filter,
+};
diff --git a/drivers/media/rc/img-ir/img-ir.h b/drivers/media/rc/img-ir/img-ir.h
new file mode 100644
index 0000000..f1387c0
--- /dev/null
+++ b/drivers/media/rc/img-ir/img-ir.h
@@ -0,0 +1,173 @@
+/*
+ * ImgTec IR Decoder found in PowerDown Controller.
+ *
+ * Copyright 2010-2014 Imagination Technologies Ltd.
+ *
+ * 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.
+ */
+
+#ifndef _IMG_IR_H_
+#define _IMG_IR_H_
+
+#include <linux/io.h>
+#include <linux/spinlock.h>
+
+#include "img-ir-raw.h"
+#include "img-ir-hw.h"
+
+/* registers */
+
+/* relative to the start of the IR block of registers */
+#define IMG_IR_CONTROL		0x00
+#define IMG_IR_STATUS		0x04
+#define IMG_IR_DATA_LW		0x08
+#define IMG_IR_DATA_UP		0x0c
+#define IMG_IR_LEAD_SYMB_TIMING	0x10
+#define IMG_IR_S00_SYMB_TIMING	0x14
+#define IMG_IR_S01_SYMB_TIMING	0x18
+#define IMG_IR_S10_SYMB_TIMING	0x1c
+#define IMG_IR_S11_SYMB_TIMING	0x20
+#define IMG_IR_FREE_SYMB_TIMING	0x24
+#define IMG_IR_POW_MOD_PARAMS	0x28
+#define IMG_IR_POW_MOD_ENABLE	0x2c
+#define IMG_IR_IRQ_MSG_DATA_LW	0x30
+#define IMG_IR_IRQ_MSG_DATA_UP	0x34
+#define IMG_IR_IRQ_MSG_MASK_LW	0x38
+#define IMG_IR_IRQ_MSG_MASK_UP	0x3c
+#define IMG_IR_IRQ_ENABLE	0x40
+#define IMG_IR_IRQ_STATUS	0x44
+#define IMG_IR_IRQ_CLEAR	0x48
+#define IMG_IR_IRCORE_ID	0xf0
+#define IMG_IR_CORE_REV		0xf4
+#define IMG_IR_CORE_DES1	0xf8
+#define IMG_IR_CORE_DES2	0xfc
+
+
+/* field masks */
+
+/* IMG_IR_CONTROL */
+#define IMG_IR_DECODEN		0x40000000
+#define IMG_IR_CODETYPE		0x30000000
+#define IMG_IR_CODETYPE_SHIFT		28
+#define IMG_IR_HDRTOG		0x08000000
+#define IMG_IR_LDRDEC		0x04000000
+#define IMG_IR_DECODINPOL	0x02000000	/* active high */
+#define IMG_IR_BITORIEN		0x01000000	/* MSB first */
+#define IMG_IR_D1VALIDSEL	0x00008000
+#define IMG_IR_BITINV		0x00000040	/* don't invert */
+#define IMG_IR_DECODEND2	0x00000010
+#define IMG_IR_BITORIEND2	0x00000002	/* MSB first */
+#define IMG_IR_BITINVD2		0x00000001	/* don't invert */
+
+/* IMG_IR_STATUS */
+#define IMG_IR_RXDVALD2		0x00001000
+#define IMG_IR_IRRXD		0x00000400
+#define IMG_IR_TOGSTATE		0x00000200
+#define IMG_IR_RXDVAL		0x00000040
+#define IMG_IR_RXDLEN		0x0000003f
+#define IMG_IR_RXDLEN_SHIFT		0
+
+/* IMG_IR_LEAD_SYMB_TIMING, IMG_IR_Sxx_SYMB_TIMING */
+#define IMG_IR_PD_MAX		0xff000000
+#define IMG_IR_PD_MAX_SHIFT		24
+#define IMG_IR_PD_MIN		0x00ff0000
+#define IMG_IR_PD_MIN_SHIFT		16
+#define IMG_IR_W_MAX		0x0000ff00
+#define IMG_IR_W_MAX_SHIFT		8
+#define IMG_IR_W_MIN		0x000000ff
+#define IMG_IR_W_MIN_SHIFT		0
+
+/* IMG_IR_FREE_SYMB_TIMING */
+#define IMG_IR_MAXLEN		0x0007e000
+#define IMG_IR_MAXLEN_SHIFT		13
+#define IMG_IR_MINLEN		0x00001f00
+#define IMG_IR_MINLEN_SHIFT		8
+#define IMG_IR_FT_MIN		0x000000ff
+#define IMG_IR_FT_MIN_SHIFT		0
+
+/* IMG_IR_POW_MOD_PARAMS */
+#define IMG_IR_PERIOD_LEN	0x3f000000
+#define IMG_IR_PERIOD_LEN_SHIFT		24
+#define IMG_IR_PERIOD_DUTY	0x003f0000
+#define IMG_IR_PERIOD_DUTY_SHIFT	16
+#define IMG_IR_STABLE_STOP	0x00003f00
+#define IMG_IR_STABLE_STOP_SHIFT	8
+#define IMG_IR_STABLE_START	0x0000003f
+#define IMG_IR_STABLE_START_SHIFT	0
+
+/* IMG_IR_POW_MOD_ENABLE */
+#define IMG_IR_POWER_OUT_EN	0x00000002
+#define IMG_IR_POWER_MOD_EN	0x00000001
+
+/* IMG_IR_IRQ_ENABLE, IMG_IR_IRQ_STATUS, IMG_IR_IRQ_CLEAR */
+#define IMG_IR_IRQ_DEC2_ERR	0x00000080
+#define IMG_IR_IRQ_DEC_ERR	0x00000040
+#define IMG_IR_IRQ_ACT_LEVEL	0x00000020
+#define IMG_IR_IRQ_FALL_EDGE	0x00000010
+#define IMG_IR_IRQ_RISE_EDGE	0x00000008
+#define IMG_IR_IRQ_DATA_MATCH	0x00000004
+#define IMG_IR_IRQ_DATA2_VALID	0x00000002
+#define IMG_IR_IRQ_DATA_VALID	0x00000001
+#define IMG_IR_IRQ_ALL		0x000000ff
+#define IMG_IR_IRQ_EDGE		(IMG_IR_IRQ_FALL_EDGE | IMG_IR_IRQ_RISE_EDGE)
+
+/* IMG_IR_CORE_ID */
+#define IMG_IR_CORE_ID		0x00ff0000
+#define IMG_IR_CORE_ID_SHIFT		16
+#define IMG_IR_CORE_CONFIG	0x0000ffff
+#define IMG_IR_CORE_CONFIG_SHIFT	0
+
+/* IMG_IR_CORE_REV */
+#define IMG_IR_DESIGNER		0xff000000
+#define IMG_IR_DESIGNER_SHIFT		24
+#define IMG_IR_MAJOR_REV	0x00ff0000
+#define IMG_IR_MAJOR_REV_SHIFT		16
+#define IMG_IR_MINOR_REV	0x0000ff00
+#define IMG_IR_MINOR_REV_SHIFT		8
+#define IMG_IR_MAINT_REV	0x000000ff
+#define IMG_IR_MAINT_REV_SHIFT		0
+
+struct device;
+struct clk;
+
+/**
+ * struct img_ir_priv - Private driver data.
+ * @dev:		Platform device.
+ * @irq:		IRQ number.
+ * @clk:		Input clock.
+ * @sys_clk:		System clock.
+ * @reg_base:		Iomem base address of IR register block.
+ * @lock:		Protects IR registers and variables in this struct.
+ * @raw:		Driver data for raw decoder.
+ * @hw:			Driver data for hardware decoder.
+ */
+struct img_ir_priv {
+	struct device		*dev;
+	int			irq;
+	struct clk		*clk;
+	struct clk		*sys_clk;
+	void __iomem		*reg_base;
+	spinlock_t		lock;
+
+	struct img_ir_priv_raw	raw;
+	struct img_ir_priv_hw	hw;
+};
+
+/* Hardware access */
+
+static inline void img_ir_write(struct img_ir_priv *priv,
+				unsigned int reg_offs, unsigned int data)
+{
+	iowrite32(data, priv->reg_base + reg_offs);
+}
+
+static inline unsigned int img_ir_read(struct img_ir_priv *priv,
+				       unsigned int reg_offs)
+{
+	return ioread32(priv->reg_base + reg_offs);
+}
+
+#endif /* _IMG_IR_H_ */
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
new file mode 100644
index 0000000..7a16e9e
--- /dev/null
+++ b/drivers/media/rc/imon.c
@@ -0,0 +1,2596 @@
+/*
+ *   imon.c:	input and display driver for SoundGraph iMON IR/VFD/LCD
+ *
+ *   Copyright(C) 2010  Jarod Wilson <jarod@wilsonet.com>
+ *   Portions based on the original lirc_imon driver,
+ *	Copyright(C) 2004  Venky Raju(dev@venky.ws)
+ *
+ *   Huge thanks to R. Geoff Newbury for invaluable debugging on the
+ *   0xffdc iMON devices, and for sending me one to hack on, without
+ *   which the support for them wouldn't be nearly as good. Thanks
+ *   also to the numerous 0xffdc device owners that tested auto-config
+ *   support for me and provided debug dumps from their devices.
+ *
+ *   imon 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/ratelimit.h>
+
+#include <linux/input.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <media/rc-core.h>
+
+#include <linux/time.h>
+#include <linux/timer.h>
+
+#define MOD_AUTHOR	"Jarod Wilson <jarod@wilsonet.com>"
+#define MOD_DESC	"Driver for SoundGraph iMON MultiMedia IR/Display"
+#define MOD_NAME	"imon"
+#define MOD_VERSION	"0.9.4"
+
+#define DISPLAY_MINOR_BASE	144
+#define DEVICE_NAME	"lcd%d"
+
+#define BUF_CHUNK_SIZE	8
+#define BUF_SIZE	128
+
+#define BIT_DURATION	250	/* each bit received is 250us */
+
+#define IMON_CLOCK_ENABLE_PACKETS	2
+
+/*** P R O T O T Y P E S ***/
+
+/* USB Callback prototypes */
+static int imon_probe(struct usb_interface *interface,
+		      const struct usb_device_id *id);
+static void imon_disconnect(struct usb_interface *interface);
+static void usb_rx_callback_intf0(struct urb *urb);
+static void usb_rx_callback_intf1(struct urb *urb);
+static void usb_tx_callback(struct urb *urb);
+
+/* suspend/resume support */
+static int imon_resume(struct usb_interface *intf);
+static int imon_suspend(struct usb_interface *intf, pm_message_t message);
+
+/* Display file_operations function prototypes */
+static int display_open(struct inode *inode, struct file *file);
+static int display_close(struct inode *inode, struct file *file);
+
+/* VFD write operation */
+static ssize_t vfd_write(struct file *file, const char __user *buf,
+			 size_t n_bytes, loff_t *pos);
+
+/* LCD file_operations override function prototypes */
+static ssize_t lcd_write(struct file *file, const char __user *buf,
+			 size_t n_bytes, loff_t *pos);
+
+/*** G L O B A L S ***/
+
+struct imon_panel_key_table {
+	u64 hw_code;
+	u32 keycode;
+};
+
+struct imon_usb_dev_descr {
+	__u16 flags;
+#define IMON_NO_FLAGS 0
+#define IMON_NEED_20MS_PKT_DELAY 1
+	struct imon_panel_key_table key_table[];
+};
+
+struct imon_context {
+	struct device *dev;
+	/* Newer devices have two interfaces */
+	struct usb_device *usbdev_intf0;
+	struct usb_device *usbdev_intf1;
+
+	bool display_supported;		/* not all controllers do */
+	bool display_isopen;		/* display port has been opened */
+	bool rf_device;			/* true if iMON 2.4G LT/DT RF device */
+	bool rf_isassociating;		/* RF remote associating */
+	bool dev_present_intf0;		/* USB device presence, interface 0 */
+	bool dev_present_intf1;		/* USB device presence, interface 1 */
+
+	struct mutex lock;		/* to lock this object */
+	wait_queue_head_t remove_ok;	/* For unexpected USB disconnects */
+
+	struct usb_endpoint_descriptor *rx_endpoint_intf0;
+	struct usb_endpoint_descriptor *rx_endpoint_intf1;
+	struct usb_endpoint_descriptor *tx_endpoint;
+	struct urb *rx_urb_intf0;
+	struct urb *rx_urb_intf1;
+	struct urb *tx_urb;
+	bool tx_control;
+	unsigned char usb_rx_buf[8];
+	unsigned char usb_tx_buf[8];
+	unsigned int send_packet_delay;
+
+	struct tx_t {
+		unsigned char data_buf[35];	/* user data buffer */
+		struct completion finished;	/* wait for write to finish */
+		bool busy;			/* write in progress */
+		int status;			/* status of tx completion */
+	} tx;
+
+	u16 vendor;			/* usb vendor ID */
+	u16 product;			/* usb product ID */
+
+	struct rc_dev *rdev;		/* rc-core device for remote */
+	struct input_dev *idev;		/* input device for panel & IR mouse */
+	struct input_dev *touch;	/* input device for touchscreen */
+
+	spinlock_t kc_lock;		/* make sure we get keycodes right */
+	u32 kc;				/* current input keycode */
+	u32 last_keycode;		/* last reported input keycode */
+	u32 rc_scancode;		/* the computed remote scancode */
+	u8 rc_toggle;			/* the computed remote toggle bit */
+	u64 rc_type;			/* iMON or MCE (RC6) IR protocol? */
+	bool release_code;		/* some keys send a release code */
+
+	u8 display_type;		/* store the display type */
+	bool pad_mouse;			/* toggle kbd(0)/mouse(1) mode */
+
+	char name_rdev[128];		/* rc input device name */
+	char phys_rdev[64];		/* rc input device phys path */
+
+	char name_idev[128];		/* input device name */
+	char phys_idev[64];		/* input device phys path */
+
+	char name_touch[128];		/* touch screen name */
+	char phys_touch[64];		/* touch screen phys path */
+	struct timer_list ttimer;	/* touch screen timer */
+	int touch_x;			/* x coordinate on touchscreen */
+	int touch_y;			/* y coordinate on touchscreen */
+	struct imon_usb_dev_descr *dev_descr; /* device description with key
+						 table for front panels */
+};
+
+#define TOUCH_TIMEOUT	(HZ/30)
+
+/* vfd character device file operations */
+static const struct file_operations vfd_fops = {
+	.owner		= THIS_MODULE,
+	.open		= &display_open,
+	.write		= &vfd_write,
+	.release	= &display_close,
+	.llseek		= noop_llseek,
+};
+
+/* lcd character device file operations */
+static const struct file_operations lcd_fops = {
+	.owner		= THIS_MODULE,
+	.open		= &display_open,
+	.write		= &lcd_write,
+	.release	= &display_close,
+	.llseek		= noop_llseek,
+};
+
+enum {
+	IMON_DISPLAY_TYPE_AUTO = 0,
+	IMON_DISPLAY_TYPE_VFD  = 1,
+	IMON_DISPLAY_TYPE_LCD  = 2,
+	IMON_DISPLAY_TYPE_VGA  = 3,
+	IMON_DISPLAY_TYPE_NONE = 4,
+};
+
+enum {
+	IMON_KEY_IMON	= 0,
+	IMON_KEY_MCE	= 1,
+	IMON_KEY_PANEL	= 2,
+};
+
+static struct usb_class_driver imon_vfd_class = {
+	.name		= DEVICE_NAME,
+	.fops		= &vfd_fops,
+	.minor_base	= DISPLAY_MINOR_BASE,
+};
+
+static struct usb_class_driver imon_lcd_class = {
+	.name		= DEVICE_NAME,
+	.fops		= &lcd_fops,
+	.minor_base	= DISPLAY_MINOR_BASE,
+};
+
+/* imon receiver front panel/knob key table */
+static const struct imon_usb_dev_descr imon_default_table = {
+	.flags = IMON_NO_FLAGS,
+	.key_table = {
+		{ 0x000000000f00ffeell, KEY_MEDIA }, /* Go */
+		{ 0x000000001200ffeell, KEY_UP },
+		{ 0x000000001300ffeell, KEY_DOWN },
+		{ 0x000000001400ffeell, KEY_LEFT },
+		{ 0x000000001500ffeell, KEY_RIGHT },
+		{ 0x000000001600ffeell, KEY_ENTER },
+		{ 0x000000001700ffeell, KEY_ESC },
+		{ 0x000000001f00ffeell, KEY_AUDIO },
+		{ 0x000000002000ffeell, KEY_VIDEO },
+		{ 0x000000002100ffeell, KEY_CAMERA },
+		{ 0x000000002700ffeell, KEY_DVD },
+		{ 0x000000002300ffeell, KEY_TV },
+		{ 0x000000002b00ffeell, KEY_EXIT },
+		{ 0x000000002c00ffeell, KEY_SELECT },
+		{ 0x000000002d00ffeell, KEY_MENU },
+		{ 0x000000000500ffeell, KEY_PREVIOUS },
+		{ 0x000000000700ffeell, KEY_REWIND },
+		{ 0x000000000400ffeell, KEY_STOP },
+		{ 0x000000003c00ffeell, KEY_PLAYPAUSE },
+		{ 0x000000000800ffeell, KEY_FASTFORWARD },
+		{ 0x000000000600ffeell, KEY_NEXT },
+		{ 0x000000010000ffeell, KEY_RIGHT },
+		{ 0x000001000000ffeell, KEY_LEFT },
+		{ 0x000000003d00ffeell, KEY_SELECT },
+		{ 0x000100000000ffeell, KEY_VOLUMEUP },
+		{ 0x010000000000ffeell, KEY_VOLUMEDOWN },
+		{ 0x000000000100ffeell, KEY_MUTE },
+		/* 0xffdc iMON MCE VFD */
+		{ 0x00010000ffffffeell, KEY_VOLUMEUP },
+		{ 0x01000000ffffffeell, KEY_VOLUMEDOWN },
+		{ 0x00000001ffffffeell, KEY_MUTE },
+		{ 0x0000000fffffffeell, KEY_MEDIA },
+		{ 0x00000012ffffffeell, KEY_UP },
+		{ 0x00000013ffffffeell, KEY_DOWN },
+		{ 0x00000014ffffffeell, KEY_LEFT },
+		{ 0x00000015ffffffeell, KEY_RIGHT },
+		{ 0x00000016ffffffeell, KEY_ENTER },
+		{ 0x00000017ffffffeell, KEY_ESC },
+		/* iMON Knob values */
+		{ 0x000100ffffffffeell, KEY_VOLUMEUP },
+		{ 0x010000ffffffffeell, KEY_VOLUMEDOWN },
+		{ 0x000008ffffffffeell, KEY_MUTE },
+		{ 0, KEY_RESERVED },
+	}
+};
+
+static const struct imon_usb_dev_descr imon_OEM_VFD = {
+	.flags = IMON_NEED_20MS_PKT_DELAY,
+	.key_table = {
+		{ 0x000000000f00ffeell, KEY_MEDIA }, /* Go */
+		{ 0x000000001200ffeell, KEY_UP },
+		{ 0x000000001300ffeell, KEY_DOWN },
+		{ 0x000000001400ffeell, KEY_LEFT },
+		{ 0x000000001500ffeell, KEY_RIGHT },
+		{ 0x000000001600ffeell, KEY_ENTER },
+		{ 0x000000001700ffeell, KEY_ESC },
+		{ 0x000000001f00ffeell, KEY_AUDIO },
+		{ 0x000000002b00ffeell, KEY_EXIT },
+		{ 0x000000002c00ffeell, KEY_SELECT },
+		{ 0x000000002d00ffeell, KEY_MENU },
+		{ 0x000000000500ffeell, KEY_PREVIOUS },
+		{ 0x000000000700ffeell, KEY_REWIND },
+		{ 0x000000000400ffeell, KEY_STOP },
+		{ 0x000000003c00ffeell, KEY_PLAYPAUSE },
+		{ 0x000000000800ffeell, KEY_FASTFORWARD },
+		{ 0x000000000600ffeell, KEY_NEXT },
+		{ 0x000000010000ffeell, KEY_RIGHT },
+		{ 0x000001000000ffeell, KEY_LEFT },
+		{ 0x000000003d00ffeell, KEY_SELECT },
+		{ 0x000100000000ffeell, KEY_VOLUMEUP },
+		{ 0x010000000000ffeell, KEY_VOLUMEDOWN },
+		{ 0x000000000100ffeell, KEY_MUTE },
+		/* 0xffdc iMON MCE VFD */
+		{ 0x00010000ffffffeell, KEY_VOLUMEUP },
+		{ 0x01000000ffffffeell, KEY_VOLUMEDOWN },
+		{ 0x00000001ffffffeell, KEY_MUTE },
+		{ 0x0000000fffffffeell, KEY_MEDIA },
+		{ 0x00000012ffffffeell, KEY_UP },
+		{ 0x00000013ffffffeell, KEY_DOWN },
+		{ 0x00000014ffffffeell, KEY_LEFT },
+		{ 0x00000015ffffffeell, KEY_RIGHT },
+		{ 0x00000016ffffffeell, KEY_ENTER },
+		{ 0x00000017ffffffeell, KEY_ESC },
+		/* iMON Knob values */
+		{ 0x000100ffffffffeell, KEY_VOLUMEUP },
+		{ 0x010000ffffffffeell, KEY_VOLUMEDOWN },
+		{ 0x000008ffffffffeell, KEY_MUTE },
+		{ 0, KEY_RESERVED },
+	}
+};
+
+/* imon receiver front panel/knob key table for DH102*/
+static const struct imon_usb_dev_descr imon_DH102 = {
+	.flags = IMON_NO_FLAGS,
+	.key_table = {
+		{ 0x000100000000ffeell, KEY_VOLUMEUP },
+		{ 0x010000000000ffeell, KEY_VOLUMEDOWN },
+		{ 0x000000010000ffeell, KEY_MUTE },
+		{ 0x0000000f0000ffeell, KEY_MEDIA },
+		{ 0x000000120000ffeell, KEY_UP },
+		{ 0x000000130000ffeell, KEY_DOWN },
+		{ 0x000000140000ffeell, KEY_LEFT },
+		{ 0x000000150000ffeell, KEY_RIGHT },
+		{ 0x000000160000ffeell, KEY_ENTER },
+		{ 0x000000170000ffeell, KEY_ESC },
+		{ 0x0000002b0000ffeell, KEY_EXIT },
+		{ 0x0000002c0000ffeell, KEY_SELECT },
+		{ 0x0000002d0000ffeell, KEY_MENU },
+		{ 0, KEY_RESERVED }
+	}
+};
+
+/*
+ * USB Device ID for iMON USB Control Boards
+ *
+ * The Windows drivers contain 6 different inf files, more or less one for
+ * each new device until the 0x0034-0x0046 devices, which all use the same
+ * driver. Some of the devices in the 34-46 range haven't been definitively
+ * identified yet. Early devices have either a TriGem Computer, Inc. or a
+ * Samsung vendor ID (0x0aa8 and 0x04e8 respectively), while all later
+ * devices use the SoundGraph vendor ID (0x15c2). This driver only supports
+ * the ffdc and later devices, which do onboard decoding.
+ */
+static struct usb_device_id imon_usb_id_table[] = {
+	/*
+	 * Several devices with this same device ID, all use iMON_PAD.inf
+	 * SoundGraph iMON PAD (IR & VFD)
+	 * SoundGraph iMON PAD (IR & LCD)
+	 * SoundGraph iMON Knob (IR only)
+	 */
+	{ USB_DEVICE(0x15c2, 0xffdc),
+	  .driver_info = (unsigned long)&imon_default_table },
+
+	/*
+	 * Newer devices, all driven by the latest iMON Windows driver, full
+	 * list of device IDs extracted via 'strings Setup/data1.hdr |grep 15c2'
+	 * Need user input to fill in details on unknown devices.
+	 */
+	/* SoundGraph iMON OEM Touch LCD (IR & 7" VGA LCD) */
+	{ USB_DEVICE(0x15c2, 0x0034),
+	  .driver_info = (unsigned long)&imon_DH102 },
+	/* SoundGraph iMON OEM Touch LCD (IR & 4.3" VGA LCD) */
+	{ USB_DEVICE(0x15c2, 0x0035),
+	  .driver_info = (unsigned long)&imon_default_table},
+	/* SoundGraph iMON OEM VFD (IR & VFD) */
+	{ USB_DEVICE(0x15c2, 0x0036),
+	  .driver_info = (unsigned long)&imon_OEM_VFD },
+	/* device specifics unknown */
+	{ USB_DEVICE(0x15c2, 0x0037),
+	  .driver_info = (unsigned long)&imon_default_table},
+	/* SoundGraph iMON OEM LCD (IR & LCD) */
+	{ USB_DEVICE(0x15c2, 0x0038),
+	  .driver_info = (unsigned long)&imon_default_table},
+	/* SoundGraph iMON UltraBay (IR & LCD) */
+	{ USB_DEVICE(0x15c2, 0x0039),
+	  .driver_info = (unsigned long)&imon_default_table},
+	/* device specifics unknown */
+	{ USB_DEVICE(0x15c2, 0x003a),
+	  .driver_info = (unsigned long)&imon_default_table},
+	/* device specifics unknown */
+	{ USB_DEVICE(0x15c2, 0x003b),
+	  .driver_info = (unsigned long)&imon_default_table},
+	/* SoundGraph iMON OEM Inside (IR only) */
+	{ USB_DEVICE(0x15c2, 0x003c),
+	  .driver_info = (unsigned long)&imon_default_table},
+	/* device specifics unknown */
+	{ USB_DEVICE(0x15c2, 0x003d),
+	  .driver_info = (unsigned long)&imon_default_table},
+	/* device specifics unknown */
+	{ USB_DEVICE(0x15c2, 0x003e),
+	  .driver_info = (unsigned long)&imon_default_table},
+	/* device specifics unknown */
+	{ USB_DEVICE(0x15c2, 0x003f),
+	  .driver_info = (unsigned long)&imon_default_table},
+	/* device specifics unknown */
+	{ USB_DEVICE(0x15c2, 0x0040),
+	  .driver_info = (unsigned long)&imon_default_table},
+	/* SoundGraph iMON MINI (IR only) */
+	{ USB_DEVICE(0x15c2, 0x0041),
+	  .driver_info = (unsigned long)&imon_default_table},
+	/* Antec Veris Multimedia Station EZ External (IR only) */
+	{ USB_DEVICE(0x15c2, 0x0042),
+	  .driver_info = (unsigned long)&imon_default_table},
+	/* Antec Veris Multimedia Station Basic Internal (IR only) */
+	{ USB_DEVICE(0x15c2, 0x0043),
+	  .driver_info = (unsigned long)&imon_default_table},
+	/* Antec Veris Multimedia Station Elite (IR & VFD) */
+	{ USB_DEVICE(0x15c2, 0x0044),
+	  .driver_info = (unsigned long)&imon_default_table},
+	/* Antec Veris Multimedia Station Premiere (IR & LCD) */
+	{ USB_DEVICE(0x15c2, 0x0045),
+	  .driver_info = (unsigned long)&imon_default_table},
+	/* device specifics unknown */
+	{ USB_DEVICE(0x15c2, 0x0046),
+	  .driver_info = (unsigned long)&imon_default_table},
+	{}
+};
+
+/* USB Device data */
+static struct usb_driver imon_driver = {
+	.name		= MOD_NAME,
+	.probe		= imon_probe,
+	.disconnect	= imon_disconnect,
+	.suspend	= imon_suspend,
+	.resume		= imon_resume,
+	.id_table	= imon_usb_id_table,
+};
+
+/* to prevent races between open() and disconnect(), probing, etc */
+static DEFINE_MUTEX(driver_lock);
+
+/* Module bookkeeping bits */
+MODULE_AUTHOR(MOD_AUTHOR);
+MODULE_DESCRIPTION(MOD_DESC);
+MODULE_VERSION(MOD_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, imon_usb_id_table);
+
+static bool debug;
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes (default: no)");
+
+/* lcd, vfd, vga or none? should be auto-detected, but can be overridden... */
+static int display_type;
+module_param(display_type, int, S_IRUGO);
+MODULE_PARM_DESC(display_type, "Type of attached display. 0=autodetect, "
+		 "1=vfd, 2=lcd, 3=vga, 4=none (default: autodetect)");
+
+static int pad_stabilize = 1;
+module_param(pad_stabilize, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(pad_stabilize, "Apply stabilization algorithm to iMON PAD "
+		 "presses in arrow key mode. 0=disable, 1=enable (default).");
+
+/*
+ * In certain use cases, mouse mode isn't really helpful, and could actually
+ * cause confusion, so allow disabling it when the IR device is open.
+ */
+static bool nomouse;
+module_param(nomouse, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(nomouse, "Disable mouse input device mode when IR device is "
+		 "open. 0=don't disable, 1=disable. (default: don't disable)");
+
+/* threshold at which a pad push registers as an arrow key in kbd mode */
+static int pad_thresh;
+module_param(pad_thresh, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(pad_thresh, "Threshold at which a pad push registers as an "
+		 "arrow key in kbd mode (default: 28)");
+
+
+static void free_imon_context(struct imon_context *ictx)
+{
+	struct device *dev = ictx->dev;
+
+	usb_free_urb(ictx->tx_urb);
+	usb_free_urb(ictx->rx_urb_intf0);
+	usb_free_urb(ictx->rx_urb_intf1);
+	kfree(ictx);
+
+	dev_dbg(dev, "%s: iMON context freed\n", __func__);
+}
+
+/**
+ * Called when the Display device (e.g. /dev/lcd0)
+ * is opened by the application.
+ */
+static int display_open(struct inode *inode, struct file *file)
+{
+	struct usb_interface *interface;
+	struct imon_context *ictx = NULL;
+	int subminor;
+	int retval = 0;
+
+	/* prevent races with disconnect */
+	mutex_lock(&driver_lock);
+
+	subminor = iminor(inode);
+	interface = usb_find_interface(&imon_driver, subminor);
+	if (!interface) {
+		pr_err("could not find interface for minor %d\n", subminor);
+		retval = -ENODEV;
+		goto exit;
+	}
+	ictx = usb_get_intfdata(interface);
+
+	if (!ictx) {
+		pr_err("no context found for minor %d\n", subminor);
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	mutex_lock(&ictx->lock);
+
+	if (!ictx->display_supported) {
+		pr_err("display not supported by device\n");
+		retval = -ENODEV;
+	} else if (ictx->display_isopen) {
+		pr_err("display port is already open\n");
+		retval = -EBUSY;
+	} else {
+		ictx->display_isopen = true;
+		file->private_data = ictx;
+		dev_dbg(ictx->dev, "display port opened\n");
+	}
+
+	mutex_unlock(&ictx->lock);
+
+exit:
+	mutex_unlock(&driver_lock);
+	return retval;
+}
+
+/**
+ * Called when the display device (e.g. /dev/lcd0)
+ * is closed by the application.
+ */
+static int display_close(struct inode *inode, struct file *file)
+{
+	struct imon_context *ictx = NULL;
+	int retval = 0;
+
+	ictx = file->private_data;
+
+	if (!ictx) {
+		pr_err("no context for device\n");
+		return -ENODEV;
+	}
+
+	mutex_lock(&ictx->lock);
+
+	if (!ictx->display_supported) {
+		pr_err("display not supported by device\n");
+		retval = -ENODEV;
+	} else if (!ictx->display_isopen) {
+		pr_err("display is not open\n");
+		retval = -EIO;
+	} else {
+		ictx->display_isopen = false;
+		dev_dbg(ictx->dev, "display port closed\n");
+	}
+
+	mutex_unlock(&ictx->lock);
+	return retval;
+}
+
+/**
+ * Sends a packet to the device -- this function must be called with
+ * ictx->lock held, or its unlock/lock sequence while waiting for tx
+ * to complete can/will lead to a deadlock.
+ */
+static int send_packet(struct imon_context *ictx)
+{
+	unsigned int pipe;
+	unsigned long timeout;
+	int interval = 0;
+	int retval = 0;
+	struct usb_ctrlrequest *control_req = NULL;
+
+	/* Check if we need to use control or interrupt urb */
+	if (!ictx->tx_control) {
+		pipe = usb_sndintpipe(ictx->usbdev_intf0,
+				      ictx->tx_endpoint->bEndpointAddress);
+		interval = ictx->tx_endpoint->bInterval;
+
+		usb_fill_int_urb(ictx->tx_urb, ictx->usbdev_intf0, pipe,
+				 ictx->usb_tx_buf,
+				 sizeof(ictx->usb_tx_buf),
+				 usb_tx_callback, ictx, interval);
+
+		ictx->tx_urb->actual_length = 0;
+	} else {
+		/* fill request into kmalloc'ed space: */
+		control_req = kmalloc(sizeof(struct usb_ctrlrequest),
+				      GFP_KERNEL);
+		if (control_req == NULL)
+			return -ENOMEM;
+
+		/* setup packet is '21 09 0200 0001 0008' */
+		control_req->bRequestType = 0x21;
+		control_req->bRequest = 0x09;
+		control_req->wValue = cpu_to_le16(0x0200);
+		control_req->wIndex = cpu_to_le16(0x0001);
+		control_req->wLength = cpu_to_le16(0x0008);
+
+		/* control pipe is endpoint 0x00 */
+		pipe = usb_sndctrlpipe(ictx->usbdev_intf0, 0);
+
+		/* build the control urb */
+		usb_fill_control_urb(ictx->tx_urb, ictx->usbdev_intf0,
+				     pipe, (unsigned char *)control_req,
+				     ictx->usb_tx_buf,
+				     sizeof(ictx->usb_tx_buf),
+				     usb_tx_callback, ictx);
+		ictx->tx_urb->actual_length = 0;
+	}
+
+	init_completion(&ictx->tx.finished);
+	ictx->tx.busy = true;
+	smp_rmb(); /* ensure later readers know we're busy */
+
+	retval = usb_submit_urb(ictx->tx_urb, GFP_KERNEL);
+	if (retval) {
+		ictx->tx.busy = false;
+		smp_rmb(); /* ensure later readers know we're not busy */
+		pr_err_ratelimited("error submitting urb(%d)\n", retval);
+	} else {
+		/* Wait for transmission to complete (or abort) */
+		mutex_unlock(&ictx->lock);
+		retval = wait_for_completion_interruptible(
+				&ictx->tx.finished);
+		if (retval) {
+			usb_kill_urb(ictx->tx_urb);
+			pr_err_ratelimited("task interrupted\n");
+		}
+		mutex_lock(&ictx->lock);
+
+		retval = ictx->tx.status;
+		if (retval)
+			pr_err_ratelimited("packet tx failed (%d)\n", retval);
+	}
+
+	kfree(control_req);
+
+	/*
+	 * Induce a mandatory delay before returning, as otherwise,
+	 * send_packet can get called so rapidly as to overwhelm the device,
+	 * particularly on faster systems and/or those with quirky usb.
+	 */
+	timeout = msecs_to_jiffies(ictx->send_packet_delay);
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout(timeout);
+
+	return retval;
+}
+
+/**
+ * Sends an associate packet to the iMON 2.4G.
+ *
+ * This might not be such a good idea, since it has an id collision with
+ * some versions of the "IR & VFD" combo. The only way to determine if it
+ * is an RF version is to look at the product description string. (Which
+ * we currently do not fetch).
+ */
+static int send_associate_24g(struct imon_context *ictx)
+{
+	int retval;
+	const unsigned char packet[8] = { 0x01, 0x00, 0x00, 0x00,
+					  0x00, 0x00, 0x00, 0x20 };
+
+	if (!ictx) {
+		pr_err("no context for device\n");
+		return -ENODEV;
+	}
+
+	if (!ictx->dev_present_intf0) {
+		pr_err("no iMON device present\n");
+		return -ENODEV;
+	}
+
+	memcpy(ictx->usb_tx_buf, packet, sizeof(packet));
+	retval = send_packet(ictx);
+
+	return retval;
+}
+
+/**
+ * Sends packets to setup and show clock on iMON display
+ *
+ * Arguments: year - last 2 digits of year, month - 1..12,
+ * day - 1..31, dow - day of the week (0-Sun...6-Sat),
+ * hour - 0..23, minute - 0..59, second - 0..59
+ */
+static int send_set_imon_clock(struct imon_context *ictx,
+			       unsigned int year, unsigned int month,
+			       unsigned int day, unsigned int dow,
+			       unsigned int hour, unsigned int minute,
+			       unsigned int second)
+{
+	unsigned char clock_enable_pkt[IMON_CLOCK_ENABLE_PACKETS][8];
+	int retval = 0;
+	int i;
+
+	if (!ictx) {
+		pr_err("no context for device\n");
+		return -ENODEV;
+	}
+
+	switch (ictx->display_type) {
+	case IMON_DISPLAY_TYPE_LCD:
+		clock_enable_pkt[0][0] = 0x80;
+		clock_enable_pkt[0][1] = year;
+		clock_enable_pkt[0][2] = month-1;
+		clock_enable_pkt[0][3] = day;
+		clock_enable_pkt[0][4] = hour;
+		clock_enable_pkt[0][5] = minute;
+		clock_enable_pkt[0][6] = second;
+
+		clock_enable_pkt[1][0] = 0x80;
+		clock_enable_pkt[1][1] = 0;
+		clock_enable_pkt[1][2] = 0;
+		clock_enable_pkt[1][3] = 0;
+		clock_enable_pkt[1][4] = 0;
+		clock_enable_pkt[1][5] = 0;
+		clock_enable_pkt[1][6] = 0;
+
+		if (ictx->product == 0xffdc) {
+			clock_enable_pkt[0][7] = 0x50;
+			clock_enable_pkt[1][7] = 0x51;
+		} else {
+			clock_enable_pkt[0][7] = 0x88;
+			clock_enable_pkt[1][7] = 0x8a;
+		}
+
+		break;
+
+	case IMON_DISPLAY_TYPE_VFD:
+		clock_enable_pkt[0][0] = year;
+		clock_enable_pkt[0][1] = month-1;
+		clock_enable_pkt[0][2] = day;
+		clock_enable_pkt[0][3] = dow;
+		clock_enable_pkt[0][4] = hour;
+		clock_enable_pkt[0][5] = minute;
+		clock_enable_pkt[0][6] = second;
+		clock_enable_pkt[0][7] = 0x40;
+
+		clock_enable_pkt[1][0] = 0;
+		clock_enable_pkt[1][1] = 0;
+		clock_enable_pkt[1][2] = 1;
+		clock_enable_pkt[1][3] = 0;
+		clock_enable_pkt[1][4] = 0;
+		clock_enable_pkt[1][5] = 0;
+		clock_enable_pkt[1][6] = 0;
+		clock_enable_pkt[1][7] = 0x42;
+
+		break;
+
+	default:
+		return -ENODEV;
+	}
+
+	for (i = 0; i < IMON_CLOCK_ENABLE_PACKETS; i++) {
+		memcpy(ictx->usb_tx_buf, clock_enable_pkt[i], 8);
+		retval = send_packet(ictx);
+		if (retval) {
+			pr_err("send_packet failed for packet %d\n", i);
+			break;
+		}
+	}
+
+	return retval;
+}
+
+/**
+ * These are the sysfs functions to handle the association on the iMON 2.4G LT.
+ */
+static ssize_t show_associate_remote(struct device *d,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct imon_context *ictx = dev_get_drvdata(d);
+
+	if (!ictx)
+		return -ENODEV;
+
+	mutex_lock(&ictx->lock);
+	if (ictx->rf_isassociating)
+		strcpy(buf, "associating\n");
+	else
+		strcpy(buf, "closed\n");
+
+	dev_info(d, "Visit http://www.lirc.org/html/imon-24g.html for "
+		 "instructions on how to associate your iMON 2.4G DT/LT "
+		 "remote\n");
+	mutex_unlock(&ictx->lock);
+	return strlen(buf);
+}
+
+static ssize_t store_associate_remote(struct device *d,
+				      struct device_attribute *attr,
+				      const char *buf, size_t count)
+{
+	struct imon_context *ictx;
+
+	ictx = dev_get_drvdata(d);
+
+	if (!ictx)
+		return -ENODEV;
+
+	mutex_lock(&ictx->lock);
+	ictx->rf_isassociating = true;
+	send_associate_24g(ictx);
+	mutex_unlock(&ictx->lock);
+
+	return count;
+}
+
+/**
+ * sysfs functions to control internal imon clock
+ */
+static ssize_t show_imon_clock(struct device *d,
+			       struct device_attribute *attr, char *buf)
+{
+	struct imon_context *ictx = dev_get_drvdata(d);
+	size_t len;
+
+	if (!ictx)
+		return -ENODEV;
+
+	mutex_lock(&ictx->lock);
+
+	if (!ictx->display_supported) {
+		len = snprintf(buf, PAGE_SIZE, "Not supported.");
+	} else {
+		len = snprintf(buf, PAGE_SIZE,
+			"To set the clock on your iMON display:\n"
+			"# date \"+%%y %%m %%d %%w %%H %%M %%S\" > imon_clock\n"
+			"%s", ictx->display_isopen ?
+			"\nNOTE: imon device must be closed\n" : "");
+	}
+
+	mutex_unlock(&ictx->lock);
+
+	return len;
+}
+
+static ssize_t store_imon_clock(struct device *d,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct imon_context *ictx = dev_get_drvdata(d);
+	ssize_t retval;
+	unsigned int year, month, day, dow, hour, minute, second;
+
+	if (!ictx)
+		return -ENODEV;
+
+	mutex_lock(&ictx->lock);
+
+	if (!ictx->display_supported) {
+		retval = -ENODEV;
+		goto exit;
+	} else if (ictx->display_isopen) {
+		retval = -EBUSY;
+		goto exit;
+	}
+
+	if (sscanf(buf, "%u %u %u %u %u %u %u",	&year, &month, &day, &dow,
+		   &hour, &minute, &second) != 7) {
+		retval = -EINVAL;
+		goto exit;
+	}
+
+	if ((month < 1 || month > 12) ||
+	    (day < 1 || day > 31) || (dow > 6) ||
+	    (hour > 23) || (minute > 59) || (second > 59)) {
+		retval = -EINVAL;
+		goto exit;
+	}
+
+	retval = send_set_imon_clock(ictx, year, month, day, dow,
+				     hour, minute, second);
+	if (retval)
+		goto exit;
+
+	retval = count;
+exit:
+	mutex_unlock(&ictx->lock);
+
+	return retval;
+}
+
+
+static DEVICE_ATTR(imon_clock, S_IWUSR | S_IRUGO, show_imon_clock,
+		   store_imon_clock);
+
+static DEVICE_ATTR(associate_remote, S_IWUSR | S_IRUGO, show_associate_remote,
+		   store_associate_remote);
+
+static struct attribute *imon_display_sysfs_entries[] = {
+	&dev_attr_imon_clock.attr,
+	NULL
+};
+
+static struct attribute_group imon_display_attr_group = {
+	.attrs = imon_display_sysfs_entries
+};
+
+static struct attribute *imon_rf_sysfs_entries[] = {
+	&dev_attr_associate_remote.attr,
+	NULL
+};
+
+static struct attribute_group imon_rf_attr_group = {
+	.attrs = imon_rf_sysfs_entries
+};
+
+/**
+ * Writes data to the VFD.  The iMON VFD is 2x16 characters
+ * and requires data in 5 consecutive USB interrupt packets,
+ * each packet but the last carrying 7 bytes.
+ *
+ * I don't know if the VFD board supports features such as
+ * scrolling, clearing rows, blanking, etc. so at
+ * the caller must provide a full screen of data.  If fewer
+ * than 32 bytes are provided spaces will be appended to
+ * generate a full screen.
+ */
+static ssize_t vfd_write(struct file *file, const char __user *buf,
+			 size_t n_bytes, loff_t *pos)
+{
+	int i;
+	int offset;
+	int seq;
+	int retval = 0;
+	struct imon_context *ictx;
+	const unsigned char vfd_packet6[] = {
+		0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF };
+
+	ictx = file->private_data;
+	if (!ictx) {
+		pr_err_ratelimited("no context for device\n");
+		return -ENODEV;
+	}
+
+	mutex_lock(&ictx->lock);
+
+	if (!ictx->dev_present_intf0) {
+		pr_err_ratelimited("no iMON device present\n");
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	if (n_bytes <= 0 || n_bytes > 32) {
+		pr_err_ratelimited("invalid payload size\n");
+		retval = -EINVAL;
+		goto exit;
+	}
+
+	if (copy_from_user(ictx->tx.data_buf, buf, n_bytes)) {
+		retval = -EFAULT;
+		goto exit;
+	}
+
+	/* Pad with spaces */
+	for (i = n_bytes; i < 32; ++i)
+		ictx->tx.data_buf[i] = ' ';
+
+	for (i = 32; i < 35; ++i)
+		ictx->tx.data_buf[i] = 0xFF;
+
+	offset = 0;
+	seq = 0;
+
+	do {
+		memcpy(ictx->usb_tx_buf, ictx->tx.data_buf + offset, 7);
+		ictx->usb_tx_buf[7] = (unsigned char) seq;
+
+		retval = send_packet(ictx);
+		if (retval) {
+			pr_err_ratelimited("send packet #%d failed\n", seq / 2);
+			goto exit;
+		} else {
+			seq += 2;
+			offset += 7;
+		}
+
+	} while (offset < 35);
+
+	/* Send packet #6 */
+	memcpy(ictx->usb_tx_buf, &vfd_packet6, sizeof(vfd_packet6));
+	ictx->usb_tx_buf[7] = (unsigned char) seq;
+	retval = send_packet(ictx);
+	if (retval)
+		pr_err_ratelimited("send packet #%d failed\n", seq / 2);
+
+exit:
+	mutex_unlock(&ictx->lock);
+
+	return (!retval) ? n_bytes : retval;
+}
+
+/**
+ * Writes data to the LCD.  The iMON OEM LCD screen expects 8-byte
+ * packets. We accept data as 16 hexadecimal digits, followed by a
+ * newline (to make it easy to drive the device from a command-line
+ * -- even though the actual binary data is a bit complicated).
+ *
+ * The device itself is not a "traditional" text-mode display. It's
+ * actually a 16x96 pixel bitmap display. That means if you want to
+ * display text, you've got to have your own "font" and translate the
+ * text into bitmaps for display. This is really flexible (you can
+ * display whatever diacritics you need, and so on), but it's also
+ * a lot more complicated than most LCDs...
+ */
+static ssize_t lcd_write(struct file *file, const char __user *buf,
+			 size_t n_bytes, loff_t *pos)
+{
+	int retval = 0;
+	struct imon_context *ictx;
+
+	ictx = file->private_data;
+	if (!ictx) {
+		pr_err_ratelimited("no context for device\n");
+		return -ENODEV;
+	}
+
+	mutex_lock(&ictx->lock);
+
+	if (!ictx->display_supported) {
+		pr_err_ratelimited("no iMON display present\n");
+		retval = -ENODEV;
+		goto exit;
+	}
+
+	if (n_bytes != 8) {
+		pr_err_ratelimited("invalid payload size: %d (expected 8)\n",
+				   (int)n_bytes);
+		retval = -EINVAL;
+		goto exit;
+	}
+
+	if (copy_from_user(ictx->usb_tx_buf, buf, 8)) {
+		retval = -EFAULT;
+		goto exit;
+	}
+
+	retval = send_packet(ictx);
+	if (retval) {
+		pr_err_ratelimited("send packet failed!\n");
+		goto exit;
+	} else {
+		dev_dbg(ictx->dev, "%s: write %d bytes to LCD\n",
+			__func__, (int) n_bytes);
+	}
+exit:
+	mutex_unlock(&ictx->lock);
+	return (!retval) ? n_bytes : retval;
+}
+
+/**
+ * Callback function for USB core API: transmit data
+ */
+static void usb_tx_callback(struct urb *urb)
+{
+	struct imon_context *ictx;
+
+	if (!urb)
+		return;
+	ictx = (struct imon_context *)urb->context;
+	if (!ictx)
+		return;
+
+	ictx->tx.status = urb->status;
+
+	/* notify waiters that write has finished */
+	ictx->tx.busy = false;
+	smp_rmb(); /* ensure later readers know we're not busy */
+	complete(&ictx->tx.finished);
+}
+
+/**
+ * report touchscreen input
+ */
+static void imon_touch_display_timeout(unsigned long data)
+{
+	struct imon_context *ictx = (struct imon_context *)data;
+
+	if (ictx->display_type != IMON_DISPLAY_TYPE_VGA)
+		return;
+
+	input_report_abs(ictx->touch, ABS_X, ictx->touch_x);
+	input_report_abs(ictx->touch, ABS_Y, ictx->touch_y);
+	input_report_key(ictx->touch, BTN_TOUCH, 0x00);
+	input_sync(ictx->touch);
+}
+
+/**
+ * iMON IR receivers support two different signal sets -- those used by
+ * the iMON remotes, and those used by the Windows MCE remotes (which is
+ * really just RC-6), but only one or the other at a time, as the signals
+ * are decoded onboard the receiver.
+ *
+ * This function gets called two different ways, one way is from
+ * rc_register_device, for initial protocol selection/setup, and the other is
+ * via a userspace-initiated protocol change request, either by direct sysfs
+ * prodding or by something like ir-keytable. In the rc_register_device case,
+ * the imon context lock is already held, but when initiated from userspace,
+ * it is not, so we must acquire it prior to calling send_packet, which
+ * requires that the lock is held.
+ */
+static int imon_ir_change_protocol(struct rc_dev *rc, u64 *rc_type)
+{
+	int retval;
+	struct imon_context *ictx = rc->priv;
+	struct device *dev = ictx->dev;
+	bool unlock = false;
+	unsigned char ir_proto_packet[] = {
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86 };
+
+	if (*rc_type && !(*rc_type & rc->allowed_protocols))
+		dev_warn(dev, "Looks like you're trying to use an IR protocol "
+			 "this device does not support\n");
+
+	if (*rc_type & RC_BIT_RC6_MCE) {
+		dev_dbg(dev, "Configuring IR receiver for MCE protocol\n");
+		ir_proto_packet[0] = 0x01;
+		*rc_type = RC_BIT_RC6_MCE;
+	} else if (*rc_type & RC_BIT_OTHER) {
+		dev_dbg(dev, "Configuring IR receiver for iMON protocol\n");
+		if (!pad_stabilize)
+			dev_dbg(dev, "PAD stabilize functionality disabled\n");
+		/* ir_proto_packet[0] = 0x00; // already the default */
+		*rc_type = RC_BIT_OTHER;
+	} else {
+		dev_warn(dev, "Unsupported IR protocol specified, overriding "
+			 "to iMON IR protocol\n");
+		if (!pad_stabilize)
+			dev_dbg(dev, "PAD stabilize functionality disabled\n");
+		/* ir_proto_packet[0] = 0x00; // already the default */
+		*rc_type = RC_BIT_OTHER;
+	}
+
+	memcpy(ictx->usb_tx_buf, &ir_proto_packet, sizeof(ir_proto_packet));
+
+	if (!mutex_is_locked(&ictx->lock)) {
+		unlock = true;
+		mutex_lock(&ictx->lock);
+	}
+
+	retval = send_packet(ictx);
+	if (retval)
+		goto out;
+
+	ictx->rc_type = *rc_type;
+	ictx->pad_mouse = false;
+
+out:
+	if (unlock)
+		mutex_unlock(&ictx->lock);
+
+	return retval;
+}
+
+static inline int tv2int(const struct timeval *a, const struct timeval *b)
+{
+	int usecs = 0;
+	int sec   = 0;
+
+	if (b->tv_usec > a->tv_usec) {
+		usecs = 1000000;
+		sec--;
+	}
+
+	usecs += a->tv_usec - b->tv_usec;
+
+	sec += a->tv_sec - b->tv_sec;
+	sec *= 1000;
+	usecs /= 1000;
+	sec += usecs;
+
+	if (sec < 0)
+		sec = 1000;
+
+	return sec;
+}
+
+/**
+ * The directional pad behaves a bit differently, depending on whether this is
+ * one of the older ffdc devices or a newer device. Newer devices appear to
+ * have a higher resolution matrix for more precise mouse movement, but it
+ * makes things overly sensitive in keyboard mode, so we do some interesting
+ * contortions to make it less touchy. Older devices run through the same
+ * routine with shorter timeout and a smaller threshold.
+ */
+static int stabilize(int a, int b, u16 timeout, u16 threshold)
+{
+	struct timeval ct;
+	static struct timeval prev_time = {0, 0};
+	static struct timeval hit_time  = {0, 0};
+	static int x, y, prev_result, hits;
+	int result = 0;
+	int msec, msec_hit;
+
+	do_gettimeofday(&ct);
+	msec = tv2int(&ct, &prev_time);
+	msec_hit = tv2int(&ct, &hit_time);
+
+	if (msec > 100) {
+		x = 0;
+		y = 0;
+		hits = 0;
+	}
+
+	x += a;
+	y += b;
+
+	prev_time = ct;
+
+	if (abs(x) > threshold || abs(y) > threshold) {
+		if (abs(y) > abs(x))
+			result = (y > 0) ? 0x7F : 0x80;
+		else
+			result = (x > 0) ? 0x7F00 : 0x8000;
+
+		x = 0;
+		y = 0;
+
+		if (result == prev_result) {
+			hits++;
+
+			if (hits > 3) {
+				switch (result) {
+				case 0x7F:
+					y = 17 * threshold / 30;
+					break;
+				case 0x80:
+					y -= 17 * threshold / 30;
+					break;
+				case 0x7F00:
+					x = 17 * threshold / 30;
+					break;
+				case 0x8000:
+					x -= 17 * threshold / 30;
+					break;
+				}
+			}
+
+			if (hits == 2 && msec_hit < timeout) {
+				result = 0;
+				hits = 1;
+			}
+		} else {
+			prev_result = result;
+			hits = 1;
+			hit_time = ct;
+		}
+	}
+
+	return result;
+}
+
+static u32 imon_remote_key_lookup(struct imon_context *ictx, u32 scancode)
+{
+	u32 keycode;
+	u32 release;
+	bool is_release_code = false;
+
+	/* Look for the initial press of a button */
+	keycode = rc_g_keycode_from_table(ictx->rdev, scancode);
+	ictx->rc_toggle = 0x0;
+	ictx->rc_scancode = scancode;
+
+	/* Look for the release of a button */
+	if (keycode == KEY_RESERVED) {
+		release = scancode & ~0x4000;
+		keycode = rc_g_keycode_from_table(ictx->rdev, release);
+		if (keycode != KEY_RESERVED)
+			is_release_code = true;
+	}
+
+	ictx->release_code = is_release_code;
+
+	return keycode;
+}
+
+static u32 imon_mce_key_lookup(struct imon_context *ictx, u32 scancode)
+{
+	u32 keycode;
+
+#define MCE_KEY_MASK 0x7000
+#define MCE_TOGGLE_BIT 0x8000
+
+	/*
+	 * On some receivers, mce keys decode to 0x8000f04xx and 0x8000f84xx
+	 * (the toggle bit flipping between alternating key presses), while
+	 * on other receivers, we see 0x8000f74xx and 0x8000ff4xx. To keep
+	 * the table trim, we always or in the bits to look up 0x8000ff4xx,
+	 * but we can't or them into all codes, as some keys are decoded in
+	 * a different way w/o the same use of the toggle bit...
+	 */
+	if (scancode & 0x80000000)
+		scancode = scancode | MCE_KEY_MASK | MCE_TOGGLE_BIT;
+
+	ictx->rc_scancode = scancode;
+	keycode = rc_g_keycode_from_table(ictx->rdev, scancode);
+
+	/* not used in mce mode, but make sure we know its false */
+	ictx->release_code = false;
+
+	return keycode;
+}
+
+static u32 imon_panel_key_lookup(struct imon_context *ictx, u64 code)
+{
+	int i;
+	u32 keycode = KEY_RESERVED;
+	struct imon_panel_key_table *key_table = ictx->dev_descr->key_table;
+
+	for (i = 0; key_table[i].hw_code != 0; i++) {
+		if (key_table[i].hw_code == (code | 0xffee)) {
+			keycode = key_table[i].keycode;
+			break;
+		}
+	}
+	ictx->release_code = false;
+	return keycode;
+}
+
+static bool imon_mouse_event(struct imon_context *ictx,
+			     unsigned char *buf, int len)
+{
+	signed char rel_x = 0x00, rel_y = 0x00;
+	u8 right_shift = 1;
+	bool mouse_input = true;
+	int dir = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ictx->kc_lock, flags);
+
+	/* newer iMON device PAD or mouse button */
+	if (ictx->product != 0xffdc && (buf[0] & 0x01) && len == 5) {
+		rel_x = buf[2];
+		rel_y = buf[3];
+		right_shift = 1;
+	/* 0xffdc iMON PAD or mouse button input */
+	} else if (ictx->product == 0xffdc && (buf[0] & 0x40) &&
+			!((buf[1] & 0x01) || ((buf[1] >> 2) & 0x01))) {
+		rel_x = (buf[1] & 0x08) | (buf[1] & 0x10) >> 2 |
+			(buf[1] & 0x20) >> 4 | (buf[1] & 0x40) >> 6;
+		if (buf[0] & 0x02)
+			rel_x |= ~0x0f;
+		rel_x = rel_x + rel_x / 2;
+		rel_y = (buf[2] & 0x08) | (buf[2] & 0x10) >> 2 |
+			(buf[2] & 0x20) >> 4 | (buf[2] & 0x40) >> 6;
+		if (buf[0] & 0x01)
+			rel_y |= ~0x0f;
+		rel_y = rel_y + rel_y / 2;
+		right_shift = 2;
+	/* some ffdc devices decode mouse buttons differently... */
+	} else if (ictx->product == 0xffdc && (buf[0] == 0x68)) {
+		right_shift = 2;
+	/* ch+/- buttons, which we use for an emulated scroll wheel */
+	} else if (ictx->kc == KEY_CHANNELUP && (buf[2] & 0x40) != 0x40) {
+		dir = 1;
+	} else if (ictx->kc == KEY_CHANNELDOWN && (buf[2] & 0x40) != 0x40) {
+		dir = -1;
+	} else
+		mouse_input = false;
+
+	spin_unlock_irqrestore(&ictx->kc_lock, flags);
+
+	if (mouse_input) {
+		dev_dbg(ictx->dev, "sending mouse data via input subsystem\n");
+
+		if (dir) {
+			input_report_rel(ictx->idev, REL_WHEEL, dir);
+		} else if (rel_x || rel_y) {
+			input_report_rel(ictx->idev, REL_X, rel_x);
+			input_report_rel(ictx->idev, REL_Y, rel_y);
+		} else {
+			input_report_key(ictx->idev, BTN_LEFT, buf[1] & 0x1);
+			input_report_key(ictx->idev, BTN_RIGHT,
+					 buf[1] >> right_shift & 0x1);
+		}
+		input_sync(ictx->idev);
+		spin_lock_irqsave(&ictx->kc_lock, flags);
+		ictx->last_keycode = ictx->kc;
+		spin_unlock_irqrestore(&ictx->kc_lock, flags);
+	}
+
+	return mouse_input;
+}
+
+static void imon_touch_event(struct imon_context *ictx, unsigned char *buf)
+{
+	mod_timer(&ictx->ttimer, jiffies + TOUCH_TIMEOUT);
+	ictx->touch_x = (buf[0] << 4) | (buf[1] >> 4);
+	ictx->touch_y = 0xfff - ((buf[2] << 4) | (buf[1] & 0xf));
+	input_report_abs(ictx->touch, ABS_X, ictx->touch_x);
+	input_report_abs(ictx->touch, ABS_Y, ictx->touch_y);
+	input_report_key(ictx->touch, BTN_TOUCH, 0x01);
+	input_sync(ictx->touch);
+}
+
+static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf)
+{
+	int dir = 0;
+	signed char rel_x = 0x00, rel_y = 0x00;
+	u16 timeout, threshold;
+	u32 scancode = KEY_RESERVED;
+	unsigned long flags;
+
+	/*
+	 * The imon directional pad functions more like a touchpad. Bytes 3 & 4
+	 * contain a position coordinate (x,y), with each component ranging
+	 * from -14 to 14. We want to down-sample this to only 4 discrete values
+	 * for up/down/left/right arrow keys. Also, when you get too close to
+	 * diagonals, it has a tendency to jump back and forth, so lets try to
+	 * ignore when they get too close.
+	 */
+	if (ictx->product != 0xffdc) {
+		/* first, pad to 8 bytes so it conforms with everything else */
+		buf[5] = buf[6] = buf[7] = 0;
+		timeout = 500;	/* in msecs */
+		/* (2*threshold) x (2*threshold) square */
+		threshold = pad_thresh ? pad_thresh : 28;
+		rel_x = buf[2];
+		rel_y = buf[3];
+
+		if (ictx->rc_type == RC_BIT_OTHER && pad_stabilize) {
+			if ((buf[1] == 0) && ((rel_x != 0) || (rel_y != 0))) {
+				dir = stabilize((int)rel_x, (int)rel_y,
+						timeout, threshold);
+				if (!dir) {
+					spin_lock_irqsave(&ictx->kc_lock,
+							  flags);
+					ictx->kc = KEY_UNKNOWN;
+					spin_unlock_irqrestore(&ictx->kc_lock,
+							       flags);
+					return;
+				}
+				buf[2] = dir & 0xFF;
+				buf[3] = (dir >> 8) & 0xFF;
+				scancode = be32_to_cpu(*((__be32 *)buf));
+			}
+		} else {
+			/*
+			 * Hack alert: instead of using keycodes, we have
+			 * to use hard-coded scancodes here...
+			 */
+			if (abs(rel_y) > abs(rel_x)) {
+				buf[2] = (rel_y > 0) ? 0x7F : 0x80;
+				buf[3] = 0;
+				if (rel_y > 0)
+					scancode = 0x01007f00; /* KEY_DOWN */
+				else
+					scancode = 0x01008000; /* KEY_UP */
+			} else {
+				buf[2] = 0;
+				buf[3] = (rel_x > 0) ? 0x7F : 0x80;
+				if (rel_x > 0)
+					scancode = 0x0100007f; /* KEY_RIGHT */
+				else
+					scancode = 0x01000080; /* KEY_LEFT */
+			}
+		}
+
+	/*
+	 * Handle on-board decoded pad events for e.g. older VFD/iMON-Pad
+	 * device (15c2:ffdc). The remote generates various codes from
+	 * 0x68nnnnB7 to 0x6AnnnnB7, the left mouse button generates
+	 * 0x688301b7 and the right one 0x688481b7. All other keys generate
+	 * 0x2nnnnnnn. Position coordinate is encoded in buf[1] and buf[2] with
+	 * reversed endianness. Extract direction from buffer, rotate endianness,
+	 * adjust sign and feed the values into stabilize(). The resulting codes
+	 * will be 0x01008000, 0x01007F00, which match the newer devices.
+	 */
+	} else {
+		timeout = 10;	/* in msecs */
+		/* (2*threshold) x (2*threshold) square */
+		threshold = pad_thresh ? pad_thresh : 15;
+
+		/* buf[1] is x */
+		rel_x = (buf[1] & 0x08) | (buf[1] & 0x10) >> 2 |
+			(buf[1] & 0x20) >> 4 | (buf[1] & 0x40) >> 6;
+		if (buf[0] & 0x02)
+			rel_x |= ~0x10+1;
+		/* buf[2] is y */
+		rel_y = (buf[2] & 0x08) | (buf[2] & 0x10) >> 2 |
+			(buf[2] & 0x20) >> 4 | (buf[2] & 0x40) >> 6;
+		if (buf[0] & 0x01)
+			rel_y |= ~0x10+1;
+
+		buf[0] = 0x01;
+		buf[1] = buf[4] = buf[5] = buf[6] = buf[7] = 0;
+
+		if (ictx->rc_type == RC_BIT_OTHER && pad_stabilize) {
+			dir = stabilize((int)rel_x, (int)rel_y,
+					timeout, threshold);
+			if (!dir) {
+				spin_lock_irqsave(&ictx->kc_lock, flags);
+				ictx->kc = KEY_UNKNOWN;
+				spin_unlock_irqrestore(&ictx->kc_lock, flags);
+				return;
+			}
+			buf[2] = dir & 0xFF;
+			buf[3] = (dir >> 8) & 0xFF;
+			scancode = be32_to_cpu(*((__be32 *)buf));
+		} else {
+			/*
+			 * Hack alert: instead of using keycodes, we have
+			 * to use hard-coded scancodes here...
+			 */
+			if (abs(rel_y) > abs(rel_x)) {
+				buf[2] = (rel_y > 0) ? 0x7F : 0x80;
+				buf[3] = 0;
+				if (rel_y > 0)
+					scancode = 0x01007f00; /* KEY_DOWN */
+				else
+					scancode = 0x01008000; /* KEY_UP */
+			} else {
+				buf[2] = 0;
+				buf[3] = (rel_x > 0) ? 0x7F : 0x80;
+				if (rel_x > 0)
+					scancode = 0x0100007f; /* KEY_RIGHT */
+				else
+					scancode = 0x01000080; /* KEY_LEFT */
+			}
+		}
+	}
+
+	if (scancode) {
+		spin_lock_irqsave(&ictx->kc_lock, flags);
+		ictx->kc = imon_remote_key_lookup(ictx, scancode);
+		spin_unlock_irqrestore(&ictx->kc_lock, flags);
+	}
+}
+
+/**
+ * figure out if these is a press or a release. We don't actually
+ * care about repeats, as those will be auto-generated within the IR
+ * subsystem for repeating scancodes.
+ */
+static int imon_parse_press_type(struct imon_context *ictx,
+				 unsigned char *buf, u8 ktype)
+{
+	int press_type = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ictx->kc_lock, flags);
+
+	/* key release of 0x02XXXXXX key */
+	if (ictx->kc == KEY_RESERVED && buf[0] == 0x02 && buf[3] == 0x00)
+		ictx->kc = ictx->last_keycode;
+
+	/* mouse button release on (some) 0xffdc devices */
+	else if (ictx->kc == KEY_RESERVED && buf[0] == 0x68 && buf[1] == 0x82 &&
+		 buf[2] == 0x81 && buf[3] == 0xb7)
+		ictx->kc = ictx->last_keycode;
+
+	/* mouse button release on (some other) 0xffdc devices */
+	else if (ictx->kc == KEY_RESERVED && buf[0] == 0x01 && buf[1] == 0x00 &&
+		 buf[2] == 0x81 && buf[3] == 0xb7)
+		ictx->kc = ictx->last_keycode;
+
+	/* mce-specific button handling, no keyup events */
+	else if (ktype == IMON_KEY_MCE) {
+		ictx->rc_toggle = buf[2];
+		press_type = 1;
+
+	/* incoherent or irrelevant data */
+	} else if (ictx->kc == KEY_RESERVED)
+		press_type = -EINVAL;
+
+	/* key release of 0xXXXXXXb7 key */
+	else if (ictx->release_code)
+		press_type = 0;
+
+	/* this is a button press */
+	else
+		press_type = 1;
+
+	spin_unlock_irqrestore(&ictx->kc_lock, flags);
+
+	return press_type;
+}
+
+/**
+ * Process the incoming packet
+ */
+static void imon_incoming_packet(struct imon_context *ictx,
+				 struct urb *urb, int intf)
+{
+	int len = urb->actual_length;
+	unsigned char *buf = urb->transfer_buffer;
+	struct device *dev = ictx->dev;
+	unsigned long flags;
+	u32 kc;
+	int i;
+	u64 scancode;
+	int press_type = 0;
+	int msec;
+	struct timeval t;
+	static struct timeval prev_time = { 0, 0 };
+	u8 ktype;
+
+	/* filter out junk data on the older 0xffdc imon devices */
+	if ((buf[0] == 0xff) && (buf[1] == 0xff) && (buf[2] == 0xff))
+		return;
+
+	/* Figure out what key was pressed */
+	if (len == 8 && buf[7] == 0xee) {
+		scancode = be64_to_cpu(*((__be64 *)buf));
+		ktype = IMON_KEY_PANEL;
+		kc = imon_panel_key_lookup(ictx, scancode);
+		ictx->release_code = false;
+	} else {
+		scancode = be32_to_cpu(*((__be32 *)buf));
+		if (ictx->rc_type == RC_BIT_RC6_MCE) {
+			ktype = IMON_KEY_IMON;
+			if (buf[0] == 0x80)
+				ktype = IMON_KEY_MCE;
+			kc = imon_mce_key_lookup(ictx, scancode);
+		} else {
+			ktype = IMON_KEY_IMON;
+			kc = imon_remote_key_lookup(ictx, scancode);
+		}
+	}
+
+	spin_lock_irqsave(&ictx->kc_lock, flags);
+	/* keyboard/mouse mode toggle button */
+	if (kc == KEY_KEYBOARD && !ictx->release_code) {
+		ictx->last_keycode = kc;
+		if (!nomouse) {
+			ictx->pad_mouse = !ictx->pad_mouse;
+			dev_dbg(dev, "toggling to %s mode\n",
+				ictx->pad_mouse ? "mouse" : "keyboard");
+			spin_unlock_irqrestore(&ictx->kc_lock, flags);
+			return;
+		} else {
+			ictx->pad_mouse = false;
+			dev_dbg(dev, "mouse mode disabled, passing key value\n");
+		}
+	}
+
+	ictx->kc = kc;
+	spin_unlock_irqrestore(&ictx->kc_lock, flags);
+
+	/* send touchscreen events through input subsystem if touchpad data */
+	if (ictx->display_type == IMON_DISPLAY_TYPE_VGA && len == 8 &&
+	    buf[7] == 0x86) {
+		imon_touch_event(ictx, buf);
+		return;
+
+	/* look for mouse events with pad in mouse mode */
+	} else if (ictx->pad_mouse) {
+		if (imon_mouse_event(ictx, buf, len))
+			return;
+	}
+
+	/* Now for some special handling to convert pad input to arrow keys */
+	if (((len == 5) && (buf[0] == 0x01) && (buf[4] == 0x00)) ||
+	    ((len == 8) && (buf[0] & 0x40) &&
+	     !(buf[1] & 0x1 || buf[1] >> 2 & 0x1))) {
+		len = 8;
+		imon_pad_to_keys(ictx, buf);
+	}
+
+	if (debug) {
+		printk(KERN_INFO "intf%d decoded packet: ", intf);
+		for (i = 0; i < len; ++i)
+			printk("%02x ", buf[i]);
+		printk("\n");
+	}
+
+	press_type = imon_parse_press_type(ictx, buf, ktype);
+	if (press_type < 0)
+		goto not_input_data;
+
+	if (ktype != IMON_KEY_PANEL) {
+		if (press_type == 0)
+			rc_keyup(ictx->rdev);
+		else {
+			if (ictx->rc_type == RC_BIT_RC6_MCE ||
+			    ictx->rc_type == RC_BIT_OTHER)
+				rc_keydown(ictx->rdev,
+					   ictx->rc_type == RC_BIT_RC6_MCE ? RC_TYPE_RC6_MCE : RC_TYPE_OTHER,
+					   ictx->rc_scancode, ictx->rc_toggle);
+			spin_lock_irqsave(&ictx->kc_lock, flags);
+			ictx->last_keycode = ictx->kc;
+			spin_unlock_irqrestore(&ictx->kc_lock, flags);
+		}
+		return;
+	}
+
+	/* Only panel type events left to process now */
+	spin_lock_irqsave(&ictx->kc_lock, flags);
+
+	do_gettimeofday(&t);
+	/* KEY_MUTE repeats from knob need to be suppressed */
+	if (ictx->kc == KEY_MUTE && ictx->kc == ictx->last_keycode) {
+		msec = tv2int(&t, &prev_time);
+		if (msec < ictx->idev->rep[REP_DELAY]) {
+			spin_unlock_irqrestore(&ictx->kc_lock, flags);
+			return;
+		}
+	}
+	prev_time = t;
+	kc = ictx->kc;
+
+	spin_unlock_irqrestore(&ictx->kc_lock, flags);
+
+	input_report_key(ictx->idev, kc, press_type);
+	input_sync(ictx->idev);
+
+	/* panel keys don't generate a release */
+	input_report_key(ictx->idev, kc, 0);
+	input_sync(ictx->idev);
+
+	spin_lock_irqsave(&ictx->kc_lock, flags);
+	ictx->last_keycode = kc;
+	spin_unlock_irqrestore(&ictx->kc_lock, flags);
+
+	return;
+
+not_input_data:
+	if (len != 8) {
+		dev_warn(dev, "imon %s: invalid incoming packet "
+			 "size (len = %d, intf%d)\n", __func__, len, intf);
+		return;
+	}
+
+	/* iMON 2.4G associate frame */
+	if (buf[0] == 0x00 &&
+	    buf[2] == 0xFF &&				/* REFID */
+	    buf[3] == 0xFF &&
+	    buf[4] == 0xFF &&
+	    buf[5] == 0xFF &&				/* iMON 2.4G */
+	   ((buf[6] == 0x4E && buf[7] == 0xDF) ||	/* LT */
+	    (buf[6] == 0x5E && buf[7] == 0xDF))) {	/* DT */
+		dev_warn(dev, "%s: remote associated refid=%02X\n",
+			 __func__, buf[1]);
+		ictx->rf_isassociating = false;
+	}
+}
+
+/**
+ * Callback function for USB core API: receive data
+ */
+static void usb_rx_callback_intf0(struct urb *urb)
+{
+	struct imon_context *ictx;
+	int intfnum = 0;
+
+	if (!urb)
+		return;
+
+	ictx = (struct imon_context *)urb->context;
+	if (!ictx)
+		return;
+
+	/*
+	 * if we get a callback before we're done configuring the hardware, we
+	 * can't yet process the data, as there's nowhere to send it, but we
+	 * still need to submit a new rx URB to avoid wedging the hardware
+	 */
+	if (!ictx->dev_present_intf0)
+		goto out;
+
+	switch (urb->status) {
+	case -ENOENT:		/* usbcore unlink successful! */
+		return;
+
+	case -ESHUTDOWN:	/* transport endpoint was shut down */
+		break;
+
+	case 0:
+		imon_incoming_packet(ictx, urb, intfnum);
+		break;
+
+	default:
+		dev_warn(ictx->dev, "imon %s: status(%d): ignored\n",
+			 __func__, urb->status);
+		break;
+	}
+
+out:
+	usb_submit_urb(ictx->rx_urb_intf0, GFP_ATOMIC);
+}
+
+static void usb_rx_callback_intf1(struct urb *urb)
+{
+	struct imon_context *ictx;
+	int intfnum = 1;
+
+	if (!urb)
+		return;
+
+	ictx = (struct imon_context *)urb->context;
+	if (!ictx)
+		return;
+
+	/*
+	 * if we get a callback before we're done configuring the hardware, we
+	 * can't yet process the data, as there's nowhere to send it, but we
+	 * still need to submit a new rx URB to avoid wedging the hardware
+	 */
+	if (!ictx->dev_present_intf1)
+		goto out;
+
+	switch (urb->status) {
+	case -ENOENT:		/* usbcore unlink successful! */
+		return;
+
+	case -ESHUTDOWN:	/* transport endpoint was shut down */
+		break;
+
+	case 0:
+		imon_incoming_packet(ictx, urb, intfnum);
+		break;
+
+	default:
+		dev_warn(ictx->dev, "imon %s: status(%d): ignored\n",
+			 __func__, urb->status);
+		break;
+	}
+
+out:
+	usb_submit_urb(ictx->rx_urb_intf1, GFP_ATOMIC);
+}
+
+/*
+ * The 0x15c2:0xffdc device ID was used for umpteen different imon
+ * devices, and all of them constantly spew interrupts, even when there
+ * is no actual data to report. However, byte 6 of this buffer looks like
+ * its unique across device variants, so we're trying to key off that to
+ * figure out which display type (if any) and what IR protocol the device
+ * actually supports. These devices have their IR protocol hard-coded into
+ * their firmware, they can't be changed on the fly like the newer hardware.
+ */
+static void imon_get_ffdc_type(struct imon_context *ictx)
+{
+	u8 ffdc_cfg_byte = ictx->usb_rx_buf[6];
+	u8 detected_display_type = IMON_DISPLAY_TYPE_NONE;
+	u64 allowed_protos = RC_BIT_OTHER;
+
+	switch (ffdc_cfg_byte) {
+	/* iMON Knob, no display, iMON IR + vol knob */
+	case 0x21:
+		dev_info(ictx->dev, "0xffdc iMON Knob, iMON IR");
+		ictx->display_supported = false;
+		break;
+	/* iMON 2.4G LT (usb stick), no display, iMON RF */
+	case 0x4e:
+		dev_info(ictx->dev, "0xffdc iMON 2.4G LT, iMON RF");
+		ictx->display_supported = false;
+		ictx->rf_device = true;
+		break;
+	/* iMON VFD, no IR (does have vol knob tho) */
+	case 0x35:
+		dev_info(ictx->dev, "0xffdc iMON VFD + knob, no IR");
+		detected_display_type = IMON_DISPLAY_TYPE_VFD;
+		break;
+	/* iMON VFD, iMON IR */
+	case 0x24:
+	case 0x85:
+		dev_info(ictx->dev, "0xffdc iMON VFD, iMON IR");
+		detected_display_type = IMON_DISPLAY_TYPE_VFD;
+		break;
+	/* iMON VFD, MCE IR */
+	case 0x46:
+	case 0x7e:
+	case 0x9e:
+		dev_info(ictx->dev, "0xffdc iMON VFD, MCE IR");
+		detected_display_type = IMON_DISPLAY_TYPE_VFD;
+		allowed_protos = RC_BIT_RC6_MCE;
+		break;
+	/* iMON LCD, MCE IR */
+	case 0x9f:
+		dev_info(ictx->dev, "0xffdc iMON LCD, MCE IR");
+		detected_display_type = IMON_DISPLAY_TYPE_LCD;
+		allowed_protos = RC_BIT_RC6_MCE;
+		break;
+	default:
+		dev_info(ictx->dev, "Unknown 0xffdc device, "
+			 "defaulting to VFD and iMON IR");
+		detected_display_type = IMON_DISPLAY_TYPE_VFD;
+		/* We don't know which one it is, allow user to set the
+		 * RC6 one from userspace if OTHER wasn't correct. */
+		allowed_protos |= RC_BIT_RC6_MCE;
+		break;
+	}
+
+	printk(KERN_CONT " (id 0x%02x)\n", ffdc_cfg_byte);
+
+	ictx->display_type = detected_display_type;
+	ictx->rc_type = allowed_protos;
+}
+
+static void imon_set_display_type(struct imon_context *ictx)
+{
+	u8 configured_display_type = IMON_DISPLAY_TYPE_VFD;
+
+	/*
+	 * Try to auto-detect the type of display if the user hasn't set
+	 * it by hand via the display_type modparam. Default is VFD.
+	 */
+
+	if (display_type == IMON_DISPLAY_TYPE_AUTO) {
+		switch (ictx->product) {
+		case 0xffdc:
+			/* set in imon_get_ffdc_type() */
+			configured_display_type = ictx->display_type;
+			break;
+		case 0x0034:
+		case 0x0035:
+			configured_display_type = IMON_DISPLAY_TYPE_VGA;
+			break;
+		case 0x0038:
+		case 0x0039:
+		case 0x0045:
+			configured_display_type = IMON_DISPLAY_TYPE_LCD;
+			break;
+		case 0x003c:
+		case 0x0041:
+		case 0x0042:
+		case 0x0043:
+			configured_display_type = IMON_DISPLAY_TYPE_NONE;
+			ictx->display_supported = false;
+			break;
+		case 0x0036:
+		case 0x0044:
+		default:
+			configured_display_type = IMON_DISPLAY_TYPE_VFD;
+			break;
+		}
+	} else {
+		configured_display_type = display_type;
+		if (display_type == IMON_DISPLAY_TYPE_NONE)
+			ictx->display_supported = false;
+		else
+			ictx->display_supported = true;
+		dev_info(ictx->dev, "%s: overriding display type to %d via "
+			 "modparam\n", __func__, display_type);
+	}
+
+	ictx->display_type = configured_display_type;
+}
+
+static struct rc_dev *imon_init_rdev(struct imon_context *ictx)
+{
+	struct rc_dev *rdev;
+	int ret;
+	const unsigned char fp_packet[] = { 0x40, 0x00, 0x00, 0x00,
+					    0x00, 0x00, 0x00, 0x88 };
+
+	rdev = rc_allocate_device();
+	if (!rdev) {
+		dev_err(ictx->dev, "remote control dev allocation failed\n");
+		goto out;
+	}
+
+	snprintf(ictx->name_rdev, sizeof(ictx->name_rdev),
+		 "iMON Remote (%04x:%04x)", ictx->vendor, ictx->product);
+	usb_make_path(ictx->usbdev_intf0, ictx->phys_rdev,
+		      sizeof(ictx->phys_rdev));
+	strlcat(ictx->phys_rdev, "/input0", sizeof(ictx->phys_rdev));
+
+	rdev->input_name = ictx->name_rdev;
+	rdev->input_phys = ictx->phys_rdev;
+	usb_to_input_id(ictx->usbdev_intf0, &rdev->input_id);
+	rdev->dev.parent = ictx->dev;
+
+	rdev->priv = ictx;
+	rdev->driver_type = RC_DRIVER_SCANCODE;
+	rdev->allowed_protocols = RC_BIT_OTHER | RC_BIT_RC6_MCE; /* iMON PAD or MCE */
+	rdev->change_protocol = imon_ir_change_protocol;
+	rdev->driver_name = MOD_NAME;
+
+	/* Enable front-panel buttons and/or knobs */
+	memcpy(ictx->usb_tx_buf, &fp_packet, sizeof(fp_packet));
+	ret = send_packet(ictx);
+	/* Not fatal, but warn about it */
+	if (ret)
+		dev_info(ictx->dev, "panel buttons/knobs setup failed\n");
+
+	if (ictx->product == 0xffdc) {
+		imon_get_ffdc_type(ictx);
+		rdev->allowed_protocols = ictx->rc_type;
+	}
+
+	imon_set_display_type(ictx);
+
+	if (ictx->rc_type == RC_BIT_RC6_MCE)
+		rdev->map_name = RC_MAP_IMON_MCE;
+	else
+		rdev->map_name = RC_MAP_IMON_PAD;
+
+	ret = rc_register_device(rdev);
+	if (ret < 0) {
+		dev_err(ictx->dev, "remote input dev register failed\n");
+		goto out;
+	}
+
+	return rdev;
+
+out:
+	rc_free_device(rdev);
+	return NULL;
+}
+
+static struct input_dev *imon_init_idev(struct imon_context *ictx)
+{
+	struct imon_panel_key_table *key_table = ictx->dev_descr->key_table;
+	struct input_dev *idev;
+	int ret, i;
+
+	idev = input_allocate_device();
+	if (!idev)
+		goto out;
+
+	snprintf(ictx->name_idev, sizeof(ictx->name_idev),
+		 "iMON Panel, Knob and Mouse(%04x:%04x)",
+		 ictx->vendor, ictx->product);
+	idev->name = ictx->name_idev;
+
+	usb_make_path(ictx->usbdev_intf0, ictx->phys_idev,
+		      sizeof(ictx->phys_idev));
+	strlcat(ictx->phys_idev, "/input1", sizeof(ictx->phys_idev));
+	idev->phys = ictx->phys_idev;
+
+	idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | BIT_MASK(EV_REL);
+
+	idev->keybit[BIT_WORD(BTN_MOUSE)] =
+		BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT);
+	idev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y) |
+		BIT_MASK(REL_WHEEL);
+
+	/* panel and/or knob code support */
+	for (i = 0; key_table[i].hw_code != 0; i++) {
+		u32 kc = key_table[i].keycode;
+		__set_bit(kc, idev->keybit);
+	}
+
+	usb_to_input_id(ictx->usbdev_intf0, &idev->id);
+	idev->dev.parent = ictx->dev;
+	input_set_drvdata(idev, ictx);
+
+	ret = input_register_device(idev);
+	if (ret < 0) {
+		dev_err(ictx->dev, "input dev register failed\n");
+		goto out;
+	}
+
+	return idev;
+
+out:
+	input_free_device(idev);
+	return NULL;
+}
+
+static struct input_dev *imon_init_touch(struct imon_context *ictx)
+{
+	struct input_dev *touch;
+	int ret;
+
+	touch = input_allocate_device();
+	if (!touch)
+		goto touch_alloc_failed;
+
+	snprintf(ictx->name_touch, sizeof(ictx->name_touch),
+		 "iMON USB Touchscreen (%04x:%04x)",
+		 ictx->vendor, ictx->product);
+	touch->name = ictx->name_touch;
+
+	usb_make_path(ictx->usbdev_intf1, ictx->phys_touch,
+		      sizeof(ictx->phys_touch));
+	strlcat(ictx->phys_touch, "/input2", sizeof(ictx->phys_touch));
+	touch->phys = ictx->phys_touch;
+
+	touch->evbit[0] =
+		BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	touch->keybit[BIT_WORD(BTN_TOUCH)] =
+		BIT_MASK(BTN_TOUCH);
+	input_set_abs_params(touch, ABS_X,
+			     0x00, 0xfff, 0, 0);
+	input_set_abs_params(touch, ABS_Y,
+			     0x00, 0xfff, 0, 0);
+
+	input_set_drvdata(touch, ictx);
+
+	usb_to_input_id(ictx->usbdev_intf1, &touch->id);
+	touch->dev.parent = ictx->dev;
+	ret = input_register_device(touch);
+	if (ret <  0) {
+		dev_info(ictx->dev, "touchscreen input dev register failed\n");
+		goto touch_register_failed;
+	}
+
+	return touch;
+
+touch_register_failed:
+	input_free_device(touch);
+
+touch_alloc_failed:
+	return NULL;
+}
+
+static bool imon_find_endpoints(struct imon_context *ictx,
+				struct usb_host_interface *iface_desc)
+{
+	struct usb_endpoint_descriptor *ep;
+	struct usb_endpoint_descriptor *rx_endpoint = NULL;
+	struct usb_endpoint_descriptor *tx_endpoint = NULL;
+	int ifnum = iface_desc->desc.bInterfaceNumber;
+	int num_endpts = iface_desc->desc.bNumEndpoints;
+	int i, ep_dir, ep_type;
+	bool ir_ep_found = false;
+	bool display_ep_found = false;
+	bool tx_control = false;
+
+	/*
+	 * Scan the endpoint list and set:
+	 *	first input endpoint = IR endpoint
+	 *	first output endpoint = display endpoint
+	 */
+	for (i = 0; i < num_endpts && !(ir_ep_found && display_ep_found); ++i) {
+		ep = &iface_desc->endpoint[i].desc;
+		ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
+		ep_type = usb_endpoint_type(ep);
+
+		if (!ir_ep_found && ep_dir == USB_DIR_IN &&
+		    ep_type == USB_ENDPOINT_XFER_INT) {
+
+			rx_endpoint = ep;
+			ir_ep_found = true;
+			dev_dbg(ictx->dev, "%s: found IR endpoint\n", __func__);
+
+		} else if (!display_ep_found && ep_dir == USB_DIR_OUT &&
+			   ep_type == USB_ENDPOINT_XFER_INT) {
+			tx_endpoint = ep;
+			display_ep_found = true;
+			dev_dbg(ictx->dev, "%s: found display endpoint\n", __func__);
+		}
+	}
+
+	if (ifnum == 0) {
+		ictx->rx_endpoint_intf0 = rx_endpoint;
+		/*
+		 * tx is used to send characters to lcd/vfd, associate RF
+		 * remotes, set IR protocol, and maybe more...
+		 */
+		ictx->tx_endpoint = tx_endpoint;
+	} else {
+		ictx->rx_endpoint_intf1 = rx_endpoint;
+	}
+
+	/*
+	 * If we didn't find a display endpoint, this is probably one of the
+	 * newer iMON devices that use control urb instead of interrupt
+	 */
+	if (!display_ep_found) {
+		tx_control = true;
+		display_ep_found = true;
+		dev_dbg(ictx->dev, "%s: device uses control endpoint, not "
+			"interface OUT endpoint\n", __func__);
+	}
+
+	/*
+	 * Some iMON receivers have no display. Unfortunately, it seems
+	 * that SoundGraph recycles device IDs between devices both with
+	 * and without... :\
+	 */
+	if (ictx->display_type == IMON_DISPLAY_TYPE_NONE) {
+		display_ep_found = false;
+		dev_dbg(ictx->dev, "%s: device has no display\n", __func__);
+	}
+
+	/*
+	 * iMON Touch devices have a VGA touchscreen, but no "display", as
+	 * that refers to e.g. /dev/lcd0 (a character device LCD or VFD).
+	 */
+	if (ictx->display_type == IMON_DISPLAY_TYPE_VGA) {
+		display_ep_found = false;
+		dev_dbg(ictx->dev, "%s: iMON Touch device found\n", __func__);
+	}
+
+	/* Input endpoint is mandatory */
+	if (!ir_ep_found)
+		pr_err("no valid input (IR) endpoint found\n");
+
+	ictx->tx_control = tx_control;
+
+	if (display_ep_found)
+		ictx->display_supported = true;
+
+	return ir_ep_found;
+
+}
+
+static struct imon_context *imon_init_intf0(struct usb_interface *intf,
+					    const struct usb_device_id *id)
+{
+	struct imon_context *ictx;
+	struct urb *rx_urb;
+	struct urb *tx_urb;
+	struct device *dev = &intf->dev;
+	struct usb_host_interface *iface_desc;
+	int ret = -ENOMEM;
+
+	ictx = kzalloc(sizeof(struct imon_context), GFP_KERNEL);
+	if (!ictx) {
+		dev_err(dev, "%s: kzalloc failed for context", __func__);
+		goto exit;
+	}
+	rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!rx_urb) {
+		dev_err(dev, "%s: usb_alloc_urb failed for IR urb", __func__);
+		goto rx_urb_alloc_failed;
+	}
+	tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!tx_urb) {
+		dev_err(dev, "%s: usb_alloc_urb failed for display urb",
+			__func__);
+		goto tx_urb_alloc_failed;
+	}
+
+	mutex_init(&ictx->lock);
+	spin_lock_init(&ictx->kc_lock);
+
+	mutex_lock(&ictx->lock);
+
+	ictx->dev = dev;
+	ictx->usbdev_intf0 = usb_get_dev(interface_to_usbdev(intf));
+	ictx->rx_urb_intf0 = rx_urb;
+	ictx->tx_urb = tx_urb;
+	ictx->rf_device = false;
+
+	ictx->vendor  = le16_to_cpu(ictx->usbdev_intf0->descriptor.idVendor);
+	ictx->product = le16_to_cpu(ictx->usbdev_intf0->descriptor.idProduct);
+
+	/* save drive info for later accessing the panel/knob key table */
+	ictx->dev_descr = (struct imon_usb_dev_descr *)id->driver_info;
+	/* default send_packet delay is 5ms but some devices need more */
+	ictx->send_packet_delay = ictx->dev_descr->flags &
+				  IMON_NEED_20MS_PKT_DELAY ? 20 : 5;
+
+	ret = -ENODEV;
+	iface_desc = intf->cur_altsetting;
+	if (!imon_find_endpoints(ictx, iface_desc)) {
+		goto find_endpoint_failed;
+	}
+
+	usb_fill_int_urb(ictx->rx_urb_intf0, ictx->usbdev_intf0,
+		usb_rcvintpipe(ictx->usbdev_intf0,
+			ictx->rx_endpoint_intf0->bEndpointAddress),
+		ictx->usb_rx_buf, sizeof(ictx->usb_rx_buf),
+		usb_rx_callback_intf0, ictx,
+		ictx->rx_endpoint_intf0->bInterval);
+
+	ret = usb_submit_urb(ictx->rx_urb_intf0, GFP_KERNEL);
+	if (ret) {
+		pr_err("usb_submit_urb failed for intf0 (%d)\n", ret);
+		goto urb_submit_failed;
+	}
+
+	ictx->idev = imon_init_idev(ictx);
+	if (!ictx->idev) {
+		dev_err(dev, "%s: input device setup failed\n", __func__);
+		goto idev_setup_failed;
+	}
+
+	ictx->rdev = imon_init_rdev(ictx);
+	if (!ictx->rdev) {
+		dev_err(dev, "%s: rc device setup failed\n", __func__);
+		goto rdev_setup_failed;
+	}
+
+	ictx->dev_present_intf0 = true;
+
+	mutex_unlock(&ictx->lock);
+	return ictx;
+
+rdev_setup_failed:
+	input_unregister_device(ictx->idev);
+idev_setup_failed:
+	usb_kill_urb(ictx->rx_urb_intf0);
+urb_submit_failed:
+find_endpoint_failed:
+	usb_put_dev(ictx->usbdev_intf0);
+	mutex_unlock(&ictx->lock);
+	usb_free_urb(tx_urb);
+tx_urb_alloc_failed:
+	usb_free_urb(rx_urb);
+rx_urb_alloc_failed:
+	kfree(ictx);
+exit:
+	dev_err(dev, "unable to initialize intf0, err %d\n", ret);
+
+	return NULL;
+}
+
+static struct imon_context *imon_init_intf1(struct usb_interface *intf,
+					    struct imon_context *ictx)
+{
+	struct urb *rx_urb;
+	struct usb_host_interface *iface_desc;
+	int ret = -ENOMEM;
+
+	rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!rx_urb) {
+		pr_err("usb_alloc_urb failed for IR urb\n");
+		goto rx_urb_alloc_failed;
+	}
+
+	mutex_lock(&ictx->lock);
+
+	if (ictx->display_type == IMON_DISPLAY_TYPE_VGA) {
+		init_timer(&ictx->ttimer);
+		ictx->ttimer.data = (unsigned long)ictx;
+		ictx->ttimer.function = imon_touch_display_timeout;
+	}
+
+	ictx->usbdev_intf1 = usb_get_dev(interface_to_usbdev(intf));
+	ictx->rx_urb_intf1 = rx_urb;
+
+	ret = -ENODEV;
+	iface_desc = intf->cur_altsetting;
+	if (!imon_find_endpoints(ictx, iface_desc))
+		goto find_endpoint_failed;
+
+	if (ictx->display_type == IMON_DISPLAY_TYPE_VGA) {
+		ictx->touch = imon_init_touch(ictx);
+		if (!ictx->touch)
+			goto touch_setup_failed;
+	} else
+		ictx->touch = NULL;
+
+	usb_fill_int_urb(ictx->rx_urb_intf1, ictx->usbdev_intf1,
+		usb_rcvintpipe(ictx->usbdev_intf1,
+			ictx->rx_endpoint_intf1->bEndpointAddress),
+		ictx->usb_rx_buf, sizeof(ictx->usb_rx_buf),
+		usb_rx_callback_intf1, ictx,
+		ictx->rx_endpoint_intf1->bInterval);
+
+	ret = usb_submit_urb(ictx->rx_urb_intf1, GFP_KERNEL);
+
+	if (ret) {
+		pr_err("usb_submit_urb failed for intf1 (%d)\n", ret);
+		goto urb_submit_failed;
+	}
+
+	ictx->dev_present_intf1 = true;
+
+	mutex_unlock(&ictx->lock);
+	return ictx;
+
+urb_submit_failed:
+	if (ictx->touch)
+		input_unregister_device(ictx->touch);
+touch_setup_failed:
+find_endpoint_failed:
+	usb_put_dev(ictx->usbdev_intf1);
+	mutex_unlock(&ictx->lock);
+	usb_free_urb(rx_urb);
+rx_urb_alloc_failed:
+	dev_err(ictx->dev, "unable to initialize intf1, err %d\n", ret);
+
+	return NULL;
+}
+
+static void imon_init_display(struct imon_context *ictx,
+			      struct usb_interface *intf)
+{
+	int ret;
+
+	dev_dbg(ictx->dev, "Registering iMON display with sysfs\n");
+
+	/* set up sysfs entry for built-in clock */
+	ret = sysfs_create_group(&intf->dev.kobj, &imon_display_attr_group);
+	if (ret)
+		dev_err(ictx->dev, "Could not create display sysfs "
+			"entries(%d)", ret);
+
+	if (ictx->display_type == IMON_DISPLAY_TYPE_LCD)
+		ret = usb_register_dev(intf, &imon_lcd_class);
+	else
+		ret = usb_register_dev(intf, &imon_vfd_class);
+	if (ret)
+		/* Not a fatal error, so ignore */
+		dev_info(ictx->dev, "could not get a minor number for "
+			 "display\n");
+
+}
+
+/**
+ * Callback function for USB core API: Probe
+ */
+static int imon_probe(struct usb_interface *interface,
+		      const struct usb_device_id *id)
+{
+	struct usb_device *usbdev = NULL;
+	struct usb_host_interface *iface_desc = NULL;
+	struct usb_interface *first_if;
+	struct device *dev = &interface->dev;
+	int ifnum, sysfs_err;
+	int ret = 0;
+	struct imon_context *ictx = NULL;
+	struct imon_context *first_if_ctx = NULL;
+	u16 vendor, product;
+
+	usbdev     = usb_get_dev(interface_to_usbdev(interface));
+	iface_desc = interface->cur_altsetting;
+	ifnum      = iface_desc->desc.bInterfaceNumber;
+	vendor     = le16_to_cpu(usbdev->descriptor.idVendor);
+	product    = le16_to_cpu(usbdev->descriptor.idProduct);
+
+	dev_dbg(dev, "%s: found iMON device (%04x:%04x, intf%d)\n",
+		__func__, vendor, product, ifnum);
+
+	/* prevent races probing devices w/multiple interfaces */
+	mutex_lock(&driver_lock);
+
+	first_if = usb_ifnum_to_if(usbdev, 0);
+	if (!first_if) {
+		ret = -ENODEV;
+		goto fail;
+	}
+
+	first_if_ctx = usb_get_intfdata(first_if);
+
+	if (ifnum == 0) {
+		ictx = imon_init_intf0(interface, id);
+		if (!ictx) {
+			pr_err("failed to initialize context!\n");
+			ret = -ENODEV;
+			goto fail;
+		}
+
+	} else {
+		/* this is the secondary interface on the device */
+
+		/* fail early if first intf failed to register */
+		if (!first_if_ctx) {
+			ret = -ENODEV;
+			goto fail;
+		}
+
+		ictx = imon_init_intf1(interface, first_if_ctx);
+		if (!ictx) {
+			pr_err("failed to attach to context!\n");
+			ret = -ENODEV;
+			goto fail;
+		}
+
+	}
+
+	usb_set_intfdata(interface, ictx);
+
+	if (ifnum == 0) {
+		mutex_lock(&ictx->lock);
+
+		if (product == 0xffdc && ictx->rf_device) {
+			sysfs_err = sysfs_create_group(&interface->dev.kobj,
+						       &imon_rf_attr_group);
+			if (sysfs_err)
+				pr_err("Could not create RF sysfs entries(%d)\n",
+				       sysfs_err);
+		}
+
+		if (ictx->display_supported)
+			imon_init_display(ictx, interface);
+
+		mutex_unlock(&ictx->lock);
+	}
+
+	dev_info(dev, "iMON device (%04x:%04x, intf%d) on "
+		 "usb<%d:%d> initialized\n", vendor, product, ifnum,
+		 usbdev->bus->busnum, usbdev->devnum);
+
+	mutex_unlock(&driver_lock);
+	usb_put_dev(usbdev);
+
+	return 0;
+
+fail:
+	mutex_unlock(&driver_lock);
+	usb_put_dev(usbdev);
+	dev_err(dev, "unable to register, err %d\n", ret);
+
+	return ret;
+}
+
+/**
+ * Callback function for USB core API: disconnect
+ */
+static void imon_disconnect(struct usb_interface *interface)
+{
+	struct imon_context *ictx;
+	struct device *dev;
+	int ifnum;
+
+	/* prevent races with multi-interface device probing and display_open */
+	mutex_lock(&driver_lock);
+
+	ictx = usb_get_intfdata(interface);
+	dev = ictx->dev;
+	ifnum = interface->cur_altsetting->desc.bInterfaceNumber;
+
+	/*
+	 * sysfs_remove_group is safe to call even if sysfs_create_group
+	 * hasn't been called
+	 */
+	sysfs_remove_group(&interface->dev.kobj, &imon_display_attr_group);
+	sysfs_remove_group(&interface->dev.kobj, &imon_rf_attr_group);
+
+	usb_set_intfdata(interface, NULL);
+
+	/* Abort ongoing write */
+	if (ictx->tx.busy) {
+		usb_kill_urb(ictx->tx_urb);
+		complete_all(&ictx->tx.finished);
+	}
+
+	if (ifnum == 0) {
+		ictx->dev_present_intf0 = false;
+		usb_kill_urb(ictx->rx_urb_intf0);
+		usb_put_dev(ictx->usbdev_intf0);
+		input_unregister_device(ictx->idev);
+		rc_unregister_device(ictx->rdev);
+		if (ictx->display_supported) {
+			if (ictx->display_type == IMON_DISPLAY_TYPE_LCD)
+				usb_deregister_dev(interface, &imon_lcd_class);
+			else if (ictx->display_type == IMON_DISPLAY_TYPE_VFD)
+				usb_deregister_dev(interface, &imon_vfd_class);
+		}
+	} else {
+		ictx->dev_present_intf1 = false;
+		usb_kill_urb(ictx->rx_urb_intf1);
+		usb_put_dev(ictx->usbdev_intf1);
+		if (ictx->display_type == IMON_DISPLAY_TYPE_VGA) {
+			input_unregister_device(ictx->touch);
+			del_timer_sync(&ictx->ttimer);
+		}
+	}
+
+	if (!ictx->dev_present_intf0 && !ictx->dev_present_intf1)
+		free_imon_context(ictx);
+
+	mutex_unlock(&driver_lock);
+
+	dev_dbg(dev, "%s: iMON device (intf%d) disconnected\n",
+		__func__, ifnum);
+}
+
+static int imon_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct imon_context *ictx = usb_get_intfdata(intf);
+	int ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
+
+	if (ifnum == 0)
+		usb_kill_urb(ictx->rx_urb_intf0);
+	else
+		usb_kill_urb(ictx->rx_urb_intf1);
+
+	return 0;
+}
+
+static int imon_resume(struct usb_interface *intf)
+{
+	int rc = 0;
+	struct imon_context *ictx = usb_get_intfdata(intf);
+	int ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
+
+	if (ifnum == 0) {
+		usb_fill_int_urb(ictx->rx_urb_intf0, ictx->usbdev_intf0,
+			usb_rcvintpipe(ictx->usbdev_intf0,
+				ictx->rx_endpoint_intf0->bEndpointAddress),
+			ictx->usb_rx_buf, sizeof(ictx->usb_rx_buf),
+			usb_rx_callback_intf0, ictx,
+			ictx->rx_endpoint_intf0->bInterval);
+
+		rc = usb_submit_urb(ictx->rx_urb_intf0, GFP_ATOMIC);
+
+	} else {
+		usb_fill_int_urb(ictx->rx_urb_intf1, ictx->usbdev_intf1,
+			usb_rcvintpipe(ictx->usbdev_intf1,
+				ictx->rx_endpoint_intf1->bEndpointAddress),
+			ictx->usb_rx_buf, sizeof(ictx->usb_rx_buf),
+			usb_rx_callback_intf1, ictx,
+			ictx->rx_endpoint_intf1->bInterval);
+
+		rc = usb_submit_urb(ictx->rx_urb_intf1, GFP_ATOMIC);
+	}
+
+	return rc;
+}
+
+module_usb_driver(imon_driver);
diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c
new file mode 100644
index 0000000..d0549fb
--- /dev/null
+++ b/drivers/media/rc/ir-hix5hd2.c
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <media/rc-core.h>
+
+#define IR_ENABLE		0x00
+#define IR_CONFIG		0x04
+#define CNT_LEADS		0x08
+#define CNT_LEADE		0x0c
+#define CNT_SLEADE		0x10
+#define CNT0_B			0x14
+#define CNT1_B			0x18
+#define IR_BUSY			0x1c
+#define IR_DATAH		0x20
+#define IR_DATAL		0x24
+#define IR_INTM			0x28
+#define IR_INTS			0x2c
+#define IR_INTC			0x30
+#define IR_START		0x34
+
+/* interrupt mask */
+#define INTMS_SYMBRCV		(BIT(24) | BIT(8))
+#define INTMS_TIMEOUT		(BIT(25) | BIT(9))
+#define INTMS_OVERFLOW		(BIT(26) | BIT(10))
+#define INT_CLR_OVERFLOW	BIT(18)
+#define INT_CLR_TIMEOUT		BIT(17)
+#define INT_CLR_RCV		BIT(16)
+#define INT_CLR_RCVTIMEOUT	(BIT(16) | BIT(17))
+
+#define IR_CLK			0x48
+#define IR_CLK_ENABLE		BIT(4)
+#define IR_CLK_RESET		BIT(5)
+
+#define IR_CFG_WIDTH_MASK	0xffff
+#define IR_CFG_WIDTH_SHIFT	16
+#define IR_CFG_FORMAT_MASK	0x3
+#define IR_CFG_FORMAT_SHIFT	14
+#define IR_CFG_INT_LEVEL_MASK	0x3f
+#define IR_CFG_INT_LEVEL_SHIFT	8
+/* only support raw mode */
+#define IR_CFG_MODE_RAW		BIT(7)
+#define IR_CFG_FREQ_MASK	0x7f
+#define IR_CFG_FREQ_SHIFT	0
+#define IR_CFG_INT_THRESHOLD	1
+/* symbol start from low to high, symbol stream end at high*/
+#define IR_CFG_SYMBOL_FMT	0
+#define IR_CFG_SYMBOL_MAXWIDTH	0x3e80
+
+#define IR_HIX5HD2_NAME		"hix5hd2-ir"
+
+struct hix5hd2_ir_priv {
+	int			irq;
+	void __iomem		*base;
+	struct device		*dev;
+	struct rc_dev		*rdev;
+	struct regmap		*regmap;
+	struct clk		*clock;
+	unsigned long		rate;
+};
+
+static void hix5hd2_ir_enable(struct hix5hd2_ir_priv *dev, bool on)
+{
+	u32 val;
+
+	regmap_read(dev->regmap, IR_CLK, &val);
+	if (on) {
+		val &= ~IR_CLK_RESET;
+		val |= IR_CLK_ENABLE;
+	} else {
+		val &= ~IR_CLK_ENABLE;
+		val |= IR_CLK_RESET;
+	}
+	regmap_write(dev->regmap, IR_CLK, val);
+}
+
+static int hix5hd2_ir_config(struct hix5hd2_ir_priv *priv)
+{
+	int timeout = 10000;
+	u32 val, rate;
+
+	writel_relaxed(0x01, priv->base + IR_ENABLE);
+	while (readl_relaxed(priv->base + IR_BUSY)) {
+		if (timeout--) {
+			udelay(1);
+		} else {
+			dev_err(priv->dev, "IR_BUSY timeout\n");
+			return -ETIMEDOUT;
+		}
+	}
+
+	/* Now only support raw mode, with symbol start from low to high */
+	rate = DIV_ROUND_CLOSEST(priv->rate, 1000000);
+	val = IR_CFG_SYMBOL_MAXWIDTH & IR_CFG_WIDTH_MASK << IR_CFG_WIDTH_SHIFT;
+	val |= IR_CFG_SYMBOL_FMT & IR_CFG_FORMAT_MASK << IR_CFG_FORMAT_SHIFT;
+	val |= (IR_CFG_INT_THRESHOLD - 1) & IR_CFG_INT_LEVEL_MASK
+	       << IR_CFG_INT_LEVEL_SHIFT;
+	val |= IR_CFG_MODE_RAW;
+	val |= (rate - 1) & IR_CFG_FREQ_MASK << IR_CFG_FREQ_SHIFT;
+	writel_relaxed(val, priv->base + IR_CONFIG);
+
+	writel_relaxed(0x00, priv->base + IR_INTM);
+	/* write arbitrary value to start  */
+	writel_relaxed(0x01, priv->base + IR_START);
+	return 0;
+}
+
+static int hix5hd2_ir_open(struct rc_dev *rdev)
+{
+	struct hix5hd2_ir_priv *priv = rdev->priv;
+
+	hix5hd2_ir_enable(priv, true);
+	return hix5hd2_ir_config(priv);
+}
+
+static void hix5hd2_ir_close(struct rc_dev *rdev)
+{
+	struct hix5hd2_ir_priv *priv = rdev->priv;
+
+	hix5hd2_ir_enable(priv, false);
+}
+
+static irqreturn_t hix5hd2_ir_rx_interrupt(int irq, void *data)
+{
+	u32 symb_num, symb_val, symb_time;
+	u32 data_l, data_h;
+	u32 irq_sr, i;
+	struct hix5hd2_ir_priv *priv = data;
+
+	irq_sr = readl_relaxed(priv->base + IR_INTS);
+	if (irq_sr & INTMS_OVERFLOW) {
+		/*
+		 * we must read IR_DATAL first, then we can clean up
+		 * IR_INTS availably since logic would not clear
+		 * fifo when overflow, drv do the job
+		 */
+		ir_raw_event_reset(priv->rdev);
+		symb_num = readl_relaxed(priv->base + IR_DATAH);
+		for (i = 0; i < symb_num; i++)
+			readl_relaxed(priv->base + IR_DATAL);
+
+		writel_relaxed(INT_CLR_OVERFLOW, priv->base + IR_INTC);
+		dev_info(priv->dev, "overflow, level=%d\n",
+			 IR_CFG_INT_THRESHOLD);
+	}
+
+	if ((irq_sr & INTMS_SYMBRCV) || (irq_sr & INTMS_TIMEOUT)) {
+		DEFINE_IR_RAW_EVENT(ev);
+
+		symb_num = readl_relaxed(priv->base + IR_DATAH);
+		for (i = 0; i < symb_num; i++) {
+			symb_val = readl_relaxed(priv->base + IR_DATAL);
+			data_l = ((symb_val & 0xffff) * 10);
+			data_h =  ((symb_val >> 16) & 0xffff) * 10;
+			symb_time = (data_l + data_h) / 10;
+
+			ev.duration = US_TO_NS(data_l);
+			ev.pulse = true;
+			ir_raw_event_store(priv->rdev, &ev);
+
+			if (symb_time < IR_CFG_SYMBOL_MAXWIDTH) {
+				ev.duration = US_TO_NS(data_h);
+				ev.pulse = false;
+				ir_raw_event_store(priv->rdev, &ev);
+			} else {
+				ir_raw_event_set_idle(priv->rdev, true);
+			}
+		}
+
+		if (irq_sr & INTMS_SYMBRCV)
+			writel_relaxed(INT_CLR_RCV, priv->base + IR_INTC);
+		if (irq_sr & INTMS_TIMEOUT)
+			writel_relaxed(INT_CLR_TIMEOUT, priv->base + IR_INTC);
+	}
+
+	/* Empty software fifo */
+	ir_raw_event_handle(priv->rdev);
+	return IRQ_HANDLED;
+}
+
+static int hix5hd2_ir_probe(struct platform_device *pdev)
+{
+	struct rc_dev *rdev;
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct hix5hd2_ir_priv *priv;
+	struct device_node *node = pdev->dev.of_node;
+	const char *map_name;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->regmap = syscon_regmap_lookup_by_phandle(node,
+						       "hisilicon,power-syscon");
+	if (IS_ERR(priv->regmap)) {
+		dev_err(dev, "no power-reg\n");
+		return -EINVAL;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(priv->base))
+		return PTR_ERR(priv->base);
+
+	priv->irq = platform_get_irq(pdev, 0);
+	if (priv->irq < 0) {
+		dev_err(dev, "irq can not get\n");
+		return priv->irq;
+	}
+
+	rdev = rc_allocate_device();
+	if (!rdev)
+		return -ENOMEM;
+
+	priv->clock = devm_clk_get(dev, NULL);
+	if (IS_ERR(priv->clock)) {
+		dev_err(dev, "clock not found\n");
+		ret = PTR_ERR(priv->clock);
+		goto err;
+	}
+	clk_prepare_enable(priv->clock);
+	priv->rate = clk_get_rate(priv->clock);
+
+	rdev->driver_type = RC_DRIVER_IR_RAW;
+	rdev->allowed_protocols = RC_BIT_ALL;
+	rdev->priv = priv;
+	rdev->open = hix5hd2_ir_open;
+	rdev->close = hix5hd2_ir_close;
+	rdev->driver_name = IR_HIX5HD2_NAME;
+	map_name = of_get_property(node, "linux,rc-map-name", NULL);
+	rdev->map_name = map_name ?: RC_MAP_EMPTY;
+	rdev->input_name = IR_HIX5HD2_NAME;
+	rdev->input_phys = IR_HIX5HD2_NAME "/input0";
+	rdev->input_id.bustype = BUS_HOST;
+	rdev->input_id.vendor = 0x0001;
+	rdev->input_id.product = 0x0001;
+	rdev->input_id.version = 0x0100;
+	rdev->rx_resolution = US_TO_NS(10);
+	rdev->timeout = US_TO_NS(IR_CFG_SYMBOL_MAXWIDTH * 10);
+
+	ret = rc_register_device(rdev);
+	if (ret < 0)
+		goto clkerr;
+
+	if (devm_request_irq(dev, priv->irq, hix5hd2_ir_rx_interrupt,
+			     0, pdev->name, priv) < 0) {
+		dev_err(dev, "IRQ %d register failed\n", priv->irq);
+		ret = -EINVAL;
+		goto regerr;
+	}
+
+	priv->rdev = rdev;
+	priv->dev = dev;
+	platform_set_drvdata(pdev, priv);
+
+	return ret;
+
+regerr:
+	rc_unregister_device(rdev);
+	rdev = NULL;
+clkerr:
+	clk_disable_unprepare(priv->clock);
+err:
+	rc_free_device(rdev);
+	dev_err(dev, "Unable to register device (%d)\n", ret);
+	return ret;
+}
+
+static int hix5hd2_ir_remove(struct platform_device *pdev)
+{
+	struct hix5hd2_ir_priv *priv = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(priv->clock);
+	rc_unregister_device(priv->rdev);
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int hix5hd2_ir_suspend(struct device *dev)
+{
+	struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(priv->clock);
+	hix5hd2_ir_enable(priv, false);
+
+	return 0;
+}
+
+static int hix5hd2_ir_resume(struct device *dev)
+{
+	struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev);
+
+	hix5hd2_ir_enable(priv, true);
+	clk_prepare_enable(priv->clock);
+
+	writel_relaxed(0x01, priv->base + IR_ENABLE);
+	writel_relaxed(0x00, priv->base + IR_INTM);
+	writel_relaxed(0xff, priv->base + IR_INTC);
+	writel_relaxed(0x01, priv->base + IR_START);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(hix5hd2_ir_pm_ops, hix5hd2_ir_suspend,
+			 hix5hd2_ir_resume);
+
+static const struct of_device_id hix5hd2_ir_table[] = {
+	{ .compatible = "hisilicon,hix5hd2-ir", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, hix5hd2_ir_table);
+
+static struct platform_driver hix5hd2_ir_driver = {
+	.driver = {
+		.name = IR_HIX5HD2_NAME,
+		.of_match_table = hix5hd2_ir_table,
+		.pm     = &hix5hd2_ir_pm_ops,
+	},
+	.probe = hix5hd2_ir_probe,
+	.remove = hix5hd2_ir_remove,
+};
+
+module_platform_driver(hix5hd2_ir_driver);
+
+MODULE_DESCRIPTION("IR controller driver for hix5hd2 platforms");
+MODULE_AUTHOR("Guoxiong Yan <yanguoxiong@huawei.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:hix5hd2-ir");
diff --git a/drivers/media/rc/ir-jvc-decoder.c b/drivers/media/rc/ir-jvc-decoder.c
new file mode 100644
index 0000000..30bcf18
--- /dev/null
+++ b/drivers/media/rc/ir-jvc-decoder.c
@@ -0,0 +1,199 @@
+/* ir-jvc-decoder.c - handle JVC IR Pulse/Space protocol
+ *
+ * Copyright (C) 2010 by David Härdeman <david@hardeman.nu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * 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.
+ */
+
+#include <linux/bitrev.h>
+#include <linux/module.h>
+#include "rc-core-priv.h"
+
+#define JVC_NBITS		16		/* dev(8) + func(8) */
+#define JVC_UNIT		525000		/* ns */
+#define JVC_HEADER_PULSE	(16 * JVC_UNIT) /* lack of header -> repeat */
+#define JVC_HEADER_SPACE	(8  * JVC_UNIT)
+#define JVC_BIT_PULSE		(1  * JVC_UNIT)
+#define JVC_BIT_0_SPACE		(1  * JVC_UNIT)
+#define JVC_BIT_1_SPACE		(3  * JVC_UNIT)
+#define JVC_TRAILER_PULSE	(1  * JVC_UNIT)
+#define	JVC_TRAILER_SPACE	(35 * JVC_UNIT)
+
+enum jvc_state {
+	STATE_INACTIVE,
+	STATE_HEADER_SPACE,
+	STATE_BIT_PULSE,
+	STATE_BIT_SPACE,
+	STATE_TRAILER_PULSE,
+	STATE_TRAILER_SPACE,
+	STATE_CHECK_REPEAT,
+};
+
+/**
+ * ir_jvc_decode() - Decode one JVC pulse or space
+ * @dev:	the struct rc_dev descriptor of the device
+ * @duration:   the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This function returns -EINVAL if the pulse violates the state machine
+ */
+static int ir_jvc_decode(struct rc_dev *dev, struct ir_raw_event ev)
+{
+	struct jvc_dec *data = &dev->raw->jvc;
+
+	if (!(dev->enabled_protocols & RC_BIT_JVC))
+		return 0;
+
+	if (!is_timing_event(ev)) {
+		if (ev.reset)
+			data->state = STATE_INACTIVE;
+		return 0;
+	}
+
+	if (!geq_margin(ev.duration, JVC_UNIT, JVC_UNIT / 2))
+		goto out;
+
+	IR_dprintk(2, "JVC decode started at state %d (%uus %s)\n",
+		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+
+again:
+	switch (data->state) {
+
+	case STATE_INACTIVE:
+		if (!ev.pulse)
+			break;
+
+		if (!eq_margin(ev.duration, JVC_HEADER_PULSE, JVC_UNIT / 2))
+			break;
+
+		data->count = 0;
+		data->first = true;
+		data->toggle = !data->toggle;
+		data->state = STATE_HEADER_SPACE;
+		return 0;
+
+	case STATE_HEADER_SPACE:
+		if (ev.pulse)
+			break;
+
+		if (!eq_margin(ev.duration, JVC_HEADER_SPACE, JVC_UNIT / 2))
+			break;
+
+		data->state = STATE_BIT_PULSE;
+		return 0;
+
+	case STATE_BIT_PULSE:
+		if (!ev.pulse)
+			break;
+
+		if (!eq_margin(ev.duration, JVC_BIT_PULSE, JVC_UNIT / 2))
+			break;
+
+		data->state = STATE_BIT_SPACE;
+		return 0;
+
+	case STATE_BIT_SPACE:
+		if (ev.pulse)
+			break;
+
+		data->bits <<= 1;
+		if (eq_margin(ev.duration, JVC_BIT_1_SPACE, JVC_UNIT / 2)) {
+			data->bits |= 1;
+			decrease_duration(&ev, JVC_BIT_1_SPACE);
+		} else if (eq_margin(ev.duration, JVC_BIT_0_SPACE, JVC_UNIT / 2))
+			decrease_duration(&ev, JVC_BIT_0_SPACE);
+		else
+			break;
+		data->count++;
+
+		if (data->count == JVC_NBITS)
+			data->state = STATE_TRAILER_PULSE;
+		else
+			data->state = STATE_BIT_PULSE;
+		return 0;
+
+	case STATE_TRAILER_PULSE:
+		if (!ev.pulse)
+			break;
+
+		if (!eq_margin(ev.duration, JVC_TRAILER_PULSE, JVC_UNIT / 2))
+			break;
+
+		data->state = STATE_TRAILER_SPACE;
+		return 0;
+
+	case STATE_TRAILER_SPACE:
+		if (ev.pulse)
+			break;
+
+		if (!geq_margin(ev.duration, JVC_TRAILER_SPACE, JVC_UNIT / 2))
+			break;
+
+		if (data->first) {
+			u32 scancode;
+			scancode = (bitrev8((data->bits >> 8) & 0xff) << 8) |
+				   (bitrev8((data->bits >> 0) & 0xff) << 0);
+			IR_dprintk(1, "JVC scancode 0x%04x\n", scancode);
+			rc_keydown(dev, RC_TYPE_JVC, scancode, data->toggle);
+			data->first = false;
+			data->old_bits = data->bits;
+		} else if (data->bits == data->old_bits) {
+			IR_dprintk(1, "JVC repeat\n");
+			rc_repeat(dev);
+		} else {
+			IR_dprintk(1, "JVC invalid repeat msg\n");
+			break;
+		}
+
+		data->count = 0;
+		data->state = STATE_CHECK_REPEAT;
+		return 0;
+
+	case STATE_CHECK_REPEAT:
+		if (!ev.pulse)
+			break;
+
+		if (eq_margin(ev.duration, JVC_HEADER_PULSE, JVC_UNIT / 2))
+			data->state = STATE_INACTIVE;
+  else
+			data->state = STATE_BIT_PULSE;
+		goto again;
+	}
+
+out:
+	IR_dprintk(1, "JVC decode failed at state %d (%uus %s)\n",
+		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+	data->state = STATE_INACTIVE;
+	return -EINVAL;
+}
+
+static struct ir_raw_handler jvc_handler = {
+	.protocols	= RC_BIT_JVC,
+	.decode		= ir_jvc_decode,
+};
+
+static int __init ir_jvc_decode_init(void)
+{
+	ir_raw_handler_register(&jvc_handler);
+
+	printk(KERN_INFO "IR JVC protocol handler initialized\n");
+	return 0;
+}
+
+static void __exit ir_jvc_decode_exit(void)
+{
+	ir_raw_handler_unregister(&jvc_handler);
+}
+
+module_init(ir_jvc_decode_init);
+module_exit(ir_jvc_decode_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Härdeman <david@hardeman.nu>");
+MODULE_DESCRIPTION("JVC IR protocol decoder");
diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c
new file mode 100644
index 0000000..efc21b1
--- /dev/null
+++ b/drivers/media/rc/ir-lirc-codec.c
@@ -0,0 +1,449 @@
+/* ir-lirc-codec.c - rc-core to classic lirc interface bridge
+ *
+ * Copyright (C) 2010 by Jarod Wilson <jarod@redhat.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 version 2 of the License.
+ *
+ *  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.
+ */
+
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/module.h>
+#include <media/lirc.h>
+#include <media/lirc_dev.h>
+#include <media/rc-core.h>
+#include "rc-core-priv.h"
+
+#define LIRCBUF_SIZE 256
+
+/**
+ * ir_lirc_decode() - Send raw IR data to lirc_dev to be relayed to the
+ *		      lircd userspace daemon for decoding.
+ * @input_dev:	the struct rc_dev descriptor of the device
+ * @duration:	the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This function returns -EINVAL if the lirc interfaces aren't wired up.
+ */
+static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev)
+{
+	struct lirc_codec *lirc = &dev->raw->lirc;
+	int sample;
+
+	if (!dev->raw->lirc.drv || !dev->raw->lirc.drv->rbuf)
+		return -EINVAL;
+
+	/* Packet start */
+	if (ev.reset) {
+		/* Userspace expects a long space event before the start of
+		 * the signal to use as a sync.  This may be done with repeat
+		 * packets and normal samples.  But if a reset has been sent
+		 * then we assume that a long time has passed, so we send a
+		 * space with the maximum time value. */
+		sample = LIRC_SPACE(LIRC_VALUE_MASK);
+		IR_dprintk(2, "delivering reset sync space to lirc_dev\n");
+
+	/* Carrier reports */
+	} else if (ev.carrier_report) {
+		sample = LIRC_FREQUENCY(ev.carrier);
+		IR_dprintk(2, "carrier report (freq: %d)\n", sample);
+
+	/* Packet end */
+	} else if (ev.timeout) {
+
+		if (lirc->gap)
+			return 0;
+
+		lirc->gap_start = ktime_get();
+		lirc->gap = true;
+		lirc->gap_duration = ev.duration;
+
+		if (!lirc->send_timeout_reports)
+			return 0;
+
+		sample = LIRC_TIMEOUT(ev.duration / 1000);
+		IR_dprintk(2, "timeout report (duration: %d)\n", sample);
+
+	/* Normal sample */
+	} else {
+
+		if (lirc->gap) {
+			int gap_sample;
+
+			lirc->gap_duration += ktime_to_ns(ktime_sub(ktime_get(),
+				lirc->gap_start));
+
+			/* Convert to ms and cap by LIRC_VALUE_MASK */
+			do_div(lirc->gap_duration, 1000);
+			lirc->gap_duration = min(lirc->gap_duration,
+							(u64)LIRC_VALUE_MASK);
+
+			gap_sample = LIRC_SPACE(lirc->gap_duration);
+			lirc_buffer_write(dev->raw->lirc.drv->rbuf,
+						(unsigned char *) &gap_sample);
+			lirc->gap = false;
+		}
+
+		sample = ev.pulse ? LIRC_PULSE(ev.duration / 1000) :
+					LIRC_SPACE(ev.duration / 1000);
+		IR_dprintk(2, "delivering %uus %s to lirc_dev\n",
+			   TO_US(ev.duration), TO_STR(ev.pulse));
+	}
+
+	lirc_buffer_write(dev->raw->lirc.drv->rbuf,
+			  (unsigned char *) &sample);
+	wake_up(&dev->raw->lirc.drv->rbuf->wait_poll);
+
+	return 0;
+}
+
+static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
+				   size_t n, loff_t *ppos)
+{
+	struct lirc_codec *lirc;
+	struct rc_dev *dev;
+	unsigned int *txbuf; /* buffer with values to transmit */
+	ssize_t ret = -EINVAL;
+	size_t count;
+	ktime_t start;
+	s64 towait;
+	unsigned int duration = 0; /* signal duration in us */
+	int i;
+
+	start = ktime_get();
+
+	lirc = lirc_get_pdata(file);
+	if (!lirc)
+		return -EFAULT;
+
+	if (n < sizeof(unsigned) || n % sizeof(unsigned))
+		return -EINVAL;
+
+	count = n / sizeof(unsigned);
+	if (count > LIRCBUF_SIZE || count % 2 == 0)
+		return -EINVAL;
+
+	txbuf = memdup_user(buf, n);
+	if (IS_ERR(txbuf))
+		return PTR_ERR(txbuf);
+
+	dev = lirc->dev;
+	if (!dev) {
+		ret = -EFAULT;
+		goto out;
+	}
+
+	if (!dev->tx_ir) {
+		ret = -ENOSYS;
+		goto out;
+	}
+
+	for (i = 0; i < count; i++) {
+		if (txbuf[i] > IR_MAX_DURATION / 1000 - duration || !txbuf[i]) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+		duration += txbuf[i];
+	}
+
+	ret = dev->tx_ir(dev, txbuf, count);
+	if (ret < 0)
+		goto out;
+
+	for (duration = i = 0; i < ret; i++)
+		duration += txbuf[i];
+
+	ret *= sizeof(unsigned int);
+
+	/*
+	 * The lircd gap calculation expects the write function to
+	 * wait for the actual IR signal to be transmitted before
+	 * returning.
+	 */
+	towait = ktime_us_delta(ktime_add_us(start, duration), ktime_get());
+	if (towait > 0) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(usecs_to_jiffies(towait));
+	}
+
+out:
+	kfree(txbuf);
+	return ret;
+}
+
+static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
+			unsigned long arg)
+{
+	struct lirc_codec *lirc;
+	struct rc_dev *dev;
+	u32 __user *argp = (u32 __user *)(arg);
+	int ret = 0;
+	__u32 val = 0, tmp;
+
+	lirc = lirc_get_pdata(filep);
+	if (!lirc)
+		return -EFAULT;
+
+	dev = lirc->dev;
+	if (!dev)
+		return -EFAULT;
+
+	if (_IOC_DIR(cmd) & _IOC_WRITE) {
+		ret = get_user(val, argp);
+		if (ret)
+			return ret;
+	}
+
+	switch (cmd) {
+
+	/* legacy support */
+	case LIRC_GET_SEND_MODE:
+		val = LIRC_CAN_SEND_PULSE & LIRC_CAN_SEND_MASK;
+		break;
+
+	case LIRC_SET_SEND_MODE:
+		if (val != (LIRC_MODE_PULSE & LIRC_CAN_SEND_MASK))
+			return -EINVAL;
+		return 0;
+
+	/* TX settings */
+	case LIRC_SET_TRANSMITTER_MASK:
+		if (!dev->s_tx_mask)
+			return -ENOSYS;
+
+		return dev->s_tx_mask(dev, val);
+
+	case LIRC_SET_SEND_CARRIER:
+		if (!dev->s_tx_carrier)
+			return -ENOSYS;
+
+		return dev->s_tx_carrier(dev, val);
+
+	case LIRC_SET_SEND_DUTY_CYCLE:
+		if (!dev->s_tx_duty_cycle)
+			return -ENOSYS;
+
+		if (val <= 0 || val >= 100)
+			return -EINVAL;
+
+		return dev->s_tx_duty_cycle(dev, val);
+
+	/* RX settings */
+	case LIRC_SET_REC_CARRIER:
+		if (!dev->s_rx_carrier_range)
+			return -ENOSYS;
+
+		if (val <= 0)
+			return -EINVAL;
+
+		return dev->s_rx_carrier_range(dev,
+					       dev->raw->lirc.carrier_low,
+					       val);
+
+	case LIRC_SET_REC_CARRIER_RANGE:
+		if (val <= 0)
+			return -EINVAL;
+
+		dev->raw->lirc.carrier_low = val;
+		return 0;
+
+	case LIRC_GET_REC_RESOLUTION:
+		val = dev->rx_resolution / 1000;
+		break;
+
+	case LIRC_SET_WIDEBAND_RECEIVER:
+		if (!dev->s_learning_mode)
+			return -ENOSYS;
+
+		return dev->s_learning_mode(dev, !!val);
+
+	case LIRC_SET_MEASURE_CARRIER_MODE:
+		if (!dev->s_carrier_report)
+			return -ENOSYS;
+
+		return dev->s_carrier_report(dev, !!val);
+
+	/* Generic timeout support */
+	case LIRC_GET_MIN_TIMEOUT:
+		if (!dev->max_timeout)
+			return -ENOSYS;
+		val = dev->min_timeout / 1000;
+		break;
+
+	case LIRC_GET_MAX_TIMEOUT:
+		if (!dev->max_timeout)
+			return -ENOSYS;
+		val = dev->max_timeout / 1000;
+		break;
+
+	case LIRC_SET_REC_TIMEOUT:
+		if (!dev->max_timeout)
+			return -ENOSYS;
+
+		tmp = val * 1000;
+
+		if (tmp < dev->min_timeout ||
+		    tmp > dev->max_timeout)
+				return -EINVAL;
+
+		dev->timeout = tmp;
+		break;
+
+	case LIRC_SET_REC_TIMEOUT_REPORTS:
+		lirc->send_timeout_reports = !!val;
+		break;
+
+	default:
+		return lirc_dev_fop_ioctl(filep, cmd, arg);
+	}
+
+	if (_IOC_DIR(cmd) & _IOC_READ)
+		ret = put_user(val, argp);
+
+	return ret;
+}
+
+static int ir_lirc_open(void *data)
+{
+	return 0;
+}
+
+static void ir_lirc_close(void *data)
+{
+	return;
+}
+
+static const struct file_operations lirc_fops = {
+	.owner		= THIS_MODULE,
+	.write		= ir_lirc_transmit_ir,
+	.unlocked_ioctl	= ir_lirc_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= ir_lirc_ioctl,
+#endif
+	.read		= lirc_dev_fop_read,
+	.poll		= lirc_dev_fop_poll,
+	.open		= lirc_dev_fop_open,
+	.release	= lirc_dev_fop_close,
+	.llseek		= no_llseek,
+};
+
+static int ir_lirc_register(struct rc_dev *dev)
+{
+	struct lirc_driver *drv;
+	struct lirc_buffer *rbuf;
+	int rc = -ENOMEM;
+	unsigned long features;
+
+	drv = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
+	if (!drv)
+		return rc;
+
+	rbuf = kzalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
+	if (!rbuf)
+		goto rbuf_alloc_failed;
+
+	rc = lirc_buffer_init(rbuf, sizeof(int), LIRCBUF_SIZE);
+	if (rc)
+		goto rbuf_init_failed;
+
+	features = LIRC_CAN_REC_MODE2;
+	if (dev->tx_ir) {
+		features |= LIRC_CAN_SEND_PULSE;
+		if (dev->s_tx_mask)
+			features |= LIRC_CAN_SET_TRANSMITTER_MASK;
+		if (dev->s_tx_carrier)
+			features |= LIRC_CAN_SET_SEND_CARRIER;
+		if (dev->s_tx_duty_cycle)
+			features |= LIRC_CAN_SET_SEND_DUTY_CYCLE;
+	}
+
+	if (dev->s_rx_carrier_range)
+		features |= LIRC_CAN_SET_REC_CARRIER |
+			LIRC_CAN_SET_REC_CARRIER_RANGE;
+
+	if (dev->s_learning_mode)
+		features |= LIRC_CAN_USE_WIDEBAND_RECEIVER;
+
+	if (dev->s_carrier_report)
+		features |= LIRC_CAN_MEASURE_CARRIER;
+
+	if (dev->max_timeout)
+		features |= LIRC_CAN_SET_REC_TIMEOUT;
+
+	snprintf(drv->name, sizeof(drv->name), "ir-lirc-codec (%s)",
+		 dev->driver_name);
+	drv->minor = -1;
+	drv->features = features;
+	drv->data = &dev->raw->lirc;
+	drv->rbuf = rbuf;
+	drv->set_use_inc = &ir_lirc_open;
+	drv->set_use_dec = &ir_lirc_close;
+	drv->code_length = sizeof(struct ir_raw_event) * 8;
+	drv->fops = &lirc_fops;
+	drv->dev = &dev->dev;
+	drv->rdev = dev;
+	drv->owner = THIS_MODULE;
+
+	drv->minor = lirc_register_driver(drv);
+	if (drv->minor < 0) {
+		rc = -ENODEV;
+		goto lirc_register_failed;
+	}
+
+	dev->raw->lirc.drv = drv;
+	dev->raw->lirc.dev = dev;
+	return 0;
+
+lirc_register_failed:
+rbuf_init_failed:
+	kfree(rbuf);
+rbuf_alloc_failed:
+	kfree(drv);
+
+	return rc;
+}
+
+static int ir_lirc_unregister(struct rc_dev *dev)
+{
+	struct lirc_codec *lirc = &dev->raw->lirc;
+
+	lirc_unregister_driver(lirc->drv->minor);
+	lirc_buffer_free(lirc->drv->rbuf);
+	kfree(lirc->drv);
+
+	return 0;
+}
+
+static struct ir_raw_handler lirc_handler = {
+	.protocols	= 0,
+	.decode		= ir_lirc_decode,
+	.raw_register	= ir_lirc_register,
+	.raw_unregister	= ir_lirc_unregister,
+};
+
+static int __init ir_lirc_codec_init(void)
+{
+	ir_raw_handler_register(&lirc_handler);
+
+	printk(KERN_INFO "IR LIRC bridge handler initialized\n");
+	return 0;
+}
+
+static void __exit ir_lirc_codec_exit(void)
+{
+	ir_raw_handler_unregister(&lirc_handler);
+}
+
+module_init(ir_lirc_codec_init);
+module_exit(ir_lirc_codec_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
+MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
+MODULE_DESCRIPTION("LIRC IR handler bridge");
diff --git a/drivers/media/rc/ir-mce_kbd-decoder.c b/drivers/media/rc/ir-mce_kbd-decoder.c
new file mode 100644
index 0000000..9f3c9b5
--- /dev/null
+++ b/drivers/media/rc/ir-mce_kbd-decoder.c
@@ -0,0 +1,449 @@
+/* ir-mce_kbd-decoder.c - A decoder for the RC6-ish keyboard/mouse IR protocol
+ * used by the Microsoft Remote Keyboard for Windows Media Center Edition,
+ * referred to by Microsoft's Windows Media Center remote specification docs
+ * as "an internal protocol called MCIR-2".
+ *
+ * Copyright (C) 2011 by Jarod Wilson <jarod@redhat.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 version 2 of the License.
+ *
+ * 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.
+ */
+#include <linux/module.h>
+
+#include "rc-core-priv.h"
+
+/*
+ * This decoder currently supports:
+ * - MCIR-2 29-bit IR signals used for mouse movement and buttons
+ * - MCIR-2 32-bit IR signals used for standard keyboard keys
+ *
+ * The media keys on the keyboard send RC-6 signals that are inditinguishable
+ * from the keys of the same name on the stock MCE remote, and will be handled
+ * by the standard RC-6 decoder, and be made available to the system via the
+ * input device for the remote, rather than the keyboard/mouse one.
+ */
+
+#define MCIR2_UNIT		333333	/* ns */
+#define MCIR2_HEADER_NBITS	5
+#define MCIR2_MOUSE_NBITS	29
+#define MCIR2_KEYBOARD_NBITS	32
+#define MCIR2_PREFIX_PULSE	(8 * MCIR2_UNIT)
+#define MCIR2_PREFIX_SPACE	(1 * MCIR2_UNIT)
+#define MCIR2_MAX_LEN		(3 * MCIR2_UNIT)
+#define MCIR2_BIT_START		(1 * MCIR2_UNIT)
+#define MCIR2_BIT_END		(1 * MCIR2_UNIT)
+#define MCIR2_BIT_0		(1 * MCIR2_UNIT)
+#define MCIR2_BIT_SET		(2 * MCIR2_UNIT)
+#define MCIR2_MODE_MASK		0xf	/* for the header bits */
+#define MCIR2_KEYBOARD_HEADER	0x4
+#define MCIR2_MOUSE_HEADER	0x1
+#define MCIR2_MASK_KEYS_START	0xe0
+
+enum mce_kbd_mode {
+	MCIR2_MODE_KEYBOARD,
+	MCIR2_MODE_MOUSE,
+	MCIR2_MODE_UNKNOWN,
+};
+
+enum mce_kbd_state {
+	STATE_INACTIVE,
+	STATE_HEADER_BIT_START,
+	STATE_HEADER_BIT_END,
+	STATE_BODY_BIT_START,
+	STATE_BODY_BIT_END,
+	STATE_FINISHED,
+};
+
+static unsigned char kbd_keycodes[256] = {
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_A,
+	KEY_B,		KEY_C,		KEY_D,		KEY_E,		KEY_F,
+	KEY_G,		KEY_H,		KEY_I,		KEY_J,		KEY_K,
+	KEY_L,		KEY_M,		KEY_N,		KEY_O,		KEY_P,
+	KEY_Q,		KEY_R,		KEY_S,		KEY_T,		KEY_U,
+	KEY_V,		KEY_W,		KEY_X,		KEY_Y,		KEY_Z,
+	KEY_1,		KEY_2,		KEY_3,		KEY_4,		KEY_5,
+	KEY_6,		KEY_7,		KEY_8,		KEY_9,		KEY_0,
+	KEY_ENTER,	KEY_ESC,	KEY_BACKSPACE,	KEY_TAB,	KEY_SPACE,
+	KEY_MINUS,	KEY_EQUAL,	KEY_LEFTBRACE,	KEY_RIGHTBRACE,	KEY_BACKSLASH,
+	KEY_RESERVED,	KEY_SEMICOLON,	KEY_APOSTROPHE,	KEY_GRAVE,	KEY_COMMA,
+	KEY_DOT,	KEY_SLASH,	KEY_CAPSLOCK,	KEY_F1,		KEY_F2,
+	KEY_F3,		KEY_F4,		KEY_F5,		KEY_F6,		KEY_F7,
+	KEY_F8,		KEY_F9,		KEY_F10,	KEY_F11,	KEY_F12,
+	KEY_SYSRQ,	KEY_SCROLLLOCK,	KEY_PAUSE,	KEY_INSERT,	KEY_HOME,
+	KEY_PAGEUP,	KEY_DELETE,	KEY_END,	KEY_PAGEDOWN,	KEY_RIGHT,
+	KEY_LEFT,	KEY_DOWN,	KEY_UP,		KEY_NUMLOCK,	KEY_KPSLASH,
+	KEY_KPASTERISK,	KEY_KPMINUS,	KEY_KPPLUS,	KEY_KPENTER,	KEY_KP1,
+	KEY_KP2,	KEY_KP3,	KEY_KP4,	KEY_KP5,	KEY_KP6,
+	KEY_KP7,	KEY_KP8,	KEY_KP9,	KEY_KP0,	KEY_KPDOT,
+	KEY_102ND,	KEY_COMPOSE,	KEY_POWER,	KEY_KPEQUAL,	KEY_F13,
+	KEY_F14,	KEY_F15,	KEY_F16,	KEY_F17,	KEY_F18,
+	KEY_F19,	KEY_F20,	KEY_F21,	KEY_F22,	KEY_F23,
+	KEY_F24,	KEY_OPEN,	KEY_HELP,	KEY_PROPS,	KEY_FRONT,
+	KEY_STOP,	KEY_AGAIN,	KEY_UNDO,	KEY_CUT,	KEY_COPY,
+	KEY_PASTE,	KEY_FIND,	KEY_MUTE,	KEY_VOLUMEUP,	KEY_VOLUMEDOWN,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_KPCOMMA,	KEY_RESERVED,
+	KEY_RO,		KEY_KATAKANAHIRAGANA, KEY_YEN,	KEY_HENKAN,	KEY_MUHENKAN,
+	KEY_KPJPCOMMA,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_HANGUEL,
+	KEY_HANJA,	KEY_KATAKANA,	KEY_HIRAGANA,	KEY_ZENKAKUHANKAKU, KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,	KEY_LEFTCTRL,
+	KEY_LEFTSHIFT,	KEY_LEFTALT,	KEY_LEFTMETA,	KEY_RIGHTCTRL,	KEY_RIGHTSHIFT,
+	KEY_RIGHTALT,	KEY_RIGHTMETA,	KEY_PLAYPAUSE,	KEY_STOPCD,	KEY_PREVIOUSSONG,
+	KEY_NEXTSONG,	KEY_EJECTCD,	KEY_VOLUMEUP,	KEY_VOLUMEDOWN,	KEY_MUTE,
+	KEY_WWW,	KEY_BACK,	KEY_FORWARD,	KEY_STOP,	KEY_FIND,
+	KEY_SCROLLUP,	KEY_SCROLLDOWN,	KEY_EDIT,	KEY_SLEEP,	KEY_COFFEE,
+	KEY_REFRESH,	KEY_CALC,	KEY_RESERVED,	KEY_RESERVED,	KEY_RESERVED,
+	KEY_RESERVED
+};
+
+static void mce_kbd_rx_timeout(unsigned long data)
+{
+	struct mce_kbd_dec *mce_kbd = (struct mce_kbd_dec *)data;
+	int i;
+	unsigned char maskcode;
+
+	IR_dprintk(2, "timer callback clearing all keys\n");
+
+	for (i = 0; i < 7; i++) {
+		maskcode = kbd_keycodes[MCIR2_MASK_KEYS_START + i];
+		input_report_key(mce_kbd->idev, maskcode, 0);
+	}
+
+	for (i = 0; i < MCIR2_MASK_KEYS_START; i++)
+		input_report_key(mce_kbd->idev, kbd_keycodes[i], 0);
+}
+
+static enum mce_kbd_mode mce_kbd_mode(struct mce_kbd_dec *data)
+{
+	switch (data->header & MCIR2_MODE_MASK) {
+	case MCIR2_KEYBOARD_HEADER:
+		return MCIR2_MODE_KEYBOARD;
+	case MCIR2_MOUSE_HEADER:
+		return MCIR2_MODE_MOUSE;
+	default:
+		return MCIR2_MODE_UNKNOWN;
+	}
+}
+
+static void ir_mce_kbd_process_keyboard_data(struct input_dev *idev,
+					     u32 scancode)
+{
+	u8 keydata   = (scancode >> 8) & 0xff;
+	u8 shiftmask = scancode & 0xff;
+	unsigned char keycode, maskcode;
+	int i, keystate;
+
+	IR_dprintk(1, "keyboard: keydata = 0x%02x, shiftmask = 0x%02x\n",
+		   keydata, shiftmask);
+
+	for (i = 0; i < 7; i++) {
+		maskcode = kbd_keycodes[MCIR2_MASK_KEYS_START + i];
+		if (shiftmask & (1 << i))
+			keystate = 1;
+		else
+			keystate = 0;
+		input_report_key(idev, maskcode, keystate);
+	}
+
+	if (keydata) {
+		keycode = kbd_keycodes[keydata];
+		input_report_key(idev, keycode, 1);
+	} else {
+		for (i = 0; i < MCIR2_MASK_KEYS_START; i++)
+			input_report_key(idev, kbd_keycodes[i], 0);
+	}
+}
+
+static void ir_mce_kbd_process_mouse_data(struct input_dev *idev, u32 scancode)
+{
+	/* raw mouse coordinates */
+	u8 xdata = (scancode >> 7) & 0x7f;
+	u8 ydata = (scancode >> 14) & 0x7f;
+	int x, y;
+	/* mouse buttons */
+	bool right = scancode & 0x40;
+	bool left  = scancode & 0x20;
+
+	if (xdata & 0x40)
+		x = -((~xdata & 0x7f) + 1);
+	else
+		x = xdata;
+
+	if (ydata & 0x40)
+		y = -((~ydata & 0x7f) + 1);
+	else
+		y = ydata;
+
+	IR_dprintk(1, "mouse: x = %d, y = %d, btns = %s%s\n",
+		   x, y, left ? "L" : "", right ? "R" : "");
+
+	input_report_rel(idev, REL_X, x);
+	input_report_rel(idev, REL_Y, y);
+
+	input_report_key(idev, BTN_LEFT, left);
+	input_report_key(idev, BTN_RIGHT, right);
+}
+
+/**
+ * ir_mce_kbd_decode() - Decode one mce_kbd pulse or space
+ * @dev:	the struct rc_dev descriptor of the device
+ * @ev:		the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This function returns -EINVAL if the pulse violates the state machine
+ */
+static int ir_mce_kbd_decode(struct rc_dev *dev, struct ir_raw_event ev)
+{
+	struct mce_kbd_dec *data = &dev->raw->mce_kbd;
+	u32 scancode;
+	unsigned long delay;
+
+	if (!(dev->enabled_protocols & RC_BIT_MCE_KBD))
+		return 0;
+
+	if (!is_timing_event(ev)) {
+		if (ev.reset)
+			data->state = STATE_INACTIVE;
+		return 0;
+	}
+
+	if (!geq_margin(ev.duration, MCIR2_UNIT, MCIR2_UNIT / 2))
+		goto out;
+
+again:
+	IR_dprintk(2, "started at state %i (%uus %s)\n",
+		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+
+	if (!geq_margin(ev.duration, MCIR2_UNIT, MCIR2_UNIT / 2))
+		return 0;
+
+	switch (data->state) {
+
+	case STATE_INACTIVE:
+		if (!ev.pulse)
+			break;
+
+		/* Note: larger margin on first pulse since each MCIR2_UNIT
+		   is quite short and some hardware takes some time to
+		   adjust to the signal */
+		if (!eq_margin(ev.duration, MCIR2_PREFIX_PULSE, MCIR2_UNIT))
+			break;
+
+		data->state = STATE_HEADER_BIT_START;
+		data->count = 0;
+		data->header = 0;
+		return 0;
+
+	case STATE_HEADER_BIT_START:
+		if (geq_margin(ev.duration, MCIR2_MAX_LEN, MCIR2_UNIT / 2))
+			break;
+
+		data->header <<= 1;
+		if (ev.pulse)
+			data->header |= 1;
+		data->count++;
+		data->state = STATE_HEADER_BIT_END;
+		return 0;
+
+	case STATE_HEADER_BIT_END:
+		if (!is_transition(&ev, &dev->raw->prev_ev))
+			break;
+
+		decrease_duration(&ev, MCIR2_BIT_END);
+
+		if (data->count != MCIR2_HEADER_NBITS) {
+			data->state = STATE_HEADER_BIT_START;
+			goto again;
+		}
+
+		switch (mce_kbd_mode(data)) {
+		case MCIR2_MODE_KEYBOARD:
+			data->wanted_bits = MCIR2_KEYBOARD_NBITS;
+			break;
+		case MCIR2_MODE_MOUSE:
+			data->wanted_bits = MCIR2_MOUSE_NBITS;
+			break;
+		default:
+			IR_dprintk(1, "not keyboard or mouse data\n");
+			goto out;
+		}
+
+		data->count = 0;
+		data->body = 0;
+		data->state = STATE_BODY_BIT_START;
+		goto again;
+
+	case STATE_BODY_BIT_START:
+		if (geq_margin(ev.duration, MCIR2_MAX_LEN, MCIR2_UNIT / 2))
+			break;
+
+		data->body <<= 1;
+		if (ev.pulse)
+			data->body |= 1;
+		data->count++;
+		data->state = STATE_BODY_BIT_END;
+		return 0;
+
+	case STATE_BODY_BIT_END:
+		if (!is_transition(&ev, &dev->raw->prev_ev))
+			break;
+
+		if (data->count == data->wanted_bits)
+			data->state = STATE_FINISHED;
+		else
+			data->state = STATE_BODY_BIT_START;
+
+		decrease_duration(&ev, MCIR2_BIT_END);
+		goto again;
+
+	case STATE_FINISHED:
+		if (ev.pulse)
+			break;
+
+		switch (data->wanted_bits) {
+		case MCIR2_KEYBOARD_NBITS:
+			scancode = data->body & 0xffff;
+			IR_dprintk(1, "keyboard data 0x%08x\n", data->body);
+			if (dev->timeout)
+				delay = usecs_to_jiffies(dev->timeout / 1000);
+			else
+				delay = msecs_to_jiffies(100);
+			mod_timer(&data->rx_timeout, jiffies + delay);
+			/* Pass data to keyboard buffer parser */
+			ir_mce_kbd_process_keyboard_data(data->idev, scancode);
+			break;
+		case MCIR2_MOUSE_NBITS:
+			scancode = data->body & 0x1fffff;
+			IR_dprintk(1, "mouse data 0x%06x\n", scancode);
+			/* Pass data to mouse buffer parser */
+			ir_mce_kbd_process_mouse_data(data->idev, scancode);
+			break;
+		default:
+			IR_dprintk(1, "not keyboard or mouse data\n");
+			goto out;
+		}
+
+		data->state = STATE_INACTIVE;
+		input_sync(data->idev);
+		return 0;
+	}
+
+out:
+	IR_dprintk(1, "failed at state %i (%uus %s)\n",
+		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+	data->state = STATE_INACTIVE;
+	input_sync(data->idev);
+	return -EINVAL;
+}
+
+static int ir_mce_kbd_register(struct rc_dev *dev)
+{
+	struct mce_kbd_dec *mce_kbd = &dev->raw->mce_kbd;
+	struct input_dev *idev;
+	int i, ret;
+
+	idev = input_allocate_device();
+	if (!idev)
+		return -ENOMEM;
+
+	snprintf(mce_kbd->name, sizeof(mce_kbd->name),
+		 "MCE IR Keyboard/Mouse (%s)", dev->driver_name);
+	strlcat(mce_kbd->phys, "/input0", sizeof(mce_kbd->phys));
+
+	idev->name = mce_kbd->name;
+	idev->phys = mce_kbd->phys;
+
+	/* Keyboard bits */
+	set_bit(EV_KEY, idev->evbit);
+	set_bit(EV_REP, idev->evbit);
+	for (i = 0; i < sizeof(kbd_keycodes); i++)
+		set_bit(kbd_keycodes[i], idev->keybit);
+
+	/* Mouse bits */
+	set_bit(EV_REL, idev->evbit);
+	set_bit(REL_X, idev->relbit);
+	set_bit(REL_Y, idev->relbit);
+	set_bit(BTN_LEFT, idev->keybit);
+	set_bit(BTN_RIGHT, idev->keybit);
+
+	/* Report scancodes too */
+	set_bit(EV_MSC, idev->evbit);
+	set_bit(MSC_SCAN, idev->mscbit);
+
+	setup_timer(&mce_kbd->rx_timeout, mce_kbd_rx_timeout,
+		    (unsigned long)mce_kbd);
+
+	input_set_drvdata(idev, mce_kbd);
+
+#if 0
+	/* Adding this reference means two input devices are associated with
+	 * this rc-core device, which ir-keytable doesn't cope with yet */
+	idev->dev.parent = &dev->dev;
+#endif
+
+	ret = input_register_device(idev);
+	if (ret < 0) {
+		input_free_device(idev);
+		return -EIO;
+	}
+
+	mce_kbd->idev = idev;
+
+	return 0;
+}
+
+static int ir_mce_kbd_unregister(struct rc_dev *dev)
+{
+	struct mce_kbd_dec *mce_kbd = &dev->raw->mce_kbd;
+	struct input_dev *idev = mce_kbd->idev;
+
+	del_timer_sync(&mce_kbd->rx_timeout);
+	input_unregister_device(idev);
+
+	return 0;
+}
+
+static struct ir_raw_handler mce_kbd_handler = {
+	.protocols	= RC_BIT_MCE_KBD,
+	.decode		= ir_mce_kbd_decode,
+	.raw_register	= ir_mce_kbd_register,
+	.raw_unregister	= ir_mce_kbd_unregister,
+};
+
+static int __init ir_mce_kbd_decode_init(void)
+{
+	ir_raw_handler_register(&mce_kbd_handler);
+
+	printk(KERN_INFO "IR MCE Keyboard/mouse protocol handler initialized\n");
+	return 0;
+}
+
+static void __exit ir_mce_kbd_decode_exit(void)
+{
+	ir_raw_handler_unregister(&mce_kbd_handler);
+}
+
+module_init(ir_mce_kbd_decode_init);
+module_exit(ir_mce_kbd_decode_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
+MODULE_DESCRIPTION("MCE Keyboard/mouse IR protocol decoder");
diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c
new file mode 100644
index 0000000..7b81fec
--- /dev/null
+++ b/drivers/media/rc/ir-nec-decoder.c
@@ -0,0 +1,227 @@
+/* ir-nec-decoder.c - handle NEC IR Pulse/Space protocol
+ *
+ * Copyright (C) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation version 2 of the License.
+ *
+ *  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.
+ */
+
+#include <linux/bitrev.h>
+#include <linux/module.h>
+#include "rc-core-priv.h"
+
+#define NEC_NBITS		32
+#define NEC_UNIT		562500  /* ns */
+#define NEC_HEADER_PULSE	(16 * NEC_UNIT)
+#define NECX_HEADER_PULSE	(8  * NEC_UNIT) /* Less common NEC variant */
+#define NEC_HEADER_SPACE	(8  * NEC_UNIT)
+#define NEC_REPEAT_SPACE	(4  * NEC_UNIT)
+#define NEC_BIT_PULSE		(1  * NEC_UNIT)
+#define NEC_BIT_0_SPACE		(1  * NEC_UNIT)
+#define NEC_BIT_1_SPACE		(3  * NEC_UNIT)
+#define	NEC_TRAILER_PULSE	(1  * NEC_UNIT)
+#define	NEC_TRAILER_SPACE	(10 * NEC_UNIT) /* even longer in reality */
+#define NECX_REPEAT_BITS	1
+
+enum nec_state {
+	STATE_INACTIVE,
+	STATE_HEADER_SPACE,
+	STATE_BIT_PULSE,
+	STATE_BIT_SPACE,
+	STATE_TRAILER_PULSE,
+	STATE_TRAILER_SPACE,
+};
+
+/**
+ * ir_nec_decode() - Decode one NEC pulse or space
+ * @dev:	the struct rc_dev descriptor of the device
+ * @duration:	the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This function returns -EINVAL if the pulse violates the state machine
+ */
+static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev)
+{
+	struct nec_dec *data = &dev->raw->nec;
+	u32 scancode;
+	u8 address, not_address, command, not_command;
+	bool send_32bits = false;
+
+	if (!(dev->enabled_protocols & RC_BIT_NEC))
+		return 0;
+
+	if (!is_timing_event(ev)) {
+		if (ev.reset)
+			data->state = STATE_INACTIVE;
+		return 0;
+	}
+
+	IR_dprintk(2, "NEC decode started at state %d (%uus %s)\n",
+		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+
+	switch (data->state) {
+
+	case STATE_INACTIVE:
+		if (!ev.pulse)
+			break;
+
+		if (eq_margin(ev.duration, NEC_HEADER_PULSE, NEC_UNIT * 2)) {
+			data->is_nec_x = false;
+			data->necx_repeat = false;
+		} else if (eq_margin(ev.duration, NECX_HEADER_PULSE, NEC_UNIT / 2))
+			data->is_nec_x = true;
+		else
+			break;
+
+		data->count = 0;
+		data->state = STATE_HEADER_SPACE;
+		return 0;
+
+	case STATE_HEADER_SPACE:
+		if (ev.pulse)
+			break;
+
+		if (eq_margin(ev.duration, NEC_HEADER_SPACE, NEC_UNIT)) {
+			data->state = STATE_BIT_PULSE;
+			return 0;
+		} else if (eq_margin(ev.duration, NEC_REPEAT_SPACE, NEC_UNIT / 2)) {
+			if (!dev->keypressed) {
+				IR_dprintk(1, "Discarding last key repeat: event after key up\n");
+			} else {
+				rc_repeat(dev);
+				IR_dprintk(1, "Repeat last key\n");
+				data->state = STATE_TRAILER_PULSE;
+			}
+			return 0;
+		}
+
+		break;
+
+	case STATE_BIT_PULSE:
+		if (!ev.pulse)
+			break;
+
+		if (!eq_margin(ev.duration, NEC_BIT_PULSE, NEC_UNIT / 2))
+			break;
+
+		data->state = STATE_BIT_SPACE;
+		return 0;
+
+	case STATE_BIT_SPACE:
+		if (ev.pulse)
+			break;
+
+		if (data->necx_repeat && data->count == NECX_REPEAT_BITS &&
+			geq_margin(ev.duration,
+			NEC_TRAILER_SPACE, NEC_UNIT / 2)) {
+				IR_dprintk(1, "Repeat last key\n");
+				rc_repeat(dev);
+				data->state = STATE_INACTIVE;
+				return 0;
+
+		} else if (data->count > NECX_REPEAT_BITS)
+			data->necx_repeat = false;
+
+		data->bits <<= 1;
+		if (eq_margin(ev.duration, NEC_BIT_1_SPACE, NEC_UNIT / 2))
+			data->bits |= 1;
+		else if (!eq_margin(ev.duration, NEC_BIT_0_SPACE, NEC_UNIT / 2))
+			break;
+		data->count++;
+
+		if (data->count == NEC_NBITS)
+			data->state = STATE_TRAILER_PULSE;
+		else
+			data->state = STATE_BIT_PULSE;
+
+		return 0;
+
+	case STATE_TRAILER_PULSE:
+		if (!ev.pulse)
+			break;
+
+		if (!eq_margin(ev.duration, NEC_TRAILER_PULSE, NEC_UNIT / 2))
+			break;
+
+		data->state = STATE_TRAILER_SPACE;
+		return 0;
+
+	case STATE_TRAILER_SPACE:
+		if (ev.pulse)
+			break;
+
+		if (!geq_margin(ev.duration, NEC_TRAILER_SPACE, NEC_UNIT / 2))
+			break;
+
+		address     = bitrev8((data->bits >> 24) & 0xff);
+		not_address = bitrev8((data->bits >> 16) & 0xff);
+		command	    = bitrev8((data->bits >>  8) & 0xff);
+		not_command = bitrev8((data->bits >>  0) & 0xff);
+
+		if ((command ^ not_command) != 0xff) {
+			IR_dprintk(1, "NEC checksum error: received 0x%08x\n",
+				   data->bits);
+			send_32bits = true;
+		}
+
+		if (send_32bits) {
+			/* NEC transport, but modified protocol, used by at
+			 * least Apple and TiVo remotes */
+			scancode = data->bits;
+			IR_dprintk(1, "NEC (modified) scancode 0x%08x\n", scancode);
+		} else if ((address ^ not_address) != 0xff) {
+			/* Extended NEC */
+			scancode = address     << 16 |
+				   not_address <<  8 |
+				   command;
+			IR_dprintk(1, "NEC (Ext) scancode 0x%06x\n", scancode);
+		} else {
+			/* Normal NEC */
+			scancode = address << 8 | command;
+			IR_dprintk(1, "NEC scancode 0x%04x\n", scancode);
+		}
+
+		if (data->is_nec_x)
+			data->necx_repeat = true;
+
+		rc_keydown(dev, RC_TYPE_NEC, scancode, 0);
+		data->state = STATE_INACTIVE;
+		return 0;
+	}
+
+	IR_dprintk(1, "NEC decode failed at count %d state %d (%uus %s)\n",
+		   data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+	data->state = STATE_INACTIVE;
+	return -EINVAL;
+}
+
+static struct ir_raw_handler nec_handler = {
+	.protocols	= RC_BIT_NEC,
+	.decode		= ir_nec_decode,
+};
+
+static int __init ir_nec_decode_init(void)
+{
+	ir_raw_handler_register(&nec_handler);
+
+	printk(KERN_INFO "IR NEC protocol handler initialized\n");
+	return 0;
+}
+
+static void __exit ir_nec_decode_exit(void)
+{
+	ir_raw_handler_unregister(&nec_handler);
+}
+
+module_init(ir_nec_decode_init);
+module_exit(ir_nec_decode_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
+MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
+MODULE_DESCRIPTION("NEC IR protocol decoder");
diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c
new file mode 100644
index 0000000..67314c0
--- /dev/null
+++ b/drivers/media/rc/ir-rc5-decoder.c
@@ -0,0 +1,211 @@
+/* ir-rc5-decoder.c - decoder for RC5(x) and StreamZap protocols
+ *
+ * Copyright (C) 2010 by Mauro Carvalho Chehab
+ * Copyright (C) 2010 by Jarod Wilson <jarod@redhat.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 version 2 of the License.
+ *
+ *  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.
+ */
+
+/*
+ * This decoder handles the 14 bit RC5 protocol, 15 bit "StreamZap" protocol
+ * and 20 bit RC5x protocol.
+ */
+
+#include "rc-core-priv.h"
+#include <linux/module.h>
+
+#define RC5_NBITS		14
+#define RC5_SZ_NBITS		15
+#define RC5X_NBITS		20
+#define CHECK_RC5X_NBITS	8
+#define RC5_UNIT		888888 /* ns */
+#define RC5_BIT_START		(1 * RC5_UNIT)
+#define RC5_BIT_END		(1 * RC5_UNIT)
+#define RC5X_SPACE		(4 * RC5_UNIT)
+#define RC5_TRAILER		(6 * RC5_UNIT) /* In reality, approx 100 */
+
+enum rc5_state {
+	STATE_INACTIVE,
+	STATE_BIT_START,
+	STATE_BIT_END,
+	STATE_CHECK_RC5X,
+	STATE_FINISHED,
+};
+
+/**
+ * ir_rc5_decode() - Decode one RC-5 pulse or space
+ * @dev:	the struct rc_dev descriptor of the device
+ * @ev:		the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This function returns -EINVAL if the pulse violates the state machine
+ */
+static int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev)
+{
+	struct rc5_dec *data = &dev->raw->rc5;
+	u8 toggle;
+	u32 scancode;
+	enum rc_type protocol;
+
+	if (!(dev->enabled_protocols & (RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ)))
+		return 0;
+
+	if (!is_timing_event(ev)) {
+		if (ev.reset)
+			data->state = STATE_INACTIVE;
+		return 0;
+	}
+
+	if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2))
+		goto out;
+
+again:
+	IR_dprintk(2, "RC5(x/sz) decode started at state %i (%uus %s)\n",
+		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+
+	if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2))
+		return 0;
+
+	switch (data->state) {
+
+	case STATE_INACTIVE:
+		if (!ev.pulse)
+			break;
+
+		data->state = STATE_BIT_START;
+		data->count = 1;
+		decrease_duration(&ev, RC5_BIT_START);
+		goto again;
+
+	case STATE_BIT_START:
+		if (!ev.pulse && geq_margin(ev.duration, RC5_TRAILER, RC5_UNIT / 2)) {
+			data->state = STATE_FINISHED;
+			goto again;
+		}
+
+		if (!eq_margin(ev.duration, RC5_BIT_START, RC5_UNIT / 2))
+			break;
+
+		data->bits <<= 1;
+		if (!ev.pulse)
+			data->bits |= 1;
+		data->count++;
+		data->state = STATE_BIT_END;
+		return 0;
+
+	case STATE_BIT_END:
+		if (!is_transition(&ev, &dev->raw->prev_ev))
+			break;
+
+		if (data->count == CHECK_RC5X_NBITS)
+			data->state = STATE_CHECK_RC5X;
+		else
+			data->state = STATE_BIT_START;
+
+		decrease_duration(&ev, RC5_BIT_END);
+		goto again;
+
+	case STATE_CHECK_RC5X:
+		if (!ev.pulse && geq_margin(ev.duration, RC5X_SPACE, RC5_UNIT / 2)) {
+			data->is_rc5x = true;
+			decrease_duration(&ev, RC5X_SPACE);
+		} else
+			data->is_rc5x = false;
+		data->state = STATE_BIT_START;
+		goto again;
+
+	case STATE_FINISHED:
+		if (ev.pulse)
+			break;
+
+		if (data->is_rc5x && data->count == RC5X_NBITS) {
+			/* RC5X */
+			u8 xdata, command, system;
+			if (!(dev->enabled_protocols & RC_BIT_RC5X)) {
+				data->state = STATE_INACTIVE;
+				return 0;
+			}
+			xdata    = (data->bits & 0x0003F) >> 0;
+			command  = (data->bits & 0x00FC0) >> 6;
+			system   = (data->bits & 0x1F000) >> 12;
+			toggle   = (data->bits & 0x20000) ? 1 : 0;
+			command += (data->bits & 0x01000) ? 0 : 0x40;
+			scancode = system << 16 | command << 8 | xdata;
+			protocol = RC_TYPE_RC5X;
+
+		} else if (!data->is_rc5x && data->count == RC5_NBITS) {
+			/* RC5 */
+			u8 command, system;
+			if (!(dev->enabled_protocols & RC_BIT_RC5)) {
+				data->state = STATE_INACTIVE;
+				return 0;
+			}
+			command  = (data->bits & 0x0003F) >> 0;
+			system   = (data->bits & 0x007C0) >> 6;
+			toggle   = (data->bits & 0x00800) ? 1 : 0;
+			command += (data->bits & 0x01000) ? 0 : 0x40;
+			scancode = system << 8 | command;
+			protocol = RC_TYPE_RC5;
+
+		} else if (!data->is_rc5x && data->count == RC5_SZ_NBITS) {
+			/* RC5 StreamZap */
+			u8 command, system;
+			if (!(dev->enabled_protocols & RC_BIT_RC5_SZ)) {
+				data->state = STATE_INACTIVE;
+				return 0;
+			}
+			command  = (data->bits & 0x0003F) >> 0;
+			system   = (data->bits & 0x02FC0) >> 6;
+			toggle   = (data->bits & 0x01000) ? 1 : 0;
+			scancode = system << 6 | command;
+			protocol = RC_TYPE_RC5_SZ;
+
+		} else
+			break;
+
+		IR_dprintk(1, "RC5(x/sz) scancode 0x%06x (p: %u, t: %u)\n",
+			   scancode, protocol, toggle);
+
+		rc_keydown(dev, protocol, scancode, toggle);
+		data->state = STATE_INACTIVE;
+		return 0;
+	}
+
+out:
+	IR_dprintk(1, "RC5(x/sz) decode failed at state %i count %d (%uus %s)\n",
+		   data->state, data->count, TO_US(ev.duration), TO_STR(ev.pulse));
+	data->state = STATE_INACTIVE;
+	return -EINVAL;
+}
+
+static struct ir_raw_handler rc5_handler = {
+	.protocols	= RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ,
+	.decode		= ir_rc5_decode,
+};
+
+static int __init ir_rc5_decode_init(void)
+{
+	ir_raw_handler_register(&rc5_handler);
+
+	printk(KERN_INFO "IR RC5(x/sz) protocol handler initialized\n");
+	return 0;
+}
+
+static void __exit ir_rc5_decode_exit(void)
+{
+	ir_raw_handler_unregister(&rc5_handler);
+}
+
+module_init(ir_rc5_decode_init);
+module_exit(ir_rc5_decode_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab and Jarod Wilson");
+MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
+MODULE_DESCRIPTION("RC5(x/sz) IR protocol decoder");
diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c
new file mode 100644
index 0000000..d16bc67
--- /dev/null
+++ b/drivers/media/rc/ir-rc6-decoder.c
@@ -0,0 +1,319 @@
+/* ir-rc6-decoder.c - A decoder for the RC6 IR protocol
+ *
+ * Copyright (C) 2010 by David Härdeman <david@hardeman.nu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * 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.
+ */
+
+#include "rc-core-priv.h"
+#include <linux/module.h>
+
+/*
+ * This decoder currently supports:
+ * RC6-0-16	(standard toggle bit in header)
+ * RC6-6A-20	(no toggle bit)
+ * RC6-6A-24	(no toggle bit)
+ * RC6-6A-32	(MCE version with toggle bit in body)
+ */
+
+#define RC6_UNIT		444444	/* nanosecs */
+#define RC6_HEADER_NBITS	4	/* not including toggle bit */
+#define RC6_0_NBITS		16
+#define RC6_6A_32_NBITS		32
+#define RC6_6A_NBITS		128	/* Variable 8..128 */
+#define RC6_PREFIX_PULSE	(6 * RC6_UNIT)
+#define RC6_PREFIX_SPACE	(2 * RC6_UNIT)
+#define RC6_BIT_START		(1 * RC6_UNIT)
+#define RC6_BIT_END		(1 * RC6_UNIT)
+#define RC6_TOGGLE_START	(2 * RC6_UNIT)
+#define RC6_TOGGLE_END		(2 * RC6_UNIT)
+#define RC6_SUFFIX_SPACE	(6 * RC6_UNIT)
+#define RC6_MODE_MASK		0x07	/* for the header bits */
+#define RC6_STARTBIT_MASK	0x08	/* for the header bits */
+#define RC6_6A_MCE_TOGGLE_MASK	0x8000	/* for the body bits */
+#define RC6_6A_LCC_MASK		0xffff0000 /* RC6-6A-32 long customer code mask */
+#define RC6_6A_MCE_CC		0x800f0000 /* MCE customer code */
+#ifndef CHAR_BIT
+#define CHAR_BIT 8	/* Normally in <limits.h> */
+#endif
+
+enum rc6_mode {
+	RC6_MODE_0,
+	RC6_MODE_6A,
+	RC6_MODE_UNKNOWN,
+};
+
+enum rc6_state {
+	STATE_INACTIVE,
+	STATE_PREFIX_SPACE,
+	STATE_HEADER_BIT_START,
+	STATE_HEADER_BIT_END,
+	STATE_TOGGLE_START,
+	STATE_TOGGLE_END,
+	STATE_BODY_BIT_START,
+	STATE_BODY_BIT_END,
+	STATE_FINISHED,
+};
+
+static enum rc6_mode rc6_mode(struct rc6_dec *data)
+{
+	switch (data->header & RC6_MODE_MASK) {
+	case 0:
+		return RC6_MODE_0;
+	case 6:
+		if (!data->toggle)
+			return RC6_MODE_6A;
+		/* fall through */
+	default:
+		return RC6_MODE_UNKNOWN;
+	}
+}
+
+/**
+ * ir_rc6_decode() - Decode one RC6 pulse or space
+ * @dev:	the struct rc_dev descriptor of the device
+ * @ev:		the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This function returns -EINVAL if the pulse violates the state machine
+ */
+static int ir_rc6_decode(struct rc_dev *dev, struct ir_raw_event ev)
+{
+	struct rc6_dec *data = &dev->raw->rc6;
+	u32 scancode;
+	u8 toggle;
+	enum rc_type protocol;
+
+	if (!(dev->enabled_protocols &
+	      (RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 |
+	       RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE)))
+		return 0;
+
+	if (!is_timing_event(ev)) {
+		if (ev.reset)
+			data->state = STATE_INACTIVE;
+		return 0;
+	}
+
+	if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2))
+		goto out;
+
+again:
+	IR_dprintk(2, "RC6 decode started at state %i (%uus %s)\n",
+		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+
+	if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2))
+		return 0;
+
+	switch (data->state) {
+
+	case STATE_INACTIVE:
+		if (!ev.pulse)
+			break;
+
+		/* Note: larger margin on first pulse since each RC6_UNIT
+		   is quite short and some hardware takes some time to
+		   adjust to the signal */
+		if (!eq_margin(ev.duration, RC6_PREFIX_PULSE, RC6_UNIT))
+			break;
+
+		data->state = STATE_PREFIX_SPACE;
+		data->count = 0;
+		return 0;
+
+	case STATE_PREFIX_SPACE:
+		if (ev.pulse)
+			break;
+
+		if (!eq_margin(ev.duration, RC6_PREFIX_SPACE, RC6_UNIT / 2))
+			break;
+
+		data->state = STATE_HEADER_BIT_START;
+		data->header = 0;
+		return 0;
+
+	case STATE_HEADER_BIT_START:
+		if (!eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2))
+			break;
+
+		data->header <<= 1;
+		if (ev.pulse)
+			data->header |= 1;
+		data->count++;
+		data->state = STATE_HEADER_BIT_END;
+		return 0;
+
+	case STATE_HEADER_BIT_END:
+		if (!is_transition(&ev, &dev->raw->prev_ev))
+			break;
+
+		if (data->count == RC6_HEADER_NBITS)
+			data->state = STATE_TOGGLE_START;
+		else
+			data->state = STATE_HEADER_BIT_START;
+
+		decrease_duration(&ev, RC6_BIT_END);
+		goto again;
+
+	case STATE_TOGGLE_START:
+		if (!eq_margin(ev.duration, RC6_TOGGLE_START, RC6_UNIT / 2))
+			break;
+
+		data->toggle = ev.pulse;
+		data->state = STATE_TOGGLE_END;
+		return 0;
+
+	case STATE_TOGGLE_END:
+		if (!is_transition(&ev, &dev->raw->prev_ev) ||
+		    !geq_margin(ev.duration, RC6_TOGGLE_END, RC6_UNIT / 2))
+			break;
+
+		if (!(data->header & RC6_STARTBIT_MASK)) {
+			IR_dprintk(1, "RC6 invalid start bit\n");
+			break;
+		}
+
+		data->state = STATE_BODY_BIT_START;
+		decrease_duration(&ev, RC6_TOGGLE_END);
+		data->count = 0;
+		data->body = 0;
+
+		switch (rc6_mode(data)) {
+		case RC6_MODE_0:
+			data->wanted_bits = RC6_0_NBITS;
+			break;
+		case RC6_MODE_6A:
+			data->wanted_bits = RC6_6A_NBITS;
+			break;
+		default:
+			IR_dprintk(1, "RC6 unknown mode\n");
+			goto out;
+		}
+		goto again;
+
+	case STATE_BODY_BIT_START:
+		if (eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2)) {
+			/* Discard LSB's that won't fit in data->body */
+			if (data->count++ < CHAR_BIT * sizeof data->body) {
+				data->body <<= 1;
+				if (ev.pulse)
+					data->body |= 1;
+			}
+			data->state = STATE_BODY_BIT_END;
+			return 0;
+		} else if (RC6_MODE_6A == rc6_mode(data) && !ev.pulse &&
+				geq_margin(ev.duration, RC6_SUFFIX_SPACE, RC6_UNIT / 2)) {
+			data->state = STATE_FINISHED;
+			goto again;
+		}
+		break;
+
+	case STATE_BODY_BIT_END:
+		if (!is_transition(&ev, &dev->raw->prev_ev))
+			break;
+
+		if (data->count == data->wanted_bits)
+			data->state = STATE_FINISHED;
+		else
+			data->state = STATE_BODY_BIT_START;
+
+		decrease_duration(&ev, RC6_BIT_END);
+		goto again;
+
+	case STATE_FINISHED:
+		if (ev.pulse)
+			break;
+
+		switch (rc6_mode(data)) {
+		case RC6_MODE_0:
+			scancode = data->body;
+			toggle = data->toggle;
+			protocol = RC_TYPE_RC6_0;
+			IR_dprintk(1, "RC6(0) scancode 0x%04x (toggle: %u)\n",
+				   scancode, toggle);
+			break;
+
+		case RC6_MODE_6A:
+			if (data->count > CHAR_BIT * sizeof data->body) {
+				IR_dprintk(1, "RC6 too many (%u) data bits\n",
+					data->count);
+				goto out;
+			}
+
+			scancode = data->body;
+			switch (data->count) {
+			case 20:
+				protocol = RC_TYPE_RC6_6A_20;
+				toggle = 0;
+				break;
+			case 24:
+				protocol = RC_BIT_RC6_6A_24;
+				toggle = 0;
+				break;
+			case 32:
+				if ((scancode & RC6_6A_LCC_MASK) == RC6_6A_MCE_CC) {
+					protocol = RC_TYPE_RC6_MCE;
+					toggle = !!(scancode & RC6_6A_MCE_TOGGLE_MASK);
+					scancode &= ~RC6_6A_MCE_TOGGLE_MASK;
+				} else {
+					protocol = RC_BIT_RC6_6A_32;
+					toggle = 0;
+				}
+				break;
+			default:
+				IR_dprintk(1, "RC6(6A) unsupported length\n");
+				goto out;
+			}
+
+			IR_dprintk(1, "RC6(6A) proto 0x%04x, scancode 0x%08x (toggle: %u)\n",
+				   protocol, scancode, toggle);
+			break;
+		default:
+			IR_dprintk(1, "RC6 unknown mode\n");
+			goto out;
+		}
+
+		rc_keydown(dev, protocol, scancode, toggle);
+		data->state = STATE_INACTIVE;
+		return 0;
+	}
+
+out:
+	IR_dprintk(1, "RC6 decode failed at state %i (%uus %s)\n",
+		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+	data->state = STATE_INACTIVE;
+	return -EINVAL;
+}
+
+static struct ir_raw_handler rc6_handler = {
+	.protocols	= RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 |
+			  RC_BIT_RC6_6A_24 | RC_BIT_RC6_6A_32 |
+			  RC_BIT_RC6_MCE,
+	.decode		= ir_rc6_decode,
+};
+
+static int __init ir_rc6_decode_init(void)
+{
+	ir_raw_handler_register(&rc6_handler);
+
+	printk(KERN_INFO "IR RC6 protocol handler initialized\n");
+	return 0;
+}
+
+static void __exit ir_rc6_decode_exit(void)
+{
+	ir_raw_handler_unregister(&rc6_handler);
+}
+
+module_init(ir_rc6_decode_init);
+module_exit(ir_rc6_decode_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Härdeman <david@hardeman.nu>");
+MODULE_DESCRIPTION("RC6 IR protocol decoder");
diff --git a/drivers/media/rc/ir-rx51.c b/drivers/media/rc/ir-rx51.c
new file mode 100644
index 0000000..b1e19a2
--- /dev/null
+++ b/drivers/media/rc/ir-rx51.c
@@ -0,0 +1,485 @@
+/*
+ *  Copyright (C) 2008 Nokia Corporation
+ *
+ *  Based on lirc_serial.c
+ *
+ *  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/module.h>
+#include <linux/interrupt.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+
+#include <plat/dmtimer.h>
+#include <plat/clock.h>
+
+#include <media/lirc.h>
+#include <media/lirc_dev.h>
+#include <media/ir-rx51.h>
+
+#define LIRC_RX51_DRIVER_FEATURES (LIRC_CAN_SET_SEND_DUTY_CYCLE |	\
+				   LIRC_CAN_SET_SEND_CARRIER |		\
+				   LIRC_CAN_SEND_PULSE)
+
+#define DRIVER_NAME "lirc_rx51"
+
+#define WBUF_LEN 256
+
+#define TIMER_MAX_VALUE 0xffffffff
+
+struct lirc_rx51 {
+	struct omap_dm_timer *pwm_timer;
+	struct omap_dm_timer *pulse_timer;
+	struct device	     *dev;
+	struct lirc_rx51_platform_data *pdata;
+	wait_queue_head_t     wqueue;
+
+	unsigned long	fclk_khz;
+	unsigned int	freq;		/* carrier frequency */
+	unsigned int	duty_cycle;	/* carrier duty cycle */
+	unsigned int	irq_num;
+	unsigned int	match;
+	int		wbuf[WBUF_LEN];
+	int		wbuf_index;
+	unsigned long	device_is_open;
+	int		pwm_timer_num;
+};
+
+static void lirc_rx51_on(struct lirc_rx51 *lirc_rx51)
+{
+	omap_dm_timer_set_pwm(lirc_rx51->pwm_timer, 0, 1,
+			      OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
+}
+
+static void lirc_rx51_off(struct lirc_rx51 *lirc_rx51)
+{
+	omap_dm_timer_set_pwm(lirc_rx51->pwm_timer, 0, 1,
+			      OMAP_TIMER_TRIGGER_NONE);
+}
+
+static int init_timing_params(struct lirc_rx51 *lirc_rx51)
+{
+	u32 load, match;
+
+	load = -(lirc_rx51->fclk_khz * 1000 / lirc_rx51->freq);
+	match = -(lirc_rx51->duty_cycle * -load / 100);
+	omap_dm_timer_set_load(lirc_rx51->pwm_timer, 1, load);
+	omap_dm_timer_set_match(lirc_rx51->pwm_timer, 1, match);
+	omap_dm_timer_write_counter(lirc_rx51->pwm_timer, TIMER_MAX_VALUE - 2);
+	omap_dm_timer_start(lirc_rx51->pwm_timer);
+	omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, 0);
+	omap_dm_timer_start(lirc_rx51->pulse_timer);
+
+	lirc_rx51->match = 0;
+
+	return 0;
+}
+
+#define tics_after(a, b) ((long)(b) - (long)(a) < 0)
+
+static int pulse_timer_set_timeout(struct lirc_rx51 *lirc_rx51, int usec)
+{
+	int counter;
+
+	BUG_ON(usec < 0);
+
+	if (lirc_rx51->match == 0)
+		counter = omap_dm_timer_read_counter(lirc_rx51->pulse_timer);
+	else
+		counter = lirc_rx51->match;
+
+	counter += (u32)(lirc_rx51->fclk_khz * usec / (1000));
+	omap_dm_timer_set_match(lirc_rx51->pulse_timer, 1, counter);
+	omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer,
+				     OMAP_TIMER_INT_MATCH);
+	if (tics_after(omap_dm_timer_read_counter(lirc_rx51->pulse_timer),
+		       counter)) {
+		return 1;
+	}
+	return 0;
+}
+
+static irqreturn_t lirc_rx51_interrupt_handler(int irq, void *ptr)
+{
+	unsigned int retval;
+	struct lirc_rx51 *lirc_rx51 = ptr;
+
+	retval = omap_dm_timer_read_status(lirc_rx51->pulse_timer);
+	if (!retval)
+		return IRQ_NONE;
+
+	if (retval & ~OMAP_TIMER_INT_MATCH)
+		dev_err_ratelimited(lirc_rx51->dev,
+				": Unexpected interrupt source: %x\n", retval);
+
+	omap_dm_timer_write_status(lirc_rx51->pulse_timer,
+				OMAP_TIMER_INT_MATCH	|
+				OMAP_TIMER_INT_OVERFLOW	|
+				OMAP_TIMER_INT_CAPTURE);
+	if (lirc_rx51->wbuf_index < 0) {
+		dev_err_ratelimited(lirc_rx51->dev,
+				": BUG wbuf_index has value of %i\n",
+				lirc_rx51->wbuf_index);
+		goto end;
+	}
+
+	/*
+	 * If we happen to hit an odd latency spike, loop through the
+	 * pulses until we catch up.
+	 */
+	do {
+		if (lirc_rx51->wbuf_index >= WBUF_LEN)
+			goto end;
+		if (lirc_rx51->wbuf[lirc_rx51->wbuf_index] == -1)
+			goto end;
+
+		if (lirc_rx51->wbuf_index % 2)
+			lirc_rx51_off(lirc_rx51);
+		else
+			lirc_rx51_on(lirc_rx51);
+
+		retval = pulse_timer_set_timeout(lirc_rx51,
+					lirc_rx51->wbuf[lirc_rx51->wbuf_index]);
+		lirc_rx51->wbuf_index++;
+
+	} while (retval);
+
+	return IRQ_HANDLED;
+end:
+	/* Stop TX here */
+	lirc_rx51_off(lirc_rx51);
+	lirc_rx51->wbuf_index = -1;
+	omap_dm_timer_stop(lirc_rx51->pwm_timer);
+	omap_dm_timer_stop(lirc_rx51->pulse_timer);
+	omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, 0);
+	wake_up_interruptible(&lirc_rx51->wqueue);
+
+	return IRQ_HANDLED;
+}
+
+static int lirc_rx51_init_port(struct lirc_rx51 *lirc_rx51)
+{
+	struct clk *clk_fclk;
+	int retval, pwm_timer = lirc_rx51->pwm_timer_num;
+
+	lirc_rx51->pwm_timer = omap_dm_timer_request_specific(pwm_timer);
+	if (lirc_rx51->pwm_timer == NULL) {
+		dev_err(lirc_rx51->dev, ": Error requesting GPT%d timer\n",
+			pwm_timer);
+		return -EBUSY;
+	}
+
+	lirc_rx51->pulse_timer = omap_dm_timer_request();
+	if (lirc_rx51->pulse_timer == NULL) {
+		dev_err(lirc_rx51->dev, ": Error requesting pulse timer\n");
+		retval = -EBUSY;
+		goto err1;
+	}
+
+	omap_dm_timer_set_source(lirc_rx51->pwm_timer, OMAP_TIMER_SRC_SYS_CLK);
+	omap_dm_timer_set_source(lirc_rx51->pulse_timer,
+				OMAP_TIMER_SRC_SYS_CLK);
+
+	omap_dm_timer_enable(lirc_rx51->pwm_timer);
+	omap_dm_timer_enable(lirc_rx51->pulse_timer);
+
+	lirc_rx51->irq_num = omap_dm_timer_get_irq(lirc_rx51->pulse_timer);
+	retval = request_irq(lirc_rx51->irq_num, lirc_rx51_interrupt_handler,
+			     IRQF_SHARED, "lirc_pulse_timer", lirc_rx51);
+	if (retval) {
+		dev_err(lirc_rx51->dev, ": Failed to request interrupt line\n");
+		goto err2;
+	}
+
+	clk_fclk = omap_dm_timer_get_fclk(lirc_rx51->pwm_timer);
+	lirc_rx51->fclk_khz = clk_fclk->rate / 1000;
+
+	return 0;
+
+err2:
+	omap_dm_timer_free(lirc_rx51->pulse_timer);
+err1:
+	omap_dm_timer_free(lirc_rx51->pwm_timer);
+
+	return retval;
+}
+
+static int lirc_rx51_free_port(struct lirc_rx51 *lirc_rx51)
+{
+	omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, 0);
+	free_irq(lirc_rx51->irq_num, lirc_rx51);
+	lirc_rx51_off(lirc_rx51);
+	omap_dm_timer_disable(lirc_rx51->pwm_timer);
+	omap_dm_timer_disable(lirc_rx51->pulse_timer);
+	omap_dm_timer_free(lirc_rx51->pwm_timer);
+	omap_dm_timer_free(lirc_rx51->pulse_timer);
+	lirc_rx51->wbuf_index = -1;
+
+	return 0;
+}
+
+static ssize_t lirc_rx51_write(struct file *file, const char *buf,
+			  size_t n, loff_t *ppos)
+{
+	int count, i;
+	struct lirc_rx51 *lirc_rx51 = file->private_data;
+
+	if (n % sizeof(int))
+		return -EINVAL;
+
+	count = n / sizeof(int);
+	if ((count > WBUF_LEN) || (count % 2 == 0))
+		return -EINVAL;
+
+	/* Wait any pending transfers to finish */
+	wait_event_interruptible(lirc_rx51->wqueue, lirc_rx51->wbuf_index < 0);
+
+	if (copy_from_user(lirc_rx51->wbuf, buf, n))
+		return -EFAULT;
+
+	/* Sanity check the input pulses */
+	for (i = 0; i < count; i++)
+		if (lirc_rx51->wbuf[i] < 0)
+			return -EINVAL;
+
+	init_timing_params(lirc_rx51);
+	if (count < WBUF_LEN)
+		lirc_rx51->wbuf[count] = -1; /* Insert termination mark */
+
+	/*
+	 * Adjust latency requirements so the device doesn't go in too
+	 * deep sleep states
+	 */
+	lirc_rx51->pdata->set_max_mpu_wakeup_lat(lirc_rx51->dev, 50);
+
+	lirc_rx51_on(lirc_rx51);
+	lirc_rx51->wbuf_index = 1;
+	pulse_timer_set_timeout(lirc_rx51, lirc_rx51->wbuf[0]);
+
+	/*
+	 * Don't return back to the userspace until the transfer has
+	 * finished
+	 */
+	wait_event_interruptible(lirc_rx51->wqueue, lirc_rx51->wbuf_index < 0);
+
+	/* We can sleep again */
+	lirc_rx51->pdata->set_max_mpu_wakeup_lat(lirc_rx51->dev, -1);
+
+	return n;
+}
+
+static long lirc_rx51_ioctl(struct file *filep,
+			unsigned int cmd, unsigned long arg)
+{
+	int result;
+	unsigned long value;
+	unsigned int ivalue;
+	struct lirc_rx51 *lirc_rx51 = filep->private_data;
+
+	switch (cmd) {
+	case LIRC_GET_SEND_MODE:
+		result = put_user(LIRC_MODE_PULSE, (unsigned long *)arg);
+		if (result)
+			return result;
+		break;
+
+	case LIRC_SET_SEND_MODE:
+		result = get_user(value, (unsigned long *)arg);
+		if (result)
+			return result;
+
+		/* only LIRC_MODE_PULSE supported */
+		if (value != LIRC_MODE_PULSE)
+			return -ENOSYS;
+		break;
+
+	case LIRC_GET_REC_MODE:
+		result = put_user(0, (unsigned long *) arg);
+		if (result)
+			return result;
+		break;
+
+	case LIRC_GET_LENGTH:
+		return -ENOSYS;
+		break;
+
+	case LIRC_SET_SEND_DUTY_CYCLE:
+		result = get_user(ivalue, (unsigned int *) arg);
+		if (result)
+			return result;
+
+		if (ivalue <= 0 || ivalue > 100) {
+			dev_err(lirc_rx51->dev, ": invalid duty cycle %d\n",
+				ivalue);
+			return -EINVAL;
+		}
+
+		lirc_rx51->duty_cycle = ivalue;
+		break;
+
+	case LIRC_SET_SEND_CARRIER:
+		result = get_user(ivalue, (unsigned int *) arg);
+		if (result)
+			return result;
+
+		if (ivalue > 500000 || ivalue < 20000) {
+			dev_err(lirc_rx51->dev, ": invalid carrier freq %d\n",
+				ivalue);
+			return -EINVAL;
+		}
+
+		lirc_rx51->freq = ivalue;
+		break;
+
+	case LIRC_GET_FEATURES:
+		result = put_user(LIRC_RX51_DRIVER_FEATURES,
+				  (unsigned long *) arg);
+		if (result)
+			return result;
+		break;
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+
+	return 0;
+}
+
+static int lirc_rx51_open(struct inode *inode, struct file *file)
+{
+	struct lirc_rx51 *lirc_rx51 = lirc_get_pdata(file);
+	BUG_ON(!lirc_rx51);
+
+	file->private_data = lirc_rx51;
+
+	if (test_and_set_bit(1, &lirc_rx51->device_is_open))
+		return -EBUSY;
+
+	return lirc_rx51_init_port(lirc_rx51);
+}
+
+static int lirc_rx51_release(struct inode *inode, struct file *file)
+{
+	struct lirc_rx51 *lirc_rx51 = file->private_data;
+
+	lirc_rx51_free_port(lirc_rx51);
+
+	clear_bit(1, &lirc_rx51->device_is_open);
+
+	return 0;
+}
+
+static struct lirc_rx51 lirc_rx51 = {
+	.freq		= 38000,
+	.duty_cycle	= 50,
+	.wbuf_index	= -1,
+};
+
+static const struct file_operations lirc_fops = {
+	.owner		= THIS_MODULE,
+	.write		= lirc_rx51_write,
+	.unlocked_ioctl	= lirc_rx51_ioctl,
+	.read		= lirc_dev_fop_read,
+	.poll		= lirc_dev_fop_poll,
+	.open		= lirc_rx51_open,
+	.release	= lirc_rx51_release,
+};
+
+static struct lirc_driver lirc_rx51_driver = {
+	.name		= DRIVER_NAME,
+	.minor		= -1,
+	.code_length	= 1,
+	.data		= &lirc_rx51,
+	.fops		= &lirc_fops,
+	.owner		= THIS_MODULE,
+};
+
+#ifdef CONFIG_PM
+
+static int lirc_rx51_suspend(struct platform_device *dev, pm_message_t state)
+{
+	/*
+	 * In case the device is still open, do not suspend. Normally
+	 * this should not be a problem as lircd only keeps the device
+	 * open only for short periods of time. We also don't want to
+	 * get involved with race conditions that might happen if we
+	 * were in a middle of a transmit. Thus, we defer any suspend
+	 * actions until transmit has completed.
+	 */
+	if (test_and_set_bit(1, &lirc_rx51.device_is_open))
+		return -EAGAIN;
+
+	clear_bit(1, &lirc_rx51.device_is_open);
+
+	return 0;
+}
+
+static int lirc_rx51_resume(struct platform_device *dev)
+{
+	return 0;
+}
+
+#else
+
+#define lirc_rx51_suspend	NULL
+#define lirc_rx51_resume	NULL
+
+#endif /* CONFIG_PM */
+
+static int lirc_rx51_probe(struct platform_device *dev)
+{
+	lirc_rx51_driver.features = LIRC_RX51_DRIVER_FEATURES;
+	lirc_rx51.pdata = dev->dev.platform_data;
+	lirc_rx51.pwm_timer_num = lirc_rx51.pdata->pwm_timer;
+	lirc_rx51.dev = &dev->dev;
+	lirc_rx51_driver.dev = &dev->dev;
+	lirc_rx51_driver.minor = lirc_register_driver(&lirc_rx51_driver);
+	init_waitqueue_head(&lirc_rx51.wqueue);
+
+	if (lirc_rx51_driver.minor < 0) {
+		dev_err(lirc_rx51.dev, ": lirc_register_driver failed: %d\n",
+		       lirc_rx51_driver.minor);
+		return lirc_rx51_driver.minor;
+	}
+	dev_info(lirc_rx51.dev, "registration ok, minor: %d, pwm: %d\n",
+		 lirc_rx51_driver.minor, lirc_rx51.pwm_timer_num);
+
+	return 0;
+}
+
+static int lirc_rx51_remove(struct platform_device *dev)
+{
+	return lirc_unregister_driver(lirc_rx51_driver.minor);
+}
+
+struct platform_driver lirc_rx51_platform_driver = {
+	.probe		= lirc_rx51_probe,
+	.remove		= lirc_rx51_remove,
+	.suspend	= lirc_rx51_suspend,
+	.resume		= lirc_rx51_resume,
+	.driver		= {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+module_platform_driver(lirc_rx51_platform_driver);
+
+MODULE_DESCRIPTION("LIRC TX driver for Nokia RX51");
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/ir-sanyo-decoder.c b/drivers/media/rc/ir-sanyo-decoder.c
new file mode 100644
index 0000000..ad1dc6a
--- /dev/null
+++ b/drivers/media/rc/ir-sanyo-decoder.c
@@ -0,0 +1,205 @@
+/* ir-sanyo-decoder.c - handle SANYO IR Pulse/Space protocol
+ *
+ * Copyright (C) 2011 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation version 2 of the License.
+ *
+ *  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.
+ *
+ * This protocol uses the NEC protocol timings. However, data is formatted as:
+ *	13 bits Custom Code
+ *	13 bits NOT(Custom Code)
+ *	8 bits Key data
+ *	8 bits NOT(Key data)
+ *
+ * According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon
+ * Information for this protocol is available at the Sanyo LC7461 datasheet.
+ */
+
+#include <linux/module.h>
+#include <linux/bitrev.h>
+#include "rc-core-priv.h"
+
+#define SANYO_NBITS		(13+13+8+8)
+#define SANYO_UNIT		562500  /* ns */
+#define SANYO_HEADER_PULSE	(16  * SANYO_UNIT)
+#define SANYO_HEADER_SPACE	(8   * SANYO_UNIT)
+#define SANYO_BIT_PULSE		(1   * SANYO_UNIT)
+#define SANYO_BIT_0_SPACE	(1   * SANYO_UNIT)
+#define SANYO_BIT_1_SPACE	(3   * SANYO_UNIT)
+#define SANYO_REPEAT_SPACE	(150 * SANYO_UNIT)
+#define	SANYO_TRAILER_PULSE	(1   * SANYO_UNIT)
+#define	SANYO_TRAILER_SPACE	(10  * SANYO_UNIT)	/* in fact, 42 */
+
+enum sanyo_state {
+	STATE_INACTIVE,
+	STATE_HEADER_SPACE,
+	STATE_BIT_PULSE,
+	STATE_BIT_SPACE,
+	STATE_TRAILER_PULSE,
+	STATE_TRAILER_SPACE,
+};
+
+/**
+ * ir_sanyo_decode() - Decode one SANYO pulse or space
+ * @dev:	the struct rc_dev descriptor of the device
+ * @duration:	the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This function returns -EINVAL if the pulse violates the state machine
+ */
+static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev)
+{
+	struct sanyo_dec *data = &dev->raw->sanyo;
+	u32 scancode;
+	u8 address, command, not_command;
+
+	if (!(dev->enabled_protocols & RC_BIT_SANYO))
+		return 0;
+
+	if (!is_timing_event(ev)) {
+		if (ev.reset) {
+			IR_dprintk(1, "SANYO event reset received. reset to state 0\n");
+			data->state = STATE_INACTIVE;
+		}
+		return 0;
+	}
+
+	IR_dprintk(2, "SANYO decode started at state %d (%uus %s)\n",
+		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+
+	switch (data->state) {
+
+	case STATE_INACTIVE:
+		if (!ev.pulse)
+			break;
+
+		if (eq_margin(ev.duration, SANYO_HEADER_PULSE, SANYO_UNIT / 2)) {
+			data->count = 0;
+			data->state = STATE_HEADER_SPACE;
+			return 0;
+		}
+		break;
+
+
+	case STATE_HEADER_SPACE:
+		if (ev.pulse)
+			break;
+
+		if (eq_margin(ev.duration, SANYO_HEADER_SPACE, SANYO_UNIT / 2)) {
+			data->state = STATE_BIT_PULSE;
+			return 0;
+		}
+
+		break;
+
+	case STATE_BIT_PULSE:
+		if (!ev.pulse)
+			break;
+
+		if (!eq_margin(ev.duration, SANYO_BIT_PULSE, SANYO_UNIT / 2))
+			break;
+
+		data->state = STATE_BIT_SPACE;
+		return 0;
+
+	case STATE_BIT_SPACE:
+		if (ev.pulse)
+			break;
+
+		if (!data->count && geq_margin(ev.duration, SANYO_REPEAT_SPACE, SANYO_UNIT / 2)) {
+			if (!dev->keypressed) {
+				IR_dprintk(1, "SANYO discarding last key repeat: event after key up\n");
+			} else {
+				rc_repeat(dev);
+				IR_dprintk(1, "SANYO repeat last key\n");
+				data->state = STATE_INACTIVE;
+			}
+			return 0;
+		}
+
+		data->bits <<= 1;
+		if (eq_margin(ev.duration, SANYO_BIT_1_SPACE, SANYO_UNIT / 2))
+			data->bits |= 1;
+		else if (!eq_margin(ev.duration, SANYO_BIT_0_SPACE, SANYO_UNIT / 2))
+			break;
+		data->count++;
+
+		if (data->count == SANYO_NBITS)
+			data->state = STATE_TRAILER_PULSE;
+		else
+			data->state = STATE_BIT_PULSE;
+
+		return 0;
+
+	case STATE_TRAILER_PULSE:
+		if (!ev.pulse)
+			break;
+
+		if (!eq_margin(ev.duration, SANYO_TRAILER_PULSE, SANYO_UNIT / 2))
+			break;
+
+		data->state = STATE_TRAILER_SPACE;
+		return 0;
+
+	case STATE_TRAILER_SPACE:
+		if (ev.pulse)
+			break;
+
+		if (!geq_margin(ev.duration, SANYO_TRAILER_SPACE, SANYO_UNIT / 2))
+			break;
+
+		address     = bitrev16((data->bits >> 29) & 0x1fff) >> 3;
+		/* not_address = bitrev16((data->bits >> 16) & 0x1fff) >> 3; */
+		command	    = bitrev8((data->bits >>  8) & 0xff);
+		not_command = bitrev8((data->bits >>  0) & 0xff);
+
+		if ((command ^ not_command) != 0xff) {
+			IR_dprintk(1, "SANYO checksum error: received 0x%08Lx\n",
+				   data->bits);
+			data->state = STATE_INACTIVE;
+			return 0;
+		}
+
+		scancode = address << 8 | command;
+		IR_dprintk(1, "SANYO scancode: 0x%06x\n", scancode);
+		rc_keydown(dev, RC_TYPE_SANYO, scancode, 0);
+		data->state = STATE_INACTIVE;
+		return 0;
+	}
+
+	IR_dprintk(1, "SANYO decode failed at count %d state %d (%uus %s)\n",
+		   data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+	data->state = STATE_INACTIVE;
+	return -EINVAL;
+}
+
+static struct ir_raw_handler sanyo_handler = {
+	.protocols	= RC_BIT_SANYO,
+	.decode		= ir_sanyo_decode,
+};
+
+static int __init ir_sanyo_decode_init(void)
+{
+	ir_raw_handler_register(&sanyo_handler);
+
+	printk(KERN_INFO "IR SANYO protocol handler initialized\n");
+	return 0;
+}
+
+static void __exit ir_sanyo_decode_exit(void)
+{
+	ir_raw_handler_unregister(&sanyo_handler);
+}
+
+module_init(ir_sanyo_decode_init);
+module_exit(ir_sanyo_decode_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
+MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
+MODULE_DESCRIPTION("SANYO IR protocol decoder");
diff --git a/drivers/media/rc/ir-sharp-decoder.c b/drivers/media/rc/ir-sharp-decoder.c
new file mode 100644
index 0000000..b7acdba
--- /dev/null
+++ b/drivers/media/rc/ir-sharp-decoder.c
@@ -0,0 +1,200 @@
+/* ir-sharp-decoder.c - handle Sharp IR Pulse/Space protocol
+ *
+ * Copyright (C) 2013-2014 Imagination Technologies Ltd.
+ *
+ * Based on NEC decoder:
+ * Copyright (C) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation version 2 of the License.
+ *
+ *  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.
+ */
+
+#include <linux/bitrev.h>
+#include <linux/module.h>
+#include "rc-core-priv.h"
+
+#define SHARP_NBITS		15
+#define SHARP_UNIT		40000  /* ns */
+#define SHARP_BIT_PULSE		(8    * SHARP_UNIT) /* 320us */
+#define SHARP_BIT_0_PERIOD	(25   * SHARP_UNIT) /* 1ms (680us space) */
+#define SHARP_BIT_1_PERIOD	(50   * SHARP_UNIT) /* 2ms (1680ms space) */
+#define SHARP_ECHO_SPACE	(1000 * SHARP_UNIT) /* 40 ms */
+#define SHARP_TRAILER_SPACE	(125  * SHARP_UNIT) /* 5 ms (even longer) */
+
+enum sharp_state {
+	STATE_INACTIVE,
+	STATE_BIT_PULSE,
+	STATE_BIT_SPACE,
+	STATE_TRAILER_PULSE,
+	STATE_ECHO_SPACE,
+	STATE_TRAILER_SPACE,
+};
+
+/**
+ * ir_sharp_decode() - Decode one Sharp pulse or space
+ * @dev:	the struct rc_dev descriptor of the device
+ * @duration:	the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This function returns -EINVAL if the pulse violates the state machine
+ */
+static int ir_sharp_decode(struct rc_dev *dev, struct ir_raw_event ev)
+{
+	struct sharp_dec *data = &dev->raw->sharp;
+	u32 msg, echo, address, command, scancode;
+
+	if (!(dev->enabled_protocols & RC_BIT_SHARP))
+		return 0;
+
+	if (!is_timing_event(ev)) {
+		if (ev.reset)
+			data->state = STATE_INACTIVE;
+		return 0;
+	}
+
+	IR_dprintk(2, "Sharp decode started at state %d (%uus %s)\n",
+		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+
+	switch (data->state) {
+
+	case STATE_INACTIVE:
+		if (!ev.pulse)
+			break;
+
+		if (!eq_margin(ev.duration, SHARP_BIT_PULSE,
+			       SHARP_BIT_PULSE / 2))
+			break;
+
+		data->count = 0;
+		data->pulse_len = ev.duration;
+		data->state = STATE_BIT_SPACE;
+		return 0;
+
+	case STATE_BIT_PULSE:
+		if (!ev.pulse)
+			break;
+
+		if (!eq_margin(ev.duration, SHARP_BIT_PULSE,
+			       SHARP_BIT_PULSE / 2))
+			break;
+
+		data->pulse_len = ev.duration;
+		data->state = STATE_BIT_SPACE;
+		return 0;
+
+	case STATE_BIT_SPACE:
+		if (ev.pulse)
+			break;
+
+		data->bits <<= 1;
+		if (eq_margin(data->pulse_len + ev.duration, SHARP_BIT_1_PERIOD,
+			      SHARP_BIT_PULSE * 2))
+			data->bits |= 1;
+		else if (!eq_margin(data->pulse_len + ev.duration,
+				    SHARP_BIT_0_PERIOD, SHARP_BIT_PULSE * 2))
+			break;
+		data->count++;
+
+		if (data->count == SHARP_NBITS ||
+		    data->count == SHARP_NBITS * 2)
+			data->state = STATE_TRAILER_PULSE;
+		else
+			data->state = STATE_BIT_PULSE;
+
+		return 0;
+
+	case STATE_TRAILER_PULSE:
+		if (!ev.pulse)
+			break;
+
+		if (!eq_margin(ev.duration, SHARP_BIT_PULSE,
+			       SHARP_BIT_PULSE / 2))
+			break;
+
+		if (data->count == SHARP_NBITS) {
+			/* exp,chk bits should be 1,0 */
+			if ((data->bits & 0x3) != 0x2)
+				break;
+			data->state = STATE_ECHO_SPACE;
+		} else {
+			data->state = STATE_TRAILER_SPACE;
+		}
+		return 0;
+
+	case STATE_ECHO_SPACE:
+		if (ev.pulse)
+			break;
+
+		if (!eq_margin(ev.duration, SHARP_ECHO_SPACE,
+			       SHARP_ECHO_SPACE / 4))
+			break;
+
+		data->state = STATE_BIT_PULSE;
+
+		return 0;
+
+	case STATE_TRAILER_SPACE:
+		if (ev.pulse)
+			break;
+
+		if (!geq_margin(ev.duration, SHARP_TRAILER_SPACE,
+				SHARP_BIT_PULSE / 2))
+			break;
+
+		/* Validate - command, ext, chk should be inverted in 2nd */
+		msg = (data->bits >> 15) & 0x7fff;
+		echo = data->bits & 0x7fff;
+		if ((msg ^ echo) != 0x3ff) {
+			IR_dprintk(1,
+				   "Sharp checksum error: received 0x%04x, 0x%04x\n",
+				   msg, echo);
+			break;
+		}
+
+		address = bitrev8((msg >> 7) & 0xf8);
+		command = bitrev8((msg >> 2) & 0xff);
+
+		scancode = address << 8 | command;
+		IR_dprintk(1, "Sharp scancode 0x%04x\n", scancode);
+
+		rc_keydown(dev, RC_TYPE_SHARP, scancode, 0);
+		data->state = STATE_INACTIVE;
+		return 0;
+	}
+
+	IR_dprintk(1, "Sharp decode failed at count %d state %d (%uus %s)\n",
+		   data->count, data->state, TO_US(ev.duration),
+		   TO_STR(ev.pulse));
+	data->state = STATE_INACTIVE;
+	return -EINVAL;
+}
+
+static struct ir_raw_handler sharp_handler = {
+	.protocols	= RC_BIT_SHARP,
+	.decode		= ir_sharp_decode,
+};
+
+static int __init ir_sharp_decode_init(void)
+{
+	ir_raw_handler_register(&sharp_handler);
+
+	pr_info("IR Sharp protocol handler initialized\n");
+	return 0;
+}
+
+static void __exit ir_sharp_decode_exit(void)
+{
+	ir_raw_handler_unregister(&sharp_handler);
+}
+
+module_init(ir_sharp_decode_init);
+module_exit(ir_sharp_decode_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("James Hogan <james.hogan@imgtec.com>");
+MODULE_DESCRIPTION("Sharp IR protocol decoder");
diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c
new file mode 100644
index 0000000..58ef06f
--- /dev/null
+++ b/drivers/media/rc/ir-sony-decoder.c
@@ -0,0 +1,199 @@
+/* ir-sony-decoder.c - handle Sony IR Pulse/Space protocol
+ *
+ * Copyright (C) 2010 by David Härdeman <david@hardeman.nu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * 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.
+ */
+
+#include <linux/bitrev.h>
+#include <linux/module.h>
+#include "rc-core-priv.h"
+
+#define SONY_UNIT		600000 /* ns */
+#define SONY_HEADER_PULSE	(4 * SONY_UNIT)
+#define	SONY_HEADER_SPACE	(1 * SONY_UNIT)
+#define SONY_BIT_0_PULSE	(1 * SONY_UNIT)
+#define SONY_BIT_1_PULSE	(2 * SONY_UNIT)
+#define SONY_BIT_SPACE		(1 * SONY_UNIT)
+#define SONY_TRAILER_SPACE	(10 * SONY_UNIT) /* minimum */
+
+enum sony_state {
+	STATE_INACTIVE,
+	STATE_HEADER_SPACE,
+	STATE_BIT_PULSE,
+	STATE_BIT_SPACE,
+	STATE_FINISHED,
+};
+
+/**
+ * ir_sony_decode() - Decode one Sony pulse or space
+ * @dev:	the struct rc_dev descriptor of the device
+ * @ev:         the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This function returns -EINVAL if the pulse violates the state machine
+ */
+static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev)
+{
+	struct sony_dec *data = &dev->raw->sony;
+	enum rc_type protocol;
+	u32 scancode;
+	u8 device, subdevice, function;
+
+	if (!(dev->enabled_protocols &
+	      (RC_BIT_SONY12 | RC_BIT_SONY15 | RC_BIT_SONY20)))
+		return 0;
+
+	if (!is_timing_event(ev)) {
+		if (ev.reset)
+			data->state = STATE_INACTIVE;
+		return 0;
+	}
+
+	if (!geq_margin(ev.duration, SONY_UNIT, SONY_UNIT / 2))
+		goto out;
+
+	IR_dprintk(2, "Sony decode started at state %d (%uus %s)\n",
+		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+
+	switch (data->state) {
+
+	case STATE_INACTIVE:
+		if (!ev.pulse)
+			break;
+
+		if (!eq_margin(ev.duration, SONY_HEADER_PULSE, SONY_UNIT / 2))
+			break;
+
+		data->count = 0;
+		data->state = STATE_HEADER_SPACE;
+		return 0;
+
+	case STATE_HEADER_SPACE:
+		if (ev.pulse)
+			break;
+
+		if (!eq_margin(ev.duration, SONY_HEADER_SPACE, SONY_UNIT / 2))
+			break;
+
+		data->state = STATE_BIT_PULSE;
+		return 0;
+
+	case STATE_BIT_PULSE:
+		if (!ev.pulse)
+			break;
+
+		data->bits <<= 1;
+		if (eq_margin(ev.duration, SONY_BIT_1_PULSE, SONY_UNIT / 2))
+			data->bits |= 1;
+		else if (!eq_margin(ev.duration, SONY_BIT_0_PULSE, SONY_UNIT / 2))
+			break;
+
+		data->count++;
+		data->state = STATE_BIT_SPACE;
+		return 0;
+
+	case STATE_BIT_SPACE:
+		if (ev.pulse)
+			break;
+
+		if (!geq_margin(ev.duration, SONY_BIT_SPACE, SONY_UNIT / 2))
+			break;
+
+		decrease_duration(&ev, SONY_BIT_SPACE);
+
+		if (!geq_margin(ev.duration, SONY_UNIT, SONY_UNIT / 2)) {
+			data->state = STATE_BIT_PULSE;
+			return 0;
+		}
+
+		data->state = STATE_FINISHED;
+		/* Fall through */
+
+	case STATE_FINISHED:
+		if (ev.pulse)
+			break;
+
+		if (!geq_margin(ev.duration, SONY_TRAILER_SPACE, SONY_UNIT / 2))
+			break;
+
+		switch (data->count) {
+		case 12:
+			if (!(dev->enabled_protocols & RC_BIT_SONY12))
+				goto finish_state_machine;
+
+			device    = bitrev8((data->bits <<  3) & 0xF8);
+			subdevice = 0;
+			function  = bitrev8((data->bits >>  4) & 0xFE);
+			protocol = RC_TYPE_SONY12;
+			break;
+		case 15:
+			if (!(dev->enabled_protocols & RC_BIT_SONY15))
+				goto finish_state_machine;
+
+			device    = bitrev8((data->bits >>  0) & 0xFF);
+			subdevice = 0;
+			function  = bitrev8((data->bits >>  7) & 0xFE);
+			protocol = RC_TYPE_SONY15;
+			break;
+		case 20:
+			if (!(dev->enabled_protocols & RC_BIT_SONY20))
+				goto finish_state_machine;
+
+			device    = bitrev8((data->bits >>  5) & 0xF8);
+			subdevice = bitrev8((data->bits >>  0) & 0xFF);
+			function  = bitrev8((data->bits >> 12) & 0xFE);
+			protocol = RC_TYPE_SONY20;
+			break;
+		default:
+			IR_dprintk(1, "Sony invalid bitcount %u\n", data->count);
+			goto out;
+		}
+
+		scancode = device << 16 | subdevice << 8 | function;
+		IR_dprintk(1, "Sony(%u) scancode 0x%05x\n", data->count, scancode);
+		rc_keydown(dev, protocol, scancode, 0);
+		goto finish_state_machine;
+	}
+
+out:
+	IR_dprintk(1, "Sony decode failed at state %d (%uus %s)\n",
+		   data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+	data->state = STATE_INACTIVE;
+	return -EINVAL;
+
+finish_state_machine:
+	data->state = STATE_INACTIVE;
+	return 0;
+}
+
+static struct ir_raw_handler sony_handler = {
+	.protocols	= RC_BIT_SONY12 | RC_BIT_SONY15 | RC_BIT_SONY20,
+	.decode		= ir_sony_decode,
+};
+
+static int __init ir_sony_decode_init(void)
+{
+	ir_raw_handler_register(&sony_handler);
+
+	printk(KERN_INFO "IR Sony protocol handler initialized\n");
+	return 0;
+}
+
+static void __exit ir_sony_decode_exit(void)
+{
+	ir_raw_handler_unregister(&sony_handler);
+}
+
+module_init(ir_sony_decode_init);
+module_exit(ir_sony_decode_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Härdeman <david@hardeman.nu>");
+MODULE_DESCRIPTION("Sony IR protocol decoder");
diff --git a/drivers/media/rc/ir-xmp-decoder.c b/drivers/media/rc/ir-xmp-decoder.c
new file mode 100644
index 0000000..1017d48
--- /dev/null
+++ b/drivers/media/rc/ir-xmp-decoder.c
@@ -0,0 +1,225 @@
+/* ir-xmp-decoder.c - handle XMP IR Pulse/Space protocol
+ *
+ * Copyright (C) 2014 by Marcel Mol
+ *
+ * This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation version 2 of the License.
+ *
+ *  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.
+ *
+ * - Based on info from http://www.hifi-remote.com
+ * - Ignore Toggle=9 frames
+ * - Ignore XMP-1 XMP-2 difference, always store 16 bit OBC
+ */
+
+#include <linux/bitrev.h>
+#include <linux/module.h>
+#include "rc-core-priv.h"
+
+#define XMP_UNIT		  136000 /* ns */
+#define XMP_LEADER		  210000 /* ns */
+#define XMP_NIBBLE_PREFIX	  760000 /* ns */
+#define	XMP_HALFFRAME_SPACE	13800000 /* ns */
+#define	XMP_TRAILER_SPACE	20000000 /* should be 80ms but not all dureation supliers can go that high */
+
+enum xmp_state {
+	STATE_INACTIVE,
+	STATE_LEADER_PULSE,
+	STATE_NIBBLE_SPACE,
+};
+
+/**
+ * ir_xmp_decode() - Decode one XMP pulse or space
+ * @dev:	the struct rc_dev descriptor of the device
+ * @duration:	the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This function returns -EINVAL if the pulse violates the state machine
+ */
+static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev)
+{
+	struct xmp_dec *data = &dev->raw->xmp;
+
+	if (!(dev->enabled_protocols & RC_BIT_XMP))
+		return 0;
+
+	if (!is_timing_event(ev)) {
+		if (ev.reset)
+			data->state = STATE_INACTIVE;
+		return 0;
+	}
+
+	IR_dprintk(2, "XMP decode started at state %d %d (%uus %s)\n",
+		   data->state, data->count, TO_US(ev.duration), TO_STR(ev.pulse));
+
+	switch (data->state) {
+
+	case STATE_INACTIVE:
+		if (!ev.pulse)
+			break;
+
+		if (eq_margin(ev.duration, XMP_LEADER, XMP_UNIT / 2)) {
+			data->count = 0;
+			data->state = STATE_NIBBLE_SPACE;
+		}
+
+		return 0;
+
+	case STATE_LEADER_PULSE:
+		if (!ev.pulse)
+			break;
+
+		if (eq_margin(ev.duration, XMP_LEADER, XMP_UNIT / 2))
+			data->state = STATE_NIBBLE_SPACE;
+
+		return 0;
+
+	case STATE_NIBBLE_SPACE:
+		if (ev.pulse)
+			break;
+
+		if (geq_margin(ev.duration, XMP_TRAILER_SPACE, XMP_NIBBLE_PREFIX)) {
+			int divider, i;
+			u8 addr, subaddr, subaddr2, toggle, oem, obc1, obc2, sum1, sum2;
+			u32 *n;
+			u32 scancode;
+
+			if (data->count != 16) {
+				IR_dprintk(2, "received TRAILER period at index %d: %u\n",
+					data->count, ev.duration);
+				data->state = STATE_INACTIVE;
+				return -EINVAL;
+			}
+
+			n = data->durations;
+			/*
+			 * the 4th nibble should be 15 so base the divider on this
+			 * to transform durations into nibbles. Substract 2000 from
+			 * the divider to compensate for fluctuations in the signal
+			 */
+			divider = (n[3] - XMP_NIBBLE_PREFIX) / 15 - 2000;
+			if (divider < 50) {
+				IR_dprintk(2, "divider to small %d.\n", divider);
+				data->state = STATE_INACTIVE;
+				return -EINVAL;
+			}
+
+			/* convert to nibbles and do some sanity checks */
+			for (i = 0; i < 16; i++)
+				n[i] = (n[i] - XMP_NIBBLE_PREFIX) / divider;
+			sum1 = (15 + n[0] + n[1] + n[2] + n[3] +
+				n[4] + n[5] + n[6] + n[7]) % 16;
+			sum2 = (15 + n[8] + n[9] + n[10] + n[11] +
+				n[12] + n[13] + n[14] + n[15]) % 16;
+
+			if (sum1 != 15 || sum2 != 15) {
+				IR_dprintk(2, "checksum errors sum1=0x%X sum2=0x%X\n",
+					sum1, sum2);
+				data->state = STATE_INACTIVE;
+				return -EINVAL;
+			}
+
+			subaddr  = n[0] << 4 | n[2];
+			subaddr2 = n[8] << 4 | n[11];
+			oem      = n[4] << 4 | n[5];
+			addr     = n[6] << 4 | n[7];
+			toggle   = n[10];
+			obc1 = n[12] << 4 | n[13];
+			obc2 = n[14] << 4 | n[15];
+			if (subaddr != subaddr2) {
+				IR_dprintk(2, "subaddress nibbles mismatch 0x%02X != 0x%02X\n",
+					subaddr, subaddr2);
+				data->state = STATE_INACTIVE;
+				return -EINVAL;
+			}
+			if (oem != 0x44)
+				IR_dprintk(1, "Warning: OEM nibbles 0x%02X. Expected 0x44\n",
+					oem);
+
+			scancode = addr << 24 | subaddr << 16 |
+				   obc1 << 8 | obc2;
+			IR_dprintk(1, "XMP scancode 0x%06x\n", scancode);
+
+			if (toggle == 0) {
+				rc_keydown(dev, RC_TYPE_XMP, scancode, 0);
+			} else {
+				rc_repeat(dev);
+				IR_dprintk(1, "Repeat last key\n");
+			}
+			data->state = STATE_INACTIVE;
+
+			return 0;
+
+		} else if (geq_margin(ev.duration, XMP_HALFFRAME_SPACE, XMP_NIBBLE_PREFIX)) {
+			/* Expect 8 or 16 nibble pulses. 16 in case of 'final' frame */
+			if (data->count == 16) {
+				IR_dprintk(2, "received half frame pulse at index %d. Probably a final frame key-up event: %u\n",
+					data->count, ev.duration);
+				/*
+				 * TODO: for now go back to half frame position
+				 *	 so trailer can be found and key press
+				 *	 can be handled.
+				 */
+				data->count = 8;
+			}
+
+			else if (data->count != 8)
+				IR_dprintk(2, "received half frame pulse at index %d: %u\n",
+					data->count, ev.duration);
+			data->state = STATE_LEADER_PULSE;
+
+			return 0;
+
+		} else if (geq_margin(ev.duration, XMP_NIBBLE_PREFIX, XMP_UNIT)) {
+			/* store nibble raw data, decode after trailer */
+			if (data->count == 16) {
+				IR_dprintk(2, "to many pulses (%d) ignoring: %u\n",
+					data->count, ev.duration);
+				data->state = STATE_INACTIVE;
+				return -EINVAL;
+			}
+			data->durations[data->count] = ev.duration;
+			data->count++;
+			data->state = STATE_LEADER_PULSE;
+
+			return 0;
+
+		}
+
+		break;
+	}
+
+	IR_dprintk(1, "XMP decode failed at count %d state %d (%uus %s)\n",
+		   data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse));
+	data->state = STATE_INACTIVE;
+	return -EINVAL;
+}
+
+static struct ir_raw_handler xmp_handler = {
+	.protocols	= RC_BIT_XMP,
+	.decode		= ir_xmp_decode,
+};
+
+static int __init ir_xmp_decode_init(void)
+{
+	ir_raw_handler_register(&xmp_handler);
+
+	printk(KERN_INFO "IR XMP protocol handler initialized\n");
+	return 0;
+}
+
+static void __exit ir_xmp_decode_exit(void)
+{
+	ir_raw_handler_unregister(&xmp_handler);
+}
+
+module_init(ir_xmp_decode_init);
+module_exit(ir_xmp_decode_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Marcel Mol <marcel@mesa.nl>");
+MODULE_AUTHOR("MESA Consulting (http://www.mesa.nl)");
+MODULE_DESCRIPTION("XMP IR protocol decoder");
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
new file mode 100644
index 0000000..63165d3
--- /dev/null
+++ b/drivers/media/rc/ite-cir.c
@@ -0,0 +1,1719 @@
+/*
+ * Driver for ITE Tech Inc. IT8712F/IT8512 CIR
+ *
+ * Copyright (C) 2010 Juan Jesús García de Soria <skandalfo@gmail.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.
+ *
+ * Inspired by the original lirc_it87 and lirc_ite8709 drivers, on top of the
+ * skeleton provided by the nuvoton-cir driver.
+ *
+ * The lirc_it87 driver was originally written by Hans-Gunter Lutke Uphues
+ * <hg_lu@web.de> in 2001, with enhancements by Christoph Bartelmus
+ * <lirc@bartelmus.de>, Andrew Calkin <r_tay@hotmail.com> and James Edwards
+ * <jimbo-lirc@edwardsclan.net>.
+ *
+ * The lirc_ite8709 driver was written by Grégory Lardière
+ * <spmf2004-lirc@yahoo.fr> in 2008.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pnp.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/bitops.h>
+#include <media/rc-core.h>
+#include <linux/pci_ids.h>
+
+#include "ite-cir.h"
+
+/* module parameters */
+
+/* debug level */
+static int debug;
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging output");
+
+/* low limit for RX carrier freq, Hz, 0 for no RX demodulation */
+static int rx_low_carrier_freq;
+module_param(rx_low_carrier_freq, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(rx_low_carrier_freq, "Override low RX carrier frequency, Hz, "
+		 "0 for no RX demodulation");
+
+/* high limit for RX carrier freq, Hz, 0 for no RX demodulation */
+static int rx_high_carrier_freq;
+module_param(rx_high_carrier_freq, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(rx_high_carrier_freq, "Override high RX carrier frequency, "
+		 "Hz, 0 for no RX demodulation");
+
+/* override tx carrier frequency */
+static int tx_carrier_freq;
+module_param(tx_carrier_freq, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(tx_carrier_freq, "Override TX carrier frequency, Hz");
+
+/* override tx duty cycle */
+static int tx_duty_cycle;
+module_param(tx_duty_cycle, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(tx_duty_cycle, "Override TX duty cycle, 1-100");
+
+/* override default sample period */
+static long sample_period;
+module_param(sample_period, long, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(sample_period, "Override carrier sample period, us");
+
+/* override detected model id */
+static int model_number = -1;
+module_param(model_number, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(model_number, "Use this model number, don't autodetect");
+
+
+/* HW-independent code functions */
+
+/* check whether carrier frequency is high frequency */
+static inline bool ite_is_high_carrier_freq(unsigned int freq)
+{
+	return freq >= ITE_HCF_MIN_CARRIER_FREQ;
+}
+
+/* get the bits required to program the carrier frequency in CFQ bits,
+ * unshifted */
+static u8 ite_get_carrier_freq_bits(unsigned int freq)
+{
+	if (ite_is_high_carrier_freq(freq)) {
+		if (freq < 425000)
+			return ITE_CFQ_400;
+
+		else if (freq < 465000)
+			return ITE_CFQ_450;
+
+		else if (freq < 490000)
+			return ITE_CFQ_480;
+
+		else
+			return ITE_CFQ_500;
+	} else {
+			/* trim to limits */
+		if (freq < ITE_LCF_MIN_CARRIER_FREQ)
+			freq = ITE_LCF_MIN_CARRIER_FREQ;
+		if (freq > ITE_LCF_MAX_CARRIER_FREQ)
+			freq = ITE_LCF_MAX_CARRIER_FREQ;
+
+		/* convert to kHz and subtract the base freq */
+		freq =
+		    DIV_ROUND_CLOSEST(freq - ITE_LCF_MIN_CARRIER_FREQ,
+				      1000);
+
+		return (u8) freq;
+	}
+}
+
+/* get the bits required to program the pulse with in TXMPW */
+static u8 ite_get_pulse_width_bits(unsigned int freq, int duty_cycle)
+{
+	unsigned long period_ns, on_ns;
+
+	/* sanitize freq into range */
+	if (freq < ITE_LCF_MIN_CARRIER_FREQ)
+		freq = ITE_LCF_MIN_CARRIER_FREQ;
+	if (freq > ITE_HCF_MAX_CARRIER_FREQ)
+		freq = ITE_HCF_MAX_CARRIER_FREQ;
+
+	period_ns = 1000000000UL / freq;
+	on_ns = period_ns * duty_cycle / 100;
+
+	if (ite_is_high_carrier_freq(freq)) {
+		if (on_ns < 750)
+			return ITE_TXMPW_A;
+
+		else if (on_ns < 850)
+			return ITE_TXMPW_B;
+
+		else if (on_ns < 950)
+			return ITE_TXMPW_C;
+
+		else if (on_ns < 1080)
+			return ITE_TXMPW_D;
+
+		else
+			return ITE_TXMPW_E;
+	} else {
+		if (on_ns < 6500)
+			return ITE_TXMPW_A;
+
+		else if (on_ns < 7850)
+			return ITE_TXMPW_B;
+
+		else if (on_ns < 9650)
+			return ITE_TXMPW_C;
+
+		else if (on_ns < 11950)
+			return ITE_TXMPW_D;
+
+		else
+			return ITE_TXMPW_E;
+	}
+}
+
+/* decode raw bytes as received by the hardware, and push them to the ir-core
+ * layer */
+static void ite_decode_bytes(struct ite_dev *dev, const u8 * data, int
+			     length)
+{
+	u32 sample_period;
+	unsigned long *ldata;
+	unsigned int next_one, next_zero, size;
+	DEFINE_IR_RAW_EVENT(ev);
+
+	if (length == 0)
+		return;
+
+	sample_period = dev->params.sample_period;
+	ldata = (unsigned long *)data;
+	size = length << 3;
+	next_one = find_next_bit_le(ldata, size, 0);
+	if (next_one > 0) {
+		ev.pulse = true;
+		ev.duration =
+		    ITE_BITS_TO_NS(next_one, sample_period);
+		ir_raw_event_store_with_filter(dev->rdev, &ev);
+	}
+
+	while (next_one < size) {
+		next_zero = find_next_zero_bit_le(ldata, size, next_one + 1);
+		ev.pulse = false;
+		ev.duration = ITE_BITS_TO_NS(next_zero - next_one, sample_period);
+		ir_raw_event_store_with_filter(dev->rdev, &ev);
+
+		if (next_zero < size) {
+			next_one =
+			    find_next_bit_le(ldata,
+						     size,
+						     next_zero + 1);
+			ev.pulse = true;
+			ev.duration =
+			    ITE_BITS_TO_NS(next_one - next_zero,
+					   sample_period);
+			ir_raw_event_store_with_filter
+			    (dev->rdev, &ev);
+		} else
+			next_one = size;
+	}
+
+	ir_raw_event_handle(dev->rdev);
+
+	ite_dbg_verbose("decoded %d bytes.", length);
+}
+
+/* set all the rx/tx carrier parameters; this must be called with the device
+ * spinlock held */
+static void ite_set_carrier_params(struct ite_dev *dev)
+{
+	unsigned int freq, low_freq, high_freq;
+	int allowance;
+	bool use_demodulator;
+	bool for_tx = dev->transmitting;
+
+	ite_dbg("%s called", __func__);
+
+	if (for_tx) {
+		/* we don't need no stinking calculations */
+		freq = dev->params.tx_carrier_freq;
+		allowance = ITE_RXDCR_DEFAULT;
+		use_demodulator = false;
+	} else {
+		low_freq = dev->params.rx_low_carrier_freq;
+		high_freq = dev->params.rx_high_carrier_freq;
+
+		if (low_freq == 0) {
+			/* don't demodulate */
+			freq =
+			ITE_DEFAULT_CARRIER_FREQ;
+			allowance = ITE_RXDCR_DEFAULT;
+			use_demodulator = false;
+		} else {
+			/* calculate the middle freq */
+			freq = (low_freq + high_freq) / 2;
+
+			/* calculate the allowance */
+			allowance =
+			    DIV_ROUND_CLOSEST(10000 * (high_freq - low_freq),
+					      ITE_RXDCR_PER_10000_STEP
+					      * (high_freq + low_freq));
+
+			if (allowance < 1)
+				allowance = 1;
+
+			if (allowance > ITE_RXDCR_MAX)
+				allowance = ITE_RXDCR_MAX;
+
+			use_demodulator = true;
+		}
+	}
+
+	/* set the carrier parameters in a device-dependent way */
+	dev->params.set_carrier_params(dev, ite_is_high_carrier_freq(freq),
+		 use_demodulator, ite_get_carrier_freq_bits(freq), allowance,
+		 ite_get_pulse_width_bits(freq, dev->params.tx_duty_cycle));
+}
+
+/* interrupt service routine for incoming and outgoing CIR data */
+static irqreturn_t ite_cir_isr(int irq, void *data)
+{
+	struct ite_dev *dev = data;
+	unsigned long flags;
+	irqreturn_t ret = IRQ_RETVAL(IRQ_NONE);
+	u8 rx_buf[ITE_RX_FIFO_LEN];
+	int rx_bytes;
+	int iflags;
+
+	ite_dbg_verbose("%s firing", __func__);
+
+	/* grab the spinlock */
+	spin_lock_irqsave(&dev->lock, flags);
+
+	/* read the interrupt flags */
+	iflags = dev->params.get_irq_causes(dev);
+
+	/* check for the receive interrupt */
+	if (iflags & (ITE_IRQ_RX_FIFO | ITE_IRQ_RX_FIFO_OVERRUN)) {
+		/* read the FIFO bytes */
+		rx_bytes =
+			dev->params.get_rx_bytes(dev, rx_buf,
+					     ITE_RX_FIFO_LEN);
+
+		if (rx_bytes > 0) {
+			/* drop the spinlock, since the ir-core layer
+			 * may call us back again through
+			 * ite_s_idle() */
+			spin_unlock_irqrestore(&dev->
+									 lock,
+									 flags);
+
+			/* decode the data we've just received */
+			ite_decode_bytes(dev, rx_buf,
+								   rx_bytes);
+
+			/* reacquire the spinlock */
+			spin_lock_irqsave(&dev->lock,
+								    flags);
+
+			/* mark the interrupt as serviced */
+			ret = IRQ_RETVAL(IRQ_HANDLED);
+		}
+	} else if (iflags & ITE_IRQ_TX_FIFO) {
+		/* FIFO space available interrupt */
+		ite_dbg_verbose("got interrupt for TX FIFO");
+
+		/* wake any sleeping transmitter */
+		wake_up_interruptible(&dev->tx_queue);
+
+		/* mark the interrupt as serviced */
+		ret = IRQ_RETVAL(IRQ_HANDLED);
+	}
+
+	/* drop the spinlock */
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	ite_dbg_verbose("%s done returning %d", __func__, (int)ret);
+
+	return ret;
+}
+
+/* set the rx carrier freq range, guess it's in Hz... */
+static int ite_set_rx_carrier_range(struct rc_dev *rcdev, u32 carrier_low, u32
+				    carrier_high)
+{
+	unsigned long flags;
+	struct ite_dev *dev = rcdev->priv;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->params.rx_low_carrier_freq = carrier_low;
+	dev->params.rx_high_carrier_freq = carrier_high;
+	ite_set_carrier_params(dev);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+/* set the tx carrier freq, guess it's in Hz... */
+static int ite_set_tx_carrier(struct rc_dev *rcdev, u32 carrier)
+{
+	unsigned long flags;
+	struct ite_dev *dev = rcdev->priv;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->params.tx_carrier_freq = carrier;
+	ite_set_carrier_params(dev);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+/* set the tx duty cycle by controlling the pulse width */
+static int ite_set_tx_duty_cycle(struct rc_dev *rcdev, u32 duty_cycle)
+{
+	unsigned long flags;
+	struct ite_dev *dev = rcdev->priv;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->params.tx_duty_cycle = duty_cycle;
+	ite_set_carrier_params(dev);
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+/* transmit out IR pulses; what you get here is a batch of alternating
+ * pulse/space/pulse/space lengths that we should write out completely through
+ * the FIFO, blocking on a full FIFO */
+static int ite_tx_ir(struct rc_dev *rcdev, unsigned *txbuf, unsigned n)
+{
+	unsigned long flags;
+	struct ite_dev *dev = rcdev->priv;
+	bool is_pulse = false;
+	int remaining_us, fifo_avail, fifo_remaining, last_idx = 0;
+	int max_rle_us, next_rle_us;
+	int ret = n;
+	u8 last_sent[ITE_TX_FIFO_LEN];
+	u8 val;
+
+	ite_dbg("%s called", __func__);
+
+	/* clear the array just in case */
+	memset(last_sent, 0, ARRAY_SIZE(last_sent));
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	/* let everybody know we're now transmitting */
+	dev->transmitting = true;
+
+	/* and set the carrier values for transmission */
+	ite_set_carrier_params(dev);
+
+	/* calculate how much time we can send in one byte */
+	max_rle_us =
+	    (ITE_BAUDRATE_DIVISOR * dev->params.sample_period *
+	     ITE_TX_MAX_RLE) / 1000;
+
+	/* disable the receiver */
+	dev->params.disable_rx(dev);
+
+	/* this is where we'll begin filling in the FIFO, until it's full.
+	 * then we'll just activate the interrupt, wait for it to wake us up
+	 * again, disable it, continue filling the FIFO... until everything
+	 * has been pushed out */
+	fifo_avail =
+	    ITE_TX_FIFO_LEN - dev->params.get_tx_used_slots(dev);
+
+	while (n > 0 && dev->in_use) {
+		/* transmit the next sample */
+		is_pulse = !is_pulse;
+		remaining_us = *(txbuf++);
+		n--;
+
+		ite_dbg("%s: %ld",
+				      ((is_pulse) ? "pulse" : "space"),
+				      (long int)
+				      remaining_us);
+
+		/* repeat while the pulse is non-zero length */
+		while (remaining_us > 0 && dev->in_use) {
+			if (remaining_us > max_rle_us)
+				next_rle_us = max_rle_us;
+
+			else
+				next_rle_us = remaining_us;
+
+			remaining_us -= next_rle_us;
+
+			/* check what's the length we have to pump out */
+			val = (ITE_TX_MAX_RLE * next_rle_us) / max_rle_us;
+
+			/* put it into the sent buffer */
+			last_sent[last_idx++] = val;
+			last_idx &= (ITE_TX_FIFO_LEN);
+
+			/* encode it for 7 bits */
+			val = (val - 1) & ITE_TX_RLE_MASK;
+
+			/* take into account pulse/space prefix */
+			if (is_pulse)
+				val |= ITE_TX_PULSE;
+
+			else
+				val |= ITE_TX_SPACE;
+
+			/*
+			 * if we get to 0 available, read again, just in case
+			 * some other slot got freed
+			 */
+			if (fifo_avail <= 0)
+				fifo_avail = ITE_TX_FIFO_LEN - dev->params.get_tx_used_slots(dev);
+
+			/* if it's still full */
+			if (fifo_avail <= 0) {
+				/* enable the tx interrupt */
+				dev->params.
+				enable_tx_interrupt(dev);
+
+				/* drop the spinlock */
+				spin_unlock_irqrestore(&dev->lock, flags);
+
+				/* wait for the FIFO to empty enough */
+				wait_event_interruptible(dev->tx_queue, (fifo_avail = ITE_TX_FIFO_LEN - dev->params.get_tx_used_slots(dev)) >= 8);
+
+				/* get the spinlock again */
+				spin_lock_irqsave(&dev->lock, flags);
+
+				/* disable the tx interrupt again. */
+				dev->params.
+				disable_tx_interrupt(dev);
+			}
+
+			/* now send the byte through the FIFO */
+			dev->params.put_tx_byte(dev, val);
+			fifo_avail--;
+		}
+	}
+
+	/* wait and don't return until the whole FIFO has been sent out;
+	 * otherwise we could configure the RX carrier params instead of the
+	 * TX ones while the transmission is still being performed! */
+	fifo_remaining = dev->params.get_tx_used_slots(dev);
+	remaining_us = 0;
+	while (fifo_remaining > 0) {
+		fifo_remaining--;
+		last_idx--;
+		last_idx &= (ITE_TX_FIFO_LEN - 1);
+		remaining_us += last_sent[last_idx];
+	}
+	remaining_us = (remaining_us * max_rle_us) / (ITE_TX_MAX_RLE);
+
+	/* drop the spinlock while we sleep */
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	/* sleep remaining_us microseconds */
+	mdelay(DIV_ROUND_UP(remaining_us, 1000));
+
+	/* reacquire the spinlock */
+	spin_lock_irqsave(&dev->lock, flags);
+
+	/* now we're not transmitting anymore */
+	dev->transmitting = false;
+
+	/* and set the carrier values for reception */
+	ite_set_carrier_params(dev);
+
+	/* reenable the receiver */
+	if (dev->in_use)
+		dev->params.enable_rx(dev);
+
+	/* notify transmission end */
+	wake_up_interruptible(&dev->tx_ended);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return ret;
+}
+
+/* idle the receiver if needed */
+static void ite_s_idle(struct rc_dev *rcdev, bool enable)
+{
+	unsigned long flags;
+	struct ite_dev *dev = rcdev->priv;
+
+	ite_dbg("%s called", __func__);
+
+	if (enable) {
+		spin_lock_irqsave(&dev->lock, flags);
+		dev->params.idle_rx(dev);
+		spin_unlock_irqrestore(&dev->lock, flags);
+	}
+}
+
+
+/* IT8712F HW-specific functions */
+
+/* retrieve a bitmask of the current causes for a pending interrupt; this may
+ * be composed of ITE_IRQ_TX_FIFO, ITE_IRQ_RX_FIFO and ITE_IRQ_RX_FIFO_OVERRUN
+ * */
+static int it87_get_irq_causes(struct ite_dev *dev)
+{
+	u8 iflags;
+	int ret = 0;
+
+	ite_dbg("%s called", __func__);
+
+	/* read the interrupt flags */
+	iflags = inb(dev->cir_addr + IT87_IIR) & IT87_II;
+
+	switch (iflags) {
+	case IT87_II_RXDS:
+		ret = ITE_IRQ_RX_FIFO;
+		break;
+	case IT87_II_RXFO:
+		ret = ITE_IRQ_RX_FIFO_OVERRUN;
+		break;
+	case IT87_II_TXLDL:
+		ret = ITE_IRQ_TX_FIFO;
+		break;
+	}
+
+	return ret;
+}
+
+/* set the carrier parameters; to be called with the spinlock held */
+static void it87_set_carrier_params(struct ite_dev *dev, bool high_freq,
+				    bool use_demodulator,
+				    u8 carrier_freq_bits, u8 allowance_bits,
+				    u8 pulse_width_bits)
+{
+	u8 val;
+
+	ite_dbg("%s called", __func__);
+
+	/* program the RCR register */
+	val = inb(dev->cir_addr + IT87_RCR)
+		& ~(IT87_HCFS | IT87_RXEND | IT87_RXDCR);
+
+	if (high_freq)
+		val |= IT87_HCFS;
+
+	if (use_demodulator)
+		val |= IT87_RXEND;
+
+	val |= allowance_bits;
+
+	outb(val, dev->cir_addr + IT87_RCR);
+
+	/* program the TCR2 register */
+	outb((carrier_freq_bits << IT87_CFQ_SHIFT) | pulse_width_bits,
+		dev->cir_addr + IT87_TCR2);
+}
+
+/* read up to buf_size bytes from the RX FIFO; to be called with the spinlock
+ * held */
+static int it87_get_rx_bytes(struct ite_dev *dev, u8 * buf, int buf_size)
+{
+	int fifo, read = 0;
+
+	ite_dbg("%s called", __func__);
+
+	/* read how many bytes are still in the FIFO */
+	fifo = inb(dev->cir_addr + IT87_RSR) & IT87_RXFBC;
+
+	while (fifo > 0 && buf_size > 0) {
+		*(buf++) = inb(dev->cir_addr + IT87_DR);
+		fifo--;
+		read++;
+		buf_size--;
+	}
+
+	return read;
+}
+
+/* return how many bytes are still in the FIFO; this will be called
+ * with the device spinlock NOT HELD while waiting for the TX FIFO to get
+ * empty; let's expect this won't be a problem */
+static int it87_get_tx_used_slots(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	return inb(dev->cir_addr + IT87_TSR) & IT87_TXFBC;
+}
+
+/* put a byte to the TX fifo; this should be called with the spinlock held */
+static void it87_put_tx_byte(struct ite_dev *dev, u8 value)
+{
+	outb(value, dev->cir_addr + IT87_DR);
+}
+
+/* idle the receiver so that we won't receive samples until another
+  pulse is detected; this must be called with the device spinlock held */
+static void it87_idle_rx(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	/* disable streaming by clearing RXACT writing it as 1 */
+	outb(inb(dev->cir_addr + IT87_RCR) | IT87_RXACT,
+		dev->cir_addr + IT87_RCR);
+
+	/* clear the FIFO */
+	outb(inb(dev->cir_addr + IT87_TCR1) | IT87_FIFOCLR,
+		dev->cir_addr + IT87_TCR1);
+}
+
+/* disable the receiver; this must be called with the device spinlock held */
+static void it87_disable_rx(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	/* disable the receiver interrupts */
+	outb(inb(dev->cir_addr + IT87_IER) & ~(IT87_RDAIE | IT87_RFOIE),
+		dev->cir_addr + IT87_IER);
+
+	/* disable the receiver */
+	outb(inb(dev->cir_addr + IT87_RCR) & ~IT87_RXEN,
+		dev->cir_addr + IT87_RCR);
+
+	/* clear the FIFO and RXACT (actually RXACT should have been cleared
+	* in the previous outb() call) */
+	it87_idle_rx(dev);
+}
+
+/* enable the receiver; this must be called with the device spinlock held */
+static void it87_enable_rx(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	/* enable the receiver by setting RXEN */
+	outb(inb(dev->cir_addr + IT87_RCR) | IT87_RXEN,
+		dev->cir_addr + IT87_RCR);
+
+	/* just prepare it to idle for the next reception */
+	it87_idle_rx(dev);
+
+	/* enable the receiver interrupts and master enable flag */
+	outb(inb(dev->cir_addr + IT87_IER) | IT87_RDAIE | IT87_RFOIE | IT87_IEC,
+		dev->cir_addr + IT87_IER);
+}
+
+/* disable the transmitter interrupt; this must be called with the device
+ * spinlock held */
+static void it87_disable_tx_interrupt(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	/* disable the transmitter interrupts */
+	outb(inb(dev->cir_addr + IT87_IER) & ~IT87_TLDLIE,
+		dev->cir_addr + IT87_IER);
+}
+
+/* enable the transmitter interrupt; this must be called with the device
+ * spinlock held */
+static void it87_enable_tx_interrupt(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	/* enable the transmitter interrupts and master enable flag */
+	outb(inb(dev->cir_addr + IT87_IER) | IT87_TLDLIE | IT87_IEC,
+		dev->cir_addr + IT87_IER);
+}
+
+/* disable the device; this must be called with the device spinlock held */
+static void it87_disable(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	/* clear out all interrupt enable flags */
+	outb(inb(dev->cir_addr + IT87_IER) &
+		~(IT87_IEC | IT87_RFOIE | IT87_RDAIE | IT87_TLDLIE),
+		dev->cir_addr + IT87_IER);
+
+	/* disable the receiver */
+	it87_disable_rx(dev);
+
+	/* erase the FIFO */
+	outb(IT87_FIFOCLR | inb(dev->cir_addr + IT87_TCR1),
+		dev->cir_addr + IT87_TCR1);
+}
+
+/* initialize the hardware */
+static void it87_init_hardware(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	/* enable just the baud rate divisor register,
+	disabling all the interrupts at the same time */
+	outb((inb(dev->cir_addr + IT87_IER) &
+		~(IT87_IEC | IT87_RFOIE | IT87_RDAIE | IT87_TLDLIE)) | IT87_BR,
+		dev->cir_addr + IT87_IER);
+
+	/* write out the baud rate divisor */
+	outb(ITE_BAUDRATE_DIVISOR & 0xff, dev->cir_addr + IT87_BDLR);
+	outb((ITE_BAUDRATE_DIVISOR >> 8) & 0xff, dev->cir_addr + IT87_BDHR);
+
+	/* disable the baud rate divisor register again */
+	outb(inb(dev->cir_addr + IT87_IER) & ~IT87_BR,
+		dev->cir_addr + IT87_IER);
+
+	/* program the RCR register defaults */
+	outb(ITE_RXDCR_DEFAULT, dev->cir_addr + IT87_RCR);
+
+	/* program the TCR1 register */
+	outb(IT87_TXMPM_DEFAULT | IT87_TXENDF | IT87_TXRLE
+		| IT87_FIFOTL_DEFAULT | IT87_FIFOCLR,
+		dev->cir_addr + IT87_TCR1);
+
+	/* program the carrier parameters */
+	ite_set_carrier_params(dev);
+}
+
+/* IT8512F on ITE8708 HW-specific functions */
+
+/* retrieve a bitmask of the current causes for a pending interrupt; this may
+ * be composed of ITE_IRQ_TX_FIFO, ITE_IRQ_RX_FIFO and ITE_IRQ_RX_FIFO_OVERRUN
+ * */
+static int it8708_get_irq_causes(struct ite_dev *dev)
+{
+	u8 iflags;
+	int ret = 0;
+
+	ite_dbg("%s called", __func__);
+
+	/* read the interrupt flags */
+	iflags = inb(dev->cir_addr + IT8708_C0IIR);
+
+	if (iflags & IT85_TLDLI)
+		ret |= ITE_IRQ_TX_FIFO;
+	if (iflags & IT85_RDAI)
+		ret |= ITE_IRQ_RX_FIFO;
+	if (iflags & IT85_RFOI)
+		ret |= ITE_IRQ_RX_FIFO_OVERRUN;
+
+	return ret;
+}
+
+/* set the carrier parameters; to be called with the spinlock held */
+static void it8708_set_carrier_params(struct ite_dev *dev, bool high_freq,
+				      bool use_demodulator,
+				      u8 carrier_freq_bits, u8 allowance_bits,
+				      u8 pulse_width_bits)
+{
+	u8 val;
+
+	ite_dbg("%s called", __func__);
+
+	/* program the C0CFR register, with HRAE=1 */
+	outb(inb(dev->cir_addr + IT8708_BANKSEL) | IT8708_HRAE,
+		dev->cir_addr + IT8708_BANKSEL);
+
+	val = (inb(dev->cir_addr + IT8708_C0CFR)
+		& ~(IT85_HCFS | IT85_CFQ)) | carrier_freq_bits;
+
+	if (high_freq)
+		val |= IT85_HCFS;
+
+	outb(val, dev->cir_addr + IT8708_C0CFR);
+
+	outb(inb(dev->cir_addr + IT8708_BANKSEL) & ~IT8708_HRAE,
+		   dev->cir_addr + IT8708_BANKSEL);
+
+	/* program the C0RCR register */
+	val = inb(dev->cir_addr + IT8708_C0RCR)
+		& ~(IT85_RXEND | IT85_RXDCR);
+
+	if (use_demodulator)
+		val |= IT85_RXEND;
+
+	val |= allowance_bits;
+
+	outb(val, dev->cir_addr + IT8708_C0RCR);
+
+	/* program the C0TCR register */
+	val = inb(dev->cir_addr + IT8708_C0TCR) & ~IT85_TXMPW;
+	val |= pulse_width_bits;
+	outb(val, dev->cir_addr + IT8708_C0TCR);
+}
+
+/* read up to buf_size bytes from the RX FIFO; to be called with the spinlock
+ * held */
+static int it8708_get_rx_bytes(struct ite_dev *dev, u8 * buf, int buf_size)
+{
+	int fifo, read = 0;
+
+	ite_dbg("%s called", __func__);
+
+	/* read how many bytes are still in the FIFO */
+	fifo = inb(dev->cir_addr + IT8708_C0RFSR) & IT85_RXFBC;
+
+	while (fifo > 0 && buf_size > 0) {
+		*(buf++) = inb(dev->cir_addr + IT8708_C0DR);
+		fifo--;
+		read++;
+		buf_size--;
+	}
+
+	return read;
+}
+
+/* return how many bytes are still in the FIFO; this will be called
+ * with the device spinlock NOT HELD while waiting for the TX FIFO to get
+ * empty; let's expect this won't be a problem */
+static int it8708_get_tx_used_slots(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	return inb(dev->cir_addr + IT8708_C0TFSR) & IT85_TXFBC;
+}
+
+/* put a byte to the TX fifo; this should be called with the spinlock held */
+static void it8708_put_tx_byte(struct ite_dev *dev, u8 value)
+{
+	outb(value, dev->cir_addr + IT8708_C0DR);
+}
+
+/* idle the receiver so that we won't receive samples until another
+  pulse is detected; this must be called with the device spinlock held */
+static void it8708_idle_rx(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	/* disable streaming by clearing RXACT writing it as 1 */
+	outb(inb(dev->cir_addr + IT8708_C0RCR) | IT85_RXACT,
+		dev->cir_addr + IT8708_C0RCR);
+
+	/* clear the FIFO */
+	outb(inb(dev->cir_addr + IT8708_C0MSTCR) | IT85_FIFOCLR,
+		dev->cir_addr + IT8708_C0MSTCR);
+}
+
+/* disable the receiver; this must be called with the device spinlock held */
+static void it8708_disable_rx(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	/* disable the receiver interrupts */
+	outb(inb(dev->cir_addr + IT8708_C0IER) &
+		~(IT85_RDAIE | IT85_RFOIE),
+		dev->cir_addr + IT8708_C0IER);
+
+	/* disable the receiver */
+	outb(inb(dev->cir_addr + IT8708_C0RCR) & ~IT85_RXEN,
+		dev->cir_addr + IT8708_C0RCR);
+
+	/* clear the FIFO and RXACT (actually RXACT should have been cleared
+	 * in the previous outb() call) */
+	it8708_idle_rx(dev);
+}
+
+/* enable the receiver; this must be called with the device spinlock held */
+static void it8708_enable_rx(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	/* enable the receiver by setting RXEN */
+	outb(inb(dev->cir_addr + IT8708_C0RCR) | IT85_RXEN,
+		dev->cir_addr + IT8708_C0RCR);
+
+	/* just prepare it to idle for the next reception */
+	it8708_idle_rx(dev);
+
+	/* enable the receiver interrupts and master enable flag */
+	outb(inb(dev->cir_addr + IT8708_C0IER)
+		|IT85_RDAIE | IT85_RFOIE | IT85_IEC,
+		dev->cir_addr + IT8708_C0IER);
+}
+
+/* disable the transmitter interrupt; this must be called with the device
+ * spinlock held */
+static void it8708_disable_tx_interrupt(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	/* disable the transmitter interrupts */
+	outb(inb(dev->cir_addr + IT8708_C0IER) & ~IT85_TLDLIE,
+		dev->cir_addr + IT8708_C0IER);
+}
+
+/* enable the transmitter interrupt; this must be called with the device
+ * spinlock held */
+static void it8708_enable_tx_interrupt(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	/* enable the transmitter interrupts and master enable flag */
+	outb(inb(dev->cir_addr + IT8708_C0IER)
+		|IT85_TLDLIE | IT85_IEC,
+		dev->cir_addr + IT8708_C0IER);
+}
+
+/* disable the device; this must be called with the device spinlock held */
+static void it8708_disable(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	/* clear out all interrupt enable flags */
+	outb(inb(dev->cir_addr + IT8708_C0IER) &
+		~(IT85_IEC | IT85_RFOIE | IT85_RDAIE | IT85_TLDLIE),
+		dev->cir_addr + IT8708_C0IER);
+
+	/* disable the receiver */
+	it8708_disable_rx(dev);
+
+	/* erase the FIFO */
+	outb(IT85_FIFOCLR | inb(dev->cir_addr + IT8708_C0MSTCR),
+		dev->cir_addr + IT8708_C0MSTCR);
+}
+
+/* initialize the hardware */
+static void it8708_init_hardware(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	/* disable all the interrupts */
+	outb(inb(dev->cir_addr + IT8708_C0IER) &
+		~(IT85_IEC | IT85_RFOIE | IT85_RDAIE | IT85_TLDLIE),
+		dev->cir_addr + IT8708_C0IER);
+
+	/* program the baud rate divisor */
+	outb(inb(dev->cir_addr + IT8708_BANKSEL) | IT8708_HRAE,
+		dev->cir_addr + IT8708_BANKSEL);
+
+	outb(ITE_BAUDRATE_DIVISOR & 0xff, dev->cir_addr + IT8708_C0BDLR);
+	outb((ITE_BAUDRATE_DIVISOR >> 8) & 0xff,
+		   dev->cir_addr + IT8708_C0BDHR);
+
+	outb(inb(dev->cir_addr + IT8708_BANKSEL) & ~IT8708_HRAE,
+		   dev->cir_addr + IT8708_BANKSEL);
+
+	/* program the C0MSTCR register defaults */
+	outb((inb(dev->cir_addr + IT8708_C0MSTCR) &
+			~(IT85_ILSEL | IT85_ILE | IT85_FIFOTL |
+			  IT85_FIFOCLR | IT85_RESET)) |
+		       IT85_FIFOTL_DEFAULT,
+		       dev->cir_addr + IT8708_C0MSTCR);
+
+	/* program the C0RCR register defaults */
+	outb((inb(dev->cir_addr + IT8708_C0RCR) &
+			~(IT85_RXEN | IT85_RDWOS | IT85_RXEND |
+			  IT85_RXACT | IT85_RXDCR)) |
+		       ITE_RXDCR_DEFAULT,
+		       dev->cir_addr + IT8708_C0RCR);
+
+	/* program the C0TCR register defaults */
+	outb((inb(dev->cir_addr + IT8708_C0TCR) &
+			~(IT85_TXMPM | IT85_TXMPW))
+		       |IT85_TXRLE | IT85_TXENDF |
+		       IT85_TXMPM_DEFAULT | IT85_TXMPW_DEFAULT,
+		       dev->cir_addr + IT8708_C0TCR);
+
+	/* program the carrier parameters */
+	ite_set_carrier_params(dev);
+}
+
+/* IT8512F on ITE8709 HW-specific functions */
+
+/* read a byte from the SRAM module */
+static inline u8 it8709_rm(struct ite_dev *dev, int index)
+{
+	outb(index, dev->cir_addr + IT8709_RAM_IDX);
+	return inb(dev->cir_addr + IT8709_RAM_VAL);
+}
+
+/* write a byte to the SRAM module */
+static inline void it8709_wm(struct ite_dev *dev, u8 val, int index)
+{
+	outb(index, dev->cir_addr + IT8709_RAM_IDX);
+	outb(val, dev->cir_addr + IT8709_RAM_VAL);
+}
+
+static void it8709_wait(struct ite_dev *dev)
+{
+	int i = 0;
+	/*
+	 * loop until device tells it's ready to continue
+	 * iterations count is usually ~750 but can sometimes achieve 13000
+	 */
+	for (i = 0; i < 15000; i++) {
+		udelay(2);
+		if (it8709_rm(dev, IT8709_MODE) == IT8709_IDLE)
+			break;
+	}
+}
+
+/* read the value of a CIR register */
+static u8 it8709_rr(struct ite_dev *dev, int index)
+{
+	/* just wait in case the previous access was a write */
+	it8709_wait(dev);
+	it8709_wm(dev, index, IT8709_REG_IDX);
+	it8709_wm(dev, IT8709_READ, IT8709_MODE);
+
+	/* wait for the read data to be available */
+	it8709_wait(dev);
+
+	/* return the read value */
+	return it8709_rm(dev, IT8709_REG_VAL);
+}
+
+/* write the value of a CIR register */
+static void it8709_wr(struct ite_dev *dev, u8 val, int index)
+{
+	/* we wait before writing, and not afterwards, since this allows us to
+	 * pipeline the host CPU with the microcontroller */
+	it8709_wait(dev);
+	it8709_wm(dev, val, IT8709_REG_VAL);
+	it8709_wm(dev, index, IT8709_REG_IDX);
+	it8709_wm(dev, IT8709_WRITE, IT8709_MODE);
+}
+
+/* retrieve a bitmask of the current causes for a pending interrupt; this may
+ * be composed of ITE_IRQ_TX_FIFO, ITE_IRQ_RX_FIFO and ITE_IRQ_RX_FIFO_OVERRUN
+ * */
+static int it8709_get_irq_causes(struct ite_dev *dev)
+{
+	u8 iflags;
+	int ret = 0;
+
+	ite_dbg("%s called", __func__);
+
+	/* read the interrupt flags */
+	iflags = it8709_rm(dev, IT8709_IIR);
+
+	if (iflags & IT85_TLDLI)
+		ret |= ITE_IRQ_TX_FIFO;
+	if (iflags & IT85_RDAI)
+		ret |= ITE_IRQ_RX_FIFO;
+	if (iflags & IT85_RFOI)
+		ret |= ITE_IRQ_RX_FIFO_OVERRUN;
+
+	return ret;
+}
+
+/* set the carrier parameters; to be called with the spinlock held */
+static void it8709_set_carrier_params(struct ite_dev *dev, bool high_freq,
+				      bool use_demodulator,
+				      u8 carrier_freq_bits, u8 allowance_bits,
+				      u8 pulse_width_bits)
+{
+	u8 val;
+
+	ite_dbg("%s called", __func__);
+
+	val = (it8709_rr(dev, IT85_C0CFR)
+		     &~(IT85_HCFS | IT85_CFQ)) |
+	    carrier_freq_bits;
+
+	if (high_freq)
+		val |= IT85_HCFS;
+
+	it8709_wr(dev, val, IT85_C0CFR);
+
+	/* program the C0RCR register */
+	val = it8709_rr(dev, IT85_C0RCR)
+		& ~(IT85_RXEND | IT85_RXDCR);
+
+	if (use_demodulator)
+		val |= IT85_RXEND;
+
+	val |= allowance_bits;
+
+	it8709_wr(dev, val, IT85_C0RCR);
+
+	/* program the C0TCR register */
+	val = it8709_rr(dev, IT85_C0TCR) & ~IT85_TXMPW;
+	val |= pulse_width_bits;
+	it8709_wr(dev, val, IT85_C0TCR);
+}
+
+/* read up to buf_size bytes from the RX FIFO; to be called with the spinlock
+ * held */
+static int it8709_get_rx_bytes(struct ite_dev *dev, u8 * buf, int buf_size)
+{
+	int fifo, read = 0;
+
+	ite_dbg("%s called", __func__);
+
+	/* read how many bytes are still in the FIFO */
+	fifo = it8709_rm(dev, IT8709_RFSR) & IT85_RXFBC;
+
+	while (fifo > 0 && buf_size > 0) {
+		*(buf++) = it8709_rm(dev, IT8709_FIFO + read);
+		fifo--;
+		read++;
+		buf_size--;
+	}
+
+	/* 'clear' the FIFO by setting the writing index to 0; this is
+	 * completely bound to be racy, but we can't help it, since it's a
+	 * limitation of the protocol */
+	it8709_wm(dev, 0, IT8709_RFSR);
+
+	return read;
+}
+
+/* return how many bytes are still in the FIFO; this will be called
+ * with the device spinlock NOT HELD while waiting for the TX FIFO to get
+ * empty; let's expect this won't be a problem */
+static int it8709_get_tx_used_slots(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	return it8709_rr(dev, IT85_C0TFSR) & IT85_TXFBC;
+}
+
+/* put a byte to the TX fifo; this should be called with the spinlock held */
+static void it8709_put_tx_byte(struct ite_dev *dev, u8 value)
+{
+	it8709_wr(dev, value, IT85_C0DR);
+}
+
+/* idle the receiver so that we won't receive samples until another
+  pulse is detected; this must be called with the device spinlock held */
+static void it8709_idle_rx(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	/* disable streaming by clearing RXACT writing it as 1 */
+	it8709_wr(dev, it8709_rr(dev, IT85_C0RCR) | IT85_RXACT,
+			    IT85_C0RCR);
+
+	/* clear the FIFO */
+	it8709_wr(dev, it8709_rr(dev, IT85_C0MSTCR) | IT85_FIFOCLR,
+			    IT85_C0MSTCR);
+}
+
+/* disable the receiver; this must be called with the device spinlock held */
+static void it8709_disable_rx(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	/* disable the receiver interrupts */
+	it8709_wr(dev, it8709_rr(dev, IT85_C0IER) &
+			    ~(IT85_RDAIE | IT85_RFOIE),
+			    IT85_C0IER);
+
+	/* disable the receiver */
+	it8709_wr(dev, it8709_rr(dev, IT85_C0RCR) & ~IT85_RXEN,
+			    IT85_C0RCR);
+
+	/* clear the FIFO and RXACT (actually RXACT should have been cleared
+	 * in the previous it8709_wr(dev, ) call) */
+	it8709_idle_rx(dev);
+}
+
+/* enable the receiver; this must be called with the device spinlock held */
+static void it8709_enable_rx(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	/* enable the receiver by setting RXEN */
+	it8709_wr(dev, it8709_rr(dev, IT85_C0RCR) | IT85_RXEN,
+			    IT85_C0RCR);
+
+	/* just prepare it to idle for the next reception */
+	it8709_idle_rx(dev);
+
+	/* enable the receiver interrupts and master enable flag */
+	it8709_wr(dev, it8709_rr(dev, IT85_C0IER)
+			    |IT85_RDAIE | IT85_RFOIE | IT85_IEC,
+			    IT85_C0IER);
+}
+
+/* disable the transmitter interrupt; this must be called with the device
+ * spinlock held */
+static void it8709_disable_tx_interrupt(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	/* disable the transmitter interrupts */
+	it8709_wr(dev, it8709_rr(dev, IT85_C0IER) & ~IT85_TLDLIE,
+			    IT85_C0IER);
+}
+
+/* enable the transmitter interrupt; this must be called with the device
+ * spinlock held */
+static void it8709_enable_tx_interrupt(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	/* enable the transmitter interrupts and master enable flag */
+	it8709_wr(dev, it8709_rr(dev, IT85_C0IER)
+			    |IT85_TLDLIE | IT85_IEC,
+			    IT85_C0IER);
+}
+
+/* disable the device; this must be called with the device spinlock held */
+static void it8709_disable(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	/* clear out all interrupt enable flags */
+	it8709_wr(dev, it8709_rr(dev, IT85_C0IER) &
+			~(IT85_IEC | IT85_RFOIE | IT85_RDAIE | IT85_TLDLIE),
+		  IT85_C0IER);
+
+	/* disable the receiver */
+	it8709_disable_rx(dev);
+
+	/* erase the FIFO */
+	it8709_wr(dev, IT85_FIFOCLR | it8709_rr(dev, IT85_C0MSTCR),
+			    IT85_C0MSTCR);
+}
+
+/* initialize the hardware */
+static void it8709_init_hardware(struct ite_dev *dev)
+{
+	ite_dbg("%s called", __func__);
+
+	/* disable all the interrupts */
+	it8709_wr(dev, it8709_rr(dev, IT85_C0IER) &
+			~(IT85_IEC | IT85_RFOIE | IT85_RDAIE | IT85_TLDLIE),
+		  IT85_C0IER);
+
+	/* program the baud rate divisor */
+	it8709_wr(dev, ITE_BAUDRATE_DIVISOR & 0xff, IT85_C0BDLR);
+	it8709_wr(dev, (ITE_BAUDRATE_DIVISOR >> 8) & 0xff,
+			IT85_C0BDHR);
+
+	/* program the C0MSTCR register defaults */
+	it8709_wr(dev, (it8709_rr(dev, IT85_C0MSTCR) &
+			~(IT85_ILSEL | IT85_ILE | IT85_FIFOTL
+			  | IT85_FIFOCLR | IT85_RESET)) | IT85_FIFOTL_DEFAULT,
+		  IT85_C0MSTCR);
+
+	/* program the C0RCR register defaults */
+	it8709_wr(dev, (it8709_rr(dev, IT85_C0RCR) &
+			~(IT85_RXEN | IT85_RDWOS | IT85_RXEND | IT85_RXACT
+			  | IT85_RXDCR)) | ITE_RXDCR_DEFAULT,
+		  IT85_C0RCR);
+
+	/* program the C0TCR register defaults */
+	it8709_wr(dev, (it8709_rr(dev, IT85_C0TCR) & ~(IT85_TXMPM | IT85_TXMPW))
+			| IT85_TXRLE | IT85_TXENDF | IT85_TXMPM_DEFAULT
+			| IT85_TXMPW_DEFAULT,
+		  IT85_C0TCR);
+
+	/* program the carrier parameters */
+	ite_set_carrier_params(dev);
+}
+
+
+/* generic hardware setup/teardown code */
+
+/* activate the device for use */
+static int ite_open(struct rc_dev *rcdev)
+{
+	struct ite_dev *dev = rcdev->priv;
+	unsigned long flags;
+
+	ite_dbg("%s called", __func__);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->in_use = true;
+
+	/* enable the receiver */
+	dev->params.enable_rx(dev);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+/* deactivate the device for use */
+static void ite_close(struct rc_dev *rcdev)
+{
+	struct ite_dev *dev = rcdev->priv;
+	unsigned long flags;
+
+	ite_dbg("%s called", __func__);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->in_use = false;
+
+	/* wait for any transmission to end */
+	spin_unlock_irqrestore(&dev->lock, flags);
+	wait_event_interruptible(dev->tx_ended, !dev->transmitting);
+	spin_lock_irqsave(&dev->lock, flags);
+
+	dev->params.disable(dev);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/* supported models and their parameters */
+static const struct ite_dev_params ite_dev_descs[] = {
+	{	/* 0: ITE8704 */
+	       .model = "ITE8704 CIR transceiver",
+	       .io_region_size = IT87_IOREG_LENGTH,
+	       .io_rsrc_no = 0,
+	       .hw_tx_capable = true,
+	       .sample_period = (u32) (1000000000ULL / 115200),
+	       .tx_carrier_freq = 38000,
+	       .tx_duty_cycle = 33,
+	       .rx_low_carrier_freq = 0,
+	       .rx_high_carrier_freq = 0,
+
+		/* operations */
+	       .get_irq_causes = it87_get_irq_causes,
+	       .enable_rx = it87_enable_rx,
+	       .idle_rx = it87_idle_rx,
+	       .disable_rx = it87_idle_rx,
+	       .get_rx_bytes = it87_get_rx_bytes,
+	       .enable_tx_interrupt = it87_enable_tx_interrupt,
+	       .disable_tx_interrupt = it87_disable_tx_interrupt,
+	       .get_tx_used_slots = it87_get_tx_used_slots,
+	       .put_tx_byte = it87_put_tx_byte,
+	       .disable = it87_disable,
+	       .init_hardware = it87_init_hardware,
+	       .set_carrier_params = it87_set_carrier_params,
+	       },
+	{	/* 1: ITE8713 */
+	       .model = "ITE8713 CIR transceiver",
+	       .io_region_size = IT87_IOREG_LENGTH,
+	       .io_rsrc_no = 0,
+	       .hw_tx_capable = true,
+	       .sample_period = (u32) (1000000000ULL / 115200),
+	       .tx_carrier_freq = 38000,
+	       .tx_duty_cycle = 33,
+	       .rx_low_carrier_freq = 0,
+	       .rx_high_carrier_freq = 0,
+
+		/* operations */
+	       .get_irq_causes = it87_get_irq_causes,
+	       .enable_rx = it87_enable_rx,
+	       .idle_rx = it87_idle_rx,
+	       .disable_rx = it87_idle_rx,
+	       .get_rx_bytes = it87_get_rx_bytes,
+	       .enable_tx_interrupt = it87_enable_tx_interrupt,
+	       .disable_tx_interrupt = it87_disable_tx_interrupt,
+	       .get_tx_used_slots = it87_get_tx_used_slots,
+	       .put_tx_byte = it87_put_tx_byte,
+	       .disable = it87_disable,
+	       .init_hardware = it87_init_hardware,
+	       .set_carrier_params = it87_set_carrier_params,
+	       },
+	{	/* 2: ITE8708 */
+	       .model = "ITE8708 CIR transceiver",
+	       .io_region_size = IT8708_IOREG_LENGTH,
+	       .io_rsrc_no = 0,
+	       .hw_tx_capable = true,
+	       .sample_period = (u32) (1000000000ULL / 115200),
+	       .tx_carrier_freq = 38000,
+	       .tx_duty_cycle = 33,
+	       .rx_low_carrier_freq = 0,
+	       .rx_high_carrier_freq = 0,
+
+		/* operations */
+	       .get_irq_causes = it8708_get_irq_causes,
+	       .enable_rx = it8708_enable_rx,
+	       .idle_rx = it8708_idle_rx,
+	       .disable_rx = it8708_idle_rx,
+	       .get_rx_bytes = it8708_get_rx_bytes,
+	       .enable_tx_interrupt = it8708_enable_tx_interrupt,
+	       .disable_tx_interrupt =
+	       it8708_disable_tx_interrupt,
+	       .get_tx_used_slots = it8708_get_tx_used_slots,
+	       .put_tx_byte = it8708_put_tx_byte,
+	       .disable = it8708_disable,
+	       .init_hardware = it8708_init_hardware,
+	       .set_carrier_params = it8708_set_carrier_params,
+	       },
+	{	/* 3: ITE8709 */
+	       .model = "ITE8709 CIR transceiver",
+	       .io_region_size = IT8709_IOREG_LENGTH,
+	       .io_rsrc_no = 2,
+	       .hw_tx_capable = true,
+	       .sample_period = (u32) (1000000000ULL / 115200),
+	       .tx_carrier_freq = 38000,
+	       .tx_duty_cycle = 33,
+	       .rx_low_carrier_freq = 0,
+	       .rx_high_carrier_freq = 0,
+
+		/* operations */
+	       .get_irq_causes = it8709_get_irq_causes,
+	       .enable_rx = it8709_enable_rx,
+	       .idle_rx = it8709_idle_rx,
+	       .disable_rx = it8709_idle_rx,
+	       .get_rx_bytes = it8709_get_rx_bytes,
+	       .enable_tx_interrupt = it8709_enable_tx_interrupt,
+	       .disable_tx_interrupt =
+	       it8709_disable_tx_interrupt,
+	       .get_tx_used_slots = it8709_get_tx_used_slots,
+	       .put_tx_byte = it8709_put_tx_byte,
+	       .disable = it8709_disable,
+	       .init_hardware = it8709_init_hardware,
+	       .set_carrier_params = it8709_set_carrier_params,
+	       },
+};
+
+static const struct pnp_device_id ite_ids[] = {
+	{"ITE8704", 0},		/* Default model */
+	{"ITE8713", 1},		/* CIR found in EEEBox 1501U */
+	{"ITE8708", 2},		/* Bridged IT8512 */
+	{"ITE8709", 3},		/* SRAM-Bridged IT8512 */
+	{"", 0},
+};
+
+/* allocate memory, probe hardware, and initialize everything */
+static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
+		     *dev_id)
+{
+	const struct ite_dev_params *dev_desc = NULL;
+	struct ite_dev *itdev = NULL;
+	struct rc_dev *rdev = NULL;
+	int ret = -ENOMEM;
+	int model_no;
+	int io_rsrc_no;
+
+	ite_dbg("%s called", __func__);
+
+	itdev = kzalloc(sizeof(struct ite_dev), GFP_KERNEL);
+	if (!itdev)
+		return ret;
+
+	/* input device for IR remote (and tx) */
+	rdev = rc_allocate_device();
+	if (!rdev)
+		goto exit_free_dev_rdev;
+	itdev->rdev = rdev;
+
+	ret = -ENODEV;
+
+	/* get the model number */
+	model_no = (int)dev_id->driver_data;
+	ite_pr(KERN_NOTICE, "Auto-detected model: %s\n",
+		ite_dev_descs[model_no].model);
+
+	if (model_number >= 0 && model_number < ARRAY_SIZE(ite_dev_descs)) {
+		model_no = model_number;
+		ite_pr(KERN_NOTICE, "The model has been fixed by a module "
+			"parameter.");
+	}
+
+	ite_pr(KERN_NOTICE, "Using model: %s\n", ite_dev_descs[model_no].model);
+
+	/* get the description for the device */
+	dev_desc = &ite_dev_descs[model_no];
+	io_rsrc_no = dev_desc->io_rsrc_no;
+
+	/* validate pnp resources */
+	if (!pnp_port_valid(pdev, io_rsrc_no) ||
+	    pnp_port_len(pdev, io_rsrc_no) != dev_desc->io_region_size) {
+		dev_err(&pdev->dev, "IR PNP Port not valid!\n");
+		goto exit_free_dev_rdev;
+	}
+
+	if (!pnp_irq_valid(pdev, 0)) {
+		dev_err(&pdev->dev, "PNP IRQ not valid!\n");
+		goto exit_free_dev_rdev;
+	}
+
+	/* store resource values */
+	itdev->cir_addr = pnp_port_start(pdev, io_rsrc_no);
+	itdev->cir_irq = pnp_irq(pdev, 0);
+
+	/* initialize spinlocks */
+	spin_lock_init(&itdev->lock);
+
+	/* initialize raw event */
+	init_ir_raw_event(&itdev->rawir);
+
+	/* set driver data into the pnp device */
+	pnp_set_drvdata(pdev, itdev);
+	itdev->pdev = pdev;
+
+	/* initialize waitqueues for transmission */
+	init_waitqueue_head(&itdev->tx_queue);
+	init_waitqueue_head(&itdev->tx_ended);
+
+	/* copy model-specific parameters */
+	itdev->params = *dev_desc;
+
+	/* apply any overrides */
+	if (sample_period > 0)
+		itdev->params.sample_period = sample_period;
+
+	if (tx_carrier_freq > 0)
+		itdev->params.tx_carrier_freq = tx_carrier_freq;
+
+	if (tx_duty_cycle > 0 && tx_duty_cycle <= 100)
+		itdev->params.tx_duty_cycle = tx_duty_cycle;
+
+	if (rx_low_carrier_freq > 0)
+		itdev->params.rx_low_carrier_freq = rx_low_carrier_freq;
+
+	if (rx_high_carrier_freq > 0)
+		itdev->params.rx_high_carrier_freq = rx_high_carrier_freq;
+
+	/* print out parameters */
+	ite_pr(KERN_NOTICE, "TX-capable: %d\n", (int)
+			 itdev->params.hw_tx_capable);
+	ite_pr(KERN_NOTICE, "Sample period (ns): %ld\n", (long)
+		     itdev->params.sample_period);
+	ite_pr(KERN_NOTICE, "TX carrier frequency (Hz): %d\n", (int)
+		     itdev->params.tx_carrier_freq);
+	ite_pr(KERN_NOTICE, "TX duty cycle (%%): %d\n", (int)
+		     itdev->params.tx_duty_cycle);
+	ite_pr(KERN_NOTICE, "RX low carrier frequency (Hz): %d\n", (int)
+		     itdev->params.rx_low_carrier_freq);
+	ite_pr(KERN_NOTICE, "RX high carrier frequency (Hz): %d\n", (int)
+		     itdev->params.rx_high_carrier_freq);
+
+	/* set up hardware initial state */
+	itdev->params.init_hardware(itdev);
+
+	/* set up ir-core props */
+	rdev->priv = itdev;
+	rdev->driver_type = RC_DRIVER_IR_RAW;
+	rdev->allowed_protocols = RC_BIT_ALL;
+	rdev->open = ite_open;
+	rdev->close = ite_close;
+	rdev->s_idle = ite_s_idle;
+	rdev->s_rx_carrier_range = ite_set_rx_carrier_range;
+	rdev->min_timeout = ITE_MIN_IDLE_TIMEOUT;
+	rdev->max_timeout = ITE_MAX_IDLE_TIMEOUT;
+	rdev->timeout = ITE_IDLE_TIMEOUT;
+	rdev->rx_resolution = ITE_BAUDRATE_DIVISOR *
+				itdev->params.sample_period;
+	rdev->tx_resolution = ITE_BAUDRATE_DIVISOR *
+				itdev->params.sample_period;
+
+	/* set up transmitter related values if needed */
+	if (itdev->params.hw_tx_capable) {
+		rdev->tx_ir = ite_tx_ir;
+		rdev->s_tx_carrier = ite_set_tx_carrier;
+		rdev->s_tx_duty_cycle = ite_set_tx_duty_cycle;
+	}
+
+	rdev->input_name = dev_desc->model;
+	rdev->input_id.bustype = BUS_HOST;
+	rdev->input_id.vendor = PCI_VENDOR_ID_ITE;
+	rdev->input_id.product = 0;
+	rdev->input_id.version = 0;
+	rdev->driver_name = ITE_DRIVER_NAME;
+	rdev->map_name = RC_MAP_RC6_MCE;
+
+	ret = rc_register_device(rdev);
+	if (ret)
+		goto exit_free_dev_rdev;
+
+	ret = -EBUSY;
+	/* now claim resources */
+	if (!request_region(itdev->cir_addr,
+				dev_desc->io_region_size, ITE_DRIVER_NAME))
+		goto exit_unregister_device;
+
+	if (request_irq(itdev->cir_irq, ite_cir_isr, IRQF_SHARED,
+			ITE_DRIVER_NAME, (void *)itdev))
+		goto exit_release_cir_addr;
+
+	ite_pr(KERN_NOTICE, "driver has been successfully loaded\n");
+
+	return 0;
+
+exit_release_cir_addr:
+	release_region(itdev->cir_addr, itdev->params.io_region_size);
+exit_unregister_device:
+	rc_unregister_device(rdev);
+	rdev = NULL;
+exit_free_dev_rdev:
+	rc_free_device(rdev);
+	kfree(itdev);
+
+	return ret;
+}
+
+static void ite_remove(struct pnp_dev *pdev)
+{
+	struct ite_dev *dev = pnp_get_drvdata(pdev);
+	unsigned long flags;
+
+	ite_dbg("%s called", __func__);
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	/* disable hardware */
+	dev->params.disable(dev);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	/* free resources */
+	free_irq(dev->cir_irq, dev);
+	release_region(dev->cir_addr, dev->params.io_region_size);
+
+	rc_unregister_device(dev->rdev);
+
+	kfree(dev);
+}
+
+static int ite_suspend(struct pnp_dev *pdev, pm_message_t state)
+{
+	struct ite_dev *dev = pnp_get_drvdata(pdev);
+	unsigned long flags;
+
+	ite_dbg("%s called", __func__);
+
+	/* wait for any transmission to end */
+	wait_event_interruptible(dev->tx_ended, !dev->transmitting);
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	/* disable all interrupts */
+	dev->params.disable(dev);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+static int ite_resume(struct pnp_dev *pdev)
+{
+	struct ite_dev *dev = pnp_get_drvdata(pdev);
+	unsigned long flags;
+
+	ite_dbg("%s called", __func__);
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	/* reinitialize hardware config registers */
+	dev->params.init_hardware(dev);
+	/* enable the receiver */
+	dev->params.enable_rx(dev);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return 0;
+}
+
+static void ite_shutdown(struct pnp_dev *pdev)
+{
+	struct ite_dev *dev = pnp_get_drvdata(pdev);
+	unsigned long flags;
+
+	ite_dbg("%s called", __func__);
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	/* disable all interrupts */
+	dev->params.disable(dev);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static struct pnp_driver ite_driver = {
+	.name		= ITE_DRIVER_NAME,
+	.id_table	= ite_ids,
+	.probe		= ite_probe,
+	.remove		= ite_remove,
+	.suspend	= ite_suspend,
+	.resume		= ite_resume,
+	.shutdown	= ite_shutdown,
+};
+
+MODULE_DEVICE_TABLE(pnp, ite_ids);
+MODULE_DESCRIPTION("ITE Tech Inc. IT8712F/ITE8512F CIR driver");
+
+MODULE_AUTHOR("Juan J. Garcia de Soria <skandalfo@gmail.com>");
+MODULE_LICENSE("GPL");
+
+module_pnp_driver(ite_driver);
diff --git a/drivers/media/rc/ite-cir.h b/drivers/media/rc/ite-cir.h
new file mode 100644
index 0000000..aa899a0
--- /dev/null
+++ b/drivers/media/rc/ite-cir.h
@@ -0,0 +1,484 @@
+/*
+ * Driver for ITE Tech Inc. IT8712F/IT8512F CIR
+ *
+ * Copyright (C) 2010 Juan Jesús García de Soria <skandalfo@gmail.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.
+ */
+
+/* platform driver name to register */
+#define ITE_DRIVER_NAME "ite-cir"
+
+/* logging macros */
+#define ite_pr(level, text, ...) \
+	printk(level KBUILD_MODNAME ": " text, ## __VA_ARGS__)
+#define ite_dbg(text, ...) do { \
+	if (debug) \
+		printk(KERN_DEBUG \
+			KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__); \
+} while (0)
+
+#define ite_dbg_verbose(text, ...) do {\
+	if (debug > 1) \
+		printk(KERN_DEBUG \
+			KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__); \
+} while (0)
+
+/* FIFO sizes */
+#define ITE_TX_FIFO_LEN 32
+#define ITE_RX_FIFO_LEN 32
+
+/* interrupt types */
+#define ITE_IRQ_TX_FIFO        1
+#define ITE_IRQ_RX_FIFO        2
+#define ITE_IRQ_RX_FIFO_OVERRUN    4
+
+/* forward declaration */
+struct ite_dev;
+
+/* struct for storing the parameters of different recognized devices */
+struct ite_dev_params {
+	/* model of the device */
+	const char *model;
+
+	/* size of the I/O region */
+	int io_region_size;
+
+	/* IR pnp I/O resource number */
+	int io_rsrc_no;
+
+	/* true if the hardware supports transmission */
+	bool hw_tx_capable;
+
+	/* base sampling period, in ns */
+	u32 sample_period;
+
+	/* rx low carrier frequency, in Hz, 0 means no demodulation */
+	unsigned int rx_low_carrier_freq;
+
+	/* tx high carrier frequency, in Hz, 0 means no demodulation */
+	unsigned int rx_high_carrier_freq;
+
+	/* tx carrier frequency, in Hz */
+	unsigned int tx_carrier_freq;
+
+	/* duty cycle, 0-100 */
+	int tx_duty_cycle;
+
+	/* hw-specific operation function pointers; most of these must be
+	 * called while holding the spin lock, except for the TX FIFO length
+	 * one */
+	/* get pending interrupt causes */
+	int (*get_irq_causes) (struct ite_dev *dev);
+
+	/* enable rx */
+	void (*enable_rx) (struct ite_dev *dev);
+
+	/* make rx enter the idle state; keep listening for a pulse, but stop
+	 * streaming space bytes */
+	void (*idle_rx) (struct ite_dev *dev);
+
+	/* disable rx completely */
+	void (*disable_rx) (struct ite_dev *dev);
+
+	/* read bytes from RX FIFO; return read count */
+	int (*get_rx_bytes) (struct ite_dev *dev, u8 *buf, int buf_size);
+
+	/* enable tx FIFO space available interrupt */
+	void (*enable_tx_interrupt) (struct ite_dev *dev);
+
+	/* disable tx FIFO space available interrupt */
+	void (*disable_tx_interrupt) (struct ite_dev *dev);
+
+	/* get number of full TX FIFO slots */
+	int (*get_tx_used_slots) (struct ite_dev *dev);
+
+	/* put a byte to the TX FIFO */
+	void (*put_tx_byte) (struct ite_dev *dev, u8 value);
+
+	/* disable hardware completely */
+	void (*disable) (struct ite_dev *dev);
+
+	/* initialize the hardware */
+	void (*init_hardware) (struct ite_dev *dev);
+
+	/* set the carrier parameters */
+	void (*set_carrier_params) (struct ite_dev *dev, bool high_freq,
+				    bool use_demodulator, u8 carrier_freq_bits,
+				    u8 allowance_bits, u8 pulse_width_bits);
+};
+
+/* ITE CIR device structure */
+struct ite_dev {
+	struct pnp_dev *pdev;
+	struct rc_dev *rdev;
+	struct ir_raw_event rawir;
+
+	/* sync data */
+	spinlock_t lock;
+	bool in_use, transmitting;
+
+	/* transmit support */
+	int tx_fifo_allowance;
+	wait_queue_head_t tx_queue, tx_ended;
+
+	/* hardware I/O settings */
+	unsigned long cir_addr;
+	int cir_irq;
+
+	/* overridable copy of model parameters */
+	struct ite_dev_params params;
+};
+
+/* common values for all kinds of hardware */
+
+/* baud rate divisor default */
+#define ITE_BAUDRATE_DIVISOR		1
+
+/* low-speed carrier frequency limits (Hz) */
+#define ITE_LCF_MIN_CARRIER_FREQ	27000
+#define ITE_LCF_MAX_CARRIER_FREQ	58000
+
+/* high-speed carrier frequency limits (Hz) */
+#define ITE_HCF_MIN_CARRIER_FREQ	400000
+#define ITE_HCF_MAX_CARRIER_FREQ	500000
+
+/* default carrier freq for when demodulator is off (Hz) */
+#define ITE_DEFAULT_CARRIER_FREQ	38000
+
+/* default idling timeout in ns (0.2 seconds) */
+#define ITE_IDLE_TIMEOUT		200000000UL
+
+/* limit timeout values */
+#define ITE_MIN_IDLE_TIMEOUT		100000000UL
+#define ITE_MAX_IDLE_TIMEOUT		1000000000UL
+
+/* convert bits to us */
+#define ITE_BITS_TO_NS(bits, sample_period) \
+((u32) ((bits) * ITE_BAUDRATE_DIVISOR * sample_period))
+
+/*
+ * n in RDCR produces a tolerance of +/- n * 6.25% around the center
+ * carrier frequency...
+ *
+ * From two limit frequencies, L (low) and H (high), we can get both the
+ * center frequency F = (L + H) / 2 and the variation from the center
+ * frequency A = (H - L) / (H + L). We can use this in order to honor the
+ * s_rx_carrier_range() call in ir-core. We'll suppose that any request
+ * setting L=0 means we must shut down the demodulator.
+ */
+#define ITE_RXDCR_PER_10000_STEP 625
+
+/* high speed carrier freq values */
+#define ITE_CFQ_400		0x03
+#define ITE_CFQ_450		0x08
+#define ITE_CFQ_480		0x0b
+#define ITE_CFQ_500		0x0d
+
+/* values for pulse widths */
+#define ITE_TXMPW_A		0x02
+#define ITE_TXMPW_B		0x03
+#define ITE_TXMPW_C		0x04
+#define ITE_TXMPW_D		0x05
+#define ITE_TXMPW_E		0x06
+
+/* values for demodulator carrier range allowance */
+#define ITE_RXDCR_DEFAULT	0x01	/* default carrier range */
+#define ITE_RXDCR_MAX		0x07	/* default carrier range */
+
+/* DR TX bits */
+#define ITE_TX_PULSE		0x00
+#define ITE_TX_SPACE		0x80
+#define ITE_TX_MAX_RLE		0x80
+#define ITE_TX_RLE_MASK		0x7f
+
+/*
+ * IT8712F
+ *
+ * hardware data obtained from:
+ *
+ * IT8712F
+ * Environment Control – Low Pin Count Input / Output
+ * (EC - LPC I/O)
+ * Preliminary Specification V0. 81
+ */
+
+/* register offsets */
+#define IT87_DR		0x00	/* data register */
+#define IT87_IER	0x01	/* interrupt enable register */
+#define IT87_RCR	0x02	/* receiver control register */
+#define IT87_TCR1	0x03	/* transmitter control register 1 */
+#define IT87_TCR2	0x04	/* transmitter control register 2 */
+#define IT87_TSR	0x05	/* transmitter status register */
+#define IT87_RSR	0x06	/* receiver status register */
+#define IT87_BDLR	0x05	/* baud rate divisor low byte register */
+#define IT87_BDHR	0x06	/* baud rate divisor high byte register */
+#define IT87_IIR	0x07	/* interrupt identification register */
+
+#define IT87_IOREG_LENGTH 0x08	/* length of register file */
+
+/* IER bits */
+#define IT87_TLDLIE	0x01	/* transmitter low data interrupt enable */
+#define IT87_RDAIE	0x02	/* receiver data available interrupt enable */
+#define IT87_RFOIE	0x04	/* receiver FIFO overrun interrupt enable */
+#define IT87_IEC	0x08	/* interrupt enable control */
+#define IT87_BR		0x10	/* baud rate register enable */
+#define IT87_RESET	0x20	/* reset */
+
+/* RCR bits */
+#define IT87_RXDCR	0x07	/* receiver demodulation carrier range mask */
+#define IT87_RXACT	0x08	/* receiver active */
+#define IT87_RXEND	0x10	/* receiver demodulation enable */
+#define IT87_RXEN	0x20	/* receiver enable */
+#define IT87_HCFS	0x40	/* high-speed carrier frequency select */
+#define IT87_RDWOS	0x80	/* receiver data without sync */
+
+/* TCR1 bits */
+#define IT87_TXMPM	0x03	/* transmitter modulation pulse mode mask */
+#define IT87_TXMPM_DEFAULT 0x00	/* modulation pulse mode default */
+#define IT87_TXENDF	0x04	/* transmitter deferral */
+#define IT87_TXRLE	0x08	/* transmitter run length enable */
+#define IT87_FIFOTL	0x30	/* FIFO level threshold mask */
+#define IT87_FIFOTL_DEFAULT 0x20	/* FIFO level threshold default
+					 * 0x00 -> 1, 0x10 -> 7, 0x20 -> 17,
+					 * 0x30 -> 25 */
+#define IT87_ILE	0x40	/* internal loopback enable */
+#define IT87_FIFOCLR	0x80	/* FIFO clear bit */
+
+/* TCR2 bits */
+#define IT87_TXMPW	0x07	/* transmitter modulation pulse width mask */
+#define IT87_TXMPW_DEFAULT 0x04	/* default modulation pulse width */
+#define IT87_CFQ	0xf8	/* carrier frequency mask */
+#define IT87_CFQ_SHIFT	3	/* carrier frequency bit shift */
+
+/* TSR bits */
+#define IT87_TXFBC	0x3f	/* transmitter FIFO byte count mask */
+
+/* RSR bits */
+#define IT87_RXFBC	0x3f	/* receiver FIFO byte count mask */
+#define IT87_RXFTO	0x80	/* receiver FIFO time-out */
+
+/* IIR bits */
+#define IT87_IP		0x01	/* interrupt pending */
+#define IT87_II		0x06	/* interrupt identification mask */
+#define IT87_II_NOINT	0x00	/* no interrupt */
+#define IT87_II_TXLDL	0x02	/* transmitter low data level */
+#define IT87_II_RXDS	0x04	/* receiver data stored */
+#define IT87_II_RXFO	0x06	/* receiver FIFO overrun */
+
+/*
+ * IT8512E/F
+ *
+ * Hardware data obtained from:
+ *
+ * IT8512E/F
+ * Embedded Controller
+ * Preliminary Specification V0.4.1
+ *
+ * Note that the CIR registers are not directly available to the host, because
+ * they only are accessible to the integrated microcontroller. Thus, in order
+ * use it, some kind of bridging is required. As the bridging may depend on
+ * the controller firmware in use, we are going to use the PNP ID in order to
+ * determine the strategy and ports available. See after these generic
+ * IT8512E/F register definitions for register definitions for those
+ * strategies.
+ */
+
+/* register offsets */
+#define IT85_C0DR	0x00	/* data register */
+#define IT85_C0MSTCR	0x01	/* master control register */
+#define IT85_C0IER	0x02	/* interrupt enable register */
+#define IT85_C0IIR	0x03	/* interrupt identification register */
+#define IT85_C0CFR	0x04	/* carrier frequency register */
+#define IT85_C0RCR	0x05	/* receiver control register */
+#define IT85_C0TCR	0x06	/* transmitter control register */
+#define IT85_C0SCK	0x07	/* slow clock control register */
+#define IT85_C0BDLR	0x08	/* baud rate divisor low byte register */
+#define IT85_C0BDHR	0x09	/* baud rate divisor high byte register */
+#define IT85_C0TFSR	0x0a	/* transmitter FIFO status register */
+#define IT85_C0RFSR	0x0b	/* receiver FIFO status register */
+#define IT85_C0WCL	0x0d	/* wakeup code length register */
+#define IT85_C0WCR	0x0e	/* wakeup code read/write register */
+#define IT85_C0WPS	0x0f	/* wakeup power control/status register */
+
+#define IT85_IOREG_LENGTH 0x10	/* length of register file */
+
+/* C0MSTCR bits */
+#define IT85_RESET	0x01	/* reset */
+#define IT85_FIFOCLR	0x02	/* FIFO clear bit */
+#define IT85_FIFOTL	0x0c	/* FIFO level threshold mask */
+#define IT85_FIFOTL_DEFAULT 0x08	/* FIFO level threshold default
+					 * 0x00 -> 1, 0x04 -> 7, 0x08 -> 17,
+					 * 0x0c -> 25 */
+#define IT85_ILE	0x10	/* internal loopback enable */
+#define IT85_ILSEL	0x20	/* internal loopback select */
+
+/* C0IER bits */
+#define IT85_TLDLIE	0x01	/* TX low data level interrupt enable */
+#define IT85_RDAIE	0x02	/* RX data available interrupt enable */
+#define IT85_RFOIE	0x04	/* RX FIFO overrun interrupt enable */
+#define IT85_IEC	0x80	/* interrupt enable function control */
+
+/* C0IIR bits */
+#define IT85_TLDLI	0x01	/* transmitter low data level interrupt */
+#define IT85_RDAI	0x02	/* receiver data available interrupt */
+#define IT85_RFOI	0x04	/* receiver FIFO overrun interrupt */
+#define IT85_NIP	0x80	/* no interrupt pending */
+
+/* C0CFR bits */
+#define IT85_CFQ	0x1f	/* carrier frequency mask */
+#define IT85_HCFS	0x20	/* high speed carrier frequency select */
+
+/* C0RCR bits */
+#define IT85_RXDCR	0x07	/* receiver demodulation carrier range mask */
+#define IT85_RXACT	0x08	/* receiver active */
+#define IT85_RXEND	0x10	/* receiver demodulation enable */
+#define IT85_RDWOS	0x20	/* receiver data without sync */
+#define IT85_RXEN	0x80	/* receiver enable */
+
+/* C0TCR bits */
+#define IT85_TXMPW	0x07	/* transmitter modulation pulse width mask */
+#define IT85_TXMPW_DEFAULT 0x04	/* default modulation pulse width */
+#define IT85_TXMPM	0x18	/* transmitter modulation pulse mode mask */
+#define IT85_TXMPM_DEFAULT 0x00	/* modulation pulse mode default */
+#define IT85_TXENDF	0x20	/* transmitter deferral */
+#define IT85_TXRLE	0x40	/* transmitter run length enable */
+
+/* C0SCK bits */
+#define IT85_SCKS	0x01	/* slow clock select */
+#define IT85_TXDCKG	0x02	/* TXD clock gating */
+#define IT85_DLL1P8E	0x04	/* DLL 1.8432M enable */
+#define IT85_DLLTE	0x08	/* DLL test enable */
+#define IT85_BRCM	0x70	/* baud rate count mode */
+#define IT85_DLLOCK	0x80	/* DLL lock */
+
+/* C0TFSR bits */
+#define IT85_TXFBC	0x3f	/* transmitter FIFO count mask */
+
+/* C0RFSR bits */
+#define IT85_RXFBC	0x3f	/* receiver FIFO count mask */
+#define IT85_RXFTO	0x80	/* receiver FIFO time-out */
+
+/* C0WCL bits */
+#define IT85_WCL	0x3f	/* wakeup code length mask */
+
+/* C0WPS bits */
+#define IT85_CIRPOSIE	0x01	/* power on/off status interrupt enable */
+#define IT85_CIRPOIS	0x02	/* power on/off interrupt status */
+#define IT85_CIRPOII	0x04	/* power on/off interrupt identification */
+#define IT85_RCRST	0x10	/* wakeup code reading counter reset bit */
+#define IT85_WCRST	0x20	/* wakeup code writing counter reset bit */
+
+/*
+ * ITE8708
+ *
+ * Hardware data obtained from hacked driver for IT8512 in this forum post:
+ *
+ *  http://ubuntuforums.org/showthread.php?t=1028640
+ *
+ * Although there's no official documentation for that driver, analysis would
+ * suggest that it maps the 16 registers of IT8512 onto two 8-register banks,
+ * selectable by a single bank-select bit that's mapped onto both banks. The
+ * IT8512 registers are mapped in a different order, so that the first bank
+ * maps the ones that are used more often, and two registers that share a
+ * reserved high-order bit are placed at the same offset in both banks in
+ * order to reuse the reserved bit as the bank select bit.
+ */
+
+/* register offsets */
+
+/* mapped onto both banks */
+#define IT8708_BANKSEL	0x07	/* bank select register */
+#define IT8708_HRAE	0x80	/* high registers access enable */
+
+/* mapped onto the low bank */
+#define IT8708_C0DR	0x00	/* data register */
+#define IT8708_C0MSTCR	0x01	/* master control register */
+#define IT8708_C0IER	0x02	/* interrupt enable register */
+#define IT8708_C0IIR	0x03	/* interrupt identification register */
+#define IT8708_C0RFSR	0x04	/* receiver FIFO status register */
+#define IT8708_C0RCR	0x05	/* receiver control register */
+#define IT8708_C0TFSR	0x06	/* transmitter FIFO status register */
+#define IT8708_C0TCR	0x07	/* transmitter control register */
+
+/* mapped onto the high bank */
+#define IT8708_C0BDLR	0x01	/* baud rate divisor low byte register */
+#define IT8708_C0BDHR	0x02	/* baud rate divisor high byte register */
+#define IT8708_C0CFR	0x04	/* carrier frequency register */
+
+/* registers whose bank mapping we don't know, since they weren't being used
+ * in the hacked driver... most probably they belong to the high bank too,
+ * since they fit in the holes the other registers leave */
+#define IT8708_C0SCK	0x03	/* slow clock control register */
+#define IT8708_C0WCL	0x05	/* wakeup code length register */
+#define IT8708_C0WCR	0x06	/* wakeup code read/write register */
+#define IT8708_C0WPS	0x07	/* wakeup power control/status register */
+
+#define IT8708_IOREG_LENGTH 0x08	/* length of register file */
+
+/* two more registers that are defined in the hacked driver, but can't be
+ * found in the data sheets; no idea what they are or how they are accessed,
+ * since the hacked driver doesn't seem to use them */
+#define IT8708_CSCRR	0x00
+#define IT8708_CGPINTR	0x01
+
+/* CSCRR bits */
+#define IT8708_CSCRR_SCRB 0x3f
+#define IT8708_CSCRR_PM	0x80
+
+/* CGPINTR bits */
+#define IT8708_CGPINT	0x01
+
+/*
+ * ITE8709
+ *
+ * Hardware interfacing data obtained from the original lirc_ite8709 driver.
+ * Verbatim from its sources:
+ *
+ * The ITE8709 device seems to be the combination of IT8512 superIO chip and
+ * a specific firmware running on the IT8512's embedded micro-controller.
+ * In addition of the embedded micro-controller, the IT8512 chip contains a
+ * CIR module and several other modules. A few modules are directly accessible
+ * by the host CPU, but most of them are only accessible by the
+ * micro-controller. The CIR module is only accessible by the
+ * micro-controller.
+ *
+ * The battery-backed SRAM module is accessible by the host CPU and the
+ * micro-controller. So one of the MC's firmware role is to act as a bridge
+ * between the host CPU and the CIR module. The firmware implements a kind of
+ * communication protocol using the SRAM module as a shared memory. The IT8512
+ * specification is publicly available on ITE's web site, but the
+ * communication protocol is not, so it was reverse-engineered.
+ */
+
+/* register offsets */
+#define IT8709_RAM_IDX	0x00	/* index into the SRAM module bytes */
+#define IT8709_RAM_VAL	0x01	/* read/write data to the indexed byte */
+
+#define IT8709_IOREG_LENGTH 0x02	/* length of register file */
+
+/* register offsets inside the SRAM module */
+#define IT8709_MODE	0x1a	/* request/ack byte */
+#define IT8709_REG_IDX	0x1b	/* index of the CIR register to access */
+#define IT8709_REG_VAL	0x1c	/* value read/to be written */
+#define IT8709_IIR	0x1e	/* interrupt identification register */
+#define IT8709_RFSR	0x1f	/* receiver FIFO status register */
+#define IT8709_FIFO	0x20	/* start of in RAM RX FIFO copy */
+
+/* MODE values */
+#define IT8709_IDLE	0x00
+#define IT8709_WRITE	0x01
+#define IT8709_READ	0x02
diff --git a/drivers/media/rc/keymaps/Kconfig b/drivers/media/rc/keymaps/Kconfig
new file mode 100644
index 0000000..767423b
--- /dev/null
+++ b/drivers/media/rc/keymaps/Kconfig
@@ -0,0 +1,15 @@
+config RC_MAP
+	tristate "Compile Remote Controller keymap modules"
+	depends on RC_CORE
+	default y
+
+	---help---
+	   This option enables the compilation of lots of Remote
+	   Controller tables. They are short tables, but if you
+	   don't use a remote controller, or prefer to load the
+	   tables on userspace, you should disable it.
+
+	   The ir-keytable program, available at v4l-utils package
+	   provide the tool and the same RC maps for load from
+	   userspace. Its available at
+		http://git.linuxtv.org/cgit.cgi/v4l-utils.git/
diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile
new file mode 100644
index 0000000..fbbd3bb
--- /dev/null
+++ b/drivers/media/rc/keymaps/Makefile
@@ -0,0 +1,107 @@
+obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
+			rc-alink-dtu-m.o \
+			rc-anysee.o \
+			rc-apac-viewcomp.o \
+			rc-asus-pc39.o \
+			rc-asus-ps3-100.o \
+			rc-ati-tv-wonder-hd-600.o \
+			rc-ati-x10.o \
+			rc-avermedia-a16d.o \
+			rc-avermedia.o \
+			rc-avermedia-cardbus.o \
+			rc-avermedia-dvbt.o \
+			rc-avermedia-m135a.o \
+			rc-avermedia-m733a-rm-k6.o \
+			rc-avermedia-rm-ks.o \
+			rc-avertv-303.o \
+			rc-azurewave-ad-tu700.o \
+			rc-behold.o \
+			rc-behold-columbus.o \
+			rc-budget-ci-old.o \
+			rc-cinergy-1400.o \
+			rc-cinergy.o \
+			rc-delock-61959.o \
+			rc-dib0700-nec.o \
+			rc-dib0700-rc5.o \
+			rc-digitalnow-tinytwin.o \
+			rc-digittrade.o \
+			rc-dm1105-nec.o \
+			rc-dntv-live-dvb-t.o \
+			rc-dntv-live-dvbt-pro.o \
+			rc-dvbsky.o \
+			rc-em-terratec.o \
+			rc-encore-enltv2.o \
+			rc-encore-enltv.o \
+			rc-encore-enltv-fm53.o \
+			rc-evga-indtube.o \
+			rc-eztv.o \
+			rc-flydvb.o \
+			rc-flyvideo.o \
+			rc-fusionhdtv-mce.o \
+			rc-gadmei-rm008z.o \
+			rc-genius-tvgo-a11mce.o \
+			rc-gotview7135.o \
+			rc-imon-mce.o \
+			rc-imon-pad.o \
+			rc-iodata-bctv7e.o \
+			rc-it913x-v1.o \
+			rc-it913x-v2.o \
+			rc-kaiomy.o \
+			rc-kworld-315u.o \
+			rc-kworld-pc150u.o \
+			rc-kworld-plus-tv-analog.o \
+			rc-leadtek-y04g0051.o \
+			rc-lirc.o \
+			rc-lme2510.o \
+			rc-manli.o \
+			rc-medion-x10.o \
+			rc-medion-x10-digitainer.o \
+			rc-medion-x10-or2x.o \
+			rc-msi-digivox-ii.o \
+			rc-msi-digivox-iii.o \
+			rc-msi-tvanywhere.o \
+			rc-msi-tvanywhere-plus.o \
+			rc-nebula.o \
+			rc-nec-terratec-cinergy-xs.o \
+			rc-norwood.o \
+			rc-npgtech.o \
+			rc-pctv-sedna.o \
+			rc-pinnacle-color.o \
+			rc-pinnacle-grey.o \
+			rc-pinnacle-pctv-hd.o \
+			rc-pixelview.o \
+			rc-pixelview-mk12.o \
+			rc-pixelview-002t.o \
+			rc-pixelview-new.o \
+			rc-powercolor-real-angel.o \
+			rc-proteus-2309.o \
+			rc-purpletv.o \
+			rc-pv951.o \
+			rc-hauppauge.o \
+			rc-rc6-mce.o \
+			rc-real-audio-220-32-keys.o \
+			rc-reddo.o \
+			rc-snapstream-firefly.o \
+			rc-streamzap.o \
+			rc-tbs-nec.o \
+			rc-technisat-ts35.o \
+			rc-technisat-usb2.o \
+			rc-terratec-cinergy-c-pci.o \
+			rc-terratec-cinergy-s2-hd.o \
+			rc-terratec-cinergy-xs.o \
+			rc-terratec-slim.o \
+			rc-terratec-slim-2.o \
+			rc-tevii-nec.o \
+			rc-tivo.o \
+			rc-total-media-in-hand.o \
+			rc-total-media-in-hand-02.o \
+			rc-trekstor.o \
+			rc-tt-1500.o \
+			rc-twinhan-dtv-cab-ci.o \
+			rc-twinhan1027.o \
+			rc-videomate-m1f.o \
+			rc-videomate-s350.o \
+			rc-videomate-tv-pvr.o \
+			rc-winfast.o \
+			rc-winfast-usbii-deluxe.o \
+			rc-su3000.o
diff --git a/drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c b/drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c
new file mode 100644
index 0000000..01d901f
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-adstech-dvb-t-pci.c
@@ -0,0 +1,90 @@
+/* adstech-dvb-t-pci.h - Keytable for adstech_dvb_t_pci Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* ADS Tech Instant TV DVB-T PCI Remote */
+
+static struct rc_map_table 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_RED },		/* 1 */
+	{ 0x01, KEY_GREEN },		/* 2 */
+	{ 0x00, KEY_YELLOW },		/* 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 },
+};
+
+static struct rc_map_list adstech_dvb_t_pci_map = {
+	.map = {
+		.scan    = adstech_dvb_t_pci,
+		.size    = ARRAY_SIZE(adstech_dvb_t_pci),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_ADSTECH_DVB_T_PCI,
+	}
+};
+
+static int __init init_rc_map_adstech_dvb_t_pci(void)
+{
+	return rc_map_register(&adstech_dvb_t_pci_map);
+}
+
+static void __exit exit_rc_map_adstech_dvb_t_pci(void)
+{
+	rc_map_unregister(&adstech_dvb_t_pci_map);
+}
+
+module_init(init_rc_map_adstech_dvb_t_pci)
+module_exit(exit_rc_map_adstech_dvb_t_pci)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-alink-dtu-m.c b/drivers/media/rc/keymaps/rc-alink-dtu-m.c
new file mode 100644
index 0000000..4e6ade8
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-alink-dtu-m.c
@@ -0,0 +1,69 @@
+/*
+ * A-Link DTU(m) remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* A-Link DTU(m) slim remote, 6 rows, 3 columns. */
+static struct rc_map_table alink_dtu_m[] = {
+	{ 0x0800, KEY_VOLUMEUP },
+	{ 0x0801, KEY_1 },
+	{ 0x0802, KEY_3 },
+	{ 0x0803, KEY_7 },
+	{ 0x0804, KEY_9 },
+	{ 0x0805, KEY_NEW },             /* symbol: PIP */
+	{ 0x0806, KEY_0 },
+	{ 0x0807, KEY_CHANNEL },         /* JUMP */
+	{ 0x080d, KEY_5 },
+	{ 0x080f, KEY_2 },
+	{ 0x0812, KEY_POWER2 },
+	{ 0x0814, KEY_CHANNELUP },
+	{ 0x0816, KEY_VOLUMEDOWN },
+	{ 0x0818, KEY_6 },
+	{ 0x081a, KEY_MUTE },
+	{ 0x081b, KEY_8 },
+	{ 0x081c, KEY_4 },
+	{ 0x081d, KEY_CHANNELDOWN },
+};
+
+static struct rc_map_list alink_dtu_m_map = {
+	.map = {
+		.scan    = alink_dtu_m,
+		.size    = ARRAY_SIZE(alink_dtu_m),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_ALINK_DTU_M,
+	}
+};
+
+static int __init init_rc_map_alink_dtu_m(void)
+{
+	return rc_map_register(&alink_dtu_m_map);
+}
+
+static void __exit exit_rc_map_alink_dtu_m(void)
+{
+	rc_map_unregister(&alink_dtu_m_map);
+}
+
+module_init(init_rc_map_alink_dtu_m)
+module_exit(exit_rc_map_alink_dtu_m)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-anysee.c b/drivers/media/rc/keymaps/rc-anysee.c
new file mode 100644
index 0000000..c735fe1
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-anysee.c
@@ -0,0 +1,94 @@
+/*
+ * Anysee remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table anysee[] = {
+	{ 0x0800, KEY_0 },
+	{ 0x0801, KEY_1 },
+	{ 0x0802, KEY_2 },
+	{ 0x0803, KEY_3 },
+	{ 0x0804, KEY_4 },
+	{ 0x0805, KEY_5 },
+	{ 0x0806, KEY_6 },
+	{ 0x0807, KEY_7 },
+	{ 0x0808, KEY_8 },
+	{ 0x0809, KEY_9 },
+	{ 0x080a, KEY_POWER2 },          /* [red power button] */
+	{ 0x080b, KEY_VIDEO },           /* [*] MODE */
+	{ 0x080c, KEY_CHANNEL },         /* [symbol counterclockwise arrow] */
+	{ 0x080d, KEY_NEXT },            /* [>>|] */
+	{ 0x080e, KEY_MENU },            /* MENU */
+	{ 0x080f, KEY_EPG },             /* [EPG] */
+	{ 0x0810, KEY_CLEAR },           /* EXIT */
+	{ 0x0811, KEY_CHANNELUP },
+	{ 0x0812, KEY_VOLUMEDOWN },
+	{ 0x0813, KEY_VOLUMEUP },
+	{ 0x0814, KEY_CHANNELDOWN },
+	{ 0x0815, KEY_OK },
+	{ 0x0816, KEY_RADIO },           /* [symbol TV/radio] */
+	{ 0x0817, KEY_INFO },            /* [i] */
+	{ 0x0818, KEY_PREVIOUS },        /* [|<<] */
+	{ 0x0819, KEY_FAVORITES },       /* FAV. */
+	{ 0x081a, KEY_SUBTITLE },        /* Subtitle */
+	{ 0x081b, KEY_CAMERA },          /* [symbol camera] */
+	{ 0x081c, KEY_YELLOW },
+	{ 0x081d, KEY_RED },
+	{ 0x081e, KEY_LANGUAGE },        /* [symbol Second Audio Program] */
+	{ 0x081f, KEY_GREEN },
+	{ 0x0820, KEY_SLEEP },           /* Sleep */
+	{ 0x0821, KEY_SCREEN },          /* 16:9 / 4:3 */
+	{ 0x0822, KEY_ZOOM },            /* SIZE */
+	{ 0x0824, KEY_FN },              /* [F1] */
+	{ 0x0825, KEY_FN },              /* [F2] */
+	{ 0x0842, KEY_MUTE },            /* symbol mute */
+	{ 0x0844, KEY_BLUE },
+	{ 0x0847, KEY_TEXT },            /* TEXT */
+	{ 0x0848, KEY_STOP },
+	{ 0x0849, KEY_RECORD },
+	{ 0x0850, KEY_PLAY },
+	{ 0x0851, KEY_PAUSE },
+};
+
+static struct rc_map_list anysee_map = {
+	.map = {
+		.scan    = anysee,
+		.size    = ARRAY_SIZE(anysee),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_ANYSEE,
+	}
+};
+
+static int __init init_rc_map_anysee(void)
+{
+	return rc_map_register(&anysee_map);
+}
+
+static void __exit exit_rc_map_anysee(void)
+{
+	rc_map_unregister(&anysee_map);
+}
+
+module_init(init_rc_map_anysee)
+module_exit(exit_rc_map_anysee)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-apac-viewcomp.c b/drivers/media/rc/keymaps/rc-apac-viewcomp.c
new file mode 100644
index 0000000..bf9efa0
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-apac-viewcomp.c
@@ -0,0 +1,81 @@
+/* apac-viewcomp.h - Keytable for apac_viewcomp Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Attila Kondoros <attila.kondoros@chello.hu> */
+
+static struct rc_map_table 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 <<<< */
+};
+
+static struct rc_map_list apac_viewcomp_map = {
+	.map = {
+		.scan    = apac_viewcomp,
+		.size    = ARRAY_SIZE(apac_viewcomp),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_APAC_VIEWCOMP,
+	}
+};
+
+static int __init init_rc_map_apac_viewcomp(void)
+{
+	return rc_map_register(&apac_viewcomp_map);
+}
+
+static void __exit exit_rc_map_apac_viewcomp(void)
+{
+	rc_map_unregister(&apac_viewcomp_map);
+}
+
+module_init(init_rc_map_apac_viewcomp)
+module_exit(exit_rc_map_apac_viewcomp)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-asus-pc39.c b/drivers/media/rc/keymaps/rc-asus-pc39.c
new file mode 100644
index 0000000..9e674ba
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-asus-pc39.c
@@ -0,0 +1,92 @@
+/* asus-pc39.h - Keytable for asus_pc39 Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * 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"
+ */
+
+static struct rc_map_table asus_pc39[] = {
+	/* Keys 0 to 9 */
+	{ 0x082a, KEY_0 },
+	{ 0x0816, KEY_1 },
+	{ 0x0812, KEY_2 },
+	{ 0x0814, KEY_3 },
+	{ 0x0836, KEY_4 },
+	{ 0x0832, KEY_5 },
+	{ 0x0834, KEY_6 },
+	{ 0x080e, KEY_7 },
+	{ 0x080a, KEY_8 },
+	{ 0x080c, KEY_9 },
+
+	{ 0x0801, KEY_RADIO },		/* radio */
+	{ 0x083c, KEY_MENU },		/* dvd/menu */
+	{ 0x0815, KEY_VOLUMEUP },
+	{ 0x0826, KEY_VOLUMEDOWN },
+	{ 0x0808, KEY_UP },
+	{ 0x0804, KEY_DOWN },
+	{ 0x0818, KEY_LEFT },
+	{ 0x0810, KEY_RIGHT },
+	{ 0x081a, KEY_VIDEO },		/* video */
+	{ 0x0806, KEY_AUDIO },		/* music */
+
+	{ 0x081e, KEY_TV },		/* tv */
+	{ 0x0822, KEY_EXIT },		/* back */
+	{ 0x0835, KEY_CHANNELUP },	/* channel / program + */
+	{ 0x0824, KEY_CHANNELDOWN },	/* channel / program - */
+	{ 0x0825, KEY_ENTER },		/* enter */
+
+	{ 0x0839, KEY_PAUSE },		/* play/pause */
+	{ 0x0821, KEY_PREVIOUS },		/* rew */
+	{ 0x0819, KEY_NEXT },		/* forward */
+	{ 0x0831, KEY_REWIND },		/* backward << */
+	{ 0x0805, KEY_FASTFORWARD },	/* forward >> */
+	{ 0x0809, KEY_STOP },
+	{ 0x0811, KEY_RECORD },		/* recording */
+	{ 0x0829, KEY_POWER },		/* the button that reads "close" */
+
+	{ 0x082e, KEY_ZOOM },		/* full screen */
+	{ 0x082c, KEY_MACRO },		/* recall */
+	{ 0x081c, KEY_HOME },		/* home */
+	{ 0x083a, KEY_PVR },		/* picture */
+	{ 0x0802, KEY_MUTE },		/* mute */
+	{ 0x083e, KEY_DVD },		/* dvd */
+};
+
+static struct rc_map_list asus_pc39_map = {
+	.map = {
+		.scan    = asus_pc39,
+		.size    = ARRAY_SIZE(asus_pc39),
+		.rc_type = RC_TYPE_RC5,
+		.name    = RC_MAP_ASUS_PC39,
+	}
+};
+
+static int __init init_rc_map_asus_pc39(void)
+{
+	return rc_map_register(&asus_pc39_map);
+}
+
+static void __exit exit_rc_map_asus_pc39(void)
+{
+	rc_map_unregister(&asus_pc39_map);
+}
+
+module_init(init_rc_map_asus_pc39)
+module_exit(exit_rc_map_asus_pc39)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-asus-ps3-100.c b/drivers/media/rc/keymaps/rc-asus-ps3-100.c
new file mode 100644
index 0000000..e45de35
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-asus-ps3-100.c
@@ -0,0 +1,91 @@
+/* asus-ps3-100.h - Keytable for asus_ps3_100 Remote Controller
+ *
+ * Copyright (c) 2012 by Mauro Carvalho Chehab
+ *
+ * Based on a previous patch from Remi Schwartz <remi.schwartz@gmail.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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table asus_ps3_100[] = {
+	{ 0x081c, KEY_HOME },             /* home */
+	{ 0x081e, KEY_TV },               /* tv */
+	{ 0x0803, KEY_TEXT },             /* teletext */
+	{ 0x0829, KEY_POWER },            /* close */
+
+	{ 0x080b, KEY_RED },              /* red */
+	{ 0x080d, KEY_YELLOW },           /* yellow */
+	{ 0x0806, KEY_BLUE },             /* blue */
+	{ 0x0807, KEY_GREEN },            /* green */
+
+	/* Keys 0 to 9 */
+	{ 0x082a, KEY_0 },
+	{ 0x0816, KEY_1 },
+	{ 0x0812, KEY_2 },
+	{ 0x0814, KEY_3 },
+	{ 0x0836, KEY_4 },
+	{ 0x0832, KEY_5 },
+	{ 0x0834, KEY_6 },
+	{ 0x080e, KEY_7 },
+	{ 0x080a, KEY_8 },
+	{ 0x080c, KEY_9 },
+
+	{ 0x0815, KEY_VOLUMEUP },
+	{ 0x0826, KEY_VOLUMEDOWN },
+	{ 0x0835, KEY_CHANNELUP },        /* channel / program + */
+	{ 0x0824, KEY_CHANNELDOWN },      /* channel / program - */
+
+	{ 0x0808, KEY_UP },
+	{ 0x0804, KEY_DOWN },
+	{ 0x0818, KEY_LEFT },
+	{ 0x0810, KEY_RIGHT },
+	{ 0x0825, KEY_ENTER },            /* enter */
+
+	{ 0x0822, KEY_EXIT },             /* back */
+	{ 0x082c, KEY_AB },               /* recall */
+
+	{ 0x0820, KEY_AUDIO },            /* TV audio */
+	{ 0x0837, KEY_SCREEN },           /* snapshot */
+	{ 0x082e, KEY_ZOOM },             /* full screen */
+	{ 0x0802, KEY_MUTE },             /* mute */
+
+	{ 0x0831, KEY_REWIND },           /* backward << */
+	{ 0x0811, KEY_RECORD },           /* recording */
+	{ 0x0809, KEY_STOP },
+	{ 0x0805, KEY_FASTFORWARD },      /* forward >> */
+	{ 0x0821, KEY_PREVIOUS },         /* rew */
+	{ 0x081a, KEY_PAUSE },            /* pause */
+	{ 0x0839, KEY_PLAY },             /* play */
+	{ 0x0819, KEY_NEXT },             /* forward */
+};
+
+static struct rc_map_list asus_ps3_100_map = {
+.map = {
+	.scan    = asus_ps3_100,
+	.size    = ARRAY_SIZE(asus_ps3_100),
+	.rc_type = RC_TYPE_RC5,
+	.name    = RC_MAP_ASUS_PS3_100,
+}
+};
+
+static int __init init_rc_map_asus_ps3_100(void)
+{
+return rc_map_register(&asus_ps3_100_map);
+}
+
+static void __exit exit_rc_map_asus_ps3_100(void)
+{
+rc_map_unregister(&asus_ps3_100_map);
+}
+
+module_init(init_rc_map_asus_ps3_100)
+module_exit(exit_rc_map_asus_ps3_100)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-ati-tv-wonder-hd-600.c b/drivers/media/rc/keymaps/rc-ati-tv-wonder-hd-600.c
new file mode 100644
index 0000000..91392d4
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-ati-tv-wonder-hd-600.c
@@ -0,0 +1,70 @@
+/* ati-tv-wonder-hd-600.h - Keytable for ati_tv_wonder_hd_600 Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* ATI TV Wonder HD 600 USB
+   Devin Heitmueller <devin.heitmueller@gmail.com>
+ */
+
+static struct rc_map_table 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},
+};
+
+static struct rc_map_list ati_tv_wonder_hd_600_map = {
+	.map = {
+		.scan    = ati_tv_wonder_hd_600,
+		.size    = ARRAY_SIZE(ati_tv_wonder_hd_600),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_ATI_TV_WONDER_HD_600,
+	}
+};
+
+static int __init init_rc_map_ati_tv_wonder_hd_600(void)
+{
+	return rc_map_register(&ati_tv_wonder_hd_600_map);
+}
+
+static void __exit exit_rc_map_ati_tv_wonder_hd_600(void)
+{
+	rc_map_unregister(&ati_tv_wonder_hd_600_map);
+}
+
+module_init(init_rc_map_ati_tv_wonder_hd_600)
+module_exit(exit_rc_map_ati_tv_wonder_hd_600)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-ati-x10.c b/drivers/media/rc/keymaps/rc-ati-x10.c
new file mode 100644
index 0000000..4bdc709
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-ati-x10.c
@@ -0,0 +1,138 @@
+/*
+ * ATI X10 RF remote keytable
+ *
+ * Copyright (C) 2011 Anssi Hannula <anssi.hannula@?ki.fi>
+ *
+ * This file is based on the static generic keytable previously found in
+ * ati_remote.c, which is
+ * Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net>
+ * Copyright (c) 2002 Vladimir Dergachev
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <media/rc-map.h>
+
+/*
+ * Intended usage comments below are from vendor-supplied
+ * Source: ATI REMOTE WONDER™ Installation Guide
+ * http://www2.ati.com/manuals/remctrl.pdf
+ *
+ * Scancodes were in strict left-right, top-bottom order on the
+ * original ATI Remote Wonder, but were moved on later models.
+ *
+ * Keys A-F are intended to be user-programmable.
+ */
+
+static struct rc_map_table ati_x10[] = {
+	/* keyboard - Above the cursor pad */
+	{ 0x00, KEY_A },
+	{ 0x01, KEY_B },
+	{ 0x02, KEY_POWER },      /* Power */
+
+	{ 0x03, KEY_TV },         /* TV */
+	{ 0x04, KEY_DVD },        /* DVD */
+	{ 0x05, KEY_WWW },        /* WEB */
+	{ 0x06, KEY_BOOKMARKS },  /* "book": Open Media Library */
+	{ 0x07, KEY_EDIT },       /* "hand": Toggle left mouse button (grab) */
+
+	/* Mouse emulation pad goes here, handled by driver separately */
+
+	{ 0x09, KEY_VOLUMEDOWN }, /* VOL + */
+	{ 0x08, KEY_VOLUMEUP },   /* VOL - */
+	{ 0x0a, KEY_MUTE },       /* MUTE  */
+	{ 0x0b, KEY_CHANNELUP },  /* CH + */
+	{ 0x0c, KEY_CHANNELDOWN },/* CH - */
+
+	/*
+	 * We could use KEY_NUMERIC_x for these, but the X11 protocol
+	 * has problems with keycodes greater than 255, so avoid those high
+	 * keycodes in default maps.
+	 */
+	{ 0x0d, KEY_1 },
+	{ 0x0e, KEY_2 },
+	{ 0x0f, KEY_3 },
+	{ 0x10, KEY_4 },
+	{ 0x11, KEY_5 },
+	{ 0x12, KEY_6 },
+	{ 0x13, KEY_7 },
+	{ 0x14, KEY_8 },
+	{ 0x15, KEY_9 },
+	{ 0x16, KEY_MENU },       /* "menu": DVD root menu */
+				  /* KEY_NUMERIC_STAR? */
+	{ 0x17, KEY_0 },
+	{ 0x18, KEY_SETUP },      /* "check": DVD setup menu */
+				  /* KEY_NUMERIC_POUND? */
+
+	/* DVD navigation buttons */
+	{ 0x19, KEY_C },
+	{ 0x1a, KEY_UP },         /* up */
+	{ 0x1b, KEY_D },
+
+	{ 0x1c, KEY_PROPS },      /* "timer" Should be Data On Screen */
+				  /* Symbol is "circle nailed to box" */
+	{ 0x1d, KEY_LEFT },       /* left */
+	{ 0x1e, KEY_OK },         /* "OK" */
+	{ 0x1f, KEY_RIGHT },      /* right */
+	{ 0x20, KEY_SCREEN },     /* "max" (X11 warning: 0x177) */
+				  /* Should be AC View Toggle, but
+				     that's not in <input/input.h>.
+				     KEY_ZOOM (0x174)? */
+	{ 0x21, KEY_E },
+	{ 0x22, KEY_DOWN },       /* down */
+	{ 0x23, KEY_F },
+	/* Play/stop/pause buttons */
+	{ 0x24, KEY_REWIND },     /* (<<) Rewind */
+	{ 0x25, KEY_PLAY },       /* ( >) Play (KEY_PLAYCD?) */
+	{ 0x26, KEY_FASTFORWARD }, /* (>>) Fast forward */
+
+	{ 0x27, KEY_RECORD },     /* ( o) red */
+	{ 0x28, KEY_STOPCD },     /* ([]) Stop  (KEY_STOP is something else!) */
+	{ 0x29, KEY_PAUSE },      /* ('') Pause (KEY_PAUSECD?) */
+
+	/* Extra keys, not on the original ATI remote */
+	{ 0x2a, KEY_NEXT },       /* (>+) */
+	{ 0x2b, KEY_PREVIOUS },   /* (<-) */
+	{ 0x2d, KEY_INFO },       /* PLAYING  (X11 warning: 0x166) */
+	{ 0x2e, KEY_HOME },       /* TOP */
+	{ 0x2f, KEY_END },        /* END */
+	{ 0x30, KEY_SELECT },     /* SELECT  (X11 warning: 0x161) */
+};
+
+static struct rc_map_list ati_x10_map = {
+	.map = {
+		.scan    = ati_x10,
+		.size    = ARRAY_SIZE(ati_x10),
+		.rc_type = RC_TYPE_OTHER,
+		.name    = RC_MAP_ATI_X10,
+	}
+};
+
+static int __init init_rc_map_ati_x10(void)
+{
+	return rc_map_register(&ati_x10_map);
+}
+
+static void __exit exit_rc_map_ati_x10(void)
+{
+	rc_map_unregister(&ati_x10_map);
+}
+
+module_init(init_rc_map_ati_x10)
+module_exit(exit_rc_map_ati_x10)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-avermedia-a16d.c b/drivers/media/rc/keymaps/rc-avermedia-a16d.c
new file mode 100644
index 0000000..ff30a71
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-avermedia-a16d.c
@@ -0,0 +1,76 @@
+/* avermedia-a16d.h - Keytable for avermedia_a16d Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table 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},
+};
+
+static struct rc_map_list avermedia_a16d_map = {
+	.map = {
+		.scan    = avermedia_a16d,
+		.size    = ARRAY_SIZE(avermedia_a16d),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_AVERMEDIA_A16D,
+	}
+};
+
+static int __init init_rc_map_avermedia_a16d(void)
+{
+	return rc_map_register(&avermedia_a16d_map);
+}
+
+static void __exit exit_rc_map_avermedia_a16d(void)
+{
+	rc_map_unregister(&avermedia_a16d_map);
+}
+
+module_init(init_rc_map_avermedia_a16d)
+module_exit(exit_rc_map_avermedia_a16d)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-avermedia-cardbus.c b/drivers/media/rc/keymaps/rc-avermedia-cardbus.c
new file mode 100644
index 0000000..d7471a6
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-avermedia-cardbus.c
@@ -0,0 +1,98 @@
+/* avermedia-cardbus.h - Keytable for avermedia_cardbus Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Oldrich Jedlicka <oldium.pro@seznam.cz> */
+
+static struct rc_map_table 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_VIDEO },		/* 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 */
+};
+
+static struct rc_map_list avermedia_cardbus_map = {
+	.map = {
+		.scan    = avermedia_cardbus,
+		.size    = ARRAY_SIZE(avermedia_cardbus),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_AVERMEDIA_CARDBUS,
+	}
+};
+
+static int __init init_rc_map_avermedia_cardbus(void)
+{
+	return rc_map_register(&avermedia_cardbus_map);
+}
+
+static void __exit exit_rc_map_avermedia_cardbus(void)
+{
+	rc_map_unregister(&avermedia_cardbus_map);
+}
+
+module_init(init_rc_map_avermedia_cardbus)
+module_exit(exit_rc_map_avermedia_cardbus)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-avermedia-dvbt.c b/drivers/media/rc/keymaps/rc-avermedia-dvbt.c
new file mode 100644
index 0000000..e2417d6
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-avermedia-dvbt.c
@@ -0,0 +1,79 @@
+/* avermedia-dvbt.h - Keytable for avermedia_dvbt Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Matt Jesson <dvb@jesson.eclipse.co.uk */
+
+static struct rc_map_table 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_VIDEO },		/* 'source' */
+	{ 0x10, KEY_TEXT },		/* 'teletext' */
+	{ 0x00, KEY_POWER },		/* 'power' */
+	{ 0x04, KEY_AUDIO },		/* 'audio' */
+	{ 0x06, KEY_ZOOM },		/* 'full screen' */
+	{ 0x18, KEY_SWITCHVIDEOMODE },	/* '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 +' */
+};
+
+static struct rc_map_list avermedia_dvbt_map = {
+	.map = {
+		.scan    = avermedia_dvbt,
+		.size    = ARRAY_SIZE(avermedia_dvbt),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_AVERMEDIA_DVBT,
+	}
+};
+
+static int __init init_rc_map_avermedia_dvbt(void)
+{
+	return rc_map_register(&avermedia_dvbt_map);
+}
+
+static void __exit exit_rc_map_avermedia_dvbt(void)
+{
+	rc_map_unregister(&avermedia_dvbt_map);
+}
+
+module_init(init_rc_map_avermedia_dvbt)
+module_exit(exit_rc_map_avermedia_dvbt)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-avermedia-m135a.c b/drivers/media/rc/keymaps/rc-avermedia-m135a.c
new file mode 100644
index 0000000..843598a
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-avermedia-m135a.c
@@ -0,0 +1,148 @@
+/* avermedia-m135a.c - Keytable for Avermedia M135A Remote Controllers
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ * Copyright (c) 2010 by Herton Ronaldo Krzesinski <herton@mandriva.com.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * Avermedia M135A with RM-JX and RM-K6 remote controls
+ *
+ * On Avermedia M135A with IR model RM-JX, the same codes exist on both
+ * Positivo (BR) and original IR, initial version and remote control codes
+ * added by Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ * Positivo also ships Avermedia M135A with model RM-K6, extra control
+ * codes added by Herton Ronaldo Krzesinski <herton@mandriva.com.br>
+ */
+
+static struct rc_map_table avermedia_m135a[] = {
+	/* RM-JX */
+	{ 0x0200, KEY_POWER2 },
+	{ 0x022e, KEY_DOT },		/* '.' */
+	{ 0x0201, KEY_MODE },		/* TV/FM or SOURCE */
+
+	{ 0x0205, KEY_1 },
+	{ 0x0206, KEY_2 },
+	{ 0x0207, KEY_3 },
+	{ 0x0209, KEY_4 },
+	{ 0x020a, KEY_5 },
+	{ 0x020b, KEY_6 },
+	{ 0x020d, KEY_7 },
+	{ 0x020e, KEY_8 },
+	{ 0x020f, KEY_9 },
+	{ 0x0211, KEY_0 },
+
+	{ 0x0213, KEY_RIGHT },		/* -> or L */
+	{ 0x0212, KEY_LEFT },		/* <- or R */
+
+	{ 0x0217, KEY_SLEEP },		/* Capturar Imagem or Snapshot */
+	{ 0x0210, KEY_SHUFFLE },	/* Amostra or 16 chan prev */
+
+	{ 0x0303, KEY_CHANNELUP },
+	{ 0x0302, KEY_CHANNELDOWN },
+	{ 0x021f, KEY_VOLUMEUP },
+	{ 0x021e, KEY_VOLUMEDOWN },
+	{ 0x020c, KEY_ENTER },		/* Full Screen */
+
+	{ 0x0214, KEY_MUTE },
+	{ 0x0208, KEY_AUDIO },
+
+	{ 0x0203, KEY_TEXT },		/* Teletext */
+	{ 0x0204, KEY_EPG },
+	{ 0x022b, KEY_TV2 },		/* TV2 or PIP */
+
+	{ 0x021d, KEY_RED },
+	{ 0x021c, KEY_YELLOW },
+	{ 0x0301, KEY_GREEN },
+	{ 0x0300, KEY_BLUE },
+
+	{ 0x021a, KEY_PLAYPAUSE },
+	{ 0x0219, KEY_RECORD },
+	{ 0x0218, KEY_PLAY },
+	{ 0x021b, KEY_STOP },
+
+	/* RM-K6 */
+	{ 0x0401, KEY_POWER2 },
+	{ 0x0406, KEY_MUTE },
+	{ 0x0408, KEY_MODE },     /* TV/FM */
+
+	{ 0x0409, KEY_1 },
+	{ 0x040a, KEY_2 },
+	{ 0x040b, KEY_3 },
+	{ 0x040c, KEY_4 },
+	{ 0x040d, KEY_5 },
+	{ 0x040e, KEY_6 },
+	{ 0x040f, KEY_7 },
+	{ 0x0410, KEY_8 },
+	{ 0x0411, KEY_9 },
+	{ 0x044c, KEY_DOT },      /* '.' */
+	{ 0x0412, KEY_0 },
+	{ 0x0407, KEY_REFRESH },  /* Refresh/Reload */
+
+	{ 0x0413, KEY_AUDIO },
+	{ 0x0440, KEY_SCREEN },   /* Full Screen toggle */
+	{ 0x0441, KEY_HOME },
+	{ 0x0442, KEY_BACK },
+	{ 0x0447, KEY_UP },
+	{ 0x0448, KEY_DOWN },
+	{ 0x0449, KEY_LEFT },
+	{ 0x044a, KEY_RIGHT },
+	{ 0x044b, KEY_OK },
+	{ 0x0404, KEY_VOLUMEUP },
+	{ 0x0405, KEY_VOLUMEDOWN },
+	{ 0x0402, KEY_CHANNELUP },
+	{ 0x0403, KEY_CHANNELDOWN },
+
+	{ 0x0443, KEY_RED },
+	{ 0x0444, KEY_GREEN },
+	{ 0x0445, KEY_YELLOW },
+	{ 0x0446, KEY_BLUE },
+
+	{ 0x0414, KEY_TEXT },
+	{ 0x0415, KEY_EPG },
+	{ 0x041a, KEY_TV2 },      /* PIP */
+	{ 0x041b, KEY_CAMERA },      /* Snapshot */
+
+	{ 0x0417, KEY_RECORD },
+	{ 0x0416, KEY_PLAYPAUSE },
+	{ 0x0418, KEY_STOP },
+	{ 0x0419, KEY_PAUSE },
+
+	{ 0x041f, KEY_PREVIOUS },
+	{ 0x041c, KEY_REWIND },
+	{ 0x041d, KEY_FORWARD },
+	{ 0x041e, KEY_NEXT },
+};
+
+static struct rc_map_list avermedia_m135a_map = {
+	.map = {
+		.scan    = avermedia_m135a,
+		.size    = ARRAY_SIZE(avermedia_m135a),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_AVERMEDIA_M135A,
+	}
+};
+
+static int __init init_rc_map_avermedia_m135a(void)
+{
+	return rc_map_register(&avermedia_m135a_map);
+}
+
+static void __exit exit_rc_map_avermedia_m135a(void)
+{
+	rc_map_unregister(&avermedia_m135a_map);
+}
+
+module_init(init_rc_map_avermedia_m135a)
+module_exit(exit_rc_map_avermedia_m135a)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c b/drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c
new file mode 100644
index 0000000..b24e748
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-avermedia-m733a-rm-k6.c
@@ -0,0 +1,96 @@
+/* avermedia-m733a-rm-k6.h - Keytable for avermedia_m733a_rm_k6 Remote Controller
+ *
+ * Copyright (c) 2010 by Herton Ronaldo Krzesinski <herton@mandriva.com.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * Avermedia M733A with IR model RM-K6
+ * This is the stock remote controller used with Positivo machines with M733A
+ * Herton Ronaldo Krzesinski <herton@mandriva.com.br>
+ */
+
+static struct rc_map_table avermedia_m733a_rm_k6[] = {
+	{ 0x0401, KEY_POWER2 },
+	{ 0x0406, KEY_MUTE },
+	{ 0x0408, KEY_MODE },     /* TV/FM */
+
+	{ 0x0409, KEY_1 },
+	{ 0x040a, KEY_2 },
+	{ 0x040b, KEY_3 },
+	{ 0x040c, KEY_4 },
+	{ 0x040d, KEY_5 },
+	{ 0x040e, KEY_6 },
+	{ 0x040f, KEY_7 },
+	{ 0x0410, KEY_8 },
+	{ 0x0411, KEY_9 },
+	{ 0x044c, KEY_DOT },      /* '.' */
+	{ 0x0412, KEY_0 },
+	{ 0x0407, KEY_REFRESH },  /* Refresh/Reload */
+
+	{ 0x0413, KEY_AUDIO },
+	{ 0x0440, KEY_SCREEN },   /* Full Screen toggle */
+	{ 0x0441, KEY_HOME },
+	{ 0x0442, KEY_BACK },
+	{ 0x0447, KEY_UP },
+	{ 0x0448, KEY_DOWN },
+	{ 0x0449, KEY_LEFT },
+	{ 0x044a, KEY_RIGHT },
+	{ 0x044b, KEY_OK },
+	{ 0x0404, KEY_VOLUMEUP },
+	{ 0x0405, KEY_VOLUMEDOWN },
+	{ 0x0402, KEY_CHANNELUP },
+	{ 0x0403, KEY_CHANNELDOWN },
+
+	{ 0x0443, KEY_RED },
+	{ 0x0444, KEY_GREEN },
+	{ 0x0445, KEY_YELLOW },
+	{ 0x0446, KEY_BLUE },
+
+	{ 0x0414, KEY_TEXT },
+	{ 0x0415, KEY_EPG },
+	{ 0x041a, KEY_TV2 },      /* PIP */
+	{ 0x041b, KEY_CAMERA },      /* Snapshot */
+
+	{ 0x0417, KEY_RECORD },
+	{ 0x0416, KEY_PLAYPAUSE },
+	{ 0x0418, KEY_STOP },
+	{ 0x0419, KEY_PAUSE },
+
+	{ 0x041f, KEY_PREVIOUS },
+	{ 0x041c, KEY_REWIND },
+	{ 0x041d, KEY_FORWARD },
+	{ 0x041e, KEY_NEXT },
+};
+
+static struct rc_map_list avermedia_m733a_rm_k6_map = {
+	.map = {
+		.scan    = avermedia_m733a_rm_k6,
+		.size    = ARRAY_SIZE(avermedia_m733a_rm_k6),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_AVERMEDIA_M733A_RM_K6,
+	}
+};
+
+static int __init init_rc_map_avermedia_m733a_rm_k6(void)
+{
+	return rc_map_register(&avermedia_m733a_rm_k6_map);
+}
+
+static void __exit exit_rc_map_avermedia_m733a_rm_k6(void)
+{
+	rc_map_unregister(&avermedia_m733a_rm_k6_map);
+}
+
+module_init(init_rc_map_avermedia_m733a_rm_k6)
+module_exit(exit_rc_map_avermedia_m733a_rm_k6)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c b/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c
new file mode 100644
index 0000000..8344bcc
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c
@@ -0,0 +1,80 @@
+/*
+ * AverMedia RM-KS remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Initial keytable is from Jose Alberto Reguero <jareguero@telefonica.net>
+   and Felipe Morales Moreno <felipe.morales.moreno@gmail.com> */
+/* FIXME: mappings are not 100% correct? */
+static struct rc_map_table avermedia_rm_ks[] = {
+	{ 0x0501, KEY_POWER2 },
+	{ 0x0502, KEY_CHANNELUP },
+	{ 0x0503, KEY_CHANNELDOWN },
+	{ 0x0504, KEY_VOLUMEUP },
+	{ 0x0505, KEY_VOLUMEDOWN },
+	{ 0x0506, KEY_MUTE },
+	{ 0x0507, KEY_RIGHT },
+	{ 0x0508, KEY_RED },
+	{ 0x0509, KEY_1 },
+	{ 0x050a, KEY_2 },
+	{ 0x050b, KEY_3 },
+	{ 0x050c, KEY_4 },
+	{ 0x050d, KEY_5 },
+	{ 0x050e, KEY_6 },
+	{ 0x050f, KEY_7 },
+	{ 0x0510, KEY_8 },
+	{ 0x0511, KEY_9 },
+	{ 0x0512, KEY_0 },
+	{ 0x0513, KEY_AUDIO },
+	{ 0x0515, KEY_EPG },
+	{ 0x0516, KEY_PLAY },
+	{ 0x0517, KEY_RECORD },
+	{ 0x0518, KEY_STOP },
+	{ 0x051c, KEY_BACK },
+	{ 0x051d, KEY_FORWARD },
+	{ 0x054d, KEY_LEFT },
+	{ 0x0556, KEY_ZOOM },
+};
+
+static struct rc_map_list avermedia_rm_ks_map = {
+	.map = {
+		.scan    = avermedia_rm_ks,
+		.size    = ARRAY_SIZE(avermedia_rm_ks),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_AVERMEDIA_RM_KS,
+	}
+};
+
+static int __init init_rc_map_avermedia_rm_ks(void)
+{
+	return rc_map_register(&avermedia_rm_ks_map);
+}
+
+static void __exit exit_rc_map_avermedia_rm_ks(void)
+{
+	rc_map_unregister(&avermedia_rm_ks_map);
+}
+
+module_init(init_rc_map_avermedia_rm_ks)
+module_exit(exit_rc_map_avermedia_rm_ks)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-avermedia.c b/drivers/media/rc/keymaps/rc-avermedia.c
new file mode 100644
index 0000000..3f68fbe
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-avermedia.c
@@ -0,0 +1,87 @@
+/* avermedia.h - Keytable for avermedia Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Alex Hermann <gaaf@gmx.net> */
+
+static struct rc_map_table 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+ */
+};
+
+static struct rc_map_list avermedia_map = {
+	.map = {
+		.scan    = avermedia,
+		.size    = ARRAY_SIZE(avermedia),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_AVERMEDIA,
+	}
+};
+
+static int __init init_rc_map_avermedia(void)
+{
+	return rc_map_register(&avermedia_map);
+}
+
+static void __exit exit_rc_map_avermedia(void)
+{
+	rc_map_unregister(&avermedia_map);
+}
+
+module_init(init_rc_map_avermedia)
+module_exit(exit_rc_map_avermedia)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-avertv-303.c b/drivers/media/rc/keymaps/rc-avertv-303.c
new file mode 100644
index 0000000..c35bc5b
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-avertv-303.c
@@ -0,0 +1,86 @@
+/* avertv-303.h - Keytable for avertv_303 Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* AVERTV STUDIO 303 Remote */
+
+static struct rc_map_table 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 },
+};
+
+static struct rc_map_list avertv_303_map = {
+	.map = {
+		.scan    = avertv_303,
+		.size    = ARRAY_SIZE(avertv_303),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_AVERTV_303,
+	}
+};
+
+static int __init init_rc_map_avertv_303(void)
+{
+	return rc_map_register(&avertv_303_map);
+}
+
+static void __exit exit_rc_map_avertv_303(void)
+{
+	rc_map_unregister(&avertv_303_map);
+}
+
+module_init(init_rc_map_avertv_303)
+module_exit(exit_rc_map_avertv_303)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-azurewave-ad-tu700.c b/drivers/media/rc/keymaps/rc-azurewave-ad-tu700.c
new file mode 100644
index 0000000..ea7f2d0
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-azurewave-ad-tu700.c
@@ -0,0 +1,103 @@
+/*
+ * TwinHan AzureWave AD-TU700(704J) remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table azurewave_ad_tu700[] = {
+	{ 0x0000, KEY_TAB },             /* Tab */
+	{ 0x0001, KEY_2 },
+	{ 0x0002, KEY_CHANNELDOWN },
+	{ 0x0003, KEY_1 },
+	{ 0x0004, KEY_MENU },            /* Record List */
+	{ 0x0005, KEY_CHANNELUP },
+	{ 0x0006, KEY_3 },
+	{ 0x0007, KEY_SLEEP },           /* Hibernate */
+	{ 0x0008, KEY_VIDEO },           /* A/V */
+	{ 0x0009, KEY_4 },
+	{ 0x000a, KEY_VOLUMEDOWN },
+	{ 0x000c, KEY_CANCEL },          /* Cancel */
+	{ 0x000d, KEY_7 },
+	{ 0x000e, KEY_AGAIN },           /* Recall */
+	{ 0x000f, KEY_TEXT },            /* Teletext */
+	{ 0x0010, KEY_MUTE },
+	{ 0x0011, KEY_RECORD },
+	{ 0x0012, KEY_FASTFORWARD },     /* FF >> */
+	{ 0x0013, KEY_BACK },            /* Back */
+	{ 0x0014, KEY_PLAY },
+	{ 0x0015, KEY_0 },
+	{ 0x0016, KEY_POWER2 },          /* [red power button] */
+	{ 0x0017, KEY_FAVORITES },       /* Favorite List */
+	{ 0x0018, KEY_RED },
+	{ 0x0019, KEY_8 },
+	{ 0x001a, KEY_STOP },
+	{ 0x001b, KEY_9 },
+	{ 0x001c, KEY_EPG },             /* Info/EPG */
+	{ 0x001d, KEY_5 },
+	{ 0x001e, KEY_VOLUMEUP },
+	{ 0x001f, KEY_6 },
+	{ 0x0040, KEY_REWIND },          /* FR << */
+	{ 0x0041, KEY_PREVIOUS },        /* Replay */
+	{ 0x0042, KEY_NEXT },            /* Skip */
+	{ 0x0043, KEY_SUBTITLE },        /* Subtitle / CC */
+	{ 0x0045, KEY_KPPLUS },          /* Zoom+ */
+	{ 0x0046, KEY_KPMINUS },         /* Zoom- */
+	{ 0x0047, KEY_NEW },             /* PIP */
+	{ 0x0048, KEY_INFO },            /* Preview */
+	{ 0x0049, KEY_MODE },            /* L/R */
+	{ 0x004a, KEY_CLEAR },           /* Clear */
+	{ 0x004b, KEY_UP },              /* up arrow */
+	{ 0x004c, KEY_PAUSE },
+	{ 0x004d, KEY_ZOOM },            /* Full Screen */
+	{ 0x004e, KEY_LEFT },            /* left arrow */
+	{ 0x004f, KEY_OK },              /* Enter / ok */
+	{ 0x0050, KEY_LANGUAGE },        /* SAP */
+	{ 0x0051, KEY_DOWN },            /* down arrow */
+	{ 0x0052, KEY_RIGHT },           /* right arrow */
+	{ 0x0053, KEY_GREEN },
+	{ 0x0054, KEY_CAMERA },          /* Capture */
+	{ 0x005e, KEY_YELLOW },
+	{ 0x005f, KEY_BLUE },
+};
+
+static struct rc_map_list azurewave_ad_tu700_map = {
+	.map = {
+		.scan    = azurewave_ad_tu700,
+		.size    = ARRAY_SIZE(azurewave_ad_tu700),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_AZUREWAVE_AD_TU700,
+	}
+};
+
+static int __init init_rc_map_azurewave_ad_tu700(void)
+{
+	return rc_map_register(&azurewave_ad_tu700_map);
+}
+
+static void __exit exit_rc_map_azurewave_ad_tu700(void)
+{
+	rc_map_unregister(&azurewave_ad_tu700_map);
+}
+
+module_init(init_rc_map_azurewave_ad_tu700)
+module_exit(exit_rc_map_azurewave_ad_tu700)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-behold-columbus.c b/drivers/media/rc/keymaps/rc-behold-columbus.c
new file mode 100644
index 0000000..1fc344e
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-behold-columbus.c
@@ -0,0 +1,109 @@
+/* behold-columbus.h - Keytable for behold_columbus Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Beholder Intl. Ltd. 2008
+ * Dmitry Belimov d.belimov@google.com
+ * Keytable is used by BeholdTV Columbus
+ * 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.
+ */
+
+static struct rc_map_table behold_columbus[] = {
+
+	/*  0x13   0x11   0x1C   0x12  *
+	 *  Mute  Source  TV/FM  Power *
+	 *                             */
+
+	{ 0x13, KEY_MUTE },
+	{ 0x11, KEY_VIDEO },
+	{ 0x1C, KEY_TUNER },	/* KEY_TV/KEY_RADIO	*/
+	{ 0x12, KEY_POWER },
+
+	/*  0x01    0x02    0x03  0x0D    *
+	 *   1       2       3   Stereo   *
+	 *                        	  *
+	 *  0x04    0x05    0x06  0x19    *
+	 *   4       5       6   Snapshot *
+	 *                        	  *
+	 *  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_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 },
+
+	/*   0x1B      0x1D      0x15        0x18     *
+	 * Timeshift  Record  ChannelDown  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 },
+
+};
+
+static struct rc_map_list behold_columbus_map = {
+	.map = {
+		.scan    = behold_columbus,
+		.size    = ARRAY_SIZE(behold_columbus),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_BEHOLD_COLUMBUS,
+	}
+};
+
+static int __init init_rc_map_behold_columbus(void)
+{
+	return rc_map_register(&behold_columbus_map);
+}
+
+static void __exit exit_rc_map_behold_columbus(void)
+{
+	rc_map_unregister(&behold_columbus_map);
+}
+
+module_init(init_rc_map_behold_columbus)
+module_exit(exit_rc_map_behold_columbus)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-behold.c b/drivers/media/rc/keymaps/rc-behold.c
new file mode 100644
index 0000000..520a96f
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-behold.c
@@ -0,0 +1,142 @@
+/* behold.h - Keytable for behold Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * Igor Kuznetsov <igk72@ya.ru>
+ * Andrey J. Melnikov <temnota@kmv.ru>
+ *
+ * Keytable is used by BeholdTV 60x series, M6 series 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.
+ */
+
+static struct rc_map_table behold[] = {
+
+	/*  0x1c            0x12  *
+	 *  TV/FM          POWER  *
+	 *                        */
+	{ 0x866b1c, KEY_TUNER },	/* XXX KEY_TV / KEY_RADIO */
+	{ 0x866b12, KEY_POWER },
+
+	/*  0x01    0x02    0x03  *
+	 *   1       2       3    *
+	 *                        *
+	 *  0x04    0x05    0x06  *
+	 *   4       5       6    *
+	 *                        *
+	 *  0x07    0x08    0x09  *
+	 *   7       8       9    *
+	 *                        */
+	{ 0x866b01, KEY_1 },
+	{ 0x866b02, KEY_2 },
+	{ 0x866b03, KEY_3 },
+	{ 0x866b04, KEY_4 },
+	{ 0x866b05, KEY_5 },
+	{ 0x866b06, KEY_6 },
+	{ 0x866b07, KEY_7 },
+	{ 0x866b08, KEY_8 },
+	{ 0x866b09, KEY_9 },
+
+	/*  0x0a    0x00    0x17  *
+	 * RECALL    0      MODE  *
+	 *                        */
+	{ 0x866b0a, KEY_AGAIN },
+	{ 0x866b00, KEY_0 },
+	{ 0x866b17, KEY_MODE },
+
+	/*  0x14          0x10    *
+	 * ASPECT      FULLSCREEN *
+	 *                        */
+	{ 0x866b14, KEY_SCREEN },
+	{ 0x866b10, KEY_ZOOM },
+
+	/*          0x0b          *
+	 *           Up           *
+	 *                        *
+	 *  0x18    0x16    0x0c  *
+	 *  Left     Ok     Right *
+	 *                        *
+	 *         0x015          *
+	 *         Down           *
+	 *                        */
+	{ 0x866b0b, KEY_CHANNELUP },
+	{ 0x866b18, KEY_VOLUMEDOWN },
+	{ 0x866b16, KEY_OK },		/* XXX KEY_ENTER */
+	{ 0x866b0c, KEY_VOLUMEUP },
+	{ 0x866b15, KEY_CHANNELDOWN },
+
+	/*  0x11            0x0d  *
+	 *  MUTE            INFO  *
+	 *                        */
+	{ 0x866b11, KEY_MUTE },
+	{ 0x866b0d, KEY_INFO },
+
+	/*  0x0f    0x1b    0x1a  *
+	 * RECORD PLAY/PAUSE STOP *
+	 *                        *
+	 *  0x0e    0x1f    0x1e  *
+	 *TELETEXT  AUDIO  SOURCE *
+	 *           RED   YELLOW *
+	 *                        */
+	{ 0x866b0f, KEY_RECORD },
+	{ 0x866b1b, KEY_PLAYPAUSE },
+	{ 0x866b1a, KEY_STOP },
+	{ 0x866b0e, KEY_TEXT },
+	{ 0x866b1f, KEY_RED },	/*XXX KEY_AUDIO	*/
+	{ 0x866b1e, KEY_VIDEO },
+
+	/*  0x1d   0x13     0x19  *
+	 * SLEEP  PREVIEW   DVB   *
+	 *         GREEN    BLUE  *
+	 *                        */
+	{ 0x866b1d, KEY_SLEEP },
+	{ 0x866b13, KEY_GREEN },
+	{ 0x866b19, KEY_BLUE },	/* XXX KEY_SAT	*/
+
+	/*  0x58           0x5c   *
+	 * FREEZE        SNAPSHOT *
+	 *                        */
+	{ 0x866b58, KEY_SLOW },
+	{ 0x866b5c, KEY_CAMERA },
+
+};
+
+static struct rc_map_list behold_map = {
+	.map = {
+		.scan    = behold,
+		.size    = ARRAY_SIZE(behold),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_BEHOLD,
+	}
+};
+
+static int __init init_rc_map_behold(void)
+{
+	return rc_map_register(&behold_map);
+}
+
+static void __exit exit_rc_map_behold(void)
+{
+	rc_map_unregister(&behold_map);
+}
+
+module_init(init_rc_map_behold)
+module_exit(exit_rc_map_behold)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-budget-ci-old.c b/drivers/media/rc/keymaps/rc-budget-ci-old.c
new file mode 100644
index 0000000..b196a5f
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-budget-ci-old.c
@@ -0,0 +1,94 @@
+/* budget-ci-old.h - Keytable for budget_ci_old Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * 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
+ */
+
+static struct rc_map_table 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 },
+};
+
+static struct rc_map_list budget_ci_old_map = {
+	.map = {
+		.scan    = budget_ci_old,
+		.size    = ARRAY_SIZE(budget_ci_old),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_BUDGET_CI_OLD,
+	}
+};
+
+static int __init init_rc_map_budget_ci_old(void)
+{
+	return rc_map_register(&budget_ci_old_map);
+}
+
+static void __exit exit_rc_map_budget_ci_old(void)
+{
+	rc_map_unregister(&budget_ci_old_map);
+}
+
+module_init(init_rc_map_budget_ci_old)
+module_exit(exit_rc_map_budget_ci_old)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-cinergy-1400.c b/drivers/media/rc/keymaps/rc-cinergy-1400.c
new file mode 100644
index 0000000..a099c08
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-cinergy-1400.c
@@ -0,0 +1,85 @@
+/* cinergy-1400.h - Keytable for cinergy_1400 Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Cinergy 1400 DVB-T */
+
+static struct rc_map_table 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 },
+};
+
+static struct rc_map_list cinergy_1400_map = {
+	.map = {
+		.scan    = cinergy_1400,
+		.size    = ARRAY_SIZE(cinergy_1400),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_CINERGY_1400,
+	}
+};
+
+static int __init init_rc_map_cinergy_1400(void)
+{
+	return rc_map_register(&cinergy_1400_map);
+}
+
+static void __exit exit_rc_map_cinergy_1400(void)
+{
+	rc_map_unregister(&cinergy_1400_map);
+}
+
+module_init(init_rc_map_cinergy_1400)
+module_exit(exit_rc_map_cinergy_1400)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-cinergy.c b/drivers/media/rc/keymaps/rc-cinergy.c
new file mode 100644
index 0000000..b0f4328
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-cinergy.c
@@ -0,0 +1,79 @@
+/* cinergy.h - Keytable for cinergy Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table 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_MEDIA },		/* 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 },
+};
+
+static struct rc_map_list cinergy_map = {
+	.map = {
+		.scan    = cinergy,
+		.size    = ARRAY_SIZE(cinergy),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_CINERGY,
+	}
+};
+
+static int __init init_rc_map_cinergy(void)
+{
+	return rc_map_register(&cinergy_map);
+}
+
+static void __exit exit_rc_map_cinergy(void)
+{
+	rc_map_unregister(&cinergy_map);
+}
+
+module_init(init_rc_map_cinergy)
+module_exit(exit_rc_map_cinergy)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-delock-61959.c b/drivers/media/rc/keymaps/rc-delock-61959.c
new file mode 100644
index 0000000..01bed86
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-delock-61959.c
@@ -0,0 +1,83 @@
+/* rc-delock-61959.c - Keytable for Delock
+ *
+ * Copyright (c) 2013 by Jakob Haufe <sur5r@sur5r.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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * Keytable for remote provided with Delock 61959
+ */
+static struct rc_map_table delock_61959[] = {
+	{ 0x866b16, KEY_POWER2 },	/* Power */
+	{ 0x866b0c, KEY_POWER },	/* Shut Down */
+
+	{ 0x866b00, KEY_1},
+	{ 0x866b01, KEY_2},
+	{ 0x866b02, KEY_3},
+	{ 0x866b03, KEY_4},
+	{ 0x866b04, KEY_5},
+	{ 0x866b05, KEY_6},
+	{ 0x866b06, KEY_7},
+	{ 0x866b07, KEY_8},
+	{ 0x866b08, KEY_9},
+	{ 0x866b14, KEY_0},
+
+	{ 0x866b0a, KEY_ZOOM},		/* Full Screen */
+	{ 0x866b10, KEY_CAMERA},	/* Photo */
+	{ 0x866b0e, KEY_CHANNEL},	/* circular arrow / Recall */
+	{ 0x866b13, KEY_ESC},           /* Back */
+
+	{ 0x866b20, KEY_UP},
+	{ 0x866b21, KEY_DOWN},
+	{ 0x866b42, KEY_LEFT},
+	{ 0x866b43, KEY_RIGHT},
+	{ 0x866b0b, KEY_OK},
+
+	{ 0x866b11, KEY_CHANNELUP},
+	{ 0x866b1b, KEY_CHANNELDOWN},
+
+	{ 0x866b12, KEY_VOLUMEUP},
+	{ 0x866b48, KEY_VOLUMEDOWN},
+	{ 0x866b44, KEY_MUTE},
+
+	{ 0x866b1a, KEY_RECORD},
+	{ 0x866b41, KEY_PLAY},
+	{ 0x866b40, KEY_STOP},
+	{ 0x866b19, KEY_PAUSE},
+	{ 0x866b1c, KEY_FASTFORWARD},	/* >> / FWD */
+	{ 0x866b1e, KEY_REWIND},	/* << / REW */
+
+};
+
+static struct rc_map_list delock_61959_map = {
+	.map = {
+		.scan    = delock_61959,
+		.size    = ARRAY_SIZE(delock_61959),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_DELOCK_61959,
+	}
+};
+
+static int __init init_rc_map_delock_61959(void)
+{
+	return rc_map_register(&delock_61959_map);
+}
+
+static void __exit exit_rc_map_delock_61959(void)
+{
+	rc_map_unregister(&delock_61959_map);
+}
+
+module_init(init_rc_map_delock_61959)
+module_exit(exit_rc_map_delock_61959)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jakob Haufe <sur5r@sur5r.net>");
+MODULE_DESCRIPTION("Delock 61959 remote keytable");
diff --git a/drivers/media/rc/keymaps/rc-dib0700-nec.c b/drivers/media/rc/keymaps/rc-dib0700-nec.c
new file mode 100644
index 0000000..a0fa543
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-dib0700-nec.c
@@ -0,0 +1,125 @@
+/* rc-dvb0700-big.c - Keytable for devices in dvb0700
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * TODO: This table is a real mess, as it merges RC codes from several
+ * devices into a big table. It also has both RC-5 and NEC codes inside.
+ * It should be broken into small tables, and the protocols should properly
+ * be identificated.
+ *
+ * The table were imported from dib0700_devices.c.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table dib0700_nec_table[] = {
+	/* Key codes for the Pixelview SBTVD remote */
+	{ 0x866b13, KEY_MUTE },
+	{ 0x866b12, KEY_POWER },
+	{ 0x866b01, KEY_1 },
+	{ 0x866b02, KEY_2 },
+	{ 0x866b03, KEY_3 },
+	{ 0x866b04, KEY_4 },
+	{ 0x866b05, KEY_5 },
+	{ 0x866b06, KEY_6 },
+	{ 0x866b07, KEY_7 },
+	{ 0x866b08, KEY_8 },
+	{ 0x866b09, KEY_9 },
+	{ 0x866b00, KEY_0 },
+	{ 0x866b0d, KEY_CHANNELUP },
+	{ 0x866b19, KEY_CHANNELDOWN },
+	{ 0x866b10, KEY_VOLUMEUP },
+	{ 0x866b0c, KEY_VOLUMEDOWN },
+
+	{ 0x866b0a, KEY_CAMERA },
+	{ 0x866b0b, KEY_ZOOM },
+	{ 0x866b1b, KEY_BACKSPACE },
+	{ 0x866b15, KEY_ENTER },
+
+	{ 0x866b1d, KEY_UP },
+	{ 0x866b1e, KEY_DOWN },
+	{ 0x866b0e, KEY_LEFT },
+	{ 0x866b0f, KEY_RIGHT },
+
+	{ 0x866b18, KEY_RECORD },
+	{ 0x866b1a, KEY_STOP },
+
+	/* Key codes for the EvolutePC TVWay+ remote */
+	{ 0x7a00, KEY_MENU },
+	{ 0x7a01, KEY_RECORD },
+	{ 0x7a02, KEY_PLAY },
+	{ 0x7a03, KEY_STOP },
+	{ 0x7a10, KEY_CHANNELUP },
+	{ 0x7a11, KEY_CHANNELDOWN },
+	{ 0x7a12, KEY_VOLUMEUP },
+	{ 0x7a13, KEY_VOLUMEDOWN },
+	{ 0x7a40, KEY_POWER },
+	{ 0x7a41, KEY_MUTE },
+
+	/* Key codes for the Elgato EyeTV Diversity silver remote */
+	{ 0x4501, KEY_POWER },
+	{ 0x4502, KEY_MUTE },
+	{ 0x4503, KEY_1 },
+	{ 0x4504, KEY_2 },
+	{ 0x4505, KEY_3 },
+	{ 0x4506, KEY_4 },
+	{ 0x4507, KEY_5 },
+	{ 0x4508, KEY_6 },
+	{ 0x4509, KEY_7 },
+	{ 0x450a, KEY_8 },
+	{ 0x450b, KEY_9 },
+	{ 0x450c, KEY_LAST },
+	{ 0x450d, KEY_0 },
+	{ 0x450e, KEY_ENTER },
+	{ 0x450f, KEY_RED },
+	{ 0x4510, KEY_CHANNELUP },
+	{ 0x4511, KEY_GREEN },
+	{ 0x4512, KEY_VOLUMEDOWN },
+	{ 0x4513, KEY_OK },
+	{ 0x4514, KEY_VOLUMEUP },
+	{ 0x4515, KEY_YELLOW },
+	{ 0x4516, KEY_CHANNELDOWN },
+	{ 0x4517, KEY_BLUE },
+	{ 0x4518, KEY_LEFT }, /* Skip backwards */
+	{ 0x4519, KEY_PLAYPAUSE },
+	{ 0x451a, KEY_RIGHT }, /* Skip forward */
+	{ 0x451b, KEY_REWIND },
+	{ 0x451c, KEY_L }, /* Live */
+	{ 0x451d, KEY_FASTFORWARD },
+	{ 0x451e, KEY_STOP }, /* 'Reveal' for Teletext */
+	{ 0x451f, KEY_MENU }, /* KEY_TEXT for Teletext */
+	{ 0x4540, KEY_RECORD }, /* Font 'Size' for Teletext */
+	{ 0x4541, KEY_SCREEN }, /*  Full screen toggle, 'Hold' for Teletext */
+	{ 0x4542, KEY_SELECT }, /* Select video input, 'Select' for Teletext */
+};
+
+static struct rc_map_list dib0700_nec_map = {
+	.map = {
+		.scan    = dib0700_nec_table,
+		.size    = ARRAY_SIZE(dib0700_nec_table),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_DIB0700_NEC_TABLE,
+	}
+};
+
+static int __init init_rc_map(void)
+{
+	return rc_map_register(&dib0700_nec_map);
+}
+
+static void __exit exit_rc_map(void)
+{
+	rc_map_unregister(&dib0700_nec_map);
+}
+
+module_init(init_rc_map)
+module_exit(exit_rc_map)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-dib0700-rc5.c b/drivers/media/rc/keymaps/rc-dib0700-rc5.c
new file mode 100644
index 0000000..9079411
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-dib0700-rc5.c
@@ -0,0 +1,236 @@
+/* rc-dvb0700-big.c - Keytable for devices in dvb0700
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * TODO: This table is a real mess, as it merges RC codes from several
+ * devices into a big table. It also has both RC-5 and NEC codes inside.
+ * It should be broken into small tables, and the protocols should properly
+ * be identificated.
+ *
+ * The table were imported from dib0700_devices.c.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table dib0700_rc5_table[] = {
+	/* Key codes for the tiny Pinnacle remote*/
+	{ 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 */
+	{ 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) */
+	{ 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 */
+	{ 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. */
+	{ 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 },
+};
+
+static struct rc_map_list dib0700_rc5_map = {
+	.map = {
+		.scan    = dib0700_rc5_table,
+		.size    = ARRAY_SIZE(dib0700_rc5_table),
+		.rc_type = RC_TYPE_RC5,
+		.name    = RC_MAP_DIB0700_RC5_TABLE,
+	}
+};
+
+static int __init init_rc_map(void)
+{
+	return rc_map_register(&dib0700_rc5_map);
+}
+
+static void __exit exit_rc_map(void)
+{
+	rc_map_unregister(&dib0700_rc5_map);
+}
+
+module_init(init_rc_map)
+module_exit(exit_rc_map)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-digitalnow-tinytwin.c b/drivers/media/rc/keymaps/rc-digitalnow-tinytwin.c
new file mode 100644
index 0000000..bed78ac
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-digitalnow-tinytwin.c
@@ -0,0 +1,99 @@
+/*
+ * DigitalNow TinyTwin remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table digitalnow_tinytwin[] = {
+	{ 0x0000, KEY_MUTE },            /* [symbol speaker] */
+	{ 0x0001, KEY_VOLUMEUP },
+	{ 0x0002, KEY_POWER2 },          /* TV [power button] */
+	{ 0x0003, KEY_2 },
+	{ 0x0004, KEY_3 },
+	{ 0x0005, KEY_4 },
+	{ 0x0006, KEY_6 },
+	{ 0x0007, KEY_7 },
+	{ 0x0008, KEY_8 },
+	{ 0x0009, KEY_NUMERIC_STAR },    /* [*] */
+	{ 0x000a, KEY_0 },
+	{ 0x000b, KEY_NUMERIC_POUND },   /* [#] */
+	{ 0x000c, KEY_RIGHT },           /* [right arrow] */
+	{ 0x000d, KEY_HOMEPAGE },        /* [symbol home] Start */
+	{ 0x000e, KEY_RED },             /* [red] Videos */
+	{ 0x0010, KEY_POWER },           /* PC [power button] */
+	{ 0x0011, KEY_YELLOW },          /* [yellow] Pictures */
+	{ 0x0012, KEY_DOWN },            /* [down arrow] */
+	{ 0x0013, KEY_GREEN },           /* [green] Music */
+	{ 0x0014, KEY_CYCLEWINDOWS },    /* BACK */
+	{ 0x0015, KEY_FAVORITES },       /* MORE */
+	{ 0x0016, KEY_UP },              /* [up arrow] */
+	{ 0x0017, KEY_LEFT },            /* [left arrow] */
+	{ 0x0018, KEY_OK },              /* OK */
+	{ 0x0019, KEY_BLUE },            /* [blue] MyTV */
+	{ 0x001a, KEY_REWIND },          /* REW [<<] */
+	{ 0x001b, KEY_PLAY },            /* PLAY */
+	{ 0x001c, KEY_5 },
+	{ 0x001d, KEY_9 },
+	{ 0x001e, KEY_VOLUMEDOWN },
+	{ 0x001f, KEY_1 },
+	{ 0x0040, KEY_STOP },            /* STOP */
+	{ 0x0042, KEY_PAUSE },           /* PAUSE */
+	{ 0x0043, KEY_SCREEN },          /* Aspect */
+	{ 0x0044, KEY_FORWARD },         /* FWD [>>] */
+	{ 0x0045, KEY_NEXT },            /* SKIP */
+	{ 0x0048, KEY_RECORD },          /* RECORD */
+	{ 0x0049, KEY_VIDEO },           /* RTV */
+	{ 0x004a, KEY_EPG },             /* Guide */
+	{ 0x004b, KEY_CHANNELUP },
+	{ 0x004c, KEY_HELP },            /* Help */
+	{ 0x004d, KEY_RADIO },           /* Radio */
+	{ 0x004f, KEY_CHANNELDOWN },
+	{ 0x0050, KEY_DVD },             /* DVD */
+	{ 0x0051, KEY_AUDIO },           /* Audio */
+	{ 0x0052, KEY_TITLE },           /* Title */
+	{ 0x0053, KEY_NEW },             /* [symbol PIP?] */
+	{ 0x0057, KEY_MENU },            /* Mouse */
+	{ 0x005a, KEY_PREVIOUS },        /* REPLAY */
+};
+
+static struct rc_map_list digitalnow_tinytwin_map = {
+	.map = {
+		.scan    = digitalnow_tinytwin,
+		.size    = ARRAY_SIZE(digitalnow_tinytwin),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_DIGITALNOW_TINYTWIN,
+	}
+};
+
+static int __init init_rc_map_digitalnow_tinytwin(void)
+{
+	return rc_map_register(&digitalnow_tinytwin_map);
+}
+
+static void __exit exit_rc_map_digitalnow_tinytwin(void)
+{
+	rc_map_unregister(&digitalnow_tinytwin_map);
+}
+
+module_init(init_rc_map_digitalnow_tinytwin)
+module_exit(exit_rc_map_digitalnow_tinytwin)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-digittrade.c b/drivers/media/rc/keymaps/rc-digittrade.c
new file mode 100644
index 0000000..a3b97a1
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-digittrade.c
@@ -0,0 +1,83 @@
+/*
+ * Digittrade DVB-T USB Stick remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Digittrade DVB-T USB Stick remote controller. */
+/* Imported from af9015.h.
+   Initial keytable was from Alain Kalker <miki@dds.nl> */
+
+/* Digittrade DVB-T USB Stick */
+static struct rc_map_table digittrade[] = {
+	{ 0x0000, KEY_9 },
+	{ 0x0001, KEY_EPG },             /* EPG */
+	{ 0x0002, KEY_VOLUMEDOWN },      /* Vol Dn */
+	{ 0x0003, KEY_TEXT },            /* TELETEXT */
+	{ 0x0004, KEY_8 },
+	{ 0x0005, KEY_MUTE },            /* MUTE */
+	{ 0x0006, KEY_POWER2 },          /* POWER */
+	{ 0x0009, KEY_ZOOM },            /* FULLSCREEN */
+	{ 0x000a, KEY_RECORD },          /* RECORD */
+	{ 0x000d, KEY_SUBTITLE },        /* SUBTITLE */
+	{ 0x000e, KEY_STOP },            /* STOP */
+	{ 0x0010, KEY_OK },              /* RETURN */
+	{ 0x0011, KEY_2 },
+	{ 0x0012, KEY_4 },
+	{ 0x0015, KEY_3 },
+	{ 0x0016, KEY_5 },
+	{ 0x0017, KEY_CHANNELDOWN },     /* Ch Dn */
+	{ 0x0019, KEY_CHANNELUP },       /* CH Up */
+	{ 0x001a, KEY_PAUSE },           /* PAUSE */
+	{ 0x001b, KEY_1 },
+	{ 0x001d, KEY_AUDIO },           /* DUAL SOUND */
+	{ 0x001e, KEY_PLAY },            /* PLAY */
+	{ 0x001f, KEY_CAMERA },          /* SNAPSHOT */
+	{ 0x0040, KEY_VOLUMEUP },        /* Vol Up */
+	{ 0x0048, KEY_7 },
+	{ 0x004c, KEY_6 },
+	{ 0x004d, KEY_PLAYPAUSE },       /* TIMESHIFT */
+	{ 0x0054, KEY_0 },
+};
+
+static struct rc_map_list digittrade_map = {
+	.map = {
+		.scan    = digittrade,
+		.size    = ARRAY_SIZE(digittrade),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_DIGITTRADE,
+	}
+};
+
+static int __init init_rc_map_digittrade(void)
+{
+	return rc_map_register(&digittrade_map);
+}
+
+static void __exit exit_rc_map_digittrade(void)
+{
+	rc_map_unregister(&digittrade_map);
+}
+
+module_init(init_rc_map_digittrade)
+module_exit(exit_rc_map_digittrade)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-dm1105-nec.c b/drivers/media/rc/keymaps/rc-dm1105-nec.c
new file mode 100644
index 0000000..46e7ae4
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-dm1105-nec.c
@@ -0,0 +1,77 @@
+/* dm1105-nec.h - Keytable for dm1105_nec Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* DVBWorld remotes
+   Igor M. Liplianin <liplianin@me.by>
+ */
+
+static struct rc_map_table 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 */
+};
+
+static struct rc_map_list dm1105_nec_map = {
+	.map = {
+		.scan    = dm1105_nec,
+		.size    = ARRAY_SIZE(dm1105_nec),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_DM1105_NEC,
+	}
+};
+
+static int __init init_rc_map_dm1105_nec(void)
+{
+	return rc_map_register(&dm1105_nec_map);
+}
+
+static void __exit exit_rc_map_dm1105_nec(void)
+{
+	rc_map_unregister(&dm1105_nec_map);
+}
+
+module_init(init_rc_map_dm1105_nec)
+module_exit(exit_rc_map_dm1105_nec)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c b/drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c
new file mode 100644
index 0000000..d2826b4
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-dntv-live-dvb-t.c
@@ -0,0 +1,79 @@
+/* dntv-live-dvb-t.h - Keytable for dntv_live_dvb_t Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* DigitalNow DNTV Live DVB-T Remote */
+
+static struct rc_map_table 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_VIDEO },		/* 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 },
+};
+
+static struct rc_map_list dntv_live_dvb_t_map = {
+	.map = {
+		.scan    = dntv_live_dvb_t,
+		.size    = ARRAY_SIZE(dntv_live_dvb_t),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_DNTV_LIVE_DVB_T,
+	}
+};
+
+static int __init init_rc_map_dntv_live_dvb_t(void)
+{
+	return rc_map_register(&dntv_live_dvb_t_map);
+}
+
+static void __exit exit_rc_map_dntv_live_dvb_t(void)
+{
+	rc_map_unregister(&dntv_live_dvb_t_map);
+}
+
+module_init(init_rc_map_dntv_live_dvb_t)
+module_exit(exit_rc_map_dntv_live_dvb_t)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-dntv-live-dvbt-pro.c b/drivers/media/rc/keymaps/rc-dntv-live-dvbt-pro.c
new file mode 100644
index 0000000..0d74769
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-dntv-live-dvbt-pro.c
@@ -0,0 +1,98 @@
+/* dntv-live-dvbt-pro.h - Keytable for dntv_live_dvbt_pro Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* DigitalNow DNTV Live! DVB-T Pro Remote */
+
+static struct rc_map_table 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 },
+};
+
+static struct rc_map_list dntv_live_dvbt_pro_map = {
+	.map = {
+		.scan    = dntv_live_dvbt_pro,
+		.size    = ARRAY_SIZE(dntv_live_dvbt_pro),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_DNTV_LIVE_DVBT_PRO,
+	}
+};
+
+static int __init init_rc_map_dntv_live_dvbt_pro(void)
+{
+	return rc_map_register(&dntv_live_dvbt_pro_map);
+}
+
+static void __exit exit_rc_map_dntv_live_dvbt_pro(void)
+{
+	rc_map_unregister(&dntv_live_dvbt_pro_map);
+}
+
+module_init(init_rc_map_dntv_live_dvbt_pro)
+module_exit(exit_rc_map_dntv_live_dvbt_pro)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-dvbsky.c b/drivers/media/rc/keymaps/rc-dvbsky.c
new file mode 100644
index 0000000..c5115a1
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-dvbsky.c
@@ -0,0 +1,78 @@
+/* rc-dvbsky.c - Keytable for DVBSky Remote Controllers
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ *
+ * Copyright (c) 2010-2012 by Nibble Max <nibble.max@gmail.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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+/*
+ * This table contains the complete RC5 code, instead of just the data part
+ */
+
+static struct rc_map_table rc5_dvbsky[] = {
+	{ 0x0000, KEY_0 },
+	{ 0x0001, KEY_1 },
+	{ 0x0002, KEY_2 },
+	{ 0x0003, KEY_3 },
+	{ 0x0004, KEY_4 },
+	{ 0x0005, KEY_5 },
+	{ 0x0006, KEY_6 },
+	{ 0x0007, KEY_7 },
+	{ 0x0008, KEY_8 },
+	{ 0x0009, KEY_9 },
+	{ 0x000a, KEY_MUTE },
+	{ 0x000d, KEY_OK },
+	{ 0x000b, KEY_STOP },
+	{ 0x000c, KEY_EXIT },
+	{ 0x000e, KEY_CAMERA }, /*Snap shot*/
+	{ 0x000f, KEY_SUBTITLE }, /*PIP*/
+	{ 0x0010, KEY_VOLUMEUP },
+	{ 0x0011, KEY_VOLUMEDOWN },
+	{ 0x0012, KEY_FAVORITES },
+	{ 0x0013, KEY_LIST }, /*Info*/
+	{ 0x0016, KEY_PAUSE },
+	{ 0x0017, KEY_PLAY },
+	{ 0x001f, KEY_RECORD },
+	{ 0x0020, KEY_CHANNELDOWN },
+	{ 0x0021, KEY_CHANNELUP },
+	{ 0x0025, KEY_POWER2 },
+	{ 0x0026, KEY_REWIND },
+	{ 0x0027, KEY_FASTFORWARD },
+	{ 0x0029, KEY_LAST },
+	{ 0x002b, KEY_MENU },
+	{ 0x002c, KEY_EPG },
+	{ 0x002d, KEY_ZOOM },
+};
+
+static struct rc_map_list rc5_dvbsky_map = {
+	.map = {
+		.scan    = rc5_dvbsky,
+		.size    = ARRAY_SIZE(rc5_dvbsky),
+		.rc_type = RC_TYPE_RC5,
+		.name    = RC_MAP_DVBSKY,
+	}
+};
+
+static int __init init_rc_map_rc5_dvbsky(void)
+{
+	return rc_map_register(&rc5_dvbsky_map);
+}
+
+static void __exit exit_rc_map_rc5_dvbsky(void)
+{
+	rc_map_unregister(&rc5_dvbsky_map);
+}
+
+module_init(init_rc_map_rc5_dvbsky)
+module_exit(exit_rc_map_rc5_dvbsky)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nibble Max <nibble.max@gmail.com>");
diff --git a/drivers/media/rc/keymaps/rc-em-terratec.c b/drivers/media/rc/keymaps/rc-em-terratec.c
new file mode 100644
index 0000000..7f1e06b
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-em-terratec.c
@@ -0,0 +1,70 @@
+/* em-terratec.h - Keytable for em_terratec Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table 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 },
+};
+
+static struct rc_map_list em_terratec_map = {
+	.map = {
+		.scan    = em_terratec,
+		.size    = ARRAY_SIZE(em_terratec),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_EM_TERRATEC,
+	}
+};
+
+static int __init init_rc_map_em_terratec(void)
+{
+	return rc_map_register(&em_terratec_map);
+}
+
+static void __exit exit_rc_map_em_terratec(void)
+{
+	rc_map_unregister(&em_terratec_map);
+}
+
+module_init(init_rc_map_em_terratec)
+module_exit(exit_rc_map_em_terratec)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-encore-enltv-fm53.c b/drivers/media/rc/keymaps/rc-encore-enltv-fm53.c
new file mode 100644
index 0000000..4fc3904
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-encore-enltv-fm53.c
@@ -0,0 +1,82 @@
+/* encore-enltv-fm53.h - Keytable for encore_enltv_fm53 Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Encore ENLTV-FM v5.3
+   Mauro Carvalho Chehab <mchehab@infradead.org>
+ */
+
+static struct rc_map_table 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 */
+};
+
+static struct rc_map_list encore_enltv_fm53_map = {
+	.map = {
+		.scan    = encore_enltv_fm53,
+		.size    = ARRAY_SIZE(encore_enltv_fm53),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_ENCORE_ENLTV_FM53,
+	}
+};
+
+static int __init init_rc_map_encore_enltv_fm53(void)
+{
+	return rc_map_register(&encore_enltv_fm53_map);
+}
+
+static void __exit exit_rc_map_encore_enltv_fm53(void)
+{
+	rc_map_unregister(&encore_enltv_fm53_map);
+}
+
+module_init(init_rc_map_encore_enltv_fm53)
+module_exit(exit_rc_map_encore_enltv_fm53)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-encore-enltv.c b/drivers/media/rc/keymaps/rc-encore-enltv.c
new file mode 100644
index 0000000..f1914e2
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-encore-enltv.c
@@ -0,0 +1,113 @@
+/* encore-enltv.h - Keytable for encore_enltv Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Encore ENLTV-FM  - black plastic, white front cover with white glowing buttons
+    Juan Pablo Sormani <sorman@gmail.com> */
+
+static struct rc_map_table 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_CAMERA },		/* 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_TEXT },		/* teletext */
+
+
+	{ 0x59, KEY_RED },		/* AP1 */
+	{ 0x41, KEY_GREEN },		/* AP2 */
+	{ 0x47, KEY_YELLOW },		/* AP3 */
+	{ 0x57, KEY_BLUE },		/* AP4 */
+};
+
+static struct rc_map_list encore_enltv_map = {
+	.map = {
+		.scan    = encore_enltv,
+		.size    = ARRAY_SIZE(encore_enltv),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_ENCORE_ENLTV,
+	}
+};
+
+static int __init init_rc_map_encore_enltv(void)
+{
+	return rc_map_register(&encore_enltv_map);
+}
+
+static void __exit exit_rc_map_encore_enltv(void)
+{
+	rc_map_unregister(&encore_enltv_map);
+}
+
+module_init(init_rc_map_encore_enltv)
+module_exit(exit_rc_map_encore_enltv)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-encore-enltv2.c b/drivers/media/rc/keymaps/rc-encore-enltv2.c
new file mode 100644
index 0000000..9c6c552
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-encore-enltv2.c
@@ -0,0 +1,91 @@
+/* encore-enltv2.h - Keytable for encore_enltv2 Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Encore ENLTV2-FM  - silver plastic - "Wand Media" written at the botton
+    Mauro Carvalho Chehab <mchehab@infradead.org> */
+
+static struct rc_map_table 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_VIDEO },		/* 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 },
+};
+
+static struct rc_map_list encore_enltv2_map = {
+	.map = {
+		.scan    = encore_enltv2,
+		.size    = ARRAY_SIZE(encore_enltv2),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_ENCORE_ENLTV2,
+	}
+};
+
+static int __init init_rc_map_encore_enltv2(void)
+{
+	return rc_map_register(&encore_enltv2_map);
+}
+
+static void __exit exit_rc_map_encore_enltv2(void)
+{
+	rc_map_unregister(&encore_enltv2_map);
+}
+
+module_init(init_rc_map_encore_enltv2)
+module_exit(exit_rc_map_encore_enltv2)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-evga-indtube.c b/drivers/media/rc/keymaps/rc-evga-indtube.c
new file mode 100644
index 0000000..2370d2a
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-evga-indtube.c
@@ -0,0 +1,62 @@
+/* evga-indtube.h - Keytable for evga_indtube Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* EVGA inDtube
+   Devin Heitmueller <devin.heitmueller@gmail.com>
+ */
+
+static struct rc_map_table 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},
+};
+
+static struct rc_map_list evga_indtube_map = {
+	.map = {
+		.scan    = evga_indtube,
+		.size    = ARRAY_SIZE(evga_indtube),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_EVGA_INDTUBE,
+	}
+};
+
+static int __init init_rc_map_evga_indtube(void)
+{
+	return rc_map_register(&evga_indtube_map);
+}
+
+static void __exit exit_rc_map_evga_indtube(void)
+{
+	rc_map_unregister(&evga_indtube_map);
+}
+
+module_init(init_rc_map_evga_indtube)
+module_exit(exit_rc_map_evga_indtube)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-eztv.c b/drivers/media/rc/keymaps/rc-eztv.c
new file mode 100644
index 0000000..b5c96ed
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-eztv.c
@@ -0,0 +1,97 @@
+/* eztv.h - Keytable for eztv Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Alfons Geser <a.geser@cox.net>
+ * updates from Job D. R. Borges <jobdrb@ig.com.br> */
+
+static struct rc_map_table 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) */
+};
+
+static struct rc_map_list eztv_map = {
+	.map = {
+		.scan    = eztv,
+		.size    = ARRAY_SIZE(eztv),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_EZTV,
+	}
+};
+
+static int __init init_rc_map_eztv(void)
+{
+	return rc_map_register(&eztv_map);
+}
+
+static void __exit exit_rc_map_eztv(void)
+{
+	rc_map_unregister(&eztv_map);
+}
+
+module_init(init_rc_map_eztv)
+module_exit(exit_rc_map_eztv)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-flydvb.c b/drivers/media/rc/keymaps/rc-flydvb.c
new file mode 100644
index 0000000..25cb89f
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-flydvb.c
@@ -0,0 +1,78 @@
+/* flydvb.h - Keytable for flydvb Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table 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_TV2 },		/* PIP */
+	{ 0x18, KEY_VIDEO },		/* 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 >>| */
+};
+
+static struct rc_map_list flydvb_map = {
+	.map = {
+		.scan    = flydvb,
+		.size    = ARRAY_SIZE(flydvb),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_FLYDVB,
+	}
+};
+
+static int __init init_rc_map_flydvb(void)
+{
+	return rc_map_register(&flydvb_map);
+}
+
+static void __exit exit_rc_map_flydvb(void)
+{
+	rc_map_unregister(&flydvb_map);
+}
+
+module_init(init_rc_map_flydvb)
+module_exit(exit_rc_map_flydvb)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-flyvideo.c b/drivers/media/rc/keymaps/rc-flyvideo.c
new file mode 100644
index 0000000..e71377d
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-flyvideo.c
@@ -0,0 +1,71 @@
+/* flyvideo.h - Keytable for flyvideo Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table 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 */
+};
+
+static struct rc_map_list flyvideo_map = {
+	.map = {
+		.scan    = flyvideo,
+		.size    = ARRAY_SIZE(flyvideo),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_FLYVIDEO,
+	}
+};
+
+static int __init init_rc_map_flyvideo(void)
+{
+	return rc_map_register(&flyvideo_map);
+}
+
+static void __exit exit_rc_map_flyvideo(void)
+{
+	rc_map_unregister(&flyvideo_map);
+}
+
+module_init(init_rc_map_flyvideo)
+module_exit(exit_rc_map_flyvideo)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-fusionhdtv-mce.c b/drivers/media/rc/keymaps/rc-fusionhdtv-mce.c
new file mode 100644
index 0000000..cf0608d
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-fusionhdtv-mce.c
@@ -0,0 +1,99 @@
+/* fusionhdtv-mce.h - Keytable for fusionhdtv_mce Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* DViCO FUSION HDTV MCE remote */
+
+static struct rc_map_table 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 },
+};
+
+static struct rc_map_list fusionhdtv_mce_map = {
+	.map = {
+		.scan    = fusionhdtv_mce,
+		.size    = ARRAY_SIZE(fusionhdtv_mce),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_FUSIONHDTV_MCE,
+	}
+};
+
+static int __init init_rc_map_fusionhdtv_mce(void)
+{
+	return rc_map_register(&fusionhdtv_mce_map);
+}
+
+static void __exit exit_rc_map_fusionhdtv_mce(void)
+{
+	rc_map_unregister(&fusionhdtv_mce_map);
+}
+
+module_init(init_rc_map_fusionhdtv_mce)
+module_exit(exit_rc_map_fusionhdtv_mce)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-gadmei-rm008z.c b/drivers/media/rc/keymaps/rc-gadmei-rm008z.c
new file mode 100644
index 0000000..03575bd
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-gadmei-rm008z.c
@@ -0,0 +1,82 @@
+/* gadmei-rm008z.h - Keytable for gadmei_rm008z Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* GADMEI UTV330+ RM008Z remote
+   Shine Liu <shinel@foxmail.com>
+ */
+
+static struct rc_map_table 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 */
+};
+
+static struct rc_map_list gadmei_rm008z_map = {
+	.map = {
+		.scan    = gadmei_rm008z,
+		.size    = ARRAY_SIZE(gadmei_rm008z),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_GADMEI_RM008Z,
+	}
+};
+
+static int __init init_rc_map_gadmei_rm008z(void)
+{
+	return rc_map_register(&gadmei_rm008z_map);
+}
+
+static void __exit exit_rc_map_gadmei_rm008z(void)
+{
+	rc_map_unregister(&gadmei_rm008z_map);
+}
+
+module_init(init_rc_map_gadmei_rm008z)
+module_exit(exit_rc_map_gadmei_rm008z)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-genius-tvgo-a11mce.c b/drivers/media/rc/keymaps/rc-genius-tvgo-a11mce.c
new file mode 100644
index 0000000..b2ab13b
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-genius-tvgo-a11mce.c
@@ -0,0 +1,85 @@
+/* genius-tvgo-a11mce.h - Keytable for genius_tvgo_a11mce Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * Remote control for the Genius TVGO A11MCE
+ * Adrian Pardini <pardo.bsso@gmail.com>
+ */
+
+static struct rc_map_table 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 },		/* -/-- */
+	/* small arrows above numbers */
+	{ 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_CAMERA },		/* Snapshot */
+	{ 0x0c, KEY_RIGHT },		/* Hide Panel */
+	/* Four buttons without label */
+	{ 0x49, KEY_RED },
+	{ 0x0b, KEY_GREEN },
+	{ 0x13, KEY_YELLOW },
+	{ 0x50, KEY_BLUE },
+};
+
+static struct rc_map_list genius_tvgo_a11mce_map = {
+	.map = {
+		.scan    = genius_tvgo_a11mce,
+		.size    = ARRAY_SIZE(genius_tvgo_a11mce),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_GENIUS_TVGO_A11MCE,
+	}
+};
+
+static int __init init_rc_map_genius_tvgo_a11mce(void)
+{
+	return rc_map_register(&genius_tvgo_a11mce_map);
+}
+
+static void __exit exit_rc_map_genius_tvgo_a11mce(void)
+{
+	rc_map_unregister(&genius_tvgo_a11mce_map);
+}
+
+module_init(init_rc_map_genius_tvgo_a11mce)
+module_exit(exit_rc_map_genius_tvgo_a11mce)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-gotview7135.c b/drivers/media/rc/keymaps/rc-gotview7135.c
new file mode 100644
index 0000000..229a36a
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-gotview7135.c
@@ -0,0 +1,80 @@
+/* gotview7135.h - Keytable for gotview7135 Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Mike Baikov <mike@baikov.com> */
+
+static struct rc_map_table 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 */
+};
+
+static struct rc_map_list gotview7135_map = {
+	.map = {
+		.scan    = gotview7135,
+		.size    = ARRAY_SIZE(gotview7135),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_GOTVIEW7135,
+	}
+};
+
+static int __init init_rc_map_gotview7135(void)
+{
+	return rc_map_register(&gotview7135_map);
+}
+
+static void __exit exit_rc_map_gotview7135(void)
+{
+	rc_map_unregister(&gotview7135_map);
+}
+
+module_init(init_rc_map_gotview7135)
+module_exit(exit_rc_map_gotview7135)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-hauppauge.c b/drivers/media/rc/keymaps/rc-hauppauge.c
new file mode 100644
index 0000000..36d57f7
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-hauppauge.c
@@ -0,0 +1,293 @@
+/* rc-hauppauge.c - Keytable for Hauppauge Remote Controllers
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * This map currently contains the code for four different RCs:
+ *	- New Hauppauge Gray;
+ *	- Old Hauppauge Gray (with a golden screen for media keys);
+ *	- Hauppauge Black;
+ *	- DSR-0112 remote bundled with Haupauge MiniStick.
+ *
+ * Copyright (c) 2010-2011 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * Hauppauge:the newer, gray remotes (seems there are multiple
+ * slightly different versions), shipped with cx88+ivtv cards.
+ *
+ * This table contains the complete RC5 code, instead of just the data part
+ */
+
+static struct rc_map_table rc5_hauppauge_new[] = {
+	/*
+	 * Remote Controller Hauppauge Gray found on modern devices
+	 * Keycodes start with address = 0x1e
+	 */
+
+	{ 0x1e3b, KEY_SELECT },		/* GO / house symbol */
+	{ 0x1e3d, KEY_POWER2 },		/* system power (green button) */
+
+	{ 0x1e1c, KEY_TV },
+	{ 0x1e18, KEY_VIDEO },		/* Videos */
+	{ 0x1e19, KEY_AUDIO },		/* Music */
+	{ 0x1e1a, KEY_CAMERA },		/* Pictures */
+
+	{ 0x1e1b, KEY_EPG },		/* Guide */
+	{ 0x1e0c, KEY_RADIO },
+
+	{ 0x1e14, KEY_UP },
+	{ 0x1e15, KEY_DOWN },
+	{ 0x1e16, KEY_LEFT },
+	{ 0x1e17, KEY_RIGHT },
+	{ 0x1e25, KEY_OK },		/* OK */
+
+	{ 0x1e1f, KEY_EXIT },		/* back/exit */
+	{ 0x1e0d, KEY_MENU },
+
+	{ 0x1e10, KEY_VOLUMEUP },
+	{ 0x1e11, KEY_VOLUMEDOWN },
+
+	{ 0x1e12, KEY_PREVIOUS },	/* previous channel */
+	{ 0x1e0f, KEY_MUTE },
+
+	{ 0x1e20, KEY_CHANNELUP },	/* channel / program + */
+	{ 0x1e21, KEY_CHANNELDOWN },	/* channel / program - */
+
+	{ 0x1e37, KEY_RECORD },		/* recording */
+	{ 0x1e36, KEY_STOP },
+
+	{ 0x1e32, KEY_REWIND },		/* backward << */
+	{ 0x1e35, KEY_PLAY },
+	{ 0x1e34, KEY_FASTFORWARD },	/* forward >> */
+
+	{ 0x1e24, KEY_PREVIOUSSONG },	/* replay |< */
+	{ 0x1e30, KEY_PAUSE },		/* pause */
+	{ 0x1e1e, KEY_NEXTSONG },	/* skip >| */
+
+	{ 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_TEXT },		/* keypad asterisk as well */
+	{ 0x1e00, KEY_0 },
+	{ 0x1e0e, KEY_SUBTITLE },	/* also the Pound key (#) */
+
+	{ 0x1e0b, KEY_RED },		/* red button */
+	{ 0x1e2e, KEY_GREEN },		/* green button */
+	{ 0x1e38, KEY_YELLOW },		/* yellow key */
+	{ 0x1e29, KEY_BLUE },		/* blue key */
+
+	/*
+	 * Old Remote Controller Hauppauge Gray with a golden screen
+	 * Keycodes start with address = 0x1f
+	 */
+	{ 0x1f3d, KEY_POWER2 },		/* system power (green button) */
+	{ 0x1f3b, KEY_SELECT },		/* GO */
+
+	/* Keys 0 to 9 */
+	{ 0x1f00, KEY_0 },
+	{ 0x1f01, KEY_1 },
+	{ 0x1f02, KEY_2 },
+	{ 0x1f03, KEY_3 },
+	{ 0x1f04, KEY_4 },
+	{ 0x1f05, KEY_5 },
+	{ 0x1f06, KEY_6 },
+	{ 0x1f07, KEY_7 },
+	{ 0x1f08, KEY_8 },
+	{ 0x1f09, KEY_9 },
+
+	{ 0x1f1f, KEY_EXIT },		/* back/exit */
+	{ 0x1f0d, KEY_MENU },
+
+	{ 0x1f10, KEY_VOLUMEUP },
+	{ 0x1f11, KEY_VOLUMEDOWN },
+	{ 0x1f20, KEY_CHANNELUP },	/* channel / program + */
+	{ 0x1f21, KEY_CHANNELDOWN },	/* channel / program - */
+	{ 0x1f25, KEY_ENTER },		/* OK */
+
+	{ 0x1f0b, KEY_RED },		/* red button */
+	{ 0x1f2e, KEY_GREEN },		/* green button */
+	{ 0x1f38, KEY_YELLOW },		/* yellow key */
+	{ 0x1f29, KEY_BLUE },		/* blue key */
+
+	{ 0x1f0f, KEY_MUTE },
+	{ 0x1f0c, KEY_RADIO },		/* There's no indicator on this key */
+	{ 0x1f3c, KEY_ZOOM },		/* full */
+
+	{ 0x1f32, KEY_REWIND },		/* backward << */
+	{ 0x1f35, KEY_PLAY },
+	{ 0x1f34, KEY_FASTFORWARD },	/* forward >> */
+
+	{ 0x1f37, KEY_RECORD },		/* recording */
+	{ 0x1f36, KEY_STOP },
+	{ 0x1f30, KEY_PAUSE },		/* pause */
+
+	{ 0x1f24, KEY_PREVIOUSSONG },	/* replay |< */
+	{ 0x1f1e, KEY_NEXTSONG },	/* skip >| */
+
+	/*
+	 * Keycodes for DSR-0112 remote bundled with Haupauge MiniStick
+	 * Keycodes start with address = 0x1d
+	 */
+	{ 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_PREVIOUS },        /* Prev.Ch .. ??? */
+	{ 0x1d14, KEY_UP },
+	{ 0x1d15, KEY_DOWN },
+	{ 0x1d16, KEY_LEFT },
+	{ 0x1d17, KEY_RIGHT },
+	{ 0x1d1c, KEY_TV },
+	{ 0x1d1e, KEY_NEXT },           /* >|             */
+	{ 0x1d1f, KEY_EXIT },
+	{ 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 },
+	{ 0x1d3f, KEY_HOME },
+
+	/*
+	 * Keycodes for PT# R-005 remote bundled with Haupauge HVR-930C
+	 * Keycodes start with address = 0x1c
+	 */
+	{ 0x1c3b, KEY_GOTO },
+	{ 0x1c3d, KEY_POWER },
+
+	{ 0x1c14, KEY_UP },
+	{ 0x1c15, KEY_DOWN },
+	{ 0x1c16, KEY_LEFT },
+	{ 0x1c17, KEY_RIGHT },
+	{ 0x1c25, KEY_OK },
+
+	{ 0x1c00, KEY_0 },
+	{ 0x1c01, KEY_1 },
+	{ 0x1c02, KEY_2 },
+	{ 0x1c03, KEY_3 },
+	{ 0x1c04, KEY_4 },
+	{ 0x1c05, KEY_5 },
+	{ 0x1c06, KEY_6 },
+	{ 0x1c07, KEY_7 },
+	{ 0x1c08, KEY_8 },
+	{ 0x1c09, KEY_9 },
+
+	{ 0x1c1f, KEY_EXIT },	/* BACK */
+	{ 0x1c0d, KEY_MENU },
+	{ 0x1c1c, KEY_TV },
+
+	{ 0x1c10, KEY_VOLUMEUP },
+	{ 0x1c11, KEY_VOLUMEDOWN },
+
+	{ 0x1c20, KEY_CHANNELUP },
+	{ 0x1c21, KEY_CHANNELDOWN },
+
+	{ 0x1c0f, KEY_MUTE },
+	{ 0x1c12, KEY_PREVIOUS }, /* Prev */
+
+	{ 0x1c36, KEY_STOP },
+	{ 0x1c37, KEY_RECORD },
+
+	{ 0x1c24, KEY_LAST },           /* <|             */
+	{ 0x1c1e, KEY_NEXT },           /* >|             */
+
+	{ 0x1c0a, KEY_TEXT },
+	{ 0x1c0e, KEY_SUBTITLE },	/* CC */
+
+	{ 0x1c32, KEY_REWIND },
+	{ 0x1c30, KEY_PAUSE },
+	{ 0x1c35, KEY_PLAY },
+	{ 0x1c34, KEY_FASTFORWARD },
+
+	/*
+	 * Keycodes for the old Black Remote Controller
+	 * This one also uses RC-5 protocol
+	 * Keycodes start with address = 0x00
+	 */
+	{ 0x001f, KEY_TV },
+	{ 0x0020, KEY_CHANNELUP },
+	{ 0x000c, KEY_RADIO },
+
+	{ 0x0011, KEY_VOLUMEDOWN },
+	{ 0x002e, KEY_ZOOM },		/* full screen */
+	{ 0x0010, KEY_VOLUMEUP },
+
+	{ 0x000d, KEY_MUTE },
+	{ 0x0021, KEY_CHANNELDOWN },
+	{ 0x0022, KEY_VIDEO },		/* source */
+
+	{ 0x0001, KEY_1 },
+	{ 0x0002, KEY_2 },
+	{ 0x0003, KEY_3 },
+
+	{ 0x0004, KEY_4 },
+	{ 0x0005, KEY_5 },
+	{ 0x0006, KEY_6 },
+
+	{ 0x0007, KEY_7 },
+	{ 0x0008, KEY_8 },
+	{ 0x0009, KEY_9 },
+
+	{ 0x001e, KEY_RED },	/* Reserved */
+	{ 0x0000, KEY_0 },
+	{ 0x0026, KEY_SLEEP },	/* Minimize */
+};
+
+static struct rc_map_list rc5_hauppauge_new_map = {
+	.map = {
+		.scan    = rc5_hauppauge_new,
+		.size    = ARRAY_SIZE(rc5_hauppauge_new),
+		.rc_type = RC_TYPE_RC5,
+		.name    = RC_MAP_HAUPPAUGE,
+	}
+};
+
+static int __init init_rc_map_rc5_hauppauge_new(void)
+{
+	return rc_map_register(&rc5_hauppauge_new_map);
+}
+
+static void __exit exit_rc_map_rc5_hauppauge_new(void)
+{
+	rc_map_unregister(&rc5_hauppauge_new_map);
+}
+
+module_init(init_rc_map_rc5_hauppauge_new)
+module_exit(exit_rc_map_rc5_hauppauge_new)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-imon-mce.c b/drivers/media/rc/keymaps/rc-imon-mce.c
new file mode 100644
index 0000000..f0da960
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-imon-mce.c
@@ -0,0 +1,143 @@
+/* rc5-imon-mce.c - Keytable for Windows Media Center RC-6 remotes for use
+ * with the SoundGraph iMON/Antec Veris hardware IR decoder
+ *
+ * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* mce-mode imon mce remote key table */
+static struct rc_map_table imon_mce[] = {
+	/* keys sorted mostly by frequency of use to optimize lookups */
+	{ 0x800ff415, KEY_REWIND },
+	{ 0x800ff414, KEY_FASTFORWARD },
+	{ 0x800ff41b, KEY_PREVIOUS },
+	{ 0x800ff41a, KEY_NEXT },
+
+	{ 0x800ff416, KEY_PLAY },
+	{ 0x800ff418, KEY_PAUSE },
+	{ 0x800ff419, KEY_STOP },
+	{ 0x800ff417, KEY_RECORD },
+
+	{ 0x02000052, KEY_UP },
+	{ 0x02000051, KEY_DOWN },
+	{ 0x02000050, KEY_LEFT },
+	{ 0x0200004f, KEY_RIGHT },
+
+	{ 0x800ff41e, KEY_UP },
+	{ 0x800ff41f, KEY_DOWN },
+	{ 0x800ff420, KEY_LEFT },
+	{ 0x800ff421, KEY_RIGHT },
+
+	/* 0x800ff40b also KEY_NUMERIC_POUND on some receivers */
+	{ 0x800ff40b, KEY_ENTER },
+	{ 0x02000028, KEY_ENTER },
+/* the OK and Enter buttons decode to the same value on some remotes
+	{ 0x02000028, KEY_OK }, */
+	{ 0x800ff422, KEY_OK },
+	{ 0x0200002a, KEY_EXIT },
+	{ 0x800ff423, KEY_EXIT },
+	{ 0x02000029, KEY_DELETE },
+	/* 0x800ff40a also KEY_NUMERIC_STAR on some receivers */
+	{ 0x800ff40a, KEY_DELETE },
+
+	{ 0x800ff40e, KEY_MUTE },
+	{ 0x800ff410, KEY_VOLUMEUP },
+	{ 0x800ff411, KEY_VOLUMEDOWN },
+	{ 0x800ff412, KEY_CHANNELUP },
+	{ 0x800ff413, KEY_CHANNELDOWN },
+
+	{ 0x0200001e, KEY_NUMERIC_1 },
+	{ 0x0200001f, KEY_NUMERIC_2 },
+	{ 0x02000020, KEY_NUMERIC_3 },
+	{ 0x02000021, KEY_NUMERIC_4 },
+	{ 0x02000022, KEY_NUMERIC_5 },
+	{ 0x02000023, KEY_NUMERIC_6 },
+	{ 0x02000024, KEY_NUMERIC_7 },
+	{ 0x02000025, KEY_NUMERIC_8 },
+	{ 0x02000026, KEY_NUMERIC_9 },
+	{ 0x02000027, KEY_NUMERIC_0 },
+
+	{ 0x800ff401, KEY_NUMERIC_1 },
+	{ 0x800ff402, KEY_NUMERIC_2 },
+	{ 0x800ff403, KEY_NUMERIC_3 },
+	{ 0x800ff404, KEY_NUMERIC_4 },
+	{ 0x800ff405, KEY_NUMERIC_5 },
+	{ 0x800ff406, KEY_NUMERIC_6 },
+	{ 0x800ff407, KEY_NUMERIC_7 },
+	{ 0x800ff408, KEY_NUMERIC_8 },
+	{ 0x800ff409, KEY_NUMERIC_9 },
+	{ 0x800ff400, KEY_NUMERIC_0 },
+
+	{ 0x02200025, KEY_NUMERIC_STAR },
+	{ 0x02200020, KEY_NUMERIC_POUND },
+	/* 0x800ff41d also KEY_BLUE on some receivers */
+	{ 0x800ff41d, KEY_NUMERIC_STAR },
+	/* 0x800ff41c also KEY_PREVIOUS on some receivers */
+	{ 0x800ff41c, KEY_NUMERIC_POUND },
+
+	{ 0x800ff446, KEY_TV },
+	{ 0x800ff447, KEY_AUDIO }, /* My Music */
+	{ 0x800ff448, KEY_PVR }, /* RecordedTV */
+	{ 0x800ff449, KEY_CAMERA },
+	{ 0x800ff44a, KEY_VIDEO },
+	/* 0x800ff424 also KEY_MENU on some receivers */
+	{ 0x800ff424, KEY_DVD },
+	/* 0x800ff425 also KEY_GREEN on some receivers */
+	{ 0x800ff425, KEY_TUNER }, /* LiveTV */
+	{ 0x800ff450, KEY_RADIO },
+
+	{ 0x800ff44c, KEY_LANGUAGE },
+	{ 0x800ff427, KEY_ZOOM }, /* Aspect */
+
+	{ 0x800ff45b, KEY_RED },
+	{ 0x800ff45c, KEY_GREEN },
+	{ 0x800ff45d, KEY_YELLOW },
+	{ 0x800ff45e, KEY_BLUE },
+
+	{ 0x800ff466, KEY_RED },
+	/* { 0x800ff425, KEY_GREEN }, */
+	{ 0x800ff468, KEY_YELLOW },
+	/* { 0x800ff41d, KEY_BLUE }, */
+
+	{ 0x800ff40f, KEY_INFO },
+	{ 0x800ff426, KEY_EPG }, /* Guide */
+	{ 0x800ff45a, KEY_SUBTITLE }, /* Caption/Teletext */
+	{ 0x800ff44d, KEY_TITLE },
+
+	{ 0x800ff40c, KEY_POWER },
+	{ 0x800ff40d, KEY_MEDIA }, /* Windows MCE button */
+
+};
+
+static struct rc_map_list imon_mce_map = {
+	.map = {
+		.scan    = imon_mce,
+		.size    = ARRAY_SIZE(imon_mce),
+		/* its RC6, but w/a hardware decoder */
+		.rc_type = RC_TYPE_RC6_MCE,
+		.name    = RC_MAP_IMON_MCE,
+	}
+};
+
+static int __init init_rc_map_imon_mce(void)
+{
+	return rc_map_register(&imon_mce_map);
+}
+
+static void __exit exit_rc_map_imon_mce(void)
+{
+	rc_map_unregister(&imon_mce_map);
+}
+
+module_init(init_rc_map_imon_mce)
+module_exit(exit_rc_map_imon_mce)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
diff --git a/drivers/media/rc/keymaps/rc-imon-pad.c b/drivers/media/rc/keymaps/rc-imon-pad.c
new file mode 100644
index 0000000..999c629
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-imon-pad.c
@@ -0,0 +1,157 @@
+/* rc5-imon-pad.c - Keytable for SoundGraph iMON PAD and Antec Veris
+ * RM-200 Remote Control
+ *
+ * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * standard imon remote key table, which isn't really entirely
+ * "standard", as different receivers decode the same key on the
+ * same remote to different hex codes, and the silkscreened names
+ * vary a bit between the SoundGraph and Antec remotes... ugh.
+ */
+static struct rc_map_table imon_pad[] = {
+	/* keys sorted mostly by frequency of use to optimize lookups */
+	{ 0x2a8195b7, KEY_REWIND },
+	{ 0x298315b7, KEY_REWIND },
+	{ 0x2b8115b7, KEY_FASTFORWARD },
+	{ 0x2b8315b7, KEY_FASTFORWARD },
+	{ 0x2b9115b7, KEY_PREVIOUS },
+	{ 0x298195b7, KEY_NEXT },
+
+	{ 0x2a8115b7, KEY_PLAY },
+	{ 0x2a8315b7, KEY_PLAY },
+	{ 0x2a9115b7, KEY_PAUSE },
+	{ 0x2b9715b7, KEY_STOP },
+	{ 0x298115b7, KEY_RECORD },
+
+	{ 0x01008000, KEY_UP },
+	{ 0x01007f00, KEY_DOWN },
+	{ 0x01000080, KEY_LEFT },
+	{ 0x0100007f, KEY_RIGHT },
+
+	{ 0x2aa515b7, KEY_UP },
+	{ 0x289515b7, KEY_DOWN },
+	{ 0x29a515b7, KEY_LEFT },
+	{ 0x2ba515b7, KEY_RIGHT },
+
+	{ 0x0200002c, KEY_SPACE }, /* Select/Space */
+	{ 0x2a9315b7, KEY_SPACE }, /* Select/Space */
+	{ 0x02000028, KEY_ENTER },
+	{ 0x28a195b7, KEY_ENTER },
+	{ 0x288195b7, KEY_EXIT },
+	{ 0x02000029, KEY_ESC },
+	{ 0x2bb715b7, KEY_ESC },
+	{ 0x0200002a, KEY_BACKSPACE },
+	{ 0x28a115b7, KEY_BACKSPACE },
+
+	{ 0x2b9595b7, KEY_MUTE },
+	{ 0x28a395b7, KEY_VOLUMEUP },
+	{ 0x28a595b7, KEY_VOLUMEDOWN },
+	{ 0x289395b7, KEY_CHANNELUP },
+	{ 0x288795b7, KEY_CHANNELDOWN },
+
+	{ 0x0200001e, KEY_NUMERIC_1 },
+	{ 0x0200001f, KEY_NUMERIC_2 },
+	{ 0x02000020, KEY_NUMERIC_3 },
+	{ 0x02000021, KEY_NUMERIC_4 },
+	{ 0x02000022, KEY_NUMERIC_5 },
+	{ 0x02000023, KEY_NUMERIC_6 },
+	{ 0x02000024, KEY_NUMERIC_7 },
+	{ 0x02000025, KEY_NUMERIC_8 },
+	{ 0x02000026, KEY_NUMERIC_9 },
+	{ 0x02000027, KEY_NUMERIC_0 },
+
+	{ 0x28b595b7, KEY_NUMERIC_1 },
+	{ 0x2bb195b7, KEY_NUMERIC_2 },
+	{ 0x28b195b7, KEY_NUMERIC_3 },
+	{ 0x2a8595b7, KEY_NUMERIC_4 },
+	{ 0x299595b7, KEY_NUMERIC_5 },
+	{ 0x2aa595b7, KEY_NUMERIC_6 },
+	{ 0x2b9395b7, KEY_NUMERIC_7 },
+	{ 0x2a8515b7, KEY_NUMERIC_8 },
+	{ 0x2aa115b7, KEY_NUMERIC_9 },
+	{ 0x2ba595b7, KEY_NUMERIC_0 },
+
+	{ 0x02200025, KEY_NUMERIC_STAR },
+	{ 0x28b515b7, KEY_NUMERIC_STAR },
+	{ 0x02200020, KEY_NUMERIC_POUND },
+	{ 0x29a115b7, KEY_NUMERIC_POUND },
+
+	{ 0x2b8515b7, KEY_VIDEO },
+	{ 0x299195b7, KEY_AUDIO },
+	{ 0x2ba115b7, KEY_IMAGES },
+	{ 0x28a515b7, KEY_TV },
+	{ 0x29a395b7, KEY_DVD },
+	{ 0x29a295b7, KEY_DVD },
+
+	/* the Menu key between DVD and Subtitle on the RM-200... */
+	{ 0x2ba385b7, KEY_MENU },
+	{ 0x2ba395b7, KEY_MENU },
+
+	{ 0x288515b7, KEY_BOOKMARKS },
+	{ 0x2ab715b7, KEY_CAMERA }, /* Thumbnail */
+	{ 0x298595b7, KEY_SUBTITLE },
+	{ 0x2b8595b7, KEY_LANGUAGE },
+
+	{ 0x29a595b7, KEY_ZOOM },
+	{ 0x2aa395b7, KEY_SCREEN }, /* FullScreen */
+
+	{ 0x299115b7, KEY_KEYBOARD },
+	{ 0x299135b7, KEY_KEYBOARD },
+
+	{ 0x01010000, BTN_LEFT },
+	{ 0x01020000, BTN_RIGHT },
+	{ 0x01010080, BTN_LEFT },
+	{ 0x01020080, BTN_RIGHT },
+	{ 0x688301b7, BTN_LEFT },
+	{ 0x688481b7, BTN_RIGHT },
+
+	{ 0x2a9395b7, KEY_CYCLEWINDOWS }, /* TaskSwitcher */
+	{ 0x2b8395b7, KEY_TIME }, /* Timer */
+
+	{ 0x289115b7, KEY_POWER },
+	{ 0x29b195b7, KEY_EJECTCD }, /* the one next to play */
+	{ 0x299395b7, KEY_EJECTCLOSECD }, /* eject (by TaskSw) */
+
+	{ 0x02800000, KEY_CONTEXT_MENU }, /* Left Menu */
+	{ 0x2b8195b7, KEY_CONTEXT_MENU }, /* Left Menu*/
+	{ 0x02000065, KEY_COMPOSE }, /* RightMenu */
+	{ 0x28b715b7, KEY_COMPOSE }, /* RightMenu */
+	{ 0x2ab195b7, KEY_MEDIA }, /* Go or MultiMon */
+	{ 0x29b715b7, KEY_DASHBOARD }, /* AppLauncher */
+};
+
+static struct rc_map_list imon_pad_map = {
+	.map = {
+		.scan    = imon_pad,
+		.size    = ARRAY_SIZE(imon_pad),
+		/* actual protocol details unknown, hardware decoder */
+		.rc_type = RC_TYPE_OTHER,
+		.name    = RC_MAP_IMON_PAD,
+	}
+};
+
+static int __init init_rc_map_imon_pad(void)
+{
+	return rc_map_register(&imon_pad_map);
+}
+
+static void __exit exit_rc_map_imon_pad(void)
+{
+	rc_map_unregister(&imon_pad_map);
+}
+
+module_init(init_rc_map_imon_pad)
+module_exit(exit_rc_map_imon_pad)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
diff --git a/drivers/media/rc/keymaps/rc-iodata-bctv7e.c b/drivers/media/rc/keymaps/rc-iodata-bctv7e.c
new file mode 100644
index 0000000..9ee154c
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-iodata-bctv7e.c
@@ -0,0 +1,89 @@
+/* iodata-bctv7e.h - Keytable for iodata_bctv7e Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* IO-DATA BCTV7E Remote */
+
+static struct rc_map_table 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_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 >| */
+};
+
+static struct rc_map_list iodata_bctv7e_map = {
+	.map = {
+		.scan    = iodata_bctv7e,
+		.size    = ARRAY_SIZE(iodata_bctv7e),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_IODATA_BCTV7E,
+	}
+};
+
+static int __init init_rc_map_iodata_bctv7e(void)
+{
+	return rc_map_register(&iodata_bctv7e_map);
+}
+
+static void __exit exit_rc_map_iodata_bctv7e(void)
+{
+	rc_map_unregister(&iodata_bctv7e_map);
+}
+
+module_init(init_rc_map_iodata_bctv7e)
+module_exit(exit_rc_map_iodata_bctv7e)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-it913x-v1.c b/drivers/media/rc/keymaps/rc-it913x-v1.c
new file mode 100644
index 0000000..0ac775f
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-it913x-v1.c
@@ -0,0 +1,95 @@
+/* ITE Generic remotes Version 1
+ *
+ * Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+
+static struct rc_map_table it913x_v1_rc[] = {
+	/* Type 1 */
+	{ 0x61d601, KEY_VIDEO },           /* Source */
+	{ 0x61d602, KEY_3 },
+	{ 0x61d603, KEY_POWER },           /* ShutDown */
+	{ 0x61d604, KEY_1 },
+	{ 0x61d605, KEY_5 },
+	{ 0x61d606, KEY_6 },
+	{ 0x61d607, KEY_CHANNELDOWN },     /* CH- */
+	{ 0x61d608, KEY_2 },
+	{ 0x61d609, KEY_CHANNELUP },       /* CH+ */
+	{ 0x61d60a, KEY_9 },
+	{ 0x61d60b, KEY_ZOOM },            /* Zoom */
+	{ 0x61d60c, KEY_7 },
+	{ 0x61d60d, KEY_8 },
+	{ 0x61d60e, KEY_VOLUMEUP },        /* Vol+ */
+	{ 0x61d60f, KEY_4 },
+	{ 0x61d610, KEY_ESC },             /* [back up arrow] */
+	{ 0x61d611, KEY_0 },
+	{ 0x61d612, KEY_OK },              /* [enter arrow] */
+	{ 0x61d613, KEY_VOLUMEDOWN },      /* Vol- */
+	{ 0x61d614, KEY_RECORD },          /* Rec */
+	{ 0x61d615, KEY_STOP },            /* Stop */
+	{ 0x61d616, KEY_PLAY },            /* Play */
+	{ 0x61d617, KEY_MUTE },            /* Mute */
+	{ 0x61d618, KEY_UP },
+	{ 0x61d619, KEY_DOWN },
+	{ 0x61d61a, KEY_LEFT },
+	{ 0x61d61b, KEY_RIGHT },
+	{ 0x61d61c, KEY_RED },
+	{ 0x61d61d, KEY_GREEN },
+	{ 0x61d61e, KEY_YELLOW },
+	{ 0x61d61f, KEY_BLUE },
+	{ 0x61d643, KEY_POWER2 },          /* [red power button] */
+	/* Type 2 - 20 buttons */
+	{ 0x807f0d, KEY_0 },
+	{ 0x807f04, KEY_1 },
+	{ 0x807f05, KEY_2 },
+	{ 0x807f06, KEY_3 },
+	{ 0x807f07, KEY_4 },
+	{ 0x807f08, KEY_5 },
+	{ 0x807f09, KEY_6 },
+	{ 0x807f0a, KEY_7 },
+	{ 0x807f1b, KEY_8 },
+	{ 0x807f1f, KEY_9 },
+	{ 0x807f12, KEY_POWER },
+	{ 0x807f01, KEY_MEDIA_REPEAT}, /* Recall */
+	{ 0x807f19, KEY_PAUSE }, /* Timeshift */
+	{ 0x807f1e, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */
+	{ 0x807f03, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/
+	{ 0x807f1a, KEY_CHANNELUP },
+	{ 0x807f02, KEY_CHANNELDOWN },
+	{ 0x807f0c, KEY_ZOOM },
+	{ 0x807f00, KEY_RECORD },
+	{ 0x807f0e, KEY_STOP },
+};
+
+static struct rc_map_list it913x_v1_map = {
+	.map = {
+		.scan    = it913x_v1_rc,
+		.size    = ARRAY_SIZE(it913x_v1_rc),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_IT913X_V1,
+	}
+};
+
+static int __init init_rc_it913x_v1_map(void)
+{
+	return rc_map_register(&it913x_v1_map);
+}
+
+static void __exit exit_rc_it913x_v1_map(void)
+{
+	rc_map_unregister(&it913x_v1_map);
+}
+
+module_init(init_rc_it913x_v1_map)
+module_exit(exit_rc_it913x_v1_map)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
diff --git a/drivers/media/rc/keymaps/rc-it913x-v2.c b/drivers/media/rc/keymaps/rc-it913x-v2.c
new file mode 100644
index 0000000..bd42a30
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-it913x-v2.c
@@ -0,0 +1,94 @@
+/* ITE Generic remotes Version 2
+ *
+ * Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+
+static struct rc_map_table it913x_v2_rc[] = {
+	/* Type 1 */
+	/* 9005 remote */
+	{ 0x807f12, KEY_POWER2 },	/* Power (RED POWER BUTTON)*/
+	{ 0x807f1a, KEY_VIDEO },	/* Source */
+	{ 0x807f1e, KEY_MUTE },		/* Mute */
+	{ 0x807f01, KEY_RECORD },	/* Record */
+	{ 0x807f02, KEY_CHANNELUP },	/* Channel+ */
+	{ 0x807f03, KEY_TIME },		/* TimeShift */
+	{ 0x807f04, KEY_VOLUMEUP },	/* Volume- */
+	{ 0x807f05, KEY_SCREEN },	/* FullScreen */
+	{ 0x807f06, KEY_VOLUMEDOWN },	/* Volume- */
+	{ 0x807f07, KEY_0 },		/* 0 */
+	{ 0x807f08, KEY_CHANNELDOWN },	/* Channel- */
+	{ 0x807f09, KEY_PREVIOUS },	/* Recall */
+	{ 0x807f0a, KEY_1 },		/* 1 */
+	{ 0x807f1b, KEY_2 },		/* 2 */
+	{ 0x807f1f, KEY_3 },		/* 3 */
+	{ 0x807f0c, KEY_4 },		/* 4 */
+	{ 0x807f0d, KEY_5 },		/* 5 */
+	{ 0x807f0e, KEY_6 },		/* 6 */
+	{ 0x807f00, KEY_7 },		/* 7 */
+	{ 0x807f0f, KEY_8 },		/* 8 */
+	{ 0x807f19, KEY_9 },		/* 9 */
+
+	/* Type 2 */
+	/* keys stereo, snapshot unassigned */
+	{ 0x866b00, KEY_0 },
+	{ 0x866b01, KEY_1 },
+	{ 0x866b02, KEY_2 },
+	{ 0x866b03, KEY_3 },
+	{ 0x866b04, KEY_4 },
+	{ 0x866b05, KEY_5 },
+	{ 0x866b06, KEY_6 },
+	{ 0x866b07, KEY_7 },
+	{ 0x866b08, KEY_8 },
+	{ 0x866b09, KEY_9 },
+	{ 0x866b12, KEY_POWER },
+	{ 0x866b13, KEY_MUTE },
+	{ 0x866b0a, KEY_PREVIOUS }, /* Recall */
+	{ 0x866b1e, KEY_PAUSE },
+	{ 0x866b0c, KEY_VOLUMEUP },
+	{ 0x866b18, KEY_VOLUMEDOWN },
+	{ 0x866b0b, KEY_CHANNELUP },
+	{ 0x866b18, KEY_CHANNELDOWN },
+	{ 0x866b10, KEY_ZOOM },
+	{ 0x866b1d, KEY_RECORD },
+	{ 0x866b0e, KEY_STOP },
+	{ 0x866b11, KEY_EPG},
+	{ 0x866b1a, KEY_FASTFORWARD },
+	{ 0x866b0f, KEY_REWIND },
+	{ 0x866b1c, KEY_TV },
+	{ 0x866b1b, KEY_TEXT },
+
+};
+
+static struct rc_map_list it913x_v2_map = {
+	.map = {
+		.scan    = it913x_v2_rc,
+		.size    = ARRAY_SIZE(it913x_v2_rc),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_IT913X_V2,
+	}
+};
+
+static int __init init_rc_it913x_v2_map(void)
+{
+	return rc_map_register(&it913x_v2_map);
+}
+
+static void __exit exit_rc_it913x_v2_map(void)
+{
+	rc_map_unregister(&it913x_v2_map);
+}
+
+module_init(init_rc_it913x_v2_map)
+module_exit(exit_rc_it913x_v2_map)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
diff --git a/drivers/media/rc/keymaps/rc-kaiomy.c b/drivers/media/rc/keymaps/rc-kaiomy.c
new file mode 100644
index 0000000..60803a7
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-kaiomy.c
@@ -0,0 +1,88 @@
+/* kaiomy.h - Keytable for kaiomy Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Kaiomy TVnPC U2
+   Mauro Carvalho Chehab <mchehab@infradead.org>
+ */
+
+static struct rc_map_table kaiomy[] = {
+	{ 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},
+};
+
+static struct rc_map_list kaiomy_map = {
+	.map = {
+		.scan    = kaiomy,
+		.size    = ARRAY_SIZE(kaiomy),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_KAIOMY,
+	}
+};
+
+static int __init init_rc_map_kaiomy(void)
+{
+	return rc_map_register(&kaiomy_map);
+}
+
+static void __exit exit_rc_map_kaiomy(void)
+{
+	rc_map_unregister(&kaiomy_map);
+}
+
+module_init(init_rc_map_kaiomy)
+module_exit(exit_rc_map_kaiomy)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-kworld-315u.c b/drivers/media/rc/keymaps/rc-kworld-315u.c
new file mode 100644
index 0000000..ba087ee
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-kworld-315u.c
@@ -0,0 +1,84 @@
+/* kworld-315u.h - Keytable for kworld_315u Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Kworld 315U
+ */
+
+static struct rc_map_table kworld_315u[] = {
+	{ 0x6143, KEY_POWER },
+	{ 0x6101, KEY_VIDEO },		/* source */
+	{ 0x610b, KEY_ZOOM },
+	{ 0x6103, KEY_POWER2 },		/* shutdown */
+
+	{ 0x6104, KEY_1 },
+	{ 0x6108, KEY_2 },
+	{ 0x6102, KEY_3 },
+	{ 0x6109, KEY_CHANNELUP },
+
+	{ 0x610f, KEY_4 },
+	{ 0x6105, KEY_5 },
+	{ 0x6106, KEY_6 },
+	{ 0x6107, KEY_CHANNELDOWN },
+
+	{ 0x610c, KEY_7 },
+	{ 0x610d, KEY_8 },
+	{ 0x610a, KEY_9 },
+	{ 0x610e, KEY_VOLUMEUP },
+
+	{ 0x6110, KEY_LAST },
+	{ 0x6111, KEY_0 },
+	{ 0x6112, KEY_ENTER },
+	{ 0x6113, KEY_VOLUMEDOWN },
+
+	{ 0x6114, KEY_RECORD },
+	{ 0x6115, KEY_STOP },
+	{ 0x6116, KEY_PLAY },
+	{ 0x6117, KEY_MUTE },
+
+	{ 0x6118, KEY_UP },
+	{ 0x6119, KEY_DOWN },
+	{ 0x611a, KEY_LEFT },
+	{ 0x611b, KEY_RIGHT },
+
+	{ 0x611c, KEY_RED },
+	{ 0x611d, KEY_GREEN },
+	{ 0x611e, KEY_YELLOW },
+	{ 0x611f, KEY_BLUE },
+};
+
+static struct rc_map_list kworld_315u_map = {
+	.map = {
+		.scan    = kworld_315u,
+		.size    = ARRAY_SIZE(kworld_315u),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_KWORLD_315U,
+	}
+};
+
+static int __init init_rc_map_kworld_315u(void)
+{
+	return rc_map_register(&kworld_315u_map);
+}
+
+static void __exit exit_rc_map_kworld_315u(void)
+{
+	rc_map_unregister(&kworld_315u_map);
+}
+
+module_init(init_rc_map_kworld_315u)
+module_exit(exit_rc_map_kworld_315u)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-kworld-pc150u.c b/drivers/media/rc/keymaps/rc-kworld-pc150u.c
new file mode 100644
index 0000000..b92e571
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-kworld-pc150u.c
@@ -0,0 +1,102 @@
+/* kworld-pc150u.c - Keytable for kworld_pc150u Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Kyle Strickland
+ *   (based on kworld-plus-tv-analog.c by
+ *    Mauro Carvalho Chehab)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Kworld PC150-U
+   Kyle Strickland <kyle@kyle.strickland.name>
+ */
+
+static struct rc_map_table kworld_pc150u[] = {
+	{ 0x0c, KEY_MEDIA },		/* Kworld key */
+	{ 0x16, KEY_EJECTCLOSECD },	/* -> ) */
+	{ 0x1d, KEY_POWER2 },
+
+	{ 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 },
+	{ 0x0a, KEY_0 },
+
+	{ 0x09, KEY_AGAIN },
+	{ 0x14, KEY_MUTE },
+
+	{ 0x1e, KEY_LAST },
+	{ 0x17, KEY_ZOOM },
+	{ 0x1f, KEY_HOMEPAGE },
+	{ 0x0e, KEY_ESC },
+
+	{ 0x20, KEY_UP },
+	{ 0x21, KEY_DOWN },
+	{ 0x42, KEY_LEFT },
+	{ 0x43, KEY_RIGHT },
+	{ 0x0b, KEY_ENTER },
+
+	{ 0x10, KEY_CHANNELUP },
+	{ 0x11, KEY_CHANNELDOWN },
+
+	{ 0x13, KEY_VOLUMEUP },
+	{ 0x12, KEY_VOLUMEDOWN },
+
+	{ 0x19, KEY_TIME},		/* Timeshift */
+	{ 0x1a, KEY_STOP},
+	{ 0x1b, KEY_RECORD},
+	{ 0x4b, KEY_EMAIL},
+
+	{ 0x40, KEY_REWIND},
+	{ 0x44, KEY_PLAYPAUSE},
+	{ 0x41, KEY_FORWARD},
+	{ 0x22, KEY_TEXT},
+
+	{ 0x15, KEY_AUDIO},		/* ((*)) */
+	{ 0x0f, KEY_MODE},		/* display ratio */
+	{ 0x1c, KEY_SYSRQ},		/* snapshot */
+	{ 0x4a, KEY_SLEEP},		/* sleep timer */
+
+	{ 0x48, KEY_SOUND},		/* switch theater mode */
+	{ 0x49, KEY_BLUE},		/* A */
+	{ 0x18, KEY_RED},		/* B */
+	{ 0x23, KEY_GREEN},		/* C */
+};
+
+static struct rc_map_list kworld_pc150u_map = {
+	.map = {
+		.scan    = kworld_pc150u,
+		.size    = ARRAY_SIZE(kworld_pc150u),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_KWORLD_PC150U,
+	}
+};
+
+static int __init init_rc_map_kworld_pc150u(void)
+{
+	return rc_map_register(&kworld_pc150u_map);
+}
+
+static void __exit exit_rc_map_kworld_pc150u(void)
+{
+	rc_map_unregister(&kworld_pc150u_map);
+}
+
+module_init(init_rc_map_kworld_pc150u)
+module_exit(exit_rc_map_kworld_pc150u)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kyle Strickland <kyle@kyle.strickland.name>");
diff --git a/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c b/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c
new file mode 100644
index 0000000..edc8685
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c
@@ -0,0 +1,100 @@
+/* kworld-plus-tv-analog.h - Keytable for kworld_plus_tv_analog Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Kworld Plus TV Analog Lite PCI IR
+   Mauro Carvalho Chehab <mchehab@infradead.org>
+ */
+
+static struct rc_map_table kworld_plus_tv_analog[] = {
+	{ 0x0c, KEY_MEDIA },		/* 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 },
+
+	/* The lower part of the IR
+	   There are several duplicated keycodes there.
+	   Most of them conflict with digits.
+	   Add mappings just to the unused scancodes.
+	   Somehow, the original driver has a way to know,
+	   but this doesn't seem to be on some GPIO.
+	   Also, it is not related to the time between keyup
+	   and keydown.
+	 */
+	{ 0x19, KEY_TIME},		/* Timeshift */
+	{ 0x1a, KEY_STOP},
+	{ 0x1b, KEY_RECORD},
+
+	{ 0x22, KEY_TEXT},
+
+	{ 0x15, KEY_AUDIO},		/* ((*)) */
+	{ 0x0f, KEY_ZOOM},
+	{ 0x1c, KEY_CAMERA},		/* snapshot */
+
+	{ 0x18, KEY_RED},		/* B */
+	{ 0x23, KEY_GREEN},		/* C */
+};
+
+static struct rc_map_list kworld_plus_tv_analog_map = {
+	.map = {
+		.scan    = kworld_plus_tv_analog,
+		.size    = ARRAY_SIZE(kworld_plus_tv_analog),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_KWORLD_PLUS_TV_ANALOG,
+	}
+};
+
+static int __init init_rc_map_kworld_plus_tv_analog(void)
+{
+	return rc_map_register(&kworld_plus_tv_analog_map);
+}
+
+static void __exit exit_rc_map_kworld_plus_tv_analog(void)
+{
+	rc_map_unregister(&kworld_plus_tv_analog_map);
+}
+
+module_init(init_rc_map_kworld_plus_tv_analog)
+module_exit(exit_rc_map_kworld_plus_tv_analog)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-leadtek-y04g0051.c b/drivers/media/rc/keymaps/rc-leadtek-y04g0051.c
new file mode 100644
index 0000000..03d762d
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-leadtek-y04g0051.c
@@ -0,0 +1,100 @@
+/*
+ * LeadTek Y04G0051 remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table leadtek_y04g0051[] = {
+	{ 0x0300, KEY_POWER2 },
+	{ 0x0303, KEY_SCREEN },
+	{ 0x0304, KEY_RIGHT },
+	{ 0x0305, KEY_1 },
+	{ 0x0306, KEY_2 },
+	{ 0x0307, KEY_3 },
+	{ 0x0308, KEY_LEFT },
+	{ 0x0309, KEY_4 },
+	{ 0x030a, KEY_5 },
+	{ 0x030b, KEY_6 },
+	{ 0x030c, KEY_UP },
+	{ 0x030d, KEY_7 },
+	{ 0x030e, KEY_8 },
+	{ 0x030f, KEY_9 },
+	{ 0x0310, KEY_DOWN },
+	{ 0x0311, KEY_AGAIN },
+	{ 0x0312, KEY_0 },
+	{ 0x0313, KEY_OK },              /* 1st ok */
+	{ 0x0314, KEY_MUTE },
+	{ 0x0316, KEY_OK },              /* 2nd ok */
+	{ 0x031e, KEY_VIDEO },           /* 2nd video */
+	{ 0x031b, KEY_AUDIO },
+	{ 0x031f, KEY_TEXT },
+	{ 0x0340, KEY_SLEEP },
+	{ 0x0341, KEY_DOT },
+	{ 0x0342, KEY_REWIND },
+	{ 0x0343, KEY_PLAY },
+	{ 0x0344, KEY_FASTFORWARD },
+	{ 0x0345, KEY_TIME },
+	{ 0x0346, KEY_STOP },            /* 2nd stop */
+	{ 0x0347, KEY_RECORD },
+	{ 0x0348, KEY_CAMERA },
+	{ 0x0349, KEY_ESC },
+	{ 0x034a, KEY_NEW },
+	{ 0x034b, KEY_RED },
+	{ 0x034c, KEY_GREEN },
+	{ 0x034d, KEY_YELLOW },
+	{ 0x034e, KEY_BLUE },
+	{ 0x034f, KEY_MENU },
+	{ 0x0350, KEY_STOP },            /* 1st stop */
+	{ 0x0351, KEY_CHANNEL },
+	{ 0x0352, KEY_VIDEO },           /* 1st video */
+	{ 0x0353, KEY_EPG },
+	{ 0x0354, KEY_PREVIOUS },
+	{ 0x0355, KEY_NEXT },
+	{ 0x0356, KEY_TV },
+	{ 0x035a, KEY_VOLUMEDOWN },
+	{ 0x035b, KEY_CHANNELUP },
+	{ 0x035e, KEY_VOLUMEUP },
+	{ 0x035f, KEY_CHANNELDOWN },
+};
+
+static struct rc_map_list leadtek_y04g0051_map = {
+	.map = {
+		.scan    = leadtek_y04g0051,
+		.size    = ARRAY_SIZE(leadtek_y04g0051),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_LEADTEK_Y04G0051,
+	}
+};
+
+static int __init init_rc_map_leadtek_y04g0051(void)
+{
+	return rc_map_register(&leadtek_y04g0051_map);
+}
+
+static void __exit exit_rc_map_leadtek_y04g0051(void)
+{
+	rc_map_unregister(&leadtek_y04g0051_map);
+}
+
+module_init(init_rc_map_leadtek_y04g0051)
+module_exit(exit_rc_map_leadtek_y04g0051)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-lirc.c b/drivers/media/rc/keymaps/rc-lirc.c
new file mode 100644
index 0000000..e172f5d
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-lirc.c
@@ -0,0 +1,42 @@
+/* rc-lirc.c - Empty dummy keytable, for use when its preferred to pass
+ * all raw IR data to the lirc userspace decoder.
+ *
+ * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.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.
+ */
+
+#include <media/rc-core.h>
+#include <linux/module.h>
+
+static struct rc_map_table lirc[] = {
+	{ },
+};
+
+static struct rc_map_list lirc_map = {
+	.map = {
+		.scan    = lirc,
+		.size    = ARRAY_SIZE(lirc),
+		.rc_type = RC_TYPE_OTHER,
+		.name    = RC_MAP_LIRC,
+	}
+};
+
+static int __init init_rc_map_lirc(void)
+{
+	return rc_map_register(&lirc_map);
+}
+
+static void __exit exit_rc_map_lirc(void)
+{
+	rc_map_unregister(&lirc_map);
+}
+
+module_init(init_rc_map_lirc)
+module_exit(exit_rc_map_lirc)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
diff --git a/drivers/media/rc/keymaps/rc-lme2510.c b/drivers/media/rc/keymaps/rc-lme2510.c
new file mode 100644
index 0000000..2b0027c
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-lme2510.c
@@ -0,0 +1,111 @@
+/* LME2510 remote control
+ *
+ *
+ * Copyright (C) 2010 Malcolm Priestley (tvboxspy@gmail.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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+
+static struct rc_map_table lme2510_rc[] = {
+	/* Type 1 - 26 buttons */
+	{ 0xef12ba45, KEY_0 },
+	{ 0xef12a05f, KEY_1 },
+	{ 0xef12af50, KEY_2 },
+	{ 0xef12a25d, KEY_3 },
+	{ 0xef12be41, KEY_4 },
+	{ 0xef12f50a, KEY_5 },
+	{ 0xef12bd42, KEY_6 },
+	{ 0xef12b847, KEY_7 },
+	{ 0xef12b649, KEY_8 },
+	{ 0xef12fa05, KEY_9 },
+	{ 0xef12bc43, KEY_POWER },
+	{ 0xef12b946, KEY_SUBTITLE },
+	{ 0xef12f906, KEY_PAUSE },
+	{ 0xef12fc03, KEY_MEDIA_REPEAT},
+	{ 0xef12fd02, KEY_PAUSE },
+	{ 0xef12a15e, KEY_VOLUMEUP },
+	{ 0xef12a35c, KEY_VOLUMEDOWN },
+	{ 0xef12f609, KEY_CHANNELUP },
+	{ 0xef12e51a, KEY_CHANNELDOWN },
+	{ 0xef12e11e, KEY_PLAY },
+	{ 0xef12e41b, KEY_ZOOM },
+	{ 0xef12a659, KEY_MUTE },
+	{ 0xef12a55a, KEY_TV },
+	{ 0xef12e718, KEY_RECORD },
+	{ 0xef12f807, KEY_EPG },
+	{ 0xef12fe01, KEY_STOP },
+	/* Type 2 - 20 buttons */
+	{ 0xff40ea15, KEY_0 },
+	{ 0xff40f708, KEY_1 },
+	{ 0xff40f609, KEY_2 },
+	{ 0xff40f50a, KEY_3 },
+	{ 0xff40f30c, KEY_4 },
+	{ 0xff40f20d, KEY_5 },
+	{ 0xff40f10e, KEY_6 },
+	{ 0xff40ef10, KEY_7 },
+	{ 0xff40ee11, KEY_8 },
+	{ 0xff40ed12, KEY_9 },
+	{ 0xff40ff00, KEY_POWER },
+	{ 0xff40fb04, KEY_MEDIA_REPEAT}, /* Recall */
+	{ 0xff40e51a, KEY_PAUSE }, /* Timeshift */
+	{ 0xff40fd02, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */
+	{ 0xff40f906, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/
+	{ 0xff40fe01, KEY_CHANNELUP },
+	{ 0xff40fa05, KEY_CHANNELDOWN },
+	{ 0xff40eb14, KEY_ZOOM },
+	{ 0xff40e718, KEY_RECORD },
+	{ 0xff40e916, KEY_STOP },
+	/* Type 3 - 20 buttons */
+	{ 0xff00e31c, KEY_0 },
+	{ 0xff00f807, KEY_1 },
+	{ 0xff00ea15, KEY_2 },
+	{ 0xff00f609, KEY_3 },
+	{ 0xff00e916, KEY_4 },
+	{ 0xff00e619, KEY_5 },
+	{ 0xff00f20d, KEY_6 },
+	{ 0xff00f30c, KEY_7 },
+	{ 0xff00e718, KEY_8 },
+	{ 0xff00a15e, KEY_9 },
+	{ 0xff00ba45, KEY_POWER },
+	{ 0xff00bb44, KEY_MEDIA_REPEAT}, /* Recall */
+	{ 0xff00b54a, KEY_PAUSE }, /* Timeshift */
+	{ 0xff00b847, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */
+	{ 0xff00bc43, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/
+	{ 0xff00b946, KEY_CHANNELUP },
+	{ 0xff00bf40, KEY_CHANNELDOWN },
+	{ 0xff00f708, KEY_ZOOM },
+	{ 0xff00bd42, KEY_RECORD },
+	{ 0xff00a55a, KEY_STOP },
+};
+
+static struct rc_map_list lme2510_map = {
+	.map = {
+		.scan    = lme2510_rc,
+		.size    = ARRAY_SIZE(lme2510_rc),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_LME2510,
+	}
+};
+
+static int __init init_rc_lme2510_map(void)
+{
+	return rc_map_register(&lme2510_map);
+}
+
+static void __exit exit_rc_lme2510_map(void)
+{
+	rc_map_unregister(&lme2510_map);
+}
+
+module_init(init_rc_lme2510_map)
+module_exit(exit_rc_lme2510_map)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
diff --git a/drivers/media/rc/keymaps/rc-manli.c b/drivers/media/rc/keymaps/rc-manli.c
new file mode 100644
index 0000000..92424ef
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-manli.c
@@ -0,0 +1,135 @@
+/* manli.h - Keytable for manli Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Michael Tokarev <mjt@tls.msk.ru>
+   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.
+ */
+
+static struct rc_map_table manli[] = {
+
+	/*  0x1c            0x12  *
+	 * FUNCTION         POWER *
+	 *   FM              (|)  *
+	 *                        */
+	{ 0x1c, KEY_RADIO },	/*XXX*/
+	{ 0x12, KEY_POWER },
+
+	/*  0x01    0x02    0x03  *
+	 *   1       2       3    *
+	 *                        *
+	 *  0x04    0x05    0x06  *
+	 *   4       5       6    *
+	 *                        *
+	 *  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 },
+
+	/*  0x0a    0x00    0x17  *
+	 * RECALL    0      +100  *
+	 *                  PLUS  *
+	 *                        */
+	{ 0x0a, KEY_AGAIN },	/*XXX KEY_REWIND? */
+	{ 0x00, KEY_0 },
+	{ 0x17, KEY_DIGITS },	/*XXX*/
+
+	/*  0x14            0x10  *
+	 *  MENU            INFO  *
+	 *  OSD                   */
+	{ 0x14, KEY_MENU },
+	{ 0x10, KEY_INFO },
+
+	/*          0x0b          *
+	 *           Up           *
+	 *                        *
+	 *  0x18    0x16    0x0c  *
+	 *  Left     Ok     Right *
+	 *                        *
+	 *         0x015          *
+	 *         Down           *
+	 *                        */
+	{ 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	*/
+
+	/*  0x0f    0x1b    0x1a  *
+	 *  AUDIO   Vol+    Chan+ *
+	 *        TIMESHIFT???    *
+	 *                        *
+	 *  0x0e    0x1f    0x1e  *
+	 *  SLEEP   Vol-    Chan- *
+	 *                        */
+	{ 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_CAMERA },
+
+	/* 0x1d unused ? */
+};
+
+static struct rc_map_list manli_map = {
+	.map = {
+		.scan    = manli,
+		.size    = ARRAY_SIZE(manli),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_MANLI,
+	}
+};
+
+static int __init init_rc_map_manli(void)
+{
+	return rc_map_register(&manli_map);
+}
+
+static void __exit exit_rc_map_manli(void)
+{
+	rc_map_unregister(&manli_map);
+}
+
+module_init(init_rc_map_manli)
+module_exit(exit_rc_map_manli)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c b/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c
new file mode 100644
index 0000000..966f9b3
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c
@@ -0,0 +1,123 @@
+/*
+ * Medion X10 RF remote keytable (Digitainer variant)
+ *
+ * Copyright (C) 2012 Anssi Hannula <anssi.hannula@iki.fi>
+ *
+ * This keymap is for a variant that has a distinctive scrollwheel instead of
+ * up/down buttons (tested with P/N 40009936 / 20018268), reportedly
+ * originally shipped with Medion Digitainer but now sold separately simply as
+ * an "X10" remote.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <media/rc-map.h>
+
+static struct rc_map_table medion_x10_digitainer[] = {
+	{ 0x02, KEY_POWER },
+
+	{ 0x2c, KEY_TV },
+	{ 0x2d, KEY_VIDEO },
+	{ 0x04, KEY_DVD },    /* CD/DVD */
+	{ 0x16, KEY_TEXT },   /* "teletext" icon, i.e. a screen with lines */
+	{ 0x06, KEY_AUDIO },
+	{ 0x2e, KEY_RADIO },
+	{ 0x31, KEY_EPG },    /* a screen with an open book */
+	{ 0x05, KEY_IMAGES }, /* Photo */
+	{ 0x2f, KEY_INFO },
+
+	{ 0x78, KEY_UP },     /* scrollwheel up 1 notch */
+	/* 0x79..0x7f: 2-8 notches, driver repeats 0x78 entry */
+
+	{ 0x70, KEY_DOWN },   /* scrollwheel down 1 notch */
+	/* 0x71..0x77: 2-8 notches, driver repeats 0x70 entry */
+
+	{ 0x19, KEY_MENU },
+	{ 0x1d, KEY_LEFT },
+	{ 0x1e, KEY_OK },     /* scrollwheel press */
+	{ 0x1f, KEY_RIGHT },
+	{ 0x20, KEY_BACK },
+
+	{ 0x09, KEY_VOLUMEUP },
+	{ 0x08, KEY_VOLUMEDOWN },
+	{ 0x00, KEY_MUTE },
+
+	{ 0x1b, KEY_SELECT }, /* also has "U" rotated 90 degrees CCW */
+
+	{ 0x0b, KEY_CHANNELUP },
+	{ 0x0c, KEY_CHANNELDOWN },
+	{ 0x1c, KEY_LAST },
+
+	{ 0x32, KEY_RED },    /* also Audio */
+	{ 0x33, KEY_GREEN },  /* also Subtitle */
+	{ 0x34, KEY_YELLOW }, /* also Angle */
+	{ 0x35, KEY_BLUE },   /* also Title */
+
+	{ 0x28, KEY_STOP },
+	{ 0x29, KEY_PAUSE },
+	{ 0x25, KEY_PLAY },
+	{ 0x21, KEY_PREVIOUS },
+	{ 0x18, KEY_CAMERA },
+	{ 0x23, KEY_NEXT },
+	{ 0x24, KEY_REWIND },
+	{ 0x27, KEY_RECORD },
+	{ 0x26, KEY_FORWARD },
+
+	{ 0x0d, KEY_1 },
+	{ 0x0e, KEY_2 },
+	{ 0x0f, KEY_3 },
+	{ 0x10, KEY_4 },
+	{ 0x11, KEY_5 },
+	{ 0x12, KEY_6 },
+	{ 0x13, KEY_7 },
+	{ 0x14, KEY_8 },
+	{ 0x15, KEY_9 },
+	{ 0x17, KEY_0 },
+
+	/* these do not actually exist on this remote, but these scancodes
+	 * exist on all other Medion X10 remotes and adding them here allows
+	 * such remotes to be adequately usable with this keymap in case
+	 * this keymap is wrongly used with them (which is quite possible as
+	 * there are lots of different Medion X10 remotes): */
+	{ 0x1a, KEY_UP },
+	{ 0x22, KEY_DOWN },
+};
+
+static struct rc_map_list medion_x10_digitainer_map = {
+	.map = {
+		.scan    = medion_x10_digitainer,
+		.size    = ARRAY_SIZE(medion_x10_digitainer),
+		.rc_type = RC_TYPE_OTHER,
+		.name    = RC_MAP_MEDION_X10_DIGITAINER,
+	}
+};
+
+static int __init init_rc_map_medion_x10_digitainer(void)
+{
+	return rc_map_register(&medion_x10_digitainer_map);
+}
+
+static void __exit exit_rc_map_medion_x10_digitainer(void)
+{
+	rc_map_unregister(&medion_x10_digitainer_map);
+}
+
+module_init(init_rc_map_medion_x10_digitainer)
+module_exit(exit_rc_map_medion_x10_digitainer)
+
+MODULE_DESCRIPTION("Medion X10 RF remote keytable (Digitainer variant)");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/keymaps/rc-medion-x10-or2x.c b/drivers/media/rc/keymaps/rc-medion-x10-or2x.c
new file mode 100644
index 0000000..b077300
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-medion-x10-or2x.c
@@ -0,0 +1,108 @@
+/*
+ * Medion X10 OR22/OR24 RF remote keytable
+ *
+ * Copyright (C) 2012 Anssi Hannula <anssi.hannula@iki.fi>
+ *
+ * This keymap is for several Medion X10 remotes that have the Windows MCE
+ * button. This has been tested with a "RF VISTA Remote Control", OR24V,
+ * P/N 20035335, but should work with other variants that have the same
+ * buttons, such as OR22V and OR24E.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <media/rc-map.h>
+
+static struct rc_map_table medion_x10_or2x[] = {
+	{ 0x02, KEY_POWER },
+	{ 0x16, KEY_TEXT },   /* "T" in a box, for teletext */
+
+	{ 0x09, KEY_VOLUMEUP },
+	{ 0x08, KEY_VOLUMEDOWN },
+	{ 0x00, KEY_MUTE },
+	{ 0x0b, KEY_CHANNELUP },
+	{ 0x0c, KEY_CHANNELDOWN },
+
+	{ 0x32, KEY_RED },
+	{ 0x33, KEY_GREEN },
+	{ 0x34, KEY_YELLOW },
+	{ 0x35, KEY_BLUE },
+
+	{ 0x18, KEY_PVR },    /* record symbol inside a tv symbol */
+	{ 0x04, KEY_DVD },    /* disc symbol */
+	{ 0x31, KEY_EPG },    /* a tv schedule symbol */
+	{ 0x1c, KEY_TV },     /* play symbol inside a tv symbol */
+	{ 0x20, KEY_BACK },
+	{ 0x2f, KEY_INFO },
+
+	{ 0x1a, KEY_UP },
+	{ 0x22, KEY_DOWN },
+	{ 0x1d, KEY_LEFT },
+	{ 0x1f, KEY_RIGHT },
+	{ 0x1e, KEY_OK },
+
+	{ 0x1b, KEY_MEDIA },  /* Windows MCE button */
+
+	{ 0x21, KEY_PREVIOUS },
+	{ 0x23, KEY_NEXT },
+	{ 0x24, KEY_REWIND },
+	{ 0x26, KEY_FORWARD },
+	{ 0x25, KEY_PLAY },
+	{ 0x28, KEY_STOP },
+	{ 0x29, KEY_PAUSE },
+	{ 0x27, KEY_RECORD },
+
+	{ 0x0d, KEY_1 },
+	{ 0x0e, KEY_2 },
+	{ 0x0f, KEY_3 },
+	{ 0x10, KEY_4 },
+	{ 0x11, KEY_5 },
+	{ 0x12, KEY_6 },
+	{ 0x13, KEY_7 },
+	{ 0x14, KEY_8 },
+	{ 0x15, KEY_9 },
+	{ 0x17, KEY_0 },
+	{ 0x30, KEY_CLEAR },
+	{ 0x36, KEY_ENTER },
+	{ 0x37, KEY_NUMERIC_STAR },
+	{ 0x38, KEY_NUMERIC_POUND },
+};
+
+static struct rc_map_list medion_x10_or2x_map = {
+	.map = {
+		.scan    = medion_x10_or2x,
+		.size    = ARRAY_SIZE(medion_x10_or2x),
+		.rc_type = RC_TYPE_OTHER,
+		.name    = RC_MAP_MEDION_X10_OR2X,
+	}
+};
+
+static int __init init_rc_map_medion_x10_or2x(void)
+{
+	return rc_map_register(&medion_x10_or2x_map);
+}
+
+static void __exit exit_rc_map_medion_x10_or2x(void)
+{
+	rc_map_unregister(&medion_x10_or2x_map);
+}
+
+module_init(init_rc_map_medion_x10_or2x)
+module_exit(exit_rc_map_medion_x10_or2x)
+
+MODULE_DESCRIPTION("Medion X10 OR22/OR24 RF remote keytable");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/keymaps/rc-medion-x10.c b/drivers/media/rc/keymaps/rc-medion-x10.c
new file mode 100644
index 0000000..479cdb8
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-medion-x10.c
@@ -0,0 +1,117 @@
+/*
+ * Medion X10 RF remote keytable
+ *
+ * Copyright (C) 2011 Anssi Hannula <anssi.hannula@?ki.fi>
+ *
+ * This file is based on a keytable provided by
+ * Jan Losinski <losinski@wh2.tu-dresden.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <media/rc-map.h>
+
+static struct rc_map_table medion_x10[] = {
+	{ 0x2c, KEY_TV },    /* TV */
+	{ 0x2d, KEY_VCR },   /* VCR */
+	{ 0x04, KEY_DVD },   /* DVD */
+	{ 0x06, KEY_AUDIO }, /* MUSIC */
+
+	{ 0x2e, KEY_RADIO },     /* RADIO */
+	{ 0x05, KEY_DIRECTORY }, /* PHOTO */
+	{ 0x2f, KEY_INFO },      /* TV-PREVIEW */
+	{ 0x30, KEY_LIST },      /* CHANNEL-LST */
+
+	{ 0x1b, KEY_SETUP }, /* SETUP */
+	{ 0x31, KEY_VIDEO }, /* VIDEO DESKTOP */
+
+	{ 0x08, KEY_VOLUMEDOWN },  /* VOL - */
+	{ 0x09, KEY_VOLUMEUP },    /* VOL + */
+	{ 0x0b, KEY_CHANNELUP },   /* CHAN + */
+	{ 0x0c, KEY_CHANNELDOWN }, /* CHAN - */
+	{ 0x00, KEY_MUTE },        /* MUTE */
+
+	{ 0x32, KEY_RED }, /* red */
+	{ 0x33, KEY_GREEN }, /* green */
+	{ 0x34, KEY_YELLOW }, /* yellow */
+	{ 0x35, KEY_BLUE }, /* blue */
+	{ 0x16, KEY_TEXT }, /* TXT */
+
+	{ 0x0d, KEY_1 },
+	{ 0x0e, KEY_2 },
+	{ 0x0f, KEY_3 },
+	{ 0x10, KEY_4 },
+	{ 0x11, KEY_5 },
+	{ 0x12, KEY_6 },
+	{ 0x13, KEY_7 },
+	{ 0x14, KEY_8 },
+	{ 0x15, KEY_9 },
+	{ 0x17, KEY_0 },
+	{ 0x1c, KEY_SEARCH }, /* TV/RAD, CH SRC */
+	{ 0x20, KEY_DELETE }, /* DELETE */
+
+	{ 0x36, KEY_KEYBOARD }, /* RENAME */
+	{ 0x18, KEY_SCREEN },   /* SNAPSHOT */
+
+	{ 0x1a, KEY_UP },    /* up */
+	{ 0x22, KEY_DOWN },  /* down */
+	{ 0x1d, KEY_LEFT },  /* left */
+	{ 0x1f, KEY_RIGHT }, /* right */
+	{ 0x1e, KEY_OK },    /* OK */
+
+	{ 0x37, KEY_SELECT }, /* ACQUIRE IMAGE */
+	{ 0x38, KEY_EDIT },   /* EDIT IMAGE */
+
+	{ 0x24, KEY_REWIND },   /* rewind  (<<) */
+	{ 0x25, KEY_PLAY },     /* play    ( >) */
+	{ 0x26, KEY_FORWARD },  /* forward (>>) */
+	{ 0x27, KEY_RECORD },   /* record  ( o) */
+	{ 0x28, KEY_STOP },     /* stop    ([]) */
+	{ 0x29, KEY_PAUSE },    /* pause   ('') */
+
+	{ 0x21, KEY_PREVIOUS },        /* prev */
+	{ 0x39, KEY_SWITCHVIDEOMODE }, /* F SCR */
+	{ 0x23, KEY_NEXT },            /* next */
+	{ 0x19, KEY_MENU },            /* MENU */
+	{ 0x3a, KEY_LANGUAGE },        /* AUDIO */
+
+	{ 0x02, KEY_POWER }, /* POWER */
+};
+
+static struct rc_map_list medion_x10_map = {
+	.map = {
+		.scan    = medion_x10,
+		.size    = ARRAY_SIZE(medion_x10),
+		.rc_type = RC_TYPE_OTHER,
+		.name    = RC_MAP_MEDION_X10,
+	}
+};
+
+static int __init init_rc_map_medion_x10(void)
+{
+	return rc_map_register(&medion_x10_map);
+}
+
+static void __exit exit_rc_map_medion_x10(void)
+{
+	rc_map_unregister(&medion_x10_map);
+}
+
+module_init(init_rc_map_medion_x10)
+module_exit(exit_rc_map_medion_x10)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-msi-digivox-ii.c b/drivers/media/rc/keymaps/rc-msi-digivox-ii.c
new file mode 100644
index 0000000..2fa71d0
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-msi-digivox-ii.c
@@ -0,0 +1,68 @@
+/*
+ * MSI DIGIVOX mini II remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table msi_digivox_ii[] = {
+	{ 0x0302, KEY_2 },
+	{ 0x0303, KEY_UP },              /* up */
+	{ 0x0304, KEY_3 },
+	{ 0x0305, KEY_CHANNELDOWN },
+	{ 0x0308, KEY_5 },
+	{ 0x0309, KEY_0 },
+	{ 0x030b, KEY_8 },
+	{ 0x030d, KEY_DOWN },            /* down */
+	{ 0x0310, KEY_9 },
+	{ 0x0311, KEY_7 },
+	{ 0x0314, KEY_VOLUMEUP },
+	{ 0x0315, KEY_CHANNELUP },
+	{ 0x0316, KEY_OK },
+	{ 0x0317, KEY_POWER2 },
+	{ 0x031a, KEY_1 },
+	{ 0x031c, KEY_4 },
+	{ 0x031d, KEY_6 },
+	{ 0x031f, KEY_VOLUMEDOWN },
+};
+
+static struct rc_map_list msi_digivox_ii_map = {
+	.map = {
+		.scan    = msi_digivox_ii,
+		.size    = ARRAY_SIZE(msi_digivox_ii),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_MSI_DIGIVOX_II,
+	}
+};
+
+static int __init init_rc_map_msi_digivox_ii(void)
+{
+	return rc_map_register(&msi_digivox_ii_map);
+}
+
+static void __exit exit_rc_map_msi_digivox_ii(void)
+{
+	rc_map_unregister(&msi_digivox_ii_map);
+}
+
+module_init(init_rc_map_msi_digivox_ii)
+module_exit(exit_rc_map_msi_digivox_ii)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-msi-digivox-iii.c b/drivers/media/rc/keymaps/rc-msi-digivox-iii.c
new file mode 100644
index 0000000..303a0b7
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-msi-digivox-iii.c
@@ -0,0 +1,86 @@
+/*
+ * MSI DIGIVOX mini III remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* MSI DIGIVOX mini III */
+/* Uses NEC extended 0x61d6. */
+/* This remote seems to be same as rc-kworld-315u.c. Anyhow, add new remote
+   since rc-kworld-315u.c lacks NEC extended address byte. */
+static struct rc_map_table msi_digivox_iii[] = {
+	{ 0x61d601, KEY_VIDEO },           /* Source */
+	{ 0x61d602, KEY_3 },
+	{ 0x61d603, KEY_POWER },           /* ShutDown */
+	{ 0x61d604, KEY_1 },
+	{ 0x61d605, KEY_5 },
+	{ 0x61d606, KEY_6 },
+	{ 0x61d607, KEY_CHANNELDOWN },     /* CH- */
+	{ 0x61d608, KEY_2 },
+	{ 0x61d609, KEY_CHANNELUP },       /* CH+ */
+	{ 0x61d60a, KEY_9 },
+	{ 0x61d60b, KEY_ZOOM },            /* Zoom */
+	{ 0x61d60c, KEY_7 },
+	{ 0x61d60d, KEY_8 },
+	{ 0x61d60e, KEY_VOLUMEUP },        /* Vol+ */
+	{ 0x61d60f, KEY_4 },
+	{ 0x61d610, KEY_ESC },             /* [back up arrow] */
+	{ 0x61d611, KEY_0 },
+	{ 0x61d612, KEY_OK },              /* [enter arrow] */
+	{ 0x61d613, KEY_VOLUMEDOWN },      /* Vol- */
+	{ 0x61d614, KEY_RECORD },          /* Rec */
+	{ 0x61d615, KEY_STOP },            /* Stop */
+	{ 0x61d616, KEY_PLAY },            /* Play */
+	{ 0x61d617, KEY_MUTE },            /* Mute */
+	{ 0x61d618, KEY_UP },
+	{ 0x61d619, KEY_DOWN },
+	{ 0x61d61a, KEY_LEFT },
+	{ 0x61d61b, KEY_RIGHT },
+	{ 0x61d61c, KEY_RED },
+	{ 0x61d61d, KEY_GREEN },
+	{ 0x61d61e, KEY_YELLOW },
+	{ 0x61d61f, KEY_BLUE },
+	{ 0x61d643, KEY_POWER2 },          /* [red power button] */
+};
+
+static struct rc_map_list msi_digivox_iii_map = {
+	.map = {
+		.scan    = msi_digivox_iii,
+		.size    = ARRAY_SIZE(msi_digivox_iii),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_MSI_DIGIVOX_III,
+	}
+};
+
+static int __init init_rc_map_msi_digivox_iii(void)
+{
+	return rc_map_register(&msi_digivox_iii_map);
+}
+
+static void __exit exit_rc_map_msi_digivox_iii(void)
+{
+	rc_map_unregister(&msi_digivox_iii_map);
+}
+
+module_init(init_rc_map_msi_digivox_iii)
+module_exit(exit_rc_map_msi_digivox_iii)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c b/drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c
new file mode 100644
index 0000000..fd7a55c
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-msi-tvanywhere-plus.c
@@ -0,0 +1,124 @@
+/* msi-tvanywhere-plus.h - Keytable for msi_tvanywhere_plus Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+  Keycodes for remote on the MSI TV@nywhere Plus. The controller IC on the card
+  is marked "KS003". The controller is I2C at address 0x30, but does not seem
+  to respond to probes until a read is performed from a valid device.
+  I don't know why...
+
+  Note: This remote may be of similar or identical design to the
+  Pixelview remote (?).  The raw codes and duplicate button codes
+  appear to be the same.
+
+  Henry Wong <henry@stuffedcow.net>
+  Some changes to formatting and keycodes by Mark Schultz <n9xmj@yahoo.com>
+*/
+
+static struct rc_map_table msi_tvanywhere_plus[] = {
+
+/*  ---- Remote Button Layout ----
+
+    POWER   SOURCE  SCAN    MUTE
+    TV/FM   1       2       3
+    |>      4       5       6
+    <|      7       8       9
+    ^^UP    0       +       RECALL
+    vvDN    RECORD  STOP    PLAY
+
+	MINIMIZE          ZOOM
+
+		  CH+
+      VOL-                   VOL+
+		  CH-
+
+	SNAPSHOT           MTS
+
+     <<      FUNC    >>     RESET
+*/
+
+	{ 0x01, KEY_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_VIDEO },		/* 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_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 */
+};
+
+static struct rc_map_list msi_tvanywhere_plus_map = {
+	.map = {
+		.scan    = msi_tvanywhere_plus,
+		.size    = ARRAY_SIZE(msi_tvanywhere_plus),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_MSI_TVANYWHERE_PLUS,
+	}
+};
+
+static int __init init_rc_map_msi_tvanywhere_plus(void)
+{
+	return rc_map_register(&msi_tvanywhere_plus_map);
+}
+
+static void __exit exit_rc_map_msi_tvanywhere_plus(void)
+{
+	rc_map_unregister(&msi_tvanywhere_plus_map);
+}
+
+module_init(init_rc_map_msi_tvanywhere_plus)
+module_exit(exit_rc_map_msi_tvanywhere_plus)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-msi-tvanywhere.c b/drivers/media/rc/keymaps/rc-msi-tvanywhere.c
new file mode 100644
index 0000000..4233a8d
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-msi-tvanywhere.c
@@ -0,0 +1,70 @@
+/* msi-tvanywhere.h - Keytable for msi_tvanywhere Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* MSI TV@nywhere MASTER remote */
+
+static struct rc_map_table 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_FN },		/* Function */
+	{ 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 },
+};
+
+static struct rc_map_list msi_tvanywhere_map = {
+	.map = {
+		.scan    = msi_tvanywhere,
+		.size    = ARRAY_SIZE(msi_tvanywhere),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_MSI_TVANYWHERE,
+	}
+};
+
+static int __init init_rc_map_msi_tvanywhere(void)
+{
+	return rc_map_register(&msi_tvanywhere_map);
+}
+
+static void __exit exit_rc_map_msi_tvanywhere(void)
+{
+	rc_map_unregister(&msi_tvanywhere_map);
+}
+
+module_init(init_rc_map_msi_tvanywhere)
+module_exit(exit_rc_map_msi_tvanywhere)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-nebula.c b/drivers/media/rc/keymaps/rc-nebula.c
new file mode 100644
index 0000000..4c50f33
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-nebula.c
@@ -0,0 +1,97 @@
+/* nebula.h - Keytable for nebula Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table nebula[] = {
+	{ 0x0000, KEY_0 },
+	{ 0x0001, KEY_1 },
+	{ 0x0002, KEY_2 },
+	{ 0x0003, KEY_3 },
+	{ 0x0004, KEY_4 },
+	{ 0x0005, KEY_5 },
+	{ 0x0006, KEY_6 },
+	{ 0x0007, KEY_7 },
+	{ 0x0008, KEY_8 },
+	{ 0x0009, KEY_9 },
+	{ 0x000a, KEY_TV },
+	{ 0x000b, KEY_AUX },
+	{ 0x000c, KEY_DVD },
+	{ 0x000d, KEY_POWER },
+	{ 0x000e, KEY_CAMERA },	/* labelled 'Picture' */
+	{ 0x000f, KEY_AUDIO },
+	{ 0x0010, KEY_INFO },
+	{ 0x0011, KEY_F13 },	/* 16:9 */
+	{ 0x0012, KEY_F14 },	/* 14:9 */
+	{ 0x0013, KEY_EPG },
+	{ 0x0014, KEY_EXIT },
+	{ 0x0015, KEY_MENU },
+	{ 0x0016, KEY_UP },
+	{ 0x0017, KEY_DOWN },
+	{ 0x0018, KEY_LEFT },
+	{ 0x0019, KEY_RIGHT },
+	{ 0x001a, KEY_ENTER },
+	{ 0x001b, KEY_CHANNELUP },
+	{ 0x001c, KEY_CHANNELDOWN },
+	{ 0x001d, KEY_VOLUMEUP },
+	{ 0x001e, KEY_VOLUMEDOWN },
+	{ 0x001f, KEY_RED },
+	{ 0x0020, KEY_GREEN },
+	{ 0x0021, KEY_YELLOW },
+	{ 0x0022, KEY_BLUE },
+	{ 0x0023, KEY_SUBTITLE },
+	{ 0x0024, KEY_F15 },	/* AD */
+	{ 0x0025, KEY_TEXT },
+	{ 0x0026, KEY_MUTE },
+	{ 0x0027, KEY_REWIND },
+	{ 0x0028, KEY_STOP },
+	{ 0x0029, KEY_PLAY },
+	{ 0x002a, KEY_FASTFORWARD },
+	{ 0x002b, KEY_F16 },	/* chapter */
+	{ 0x002c, KEY_PAUSE },
+	{ 0x002d, KEY_PLAY },
+	{ 0x002e, KEY_RECORD },
+	{ 0x002f, KEY_F17 },	/* picture in picture */
+	{ 0x0030, KEY_KPPLUS },	/* zoom in */
+	{ 0x0031, KEY_KPMINUS },	/* zoom out */
+	{ 0x0032, KEY_F18 },	/* capture */
+	{ 0x0033, KEY_F19 },	/* web */
+	{ 0x0034, KEY_EMAIL },
+	{ 0x0035, KEY_PHONE },
+	{ 0x0036, KEY_PC },
+};
+
+static struct rc_map_list nebula_map = {
+	.map = {
+		.scan    = nebula,
+		.size    = ARRAY_SIZE(nebula),
+		.rc_type = RC_TYPE_RC5,
+		.name    = RC_MAP_NEBULA,
+	}
+};
+
+static int __init init_rc_map_nebula(void)
+{
+	return rc_map_register(&nebula_map);
+}
+
+static void __exit exit_rc_map_nebula(void)
+{
+	rc_map_unregister(&nebula_map);
+}
+
+module_init(init_rc_map_nebula)
+module_exit(exit_rc_map_nebula)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c b/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c
new file mode 100644
index 0000000..292bbad
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c
@@ -0,0 +1,158 @@
+/* nec-terratec-cinergy-xs.h - Keytable for nec_terratec_cinergy_xs Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Terratec Cinergy Hybrid T USB XS FM
+   Mauro Carvalho Chehab
+ */
+
+static struct rc_map_table nec_terratec_cinergy_xs[] = {
+
+	/* Terratec Grey IR, with most keys in orange */
+	{ 0x1441, KEY_HOME},
+	{ 0x1401, KEY_POWER2},
+
+	{ 0x1442, KEY_MENU},		/* DVD menu */
+	{ 0x1443, KEY_SUBTITLE},
+	{ 0x1444, KEY_TEXT},		/* Teletext */
+	{ 0x1445, KEY_DELETE},
+
+	{ 0x1402, KEY_1},
+	{ 0x1403, KEY_2},
+	{ 0x1404, KEY_3},
+	{ 0x1405, KEY_4},
+	{ 0x1406, KEY_5},
+	{ 0x1407, KEY_6},
+	{ 0x1408, KEY_7},
+	{ 0x1409, KEY_8},
+	{ 0x140a, KEY_9},
+	{ 0x140c, KEY_0},
+
+	{ 0x140b, KEY_TUNER},		/* AV */
+	{ 0x140d, KEY_MODE},		/* A.B */
+
+	{ 0x1446, KEY_TV},
+	{ 0x1447, KEY_DVD},
+	{ 0x1449, KEY_VIDEO},
+	{ 0x144a, KEY_RADIO},		/* Music */
+	{ 0x144b, KEY_CAMERA},		/* PIC */
+
+	{ 0x1410, KEY_UP},
+	{ 0x1411, KEY_LEFT},
+	{ 0x1412, KEY_OK},
+	{ 0x1413, KEY_RIGHT},
+	{ 0x1414, KEY_DOWN},
+
+	{ 0x140f, KEY_EPG},
+	{ 0x1416, KEY_INFO},
+	{ 0x144d, KEY_BACKSPACE},
+
+	{ 0x141c, KEY_VOLUMEUP},
+	{ 0x141e, KEY_VOLUMEDOWN},
+
+	{ 0x144c, KEY_PLAY},
+	{ 0x141d, KEY_MUTE},
+
+	{ 0x141b, KEY_CHANNELUP},
+	{ 0x141f, KEY_CHANNELDOWN},
+
+	{ 0x1417, KEY_RED},
+	{ 0x1418, KEY_GREEN},
+	{ 0x1419, KEY_YELLOW},
+	{ 0x141a, KEY_BLUE},
+
+	{ 0x1458, KEY_RECORD},
+	{ 0x1448, KEY_STOP},
+	{ 0x1440, KEY_PAUSE},
+
+	{ 0x1454, KEY_LAST},
+	{ 0x144e, KEY_REWIND},
+	{ 0x144f, KEY_FASTFORWARD},
+	{ 0x145c, KEY_NEXT},
+
+	/* Terratec Black IR, with most keys in black */
+	{ 0x04eb01, KEY_POWER2},
+
+	{ 0x04eb02, KEY_1},
+	{ 0x04eb03, KEY_2},
+	{ 0x04eb04, KEY_3},
+	{ 0x04eb05, KEY_4},
+	{ 0x04eb06, KEY_5},
+	{ 0x04eb07, KEY_6},
+	{ 0x04eb08, KEY_7},
+	{ 0x04eb09, KEY_8},
+	{ 0x04eb0a, KEY_9},
+	{ 0x04eb0c, KEY_0},
+
+	{ 0x04eb0b, KEY_TEXT},		/* TXT */
+	{ 0x04eb0d, KEY_REFRESH},	/* Refresh */
+
+	{ 0x04eb0e, KEY_HOME},
+	{ 0x04eb0f, KEY_EPG},
+
+	{ 0x04eb10, KEY_UP},
+	{ 0x04eb11, KEY_LEFT},
+	{ 0x04eb12, KEY_OK},
+	{ 0x04eb13, KEY_RIGHT},
+	{ 0x04eb14, KEY_DOWN},
+
+	{ 0x04eb15, KEY_BACKSPACE},
+	{ 0x04eb16, KEY_INFO},
+
+	{ 0x04eb17, KEY_RED},
+	{ 0x04eb18, KEY_GREEN},
+	{ 0x04eb19, KEY_YELLOW},
+	{ 0x04eb1a, KEY_BLUE},
+
+	{ 0x04eb1c, KEY_VOLUMEUP},
+	{ 0x04eb1e, KEY_VOLUMEDOWN},
+
+	{ 0x04eb1d, KEY_MUTE},
+
+	{ 0x04eb1b, KEY_CHANNELUP},
+	{ 0x04eb1f, KEY_CHANNELDOWN},
+
+	{ 0x04eb40, KEY_RECORD},
+	{ 0x04eb4c, KEY_PLAY},
+	{ 0x04eb58, KEY_PAUSE},
+
+	{ 0x04eb54, KEY_REWIND},
+	{ 0x04eb48, KEY_STOP},
+	{ 0x04eb5c, KEY_NEXT},
+};
+
+static struct rc_map_list nec_terratec_cinergy_xs_map = {
+	.map = {
+		.scan    = nec_terratec_cinergy_xs,
+		.size    = ARRAY_SIZE(nec_terratec_cinergy_xs),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_NEC_TERRATEC_CINERGY_XS,
+	}
+};
+
+static int __init init_rc_map_nec_terratec_cinergy_xs(void)
+{
+	return rc_map_register(&nec_terratec_cinergy_xs_map);
+}
+
+static void __exit exit_rc_map_nec_terratec_cinergy_xs(void)
+{
+	rc_map_unregister(&nec_terratec_cinergy_xs_map);
+}
+
+module_init(init_rc_map_nec_terratec_cinergy_xs)
+module_exit(exit_rc_map_nec_terratec_cinergy_xs)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-norwood.c b/drivers/media/rc/keymaps/rc-norwood.c
new file mode 100644
index 0000000..ca1b82a
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-norwood.c
@@ -0,0 +1,86 @@
+/* norwood.h - Keytable for norwood Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Norwood Micro (non-Pro) TV Tuner
+   By Peter Naulls <peter@chocky.org>
+   Key comments are the functions given in the manual */
+
+static struct rc_map_table 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_VIDEO },		/* 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 },	/* Forward              */
+	{ 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      */
+};
+
+static struct rc_map_list norwood_map = {
+	.map = {
+		.scan    = norwood,
+		.size    = ARRAY_SIZE(norwood),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_NORWOOD,
+	}
+};
+
+static int __init init_rc_map_norwood(void)
+{
+	return rc_map_register(&norwood_map);
+}
+
+static void __exit exit_rc_map_norwood(void)
+{
+	rc_map_unregister(&norwood_map);
+}
+
+module_init(init_rc_map_norwood)
+module_exit(exit_rc_map_norwood)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-npgtech.c b/drivers/media/rc/keymaps/rc-npgtech.c
new file mode 100644
index 0000000..1fb9460
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-npgtech.c
@@ -0,0 +1,81 @@
+/* npgtech.h - Keytable for npgtech Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table 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 },
+
+};
+
+static struct rc_map_list npgtech_map = {
+	.map = {
+		.scan    = npgtech,
+		.size    = ARRAY_SIZE(npgtech),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_NPGTECH,
+	}
+};
+
+static int __init init_rc_map_npgtech(void)
+{
+	return rc_map_register(&npgtech_map);
+}
+
+static void __exit exit_rc_map_npgtech(void)
+{
+	rc_map_unregister(&npgtech_map);
+}
+
+module_init(init_rc_map_npgtech)
+module_exit(exit_rc_map_npgtech)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pctv-sedna.c b/drivers/media/rc/keymaps/rc-pctv-sedna.c
new file mode 100644
index 0000000..5ef01ab
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-pctv-sedna.c
@@ -0,0 +1,81 @@
+/* pctv-sedna.h - Keytable for pctv_sedna Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* 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 */
+
+static struct rc_map_table 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_VIDEO },	/* 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 */
+};
+
+static struct rc_map_list pctv_sedna_map = {
+	.map = {
+		.scan    = pctv_sedna,
+		.size    = ARRAY_SIZE(pctv_sedna),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_PCTV_SEDNA,
+	}
+};
+
+static int __init init_rc_map_pctv_sedna(void)
+{
+	return rc_map_register(&pctv_sedna_map);
+}
+
+static void __exit exit_rc_map_pctv_sedna(void)
+{
+	rc_map_unregister(&pctv_sedna_map);
+}
+
+module_init(init_rc_map_pctv_sedna)
+module_exit(exit_rc_map_pctv_sedna)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pinnacle-color.c b/drivers/media/rc/keymaps/rc-pinnacle-color.c
new file mode 100644
index 0000000..a218b47
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-pinnacle-color.c
@@ -0,0 +1,95 @@
+/* pinnacle-color.h - Keytable for pinnacle_color Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table 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 },
+};
+
+static struct rc_map_list pinnacle_color_map = {
+	.map = {
+		.scan    = pinnacle_color,
+		.size    = ARRAY_SIZE(pinnacle_color),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_PINNACLE_COLOR,
+	}
+};
+
+static int __init init_rc_map_pinnacle_color(void)
+{
+	return rc_map_register(&pinnacle_color_map);
+}
+
+static void __exit exit_rc_map_pinnacle_color(void)
+{
+	rc_map_unregister(&pinnacle_color_map);
+}
+
+module_init(init_rc_map_pinnacle_color)
+module_exit(exit_rc_map_pinnacle_color)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pinnacle-grey.c b/drivers/media/rc/keymaps/rc-pinnacle-grey.c
new file mode 100644
index 0000000..4a3f467
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-pinnacle-grey.c
@@ -0,0 +1,90 @@
+/* pinnacle-grey.h - Keytable for pinnacle_grey Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table 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 },
+};
+
+static struct rc_map_list pinnacle_grey_map = {
+	.map = {
+		.scan    = pinnacle_grey,
+		.size    = ARRAY_SIZE(pinnacle_grey),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_PINNACLE_GREY,
+	}
+};
+
+static int __init init_rc_map_pinnacle_grey(void)
+{
+	return rc_map_register(&pinnacle_grey_map);
+}
+
+static void __exit exit_rc_map_pinnacle_grey(void)
+{
+	rc_map_unregister(&pinnacle_grey_map);
+}
+
+module_init(init_rc_map_pinnacle_grey)
+module_exit(exit_rc_map_pinnacle_grey)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c b/drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c
new file mode 100644
index 0000000..e89cc10
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-pinnacle-pctv-hd.c
@@ -0,0 +1,71 @@
+/* pinnacle-pctv-hd.h - Keytable for pinnacle_pctv_hd Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Pinnacle PCTV HD 800i mini remote */
+
+static struct rc_map_table pinnacle_pctv_hd[] = {
+	/* Key codes for the tiny Pinnacle remote*/
+	{ 0x0700, KEY_MUTE },
+	{ 0x0701, KEY_MENU }, /* Pinnacle logo */
+	{ 0x0739, KEY_POWER },
+	{ 0x0703, KEY_VOLUMEUP },
+	{ 0x0705, KEY_OK },
+	{ 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_ZOOM }, /* 'Square' key */
+	{ 0x072a, KEY_SUBTITLE },   /* 'T' key */
+	{ 0x072d, KEY_REWIND },
+	{ 0x0730, KEY_PLAYPAUSE },
+	{ 0x0733, KEY_FASTFORWARD },
+	{ 0x0736, KEY_RECORD },
+	{ 0x073c, KEY_STOP },
+	{ 0x073f, KEY_HELP }, /* '?' key */
+};
+
+static struct rc_map_list pinnacle_pctv_hd_map = {
+	.map = {
+		.scan    = pinnacle_pctv_hd,
+		.size    = ARRAY_SIZE(pinnacle_pctv_hd),
+		.rc_type = RC_TYPE_RC5,
+		.name    = RC_MAP_PINNACLE_PCTV_HD,
+	}
+};
+
+static int __init init_rc_map_pinnacle_pctv_hd(void)
+{
+	return rc_map_register(&pinnacle_pctv_hd_map);
+}
+
+static void __exit exit_rc_map_pinnacle_pctv_hd(void)
+{
+	rc_map_unregister(&pinnacle_pctv_hd_map);
+}
+
+module_init(init_rc_map_pinnacle_pctv_hd)
+module_exit(exit_rc_map_pinnacle_pctv_hd)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pixelview-002t.c b/drivers/media/rc/keymaps/rc-pixelview-002t.c
new file mode 100644
index 0000000..d967c38
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-pixelview-002t.c
@@ -0,0 +1,78 @@
+/* rc-pixelview-mk12.h - Keytable for pixelview Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * Keytable for 002-T IR remote provided together with Pixelview
+ * SBTVD Hybrid Remote Controller. Uses NEC extended format.
+ */
+static struct rc_map_table pixelview_002t[] = {
+	{ 0x866b13, KEY_MUTE },
+	{ 0x866b12, KEY_POWER2 },	/* power */
+
+	{ 0x866b01, KEY_1 },
+	{ 0x866b02, KEY_2 },
+	{ 0x866b03, KEY_3 },
+	{ 0x866b04, KEY_4 },
+	{ 0x866b05, KEY_5 },
+	{ 0x866b06, KEY_6 },
+	{ 0x866b07, KEY_7 },
+	{ 0x866b08, KEY_8 },
+	{ 0x866b09, KEY_9 },
+	{ 0x866b00, KEY_0 },
+
+	{ 0x866b0d, KEY_CHANNELUP },
+	{ 0x866b19, KEY_CHANNELDOWN },
+	{ 0x866b10, KEY_VOLUMEUP },	/* vol + */
+	{ 0x866b0c, KEY_VOLUMEDOWN },	/* vol - */
+
+	{ 0x866b0a, KEY_CAMERA },	/* snapshot */
+	{ 0x866b0b, KEY_ZOOM },		/* zoom */
+
+	{ 0x866b1b, KEY_BACKSPACE },
+	{ 0x866b15, KEY_ENTER },
+
+	{ 0x866b1d, KEY_UP },
+	{ 0x866b1e, KEY_DOWN },
+	{ 0x866b0e, KEY_LEFT },
+	{ 0x866b0f, KEY_RIGHT },
+
+	{ 0x866b18, KEY_RECORD },
+	{ 0x866b1a, KEY_STOP },
+};
+
+static struct rc_map_list pixelview_map = {
+	.map = {
+		.scan    = pixelview_002t,
+		.size    = ARRAY_SIZE(pixelview_002t),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_PIXELVIEW_002T,
+	}
+};
+
+static int __init init_rc_map_pixelview(void)
+{
+	return rc_map_register(&pixelview_map);
+}
+
+static void __exit exit_rc_map_pixelview(void)
+{
+	rc_map_unregister(&pixelview_map);
+}
+
+module_init(init_rc_map_pixelview)
+module_exit(exit_rc_map_pixelview)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pixelview-mk12.c b/drivers/media/rc/keymaps/rc-pixelview-mk12.c
new file mode 100644
index 0000000..224d0ef
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-pixelview-mk12.c
@@ -0,0 +1,84 @@
+/* rc-pixelview-mk12.h - Keytable for pixelview Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * Keytable for MK-F12 IR remote provided together with Pixelview
+ * Ultra Pro Remote Controller. Uses NEC extended format.
+ */
+static struct rc_map_table pixelview_mk12[] = {
+	{ 0x866b03, KEY_TUNER },	/* Timeshift */
+	{ 0x866b1e, KEY_POWER2 },	/* power */
+
+	{ 0x866b01, KEY_1 },
+	{ 0x866b0b, KEY_2 },
+	{ 0x866b1b, KEY_3 },
+	{ 0x866b05, KEY_4 },
+	{ 0x866b09, KEY_5 },
+	{ 0x866b15, KEY_6 },
+	{ 0x866b06, KEY_7 },
+	{ 0x866b0a, KEY_8 },
+	{ 0x866b12, KEY_9 },
+	{ 0x866b02, KEY_0 },
+
+	{ 0x866b13, KEY_AGAIN },	/* loop */
+	{ 0x866b10, KEY_DIGITS },	/* +100 */
+
+	{ 0x866b00, KEY_VIDEO },		/* source */
+	{ 0x866b18, KEY_MUTE },		/* mute */
+	{ 0x866b19, KEY_CAMERA },	/* snapshot */
+	{ 0x866b1a, KEY_SEARCH },	/* scan */
+
+	{ 0x866b16, KEY_CHANNELUP },	/* chn + */
+	{ 0x866b14, KEY_CHANNELDOWN },	/* chn - */
+	{ 0x866b1f, KEY_VOLUMEUP },	/* vol + */
+	{ 0x866b17, KEY_VOLUMEDOWN },	/* vol - */
+	{ 0x866b1c, KEY_ZOOM },		/* zoom */
+
+	{ 0x866b04, KEY_REWIND },
+	{ 0x866b0e, KEY_RECORD },
+	{ 0x866b0c, KEY_FORWARD },
+
+	{ 0x866b1d, KEY_STOP },
+	{ 0x866b08, KEY_PLAY },
+	{ 0x866b0f, KEY_PAUSE },
+
+	{ 0x866b0d, KEY_TV },
+	{ 0x866b07, KEY_RADIO },	/* FM */
+};
+
+static struct rc_map_list pixelview_map = {
+	.map = {
+		.scan    = pixelview_mk12,
+		.size    = ARRAY_SIZE(pixelview_mk12),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_PIXELVIEW_MK12,
+	}
+};
+
+static int __init init_rc_map_pixelview(void)
+{
+	return rc_map_register(&pixelview_map);
+}
+
+static void __exit exit_rc_map_pixelview(void)
+{
+	rc_map_unregister(&pixelview_map);
+}
+
+module_init(init_rc_map_pixelview)
+module_exit(exit_rc_map_pixelview)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pixelview-new.c b/drivers/media/rc/keymaps/rc-pixelview-new.c
new file mode 100644
index 0000000..781d788
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-pixelview-new.c
@@ -0,0 +1,84 @@
+/* pixelview-new.h - Keytable for pixelview_new Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+   Mauro Carvalho Chehab <mchehab@infradead.org>
+   present on PV MPEG 8000GT
+ */
+
+static struct rc_map_table 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_VIDEO },		/* 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 },
+};
+
+static struct rc_map_list pixelview_new_map = {
+	.map = {
+		.scan    = pixelview_new,
+		.size    = ARRAY_SIZE(pixelview_new),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_PIXELVIEW_NEW,
+	}
+};
+
+static int __init init_rc_map_pixelview_new(void)
+{
+	return rc_map_register(&pixelview_new_map);
+}
+
+static void __exit exit_rc_map_pixelview_new(void)
+{
+	rc_map_unregister(&pixelview_new_map);
+}
+
+module_init(init_rc_map_pixelview_new)
+module_exit(exit_rc_map_pixelview_new)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pixelview.c b/drivers/media/rc/keymaps/rc-pixelview.c
new file mode 100644
index 0000000..39e6fea
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-pixelview.c
@@ -0,0 +1,83 @@
+/* pixelview.h - Keytable for pixelview Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table pixelview[] = {
+
+	{ 0x1e, KEY_POWER },	/* power */
+	{ 0x07, KEY_VIDEO },	/* source */
+	{ 0x1c, KEY_SEARCH },	/* scan */
+
+
+	{ 0x03, KEY_TUNER },		/* TV/FM */
+
+	{ 0x00, KEY_RECORD },
+	{ 0x08, KEY_STOP },
+	{ 0x11, KEY_PLAY },
+
+	{ 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 */
+
+	{ 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 },		/* >>> */
+
+	{ 0x0d, KEY_GOTO },		/* mts */
+	{ 0x1d, KEY_REFRESH },		/* reset */
+	{ 0x18, KEY_MUTE },		/* mute/unmute */
+};
+
+static struct rc_map_list pixelview_map = {
+	.map = {
+		.scan    = pixelview,
+		.size    = ARRAY_SIZE(pixelview),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_PIXELVIEW,
+	}
+};
+
+static int __init init_rc_map_pixelview(void)
+{
+	return rc_map_register(&pixelview_map);
+}
+
+static void __exit exit_rc_map_pixelview(void)
+{
+	rc_map_unregister(&pixelview_map);
+}
+
+module_init(init_rc_map_pixelview)
+module_exit(exit_rc_map_pixelview)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-powercolor-real-angel.c b/drivers/media/rc/keymaps/rc-powercolor-real-angel.c
new file mode 100644
index 0000000..e96fa3a
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-powercolor-real-angel.c
@@ -0,0 +1,82 @@
+/* powercolor-real-angel.h - Keytable for powercolor_real_angel Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * Remote control for Powercolor Real Angel 330
+ * Daniel Fraga <fragabr@gmail.com>
+ */
+
+static struct rc_map_table 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 */
+};
+
+static struct rc_map_list powercolor_real_angel_map = {
+	.map = {
+		.scan    = powercolor_real_angel,
+		.size    = ARRAY_SIZE(powercolor_real_angel),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_POWERCOLOR_REAL_ANGEL,
+	}
+};
+
+static int __init init_rc_map_powercolor_real_angel(void)
+{
+	return rc_map_register(&powercolor_real_angel_map);
+}
+
+static void __exit exit_rc_map_powercolor_real_angel(void)
+{
+	rc_map_unregister(&powercolor_real_angel_map);
+}
+
+module_init(init_rc_map_powercolor_real_angel)
+module_exit(exit_rc_map_powercolor_real_angel)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-proteus-2309.c b/drivers/media/rc/keymaps/rc-proteus-2309.c
new file mode 100644
index 0000000..eef626e
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-proteus-2309.c
@@ -0,0 +1,70 @@
+/* proteus-2309.h - Keytable for proteus_2309 Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Michal Majchrowicz <mmajchrowicz@gmail.com> */
+
+static struct rc_map_table 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_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 },
+};
+
+static struct rc_map_list proteus_2309_map = {
+	.map = {
+		.scan    = proteus_2309,
+		.size    = ARRAY_SIZE(proteus_2309),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_PROTEUS_2309,
+	}
+};
+
+static int __init init_rc_map_proteus_2309(void)
+{
+	return rc_map_register(&proteus_2309_map);
+}
+
+static void __exit exit_rc_map_proteus_2309(void)
+{
+	rc_map_unregister(&proteus_2309_map);
+}
+
+module_init(init_rc_map_proteus_2309)
+module_exit(exit_rc_map_proteus_2309)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-purpletv.c b/drivers/media/rc/keymaps/rc-purpletv.c
new file mode 100644
index 0000000..cec6fe4
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-purpletv.c
@@ -0,0 +1,82 @@
+/* purpletv.h - Keytable for purpletv Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table 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 ? */
+
+};
+
+static struct rc_map_list purpletv_map = {
+	.map = {
+		.scan    = purpletv,
+		.size    = ARRAY_SIZE(purpletv),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_PURPLETV,
+	}
+};
+
+static int __init init_rc_map_purpletv(void)
+{
+	return rc_map_register(&purpletv_map);
+}
+
+static void __exit exit_rc_map_purpletv(void)
+{
+	rc_map_unregister(&purpletv_map);
+}
+
+module_init(init_rc_map_purpletv)
+module_exit(exit_rc_map_purpletv)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-pv951.c b/drivers/media/rc/keymaps/rc-pv951.c
new file mode 100644
index 0000000..5ac89ce
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-pv951.c
@@ -0,0 +1,79 @@
+/* pv951.h - Keytable for pv951 Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Mark Phalan <phalanm@o2.ie> */
+
+static struct rc_map_table 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_VIDEO },		/* SOURCE */
+	{ 0x0a, KEY_KPPLUS },		/* +100 */
+	{ 0x14, KEY_EQUAL },		/* SYNC */
+	{ 0x1c, KEY_TV },		/* PC/TV */
+};
+
+static struct rc_map_list pv951_map = {
+	.map = {
+		.scan    = pv951,
+		.size    = ARRAY_SIZE(pv951),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_PV951,
+	}
+};
+
+static int __init init_rc_map_pv951(void)
+{
+	return rc_map_register(&pv951_map);
+}
+
+static void __exit exit_rc_map_pv951(void)
+{
+	rc_map_unregister(&pv951_map);
+}
+
+module_init(init_rc_map_pv951)
+module_exit(exit_rc_map_pv951)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-rc6-mce.c b/drivers/media/rc/keymaps/rc-rc6-mce.c
new file mode 100644
index 0000000..ef4006f
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-rc6-mce.c
@@ -0,0 +1,119 @@
+/* rc-rc6-mce.c - Keytable for Windows Media Center RC-6 remotes for use
+ * with the Media Center Edition eHome Infrared Transceiver.
+ *
+ * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.com>
+ *
+ * See http://mediacenterguides.com/book/export/html/31 for details on
+ * key mappings.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table rc6_mce[] = {
+
+	{ 0x800f0400, KEY_NUMERIC_0 },
+	{ 0x800f0401, KEY_NUMERIC_1 },
+	{ 0x800f0402, KEY_NUMERIC_2 },
+	{ 0x800f0403, KEY_NUMERIC_3 },
+	{ 0x800f0404, KEY_NUMERIC_4 },
+	{ 0x800f0405, KEY_NUMERIC_5 },
+	{ 0x800f0406, KEY_NUMERIC_6 },
+	{ 0x800f0407, KEY_NUMERIC_7 },
+	{ 0x800f0408, KEY_NUMERIC_8 },
+	{ 0x800f0409, KEY_NUMERIC_9 },
+
+	{ 0x800f040a, KEY_DELETE },
+	{ 0x800f040b, KEY_ENTER },
+	{ 0x800f040c, KEY_SLEEP },		/* Formerly PC Power */
+	{ 0x800f040d, KEY_MEDIA },		/* Windows MCE button */
+	{ 0x800f040e, KEY_MUTE },
+	{ 0x800f040f, KEY_INFO },
+
+	{ 0x800f0410, KEY_VOLUMEUP },
+	{ 0x800f0411, KEY_VOLUMEDOWN },
+	{ 0x800f0412, KEY_CHANNELUP },
+	{ 0x800f0413, KEY_CHANNELDOWN },
+
+	{ 0x800f0414, KEY_FASTFORWARD },
+	{ 0x800f0415, KEY_REWIND },
+	{ 0x800f0416, KEY_PLAY },
+	{ 0x800f0417, KEY_RECORD },
+	{ 0x800f0418, KEY_PAUSE },
+	{ 0x800f0419, KEY_STOP },
+	{ 0x800f041a, KEY_NEXT },
+	{ 0x800f041b, KEY_PREVIOUS },
+	{ 0x800f041c, KEY_NUMERIC_POUND },
+	{ 0x800f041d, KEY_NUMERIC_STAR },
+
+	{ 0x800f041e, KEY_UP },
+	{ 0x800f041f, KEY_DOWN },
+	{ 0x800f0420, KEY_LEFT },
+	{ 0x800f0421, KEY_RIGHT },
+
+	{ 0x800f0422, KEY_OK },
+	{ 0x800f0423, KEY_EXIT },
+	{ 0x800f0424, KEY_DVD },
+	{ 0x800f0425, KEY_TUNER },		/* LiveTV */
+	{ 0x800f0426, KEY_EPG },		/* Guide */
+	{ 0x800f0427, KEY_ZOOM },		/* Aspect */
+
+	{ 0x800f0432, KEY_MODE },		/* Visualization */
+	{ 0x800f0433, KEY_PRESENTATION },	/* Slide Show */
+	{ 0x800f0434, KEY_EJECTCD },
+	{ 0x800f043a, KEY_BRIGHTNESSUP },
+
+	{ 0x800f0446, KEY_TV },
+	{ 0x800f0447, KEY_AUDIO },		/* My Music */
+	{ 0x800f0448, KEY_PVR },		/* RecordedTV */
+	{ 0x800f0449, KEY_CAMERA },
+	{ 0x800f044a, KEY_VIDEO },
+	{ 0x800f044c, KEY_LANGUAGE },
+	{ 0x800f044d, KEY_TITLE },
+	{ 0x800f044e, KEY_PRINT },	/* Print - HP OEM version of remote */
+
+	{ 0x800f0450, KEY_RADIO },
+
+	{ 0x800f045a, KEY_SUBTITLE },		/* Caption/Teletext */
+	{ 0x800f045b, KEY_RED },
+	{ 0x800f045c, KEY_GREEN },
+	{ 0x800f045d, KEY_YELLOW },
+	{ 0x800f045e, KEY_BLUE },
+
+	{ 0x800f0465, KEY_POWER2 },	/* TV Power */
+	{ 0x800f046e, KEY_PLAYPAUSE },
+	{ 0x800f046f, KEY_PLAYER },	/* Start media application (NEW) */
+
+	{ 0x800f0480, KEY_BRIGHTNESSDOWN },
+	{ 0x800f0481, KEY_PLAYPAUSE },
+};
+
+static struct rc_map_list rc6_mce_map = {
+	.map = {
+		.scan    = rc6_mce,
+		.size    = ARRAY_SIZE(rc6_mce),
+		.rc_type = RC_TYPE_RC6_MCE,
+		.name    = RC_MAP_RC6_MCE,
+	}
+};
+
+static int __init init_rc_map_rc6_mce(void)
+{
+	return rc_map_register(&rc6_mce_map);
+}
+
+static void __exit exit_rc_map_rc6_mce(void)
+{
+	rc_map_unregister(&rc6_mce_map);
+}
+
+module_init(init_rc_map_rc6_mce)
+module_exit(exit_rc_map_rc6_mce)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
diff --git a/drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c b/drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c
new file mode 100644
index 0000000..9f778bd
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-real-audio-220-32-keys.c
@@ -0,0 +1,79 @@
+/* real-audio-220-32-keys.h - Keytable for real_audio_220_32_keys Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Zogis Real Audio 220 - 32 keys IR */
+
+static struct rc_map_table 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_VIDEO},		/* 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 */
+
+};
+
+static struct rc_map_list real_audio_220_32_keys_map = {
+	.map = {
+		.scan    = real_audio_220_32_keys,
+		.size    = ARRAY_SIZE(real_audio_220_32_keys),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_REAL_AUDIO_220_32_KEYS,
+	}
+};
+
+static int __init init_rc_map_real_audio_220_32_keys(void)
+{
+	return rc_map_register(&real_audio_220_32_keys_map);
+}
+
+static void __exit exit_rc_map_real_audio_220_32_keys(void)
+{
+	rc_map_unregister(&real_audio_220_32_keys_map);
+}
+
+module_init(init_rc_map_real_audio_220_32_keys)
+module_exit(exit_rc_map_real_audio_220_32_keys)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-reddo.c b/drivers/media/rc/keymaps/rc-reddo.c
new file mode 100644
index 0000000..b80b336
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-reddo.c
@@ -0,0 +1,86 @@
+/*
+ * MSI DIGIVOX mini III remote controller keytable
+ *
+ * Copyright (C) 2013 Antti Palosaari <crope@iki.fi>
+ *
+ *    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.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * Derived from MSI DIGIVOX mini III remote (rc-msi-digivox-iii.c)
+ *
+ * Differences between these remotes are:
+ *
+ * 1) scancode 0x61d601 is mapped to different button:
+ *    MSI DIGIVOX mini III   "Source" = KEY_VIDEO
+ *    Reddo                     "EPG" = KEY_EPG
+ *
+ * 2) Reddo remote has less buttons. Missing buttons are: colored buttons,
+ *    navigation buttons and main power button.
+ */
+
+static struct rc_map_table reddo[] = {
+	{ 0x61d601, KEY_EPG },             /* EPG */
+	{ 0x61d602, KEY_3 },
+	{ 0x61d604, KEY_1 },
+	{ 0x61d605, KEY_5 },
+	{ 0x61d606, KEY_6 },
+	{ 0x61d607, KEY_CHANNELDOWN },     /* CH- */
+	{ 0x61d608, KEY_2 },
+	{ 0x61d609, KEY_CHANNELUP },       /* CH+ */
+	{ 0x61d60a, KEY_9 },
+	{ 0x61d60b, KEY_ZOOM },            /* Zoom */
+	{ 0x61d60c, KEY_7 },
+	{ 0x61d60d, KEY_8 },
+	{ 0x61d60e, KEY_VOLUMEUP },        /* Vol+ */
+	{ 0x61d60f, KEY_4 },
+	{ 0x61d610, KEY_ESC },             /* [back up arrow] */
+	{ 0x61d611, KEY_0 },
+	{ 0x61d612, KEY_OK },              /* [enter arrow] */
+	{ 0x61d613, KEY_VOLUMEDOWN },      /* Vol- */
+	{ 0x61d614, KEY_RECORD },          /* Rec */
+	{ 0x61d615, KEY_STOP },            /* Stop */
+	{ 0x61d616, KEY_PLAY },            /* Play */
+	{ 0x61d617, KEY_MUTE },            /* Mute */
+	{ 0x61d643, KEY_POWER2 },          /* [red power button] */
+};
+
+static struct rc_map_list reddo_map = {
+	.map = {
+		.scan    = reddo,
+		.size    = ARRAY_SIZE(reddo),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_REDDO,
+	}
+};
+
+static int __init init_rc_map_reddo(void)
+{
+	return rc_map_register(&reddo_map);
+}
+
+static void __exit exit_rc_map_reddo(void)
+{
+	rc_map_unregister(&reddo_map);
+}
+
+module_init(init_rc_map_reddo)
+module_exit(exit_rc_map_reddo)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-snapstream-firefly.c b/drivers/media/rc/keymaps/rc-snapstream-firefly.c
new file mode 100644
index 0000000..c7f33ec
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-snapstream-firefly.c
@@ -0,0 +1,107 @@
+/*
+ * SnapStream Firefly X10 RF remote keytable
+ *
+ * Copyright (C) 2011 Anssi Hannula <anssi.hannula@?ki.fi>
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <media/rc-map.h>
+
+static struct rc_map_table snapstream_firefly[] = {
+	{ 0x2c, KEY_ZOOM },       /* Maximize */
+	{ 0x02, KEY_CLOSE },
+
+	{ 0x0d, KEY_1 },
+	{ 0x0e, KEY_2 },
+	{ 0x0f, KEY_3 },
+	{ 0x10, KEY_4 },
+	{ 0x11, KEY_5 },
+	{ 0x12, KEY_6 },
+	{ 0x13, KEY_7 },
+	{ 0x14, KEY_8 },
+	{ 0x15, KEY_9 },
+	{ 0x17, KEY_0 },
+	{ 0x16, KEY_BACK },
+	{ 0x18, KEY_KPENTER },    /* ent */
+
+	{ 0x09, KEY_VOLUMEUP },
+	{ 0x08, KEY_VOLUMEDOWN },
+	{ 0x0a, KEY_MUTE },
+	{ 0x0b, KEY_CHANNELUP },
+	{ 0x0c, KEY_CHANNELDOWN },
+	{ 0x00, KEY_VENDOR },     /* firefly */
+
+	{ 0x2e, KEY_INFO },
+	{ 0x2f, KEY_OPTION },
+
+	{ 0x1d, KEY_LEFT },
+	{ 0x1f, KEY_RIGHT },
+	{ 0x22, KEY_DOWN },
+	{ 0x1a, KEY_UP },
+	{ 0x1e, KEY_OK },
+
+	{ 0x1c, KEY_MENU },
+	{ 0x20, KEY_EXIT },
+
+	{ 0x27, KEY_RECORD },
+	{ 0x25, KEY_PLAY },
+	{ 0x28, KEY_STOP },
+	{ 0x24, KEY_REWIND },
+	{ 0x26, KEY_FORWARD },
+	{ 0x29, KEY_PAUSE },
+	{ 0x2b, KEY_PREVIOUS },
+	{ 0x2a, KEY_NEXT },
+
+	{ 0x06, KEY_AUDIO },      /* Music */
+	{ 0x05, KEY_IMAGES },     /* Photos */
+	{ 0x04, KEY_DVD },
+	{ 0x03, KEY_TV },
+	{ 0x07, KEY_VIDEO },
+
+	{ 0x01, KEY_HELP },
+	{ 0x2d, KEY_MODE },       /* Mouse */
+
+	{ 0x19, KEY_A },
+	{ 0x1b, KEY_B },
+	{ 0x21, KEY_C },
+	{ 0x23, KEY_D },
+};
+
+static struct rc_map_list snapstream_firefly_map = {
+	.map = {
+		.scan    = snapstream_firefly,
+		.size    = ARRAY_SIZE(snapstream_firefly),
+		.rc_type = RC_TYPE_OTHER,
+		.name    = RC_MAP_SNAPSTREAM_FIREFLY,
+	}
+};
+
+static int __init init_rc_map_snapstream_firefly(void)
+{
+	return rc_map_register(&snapstream_firefly_map);
+}
+
+static void __exit exit_rc_map_snapstream_firefly(void)
+{
+	rc_map_unregister(&snapstream_firefly_map);
+}
+
+module_init(init_rc_map_snapstream_firefly)
+module_exit(exit_rc_map_snapstream_firefly)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-streamzap.c b/drivers/media/rc/keymaps/rc-streamzap.c
new file mode 100644
index 0000000..23c0611
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-streamzap.c
@@ -0,0 +1,81 @@
+/* rc-streamzap.c - Keytable for Streamzap PC Remote, for use
+ * with the Streamzap PC Remote IR Receiver.
+ *
+ * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table streamzap[] = {
+/*
+ * The Streamzap remote is almost, but not quite, RC-5, as it has an extra
+ * bit in it.
+ */
+	{ 0x28c0, KEY_NUMERIC_0 },
+	{ 0x28c1, KEY_NUMERIC_1 },
+	{ 0x28c2, KEY_NUMERIC_2 },
+	{ 0x28c3, KEY_NUMERIC_3 },
+	{ 0x28c4, KEY_NUMERIC_4 },
+	{ 0x28c5, KEY_NUMERIC_5 },
+	{ 0x28c6, KEY_NUMERIC_6 },
+	{ 0x28c7, KEY_NUMERIC_7 },
+	{ 0x28c8, KEY_NUMERIC_8 },
+	{ 0x28c9, KEY_NUMERIC_9 },
+	{ 0x28ca, KEY_POWER },
+	{ 0x28cb, KEY_MUTE },
+	{ 0x28cc, KEY_CHANNELUP },
+	{ 0x28cd, KEY_VOLUMEUP },
+	{ 0x28ce, KEY_CHANNELDOWN },
+	{ 0x28cf, KEY_VOLUMEDOWN },
+	{ 0x28d0, KEY_UP },
+	{ 0x28d1, KEY_LEFT },
+	{ 0x28d2, KEY_OK },
+	{ 0x28d3, KEY_RIGHT },
+	{ 0x28d4, KEY_DOWN },
+	{ 0x28d5, KEY_MENU },
+	{ 0x28d6, KEY_EXIT },
+	{ 0x28d7, KEY_PLAY },
+	{ 0x28d8, KEY_PAUSE },
+	{ 0x28d9, KEY_STOP },
+	{ 0x28da, KEY_BACK },
+	{ 0x28db, KEY_FORWARD },
+	{ 0x28dc, KEY_RECORD },
+	{ 0x28dd, KEY_REWIND },
+	{ 0x28de, KEY_FASTFORWARD },
+	{ 0x28e0, KEY_RED },
+	{ 0x28e1, KEY_GREEN },
+	{ 0x28e2, KEY_YELLOW },
+	{ 0x28e3, KEY_BLUE },
+
+};
+
+static struct rc_map_list streamzap_map = {
+	.map = {
+		.scan    = streamzap,
+		.size    = ARRAY_SIZE(streamzap),
+		.rc_type = RC_TYPE_RC5_SZ,
+		.name    = RC_MAP_STREAMZAP,
+	}
+};
+
+static int __init init_rc_map_streamzap(void)
+{
+	return rc_map_register(&streamzap_map);
+}
+
+static void __exit exit_rc_map_streamzap(void)
+{
+	rc_map_unregister(&streamzap_map);
+}
+
+module_init(init_rc_map_streamzap)
+module_exit(exit_rc_map_streamzap)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
diff --git a/drivers/media/rc/keymaps/rc-su3000.c b/drivers/media/rc/keymaps/rc-su3000.c
new file mode 100644
index 0000000..8dbd3e9
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-su3000.c
@@ -0,0 +1,75 @@
+/* rc-su3000.h - Keytable for Geniatech HDStar Remote Controller
+ *
+ * Copyright (c) 2013 by Evgeny Plehov <Evgeny Plehov@ukr.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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table su3000[] = {
+	{ 0x25, KEY_POWER },	/* right-bottom Red */
+	{ 0x0a, KEY_MUTE },	/* -/-- */
+	{ 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 },
+	{ 0x20, KEY_UP },	/* CH+ */
+	{ 0x21, KEY_DOWN },	/* CH+ */
+	{ 0x12, KEY_VOLUMEUP },	/* Brightness Up */
+	{ 0x13, KEY_VOLUMEDOWN },/* Brightness Down */
+	{ 0x1f, KEY_RECORD },
+	{ 0x17, KEY_PLAY },
+	{ 0x16, KEY_PAUSE },
+	{ 0x0b, KEY_STOP },
+	{ 0x27, KEY_FASTFORWARD },/* >> */
+	{ 0x26, KEY_REWIND },	/* << */
+	{ 0x0d, KEY_OK },	/* Mute */
+	{ 0x11, KEY_LEFT },	/* VOL- */
+	{ 0x10, KEY_RIGHT },	/* VOL+ */
+	{ 0x29, KEY_BACK },	/* button under 9 */
+	{ 0x2c, KEY_MENU },	/* TTX */
+	{ 0x2b, KEY_EPG },	/* EPG */
+	{ 0x1e, KEY_RED },	/* OSD */
+	{ 0x0e, KEY_GREEN },	/* Window */
+	{ 0x2d, KEY_YELLOW },	/* button under << */
+	{ 0x0f, KEY_BLUE },	/* bottom yellow button */
+	{ 0x14, KEY_AUDIO },	/* Snapshot */
+	{ 0x38, KEY_TV },	/* TV/Radio */
+	{ 0x0c, KEY_ESC }	/* upper Red button */
+};
+
+static struct rc_map_list su3000_map = {
+	.map = {
+		.scan    = su3000,
+		.size    = ARRAY_SIZE(su3000),
+		.rc_type = RC_TYPE_RC5,
+		.name    = RC_MAP_SU3000,
+	}
+};
+
+static int __init init_rc_map_su3000(void)
+{
+	return rc_map_register(&su3000_map);
+}
+
+static void __exit exit_rc_map_su3000(void)
+{
+	rc_map_unregister(&su3000_map);
+}
+
+module_init(init_rc_map_su3000)
+module_exit(exit_rc_map_su3000)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Evgeny Plehov <Evgeny Plehov@ukr.net>");
diff --git a/drivers/media/rc/keymaps/rc-tbs-nec.c b/drivers/media/rc/keymaps/rc-tbs-nec.c
new file mode 100644
index 0000000..24ce2a2
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-tbs-nec.c
@@ -0,0 +1,76 @@
+/* tbs-nec.h - Keytable for tbs_nec Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table tbs_nec[] = {
+	{ 0x84, KEY_POWER2},		/* power */
+	{ 0x94, KEY_MUTE},		/* mute */
+	{ 0x87, KEY_1},
+	{ 0x86, KEY_2},
+	{ 0x85, KEY_3},
+	{ 0x8b, KEY_4},
+	{ 0x8a, KEY_5},
+	{ 0x89, KEY_6},
+	{ 0x8f, KEY_7},
+	{ 0x8e, KEY_8},
+	{ 0x8d, KEY_9},
+	{ 0x92, KEY_0},
+	{ 0xc0, KEY_10CHANNELSUP},	/* 10+ */
+	{ 0xd0, KEY_10CHANNELSDOWN},	/* 10- */
+	{ 0x96, KEY_CHANNELUP},		/* ch+ */
+	{ 0x91, KEY_CHANNELDOWN},	/* ch- */
+	{ 0x93, KEY_VOLUMEUP},		/* vol+ */
+	{ 0x8c, KEY_VOLUMEDOWN},	/* vol- */
+	{ 0x83, KEY_RECORD},		/* rec */
+	{ 0x98, KEY_PAUSE},		/* pause, yellow */
+	{ 0x99, KEY_OK},		/* ok */
+	{ 0x9a, KEY_CAMERA},		/* snapshot */
+	{ 0x81, KEY_UP},
+	{ 0x90, KEY_LEFT},
+	{ 0x82, KEY_RIGHT},
+	{ 0x88, KEY_DOWN},
+	{ 0x95, KEY_FAVORITES},		/* blue */
+	{ 0x97, KEY_SUBTITLE},		/* green */
+	{ 0x9d, KEY_ZOOM},
+	{ 0x9f, KEY_EXIT},
+	{ 0x9e, KEY_MENU},
+	{ 0x9c, KEY_EPG},
+	{ 0x80, KEY_PREVIOUS},		/* red */
+	{ 0x9b, KEY_MODE},
+};
+
+static struct rc_map_list tbs_nec_map = {
+	.map = {
+		.scan    = tbs_nec,
+		.size    = ARRAY_SIZE(tbs_nec),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_TBS_NEC,
+	}
+};
+
+static int __init init_rc_map_tbs_nec(void)
+{
+	return rc_map_register(&tbs_nec_map);
+}
+
+static void __exit exit_rc_map_tbs_nec(void)
+{
+	rc_map_unregister(&tbs_nec_map);
+}
+
+module_init(init_rc_map_tbs_nec)
+module_exit(exit_rc_map_tbs_nec)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-technisat-ts35.c b/drivers/media/rc/keymaps/rc-technisat-ts35.c
new file mode 100644
index 0000000..3328cbe
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-technisat-ts35.c
@@ -0,0 +1,76 @@
+/* rc-technisat-ts35.c - Keytable for TechniSat TS35 remote
+ *
+ * Copyright (c) 2013 by Jan Klötzke <jan@kloetzke.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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table technisat_ts35[] = {
+	{0x32, KEY_MUTE},
+	{0x07, KEY_MEDIA},
+	{0x1c, KEY_AB},
+	{0x33, KEY_POWER},
+
+	{0x3e, KEY_1},
+	{0x3d, KEY_2},
+	{0x3c, KEY_3},
+	{0x3b, KEY_4},
+	{0x3a, KEY_5},
+	{0x39, KEY_6},
+	{0x38, KEY_7},
+	{0x37, KEY_8},
+	{0x36, KEY_9},
+	{0x3f, KEY_0},
+	{0x35, KEY_DIGITS},
+	{0x2c, KEY_TV},
+
+	{0x20, KEY_INFO},
+	{0x2d, KEY_MENU},
+	{0x1f, KEY_UP},
+	{0x1e, KEY_DOWN},
+	{0x2e, KEY_LEFT},
+	{0x2f, KEY_RIGHT},
+	{0x28, KEY_OK},
+	{0x10, KEY_EPG},
+	{0x1d, KEY_BACK},
+
+	{0x14, KEY_RED},
+	{0x13, KEY_GREEN},
+	{0x12, KEY_YELLOW},
+	{0x11, KEY_BLUE},
+
+	{0x09, KEY_SELECT},
+	{0x03, KEY_TEXT},
+	{0x16, KEY_STOP},
+	{0x30, KEY_HELP},
+};
+
+static struct rc_map_list technisat_ts35_map = {
+	.map = {
+		.scan    = technisat_ts35,
+		.size    = ARRAY_SIZE(technisat_ts35),
+		.rc_type = RC_TYPE_UNKNOWN,
+		.name    = RC_MAP_TECHNISAT_TS35,
+	}
+};
+
+static int __init init_rc_map(void)
+{
+	return rc_map_register(&technisat_ts35_map);
+}
+
+static void __exit exit_rc_map(void)
+{
+	rc_map_unregister(&technisat_ts35_map);
+}
+
+module_init(init_rc_map)
+module_exit(exit_rc_map)
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/keymaps/rc-technisat-usb2.c b/drivers/media/rc/keymaps/rc-technisat-usb2.c
new file mode 100644
index 0000000..f9733bb
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-technisat-usb2.c
@@ -0,0 +1,94 @@
+/* rc-technisat-usb2.c - Keytable for SkyStar HD USB
+ *
+ * Copyright (C) 2010 Patrick Boettcher,
+ *                    Kernel Labs Inc. PO Box 745, St James, NY 11780
+ *
+ * Development was sponsored by Technisat Digital UK Limited, whose
+ * registered office is Witan Gate House 500 - 600 Witan Gate West,
+ * Milton Keynes, MK9 1SH
+ *
+ * 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.
+ *
+ *
+ * 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.
+ *
+ * THIS PROGRAM IS PROVIDED "AS IS" AND BOTH THE COPYRIGHT HOLDER AND
+ * TECHNISAT DIGITAL UK LTD DISCLAIM ALL WARRANTIES WITH REGARD TO
+ * THIS PROGRAM INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY OR
+ * FITNESS FOR A PARTICULAR PURPOSE.  NEITHER THE COPYRIGHT HOLDER
+ * NOR TECHNISAT DIGITAL UK LIMITED SHALL BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS PROGRAM. See the
+ * GNU General Public License for more details.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table technisat_usb2[] = {
+	{0x0a0c, KEY_POWER},
+	{0x0a01, KEY_1},
+	{0x0a02, KEY_2},
+	{0x0a03, KEY_3},
+	{0x0a0d, KEY_MUTE},
+	{0x0a04, KEY_4},
+	{0x0a05, KEY_5},
+	{0x0a06, KEY_6},
+	{0x0a38, KEY_VIDEO}, /* EXT */
+	{0x0a07, KEY_7},
+	{0x0a08, KEY_8},
+	{0x0a09, KEY_9},
+	{0x0a00, KEY_0},
+	{0x0a4f, KEY_INFO},
+	{0x0a20, KEY_CHANNELUP},
+	{0x0a52, KEY_MENU},
+	{0x0a11, KEY_VOLUMEUP},
+	{0x0a57, KEY_OK},
+	{0x0a10, KEY_VOLUMEDOWN},
+	{0x0a2f, KEY_EPG},
+	{0x0a21, KEY_CHANNELDOWN},
+	{0x0a22, KEY_REFRESH},
+	{0x0a3c, KEY_TEXT},
+	{0x0a76, KEY_ENTER}, /* HOOK */
+	{0x0a0f, KEY_HELP},
+	{0x0a6b, KEY_RED},
+	{0x0a6c, KEY_GREEN},
+	{0x0a6d, KEY_YELLOW},
+	{0x0a6e, KEY_BLUE},
+	{0x0a29, KEY_STOP},
+	{0x0a23, KEY_LANGUAGE},
+	{0x0a53, KEY_TV},
+	{0x0a0a, KEY_PROGRAM},
+};
+
+static struct rc_map_list technisat_usb2_map = {
+	.map = {
+		.scan    = technisat_usb2,
+		.size    = ARRAY_SIZE(technisat_usb2),
+		.rc_type = RC_TYPE_RC5,
+		.name    = RC_MAP_TECHNISAT_USB2,
+	}
+};
+
+static int __init init_rc_map(void)
+{
+	return rc_map_register(&technisat_usb2_map);
+}
+
+static void __exit exit_rc_map(void)
+{
+	rc_map_unregister(&technisat_usb2_map);
+}
+
+module_init(init_rc_map)
+module_exit(exit_rc_map)
+
+MODULE_AUTHOR("Patrick Boettcher <pboettcher@kernellabs.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/keymaps/rc-terratec-cinergy-c-pci.c b/drivers/media/rc/keymaps/rc-terratec-cinergy-c-pci.c
new file mode 100644
index 0000000..7958f45
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-terratec-cinergy-c-pci.c
@@ -0,0 +1,88 @@
+/* keytable for Terratec Cinergy C PCI Remote Controller
+ *
+ * Copyright (c) 2010 by Igor M. Liplianin <liplianin@me.by>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table terratec_cinergy_c_pci[] = {
+	{ 0x3e, KEY_POWER},
+	{ 0x3d, KEY_1},
+	{ 0x3c, KEY_2},
+	{ 0x3b, KEY_3},
+	{ 0x3a, KEY_4},
+	{ 0x39, KEY_5},
+	{ 0x38, KEY_6},
+	{ 0x37, KEY_7},
+	{ 0x36, KEY_8},
+	{ 0x35, KEY_9},
+	{ 0x34, KEY_VIDEO_NEXT}, /* AV */
+	{ 0x33, KEY_0},
+	{ 0x32, KEY_REFRESH},
+	{ 0x30, KEY_EPG},
+	{ 0x2f, KEY_UP},
+	{ 0x2e, KEY_LEFT},
+	{ 0x2d, KEY_OK},
+	{ 0x2c, KEY_RIGHT},
+	{ 0x2b, KEY_DOWN},
+	{ 0x29, KEY_INFO},
+	{ 0x28, KEY_RED},
+	{ 0x27, KEY_GREEN},
+	{ 0x26, KEY_YELLOW},
+	{ 0x25, KEY_BLUE},
+	{ 0x24, KEY_CHANNELUP},
+	{ 0x23, KEY_VOLUMEUP},
+	{ 0x22, KEY_MUTE},
+	{ 0x21, KEY_VOLUMEDOWN},
+	{ 0x20, KEY_CHANNELDOWN},
+	{ 0x1f, KEY_PAUSE},
+	{ 0x1e, KEY_HOME},
+	{ 0x1d, KEY_MENU}, /* DVD Menu */
+	{ 0x1c, KEY_SUBTITLE},
+	{ 0x1b, KEY_TEXT}, /* Teletext */
+	{ 0x1a, KEY_DELETE},
+	{ 0x19, KEY_TV},
+	{ 0x18, KEY_DVD},
+	{ 0x17, KEY_STOP},
+	{ 0x16, KEY_VIDEO},
+	{ 0x15, KEY_AUDIO}, /* Music */
+	{ 0x14, KEY_SCREEN}, /* Pic */
+	{ 0x13, KEY_PLAY},
+	{ 0x12, KEY_BACK},
+	{ 0x11, KEY_REWIND},
+	{ 0x10, KEY_FASTFORWARD},
+	{ 0x0b, KEY_PREVIOUS},
+	{ 0x07, KEY_RECORD},
+	{ 0x03, KEY_NEXT},
+
+};
+
+static struct rc_map_list terratec_cinergy_c_pci_map = {
+	.map = {
+		.scan    = terratec_cinergy_c_pci,
+		.size    = ARRAY_SIZE(terratec_cinergy_c_pci),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_TERRATEC_CINERGY_C_PCI,
+	}
+};
+
+static int __init init_rc_map_terratec_cinergy_c_pci(void)
+{
+	return rc_map_register(&terratec_cinergy_c_pci_map);
+}
+
+static void __exit exit_rc_map_terratec_cinergy_c_pci(void)
+{
+	rc_map_unregister(&terratec_cinergy_c_pci_map);
+}
+
+module_init(init_rc_map_terratec_cinergy_c_pci);
+module_exit(exit_rc_map_terratec_cinergy_c_pci);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/keymaps/rc-terratec-cinergy-s2-hd.c b/drivers/media/rc/keymaps/rc-terratec-cinergy-s2-hd.c
new file mode 100644
index 0000000..1e096bb
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-terratec-cinergy-s2-hd.c
@@ -0,0 +1,86 @@
+/* keytable for Terratec Cinergy S2 HD Remote Controller
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table terratec_cinergy_s2_hd[] = {
+	{ 0x03, KEY_NEXT},               /* >| */
+	{ 0x07, KEY_RECORD},
+	{ 0x0b, KEY_PREVIOUS},           /* |< */
+	{ 0x10, KEY_FASTFORWARD},        /* >> */
+	{ 0x11, KEY_REWIND},             /* << */
+	{ 0x12, KEY_ESC},                /* Back */
+	{ 0x13, KEY_PLAY},
+	{ 0x14, KEY_IMAGES},
+	{ 0x15, KEY_AUDIO},
+	{ 0x16, KEY_MEDIA},              /* Video-Menu */
+	{ 0x17, KEY_STOP},
+	{ 0x18, KEY_DVD},
+	{ 0x19, KEY_TV},
+	{ 0x1a, KEY_DELETE},
+	{ 0x1b, KEY_TEXT},
+	{ 0x1c, KEY_SUBTITLE},
+	{ 0x1d, KEY_MENU},               /* DVD-Menu */
+	{ 0x1e, KEY_HOME},
+	{ 0x1f, KEY_PAUSE},
+	{ 0x20, KEY_CHANNELDOWN},
+	{ 0x21, KEY_VOLUMEDOWN},
+	{ 0x22, KEY_MUTE},
+	{ 0x23, KEY_VOLUMEUP},
+	{ 0x24, KEY_CHANNELUP},
+	{ 0x25, KEY_BLUE},
+	{ 0x26, KEY_YELLOW},
+	{ 0x27, KEY_GREEN},
+	{ 0x28, KEY_RED},
+	{ 0x29, KEY_INFO},
+	{ 0x2b, KEY_DOWN},
+	{ 0x2c, KEY_RIGHT},
+	{ 0x2d, KEY_OK},
+	{ 0x2e, KEY_LEFT},
+	{ 0x2f, KEY_UP},
+	{ 0x30, KEY_EPG},
+	{ 0x32, KEY_VIDEO},              /* A<=>B */
+	{ 0x33, KEY_0},
+	{ 0x34, KEY_VCR},                /* AV */
+	{ 0x35, KEY_9},
+	{ 0x36, KEY_8},
+	{ 0x37, KEY_7},
+	{ 0x38, KEY_6},
+	{ 0x39, KEY_5},
+	{ 0x3a, KEY_4},
+	{ 0x3b, KEY_3},
+	{ 0x3c, KEY_2},
+	{ 0x3d, KEY_1},
+	{ 0x3e, KEY_POWER},
+
+};
+
+static struct rc_map_list terratec_cinergy_s2_hd_map = {
+	.map = {
+		.scan    = terratec_cinergy_s2_hd,
+		.size    = ARRAY_SIZE(terratec_cinergy_s2_hd),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_TERRATEC_CINERGY_S2_HD,
+	}
+};
+
+static int __init init_rc_map_terratec_cinergy_s2_hd(void)
+{
+	return rc_map_register(&terratec_cinergy_s2_hd_map);
+}
+
+static void __exit exit_rc_map_terratec_cinergy_s2_hd(void)
+{
+	rc_map_unregister(&terratec_cinergy_s2_hd_map);
+}
+
+module_init(init_rc_map_terratec_cinergy_s2_hd);
+module_exit(exit_rc_map_terratec_cinergy_s2_hd);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/keymaps/rc-terratec-cinergy-xs.c b/drivers/media/rc/keymaps/rc-terratec-cinergy-xs.c
new file mode 100644
index 0000000..97eb83a
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-terratec-cinergy-xs.c
@@ -0,0 +1,93 @@
+/* terratec-cinergy-xs.h - Keytable for terratec_cinergy_xs Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Terratec Cinergy Hybrid T USB XS
+   Devin Heitmueller <dheitmueller@linuxtv.org>
+ */
+
+static struct rc_map_table 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},
+};
+
+static struct rc_map_list terratec_cinergy_xs_map = {
+	.map = {
+		.scan    = terratec_cinergy_xs,
+		.size    = ARRAY_SIZE(terratec_cinergy_xs),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_TERRATEC_CINERGY_XS,
+	}
+};
+
+static int __init init_rc_map_terratec_cinergy_xs(void)
+{
+	return rc_map_register(&terratec_cinergy_xs_map);
+}
+
+static void __exit exit_rc_map_terratec_cinergy_xs(void)
+{
+	rc_map_unregister(&terratec_cinergy_xs_map);
+}
+
+module_init(init_rc_map_terratec_cinergy_xs)
+module_exit(exit_rc_map_terratec_cinergy_xs)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-terratec-slim-2.c b/drivers/media/rc/keymaps/rc-terratec-slim-2.c
new file mode 100644
index 0000000..4c149ef
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-terratec-slim-2.c
@@ -0,0 +1,73 @@
+/*
+ * TerraTec remote controller keytable
+ *
+ * Copyright (C) 2011 Martin Groszhauser <mgroszhauser@gmail.com>
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ *    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.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * TerraTec slim remote, 6 rows, 3 columns.
+ * Keytable from Martin Groszhauser <mgroszhauser@gmail.com>
+ */
+static struct rc_map_table terratec_slim_2[] = {
+	{ 0x8001, KEY_MUTE },            /* MUTE */
+	{ 0x8002, KEY_VOLUMEDOWN },
+	{ 0x8003, KEY_CHANNELDOWN },
+	{ 0x8004, KEY_1 },
+	{ 0x8005, KEY_2 },
+	{ 0x8006, KEY_3 },
+	{ 0x8007, KEY_4 },
+	{ 0x8008, KEY_5 },
+	{ 0x8009, KEY_6 },
+	{ 0x800a, KEY_7 },
+	{ 0x800c, KEY_ZOOM },            /* [fullscreen] */
+	{ 0x800d, KEY_0 },
+	{ 0x800e, KEY_AGAIN },           /* [two arrows forming a circle] */
+	{ 0x8012, KEY_POWER2 },          /* [red power button] */
+	{ 0x801a, KEY_VOLUMEUP },
+	{ 0x801b, KEY_8 },
+	{ 0x801e, KEY_CHANNELUP },
+	{ 0x801f, KEY_9 },
+};
+
+static struct rc_map_list terratec_slim_2_map = {
+	.map = {
+		.scan    = terratec_slim_2,
+		.size    = ARRAY_SIZE(terratec_slim_2),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_TERRATEC_SLIM_2,
+	}
+};
+
+static int __init init_rc_map_terratec_slim_2(void)
+{
+	return rc_map_register(&terratec_slim_2_map);
+}
+
+static void __exit exit_rc_map_terratec_slim_2(void)
+{
+	rc_map_unregister(&terratec_slim_2_map);
+}
+
+module_init(init_rc_map_terratec_slim_2)
+module_exit(exit_rc_map_terratec_slim_2)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-terratec-slim.c b/drivers/media/rc/keymaps/rc-terratec-slim.c
new file mode 100644
index 0000000..3d8a19c
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-terratec-slim.c
@@ -0,0 +1,80 @@
+/*
+ * TerraTec remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* TerraTec slim remote, 7 rows, 4 columns. */
+/* Uses NEC extended 0x02bd. */
+static struct rc_map_table terratec_slim[] = {
+	{ 0x02bd00, KEY_1 },
+	{ 0x02bd01, KEY_2 },
+	{ 0x02bd02, KEY_3 },
+	{ 0x02bd03, KEY_4 },
+	{ 0x02bd04, KEY_5 },
+	{ 0x02bd05, KEY_6 },
+	{ 0x02bd06, KEY_7 },
+	{ 0x02bd07, KEY_8 },
+	{ 0x02bd08, KEY_9 },
+	{ 0x02bd09, KEY_0 },
+	{ 0x02bd0a, KEY_MUTE },
+	{ 0x02bd0b, KEY_NEW },             /* symbol: PIP */
+	{ 0x02bd0e, KEY_VOLUMEDOWN },
+	{ 0x02bd0f, KEY_PLAYPAUSE },
+	{ 0x02bd10, KEY_RIGHT },
+	{ 0x02bd11, KEY_LEFT },
+	{ 0x02bd12, KEY_UP },
+	{ 0x02bd13, KEY_DOWN },
+	{ 0x02bd15, KEY_OK },
+	{ 0x02bd16, KEY_STOP },
+	{ 0x02bd17, KEY_CAMERA },          /* snapshot */
+	{ 0x02bd18, KEY_CHANNELUP },
+	{ 0x02bd19, KEY_RECORD },
+	{ 0x02bd1a, KEY_CHANNELDOWN },
+	{ 0x02bd1c, KEY_ESC },
+	{ 0x02bd1f, KEY_VOLUMEUP },
+	{ 0x02bd44, KEY_EPG },
+	{ 0x02bd45, KEY_POWER2 },          /* [red power button] */
+};
+
+static struct rc_map_list terratec_slim_map = {
+	.map = {
+		.scan    = terratec_slim,
+		.size    = ARRAY_SIZE(terratec_slim),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_TERRATEC_SLIM,
+	}
+};
+
+static int __init init_rc_map_terratec_slim(void)
+{
+	return rc_map_register(&terratec_slim_map);
+}
+
+static void __exit exit_rc_map_terratec_slim(void)
+{
+	rc_map_unregister(&terratec_slim_map);
+}
+
+module_init(init_rc_map_terratec_slim)
+module_exit(exit_rc_map_terratec_slim)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-tevii-nec.c b/drivers/media/rc/keymaps/rc-tevii-nec.c
new file mode 100644
index 0000000..38e0c08
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-tevii-nec.c
@@ -0,0 +1,89 @@
+/* tevii-nec.h - Keytable for tevii_nec Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table tevii_nec[] = {
+	{ 0x0a, KEY_POWER2},
+	{ 0x0c, KEY_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_MENU},
+	{ 0x0f, KEY_VOLUMEDOWN},
+	{ 0x1a, KEY_LAST},
+	{ 0x0e, KEY_OPEN},
+	{ 0x04, KEY_RECORD},
+	{ 0x09, KEY_VOLUMEUP},
+	{ 0x08, KEY_CHANNELUP},
+	{ 0x07, KEY_PVR},
+	{ 0x0b, KEY_TIME},
+	{ 0x02, KEY_RIGHT},
+	{ 0x03, KEY_LEFT},
+	{ 0x00, KEY_UP},
+	{ 0x1f, KEY_OK},
+	{ 0x01, KEY_DOWN},
+	{ 0x05, KEY_TUNER},
+	{ 0x06, KEY_CHANNELDOWN},
+	{ 0x40, KEY_PLAYPAUSE},
+	{ 0x1e, KEY_REWIND},
+	{ 0x1b, KEY_FAVORITES},
+	{ 0x1d, KEY_BACK},
+	{ 0x4d, KEY_FASTFORWARD},
+	{ 0x44, KEY_EPG},
+	{ 0x4c, KEY_INFO},
+	{ 0x41, KEY_AB},
+	{ 0x43, KEY_AUDIO},
+	{ 0x45, KEY_SUBTITLE},
+	{ 0x4a, KEY_LIST},
+	{ 0x46, KEY_F1},
+	{ 0x47, KEY_F2},
+	{ 0x5e, KEY_F3},
+	{ 0x5c, KEY_F4},
+	{ 0x52, KEY_F5},
+	{ 0x5a, KEY_F6},
+	{ 0x56, KEY_MODE},
+	{ 0x58, KEY_SWITCHVIDEOMODE},
+};
+
+static struct rc_map_list tevii_nec_map = {
+	.map = {
+		.scan    = tevii_nec,
+		.size    = ARRAY_SIZE(tevii_nec),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_TEVII_NEC,
+	}
+};
+
+static int __init init_rc_map_tevii_nec(void)
+{
+	return rc_map_register(&tevii_nec_map);
+}
+
+static void __exit exit_rc_map_tevii_nec(void)
+{
+	rc_map_unregister(&tevii_nec_map);
+}
+
+module_init(init_rc_map_tevii_nec)
+module_exit(exit_rc_map_tevii_nec)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-tivo.c b/drivers/media/rc/keymaps/rc-tivo.c
new file mode 100644
index 0000000..454e062
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-tivo.c
@@ -0,0 +1,99 @@
+/* rc-tivo.c - Keytable for TiVo remotes
+ *
+ * Copyright (c) 2011 by Jarod Wilson <jarod@redhat.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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/*
+ * Initial mapping is for the TiVo remote included in the Nero LiquidTV bundle,
+ * which also ships with a TiVo-branded IR transceiver, supported by the mceusb
+ * driver. Note that the remote uses an NEC-ish protocol, but instead of having
+ * a command/not_command pair, it has a vendor ID of 0xa10c, but some keys, the
+ * NEC extended checksums do pass, so the table presently has the intended
+ * values and the checksum-passed versions for those keys.
+ */
+static struct rc_map_table tivo[] = {
+	{ 0xa10c900f, KEY_MEDIA },	/* TiVo Button */
+	{ 0xa10c0807, KEY_POWER2 },	/* TV Power */
+	{ 0xa10c8807, KEY_TV },		/* Live TV/Swap */
+	{ 0xa10c2c03, KEY_VIDEO_NEXT },	/* TV Input */
+	{ 0xa10cc807, KEY_INFO },
+	{ 0xa10cfa05, KEY_CYCLEWINDOWS }, /* Window */
+	{ 0x0085305f, KEY_CYCLEWINDOWS },
+	{ 0xa10c6c03, KEY_EPG },	/* Guide */
+
+	{ 0xa10c2807, KEY_UP },
+	{ 0xa10c6807, KEY_DOWN },
+	{ 0xa10ce807, KEY_LEFT },
+	{ 0xa10ca807, KEY_RIGHT },
+
+	{ 0xa10c1807, KEY_SCROLLDOWN },	/* Red Thumbs Down */
+	{ 0xa10c9807, KEY_SELECT },
+	{ 0xa10c5807, KEY_SCROLLUP },	/* Green Thumbs Up */
+
+	{ 0xa10c3807, KEY_VOLUMEUP },
+	{ 0xa10cb807, KEY_VOLUMEDOWN },
+	{ 0xa10cd807, KEY_MUTE },
+	{ 0xa10c040b, KEY_RECORD },
+	{ 0xa10c7807, KEY_CHANNELUP },
+	{ 0xa10cf807, KEY_CHANNELDOWN },
+	{ 0x0085301f, KEY_CHANNELDOWN },
+
+	{ 0xa10c840b, KEY_PLAY },
+	{ 0xa10cc40b, KEY_PAUSE },
+	{ 0xa10ca40b, KEY_SLOW },
+	{ 0xa10c440b, KEY_REWIND },
+	{ 0xa10c240b, KEY_FASTFORWARD },
+	{ 0xa10c640b, KEY_PREVIOUS },
+	{ 0xa10ce40b, KEY_NEXT },	/* ->| */
+
+	{ 0xa10c220d, KEY_ZOOM },	/* Aspect */
+	{ 0xa10c120d, KEY_STOP },
+	{ 0xa10c520d, KEY_DVD },	/* DVD Menu */
+
+	{ 0xa10c140b, KEY_NUMERIC_1 },
+	{ 0xa10c940b, KEY_NUMERIC_2 },
+	{ 0xa10c540b, KEY_NUMERIC_3 },
+	{ 0xa10cd40b, KEY_NUMERIC_4 },
+	{ 0xa10c340b, KEY_NUMERIC_5 },
+	{ 0xa10cb40b, KEY_NUMERIC_6 },
+	{ 0xa10c740b, KEY_NUMERIC_7 },
+	{ 0xa10cf40b, KEY_NUMERIC_8 },
+	{ 0x0085302f, KEY_NUMERIC_8 },
+	{ 0xa10c0c03, KEY_NUMERIC_9 },
+	{ 0xa10c8c03, KEY_NUMERIC_0 },
+	{ 0xa10ccc03, KEY_ENTER },
+	{ 0xa10c4c03, KEY_CLEAR },
+};
+
+static struct rc_map_list tivo_map = {
+	.map = {
+		.scan    = tivo,
+		.size    = ARRAY_SIZE(tivo),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_TIVO,
+	}
+};
+
+static int __init init_rc_map_tivo(void)
+{
+	return rc_map_register(&tivo_map);
+}
+
+static void __exit exit_rc_map_tivo(void)
+{
+	rc_map_unregister(&tivo_map);
+}
+
+module_init(init_rc_map_tivo)
+module_exit(exit_rc_map_tivo)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
diff --git a/drivers/media/rc/keymaps/rc-total-media-in-hand-02.c b/drivers/media/rc/keymaps/rc-total-media-in-hand-02.c
new file mode 100644
index 0000000..47270f7
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-total-media-in-hand-02.c
@@ -0,0 +1,86 @@
+/*
+ * Total Media In Hand_02 remote controller keytable for Mygica X8507
+ *
+ * Copyright (C) 2012 Alfredo J. Delaiti <alfredodelaiti@netscape.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.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+
+static struct rc_map_table total_media_in_hand_02[] = {
+	{ 0x0000, KEY_0 },
+	{ 0x0001, KEY_1 },
+	{ 0x0002, KEY_2 },
+	{ 0x0003, KEY_3 },
+	{ 0x0004, KEY_4 },
+	{ 0x0005, KEY_5 },
+	{ 0x0006, KEY_6 },
+	{ 0x0007, KEY_7 },
+	{ 0x0008, KEY_8 },
+	{ 0x0009, KEY_9 },
+	{ 0x000a, KEY_MUTE },
+	{ 0x000b, KEY_STOP },                   /* Stop */
+	{ 0x000c, KEY_POWER2 },                 /* Turn on/off application */
+	{ 0x000d, KEY_OK },                     /* OK */
+	{ 0x000e, KEY_CAMERA },                 /* Snapshot */
+	{ 0x000f, KEY_ZOOM },                   /* Full Screen/Restore */
+	{ 0x0010, KEY_RIGHT },                  /* Right arrow */
+	{ 0x0011, KEY_LEFT },                   /* Left arrow */
+	{ 0x0012, KEY_CHANNELUP },
+	{ 0x0013, KEY_CHANNELDOWN },
+	{ 0x0014, KEY_SHUFFLE },
+	{ 0x0016, KEY_PAUSE },
+	{ 0x0017, KEY_PLAY },                   /* Play */
+	{ 0x001e, KEY_TIME },                   /* Time Shift */
+	{ 0x001f, KEY_RECORD },
+	{ 0x0020, KEY_UP },
+	{ 0x0021, KEY_DOWN },
+	{ 0x0025, KEY_POWER },                  /* Turn off computer */
+	{ 0x0026, KEY_REWIND },                 /* FR << */
+	{ 0x0027, KEY_FASTFORWARD },            /* FF >> */
+	{ 0x0029, KEY_ESC },
+	{ 0x002b, KEY_VOLUMEUP },
+	{ 0x002c, KEY_VOLUMEDOWN },
+	{ 0x002d, KEY_CHANNEL },                /* CH Surfing */
+	{ 0x0038, KEY_VIDEO },                  /* TV/AV/S-Video/YPbPr */
+};
+
+static struct rc_map_list total_media_in_hand_02_map = {
+	.map = {
+		.scan    = total_media_in_hand_02,
+		.size    = ARRAY_SIZE(total_media_in_hand_02),
+		.rc_type = RC_TYPE_RC5,
+		.name    = RC_MAP_TOTAL_MEDIA_IN_HAND_02,
+	}
+};
+
+static int __init init_rc_map_total_media_in_hand_02(void)
+{
+	return rc_map_register(&total_media_in_hand_02_map);
+}
+
+static void __exit exit_rc_map_total_media_in_hand_02(void)
+{
+	rc_map_unregister(&total_media_in_hand_02_map);
+}
+
+module_init(init_rc_map_total_media_in_hand_02)
+module_exit(exit_rc_map_total_media_in_hand_02)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(" Alfredo J. Delaiti <alfredodelaiti@netscape.net>");
diff --git a/drivers/media/rc/keymaps/rc-total-media-in-hand.c b/drivers/media/rc/keymaps/rc-total-media-in-hand.c
new file mode 100644
index 0000000..5b9f9ec
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-total-media-in-hand.c
@@ -0,0 +1,86 @@
+/*
+ * Total Media In Hand remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Uses NEC extended 0x02bd */
+static struct rc_map_table total_media_in_hand[] = {
+	{ 0x02bd00, KEY_1 },
+	{ 0x02bd01, KEY_2 },
+	{ 0x02bd02, KEY_3 },
+	{ 0x02bd03, KEY_4 },
+	{ 0x02bd04, KEY_5 },
+	{ 0x02bd05, KEY_6 },
+	{ 0x02bd06, KEY_7 },
+	{ 0x02bd07, KEY_8 },
+	{ 0x02bd08, KEY_9 },
+	{ 0x02bd09, KEY_0 },
+	{ 0x02bd0a, KEY_MUTE },
+	{ 0x02bd0b, KEY_CYCLEWINDOWS },    /* yellow, [min / max] */
+	{ 0x02bd0c, KEY_VIDEO },           /* TV / AV */
+	{ 0x02bd0e, KEY_VOLUMEDOWN },
+	{ 0x02bd0f, KEY_TIME },            /* TimeShift */
+	{ 0x02bd10, KEY_RIGHT },           /* right arrow */
+	{ 0x02bd11, KEY_LEFT },            /* left arrow */
+	{ 0x02bd12, KEY_UP },              /* up arrow */
+	{ 0x02bd13, KEY_DOWN },            /* down arrow */
+	{ 0x02bd14, KEY_POWER2 },          /* [red] */
+	{ 0x02bd15, KEY_OK },              /* OK */
+	{ 0x02bd16, KEY_STOP },
+	{ 0x02bd17, KEY_CAMERA },          /* Snapshot */
+	{ 0x02bd18, KEY_CHANNELUP },
+	{ 0x02bd19, KEY_RECORD },
+	{ 0x02bd1a, KEY_CHANNELDOWN },
+	{ 0x02bd1c, KEY_ESC },             /* Esc */
+	{ 0x02bd1e, KEY_PLAY },
+	{ 0x02bd1f, KEY_VOLUMEUP },
+	{ 0x02bd40, KEY_PAUSE },
+	{ 0x02bd41, KEY_FASTFORWARD },     /* FF >> */
+	{ 0x02bd42, KEY_REWIND },          /* FR << */
+	{ 0x02bd43, KEY_ZOOM },            /* [window + mouse pointer] */
+	{ 0x02bd44, KEY_SHUFFLE },         /* Shuffle */
+	{ 0x02bd45, KEY_INFO },            /* [red (I)] */
+};
+
+static struct rc_map_list total_media_in_hand_map = {
+	.map = {
+		.scan    = total_media_in_hand,
+		.size    = ARRAY_SIZE(total_media_in_hand),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_TOTAL_MEDIA_IN_HAND,
+	}
+};
+
+static int __init init_rc_map_total_media_in_hand(void)
+{
+	return rc_map_register(&total_media_in_hand_map);
+}
+
+static void __exit exit_rc_map_total_media_in_hand(void)
+{
+	rc_map_unregister(&total_media_in_hand_map);
+}
+
+module_init(init_rc_map_total_media_in_hand)
+module_exit(exit_rc_map_total_media_in_hand)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-trekstor.c b/drivers/media/rc/keymaps/rc-trekstor.c
new file mode 100644
index 0000000..f9a2e0f
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-trekstor.c
@@ -0,0 +1,81 @@
+/*
+ * TrekStor remote controller keytable
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ *    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.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* TrekStor DVB-T USB Stick remote controller. */
+/* Imported from af9015.h.
+   Initial keytable was from Marc Schneider <macke@macke.org> */
+static struct rc_map_table trekstor[] = {
+	{ 0x0084, KEY_0 },
+	{ 0x0085, KEY_MUTE },            /* Mute */
+	{ 0x0086, KEY_HOMEPAGE },        /* Home */
+	{ 0x0087, KEY_UP },              /* Up */
+	{ 0x0088, KEY_OK },              /* OK */
+	{ 0x0089, KEY_RIGHT },           /* Right */
+	{ 0x008a, KEY_FASTFORWARD },     /* Fast forward */
+	{ 0x008b, KEY_VOLUMEUP },        /* Volume + */
+	{ 0x008c, KEY_DOWN },            /* Down */
+	{ 0x008d, KEY_PLAY },            /* Play/Pause */
+	{ 0x008e, KEY_STOP },            /* Stop */
+	{ 0x008f, KEY_EPG },             /* Info/EPG */
+	{ 0x0090, KEY_7 },
+	{ 0x0091, KEY_4 },
+	{ 0x0092, KEY_1 },
+	{ 0x0093, KEY_CHANNELDOWN },     /* Channel - */
+	{ 0x0094, KEY_8 },
+	{ 0x0095, KEY_5 },
+	{ 0x0096, KEY_2 },
+	{ 0x0097, KEY_CHANNELUP },       /* Channel + */
+	{ 0x0098, KEY_9 },
+	{ 0x0099, KEY_6 },
+	{ 0x009a, KEY_3 },
+	{ 0x009b, KEY_VOLUMEDOWN },      /* Volume - */
+	{ 0x009c, KEY_TV },              /* TV */
+	{ 0x009d, KEY_RECORD },          /* Record */
+	{ 0x009e, KEY_REWIND },          /* Rewind */
+	{ 0x009f, KEY_LEFT },            /* Left */
+};
+
+static struct rc_map_list trekstor_map = {
+	.map = {
+		.scan    = trekstor,
+		.size    = ARRAY_SIZE(trekstor),
+		.rc_type = RC_TYPE_NEC,
+		.name    = RC_MAP_TREKSTOR,
+	}
+};
+
+static int __init init_rc_map_trekstor(void)
+{
+	return rc_map_register(&trekstor_map);
+}
+
+static void __exit exit_rc_map_trekstor(void)
+{
+	rc_map_unregister(&trekstor_map);
+}
+
+module_init(init_rc_map_trekstor)
+module_exit(exit_rc_map_trekstor)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
diff --git a/drivers/media/rc/keymaps/rc-tt-1500.c b/drivers/media/rc/keymaps/rc-tt-1500.c
new file mode 100644
index 0000000..c766d3b
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-tt-1500.c
@@ -0,0 +1,83 @@
+/* tt-1500.h - Keytable for tt_1500 Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* for the Technotrend 1500 bundled remotes (grey and black): */
+
+static struct rc_map_table tt_1500[] = {
+	{ 0x1501, KEY_POWER },
+	{ 0x1502, KEY_SHUFFLE },		/* ? double-arrow key */
+	{ 0x1503, KEY_1 },
+	{ 0x1504, KEY_2 },
+	{ 0x1505, KEY_3 },
+	{ 0x1506, KEY_4 },
+	{ 0x1507, KEY_5 },
+	{ 0x1508, KEY_6 },
+	{ 0x1509, KEY_7 },
+	{ 0x150a, KEY_8 },
+	{ 0x150b, KEY_9 },
+	{ 0x150c, KEY_0 },
+	{ 0x150d, KEY_UP },
+	{ 0x150e, KEY_LEFT },
+	{ 0x150f, KEY_OK },
+	{ 0x1510, KEY_RIGHT },
+	{ 0x1511, KEY_DOWN },
+	{ 0x1512, KEY_INFO },
+	{ 0x1513, KEY_EXIT },
+	{ 0x1514, KEY_RED },
+	{ 0x1515, KEY_GREEN },
+	{ 0x1516, KEY_YELLOW },
+	{ 0x1517, KEY_BLUE },
+	{ 0x1518, KEY_MUTE },
+	{ 0x1519, KEY_TEXT },
+	{ 0x151a, KEY_MODE },		/* ? TV/Radio */
+	{ 0x1521, KEY_OPTION },
+	{ 0x1522, KEY_EPG },
+	{ 0x1523, KEY_CHANNELUP },
+	{ 0x1524, KEY_CHANNELDOWN },
+	{ 0x1525, KEY_VOLUMEUP },
+	{ 0x1526, KEY_VOLUMEDOWN },
+	{ 0x1527, KEY_SETUP },
+	{ 0x153a, KEY_RECORD },		/* these keys are only in the black remote */
+	{ 0x153b, KEY_PLAY },
+	{ 0x153c, KEY_STOP },
+	{ 0x153d, KEY_REWIND },
+	{ 0x153e, KEY_PAUSE },
+	{ 0x153f, KEY_FORWARD },
+};
+
+static struct rc_map_list tt_1500_map = {
+	.map = {
+		.scan    = tt_1500,
+		.size    = ARRAY_SIZE(tt_1500),
+		.rc_type = RC_TYPE_RC5,
+		.name    = RC_MAP_TT_1500,
+	}
+};
+
+static int __init init_rc_map_tt_1500(void)
+{
+	return rc_map_register(&tt_1500_map);
+}
+
+static void __exit exit_rc_map_tt_1500(void)
+{
+	rc_map_unregister(&tt_1500_map);
+}
+
+module_init(init_rc_map_tt_1500)
+module_exit(exit_rc_map_tt_1500)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-twinhan-dtv-cab-ci.c b/drivers/media/rc/keymaps/rc-twinhan-dtv-cab-ci.c
new file mode 100644
index 0000000..202500c
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-twinhan-dtv-cab-ci.c
@@ -0,0 +1,98 @@
+/* keytable for Twinhan DTV CAB CI Remote Controller
+ *
+ * Copyright (c) 2010 by Igor M. Liplianin <liplianin@me.by>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table twinhan_dtv_cab_ci[] = {
+	{ 0x29, KEY_POWER},
+	{ 0x28, KEY_FAVORITES},
+	{ 0x30, KEY_TEXT},
+	{ 0x17, KEY_INFO},              /* Preview */
+	{ 0x23, KEY_EPG},
+	{ 0x3b, KEY_F22},               /* Record List */
+
+	{ 0x3c, KEY_1},
+	{ 0x3e, KEY_2},
+	{ 0x39, KEY_3},
+	{ 0x36, KEY_4},
+	{ 0x22, KEY_5},
+	{ 0x20, KEY_6},
+	{ 0x32, KEY_7},
+	{ 0x26, KEY_8},
+	{ 0x24, KEY_9},
+	{ 0x2a, KEY_0},
+
+	{ 0x33, KEY_CANCEL},
+	{ 0x2c, KEY_BACK},
+	{ 0x15, KEY_CLEAR},
+	{ 0x3f, KEY_TAB},
+	{ 0x10, KEY_ENTER},
+	{ 0x14, KEY_UP},
+	{ 0x0d, KEY_RIGHT},
+	{ 0x0e, KEY_DOWN},
+	{ 0x11, KEY_LEFT},
+
+	{ 0x21, KEY_VOLUMEUP},
+	{ 0x35, KEY_VOLUMEDOWN},
+	{ 0x3d, KEY_CHANNELDOWN},
+	{ 0x3a, KEY_CHANNELUP},
+	{ 0x2e, KEY_RECORD},
+	{ 0x2b, KEY_PLAY},
+	{ 0x13, KEY_PAUSE},
+	{ 0x25, KEY_STOP},
+
+	{ 0x1f, KEY_REWIND},
+	{ 0x2d, KEY_FASTFORWARD},
+	{ 0x1e, KEY_PREVIOUS},          /* Replay |< */
+	{ 0x1d, KEY_NEXT},              /* Skip   >| */
+
+	{ 0x0b, KEY_CAMERA},            /* Capture */
+	{ 0x0f, KEY_LANGUAGE},          /* SAP */
+	{ 0x18, KEY_MODE},              /* PIP */
+	{ 0x12, KEY_ZOOM},              /* Full screen */
+	{ 0x1c, KEY_SUBTITLE},
+	{ 0x2f, KEY_MUTE},
+	{ 0x16, KEY_F20},               /* L/R */
+	{ 0x38, KEY_F21},               /* Hibernate */
+
+	{ 0x37, KEY_SWITCHVIDEOMODE},   /* A/V */
+	{ 0x31, KEY_AGAIN},             /* Recall */
+	{ 0x1a, KEY_KPPLUS},            /* Zoom+ */
+	{ 0x19, KEY_KPMINUS},           /* Zoom- */
+	{ 0x27, KEY_RED},
+	{ 0x0C, KEY_GREEN},
+	{ 0x01, KEY_YELLOW},
+	{ 0x00, KEY_BLUE},
+};
+
+static struct rc_map_list twinhan_dtv_cab_ci_map = {
+	.map = {
+		.scan    = twinhan_dtv_cab_ci,
+		.size    = ARRAY_SIZE(twinhan_dtv_cab_ci),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_TWINHAN_DTV_CAB_CI,
+	}
+};
+
+static int __init init_rc_map_twinhan_dtv_cab_ci(void)
+{
+	return rc_map_register(&twinhan_dtv_cab_ci_map);
+}
+
+static void __exit exit_rc_map_twinhan_dtv_cab_ci(void)
+{
+	rc_map_unregister(&twinhan_dtv_cab_ci_map);
+}
+
+module_init(init_rc_map_twinhan_dtv_cab_ci);
+module_exit(exit_rc_map_twinhan_dtv_cab_ci);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/keymaps/rc-twinhan1027.c b/drivers/media/rc/keymaps/rc-twinhan1027.c
new file mode 100644
index 0000000..509299b
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-twinhan1027.c
@@ -0,0 +1,88 @@
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table twinhan_vp1027[] = {
+	{ 0x16, KEY_POWER2 },
+	{ 0x17, KEY_FAVORITES },
+	{ 0x0f, KEY_TEXT },
+	{ 0x48, KEY_INFO},
+	{ 0x1c, KEY_EPG },
+	{ 0x04, KEY_LIST },
+
+	{ 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 },
+	{ 0x15, KEY_0 },
+
+	{ 0x0c, KEY_CANCEL },
+	{ 0x4a, KEY_CLEAR },
+	{ 0x13, KEY_BACKSPACE },
+	{ 0x00, KEY_TAB },
+
+	{ 0x4b, KEY_UP },
+	{ 0x51, KEY_DOWN },
+	{ 0x4e, KEY_LEFT },
+	{ 0x52, KEY_RIGHT },
+	{ 0x4f, KEY_ENTER },
+
+	{ 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 },
+	{ 0x42, KEY_NEXTSONG },
+	{ 0x54, KEY_SAVE },
+	{ 0x50, KEY_LANGUAGE },
+	{ 0x47, KEY_MEDIA },
+	{ 0x4d, KEY_SCREEN },
+	{ 0x43, KEY_SUBTITLE },
+	{ 0x10, KEY_MUTE },
+	{ 0x49, KEY_AUDIO },
+	{ 0x07, KEY_SLEEP },
+	{ 0x08, KEY_VIDEO },
+	{ 0x0e, KEY_AGAIN },
+	{ 0x45, KEY_EQUAL },
+	{ 0x46, KEY_MINUS },
+	{ 0x18, KEY_RED },
+	{ 0x53, KEY_GREEN },
+	{ 0x5e, KEY_YELLOW },
+	{ 0x5f, KEY_BLUE },
+};
+
+static struct rc_map_list twinhan_vp1027_map = {
+	.map = {
+		.scan    = twinhan_vp1027,
+		.size    = ARRAY_SIZE(twinhan_vp1027),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_TWINHAN_VP1027_DVBS,
+	}
+};
+
+static int __init init_rc_map_twinhan_vp1027(void)
+{
+	return rc_map_register(&twinhan_vp1027_map);
+}
+
+static void __exit exit_rc_map_twinhan_vp1027(void)
+{
+	rc_map_unregister(&twinhan_vp1027_map);
+}
+
+module_init(init_rc_map_twinhan_vp1027)
+module_exit(exit_rc_map_twinhan_vp1027)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sergey Ivanov <123kash@gmail.com>");
diff --git a/drivers/media/rc/keymaps/rc-videomate-m1f.c b/drivers/media/rc/keymaps/rc-videomate-m1f.c
new file mode 100644
index 0000000..23ee05e
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-videomate-m1f.c
@@ -0,0 +1,93 @@
+/* videomate-k100.h - Keytable for videomate_k100 Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Pavel Osnova <pvosnova@gmail.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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table videomate_k100[] = {
+	{ 0x01, KEY_POWER },
+	{ 0x31, KEY_TUNER },
+	{ 0x33, KEY_VIDEO },
+	{ 0x2f, KEY_RADIO },
+	{ 0x30, KEY_CAMERA },
+	{ 0x2d, KEY_NEW }, /* TV record button */
+	{ 0x17, KEY_CYCLEWINDOWS },
+	{ 0x2c, KEY_ANGLE },
+	{ 0x2b, KEY_LANGUAGE },
+	{ 0x32, KEY_SEARCH }, /* '...' button */
+	{ 0x11, KEY_UP },
+	{ 0x13, KEY_LEFT },
+	{ 0x15, KEY_OK },
+	{ 0x14, KEY_RIGHT },
+	{ 0x12, KEY_DOWN },
+	{ 0x16, KEY_BACKSPACE },
+	{ 0x02, KEY_ZOOM }, /* WIN key */
+	{ 0x04, KEY_INFO },
+	{ 0x05, KEY_VOLUMEUP },
+	{ 0x03, KEY_MUTE },
+	{ 0x07, KEY_CHANNELUP },
+	{ 0x06, KEY_VOLUMEDOWN },
+	{ 0x08, KEY_CHANNELDOWN },
+	{ 0x0c, KEY_RECORD },
+	{ 0x0e, KEY_STOP },
+	{ 0x0a, KEY_BACK },
+	{ 0x0b, KEY_PLAY },
+	{ 0x09, KEY_FORWARD },
+	{ 0x10, KEY_PREVIOUS },
+	{ 0x0d, KEY_PAUSE },
+	{ 0x0f, KEY_NEXT },
+	{ 0x1e, KEY_1 },
+	{ 0x1f, KEY_2 },
+	{ 0x20, KEY_3 },
+	{ 0x21, KEY_4 },
+	{ 0x22, KEY_5 },
+	{ 0x23, KEY_6 },
+	{ 0x24, KEY_7 },
+	{ 0x25, KEY_8 },
+	{ 0x26, KEY_9 },
+	{ 0x2a, KEY_NUMERIC_STAR }, /* * key */
+	{ 0x1d, KEY_0 },
+	{ 0x29, KEY_SUBTITLE }, /* # key */
+	{ 0x27, KEY_CLEAR },
+	{ 0x34, KEY_SCREEN },
+	{ 0x28, KEY_ENTER },
+	{ 0x19, KEY_RED },
+	{ 0x1a, KEY_GREEN },
+	{ 0x1b, KEY_YELLOW },
+	{ 0x1c, KEY_BLUE },
+	{ 0x18, KEY_TEXT },
+};
+
+static struct rc_map_list videomate_k100_map = {
+	.map = {
+		.scan    = videomate_k100,
+		.size    = ARRAY_SIZE(videomate_k100),
+		.rc_type = RC_TYPE_UNKNOWN,     /* Legacy IR type */
+		.name    = RC_MAP_VIDEOMATE_K100,
+	}
+};
+
+static int __init init_rc_map_videomate_k100(void)
+{
+	return rc_map_register(&videomate_k100_map);
+}
+
+static void __exit exit_rc_map_videomate_k100(void)
+{
+	rc_map_unregister(&videomate_k100_map);
+}
+
+module_init(init_rc_map_videomate_k100)
+module_exit(exit_rc_map_videomate_k100)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pavel Osnova <pvosnova@gmail.com>");
diff --git a/drivers/media/rc/keymaps/rc-videomate-s350.c b/drivers/media/rc/keymaps/rc-videomate-s350.c
new file mode 100644
index 0000000..8a35477
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-videomate-s350.c
@@ -0,0 +1,86 @@
+/* videomate-s350.h - Keytable for videomate_s350 Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table 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},
+};
+
+static struct rc_map_list videomate_s350_map = {
+	.map = {
+		.scan    = videomate_s350,
+		.size    = ARRAY_SIZE(videomate_s350),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_VIDEOMATE_S350,
+	}
+};
+
+static int __init init_rc_map_videomate_s350(void)
+{
+	return rc_map_register(&videomate_s350_map);
+}
+
+static void __exit exit_rc_map_videomate_s350(void)
+{
+	rc_map_unregister(&videomate_s350_map);
+}
+
+module_init(init_rc_map_videomate_s350)
+module_exit(exit_rc_map_videomate_s350)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-videomate-tv-pvr.c b/drivers/media/rc/keymaps/rc-videomate-tv-pvr.c
new file mode 100644
index 0000000..eb0cda7
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-videomate-tv-pvr.c
@@ -0,0 +1,88 @@
+/* videomate-tv-pvr.h - Keytable for videomate_tv_pvr Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table 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 },
+};
+
+static struct rc_map_list videomate_tv_pvr_map = {
+	.map = {
+		.scan    = videomate_tv_pvr,
+		.size    = ARRAY_SIZE(videomate_tv_pvr),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_VIDEOMATE_TV_PVR,
+	}
+};
+
+static int __init init_rc_map_videomate_tv_pvr(void)
+{
+	return rc_map_register(&videomate_tv_pvr_map);
+}
+
+static void __exit exit_rc_map_videomate_tv_pvr(void)
+{
+	rc_map_unregister(&videomate_tv_pvr_map);
+}
+
+module_init(init_rc_map_videomate_tv_pvr)
+module_exit(exit_rc_map_videomate_tv_pvr)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-winfast-usbii-deluxe.c b/drivers/media/rc/keymaps/rc-winfast-usbii-deluxe.c
new file mode 100644
index 0000000..c1dd598
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-winfast-usbii-deluxe.c
@@ -0,0 +1,83 @@
+/* winfast-usbii-deluxe.h - Keytable for winfast_usbii_deluxe Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Leadtek Winfast TV USB II Deluxe remote
+   Magnus Alm <magnus.alm@gmail.com>
+ */
+
+static struct rc_map_table winfast_usbii_deluxe[] = {
+	{ 0x62, KEY_0},
+	{ 0x75, KEY_1},
+	{ 0x76, KEY_2},
+	{ 0x77, KEY_3},
+	{ 0x79, KEY_4},
+	{ 0x7a, KEY_5},
+	{ 0x7b, KEY_6},
+	{ 0x7d, KEY_7},
+	{ 0x7e, KEY_8},
+	{ 0x7f, KEY_9},
+
+	{ 0x38, KEY_CAMERA},		/* SNAPSHOT */
+	{ 0x37, KEY_RECORD},		/* RECORD */
+	{ 0x35, KEY_TIME},		/* TIMESHIFT */
+
+	{ 0x74, KEY_VOLUMEUP},		/* VOLUMEUP */
+	{ 0x78, KEY_VOLUMEDOWN},	/* VOLUMEDOWN */
+	{ 0x64, KEY_MUTE},		/* MUTE */
+
+	{ 0x21, KEY_CHANNEL},		/* SURF */
+	{ 0x7c, KEY_CHANNELUP},		/* CHANNELUP */
+	{ 0x60, KEY_CHANNELDOWN},	/* CHANNELDOWN */
+	{ 0x61, KEY_LAST},		/* LAST CHANNEL (RECALL) */
+
+	{ 0x72, KEY_VIDEO}, 		/* INPUT MODES (TV/FM) */
+
+	{ 0x70, KEY_POWER2},		/* TV ON/OFF */
+
+	{ 0x39, KEY_CYCLEWINDOWS},	/* MINIMIZE (BOSS) */
+	{ 0x3a, KEY_NEW},		/* PIP */
+	{ 0x73, KEY_ZOOM},		/* FULLSECREEN */
+
+	{ 0x66, KEY_INFO},		/* OSD (DISPLAY) */
+
+	{ 0x31, KEY_DOT},		/* '.' */
+	{ 0x63, KEY_ENTER},		/* ENTER */
+
+};
+
+static struct rc_map_list winfast_usbii_deluxe_map = {
+	.map = {
+		.scan    = winfast_usbii_deluxe,
+		.size    = ARRAY_SIZE(winfast_usbii_deluxe),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_WINFAST_USBII_DELUXE,
+	}
+};
+
+static int __init init_rc_map_winfast_usbii_deluxe(void)
+{
+	return rc_map_register(&winfast_usbii_deluxe_map);
+}
+
+static void __exit exit_rc_map_winfast_usbii_deluxe(void)
+{
+	rc_map_unregister(&winfast_usbii_deluxe_map);
+}
+
+module_init(init_rc_map_winfast_usbii_deluxe)
+module_exit(exit_rc_map_winfast_usbii_deluxe)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/keymaps/rc-winfast.c b/drivers/media/rc/keymaps/rc-winfast.c
new file mode 100644
index 0000000..8a779da
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-winfast.c
@@ -0,0 +1,103 @@
+/* winfast.h - Keytable for winfast Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Table for Leadtek Winfast Remote Controls - used by both bttv and cx88 */
+
+static struct rc_map_table 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_POWER2 },
+	{ 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_RIGHT },
+	{ 0x08, KEY_LEFT },
+	{ 0x0c, KEY_UP },
+	{ 0x10, KEY_DOWN },
+	{ 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_TV2 },		/* 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_CAMERA },		/* 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 },
+
+	{ 0x1a, KEY_MODE},		/* change to MCE mode on Y04G0051 */
+	{ 0x3e, KEY_VOLUMEUP },		/* MCE +VOL, on Y04G0033 */
+	{ 0x3a, KEY_VOLUMEDOWN },	/* MCE -VOL, on Y04G0033 */
+	{ 0x3b, KEY_CHANNELUP },	/* MCE +CH,  on Y04G0033 */
+	{ 0x3f, KEY_CHANNELDOWN }	/* MCE -CH,  on Y04G0033 */
+};
+
+static struct rc_map_list winfast_map = {
+	.map = {
+		.scan    = winfast,
+		.size    = ARRAY_SIZE(winfast),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_WINFAST,
+	}
+};
+
+static int __init init_rc_map_winfast(void)
+{
+	return rc_map_register(&winfast_map);
+}
+
+static void __exit exit_rc_map_winfast(void)
+{
+	rc_map_unregister(&winfast_map);
+}
+
+module_init(init_rc_map_winfast)
+module_exit(exit_rc_map_winfast)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c
new file mode 100644
index 0000000..4de0e85
--- /dev/null
+++ b/drivers/media/rc/lirc_dev.c
@@ -0,0 +1,833 @@
+/*
+ * LIRC base driver
+ *
+ * by Artur Lipowski <alipowski@interia.pl>
+ *
+ *  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/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/completion.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/unistd.h>
+#include <linux/kthread.h>
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+
+#include <media/rc-core.h>
+#include <media/lirc.h>
+#include <media/lirc_dev.h>
+
+static bool debug;
+
+#define IRCTL_DEV_NAME	"BaseRemoteCtl"
+#define NOPLUG		-1
+#define LOGHEAD		"lirc_dev (%s[%d]): "
+
+static dev_t lirc_base_dev;
+
+struct irctl {
+	struct lirc_driver d;
+	int attached;
+	int open;
+
+	struct mutex irctl_lock;
+	struct lirc_buffer *buf;
+	unsigned int chunk_size;
+
+	struct cdev *cdev;
+
+	struct task_struct *task;
+	long jiffies_to_wait;
+};
+
+static DEFINE_MUTEX(lirc_dev_lock);
+
+static struct irctl *irctls[MAX_IRCTL_DEVICES];
+
+/* Only used for sysfs but defined to void otherwise */
+static struct class *lirc_class;
+
+/*  helper function
+ *  initializes the irctl structure
+ */
+static void lirc_irctl_init(struct irctl *ir)
+{
+	mutex_init(&ir->irctl_lock);
+	ir->d.minor = NOPLUG;
+}
+
+static void lirc_irctl_cleanup(struct irctl *ir)
+{
+	dev_dbg(ir->d.dev, LOGHEAD "cleaning up\n", ir->d.name, ir->d.minor);
+
+	device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor));
+
+	if (ir->buf != ir->d.rbuf) {
+		lirc_buffer_free(ir->buf);
+		kfree(ir->buf);
+	}
+	ir->buf = NULL;
+}
+
+/*  helper function
+ *  reads key codes from driver and puts them into buffer
+ *  returns 0 on success
+ */
+static int lirc_add_to_buf(struct irctl *ir)
+{
+	if (ir->d.add_to_buf) {
+		int res = -ENODATA;
+		int got_data = 0;
+
+		/*
+		 * service the device as long as it is returning
+		 * data and we have space
+		 */
+get_data:
+		res = ir->d.add_to_buf(ir->d.data, ir->buf);
+		if (res == 0) {
+			got_data++;
+			goto get_data;
+		}
+
+		if (res == -ENODEV)
+			kthread_stop(ir->task);
+
+		return got_data ? 0 : res;
+	}
+
+	return 0;
+}
+
+/* main function of the polling thread
+ */
+static int lirc_thread(void *irctl)
+{
+	struct irctl *ir = irctl;
+
+	dev_dbg(ir->d.dev, LOGHEAD "poll thread started\n",
+		ir->d.name, ir->d.minor);
+
+	do {
+		if (ir->open) {
+			if (ir->jiffies_to_wait) {
+				set_current_state(TASK_INTERRUPTIBLE);
+				schedule_timeout(ir->jiffies_to_wait);
+			}
+			if (kthread_should_stop())
+				break;
+			if (!lirc_add_to_buf(ir))
+				wake_up_interruptible(&ir->buf->wait_poll);
+		} else {
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule();
+		}
+	} while (!kthread_should_stop());
+
+	dev_dbg(ir->d.dev, LOGHEAD "poll thread ended\n",
+		ir->d.name, ir->d.minor);
+
+	return 0;
+}
+
+
+static const struct file_operations lirc_dev_fops = {
+	.owner		= THIS_MODULE,
+	.read		= lirc_dev_fop_read,
+	.write		= lirc_dev_fop_write,
+	.poll		= lirc_dev_fop_poll,
+	.unlocked_ioctl	= lirc_dev_fop_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= lirc_dev_fop_ioctl,
+#endif
+	.open		= lirc_dev_fop_open,
+	.release	= lirc_dev_fop_close,
+	.llseek		= noop_llseek,
+};
+
+static int lirc_cdev_add(struct irctl *ir)
+{
+	int retval = -ENOMEM;
+	struct lirc_driver *d = &ir->d;
+	struct cdev *cdev;
+
+	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+	if (!cdev)
+		goto err_out;
+
+	if (d->fops) {
+		cdev_init(cdev, d->fops);
+		cdev->owner = d->owner;
+	} else {
+		cdev_init(cdev, &lirc_dev_fops);
+		cdev->owner = THIS_MODULE;
+	}
+	retval = kobject_set_name(&cdev->kobj, "lirc%d", d->minor);
+	if (retval)
+		goto err_out;
+
+	retval = cdev_add(cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1);
+	if (retval) {
+		kobject_put(&cdev->kobj);
+		goto err_out;
+	}
+
+	ir->cdev = cdev;
+
+	return 0;
+
+err_out:
+	kfree(cdev);
+	return retval;
+}
+
+int lirc_register_driver(struct lirc_driver *d)
+{
+	struct irctl *ir;
+	int minor;
+	int bytes_in_key;
+	unsigned int chunk_size;
+	unsigned int buffer_size;
+	int err;
+
+	if (!d) {
+		printk(KERN_ERR "lirc_dev: lirc_register_driver: "
+		       "driver pointer must be not NULL!\n");
+		err = -EBADRQC;
+		goto out;
+	}
+
+	if (!d->dev) {
+		printk(KERN_ERR "%s: dev pointer not filled in!\n", __func__);
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (MAX_IRCTL_DEVICES <= d->minor) {
+		dev_err(d->dev, "lirc_dev: lirc_register_driver: "
+			"\"minor\" must be between 0 and %d (%d)!\n",
+			MAX_IRCTL_DEVICES - 1, d->minor);
+		err = -EBADRQC;
+		goto out;
+	}
+
+	if (1 > d->code_length || (BUFLEN * 8) < d->code_length) {
+		dev_err(d->dev, "lirc_dev: lirc_register_driver: "
+			"code length in bits for minor (%d) "
+			"must be less than %d!\n",
+			d->minor, BUFLEN * 8);
+		err = -EBADRQC;
+		goto out;
+	}
+
+	dev_dbg(d->dev, "lirc_dev: lirc_register_driver: sample_rate: %d\n",
+		d->sample_rate);
+	if (d->sample_rate) {
+		if (2 > d->sample_rate || HZ < d->sample_rate) {
+			dev_err(d->dev, "lirc_dev: lirc_register_driver: "
+				"sample_rate must be between 2 and %d!\n", HZ);
+			err = -EBADRQC;
+			goto out;
+		}
+		if (!d->add_to_buf) {
+			dev_err(d->dev, "lirc_dev: lirc_register_driver: "
+				"add_to_buf cannot be NULL when "
+				"sample_rate is set\n");
+			err = -EBADRQC;
+			goto out;
+		}
+	} else if (!(d->fops && d->fops->read) && !d->rbuf) {
+		dev_err(d->dev, "lirc_dev: lirc_register_driver: "
+			"fops->read and rbuf cannot all be NULL!\n");
+		err = -EBADRQC;
+		goto out;
+	} else if (!d->rbuf) {
+		if (!(d->fops && d->fops->read && d->fops->poll &&
+		      d->fops->unlocked_ioctl)) {
+			dev_err(d->dev, "lirc_dev: lirc_register_driver: "
+				"neither read, poll nor unlocked_ioctl can be NULL!\n");
+			err = -EBADRQC;
+			goto out;
+		}
+	}
+
+	mutex_lock(&lirc_dev_lock);
+
+	minor = d->minor;
+
+	if (minor < 0) {
+		/* find first free slot for driver */
+		for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++)
+			if (!irctls[minor])
+				break;
+		if (MAX_IRCTL_DEVICES == minor) {
+			dev_err(d->dev, "lirc_dev: lirc_register_driver: "
+				"no free slots for drivers!\n");
+			err = -ENOMEM;
+			goto out_lock;
+		}
+	} else if (irctls[minor]) {
+		dev_err(d->dev, "lirc_dev: lirc_register_driver: "
+			"minor (%d) just registered!\n", minor);
+		err = -EBUSY;
+		goto out_lock;
+	}
+
+	ir = kzalloc(sizeof(struct irctl), GFP_KERNEL);
+	if (!ir) {
+		err = -ENOMEM;
+		goto out_lock;
+	}
+	lirc_irctl_init(ir);
+	irctls[minor] = ir;
+	d->minor = minor;
+
+	if (d->sample_rate) {
+		ir->jiffies_to_wait = HZ / d->sample_rate;
+	} else {
+		/* it means - wait for external event in task queue */
+		ir->jiffies_to_wait = 0;
+	}
+
+	/* some safety check 8-) */
+	d->name[sizeof(d->name)-1] = '\0';
+
+	bytes_in_key = BITS_TO_LONGS(d->code_length) +
+			(d->code_length % 8 ? 1 : 0);
+	buffer_size = d->buffer_size ? d->buffer_size : BUFLEN / bytes_in_key;
+	chunk_size  = d->chunk_size  ? d->chunk_size  : bytes_in_key;
+
+	if (d->rbuf) {
+		ir->buf = d->rbuf;
+	} else {
+		ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
+		if (!ir->buf) {
+			err = -ENOMEM;
+			goto out_lock;
+		}
+		err = lirc_buffer_init(ir->buf, chunk_size, buffer_size);
+		if (err) {
+			kfree(ir->buf);
+			goto out_lock;
+		}
+	}
+	ir->chunk_size = ir->buf->chunk_size;
+
+	if (d->features == 0)
+		d->features = LIRC_CAN_REC_LIRCCODE;
+
+	ir->d = *d;
+
+	device_create(lirc_class, ir->d.dev,
+		      MKDEV(MAJOR(lirc_base_dev), ir->d.minor), NULL,
+		      "lirc%u", ir->d.minor);
+
+	if (d->sample_rate) {
+		/* try to fire up polling thread */
+		ir->task = kthread_run(lirc_thread, (void *)ir, "lirc_dev");
+		if (IS_ERR(ir->task)) {
+			dev_err(d->dev, "lirc_dev: lirc_register_driver: "
+				"cannot run poll thread for minor = %d\n",
+				d->minor);
+			err = -ECHILD;
+			goto out_sysfs;
+		}
+	}
+
+	err = lirc_cdev_add(ir);
+	if (err)
+		goto out_sysfs;
+
+	ir->attached = 1;
+	mutex_unlock(&lirc_dev_lock);
+
+	dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n",
+		 ir->d.name, ir->d.minor);
+	return minor;
+
+out_sysfs:
+	device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor));
+out_lock:
+	mutex_unlock(&lirc_dev_lock);
+out:
+	return err;
+}
+EXPORT_SYMBOL(lirc_register_driver);
+
+int lirc_unregister_driver(int minor)
+{
+	struct irctl *ir;
+	struct cdev *cdev;
+
+	if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {
+		printk(KERN_ERR "lirc_dev: %s: minor (%d) must be between "
+		       "0 and %d!\n", __func__, minor, MAX_IRCTL_DEVICES - 1);
+		return -EBADRQC;
+	}
+
+	ir = irctls[minor];
+	if (!ir) {
+		printk(KERN_ERR "lirc_dev: %s: failed to get irctl struct "
+		       "for minor %d!\n", __func__, minor);
+		return -ENOENT;
+	}
+
+	cdev = ir->cdev;
+
+	mutex_lock(&lirc_dev_lock);
+
+	if (ir->d.minor != minor) {
+		printk(KERN_ERR "lirc_dev: %s: minor (%d) device not "
+		       "registered!\n", __func__, minor);
+		mutex_unlock(&lirc_dev_lock);
+		return -ENOENT;
+	}
+
+	/* end up polling thread */
+	if (ir->task)
+		kthread_stop(ir->task);
+
+	dev_dbg(ir->d.dev, "lirc_dev: driver %s unregistered from minor = %d\n",
+		ir->d.name, ir->d.minor);
+
+	ir->attached = 0;
+	if (ir->open) {
+		dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n",
+			ir->d.name, ir->d.minor);
+		wake_up_interruptible(&ir->buf->wait_poll);
+		mutex_lock(&ir->irctl_lock);
+		ir->d.set_use_dec(ir->d.data);
+		module_put(cdev->owner);
+		mutex_unlock(&ir->irctl_lock);
+	} else {
+		lirc_irctl_cleanup(ir);
+		cdev_del(cdev);
+		kfree(cdev);
+		kfree(ir);
+		irctls[minor] = NULL;
+	}
+
+	mutex_unlock(&lirc_dev_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(lirc_unregister_driver);
+
+int lirc_dev_fop_open(struct inode *inode, struct file *file)
+{
+	struct irctl *ir;
+	struct cdev *cdev;
+	int retval = 0;
+
+	if (iminor(inode) >= MAX_IRCTL_DEVICES) {
+		printk(KERN_WARNING "lirc_dev [%d]: open result = -ENODEV\n",
+		       iminor(inode));
+		return -ENODEV;
+	}
+
+	if (mutex_lock_interruptible(&lirc_dev_lock))
+		return -ERESTARTSYS;
+
+	ir = irctls[iminor(inode)];
+	if (!ir) {
+		retval = -ENODEV;
+		goto error;
+	}
+
+	dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor);
+
+	if (ir->d.minor == NOPLUG) {
+		retval = -ENODEV;
+		goto error;
+	}
+
+	if (ir->open) {
+		retval = -EBUSY;
+		goto error;
+	}
+
+	if (ir->d.rdev) {
+		retval = rc_open(ir->d.rdev);
+		if (retval)
+			goto error;
+	}
+
+	cdev = ir->cdev;
+	if (try_module_get(cdev->owner)) {
+		ir->open++;
+		retval = ir->d.set_use_inc(ir->d.data);
+
+		if (retval) {
+			module_put(cdev->owner);
+			ir->open--;
+		} else {
+			lirc_buffer_clear(ir->buf);
+		}
+		if (ir->task)
+			wake_up_process(ir->task);
+	}
+
+error:
+	if (ir)
+		dev_dbg(ir->d.dev, LOGHEAD "open result = %d\n",
+			ir->d.name, ir->d.minor, retval);
+
+	mutex_unlock(&lirc_dev_lock);
+
+	nonseekable_open(inode, file);
+
+	return retval;
+}
+EXPORT_SYMBOL(lirc_dev_fop_open);
+
+int lirc_dev_fop_close(struct inode *inode, struct file *file)
+{
+	struct irctl *ir = irctls[iminor(inode)];
+	struct cdev *cdev;
+
+	if (!ir) {
+		printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
+		return -EINVAL;
+	}
+
+	cdev = ir->cdev;
+
+	dev_dbg(ir->d.dev, LOGHEAD "close called\n", ir->d.name, ir->d.minor);
+
+	WARN_ON(mutex_lock_killable(&lirc_dev_lock));
+
+	rc_close(ir->d.rdev);
+
+	ir->open--;
+	if (ir->attached) {
+		ir->d.set_use_dec(ir->d.data);
+		module_put(cdev->owner);
+	} else {
+		lirc_irctl_cleanup(ir);
+		cdev_del(cdev);
+		irctls[ir->d.minor] = NULL;
+		kfree(cdev);
+		kfree(ir);
+	}
+
+	mutex_unlock(&lirc_dev_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(lirc_dev_fop_close);
+
+unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait)
+{
+	struct irctl *ir = irctls[iminor(file_inode(file))];
+	unsigned int ret;
+
+	if (!ir) {
+		printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
+		return POLLERR;
+	}
+
+	dev_dbg(ir->d.dev, LOGHEAD "poll called\n", ir->d.name, ir->d.minor);
+
+	if (!ir->attached)
+		return POLLERR;
+
+	if (ir->buf) {
+		poll_wait(file, &ir->buf->wait_poll, wait);
+
+		if (lirc_buffer_empty(ir->buf))
+			ret = 0;
+		else
+			ret = POLLIN | POLLRDNORM;
+	} else
+		ret = POLLERR;
+
+	dev_dbg(ir->d.dev, LOGHEAD "poll result = %d\n",
+		ir->d.name, ir->d.minor, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(lirc_dev_fop_poll);
+
+long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	__u32 mode;
+	int result = 0;
+	struct irctl *ir = irctls[iminor(file_inode(file))];
+
+	if (!ir) {
+		printk(KERN_ERR "lirc_dev: %s: no irctl found!\n", __func__);
+		return -ENODEV;
+	}
+
+	dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n",
+		ir->d.name, ir->d.minor, cmd);
+
+	if (ir->d.minor == NOPLUG || !ir->attached) {
+		dev_dbg(ir->d.dev, LOGHEAD "ioctl result = -ENODEV\n",
+			ir->d.name, ir->d.minor);
+		return -ENODEV;
+	}
+
+	mutex_lock(&ir->irctl_lock);
+
+	switch (cmd) {
+	case LIRC_GET_FEATURES:
+		result = put_user(ir->d.features, (__u32 __user *)arg);
+		break;
+	case LIRC_GET_REC_MODE:
+		if (!(ir->d.features & LIRC_CAN_REC_MASK)) {
+			result = -ENOSYS;
+			break;
+		}
+
+		result = put_user(LIRC_REC2MODE
+				  (ir->d.features & LIRC_CAN_REC_MASK),
+				  (__u32 __user *)arg);
+		break;
+	case LIRC_SET_REC_MODE:
+		if (!(ir->d.features & LIRC_CAN_REC_MASK)) {
+			result = -ENOSYS;
+			break;
+		}
+
+		result = get_user(mode, (__u32 __user *)arg);
+		if (!result && !(LIRC_MODE2REC(mode) & ir->d.features))
+			result = -EINVAL;
+		/*
+		 * FIXME: We should actually set the mode somehow but
+		 * for now, lirc_serial doesn't support mode changing either
+		 */
+		break;
+	case LIRC_GET_LENGTH:
+		result = put_user(ir->d.code_length, (__u32 __user *)arg);
+		break;
+	case LIRC_GET_MIN_TIMEOUT:
+		if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||
+		    ir->d.min_timeout == 0) {
+			result = -ENOSYS;
+			break;
+		}
+
+		result = put_user(ir->d.min_timeout, (__u32 __user *)arg);
+		break;
+	case LIRC_GET_MAX_TIMEOUT:
+		if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||
+		    ir->d.max_timeout == 0) {
+			result = -ENOSYS;
+			break;
+		}
+
+		result = put_user(ir->d.max_timeout, (__u32 __user *)arg);
+		break;
+	default:
+		result = -EINVAL;
+	}
+
+	dev_dbg(ir->d.dev, LOGHEAD "ioctl result = %d\n",
+		ir->d.name, ir->d.minor, result);
+
+	mutex_unlock(&ir->irctl_lock);
+
+	return result;
+}
+EXPORT_SYMBOL(lirc_dev_fop_ioctl);
+
+ssize_t lirc_dev_fop_read(struct file *file,
+			  char __user *buffer,
+			  size_t length,
+			  loff_t *ppos)
+{
+	struct irctl *ir = irctls[iminor(file_inode(file))];
+	unsigned char *buf;
+	int ret = 0, written = 0;
+	DECLARE_WAITQUEUE(wait, current);
+
+	if (!ir) {
+		printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
+		return -ENODEV;
+	}
+
+	dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor);
+
+	buf = kzalloc(ir->chunk_size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	if (mutex_lock_interruptible(&ir->irctl_lock)) {
+		ret = -ERESTARTSYS;
+		goto out_unlocked;
+	}
+	if (!ir->attached) {
+		ret = -ENODEV;
+		goto out_locked;
+	}
+
+	if (length % ir->chunk_size) {
+		ret = -EINVAL;
+		goto out_locked;
+	}
+
+	/*
+	 * we add ourselves to the task queue before buffer check
+	 * to avoid losing scan code (in case when queue is awaken somewhere
+	 * between while condition checking and scheduling)
+	 */
+	add_wait_queue(&ir->buf->wait_poll, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+
+	/*
+	 * while we didn't provide 'length' bytes, device is opened in blocking
+	 * mode and 'copy_to_user' is happy, wait for data.
+	 */
+	while (written < length && ret == 0) {
+		if (lirc_buffer_empty(ir->buf)) {
+			/* According to the read(2) man page, 'written' can be
+			 * returned as less than 'length', instead of blocking
+			 * again, returning -EWOULDBLOCK, or returning
+			 * -ERESTARTSYS */
+			if (written)
+				break;
+			if (file->f_flags & O_NONBLOCK) {
+				ret = -EWOULDBLOCK;
+				break;
+			}
+			if (signal_pending(current)) {
+				ret = -ERESTARTSYS;
+				break;
+			}
+
+			mutex_unlock(&ir->irctl_lock);
+			schedule();
+			set_current_state(TASK_INTERRUPTIBLE);
+
+			if (mutex_lock_interruptible(&ir->irctl_lock)) {
+				ret = -ERESTARTSYS;
+				remove_wait_queue(&ir->buf->wait_poll, &wait);
+				set_current_state(TASK_RUNNING);
+				goto out_unlocked;
+			}
+
+			if (!ir->attached) {
+				ret = -ENODEV;
+				break;
+			}
+		} else {
+			lirc_buffer_read(ir->buf, buf);
+			ret = copy_to_user((void __user *)buffer+written, buf,
+					   ir->buf->chunk_size);
+			if (!ret)
+				written += ir->buf->chunk_size;
+			else
+				ret = -EFAULT;
+		}
+	}
+
+	remove_wait_queue(&ir->buf->wait_poll, &wait);
+	set_current_state(TASK_RUNNING);
+
+out_locked:
+	mutex_unlock(&ir->irctl_lock);
+
+out_unlocked:
+	kfree(buf);
+	dev_dbg(ir->d.dev, LOGHEAD "read result = %s (%d)\n",
+		ir->d.name, ir->d.minor, ret ? "<fail>" : "<ok>", ret);
+
+	return ret ? ret : written;
+}
+EXPORT_SYMBOL(lirc_dev_fop_read);
+
+void *lirc_get_pdata(struct file *file)
+{
+	return irctls[iminor(file_inode(file))]->d.data;
+}
+EXPORT_SYMBOL(lirc_get_pdata);
+
+
+ssize_t lirc_dev_fop_write(struct file *file, const char __user *buffer,
+			   size_t length, loff_t *ppos)
+{
+	struct irctl *ir = irctls[iminor(file_inode(file))];
+
+	if (!ir) {
+		printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
+		return -ENODEV;
+	}
+
+	dev_dbg(ir->d.dev, LOGHEAD "write called\n", ir->d.name, ir->d.minor);
+
+	if (!ir->attached)
+		return -ENODEV;
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(lirc_dev_fop_write);
+
+
+static int __init lirc_dev_init(void)
+{
+	int retval;
+
+	lirc_class = class_create(THIS_MODULE, "lirc");
+	if (IS_ERR(lirc_class)) {
+		retval = PTR_ERR(lirc_class);
+		printk(KERN_ERR "lirc_dev: class_create failed\n");
+		goto error;
+	}
+
+	retval = alloc_chrdev_region(&lirc_base_dev, 0, MAX_IRCTL_DEVICES,
+				     IRCTL_DEV_NAME);
+	if (retval) {
+		class_destroy(lirc_class);
+		printk(KERN_ERR "lirc_dev: alloc_chrdev_region failed\n");
+		goto error;
+	}
+
+
+	printk(KERN_INFO "lirc_dev: IR Remote Control driver registered, "
+	       "major %d \n", MAJOR(lirc_base_dev));
+
+error:
+	return retval;
+}
+
+
+
+static void __exit lirc_dev_exit(void)
+{
+	class_destroy(lirc_class);
+	unregister_chrdev_region(lirc_base_dev, MAX_IRCTL_DEVICES);
+	printk(KERN_INFO "lirc_dev: module unloaded\n");
+}
+
+module_init(lirc_dev_init);
+module_exit(lirc_dev_exit);
+
+MODULE_DESCRIPTION("LIRC base driver module");
+MODULE_AUTHOR("Artur Lipowski");
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging messages");
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
new file mode 100644
index 0000000..f838d9c
--- /dev/null
+++ b/drivers/media/rc/mceusb.c
@@ -0,0 +1,1479 @@
+/*
+ * Driver for USB Windows Media Center Ed. eHome Infrared Transceivers
+ *
+ * Copyright (c) 2010-2011, Jarod Wilson <jarod@redhat.com>
+ *
+ * Based on the original lirc_mceusb and lirc_mceusb2 drivers, by Dan
+ * Conti, Martin Blatter and Daniel Melander, the latter of which was
+ * in turn also based on the lirc_atiusb driver by Paul Miller. The
+ * two mce drivers were merged into one by Jarod Wilson, with transmit
+ * support for the 1st-gen device added primarily by Patrick Calhoun,
+ * with a bit of tweaks by Jarod. Debugging improvements and proper
+ * support for what appears to be 3rd-gen hardware added by Jarod.
+ * Initial port from lirc driver to ir-core drivery by Jarod, based
+ * partially on a port to an earlier proposed IR infrastructure by
+ * Jon Smirl, which included enhancements and simplifications to the
+ * incoming IR buffer parsing routines.
+ *
+ * Updated in July of 2011 with the aid of Microsoft's official
+ * remote/transceiver requirements and specification document, found at
+ * download.microsoft.com, title
+ * Windows-Media-Center-RC-IR-Collection-Green-Button-Specification-03-08-2011-V2.pdf
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <linux/pm_wakeup.h>
+#include <media/rc-core.h>
+
+#define DRIVER_VERSION	"1.92"
+#define DRIVER_AUTHOR	"Jarod Wilson <jarod@redhat.com>"
+#define DRIVER_DESC	"Windows Media Center Ed. eHome Infrared Transceiver " \
+			"device driver"
+#define DRIVER_NAME	"mceusb"
+
+#define USB_BUFLEN		32 /* USB reception buffer length */
+#define USB_CTRL_MSG_SZ		2  /* Size of usb ctrl msg on gen1 hw */
+#define MCE_G1_INIT_MSGS	40 /* Init messages on gen1 hw to throw out */
+
+/* MCE constants */
+#define MCE_CMDBUF_SIZE		384  /* MCE Command buffer length */
+#define MCE_TIME_UNIT		50   /* Approx 50us resolution */
+#define MCE_CODE_LENGTH		5    /* Normal length of packet (with header) */
+#define MCE_PACKET_SIZE		4    /* Normal length of packet (without header) */
+#define MCE_IRDATA_HEADER	0x84 /* Actual header format is 0x80 + num_bytes */
+#define MCE_IRDATA_TRAILER	0x80 /* End of IR data */
+#define MCE_MAX_CHANNELS	2    /* Two transmitters, hardware dependent? */
+#define MCE_DEFAULT_TX_MASK	0x03 /* Vals: TX1=0x01, TX2=0x02, ALL=0x03 */
+#define MCE_PULSE_BIT		0x80 /* Pulse bit, MSB set == PULSE else SPACE */
+#define MCE_PULSE_MASK		0x7f /* Pulse mask */
+#define MCE_MAX_PULSE_LENGTH	0x7f /* Longest transmittable pulse symbol */
+
+/*
+ * The interface between the host and the IR hardware is command-response
+ * based. All commands and responses have a consistent format, where a lead
+ * byte always identifies the type of data following it. The lead byte has
+ * a port value in the 3 highest bits and a length value in the 5 lowest
+ * bits.
+ *
+ * The length field is overloaded, with a value of 11111 indicating that the
+ * following byte is a command or response code, and the length of the entire
+ * message is determined by the code. If the length field is not 11111, then
+ * it specifies the number of bytes of port data that follow.
+ */
+#define MCE_CMD			0x1f
+#define MCE_PORT_IR		0x4	/* (0x4 << 5) | MCE_CMD = 0x9f */
+#define MCE_PORT_SYS		0x7	/* (0x7 << 5) | MCE_CMD = 0xff */
+#define MCE_PORT_SER		0x6	/* 0xc0 thru 0xdf flush & 0x1f bytes */
+#define MCE_PORT_MASK		0xe0	/* Mask out command bits */
+
+/* Command port headers */
+#define MCE_CMD_PORT_IR		0x9f	/* IR-related cmd/rsp */
+#define MCE_CMD_PORT_SYS	0xff	/* System (non-IR) device cmd/rsp */
+
+/* Commands that set device state  (2-4 bytes in length) */
+#define MCE_CMD_RESET		0xfe	/* Reset device, 2 bytes */
+#define MCE_CMD_RESUME		0xaa	/* Resume device after error, 2 bytes */
+#define MCE_CMD_SETIRCFS	0x06	/* Set tx carrier, 4 bytes */
+#define MCE_CMD_SETIRTIMEOUT	0x0c	/* Set timeout, 4 bytes */
+#define MCE_CMD_SETIRTXPORTS	0x08	/* Set tx ports, 3 bytes */
+#define MCE_CMD_SETIRRXPORTEN	0x14	/* Set rx ports, 3 bytes */
+#define MCE_CMD_FLASHLED	0x23	/* Flash receiver LED, 2 bytes */
+
+/* Commands that query device state (all 2 bytes, unless noted) */
+#define MCE_CMD_GETIRCFS	0x07	/* Get carrier */
+#define MCE_CMD_GETIRTIMEOUT	0x0d	/* Get timeout */
+#define MCE_CMD_GETIRTXPORTS	0x13	/* Get tx ports */
+#define MCE_CMD_GETIRRXPORTEN	0x15	/* Get rx ports */
+#define MCE_CMD_GETPORTSTATUS	0x11	/* Get tx port status, 3 bytes */
+#define MCE_CMD_GETIRNUMPORTS	0x16	/* Get number of ports */
+#define MCE_CMD_GETWAKESOURCE	0x17	/* Get wake source */
+#define MCE_CMD_GETEMVER	0x22	/* Get emulator interface version */
+#define MCE_CMD_GETDEVDETAILS	0x21	/* Get device details (em ver2 only) */
+#define MCE_CMD_GETWAKESUPPORT	0x20	/* Get wake details (em ver2 only) */
+#define MCE_CMD_GETWAKEVERSION	0x18	/* Get wake pattern (em ver2 only) */
+
+/* Misc commands */
+#define MCE_CMD_NOP		0xff	/* No operation */
+
+/* Responses to commands (non-error cases) */
+#define MCE_RSP_EQIRCFS		0x06	/* tx carrier, 4 bytes */
+#define MCE_RSP_EQIRTIMEOUT	0x0c	/* rx timeout, 4 bytes */
+#define MCE_RSP_GETWAKESOURCE	0x17	/* wake source, 3 bytes */
+#define MCE_RSP_EQIRTXPORTS	0x08	/* tx port mask, 3 bytes */
+#define MCE_RSP_EQIRRXPORTEN	0x14	/* rx port mask, 3 bytes */
+#define MCE_RSP_GETPORTSTATUS	0x11	/* tx port status, 7 bytes */
+#define MCE_RSP_EQIRRXCFCNT	0x15	/* rx carrier count, 4 bytes */
+#define MCE_RSP_EQIRNUMPORTS	0x16	/* number of ports, 4 bytes */
+#define MCE_RSP_EQWAKESUPPORT	0x20	/* wake capabilities, 3 bytes */
+#define MCE_RSP_EQWAKEVERSION	0x18	/* wake pattern details, 6 bytes */
+#define MCE_RSP_EQDEVDETAILS	0x21	/* device capabilities, 3 bytes */
+#define MCE_RSP_EQEMVER		0x22	/* emulator interface ver, 3 bytes */
+#define MCE_RSP_FLASHLED	0x23	/* success flashing LED, 2 bytes */
+
+/* Responses to error cases, must send MCE_CMD_RESUME to clear them */
+#define MCE_RSP_CMD_ILLEGAL	0xfe	/* illegal command for port, 2 bytes */
+#define MCE_RSP_TX_TIMEOUT	0x81	/* tx timed out, 2 bytes */
+
+/* Misc commands/responses not defined in the MCE remote/transceiver spec */
+#define MCE_CMD_SIG_END		0x01	/* End of signal */
+#define MCE_CMD_PING		0x03	/* Ping device */
+#define MCE_CMD_UNKNOWN		0x04	/* Unknown */
+#define MCE_CMD_UNKNOWN2	0x05	/* Unknown */
+#define MCE_CMD_UNKNOWN3	0x09	/* Unknown */
+#define MCE_CMD_UNKNOWN4	0x0a	/* Unknown */
+#define MCE_CMD_G_REVISION	0x0b	/* Get hw/sw revision */
+#define MCE_CMD_UNKNOWN5	0x0e	/* Unknown */
+#define MCE_CMD_UNKNOWN6	0x0f	/* Unknown */
+#define MCE_CMD_UNKNOWN8	0x19	/* Unknown */
+#define MCE_CMD_UNKNOWN9	0x1b	/* Unknown */
+#define MCE_CMD_NULL		0x00	/* These show up various places... */
+
+/* if buf[i] & MCE_PORT_MASK == 0x80 and buf[i] != MCE_CMD_PORT_IR,
+ * then we're looking at a raw IR data sample */
+#define MCE_COMMAND_IRDATA	0x80
+#define MCE_PACKET_LENGTH_MASK	0x1f /* Packet length mask */
+
+/* general constants */
+#define SEND_FLAG_IN_PROGRESS	1
+#define SEND_FLAG_COMPLETE	2
+#define RECV_FLAG_IN_PROGRESS	3
+#define RECV_FLAG_COMPLETE	4
+
+#define MCEUSB_RX		1
+#define MCEUSB_TX		2
+
+#define VENDOR_PHILIPS		0x0471
+#define VENDOR_SMK		0x0609
+#define VENDOR_TATUNG		0x1460
+#define VENDOR_GATEWAY		0x107b
+#define VENDOR_SHUTTLE		0x1308
+#define VENDOR_SHUTTLE2		0x051c
+#define VENDOR_MITSUMI		0x03ee
+#define VENDOR_TOPSEED		0x1784
+#define VENDOR_RICAVISION	0x179d
+#define VENDOR_ITRON		0x195d
+#define VENDOR_FIC		0x1509
+#define VENDOR_LG		0x043e
+#define VENDOR_MICROSOFT	0x045e
+#define VENDOR_FORMOSA		0x147a
+#define VENDOR_FINTEK		0x1934
+#define VENDOR_PINNACLE		0x2304
+#define VENDOR_ECS		0x1019
+#define VENDOR_WISTRON		0x0fb8
+#define VENDOR_COMPRO		0x185b
+#define VENDOR_NORTHSTAR	0x04eb
+#define VENDOR_REALTEK		0x0bda
+#define VENDOR_TIVO		0x105a
+#define VENDOR_CONEXANT		0x0572
+#define VENDOR_TWISTEDMELON	0x2596
+#define VENDOR_HAUPPAUGE	0x2040
+#define VENDOR_PCTV		0x2013
+
+enum mceusb_model_type {
+	MCE_GEN2 = 0,		/* Most boards */
+	MCE_GEN1,
+	MCE_GEN3,
+	MCE_GEN2_TX_INV,
+	POLARIS_EVK,
+	CX_HYBRID_TV,
+	MULTIFUNCTION,
+	TIVO_KIT,
+	MCE_GEN2_NO_TX,
+	HAUPPAUGE_CX_HYBRID_TV,
+};
+
+struct mceusb_model {
+	u32 mce_gen1:1;
+	u32 mce_gen2:1;
+	u32 mce_gen3:1;
+	u32 tx_mask_normal:1;
+	u32 no_tx:1;
+
+	int ir_intfnum;
+
+	const char *rc_map;	/* Allow specify a per-board map */
+	const char *name;	/* per-board name */
+};
+
+static const struct mceusb_model mceusb_model[] = {
+	[MCE_GEN1] = {
+		.mce_gen1 = 1,
+		.tx_mask_normal = 1,
+	},
+	[MCE_GEN2] = {
+		.mce_gen2 = 1,
+	},
+	[MCE_GEN2_NO_TX] = {
+		.mce_gen2 = 1,
+		.no_tx = 1,
+	},
+	[MCE_GEN2_TX_INV] = {
+		.mce_gen2 = 1,
+		.tx_mask_normal = 1,
+	},
+	[MCE_GEN3] = {
+		.mce_gen3 = 1,
+		.tx_mask_normal = 1,
+	},
+	[POLARIS_EVK] = {
+		/*
+		 * In fact, the EVK is shipped without
+		 * remotes, but we should have something handy,
+		 * to allow testing it
+		 */
+		.name = "Conexant Hybrid TV (cx231xx) MCE IR",
+	},
+	[CX_HYBRID_TV] = {
+		.no_tx = 1, /* tx isn't wired up at all */
+		.name = "Conexant Hybrid TV (cx231xx) MCE IR",
+	},
+	[HAUPPAUGE_CX_HYBRID_TV] = {
+		.no_tx = 1, /* eeprom says it has no tx */
+		.name = "Conexant Hybrid TV (cx231xx) MCE IR no TX",
+	},
+	[MULTIFUNCTION] = {
+		.mce_gen2 = 1,
+		.ir_intfnum = 2,
+	},
+	[TIVO_KIT] = {
+		.mce_gen2 = 1,
+		.rc_map = RC_MAP_TIVO,
+	},
+};
+
+static struct usb_device_id mceusb_dev_table[] = {
+	/* Original Microsoft MCE IR Transceiver (often HP-branded) */
+	{ USB_DEVICE(VENDOR_MICROSOFT, 0x006d),
+	  .driver_info = MCE_GEN1 },
+	/* Philips Infrared Transceiver - Sahara branded */
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x0608) },
+	/* Philips Infrared Transceiver - HP branded */
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x060c),
+	  .driver_info = MCE_GEN2_TX_INV },
+	/* Philips SRM5100 */
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x060d) },
+	/* Philips Infrared Transceiver - Omaura */
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x060f) },
+	/* Philips Infrared Transceiver - Spinel plus */
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x0613) },
+	/* Philips eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x0815) },
+	/* Philips/Spinel plus IR transceiver for ASUS */
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x206c) },
+	/* Philips/Spinel plus IR transceiver for ASUS */
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x2088) },
+	/* Philips IR transceiver (Dell branded) */
+	{ USB_DEVICE(VENDOR_PHILIPS, 0x2093),
+	  .driver_info = MCE_GEN2_TX_INV },
+	/* Realtek MCE IR Receiver and card reader */
+	{ USB_DEVICE(VENDOR_REALTEK, 0x0161),
+	  .driver_info = MULTIFUNCTION },
+	/* SMK/Toshiba G83C0004D410 */
+	{ USB_DEVICE(VENDOR_SMK, 0x031d),
+	  .driver_info = MCE_GEN2_TX_INV },
+	/* SMK eHome Infrared Transceiver (Sony VAIO) */
+	{ USB_DEVICE(VENDOR_SMK, 0x0322),
+	  .driver_info = MCE_GEN2_TX_INV },
+	/* bundled with Hauppauge PVR-150 */
+	{ USB_DEVICE(VENDOR_SMK, 0x0334),
+	  .driver_info = MCE_GEN2_TX_INV },
+	/* SMK eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_SMK, 0x0338) },
+	/* SMK/I-O Data GV-MC7/RCKIT Receiver */
+	{ USB_DEVICE(VENDOR_SMK, 0x0353),
+	  .driver_info = MCE_GEN2_NO_TX },
+	/* Tatung eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_TATUNG, 0x9150) },
+	/* Shuttle eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_SHUTTLE, 0xc001) },
+	/* Shuttle eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_SHUTTLE2, 0xc001) },
+	/* Gateway eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_GATEWAY, 0x3009) },
+	/* Mitsumi */
+	{ USB_DEVICE(VENDOR_MITSUMI, 0x2501) },
+	/* Topseed eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0001),
+	  .driver_info = MCE_GEN2_TX_INV },
+	/* Topseed HP eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0006),
+	  .driver_info = MCE_GEN2_TX_INV },
+	/* Topseed eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0007),
+	  .driver_info = MCE_GEN2_TX_INV },
+	/* Topseed eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0008),
+	  .driver_info = MCE_GEN3 },
+	/* Topseed eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x000a),
+	  .driver_info = MCE_GEN2_TX_INV },
+	/* Topseed eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_TOPSEED, 0x0011),
+	  .driver_info = MCE_GEN3 },
+	/* Ricavision internal Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_RICAVISION, 0x0010) },
+	/* Itron ione Libra Q-11 */
+	{ USB_DEVICE(VENDOR_ITRON, 0x7002) },
+	/* FIC eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_FIC, 0x9242) },
+	/* LG eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_LG, 0x9803) },
+	/* Microsoft MCE Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_MICROSOFT, 0x00a0) },
+	/* Formosa eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_FORMOSA, 0xe015) },
+	/* Formosa21 / eHome Infrared Receiver */
+	{ USB_DEVICE(VENDOR_FORMOSA, 0xe016) },
+	/* Formosa aim / Trust MCE Infrared Receiver */
+	{ USB_DEVICE(VENDOR_FORMOSA, 0xe017),
+	  .driver_info = MCE_GEN2_NO_TX },
+	/* Formosa Industrial Computing / Beanbag Emulation Device */
+	{ USB_DEVICE(VENDOR_FORMOSA, 0xe018) },
+	/* Formosa21 / eHome Infrared Receiver */
+	{ USB_DEVICE(VENDOR_FORMOSA, 0xe03a) },
+	/* Formosa Industrial Computing AIM IR605/A */
+	{ USB_DEVICE(VENDOR_FORMOSA, 0xe03c) },
+	/* Formosa Industrial Computing */
+	{ USB_DEVICE(VENDOR_FORMOSA, 0xe03e) },
+	/* Formosa Industrial Computing */
+	{ USB_DEVICE(VENDOR_FORMOSA, 0xe042) },
+	/* Fintek eHome Infrared Transceiver (HP branded) */
+	{ USB_DEVICE(VENDOR_FINTEK, 0x5168),
+	  .driver_info = MCE_GEN2_TX_INV },
+	/* Fintek eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_FINTEK, 0x0602) },
+	/* Fintek eHome Infrared Transceiver (in the AOpen MP45) */
+	{ USB_DEVICE(VENDOR_FINTEK, 0x0702) },
+	/* Pinnacle Remote Kit */
+	{ USB_DEVICE(VENDOR_PINNACLE, 0x0225),
+	  .driver_info = MCE_GEN3 },
+	/* Elitegroup Computer Systems IR */
+	{ USB_DEVICE(VENDOR_ECS, 0x0f38) },
+	/* Wistron Corp. eHome Infrared Receiver */
+	{ USB_DEVICE(VENDOR_WISTRON, 0x0002) },
+	/* Compro K100 */
+	{ USB_DEVICE(VENDOR_COMPRO, 0x3020) },
+	/* Compro K100 v2 */
+	{ USB_DEVICE(VENDOR_COMPRO, 0x3082) },
+	/* Northstar Systems, Inc. eHome Infrared Transceiver */
+	{ USB_DEVICE(VENDOR_NORTHSTAR, 0xe004) },
+	/* TiVo PC IR Receiver */
+	{ USB_DEVICE(VENDOR_TIVO, 0x2000),
+	  .driver_info = TIVO_KIT },
+	/* Conexant Hybrid TV "Shelby" Polaris SDK */
+	{ USB_DEVICE(VENDOR_CONEXANT, 0x58a1),
+	  .driver_info = POLARIS_EVK },
+	/* Conexant Hybrid TV RDU253S Polaris */
+	{ USB_DEVICE(VENDOR_CONEXANT, 0x58a5),
+	  .driver_info = CX_HYBRID_TV },
+	/* Twisted Melon Inc. - Manta Mini Receiver */
+	{ USB_DEVICE(VENDOR_TWISTEDMELON, 0x8008) },
+	/* Twisted Melon Inc. - Manta Pico Receiver */
+	{ USB_DEVICE(VENDOR_TWISTEDMELON, 0x8016) },
+	/* Twisted Melon Inc. - Manta Transceiver */
+	{ USB_DEVICE(VENDOR_TWISTEDMELON, 0x8042) },
+	/* Hauppauge WINTV-HVR-HVR 930C-HD - based on cx231xx */
+	{ USB_DEVICE(VENDOR_HAUPPAUGE, 0xb130),
+	  .driver_info = HAUPPAUGE_CX_HYBRID_TV },
+	{ USB_DEVICE(VENDOR_HAUPPAUGE, 0xb131),
+	  .driver_info = HAUPPAUGE_CX_HYBRID_TV },
+	{ USB_DEVICE(VENDOR_HAUPPAUGE, 0xb138),
+	  .driver_info = HAUPPAUGE_CX_HYBRID_TV },
+	{ USB_DEVICE(VENDOR_HAUPPAUGE, 0xb139),
+	  .driver_info = HAUPPAUGE_CX_HYBRID_TV },
+	{ USB_DEVICE(VENDOR_PCTV, 0x0259),
+	  .driver_info = HAUPPAUGE_CX_HYBRID_TV },
+	{ USB_DEVICE(VENDOR_PCTV, 0x025e),
+	  .driver_info = HAUPPAUGE_CX_HYBRID_TV },
+
+	/* Terminating entry */
+	{ }
+};
+
+/* data structure for each usb transceiver */
+struct mceusb_dev {
+	/* ir-core bits */
+	struct rc_dev *rc;
+
+	/* optional features we can enable */
+	bool carrier_report_enabled;
+	bool learning_enabled;
+
+	/* core device bits */
+	struct device *dev;
+
+	/* usb */
+	struct usb_device *usbdev;
+	struct urb *urb_in;
+	struct usb_endpoint_descriptor *usb_ep_out;
+
+	/* buffers and dma */
+	unsigned char *buf_in;
+	unsigned int len_in;
+	dma_addr_t dma_in;
+
+	enum {
+		CMD_HEADER = 0,
+		SUBCMD,
+		CMD_DATA,
+		PARSE_IRDATA,
+	} parser_state;
+
+	u8 cmd, rem;		/* Remaining IR data bytes in packet */
+
+	struct {
+		u32 connected:1;
+		u32 tx_mask_normal:1;
+		u32 microsoft_gen1:1;
+		u32 no_tx:1;
+	} flags;
+
+	/* transmit support */
+	int send_flags;
+	u32 carrier;
+	unsigned char tx_mask;
+
+	char name[128];
+	char phys[64];
+	enum mceusb_model_type model;
+
+	bool need_reset;	/* flag to issue a device resume cmd */
+	u8 emver;		/* emulator interface version */
+	u8 num_txports;		/* number of transmit ports */
+	u8 num_rxports;		/* number of receive sensors */
+	u8 txports_cabled;	/* bitmask of transmitters with cable */
+	u8 rxports_active;	/* bitmask of active receive sensors */
+};
+
+/* MCE Device Command Strings, generally a port and command pair */
+static char DEVICE_RESUME[]	= {MCE_CMD_NULL, MCE_CMD_PORT_SYS,
+				   MCE_CMD_RESUME};
+static char GET_REVISION[]	= {MCE_CMD_PORT_SYS, MCE_CMD_G_REVISION};
+static char GET_EMVER[]		= {MCE_CMD_PORT_SYS, MCE_CMD_GETEMVER};
+static char GET_WAKEVERSION[]	= {MCE_CMD_PORT_SYS, MCE_CMD_GETWAKEVERSION};
+static char FLASH_LED[]		= {MCE_CMD_PORT_SYS, MCE_CMD_FLASHLED};
+static char GET_UNKNOWN2[]	= {MCE_CMD_PORT_IR, MCE_CMD_UNKNOWN2};
+static char GET_CARRIER_FREQ[]	= {MCE_CMD_PORT_IR, MCE_CMD_GETIRCFS};
+static char GET_RX_TIMEOUT[]	= {MCE_CMD_PORT_IR, MCE_CMD_GETIRTIMEOUT};
+static char GET_NUM_PORTS[]	= {MCE_CMD_PORT_IR, MCE_CMD_GETIRNUMPORTS};
+static char GET_TX_BITMASK[]	= {MCE_CMD_PORT_IR, MCE_CMD_GETIRTXPORTS};
+static char GET_RX_SENSOR[]	= {MCE_CMD_PORT_IR, MCE_CMD_GETIRRXPORTEN};
+/* sub in desired values in lower byte or bytes for full command */
+/* FIXME: make use of these for transmit.
+static char SET_CARRIER_FREQ[]	= {MCE_CMD_PORT_IR,
+				   MCE_CMD_SETIRCFS, 0x00, 0x00};
+static char SET_TX_BITMASK[]	= {MCE_CMD_PORT_IR, MCE_CMD_SETIRTXPORTS, 0x00};
+static char SET_RX_TIMEOUT[]	= {MCE_CMD_PORT_IR,
+				   MCE_CMD_SETIRTIMEOUT, 0x00, 0x00};
+static char SET_RX_SENSOR[]	= {MCE_CMD_PORT_IR,
+				   MCE_RSP_EQIRRXPORTEN, 0x00};
+*/
+
+static int mceusb_cmd_datasize(u8 cmd, u8 subcmd)
+{
+	int datasize = 0;
+
+	switch (cmd) {
+	case MCE_CMD_NULL:
+		if (subcmd == MCE_CMD_PORT_SYS)
+			datasize = 1;
+		break;
+	case MCE_CMD_PORT_SYS:
+		switch (subcmd) {
+		case MCE_RSP_GETPORTSTATUS:
+			datasize = 5;
+			break;
+		case MCE_RSP_EQWAKEVERSION:
+			datasize = 4;
+			break;
+		case MCE_CMD_G_REVISION:
+			datasize = 2;
+			break;
+		case MCE_RSP_EQWAKESUPPORT:
+		case MCE_RSP_GETWAKESOURCE:
+		case MCE_RSP_EQDEVDETAILS:
+		case MCE_RSP_EQEMVER:
+			datasize = 1;
+			break;
+		}
+	case MCE_CMD_PORT_IR:
+		switch (subcmd) {
+		case MCE_CMD_UNKNOWN:
+		case MCE_RSP_EQIRCFS:
+		case MCE_RSP_EQIRTIMEOUT:
+		case MCE_RSP_EQIRRXCFCNT:
+		case MCE_RSP_EQIRNUMPORTS:
+			datasize = 2;
+			break;
+		case MCE_CMD_SIG_END:
+		case MCE_RSP_EQIRTXPORTS:
+		case MCE_RSP_EQIRRXPORTEN:
+			datasize = 1;
+			break;
+		}
+	}
+	return datasize;
+}
+
+static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
+				 int offset, int len, bool out)
+{
+#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG)
+	char *inout;
+	u8 cmd, subcmd, data1, data2, data3, data4;
+	struct device *dev = ir->dev;
+	int start, skip = 0;
+	u32 carrier, period;
+
+	/* skip meaningless 0xb1 0x60 header bytes on orig receiver */
+	if (ir->flags.microsoft_gen1 && !out && !offset)
+		skip = 2;
+
+	if (len <= skip)
+		return;
+
+	dev_dbg(dev, "%cx data: %*ph (length=%d)",
+		(out ? 't' : 'r'), min(len, USB_BUFLEN), buf, len);
+
+	inout = out ? "Request" : "Got";
+
+	start  = offset + skip;
+	cmd    = buf[start] & 0xff;
+	subcmd = buf[start + 1] & 0xff;
+	data1  = buf[start + 2] & 0xff;
+	data2  = buf[start + 3] & 0xff;
+	data3  = buf[start + 4] & 0xff;
+	data4  = buf[start + 5] & 0xff;
+
+	switch (cmd) {
+	case MCE_CMD_NULL:
+		if (subcmd == MCE_CMD_NULL)
+			break;
+		if ((subcmd == MCE_CMD_PORT_SYS) &&
+		    (data1 == MCE_CMD_RESUME))
+			dev_dbg(dev, "Device resume requested");
+		else
+			dev_dbg(dev, "Unknown command 0x%02x 0x%02x",
+				 cmd, subcmd);
+		break;
+	case MCE_CMD_PORT_SYS:
+		switch (subcmd) {
+		case MCE_RSP_EQEMVER:
+			if (!out)
+				dev_dbg(dev, "Emulator interface version %x",
+					 data1);
+			break;
+		case MCE_CMD_G_REVISION:
+			if (len == 2)
+				dev_dbg(dev, "Get hw/sw rev?");
+			else
+				dev_dbg(dev, "hw/sw rev 0x%02x 0x%02x 0x%02x 0x%02x",
+					 data1, data2,
+					 buf[start + 4], buf[start + 5]);
+			break;
+		case MCE_CMD_RESUME:
+			dev_dbg(dev, "Device resume requested");
+			break;
+		case MCE_RSP_CMD_ILLEGAL:
+			dev_dbg(dev, "Illegal PORT_SYS command");
+			break;
+		case MCE_RSP_EQWAKEVERSION:
+			if (!out)
+				dev_dbg(dev, "Wake version, proto: 0x%02x, "
+					 "payload: 0x%02x, address: 0x%02x, "
+					 "version: 0x%02x",
+					 data1, data2, data3, data4);
+			break;
+		case MCE_RSP_GETPORTSTATUS:
+			if (!out)
+				/* We use data1 + 1 here, to match hw labels */
+				dev_dbg(dev, "TX port %d: blaster is%s connected",
+					 data1 + 1, data4 ? " not" : "");
+			break;
+		case MCE_CMD_FLASHLED:
+			dev_dbg(dev, "Attempting to flash LED");
+			break;
+		default:
+			dev_dbg(dev, "Unknown command 0x%02x 0x%02x",
+				 cmd, subcmd);
+			break;
+		}
+		break;
+	case MCE_CMD_PORT_IR:
+		switch (subcmd) {
+		case MCE_CMD_SIG_END:
+			dev_dbg(dev, "End of signal");
+			break;
+		case MCE_CMD_PING:
+			dev_dbg(dev, "Ping");
+			break;
+		case MCE_CMD_UNKNOWN:
+			dev_dbg(dev, "Resp to 9f 05 of 0x%02x 0x%02x",
+				 data1, data2);
+			break;
+		case MCE_RSP_EQIRCFS:
+			period = DIV_ROUND_CLOSEST(
+					(1U << data1 * 2) * (data2 + 1), 10);
+			if (!period)
+				break;
+			carrier = (1000 * 1000) / period;
+			dev_dbg(dev, "%s carrier of %u Hz (period %uus)",
+				 inout, carrier, period);
+			break;
+		case MCE_CMD_GETIRCFS:
+			dev_dbg(dev, "Get carrier mode and freq");
+			break;
+		case MCE_RSP_EQIRTXPORTS:
+			dev_dbg(dev, "%s transmit blaster mask of 0x%02x",
+				 inout, data1);
+			break;
+		case MCE_RSP_EQIRTIMEOUT:
+			/* value is in units of 50us, so x*50/1000 ms */
+			period = ((data1 << 8) | data2) * MCE_TIME_UNIT / 1000;
+			dev_dbg(dev, "%s receive timeout of %d ms",
+				 inout, period);
+			break;
+		case MCE_CMD_GETIRTIMEOUT:
+			dev_dbg(dev, "Get receive timeout");
+			break;
+		case MCE_CMD_GETIRTXPORTS:
+			dev_dbg(dev, "Get transmit blaster mask");
+			break;
+		case MCE_RSP_EQIRRXPORTEN:
+			dev_dbg(dev, "%s %s-range receive sensor in use",
+				 inout, data1 == 0x02 ? "short" : "long");
+			break;
+		case MCE_CMD_GETIRRXPORTEN:
+		/* aka MCE_RSP_EQIRRXCFCNT */
+			if (out)
+				dev_dbg(dev, "Get receive sensor");
+			else if (ir->learning_enabled)
+				dev_dbg(dev, "RX pulse count: %d",
+					 ((data1 << 8) | data2));
+			break;
+		case MCE_RSP_EQIRNUMPORTS:
+			if (out)
+				break;
+			dev_dbg(dev, "Num TX ports: %x, num RX ports: %x",
+				 data1, data2);
+			break;
+		case MCE_RSP_CMD_ILLEGAL:
+			dev_dbg(dev, "Illegal PORT_IR command");
+			break;
+		default:
+			dev_dbg(dev, "Unknown command 0x%02x 0x%02x",
+				 cmd, subcmd);
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+
+	if (cmd == MCE_IRDATA_TRAILER)
+		dev_dbg(dev, "End of raw IR data");
+	else if ((cmd != MCE_CMD_PORT_IR) &&
+		 ((cmd & MCE_PORT_MASK) == MCE_COMMAND_IRDATA))
+		dev_dbg(dev, "Raw IR data, %d pulse/space samples", ir->rem);
+#endif
+}
+
+static void mce_async_callback(struct urb *urb)
+{
+	struct mceusb_dev *ir;
+	int len;
+
+	if (!urb)
+		return;
+
+	ir = urb->context;
+
+	switch (urb->status) {
+	/* success */
+	case 0:
+		len = urb->actual_length;
+
+		mceusb_dev_printdata(ir, urb->transfer_buffer, 0, len, true);
+		break;
+
+	case -ECONNRESET:
+	case -ENOENT:
+	case -EILSEQ:
+	case -ESHUTDOWN:
+		break;
+
+	case -EPIPE:
+	default:
+		dev_err(ir->dev, "Error: request urb status = %d", urb->status);
+		break;
+	}
+
+	/* the transfer buffer and urb were allocated in mce_request_packet */
+	kfree(urb->transfer_buffer);
+	usb_free_urb(urb);
+}
+
+/* request incoming or send outgoing usb packet - used to initialize remote */
+static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data,
+			       int size, int urb_type)
+{
+	int res, pipe;
+	struct urb *async_urb;
+	struct device *dev = ir->dev;
+	unsigned char *async_buf;
+
+	if (urb_type == MCEUSB_TX) {
+		async_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (unlikely(!async_urb)) {
+			dev_err(dev, "Error, couldn't allocate urb!\n");
+			return;
+		}
+
+		async_buf = kzalloc(size, GFP_KERNEL);
+		if (!async_buf) {
+			dev_err(dev, "Error, couldn't allocate buf!\n");
+			usb_free_urb(async_urb);
+			return;
+		}
+
+		/* outbound data */
+		if (usb_endpoint_xfer_int(ir->usb_ep_out)) {
+			pipe = usb_sndintpipe(ir->usbdev,
+					 ir->usb_ep_out->bEndpointAddress);
+			usb_fill_int_urb(async_urb, ir->usbdev, pipe, async_buf,
+					 size, mce_async_callback, ir,
+					 ir->usb_ep_out->bInterval);
+		} else {
+			pipe = usb_sndbulkpipe(ir->usbdev,
+					 ir->usb_ep_out->bEndpointAddress);
+			usb_fill_bulk_urb(async_urb, ir->usbdev, pipe,
+					 async_buf, size, mce_async_callback,
+					 ir);
+		}
+		memcpy(async_buf, data, size);
+
+	} else if (urb_type == MCEUSB_RX) {
+		/* standard request */
+		async_urb = ir->urb_in;
+		ir->send_flags = RECV_FLAG_IN_PROGRESS;
+
+	} else {
+		dev_err(dev, "Error! Unknown urb type %d\n", urb_type);
+		return;
+	}
+
+	dev_dbg(dev, "receive request called (size=%#x)", size);
+
+	async_urb->transfer_buffer_length = size;
+	async_urb->dev = ir->usbdev;
+
+	res = usb_submit_urb(async_urb, GFP_ATOMIC);
+	if (res) {
+		dev_err(dev, "receive request FAILED! (res=%d)", res);
+		return;
+	}
+	dev_dbg(dev, "receive request complete (res=%d)", res);
+}
+
+static void mce_async_out(struct mceusb_dev *ir, unsigned char *data, int size)
+{
+	int rsize = sizeof(DEVICE_RESUME);
+
+	if (ir->need_reset) {
+		ir->need_reset = false;
+		mce_request_packet(ir, DEVICE_RESUME, rsize, MCEUSB_TX);
+		msleep(10);
+	}
+
+	mce_request_packet(ir, data, size, MCEUSB_TX);
+	msleep(10);
+}
+
+static void mce_flush_rx_buffer(struct mceusb_dev *ir, int size)
+{
+	mce_request_packet(ir, NULL, size, MCEUSB_RX);
+}
+
+/* Send data out the IR blaster port(s) */
+static int mceusb_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count)
+{
+	struct mceusb_dev *ir = dev->priv;
+	int i, length, ret = 0;
+	int cmdcount = 0;
+	unsigned char cmdbuf[MCE_CMDBUF_SIZE];
+
+	/* MCE tx init header */
+	cmdbuf[cmdcount++] = MCE_CMD_PORT_IR;
+	cmdbuf[cmdcount++] = MCE_CMD_SETIRTXPORTS;
+	cmdbuf[cmdcount++] = ir->tx_mask;
+
+	/* Send the set TX ports command */
+	mce_async_out(ir, cmdbuf, cmdcount);
+	cmdcount = 0;
+
+	/* Generate mce packet data */
+	for (i = 0; (i < count) && (cmdcount < MCE_CMDBUF_SIZE); i++) {
+		txbuf[i] = txbuf[i] / MCE_TIME_UNIT;
+
+		do { /* loop to support long pulses/spaces > 127*50us=6.35ms */
+
+			/* Insert mce packet header every 4th entry */
+			if ((cmdcount < MCE_CMDBUF_SIZE) &&
+			    (cmdcount % MCE_CODE_LENGTH) == 0)
+				cmdbuf[cmdcount++] = MCE_IRDATA_HEADER;
+
+			/* Insert mce packet data */
+			if (cmdcount < MCE_CMDBUF_SIZE)
+				cmdbuf[cmdcount++] =
+					(txbuf[i] < MCE_PULSE_BIT ?
+					 txbuf[i] : MCE_MAX_PULSE_LENGTH) |
+					 (i & 1 ? 0x00 : MCE_PULSE_BIT);
+			else {
+				ret = -EINVAL;
+				goto out;
+			}
+
+		} while ((txbuf[i] > MCE_MAX_PULSE_LENGTH) &&
+			 (txbuf[i] -= MCE_MAX_PULSE_LENGTH));
+	}
+
+	/* Check if we have room for the empty packet at the end */
+	if (cmdcount >= MCE_CMDBUF_SIZE) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* Fix packet length in last header */
+	length = cmdcount % MCE_CODE_LENGTH;
+	cmdbuf[cmdcount - length] -= MCE_CODE_LENGTH - length;
+
+	/* All mce commands end with an empty packet (0x80) */
+	cmdbuf[cmdcount++] = MCE_IRDATA_TRAILER;
+
+	/* Transmit the command to the mce device */
+	mce_async_out(ir, cmdbuf, cmdcount);
+
+out:
+	return ret ? ret : count;
+}
+
+/* Sets active IR outputs -- mce devices typically have two */
+static int mceusb_set_tx_mask(struct rc_dev *dev, u32 mask)
+{
+	struct mceusb_dev *ir = dev->priv;
+
+	if (ir->flags.tx_mask_normal)
+		ir->tx_mask = mask;
+	else
+		ir->tx_mask = (mask != MCE_DEFAULT_TX_MASK ?
+				mask ^ MCE_DEFAULT_TX_MASK : mask) << 1;
+
+	return 0;
+}
+
+/* Sets the send carrier frequency and mode */
+static int mceusb_set_tx_carrier(struct rc_dev *dev, u32 carrier)
+{
+	struct mceusb_dev *ir = dev->priv;
+	int clk = 10000000;
+	int prescaler = 0, divisor = 0;
+	unsigned char cmdbuf[4] = { MCE_CMD_PORT_IR,
+				    MCE_CMD_SETIRCFS, 0x00, 0x00 };
+
+	/* Carrier has changed */
+	if (ir->carrier != carrier) {
+
+		if (carrier == 0) {
+			ir->carrier = carrier;
+			cmdbuf[2] = MCE_CMD_SIG_END;
+			cmdbuf[3] = MCE_IRDATA_TRAILER;
+			dev_dbg(ir->dev, "disabling carrier modulation");
+			mce_async_out(ir, cmdbuf, sizeof(cmdbuf));
+			return carrier;
+		}
+
+		for (prescaler = 0; prescaler < 4; ++prescaler) {
+			divisor = (clk >> (2 * prescaler)) / carrier;
+			if (divisor <= 0xff) {
+				ir->carrier = carrier;
+				cmdbuf[2] = prescaler;
+				cmdbuf[3] = divisor;
+				dev_dbg(ir->dev, "requesting %u HZ carrier",
+								carrier);
+
+				/* Transmit new carrier to mce device */
+				mce_async_out(ir, cmdbuf, sizeof(cmdbuf));
+				return carrier;
+			}
+		}
+
+		return -EINVAL;
+
+	}
+
+	return carrier;
+}
+
+/*
+ * We don't do anything but print debug spew for many of the command bits
+ * we receive from the hardware, but some of them are useful information
+ * we want to store so that we can use them.
+ */
+static void mceusb_handle_command(struct mceusb_dev *ir, int index)
+{
+	u8 hi = ir->buf_in[index + 1] & 0xff;
+	u8 lo = ir->buf_in[index + 2] & 0xff;
+
+	switch (ir->buf_in[index]) {
+	/* the one and only 5-byte return value command */
+	case MCE_RSP_GETPORTSTATUS:
+		if ((ir->buf_in[index + 4] & 0xff) == 0x00)
+			ir->txports_cabled |= 1 << hi;
+		break;
+
+	/* 2-byte return value commands */
+	case MCE_RSP_EQIRTIMEOUT:
+		ir->rc->timeout = US_TO_NS((hi << 8 | lo) * MCE_TIME_UNIT);
+		break;
+	case MCE_RSP_EQIRNUMPORTS:
+		ir->num_txports = hi;
+		ir->num_rxports = lo;
+		break;
+
+	/* 1-byte return value commands */
+	case MCE_RSP_EQEMVER:
+		ir->emver = hi;
+		break;
+	case MCE_RSP_EQIRTXPORTS:
+		ir->tx_mask = hi;
+		break;
+	case MCE_RSP_EQIRRXPORTEN:
+		ir->learning_enabled = ((hi & 0x02) == 0x02);
+		ir->rxports_active = hi;
+		break;
+	case MCE_RSP_CMD_ILLEGAL:
+		ir->need_reset = true;
+		break;
+	default:
+		break;
+	}
+}
+
+static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
+{
+	DEFINE_IR_RAW_EVENT(rawir);
+	bool event = false;
+	int i = 0;
+
+	/* skip meaningless 0xb1 0x60 header bytes on orig receiver */
+	if (ir->flags.microsoft_gen1)
+		i = 2;
+
+	/* if there's no data, just return now */
+	if (buf_len <= i)
+		return;
+
+	for (; i < buf_len; i++) {
+		switch (ir->parser_state) {
+		case SUBCMD:
+			ir->rem = mceusb_cmd_datasize(ir->cmd, ir->buf_in[i]);
+			mceusb_dev_printdata(ir, ir->buf_in, i - 1,
+					     ir->rem + 2, false);
+			mceusb_handle_command(ir, i);
+			ir->parser_state = CMD_DATA;
+			break;
+		case PARSE_IRDATA:
+			ir->rem--;
+			init_ir_raw_event(&rawir);
+			rawir.pulse = ((ir->buf_in[i] & MCE_PULSE_BIT) != 0);
+			rawir.duration = (ir->buf_in[i] & MCE_PULSE_MASK)
+					 * US_TO_NS(MCE_TIME_UNIT);
+
+			dev_dbg(ir->dev, "Storing %s with duration %d",
+				rawir.pulse ? "pulse" : "space",
+				rawir.duration);
+
+			if (ir_raw_event_store_with_filter(ir->rc, &rawir))
+				event = true;
+			break;
+		case CMD_DATA:
+			ir->rem--;
+			break;
+		case CMD_HEADER:
+			/* decode mce packets of the form (84),AA,BB,CC,DD */
+			/* IR data packets can span USB messages - rem */
+			ir->cmd = ir->buf_in[i];
+			if ((ir->cmd == MCE_CMD_PORT_IR) ||
+			    ((ir->cmd & MCE_PORT_MASK) !=
+			     MCE_COMMAND_IRDATA)) {
+				ir->parser_state = SUBCMD;
+				continue;
+			}
+			ir->rem = (ir->cmd & MCE_PACKET_LENGTH_MASK);
+			mceusb_dev_printdata(ir, ir->buf_in,
+					     i, ir->rem + 1, false);
+			if (ir->rem)
+				ir->parser_state = PARSE_IRDATA;
+			else
+				ir_raw_event_reset(ir->rc);
+			break;
+		}
+
+		if (ir->parser_state != CMD_HEADER && !ir->rem)
+			ir->parser_state = CMD_HEADER;
+	}
+	if (event) {
+		dev_dbg(ir->dev, "processed IR data");
+		ir_raw_event_handle(ir->rc);
+	}
+}
+
+static void mceusb_dev_recv(struct urb *urb)
+{
+	struct mceusb_dev *ir;
+	int buf_len;
+
+	if (!urb)
+		return;
+
+	ir = urb->context;
+	if (!ir) {
+		usb_unlink_urb(urb);
+		return;
+	}
+
+	buf_len = urb->actual_length;
+
+	if (ir->send_flags == RECV_FLAG_IN_PROGRESS) {
+		ir->send_flags = SEND_FLAG_COMPLETE;
+		dev_dbg(ir->dev, "setup answer received %d bytes\n",
+			buf_len);
+	}
+
+	switch (urb->status) {
+	/* success */
+	case 0:
+		mceusb_process_ir_data(ir, buf_len);
+		break;
+
+	case -ECONNRESET:
+	case -ENOENT:
+	case -EILSEQ:
+	case -ESHUTDOWN:
+		usb_unlink_urb(urb);
+		return;
+
+	case -EPIPE:
+	default:
+		dev_err(ir->dev, "Error: urb status = %d", urb->status);
+		break;
+	}
+
+	usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static void mceusb_get_emulator_version(struct mceusb_dev *ir)
+{
+	/* If we get no reply or an illegal command reply, its ver 1, says MS */
+	ir->emver = 1;
+	mce_async_out(ir, GET_EMVER, sizeof(GET_EMVER));
+}
+
+static void mceusb_gen1_init(struct mceusb_dev *ir)
+{
+	int ret;
+	struct device *dev = ir->dev;
+	char *data;
+
+	data = kzalloc(USB_CTRL_MSG_SZ, GFP_KERNEL);
+	if (!data) {
+		dev_err(dev, "%s: memory allocation failed!", __func__);
+		return;
+	}
+
+	/*
+	 * This is a strange one. Windows issues a set address to the device
+	 * on the receive control pipe and expect a certain value pair back
+	 */
+	ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
+			      USB_REQ_SET_ADDRESS, USB_TYPE_VENDOR, 0, 0,
+			      data, USB_CTRL_MSG_SZ, HZ * 3);
+	dev_dbg(dev, "set address - ret = %d", ret);
+	dev_dbg(dev, "set address - data[0] = %d, data[1] = %d",
+						data[0], data[1]);
+
+	/* set feature: bit rate 38400 bps */
+	ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
+			      USB_REQ_SET_FEATURE, USB_TYPE_VENDOR,
+			      0xc04e, 0x0000, NULL, 0, HZ * 3);
+
+	dev_dbg(dev, "set feature - ret = %d", ret);
+
+	/* bRequest 4: set char length to 8 bits */
+	ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
+			      4, USB_TYPE_VENDOR,
+			      0x0808, 0x0000, NULL, 0, HZ * 3);
+	dev_dbg(dev, "set char length - retB = %d", ret);
+
+	/* bRequest 2: set handshaking to use DTR/DSR */
+	ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
+			      2, USB_TYPE_VENDOR,
+			      0x0000, 0x0100, NULL, 0, HZ * 3);
+	dev_dbg(dev, "set handshake  - retC = %d", ret);
+
+	/* device resume */
+	mce_async_out(ir, DEVICE_RESUME, sizeof(DEVICE_RESUME));
+
+	/* get hw/sw revision? */
+	mce_async_out(ir, GET_REVISION, sizeof(GET_REVISION));
+
+	kfree(data);
+}
+
+static void mceusb_gen2_init(struct mceusb_dev *ir)
+{
+	/* device resume */
+	mce_async_out(ir, DEVICE_RESUME, sizeof(DEVICE_RESUME));
+
+	/* get wake version (protocol, key, address) */
+	mce_async_out(ir, GET_WAKEVERSION, sizeof(GET_WAKEVERSION));
+
+	/* unknown what this one actually returns... */
+	mce_async_out(ir, GET_UNKNOWN2, sizeof(GET_UNKNOWN2));
+}
+
+static void mceusb_get_parameters(struct mceusb_dev *ir)
+{
+	int i;
+	unsigned char cmdbuf[3] = { MCE_CMD_PORT_SYS,
+				    MCE_CMD_GETPORTSTATUS, 0x00 };
+
+	/* defaults, if the hardware doesn't support querying */
+	ir->num_txports = 2;
+	ir->num_rxports = 2;
+
+	/* get number of tx and rx ports */
+	mce_async_out(ir, GET_NUM_PORTS, sizeof(GET_NUM_PORTS));
+
+	/* get the carrier and frequency */
+	mce_async_out(ir, GET_CARRIER_FREQ, sizeof(GET_CARRIER_FREQ));
+
+	if (ir->num_txports && !ir->flags.no_tx)
+		/* get the transmitter bitmask */
+		mce_async_out(ir, GET_TX_BITMASK, sizeof(GET_TX_BITMASK));
+
+	/* get receiver timeout value */
+	mce_async_out(ir, GET_RX_TIMEOUT, sizeof(GET_RX_TIMEOUT));
+
+	/* get receiver sensor setting */
+	mce_async_out(ir, GET_RX_SENSOR, sizeof(GET_RX_SENSOR));
+
+	for (i = 0; i < ir->num_txports; i++) {
+		cmdbuf[2] = i;
+		mce_async_out(ir, cmdbuf, sizeof(cmdbuf));
+	}
+}
+
+static void mceusb_flash_led(struct mceusb_dev *ir)
+{
+	if (ir->emver < 2)
+		return;
+
+	mce_async_out(ir, FLASH_LED, sizeof(FLASH_LED));
+}
+
+static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir)
+{
+	struct usb_device *udev = ir->usbdev;
+	struct device *dev = ir->dev;
+	struct rc_dev *rc;
+	int ret;
+
+	rc = rc_allocate_device();
+	if (!rc) {
+		dev_err(dev, "remote dev allocation failed");
+		goto out;
+	}
+
+	snprintf(ir->name, sizeof(ir->name), "%s (%04x:%04x)",
+		 mceusb_model[ir->model].name ?
+			mceusb_model[ir->model].name :
+			"Media Center Ed. eHome Infrared Remote Transceiver",
+		 le16_to_cpu(ir->usbdev->descriptor.idVendor),
+		 le16_to_cpu(ir->usbdev->descriptor.idProduct));
+
+	usb_make_path(ir->usbdev, ir->phys, sizeof(ir->phys));
+
+	rc->input_name = ir->name;
+	rc->input_phys = ir->phys;
+	usb_to_input_id(ir->usbdev, &rc->input_id);
+	rc->dev.parent = dev;
+	rc->priv = ir;
+	rc->driver_type = RC_DRIVER_IR_RAW;
+	rc->allowed_protocols = RC_BIT_ALL;
+	rc->timeout = MS_TO_NS(100);
+	if (!ir->flags.no_tx) {
+		rc->s_tx_mask = mceusb_set_tx_mask;
+		rc->s_tx_carrier = mceusb_set_tx_carrier;
+		rc->tx_ir = mceusb_tx_ir;
+	}
+	rc->driver_name = DRIVER_NAME;
+
+	switch (le16_to_cpu(udev->descriptor.idVendor)) {
+	case VENDOR_HAUPPAUGE:
+		rc->map_name = RC_MAP_HAUPPAUGE;
+		break;
+	case VENDOR_PCTV:
+		rc->map_name = RC_MAP_PINNACLE_PCTV_HD;
+		break;
+	default:
+		rc->map_name = RC_MAP_RC6_MCE;
+	}
+	if (mceusb_model[ir->model].rc_map)
+		rc->map_name = mceusb_model[ir->model].rc_map;
+
+	ret = rc_register_device(rc);
+	if (ret < 0) {
+		dev_err(dev, "remote dev registration failed");
+		goto out;
+	}
+
+	return rc;
+
+out:
+	rc_free_device(rc);
+	return NULL;
+}
+
+static int mceusb_dev_probe(struct usb_interface *intf,
+			    const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct usb_host_interface *idesc;
+	struct usb_endpoint_descriptor *ep = NULL;
+	struct usb_endpoint_descriptor *ep_in = NULL;
+	struct usb_endpoint_descriptor *ep_out = NULL;
+	struct mceusb_dev *ir = NULL;
+	int pipe, maxp, i;
+	char buf[63], name[128] = "";
+	enum mceusb_model_type model = id->driver_info;
+	bool is_gen3;
+	bool is_microsoft_gen1;
+	bool tx_mask_normal;
+	int ir_intfnum;
+
+	dev_dbg(&intf->dev, "%s called", __func__);
+
+	idesc  = intf->cur_altsetting;
+
+	is_gen3 = mceusb_model[model].mce_gen3;
+	is_microsoft_gen1 = mceusb_model[model].mce_gen1;
+	tx_mask_normal = mceusb_model[model].tx_mask_normal;
+	ir_intfnum = mceusb_model[model].ir_intfnum;
+
+	/* There are multi-function devices with non-IR interfaces */
+	if (idesc->desc.bInterfaceNumber != ir_intfnum)
+		return -ENODEV;
+
+	/* step through the endpoints to find first bulk in and out endpoint */
+	for (i = 0; i < idesc->desc.bNumEndpoints; ++i) {
+		ep = &idesc->endpoint[i].desc;
+
+		if (ep_in == NULL) {
+			if (usb_endpoint_is_bulk_in(ep)) {
+				ep_in = ep;
+				dev_dbg(&intf->dev, "acceptable bulk inbound endpoint found\n");
+			} else if (usb_endpoint_is_int_in(ep)) {
+				ep_in = ep;
+				ep_in->bInterval = 1;
+				dev_dbg(&intf->dev, "acceptable interrupt inbound endpoint found\n");
+			}
+		}
+
+		if (ep_out == NULL) {
+			if (usb_endpoint_is_bulk_out(ep)) {
+				ep_out = ep;
+				dev_dbg(&intf->dev, "acceptable bulk outbound endpoint found\n");
+			} else if (usb_endpoint_is_int_out(ep)) {
+				ep_out = ep;
+				ep_out->bInterval = 1;
+				dev_dbg(&intf->dev, "acceptable interrupt outbound endpoint found\n");
+			}
+		}
+	}
+	if (!ep_in || !ep_out) {
+		dev_dbg(&intf->dev, "required endpoints not found\n");
+		return -ENODEV;
+	}
+
+	if (usb_endpoint_xfer_int(ep_in))
+		pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress);
+	else
+		pipe = usb_rcvbulkpipe(dev, ep_in->bEndpointAddress);
+	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+	ir = kzalloc(sizeof(struct mceusb_dev), GFP_KERNEL);
+	if (!ir)
+		goto mem_alloc_fail;
+
+	ir->buf_in = usb_alloc_coherent(dev, maxp, GFP_ATOMIC, &ir->dma_in);
+	if (!ir->buf_in)
+		goto buf_in_alloc_fail;
+
+	ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
+	if (!ir->urb_in)
+		goto urb_in_alloc_fail;
+
+	ir->usbdev = usb_get_dev(dev);
+	ir->dev = &intf->dev;
+	ir->len_in = maxp;
+	ir->flags.microsoft_gen1 = is_microsoft_gen1;
+	ir->flags.tx_mask_normal = tx_mask_normal;
+	ir->flags.no_tx = mceusb_model[model].no_tx;
+	ir->model = model;
+
+	/* Saving usb interface data for use by the transmitter routine */
+	ir->usb_ep_out = ep_out;
+
+	if (dev->descriptor.iManufacturer
+	    && usb_string(dev, dev->descriptor.iManufacturer,
+			  buf, sizeof(buf)) > 0)
+		strlcpy(name, buf, sizeof(name));
+	if (dev->descriptor.iProduct
+	    && usb_string(dev, dev->descriptor.iProduct,
+			  buf, sizeof(buf)) > 0)
+		snprintf(name + strlen(name), sizeof(name) - strlen(name),
+			 " %s", buf);
+
+	ir->rc = mceusb_init_rc_dev(ir);
+	if (!ir->rc)
+		goto rc_dev_fail;
+
+	/* wire up inbound data handler */
+	usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in, maxp,
+				mceusb_dev_recv, ir, ep_in->bInterval);
+	ir->urb_in->transfer_dma = ir->dma_in;
+	ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	/* flush buffers on the device */
+	dev_dbg(&intf->dev, "Flushing receive buffers\n");
+	mce_flush_rx_buffer(ir, maxp);
+
+	/* figure out which firmware/emulator version this hardware has */
+	mceusb_get_emulator_version(ir);
+
+	/* initialize device */
+	if (ir->flags.microsoft_gen1)
+		mceusb_gen1_init(ir);
+	else if (!is_gen3)
+		mceusb_gen2_init(ir);
+
+	mceusb_get_parameters(ir);
+
+	mceusb_flash_led(ir);
+
+	if (!ir->flags.no_tx)
+		mceusb_set_tx_mask(ir->rc, MCE_DEFAULT_TX_MASK);
+
+	usb_set_intfdata(intf, ir);
+
+	/* enable wake via this device */
+	device_set_wakeup_capable(ir->dev, true);
+	device_set_wakeup_enable(ir->dev, true);
+
+	dev_info(&intf->dev, "Registered %s with mce emulator interface version %x",
+		name, ir->emver);
+	dev_info(&intf->dev, "%x tx ports (0x%x cabled) and %x rx sensors (0x%x active)",
+		 ir->num_txports, ir->txports_cabled,
+		 ir->num_rxports, ir->rxports_active);
+
+	return 0;
+
+	/* Error-handling path */
+rc_dev_fail:
+	usb_put_dev(ir->usbdev);
+	usb_free_urb(ir->urb_in);
+urb_in_alloc_fail:
+	usb_free_coherent(dev, maxp, ir->buf_in, ir->dma_in);
+buf_in_alloc_fail:
+	kfree(ir);
+mem_alloc_fail:
+	dev_err(&intf->dev, "%s: device setup failed!", __func__);
+
+	return -ENOMEM;
+}
+
+
+static void mceusb_dev_disconnect(struct usb_interface *intf)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct mceusb_dev *ir = usb_get_intfdata(intf);
+
+	usb_set_intfdata(intf, NULL);
+
+	if (!ir)
+		return;
+
+	ir->usbdev = NULL;
+	rc_unregister_device(ir->rc);
+	usb_kill_urb(ir->urb_in);
+	usb_free_urb(ir->urb_in);
+	usb_free_coherent(dev, ir->len_in, ir->buf_in, ir->dma_in);
+	usb_put_dev(dev);
+
+	kfree(ir);
+}
+
+static int mceusb_dev_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct mceusb_dev *ir = usb_get_intfdata(intf);
+	dev_info(ir->dev, "suspend");
+	usb_kill_urb(ir->urb_in);
+	return 0;
+}
+
+static int mceusb_dev_resume(struct usb_interface *intf)
+{
+	struct mceusb_dev *ir = usb_get_intfdata(intf);
+	dev_info(ir->dev, "resume");
+	if (usb_submit_urb(ir->urb_in, GFP_ATOMIC))
+		return -EIO;
+	return 0;
+}
+
+static struct usb_driver mceusb_dev_driver = {
+	.name =		DRIVER_NAME,
+	.probe =	mceusb_dev_probe,
+	.disconnect =	mceusb_dev_disconnect,
+	.suspend =	mceusb_dev_suspend,
+	.resume =	mceusb_dev_resume,
+	.reset_resume =	mceusb_dev_resume,
+	.id_table =	mceusb_dev_table
+};
+
+module_usb_driver(mceusb_dev_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, mceusb_dev_table);
diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c
new file mode 100644
index 0000000..fcc3b82
--- /dev/null
+++ b/drivers/media/rc/meson-ir.c
@@ -0,0 +1,216 @@
+/*
+ * Driver for Amlogic Meson IR remote receiver
+ *
+ * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#include <media/rc-core.h>
+
+#define DRIVER_NAME		"meson-ir"
+
+#define IR_DEC_LDR_ACTIVE	0x00
+#define IR_DEC_LDR_IDLE		0x04
+#define IR_DEC_LDR_REPEAT	0x08
+#define IR_DEC_BIT_0		0x0c
+#define IR_DEC_REG0		0x10
+#define IR_DEC_FRAME		0x14
+#define IR_DEC_STATUS		0x18
+#define IR_DEC_REG1		0x1c
+
+#define REG0_RATE_MASK		(BIT(11) - 1)
+
+#define REG1_MODE_MASK		(BIT(7) | BIT(8))
+#define REG1_MODE_NEC		(0 << 7)
+#define REG1_MODE_GENERAL	(2 << 7)
+
+#define REG1_TIME_IV_SHIFT	16
+#define REG1_TIME_IV_MASK	((BIT(13) - 1) << REG1_TIME_IV_SHIFT)
+
+#define REG1_IRQSEL_MASK	(BIT(2) | BIT(3))
+#define REG1_IRQSEL_NEC_MODE	(0 << 2)
+#define REG1_IRQSEL_RISE_FALL	(1 << 2)
+#define REG1_IRQSEL_FALL	(2 << 2)
+#define REG1_IRQSEL_RISE	(3 << 2)
+
+#define REG1_RESET		BIT(0)
+#define REG1_ENABLE		BIT(15)
+
+#define STATUS_IR_DEC_IN	BIT(8)
+
+#define MESON_TRATE		10	/* us */
+
+struct meson_ir {
+	void __iomem	*reg;
+	struct rc_dev	*rc;
+	int		irq;
+	spinlock_t	lock;
+};
+
+static void meson_ir_set_mask(struct meson_ir *ir, unsigned int reg,
+			      u32 mask, u32 value)
+{
+	u32 data;
+
+	data = readl(ir->reg + reg);
+	data &= ~mask;
+	data |= (value & mask);
+	writel(data, ir->reg + reg);
+}
+
+static irqreturn_t meson_ir_irq(int irqno, void *dev_id)
+{
+	struct meson_ir *ir = dev_id;
+	u32 duration;
+	DEFINE_IR_RAW_EVENT(rawir);
+
+	spin_lock(&ir->lock);
+
+	duration = readl(ir->reg + IR_DEC_REG1);
+	duration = (duration & REG1_TIME_IV_MASK) >> REG1_TIME_IV_SHIFT;
+	rawir.duration = US_TO_NS(duration * MESON_TRATE);
+
+	rawir.pulse = !!(readl(ir->reg + IR_DEC_STATUS) & STATUS_IR_DEC_IN);
+
+	ir_raw_event_store_with_filter(ir->rc, &rawir);
+	ir_raw_event_handle(ir->rc);
+
+	spin_unlock(&ir->lock);
+
+	return IRQ_HANDLED;
+}
+
+static int meson_ir_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	struct resource *res;
+	const char *map_name;
+	struct meson_ir *ir;
+	int ret;
+
+	ir = devm_kzalloc(dev, sizeof(struct meson_ir), GFP_KERNEL);
+	if (!ir)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ir->reg = devm_ioremap_resource(dev, res);
+	if (IS_ERR(ir->reg)) {
+		dev_err(dev, "failed to map registers\n");
+		return PTR_ERR(ir->reg);
+	}
+
+	ir->irq = platform_get_irq(pdev, 0);
+	if (ir->irq < 0) {
+		dev_err(dev, "no irq resource\n");
+		return ir->irq;
+	}
+
+	ir->rc = rc_allocate_device();
+	if (!ir->rc) {
+		dev_err(dev, "failed to allocate rc device\n");
+		return -ENOMEM;
+	}
+
+	ir->rc->priv = ir;
+	ir->rc->input_name = DRIVER_NAME;
+	ir->rc->input_phys = DRIVER_NAME "/input0";
+	ir->rc->input_id.bustype = BUS_HOST;
+	map_name = of_get_property(node, "linux,rc-map-name", NULL);
+	ir->rc->map_name = map_name ? map_name : RC_MAP_EMPTY;
+	ir->rc->dev.parent = dev;
+	ir->rc->driver_type = RC_DRIVER_IR_RAW;
+	ir->rc->allowed_protocols = RC_BIT_ALL;
+	ir->rc->rx_resolution = US_TO_NS(MESON_TRATE);
+	ir->rc->timeout = MS_TO_NS(200);
+	ir->rc->driver_name = DRIVER_NAME;
+
+	spin_lock_init(&ir->lock);
+	platform_set_drvdata(pdev, ir);
+
+	ret = rc_register_device(ir->rc);
+	if (ret) {
+		dev_err(dev, "failed to register rc device\n");
+		goto out_free;
+	}
+
+	ret = devm_request_irq(dev, ir->irq, meson_ir_irq, 0, "ir-meson", ir);
+	if (ret) {
+		dev_err(dev, "failed to request irq\n");
+		goto out_unreg;
+	}
+
+	/* Reset the decoder */
+	meson_ir_set_mask(ir, IR_DEC_REG1, REG1_RESET, REG1_RESET);
+	meson_ir_set_mask(ir, IR_DEC_REG1, REG1_RESET, 0);
+	/* Set general operation mode */
+	meson_ir_set_mask(ir, IR_DEC_REG1, REG1_MODE_MASK, REG1_MODE_GENERAL);
+	/* Set rate */
+	meson_ir_set_mask(ir, IR_DEC_REG0, REG0_RATE_MASK, MESON_TRATE - 1);
+	/* IRQ on rising and falling edges */
+	meson_ir_set_mask(ir, IR_DEC_REG1, REG1_IRQSEL_MASK,
+			  REG1_IRQSEL_RISE_FALL);
+	/* Enable the decoder */
+	meson_ir_set_mask(ir, IR_DEC_REG1, REG1_ENABLE, REG1_ENABLE);
+
+	dev_info(dev, "receiver initialized\n");
+
+	return 0;
+out_unreg:
+	rc_unregister_device(ir->rc);
+	ir->rc = NULL;
+out_free:
+	rc_free_device(ir->rc);
+
+	return ret;
+}
+
+static int meson_ir_remove(struct platform_device *pdev)
+{
+	struct meson_ir *ir = platform_get_drvdata(pdev);
+	unsigned long flags;
+
+	/* Disable the decoder */
+	spin_lock_irqsave(&ir->lock, flags);
+	meson_ir_set_mask(ir, IR_DEC_REG1, REG1_ENABLE, 0);
+	spin_unlock_irqrestore(&ir->lock, flags);
+
+	rc_unregister_device(ir->rc);
+
+	return 0;
+}
+
+static const struct of_device_id meson_ir_match[] = {
+	{ .compatible = "amlogic,meson6-ir" },
+	{ },
+};
+
+static struct platform_driver meson_ir_driver = {
+	.probe		= meson_ir_probe,
+	.remove		= meson_ir_remove,
+	.driver = {
+		.name		= DRIVER_NAME,
+		.of_match_table	= meson_ir_match,
+	},
+};
+
+module_platform_driver(meson_ir_driver);
+
+MODULE_DESCRIPTION("Amlogic Meson IR remote receiver driver");
+MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c
new file mode 100644
index 0000000..85af7a8
--- /dev/null
+++ b/drivers/media/rc/nuvoton-cir.c
@@ -0,0 +1,1231 @@
+/*
+ * Driver for Nuvoton Technology Corporation w83667hg/w83677hg-i CIR
+ *
+ * Copyright (C) 2010 Jarod Wilson <jarod@redhat.com>
+ * Copyright (C) 2009 Nuvoton PS Team
+ *
+ * Special thanks to Nuvoton for providing hardware, spec sheets and
+ * sample code upon which portions of this driver are based. Indirect
+ * thanks also to Maxim Levitsky, whose ene_ir driver this driver is
+ * modeled after.
+ *
+ * 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
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pnp.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <media/rc-core.h>
+#include <linux/pci_ids.h>
+
+#include "nuvoton-cir.h"
+
+/* write val to config reg */
+static inline void nvt_cr_write(struct nvt_dev *nvt, u8 val, u8 reg)
+{
+	outb(reg, nvt->cr_efir);
+	outb(val, nvt->cr_efdr);
+}
+
+/* read val from config reg */
+static inline u8 nvt_cr_read(struct nvt_dev *nvt, u8 reg)
+{
+	outb(reg, nvt->cr_efir);
+	return inb(nvt->cr_efdr);
+}
+
+/* update config register bit without changing other bits */
+static inline void nvt_set_reg_bit(struct nvt_dev *nvt, u8 val, u8 reg)
+{
+	u8 tmp = nvt_cr_read(nvt, reg) | val;
+	nvt_cr_write(nvt, tmp, reg);
+}
+
+/* clear config register bit without changing other bits */
+static inline void nvt_clear_reg_bit(struct nvt_dev *nvt, u8 val, u8 reg)
+{
+	u8 tmp = nvt_cr_read(nvt, reg) & ~val;
+	nvt_cr_write(nvt, tmp, reg);
+}
+
+/* enter extended function mode */
+static inline void nvt_efm_enable(struct nvt_dev *nvt)
+{
+	/* Enabling Extended Function Mode explicitly requires writing 2x */
+	outb(EFER_EFM_ENABLE, nvt->cr_efir);
+	outb(EFER_EFM_ENABLE, nvt->cr_efir);
+}
+
+/* exit extended function mode */
+static inline void nvt_efm_disable(struct nvt_dev *nvt)
+{
+	outb(EFER_EFM_DISABLE, nvt->cr_efir);
+}
+
+/*
+ * When you want to address a specific logical device, write its logical
+ * device number to CR_LOGICAL_DEV_SEL, then enable/disable by writing
+ * 0x1/0x0 respectively to CR_LOGICAL_DEV_EN.
+ */
+static inline void nvt_select_logical_dev(struct nvt_dev *nvt, u8 ldev)
+{
+	outb(CR_LOGICAL_DEV_SEL, nvt->cr_efir);
+	outb(ldev, nvt->cr_efdr);
+}
+
+/* write val to cir config register */
+static inline void nvt_cir_reg_write(struct nvt_dev *nvt, u8 val, u8 offset)
+{
+	outb(val, nvt->cir_addr + offset);
+}
+
+/* read val from cir config register */
+static u8 nvt_cir_reg_read(struct nvt_dev *nvt, u8 offset)
+{
+	u8 val;
+
+	val = inb(nvt->cir_addr + offset);
+
+	return val;
+}
+
+/* write val to cir wake register */
+static inline void nvt_cir_wake_reg_write(struct nvt_dev *nvt,
+					  u8 val, u8 offset)
+{
+	outb(val, nvt->cir_wake_addr + offset);
+}
+
+/* read val from cir wake config register */
+static u8 nvt_cir_wake_reg_read(struct nvt_dev *nvt, u8 offset)
+{
+	u8 val;
+
+	val = inb(nvt->cir_wake_addr + offset);
+
+	return val;
+}
+
+/* dump current cir register contents */
+static void cir_dump_regs(struct nvt_dev *nvt)
+{
+	nvt_efm_enable(nvt);
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
+
+	pr_info("%s: Dump CIR logical device registers:\n", NVT_DRIVER_NAME);
+	pr_info(" * CR CIR ACTIVE :   0x%x\n",
+		nvt_cr_read(nvt, CR_LOGICAL_DEV_EN));
+	pr_info(" * CR CIR BASE ADDR: 0x%x\n",
+		(nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) |
+		nvt_cr_read(nvt, CR_CIR_BASE_ADDR_LO));
+	pr_info(" * CR CIR IRQ NUM:   0x%x\n",
+		nvt_cr_read(nvt, CR_CIR_IRQ_RSRC));
+
+	nvt_efm_disable(nvt);
+
+	pr_info("%s: Dump CIR registers:\n", NVT_DRIVER_NAME);
+	pr_info(" * IRCON:     0x%x\n", nvt_cir_reg_read(nvt, CIR_IRCON));
+	pr_info(" * IRSTS:     0x%x\n", nvt_cir_reg_read(nvt, CIR_IRSTS));
+	pr_info(" * IREN:      0x%x\n", nvt_cir_reg_read(nvt, CIR_IREN));
+	pr_info(" * RXFCONT:   0x%x\n", nvt_cir_reg_read(nvt, CIR_RXFCONT));
+	pr_info(" * CP:        0x%x\n", nvt_cir_reg_read(nvt, CIR_CP));
+	pr_info(" * CC:        0x%x\n", nvt_cir_reg_read(nvt, CIR_CC));
+	pr_info(" * SLCH:      0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCH));
+	pr_info(" * SLCL:      0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCL));
+	pr_info(" * FIFOCON:   0x%x\n", nvt_cir_reg_read(nvt, CIR_FIFOCON));
+	pr_info(" * IRFIFOSTS: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFIFOSTS));
+	pr_info(" * SRXFIFO:   0x%x\n", nvt_cir_reg_read(nvt, CIR_SRXFIFO));
+	pr_info(" * TXFCONT:   0x%x\n", nvt_cir_reg_read(nvt, CIR_TXFCONT));
+	pr_info(" * STXFIFO:   0x%x\n", nvt_cir_reg_read(nvt, CIR_STXFIFO));
+	pr_info(" * FCCH:      0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCH));
+	pr_info(" * FCCL:      0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCL));
+	pr_info(" * IRFSM:     0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFSM));
+}
+
+/* dump current cir wake register contents */
+static void cir_wake_dump_regs(struct nvt_dev *nvt)
+{
+	u8 i, fifo_len;
+
+	nvt_efm_enable(nvt);
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE);
+
+	pr_info("%s: Dump CIR WAKE logical device registers:\n",
+		NVT_DRIVER_NAME);
+	pr_info(" * CR CIR WAKE ACTIVE :   0x%x\n",
+		nvt_cr_read(nvt, CR_LOGICAL_DEV_EN));
+	pr_info(" * CR CIR WAKE BASE ADDR: 0x%x\n",
+		(nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) |
+		nvt_cr_read(nvt, CR_CIR_BASE_ADDR_LO));
+	pr_info(" * CR CIR WAKE IRQ NUM:   0x%x\n",
+		nvt_cr_read(nvt, CR_CIR_IRQ_RSRC));
+
+	nvt_efm_disable(nvt);
+
+	pr_info("%s: Dump CIR WAKE registers\n", NVT_DRIVER_NAME);
+	pr_info(" * IRCON:          0x%x\n",
+		nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON));
+	pr_info(" * IRSTS:          0x%x\n",
+		nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRSTS));
+	pr_info(" * IREN:           0x%x\n",
+		nvt_cir_wake_reg_read(nvt, CIR_WAKE_IREN));
+	pr_info(" * FIFO CMP DEEP:  0x%x\n",
+		nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_DEEP));
+	pr_info(" * FIFO CMP TOL:   0x%x\n",
+		nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_TOL));
+	pr_info(" * FIFO COUNT:     0x%x\n",
+		nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT));
+	pr_info(" * SLCH:           0x%x\n",
+		nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCH));
+	pr_info(" * SLCL:           0x%x\n",
+		nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCL));
+	pr_info(" * FIFOCON:        0x%x\n",
+		nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFOCON));
+	pr_info(" * SRXFSTS:        0x%x\n",
+		nvt_cir_wake_reg_read(nvt, CIR_WAKE_SRXFSTS));
+	pr_info(" * SAMPLE RX FIFO: 0x%x\n",
+		nvt_cir_wake_reg_read(nvt, CIR_WAKE_SAMPLE_RX_FIFO));
+	pr_info(" * WR FIFO DATA:   0x%x\n",
+		nvt_cir_wake_reg_read(nvt, CIR_WAKE_WR_FIFO_DATA));
+	pr_info(" * RD FIFO ONLY:   0x%x\n",
+		nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY));
+	pr_info(" * RD FIFO ONLY IDX: 0x%x\n",
+		nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX));
+	pr_info(" * FIFO IGNORE:    0x%x\n",
+		nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_IGNORE));
+	pr_info(" * IRFSM:          0x%x\n",
+		nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRFSM));
+
+	fifo_len = nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT);
+	pr_info("%s: Dump CIR WAKE FIFO (len %d)\n", NVT_DRIVER_NAME, fifo_len);
+	pr_info("* Contents =");
+	for (i = 0; i < fifo_len; i++)
+		pr_cont(" %02x",
+			nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY));
+	pr_cont("\n");
+}
+
+/* detect hardware features */
+static int nvt_hw_detect(struct nvt_dev *nvt)
+{
+	unsigned long flags;
+	u8 chip_major, chip_minor;
+	char chip_id[12];
+	bool chip_unknown = false;
+
+	nvt_efm_enable(nvt);
+
+	/* Check if we're wired for the alternate EFER setup */
+	chip_major = nvt_cr_read(nvt, CR_CHIP_ID_HI);
+	if (chip_major == 0xff) {
+		nvt->cr_efir = CR_EFIR2;
+		nvt->cr_efdr = CR_EFDR2;
+		nvt_efm_enable(nvt);
+		chip_major = nvt_cr_read(nvt, CR_CHIP_ID_HI);
+	}
+
+	chip_minor = nvt_cr_read(nvt, CR_CHIP_ID_LO);
+
+	/* these are the known working chip revisions... */
+	switch (chip_major) {
+	case CHIP_ID_HIGH_667:
+		strcpy(chip_id, "w83667hg\0");
+		if (chip_minor != CHIP_ID_LOW_667)
+			chip_unknown = true;
+		break;
+	case CHIP_ID_HIGH_677B:
+		strcpy(chip_id, "w83677hg\0");
+		if (chip_minor != CHIP_ID_LOW_677B2 &&
+		    chip_minor != CHIP_ID_LOW_677B3)
+			chip_unknown = true;
+		break;
+	case CHIP_ID_HIGH_677C:
+		strcpy(chip_id, "w83677hg-c\0");
+		if (chip_minor != CHIP_ID_LOW_677C)
+			chip_unknown = true;
+		break;
+	default:
+		strcpy(chip_id, "w836x7hg\0");
+		chip_unknown = true;
+		break;
+	}
+
+	/* warn, but still let the driver load, if we don't know this chip */
+	if (chip_unknown)
+		nvt_pr(KERN_WARNING, "%s: unknown chip, id: 0x%02x 0x%02x, "
+		       "it may not work...", chip_id, chip_major, chip_minor);
+	else
+		nvt_dbg("%s: chip id: 0x%02x 0x%02x",
+			chip_id, chip_major, chip_minor);
+
+	nvt_efm_disable(nvt);
+
+	spin_lock_irqsave(&nvt->nvt_lock, flags);
+	nvt->chip_major = chip_major;
+	nvt->chip_minor = chip_minor;
+	spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+
+	return 0;
+}
+
+static void nvt_cir_ldev_init(struct nvt_dev *nvt)
+{
+	u8 val, psreg, psmask, psval;
+
+	if (nvt->chip_major == CHIP_ID_HIGH_667) {
+		psreg = CR_MULTIFUNC_PIN_SEL;
+		psmask = MULTIFUNC_PIN_SEL_MASK;
+		psval = MULTIFUNC_ENABLE_CIR | MULTIFUNC_ENABLE_CIRWB;
+	} else {
+		psreg = CR_OUTPUT_PIN_SEL;
+		psmask = OUTPUT_PIN_SEL_MASK;
+		psval = OUTPUT_ENABLE_CIR | OUTPUT_ENABLE_CIRWB;
+	}
+
+	/* output pin selection: enable CIR, with WB sensor enabled */
+	val = nvt_cr_read(nvt, psreg);
+	val &= psmask;
+	val |= psval;
+	nvt_cr_write(nvt, val, psreg);
+
+	/* Select CIR logical device and enable */
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
+	nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN);
+
+	nvt_cr_write(nvt, nvt->cir_addr >> 8, CR_CIR_BASE_ADDR_HI);
+	nvt_cr_write(nvt, nvt->cir_addr & 0xff, CR_CIR_BASE_ADDR_LO);
+
+	nvt_cr_write(nvt, nvt->cir_irq, CR_CIR_IRQ_RSRC);
+
+	nvt_dbg("CIR initialized, base io port address: 0x%lx, irq: %d",
+		nvt->cir_addr, nvt->cir_irq);
+}
+
+static void nvt_cir_wake_ldev_init(struct nvt_dev *nvt)
+{
+	/* Select ACPI logical device, enable it and CIR Wake */
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_ACPI);
+	nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN);
+
+	/* Enable CIR Wake via PSOUT# (Pin60) */
+	nvt_set_reg_bit(nvt, CIR_WAKE_ENABLE_BIT, CR_ACPI_CIR_WAKE);
+
+	/* enable pme interrupt of cir wakeup event */
+	nvt_set_reg_bit(nvt, PME_INTR_CIR_PASS_BIT, CR_ACPI_IRQ_EVENTS2);
+
+	/* Select CIR Wake logical device and enable */
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE);
+	nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN);
+
+	nvt_cr_write(nvt, nvt->cir_wake_addr >> 8, CR_CIR_BASE_ADDR_HI);
+	nvt_cr_write(nvt, nvt->cir_wake_addr & 0xff, CR_CIR_BASE_ADDR_LO);
+
+	nvt_cr_write(nvt, nvt->cir_wake_irq, CR_CIR_IRQ_RSRC);
+
+	nvt_dbg("CIR Wake initialized, base io port address: 0x%lx, irq: %d",
+		nvt->cir_wake_addr, nvt->cir_wake_irq);
+}
+
+/* clear out the hardware's cir rx fifo */
+static void nvt_clear_cir_fifo(struct nvt_dev *nvt)
+{
+	u8 val;
+
+	val = nvt_cir_reg_read(nvt, CIR_FIFOCON);
+	nvt_cir_reg_write(nvt, val | CIR_FIFOCON_RXFIFOCLR, CIR_FIFOCON);
+}
+
+/* clear out the hardware's cir wake rx fifo */
+static void nvt_clear_cir_wake_fifo(struct nvt_dev *nvt)
+{
+	u8 val;
+
+	val = nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFOCON);
+	nvt_cir_wake_reg_write(nvt, val | CIR_WAKE_FIFOCON_RXFIFOCLR,
+			       CIR_WAKE_FIFOCON);
+}
+
+/* clear out the hardware's cir tx fifo */
+static void nvt_clear_tx_fifo(struct nvt_dev *nvt)
+{
+	u8 val;
+
+	val = nvt_cir_reg_read(nvt, CIR_FIFOCON);
+	nvt_cir_reg_write(nvt, val | CIR_FIFOCON_TXFIFOCLR, CIR_FIFOCON);
+}
+
+/* enable RX Trigger Level Reach and Packet End interrupts */
+static void nvt_set_cir_iren(struct nvt_dev *nvt)
+{
+	u8 iren;
+
+	iren = CIR_IREN_RTR | CIR_IREN_PE;
+	nvt_cir_reg_write(nvt, iren, CIR_IREN);
+}
+
+static void nvt_cir_regs_init(struct nvt_dev *nvt)
+{
+	/* set sample limit count (PE interrupt raised when reached) */
+	nvt_cir_reg_write(nvt, CIR_RX_LIMIT_COUNT >> 8, CIR_SLCH);
+	nvt_cir_reg_write(nvt, CIR_RX_LIMIT_COUNT & 0xff, CIR_SLCL);
+
+	/* set fifo irq trigger levels */
+	nvt_cir_reg_write(nvt, CIR_FIFOCON_TX_TRIGGER_LEV |
+			  CIR_FIFOCON_RX_TRIGGER_LEV, CIR_FIFOCON);
+
+	/*
+	 * Enable TX and RX, specify carrier on = low, off = high, and set
+	 * sample period (currently 50us)
+	 */
+	nvt_cir_reg_write(nvt,
+			  CIR_IRCON_TXEN | CIR_IRCON_RXEN |
+			  CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL,
+			  CIR_IRCON);
+
+	/* clear hardware rx and tx fifos */
+	nvt_clear_cir_fifo(nvt);
+	nvt_clear_tx_fifo(nvt);
+
+	/* clear any and all stray interrupts */
+	nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS);
+
+	/* and finally, enable interrupts */
+	nvt_set_cir_iren(nvt);
+}
+
+static void nvt_cir_wake_regs_init(struct nvt_dev *nvt)
+{
+	/* set number of bytes needed for wake from s3 (default 65) */
+	nvt_cir_wake_reg_write(nvt, CIR_WAKE_FIFO_CMP_BYTES,
+			       CIR_WAKE_FIFO_CMP_DEEP);
+
+	/* set tolerance/variance allowed per byte during wake compare */
+	nvt_cir_wake_reg_write(nvt, CIR_WAKE_CMP_TOLERANCE,
+			       CIR_WAKE_FIFO_CMP_TOL);
+
+	/* set sample limit count (PE interrupt raised when reached) */
+	nvt_cir_wake_reg_write(nvt, CIR_RX_LIMIT_COUNT >> 8, CIR_WAKE_SLCH);
+	nvt_cir_wake_reg_write(nvt, CIR_RX_LIMIT_COUNT & 0xff, CIR_WAKE_SLCL);
+
+	/* set cir wake fifo rx trigger level (currently 67) */
+	nvt_cir_wake_reg_write(nvt, CIR_WAKE_FIFOCON_RX_TRIGGER_LEV,
+			       CIR_WAKE_FIFOCON);
+
+	/*
+	 * Enable TX and RX, specific carrier on = low, off = high, and set
+	 * sample period (currently 50us)
+	 */
+	nvt_cir_wake_reg_write(nvt, CIR_WAKE_IRCON_MODE0 | CIR_WAKE_IRCON_RXEN |
+			       CIR_WAKE_IRCON_R | CIR_WAKE_IRCON_RXINV |
+			       CIR_WAKE_IRCON_SAMPLE_PERIOD_SEL,
+			       CIR_WAKE_IRCON);
+
+	/* clear cir wake rx fifo */
+	nvt_clear_cir_wake_fifo(nvt);
+
+	/* clear any and all stray interrupts */
+	nvt_cir_wake_reg_write(nvt, 0xff, CIR_WAKE_IRSTS);
+}
+
+static void nvt_enable_wake(struct nvt_dev *nvt)
+{
+	nvt_efm_enable(nvt);
+
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_ACPI);
+	nvt_set_reg_bit(nvt, CIR_WAKE_ENABLE_BIT, CR_ACPI_CIR_WAKE);
+	nvt_set_reg_bit(nvt, PME_INTR_CIR_PASS_BIT, CR_ACPI_IRQ_EVENTS2);
+
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE);
+	nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN);
+
+	nvt_efm_disable(nvt);
+
+	nvt_cir_wake_reg_write(nvt, CIR_WAKE_IRCON_MODE0 | CIR_WAKE_IRCON_RXEN |
+			       CIR_WAKE_IRCON_R | CIR_WAKE_IRCON_RXINV |
+			       CIR_WAKE_IRCON_SAMPLE_PERIOD_SEL,
+			       CIR_WAKE_IRCON);
+	nvt_cir_wake_reg_write(nvt, 0xff, CIR_WAKE_IRSTS);
+	nvt_cir_wake_reg_write(nvt, 0, CIR_WAKE_IREN);
+}
+
+#if 0 /* Currently unused */
+/* rx carrier detect only works in learning mode, must be called w/nvt_lock */
+static u32 nvt_rx_carrier_detect(struct nvt_dev *nvt)
+{
+	u32 count, carrier, duration = 0;
+	int i;
+
+	count = nvt_cir_reg_read(nvt, CIR_FCCL) |
+		nvt_cir_reg_read(nvt, CIR_FCCH) << 8;
+
+	for (i = 0; i < nvt->pkts; i++) {
+		if (nvt->buf[i] & BUF_PULSE_BIT)
+			duration += nvt->buf[i] & BUF_LEN_MASK;
+	}
+
+	duration *= SAMPLE_PERIOD;
+
+	if (!count || !duration) {
+		nvt_pr(KERN_NOTICE, "Unable to determine carrier! (c:%u, d:%u)",
+		       count, duration);
+		return 0;
+	}
+
+	carrier = MS_TO_NS(count) / duration;
+
+	if ((carrier > MAX_CARRIER) || (carrier < MIN_CARRIER))
+		nvt_dbg("WTF? Carrier frequency out of range!");
+
+	nvt_dbg("Carrier frequency: %u (count %u, duration %u)",
+		carrier, count, duration);
+
+	return carrier;
+}
+#endif
+/*
+ * set carrier frequency
+ *
+ * set carrier on 2 registers: CP & CC
+ * always set CP as 0x81
+ * set CC by SPEC, CC = 3MHz/carrier - 1
+ */
+static int nvt_set_tx_carrier(struct rc_dev *dev, u32 carrier)
+{
+	struct nvt_dev *nvt = dev->priv;
+	u16 val;
+
+	if (carrier == 0)
+		return -EINVAL;
+
+	nvt_cir_reg_write(nvt, 1, CIR_CP);
+	val = 3000000 / (carrier) - 1;
+	nvt_cir_reg_write(nvt, val & 0xff, CIR_CC);
+
+	nvt_dbg("cp: 0x%x cc: 0x%x\n",
+		nvt_cir_reg_read(nvt, CIR_CP), nvt_cir_reg_read(nvt, CIR_CC));
+
+	return 0;
+}
+
+/*
+ * nvt_tx_ir
+ *
+ * 1) clean TX fifo first (handled by AP)
+ * 2) copy data from user space
+ * 3) disable RX interrupts, enable TX interrupts: TTR & TFU
+ * 4) send 9 packets to TX FIFO to open TTR
+ * in interrupt_handler:
+ * 5) send all data out
+ * go back to write():
+ * 6) disable TX interrupts, re-enable RX interupts
+ *
+ * The key problem of this function is user space data may larger than
+ * driver's data buf length. So nvt_tx_ir() will only copy TX_BUF_LEN data to
+ * buf, and keep current copied data buf num in cur_buf_num. But driver's buf
+ * number may larger than TXFCONT (0xff). So in interrupt_handler, it has to
+ * set TXFCONT as 0xff, until buf_count less than 0xff.
+ */
+static int nvt_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned n)
+{
+	struct nvt_dev *nvt = dev->priv;
+	unsigned long flags;
+	unsigned int i;
+	u8 iren;
+	int ret;
+
+	spin_lock_irqsave(&nvt->tx.lock, flags);
+
+	ret = min((unsigned)(TX_BUF_LEN / sizeof(unsigned)), n);
+	nvt->tx.buf_count = (ret * sizeof(unsigned));
+
+	memcpy(nvt->tx.buf, txbuf, nvt->tx.buf_count);
+
+	nvt->tx.cur_buf_num = 0;
+
+	/* save currently enabled interrupts */
+	iren = nvt_cir_reg_read(nvt, CIR_IREN);
+
+	/* now disable all interrupts, save TFU & TTR */
+	nvt_cir_reg_write(nvt, CIR_IREN_TFU | CIR_IREN_TTR, CIR_IREN);
+
+	nvt->tx.tx_state = ST_TX_REPLY;
+
+	nvt_cir_reg_write(nvt, CIR_FIFOCON_TX_TRIGGER_LEV_8 |
+			  CIR_FIFOCON_RXFIFOCLR, CIR_FIFOCON);
+
+	/* trigger TTR interrupt by writing out ones, (yes, it's ugly) */
+	for (i = 0; i < 9; i++)
+		nvt_cir_reg_write(nvt, 0x01, CIR_STXFIFO);
+
+	spin_unlock_irqrestore(&nvt->tx.lock, flags);
+
+	wait_event(nvt->tx.queue, nvt->tx.tx_state == ST_TX_REQUEST);
+
+	spin_lock_irqsave(&nvt->tx.lock, flags);
+	nvt->tx.tx_state = ST_TX_NONE;
+	spin_unlock_irqrestore(&nvt->tx.lock, flags);
+
+	/* restore enabled interrupts to prior state */
+	nvt_cir_reg_write(nvt, iren, CIR_IREN);
+
+	return ret;
+}
+
+/* dump contents of the last rx buffer we got from the hw rx fifo */
+static void nvt_dump_rx_buf(struct nvt_dev *nvt)
+{
+	int i;
+
+	printk(KERN_DEBUG "%s (len %d): ", __func__, nvt->pkts);
+	for (i = 0; (i < nvt->pkts) && (i < RX_BUF_LEN); i++)
+		printk(KERN_CONT "0x%02x ", nvt->buf[i]);
+	printk(KERN_CONT "\n");
+}
+
+/*
+ * Process raw data in rx driver buffer, store it in raw IR event kfifo,
+ * trigger decode when appropriate.
+ *
+ * We get IR data samples one byte at a time. If the msb is set, its a pulse,
+ * otherwise its a space. The lower 7 bits are the count of SAMPLE_PERIOD
+ * (default 50us) intervals for that pulse/space. A discrete signal is
+ * followed by a series of 0x7f packets, then either 0x7<something> or 0x80
+ * to signal more IR coming (repeats) or end of IR, respectively. We store
+ * sample data in the raw event kfifo until we see 0x7<something> (except f)
+ * or 0x80, at which time, we trigger a decode operation.
+ */
+static void nvt_process_rx_ir_data(struct nvt_dev *nvt)
+{
+	DEFINE_IR_RAW_EVENT(rawir);
+	u8 sample;
+	int i;
+
+	nvt_dbg_verbose("%s firing", __func__);
+
+	if (debug)
+		nvt_dump_rx_buf(nvt);
+
+	nvt_dbg_verbose("Processing buffer of len %d", nvt->pkts);
+
+	init_ir_raw_event(&rawir);
+
+	for (i = 0; i < nvt->pkts; i++) {
+		sample = nvt->buf[i];
+
+		rawir.pulse = ((sample & BUF_PULSE_BIT) != 0);
+		rawir.duration = US_TO_NS((sample & BUF_LEN_MASK)
+					  * SAMPLE_PERIOD);
+
+		nvt_dbg("Storing %s with duration %d",
+			rawir.pulse ? "pulse" : "space", rawir.duration);
+
+		ir_raw_event_store_with_filter(nvt->rdev, &rawir);
+
+		/*
+		 * BUF_PULSE_BIT indicates end of IR data, BUF_REPEAT_BYTE
+		 * indicates end of IR signal, but new data incoming. In both
+		 * cases, it means we're ready to call ir_raw_event_handle
+		 */
+		if ((sample == BUF_PULSE_BIT) && (i + 1 < nvt->pkts)) {
+			nvt_dbg("Calling ir_raw_event_handle (signal end)\n");
+			ir_raw_event_handle(nvt->rdev);
+		}
+	}
+
+	nvt->pkts = 0;
+
+	nvt_dbg("Calling ir_raw_event_handle (buffer empty)\n");
+	ir_raw_event_handle(nvt->rdev);
+
+	nvt_dbg_verbose("%s done", __func__);
+}
+
+static void nvt_handle_rx_fifo_overrun(struct nvt_dev *nvt)
+{
+	nvt_pr(KERN_WARNING, "RX FIFO overrun detected, flushing data!");
+
+	nvt->pkts = 0;
+	nvt_clear_cir_fifo(nvt);
+	ir_raw_event_reset(nvt->rdev);
+}
+
+/* copy data from hardware rx fifo into driver buffer */
+static void nvt_get_rx_ir_data(struct nvt_dev *nvt)
+{
+	unsigned long flags;
+	u8 fifocount, val;
+	unsigned int b_idx;
+	bool overrun = false;
+	int i;
+
+	/* Get count of how many bytes to read from RX FIFO */
+	fifocount = nvt_cir_reg_read(nvt, CIR_RXFCONT);
+	/* if we get 0xff, probably means the logical dev is disabled */
+	if (fifocount == 0xff)
+		return;
+	/* watch out for a fifo overrun condition */
+	else if (fifocount > RX_BUF_LEN) {
+		overrun = true;
+		fifocount = RX_BUF_LEN;
+	}
+
+	nvt_dbg("attempting to fetch %u bytes from hw rx fifo", fifocount);
+
+	spin_lock_irqsave(&nvt->nvt_lock, flags);
+
+	b_idx = nvt->pkts;
+
+	/* This should never happen, but lets check anyway... */
+	if (b_idx + fifocount > RX_BUF_LEN) {
+		nvt_process_rx_ir_data(nvt);
+		b_idx = 0;
+	}
+
+	/* Read fifocount bytes from CIR Sample RX FIFO register */
+	for (i = 0; i < fifocount; i++) {
+		val = nvt_cir_reg_read(nvt, CIR_SRXFIFO);
+		nvt->buf[b_idx + i] = val;
+	}
+
+	nvt->pkts += fifocount;
+	nvt_dbg("%s: pkts now %d", __func__, nvt->pkts);
+
+	nvt_process_rx_ir_data(nvt);
+
+	if (overrun)
+		nvt_handle_rx_fifo_overrun(nvt);
+
+	spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+}
+
+static void nvt_cir_log_irqs(u8 status, u8 iren)
+{
+	nvt_pr(KERN_INFO, "IRQ 0x%02x (IREN 0x%02x) :%s%s%s%s%s%s%s%s%s",
+		status, iren,
+		status & CIR_IRSTS_RDR	? " RDR"	: "",
+		status & CIR_IRSTS_RTR	? " RTR"	: "",
+		status & CIR_IRSTS_PE	? " PE"		: "",
+		status & CIR_IRSTS_RFO	? " RFO"	: "",
+		status & CIR_IRSTS_TE	? " TE"		: "",
+		status & CIR_IRSTS_TTR	? " TTR"	: "",
+		status & CIR_IRSTS_TFU	? " TFU"	: "",
+		status & CIR_IRSTS_GH	? " GH"		: "",
+		status & ~(CIR_IRSTS_RDR | CIR_IRSTS_RTR | CIR_IRSTS_PE |
+			   CIR_IRSTS_RFO | CIR_IRSTS_TE | CIR_IRSTS_TTR |
+			   CIR_IRSTS_TFU | CIR_IRSTS_GH) ? " ?" : "");
+}
+
+static bool nvt_cir_tx_inactive(struct nvt_dev *nvt)
+{
+	unsigned long flags;
+	bool tx_inactive;
+	u8 tx_state;
+
+	spin_lock_irqsave(&nvt->tx.lock, flags);
+	tx_state = nvt->tx.tx_state;
+	spin_unlock_irqrestore(&nvt->tx.lock, flags);
+
+	tx_inactive = (tx_state == ST_TX_NONE);
+
+	return tx_inactive;
+}
+
+/* interrupt service routine for incoming and outgoing CIR data */
+static irqreturn_t nvt_cir_isr(int irq, void *data)
+{
+	struct nvt_dev *nvt = data;
+	u8 status, iren, cur_state;
+	unsigned long flags;
+
+	nvt_dbg_verbose("%s firing", __func__);
+
+	nvt_efm_enable(nvt);
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
+	nvt_efm_disable(nvt);
+
+	/*
+	 * Get IR Status register contents. Write 1 to ack/clear
+	 *
+	 * bit: reg name      - description
+	 *   7: CIR_IRSTS_RDR - RX Data Ready
+	 *   6: CIR_IRSTS_RTR - RX FIFO Trigger Level Reach
+	 *   5: CIR_IRSTS_PE  - Packet End
+	 *   4: CIR_IRSTS_RFO - RX FIFO Overrun (RDR will also be set)
+	 *   3: CIR_IRSTS_TE  - TX FIFO Empty
+	 *   2: CIR_IRSTS_TTR - TX FIFO Trigger Level Reach
+	 *   1: CIR_IRSTS_TFU - TX FIFO Underrun
+	 *   0: CIR_IRSTS_GH  - Min Length Detected
+	 */
+	status = nvt_cir_reg_read(nvt, CIR_IRSTS);
+	if (!status) {
+		nvt_dbg_verbose("%s exiting, IRSTS 0x0", __func__);
+		nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS);
+		return IRQ_RETVAL(IRQ_NONE);
+	}
+
+	/* ack/clear all irq flags we've got */
+	nvt_cir_reg_write(nvt, status, CIR_IRSTS);
+	nvt_cir_reg_write(nvt, 0, CIR_IRSTS);
+
+	/* Interrupt may be shared with CIR Wake, bail if CIR not enabled */
+	iren = nvt_cir_reg_read(nvt, CIR_IREN);
+	if (!iren) {
+		nvt_dbg_verbose("%s exiting, CIR not enabled", __func__);
+		return IRQ_RETVAL(IRQ_NONE);
+	}
+
+	if (debug)
+		nvt_cir_log_irqs(status, iren);
+
+	if (status & CIR_IRSTS_RTR) {
+		/* FIXME: add code for study/learn mode */
+		/* We only do rx if not tx'ing */
+		if (nvt_cir_tx_inactive(nvt))
+			nvt_get_rx_ir_data(nvt);
+	}
+
+	if (status & CIR_IRSTS_PE) {
+		if (nvt_cir_tx_inactive(nvt))
+			nvt_get_rx_ir_data(nvt);
+
+		spin_lock_irqsave(&nvt->nvt_lock, flags);
+
+		cur_state = nvt->study_state;
+
+		spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+
+		if (cur_state == ST_STUDY_NONE)
+			nvt_clear_cir_fifo(nvt);
+	}
+
+	if (status & CIR_IRSTS_TE)
+		nvt_clear_tx_fifo(nvt);
+
+	if (status & CIR_IRSTS_TTR) {
+		unsigned int pos, count;
+		u8 tmp;
+
+		spin_lock_irqsave(&nvt->tx.lock, flags);
+
+		pos = nvt->tx.cur_buf_num;
+		count = nvt->tx.buf_count;
+
+		/* Write data into the hardware tx fifo while pos < count */
+		if (pos < count) {
+			nvt_cir_reg_write(nvt, nvt->tx.buf[pos], CIR_STXFIFO);
+			nvt->tx.cur_buf_num++;
+		/* Disable TX FIFO Trigger Level Reach (TTR) interrupt */
+		} else {
+			tmp = nvt_cir_reg_read(nvt, CIR_IREN);
+			nvt_cir_reg_write(nvt, tmp & ~CIR_IREN_TTR, CIR_IREN);
+		}
+
+		spin_unlock_irqrestore(&nvt->tx.lock, flags);
+
+	}
+
+	if (status & CIR_IRSTS_TFU) {
+		spin_lock_irqsave(&nvt->tx.lock, flags);
+		if (nvt->tx.tx_state == ST_TX_REPLY) {
+			nvt->tx.tx_state = ST_TX_REQUEST;
+			wake_up(&nvt->tx.queue);
+		}
+		spin_unlock_irqrestore(&nvt->tx.lock, flags);
+	}
+
+	nvt_dbg_verbose("%s done", __func__);
+	return IRQ_RETVAL(IRQ_HANDLED);
+}
+
+/* Interrupt service routine for CIR Wake */
+static irqreturn_t nvt_cir_wake_isr(int irq, void *data)
+{
+	u8 status, iren, val;
+	struct nvt_dev *nvt = data;
+	unsigned long flags;
+
+	nvt_dbg_wake("%s firing", __func__);
+
+	status = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRSTS);
+	if (!status)
+		return IRQ_RETVAL(IRQ_NONE);
+
+	if (status & CIR_WAKE_IRSTS_IR_PENDING)
+		nvt_clear_cir_wake_fifo(nvt);
+
+	nvt_cir_wake_reg_write(nvt, status, CIR_WAKE_IRSTS);
+	nvt_cir_wake_reg_write(nvt, 0, CIR_WAKE_IRSTS);
+
+	/* Interrupt may be shared with CIR, bail if Wake not enabled */
+	iren = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IREN);
+	if (!iren) {
+		nvt_dbg_wake("%s exiting, wake not enabled", __func__);
+		return IRQ_RETVAL(IRQ_HANDLED);
+	}
+
+	if ((status & CIR_WAKE_IRSTS_PE) &&
+	    (nvt->wake_state == ST_WAKE_START)) {
+		while (nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX)) {
+			val = nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY);
+			nvt_dbg("setting wake up key: 0x%x", val);
+		}
+
+		nvt_cir_wake_reg_write(nvt, 0, CIR_WAKE_IREN);
+		spin_lock_irqsave(&nvt->nvt_lock, flags);
+		nvt->wake_state = ST_WAKE_FINISH;
+		spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+	}
+
+	nvt_dbg_wake("%s done", __func__);
+	return IRQ_RETVAL(IRQ_HANDLED);
+}
+
+static void nvt_enable_cir(struct nvt_dev *nvt)
+{
+	/* set function enable flags */
+	nvt_cir_reg_write(nvt, CIR_IRCON_TXEN | CIR_IRCON_RXEN |
+			  CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL,
+			  CIR_IRCON);
+
+	nvt_efm_enable(nvt);
+
+	/* enable the CIR logical device */
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
+	nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN);
+
+	nvt_efm_disable(nvt);
+
+	/* clear all pending interrupts */
+	nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS);
+
+	/* enable interrupts */
+	nvt_set_cir_iren(nvt);
+}
+
+static void nvt_disable_cir(struct nvt_dev *nvt)
+{
+	/* disable CIR interrupts */
+	nvt_cir_reg_write(nvt, 0, CIR_IREN);
+
+	/* clear any and all pending interrupts */
+	nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS);
+
+	/* clear all function enable flags */
+	nvt_cir_reg_write(nvt, 0, CIR_IRCON);
+
+	/* clear hardware rx and tx fifos */
+	nvt_clear_cir_fifo(nvt);
+	nvt_clear_tx_fifo(nvt);
+
+	nvt_efm_enable(nvt);
+
+	/* disable the CIR logical device */
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
+	nvt_cr_write(nvt, LOGICAL_DEV_DISABLE, CR_LOGICAL_DEV_EN);
+
+	nvt_efm_disable(nvt);
+}
+
+static int nvt_open(struct rc_dev *dev)
+{
+	struct nvt_dev *nvt = dev->priv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&nvt->nvt_lock, flags);
+	nvt_enable_cir(nvt);
+	spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+
+	return 0;
+}
+
+static void nvt_close(struct rc_dev *dev)
+{
+	struct nvt_dev *nvt = dev->priv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&nvt->nvt_lock, flags);
+	nvt_disable_cir(nvt);
+	spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+}
+
+/* Allocate memory, probe hardware, and initialize everything */
+static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
+{
+	struct nvt_dev *nvt;
+	struct rc_dev *rdev;
+	int ret = -ENOMEM;
+
+	nvt = kzalloc(sizeof(struct nvt_dev), GFP_KERNEL);
+	if (!nvt)
+		return ret;
+
+	/* input device for IR remote (and tx) */
+	rdev = rc_allocate_device();
+	if (!rdev)
+		goto exit_free_dev_rdev;
+
+	ret = -ENODEV;
+	/* activate pnp device */
+	if (pnp_activate_dev(pdev) < 0) {
+		dev_err(&pdev->dev, "Could not activate PNP device!\n");
+		goto exit_free_dev_rdev;
+	}
+
+	/* validate pnp resources */
+	if (!pnp_port_valid(pdev, 0) ||
+	    pnp_port_len(pdev, 0) < CIR_IOREG_LENGTH) {
+		dev_err(&pdev->dev, "IR PNP Port not valid!\n");
+		goto exit_free_dev_rdev;
+	}
+
+	if (!pnp_irq_valid(pdev, 0)) {
+		dev_err(&pdev->dev, "PNP IRQ not valid!\n");
+		goto exit_free_dev_rdev;
+	}
+
+	if (!pnp_port_valid(pdev, 1) ||
+	    pnp_port_len(pdev, 1) < CIR_IOREG_LENGTH) {
+		dev_err(&pdev->dev, "Wake PNP Port not valid!\n");
+		goto exit_free_dev_rdev;
+	}
+
+	nvt->cir_addr = pnp_port_start(pdev, 0);
+	nvt->cir_irq  = pnp_irq(pdev, 0);
+
+	nvt->cir_wake_addr = pnp_port_start(pdev, 1);
+	/* irq is always shared between cir and cir wake */
+	nvt->cir_wake_irq  = nvt->cir_irq;
+
+	nvt->cr_efir = CR_EFIR;
+	nvt->cr_efdr = CR_EFDR;
+
+	spin_lock_init(&nvt->nvt_lock);
+	spin_lock_init(&nvt->tx.lock);
+
+	pnp_set_drvdata(pdev, nvt);
+	nvt->pdev = pdev;
+
+	init_waitqueue_head(&nvt->tx.queue);
+
+	ret = nvt_hw_detect(nvt);
+	if (ret)
+		goto exit_free_dev_rdev;
+
+	/* Initialize CIR & CIR Wake Logical Devices */
+	nvt_efm_enable(nvt);
+	nvt_cir_ldev_init(nvt);
+	nvt_cir_wake_ldev_init(nvt);
+	nvt_efm_disable(nvt);
+
+	/* Initialize CIR & CIR Wake Config Registers */
+	nvt_cir_regs_init(nvt);
+	nvt_cir_wake_regs_init(nvt);
+
+	/* Set up the rc device */
+	rdev->priv = nvt;
+	rdev->driver_type = RC_DRIVER_IR_RAW;
+	rdev->allowed_protocols = RC_BIT_ALL;
+	rdev->open = nvt_open;
+	rdev->close = nvt_close;
+	rdev->tx_ir = nvt_tx_ir;
+	rdev->s_tx_carrier = nvt_set_tx_carrier;
+	rdev->input_name = "Nuvoton w836x7hg Infrared Remote Transceiver";
+	rdev->input_phys = "nuvoton/cir0";
+	rdev->input_id.bustype = BUS_HOST;
+	rdev->input_id.vendor = PCI_VENDOR_ID_WINBOND2;
+	rdev->input_id.product = nvt->chip_major;
+	rdev->input_id.version = nvt->chip_minor;
+	rdev->dev.parent = &pdev->dev;
+	rdev->driver_name = NVT_DRIVER_NAME;
+	rdev->map_name = RC_MAP_RC6_MCE;
+	rdev->timeout = MS_TO_NS(100);
+	/* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */
+	rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD);
+#if 0
+	rdev->min_timeout = XYZ;
+	rdev->max_timeout = XYZ;
+	/* tx bits */
+	rdev->tx_resolution = XYZ;
+#endif
+	nvt->rdev = rdev;
+
+	ret = rc_register_device(rdev);
+	if (ret)
+		goto exit_free_dev_rdev;
+
+	ret = -EBUSY;
+	/* now claim resources */
+	if (!request_region(nvt->cir_addr,
+			    CIR_IOREG_LENGTH, NVT_DRIVER_NAME))
+		goto exit_unregister_device;
+
+	if (request_irq(nvt->cir_irq, nvt_cir_isr, IRQF_SHARED,
+			NVT_DRIVER_NAME, (void *)nvt))
+		goto exit_release_cir_addr;
+
+	if (!request_region(nvt->cir_wake_addr,
+			    CIR_IOREG_LENGTH, NVT_DRIVER_NAME))
+		goto exit_free_irq;
+
+	if (request_irq(nvt->cir_wake_irq, nvt_cir_wake_isr, IRQF_SHARED,
+			NVT_DRIVER_NAME, (void *)nvt))
+		goto exit_release_cir_wake_addr;
+
+	device_init_wakeup(&pdev->dev, true);
+
+	nvt_pr(KERN_NOTICE, "driver has been successfully loaded\n");
+	if (debug) {
+		cir_dump_regs(nvt);
+		cir_wake_dump_regs(nvt);
+	}
+
+	return 0;
+
+exit_release_cir_wake_addr:
+	release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH);
+exit_free_irq:
+	free_irq(nvt->cir_irq, nvt);
+exit_release_cir_addr:
+	release_region(nvt->cir_addr, CIR_IOREG_LENGTH);
+exit_unregister_device:
+	rc_unregister_device(rdev);
+	rdev = NULL;
+exit_free_dev_rdev:
+	rc_free_device(rdev);
+	kfree(nvt);
+
+	return ret;
+}
+
+static void nvt_remove(struct pnp_dev *pdev)
+{
+	struct nvt_dev *nvt = pnp_get_drvdata(pdev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&nvt->nvt_lock, flags);
+	/* disable CIR */
+	nvt_cir_reg_write(nvt, 0, CIR_IREN);
+	nvt_disable_cir(nvt);
+	/* enable CIR Wake (for IR power-on) */
+	nvt_enable_wake(nvt);
+	spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+
+	/* free resources */
+	free_irq(nvt->cir_irq, nvt);
+	free_irq(nvt->cir_wake_irq, nvt);
+	release_region(nvt->cir_addr, CIR_IOREG_LENGTH);
+	release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH);
+
+	rc_unregister_device(nvt->rdev);
+
+	kfree(nvt);
+}
+
+static int nvt_suspend(struct pnp_dev *pdev, pm_message_t state)
+{
+	struct nvt_dev *nvt = pnp_get_drvdata(pdev);
+	unsigned long flags;
+
+	nvt_dbg("%s called", __func__);
+
+	/* zero out misc state tracking */
+	spin_lock_irqsave(&nvt->nvt_lock, flags);
+	nvt->study_state = ST_STUDY_NONE;
+	nvt->wake_state = ST_WAKE_NONE;
+	spin_unlock_irqrestore(&nvt->nvt_lock, flags);
+
+	spin_lock_irqsave(&nvt->tx.lock, flags);
+	nvt->tx.tx_state = ST_TX_NONE;
+	spin_unlock_irqrestore(&nvt->tx.lock, flags);
+
+	/* disable all CIR interrupts */
+	nvt_cir_reg_write(nvt, 0, CIR_IREN);
+
+	nvt_efm_enable(nvt);
+
+	/* disable cir logical dev */
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
+	nvt_cr_write(nvt, LOGICAL_DEV_DISABLE, CR_LOGICAL_DEV_EN);
+
+	nvt_efm_disable(nvt);
+
+	/* make sure wake is enabled */
+	nvt_enable_wake(nvt);
+
+	return 0;
+}
+
+static int nvt_resume(struct pnp_dev *pdev)
+{
+	struct nvt_dev *nvt = pnp_get_drvdata(pdev);
+
+	nvt_dbg("%s called", __func__);
+
+	/* open interrupt */
+	nvt_set_cir_iren(nvt);
+
+	/* Enable CIR logical device */
+	nvt_efm_enable(nvt);
+	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
+	nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN);
+
+	nvt_efm_disable(nvt);
+
+	nvt_cir_regs_init(nvt);
+	nvt_cir_wake_regs_init(nvt);
+
+	return 0;
+}
+
+static void nvt_shutdown(struct pnp_dev *pdev)
+{
+	struct nvt_dev *nvt = pnp_get_drvdata(pdev);
+	nvt_enable_wake(nvt);
+}
+
+static const struct pnp_device_id nvt_ids[] = {
+	{ "WEC0530", 0 },   /* CIR */
+	{ "NTN0530", 0 },   /* CIR for new chip's pnp id*/
+	{ "", 0 },
+};
+
+static struct pnp_driver nvt_driver = {
+	.name		= NVT_DRIVER_NAME,
+	.id_table	= nvt_ids,
+	.flags		= PNP_DRIVER_RES_DO_NOT_CHANGE,
+	.probe		= nvt_probe,
+	.remove		= nvt_remove,
+	.suspend	= nvt_suspend,
+	.resume		= nvt_resume,
+	.shutdown	= nvt_shutdown,
+};
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debugging output");
+
+MODULE_DEVICE_TABLE(pnp, nvt_ids);
+MODULE_DESCRIPTION("Nuvoton W83667HG-A & W83677HG-I CIR driver");
+
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
+MODULE_LICENSE("GPL");
+
+module_pnp_driver(nvt_driver);
diff --git a/drivers/media/rc/nuvoton-cir.h b/drivers/media/rc/nuvoton-cir.h
new file mode 100644
index 0000000..e1cf23c
--- /dev/null
+++ b/drivers/media/rc/nuvoton-cir.h
@@ -0,0 +1,417 @@
+/*
+ * Driver for Nuvoton Technology Corporation w83667hg/w83677hg-i CIR
+ *
+ * Copyright (C) 2010 Jarod Wilson <jarod@redhat.com>
+ * Copyright (C) 2009 Nuvoton PS Team
+ *
+ * Special thanks to Nuvoton for providing hardware, spec sheets and
+ * sample code upon which portions of this driver are based. Indirect
+ * thanks also to Maxim Levitsky, whose ene_ir driver this driver is
+ * modeled after.
+ *
+ * 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/spinlock.h>
+#include <linux/ioctl.h>
+
+/* platform driver name to register */
+#define NVT_DRIVER_NAME "nuvoton-cir"
+
+/* debugging module parameter */
+static int debug;
+
+
+#define nvt_pr(level, text, ...) \
+	printk(level KBUILD_MODNAME ": " text, ## __VA_ARGS__)
+
+#define nvt_dbg(text, ...) \
+	if (debug) \
+		printk(KERN_DEBUG \
+			KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__)
+
+#define nvt_dbg_verbose(text, ...) \
+	if (debug > 1) \
+		printk(KERN_DEBUG \
+			KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__)
+
+#define nvt_dbg_wake(text, ...) \
+	if (debug > 2) \
+		printk(KERN_DEBUG \
+			KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__)
+
+
+/*
+ * Original lirc driver said min value of 76, and recommended value of 256
+ * for the buffer length, but then used 2048. Never mind that the size of the
+ * RX FIFO is 32 bytes... So I'm using 32 for RX and 256 for TX atm, but I'm
+ * not sure if maybe that TX value is off by a factor of 8 (bits vs. bytes),
+ * and I don't have TX-capable hardware to test/debug on...
+ */
+#define TX_BUF_LEN 256
+#define RX_BUF_LEN 32
+
+struct nvt_dev {
+	struct pnp_dev *pdev;
+	struct rc_dev *rdev;
+
+	spinlock_t nvt_lock;
+
+	/* for rx */
+	u8 buf[RX_BUF_LEN];
+	unsigned int pkts;
+
+	struct {
+		spinlock_t lock;
+		u8 buf[TX_BUF_LEN];
+		unsigned int buf_count;
+		unsigned int cur_buf_num;
+		wait_queue_head_t queue;
+		u8 tx_state;
+	} tx;
+
+	/* EFER Config register index/data pair */
+	u32 cr_efir;
+	u32 cr_efdr;
+
+	/* hardware I/O settings */
+	unsigned long cir_addr;
+	unsigned long cir_wake_addr;
+	int cir_irq;
+	int cir_wake_irq;
+
+	/* hardware id */
+	u8 chip_major;
+	u8 chip_minor;
+
+	/* hardware features */
+	bool hw_learning_capable;
+	bool hw_tx_capable;
+
+	/* rx settings */
+	bool learning_enabled;
+
+	/* track cir wake state */
+	u8 wake_state;
+	/* for study */
+	u8 study_state;
+	/* carrier period = 1 / frequency */
+	u32 carrier;
+};
+
+/* study states */
+#define ST_STUDY_NONE      0x0
+#define ST_STUDY_START     0x1
+#define ST_STUDY_CARRIER   0x2
+#define ST_STUDY_ALL_RECV  0x4
+
+/* wake states */
+#define ST_WAKE_NONE	0x0
+#define ST_WAKE_START	0x1
+#define ST_WAKE_FINISH	0x2
+
+/* receive states */
+#define ST_RX_WAIT_7F		0x1
+#define ST_RX_WAIT_HEAD		0x2
+#define ST_RX_WAIT_SILENT_END	0x4
+
+/* send states */
+#define ST_TX_NONE	0x0
+#define ST_TX_REQUEST	0x2
+#define ST_TX_REPLY	0x4
+
+/* buffer packet constants */
+#define BUF_PULSE_BIT	0x80
+#define BUF_LEN_MASK	0x7f
+#define BUF_REPEAT_BYTE	0x70
+#define BUF_REPEAT_MASK	0xf0
+
+/* CIR settings */
+
+/* total length of CIR and CIR WAKE */
+#define CIR_IOREG_LENGTH	0x0f
+
+/* RX limit length, 8 high bits for SLCH, 8 low bits for SLCL (0x7d0 = 2000) */
+#define CIR_RX_LIMIT_COUNT	0x7d0
+
+/* CIR Regs */
+#define CIR_IRCON	0x00
+#define CIR_IRSTS	0x01
+#define CIR_IREN	0x02
+#define CIR_RXFCONT	0x03
+#define CIR_CP		0x04
+#define CIR_CC		0x05
+#define CIR_SLCH	0x06
+#define CIR_SLCL	0x07
+#define CIR_FIFOCON	0x08
+#define CIR_IRFIFOSTS	0x09
+#define CIR_SRXFIFO	0x0a
+#define CIR_TXFCONT	0x0b
+#define CIR_STXFIFO	0x0c
+#define CIR_FCCH	0x0d
+#define CIR_FCCL	0x0e
+#define CIR_IRFSM	0x0f
+
+/* CIR IRCON settings */
+#define CIR_IRCON_RECV	 0x80
+#define CIR_IRCON_WIREN	 0x40
+#define CIR_IRCON_TXEN	 0x20
+#define CIR_IRCON_RXEN	 0x10
+#define CIR_IRCON_WRXINV 0x08
+#define CIR_IRCON_RXINV	 0x04
+
+#define CIR_IRCON_SAMPLE_PERIOD_SEL_1	0x00
+#define CIR_IRCON_SAMPLE_PERIOD_SEL_25	0x01
+#define CIR_IRCON_SAMPLE_PERIOD_SEL_50	0x02
+#define CIR_IRCON_SAMPLE_PERIOD_SEL_100	0x03
+
+/* FIXME: make this a runtime option */
+/* select sample period as 50us */
+#define CIR_IRCON_SAMPLE_PERIOD_SEL	CIR_IRCON_SAMPLE_PERIOD_SEL_50
+
+/* CIR IRSTS settings */
+#define CIR_IRSTS_RDR	0x80
+#define CIR_IRSTS_RTR	0x40
+#define CIR_IRSTS_PE	0x20
+#define CIR_IRSTS_RFO	0x10
+#define CIR_IRSTS_TE	0x08
+#define CIR_IRSTS_TTR	0x04
+#define CIR_IRSTS_TFU	0x02
+#define CIR_IRSTS_GH	0x01
+
+/* CIR IREN settings */
+#define CIR_IREN_RDR	0x80
+#define CIR_IREN_RTR	0x40
+#define CIR_IREN_PE	0x20
+#define CIR_IREN_RFO	0x10
+#define CIR_IREN_TE	0x08
+#define CIR_IREN_TTR	0x04
+#define CIR_IREN_TFU	0x02
+#define CIR_IREN_GH	0x01
+
+/* CIR FIFOCON settings */
+#define CIR_FIFOCON_TXFIFOCLR		0x80
+
+#define CIR_FIFOCON_TX_TRIGGER_LEV_31	0x00
+#define CIR_FIFOCON_TX_TRIGGER_LEV_24	0x10
+#define CIR_FIFOCON_TX_TRIGGER_LEV_16	0x20
+#define CIR_FIFOCON_TX_TRIGGER_LEV_8	0x30
+
+/* FIXME: make this a runtime option */
+/* select TX trigger level as 16 */
+#define CIR_FIFOCON_TX_TRIGGER_LEV	CIR_FIFOCON_TX_TRIGGER_LEV_16
+
+#define CIR_FIFOCON_RXFIFOCLR		0x08
+
+#define CIR_FIFOCON_RX_TRIGGER_LEV_1	0x00
+#define CIR_FIFOCON_RX_TRIGGER_LEV_8	0x01
+#define CIR_FIFOCON_RX_TRIGGER_LEV_16	0x02
+#define CIR_FIFOCON_RX_TRIGGER_LEV_24	0x03
+
+/* FIXME: make this a runtime option */
+/* select RX trigger level as 24 */
+#define CIR_FIFOCON_RX_TRIGGER_LEV	CIR_FIFOCON_RX_TRIGGER_LEV_24
+
+/* CIR IRFIFOSTS settings */
+#define CIR_IRFIFOSTS_IR_PENDING	0x80
+#define CIR_IRFIFOSTS_RX_GS		0x40
+#define CIR_IRFIFOSTS_RX_FTA		0x20
+#define CIR_IRFIFOSTS_RX_EMPTY		0x10
+#define CIR_IRFIFOSTS_RX_FULL		0x08
+#define CIR_IRFIFOSTS_TX_FTA		0x04
+#define CIR_IRFIFOSTS_TX_EMPTY		0x02
+#define CIR_IRFIFOSTS_TX_FULL		0x01
+
+
+/* CIR WAKE UP Regs */
+#define CIR_WAKE_IRCON			0x00
+#define CIR_WAKE_IRSTS			0x01
+#define CIR_WAKE_IREN			0x02
+#define CIR_WAKE_FIFO_CMP_DEEP		0x03
+#define CIR_WAKE_FIFO_CMP_TOL		0x04
+#define CIR_WAKE_FIFO_COUNT		0x05
+#define CIR_WAKE_SLCH			0x06
+#define CIR_WAKE_SLCL			0x07
+#define CIR_WAKE_FIFOCON		0x08
+#define CIR_WAKE_SRXFSTS		0x09
+#define CIR_WAKE_SAMPLE_RX_FIFO		0x0a
+#define CIR_WAKE_WR_FIFO_DATA		0x0b
+#define CIR_WAKE_RD_FIFO_ONLY		0x0c
+#define CIR_WAKE_RD_FIFO_ONLY_IDX	0x0d
+#define CIR_WAKE_FIFO_IGNORE		0x0e
+#define CIR_WAKE_IRFSM			0x0f
+
+/* CIR WAKE UP IRCON settings */
+#define CIR_WAKE_IRCON_DEC_RST		0x80
+#define CIR_WAKE_IRCON_MODE1		0x40
+#define CIR_WAKE_IRCON_MODE0		0x20
+#define CIR_WAKE_IRCON_RXEN		0x10
+#define CIR_WAKE_IRCON_R		0x08
+#define CIR_WAKE_IRCON_RXINV		0x04
+
+/* FIXME/jarod: make this a runtime option */
+/* select a same sample period like cir register */
+#define CIR_WAKE_IRCON_SAMPLE_PERIOD_SEL	CIR_IRCON_SAMPLE_PERIOD_SEL_50
+
+/* CIR WAKE IRSTS Bits */
+#define CIR_WAKE_IRSTS_RDR		0x80
+#define CIR_WAKE_IRSTS_RTR		0x40
+#define CIR_WAKE_IRSTS_PE		0x20
+#define CIR_WAKE_IRSTS_RFO		0x10
+#define CIR_WAKE_IRSTS_GH		0x08
+#define CIR_WAKE_IRSTS_IR_PENDING	0x01
+
+/* CIR WAKE UP IREN Bits */
+#define CIR_WAKE_IREN_RDR		0x80
+#define CIR_WAKE_IREN_RTR		0x40
+#define CIR_WAKE_IREN_PE		0x20
+#define CIR_WAKE_IREN_RFO		0x10
+#define CIR_WAKE_IREN_TE		0x08
+#define CIR_WAKE_IREN_TTR		0x04
+#define CIR_WAKE_IREN_TFU		0x02
+#define CIR_WAKE_IREN_GH		0x01
+
+/* CIR WAKE FIFOCON settings */
+#define CIR_WAKE_FIFOCON_RXFIFOCLR	0x08
+
+#define CIR_WAKE_FIFOCON_RX_TRIGGER_LEV_67	0x00
+#define CIR_WAKE_FIFOCON_RX_TRIGGER_LEV_66	0x01
+#define CIR_WAKE_FIFOCON_RX_TRIGGER_LEV_65	0x02
+#define CIR_WAKE_FIFOCON_RX_TRIGGER_LEV_64	0x03
+
+/* FIXME: make this a runtime option */
+/* select WAKE UP RX trigger level as 67 */
+#define CIR_WAKE_FIFOCON_RX_TRIGGER_LEV	CIR_WAKE_FIFOCON_RX_TRIGGER_LEV_67
+
+/* CIR WAKE SRXFSTS settings */
+#define CIR_WAKE_IRFIFOSTS_RX_GS	0x80
+#define CIR_WAKE_IRFIFOSTS_RX_FTA	0x40
+#define CIR_WAKE_IRFIFOSTS_RX_EMPTY	0x20
+#define CIR_WAKE_IRFIFOSTS_RX_FULL	0x10
+
+/*
+ * The CIR Wake FIFO buffer is 67 bytes long, but the stock remote wakes
+ * the system comparing only 65 bytes (fails with this set to 67)
+ */
+#define CIR_WAKE_FIFO_CMP_BYTES		65
+/* CIR Wake byte comparison tolerance */
+#define CIR_WAKE_CMP_TOLERANCE		5
+
+/*
+ * Extended Function Enable Registers:
+ *  Extended Function Index Register
+ *  Extended Function Data Register
+ */
+#define CR_EFIR			0x2e
+#define CR_EFDR			0x2f
+
+/* Possible alternate EFER values, depends on how the chip is wired */
+#define CR_EFIR2		0x4e
+#define CR_EFDR2		0x4f
+
+/* Extended Function Mode enable/disable magic values */
+#define EFER_EFM_ENABLE		0x87
+#define EFER_EFM_DISABLE	0xaa
+
+/* Chip IDs found in CR_CHIP_ID_{HI,LO} */
+#define CHIP_ID_HIGH_667	0xa5
+#define CHIP_ID_HIGH_677B	0xb4
+#define CHIP_ID_HIGH_677C	0xc3
+#define CHIP_ID_LOW_667		0x13
+#define CHIP_ID_LOW_677B2	0x72
+#define CHIP_ID_LOW_677B3	0x73
+#define CHIP_ID_LOW_677C	0x33
+
+/* Config regs we need to care about */
+#define CR_SOFTWARE_RESET	0x02
+#define CR_LOGICAL_DEV_SEL	0x07
+#define CR_CHIP_ID_HI		0x20
+#define CR_CHIP_ID_LO		0x21
+#define CR_DEV_POWER_DOWN	0x22 /* bit 2 is CIR power, default power on */
+#define CR_OUTPUT_PIN_SEL	0x27
+#define CR_MULTIFUNC_PIN_SEL	0x2c
+#define CR_LOGICAL_DEV_EN	0x30 /* valid for all logical devices */
+/* next three regs valid for both the CIR and CIR_WAKE logical devices */
+#define CR_CIR_BASE_ADDR_HI	0x60
+#define CR_CIR_BASE_ADDR_LO	0x61
+#define CR_CIR_IRQ_RSRC		0x70
+/* next three regs valid only for ACPI logical dev */
+#define CR_ACPI_CIR_WAKE	0xe0
+#define CR_ACPI_IRQ_EVENTS	0xf6
+#define CR_ACPI_IRQ_EVENTS2	0xf7
+
+/* Logical devices that we need to care about */
+#define LOGICAL_DEV_LPT		0x01
+#define LOGICAL_DEV_CIR		0x06
+#define LOGICAL_DEV_ACPI	0x0a
+#define LOGICAL_DEV_CIR_WAKE	0x0e
+
+#define LOGICAL_DEV_DISABLE	0x00
+#define LOGICAL_DEV_ENABLE	0x01
+
+#define CIR_WAKE_ENABLE_BIT	0x08
+#define PME_INTR_CIR_PASS_BIT	0x08
+
+/* w83677hg CIR pin config */
+#define OUTPUT_PIN_SEL_MASK	0xbc
+#define OUTPUT_ENABLE_CIR	0x01 /* Pin95=CIRRX, Pin96=CIRTX1 */
+#define OUTPUT_ENABLE_CIRWB	0x40 /* enable wide-band sensor */
+
+/* w83667hg CIR pin config */
+#define MULTIFUNC_PIN_SEL_MASK	0x1f
+#define MULTIFUNC_ENABLE_CIR	0x80 /* Pin75=CIRRX, Pin76=CIRTX1 */
+#define MULTIFUNC_ENABLE_CIRWB	0x20 /* enable wide-band sensor */
+
+/* MCE CIR signal length, related on sample period */
+
+/* MCE CIR controller signal length: about 43ms
+ * 43ms / 50us (sample period) * 0.85 (inaccuracy)
+ */
+#define CONTROLLER_BUF_LEN_MIN 830
+
+/* MCE CIR keyboard signal length: about 26ms
+ * 26ms / 50us (sample period) * 0.85 (inaccuracy)
+ */
+#define KEYBOARD_BUF_LEN_MAX 650
+#define KEYBOARD_BUF_LEN_MIN 610
+
+/* MCE CIR mouse signal length: about 24ms
+ * 24ms / 50us (sample period) * 0.85 (inaccuracy)
+ */
+#define MOUSE_BUF_LEN_MIN 565
+
+#define CIR_SAMPLE_PERIOD 50
+#define CIR_SAMPLE_LOW_INACCURACY 0.85
+
+/* MAX silence time that driver will sent to lirc */
+#define MAX_SILENCE_TIME 60000
+
+#if CIR_IRCON_SAMPLE_PERIOD_SEL == CIR_IRCON_SAMPLE_PERIOD_SEL_100
+#define SAMPLE_PERIOD 100
+
+#elif CIR_IRCON_SAMPLE_PERIOD_SEL == CIR_IRCON_SAMPLE_PERIOD_SEL_50
+#define SAMPLE_PERIOD 50
+
+#elif CIR_IRCON_SAMPLE_PERIOD_SEL == CIR_IRCON_SAMPLE_PERIOD_SEL_25
+#define SAMPLE_PERIOD 25
+
+#else
+#define SAMPLE_PERIOD 1
+#endif
+
+/* as VISTA MCE definition, valid carrier value */
+#define MAX_CARRIER 60000
+#define MIN_CARRIER 30000
diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
new file mode 100644
index 0000000..b68d4f7
--- /dev/null
+++ b/drivers/media/rc/rc-core-priv.h
@@ -0,0 +1,241 @@
+/*
+ * Remote Controller core raw events header
+ *
+ * Copyright (C) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation version 2 of the License.
+ *
+ *  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.
+ */
+
+#ifndef _RC_CORE_PRIV
+#define _RC_CORE_PRIV
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <media/rc-core.h>
+
+struct ir_raw_handler {
+	struct list_head list;
+
+	u64 protocols; /* which are handled by this handler */
+	int (*decode)(struct rc_dev *dev, struct ir_raw_event event);
+
+	/* These two should only be used by the lirc decoder */
+	int (*raw_register)(struct rc_dev *dev);
+	int (*raw_unregister)(struct rc_dev *dev);
+};
+
+struct ir_raw_event_ctrl {
+	struct list_head		list;		/* to keep track of raw clients */
+	struct task_struct		*thread;
+	spinlock_t			lock;
+	struct kfifo_rec_ptr_1		kfifo;		/* fifo for the pulse/space durations */
+	ktime_t				last_event;	/* when last event occurred */
+	enum raw_event_type		last_type;	/* last event type */
+	struct rc_dev			*dev;		/* pointer to the parent rc_dev */
+
+	/* raw decoder state follows */
+	struct ir_raw_event prev_ev;
+	struct ir_raw_event this_ev;
+	struct nec_dec {
+		int state;
+		unsigned count;
+		u32 bits;
+		bool is_nec_x;
+		bool necx_repeat;
+	} nec;
+	struct rc5_dec {
+		int state;
+		u32 bits;
+		unsigned count;
+		bool is_rc5x;
+	} rc5;
+	struct rc6_dec {
+		int state;
+		u8 header;
+		u32 body;
+		bool toggle;
+		unsigned count;
+		unsigned wanted_bits;
+	} rc6;
+	struct sony_dec {
+		int state;
+		u32 bits;
+		unsigned count;
+	} sony;
+	struct jvc_dec {
+		int state;
+		u16 bits;
+		u16 old_bits;
+		unsigned count;
+		bool first;
+		bool toggle;
+	} jvc;
+	struct sanyo_dec {
+		int state;
+		unsigned count;
+		u64 bits;
+	} sanyo;
+	struct sharp_dec {
+		int state;
+		unsigned count;
+		u32 bits;
+		unsigned int pulse_len;
+	} sharp;
+	struct mce_kbd_dec {
+		struct input_dev *idev;
+		struct timer_list rx_timeout;
+		char name[64];
+		char phys[64];
+		int state;
+		u8 header;
+		u32 body;
+		unsigned count;
+		unsigned wanted_bits;
+	} mce_kbd;
+	struct lirc_codec {
+		struct rc_dev *dev;
+		struct lirc_driver *drv;
+		int carrier_low;
+
+		ktime_t gap_start;
+		u64 gap_duration;
+		bool gap;
+		bool send_timeout_reports;
+
+	} lirc;
+	struct xmp_dec {
+		int state;
+		unsigned count;
+		u32 durations[16];
+	} xmp;
+};
+
+/* macros for IR decoders */
+static inline bool geq_margin(unsigned d1, unsigned d2, unsigned margin)
+{
+	return d1 > (d2 - margin);
+}
+
+static inline bool eq_margin(unsigned d1, unsigned d2, unsigned margin)
+{
+	return ((d1 > (d2 - margin)) && (d1 < (d2 + margin)));
+}
+
+static inline bool is_transition(struct ir_raw_event *x, struct ir_raw_event *y)
+{
+	return x->pulse != y->pulse;
+}
+
+static inline void decrease_duration(struct ir_raw_event *ev, unsigned duration)
+{
+	if (duration > ev->duration)
+		ev->duration = 0;
+	else
+		ev->duration -= duration;
+}
+
+/* Returns true if event is normal pulse/space event */
+static inline bool is_timing_event(struct ir_raw_event ev)
+{
+	return !ev.carrier_report && !ev.reset;
+}
+
+#define TO_US(duration)			DIV_ROUND_CLOSEST((duration), 1000)
+#define TO_STR(is_pulse)		((is_pulse) ? "pulse" : "space")
+
+/*
+ * Routines from rc-raw.c to be used internally and by decoders
+ */
+u64 ir_raw_get_allowed_protocols(void);
+int ir_raw_event_register(struct rc_dev *dev);
+void ir_raw_event_unregister(struct rc_dev *dev);
+int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler);
+void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler);
+void ir_raw_init(void);
+
+/*
+ * Decoder initialization code
+ *
+ * Those load logic are called during ir-core init, and automatically
+ * loads the compiled decoders for their usage with IR raw events
+ */
+
+/* from ir-nec-decoder.c */
+#ifdef CONFIG_IR_NEC_DECODER_MODULE
+#define load_nec_decode()	request_module_nowait("ir-nec-decoder")
+#else
+static inline void load_nec_decode(void) { }
+#endif
+
+/* from ir-rc5-decoder.c */
+#ifdef CONFIG_IR_RC5_DECODER_MODULE
+#define load_rc5_decode()	request_module_nowait("ir-rc5-decoder")
+#else
+static inline void load_rc5_decode(void) { }
+#endif
+
+/* from ir-rc6-decoder.c */
+#ifdef CONFIG_IR_RC6_DECODER_MODULE
+#define load_rc6_decode()	request_module_nowait("ir-rc6-decoder")
+#else
+static inline void load_rc6_decode(void) { }
+#endif
+
+/* from ir-jvc-decoder.c */
+#ifdef CONFIG_IR_JVC_DECODER_MODULE
+#define load_jvc_decode()	request_module_nowait("ir-jvc-decoder")
+#else
+static inline void load_jvc_decode(void) { }
+#endif
+
+/* from ir-sony-decoder.c */
+#ifdef CONFIG_IR_SONY_DECODER_MODULE
+#define load_sony_decode()	request_module_nowait("ir-sony-decoder")
+#else
+static inline void load_sony_decode(void) { }
+#endif
+
+/* from ir-sanyo-decoder.c */
+#ifdef CONFIG_IR_SANYO_DECODER_MODULE
+#define load_sanyo_decode()	request_module_nowait("ir-sanyo-decoder")
+#else
+static inline void load_sanyo_decode(void) { }
+#endif
+
+/* from ir-sharp-decoder.c */
+#ifdef CONFIG_IR_SHARP_DECODER_MODULE
+#define load_sharp_decode()	request_module_nowait("ir-sharp-decoder")
+#else
+static inline void load_sharp_decode(void) { }
+#endif
+
+/* from ir-mce_kbd-decoder.c */
+#ifdef CONFIG_IR_MCE_KBD_DECODER_MODULE
+#define load_mce_kbd_decode()	request_module_nowait("ir-mce_kbd-decoder")
+#else
+static inline void load_mce_kbd_decode(void) { }
+#endif
+
+/* from ir-lirc-codec.c */
+#ifdef CONFIG_IR_LIRC_CODEC_MODULE
+#define load_lirc_codec()	request_module_nowait("ir-lirc-codec")
+#else
+static inline void load_lirc_codec(void) { }
+#endif
+
+/* from ir-xmp-decoder.c */
+#ifdef CONFIG_IR_XMP_DECODER_MODULE
+#define load_xmp_decode()      request_module_nowait("ir-xmp-decoder")
+#else
+static inline void load_xmp_decode(void) { }
+#endif
+
+
+#endif /* _RC_CORE_PRIV */
diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c
new file mode 100644
index 0000000..ad26052
--- /dev/null
+++ b/drivers/media/rc/rc-ir-raw.c
@@ -0,0 +1,369 @@
+/* rc-ir-raw.c - handle IR pulse/space events
+ *
+ * Copyright (C) 2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation version 2 of the License.
+ *
+ *  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.
+ */
+
+#include <linux/export.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/kmod.h>
+#include <linux/sched.h>
+#include <linux/freezer.h>
+#include "rc-core-priv.h"
+
+/* Define the max number of pulse/space transitions to buffer */
+#define MAX_IR_EVENT_SIZE      512
+
+/* Used to keep track of IR raw clients, protected by ir_raw_handler_lock */
+static LIST_HEAD(ir_raw_client_list);
+
+/* Used to handle IR raw handler extensions */
+static DEFINE_MUTEX(ir_raw_handler_lock);
+static LIST_HEAD(ir_raw_handler_list);
+static u64 available_protocols;
+
+static int ir_raw_event_thread(void *data)
+{
+	struct ir_raw_event ev;
+	struct ir_raw_handler *handler;
+	struct ir_raw_event_ctrl *raw = (struct ir_raw_event_ctrl *)data;
+	int retval;
+
+	while (!kthread_should_stop()) {
+
+		spin_lock_irq(&raw->lock);
+		retval = kfifo_len(&raw->kfifo);
+
+		if (retval < sizeof(ev)) {
+			set_current_state(TASK_INTERRUPTIBLE);
+
+			if (kthread_should_stop())
+				set_current_state(TASK_RUNNING);
+
+			spin_unlock_irq(&raw->lock);
+			schedule();
+			continue;
+		}
+
+		retval = kfifo_out(&raw->kfifo, &ev, sizeof(ev));
+		spin_unlock_irq(&raw->lock);
+
+		mutex_lock(&ir_raw_handler_lock);
+		list_for_each_entry(handler, &ir_raw_handler_list, list)
+			handler->decode(raw->dev, ev);
+		raw->prev_ev = ev;
+		mutex_unlock(&ir_raw_handler_lock);
+	}
+
+	return 0;
+}
+
+/**
+ * ir_raw_event_store() - pass a pulse/space duration to the raw ir decoders
+ * @dev:	the struct rc_dev device descriptor
+ * @ev:		the struct ir_raw_event descriptor of the pulse/space
+ *
+ * This routine (which may be called from an interrupt context) stores a
+ * pulse/space duration for the raw ir decoding state machines. Pulses are
+ * signalled as positive values and spaces as negative values. A zero value
+ * will reset the decoding state machines.
+ */
+int ir_raw_event_store(struct rc_dev *dev, struct ir_raw_event *ev)
+{
+	if (!dev->raw)
+		return -EINVAL;
+
+	IR_dprintk(2, "sample: (%05dus %s)\n",
+		   TO_US(ev->duration), TO_STR(ev->pulse));
+
+	if (kfifo_in(&dev->raw->kfifo, ev, sizeof(*ev)) != sizeof(*ev))
+		return -ENOMEM;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ir_raw_event_store);
+
+/**
+ * ir_raw_event_store_edge() - notify raw ir decoders of the start of a pulse/space
+ * @dev:	the struct rc_dev device descriptor
+ * @type:	the type of the event that has occurred
+ *
+ * This routine (which may be called from an interrupt context) is used to
+ * store the beginning of an ir pulse or space (or the start/end of ir
+ * reception) for the raw ir decoding state machines. This is used by
+ * hardware which does not provide durations directly but only interrupts
+ * (or similar events) on state change.
+ */
+int ir_raw_event_store_edge(struct rc_dev *dev, enum raw_event_type type)
+{
+	ktime_t			now;
+	s64			delta; /* ns */
+	DEFINE_IR_RAW_EVENT(ev);
+	int			rc = 0;
+	int			delay;
+
+	if (!dev->raw)
+		return -EINVAL;
+
+	now = ktime_get();
+	delta = ktime_to_ns(ktime_sub(now, dev->raw->last_event));
+	delay = MS_TO_NS(dev->input_dev->rep[REP_DELAY]);
+
+	/* Check for a long duration since last event or if we're
+	 * being called for the first time, note that delta can't
+	 * possibly be negative.
+	 */
+	if (delta > delay || !dev->raw->last_type)
+		type |= IR_START_EVENT;
+	else
+		ev.duration = delta;
+
+	if (type & IR_START_EVENT)
+		ir_raw_event_reset(dev);
+	else if (dev->raw->last_type & IR_SPACE) {
+		ev.pulse = false;
+		rc = ir_raw_event_store(dev, &ev);
+	} else if (dev->raw->last_type & IR_PULSE) {
+		ev.pulse = true;
+		rc = ir_raw_event_store(dev, &ev);
+	} else
+		return 0;
+
+	dev->raw->last_event = now;
+	dev->raw->last_type = type;
+	return rc;
+}
+EXPORT_SYMBOL_GPL(ir_raw_event_store_edge);
+
+/**
+ * ir_raw_event_store_with_filter() - pass next pulse/space to decoders with some processing
+ * @dev:	the struct rc_dev device descriptor
+ * @type:	the type of the event that has occurred
+ *
+ * This routine (which may be called from an interrupt context) works
+ * in similar manner to ir_raw_event_store_edge.
+ * This routine is intended for devices with limited internal buffer
+ * It automerges samples of same type, and handles timeouts. Returns non-zero
+ * if the event was added, and zero if the event was ignored due to idle
+ * processing.
+ */
+int ir_raw_event_store_with_filter(struct rc_dev *dev, struct ir_raw_event *ev)
+{
+	if (!dev->raw)
+		return -EINVAL;
+
+	/* Ignore spaces in idle mode */
+	if (dev->idle && !ev->pulse)
+		return 0;
+	else if (dev->idle)
+		ir_raw_event_set_idle(dev, false);
+
+	if (!dev->raw->this_ev.duration)
+		dev->raw->this_ev = *ev;
+	else if (ev->pulse == dev->raw->this_ev.pulse)
+		dev->raw->this_ev.duration += ev->duration;
+	else {
+		ir_raw_event_store(dev, &dev->raw->this_ev);
+		dev->raw->this_ev = *ev;
+	}
+
+	/* Enter idle mode if nessesary */
+	if (!ev->pulse && dev->timeout &&
+	    dev->raw->this_ev.duration >= dev->timeout)
+		ir_raw_event_set_idle(dev, true);
+
+	return 1;
+}
+EXPORT_SYMBOL_GPL(ir_raw_event_store_with_filter);
+
+/**
+ * ir_raw_event_set_idle() - provide hint to rc-core when the device is idle or not
+ * @dev:	the struct rc_dev device descriptor
+ * @idle:	whether the device is idle or not
+ */
+void ir_raw_event_set_idle(struct rc_dev *dev, bool idle)
+{
+	if (!dev->raw)
+		return;
+
+	IR_dprintk(2, "%s idle mode\n", idle ? "enter" : "leave");
+
+	if (idle) {
+		dev->raw->this_ev.timeout = true;
+		ir_raw_event_store(dev, &dev->raw->this_ev);
+		init_ir_raw_event(&dev->raw->this_ev);
+	}
+
+	if (dev->s_idle)
+		dev->s_idle(dev, idle);
+
+	dev->idle = idle;
+}
+EXPORT_SYMBOL_GPL(ir_raw_event_set_idle);
+
+/**
+ * ir_raw_event_handle() - schedules the decoding of stored ir data
+ * @dev:	the struct rc_dev device descriptor
+ *
+ * This routine will tell rc-core to start decoding stored ir data.
+ */
+void ir_raw_event_handle(struct rc_dev *dev)
+{
+	unsigned long flags;
+
+	if (!dev->raw)
+		return;
+
+	spin_lock_irqsave(&dev->raw->lock, flags);
+	wake_up_process(dev->raw->thread);
+	spin_unlock_irqrestore(&dev->raw->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ir_raw_event_handle);
+
+/* used internally by the sysfs interface */
+u64
+ir_raw_get_allowed_protocols(void)
+{
+	u64 protocols;
+	mutex_lock(&ir_raw_handler_lock);
+	protocols = available_protocols;
+	mutex_unlock(&ir_raw_handler_lock);
+	return protocols;
+}
+
+static int change_protocol(struct rc_dev *dev, u64 *rc_type)
+{
+	/* the caller will update dev->enabled_protocols */
+	return 0;
+}
+
+/*
+ * Used to (un)register raw event clients
+ */
+int ir_raw_event_register(struct rc_dev *dev)
+{
+	int rc;
+	struct ir_raw_handler *handler;
+
+	if (!dev)
+		return -EINVAL;
+
+	dev->raw = kzalloc(sizeof(*dev->raw), GFP_KERNEL);
+	if (!dev->raw)
+		return -ENOMEM;
+
+	dev->raw->dev = dev;
+	dev->change_protocol = change_protocol;
+	rc = kfifo_alloc(&dev->raw->kfifo,
+			 sizeof(struct ir_raw_event) * MAX_IR_EVENT_SIZE,
+			 GFP_KERNEL);
+	if (rc < 0)
+		goto out;
+
+	spin_lock_init(&dev->raw->lock);
+	dev->raw->thread = kthread_run(ir_raw_event_thread, dev->raw,
+				       "rc%u", dev->minor);
+
+	if (IS_ERR(dev->raw->thread)) {
+		rc = PTR_ERR(dev->raw->thread);
+		goto out;
+	}
+
+	mutex_lock(&ir_raw_handler_lock);
+	list_add_tail(&dev->raw->list, &ir_raw_client_list);
+	list_for_each_entry(handler, &ir_raw_handler_list, list)
+		if (handler->raw_register)
+			handler->raw_register(dev);
+	mutex_unlock(&ir_raw_handler_lock);
+
+	return 0;
+
+out:
+	kfree(dev->raw);
+	dev->raw = NULL;
+	return rc;
+}
+
+void ir_raw_event_unregister(struct rc_dev *dev)
+{
+	struct ir_raw_handler *handler;
+
+	if (!dev || !dev->raw)
+		return;
+
+	kthread_stop(dev->raw->thread);
+
+	mutex_lock(&ir_raw_handler_lock);
+	list_del(&dev->raw->list);
+	list_for_each_entry(handler, &ir_raw_handler_list, list)
+		if (handler->raw_unregister)
+			handler->raw_unregister(dev);
+	mutex_unlock(&ir_raw_handler_lock);
+
+	kfifo_free(&dev->raw->kfifo);
+	kfree(dev->raw);
+	dev->raw = NULL;
+}
+
+/*
+ * Extension interface - used to register the IR decoders
+ */
+
+int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler)
+{
+	struct ir_raw_event_ctrl *raw;
+
+	mutex_lock(&ir_raw_handler_lock);
+	list_add_tail(&ir_raw_handler->list, &ir_raw_handler_list);
+	if (ir_raw_handler->raw_register)
+		list_for_each_entry(raw, &ir_raw_client_list, list)
+			ir_raw_handler->raw_register(raw->dev);
+	available_protocols |= ir_raw_handler->protocols;
+	mutex_unlock(&ir_raw_handler_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(ir_raw_handler_register);
+
+void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler)
+{
+	struct ir_raw_event_ctrl *raw;
+
+	mutex_lock(&ir_raw_handler_lock);
+	list_del(&ir_raw_handler->list);
+	if (ir_raw_handler->raw_unregister)
+		list_for_each_entry(raw, &ir_raw_client_list, list)
+			ir_raw_handler->raw_unregister(raw->dev);
+	available_protocols &= ~ir_raw_handler->protocols;
+	mutex_unlock(&ir_raw_handler_lock);
+}
+EXPORT_SYMBOL(ir_raw_handler_unregister);
+
+void ir_raw_init(void)
+{
+	/* Load the decoder modules */
+
+	load_nec_decode();
+	load_rc5_decode();
+	load_rc6_decode();
+	load_jvc_decode();
+	load_sony_decode();
+	load_sanyo_decode();
+	load_sharp_decode();
+	load_mce_kbd_decode();
+	load_lirc_codec();
+	load_xmp_decode();
+
+	/* If needed, we may later add some init code. In this case,
+	   it is needed to change the CONFIG_MODULE test at rc-core.h
+	 */
+}
diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c
new file mode 100644
index 0000000..63dace8
--- /dev/null
+++ b/drivers/media/rc/rc-loopback.c
@@ -0,0 +1,246 @@
+/*
+ * Loopback driver for rc-core,
+ *
+ * Copyright (c) 2010 David Härdeman <david@hardeman.nu>
+ *
+ * This driver receives TX data and passes it back as RX data,
+ * which is useful for (scripted) debugging of rc-core without
+ * having to use actual hardware.
+ *
+ * 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/device.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <media/rc-core.h>
+
+#define DRIVER_NAME	"rc-loopback"
+#define dprintk(x...)	if (debug) printk(KERN_INFO DRIVER_NAME ": " x)
+#define RXMASK_REGULAR	0x1
+#define RXMASK_LEARNING	0x2
+
+static bool debug;
+
+struct loopback_dev {
+	struct rc_dev *dev;
+	u32 txmask;
+	u32 txcarrier;
+	u32 txduty;
+	bool idle;
+	bool learning;
+	bool carrierreport;
+	u32 rxcarriermin;
+	u32 rxcarriermax;
+};
+
+static struct loopback_dev loopdev;
+
+static int loop_set_tx_mask(struct rc_dev *dev, u32 mask)
+{
+	struct loopback_dev *lodev = dev->priv;
+
+	if ((mask & (RXMASK_REGULAR | RXMASK_LEARNING)) != mask) {
+		dprintk("invalid tx mask: %u\n", mask);
+		return -EINVAL;
+	}
+
+	dprintk("setting tx mask: %u\n", mask);
+	lodev->txmask = mask;
+	return 0;
+}
+
+static int loop_set_tx_carrier(struct rc_dev *dev, u32 carrier)
+{
+	struct loopback_dev *lodev = dev->priv;
+
+	dprintk("setting tx carrier: %u\n", carrier);
+	lodev->txcarrier = carrier;
+	return 0;
+}
+
+static int loop_set_tx_duty_cycle(struct rc_dev *dev, u32 duty_cycle)
+{
+	struct loopback_dev *lodev = dev->priv;
+
+	if (duty_cycle < 1 || duty_cycle > 99) {
+		dprintk("invalid duty cycle: %u\n", duty_cycle);
+		return -EINVAL;
+	}
+
+	dprintk("setting duty cycle: %u\n", duty_cycle);
+	lodev->txduty = duty_cycle;
+	return 0;
+}
+
+static int loop_set_rx_carrier_range(struct rc_dev *dev, u32 min, u32 max)
+{
+	struct loopback_dev *lodev = dev->priv;
+
+	if (min < 1 || min > max) {
+		dprintk("invalid rx carrier range %u to %u\n", min, max);
+		return -EINVAL;
+	}
+
+	dprintk("setting rx carrier range %u to %u\n", min, max);
+	lodev->rxcarriermin = min;
+	lodev->rxcarriermax = max;
+	return 0;
+}
+
+static int loop_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count)
+{
+	struct loopback_dev *lodev = dev->priv;
+	u32 rxmask;
+	unsigned i;
+	DEFINE_IR_RAW_EVENT(rawir);
+
+	if (lodev->txcarrier < lodev->rxcarriermin ||
+	    lodev->txcarrier > lodev->rxcarriermax) {
+		dprintk("ignoring tx, carrier out of range\n");
+		goto out;
+	}
+
+	if (lodev->learning)
+		rxmask = RXMASK_LEARNING;
+	else
+		rxmask = RXMASK_REGULAR;
+
+	if (!(rxmask & lodev->txmask)) {
+		dprintk("ignoring tx, rx mask mismatch\n");
+		goto out;
+	}
+
+	for (i = 0; i < count; i++) {
+		rawir.pulse = i % 2 ? false : true;
+		rawir.duration = txbuf[i] * 1000;
+		if (rawir.duration)
+			ir_raw_event_store_with_filter(dev, &rawir);
+	}
+
+	/* Fake a silence long enough to cause us to go idle */
+	rawir.pulse = false;
+	rawir.duration = dev->timeout;
+	ir_raw_event_store_with_filter(dev, &rawir);
+
+	ir_raw_event_handle(dev);
+
+out:
+	return count;
+}
+
+static void loop_set_idle(struct rc_dev *dev, bool enable)
+{
+	struct loopback_dev *lodev = dev->priv;
+
+	if (lodev->idle != enable) {
+		dprintk("%sing idle mode\n", enable ? "enter" : "exit");
+		lodev->idle = enable;
+	}
+}
+
+static int loop_set_learning_mode(struct rc_dev *dev, int enable)
+{
+	struct loopback_dev *lodev = dev->priv;
+
+	if (lodev->learning != enable) {
+		dprintk("%sing learning mode\n", enable ? "enter" : "exit");
+		lodev->learning = !!enable;
+	}
+
+	return 0;
+}
+
+static int loop_set_carrier_report(struct rc_dev *dev, int enable)
+{
+	struct loopback_dev *lodev = dev->priv;
+
+	if (lodev->carrierreport != enable) {
+		dprintk("%sabling carrier reports\n", enable ? "en" : "dis");
+		lodev->carrierreport = !!enable;
+	}
+
+	return 0;
+}
+
+static int __init loop_init(void)
+{
+	struct rc_dev *rc;
+	int ret;
+
+	rc = rc_allocate_device();
+	if (!rc) {
+		printk(KERN_ERR DRIVER_NAME ": rc_dev allocation failed\n");
+		return -ENOMEM;
+	}
+
+	rc->input_name		= "rc-core loopback device";
+	rc->input_phys		= "rc-core/virtual";
+	rc->input_id.bustype	= BUS_VIRTUAL;
+	rc->input_id.version	= 1;
+	rc->driver_name		= DRIVER_NAME;
+	rc->map_name		= RC_MAP_EMPTY;
+	rc->priv		= &loopdev;
+	rc->driver_type		= RC_DRIVER_IR_RAW;
+	rc->allowed_protocols	= RC_BIT_ALL;
+	rc->timeout		= 100 * 1000 * 1000; /* 100 ms */
+	rc->min_timeout		= 1;
+	rc->max_timeout		= UINT_MAX;
+	rc->rx_resolution	= 1000;
+	rc->tx_resolution	= 1000;
+	rc->s_tx_mask		= loop_set_tx_mask;
+	rc->s_tx_carrier	= loop_set_tx_carrier;
+	rc->s_tx_duty_cycle	= loop_set_tx_duty_cycle;
+	rc->s_rx_carrier_range	= loop_set_rx_carrier_range;
+	rc->tx_ir		= loop_tx_ir;
+	rc->s_idle		= loop_set_idle;
+	rc->s_learning_mode	= loop_set_learning_mode;
+	rc->s_carrier_report	= loop_set_carrier_report;
+
+	loopdev.txmask		= RXMASK_REGULAR;
+	loopdev.txcarrier	= 36000;
+	loopdev.txduty		= 50;
+	loopdev.rxcarriermin	= 1;
+	loopdev.rxcarriermax	= ~0;
+	loopdev.idle		= true;
+	loopdev.learning	= false;
+	loopdev.carrierreport	= false;
+
+	ret = rc_register_device(rc);
+	if (ret < 0) {
+		printk(KERN_ERR DRIVER_NAME ": rc_dev registration failed\n");
+		rc_free_device(rc);
+		return ret;
+	}
+
+	loopdev.dev = rc;
+	return 0;
+}
+
+static void __exit loop_exit(void)
+{
+	rc_unregister_device(loopdev.dev);
+}
+
+module_init(loop_init);
+module_exit(loop_exit);
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable debug messages");
+
+MODULE_DESCRIPTION("Loopback device for rc-core debugging");
+MODULE_AUTHOR("David Härdeman <david@hardeman.nu>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
new file mode 100644
index 0000000..3f0f71a
--- /dev/null
+++ b/drivers/media/rc/rc-main.c
@@ -0,0 +1,1538 @@
+/* rc-main.c - Remote Controller core module
+ *
+ * Copyright (C) 2009-2010 by Mauro Carvalho Chehab
+ *
+ * This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation version 2 of the License.
+ *
+ *  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.
+ */
+
+#include <media/rc-core.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/leds.h>
+#include <linux/slab.h>
+#include <linux/idr.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include "rc-core-priv.h"
+
+/* Sizes are in bytes, 256 bytes allows for 32 entries on x64 */
+#define IR_TAB_MIN_SIZE	256
+#define IR_TAB_MAX_SIZE	8192
+#define RC_DEV_MAX	256
+
+/* FIXME: IR_KEYPRESS_TIMEOUT should be protocol specific */
+#define IR_KEYPRESS_TIMEOUT 250
+
+/* Used to keep track of known keymaps */
+static LIST_HEAD(rc_map_list);
+static DEFINE_SPINLOCK(rc_map_lock);
+static struct led_trigger *led_feedback;
+
+/* Used to keep track of rc devices */
+static DEFINE_IDA(rc_ida);
+
+static struct rc_map_list *seek_rc_map(const char *name)
+{
+	struct rc_map_list *map = NULL;
+
+	spin_lock(&rc_map_lock);
+	list_for_each_entry(map, &rc_map_list, list) {
+		if (!strcmp(name, map->map.name)) {
+			spin_unlock(&rc_map_lock);
+			return map;
+		}
+	}
+	spin_unlock(&rc_map_lock);
+
+	return NULL;
+}
+
+struct rc_map *rc_map_get(const char *name)
+{
+
+	struct rc_map_list *map;
+
+	map = seek_rc_map(name);
+#ifdef MODULE
+	if (!map) {
+		int rc = request_module("%s", name);
+		if (rc < 0) {
+			printk(KERN_ERR "Couldn't load IR keymap %s\n", name);
+			return NULL;
+		}
+		msleep(20);	/* Give some time for IR to register */
+
+		map = seek_rc_map(name);
+	}
+#endif
+	if (!map) {
+		printk(KERN_ERR "IR keymap %s not found\n", name);
+		return NULL;
+	}
+
+	printk(KERN_INFO "Registered IR keymap %s\n", map->map.name);
+
+	return &map->map;
+}
+EXPORT_SYMBOL_GPL(rc_map_get);
+
+int rc_map_register(struct rc_map_list *map)
+{
+	spin_lock(&rc_map_lock);
+	list_add_tail(&map->list, &rc_map_list);
+	spin_unlock(&rc_map_lock);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rc_map_register);
+
+void rc_map_unregister(struct rc_map_list *map)
+{
+	spin_lock(&rc_map_lock);
+	list_del(&map->list);
+	spin_unlock(&rc_map_lock);
+}
+EXPORT_SYMBOL_GPL(rc_map_unregister);
+
+
+static struct rc_map_table empty[] = {
+	{ 0x2a, KEY_COFFEE },
+};
+
+static struct rc_map_list empty_map = {
+	.map = {
+		.scan    = empty,
+		.size    = ARRAY_SIZE(empty),
+		.rc_type = RC_TYPE_UNKNOWN,	/* Legacy IR type */
+		.name    = RC_MAP_EMPTY,
+	}
+};
+
+/**
+ * ir_create_table() - initializes a scancode table
+ * @rc_map:	the rc_map to initialize
+ * @name:	name to assign to the table
+ * @rc_type:	ir type to assign to the new table
+ * @size:	initial size of the table
+ * @return:	zero on success or a negative error code
+ *
+ * This routine will initialize the rc_map and will allocate
+ * memory to hold at least the specified number of elements.
+ */
+static int ir_create_table(struct rc_map *rc_map,
+			   const char *name, u64 rc_type, size_t size)
+{
+	rc_map->name = name;
+	rc_map->rc_type = rc_type;
+	rc_map->alloc = roundup_pow_of_two(size * sizeof(struct rc_map_table));
+	rc_map->size = rc_map->alloc / sizeof(struct rc_map_table);
+	rc_map->scan = kmalloc(rc_map->alloc, GFP_KERNEL);
+	if (!rc_map->scan)
+		return -ENOMEM;
+
+	IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n",
+		   rc_map->size, rc_map->alloc);
+	return 0;
+}
+
+/**
+ * ir_free_table() - frees memory allocated by a scancode table
+ * @rc_map:	the table whose mappings need to be freed
+ *
+ * This routine will free memory alloctaed for key mappings used by given
+ * scancode table.
+ */
+static void ir_free_table(struct rc_map *rc_map)
+{
+	rc_map->size = 0;
+	kfree(rc_map->scan);
+	rc_map->scan = NULL;
+}
+
+/**
+ * ir_resize_table() - resizes a scancode table if necessary
+ * @rc_map:	the rc_map to resize
+ * @gfp_flags:	gfp flags to use when allocating memory
+ * @return:	zero on success or a negative error code
+ *
+ * This routine will shrink the rc_map if it has lots of
+ * unused entries and grow it if it is full.
+ */
+static int ir_resize_table(struct rc_map *rc_map, gfp_t gfp_flags)
+{
+	unsigned int oldalloc = rc_map->alloc;
+	unsigned int newalloc = oldalloc;
+	struct rc_map_table *oldscan = rc_map->scan;
+	struct rc_map_table *newscan;
+
+	if (rc_map->size == rc_map->len) {
+		/* All entries in use -> grow keytable */
+		if (rc_map->alloc >= IR_TAB_MAX_SIZE)
+			return -ENOMEM;
+
+		newalloc *= 2;
+		IR_dprintk(1, "Growing table to %u bytes\n", newalloc);
+	}
+
+	if ((rc_map->len * 3 < rc_map->size) && (oldalloc > IR_TAB_MIN_SIZE)) {
+		/* Less than 1/3 of entries in use -> shrink keytable */
+		newalloc /= 2;
+		IR_dprintk(1, "Shrinking table to %u bytes\n", newalloc);
+	}
+
+	if (newalloc == oldalloc)
+		return 0;
+
+	newscan = kmalloc(newalloc, gfp_flags);
+	if (!newscan) {
+		IR_dprintk(1, "Failed to kmalloc %u bytes\n", newalloc);
+		return -ENOMEM;
+	}
+
+	memcpy(newscan, rc_map->scan, rc_map->len * sizeof(struct rc_map_table));
+	rc_map->scan = newscan;
+	rc_map->alloc = newalloc;
+	rc_map->size = rc_map->alloc / sizeof(struct rc_map_table);
+	kfree(oldscan);
+	return 0;
+}
+
+/**
+ * ir_update_mapping() - set a keycode in the scancode->keycode table
+ * @dev:	the struct rc_dev device descriptor
+ * @rc_map:	scancode table to be adjusted
+ * @index:	index of the mapping that needs to be updated
+ * @keycode:	the desired keycode
+ * @return:	previous keycode assigned to the mapping
+ *
+ * This routine is used to update scancode->keycode mapping at given
+ * position.
+ */
+static unsigned int ir_update_mapping(struct rc_dev *dev,
+				      struct rc_map *rc_map,
+				      unsigned int index,
+				      unsigned int new_keycode)
+{
+	int old_keycode = rc_map->scan[index].keycode;
+	int i;
+
+	/* Did the user wish to remove the mapping? */
+	if (new_keycode == KEY_RESERVED || new_keycode == KEY_UNKNOWN) {
+		IR_dprintk(1, "#%d: Deleting scan 0x%04x\n",
+			   index, rc_map->scan[index].scancode);
+		rc_map->len--;
+		memmove(&rc_map->scan[index], &rc_map->scan[index+ 1],
+			(rc_map->len - index) * sizeof(struct rc_map_table));
+	} else {
+		IR_dprintk(1, "#%d: %s scan 0x%04x with key 0x%04x\n",
+			   index,
+			   old_keycode == KEY_RESERVED ? "New" : "Replacing",
+			   rc_map->scan[index].scancode, new_keycode);
+		rc_map->scan[index].keycode = new_keycode;
+		__set_bit(new_keycode, dev->input_dev->keybit);
+	}
+
+	if (old_keycode != KEY_RESERVED) {
+		/* A previous mapping was updated... */
+		__clear_bit(old_keycode, dev->input_dev->keybit);
+		/* ... but another scancode might use the same keycode */
+		for (i = 0; i < rc_map->len; i++) {
+			if (rc_map->scan[i].keycode == old_keycode) {
+				__set_bit(old_keycode, dev->input_dev->keybit);
+				break;
+			}
+		}
+
+		/* Possibly shrink the keytable, failure is not a problem */
+		ir_resize_table(rc_map, GFP_ATOMIC);
+	}
+
+	return old_keycode;
+}
+
+/**
+ * ir_establish_scancode() - set a keycode in the scancode->keycode table
+ * @dev:	the struct rc_dev device descriptor
+ * @rc_map:	scancode table to be searched
+ * @scancode:	the desired scancode
+ * @resize:	controls whether we allowed to resize the table to
+ *		accommodate not yet present scancodes
+ * @return:	index of the mapping containing scancode in question
+ *		or -1U in case of failure.
+ *
+ * This routine is used to locate given scancode in rc_map.
+ * If scancode is not yet present the routine will allocate a new slot
+ * for it.
+ */
+static unsigned int ir_establish_scancode(struct rc_dev *dev,
+					  struct rc_map *rc_map,
+					  unsigned int scancode,
+					  bool resize)
+{
+	unsigned int i;
+
+	/*
+	 * Unfortunately, some hardware-based IR decoders don't provide
+	 * all bits for the complete IR code. In general, they provide only
+	 * the command part of the IR code. Yet, as it is possible to replace
+	 * the provided IR with another one, it is needed to allow loading
+	 * IR tables from other remotes. So, we support specifying a mask to
+	 * indicate the valid bits of the scancodes.
+	 */
+	if (dev->scancode_mask)
+		scancode &= dev->scancode_mask;
+
+	/* First check if we already have a mapping for this ir command */
+	for (i = 0; i < rc_map->len; i++) {
+		if (rc_map->scan[i].scancode == scancode)
+			return i;
+
+		/* Keytable is sorted from lowest to highest scancode */
+		if (rc_map->scan[i].scancode >= scancode)
+			break;
+	}
+
+	/* No previous mapping found, we might need to grow the table */
+	if (rc_map->size == rc_map->len) {
+		if (!resize || ir_resize_table(rc_map, GFP_ATOMIC))
+			return -1U;
+	}
+
+	/* i is the proper index to insert our new keycode */
+	if (i < rc_map->len)
+		memmove(&rc_map->scan[i + 1], &rc_map->scan[i],
+			(rc_map->len - i) * sizeof(struct rc_map_table));
+	rc_map->scan[i].scancode = scancode;
+	rc_map->scan[i].keycode = KEY_RESERVED;
+	rc_map->len++;
+
+	return i;
+}
+
+/**
+ * ir_setkeycode() - set a keycode in the scancode->keycode table
+ * @idev:	the struct input_dev device descriptor
+ * @scancode:	the desired scancode
+ * @keycode:	result
+ * @return:	-EINVAL if the keycode could not be inserted, otherwise zero.
+ *
+ * This routine is used to handle evdev EVIOCSKEY ioctl.
+ */
+static int ir_setkeycode(struct input_dev *idev,
+			 const struct input_keymap_entry *ke,
+			 unsigned int *old_keycode)
+{
+	struct rc_dev *rdev = input_get_drvdata(idev);
+	struct rc_map *rc_map = &rdev->rc_map;
+	unsigned int index;
+	unsigned int scancode;
+	int retval = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&rc_map->lock, flags);
+
+	if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
+		index = ke->index;
+		if (index >= rc_map->len) {
+			retval = -EINVAL;
+			goto out;
+		}
+	} else {
+		retval = input_scancode_to_scalar(ke, &scancode);
+		if (retval)
+			goto out;
+
+		index = ir_establish_scancode(rdev, rc_map, scancode, true);
+		if (index >= rc_map->len) {
+			retval = -ENOMEM;
+			goto out;
+		}
+	}
+
+	*old_keycode = ir_update_mapping(rdev, rc_map, index, ke->keycode);
+
+out:
+	spin_unlock_irqrestore(&rc_map->lock, flags);
+	return retval;
+}
+
+/**
+ * ir_setkeytable() - sets several entries in the scancode->keycode table
+ * @dev:	the struct rc_dev device descriptor
+ * @to:		the struct rc_map to copy entries to
+ * @from:	the struct rc_map to copy entries from
+ * @return:	-ENOMEM if all keycodes could not be inserted, otherwise zero.
+ *
+ * This routine is used to handle table initialization.
+ */
+static int ir_setkeytable(struct rc_dev *dev,
+			  const struct rc_map *from)
+{
+	struct rc_map *rc_map = &dev->rc_map;
+	unsigned int i, index;
+	int rc;
+
+	rc = ir_create_table(rc_map, from->name,
+			     from->rc_type, from->size);
+	if (rc)
+		return rc;
+
+	IR_dprintk(1, "Allocated space for %u keycode entries (%u bytes)\n",
+		   rc_map->size, rc_map->alloc);
+
+	for (i = 0; i < from->size; i++) {
+		index = ir_establish_scancode(dev, rc_map,
+					      from->scan[i].scancode, false);
+		if (index >= rc_map->len) {
+			rc = -ENOMEM;
+			break;
+		}
+
+		ir_update_mapping(dev, rc_map, index,
+				  from->scan[i].keycode);
+	}
+
+	if (rc)
+		ir_free_table(rc_map);
+
+	return rc;
+}
+
+/**
+ * ir_lookup_by_scancode() - locate mapping by scancode
+ * @rc_map:	the struct rc_map to search
+ * @scancode:	scancode to look for in the table
+ * @return:	index in the table, -1U if not found
+ *
+ * This routine performs binary search in RC keykeymap table for
+ * given scancode.
+ */
+static unsigned int ir_lookup_by_scancode(const struct rc_map *rc_map,
+					  unsigned int scancode)
+{
+	int start = 0;
+	int end = rc_map->len - 1;
+	int mid;
+
+	while (start <= end) {
+		mid = (start + end) / 2;
+		if (rc_map->scan[mid].scancode < scancode)
+			start = mid + 1;
+		else if (rc_map->scan[mid].scancode > scancode)
+			end = mid - 1;
+		else
+			return mid;
+	}
+
+	return -1U;
+}
+
+/**
+ * ir_getkeycode() - get a keycode from the scancode->keycode table
+ * @idev:	the struct input_dev device descriptor
+ * @scancode:	the desired scancode
+ * @keycode:	used to return the keycode, if found, or KEY_RESERVED
+ * @return:	always returns zero.
+ *
+ * This routine is used to handle evdev EVIOCGKEY ioctl.
+ */
+static int ir_getkeycode(struct input_dev *idev,
+			 struct input_keymap_entry *ke)
+{
+	struct rc_dev *rdev = input_get_drvdata(idev);
+	struct rc_map *rc_map = &rdev->rc_map;
+	struct rc_map_table *entry;
+	unsigned long flags;
+	unsigned int index;
+	unsigned int scancode;
+	int retval;
+
+	spin_lock_irqsave(&rc_map->lock, flags);
+
+	if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
+		index = ke->index;
+	} else {
+		retval = input_scancode_to_scalar(ke, &scancode);
+		if (retval)
+			goto out;
+
+		index = ir_lookup_by_scancode(rc_map, scancode);
+	}
+
+	if (index < rc_map->len) {
+		entry = &rc_map->scan[index];
+
+		ke->index = index;
+		ke->keycode = entry->keycode;
+		ke->len = sizeof(entry->scancode);
+		memcpy(ke->scancode, &entry->scancode, sizeof(entry->scancode));
+
+	} else if (!(ke->flags & INPUT_KEYMAP_BY_INDEX)) {
+		/*
+		 * We do not really know the valid range of scancodes
+		 * so let's respond with KEY_RESERVED to anything we
+		 * do not have mapping for [yet].
+		 */
+		ke->index = index;
+		ke->keycode = KEY_RESERVED;
+	} else {
+		retval = -EINVAL;
+		goto out;
+	}
+
+	retval = 0;
+
+out:
+	spin_unlock_irqrestore(&rc_map->lock, flags);
+	return retval;
+}
+
+/**
+ * rc_g_keycode_from_table() - gets the keycode that corresponds to a scancode
+ * @dev:	the struct rc_dev descriptor of the device
+ * @scancode:	the scancode to look for
+ * @return:	the corresponding keycode, or KEY_RESERVED
+ *
+ * This routine is used by drivers which need to convert a scancode to a
+ * keycode. Normally it should not be used since drivers should have no
+ * interest in keycodes.
+ */
+u32 rc_g_keycode_from_table(struct rc_dev *dev, u32 scancode)
+{
+	struct rc_map *rc_map = &dev->rc_map;
+	unsigned int keycode;
+	unsigned int index;
+	unsigned long flags;
+
+	spin_lock_irqsave(&rc_map->lock, flags);
+
+	index = ir_lookup_by_scancode(rc_map, scancode);
+	keycode = index < rc_map->len ?
+			rc_map->scan[index].keycode : KEY_RESERVED;
+
+	spin_unlock_irqrestore(&rc_map->lock, flags);
+
+	if (keycode != KEY_RESERVED)
+		IR_dprintk(1, "%s: scancode 0x%04x keycode 0x%02x\n",
+			   dev->input_name, scancode, keycode);
+
+	return keycode;
+}
+EXPORT_SYMBOL_GPL(rc_g_keycode_from_table);
+
+/**
+ * ir_do_keyup() - internal function to signal the release of a keypress
+ * @dev:	the struct rc_dev descriptor of the device
+ * @sync:	whether or not to call input_sync
+ *
+ * This function is used internally to release a keypress, it must be
+ * called with keylock held.
+ */
+static void ir_do_keyup(struct rc_dev *dev, bool sync)
+{
+	if (!dev->keypressed)
+		return;
+
+	IR_dprintk(1, "keyup key 0x%04x\n", dev->last_keycode);
+	input_report_key(dev->input_dev, dev->last_keycode, 0);
+	led_trigger_event(led_feedback, LED_OFF);
+	if (sync)
+		input_sync(dev->input_dev);
+	dev->keypressed = false;
+}
+
+/**
+ * rc_keyup() - signals the release of a keypress
+ * @dev:	the struct rc_dev descriptor of the device
+ *
+ * This routine is used to signal that a key has been released on the
+ * remote control.
+ */
+void rc_keyup(struct rc_dev *dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->keylock, flags);
+	ir_do_keyup(dev, true);
+	spin_unlock_irqrestore(&dev->keylock, flags);
+}
+EXPORT_SYMBOL_GPL(rc_keyup);
+
+/**
+ * ir_timer_keyup() - generates a keyup event after a timeout
+ * @cookie:	a pointer to the struct rc_dev for the device
+ *
+ * This routine will generate a keyup event some time after a keydown event
+ * is generated when no further activity has been detected.
+ */
+static void ir_timer_keyup(unsigned long cookie)
+{
+	struct rc_dev *dev = (struct rc_dev *)cookie;
+	unsigned long flags;
+
+	/*
+	 * ir->keyup_jiffies is used to prevent a race condition if a
+	 * hardware interrupt occurs at this point and the keyup timer
+	 * event is moved further into the future as a result.
+	 *
+	 * The timer will then be reactivated and this function called
+	 * again in the future. We need to exit gracefully in that case
+	 * to allow the input subsystem to do its auto-repeat magic or
+	 * a keyup event might follow immediately after the keydown.
+	 */
+	spin_lock_irqsave(&dev->keylock, flags);
+	if (time_is_before_eq_jiffies(dev->keyup_jiffies))
+		ir_do_keyup(dev, true);
+	spin_unlock_irqrestore(&dev->keylock, flags);
+}
+
+/**
+ * rc_repeat() - signals that a key is still pressed
+ * @dev:	the struct rc_dev descriptor of the device
+ *
+ * This routine is used by IR decoders when a repeat message which does
+ * not include the necessary bits to reproduce the scancode has been
+ * received.
+ */
+void rc_repeat(struct rc_dev *dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->keylock, flags);
+
+	input_event(dev->input_dev, EV_MSC, MSC_SCAN, dev->last_scancode);
+	input_sync(dev->input_dev);
+
+	if (!dev->keypressed)
+		goto out;
+
+	dev->keyup_jiffies = jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT);
+	mod_timer(&dev->timer_keyup, dev->keyup_jiffies);
+
+out:
+	spin_unlock_irqrestore(&dev->keylock, flags);
+}
+EXPORT_SYMBOL_GPL(rc_repeat);
+
+/**
+ * ir_do_keydown() - internal function to process a keypress
+ * @dev:	the struct rc_dev descriptor of the device
+ * @protocol:	the protocol of the keypress
+ * @scancode:   the scancode of the keypress
+ * @keycode:    the keycode of the keypress
+ * @toggle:     the toggle value of the keypress
+ *
+ * This function is used internally to register a keypress, it must be
+ * called with keylock held.
+ */
+static void ir_do_keydown(struct rc_dev *dev, enum rc_type protocol,
+			  u32 scancode, u32 keycode, u8 toggle)
+{
+	bool new_event = (!dev->keypressed		 ||
+			  dev->last_protocol != protocol ||
+			  dev->last_scancode != scancode ||
+			  dev->last_toggle   != toggle);
+
+	if (new_event && dev->keypressed)
+		ir_do_keyup(dev, false);
+
+	input_event(dev->input_dev, EV_MSC, MSC_SCAN, scancode);
+
+	if (new_event && keycode != KEY_RESERVED) {
+		/* Register a keypress */
+		dev->keypressed = true;
+		dev->last_protocol = protocol;
+		dev->last_scancode = scancode;
+		dev->last_toggle = toggle;
+		dev->last_keycode = keycode;
+
+		IR_dprintk(1, "%s: key down event, "
+			   "key 0x%04x, protocol 0x%04x, scancode 0x%08x\n",
+			   dev->input_name, keycode, protocol, scancode);
+		input_report_key(dev->input_dev, keycode, 1);
+
+		led_trigger_event(led_feedback, LED_FULL);
+	}
+
+	input_sync(dev->input_dev);
+}
+
+/**
+ * rc_keydown() - generates input event for a key press
+ * @dev:	the struct rc_dev descriptor of the device
+ * @protocol:	the protocol for the keypress
+ * @scancode:	the scancode for the keypress
+ * @toggle:     the toggle value (protocol dependent, if the protocol doesn't
+ *              support toggle values, this should be set to zero)
+ *
+ * This routine is used to signal that a key has been pressed on the
+ * remote control.
+ */
+void rc_keydown(struct rc_dev *dev, enum rc_type protocol, u32 scancode, u8 toggle)
+{
+	unsigned long flags;
+	u32 keycode = rc_g_keycode_from_table(dev, scancode);
+
+	spin_lock_irqsave(&dev->keylock, flags);
+	ir_do_keydown(dev, protocol, scancode, keycode, toggle);
+
+	if (dev->keypressed) {
+		dev->keyup_jiffies = jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT);
+		mod_timer(&dev->timer_keyup, dev->keyup_jiffies);
+	}
+	spin_unlock_irqrestore(&dev->keylock, flags);
+}
+EXPORT_SYMBOL_GPL(rc_keydown);
+
+/**
+ * rc_keydown_notimeout() - generates input event for a key press without
+ *                          an automatic keyup event at a later time
+ * @dev:	the struct rc_dev descriptor of the device
+ * @protocol:	the protocol for the keypress
+ * @scancode:	the scancode for the keypress
+ * @toggle:     the toggle value (protocol dependent, if the protocol doesn't
+ *              support toggle values, this should be set to zero)
+ *
+ * This routine is used to signal that a key has been pressed on the
+ * remote control. The driver must manually call rc_keyup() at a later stage.
+ */
+void rc_keydown_notimeout(struct rc_dev *dev, enum rc_type protocol,
+			  u32 scancode, u8 toggle)
+{
+	unsigned long flags;
+	u32 keycode = rc_g_keycode_from_table(dev, scancode);
+
+	spin_lock_irqsave(&dev->keylock, flags);
+	ir_do_keydown(dev, protocol, scancode, keycode, toggle);
+	spin_unlock_irqrestore(&dev->keylock, flags);
+}
+EXPORT_SYMBOL_GPL(rc_keydown_notimeout);
+
+int rc_open(struct rc_dev *rdev)
+{
+	int rval = 0;
+
+	if (!rdev)
+		return -EINVAL;
+
+	mutex_lock(&rdev->lock);
+	if (!rdev->users++ && rdev->open != NULL)
+		rval = rdev->open(rdev);
+
+	if (rval)
+		rdev->users--;
+
+	mutex_unlock(&rdev->lock);
+
+	return rval;
+}
+EXPORT_SYMBOL_GPL(rc_open);
+
+static int ir_open(struct input_dev *idev)
+{
+	struct rc_dev *rdev = input_get_drvdata(idev);
+
+	return rc_open(rdev);
+}
+
+void rc_close(struct rc_dev *rdev)
+{
+	if (rdev) {
+		mutex_lock(&rdev->lock);
+
+		if (!--rdev->users && rdev->close != NULL)
+			rdev->close(rdev);
+
+		mutex_unlock(&rdev->lock);
+	}
+}
+EXPORT_SYMBOL_GPL(rc_close);
+
+static void ir_close(struct input_dev *idev)
+{
+	struct rc_dev *rdev = input_get_drvdata(idev);
+	rc_close(rdev);
+}
+
+/* class for /sys/class/rc */
+static char *rc_devnode(struct device *dev, umode_t *mode)
+{
+	return kasprintf(GFP_KERNEL, "rc/%s", dev_name(dev));
+}
+
+static struct class rc_class = {
+	.name		= "rc",
+	.devnode	= rc_devnode,
+};
+
+/*
+ * These are the protocol textual descriptions that are
+ * used by the sysfs protocols file. Note that the order
+ * of the entries is relevant.
+ */
+static struct {
+	u64	type;
+	char	*name;
+} proto_names[] = {
+	{ RC_BIT_NONE,		"none"		},
+	{ RC_BIT_OTHER,		"other"		},
+	{ RC_BIT_UNKNOWN,	"unknown"	},
+	{ RC_BIT_RC5 |
+	  RC_BIT_RC5X,		"rc-5"		},
+	{ RC_BIT_NEC,		"nec"		},
+	{ RC_BIT_RC6_0 |
+	  RC_BIT_RC6_6A_20 |
+	  RC_BIT_RC6_6A_24 |
+	  RC_BIT_RC6_6A_32 |
+	  RC_BIT_RC6_MCE,	"rc-6"		},
+	{ RC_BIT_JVC,		"jvc"		},
+	{ RC_BIT_SONY12 |
+	  RC_BIT_SONY15 |
+	  RC_BIT_SONY20,	"sony"		},
+	{ RC_BIT_RC5_SZ,	"rc-5-sz"	},
+	{ RC_BIT_SANYO,		"sanyo"		},
+	{ RC_BIT_SHARP,		"sharp"		},
+	{ RC_BIT_MCE_KBD,	"mce_kbd"	},
+	{ RC_BIT_XMP,		"xmp"		},
+};
+
+/**
+ * struct rc_filter_attribute - Device attribute relating to a filter type.
+ * @attr:	Device attribute.
+ * @type:	Filter type.
+ * @mask:	false for filter value, true for filter mask.
+ */
+struct rc_filter_attribute {
+	struct device_attribute		attr;
+	enum rc_filter_type		type;
+	bool				mask;
+};
+#define to_rc_filter_attr(a) container_of(a, struct rc_filter_attribute, attr)
+
+#define RC_PROTO_ATTR(_name, _mode, _show, _store, _type)		\
+	struct rc_filter_attribute dev_attr_##_name = {			\
+		.attr = __ATTR(_name, _mode, _show, _store),		\
+		.type = (_type),					\
+	}
+#define RC_FILTER_ATTR(_name, _mode, _show, _store, _type, _mask)	\
+	struct rc_filter_attribute dev_attr_##_name = {			\
+		.attr = __ATTR(_name, _mode, _show, _store),		\
+		.type = (_type),					\
+		.mask = (_mask),					\
+	}
+
+static bool lirc_is_present(void)
+{
+#if defined(CONFIG_LIRC_MODULE)
+	struct module *lirc;
+
+	mutex_lock(&module_mutex);
+	lirc = find_module("lirc_dev");
+	mutex_unlock(&module_mutex);
+
+	return lirc ? true : false;
+#elif defined(CONFIG_LIRC)
+	return true;
+#else
+	return false;
+#endif
+}
+
+/**
+ * show_protocols() - shows the current/wakeup IR protocol(s)
+ * @device:	the device descriptor
+ * @mattr:	the device attribute struct
+ * @buf:	a pointer to the output buffer
+ *
+ * This routine is a callback routine for input read the IR protocol type(s).
+ * it is trigged by reading /sys/class/rc/rc?/[wakeup_]protocols.
+ * It returns the protocol names of supported protocols.
+ * Enabled protocols are printed in brackets.
+ *
+ * dev->lock is taken to guard against races between device
+ * registration, store_protocols and show_protocols.
+ */
+static ssize_t show_protocols(struct device *device,
+			      struct device_attribute *mattr, char *buf)
+{
+	struct rc_dev *dev = to_rc_dev(device);
+	struct rc_filter_attribute *fattr = to_rc_filter_attr(mattr);
+	u64 allowed, enabled;
+	char *tmp = buf;
+	int i;
+
+	/* Device is being removed */
+	if (!dev)
+		return -EINVAL;
+
+	mutex_lock(&dev->lock);
+
+	if (fattr->type == RC_FILTER_NORMAL) {
+		enabled = dev->enabled_protocols;
+		allowed = dev->allowed_protocols;
+		if (dev->raw && !allowed)
+			allowed = ir_raw_get_allowed_protocols();
+	} else {
+		enabled = dev->enabled_wakeup_protocols;
+		allowed = dev->allowed_wakeup_protocols;
+	}
+
+	mutex_unlock(&dev->lock);
+
+	IR_dprintk(1, "%s: allowed - 0x%llx, enabled - 0x%llx\n",
+		   __func__, (long long)allowed, (long long)enabled);
+
+	for (i = 0; i < ARRAY_SIZE(proto_names); i++) {
+		if (allowed & enabled & proto_names[i].type)
+			tmp += sprintf(tmp, "[%s] ", proto_names[i].name);
+		else if (allowed & proto_names[i].type)
+			tmp += sprintf(tmp, "%s ", proto_names[i].name);
+
+		if (allowed & proto_names[i].type)
+			allowed &= ~proto_names[i].type;
+	}
+
+	if (dev->driver_type == RC_DRIVER_IR_RAW && lirc_is_present())
+		tmp += sprintf(tmp, "[lirc] ");
+
+	if (tmp != buf)
+		tmp--;
+	*tmp = '\n';
+
+	return tmp + 1 - buf;
+}
+
+/**
+ * parse_protocol_change() - parses a protocol change request
+ * @protocols:	pointer to the bitmask of current protocols
+ * @buf:	pointer to the buffer with a list of changes
+ *
+ * Writing "+proto" will add a protocol to the protocol mask.
+ * Writing "-proto" will remove a protocol from protocol mask.
+ * Writing "proto" will enable only "proto".
+ * Writing "none" will disable all protocols.
+ * Returns the number of changes performed or a negative error code.
+ */
+static int parse_protocol_change(u64 *protocols, const char *buf)
+{
+	const char *tmp;
+	unsigned count = 0;
+	bool enable, disable;
+	u64 mask;
+	int i;
+
+	while ((tmp = strsep((char **)&buf, " \n")) != NULL) {
+		if (!*tmp)
+			break;
+
+		if (*tmp == '+') {
+			enable = true;
+			disable = false;
+			tmp++;
+		} else if (*tmp == '-') {
+			enable = false;
+			disable = true;
+			tmp++;
+		} else {
+			enable = false;
+			disable = false;
+		}
+
+		for (i = 0; i < ARRAY_SIZE(proto_names); i++) {
+			if (!strcasecmp(tmp, proto_names[i].name)) {
+				mask = proto_names[i].type;
+				break;
+			}
+		}
+
+		if (i == ARRAY_SIZE(proto_names)) {
+			if (!strcasecmp(tmp, "lirc"))
+				mask = 0;
+			else {
+				IR_dprintk(1, "Unknown protocol: '%s'\n", tmp);
+				return -EINVAL;
+			}
+		}
+
+		count++;
+
+		if (enable)
+			*protocols |= mask;
+		else if (disable)
+			*protocols &= ~mask;
+		else
+			*protocols = mask;
+	}
+
+	if (!count) {
+		IR_dprintk(1, "Protocol not specified\n");
+		return -EINVAL;
+	}
+
+	return count;
+}
+
+/**
+ * store_protocols() - changes the current/wakeup IR protocol(s)
+ * @device:	the device descriptor
+ * @mattr:	the device attribute struct
+ * @buf:	a pointer to the input buffer
+ * @len:	length of the input buffer
+ *
+ * This routine is for changing the IR protocol type.
+ * It is trigged by writing to /sys/class/rc/rc?/[wakeup_]protocols.
+ * See parse_protocol_change() for the valid commands.
+ * Returns @len on success or a negative error code.
+ *
+ * dev->lock is taken to guard against races between device
+ * registration, store_protocols and show_protocols.
+ */
+static ssize_t store_protocols(struct device *device,
+			       struct device_attribute *mattr,
+			       const char *buf, size_t len)
+{
+	struct rc_dev *dev = to_rc_dev(device);
+	struct rc_filter_attribute *fattr = to_rc_filter_attr(mattr);
+	u64 *current_protocols;
+	int (*change_protocol)(struct rc_dev *dev, u64 *rc_type);
+	struct rc_scancode_filter *filter;
+	int (*set_filter)(struct rc_dev *dev, struct rc_scancode_filter *filter);
+	u64 old_protocols, new_protocols;
+	ssize_t rc;
+
+	/* Device is being removed */
+	if (!dev)
+		return -EINVAL;
+
+	if (fattr->type == RC_FILTER_NORMAL) {
+		IR_dprintk(1, "Normal protocol change requested\n");
+		current_protocols = &dev->enabled_protocols;
+		change_protocol = dev->change_protocol;
+		filter = &dev->scancode_filter;
+		set_filter = dev->s_filter;
+	} else {
+		IR_dprintk(1, "Wakeup protocol change requested\n");
+		current_protocols = &dev->enabled_wakeup_protocols;
+		change_protocol = dev->change_wakeup_protocol;
+		filter = &dev->scancode_wakeup_filter;
+		set_filter = dev->s_wakeup_filter;
+	}
+
+	if (!change_protocol) {
+		IR_dprintk(1, "Protocol switching not supported\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&dev->lock);
+
+	old_protocols = *current_protocols;
+	new_protocols = old_protocols;
+	rc = parse_protocol_change(&new_protocols, buf);
+	if (rc < 0)
+		goto out;
+
+	rc = change_protocol(dev, &new_protocols);
+	if (rc < 0) {
+		IR_dprintk(1, "Error setting protocols to 0x%llx\n",
+			   (long long)new_protocols);
+		goto out;
+	}
+
+	if (new_protocols != old_protocols) {
+		*current_protocols = new_protocols;
+		IR_dprintk(1, "Protocols changed to 0x%llx\n",
+			   (long long)new_protocols);
+	}
+
+	/*
+	 * If a protocol change was attempted the filter may need updating, even
+	 * if the actual protocol mask hasn't changed (since the driver may have
+	 * cleared the filter).
+	 * Try setting the same filter with the new protocol (if any).
+	 * Fall back to clearing the filter.
+	 */
+	if (set_filter && filter->mask) {
+		if (new_protocols)
+			rc = set_filter(dev, filter);
+		else
+			rc = -1;
+
+		if (rc < 0) {
+			filter->data = 0;
+			filter->mask = 0;
+			set_filter(dev, filter);
+		}
+	}
+
+	rc = len;
+
+out:
+	mutex_unlock(&dev->lock);
+	return rc;
+}
+
+/**
+ * show_filter() - shows the current scancode filter value or mask
+ * @device:	the device descriptor
+ * @attr:	the device attribute struct
+ * @buf:	a pointer to the output buffer
+ *
+ * This routine is a callback routine to read a scancode filter value or mask.
+ * It is trigged by reading /sys/class/rc/rc?/[wakeup_]filter[_mask].
+ * It prints the current scancode filter value or mask of the appropriate filter
+ * type in hexadecimal into @buf and returns the size of the buffer.
+ *
+ * Bits of the filter value corresponding to set bits in the filter mask are
+ * compared against input scancodes and non-matching scancodes are discarded.
+ *
+ * dev->lock is taken to guard against races between device registration,
+ * store_filter and show_filter.
+ */
+static ssize_t show_filter(struct device *device,
+			   struct device_attribute *attr,
+			   char *buf)
+{
+	struct rc_dev *dev = to_rc_dev(device);
+	struct rc_filter_attribute *fattr = to_rc_filter_attr(attr);
+	struct rc_scancode_filter *filter;
+	u32 val;
+
+	/* Device is being removed */
+	if (!dev)
+		return -EINVAL;
+
+	if (fattr->type == RC_FILTER_NORMAL)
+		filter = &dev->scancode_filter;
+	else
+		filter = &dev->scancode_wakeup_filter;
+
+	mutex_lock(&dev->lock);
+	if (fattr->mask)
+		val = filter->mask;
+	else
+		val = filter->data;
+	mutex_unlock(&dev->lock);
+
+	return sprintf(buf, "%#x\n", val);
+}
+
+/**
+ * store_filter() - changes the scancode filter value
+ * @device:	the device descriptor
+ * @attr:	the device attribute struct
+ * @buf:	a pointer to the input buffer
+ * @len:	length of the input buffer
+ *
+ * This routine is for changing a scancode filter value or mask.
+ * It is trigged by writing to /sys/class/rc/rc?/[wakeup_]filter[_mask].
+ * Returns -EINVAL if an invalid filter value for the current protocol was
+ * specified or if scancode filtering is not supported by the driver, otherwise
+ * returns @len.
+ *
+ * Bits of the filter value corresponding to set bits in the filter mask are
+ * compared against input scancodes and non-matching scancodes are discarded.
+ *
+ * dev->lock is taken to guard against races between device registration,
+ * store_filter and show_filter.
+ */
+static ssize_t store_filter(struct device *device,
+			    struct device_attribute *attr,
+			    const char *buf, size_t len)
+{
+	struct rc_dev *dev = to_rc_dev(device);
+	struct rc_filter_attribute *fattr = to_rc_filter_attr(attr);
+	struct rc_scancode_filter new_filter, *filter;
+	int ret;
+	unsigned long val;
+	int (*set_filter)(struct rc_dev *dev, struct rc_scancode_filter *filter);
+	u64 *enabled_protocols;
+
+	/* Device is being removed */
+	if (!dev)
+		return -EINVAL;
+
+	ret = kstrtoul(buf, 0, &val);
+	if (ret < 0)
+		return ret;
+
+	if (fattr->type == RC_FILTER_NORMAL) {
+		set_filter = dev->s_filter;
+		enabled_protocols = &dev->enabled_protocols;
+		filter = &dev->scancode_filter;
+	} else {
+		set_filter = dev->s_wakeup_filter;
+		enabled_protocols = &dev->enabled_wakeup_protocols;
+		filter = &dev->scancode_wakeup_filter;
+	}
+
+	if (!set_filter)
+		return -EINVAL;
+
+	mutex_lock(&dev->lock);
+
+	new_filter = *filter;
+	if (fattr->mask)
+		new_filter.mask = val;
+	else
+		new_filter.data = val;
+
+	if (!*enabled_protocols && val) {
+		/* refuse to set a filter unless a protocol is enabled */
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	ret = set_filter(dev, &new_filter);
+	if (ret < 0)
+		goto unlock;
+
+	*filter = new_filter;
+
+unlock:
+	mutex_unlock(&dev->lock);
+	return (ret < 0) ? ret : len;
+}
+
+static void rc_dev_release(struct device *device)
+{
+}
+
+#define ADD_HOTPLUG_VAR(fmt, val...)					\
+	do {								\
+		int err = add_uevent_var(env, fmt, val);		\
+		if (err)						\
+			return err;					\
+	} while (0)
+
+static int rc_dev_uevent(struct device *device, struct kobj_uevent_env *env)
+{
+	struct rc_dev *dev = to_rc_dev(device);
+
+	if (dev->rc_map.name)
+		ADD_HOTPLUG_VAR("NAME=%s", dev->rc_map.name);
+	if (dev->driver_name)
+		ADD_HOTPLUG_VAR("DRV_NAME=%s", dev->driver_name);
+
+	return 0;
+}
+
+/*
+ * Static device attribute struct with the sysfs attributes for IR's
+ */
+static RC_PROTO_ATTR(protocols, S_IRUGO | S_IWUSR,
+		     show_protocols, store_protocols, RC_FILTER_NORMAL);
+static RC_PROTO_ATTR(wakeup_protocols, S_IRUGO | S_IWUSR,
+		     show_protocols, store_protocols, RC_FILTER_WAKEUP);
+static RC_FILTER_ATTR(filter, S_IRUGO|S_IWUSR,
+		      show_filter, store_filter, RC_FILTER_NORMAL, false);
+static RC_FILTER_ATTR(filter_mask, S_IRUGO|S_IWUSR,
+		      show_filter, store_filter, RC_FILTER_NORMAL, true);
+static RC_FILTER_ATTR(wakeup_filter, S_IRUGO|S_IWUSR,
+		      show_filter, store_filter, RC_FILTER_WAKEUP, false);
+static RC_FILTER_ATTR(wakeup_filter_mask, S_IRUGO|S_IWUSR,
+		      show_filter, store_filter, RC_FILTER_WAKEUP, true);
+
+static struct attribute *rc_dev_protocol_attrs[] = {
+	&dev_attr_protocols.attr.attr,
+	NULL,
+};
+
+static struct attribute_group rc_dev_protocol_attr_grp = {
+	.attrs	= rc_dev_protocol_attrs,
+};
+
+static struct attribute *rc_dev_wakeup_protocol_attrs[] = {
+	&dev_attr_wakeup_protocols.attr.attr,
+	NULL,
+};
+
+static struct attribute_group rc_dev_wakeup_protocol_attr_grp = {
+	.attrs	= rc_dev_wakeup_protocol_attrs,
+};
+
+static struct attribute *rc_dev_filter_attrs[] = {
+	&dev_attr_filter.attr.attr,
+	&dev_attr_filter_mask.attr.attr,
+	NULL,
+};
+
+static struct attribute_group rc_dev_filter_attr_grp = {
+	.attrs	= rc_dev_filter_attrs,
+};
+
+static struct attribute *rc_dev_wakeup_filter_attrs[] = {
+	&dev_attr_wakeup_filter.attr.attr,
+	&dev_attr_wakeup_filter_mask.attr.attr,
+	NULL,
+};
+
+static struct attribute_group rc_dev_wakeup_filter_attr_grp = {
+	.attrs	= rc_dev_wakeup_filter_attrs,
+};
+
+static struct device_type rc_dev_type = {
+	.release	= rc_dev_release,
+	.uevent		= rc_dev_uevent,
+};
+
+struct rc_dev *rc_allocate_device(void)
+{
+	struct rc_dev *dev;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return NULL;
+
+	dev->input_dev = input_allocate_device();
+	if (!dev->input_dev) {
+		kfree(dev);
+		return NULL;
+	}
+
+	dev->input_dev->getkeycode = ir_getkeycode;
+	dev->input_dev->setkeycode = ir_setkeycode;
+	input_set_drvdata(dev->input_dev, dev);
+
+	spin_lock_init(&dev->rc_map.lock);
+	spin_lock_init(&dev->keylock);
+	mutex_init(&dev->lock);
+	setup_timer(&dev->timer_keyup, ir_timer_keyup, (unsigned long)dev);
+
+	dev->dev.type = &rc_dev_type;
+	dev->dev.class = &rc_class;
+	device_initialize(&dev->dev);
+
+	__module_get(THIS_MODULE);
+	return dev;
+}
+EXPORT_SYMBOL_GPL(rc_allocate_device);
+
+void rc_free_device(struct rc_dev *dev)
+{
+	if (!dev)
+		return;
+
+	input_free_device(dev->input_dev);
+
+	put_device(&dev->dev);
+
+	kfree(dev);
+	module_put(THIS_MODULE);
+}
+EXPORT_SYMBOL_GPL(rc_free_device);
+
+int rc_register_device(struct rc_dev *dev)
+{
+	static bool raw_init = false; /* raw decoders loaded? */
+	struct rc_map *rc_map;
+	const char *path;
+	int attr = 0;
+	int minor;
+	int rc;
+
+	if (!dev || !dev->map_name)
+		return -EINVAL;
+
+	rc_map = rc_map_get(dev->map_name);
+	if (!rc_map)
+		rc_map = rc_map_get(RC_MAP_EMPTY);
+	if (!rc_map || !rc_map->scan || rc_map->size == 0)
+		return -EINVAL;
+
+	set_bit(EV_KEY, dev->input_dev->evbit);
+	set_bit(EV_REP, dev->input_dev->evbit);
+	set_bit(EV_MSC, dev->input_dev->evbit);
+	set_bit(MSC_SCAN, dev->input_dev->mscbit);
+	if (dev->open)
+		dev->input_dev->open = ir_open;
+	if (dev->close)
+		dev->input_dev->close = ir_close;
+
+	minor = ida_simple_get(&rc_ida, 0, RC_DEV_MAX, GFP_KERNEL);
+	if (minor < 0)
+		return minor;
+
+	dev->minor = minor;
+	dev_set_name(&dev->dev, "rc%u", dev->minor);
+	dev_set_drvdata(&dev->dev, dev);
+
+	dev->dev.groups = dev->sysfs_groups;
+	dev->sysfs_groups[attr++] = &rc_dev_protocol_attr_grp;
+	if (dev->s_filter)
+		dev->sysfs_groups[attr++] = &rc_dev_filter_attr_grp;
+	if (dev->s_wakeup_filter)
+		dev->sysfs_groups[attr++] = &rc_dev_wakeup_filter_attr_grp;
+	if (dev->change_wakeup_protocol)
+		dev->sysfs_groups[attr++] = &rc_dev_wakeup_protocol_attr_grp;
+	dev->sysfs_groups[attr++] = NULL;
+
+	/*
+	 * Take the lock here, as the device sysfs node will appear
+	 * when device_add() is called, which may trigger an ir-keytable udev
+	 * rule, which will in turn call show_protocols and access
+	 * dev->enabled_protocols before it has been initialized.
+	 */
+	mutex_lock(&dev->lock);
+
+	rc = device_add(&dev->dev);
+	if (rc)
+		goto out_unlock;
+
+	rc = ir_setkeytable(dev, rc_map);
+	if (rc)
+		goto out_dev;
+
+	dev->input_dev->dev.parent = &dev->dev;
+	memcpy(&dev->input_dev->id, &dev->input_id, sizeof(dev->input_id));
+	dev->input_dev->phys = dev->input_phys;
+	dev->input_dev->name = dev->input_name;
+
+	/* input_register_device can call ir_open, so unlock mutex here */
+	mutex_unlock(&dev->lock);
+
+	rc = input_register_device(dev->input_dev);
+
+	mutex_lock(&dev->lock);
+
+	if (rc)
+		goto out_table;
+
+	/*
+	 * Default delay of 250ms is too short for some protocols, especially
+	 * since the timeout is currently set to 250ms. Increase it to 500ms,
+	 * to avoid wrong repetition of the keycodes. Note that this must be
+	 * set after the call to input_register_device().
+	 */
+	dev->input_dev->rep[REP_DELAY] = 500;
+
+	/*
+	 * As a repeat event on protocols like RC-5 and NEC take as long as
+	 * 110/114ms, using 33ms as a repeat period is not the right thing
+	 * to do.
+	 */
+	dev->input_dev->rep[REP_PERIOD] = 125;
+
+	path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
+	printk(KERN_INFO "%s: %s as %s\n",
+		dev_name(&dev->dev),
+		dev->input_name ? dev->input_name : "Unspecified device",
+		path ? path : "N/A");
+	kfree(path);
+
+	if (dev->driver_type == RC_DRIVER_IR_RAW) {
+		/* Load raw decoders, if they aren't already */
+		if (!raw_init) {
+			IR_dprintk(1, "Loading raw decoders\n");
+			ir_raw_init();
+			raw_init = true;
+		}
+		/* calls ir_register_device so unlock mutex here*/
+		mutex_unlock(&dev->lock);
+		rc = ir_raw_event_register(dev);
+		mutex_lock(&dev->lock);
+		if (rc < 0)
+			goto out_input;
+	}
+
+	if (dev->change_protocol) {
+		u64 rc_type = (1ll << rc_map->rc_type);
+		rc = dev->change_protocol(dev, &rc_type);
+		if (rc < 0)
+			goto out_raw;
+		dev->enabled_protocols = rc_type;
+	}
+
+	mutex_unlock(&dev->lock);
+
+	IR_dprintk(1, "Registered rc%u (driver: %s, remote: %s, mode %s)\n",
+		   dev->minor,
+		   dev->driver_name ? dev->driver_name : "unknown",
+		   rc_map->name ? rc_map->name : "unknown",
+		   dev->driver_type == RC_DRIVER_IR_RAW ? "raw" : "cooked");
+
+	return 0;
+
+out_raw:
+	if (dev->driver_type == RC_DRIVER_IR_RAW)
+		ir_raw_event_unregister(dev);
+out_input:
+	input_unregister_device(dev->input_dev);
+	dev->input_dev = NULL;
+out_table:
+	ir_free_table(&dev->rc_map);
+out_dev:
+	device_del(&dev->dev);
+out_unlock:
+	mutex_unlock(&dev->lock);
+	ida_simple_remove(&rc_ida, minor);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(rc_register_device);
+
+void rc_unregister_device(struct rc_dev *dev)
+{
+	if (!dev)
+		return;
+
+	del_timer_sync(&dev->timer_keyup);
+
+	if (dev->driver_type == RC_DRIVER_IR_RAW)
+		ir_raw_event_unregister(dev);
+
+	/* Freeing the table should also call the stop callback */
+	ir_free_table(&dev->rc_map);
+	IR_dprintk(1, "Freed keycode table\n");
+
+	input_unregister_device(dev->input_dev);
+	dev->input_dev = NULL;
+
+	device_del(&dev->dev);
+
+	ida_simple_remove(&rc_ida, dev->minor);
+
+	rc_free_device(dev);
+}
+
+EXPORT_SYMBOL_GPL(rc_unregister_device);
+
+/*
+ * Init/exit code for the module. Basically, creates/removes /sys/class/rc
+ */
+
+static int __init rc_core_init(void)
+{
+	int rc = class_register(&rc_class);
+	if (rc) {
+		printk(KERN_ERR "rc_core: unable to register rc class\n");
+		return rc;
+	}
+
+	led_trigger_register_simple("rc-feedback", &led_feedback);
+	rc_map_register(&empty_map);
+
+	return 0;
+}
+
+static void __exit rc_core_exit(void)
+{
+	class_unregister(&rc_class);
+	led_trigger_unregister_simple(led_feedback);
+	rc_map_unregister(&empty_map);
+}
+
+subsys_initcall(rc_core_init);
+module_exit(rc_core_exit);
+
+int rc_core_debug;    /* ir_debug level (0,1,2) */
+EXPORT_SYMBOL_GPL(rc_core_debug);
+module_param_named(debug, rc_core_debug, int, 0644);
+
+MODULE_AUTHOR("Mauro Carvalho Chehab");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
new file mode 100644
index 0000000..ec74244
--- /dev/null
+++ b/drivers/media/rc/redrat3.c
@@ -0,0 +1,1098 @@
+/*
+ * USB RedRat3 IR Transceiver rc-core driver
+ *
+ * Copyright (c) 2011 by Jarod Wilson <jarod@redhat.com>
+ *  based heavily on the work of Stephen Cox, with additional
+ *  help from RedRat Ltd.
+ *
+ * This driver began life based an an old version of the first-generation
+ * lirc_mceusb driver from the lirc 0.7.2 distribution. It was then
+ * significantly rewritten by Stephen Cox with the aid of RedRat Ltd's
+ * Chris Dodge.
+ *
+ * The driver was then ported to rc-core and significantly rewritten again,
+ * by Jarod, using the in-kernel mceusb driver as a guide, after an initial
+ * port effort was started by Stephen.
+ *
+ * TODO LIST:
+ * - fix lirc not showing repeats properly
+ * --
+ *
+ * The RedRat3 is a USB transceiver with both send & receive,
+ * with 2 separate sensors available for receive to enable
+ * both good long range reception for general use, and good
+ * short range reception when required for learning a signal.
+ *
+ * http://www.redrat.co.uk/
+ *
+ * It uses its own little protocol to communicate, the required
+ * parts of which are embedded within this driver.
+ * --
+ *
+ * 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 <asm/unaligned.h>
+#include <linux/device.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <media/rc-core.h>
+
+/* Driver Information */
+#define DRIVER_AUTHOR "Jarod Wilson <jarod@redhat.com>"
+#define DRIVER_AUTHOR2 "The Dweller, Stephen Cox"
+#define DRIVER_DESC "RedRat3 USB IR Transceiver Driver"
+#define DRIVER_NAME "redrat3"
+
+/* bulk data transfer types */
+#define RR3_ERROR		0x01
+#define RR3_MOD_SIGNAL_IN	0x20
+#define RR3_MOD_SIGNAL_OUT	0x21
+
+/* Get the RR firmware version */
+#define RR3_FW_VERSION		0xb1
+#define RR3_FW_VERSION_LEN	64
+/* Send encoded signal bulk-sent earlier*/
+#define RR3_TX_SEND_SIGNAL	0xb3
+#define RR3_SET_IR_PARAM	0xb7
+#define RR3_GET_IR_PARAM	0xb8
+/* Blink the red LED on the device */
+#define RR3_BLINK_LED		0xb9
+/* Read serial number of device */
+#define RR3_READ_SER_NO		0xba
+#define RR3_SER_NO_LEN		4
+/* Start capture with the RC receiver */
+#define RR3_RC_DET_ENABLE	0xbb
+/* Stop capture with the RC receiver */
+#define RR3_RC_DET_DISABLE	0xbc
+/* Return the status of RC detector capture */
+#define RR3_RC_DET_STATUS	0xbd
+/* Reset redrat */
+#define RR3_RESET		0xa0
+
+/* Max number of lengths in the signal. */
+#define RR3_IR_IO_MAX_LENGTHS	0x01
+/* Periods to measure mod. freq. */
+#define RR3_IR_IO_PERIODS_MF	0x02
+/* Size of memory for main signal data */
+#define RR3_IR_IO_SIG_MEM_SIZE	0x03
+/* Delta value when measuring lengths */
+#define RR3_IR_IO_LENGTH_FUZZ	0x04
+/* Timeout for end of signal detection */
+#define RR3_IR_IO_SIG_TIMEOUT	0x05
+/* Minimum value for pause recognition. */
+#define RR3_IR_IO_MIN_PAUSE	0x06
+
+/* Clock freq. of EZ-USB chip */
+#define RR3_CLK			24000000
+/* Clock periods per timer count */
+#define RR3_CLK_PER_COUNT	12
+/* (RR3_CLK / RR3_CLK_PER_COUNT) */
+#define RR3_CLK_CONV_FACTOR	2000000
+/* USB bulk-in IR data endpoint address */
+#define RR3_BULK_IN_EP_ADDR	0x82
+
+/* Size of the fixed-length portion of the signal */
+#define RR3_DRIVER_MAXLENS	128
+#define RR3_MAX_SIG_SIZE	512
+#define RR3_TIME_UNIT		50
+#define RR3_END_OF_SIGNAL	0x7f
+#define RR3_TX_TRAILER_LEN	2
+#define RR3_RX_MIN_TIMEOUT	5
+#define RR3_RX_MAX_TIMEOUT	2000
+
+/* The 8051's CPUCS Register address */
+#define RR3_CPUCS_REG_ADDR	0x7f92
+
+#define USB_RR3USB_VENDOR_ID	0x112a
+#define USB_RR3USB_PRODUCT_ID	0x0001
+#define USB_RR3IIUSB_PRODUCT_ID	0x0005
+
+struct redrat3_header {
+	__be16 length;
+	__be16 transfer_type;
+} __packed;
+
+/* sending and receiving irdata */
+struct redrat3_irdata {
+	struct redrat3_header header;
+	__be32 pause;
+	__be16 mod_freq_count;
+	__be16 num_periods;
+	__u8 max_lengths;
+	__u8 no_lengths;
+	__be16 max_sig_size;
+	__be16 sig_size;
+	__u8 no_repeats;
+	__be16 lens[RR3_DRIVER_MAXLENS]; /* not aligned */
+	__u8 sigdata[RR3_MAX_SIG_SIZE];
+} __packed;
+
+/* firmware errors */
+struct redrat3_error {
+	struct redrat3_header header;
+	__be16 fw_error;
+} __packed;
+
+/* table of devices that work with this driver */
+static struct usb_device_id redrat3_dev_table[] = {
+	/* Original version of the RedRat3 */
+	{USB_DEVICE(USB_RR3USB_VENDOR_ID, USB_RR3USB_PRODUCT_ID)},
+	/* Second Version/release of the RedRat3 - RetRat3-II */
+	{USB_DEVICE(USB_RR3USB_VENDOR_ID, USB_RR3IIUSB_PRODUCT_ID)},
+	{}			/* Terminating entry */
+};
+
+/* Structure to hold all of our device specific stuff */
+struct redrat3_dev {
+	/* core device bits */
+	struct rc_dev *rc;
+	struct device *dev;
+
+	/* led control */
+	struct led_classdev led;
+	atomic_t flash;
+	struct usb_ctrlrequest flash_control;
+	struct urb *flash_urb;
+	u8 flash_in_buf;
+
+	/* save off the usb device pointer */
+	struct usb_device *udev;
+
+	/* the receive endpoint */
+	struct usb_endpoint_descriptor *ep_in;
+	/* the buffer to receive data */
+	void *bulk_in_buf;
+	/* urb used to read ir data */
+	struct urb *read_urb;
+
+	/* the send endpoint */
+	struct usb_endpoint_descriptor *ep_out;
+
+	/* usb dma */
+	dma_addr_t dma_in;
+
+	/* rx signal timeout timer */
+	struct timer_list rx_timeout;
+	u32 hw_timeout;
+
+	/* Is the device currently transmitting?*/
+	bool transmitting;
+
+	/* store for current packet */
+	struct redrat3_irdata irdata;
+	u16 bytes_read;
+
+	u32 carrier;
+
+	char name[64];
+	char phys[64];
+};
+
+/*
+ * redrat3_issue_async
+ *
+ *  Issues an async read to the ir data in port..
+ *  sets the callback to be redrat3_handle_async
+ */
+static void redrat3_issue_async(struct redrat3_dev *rr3)
+{
+	int res;
+
+	res = usb_submit_urb(rr3->read_urb, GFP_ATOMIC);
+	if (res)
+		dev_dbg(rr3->dev,
+			"%s: receive request FAILED! (res %d, len %d)\n",
+			__func__, res, rr3->read_urb->transfer_buffer_length);
+}
+
+static void redrat3_dump_fw_error(struct redrat3_dev *rr3, int code)
+{
+	if (!rr3->transmitting && (code != 0x40))
+		dev_info(rr3->dev, "fw error code 0x%02x: ", code);
+
+	switch (code) {
+	case 0x00:
+		pr_cont("No Error\n");
+		break;
+
+	/* Codes 0x20 through 0x2f are IR Firmware Errors */
+	case 0x20:
+		pr_cont("Initial signal pulse not long enough "
+			"to measure carrier frequency\n");
+		break;
+	case 0x21:
+		pr_cont("Not enough length values allocated for signal\n");
+		break;
+	case 0x22:
+		pr_cont("Not enough memory allocated for signal data\n");
+		break;
+	case 0x23:
+		pr_cont("Too many signal repeats\n");
+		break;
+	case 0x28:
+		pr_cont("Insufficient memory available for IR signal "
+			"data memory allocation\n");
+		break;
+	case 0x29:
+		pr_cont("Insufficient memory available "
+			"for IrDa signal data memory allocation\n");
+		break;
+
+	/* Codes 0x30 through 0x3f are USB Firmware Errors */
+	case 0x30:
+		pr_cont("Insufficient memory available for bulk "
+			"transfer structure\n");
+		break;
+
+	/*
+	 * Other error codes... These are primarily errors that can occur in
+	 * the control messages sent to the redrat
+	 */
+	case 0x40:
+		if (!rr3->transmitting)
+			pr_cont("Signal capture has been terminated\n");
+		break;
+	case 0x41:
+		pr_cont("Attempt to set/get and unknown signal I/O "
+			"algorithm parameter\n");
+		break;
+	case 0x42:
+		pr_cont("Signal capture already started\n");
+		break;
+
+	default:
+		pr_cont("Unknown Error\n");
+		break;
+	}
+}
+
+static u32 redrat3_val_to_mod_freq(struct redrat3_irdata *irdata)
+{
+	u32 mod_freq = 0;
+	u16 mod_freq_count = be16_to_cpu(irdata->mod_freq_count);
+
+	if (mod_freq_count != 0)
+		mod_freq = (RR3_CLK * be16_to_cpu(irdata->num_periods)) /
+			(mod_freq_count * RR3_CLK_PER_COUNT);
+
+	return mod_freq;
+}
+
+/* this function scales down the figures for the same result... */
+static u32 redrat3_len_to_us(u32 length)
+{
+	u32 biglen = length * 1000;
+	u32 divisor = (RR3_CLK_CONV_FACTOR) / 1000;
+	u32 result = (u32) (biglen / divisor);
+
+	/* don't allow zero lengths to go back, breaks lirc */
+	return result ? result : 1;
+}
+
+/*
+ * convert us back into redrat3 lengths
+ *
+ * length * 1000   length * 1000000
+ * ------------- = ---------------- = micro
+ * rr3clk / 1000       rr3clk
+
+ * 6 * 2       4 * 3        micro * rr3clk          micro * rr3clk / 1000
+ * ----- = 4   ----- = 6    -------------- = len    ---------------------
+ *   3           2             1000000                    1000
+ */
+static u32 redrat3_us_to_len(u32 microsec)
+{
+	u32 result;
+	u32 divisor;
+
+	microsec = (microsec > IR_MAX_DURATION) ? IR_MAX_DURATION : microsec;
+	divisor = (RR3_CLK_CONV_FACTOR / 1000);
+	result = (u32)(microsec * divisor) / 1000;
+
+	/* don't allow zero lengths to go back, breaks lirc */
+	return result ? result : 1;
+}
+
+/* timer callback to send reset event */
+static void redrat3_rx_timeout(unsigned long data)
+{
+	struct redrat3_dev *rr3 = (struct redrat3_dev *)data;
+
+	dev_dbg(rr3->dev, "calling ir_raw_event_reset\n");
+	ir_raw_event_reset(rr3->rc);
+}
+
+static void redrat3_process_ir_data(struct redrat3_dev *rr3)
+{
+	DEFINE_IR_RAW_EVENT(rawir);
+	struct device *dev;
+	unsigned i, trailer = 0;
+	unsigned sig_size, single_len, offset, val;
+	unsigned long delay;
+	u32 mod_freq;
+
+	if (!rr3) {
+		pr_err("%s called with no context!\n", __func__);
+		return;
+	}
+
+	dev = rr3->dev;
+
+	/* Make sure we reset the IR kfifo after a bit of inactivity */
+	delay = usecs_to_jiffies(rr3->hw_timeout);
+	mod_timer(&rr3->rx_timeout, jiffies + delay);
+
+	mod_freq = redrat3_val_to_mod_freq(&rr3->irdata);
+	dev_dbg(dev, "Got mod_freq of %u\n", mod_freq);
+
+	/* process each rr3 encoded byte into an int */
+	sig_size = be16_to_cpu(rr3->irdata.sig_size);
+	for (i = 0; i < sig_size; i++) {
+		offset = rr3->irdata.sigdata[i];
+		val = get_unaligned_be16(&rr3->irdata.lens[offset]);
+		single_len = redrat3_len_to_us(val);
+
+		/* we should always get pulse/space/pulse/space samples */
+		if (i % 2)
+			rawir.pulse = false;
+		else
+			rawir.pulse = true;
+
+		rawir.duration = US_TO_NS(single_len);
+		/* Save initial pulse length to fudge trailer */
+		if (i == 0)
+			trailer = rawir.duration;
+		/* cap the value to IR_MAX_DURATION */
+		rawir.duration = (rawir.duration > IR_MAX_DURATION) ?
+				 IR_MAX_DURATION : rawir.duration;
+
+		dev_dbg(dev, "storing %s with duration %d (i: %d)\n",
+			rawir.pulse ? "pulse" : "space", rawir.duration, i);
+		ir_raw_event_store_with_filter(rr3->rc, &rawir);
+	}
+
+	/* add a trailing space, if need be */
+	if (i % 2) {
+		rawir.pulse = false;
+		/* this duration is made up, and may not be ideal... */
+		if (trailer < US_TO_NS(1000))
+			rawir.duration = US_TO_NS(2800);
+		else
+			rawir.duration = trailer;
+		dev_dbg(dev, "storing trailing space with duration %d\n",
+			rawir.duration);
+		ir_raw_event_store_with_filter(rr3->rc, &rawir);
+	}
+
+	dev_dbg(dev, "calling ir_raw_event_handle\n");
+	ir_raw_event_handle(rr3->rc);
+}
+
+/* Util fn to send rr3 cmds */
+static int redrat3_send_cmd(int cmd, struct redrat3_dev *rr3)
+{
+	struct usb_device *udev;
+	u8 *data;
+	int res;
+
+	data = kzalloc(sizeof(u8), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	udev = rr3->udev;
+	res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), cmd,
+			      USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+			      0x0000, 0x0000, data, sizeof(u8), HZ * 10);
+
+	if (res < 0) {
+		dev_err(rr3->dev, "%s: Error sending rr3 cmd res %d, data %d",
+			__func__, res, *data);
+		res = -EIO;
+	} else
+		res = data[0];
+
+	kfree(data);
+
+	return res;
+}
+
+/* Enables the long range detector and starts async receive */
+static int redrat3_enable_detector(struct redrat3_dev *rr3)
+{
+	struct device *dev = rr3->dev;
+	u8 ret;
+
+	ret = redrat3_send_cmd(RR3_RC_DET_ENABLE, rr3);
+	if (ret != 0)
+		dev_dbg(dev, "%s: unexpected ret of %d\n",
+			__func__, ret);
+
+	ret = redrat3_send_cmd(RR3_RC_DET_STATUS, rr3);
+	if (ret != 1) {
+		dev_err(dev, "%s: detector status: %d, should be 1\n",
+			__func__, ret);
+		return -EIO;
+	}
+
+	redrat3_issue_async(rr3);
+
+	return 0;
+}
+
+static inline void redrat3_delete(struct redrat3_dev *rr3,
+				  struct usb_device *udev)
+{
+	usb_kill_urb(rr3->read_urb);
+	usb_kill_urb(rr3->flash_urb);
+	usb_free_urb(rr3->read_urb);
+	usb_free_urb(rr3->flash_urb);
+	usb_free_coherent(udev, le16_to_cpu(rr3->ep_in->wMaxPacketSize),
+			  rr3->bulk_in_buf, rr3->dma_in);
+
+	kfree(rr3);
+}
+
+static u32 redrat3_get_timeout(struct redrat3_dev *rr3)
+{
+	__be32 *tmp;
+	u32 timeout = MS_TO_US(150); /* a sane default, if things go haywire */
+	int len, ret, pipe;
+
+	len = sizeof(*tmp);
+	tmp = kzalloc(len, GFP_KERNEL);
+	if (!tmp) {
+		dev_warn(rr3->dev, "Memory allocation faillure\n");
+		return timeout;
+	}
+
+	pipe = usb_rcvctrlpipe(rr3->udev, 0);
+	ret = usb_control_msg(rr3->udev, pipe, RR3_GET_IR_PARAM,
+			      USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+			      RR3_IR_IO_SIG_TIMEOUT, 0, tmp, len, HZ * 5);
+	if (ret != len)
+		dev_warn(rr3->dev, "Failed to read timeout from hardware\n");
+	else {
+		timeout = redrat3_len_to_us(be32_to_cpup(tmp));
+
+		dev_dbg(rr3->dev, "Got timeout of %d ms\n", timeout / 1000);
+	}
+
+	kfree(tmp);
+
+	return timeout;
+}
+
+static void redrat3_reset(struct redrat3_dev *rr3)
+{
+	struct usb_device *udev = rr3->udev;
+	struct device *dev = rr3->dev;
+	int rc, rxpipe, txpipe;
+	u8 *val;
+	int len = sizeof(u8);
+
+	rxpipe = usb_rcvctrlpipe(udev, 0);
+	txpipe = usb_sndctrlpipe(udev, 0);
+
+	val = kmalloc(len, GFP_KERNEL);
+	if (!val) {
+		dev_err(dev, "Memory allocation failure\n");
+		return;
+	}
+
+	*val = 0x01;
+	rc = usb_control_msg(udev, rxpipe, RR3_RESET,
+			     USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+			     RR3_CPUCS_REG_ADDR, 0, val, len, HZ * 25);
+	dev_dbg(dev, "reset returned 0x%02x\n", rc);
+
+	*val = 5;
+	rc = usb_control_msg(udev, txpipe, RR3_SET_IR_PARAM,
+			     USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+			     RR3_IR_IO_LENGTH_FUZZ, 0, val, len, HZ * 25);
+	dev_dbg(dev, "set ir parm len fuzz %d rc 0x%02x\n", *val, rc);
+
+	*val = RR3_DRIVER_MAXLENS;
+	rc = usb_control_msg(udev, txpipe, RR3_SET_IR_PARAM,
+			     USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+			     RR3_IR_IO_MAX_LENGTHS, 0, val, len, HZ * 25);
+	dev_dbg(dev, "set ir parm max lens %d rc 0x%02x\n", *val, rc);
+
+	kfree(val);
+}
+
+static void redrat3_get_firmware_rev(struct redrat3_dev *rr3)
+{
+	int rc = 0;
+	char *buffer;
+
+	buffer = kzalloc(sizeof(char) * (RR3_FW_VERSION_LEN + 1), GFP_KERNEL);
+	if (!buffer) {
+		dev_err(rr3->dev, "Memory allocation failure\n");
+		return;
+	}
+
+	rc = usb_control_msg(rr3->udev, usb_rcvctrlpipe(rr3->udev, 0),
+			     RR3_FW_VERSION,
+			     USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+			     0, 0, buffer, RR3_FW_VERSION_LEN, HZ * 5);
+
+	if (rc >= 0)
+		dev_info(rr3->dev, "Firmware rev: %s", buffer);
+	else
+		dev_err(rr3->dev, "Problem fetching firmware ID\n");
+
+	kfree(buffer);
+}
+
+static void redrat3_read_packet_start(struct redrat3_dev *rr3, unsigned len)
+{
+	struct redrat3_header *header = rr3->bulk_in_buf;
+	unsigned pktlen, pkttype;
+
+	/* grab the Length and type of transfer */
+	pktlen = be16_to_cpu(header->length);
+	pkttype = be16_to_cpu(header->transfer_type);
+
+	if (pktlen > sizeof(rr3->irdata)) {
+		dev_warn(rr3->dev, "packet length %u too large\n", pktlen);
+		return;
+	}
+
+	switch (pkttype) {
+	case RR3_ERROR:
+		if (len >= sizeof(struct redrat3_error)) {
+			struct redrat3_error *error = rr3->bulk_in_buf;
+			unsigned fw_error = be16_to_cpu(error->fw_error);
+			redrat3_dump_fw_error(rr3, fw_error);
+		}
+		break;
+
+	case RR3_MOD_SIGNAL_IN:
+		memcpy(&rr3->irdata, rr3->bulk_in_buf, len);
+		rr3->bytes_read = len;
+		dev_dbg(rr3->dev, "bytes_read %d, pktlen %d\n",
+			rr3->bytes_read, pktlen);
+		break;
+
+	default:
+		dev_dbg(rr3->dev, "ignoring packet with type 0x%02x, len of %d, 0x%02x\n",
+						pkttype, len, pktlen);
+		break;
+	}
+}
+
+static void redrat3_read_packet_continue(struct redrat3_dev *rr3, unsigned len)
+{
+	void *irdata = &rr3->irdata;
+
+	if (len + rr3->bytes_read > sizeof(rr3->irdata)) {
+		dev_warn(rr3->dev, "too much data for packet\n");
+		rr3->bytes_read = 0;
+		return;
+	}
+
+	memcpy(irdata + rr3->bytes_read, rr3->bulk_in_buf, len);
+
+	rr3->bytes_read += len;
+	dev_dbg(rr3->dev, "bytes_read %d, pktlen %d\n", rr3->bytes_read,
+				 be16_to_cpu(rr3->irdata.header.length));
+}
+
+/* gather IR data from incoming urb, process it when we have enough */
+static int redrat3_get_ir_data(struct redrat3_dev *rr3, unsigned len)
+{
+	struct device *dev = rr3->dev;
+	unsigned pkttype;
+	int ret = 0;
+
+	if (rr3->bytes_read == 0 && len >= sizeof(struct redrat3_header)) {
+		redrat3_read_packet_start(rr3, len);
+	} else if (rr3->bytes_read != 0) {
+		redrat3_read_packet_continue(rr3, len);
+	} else if (rr3->bytes_read == 0) {
+		dev_err(dev, "error: no packet data read\n");
+		ret = -ENODATA;
+		goto out;
+	}
+
+	if (rr3->bytes_read < be16_to_cpu(rr3->irdata.header.length) +
+						sizeof(struct redrat3_header))
+		/* we're still accumulating data */
+		return 0;
+
+	/* if we get here, we've got IR data to decode */
+	pkttype = be16_to_cpu(rr3->irdata.header.transfer_type);
+	if (pkttype == RR3_MOD_SIGNAL_IN)
+		redrat3_process_ir_data(rr3);
+	else
+		dev_dbg(dev, "discarding non-signal data packet (type 0x%02x)\n",
+								pkttype);
+
+out:
+	rr3->bytes_read = 0;
+	return ret;
+}
+
+/* callback function from USB when async USB request has completed */
+static void redrat3_handle_async(struct urb *urb)
+{
+	struct redrat3_dev *rr3;
+	int ret;
+
+	if (!urb)
+		return;
+
+	rr3 = urb->context;
+	if (!rr3) {
+		pr_err("%s called with invalid context!\n", __func__);
+		usb_unlink_urb(urb);
+		return;
+	}
+
+	switch (urb->status) {
+	case 0:
+		ret = redrat3_get_ir_data(rr3, urb->actual_length);
+		if (!ret) {
+			/* no error, prepare to read more */
+			redrat3_issue_async(rr3);
+		}
+		break;
+
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		usb_unlink_urb(urb);
+		return;
+
+	case -EPIPE:
+	default:
+		dev_warn(rr3->dev, "Error: urb status = %d\n", urb->status);
+		rr3->bytes_read = 0;
+		break;
+	}
+}
+
+static u16 mod_freq_to_val(unsigned int mod_freq)
+{
+	int mult = 6000000;
+
+	/* Clk used in mod. freq. generation is CLK24/4. */
+	return 65536 - (mult / mod_freq);
+}
+
+static int redrat3_set_tx_carrier(struct rc_dev *rcdev, u32 carrier)
+{
+	struct redrat3_dev *rr3 = rcdev->priv;
+	struct device *dev = rr3->dev;
+
+	dev_dbg(dev, "Setting modulation frequency to %u", carrier);
+	if (carrier == 0)
+		return -EINVAL;
+
+	rr3->carrier = carrier;
+
+	return carrier;
+}
+
+static int redrat3_transmit_ir(struct rc_dev *rcdev, unsigned *txbuf,
+				unsigned count)
+{
+	struct redrat3_dev *rr3 = rcdev->priv;
+	struct device *dev = rr3->dev;
+	struct redrat3_irdata *irdata = NULL;
+	int ret, ret_len;
+	int lencheck, cur_sample_len, pipe;
+	int *sample_lens = NULL;
+	u8 curlencheck = 0;
+	unsigned i, sendbuf_len;
+
+	if (rr3->transmitting) {
+		dev_warn(dev, "%s: transmitter already in use\n", __func__);
+		return -EAGAIN;
+	}
+
+	if (count > RR3_MAX_SIG_SIZE - RR3_TX_TRAILER_LEN)
+		return -EINVAL;
+
+	/* rr3 will disable rc detector on transmit */
+	rr3->transmitting = true;
+
+	sample_lens = kzalloc(sizeof(int) * RR3_DRIVER_MAXLENS, GFP_KERNEL);
+	if (!sample_lens) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	irdata = kzalloc(sizeof(*irdata), GFP_KERNEL);
+	if (!irdata) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < count; i++) {
+		cur_sample_len = redrat3_us_to_len(txbuf[i]);
+		if (cur_sample_len > 0xffff) {
+			dev_warn(dev, "transmit period of %uus truncated to %uus\n",
+					txbuf[i], redrat3_len_to_us(0xffff));
+			cur_sample_len = 0xffff;
+		}
+		for (lencheck = 0; lencheck < curlencheck; lencheck++) {
+			if (sample_lens[lencheck] == cur_sample_len)
+				break;
+		}
+		if (lencheck == curlencheck) {
+			dev_dbg(dev, "txbuf[%d]=%u, pos %d, enc %u\n",
+				i, txbuf[i], curlencheck, cur_sample_len);
+			if (curlencheck < RR3_DRIVER_MAXLENS) {
+				/* now convert the value to a proper
+				 * rr3 value.. */
+				sample_lens[curlencheck] = cur_sample_len;
+				put_unaligned_be16(cur_sample_len,
+						&irdata->lens[curlencheck]);
+				curlencheck++;
+			} else {
+				ret = -EINVAL;
+				goto out;
+			}
+		}
+		irdata->sigdata[i] = lencheck;
+	}
+
+	irdata->sigdata[count] = RR3_END_OF_SIGNAL;
+	irdata->sigdata[count + 1] = RR3_END_OF_SIGNAL;
+
+	sendbuf_len = offsetof(struct redrat3_irdata,
+					sigdata[count + RR3_TX_TRAILER_LEN]);
+	/* fill in our packet header */
+	irdata->header.length = cpu_to_be16(sendbuf_len -
+						sizeof(struct redrat3_header));
+	irdata->header.transfer_type = cpu_to_be16(RR3_MOD_SIGNAL_OUT);
+	irdata->pause = cpu_to_be32(redrat3_len_to_us(100));
+	irdata->mod_freq_count = cpu_to_be16(mod_freq_to_val(rr3->carrier));
+	irdata->no_lengths = curlencheck;
+	irdata->sig_size = cpu_to_be16(count + RR3_TX_TRAILER_LEN);
+
+	pipe = usb_sndbulkpipe(rr3->udev, rr3->ep_out->bEndpointAddress);
+	ret = usb_bulk_msg(rr3->udev, pipe, irdata,
+			    sendbuf_len, &ret_len, 10 * HZ);
+	dev_dbg(dev, "sent %d bytes, (ret %d)\n", ret_len, ret);
+
+	/* now tell the hardware to transmit what we sent it */
+	pipe = usb_rcvctrlpipe(rr3->udev, 0);
+	ret = usb_control_msg(rr3->udev, pipe, RR3_TX_SEND_SIGNAL,
+			      USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+			      0, 0, irdata, 2, HZ * 10);
+
+	if (ret < 0)
+		dev_err(dev, "Error: control msg send failed, rc %d\n", ret);
+	else
+		ret = count;
+
+out:
+	kfree(sample_lens);
+	kfree(irdata);
+
+	rr3->transmitting = false;
+	/* rr3 re-enables rc detector because it was enabled before */
+
+	return ret;
+}
+
+static void redrat3_brightness_set(struct led_classdev *led_dev, enum
+						led_brightness brightness)
+{
+	struct redrat3_dev *rr3 = container_of(led_dev, struct redrat3_dev,
+									led);
+
+	if (brightness != LED_OFF && atomic_cmpxchg(&rr3->flash, 0, 1) == 0) {
+		int ret = usb_submit_urb(rr3->flash_urb, GFP_ATOMIC);
+		if (ret != 0) {
+			dev_dbg(rr3->dev, "%s: unexpected ret of %d\n",
+				__func__, ret);
+			atomic_set(&rr3->flash, 0);
+		}
+	}
+}
+
+static void redrat3_led_complete(struct urb *urb)
+{
+	struct redrat3_dev *rr3 = urb->context;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		usb_unlink_urb(urb);
+		return;
+	case -EPIPE:
+	default:
+		dev_dbg(rr3->dev, "Error: urb status = %d\n", urb->status);
+		break;
+	}
+
+	rr3->led.brightness = LED_OFF;
+	atomic_dec(&rr3->flash);
+}
+
+static struct rc_dev *redrat3_init_rc_dev(struct redrat3_dev *rr3)
+{
+	struct device *dev = rr3->dev;
+	struct rc_dev *rc;
+	int ret = -ENODEV;
+	u16 prod = le16_to_cpu(rr3->udev->descriptor.idProduct);
+
+	rc = rc_allocate_device();
+	if (!rc) {
+		dev_err(dev, "remote input dev allocation failed\n");
+		goto out;
+	}
+
+	snprintf(rr3->name, sizeof(rr3->name), "RedRat3%s "
+		 "Infrared Remote Transceiver (%04x:%04x)",
+		 prod == USB_RR3IIUSB_PRODUCT_ID ? "-II" : "",
+		 le16_to_cpu(rr3->udev->descriptor.idVendor), prod);
+
+	usb_make_path(rr3->udev, rr3->phys, sizeof(rr3->phys));
+
+	rc->input_name = rr3->name;
+	rc->input_phys = rr3->phys;
+	usb_to_input_id(rr3->udev, &rc->input_id);
+	rc->dev.parent = dev;
+	rc->priv = rr3;
+	rc->driver_type = RC_DRIVER_IR_RAW;
+	rc->allowed_protocols = RC_BIT_ALL;
+	rc->timeout = US_TO_NS(2750);
+	rc->tx_ir = redrat3_transmit_ir;
+	rc->s_tx_carrier = redrat3_set_tx_carrier;
+	rc->driver_name = DRIVER_NAME;
+	rc->rx_resolution = US_TO_NS(2);
+	rc->map_name = RC_MAP_HAUPPAUGE;
+
+	ret = rc_register_device(rc);
+	if (ret < 0) {
+		dev_err(dev, "remote dev registration failed\n");
+		goto out;
+	}
+
+	return rc;
+
+out:
+	rc_free_device(rc);
+	return NULL;
+}
+
+static int redrat3_dev_probe(struct usb_interface *intf,
+			     const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct device *dev = &intf->dev;
+	struct usb_host_interface *uhi;
+	struct redrat3_dev *rr3;
+	struct usb_endpoint_descriptor *ep;
+	struct usb_endpoint_descriptor *ep_in = NULL;
+	struct usb_endpoint_descriptor *ep_out = NULL;
+	u8 addr, attrs;
+	int pipe, i;
+	int retval = -ENOMEM;
+
+	uhi = intf->cur_altsetting;
+
+	/* find our bulk-in and bulk-out endpoints */
+	for (i = 0; i < uhi->desc.bNumEndpoints; ++i) {
+		ep = &uhi->endpoint[i].desc;
+		addr = ep->bEndpointAddress;
+		attrs = ep->bmAttributes;
+
+		if ((ep_in == NULL) &&
+		    ((addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) &&
+		    ((attrs & USB_ENDPOINT_XFERTYPE_MASK) ==
+		     USB_ENDPOINT_XFER_BULK)) {
+			dev_dbg(dev, "found bulk-in endpoint at 0x%02x\n",
+				ep->bEndpointAddress);
+			/* data comes in on 0x82, 0x81 is for other data... */
+			if (ep->bEndpointAddress == RR3_BULK_IN_EP_ADDR)
+				ep_in = ep;
+		}
+
+		if ((ep_out == NULL) &&
+		    ((addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) &&
+		    ((attrs & USB_ENDPOINT_XFERTYPE_MASK) ==
+		     USB_ENDPOINT_XFER_BULK)) {
+			dev_dbg(dev, "found bulk-out endpoint at 0x%02x\n",
+				ep->bEndpointAddress);
+			ep_out = ep;
+		}
+	}
+
+	if (!ep_in || !ep_out) {
+		dev_err(dev, "Couldn't find both in and out endpoints\n");
+		retval = -ENODEV;
+		goto no_endpoints;
+	}
+
+	/* allocate memory for our device state and initialize it */
+	rr3 = kzalloc(sizeof(*rr3), GFP_KERNEL);
+	if (rr3 == NULL) {
+		dev_err(dev, "Memory allocation failure\n");
+		goto no_endpoints;
+	}
+
+	rr3->dev = &intf->dev;
+
+	/* set up bulk-in endpoint */
+	rr3->read_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!rr3->read_urb) {
+		dev_err(dev, "Read urb allocation failure\n");
+		goto error;
+	}
+
+	rr3->ep_in = ep_in;
+	rr3->bulk_in_buf = usb_alloc_coherent(udev,
+		le16_to_cpu(ep_in->wMaxPacketSize), GFP_KERNEL, &rr3->dma_in);
+	if (!rr3->bulk_in_buf) {
+		dev_err(dev, "Read buffer allocation failure\n");
+		goto error;
+	}
+
+	pipe = usb_rcvbulkpipe(udev, ep_in->bEndpointAddress);
+	usb_fill_bulk_urb(rr3->read_urb, udev, pipe, rr3->bulk_in_buf,
+		le16_to_cpu(ep_in->wMaxPacketSize), redrat3_handle_async, rr3);
+	rr3->read_urb->transfer_dma = rr3->dma_in;
+	rr3->read_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	rr3->ep_out = ep_out;
+	rr3->udev = udev;
+
+	redrat3_reset(rr3);
+	redrat3_get_firmware_rev(rr3);
+
+	/* might be all we need to do? */
+	retval = redrat3_enable_detector(rr3);
+	if (retval < 0)
+		goto error;
+
+	/* store current hardware timeout, in us, will use for kfifo resets */
+	rr3->hw_timeout = redrat3_get_timeout(rr3);
+
+	/* default.. will get overridden by any sends with a freq defined */
+	rr3->carrier = 38000;
+
+	/* led control */
+	rr3->led.name = "redrat3:red:feedback";
+	rr3->led.default_trigger = "rc-feedback";
+	rr3->led.brightness_set = redrat3_brightness_set;
+	retval = led_classdev_register(&intf->dev, &rr3->led);
+	if (retval)
+		goto error;
+
+	atomic_set(&rr3->flash, 0);
+	rr3->flash_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!rr3->flash_urb) {
+		retval = -ENOMEM;
+		goto led_free_error;
+	}
+
+	/* setup packet is 'c0 b9 0000 0000 0001' */
+	rr3->flash_control.bRequestType = 0xc0;
+	rr3->flash_control.bRequest = RR3_BLINK_LED;
+	rr3->flash_control.wLength = cpu_to_le16(1);
+
+	usb_fill_control_urb(rr3->flash_urb, udev, usb_rcvctrlpipe(udev, 0),
+			(unsigned char *)&rr3->flash_control,
+			&rr3->flash_in_buf, sizeof(rr3->flash_in_buf),
+			redrat3_led_complete, rr3);
+
+	rr3->rc = redrat3_init_rc_dev(rr3);
+	if (!rr3->rc) {
+		retval = -ENOMEM;
+		goto led_free_error;
+	}
+	setup_timer(&rr3->rx_timeout, redrat3_rx_timeout, (unsigned long)rr3);
+
+	/* we can register the device now, as it is ready */
+	usb_set_intfdata(intf, rr3);
+
+	return 0;
+
+led_free_error:
+	led_classdev_unregister(&rr3->led);
+error:
+	redrat3_delete(rr3, rr3->udev);
+
+no_endpoints:
+	dev_err(dev, "%s: retval = %x", __func__, retval);
+
+	return retval;
+}
+
+static void redrat3_dev_disconnect(struct usb_interface *intf)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct redrat3_dev *rr3 = usb_get_intfdata(intf);
+
+	if (!rr3)
+		return;
+
+	usb_set_intfdata(intf, NULL);
+	rc_unregister_device(rr3->rc);
+	led_classdev_unregister(&rr3->led);
+	del_timer_sync(&rr3->rx_timeout);
+	redrat3_delete(rr3, udev);
+}
+
+static int redrat3_dev_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct redrat3_dev *rr3 = usb_get_intfdata(intf);
+
+	led_classdev_suspend(&rr3->led);
+	usb_kill_urb(rr3->read_urb);
+	usb_kill_urb(rr3->flash_urb);
+	return 0;
+}
+
+static int redrat3_dev_resume(struct usb_interface *intf)
+{
+	struct redrat3_dev *rr3 = usb_get_intfdata(intf);
+
+	if (usb_submit_urb(rr3->read_urb, GFP_ATOMIC))
+		return -EIO;
+	led_classdev_resume(&rr3->led);
+	return 0;
+}
+
+static struct usb_driver redrat3_dev_driver = {
+	.name		= DRIVER_NAME,
+	.probe		= redrat3_dev_probe,
+	.disconnect	= redrat3_dev_disconnect,
+	.suspend	= redrat3_dev_suspend,
+	.resume		= redrat3_dev_resume,
+	.reset_resume	= redrat3_dev_resume,
+	.id_table	= redrat3_dev_table
+};
+
+module_usb_driver(redrat3_dev_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_AUTHOR(DRIVER_AUTHOR2);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, redrat3_dev_table);
diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c
new file mode 100644
index 0000000..37d0401
--- /dev/null
+++ b/drivers/media/rc/st_rc.c
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2013 STMicroelectronics Limited
+ * Author: Srinivas Kandagatla <srinivas.kandagatla@st.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.
+ */
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <media/rc-core.h>
+#include <linux/pinctrl/consumer.h>
+
+struct st_rc_device {
+	struct device			*dev;
+	int				irq;
+	int				irq_wake;
+	struct clk			*sys_clock;
+	void __iomem			*base;	/* Register base address */
+	void __iomem			*rx_base;/* RX Register base address */
+	struct rc_dev			*rdev;
+	bool				overclocking;
+	int				sample_mult;
+	int				sample_div;
+	bool				rxuhfmode;
+	struct	reset_control		*rstc;
+};
+
+/* Registers */
+#define IRB_SAMPLE_RATE_COMM	0x64	/* sample freq divisor*/
+#define IRB_CLOCK_SEL		0x70	/* clock select       */
+#define IRB_CLOCK_SEL_STATUS	0x74	/* clock status       */
+/* IRB IR/UHF receiver registers */
+#define IRB_RX_ON               0x40	/* pulse time capture */
+#define IRB_RX_SYS              0X44	/* sym period capture */
+#define IRB_RX_INT_EN           0x48	/* IRQ enable (R/W)   */
+#define IRB_RX_INT_STATUS       0x4c	/* IRQ status (R/W)   */
+#define IRB_RX_EN               0x50	/* Receive enable     */
+#define IRB_MAX_SYM_PERIOD      0x54	/* max sym value      */
+#define IRB_RX_INT_CLEAR        0x58	/* overrun status     */
+#define IRB_RX_STATUS           0x6c	/* receive status     */
+#define IRB_RX_NOISE_SUPPR      0x5c	/* noise suppression  */
+#define IRB_RX_POLARITY_INV     0x68	/* polarity inverter  */
+
+/**
+ * IRQ set: Enable full FIFO                 1  -> bit  3;
+ *          Enable overrun IRQ               1  -> bit  2;
+ *          Enable last symbol IRQ           1  -> bit  1:
+ *          Enable RX interrupt              1  -> bit  0;
+ */
+#define IRB_RX_INTS		0x0f
+#define IRB_RX_OVERRUN_INT	0x04
+ /* maximum symbol period (microsecs),timeout to detect end of symbol train */
+#define MAX_SYMB_TIME		0x5000
+#define IRB_SAMPLE_FREQ		10000000
+#define	IRB_FIFO_NOT_EMPTY	0xff00
+#define IRB_OVERFLOW		0x4
+#define IRB_TIMEOUT		0xffff
+#define IR_ST_NAME "st-rc"
+
+static void st_rc_send_lirc_timeout(struct rc_dev *rdev)
+{
+	DEFINE_IR_RAW_EVENT(ev);
+	ev.timeout = true;
+	ir_raw_event_store(rdev, &ev);
+}
+
+/**
+ * RX graphical example to better understand the difference between ST IR block
+ * output and standard definition used by LIRC (and most of the world!)
+ *
+ *           mark                                     mark
+ *      |-IRB_RX_ON-|                            |-IRB_RX_ON-|
+ *      ___  ___  ___                            ___  ___  ___             _
+ *      | |  | |  | |                            | |  | |  | |             |
+ *      | |  | |  | |         space 0            | |  | |  | |   space 1   |
+ * _____| |__| |__| |____________________________| |__| |__| |_____________|
+ *
+ *      |--------------- IRB_RX_SYS -------------|------ IRB_RX_SYS -------|
+ *
+ *      |------------- encoding bit 0 -----------|---- encoding bit 1 -----|
+ *
+ * ST hardware returns mark (IRB_RX_ON) and total symbol time (IRB_RX_SYS), so
+ * convert to standard mark/space we have to calculate space=(IRB_RX_SYS-mark)
+ * The mark time represents the amount of time the carrier (usually 36-40kHz)
+ * is detected.The above examples shows Pulse Width Modulation encoding where
+ * bit 0 is represented by space>mark.
+ */
+
+static irqreturn_t st_rc_rx_interrupt(int irq, void *data)
+{
+	unsigned int symbol, mark = 0;
+	struct st_rc_device *dev = data;
+	int last_symbol = 0;
+	u32 status;
+	DEFINE_IR_RAW_EVENT(ev);
+
+	if (dev->irq_wake)
+		pm_wakeup_event(dev->dev, 0);
+
+	status  = readl(dev->rx_base + IRB_RX_STATUS);
+
+	while (status & (IRB_FIFO_NOT_EMPTY | IRB_OVERFLOW)) {
+		u32 int_status = readl(dev->rx_base + IRB_RX_INT_STATUS);
+		if (unlikely(int_status & IRB_RX_OVERRUN_INT)) {
+			/* discard the entire collection in case of errors!  */
+			ir_raw_event_reset(dev->rdev);
+			dev_info(dev->dev, "IR RX overrun\n");
+			writel(IRB_RX_OVERRUN_INT,
+					dev->rx_base + IRB_RX_INT_CLEAR);
+			continue;
+		}
+
+		symbol = readl(dev->rx_base + IRB_RX_SYS);
+		mark = readl(dev->rx_base + IRB_RX_ON);
+
+		if (symbol == IRB_TIMEOUT)
+			last_symbol = 1;
+
+		 /* Ignore any noise */
+		if ((mark > 2) && (symbol > 1)) {
+			symbol -= mark;
+			if (dev->overclocking) { /* adjustments to timings */
+				symbol *= dev->sample_mult;
+				symbol /= dev->sample_div;
+				mark *= dev->sample_mult;
+				mark /= dev->sample_div;
+			}
+
+			ev.duration = US_TO_NS(mark);
+			ev.pulse = true;
+			ir_raw_event_store(dev->rdev, &ev);
+
+			if (!last_symbol) {
+				ev.duration = US_TO_NS(symbol);
+				ev.pulse = false;
+				ir_raw_event_store(dev->rdev, &ev);
+			} else  {
+				st_rc_send_lirc_timeout(dev->rdev);
+			}
+
+		}
+		last_symbol = 0;
+		status  = readl(dev->rx_base + IRB_RX_STATUS);
+	}
+
+	writel(IRB_RX_INTS, dev->rx_base + IRB_RX_INT_CLEAR);
+
+	/* Empty software fifo */
+	ir_raw_event_handle(dev->rdev);
+	return IRQ_HANDLED;
+}
+
+static void st_rc_hardware_init(struct st_rc_device *dev)
+{
+	int baseclock, freqdiff;
+	unsigned int rx_max_symbol_per = MAX_SYMB_TIME;
+	unsigned int rx_sampling_freq_div;
+
+	/* Enable the IP */
+	if (dev->rstc)
+		reset_control_deassert(dev->rstc);
+
+	clk_prepare_enable(dev->sys_clock);
+	baseclock = clk_get_rate(dev->sys_clock);
+
+	/* IRB input pins are inverted internally from high to low. */
+	writel(1, dev->rx_base + IRB_RX_POLARITY_INV);
+
+	rx_sampling_freq_div = baseclock / IRB_SAMPLE_FREQ;
+	writel(rx_sampling_freq_div, dev->base + IRB_SAMPLE_RATE_COMM);
+
+	freqdiff = baseclock - (rx_sampling_freq_div * IRB_SAMPLE_FREQ);
+	if (freqdiff) { /* over clocking, workout the adjustment factors */
+		dev->overclocking = true;
+		dev->sample_mult = 1000;
+		dev->sample_div = baseclock / (10000 * rx_sampling_freq_div);
+		rx_max_symbol_per = (rx_max_symbol_per * 1000)/dev->sample_div;
+	}
+
+	writel(rx_max_symbol_per, dev->rx_base + IRB_MAX_SYM_PERIOD);
+}
+
+static int st_rc_remove(struct platform_device *pdev)
+{
+	struct st_rc_device *rc_dev = platform_get_drvdata(pdev);
+	clk_disable_unprepare(rc_dev->sys_clock);
+	rc_unregister_device(rc_dev->rdev);
+	return 0;
+}
+
+static int st_rc_open(struct rc_dev *rdev)
+{
+	struct st_rc_device *dev = rdev->priv;
+	unsigned long flags;
+	local_irq_save(flags);
+	/* enable interrupts and receiver */
+	writel(IRB_RX_INTS, dev->rx_base + IRB_RX_INT_EN);
+	writel(0x01, dev->rx_base + IRB_RX_EN);
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+static void st_rc_close(struct rc_dev *rdev)
+{
+	struct st_rc_device *dev = rdev->priv;
+	/* disable interrupts and receiver */
+	writel(0x00, dev->rx_base + IRB_RX_EN);
+	writel(0x00, dev->rx_base + IRB_RX_INT_EN);
+}
+
+static int st_rc_probe(struct platform_device *pdev)
+{
+	int ret = -EINVAL;
+	struct rc_dev *rdev;
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct st_rc_device *rc_dev;
+	struct device_node *np = pdev->dev.of_node;
+	const char *rx_mode;
+
+	rc_dev = devm_kzalloc(dev, sizeof(struct st_rc_device), GFP_KERNEL);
+
+	if (!rc_dev)
+		return -ENOMEM;
+
+	rdev = rc_allocate_device();
+
+	if (!rdev)
+		return -ENOMEM;
+
+	if (np && !of_property_read_string(np, "rx-mode", &rx_mode)) {
+
+		if (!strcmp(rx_mode, "uhf")) {
+			rc_dev->rxuhfmode = true;
+		} else if (!strcmp(rx_mode, "infrared")) {
+			rc_dev->rxuhfmode = false;
+		} else {
+			dev_err(dev, "Unsupported rx mode [%s]\n", rx_mode);
+			goto err;
+		}
+
+	} else {
+		goto err;
+	}
+
+	rc_dev->sys_clock = devm_clk_get(dev, NULL);
+	if (IS_ERR(rc_dev->sys_clock)) {
+		dev_err(dev, "System clock not found\n");
+		ret = PTR_ERR(rc_dev->sys_clock);
+		goto err;
+	}
+
+	rc_dev->irq = platform_get_irq(pdev, 0);
+	if (rc_dev->irq < 0) {
+		ret = rc_dev->irq;
+		goto err;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	rc_dev->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(rc_dev->base)) {
+		ret = PTR_ERR(rc_dev->base);
+		goto err;
+	}
+
+	if (rc_dev->rxuhfmode)
+		rc_dev->rx_base = rc_dev->base + 0x40;
+	else
+		rc_dev->rx_base = rc_dev->base;
+
+
+	rc_dev->rstc = reset_control_get_optional(dev, NULL);
+	if (IS_ERR(rc_dev->rstc))
+		rc_dev->rstc = NULL;
+
+	rc_dev->dev = dev;
+	platform_set_drvdata(pdev, rc_dev);
+	st_rc_hardware_init(rc_dev);
+
+	rdev->driver_type = RC_DRIVER_IR_RAW;
+	rdev->allowed_protocols = RC_BIT_ALL;
+	/* rx sampling rate is 10Mhz */
+	rdev->rx_resolution = 100;
+	rdev->timeout = US_TO_NS(MAX_SYMB_TIME);
+	rdev->priv = rc_dev;
+	rdev->open = st_rc_open;
+	rdev->close = st_rc_close;
+	rdev->driver_name = IR_ST_NAME;
+	rdev->map_name = RC_MAP_LIRC;
+	rdev->input_name = "ST Remote Control Receiver";
+
+	/* enable wake via this device */
+	device_set_wakeup_capable(dev, true);
+	device_set_wakeup_enable(dev, true);
+
+	ret = rc_register_device(rdev);
+	if (ret < 0)
+		goto clkerr;
+
+	rc_dev->rdev = rdev;
+	if (devm_request_irq(dev, rc_dev->irq, st_rc_rx_interrupt,
+			IRQF_NO_SUSPEND, IR_ST_NAME, rc_dev) < 0) {
+		dev_err(dev, "IRQ %d register failed\n", rc_dev->irq);
+		ret = -EINVAL;
+		goto rcerr;
+	}
+
+	/**
+	 * for LIRC_MODE_MODE2 or LIRC_MODE_PULSE or LIRC_MODE_RAW
+	 * lircd expects a long space first before a signal train to sync.
+	 */
+	st_rc_send_lirc_timeout(rdev);
+
+	dev_info(dev, "setup in %s mode\n", rc_dev->rxuhfmode ? "UHF" : "IR");
+
+	return ret;
+rcerr:
+	rc_unregister_device(rdev);
+	rdev = NULL;
+clkerr:
+	clk_disable_unprepare(rc_dev->sys_clock);
+err:
+	rc_free_device(rdev);
+	dev_err(dev, "Unable to register device (%d)\n", ret);
+	return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int st_rc_suspend(struct device *dev)
+{
+	struct st_rc_device *rc_dev = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev)) {
+		if (!enable_irq_wake(rc_dev->irq))
+			rc_dev->irq_wake = 1;
+		else
+			return -EINVAL;
+	} else {
+		pinctrl_pm_select_sleep_state(dev);
+		writel(0x00, rc_dev->rx_base + IRB_RX_EN);
+		writel(0x00, rc_dev->rx_base + IRB_RX_INT_EN);
+		clk_disable_unprepare(rc_dev->sys_clock);
+		if (rc_dev->rstc)
+			reset_control_assert(rc_dev->rstc);
+	}
+
+	return 0;
+}
+
+static int st_rc_resume(struct device *dev)
+{
+	struct st_rc_device *rc_dev = dev_get_drvdata(dev);
+	struct rc_dev	*rdev = rc_dev->rdev;
+
+	if (rc_dev->irq_wake) {
+		disable_irq_wake(rc_dev->irq);
+		rc_dev->irq_wake = 0;
+	} else {
+		pinctrl_pm_select_default_state(dev);
+		st_rc_hardware_init(rc_dev);
+		if (rdev->users) {
+			writel(IRB_RX_INTS, rc_dev->rx_base + IRB_RX_INT_EN);
+			writel(0x01, rc_dev->rx_base + IRB_RX_EN);
+		}
+	}
+
+	return 0;
+}
+
+#endif
+
+static SIMPLE_DEV_PM_OPS(st_rc_pm_ops, st_rc_suspend, st_rc_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id st_rc_match[] = {
+	{ .compatible = "st,comms-irb", },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, st_rc_match);
+#endif
+
+static struct platform_driver st_rc_driver = {
+	.driver = {
+		.name = IR_ST_NAME,
+		.of_match_table = of_match_ptr(st_rc_match),
+		.pm     = &st_rc_pm_ops,
+	},
+	.probe = st_rc_probe,
+	.remove = st_rc_remove,
+};
+
+module_platform_driver(st_rc_driver);
+
+MODULE_DESCRIPTION("RC Transceiver driver for STMicroelectronics platforms");
+MODULE_AUTHOR("STMicroelectronics (R&D) Ltd");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c
new file mode 100644
index 0000000..5a17cb8
--- /dev/null
+++ b/drivers/media/rc/streamzap.c
@@ -0,0 +1,514 @@
+/*
+ * Streamzap Remote Control driver
+ *
+ * Copyright (c) 2005 Christoph Bartelmus <lirc@bartelmus.de>
+ * Copyright (c) 2010 Jarod Wilson <jarod@wilsonet.com>
+ *
+ * This driver was based on the work of Greg Wickham and Adrian
+ * Dewhurst. It was substantially rewritten to support correct signal
+ * gaps and now maintains a delay buffer, which is used to present
+ * consistent timing behaviour to user space applications. Without the
+ * delay buffer an ugly hack would be required in lircd, which can
+ * cause sluggish signal decoding in certain situations.
+ *
+ * Ported to in-kernel ir-core interface by Jarod Wilson
+ *
+ * This driver is based on the USB skeleton driver packaged with the
+ * kernel; copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.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/device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <media/rc-core.h>
+
+#define DRIVER_VERSION	"1.61"
+#define DRIVER_NAME	"streamzap"
+#define DRIVER_DESC	"Streamzap Remote Control driver"
+
+#define USB_STREAMZAP_VENDOR_ID		0x0e9c
+#define USB_STREAMZAP_PRODUCT_ID	0x0000
+
+/* table of devices that work with this driver */
+static struct usb_device_id streamzap_table[] = {
+	/* Streamzap Remote Control */
+	{ USB_DEVICE(USB_STREAMZAP_VENDOR_ID, USB_STREAMZAP_PRODUCT_ID) },
+	/* Terminating entry */
+	{ }
+};
+
+MODULE_DEVICE_TABLE(usb, streamzap_table);
+
+#define SZ_PULSE_MASK 0xf0
+#define SZ_SPACE_MASK 0x0f
+#define SZ_TIMEOUT    0xff
+#define SZ_RESOLUTION 256
+
+/* number of samples buffered */
+#define SZ_BUF_LEN 128
+
+enum StreamzapDecoderState {
+	PulseSpace,
+	FullPulse,
+	FullSpace,
+	IgnorePulse
+};
+
+/* structure to hold our device specific stuff */
+struct streamzap_ir {
+	/* ir-core */
+	struct rc_dev *rdev;
+
+	/* core device info */
+	struct device *dev;
+
+	/* usb */
+	struct usb_device	*usbdev;
+	struct usb_interface	*interface;
+	struct usb_endpoint_descriptor *endpoint;
+	struct urb		*urb_in;
+
+	/* buffer & dma */
+	unsigned char		*buf_in;
+	dma_addr_t		dma_in;
+	unsigned int		buf_in_len;
+
+	/* track what state we're in */
+	enum StreamzapDecoderState decoder_state;
+	/* tracks whether we are currently receiving some signal */
+	bool			idle;
+	/* sum of signal lengths received since signal start */
+	unsigned long		sum;
+	/* start time of signal; necessary for gap tracking */
+	struct timeval		signal_last;
+	struct timeval		signal_start;
+	bool			timeout_enabled;
+
+	char			name[128];
+	char			phys[64];
+};
+
+
+/* local function prototypes */
+static int streamzap_probe(struct usb_interface *interface,
+			   const struct usb_device_id *id);
+static void streamzap_disconnect(struct usb_interface *interface);
+static void streamzap_callback(struct urb *urb);
+static int streamzap_suspend(struct usb_interface *intf, pm_message_t message);
+static int streamzap_resume(struct usb_interface *intf);
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver streamzap_driver = {
+	.name =		DRIVER_NAME,
+	.probe =	streamzap_probe,
+	.disconnect =	streamzap_disconnect,
+	.suspend =	streamzap_suspend,
+	.resume =	streamzap_resume,
+	.id_table =	streamzap_table,
+};
+
+static void sz_push(struct streamzap_ir *sz, struct ir_raw_event rawir)
+{
+	dev_dbg(sz->dev, "Storing %s with duration %u us\n",
+		(rawir.pulse ? "pulse" : "space"), rawir.duration);
+	ir_raw_event_store_with_filter(sz->rdev, &rawir);
+}
+
+static void sz_push_full_pulse(struct streamzap_ir *sz,
+			       unsigned char value)
+{
+	DEFINE_IR_RAW_EVENT(rawir);
+
+	if (sz->idle) {
+		long deltv;
+
+		sz->signal_last = sz->signal_start;
+		do_gettimeofday(&sz->signal_start);
+
+		deltv = sz->signal_start.tv_sec - sz->signal_last.tv_sec;
+		rawir.pulse = false;
+		if (deltv > 15) {
+			/* really long time */
+			rawir.duration = IR_MAX_DURATION;
+		} else {
+			rawir.duration = (int)(deltv * 1000000 +
+				sz->signal_start.tv_usec -
+				sz->signal_last.tv_usec);
+			rawir.duration -= sz->sum;
+			rawir.duration = US_TO_NS(rawir.duration);
+			rawir.duration = (rawir.duration > IR_MAX_DURATION) ?
+					 IR_MAX_DURATION : rawir.duration;
+		}
+		sz_push(sz, rawir);
+
+		sz->idle = false;
+		sz->sum = 0;
+	}
+
+	rawir.pulse = true;
+	rawir.duration = ((int) value) * SZ_RESOLUTION;
+	rawir.duration += SZ_RESOLUTION / 2;
+	sz->sum += rawir.duration;
+	rawir.duration = US_TO_NS(rawir.duration);
+	rawir.duration = (rawir.duration > IR_MAX_DURATION) ?
+			 IR_MAX_DURATION : rawir.duration;
+	sz_push(sz, rawir);
+}
+
+static void sz_push_half_pulse(struct streamzap_ir *sz,
+			       unsigned char value)
+{
+	sz_push_full_pulse(sz, (value & SZ_PULSE_MASK) >> 4);
+}
+
+static void sz_push_full_space(struct streamzap_ir *sz,
+			       unsigned char value)
+{
+	DEFINE_IR_RAW_EVENT(rawir);
+
+	rawir.pulse = false;
+	rawir.duration = ((int) value) * SZ_RESOLUTION;
+	rawir.duration += SZ_RESOLUTION / 2;
+	sz->sum += rawir.duration;
+	rawir.duration = US_TO_NS(rawir.duration);
+	sz_push(sz, rawir);
+}
+
+static void sz_push_half_space(struct streamzap_ir *sz,
+			       unsigned long value)
+{
+	sz_push_full_space(sz, value & SZ_SPACE_MASK);
+}
+
+/**
+ * streamzap_callback - usb IRQ handler callback
+ *
+ * This procedure is invoked on reception of data from
+ * the usb remote.
+ */
+static void streamzap_callback(struct urb *urb)
+{
+	struct streamzap_ir *sz;
+	unsigned int i;
+	int len;
+
+	if (!urb)
+		return;
+
+	sz = urb->context;
+	len = urb->actual_length;
+
+	switch (urb->status) {
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/*
+		 * this urb is terminated, clean up.
+		 * sz might already be invalid at this point
+		 */
+		dev_err(sz->dev, "urb terminated, status: %d\n", urb->status);
+		return;
+	default:
+		break;
+	}
+
+	dev_dbg(sz->dev, "%s: received urb, len %d\n", __func__, len);
+	for (i = 0; i < len; i++) {
+		dev_dbg(sz->dev, "sz->buf_in[%d]: %x\n",
+			i, (unsigned char)sz->buf_in[i]);
+		switch (sz->decoder_state) {
+		case PulseSpace:
+			if ((sz->buf_in[i] & SZ_PULSE_MASK) ==
+				SZ_PULSE_MASK) {
+				sz->decoder_state = FullPulse;
+				continue;
+			} else if ((sz->buf_in[i] & SZ_SPACE_MASK)
+					== SZ_SPACE_MASK) {
+				sz_push_half_pulse(sz, sz->buf_in[i]);
+				sz->decoder_state = FullSpace;
+				continue;
+			} else {
+				sz_push_half_pulse(sz, sz->buf_in[i]);
+				sz_push_half_space(sz, sz->buf_in[i]);
+			}
+			break;
+		case FullPulse:
+			sz_push_full_pulse(sz, sz->buf_in[i]);
+			sz->decoder_state = IgnorePulse;
+			break;
+		case FullSpace:
+			if (sz->buf_in[i] == SZ_TIMEOUT) {
+				DEFINE_IR_RAW_EVENT(rawir);
+
+				rawir.pulse = false;
+				rawir.duration = sz->rdev->timeout;
+				sz->idle = true;
+				if (sz->timeout_enabled)
+					sz_push(sz, rawir);
+				ir_raw_event_handle(sz->rdev);
+				ir_raw_event_reset(sz->rdev);
+			} else {
+				sz_push_full_space(sz, sz->buf_in[i]);
+			}
+			sz->decoder_state = PulseSpace;
+			break;
+		case IgnorePulse:
+			if ((sz->buf_in[i] & SZ_SPACE_MASK) ==
+				SZ_SPACE_MASK) {
+				sz->decoder_state = FullSpace;
+				continue;
+			}
+			sz_push_half_space(sz, sz->buf_in[i]);
+			sz->decoder_state = PulseSpace;
+			break;
+		}
+	}
+
+	ir_raw_event_handle(sz->rdev);
+	usb_submit_urb(urb, GFP_ATOMIC);
+
+	return;
+}
+
+static struct rc_dev *streamzap_init_rc_dev(struct streamzap_ir *sz)
+{
+	struct rc_dev *rdev;
+	struct device *dev = sz->dev;
+	int ret;
+
+	rdev = rc_allocate_device();
+	if (!rdev) {
+		dev_err(dev, "remote dev allocation failed\n");
+		goto out;
+	}
+
+	snprintf(sz->name, sizeof(sz->name), "Streamzap PC Remote Infrared "
+		 "Receiver (%04x:%04x)",
+		 le16_to_cpu(sz->usbdev->descriptor.idVendor),
+		 le16_to_cpu(sz->usbdev->descriptor.idProduct));
+	usb_make_path(sz->usbdev, sz->phys, sizeof(sz->phys));
+	strlcat(sz->phys, "/input0", sizeof(sz->phys));
+
+	rdev->input_name = sz->name;
+	rdev->input_phys = sz->phys;
+	usb_to_input_id(sz->usbdev, &rdev->input_id);
+	rdev->dev.parent = dev;
+	rdev->priv = sz;
+	rdev->driver_type = RC_DRIVER_IR_RAW;
+	rdev->allowed_protocols = RC_BIT_ALL;
+	rdev->driver_name = DRIVER_NAME;
+	rdev->map_name = RC_MAP_STREAMZAP;
+
+	ret = rc_register_device(rdev);
+	if (ret < 0) {
+		dev_err(dev, "remote input device register failed\n");
+		goto out;
+	}
+
+	return rdev;
+
+out:
+	rc_free_device(rdev);
+	return NULL;
+}
+
+/**
+ *	streamzap_probe
+ *
+ *	Called by usb-core to associated with a candidate device
+ *	On any failure the return value is the ERROR
+ *	On success return 0
+ */
+static int streamzap_probe(struct usb_interface *intf,
+			   const struct usb_device_id *id)
+{
+	struct usb_device *usbdev = interface_to_usbdev(intf);
+	struct usb_host_interface *iface_host;
+	struct streamzap_ir *sz = NULL;
+	char buf[63], name[128] = "";
+	int retval = -ENOMEM;
+	int pipe, maxp;
+
+	/* Allocate space for device driver specific data */
+	sz = kzalloc(sizeof(struct streamzap_ir), GFP_KERNEL);
+	if (!sz)
+		return -ENOMEM;
+
+	sz->usbdev = usbdev;
+	sz->interface = intf;
+
+	/* Check to ensure endpoint information matches requirements */
+	iface_host = intf->cur_altsetting;
+
+	if (iface_host->desc.bNumEndpoints != 1) {
+		dev_err(&intf->dev, "%s: Unexpected desc.bNumEndpoints (%d)\n",
+			__func__, iface_host->desc.bNumEndpoints);
+		retval = -ENODEV;
+		goto free_sz;
+	}
+
+	sz->endpoint = &(iface_host->endpoint[0].desc);
+	if (!usb_endpoint_dir_in(sz->endpoint)) {
+		dev_err(&intf->dev, "%s: endpoint doesn't match input device "
+			"02%02x\n", __func__, sz->endpoint->bEndpointAddress);
+		retval = -ENODEV;
+		goto free_sz;
+	}
+
+	if (!usb_endpoint_xfer_int(sz->endpoint)) {
+		dev_err(&intf->dev, "%s: endpoint attributes don't match xfer "
+			"02%02x\n", __func__, sz->endpoint->bmAttributes);
+		retval = -ENODEV;
+		goto free_sz;
+	}
+
+	pipe = usb_rcvintpipe(usbdev, sz->endpoint->bEndpointAddress);
+	maxp = usb_maxpacket(usbdev, pipe, usb_pipeout(pipe));
+
+	if (maxp == 0) {
+		dev_err(&intf->dev, "%s: endpoint Max Packet Size is 0!?!\n",
+			__func__);
+		retval = -ENODEV;
+		goto free_sz;
+	}
+
+	/* Allocate the USB buffer and IRQ URB */
+	sz->buf_in = usb_alloc_coherent(usbdev, maxp, GFP_ATOMIC, &sz->dma_in);
+	if (!sz->buf_in)
+		goto free_sz;
+
+	sz->urb_in = usb_alloc_urb(0, GFP_KERNEL);
+	if (!sz->urb_in)
+		goto free_buf_in;
+
+	sz->dev = &intf->dev;
+	sz->buf_in_len = maxp;
+
+	if (usbdev->descriptor.iManufacturer
+	    && usb_string(usbdev, usbdev->descriptor.iManufacturer,
+			  buf, sizeof(buf)) > 0)
+		strlcpy(name, buf, sizeof(name));
+
+	if (usbdev->descriptor.iProduct
+	    && usb_string(usbdev, usbdev->descriptor.iProduct,
+			  buf, sizeof(buf)) > 0)
+		snprintf(name + strlen(name), sizeof(name) - strlen(name),
+			 " %s", buf);
+
+	sz->rdev = streamzap_init_rc_dev(sz);
+	if (!sz->rdev)
+		goto rc_dev_fail;
+
+	sz->idle = true;
+	sz->decoder_state = PulseSpace;
+	/* FIXME: don't yet have a way to set this */
+	sz->timeout_enabled = true;
+	sz->rdev->timeout = ((US_TO_NS(SZ_TIMEOUT * SZ_RESOLUTION) &
+				IR_MAX_DURATION) | 0x03000000);
+	#if 0
+	/* not yet supported, depends on patches from maxim */
+	/* see also: LIRC_GET_REC_RESOLUTION and LIRC_SET_REC_TIMEOUT */
+	sz->min_timeout = US_TO_NS(SZ_TIMEOUT * SZ_RESOLUTION);
+	sz->max_timeout = US_TO_NS(SZ_TIMEOUT * SZ_RESOLUTION);
+	#endif
+
+	do_gettimeofday(&sz->signal_start);
+
+	/* Complete final initialisations */
+	usb_fill_int_urb(sz->urb_in, usbdev, pipe, sz->buf_in,
+			 maxp, (usb_complete_t)streamzap_callback,
+			 sz, sz->endpoint->bInterval);
+	sz->urb_in->transfer_dma = sz->dma_in;
+	sz->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	usb_set_intfdata(intf, sz);
+
+	if (usb_submit_urb(sz->urb_in, GFP_ATOMIC))
+		dev_err(sz->dev, "urb submit failed\n");
+
+	dev_info(sz->dev, "Registered %s on usb%d:%d\n", name,
+		 usbdev->bus->busnum, usbdev->devnum);
+
+	return 0;
+
+rc_dev_fail:
+	usb_free_urb(sz->urb_in);
+free_buf_in:
+	usb_free_coherent(usbdev, maxp, sz->buf_in, sz->dma_in);
+free_sz:
+	kfree(sz);
+
+	return retval;
+}
+
+/**
+ * streamzap_disconnect
+ *
+ * Called by the usb core when the device is removed from the system.
+ *
+ * This routine guarantees that the driver will not submit any more urbs
+ * by clearing dev->usbdev.  It is also supposed to terminate any currently
+ * active urbs.  Unfortunately, usb_bulk_msg(), used in streamzap_read(),
+ * does not provide any way to do this.
+ */
+static void streamzap_disconnect(struct usb_interface *interface)
+{
+	struct streamzap_ir *sz = usb_get_intfdata(interface);
+	struct usb_device *usbdev = interface_to_usbdev(interface);
+
+	usb_set_intfdata(interface, NULL);
+
+	if (!sz)
+		return;
+
+	sz->usbdev = NULL;
+	rc_unregister_device(sz->rdev);
+	usb_kill_urb(sz->urb_in);
+	usb_free_urb(sz->urb_in);
+	usb_free_coherent(usbdev, sz->buf_in_len, sz->buf_in, sz->dma_in);
+
+	kfree(sz);
+}
+
+static int streamzap_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct streamzap_ir *sz = usb_get_intfdata(intf);
+
+	usb_kill_urb(sz->urb_in);
+
+	return 0;
+}
+
+static int streamzap_resume(struct usb_interface *intf)
+{
+	struct streamzap_ir *sz = usb_get_intfdata(intf);
+
+	if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
+		dev_err(sz->dev, "Error sumbiting urb\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+module_usb_driver(streamzap_driver);
+
+MODULE_AUTHOR("Jarod Wilson <jarod@wilsonet.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
new file mode 100644
index 0000000..40f7768
--- /dev/null
+++ b/drivers/media/rc/sunxi-cir.c
@@ -0,0 +1,343 @@
+/*
+ * Driver for Allwinner sunXi IR controller
+ *
+ * Copyright (C) 2014 Alexsey Shestacov <wingrime@linux-sunxi.org>
+ * Copyright (C) 2014 Alexander Bersenev <bay@hackerdom.ru>
+ *
+ * Based on sun5i-ir.c:
+ * Copyright (C) 2007-2012 Daniel Wang
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.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.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/reset.h>
+#include <media/rc-core.h>
+
+#define SUNXI_IR_DEV "sunxi-ir"
+
+/* Registers */
+/* IR Control */
+#define SUNXI_IR_CTL_REG      0x00
+/* Global Enable */
+#define REG_CTL_GEN			BIT(0)
+/* RX block enable */
+#define REG_CTL_RXEN			BIT(1)
+/* CIR mode */
+#define REG_CTL_MD			(BIT(4) | BIT(5))
+
+/* Rx Config */
+#define SUNXI_IR_RXCTL_REG    0x10
+/* Pulse Polarity Invert flag */
+#define REG_RXCTL_RPPI			BIT(2)
+
+/* Rx Data */
+#define SUNXI_IR_RXFIFO_REG   0x20
+
+/* Rx Interrupt Enable */
+#define SUNXI_IR_RXINT_REG    0x2C
+/* Rx FIFO Overflow */
+#define REG_RXINT_ROI_EN		BIT(0)
+/* Rx Packet End */
+#define REG_RXINT_RPEI_EN		BIT(1)
+/* Rx FIFO Data Available */
+#define REG_RXINT_RAI_EN		BIT(4)
+
+/* Rx FIFO available byte level */
+#define REG_RXINT_RAL(val)    ((val) << 8)
+
+/* Rx Interrupt Status */
+#define SUNXI_IR_RXSTA_REG    0x30
+/* RX FIFO Get Available Counter */
+#define REG_RXSTA_GET_AC(val) (((val) >> 8) & (ir->fifo_size * 2 - 1))
+/* Clear all interrupt status value */
+#define REG_RXSTA_CLEARALL    0xff
+
+/* IR Sample Config */
+#define SUNXI_IR_CIR_REG      0x34
+/* CIR_REG register noise threshold */
+#define REG_CIR_NTHR(val)    (((val) << 2) & (GENMASK(7, 2)))
+/* CIR_REG register idle threshold */
+#define REG_CIR_ITHR(val)    (((val) << 8) & (GENMASK(15, 8)))
+
+/* Required frequency for IR0 or IR1 clock in CIR mode */
+#define SUNXI_IR_BASE_CLK     8000000
+/* Frequency after IR internal divider  */
+#define SUNXI_IR_CLK          (SUNXI_IR_BASE_CLK / 64)
+/* Sample period in ns */
+#define SUNXI_IR_SAMPLE       (1000000000ul / SUNXI_IR_CLK)
+/* Noise threshold in samples  */
+#define SUNXI_IR_RXNOISE      1
+/* Idle Threshold in samples */
+#define SUNXI_IR_RXIDLE       20
+/* Time after which device stops sending data in ms */
+#define SUNXI_IR_TIMEOUT      120
+
+struct sunxi_ir {
+	spinlock_t      ir_lock;
+	struct rc_dev   *rc;
+	void __iomem    *base;
+	int             irq;
+	int		fifo_size;
+	struct clk      *clk;
+	struct clk      *apb_clk;
+	struct reset_control *rst;
+	const char      *map_name;
+};
+
+static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id)
+{
+	unsigned long status;
+	unsigned char dt;
+	unsigned int cnt, rc;
+	struct sunxi_ir *ir = dev_id;
+	DEFINE_IR_RAW_EVENT(rawir);
+
+	spin_lock(&ir->ir_lock);
+
+	status = readl(ir->base + SUNXI_IR_RXSTA_REG);
+
+	/* clean all pending statuses */
+	writel(status | REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG);
+
+	if (status & (REG_RXINT_RAI_EN | REG_RXINT_RPEI_EN)) {
+		/* How many messages in fifo */
+		rc  = REG_RXSTA_GET_AC(status);
+		/* Sanity check */
+		rc = rc > ir->fifo_size ? ir->fifo_size : rc;
+		/* If we have data */
+		for (cnt = 0; cnt < rc; cnt++) {
+			/* for each bit in fifo */
+			dt = readb(ir->base + SUNXI_IR_RXFIFO_REG);
+			rawir.pulse = (dt & 0x80) != 0;
+			rawir.duration = ((dt & 0x7f) + 1) * SUNXI_IR_SAMPLE;
+			ir_raw_event_store_with_filter(ir->rc, &rawir);
+		}
+	}
+
+	if (status & REG_RXINT_ROI_EN) {
+		ir_raw_event_reset(ir->rc);
+	} else if (status & REG_RXINT_RPEI_EN) {
+		ir_raw_event_set_idle(ir->rc, true);
+		ir_raw_event_handle(ir->rc);
+	}
+
+	spin_unlock(&ir->ir_lock);
+
+	return IRQ_HANDLED;
+}
+
+static int sunxi_ir_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	unsigned long tmp = 0;
+
+	struct device *dev = &pdev->dev;
+	struct device_node *dn = dev->of_node;
+	struct resource *res;
+	struct sunxi_ir *ir;
+
+	ir = devm_kzalloc(dev, sizeof(struct sunxi_ir), GFP_KERNEL);
+	if (!ir)
+		return -ENOMEM;
+
+	spin_lock_init(&ir->ir_lock);
+
+	if (of_device_is_compatible(dn, "allwinner,sun5i-a13-ir"))
+		ir->fifo_size = 64;
+	else
+		ir->fifo_size = 16;
+
+	/* Clock */
+	ir->apb_clk = devm_clk_get(dev, "apb");
+	if (IS_ERR(ir->apb_clk)) {
+		dev_err(dev, "failed to get a apb clock.\n");
+		return PTR_ERR(ir->apb_clk);
+	}
+	ir->clk = devm_clk_get(dev, "ir");
+	if (IS_ERR(ir->clk)) {
+		dev_err(dev, "failed to get a ir clock.\n");
+		return PTR_ERR(ir->clk);
+	}
+
+	/* Reset (optional) */
+	ir->rst = devm_reset_control_get_optional(dev, NULL);
+	if (IS_ERR(ir->rst)) {
+		ret = PTR_ERR(ir->rst);
+		if (ret == -EPROBE_DEFER)
+			return ret;
+		ir->rst = NULL;
+	} else {
+		ret = reset_control_deassert(ir->rst);
+		if (ret)
+			return ret;
+	}
+
+	ret = clk_set_rate(ir->clk, SUNXI_IR_BASE_CLK);
+	if (ret) {
+		dev_err(dev, "set ir base clock failed!\n");
+		goto exit_reset_assert;
+	}
+
+	if (clk_prepare_enable(ir->apb_clk)) {
+		dev_err(dev, "try to enable apb_ir_clk failed\n");
+		ret = -EINVAL;
+		goto exit_reset_assert;
+	}
+
+	if (clk_prepare_enable(ir->clk)) {
+		dev_err(dev, "try to enable ir_clk failed\n");
+		ret = -EINVAL;
+		goto exit_clkdisable_apb_clk;
+	}
+
+	/* IO */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ir->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(ir->base)) {
+		dev_err(dev, "failed to map registers\n");
+		ret = PTR_ERR(ir->base);
+		goto exit_clkdisable_clk;
+	}
+
+	ir->rc = rc_allocate_device();
+	if (!ir->rc) {
+		dev_err(dev, "failed to allocate device\n");
+		ret = -ENOMEM;
+		goto exit_clkdisable_clk;
+	}
+
+	ir->rc->priv = ir;
+	ir->rc->input_name = SUNXI_IR_DEV;
+	ir->rc->input_phys = "sunxi-ir/input0";
+	ir->rc->input_id.bustype = BUS_HOST;
+	ir->rc->input_id.vendor = 0x0001;
+	ir->rc->input_id.product = 0x0001;
+	ir->rc->input_id.version = 0x0100;
+	ir->map_name = of_get_property(dn, "linux,rc-map-name", NULL);
+	ir->rc->map_name = ir->map_name ?: RC_MAP_EMPTY;
+	ir->rc->dev.parent = dev;
+	ir->rc->driver_type = RC_DRIVER_IR_RAW;
+	ir->rc->allowed_protocols = RC_BIT_ALL;
+	ir->rc->rx_resolution = SUNXI_IR_SAMPLE;
+	ir->rc->timeout = MS_TO_NS(SUNXI_IR_TIMEOUT);
+	ir->rc->driver_name = SUNXI_IR_DEV;
+
+	ret = rc_register_device(ir->rc);
+	if (ret) {
+		dev_err(dev, "failed to register rc device\n");
+		goto exit_free_dev;
+	}
+
+	platform_set_drvdata(pdev, ir);
+
+	/* IRQ */
+	ir->irq = platform_get_irq(pdev, 0);
+	if (ir->irq < 0) {
+		dev_err(dev, "no irq resource\n");
+		ret = ir->irq;
+		goto exit_free_dev;
+	}
+
+	ret = devm_request_irq(dev, ir->irq, sunxi_ir_irq, 0, SUNXI_IR_DEV, ir);
+	if (ret) {
+		dev_err(dev, "failed request irq\n");
+		goto exit_free_dev;
+	}
+
+	/* Enable CIR Mode */
+	writel(REG_CTL_MD, ir->base+SUNXI_IR_CTL_REG);
+
+	/* Set noise threshold and idle threshold */
+	writel(REG_CIR_NTHR(SUNXI_IR_RXNOISE)|REG_CIR_ITHR(SUNXI_IR_RXIDLE),
+	       ir->base + SUNXI_IR_CIR_REG);
+
+	/* Invert Input Signal */
+	writel(REG_RXCTL_RPPI, ir->base + SUNXI_IR_RXCTL_REG);
+
+	/* Clear All Rx Interrupt Status */
+	writel(REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG);
+
+	/*
+	 * Enable IRQ on overflow, packet end, FIFO available with trigger
+	 * level
+	 */
+	writel(REG_RXINT_ROI_EN | REG_RXINT_RPEI_EN |
+	       REG_RXINT_RAI_EN | REG_RXINT_RAL(ir->fifo_size / 2 - 1),
+	       ir->base + SUNXI_IR_RXINT_REG);
+
+	/* Enable IR Module */
+	tmp = readl(ir->base + SUNXI_IR_CTL_REG);
+	writel(tmp | REG_CTL_GEN | REG_CTL_RXEN, ir->base + SUNXI_IR_CTL_REG);
+
+	dev_info(dev, "initialized sunXi IR driver\n");
+	return 0;
+
+exit_free_dev:
+	rc_free_device(ir->rc);
+exit_clkdisable_clk:
+	clk_disable_unprepare(ir->clk);
+exit_clkdisable_apb_clk:
+	clk_disable_unprepare(ir->apb_clk);
+exit_reset_assert:
+	if (ir->rst)
+		reset_control_assert(ir->rst);
+
+	return ret;
+}
+
+static int sunxi_ir_remove(struct platform_device *pdev)
+{
+	unsigned long flags;
+	struct sunxi_ir *ir = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(ir->clk);
+	clk_disable_unprepare(ir->apb_clk);
+	if (ir->rst)
+		reset_control_assert(ir->rst);
+
+	spin_lock_irqsave(&ir->ir_lock, flags);
+	/* disable IR IRQ */
+	writel(0, ir->base + SUNXI_IR_RXINT_REG);
+	/* clear All Rx Interrupt Status */
+	writel(REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG);
+	/* disable IR */
+	writel(0, ir->base + SUNXI_IR_CTL_REG);
+	spin_unlock_irqrestore(&ir->ir_lock, flags);
+
+	rc_unregister_device(ir->rc);
+	return 0;
+}
+
+static const struct of_device_id sunxi_ir_match[] = {
+	{ .compatible = "allwinner,sun4i-a10-ir", },
+	{ .compatible = "allwinner,sun5i-a13-ir", },
+	{},
+};
+
+static struct platform_driver sunxi_ir_driver = {
+	.probe          = sunxi_ir_probe,
+	.remove         = sunxi_ir_remove,
+	.driver = {
+		.name = SUNXI_IR_DEV,
+		.of_match_table = sunxi_ir_match,
+	},
+};
+
+module_platform_driver(sunxi_ir_driver);
+
+MODULE_DESCRIPTION("Allwinner sunXi IR controller driver");
+MODULE_AUTHOR("Alexsey Shestacov <wingrime@linux-sunxi.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c
new file mode 100644
index 0000000..bc214e2
--- /dev/null
+++ b/drivers/media/rc/ttusbir.c
@@ -0,0 +1,449 @@
+/*
+ * TechnoTrend USB IR Receiver
+ *
+ * Copyright (C) 2012 Sean Young <sean@mess.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <linux/slab.h>
+#include <linux/leds.h>
+#include <media/rc-core.h>
+
+#define DRIVER_NAME	"ttusbir"
+#define DRIVER_DESC	"TechnoTrend USB IR Receiver"
+/*
+ * The Windows driver uses 8 URBS, the original lirc drivers has a
+ * configurable amount (2 default, 4 max). This device generates about 125
+ * messages per second (!), whether IR is idle or not.
+ */
+#define NUM_URBS	4
+#define NS_PER_BYTE	62500
+#define NS_PER_BIT	(NS_PER_BYTE/8)
+
+struct ttusbir {
+	struct rc_dev *rc;
+	struct device *dev;
+	struct usb_device *udev;
+
+	struct urb *urb[NUM_URBS];
+
+	struct led_classdev led;
+	struct urb *bulk_urb;
+	uint8_t bulk_buffer[5];
+	int bulk_out_endp, iso_in_endp;
+	bool led_on, is_led_on;
+	atomic_t led_complete;
+
+	char phys[64];
+};
+
+static enum led_brightness ttusbir_brightness_get(struct led_classdev *led_dev)
+{
+	struct ttusbir *tt = container_of(led_dev, struct ttusbir, led);
+
+	return tt->led_on ? LED_FULL : LED_OFF;
+}
+
+static void ttusbir_set_led(struct ttusbir *tt)
+{
+	int ret;
+
+	smp_mb();
+
+	if (tt->led_on != tt->is_led_on && tt->udev &&
+				atomic_add_unless(&tt->led_complete, 1, 1)) {
+		tt->bulk_buffer[4] = tt->is_led_on = tt->led_on;
+		ret = usb_submit_urb(tt->bulk_urb, GFP_ATOMIC);
+		if (ret) {
+			dev_warn(tt->dev, "failed to submit bulk urb: %d\n",
+									ret);
+			atomic_dec(&tt->led_complete);
+		}
+	}
+}
+
+static void ttusbir_brightness_set(struct led_classdev *led_dev, enum
+						led_brightness brightness)
+{
+	struct ttusbir *tt = container_of(led_dev, struct ttusbir, led);
+
+	tt->led_on = brightness != LED_OFF;
+
+	ttusbir_set_led(tt);
+}
+
+/*
+ * The urb cannot be reused until the urb completes
+ */
+static void ttusbir_bulk_complete(struct urb *urb)
+{
+	struct ttusbir *tt = urb->context;
+
+	atomic_dec(&tt->led_complete);
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		usb_unlink_urb(urb);
+		return;
+	case -EPIPE:
+	default:
+		dev_dbg(tt->dev, "Error: urb status = %d\n", urb->status);
+		break;
+	}
+
+	ttusbir_set_led(tt);
+}
+
+/*
+ * The data is one bit per sample, a set bit signifying silence and samples
+ * being MSB first. Bit 0 can contain garbage so take it to be whatever
+ * bit 1 is, so we don't have unexpected edges.
+ */
+static void ttusbir_process_ir_data(struct ttusbir *tt, uint8_t *buf)
+{
+	struct ir_raw_event rawir;
+	unsigned i, v, b;
+	bool event = false;
+
+	init_ir_raw_event(&rawir);
+
+	for (i = 0; i < 128; i++) {
+		v = buf[i] & 0xfe;
+		switch (v) {
+		case 0xfe:
+			rawir.pulse = false;
+			rawir.duration = NS_PER_BYTE;
+			if (ir_raw_event_store_with_filter(tt->rc, &rawir))
+				event = true;
+			break;
+		case 0:
+			rawir.pulse = true;
+			rawir.duration = NS_PER_BYTE;
+			if (ir_raw_event_store_with_filter(tt->rc, &rawir))
+				event = true;
+			break;
+		default:
+			/* one edge per byte */
+			if (v & 2) {
+				b = ffz(v | 1);
+				rawir.pulse = true;
+			} else {
+				b = ffs(v) - 1;
+				rawir.pulse = false;
+			}
+
+			rawir.duration = NS_PER_BIT * (8 - b);
+			if (ir_raw_event_store_with_filter(tt->rc, &rawir))
+				event = true;
+
+			rawir.pulse = !rawir.pulse;
+			rawir.duration = NS_PER_BIT * b;
+			if (ir_raw_event_store_with_filter(tt->rc, &rawir))
+				event = true;
+			break;
+		}
+	}
+
+	/* don't wakeup when there's nothing to do */
+	if (event)
+		ir_raw_event_handle(tt->rc);
+}
+
+static void ttusbir_urb_complete(struct urb *urb)
+{
+	struct ttusbir *tt = urb->context;
+	int rc;
+
+	switch (urb->status) {
+	case 0:
+		ttusbir_process_ir_data(tt, urb->transfer_buffer);
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		usb_unlink_urb(urb);
+		return;
+	case -EPIPE:
+	default:
+		dev_dbg(tt->dev, "Error: urb status = %d\n", urb->status);
+		break;
+	}
+
+	rc = usb_submit_urb(urb, GFP_ATOMIC);
+	if (rc && rc != -ENODEV)
+		dev_warn(tt->dev, "failed to resubmit urb: %d\n", rc);
+}
+
+static int ttusbir_probe(struct usb_interface *intf,
+			 const struct usb_device_id *id)
+{
+	struct ttusbir *tt;
+	struct usb_interface_descriptor *idesc;
+	struct usb_endpoint_descriptor *desc;
+	struct rc_dev *rc;
+	int i, j, ret;
+	int altsetting = -1;
+
+	tt = kzalloc(sizeof(*tt), GFP_KERNEL);
+	rc = rc_allocate_device();
+	if (!tt || !rc) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* find the correct alt setting */
+	for (i = 0; i < intf->num_altsetting && altsetting == -1; i++) {
+		int max_packet, bulk_out_endp = -1, iso_in_endp = -1;
+
+		idesc = &intf->altsetting[i].desc;
+
+		for (j = 0; j < idesc->bNumEndpoints; j++) {
+			desc = &intf->altsetting[i].endpoint[j].desc;
+			max_packet = le16_to_cpu(desc->wMaxPacketSize);
+			if (usb_endpoint_dir_in(desc) &&
+					usb_endpoint_xfer_isoc(desc) &&
+					max_packet == 0x10)
+				iso_in_endp = j;
+			else if (usb_endpoint_dir_out(desc) &&
+					usb_endpoint_xfer_bulk(desc) &&
+					max_packet == 0x20)
+				bulk_out_endp = j;
+
+			if (bulk_out_endp != -1 && iso_in_endp != -1) {
+				tt->bulk_out_endp = bulk_out_endp;
+				tt->iso_in_endp = iso_in_endp;
+				altsetting = i;
+				break;
+			}
+		}
+	}
+
+	if (altsetting == -1) {
+		dev_err(&intf->dev, "cannot find expected altsetting\n");
+		ret = -ENODEV;
+		goto out;
+	}
+
+	tt->dev = &intf->dev;
+	tt->udev = interface_to_usbdev(intf);
+	tt->rc = rc;
+
+	ret = usb_set_interface(tt->udev, 0, altsetting);
+	if (ret)
+		goto out;
+
+	for (i = 0; i < NUM_URBS; i++) {
+		struct urb *urb = usb_alloc_urb(8, GFP_KERNEL);
+		void *buffer;
+
+		if (!urb) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		urb->dev = tt->udev;
+		urb->context = tt;
+		urb->pipe = usb_rcvisocpipe(tt->udev, tt->iso_in_endp);
+		urb->interval = 1;
+		buffer = usb_alloc_coherent(tt->udev, 128, GFP_KERNEL,
+						&urb->transfer_dma);
+		if (!buffer) {
+			usb_free_urb(urb);
+			ret = -ENOMEM;
+			goto out;
+		}
+		urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP | URB_ISO_ASAP;
+		urb->transfer_buffer = buffer;
+		urb->complete = ttusbir_urb_complete;
+		urb->number_of_packets = 8;
+		urb->transfer_buffer_length = 128;
+
+		for (j = 0; j < 8; j++) {
+			urb->iso_frame_desc[j].offset = j * 16;
+			urb->iso_frame_desc[j].length = 16;
+		}
+
+		tt->urb[i] = urb;
+	}
+
+	tt->bulk_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!tt->bulk_urb) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	tt->bulk_buffer[0] = 0xaa;
+	tt->bulk_buffer[1] = 0x01;
+	tt->bulk_buffer[2] = 0x05;
+	tt->bulk_buffer[3] = 0x01;
+
+	usb_fill_bulk_urb(tt->bulk_urb, tt->udev, usb_sndbulkpipe(tt->udev,
+		tt->bulk_out_endp), tt->bulk_buffer, sizeof(tt->bulk_buffer),
+						ttusbir_bulk_complete, tt);
+
+	tt->led.name = "ttusbir:green:power";
+	tt->led.default_trigger = "rc-feedback";
+	tt->led.brightness_set = ttusbir_brightness_set;
+	tt->led.brightness_get = ttusbir_brightness_get;
+	tt->is_led_on = tt->led_on = true;
+	atomic_set(&tt->led_complete, 0);
+	ret = led_classdev_register(&intf->dev, &tt->led);
+	if (ret)
+		goto out;
+
+	usb_make_path(tt->udev, tt->phys, sizeof(tt->phys));
+
+	rc->input_name = DRIVER_DESC;
+	rc->input_phys = tt->phys;
+	usb_to_input_id(tt->udev, &rc->input_id);
+	rc->dev.parent = &intf->dev;
+	rc->driver_type = RC_DRIVER_IR_RAW;
+	rc->allowed_protocols = RC_BIT_ALL;
+	rc->priv = tt;
+	rc->driver_name = DRIVER_NAME;
+	rc->map_name = RC_MAP_TT_1500;
+	rc->timeout = MS_TO_NS(100);
+	/*
+	 * The precision is NS_PER_BIT, but since every 8th bit can be
+	 * overwritten with garbage the accuracy is at best 2 * NS_PER_BIT.
+	 */
+	rc->rx_resolution = NS_PER_BIT;
+
+	ret = rc_register_device(rc);
+	if (ret) {
+		dev_err(&intf->dev, "failed to register rc device %d\n", ret);
+		goto out2;
+	}
+
+	usb_set_intfdata(intf, tt);
+
+	for (i = 0; i < NUM_URBS; i++) {
+		ret = usb_submit_urb(tt->urb[i], GFP_KERNEL);
+		if (ret) {
+			dev_err(tt->dev, "failed to submit urb %d\n", ret);
+			goto out3;
+		}
+	}
+
+	return 0;
+out3:
+	rc_unregister_device(rc);
+	rc = NULL;
+out2:
+	led_classdev_unregister(&tt->led);
+out:
+	if (tt) {
+		for (i = 0; i < NUM_URBS && tt->urb[i]; i++) {
+			struct urb *urb = tt->urb[i];
+
+			usb_kill_urb(urb);
+			usb_free_coherent(tt->udev, 128, urb->transfer_buffer,
+							urb->transfer_dma);
+			usb_free_urb(urb);
+		}
+		usb_kill_urb(tt->bulk_urb);
+		usb_free_urb(tt->bulk_urb);
+		kfree(tt);
+	}
+	rc_free_device(rc);
+
+	return ret;
+}
+
+static void ttusbir_disconnect(struct usb_interface *intf)
+{
+	struct ttusbir *tt = usb_get_intfdata(intf);
+	struct usb_device *udev = tt->udev;
+	int i;
+
+	tt->udev = NULL;
+
+	rc_unregister_device(tt->rc);
+	led_classdev_unregister(&tt->led);
+	for (i = 0; i < NUM_URBS; i++) {
+		usb_kill_urb(tt->urb[i]);
+		usb_free_coherent(udev, 128, tt->urb[i]->transfer_buffer,
+						tt->urb[i]->transfer_dma);
+		usb_free_urb(tt->urb[i]);
+	}
+	usb_kill_urb(tt->bulk_urb);
+	usb_free_urb(tt->bulk_urb);
+	usb_set_intfdata(intf, NULL);
+	kfree(tt);
+}
+
+static int ttusbir_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct ttusbir *tt = usb_get_intfdata(intf);
+	int i;
+
+	for (i = 0; i < NUM_URBS; i++)
+		usb_kill_urb(tt->urb[i]);
+
+	led_classdev_suspend(&tt->led);
+	usb_kill_urb(tt->bulk_urb);
+
+	return 0;
+}
+
+static int ttusbir_resume(struct usb_interface *intf)
+{
+	struct ttusbir *tt = usb_get_intfdata(intf);
+	int i, rc;
+
+	tt->is_led_on = true;
+	led_classdev_resume(&tt->led);
+
+	for (i = 0; i < NUM_URBS; i++) {
+		rc = usb_submit_urb(tt->urb[i], GFP_KERNEL);
+		if (rc) {
+			dev_warn(tt->dev, "failed to submit urb: %d\n", rc);
+			break;
+		}
+	}
+
+	return rc;
+}
+
+static const struct usb_device_id ttusbir_table[] = {
+	{ USB_DEVICE(0x0b48, 0x2003) },
+	{ }
+};
+
+static struct usb_driver ttusbir_driver = {
+	.name = DRIVER_NAME,
+	.id_table = ttusbir_table,
+	.probe = ttusbir_probe,
+	.suspend = ttusbir_suspend,
+	.resume = ttusbir_resume,
+	.reset_resume = ttusbir_resume,
+	.disconnect = ttusbir_disconnect,
+};
+
+module_usb_driver(ttusbir_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, ttusbir_table);
+
diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c
new file mode 100644
index 0000000..d839f73
--- /dev/null
+++ b/drivers/media/rc/winbond-cir.c
@@ -0,0 +1,1228 @@
+/*
+ *  winbond-cir.c - Driver for the Consumer IR functionality of Winbond
+ *                  SuperI/O chips.
+ *
+ *  Currently supports the Winbond WPCD376i chip (PNP id WEC1022), but
+ *  could probably support others (Winbond WEC102X, NatSemi, etc)
+ *  with minor modifications.
+ *
+ *  Original Author: David Härdeman <david@hardeman.nu>
+ *     Copyright (C) 2012 Sean Young <sean@mess.org>
+ *     Copyright (C) 2009 - 2011 David Härdeman <david@hardeman.nu>
+ *
+ *  Dedicated to my daughter Matilda, without whose loving attention this
+ *  driver would have been finished in half the time and with a fraction
+ *  of the bugs.
+ *
+ *  Written using:
+ *    o Winbond WPCD376I datasheet helpfully provided by Jesse Barnes at Intel
+ *    o NatSemi PC87338/PC97338 datasheet (for the serial port stuff)
+ *    o DSDT dumps
+ *
+ *  Supported features:
+ *    o IR Receive
+ *    o IR Transmit
+ *    o Wake-On-CIR functionality
+ *    o Carrier detection
+ *
+ *  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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/pnp.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/leds.h>
+#include <linux/spinlock.h>
+#include <linux/pci_ids.h>
+#include <linux/io.h>
+#include <linux/bitrev.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <media/rc-core.h>
+
+#define DRVNAME "winbond-cir"
+
+/* CEIR Wake-Up Registers, relative to data->wbase                      */
+#define WBCIR_REG_WCEIR_CTL	0x03 /* CEIR Receiver Control		*/
+#define WBCIR_REG_WCEIR_STS	0x04 /* CEIR Receiver Status		*/
+#define WBCIR_REG_WCEIR_EV_EN	0x05 /* CEIR Receiver Event Enable	*/
+#define WBCIR_REG_WCEIR_CNTL	0x06 /* CEIR Receiver Counter Low	*/
+#define WBCIR_REG_WCEIR_CNTH	0x07 /* CEIR Receiver Counter High	*/
+#define WBCIR_REG_WCEIR_INDEX	0x08 /* CEIR Receiver Index		*/
+#define WBCIR_REG_WCEIR_DATA	0x09 /* CEIR Receiver Data		*/
+#define WBCIR_REG_WCEIR_CSL	0x0A /* CEIR Re. Compare Strlen		*/
+#define WBCIR_REG_WCEIR_CFG1	0x0B /* CEIR Re. Configuration 1	*/
+#define WBCIR_REG_WCEIR_CFG2	0x0C /* CEIR Re. Configuration 2	*/
+
+/* CEIR Enhanced Functionality Registers, relative to data->ebase       */
+#define WBCIR_REG_ECEIR_CTS	0x00 /* Enhanced IR Control Status	*/
+#define WBCIR_REG_ECEIR_CCTL	0x01 /* Infrared Counter Control	*/
+#define WBCIR_REG_ECEIR_CNT_LO	0x02 /* Infrared Counter LSB		*/
+#define WBCIR_REG_ECEIR_CNT_HI	0x03 /* Infrared Counter MSB		*/
+#define WBCIR_REG_ECEIR_IREM	0x04 /* Infrared Emitter Status		*/
+
+/* SP3 Banked Registers, relative to data->sbase                        */
+#define WBCIR_REG_SP3_BSR	0x03 /* Bank Select, all banks		*/
+				      /* Bank 0				*/
+#define WBCIR_REG_SP3_RXDATA	0x00 /* FIFO RX data (r)		*/
+#define WBCIR_REG_SP3_TXDATA	0x00 /* FIFO TX data (w)		*/
+#define WBCIR_REG_SP3_IER	0x01 /* Interrupt Enable		*/
+#define WBCIR_REG_SP3_EIR	0x02 /* Event Identification (r)	*/
+#define WBCIR_REG_SP3_FCR	0x02 /* FIFO Control (w)		*/
+#define WBCIR_REG_SP3_MCR	0x04 /* Mode Control			*/
+#define WBCIR_REG_SP3_LSR	0x05 /* Link Status			*/
+#define WBCIR_REG_SP3_MSR	0x06 /* Modem Status			*/
+#define WBCIR_REG_SP3_ASCR	0x07 /* Aux Status and Control		*/
+				      /* Bank 2				*/
+#define WBCIR_REG_SP3_BGDL	0x00 /* Baud Divisor LSB		*/
+#define WBCIR_REG_SP3_BGDH	0x01 /* Baud Divisor MSB		*/
+#define WBCIR_REG_SP3_EXCR1	0x02 /* Extended Control 1		*/
+#define WBCIR_REG_SP3_EXCR2	0x04 /* Extended Control 2		*/
+#define WBCIR_REG_SP3_TXFLV	0x06 /* TX FIFO Level			*/
+#define WBCIR_REG_SP3_RXFLV	0x07 /* RX FIFO Level			*/
+				      /* Bank 3				*/
+#define WBCIR_REG_SP3_MRID	0x00 /* Module Identification		*/
+#define WBCIR_REG_SP3_SH_LCR	0x01 /* LCR Shadow			*/
+#define WBCIR_REG_SP3_SH_FCR	0x02 /* FCR Shadow			*/
+				      /* Bank 4				*/
+#define WBCIR_REG_SP3_IRCR1	0x02 /* Infrared Control 1		*/
+				      /* Bank 5				*/
+#define WBCIR_REG_SP3_IRCR2	0x04 /* Infrared Control 2		*/
+				      /* Bank 6				*/
+#define WBCIR_REG_SP3_IRCR3	0x00 /* Infrared Control 3		*/
+#define WBCIR_REG_SP3_SIR_PW	0x02 /* SIR Pulse Width			*/
+				      /* Bank 7				*/
+#define WBCIR_REG_SP3_IRRXDC	0x00 /* IR RX Demod Control		*/
+#define WBCIR_REG_SP3_IRTXMC	0x01 /* IR TX Mod Control		*/
+#define WBCIR_REG_SP3_RCCFG	0x02 /* CEIR Config			*/
+#define WBCIR_REG_SP3_IRCFG1	0x04 /* Infrared Config 1		*/
+#define WBCIR_REG_SP3_IRCFG4	0x07 /* Infrared Config 4		*/
+
+/*
+ * Magic values follow
+ */
+
+/* No interrupts for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
+#define WBCIR_IRQ_NONE		0x00
+/* RX data bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
+#define WBCIR_IRQ_RX		0x01
+/* TX data low bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
+#define WBCIR_IRQ_TX_LOW	0x02
+/* Over/Under-flow bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
+#define WBCIR_IRQ_ERR		0x04
+/* TX data empty bit for WBCEIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
+#define WBCIR_IRQ_TX_EMPTY	0x20
+/* Led enable/disable bit for WBCIR_REG_ECEIR_CTS */
+#define WBCIR_LED_ENABLE	0x80
+/* RX data available bit for WBCIR_REG_SP3_LSR */
+#define WBCIR_RX_AVAIL		0x01
+/* RX data overrun error bit for WBCIR_REG_SP3_LSR */
+#define WBCIR_RX_OVERRUN	0x02
+/* TX End-Of-Transmission bit for WBCIR_REG_SP3_ASCR */
+#define WBCIR_TX_EOT		0x04
+/* RX disable bit for WBCIR_REG_SP3_ASCR */
+#define WBCIR_RX_DISABLE	0x20
+/* TX data underrun error bit for WBCIR_REG_SP3_ASCR */
+#define WBCIR_TX_UNDERRUN	0x40
+/* Extended mode enable bit for WBCIR_REG_SP3_EXCR1 */
+#define WBCIR_EXT_ENABLE	0x01
+/* Select compare register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */
+#define WBCIR_REGSEL_COMPARE	0x10
+/* Select mask register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */
+#define WBCIR_REGSEL_MASK	0x20
+/* Starting address of selected register in WBCIR_REG_WCEIR_INDEX */
+#define WBCIR_REG_ADDR0		0x00
+/* Enable carrier counter */
+#define WBCIR_CNTR_EN		0x01
+/* Reset carrier counter */
+#define WBCIR_CNTR_R		0x02
+/* Invert TX */
+#define WBCIR_IRTX_INV		0x04
+/* Receiver oversampling */
+#define WBCIR_RX_T_OV		0x40
+
+/* Valid banks for the SP3 UART */
+enum wbcir_bank {
+	WBCIR_BANK_0          = 0x00,
+	WBCIR_BANK_1          = 0x80,
+	WBCIR_BANK_2          = 0xE0,
+	WBCIR_BANK_3          = 0xE4,
+	WBCIR_BANK_4          = 0xE8,
+	WBCIR_BANK_5          = 0xEC,
+	WBCIR_BANK_6          = 0xF0,
+	WBCIR_BANK_7          = 0xF4,
+};
+
+/* Supported power-on IR Protocols */
+enum wbcir_protocol {
+	IR_PROTOCOL_RC5          = 0x0,
+	IR_PROTOCOL_NEC          = 0x1,
+	IR_PROTOCOL_RC6          = 0x2,
+};
+
+/* Possible states for IR reception */
+enum wbcir_rxstate {
+	WBCIR_RXSTATE_INACTIVE = 0,
+	WBCIR_RXSTATE_ACTIVE,
+	WBCIR_RXSTATE_ERROR
+};
+
+/* Possible states for IR transmission */
+enum wbcir_txstate {
+	WBCIR_TXSTATE_INACTIVE = 0,
+	WBCIR_TXSTATE_ACTIVE,
+	WBCIR_TXSTATE_ERROR
+};
+
+/* Misc */
+#define WBCIR_NAME	"Winbond CIR"
+#define WBCIR_ID_FAMILY          0xF1 /* Family ID for the WPCD376I	*/
+#define	WBCIR_ID_CHIP            0x04 /* Chip ID for the WPCD376I	*/
+#define INVALID_SCANCODE   0x7FFFFFFF /* Invalid with all protos	*/
+#define WAKEUP_IOMEM_LEN         0x10 /* Wake-Up I/O Reg Len		*/
+#define EHFUNC_IOMEM_LEN         0x10 /* Enhanced Func I/O Reg Len	*/
+#define SP_IOMEM_LEN             0x08 /* Serial Port 3 (IR) Reg Len	*/
+
+/* Per-device data */
+struct wbcir_data {
+	spinlock_t spinlock;
+	struct rc_dev *dev;
+	struct led_classdev led;
+
+	unsigned long wbase;        /* Wake-Up Baseaddr		*/
+	unsigned long ebase;        /* Enhanced Func. Baseaddr	*/
+	unsigned long sbase;        /* Serial Port Baseaddr	*/
+	unsigned int  irq;          /* Serial Port IRQ		*/
+	u8 irqmask;
+
+	/* RX state */
+	enum wbcir_rxstate rxstate;
+	int carrier_report_enabled;
+	u32 pulse_duration;
+
+	/* TX state */
+	enum wbcir_txstate txstate;
+	u32 txlen;
+	u32 txoff;
+	u32 *txbuf;
+	u8 txmask;
+	u32 txcarrier;
+};
+
+static enum wbcir_protocol protocol = IR_PROTOCOL_RC6;
+module_param(protocol, uint, 0444);
+MODULE_PARM_DESC(protocol, "IR protocol to use for the power-on command "
+		 "(0 = RC5, 1 = NEC, 2 = RC6A, default)");
+
+static bool invert; /* default = 0 */
+module_param(invert, bool, 0444);
+MODULE_PARM_DESC(invert, "Invert the signal from the IR receiver");
+
+static bool txandrx; /* default = 0 */
+module_param(txandrx, bool, 0444);
+MODULE_PARM_DESC(txandrx, "Allow simultaneous TX and RX");
+
+static unsigned int wake_sc = 0x800F040C;
+module_param(wake_sc, uint, 0644);
+MODULE_PARM_DESC(wake_sc, "Scancode of the power-on IR command");
+
+static unsigned int wake_rc6mode = 6;
+module_param(wake_rc6mode, uint, 0644);
+MODULE_PARM_DESC(wake_rc6mode, "RC6 mode for the power-on command "
+		 "(0 = 0, 6 = 6A, default)");
+
+
+
+/*****************************************************************************
+ *
+ * UTILITY FUNCTIONS
+ *
+ *****************************************************************************/
+
+/* Caller needs to hold wbcir_lock */
+static void
+wbcir_set_bits(unsigned long addr, u8 bits, u8 mask)
+{
+	u8 val;
+
+	val = inb(addr);
+	val = ((val & ~mask) | (bits & mask));
+	outb(val, addr);
+}
+
+/* Selects the register bank for the serial port */
+static inline void
+wbcir_select_bank(struct wbcir_data *data, enum wbcir_bank bank)
+{
+	outb(bank, data->sbase + WBCIR_REG_SP3_BSR);
+}
+
+static inline void
+wbcir_set_irqmask(struct wbcir_data *data, u8 irqmask)
+{
+	if (data->irqmask == irqmask)
+		return;
+
+	wbcir_select_bank(data, WBCIR_BANK_0);
+	outb(irqmask, data->sbase + WBCIR_REG_SP3_IER);
+	data->irqmask = irqmask;
+}
+
+static enum led_brightness
+wbcir_led_brightness_get(struct led_classdev *led_cdev)
+{
+	struct wbcir_data *data = container_of(led_cdev,
+					       struct wbcir_data,
+					       led);
+
+	if (inb(data->ebase + WBCIR_REG_ECEIR_CTS) & WBCIR_LED_ENABLE)
+		return LED_FULL;
+	else
+		return LED_OFF;
+}
+
+static void
+wbcir_led_brightness_set(struct led_classdev *led_cdev,
+			 enum led_brightness brightness)
+{
+	struct wbcir_data *data = container_of(led_cdev,
+					       struct wbcir_data,
+					       led);
+
+	wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CTS,
+		       brightness == LED_OFF ? 0x00 : WBCIR_LED_ENABLE,
+		       WBCIR_LED_ENABLE);
+}
+
+/* Manchester encodes bits to RC6 message cells (see wbcir_shutdown) */
+static u8
+wbcir_to_rc6cells(u8 val)
+{
+	u8 coded = 0x00;
+	int i;
+
+	val &= 0x0F;
+	for (i = 0; i < 4; i++) {
+		if (val & 0x01)
+			coded |= 0x02 << (i * 2);
+		else
+			coded |= 0x01 << (i * 2);
+		val >>= 1;
+	}
+
+	return coded;
+}
+
+/*****************************************************************************
+ *
+ * INTERRUPT FUNCTIONS
+ *
+ *****************************************************************************/
+
+static void
+wbcir_carrier_report(struct wbcir_data *data)
+{
+	unsigned counter = inb(data->ebase + WBCIR_REG_ECEIR_CNT_LO) |
+			inb(data->ebase + WBCIR_REG_ECEIR_CNT_HI) << 8;
+
+	if (counter > 0 && counter < 0xffff) {
+		DEFINE_IR_RAW_EVENT(ev);
+
+		ev.carrier_report = 1;
+		ev.carrier = DIV_ROUND_CLOSEST(counter * 1000000u,
+						data->pulse_duration);
+
+		ir_raw_event_store(data->dev, &ev);
+	}
+
+	/* reset and restart the counter */
+	data->pulse_duration = 0;
+	wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL, WBCIR_CNTR_R,
+						WBCIR_CNTR_EN | WBCIR_CNTR_R);
+	wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL, WBCIR_CNTR_EN,
+						WBCIR_CNTR_EN | WBCIR_CNTR_R);
+}
+
+static void
+wbcir_idle_rx(struct rc_dev *dev, bool idle)
+{
+	struct wbcir_data *data = dev->priv;
+
+	if (!idle && data->rxstate == WBCIR_RXSTATE_INACTIVE)
+		data->rxstate = WBCIR_RXSTATE_ACTIVE;
+
+	if (idle && data->rxstate != WBCIR_RXSTATE_INACTIVE) {
+		data->rxstate = WBCIR_RXSTATE_INACTIVE;
+
+		if (data->carrier_report_enabled)
+			wbcir_carrier_report(data);
+
+		/* Tell hardware to go idle by setting RXINACTIVE */
+		outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR);
+	}
+}
+
+static void
+wbcir_irq_rx(struct wbcir_data *data, struct pnp_dev *device)
+{
+	u8 irdata;
+	DEFINE_IR_RAW_EVENT(rawir);
+	unsigned duration;
+
+	/* Since RXHDLEV is set, at least 8 bytes are in the FIFO */
+	while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL) {
+		irdata = inb(data->sbase + WBCIR_REG_SP3_RXDATA);
+		if (data->rxstate == WBCIR_RXSTATE_ERROR)
+			continue;
+
+		duration = ((irdata & 0x7F) + 1) *
+			(data->carrier_report_enabled ? 2 : 10);
+		rawir.pulse = irdata & 0x80 ? false : true;
+		rawir.duration = US_TO_NS(duration);
+
+		if (rawir.pulse)
+			data->pulse_duration += duration;
+
+		ir_raw_event_store_with_filter(data->dev, &rawir);
+	}
+
+	ir_raw_event_handle(data->dev);
+}
+
+static void
+wbcir_irq_tx(struct wbcir_data *data)
+{
+	unsigned int space;
+	unsigned int used;
+	u8 bytes[16];
+	u8 byte;
+
+	if (!data->txbuf)
+		return;
+
+	switch (data->txstate) {
+	case WBCIR_TXSTATE_INACTIVE:
+		/* TX FIFO empty */
+		space = 16;
+		break;
+	case WBCIR_TXSTATE_ACTIVE:
+		/* TX FIFO low (3 bytes or less) */
+		space = 13;
+		break;
+	case WBCIR_TXSTATE_ERROR:
+		space = 0;
+		break;
+	default:
+		return;
+	}
+
+	/*
+	 * TX data is run-length coded in bytes: YXXXXXXX
+	 * Y = space (1) or pulse (0)
+	 * X = duration, encoded as (X + 1) * 10us (i.e 10 to 1280 us)
+	 */
+	for (used = 0; used < space && data->txoff != data->txlen; used++) {
+		if (data->txbuf[data->txoff] == 0) {
+			data->txoff++;
+			continue;
+		}
+		byte = min((u32)0x80, data->txbuf[data->txoff]);
+		data->txbuf[data->txoff] -= byte;
+		byte--;
+		byte |= (data->txoff % 2 ? 0x80 : 0x00); /* pulse/space */
+		bytes[used] = byte;
+	}
+
+	while (data->txbuf[data->txoff] == 0 && data->txoff != data->txlen)
+		data->txoff++;
+
+	if (used == 0) {
+		/* Finished */
+		if (data->txstate == WBCIR_TXSTATE_ERROR)
+			/* Clear TX underrun bit */
+			outb(WBCIR_TX_UNDERRUN, data->sbase + WBCIR_REG_SP3_ASCR);
+		wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR);
+		kfree(data->txbuf);
+		data->txbuf = NULL;
+		data->txstate = WBCIR_TXSTATE_INACTIVE;
+	} else if (data->txoff == data->txlen) {
+		/* At the end of transmission, tell the hw before last byte */
+		outsb(data->sbase + WBCIR_REG_SP3_TXDATA, bytes, used - 1);
+		outb(WBCIR_TX_EOT, data->sbase + WBCIR_REG_SP3_ASCR);
+		outb(bytes[used - 1], data->sbase + WBCIR_REG_SP3_TXDATA);
+		wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR |
+				  WBCIR_IRQ_TX_EMPTY);
+	} else {
+		/* More data to follow... */
+		outsb(data->sbase + WBCIR_REG_SP3_RXDATA, bytes, used);
+		if (data->txstate == WBCIR_TXSTATE_INACTIVE) {
+			wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR |
+					  WBCIR_IRQ_TX_LOW);
+			data->txstate = WBCIR_TXSTATE_ACTIVE;
+		}
+	}
+}
+
+static irqreturn_t
+wbcir_irq_handler(int irqno, void *cookie)
+{
+	struct pnp_dev *device = cookie;
+	struct wbcir_data *data = pnp_get_drvdata(device);
+	unsigned long flags;
+	u8 status;
+
+	spin_lock_irqsave(&data->spinlock, flags);
+	wbcir_select_bank(data, WBCIR_BANK_0);
+	status = inb(data->sbase + WBCIR_REG_SP3_EIR);
+	status &= data->irqmask;
+
+	if (!status) {
+		spin_unlock_irqrestore(&data->spinlock, flags);
+		return IRQ_NONE;
+	}
+
+	if (status & WBCIR_IRQ_ERR) {
+		/* RX overflow? (read clears bit) */
+		if (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_OVERRUN) {
+			data->rxstate = WBCIR_RXSTATE_ERROR;
+			ir_raw_event_reset(data->dev);
+		}
+
+		/* TX underflow? */
+		if (inb(data->sbase + WBCIR_REG_SP3_ASCR) & WBCIR_TX_UNDERRUN)
+			data->txstate = WBCIR_TXSTATE_ERROR;
+	}
+
+	if (status & WBCIR_IRQ_RX)
+		wbcir_irq_rx(data, device);
+
+	if (status & (WBCIR_IRQ_TX_LOW | WBCIR_IRQ_TX_EMPTY))
+		wbcir_irq_tx(data);
+
+	spin_unlock_irqrestore(&data->spinlock, flags);
+	return IRQ_HANDLED;
+}
+
+/*****************************************************************************
+ *
+ * RC-CORE INTERFACE FUNCTIONS
+ *
+ *****************************************************************************/
+
+static int
+wbcir_set_carrier_report(struct rc_dev *dev, int enable)
+{
+	struct wbcir_data *data = dev->priv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&data->spinlock, flags);
+
+	if (data->carrier_report_enabled == enable) {
+		spin_unlock_irqrestore(&data->spinlock, flags);
+		return 0;
+	}
+
+	data->pulse_duration = 0;
+	wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL, WBCIR_CNTR_R,
+						WBCIR_CNTR_EN | WBCIR_CNTR_R);
+
+	if (enable && data->dev->idle)
+		wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL,
+				WBCIR_CNTR_EN, WBCIR_CNTR_EN | WBCIR_CNTR_R);
+
+	/* Set a higher sampling resolution if carrier reports are enabled */
+	wbcir_select_bank(data, WBCIR_BANK_2);
+	data->dev->rx_resolution = US_TO_NS(enable ? 2 : 10);
+	outb(enable ? 0x03 : 0x0f, data->sbase + WBCIR_REG_SP3_BGDL);
+	outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH);
+
+	/* Enable oversampling if carrier reports are enabled */
+	wbcir_select_bank(data, WBCIR_BANK_7);
+	wbcir_set_bits(data->sbase + WBCIR_REG_SP3_RCCFG,
+				enable ? WBCIR_RX_T_OV : 0, WBCIR_RX_T_OV);
+
+	data->carrier_report_enabled = enable;
+	spin_unlock_irqrestore(&data->spinlock, flags);
+
+	return 0;
+}
+
+static int
+wbcir_txcarrier(struct rc_dev *dev, u32 carrier)
+{
+	struct wbcir_data *data = dev->priv;
+	unsigned long flags;
+	u8 val;
+	u32 freq;
+
+	freq = DIV_ROUND_CLOSEST(carrier, 1000);
+	if (freq < 30 || freq > 60)
+		return -EINVAL;
+
+	switch (freq) {
+	case 58:
+	case 59:
+	case 60:
+		val = freq - 58;
+		freq *= 1000;
+		break;
+	case 57:
+		val = freq - 27;
+		freq = 56900;
+		break;
+	default:
+		val = freq - 27;
+		freq *= 1000;
+		break;
+	}
+
+	spin_lock_irqsave(&data->spinlock, flags);
+	if (data->txstate != WBCIR_TXSTATE_INACTIVE) {
+		spin_unlock_irqrestore(&data->spinlock, flags);
+		return -EBUSY;
+	}
+
+	if (data->txcarrier != freq) {
+		wbcir_select_bank(data, WBCIR_BANK_7);
+		wbcir_set_bits(data->sbase + WBCIR_REG_SP3_IRTXMC, val, 0x1F);
+		data->txcarrier = freq;
+	}
+
+	spin_unlock_irqrestore(&data->spinlock, flags);
+	return 0;
+}
+
+static int
+wbcir_txmask(struct rc_dev *dev, u32 mask)
+{
+	struct wbcir_data *data = dev->priv;
+	unsigned long flags;
+	u8 val;
+
+	/* Four outputs, only one output can be enabled at a time */
+	switch (mask) {
+	case 0x1:
+		val = 0x0;
+		break;
+	case 0x2:
+		val = 0x1;
+		break;
+	case 0x4:
+		val = 0x2;
+		break;
+	case 0x8:
+		val = 0x3;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&data->spinlock, flags);
+	if (data->txstate != WBCIR_TXSTATE_INACTIVE) {
+		spin_unlock_irqrestore(&data->spinlock, flags);
+		return -EBUSY;
+	}
+
+	if (data->txmask != mask) {
+		wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CTS, val, 0x0c);
+		data->txmask = mask;
+	}
+
+	spin_unlock_irqrestore(&data->spinlock, flags);
+	return 0;
+}
+
+static int
+wbcir_tx(struct rc_dev *dev, unsigned *b, unsigned count)
+{
+	struct wbcir_data *data = dev->priv;
+	unsigned *buf;
+	unsigned i;
+	unsigned long flags;
+
+	buf = kmalloc(count * sizeof(*b), GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	/* Convert values to multiples of 10us */
+	for (i = 0; i < count; i++)
+		buf[i] = DIV_ROUND_CLOSEST(b[i], 10);
+
+	/* Not sure if this is possible, but better safe than sorry */
+	spin_lock_irqsave(&data->spinlock, flags);
+	if (data->txstate != WBCIR_TXSTATE_INACTIVE) {
+		spin_unlock_irqrestore(&data->spinlock, flags);
+		kfree(buf);
+		return -EBUSY;
+	}
+
+	/* Fill the TX fifo once, the irq handler will do the rest */
+	data->txbuf = buf;
+	data->txlen = count;
+	data->txoff = 0;
+	wbcir_irq_tx(data);
+
+	/* We're done */
+	spin_unlock_irqrestore(&data->spinlock, flags);
+	return count;
+}
+
+/*****************************************************************************
+ *
+ * SETUP/INIT/SUSPEND/RESUME FUNCTIONS
+ *
+ *****************************************************************************/
+
+static void
+wbcir_shutdown(struct pnp_dev *device)
+{
+	struct device *dev = &device->dev;
+	struct wbcir_data *data = pnp_get_drvdata(device);
+	bool do_wake = true;
+	u8 match[11];
+	u8 mask[11];
+	u8 rc6_csl = 0;
+	int i;
+
+	memset(match, 0, sizeof(match));
+	memset(mask, 0, sizeof(mask));
+
+	if (wake_sc == INVALID_SCANCODE || !device_may_wakeup(dev)) {
+		do_wake = false;
+		goto finish;
+	}
+
+	switch (protocol) {
+	case IR_PROTOCOL_RC5:
+		if (wake_sc > 0xFFF) {
+			do_wake = false;
+			dev_err(dev, "RC5 - Invalid wake scancode\n");
+			break;
+		}
+
+		/* Mask = 13 bits, ex toggle */
+		mask[0] = 0xFF;
+		mask[1] = 0x17;
+
+		match[0]  = (wake_sc & 0x003F);      /* 6 command bits */
+		match[0] |= (wake_sc & 0x0180) >> 1; /* 2 address bits */
+		match[1]  = (wake_sc & 0x0E00) >> 9; /* 3 address bits */
+		if (!(wake_sc & 0x0040))             /* 2nd start bit  */
+			match[1] |= 0x10;
+
+		break;
+
+	case IR_PROTOCOL_NEC:
+		if (wake_sc > 0xFFFFFF) {
+			do_wake = false;
+			dev_err(dev, "NEC - Invalid wake scancode\n");
+			break;
+		}
+
+		mask[0] = mask[1] = mask[2] = mask[3] = 0xFF;
+
+		match[1] = bitrev8((wake_sc & 0xFF));
+		match[0] = ~match[1];
+
+		match[3] = bitrev8((wake_sc & 0xFF00) >> 8);
+		if (wake_sc > 0xFFFF)
+			match[2] = bitrev8((wake_sc & 0xFF0000) >> 16);
+		else
+			match[2] = ~match[3];
+
+		break;
+
+	case IR_PROTOCOL_RC6:
+
+		if (wake_rc6mode == 0) {
+			if (wake_sc > 0xFFFF) {
+				do_wake = false;
+				dev_err(dev, "RC6 - Invalid wake scancode\n");
+				break;
+			}
+
+			/* Command */
+			match[0] = wbcir_to_rc6cells(wake_sc >>  0);
+			mask[0]  = 0xFF;
+			match[1] = wbcir_to_rc6cells(wake_sc >>  4);
+			mask[1]  = 0xFF;
+
+			/* Address */
+			match[2] = wbcir_to_rc6cells(wake_sc >>  8);
+			mask[2]  = 0xFF;
+			match[3] = wbcir_to_rc6cells(wake_sc >> 12);
+			mask[3]  = 0xFF;
+
+			/* Header */
+			match[4] = 0x50; /* mode1 = mode0 = 0, ignore toggle */
+			mask[4]  = 0xF0;
+			match[5] = 0x09; /* start bit = 1, mode2 = 0 */
+			mask[5]  = 0x0F;
+
+			rc6_csl = 44;
+
+		} else if (wake_rc6mode == 6) {
+			i = 0;
+
+			/* Command */
+			match[i]  = wbcir_to_rc6cells(wake_sc >>  0);
+			mask[i++] = 0xFF;
+			match[i]  = wbcir_to_rc6cells(wake_sc >>  4);
+			mask[i++] = 0xFF;
+
+			/* Address + Toggle */
+			match[i]  = wbcir_to_rc6cells(wake_sc >>  8);
+			mask[i++] = 0xFF;
+			match[i]  = wbcir_to_rc6cells(wake_sc >> 12);
+			mask[i++] = 0x3F;
+
+			/* Customer bits 7 - 0 */
+			match[i]  = wbcir_to_rc6cells(wake_sc >> 16);
+			mask[i++] = 0xFF;
+			match[i]  = wbcir_to_rc6cells(wake_sc >> 20);
+			mask[i++] = 0xFF;
+
+			if (wake_sc & 0x80000000) {
+				/* Customer range bit and bits 15 - 8 */
+				match[i]  = wbcir_to_rc6cells(wake_sc >> 24);
+				mask[i++] = 0xFF;
+				match[i]  = wbcir_to_rc6cells(wake_sc >> 28);
+				mask[i++] = 0xFF;
+				rc6_csl = 76;
+			} else if (wake_sc <= 0x007FFFFF) {
+				rc6_csl = 60;
+			} else {
+				do_wake = false;
+				dev_err(dev, "RC6 - Invalid wake scancode\n");
+				break;
+			}
+
+			/* Header */
+			match[i]  = 0x93; /* mode1 = mode0 = 1, submode = 0 */
+			mask[i++] = 0xFF;
+			match[i]  = 0x0A; /* start bit = 1, mode2 = 1 */
+			mask[i++] = 0x0F;
+
+		} else {
+			do_wake = false;
+			dev_err(dev, "RC6 - Invalid wake mode\n");
+		}
+
+		break;
+
+	default:
+		do_wake = false;
+		break;
+	}
+
+finish:
+	if (do_wake) {
+		/* Set compare and compare mask */
+		wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_INDEX,
+			       WBCIR_REGSEL_COMPARE | WBCIR_REG_ADDR0,
+			       0x3F);
+		outsb(data->wbase + WBCIR_REG_WCEIR_DATA, match, 11);
+		wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_INDEX,
+			       WBCIR_REGSEL_MASK | WBCIR_REG_ADDR0,
+			       0x3F);
+		outsb(data->wbase + WBCIR_REG_WCEIR_DATA, mask, 11);
+
+		/* RC6 Compare String Len */
+		outb(rc6_csl, data->wbase + WBCIR_REG_WCEIR_CSL);
+
+		/* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */
+		wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17);
+
+		/* Clear BUFF_EN, Clear END_EN, Set MATCH_EN */
+		wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x01, 0x07);
+
+		/* Set CEIR_EN */
+		wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x01, 0x01);
+
+	} else {
+		/* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */
+		wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
+
+		/* Clear CEIR_EN */
+		wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01);
+	}
+
+	/*
+	 * ACPI will set the HW disable bit for SP3 which means that the
+	 * output signals are left in an undefined state which may cause
+	 * spurious interrupts which we need to ignore until the hardware
+	 * is reinitialized.
+	 */
+	wbcir_set_irqmask(data, WBCIR_IRQ_NONE);
+	disable_irq(data->irq);
+}
+
+static int
+wbcir_suspend(struct pnp_dev *device, pm_message_t state)
+{
+	struct wbcir_data *data = pnp_get_drvdata(device);
+	led_classdev_suspend(&data->led);
+	wbcir_shutdown(device);
+	return 0;
+}
+
+static void
+wbcir_init_hw(struct wbcir_data *data)
+{
+	u8 tmp;
+
+	/* Disable interrupts */
+	wbcir_set_irqmask(data, WBCIR_IRQ_NONE);
+
+	/* Set PROT_SEL, RX_INV, Clear CEIR_EN (needed for the led) */
+	tmp = protocol << 4;
+	if (invert)
+		tmp |= 0x08;
+	outb(tmp, data->wbase + WBCIR_REG_WCEIR_CTL);
+
+	/* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */
+	wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17);
+
+	/* Clear BUFF_EN, Clear END_EN, Clear MATCH_EN */
+	wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
+
+	/* Set RC5 cell time to correspond to 36 kHz */
+	wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CFG1, 0x4A, 0x7F);
+
+	/* Set IRTX_INV */
+	if (invert)
+		outb(WBCIR_IRTX_INV, data->ebase + WBCIR_REG_ECEIR_CCTL);
+	else
+		outb(0x00, data->ebase + WBCIR_REG_ECEIR_CCTL);
+
+	/*
+	 * Clear IR LED, set SP3 clock to 24Mhz, set TX mask to IRTX1,
+	 * set SP3_IRRX_SW to binary 01, helpfully not documented
+	 */
+	outb(0x10, data->ebase + WBCIR_REG_ECEIR_CTS);
+	data->txmask = 0x1;
+
+	/* Enable extended mode */
+	wbcir_select_bank(data, WBCIR_BANK_2);
+	outb(WBCIR_EXT_ENABLE, data->sbase + WBCIR_REG_SP3_EXCR1);
+
+	/*
+	 * Configure baud generator, IR data will be sampled at
+	 * a bitrate of: (24Mhz * prescaler) / (divisor * 16).
+	 *
+	 * The ECIR registers include a flag to change the
+	 * 24Mhz clock freq to 48Mhz.
+	 *
+	 * It's not documented in the specs, but fifo levels
+	 * other than 16 seems to be unsupported.
+	 */
+
+	/* prescaler 1.0, tx/rx fifo lvl 16 */
+	outb(0x30, data->sbase + WBCIR_REG_SP3_EXCR2);
+
+	/* Set baud divisor to sample every 10 us */
+	outb(0x0f, data->sbase + WBCIR_REG_SP3_BGDL);
+	outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH);
+
+	/* Set CEIR mode */
+	wbcir_select_bank(data, WBCIR_BANK_0);
+	outb(0xC0, data->sbase + WBCIR_REG_SP3_MCR);
+	inb(data->sbase + WBCIR_REG_SP3_LSR); /* Clear LSR */
+	inb(data->sbase + WBCIR_REG_SP3_MSR); /* Clear MSR */
+
+	/* Disable RX demod, enable run-length enc/dec, set freq span */
+	wbcir_select_bank(data, WBCIR_BANK_7);
+	outb(0x90, data->sbase + WBCIR_REG_SP3_RCCFG);
+
+	/* Disable timer */
+	wbcir_select_bank(data, WBCIR_BANK_4);
+	outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR1);
+
+	/* Disable MSR interrupt, clear AUX_IRX, mask RX during TX? */
+	wbcir_select_bank(data, WBCIR_BANK_5);
+	outb(txandrx ? 0x03 : 0x02, data->sbase + WBCIR_REG_SP3_IRCR2);
+
+	/* Disable CRC */
+	wbcir_select_bank(data, WBCIR_BANK_6);
+	outb(0x20, data->sbase + WBCIR_REG_SP3_IRCR3);
+
+	/* Set RX demodulation freq, not really used */
+	wbcir_select_bank(data, WBCIR_BANK_7);
+	outb(0xF2, data->sbase + WBCIR_REG_SP3_IRRXDC);
+
+	/* Set TX modulation, 36kHz, 7us pulse width */
+	outb(0x69, data->sbase + WBCIR_REG_SP3_IRTXMC);
+	data->txcarrier = 36000;
+
+	/* Set invert and pin direction */
+	if (invert)
+		outb(0x10, data->sbase + WBCIR_REG_SP3_IRCFG4);
+	else
+		outb(0x00, data->sbase + WBCIR_REG_SP3_IRCFG4);
+
+	/* Set FIFO thresholds (RX = 8, TX = 3), reset RX/TX */
+	wbcir_select_bank(data, WBCIR_BANK_0);
+	outb(0x97, data->sbase + WBCIR_REG_SP3_FCR);
+
+	/* Clear AUX status bits */
+	outb(0xE0, data->sbase + WBCIR_REG_SP3_ASCR);
+
+	/* Clear RX state */
+	data->rxstate = WBCIR_RXSTATE_INACTIVE;
+	ir_raw_event_reset(data->dev);
+	ir_raw_event_set_idle(data->dev, true);
+
+	/* Clear TX state */
+	if (data->txstate == WBCIR_TXSTATE_ACTIVE) {
+		kfree(data->txbuf);
+		data->txbuf = NULL;
+		data->txstate = WBCIR_TXSTATE_INACTIVE;
+	}
+
+	/* Enable interrupts */
+	wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR);
+}
+
+static int
+wbcir_resume(struct pnp_dev *device)
+{
+	struct wbcir_data *data = pnp_get_drvdata(device);
+
+	wbcir_init_hw(data);
+	enable_irq(data->irq);
+	led_classdev_resume(&data->led);
+
+	return 0;
+}
+
+static int
+wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
+{
+	struct device *dev = &device->dev;
+	struct wbcir_data *data;
+	int err;
+
+	if (!(pnp_port_len(device, 0) == EHFUNC_IOMEM_LEN &&
+	      pnp_port_len(device, 1) == WAKEUP_IOMEM_LEN &&
+	      pnp_port_len(device, 2) == SP_IOMEM_LEN)) {
+		dev_err(dev, "Invalid resources\n");
+		return -ENODEV;
+	}
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		err = -ENOMEM;
+		goto exit;
+	}
+
+	pnp_set_drvdata(device, data);
+
+	spin_lock_init(&data->spinlock);
+	data->ebase = pnp_port_start(device, 0);
+	data->wbase = pnp_port_start(device, 1);
+	data->sbase = pnp_port_start(device, 2);
+	data->irq = pnp_irq(device, 0);
+
+	if (data->wbase == 0 || data->ebase == 0 ||
+	    data->sbase == 0 || data->irq == 0) {
+		err = -ENODEV;
+		dev_err(dev, "Invalid resources\n");
+		goto exit_free_data;
+	}
+
+	dev_dbg(&device->dev, "Found device "
+		"(w: 0x%lX, e: 0x%lX, s: 0x%lX, i: %u)\n",
+		data->wbase, data->ebase, data->sbase, data->irq);
+
+	data->led.name = "cir::activity";
+	data->led.default_trigger = "rc-feedback";
+	data->led.brightness_set = wbcir_led_brightness_set;
+	data->led.brightness_get = wbcir_led_brightness_get;
+	err = led_classdev_register(&device->dev, &data->led);
+	if (err)
+		goto exit_free_data;
+
+	data->dev = rc_allocate_device();
+	if (!data->dev) {
+		err = -ENOMEM;
+		goto exit_unregister_led;
+	}
+
+	data->dev->driver_type = RC_DRIVER_IR_RAW;
+	data->dev->driver_name = DRVNAME;
+	data->dev->input_name = WBCIR_NAME;
+	data->dev->input_phys = "wbcir/cir0";
+	data->dev->input_id.bustype = BUS_HOST;
+	data->dev->input_id.vendor = PCI_VENDOR_ID_WINBOND;
+	data->dev->input_id.product = WBCIR_ID_FAMILY;
+	data->dev->input_id.version = WBCIR_ID_CHIP;
+	data->dev->map_name = RC_MAP_RC6_MCE;
+	data->dev->s_idle = wbcir_idle_rx;
+	data->dev->s_carrier_report = wbcir_set_carrier_report;
+	data->dev->s_tx_mask = wbcir_txmask;
+	data->dev->s_tx_carrier = wbcir_txcarrier;
+	data->dev->tx_ir = wbcir_tx;
+	data->dev->priv = data;
+	data->dev->dev.parent = &device->dev;
+	data->dev->timeout = MS_TO_NS(100);
+	data->dev->rx_resolution = US_TO_NS(2);
+	data->dev->allowed_protocols = RC_BIT_ALL;
+
+	err = rc_register_device(data->dev);
+	if (err)
+		goto exit_free_rc;
+
+	if (!request_region(data->wbase, WAKEUP_IOMEM_LEN, DRVNAME)) {
+		dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
+			data->wbase, data->wbase + WAKEUP_IOMEM_LEN - 1);
+		err = -EBUSY;
+		goto exit_unregister_device;
+	}
+
+	if (!request_region(data->ebase, EHFUNC_IOMEM_LEN, DRVNAME)) {
+		dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
+			data->ebase, data->ebase + EHFUNC_IOMEM_LEN - 1);
+		err = -EBUSY;
+		goto exit_release_wbase;
+	}
+
+	if (!request_region(data->sbase, SP_IOMEM_LEN, DRVNAME)) {
+		dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
+			data->sbase, data->sbase + SP_IOMEM_LEN - 1);
+		err = -EBUSY;
+		goto exit_release_ebase;
+	}
+
+	err = request_irq(data->irq, wbcir_irq_handler,
+			  0, DRVNAME, device);
+	if (err) {
+		dev_err(dev, "Failed to claim IRQ %u\n", data->irq);
+		err = -EBUSY;
+		goto exit_release_sbase;
+	}
+
+	device_init_wakeup(&device->dev, 1);
+
+	wbcir_init_hw(data);
+
+	return 0;
+
+exit_release_sbase:
+	release_region(data->sbase, SP_IOMEM_LEN);
+exit_release_ebase:
+	release_region(data->ebase, EHFUNC_IOMEM_LEN);
+exit_release_wbase:
+	release_region(data->wbase, WAKEUP_IOMEM_LEN);
+exit_unregister_device:
+	rc_unregister_device(data->dev);
+	data->dev = NULL;
+exit_free_rc:
+	rc_free_device(data->dev);
+exit_unregister_led:
+	led_classdev_unregister(&data->led);
+exit_free_data:
+	kfree(data);
+	pnp_set_drvdata(device, NULL);
+exit:
+	return err;
+}
+
+static void
+wbcir_remove(struct pnp_dev *device)
+{
+	struct wbcir_data *data = pnp_get_drvdata(device);
+
+	/* Disable interrupts */
+	wbcir_set_irqmask(data, WBCIR_IRQ_NONE);
+	free_irq(data->irq, device);
+
+	/* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */
+	wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_STS, 0x17, 0x17);
+
+	/* Clear CEIR_EN */
+	wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01);
+
+	/* Clear BUFF_EN, END_EN, MATCH_EN */
+	wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07);
+
+	rc_unregister_device(data->dev);
+
+	led_classdev_unregister(&data->led);
+
+	/* This is ok since &data->led isn't actually used */
+	wbcir_led_brightness_set(&data->led, LED_OFF);
+
+	release_region(data->wbase, WAKEUP_IOMEM_LEN);
+	release_region(data->ebase, EHFUNC_IOMEM_LEN);
+	release_region(data->sbase, SP_IOMEM_LEN);
+
+	kfree(data);
+
+	pnp_set_drvdata(device, NULL);
+}
+
+static const struct pnp_device_id wbcir_ids[] = {
+	{ "WEC1022", 0 },
+	{ "", 0 }
+};
+MODULE_DEVICE_TABLE(pnp, wbcir_ids);
+
+static struct pnp_driver wbcir_driver = {
+	.name     = WBCIR_NAME,
+	.id_table = wbcir_ids,
+	.probe    = wbcir_probe,
+	.remove   = wbcir_remove,
+	.suspend  = wbcir_suspend,
+	.resume   = wbcir_resume,
+	.shutdown = wbcir_shutdown
+};
+
+static int __init
+wbcir_init(void)
+{
+	int ret;
+
+	switch (protocol) {
+	case IR_PROTOCOL_RC5:
+	case IR_PROTOCOL_NEC:
+	case IR_PROTOCOL_RC6:
+		break;
+	default:
+		pr_err("Invalid power-on protocol\n");
+	}
+
+	ret = pnp_register_driver(&wbcir_driver);
+	if (ret)
+		pr_err("Unable to register driver\n");
+
+	return ret;
+}
+
+static void __exit
+wbcir_exit(void)
+{
+	pnp_unregister_driver(&wbcir_driver);
+}
+
+module_init(wbcir_init);
+module_exit(wbcir_exit);
+
+MODULE_AUTHOR("David Härdeman <david@hardeman.nu>");
+MODULE_DESCRIPTION("Winbond SuperI/O Consumer IR Driver");
+MODULE_LICENSE("GPL");