diff options
Diffstat (limited to 'drivers/media/rc')
-rw-r--r-- | drivers/media/rc/Kconfig | 26 | ||||
-rw-r--r-- | drivers/media/rc/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/rc/igorplugusb.c | 261 | ||||
-rw-r--r-- | drivers/media/rc/img-ir/Kconfig | 1 | ||||
-rw-r--r-- | drivers/media/rc/img-ir/img-ir-hw.c | 28 | ||||
-rw-r--r-- | drivers/media/rc/img-ir/img-ir-hw.h | 6 | ||||
-rw-r--r-- | drivers/media/rc/imon.c | 3 | ||||
-rw-r--r-- | drivers/media/rc/ir-hix5hd2.c | 2 | ||||
-rw-r--r-- | drivers/media/rc/ir-lirc-codec.c | 12 | ||||
-rw-r--r-- | drivers/media/rc/ir-rc5-decoder.c | 2 | ||||
-rw-r--r-- | drivers/media/rc/ir-rc6-decoder.c | 2 | ||||
-rw-r--r-- | drivers/media/rc/lirc_dev.c | 3 | ||||
-rw-r--r-- | drivers/media/rc/meson-ir.c | 216 | ||||
-rw-r--r-- | drivers/media/rc/rc-ir-raw.c | 1 | ||||
-rw-r--r-- | drivers/media/rc/rc-main.c | 10 | ||||
-rw-r--r-- | drivers/media/rc/redrat3.c | 4 |
16 files changed, 557 insertions, 22 deletions
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 8ce08107a69d..ddfab256b9a5 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -223,6 +223,17 @@ config IR_FINTEK To compile this driver as a module, choose M here: the module will be called fintek-cir. +config IR_MESON + tristate "Amlogic Meson IR remote receiver" + depends on RC_CORE + depends on ARCH_MESON || COMPILE_TEST + ---help--- + Say Y if you want to use the IR remote receiver available + on Amlogic Meson SoCs. + + To compile this driver as a module, choose M here: the + module will be called meson-ir. + config IR_NUVOTON tristate "Nuvoton w836x7hg Consumer Infrared Transceiver" depends on PNP @@ -277,6 +288,21 @@ config IR_WINBOND_CIR To compile this driver as a module, choose M here: the module will be called winbond_cir. +config IR_IGORPLUGUSB + tristate "IgorPlug-USB IR Receiver" + depends on USB_ARCH_HAS_HCD + depends on RC_CORE + select USB + ---help--- + Say Y here if you want to use the IgorPlug-USB IR Receiver by + Igor Cesko. This device is included on the Fit-PC2. + + Note that this device can only record bursts of 36 IR pulses and + spaces, which is not enough for the NEC, Sanyo and RC-6 protocol. + + To compile this driver as a module, choose M here: the module will + be called igorplugusb. + config IR_IGUANA tristate "IguanaWorks USB IR Transceiver" depends on USB_ARCH_HAS_HCD diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index 0989f940e9cf..379a5c0f1379 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_IR_IMON) += imon.o obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o obj-$(CONFIG_IR_MCEUSB) += mceusb.o obj-$(CONFIG_IR_FINTEK) += fintek-cir.o +obj-$(CONFIG_IR_MESON) += meson-ir.o obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o obj-$(CONFIG_IR_ENE) += ene_ir.o obj-$(CONFIG_IR_REDRAT3) += redrat3.o @@ -30,6 +31,7 @@ obj-$(CONFIG_IR_STREAMZAP) += streamzap.o obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o +obj-$(CONFIG_IR_IGORPLUGUSB) += igorplugusb.o obj-$(CONFIG_IR_IGUANA) += iguanair.o obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o obj-$(CONFIG_RC_ST) += st_rc.o diff --git a/drivers/media/rc/igorplugusb.c b/drivers/media/rc/igorplugusb.c new file mode 100644 index 000000000000..b36e51576f8e --- /dev/null +++ b/drivers/media/rc/igorplugusb.c @@ -0,0 +1,261 @@ +/* + * IgorPlug-USB IR Receiver + * + * Copyright (C) 2014 Sean Young <sean@mess.org> + * + * Supports the standard homebrew IgorPlugUSB receiver with Igor's firmware. + * See http://www.cesko.host.sk/IgorPlugUSB/IgorPlug-USB%20(AVR)_eng.htm + * + * Based on the lirc_igorplugusb.c driver: + * Copyright (C) 2004 Jan M. Hochstein + * <hochstein@algo.informatik.tu-darmstadt.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/usb/input.h> +#include <media/rc-core.h> + +#define DRIVER_DESC "IgorPlug-USB IR Receiver" +#define DRIVER_NAME "igorplugusb" + +#define HEADERLEN 3 +#define BUFLEN 36 +#define MAX_PACKET (HEADERLEN + BUFLEN) + +#define SET_INFRABUFFER_EMPTY 1 +#define GET_INFRACODE 2 + + +struct igorplugusb { + struct rc_dev *rc; + struct device *dev; + + struct urb *urb; + struct usb_ctrlrequest request; + + struct timer_list timer; + + uint8_t buf_in[MAX_PACKET]; + + char phys[64]; +}; + +static void igorplugusb_cmd(struct igorplugusb *ir, int cmd); + +static void igorplugusb_irdata(struct igorplugusb *ir, unsigned len) +{ + DEFINE_IR_RAW_EVENT(rawir); + unsigned i, start, overflow; + + dev_dbg(ir->dev, "irdata: %*ph (len=%u)", len, ir->buf_in, len); + + /* + * If more than 36 pulses and spaces follow each other, the igorplugusb + * overwrites its buffer from the beginning. The overflow value is the + * last offset which was not overwritten. Everything from this offset + * onwards occurred before everything until this offset. + */ + overflow = ir->buf_in[2]; + i = start = overflow + HEADERLEN; + + if (start >= len) { + dev_err(ir->dev, "receive overflow invalid: %u", overflow); + } else { + if (overflow > 0) + dev_warn(ir->dev, "receive overflow, at least %u lost", + overflow); + + do { + rawir.duration = ir->buf_in[i] * 85333; + rawir.pulse = i & 1; + + ir_raw_event_store_with_filter(ir->rc, &rawir); + + if (++i == len) + i = HEADERLEN; + } while (i != start); + + /* add a trailing space */ + rawir.duration = ir->rc->timeout; + rawir.pulse = false; + ir_raw_event_store_with_filter(ir->rc, &rawir); + + ir_raw_event_handle(ir->rc); + } + + igorplugusb_cmd(ir, SET_INFRABUFFER_EMPTY); +} + +static void igorplugusb_callback(struct urb *urb) +{ + struct usb_ctrlrequest *req; + struct igorplugusb *ir = urb->context; + + req = (struct usb_ctrlrequest *)urb->setup_packet; + + switch (urb->status) { + case 0: + if (req->bRequest == GET_INFRACODE && + urb->actual_length > HEADERLEN) + igorplugusb_irdata(ir, urb->actual_length); + else /* request IR */ + mod_timer(&ir->timer, jiffies + msecs_to_jiffies(50)); + break; + case -EPROTO: + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + usb_unlink_urb(urb); + return; + default: + dev_warn(ir->dev, "Error: urb status = %d\n", urb->status); + igorplugusb_cmd(ir, SET_INFRABUFFER_EMPTY); + break; + } +} + +static void igorplugusb_cmd(struct igorplugusb *ir, int cmd) +{ + int ret; + + ir->request.bRequest = cmd; + ir->urb->transfer_flags = 0; + ret = usb_submit_urb(ir->urb, GFP_ATOMIC); + if (ret) + dev_err(ir->dev, "submit urb failed: %d", ret); +} + +static void igorplugusb_timer(unsigned long data) +{ + struct igorplugusb *ir = (struct igorplugusb *)data; + + igorplugusb_cmd(ir, GET_INFRACODE); +} + +static int igorplugusb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev; + struct usb_host_interface *idesc; + struct usb_endpoint_descriptor *ep; + struct igorplugusb *ir; + struct rc_dev *rc; + int ret; + + udev = interface_to_usbdev(intf); + idesc = intf->cur_altsetting; + + if (idesc->desc.bNumEndpoints != 1) { + dev_err(&intf->dev, "incorrect number of endpoints"); + return -ENODEV; + } + + ep = &idesc->endpoint[0].desc; + if (!usb_endpoint_dir_in(ep) || !usb_endpoint_xfer_control(ep)) { + dev_err(&intf->dev, "endpoint incorrect"); + return -ENODEV; + } + + ir = devm_kzalloc(&intf->dev, sizeof(*ir), GFP_KERNEL); + if (!ir) + return -ENOMEM; + + ir->dev = &intf->dev; + + setup_timer(&ir->timer, igorplugusb_timer, (unsigned long)ir); + + ir->request.bRequest = GET_INFRACODE; + ir->request.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN; + ir->request.wLength = cpu_to_le16(sizeof(ir->buf_in)); + + ir->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!ir->urb) + return -ENOMEM; + + usb_fill_control_urb(ir->urb, udev, + usb_rcvctrlpipe(udev, 0), (uint8_t *)&ir->request, + ir->buf_in, sizeof(ir->buf_in), igorplugusb_callback, ir); + + usb_make_path(udev, ir->phys, sizeof(ir->phys)); + + rc = rc_allocate_device(); + rc->input_name = DRIVER_DESC; + rc->input_phys = ir->phys; + usb_to_input_id(udev, &rc->input_id); + rc->dev.parent = &intf->dev; + rc->driver_type = RC_DRIVER_IR_RAW; + /* + * This device can only store 36 pulses + spaces, which is not enough + * for the NEC protocol and many others. + */ + rc->allowed_protocols = RC_BIT_ALL & ~(RC_BIT_NEC | RC_BIT_RC6_6A_20 | + RC_BIT_RC6_6A_24 | RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE | + RC_BIT_SONY20 | RC_BIT_MCE_KBD | RC_BIT_SANYO); + + rc->priv = ir; + rc->driver_name = DRIVER_NAME; + rc->map_name = RC_MAP_HAUPPAUGE; + rc->timeout = MS_TO_NS(100); + rc->rx_resolution = 85333; + + ir->rc = rc; + ret = rc_register_device(rc); + if (ret) { + dev_err(&intf->dev, "failed to register rc device: %d", ret); + rc_free_device(rc); + usb_free_urb(ir->urb); + return ret; + } + + usb_set_intfdata(intf, ir); + + igorplugusb_cmd(ir, SET_INFRABUFFER_EMPTY); + + return 0; +} + +static void igorplugusb_disconnect(struct usb_interface *intf) +{ + struct igorplugusb *ir = usb_get_intfdata(intf); + + rc_unregister_device(ir->rc); + del_timer_sync(&ir->timer); + usb_set_intfdata(intf, NULL); + usb_kill_urb(ir->urb); + usb_free_urb(ir->urb); +} + +static struct usb_device_id igorplugusb_table[] = { + /* Igor Plug USB (Atmel's Manufact. ID) */ + { USB_DEVICE(0x03eb, 0x0002) }, + /* Fit PC2 Infrared Adapter */ + { USB_DEVICE(0x03eb, 0x21fe) }, + /* Terminating entry */ + { } +}; + +static struct usb_driver igorplugusb_driver = { + .name = DRIVER_NAME, + .probe = igorplugusb_probe, + .disconnect = igorplugusb_disconnect, + .id_table = igorplugusb_table +}; + +module_usb_driver(igorplugusb_driver); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Sean Young <sean@mess.org>"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(usb, igorplugusb_table); diff --git a/drivers/media/rc/img-ir/Kconfig b/drivers/media/rc/img-ir/Kconfig index 03ba9fc170fb..580715c7fc5e 100644 --- a/drivers/media/rc/img-ir/Kconfig +++ b/drivers/media/rc/img-ir/Kconfig @@ -1,6 +1,7 @@ config IR_IMG tristate "ImgTec IR Decoder" depends on RC_CORE + depends on METAG || MIPS || COMPILE_TEST select IR_IMG_HW if !IR_IMG_RAW help Say Y or M here if you want to use the ImgTec infrared decoder diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c index ec49f94425fc..2fd47c9bf5d8 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.c +++ b/drivers/media/rc/img-ir/img-ir-hw.c @@ -530,6 +530,22 @@ static void img_ir_set_decoder(struct img_ir_priv *priv, u32 ir_status, irq_en; spin_lock_irq(&priv->lock); + /* + * First record that the protocol is being stopped so that the end timer + * isn't restarted while we're trying to stop it. + */ + hw->stopping = true; + + /* + * Release the lock to stop the end timer, since the end timer handler + * acquires the lock and we don't want to deadlock waiting for it. + */ + spin_unlock_irq(&priv->lock); + del_timer_sync(&hw->end_timer); + spin_lock_irq(&priv->lock); + + hw->stopping = false; + /* switch off and disable interrupts */ img_ir_write(priv, IMG_IR_CONTROL, 0); irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE); @@ -541,12 +557,13 @@ static void img_ir_set_decoder(struct img_ir_priv *priv, if (ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2)) { ir_status &= ~(IMG_IR_RXDVAL | IMG_IR_RXDVALD2); img_ir_write(priv, IMG_IR_STATUS, ir_status); - img_ir_read(priv, IMG_IR_DATA_LW); - img_ir_read(priv, IMG_IR_DATA_UP); } - /* stop the end timer and switch back to normal mode */ - del_timer_sync(&hw->end_timer); + /* always read data to clear buffer if IR wakes the device */ + img_ir_read(priv, IMG_IR_DATA_LW); + img_ir_read(priv, IMG_IR_DATA_UP); + + /* switch back to normal mode */ hw->mode = IMG_IR_M_NORMAL; /* clear the wakeup scancode filter */ @@ -817,7 +834,8 @@ static void img_ir_handle_data(struct img_ir_priv *priv, u32 len, u64 raw) } - if (dec->repeat) { + /* we mustn't update the end timer while trying to stop it */ + if (dec->repeat && !hw->stopping) { unsigned long interval; img_ir_begin_repeat(priv); diff --git a/drivers/media/rc/img-ir/img-ir-hw.h b/drivers/media/rc/img-ir/img-ir-hw.h index 8fcc16c32c5b..5c2b216c5fe3 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.h +++ b/drivers/media/rc/img-ir/img-ir-hw.h @@ -186,9 +186,6 @@ struct img_ir_reg_timings { struct img_ir_timing_regvals rtimings; }; -int img_ir_register_decoder(struct img_ir_decoder *dec); -void img_ir_unregister_decoder(struct img_ir_decoder *dec); - struct img_ir_priv; #ifdef CONFIG_IR_IMG_HW @@ -214,6 +211,8 @@ enum img_ir_mode { * @flags: IMG_IR_F_*. * @filters: HW filters (derived from scancode filters). * @mode: Current decode mode. + * @stopping: Indicates that decoder is being taken down and timers + * should not be restarted. * @suspend_irqen: Saved IRQ enable mask over suspend. */ struct img_ir_priv_hw { @@ -229,6 +228,7 @@ struct img_ir_priv_hw { struct img_ir_filter filters[RC_FILTER_MAX]; enum img_ir_mode mode; + bool stopping; u32 suspend_irqen; }; diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index b8837dd39bb2..65f80b8b9f7a 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -1678,7 +1678,8 @@ static void imon_incoming_packet(struct imon_context *ictx, if (press_type == 0) rc_keyup(ictx->rdev); else { - if (ictx->rc_type == RC_BIT_RC6_MCE) + if (ictx->rc_type == RC_BIT_RC6_MCE || + ictx->rc_type == RC_BIT_OTHER) rc_keydown(ictx->rdev, ictx->rc_type == RC_BIT_RC6_MCE ? RC_TYPE_RC6_MCE : RC_TYPE_OTHER, ictx->rc_scancode, ictx->rc_toggle); diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c index 08bbd4f508cd..b0df62961c14 100644 --- a/drivers/media/rc/ir-hix5hd2.c +++ b/drivers/media/rc/ir-hix5hd2.c @@ -297,7 +297,7 @@ static int hix5hd2_ir_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int hix5hd2_ir_suspend(struct device *dev) { struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev); diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c index ed2c8a1ed8ca..98893a8332c7 100644 --- a/drivers/media/rc/ir-lirc-codec.c +++ b/drivers/media/rc/ir-lirc-codec.c @@ -42,11 +42,17 @@ static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev) return -EINVAL; /* Packet start */ - if (ev.reset) - return 0; + if (ev.reset) { + /* Userspace expects a long space event before the start of + * the signal to use as a sync. This may be done with repeat + * packets and normal samples. But if a reset has been sent + * then we assume that a long time has passed, so we send a + * space with the maximum time value. */ + sample = LIRC_SPACE(LIRC_VALUE_MASK); + IR_dprintk(2, "delivering reset sync space to lirc_dev\n"); /* Carrier reports */ - if (ev.carrier_report) { + } else if (ev.carrier_report) { sample = LIRC_FREQUENCY(ev.carrier); IR_dprintk(2, "carrier report (freq: %d)\n", sample); diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c index 2ef763928ca4..84fa6e9b59a1 100644 --- a/drivers/media/rc/ir-rc5-decoder.c +++ b/drivers/media/rc/ir-rc5-decoder.c @@ -53,7 +53,7 @@ static int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev) u32 scancode; enum rc_type protocol; - if (!(dev->enabled_protocols & (RC_BIT_RC5 | RC_BIT_RC5X))) + if (!(dev->enabled_protocols & (RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ))) return 0; if (!is_timing_event(ev)) { diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c index f1f098e22f7e..d16bc67af732 100644 --- a/drivers/media/rc/ir-rc6-decoder.c +++ b/drivers/media/rc/ir-rc6-decoder.c @@ -259,8 +259,8 @@ again: case 32: if ((scancode & RC6_6A_LCC_MASK) == RC6_6A_MCE_CC) { protocol = RC_TYPE_RC6_MCE; - scancode &= ~RC6_6A_MCE_TOGGLE_MASK; toggle = !!(scancode & RC6_6A_MCE_TOGGLE_MASK); + scancode &= ~RC6_6A_MCE_TOGGLE_MASK; } else { protocol = RC_BIT_RC6_6A_32; toggle = 0; diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index 249d2fbc8f37..1e0545a67959 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -518,8 +518,7 @@ int lirc_dev_fop_close(struct inode *inode, struct file *file) WARN_ON(mutex_lock_killable(&lirc_dev_lock)); - if (ir->d.rdev) - rc_close(ir->d.rdev); + rc_close(ir->d.rdev); ir->open--; if (ir->attached) { diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c new file mode 100644 index 000000000000..fcc3b82d1454 --- /dev/null +++ b/drivers/media/rc/meson-ir.c @@ -0,0 +1,216 @@ +/* + * Driver for Amlogic Meson IR remote receiver + * + * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> + +#include <media/rc-core.h> + +#define DRIVER_NAME "meson-ir" + +#define IR_DEC_LDR_ACTIVE 0x00 +#define IR_DEC_LDR_IDLE 0x04 +#define IR_DEC_LDR_REPEAT 0x08 +#define IR_DEC_BIT_0 0x0c +#define IR_DEC_REG0 0x10 +#define IR_DEC_FRAME 0x14 +#define IR_DEC_STATUS 0x18 +#define IR_DEC_REG1 0x1c + +#define REG0_RATE_MASK (BIT(11) - 1) + +#define REG1_MODE_MASK (BIT(7) | BIT(8)) +#define REG1_MODE_NEC (0 << 7) +#define REG1_MODE_GENERAL (2 << 7) + +#define REG1_TIME_IV_SHIFT 16 +#define REG1_TIME_IV_MASK ((BIT(13) - 1) << REG1_TIME_IV_SHIFT) + +#define REG1_IRQSEL_MASK (BIT(2) | BIT(3)) +#define REG1_IRQSEL_NEC_MODE (0 << 2) +#define REG1_IRQSEL_RISE_FALL (1 << 2) +#define REG1_IRQSEL_FALL (2 << 2) +#define REG1_IRQSEL_RISE (3 << 2) + +#define REG1_RESET BIT(0) +#define REG1_ENABLE BIT(15) + +#define STATUS_IR_DEC_IN BIT(8) + +#define MESON_TRATE 10 /* us */ + +struct meson_ir { + void __iomem *reg; + struct rc_dev *rc; + int irq; + spinlock_t lock; +}; + +static void meson_ir_set_mask(struct meson_ir *ir, unsigned int reg, + u32 mask, u32 value) +{ + u32 data; + + data = readl(ir->reg + reg); + data &= ~mask; + data |= (value & mask); + writel(data, ir->reg + reg); +} + +static irqreturn_t meson_ir_irq(int irqno, void *dev_id) +{ + struct meson_ir *ir = dev_id; + u32 duration; + DEFINE_IR_RAW_EVENT(rawir); + + spin_lock(&ir->lock); + + duration = readl(ir->reg + IR_DEC_REG1); + duration = (duration & REG1_TIME_IV_MASK) >> REG1_TIME_IV_SHIFT; + rawir.duration = US_TO_NS(duration * MESON_TRATE); + + rawir.pulse = !!(readl(ir->reg + IR_DEC_STATUS) & STATUS_IR_DEC_IN); + + ir_raw_event_store_with_filter(ir->rc, &rawir); + ir_raw_event_handle(ir->rc); + + spin_unlock(&ir->lock); + + return IRQ_HANDLED; +} + +static int meson_ir_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct resource *res; + const char *map_name; + struct meson_ir *ir; + int ret; + + ir = devm_kzalloc(dev, sizeof(struct meson_ir), GFP_KERNEL); + if (!ir) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ir->reg = devm_ioremap_resource(dev, res); + if (IS_ERR(ir->reg)) { + dev_err(dev, "failed to map registers\n"); + return PTR_ERR(ir->reg); + } + + ir->irq = platform_get_irq(pdev, 0); + if (ir->irq < 0) { + dev_err(dev, "no irq resource\n"); + return ir->irq; + } + + ir->rc = rc_allocate_device(); + if (!ir->rc) { + dev_err(dev, "failed to allocate rc device\n"); + return -ENOMEM; + } + + ir->rc->priv = ir; + ir->rc->input_name = DRIVER_NAME; + ir->rc->input_phys = DRIVER_NAME "/input0"; + ir->rc->input_id.bustype = BUS_HOST; + map_name = of_get_property(node, "linux,rc-map-name", NULL); + ir->rc->map_name = map_name ? map_name : RC_MAP_EMPTY; + ir->rc->dev.parent = dev; + ir->rc->driver_type = RC_DRIVER_IR_RAW; + ir->rc->allowed_protocols = RC_BIT_ALL; + ir->rc->rx_resolution = US_TO_NS(MESON_TRATE); + ir->rc->timeout = MS_TO_NS(200); + ir->rc->driver_name = DRIVER_NAME; + + spin_lock_init(&ir->lock); + platform_set_drvdata(pdev, ir); + + ret = rc_register_device(ir->rc); + if (ret) { + dev_err(dev, "failed to register rc device\n"); + goto out_free; + } + + ret = devm_request_irq(dev, ir->irq, meson_ir_irq, 0, "ir-meson", ir); + if (ret) { + dev_err(dev, "failed to request irq\n"); + goto out_unreg; + } + + /* Reset the decoder */ + meson_ir_set_mask(ir, IR_DEC_REG1, REG1_RESET, REG1_RESET); + meson_ir_set_mask(ir, IR_DEC_REG1, REG1_RESET, 0); + /* Set general operation mode */ + meson_ir_set_mask(ir, IR_DEC_REG1, REG1_MODE_MASK, REG1_MODE_GENERAL); + /* Set rate */ + meson_ir_set_mask(ir, IR_DEC_REG0, REG0_RATE_MASK, MESON_TRATE - 1); + /* IRQ on rising and falling edges */ + meson_ir_set_mask(ir, IR_DEC_REG1, REG1_IRQSEL_MASK, + REG1_IRQSEL_RISE_FALL); + /* Enable the decoder */ + meson_ir_set_mask(ir, IR_DEC_REG1, REG1_ENABLE, REG1_ENABLE); + + dev_info(dev, "receiver initialized\n"); + + return 0; +out_unreg: + rc_unregister_device(ir->rc); + ir->rc = NULL; +out_free: + rc_free_device(ir->rc); + + return ret; +} + +static int meson_ir_remove(struct platform_device *pdev) +{ + struct meson_ir *ir = platform_get_drvdata(pdev); + unsigned long flags; + + /* Disable the decoder */ + spin_lock_irqsave(&ir->lock, flags); + meson_ir_set_mask(ir, IR_DEC_REG1, REG1_ENABLE, 0); + spin_unlock_irqrestore(&ir->lock, flags); + + rc_unregister_device(ir->rc); + + return 0; +} + +static const struct of_device_id meson_ir_match[] = { + { .compatible = "amlogic,meson6-ir" }, + { }, +}; + +static struct platform_driver meson_ir_driver = { + .probe = meson_ir_probe, + .remove = meson_ir_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = meson_ir_match, + }, +}; + +module_platform_driver(meson_ir_driver); + +MODULE_DESCRIPTION("Amlogic Meson IR remote receiver driver"); +MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c index e8fff2add265..b732ac6a26d8 100644 --- a/drivers/media/rc/rc-ir-raw.c +++ b/drivers/media/rc/rc-ir-raw.c @@ -262,7 +262,6 @@ int ir_raw_event_register(struct rc_dev *dev) return -ENOMEM; dev->raw->dev = dev; - dev->enabled_protocols = ~0; dev->change_protocol = change_protocol; rc = kfifo_alloc(&dev->raw->kfifo, sizeof(struct ir_raw_event) * MAX_IR_EVENT_SIZE, diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index a7991c7d010a..86ffcd54339e 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -1298,8 +1298,7 @@ void rc_free_device(struct rc_dev *dev) if (!dev) return; - if (dev->input_dev) - input_free_device(dev->input_dev); + input_free_device(dev->input_dev); put_device(&dev->dev); @@ -1414,13 +1413,18 @@ int rc_register_device(struct rc_dev *dev) ir_raw_init(); raw_init = true; } + /* calls ir_register_device so unlock mutex here*/ + mutex_unlock(&dev->lock); rc = ir_raw_event_register(dev); + mutex_lock(&dev->lock); if (rc < 0) goto out_input; } if (dev->change_protocol) { - u64 rc_type = (1 << rc_map->rc_type); + u64 rc_type = (1ll << rc_map->rc_type); + if (dev->driver_type == RC_DRIVER_IR_RAW) + rc_type |= RC_BIT_LIRC; rc = dev->change_protocol(dev, &rc_type); if (rc < 0) goto out_raw; diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c index 795b394a5d84..c4def66f9aa2 100644 --- a/drivers/media/rc/redrat3.c +++ b/drivers/media/rc/redrat3.c @@ -966,7 +966,7 @@ static int redrat3_dev_probe(struct usb_interface *intf, rr3->ep_in = ep_in; rr3->bulk_in_buf = usb_alloc_coherent(udev, - le16_to_cpu(ep_in->wMaxPacketSize), GFP_ATOMIC, &rr3->dma_in); + le16_to_cpu(ep_in->wMaxPacketSize), GFP_KERNEL, &rr3->dma_in); if (!rr3->bulk_in_buf) { dev_err(dev, "Read buffer allocation failure\n"); goto error; @@ -975,6 +975,8 @@ static int redrat3_dev_probe(struct usb_interface *intf, pipe = usb_rcvbulkpipe(udev, ep_in->bEndpointAddress); usb_fill_bulk_urb(rr3->read_urb, udev, pipe, rr3->bulk_in_buf, le16_to_cpu(ep_in->wMaxPacketSize), redrat3_handle_async, rr3); + rr3->read_urb->transfer_dma = rr3->dma_in; + rr3->read_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; rr3->ep_out = ep_out; rr3->udev = udev; |