V4L/DVB (10759): cx18: Convert GPIO connected functions to act as v4l2_subdevices

Convert GPIO line functions, such a audio routing and device resets, to
v4l2_subdevices.  This essentially completes the conversion of cx18 to the
v4l2_device/v4l2_subdevice framework.  No regression testing has taken place as
of yet.  Also an ivtv legacy bug with GPIO mux routing and going to/from radio
mode was commented, but not fixed.

Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c
index 6644534..9bc2218 100644
--- a/drivers/media/video/cx18/cx18-cards.c
+++ b/drivers/media/video/cx18/cx18-cards.c
@@ -56,7 +56,7 @@
 	.hw_audio_ctrl = CX18_HW_418_AV,
 	.hw_muxer = CX18_HW_CS5345,
 	.hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
-		  CX18_HW_CS5345 | CX18_HW_DVB,
+		  CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL,
 	.video_inputs = {
 		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE7 },
 		{ CX18_CARD_INPUT_SVIDEO1,    1, CX18_AV_SVIDEO1    },
@@ -102,7 +102,7 @@
 	.hw_audio_ctrl = CX18_HW_418_AV,
 	.hw_muxer = CX18_HW_CS5345,
 	.hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
-		  CX18_HW_CS5345 | CX18_HW_DVB,
+		  CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL,
 	.video_inputs = {
 		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE7 },
 		{ CX18_CARD_INPUT_SVIDEO1,    1, CX18_AV_SVIDEO1    },
@@ -155,7 +155,7 @@
 	.comment = "Analog TV capture supported\n",
 	.v4l2_capabilities = CX18_CAP_ENCODER,
 	.hw_audio_ctrl = CX18_HW_418_AV,
-	.hw_all = CX18_HW_418_AV | CX18_HW_TUNER,
+	.hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_RESET_CTRL,
 	.video_inputs = {
 		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE2 },
 		{ CX18_CARD_INPUT_SVIDEO1,    1,
@@ -202,7 +202,7 @@
 	.comment = "Analog video capture works; some audio line in may not.\n",
 	.v4l2_capabilities = CX18_CAP_ENCODER,
 	.hw_audio_ctrl = CX18_HW_418_AV,
-	.hw_all = CX18_HW_418_AV | CX18_HW_TUNER,
+	.hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_RESET_CTRL,
 	.video_inputs = {
 		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE2 },
 		{ CX18_CARD_INPUT_SVIDEO1,    1,
@@ -252,8 +252,8 @@
 	.comment = "Analog TV capture supported\n",
 	.v4l2_capabilities = CX18_CAP_ENCODER,
 	.hw_audio_ctrl = CX18_HW_418_AV,
-	.hw_muxer = CX18_HW_GPIO_AUDIO_MUX,
-	.hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_AUDIO_MUX,
+	.hw_muxer = CX18_HW_GPIO_MUX,
+	.hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX,
 	.video_inputs = {
 		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE2 },
 		{ CX18_CARD_INPUT_SVIDEO1,    1,
@@ -307,7 +307,7 @@
 		  "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
 	.v4l2_capabilities = CX18_CAP_ENCODER,
 	.hw_audio_ctrl = CX18_HW_418_AV,
-	.hw_all = CX18_HW_418_AV | CX18_HW_TUNER,
+	.hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_RESET_CTRL,
 	.video_inputs = {
 		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE6 },
 		{ CX18_CARD_INPUT_SVIDEO1,    1,
@@ -351,8 +351,9 @@
 		  "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
 	.v4l2_capabilities = CX18_CAP_ENCODER,
 	.hw_audio_ctrl = CX18_HW_418_AV,
-	.hw_muxer = CX18_HW_GPIO_AUDIO_MUX,
-	.hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_AUDIO_MUX,
+	.hw_muxer = CX18_HW_GPIO_MUX,
+	.hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX |
+		  CX18_HW_GPIO_RESET_CTRL,
 	.video_inputs = {
 		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE2 },
 		{ CX18_CARD_INPUT_SVIDEO1,    1,
diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h
index bd7f955..3c552b6b 100644
--- a/drivers/media/video/cx18/cx18-cards.h
+++ b/drivers/media/video/cx18/cx18-cards.h
@@ -27,7 +27,8 @@
 #define CX18_HW_CS5345		(1 << 2)
 #define CX18_HW_DVB		(1 << 3)
 #define CX18_HW_418_AV		(1 << 4)
-#define CX18_HW_GPIO_AUDIO_MUX	(1 << 5)
+#define CX18_HW_GPIO_MUX	(1 << 5)
+#define CX18_HW_GPIO_RESET_CTRL	(1 << 6)
 
 /* video inputs */
 #define	CX18_CARD_INPUT_VID_TUNER	1
diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c
index e5604c2..925e01f 100644
--- a/drivers/media/video/cx18/cx18-controls.c
+++ b/drivers/media/video/cx18/cx18-controls.c
@@ -22,11 +22,9 @@
  */
 
 #include "cx18-driver.h"
-#include "cx18-av-core.h"
 #include "cx18-cards.h"
 #include "cx18-ioctl.h"
 #include "cx18-audio.h"
-#include "cx18-i2c.h"
 #include "cx18-mailbox.h"
 #include "cx18-controls.h"
 
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index edbb83c..79b3bf5 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -684,7 +684,6 @@
 			continue;
 
 		switch (device) {
-		case CX18_HW_GPIO_AUDIO_MUX:
 		case CX18_HW_DVB:
 		case CX18_HW_TVEEPROM:
 			/* These subordinate devices do not use probing */
@@ -695,6 +694,16 @@
 			/* Just note that the card uses it (i.e. has analog) */
 			cx->hw_flags |= device;
 			break;
+		case CX18_HW_GPIO_RESET_CTRL:
+			/*
+			 * The Reset Controller gets probed and added to
+			 * hw_flags earlier for i2c adapter/bus initialization
+			 */
+			break;
+		case CX18_HW_GPIO_MUX:
+			if (cx18_gpio_register(cx, device) == 0)
+				cx->hw_flags |= device;
+			break;
 		default:
 			if (cx18_i2c_register(cx, i) == 0)
 				cx->hw_flags |= device;
@@ -793,7 +802,6 @@
 	cx->scb = (struct cx18_scb __iomem *)(cx->enc_mem + SCB_OFFSET);
 	cx18_init_scb(cx);
 
-	/* Initialize GPIO early so I2C device resets can be performed */
 	cx18_gpio_init(cx);
 
 	/* Initialize integrated A/V decoder early to set PLLs, just in case */
@@ -802,9 +810,17 @@
 		CX18_ERR("Could not register A/V decoder subdevice\n");
 		goto free_map;
 	}
-	/* Initialize the A/V decoder PLLs to sane defaults */
 	cx18_call_hw(cx, CX18_HW_418_AV, core, init, (u32) CX18_AV_INIT_PLLS);
 
+	/* Initialize GPIO Reset Controller to do chip resets during i2c init */
+	if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) {
+		if (cx18_gpio_register(cx, CX18_HW_GPIO_RESET_CTRL) != 0)
+			CX18_WARN("Could not register GPIO reset controller"
+				  "subdevice; proceeding anyway.\n");
+		else
+			cx->hw_flags |= CX18_HW_GPIO_RESET_CTRL;
+	}
+
 	/* active i2c  */
 	CX18_DEBUG_INFO("activating i2c...\n");
 	retval = init_cx18_i2c(cx);
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index b81106d..9ee6080 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -449,7 +449,7 @@
 	struct pci_dev *pci_dev;
 	struct v4l2_device v4l2_dev;
 	struct v4l2_subdev *sd_av;     /* A/V decoder/digitizer sub-device */
-	struct v4l2_subdev *sd_extmux; /* External audio multiplexer sub-dev */
+	struct v4l2_subdev *sd_extmux; /* External multiplexer sub-dev */
 
 	const struct cx18_card *card;	/* card information */
 	const char *card_name;  /* full name of the card */
@@ -534,6 +534,8 @@
 	u32 gpio_dir;
 	u32 gpio_val;
 	struct mutex gpio_lock;
+	struct v4l2_subdev sd_gpiomux;
+	struct v4l2_subdev sd_resetctrl;
 
 	/* v4l2 and User settings */
 
diff --git a/drivers/media/video/cx18/cx18-gpio.c b/drivers/media/video/cx18/cx18-gpio.c
index 1a99329..dcc19b1 100644
--- a/drivers/media/video/cx18/cx18-gpio.c
+++ b/drivers/media/video/cx18/cx18-gpio.c
@@ -46,6 +46,9 @@
  * gpio13: cs5345 reset pin
 */
 
+/*
+ * File scope utility functions
+ */
 static void gpio_write(struct cx18 *cx)
 {
 	u32 dir_lo = cx->gpio_dir & 0xffff;
@@ -63,73 +66,201 @@
 					CX18_REG_GPIO_OUT2, val_hi, dir_hi);
 }
 
-void cx18_reset_i2c_slaves_gpio(struct cx18 *cx)
+static void gpio_update(struct cx18 *cx, u32 mask, u32 data)
 {
-	const struct cx18_gpio_i2c_slave_reset *p;
-
-	p = &cx->card->gpio_i2c_slave_reset;
-
-	if ((p->active_lo_mask | p->active_hi_mask) == 0)
+	if (mask == 0)
 		return;
 
-	/* Assuming that the masks are a subset of the bits in gpio_dir */
+	mutex_lock(&cx->gpio_lock);
+	cx->gpio_val = (cx->gpio_val & ~mask) | (data & mask);
+	gpio_write(cx);
+	mutex_unlock(&cx->gpio_lock);
+}
+
+static void gpio_reset_seq(struct cx18 *cx, u32 active_lo, u32 active_hi,
+			   unsigned int assert_msecs,
+			   unsigned int recovery_msecs)
+{
+	u32 mask;
+
+	mask = active_lo | active_hi;
+	if (mask == 0)
+		return;
+
+	/*
+	 * Assuming that active_hi and active_lo are a subsets of the bits in
+	 * gpio_dir.  Also assumes that active_lo and active_hi don't overlap
+	 * in any bit position
+	 */
 
 	/* Assert */
-	mutex_lock(&cx->gpio_lock);
-	cx->gpio_val =
-		(cx->gpio_val | p->active_hi_mask) & ~(p->active_lo_mask);
-	gpio_write(cx);
-	schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_asserted));
+	gpio_update(cx, mask, ~active_lo);
+	schedule_timeout_uninterruptible(msecs_to_jiffies(assert_msecs));
 
 	/* Deassert */
-	cx->gpio_val =
-		(cx->gpio_val | p->active_lo_mask) & ~(p->active_hi_mask);
-	gpio_write(cx);
-	schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_recovery));
-	mutex_unlock(&cx->gpio_lock);
+	gpio_update(cx, mask, ~active_hi);
+	schedule_timeout_uninterruptible(msecs_to_jiffies(recovery_msecs));
 }
 
-void cx18_reset_ir_gpio(void *data)
+/*
+ * GPIO Multiplexer - logical device
+ */
+static int gpiomux_log_status(struct v4l2_subdev *sd)
 {
-	struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+	mutex_lock(&cx->gpio_lock);
+	CX18_INFO("GPIO:  direction 0x%08x, value 0x%08x\n",
+		cx->gpio_dir, cx->gpio_val);
+	mutex_unlock(&cx->gpio_lock);
+	return 0;
+}
+
+static int gpiomux_s_radio(struct v4l2_subdev *sd)
+{
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+	/*
+	 * FIXME - work out the cx->active/audio_input mess - this is
+	 * intended to handle the switch to radio mode and set the
+	 * audio routing, but we need to update the state in cx
+	 */
+	gpio_update(cx, cx->card->gpio_audio_input.mask,
+			cx->card->gpio_audio_input.radio);
+	return 0;
+}
+
+static int gpiomux_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+	u32 data;
+
+	switch (cx->card->audio_inputs[cx->audio_input].muxer_input) {
+	case 1:
+		data = cx->card->gpio_audio_input.linein;
+		break;
+	case 0:
+		data = cx->card->gpio_audio_input.tuner;
+		break;
+	default:
+		/*
+		 * FIXME - work out the cx->active/audio_input mess - this is
+		 * intended to handle the switch from radio mode and set the
+		 * audio routing, but we need to update the state in cx
+		 */
+		data = cx->card->gpio_audio_input.tuner;
+		break;
+	}
+	gpio_update(cx, cx->card->gpio_audio_input.mask, data);
+	return 0;
+}
+
+static int gpiomux_s_audio_routing(struct v4l2_subdev *sd,
+				   const struct v4l2_routing *route)
+{
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+	u32 data;
+
+	switch (route->input) {
+	case 0:
+		data = cx->card->gpio_audio_input.tuner;
+		break;
+	case 1:
+		data = cx->card->gpio_audio_input.linein;
+		break;
+	case 2:
+		data = cx->card->gpio_audio_input.radio;
+		break;
+	default:
+		return -EINVAL;
+	}
+	gpio_update(cx, cx->card->gpio_audio_input.mask, data);
+	return 0;
+}
+
+static const struct v4l2_subdev_core_ops gpiomux_core_ops = {
+	.log_status = gpiomux_log_status,
+};
+
+static const struct v4l2_subdev_tuner_ops gpiomux_tuner_ops = {
+	.s_std = gpiomux_s_std,
+	.s_radio = gpiomux_s_radio,
+};
+
+static const struct v4l2_subdev_audio_ops gpiomux_audio_ops = {
+	.s_routing = gpiomux_s_audio_routing,
+};
+
+static const struct v4l2_subdev_ops gpiomux_ops = {
+	.core = &gpiomux_core_ops,
+	.tuner = &gpiomux_tuner_ops,
+	.audio = &gpiomux_audio_ops,
+};
+
+/*
+ * GPIO Reset Controller - logical device
+ */
+static int resetctrl_log_status(struct v4l2_subdev *sd)
+{
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+	mutex_lock(&cx->gpio_lock);
+	CX18_INFO("GPIO:  direction 0x%08x, value 0x%08x\n",
+		cx->gpio_dir, cx->gpio_val);
+	mutex_unlock(&cx->gpio_lock);
+	return 0;
+}
+
+static int resetctrl_reset(struct v4l2_subdev *sd, u32 val)
+{
+	struct cx18 *cx = v4l2_get_subdevdata(sd);
 	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));
+	switch (val) {
+	case CX18_GPIO_RESET_I2C:
+		gpio_reset_seq(cx, p->active_lo_mask, p->active_hi_mask,
+			       p->msecs_asserted, p->msecs_recovery);
+		break;
+	case CX18_GPIO_RESET_Z8F0811:
+		/*
+		 * 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
+		 * 5. Zilog comes out of reset, loads reset vector address and
+		 *    executes from there. Required recovery delay unknown.
+		 */
+		gpio_reset_seq(cx, p->ir_reset_mask, 0,
+			       p->msecs_asserted, p->msecs_recovery);
+		break;
+	case CX18_GPIO_RESET_XC2028:
+		if (cx->card->tuners[0].tuner == TUNER_XC2028)
+			gpio_reset_seq(cx, (1 << cx->card->xceive_pin), 0,
+				       1, 1);
+		break;
+	}
+	return 0;
 }
-EXPORT_SYMBOL(cx18_reset_ir_gpio);
-/* This symbol is exported for use by an infrared module for the IR-blaster */
 
+static const struct v4l2_subdev_core_ops resetctrl_core_ops = {
+	.log_status = resetctrl_log_status,
+	.reset = resetctrl_reset,
+};
+
+static const struct v4l2_subdev_ops resetctrl_ops = {
+	.core = &resetctrl_core_ops,
+};
+
+/*
+ * External entry points
+ */
 void cx18_gpio_init(struct cx18 *cx)
 {
 	mutex_lock(&cx->gpio_lock);
@@ -156,6 +287,49 @@
 	mutex_unlock(&cx->gpio_lock);
 }
 
+int cx18_gpio_register(struct cx18 *cx, u32 hw)
+{
+	struct v4l2_subdev *sd;
+	const struct v4l2_subdev_ops *ops;
+	char *str;
+
+	switch (hw) {
+	case CX18_HW_GPIO_MUX:
+		sd = &cx->sd_gpiomux;
+		ops = &gpiomux_ops;
+		str = "gpio mux";
+		break;
+	case CX18_HW_GPIO_RESET_CTRL:
+		sd = &cx->sd_resetctrl;
+		ops = &resetctrl_ops;
+		str = "gpio reset ctrl";
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	v4l2_subdev_init(sd, ops);
+	v4l2_set_subdevdata(sd, cx);
+	snprintf(sd->name, sizeof(sd->name), "%s %s", cx->v4l2_dev.name, str);
+	sd->grp_id = hw;
+	return v4l2_device_register_subdev(&cx->v4l2_dev, sd);
+}
+
+void cx18_reset_ir_gpio(void *data)
+{
+	struct cx18 *cx = to_cx18((struct v4l2_device *)data);
+
+	if (cx->card->gpio_i2c_slave_reset.ir_reset_mask == 0)
+		return;
+
+	CX18_DEBUG_INFO("Resetting IR microcontroller\n");
+
+	v4l2_subdev_call(&cx->sd_resetctrl,
+			 core, reset, CX18_GPIO_RESET_Z8F0811);
+}
+EXPORT_SYMBOL(cx18_reset_ir_gpio);
+/* This symbol is exported for use by lirc_pvr150 for the IR-blaster */
+
 /* Xceive tuner reset function */
 int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value)
 {
@@ -163,56 +337,11 @@
 	struct cx18_i2c_algo_callback_data *cb_data = algo->data;
 	struct cx18 *cx = cb_data->cx;
 
-	if (cmd != XC2028_TUNER_RESET)
+	if (cmd != XC2028_TUNER_RESET ||
+	    cx->card->tuners[0].tuner != TUNER_XC2028)
 		return 0;
-	CX18_DEBUG_INFO("Resetting tuner\n");
 
-	mutex_lock(&cx->gpio_lock);
-	cx->gpio_val &= ~(1 << cx->card->xceive_pin);
-	gpio_write(cx);
-	mutex_unlock(&cx->gpio_lock);
-	schedule_timeout_interruptible(msecs_to_jiffies(1));
-
-	mutex_lock(&cx->gpio_lock);
-	cx->gpio_val |= 1 << cx->card->xceive_pin;
-	gpio_write(cx);
-	mutex_unlock(&cx->gpio_lock);
-	schedule_timeout_interruptible(msecs_to_jiffies(1));
-	return 0;
-}
-
-int cx18_gpio(struct cx18 *cx, unsigned int command, void *arg)
-{
-	struct v4l2_routing *route = arg;
-	u32 mask, data;
-
-	switch (command) {
-	case VIDIOC_INT_S_AUDIO_ROUTING:
-		if (route->input > 2)
-			return -EINVAL;
-		mask = cx->card->gpio_audio_input.mask;
-		switch (route->input) {
-		case 0:
-			data = cx->card->gpio_audio_input.tuner;
-			break;
-		case 1:
-			data = cx->card->gpio_audio_input.linein;
-			break;
-		case 2:
-		default:
-			data = cx->card->gpio_audio_input.radio;
-			break;
-		}
-		break;
-
-	default:
-		return -EINVAL;
-	}
-	if (mask) {
-		mutex_lock(&cx->gpio_lock);
-		cx->gpio_val = (cx->gpio_val & ~mask) | (data & mask);
-		gpio_write(cx);
-		mutex_unlock(&cx->gpio_lock);
-	}
-	return 0;
+	CX18_DEBUG_INFO("Resetting XCeive tuner\n");
+	return v4l2_subdev_call(&cx->sd_resetctrl,
+				core, reset, CX18_GPIO_RESET_XC2028);
 }
diff --git a/drivers/media/video/cx18/cx18-gpio.h b/drivers/media/video/cx18/cx18-gpio.h
index 39ffccc..f9a5ca3 100644
--- a/drivers/media/video/cx18/cx18-gpio.h
+++ b/drivers/media/video/cx18/cx18-gpio.h
@@ -22,7 +22,13 @@
  */
 
 void cx18_gpio_init(struct cx18 *cx);
-void cx18_reset_i2c_slaves_gpio(struct cx18 *cx);
+int cx18_gpio_register(struct cx18 *cx, u32 hw);
+
+enum cx18_gpio_reset_type {
+	CX18_GPIO_RESET_I2C     = 0,
+	CX18_GPIO_RESET_Z8F0811 = 1,
+	CX18_GPIO_RESET_XC2028  = 2,
+};
+
 void cx18_reset_ir_gpio(void *data);
 int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value);
-int cx18_gpio(struct cx18 *cx, unsigned int command, void *arg);
diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c
index 6357dc4..d092643 100644
--- a/drivers/media/video/cx18/cx18-i2c.c
+++ b/drivers/media/video/cx18/cx18-i2c.c
@@ -26,7 +26,6 @@
 #include "cx18-io.h"
 #include "cx18-cards.h"
 #include "cx18-gpio.h"
-#include "cx18-av-core.h"
 #include "cx18-i2c.h"
 #include "cx18-irq.h"
 
@@ -49,7 +48,8 @@
 	CX18_CS5345_I2C_ADDR,	/* CX18_HW_CS5345 */
 	0,			/* CX18_HW_DVB */
 	0,			/* CX18_HW_418_AV */
-	0,			/* CX18_HW_GPIO_AUDIO_MUX */
+	0,			/* CX18_HW_GPIO_MUX */
+	0,			/* CX18_HW_GPIO_RESET_CTRL */
 };
 
 /* This array should match the CX18_HW_ defines */
@@ -60,7 +60,8 @@
 	0,	/* CX18_HW_CS5345 */
 	0,	/* CX18_HW_DVB */
 	0,	/* CX18_HW_418_AV */
-	0,	/* CX18_HW_GPIO_AUDIO_MUX */
+	0,	/* CX18_HW_GPIO_MUX */
+	0,	/* CX18_HW_GPIO_RESET_CTRL */
 };
 
 /* This array should match the CX18_HW_ defines */
@@ -70,7 +71,8 @@
 	"cs5345",	/* CX18_HW_CS5345 */
 	NULL,		/* CX18_HW_DVB */
 	NULL,		/* CX18_HW_418_AV */
-	NULL,		/* CX18_HW_GPIO_AUDIO_MUX */
+	NULL,		/* CX18_HW_GPIO_MUX */
+	NULL,		/* CX18_HW_GPIO_RESET_CTRL */
 };
 
 /* This array should match the CX18_HW_ defines */
@@ -80,7 +82,8 @@
 	"cs5345",
 	"cx23418_DTV",
 	"cx23418_AV",
-	"gpio_audio_mux",
+	"gpio_mux",
+	"gpio_reset_ctrl",
 };
 
 int cx18_i2c_register(struct cx18 *cx, unsigned idx)
@@ -262,7 +265,8 @@
 	cx18_setscl(&cx->i2c_algo_cb_data[1], 1);
 	cx18_setsda(&cx->i2c_algo_cb_data[1], 1);
 
-	cx18_reset_i2c_slaves_gpio(cx);
+	cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL,
+		     core, reset, (u32) CX18_GPIO_RESET_I2C);
 
 	return i2c_bit_add_bus(&cx->i2c_adap[0]) ||
 		i2c_bit_add_bus(&cx->i2c_adap[1]);
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index 13ebd4a..e4c9e3d 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -940,7 +940,8 @@
 		u32 val = *(u32 *)arg;
 
 		if ((val == 0) || (val & 0x01))
-			cx18_reset_ir_gpio(&cx->i2c_algo_cb_data[0]);
+			cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, core, reset,
+				     (u32) CX18_GPIO_RESET_Z8F0811);
 		break;
 	}
 
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index 1d17884..ce65cc6 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -32,7 +32,6 @@
 #include "cx18-streams.h"
 #include "cx18-cards.h"
 #include "cx18-scb.h"
-#include "cx18-av-core.h"
 #include "cx18-dvb.h"
 
 #define CX18_DSP0_INTERRUPT_MASK     	0xd0004C
diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c
index 91c21dc..a81fe2e 100644
--- a/drivers/media/video/cx18/cx18-vbi.c
+++ b/drivers/media/video/cx18/cx18-vbi.c
@@ -25,7 +25,6 @@
 #include "cx18-vbi.h"
 #include "cx18-ioctl.h"
 #include "cx18-queue.h"
-#include "cx18-av-core.h"
 
 /*
  * Raster Reference/Protection (RP) bytes, used in Start/End Active
diff --git a/drivers/media/video/cx18/cx18-video.c b/drivers/media/video/cx18/cx18-video.c
index fabacb0..6fdaded 100644
--- a/drivers/media/video/cx18/cx18-video.c
+++ b/drivers/media/video/cx18/cx18-video.c
@@ -21,7 +21,6 @@
 
 #include "cx18-driver.h"
 #include "cx18-video.h"
-#include "cx18-av-core.h"
 #include "cx18-cards.h"
 
 void cx18_video_set_io(struct cx18 *cx)