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/pwm.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 "pwm-ir-tx" 16 #define DEVICE_NAME "PWM IR Transmitter" 17 18 struct pwm_ir { 19 struct pwm_device *pwm; 20 unsigned int carrier; 21 unsigned int duty_cycle; 22 }; 23 24 static const struct of_device_id pwm_ir_of_match[] = { 25 { .compatible = "pwm-ir-tx", }, 26 { }, 27 }; 28 MODULE_DEVICE_TABLE(of, pwm_ir_of_match); 29 30 static int pwm_ir_set_duty_cycle(struct rc_dev *dev, u32 duty_cycle) 31 { 32 struct pwm_ir *pwm_ir = dev->priv; 33 34 pwm_ir->duty_cycle = duty_cycle; 35 36 return 0; 37 } 38 39 static int pwm_ir_set_carrier(struct rc_dev *dev, u32 carrier) 40 { 41 struct pwm_ir *pwm_ir = dev->priv; 42 43 if (!carrier) 44 return -EINVAL; 45 46 pwm_ir->carrier = carrier; 47 48 return 0; 49 } 50 51 static int pwm_ir_tx(struct rc_dev *dev, unsigned int *txbuf, 52 unsigned int count) 53 { 54 struct pwm_ir *pwm_ir = dev->priv; 55 struct pwm_device *pwm = pwm_ir->pwm; 56 int i, duty, period; 57 ktime_t edge; 58 long delta; 59 60 period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, pwm_ir->carrier); 61 duty = DIV_ROUND_CLOSEST(pwm_ir->duty_cycle * period, 100); 62 63 pwm_config(pwm, duty, period); 64 65 edge = ktime_get(); 66 67 for (i = 0; i < count; i++) { 68 if (i % 2) // space 69 pwm_disable(pwm); 70 else 71 pwm_enable(pwm); 72 73 edge = ktime_add_us(edge, txbuf[i]); 74 delta = ktime_us_delta(edge, ktime_get()); 75 if (delta > 0) 76 usleep_range(delta, delta + 10); 77 } 78 79 pwm_disable(pwm); 80 81 return count; 82 } 83 84 static int pwm_ir_probe(struct platform_device *pdev) 85 { 86 struct pwm_ir *pwm_ir; 87 struct rc_dev *rcdev; 88 int rc; 89 90 pwm_ir = devm_kmalloc(&pdev->dev, sizeof(*pwm_ir), GFP_KERNEL); 91 if (!pwm_ir) 92 return -ENOMEM; 93 94 pwm_ir->pwm = devm_pwm_get(&pdev->dev, NULL); 95 if (IS_ERR(pwm_ir->pwm)) 96 return PTR_ERR(pwm_ir->pwm); 97 98 pwm_ir->carrier = 38000; 99 pwm_ir->duty_cycle = 50; 100 101 rcdev = devm_rc_allocate_device(&pdev->dev, RC_DRIVER_IR_RAW_TX); 102 if (!rcdev) 103 return -ENOMEM; 104 105 rcdev->priv = pwm_ir; 106 rcdev->driver_name = DRIVER_NAME; 107 rcdev->device_name = DEVICE_NAME; 108 rcdev->tx_ir = pwm_ir_tx; 109 rcdev->s_tx_duty_cycle = pwm_ir_set_duty_cycle; 110 rcdev->s_tx_carrier = pwm_ir_set_carrier; 111 112 rc = devm_rc_register_device(&pdev->dev, rcdev); 113 if (rc < 0) 114 dev_err(&pdev->dev, "failed to register rc device\n"); 115 116 return rc; 117 } 118 119 static struct platform_driver pwm_ir_driver = { 120 .probe = pwm_ir_probe, 121 .driver = { 122 .name = DRIVER_NAME, 123 .of_match_table = of_match_ptr(pwm_ir_of_match), 124 }, 125 }; 126 module_platform_driver(pwm_ir_driver); 127 128 MODULE_DESCRIPTION("PWM IR Transmitter"); 129 MODULE_AUTHOR("Sean Young <sean@mess.org>"); 130 MODULE_LICENSE("GPL"); 131