1236b83a3SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
224d79ebcSSean Young /*
324d79ebcSSean Young * Copyright (C) 2017 Sean Young <sean@mess.org>
424d79ebcSSean Young */
524d79ebcSSean Young
624d79ebcSSean Young #include <linux/kernel.h>
724d79ebcSSean Young #include <linux/module.h>
824d79ebcSSean Young #include <linux/gpio/consumer.h>
924d79ebcSSean Young #include <linux/delay.h>
1024d79ebcSSean Young #include <linux/slab.h>
1124d79ebcSSean Young #include <linux/of.h>
1224d79ebcSSean Young #include <linux/platform_device.h>
1324d79ebcSSean Young #include <media/rc-core.h>
1424d79ebcSSean Young
1524d79ebcSSean Young #define DRIVER_NAME "gpio-ir-tx"
1624d79ebcSSean Young #define DEVICE_NAME "GPIO IR Bit Banging Transmitter"
1724d79ebcSSean Young
1824d79ebcSSean Young struct gpio_ir {
1924d79ebcSSean Young struct gpio_desc *gpio;
2024d79ebcSSean Young unsigned int carrier;
2124d79ebcSSean Young unsigned int duty_cycle;
2224d79ebcSSean Young };
2324d79ebcSSean Young
2424d79ebcSSean Young static const struct of_device_id gpio_ir_tx_of_match[] = {
2524d79ebcSSean Young { .compatible = "gpio-ir-tx", },
2624d79ebcSSean Young { },
2724d79ebcSSean Young };
2824d79ebcSSean Young MODULE_DEVICE_TABLE(of, gpio_ir_tx_of_match);
2924d79ebcSSean Young
gpio_ir_tx_set_duty_cycle(struct rc_dev * dev,u32 duty_cycle)3024d79ebcSSean Young static int gpio_ir_tx_set_duty_cycle(struct rc_dev *dev, u32 duty_cycle)
3124d79ebcSSean Young {
3224d79ebcSSean Young struct gpio_ir *gpio_ir = dev->priv;
3324d79ebcSSean Young
3424d79ebcSSean Young gpio_ir->duty_cycle = duty_cycle;
3524d79ebcSSean Young
3624d79ebcSSean Young return 0;
3724d79ebcSSean Young }
3824d79ebcSSean Young
gpio_ir_tx_set_carrier(struct rc_dev * dev,u32 carrier)3924d79ebcSSean Young static int gpio_ir_tx_set_carrier(struct rc_dev *dev, u32 carrier)
4024d79ebcSSean Young {
4124d79ebcSSean Young struct gpio_ir *gpio_ir = dev->priv;
4224d79ebcSSean Young
431195a28dSSean Young if (carrier > 500000)
4424d79ebcSSean Young return -EINVAL;
4524d79ebcSSean Young
4624d79ebcSSean Young gpio_ir->carrier = carrier;
4724d79ebcSSean Young
4824d79ebcSSean Young return 0;
4924d79ebcSSean Young }
5024d79ebcSSean Young
delay_until(ktime_t until)515ad05ecaSSean Young static void delay_until(ktime_t until)
525ad05ecaSSean Young {
535ad05ecaSSean Young /*
545ad05ecaSSean Young * delta should never exceed 0.5 seconds (IR_MAX_DURATION) and on
555ad05ecaSSean Young * m68k ndelay(s64) does not compile; so use s32 rather than s64.
565ad05ecaSSean Young */
575ad05ecaSSean Young s32 delta;
585ad05ecaSSean Young
595ad05ecaSSean Young while (true) {
605ad05ecaSSean Young delta = ktime_us_delta(until, ktime_get());
615ad05ecaSSean Young if (delta <= 0)
625ad05ecaSSean Young return;
635ad05ecaSSean Young
645ad05ecaSSean Young /* udelay more than 1ms may not work */
652a952d92SSean Young if (delta >= 1000) {
662a952d92SSean Young mdelay(delta / 1000);
672a952d92SSean Young continue;
682a952d92SSean Young }
692a952d92SSean Young
705ad05ecaSSean Young udelay(delta);
712a952d92SSean Young break;
725ad05ecaSSean Young }
735ad05ecaSSean Young }
745ad05ecaSSean Young
gpio_ir_tx_unmodulated(struct gpio_ir * gpio_ir,uint * txbuf,uint count)751195a28dSSean Young static void gpio_ir_tx_unmodulated(struct gpio_ir *gpio_ir, uint *txbuf,
761195a28dSSean Young uint count)
7724d79ebcSSean Young {
781195a28dSSean Young ktime_t edge;
791195a28dSSean Young int i;
801195a28dSSean Young
811451b932SSean Young local_irq_disable();
821195a28dSSean Young
831195a28dSSean Young edge = ktime_get();
841195a28dSSean Young
851195a28dSSean Young for (i = 0; i < count; i++) {
861195a28dSSean Young gpiod_set_value(gpio_ir->gpio, !(i % 2));
871195a28dSSean Young
881195a28dSSean Young edge = ktime_add_us(edge, txbuf[i]);
895ad05ecaSSean Young delay_until(edge);
901195a28dSSean Young }
911195a28dSSean Young
921195a28dSSean Young gpiod_set_value(gpio_ir->gpio, 0);
931195a28dSSean Young }
941195a28dSSean Young
gpio_ir_tx_modulated(struct gpio_ir * gpio_ir,uint * txbuf,uint count)951195a28dSSean Young static void gpio_ir_tx_modulated(struct gpio_ir *gpio_ir, uint *txbuf,
961195a28dSSean Young uint count)
971195a28dSSean Young {
9824d79ebcSSean Young ktime_t edge;
9924d79ebcSSean Young /*
10024d79ebcSSean Young * delta should never exceed 0.5 seconds (IR_MAX_DURATION) and on
10124d79ebcSSean Young * m68k ndelay(s64) does not compile; so use s32 rather than s64.
10224d79ebcSSean Young */
10324d79ebcSSean Young s32 delta;
10424d79ebcSSean Young int i;
10524d79ebcSSean Young unsigned int pulse, space;
10624d79ebcSSean Young
10724d79ebcSSean Young /* Ensure the dividend fits into 32 bit */
10824d79ebcSSean Young pulse = DIV_ROUND_CLOSEST(gpio_ir->duty_cycle * (NSEC_PER_SEC / 100),
10924d79ebcSSean Young gpio_ir->carrier);
11024d79ebcSSean Young space = DIV_ROUND_CLOSEST((100 - gpio_ir->duty_cycle) *
11124d79ebcSSean Young (NSEC_PER_SEC / 100), gpio_ir->carrier);
11224d79ebcSSean Young
1131451b932SSean Young local_irq_disable();
11424d79ebcSSean Young
11524d79ebcSSean Young edge = ktime_get();
11624d79ebcSSean Young
11724d79ebcSSean Young for (i = 0; i < count; i++) {
11824d79ebcSSean Young if (i % 2) {
11924d79ebcSSean Young // space
12024d79ebcSSean Young edge = ktime_add_us(edge, txbuf[i]);
1215ad05ecaSSean Young delay_until(edge);
12224d79ebcSSean Young } else {
12324d79ebcSSean Young // pulse
12424d79ebcSSean Young ktime_t last = ktime_add_us(edge, txbuf[i]);
12524d79ebcSSean Young
126766cbb31SSean Young while (ktime_before(ktime_get(), last)) {
12724d79ebcSSean Young gpiod_set_value(gpio_ir->gpio, 1);
128766cbb31SSean Young edge = ktime_add_ns(edge, pulse);
129766cbb31SSean Young delta = ktime_to_ns(ktime_sub(edge,
130766cbb31SSean Young ktime_get()));
13124d79ebcSSean Young if (delta > 0)
13224d79ebcSSean Young ndelay(delta);
13324d79ebcSSean Young gpiod_set_value(gpio_ir->gpio, 0);
134766cbb31SSean Young edge = ktime_add_ns(edge, space);
135766cbb31SSean Young delta = ktime_to_ns(ktime_sub(edge,
136766cbb31SSean Young ktime_get()));
13724d79ebcSSean Young if (delta > 0)
13824d79ebcSSean Young ndelay(delta);
13924d79ebcSSean Young }
14024d79ebcSSean Young
14124d79ebcSSean Young edge = last;
14224d79ebcSSean Young }
14324d79ebcSSean Young }
1441195a28dSSean Young }
1451195a28dSSean Young
gpio_ir_tx(struct rc_dev * dev,unsigned int * txbuf,unsigned int count)1461195a28dSSean Young static int gpio_ir_tx(struct rc_dev *dev, unsigned int *txbuf,
1471195a28dSSean Young unsigned int count)
1481195a28dSSean Young {
1491195a28dSSean Young struct gpio_ir *gpio_ir = dev->priv;
1501451b932SSean Young unsigned long flags;
1511195a28dSSean Young
1521451b932SSean Young local_irq_save(flags);
1531195a28dSSean Young if (gpio_ir->carrier)
1541195a28dSSean Young gpio_ir_tx_modulated(gpio_ir, txbuf, count);
1551195a28dSSean Young else
1561195a28dSSean Young gpio_ir_tx_unmodulated(gpio_ir, txbuf, count);
1571451b932SSean Young local_irq_restore(flags);
15824d79ebcSSean Young
15924d79ebcSSean Young return count;
16024d79ebcSSean Young }
16124d79ebcSSean Young
gpio_ir_tx_probe(struct platform_device * pdev)16224d79ebcSSean Young static int gpio_ir_tx_probe(struct platform_device *pdev)
16324d79ebcSSean Young {
16424d79ebcSSean Young struct gpio_ir *gpio_ir;
16524d79ebcSSean Young struct rc_dev *rcdev;
16624d79ebcSSean Young int rc;
16724d79ebcSSean Young
16824d79ebcSSean Young gpio_ir = devm_kmalloc(&pdev->dev, sizeof(*gpio_ir), GFP_KERNEL);
16924d79ebcSSean Young if (!gpio_ir)
17024d79ebcSSean Young return -ENOMEM;
17124d79ebcSSean Young
17224d79ebcSSean Young rcdev = devm_rc_allocate_device(&pdev->dev, RC_DRIVER_IR_RAW_TX);
17324d79ebcSSean Young if (!rcdev)
17424d79ebcSSean Young return -ENOMEM;
17524d79ebcSSean Young
17624d79ebcSSean Young gpio_ir->gpio = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW);
1776cb7d1b3SYang Yingliang if (IS_ERR(gpio_ir->gpio))
1786cb7d1b3SYang Yingliang return dev_err_probe(&pdev->dev, PTR_ERR(gpio_ir->gpio),
1796cb7d1b3SYang Yingliang "Failed to get gpio\n");
18024d79ebcSSean Young
18124d79ebcSSean Young rcdev->priv = gpio_ir;
18224d79ebcSSean Young rcdev->driver_name = DRIVER_NAME;
18324d79ebcSSean Young rcdev->device_name = DEVICE_NAME;
18424d79ebcSSean Young rcdev->tx_ir = gpio_ir_tx;
18524d79ebcSSean Young rcdev->s_tx_duty_cycle = gpio_ir_tx_set_duty_cycle;
18624d79ebcSSean Young rcdev->s_tx_carrier = gpio_ir_tx_set_carrier;
18724d79ebcSSean Young
18824d79ebcSSean Young gpio_ir->carrier = 38000;
18924d79ebcSSean Young gpio_ir->duty_cycle = 50;
19024d79ebcSSean Young
19124d79ebcSSean Young rc = devm_rc_register_device(&pdev->dev, rcdev);
19224d79ebcSSean Young if (rc < 0)
19324d79ebcSSean Young dev_err(&pdev->dev, "failed to register rc device\n");
19424d79ebcSSean Young
19524d79ebcSSean Young return rc;
19624d79ebcSSean Young }
19724d79ebcSSean Young
19824d79ebcSSean Young static struct platform_driver gpio_ir_tx_driver = {
19924d79ebcSSean Young .probe = gpio_ir_tx_probe,
20024d79ebcSSean Young .driver = {
20124d79ebcSSean Young .name = DRIVER_NAME,
202*b38029bbSKrzysztof Kozlowski .of_match_table = gpio_ir_tx_of_match,
20324d79ebcSSean Young },
20424d79ebcSSean Young };
20524d79ebcSSean Young module_platform_driver(gpio_ir_tx_driver);
20624d79ebcSSean Young
20724d79ebcSSean Young MODULE_DESCRIPTION("GPIO IR Bit Banging Transmitter");
20824d79ebcSSean Young MODULE_AUTHOR("Sean Young <sean@mess.org>");
20924d79ebcSSean Young MODULE_LICENSE("GPL");
210