As Randy Dunlap <randy.dunlap@oracle.com> reported, cx88 has some compilation issues:
drivers/built-in.o: In function `cx88_call_i2c_clients':
(.text+0x20af17): undefined reference to `videobuf_dvb_get_frontend'
drivers/built-in.o: In function `cx8802_probe':
cx88-mpeg.c:(.devinit.text+0x268c4): undefined reference to `videobuf_dvb_alloc_frontend'
cx88-mpeg.c:(.devinit.text+0x268ea): undefined reference to `videobuf_dvb_dealloc_frontends'
With those configs:
CONFIG_VIDEO_CX88=y
CONFIG_VIDEO_CX88_BLACKBIRD=y
CONFIG_VIDEO_CX88_DVB=m
CONFIG_DVB_CORE=m
After carefully examining the code, with the current code, several cx88 drivers
(cx8800, cx8802, cx88_dvb and cx88_blackbird) should be compiled as a module,
if one of them is marked as such. Just fixing Kconfig could create a very complex
set of rules. Also, this hides a problem with the current approach where the dvb
functionality weren't confined inside dvb module.
What happens is that:
	- cx88-i2c (part of cx8800) has some special rules if DVB;
	- cx88-mpeg (cx8802 module) has also part of DVB init code;
	- cx88-dvb has the rest of the dvb code;
	- cx88-blackbird can be used with cx88-mpeg, having cx88-dvb or not.
So, instead of doing some tricks at Kconfig and wait for a next breakage,
this patch moves the dvb code inside cx88-i2c and cx88-mpeg into cx88-dvb.
Another problem is that cx8802 were being compiled, even without cx88-dvb
and cx88-blackbird modules.
While on this code, let's fix also a reported problem:
http://www.linuxtv.org/pipermail/linux-dvb/2009-January/031225.html
A solution for the issue were proposed here:
http://www.mail-archive.com/linux-media@vger.kernel.org/msg00021.html
Thanks to Randy, Andy, Gregoire and Thomas for helping us to detect
and solve the issues.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
          To compile this driver as a module, choose M here: the
          module will be called cx88-dvb.
 
+config VIDEO_CX88_MPEG
+       tristate
+       depends on VIDEO_CX88_DVB || VIDEO_CX88_BLACKBIRD
+       default y
+
 config VIDEO_CX88_VP3054
        tristate "VP-3054 Secondary I2C Bus Support"
        default m
 
 cx8800-objs    := cx88-video.o cx88-vbi.o
 cx8802-objs    := cx88-mpeg.o
 
-obj-$(CONFIG_VIDEO_CX88) += cx88xx.o cx8800.o cx8802.o
+obj-$(CONFIG_VIDEO_CX88) += cx88xx.o cx8800.o
+obj-$(CONFIG_VIDEO_CX88_MPEG) += cx8802.o
 obj-$(CONFIG_VIDEO_CX88_ALSA) += cx88-alsa.o
 obj-$(CONFIG_VIDEO_CX88_BLACKBIRD) += cx88-blackbird.o
 obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o
 
        return ret;
 }
 
+static void cx88_dvb_gate_ctrl(struct cx88_core  *core, int open)
+{
+       struct videobuf_dvb_frontends *f;
+       struct videobuf_dvb_frontend *fe;
+
+       if (!core->dvbdev)
+               return;
+
+       f = &core->dvbdev->frontends;
+
+       if (!f)
+               return;
+
+       if (f->gate <= 1) /* undefined or fe0 */
+               fe = videobuf_dvb_get_frontend(f, 1);
+       else
+               fe = videobuf_dvb_get_frontend(f, f->gate);
+
+       if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl)
+               fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, open);
+}
+
 /* ------------------------------------------------------------------ */
 
 static int dvico_fusionhdtv_demod_init(struct dvb_frontend* fe)
        struct cx88_core *core = dev->core;
        struct videobuf_dvb_frontend *fe0, *fe1 = NULL;
        int mfe_shared = 0; /* bus not shared by default */
+       int i;
 
        if (0 != core->i2c_rc) {
                printk(KERN_ERR "%s/2: no i2c-bus available, cannot attach dvb drivers\n", core->name);
                goto frontend_detach;
        }
 
+       if (!core->board.num_frontends)
+               return -EINVAL;
+
+       mutex_init(&dev->frontends.lock);
+       INIT_LIST_HEAD(&dev->frontends.felist);
+
+       printk(KERN_INFO "%s() allocating %d frontend(s)\n", __func__,
+                        core->board.num_frontends);
+       for (i = 1; i <= core->board.num_frontends; i++) {
+               fe0 = videobuf_dvb_alloc_frontend(&dev->frontends, i);
+               if (!fe0) {
+                       printk(KERN_ERR "%s() failed to alloc\n", __func__);
+                       videobuf_dvb_dealloc_frontends(&dev->frontends);
+                       goto frontend_detach;
+               }
+       }
+
        /* Get the first frontend */
        fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
        if (!fe0)
        /* multi-frontend gate control is undefined or defaults to fe0 */
        dev->frontends.gate = 0;
 
+       /* Sets the gate control callback to be used by i2c command calls */
+       core->gate_ctrl = cx88_dvb_gate_ctrl;
+
        /* init frontend(s) */
        switch (core->boardnr) {
        case CX88_BOARD_HAUPPAUGE_DVB_T1:
                &dev->pci->dev, adapter_nr, mfe_shared);
 
 frontend_detach:
+       core->gate_ctrl = NULL;
        videobuf_dvb_dealloc_frontends(&dev->frontends);
        return -EINVAL;
 }
 
        vp3054_i2c_remove(dev);
 
+       core->gate_ctrl = NULL;
+
        return 0;
 }
 
 
 
 void cx88_call_i2c_clients(struct cx88_core *core, unsigned int cmd, void *arg)
 {
-#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE)
-       struct videobuf_dvb_frontends *f = &core->dvbdev->frontends;
-       struct videobuf_dvb_frontend *fe = NULL;
-#endif
        if (0 != core->i2c_rc)
                return;
 
-#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE)
-       if (core->dvbdev && f) {
-               if(f->gate <= 1) /* undefined or fe0 */
-                       fe = videobuf_dvb_get_frontend(f, 1);
-               else
-                       fe = videobuf_dvb_get_frontend(f, f->gate);
+       if (core->gate_ctrl)
+               core->gate_ctrl(core, 1);
 
-               if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl)
-                       fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, 1);
+       i2c_clients_command(&core->i2c_adap, cmd, arg);
 
-               i2c_clients_command(&core->i2c_adap, cmd, arg);
-
-               if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl)
-                       fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, 0);
-       } else
-#endif
-               i2c_clients_command(&core->i2c_adap, cmd, arg);
+       if (core->gate_ctrl)
+               core->gate_ctrl(core, 0);
 }
 
 static const struct i2c_algo_bit_data cx8800_i2c_algo_template = {
 
        dev->pci = pci_dev;
        dev->core = core;
 
+       /* Maintain a reference so cx88-video can query the 8802 device. */
+       core->dvbdev = dev;
+
        err = cx8802_init_common(dev);
        if (err != 0)
                goto fail_free;
        INIT_LIST_HEAD(&dev->drvlist);
        list_add_tail(&dev->devlist,&cx8802_devlist);
 
-#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE)
-       mutex_init(&dev->frontends.lock);
-       INIT_LIST_HEAD(&dev->frontends.felist);
-
-       if (core->board.num_frontends) {
-               struct videobuf_dvb_frontend *fe;
-               int i;
-
-               printk(KERN_INFO "%s() allocating %d frontend(s)\n", __func__,
-                       core->board.num_frontends);
-               for (i = 1; i <= core->board.num_frontends; i++) {
-                       fe = videobuf_dvb_alloc_frontend(&dev->frontends, i);
-                       if(fe == NULL) {
-                               printk(KERN_ERR "%s() failed to alloc\n",
-                                       __func__);
-                               videobuf_dvb_dealloc_frontends(&dev->frontends);
-                               err = -ENOMEM;
-                               goto fail_free;
-                       }
-               }
-       }
-#endif
-
-       /* Maintain a reference so cx88-video can query the 8802 device. */
-       core->dvbdev = dev;
-
        /* now autoload cx88-dvb or cx88-blackbird */
        request_modules(dev);
        return 0;
  fail_free:
        kfree(dev);
  fail_core:
+       core->dvbdev = NULL;
        cx88_core_put(core,pci_dev);
        return err;
 }
 
        struct btcx_riscmem    stopper;
        u32                    count;
 };
+struct cx88_core;
 
 struct cx88_core {
        struct list_head           devlist;
 
        /* config info -- dvb */
 #if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE)
-       int                        (*prev_set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage);
+       int                        (*prev_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage);
+       void                       (*gate_ctrl)(struct cx88_core  *core, int open);
 #endif
 
        /* state info */