Merge branch 'fix/usb-audio' into for-linus
[safe/jmp/linux-2.6] / sound / soc / soc-core.c
index 3411439..ec3f8bb 100644 (file)
@@ -43,6 +43,15 @@ static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
 static struct dentry *debugfs_root;
 #endif
 
+static DEFINE_MUTEX(client_mutex);
+static LIST_HEAD(card_list);
+static LIST_HEAD(dai_list);
+static LIST_HEAD(platform_list);
+static LIST_HEAD(codec_list);
+
+static int snd_soc_register_card(struct snd_soc_card *card);
+static int snd_soc_unregister_card(struct snd_soc_card *card);
+
 /*
  * This is a timeout to do a DAPM powerdown after a stream is closed().
  * It can be used to eliminate pops between different playback streams, e.g.
@@ -650,9 +659,9 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
        for (i = 0; i < card->num_links; i++) {
                struct snd_soc_dai  *cpu_dai = card->dai_link[i].cpu_dai;
                if (cpu_dai->suspend && !cpu_dai->ac97_control)
-                       cpu_dai->suspend(pdev, cpu_dai);
+                       cpu_dai->suspend(cpu_dai);
                if (platform->suspend)
-                       platform->suspend(pdev, cpu_dai);
+                       platform->suspend(cpu_dai);
        }
 
        /* close any waiting streams and save state */
@@ -676,7 +685,7 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state)
        for (i = 0; i < card->num_links; i++) {
                struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
                if (cpu_dai->suspend && cpu_dai->ac97_control)
-                       cpu_dai->suspend(pdev, cpu_dai);
+                       cpu_dai->suspend(cpu_dai);
        }
 
        if (card->suspend_post)
@@ -712,7 +721,7 @@ static void soc_resume_deferred(struct work_struct *work)
        for (i = 0; i < card->num_links; i++) {
                struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
                if (cpu_dai->resume && cpu_dai->ac97_control)
-                       cpu_dai->resume(pdev, cpu_dai);
+                       cpu_dai->resume(cpu_dai);
        }
 
        if (codec_dev->resume)
@@ -739,9 +748,9 @@ static void soc_resume_deferred(struct work_struct *work)
        for (i = 0; i < card->num_links; i++) {
                struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
                if (cpu_dai->resume && !cpu_dai->ac97_control)
-                       cpu_dai->resume(pdev, cpu_dai);
+                       cpu_dai->resume(cpu_dai);
                if (platform->resume)
-                       platform->resume(pdev, cpu_dai);
+                       platform->resume(cpu_dai);
        }
 
        if (card->resume_post)
@@ -772,22 +781,79 @@ static int soc_resume(struct platform_device *pdev)
 #define soc_resume     NULL
 #endif
 
-/* probes a new socdev */
-static int soc_probe(struct platform_device *pdev)
+static void snd_soc_instantiate_card(struct snd_soc_card *card)
 {
-       int ret = 0, i;
-       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-       struct snd_soc_card *card = socdev->card;
-       struct snd_soc_platform *platform = card->platform;
-       struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
+       struct platform_device *pdev = container_of(card->dev,
+                                                   struct platform_device,
+                                                   dev);
+       struct snd_soc_codec_device *codec_dev = card->socdev->codec_dev;
+       struct snd_soc_platform *platform;
+       struct snd_soc_dai *dai;
+       int i, found, ret, ac97;
+
+       if (card->instantiated)
+               return;
+
+       found = 0;
+       list_for_each_entry(platform, &platform_list, list)
+               if (card->platform == platform) {
+                       found = 1;
+                       break;
+               }
+       if (!found) {
+               dev_dbg(card->dev, "Platform %s not registered\n",
+                       card->platform->name);
+               return;
+       }
 
-       /* Bodge while we push things out of socdev */
-       card->socdev = socdev;
+       ac97 = 0;
+       for (i = 0; i < card->num_links; i++) {
+               found = 0;
+               list_for_each_entry(dai, &dai_list, list)
+                       if (card->dai_link[i].cpu_dai == dai) {
+                               found = 1;
+                               break;
+                       }
+               if (!found) {
+                       dev_dbg(card->dev, "DAI %s not registered\n",
+                               card->dai_link[i].cpu_dai->name);
+                       return;
+               }
+
+               if (card->dai_link[i].cpu_dai->ac97_control)
+                       ac97 = 1;
+       }
+
+       /* If we have AC97 in the system then don't wait for the
+        * codec.  This will need revisiting if we have to handle
+        * systems with mixed AC97 and non-AC97 parts.  Only check for
+        * DAIs currently; we can't do this per link since some AC97
+        * codecs have non-AC97 DAIs.
+        */
+       if (!ac97)
+               for (i = 0; i < card->num_links; i++) {
+                       found = 0;
+                       list_for_each_entry(dai, &dai_list, list)
+                               if (card->dai_link[i].codec_dai == dai) {
+                                       found = 1;
+                                       break;
+                               }
+                       if (!found) {
+                               dev_dbg(card->dev, "DAI %s not registered\n",
+                                       card->dai_link[i].codec_dai->name);
+                               return;
+                       }
+               }
+
+       /* Note that we do not current check for codec components */
+
+       dev_dbg(card->dev, "All components present, instantiating\n");
 
+       /* Found everything, bring it up */
        if (card->probe) {
                ret = card->probe(pdev);
                if (ret < 0)
-                       return ret;
+                       return;
        }
 
        for (i = 0; i < card->num_links; i++) {
@@ -818,7 +884,9 @@ static int soc_probe(struct platform_device *pdev)
        INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
 #endif
 
-       return 0;
+       card->instantiated = 1;
+
+       return;
 
 platform_err:
        if (codec_dev->remove)
@@ -833,8 +901,38 @@ cpu_dai_err:
 
        if (card->remove)
                card->remove(pdev);
+}
 
-       return ret;
+/*
+ * Attempt to initialise any uninitalised cards.  Must be called with
+ * client_mutex.
+ */
+static void snd_soc_instantiate_cards(void)
+{
+       struct snd_soc_card *card;
+       list_for_each_entry(card, &card_list, list)
+               snd_soc_instantiate_card(card);
+}
+
+/* probes a new socdev */
+static int soc_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_card *card = socdev->card;
+
+       /* Bodge while we push things out of socdev */
+       card->socdev = socdev;
+
+       /* Bodge while we unpick instantiation */
+       card->dev = &pdev->dev;
+       ret = snd_soc_register_card(card);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "Failed to register card\n");
+               return ret;
+       }
+
+       return 0;
 }
 
 /* removes a socdev */
@@ -863,6 +961,8 @@ static int soc_remove(struct platform_device *pdev)
        if (card->remove)
                card->remove(pdev);
 
+       snd_soc_unregister_card(card);
+
        return 0;
 }
 
@@ -1200,6 +1300,8 @@ EXPORT_SYMBOL_GPL(snd_soc_test_bits);
 /**
  * snd_soc_new_pcms - create new sound card and pcms
  * @socdev: the SoC audio device
+ * @idx: ALSA card index
+ * @xid: card identification
  *
  * Create a new sound card based upon the codec and interface pcms.
  *
@@ -1283,7 +1385,10 @@ int snd_soc_init_card(struct snd_soc_device *socdev)
 
        mutex_lock(&codec->mutex);
 #ifdef CONFIG_SND_SOC_AC97_BUS
-       if (ac97) {
+       /* Only instantiate AC97 if not already done by the adaptor
+        * for the generic AC97 subsystem.
+        */
+       if (ac97 && strcmp(codec->name, "AC97") != 0) {
                ret = soc_ac97_dev_register(codec);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: AC97 device register failed\n");
@@ -1372,7 +1477,7 @@ EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams);
  * snd_soc_cnew - create new control
  * @_template: control template
  * @data: control private data
- * @lnng_name: control long name
+ * @long_name: control long name
  *
  * Create a new mixer control from a template control.
  *
@@ -1422,7 +1527,7 @@ EXPORT_SYMBOL_GPL(snd_soc_info_enum_double);
 /**
  * snd_soc_get_enum_double - enumerated double mixer get callback
  * @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
  *
  * Callback to get the value of a double enumerated mixer.
  *
@@ -1451,7 +1556,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_enum_double);
 /**
  * snd_soc_put_enum_double - enumerated double mixer put callback
  * @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
  *
  * Callback to set the value of a double enumerated mixer.
  *
@@ -1483,6 +1588,80 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
 EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
 
 /**
+ * snd_soc_get_value_enum_double - semi enumerated double mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a double semi enumerated mixer.
+ *
+ * Semi enumerated mixer: the enumerated items are referred as values. Can be
+ * used for handling bitfield coded enumeration for example.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       unsigned short reg_val, val, mux;
+
+       reg_val = snd_soc_read(codec, e->reg);
+       val = (reg_val >> e->shift_l) & e->mask;
+       for (mux = 0; mux < e->max; mux++) {
+               if (val == e->values[mux])
+                       break;
+       }
+       ucontrol->value.enumerated.item[0] = mux;
+       if (e->shift_l != e->shift_r) {
+               val = (reg_val >> e->shift_r) & e->mask;
+               for (mux = 0; mux < e->max; mux++) {
+                       if (val == e->values[mux])
+                               break;
+               }
+               ucontrol->value.enumerated.item[1] = mux;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_value_enum_double);
+
+/**
+ * snd_soc_put_value_enum_double - semi enumerated double mixer put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a double semi enumerated mixer.
+ *
+ * Semi enumerated mixer: the enumerated items are referred as values. Can be
+ * used for handling bitfield coded enumeration for example.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       unsigned short val;
+       unsigned short mask;
+
+       if (ucontrol->value.enumerated.item[0] > e->max - 1)
+               return -EINVAL;
+       val = e->values[ucontrol->value.enumerated.item[0]] << e->shift_l;
+       mask = e->mask << e->shift_l;
+       if (e->shift_l != e->shift_r) {
+               if (ucontrol->value.enumerated.item[1] > e->max - 1)
+                       return -EINVAL;
+               val |= e->values[ucontrol->value.enumerated.item[1]] << e->shift_r;
+               mask |= e->mask << e->shift_r;
+       }
+
+       return snd_soc_update_bits(codec, e->reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double);
+
+/**
  * snd_soc_info_enum_ext - external enumerated single mixer info callback
  * @kcontrol: mixer control
  * @uinfo: control element information
@@ -1568,7 +1747,7 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
 /**
  * snd_soc_get_volsw - single mixer get callback
  * @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
  *
  * Callback to get the value of a single mixer control.
  *
@@ -1607,7 +1786,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw);
 /**
  * snd_soc_put_volsw - single mixer put callback
  * @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
  *
  * Callback to set the value of a single mixer control.
  *
@@ -1675,7 +1854,7 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r);
 /**
  * snd_soc_get_volsw_2r - double mixer get callback
  * @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
  *
  * Callback to get the value of a double mixer control that spans 2 registers.
  *
@@ -1712,7 +1891,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_2r);
 /**
  * snd_soc_put_volsw_2r - double mixer set callback
  * @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
  *
  * Callback to set the value of a double mixer control that spans 2 registers.
  *
@@ -1782,7 +1961,7 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw_s8);
 /**
  * snd_soc_get_volsw_s8 - signed mixer get callback
  * @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
  *
  * Callback to get the value of a signed mixer control.
  *
@@ -1809,7 +1988,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_s8);
 /**
  * snd_soc_put_volsw_sgn - signed mixer put callback
  * @kcontrol: mixer control
- * @uinfo: control element information
+ * @ucontrol: control element information
  *
  * Callback to set the value of a signed mixer control.
  *
@@ -1854,7 +2033,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
 /**
  * snd_soc_dai_set_clkdiv - configure DAI clock dividers.
  * @dai: DAI
- * @clk_id: DAI specific clock divider ID
+ * @div_id: DAI specific clock divider ID
  * @div: new clock divisor.
  *
  * Configures the clock dividers. This is used to derive the best DAI bit and
@@ -1957,7 +2136,217 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute)
 }
 EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
 
-static int __devinit snd_soc_init(void)
+/**
+ * snd_soc_register_card - Register a card with the ASoC core
+ *
+ * @card: Card to register
+ *
+ * Note that currently this is an internal only function: it will be
+ * exposed to machine drivers after further backporting of ASoC v2
+ * registration APIs.
+ */
+static int snd_soc_register_card(struct snd_soc_card *card)
+{
+       if (!card->name || !card->dev)
+               return -EINVAL;
+
+       INIT_LIST_HEAD(&card->list);
+       card->instantiated = 0;
+
+       mutex_lock(&client_mutex);
+       list_add(&card->list, &card_list);
+       snd_soc_instantiate_cards();
+       mutex_unlock(&client_mutex);
+
+       dev_dbg(card->dev, "Registered card '%s'\n", card->name);
+
+       return 0;
+}
+
+/**
+ * snd_soc_unregister_card - Unregister a card with the ASoC core
+ *
+ * @card: Card to unregister
+ *
+ * Note that currently this is an internal only function: it will be
+ * exposed to machine drivers after further backporting of ASoC v2
+ * registration APIs.
+ */
+static int snd_soc_unregister_card(struct snd_soc_card *card)
+{
+       mutex_lock(&client_mutex);
+       list_del(&card->list);
+       mutex_unlock(&client_mutex);
+
+       dev_dbg(card->dev, "Unregistered card '%s'\n", card->name);
+
+       return 0;
+}
+
+/**
+ * snd_soc_register_dai - Register a DAI with the ASoC core
+ *
+ * @dai: DAI to register
+ */
+int snd_soc_register_dai(struct snd_soc_dai *dai)
+{
+       if (!dai->name)
+               return -EINVAL;
+
+       /* The device should become mandatory over time */
+       if (!dai->dev)
+               printk(KERN_WARNING "No device for DAI %s\n", dai->name);
+
+       INIT_LIST_HEAD(&dai->list);
+
+       mutex_lock(&client_mutex);
+       list_add(&dai->list, &dai_list);
+       snd_soc_instantiate_cards();
+       mutex_unlock(&client_mutex);
+
+       pr_debug("Registered DAI '%s'\n", dai->name);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_register_dai);
+
+/**
+ * snd_soc_unregister_dai - Unregister a DAI from the ASoC core
+ *
+ * @dai: DAI to unregister
+ */
+void snd_soc_unregister_dai(struct snd_soc_dai *dai)
+{
+       mutex_lock(&client_mutex);
+       list_del(&dai->list);
+       mutex_unlock(&client_mutex);
+
+       pr_debug("Unregistered DAI '%s'\n", dai->name);
+}
+EXPORT_SYMBOL_GPL(snd_soc_unregister_dai);
+
+/**
+ * snd_soc_register_dais - Register multiple DAIs with the ASoC core
+ *
+ * @dai: Array of DAIs to register
+ * @count: Number of DAIs
+ */
+int snd_soc_register_dais(struct snd_soc_dai *dai, size_t count)
+{
+       int i, ret;
+
+       for (i = 0; i < count; i++) {
+               ret = snd_soc_register_dai(&dai[i]);
+               if (ret != 0)
+                       goto err;
+       }
+
+       return 0;
+
+err:
+       for (i--; i >= 0; i--)
+               snd_soc_unregister_dai(&dai[i]);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_register_dais);
+
+/**
+ * snd_soc_unregister_dais - Unregister multiple DAIs from the ASoC core
+ *
+ * @dai: Array of DAIs to unregister
+ * @count: Number of DAIs
+ */
+void snd_soc_unregister_dais(struct snd_soc_dai *dai, size_t count)
+{
+       int i;
+
+       for (i = 0; i < count; i++)
+               snd_soc_unregister_dai(&dai[i]);
+}
+EXPORT_SYMBOL_GPL(snd_soc_unregister_dais);
+
+/**
+ * snd_soc_register_platform - Register a platform with the ASoC core
+ *
+ * @platform: platform to register
+ */
+int snd_soc_register_platform(struct snd_soc_platform *platform)
+{
+       if (!platform->name)
+               return -EINVAL;
+
+       INIT_LIST_HEAD(&platform->list);
+
+       mutex_lock(&client_mutex);
+       list_add(&platform->list, &platform_list);
+       snd_soc_instantiate_cards();
+       mutex_unlock(&client_mutex);
+
+       pr_debug("Registered platform '%s'\n", platform->name);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_register_platform);
+
+/**
+ * snd_soc_unregister_platform - Unregister a platform from the ASoC core
+ *
+ * @platform: platform to unregister
+ */
+void snd_soc_unregister_platform(struct snd_soc_platform *platform)
+{
+       mutex_lock(&client_mutex);
+       list_del(&platform->list);
+       mutex_unlock(&client_mutex);
+
+       pr_debug("Unregistered platform '%s'\n", platform->name);
+}
+EXPORT_SYMBOL_GPL(snd_soc_unregister_platform);
+
+/**
+ * snd_soc_register_codec - Register a codec with the ASoC core
+ *
+ * @codec: codec to register
+ */
+int snd_soc_register_codec(struct snd_soc_codec *codec)
+{
+       if (!codec->name)
+               return -EINVAL;
+
+       /* The device should become mandatory over time */
+       if (!codec->dev)
+               printk(KERN_WARNING "No device for codec %s\n", codec->name);
+
+       INIT_LIST_HEAD(&codec->list);
+
+       mutex_lock(&client_mutex);
+       list_add(&codec->list, &codec_list);
+       snd_soc_instantiate_cards();
+       mutex_unlock(&client_mutex);
+
+       pr_debug("Registered codec '%s'\n", codec->name);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_register_codec);
+
+/**
+ * snd_soc_unregister_codec - Unregister a codec from the ASoC core
+ *
+ * @codec: codec to unregister
+ */
+void snd_soc_unregister_codec(struct snd_soc_codec *codec)
+{
+       mutex_lock(&client_mutex);
+       list_del(&codec->list);
+       mutex_unlock(&client_mutex);
+
+       pr_debug("Unregistered codec '%s'\n", codec->name);
+}
+EXPORT_SYMBOL_GPL(snd_soc_unregister_codec);
+
+static int __init snd_soc_init(void)
 {
 #ifdef CONFIG_DEBUG_FS
        debugfs_root = debugfs_create_dir("asoc", NULL);