Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

media: rc: pwm-ir-tx: add new driver

This is new driver which uses pwm, so it is more power-efficient
than the bit banging gpio-ir-tx driver.

Signed-off-by: Sean Young <sean@mess.org>
Reviewed-by: Pavel Machek <pavel@ucw.cz>
Tested-by: Matthias Reichl <hias@horus.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>

authored by

Sean Young and committed by
Mauro Carvalho Chehab
db3df876 24d79ebc

+157
+6
MAINTAINERS
··· 10794 10794 F: Documentation/hwmon/pwm-fan 10795 10795 F: drivers/hwmon/pwm-fan.c 10796 10796 10797 + PWM IR Transmitter 10798 + M: Sean Young <sean@mess.org> 10799 + L: linux-media@vger.kernel.org 10800 + S: Maintained 10801 + F: drivers/media/rc/pwm-ir-tx.c 10802 + 10797 10803 PWM SUBSYSTEM 10798 10804 M: Thierry Reding <thierry.reding@gmail.com> 10799 10805 L: linux-pwm@vger.kernel.org
+12
drivers/media/rc/Kconfig
··· 410 410 To compile this driver as a module, choose M here: the module will 411 411 be called gpio-ir-tx. 412 412 413 + config IR_PWM_TX 414 + tristate "PWM IR transmitter" 415 + depends on RC_CORE 416 + depends on LIRC 417 + depends on PWM 418 + ---help--- 419 + Say Y if you want to use a PWM based IR transmitter. This is 420 + more power efficient than the bit banging gpio driver. 421 + 422 + To compile this driver as a module, choose M here: the module will 423 + be called pwm-ir-tx. 424 + 413 425 config RC_ST 414 426 tristate "ST remote control receiver" 415 427 depends on RC_CORE
+1
drivers/media/rc/Makefile
··· 33 33 obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o 34 34 obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o 35 35 obj-$(CONFIG_IR_GPIO_TX) += gpio-ir-tx.o 36 + obj-$(CONFIG_IR_PWM_TX) += pwm-ir-tx.o 36 37 obj-$(CONFIG_IR_IGORPLUGUSB) += igorplugusb.o 37 38 obj-$(CONFIG_IR_IGUANA) += iguanair.o 38 39 obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o
+138
drivers/media/rc/pwm-ir-tx.c
··· 1 + /* 2 + * Copyright (C) 2017 Sean Young <sean@mess.org> 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License version 2, or 6 + * (at your option) any later version. 7 + * 8 + * This program is distributed in the hope that it will be useful, 9 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 + * GNU General Public License for more details. 12 + */ 13 + 14 + #include <linux/kernel.h> 15 + #include <linux/module.h> 16 + #include <linux/pwm.h> 17 + #include <linux/delay.h> 18 + #include <linux/slab.h> 19 + #include <linux/of.h> 20 + #include <linux/platform_device.h> 21 + #include <media/rc-core.h> 22 + 23 + #define DRIVER_NAME "pwm-ir-tx" 24 + #define DEVICE_NAME "PWM IR Transmitter" 25 + 26 + struct pwm_ir { 27 + struct pwm_device *pwm; 28 + unsigned int carrier; 29 + unsigned int duty_cycle; 30 + }; 31 + 32 + static const struct of_device_id pwm_ir_of_match[] = { 33 + { .compatible = "pwm-ir-tx", }, 34 + { }, 35 + }; 36 + MODULE_DEVICE_TABLE(of, pwm_ir_of_match); 37 + 38 + static int pwm_ir_set_duty_cycle(struct rc_dev *dev, u32 duty_cycle) 39 + { 40 + struct pwm_ir *pwm_ir = dev->priv; 41 + 42 + pwm_ir->duty_cycle = duty_cycle; 43 + 44 + return 0; 45 + } 46 + 47 + static int pwm_ir_set_carrier(struct rc_dev *dev, u32 carrier) 48 + { 49 + struct pwm_ir *pwm_ir = dev->priv; 50 + 51 + if (!carrier) 52 + return -EINVAL; 53 + 54 + pwm_ir->carrier = carrier; 55 + 56 + return 0; 57 + } 58 + 59 + static int pwm_ir_tx(struct rc_dev *dev, unsigned int *txbuf, 60 + unsigned int count) 61 + { 62 + struct pwm_ir *pwm_ir = dev->priv; 63 + struct pwm_device *pwm = pwm_ir->pwm; 64 + int i, duty, period; 65 + ktime_t edge; 66 + long delta; 67 + 68 + period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, pwm_ir->carrier); 69 + duty = DIV_ROUND_CLOSEST(pwm_ir->duty_cycle * period, 100); 70 + 71 + pwm_config(pwm, duty, period); 72 + 73 + edge = ktime_get(); 74 + 75 + for (i = 0; i < count; i++) { 76 + if (i % 2) // space 77 + pwm_disable(pwm); 78 + else 79 + pwm_enable(pwm); 80 + 81 + edge = ktime_add_us(edge, txbuf[i]); 82 + delta = ktime_us_delta(edge, ktime_get()); 83 + if (delta > 0) 84 + usleep_range(delta, delta + 10); 85 + } 86 + 87 + pwm_disable(pwm); 88 + 89 + return count; 90 + } 91 + 92 + static int pwm_ir_probe(struct platform_device *pdev) 93 + { 94 + struct pwm_ir *pwm_ir; 95 + struct rc_dev *rcdev; 96 + int rc; 97 + 98 + pwm_ir = devm_kmalloc(&pdev->dev, sizeof(*pwm_ir), GFP_KERNEL); 99 + if (!pwm_ir) 100 + return -ENOMEM; 101 + 102 + pwm_ir->pwm = devm_pwm_get(&pdev->dev, NULL); 103 + if (IS_ERR(pwm_ir->pwm)) 104 + return PTR_ERR(pwm_ir->pwm); 105 + 106 + pwm_ir->carrier = 38000; 107 + pwm_ir->duty_cycle = 50; 108 + 109 + rcdev = devm_rc_allocate_device(&pdev->dev, RC_DRIVER_IR_RAW_TX); 110 + if (!rcdev) 111 + return -ENOMEM; 112 + 113 + rcdev->priv = pwm_ir; 114 + rcdev->driver_name = DRIVER_NAME; 115 + rcdev->device_name = DEVICE_NAME; 116 + rcdev->tx_ir = pwm_ir_tx; 117 + rcdev->s_tx_duty_cycle = pwm_ir_set_duty_cycle; 118 + rcdev->s_tx_carrier = pwm_ir_set_carrier; 119 + 120 + rc = devm_rc_register_device(&pdev->dev, rcdev); 121 + if (rc < 0) 122 + dev_err(&pdev->dev, "failed to register rc device\n"); 123 + 124 + return rc; 125 + } 126 + 127 + static struct platform_driver pwm_ir_driver = { 128 + .probe = pwm_ir_probe, 129 + .driver = { 130 + .name = DRIVER_NAME, 131 + .of_match_table = of_match_ptr(pwm_ir_of_match), 132 + }, 133 + }; 134 + module_platform_driver(pwm_ir_driver); 135 + 136 + MODULE_DESCRIPTION("PWM IR Transmitter"); 137 + MODULE_AUTHOR("Sean Young <sean@mess.org>"); 138 + MODULE_LICENSE("GPL");