2012-08-10 13:16:36 +04:00
/*
* Copyright ( C ) 2008 Nokia Corporation
*
* Based on lirc_serial . c
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
2016-06-22 22:22:17 +03:00
# include <linux/clk.h>
2012-08-10 13:16:36 +04:00
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/wait.h>
2016-06-22 22:22:19 +03:00
# include <linux/pwm.h>
2016-06-22 22:22:20 +03:00
# include <linux/of.h>
2016-06-22 22:22:21 +03:00
# include <linux/hrtimer.h>
2012-08-10 13:16:36 +04:00
2016-12-19 23:48:29 +03:00
# include <media/rc-core.h>
2012-08-10 13:16:36 +04:00
# define WBUF_LEN 256
2016-12-19 23:48:29 +03:00
struct ir_rx51 {
struct rc_dev * rcdev ;
2016-06-22 22:22:19 +03:00
struct pwm_device * pwm ;
2016-06-22 22:22:21 +03:00
struct hrtimer timer ;
2012-08-10 13:16:36 +04:00
struct device * dev ;
wait_queue_head_t wqueue ;
unsigned int freq ; /* carrier frequency */
unsigned int duty_cycle ; /* carrier duty cycle */
int wbuf [ WBUF_LEN ] ;
int wbuf_index ;
unsigned long device_is_open ;
} ;
2016-12-19 23:48:29 +03:00
static inline void ir_rx51_on ( struct ir_rx51 * ir_rx51 )
2012-08-10 13:16:36 +04:00
{
2016-12-19 23:48:29 +03:00
pwm_enable ( ir_rx51 - > pwm ) ;
2012-08-10 13:16:36 +04:00
}
2016-12-19 23:48:29 +03:00
static inline void ir_rx51_off ( struct ir_rx51 * ir_rx51 )
2012-08-10 13:16:36 +04:00
{
2016-12-19 23:48:29 +03:00
pwm_disable ( ir_rx51 - > pwm ) ;
2012-08-10 13:16:36 +04:00
}
2016-12-19 23:48:29 +03:00
static int init_timing_params ( struct ir_rx51 * ir_rx51 )
2012-08-10 13:16:36 +04:00
{
2016-12-19 23:48:29 +03:00
struct pwm_device * pwm = ir_rx51 - > pwm ;
int duty , period = DIV_ROUND_CLOSEST ( NSEC_PER_SEC , ir_rx51 - > freq ) ;
2016-06-22 22:22:19 +03:00
2016-12-19 23:48:29 +03:00
duty = DIV_ROUND_CLOSEST ( ir_rx51 - > duty_cycle * period , 100 ) ;
2016-06-22 22:22:19 +03:00
pwm_config ( pwm , duty , period ) ;
2012-08-10 13:16:36 +04:00
return 0 ;
}
2016-12-19 23:48:29 +03:00
static enum hrtimer_restart ir_rx51_timer_cb ( struct hrtimer * timer )
2012-08-10 13:16:36 +04:00
{
2016-12-19 23:48:29 +03:00
struct ir_rx51 * ir_rx51 = container_of ( timer , struct ir_rx51 , timer ) ;
2016-06-22 22:22:21 +03:00
ktime_t now ;
2012-08-10 13:16:36 +04:00
2016-12-19 23:48:29 +03:00
if ( ir_rx51 - > wbuf_index < 0 ) {
dev_err_ratelimited ( ir_rx51 - > dev ,
" BUG wbuf_index has value of %i \n " ,
ir_rx51 - > wbuf_index ) ;
2012-08-10 13:16:36 +04:00
goto end ;
}
/*
* If we happen to hit an odd latency spike , loop through the
* pulses until we catch up .
*/
do {
2016-06-22 22:22:21 +03:00
u64 ns ;
2016-12-19 23:48:29 +03:00
if ( ir_rx51 - > wbuf_index > = WBUF_LEN )
2012-08-10 13:16:36 +04:00
goto end ;
2016-12-19 23:48:29 +03:00
if ( ir_rx51 - > wbuf [ ir_rx51 - > wbuf_index ] = = - 1 )
2012-08-10 13:16:36 +04:00
goto end ;
2016-12-19 23:48:29 +03:00
if ( ir_rx51 - > wbuf_index % 2 )
ir_rx51_off ( ir_rx51 ) ;
2012-08-10 13:16:36 +04:00
else
2016-12-19 23:48:29 +03:00
ir_rx51_on ( ir_rx51 ) ;
2012-08-10 13:16:36 +04:00
2016-12-19 23:48:29 +03:00
ns = US_TO_NS ( ir_rx51 - > wbuf [ ir_rx51 - > wbuf_index ] ) ;
2016-06-22 22:22:21 +03:00
hrtimer_add_expires_ns ( timer , ns ) ;
2016-12-19 23:48:29 +03:00
ir_rx51 - > wbuf_index + + ;
2012-08-10 13:16:36 +04:00
2016-06-22 22:22:21 +03:00
now = timer - > base - > get_time ( ) ;
2016-12-25 13:38:40 +03:00
} while ( hrtimer_get_expires_tv64 ( timer ) < now ) ;
2012-08-10 13:16:36 +04:00
2016-06-22 22:22:21 +03:00
return HRTIMER_RESTART ;
2012-08-10 13:16:36 +04:00
end :
/* Stop TX here */
2016-12-19 23:48:29 +03:00
ir_rx51_off ( ir_rx51 ) ;
ir_rx51 - > wbuf_index = - 1 ;
2016-06-22 22:22:19 +03:00
2016-12-19 23:48:29 +03:00
wake_up_interruptible ( & ir_rx51 - > wqueue ) ;
2012-08-10 13:16:36 +04:00
2016-06-22 22:22:21 +03:00
return HRTIMER_NORESTART ;
2012-08-10 13:16:36 +04:00
}
2016-12-19 23:48:29 +03:00
static int ir_rx51_tx ( struct rc_dev * dev , unsigned int * buffer ,
unsigned int count )
2012-08-10 13:16:36 +04:00
{
2016-12-19 23:48:29 +03:00
struct ir_rx51 * ir_rx51 = dev - > priv ;
2012-08-10 13:16:36 +04:00
2016-12-19 23:48:29 +03:00
if ( count > WBUF_LEN )
2012-08-10 13:16:36 +04:00
return - EINVAL ;
2016-12-19 23:48:29 +03:00
memcpy ( ir_rx51 - > wbuf , buffer , count * sizeof ( unsigned int ) ) ;
2012-08-10 13:16:36 +04:00
/* Wait any pending transfers to finish */
2016-12-19 23:48:29 +03:00
wait_event_interruptible ( ir_rx51 - > wqueue , ir_rx51 - > wbuf_index < 0 ) ;
2012-08-10 13:16:36 +04:00
2016-12-19 23:48:29 +03:00
init_timing_params ( ir_rx51 ) ;
2012-08-10 13:16:36 +04:00
if ( count < WBUF_LEN )
2016-12-19 23:48:29 +03:00
ir_rx51 - > wbuf [ count ] = - 1 ; /* Insert termination mark */
2012-08-10 13:16:36 +04:00
/*
2018-04-16 20:22:01 +03:00
* REVISIT : Adjust latency requirements so the device doesn ' t go in too
* deep sleep states with pm_qos_add_request ( ) .
2012-08-10 13:16:36 +04:00
*/
2016-12-19 23:48:29 +03:00
ir_rx51_on ( ir_rx51 ) ;
ir_rx51 - > wbuf_index = 1 ;
hrtimer_start ( & ir_rx51 - > timer ,
ns_to_ktime ( US_TO_NS ( ir_rx51 - > wbuf [ 0 ] ) ) ,
2016-06-22 22:22:21 +03:00
HRTIMER_MODE_REL ) ;
2012-08-10 13:16:36 +04:00
/*
* Don ' t return back to the userspace until the transfer has
* finished
*/
2016-12-19 23:48:29 +03:00
wait_event_interruptible ( ir_rx51 - > wqueue , ir_rx51 - > wbuf_index < 0 ) ;
2012-08-10 13:16:36 +04:00
2018-04-16 20:22:01 +03:00
/* REVISIT: Remove pm_qos constraint, we can sleep again */
2012-08-10 13:16:36 +04:00
2016-12-19 23:48:29 +03:00
return count ;
2012-08-10 13:16:36 +04:00
}
2016-12-19 23:48:29 +03:00
static int ir_rx51_open ( struct rc_dev * dev )
2012-08-10 13:16:36 +04:00
{
2016-12-19 23:48:29 +03:00
struct ir_rx51 * ir_rx51 = dev - > priv ;
2012-08-10 13:16:36 +04:00
2016-12-19 23:48:29 +03:00
if ( test_and_set_bit ( 1 , & ir_rx51 - > device_is_open ) )
2012-08-10 13:16:36 +04:00
return - EBUSY ;
2016-12-19 23:48:29 +03:00
ir_rx51 - > pwm = pwm_get ( ir_rx51 - > dev , NULL ) ;
if ( IS_ERR ( ir_rx51 - > pwm ) ) {
int res = PTR_ERR ( ir_rx51 - > pwm ) ;
2016-06-22 22:22:21 +03:00
2016-12-19 23:48:29 +03:00
dev_err ( ir_rx51 - > dev , " pwm_get failed: %d \n " , res ) ;
2016-06-22 22:22:21 +03:00
return res ;
}
return 0 ;
2012-08-10 13:16:36 +04:00
}
2016-12-19 23:48:29 +03:00
static void ir_rx51_release ( struct rc_dev * dev )
2012-08-10 13:16:36 +04:00
{
2016-12-19 23:48:29 +03:00
struct ir_rx51 * ir_rx51 = dev - > priv ;
2012-08-10 13:16:36 +04:00
2016-12-19 23:48:29 +03:00
hrtimer_cancel ( & ir_rx51 - > timer ) ;
ir_rx51_off ( ir_rx51 ) ;
pwm_put ( ir_rx51 - > pwm ) ;
2012-08-10 13:16:36 +04:00
2016-12-19 23:48:29 +03:00
clear_bit ( 1 , & ir_rx51 - > device_is_open ) ;
2012-08-10 13:16:36 +04:00
}
2016-12-19 23:48:29 +03:00
static struct ir_rx51 ir_rx51 = {
2012-08-10 13:16:36 +04:00
. duty_cycle = 50 ,
. wbuf_index = - 1 ,
} ;
2016-12-19 23:48:29 +03:00
static int ir_rx51_set_duty_cycle ( struct rc_dev * dev , u32 duty )
{
struct ir_rx51 * ir_rx51 = dev - > priv ;
2012-08-10 13:16:36 +04:00
2016-12-19 23:48:29 +03:00
ir_rx51 - > duty_cycle = duty ;
return 0 ;
}
static int ir_rx51_set_tx_carrier ( struct rc_dev * dev , u32 carrier )
{
struct ir_rx51 * ir_rx51 = dev - > priv ;
if ( carrier > 500000 | | carrier < 20000 )
return - EINVAL ;
ir_rx51 - > freq = carrier ;
return 0 ;
}
2012-08-10 13:16:36 +04:00
# ifdef CONFIG_PM
2016-12-19 23:48:29 +03:00
static int ir_rx51_suspend ( struct platform_device * dev , pm_message_t state )
2012-08-10 13:16:36 +04:00
{
/*
* In case the device is still open , do not suspend . Normally
* this should not be a problem as lircd only keeps the device
* open only for short periods of time . We also don ' t want to
* get involved with race conditions that might happen if we
* were in a middle of a transmit . Thus , we defer any suspend
* actions until transmit has completed .
*/
2016-12-19 23:48:29 +03:00
if ( test_and_set_bit ( 1 , & ir_rx51 . device_is_open ) )
2012-08-10 13:16:36 +04:00
return - EAGAIN ;
2016-12-19 23:48:29 +03:00
clear_bit ( 1 , & ir_rx51 . device_is_open ) ;
2012-08-10 13:16:36 +04:00
return 0 ;
}
2016-12-19 23:48:29 +03:00
static int ir_rx51_resume ( struct platform_device * dev )
2012-08-10 13:16:36 +04:00
{
return 0 ;
}
# else
2016-12-19 23:48:29 +03:00
# define ir_rx51_suspend NULL
# define ir_rx51_resume NULL
2012-08-10 13:16:36 +04:00
# endif /* CONFIG_PM */
2016-12-19 23:48:29 +03:00
static int ir_rx51_probe ( struct platform_device * dev )
2012-08-10 13:16:36 +04:00
{
2016-06-22 22:22:19 +03:00
struct pwm_device * pwm ;
2016-12-19 23:48:29 +03:00
struct rc_dev * rcdev ;
2016-06-22 22:22:19 +03:00
pwm = pwm_get ( & dev - > dev , NULL ) ;
if ( IS_ERR ( pwm ) ) {
int err = PTR_ERR ( pwm ) ;
if ( err ! = - EPROBE_DEFER )
dev_err ( & dev - > dev , " pwm_get failed: %d \n " , err ) ;
return err ;
}
/* Use default, in case userspace does not set the carrier */
2016-12-19 23:48:29 +03:00
ir_rx51 . freq = DIV_ROUND_CLOSEST ( pwm_get_period ( pwm ) , NSEC_PER_SEC ) ;
2016-06-22 22:22:19 +03:00
pwm_put ( pwm ) ;
2016-12-19 23:48:29 +03:00
hrtimer_init ( & ir_rx51 . timer , CLOCK_MONOTONIC , HRTIMER_MODE_REL ) ;
ir_rx51 . timer . function = ir_rx51_timer_cb ;
2016-06-22 22:22:21 +03:00
2016-12-19 23:48:29 +03:00
ir_rx51 . dev = & dev - > dev ;
2012-08-10 13:16:36 +04:00
2016-12-19 23:48:29 +03:00
rcdev = devm_rc_allocate_device ( & dev - > dev , RC_DRIVER_IR_RAW_TX ) ;
if ( ! rcdev )
return - ENOMEM ;
2012-08-10 13:16:36 +04:00
2016-12-19 23:48:29 +03:00
rcdev - > priv = & ir_rx51 ;
rcdev - > open = ir_rx51_open ;
rcdev - > close = ir_rx51_release ;
rcdev - > tx_ir = ir_rx51_tx ;
rcdev - > s_tx_duty_cycle = ir_rx51_set_duty_cycle ;
rcdev - > s_tx_carrier = ir_rx51_set_tx_carrier ;
rcdev - > driver_name = KBUILD_MODNAME ;
ir_rx51 . rcdev = rcdev ;
return devm_rc_register_device ( & dev - > dev , ir_rx51 . rcdev ) ;
2012-08-10 13:16:36 +04:00
}
2016-12-19 23:48:29 +03:00
static int ir_rx51_remove ( struct platform_device * dev )
2012-08-10 13:16:36 +04:00
{
2016-12-19 23:48:29 +03:00
return 0 ;
2012-08-10 13:16:36 +04:00
}
2016-12-19 23:48:29 +03:00
static const struct of_device_id ir_rx51_match [ ] = {
2016-06-22 22:22:20 +03:00
{
. compatible = " nokia,n900-ir " ,
} ,
{ } ,
} ;
2016-12-19 23:48:29 +03:00
MODULE_DEVICE_TABLE ( of , ir_rx51_match ) ;
2016-06-22 22:22:20 +03:00
2016-12-19 23:48:29 +03:00
static struct platform_driver ir_rx51_platform_driver = {
. probe = ir_rx51_probe ,
. remove = ir_rx51_remove ,
. suspend = ir_rx51_suspend ,
. resume = ir_rx51_resume ,
2012-08-10 13:16:36 +04:00
. driver = {
2016-12-19 23:48:29 +03:00
. name = KBUILD_MODNAME ,
. of_match_table = of_match_ptr ( ir_rx51_match ) ,
2012-08-10 13:16:36 +04:00
} ,
} ;
2016-12-19 23:48:29 +03:00
module_platform_driver ( ir_rx51_platform_driver ) ;
2012-08-10 13:16:36 +04:00
2016-12-19 23:48:29 +03:00
MODULE_DESCRIPTION ( " IR TX driver for Nokia RX51 " ) ;
2012-08-10 13:16:36 +04:00
MODULE_AUTHOR ( " Nokia Corporation " ) ;
MODULE_LICENSE ( " GPL " ) ;