diff options
-rw-r--r-- | drivers/media/rc/gpio-ir-recv.c | 53 |
1 files changed, 53 insertions, 0 deletions
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c index a20413008c3c..22e524b69806 100644 --- a/drivers/media/rc/gpio-ir-recv.c +++ b/drivers/media/rc/gpio-ir-recv.c @@ -11,6 +11,8 @@ #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/pm_qos.h> #include <linux/irq.h> #include <media/rc-core.h> @@ -20,17 +22,38 @@ struct gpio_rc_dev { struct rc_dev *rcdev; struct gpio_desc *gpiod; int irq; + struct device *pmdev; + struct pm_qos_request qos; }; static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id) { int val; struct gpio_rc_dev *gpio_dev = dev_id; + struct device *pmdev = gpio_dev->pmdev; + + /* + * For some cpuidle systems, not all: + * Respond to interrupt taking more latency when cpu in idle. + * Invoke asynchronous pm runtime get from interrupt context, + * this may introduce a millisecond delay to call resume callback, + * where to disable cpuilde. + * + * Two issues lead to fail to decode first frame, one is latency to + * respond to interrupt, another is delay introduced by async api. + */ + if (pmdev) + pm_runtime_get(pmdev); val = gpiod_get_value(gpio_dev->gpiod); if (val >= 0) ir_raw_event_store_edge(gpio_dev->rcdev, val == 1); + if (pmdev) { + pm_runtime_mark_last_busy(pmdev); + pm_runtime_put_autosuspend(pmdev); + } + return IRQ_HANDLED; } @@ -40,6 +63,7 @@ static int gpio_ir_recv_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct gpio_rc_dev *gpio_dev; struct rc_dev *rcdev; + u32 period = 0; int rc; if (!np) @@ -90,6 +114,15 @@ static int gpio_ir_recv_probe(struct platform_device *pdev) return rc; } + of_property_read_u32(np, "linux,autosuspend-period", &period); + if (period) { + gpio_dev->pmdev = dev; + pm_runtime_set_autosuspend_delay(dev, period); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + } + platform_set_drvdata(pdev, gpio_dev); return devm_request_irq(dev, gpio_dev->irq, gpio_ir_recv_irq, @@ -122,9 +155,29 @@ static int gpio_ir_recv_resume(struct device *dev) return 0; } +static int gpio_ir_recv_runtime_suspend(struct device *dev) +{ + struct gpio_rc_dev *gpio_dev = dev_get_drvdata(dev); + + cpu_latency_qos_remove_request(&gpio_dev->qos); + + return 0; +} + +static int gpio_ir_recv_runtime_resume(struct device *dev) +{ + struct gpio_rc_dev *gpio_dev = dev_get_drvdata(dev); + + cpu_latency_qos_add_request(&gpio_dev->qos, 0); + + return 0; +} + static const struct dev_pm_ops gpio_ir_recv_pm_ops = { .suspend = gpio_ir_recv_suspend, .resume = gpio_ir_recv_resume, + .runtime_suspend = gpio_ir_recv_runtime_suspend, + .runtime_resume = gpio_ir_recv_runtime_resume, }; #endif |