]> pilppa.com Git - linux-2.6-omap-h63xx.git/commitdiff
V4L/DVB (10758): cx18: Convert I2C devices to v4l2_subdevices
authorAndy Walls <awalls@radix.net>
Sat, 21 Feb 2009 02:52:13 +0000 (23:52 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 30 Mar 2009 15:43:01 +0000 (12:43 -0300)
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>
13 files changed:
drivers/media/video/cx18/cx18-audio.c
drivers/media/video/cx18/cx18-audio.h
drivers/media/video/cx18/cx18-av-core.c
drivers/media/video/cx18/cx18-av-core.h
drivers/media/video/cx18/cx18-cards.c
drivers/media/video/cx18/cx18-cards.h
drivers/media/video/cx18/cx18-controls.c
drivers/media/video/cx18/cx18-driver.c
drivers/media/video/cx18/cx18-driver.h
drivers/media/video/cx18/cx18-fileops.c
drivers/media/video/cx18/cx18-i2c.c
drivers/media/video/cx18/cx18-i2c.h
drivers/media/video/cx18/cx18-ioctl.c

index d19bd778c6ac1fcc2080e437d204c46d7149a554..ccd170887b89e7af0cdb5c195f0037e182f0c0eb 100644 (file)
@@ -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
 
    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]);
-}
index cb569a69379cc38298d5d2d791d9865f758ed301..2731d29b0ab95078cb856cac4c9fb4067fa9c6c6 100644 (file)
@@ -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);
index f9bb77a25ee950d0a7d6d2ab91559d2caeea6ba9..2128070154d3c3501489e821df0893431bf37c1b 100644 (file)
@@ -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)
index 90bdca96029210860f07da28faf304d8ffc8e8c5..cd9c0e70f1fc135d1571b0526b7aa56040cd16a4 100644 (file)
@@ -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);
 
 /* ----------------------------------------------------------------------- */
index 6e2105ac2bc4bf62ec76fbfca825486e2f56486a..6644534db564dc865fc46453c4c83ec52fbf04d3 100644 (file)
@@ -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,
index f8ee29f102d42c50d0b8948bae43b49831e0a6d9..bd7f9556f18cc36174a75a474f3d8beade036f73 100644 (file)
  */
 
 /* 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];
index 9505c417371d4726ae1c5e2418b75fefdc3f94a2..e5604c2328ad66f6200208bc3ec37fe6aee9aaa8 100644 (file)
@@ -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;
index f5a41dd663dc23db8005c46e62fc9d29ab312c75..edbb83c4c564f8336036bf6c3815fb2cfacb3f57 100644 (file)
@@ -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);
                }
        }
 
index 4b50878fc2655d4e239623cae99004c32091045a..b81106d682ae8a5e13826e897292b5116aae98ca 100644 (file)
@@ -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 */
index 757982ea37660340531b87a2a73df3e4fa8388f4..4d7d6d5a7f86d2f9f7aca66643a7ecceada91be3 100644 (file)
@@ -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. */
index db7b55281f500cdacceb6b443de3a9cfa033754b..6357dc44ab5ba92de3babfb6208384368582c99e 100644 (file)
 
 #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;
        }
 
index 4869739013bdf3fbd68f37696f890256f89961d6..bdfd1921e30021062f8c1ddcd50f476f3bf8d513 100644 (file)
  *  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);
index 0ddf4dd55308f991dac27348de2c4915c60d88a1..13ebd4a70f0d1e840ba2707ef90f29bed78cebd1 100644 (file)
@@ -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);