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 18 struct gpio_ir { 19 struct gpio_desc *gpio; 20 unsigned int carrier; 21 unsigned int duty_cycle; 22 /* we need a spinlock to hold the cpu while transmitting */ 23 spinlock_t lock; 24 }; 25 26 static const struct of_device_id gpio_ir_tx_of_match[] = { 27 { .compatible = "gpio-ir-tx", }, 28 { }, 29 }; 30 MODULE_DEVICE_TABLE(of, gpio_ir_tx_of_match); 31 32 static int gpio_ir_tx_set_duty_cycle(struct rc_dev *dev, u32 duty_cycle) 33 { 34 struct gpio_ir *gpio_ir = dev->priv; 35 36 gpio_ir->duty_cycle = duty_cycle; 37 38 return 0; 39 } 40 41 static int gpio_ir_tx_set_carrier(struct rc_dev *dev, u32 carrier) 42 { 43 struct gpio_ir *gpio_ir = dev->priv; 44 45 if (!carrier) 46 return -EINVAL; 47 48 gpio_ir->carrier = carrier; 49 50 return 0; 51 } 52 53 static int gpio_ir_tx(struct rc_dev *dev, unsigned int *txbuf, 54 unsigned int count) 55 { 56 struct gpio_ir *gpio_ir = dev->priv; 57 unsigned long flags; 58 ktime_t edge; 59 /* 60 * delta should never exceed 0.5 seconds (IR_MAX_DURATION) and on 61 * m68k ndelay(s64) does not compile; so use s32 rather than s64. 62 */ 63 s32 delta; 64 int i; 65 unsigned int pulse, space; 66 67 /* Ensure the dividend fits into 32 bit */ 68 pulse = DIV_ROUND_CLOSEST(gpio_ir->duty_cycle * (NSEC_PER_SEC / 100), 69 gpio_ir->carrier); 70 space = DIV_ROUND_CLOSEST((100 - gpio_ir->duty_cycle) * 71 (NSEC_PER_SEC / 100), gpio_ir->carrier); 72 73 spin_lock_irqsave(&gpio_ir->lock, flags); 74 75 edge = ktime_get(); 76 77 for (i = 0; i < count; i++) { 78 if (i % 2) { 79 // space 80 edge = ktime_add_us(edge, txbuf[i]); 81 delta = ktime_us_delta(edge, ktime_get()); 82 if (delta > 10) { 83 spin_unlock_irqrestore(&gpio_ir->lock, flags); 84 usleep_range(delta, delta + 10); 85 spin_lock_irqsave(&gpio_ir->lock, flags); 86 } else if (delta > 0) { 87 udelay(delta); 88 } 89 } else { 90 // pulse 91 ktime_t last = ktime_add_us(edge, txbuf[i]); 92 93 while (ktime_before(ktime_get(), last)) { 94 gpiod_set_value(gpio_ir->gpio, 1); 95 edge = ktime_add_ns(edge, pulse); 96 delta = ktime_to_ns(ktime_sub(edge, 97 ktime_get())); 98 if (delta > 0) 99 ndelay(delta); 100 gpiod_set_value(gpio_ir->gpio, 0); 101 edge = ktime_add_ns(edge, space); 102 delta = ktime_to_ns(ktime_sub(edge, 103 ktime_get())); 104 if (delta > 0) 105 ndelay(delta); 106 } 107 108 edge = last; 109 } 110 } 111 112 spin_unlock_irqrestore(&gpio_ir->lock, flags); 113 114 return count; 115 } 116 117 static int gpio_ir_tx_probe(struct platform_device *pdev) 118 { 119 struct gpio_ir *gpio_ir; 120 struct rc_dev *rcdev; 121 int rc; 122 123 gpio_ir = devm_kmalloc(&pdev->dev, sizeof(*gpio_ir), GFP_KERNEL); 124 if (!gpio_ir) 125 return -ENOMEM; 126 127 rcdev = devm_rc_allocate_device(&pdev->dev, RC_DRIVER_IR_RAW_TX); 128 if (!rcdev) 129 return -ENOMEM; 130 131 gpio_ir->gpio = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW); 132 if (IS_ERR(gpio_ir->gpio)) { 133 if (PTR_ERR(gpio_ir->gpio) != -EPROBE_DEFER) 134 dev_err(&pdev->dev, "Failed to get gpio (%ld)\n", 135 PTR_ERR(gpio_ir->gpio)); 136 return PTR_ERR(gpio_ir->gpio); 137 } 138 139 rcdev->priv = gpio_ir; 140 rcdev->driver_name = DRIVER_NAME; 141 rcdev->device_name = DEVICE_NAME; 142 rcdev->tx_ir = gpio_ir_tx; 143 rcdev->s_tx_duty_cycle = gpio_ir_tx_set_duty_cycle; 144 rcdev->s_tx_carrier = gpio_ir_tx_set_carrier; 145 146 gpio_ir->carrier = 38000; 147 gpio_ir->duty_cycle = 50; 148 spin_lock_init(&gpio_ir->lock); 149 150 rc = devm_rc_register_device(&pdev->dev, rcdev); 151 if (rc < 0) 152 dev_err(&pdev->dev, "failed to register rc device\n"); 153 154 return rc; 155 } 156 157 static struct platform_driver gpio_ir_tx_driver = { 158 .probe = gpio_ir_tx_probe, 159 .driver = { 160 .name = DRIVER_NAME, 161 .of_match_table = of_match_ptr(gpio_ir_tx_of_match), 162 }, 163 }; 164 module_platform_driver(gpio_ir_tx_driver); 165 166 MODULE_DESCRIPTION("GPIO IR Bit Banging Transmitter"); 167 MODULE_AUTHOR("Sean Young <sean@mess.org>"); 168 MODULE_LICENSE("GPL"); 169