xref: /openbmc/linux/drivers/media/rc/gpio-ir-tx.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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