[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