diff options
Diffstat (limited to 'drivers/media/rc')
-rw-r--r-- | drivers/media/rc/ati_remote.c | 47 | ||||
-rw-r--r-- | drivers/media/rc/igorplugusb.c | 17 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/rc-avermedia-rm-ks.c | 56 | ||||
-rw-r--r-- | drivers/media/rc/lirc_dev.c | 7 | ||||
-rw-r--r-- | drivers/media/rc/mceusb.c | 5 | ||||
-rw-r--r-- | drivers/media/rc/nuvoton-cir.c | 358 | ||||
-rw-r--r-- | drivers/media/rc/nuvoton-cir.h | 15 | ||||
-rw-r--r-- | drivers/media/rc/rc-core-priv.h | 6 | ||||
-rw-r--r-- | drivers/media/rc/rc-ir-raw.c | 23 | ||||
-rw-r--r-- | drivers/media/rc/rc-main.c | 48 | ||||
-rw-r--r-- | drivers/media/rc/sunxi-cir.c | 1 |
11 files changed, 368 insertions, 215 deletions
diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index a35631891..3f61d77d4 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -443,6 +443,21 @@ static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, return retval; } +struct accel_times { + const char value; + unsigned int msecs; +}; + +static const struct accel_times accel[] = { + { 1, 125 }, + { 2, 250 }, + { 4, 500 }, + { 6, 1000 }, + { 9, 1500 }, + { 13, 2000 }, + { 20, 0 }, +}; + /* * ati_remote_compute_accel * @@ -454,30 +469,22 @@ static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, */ static int ati_remote_compute_accel(struct ati_remote *ati_remote) { - static const char accel[] = { 1, 2, 4, 6, 9, 13, 20 }; - unsigned long now = jiffies; - int acc; + unsigned long now = jiffies, reset_time; + int i; + + reset_time = msecs_to_jiffies(250); - if (time_after(now, ati_remote->old_jiffies + msecs_to_jiffies(250))) { - acc = 1; + if (time_after(now, ati_remote->old_jiffies + reset_time)) { ati_remote->acc_jiffies = now; + return 1; } - else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(125))) - acc = accel[0]; - else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(250))) - acc = accel[1]; - else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(500))) - acc = accel[2]; - else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1000))) - acc = accel[3]; - else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1500))) - acc = accel[4]; - else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(2000))) - acc = accel[5]; - else - acc = accel[6]; + for (i = 0; i < ARRAY_SIZE(accel) - 1; i++) { + unsigned long timeout = msecs_to_jiffies(accel[i].msecs); - return acc; + if (time_before(now, ati_remote->acc_jiffies + timeout)) + return accel[i].value; + } + return accel[i].value; } /* diff --git a/drivers/media/rc/igorplugusb.c b/drivers/media/rc/igorplugusb.c index b36e51576..e0c531fa0 100644 --- a/drivers/media/rc/igorplugusb.c +++ b/drivers/media/rc/igorplugusb.c @@ -152,7 +152,7 @@ static int igorplugusb_probe(struct usb_interface *intf, struct usb_endpoint_descriptor *ep; struct igorplugusb *ir; struct rc_dev *rc; - int ret; + int ret = -ENOMEM; udev = interface_to_usbdev(intf); idesc = intf->cur_altsetting; @@ -182,7 +182,7 @@ static int igorplugusb_probe(struct usb_interface *intf, ir->urb = usb_alloc_urb(0, GFP_KERNEL); if (!ir->urb) - return -ENOMEM; + goto fail; usb_fill_control_urb(ir->urb, udev, usb_rcvctrlpipe(udev, 0), (uint8_t *)&ir->request, @@ -191,6 +191,9 @@ static int igorplugusb_probe(struct usb_interface *intf, usb_make_path(udev, ir->phys, sizeof(ir->phys)); rc = rc_allocate_device(); + if (!rc) + goto fail; + rc->input_name = DRIVER_DESC; rc->input_phys = ir->phys; usb_to_input_id(udev, &rc->input_id); @@ -214,9 +217,7 @@ static int igorplugusb_probe(struct usb_interface *intf, ret = rc_register_device(rc); if (ret) { dev_err(&intf->dev, "failed to register rc device: %d", ret); - rc_free_device(rc); - usb_free_urb(ir->urb); - return ret; + goto fail; } usb_set_intfdata(intf, ir); @@ -224,6 +225,12 @@ static int igorplugusb_probe(struct usb_interface *intf, igorplugusb_cmd(ir, SET_INFRABUFFER_EMPTY); return 0; +fail: + rc_free_device(ir->rc); + usb_free_urb(ir->urb); + del_timer(&ir->timer); + + return ret; } static void igorplugusb_disconnect(struct usb_interface *intf) diff --git a/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c b/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c index 8344bcc59..2583400ca 100644 --- a/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c +++ b/drivers/media/rc/keymaps/rc-avermedia-rm-ks.c @@ -23,35 +23,35 @@ /* Initial keytable is from Jose Alberto Reguero <jareguero@telefonica.net> and Felipe Morales Moreno <felipe.morales.moreno@gmail.com> */ -/* FIXME: mappings are not 100% correct? */ +/* Keytable fixed by Philippe Valembois <lephilousophe@users.sourceforge.net> */ static struct rc_map_table avermedia_rm_ks[] = { - { 0x0501, KEY_POWER2 }, - { 0x0502, KEY_CHANNELUP }, - { 0x0503, KEY_CHANNELDOWN }, - { 0x0504, KEY_VOLUMEUP }, - { 0x0505, KEY_VOLUMEDOWN }, - { 0x0506, KEY_MUTE }, - { 0x0507, KEY_RIGHT }, - { 0x0508, KEY_RED }, - { 0x0509, KEY_1 }, - { 0x050a, KEY_2 }, - { 0x050b, KEY_3 }, - { 0x050c, KEY_4 }, - { 0x050d, KEY_5 }, - { 0x050e, KEY_6 }, - { 0x050f, KEY_7 }, - { 0x0510, KEY_8 }, - { 0x0511, KEY_9 }, - { 0x0512, KEY_0 }, - { 0x0513, KEY_AUDIO }, - { 0x0515, KEY_EPG }, - { 0x0516, KEY_PLAY }, - { 0x0517, KEY_RECORD }, - { 0x0518, KEY_STOP }, - { 0x051c, KEY_BACK }, - { 0x051d, KEY_FORWARD }, - { 0x054d, KEY_LEFT }, - { 0x0556, KEY_ZOOM }, + { 0x0501, KEY_POWER2 }, /* Power (RED POWER BUTTON) */ + { 0x0502, KEY_CHANNELUP }, /* Channel+ */ + { 0x0503, KEY_CHANNELDOWN }, /* Channel- */ + { 0x0504, KEY_VOLUMEUP }, /* Volume+ */ + { 0x0505, KEY_VOLUMEDOWN }, /* Volume- */ + { 0x0506, KEY_MUTE }, /* Mute */ + { 0x0507, KEY_AGAIN }, /* Recall */ + { 0x0508, KEY_VIDEO }, /* Source */ + { 0x0509, KEY_1 }, /* 1 */ + { 0x050a, KEY_2 }, /* 2 */ + { 0x050b, KEY_3 }, /* 3 */ + { 0x050c, KEY_4 }, /* 4 */ + { 0x050d, KEY_5 }, /* 5 */ + { 0x050e, KEY_6 }, /* 6 */ + { 0x050f, KEY_7 }, /* 7 */ + { 0x0510, KEY_8 }, /* 8 */ + { 0x0511, KEY_9 }, /* 9 */ + { 0x0512, KEY_0 }, /* 0 */ + { 0x0513, KEY_AUDIO }, /* Audio */ + { 0x0515, KEY_EPG }, /* EPG */ + { 0x0516, KEY_PLAYPAUSE }, /* Play/Pause */ + { 0x0517, KEY_RECORD }, /* Record */ + { 0x0518, KEY_STOP }, /* Stop */ + { 0x051c, KEY_BACK }, /* << */ + { 0x051d, KEY_FORWARD }, /* >> */ + { 0x054d, KEY_INFO }, /* Display information */ + { 0x0556, KEY_ZOOM }, /* Fullscreen */ }; static struct rc_map_list avermedia_rm_ks_map = { diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index 4de0e85af..92ae1903c 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -506,6 +506,7 @@ int lirc_dev_fop_close(struct inode *inode, struct file *file) { struct irctl *ir = irctls[iminor(inode)]; struct cdev *cdev; + int ret; if (!ir) { printk(KERN_ERR "%s: called with invalid irctl\n", __func__); @@ -516,7 +517,8 @@ int lirc_dev_fop_close(struct inode *inode, struct file *file) dev_dbg(ir->d.dev, LOGHEAD "close called\n", ir->d.name, ir->d.minor); - WARN_ON(mutex_lock_killable(&lirc_dev_lock)); + ret = mutex_lock_killable(&lirc_dev_lock); + WARN_ON(ret); rc_close(ir->d.rdev); @@ -532,7 +534,8 @@ int lirc_dev_fop_close(struct inode *inode, struct file *file) kfree(ir); } - mutex_unlock(&lirc_dev_lock); + if (!ret) + mutex_unlock(&lirc_dev_lock); return 0; } diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 2cdb740cd..35155ae50 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -587,9 +587,8 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf, if (len == 2) dev_dbg(dev, "Get hw/sw rev?"); else - dev_dbg(dev, "hw/sw rev 0x%02x 0x%02x 0x%02x 0x%02x", - data1, data2, - buf[start + 4], buf[start + 5]); + dev_dbg(dev, "hw/sw rev %*ph", + 4, &buf[start + 2]); break; case MCE_CMD_RESUME: dev_dbg(dev, "Device resume requested"); diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index 18adf580f..99b303b70 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -39,6 +39,8 @@ #include "nuvoton-cir.h" +static void nvt_clear_cir_wake_fifo(struct nvt_dev *nvt); + static const struct nvt_chip nvt_chips[] = { { "w83667hg", NVT_W83667HG }, { "NCT6775F", NVT_6775F }, @@ -80,17 +82,24 @@ static inline void nvt_clear_reg_bit(struct nvt_dev *nvt, u8 val, u8 reg) } /* enter extended function mode */ -static inline void nvt_efm_enable(struct nvt_dev *nvt) +static inline int nvt_efm_enable(struct nvt_dev *nvt) { + if (!request_muxed_region(nvt->cr_efir, 2, NVT_DRIVER_NAME)) + return -EBUSY; + /* Enabling Extended Function Mode explicitly requires writing 2x */ outb(EFER_EFM_ENABLE, nvt->cr_efir); outb(EFER_EFM_ENABLE, nvt->cr_efir); + + return 0; } /* exit extended function mode */ static inline void nvt_efm_disable(struct nvt_dev *nvt) { outb(EFER_EFM_DISABLE, nvt->cr_efir); + + release_region(nvt->cr_efir, 2); } /* @@ -100,8 +109,25 @@ static inline void nvt_efm_disable(struct nvt_dev *nvt) */ 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); + nvt_cr_write(nvt, ldev, CR_LOGICAL_DEV_SEL); +} + +/* select and enable logical device with setting EFM mode*/ +static inline void nvt_enable_logical_dev(struct nvt_dev *nvt, u8 ldev) +{ + nvt_efm_enable(nvt); + nvt_select_logical_dev(nvt, ldev); + nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN); + nvt_efm_disable(nvt); +} + +/* select and disable logical device with setting EFM mode*/ +static inline void nvt_disable_logical_dev(struct nvt_dev *nvt, u8 ldev) +{ + nvt_efm_enable(nvt); + nvt_select_logical_dev(nvt, ldev); + nvt_cr_write(nvt, LOGICAL_DEV_DISABLE, CR_LOGICAL_DEV_EN); + nvt_efm_disable(nvt); } /* write val to cir config register */ @@ -137,6 +163,120 @@ static u8 nvt_cir_wake_reg_read(struct nvt_dev *nvt, u8 offset) return val; } +/* don't override io address if one is set already */ +static void nvt_set_ioaddr(struct nvt_dev *nvt, unsigned long *ioaddr) +{ + unsigned long old_addr; + + old_addr = nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8; + old_addr |= nvt_cr_read(nvt, CR_CIR_BASE_ADDR_LO); + + if (old_addr) + *ioaddr = old_addr; + else { + nvt_cr_write(nvt, *ioaddr >> 8, CR_CIR_BASE_ADDR_HI); + nvt_cr_write(nvt, *ioaddr & 0xff, CR_CIR_BASE_ADDR_LO); + } +} + +static ssize_t wakeup_data_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rc_dev *rc_dev = to_rc_dev(dev); + struct nvt_dev *nvt = rc_dev->priv; + int fifo_len, duration; + unsigned long flags; + ssize_t buf_len = 0; + int i; + + spin_lock_irqsave(&nvt->nvt_lock, flags); + + fifo_len = nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT); + fifo_len = min(fifo_len, WAKEUP_MAX_SIZE); + + /* go to first element to be read */ + while (nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX)) + nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY); + + for (i = 0; i < fifo_len; i++) { + duration = nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY); + duration = (duration & BUF_LEN_MASK) * SAMPLE_PERIOD; + buf_len += snprintf(buf + buf_len, PAGE_SIZE - buf_len, + "%d ", duration); + } + buf_len += snprintf(buf + buf_len, PAGE_SIZE - buf_len, "\n"); + + spin_unlock_irqrestore(&nvt->nvt_lock, flags); + + return buf_len; +} + +static ssize_t wakeup_data_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct rc_dev *rc_dev = to_rc_dev(dev); + struct nvt_dev *nvt = rc_dev->priv; + unsigned long flags; + u8 tolerance, config, wake_buf[WAKEUP_MAX_SIZE]; + char **argv; + int i, count; + unsigned int val; + ssize_t ret; + + argv = argv_split(GFP_KERNEL, buf, &count); + if (!argv) + return -ENOMEM; + if (!count || count > WAKEUP_MAX_SIZE) { + ret = -EINVAL; + goto out; + } + + for (i = 0; i < count; i++) { + ret = kstrtouint(argv[i], 10, &val); + if (ret) + goto out; + val = DIV_ROUND_CLOSEST(val, SAMPLE_PERIOD); + if (!val || val > 0x7f) { + ret = -EINVAL; + goto out; + } + wake_buf[i] = val; + /* sequence must start with a pulse */ + if (i % 2 == 0) + wake_buf[i] |= BUF_PULSE_BIT; + } + + /* hardcode the tolerance to 10% */ + tolerance = DIV_ROUND_UP(count, 10); + + spin_lock_irqsave(&nvt->nvt_lock, flags); + + nvt_clear_cir_wake_fifo(nvt); + nvt_cir_wake_reg_write(nvt, count, CIR_WAKE_FIFO_CMP_DEEP); + nvt_cir_wake_reg_write(nvt, tolerance, CIR_WAKE_FIFO_CMP_TOL); + + config = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON); + + /* enable writes to wake fifo */ + nvt_cir_wake_reg_write(nvt, config | CIR_WAKE_IRCON_MODE1, + CIR_WAKE_IRCON); + + for (i = 0; i < count; i++) + nvt_cir_wake_reg_write(nvt, wake_buf[i], CIR_WAKE_WR_FIFO_DATA); + + nvt_cir_wake_reg_write(nvt, config, CIR_WAKE_IRCON); + + spin_unlock_irqrestore(&nvt->nvt_lock, flags); + + ret = len; +out: + argv_free(argv); + return ret; +} +static DEVICE_ATTR_RW(wakeup_data); + /* dump current cir register contents */ static void cir_dump_regs(struct nvt_dev *nvt) { @@ -251,7 +391,7 @@ static inline const char *nvt_find_chip(struct nvt_dev *nvt, int id) /* detect hardware features */ -static void nvt_hw_detect(struct nvt_dev *nvt) +static int nvt_hw_detect(struct nvt_dev *nvt) { const char *chip_name; int chip_id; @@ -266,10 +406,17 @@ static void nvt_hw_detect(struct nvt_dev *nvt) nvt_efm_enable(nvt); nvt->chip_major = nvt_cr_read(nvt, CR_CHIP_ID_HI); } - nvt->chip_minor = nvt_cr_read(nvt, CR_CHIP_ID_LO); + nvt_efm_disable(nvt); + chip_id = nvt->chip_major << 8 | nvt->chip_minor; + if (chip_id == NVT_INVALID) { + dev_err(&nvt->pdev->dev, + "No device found on either EFM port\n"); + return -ENODEV; + } + chip_name = nvt_find_chip(nvt, chip_id); /* warn, but still let the driver load, if we don't know this chip */ @@ -282,7 +429,7 @@ static void nvt_hw_detect(struct nvt_dev *nvt) "found %s or compatible: chip id: 0x%02x 0x%02x", chip_name, nvt->chip_major, nvt->chip_minor); - nvt_efm_disable(nvt); + return 0; } static void nvt_cir_ldev_init(struct nvt_dev *nvt) @@ -305,12 +452,10 @@ static void nvt_cir_ldev_init(struct nvt_dev *nvt) val |= psval; nvt_cr_write(nvt, val, psreg); - /* Select CIR logical device and enable */ + /* Select CIR logical device */ 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_set_ioaddr(nvt, &nvt->cir_addr); nvt_cr_write(nvt, nvt->cir_irq, CR_CIR_IRQ_RSRC); @@ -320,7 +465,7 @@ static void nvt_cir_ldev_init(struct nvt_dev *nvt) static void nvt_cir_wake_ldev_init(struct nvt_dev *nvt) { - /* Select ACPI logical device, enable it and CIR Wake */ + /* Select ACPI logical device and anable it */ nvt_select_logical_dev(nvt, LOGICAL_DEV_ACPI); nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN); @@ -330,12 +475,10 @@ static void nvt_cir_wake_ldev_init(struct nvt_dev *nvt) /* 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 */ + /* Select CIR Wake logical device */ 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_set_ioaddr(nvt, &nvt->cir_wake_addr); nvt_cr_write(nvt, nvt->cir_wake_irq, CR_CIR_IRQ_RSRC); @@ -355,11 +498,19 @@ static void nvt_clear_cir_fifo(struct nvt_dev *nvt) /* clear out the hardware's cir wake rx fifo */ static void nvt_clear_cir_wake_fifo(struct nvt_dev *nvt) { - u8 val; + u8 val, config; + + config = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON); + + /* clearing wake fifo works in learning mode only */ + nvt_cir_wake_reg_write(nvt, config & ~CIR_WAKE_IRCON_MODE0, + CIR_WAKE_IRCON); val = nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFOCON); nvt_cir_wake_reg_write(nvt, val | CIR_WAKE_FIFOCON_RXFIFOCLR, CIR_WAKE_FIFOCON); + + nvt_cir_wake_reg_write(nvt, config, CIR_WAKE_IRCON); } /* clear out the hardware's cir tx fifo */ @@ -408,6 +559,9 @@ static void nvt_cir_regs_init(struct nvt_dev *nvt) /* and finally, enable interrupts */ nvt_set_cir_iren(nvt); + + /* enable the CIR logical device */ + nvt_enable_logical_dev(nvt, LOGICAL_DEV_CIR); } static void nvt_cir_wake_regs_init(struct nvt_dev *nvt) @@ -442,10 +596,15 @@ static void nvt_cir_wake_regs_init(struct nvt_dev *nvt) /* clear any and all stray interrupts */ nvt_cir_wake_reg_write(nvt, 0xff, CIR_WAKE_IRSTS); + + /* enable the CIR WAKE logical device */ + nvt_enable_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE); } static void nvt_enable_wake(struct nvt_dev *nvt) { + unsigned long flags; + nvt_efm_enable(nvt); nvt_select_logical_dev(nvt, LOGICAL_DEV_ACPI); @@ -457,12 +616,16 @@ static void nvt_enable_wake(struct nvt_dev *nvt) nvt_efm_disable(nvt); + spin_lock_irqsave(&nvt->nvt_lock, flags); + 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); + + spin_unlock_irqrestore(&nvt->nvt_lock, flags); } #if 0 /* Currently unused */ @@ -670,7 +833,6 @@ static void nvt_handle_rx_fifo_overrun(struct nvt_dev *nvt) /* 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; @@ -689,8 +851,6 @@ static void nvt_get_rx_ir_data(struct nvt_dev *nvt) 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... */ @@ -712,8 +872,6 @@ static void nvt_get_rx_ir_data(struct nvt_dev *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) @@ -736,16 +894,13 @@ static void nvt_cir_log_irqs(u8 status, u8 iren) 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; + return tx_state == ST_TX_NONE; } /* interrupt service routine for incoming and outgoing CIR data */ @@ -757,9 +912,7 @@ static irqreturn_t nvt_cir_isr(int irq, void *data) nvt_dbg_verbose("%s firing", __func__); - nvt_efm_enable(nvt); - nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR); - nvt_efm_disable(nvt); + spin_lock_irqsave(&nvt->nvt_lock, flags); /* * Get IR Status register contents. Write 1 to ack/clear @@ -775,9 +928,14 @@ static irqreturn_t nvt_cir_isr(int irq, void *data) * 0: CIR_IRSTS_GH - Min Length Detected */ status = nvt_cir_reg_read(nvt, CIR_IRSTS); - if (!status) { + iren = nvt_cir_reg_read(nvt, CIR_IREN); + + /* IRQ may be shared with CIR WAKE, therefore check for each + * status bit whether the related interrupt source is enabled + */ + if (!(status & iren)) { + spin_unlock_irqrestore(&nvt->nvt_lock, flags); nvt_dbg_verbose("%s exiting, IRSTS 0x0", __func__); - nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS); return IRQ_NONE; } @@ -785,13 +943,6 @@ static irqreturn_t nvt_cir_isr(int irq, void *data) 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_NONE; - } - nvt_cir_log_irqs(status, iren); if (status & CIR_IRSTS_RTR) { @@ -805,16 +956,14 @@ static irqreturn_t nvt_cir_isr(int irq, void *data) 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); } + spin_unlock_irqrestore(&nvt->nvt_lock, flags); + if (status & CIR_IRSTS_TE) nvt_clear_tx_fifo(nvt); @@ -863,9 +1012,18 @@ static irqreturn_t nvt_cir_wake_isr(int irq, void *data) nvt_dbg_wake("%s firing", __func__); + spin_lock_irqsave(&nvt->nvt_lock, flags); + status = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRSTS); - if (!status) + iren = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IREN); + + /* IRQ may be shared with CIR, therefore check for each + * status bit whether the related interrupt source is enabled + */ + if (!(status & iren)) { + spin_unlock_irqrestore(&nvt->nvt_lock, flags); return IRQ_NONE; + } if (status & CIR_WAKE_IRSTS_IR_PENDING) nvt_clear_cir_wake_fifo(nvt); @@ -873,13 +1031,6 @@ static irqreturn_t nvt_cir_wake_isr(int irq, void *data) 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_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)) { @@ -888,39 +1039,21 @@ static irqreturn_t nvt_cir_wake_isr(int irq, void *data) } 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); } + spin_unlock_irqrestore(&nvt->nvt_lock, flags); + nvt_dbg_wake("%s done", __func__); return IRQ_HANDLED; } -static void nvt_enable_cir(struct nvt_dev *nvt) +static void nvt_disable_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); + unsigned long flags; - /* enable interrupts */ - nvt_set_cir_iren(nvt); -} + spin_lock_irqsave(&nvt->nvt_lock, flags); -static void nvt_disable_cir(struct nvt_dev *nvt) -{ /* disable CIR interrupts */ nvt_cir_reg_write(nvt, 0, CIR_IREN); @@ -934,13 +1067,10 @@ static void nvt_disable_cir(struct nvt_dev *nvt) nvt_clear_cir_fifo(nvt); nvt_clear_tx_fifo(nvt); - nvt_efm_enable(nvt); + spin_unlock_irqrestore(&nvt->nvt_lock, flags); /* 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); + nvt_disable_logical_dev(nvt, LOGICAL_DEV_CIR); } static int nvt_open(struct rc_dev *dev) @@ -949,20 +1079,31 @@ static int nvt_open(struct rc_dev *dev) unsigned long flags; spin_lock_irqsave(&nvt->nvt_lock, flags); - nvt_enable_cir(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); + + /* clear all pending interrupts */ + nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS); + + /* enable interrupts */ + nvt_set_cir_iren(nvt); + spin_unlock_irqrestore(&nvt->nvt_lock, flags); + /* enable the CIR logical device */ + nvt_enable_logical_dev(nvt, LOGICAL_DEV_CIR); + return 0; } static void nvt_close(struct rc_dev *dev) { struct nvt_dev *nvt = dev->priv; - unsigned long flags; - spin_lock_irqsave(&nvt->nvt_lock, flags); nvt_disable_cir(nvt); - spin_unlock_irqrestore(&nvt->nvt_lock, flags); } /* Allocate memory, probe hardware, and initialize everything */ @@ -1024,7 +1165,9 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) init_waitqueue_head(&nvt->tx.queue); - nvt_hw_detect(nvt); + ret = nvt_hw_detect(nvt); + if (ret) + goto exit_free_dev_rdev; /* Initialize CIR & CIR Wake Logical Devices */ nvt_efm_enable(nvt); @@ -1032,7 +1175,10 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) nvt_cir_wake_ldev_init(nvt); nvt_efm_disable(nvt); - /* Initialize CIR & CIR Wake Config Registers */ + /* + * Initialize CIR & CIR Wake Config Registers + * and enable logical devices + */ nvt_cir_regs_init(nvt); nvt_cir_wake_regs_init(nvt); @@ -1079,12 +1225,16 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) goto exit_unregister_device; if (!devm_request_region(&pdev->dev, nvt->cir_wake_addr, - CIR_IOREG_LENGTH, NVT_DRIVER_NAME)) + CIR_IOREG_LENGTH, NVT_DRIVER_NAME "-wake")) goto exit_unregister_device; if (devm_request_irq(&pdev->dev, nvt->cir_wake_irq, nvt_cir_wake_isr, IRQF_SHARED, - NVT_DRIVER_NAME, (void *)nvt)) + NVT_DRIVER_NAME "-wake", (void *)nvt)) + goto exit_unregister_device; + + ret = device_create_file(&rdev->dev, &dev_attr_wakeup_data); + if (ret) goto exit_unregister_device; device_init_wakeup(&pdev->dev, true); @@ -1109,15 +1259,13 @@ exit_free_dev_rdev: static void nvt_remove(struct pnp_dev *pdev) { struct nvt_dev *nvt = pnp_get_drvdata(pdev); - unsigned long flags; - spin_lock_irqsave(&nvt->nvt_lock, flags); - /* disable CIR */ - nvt_cir_reg_write(nvt, 0, CIR_IREN); + device_remove_file(&nvt->rdev->dev, &dev_attr_wakeup_data); + nvt_disable_cir(nvt); + /* enable CIR Wake (for IR power-on) */ nvt_enable_wake(nvt); - spin_unlock_irqrestore(&nvt->nvt_lock, flags); rc_unregister_device(nvt->rdev); } @@ -1129,26 +1277,23 @@ static int nvt_suspend(struct pnp_dev *pdev, pm_message_t state) 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); + spin_lock_irqsave(&nvt->nvt_lock, flags); + + /* zero out misc state tracking */ + nvt->study_state = ST_STUDY_NONE; + nvt->wake_state = ST_WAKE_NONE; + /* disable all CIR interrupts */ nvt_cir_reg_write(nvt, 0, CIR_IREN); - nvt_efm_enable(nvt); + spin_unlock_irqrestore(&nvt->nvt_lock, flags); /* 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); + nvt_disable_logical_dev(nvt, LOGICAL_DEV_CIR); /* make sure wake is enabled */ nvt_enable_wake(nvt); @@ -1162,16 +1307,6 @@ static int nvt_resume(struct pnp_dev *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); @@ -1181,6 +1316,7 @@ static int nvt_resume(struct pnp_dev *pdev) static void nvt_shutdown(struct pnp_dev *pdev) { struct nvt_dev *nvt = pnp_get_drvdata(pdev); + nvt_enable_wake(nvt); } diff --git a/drivers/media/rc/nuvoton-cir.h b/drivers/media/rc/nuvoton-cir.h index 0ad15d34e..c9c98ebb1 100644 --- a/drivers/media/rc/nuvoton-cir.h +++ b/drivers/media/rc/nuvoton-cir.h @@ -68,7 +68,8 @@ enum nvt_chip_ver { NVT_W83667HG = 0xa510, NVT_6775F = 0xb470, NVT_6776F = 0xc330, - NVT_6779D = 0xc560 + NVT_6779D = 0xc560, + NVT_INVALID = 0xffff, }; struct nvt_chip { @@ -157,8 +158,8 @@ struct nvt_dev { /* 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 +/* RX limit length, 8 high bits for SLCH, 8 low bits for SLCL */ +#define CIR_RX_LIMIT_COUNT (IR_DEFAULT_TIMEOUT / US_TO_NS(SAMPLE_PERIOD)) /* CIR Regs */ #define CIR_IRCON 0x00 @@ -292,10 +293,7 @@ struct nvt_dev { #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 +#define CIR_WAKE_IREN_GH 0x08 /* CIR WAKE FIFOCON settings */ #define CIR_WAKE_FIFOCON_RXFIFOCLR 0x08 @@ -419,3 +417,6 @@ struct nvt_dev { /* as VISTA MCE definition, valid carrier value */ #define MAX_CARRIER 60000 #define MIN_CARRIER 30000 + +/* max wakeup sequence length */ +#define WAKEUP_MAX_SIZE 65 diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index 7359f3d03..585d5e521 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -16,6 +16,9 @@ #ifndef _RC_CORE_PRIV #define _RC_CORE_PRIV +/* Define the max number of pulse/space transitions to buffer */ +#define MAX_IR_EVENT_SIZE 512 + #include <linux/slab.h> #include <linux/spinlock.h> #include <media/rc-core.h> @@ -35,7 +38,8 @@ struct ir_raw_event_ctrl { struct list_head list; /* to keep track of raw clients */ struct task_struct *thread; spinlock_t lock; - struct kfifo_rec_ptr_1 kfifo; /* fifo for the pulse/space durations */ + /* fifo for the pulse/space durations */ + DECLARE_KFIFO(kfifo, struct ir_raw_event, MAX_IR_EVENT_SIZE); ktime_t last_event; /* when last event occurred */ enum raw_event_type last_type; /* last event type */ struct rc_dev *dev; /* pointer to the parent rc_dev */ diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c index c69807fe2..144304c94 100644 --- a/drivers/media/rc/rc-ir-raw.c +++ b/drivers/media/rc/rc-ir-raw.c @@ -20,9 +20,6 @@ #include <linux/freezer.h> #include "rc-core-priv.h" -/* Define the max number of pulse/space transitions to buffer */ -#define MAX_IR_EVENT_SIZE 512 - /* Used to keep track of IR raw clients, protected by ir_raw_handler_lock */ static LIST_HEAD(ir_raw_client_list); @@ -36,14 +33,12 @@ static int ir_raw_event_thread(void *data) struct ir_raw_event ev; struct ir_raw_handler *handler; struct ir_raw_event_ctrl *raw = (struct ir_raw_event_ctrl *)data; - int retval; while (!kthread_should_stop()) { spin_lock_irq(&raw->lock); - retval = kfifo_len(&raw->kfifo); - if (retval < sizeof(ev)) { + if (!kfifo_len(&raw->kfifo)) { set_current_state(TASK_INTERRUPTIBLE); if (kthread_should_stop()) @@ -54,7 +49,8 @@ static int ir_raw_event_thread(void *data) continue; } - retval = kfifo_out(&raw->kfifo, &ev, sizeof(ev)); + if(!kfifo_out(&raw->kfifo, &ev, 1)) + dev_err(&raw->dev->dev, "IR event FIFO is empty!\n"); spin_unlock_irq(&raw->lock); mutex_lock(&ir_raw_handler_lock); @@ -87,8 +83,10 @@ int ir_raw_event_store(struct rc_dev *dev, struct ir_raw_event *ev) IR_dprintk(2, "sample: (%05dus %s)\n", TO_US(ev->duration), TO_STR(ev->pulse)); - if (kfifo_in(&dev->raw->kfifo, ev, sizeof(*ev)) != sizeof(*ev)) - return -ENOMEM; + if (!kfifo_put(&dev->raw->kfifo, *ev)) { + dev_err(&dev->dev, "IR event FIFO is full!\n"); + return -ENOSPC; + } return 0; } @@ -273,11 +271,7 @@ int ir_raw_event_register(struct rc_dev *dev) dev->raw->dev = dev; dev->change_protocol = change_protocol; - rc = kfifo_alloc(&dev->raw->kfifo, - sizeof(struct ir_raw_event) * MAX_IR_EVENT_SIZE, - GFP_KERNEL); - if (rc < 0) - goto out; + INIT_KFIFO(dev->raw->kfifo); spin_lock_init(&dev->raw->lock); dev->raw->thread = kthread_run(ir_raw_event_thread, dev->raw, @@ -319,7 +313,6 @@ void ir_raw_event_unregister(struct rc_dev *dev) handler->raw_unregister(dev); mutex_unlock(&ir_raw_handler_lock); - kfifo_free(&dev->raw->kfifo); kfree(dev->raw); dev->raw = NULL; } diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 1042fa331..4e9bbe735 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -13,6 +13,7 @@ */ #include <media/rc-core.h> +#include <linux/atomic.h> #include <linux/spinlock.h> #include <linux/delay.h> #include <linux/input.h> @@ -723,6 +724,7 @@ int rc_open(struct rc_dev *rdev) return -EINVAL; mutex_lock(&rdev->lock); + if (!rdev->users++ && rdev->open != NULL) rval = rdev->open(rdev); @@ -873,6 +875,9 @@ static ssize_t show_protocols(struct device *device, if (!dev) return -EINVAL; + if (!atomic_read(&dev->initialized)) + return -ERESTARTSYS; + mutex_lock(&dev->lock); if (fattr->type == RC_FILTER_NORMAL) { @@ -1054,6 +1059,9 @@ static ssize_t store_protocols(struct device *device, if (!dev) return -EINVAL; + if (!atomic_read(&dev->initialized)) + return -ERESTARTSYS; + if (fattr->type == RC_FILTER_NORMAL) { IR_dprintk(1, "Normal protocol change requested\n"); current_protocols = &dev->enabled_protocols; @@ -1154,12 +1162,16 @@ static ssize_t show_filter(struct device *device, if (!dev) return -EINVAL; + if (!atomic_read(&dev->initialized)) + return -ERESTARTSYS; + + mutex_lock(&dev->lock); + if (fattr->type == RC_FILTER_NORMAL) filter = &dev->scancode_filter; else filter = &dev->scancode_wakeup_filter; - mutex_lock(&dev->lock); if (fattr->mask) val = filter->mask; else @@ -1204,6 +1216,9 @@ static ssize_t store_filter(struct device *device, if (!dev) return -EINVAL; + if (!atomic_read(&dev->initialized)) + return -ERESTARTSYS; + ret = kstrtoul(buf, 0, &val); if (ret < 0) return ret; @@ -1408,6 +1423,7 @@ int rc_register_device(struct rc_dev *dev) dev->minor = minor; dev_set_name(&dev->dev, "rc%u", dev->minor); dev_set_drvdata(&dev->dev, dev); + atomic_set(&dev->initialized, 0); dev->dev.groups = dev->sysfs_groups; dev->sysfs_groups[attr++] = &rc_dev_protocol_attr_grp; @@ -1419,14 +1435,6 @@ int rc_register_device(struct rc_dev *dev) dev->sysfs_groups[attr++] = &rc_dev_wakeup_protocol_attr_grp; dev->sysfs_groups[attr++] = NULL; - /* - * Take the lock here, as the device sysfs node will appear - * when device_add() is called, which may trigger an ir-keytable udev - * rule, which will in turn call show_protocols and access - * dev->enabled_protocols before it has been initialized. - */ - mutex_lock(&dev->lock); - rc = device_add(&dev->dev); if (rc) goto out_unlock; @@ -1440,16 +1448,6 @@ int rc_register_device(struct rc_dev *dev) dev->input_dev->phys = dev->input_phys; dev->input_dev->name = dev->input_name; - /* input_register_device can call ir_open, so unlock mutex here */ - mutex_unlock(&dev->lock); - - rc = input_register_device(dev->input_dev); - - mutex_lock(&dev->lock); - - if (rc) - goto out_table; - /* * Default delay of 250ms is too short for some protocols, especially * since the timeout is currently set to 250ms. Increase it to 500ms, @@ -1465,6 +1463,11 @@ int rc_register_device(struct rc_dev *dev) */ dev->input_dev->rep[REP_PERIOD] = 125; + /* rc_open will be called here */ + rc = input_register_device(dev->input_dev); + if (rc) + goto out_table; + path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); dev_info(&dev->dev, "%s as %s\n", dev->input_name ?: "Unspecified device", path ?: "N/A"); @@ -1475,10 +1478,7 @@ int rc_register_device(struct rc_dev *dev) request_module_nowait("ir-lirc-codec"); raw_init = true; } - /* calls ir_register_device so unlock mutex here*/ - mutex_unlock(&dev->lock); rc = ir_raw_event_register(dev); - mutex_lock(&dev->lock); if (rc < 0) goto out_input; } @@ -1491,6 +1491,9 @@ int rc_register_device(struct rc_dev *dev) dev->enabled_protocols = rc_type; } + /* Allow the RC sysfs nodes to be accessible */ + mutex_lock(&dev->lock); + atomic_set(&dev->initialized, 1); mutex_unlock(&dev->lock); IR_dprintk(1, "Registered rc%u (driver: %s, remote: %s, mode %s)\n", @@ -1512,7 +1515,6 @@ out_table: out_dev: device_del(&dev->dev); out_unlock: - mutex_unlock(&dev->lock); ida_simple_remove(&rc_ida, minor); return rc; } diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c index 40f77685c..eaadc0817 100644 --- a/drivers/media/rc/sunxi-cir.c +++ b/drivers/media/rc/sunxi-cir.c @@ -326,6 +326,7 @@ static const struct of_device_id sunxi_ir_match[] = { { .compatible = "allwinner,sun5i-a13-ir", }, {}, }; +MODULE_DEVICE_TABLE(of, sunxi_ir_match); static struct platform_driver sunxi_ir_driver = { .probe = sunxi_ir_probe, |