After commit ea8912b788f8 ("media: gpio-ir-tx: improve precision of transmitted signal due to scheduling"), the gpio-ir-tx driver disables interrupts for the entire duration of an IR frame, which is 500ms. Experimentation on an raspberry pi 3 model b under various loads shows that sleeping can be done using usleep_range() for periods > 40us. Fixes: ea8912b788f8 ("media: gpio-ir-tx: improve precision of transmitted signal due to scheduling") Link: https://lkml.org/lkml/2020/9/2/304 Suggested-by: Pavel Machek <pavel@xxxxxxx> Signed-off-by: Sean Young <sean@xxxxxxxx> --- drivers/media/rc/gpio-ir-tx.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/media/rc/gpio-ir-tx.c b/drivers/media/rc/gpio-ir-tx.c index c6cd2e6d8e65..be77c5b0d9d0 100644 --- a/drivers/media/rc/gpio-ir-tx.c +++ b/drivers/media/rc/gpio-ir-tx.c @@ -51,11 +51,12 @@ static int gpio_ir_tx_set_carrier(struct rc_dev *dev, u32 carrier) static void gpio_ir_tx_unmodulated(struct gpio_ir *gpio_ir, uint *txbuf, uint count) { + unsigned long flags; ktime_t edge; s32 delta; int i; - local_irq_disable(); + local_irq_save(flags); edge = ktime_get(); @@ -64,16 +65,25 @@ static void gpio_ir_tx_unmodulated(struct gpio_ir *gpio_ir, uint *txbuf, edge = ktime_add_us(edge, txbuf[i]); delta = ktime_us_delta(edge, ktime_get()); + if (delta > 50) { + local_irq_restore(flags); + usleep_range(delta - 40, delta - 40); + local_irq_save(flags); + delta = ktime_us_delta(edge, ktime_get()); + } if (delta > 0) udelay(delta); } gpiod_set_value(gpio_ir->gpio, 0); + + local_irq_restore(flags); } static void gpio_ir_tx_modulated(struct gpio_ir *gpio_ir, uint *txbuf, uint count) { + unsigned long flags; ktime_t edge; /* * delta should never exceed 0.5 seconds (IR_MAX_DURATION) and on @@ -89,7 +99,7 @@ static void gpio_ir_tx_modulated(struct gpio_ir *gpio_ir, uint *txbuf, space = DIV_ROUND_CLOSEST((100 - gpio_ir->duty_cycle) * (NSEC_PER_SEC / 100), gpio_ir->carrier); - local_irq_disable(); + local_irq_save(flags); edge = ktime_get(); @@ -98,6 +108,12 @@ static void gpio_ir_tx_modulated(struct gpio_ir *gpio_ir, uint *txbuf, // space edge = ktime_add_us(edge, txbuf[i]); delta = ktime_us_delta(edge, ktime_get()); + if (delta > 50) { + local_irq_restore(flags); + usleep_range(delta - 40, delta - 40); + local_irq_save(flags); + delta = ktime_us_delta(edge, ktime_get()); + } if (delta > 0) udelay(delta); } else { @@ -122,20 +138,19 @@ static void gpio_ir_tx_modulated(struct gpio_ir *gpio_ir, uint *txbuf, edge = last; } } + + local_irq_restore(flags); } static int gpio_ir_tx(struct rc_dev *dev, unsigned int *txbuf, unsigned int count) { struct gpio_ir *gpio_ir = dev->priv; - unsigned long flags; - local_irq_save(flags); if (gpio_ir->carrier) gpio_ir_tx_modulated(gpio_ir, txbuf, count); else gpio_ir_tx_unmodulated(gpio_ir, txbuf, count); - local_irq_restore(flags); return count; } -- 2.26.2