2009-09-27 20:55:41 -03:00
/*
* Driver for the Conexant CX23885 / 7 / 8 PCIe bridge
*
* Infrared remote control input device
*
* Most of this file is
*
* Copyright ( C ) 2009 Andy Walls < awalls @ radix . net >
*
* However , the cx23885_input_ { init , fini } functions contained herein are
* derived from Linux kernel files linux / media / video / . . . / . . . - input . c marked as :
*
* Copyright ( C ) 2008 < srinivasa . deevi at conexant dot com >
* Copyright ( C ) 2005 Ludovico Cavedon < cavedon @ sssup . it >
* Markus Rechberger < mrechberger @ gmail . com >
* Mauro Carvalho Chehab < mchehab @ infradead . org >
* Sascha Sommer < saschasommer @ freenet . de >
* Copyright ( C ) 2004 , 2005 Chris Pascoe
* Copyright ( C ) 2003 , 2004 Gerd Knorr
* Copyright ( C ) 2003 Pavel Machek
*
* 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 .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA
* 02110 - 1301 , USA .
*/
# include <linux/input.h>
# include <media/ir-common.h>
# include <media/v4l2-subdev.h>
# include "cx23885.h"
# define RC5_BITS 14
# define RC5_HALF_BITS (2*RC5_BITS)
# define RC5_HALF_BITS_MASK ((1 << RC5_HALF_BITS) - 1)
# define RC5_START_BITS_NORMAL 0x3 /* Command range 0 - 63 */
# define RC5_START_BITS_EXTENDED 0x2 /* Command range 64 - 127 */
# define RC5_EXTENDED_COMMAND_OFFSET 64
static inline unsigned int rc5_command ( u32 rc5_baseband )
{
return RC5_INSTR ( rc5_baseband ) +
( ( RC5_START ( rc5_baseband ) = = RC5_START_BITS_EXTENDED )
? RC5_EXTENDED_COMMAND_OFFSET : 0 ) ;
}
static void cx23885_input_process_raw_rc5 ( struct cx23885_dev * dev )
{
struct card_ir * ir_input = dev - > ir_input ;
unsigned int code , command ;
u32 rc5 ;
/* Ignore codes that are too short to be valid RC-5 */
if ( ir_input - > last_bit < ( RC5_HALF_BITS - 1 ) )
return ;
/* The library has the manchester coding backwards; XOR to adapt. */
code = ( ir_input - > code & RC5_HALF_BITS_MASK ) ^ RC5_HALF_BITS_MASK ;
rc5 = ir_rc5_decode ( code ) ;
switch ( RC5_START ( rc5 ) ) {
case RC5_START_BITS_NORMAL :
break ;
case RC5_START_BITS_EXTENDED :
/* Don't allow if the remote only emits standard commands */
if ( ir_input - > start = = RC5_START_BITS_NORMAL )
return ;
break ;
default :
return ;
}
if ( ir_input - > addr ! = RC5_ADDR ( rc5 ) )
return ;
/* Don't generate a keypress for RC-5 auto-repeated keypresses */
command = rc5_command ( rc5 ) ;
if ( RC5_TOGGLE ( rc5 ) ! = RC5_TOGGLE ( ir_input - > last_rc5 ) | |
command ! = rc5_command ( ir_input - > last_rc5 ) | |
/* Catch T == 0, CMD == 0 (e.g. '0') as first keypress after init */
RC5_START ( ir_input - > last_rc5 ) = = 0 ) {
/* This keypress is differnet: not an auto repeat */
ir_input_nokey ( ir_input - > dev , & ir_input - > ir ) ;
2009-11-27 22:40:22 -03:00
ir_input_keydown ( ir_input - > dev , & ir_input - > ir , command ) ;
2009-09-27 20:55:41 -03:00
}
ir_input - > last_rc5 = rc5 ;
/* Schedule when we should do the key up event: ir_input_nokey() */
mod_timer ( & ir_input - > timer_keyup ,
jiffies + msecs_to_jiffies ( ir_input - > rc5_key_timeout ) ) ;
}
static void cx23885_input_next_pulse_width_rc5 ( struct cx23885_dev * dev ,
u32 ns_pulse )
{
const int rc5_quarterbit_ns = 444444 ; /* 32 cycles/36 kHz/2 = 444 us */
struct card_ir * ir_input = dev - > ir_input ;
int i , level , quarterbits , halfbits ;
if ( ! ir_input - > active ) {
ir_input - > active = 1 ;
/* assume an initial space that we may not detect or measure */
ir_input - > code = 0 ;
ir_input - > last_bit = 0 ;
}
if ( ns_pulse = = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END ) {
ir_input - > last_bit + + ; /* Account for the final space */
ir_input - > active = 0 ;
cx23885_input_process_raw_rc5 ( dev ) ;
return ;
}
level = ( ns_pulse & V4L2_SUBDEV_IR_PULSE_LEVEL_MASK ) ? 1 : 0 ;
/* Skip any leading space to sync to the start bit */
if ( ir_input - > last_bit = = 0 & & level = = 0 )
return ;
/*
* With valid RC - 5 we can get up to two consecutive half - bits in a
* single pulse measurment . Experiments have shown that the duration
* of a half - bit can vary . Make sure we always end up with an even
* number of quarter bits at the same level ( mark or space ) .
*/
ns_pulse & = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS ;
quarterbits = ns_pulse / rc5_quarterbit_ns ;
if ( quarterbits & 1 )
quarterbits + + ;
halfbits = quarterbits / 2 ;
for ( i = 0 ; i < halfbits ; i + + ) {
ir_input - > last_bit + + ;
ir_input - > code | = ( level < < ir_input - > last_bit ) ;
if ( ir_input - > last_bit > = RC5_HALF_BITS - 1 ) {
ir_input - > active = 0 ;
cx23885_input_process_raw_rc5 ( dev ) ;
/*
* If level is 1 , a leading mark is invalid for RC5 .
* If level is 0 , we scan past extra intial space .
* Either way we don ' t want to reactivate collecting
* marks or spaces here with any left over half - bits .
*/
break ;
}
}
}
static void cx23885_input_process_pulse_widths_rc5 ( struct cx23885_dev * dev ,
bool add_eom )
{
struct card_ir * ir_input = dev - > ir_input ;
struct ir_input_state * ir_input_state = & ir_input - > ir ;
u32 ns_pulse [ RC5_HALF_BITS + 1 ] ;
ssize_t num = 0 ;
int count , i ;
do {
v4l2_subdev_call ( dev - > sd_ir , ir , rx_read , ( u8 * ) ns_pulse ,
sizeof ( ns_pulse ) , & num ) ;
count = num / sizeof ( u32 ) ;
/* Append an end of Rx seq, if the caller requested */
if ( add_eom & & count < ARRAY_SIZE ( ns_pulse ) ) {
ns_pulse [ count ] = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END ;
count + + ;
}
/* Just drain the Rx FIFO, if we're called, but not RC-5 */
if ( ir_input_state - > ir_type ! = IR_TYPE_RC5 )
continue ;
for ( i = 0 ; i < count ; i + + )
cx23885_input_next_pulse_width_rc5 ( dev , ns_pulse [ i ] ) ;
} while ( num ! = 0 ) ;
}
void cx23885_input_rx_work_handler ( struct cx23885_dev * dev , u32 events )
{
struct v4l2_subdev_ir_parameters params ;
int overrun , data_available ;
if ( dev - > sd_ir = = NULL | | events = = 0 )
return ;
switch ( dev - > board ) {
case CX23885_BOARD_HAUPPAUGE_HVR1850 :
2009-11-11 15:46:09 -03:00
case CX23885_BOARD_HAUPPAUGE_HVR1290 :
2009-09-27 20:55:41 -03:00
/*
* The only board we handle right now . However other boards
* using the CX2388x integrated IR controller should be similar
*/
break ;
default :
return ;
}
overrun = events & ( V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN |
V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN ) ;
data_available = events & ( V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED |
V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ ) ;
if ( overrun ) {
/* If there was a FIFO overrun, stop the device */
v4l2_subdev_call ( dev - > sd_ir , ir , rx_g_parameters , & params ) ;
params . enable = false ;
/* Mitigate race with cx23885_input_ir_stop() */
params . shutdown = atomic_read ( & dev - > ir_input_stopping ) ;
v4l2_subdev_call ( dev - > sd_ir , ir , rx_s_parameters , & params ) ;
}
if ( data_available )
cx23885_input_process_pulse_widths_rc5 ( dev , overrun ) ;
if ( overrun ) {
/* If there was a FIFO overrun, clear & restart the device */
params . enable = true ;
/* Mitigate race with cx23885_input_ir_stop() */
params . shutdown = atomic_read ( & dev - > ir_input_stopping ) ;
v4l2_subdev_call ( dev - > sd_ir , ir , rx_s_parameters , & params ) ;
}
}
static void cx23885_input_ir_start ( struct cx23885_dev * dev )
{
struct card_ir * ir_input = dev - > ir_input ;
struct ir_input_state * ir_input_state = & ir_input - > ir ;
struct v4l2_subdev_ir_parameters params ;
if ( dev - > sd_ir = = NULL )
return ;
atomic_set ( & dev - > ir_input_stopping , 0 ) ;
/* keyup timer set up, if needed */
switch ( dev - > board ) {
case CX23885_BOARD_HAUPPAUGE_HVR1850 :
2009-11-11 15:46:09 -03:00
case CX23885_BOARD_HAUPPAUGE_HVR1290 :
2009-09-27 20:55:41 -03:00
setup_timer ( & ir_input - > timer_keyup ,
ir_rc5_timer_keyup , /* Not actually RC-5 specific */
( unsigned long ) ir_input ) ;
if ( ir_input_state - > ir_type = = IR_TYPE_RC5 ) {
/*
* RC - 5 repeats a held key every
* 64 bits * ( 2 * 32 / 36000 ) sec / bit = 113.778 ms
*/
ir_input - > rc5_key_timeout = 115 ;
}
break ;
}
v4l2_subdev_call ( dev - > sd_ir , ir , rx_g_parameters , & params ) ;
switch ( dev - > board ) {
case CX23885_BOARD_HAUPPAUGE_HVR1850 :
2009-11-11 15:46:09 -03:00
case CX23885_BOARD_HAUPPAUGE_HVR1290 :
2009-09-27 20:55:41 -03:00
/*
* The IR controller on this board only returns pulse widths .
* Any other mode setting will fail to set up the device .
*/
params . mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH ;
params . enable = true ;
params . interrupt_enable = true ;
params . shutdown = false ;
/* Setup for baseband compatible with both RC-5 and RC-6A */
params . modulation = false ;
/* RC-5: 2,222,222 ns = 1/36 kHz * 32 cycles * 2 marks * 1.25*/
/* RC-6A: 3,333,333 ns = 1/36 kHz * 16 cycles * 6 marks * 1.25*/
params . max_pulse_width = 3333333 ; /* ns */
/* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */
/* RC-6A: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */
params . noise_filter_min_width = 333333 ; /* ns */
/*
* This board has inverted receive sense :
* mark is received as low logic level ;
* falling edges are detected as rising edges ; etc .
*/
params . invert = true ;
break ;
}
v4l2_subdev_call ( dev - > sd_ir , ir , rx_s_parameters , & params ) ;
}
static void cx23885_input_ir_stop ( struct cx23885_dev * dev )
{
struct card_ir * ir_input = dev - > ir_input ;
struct v4l2_subdev_ir_parameters params ;
if ( dev - > sd_ir = = NULL )
return ;
/*
* Stop the sd_ir subdevice from generating notifications and
* scheduling work .
* It is shutdown this way in order to mitigate a race with
* cx23885_input_rx_work_handler ( ) in the overrun case , which could
* re - enable the subdevice .
*/
atomic_set ( & dev - > ir_input_stopping , 1 ) ;
v4l2_subdev_call ( dev - > sd_ir , ir , rx_g_parameters , & params ) ;
while ( params . shutdown = = false ) {
params . enable = false ;
params . interrupt_enable = false ;
params . shutdown = true ;
v4l2_subdev_call ( dev - > sd_ir , ir , rx_s_parameters , & params ) ;
v4l2_subdev_call ( dev - > sd_ir , ir , rx_g_parameters , & params ) ;
}
flush_scheduled_work ( ) ;
switch ( dev - > board ) {
case CX23885_BOARD_HAUPPAUGE_HVR1850 :
2009-11-11 15:46:09 -03:00
case CX23885_BOARD_HAUPPAUGE_HVR1290 :
2009-09-27 20:55:41 -03:00
del_timer_sync ( & ir_input - > timer_keyup ) ;
break ;
}
}
int cx23885_input_init ( struct cx23885_dev * dev )
{
struct card_ir * ir ;
struct input_dev * input_dev ;
struct ir_scancode_table * ir_codes = NULL ;
int ir_type , ir_addr , ir_start ;
int ret ;
/*
* If the IR device ( hardware registers , chip , GPIO lines , etc . ) isn ' t
* encapsulated in a v4l2_subdev , then I ' m not going to deal with it .
*/
if ( dev - > sd_ir = = NULL )
return - ENODEV ;
switch ( dev - > board ) {
case CX23885_BOARD_HAUPPAUGE_HVR1850 :
2009-11-11 15:46:09 -03:00
case CX23885_BOARD_HAUPPAUGE_HVR1290 :
2009-09-27 20:55:41 -03:00
/* Parameters for the grey Hauppauge remote for the HVR-1850 */
ir_codes = & ir_codes_hauppauge_new_table ;
ir_type = IR_TYPE_RC5 ;
ir_addr = 0x1e ; /* RC-5 system bits emitted by the remote */
ir_start = RC5_START_BITS_NORMAL ; /* A basic RC-5 remote */
break ;
}
if ( ir_codes = = NULL )
return - ENODEV ;
ir = kzalloc ( sizeof ( * ir ) , GFP_KERNEL ) ;
input_dev = input_allocate_device ( ) ;
if ( ! ir | | ! input_dev ) {
ret = - ENOMEM ;
goto err_out_free ;
}
ir - > dev = input_dev ;
ir - > addr = ir_addr ;
ir - > start = ir_start ;
/* init input device */
snprintf ( ir - > name , sizeof ( ir - > name ) , " cx23885 IR (%s) " ,
cx23885_boards [ dev - > board ] . name ) ;
snprintf ( ir - > phys , sizeof ( ir - > phys ) , " pci-%s/ir0 " , pci_name ( dev - > pci ) ) ;
2009-11-29 08:19:59 -03:00
ret = ir_input_init ( input_dev , & ir - > ir , ir_type , ir_codes ) ;
if ( ret < 0 )
goto err_out_free ;
2009-09-27 20:55:41 -03:00
input_dev - > name = ir - > name ;
input_dev - > phys = ir - > phys ;
input_dev - > id . bustype = BUS_PCI ;
input_dev - > id . version = 1 ;
if ( dev - > pci - > subsystem_vendor ) {
input_dev - > id . vendor = dev - > pci - > subsystem_vendor ;
input_dev - > id . product = dev - > pci - > subsystem_device ;
} else {
input_dev - > id . vendor = dev - > pci - > vendor ;
input_dev - > id . product = dev - > pci - > device ;
}
input_dev - > dev . parent = & dev - > pci - > dev ;
dev - > ir_input = ir ;
cx23885_input_ir_start ( dev ) ;
ret = input_register_device ( ir - > dev ) ;
if ( ret )
goto err_out_stop ;
return 0 ;
err_out_stop :
cx23885_input_ir_stop ( dev ) ;
dev - > ir_input = NULL ;
err_out_free :
2009-11-29 08:19:59 -03:00
ir_input_free ( input_dev ) ;
2009-09-27 20:55:41 -03:00
input_free_device ( input_dev ) ;
kfree ( ir ) ;
return ret ;
}
void cx23885_input_fini ( struct cx23885_dev * dev )
{
/* Always stop the IR hardware from generating interrupts */
cx23885_input_ir_stop ( dev ) ;
if ( dev - > ir_input = = NULL )
return ;
2009-11-29 08:19:59 -03:00
ir_input_free ( dev - > ir_input - > dev ) ;
2009-09-27 20:55:41 -03:00
input_unregister_device ( dev - > ir_input - > dev ) ;
kfree ( dev - > ir_input ) ;
dev - > ir_input = NULL ;
}