linux/drivers/media/rc/gpio-ir-tx.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) 2017 Sean Young <sean@mess.org>
   4 */
   5
   6#include <linux/kernel.h>
   7#include <linux/module.h>
   8#include <linux/gpio/consumer.h>
   9#include <linux/delay.h>
  10#include <linux/slab.h>
  11#include <linux/of.h>
  12#include <linux/platform_device.h>
  13#include <media/rc-core.h>
  14
  15#define DRIVER_NAME     "gpio-ir-tx"
  16#define DEVICE_NAME     "GPIO IR Bit Banging Transmitter"
  17
  18struct gpio_ir {
  19        struct gpio_desc *gpio;
  20        unsigned int carrier;
  21        unsigned int duty_cycle;
  22};
  23
  24static const struct of_device_id gpio_ir_tx_of_match[] = {
  25        { .compatible = "gpio-ir-tx", },
  26        { },
  27};
  28MODULE_DEVICE_TABLE(of, gpio_ir_tx_of_match);
  29
  30static int gpio_ir_tx_set_duty_cycle(struct rc_dev *dev, u32 duty_cycle)
  31{
  32        struct gpio_ir *gpio_ir = dev->priv;
  33
  34        gpio_ir->duty_cycle = duty_cycle;
  35
  36        return 0;
  37}
  38
  39static int gpio_ir_tx_set_carrier(struct rc_dev *dev, u32 carrier)
  40{
  41        struct gpio_ir *gpio_ir = dev->priv;
  42
  43        if (carrier > 500000)
  44                return -EINVAL;
  45
  46        gpio_ir->carrier = carrier;
  47
  48        return 0;
  49}
  50
  51static void gpio_ir_tx_unmodulated(struct gpio_ir *gpio_ir, uint *txbuf,
  52                                   uint count)
  53{
  54        ktime_t edge;
  55        s32 delta;
  56        int i;
  57
  58        local_irq_disable();
  59
  60        edge = ktime_get();
  61
  62        for (i = 0; i < count; i++) {
  63                gpiod_set_value(gpio_ir->gpio, !(i % 2));
  64
  65                edge = ktime_add_us(edge, txbuf[i]);
  66                delta = ktime_us_delta(edge, ktime_get());
  67                if (delta > 0)
  68                        udelay(delta);
  69        }
  70
  71        gpiod_set_value(gpio_ir->gpio, 0);
  72}
  73
  74static void gpio_ir_tx_modulated(struct gpio_ir *gpio_ir, uint *txbuf,
  75                                 uint count)
  76{
  77        ktime_t edge;
  78        /*
  79         * delta should never exceed 0.5 seconds (IR_MAX_DURATION) and on
  80         * m68k ndelay(s64) does not compile; so use s32 rather than s64.
  81         */
  82        s32 delta;
  83        int i;
  84        unsigned int pulse, space;
  85
  86        /* Ensure the dividend fits into 32 bit */
  87        pulse = DIV_ROUND_CLOSEST(gpio_ir->duty_cycle * (NSEC_PER_SEC / 100),
  88                                  gpio_ir->carrier);
  89        space = DIV_ROUND_CLOSEST((100 - gpio_ir->duty_cycle) *
  90                                  (NSEC_PER_SEC / 100), gpio_ir->carrier);
  91
  92        local_irq_disable();
  93
  94        edge = ktime_get();
  95
  96        for (i = 0; i < count; i++) {
  97                if (i % 2) {
  98                        // space
  99                        edge = ktime_add_us(edge, txbuf[i]);
 100                        delta = ktime_us_delta(edge, ktime_get());
 101                        if (delta > 0)
 102                                udelay(delta);
 103                } else {
 104                        // pulse
 105                        ktime_t last = ktime_add_us(edge, txbuf[i]);
 106
 107                        while (ktime_before(ktime_get(), last)) {
 108                                gpiod_set_value(gpio_ir->gpio, 1);
 109                                edge = ktime_add_ns(edge, pulse);
 110                                delta = ktime_to_ns(ktime_sub(edge,
 111                                                              ktime_get()));
 112                                if (delta > 0)
 113                                        ndelay(delta);
 114                                gpiod_set_value(gpio_ir->gpio, 0);
 115                                edge = ktime_add_ns(edge, space);
 116                                delta = ktime_to_ns(ktime_sub(edge,
 117                                                              ktime_get()));
 118                                if (delta > 0)
 119                                        ndelay(delta);
 120                        }
 121
 122                        edge = last;
 123                }
 124        }
 125}
 126
 127static int gpio_ir_tx(struct rc_dev *dev, unsigned int *txbuf,
 128                      unsigned int count)
 129{
 130        struct gpio_ir *gpio_ir = dev->priv;
 131        unsigned long flags;
 132
 133        local_irq_save(flags);
 134        if (gpio_ir->carrier)
 135                gpio_ir_tx_modulated(gpio_ir, txbuf, count);
 136        else
 137                gpio_ir_tx_unmodulated(gpio_ir, txbuf, count);
 138        local_irq_restore(flags);
 139
 140        return count;
 141}
 142
 143static int gpio_ir_tx_probe(struct platform_device *pdev)
 144{
 145        struct gpio_ir *gpio_ir;
 146        struct rc_dev *rcdev;
 147        int rc;
 148
 149        gpio_ir = devm_kmalloc(&pdev->dev, sizeof(*gpio_ir), GFP_KERNEL);
 150        if (!gpio_ir)
 151                return -ENOMEM;
 152
 153        rcdev = devm_rc_allocate_device(&pdev->dev, RC_DRIVER_IR_RAW_TX);
 154        if (!rcdev)
 155                return -ENOMEM;
 156
 157        gpio_ir->gpio = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW);
 158        if (IS_ERR(gpio_ir->gpio)) {
 159                if (PTR_ERR(gpio_ir->gpio) != -EPROBE_DEFER)
 160                        dev_err(&pdev->dev, "Failed to get gpio (%ld)\n",
 161                                PTR_ERR(gpio_ir->gpio));
 162                return PTR_ERR(gpio_ir->gpio);
 163        }
 164
 165        rcdev->priv = gpio_ir;
 166        rcdev->driver_name = DRIVER_NAME;
 167        rcdev->device_name = DEVICE_NAME;
 168        rcdev->tx_ir = gpio_ir_tx;
 169        rcdev->s_tx_duty_cycle = gpio_ir_tx_set_duty_cycle;
 170        rcdev->s_tx_carrier = gpio_ir_tx_set_carrier;
 171
 172        gpio_ir->carrier = 38000;
 173        gpio_ir->duty_cycle = 50;
 174
 175        rc = devm_rc_register_device(&pdev->dev, rcdev);
 176        if (rc < 0)
 177                dev_err(&pdev->dev, "failed to register rc device\n");
 178
 179        return rc;
 180}
 181
 182static struct platform_driver gpio_ir_tx_driver = {
 183        .probe  = gpio_ir_tx_probe,
 184        .driver = {
 185                .name   = DRIVER_NAME,
 186                .of_match_table = of_match_ptr(gpio_ir_tx_of_match),
 187        },
 188};
 189module_platform_driver(gpio_ir_tx_driver);
 190
 191MODULE_DESCRIPTION("GPIO IR Bit Banging Transmitter");
 192MODULE_AUTHOR("Sean Young <sean@mess.org>");
 193MODULE_LICENSE("GPL");
 194