aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSrinivas Kandagatla <srinivas.kandagatla@st.com>2013-10-18 05:01:14 -0400
committerMauro Carvalho Chehab <m.chehab@samsung.com>2013-10-31 06:20:08 -0400
commit80f93c7b0f4599ffbdac8d964ecd1162b8b618b9 (patch)
tree09c46fbdf9d901d4b049af57e699581359625b2d
parent8ab1aa87f3f7381be195efcabf08dbc74626f25d (diff)
[media] media: st-rc: Add ST remote control driver
This patch adds support to ST RC driver, which is basically a IR/UHF receiver and transmitter. This IP (IRB) is common across all the ST parts for settop box platforms. IRB is embedded in ST COMMS IP block. It supports both Rx & Tx functionality. This driver adds only Rx functionality via LIRC codec. Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@st.com> Acked-by: Sean Young <sean@mess.org> Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
-rw-r--r--Documentation/devicetree/bindings/media/st-rc.txt29
-rw-r--r--drivers/media/rc/Kconfig10
-rw-r--r--drivers/media/rc/Makefile1
-rw-r--r--drivers/media/rc/st_rc.c395
4 files changed, 435 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/media/st-rc.txt b/Documentation/devicetree/bindings/media/st-rc.txt
new file mode 100644
index 000000000000..05c432d08bca
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/st-rc.txt
@@ -0,0 +1,29 @@
1Device-Tree bindings for ST IRB IP
2
3Required properties:
4 - compatible: Should contain "st,comms-irb".
5 - reg: Base physical address of the controller and length of memory
6 mapped region.
7 - interrupts: interrupt-specifier for the sole interrupt generated by
8 the device. The interrupt specifier format depends on the interrupt
9 controller parent.
10 - rx-mode: can be "infrared" or "uhf". This property specifies the L1
11 protocol used for receiving remote control signals. rx-mode should
12 be present iff the rx pins are wired up.
13 - tx-mode: should be "infrared". This property specifies the L1
14 protocol used for transmitting remote control signals. tx-mode should
15 be present iff the tx pins are wired up.
16
17Optional properties:
18 - pinctrl-names, pinctrl-0: the pincontrol settings to configure muxing
19 properly for IRB pins.
20 - clocks : phandle with clock-specifier pair for IRB.
21
22Example node:
23
24 rc: rc@fe518000 {
25 compatible = "st,comms-irb";
26 reg = <0xfe518000 0x234>;
27 interrupts = <0 203 0>;
28 rx-mode = "infrared";
29 };
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index 11e84bcc23a1..904f11367c29 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -322,4 +322,14 @@ config IR_GPIO_CIR
322 To compile this driver as a module, choose M here: the module will 322 To compile this driver as a module, choose M here: the module will
323 be called gpio-ir-recv. 323 be called gpio-ir-recv.
324 324
325config RC_ST
326 tristate "ST remote control receiver"
327 depends on ARCH_STI && RC_CORE
328 help
329 Say Y here if you want support for ST remote control driver
330 which allows both IR and UHF RX.
331 The driver passes raw pulse and space information to the LIRC decoder.
332
333 If you're not sure, select N here.
334
325endif #RC_DEVICES 335endif #RC_DEVICES
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index 56bacf07b361..f4eb32c0a455 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -30,3 +30,4 @@ obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o
30obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o 30obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o
31obj-$(CONFIG_IR_IGUANA) += iguanair.o 31obj-$(CONFIG_IR_IGUANA) += iguanair.o
32obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o 32obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o
33obj-$(CONFIG_RC_ST) += st_rc.o
diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c
new file mode 100644
index 000000000000..65120c2d47ad
--- /dev/null
+++ b/drivers/media/rc/st_rc.c
@@ -0,0 +1,395 @@
1/*
2 * Copyright (C) 2013 STMicroelectronics Limited
3 * Author: Srinivas Kandagatla <srinivas.kandagatla@st.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10#include <linux/kernel.h>
11#include <linux/clk.h>
12#include <linux/interrupt.h>
13#include <linux/module.h>
14#include <linux/of.h>
15#include <linux/platform_device.h>
16#include <media/rc-core.h>
17#include <linux/pinctrl/consumer.h>
18
19struct st_rc_device {
20 struct device *dev;
21 int irq;
22 int irq_wake;
23 struct clk *sys_clock;
24 void *base; /* Register base address */
25 void *rx_base;/* RX Register base address */
26 struct rc_dev *rdev;
27 bool overclocking;
28 int sample_mult;
29 int sample_div;
30 bool rxuhfmode;
31};
32
33/* Registers */
34#define IRB_SAMPLE_RATE_COMM 0x64 /* sample freq divisor*/
35#define IRB_CLOCK_SEL 0x70 /* clock select */
36#define IRB_CLOCK_SEL_STATUS 0x74 /* clock status */
37/* IRB IR/UHF receiver registers */
38#define IRB_RX_ON 0x40 /* pulse time capture */
39#define IRB_RX_SYS 0X44 /* sym period capture */
40#define IRB_RX_INT_EN 0x48 /* IRQ enable (R/W) */
41#define IRB_RX_INT_STATUS 0x4c /* IRQ status (R/W) */
42#define IRB_RX_EN 0x50 /* Receive enable */
43#define IRB_MAX_SYM_PERIOD 0x54 /* max sym value */
44#define IRB_RX_INT_CLEAR 0x58 /* overrun status */
45#define IRB_RX_STATUS 0x6c /* receive status */
46#define IRB_RX_NOISE_SUPPR 0x5c /* noise suppression */
47#define IRB_RX_POLARITY_INV 0x68 /* polarity inverter */
48
49/**
50 * IRQ set: Enable full FIFO 1 -> bit 3;
51 * Enable overrun IRQ 1 -> bit 2;
52 * Enable last symbol IRQ 1 -> bit 1:
53 * Enable RX interrupt 1 -> bit 0;
54 */
55#define IRB_RX_INTS 0x0f
56#define IRB_RX_OVERRUN_INT 0x04
57 /* maximum symbol period (microsecs),timeout to detect end of symbol train */
58#define MAX_SYMB_TIME 0x5000
59#define IRB_SAMPLE_FREQ 10000000
60#define IRB_FIFO_NOT_EMPTY 0xff00
61#define IRB_OVERFLOW 0x4
62#define IRB_TIMEOUT 0xffff
63#define IR_ST_NAME "st-rc"
64
65static void st_rc_send_lirc_timeout(struct rc_dev *rdev)
66{
67 DEFINE_IR_RAW_EVENT(ev);
68 ev.timeout = true;
69 ir_raw_event_store(rdev, &ev);
70}
71
72/**
73 * RX graphical example to better understand the difference between ST IR block
74 * output and standard definition used by LIRC (and most of the world!)
75 *
76 * mark mark
77 * |-IRB_RX_ON-| |-IRB_RX_ON-|
78 * ___ ___ ___ ___ ___ ___ _
79 * | | | | | | | | | | | | |
80 * | | | | | | space 0 | | | | | | space 1 |
81 * _____| |__| |__| |____________________________| |__| |__| |_____________|
82 *
83 * |--------------- IRB_RX_SYS -------------|------ IRB_RX_SYS -------|
84 *
85 * |------------- encoding bit 0 -----------|---- encoding bit 1 -----|
86 *
87 * ST hardware returns mark (IRB_RX_ON) and total symbol time (IRB_RX_SYS), so
88 * convert to standard mark/space we have to calculate space=(IRB_RX_SYS-mark)
89 * The mark time represents the amount of time the carrier (usually 36-40kHz)
90 * is detected.The above examples shows Pulse Width Modulation encoding where
91 * bit 0 is represented by space>mark.
92 */
93
94static irqreturn_t st_rc_rx_interrupt(int irq, void *data)
95{
96 unsigned int symbol, mark = 0;
97 struct st_rc_device *dev = data;
98 int last_symbol = 0;
99 u32 status;
100 DEFINE_IR_RAW_EVENT(ev);
101
102 if (dev->irq_wake)
103 pm_wakeup_event(dev->dev, 0);
104
105 status = readl(dev->rx_base + IRB_RX_STATUS);
106
107 while (status & (IRB_FIFO_NOT_EMPTY | IRB_OVERFLOW)) {
108 u32 int_status = readl(dev->rx_base + IRB_RX_INT_STATUS);
109 if (unlikely(int_status & IRB_RX_OVERRUN_INT)) {
110 /* discard the entire collection in case of errors! */
111 ir_raw_event_reset(dev->rdev);
112 dev_info(dev->dev, "IR RX overrun\n");
113 writel(IRB_RX_OVERRUN_INT,
114 dev->rx_base + IRB_RX_INT_CLEAR);
115 continue;
116 }
117
118 symbol = readl(dev->rx_base + IRB_RX_SYS);
119 mark = readl(dev->rx_base + IRB_RX_ON);
120
121 if (symbol == IRB_TIMEOUT)
122 last_symbol = 1;
123
124 /* Ignore any noise */
125 if ((mark > 2) && (symbol > 1)) {
126 symbol -= mark;
127 if (dev->overclocking) { /* adjustments to timings */
128 symbol *= dev->sample_mult;
129 symbol /= dev->sample_div;
130 mark *= dev->sample_mult;
131 mark /= dev->sample_div;
132 }
133
134 ev.duration = US_TO_NS(mark);
135 ev.pulse = true;
136 ir_raw_event_store(dev->rdev, &ev);
137
138 if (!last_symbol) {
139 ev.duration = US_TO_NS(symbol);
140 ev.pulse = false;
141 ir_raw_event_store(dev->rdev, &ev);
142 } else {
143 st_rc_send_lirc_timeout(dev->rdev);
144 }
145
146 }
147 last_symbol = 0;
148 status = readl(dev->rx_base + IRB_RX_STATUS);
149 }
150
151 writel(IRB_RX_INTS, dev->rx_base + IRB_RX_INT_CLEAR);
152
153 /* Empty software fifo */
154 ir_raw_event_handle(dev->rdev);
155 return IRQ_HANDLED;
156}
157
158static void st_rc_hardware_init(struct st_rc_device *dev)
159{
160 int baseclock, freqdiff;
161 unsigned int rx_max_symbol_per = MAX_SYMB_TIME;
162 unsigned int rx_sampling_freq_div;
163
164 clk_prepare_enable(dev->sys_clock);
165 baseclock = clk_get_rate(dev->sys_clock);
166
167 /* IRB input pins are inverted internally from high to low. */
168 writel(1, dev->rx_base + IRB_RX_POLARITY_INV);
169
170 rx_sampling_freq_div = baseclock / IRB_SAMPLE_FREQ;
171 writel(rx_sampling_freq_div, dev->base + IRB_SAMPLE_RATE_COMM);
172
173 freqdiff = baseclock - (rx_sampling_freq_div * IRB_SAMPLE_FREQ);
174 if (freqdiff) { /* over clocking, workout the adjustment factors */
175 dev->overclocking = true;
176 dev->sample_mult = 1000;
177 dev->sample_div = baseclock / (10000 * rx_sampling_freq_div);
178 rx_max_symbol_per = (rx_max_symbol_per * 1000)/dev->sample_div;
179 }
180
181 writel(rx_max_symbol_per, dev->rx_base + IRB_MAX_SYM_PERIOD);
182}
183
184static int st_rc_remove(struct platform_device *pdev)
185{
186 struct st_rc_device *rc_dev = platform_get_drvdata(pdev);
187 clk_disable_unprepare(rc_dev->sys_clock);
188 rc_unregister_device(rc_dev->rdev);
189 return 0;
190}
191
192static int st_rc_open(struct rc_dev *rdev)
193{
194 struct st_rc_device *dev = rdev->priv;
195 unsigned long flags;
196 local_irq_save(flags);
197 /* enable interrupts and receiver */
198 writel(IRB_RX_INTS, dev->rx_base + IRB_RX_INT_EN);
199 writel(0x01, dev->rx_base + IRB_RX_EN);
200 local_irq_restore(flags);
201
202 return 0;
203}
204
205static void st_rc_close(struct rc_dev *rdev)
206{
207 struct st_rc_device *dev = rdev->priv;
208 /* disable interrupts and receiver */
209 writel(0x00, dev->rx_base + IRB_RX_EN);
210 writel(0x00, dev->rx_base + IRB_RX_INT_EN);
211}
212
213static int st_rc_probe(struct platform_device *pdev)
214{
215 int ret = -EINVAL;
216 struct rc_dev *rdev;
217 struct device *dev = &pdev->dev;
218 struct resource *res;
219 struct st_rc_device *rc_dev;
220 struct device_node *np = pdev->dev.of_node;
221 const char *rx_mode;
222
223 rc_dev = devm_kzalloc(dev, sizeof(struct st_rc_device), GFP_KERNEL);
224
225 if (!rc_dev)
226 return -ENOMEM;
227
228 rdev = rc_allocate_device();
229
230 if (!rdev)
231 return -ENOMEM;
232
233 if (np && !of_property_read_string(np, "rx-mode", &rx_mode)) {
234
235 if (!strcmp(rx_mode, "uhf")) {
236 rc_dev->rxuhfmode = true;
237 } else if (!strcmp(rx_mode, "infrared")) {
238 rc_dev->rxuhfmode = false;
239 } else {
240 dev_err(dev, "Unsupported rx mode [%s]\n", rx_mode);
241 goto err;
242 }
243
244 } else {
245 goto err;
246 }
247
248 rc_dev->sys_clock = devm_clk_get(dev, NULL);
249 if (IS_ERR(rc_dev->sys_clock)) {
250 dev_err(dev, "System clock not found\n");
251 ret = PTR_ERR(rc_dev->sys_clock);
252 goto err;
253 }
254
255 rc_dev->irq = platform_get_irq(pdev, 0);
256 if (rc_dev->irq < 0) {
257 ret = rc_dev->irq;
258 goto err;
259 }
260
261 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
262
263 rc_dev->base = devm_ioremap_resource(dev, res);
264 if (IS_ERR(rc_dev->base)) {
265 ret = PTR_ERR(rc_dev->base);
266 goto err;
267 }
268
269 if (rc_dev->rxuhfmode)
270 rc_dev->rx_base = rc_dev->base + 0x40;
271 else
272 rc_dev->rx_base = rc_dev->base;
273
274 rc_dev->dev = dev;
275 platform_set_drvdata(pdev, rc_dev);
276 st_rc_hardware_init(rc_dev);
277
278 rdev->driver_type = RC_DRIVER_IR_RAW;
279 rdev->allowed_protos = RC_BIT_ALL;
280 /* rx sampling rate is 10Mhz */
281 rdev->rx_resolution = 100;
282 rdev->timeout = US_TO_NS(MAX_SYMB_TIME);
283 rdev->priv = rc_dev;
284 rdev->open = st_rc_open;
285 rdev->close = st_rc_close;
286 rdev->driver_name = IR_ST_NAME;
287 rdev->map_name = RC_MAP_LIRC;
288 rdev->input_name = "ST Remote Control Receiver";
289
290 /* enable wake via this device */
291 device_set_wakeup_capable(dev, true);
292 device_set_wakeup_enable(dev, true);
293
294 ret = rc_register_device(rdev);
295 if (ret < 0)
296 goto clkerr;
297
298 rc_dev->rdev = rdev;
299 if (devm_request_irq(dev, rc_dev->irq, st_rc_rx_interrupt,
300 IRQF_NO_SUSPEND, IR_ST_NAME, rc_dev) < 0) {
301 dev_err(dev, "IRQ %d register failed\n", rc_dev->irq);
302 ret = -EINVAL;
303 goto rcerr;
304 }
305
306 /**
307 * for LIRC_MODE_MODE2 or LIRC_MODE_PULSE or LIRC_MODE_RAW
308 * lircd expects a long space first before a signal train to sync.
309 */
310 st_rc_send_lirc_timeout(rdev);
311
312 dev_info(dev, "setup in %s mode\n", rc_dev->rxuhfmode ? "UHF" : "IR");
313
314 return ret;
315rcerr:
316 rc_unregister_device(rdev);
317 rdev = NULL;
318clkerr:
319 clk_disable_unprepare(rc_dev->sys_clock);
320err:
321 rc_free_device(rdev);
322 dev_err(dev, "Unable to register device (%d)\n", ret);
323 return ret;
324}
325
326#ifdef CONFIG_PM
327static int st_rc_suspend(struct device *dev)
328{
329 struct st_rc_device *rc_dev = dev_get_drvdata(dev);
330
331 if (device_may_wakeup(dev)) {
332 if (!enable_irq_wake(rc_dev->irq))
333 rc_dev->irq_wake = 1;
334 else
335 return -EINVAL;
336 } else {
337 pinctrl_pm_select_sleep_state(dev);
338 writel(0x00, rc_dev->rx_base + IRB_RX_EN);
339 writel(0x00, rc_dev->rx_base + IRB_RX_INT_EN);
340 clk_disable_unprepare(rc_dev->sys_clock);
341 }
342
343 return 0;
344}
345
346static int st_rc_resume(struct device *dev)
347{
348 struct st_rc_device *rc_dev = dev_get_drvdata(dev);
349 struct rc_dev *rdev = rc_dev->rdev;
350
351 if (rc_dev->irq_wake) {
352 disable_irq_wake(rc_dev->irq);
353 rc_dev->irq_wake = 0;
354 } else {
355 pinctrl_pm_select_default_state(dev);
356 st_rc_hardware_init(rc_dev);
357 if (rdev->users) {
358 writel(IRB_RX_INTS, rc_dev->rx_base + IRB_RX_INT_EN);
359 writel(0x01, rc_dev->rx_base + IRB_RX_EN);
360 }
361 }
362
363 return 0;
364}
365
366static SIMPLE_DEV_PM_OPS(st_rc_pm_ops, st_rc_suspend, st_rc_resume);
367#endif
368
369#ifdef CONFIG_OF
370static struct of_device_id st_rc_match[] = {
371 { .compatible = "st,comms-irb", },
372 {},
373};
374
375MODULE_DEVICE_TABLE(of, st_rc_match);
376#endif
377
378static struct platform_driver st_rc_driver = {
379 .driver = {
380 .name = IR_ST_NAME,
381 .owner = THIS_MODULE,
382 .of_match_table = of_match_ptr(st_rc_match),
383#ifdef CONFIG_PM
384 .pm = &st_rc_pm_ops,
385#endif
386 },
387 .probe = st_rc_probe,
388 .remove = st_rc_remove,
389};
390
391module_platform_driver(st_rc_driver);
392
393MODULE_DESCRIPTION("RC Transceiver driver for STMicroelectronics platforms");
394MODULE_AUTHOR("STMicroelectronics (R&D) Ltd");
395MODULE_LICENSE("GPL");