V4L/DVB (9607): em28xx: Properly implement poll support for IR's

The first em28xx were based on i2c IR's. However, some newer designs
are coming with a polling-based IR. Those are done by reading a register
set at em28xx.

This patch adds core polling support for those devices. Later patches will
add support for some device-specific IR's.

This patch adds the same basic IR polling code used by bttv, cx88 and saa7134, and
shares the common getkey masks defined at ir-common.

Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c
index eab3d95..afd6760 100644
--- a/drivers/media/video/em28xx/em28xx-input.c
+++ b/drivers/media/video/em28xx/em28xx-input.c
@@ -38,12 +38,42 @@
 module_param(ir_debug, int, 0644);
 MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
 
-#define dprintk(fmt, arg...) \
+#define i2cdprintk(fmt, arg...) \
 	if (ir_debug) { \
 		printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg); \
 	}
 
-/* ----------------------------------------------------------------------- */
+#define dprintk(fmt, arg...) \
+	if (ir_debug) { \
+		printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
+	}
+
+/**********************************************************
+ Polling structure used by em28xx IR's
+ **********************************************************/
+
+struct em28xx_IR {
+	struct em28xx *dev;
+	struct input_dev *input;
+	struct ir_input_state ir;
+	char name[32];
+	char phys[32];
+
+	/* poll external decoder */
+	int polling;
+	struct work_struct work;
+	struct timer_list timer;
+	u32 last_gpio;
+	u32 mask_keycode;
+	u32 mask_keydown;
+	u32 mask_keyup;
+
+	int  (*get_key)(struct em28xx_IR *);
+};
+
+/**********************************************************
+ I2C IR based get keycodes - should be used with ir-kbd-i2c
+ **********************************************************/
 
 int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
 {
@@ -51,7 +81,7 @@
 
 	/* poll IR chip */
 	if (1 != i2c_master_recv(&ir->c, &b, 1)) {
-		dprintk("read error\n");
+		i2cdprintk("read error\n");
 		return -EIO;
 	}
 
@@ -59,7 +89,7 @@
 	   down, while 0xff indicates that no button is hold
 	   down. 0xfe sequences are sometimes interrupted by 0xFF */
 
-	dprintk("key %02x\n", b);
+	i2cdprintk("key %02x\n", b);
 
 	if (b == 0xff)
 		return 0;
@@ -73,7 +103,6 @@
 	return 1;
 }
 
-
 int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
 {
 	unsigned char buf[2];
@@ -97,7 +126,7 @@
 		 ((buf[0]&0x10)>>3) | /* 0000 0010 */
 		 ((buf[0]&0x20)>>5);  /* 0000 0001 */
 
-	dprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x)\n",
+	i2cdprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x)\n",
 			code, buf[0]);
 
 	/* return key */
@@ -114,11 +143,11 @@
 	/* poll IR chip */
 
 	if (3 != i2c_master_recv(&ir->c, buf, 3)) {
-		dprintk("read error\n");
+		i2cdprintk("read error\n");
 		return -EIO;
 	}
 
-	dprintk("key %02x\n", buf[2]&0x3f);
+	i2cdprintk("key %02x\n", buf[2]&0x3f);
 	if (buf[0] != 0x00)
 		return 0;
 
@@ -128,6 +157,188 @@
 	return 1;
 }
 
+/**********************************************************
+ Poll based get keycode functions
+ **********************************************************/
+
+static int default_polling_getkey(struct em28xx_IR *ir)
+{
+	struct em28xx *dev = ir->dev;
+	int rc;
+	u32 msg;
+
+	/* Read key toggle, brand, and key code */
+	rc = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R45_IR,
+					  (u8 *)&msg, sizeof(msg));
+	if (rc < 0)
+		return rc;
+
+	return (int)(msg & 0x7fffffffl);
+}
+
+/**********************************************************
+ Polling code for em28xx
+ **********************************************************/
+
+static void em28xx_ir_handle_key(struct em28xx_IR *ir)
+{
+	int    gpio;
+	u32    data;
+
+	/* read gpio value */
+	gpio = ir->get_key(ir);
+	if (gpio < 0)
+		return;
+
+	if (gpio == ir->last_gpio)
+		return;
+	ir->last_gpio = gpio;
+
+	/* extract data */
+	data = ir_extract_bits(gpio, ir->mask_keycode);
+	dprintk("irq gpio=0x%x code=%d | poll%s%s\n",
+		   gpio, data,
+		   (gpio & ir->mask_keydown) ? " down" : "",
+		   (gpio & ir->mask_keyup) ? " up" : "");
+
+	/* Generate keyup/keydown events */
+	if (ir->mask_keydown) {
+		/* bit set on keydown */
+		if (gpio & ir->mask_keydown)
+			ir_input_keydown(ir->input, &ir->ir, data, data);
+		else
+			ir_input_nokey(ir->input, &ir->ir);
+	} else if (ir->mask_keyup) {
+		/* bit cleared on keydown */
+		if (!(gpio & ir->mask_keyup))
+			ir_input_keydown(ir->input, &ir->ir, data, data);
+		else
+			ir_input_nokey(ir->input, &ir->ir);
+	} else {
+		/* can't distinguish keydown/up :-/ */
+		ir_input_keydown(ir->input, &ir->ir, data, data);
+		ir_input_nokey(ir->input, &ir->ir);
+	}
+}
+
+static void ir_timer(unsigned long data)
+{
+	struct em28xx_IR *ir = (struct em28xx_IR *)data;
+
+	schedule_work(&ir->work);
+}
+
+static void em28xx_ir_work(struct work_struct *work)
+{
+	struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work);
+
+	em28xx_ir_handle_key(ir);
+	mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
+}
+
+void em28xx_ir_start(struct em28xx_IR *ir)
+{
+	setup_timer(&ir->timer, ir_timer, (unsigned long)ir);
+	INIT_WORK(&ir->work, em28xx_ir_work);
+	schedule_work(&ir->work);
+}
+
+static void em28xx_ir_stop(struct em28xx_IR *ir)
+{
+	del_timer_sync(&ir->timer);
+	flush_scheduled_work();
+}
+
+int em28xx_ir_init(struct em28xx *dev)
+{
+	struct em28xx_IR *ir;
+	struct input_dev *input_dev;
+	IR_KEYTAB_TYPE *ir_codes = NULL;
+	int ir_type = IR_TYPE_OTHER;
+	int err = -ENOMEM;
+
+	ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+	input_dev = input_allocate_device();
+	if (!ir || !input_dev)
+		goto err_out_free;
+
+	ir->input = input_dev;
+
+	/* */
+	ir->get_key = default_polling_getkey;
+	ir->polling = 50; /* ms */
+
+	/* detect & configure */
+	switch (dev->model) {
+	}
+
+	if (NULL == ir_codes) {
+		err = -ENODEV;
+		goto err_out_free;
+	}
+
+	/* Get the current key status, to avoid adding an
+	   unexistent key code */
+	ir->last_gpio    = ir->get_key(ir);
+
+	/* init input device */
+	snprintf(ir->name, sizeof(ir->name), "em28xx IR (%s)",
+						dev->name);
+
+	usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
+	strlcat(ir->phys, "/input0", sizeof(ir->phys));
+
+	ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+	input_dev->name = ir->name;
+	input_dev->phys = ir->phys;
+	input_dev->id.bustype = BUS_USB;
+	input_dev->id.version = 1;
+	input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
+	input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
+
+	input_dev->dev.parent = &dev->udev->dev;
+	/* record handles to ourself */
+	ir->dev = dev;
+	dev->ir = ir;
+
+	em28xx_ir_start(ir);
+
+	/* all done */
+	err = input_register_device(ir->input);
+	if (err)
+		goto err_out_stop;
+
+	return 0;
+ err_out_stop:
+	em28xx_ir_stop(ir);
+	dev->ir = NULL;
+ err_out_free:
+	input_free_device(input_dev);
+	kfree(ir);
+	return err;
+}
+
+int em28xx_ir_fini(struct em28xx *dev)
+{
+	struct em28xx_IR *ir = dev->ir;
+
+	/* skip detach on non attached boards */
+	if (!ir)
+		return 0;
+
+	em28xx_ir_stop(ir);
+	input_unregister_device(ir->input);
+	kfree(ir);
+
+	/* done */
+	dev->ir = NULL;
+	return 0;
+}
+
+/**********************************************************
+ Handle Webcam snapshot button
+ **********************************************************/
+
 static void em28xx_query_sbutton(struct work_struct *work)
 {
 	/* Poll the register and see if the button is depressed */
@@ -210,9 +421,3 @@
 	}
 	return;
 }
-
-/* ----------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */