Skip to content

Commit db3df87

Browse files
seanyoungmchehab
authored andcommitted
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 <[email protected]> Reviewed-by: Pavel Machek <[email protected]> Tested-by: Matthias Reichl <[email protected]> Signed-off-by: Mauro Carvalho Chehab <[email protected]>
1 parent 24d79eb commit db3df87

File tree

4 files changed

+157
-0
lines changed

4 files changed

+157
-0
lines changed

MAINTAINERS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10794,6 +10794,12 @@ F: Documentation/devicetree/bindings/hwmon/pwm-fan.txt
1079410794
F: Documentation/hwmon/pwm-fan
1079510795
F: drivers/hwmon/pwm-fan.c
1079610796

10797+
PWM IR Transmitter
10798+
M: Sean Young <[email protected]>
10799+
10800+
S: Maintained
10801+
F: drivers/media/rc/pwm-ir-tx.c
10802+
1079710803
PWM SUBSYSTEM
1079810804
M: Thierry Reding <[email protected]>
1079910805

drivers/media/rc/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,18 @@ config IR_GPIO_TX
410410
To compile this driver as a module, choose M here: the module will
411411
be called gpio-ir-tx.
412412

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+
413425
config RC_ST
414426
tristate "ST remote control receiver"
415427
depends on RC_CORE

drivers/media/rc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o
3333
obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o
3434
obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o
3535
obj-$(CONFIG_IR_GPIO_TX) += gpio-ir-tx.o
36+
obj-$(CONFIG_IR_PWM_TX) += pwm-ir-tx.o
3637
obj-$(CONFIG_IR_IGORPLUGUSB) += igorplugusb.o
3738
obj-$(CONFIG_IR_IGUANA) += iguanair.o
3839
obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o

drivers/media/rc/pwm-ir-tx.c

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Copyright (C) 2017 Sean Young <[email protected]>
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 <[email protected]>");
138+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)