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 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 - 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 ++ * Copyright(C) 2010 Jarod Wilson + * Portions based on the original lirc_imon driver, + * Copyright(C) 2004 Venky Raju(dev@venky.ws) + * +@@ -44,7 +44,7 @@ + #define MOD_AUTHOR "Jarod Wilson " + #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 ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 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 (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 "); ++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 ++ * 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 ++#include ++ ++/* 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 */