2015-06-29 11:09:42 -03:00
/*
* SMI PCIe driver for DVBSky cards .
*
* Copyright ( C ) 2014 Max nibble < nibble . max @ gmail . com >
*
* 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 .
*/
# include "smipcie.h"
static void smi_ir_enableInterrupt ( struct smi_rc * ir )
{
struct smi_dev * dev = ir - > dev ;
smi_write ( MSI_INT_ENA_SET , IR_X_INT ) ;
}
static void smi_ir_disableInterrupt ( struct smi_rc * ir )
{
struct smi_dev * dev = ir - > dev ;
smi_write ( MSI_INT_ENA_CLR , IR_X_INT ) ;
}
static void smi_ir_clearInterrupt ( struct smi_rc * ir )
{
struct smi_dev * dev = ir - > dev ;
smi_write ( MSI_INT_STATUS_CLR , IR_X_INT ) ;
}
static void smi_ir_stop ( struct smi_rc * ir )
{
struct smi_dev * dev = ir - > dev ;
smi_ir_disableInterrupt ( ir ) ;
smi_clear ( IR_Init_Reg , 0x80 ) ;
}
# define BITS_PER_COMMAND 14
# define GROUPS_PER_BIT 2
# define IR_RC5_MIN_BIT 36
# define IR_RC5_MAX_BIT 52
static u32 smi_decode_rc5 ( u8 * pData , u8 size )
{
u8 index , current_bit , bit_count ;
u8 group_array [ BITS_PER_COMMAND * GROUPS_PER_BIT + 4 ] ;
u8 group_index = 0 ;
u32 command = 0xFFFFFFFF ;
group_array [ group_index + + ] = 1 ;
for ( index = 0 ; index < size ; index + + ) {
current_bit = ( pData [ index ] & 0x80 ) ? 1 : 0 ;
bit_count = pData [ index ] & 0x7f ;
if ( ( current_bit = = 1 ) & & ( bit_count > = 2 * IR_RC5_MAX_BIT + 1 ) ) {
goto process_code ;
} else if ( ( bit_count > = IR_RC5_MIN_BIT ) & &
( bit_count < = IR_RC5_MAX_BIT ) ) {
group_array [ group_index + + ] = current_bit ;
} else if ( ( bit_count > IR_RC5_MAX_BIT ) & &
( bit_count < = 2 * IR_RC5_MAX_BIT ) ) {
group_array [ group_index + + ] = current_bit ;
group_array [ group_index + + ] = current_bit ;
} else {
goto invalid_timing ;
}
if ( group_index > = BITS_PER_COMMAND * GROUPS_PER_BIT )
goto process_code ;
if ( ( group_index = = BITS_PER_COMMAND * GROUPS_PER_BIT - 1 )
& & ( group_array [ group_index - 1 ] = = 0 ) ) {
group_array [ group_index + + ] = 1 ;
goto process_code ;
}
}
process_code :
if ( group_index = = ( BITS_PER_COMMAND * GROUPS_PER_BIT - 1 ) )
group_array [ group_index + + ] = 1 ;
if ( group_index = = BITS_PER_COMMAND * GROUPS_PER_BIT ) {
command = 0 ;
for ( index = 0 ; index < ( BITS_PER_COMMAND * GROUPS_PER_BIT ) ;
index = index + 2 ) {
if ( ( group_array [ index ] = = 1 ) & &
( group_array [ index + 1 ] = = 0 ) ) {
command | = ( 1 < < ( BITS_PER_COMMAND -
( index / 2 ) - 1 ) ) ;
} else if ( ( group_array [ index ] = = 0 ) & &
( group_array [ index + 1 ] = = 1 ) ) {
/* */
} else {
command = 0xFFFFFFFF ;
goto invalid_timing ;
}
}
}
invalid_timing :
return command ;
}
static void smi_ir_decode ( struct work_struct * work )
{
struct smi_rc * ir = container_of ( work , struct smi_rc , work ) ;
struct smi_dev * dev = ir - > dev ;
struct rc_dev * rc_dev = ir - > rc_dev ;
u32 dwIRControl , dwIRData , dwIRCode , scancode ;
u8 index , ucIRCount , readLoop , rc5_command , rc5_system , toggle ;
dwIRControl = smi_read ( IR_Init_Reg ) ;
if ( dwIRControl & rbIRVld ) {
ucIRCount = ( u8 ) smi_read ( IR_Data_Cnt ) ;
if ( ucIRCount < 4 )
goto end_ir_decode ;
readLoop = ucIRCount / 4 ;
if ( ucIRCount % 4 )
readLoop + = 1 ;
for ( index = 0 ; index < readLoop ; index + + ) {
dwIRData = smi_read ( IR_DATA_BUFFER_BASE + ( index * 4 ) ) ;
ir - > irData [ index * 4 + 0 ] = ( u8 ) ( dwIRData ) ;
ir - > irData [ index * 4 + 1 ] = ( u8 ) ( dwIRData > > 8 ) ;
ir - > irData [ index * 4 + 2 ] = ( u8 ) ( dwIRData > > 16 ) ;
ir - > irData [ index * 4 + 3 ] = ( u8 ) ( dwIRData > > 24 ) ;
}
dwIRCode = smi_decode_rc5 ( ir - > irData , ucIRCount ) ;
if ( dwIRCode ! = 0xFFFFFFFF ) {
rc5_command = dwIRCode & 0x3F ;
rc5_system = ( dwIRCode & 0x7C0 ) > > 6 ;
toggle = ( dwIRCode & 0x800 ) ? 1 : 0 ;
scancode = rc5_system < < 8 | rc5_command ;
rc_keydown ( rc_dev , RC_TYPE_RC5 , scancode , toggle ) ;
}
}
end_ir_decode :
smi_set ( IR_Init_Reg , 0x04 ) ;
smi_ir_enableInterrupt ( ir ) ;
}
/* ir functions call by main driver.*/
int smi_ir_irq ( struct smi_rc * ir , u32 int_status )
{
int handled = 0 ;
if ( int_status & IR_X_INT ) {
smi_ir_disableInterrupt ( ir ) ;
smi_ir_clearInterrupt ( ir ) ;
schedule_work ( & ir - > work ) ;
handled = 1 ;
}
return handled ;
}
void smi_ir_start ( struct smi_rc * ir )
{
struct smi_dev * dev = ir - > dev ;
smi_write ( IR_Idle_Cnt_Low , 0x00140070 ) ;
msleep ( 20 ) ;
smi_set ( IR_Init_Reg , 0x90 ) ;
smi_ir_enableInterrupt ( ir ) ;
}
int smi_ir_init ( struct smi_dev * dev )
{
int ret ;
struct rc_dev * rc_dev ;
struct smi_rc * ir = & dev - > ir ;
2016-12-16 06:50:58 -02:00
rc_dev = rc_allocate_device ( RC_DRIVER_SCANCODE ) ;
2015-06-29 11:09:42 -03:00
if ( ! rc_dev )
return - ENOMEM ;
/* init input device */
2017-07-01 12:13:19 -04:00
snprintf ( ir - > device_name , sizeof ( ir - > device_name ) , " IR (%s) " ,
2015-06-29 11:09:42 -03:00
dev - > info - > name ) ;
snprintf ( ir - > input_phys , sizeof ( ir - > input_phys ) , " pci-%s/ir0 " ,
pci_name ( dev - > pci_dev ) ) ;
rc_dev - > driver_name = " SMI_PCIe " ;
rc_dev - > input_phys = ir - > input_phys ;
2017-07-01 12:13:19 -04:00
rc_dev - > device_name = ir - > device_name ;
2015-06-29 11:09:42 -03:00
rc_dev - > input_id . bustype = BUS_PCI ;
rc_dev - > input_id . version = 1 ;
rc_dev - > input_id . vendor = dev - > pci_dev - > subsystem_vendor ;
rc_dev - > input_id . product = dev - > pci_dev - > subsystem_device ;
rc_dev - > dev . parent = & dev - > pci_dev - > dev ;
2016-03-11 03:48:03 -03:00
rc_dev - > map_name = dev - > info - > rc_map ;
2015-06-29 11:09:42 -03:00
ir - > rc_dev = rc_dev ;
ir - > dev = dev ;
INIT_WORK ( & ir - > work , smi_ir_decode ) ;
smi_ir_disableInterrupt ( ir ) ;
ret = rc_register_device ( rc_dev ) ;
if ( ret )
goto ir_err ;
return 0 ;
ir_err :
rc_free_device ( rc_dev ) ;
return ret ;
}
void smi_ir_exit ( struct smi_dev * dev )
{
struct smi_rc * ir = & dev - > ir ;
struct rc_dev * rc_dev = ir - > rc_dev ;
smi_ir_stop ( ir ) ;
rc_unregister_device ( rc_dev ) ;
ir - > rc_dev = NULL ;
}