2019-05-23 11:14:59 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2017-07-07 05:51:59 -04:00
/*
* Copyright ( C ) 2017 Sean Young < sean @ mess . org >
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/gpio/consumer.h>
# include <linux/delay.h>
# include <linux/slab.h>
# include <linux/of.h>
# include <linux/platform_device.h>
# include <media/rc-core.h>
# define DRIVER_NAME "gpio-ir-tx"
# define DEVICE_NAME "GPIO IR Bit Banging Transmitter"
struct gpio_ir {
struct gpio_desc * gpio ;
unsigned int carrier ;
unsigned int duty_cycle ;
} ;
static const struct of_device_id gpio_ir_tx_of_match [ ] = {
{ . compatible = " gpio-ir-tx " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , gpio_ir_tx_of_match ) ;
static int gpio_ir_tx_set_duty_cycle ( struct rc_dev * dev , u32 duty_cycle )
{
struct gpio_ir * gpio_ir = dev - > priv ;
gpio_ir - > duty_cycle = duty_cycle ;
return 0 ;
}
static int gpio_ir_tx_set_carrier ( struct rc_dev * dev , u32 carrier )
{
struct gpio_ir * gpio_ir = dev - > priv ;
2020-05-06 11:54:39 +02:00
if ( carrier > 500000 )
2017-07-07 05:51:59 -04:00
return - EINVAL ;
gpio_ir - > carrier = carrier ;
return 0 ;
}
2020-05-06 11:54:39 +02:00
static void gpio_ir_tx_unmodulated ( struct gpio_ir * gpio_ir , uint * txbuf ,
uint count )
{
ktime_t edge ;
s32 delta ;
int i ;
2020-08-13 11:08:49 +02:00
local_irq_disable ( ) ;
2020-05-06 11:54:39 +02:00
edge = ktime_get ( ) ;
for ( i = 0 ; i < count ; i + + ) {
gpiod_set_value ( gpio_ir - > gpio , ! ( i % 2 ) ) ;
edge = ktime_add_us ( edge , txbuf [ i ] ) ;
delta = ktime_us_delta ( edge , ktime_get ( ) ) ;
if ( delta > 0 )
udelay ( delta ) ;
}
gpiod_set_value ( gpio_ir - > gpio , 0 ) ;
}
static void gpio_ir_tx_modulated ( struct gpio_ir * gpio_ir , uint * txbuf ,
uint count )
2017-07-07 05:51:59 -04:00
{
ktime_t edge ;
/*
* delta should never exceed 0.5 seconds ( IR_MAX_DURATION ) and on
* m68k ndelay ( s64 ) does not compile ; so use s32 rather than s64 .
*/
s32 delta ;
int i ;
unsigned int pulse , space ;
/* Ensure the dividend fits into 32 bit */
pulse = DIV_ROUND_CLOSEST ( gpio_ir - > duty_cycle * ( NSEC_PER_SEC / 100 ) ,
gpio_ir - > carrier ) ;
space = DIV_ROUND_CLOSEST ( ( 100 - gpio_ir - > duty_cycle ) *
( NSEC_PER_SEC / 100 ) , gpio_ir - > carrier ) ;
2020-08-13 11:08:49 +02:00
local_irq_disable ( ) ;
2017-07-07 05:51:59 -04:00
edge = ktime_get ( ) ;
for ( i = 0 ; i < count ; i + + ) {
if ( i % 2 ) {
// space
edge = ktime_add_us ( edge , txbuf [ i ] ) ;
delta = ktime_us_delta ( edge , ktime_get ( ) ) ;
2020-05-02 14:50:52 +02:00
if ( delta > 0 )
2017-07-07 05:51:59 -04:00
udelay ( delta ) ;
} else {
// pulse
ktime_t last = ktime_add_us ( edge , txbuf [ i ] ) ;
2017-08-25 10:45:47 -04:00
while ( ktime_before ( ktime_get ( ) , last ) ) {
2017-07-07 05:51:59 -04:00
gpiod_set_value ( gpio_ir - > gpio , 1 ) ;
2017-08-25 10:45:47 -04:00
edge = ktime_add_ns ( edge , pulse ) ;
delta = ktime_to_ns ( ktime_sub ( edge ,
ktime_get ( ) ) ) ;
2017-07-07 05:51:59 -04:00
if ( delta > 0 )
ndelay ( delta ) ;
gpiod_set_value ( gpio_ir - > gpio , 0 ) ;
2017-08-25 10:45:47 -04:00
edge = ktime_add_ns ( edge , space ) ;
delta = ktime_to_ns ( ktime_sub ( edge ,
ktime_get ( ) ) ) ;
2017-07-07 05:51:59 -04:00
if ( delta > 0 )
ndelay ( delta ) ;
}
edge = last ;
}
}
2020-05-06 11:54:39 +02:00
}
static int gpio_ir_tx ( struct rc_dev * dev , unsigned int * txbuf ,
unsigned int count )
{
struct gpio_ir * gpio_ir = dev - > priv ;
2020-08-13 11:08:49 +02:00
unsigned long flags ;
2020-05-06 11:54:39 +02:00
2020-08-13 11:08:49 +02:00
local_irq_save ( flags ) ;
2020-05-06 11:54:39 +02:00
if ( gpio_ir - > carrier )
gpio_ir_tx_modulated ( gpio_ir , txbuf , count ) ;
else
gpio_ir_tx_unmodulated ( gpio_ir , txbuf , count ) ;
2020-08-13 11:08:49 +02:00
local_irq_restore ( flags ) ;
2017-07-07 05:51:59 -04:00
return count ;
}
static int gpio_ir_tx_probe ( struct platform_device * pdev )
{
struct gpio_ir * gpio_ir ;
struct rc_dev * rcdev ;
int rc ;
gpio_ir = devm_kmalloc ( & pdev - > dev , sizeof ( * gpio_ir ) , GFP_KERNEL ) ;
if ( ! gpio_ir )
return - ENOMEM ;
rcdev = devm_rc_allocate_device ( & pdev - > dev , RC_DRIVER_IR_RAW_TX ) ;
if ( ! rcdev )
return - ENOMEM ;
gpio_ir - > gpio = devm_gpiod_get ( & pdev - > dev , NULL , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( gpio_ir - > gpio ) ) {
if ( PTR_ERR ( gpio_ir - > gpio ) ! = - EPROBE_DEFER )
dev_err ( & pdev - > dev , " Failed to get gpio (%ld) \n " ,
PTR_ERR ( gpio_ir - > gpio ) ) ;
return PTR_ERR ( gpio_ir - > gpio ) ;
}
rcdev - > priv = gpio_ir ;
rcdev - > driver_name = DRIVER_NAME ;
rcdev - > device_name = DEVICE_NAME ;
rcdev - > tx_ir = gpio_ir_tx ;
rcdev - > s_tx_duty_cycle = gpio_ir_tx_set_duty_cycle ;
rcdev - > s_tx_carrier = gpio_ir_tx_set_carrier ;
gpio_ir - > carrier = 38000 ;
gpio_ir - > duty_cycle = 50 ;
rc = devm_rc_register_device ( & pdev - > dev , rcdev ) ;
if ( rc < 0 )
dev_err ( & pdev - > dev , " failed to register rc device \n " ) ;
return rc ;
}
static struct platform_driver gpio_ir_tx_driver = {
. probe = gpio_ir_tx_probe ,
. driver = {
. name = DRIVER_NAME ,
. of_match_table = of_match_ptr ( gpio_ir_tx_of_match ) ,
} ,
} ;
module_platform_driver ( gpio_ir_tx_driver ) ;
MODULE_DESCRIPTION ( " GPIO IR Bit Banging Transmitter " ) ;
MODULE_AUTHOR ( " Sean Young <sean@mess.org> " ) ;
MODULE_LICENSE ( " GPL " ) ;