162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Device driver for GPIO attached remote control interfaces 562306a36Sopenharmony_ci * on Conexant 2388x based TV/DVB cards. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2003 Pavel Machek 862306a36Sopenharmony_ci * Copyright (c) 2004 Gerd Knorr 962306a36Sopenharmony_ci * Copyright (c) 2004, 2005 Chris Pascoe 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "cx88.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/hrtimer.h> 1662306a36Sopenharmony_ci#include <linux/pci.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <media/rc-core.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define MODULE_NAME "cx88xx" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* ---------------------------------------------------------------------- */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistruct cx88_IR { 2762306a36Sopenharmony_ci struct cx88_core *core; 2862306a36Sopenharmony_ci struct rc_dev *dev; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci int users; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci char name[32]; 3362306a36Sopenharmony_ci char phys[32]; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci /* sample from gpio pin 16 */ 3662306a36Sopenharmony_ci u32 sampling; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci /* poll external decoder */ 3962306a36Sopenharmony_ci int polling; 4062306a36Sopenharmony_ci struct hrtimer timer; 4162306a36Sopenharmony_ci u32 gpio_addr; 4262306a36Sopenharmony_ci u32 last_gpio; 4362306a36Sopenharmony_ci u32 mask_keycode; 4462306a36Sopenharmony_ci u32 mask_keydown; 4562306a36Sopenharmony_ci u32 mask_keyup; 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic unsigned int ir_samplerate = 4; 4962306a36Sopenharmony_cimodule_param(ir_samplerate, uint, 0444); 5062306a36Sopenharmony_ciMODULE_PARM_DESC(ir_samplerate, "IR samplerate in kHz, 1 - 20, default 4"); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic int ir_debug; 5362306a36Sopenharmony_cimodule_param(ir_debug, int, 0644); /* debug level [IR] */ 5462306a36Sopenharmony_ciMODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define ir_dprintk(fmt, arg...) do { \ 5762306a36Sopenharmony_ci if (ir_debug) \ 5862306a36Sopenharmony_ci printk(KERN_DEBUG "%s IR: " fmt, ir->core->name, ##arg);\ 5962306a36Sopenharmony_ci} while (0) 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define dprintk(fmt, arg...) do { \ 6262306a36Sopenharmony_ci if (ir_debug) \ 6362306a36Sopenharmony_ci printk(KERN_DEBUG "cx88 IR: " fmt, ##arg); \ 6462306a36Sopenharmony_ci} while (0) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* ---------------------------------------------------------------------- */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void cx88_ir_handle_key(struct cx88_IR *ir) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct cx88_core *core = ir->core; 7162306a36Sopenharmony_ci u32 gpio, data, auxgpio; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* read gpio value */ 7462306a36Sopenharmony_ci gpio = cx_read(ir->gpio_addr); 7562306a36Sopenharmony_ci switch (core->boardnr) { 7662306a36Sopenharmony_ci case CX88_BOARD_NPGTECH_REALTV_TOP10FM: 7762306a36Sopenharmony_ci /* 7862306a36Sopenharmony_ci * This board apparently uses a combination of 2 GPIO 7962306a36Sopenharmony_ci * to represent the keys. Additionally, the second GPIO 8062306a36Sopenharmony_ci * can be used for parity. 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * Example: 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * for key "5" 8562306a36Sopenharmony_ci * gpio = 0x758, auxgpio = 0xe5 or 0xf5 8662306a36Sopenharmony_ci * for key "Power" 8762306a36Sopenharmony_ci * gpio = 0x758, auxgpio = 0xed or 0xfd 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci auxgpio = cx_read(MO_GP1_IO); 9162306a36Sopenharmony_ci /* Take out the parity part */ 9262306a36Sopenharmony_ci gpio = (gpio & 0x7fd) + (auxgpio & 0xef); 9362306a36Sopenharmony_ci break; 9462306a36Sopenharmony_ci case CX88_BOARD_WINFAST_DTV1000: 9562306a36Sopenharmony_ci case CX88_BOARD_WINFAST_DTV1800H: 9662306a36Sopenharmony_ci case CX88_BOARD_WINFAST_DTV1800H_XC4000: 9762306a36Sopenharmony_ci case CX88_BOARD_WINFAST_DTV2000H_PLUS: 9862306a36Sopenharmony_ci case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: 9962306a36Sopenharmony_ci case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36: 10062306a36Sopenharmony_ci case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43: 10162306a36Sopenharmony_ci gpio = (gpio & 0x6ff) | ((cx_read(MO_GP1_IO) << 8) & 0x900); 10262306a36Sopenharmony_ci auxgpio = gpio; 10362306a36Sopenharmony_ci break; 10462306a36Sopenharmony_ci default: 10562306a36Sopenharmony_ci auxgpio = gpio; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci if (ir->polling) { 10862306a36Sopenharmony_ci if (ir->last_gpio == auxgpio) 10962306a36Sopenharmony_ci return; 11062306a36Sopenharmony_ci ir->last_gpio = auxgpio; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* extract data */ 11462306a36Sopenharmony_ci data = ir_extract_bits(gpio, ir->mask_keycode); 11562306a36Sopenharmony_ci ir_dprintk("irq gpio=0x%x code=%d | %s%s%s\n", 11662306a36Sopenharmony_ci gpio, data, 11762306a36Sopenharmony_ci ir->polling ? "poll" : "irq", 11862306a36Sopenharmony_ci (gpio & ir->mask_keydown) ? " down" : "", 11962306a36Sopenharmony_ci (gpio & ir->mask_keyup) ? " up" : ""); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (ir->core->boardnr == CX88_BOARD_NORWOOD_MICRO) { 12262306a36Sopenharmony_ci u32 gpio_key = cx_read(MO_GP0_IO); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci data = (data << 4) | ((gpio_key & 0xf0) >> 4); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci rc_keydown(ir->dev, RC_PROTO_UNKNOWN, data, 0); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci } else if (ir->core->boardnr == CX88_BOARD_PROLINK_PLAYTVPVR || 12962306a36Sopenharmony_ci ir->core->boardnr == CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO) { 13062306a36Sopenharmony_ci /* bit cleared on keydown, NEC scancode, 0xAAAACC, A = 0x866b */ 13162306a36Sopenharmony_ci u16 addr; 13262306a36Sopenharmony_ci u8 cmd; 13362306a36Sopenharmony_ci u32 scancode; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci addr = (data >> 8) & 0xffff; 13662306a36Sopenharmony_ci cmd = (data >> 0) & 0x00ff; 13762306a36Sopenharmony_ci scancode = RC_SCANCODE_NECX(addr, cmd); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (0 == (gpio & ir->mask_keyup)) 14062306a36Sopenharmony_ci rc_keydown_notimeout(ir->dev, RC_PROTO_NECX, scancode, 14162306a36Sopenharmony_ci 0); 14262306a36Sopenharmony_ci else 14362306a36Sopenharmony_ci rc_keyup(ir->dev); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci } else if (ir->mask_keydown) { 14662306a36Sopenharmony_ci /* bit set on keydown */ 14762306a36Sopenharmony_ci if (gpio & ir->mask_keydown) 14862306a36Sopenharmony_ci rc_keydown_notimeout(ir->dev, RC_PROTO_UNKNOWN, data, 14962306a36Sopenharmony_ci 0); 15062306a36Sopenharmony_ci else 15162306a36Sopenharmony_ci rc_keyup(ir->dev); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci } else if (ir->mask_keyup) { 15462306a36Sopenharmony_ci /* bit cleared on keydown */ 15562306a36Sopenharmony_ci if (0 == (gpio & ir->mask_keyup)) 15662306a36Sopenharmony_ci rc_keydown_notimeout(ir->dev, RC_PROTO_UNKNOWN, data, 15762306a36Sopenharmony_ci 0); 15862306a36Sopenharmony_ci else 15962306a36Sopenharmony_ci rc_keyup(ir->dev); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci } else { 16262306a36Sopenharmony_ci /* can't distinguish keydown/up :-/ */ 16362306a36Sopenharmony_ci rc_keydown_notimeout(ir->dev, RC_PROTO_UNKNOWN, data, 0); 16462306a36Sopenharmony_ci rc_keyup(ir->dev); 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic enum hrtimer_restart cx88_ir_work(struct hrtimer *timer) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci u64 missed; 17162306a36Sopenharmony_ci struct cx88_IR *ir = container_of(timer, struct cx88_IR, timer); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci cx88_ir_handle_key(ir); 17462306a36Sopenharmony_ci missed = hrtimer_forward_now(&ir->timer, 17562306a36Sopenharmony_ci ktime_set(0, ir->polling * 1000000)); 17662306a36Sopenharmony_ci if (missed > 1) 17762306a36Sopenharmony_ci ir_dprintk("Missed ticks %llu\n", missed - 1); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return HRTIMER_RESTART; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int __cx88_ir_start(void *priv) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct cx88_core *core = priv; 18562306a36Sopenharmony_ci struct cx88_IR *ir; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (!core || !core->ir) 18862306a36Sopenharmony_ci return -EINVAL; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci ir = core->ir; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (ir->polling) { 19362306a36Sopenharmony_ci hrtimer_init(&ir->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 19462306a36Sopenharmony_ci ir->timer.function = cx88_ir_work; 19562306a36Sopenharmony_ci hrtimer_start(&ir->timer, 19662306a36Sopenharmony_ci ktime_set(0, ir->polling * 1000000), 19762306a36Sopenharmony_ci HRTIMER_MODE_REL); 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci if (ir->sampling) { 20062306a36Sopenharmony_ci core->pci_irqmask |= PCI_INT_IR_SMPINT; 20162306a36Sopenharmony_ci cx_write(MO_DDS_IO, 0x33F286 * ir_samplerate); /* samplerate */ 20262306a36Sopenharmony_ci cx_write(MO_DDSCFG_IO, 0x5); /* enable */ 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic void __cx88_ir_stop(void *priv) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct cx88_core *core = priv; 21062306a36Sopenharmony_ci struct cx88_IR *ir; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (!core || !core->ir) 21362306a36Sopenharmony_ci return; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci ir = core->ir; 21662306a36Sopenharmony_ci if (ir->sampling) { 21762306a36Sopenharmony_ci cx_write(MO_DDSCFG_IO, 0x0); 21862306a36Sopenharmony_ci core->pci_irqmask &= ~PCI_INT_IR_SMPINT; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (ir->polling) 22262306a36Sopenharmony_ci hrtimer_cancel(&ir->timer); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ciint cx88_ir_start(struct cx88_core *core) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci if (core->ir->users) 22862306a36Sopenharmony_ci return __cx88_ir_start(core); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return 0; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ciEXPORT_SYMBOL(cx88_ir_start); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_civoid cx88_ir_stop(struct cx88_core *core) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci if (core->ir->users) 23762306a36Sopenharmony_ci __cx88_ir_stop(core); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ciEXPORT_SYMBOL(cx88_ir_stop); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int cx88_ir_open(struct rc_dev *rc) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct cx88_core *core = rc->priv; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci core->ir->users++; 24662306a36Sopenharmony_ci return __cx88_ir_start(core); 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic void cx88_ir_close(struct rc_dev *rc) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct cx88_core *core = rc->priv; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci core->ir->users--; 25462306a36Sopenharmony_ci if (!core->ir->users) 25562306a36Sopenharmony_ci __cx88_ir_stop(core); 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci/* ---------------------------------------------------------------------- */ 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ciint cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct cx88_IR *ir; 26362306a36Sopenharmony_ci struct rc_dev *dev; 26462306a36Sopenharmony_ci char *ir_codes = NULL; 26562306a36Sopenharmony_ci u64 rc_proto = RC_PROTO_BIT_OTHER; 26662306a36Sopenharmony_ci int err = -ENOMEM; 26762306a36Sopenharmony_ci u32 hardware_mask = 0; /* For devices with a hardware mask, when 26862306a36Sopenharmony_ci * used with a full-code IR table 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci ir = kzalloc(sizeof(*ir), GFP_KERNEL); 27262306a36Sopenharmony_ci dev = rc_allocate_device(RC_DRIVER_IR_RAW); 27362306a36Sopenharmony_ci if (!ir || !dev) 27462306a36Sopenharmony_ci goto err_out_free; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci ir->dev = dev; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* detect & configure */ 27962306a36Sopenharmony_ci switch (core->boardnr) { 28062306a36Sopenharmony_ci case CX88_BOARD_DNTV_LIVE_DVB_T: 28162306a36Sopenharmony_ci case CX88_BOARD_KWORLD_DVB_T: 28262306a36Sopenharmony_ci case CX88_BOARD_KWORLD_DVB_T_CX22702: 28362306a36Sopenharmony_ci ir_codes = RC_MAP_DNTV_LIVE_DVB_T; 28462306a36Sopenharmony_ci ir->gpio_addr = MO_GP1_IO; 28562306a36Sopenharmony_ci ir->mask_keycode = 0x1f; 28662306a36Sopenharmony_ci ir->mask_keyup = 0x60; 28762306a36Sopenharmony_ci ir->polling = 50; /* ms */ 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1: 29062306a36Sopenharmony_ci ir_codes = RC_MAP_CINERGY_1400; 29162306a36Sopenharmony_ci ir->sampling = 0xeb04; /* address */ 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci case CX88_BOARD_HAUPPAUGE: 29462306a36Sopenharmony_ci case CX88_BOARD_HAUPPAUGE_DVB_T1: 29562306a36Sopenharmony_ci case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: 29662306a36Sopenharmony_ci case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: 29762306a36Sopenharmony_ci case CX88_BOARD_HAUPPAUGE_HVR1100: 29862306a36Sopenharmony_ci case CX88_BOARD_HAUPPAUGE_HVR3000: 29962306a36Sopenharmony_ci case CX88_BOARD_HAUPPAUGE_HVR4000: 30062306a36Sopenharmony_ci case CX88_BOARD_HAUPPAUGE_HVR4000LITE: 30162306a36Sopenharmony_ci case CX88_BOARD_PCHDTV_HD3000: 30262306a36Sopenharmony_ci case CX88_BOARD_PCHDTV_HD5500: 30362306a36Sopenharmony_ci case CX88_BOARD_HAUPPAUGE_IRONLY: 30462306a36Sopenharmony_ci ir_codes = RC_MAP_HAUPPAUGE; 30562306a36Sopenharmony_ci ir->sampling = 1; 30662306a36Sopenharmony_ci break; 30762306a36Sopenharmony_ci case CX88_BOARD_WINFAST_DTV2000H: 30862306a36Sopenharmony_ci case CX88_BOARD_WINFAST_DTV2000H_J: 30962306a36Sopenharmony_ci case CX88_BOARD_WINFAST_DTV1800H: 31062306a36Sopenharmony_ci case CX88_BOARD_WINFAST_DTV1800H_XC4000: 31162306a36Sopenharmony_ci case CX88_BOARD_WINFAST_DTV2000H_PLUS: 31262306a36Sopenharmony_ci ir_codes = RC_MAP_WINFAST; 31362306a36Sopenharmony_ci ir->gpio_addr = MO_GP0_IO; 31462306a36Sopenharmony_ci ir->mask_keycode = 0x8f8; 31562306a36Sopenharmony_ci ir->mask_keyup = 0x100; 31662306a36Sopenharmony_ci ir->polling = 50; /* ms */ 31762306a36Sopenharmony_ci break; 31862306a36Sopenharmony_ci case CX88_BOARD_WINFAST2000XP_EXPERT: 31962306a36Sopenharmony_ci case CX88_BOARD_WINFAST_DTV1000: 32062306a36Sopenharmony_ci case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: 32162306a36Sopenharmony_ci case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36: 32262306a36Sopenharmony_ci case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43: 32362306a36Sopenharmony_ci ir_codes = RC_MAP_WINFAST; 32462306a36Sopenharmony_ci ir->gpio_addr = MO_GP0_IO; 32562306a36Sopenharmony_ci ir->mask_keycode = 0x8f8; 32662306a36Sopenharmony_ci ir->mask_keyup = 0x100; 32762306a36Sopenharmony_ci ir->polling = 1; /* ms */ 32862306a36Sopenharmony_ci break; 32962306a36Sopenharmony_ci case CX88_BOARD_IODATA_GVBCTV7E: 33062306a36Sopenharmony_ci ir_codes = RC_MAP_IODATA_BCTV7E; 33162306a36Sopenharmony_ci ir->gpio_addr = MO_GP0_IO; 33262306a36Sopenharmony_ci ir->mask_keycode = 0xfd; 33362306a36Sopenharmony_ci ir->mask_keydown = 0x02; 33462306a36Sopenharmony_ci ir->polling = 5; /* ms */ 33562306a36Sopenharmony_ci break; 33662306a36Sopenharmony_ci case CX88_BOARD_PROLINK_PLAYTVPVR: 33762306a36Sopenharmony_ci case CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO: 33862306a36Sopenharmony_ci /* 33962306a36Sopenharmony_ci * It seems that this hardware is paired with NEC extended 34062306a36Sopenharmony_ci * address 0x866b. So, unfortunately, its usage with other 34162306a36Sopenharmony_ci * IR's with different address won't work. Still, there are 34262306a36Sopenharmony_ci * other IR's from the same manufacturer that works, like the 34362306a36Sopenharmony_ci * 002-T mini RC, provided with newer PV hardware 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_ci ir_codes = RC_MAP_PIXELVIEW_MK12; 34662306a36Sopenharmony_ci rc_proto = RC_PROTO_BIT_NECX; 34762306a36Sopenharmony_ci ir->gpio_addr = MO_GP1_IO; 34862306a36Sopenharmony_ci ir->mask_keyup = 0x80; 34962306a36Sopenharmony_ci ir->polling = 10; /* ms */ 35062306a36Sopenharmony_ci hardware_mask = 0x3f; /* Hardware returns only 6 bits from command part */ 35162306a36Sopenharmony_ci break; 35262306a36Sopenharmony_ci case CX88_BOARD_PROLINK_PV_8000GT: 35362306a36Sopenharmony_ci case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: 35462306a36Sopenharmony_ci ir_codes = RC_MAP_PIXELVIEW_NEW; 35562306a36Sopenharmony_ci ir->gpio_addr = MO_GP1_IO; 35662306a36Sopenharmony_ci ir->mask_keycode = 0x3f; 35762306a36Sopenharmony_ci ir->mask_keyup = 0x80; 35862306a36Sopenharmony_ci ir->polling = 1; /* ms */ 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci case CX88_BOARD_KWORLD_LTV883: 36162306a36Sopenharmony_ci ir_codes = RC_MAP_PIXELVIEW; 36262306a36Sopenharmony_ci ir->gpio_addr = MO_GP1_IO; 36362306a36Sopenharmony_ci ir->mask_keycode = 0x1f; 36462306a36Sopenharmony_ci ir->mask_keyup = 0x60; 36562306a36Sopenharmony_ci ir->polling = 1; /* ms */ 36662306a36Sopenharmony_ci break; 36762306a36Sopenharmony_ci case CX88_BOARD_ADSTECH_DVB_T_PCI: 36862306a36Sopenharmony_ci ir_codes = RC_MAP_ADSTECH_DVB_T_PCI; 36962306a36Sopenharmony_ci ir->gpio_addr = MO_GP1_IO; 37062306a36Sopenharmony_ci ir->mask_keycode = 0xbf; 37162306a36Sopenharmony_ci ir->mask_keyup = 0x40; 37262306a36Sopenharmony_ci ir->polling = 50; /* ms */ 37362306a36Sopenharmony_ci break; 37462306a36Sopenharmony_ci case CX88_BOARD_MSI_TVANYWHERE_MASTER: 37562306a36Sopenharmony_ci ir_codes = RC_MAP_MSI_TVANYWHERE; 37662306a36Sopenharmony_ci ir->gpio_addr = MO_GP1_IO; 37762306a36Sopenharmony_ci ir->mask_keycode = 0x1f; 37862306a36Sopenharmony_ci ir->mask_keyup = 0x40; 37962306a36Sopenharmony_ci ir->polling = 1; /* ms */ 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci case CX88_BOARD_AVERTV_303: 38262306a36Sopenharmony_ci case CX88_BOARD_AVERTV_STUDIO_303: 38362306a36Sopenharmony_ci ir_codes = RC_MAP_AVERTV_303; 38462306a36Sopenharmony_ci ir->gpio_addr = MO_GP2_IO; 38562306a36Sopenharmony_ci ir->mask_keycode = 0xfb; 38662306a36Sopenharmony_ci ir->mask_keydown = 0x02; 38762306a36Sopenharmony_ci ir->polling = 50; /* ms */ 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci case CX88_BOARD_OMICOM_SS4_PCI: 39062306a36Sopenharmony_ci case CX88_BOARD_SATTRADE_ST4200: 39162306a36Sopenharmony_ci case CX88_BOARD_TBS_8920: 39262306a36Sopenharmony_ci case CX88_BOARD_TBS_8910: 39362306a36Sopenharmony_ci case CX88_BOARD_PROF_7300: 39462306a36Sopenharmony_ci case CX88_BOARD_PROF_7301: 39562306a36Sopenharmony_ci case CX88_BOARD_PROF_6200: 39662306a36Sopenharmony_ci ir_codes = RC_MAP_TBS_NEC; 39762306a36Sopenharmony_ci ir->sampling = 0xff00; /* address */ 39862306a36Sopenharmony_ci break; 39962306a36Sopenharmony_ci case CX88_BOARD_TEVII_S464: 40062306a36Sopenharmony_ci case CX88_BOARD_TEVII_S460: 40162306a36Sopenharmony_ci case CX88_BOARD_TEVII_S420: 40262306a36Sopenharmony_ci ir_codes = RC_MAP_TEVII_NEC; 40362306a36Sopenharmony_ci ir->sampling = 0xff00; /* address */ 40462306a36Sopenharmony_ci break; 40562306a36Sopenharmony_ci case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: 40662306a36Sopenharmony_ci ir_codes = RC_MAP_DNTV_LIVE_DVBT_PRO; 40762306a36Sopenharmony_ci ir->sampling = 0xff00; /* address */ 40862306a36Sopenharmony_ci break; 40962306a36Sopenharmony_ci case CX88_BOARD_NORWOOD_MICRO: 41062306a36Sopenharmony_ci ir_codes = RC_MAP_NORWOOD; 41162306a36Sopenharmony_ci ir->gpio_addr = MO_GP1_IO; 41262306a36Sopenharmony_ci ir->mask_keycode = 0x0e; 41362306a36Sopenharmony_ci ir->mask_keyup = 0x80; 41462306a36Sopenharmony_ci ir->polling = 50; /* ms */ 41562306a36Sopenharmony_ci break; 41662306a36Sopenharmony_ci case CX88_BOARD_NPGTECH_REALTV_TOP10FM: 41762306a36Sopenharmony_ci ir_codes = RC_MAP_NPGTECH; 41862306a36Sopenharmony_ci ir->gpio_addr = MO_GP0_IO; 41962306a36Sopenharmony_ci ir->mask_keycode = 0xfa; 42062306a36Sopenharmony_ci ir->polling = 50; /* ms */ 42162306a36Sopenharmony_ci break; 42262306a36Sopenharmony_ci case CX88_BOARD_PINNACLE_PCTV_HD_800i: 42362306a36Sopenharmony_ci ir_codes = RC_MAP_PINNACLE_PCTV_HD; 42462306a36Sopenharmony_ci ir->sampling = 1; 42562306a36Sopenharmony_ci break; 42662306a36Sopenharmony_ci case CX88_BOARD_POWERCOLOR_REAL_ANGEL: 42762306a36Sopenharmony_ci ir_codes = RC_MAP_POWERCOLOR_REAL_ANGEL; 42862306a36Sopenharmony_ci ir->gpio_addr = MO_GP2_IO; 42962306a36Sopenharmony_ci ir->mask_keycode = 0x7e; 43062306a36Sopenharmony_ci ir->polling = 100; /* ms */ 43162306a36Sopenharmony_ci break; 43262306a36Sopenharmony_ci case CX88_BOARD_TWINHAN_VP1027_DVBS: 43362306a36Sopenharmony_ci ir_codes = RC_MAP_TWINHAN_VP1027_DVBS; 43462306a36Sopenharmony_ci ir->sampling = 0xff00; /* address */ 43562306a36Sopenharmony_ci break; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (!ir_codes) { 43962306a36Sopenharmony_ci err = -ENODEV; 44062306a36Sopenharmony_ci goto err_out_free; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* 44462306a36Sopenharmony_ci * The usage of mask_keycode were very convenient, due to several 44562306a36Sopenharmony_ci * reasons. Among others, the scancode tables were using the scancode 44662306a36Sopenharmony_ci * as the index elements. So, the less bits it was used, the smaller 44762306a36Sopenharmony_ci * the table were stored. After the input changes, the better is to use 44862306a36Sopenharmony_ci * the full scancodes, since it allows replacing the IR remote by 44962306a36Sopenharmony_ci * another one. Unfortunately, there are still some hardware, like 45062306a36Sopenharmony_ci * Pixelview Ultra Pro, where only part of the scancode is sent via 45162306a36Sopenharmony_ci * GPIO. So, there's no way to get the full scancode. Due to that, 45262306a36Sopenharmony_ci * hardware_mask were introduced here: it represents those hardware 45362306a36Sopenharmony_ci * that has such limits. 45462306a36Sopenharmony_ci */ 45562306a36Sopenharmony_ci if (hardware_mask && !ir->mask_keycode) 45662306a36Sopenharmony_ci ir->mask_keycode = hardware_mask; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* init input device */ 45962306a36Sopenharmony_ci snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)", core->board.name); 46062306a36Sopenharmony_ci snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(pci)); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci dev->device_name = ir->name; 46362306a36Sopenharmony_ci dev->input_phys = ir->phys; 46462306a36Sopenharmony_ci dev->input_id.bustype = BUS_PCI; 46562306a36Sopenharmony_ci dev->input_id.version = 1; 46662306a36Sopenharmony_ci if (pci->subsystem_vendor) { 46762306a36Sopenharmony_ci dev->input_id.vendor = pci->subsystem_vendor; 46862306a36Sopenharmony_ci dev->input_id.product = pci->subsystem_device; 46962306a36Sopenharmony_ci } else { 47062306a36Sopenharmony_ci dev->input_id.vendor = pci->vendor; 47162306a36Sopenharmony_ci dev->input_id.product = pci->device; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci dev->dev.parent = &pci->dev; 47462306a36Sopenharmony_ci dev->map_name = ir_codes; 47562306a36Sopenharmony_ci dev->driver_name = MODULE_NAME; 47662306a36Sopenharmony_ci dev->priv = core; 47762306a36Sopenharmony_ci dev->open = cx88_ir_open; 47862306a36Sopenharmony_ci dev->close = cx88_ir_close; 47962306a36Sopenharmony_ci dev->scancode_mask = hardware_mask; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (ir->sampling) { 48262306a36Sopenharmony_ci dev->timeout = MS_TO_US(10); /* 10 ms */ 48362306a36Sopenharmony_ci } else { 48462306a36Sopenharmony_ci dev->driver_type = RC_DRIVER_SCANCODE; 48562306a36Sopenharmony_ci dev->allowed_protocols = rc_proto; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci ir->core = core; 48962306a36Sopenharmony_ci core->ir = ir; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci /* all done */ 49262306a36Sopenharmony_ci err = rc_register_device(dev); 49362306a36Sopenharmony_ci if (err) 49462306a36Sopenharmony_ci goto err_out_free; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci return 0; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cierr_out_free: 49962306a36Sopenharmony_ci rc_free_device(dev); 50062306a36Sopenharmony_ci core->ir = NULL; 50162306a36Sopenharmony_ci kfree(ir); 50262306a36Sopenharmony_ci return err; 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ciint cx88_ir_fini(struct cx88_core *core) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci struct cx88_IR *ir = core->ir; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* skip detach on non attached boards */ 51062306a36Sopenharmony_ci if (!ir) 51162306a36Sopenharmony_ci return 0; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci cx88_ir_stop(core); 51462306a36Sopenharmony_ci rc_unregister_device(ir->dev); 51562306a36Sopenharmony_ci kfree(ir); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci /* done */ 51862306a36Sopenharmony_ci core->ir = NULL; 51962306a36Sopenharmony_ci return 0; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci/* ---------------------------------------------------------------------- */ 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_civoid cx88_ir_irq(struct cx88_core *core) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci struct cx88_IR *ir = core->ir; 52762306a36Sopenharmony_ci u32 samples; 52862306a36Sopenharmony_ci unsigned int todo, bits; 52962306a36Sopenharmony_ci struct ir_raw_event ev = {}; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (!ir || !ir->sampling) 53262306a36Sopenharmony_ci return; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* 53562306a36Sopenharmony_ci * Samples are stored in a 32 bit register, oldest sample in 53662306a36Sopenharmony_ci * the msb. A set bit represents space and an unset bit 53762306a36Sopenharmony_ci * represents a pulse. 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_ci samples = cx_read(MO_SAMPLE_IO); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (samples == 0xff && ir->dev->idle) 54262306a36Sopenharmony_ci return; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci for (todo = 32; todo > 0; todo -= bits) { 54562306a36Sopenharmony_ci ev.pulse = samples & 0x80000000 ? false : true; 54662306a36Sopenharmony_ci bits = min(todo, 32U - fls(ev.pulse ? samples : ~samples)); 54762306a36Sopenharmony_ci ev.duration = (bits * (USEC_PER_SEC / 1000)) / ir_samplerate; 54862306a36Sopenharmony_ci ir_raw_event_store_with_filter(ir->dev, &ev); 54962306a36Sopenharmony_ci samples <<= bits; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci ir_raw_event_handle(ir->dev); 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic int get_key_pvr2000(struct IR_i2c *ir, enum rc_proto *protocol, 55562306a36Sopenharmony_ci u32 *scancode, u8 *toggle) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci int flags, code; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* poll IR chip */ 56062306a36Sopenharmony_ci flags = i2c_smbus_read_byte_data(ir->c, 0x10); 56162306a36Sopenharmony_ci if (flags < 0) { 56262306a36Sopenharmony_ci dprintk("read error\n"); 56362306a36Sopenharmony_ci return 0; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci /* key pressed ? */ 56662306a36Sopenharmony_ci if (0 == (flags & 0x80)) 56762306a36Sopenharmony_ci return 0; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* read actual key code */ 57062306a36Sopenharmony_ci code = i2c_smbus_read_byte_data(ir->c, 0x00); 57162306a36Sopenharmony_ci if (code < 0) { 57262306a36Sopenharmony_ci dprintk("read error\n"); 57362306a36Sopenharmony_ci return 0; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci dprintk("IR Key/Flags: (0x%02x/0x%02x)\n", 57762306a36Sopenharmony_ci code & 0xff, flags & 0xff); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci *protocol = RC_PROTO_UNKNOWN; 58062306a36Sopenharmony_ci *scancode = code & 0xff; 58162306a36Sopenharmony_ci *toggle = 0; 58262306a36Sopenharmony_ci return 1; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_civoid cx88_i2c_init_ir(struct cx88_core *core) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci struct i2c_board_info info; 58862306a36Sopenharmony_ci static const unsigned short default_addr_list[] = { 58962306a36Sopenharmony_ci 0x18, 0x33, 0x6b, 0x71, 59062306a36Sopenharmony_ci I2C_CLIENT_END 59162306a36Sopenharmony_ci }; 59262306a36Sopenharmony_ci static const unsigned short pvr2000_addr_list[] = { 59362306a36Sopenharmony_ci 0x18, 0x1a, 59462306a36Sopenharmony_ci I2C_CLIENT_END 59562306a36Sopenharmony_ci }; 59662306a36Sopenharmony_ci const unsigned short *addr_list = default_addr_list; 59762306a36Sopenharmony_ci const unsigned short *addrp; 59862306a36Sopenharmony_ci /* Instantiate the IR receiver device, if present */ 59962306a36Sopenharmony_ci if (core->i2c_rc != 0) 60062306a36Sopenharmony_ci return; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci memset(&info, 0, sizeof(struct i2c_board_info)); 60362306a36Sopenharmony_ci strscpy(info.type, "ir_video", I2C_NAME_SIZE); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci switch (core->boardnr) { 60662306a36Sopenharmony_ci case CX88_BOARD_LEADTEK_PVR2000: 60762306a36Sopenharmony_ci addr_list = pvr2000_addr_list; 60862306a36Sopenharmony_ci core->init_data.name = "cx88 Leadtek PVR 2000 remote"; 60962306a36Sopenharmony_ci core->init_data.type = RC_PROTO_BIT_UNKNOWN; 61062306a36Sopenharmony_ci core->init_data.get_key = get_key_pvr2000; 61162306a36Sopenharmony_ci core->init_data.ir_codes = RC_MAP_EMPTY; 61262306a36Sopenharmony_ci break; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci /* 61662306a36Sopenharmony_ci * We can't call i2c_new_scanned_device() because it uses 61762306a36Sopenharmony_ci * quick writes for probing and at least some RC receiver 61862306a36Sopenharmony_ci * devices only reply to reads. 61962306a36Sopenharmony_ci * Also, Hauppauge XVR needs to be specified, as address 0x71 62062306a36Sopenharmony_ci * conflicts with another remote type used with saa7134 62162306a36Sopenharmony_ci */ 62262306a36Sopenharmony_ci for (addrp = addr_list; *addrp != I2C_CLIENT_END; addrp++) { 62362306a36Sopenharmony_ci info.platform_data = NULL; 62462306a36Sopenharmony_ci memset(&core->init_data, 0, sizeof(core->init_data)); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (*addrp == 0x71) { 62762306a36Sopenharmony_ci /* Hauppauge Z8F0811 */ 62862306a36Sopenharmony_ci strscpy(info.type, "ir_z8f0811_haup", I2C_NAME_SIZE); 62962306a36Sopenharmony_ci core->init_data.name = core->board.name; 63062306a36Sopenharmony_ci core->init_data.ir_codes = RC_MAP_HAUPPAUGE; 63162306a36Sopenharmony_ci core->init_data.type = RC_PROTO_BIT_RC5 | 63262306a36Sopenharmony_ci RC_PROTO_BIT_RC6_MCE | RC_PROTO_BIT_RC6_6A_32; 63362306a36Sopenharmony_ci core->init_data.internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci info.platform_data = &core->init_data; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci if (i2c_smbus_xfer(&core->i2c_adap, *addrp, 0, 63862306a36Sopenharmony_ci I2C_SMBUS_READ, 0, 63962306a36Sopenharmony_ci I2C_SMBUS_QUICK, NULL) >= 0) { 64062306a36Sopenharmony_ci info.addr = *addrp; 64162306a36Sopenharmony_ci i2c_new_client_device(&core->i2c_adap, &info); 64262306a36Sopenharmony_ci break; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci/* ---------------------------------------------------------------------- */ 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ciMODULE_AUTHOR("Gerd Knorr, Pavel Machek, Chris Pascoe"); 65062306a36Sopenharmony_ciMODULE_DESCRIPTION("input driver for cx88 GPIO-based IR remote controls"); 65162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 652