diff options
author | Zoltan Herpai <wigyori@uid0.hu> | 2014-09-21 16:25:45 +0000 |
---|---|---|
committer | Zoltan Herpai <wigyori@uid0.hu> | 2014-09-21 16:25:45 +0000 |
commit | e6d53c13662bb717b36f274df8a65397b18cf42f (patch) | |
tree | f1b9b0d517092323791ca530ff90301641691205 | |
parent | e02dd2f19c3d99e20fc3ed063f706d023004f3cf (diff) | |
download | upstream-e6d53c13662bb717b36f274df8a65397b18cf42f.tar.gz upstream-e6d53c13662bb717b36f274df8a65397b18cf42f.tar.bz2 upstream-e6d53c13662bb717b36f274df8a65397b18cf42f.zip |
sunxi: add support for infra receiver on A20, along with IR-related backports from 3.15
Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
SVN-Revision: 42630
6 files changed, 2073 insertions, 0 deletions
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 index 0000000000..dd0599e1fd --- /dev/null +++ b/target/linux/sunxi/patches-3.14/280-ir-add-driver.patch @@ -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 index 0000000000..58ca0ee481 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/281-dt-sun7i-add-ir-pins.patch @@ -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 index 0000000000..4a544cd695 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/282-dt-sun7i-add-ir-ctrlers.patch @@ -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 index 0000000000..290b990621 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/283-dt-sun7i-add-ir-to-cb2-cbt.patch @@ -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 index 0000000000..37ccffce7e --- /dev/null +++ b/target/linux/sunxi/patches-3.14/284-ir-backports-from-3.15.patch @@ -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 index 0000000000..b075799aa3 --- /dev/null +++ b/target/linux/sunxi/patches-3.14/285-dt-sun7i-add-ir-to-bananapi.patch @@ -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>; |