aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorBeniamino Galvani <b.galvani@gmail.com>2014-11-18 15:22:34 -0500
committerMauro Carvalho Chehab <mchehab@osg.samsung.com>2014-11-26 10:29:01 -0500
commit12ddbadf383d551e2681eb361b4f5c400363b5cd (patch)
tree9b0ab4c8a8ea798486e4545e86bb2d40fa7ff465 /drivers/media
parent36cdfa76635b502c8c7bb7df695d7d5117eca7de (diff)
[media] media: rc: add driver for Amlogic Meson IR remote receiver
Amlogic Meson SoCs include a infrared remote control receiver that can operate in two modes: "NEC" mode in which the hardware decodes frames using the NEC IR protocol, and "general" mode in which the receiver simply reports the duration of pulses and spaces for software decoding. This is a driver for the IR receiver that implements software decoding of received frames. Signed-off-by: Beniamino Galvani <b.galvani@gmail.com> Acked-by: Carlo Caione <carlo@caione.org> Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/rc/Kconfig11
-rw-r--r--drivers/media/rc/Makefile1
-rw-r--r--drivers/media/rc/meson-ir.c216
3 files changed, 228 insertions, 0 deletions
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index 1aea7320f52a..ddfab256b9a5 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -223,6 +223,17 @@ config IR_FINTEK
223 To compile this driver as a module, choose M here: the 223 To compile this driver as a module, choose M here: the
224 module will be called fintek-cir. 224 module will be called fintek-cir.
225 225
226config IR_MESON
227 tristate "Amlogic Meson IR remote receiver"
228 depends on RC_CORE
229 depends on ARCH_MESON || COMPILE_TEST
230 ---help---
231 Say Y if you want to use the IR remote receiver available
232 on Amlogic Meson SoCs.
233
234 To compile this driver as a module, choose M here: the
235 module will be called meson-ir.
236
226config IR_NUVOTON 237config IR_NUVOTON
227 tristate "Nuvoton w836x7hg Consumer Infrared Transceiver" 238 tristate "Nuvoton w836x7hg Consumer Infrared Transceiver"
228 depends on PNP 239 depends on PNP
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index 8f509e0d92cf..379a5c0f1379 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_IR_IMON) += imon.o
22obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o 22obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o
23obj-$(CONFIG_IR_MCEUSB) += mceusb.o 23obj-$(CONFIG_IR_MCEUSB) += mceusb.o
24obj-$(CONFIG_IR_FINTEK) += fintek-cir.o 24obj-$(CONFIG_IR_FINTEK) += fintek-cir.o
25obj-$(CONFIG_IR_MESON) += meson-ir.o
25obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o 26obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o
26obj-$(CONFIG_IR_ENE) += ene_ir.o 27obj-$(CONFIG_IR_ENE) += ene_ir.o
27obj-$(CONFIG_IR_REDRAT3) += redrat3.o 28obj-$(CONFIG_IR_REDRAT3) += redrat3.o
diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c
new file mode 100644
index 000000000000..fcc3b82d1454
--- /dev/null
+++ b/drivers/media/rc/meson-ir.c
@@ -0,0 +1,216 @@
1/*
2 * Driver for Amlogic Meson IR remote receiver
3 *
4 * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * version 2 as published by the Free Software Foundation.
9 *
10 * You should have received a copy of the GNU General Public License
11 * along with this program. If not, see <http://www.gnu.org/licenses/>.
12 */
13
14#include <linux/device.h>
15#include <linux/err.h>
16#include <linux/interrupt.h>
17#include <linux/io.h>
18#include <linux/module.h>
19#include <linux/of_platform.h>
20#include <linux/platform_device.h>
21#include <linux/spinlock.h>
22
23#include <media/rc-core.h>
24
25#define DRIVER_NAME "meson-ir"
26
27#define IR_DEC_LDR_ACTIVE 0x00
28#define IR_DEC_LDR_IDLE 0x04
29#define IR_DEC_LDR_REPEAT 0x08
30#define IR_DEC_BIT_0 0x0c
31#define IR_DEC_REG0 0x10
32#define IR_DEC_FRAME 0x14
33#define IR_DEC_STATUS 0x18
34#define IR_DEC_REG1 0x1c
35
36#define REG0_RATE_MASK (BIT(11) - 1)
37
38#define REG1_MODE_MASK (BIT(7) | BIT(8))
39#define REG1_MODE_NEC (0 << 7)
40#define REG1_MODE_GENERAL (2 << 7)
41
42#define REG1_TIME_IV_SHIFT 16
43#define REG1_TIME_IV_MASK ((BIT(13) - 1) << REG1_TIME_IV_SHIFT)
44
45#define REG1_IRQSEL_MASK (BIT(2) | BIT(3))
46#define REG1_IRQSEL_NEC_MODE (0 << 2)
47#define REG1_IRQSEL_RISE_FALL (1 << 2)
48#define REG1_IRQSEL_FALL (2 << 2)
49#define REG1_IRQSEL_RISE (3 << 2)
50
51#define REG1_RESET BIT(0)
52#define REG1_ENABLE BIT(15)
53
54#define STATUS_IR_DEC_IN BIT(8)
55
56#define MESON_TRATE 10 /* us */
57
58struct meson_ir {
59 void __iomem *reg;
60 struct rc_dev *rc;
61 int irq;
62 spinlock_t lock;
63};
64
65static void meson_ir_set_mask(struct meson_ir *ir, unsigned int reg,
66 u32 mask, u32 value)
67{
68 u32 data;
69
70 data = readl(ir->reg + reg);
71 data &= ~mask;
72 data |= (value & mask);
73 writel(data, ir->reg + reg);
74}
75
76static irqreturn_t meson_ir_irq(int irqno, void *dev_id)
77{
78 struct meson_ir *ir = dev_id;
79 u32 duration;
80 DEFINE_IR_RAW_EVENT(rawir);
81
82 spin_lock(&ir->lock);
83
84 duration = readl(ir->reg + IR_DEC_REG1);
85 duration = (duration & REG1_TIME_IV_MASK) >> REG1_TIME_IV_SHIFT;
86 rawir.duration = US_TO_NS(duration * MESON_TRATE);
87
88 rawir.pulse = !!(readl(ir->reg + IR_DEC_STATUS) & STATUS_IR_DEC_IN);
89
90 ir_raw_event_store_with_filter(ir->rc, &rawir);
91 ir_raw_event_handle(ir->rc);
92
93 spin_unlock(&ir->lock);
94
95 return IRQ_HANDLED;
96}
97
98static int meson_ir_probe(struct platform_device *pdev)
99{
100 struct device *dev = &pdev->dev;
101 struct device_node *node = dev->of_node;
102 struct resource *res;
103 const char *map_name;
104 struct meson_ir *ir;
105 int ret;
106
107 ir = devm_kzalloc(dev, sizeof(struct meson_ir), GFP_KERNEL);
108 if (!ir)
109 return -ENOMEM;
110
111 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
112 ir->reg = devm_ioremap_resource(dev, res);
113 if (IS_ERR(ir->reg)) {
114 dev_err(dev, "failed to map registers\n");
115 return PTR_ERR(ir->reg);
116 }
117
118 ir->irq = platform_get_irq(pdev, 0);
119 if (ir->irq < 0) {
120 dev_err(dev, "no irq resource\n");
121 return ir->irq;
122 }
123
124 ir->rc = rc_allocate_device();
125 if (!ir->rc) {
126 dev_err(dev, "failed to allocate rc device\n");
127 return -ENOMEM;
128 }
129
130 ir->rc->priv = ir;
131 ir->rc->input_name = DRIVER_NAME;
132 ir->rc->input_phys = DRIVER_NAME "/input0";
133 ir->rc->input_id.bustype = BUS_HOST;
134 map_name = of_get_property(node, "linux,rc-map-name", NULL);
135 ir->rc->map_name = map_name ? map_name : RC_MAP_EMPTY;
136 ir->rc->dev.parent = dev;
137 ir->rc->driver_type = RC_DRIVER_IR_RAW;
138 ir->rc->allowed_protocols = RC_BIT_ALL;
139 ir->rc->rx_resolution = US_TO_NS(MESON_TRATE);
140 ir->rc->timeout = MS_TO_NS(200);
141 ir->rc->driver_name = DRIVER_NAME;
142
143 spin_lock_init(&ir->lock);
144 platform_set_drvdata(pdev, ir);
145
146 ret = rc_register_device(ir->rc);
147 if (ret) {
148 dev_err(dev, "failed to register rc device\n");
149 goto out_free;
150 }
151
152 ret = devm_request_irq(dev, ir->irq, meson_ir_irq, 0, "ir-meson", ir);
153 if (ret) {
154 dev_err(dev, "failed to request irq\n");
155 goto out_unreg;
156 }
157
158 /* Reset the decoder */
159 meson_ir_set_mask(ir, IR_DEC_REG1, REG1_RESET, REG1_RESET);
160 meson_ir_set_mask(ir, IR_DEC_REG1, REG1_RESET, 0);
161 /* Set general operation mode */
162 meson_ir_set_mask(ir, IR_DEC_REG1, REG1_MODE_MASK, REG1_MODE_GENERAL);
163 /* Set rate */
164 meson_ir_set_mask(ir, IR_DEC_REG0, REG0_RATE_MASK, MESON_TRATE - 1);
165 /* IRQ on rising and falling edges */
166 meson_ir_set_mask(ir, IR_DEC_REG1, REG1_IRQSEL_MASK,
167 REG1_IRQSEL_RISE_FALL);
168 /* Enable the decoder */
169 meson_ir_set_mask(ir, IR_DEC_REG1, REG1_ENABLE, REG1_ENABLE);
170
171 dev_info(dev, "receiver initialized\n");
172
173 return 0;
174out_unreg:
175 rc_unregister_device(ir->rc);
176 ir->rc = NULL;
177out_free:
178 rc_free_device(ir->rc);
179
180 return ret;
181}
182
183static int meson_ir_remove(struct platform_device *pdev)
184{
185 struct meson_ir *ir = platform_get_drvdata(pdev);
186 unsigned long flags;
187
188 /* Disable the decoder */
189 spin_lock_irqsave(&ir->lock, flags);
190 meson_ir_set_mask(ir, IR_DEC_REG1, REG1_ENABLE, 0);
191 spin_unlock_irqrestore(&ir->lock, flags);
192
193 rc_unregister_device(ir->rc);
194
195 return 0;
196}
197
198static const struct of_device_id meson_ir_match[] = {
199 { .compatible = "amlogic,meson6-ir" },
200 { },
201};
202
203static struct platform_driver meson_ir_driver = {
204 .probe = meson_ir_probe,
205 .remove = meson_ir_remove,
206 .driver = {
207 .name = DRIVER_NAME,
208 .of_match_table = meson_ir_match,
209 },
210};
211
212module_platform_driver(meson_ir_driver);
213
214MODULE_DESCRIPTION("Amlogic Meson IR remote receiver driver");
215MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
216MODULE_LICENSE("GPL v2");