sunxi: add support for infra receiver on A20, along with IR-related backports from...
authorZoltan Herpai <wigyori@uid0.hu>
Sun, 21 Sep 2014 16:25:45 +0000 (16:25 +0000)
committerZoltan Herpai <wigyori@uid0.hu>
Sun, 21 Sep 2014 16:25:45 +0000 (16:25 +0000)
Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
SVN-Revision: 42630

target/linux/sunxi/patches-3.14/280-ir-add-driver.patch [new file with mode: 0644]
target/linux/sunxi/patches-3.14/281-dt-sun7i-add-ir-pins.patch [new file with mode: 0644]
target/linux/sunxi/patches-3.14/282-dt-sun7i-add-ir-ctrlers.patch [new file with mode: 0644]
target/linux/sunxi/patches-3.14/283-dt-sun7i-add-ir-to-cb2-cbt.patch [new file with mode: 0644]
target/linux/sunxi/patches-3.14/284-ir-backports-from-3.15.patch [new file with mode: 0644]
target/linux/sunxi/patches-3.14/285-dt-sun7i-add-ir-to-bananapi.patch [new file with mode: 0644]

diff --git a/target/linux/sunxi/patches-3.14/280-ir-add-driver.patch b/target/linux/sunxi/patches-3.14/280-ir-add-driver.patch
new file mode 100644 (file)
index 0000000..dd0599e
--- /dev/null
@@ -0,0 +1,370 @@
+From 601b6a88cd14e655ccd246fe122cbf496a891cbb Mon Sep 17 00:00:00 2001
+From: Alexander Bersenev <bay@hackerdom.ru>
+Date: Mon, 9 Jun 2014 00:08:10 +0600
+Subject: [PATCH] rc: add sunxi-ir driver
+
+This patch adds driver for sunxi IR controller.
+It is based on Alexsey Shestacov's work based on the original driver
+supplied by Allwinner.
+
+Signed-off-by: Alexander Bersenev <bay@hackerdom.ru>
+Signed-off-by: Alexsey Shestacov <wingrime@linux-sunxi.org>
+---
+ drivers/media/rc/Kconfig     |  10 ++
+ drivers/media/rc/Makefile    |   1 +
+ drivers/media/rc/sunxi-cir.c | 318 +++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 329 insertions(+)
+ create mode 100644 drivers/media/rc/sunxi-cir.c
+
+diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
+index 8fbd377..9427fad 100644
+--- a/drivers/media/rc/Kconfig
++++ b/drivers/media/rc/Kconfig
+@@ -343,4 +343,14 @@ config RC_ST
+        If you're not sure, select N here.
++config IR_SUNXI
++    tristate "SUNXI IR remote control"
++    depends on RC_CORE
++    depends on ARCH_SUNXI
++    ---help---
++      Say Y if you want to use sunXi internal IR Controller
++
++      To compile this driver as a module, choose M here: the module will
++      be called sunxi-ir.
++
+ endif #RC_DEVICES
+diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
+index f8b54ff..9ee9ee7 100644
+--- a/drivers/media/rc/Makefile
++++ b/drivers/media/rc/Makefile
+@@ -32,3 +32,4 @@ obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o
+ obj-$(CONFIG_IR_IGUANA) += iguanair.o
+ obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o
+ obj-$(CONFIG_RC_ST) += st_rc.o
++obj-$(CONFIG_IR_SUNXI) += sunxi-cir.o
+diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
+new file mode 100644
+index 0000000..5971b69
+--- /dev/null
++++ b/drivers/media/rc/sunxi-cir.c
+@@ -0,0 +1,318 @@
++/*
++ * Driver for Allwinner sunXi IR controller
++ *
++ * Copyright (C) 2014 Alexsey Shestacov <wingrime@linux-sunxi.org>
++ * Copyright (C) 2014 Alexander Bersenev <bay@hackerdom.ru>
++ *
++ * Based on sun5i-ir.c:
++ * Copyright (C) 2007-2012 Daniel Wang
++ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
++ *
++ * 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/clk.h>
++#include <linux/interrupt.h>
++#include <linux/module.h>
++#include <linux/of_platform.h>
++#include <media/rc-core.h>
++
++#define SUNXI_IR_DEV "sunxi-ir"
++
++/* Registers */
++/* IR Control */
++#define SUNXI_IR_CTL_REG      0x00
++/* Global Enable */
++#define REG_CTL_GEN                   BIT(0)
++/* RX block enable */
++#define REG_CTL_RXEN                  BIT(1)
++/* CIR mode */
++#define REG_CTL_MD                    (BIT(4) | BIT(5))
++
++/* Rx Config */
++#define SUNXI_IR_RXCTL_REG    0x10
++/* Pulse Polarity Invert flag */
++#define REG_RXCTL_RPPI                        BIT(2)
++
++/* Rx Data */
++#define SUNXI_IR_RXFIFO_REG   0x20
++
++/* Rx Interrupt Enable */
++#define SUNXI_IR_RXINT_REG    0x2C
++/* Rx FIFO Overflow */
++#define REG_RXINT_ROI_EN              BIT(0)
++/* Rx Packet End */
++#define REG_RXINT_RPEI_EN             BIT(1)
++/* Rx FIFO Data Available */
++#define REG_RXINT_RAI_EN              BIT(4)
++
++/* Rx FIFO available byte level */
++#define REG_RXINT_RAL(val)    (((val) << 8) & (GENMASK(11, 8)))
++
++/* Rx Interrupt Status */
++#define SUNXI_IR_RXSTA_REG    0x30
++/* RX FIFO Get Available Counter */
++#define REG_RXSTA_GET_AC(val) (((val) >> 8) & (GENMASK(5, 0)))
++/* Clear all interrupt status value */
++#define REG_RXSTA_CLEARALL    0xff
++
++/* IR Sample Config */
++#define SUNXI_IR_CIR_REG      0x34
++/* CIR_REG register noise threshold */
++#define REG_CIR_NTHR(val)    (((val) << 2) & (GENMASK(7, 2)))
++/* CIR_REG register idle threshold */
++#define REG_CIR_ITHR(val)    (((val) << 8) & (GENMASK(15, 8)))
++
++/* Hardware supported fifo size */
++#define SUNXI_IR_FIFO_SIZE    16
++/* How many messages in FIFO trigger IRQ */
++#define TRIGGER_LEVEL         8
++/* Required frequency for IR0 or IR1 clock in CIR mode */
++#define SUNXI_IR_BASE_CLK     8000000
++/* Frequency after IR internal divider  */
++#define SUNXI_IR_CLK          (SUNXI_IR_BASE_CLK / 64)
++/* Sample period in ns */
++#define SUNXI_IR_SAMPLE       (1000000000ul / SUNXI_IR_CLK)
++/* Noise threshold in samples  */
++#define SUNXI_IR_RXNOISE      1
++/* Idle Threshold in samples */
++#define SUNXI_IR_RXIDLE       20
++/* Time after which device stops sending data in ms */
++#define SUNXI_IR_TIMEOUT      120
++
++struct sunxi_ir {
++      spinlock_t      ir_lock;
++      struct rc_dev   *rc;
++      void __iomem    *base;
++      int             irq;
++      struct clk      *clk;
++      struct clk      *apb_clk;
++      const char      *map_name;
++};
++
++static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id)
++{
++      unsigned long status;
++      unsigned char dt;
++      unsigned int cnt, rc;
++      struct sunxi_ir *ir = dev_id;
++      DEFINE_IR_RAW_EVENT(rawir);
++
++      spin_lock(&ir->ir_lock);
++
++      status = readl(ir->base + SUNXI_IR_RXSTA_REG);
++
++      /* clean all pending statuses */
++      writel(status | REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG);
++
++      if (status & REG_RXINT_RAI_EN) {
++              /* How many messages in fifo */
++              rc  = REG_RXSTA_GET_AC(status);
++              /* Sanity check */
++              rc = rc > SUNXI_IR_FIFO_SIZE ? SUNXI_IR_FIFO_SIZE : rc;
++              /* If we have data */
++              for (cnt = 0; cnt < rc; cnt++) {
++                      /* for each bit in fifo */
++                      dt = readb(ir->base + SUNXI_IR_RXFIFO_REG);
++                      rawir.pulse = (dt & 0x80) != 0;
++                      rawir.duration = ((dt & 0x7f) + 1) * SUNXI_IR_SAMPLE;
++                      ir_raw_event_store_with_filter(ir->rc, &rawir);
++              }
++      }
++
++      if (status & REG_RXINT_ROI_EN) {
++              ir_raw_event_reset(ir->rc);
++      } else if (status & REG_RXINT_RPEI_EN) {
++              ir_raw_event_set_idle(ir->rc, true);
++              ir_raw_event_handle(ir->rc);
++      }
++
++      spin_unlock(&ir->ir_lock);
++
++      return IRQ_HANDLED;
++}
++
++static int sunxi_ir_probe(struct platform_device *pdev)
++{
++      int ret = 0;
++      unsigned long tmp = 0;
++
++      struct device *dev = &pdev->dev;
++      struct device_node *dn = dev->of_node;
++      struct resource *res;
++      struct sunxi_ir *ir;
++
++      ir = devm_kzalloc(dev, sizeof(struct sunxi_ir), GFP_KERNEL);
++      if (!ir)
++              return -ENOMEM;
++
++      /* Clock */
++      ir->apb_clk = devm_clk_get(dev, "apb");
++      if (IS_ERR(ir->apb_clk)) {
++              dev_err(dev, "failed to get a apb clock.\n");
++              return PTR_ERR(ir->apb_clk);
++      }
++      ir->clk = devm_clk_get(dev, "ir");
++      if (IS_ERR(ir->clk)) {
++              dev_err(dev, "failed to get a ir clock.\n");
++              return PTR_ERR(ir->clk);
++      }
++
++      ret = clk_set_rate(ir->clk, SUNXI_IR_BASE_CLK);
++      if (ret) {
++              dev_err(dev, "set ir base clock failed!\n");
++              return ret;
++      }
++
++      if (clk_prepare_enable(ir->apb_clk)) {
++              dev_err(dev, "try to enable apb_ir_clk failed\n");
++              return -EINVAL;
++      }
++
++      if (clk_prepare_enable(ir->clk)) {
++              dev_err(dev, "try to enable ir_clk failed\n");
++              ret = -EINVAL;
++              goto exit_clkdisable_apb_clk;
++      }
++
++      /* IO */
++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      ir->base = devm_ioremap_resource(dev, res);
++      if (IS_ERR(ir->base)) {
++              dev_err(dev, "failed to map registers\n");
++              ret = PTR_ERR(ir->base);
++              goto exit_clkdisable_clk;
++      }
++
++      ir->rc = rc_allocate_device();
++      if (!ir->rc) {
++              dev_err(dev, "failed to allocate device\n");
++              ret = -ENOMEM;
++              goto exit_clkdisable_clk;
++      }
++
++      ir->rc->priv = ir;
++      ir->rc->input_name = SUNXI_IR_DEV;
++      ir->rc->input_phys = "sunxi-ir/input0";
++      ir->rc->input_id.bustype = BUS_HOST;
++      ir->rc->input_id.vendor = 0x0001;
++      ir->rc->input_id.product = 0x0001;
++      ir->rc->input_id.version = 0x0100;
++      ir->map_name = of_get_property(dn, "linux,rc-map-name", NULL);
++      ir->rc->map_name = ir->map_name ?: RC_MAP_EMPTY;
++      ir->rc->dev.parent = dev;
++      ir->rc->driver_type = RC_DRIVER_IR_RAW;
++      rc_set_allowed_protocols(ir->rc, RC_BIT_ALL);
++      ir->rc->rx_resolution = SUNXI_IR_SAMPLE;
++      ir->rc->timeout = MS_TO_NS(SUNXI_IR_TIMEOUT);
++      ir->rc->driver_name = SUNXI_IR_DEV;
++
++      ret = rc_register_device(ir->rc);
++      if (ret) {
++              dev_err(dev, "failed to register rc device\n");
++              goto exit_free_dev;
++      }
++
++      platform_set_drvdata(pdev, ir);
++
++      /* IRQ */
++      ir->irq = platform_get_irq(pdev, 0);
++      if (ir->irq < 0) {
++              dev_err(dev, "no irq resource\n");
++              ret = ir->irq;
++              goto exit_free_dev;
++      }
++
++      ret = devm_request_irq(dev, ir->irq, sunxi_ir_irq, 0, SUNXI_IR_DEV, ir);
++      if (ret) {
++              dev_err(dev, "failed request irq\n");
++              goto exit_free_dev;
++      }
++
++      /* Enable CIR Mode */
++      writel(REG_CTL_MD, ir->base+SUNXI_IR_CTL_REG);
++
++      /* Set noise threshold and idle threshold */
++      writel(REG_CIR_NTHR(SUNXI_IR_RXNOISE)|REG_CIR_ITHR(SUNXI_IR_RXIDLE),
++             ir->base + SUNXI_IR_CIR_REG);
++
++      /* Invert Input Signal */
++      writel(REG_RXCTL_RPPI, ir->base + SUNXI_IR_RXCTL_REG);
++
++      /* Clear All Rx Interrupt Status */
++      writel(REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG);
++
++      /*
++       * Enable IRQ on overflow, packet end, FIFO available with trigger
++       * level
++       */
++      writel(REG_RXINT_ROI_EN | REG_RXINT_RPEI_EN |
++             REG_RXINT_RAI_EN | REG_RXINT_RAL(TRIGGER_LEVEL - 1),
++             ir->base + SUNXI_IR_RXINT_REG);
++
++      /* Enable IR Module */
++      tmp = readl(ir->base + SUNXI_IR_CTL_REG);
++      writel(tmp | REG_CTL_GEN | REG_CTL_RXEN, ir->base + SUNXI_IR_CTL_REG);
++
++      dev_info(dev, "initialized sunXi IR driver\n");
++      return 0;
++
++exit_free_dev:
++      rc_free_device(ir->rc);
++exit_clkdisable_clk:
++      clk_disable_unprepare(ir->clk);
++exit_clkdisable_apb_clk:
++      clk_disable_unprepare(ir->apb_clk);
++
++      return ret;
++}
++
++static int sunxi_ir_remove(struct platform_device *pdev)
++{
++      unsigned long flags;
++      struct sunxi_ir *ir = platform_get_drvdata(pdev);
++
++      clk_disable_unprepare(ir->clk);
++      clk_disable_unprepare(ir->apb_clk);
++
++      spin_lock_irqsave(&ir->ir_lock, flags);
++      /* disable IR IRQ */
++      writel(0, ir->base + SUNXI_IR_RXINT_REG);
++      /* clear All Rx Interrupt Status */
++      writel(REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG);
++      /* disable IR */
++      writel(0, ir->base + SUNXI_IR_CTL_REG);
++      spin_unlock_irqrestore(&ir->ir_lock, flags);
++
++      rc_unregister_device(ir->rc);
++      return 0;
++}
++
++static const struct of_device_id sunxi_ir_match[] = {
++      { .compatible = "allwinner,sun7i-a20-ir", },
++      {},
++};
++
++static struct platform_driver sunxi_ir_driver = {
++      .probe          = sunxi_ir_probe,
++      .remove         = sunxi_ir_remove,
++      .driver = {
++              .name = SUNXI_IR_DEV,
++              .owner = THIS_MODULE,
++              .of_match_table = sunxi_ir_match,
++      },
++};
++
++module_platform_driver(sunxi_ir_driver);
++
++MODULE_DESCRIPTION("Allwinner sunXi IR controller driver");
++MODULE_AUTHOR("Alexsey Shestacov <wingrime@linux-sunxi.org>");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/sunxi/patches-3.14/281-dt-sun7i-add-ir-pins.patch b/target/linux/sunxi/patches-3.14/281-dt-sun7i-add-ir-pins.patch
new file mode 100644 (file)
index 0000000..58ca0ee
--- /dev/null
@@ -0,0 +1,38 @@
+From 7720fe5faadae243806833c7a7ec5d15bae244c4 Mon Sep 17 00:00:00 2001
+From: Alexander Bersenev <bay@hackerdom.ru>
+Date: Mon, 9 Jun 2014 00:08:11 +0600
+Subject: [PATCH] ARM: sunxi: Add pins for IR controller on A20 to dtsi
+
+This patch adds pins for two IR controllers on A20
+
+Signed-off-by: Alexander Bersenev <bay@hackerdom.ru>
+Signed-off-by: Alexsey Shestacov <wingrime@linux-sunxi.org>
+---
+ arch/arm/boot/dts/sun7i-a20.dtsi | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
+index e67e451..4e4d6ce 100644
+--- a/arch/arm/boot/dts/sun7i-a20.dtsi
++++ b/arch/arm/boot/dts/sun7i-a20.dtsi
+@@ -738,6 +738,20 @@
+                               allwinner,drive = <2>;
+                               allwinner,pull = <0>;
+                       };
++
++                      ir0_pins_a: ir0@0 {
++                                  allwinner,pins = "PB3","PB4";
++                                  allwinner,function = "ir0";
++                                  allwinner,drive = <0>;
++                                  allwinner,pull = <0>;
++                      };
++
++                      ir1_pins_a: ir1@0 {
++                                  allwinner,pins = "PB22","PB23";
++                                  allwinner,function = "ir1";
++                                  allwinner,drive = <0>;
++                                  allwinner,pull = <0>;
++                      };
+               };
+               timer@01c20c00 {
diff --git a/target/linux/sunxi/patches-3.14/282-dt-sun7i-add-ir-ctrlers.patch b/target/linux/sunxi/patches-3.14/282-dt-sun7i-add-ir-ctrlers.patch
new file mode 100644 (file)
index 0000000..4a544cd
--- /dev/null
@@ -0,0 +1,42 @@
+From 0af3258f87d590864187b0187c7aa7428801ef80 Mon Sep 17 00:00:00 2001
+From: Alexander Bersenev <bay@hackerdom.ru>
+Date: Mon, 9 Jun 2014 00:08:12 +0600
+Subject: [PATCH] ARM: sunxi: Add IR controllers on A20 to dtsi
+
+This patch adds records for two IR controllers on A20
+
+Signed-off-by: Alexander Bersenev <bay@hackerdom.ru>
+Signed-off-by: Alexsey Shestacov <wingrime@linux-sunxi.org>
+---
+ arch/arm/boot/dts/sun7i-a20.dtsi | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
+index 4e4d6ce..3050369 100644
+--- a/arch/arm/boot/dts/sun7i-a20.dtsi
++++ b/arch/arm/boot/dts/sun7i-a20.dtsi
+@@ -784,6 +784,24 @@
+                       #pwm-cells = <3>;
+               };
++              ir0: ir@01c21800 {
++                      compatible = "allwinner,sun7i-a20-ir";
++                      clocks = <&apb0_gates 6>, <&ir0_clk>;
++                      clock-names = "apb", "ir";
++                      interrupts = <0 5 4>;
++                      reg = <0x01c21800 0x40>;
++                      status = "disabled";
++              };
++
++              ir1: ir@01c21c00 {
++                      compatible = "allwinner,sun7i-a20-ir";
++                      clocks = <&apb0_gates 7>, <&ir1_clk>;
++                      clock-names = "apb", "ir";
++                      interrupts = <0 6 4>;
++                      reg = <0x01c21c00 0x40>;
++                      status = "disabled";
++              };
++
+               lradc: lradc@01c22800 {
+                       compatible = "allwinner,sun4i-lradc-keys";
+                       reg = <0x01c22800 0x100>;
diff --git a/target/linux/sunxi/patches-3.14/283-dt-sun7i-add-ir-to-cb2-cbt.patch b/target/linux/sunxi/patches-3.14/283-dt-sun7i-add-ir-to-cb2-cbt.patch
new file mode 100644 (file)
index 0000000..290b990
--- /dev/null
@@ -0,0 +1,51 @@
+From 05cffcda932b0914d78a08891c13e61593a6ce02 Mon Sep 17 00:00:00 2001
+From: Alexander Bersenev <bay@hackerdom.ru>
+Date: Mon, 9 Jun 2014 00:08:13 +0600
+Subject: [PATCH] ARM: sunxi: Enable IR controller on cubieboard 2 and
+ cubietruck in dts
+
+This patch enables two IR devices in dts:
+- One IR device physically found on Cubieboard 2
+- One IR device physically found on Cubietruck
+
+Signed-off-by: Alexander Bersenev <bay@hackerdom.ru>
+Signed-off-by: Alexsey Shestacov <wingrime@linux-sunxi.org>
+---
+ arch/arm/boot/dts/sun7i-a20-cubieboard2.dts | 6 ++++++
+ arch/arm/boot/dts/sun7i-a20-cubietruck.dts  | 6 ++++++
+ 2 files changed, 12 insertions(+)
+
+diff --git a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
+index 97bcb2a..81bf3df 100644
+--- a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
++++ b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
+@@ -66,6 +66,12 @@
+                       };
+               };
++              ir0: ir@01c21800 {
++                      pinctrl-names = "default";
++                      pinctrl-0 = <&ir0_pins_a>;
++                      status = "okay";
++              };
++
+               uart0: serial@01c28000 {
+                       pinctrl-names = "default";
+                       pinctrl-0 = <&uart0_pins_a>;
+diff --git a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
+index 624e0a5..3931986 100644
+--- a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
++++ b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
+@@ -114,6 +114,12 @@
+                       status = "okay";
+               };
++              ir0: ir@01c21800 {
++                      pinctrl-names = "default";
++                      pinctrl-0 = <&ir0_pins_a>;
++                      status = "okay";
++              };
++
+               uart0: serial@01c28000 {
+                       pinctrl-names = "default";
+                       pinctrl-0 = <&uart0_pins_a>;
diff --git a/target/linux/sunxi/patches-3.14/284-ir-backports-from-3.15.patch b/target/linux/sunxi/patches-3.14/284-ir-backports-from-3.15.patch
new file mode 100644 (file)
index 0000000..37ccffc
--- /dev/null
@@ -0,0 +1,1538 @@
+From 00942d1a1bd93ac108c1b92d504c568a37be1833 Mon Sep 17 00:00:00 2001
+From: James Hogan <james.hogan@imgtec.com>
+Date: Fri, 17 Jan 2014 10:58:49 -0300
+Subject: [PATCH] [media] media: rc: add sysfs scancode filtering interface
+
+Add and document a generic sysfs based scancode filtering interface for
+making use of IR data matching hardware to filter out uninteresting
+scancodes. Two filters exist, one for normal operation and one for
+filtering scancodes which are permitted to wake the system from suspend.
+
+The following files are added to /sys/class/rc/rc?/:
+ - filter: normal scancode filter value
+ - filter_mask: normal scancode filter mask
+ - wakeup_filter: wakeup scancode filter value
+ - wakeup_filter_mask: wakeup scancode filter mask
+
+A new s_filter() driver callback is added which must arrange for the
+specified filter to be applied at the right time. Drivers can convert
+the scancode filter into a raw IR data filter, which can be applied
+immediately or later (for wake up filters).
+
+Signed-off-by: James Hogan <james.hogan@imgtec.com>
+Cc: Mauro Carvalho Chehab <m.chehab@samsung.com>
+Cc: linux-media@vger.kernel.org
+Cc: Rob Landley <rob@landley.net>
+Cc: linux-doc@vger.kernel.org
+Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
+---
+ Documentation/ABI/testing/sysfs-class-rc |  58 +++++++++++++
+ drivers/media/rc/rc-main.c               | 136 +++++++++++++++++++++++++++++++
+ include/media/rc-core.h                  |  29 +++++++
+ 3 files changed, 223 insertions(+)
+
+diff --git a/Documentation/ABI/testing/sysfs-class-rc b/Documentation/ABI/testing/sysfs-class-rc
+index 52bc057..c0e1d14 100644
+diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
+index f1b67db..fa8b957 100644
+--- a/drivers/media/rc/rc-main.c
++++ b/drivers/media/rc/rc-main.c
+@@ -969,6 +969,130 @@ static ssize_t store_protocols(struct device *device,
+       return ret;
+ }
++/**
++ * struct rc_filter_attribute - Device attribute relating to a filter type.
++ * @attr:     Device attribute.
++ * @type:     Filter type.
++ * @mask:     false for filter value, true for filter mask.
++ */
++struct rc_filter_attribute {
++      struct device_attribute         attr;
++      enum rc_filter_type             type;
++      bool                            mask;
++};
++#define to_rc_filter_attr(a) container_of(a, struct rc_filter_attribute, attr)
++
++#define RC_FILTER_ATTR(_name, _mode, _show, _store, _type, _mask)     \
++      struct rc_filter_attribute dev_attr_##_name = {                 \
++              .attr = __ATTR(_name, _mode, _show, _store),            \
++              .type = (_type),                                        \
++              .mask = (_mask),                                        \
++      }
++
++/**
++ * show_filter() - shows the current scancode filter value or mask
++ * @device:   the device descriptor
++ * @attr:     the device attribute struct
++ * @buf:      a pointer to the output buffer
++ *
++ * This routine is a callback routine to read a scancode filter value or mask.
++ * It is trigged by reading /sys/class/rc/rc?/[wakeup_]filter[_mask].
++ * It prints the current scancode filter value or mask of the appropriate filter
++ * type in hexadecimal into @buf and returns the size of the buffer.
++ *
++ * Bits of the filter value corresponding to set bits in the filter mask are
++ * compared against input scancodes and non-matching scancodes are discarded.
++ *
++ * dev->lock is taken to guard against races between device registration,
++ * store_filter and show_filter.
++ */
++static ssize_t show_filter(struct device *device,
++                         struct device_attribute *attr,
++                         char *buf)
++{
++      struct rc_dev *dev = to_rc_dev(device);
++      struct rc_filter_attribute *fattr = to_rc_filter_attr(attr);
++      u32 val;
++
++      /* Device is being removed */
++      if (!dev)
++              return -EINVAL;
++
++      mutex_lock(&dev->lock);
++      if (!dev->s_filter)
++              val = 0;
++      else if (fattr->mask)
++              val = dev->scancode_filters[fattr->type].mask;
++      else
++              val = dev->scancode_filters[fattr->type].data;
++      mutex_unlock(&dev->lock);
++
++      return sprintf(buf, "%#x\n", val);
++}
++
++/**
++ * store_filter() - changes the scancode filter value
++ * @device:   the device descriptor
++ * @attr:     the device attribute struct
++ * @buf:      a pointer to the input buffer
++ * @len:      length of the input buffer
++ *
++ * This routine is for changing a scancode filter value or mask.
++ * It is trigged by writing to /sys/class/rc/rc?/[wakeup_]filter[_mask].
++ * Returns -EINVAL if an invalid filter value for the current protocol was
++ * specified or if scancode filtering is not supported by the driver, otherwise
++ * returns @len.
++ *
++ * Bits of the filter value corresponding to set bits in the filter mask are
++ * compared against input scancodes and non-matching scancodes are discarded.
++ *
++ * dev->lock is taken to guard against races between device registration,
++ * store_filter and show_filter.
++ */
++static ssize_t store_filter(struct device *device,
++                          struct device_attribute *attr,
++                          const char *buf,
++                          size_t count)
++{
++      struct rc_dev *dev = to_rc_dev(device);
++      struct rc_filter_attribute *fattr = to_rc_filter_attr(attr);
++      struct rc_scancode_filter local_filter, *filter;
++      int ret;
++      unsigned long val;
++
++      /* Device is being removed */
++      if (!dev)
++              return -EINVAL;
++
++      ret = kstrtoul(buf, 0, &val);
++      if (ret < 0)
++              return ret;
++
++      /* Scancode filter not supported (but still accept 0) */
++      if (!dev->s_filter)
++              return val ? -EINVAL : count;
++
++      mutex_lock(&dev->lock);
++
++      /* Tell the driver about the new filter */
++      filter = &dev->scancode_filters[fattr->type];
++      local_filter = *filter;
++      if (fattr->mask)
++              local_filter.mask = val;
++      else
++              local_filter.data = val;
++      ret = dev->s_filter(dev, fattr->type, &local_filter);
++      if (ret < 0)
++              goto unlock;
++
++      /* Success, commit the new filter */
++      *filter = local_filter;
++
++unlock:
++      mutex_unlock(&dev->lock);
++      return count;
++}
++
+ static void rc_dev_release(struct device *device)
+ {
+ }
+@@ -1000,9 +1124,21 @@ static int rc_dev_uevent(struct device *device, struct kobj_uevent_env *env)
+  */
+ static DEVICE_ATTR(protocols, S_IRUGO | S_IWUSR,
+                  show_protocols, store_protocols);
++static RC_FILTER_ATTR(filter, S_IRUGO|S_IWUSR,
++                    show_filter, store_filter, RC_FILTER_NORMAL, false);
++static RC_FILTER_ATTR(filter_mask, S_IRUGO|S_IWUSR,
++                    show_filter, store_filter, RC_FILTER_NORMAL, true);
++static RC_FILTER_ATTR(wakeup_filter, S_IRUGO|S_IWUSR,
++                    show_filter, store_filter, RC_FILTER_WAKEUP, false);
++static RC_FILTER_ATTR(wakeup_filter_mask, S_IRUGO|S_IWUSR,
++                    show_filter, store_filter, RC_FILTER_WAKEUP, true);
+ static struct attribute *rc_dev_attrs[] = {
+       &dev_attr_protocols.attr,
++      &dev_attr_filter.attr.attr,
++      &dev_attr_filter_mask.attr.attr,
++      &dev_attr_wakeup_filter.attr.attr,
++      &dev_attr_wakeup_filter_mask.attr.attr,
+       NULL,
+ };
+diff --git a/include/media/rc-core.h b/include/media/rc-core.h
+index 2f6f1f7..4a72176 100644
+--- a/include/media/rc-core.h
++++ b/include/media/rc-core.h
+@@ -35,6 +35,29 @@ enum rc_driver_type {
+ };
+ /**
++ * struct rc_scancode_filter - Filter scan codes.
++ * @data:     Scancode data to match.
++ * @mask:     Mask of bits of scancode to compare.
++ */
++struct rc_scancode_filter {
++      u32 data;
++      u32 mask;
++};
++
++/**
++ * enum rc_filter_type - Filter type constants.
++ * @RC_FILTER_NORMAL: Filter for normal operation.
++ * @RC_FILTER_WAKEUP: Filter for waking from suspend.
++ * @RC_FILTER_MAX:    Number of filter types.
++ */
++enum rc_filter_type {
++      RC_FILTER_NORMAL = 0,
++      RC_FILTER_WAKEUP,
++
++      RC_FILTER_MAX
++};
++
++/**
+  * struct rc_dev - represents a remote control device
+  * @dev: driver model's view of this device
+  * @input_name: name of the input child device
+@@ -70,6 +93,7 @@ enum rc_driver_type {
+  * @max_timeout: maximum timeout supported by device
+  * @rx_resolution : resolution (in ns) of input sampler
+  * @tx_resolution: resolution (in ns) of output sampler
++ * @scancode_filters: scancode filters (indexed by enum rc_filter_type)
+  * @change_protocol: allow changing the protocol used on hardware decoders
+  * @open: callback to allow drivers to enable polling/irq when IR input device
+  *    is opened.
+@@ -84,6 +108,7 @@ enum rc_driver_type {
+  *    device doesn't interrupt host until it sees IR pulses
+  * @s_learning_mode: enable wide band receiver used for learning
+  * @s_carrier_report: enable carrier reports
++ * @s_filter: set the scancode filter of a given type
+  */
+ struct rc_dev {
+       struct device                   dev;
+@@ -116,6 +141,7 @@ struct rc_dev {
+       u32                             max_timeout;
+       u32                             rx_resolution;
+       u32                             tx_resolution;
++      struct rc_scancode_filter       scancode_filters[RC_FILTER_MAX];
+       int                             (*change_protocol)(struct rc_dev *dev, u64 *rc_type);
+       int                             (*open)(struct rc_dev *dev);
+       void                            (*close)(struct rc_dev *dev);
+@@ -127,6 +153,9 @@ struct rc_dev {
+       void                            (*s_idle)(struct rc_dev *dev, bool enable);
+       int                             (*s_learning_mode)(struct rc_dev *dev, int enable);
+       int                             (*s_carrier_report) (struct rc_dev *dev, int enable);
++      int                             (*s_filter)(struct rc_dev *dev,
++                                                  enum rc_filter_type type,
++                                                  struct rc_scancode_filter *filter);
+ };
+ #define to_rc_dev(d) container_of(d, struct rc_dev, dev)
+From 7b802ce7e8c67510389fdbbe29edd87a75df3a93 Mon Sep 17 00:00:00 2001
+From: James Hogan <james.hogan@imgtec.com>
+Date: Mon, 10 Feb 2014 18:31:56 -0300
+Subject: [PATCH] [media] rc-main: store_filter: pass errors to userland
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Propagate errors returned by drivers from the s_filter callback back to
+userland when updating scancode filters. This allows userland to see
+when the filter couldn't be updated, usually because it's not a valid
+filter for the hardware.
+
+Previously the filter was being updated conditionally on success of
+s_filter, but the write always reported success back to userland.
+
+Reported-by: Antti Seppälä <a.seppala@gmail.com>
+Signed-off-by: James Hogan <james.hogan@imgtec.com>
+Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
+---
+ drivers/media/rc/rc-main.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
+index 2ec60f8..6448128 100644
+--- a/drivers/media/rc/rc-main.c
++++ b/drivers/media/rc/rc-main.c
+@@ -1090,7 +1090,7 @@ static ssize_t store_filter(struct device *device,
+ unlock:
+       mutex_unlock(&dev->lock);
+-      return count;
++      return (ret < 0) ? ret : count;
+ }
+ static void rc_dev_release(struct device *device)
+From b8c7d915087c97a21fa415fa0e860e59739da202 Mon Sep 17 00:00:00 2001
+From: James Hogan <james.hogan@imgtec.com>
+Date: Fri, 28 Feb 2014 20:17:02 -0300
+Subject: [PATCH] [media] rc-main: add generic scancode filtering
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add generic scancode filtering of RC input events, and fall back to
+permitting any RC_FILTER_NORMAL scancode filter to be set if no s_filter
+callback exists. This allows raw IR decoder events to be filtered, and
+potentially allows hardware decoders to set looser filters and rely on
+generic code to filter out the corner cases.
+
+Signed-off-by: James Hogan <james.hogan@imgtec.com>
+Reviewed-by: Antti Seppälä <a.seppala@gmail.com>
+Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
+---
+ drivers/media/rc/rc-main.c | 20 +++++++++++++-------
+ 1 file changed, 13 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
+index 6448128..0a4f680f 100644
+--- a/drivers/media/rc/rc-main.c
++++ b/drivers/media/rc/rc-main.c
+@@ -633,6 +633,7 @@ EXPORT_SYMBOL_GPL(rc_repeat);
+ static void ir_do_keydown(struct rc_dev *dev, int scancode,
+                         u32 keycode, u8 toggle)
+ {
++      struct rc_scancode_filter *filter;
+       bool new_event = !dev->keypressed ||
+                        dev->last_scancode != scancode ||
+                        dev->last_toggle != toggle;
+@@ -640,6 +641,11 @@ static void ir_do_keydown(struct rc_dev *dev, int scancode,
+       if (new_event && dev->keypressed)
+               ir_do_keyup(dev, false);
++      /* Generic scancode filtering */
++      filter = &dev->scancode_filters[RC_FILTER_NORMAL];
++      if (filter->mask && ((scancode ^ filter->data) & filter->mask))
++              return;
++
+       input_event(dev->input_dev, EV_MSC, MSC_SCAN, scancode);
+       if (new_event && keycode != KEY_RESERVED) {
+@@ -1019,9 +1025,7 @@ static ssize_t show_filter(struct device *device,
+               return -EINVAL;
+       mutex_lock(&dev->lock);
+-      if (!dev->s_filter)
+-              val = 0;
+-      else if (fattr->mask)
++      if (fattr->mask)
+               val = dev->scancode_filters[fattr->type].mask;
+       else
+               val = dev->scancode_filters[fattr->type].data;
+@@ -1069,7 +1073,7 @@ static ssize_t store_filter(struct device *device,
+               return ret;
+       /* Scancode filter not supported (but still accept 0) */
+-      if (!dev->s_filter)
++      if (!dev->s_filter && fattr->type != RC_FILTER_NORMAL)
+               return val ? -EINVAL : count;
+       mutex_lock(&dev->lock);
+@@ -1081,9 +1085,11 @@ static ssize_t store_filter(struct device *device,
+               local_filter.mask = val;
+       else
+               local_filter.data = val;
+-      ret = dev->s_filter(dev, fattr->type, &local_filter);
+-      if (ret < 0)
+-              goto unlock;
++      if (dev->s_filter) {
++              ret = dev->s_filter(dev, fattr->type, &local_filter);
++              if (ret < 0)
++                      goto unlock;
++      }
+       /* Success, commit the new filter */
+       *filter = local_filter;
+From 1a1934fab0c920f0d3bceeb60c9fe2dae8a56be9 Mon Sep 17 00:00:00 2001
+From: James Hogan <james.hogan@imgtec.com>
+Date: Fri, 28 Feb 2014 20:17:03 -0300
+Subject: [PATCH] [media] rc: abstract access to allowed/enabled protocols
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The allowed and enabled protocol masks need to be expanded to be per
+filter type in order to support wakeup filter protocol selection. To
+ease that process abstract access to the rc_dev::allowed_protos and
+rc_dev::enabled_protocols members with inline functions.
+
+Signed-off-by: James Hogan <james.hogan@imgtec.com>
+Reviewed-by: Antti Seppälä <a.seppala@gmail.com>
+Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
+---
+ drivers/hid/hid-picolcd_cir.c               |  2 +-
+ drivers/media/common/siano/smsir.c          |  2 +-
+ drivers/media/i2c/ir-kbd-i2c.c              |  4 ++--
+ drivers/media/pci/cx23885/cx23885-input.c   |  2 +-
+ drivers/media/pci/cx88/cx88-input.c         |  2 +-
+ drivers/media/rc/ati_remote.c               |  2 +-
+ drivers/media/rc/ene_ir.c                   |  2 +-
+ drivers/media/rc/fintek-cir.c               |  2 +-
+ drivers/media/rc/gpio-ir-recv.c             |  4 ++--
+ drivers/media/rc/iguanair.c                 |  2 +-
+ drivers/media/rc/imon.c                     |  7 ++++---
+ drivers/media/rc/ir-jvc-decoder.c           |  2 +-
+ drivers/media/rc/ir-lirc-codec.c            |  2 +-
+ drivers/media/rc/ir-mce_kbd-decoder.c       |  2 +-
+ drivers/media/rc/ir-nec-decoder.c           |  2 +-
+ drivers/media/rc/ir-raw.c                   |  2 +-
+ drivers/media/rc/ir-rc5-decoder.c           |  6 +++---
+ drivers/media/rc/ir-rc5-sz-decoder.c        |  2 +-
+ drivers/media/rc/ir-rc6-decoder.c           |  6 +++---
+ drivers/media/rc/ir-sanyo-decoder.c         |  2 +-
+ drivers/media/rc/ir-sharp-decoder.c         |  2 +-
+ drivers/media/rc/ir-sony-decoder.c          | 10 +++++-----
+ drivers/media/rc/ite-cir.c                  |  2 +-
+ drivers/media/rc/mceusb.c                   |  2 +-
+ drivers/media/rc/nuvoton-cir.c              |  2 +-
+ drivers/media/rc/rc-loopback.c              |  2 +-
+ drivers/media/rc/redrat3.c                  |  2 +-
+ drivers/media/rc/st_rc.c                    |  2 +-
+ drivers/media/rc/streamzap.c                |  2 +-
+ drivers/media/rc/ttusbir.c                  |  2 +-
+ drivers/media/rc/winbond-cir.c              |  2 +-
+ drivers/media/usb/dvb-usb-v2/dvb_usb_core.c |  2 +-
+ drivers/media/usb/dvb-usb/dvb-usb-remote.c  |  2 +-
+ drivers/media/usb/em28xx/em28xx-input.c     |  8 ++++----
+ drivers/media/usb/tm6000/tm6000-input.c     |  2 +-
+ include/media/rc-core.h                     | 22 ++++++++++++++++++++++
+ 36 files changed, 73 insertions(+), 50 deletions(-)
+
+diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c
+index 59d5eb1..cf1a9f1 100644
+--- a/drivers/hid/hid-picolcd_cir.c
++++ b/drivers/hid/hid-picolcd_cir.c
+@@ -114,7 +114,7 @@ int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report)
+       rdev->priv             = data;
+       rdev->driver_type      = RC_DRIVER_IR_RAW;
+-      rdev->allowed_protos   = RC_BIT_ALL;
++      rc_set_allowed_protocols(rdev, RC_BIT_ALL);
+       rdev->open             = picolcd_cir_open;
+       rdev->close            = picolcd_cir_close;
+       rdev->input_name       = data->hdev->name;
+diff --git a/drivers/media/common/siano/smsir.c b/drivers/media/common/siano/smsir.c
+index b8c5cad..6d7c0c8 100644
+--- a/drivers/media/common/siano/smsir.c
++++ b/drivers/media/common/siano/smsir.c
+@@ -88,7 +88,7 @@ int sms_ir_init(struct smscore_device_t *coredev)
+       dev->priv = coredev;
+       dev->driver_type = RC_DRIVER_IR_RAW;
+-      dev->allowed_protos = RC_BIT_ALL;
++      rc_set_allowed_protocols(dev, RC_BIT_ALL);
+       dev->map_name = sms_get_board(board_id)->rc_codes;
+       dev->driver_name = MODULE_NAME;
+diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c
+index 99ee456..c8fe135 100644
+--- a/drivers/media/i2c/ir-kbd-i2c.c
++++ b/drivers/media/i2c/ir-kbd-i2c.c
+@@ -431,8 +431,8 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
+        * Initialize the other fields of rc_dev
+        */
+       rc->map_name       = ir->ir_codes;
+-      rc->allowed_protos = rc_type;
+-      rc->enabled_protocols = rc_type;
++      rc_set_allowed_protocols(rc, rc_type);
++      rc_set_enabled_protocols(rc, rc_type);
+       if (!rc->driver_name)
+               rc->driver_name = MODULE_NAME;
+diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c
+index 8a49e7c..097d0a0 100644
+--- a/drivers/media/pci/cx23885/cx23885-input.c
++++ b/drivers/media/pci/cx23885/cx23885-input.c
+@@ -346,7 +346,7 @@ int cx23885_input_init(struct cx23885_dev *dev)
+       }
+       rc->dev.parent = &dev->pci->dev;
+       rc->driver_type = driver_type;
+-      rc->allowed_protos = allowed_protos;
++      rc_set_allowed_protocols(rc, allowed_protos);
+       rc->priv = kernel_ir;
+       rc->open = cx23885_input_ir_open;
+       rc->close = cx23885_input_ir_close;
+diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c
+index f29e18c..f991696 100644
+--- a/drivers/media/pci/cx88/cx88-input.c
++++ b/drivers/media/pci/cx88/cx88-input.c
+@@ -469,7 +469,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
+               dev->timeout = 10 * 1000 * 1000; /* 10 ms */
+       } else {
+               dev->driver_type = RC_DRIVER_SCANCODE;
+-              dev->allowed_protos = rc_type;
++              rc_set_allowed_protocols(dev, rc_type);
+       }
+       ir->core = core;
+diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c
+index 4d6a63f..2df7c55 100644
+--- a/drivers/media/rc/ati_remote.c
++++ b/drivers/media/rc/ati_remote.c
+@@ -784,7 +784,7 @@ static void ati_remote_rc_init(struct ati_remote *ati_remote)
+       rdev->priv = ati_remote;
+       rdev->driver_type = RC_DRIVER_SCANCODE;
+-      rdev->allowed_protos = RC_BIT_OTHER;
++      rc_set_allowed_protocols(rdev, RC_BIT_OTHER);
+       rdev->driver_name = "ati_remote";
+       rdev->open = ati_remote_rc_open;
+diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
+index c1444f8..fc9d23f 100644
+--- a/drivers/media/rc/ene_ir.c
++++ b/drivers/media/rc/ene_ir.c
+@@ -1059,7 +1059,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
+               learning_mode_force = false;
+       rdev->driver_type = RC_DRIVER_IR_RAW;
+-      rdev->allowed_protos = RC_BIT_ALL;
++      rc_set_allowed_protocols(rdev, RC_BIT_ALL);
+       rdev->priv = dev;
+       rdev->open = ene_open;
+       rdev->close = ene_close;
+diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
+index d6fa441..46b66e5 100644
+--- a/drivers/media/rc/fintek-cir.c
++++ b/drivers/media/rc/fintek-cir.c
+@@ -541,7 +541,7 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id
+       /* Set up the rc device */
+       rdev->priv = fintek;
+       rdev->driver_type = RC_DRIVER_IR_RAW;
+-      rdev->allowed_protos = RC_BIT_ALL;
++      rc_set_allowed_protocols(rdev, RC_BIT_ALL);
+       rdev->open = fintek_open;
+       rdev->close = fintek_close;
+       rdev->input_name = FINTEK_DESCRIPTION;
+diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
+index 80c611c..29b5f89 100644
+--- a/drivers/media/rc/gpio-ir-recv.c
++++ b/drivers/media/rc/gpio-ir-recv.c
+@@ -145,9 +145,9 @@ static int gpio_ir_recv_probe(struct platform_device *pdev)
+       rcdev->dev.parent = &pdev->dev;
+       rcdev->driver_name = GPIO_IR_DRIVER_NAME;
+       if (pdata->allowed_protos)
+-              rcdev->allowed_protos = pdata->allowed_protos;
++              rc_set_allowed_protocols(rcdev, pdata->allowed_protos);
+       else
+-              rcdev->allowed_protos = RC_BIT_ALL;
++              rc_set_allowed_protocols(rcdev, RC_BIT_ALL);
+       rcdev->map_name = pdata->map_name ?: RC_MAP_EMPTY;
+       gpio_dev->rcdev = rcdev;
+diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
+index a83519a..627ddfd 100644
+--- a/drivers/media/rc/iguanair.c
++++ b/drivers/media/rc/iguanair.c
+@@ -495,7 +495,7 @@ static int iguanair_probe(struct usb_interface *intf,
+       usb_to_input_id(ir->udev, &rc->input_id);
+       rc->dev.parent = &intf->dev;
+       rc->driver_type = RC_DRIVER_IR_RAW;
+-      rc->allowed_protos = RC_BIT_ALL;
++      rc_set_allowed_protocols(rc, RC_BIT_ALL);
+       rc->priv = ir;
+       rc->open = iguanair_open;
+       rc->close = iguanair_close;
+diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
+index 822b9f4..6f24e77 100644
+--- a/drivers/media/rc/imon.c
++++ b/drivers/media/rc/imon.c
+@@ -1017,7 +1017,7 @@ static int imon_ir_change_protocol(struct rc_dev *rc, u64 *rc_type)
+       unsigned char ir_proto_packet[] = {
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86 };
+-      if (*rc_type && !(*rc_type & rc->allowed_protos))
++      if (*rc_type && !rc_protocols_allowed(rc, *rc_type))
+               dev_warn(dev, "Looks like you're trying to use an IR protocol "
+                        "this device does not support\n");
+@@ -1867,7 +1867,8 @@ static struct rc_dev *imon_init_rdev(struct imon_context *ictx)
+       rdev->priv = ictx;
+       rdev->driver_type = RC_DRIVER_SCANCODE;
+-      rdev->allowed_protos = RC_BIT_OTHER | RC_BIT_RC6_MCE; /* iMON PAD or MCE */
++                                      /* iMON PAD or MCE */
++      rc_set_allowed_protocols(rdev, RC_BIT_OTHER | RC_BIT_RC6_MCE);
+       rdev->change_protocol = imon_ir_change_protocol;
+       rdev->driver_name = MOD_NAME;
+@@ -1880,7 +1881,7 @@ static struct rc_dev *imon_init_rdev(struct imon_context *ictx)
+       if (ictx->product == 0xffdc) {
+               imon_get_ffdc_type(ictx);
+-              rdev->allowed_protos = ictx->rc_type;
++              rc_set_allowed_protocols(rdev, ictx->rc_type);
+       }
+       imon_set_display_type(ictx);
+diff --git a/drivers/media/rc/ir-jvc-decoder.c b/drivers/media/rc/ir-jvc-decoder.c
+index 3948138..4ea62a1 100644
+--- a/drivers/media/rc/ir-jvc-decoder.c
++++ b/drivers/media/rc/ir-jvc-decoder.c
+@@ -47,7 +47,7 @@ static int ir_jvc_decode(struct rc_dev *dev, struct ir_raw_event ev)
+ {
+       struct jvc_dec *data = &dev->raw->jvc;
+-      if (!(dev->enabled_protocols & RC_BIT_JVC))
++      if (!rc_protocols_enabled(dev, RC_BIT_JVC))
+               return 0;
+       if (!is_timing_event(ev)) {
+diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c
+index ed2c8a1..d731da6 100644
+--- a/drivers/media/rc/ir-lirc-codec.c
++++ b/drivers/media/rc/ir-lirc-codec.c
+@@ -35,7 +35,7 @@ static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev)
+       struct lirc_codec *lirc = &dev->raw->lirc;
+       int sample;
+-      if (!(dev->enabled_protocols & RC_BIT_LIRC))
++      if (!rc_protocols_enabled(dev, RC_BIT_LIRC))
+               return 0;
+       if (!dev->raw->lirc.drv || !dev->raw->lirc.drv->rbuf)
+diff --git a/drivers/media/rc/ir-mce_kbd-decoder.c b/drivers/media/rc/ir-mce_kbd-decoder.c
+index 9f3c9b5..0c55f79 100644
+--- a/drivers/media/rc/ir-mce_kbd-decoder.c
++++ b/drivers/media/rc/ir-mce_kbd-decoder.c
+@@ -216,7 +216,7 @@ static int ir_mce_kbd_decode(struct rc_dev *dev, struct ir_raw_event ev)
+       u32 scancode;
+       unsigned long delay;
+-      if (!(dev->enabled_protocols & RC_BIT_MCE_KBD))
++      if (!rc_protocols_enabled(dev, RC_BIT_MCE_KBD))
+               return 0;
+       if (!is_timing_event(ev)) {
+diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c
+index e687a42..9de1791 100644
+--- a/drivers/media/rc/ir-nec-decoder.c
++++ b/drivers/media/rc/ir-nec-decoder.c
+@@ -52,7 +52,7 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev)
+       u8 address, not_address, command, not_command;
+       bool send_32bits = false;
+-      if (!(dev->enabled_protocols & RC_BIT_NEC))
++      if (!rc_protocols_enabled(dev, RC_BIT_NEC))
+               return 0;
+       if (!is_timing_event(ev)) {
+diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c
+index f0656fa..763c9d1 100644
+--- a/drivers/media/rc/ir-raw.c
++++ b/drivers/media/rc/ir-raw.c
+@@ -256,7 +256,7 @@ int ir_raw_event_register(struct rc_dev *dev)
+               return -ENOMEM;
+       dev->raw->dev = dev;
+-      dev->enabled_protocols = ~0;
++      rc_set_enabled_protocols(dev, ~0);
+       rc = kfifo_alloc(&dev->raw->kfifo,
+                        sizeof(struct ir_raw_event) * MAX_IR_EVENT_SIZE,
+                        GFP_KERNEL);
+diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c
+index 1085e17..4295d9b2 100644
+--- a/drivers/media/rc/ir-rc5-decoder.c
++++ b/drivers/media/rc/ir-rc5-decoder.c
+@@ -52,7 +52,7 @@ static int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev)
+       u8 toggle;
+       u32 scancode;
+-      if (!(dev->enabled_protocols & (RC_BIT_RC5 | RC_BIT_RC5X)))
++      if (!rc_protocols_enabled(dev, RC_BIT_RC5 | RC_BIT_RC5X))
+               return 0;
+       if (!is_timing_event(ev)) {
+@@ -128,7 +128,7 @@ static int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev)
+               if (data->wanted_bits == RC5X_NBITS) {
+                       /* RC5X */
+                       u8 xdata, command, system;
+-                      if (!(dev->enabled_protocols & RC_BIT_RC5X)) {
++                      if (!rc_protocols_enabled(dev, RC_BIT_RC5X)) {
+                               data->state = STATE_INACTIVE;
+                               return 0;
+                       }
+@@ -145,7 +145,7 @@ static int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev)
+               } else {
+                       /* RC5 */
+                       u8 command, system;
+-                      if (!(dev->enabled_protocols & RC_BIT_RC5)) {
++                      if (!rc_protocols_enabled(dev, RC_BIT_RC5)) {
+                               data->state = STATE_INACTIVE;
+                               return 0;
+                       }
+diff --git a/drivers/media/rc/ir-rc5-sz-decoder.c b/drivers/media/rc/ir-rc5-sz-decoder.c
+index 984e5b9..dc18b74 100644
+--- a/drivers/media/rc/ir-rc5-sz-decoder.c
++++ b/drivers/media/rc/ir-rc5-sz-decoder.c
+@@ -48,7 +48,7 @@ static int ir_rc5_sz_decode(struct rc_dev *dev, struct ir_raw_event ev)
+       u8 toggle, command, system;
+       u32 scancode;
+-      if (!(dev->enabled_protocols & RC_BIT_RC5_SZ))
++      if (!rc_protocols_enabled(dev, 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 7cba7d3..cfbd64e 100644
+--- a/drivers/media/rc/ir-rc6-decoder.c
++++ b/drivers/media/rc/ir-rc6-decoder.c
+@@ -89,9 +89,9 @@ static int ir_rc6_decode(struct rc_dev *dev, struct ir_raw_event ev)
+       u32 scancode;
+       u8 toggle;
+-      if (!(dev->enabled_protocols &
+-            (RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 |
+-             RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE)))
++      if (!rc_protocols_enabled(dev, RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 |
++                                RC_BIT_RC6_6A_24 | RC_BIT_RC6_6A_32 |
++                                RC_BIT_RC6_MCE))
+               return 0;
+       if (!is_timing_event(ev)) {
+diff --git a/drivers/media/rc/ir-sanyo-decoder.c b/drivers/media/rc/ir-sanyo-decoder.c
+index e1351ed..eb715f0 100644
+--- a/drivers/media/rc/ir-sanyo-decoder.c
++++ b/drivers/media/rc/ir-sanyo-decoder.c
+@@ -58,7 +58,7 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev)
+       u32 scancode;
+       u8 address, command, not_command;
+-      if (!(dev->enabled_protocols & RC_BIT_SANYO))
++      if (!rc_protocols_enabled(dev, RC_BIT_SANYO))
+               return 0;
+       if (!is_timing_event(ev)) {
+diff --git a/drivers/media/rc/ir-sharp-decoder.c b/drivers/media/rc/ir-sharp-decoder.c
+index 4895bc7..66d2039 100644
+diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c
+index 29ab9c2..599c19a 100644
+--- a/drivers/media/rc/ir-sony-decoder.c
++++ b/drivers/media/rc/ir-sony-decoder.c
+@@ -45,8 +45,8 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev)
+       u32 scancode;
+       u8 device, subdevice, function;
+-      if (!(dev->enabled_protocols &
+-            (RC_BIT_SONY12 | RC_BIT_SONY15 | RC_BIT_SONY20)))
++      if (!rc_protocols_enabled(dev, RC_BIT_SONY12 | RC_BIT_SONY15 |
++                                RC_BIT_SONY20))
+               return 0;
+       if (!is_timing_event(ev)) {
+@@ -124,7 +124,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev)
+               switch (data->count) {
+               case 12:
+-                      if (!(dev->enabled_protocols & RC_BIT_SONY12)) {
++                      if (!rc_protocols_enabled(dev, RC_BIT_SONY12)) {
+                               data->state = STATE_INACTIVE;
+                               return 0;
+                       }
+@@ -133,7 +133,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev)
+                       function  = bitrev8((data->bits >>  4) & 0xFE);
+                       break;
+               case 15:
+-                      if (!(dev->enabled_protocols & RC_BIT_SONY15)) {
++                      if (!rc_protocols_enabled(dev, RC_BIT_SONY15)) {
+                               data->state = STATE_INACTIVE;
+                               return 0;
+                       }
+@@ -142,7 +142,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev)
+                       function  = bitrev8((data->bits >>  7) & 0xFE);
+                       break;
+               case 20:
+-                      if (!(dev->enabled_protocols & RC_BIT_SONY20)) {
++                      if (!rc_protocols_enabled(dev, RC_BIT_SONY20)) {
+                               data->state = STATE_INACTIVE;
+                               return 0;
+                       }
+diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
+index 63b4225..ab24cc6 100644
+--- a/drivers/media/rc/ite-cir.c
++++ b/drivers/media/rc/ite-cir.c
+@@ -1563,7 +1563,7 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
+       /* set up ir-core props */
+       rdev->priv = itdev;
+       rdev->driver_type = RC_DRIVER_IR_RAW;
+-      rdev->allowed_protos = RC_BIT_ALL;
++      rc_set_allowed_protocols(rdev, RC_BIT_ALL);
+       rdev->open = ite_open;
+       rdev->close = ite_close;
+       rdev->s_idle = ite_s_idle;
+diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
+index c01b4c1..5d8f3d4 100644
+--- a/drivers/media/rc/mceusb.c
++++ b/drivers/media/rc/mceusb.c
+@@ -1211,7 +1211,7 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir)
+       rc->dev.parent = dev;
+       rc->priv = ir;
+       rc->driver_type = RC_DRIVER_IR_RAW;
+-      rc->allowed_protos = RC_BIT_ALL;
++      rc_set_allowed_protocols(rc, RC_BIT_ALL);
+       rc->timeout = MS_TO_NS(100);
+       if (!ir->flags.no_tx) {
+               rc->s_tx_mask = mceusb_set_tx_mask;
+diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c
+index b81325d..d244e1a 100644
+--- a/drivers/media/rc/nuvoton-cir.c
++++ b/drivers/media/rc/nuvoton-cir.c
+@@ -1044,7 +1044,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
+       /* Set up the rc device */
+       rdev->priv = nvt;
+       rdev->driver_type = RC_DRIVER_IR_RAW;
+-      rdev->allowed_protos = RC_BIT_ALL;
++      rc_set_allowed_protocols(rdev, RC_BIT_ALL);
+       rdev->open = nvt_open;
+       rdev->close = nvt_close;
+       rdev->tx_ir = nvt_tx_ir;
+diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c
+index 53d0282..0a88e0c 100644
+--- a/drivers/media/rc/rc-loopback.c
++++ b/drivers/media/rc/rc-loopback.c
+@@ -195,7 +195,7 @@ static int __init loop_init(void)
+       rc->map_name            = RC_MAP_EMPTY;
+       rc->priv                = &loopdev;
+       rc->driver_type         = RC_DRIVER_IR_RAW;
+-      rc->allowed_protos      = RC_BIT_ALL;
++      rc_set_allowed_protocols(rc, RC_BIT_ALL);
+       rc->timeout             = 100 * 1000 * 1000; /* 100 ms */
+       rc->min_timeout         = 1;
+       rc->max_timeout         = UINT_MAX;
+diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
+index a5d4f88..47cd373 100644
+--- a/drivers/media/rc/redrat3.c
++++ b/drivers/media/rc/redrat3.c
+@@ -922,7 +922,7 @@ static struct rc_dev *redrat3_init_rc_dev(struct redrat3_dev *rr3)
+       rc->dev.parent = dev;
+       rc->priv = rr3;
+       rc->driver_type = RC_DRIVER_IR_RAW;
+-      rc->allowed_protos = RC_BIT_ALL;
++      rc_set_allowed_protocols(rc, RC_BIT_ALL);
+       rc->timeout = US_TO_NS(2750);
+       rc->tx_ir = redrat3_transmit_ir;
+       rc->s_tx_carrier = redrat3_set_tx_carrier;
+diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c
+index 8f0cddb..22e4c1f 100644
+--- a/drivers/media/rc/st_rc.c
++++ b/drivers/media/rc/st_rc.c
+@@ -287,7 +287,7 @@ static int st_rc_probe(struct platform_device *pdev)
+       st_rc_hardware_init(rc_dev);
+       rdev->driver_type = RC_DRIVER_IR_RAW;
+-      rdev->allowed_protos = RC_BIT_ALL;
++      rc_set_allowed_protocols(rdev, RC_BIT_ALL);
+       /* rx sampling rate is 10Mhz */
+       rdev->rx_resolution = 100;
+       rdev->timeout = US_TO_NS(MAX_SYMB_TIME);
+diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c
+index d7b11e6..f4e0bc3 100644
+--- a/drivers/media/rc/streamzap.c
++++ b/drivers/media/rc/streamzap.c
+@@ -322,7 +322,7 @@ static struct rc_dev *streamzap_init_rc_dev(struct streamzap_ir *sz)
+       rdev->dev.parent = dev;
+       rdev->priv = sz;
+       rdev->driver_type = RC_DRIVER_IR_RAW;
+-      rdev->allowed_protos = RC_BIT_ALL;
++      rc_set_allowed_protocols(rdev, RC_BIT_ALL);
+       rdev->driver_name = DRIVER_NAME;
+       rdev->map_name = RC_MAP_STREAMZAP;
+diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c
+index d8de205..c5be38e 100644
+--- a/drivers/media/rc/ttusbir.c
++++ b/drivers/media/rc/ttusbir.c
+@@ -318,7 +318,7 @@ static int ttusbir_probe(struct usb_interface *intf,
+       usb_to_input_id(tt->udev, &rc->input_id);
+       rc->dev.parent = &intf->dev;
+       rc->driver_type = RC_DRIVER_IR_RAW;
+-      rc->allowed_protos = RC_BIT_ALL;
++      rc_set_allowed_protocols(rc, RC_BIT_ALL);
+       rc->priv = tt;
+       rc->driver_name = DRIVER_NAME;
+       rc->map_name = RC_MAP_TT_1500;
+diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c
+index 904baf4..a8b981f 100644
+--- a/drivers/media/rc/winbond-cir.c
++++ b/drivers/media/rc/winbond-cir.c
+@@ -1082,7 +1082,7 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
+       data->dev->dev.parent = &device->dev;
+       data->dev->timeout = MS_TO_NS(100);
+       data->dev->rx_resolution = US_TO_NS(2);
+-      data->dev->allowed_protos = RC_BIT_ALL;
++      rc_set_allowed_protocols(data->dev, RC_BIT_ALL);
+       err = rc_register_device(data->dev);
+       if (err)
+diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
+index 8a054d6..de02db8 100644
+--- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
++++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
+@@ -164,7 +164,7 @@ static int dvb_usbv2_remote_init(struct dvb_usb_device *d)
+       dev->driver_name = (char *) d->props->driver_name;
+       dev->map_name = d->rc.map_name;
+       dev->driver_type = d->rc.driver_type;
+-      dev->allowed_protos = d->rc.allowed_protos;
++      rc_set_allowed_protocols(dev, d->rc.allowed_protos);
+       dev->change_protocol = d->rc.change_protocol;
+       dev->priv = d;
+diff --git a/drivers/media/usb/dvb-usb/dvb-usb-remote.c b/drivers/media/usb/dvb-usb/dvb-usb-remote.c
+index 41bacff..4058aea 100644
+--- a/drivers/media/usb/dvb-usb/dvb-usb-remote.c
++++ b/drivers/media/usb/dvb-usb/dvb-usb-remote.c
+@@ -272,7 +272,7 @@ static int rc_core_dvb_usb_remote_init(struct dvb_usb_device *d)
+       dev->driver_name = d->props.rc.core.module_name;
+       dev->map_name = d->props.rc.core.rc_codes;
+       dev->change_protocol = d->props.rc.core.change_protocol;
+-      dev->allowed_protos = d->props.rc.core.allowed_protos;
++      rc_set_allowed_protocols(dev, d->props.rc.core.allowed_protos);
+       dev->driver_type = d->props.rc.core.driver_type;
+       usb_to_input_id(d->udev, &dev->input_id);
+       dev->input_name = "IR-receiver inside an USB DVB receiver";
+diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c
+index 2a9bf66..56ef49d 100644
+--- a/drivers/media/usb/em28xx/em28xx-input.c
++++ b/drivers/media/usb/em28xx/em28xx-input.c
+@@ -727,7 +727,7 @@ static int em28xx_ir_init(struct em28xx *dev)
+               case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2:
+                       rc->map_name = RC_MAP_HAUPPAUGE;
+                       ir->get_key_i2c = em28xx_get_key_em_haup;
+-                      rc->allowed_protos = RC_BIT_RC5;
++                      rc_set_allowed_protocols(rc, RC_BIT_RC5);
+                       break;
+               case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE:
+                       rc->map_name = RC_MAP_WINFAST_USBII_DELUXE;
+@@ -743,7 +743,7 @@ static int em28xx_ir_init(struct em28xx *dev)
+               switch (dev->chip_id) {
+               case CHIP_ID_EM2860:
+               case CHIP_ID_EM2883:
+-                      rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC;
++                      rc_set_allowed_protocols(rc, RC_BIT_RC5 | RC_BIT_NEC);
+                       ir->get_key = default_polling_getkey;
+                       break;
+               case CHIP_ID_EM2884:
+@@ -751,8 +751,8 @@ static int em28xx_ir_init(struct em28xx *dev)
+               case CHIP_ID_EM28174:
+               case CHIP_ID_EM28178:
+                       ir->get_key = em2874_polling_getkey;
+-                      rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC |
+-                                           RC_BIT_RC6_0;
++                      rc_set_allowed_protocols(rc, RC_BIT_RC5 | RC_BIT_NEC |
++                                               RC_BIT_RC6_0);
+                       break;
+               default:
+                       err = -ENODEV;
+diff --git a/drivers/media/usb/tm6000/tm6000-input.c b/drivers/media/usb/tm6000/tm6000-input.c
+index 8a6bbf1..d1af543 100644
+--- a/drivers/media/usb/tm6000/tm6000-input.c
++++ b/drivers/media/usb/tm6000/tm6000-input.c
+@@ -422,7 +422,7 @@ int tm6000_ir_init(struct tm6000_core *dev)
+       ir->rc = rc;
+       /* input setup */
+-      rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC;
++      rc_set_allowed_protocols(rc, RC_BIT_RC5 | RC_BIT_NEC);
+       /* Neded, in order to support NEC remotes with 24 or 32 bits */
+       rc->scanmask = 0xffff;
+       rc->priv = ir;
+diff --git a/include/media/rc-core.h b/include/media/rc-core.h
+index 5e7197e..6f3c3d9 100644
+--- a/include/media/rc-core.h
++++ b/include/media/rc-core.h
+@@ -160,6 +160,28 @@ struct rc_dev {
+ #define to_rc_dev(d) container_of(d, struct rc_dev, dev)
++static inline bool rc_protocols_allowed(struct rc_dev *rdev, u64 protos)
++{
++      return rdev->allowed_protos & protos;
++}
++
++/* should be called prior to registration or with mutex held */
++static inline void rc_set_allowed_protocols(struct rc_dev *rdev, u64 protos)
++{
++      rdev->allowed_protos = protos;
++}
++
++static inline bool rc_protocols_enabled(struct rc_dev *rdev, u64 protos)
++{
++      return rdev->enabled_protocols & protos;
++}
++
++/* should be called prior to registration or with mutex held */
++static inline void rc_set_enabled_protocols(struct rc_dev *rdev, u64 protos)
++{
++      rdev->enabled_protocols = protos;
++}
++
+ /*
+  * From rc-main.c
+  * Those functions can be used on any type of Remote Controller. They
+From acff5f24732acc8a55d0a0f0ee1d19442267df63 Mon Sep 17 00:00:00 2001
+From: James Hogan <james.hogan@imgtec.com>
+Date: Fri, 28 Feb 2014 20:17:04 -0300
+Subject: [PATCH] [media] rc: add allowed/enabled wakeup protocol masks
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Only a single allowed and enabled protocol mask currently exists in
+struct rc_dev, however to support a separate wakeup filter protocol two
+of each are needed, ideally as an array.
+
+Therefore make both rc_dev::allowed_protos and rc_dev::enabled_protocols
+arrays, update all users to reference the first element
+(RC_FILTER_NORMAL), and add a couple more helper functions for drivers
+to use for setting the allowed and enabled wakeup protocols.
+
+We also rename allowed_protos to allowed_protocols while we're at it,
+which is more consistent with enabled_protocols.
+
+Signed-off-by: James Hogan <james.hogan@imgtec.com>
+Reviewed-by: Antti Seppälä <a.seppala@gmail.com>
+Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
+---
+ drivers/media/rc/rc-main.c | 10 +++++-----
+ include/media/rc-core.h    | 32 ++++++++++++++++++++++++--------
+ 2 files changed, 29 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
+index 0a4f680f..309d791 100644
+--- a/drivers/media/rc/rc-main.c
++++ b/drivers/media/rc/rc-main.c
+@@ -830,9 +830,9 @@ static ssize_t show_protocols(struct device *device,
+       mutex_lock(&dev->lock);
+-      enabled = dev->enabled_protocols;
++      enabled = dev->enabled_protocols[RC_FILTER_NORMAL];
+       if (dev->driver_type == RC_DRIVER_SCANCODE)
+-              allowed = dev->allowed_protos;
++              allowed = dev->allowed_protocols[RC_FILTER_NORMAL];
+       else if (dev->raw)
+               allowed = ir_raw_get_allowed_protocols();
+       else {
+@@ -906,7 +906,7 @@ static ssize_t store_protocols(struct device *device,
+               ret = -EINVAL;
+               goto out;
+       }
+-      type = dev->enabled_protocols;
++      type = dev->enabled_protocols[RC_FILTER_NORMAL];
+       while ((tmp = strsep((char **) &data, " \n")) != NULL) {
+               if (!*tmp)
+@@ -964,7 +964,7 @@ static ssize_t store_protocols(struct device *device,
+               }
+       }
+-      dev->enabled_protocols = type;
++      dev->enabled_protocols[RC_FILTER_NORMAL] = type;
+       IR_dprintk(1, "Current protocol(s): 0x%llx\n",
+                  (long long)type);
+@@ -1316,7 +1316,7 @@ int rc_register_device(struct rc_dev *dev)
+               rc = dev->change_protocol(dev, &rc_type);
+               if (rc < 0)
+                       goto out_raw;
+-              dev->enabled_protocols = rc_type;
++              dev->enabled_protocols[RC_FILTER_NORMAL] = rc_type;
+       }
+       mutex_unlock(&dev->lock);
+diff --git a/include/media/rc-core.h b/include/media/rc-core.h
+index 6f3c3d9..f165115 100644
+--- a/include/media/rc-core.h
++++ b/include/media/rc-core.h
+@@ -73,8 +73,10 @@ enum rc_filter_type {
+  * @input_dev: the input child device used to communicate events to userspace
+  * @driver_type: specifies if protocol decoding is done in hardware or software
+  * @idle: used to keep track of RX state
+- * @allowed_protos: bitmask with the supported RC_BIT_* protocols
+- * @enabled_protocols: bitmask with the enabled RC_BIT_* protocols
++ * @allowed_protocols: bitmask with the supported RC_BIT_* protocols for each
++ *    filter type
++ * @enabled_protocols: bitmask with the enabled RC_BIT_* protocols for each
++ *    filter type
+  * @scanmask: some hardware decoders are not capable of providing the full
+  *    scancode to the application. As this is a hardware limit, we can't do
+  *    anything with it. Yet, as the same keycode table can be used with other
+@@ -124,8 +126,8 @@ struct rc_dev {
+       struct input_dev                *input_dev;
+       enum rc_driver_type             driver_type;
+       bool                            idle;
+-      u64                             allowed_protos;
+-      u64                             enabled_protocols;
++      u64                             allowed_protocols[RC_FILTER_MAX];
++      u64                             enabled_protocols[RC_FILTER_MAX];
+       u32                             users;
+       u32                             scanmask;
+       void                            *priv;
+@@ -162,24 +164,38 @@ struct rc_dev {
+ static inline bool rc_protocols_allowed(struct rc_dev *rdev, u64 protos)
+ {
+-      return rdev->allowed_protos & protos;
++      return rdev->allowed_protocols[RC_FILTER_NORMAL] & protos;
+ }
+ /* should be called prior to registration or with mutex held */
+ static inline void rc_set_allowed_protocols(struct rc_dev *rdev, u64 protos)
+ {
+-      rdev->allowed_protos = protos;
++      rdev->allowed_protocols[RC_FILTER_NORMAL] = protos;
+ }
+ static inline bool rc_protocols_enabled(struct rc_dev *rdev, u64 protos)
+ {
+-      return rdev->enabled_protocols & protos;
++      return rdev->enabled_protocols[RC_FILTER_NORMAL] & protos;
+ }
+ /* should be called prior to registration or with mutex held */
+ static inline void rc_set_enabled_protocols(struct rc_dev *rdev, u64 protos)
+ {
+-      rdev->enabled_protocols = protos;
++      rdev->enabled_protocols[RC_FILTER_NORMAL] = protos;
++}
++
++/* should be called prior to registration or with mutex held */
++static inline void rc_set_allowed_wakeup_protocols(struct rc_dev *rdev,
++                                                 u64 protos)
++{
++      rdev->allowed_protocols[RC_FILTER_WAKEUP] = protos;
++}
++
++/* should be called prior to registration or with mutex held */
++static inline void rc_set_enabled_wakeup_protocols(struct rc_dev *rdev,
++                                                 u64 protos)
++{
++      rdev->enabled_protocols[RC_FILTER_WAKEUP] = protos;
+ }
+ /*
+From ab88c66deace78989aa71cb139284cf7fb227ba4 Mon Sep 17 00:00:00 2001
+From: James Hogan <james.hogan@imgtec.com>
+Date: Fri, 28 Feb 2014 20:17:05 -0300
+Subject: [PATCH] [media] rc: add wakeup_protocols sysfs file
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add a wakeup_protocols sysfs file which controls the new
+rc_dev::enabled_protocols[RC_FILTER_WAKEUP], which is the mask of
+protocols that are used for the wakeup filter.
+
+A new RC driver callback change_wakeup_protocol() is called to change
+the wakeup protocol mask.
+
+Signed-off-by: James Hogan <james.hogan@imgtec.com>
+Reviewed-by: Antti Seppälä <a.seppala@gmail.com>
+Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
+---
+ Documentation/ABI/testing/sysfs-class-rc           | 23 +++++-
+ .../DocBook/media/v4l/remote_controllers.xml       | 20 +++++-
+ drivers/media/rc/rc-main.c                         | 82 +++++++++++++---------
+ include/media/rc-core.h                            |  3 +
+ 4 files changed, 90 insertions(+), 38 deletions(-)
+
+diff --git a/Documentation/ABI/testing/sysfs-class-rc b/Documentation/ABI/testing/sysfs-class-rc
+index c0e1d14..b65674d 100644
+diff --git a/Documentation/DocBook/media/v4l/remote_controllers.xml b/Documentation/DocBook/media/v4l/remote_controllers.xml
+index c440a81..5124a6c 100644
+diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
+index 309d791..e6e3ec7 100644
+--- a/drivers/media/rc/rc-main.c
++++ b/drivers/media/rc/rc-main.c
+@@ -803,13 +803,38 @@ static struct {
+ };
+ /**
+- * show_protocols() - shows the current IR protocol(s)
++ * struct rc_filter_attribute - Device attribute relating to a filter type.
++ * @attr:     Device attribute.
++ * @type:     Filter type.
++ * @mask:     false for filter value, true for filter mask.
++ */
++struct rc_filter_attribute {
++      struct device_attribute         attr;
++      enum rc_filter_type             type;
++      bool                            mask;
++};
++#define to_rc_filter_attr(a) container_of(a, struct rc_filter_attribute, attr)
++
++#define RC_PROTO_ATTR(_name, _mode, _show, _store, _type)             \
++      struct rc_filter_attribute dev_attr_##_name = {                 \
++              .attr = __ATTR(_name, _mode, _show, _store),            \
++              .type = (_type),                                        \
++      }
++#define RC_FILTER_ATTR(_name, _mode, _show, _store, _type, _mask)     \
++      struct rc_filter_attribute dev_attr_##_name = {                 \
++              .attr = __ATTR(_name, _mode, _show, _store),            \
++              .type = (_type),                                        \
++              .mask = (_mask),                                        \
++      }
++
++/**
++ * show_protocols() - shows the current/wakeup IR protocol(s)
+  * @device:   the device descriptor
+  * @mattr:    the device attribute struct (unused)
+  * @buf:      a pointer to the output buffer
+  *
+  * This routine is a callback routine for input read the IR protocol type(s).
+- * it is trigged by reading /sys/class/rc/rc?/protocols.
++ * it is trigged by reading /sys/class/rc/rc?/[wakeup_]protocols.
+  * It returns the protocol names of supported protocols.
+  * Enabled protocols are printed in brackets.
+  *
+@@ -820,6 +845,7 @@ static ssize_t show_protocols(struct device *device,
+                             struct device_attribute *mattr, char *buf)
+ {
+       struct rc_dev *dev = to_rc_dev(device);
++      struct rc_filter_attribute *fattr = to_rc_filter_attr(mattr);
+       u64 allowed, enabled;
+       char *tmp = buf;
+       int i;
+@@ -830,9 +856,10 @@ static ssize_t show_protocols(struct device *device,
+       mutex_lock(&dev->lock);
+-      enabled = dev->enabled_protocols[RC_FILTER_NORMAL];
+-      if (dev->driver_type == RC_DRIVER_SCANCODE)
+-              allowed = dev->allowed_protocols[RC_FILTER_NORMAL];
++      enabled = dev->enabled_protocols[fattr->type];
++      if (dev->driver_type == RC_DRIVER_SCANCODE ||
++          fattr->type == RC_FILTER_WAKEUP)
++              allowed = dev->allowed_protocols[fattr->type];
+       else if (dev->raw)
+               allowed = ir_raw_get_allowed_protocols();
+       else {
+@@ -864,14 +891,14 @@ static ssize_t show_protocols(struct device *device,
+ }
+ /**
+- * store_protocols() - changes the current IR protocol(s)
++ * store_protocols() - changes the current/wakeup IR protocol(s)
+  * @device:   the device descriptor
+  * @mattr:    the device attribute struct (unused)
+  * @buf:      a pointer to the input buffer
+  * @len:      length of the input buffer
+  *
+  * This routine is for changing the IR protocol type.
+- * It is trigged by writing to /sys/class/rc/rc?/protocols.
++ * It is trigged by writing to /sys/class/rc/rc?/[wakeup_]protocols.
+  * Writing "+proto" will add a protocol to the list of enabled protocols.
+  * Writing "-proto" will remove a protocol from the list of enabled protocols.
+  * Writing "proto" will enable only "proto".
+@@ -888,12 +915,14 @@ static ssize_t store_protocols(struct device *device,
+                              size_t len)
+ {
+       struct rc_dev *dev = to_rc_dev(device);
++      struct rc_filter_attribute *fattr = to_rc_filter_attr(mattr);
+       bool enable, disable;
+       const char *tmp;
+       u64 type;
+       u64 mask;
+       int rc, i, count = 0;
+       ssize_t ret;
++      int (*change_protocol)(struct rc_dev *dev, u64 *rc_type);
+       /* Device is being removed */
+       if (!dev)
+@@ -906,7 +935,7 @@ static ssize_t store_protocols(struct device *device,
+               ret = -EINVAL;
+               goto out;
+       }
+-      type = dev->enabled_protocols[RC_FILTER_NORMAL];
++      type = dev->enabled_protocols[fattr->type];
+       while ((tmp = strsep((char **) &data, " \n")) != NULL) {
+               if (!*tmp)
+@@ -954,8 +983,10 @@ static ssize_t store_protocols(struct device *device,
+               goto out;
+       }
+-      if (dev->change_protocol) {
+-              rc = dev->change_protocol(dev, &type);
++      change_protocol = (fattr->type == RC_FILTER_NORMAL)
++              ? dev->change_protocol : dev->change_wakeup_protocol;
++      if (change_protocol) {
++              rc = change_protocol(dev, &type);
+               if (rc < 0) {
+                       IR_dprintk(1, "Error setting protocols to 0x%llx\n",
+                                  (long long)type);
+@@ -964,7 +995,7 @@ static ssize_t store_protocols(struct device *device,
+               }
+       }
+-      dev->enabled_protocols[RC_FILTER_NORMAL] = type;
++      dev->enabled_protocols[fattr->type] = type;
+       IR_dprintk(1, "Current protocol(s): 0x%llx\n",
+                  (long long)type);
+@@ -976,26 +1007,6 @@ static ssize_t store_protocols(struct device *device,
+ }
+ /**
+- * struct rc_filter_attribute - Device attribute relating to a filter type.
+- * @attr:     Device attribute.
+- * @type:     Filter type.
+- * @mask:     false for filter value, true for filter mask.
+- */
+-struct rc_filter_attribute {
+-      struct device_attribute         attr;
+-      enum rc_filter_type             type;
+-      bool                            mask;
+-};
+-#define to_rc_filter_attr(a) container_of(a, struct rc_filter_attribute, attr)
+-
+-#define RC_FILTER_ATTR(_name, _mode, _show, _store, _type, _mask)     \
+-      struct rc_filter_attribute dev_attr_##_name = {                 \
+-              .attr = __ATTR(_name, _mode, _show, _store),            \
+-              .type = (_type),                                        \
+-              .mask = (_mask),                                        \
+-      }
+-
+-/**
+  * show_filter() - shows the current scancode filter value or mask
+  * @device:   the device descriptor
+  * @attr:     the device attribute struct
+@@ -1128,8 +1139,10 @@ static int rc_dev_uevent(struct device *device, struct kobj_uevent_env *env)
+ /*
+  * Static device attribute struct with the sysfs attributes for IR's
+  */
+-static DEVICE_ATTR(protocols, S_IRUGO | S_IWUSR,
+-                 show_protocols, store_protocols);
++static RC_PROTO_ATTR(protocols, S_IRUGO | S_IWUSR,
++                   show_protocols, store_protocols, RC_FILTER_NORMAL);
++static RC_PROTO_ATTR(wakeup_protocols, S_IRUGO | S_IWUSR,
++                   show_protocols, store_protocols, RC_FILTER_WAKEUP);
+ static RC_FILTER_ATTR(filter, S_IRUGO|S_IWUSR,
+                     show_filter, store_filter, RC_FILTER_NORMAL, false);
+ static RC_FILTER_ATTR(filter_mask, S_IRUGO|S_IWUSR,
+@@ -1140,7 +1153,8 @@ static RC_FILTER_ATTR(wakeup_filter_mask, S_IRUGO|S_IWUSR,
+                     show_filter, store_filter, RC_FILTER_WAKEUP, true);
+ static struct attribute *rc_dev_attrs[] = {
+-      &dev_attr_protocols.attr,
++      &dev_attr_protocols.attr.attr,
++      &dev_attr_wakeup_protocols.attr.attr,
+       &dev_attr_filter.attr.attr,
+       &dev_attr_filter_mask.attr.attr,
+       &dev_attr_wakeup_filter.attr.attr,
+diff --git a/include/media/rc-core.h b/include/media/rc-core.h
+index f165115..0b9f890 100644
+--- a/include/media/rc-core.h
++++ b/include/media/rc-core.h
+@@ -97,6 +97,8 @@ enum rc_filter_type {
+  * @tx_resolution: resolution (in ns) of output sampler
+  * @scancode_filters: scancode filters (indexed by enum rc_filter_type)
+  * @change_protocol: allow changing the protocol used on hardware decoders
++ * @change_wakeup_protocol: allow changing the protocol used for wakeup
++ *    filtering
+  * @open: callback to allow drivers to enable polling/irq when IR input device
+  *    is opened.
+  * @close: callback to allow drivers to disable polling/irq when IR input device
+@@ -145,6 +147,7 @@ struct rc_dev {
+       u32                             tx_resolution;
+       struct rc_scancode_filter       scancode_filters[RC_FILTER_MAX];
+       int                             (*change_protocol)(struct rc_dev *dev, u64 *rc_type);
++      int                             (*change_wakeup_protocol)(struct rc_dev *dev, u64 *rc_type);
+       int                             (*open)(struct rc_dev *dev);
+       void                            (*close)(struct rc_dev *dev);
+       int                             (*s_tx_mask)(struct rc_dev *dev, u32 mask);
+From 6bea25af147fcddcd8fd4557f4184c847c5c6ffd Mon Sep 17 00:00:00 2001
+From: James Hogan <james.hogan@imgtec.com>
+Date: Fri, 28 Feb 2014 20:17:06 -0300
+Subject: [PATCH] [media] rc-main: automatically refresh filter on protocol
+ change
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+When either of the normal or wakeup filter protocols are changed,
+refresh the corresponding scancode filter, i.e. try and set the same
+scancode filter with the new protocol. If that fails clear the filter
+instead.
+
+If no protocol was selected the filter is just cleared, and if no
+s_filter callback exists the filter is left unmodified.
+
+Similarly clear the filter mask when the filter is set if no protocol is
+currently selected.
+
+This simplifies driver code which no longer has to explicitly worry
+about modifying the filter on a protocol change. This also allows the
+change_wakeup_protocol callback to be omitted entirely if there is only
+a single available wakeup protocol at a time, since selecting no
+protocol will automatically clear the wakeup filter, disabling wakeup.
+
+Signed-off-by: James Hogan <james.hogan@imgtec.com>
+Reviewed-by: Antti Seppälä <a.seppala@gmail.com>
+Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
+---
+ drivers/media/rc/rc-main.c | 41 +++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 39 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
+index e6e3ec7..b1a6900 100644
+--- a/drivers/media/rc/rc-main.c
++++ b/drivers/media/rc/rc-main.c
+@@ -918,11 +918,12 @@ static ssize_t store_protocols(struct device *device,
+       struct rc_filter_attribute *fattr = to_rc_filter_attr(mattr);
+       bool enable, disable;
+       const char *tmp;
+-      u64 type;
++      u64 old_type, type;
+       u64 mask;
+       int rc, i, count = 0;
+       ssize_t ret;
+       int (*change_protocol)(struct rc_dev *dev, u64 *rc_type);
++      struct rc_scancode_filter local_filter, *filter;
+       /* Device is being removed */
+       if (!dev)
+@@ -935,7 +936,8 @@ static ssize_t store_protocols(struct device *device,
+               ret = -EINVAL;
+               goto out;
+       }
+-      type = dev->enabled_protocols[fattr->type];
++      old_type = dev->enabled_protocols[fattr->type];
++      type = old_type;
+       while ((tmp = strsep((char **) &data, " \n")) != NULL) {
+               if (!*tmp)
+@@ -999,6 +1001,36 @@ static ssize_t store_protocols(struct device *device,
+       IR_dprintk(1, "Current protocol(s): 0x%llx\n",
+                  (long long)type);
++      /*
++       * If the protocol is changed the filter needs updating.
++       * Try setting the same filter with the new protocol (if any).
++       * Fall back to clearing the filter.
++       */
++      filter = &dev->scancode_filters[fattr->type];
++      if (old_type != type && filter->mask) {
++              local_filter = *filter;
++              if (!type) {
++                      /* no protocol => clear filter */
++                      ret = -1;
++              } else if (!dev->s_filter) {
++                      /* generic filtering => accept any filter */
++                      ret = 0;
++              } else {
++                      /* hardware filtering => try setting, otherwise clear */
++                      ret = dev->s_filter(dev, fattr->type, &local_filter);
++              }
++              if (ret < 0) {
++                      /* clear the filter */
++                      local_filter.data = 0;
++                      local_filter.mask = 0;
++                      if (dev->s_filter)
++                              dev->s_filter(dev, fattr->type, &local_filter);
++              }
++
++              /* commit the new filter */
++              *filter = local_filter;
++      }
++
+       ret = len;
+ out:
+@@ -1096,6 +1128,11 @@ static ssize_t store_filter(struct device *device,
+               local_filter.mask = val;
+       else
+               local_filter.data = val;
++      if (!dev->enabled_protocols[fattr->type] && local_filter.mask) {
++              /* refuse to set a filter unless a protocol is enabled */
++              ret = -EINVAL;
++              goto unlock;
++      }
+       if (dev->s_filter) {
+               ret = dev->s_filter(dev, fattr->type, &local_filter);
+               if (ret < 0)
+From 262912335c823a2bbcc87003ee55d62cc27f4e48 Mon Sep 17 00:00:00 2001
+From: James Hogan <james.hogan@imgtec.com>
+Date: Sat, 1 Mar 2014 19:52:25 -0300
+Subject: [PATCH] [media] rc-main: fix missing unlock if no devno left
+
+While playing with make coccicheck I noticed this message:
+drivers/media/rc/rc-main.c:1245:3-9: preceding lock on line 1238
+
+It was introduced by commit 587d1b06e07b ([media] rc-core: reuse device
+numbers) which returns -ENOMEM after a mutex_lock without first
+unlocking it when there are no more device numbers left. The added code
+doesn't depend on the device lock, so move it before the lock is taken.
+
+Signed-off-by: James Hogan <james.hogan@imgtec.com>
+Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
+---
+ drivers/media/rc/rc-main.c | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
+index b1a6900..f87e0f0 100644
+--- a/drivers/media/rc/rc-main.c
++++ b/drivers/media/rc/rc-main.c
+@@ -1286,14 +1286,6 @@ int rc_register_device(struct rc_dev *dev)
+       if (dev->close)
+               dev->input_dev->close = ir_close;
+-      /*
+-       * 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);
+-
+       do {
+               devno = find_first_zero_bit(ir_core_dev_number,
+                                           IRRCV_NUM_DEVICES);
+@@ -1302,6 +1294,14 @@ int rc_register_device(struct rc_dev *dev)
+                       return -ENOMEM;
+       } while (test_and_set_bit(devno, ir_core_dev_number));
++      /*
++       * 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);
++
+       dev->devno = devno;
+       dev_set_name(&dev->dev, "rc%ld", dev->devno);
+       dev_set_drvdata(&dev->dev, dev);
diff --git a/target/linux/sunxi/patches-3.14/285-dt-sun7i-add-ir-to-bananapi.patch b/target/linux/sunxi/patches-3.14/285-dt-sun7i-add-ir-to-bananapi.patch
new file mode 100644 (file)
index 0000000..b075799
--- /dev/null
@@ -0,0 +1,34 @@
+From 05cffcda932b0914d78a08891c13e61593a6ce02 Mon Sep 17 00:00:00 2001
+From: Alexander Bersenev <bay@hackerdom.ru>
+Date: Mon, 9 Jun 2014 00:08:13 +0600
+Subject: [PATCH] ARM: sunxi: Enable IR controller on cubieboard 2 and
+ cubietruck in dts
+
+This patch enables two IR devices in dts:
+- One IR device physically found on Cubieboard 2
+- One IR device physically found on Cubietruck
+
+Signed-off-by: Alexander Bersenev <bay@hackerdom.ru>
+Signed-off-by: Alexsey Shestacov <wingrime@linux-sunxi.org>
+---
+ arch/arm/boot/dts/sun7i-a20-bananapi.dts | 6 ++++++
+ arch/arm/boot/dts/sun7i-a20-cubietruck.dts  | 6 ++++++
+ 2 files changed, 12 insertions(+)
+
+diff --git a/arch/arm/boot/dts/sun7i-a20-bananapi.dts b/arch/arm/boot/dts/sun7i-a20-bananapi.dts
+index 97bcb2a..81bf3df 100644
+--- a/arch/arm/boot/dts/sun7i-a20-bananapi.dts
++++ b/arch/arm/boot/dts/sun7i-a20-bananapi.dts
+@@ -66,6 +66,12 @@
+                       };
+               };
++              ir0: ir@01c21800 {
++                      pinctrl-names = "default";
++                      pinctrl-0 = <&ir0_pins_a>;
++                      status = "okay";
++              };
++
+               uart0: serial@01c28000 {
+                       pinctrl-names = "default";
+                       pinctrl-0 = <&uart0_pins_a>;