From ff2a20018094c593a35f4887bbdabf8926ddb6e6 Mon Sep 17 00:00:00 2001
From: Andy Walls <awalls@radix.net>
Date: Fri, 20 Feb 2009 23:52:13 -0300
Subject: V4L/DVB (10758): cx18: Convert I2C devices to v4l2_subdevices

This is a major perturbation to cx18 I2C device handling to convert it to the
v4l2_device/subdeivce framework.  This change breaks GPIO audio multiplexer
control for the time being.  It will be fixed in a coming change.

Signed-off-by: Andy Walls <awalls@radix.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
---
 drivers/media/video/cx18/cx18-audio.c    |  50 ++----
 drivers/media/video/cx18/cx18-audio.h    |   2 -
 drivers/media/video/cx18/cx18-av-core.c  |  15 +-
 drivers/media/video/cx18/cx18-av-core.h  |   2 +-
 drivers/media/video/cx18/cx18-cards.c    |  32 ++--
 drivers/media/video/cx18/cx18-cards.h    |  14 +-
 drivers/media/video/cx18/cx18-controls.c |  15 +-
 drivers/media/video/cx18/cx18-driver.c   |  86 +++++-----
 drivers/media/video/cx18/cx18-driver.h   |  24 ++-
 drivers/media/video/cx18/cx18-fileops.c  |   6 +-
 drivers/media/video/cx18/cx18-i2c.c      | 284 ++++++++-----------------------
 drivers/media/video/cx18/cx18-i2c.h      |   5 +-
 drivers/media/video/cx18/cx18-ioctl.c    |  78 ++++++---
 13 files changed, 250 insertions(+), 363 deletions(-)

(limited to 'drivers/media/video/cx18')

diff --git a/drivers/media/video/cx18/cx18-audio.c b/drivers/media/video/cx18/cx18-audio.c
index d19bd778c6a..ccd170887b8 100644
--- a/drivers/media/video/cx18/cx18-audio.c
+++ b/drivers/media/video/cx18/cx18-audio.c
@@ -23,9 +23,7 @@
 
 #include "cx18-driver.h"
 #include "cx18-io.h"
-#include "cx18-i2c.h"
 #include "cx18-cards.h"
-#include "cx18-audio.h"
 
 #define CX18_AUDIO_ENABLE 0xc72014
 
@@ -33,54 +31,32 @@
    settings. */
 int cx18_audio_set_io(struct cx18 *cx)
 {
+	const struct cx18_card_audio_input *in;
 	struct v4l2_routing route;
-	u32 audio_input;
 	u32 val;
-	int mux_input;
 	int err;
 
 	/* Determine which input to use */
-	if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
-		audio_input = cx->card->radio_input.audio_input;
-		mux_input = cx->card->radio_input.muxer_input;
-	} else {
-		audio_input =
-			cx->card->audio_inputs[cx->audio_input].audio_input;
-		mux_input =
-			cx->card->audio_inputs[cx->audio_input].muxer_input;
-	}
+	if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags))
+		in = &cx->card->radio_input;
+	else
+		in = &cx->card->audio_inputs[cx->audio_input];
 
 	/* handle muxer chips */
-	route.input = mux_input;
+	route.input = in->muxer_input;
 	route.output = 0;
-	cx18_i2c_hw(cx, cx->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route);
+	v4l2_subdev_call(cx->sd_extmux, audio, s_routing, &route);
 
-	route.input = audio_input;
-	err = cx18_i2c_hw(cx, cx->card->hw_audio_ctrl,
-			VIDIOC_INT_S_AUDIO_ROUTING, &route);
+	route.input = in->audio_input;
+	err = cx18_call_hw_err(cx, cx->card->hw_audio_ctrl,
+			       audio, s_routing, &route);
 	if (err)
 		return err;
 
+	/* FIXME - this internal mux should be abstracted to a subdev */
 	val = cx18_read_reg(cx, CX18_AUDIO_ENABLE) & ~0x30;
-	val |= (audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 :
-					(audio_input << 4);
+	val |= (in->audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 :
+					(in->audio_input << 4);
 	cx18_write_reg_expect(cx, val | 0xb00, CX18_AUDIO_ENABLE, val, 0x30);
 	return 0;
 }
-
-void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route)
-{
-	cx18_i2c_hw(cx, cx->card->hw_audio_ctrl,
-			VIDIOC_INT_S_AUDIO_ROUTING, route);
-}
-
-void cx18_audio_set_audio_clock_freq(struct cx18 *cx, u8 freq)
-{
-	static u32 freqs[3] = { 44100, 48000, 32000 };
-
-	/* The audio clock of the digitizer must match the codec sample
-	   rate otherwise you get some very strange effects. */
-	if (freq > 2)
-		return;
-	cx18_call_i2c_clients(cx, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[freq]);
-}
diff --git a/drivers/media/video/cx18/cx18-audio.h b/drivers/media/video/cx18/cx18-audio.h
index cb569a69379..2731d29b0ab 100644
--- a/drivers/media/video/cx18/cx18-audio.h
+++ b/drivers/media/video/cx18/cx18-audio.h
@@ -22,5 +22,3 @@
  */
 
 int cx18_audio_set_io(struct cx18 *cx);
-void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route);
-void cx18_audio_set_audio_clock_freq(struct cx18 *cx, u8 freq);
diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c
index f9bb77a25ee..2128070154d 100644
--- a/drivers/media/video/cx18/cx18-av-core.c
+++ b/drivers/media/video/cx18/cx18-av-core.c
@@ -1209,9 +1209,10 @@ static const struct v4l2_subdev_ops cx18_av_ops = {
 	.video = &cx18_av_video_ops,
 };
 
-int cx18_av_probe(struct cx18 *cx, struct v4l2_subdev **sd)
+int cx18_av_probe(struct cx18 *cx)
 {
 	struct cx18_av_state *state = &cx->av_state;
+	struct v4l2_subdev *sd;
 
 	state->rev = cx18_av_read4(cx, CXADEC_CHIP_CTRL) & 0xffff;
 	state->id = ((state->rev >> 4) == CXADEC_CHIP_TYPE_MAKO)
@@ -1224,13 +1225,13 @@ int cx18_av_probe(struct cx18 *cx, struct v4l2_subdev **sd)
 	state->slicer_line_delay = 0;
 	state->slicer_line_offset = (10 + state->slicer_line_delay - 2);
 
-	*sd = &state->sd;
-	v4l2_subdev_init(*sd, &cx18_av_ops);
-	v4l2_set_subdevdata(*sd, cx);
-	snprintf((*sd)->name, sizeof((*sd)->name),
+	sd = &state->sd;
+	v4l2_subdev_init(sd, &cx18_av_ops);
+	v4l2_set_subdevdata(sd, cx);
+	snprintf(sd->name, sizeof(sd->name),
 		 "%s internal A/V decoder", cx->v4l2_dev.name);
-	(*sd)->grp_id = CX18_HW_CX23418;
-	return v4l2_device_register_subdev(&cx->v4l2_dev, *sd);
+	sd->grp_id = CX18_HW_418_AV;
+	return v4l2_device_register_subdev(&cx->v4l2_dev, sd);
 }
 
 void cx18_av_exit(struct cx18 *cx, struct v4l2_subdev *sd)
diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h
index 90bdca96029..cd9c0e70f1f 100644
--- a/drivers/media/video/cx18/cx18-av-core.h
+++ b/drivers/media/video/cx18/cx18-av-core.h
@@ -342,7 +342,7 @@ int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value);
 int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value);
 void cx18_av_std_setup(struct cx18 *cx);
 
-int cx18_av_probe(struct cx18 *cx, struct v4l2_subdev **sd);
+int cx18_av_probe(struct cx18 *cx);
 void cx18_av_exit(struct cx18 *cx, struct v4l2_subdev *sd);
 
 /* ----------------------------------------------------------------------- */
diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c
index 6e2105ac2bc..6644534db56 100644
--- a/drivers/media/video/cx18/cx18-cards.c
+++ b/drivers/media/video/cx18/cx18-cards.c
@@ -53,9 +53,9 @@ static const struct cx18_card cx18_card_hvr1600_esmt = {
 	.name = "Hauppauge HVR-1600",
 	.comment = "Simultaneous Digital and Analog TV capture supported\n",
 	.v4l2_capabilities = CX18_CAP_ENCODER,
-	.hw_audio_ctrl = CX18_HW_CX23418,
+	.hw_audio_ctrl = CX18_HW_418_AV,
 	.hw_muxer = CX18_HW_CS5345,
-	.hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER |
+	.hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
 		  CX18_HW_CS5345 | CX18_HW_DVB,
 	.video_inputs = {
 		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE7 },
@@ -99,9 +99,9 @@ static const struct cx18_card cx18_card_hvr1600_samsung = {
 	.name = "Hauppauge HVR-1600 (Preproduction)",
 	.comment = "Simultaneous Digital and Analog TV capture supported\n",
 	.v4l2_capabilities = CX18_CAP_ENCODER,
-	.hw_audio_ctrl = CX18_HW_CX23418,
+	.hw_audio_ctrl = CX18_HW_418_AV,
 	.hw_muxer = CX18_HW_CS5345,
-	.hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER |
+	.hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
 		  CX18_HW_CS5345 | CX18_HW_DVB,
 	.video_inputs = {
 		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE7 },
@@ -154,8 +154,8 @@ static const struct cx18_card cx18_card_h900 = {
 	.name = "Compro VideoMate H900",
 	.comment = "Analog TV capture supported\n",
 	.v4l2_capabilities = CX18_CAP_ENCODER,
-	.hw_audio_ctrl = CX18_HW_CX23418,
-	.hw_all = CX18_HW_TUNER,
+	.hw_audio_ctrl = CX18_HW_418_AV,
+	.hw_all = CX18_HW_418_AV | CX18_HW_TUNER,
 	.video_inputs = {
 		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE2 },
 		{ CX18_CARD_INPUT_SVIDEO1,    1,
@@ -201,8 +201,8 @@ static const struct cx18_card cx18_card_mpc718 = {
 	.name = "Yuan MPC718",
 	.comment = "Analog video capture works; some audio line in may not.\n",
 	.v4l2_capabilities = CX18_CAP_ENCODER,
-	.hw_audio_ctrl = CX18_HW_CX23418,
-	.hw_all = CX18_HW_TUNER,
+	.hw_audio_ctrl = CX18_HW_418_AV,
+	.hw_all = CX18_HW_418_AV | CX18_HW_TUNER,
 	.video_inputs = {
 		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE2 },
 		{ CX18_CARD_INPUT_SVIDEO1,    1,
@@ -251,9 +251,9 @@ static const struct cx18_card cx18_card_cnxt_raptor_pal = {
 	.name = "Conexant Raptor PAL/SECAM",
 	.comment = "Analog TV capture supported\n",
 	.v4l2_capabilities = CX18_CAP_ENCODER,
-	.hw_audio_ctrl = CX18_HW_CX23418,
-	.hw_muxer = CX18_HW_GPIO,
-	.hw_all = CX18_HW_TUNER | CX18_HW_GPIO,
+	.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,
 	.video_inputs = {
 		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE2 },
 		{ CX18_CARD_INPUT_SVIDEO1,    1,
@@ -306,8 +306,8 @@ static const struct cx18_card cx18_card_toshiba_qosmio_dvbt = {
 	.comment = "Experimenters and photos needed for device to work well.\n"
 		  "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
 	.v4l2_capabilities = CX18_CAP_ENCODER,
-	.hw_audio_ctrl = CX18_HW_CX23418,
-	.hw_all = CX18_HW_TUNER,
+	.hw_audio_ctrl = CX18_HW_418_AV,
+	.hw_all = CX18_HW_418_AV | CX18_HW_TUNER,
 	.video_inputs = {
 		{ CX18_CARD_INPUT_VID_TUNER,  0, CX18_AV_COMPOSITE6 },
 		{ CX18_CARD_INPUT_SVIDEO1,    1,
@@ -350,9 +350,9 @@ static const struct cx18_card cx18_card_leadtek_pvr2100 = {
 	.comment = "Experimenters and photos needed for device to work well.\n"
 		  "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
 	.v4l2_capabilities = CX18_CAP_ENCODER,
-	.hw_audio_ctrl = CX18_HW_CX23418,
-	.hw_muxer = CX18_HW_GPIO,
-	.hw_all = CX18_HW_TUNER | CX18_HW_GPIO,
+	.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,
 	.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 f8ee29f102d..bd7f9556f18 100644
--- a/drivers/media/video/cx18/cx18-cards.h
+++ b/drivers/media/video/cx18/cx18-cards.h
@@ -22,12 +22,12 @@
  */
 
 /* hardware flags */
-#define CX18_HW_TUNER     (1 << 0)
-#define CX18_HW_TVEEPROM  (1 << 1)
-#define CX18_HW_CS5345    (1 << 2)
-#define CX18_HW_GPIO      (1 << 3)
-#define CX18_HW_CX23418   (1 << 4)
-#define CX18_HW_DVB   	  (1 << 5)
+#define CX18_HW_TUNER		(1 << 0)
+#define CX18_HW_TVEEPROM	(1 << 1)
+#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)
 
 /* video inputs */
 #define	CX18_CARD_INPUT_VID_TUNER	1
@@ -121,7 +121,7 @@ struct cx18_card {
 	char *comment;
 	u32 v4l2_capabilities;
 	u32 hw_audio_ctrl;	/* hardware used for the V4L2 controls (only
-				   1 dev allowed) */
+				   1 dev allowed currently) */
 	u32 hw_muxer;		/* hardware used to multiplex audio input */
 	u32 hw_all;		/* all hardware used by the board */
 	struct cx18_card_video_input video_inputs[CX18_CARD_MAX_VIDEO_INPUTS];
diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c
index 9505c417371..e5604c2328a 100644
--- a/drivers/media/video/cx18/cx18-controls.c
+++ b/drivers/media/video/cx18/cx18-controls.c
@@ -77,7 +77,7 @@ int cx18_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl)
 	case V4L2_CID_AUDIO_BASS:
 	case V4L2_CID_AUDIO_TREBLE:
 	case V4L2_CID_AUDIO_LOUDNESS:
-		if (cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl))
+		if (v4l2_subdev_call(cx->sd_av, core, queryctrl, qctrl))
 			qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
 		return 0;
 
@@ -134,7 +134,7 @@ static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
 	case V4L2_CID_AUDIO_BASS:
 	case V4L2_CID_AUDIO_TREBLE:
 	case V4L2_CID_AUDIO_LOUDNESS:
-		return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl);
+		return v4l2_subdev_call(cx->sd_av, core, s_ctrl, vctrl);
 
 	default:
 		CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
@@ -159,7 +159,8 @@ static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
 	case V4L2_CID_AUDIO_BASS:
 	case V4L2_CID_AUDIO_TREBLE:
 	case V4L2_CID_AUDIO_LOUDNESS:
-		return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl);
+		return v4l2_subdev_call(cx->sd_av, core, g_ctrl, vctrl);
+
 	default:
 		CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
 		return -EINVAL;
@@ -260,10 +261,12 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
 		return err;
 	}
 	if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+		static u32 freqs[3] = { 44100, 48000, 32000 };
 		struct cx18_api_func_private priv;
 		struct cx2341x_mpeg_params p = cx->params;
 		int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing),
 						c, VIDIOC_S_EXT_CTRLS);
+		unsigned int idx;
 
 		if (err)
 			return err;
@@ -287,7 +290,11 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
 			err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt);
 		cx->params = p;
 		cx->dualwatch_stereo_mode = p.audio_properties & 0x0300;
-		cx18_audio_set_audio_clock_freq(cx, p.audio_properties & 0x03);
+		idx = p.audio_properties & 0x03;
+		/* The audio clock of the digitizer must match the codec sample
+		   rate otherwise you get some very strange effects. */
+		if (idx < sizeof(freqs))
+			cx18_call_all(cx, audio, s_clock_freq, freqs[idx]);
 		return err;
 	}
 	return -EINVAL;
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index f5a41dd663d..edbb83c4c56 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -269,11 +269,16 @@ static void cx18_iounmap(struct cx18 *cx)
 /* Hauppauge card? get values from tveeprom */
 void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv)
 {
+	struct i2c_client c;
 	u8 eedata[256];
 
-	cx->i2c_client[0].addr = 0xA0 >> 1;
-	tveeprom_read(&cx->i2c_client[0], eedata, sizeof(eedata));
-	tveeprom_hauppauge_analog(&cx->i2c_client[0], tv, eedata);
+	strncpy(c.name, "cx18 tveeprom tmp", sizeof(c.name));
+	c.name[sizeof(c.name)-1] = '\0';
+	c.adapter = &cx->i2c_adap[0];
+	c.addr = 0xA0 >> 1;
+
+	tveeprom_read(&c, eedata, sizeof(eedata));
+	tveeprom_hauppauge_analog(&c, tv, eedata);
 }
 
 static void cx18_process_eeprom(struct cx18 *cx)
@@ -553,8 +558,6 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
 	cx->base_addr = pci_resource_start(cx->pci_dev, 0);
 
 	mutex_init(&cx->serialize_lock);
-	mutex_init(&cx->i2c_bus_lock[0]);
-	mutex_init(&cx->i2c_bus_lock[1]);
 	mutex_init(&cx->gpio_lock);
 	mutex_init(&cx->epu2apu_mb_lock);
 	mutex_init(&cx->epu2cpu_mb_lock);
@@ -669,54 +672,41 @@ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev,
 	return 0;
 }
 
-#ifdef MODULE
-static u32 cx18_request_module(struct cx18 *cx, u32 hw,
-		const char *name, u32 id)
-{
-	if ((hw & id) == 0)
-		return hw;
-	if (request_module("%s", name) != 0) {
-		CX18_ERR("Failed to load module %s\n", name);
-		return hw & ~id;
-	}
-	CX18_DEBUG_INFO("Loaded module %s\n", name);
-	return hw;
-}
-#endif
-
-static void cx18_load_and_init_modules(struct cx18 *cx)
+static void cx18_init_subdevs(struct cx18 *cx)
 {
 	u32 hw = cx->card->hw_all;
+	u32 device;
 	int i;
 
-#ifdef MODULE
-	/* load modules */
-#ifdef CONFIG_MEDIA_TUNER_MODULE
-	hw = cx18_request_module(cx, hw, "tuner", CX18_HW_TUNER);
-#endif
-#ifdef CONFIG_VIDEO_CS5345_MODULE
-	hw = cx18_request_module(cx, hw, "cs5345", CX18_HW_CS5345);
-#endif
-#endif
-
-	/* check which i2c devices are actually found */
-	for (i = 0; i < 32; i++) {
-		u32 device = 1 << i;
+	for (i = 0, device = 1; i < 32; i++, device <<= 1) {
 
 		if (!(device & hw))
 			continue;
-		if (device == CX18_HW_GPIO || device == CX18_HW_TVEEPROM ||
-		    device == CX18_HW_CX23418 || device == CX18_HW_DVB) {
-			/* These 'devices' do not use i2c probing */
+
+		switch (device) {
+		case CX18_HW_GPIO_AUDIO_MUX:
+		case CX18_HW_DVB:
+		case CX18_HW_TVEEPROM:
+			/* These subordinate devices do not use probing */
 			cx->hw_flags |= device;
-			continue;
-		}
-		cx18_i2c_register(cx, i);
-		if (cx18_i2c_hw_addr(cx, device) > 0)
+			break;
+		case CX18_HW_418_AV:
+			/* The A/V decoder gets probed earlier to set PLLs */
+			/* Just note that the card uses it (i.e. has analog) */
 			cx->hw_flags |= device;
+			break;
+		default:
+			if (cx18_i2c_register(cx, i) == 0)
+				cx->hw_flags |= device;
+			break;
+		}
 	}
 
-	hw = cx->hw_flags;
+	if (cx->hw_flags & CX18_HW_418_AV)
+		cx->sd_av = cx18_find_hw(cx, CX18_HW_418_AV);
+
+	if (cx->card->hw_muxer != 0)
+		cx->sd_extmux = cx18_find_hw(cx, cx->card->hw_muxer);
 }
 
 static int __devinit cx18_probe(struct pci_dev *pci_dev,
@@ -803,15 +793,17 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
 	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);
 
-	retval = cx18_av_probe(cx, &cx->sd_av);
+	/* Initialize integrated A/V decoder early to set PLLs, just in case */
+	retval = cx18_av_probe(cx);
 	if (retval) {
 		CX18_ERR("Could not register A/V decoder subdevice\n");
 		goto free_map;
 	}
 	/* Initialize the A/V decoder PLLs to sane defaults */
-	v4l2_subdev_call(cx->sd_av, core, init, (u32) CX18_AV_INIT_PLLS);
+	cx18_call_hw(cx, CX18_HW_418_AV, core, init, (u32) CX18_AV_INIT_PLLS);
 
 	/* active i2c  */
 	CX18_DEBUG_INFO("activating i2c...\n");
@@ -873,7 +865,7 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
 	   initialization. */
 	cx18_init_struct2(cx);
 
-	cx18_load_and_init_modules(cx);
+	cx18_init_subdevs(cx);
 
 	if (cx->std & V4L2_STD_525_60) {
 		cx->is_60hz = 1;
@@ -895,7 +887,7 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
 		setup.mode_mask = T_ANALOG_TV;  /* matches TV tuners */
 		setup.tuner_callback = (setup.type == TUNER_XC2028) ?
 			cx18_reset_tuner_gpio : NULL;
-		cx18_call_i2c_clients(cx, TUNER_SET_TYPE_ADDR, &setup);
+		cx18_call_all(cx, tuner, s_type_addr, &setup);
 		if (setup.type == TUNER_XC2028) {
 			static struct xc2028_ctrl ctrl = {
 				.fname = XC2028_DEFAULT_FIRMWARE,
@@ -905,7 +897,7 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
 				.tuner = cx->options.tuner,
 				.priv = &ctrl,
 			};
-			cx18_call_i2c_clients(cx, TUNER_SET_CONFIG, &cfg);
+			cx18_call_all(cx, tuner, s_config, &cfg);
 		}
 	}
 
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index 4b50878fc26..b81106d682a 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -448,7 +448,8 @@ struct cx18 {
 	int instance;
 	struct pci_dev *pci_dev;
 	struct v4l2_device v4l2_dev;
-	struct v4l2_subdev *sd_av;
+	struct v4l2_subdev *sd_av;     /* A/V decoder/digitizer sub-device */
+	struct v4l2_subdev *sd_extmux; /* External audio multiplexer sub-dev */
 
 	const struct cx18_card *card;	/* card information */
 	const char *card_name;  /* full name of the card */
@@ -528,9 +529,6 @@ struct cx18 {
 	struct i2c_adapter i2c_adap[2];
 	struct i2c_algo_bit_data i2c_algo[2];
 	struct cx18_i2c_algo_callback_data i2c_algo_cb_data[2];
-	struct i2c_client i2c_client[2];
-	struct mutex i2c_bus_lock[2];
-	struct i2c_client *i2c_clients[I2C_CLIENTS_MAX];
 
 	/* gpio */
 	u32 gpio_dir;
@@ -573,4 +571,22 @@ static inline int cx18_raw_vbi(const struct cx18 *cx)
 	return cx->vbi.in.type == V4L2_BUF_TYPE_VBI_CAPTURE;
 }
 
+/* Call the specified callback for all subdevs with a grp_id bit matching the
+ * mask in hw (if 0, then match them all). Ignore any errors. */
+#define cx18_call_hw(cx, hw, o, f, args...) \
+	__v4l2_device_call_subdevs(&(cx)->v4l2_dev, \
+				   !(hw) || (sd->grp_id & (hw)), o, f , ##args)
+
+#define cx18_call_all(cx, o, f, args...) cx18_call_hw(cx, 0, o, f , ##args)
+
+/* Call the specified callback for all subdevs with a grp_id bit matching the
+ * mask in hw (if 0, then match them all). If the callback returns an error
+ * other than 0 or -ENOIOCTLCMD, then return with that error code. */
+#define cx18_call_hw_err(cx, hw, o, f, args...) \
+	__v4l2_device_call_subdevs_until_err( \
+		   &(cx)->v4l2_dev, !(hw) || (sd->grp_id & (hw)), o, f , ##args)
+
+#define cx18_call_all_err(cx, o, f, args...) \
+	cx18_call_hw_err(cx, 0, o, f , ##args)
+
 #endif /* CX18_DRIVER_H */
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c
index 757982ea376..4d7d6d5a7f8 100644
--- a/drivers/media/video/cx18/cx18-fileops.c
+++ b/drivers/media/video/cx18/cx18-fileops.c
@@ -136,7 +136,7 @@ static void cx18_dualwatch(struct cx18 *cx)
 
 	new_stereo_mode = cx->params.audio_properties & stereo_mask;
 	memset(&vt, 0, sizeof(vt));
-	cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, &vt);
+	cx18_call_all(cx, tuner, g_tuner, &vt);
 	if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 &&
 			(vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
 		new_stereo_mode = dual;
@@ -608,7 +608,7 @@ int cx18_v4l2_close(struct file *filp)
 		/* Mark that the radio is no longer in use */
 		clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags);
 		/* Switch tuner to TV */
-		cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std);
+		cx18_call_all(cx, tuner, s_std, cx->std);
 		/* Select correct audio input (i.e. TV tuner or Line in) */
 		cx18_audio_set_io(cx);
 		if (atomic_read(&cx->ana_capturing) > 0) {
@@ -671,7 +671,7 @@ static int cx18_serialized_open(struct cx18_stream *s, struct file *filp)
 		/* We have the radio */
 		cx18_mute(cx);
 		/* Switch tuner to radio */
-		cx18_call_i2c_clients(cx, AUDC_SET_RADIO, NULL);
+		cx18_call_all(cx, tuner, s_radio);
 		/* Select the correct audio input (i.e. radio tuner) */
 		cx18_audio_set_io(cx);
 		/* Done! Unmute and continue. */
diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c
index db7b55281f5..6357dc44ab5 100644
--- a/drivers/media/video/cx18/cx18-i2c.c
+++ b/drivers/media/video/cx18/cx18-i2c.c
@@ -42,32 +42,35 @@
 
 #define CX18_CS5345_I2C_ADDR		0x4c
 
-/* This array should match the CX18_HW_ defines */
-static const u8 hw_driverids[] = {
-	I2C_DRIVERID_TUNER,
-	I2C_DRIVERID_TVEEPROM,
-	I2C_DRIVERID_CS5345,
-	0, 		/* CX18_HW_GPIO dummy driver ID */
-	0 		/* CX18_HW_CX23418 dummy driver ID */
-};
-
 /* This array should match the CX18_HW_ defines */
 static const u8 hw_addrs[] = {
-	0,
-	0,
-	CX18_CS5345_I2C_ADDR,
-	0, 		/* CX18_HW_GPIO dummy driver ID */
-	0,		/* CX18_HW_CX23418 dummy driver ID */
+	0,			/* CX18_HW_TUNER */
+	0,			/* CX18_HW_TVEEPROM */
+	CX18_CS5345_I2C_ADDR,	/* CX18_HW_CS5345 */
+	0,			/* CX18_HW_DVB */
+	0,			/* CX18_HW_418_AV */
+	0,			/* CX18_HW_GPIO_AUDIO_MUX */
 };
 
 /* This array should match the CX18_HW_ defines */
 /* This might well become a card-specific array */
 static const u8 hw_bus[] = {
-	0,
-	0,
-	0,
-	0, 		/* CX18_HW_GPIO dummy driver ID */
-	0,		/* CX18_HW_CX23418 dummy driver ID */
+	1,	/* CX18_HW_TUNER */
+	0,	/* CX18_HW_TVEEPROM */
+	0,	/* CX18_HW_CS5345 */
+	0,	/* CX18_HW_DVB */
+	0,	/* CX18_HW_418_AV */
+	0,	/* CX18_HW_GPIO_AUDIO_MUX */
+};
+
+/* This array should match the CX18_HW_ defines */
+static const char * const hw_modules[] = {
+	"tuner",	/* CX18_HW_TUNER */
+	NULL,		/* CX18_HW_TVEEPROM */
+	"cs5345",	/* CX18_HW_CS5345 */
+	NULL,		/* CX18_HW_DVB */
+	NULL,		/* CX18_HW_418_AV */
+	NULL,		/* CX18_HW_GPIO_AUDIO_MUX */
 };
 
 /* This array should match the CX18_HW_ defines */
@@ -75,83 +78,66 @@ static const char * const hw_devicenames[] = {
 	"tuner",
 	"tveeprom",
 	"cs5345",
-	"gpio",
-	"cx23418",
+	"cx23418_DTV",
+	"cx23418_AV",
+	"gpio_audio_mux",
 };
 
 int cx18_i2c_register(struct cx18 *cx, unsigned idx)
 {
-	struct i2c_board_info info;
-	struct i2c_client *c;
-	u8 id, bus;
-	int i;
-
-	CX18_DEBUG_I2C("i2c client register\n");
-	if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0)
+	struct v4l2_subdev *sd;
+	int bus = hw_bus[idx];
+	struct i2c_adapter *adap = &cx->i2c_adap[bus];
+	const char *mod = hw_modules[idx];
+	const char *type = hw_devicenames[idx];
+	u32 hw = 1 << idx;
+
+	if (idx >= ARRAY_SIZE(hw_addrs))
 		return -1;
-	id = hw_driverids[idx];
-	bus = hw_bus[idx];
-	memset(&info, 0, sizeof(info));
-	strlcpy(info.type, hw_devicenames[idx], sizeof(info.type));
-	info.addr = hw_addrs[idx];
-	for (i = 0; i < I2C_CLIENTS_MAX; i++)
-		if (cx->i2c_clients[i] == NULL)
-			break;
-
-	if (i == I2C_CLIENTS_MAX) {
-		CX18_ERR("insufficient room for new I2C client!\n");
-		return -ENOMEM;
-	}
 
-	if (id != I2C_DRIVERID_TUNER) {
-		c = i2c_new_device(&cx->i2c_adap[bus], &info);
-		if (c->driver == NULL)
-			i2c_unregister_device(c);
-		else
-			cx->i2c_clients[i] = c;
-		return cx->i2c_clients[i] ? 0 : -ENODEV;
+	if (hw == CX18_HW_TUNER) {
+		/* special tuner group handling */
+		sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
+						cx->card_i2c->radio);
+		if (sd != NULL)
+			sd->grp_id = hw;
+		sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
+						cx->card_i2c->demod);
+		if (sd != NULL)
+			sd->grp_id = hw;
+		sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
+						cx->card_i2c->tv);
+		if (sd != NULL)
+			sd->grp_id = hw;
+		return sd != NULL ? 0 : -1;
 	}
 
-	/* special tuner handling */
-	c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->radio);
-	if (c && c->driver == NULL)
-		i2c_unregister_device(c);
-	else if (c)
-		cx->i2c_clients[i++] = c;
-	c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->demod);
-	if (c && c->driver == NULL)
-		i2c_unregister_device(c);
-	else if (c)
-		cx->i2c_clients[i++] = c;
-	c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->tv);
-	if (c && c->driver == NULL)
-		i2c_unregister_device(c);
-	else if (c)
-		cx->i2c_clients[i++] = c;
-	return 0;
-}
+	/* Is it not an I2C device or one we do not wish to register? */
+	if (!hw_addrs[idx])
+		return -1;
 
-static int attach_inform(struct i2c_client *client)
-{
-	return 0;
+	/* It's an I2C device other than an analog tuner */
+	sd = v4l2_i2c_new_subdev(adap, mod, type, hw_addrs[idx]);
+	if (sd != NULL)
+		sd->grp_id = hw;
+	return sd != NULL ? 0 : -1;
 }
 
-static int detach_inform(struct i2c_client *client)
+/* Find the first member of the subdev group id in hw */
+struct v4l2_subdev *cx18_find_hw(struct cx18 *cx, u32 hw)
 {
-	int i;
-	struct cx18 *cx = (struct cx18 *)i2c_get_adapdata(client->adapter);
+	struct v4l2_subdev *result = NULL;
+	struct v4l2_subdev *sd;
 
-	CX18_DEBUG_I2C("i2c client detach\n");
-	for (i = 0; i < I2C_CLIENTS_MAX; i++) {
-		if (cx->i2c_clients[i] == client) {
-			cx->i2c_clients[i] = NULL;
+	spin_lock(&cx->v4l2_dev.lock);
+	v4l2_device_for_each_subdev(sd, &cx->v4l2_dev) {
+		if (sd->grp_id == hw) {
+			result = sd;
 			break;
 		}
 	}
-	CX18_DEBUG_I2C("i2c detach [client=%s,%s]\n",
-		   client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed");
-
-	return 0;
+	spin_unlock(&cx->v4l2_dev.lock);
+	return result;
 }
 
 static void cx18_setscl(void *data, int state)
@@ -204,8 +190,6 @@ static struct i2c_adapter cx18_i2c_adap_template = {
 	.id = I2C_HW_B_CX2341X,
 	.algo = NULL,                   /* set by i2c-algo-bit */
 	.algo_data = NULL,              /* filled from template */
-	.client_register = attach_inform,
-	.client_unregister = detach_inform,
 	.owner = THIS_MODULE,
 };
 
@@ -221,151 +205,27 @@ static struct i2c_algo_bit_data cx18_i2c_algo_template = {
 	.timeout	= CX18_ALGO_BIT_TIMEOUT*HZ /* jiffies */
 };
 
-static struct i2c_client cx18_i2c_client_template = {
-	.name = "cx18 internal",
-};
-
-int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg)
-{
-	struct i2c_client *client;
-	int retval;
-	int i;
-
-	CX18_DEBUG_I2C("call_i2c_client addr=%02x\n", addr);
-	for (i = 0; i < I2C_CLIENTS_MAX; i++) {
-		client = cx->i2c_clients[i];
-		if (client == NULL || client->driver == NULL ||
-				client->driver->command == NULL)
-			continue;
-		if (addr == client->addr) {
-			retval = client->driver->command(client, cmd, arg);
-			return retval;
-		}
-	}
-	if (cmd != VIDIOC_DBG_G_CHIP_IDENT)
-		CX18_ERR("i2c addr 0x%02x not found for cmd 0x%x!\n",
-			       addr, cmd);
-	return -ENODEV;
-}
-
-/* Find the i2c device based on the driver ID and return
-   its i2c address or -ENODEV if no matching device was found. */
-static int cx18_i2c_id_addr(struct cx18 *cx, u32 id)
-{
-	struct i2c_client *client;
-	int retval = -ENODEV;
-	int i;
-
-	for (i = 0; i < I2C_CLIENTS_MAX; i++) {
-		client = cx->i2c_clients[i];
-		if (client == NULL || client->driver == NULL)
-			continue;
-		if (id == client->driver->id) {
-			retval = client->addr;
-			break;
-		}
-	}
-	return retval;
-}
-
-/* Find the i2c device name matching the CX18_HW_ flag */
-static const char *cx18_i2c_hw_name(u32 hw)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
-		if (1 << i == hw)
-			return hw_devicenames[i];
-	return "unknown device";
-}
-
-/* Find the i2c device matching the CX18_HW_ flag and return
-   its i2c address or -ENODEV if no matching device was found. */
-int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
-		if (1 << i == hw)
-			return cx18_i2c_id_addr(cx, hw_driverids[i]);
-	return -ENODEV;
-}
-
-/* Calls i2c device based on CX18_HW_ flag. If hw == 0, then do nothing.
-   If hw == CX18_HW_GPIO then call the gpio handler. */
-int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg)
-{
-	int addr;
-
-	if (hw == 0)
-		return 0;
-
-	if (hw == CX18_HW_GPIO)
-		return cx18_gpio(cx, cmd, arg);
-
-	if (hw == CX18_HW_CX23418)
-		return v4l2_subdev_command(cx->sd_av, cmd, arg);
-
-	addr = cx18_i2c_hw_addr(cx, hw);
-	if (addr < 0) {
-		CX18_ERR("i2c hardware 0x%08x (%s) not found for cmd 0x%x!\n",
-			       hw, cx18_i2c_hw_name(hw), cmd);
-		return addr;
-	}
-	return cx18_call_i2c_client(cx, addr, cmd, arg);
-}
-
-/* broadcast cmd for all I2C clients and for the gpio subsystem */
-void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg)
-{
-	if (cx->i2c_adap[0].algo == NULL || cx->i2c_adap[1].algo == NULL) {
-		CX18_ERR("adapter is not set\n");
-		return;
-	}
-	v4l2_subdev_command(cx->sd_av, cmd, arg);
-	i2c_clients_command(&cx->i2c_adap[0], cmd, arg);
-	i2c_clients_command(&cx->i2c_adap[1], cmd, arg);
-	if (cx->hw_flags & CX18_HW_GPIO)
-		cx18_gpio(cx, cmd, arg);
-}
-
 /* init + register i2c algo-bit adapter */
 int init_cx18_i2c(struct cx18 *cx)
 {
 	int i;
 	CX18_DEBUG_I2C("i2c init\n");
 
-	/* Sanity checks for the I2C hardware arrays. They must be the
-	 * same size and GPIO/CX23418 must be the last entries.
-	 */
-	if (ARRAY_SIZE(hw_driverids) != ARRAY_SIZE(hw_addrs) ||
-	    ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) ||
-	    CX18_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 2)) ||
-	    CX18_HW_CX23418 != (1 << (ARRAY_SIZE(hw_addrs) - 1)) ||
-	    hw_driverids[ARRAY_SIZE(hw_addrs) - 1]) {
-		CX18_ERR("Mismatched I2C hardware arrays\n");
-		return -ENODEV;
-	}
-
 	for (i = 0; i < 2; i++) {
-		memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template,
-			sizeof(struct i2c_adapter));
+		/* Setup algorithm for adapter */
 		memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template,
 			sizeof(struct i2c_algo_bit_data));
 		cx->i2c_algo_cb_data[i].cx = cx;
 		cx->i2c_algo_cb_data[i].bus_index = i;
 		cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i];
-		cx->i2c_adap[i].algo_data = &cx->i2c_algo[i];
 
+		/* Setup adapter */
+		memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template,
+			sizeof(struct i2c_adapter));
+		cx->i2c_adap[i].algo_data = &cx->i2c_algo[i];
 		sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name),
 				" #%d-%d", cx->instance, i);
-		i2c_set_adapdata(&cx->i2c_adap[i], cx);
-
-		memcpy(&cx->i2c_client[i], &cx18_i2c_client_template,
-			sizeof(struct i2c_client));
-		sprintf(cx->i2c_client[i].name +
-				strlen(cx->i2c_client[i].name), "%d", i);
-		cx->i2c_client[i].adapter = &cx->i2c_adap[i];
+		i2c_set_adapdata(&cx->i2c_adap[i], &cx->v4l2_dev);
 		cx->i2c_adap[i].dev.parent = &cx->pci_dev->dev;
 	}
 
diff --git a/drivers/media/video/cx18/cx18-i2c.h b/drivers/media/video/cx18/cx18-i2c.h
index 4869739013b..bdfd1921e30 100644
--- a/drivers/media/video/cx18/cx18-i2c.h
+++ b/drivers/media/video/cx18/cx18-i2c.h
@@ -21,11 +21,8 @@
  *  02111-1307  USA
  */
 
-int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw);
-int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg);
-int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg);
-void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg);
 int cx18_i2c_register(struct cx18 *cx, unsigned idx);
+struct v4l2_subdev *cx18_find_hw(struct cx18 *cx, u32 hw);
 
 /* init + register i2c algo-bit adapter */
 int init_cx18_i2c(struct cx18 *cx);
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index 0ddf4dd5530..13ebd4a70f0 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -372,15 +372,52 @@ static int cx18_g_chip_ident(struct file *file, void *fh,
 				struct v4l2_dbg_chip_ident *chip)
 {
 	struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
+	int err = 0;
 
 	chip->ident = V4L2_IDENT_NONE;
 	chip->revision = 0;
-	if (v4l2_chip_match_host(&chip->match)) {
-		chip->ident = V4L2_IDENT_CX23418;
-		return 0;
+	switch (chip->match.type) {
+	case V4L2_CHIP_MATCH_HOST:
+		switch (chip->match.addr) {
+		case 0:
+			chip->ident = V4L2_IDENT_CX23418;
+			chip->revision = cx18_read_reg(cx, 0xC72028);
+			break;
+		case 1:
+			/*
+			 * The A/V decoder is always present, but in the rare
+			 * case that the card doesn't have analog, we don't
+			 * use it.  We find it w/o using the cx->sd_av pointer
+			 */
+			cx18_call_hw(cx, CX18_HW_418_AV,
+				     core, g_chip_ident, chip);
+			break;
+		default:
+			/*
+			 * Could return ident = V4L2_IDENT_UNKNOWN if we had
+			 * other host chips at higher addresses, but we don't
+			 */
+			err = -EINVAL; /* per V4L2 spec */
+			break;
+		}
+		break;
+	case V4L2_CHIP_MATCH_I2C_DRIVER:
+		/* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */
+		cx18_call_all(cx, core, g_chip_ident, chip);
+		break;
+	case V4L2_CHIP_MATCH_I2C_ADDR:
+		/*
+		 * We could return V4L2_IDENT_UNKNOWN, but we don't do the work
+		 * to look if a chip is at the address with no driver.  That's a
+		 * dangerous thing to do with EEPROMs anyway.
+		 */
+		cx18_call_all(cx, core, g_chip_ident, chip);
+		break;
+	default:
+		err = -EINVAL;
+		break;
 	}
-	cx18_call_i2c_clients(cx, VIDIOC_DBG_G_CHIP_IDENT, chip);
-	return 0;
+	return err;
 }
 
 #ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -394,10 +431,10 @@ static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg)
 		return -EINVAL;
 
 	regs->size = 4;
-	if (cmd == VIDIOC_DBG_G_REGISTER)
-		regs->val = cx18_read_enc(cx, regs->reg);
-	else
+	if (cmd == VIDIOC_DBG_S_REGISTER)
 		cx18_write_enc(cx, regs->val, regs->reg);
+	else
+		regs->val = cx18_read_enc(cx, regs->reg);
 	return 0;
 }
 
@@ -408,7 +445,8 @@ static int cx18_g_register(struct file *file, void *fh,
 
 	if (v4l2_chip_match_host(&reg->match))
 		return cx18_cxc(cx, VIDIOC_DBG_G_REGISTER, reg);
-	cx18_call_i2c_clients(cx, VIDIOC_DBG_G_REGISTER, reg);
+	/* FIXME - errors shouldn't be ignored */
+	cx18_call_all(cx, core, g_register, reg);
 	return 0;
 }
 
@@ -419,7 +457,8 @@ static int cx18_s_register(struct file *file, void *fh,
 
 	if (v4l2_chip_match_host(&reg->match))
 		return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg);
-	cx18_call_i2c_clients(cx, VIDIOC_DBG_S_REGISTER, reg);
+	/* FIXME - errors shouldn't be ignored */
+	cx18_call_all(cx, core, s_register, reg);
 	return 0;
 }
 #endif
@@ -598,7 +637,7 @@ static int cx18_g_frequency(struct file *file, void *fh,
 	if (vf->tuner != 0)
 		return -EINVAL;
 
-	cx18_call_i2c_clients(cx, VIDIOC_G_FREQUENCY, vf);
+	cx18_call_all(cx, tuner, g_frequency, vf);
 	return 0;
 }
 
@@ -617,7 +656,7 @@ int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
 
 	cx18_mute(cx);
 	CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency);
-	cx18_call_i2c_clients(cx, VIDIOC_S_FREQUENCY, vf);
+	cx18_call_all(cx, tuner, s_frequency, vf);
 	cx18_unmute(cx);
 	return 0;
 }
@@ -666,7 +705,7 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std)
 			(unsigned long long) cx->std);
 
 	/* Tuner */
-	cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std);
+	cx18_call_all(cx, tuner, s_std, cx->std);
 	return 0;
 }
 
@@ -683,9 +722,7 @@ static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
 	if (vt->index != 0)
 		return -EINVAL;
 
-	/* Setting tuner can only set audio mode */
-	cx18_call_i2c_clients(cx, VIDIOC_S_TUNER, vt);
-
+	cx18_call_all(cx, tuner, s_tuner, vt);
 	return 0;
 }
 
@@ -696,7 +733,7 @@ static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
 	if (vt->index != 0)
 		return -EINVAL;
 
-	cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, vt);
+	cx18_call_all(cx, tuner, g_tuner, vt);
 
 	if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
 		strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name));
@@ -853,7 +890,7 @@ static int cx18_log_status(struct file *file, void *fh)
 
 		cx18_read_eeprom(cx, &tv);
 	}
-	cx18_call_i2c_clients(cx, VIDIOC_LOG_STATUS, NULL);
+	cx18_call_all(cx, core, log_status);
 	cx18_get_input(cx, cx->active_input, &vidin);
 	cx18_get_audio_input(cx, cx->audio_input, &audin);
 	CX18_INFO("Video Input: %s\n", vidin.name);
@@ -894,7 +931,8 @@ static long cx18_default(struct file *file, void *fh, int cmd, void *arg)
 
 		CX18_DEBUG_IOCTL("VIDIOC_INT_S_AUDIO_ROUTING(%d, %d)\n",
 			route->input, route->output);
-		cx18_audio_set_route(cx, route);
+		cx18_call_hw(cx, cx->card->hw_audio_ctrl, audio, s_routing,
+			     route);
 		break;
 	}
 
@@ -922,6 +960,8 @@ long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd,
 
 	mutex_lock(&cx->serialize_lock);
 
+	/* FIXME - consolidate v4l2_prio_check()'s here */
+
 	if (cx18_debug & CX18_DBGFLG_IOCTL)
 		vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
 	res = video_ioctl2(filp, cmd, arg);
-- 
cgit v1.2.3-70-g09d2