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