1f22e9e71SMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0+
2f22e9e71SMauro Carvalho Chehab //
3f22e9e71SMauro Carvalho Chehab // handle em28xx IR remotes via linux kernel input layer.
4f22e9e71SMauro Carvalho Chehab //
5f22e9e71SMauro Carvalho Chehab // Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
6f22e9e71SMauro Carvalho Chehab //		      Markus Rechberger <mrechberger@gmail.com>
732590819SMauro Carvalho Chehab //		      Mauro Carvalho Chehab <mchehab@kernel.org>
8f22e9e71SMauro Carvalho Chehab //		      Sascha Sommer <saschasommer@freenet.de>
90c0d06caSMauro Carvalho Chehab 
108314d402SMauro Carvalho Chehab #include "em28xx.h"
118314d402SMauro Carvalho Chehab 
120c0d06caSMauro Carvalho Chehab #include <linux/module.h>
130c0d06caSMauro Carvalho Chehab #include <linux/init.h>
140c0d06caSMauro Carvalho Chehab #include <linux/delay.h>
150c0d06caSMauro Carvalho Chehab #include <linux/interrupt.h>
160c0d06caSMauro Carvalho Chehab #include <linux/usb.h>
170547858bSSean Young #include <linux/usb/input.h>
180c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
19120703f9SDavid Härdeman #include <linux/bitrev.h>
200c0d06caSMauro Carvalho Chehab 
210c0d06caSMauro Carvalho Chehab #define EM28XX_SNAPSHOT_KEY				KEY_CAMERA
220ff950a7SFrank Schaefer #define EM28XX_BUTTONS_DEBOUNCED_QUERY_INTERVAL		500 /* [ms] */
230ff950a7SFrank Schaefer #define EM28XX_BUTTONS_VOLATILE_QUERY_INTERVAL		100 /* [ms] */
240c0d06caSMauro Carvalho Chehab 
250c0d06caSMauro Carvalho Chehab static unsigned int ir_debug;
260c0d06caSMauro Carvalho Chehab module_param(ir_debug, int, 0644);
270c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
280c0d06caSMauro Carvalho Chehab 
290c0d06caSMauro Carvalho Chehab #define MODULE_NAME "em28xx"
300c0d06caSMauro Carvalho Chehab 
31ce8591ffSMauro Carvalho Chehab #define dprintk(fmt, arg...) do {					\
32ce8591ffSMauro Carvalho Chehab 	if (ir_debug)							\
3329b05e22SMauro Carvalho Chehab 		dev_printk(KERN_DEBUG, &ir->dev->intf->dev,		\
34ce8591ffSMauro Carvalho Chehab 			   "input: %s: " fmt, __func__, ## arg);	\
35ce8591ffSMauro Carvalho Chehab } while (0)
360c0d06caSMauro Carvalho Chehab 
3708cc05e4SMauro Carvalho Chehab /*
3808cc05e4SMauro Carvalho Chehab  * Polling structure used by em28xx IR's
3908cc05e4SMauro Carvalho Chehab  */
400c0d06caSMauro Carvalho Chehab 
410c0d06caSMauro Carvalho Chehab struct em28xx_ir_poll_result {
420c0d06caSMauro Carvalho Chehab 	unsigned int toggle_bit:1;
430c0d06caSMauro Carvalho Chehab 	unsigned int read_count:7;
44105e3687SMauro Carvalho Chehab 
456d741bfeSSean Young 	enum rc_proto protocol;
46105e3687SMauro Carvalho Chehab 	u32 scancode;
470c0d06caSMauro Carvalho Chehab };
480c0d06caSMauro Carvalho Chehab 
490c0d06caSMauro Carvalho Chehab struct em28xx_IR {
500c0d06caSMauro Carvalho Chehab 	struct em28xx *dev;
510c0d06caSMauro Carvalho Chehab 	struct rc_dev *rc;
520c0d06caSMauro Carvalho Chehab 	char phys[32];
530c0d06caSMauro Carvalho Chehab 
54768da3dbSFrank Schaefer 	/* poll decoder */
550c0d06caSMauro Carvalho Chehab 	int polling;
560c0d06caSMauro Carvalho Chehab 	struct delayed_work work;
570c0d06caSMauro Carvalho Chehab 	unsigned int full_code:1;
580c0d06caSMauro Carvalho Chehab 	unsigned int last_readcount;
596d741bfeSSean Young 	u64 rc_proto;
600c0d06caSMauro Carvalho Chehab 
6178e719a5SFrank Schaefer 	struct i2c_client *i2c_client;
62768da3dbSFrank Schaefer 
636d741bfeSSean Young 	int  (*get_key_i2c)(struct i2c_client *ir, enum rc_proto *protocol,
646d741bfeSSean Young 			    u32 *scancode);
6508cc05e4SMauro Carvalho Chehab 	int  (*get_key)(struct em28xx_IR *ir, struct em28xx_ir_poll_result *r);
660c0d06caSMauro Carvalho Chehab };
670c0d06caSMauro Carvalho Chehab 
6808cc05e4SMauro Carvalho Chehab /*
6908cc05e4SMauro Carvalho Chehab  * I2C IR based get keycodes - should be used with ir-kbd-i2c
7008cc05e4SMauro Carvalho Chehab  */
710c0d06caSMauro Carvalho Chehab 
em28xx_get_key_terratec(struct i2c_client * i2c_dev,enum rc_proto * protocol,u32 * scancode)72120703f9SDavid Härdeman static int em28xx_get_key_terratec(struct i2c_client *i2c_dev,
736d741bfeSSean Young 				   enum rc_proto *protocol, u32 *scancode)
740c0d06caSMauro Carvalho Chehab {
75728d9fd9SMauro Carvalho Chehab 	int rc;
760c0d06caSMauro Carvalho Chehab 	unsigned char b;
770c0d06caSMauro Carvalho Chehab 
780c0d06caSMauro Carvalho Chehab 	/* poll IR chip */
79728d9fd9SMauro Carvalho Chehab 	rc = i2c_master_recv(i2c_dev, &b, 1);
80728d9fd9SMauro Carvalho Chehab 	if (rc != 1) {
81728d9fd9SMauro Carvalho Chehab 		if (rc < 0)
82728d9fd9SMauro Carvalho Chehab 			return rc;
830c0d06caSMauro Carvalho Chehab 		return -EIO;
84728d9fd9SMauro Carvalho Chehab 	}
850c0d06caSMauro Carvalho Chehab 
8608cc05e4SMauro Carvalho Chehab 	/*
8708cc05e4SMauro Carvalho Chehab 	 * it seems that 0xFE indicates that a button is still hold
8808cc05e4SMauro Carvalho Chehab 	 * down, while 0xff indicates that no button is hold down.
8908cc05e4SMauro Carvalho Chehab 	 */
900c0d06caSMauro Carvalho Chehab 
910c0d06caSMauro Carvalho Chehab 	if (b == 0xff)
920c0d06caSMauro Carvalho Chehab 		return 0;
930c0d06caSMauro Carvalho Chehab 
940c0d06caSMauro Carvalho Chehab 	if (b == 0xfe)
950c0d06caSMauro Carvalho Chehab 		/* keep old data */
960c0d06caSMauro Carvalho Chehab 		return 1;
970c0d06caSMauro Carvalho Chehab 
986d741bfeSSean Young 	*protocol = RC_PROTO_UNKNOWN;
99120703f9SDavid Härdeman 	*scancode = b;
1000c0d06caSMauro Carvalho Chehab 	return 1;
1010c0d06caSMauro Carvalho Chehab }
1020c0d06caSMauro Carvalho Chehab 
em28xx_get_key_em_haup(struct i2c_client * i2c_dev,enum rc_proto * protocol,u32 * scancode)103120703f9SDavid Härdeman static int em28xx_get_key_em_haup(struct i2c_client *i2c_dev,
1046d741bfeSSean Young 				  enum rc_proto *protocol, u32 *scancode)
1050c0d06caSMauro Carvalho Chehab {
1060c0d06caSMauro Carvalho Chehab 	unsigned char buf[2];
1070c0d06caSMauro Carvalho Chehab 	int size;
1080c0d06caSMauro Carvalho Chehab 
1090c0d06caSMauro Carvalho Chehab 	/* poll IR chip */
110768da3dbSFrank Schaefer 	size = i2c_master_recv(i2c_dev, buf, sizeof(buf));
1110c0d06caSMauro Carvalho Chehab 
1120c0d06caSMauro Carvalho Chehab 	if (size != 2)
1130c0d06caSMauro Carvalho Chehab 		return -EIO;
1140c0d06caSMauro Carvalho Chehab 
1150c0d06caSMauro Carvalho Chehab 	/* Does eliminate repeated parity code */
1160c0d06caSMauro Carvalho Chehab 	if (buf[1] == 0xff)
1170c0d06caSMauro Carvalho Chehab 		return 0;
1180c0d06caSMauro Carvalho Chehab 
1190c0d06caSMauro Carvalho Chehab 	/*
1200c0d06caSMauro Carvalho Chehab 	 * Rearranges bits to the right order.
1210c0d06caSMauro Carvalho Chehab 	 * The bit order were determined experimentally by using
1220c0d06caSMauro Carvalho Chehab 	 * The original Hauppauge Grey IR and another RC5 that uses addr=0x08
1230c0d06caSMauro Carvalho Chehab 	 * The RC5 code has 14 bits, but we've experimentally determined
1240c0d06caSMauro Carvalho Chehab 	 * the meaning for only 11 bits.
1250c0d06caSMauro Carvalho Chehab 	 * So, the code translation is not complete. Yet, it is enough to
1260c0d06caSMauro Carvalho Chehab 	 * work with the provided RC5 IR.
1270c0d06caSMauro Carvalho Chehab 	 */
1286d741bfeSSean Young 	*protocol = RC_PROTO_RC5;
129120703f9SDavid Härdeman 	*scancode = (bitrev8(buf[1]) & 0x1f) << 8 | bitrev8(buf[0]) >> 2;
1300c0d06caSMauro Carvalho Chehab 	return 1;
1310c0d06caSMauro Carvalho Chehab }
1320c0d06caSMauro Carvalho Chehab 
em28xx_get_key_pinnacle_usb_grey(struct i2c_client * i2c_dev,enum rc_proto * protocol,u32 * scancode)133768da3dbSFrank Schaefer static int em28xx_get_key_pinnacle_usb_grey(struct i2c_client *i2c_dev,
1346d741bfeSSean Young 					    enum rc_proto *protocol,
1356d741bfeSSean Young 					    u32 *scancode)
1360c0d06caSMauro Carvalho Chehab {
1370c0d06caSMauro Carvalho Chehab 	unsigned char buf[3];
1380c0d06caSMauro Carvalho Chehab 
1390c0d06caSMauro Carvalho Chehab 	/* poll IR chip */
1400c0d06caSMauro Carvalho Chehab 
14108cc05e4SMauro Carvalho Chehab 	if (i2c_master_recv(i2c_dev, buf, 3) != 3)
1420c0d06caSMauro Carvalho Chehab 		return -EIO;
1430c0d06caSMauro Carvalho Chehab 
1440c0d06caSMauro Carvalho Chehab 	if (buf[0] != 0x00)
1450c0d06caSMauro Carvalho Chehab 		return 0;
1460c0d06caSMauro Carvalho Chehab 
1476d741bfeSSean Young 	*protocol = RC_PROTO_UNKNOWN;
148120703f9SDavid Härdeman 	*scancode = buf[2] & 0x3f;
1490c0d06caSMauro Carvalho Chehab 	return 1;
1500c0d06caSMauro Carvalho Chehab }
1510c0d06caSMauro Carvalho Chehab 
em28xx_get_key_winfast_usbii_deluxe(struct i2c_client * i2c_dev,enum rc_proto * protocol,u32 * scancode)152768da3dbSFrank Schaefer static int em28xx_get_key_winfast_usbii_deluxe(struct i2c_client *i2c_dev,
1536d741bfeSSean Young 					       enum rc_proto *protocol,
1546d741bfeSSean Young 					       u32 *scancode)
1550c0d06caSMauro Carvalho Chehab {
1560c0d06caSMauro Carvalho Chehab 	unsigned char subaddr, keydetect, key;
1570c0d06caSMauro Carvalho Chehab 
15808cc05e4SMauro Carvalho Chehab 	struct i2c_msg msg[] = {
15908cc05e4SMauro Carvalho Chehab 		{
16008cc05e4SMauro Carvalho Chehab 			.addr = i2c_dev->addr,
16108cc05e4SMauro Carvalho Chehab 			.flags = 0,
16208cc05e4SMauro Carvalho Chehab 			.buf = &subaddr, .len = 1
16308cc05e4SMauro Carvalho Chehab 		}, {
16408cc05e4SMauro Carvalho Chehab 			.addr = i2c_dev->addr,
16508cc05e4SMauro Carvalho Chehab 			.flags = I2C_M_RD,
16608cc05e4SMauro Carvalho Chehab 			.buf = &keydetect,
16708cc05e4SMauro Carvalho Chehab 			.len = 1
16808cc05e4SMauro Carvalho Chehab 		}
16908cc05e4SMauro Carvalho Chehab 	};
1700c0d06caSMauro Carvalho Chehab 
1710c0d06caSMauro Carvalho Chehab 	subaddr = 0x10;
17208cc05e4SMauro Carvalho Chehab 	if (i2c_transfer(i2c_dev->adapter, msg, 2) != 2)
1730c0d06caSMauro Carvalho Chehab 		return -EIO;
1740c0d06caSMauro Carvalho Chehab 	if (keydetect == 0x00)
1750c0d06caSMauro Carvalho Chehab 		return 0;
1760c0d06caSMauro Carvalho Chehab 
1770c0d06caSMauro Carvalho Chehab 	subaddr = 0x00;
1780c0d06caSMauro Carvalho Chehab 	msg[1].buf = &key;
17908cc05e4SMauro Carvalho Chehab 	if (i2c_transfer(i2c_dev->adapter, msg, 2) != 2)
1800c0d06caSMauro Carvalho Chehab 		return -EIO;
1810c0d06caSMauro Carvalho Chehab 	if (key == 0x00)
1820c0d06caSMauro Carvalho Chehab 		return 0;
1830c0d06caSMauro Carvalho Chehab 
1846d741bfeSSean Young 	*protocol = RC_PROTO_UNKNOWN;
185120703f9SDavid Härdeman 	*scancode = key;
1860c0d06caSMauro Carvalho Chehab 	return 1;
1870c0d06caSMauro Carvalho Chehab }
1880c0d06caSMauro Carvalho Chehab 
18908cc05e4SMauro Carvalho Chehab /*
19008cc05e4SMauro Carvalho Chehab  * Poll based get keycode functions
19108cc05e4SMauro Carvalho Chehab  */
1920c0d06caSMauro Carvalho Chehab 
1930c0d06caSMauro Carvalho Chehab /* This is for the em2860/em2880 */
default_polling_getkey(struct em28xx_IR * ir,struct em28xx_ir_poll_result * poll_result)1940c0d06caSMauro Carvalho Chehab static int default_polling_getkey(struct em28xx_IR *ir,
1950c0d06caSMauro Carvalho Chehab 				  struct em28xx_ir_poll_result *poll_result)
1960c0d06caSMauro Carvalho Chehab {
1970c0d06caSMauro Carvalho Chehab 	struct em28xx *dev = ir->dev;
1980c0d06caSMauro Carvalho Chehab 	int rc;
1990c0d06caSMauro Carvalho Chehab 	u8 msg[3] = { 0, 0, 0 };
2000c0d06caSMauro Carvalho Chehab 
20108cc05e4SMauro Carvalho Chehab 	/*
20208cc05e4SMauro Carvalho Chehab 	 * Read key toggle, brand, and key code
20308cc05e4SMauro Carvalho Chehab 	 * on registers 0x45, 0x46 and 0x47
2040c0d06caSMauro Carvalho Chehab 	 */
2050c0d06caSMauro Carvalho Chehab 	rc = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R45_IR,
2060c0d06caSMauro Carvalho Chehab 					  msg, sizeof(msg));
2070c0d06caSMauro Carvalho Chehab 	if (rc < 0)
2080c0d06caSMauro Carvalho Chehab 		return rc;
2090c0d06caSMauro Carvalho Chehab 
2100c0d06caSMauro Carvalho Chehab 	/* Infrared toggle (Reg 0x45[7]) */
2110c0d06caSMauro Carvalho Chehab 	poll_result->toggle_bit = (msg[0] >> 7);
2120c0d06caSMauro Carvalho Chehab 
2130c0d06caSMauro Carvalho Chehab 	/* Infrared read count (Reg 0x45[6:0] */
2140c0d06caSMauro Carvalho Chehab 	poll_result->read_count = (msg[0] & 0x7f);
2150c0d06caSMauro Carvalho Chehab 
216105e3687SMauro Carvalho Chehab 	/* Remote Control Address/Data (Regs 0x46/0x47) */
2176d741bfeSSean Young 	switch (ir->rc_proto) {
2186d741bfeSSean Young 	case RC_PROTO_BIT_RC5:
2196d741bfeSSean Young 		poll_result->protocol = RC_PROTO_RC5;
220120703f9SDavid Härdeman 		poll_result->scancode = RC_SCANCODE_RC5(msg[1], msg[2]);
221120703f9SDavid Härdeman 		break;
222120703f9SDavid Härdeman 
2236d741bfeSSean Young 	case RC_PROTO_BIT_NEC:
2246d741bfeSSean Young 		poll_result->protocol = RC_PROTO_NEC;
225120703f9SDavid Härdeman 		poll_result->scancode = RC_SCANCODE_NEC(msg[1], msg[2]);
226120703f9SDavid Härdeman 		break;
227120703f9SDavid Härdeman 
228120703f9SDavid Härdeman 	default:
2296d741bfeSSean Young 		poll_result->protocol = RC_PROTO_UNKNOWN;
230105e3687SMauro Carvalho Chehab 		poll_result->scancode = msg[1] << 8 | msg[2];
231120703f9SDavid Härdeman 		break;
232120703f9SDavid Härdeman 	}
2330c0d06caSMauro Carvalho Chehab 
2340c0d06caSMauro Carvalho Chehab 	return 0;
2350c0d06caSMauro Carvalho Chehab }
2360c0d06caSMauro Carvalho Chehab 
em2874_polling_getkey(struct em28xx_IR * ir,struct em28xx_ir_poll_result * poll_result)2370c0d06caSMauro Carvalho Chehab static int em2874_polling_getkey(struct em28xx_IR *ir,
2380c0d06caSMauro Carvalho Chehab 				 struct em28xx_ir_poll_result *poll_result)
2390c0d06caSMauro Carvalho Chehab {
2400c0d06caSMauro Carvalho Chehab 	struct em28xx *dev = ir->dev;
2410c0d06caSMauro Carvalho Chehab 	int rc;
2420c0d06caSMauro Carvalho Chehab 	u8 msg[5] = { 0, 0, 0, 0, 0 };
2430c0d06caSMauro Carvalho Chehab 
24408cc05e4SMauro Carvalho Chehab 	/*
24508cc05e4SMauro Carvalho Chehab 	 * Read key toggle, brand, and key code
24608cc05e4SMauro Carvalho Chehab 	 * on registers 0x51-55
2470c0d06caSMauro Carvalho Chehab 	 */
2480c0d06caSMauro Carvalho Chehab 	rc = dev->em28xx_read_reg_req_len(dev, 0, EM2874_R51_IR,
2490c0d06caSMauro Carvalho Chehab 					  msg, sizeof(msg));
2500c0d06caSMauro Carvalho Chehab 	if (rc < 0)
2510c0d06caSMauro Carvalho Chehab 		return rc;
2520c0d06caSMauro Carvalho Chehab 
2530c0d06caSMauro Carvalho Chehab 	/* Infrared toggle (Reg 0x51[7]) */
2540c0d06caSMauro Carvalho Chehab 	poll_result->toggle_bit = (msg[0] >> 7);
2550c0d06caSMauro Carvalho Chehab 
2560c0d06caSMauro Carvalho Chehab 	/* Infrared read count (Reg 0x51[6:0] */
2570c0d06caSMauro Carvalho Chehab 	poll_result->read_count = (msg[0] & 0x7f);
2580c0d06caSMauro Carvalho Chehab 
259105e3687SMauro Carvalho Chehab 	/*
260105e3687SMauro Carvalho Chehab 	 * Remote Control Address (Reg 0x52)
261105e3687SMauro Carvalho Chehab 	 * Remote Control Data (Reg 0x53-0x55)
262105e3687SMauro Carvalho Chehab 	 */
2636d741bfeSSean Young 	switch (ir->rc_proto) {
2646d741bfeSSean Young 	case RC_PROTO_BIT_RC5:
2656d741bfeSSean Young 		poll_result->protocol = RC_PROTO_RC5;
266120703f9SDavid Härdeman 		poll_result->scancode = RC_SCANCODE_RC5(msg[1], msg[2]);
267105e3687SMauro Carvalho Chehab 		break;
268120703f9SDavid Härdeman 
2696d741bfeSSean Young 	case RC_PROTO_BIT_NEC:
2706bd914bcSSean Young 		poll_result->scancode = ir_nec_bytes_to_scancode(msg[1], msg[2], msg[3], msg[4],
2716bd914bcSSean Young 								 &poll_result->protocol);
272105e3687SMauro Carvalho Chehab 		break;
273120703f9SDavid Härdeman 
2746d741bfeSSean Young 	case RC_PROTO_BIT_RC6_0:
2756d741bfeSSean Young 		poll_result->protocol = RC_PROTO_RC6_0;
276120703f9SDavid Härdeman 		poll_result->scancode = RC_SCANCODE_RC6_0(msg[1], msg[2]);
2770dae8839SMauro Carvalho Chehab 		break;
278120703f9SDavid Härdeman 
279105e3687SMauro Carvalho Chehab 	default:
2806d741bfeSSean Young 		poll_result->protocol = RC_PROTO_UNKNOWN;
281105e3687SMauro Carvalho Chehab 		poll_result->scancode = (msg[1] << 24) | (msg[2] << 16) |
282105e3687SMauro Carvalho Chehab 					(msg[3] << 8)  | msg[4];
283105e3687SMauro Carvalho Chehab 		break;
284105e3687SMauro Carvalho Chehab 	}
2850c0d06caSMauro Carvalho Chehab 
2860c0d06caSMauro Carvalho Chehab 	return 0;
2870c0d06caSMauro Carvalho Chehab }
2880c0d06caSMauro Carvalho Chehab 
28908cc05e4SMauro Carvalho Chehab /*
29008cc05e4SMauro Carvalho Chehab  * Polling code for em28xx
29108cc05e4SMauro Carvalho Chehab  */
2920c0d06caSMauro Carvalho Chehab 
em28xx_i2c_ir_handle_key(struct em28xx_IR * ir)293768da3dbSFrank Schaefer static int em28xx_i2c_ir_handle_key(struct em28xx_IR *ir)
294768da3dbSFrank Schaefer {
295120703f9SDavid Härdeman 	static u32 scancode;
2966d741bfeSSean Young 	enum rc_proto protocol;
297768da3dbSFrank Schaefer 	int rc;
298768da3dbSFrank Schaefer 
29978e719a5SFrank Schaefer 	rc = ir->get_key_i2c(ir->i2c_client, &protocol, &scancode);
300768da3dbSFrank Schaefer 	if (rc < 0) {
301768da3dbSFrank Schaefer 		dprintk("ir->get_key_i2c() failed: %d\n", rc);
302768da3dbSFrank Schaefer 		return rc;
303768da3dbSFrank Schaefer 	}
304768da3dbSFrank Schaefer 
305768da3dbSFrank Schaefer 	if (rc) {
306120703f9SDavid Härdeman 		dprintk("%s: proto = 0x%04x, scancode = 0x%04x\n",
307120703f9SDavid Härdeman 			__func__, protocol, scancode);
308120703f9SDavid Härdeman 		rc_keydown(ir->rc, protocol, scancode, 0);
309768da3dbSFrank Schaefer 	}
310768da3dbSFrank Schaefer 	return 0;
311768da3dbSFrank Schaefer }
312768da3dbSFrank Schaefer 
em28xx_ir_handle_key(struct em28xx_IR * ir)3130c0d06caSMauro Carvalho Chehab static void em28xx_ir_handle_key(struct em28xx_IR *ir)
3140c0d06caSMauro Carvalho Chehab {
3150c0d06caSMauro Carvalho Chehab 	int result;
3160c0d06caSMauro Carvalho Chehab 	struct em28xx_ir_poll_result poll_result;
3170c0d06caSMauro Carvalho Chehab 
3180c0d06caSMauro Carvalho Chehab 	/* read the registers containing the IR status */
3190c0d06caSMauro Carvalho Chehab 	result = ir->get_key(ir, &poll_result);
3200c0d06caSMauro Carvalho Chehab 	if (unlikely(result < 0)) {
321768da3dbSFrank Schaefer 		dprintk("ir->get_key() failed: %d\n", result);
3220c0d06caSMauro Carvalho Chehab 		return;
3230c0d06caSMauro Carvalho Chehab 	}
3240c0d06caSMauro Carvalho Chehab 
3250c0d06caSMauro Carvalho Chehab 	if (unlikely(poll_result.read_count != ir->last_readcount)) {
326105e3687SMauro Carvalho Chehab 		dprintk("%s: toggle: %d, count: %d, key 0x%04x\n", __func__,
3270c0d06caSMauro Carvalho Chehab 			poll_result.toggle_bit, poll_result.read_count,
328105e3687SMauro Carvalho Chehab 			poll_result.scancode);
3290c0d06caSMauro Carvalho Chehab 		if (ir->full_code)
3300c0d06caSMauro Carvalho Chehab 			rc_keydown(ir->rc,
331120703f9SDavid Härdeman 				   poll_result.protocol,
332105e3687SMauro Carvalho Chehab 				   poll_result.scancode,
3330c0d06caSMauro Carvalho Chehab 				   poll_result.toggle_bit);
3340c0d06caSMauro Carvalho Chehab 		else
3350c0d06caSMauro Carvalho Chehab 			rc_keydown(ir->rc,
3366d741bfeSSean Young 				   RC_PROTO_UNKNOWN,
337105e3687SMauro Carvalho Chehab 				   poll_result.scancode & 0xff,
3380c0d06caSMauro Carvalho Chehab 				   poll_result.toggle_bit);
3390c0d06caSMauro Carvalho Chehab 
3400c0d06caSMauro Carvalho Chehab 		if (ir->dev->chip_id == CHIP_ID_EM2874 ||
3410c0d06caSMauro Carvalho Chehab 		    ir->dev->chip_id == CHIP_ID_EM2884)
34208cc05e4SMauro Carvalho Chehab 			/*
34308cc05e4SMauro Carvalho Chehab 			 * The em2874 clears the readcount field every time the
34408cc05e4SMauro Carvalho Chehab 			 * register is read.  The em2860/2880 datasheet says
34508cc05e4SMauro Carvalho Chehab 			 * that it is supposed to clear the readcount, but it
34608cc05e4SMauro Carvalho Chehab 			 * doesn't. So with the em2874, we are looking for a
34708cc05e4SMauro Carvalho Chehab 			 * non-zero read count as opposed to a readcount
34808cc05e4SMauro Carvalho Chehab 			 * that is incrementing
34908cc05e4SMauro Carvalho Chehab 			 */
3500c0d06caSMauro Carvalho Chehab 			ir->last_readcount = 0;
3510c0d06caSMauro Carvalho Chehab 		else
3520c0d06caSMauro Carvalho Chehab 			ir->last_readcount = poll_result.read_count;
3530c0d06caSMauro Carvalho Chehab 	}
3540c0d06caSMauro Carvalho Chehab }
3550c0d06caSMauro Carvalho Chehab 
em28xx_ir_work(struct work_struct * work)3560c0d06caSMauro Carvalho Chehab static void em28xx_ir_work(struct work_struct *work)
3570c0d06caSMauro Carvalho Chehab {
3580c0d06caSMauro Carvalho Chehab 	struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work.work);
3590c0d06caSMauro Carvalho Chehab 
36078e719a5SFrank Schaefer 	if (ir->i2c_client) /* external i2c device */
3619b4539beSFrank Schaefer 		em28xx_i2c_ir_handle_key(ir);
3629b4539beSFrank Schaefer 	else /* internal device */
3630c0d06caSMauro Carvalho Chehab 		em28xx_ir_handle_key(ir);
3640c0d06caSMauro Carvalho Chehab 	schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
3650c0d06caSMauro Carvalho Chehab }
3660c0d06caSMauro Carvalho Chehab 
em28xx_ir_start(struct rc_dev * rc)3670c0d06caSMauro Carvalho Chehab static int em28xx_ir_start(struct rc_dev *rc)
3680c0d06caSMauro Carvalho Chehab {
3690c0d06caSMauro Carvalho Chehab 	struct em28xx_IR *ir = rc->priv;
3700c0d06caSMauro Carvalho Chehab 
3710c0d06caSMauro Carvalho Chehab 	INIT_DELAYED_WORK(&ir->work, em28xx_ir_work);
3720c0d06caSMauro Carvalho Chehab 	schedule_delayed_work(&ir->work, 0);
3730c0d06caSMauro Carvalho Chehab 
3740c0d06caSMauro Carvalho Chehab 	return 0;
3750c0d06caSMauro Carvalho Chehab }
3760c0d06caSMauro Carvalho Chehab 
em28xx_ir_stop(struct rc_dev * rc)3770c0d06caSMauro Carvalho Chehab static void em28xx_ir_stop(struct rc_dev *rc)
3780c0d06caSMauro Carvalho Chehab {
3790c0d06caSMauro Carvalho Chehab 	struct em28xx_IR *ir = rc->priv;
3800c0d06caSMauro Carvalho Chehab 
3810c0d06caSMauro Carvalho Chehab 	cancel_delayed_work_sync(&ir->work);
3820c0d06caSMauro Carvalho Chehab }
3830c0d06caSMauro Carvalho Chehab 
em2860_ir_change_protocol(struct rc_dev * rc_dev,u64 * rc_proto)3846d741bfeSSean Young static int em2860_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_proto)
3850c0d06caSMauro Carvalho Chehab {
3860dae8839SMauro Carvalho Chehab 	struct em28xx_IR *ir = rc_dev->priv;
3870dae8839SMauro Carvalho Chehab 	struct em28xx *dev = ir->dev;
3880dae8839SMauro Carvalho Chehab 
3890dae8839SMauro Carvalho Chehab 	/* Adjust xclk based on IR table for RC5/NEC tables */
3906d741bfeSSean Young 	if (*rc_proto & RC_PROTO_BIT_RC5) {
3910dae8839SMauro Carvalho Chehab 		dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE;
3920dae8839SMauro Carvalho Chehab 		ir->full_code = 1;
3936d741bfeSSean Young 		*rc_proto = RC_PROTO_BIT_RC5;
3946d741bfeSSean Young 	} else if (*rc_proto & RC_PROTO_BIT_NEC) {
3950dae8839SMauro Carvalho Chehab 		dev->board.xclk &= ~EM28XX_XCLK_IR_RC5_MODE;
3960dae8839SMauro Carvalho Chehab 		ir->full_code = 1;
3976d741bfeSSean Young 		*rc_proto = RC_PROTO_BIT_NEC;
3986d741bfeSSean Young 	} else if (*rc_proto & RC_PROTO_BIT_UNKNOWN) {
3996d741bfeSSean Young 		*rc_proto = RC_PROTO_BIT_UNKNOWN;
4000dae8839SMauro Carvalho Chehab 	} else {
4016d741bfeSSean Young 		*rc_proto = ir->rc_proto;
4020dae8839SMauro Carvalho Chehab 		return -EINVAL;
4030dae8839SMauro Carvalho Chehab 	}
4040dae8839SMauro Carvalho Chehab 	em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk,
4050dae8839SMauro Carvalho Chehab 			      EM28XX_XCLK_IR_RC5_MODE);
4060dae8839SMauro Carvalho Chehab 
4076d741bfeSSean Young 	ir->rc_proto = *rc_proto;
4080dae8839SMauro Carvalho Chehab 
4090dae8839SMauro Carvalho Chehab 	return 0;
4100dae8839SMauro Carvalho Chehab }
4110dae8839SMauro Carvalho Chehab 
em2874_ir_change_protocol(struct rc_dev * rc_dev,u64 * rc_proto)4126d741bfeSSean Young static int em2874_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_proto)
4130dae8839SMauro Carvalho Chehab {
4140c0d06caSMauro Carvalho Chehab 	struct em28xx_IR *ir = rc_dev->priv;
4150c0d06caSMauro Carvalho Chehab 	struct em28xx *dev = ir->dev;
4160c0d06caSMauro Carvalho Chehab 	u8 ir_config = EM2874_IR_RC5;
4170c0d06caSMauro Carvalho Chehab 
4180dae8839SMauro Carvalho Chehab 	/* Adjust xclk and set type based on IR table for RC5/NEC/RC6 tables */
4196d741bfeSSean Young 	if (*rc_proto & RC_PROTO_BIT_RC5) {
4200c0d06caSMauro Carvalho Chehab 		dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE;
4210c0d06caSMauro Carvalho Chehab 		ir->full_code = 1;
4226d741bfeSSean Young 		*rc_proto = RC_PROTO_BIT_RC5;
4236d741bfeSSean Young 	} else if (*rc_proto & RC_PROTO_BIT_NEC) {
4240c0d06caSMauro Carvalho Chehab 		dev->board.xclk &= ~EM28XX_XCLK_IR_RC5_MODE;
425105e3687SMauro Carvalho Chehab 		ir_config = EM2874_IR_NEC | EM2874_IR_NEC_NO_PARITY;
4260c0d06caSMauro Carvalho Chehab 		ir->full_code = 1;
4276d741bfeSSean Young 		*rc_proto = RC_PROTO_BIT_NEC;
4286d741bfeSSean Young 	} else if (*rc_proto & RC_PROTO_BIT_RC6_0) {
4290dae8839SMauro Carvalho Chehab 		dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE;
4300dae8839SMauro Carvalho Chehab 		ir_config = EM2874_IR_RC6_MODE_0;
4310dae8839SMauro Carvalho Chehab 		ir->full_code = 1;
4326d741bfeSSean Young 		*rc_proto = RC_PROTO_BIT_RC6_0;
4336d741bfeSSean Young 	} else if (*rc_proto & RC_PROTO_BIT_UNKNOWN) {
4346d741bfeSSean Young 		*rc_proto = RC_PROTO_BIT_UNKNOWN;
4350dae8839SMauro Carvalho Chehab 	} else {
4366d741bfeSSean Young 		*rc_proto = ir->rc_proto;
4370dae8839SMauro Carvalho Chehab 		return -EINVAL;
4380dae8839SMauro Carvalho Chehab 	}
4390dae8839SMauro Carvalho Chehab 	em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1);
4400c0d06caSMauro Carvalho Chehab 	em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk,
4410c0d06caSMauro Carvalho Chehab 			      EM28XX_XCLK_IR_RC5_MODE);
4420c0d06caSMauro Carvalho Chehab 
4436d741bfeSSean Young 	ir->rc_proto = *rc_proto;
4440dae8839SMauro Carvalho Chehab 
4450dae8839SMauro Carvalho Chehab 	return 0;
4460dae8839SMauro Carvalho Chehab }
4476d741bfeSSean Young 
em28xx_ir_change_protocol(struct rc_dev * rc_dev,u64 * rc_proto)4486d741bfeSSean Young static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_proto)
4490dae8839SMauro Carvalho Chehab {
4500dae8839SMauro Carvalho Chehab 	struct em28xx_IR *ir = rc_dev->priv;
4510dae8839SMauro Carvalho Chehab 	struct em28xx *dev = ir->dev;
4520dae8839SMauro Carvalho Chehab 
4530c0d06caSMauro Carvalho Chehab 	/* Setup the proper handler based on the chip */
4540c0d06caSMauro Carvalho Chehab 	switch (dev->chip_id) {
4550c0d06caSMauro Carvalho Chehab 	case CHIP_ID_EM2860:
4560c0d06caSMauro Carvalho Chehab 	case CHIP_ID_EM2883:
4576d741bfeSSean Young 		return em2860_ir_change_protocol(rc_dev, rc_proto);
4580c0d06caSMauro Carvalho Chehab 	case CHIP_ID_EM2884:
4590c0d06caSMauro Carvalho Chehab 	case CHIP_ID_EM2874:
4600c0d06caSMauro Carvalho Chehab 	case CHIP_ID_EM28174:
4619f1d0bdaSAntti Palosaari 	case CHIP_ID_EM28178:
4626d741bfeSSean Young 		return em2874_ir_change_protocol(rc_dev, rc_proto);
4630c0d06caSMauro Carvalho Chehab 	default:
46429b05e22SMauro Carvalho Chehab 		dev_err(&ir->dev->intf->dev,
465ce8591ffSMauro Carvalho Chehab 			"Unrecognized em28xx chip id 0x%02x: IR not supported\n",
4660c0d06caSMauro Carvalho Chehab 			dev->chip_id);
4670dae8839SMauro Carvalho Chehab 		return -EINVAL;
4680c0d06caSMauro Carvalho Chehab 	}
4690c0d06caSMauro Carvalho Chehab }
4700c0d06caSMauro Carvalho Chehab 
em28xx_probe_i2c_ir(struct em28xx * dev)4711d968cdaSFrank Schaefer static int em28xx_probe_i2c_ir(struct em28xx *dev)
4720c0d06caSMauro Carvalho Chehab {
473768da3dbSFrank Schaefer 	int i = 0;
47408cc05e4SMauro Carvalho Chehab 	/*
47508cc05e4SMauro Carvalho Chehab 	 * Leadtek winfast tv USBII deluxe can find a non working IR-device
47608cc05e4SMauro Carvalho Chehab 	 * at address 0x18, so if that address is needed for another board in
47708cc05e4SMauro Carvalho Chehab 	 * the future, please put it after 0x1f.
47808cc05e4SMauro Carvalho Chehab 	 */
4796fe59b7eSColin Ian King 	static const unsigned short addr_list[] = {
4800c0d06caSMauro Carvalho Chehab 		 0x1f, 0x30, 0x47, I2C_CLIENT_END
4810c0d06caSMauro Carvalho Chehab 	};
4820c0d06caSMauro Carvalho Chehab 
483768da3dbSFrank Schaefer 	while (addr_list[i] != I2C_CLIENT_END) {
48408cc05e4SMauro Carvalho Chehab 		if (i2c_probe_func_quick_read(&dev->i2c_adap[dev->def_i2c_bus],
48508cc05e4SMauro Carvalho Chehab 					      addr_list[i]) == 1)
4861d968cdaSFrank Schaefer 			return addr_list[i];
487768da3dbSFrank Schaefer 		i++;
488768da3dbSFrank Schaefer 	}
4890c0d06caSMauro Carvalho Chehab 
4901d968cdaSFrank Schaefer 	return -ENODEV;
4910c0d06caSMauro Carvalho Chehab }
4920c0d06caSMauro Carvalho Chehab 
49308cc05e4SMauro Carvalho Chehab /*
49408cc05e4SMauro Carvalho Chehab  * Handle buttons
49508cc05e4SMauro Carvalho Chehab  */
4960c0d06caSMauro Carvalho Chehab 
em28xx_query_buttons(struct work_struct * work)497f5222609SFrank Schaefer static void em28xx_query_buttons(struct work_struct *work)
4980c0d06caSMauro Carvalho Chehab {
4990c0d06caSMauro Carvalho Chehab 	struct em28xx *dev =
500f5222609SFrank Schaefer 		container_of(work, struct em28xx, buttons_query_work.work);
501f5222609SFrank Schaefer 	u8 i, j;
502f5222609SFrank Schaefer 	int regval;
5037763481aSFrank Schaefer 	bool is_pressed, was_pressed;
5046063d077SFrank Schaefer 	const struct em28xx_led *led;
5050c0d06caSMauro Carvalho Chehab 
506f5222609SFrank Schaefer 	/* Poll and evaluate all addresses */
507f5222609SFrank Schaefer 	for (i = 0; i < dev->num_button_polling_addresses; i++) {
508f5222609SFrank Schaefer 		/* Read value from register */
509f5222609SFrank Schaefer 		regval = em28xx_read_reg(dev, dev->button_polling_addresses[i]);
510f5222609SFrank Schaefer 		if (regval < 0)
511f5222609SFrank Schaefer 			continue;
512f5222609SFrank Schaefer 		/* Check states of the buttons and act */
513f5222609SFrank Schaefer 		j = 0;
514f5222609SFrank Schaefer 		while (dev->board.buttons[j].role >= 0 &&
515f5222609SFrank Schaefer 		       dev->board.buttons[j].role < EM28XX_NUM_BUTTON_ROLES) {
51608cc05e4SMauro Carvalho Chehab 			const struct em28xx_button *button;
51708cc05e4SMauro Carvalho Chehab 
51808cc05e4SMauro Carvalho Chehab 			button = &dev->board.buttons[j];
5190108ae7fSMauro Carvalho Chehab 
520f5222609SFrank Schaefer 			/* Check if button uses the current address */
521f5222609SFrank Schaefer 			if (button->reg_r != dev->button_polling_addresses[i]) {
522f5222609SFrank Schaefer 				j++;
523f5222609SFrank Schaefer 				continue;
5240c0d06caSMauro Carvalho Chehab 			}
5257763481aSFrank Schaefer 			/* Determine if button is and was pressed last time */
5267763481aSFrank Schaefer 			is_pressed = regval & button->mask;
5277763481aSFrank Schaefer 			was_pressed = dev->button_polling_last_values[i]
5287763481aSFrank Schaefer 				       & button->mask;
5297763481aSFrank Schaefer 			if (button->inverted) {
5307763481aSFrank Schaefer 				is_pressed = !is_pressed;
5317763481aSFrank Schaefer 				was_pressed = !was_pressed;
5327763481aSFrank Schaefer 			}
5337763481aSFrank Schaefer 			/* Clear button state (if needed) */
5347763481aSFrank Schaefer 			if (is_pressed && button->reg_clearing)
5357763481aSFrank Schaefer 				em28xx_write_reg(dev, button->reg_clearing,
5367763481aSFrank Schaefer 						 (~regval & button->mask)
5377763481aSFrank Schaefer 						    | (regval & ~button->mask));
538f5222609SFrank Schaefer 			/* Handle button state */
5397763481aSFrank Schaefer 			if (!is_pressed || was_pressed) {
540f5222609SFrank Schaefer 				j++;
541f5222609SFrank Schaefer 				continue;
542f5222609SFrank Schaefer 			}
543f5222609SFrank Schaefer 			switch (button->role) {
544f5222609SFrank Schaefer 			case EM28XX_BUTTON_SNAPSHOT:
545f5222609SFrank Schaefer 				/* Emulate the keypress */
546f5222609SFrank Schaefer 				input_report_key(dev->sbutton_input_dev,
547f5222609SFrank Schaefer 						 EM28XX_SNAPSHOT_KEY, 1);
548f5222609SFrank Schaefer 				/* Unpress the key */
549f5222609SFrank Schaefer 				input_report_key(dev->sbutton_input_dev,
550f5222609SFrank Schaefer 						 EM28XX_SNAPSHOT_KEY, 0);
551f5222609SFrank Schaefer 				break;
5526063d077SFrank Schaefer 			case EM28XX_BUTTON_ILLUMINATION:
5536063d077SFrank Schaefer 				led = em28xx_find_led(dev,
5546063d077SFrank Schaefer 						      EM28XX_LED_ILLUMINATION);
5556063d077SFrank Schaefer 				/* Switch illumination LED on/off */
5566063d077SFrank Schaefer 				if (led)
5576063d077SFrank Schaefer 					em28xx_toggle_reg_bits(dev,
5586063d077SFrank Schaefer 							       led->gpio_reg,
5596063d077SFrank Schaefer 							       led->gpio_mask);
5606063d077SFrank Schaefer 				break;
561f5222609SFrank Schaefer 			default:
562f5222609SFrank Schaefer 				WARN_ONCE(1, "BUG: unhandled button role.");
563f5222609SFrank Schaefer 			}
564f5222609SFrank Schaefer 			/* Next button */
565f5222609SFrank Schaefer 			j++;
566f5222609SFrank Schaefer 		}
5677763481aSFrank Schaefer 		/* Save current value for comparison during the next polling */
5687763481aSFrank Schaefer 		dev->button_polling_last_values[i] = regval;
569f5222609SFrank Schaefer 	}
5700c0d06caSMauro Carvalho Chehab 	/* Schedule next poll */
571f5222609SFrank Schaefer 	schedule_delayed_work(&dev->buttons_query_work,
5720ff950a7SFrank Schaefer 			      msecs_to_jiffies(dev->button_polling_interval));
5730c0d06caSMauro Carvalho Chehab }
5740c0d06caSMauro Carvalho Chehab 
em28xx_register_snapshot_button(struct em28xx * dev)575f5222609SFrank Schaefer static int em28xx_register_snapshot_button(struct em28xx *dev)
5760c0d06caSMauro Carvalho Chehab {
577c6d48134SMauro Carvalho Chehab 	struct usb_device *udev = interface_to_usbdev(dev->intf);
5780c0d06caSMauro Carvalho Chehab 	struct input_dev *input_dev;
5790c0d06caSMauro Carvalho Chehab 	int err;
5800c0d06caSMauro Carvalho Chehab 
58129b05e22SMauro Carvalho Chehab 	dev_info(&dev->intf->dev, "Registering snapshot button...\n");
5820c0d06caSMauro Carvalho Chehab 	input_dev = input_allocate_device();
583da4a7339SJoe Perches 	if (!input_dev)
584f5222609SFrank Schaefer 		return -ENOMEM;
5850c0d06caSMauro Carvalho Chehab 
586c6d48134SMauro Carvalho Chehab 	usb_make_path(udev, dev->snapshot_button_path,
5870c0d06caSMauro Carvalho Chehab 		      sizeof(dev->snapshot_button_path));
5880c0d06caSMauro Carvalho Chehab 	strlcat(dev->snapshot_button_path, "/sbutton",
5890c0d06caSMauro Carvalho Chehab 		sizeof(dev->snapshot_button_path));
5900c0d06caSMauro Carvalho Chehab 
5910c0d06caSMauro Carvalho Chehab 	input_dev->name = "em28xx snapshot button";
5920c0d06caSMauro Carvalho Chehab 	input_dev->phys = dev->snapshot_button_path;
5930c0d06caSMauro Carvalho Chehab 	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
5940c0d06caSMauro Carvalho Chehab 	set_bit(EM28XX_SNAPSHOT_KEY, input_dev->keybit);
5950c0d06caSMauro Carvalho Chehab 	input_dev->keycodesize = 0;
5960c0d06caSMauro Carvalho Chehab 	input_dev->keycodemax = 0;
5970547858bSSean Young 	usb_to_input_id(udev, &input_dev->id);
59829b05e22SMauro Carvalho Chehab 	input_dev->dev.parent = &dev->intf->dev;
5990c0d06caSMauro Carvalho Chehab 
6000c0d06caSMauro Carvalho Chehab 	err = input_register_device(input_dev);
6010c0d06caSMauro Carvalho Chehab 	if (err) {
60229b05e22SMauro Carvalho Chehab 		dev_err(&dev->intf->dev, "input_register_device failed\n");
6030c0d06caSMauro Carvalho Chehab 		input_free_device(input_dev);
604f5222609SFrank Schaefer 		return err;
6050c0d06caSMauro Carvalho Chehab 	}
6060c0d06caSMauro Carvalho Chehab 
6070c0d06caSMauro Carvalho Chehab 	dev->sbutton_input_dev = input_dev;
608f5222609SFrank Schaefer 	return 0;
6090c0d06caSMauro Carvalho Chehab }
6100c0d06caSMauro Carvalho Chehab 
em28xx_init_buttons(struct em28xx * dev)611f5222609SFrank Schaefer static void em28xx_init_buttons(struct em28xx *dev)
6120c0d06caSMauro Carvalho Chehab {
613f5222609SFrank Schaefer 	u8  i = 0, j = 0;
6147e6c8c19SMauro Carvalho Chehab 	bool addr_new = false;
615f5222609SFrank Schaefer 
6160ff950a7SFrank Schaefer 	dev->button_polling_interval = EM28XX_BUTTONS_DEBOUNCED_QUERY_INTERVAL;
617f5222609SFrank Schaefer 	while (dev->board.buttons[i].role >= 0 &&
618f5222609SFrank Schaefer 	       dev->board.buttons[i].role < EM28XX_NUM_BUTTON_ROLES) {
6190108ae7fSMauro Carvalho Chehab 		const struct em28xx_button *button = &dev->board.buttons[i];
6200108ae7fSMauro Carvalho Chehab 
621f5222609SFrank Schaefer 		/* Check if polling address is already on the list */
6227e6c8c19SMauro Carvalho Chehab 		addr_new = true;
623f5222609SFrank Schaefer 		for (j = 0; j < dev->num_button_polling_addresses; j++) {
624f5222609SFrank Schaefer 			if (button->reg_r == dev->button_polling_addresses[j]) {
6257e6c8c19SMauro Carvalho Chehab 				addr_new = false;
626f5222609SFrank Schaefer 				break;
627f5222609SFrank Schaefer 			}
628f5222609SFrank Schaefer 		}
629f5222609SFrank Schaefer 		/* Check if max. number of polling addresses is exceeded */
630f5222609SFrank Schaefer 		if (addr_new && dev->num_button_polling_addresses
631f5222609SFrank Schaefer 					   >= EM28XX_NUM_BUTTON_ADDRESSES_MAX) {
632f5222609SFrank Schaefer 			WARN_ONCE(1, "BUG: maximum number of button polling addresses exceeded.");
6330ff950a7SFrank Schaefer 			goto next_button;
634f5222609SFrank Schaefer 		}
6356063d077SFrank Schaefer 		/* Button role specific checks and actions */
636f5222609SFrank Schaefer 		if (button->role == EM28XX_BUTTON_SNAPSHOT) {
6376063d077SFrank Schaefer 			/* Register input device */
638f5222609SFrank Schaefer 			if (em28xx_register_snapshot_button(dev) < 0)
6390ff950a7SFrank Schaefer 				goto next_button;
6406063d077SFrank Schaefer 		} else if (button->role == EM28XX_BUTTON_ILLUMINATION) {
6416063d077SFrank Schaefer 			/* Check sanity */
6426063d077SFrank Schaefer 			if (!em28xx_find_led(dev, EM28XX_LED_ILLUMINATION)) {
64329b05e22SMauro Carvalho Chehab 				dev_err(&dev->intf->dev,
644ce8591ffSMauro Carvalho Chehab 					"BUG: illumination button defined, but no illumination LED.\n");
6450ff950a7SFrank Schaefer 				goto next_button;
6466063d077SFrank Schaefer 			}
647f5222609SFrank Schaefer 		}
648f5222609SFrank Schaefer 		/* Add read address to list of polling addresses */
649f5222609SFrank Schaefer 		if (addr_new) {
650f5222609SFrank Schaefer 			unsigned int index = dev->num_button_polling_addresses;
65108cc05e4SMauro Carvalho Chehab 
652f5222609SFrank Schaefer 			dev->button_polling_addresses[index] = button->reg_r;
653f5222609SFrank Schaefer 			dev->num_button_polling_addresses++;
654f5222609SFrank Schaefer 		}
6550ff950a7SFrank Schaefer 		/* Reduce polling interval if necessary */
6560ff950a7SFrank Schaefer 		if (!button->reg_clearing)
6570ff950a7SFrank Schaefer 			dev->button_polling_interval =
6580ff950a7SFrank Schaefer 					 EM28XX_BUTTONS_VOLATILE_QUERY_INTERVAL;
6590ff950a7SFrank Schaefer next_button:
660f5222609SFrank Schaefer 		/* Next button */
661f5222609SFrank Schaefer 		i++;
662f5222609SFrank Schaefer 	}
663f5222609SFrank Schaefer 
664f5222609SFrank Schaefer 	/* Start polling */
665f5222609SFrank Schaefer 	if (dev->num_button_polling_addresses) {
6667763481aSFrank Schaefer 		memset(dev->button_polling_last_values, 0,
6677763481aSFrank Schaefer 		       EM28XX_NUM_BUTTON_ADDRESSES_MAX);
668f5222609SFrank Schaefer 		schedule_delayed_work(&dev->buttons_query_work,
6690ff950a7SFrank Schaefer 				      msecs_to_jiffies(dev->button_polling_interval));
670f5222609SFrank Schaefer 	}
671f5222609SFrank Schaefer }
672f5222609SFrank Schaefer 
em28xx_shutdown_buttons(struct em28xx * dev)673f5222609SFrank Schaefer static void em28xx_shutdown_buttons(struct em28xx *dev)
674f5222609SFrank Schaefer {
675f5222609SFrank Schaefer 	/* Cancel polling */
676f5222609SFrank Schaefer 	cancel_delayed_work_sync(&dev->buttons_query_work);
677f5222609SFrank Schaefer 	/* Clear polling addresses list */
678f5222609SFrank Schaefer 	dev->num_button_polling_addresses = 0;
679f5222609SFrank Schaefer 	/* Deregister input devices */
68008cc05e4SMauro Carvalho Chehab 	if (dev->sbutton_input_dev) {
68129b05e22SMauro Carvalho Chehab 		dev_info(&dev->intf->dev, "Deregistering snapshot button\n");
6820c0d06caSMauro Carvalho Chehab 		input_unregister_device(dev->sbutton_input_dev);
6830c0d06caSMauro Carvalho Chehab 		dev->sbutton_input_dev = NULL;
6840c0d06caSMauro Carvalho Chehab 	}
6850c0d06caSMauro Carvalho Chehab }
6860c0d06caSMauro Carvalho Chehab 
em28xx_ir_init(struct em28xx * dev)6870c0d06caSMauro Carvalho Chehab static int em28xx_ir_init(struct em28xx *dev)
6880c0d06caSMauro Carvalho Chehab {
689c6d48134SMauro Carvalho Chehab 	struct usb_device *udev = interface_to_usbdev(dev->intf);
6900c0d06caSMauro Carvalho Chehab 	struct em28xx_IR *ir;
6910c0d06caSMauro Carvalho Chehab 	struct rc_dev *rc;
6920c0d06caSMauro Carvalho Chehab 	int err = -ENOMEM;
6936d741bfeSSean Young 	u64 rc_proto;
6941d968cdaSFrank Schaefer 	u16 i2c_rc_dev_addr = 0;
6950c0d06caSMauro Carvalho Chehab 
696822b8deaSMauro Carvalho Chehab 	if (dev->is_audio_only) {
697822b8deaSMauro Carvalho Chehab 		/* Shouldn't initialize IR for this interface */
698822b8deaSMauro Carvalho Chehab 		return 0;
699822b8deaSMauro Carvalho Chehab 	}
700822b8deaSMauro Carvalho Chehab 
70147677e51SMauro Carvalho Chehab 	kref_get(&dev->ref);
702bbfebeeaSRussell King 	INIT_DELAYED_WORK(&dev->buttons_query_work, em28xx_query_buttons);
70347677e51SMauro Carvalho Chehab 
704f5222609SFrank Schaefer 	if (dev->board.buttons)
705f5222609SFrank Schaefer 		em28xx_init_buttons(dev);
7068303dc99SMauro Carvalho Chehab 
7078303dc99SMauro Carvalho Chehab 	if (dev->board.has_ir_i2c) {
7081d968cdaSFrank Schaefer 		i2c_rc_dev_addr = em28xx_probe_i2c_ir(dev);
7091d968cdaSFrank Schaefer 		if (!i2c_rc_dev_addr) {
710768da3dbSFrank Schaefer 			dev->board.has_ir_i2c = 0;
71129b05e22SMauro Carvalho Chehab 			dev_warn(&dev->intf->dev,
712ce8591ffSMauro Carvalho Chehab 				 "No i2c IR remote control device found.\n");
713*ac568863SIgor Matheus Andrade Torrente 			err = -ENODEV;
714*ac568863SIgor Matheus Andrade Torrente 			goto ref_put;
715768da3dbSFrank Schaefer 		}
7168303dc99SMauro Carvalho Chehab 	}
7178303dc99SMauro Carvalho Chehab 
71808cc05e4SMauro Carvalho Chehab 	if (!dev->board.ir_codes && !dev->board.has_ir_i2c) {
7190c0d06caSMauro Carvalho Chehab 		/* No remote control support */
72029b05e22SMauro Carvalho Chehab 		dev_warn(&dev->intf->dev,
721ce8591ffSMauro Carvalho Chehab 			 "Remote control support is not available for this card.\n");
7220c0d06caSMauro Carvalho Chehab 		return 0;
7230c0d06caSMauro Carvalho Chehab 	}
7240c0d06caSMauro Carvalho Chehab 
72529b05e22SMauro Carvalho Chehab 	dev_info(&dev->intf->dev, "Registering input extension\n");
7269634614fSMauro Carvalho Chehab 
7270c0d06caSMauro Carvalho Chehab 	ir = kzalloc(sizeof(*ir), GFP_KERNEL);
72834d7c248SDan Carpenter 	if (!ir)
729*ac568863SIgor Matheus Andrade Torrente 		goto ref_put;
7300f7499fdSAndi Shyti 	rc = rc_allocate_device(RC_DRIVER_SCANCODE);
73134d7c248SDan Carpenter 	if (!rc)
7322f5741aaSFrank Schaefer 		goto error;
7330c0d06caSMauro Carvalho Chehab 
7340c0d06caSMauro Carvalho Chehab 	/* record handles to ourself */
7350c0d06caSMauro Carvalho Chehab 	ir->dev = dev;
7360c0d06caSMauro Carvalho Chehab 	dev->ir = ir;
7370c0d06caSMauro Carvalho Chehab 	ir->rc = rc;
7380c0d06caSMauro Carvalho Chehab 
7390c0d06caSMauro Carvalho Chehab 	rc->priv = ir;
7400c0d06caSMauro Carvalho Chehab 	rc->open = em28xx_ir_start;
7410c0d06caSMauro Carvalho Chehab 	rc->close = em28xx_ir_stop;
7420c0d06caSMauro Carvalho Chehab 
743768da3dbSFrank Schaefer 	if (dev->board.has_ir_i2c) {	/* external i2c device */
744768da3dbSFrank Schaefer 		switch (dev->model) {
745768da3dbSFrank Schaefer 		case EM2800_BOARD_TERRATEC_CINERGY_200:
746768da3dbSFrank Schaefer 		case EM2820_BOARD_TERRATEC_CINERGY_250:
747768da3dbSFrank Schaefer 			rc->map_name = RC_MAP_EM_TERRATEC;
748768da3dbSFrank Schaefer 			ir->get_key_i2c = em28xx_get_key_terratec;
749768da3dbSFrank Schaefer 			break;
750768da3dbSFrank Schaefer 		case EM2820_BOARD_PINNACLE_USB_2:
751768da3dbSFrank Schaefer 			rc->map_name = RC_MAP_PINNACLE_GREY;
752768da3dbSFrank Schaefer 			ir->get_key_i2c = em28xx_get_key_pinnacle_usb_grey;
753768da3dbSFrank Schaefer 			break;
754768da3dbSFrank Schaefer 		case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2:
755768da3dbSFrank Schaefer 			rc->map_name = RC_MAP_HAUPPAUGE;
756768da3dbSFrank Schaefer 			ir->get_key_i2c = em28xx_get_key_em_haup;
7576d741bfeSSean Young 			rc->allowed_protocols = RC_PROTO_BIT_RC5;
758768da3dbSFrank Schaefer 			break;
759768da3dbSFrank Schaefer 		case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE:
760768da3dbSFrank Schaefer 			rc->map_name = RC_MAP_WINFAST_USBII_DELUXE;
761768da3dbSFrank Schaefer 			ir->get_key_i2c = em28xx_get_key_winfast_usbii_deluxe;
762768da3dbSFrank Schaefer 			break;
763768da3dbSFrank Schaefer 		default:
764768da3dbSFrank Schaefer 			err = -ENODEV;
765768da3dbSFrank Schaefer 			goto error;
766768da3dbSFrank Schaefer 		}
767768da3dbSFrank Schaefer 
76808cc05e4SMauro Carvalho Chehab 		ir->i2c_client = kzalloc(sizeof(*ir->i2c_client), GFP_KERNEL);
76978e719a5SFrank Schaefer 		if (!ir->i2c_client)
77078e719a5SFrank Schaefer 			goto error;
77178e719a5SFrank Schaefer 		ir->i2c_client->adapter = &ir->dev->i2c_adap[dev->def_i2c_bus];
77278e719a5SFrank Schaefer 		ir->i2c_client->addr = i2c_rc_dev_addr;
77378e719a5SFrank Schaefer 		ir->i2c_client->flags = 0;
77478e719a5SFrank Schaefer 		/* NOTE: all other fields of i2c_client are unused */
775768da3dbSFrank Schaefer 	} else {	/* internal device */
7760dae8839SMauro Carvalho Chehab 		switch (dev->chip_id) {
7770dae8839SMauro Carvalho Chehab 		case CHIP_ID_EM2860:
7780dae8839SMauro Carvalho Chehab 		case CHIP_ID_EM2883:
7796d741bfeSSean Young 			rc->allowed_protocols = RC_PROTO_BIT_RC5 |
7806d741bfeSSean Young 						RC_PROTO_BIT_NEC;
7816ea887efSFrank Schaefer 			ir->get_key = default_polling_getkey;
7820dae8839SMauro Carvalho Chehab 			break;
7830dae8839SMauro Carvalho Chehab 		case CHIP_ID_EM2884:
7840dae8839SMauro Carvalho Chehab 		case CHIP_ID_EM2874:
7850dae8839SMauro Carvalho Chehab 		case CHIP_ID_EM28174:
7869f1d0bdaSAntti Palosaari 		case CHIP_ID_EM28178:
7876ea887efSFrank Schaefer 			ir->get_key = em2874_polling_getkey;
7886d741bfeSSean Young 			rc->allowed_protocols = RC_PROTO_BIT_RC5 |
7896d741bfeSSean Young 				RC_PROTO_BIT_NEC | RC_PROTO_BIT_NECX |
7906d741bfeSSean Young 				RC_PROTO_BIT_NEC32 | RC_PROTO_BIT_RC6_0;
7910dae8839SMauro Carvalho Chehab 			break;
7920dae8839SMauro Carvalho Chehab 		default:
7930dae8839SMauro Carvalho Chehab 			err = -ENODEV;
7942f5741aaSFrank Schaefer 			goto error;
7950dae8839SMauro Carvalho Chehab 		}
7960dae8839SMauro Carvalho Chehab 
797768da3dbSFrank Schaefer 		rc->change_protocol = em28xx_ir_change_protocol;
798768da3dbSFrank Schaefer 		rc->map_name = dev->board.ir_codes;
799768da3dbSFrank Schaefer 
8000c0d06caSMauro Carvalho Chehab 		/* By default, keep protocol field untouched */
8016d741bfeSSean Young 		rc_proto = RC_PROTO_BIT_UNKNOWN;
8026d741bfeSSean Young 		err = em28xx_ir_change_protocol(rc, &rc_proto);
8030c0d06caSMauro Carvalho Chehab 		if (err)
8042f5741aaSFrank Schaefer 			goto error;
805768da3dbSFrank Schaefer 	}
8060c0d06caSMauro Carvalho Chehab 
8070c0d06caSMauro Carvalho Chehab 	/* This is how often we ask the chip for IR information */
8080c0d06caSMauro Carvalho Chehab 	ir->polling = 100; /* ms */
8090c0d06caSMauro Carvalho Chehab 
810c6d48134SMauro Carvalho Chehab 	usb_make_path(udev, ir->phys, sizeof(ir->phys));
8110c0d06caSMauro Carvalho Chehab 	strlcat(ir->phys, "/input0", sizeof(ir->phys));
8120c0d06caSMauro Carvalho Chehab 
8135c1c6953SSean Young 	rc->device_name = em28xx_boards[dev->model].name;
8140c0d06caSMauro Carvalho Chehab 	rc->input_phys = ir->phys;
8150547858bSSean Young 	usb_to_input_id(udev, &rc->input_id);
81629b05e22SMauro Carvalho Chehab 	rc->dev.parent = &dev->intf->dev;
8170c0d06caSMauro Carvalho Chehab 	rc->driver_name = MODULE_NAME;
8180c0d06caSMauro Carvalho Chehab 
8190c0d06caSMauro Carvalho Chehab 	/* all done */
8200c0d06caSMauro Carvalho Chehab 	err = rc_register_device(rc);
8210c0d06caSMauro Carvalho Chehab 	if (err)
8222f5741aaSFrank Schaefer 		goto error;
8230c0d06caSMauro Carvalho Chehab 
8241250a85bSMauro Carvalho Chehab 	dev_info(&dev->intf->dev, "Input extension successfully initialized\n");
8259634614fSMauro Carvalho Chehab 
8260c0d06caSMauro Carvalho Chehab 	return 0;
8270c0d06caSMauro Carvalho Chehab 
8282f5741aaSFrank Schaefer error:
82978e719a5SFrank Schaefer 	kfree(ir->i2c_client);
8300c0d06caSMauro Carvalho Chehab 	dev->ir = NULL;
8310c0d06caSMauro Carvalho Chehab 	rc_free_device(rc);
8320c0d06caSMauro Carvalho Chehab 	kfree(ir);
833*ac568863SIgor Matheus Andrade Torrente ref_put:
834*ac568863SIgor Matheus Andrade Torrente 	em28xx_shutdown_buttons(dev);
8350c0d06caSMauro Carvalho Chehab 	return err;
8360c0d06caSMauro Carvalho Chehab }
8370c0d06caSMauro Carvalho Chehab 
em28xx_ir_fini(struct em28xx * dev)8380c0d06caSMauro Carvalho Chehab static int em28xx_ir_fini(struct em28xx *dev)
8390c0d06caSMauro Carvalho Chehab {
8400c0d06caSMauro Carvalho Chehab 	struct em28xx_IR *ir = dev->ir;
8410c0d06caSMauro Carvalho Chehab 
842822b8deaSMauro Carvalho Chehab 	if (dev->is_audio_only) {
843822b8deaSMauro Carvalho Chehab 		/* Shouldn't initialize IR for this interface */
844822b8deaSMauro Carvalho Chehab 		return 0;
845822b8deaSMauro Carvalho Chehab 	}
846822b8deaSMauro Carvalho Chehab 
84729b05e22SMauro Carvalho Chehab 	dev_info(&dev->intf->dev, "Closing input extension\n");
848aa929ad7SMauro Carvalho Chehab 
849f5222609SFrank Schaefer 	em28xx_shutdown_buttons(dev);
8500c0d06caSMauro Carvalho Chehab 
8510c0d06caSMauro Carvalho Chehab 	/* skip detach on non attached boards */
8520c0d06caSMauro Carvalho Chehab 	if (!ir)
85347677e51SMauro Carvalho Chehab 		goto ref_put;
8540c0d06caSMauro Carvalho Chehab 
8550c0d06caSMauro Carvalho Chehab 	rc_unregister_device(ir->rc);
8560c0d06caSMauro Carvalho Chehab 
85778e719a5SFrank Schaefer 	kfree(ir->i2c_client);
85878e719a5SFrank Schaefer 
8590c0d06caSMauro Carvalho Chehab 	/* done */
8600c0d06caSMauro Carvalho Chehab 	kfree(ir);
8610c0d06caSMauro Carvalho Chehab 	dev->ir = NULL;
86247677e51SMauro Carvalho Chehab 
86347677e51SMauro Carvalho Chehab ref_put:
86447677e51SMauro Carvalho Chehab 	kref_put(&dev->ref, em28xx_free_device);
86547677e51SMauro Carvalho Chehab 
8660c0d06caSMauro Carvalho Chehab 	return 0;
8670c0d06caSMauro Carvalho Chehab }
8680c0d06caSMauro Carvalho Chehab 
em28xx_ir_suspend(struct em28xx * dev)8695025076aSShuah Khan static int em28xx_ir_suspend(struct em28xx *dev)
8705025076aSShuah Khan {
8715025076aSShuah Khan 	struct em28xx_IR *ir = dev->ir;
8725025076aSShuah Khan 
8735025076aSShuah Khan 	if (dev->is_audio_only)
8745025076aSShuah Khan 		return 0;
8755025076aSShuah Khan 
87629b05e22SMauro Carvalho Chehab 	dev_info(&dev->intf->dev, "Suspending input extension\n");
8775025076aSShuah Khan 	if (ir)
8785025076aSShuah Khan 		cancel_delayed_work_sync(&ir->work);
8795025076aSShuah Khan 	cancel_delayed_work_sync(&dev->buttons_query_work);
88008cc05e4SMauro Carvalho Chehab 	/*
88108cc05e4SMauro Carvalho Chehab 	 * is canceling delayed work sufficient or does the rc event
88208cc05e4SMauro Carvalho Chehab 	 * kthread needs stopping? kthread is stopped in
88308cc05e4SMauro Carvalho Chehab 	 * ir_raw_event_unregister()
88408cc05e4SMauro Carvalho Chehab 	 */
8855025076aSShuah Khan 	return 0;
8865025076aSShuah Khan }
8875025076aSShuah Khan 
em28xx_ir_resume(struct em28xx * dev)8885025076aSShuah Khan static int em28xx_ir_resume(struct em28xx *dev)
8895025076aSShuah Khan {
8905025076aSShuah Khan 	struct em28xx_IR *ir = dev->ir;
8915025076aSShuah Khan 
8925025076aSShuah Khan 	if (dev->is_audio_only)
8935025076aSShuah Khan 		return 0;
8945025076aSShuah Khan 
89529b05e22SMauro Carvalho Chehab 	dev_info(&dev->intf->dev, "Resuming input extension\n");
89608cc05e4SMauro Carvalho Chehab 	/*
89708cc05e4SMauro Carvalho Chehab 	 * if suspend calls ir_raw_event_unregister(), the should call
89808cc05e4SMauro Carvalho Chehab 	 * ir_raw_event_register()
89908cc05e4SMauro Carvalho Chehab 	 */
9005025076aSShuah Khan 	if (ir)
9015025076aSShuah Khan 		schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
9025025076aSShuah Khan 	if (dev->num_button_polling_addresses)
9035025076aSShuah Khan 		schedule_delayed_work(&dev->buttons_query_work,
9045025076aSShuah Khan 				      msecs_to_jiffies(dev->button_polling_interval));
9055025076aSShuah Khan 	return 0;
9065025076aSShuah Khan }
9075025076aSShuah Khan 
9080c0d06caSMauro Carvalho Chehab static struct em28xx_ops rc_ops = {
9090c0d06caSMauro Carvalho Chehab 	.id   = EM28XX_RC,
9100c0d06caSMauro Carvalho Chehab 	.name = "Em28xx Input Extension",
9110c0d06caSMauro Carvalho Chehab 	.init = em28xx_ir_init,
9120c0d06caSMauro Carvalho Chehab 	.fini = em28xx_ir_fini,
9135025076aSShuah Khan 	.suspend = em28xx_ir_suspend,
9145025076aSShuah Khan 	.resume = em28xx_ir_resume,
9150c0d06caSMauro Carvalho Chehab };
9160c0d06caSMauro Carvalho Chehab 
em28xx_rc_register(void)9170c0d06caSMauro Carvalho Chehab static int __init em28xx_rc_register(void)
9180c0d06caSMauro Carvalho Chehab {
9190c0d06caSMauro Carvalho Chehab 	return em28xx_register_extension(&rc_ops);
9200c0d06caSMauro Carvalho Chehab }
9210c0d06caSMauro Carvalho Chehab 
em28xx_rc_unregister(void)9220c0d06caSMauro Carvalho Chehab static void __exit em28xx_rc_unregister(void)
9230c0d06caSMauro Carvalho Chehab {
9240c0d06caSMauro Carvalho Chehab 	em28xx_unregister_extension(&rc_ops);
9250c0d06caSMauro Carvalho Chehab }
9260c0d06caSMauro Carvalho Chehab 
927f22e9e71SMauro Carvalho Chehab MODULE_LICENSE("GPL v2");
92837e59f87SMauro Carvalho Chehab MODULE_AUTHOR("Mauro Carvalho Chehab");
929d8992b09SMauro Carvalho Chehab MODULE_DESCRIPTION(DRIVER_DESC " - input interface");
930d8992b09SMauro Carvalho Chehab MODULE_VERSION(EM28XX_VERSION);
9310c0d06caSMauro Carvalho Chehab 
9320c0d06caSMauro Carvalho Chehab module_init(em28xx_rc_register);
9330c0d06caSMauro Carvalho Chehab module_exit(em28xx_rc_unregister);
934