[media] ivtv, ivtv-alsa: Add initial ivtv-alsa interface driver for ivtv

This is a cut-and-paste port of the cx18-alsa driver to
create an ivtv-alsa interface module for the ivtv driver.
It is not actually hooked-up to the PCM stream DMA buffers
from the ivtv driver yet.  That will be done in a coming change,
since that portion is so very different from the cx18 driver.
This code has all or more of the bugs and shortcomings of the
cx18-alsa interface driver: inconsistent use of itvsc->slock,
ivtv-alsa-mixer.c is dead code, assumes 48 ksps regardless
of the actual setting of the audio capture, problems with
proper struct ivtv and struct ivtv_stream housekeeping,
struct ivtv_open_id.v4l2_fh abuse, and $DIETY knows what else.

Signed-off-by: Andy Walls <awalls@md.metrocast.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c
index 5462ce2..eed95a3 100644
--- a/drivers/media/pci/ivtv/ivtv-driver.c
+++ b/drivers/media/pci/ivtv/ivtv-driver.c
@@ -68,6 +68,10 @@
    setting this to 1 you ensure that radio0 is now also radio1. */
 int ivtv_first_minor;
 
+/* Callback for registering extensions */
+int (*ivtv_ext_init)(struct ivtv *);
+EXPORT_SYMBOL(ivtv_ext_init);
+
 /* add your revision and whatnot here */
 static struct pci_device_id ivtv_pci_tbl[] __devinitdata = {
 	{PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV15,
@@ -279,6 +283,34 @@
 
 MODULE_VERSION(IVTV_VERSION);
 
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+	struct ivtv *dev = container_of(work, struct ivtv, request_module_wk);
+
+	/* Make sure ivtv-alsa module is loaded */
+	request_module("ivtv-alsa");
+
+	/* Initialize ivtv-alsa for this instance of the cx18 device */
+	if (ivtv_ext_init != NULL)
+		ivtv_ext_init(dev);
+}
+
+static void request_modules(struct ivtv *dev)
+{
+	INIT_WORK(&dev->request_module_wk, request_module_async);
+	schedule_work(&dev->request_module_wk);
+}
+
+static void flush_request_modules(struct ivtv *dev)
+{
+	flush_work_sync(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#define flush_request_modules(dev)
+#endif /* CONFIG_MODULES */
+
 void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask)
 {
 	itv->irqmask &= ~mask;
@@ -1253,6 +1285,9 @@
 		goto free_streams;
 	}
 	IVTV_INFO("Initialized card: %s\n", itv->card_name);
+
+	/* Load ivtv submodules (ivtv-alsa) */
+	request_modules(itv);
 	return 0;
 
 free_streams:
@@ -1380,6 +1415,8 @@
 
 	IVTV_DEBUG_INFO("Removing card\n");
 
+	flush_request_modules(itv);
+
 	if (test_bit(IVTV_F_I_INITED, &itv->i_flags)) {
 		/* Stop all captures */
 		IVTV_DEBUG_INFO("Stopping all streams\n");