brintos

brintos / linux-shallow public Read only

0
0
Text · 5.3 KiB · b29a1a9 Raw
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