| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* Copyright (c) 2012, Code Aurora Forum. All rights reserved. |
|---|
| 2 | | - * |
|---|
| 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 | 3 | */ |
|---|
| 12 | 4 | |
|---|
| 13 | 5 | #include <linux/kernel.h> |
|---|
| .. | .. |
|---|
| 19 | 11 | #include <linux/of.h> |
|---|
| 20 | 12 | #include <linux/of_gpio.h> |
|---|
| 21 | 13 | #include <linux/platform_device.h> |
|---|
| 14 | +#include <linux/pm_runtime.h> |
|---|
| 15 | +#include <linux/pm_qos.h> |
|---|
| 22 | 16 | #include <linux/irq.h> |
|---|
| 23 | 17 | #include <media/rc-core.h> |
|---|
| 24 | 18 | |
|---|
| .. | .. |
|---|
| 28 | 22 | struct rc_dev *rcdev; |
|---|
| 29 | 23 | struct gpio_desc *gpiod; |
|---|
| 30 | 24 | int irq; |
|---|
| 25 | + struct device *pmdev; |
|---|
| 26 | + struct pm_qos_request qos; |
|---|
| 31 | 27 | }; |
|---|
| 32 | 28 | |
|---|
| 33 | 29 | static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id) |
|---|
| 34 | 30 | { |
|---|
| 35 | 31 | int val; |
|---|
| 36 | 32 | struct gpio_rc_dev *gpio_dev = dev_id; |
|---|
| 33 | + struct device *pmdev = gpio_dev->pmdev; |
|---|
| 34 | + |
|---|
| 35 | + /* |
|---|
| 36 | + * For some cpuidle systems, not all: |
|---|
| 37 | + * Respond to interrupt taking more latency when cpu in idle. |
|---|
| 38 | + * Invoke asynchronous pm runtime get from interrupt context, |
|---|
| 39 | + * this may introduce a millisecond delay to call resume callback, |
|---|
| 40 | + * where to disable cpuilde. |
|---|
| 41 | + * |
|---|
| 42 | + * Two issues lead to fail to decode first frame, one is latency to |
|---|
| 43 | + * respond to interrupt, another is delay introduced by async api. |
|---|
| 44 | + */ |
|---|
| 45 | + if (pmdev) |
|---|
| 46 | + pm_runtime_get(pmdev); |
|---|
| 37 | 47 | |
|---|
| 38 | 48 | val = gpiod_get_value(gpio_dev->gpiod); |
|---|
| 39 | 49 | if (val >= 0) |
|---|
| 40 | 50 | ir_raw_event_store_edge(gpio_dev->rcdev, val == 1); |
|---|
| 51 | + |
|---|
| 52 | + if (pmdev) { |
|---|
| 53 | + pm_runtime_mark_last_busy(pmdev); |
|---|
| 54 | + pm_runtime_put_autosuspend(pmdev); |
|---|
| 55 | + } |
|---|
| 41 | 56 | |
|---|
| 42 | 57 | return IRQ_HANDLED; |
|---|
| 43 | 58 | } |
|---|
| .. | .. |
|---|
| 48 | 63 | struct device_node *np = dev->of_node; |
|---|
| 49 | 64 | struct gpio_rc_dev *gpio_dev; |
|---|
| 50 | 65 | struct rc_dev *rcdev; |
|---|
| 66 | + u32 period = 0; |
|---|
| 51 | 67 | int rc; |
|---|
| 52 | 68 | |
|---|
| 53 | 69 | if (!np) |
|---|
| .. | .. |
|---|
| 98 | 114 | return rc; |
|---|
| 99 | 115 | } |
|---|
| 100 | 116 | |
|---|
| 117 | + of_property_read_u32(np, "linux,autosuspend-period", &period); |
|---|
| 118 | + if (period) { |
|---|
| 119 | + gpio_dev->pmdev = dev; |
|---|
| 120 | + pm_runtime_set_autosuspend_delay(dev, period); |
|---|
| 121 | + pm_runtime_use_autosuspend(dev); |
|---|
| 122 | + pm_runtime_set_suspended(dev); |
|---|
| 123 | + pm_runtime_enable(dev); |
|---|
| 124 | + } |
|---|
| 125 | + |
|---|
| 101 | 126 | platform_set_drvdata(pdev, gpio_dev); |
|---|
| 102 | 127 | |
|---|
| 103 | 128 | return devm_request_irq(dev, gpio_dev->irq, gpio_ir_recv_irq, |
|---|
| .. | .. |
|---|
| 130 | 155 | return 0; |
|---|
| 131 | 156 | } |
|---|
| 132 | 157 | |
|---|
| 158 | +static int gpio_ir_recv_runtime_suspend(struct device *dev) |
|---|
| 159 | +{ |
|---|
| 160 | + struct gpio_rc_dev *gpio_dev = dev_get_drvdata(dev); |
|---|
| 161 | + |
|---|
| 162 | + cpu_latency_qos_remove_request(&gpio_dev->qos); |
|---|
| 163 | + |
|---|
| 164 | + return 0; |
|---|
| 165 | +} |
|---|
| 166 | + |
|---|
| 167 | +static int gpio_ir_recv_runtime_resume(struct device *dev) |
|---|
| 168 | +{ |
|---|
| 169 | + struct gpio_rc_dev *gpio_dev = dev_get_drvdata(dev); |
|---|
| 170 | + |
|---|
| 171 | + cpu_latency_qos_add_request(&gpio_dev->qos, 0); |
|---|
| 172 | + |
|---|
| 173 | + return 0; |
|---|
| 174 | +} |
|---|
| 175 | + |
|---|
| 133 | 176 | static const struct dev_pm_ops gpio_ir_recv_pm_ops = { |
|---|
| 134 | 177 | .suspend = gpio_ir_recv_suspend, |
|---|
| 135 | 178 | .resume = gpio_ir_recv_resume, |
|---|
| 179 | + .runtime_suspend = gpio_ir_recv_runtime_suspend, |
|---|
| 180 | + .runtime_resume = gpio_ir_recv_runtime_resume, |
|---|
| 136 | 181 | }; |
|---|
| 137 | 182 | #endif |
|---|
| 138 | 183 | |
|---|