[kernel/f14/master] IR stack update (matches 2.6.37-rc1-to-be code)

Jarod Wilson jwilson at fedoraproject.org
Mon Oct 11 15:38:54 UTC 2010


commit 866bf4f059d8d197d0758965bc93bb4483576ba0
Author: Jarod Wilson <jarod at redhat.com>
Date:   Mon Oct 11 11:37:53 2010 -0400

    IR stack update (matches 2.6.37-rc1-to-be code)
    
    - update imon driver to fix issues with key releases and properly
      auto-configure another 0xffdc device (VFD + MCE IR)
    - add new nuvoton-cir driver (for integrated IR in ASRock ION 330HT)
    - add lirc compat ioctl portability fixups
    
    Signed-off-by: Jarod Wilson <jarod at redhat.com>

 config-generic                           |    1 +
 kernel.spec                              |   12 +-
 linux-2.6-lirc-ioctl-compat-fixups.patch |  569 +++++++
 linux-2.6-v4l-dvb-ir-core-update-3.patch | 2709 ++++++++++++++++++++++++++++++
 4 files changed, 3290 insertions(+), 1 deletions(-)
---
diff --git a/config-generic b/config-generic
index a41fb89..9593409 100644
--- a/config-generic
+++ b/config-generic
@@ -2593,6 +2593,7 @@ CONFIG_IR_LIRC_CODEC=m
 CONFIG_IR_ENE=m
 CONFIG_IR_IMON=m
 CONFIG_IR_MCEUSB=m
+CONFIG_IR_NUVOTON=m
 CONFIG_IR_STREAMZAP=m
 CONFIG_IR_WINBOND_CIR=m
 
diff --git a/kernel.spec b/kernel.spec
index 088333c..aaa4f7b 100644
--- a/kernel.spec
+++ b/kernel.spec
@@ -48,7 +48,7 @@ Summary: The Linux kernel
 # reset this by hand to 1 (or to 0 and then use rpmdev-bumpspec).
 # scripts/rebase.sh should be made to do that for you, actually.
 #
-%global baserelease 39
+%global baserelease 40
 %global fedora_build %{baserelease}
 
 # base_sublevel is the kernel version we're starting with and patching
@@ -701,6 +701,8 @@ Patch2915: lirc-staging-2.6.36.patch
 #Patch2916: lirc-staging-2.6.36-fixes.patch
 Patch2917: hdpvr-ir-enable.patch
 Patch2918: linux-2.6-v4l-dvb-ir-core-update-2.patch
+Patch2919: linux-2.6-v4l-dvb-ir-core-update-3.patch
+Patch2920: linux-2.6-lirc-ioctl-compat-fixups.patch
 
 Patch2950: linux-2.6-via-velocity-dma-fix.patch
 
@@ -1341,6 +1343,8 @@ ApplyPatch lirc-staging-2.6.36.patch
 # enable IR receiver on Hauppauge HD PVR (v4l-dvb merge pending)
 ApplyPatch hdpvr-ir-enable.patch
 ApplyPatch linux-2.6-v4l-dvb-ir-core-update-2.patch
+ApplyPatch linux-2.6-v4l-dvb-ir-core-update-3.patch
+ApplyPatch linux-2.6-lirc-ioctl-compat-fixups.patch
 
 # Fix DMA bug on via-velocity
 ApplyPatch linux-2.6-via-velocity-dma-fix.patch
@@ -1979,6 +1983,12 @@ fi
 # and build.
 
 %changelog
+* Mon Oct 11 2010 Jarod Wilson <jarod at redhat.com> 2.6.35.6-40
+- update imon driver to fix issues with key releases and properly
+  auto-configure another 0xffdc device (VFD + MCE IR)
+- add new nuvoton-cir driver (for integrated IR in ASRock ION 330HT)
+- add lirc compat ioctl portability fixups
+
 * Mon Oct 11 2010 Ben Skeggs <bskeggs at redhat.com>
 - fix ttm bug that can cause nouveau to crash
 
diff --git a/linux-2.6-lirc-ioctl-compat-fixups.patch b/linux-2.6-lirc-ioctl-compat-fixups.patch
new file mode 100644
index 0000000..eec5a22
--- /dev/null
+++ b/linux-2.6-lirc-ioctl-compat-fixups.patch
@@ -0,0 +1,569 @@
+ drivers/media/IR/ir-lirc-codec.c        |   13 +++++++----
+ drivers/media/IR/lirc_dev.c             |   36 ++++++++++++++++++++----------
+ drivers/staging/lirc/lirc_igorplugusb.c |    2 +-
+ drivers/staging/lirc/lirc_it87.c        |   20 +++++++++-------
+ drivers/staging/lirc/lirc_ite8709.c     |    6 ++--
+ drivers/staging/lirc/lirc_parallel.c    |   35 ++++++++++++++++-------------
+ drivers/staging/lirc/lirc_serial.c      |   24 +++++++++++---------
+ drivers/staging/lirc/lirc_sir.c         |   24 +++++++++++---------
+ drivers/staging/lirc/lirc_zilog.c       |    3 ++
+ include/media/lirc_dev.h                |    6 ++--
+ 10 files changed, 98 insertions(+), 71 deletions(-)
+
+diff --git a/drivers/media/IR/ir-lirc-codec.c b/drivers/media/IR/ir-lirc-codec.c
+index e63f757..20ac9a4 100644
+--- a/drivers/media/IR/ir-lirc-codec.c
++++ b/drivers/media/IR/ir-lirc-codec.c
+@@ -102,7 +102,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
+ 	struct ir_input_dev *ir_dev;
+ 	int ret = 0;
+ 	void *drv_data;
+-	unsigned long val = 0;
++	__u32 val = 0;
+ 
+ 	lirc = lirc_get_pdata(filep);
+ 	if (!lirc)
+@@ -115,7 +115,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
+ 	drv_data = ir_dev->props->priv;
+ 
+ 	if (_IOC_DIR(cmd) & _IOC_WRITE) {
+-		ret = get_user(val, (unsigned long *)arg);
++		ret = get_user(val, (__u32 *)arg);
+ 		if (ret)
+ 			return ret;
+ 	}
+@@ -135,14 +135,14 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
+ 	/* TX settings */
+ 	case LIRC_SET_TRANSMITTER_MASK:
+ 		if (ir_dev->props->s_tx_mask)
+-			ret = ir_dev->props->s_tx_mask(drv_data, (u32)val);
++			ret = ir_dev->props->s_tx_mask(drv_data, val);
+ 		else
+ 			return -EINVAL;
+ 		break;
+ 
+ 	case LIRC_SET_SEND_CARRIER:
+ 		if (ir_dev->props->s_tx_carrier)
+-			ir_dev->props->s_tx_carrier(drv_data, (u32)val);
++			ir_dev->props->s_tx_carrier(drv_data, val);
+ 		else
+ 			return -EINVAL;
+ 		break;
+@@ -212,7 +212,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
+ 	}
+ 
+ 	if (_IOC_DIR(cmd) & _IOC_READ)
+-		ret = put_user(val, (unsigned long *)arg);
++		ret = put_user(val, (__u32 *)arg);
+ 
+ 	return ret;
+ }
+@@ -231,6 +231,9 @@ static 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,
+diff --git a/drivers/media/IR/lirc_dev.c b/drivers/media/IR/lirc_dev.c
+index 899891b..930e4a7 100644
+--- a/drivers/media/IR/lirc_dev.c
++++ b/drivers/media/IR/lirc_dev.c
+@@ -161,6 +161,9 @@ static struct file_operations fops = {
+ 	.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,
+ };
+@@ -359,19 +362,23 @@ int lirc_unregister_driver(int minor)
+ 	struct irctl *ir;
+ 
+ 	if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {
+-		printk(KERN_ERR "lirc_dev: lirc_unregister_driver: "
+-		       "\"minor (%d)\" must be between 0 and %d!\n",
+-		       minor, MAX_IRCTL_DEVICES-1);
++		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;
++	}
+ 
+ 	mutex_lock(&lirc_dev_lock);
+ 
+ 	if (ir->d.minor != minor) {
+-		printk(KERN_ERR "lirc_dev: lirc_unregister_driver: "
+-		       "minor (%d) device not registered!", minor);
++		printk(KERN_ERR "lirc_dev: %s: minor (%d) device not "
++		       "registered!\n", __func__, minor);
+ 		mutex_unlock(&lirc_dev_lock);
+ 		return -ENOENT;
+ 	}
+@@ -519,10 +526,15 @@ EXPORT_SYMBOL(lirc_dev_fop_poll);
+ 
+ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+ {
+-	unsigned long mode;
++	__u32 mode;
+ 	int result = 0;
+ 	struct irctl *ir = file->private_data;
+ 
++	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);
+ 
+@@ -536,7 +548,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+ 
+ 	switch (cmd) {
+ 	case LIRC_GET_FEATURES:
+-		result = put_user(ir->d.features, (unsigned long *)arg);
++		result = put_user(ir->d.features, (__u32 *)arg);
+ 		break;
+ 	case LIRC_GET_REC_MODE:
+ 		if (!(ir->d.features & LIRC_CAN_REC_MASK)) {
+@@ -546,7 +558,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+ 
+ 		result = put_user(LIRC_REC2MODE
+ 				  (ir->d.features & LIRC_CAN_REC_MASK),
+-				  (unsigned long *)arg);
++				  (__u32 *)arg);
+ 		break;
+ 	case LIRC_SET_REC_MODE:
+ 		if (!(ir->d.features & LIRC_CAN_REC_MASK)) {
+@@ -554,7 +566,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+ 			break;
+ 		}
+ 
+-		result = get_user(mode, (unsigned long *)arg);
++		result = get_user(mode, (__u32 *)arg);
+ 		if (!result && !(LIRC_MODE2REC(mode) & ir->d.features))
+ 			result = -EINVAL;
+ 		/*
+@@ -563,7 +575,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+ 		 */
+ 		break;
+ 	case LIRC_GET_LENGTH:
+-		result = put_user(ir->d.code_length, (unsigned long *)arg);
++		result = put_user(ir->d.code_length, (__u32 *)arg);
+ 		break;
+ 	case LIRC_GET_MIN_TIMEOUT:
+ 		if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||
+@@ -572,7 +584,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+ 			break;
+ 		}
+ 
+-		result = put_user(ir->d.min_timeout, (unsigned long *)arg);
++		result = put_user(ir->d.min_timeout, (__u32 *)arg);
+ 		break;
+ 	case LIRC_GET_MAX_TIMEOUT:
+ 		if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||
+@@ -581,7 +593,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+ 			break;
+ 		}
+ 
+-		result = put_user(ir->d.max_timeout, (unsigned long *)arg);
++		result = put_user(ir->d.max_timeout, (__u32 *)arg);
+ 		break;
+ 	default:
+ 		result = -EINVAL;
+diff --git a/drivers/staging/lirc/lirc_igorplugusb.c b/drivers/staging/lirc/lirc_igorplugusb.c
+index bce600e..e680d88 100644
+--- a/drivers/staging/lirc/lirc_igorplugusb.c
++++ b/drivers/staging/lirc/lirc_igorplugusb.c
+@@ -390,7 +390,7 @@ static int usb_remote_probe(struct usb_interface *intf,
+ 	devnum = dev->devnum;
+ 	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+ 
+-	dprintk(DRIVER_NAME "[%d]: bytes_in_key=%lu maxp=%d\n",
++	dprintk(DRIVER_NAME "[%d]: bytes_in_key=%zu maxp=%d\n",
+ 		devnum, CODE_LENGTH, maxp);
+ 
+ 
+diff --git a/drivers/staging/lirc/lirc_it87.c b/drivers/staging/lirc/lirc_it87.c
+index ec11c0e..bd5006c 100644
+--- a/drivers/staging/lirc/lirc_it87.c
++++ b/drivers/staging/lirc/lirc_it87.c
+@@ -239,8 +239,7 @@ static ssize_t lirc_write(struct file *file, const char *buf,
+ static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+ {
+ 	int retval = 0;
+-	unsigned long value = 0;
+-	unsigned int ivalue;
++	__u32 value = 0;
+ 	unsigned long hw_flags;
+ 
+ 	if (cmd == LIRC_GET_FEATURES)
+@@ -256,24 +255,24 @@ static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+ 	case LIRC_GET_FEATURES:
+ 	case LIRC_GET_SEND_MODE:
+ 	case LIRC_GET_REC_MODE:
+-		retval = put_user(value, (unsigned long *) arg);
++		retval = put_user(value, (__u32 *) arg);
+ 		break;
+ 
+ 	case LIRC_SET_SEND_MODE:
+ 	case LIRC_SET_REC_MODE:
+-		retval = get_user(value, (unsigned long *) arg);
++		retval = get_user(value, (__u32 *) arg);
+ 		break;
+ 
+ 	case LIRC_SET_SEND_CARRIER:
+-		retval = get_user(ivalue, (unsigned int *) arg);
++		retval = get_user(value, (__u32 *) arg);
+ 		if (retval)
+ 			return retval;
+-		ivalue /= 1000;
+-		if (ivalue > IT87_CIR_FREQ_MAX ||
+-		    ivalue < IT87_CIR_FREQ_MIN)
++		value /= 1000;
++		if (value > IT87_CIR_FREQ_MAX ||
++		    value < IT87_CIR_FREQ_MIN)
+ 			return -EINVAL;
+ 
+-		it87_freq = ivalue;
++		it87_freq = value;
+ 
+ 		spin_lock_irqsave(&hardware_lock, hw_flags);
+ 		outb(((inb(io + IT87_CIR_TCR2) & IT87_CIR_TCR2_TXMPW) |
+@@ -340,6 +339,9 @@ static const struct file_operations lirc_fops = {
+ 	.write		= lirc_write,
+ 	.poll		= lirc_poll,
+ 	.unlocked_ioctl	= lirc_ioctl,
++#ifdef CONFIG_COMPAT
++	.compat_ioctl	= lirc_ioctl,
++#endif
+ 	.open		= lirc_open,
+ 	.release	= lirc_close,
+ };
+diff --git a/drivers/staging/lirc/lirc_ite8709.c b/drivers/staging/lirc/lirc_ite8709.c
+index 9352f45..cb20cfd 100644
+--- a/drivers/staging/lirc/lirc_ite8709.c
++++ b/drivers/staging/lirc/lirc_ite8709.c
+@@ -102,8 +102,8 @@ struct ite8709_device {
+ 	int io;
+ 	int irq;
+ 	spinlock_t hardware_lock;
+-	unsigned long long acc_pulse;
+-	unsigned long long acc_space;
++	__u64 acc_pulse;
++	__u64 acc_space;
+ 	char lastbit;
+ 	struct timeval last_tv;
+ 	struct lirc_driver driver;
+@@ -220,7 +220,7 @@ static void ite8709_set_use_dec(void *data)
+ }
+ 
+ static void ite8709_add_read_queue(struct ite8709_device *dev, int flag,
+-					unsigned long long val)
++				   __u64 val)
+ {
+ 	int value;
+ 
+diff --git a/drivers/staging/lirc/lirc_parallel.c b/drivers/staging/lirc/lirc_parallel.c
+index 6da4a8c..b8cce87 100644
+--- a/drivers/staging/lirc/lirc_parallel.c
++++ b/drivers/staging/lirc/lirc_parallel.c
+@@ -301,9 +301,9 @@ static void irq_handler(void *blah)
+ 
+ 	if (signal != 0) {
+ 		/* ajust value to usecs */
+-		unsigned long long helper;
++		__u64 helper;
+ 
+-		helper = ((unsigned long long) signal)*1000000;
++		helper = ((__u64) signal)*1000000;
+ 		do_div(helper, timer);
+ 		signal = (long) helper;
+ 
+@@ -404,9 +404,9 @@ static ssize_t lirc_write(struct file *filep, const char *buf, size_t n,
+ 
+ 	/* adjust values from usecs */
+ 	for (i = 0; i < count; i++) {
+-		unsigned long long helper;
++		__u64 helper;
+ 
+-		helper = ((unsigned long long) wbuf[i])*timer;
++		helper = ((__u64) wbuf[i])*timer;
+ 		do_div(helper, 1000000);
+ 		wbuf[i] = (int) helper;
+ 	}
+@@ -464,48 +464,48 @@ static unsigned int lirc_poll(struct file *file, poll_table *wait)
+ static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+ {
+ 	int result;
+-	unsigned long features = LIRC_CAN_SET_TRANSMITTER_MASK |
+-				 LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
+-	unsigned long mode;
+-	unsigned int ivalue;
++	__u32 features = LIRC_CAN_SET_TRANSMITTER_MASK |
++			 LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
++	__u32 mode;
++	__u32 value;
+ 
+ 	switch (cmd) {
+ 	case LIRC_GET_FEATURES:
+-		result = put_user(features, (unsigned long *) arg);
++		result = put_user(features, (__u32 *) arg);
+ 		if (result)
+ 			return result;
+ 		break;
+ 	case LIRC_GET_SEND_MODE:
+-		result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg);
++		result = put_user(LIRC_MODE_PULSE, (__u32 *) arg);
+ 		if (result)
+ 			return result;
+ 		break;
+ 	case LIRC_GET_REC_MODE:
+-		result = put_user(LIRC_MODE_MODE2, (unsigned long *) arg);
++		result = put_user(LIRC_MODE_MODE2, (__u32 *) arg);
+ 		if (result)
+ 			return result;
+ 		break;
+ 	case LIRC_SET_SEND_MODE:
+-		result = get_user(mode, (unsigned long *) arg);
++		result = get_user(mode, (__u32 *) arg);
+ 		if (result)
+ 			return result;
+ 		if (mode != LIRC_MODE_PULSE)
+ 			return -EINVAL;
+ 		break;
+ 	case LIRC_SET_REC_MODE:
+-		result = get_user(mode, (unsigned long *) arg);
++		result = get_user(mode, (__u32 *) arg);
+ 		if (result)
+ 			return result;
+ 		if (mode != LIRC_MODE_MODE2)
+ 			return -ENOSYS;
+ 		break;
+ 	case LIRC_SET_TRANSMITTER_MASK:
+-		result = get_user(ivalue, (unsigned int *) arg);
++		result = get_user(value, (__u32 *) arg);
+ 		if (result)
+ 			return result;
+-		if ((ivalue & LIRC_PARALLEL_TRANSMITTER_MASK) != ivalue)
++		if ((value & LIRC_PARALLEL_TRANSMITTER_MASK) != value)
+ 			return LIRC_PARALLEL_MAX_TRANSMITTERS;
+-		tx_mask = ivalue;
++		tx_mask = value;
+ 		break;
+ 	default:
+ 		return -ENOIOCTLCMD;
+@@ -546,6 +546,9 @@ static const struct file_operations lirc_fops = {
+ 	.write		= lirc_write,
+ 	.poll		= lirc_poll,
+ 	.unlocked_ioctl	= lirc_ioctl,
++#ifdef CONFIG_COMPAT
++	.compat_ioctl	= lirc_ioctl,
++#endif
+ 	.open		= lirc_open,
+ 	.release	= lirc_close
+ };
+diff --git a/drivers/staging/lirc/lirc_serial.c b/drivers/staging/lirc/lirc_serial.c
+index 9456f8e..05a9bf3 100644
+--- a/drivers/staging/lirc/lirc_serial.c
++++ b/drivers/staging/lirc/lirc_serial.c
+@@ -372,7 +372,7 @@ static unsigned long conv_us_to_clocks;
+ static int init_timing_params(unsigned int new_duty_cycle,
+ 		unsigned int new_freq)
+ {
+-	unsigned long long loops_per_sec, work;
++	__u64 loops_per_sec, work;
+ 
+ 	duty_cycle = new_duty_cycle;
+ 	freq = new_freq;
+@@ -987,8 +987,7 @@ static ssize_t lirc_write(struct file *file, const char *buf,
+ static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+ {
+ 	int result;
+-	unsigned long value;
+-	unsigned int ivalue;
++	__u32 value;
+ 
+ 	switch (cmd) {
+ 	case LIRC_GET_SEND_MODE:
+@@ -997,7 +996,7 @@ static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+ 
+ 		result = put_user(LIRC_SEND2MODE
+ 				  (hardware[type].features&LIRC_CAN_SEND_MASK),
+-				  (unsigned long *) arg);
++				  (__u32 *) arg);
+ 		if (result)
+ 			return result;
+ 		break;
+@@ -1006,7 +1005,7 @@ static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+ 		if (!(hardware[type].features&LIRC_CAN_SEND_MASK))
+ 			return -ENOIOCTLCMD;
+ 
+-		result = get_user(value, (unsigned long *) arg);
++		result = get_user(value, (__u32 *) arg);
+ 		if (result)
+ 			return result;
+ 		/* only LIRC_MODE_PULSE supported */
+@@ -1023,12 +1022,12 @@ static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+ 		if (!(hardware[type].features&LIRC_CAN_SET_SEND_DUTY_CYCLE))
+ 			return -ENOIOCTLCMD;
+ 
+-		result = get_user(ivalue, (unsigned int *) arg);
++		result = get_user(value, (__u32 *) arg);
+ 		if (result)
+ 			return result;
+-		if (ivalue <= 0 || ivalue > 100)
++		if (value <= 0 || value > 100)
+ 			return -EINVAL;
+-		return init_timing_params(ivalue, freq);
++		return init_timing_params(value, freq);
+ 		break;
+ 
+ 	case LIRC_SET_SEND_CARRIER:
+@@ -1036,12 +1035,12 @@ static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+ 		if (!(hardware[type].features&LIRC_CAN_SET_SEND_CARRIER))
+ 			return -ENOIOCTLCMD;
+ 
+-		result = get_user(ivalue, (unsigned int *) arg);
++		result = get_user(value, (__u32 *) arg);
+ 		if (result)
+ 			return result;
+-		if (ivalue > 500000 || ivalue < 20000)
++		if (value > 500000 || value < 20000)
+ 			return -EINVAL;
+-		return init_timing_params(duty_cycle, ivalue);
++		return init_timing_params(duty_cycle, value);
+ 		break;
+ 
+ 	default:
+@@ -1054,6 +1053,9 @@ static const struct file_operations lirc_fops = {
+ 	.owner		= THIS_MODULE,
+ 	.write		= lirc_write,
+ 	.unlocked_ioctl	= lirc_ioctl,
++#ifdef CONFIG_COMPAT
++	.compat_ioctl	= lirc_ioctl,
++#endif
+ 	.read		= lirc_dev_fop_read,
+ 	.poll		= lirc_dev_fop_poll,
+ 	.open		= lirc_dev_fop_open,
+diff --git a/drivers/staging/lirc/lirc_sir.c b/drivers/staging/lirc/lirc_sir.c
+index eb08fa7..c4cb3aa 100644
+--- a/drivers/staging/lirc/lirc_sir.c
++++ b/drivers/staging/lirc/lirc_sir.c
+@@ -336,9 +336,8 @@ static ssize_t lirc_write(struct file *file, const char *buf, size_t n,
+ static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+ {
+ 	int retval = 0;
+-	unsigned long value = 0;
++	__u32 value = 0;
+ #ifdef LIRC_ON_SA1100
+-	unsigned int ivalue;
+ 
+ 	if (cmd == LIRC_GET_FEATURES)
+ 		value = LIRC_CAN_SEND_PULSE |
+@@ -362,22 +361,22 @@ static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+ 	case LIRC_GET_FEATURES:
+ 	case LIRC_GET_SEND_MODE:
+ 	case LIRC_GET_REC_MODE:
+-		retval = put_user(value, (unsigned long *) arg);
++		retval = put_user(value, (__u32 *) arg);
+ 		break;
+ 
+ 	case LIRC_SET_SEND_MODE:
+ 	case LIRC_SET_REC_MODE:
+-		retval = get_user(value, (unsigned long *) arg);
++		retval = get_user(value, (__u32 *) arg);
+ 		break;
+ #ifdef LIRC_ON_SA1100
+ 	case LIRC_SET_SEND_DUTY_CYCLE:
+-		retval = get_user(ivalue, (unsigned int *) arg);
++		retval = get_user(value, (__u32 *) arg);
+ 		if (retval)
+ 			return retval;
+-		if (ivalue <= 0 || ivalue > 100)
++		if (value <= 0 || value > 100)
+ 			return -EINVAL;
+-		/* (ivalue/100)*(1000000/freq) */
+-		duty_cycle = ivalue;
++		/* (value/100)*(1000000/freq) */
++		duty_cycle = value;
+ 		pulse_width = (unsigned long) duty_cycle*10000/freq;
+ 		space_width = (unsigned long) 1000000L/freq-pulse_width;
+ 		if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
+@@ -386,12 +385,12 @@ static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
+ 			space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
+ 		break;
+ 	case LIRC_SET_SEND_CARRIER:
+-		retval = get_user(ivalue, (unsigned int *) arg);
++		retval = get_user(value, (__u32 *) arg);
+ 		if (retval)
+ 			return retval;
+-		if (ivalue > 500000 || ivalue < 20000)
++		if (value > 500000 || value < 20000)
+ 			return -EINVAL;
+-		freq = ivalue;
++		freq = value;
+ 		pulse_width = (unsigned long) duty_cycle*10000/freq;
+ 		space_width = (unsigned long) 1000000L/freq-pulse_width;
+ 		if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
+@@ -457,6 +456,9 @@ static const struct file_operations lirc_fops = {
+ 	.write		= lirc_write,
+ 	.poll		= lirc_poll,
+ 	.unlocked_ioctl	= lirc_ioctl,
++#ifdef CONFIG_COMPAT
++	.compat_ioctl	= lirc_ioctl,
++#endif
+ 	.open		= lirc_dev_fop_open,
+ 	.release	= lirc_dev_fop_close,
+ };
+diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c
+index 100caab..d920644 100644
+--- a/drivers/staging/lirc/lirc_zilog.c
++++ b/drivers/staging/lirc/lirc_zilog.c
+@@ -1139,6 +1139,9 @@ static const struct file_operations lirc_fops = {
+ 	.write		= write,
+ 	.poll		= poll,
+ 	.unlocked_ioctl	= ioctl,
++#ifdef CONFIG_COMPAT
++	.compat_ioctl	= ioctl,
++#endif
+ 	.open		= open,
+ 	.release	= close
+ };
+diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h
+index b1f6066..54780a5 100644
+--- a/include/media/lirc_dev.h
++++ b/include/media/lirc_dev.h
+@@ -125,10 +125,10 @@ static inline unsigned int lirc_buffer_write(struct lirc_buffer *buf,
+ struct lirc_driver {
+ 	char name[40];
+ 	int minor;
+-	unsigned long code_length;
++	__u32 code_length;
+ 	unsigned int buffer_size; /* in chunks holding one code each */
+ 	int sample_rate;
+-	unsigned long features;
++	__u32 features;
+ 
+ 	unsigned int chunk_size;
+ 
+@@ -139,7 +139,7 @@ struct lirc_driver {
+ 	struct lirc_buffer *rbuf;
+ 	int (*set_use_inc) (void *data);
+ 	void (*set_use_dec) (void *data);
+-	struct file_operations *fops;
++	const struct file_operations *fops;
+ 	struct device *dev;
+ 	struct module *owner;
+ };
diff --git a/linux-2.6-v4l-dvb-ir-core-update-3.patch b/linux-2.6-v4l-dvb-ir-core-update-3.patch
new file mode 100644
index 0000000..2710fc4
--- /dev/null
+++ b/linux-2.6-v4l-dvb-ir-core-update-3.patch
@@ -0,0 +1,2709 @@
+ drivers/media/IR/Kconfig         |   27 +-
+ drivers/media/IR/Makefile        |    1 +
+ drivers/media/IR/imon.c          |  583 +++++++++++--------
+ drivers/media/IR/ir-keytable.c   |    3 +-
+ drivers/media/IR/nuvoton-cir.c   | 1237 ++++++++++++++++++++++++++++++++++++++
+ drivers/media/IR/nuvoton-cir.h   |  408 +++++++++++++
+ include/media/ir-core.h          |    1 +
+ 7 files changed, 2010 insertions(+), 250 deletions(-)
+
+diff --git a/drivers/media/IR/Kconfig b/drivers/media/IR/Kconfig
+index 152000d..d67fff3 100644
+--- a/drivers/media/IR/Kconfig
++++ b/drivers/media/IR/Kconfig
+@@ -101,6 +101,20 @@ config IR_LIRC_CODEC
+ 	   Enable this option to pass raw IR to and from userspace via
+ 	   the LIRC interface.
+ 
++config IR_ENE
++	tristate "ENE eHome Receiver/Transciever (pnp id: ENE0100/ENE02xxx)"
++	depends on PNP
++	depends on IR_CORE
++	---help---
++	   Say Y here to enable support for integrated infrared receiver
++	   /transciever made by ENE.
++
++	   You can see if you have it by looking at lspnp output.
++	   Output should include ENE0100 ENE0200 or something similiar.
++
++	   To compile this driver as a module, choose M here: the
++	   module will be called ene_ir.
++
+ config IR_IMON
+ 	tristate "SoundGraph iMON Receiver and Display"
+ 	depends on USB_ARCH_HAS_HCD
+@@ -125,19 +139,18 @@ config IR_MCEUSB
+ 	   To compile this driver as a module, choose M here: the
+ 	   module will be called mceusb.
+ 
+-config IR_ENE
+-	tristate "ENE eHome Receiver/Transciever (pnp id: ENE0100/ENE02xxx)"
++config IR_NUVOTON
++	tristate "Nuvoton w836x7hg Consumer Infrared Transceiver"
+ 	depends on PNP
+ 	depends on IR_CORE
+ 	---help---
+ 	   Say Y here to enable support for integrated infrared receiver
+-	   /transciever made by ENE.
+-
+-	   You can see if you have it by looking at lspnp output.
+-	   Output should include ENE0100 ENE0200 or something similiar.
++	   /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 ene_ir.
++	   module will be called nuvoton-cir.
+ 
+ config IR_STREAMZAP
+ 	tristate "Streamzap PC Remote IR Receiver"
+diff --git a/drivers/media/IR/Makefile b/drivers/media/IR/Makefile
+index 953c6c4..f9574ad 100644
+--- a/drivers/media/IR/Makefile
++++ b/drivers/media/IR/Makefile
+@@ -17,5 +17,6 @@ obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o
+ # stand-alone IR receivers/transmitters
+ obj-$(CONFIG_IR_IMON) += imon.o
+ obj-$(CONFIG_IR_MCEUSB) += mceusb.o
++obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o
+ obj-$(CONFIG_IR_ENE) += ene_ir.o
+ obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
+diff --git a/drivers/media/IR/imon.c b/drivers/media/IR/imon.c
+index c185422..7a97176 100644
+--- a/drivers/media/IR/imon.c
++++ b/drivers/media/IR/imon.c
+@@ -1,7 +1,7 @@
+ /*
+  *   imon.c:	input and display driver for SoundGraph iMON IR/VFD/LCD
+  *
+- *   Copyright(C) 2009  Jarod Wilson <jarod at wilsonet.com>
++ *   Copyright(C) 2010  Jarod Wilson <jarod at wilsonet.com>
+  *   Portions based on the original lirc_imon driver,
+  *	Copyright(C) 2004  Venky Raju(dev at venky.ws)
+  *
+@@ -44,7 +44,7 @@
+ #define MOD_AUTHOR	"Jarod Wilson <jarod at wilsonet.com>"
+ #define MOD_DESC	"Driver for SoundGraph iMON MultiMedia IR/Display"
+ #define MOD_NAME	"imon"
+-#define MOD_VERSION	"0.9.1"
++#define MOD_VERSION	"0.9.2"
+ 
+ #define DISPLAY_MINOR_BASE	144
+ #define DEVICE_NAME	"lcd%d"
+@@ -121,21 +121,26 @@ struct imon_context {
+ 	u16 vendor;			/* usb vendor ID */
+ 	u16 product;			/* usb product ID */
+ 
+-	struct input_dev *idev;		/* input device for remote */
++	struct input_dev *rdev;		/* input 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 ir_type;			/* iMON or MCE (RC6) IR protocol? */
+-	u8 mce_toggle_bit;		/* last mce toggle bit */
+ 	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 */
+-	struct timer_list itimer;	/* input device timer, need for rc6 */
+ 
+ 	char name_touch[128];		/* touch screen name */
+ 	char phys_touch[64];		/* touch screen phys path */
+@@ -287,6 +292,9 @@ static const struct {
+ 	{ 0x000100000000ffeell, KEY_VOLUMEUP },
+ 	{ 0x010000000000ffeell, KEY_VOLUMEDOWN },
+ 	{ 0x000000000100ffeell, KEY_MUTE },
++	/* 0xffdc iMON MCE VFD */
++	{ 0x00010000ffffffeell, KEY_VOLUMEUP },
++	{ 0x01000000ffffffeell, KEY_VOLUMEDOWN },
+ 	/* iMON Knob values */
+ 	{ 0x000100ffffffffeell, KEY_VOLUMEUP },
+ 	{ 0x010000ffffffffeell, KEY_VOLUMEDOWN },
+@@ -956,17 +964,6 @@ static void usb_tx_callback(struct urb *urb)
+ }
+ 
+ /**
+- * mce/rc6 keypresses have no distinct release code, use timer
+- */
+-static void imon_mce_timeout(unsigned long data)
+-{
+-	struct imon_context *ictx = (struct imon_context *)data;
+-
+-	input_report_key(ictx->idev, ictx->last_keycode, 0);
+-	input_sync(ictx->idev);
+-}
+-
+-/**
+  * report touchscreen input
+  */
+ static void imon_touch_display_timeout(unsigned long data)
+@@ -1006,9 +1003,6 @@ int imon_ir_change_protocol(void *priv, u64 ir_type)
+ 		dev_dbg(dev, "Configuring IR receiver for MCE protocol\n");
+ 		ir_proto_packet[0] = 0x01;
+ 		pad_mouse = false;
+-		init_timer(&ictx->itimer);
+-		ictx->itimer.data = (unsigned long)ictx;
+-		ictx->itimer.function = imon_mce_timeout;
+ 		break;
+ 	case IR_TYPE_UNKNOWN:
+ 	case IR_TYPE_OTHER:
+@@ -1147,20 +1141,21 @@ static int stabilize(int a, int b, u16 timeout, u16 threshold)
+ 	return result;
+ }
+ 
+-static u32 imon_remote_key_lookup(struct imon_context *ictx, u32 hw_code)
++static u32 imon_remote_key_lookup(struct imon_context *ictx, u32 scancode)
+ {
+-	u32 scancode = be32_to_cpu(hw_code);
+ 	u32 keycode;
+ 	u32 release;
+ 	bool is_release_code = false;
+ 
+ 	/* Look for the initial press of a button */
+-	keycode = ir_g_keycode_from_table(ictx->idev, scancode);
++	keycode = ir_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 = ir_g_keycode_from_table(ictx->idev, release);
++		keycode = ir_g_keycode_from_table(ictx->rdev, release);
+ 		if (keycode != KEY_RESERVED)
+ 			is_release_code = true;
+ 	}
+@@ -1170,9 +1165,8 @@ static u32 imon_remote_key_lookup(struct imon_context *ictx, u32 hw_code)
+ 	return keycode;
+ }
+ 
+-static u32 imon_mce_key_lookup(struct imon_context *ictx, u32 hw_code)
++static u32 imon_mce_key_lookup(struct imon_context *ictx, u32 scancode)
+ {
+-	u32 scancode = be32_to_cpu(hw_code);
+ 	u32 keycode;
+ 
+ #define MCE_KEY_MASK 0x7000
+@@ -1186,18 +1180,21 @@ static u32 imon_mce_key_lookup(struct imon_context *ictx, u32 hw_code)
+ 	 * 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 >> 24) & 0x80)
++	if (scancode & 0x80000000)
+ 		scancode = scancode | MCE_KEY_MASK | MCE_TOGGLE_BIT;
+ 
+-	keycode = ir_g_keycode_from_table(ictx->idev, scancode);
++	ictx->rc_scancode = scancode;
++	keycode = ir_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(u64 hw_code)
++static u32 imon_panel_key_lookup(u64 code)
+ {
+ 	int i;
+-	u64 code = be64_to_cpu(hw_code);
+ 	u32 keycode = KEY_RESERVED;
+ 
+ 	for (i = 0; i < ARRAY_SIZE(imon_panel_key_table); i++) {
+@@ -1217,6 +1214,9 @@ static bool imon_mouse_event(struct imon_context *ictx,
+ 	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) {
+@@ -1248,6 +1248,8 @@ static bool imon_mouse_event(struct imon_context *ictx,
+ 	} 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");
+ 
+@@ -1262,7 +1264,9 @@ static bool imon_mouse_event(struct imon_context *ictx,
+ 					 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;
+@@ -1284,8 +1288,8 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf)
+ 	int dir = 0;
+ 	char rel_x = 0x00, rel_y = 0x00;
+ 	u16 timeout, threshold;
+-	u64 temp_key;
+-	u32 remote_key;
++	u32 scancode = KEY_RESERVED;
++	unsigned long flags;
+ 
+ 	/*
+ 	 * The imon directional pad functions more like a touchpad. Bytes 3 & 4
+@@ -1309,26 +1313,36 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf)
+ 				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;
+-				memcpy(&temp_key, buf, sizeof(temp_key));
+-				remote_key = (u32) (le64_to_cpu(temp_key)
+-						    & 0xffffffff);
+-				ictx->kc = imon_remote_key_lookup(ictx,
+-								  remote_key);
++				scancode = be32_to_cpu(*((u32 *)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;
+-				ictx->kc = (rel_y > 0) ? KEY_DOWN : KEY_UP;
++				if (rel_y > 0)
++					scancode = 0x01007f00; /* KEY_DOWN */
++				else
++					scancode = 0x01008000; /* KEY_UP */
+ 			} else {
+ 				buf[2] = 0;
+ 				buf[3] = (rel_x > 0) ? 0x7F : 0x80;
+-				ictx->kc = (rel_x > 0) ? KEY_RIGHT : KEY_LEFT;
++				if (rel_x > 0)
++					scancode = 0x0100007f; /* KEY_RIGHT */
++				else
++					scancode = 0x01000080; /* KEY_LEFT */
+ 			}
+ 		}
+ 
+@@ -1365,34 +1379,56 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf)
+ 			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;
+-			memcpy(&temp_key, buf, sizeof(temp_key));
+-			remote_key = (u32) (le64_to_cpu(temp_key) & 0xffffffff);
+-			ictx->kc = imon_remote_key_lookup(ictx, remote_key);
++			scancode = be32_to_cpu(*((u32 *)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;
+-				ictx->kc = (rel_y > 0) ? KEY_DOWN : KEY_UP;
++				if (rel_y > 0)
++					scancode = 0x01007f00; /* KEY_DOWN */
++				else
++					scancode = 0x01008000; /* KEY_UP */
+ 			} else {
+ 				buf[2] = 0;
+ 				buf[3] = (rel_x > 0) ? 0x7F : 0x80;
+-				ictx->kc = (rel_x > 0) ? KEY_RIGHT : KEY_LEFT;
++				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;
+-	int rep_delay = ictx->idev->rep[REP_DELAY];
+-	int rep_period = ictx->idev->rep[REP_PERIOD];
++	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)
+@@ -1408,22 +1444,10 @@ static int imon_parse_press_type(struct imon_context *ictx,
+ 		 buf[2] == 0x81 && buf[3] == 0xb7)
+ 		ictx->kc = ictx->last_keycode;
+ 
+-	/* mce-specific button handling */
++	/* mce-specific button handling, no keyup events */
+ 	else if (ktype == IMON_KEY_MCE) {
+-		/* initial press */
+-		if (ictx->kc != ictx->last_keycode
+-		    || buf[2] != ictx->mce_toggle_bit) {
+-			ictx->last_keycode = ictx->kc;
+-			ictx->mce_toggle_bit = buf[2];
+-			press_type = 1;
+-			mod_timer(&ictx->itimer,
+-				  jiffies + msecs_to_jiffies(rep_delay));
+-		/* repeat */
+-		} else {
+-			press_type = 2;
+-			mod_timer(&ictx->itimer,
+-				  jiffies + msecs_to_jiffies(rep_period));
+-		}
++		ictx->rc_toggle = buf[2];
++		press_type = 1;
+ 
+ 	/* incoherent or irrelevant data */
+ 	} else if (ictx->kc == KEY_RESERVED)
+@@ -1437,6 +1461,8 @@ static int imon_parse_press_type(struct imon_context *ictx,
+ 	else
+ 		press_type = 1;
+ 
++	spin_unlock_irqrestore(&ictx->kc_lock, flags);
++
+ 	return press_type;
+ }
+ 
+@@ -1449,41 +1475,45 @@ static void imon_incoming_packet(struct imon_context *ictx,
+ 	int len = urb->actual_length;
+ 	unsigned char *buf = urb->transfer_buffer;
+ 	struct device *dev = ictx->dev;
++	unsigned long flags;
+ 	u32 kc;
+ 	bool norelease = false;
+ 	int i;
+-	u64 temp_key;
+-	u64 panel_key = 0;
+-	u32 remote_key = 0;
++	u64 scancode;
+ 	struct input_dev *idev = NULL;
++	struct ir_input_dev *irdev = NULL;
+ 	int press_type = 0;
+ 	int msec;
+ 	struct timeval t;
+ 	static struct timeval prev_time = { 0, 0 };
+-	u8 ktype = IMON_KEY_IMON;
++	u8 ktype;
+ 
+ 	idev = ictx->idev;
++	irdev = input_get_drvdata(idev);
+ 
+ 	/* 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 */
+-	memcpy(&temp_key, buf, sizeof(temp_key));
+ 	if (len == 8 && buf[7] == 0xee) {
++		scancode = be64_to_cpu(*((u64 *)buf));
+ 		ktype = IMON_KEY_PANEL;
+-		panel_key = le64_to_cpu(temp_key);
+-		kc = imon_panel_key_lookup(panel_key);
++		kc = imon_panel_key_lookup(scancode);
+ 	} else {
+-		remote_key = (u32) (le64_to_cpu(temp_key) & 0xffffffff);
++		scancode = be32_to_cpu(*((u32 *)buf));
+ 		if (ictx->ir_type == IR_TYPE_RC6) {
++			ktype = IMON_KEY_IMON;
+ 			if (buf[0] == 0x80)
+ 				ktype = IMON_KEY_MCE;
+-			kc = imon_mce_key_lookup(ictx, remote_key);
+-		} else
+-			kc = imon_remote_key_lookup(ictx, remote_key);
++			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;
+@@ -1491,6 +1521,7 @@ static void imon_incoming_packet(struct imon_context *ictx,
+ 			ictx->pad_mouse = ~(ictx->pad_mouse) & 0x1;
+ 			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 = 0;
+@@ -1499,11 +1530,13 @@ static void imon_incoming_packet(struct imon_context *ictx,
+ 	}
+ 
+ 	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) {
+@@ -1531,36 +1564,56 @@ static void imon_incoming_packet(struct imon_context *ictx,
+ 	if (press_type < 0)
+ 		goto not_input_data;
+ 
++	spin_lock_irqsave(&ictx->kc_lock, flags);
+ 	if (ictx->kc == KEY_UNKNOWN)
+ 		goto unknown_key;
++	spin_unlock_irqrestore(&ictx->kc_lock, flags);
++
++	if (ktype != IMON_KEY_PANEL) {
++		if (press_type == 0)
++			ir_keyup(irdev);
++		else {
++			ir_keydown(ictx->rdev, 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);
+ 
+-	/* KEY_MUTE repeats from MCE and knob need to be suppressed */
+-	if ((ictx->kc == KEY_MUTE && ictx->kc == ictx->last_keycode)
+-	    && (buf[7] == 0xee || ktype == IMON_KEY_MCE)) {
++	/* KEY_MUTE repeats from knob need to be suppressed */
++	if (ictx->kc == KEY_MUTE && ictx->kc == ictx->last_keycode) {
+ 		do_gettimeofday(&t);
+ 		msec = tv2int(&t, &prev_time);
+ 		prev_time = t;
+-		if (msec < idev->rep[REP_DELAY])
++		if (msec < idev->rep[REP_DELAY]) {
++			spin_unlock_irqrestore(&ictx->kc_lock, flags);
+ 			return;
++		}
+ 	}
++	kc = ictx->kc;
++
++	spin_unlock_irqrestore(&ictx->kc_lock, flags);
+ 
+-	input_report_key(idev, ictx->kc, press_type);
++	input_report_key(idev, kc, press_type);
+ 	input_sync(idev);
+ 
+-	/* panel keys and some remote keys don't generate a release */
+-	if (panel_key || norelease) {
+-		input_report_key(idev, ictx->kc, 0);
+-		input_sync(idev);
+-	}
++	/* panel keys don't generate a release */
++	input_report_key(idev, kc, 0);
++	input_sync(idev);
+ 
+-	ictx->last_keycode = ictx->kc;
++	ictx->last_keycode = kc;
+ 
+ 	return;
+ 
+ unknown_key:
++	spin_unlock_irqrestore(&ictx->kc_lock, flags);
+ 	dev_info(dev, "%s: unknown keypress, code 0x%llx\n", __func__,
+-		 (panel_key ? be64_to_cpu(panel_key) :
+-			      be32_to_cpu(remote_key)));
++		 (long long)scancode);
+ 	return;
+ 
+ not_input_data:
+@@ -1651,31 +1704,205 @@ static void usb_rx_callback_intf1(struct urb *urb)
+ 	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 = IR_TYPE_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 0x9e:
++		dev_info(ictx->dev, "0xffdc iMON VFD, MCE IR");
++		detected_display_type = IMON_DISPLAY_TYPE_VFD;
++		allowed_protos = IR_TYPE_RC6;
++		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 = IR_TYPE_RC6;
++		break;
++	default:
++		dev_info(ictx->dev, "Unknown 0xffdc device, "
++			 "defaulting to VFD and iMON IR");
++		detected_display_type = IMON_DISPLAY_TYPE_VFD;
++		break;
++	}
++
++	printk(KERN_CONT " (id 0x%02x)\n", ffdc_cfg_byte);
++
++	ictx->display_type = detected_display_type;
++	ictx->props->allowed_protos = allowed_protos;
++	ictx->ir_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 input_dev *imon_init_rdev(struct imon_context *ictx)
++{
++	struct input_dev *rdev;
++	struct ir_dev_props *props;
++	int ret;
++	char *ir_codes = NULL;
++	const unsigned char fp_packet[] = { 0x40, 0x00, 0x00, 0x00,
++					    0x00, 0x00, 0x00, 0x88 };
++
++	rdev = input_allocate_device();
++	props = kzalloc(sizeof(*props), GFP_KERNEL);
++	if (!rdev || !props) {
++		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->name = ictx->name_rdev;
++	rdev->phys = ictx->phys_rdev;
++	usb_to_input_id(ictx->usbdev_intf0, &rdev->id);
++	rdev->dev.parent = ictx->dev;
++	rdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
++	input_set_drvdata(rdev, ictx);
++
++	props->priv = ictx;
++	props->driver_type = RC_DRIVER_SCANCODE;
++	props->allowed_protos = IR_TYPE_OTHER | IR_TYPE_RC6; /* iMON PAD or MCE */
++	props->change_protocol = imon_ir_change_protocol;
++	ictx->props = props;
++
++	/* 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);
++
++	imon_set_display_type(ictx);
++
++	if (ictx->ir_type == IR_TYPE_RC6)
++		ir_codes = RC_MAP_IMON_MCE;
++	else
++		ir_codes = RC_MAP_IMON_PAD;
++
++	ret = ir_input_register(rdev, ir_codes, props, MOD_NAME);
++	if (ret < 0) {
++		dev_err(ictx->dev, "remote input dev register failed\n");
++		goto out;
++	}
++
++	return rdev;
++
++out:
++	kfree(props);
++	input_free_device(rdev);
++	return NULL;
++}
++
+ static struct input_dev *imon_init_idev(struct imon_context *ictx)
+ {
+ 	struct input_dev *idev;
+-	struct ir_dev_props *props;
+ 	int ret, i;
+ 
+ 	idev = input_allocate_device();
+ 	if (!idev) {
+-		dev_err(ictx->dev, "remote input dev allocation failed\n");
+-		goto idev_alloc_failed;
+-	}
+-
+-	props = kzalloc(sizeof(struct ir_dev_props), GFP_KERNEL);
+-	if (!props) {
+-		dev_err(ictx->dev, "remote ir dev props allocation failed\n");
+-		goto props_alloc_failed;
++		dev_err(ictx->dev, "input dev allocation failed\n");
++		goto out;
+ 	}
+ 
+ 	snprintf(ictx->name_idev, sizeof(ictx->name_idev),
+-		 "iMON Remote (%04x:%04x)", ictx->vendor, ictx->product);
++		 "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, "/input0", 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);
+@@ -1691,30 +1918,20 @@ static struct input_dev *imon_init_idev(struct imon_context *ictx)
+ 		__set_bit(kc, idev->keybit);
+ 	}
+ 
+-	props->priv = ictx;
+-	props->driver_type = RC_DRIVER_SCANCODE;
+-	/* IR_TYPE_OTHER maps to iMON PAD remote, IR_TYPE_RC6 to MCE remote */
+-	props->allowed_protos = IR_TYPE_OTHER | IR_TYPE_RC6;
+-	props->change_protocol = imon_ir_change_protocol;
+-	ictx->props = props;
+-
+ 	usb_to_input_id(ictx->usbdev_intf0, &idev->id);
+ 	idev->dev.parent = ictx->dev;
++	input_set_drvdata(idev, ictx);
+ 
+-	ret = ir_input_register(idev, RC_MAP_IMON_PAD, props, MOD_NAME);
++	ret = input_register_device(idev);
+ 	if (ret < 0) {
+-		dev_err(ictx->dev, "remote input dev register failed\n");
+-		goto idev_register_failed;
++		dev_err(ictx->dev, "input dev register failed\n");
++		goto out;
+ 	}
+ 
+ 	return idev;
+ 
+-idev_register_failed:
+-	kfree(props);
+-props_alloc_failed:
++out:
+ 	input_free_device(idev);
+-idev_alloc_failed:
+-
+ 	return NULL;
+ }
+ 
+@@ -1736,7 +1953,7 @@ static struct input_dev *imon_init_touch(struct imon_context *ictx)
+ 
+ 	usb_make_path(ictx->usbdev_intf1, ictx->phys_touch,
+ 		      sizeof(ictx->phys_touch));
+-	strlcat(ictx->phys_touch, "/input1", sizeof(ictx->phys_touch));
++	strlcat(ictx->phys_touch, "/input2", sizeof(ictx->phys_touch));
+ 	touch->phys = ictx->phys_touch;
+ 
+ 	touch->evbit[0] =
+@@ -1886,6 +2103,7 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf)
+ 	}
+ 
+ 	mutex_init(&ictx->lock);
++	spin_lock_init(&ictx->kc_lock);
+ 
+ 	mutex_lock(&ictx->lock);
+ 
+@@ -1911,6 +2129,12 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf)
+ 		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;
++	}
++
+ 	usb_fill_int_urb(ictx->rx_urb_intf0, ictx->usbdev_intf0,
+ 		usb_rcvintpipe(ictx->usbdev_intf0,
+ 			ictx->rx_endpoint_intf0->bEndpointAddress),
+@@ -1928,7 +2152,9 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf)
+ 	return ictx;
+ 
+ urb_submit_failed:
+-	ir_input_unregister(ictx->idev);
++	ir_input_unregister(ictx->rdev);
++rdev_setup_failed:
++	input_unregister_device(ictx->idev);
+ idev_setup_failed:
+ find_endpoint_failed:
+ 	mutex_unlock(&ictx->lock);
+@@ -2010,116 +2236,6 @@ rx_urb_alloc_failed:
+ 	return NULL;
+ }
+ 
+-/*
+- * 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 = IR_TYPE_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 LCD, MCE IR */
+-	case 0x9e:
+-	case 0x9f:
+-		dev_info(ictx->dev, "0xffdc iMON LCD, MCE IR");
+-		detected_display_type = IMON_DISPLAY_TYPE_LCD;
+-		allowed_protos = IR_TYPE_RC6;
+-		break;
+-	default:
+-		dev_info(ictx->dev, "Unknown 0xffdc device, "
+-			 "defaulting to VFD and iMON IR");
+-		detected_display_type = IMON_DISPLAY_TYPE_VFD;
+-		break;
+-	}
+-
+-	printk(KERN_CONT " (id 0x%02x)\n", ffdc_cfg_byte);
+-
+-	ictx->display_type = detected_display_type;
+-	ictx->props->allowed_protos = allowed_protos;
+-	ictx->ir_type = allowed_protos;
+-}
+-
+-static void imon_set_display_type(struct imon_context *ictx,
+-				  struct usb_interface *intf)
+-{
+-	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 void imon_init_display(struct imon_context *ictx,
+ 			      struct usb_interface *intf)
+ {
+@@ -2160,8 +2276,6 @@ static int __devinit imon_probe(struct usb_interface *interface,
+ 	struct imon_context *ictx = NULL;
+ 	struct imon_context *first_if_ctx = NULL;
+ 	u16 vendor, product;
+-	const unsigned char fp_packet[] = { 0x40, 0x00, 0x00, 0x00,
+-					    0x00, 0x00, 0x00, 0x88 };
+ 
+ 	code_length = BUF_CHUNK_SIZE * 8;
+ 
+@@ -2202,19 +2316,6 @@ static int __devinit imon_probe(struct usb_interface *interface,
+ 	usb_set_intfdata(interface, ictx);
+ 
+ 	if (ifnum == 0) {
+-		/* 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(dev, "failed to enable panel buttons "
+-				 "and/or knobs\n");
+-
+-		if (product == 0xffdc)
+-			imon_get_ffdc_type(ictx);
+-
+-		imon_set_display_type(ictx, interface);
+-
+ 		if (product == 0xffdc && ictx->rf_device) {
+ 			sysfs_err = sysfs_create_group(&interface->dev.kobj,
+ 						       &imon_rf_attribute_group);
+@@ -2289,7 +2390,8 @@ static void __devexit imon_disconnect(struct usb_interface *interface)
+ 	if (ifnum == 0) {
+ 		ictx->dev_present_intf0 = false;
+ 		usb_kill_urb(ictx->rx_urb_intf0);
+-		ir_input_unregister(ictx->idev);
++		input_unregister_device(ictx->idev);
++		ir_input_unregister(ictx->rdev);
+ 		if (ictx->display_supported) {
+ 			if (ictx->display_type == IMON_DISPLAY_TYPE_LCD)
+ 				usb_deregister_dev(interface, &imon_lcd_class);
+@@ -2309,11 +2411,8 @@ static void __devexit imon_disconnect(struct usb_interface *interface)
+ 		mutex_unlock(&ictx->lock);
+ 		if (!ictx->display_isopen)
+ 			free_imon_context(ictx);
+-	} else {
+-		if (ictx->ir_type == IR_TYPE_RC6)
+-			del_timer_sync(&ictx->itimer);
++	} else
+ 		mutex_unlock(&ictx->lock);
+-	}
+ 
+ 	mutex_unlock(&driver_lock);
+ 
+diff --git a/drivers/media/IR/ir-keytable.c b/drivers/media/IR/ir-keytable.c
+index 7961d59..59510cd 100644
+--- a/drivers/media/IR/ir-keytable.c
++++ b/drivers/media/IR/ir-keytable.c
+@@ -285,7 +285,7 @@ EXPORT_SYMBOL_GPL(ir_g_keycode_from_table);
+  * This routine is used to signal that a key has been released on the
+  * remote control. It reports a keyup input event via input_report_key().
+  */
+-static void ir_keyup(struct ir_input_dev *ir)
++void ir_keyup(struct ir_input_dev *ir)
+ {
+ 	if (!ir->keypressed)
+ 		return;
+@@ -295,6 +295,7 @@ static void ir_keyup(struct ir_input_dev *ir)
+ 	input_sync(ir->input_dev);
+ 	ir->keypressed = false;
+ }
++EXPORT_SYMBOL_GPL(ir_keyup);
+ 
+ /**
+  * ir_timer_keyup() - generates a keyup event after a timeout
+diff --git a/drivers/media/IR/nuvoton-cir.c b/drivers/media/IR/nuvoton-cir.c
+new file mode 100644
+index 0000000..fdb280e
+--- /dev/null
++++ b/drivers/media/IR/nuvoton-cir.c
+@@ -0,0 +1,1237 @@
++/*
++ * Driver for Nuvoton Technology Corporation w83667hg/w83677hg-i CIR
++ *
++ * Copyright (C) 2010 Jarod Wilson <jarod at 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/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 <linux/input.h>
++#include <media/ir-core.h>
++#include <linux/pci_ids.h>
++
++#include "nuvoton-cir.h"
++
++static char *chip_id = "w836x7hg";
++
++/* 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);
++
++	printk("%s: Dump CIR logical device registers:\n", NVT_DRIVER_NAME);
++	printk(" * CR CIR ACTIVE :   0x%x\n",
++	       nvt_cr_read(nvt, CR_LOGICAL_DEV_EN));
++	printk(" * 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));
++	printk(" * CR CIR IRQ NUM:   0x%x\n",
++	       nvt_cr_read(nvt, CR_CIR_IRQ_RSRC));
++
++	nvt_efm_disable(nvt);
++
++	printk("%s: Dump CIR registers:\n", NVT_DRIVER_NAME);
++	printk(" * IRCON:     0x%x\n", nvt_cir_reg_read(nvt, CIR_IRCON));
++	printk(" * IRSTS:     0x%x\n", nvt_cir_reg_read(nvt, CIR_IRSTS));
++	printk(" * IREN:      0x%x\n", nvt_cir_reg_read(nvt, CIR_IREN));
++	printk(" * RXFCONT:   0x%x\n", nvt_cir_reg_read(nvt, CIR_RXFCONT));
++	printk(" * CP:        0x%x\n", nvt_cir_reg_read(nvt, CIR_CP));
++	printk(" * CC:        0x%x\n", nvt_cir_reg_read(nvt, CIR_CC));
++	printk(" * SLCH:      0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCH));
++	printk(" * SLCL:      0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCL));
++	printk(" * FIFOCON:   0x%x\n", nvt_cir_reg_read(nvt, CIR_FIFOCON));
++	printk(" * IRFIFOSTS: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFIFOSTS));
++	printk(" * SRXFIFO:   0x%x\n", nvt_cir_reg_read(nvt, CIR_SRXFIFO));
++	printk(" * TXFCONT:   0x%x\n", nvt_cir_reg_read(nvt, CIR_TXFCONT));
++	printk(" * STXFIFO:   0x%x\n", nvt_cir_reg_read(nvt, CIR_STXFIFO));
++	printk(" * FCCH:      0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCH));
++	printk(" * FCCL:      0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCL));
++	printk(" * 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);
++
++	printk("%s: Dump CIR WAKE logical device registers:\n",
++	       NVT_DRIVER_NAME);
++	printk(" * CR CIR WAKE ACTIVE :   0x%x\n",
++	       nvt_cr_read(nvt, CR_LOGICAL_DEV_EN));
++	printk(" * 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));
++	printk(" * CR CIR WAKE IRQ NUM:   0x%x\n",
++	       nvt_cr_read(nvt, CR_CIR_IRQ_RSRC));
++
++	nvt_efm_disable(nvt);
++
++	printk("%s: Dump CIR WAKE registers\n", NVT_DRIVER_NAME);
++	printk(" * IRCON:          0x%x\n",
++	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON));
++	printk(" * IRSTS:          0x%x\n",
++	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRSTS));
++	printk(" * IREN:           0x%x\n",
++	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_IREN));
++	printk(" * FIFO CMP DEEP:  0x%x\n",
++	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_DEEP));
++	printk(" * FIFO CMP TOL:   0x%x\n",
++	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_TOL));
++	printk(" * FIFO COUNT:     0x%x\n",
++	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT));
++	printk(" * SLCH:           0x%x\n",
++	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCH));
++	printk(" * SLCL:           0x%x\n",
++	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCL));
++	printk(" * FIFOCON:        0x%x\n",
++	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFOCON));
++	printk(" * SRXFSTS:        0x%x\n",
++	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_SRXFSTS));
++	printk(" * SAMPLE RX FIFO: 0x%x\n",
++	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_SAMPLE_RX_FIFO));
++	printk(" * WR FIFO DATA:   0x%x\n",
++	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_WR_FIFO_DATA));
++	printk(" * RD FIFO ONLY:   0x%x\n",
++	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY));
++	printk(" * RD FIFO ONLY IDX: 0x%x\n",
++	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX));
++	printk(" * FIFO IGNORE:    0x%x\n",
++	       nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_IGNORE));
++	printk(" * 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);
++	printk("%s: Dump CIR WAKE FIFO (len %d)\n", NVT_DRIVER_NAME, fifo_len);
++	printk("* Contents = ");
++	for (i = 0; i < fifo_len; i++)
++		printk("%02x ",
++		       nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY));
++	printk("\n");
++}
++
++/* detect hardware features */
++static int nvt_hw_detect(struct nvt_dev *nvt)
++{
++	unsigned long flags;
++	u8 chip_major, chip_minor;
++	int ret = 0;
++
++	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);
++	nvt_dbg("%s: chip id: 0x%02x 0x%02x", chip_id, chip_major, chip_minor);
++
++	if (chip_major != CHIP_ID_HIGH &&
++	    (chip_minor != CHIP_ID_LOW || chip_minor != CHIP_ID_LOW2))
++		ret = -ENODEV;
++
++	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 ret;
++}
++
++static void nvt_cir_ldev_init(struct nvt_dev *nvt)
++{
++	u8 val;
++
++	/* output pin selection (Pin95=CIRRX, Pin96=CIRTX1, WB enabled */
++	val = nvt_cr_read(nvt, CR_OUTPUT_PIN_SEL);
++	val &= OUTPUT_PIN_SEL_MASK;
++	val |= (OUTPUT_ENABLE_CIR | OUTPUT_ENABLE_CIRWB);
++	nvt_cr_write(nvt, val, CR_OUTPUT_PIN_SEL);
++
++	/* 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 cir interrupt of mouse/keyboard IRQ event */
++	nvt_set_reg_bit(nvt, CIR_INTR_MOUSE_IRQ_BIT, CR_ACPI_IRQ_EVENTS);
++
++	/* 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 key comparison (default 67) */
++	nvt_cir_wake_reg_write(nvt, CIR_WAKE_FIFO_LEN, 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, CIR_INTR_MOUSE_IRQ_BIT, CR_ACPI_IRQ_EVENTS);
++	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);
++}
++
++/* 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 = (count * 1000000) / 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;
++}
++
++/*
++ * 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(void *data, u32 carrier)
++{
++	struct nvt_dev *nvt = data;
++	u16 val;
++
++	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(void *priv, int *txbuf, u32 n)
++{
++	struct nvt_dev *nvt = priv;
++	unsigned long flags;
++	size_t cur_count;
++	unsigned int i;
++	u8 iren;
++	int ret;
++
++	spin_lock_irqsave(&nvt->tx.lock, flags);
++
++	if (n >= TX_BUF_LEN) {
++		nvt->tx.buf_count = cur_count = TX_BUF_LEN;
++		ret = TX_BUF_LEN;
++	} else {
++		nvt->tx.buf_count = cur_count = n;
++		ret = n;
++	}
++
++	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("%s (len %d): ", __func__, nvt->pkts);
++	for (i = 0; (i < nvt->pkts) && (i < RX_BUF_LEN); i++)
++		printk("0x%02x ", nvt->buf[i]);
++	printk("\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)
++{
++	struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
++	unsigned int count;
++	u32 carrier;
++	u8 sample;
++	int i;
++
++	nvt_dbg_verbose("%s firing", __func__);
++
++	if (debug)
++		nvt_dump_rx_buf(nvt);
++
++	if (nvt->carrier_detect_enabled)
++		carrier = nvt_rx_carrier_detect(nvt);
++
++	count = nvt->pkts;
++	nvt_dbg_verbose("Processing buffer of len %d", count);
++
++	for (i = 0; i < count; i++) {
++		nvt->pkts--;
++		sample = nvt->buf[i];
++
++		rawir.pulse = ((sample & BUF_PULSE_BIT) != 0);
++		rawir.duration = (sample & BUF_LEN_MASK)
++					* SAMPLE_PERIOD * 1000;
++
++		if ((sample & BUF_LEN_MASK) == BUF_LEN_MASK) {
++			if (nvt->rawir.pulse == rawir.pulse)
++				nvt->rawir.duration += rawir.duration;
++			else {
++				nvt->rawir.duration = rawir.duration;
++				nvt->rawir.pulse = rawir.pulse;
++			}
++			continue;
++		}
++
++		rawir.duration += nvt->rawir.duration;
++		nvt->rawir.duration = 0;
++		nvt->rawir.pulse = rawir.pulse;
++
++		if (sample == BUF_PULSE_BIT)
++			rawir.pulse = false;
++
++		if (rawir.duration) {
++			nvt_dbg("Storing %s with duration %d",
++				rawir.pulse ? "pulse" : "space",
++				rawir.duration);
++
++			ir_raw_event_store(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 || ((sample != BUF_LEN_MASK) &&
++		    (sample & BUF_REPEAT_MASK) == BUF_REPEAT_BYTE))
++			ir_raw_event_handle(nvt->rdev);
++	}
++
++	if (nvt->pkts) {
++		nvt_dbg("Odd, pkts should be 0 now... (its %u)", nvt->pkts);
++		nvt->pkts = 0;
++	}
++
++	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(void *data)
++{
++	struct nvt_dev *nvt = (struct nvt_dev *)data;
++	unsigned long flags;
++
++	spin_lock_irqsave(&nvt->nvt_lock, flags);
++	nvt->in_use = true;
++	nvt_enable_cir(nvt);
++	spin_unlock_irqrestore(&nvt->nvt_lock, flags);
++
++	return 0;
++}
++
++static void nvt_close(void *data)
++{
++	struct nvt_dev *nvt = (struct nvt_dev *)data;
++	unsigned long flags;
++
++	spin_lock_irqsave(&nvt->nvt_lock, flags);
++	nvt->in_use = false;
++	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 = NULL;
++	struct input_dev *rdev = NULL;
++	struct ir_dev_props *props = NULL;
++	int ret = -ENOMEM;
++
++	nvt = kzalloc(sizeof(struct nvt_dev), GFP_KERNEL);
++	if (!nvt)
++		return ret;
++
++	props = kzalloc(sizeof(struct ir_dev_props), GFP_KERNEL);
++	if (!props)
++		goto failure;
++
++	/* input device for IR remote (and tx) */
++	rdev = input_allocate_device();
++	if (!rdev)
++		goto failure;
++
++	ret = -ENODEV;
++	/* 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 failure;
++	}
++
++	if (!pnp_irq_valid(pdev, 0)) {
++		dev_err(&pdev->dev, "PNP IRQ not valid!\n");
++		goto failure;
++	}
++
++	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 failure;
++	}
++
++	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);
++
++	ret = -EBUSY;
++	/* now claim resources */
++	if (!request_region(nvt->cir_addr,
++			    CIR_IOREG_LENGTH, NVT_DRIVER_NAME))
++		goto failure;
++
++	if (request_irq(nvt->cir_irq, nvt_cir_isr, IRQF_SHARED,
++			NVT_DRIVER_NAME, (void *)nvt))
++		goto failure;
++
++	if (!request_region(nvt->cir_wake_addr,
++			    CIR_IOREG_LENGTH, NVT_DRIVER_NAME))
++		goto failure;
++
++	if (request_irq(nvt->cir_wake_irq, nvt_cir_wake_isr, IRQF_SHARED,
++			NVT_DRIVER_NAME, (void *)nvt))
++		goto failure;
++
++	pnp_set_drvdata(pdev, nvt);
++	nvt->pdev = pdev;
++
++	init_waitqueue_head(&nvt->tx.queue);
++
++	ret = nvt_hw_detect(nvt);
++	if (ret)
++		goto failure;
++
++	/* 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 ir-core props */
++	props->priv = nvt;
++	props->driver_type = RC_DRIVER_IR_RAW;
++	props->allowed_protos = IR_TYPE_ALL;
++	props->open = nvt_open;
++	props->close = nvt_close;
++#if 0
++	props->min_timeout = XYZ;
++	props->max_timeout = XYZ;
++	props->timeout = XYZ;
++	/* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */
++	props->rx_resolution = XYZ;
++
++	/* tx bits */
++	props->tx_resolution = XYZ;
++#endif
++	props->tx_ir = nvt_tx_ir;
++	props->s_tx_carrier = nvt_set_tx_carrier;
++
++	rdev->name = "Nuvoton w836x7hg Infrared Remote Transceiver";
++	rdev->id.bustype = BUS_HOST;
++	rdev->id.vendor = PCI_VENDOR_ID_WINBOND2;
++	rdev->id.product = nvt->chip_major;
++	rdev->id.version = nvt->chip_minor;
++
++	nvt->props = props;
++	nvt->rdev = rdev;
++
++	device_set_wakeup_capable(&pdev->dev, 1);
++	device_set_wakeup_enable(&pdev->dev, 1);
++
++	ret = ir_input_register(rdev, RC_MAP_RC6_MCE, props, NVT_DRIVER_NAME);
++	if (ret)
++		goto failure;
++
++	nvt_pr(KERN_NOTICE, "driver has been successfully loaded\n");
++	if (debug) {
++		cir_dump_regs(nvt);
++		cir_wake_dump_regs(nvt);
++	}
++
++	return 0;
++
++failure:
++	if (nvt->cir_irq)
++		free_irq(nvt->cir_irq, nvt);
++	if (nvt->cir_addr)
++		release_region(nvt->cir_addr, CIR_IOREG_LENGTH);
++
++	if (nvt->cir_wake_irq)
++		free_irq(nvt->cir_wake_irq, nvt);
++	if (nvt->cir_wake_addr)
++		release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH);
++
++	input_free_device(rdev);
++	kfree(props);
++	kfree(nvt);
++
++	return ret;
++}
++
++static void __devexit 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);
++
++	ir_input_unregister(nvt->rdev);
++
++	kfree(nvt->props);
++	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)
++{
++	int ret = 0;
++	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 ret;
++}
++
++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		= __devexit_p(nvt_remove),
++	.suspend	= nvt_suspend,
++	.resume		= nvt_resume,
++	.shutdown	= nvt_shutdown,
++};
++
++int nvt_init(void)
++{
++	return pnp_register_driver(&nvt_driver);
++}
++
++void nvt_exit(void)
++{
++	pnp_unregister_driver(&nvt_driver);
++}
++
++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 at redhat.com>");
++MODULE_LICENSE("GPL");
++
++module_init(nvt_init);
++module_exit(nvt_exit);
+diff --git a/drivers/media/IR/nuvoton-cir.h b/drivers/media/IR/nuvoton-cir.h
+new file mode 100644
+index 0000000..12bfe89
+--- /dev/null
++++ b/drivers/media/IR/nuvoton-cir.h
+@@ -0,0 +1,408 @@
++/*
++ * Driver for Nuvoton Technology Corporation w83667hg/w83677hg-i CIR
++ *
++ * Copyright (C) 2010 Jarod Wilson <jarod at 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 <asm/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 input_dev *rdev;
++	struct ir_dev_props *props;
++	struct ir_raw_event rawir;
++
++	spinlock_t nvt_lock;
++	bool in_use;
++
++	/* 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 */
++	u8 cr_efir;
++	u8 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;
++	bool carrier_detect_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
++
++/* CIR Wake FIFO buffer is 67 bytes long */
++#define CIR_WAKE_FIFO_LEN		67
++/* 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		0xb4
++#define CHIP_ID_LOW		0x72
++#define CHIP_ID_LOW2		0x73
++
++/* 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_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 CIR_INTR_MOUSE_IRQ_BIT	0x80
++#define PME_INTR_CIR_PASS_BIT	0x08
++
++#define OUTPUT_PIN_SEL_MASK	0xbc
++#define OUTPUT_ENABLE_CIR	0x01 /* Pin95=CIRRX, Pin96=CIRTX1 */
++#define OUTPUT_ENABLE_CIRWB	0x40 /* 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/include/media/ir-core.h b/include/media/ir-core.h
+index eb7fddf..4dd43d4 100644
+--- a/include/media/ir-core.h
++++ b/include/media/ir-core.h
+@@ -157,6 +157,7 @@ void ir_input_unregister(struct input_dev *input_dev);
+ 
+ void ir_repeat(struct input_dev *dev);
+ void ir_keydown(struct input_dev *dev, int scancode, u8 toggle);
++void ir_keyup(struct ir_input_dev *ir);
+ u32 ir_g_keycode_from_table(struct input_dev *input_dev, u32 scancode);
+ 
+ /* From ir-raw-event.c */


More information about the scm-commits mailing list