pwm-ir-tx.c (2806B)
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 18struct pwm_ir { 19 struct pwm_device *pwm; 20 unsigned int carrier; 21 unsigned int duty_cycle; 22}; 23 24static const struct of_device_id pwm_ir_of_match[] = { 25 { .compatible = "pwm-ir-tx", }, 26 { }, 27}; 28MODULE_DEVICE_TABLE(of, pwm_ir_of_match); 29 30static 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 39static 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 51static 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 struct pwm_state state; 57 int i; 58 ktime_t edge; 59 long delta; 60 61 pwm_init_state(pwm, &state); 62 63 state.period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, pwm_ir->carrier); 64 pwm_set_relative_duty_cycle(&state, pwm_ir->duty_cycle, 100); 65 66 edge = ktime_get(); 67 68 for (i = 0; i < count; i++) { 69 state.enabled = !(i % 2); 70 pwm_apply_state(pwm, &state); 71 72 edge = ktime_add_us(edge, txbuf[i]); 73 delta = ktime_us_delta(edge, ktime_get()); 74 if (delta > 0) 75 usleep_range(delta, delta + 10); 76 } 77 78 state.enabled = false; 79 pwm_apply_state(pwm, &state); 80 81 return count; 82} 83 84static 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 119static 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}; 126module_platform_driver(pwm_ir_driver); 127 128MODULE_DESCRIPTION("PWM IR Transmitter"); 129MODULE_AUTHOR("Sean Young <sean@mess.org>"); 130MODULE_LICENSE("GPL");