18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Device driver for GPIO attached remote control interfaces 58c2ecf20Sopenharmony_ci * on Conexant 2388x based TV/DVB cards. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2003 Pavel Machek 88c2ecf20Sopenharmony_ci * Copyright (c) 2004 Gerd Knorr 98c2ecf20Sopenharmony_ci * Copyright (c) 2004, 2005 Chris Pascoe 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "cx88.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/hrtimer.h> 168c2ecf20Sopenharmony_ci#include <linux/pci.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <media/rc-core.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define MODULE_NAME "cx88xx" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct cx88_IR { 278c2ecf20Sopenharmony_ci struct cx88_core *core; 288c2ecf20Sopenharmony_ci struct rc_dev *dev; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci int users; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci char name[32]; 338c2ecf20Sopenharmony_ci char phys[32]; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci /* sample from gpio pin 16 */ 368c2ecf20Sopenharmony_ci u32 sampling; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci /* poll external decoder */ 398c2ecf20Sopenharmony_ci int polling; 408c2ecf20Sopenharmony_ci struct hrtimer timer; 418c2ecf20Sopenharmony_ci u32 gpio_addr; 428c2ecf20Sopenharmony_ci u32 last_gpio; 438c2ecf20Sopenharmony_ci u32 mask_keycode; 448c2ecf20Sopenharmony_ci u32 mask_keydown; 458c2ecf20Sopenharmony_ci u32 mask_keyup; 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic unsigned int ir_samplerate = 4; 498c2ecf20Sopenharmony_cimodule_param(ir_samplerate, uint, 0444); 508c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ir_samplerate, "IR samplerate in kHz, 1 - 20, default 4"); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic int ir_debug; 538c2ecf20Sopenharmony_cimodule_param(ir_debug, int, 0644); /* debug level [IR] */ 548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define ir_dprintk(fmt, arg...) do { \ 578c2ecf20Sopenharmony_ci if (ir_debug) \ 588c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s IR: " fmt, ir->core->name, ##arg);\ 598c2ecf20Sopenharmony_ci} while (0) 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define dprintk(fmt, arg...) do { \ 628c2ecf20Sopenharmony_ci if (ir_debug) \ 638c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cx88 IR: " fmt, ##arg); \ 648c2ecf20Sopenharmony_ci} while (0) 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void cx88_ir_handle_key(struct cx88_IR *ir) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct cx88_core *core = ir->core; 718c2ecf20Sopenharmony_ci u32 gpio, data, auxgpio; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* read gpio value */ 748c2ecf20Sopenharmony_ci gpio = cx_read(ir->gpio_addr); 758c2ecf20Sopenharmony_ci switch (core->boardnr) { 768c2ecf20Sopenharmony_ci case CX88_BOARD_NPGTECH_REALTV_TOP10FM: 778c2ecf20Sopenharmony_ci /* 788c2ecf20Sopenharmony_ci * This board apparently uses a combination of 2 GPIO 798c2ecf20Sopenharmony_ci * to represent the keys. Additionally, the second GPIO 808c2ecf20Sopenharmony_ci * can be used for parity. 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * Example: 838c2ecf20Sopenharmony_ci * 848c2ecf20Sopenharmony_ci * for key "5" 858c2ecf20Sopenharmony_ci * gpio = 0x758, auxgpio = 0xe5 or 0xf5 868c2ecf20Sopenharmony_ci * for key "Power" 878c2ecf20Sopenharmony_ci * gpio = 0x758, auxgpio = 0xed or 0xfd 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci auxgpio = cx_read(MO_GP1_IO); 918c2ecf20Sopenharmony_ci /* Take out the parity part */ 928c2ecf20Sopenharmony_ci gpio = (gpio & 0x7fd) + (auxgpio & 0xef); 938c2ecf20Sopenharmony_ci break; 948c2ecf20Sopenharmony_ci case CX88_BOARD_WINFAST_DTV1000: 958c2ecf20Sopenharmony_ci case CX88_BOARD_WINFAST_DTV1800H: 968c2ecf20Sopenharmony_ci case CX88_BOARD_WINFAST_DTV1800H_XC4000: 978c2ecf20Sopenharmony_ci case CX88_BOARD_WINFAST_DTV2000H_PLUS: 988c2ecf20Sopenharmony_ci case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: 998c2ecf20Sopenharmony_ci case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36: 1008c2ecf20Sopenharmony_ci case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43: 1018c2ecf20Sopenharmony_ci gpio = (gpio & 0x6ff) | ((cx_read(MO_GP1_IO) << 8) & 0x900); 1028c2ecf20Sopenharmony_ci auxgpio = gpio; 1038c2ecf20Sopenharmony_ci break; 1048c2ecf20Sopenharmony_ci default: 1058c2ecf20Sopenharmony_ci auxgpio = gpio; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci if (ir->polling) { 1088c2ecf20Sopenharmony_ci if (ir->last_gpio == auxgpio) 1098c2ecf20Sopenharmony_ci return; 1108c2ecf20Sopenharmony_ci ir->last_gpio = auxgpio; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* extract data */ 1148c2ecf20Sopenharmony_ci data = ir_extract_bits(gpio, ir->mask_keycode); 1158c2ecf20Sopenharmony_ci ir_dprintk("irq gpio=0x%x code=%d | %s%s%s\n", 1168c2ecf20Sopenharmony_ci gpio, data, 1178c2ecf20Sopenharmony_ci ir->polling ? "poll" : "irq", 1188c2ecf20Sopenharmony_ci (gpio & ir->mask_keydown) ? " down" : "", 1198c2ecf20Sopenharmony_ci (gpio & ir->mask_keyup) ? " up" : ""); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (ir->core->boardnr == CX88_BOARD_NORWOOD_MICRO) { 1228c2ecf20Sopenharmony_ci u32 gpio_key = cx_read(MO_GP0_IO); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci data = (data << 4) | ((gpio_key & 0xf0) >> 4); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci rc_keydown(ir->dev, RC_PROTO_UNKNOWN, data, 0); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci } else if (ir->core->boardnr == CX88_BOARD_PROLINK_PLAYTVPVR || 1298c2ecf20Sopenharmony_ci ir->core->boardnr == CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO) { 1308c2ecf20Sopenharmony_ci /* bit cleared on keydown, NEC scancode, 0xAAAACC, A = 0x866b */ 1318c2ecf20Sopenharmony_ci u16 addr; 1328c2ecf20Sopenharmony_ci u8 cmd; 1338c2ecf20Sopenharmony_ci u32 scancode; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci addr = (data >> 8) & 0xffff; 1368c2ecf20Sopenharmony_ci cmd = (data >> 0) & 0x00ff; 1378c2ecf20Sopenharmony_ci scancode = RC_SCANCODE_NECX(addr, cmd); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (0 == (gpio & ir->mask_keyup)) 1408c2ecf20Sopenharmony_ci rc_keydown_notimeout(ir->dev, RC_PROTO_NECX, scancode, 1418c2ecf20Sopenharmony_ci 0); 1428c2ecf20Sopenharmony_ci else 1438c2ecf20Sopenharmony_ci rc_keyup(ir->dev); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci } else if (ir->mask_keydown) { 1468c2ecf20Sopenharmony_ci /* bit set on keydown */ 1478c2ecf20Sopenharmony_ci if (gpio & ir->mask_keydown) 1488c2ecf20Sopenharmony_ci rc_keydown_notimeout(ir->dev, RC_PROTO_UNKNOWN, data, 1498c2ecf20Sopenharmony_ci 0); 1508c2ecf20Sopenharmony_ci else 1518c2ecf20Sopenharmony_ci rc_keyup(ir->dev); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci } else if (ir->mask_keyup) { 1548c2ecf20Sopenharmony_ci /* bit cleared on keydown */ 1558c2ecf20Sopenharmony_ci if (0 == (gpio & ir->mask_keyup)) 1568c2ecf20Sopenharmony_ci rc_keydown_notimeout(ir->dev, RC_PROTO_UNKNOWN, data, 1578c2ecf20Sopenharmony_ci 0); 1588c2ecf20Sopenharmony_ci else 1598c2ecf20Sopenharmony_ci rc_keyup(ir->dev); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci } else { 1628c2ecf20Sopenharmony_ci /* can't distinguish keydown/up :-/ */ 1638c2ecf20Sopenharmony_ci rc_keydown_notimeout(ir->dev, RC_PROTO_UNKNOWN, data, 0); 1648c2ecf20Sopenharmony_ci rc_keyup(ir->dev); 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic enum hrtimer_restart cx88_ir_work(struct hrtimer *timer) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci u64 missed; 1718c2ecf20Sopenharmony_ci struct cx88_IR *ir = container_of(timer, struct cx88_IR, timer); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci cx88_ir_handle_key(ir); 1748c2ecf20Sopenharmony_ci missed = hrtimer_forward_now(&ir->timer, 1758c2ecf20Sopenharmony_ci ktime_set(0, ir->polling * 1000000)); 1768c2ecf20Sopenharmony_ci if (missed > 1) 1778c2ecf20Sopenharmony_ci ir_dprintk("Missed ticks %llu\n", missed - 1); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return HRTIMER_RESTART; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic int __cx88_ir_start(void *priv) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct cx88_core *core = priv; 1858c2ecf20Sopenharmony_ci struct cx88_IR *ir; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (!core || !core->ir) 1888c2ecf20Sopenharmony_ci return -EINVAL; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci ir = core->ir; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (ir->polling) { 1938c2ecf20Sopenharmony_ci hrtimer_init(&ir->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 1948c2ecf20Sopenharmony_ci ir->timer.function = cx88_ir_work; 1958c2ecf20Sopenharmony_ci hrtimer_start(&ir->timer, 1968c2ecf20Sopenharmony_ci ktime_set(0, ir->polling * 1000000), 1978c2ecf20Sopenharmony_ci HRTIMER_MODE_REL); 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci if (ir->sampling) { 2008c2ecf20Sopenharmony_ci core->pci_irqmask |= PCI_INT_IR_SMPINT; 2018c2ecf20Sopenharmony_ci cx_write(MO_DDS_IO, 0x33F286 * ir_samplerate); /* samplerate */ 2028c2ecf20Sopenharmony_ci cx_write(MO_DDSCFG_IO, 0x5); /* enable */ 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic void __cx88_ir_stop(void *priv) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct cx88_core *core = priv; 2108c2ecf20Sopenharmony_ci struct cx88_IR *ir; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (!core || !core->ir) 2138c2ecf20Sopenharmony_ci return; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci ir = core->ir; 2168c2ecf20Sopenharmony_ci if (ir->sampling) { 2178c2ecf20Sopenharmony_ci cx_write(MO_DDSCFG_IO, 0x0); 2188c2ecf20Sopenharmony_ci core->pci_irqmask &= ~PCI_INT_IR_SMPINT; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (ir->polling) 2228c2ecf20Sopenharmony_ci hrtimer_cancel(&ir->timer); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ciint cx88_ir_start(struct cx88_core *core) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci if (core->ir->users) 2288c2ecf20Sopenharmony_ci return __cx88_ir_start(core); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cx88_ir_start); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_civoid cx88_ir_stop(struct cx88_core *core) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci if (core->ir->users) 2378c2ecf20Sopenharmony_ci __cx88_ir_stop(core); 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cx88_ir_stop); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int cx88_ir_open(struct rc_dev *rc) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct cx88_core *core = rc->priv; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci core->ir->users++; 2468c2ecf20Sopenharmony_ci return __cx88_ir_start(core); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic void cx88_ir_close(struct rc_dev *rc) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct cx88_core *core = rc->priv; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci core->ir->users--; 2548c2ecf20Sopenharmony_ci if (!core->ir->users) 2558c2ecf20Sopenharmony_ci __cx88_ir_stop(core); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ciint cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct cx88_IR *ir; 2638c2ecf20Sopenharmony_ci struct rc_dev *dev; 2648c2ecf20Sopenharmony_ci char *ir_codes = NULL; 2658c2ecf20Sopenharmony_ci u64 rc_proto = RC_PROTO_BIT_OTHER; 2668c2ecf20Sopenharmony_ci int err = -ENOMEM; 2678c2ecf20Sopenharmony_ci u32 hardware_mask = 0; /* For devices with a hardware mask, when 2688c2ecf20Sopenharmony_ci * used with a full-code IR table 2698c2ecf20Sopenharmony_ci */ 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci ir = kzalloc(sizeof(*ir), GFP_KERNEL); 2728c2ecf20Sopenharmony_ci dev = rc_allocate_device(RC_DRIVER_IR_RAW); 2738c2ecf20Sopenharmony_ci if (!ir || !dev) 2748c2ecf20Sopenharmony_ci goto err_out_free; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci ir->dev = dev; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* detect & configure */ 2798c2ecf20Sopenharmony_ci switch (core->boardnr) { 2808c2ecf20Sopenharmony_ci case CX88_BOARD_DNTV_LIVE_DVB_T: 2818c2ecf20Sopenharmony_ci case CX88_BOARD_KWORLD_DVB_T: 2828c2ecf20Sopenharmony_ci case CX88_BOARD_KWORLD_DVB_T_CX22702: 2838c2ecf20Sopenharmony_ci ir_codes = RC_MAP_DNTV_LIVE_DVB_T; 2848c2ecf20Sopenharmony_ci ir->gpio_addr = MO_GP1_IO; 2858c2ecf20Sopenharmony_ci ir->mask_keycode = 0x1f; 2868c2ecf20Sopenharmony_ci ir->mask_keyup = 0x60; 2878c2ecf20Sopenharmony_ci ir->polling = 50; /* ms */ 2888c2ecf20Sopenharmony_ci break; 2898c2ecf20Sopenharmony_ci case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1: 2908c2ecf20Sopenharmony_ci ir_codes = RC_MAP_CINERGY_1400; 2918c2ecf20Sopenharmony_ci ir->sampling = 0xeb04; /* address */ 2928c2ecf20Sopenharmony_ci break; 2938c2ecf20Sopenharmony_ci case CX88_BOARD_HAUPPAUGE: 2948c2ecf20Sopenharmony_ci case CX88_BOARD_HAUPPAUGE_DVB_T1: 2958c2ecf20Sopenharmony_ci case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: 2968c2ecf20Sopenharmony_ci case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: 2978c2ecf20Sopenharmony_ci case CX88_BOARD_HAUPPAUGE_HVR1100: 2988c2ecf20Sopenharmony_ci case CX88_BOARD_HAUPPAUGE_HVR3000: 2998c2ecf20Sopenharmony_ci case CX88_BOARD_HAUPPAUGE_HVR4000: 3008c2ecf20Sopenharmony_ci case CX88_BOARD_HAUPPAUGE_HVR4000LITE: 3018c2ecf20Sopenharmony_ci case CX88_BOARD_PCHDTV_HD3000: 3028c2ecf20Sopenharmony_ci case CX88_BOARD_PCHDTV_HD5500: 3038c2ecf20Sopenharmony_ci case CX88_BOARD_HAUPPAUGE_IRONLY: 3048c2ecf20Sopenharmony_ci ir_codes = RC_MAP_HAUPPAUGE; 3058c2ecf20Sopenharmony_ci ir->sampling = 1; 3068c2ecf20Sopenharmony_ci break; 3078c2ecf20Sopenharmony_ci case CX88_BOARD_WINFAST_DTV2000H: 3088c2ecf20Sopenharmony_ci case CX88_BOARD_WINFAST_DTV2000H_J: 3098c2ecf20Sopenharmony_ci case CX88_BOARD_WINFAST_DTV1800H: 3108c2ecf20Sopenharmony_ci case CX88_BOARD_WINFAST_DTV1800H_XC4000: 3118c2ecf20Sopenharmony_ci case CX88_BOARD_WINFAST_DTV2000H_PLUS: 3128c2ecf20Sopenharmony_ci ir_codes = RC_MAP_WINFAST; 3138c2ecf20Sopenharmony_ci ir->gpio_addr = MO_GP0_IO; 3148c2ecf20Sopenharmony_ci ir->mask_keycode = 0x8f8; 3158c2ecf20Sopenharmony_ci ir->mask_keyup = 0x100; 3168c2ecf20Sopenharmony_ci ir->polling = 50; /* ms */ 3178c2ecf20Sopenharmony_ci break; 3188c2ecf20Sopenharmony_ci case CX88_BOARD_WINFAST2000XP_EXPERT: 3198c2ecf20Sopenharmony_ci case CX88_BOARD_WINFAST_DTV1000: 3208c2ecf20Sopenharmony_ci case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: 3218c2ecf20Sopenharmony_ci case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36: 3228c2ecf20Sopenharmony_ci case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43: 3238c2ecf20Sopenharmony_ci ir_codes = RC_MAP_WINFAST; 3248c2ecf20Sopenharmony_ci ir->gpio_addr = MO_GP0_IO; 3258c2ecf20Sopenharmony_ci ir->mask_keycode = 0x8f8; 3268c2ecf20Sopenharmony_ci ir->mask_keyup = 0x100; 3278c2ecf20Sopenharmony_ci ir->polling = 1; /* ms */ 3288c2ecf20Sopenharmony_ci break; 3298c2ecf20Sopenharmony_ci case CX88_BOARD_IODATA_GVBCTV7E: 3308c2ecf20Sopenharmony_ci ir_codes = RC_MAP_IODATA_BCTV7E; 3318c2ecf20Sopenharmony_ci ir->gpio_addr = MO_GP0_IO; 3328c2ecf20Sopenharmony_ci ir->mask_keycode = 0xfd; 3338c2ecf20Sopenharmony_ci ir->mask_keydown = 0x02; 3348c2ecf20Sopenharmony_ci ir->polling = 5; /* ms */ 3358c2ecf20Sopenharmony_ci break; 3368c2ecf20Sopenharmony_ci case CX88_BOARD_PROLINK_PLAYTVPVR: 3378c2ecf20Sopenharmony_ci case CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO: 3388c2ecf20Sopenharmony_ci /* 3398c2ecf20Sopenharmony_ci * It seems that this hardware is paired with NEC extended 3408c2ecf20Sopenharmony_ci * address 0x866b. So, unfortunately, its usage with other 3418c2ecf20Sopenharmony_ci * IR's with different address won't work. Still, there are 3428c2ecf20Sopenharmony_ci * other IR's from the same manufacturer that works, like the 3438c2ecf20Sopenharmony_ci * 002-T mini RC, provided with newer PV hardware 3448c2ecf20Sopenharmony_ci */ 3458c2ecf20Sopenharmony_ci ir_codes = RC_MAP_PIXELVIEW_MK12; 3468c2ecf20Sopenharmony_ci rc_proto = RC_PROTO_BIT_NECX; 3478c2ecf20Sopenharmony_ci ir->gpio_addr = MO_GP1_IO; 3488c2ecf20Sopenharmony_ci ir->mask_keyup = 0x80; 3498c2ecf20Sopenharmony_ci ir->polling = 10; /* ms */ 3508c2ecf20Sopenharmony_ci hardware_mask = 0x3f; /* Hardware returns only 6 bits from command part */ 3518c2ecf20Sopenharmony_ci break; 3528c2ecf20Sopenharmony_ci case CX88_BOARD_PROLINK_PV_8000GT: 3538c2ecf20Sopenharmony_ci case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: 3548c2ecf20Sopenharmony_ci ir_codes = RC_MAP_PIXELVIEW_NEW; 3558c2ecf20Sopenharmony_ci ir->gpio_addr = MO_GP1_IO; 3568c2ecf20Sopenharmony_ci ir->mask_keycode = 0x3f; 3578c2ecf20Sopenharmony_ci ir->mask_keyup = 0x80; 3588c2ecf20Sopenharmony_ci ir->polling = 1; /* ms */ 3598c2ecf20Sopenharmony_ci break; 3608c2ecf20Sopenharmony_ci case CX88_BOARD_KWORLD_LTV883: 3618c2ecf20Sopenharmony_ci ir_codes = RC_MAP_PIXELVIEW; 3628c2ecf20Sopenharmony_ci ir->gpio_addr = MO_GP1_IO; 3638c2ecf20Sopenharmony_ci ir->mask_keycode = 0x1f; 3648c2ecf20Sopenharmony_ci ir->mask_keyup = 0x60; 3658c2ecf20Sopenharmony_ci ir->polling = 1; /* ms */ 3668c2ecf20Sopenharmony_ci break; 3678c2ecf20Sopenharmony_ci case CX88_BOARD_ADSTECH_DVB_T_PCI: 3688c2ecf20Sopenharmony_ci ir_codes = RC_MAP_ADSTECH_DVB_T_PCI; 3698c2ecf20Sopenharmony_ci ir->gpio_addr = MO_GP1_IO; 3708c2ecf20Sopenharmony_ci ir->mask_keycode = 0xbf; 3718c2ecf20Sopenharmony_ci ir->mask_keyup = 0x40; 3728c2ecf20Sopenharmony_ci ir->polling = 50; /* ms */ 3738c2ecf20Sopenharmony_ci break; 3748c2ecf20Sopenharmony_ci case CX88_BOARD_MSI_TVANYWHERE_MASTER: 3758c2ecf20Sopenharmony_ci ir_codes = RC_MAP_MSI_TVANYWHERE; 3768c2ecf20Sopenharmony_ci ir->gpio_addr = MO_GP1_IO; 3778c2ecf20Sopenharmony_ci ir->mask_keycode = 0x1f; 3788c2ecf20Sopenharmony_ci ir->mask_keyup = 0x40; 3798c2ecf20Sopenharmony_ci ir->polling = 1; /* ms */ 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci case CX88_BOARD_AVERTV_303: 3828c2ecf20Sopenharmony_ci case CX88_BOARD_AVERTV_STUDIO_303: 3838c2ecf20Sopenharmony_ci ir_codes = RC_MAP_AVERTV_303; 3848c2ecf20Sopenharmony_ci ir->gpio_addr = MO_GP2_IO; 3858c2ecf20Sopenharmony_ci ir->mask_keycode = 0xfb; 3868c2ecf20Sopenharmony_ci ir->mask_keydown = 0x02; 3878c2ecf20Sopenharmony_ci ir->polling = 50; /* ms */ 3888c2ecf20Sopenharmony_ci break; 3898c2ecf20Sopenharmony_ci case CX88_BOARD_OMICOM_SS4_PCI: 3908c2ecf20Sopenharmony_ci case CX88_BOARD_SATTRADE_ST4200: 3918c2ecf20Sopenharmony_ci case CX88_BOARD_TBS_8920: 3928c2ecf20Sopenharmony_ci case CX88_BOARD_TBS_8910: 3938c2ecf20Sopenharmony_ci case CX88_BOARD_PROF_7300: 3948c2ecf20Sopenharmony_ci case CX88_BOARD_PROF_7301: 3958c2ecf20Sopenharmony_ci case CX88_BOARD_PROF_6200: 3968c2ecf20Sopenharmony_ci ir_codes = RC_MAP_TBS_NEC; 3978c2ecf20Sopenharmony_ci ir->sampling = 0xff00; /* address */ 3988c2ecf20Sopenharmony_ci break; 3998c2ecf20Sopenharmony_ci case CX88_BOARD_TEVII_S464: 4008c2ecf20Sopenharmony_ci case CX88_BOARD_TEVII_S460: 4018c2ecf20Sopenharmony_ci case CX88_BOARD_TEVII_S420: 4028c2ecf20Sopenharmony_ci ir_codes = RC_MAP_TEVII_NEC; 4038c2ecf20Sopenharmony_ci ir->sampling = 0xff00; /* address */ 4048c2ecf20Sopenharmony_ci break; 4058c2ecf20Sopenharmony_ci case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: 4068c2ecf20Sopenharmony_ci ir_codes = RC_MAP_DNTV_LIVE_DVBT_PRO; 4078c2ecf20Sopenharmony_ci ir->sampling = 0xff00; /* address */ 4088c2ecf20Sopenharmony_ci break; 4098c2ecf20Sopenharmony_ci case CX88_BOARD_NORWOOD_MICRO: 4108c2ecf20Sopenharmony_ci ir_codes = RC_MAP_NORWOOD; 4118c2ecf20Sopenharmony_ci ir->gpio_addr = MO_GP1_IO; 4128c2ecf20Sopenharmony_ci ir->mask_keycode = 0x0e; 4138c2ecf20Sopenharmony_ci ir->mask_keyup = 0x80; 4148c2ecf20Sopenharmony_ci ir->polling = 50; /* ms */ 4158c2ecf20Sopenharmony_ci break; 4168c2ecf20Sopenharmony_ci case CX88_BOARD_NPGTECH_REALTV_TOP10FM: 4178c2ecf20Sopenharmony_ci ir_codes = RC_MAP_NPGTECH; 4188c2ecf20Sopenharmony_ci ir->gpio_addr = MO_GP0_IO; 4198c2ecf20Sopenharmony_ci ir->mask_keycode = 0xfa; 4208c2ecf20Sopenharmony_ci ir->polling = 50; /* ms */ 4218c2ecf20Sopenharmony_ci break; 4228c2ecf20Sopenharmony_ci case CX88_BOARD_PINNACLE_PCTV_HD_800i: 4238c2ecf20Sopenharmony_ci ir_codes = RC_MAP_PINNACLE_PCTV_HD; 4248c2ecf20Sopenharmony_ci ir->sampling = 1; 4258c2ecf20Sopenharmony_ci break; 4268c2ecf20Sopenharmony_ci case CX88_BOARD_POWERCOLOR_REAL_ANGEL: 4278c2ecf20Sopenharmony_ci ir_codes = RC_MAP_POWERCOLOR_REAL_ANGEL; 4288c2ecf20Sopenharmony_ci ir->gpio_addr = MO_GP2_IO; 4298c2ecf20Sopenharmony_ci ir->mask_keycode = 0x7e; 4308c2ecf20Sopenharmony_ci ir->polling = 100; /* ms */ 4318c2ecf20Sopenharmony_ci break; 4328c2ecf20Sopenharmony_ci case CX88_BOARD_TWINHAN_VP1027_DVBS: 4338c2ecf20Sopenharmony_ci ir_codes = RC_MAP_TWINHAN_VP1027_DVBS; 4348c2ecf20Sopenharmony_ci ir->sampling = 0xff00; /* address */ 4358c2ecf20Sopenharmony_ci break; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (!ir_codes) { 4398c2ecf20Sopenharmony_ci err = -ENODEV; 4408c2ecf20Sopenharmony_ci goto err_out_free; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci /* 4448c2ecf20Sopenharmony_ci * The usage of mask_keycode were very convenient, due to several 4458c2ecf20Sopenharmony_ci * reasons. Among others, the scancode tables were using the scancode 4468c2ecf20Sopenharmony_ci * as the index elements. So, the less bits it was used, the smaller 4478c2ecf20Sopenharmony_ci * the table were stored. After the input changes, the better is to use 4488c2ecf20Sopenharmony_ci * the full scancodes, since it allows replacing the IR remote by 4498c2ecf20Sopenharmony_ci * another one. Unfortunately, there are still some hardware, like 4508c2ecf20Sopenharmony_ci * Pixelview Ultra Pro, where only part of the scancode is sent via 4518c2ecf20Sopenharmony_ci * GPIO. So, there's no way to get the full scancode. Due to that, 4528c2ecf20Sopenharmony_ci * hardware_mask were introduced here: it represents those hardware 4538c2ecf20Sopenharmony_ci * that has such limits. 4548c2ecf20Sopenharmony_ci */ 4558c2ecf20Sopenharmony_ci if (hardware_mask && !ir->mask_keycode) 4568c2ecf20Sopenharmony_ci ir->mask_keycode = hardware_mask; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci /* init input device */ 4598c2ecf20Sopenharmony_ci snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)", core->board.name); 4608c2ecf20Sopenharmony_ci snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(pci)); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci dev->device_name = ir->name; 4638c2ecf20Sopenharmony_ci dev->input_phys = ir->phys; 4648c2ecf20Sopenharmony_ci dev->input_id.bustype = BUS_PCI; 4658c2ecf20Sopenharmony_ci dev->input_id.version = 1; 4668c2ecf20Sopenharmony_ci if (pci->subsystem_vendor) { 4678c2ecf20Sopenharmony_ci dev->input_id.vendor = pci->subsystem_vendor; 4688c2ecf20Sopenharmony_ci dev->input_id.product = pci->subsystem_device; 4698c2ecf20Sopenharmony_ci } else { 4708c2ecf20Sopenharmony_ci dev->input_id.vendor = pci->vendor; 4718c2ecf20Sopenharmony_ci dev->input_id.product = pci->device; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci dev->dev.parent = &pci->dev; 4748c2ecf20Sopenharmony_ci dev->map_name = ir_codes; 4758c2ecf20Sopenharmony_ci dev->driver_name = MODULE_NAME; 4768c2ecf20Sopenharmony_ci dev->priv = core; 4778c2ecf20Sopenharmony_ci dev->open = cx88_ir_open; 4788c2ecf20Sopenharmony_ci dev->close = cx88_ir_close; 4798c2ecf20Sopenharmony_ci dev->scancode_mask = hardware_mask; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (ir->sampling) { 4828c2ecf20Sopenharmony_ci dev->timeout = MS_TO_US(10); /* 10 ms */ 4838c2ecf20Sopenharmony_ci } else { 4848c2ecf20Sopenharmony_ci dev->driver_type = RC_DRIVER_SCANCODE; 4858c2ecf20Sopenharmony_ci dev->allowed_protocols = rc_proto; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci ir->core = core; 4898c2ecf20Sopenharmony_ci core->ir = ir; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* all done */ 4928c2ecf20Sopenharmony_ci err = rc_register_device(dev); 4938c2ecf20Sopenharmony_ci if (err) 4948c2ecf20Sopenharmony_ci goto err_out_free; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci return 0; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cierr_out_free: 4998c2ecf20Sopenharmony_ci rc_free_device(dev); 5008c2ecf20Sopenharmony_ci core->ir = NULL; 5018c2ecf20Sopenharmony_ci kfree(ir); 5028c2ecf20Sopenharmony_ci return err; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ciint cx88_ir_fini(struct cx88_core *core) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci struct cx88_IR *ir = core->ir; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* skip detach on non attached boards */ 5108c2ecf20Sopenharmony_ci if (!ir) 5118c2ecf20Sopenharmony_ci return 0; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci cx88_ir_stop(core); 5148c2ecf20Sopenharmony_ci rc_unregister_device(ir->dev); 5158c2ecf20Sopenharmony_ci kfree(ir); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* done */ 5188c2ecf20Sopenharmony_ci core->ir = NULL; 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_civoid cx88_ir_irq(struct cx88_core *core) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci struct cx88_IR *ir = core->ir; 5278c2ecf20Sopenharmony_ci u32 samples; 5288c2ecf20Sopenharmony_ci unsigned int todo, bits; 5298c2ecf20Sopenharmony_ci struct ir_raw_event ev = {}; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (!ir || !ir->sampling) 5328c2ecf20Sopenharmony_ci return; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* 5358c2ecf20Sopenharmony_ci * Samples are stored in a 32 bit register, oldest sample in 5368c2ecf20Sopenharmony_ci * the msb. A set bit represents space and an unset bit 5378c2ecf20Sopenharmony_ci * represents a pulse. 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_ci samples = cx_read(MO_SAMPLE_IO); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci if (samples == 0xff && ir->dev->idle) 5428c2ecf20Sopenharmony_ci return; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci for (todo = 32; todo > 0; todo -= bits) { 5458c2ecf20Sopenharmony_ci ev.pulse = samples & 0x80000000 ? false : true; 5468c2ecf20Sopenharmony_ci bits = min(todo, 32U - fls(ev.pulse ? samples : ~samples)); 5478c2ecf20Sopenharmony_ci ev.duration = (bits * (USEC_PER_SEC / 1000)) / ir_samplerate; 5488c2ecf20Sopenharmony_ci ir_raw_event_store_with_filter(ir->dev, &ev); 5498c2ecf20Sopenharmony_ci samples <<= bits; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci ir_raw_event_handle(ir->dev); 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic int get_key_pvr2000(struct IR_i2c *ir, enum rc_proto *protocol, 5558c2ecf20Sopenharmony_ci u32 *scancode, u8 *toggle) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci int flags, code; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* poll IR chip */ 5608c2ecf20Sopenharmony_ci flags = i2c_smbus_read_byte_data(ir->c, 0x10); 5618c2ecf20Sopenharmony_ci if (flags < 0) { 5628c2ecf20Sopenharmony_ci dprintk("read error\n"); 5638c2ecf20Sopenharmony_ci return 0; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci /* key pressed ? */ 5668c2ecf20Sopenharmony_ci if (0 == (flags & 0x80)) 5678c2ecf20Sopenharmony_ci return 0; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* read actual key code */ 5708c2ecf20Sopenharmony_ci code = i2c_smbus_read_byte_data(ir->c, 0x00); 5718c2ecf20Sopenharmony_ci if (code < 0) { 5728c2ecf20Sopenharmony_ci dprintk("read error\n"); 5738c2ecf20Sopenharmony_ci return 0; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci dprintk("IR Key/Flags: (0x%02x/0x%02x)\n", 5778c2ecf20Sopenharmony_ci code & 0xff, flags & 0xff); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci *protocol = RC_PROTO_UNKNOWN; 5808c2ecf20Sopenharmony_ci *scancode = code & 0xff; 5818c2ecf20Sopenharmony_ci *toggle = 0; 5828c2ecf20Sopenharmony_ci return 1; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_civoid cx88_i2c_init_ir(struct cx88_core *core) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci struct i2c_board_info info; 5888c2ecf20Sopenharmony_ci static const unsigned short default_addr_list[] = { 5898c2ecf20Sopenharmony_ci 0x18, 0x6b, 0x71, 5908c2ecf20Sopenharmony_ci I2C_CLIENT_END 5918c2ecf20Sopenharmony_ci }; 5928c2ecf20Sopenharmony_ci static const unsigned short pvr2000_addr_list[] = { 5938c2ecf20Sopenharmony_ci 0x18, 0x1a, 5948c2ecf20Sopenharmony_ci I2C_CLIENT_END 5958c2ecf20Sopenharmony_ci }; 5968c2ecf20Sopenharmony_ci const unsigned short *addr_list = default_addr_list; 5978c2ecf20Sopenharmony_ci const unsigned short *addrp; 5988c2ecf20Sopenharmony_ci /* Instantiate the IR receiver device, if present */ 5998c2ecf20Sopenharmony_ci if (core->i2c_rc != 0) 6008c2ecf20Sopenharmony_ci return; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci memset(&info, 0, sizeof(struct i2c_board_info)); 6038c2ecf20Sopenharmony_ci strscpy(info.type, "ir_video", I2C_NAME_SIZE); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci switch (core->boardnr) { 6068c2ecf20Sopenharmony_ci case CX88_BOARD_LEADTEK_PVR2000: 6078c2ecf20Sopenharmony_ci addr_list = pvr2000_addr_list; 6088c2ecf20Sopenharmony_ci core->init_data.name = "cx88 Leadtek PVR 2000 remote"; 6098c2ecf20Sopenharmony_ci core->init_data.type = RC_PROTO_BIT_UNKNOWN; 6108c2ecf20Sopenharmony_ci core->init_data.get_key = get_key_pvr2000; 6118c2ecf20Sopenharmony_ci core->init_data.ir_codes = RC_MAP_EMPTY; 6128c2ecf20Sopenharmony_ci break; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci /* 6168c2ecf20Sopenharmony_ci * We can't call i2c_new_scanned_device() because it uses 6178c2ecf20Sopenharmony_ci * quick writes for probing and at least some RC receiver 6188c2ecf20Sopenharmony_ci * devices only reply to reads. 6198c2ecf20Sopenharmony_ci * Also, Hauppauge XVR needs to be specified, as address 0x71 6208c2ecf20Sopenharmony_ci * conflicts with another remote type used with saa7134 6218c2ecf20Sopenharmony_ci */ 6228c2ecf20Sopenharmony_ci for (addrp = addr_list; *addrp != I2C_CLIENT_END; addrp++) { 6238c2ecf20Sopenharmony_ci info.platform_data = NULL; 6248c2ecf20Sopenharmony_ci memset(&core->init_data, 0, sizeof(core->init_data)); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (*addrp == 0x71) { 6278c2ecf20Sopenharmony_ci /* Hauppauge Z8F0811 */ 6288c2ecf20Sopenharmony_ci strscpy(info.type, "ir_z8f0811_haup", I2C_NAME_SIZE); 6298c2ecf20Sopenharmony_ci core->init_data.name = core->board.name; 6308c2ecf20Sopenharmony_ci core->init_data.ir_codes = RC_MAP_HAUPPAUGE; 6318c2ecf20Sopenharmony_ci core->init_data.type = RC_PROTO_BIT_RC5 | 6328c2ecf20Sopenharmony_ci RC_PROTO_BIT_RC6_MCE | RC_PROTO_BIT_RC6_6A_32; 6338c2ecf20Sopenharmony_ci core->init_data.internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci info.platform_data = &core->init_data; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci if (i2c_smbus_xfer(&core->i2c_adap, *addrp, 0, 6388c2ecf20Sopenharmony_ci I2C_SMBUS_READ, 0, 6398c2ecf20Sopenharmony_ci I2C_SMBUS_QUICK, NULL) >= 0) { 6408c2ecf20Sopenharmony_ci info.addr = *addrp; 6418c2ecf20Sopenharmony_ci i2c_new_client_device(&core->i2c_adap, &info); 6428c2ecf20Sopenharmony_ci break; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci/* ---------------------------------------------------------------------- */ 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ciMODULE_AUTHOR("Gerd Knorr, Pavel Machek, Chris Pascoe"); 6508c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("input driver for cx88 GPIO-based IR remote controls"); 6518c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 652