217 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.3 */4 5#include <linux/kernel.h>6#include <linux/init.h>7#include <linux/module.h>8#include <linux/interrupt.h>9#include <linux/gpio/consumer.h>10#include <linux/slab.h>11#include <linux/of.h>12#include <linux/platform_device.h>13#include <linux/pm_runtime.h>14#include <linux/pm_qos.h>15#include <linux/irq.h>16#include <media/rc-core.h>17 18#define GPIO_IR_DEVICE_NAME "gpio_ir_recv"19 20struct gpio_rc_dev {21 struct rc_dev *rcdev;22 struct gpio_desc *gpiod;23 int irq;24 struct device *pmdev;25 struct pm_qos_request qos;26};27 28static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id)29{30 int val;31 struct gpio_rc_dev *gpio_dev = dev_id;32 struct device *pmdev = gpio_dev->pmdev;33 34 /*35 * For some cpuidle systems, not all:36 * Respond to interrupt taking more latency when cpu in idle.37 * Invoke asynchronous pm runtime get from interrupt context,38 * this may introduce a millisecond delay to call resume callback,39 * where to disable cpuilde.40 *41 * Two issues lead to fail to decode first frame, one is latency to42 * respond to interrupt, another is delay introduced by async api.43 */44 if (pmdev)45 pm_runtime_get(pmdev);46 47 val = gpiod_get_value(gpio_dev->gpiod);48 if (val >= 0)49 ir_raw_event_store_edge(gpio_dev->rcdev, val == 1);50 51 if (pmdev) {52 pm_runtime_mark_last_busy(pmdev);53 pm_runtime_put_autosuspend(pmdev);54 }55 56 return IRQ_HANDLED;57}58 59static int gpio_ir_recv_probe(struct platform_device *pdev)60{61 struct device *dev = &pdev->dev;62 struct device_node *np = dev->of_node;63 struct gpio_rc_dev *gpio_dev;64 struct rc_dev *rcdev;65 u32 period = 0;66 int rc;67 68 if (!np)69 return -ENODEV;70 71 gpio_dev = devm_kzalloc(dev, sizeof(*gpio_dev), GFP_KERNEL);72 if (!gpio_dev)73 return -ENOMEM;74 75 gpio_dev->gpiod = devm_gpiod_get(dev, NULL, GPIOD_IN);76 if (IS_ERR(gpio_dev->gpiod))77 return dev_err_probe(dev, PTR_ERR(gpio_dev->gpiod),78 "error getting gpio\n");79 gpio_dev->irq = gpiod_to_irq(gpio_dev->gpiod);80 if (gpio_dev->irq < 0)81 return gpio_dev->irq;82 83 rcdev = devm_rc_allocate_device(dev, RC_DRIVER_IR_RAW);84 if (!rcdev)85 return -ENOMEM;86 87 rcdev->priv = gpio_dev;88 rcdev->device_name = GPIO_IR_DEVICE_NAME;89 rcdev->input_phys = GPIO_IR_DEVICE_NAME "/input0";90 rcdev->input_id.bustype = BUS_HOST;91 rcdev->input_id.vendor = 0x0001;92 rcdev->input_id.product = 0x0001;93 rcdev->input_id.version = 0x0100;94 rcdev->dev.parent = dev;95 rcdev->driver_name = KBUILD_MODNAME;96 rcdev->min_timeout = 1;97 rcdev->timeout = IR_DEFAULT_TIMEOUT;98 rcdev->max_timeout = 10 * IR_DEFAULT_TIMEOUT;99 rcdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;100 rcdev->map_name = of_get_property(np, "linux,rc-map-name", NULL);101 if (!rcdev->map_name)102 rcdev->map_name = RC_MAP_EMPTY;103 104 gpio_dev->rcdev = rcdev;105 if (of_property_read_bool(np, "wakeup-source"))106 device_init_wakeup(dev, true);107 108 rc = devm_rc_register_device(dev, rcdev);109 if (rc < 0) {110 dev_err(dev, "failed to register rc device (%d)\n", rc);111 return rc;112 }113 114 of_property_read_u32(np, "linux,autosuspend-period", &period);115 if (period) {116 gpio_dev->pmdev = dev;117 pm_runtime_set_autosuspend_delay(dev, period);118 pm_runtime_use_autosuspend(dev);119 pm_runtime_set_suspended(dev);120 pm_runtime_enable(dev);121 }122 123 platform_set_drvdata(pdev, gpio_dev);124 125 return devm_request_irq(dev, gpio_dev->irq, gpio_ir_recv_irq,126 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,127 "gpio-ir-recv-irq", gpio_dev);128}129 130static void gpio_ir_recv_remove(struct platform_device *pdev)131{132 struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);133 struct device *pmdev = gpio_dev->pmdev;134 135 if (pmdev) {136 pm_runtime_get_sync(pmdev);137 cpu_latency_qos_remove_request(&gpio_dev->qos);138 139 pm_runtime_disable(pmdev);140 pm_runtime_put_noidle(pmdev);141 pm_runtime_set_suspended(pmdev);142 }143}144 145#ifdef CONFIG_PM146static int gpio_ir_recv_suspend(struct device *dev)147{148 struct gpio_rc_dev *gpio_dev = dev_get_drvdata(dev);149 150 if (device_may_wakeup(dev))151 enable_irq_wake(gpio_dev->irq);152 else153 disable_irq(gpio_dev->irq);154 155 return 0;156}157 158static int gpio_ir_recv_resume(struct device *dev)159{160 struct gpio_rc_dev *gpio_dev = dev_get_drvdata(dev);161 162 if (device_may_wakeup(dev))163 disable_irq_wake(gpio_dev->irq);164 else165 enable_irq(gpio_dev->irq);166 167 return 0;168}169 170static int gpio_ir_recv_runtime_suspend(struct device *dev)171{172 struct gpio_rc_dev *gpio_dev = dev_get_drvdata(dev);173 174 cpu_latency_qos_remove_request(&gpio_dev->qos);175 176 return 0;177}178 179static int gpio_ir_recv_runtime_resume(struct device *dev)180{181 struct gpio_rc_dev *gpio_dev = dev_get_drvdata(dev);182 183 cpu_latency_qos_add_request(&gpio_dev->qos, 0);184 185 return 0;186}187 188static const struct dev_pm_ops gpio_ir_recv_pm_ops = {189 .suspend = gpio_ir_recv_suspend,190 .resume = gpio_ir_recv_resume,191 .runtime_suspend = gpio_ir_recv_runtime_suspend,192 .runtime_resume = gpio_ir_recv_runtime_resume,193};194#endif195 196static const struct of_device_id gpio_ir_recv_of_match[] = {197 { .compatible = "gpio-ir-receiver", },198 { },199};200MODULE_DEVICE_TABLE(of, gpio_ir_recv_of_match);201 202static struct platform_driver gpio_ir_recv_driver = {203 .probe = gpio_ir_recv_probe,204 .remove_new = gpio_ir_recv_remove,205 .driver = {206 .name = KBUILD_MODNAME,207 .of_match_table = gpio_ir_recv_of_match,208#ifdef CONFIG_PM209 .pm = &gpio_ir_recv_pm_ops,210#endif211 },212};213module_platform_driver(gpio_ir_recv_driver);214 215MODULE_DESCRIPTION("GPIO IR Receiver driver");216MODULE_LICENSE("GPL v2");217