blob: 8a999683486aedaab0010eb45e7d5e9d9a1efdc6 [file] [log] [blame]
Ravi Kumar Vbd98cda2012-10-12 20:39:58 +05301/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +05302 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/kernel.h>
14#include <linux/init.h>
15#include <linux/module.h>
16#include <linux/interrupt.h>
17#include <linux/gpio.h>
18#include <linux/slab.h>
19#include <linux/platform_device.h>
20#include <linux/irq.h>
Ravi Kumar Vbd98cda2012-10-12 20:39:58 +053021#include <linux/pm_qos.h>
22#include <linux/timer.h>
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +053023#include <media/rc-core.h>
24#include <media/gpio-ir-recv.h>
25
Ravi Kumar Ve25bde42012-02-28 01:51:40 -030026#define GPIO_IR_DRIVER_NAME "gpio-rc-recv"
27#define GPIO_IR_DEVICE_NAME "gpio_ir_recv"
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +053028
Ravi Kumar Vbd98cda2012-10-12 20:39:58 +053029static int gpio_ir_timeout = 200;
30module_param_named(gpio_ir_timeout, gpio_ir_timeout, int, 0664);
31
32static int __init gpio_ir_timeout_setup(char *p)
33{
34 gpio_ir_timeout = memparse(p, NULL);
35 return 0;
36}
37
38early_param("gpio_ir_timeout", gpio_ir_timeout_setup);
39
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +053040struct gpio_rc_dev {
41 struct rc_dev *rcdev;
Ravi Kumar Vbd98cda2012-10-12 20:39:58 +053042 struct pm_qos_request pm_qos_req;
43 struct timer_list gpio_ir_timer;
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +053044 unsigned int gpio_nr;
45 bool active_low;
Ravi Kumar V507b9622012-06-15 17:25:44 +053046 int can_sleep;
Ravi Kumar Vbd98cda2012-10-12 20:39:58 +053047 bool can_wakeup;
48 bool pm_qos_vote;
49 int gpio_irq_latency;
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +053050};
51
Ravi Kumar Ve25bde42012-02-28 01:51:40 -030052static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id)
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +053053{
Ravi Kumar Ve25bde42012-02-28 01:51:40 -030054 struct gpio_rc_dev *gpio_dev = dev_id;
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +053055 unsigned int gval;
56 int rc = 0;
57 enum raw_event_type type = IR_SPACE;
58
Ravi Kumar Vbd98cda2012-10-12 20:39:58 +053059 if (!gpio_dev->pm_qos_vote && gpio_dev->can_wakeup) {
60 gpio_dev->pm_qos_vote = 1;
61 pm_qos_update_request(&gpio_dev->pm_qos_req,
62 gpio_dev->gpio_irq_latency);
63 }
64
Ravi Kumar V507b9622012-06-15 17:25:44 +053065 if (gpio_dev->can_sleep)
66 gval = gpio_get_value_cansleep(gpio_dev->gpio_nr);
67 else
68 gval = gpio_get_value(gpio_dev->gpio_nr);
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +053069
70 if (gval < 0)
71 goto err_get_value;
72
73 if (gpio_dev->active_low)
74 gval = !gval;
75
76 if (gval == 1)
77 type = IR_PULSE;
78
79 rc = ir_raw_event_store_edge(gpio_dev->rcdev, type);
80 if (rc < 0)
81 goto err_get_value;
82
83 ir_raw_event_handle(gpio_dev->rcdev);
84
Ravi Kumar Vbd98cda2012-10-12 20:39:58 +053085 if (gpio_dev->can_wakeup)
86 mod_timer(&gpio_dev->gpio_ir_timer,
87 jiffies + msecs_to_jiffies(gpio_ir_timeout));
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +053088err_get_value:
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +053089 return IRQ_HANDLED;
90}
91
Ravi Kumar Vbd98cda2012-10-12 20:39:58 +053092static void gpio_ir_timer(unsigned long data)
93{
94 struct gpio_rc_dev *gpio_dev = (struct gpio_rc_dev *)data;
95
96 pm_qos_update_request(&gpio_dev->pm_qos_req, PM_QOS_DEFAULT_VALUE);
97 pm_qos_request_active(&gpio_dev->pm_qos_req);
98 gpio_dev->pm_qos_vote = 0;
99}
100
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +0530101static int __devinit gpio_ir_recv_probe(struct platform_device *pdev)
102{
103 struct gpio_rc_dev *gpio_dev;
104 struct rc_dev *rcdev;
105 const struct gpio_ir_recv_platform_data *pdata =
106 pdev->dev.platform_data;
Ravi Kumar Ve25bde42012-02-28 01:51:40 -0300107 int rc;
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +0530108
109 if (!pdata)
110 return -EINVAL;
111
112 if (pdata->gpio_nr < 0)
113 return -EINVAL;
114
115 gpio_dev = kzalloc(sizeof(struct gpio_rc_dev), GFP_KERNEL);
116 if (!gpio_dev)
117 return -ENOMEM;
118
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +0530119 rcdev = rc_allocate_device();
120 if (!rcdev) {
121 rc = -ENOMEM;
122 goto err_allocate_device;
123 }
124
125 rcdev->driver_type = RC_DRIVER_IR_RAW;
Ravi Kumar Ve25bde42012-02-28 01:51:40 -0300126 rcdev->allowed_protos = RC_TYPE_ALL;
127 rcdev->input_name = GPIO_IR_DEVICE_NAME;
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +0530128 rcdev->input_id.bustype = BUS_HOST;
Ravi Kumar Ve25bde42012-02-28 01:51:40 -0300129 rcdev->driver_name = GPIO_IR_DRIVER_NAME;
Ravi Kumar V9d229f82012-05-10 12:38:05 +0530130 rcdev->map_name = RC_MAP_SAMSUNG_NECX;
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +0530131
132 gpio_dev->rcdev = rcdev;
133 gpio_dev->gpio_nr = pdata->gpio_nr;
134 gpio_dev->active_low = pdata->active_low;
Ravi Kumar Vbd98cda2012-10-12 20:39:58 +0530135 gpio_dev->can_wakeup = pdata->can_wakeup;
136 gpio_dev->gpio_irq_latency = pdata->swfi_latency + 1;
137 gpio_dev->pm_qos_vote = 0;
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +0530138
139 rc = gpio_request(pdata->gpio_nr, "gpio-ir-recv");
140 if (rc < 0)
141 goto err_gpio_request;
Ravi Kumar V507b9622012-06-15 17:25:44 +0530142
143 gpio_dev->can_sleep = gpio_cansleep(pdata->gpio_nr);
144
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +0530145 rc = gpio_direction_input(pdata->gpio_nr);
146 if (rc < 0)
147 goto err_gpio_direction_input;
148
149 rc = rc_register_device(rcdev);
150 if (rc < 0) {
151 dev_err(&pdev->dev, "failed to register rc device\n");
152 goto err_register_rc_device;
153 }
154
155 platform_set_drvdata(pdev, gpio_dev);
156
Ravi Kumar Ve25bde42012-02-28 01:51:40 -0300157 rc = request_any_context_irq(gpio_to_irq(pdata->gpio_nr),
158 gpio_ir_recv_irq,
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +0530159 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
Ravi Kumar Ve25bde42012-02-28 01:51:40 -0300160 "gpio-ir-recv-irq", gpio_dev);
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +0530161 if (rc < 0)
162 goto err_request_irq;
163
Ravi Kumar Vbd98cda2012-10-12 20:39:58 +0530164 if (gpio_dev->can_wakeup) {
165 pm_qos_add_request(&gpio_dev->pm_qos_req,
166 PM_QOS_CPU_DMA_LATENCY,
167 PM_QOS_DEFAULT_VALUE);
168 device_init_wakeup(&pdev->dev, pdata->can_wakeup);
169 setup_timer(&gpio_dev->gpio_ir_timer, gpio_ir_timer,
170 (unsigned long)gpio_dev);
171 }
Trilok Sonicc1f41d2012-04-19 22:24:17 +0530172
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +0530173 return 0;
174
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +0530175err_request_irq:
176 platform_set_drvdata(pdev, NULL);
177 rc_unregister_device(rcdev);
178err_register_rc_device:
179err_gpio_direction_input:
180 gpio_free(pdata->gpio_nr);
181err_gpio_request:
182 rc_free_device(rcdev);
183 rcdev = NULL;
184err_allocate_device:
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +0530185 kfree(gpio_dev);
186 return rc;
187}
188
189static int __devexit gpio_ir_recv_remove(struct platform_device *pdev)
190{
191 struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
192
Ravi Kumar Vbd98cda2012-10-12 20:39:58 +0530193 if (gpio_dev->can_wakeup) {
194 del_timer_sync(&gpio_dev->gpio_ir_timer);
195 pm_qos_remove_request(&gpio_dev->pm_qos_req);
196 }
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +0530197 free_irq(gpio_to_irq(gpio_dev->gpio_nr), gpio_dev);
198 platform_set_drvdata(pdev, NULL);
199 rc_unregister_device(gpio_dev->rcdev);
200 gpio_free(gpio_dev->gpio_nr);
201 rc_free_device(gpio_dev->rcdev);
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +0530202 kfree(gpio_dev);
203 return 0;
204}
205
Ravi Kumar Ve25bde42012-02-28 01:51:40 -0300206#ifdef CONFIG_PM
207static int gpio_ir_recv_suspend(struct device *dev)
208{
209 struct platform_device *pdev = to_platform_device(dev);
210 struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
211
212 if (device_may_wakeup(dev))
213 enable_irq_wake(gpio_to_irq(gpio_dev->gpio_nr));
214 else
215 disable_irq(gpio_to_irq(gpio_dev->gpio_nr));
216
217 return 0;
218}
219
220static int gpio_ir_recv_resume(struct device *dev)
221{
222 struct platform_device *pdev = to_platform_device(dev);
223 struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
224
225 if (device_may_wakeup(dev))
226 disable_irq_wake(gpio_to_irq(gpio_dev->gpio_nr));
227 else
228 enable_irq(gpio_to_irq(gpio_dev->gpio_nr));
229
230 return 0;
231}
232
233static const struct dev_pm_ops gpio_ir_recv_pm_ops = {
234 .suspend = gpio_ir_recv_suspend,
235 .resume = gpio_ir_recv_resume,
236};
237#endif
238
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +0530239static struct platform_driver gpio_ir_recv_driver = {
240 .probe = gpio_ir_recv_probe,
241 .remove = __devexit_p(gpio_ir_recv_remove),
242 .driver = {
Ravi Kumar Ve25bde42012-02-28 01:51:40 -0300243 .name = GPIO_IR_DRIVER_NAME,
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +0530244 .owner = THIS_MODULE,
Ravi Kumar Ve25bde42012-02-28 01:51:40 -0300245#ifdef CONFIG_PM
246 .pm = &gpio_ir_recv_pm_ops,
247#endif
Ankush Khandelwal3fb6bc22012-01-31 19:09:58 +0530248 },
249};
250
251static int __init gpio_ir_recv_init(void)
252{
253 return platform_driver_register(&gpio_ir_recv_driver);
254}
255module_init(gpio_ir_recv_init);
256
257static void __exit gpio_ir_recv_exit(void)
258{
259 platform_driver_unregister(&gpio_ir_recv_driver);
260}
261module_exit(gpio_ir_recv_exit);
262
263MODULE_DESCRIPTION("GPIO IR Receiver driver");
264MODULE_LICENSE("GPL v2");