aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTimo Kokkonen <timo.t.kokkonen@iki.fi>2012-08-10 05:16:36 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2012-08-13 15:53:45 -0400
commitc332e8472d7db67766bcad33390c607fdd9ac5bc (patch)
treeeb69c948d5ca73c50e7a8dcbfaea97c6bb5470dc
parent65d9bc9236bb1f95b5d29912cc714d0c0c7bb79a (diff)
[media] media: rc: Introduce RX51 IR transmitter driver
This is the driver for the IR transmitter diode found on the Nokia N900 (also known as RX51) device. The driver is mostly the same as found in the original 2.6.28 based kernel that comes with the device. The following modifications have been made compared to the original driver version: - Adopt to the changes that has happen in the kernel during the past five years, such as the change in the include paths - The OMAP DM-timers require much more care nowadays. The timers need to be enabled and disabled or otherwise many actions fail. Timers must not be freed without first stopping them or otherwise the timer cannot be requested again. The code has been tested with sending IR codes with N900 device running Debian userland. The device receiving the codes was Anysee DVB-C USB receiver. Signed-off-by: Timo Kokkonen <timo.t.kokkonen@iki.fi> Cc: Tony Lindgren <tony@atomide.com> Cc: linux-omap@vger.kernel.org Cc: Sakari Ailus <sakari.ailus@iki.fi> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r--drivers/media/rc/Kconfig10
-rw-r--r--drivers/media/rc/Makefile1
-rw-r--r--drivers/media/rc/ir-rx51.c496
-rw-r--r--include/media/ir-rx51.h10
4 files changed, 517 insertions, 0 deletions
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index 64be610fe27d..016f9ab1c1e9 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -287,6 +287,16 @@ config IR_TTUSBIR
287 To compile this driver as a module, choose M here: the module will 287 To compile this driver as a module, choose M here: the module will
288 be called ttusbir. 288 be called ttusbir.
289 289
290config IR_RX51
291 tristate "Nokia N900 IR transmitter diode
292 depends on MACH_NOKIA_RX51 && OMAP_DM_TIMER
293 ---help---
294 Say Y or M here if you want to enable support for the IR
295 transmitter diode built in the Nokia N900 (RX51) device.
296
297 The driver uses omap DM timers for gereating the carrier
298 wave and pulses.
299
290config RC_LOOPBACK 300config RC_LOOPBACK
291 tristate "Remote Control Loopback Driver" 301 tristate "Remote Control Loopback Driver"
292 depends on RC_CORE 302 depends on RC_CORE
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index 66c8baed565d..56bacf07b361 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_IR_FINTEK) += fintek-cir.o
23obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o 23obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o
24obj-$(CONFIG_IR_ENE) += ene_ir.o 24obj-$(CONFIG_IR_ENE) += ene_ir.o
25obj-$(CONFIG_IR_REDRAT3) += redrat3.o 25obj-$(CONFIG_IR_REDRAT3) += redrat3.o
26obj-$(CONFIG_IR_RX51) += ir-rx51.o
26obj-$(CONFIG_IR_STREAMZAP) += streamzap.o 27obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
27obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o 28obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o
28obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o 29obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o
diff --git a/drivers/media/rc/ir-rx51.c b/drivers/media/rc/ir-rx51.c
new file mode 100644
index 000000000000..9487dd33a89a
--- /dev/null
+++ b/drivers/media/rc/ir-rx51.c
@@ -0,0 +1,496 @@
1/*
2 * Copyright (C) 2008 Nokia Corporation
3 *
4 * Based on lirc_serial.c
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22#include <linux/module.h>
23#include <linux/interrupt.h>
24#include <linux/uaccess.h>
25#include <linux/platform_device.h>
26#include <linux/sched.h>
27#include <linux/wait.h>
28
29#include <plat/dmtimer.h>
30#include <plat/clock.h>
31#include <plat/omap-pm.h>
32
33#include <media/lirc.h>
34#include <media/lirc_dev.h>
35#include <media/ir-rx51.h>
36
37#define LIRC_RX51_DRIVER_FEATURES (LIRC_CAN_SET_SEND_DUTY_CYCLE | \
38 LIRC_CAN_SET_SEND_CARRIER | \
39 LIRC_CAN_SEND_PULSE)
40
41#define DRIVER_NAME "lirc_rx51"
42
43#define WBUF_LEN 256
44
45#define TIMER_MAX_VALUE 0xffffffff
46
47struct lirc_rx51 {
48 struct omap_dm_timer *pwm_timer;
49 struct omap_dm_timer *pulse_timer;
50 struct device *dev;
51 struct lirc_rx51_platform_data *pdata;
52 wait_queue_head_t wqueue;
53
54 unsigned long fclk_khz;
55 unsigned int freq; /* carrier frequency */
56 unsigned int duty_cycle; /* carrier duty cycle */
57 unsigned int irq_num;
58 unsigned int match;
59 int wbuf[WBUF_LEN];
60 int wbuf_index;
61 unsigned long device_is_open;
62 unsigned int pwm_timer_num;
63};
64
65static void lirc_rx51_on(struct lirc_rx51 *lirc_rx51)
66{
67 omap_dm_timer_set_pwm(lirc_rx51->pwm_timer, 0, 1,
68 OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
69}
70
71static void lirc_rx51_off(struct lirc_rx51 *lirc_rx51)
72{
73 omap_dm_timer_set_pwm(lirc_rx51->pwm_timer, 0, 1,
74 OMAP_TIMER_TRIGGER_NONE);
75}
76
77static int init_timing_params(struct lirc_rx51 *lirc_rx51)
78{
79 u32 load, match;
80
81 load = -(lirc_rx51->fclk_khz * 1000 / lirc_rx51->freq);
82 match = -(lirc_rx51->duty_cycle * -load / 100);
83 omap_dm_timer_set_load(lirc_rx51->pwm_timer, 1, load);
84 omap_dm_timer_set_match(lirc_rx51->pwm_timer, 1, match);
85 omap_dm_timer_write_counter(lirc_rx51->pwm_timer, TIMER_MAX_VALUE - 2);
86 omap_dm_timer_start(lirc_rx51->pwm_timer);
87 omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, 0);
88 omap_dm_timer_start(lirc_rx51->pulse_timer);
89
90 lirc_rx51->match = 0;
91
92 return 0;
93}
94
95#define tics_after(a, b) ((long)(b) - (long)(a) < 0)
96
97static int pulse_timer_set_timeout(struct lirc_rx51 *lirc_rx51, int usec)
98{
99 int counter;
100
101 BUG_ON(usec < 0);
102
103 if (lirc_rx51->match == 0)
104 counter = omap_dm_timer_read_counter(lirc_rx51->pulse_timer);
105 else
106 counter = lirc_rx51->match;
107
108 counter += (u32)(lirc_rx51->fclk_khz * usec / (1000));
109 omap_dm_timer_set_match(lirc_rx51->pulse_timer, 1, counter);
110 omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer,
111 OMAP_TIMER_INT_MATCH);
112 if (tics_after(omap_dm_timer_read_counter(lirc_rx51->pulse_timer),
113 counter)) {
114 return 1;
115 }
116 return 0;
117}
118
119static irqreturn_t lirc_rx51_interrupt_handler(int irq, void *ptr)
120{
121 unsigned int retval;
122 struct lirc_rx51 *lirc_rx51 = ptr;
123
124 retval = omap_dm_timer_read_status(lirc_rx51->pulse_timer);
125 if (!retval)
126 return IRQ_NONE;
127
128 if ((retval & ~OMAP_TIMER_INT_MATCH))
129 dev_err_ratelimited(lirc_rx51->dev,
130 ": Unexpected interrupt source: %x\n", retval);
131
132 omap_dm_timer_write_status(lirc_rx51->pulse_timer, 7);
133 if (lirc_rx51->wbuf_index < 0) {
134 dev_err_ratelimited(lirc_rx51->dev,
135 ": BUG wbuf_index has value of %i\n",
136 lirc_rx51->wbuf_index);
137 goto end;
138 }
139
140 /*
141 * If we happen to hit an odd latency spike, loop through the
142 * pulses until we catch up.
143 */
144 do {
145 if (lirc_rx51->wbuf_index >= WBUF_LEN)
146 goto end;
147 if (lirc_rx51->wbuf[lirc_rx51->wbuf_index] == -1)
148 goto end;
149
150 if (lirc_rx51->wbuf_index % 2)
151 lirc_rx51_off(lirc_rx51);
152 else
153 lirc_rx51_on(lirc_rx51);
154
155 retval = pulse_timer_set_timeout(lirc_rx51,
156 lirc_rx51->wbuf[lirc_rx51->wbuf_index]);
157 lirc_rx51->wbuf_index++;
158
159 } while (retval);
160
161 return IRQ_HANDLED;
162end:
163 /* Stop TX here */
164 lirc_rx51_off(lirc_rx51);
165 lirc_rx51->wbuf_index = -1;
166 omap_dm_timer_stop(lirc_rx51->pwm_timer);
167 omap_dm_timer_stop(lirc_rx51->pulse_timer);
168 omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, 0);
169 wake_up_interruptible(&lirc_rx51->wqueue);
170
171 return IRQ_HANDLED;
172}
173
174static int lirc_rx51_init_port(struct lirc_rx51 *lirc_rx51)
175{
176 struct clk *clk_fclk;
177 int retval, pwm_timer = lirc_rx51->pwm_timer_num;
178
179 lirc_rx51->pwm_timer = omap_dm_timer_request_specific(pwm_timer);
180 if (lirc_rx51->pwm_timer == NULL) {
181 dev_err(lirc_rx51->dev, ": Error requesting GPT%d timer\n",
182 pwm_timer);
183 return -EBUSY;
184 }
185
186 lirc_rx51->pulse_timer = omap_dm_timer_request();
187 if (lirc_rx51->pulse_timer == NULL) {
188 dev_err(lirc_rx51->dev, ": Error requesting pulse timer\n");
189 retval = -EBUSY;
190 goto err1;
191 }
192
193 omap_dm_timer_set_source(lirc_rx51->pwm_timer, OMAP_TIMER_SRC_SYS_CLK);
194 omap_dm_timer_set_source(lirc_rx51->pulse_timer,
195 OMAP_TIMER_SRC_SYS_CLK);
196
197 omap_dm_timer_enable(lirc_rx51->pwm_timer);
198 omap_dm_timer_enable(lirc_rx51->pulse_timer);
199
200 lirc_rx51->irq_num = omap_dm_timer_get_irq(lirc_rx51->pulse_timer);
201 retval = request_irq(lirc_rx51->irq_num, lirc_rx51_interrupt_handler,
202 IRQF_DISABLED | IRQF_SHARED,
203 "lirc_pulse_timer", lirc_rx51);
204 if (retval) {
205 dev_err(lirc_rx51->dev, ": Failed to request interrupt line\n");
206 goto err2;
207 }
208
209 clk_fclk = omap_dm_timer_get_fclk(lirc_rx51->pwm_timer);
210 lirc_rx51->fclk_khz = clk_fclk->rate / 1000;
211
212 return 0;
213
214err2:
215 omap_dm_timer_free(lirc_rx51->pulse_timer);
216err1:
217 omap_dm_timer_free(lirc_rx51->pwm_timer);
218
219 return retval;
220}
221
222static int lirc_rx51_free_port(struct lirc_rx51 *lirc_rx51)
223{
224 omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, 0);
225 free_irq(lirc_rx51->irq_num, lirc_rx51);
226 lirc_rx51_off(lirc_rx51);
227 omap_dm_timer_disable(lirc_rx51->pwm_timer);
228 omap_dm_timer_disable(lirc_rx51->pulse_timer);
229 omap_dm_timer_free(lirc_rx51->pwm_timer);
230 omap_dm_timer_free(lirc_rx51->pulse_timer);
231 lirc_rx51->wbuf_index = -1;
232
233 return 0;
234}
235
236static ssize_t lirc_rx51_write(struct file *file, const char *buf,
237 size_t n, loff_t *ppos)
238{
239 int count, i;
240 struct lirc_rx51 *lirc_rx51 = file->private_data;
241
242 if (n % sizeof(int))
243 return -EINVAL;
244
245 count = n / sizeof(int);
246 if ((count > WBUF_LEN) || (count % 2 == 0))
247 return -EINVAL;
248
249 /* Wait any pending transfers to finish */
250 wait_event_interruptible(lirc_rx51->wqueue, lirc_rx51->wbuf_index < 0);
251
252 if (copy_from_user(lirc_rx51->wbuf, buf, n))
253 return -EFAULT;
254
255 /* Sanity check the input pulses */
256 for (i = 0; i < count; i++)
257 if (lirc_rx51->wbuf[i] < 0)
258 return -EINVAL;
259
260 init_timing_params(lirc_rx51);
261 if (count < WBUF_LEN)
262 lirc_rx51->wbuf[count] = -1; /* Insert termination mark */
263
264 /*
265 * Adjust latency requirements so the device doesn't go in too
266 * deep sleep states
267 */
268 lirc_rx51->pdata->set_max_mpu_wakeup_lat(lirc_rx51->dev, 50);
269
270 lirc_rx51_on(lirc_rx51);
271 lirc_rx51->wbuf_index = 1;
272 pulse_timer_set_timeout(lirc_rx51, lirc_rx51->wbuf[0]);
273
274 /*
275 * Don't return back to the userspace until the transfer has
276 * finished
277 */
278 wait_event_interruptible(lirc_rx51->wqueue, lirc_rx51->wbuf_index < 0);
279
280 /* We can sleep again */
281 lirc_rx51->pdata->set_max_mpu_wakeup_lat(lirc_rx51->dev, -1);
282
283 return n;
284}
285
286static long lirc_rx51_ioctl(struct file *filep,
287 unsigned int cmd, unsigned long arg)
288{
289 int result;
290 unsigned long value;
291 unsigned int ivalue;
292 struct lirc_rx51 *lirc_rx51 = filep->private_data;
293
294 switch (cmd) {
295 case LIRC_GET_SEND_MODE:
296 result = put_user(LIRC_MODE_PULSE, (unsigned long *)arg);
297 if (result)
298 return result;
299 break;
300
301 case LIRC_SET_SEND_MODE:
302 result = get_user(value, (unsigned long *)arg);
303 if (result)
304 return result;
305
306 /* only LIRC_MODE_PULSE supported */
307 if (value != LIRC_MODE_PULSE)
308 return -ENOSYS;
309 break;
310
311 case LIRC_GET_REC_MODE:
312 result = put_user(0, (unsigned long *) arg);
313 if (result)
314 return result;
315 break;
316
317 case LIRC_GET_LENGTH:
318 return -ENOSYS;
319 break;
320
321 case LIRC_SET_SEND_DUTY_CYCLE:
322 result = get_user(ivalue, (unsigned int *) arg);
323 if (result)
324 return result;
325
326 if (ivalue <= 0 || ivalue > 100) {
327 dev_err(lirc_rx51->dev, ": invalid duty cycle %d\n",
328 ivalue);
329 return -EINVAL;
330 }
331
332 lirc_rx51->duty_cycle = ivalue;
333 break;
334
335 case LIRC_SET_SEND_CARRIER:
336 result = get_user(ivalue, (unsigned int *) arg);
337 if (result)
338 return result;
339
340 if (ivalue > 500000 || ivalue < 20000) {
341 dev_err(lirc_rx51->dev, ": invalid carrier freq %d\n",
342 ivalue);
343 return -EINVAL;
344 }
345
346 lirc_rx51->freq = ivalue;
347 break;
348
349 case LIRC_GET_FEATURES:
350 result = put_user(LIRC_RX51_DRIVER_FEATURES,
351 (unsigned long *) arg);
352 if (result)
353 return result;
354 break;
355
356 default:
357 return -ENOIOCTLCMD;
358 }
359
360 return 0;
361}
362
363static int lirc_rx51_open(struct inode *inode, struct file *file)
364{
365 struct lirc_rx51 *lirc_rx51 = lirc_get_pdata(file);
366 BUG_ON(!lirc_rx51);
367
368 file->private_data = lirc_rx51;
369
370 if (test_and_set_bit(1, &lirc_rx51->device_is_open))
371 return -EBUSY;
372
373 return lirc_rx51_init_port(lirc_rx51);
374}
375
376static int lirc_rx51_release(struct inode *inode, struct file *file)
377{
378 struct lirc_rx51 *lirc_rx51 = file->private_data;
379
380 lirc_rx51_free_port(lirc_rx51);
381
382 clear_bit(1, &lirc_rx51->device_is_open);
383
384 return 0;
385}
386
387static struct lirc_rx51 lirc_rx51 = {
388 .freq = 38000,
389 .duty_cycle = 50,
390 .wbuf_index = -1,
391};
392
393static const struct file_operations lirc_fops = {
394 .owner = THIS_MODULE,
395 .write = lirc_rx51_write,
396 .unlocked_ioctl = lirc_rx51_ioctl,
397 .read = lirc_dev_fop_read,
398 .poll = lirc_dev_fop_poll,
399 .open = lirc_rx51_open,
400 .release = lirc_rx51_release,
401};
402
403static struct lirc_driver lirc_rx51_driver = {
404 .name = DRIVER_NAME,
405 .minor = -1,
406 .code_length = 1,
407 .data = &lirc_rx51,
408 .fops = &lirc_fops,
409 .owner = THIS_MODULE,
410};
411
412#ifdef CONFIG_PM
413
414static int lirc_rx51_suspend(struct platform_device *dev, pm_message_t state)
415{
416 /*
417 * In case the device is still open, do not suspend. Normally
418 * this should not be a problem as lircd only keeps the device
419 * open only for short periods of time. We also don't want to
420 * get involved with race conditions that might happen if we
421 * were in a middle of a transmit. Thus, we defer any suspend
422 * actions until transmit has completed.
423 */
424 if (test_and_set_bit(1, &lirc_rx51.device_is_open))
425 return -EAGAIN;
426
427 clear_bit(1, &lirc_rx51.device_is_open);
428
429 return 0;
430}
431
432static int lirc_rx51_resume(struct platform_device *dev)
433{
434 return 0;
435}
436
437#else
438
439#define lirc_rx51_suspend NULL
440#define lirc_rx51_resume NULL
441
442#endif /* CONFIG_PM */
443
444static int __devinit lirc_rx51_probe(struct platform_device *dev)
445{
446 lirc_rx51_driver.features = LIRC_RX51_DRIVER_FEATURES;
447 lirc_rx51.pdata = dev->dev.platform_data;
448 lirc_rx51.pwm_timer_num = lirc_rx51.pdata->pwm_timer;
449 lirc_rx51.dev = &dev->dev;
450 lirc_rx51_driver.dev = &dev->dev;
451 lirc_rx51_driver.minor = lirc_register_driver(&lirc_rx51_driver);
452 init_waitqueue_head(&lirc_rx51.wqueue);
453
454 if (lirc_rx51_driver.minor < 0) {
455 dev_err(lirc_rx51.dev, ": lirc_register_driver failed: %d\n",
456 lirc_rx51_driver.minor);
457 return lirc_rx51_driver.minor;
458 }
459 dev_info(lirc_rx51.dev, "registration ok, minor: %d, pwm: %d\n",
460 lirc_rx51_driver.minor, lirc_rx51.pwm_timer_num);
461
462 return 0;
463}
464
465static int __exit lirc_rx51_remove(struct platform_device *dev)
466{
467 return lirc_unregister_driver(lirc_rx51_driver.minor);
468}
469
470struct platform_driver lirc_rx51_platform_driver = {
471 .probe = lirc_rx51_probe,
472 .remove = __exit_p(lirc_rx51_remove),
473 .suspend = lirc_rx51_suspend,
474 .resume = lirc_rx51_resume,
475 .remove = __exit_p(lirc_rx51_remove),
476 .driver = {
477 .name = DRIVER_NAME,
478 .owner = THIS_MODULE,
479 },
480};
481
482static int __init lirc_rx51_init(void)
483{
484 return platform_driver_register(&lirc_rx51_platform_driver);
485}
486module_init(lirc_rx51_init);
487
488static void __exit lirc_rx51_exit(void)
489{
490 platform_driver_unregister(&lirc_rx51_platform_driver);
491}
492module_exit(lirc_rx51_exit);
493
494MODULE_DESCRIPTION("LIRC TX driver for Nokia RX51");
495MODULE_AUTHOR("Nokia Corporation");
496MODULE_LICENSE("GPL");
diff --git a/include/media/ir-rx51.h b/include/media/ir-rx51.h
new file mode 100644
index 000000000000..104aa892f31b
--- /dev/null
+++ b/include/media/ir-rx51.h
@@ -0,0 +1,10 @@
1#ifndef _LIRC_RX51_H
2#define _LIRC_RX51_H
3
4struct lirc_rx51_platform_data {
5 int pwm_timer;
6
7 int(*set_max_mpu_wakeup_lat)(struct device *dev, long t);
8};
9
10#endif