2010-06-21 00:16:52 +04:00
/*
2010-10-20 13:35:54 +04:00
* tm6000 - input . c - driver for TM5600 / TM6000 / TM6010 USB video capture devices
*
* Copyright ( C ) 2010 Stefan Ringel < stefan . ringel @ arcor . de >
*
* 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 version 2
*
* 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 .
2010-06-21 00:16:52 +04:00
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/input.h>
# include <linux/usb.h>
2010-11-17 19:28:38 +03:00
# include <media/rc-core.h>
2010-06-21 00:16:52 +04:00
# include "tm6000.h"
# include "tm6000-regs.h"
static unsigned int ir_debug ;
module_param ( ir_debug , int , 0644 ) ;
2011-11-29 18:30:30 +04:00
MODULE_PARM_DESC ( ir_debug , " debug message level " ) ;
2010-06-21 00:16:52 +04:00
static unsigned int enable_ir = 1 ;
module_param ( enable_ir , int , 0644 ) ;
2010-08-24 23:36:09 +04:00
MODULE_PARM_DESC ( enable_ir , " enable ir (default is enable) " ) ;
2010-06-21 00:16:52 +04:00
2011-11-29 18:30:30 +04:00
static unsigned int ir_clock_mhz = 12 ;
module_param ( ir_clock_mhz , int , 0644 ) ;
2017-01-12 17:30:47 +03:00
MODULE_PARM_DESC ( ir_clock_mhz , " ir clock, in MHz " ) ;
2011-11-29 18:30:30 +04:00
# define URB_SUBMIT_DELAY 100 /* ms - Delay to submit an URB request on retrial and init */
# define URB_INT_LED_DELAY 100 /* ms - Delay to turn led on again on int mode */
2010-12-22 11:57:46 +03:00
2010-06-21 00:16:52 +04:00
# undef dprintk
2011-11-29 18:30:30 +04:00
# define dprintk(level, fmt, arg...) do {\
if ( ir_debug > = level ) \
2010-06-21 00:16:52 +04:00
printk ( KERN_DEBUG " %s/ir: " fmt , ir - > name , # # arg ) ; \
2011-11-29 18:30:30 +04:00
} while ( 0 )
2010-06-21 00:16:52 +04:00
struct tm6000_ir_poll_result {
2010-09-09 21:45:22 +04:00
u16 rc_data ;
2010-06-21 00:16:52 +04:00
} ;
struct tm6000_IR {
struct tm6000_core * dev ;
2010-10-29 23:08:23 +04:00
struct rc_dev * rc ;
2010-06-21 00:16:52 +04:00
char name [ 32 ] ;
char phys [ 32 ] ;
/* poll expernal decoder */
int polling ;
struct delayed_work work ;
u8 wait : 1 ;
2011-11-29 18:30:30 +04:00
u8 pwled : 2 ;
u8 submit_urb : 1 ;
2010-06-21 00:16:52 +04:00
struct urb * int_urb ;
/* IR device properties */
2017-08-07 23:20:58 +03:00
u64 rc_proto ;
2010-06-21 00:16:52 +04:00
} ;
void tm6000_ir_wait ( struct tm6000_core * dev , u8 state )
{
struct tm6000_IR * ir = dev - > ir ;
if ( ! dev - > ir )
return ;
2011-11-29 18:30:30 +04:00
dprintk ( 2 , " %s: %i \n " , __func__ , ir - > wait ) ;
2010-06-21 00:16:52 +04:00
if ( state )
ir - > wait = 1 ;
else
ir - > wait = 0 ;
}
static int tm6000_ir_config ( struct tm6000_IR * ir )
{
struct tm6000_core * dev = ir - > dev ;
2011-11-29 18:30:30 +04:00
u32 pulse = 0 , leader = 0 ;
dprintk ( 2 , " %s \n " , __func__ ) ;
/*
* The IR decoder supports RC - 5 or NEC , with a configurable timing .
* The timing configuration there is not that accurate , as it uses
* approximate values . The NEC spec mentions a 562.5 unit period ,
* and RC - 5 uses a 888.8 period .
* Currently , driver assumes a clock provided by a 12 MHz XTAL , but
* a modprobe parameter can adjust it .
* Adjustments are required for other timings .
* It seems that the 900 ms timing for NEC is used to detect a RC - 5
* IR , in order to discard such decoding
*/
2010-06-21 00:16:52 +04:00
2017-08-07 23:20:58 +03:00
switch ( ir - > rc_proto ) {
case RC_PROTO_BIT_NEC :
2011-11-29 18:30:30 +04:00
leader = 900 ; /* ms */
pulse = 700 ; /* ms - the actual value would be 562 */
2010-12-22 11:57:46 +03:00
break ;
default :
2017-08-07 23:20:58 +03:00
case RC_PROTO_BIT_RC5 :
2011-11-29 18:30:30 +04:00
leader = 900 ; /* ms - from the NEC decoding */
pulse = 1780 ; /* ms - The actual value would be 1776 */
2010-12-22 11:57:46 +03:00
break ;
2010-06-21 00:16:52 +04:00
}
2010-12-22 11:57:46 +03:00
2011-11-29 18:30:30 +04:00
pulse = ir_clock_mhz * pulse ;
leader = ir_clock_mhz * leader ;
2017-08-07 23:20:58 +03:00
if ( ir - > rc_proto = = RC_PROTO_BIT_NEC )
2011-11-29 18:30:30 +04:00
leader = leader | 0x8000 ;
dprintk ( 2 , " %s: %s, %d MHz, leader = 0x%04x, pulse = 0x%06x \n " ,
__func__ ,
2017-08-07 23:20:58 +03:00
( ir - > rc_proto = = RC_PROTO_BIT_NEC ) ? " NEC " : " RC-5 " ,
2011-11-29 18:30:30 +04:00
ir_clock_mhz , leader , pulse ) ;
/* Remote WAKEUP = enable, normal mode, from IR decoder output */
tm6000_set_reg ( dev , TM6010_REQ07_RE5_REMOTE_WAKEUP , 0xfe ) ;
/* Enable IR reception on non-busrt mode */
tm6000_set_reg ( dev , TM6010_REQ07_RD8_IR , 0x2f ) ;
/* IR_WKUP_SEL = Low byte in decoded IR data */
tm6000_set_reg ( dev , TM6010_REQ07_RDA_IR_WAKEUP_SEL , 0xff ) ;
/* IR_WKU_ADD code */
tm6000_set_reg ( dev , TM6010_REQ07_RDB_IR_WAKEUP_ADD , 0xff ) ;
tm6000_set_reg ( dev , TM6010_REQ07_RDC_IR_LEADER1 , leader > > 8 ) ;
tm6000_set_reg ( dev , TM6010_REQ07_RDD_IR_LEADER0 , leader ) ;
tm6000_set_reg ( dev , TM6010_REQ07_RDE_IR_PULSE_CNT1 , pulse > > 8 ) ;
tm6000_set_reg ( dev , TM6010_REQ07_RDF_IR_PULSE_CNT0 , pulse ) ;
if ( ! ir - > polling )
tm6000_set_reg ( dev , REQ_04_EN_DISABLE_MCU_INT , 2 , 0 ) ;
else
tm6000_set_reg ( dev , REQ_04_EN_DISABLE_MCU_INT , 2 , 1 ) ;
msleep ( 10 ) ;
/* Shows that IR is working via the LED */
tm6000_flash_led ( dev , 0 ) ;
msleep ( 100 ) ;
tm6000_flash_led ( dev , 1 ) ;
ir - > pwled = 1 ;
2010-06-21 00:16:52 +04:00
return 0 ;
}
2014-04-04 03:31:30 +04:00
static void tm6000_ir_keydown ( struct tm6000_IR * ir ,
const char * buf , unsigned int len )
{
u8 device , command ;
u32 scancode ;
2017-08-07 23:20:58 +03:00
enum rc_proto protocol ;
2014-04-04 03:31:30 +04:00
if ( len < 1 )
return ;
command = buf [ 0 ] ;
device = ( len > 1 ? buf [ 1 ] : 0x0 ) ;
2017-08-07 23:20:58 +03:00
switch ( ir - > rc_proto ) {
case RC_PROTO_BIT_RC5 :
protocol = RC_PROTO_RC5 ;
2014-04-04 03:31:30 +04:00
scancode = RC_SCANCODE_RC5 ( device , command ) ;
break ;
2017-08-07 23:20:58 +03:00
case RC_PROTO_BIT_NEC :
protocol = RC_PROTO_NEC ;
2014-04-04 03:31:30 +04:00
scancode = RC_SCANCODE_NEC ( device , command ) ;
break ;
default :
2017-08-07 23:20:58 +03:00
protocol = RC_PROTO_OTHER ;
2014-04-04 03:31:30 +04:00
scancode = RC_SCANCODE_OTHER ( device < < 8 | command ) ;
break ;
}
dprintk ( 1 , " %s, protocol: 0x%04x, scancode: 0x%08x \n " ,
__func__ , protocol , scancode ) ;
rc_keydown ( ir - > rc , protocol , scancode , 0 ) ;
}
2010-06-21 00:16:52 +04:00
static void tm6000_ir_urb_received ( struct urb * urb )
{
struct tm6000_core * dev = urb - > context ;
struct tm6000_IR * ir = dev - > ir ;
2011-11-29 18:30:30 +04:00
char * buf ;
2010-07-06 00:09:11 +04:00
2011-11-29 18:30:30 +04:00
dprintk ( 2 , " %s \n " , __func__ ) ;
if ( urb - > status < 0 | | urb - > actual_length < = 0 ) {
printk ( KERN_INFO " tm6000: IR URB failure: status: %i, length %i \n " ,
urb - > status , urb - > actual_length ) ;
ir - > submit_urb = 1 ;
schedule_delayed_work ( & ir - > work , msecs_to_jiffies ( URB_SUBMIT_DELAY ) ) ;
return ;
}
buf = urb - > transfer_buffer ;
2010-06-21 00:16:52 +04:00
2011-11-29 18:30:30 +04:00
if ( ir_debug )
print_hex_dump ( KERN_DEBUG , " tm6000: IR data: " ,
DUMP_PREFIX_OFFSET , 16 , 1 ,
buf , urb - > actual_length , false ) ;
2010-06-21 00:16:52 +04:00
2014-04-04 03:31:30 +04:00
tm6000_ir_keydown ( ir , urb - > transfer_buffer , urb - > actual_length ) ;
2010-06-21 00:16:52 +04:00
2012-04-20 14:30:48 +04:00
usb_submit_urb ( urb , GFP_ATOMIC ) ;
2011-11-29 18:30:30 +04:00
/*
* Flash the led . We can ' t do it here , as it is running on IRQ context .
* So , use the scheduler to do it , in a few ms .
*/
ir - > pwled = 2 ;
schedule_delayed_work ( & ir - > work , msecs_to_jiffies ( 10 ) ) ;
2010-06-21 00:16:52 +04:00
}
2011-11-29 18:30:30 +04:00
static void tm6000_ir_handle_key ( struct work_struct * work )
2010-06-21 00:16:52 +04:00
{
2011-11-29 18:30:30 +04:00
struct tm6000_IR * ir = container_of ( work , struct tm6000_IR , work . work ) ;
2010-06-21 00:16:52 +04:00
struct tm6000_core * dev = ir - > dev ;
int rc ;
u8 buf [ 2 ] ;
2011-11-29 18:30:30 +04:00
if ( ir - > wait )
return ;
2010-09-09 21:45:22 +04:00
2011-11-29 18:30:30 +04:00
dprintk ( 3 , " %s \n " , __func__ ) ;
2010-09-09 21:45:22 +04:00
2011-11-29 18:30:30 +04:00
rc = tm6000_read_write_usb ( dev , USB_DIR_IN |
USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
REQ_02_GET_IR_CODE , 0 , 0 , buf , 2 ) ;
if ( rc < 0 )
return ;
2010-06-21 00:16:52 +04:00
2011-11-29 18:30:30 +04:00
/* Check if something was read */
2014-04-04 03:31:30 +04:00
if ( ( buf [ 0 ] & 0xff ) = = 0xff ) {
2011-11-29 18:30:30 +04:00
if ( ! ir - > pwled ) {
tm6000_flash_led ( dev , 1 ) ;
ir - > pwled = 1 ;
2010-09-09 21:45:22 +04:00
}
2011-11-29 18:30:30 +04:00
return ;
2010-06-21 00:16:52 +04:00
}
2011-11-29 18:30:30 +04:00
2014-04-04 03:31:30 +04:00
tm6000_ir_keydown ( ir , buf , rc ) ;
2011-11-29 18:30:30 +04:00
tm6000_flash_led ( dev , 0 ) ;
ir - > pwled = 0 ;
/* Re-schedule polling */
schedule_delayed_work ( & ir - > work , msecs_to_jiffies ( ir - > polling ) ) ;
2010-06-21 00:16:52 +04:00
}
2011-11-29 18:30:30 +04:00
static void tm6000_ir_int_work ( struct work_struct * work )
2010-06-21 00:16:52 +04:00
{
2011-11-29 18:30:30 +04:00
struct tm6000_IR * ir = container_of ( work , struct tm6000_IR , work . work ) ;
2010-12-22 11:57:46 +03:00
struct tm6000_core * dev = ir - > dev ;
2011-11-29 18:30:30 +04:00
int rc ;
2010-06-21 00:16:52 +04:00
2011-11-29 18:30:30 +04:00
dprintk ( 3 , " %s, submit_urb = %d, pwled = %d \n " , __func__ , ir - > submit_urb ,
ir - > pwled ) ;
2010-06-21 00:16:52 +04:00
2011-11-29 18:30:30 +04:00
if ( ir - > submit_urb ) {
dprintk ( 3 , " Resubmit urb \n " ) ;
tm6000_set_reg ( dev , REQ_04_EN_DISABLE_MCU_INT , 2 , 0 ) ;
rc = usb_submit_urb ( ir - > int_urb , GFP_ATOMIC ) ;
if ( rc < 0 ) {
printk ( KERN_ERR " tm6000: Can't submit an IR interrupt. Error %i \n " ,
rc ) ;
/* Retry in 100 ms */
schedule_delayed_work ( & ir - > work , msecs_to_jiffies ( URB_SUBMIT_DELAY ) ) ;
return ;
}
ir - > submit_urb = 0 ;
2010-12-22 11:57:46 +03:00
}
2011-11-29 18:30:30 +04:00
/* Led is enabled only if USB submit doesn't fail */
if ( ir - > pwled = = 2 ) {
2010-12-22 11:57:46 +03:00
tm6000_flash_led ( dev , 0 ) ;
2011-11-29 18:30:30 +04:00
ir - > pwled = 0 ;
schedule_delayed_work ( & ir - > work , msecs_to_jiffies ( URB_INT_LED_DELAY ) ) ;
} else if ( ! ir - > pwled ) {
tm6000_flash_led ( dev , 1 ) ;
ir - > pwled = 1 ;
2010-06-21 00:16:52 +04:00
}
}
2010-10-29 23:08:23 +04:00
static int tm6000_ir_start ( struct rc_dev * rc )
2010-06-21 00:16:52 +04:00
{
2010-10-29 23:08:23 +04:00
struct tm6000_IR * ir = rc - > priv ;
2010-06-21 00:16:52 +04:00
2011-11-29 18:30:30 +04:00
dprintk ( 2 , " %s \n " , __func__ ) ;
2010-06-21 00:16:52 +04:00
schedule_delayed_work ( & ir - > work , 0 ) ;
return 0 ;
}
2010-10-29 23:08:23 +04:00
static void tm6000_ir_stop ( struct rc_dev * rc )
2010-06-21 00:16:52 +04:00
{
2010-10-29 23:08:23 +04:00
struct tm6000_IR * ir = rc - > priv ;
2010-06-21 00:16:52 +04:00
2011-11-29 18:30:30 +04:00
dprintk ( 2 , " %s \n " , __func__ ) ;
2010-06-21 00:16:52 +04:00
cancel_delayed_work_sync ( & ir - > work ) ;
}
2017-08-07 23:20:58 +03:00
static int tm6000_ir_change_protocol ( struct rc_dev * rc , u64 * rc_proto )
2010-06-21 00:16:52 +04:00
{
2010-10-29 23:08:23 +04:00
struct tm6000_IR * ir = rc - > priv ;
2010-06-21 00:16:52 +04:00
2010-12-22 11:57:46 +03:00
if ( ! ir )
return 0 ;
2011-11-29 18:30:30 +04:00
dprintk ( 2 , " %s \n " , __func__ ) ;
2017-08-07 23:20:58 +03:00
ir - > rc_proto = * rc_proto ;
2010-06-21 00:16:52 +04:00
tm6000_ir_config ( ir ) ;
/* TODO */
return 0 ;
}
2011-11-29 18:30:30 +04:00
static int __tm6000_ir_int_start ( struct rc_dev * rc )
2010-12-22 11:57:46 +03:00
{
2011-11-29 18:30:30 +04:00
struct tm6000_IR * ir = rc - > priv ;
2012-08-14 18:49:46 +04:00
struct tm6000_core * dev ;
2010-12-22 11:57:46 +03:00
int pipe , size ;
int err = - ENOMEM ;
if ( ! ir )
return - ENODEV ;
2012-08-14 18:49:46 +04:00
dev = ir - > dev ;
2010-12-22 11:57:46 +03:00
2011-11-29 18:30:30 +04:00
dprintk ( 2 , " %s \n " , __func__ ) ;
ir - > int_urb = usb_alloc_urb ( 0 , GFP_ATOMIC ) ;
2011-01-03 08:44:38 +03:00
if ( ! ir - > int_urb )
return - ENOMEM ;
2010-12-22 11:57:46 +03:00
pipe = usb_rcvintpipe ( dev - > udev ,
dev - > int_in . endp - > desc . bEndpointAddress
& USB_ENDPOINT_NUMBER_MASK ) ;
size = usb_maxpacket ( dev - > udev , pipe , usb_pipeout ( pipe ) ) ;
2011-11-29 18:30:30 +04:00
dprintk ( 1 , " IR max size: %d \n " , size ) ;
2010-12-22 11:57:46 +03:00
2011-11-29 18:30:30 +04:00
ir - > int_urb - > transfer_buffer = kzalloc ( size , GFP_ATOMIC ) ;
2017-09-14 15:34:39 +03:00
if ( ! ir - > int_urb - > transfer_buffer ) {
2010-12-22 11:57:46 +03:00
usb_free_urb ( ir - > int_urb ) ;
return err ;
}
2011-11-29 18:30:30 +04:00
dprintk ( 1 , " int interval: %d \n " , dev - > int_in . endp - > desc . bInterval ) ;
2010-12-22 11:57:46 +03:00
usb_fill_int_urb ( ir - > int_urb , dev - > udev , pipe ,
ir - > int_urb - > transfer_buffer , size ,
tm6000_ir_urb_received , dev ,
dev - > int_in . endp - > desc . bInterval ) ;
2011-11-29 18:30:30 +04:00
ir - > submit_urb = 1 ;
schedule_delayed_work ( & ir - > work , msecs_to_jiffies ( URB_SUBMIT_DELAY ) ) ;
2010-12-22 11:57:46 +03:00
return 0 ;
}
2011-11-29 18:30:30 +04:00
static void __tm6000_ir_int_stop ( struct rc_dev * rc )
2010-12-22 11:57:46 +03:00
{
2011-11-29 18:30:30 +04:00
struct tm6000_IR * ir = rc - > priv ;
2010-12-22 11:57:46 +03:00
2011-11-29 18:30:30 +04:00
if ( ! ir | | ! ir - > int_urb )
2010-12-22 11:57:46 +03:00
return ;
2011-11-29 18:30:30 +04:00
dprintk ( 2 , " %s \n " , __func__ ) ;
2010-12-22 11:57:46 +03:00
usb_kill_urb ( ir - > int_urb ) ;
kfree ( ir - > int_urb - > transfer_buffer ) ;
usb_free_urb ( ir - > int_urb ) ;
ir - > int_urb = NULL ;
2011-11-29 18:30:30 +04:00
}
int tm6000_ir_int_start ( struct tm6000_core * dev )
{
struct tm6000_IR * ir = dev - > ir ;
2011-11-30 21:57:28 +04:00
if ( ! ir )
2011-12-11 18:20:49 +04:00
return 0 ;
2011-11-30 21:57:28 +04:00
2011-11-29 18:30:30 +04:00
return __tm6000_ir_int_start ( ir - > rc ) ;
}
void tm6000_ir_int_stop ( struct tm6000_core * dev )
{
struct tm6000_IR * ir = dev - > ir ;
2011-11-30 21:57:28 +04:00
if ( ! ir | | ! ir - > rc )
return ;
2011-11-29 18:30:30 +04:00
__tm6000_ir_int_stop ( ir - > rc ) ;
2010-12-22 11:57:46 +03:00
}
2010-06-21 00:16:52 +04:00
int tm6000_ir_init ( struct tm6000_core * dev )
{
struct tm6000_IR * ir ;
2010-10-29 23:08:23 +04:00
struct rc_dev * rc ;
2010-06-21 00:16:52 +04:00
int err = - ENOMEM ;
2017-08-07 23:20:58 +03:00
u64 rc_proto ;
2010-06-21 00:16:52 +04:00
if ( ! enable_ir )
return - ENODEV ;
if ( ! dev - > caps . has_remote )
return 0 ;
if ( ! dev - > ir_codes )
return 0 ;
2011-11-29 18:30:30 +04:00
ir = kzalloc ( sizeof ( * ir ) , GFP_ATOMIC ) ;
2016-12-16 11:50:58 +03:00
rc = rc_allocate_device ( RC_DRIVER_SCANCODE ) ;
2011-01-03 08:47:40 +03:00
if ( ! ir | | ! rc )
2010-10-29 23:08:23 +04:00
goto out ;
2010-06-21 00:16:52 +04:00
2011-12-14 09:17:36 +04:00
dprintk ( 2 , " %s \n " , __func__ ) ;
2010-06-21 00:16:52 +04:00
/* record handles to ourself */
ir - > dev = dev ;
dev - > ir = ir ;
2010-10-29 23:08:23 +04:00
ir - > rc = rc ;
2010-06-21 00:16:52 +04:00
2011-11-29 18:30:30 +04:00
/* input setup */
2017-08-07 23:20:58 +03:00
rc - > allowed_protocols = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_NEC ;
2017-02-28 01:29:14 +03:00
/* Needed, in order to support NEC remotes with 24 or 32 bits */
2014-04-04 03:32:26 +04:00
rc - > scancode_mask = 0xffff ;
2010-10-29 23:08:23 +04:00
rc - > priv = ir ;
rc - > change_protocol = tm6000_ir_change_protocol ;
2011-12-06 16:39:35 +04:00
if ( dev - > int_in . endp ) {
2011-11-29 18:30:30 +04:00
rc - > open = __tm6000_ir_int_start ;
rc - > close = __tm6000_ir_int_stop ;
INIT_DELAYED_WORK ( & ir - > work , tm6000_ir_int_work ) ;
} else {
rc - > open = tm6000_ir_start ;
rc - > close = tm6000_ir_stop ;
ir - > polling = 50 ;
INIT_DELAYED_WORK ( & ir - > work , tm6000_ir_handle_key ) ;
}
2010-06-21 00:16:52 +04:00
snprintf ( ir - > name , sizeof ( ir - > name ) , " tm5600/60x0 IR (%s) " ,
dev - > name ) ;
usb_make_path ( dev - > udev , ir - > phys , sizeof ( ir - > phys ) ) ;
strlcat ( ir - > phys , " /input0 " , sizeof ( ir - > phys ) ) ;
2017-08-07 23:20:58 +03:00
rc_proto = RC_PROTO_BIT_UNKNOWN ;
tm6000_ir_change_protocol ( rc , & rc_proto ) ;
2010-06-21 00:16:52 +04:00
2017-07-01 19:13:19 +03:00
rc - > device_name = ir - > name ;
2010-10-29 23:08:23 +04:00
rc - > input_phys = ir - > phys ;
rc - > input_id . bustype = BUS_USB ;
rc - > input_id . version = 1 ;
rc - > input_id . vendor = le16_to_cpu ( dev - > udev - > descriptor . idVendor ) ;
rc - > input_id . product = le16_to_cpu ( dev - > udev - > descriptor . idProduct ) ;
rc - > map_name = dev - > ir_codes ;
rc - > driver_name = " tm6000 " ;
rc - > dev . parent = & dev - > udev - > dev ;
2010-06-21 00:16:52 +04:00
/* ir register */
2010-10-29 23:08:23 +04:00
err = rc_register_device ( rc ) ;
2010-06-21 00:16:52 +04:00
if ( err )
2010-10-29 23:08:23 +04:00
goto out ;
2010-06-21 00:16:52 +04:00
return 0 ;
2010-10-29 23:08:23 +04:00
out :
2010-06-21 00:16:52 +04:00
dev - > ir = NULL ;
2010-10-29 23:08:23 +04:00
rc_free_device ( rc ) ;
2010-06-21 00:16:52 +04:00
kfree ( ir ) ;
return err ;
}
int tm6000_ir_fini ( struct tm6000_core * dev )
{
struct tm6000_IR * ir = dev - > ir ;
/* skip detach on non attached board */
if ( ! ir )
return 0 ;
2011-11-29 18:30:30 +04:00
dprintk ( 2 , " %s \n " , __func__ ) ;
if ( ! ir - > polling )
__tm6000_ir_int_stop ( ir - > rc ) ;
tm6000_ir_stop ( ir - > rc ) ;
/* Turn off the led */
tm6000_flash_led ( dev , 0 ) ;
ir - > pwled = 0 ;
2012-01-29 04:41:52 +04:00
rc_unregister_device ( ir - > rc ) ;
2010-06-21 00:16:52 +04:00
kfree ( ir ) ;
dev - > ir = NULL ;
return 0 ;
}