2005-04-16 15:20:36 -07:00
/*
*
* Device driver for GPIO attached remote control interfaces
* on Conexant 2388 x based TV / DVB cards .
*
* Copyright ( c ) 2003 Pavel Machek
* Copyright ( c ) 2004 Gerd Knorr
2006-01-09 15:25:35 -02:00
* Copyright ( c ) 2004 , 2005 Chris Pascoe
2005-04-16 15:20:36 -07:00
*
* 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 . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/init.h>
# include <linux/delay.h>
# include <linux/input.h>
# include <linux/pci.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include "cx88.h"
2006-01-09 15:25:21 -02:00
# include <media/ir-common.h>
2005-04-16 15:20:36 -07:00
/* ---------------------------------------------------------------------- */
struct cx88_IR {
2005-07-12 13:58:44 -07:00
struct cx88_core * core ;
2005-09-15 02:01:53 -05:00
struct input_dev * input ;
2005-07-12 13:58:44 -07:00
struct ir_input_state ir ;
char name [ 32 ] ;
char phys [ 32 ] ;
2005-04-16 15:20:36 -07:00
/* sample from gpio pin 16 */
2006-01-09 15:25:35 -02:00
u32 sampling ;
2005-07-12 13:58:44 -07:00
u32 samples [ 16 ] ;
int scount ;
unsigned long release ;
2005-04-16 15:20:36 -07:00
/* poll external decoder */
2005-07-12 13:58:44 -07:00
int polling ;
struct work_struct work ;
struct timer_list timer ;
u32 gpio_addr ;
u32 last_gpio ;
u32 mask_keycode ;
u32 mask_keydown ;
u32 mask_keyup ;
2005-04-16 15:20:36 -07:00
} ;
static int ir_debug = 0 ;
2005-07-12 13:58:44 -07:00
module_param ( ir_debug , int , 0644 ) ; /* debug level [IR] */
2005-04-16 15:20:36 -07:00
MODULE_PARM_DESC ( ir_debug , " enable debug messages [IR] " ) ;
# define ir_dprintk(fmt, arg...) if (ir_debug) \
2005-09-09 13:03:41 -07:00
printk ( KERN_DEBUG " %s IR: " fmt , ir - > core - > name , # # arg )
2005-04-16 15:20:36 -07:00
/* ---------------------------------------------------------------------- */
static void cx88_ir_handle_key ( struct cx88_IR * ir )
{
struct cx88_core * core = ir - > core ;
2006-05-22 07:44:02 -03:00
u32 gpio , data , auxgpio ;
2005-04-16 15:20:36 -07:00
/* read gpio value */
gpio = cx_read ( ir - > gpio_addr ) ;
2006-06-08 17:36:17 -03:00
if ( core - > board = = CX88_BOARD_NPGTECH_REALTV_TOP10FM ) {
2006-05-22 07:44:02 -03:00
/* This board apparently uses a combination of 2 GPIO
to represent the keys . Additionally , the second GPIO
can be used for parity .
Example :
for key " 5 "
gpio = 0x758 , auxgpio = 0xe5 or 0xf5
for key " Power "
gpio = 0x758 , auxgpio = 0xed or 0xfd
*/
auxgpio = cx_read ( MO_GP1_IO ) ;
/* Take out the parity part */
2006-07-17 16:34:27 -03:00
gpio = ( gpio & 0x7fd ) + ( auxgpio & 0xef ) ;
2006-05-22 07:44:02 -03:00
} else
auxgpio = gpio ;
2005-04-16 15:20:36 -07:00
if ( ir - > polling ) {
2006-05-22 07:44:02 -03:00
if ( ir - > last_gpio = = auxgpio )
2005-04-16 15:20:36 -07:00
return ;
2006-05-22 07:44:02 -03:00
ir - > last_gpio = auxgpio ;
2005-04-16 15:20:36 -07:00
}
/* extract data */
data = ir_extract_bits ( gpio , ir - > mask_keycode ) ;
ir_dprintk ( " irq gpio=0x%x code=%d | %s%s%s \n " ,
2005-07-12 13:58:44 -07:00
gpio , data ,
ir - > polling ? " poll " : " irq " ,
( gpio & ir - > mask_keydown ) ? " down " : " " ,
( gpio & ir - > mask_keyup ) ? " up " : " " ) ;
2005-04-16 15:20:36 -07:00
if ( ir - > mask_keydown ) {
/* bit set on keydown */
if ( gpio & ir - > mask_keydown ) {
2005-09-15 02:01:53 -05:00
ir_input_keydown ( ir - > input , & ir - > ir , data , data ) ;
2005-04-16 15:20:36 -07:00
} else {
2005-09-15 02:01:53 -05:00
ir_input_nokey ( ir - > input , & ir - > ir ) ;
2005-04-16 15:20:36 -07:00
}
} else if ( ir - > mask_keyup ) {
/* bit cleared on keydown */
if ( 0 = = ( gpio & ir - > mask_keyup ) ) {
2005-09-15 02:01:53 -05:00
ir_input_keydown ( ir - > input , & ir - > ir , data , data ) ;
2005-04-16 15:20:36 -07:00
} else {
2005-09-15 02:01:53 -05:00
ir_input_nokey ( ir - > input , & ir - > ir ) ;
2005-04-16 15:20:36 -07:00
}
} else {
/* can't distinguish keydown/up :-/ */
2005-09-15 02:01:53 -05:00
ir_input_keydown ( ir - > input , & ir - > ir , data , data ) ;
ir_input_nokey ( ir - > input , & ir - > ir ) ;
2005-04-16 15:20:36 -07:00
}
}
static void ir_timer ( unsigned long data )
{
2005-07-12 13:58:44 -07:00
struct cx88_IR * ir = ( struct cx88_IR * ) data ;
2005-04-16 15:20:36 -07:00
schedule_work ( & ir - > work ) ;
}
static void cx88_ir_work ( void * data )
{
struct cx88_IR * ir = data ;
unsigned long timeout ;
cx88_ir_handle_key ( ir ) ;
timeout = jiffies + ( ir - > polling * HZ / 1000 ) ;
mod_timer ( & ir - > timer , timeout ) ;
}
/* ---------------------------------------------------------------------- */
int cx88_ir_init ( struct cx88_core * core , struct pci_dev * pci )
{
struct cx88_IR * ir ;
2005-09-15 02:01:53 -05:00
struct input_dev * input_dev ;
2005-04-16 15:20:36 -07:00
IR_KEYTAB_TYPE * ir_codes = NULL ;
int ir_type = IR_TYPE_OTHER ;
2005-09-15 02:01:53 -05:00
ir = kzalloc ( sizeof ( * ir ) , GFP_KERNEL ) ;
input_dev = input_allocate_device ( ) ;
if ( ! ir | | ! input_dev ) {
kfree ( ir ) ;
input_free_device ( input_dev ) ;
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
2005-09-15 02:01:53 -05:00
}
ir - > input = input_dev ;
2005-04-16 15:20:36 -07:00
/* detect & configure */
switch ( core - > board ) {
case CX88_BOARD_DNTV_LIVE_DVB_T :
2005-06-23 22:05:03 -07:00
case CX88_BOARD_KWORLD_DVB_T :
2006-02-07 06:45:33 -02:00
case CX88_BOARD_KWORLD_DVB_T_CX22702 :
2005-07-12 13:58:44 -07:00
ir_codes = ir_codes_dntv_live_dvb_t ;
ir - > gpio_addr = MO_GP1_IO ;
2005-04-16 15:20:36 -07:00
ir - > mask_keycode = 0x1f ;
2005-07-12 13:58:44 -07:00
ir - > mask_keyup = 0x60 ;
ir - > polling = 50 ; /* ms */
2005-04-16 15:20:36 -07:00
break ;
2005-09-09 13:03:41 -07:00
case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1 :
ir_codes = ir_codes_cinergy_1400 ;
ir_type = IR_TYPE_PD ;
2006-01-09 15:25:35 -02:00
ir - > sampling = 0xeb04 ; /* address */
2005-09-09 13:03:41 -07:00
break ;
2005-04-16 15:20:36 -07:00
case CX88_BOARD_HAUPPAUGE :
case CX88_BOARD_HAUPPAUGE_DVB_T1 :
2006-01-09 15:25:02 -02:00
case CX88_BOARD_HAUPPAUGE_NOVASE2_S1 :
case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1 :
2006-01-09 15:25:12 -02:00
case CX88_BOARD_HAUPPAUGE_HVR1100 :
2005-07-12 13:58:44 -07:00
ir_codes = ir_codes_hauppauge_new ;
ir_type = IR_TYPE_RC5 ;
ir - > sampling = 1 ;
2005-04-16 15:20:36 -07:00
break ;
2006-05-29 13:56:24 -03:00
case CX88_BOARD_WINFAST_DTV2000H :
2005-04-16 15:20:36 -07:00
case CX88_BOARD_WINFAST2000XP_EXPERT :
2005-07-12 13:58:44 -07:00
ir_codes = ir_codes_winfast ;
ir - > gpio_addr = MO_GP0_IO ;
2005-04-16 15:20:36 -07:00
ir - > mask_keycode = 0x8f8 ;
2005-07-12 13:58:44 -07:00
ir - > mask_keyup = 0x100 ;
2006-05-29 13:56:24 -03:00
ir - > polling = 50 ; /* ms */
2005-04-16 15:20:36 -07:00
break ;
case CX88_BOARD_IODATA_GVBCTV7E :
2005-07-12 13:58:44 -07:00
ir_codes = ir_codes_iodata_bctv7e ;
ir - > gpio_addr = MO_GP0_IO ;
2005-04-16 15:20:36 -07:00
ir - > mask_keycode = 0xfd ;
ir - > mask_keydown = 0x02 ;
2005-07-12 13:58:44 -07:00
ir - > polling = 5 ; /* ms */
2005-04-16 15:20:36 -07:00
break ;
2006-03-11 23:54:58 -03:00
case CX88_BOARD_PROLINK_PLAYTVPVR :
2005-06-23 22:04:53 -07:00
case CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO :
2005-07-12 13:58:44 -07:00
ir_codes = ir_codes_pixelview ;
ir - > gpio_addr = MO_GP1_IO ;
2005-06-23 22:04:53 -07:00
ir - > mask_keycode = 0x1f ;
2005-07-12 13:58:44 -07:00
ir - > mask_keyup = 0x80 ;
ir - > polling = 1 ; /* ms */
2005-06-23 22:04:53 -07:00
break ;
2006-01-23 09:44:10 -02:00
case CX88_BOARD_KWORLD_LTV883 :
ir_codes = ir_codes_pixelview ;
ir - > gpio_addr = MO_GP1_IO ;
ir - > mask_keycode = 0x1f ;
ir - > mask_keyup = 0x60 ;
ir - > polling = 1 ; /* ms */
break ;
2005-07-07 17:58:36 -07:00
case CX88_BOARD_ADSTECH_DVB_T_PCI :
2005-07-12 13:58:44 -07:00
ir_codes = ir_codes_adstech_dvb_t_pci ;
ir - > gpio_addr = MO_GP1_IO ;
2005-07-07 17:58:36 -07:00
ir - > mask_keycode = 0xbf ;
2005-07-12 13:58:44 -07:00
ir - > mask_keyup = 0x40 ;
ir - > polling = 50 ; /* ms */
break ;
case CX88_BOARD_MSI_TVANYWHERE_MASTER :
ir_codes = ir_codes_msi_tvanywhere ;
ir - > gpio_addr = MO_GP1_IO ;
ir - > mask_keycode = 0x1f ;
ir - > mask_keyup = 0x40 ;
ir - > polling = 1 ; /* ms */
2005-07-07 17:58:36 -07:00
break ;
2006-01-09 15:25:19 -02:00
case CX88_BOARD_AVERTV_303 :
2006-01-09 15:32:46 -02:00
case CX88_BOARD_AVERTV_STUDIO_303 :
2006-01-09 15:25:19 -02:00
ir_codes = ir_codes_avertv_303 ;
ir - > gpio_addr = MO_GP2_IO ;
ir - > mask_keycode = 0xfb ;
ir - > mask_keydown = 0x02 ;
ir - > polling = 50 ; /* ms */
break ;
2006-01-09 15:25:35 -02:00
case CX88_BOARD_DNTV_LIVE_DVB_T_PRO :
ir_codes = ir_codes_dntv_live_dvbt_pro ;
ir_type = IR_TYPE_PD ;
ir - > sampling = 0xff00 ; /* address */
break ;
2006-06-08 17:36:17 -03:00
case CX88_BOARD_NPGTECH_REALTV_TOP10FM :
2006-05-22 07:44:02 -03:00
ir_codes = ir_codes_npgtech ;
ir - > gpio_addr = MO_GP0_IO ;
ir - > mask_keycode = 0xfa ;
ir - > polling = 50 ; /* ms */
break ;
2005-04-16 15:20:36 -07:00
}
2005-06-23 22:05:03 -07:00
2005-04-16 15:20:36 -07:00
if ( NULL = = ir_codes ) {
kfree ( ir ) ;
2005-09-15 02:01:53 -05:00
input_free_device ( input_dev ) ;
2005-04-16 15:20:36 -07:00
return - ENODEV ;
}
/* init input device */
snprintf ( ir - > name , sizeof ( ir - > name ) , " cx88 IR (%s) " ,
cx88_boards [ core - > board ] . name ) ;
2005-07-12 13:58:44 -07:00
snprintf ( ir - > phys , sizeof ( ir - > phys ) , " pci-%s/ir0 " , pci_name ( pci ) ) ;
2005-04-16 15:20:36 -07:00
2005-09-15 02:01:53 -05:00
ir_input_init ( input_dev , & ir - > ir , ir_type , ir_codes ) ;
input_dev - > name = ir - > name ;
input_dev - > phys = ir - > phys ;
input_dev - > id . bustype = BUS_PCI ;
input_dev - > id . version = 1 ;
2005-04-16 15:20:36 -07:00
if ( pci - > subsystem_vendor ) {
2005-09-15 02:01:53 -05:00
input_dev - > id . vendor = pci - > subsystem_vendor ;
input_dev - > id . product = pci - > subsystem_device ;
2005-04-16 15:20:36 -07:00
} else {
2005-09-15 02:01:53 -05:00
input_dev - > id . vendor = pci - > vendor ;
input_dev - > id . product = pci - > device ;
2005-04-16 15:20:36 -07:00
}
2005-09-15 02:01:53 -05:00
input_dev - > cdev . dev = & pci - > dev ;
2005-04-16 15:20:36 -07:00
/* record handles to ourself */
ir - > core = core ;
core - > ir = ir ;
if ( ir - > polling ) {
INIT_WORK ( & ir - > work , cx88_ir_work , ir ) ;
init_timer ( & ir - > timer ) ;
ir - > timer . function = ir_timer ;
2005-07-12 13:58:44 -07:00
ir - > timer . data = ( unsigned long ) ir ;
2005-04-16 15:20:36 -07:00
schedule_work ( & ir - > work ) ;
}
if ( ir - > sampling ) {
2005-07-12 13:58:44 -07:00
core - > pci_irqmask | = ( 1 < < 18 ) ; /* IR_SMP_INT */
cx_write ( MO_DDS_IO , 0xa80a80 ) ; /* 4 kHz sample rate */
cx_write ( MO_DDSCFG_IO , 0x5 ) ; /* enable */
2005-04-16 15:20:36 -07:00
}
/* all done */
2005-09-15 02:01:53 -05:00
input_register_device ( ir - > input ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
int cx88_ir_fini ( struct cx88_core * core )
{
struct cx88_IR * ir = core - > ir ;
/* skip detach on non attached boards */
if ( NULL = = ir )
return 0 ;
2006-01-09 15:25:35 -02:00
if ( ir - > sampling ) {
cx_write ( MO_DDSCFG_IO , 0x0 ) ;
core - > pci_irqmask & = ~ ( 1 < < 18 ) ;
}
2005-04-16 15:20:36 -07:00
if ( ir - > polling ) {
del_timer ( & ir - > timer ) ;
flush_scheduled_work ( ) ;
}
2005-09-15 02:01:53 -05:00
input_unregister_device ( ir - > input ) ;
2005-04-16 15:20:36 -07:00
kfree ( ir ) ;
/* done */
core - > ir = NULL ;
return 0 ;
}
/* ---------------------------------------------------------------------- */
void cx88_ir_irq ( struct cx88_core * core )
{
struct cx88_IR * ir = core - > ir ;
2005-09-09 13:03:41 -07:00
u32 samples , ircode ;
2005-04-16 15:20:36 -07:00
int i ;
if ( NULL = = ir )
return ;
if ( ! ir - > sampling )
return ;
samples = cx_read ( MO_SAMPLE_IO ) ;
2005-07-12 13:58:44 -07:00
if ( 0 ! = samples & & 0xffffffff ! = samples ) {
2005-04-16 15:20:36 -07:00
/* record sample data */
if ( ir - > scount < ARRAY_SIZE ( ir - > samples ) )
ir - > samples [ ir - > scount + + ] = samples ;
return ;
}
if ( ! ir - > scount ) {
/* nothing to sample */
2005-07-12 13:58:44 -07:00
if ( ir - > ir . keypressed & & time_after ( jiffies , ir - > release ) )
2005-09-15 02:01:53 -05:00
ir_input_nokey ( ir - > input , & ir - > ir ) ;
2005-04-16 15:20:36 -07:00
return ;
}
/* have a complete sample */
if ( ir - > scount < ARRAY_SIZE ( ir - > samples ) )
ir - > samples [ ir - > scount + + ] = samples ;
for ( i = 0 ; i < ir - > scount ; i + + )
ir - > samples [ i ] = ~ ir - > samples [ i ] ;
if ( ir_debug )
2005-07-12 13:58:44 -07:00
ir_dump_samples ( ir - > samples , ir - > scount ) ;
2005-04-16 15:20:36 -07:00
/* decode it */
switch ( core - > board ) {
2005-09-09 13:03:41 -07:00
case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1 :
2006-01-09 15:25:35 -02:00
case CX88_BOARD_DNTV_LIVE_DVB_T_PRO :
2005-09-09 13:03:41 -07:00
ircode = ir_decode_pulsedistance ( ir - > samples , ir - > scount , 1 , 4 ) ;
if ( ircode = = 0xffffffff ) { /* decoding error */
ir_dprintk ( " pulse distance decoding error \n " ) ;
break ;
}
ir_dprintk ( " pulse distance decoded: %x \n " , ircode ) ;
if ( ircode = = 0 ) { /* key still pressed */
ir_dprintk ( " pulse distance decoded repeat code \n " ) ;
ir - > release = jiffies + msecs_to_jiffies ( 120 ) ;
break ;
}
2006-01-09 15:25:35 -02:00
if ( ( ircode & 0xffff ) ! = ( ir - > sampling & 0xffff ) ) { /* wrong address */
2005-09-09 13:03:41 -07:00
ir_dprintk ( " pulse distance decoded wrong address \n " ) ;
2005-11-08 21:37:43 -08:00
break ;
2005-09-09 13:03:41 -07:00
}
if ( ( ( ~ ircode > > 24 ) & 0xff ) ! = ( ( ircode > > 16 ) & 0xff ) ) { /* wrong checksum */
ir_dprintk ( " pulse distance decoded wrong check sum \n " ) ;
break ;
}
ir_dprintk ( " Key Code: %x \n " , ( ircode > > 16 ) & 0x7f ) ;
2005-09-15 02:01:53 -05:00
ir_input_keydown ( ir - > input , & ir - > ir , ( ircode > > 16 ) & 0x7f , ( ircode > > 16 ) & 0xff ) ;
2005-09-09 13:03:41 -07:00
ir - > release = jiffies + msecs_to_jiffies ( 120 ) ;
break ;
2005-04-16 15:20:36 -07:00
case CX88_BOARD_HAUPPAUGE :
case CX88_BOARD_HAUPPAUGE_DVB_T1 :
2006-01-09 15:25:02 -02:00
case CX88_BOARD_HAUPPAUGE_NOVASE2_S1 :
case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1 :
2006-01-11 19:40:01 -02:00
case CX88_BOARD_HAUPPAUGE_HVR1100 :
2005-09-09 13:03:41 -07:00
ircode = ir_decode_biphase ( ir - > samples , ir - > scount , 5 , 7 ) ;
ir_dprintk ( " biphase decoded: %x \n " , ircode ) ;
if ( ( ircode & 0xfffff000 ) ! = 0x3000 )
2005-04-16 15:20:36 -07:00
break ;
2005-09-15 02:01:53 -05:00
ir_input_keydown ( ir - > input , & ir - > ir , ircode & 0x3f , ircode ) ;
2005-04-16 15:20:36 -07:00
ir - > release = jiffies + msecs_to_jiffies ( 120 ) ;
break ;
}
ir - > scount = 0 ;
return ;
}
/* ---------------------------------------------------------------------- */
MODULE_AUTHOR ( " Gerd Knorr, Pavel Machek, Chris Pascoe " ) ;
MODULE_DESCRIPTION ( " input driver for cx88 GPIO-based IR remote controls " ) ;
MODULE_LICENSE ( " GPL " ) ;
/*
* Local variables :
* c - basic - offset : 8
* End :
*/