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"); 139