V4L/DVB (8332): cx18: Suport external reset of the Z8F0811 IR controller on HVR-1600 for lirc

cx18: added in cx18_ir_reset_gpio function for lirc_pvr150 like module.  Also
added the ability to reset the IR chip via ioctl like ivtv.  This needs the
mutex to protect gpio_dir and gpio_val in struct cx18 as gpio changes can
come from a few different asynchronous sources now.

Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c
index 0caae1a..8fe5f38 100644
--- a/drivers/media/video/cx18/cx18-cards.c
+++ b/drivers/media/video/cx18/cx18-cards.c
@@ -88,6 +88,7 @@
 		.active_lo_mask = 0x3001,
 		.msecs_asserted = 10,
 		.msecs_recovery = 40,
+		.ir_reset_mask  = 0x0001,
 	},
 	.i2c = &cx18_i2c_std,
 };
@@ -133,6 +134,7 @@
 		.active_lo_mask = 0x3001,
 		.msecs_asserted = 10,
 		.msecs_recovery = 40,
+		.ir_reset_mask  = 0x0001,
 	},
 	.i2c = &cx18_i2c_std,
 };
diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h
index dc283d75..32155f6 100644
--- a/drivers/media/video/cx18/cx18-cards.h
+++ b/drivers/media/video/cx18/cx18-cards.h
@@ -83,6 +83,7 @@
 	u32 active_hi_mask; /* GPIO outputs that reset i2c chips when high */
 	int msecs_asserted; /* time period reset must remain asserted */
 	int msecs_recovery; /* time after deassert for chips to be ready */
+	u32 ir_reset_mask;  /* GPIO to reset the Zilog Z8F0811 IR contoller */
 };
 
 struct cx18_gpio_audio_input { 	/* select tuner/line in input */
diff --git a/drivers/media/video/cx18/cx18-gpio.c b/drivers/media/video/cx18/cx18-gpio.c
index d753a40..3d495db 100644
--- a/drivers/media/video/cx18/cx18-gpio.c
+++ b/drivers/media/video/cx18/cx18-gpio.c
@@ -83,6 +83,47 @@
 	mutex_unlock(&cx->gpio_lock);
 }
 
+void cx18_reset_ir_gpio(void *data)
+{
+	struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
+	const struct cx18_gpio_i2c_slave_reset *p;
+
+	p = &cx->card->gpio_i2c_slave_reset;
+
+	if (p->ir_reset_mask == 0)
+		return;
+
+	CX18_DEBUG_INFO("Resetting IR microcontroller\n");
+
+	/*
+	   Assert timing for the Z8F0811 on HVR-1600 boards:
+	   1. Assert RESET for min of 4 clock cycles at 18.432 MHz to initiate
+	   2. Reset then takes 66 WDT cycles at 10 kHz + 16 xtal clock cycles
+		(6,601,085 nanoseconds ~= 7 milliseconds)
+	   3. DBG pin must be high before chip exits reset for normal operation.
+		DBG is open drain and hopefully pulled high since we don't
+		normally drive it (GPIO 1?) for the HVR-1600
+	   4. Z8F0811 won't exit reset until RESET is deasserted
+	*/
+	mutex_lock(&cx->gpio_lock);
+	cx->gpio_val = cx->gpio_val & ~p->ir_reset_mask;
+	gpio_write(cx);
+	mutex_unlock(&cx->gpio_lock);
+	schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_asserted));
+
+	/*
+	   Zilog comes out of reset, loads reset vector address and executes
+	   from there. Required recovery delay unknown.
+	*/
+	mutex_lock(&cx->gpio_lock);
+	cx->gpio_val = cx->gpio_val | p->ir_reset_mask;
+	gpio_write(cx);
+	mutex_unlock(&cx->gpio_lock);
+	schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_recovery));
+}
+EXPORT_SYMBOL(cx18_reset_ir_gpio);
+/* This symbol is exported for use by an infrared module for the IR-blaster */
+
 void cx18_gpio_init(struct cx18 *cx)
 {
 	mutex_lock(&cx->gpio_lock);
diff --git a/drivers/media/video/cx18/cx18-gpio.h b/drivers/media/video/cx18/cx18-gpio.h
index 7447fed..22cd7dd 100644
--- a/drivers/media/video/cx18/cx18-gpio.h
+++ b/drivers/media/video/cx18/cx18-gpio.h
@@ -22,5 +22,6 @@
 
 void cx18_gpio_init(struct cx18 *cx);
 void cx18_reset_i2c_slaves_gpio(struct cx18 *cx);
+void cx18_reset_ir_gpio(void *data);
 int cx18_reset_tuner_gpio(void *dev, int cmd, int value);
 int cx18_gpio(struct cx18 *cx, unsigned int command, void *arg);
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index 146a4b8..0d74e59 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -754,6 +754,15 @@
 		cx18_audio_set_route(cx, route);
 		break;
 	}
+
+	case VIDIOC_INT_RESET: {
+		u32 val = *(u32 *)arg;
+
+		if ((val == 0) || (val & 0x01))
+			cx18_reset_ir_gpio(&cx->i2c_algo_cb_data[0]);
+		break;
+	}
+
 	default:
 		return -EINVAL;
 	}