]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - sound/soc/soc-core.c
ASoC: Tune performance of WM8958 revision A
[mirror_ubuntu-artful-kernel.git] / sound / soc / soc-core.c
index 614a8b30d87bdefdb99414f06d1d74438e60cd18..eb950f73076a8cd19092fdbaf20055beca2b141f 100644 (file)
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
-#include <sound/soc-dapm.h>
 #include <sound/initval.h>
 
+#define CREATE_TRACE_POINTS
+#include <trace/events/asoc.h>
+
 #define NAME_SIZE      32
 
 static DEFINE_MUTEX(pcm_mutex);
@@ -238,8 +240,10 @@ static const struct file_operations codec_reg_fops = {
 
 static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
 {
-       codec->debugfs_codec_root = debugfs_create_dir(codec->name ,
-                                                      debugfs_root);
+       struct dentry *debugfs_card_root = codec->card->debugfs_card_root;
+
+       codec->debugfs_codec_root = debugfs_create_dir(codec->name,
+                                                      debugfs_card_root);
        if (!codec->debugfs_codec_root) {
                printk(KERN_WARNING
                       "ASoC: Failed to create codec debugfs directory\n");
@@ -253,20 +257,13 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
                printk(KERN_WARNING
                       "ASoC: Failed to create codec register debugfs file\n");
 
-       codec->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0644,
-                                                    codec->debugfs_codec_root,
-                                                    &codec->pop_time);
-       if (!codec->debugfs_pop_time)
-               printk(KERN_WARNING
-                      "Failed to create pop time debugfs file\n");
-
-       codec->debugfs_dapm = debugfs_create_dir("dapm",
+       codec->dapm.debugfs_dapm = debugfs_create_dir("dapm",
                                                 codec->debugfs_codec_root);
-       if (!codec->debugfs_dapm)
+       if (!codec->dapm.debugfs_dapm)
                printk(KERN_WARNING
                       "Failed to create DAPM debugfs directory\n");
 
-       snd_soc_dapm_debugfs_init(codec);
+       snd_soc_dapm_debugfs_init(&codec->dapm);
 }
 
 static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
@@ -374,6 +371,29 @@ static const struct file_operations platform_list_fops = {
        .llseek = default_llseek,/* read accesses f_pos */
 };
 
+static void soc_init_card_debugfs(struct snd_soc_card *card)
+{
+       card->debugfs_card_root = debugfs_create_dir(card->name,
+                                                    debugfs_root);
+       if (!card->debugfs_card_root) {
+               dev_warn(card->dev,
+                        "ASoC: Failed to create codec debugfs directory\n");
+               return;
+       }
+
+       card->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0644,
+                                                   card->debugfs_card_root,
+                                                   &card->pop_time);
+       if (!card->debugfs_pop_time)
+               dev_warn(card->dev,
+                      "Failed to create pop time debugfs file\n");
+}
+
+static void soc_cleanup_card_debugfs(struct snd_soc_card *card)
+{
+       debugfs_remove_recursive(card->debugfs_card_root);
+}
+
 #else
 
 static inline void soc_init_codec_debugfs(struct snd_soc_codec *codec)
@@ -383,6 +403,14 @@ static inline void soc_init_codec_debugfs(struct snd_soc_codec *codec)
 static inline void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
 {
 }
+
+static inline void soc_init_card_debugfs(struct snd_soc_card *card)
+{
+}
+
+static inline void soc_cleanup_card_debugfs(struct snd_soc_card *card)
+{
+}
 #endif
 
 #ifdef CONFIG_SND_SOC_AC97_BUS
@@ -958,6 +986,7 @@ static int soc_suspend(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
        struct snd_soc_card *card = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
        int i;
 
        /* If the initialization of this soc device failed, there is no codec
@@ -1017,7 +1046,7 @@ static int soc_suspend(struct device *dev)
        /* close any waiting streams and save state */
        for (i = 0; i < card->num_rtd; i++) {
                run_delayed_work(&card->rtd[i].delayed_work);
-               card->rtd[i].codec->suspend_bias_level = card->rtd[i].codec->bias_level;
+               card->rtd[i].codec->dapm.suspend_bias_level = card->rtd[i].codec->dapm.bias_level;
        }
 
        for (i = 0; i < card->num_rtd; i++) {
@@ -1036,12 +1065,11 @@ static int soc_suspend(struct device *dev)
        }
 
        /* suspend all CODECs */
-       for (i = 0; i < card->num_rtd; i++) {
-               struct snd_soc_codec *codec = card->rtd[i].codec;
+       list_for_each_entry(codec, &card->codec_dev_list, card_list) {
                /* If there are paths active then the CODEC will be held with
                 * bias _ON and should not be suspended. */
                if (!codec->suspended && codec->driver->suspend) {
-                       switch (codec->bias_level) {
+                       switch (codec->dapm.bias_level) {
                        case SND_SOC_BIAS_STANDBY:
                        case SND_SOC_BIAS_OFF:
                                codec->driver->suspend(codec, PMSG_SUSPEND);
@@ -1078,6 +1106,7 @@ static void soc_resume_deferred(struct work_struct *work)
        struct snd_soc_card *card =
                        container_of(work, struct snd_soc_card, deferred_resume_work);
        struct platform_device *pdev = to_platform_device(card->dev);
+       struct snd_soc_codec *codec;
        int i;
 
        /* our power state is still SNDRV_CTL_POWER_D3hot from suspend time,
@@ -1103,14 +1132,13 @@ static void soc_resume_deferred(struct work_struct *work)
                        cpu_dai->driver->resume(cpu_dai);
        }
 
-       for (i = 0; i < card->num_rtd; i++) {
-               struct snd_soc_codec *codec = card->rtd[i].codec;
+       list_for_each_entry(codec, &card->codec_dev_list, card_list) {
                /* If the CODEC was idle over suspend then it will have been
                 * left with bias OFF or STANDBY and suspended so we must now
                 * resume.  Otherwise the suspend was suppressed.
                 */
                if (codec->driver->resume && codec->suspended) {
-                       switch (codec->bias_level) {
+                       switch (codec->dapm.bias_level) {
                        case SND_SOC_BIAS_STANDBY:
                        case SND_SOC_BIAS_OFF:
                                codec->driver->resume(codec);
@@ -1346,7 +1374,7 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num)
                }
 
                /* Make sure all DAPM widgets are freed */
-               snd_soc_dapm_free(codec);
+               snd_soc_dapm_free(&codec->dapm);
 
                soc_cleanup_codec_debugfs(codec);
                device_remove_file(&rtd->dev, &dev_attr_codec_reg);
@@ -1368,6 +1396,23 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num)
        }
 }
 
+static void soc_set_name_prefix(struct snd_soc_card *card,
+                               struct snd_soc_codec *codec)
+{
+       int i;
+
+       if (card->prefix_map == NULL)
+               return;
+
+       for (i = 0; i < card->num_prefixes; i++) {
+               struct snd_soc_prefix_map *map = &card->prefix_map[i];
+               if (map->dev_name && !strcmp(codec->name, map->dev_name)) {
+                       codec->name_prefix = map->name_prefix;
+                       break;
+               }
+       }
+}
+
 static void rtd_release(struct device *dev) {}
 
 static int soc_probe_dai_link(struct snd_soc_card *card, int num)
@@ -1377,6 +1422,7 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
        struct snd_soc_codec *codec = rtd->codec;
        struct snd_soc_platform *platform = rtd->platform;
        struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai;
+       const char *temp;
        int ret;
 
        dev_dbg(card->dev, "probe %s dai link %d\n", card->name, num);
@@ -1410,6 +1456,8 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
 
        /* probe the CODEC */
        if (!codec->probed) {
+               codec->dapm.card = card;
+               soc_set_name_prefix(card, codec);
                if (codec->driver->probe) {
                        ret = codec->driver->probe(codec);
                        if (ret < 0) {
@@ -1462,16 +1510,20 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num)
 
        /* now that all clients have probed, initialise the DAI link */
        if (dai_link->init) {
+               /* machine controls, routes and widgets are not prefixed */
+               temp = rtd->codec->name_prefix;
+               rtd->codec->name_prefix = NULL;
                ret = dai_link->init(rtd);
                if (ret < 0) {
                        printk(KERN_ERR "asoc: failed to init %s\n", dai_link->stream_name);
                        return ret;
                }
+               rtd->codec->name_prefix = temp;
        }
 
        /* Make sure all DAPM widgets are instantiated */
-       snd_soc_dapm_new_widgets(codec);
-       snd_soc_dapm_sync(codec);
+       snd_soc_dapm_new_widgets(&codec->dapm);
+       snd_soc_dapm_sync(&codec->dapm);
 
        /* register the rtd device */
        rtd->dev.release = rtd_release;
@@ -1551,6 +1603,129 @@ static void soc_unregister_ac97_dai_link(struct snd_soc_codec *codec)
 }
 #endif
 
+static int soc_probe_aux_dev(struct snd_soc_card *card, int num)
+{
+       struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
+       struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
+       struct snd_soc_codec *codec;
+       const char *temp;
+       int ret = 0;
+
+       /* find CODEC from registered CODECs*/
+       list_for_each_entry(codec, &codec_list, list) {
+               if (!strcmp(codec->name, aux_dev->codec_name)) {
+                       if (codec->probed) {
+                               dev_err(codec->dev,
+                                       "asoc: codec already probed");
+                               ret = -EBUSY;
+                               goto out;
+                       }
+                       break;
+               }
+       }
+
+       if (!try_module_get(codec->dev->driver->owner))
+               return -ENODEV;
+
+       codec->card = card;
+       codec->dapm.card = card;
+
+       soc_set_name_prefix(card, codec);
+       if (codec->driver->probe) {
+               ret = codec->driver->probe(codec);
+               if (ret < 0) {
+                       dev_err(codec->dev, "asoc: failed to probe CODEC");
+                       return ret;
+               }
+       }
+
+       soc_init_codec_debugfs(codec);
+
+       /* mark codec as probed and add to card codec list */
+       codec->probed = 1;
+       list_add(&codec->card_list, &card->codec_dev_list);
+
+       /* now that all clients have probed, initialise the DAI link */
+       if (aux_dev->init) {
+               /* machine controls, routes and widgets are not prefixed */
+               temp = codec->name_prefix;
+               codec->name_prefix = NULL;
+               ret = aux_dev->init(&codec->dapm);
+               if (ret < 0) {
+                       dev_err(codec->dev,
+                               "asoc: failed to init %s\n", aux_dev->name);
+                       return ret;
+               }
+               codec->name_prefix = temp;
+       }
+
+       /* Make sure all DAPM widgets are instantiated */
+       snd_soc_dapm_new_widgets(&codec->dapm);
+       snd_soc_dapm_sync(&codec->dapm);
+
+       /* register the rtd device */
+       rtd->codec = codec;
+       rtd->card = card;
+       rtd->dev.parent = card->dev;
+       rtd->dev.release = rtd_release;
+       rtd->dev.init_name = aux_dev->name;
+       ret = device_register(&rtd->dev);
+       if (ret < 0) {
+               dev_err(codec->dev,
+                       "asoc: failed to register aux runtime device %d\n",
+                       ret);
+               return ret;
+       }
+       rtd->dev_registered = 1;
+
+       /* add DAPM sysfs entries for this codec */
+       ret = snd_soc_dapm_sys_add(&rtd->dev);
+       if (ret < 0)
+               dev_err(codec->dev,
+                       "asoc: failed to add codec dapm sysfs entries\n");
+
+       /* add codec sysfs entries */
+       ret = device_create_file(&rtd->dev, &dev_attr_codec_reg);
+       if (ret < 0)
+               dev_err(codec->dev, "asoc: failed to add codec sysfs files\n");
+
+out:
+       return ret;
+}
+
+static void soc_remove_aux_dev(struct snd_soc_card *card, int num)
+{
+       struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
+       struct snd_soc_codec *codec = rtd->codec;
+       int err;
+
+       /* unregister the rtd device */
+       if (rtd->dev_registered) {
+               device_unregister(&rtd->dev);
+               rtd->dev_registered = 0;
+       }
+
+       /* remove the CODEC */
+       if (codec && codec->probed) {
+               if (codec->driver->remove) {
+                       err = codec->driver->remove(codec);
+                       if (err < 0)
+                               dev_err(codec->dev,
+                                       "asoc: failed to remove %s\n",
+                                       codec->name);
+               }
+
+               /* Make sure all DAPM widgets are freed */
+               snd_soc_dapm_free(&codec->dapm);
+
+               soc_cleanup_codec_debugfs(codec);
+               device_remove_file(&rtd->dev, &dev_attr_codec_reg);
+               codec->probed = 0;
+               list_del(&codec->card_list);
+               module_put(codec->dev->driver->owner);
+       }
+}
+
 static void snd_soc_instantiate_card(struct snd_soc_card *card)
 {
        struct platform_device *pdev = to_platform_device(card->dev);
@@ -1605,6 +1780,15 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
                }
        }
 
+       for (i = 0; i < card->num_aux_devs; i++) {
+               ret = soc_probe_aux_dev(card, i);
+               if (ret < 0) {
+                       pr_err("asoc: failed to add auxiliary devices %s: %d\n",
+                              card->name, ret);
+                       goto probe_aux_dev_err;
+               }
+       }
+
        snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),
                 "%s",  card->name);
        snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),
@@ -1631,6 +1815,10 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
        mutex_unlock(&card->mutex);
        return;
 
+probe_aux_dev_err:
+       for (i = 0; i < card->num_aux_devs; i++)
+               soc_remove_aux_dev(card, i);
+
 probe_dai_err:
        for (i = 0; i < card->num_links; i++)
                soc_remove_dai_link(card, i);
@@ -1667,6 +1855,8 @@ static int soc_probe(struct platform_device *pdev)
        INIT_LIST_HEAD(&card->codec_dev_list);
        INIT_LIST_HEAD(&card->platform_dev_list);
 
+       soc_init_card_debugfs(card);
+
        ret = snd_soc_register_card(card);
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to register card\n");
@@ -1690,10 +1880,16 @@ static int soc_remove(struct platform_device *pdev)
                        run_delayed_work(&rtd->delayed_work);
                }
 
+               /* remove auxiliary devices */
+               for (i = 0; i < card->num_aux_devs; i++)
+                       soc_remove_aux_dev(card, i);
+
                /* remove and free each DAI */
                for (i = 0; i < card->num_rtd; i++)
                        soc_remove_dai_link(card, i);
 
+               soc_cleanup_card_debugfs(card);
+
                /* remove the card */
                if (card->remove)
                        card->remove(pdev);
@@ -1877,6 +2073,27 @@ void snd_soc_free_ac97_codec(struct snd_soc_codec *codec)
 }
 EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
 
+unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+       unsigned int ret;
+
+       ret = codec->driver->read(codec, reg);
+       dev_dbg(codec->dev, "read %x => %x\n", reg, ret);
+       trace_snd_soc_reg_read(codec, reg, ret);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_read);
+
+unsigned int snd_soc_write(struct snd_soc_codec *codec,
+                          unsigned int reg, unsigned int val)
+{
+       dev_dbg(codec->dev, "write %x = %x\n", reg, val);
+       trace_snd_soc_reg_write(codec, reg, val);
+       return codec->driver->write(codec, reg, val);
+}
+EXPORT_SYMBOL_GPL(snd_soc_write);
+
 /**
  * snd_soc_update_bits - update codec register bits
  * @codec: audio codec
@@ -2017,14 +2234,22 @@ int snd_soc_add_controls(struct snd_soc_codec *codec,
        const struct snd_kcontrol_new *controls, int num_controls)
 {
        struct snd_card *card = codec->card->snd_card;
+       char prefixed_name[44], *name;
        int err, i;
 
        for (i = 0; i < num_controls; i++) {
                const struct snd_kcontrol_new *control = &controls[i];
-               err = snd_ctl_add(card, snd_soc_cnew(control, codec, NULL));
+               if (codec->name_prefix) {
+                       snprintf(prefixed_name, sizeof(prefixed_name), "%s %s",
+                                codec->name_prefix, control->name);
+                       name = prefixed_name;
+               } else {
+                       name = control->name;
+               }
+               err = snd_ctl_add(card, snd_soc_cnew(control, codec, name));
                if (err < 0) {
                        dev_err(codec->dev, "%s: Failed to add %s: %d\n",
-                               codec->name, control->name, err);
+                               codec->name, name, err);
                        return err;
                }
        }
@@ -2861,10 +3086,12 @@ static int snd_soc_register_card(struct snd_soc_card *card)
        if (!card->name || !card->dev)
                return -EINVAL;
 
-       card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) * card->num_links,
-                       GFP_KERNEL);
+       card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) *
+                           (card->num_links + card->num_aux_devs),
+                           GFP_KERNEL);
        if (card->rtd == NULL)
                return -ENOMEM;
+       card->rtd_aux = &card->rtd[card->num_links];
 
        for (i = 0; i < card->num_links; i++)
                card->rtd[i].dai_link = &card->dai_link[i];
@@ -3043,8 +3270,10 @@ int snd_soc_register_dais(struct device *dev,
        for (i = 0; i < count; i++) {
 
                dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
-               if (dai == NULL)
-                       return -ENOMEM;
+               if (dai == NULL) {
+                       ret = -ENOMEM;
+                       goto err;
+               }
 
                /* create DAI component name */
                dai->name = fmt_multiple_name(dev, &dai_drv[i]);
@@ -3217,30 +3446,25 @@ int snd_soc_register_codec(struct device *dev,
                return -ENOMEM;
        }
 
-       /* allocate CODEC register cache */
-       if (codec_drv->reg_cache_size && codec_drv->reg_word_size) {
-
-               if (codec_drv->reg_cache_default)
-                       codec->reg_cache = kmemdup(codec_drv->reg_cache_default,
-                               codec_drv->reg_cache_size * codec_drv->reg_word_size, GFP_KERNEL);
-               else
-                       codec->reg_cache = kzalloc(codec_drv->reg_cache_size *
-                               codec_drv->reg_word_size, GFP_KERNEL);
-
-               if (codec->reg_cache == NULL) {
-                       kfree(codec->name);
-                       kfree(codec);
-                       return -ENOMEM;
-               }
-       }
-
+       INIT_LIST_HEAD(&codec->dapm.widgets);
+       INIT_LIST_HEAD(&codec->dapm.paths);
+       codec->dapm.bias_level = SND_SOC_BIAS_OFF;
+       codec->dapm.dev = dev;
+       codec->dapm.codec = codec;
        codec->dev = dev;
        codec->driver = codec_drv;
-       codec->bias_level = SND_SOC_BIAS_OFF;
        codec->num_dai = num_dai;
        mutex_init(&codec->mutex);
-       INIT_LIST_HEAD(&codec->dapm_widgets);
-       INIT_LIST_HEAD(&codec->dapm_paths);
+
+       /* allocate CODEC register cache */
+       if (codec_drv->reg_cache_size && codec_drv->reg_word_size) {
+               ret = snd_soc_cache_init(codec);
+               if (ret < 0) {
+                       dev_err(codec->dev, "Failed to set cache compression type: %d\n",
+                               ret);
+                       goto error_cache;
+               }
+       }
 
        for (i = 0; i < num_dai; i++) {
                fixup_codec_formats(&dai_drv[i].playback);
@@ -3251,7 +3475,7 @@ int snd_soc_register_codec(struct device *dev,
        if (num_dai) {
                ret = snd_soc_register_dais(dev, dai_drv, num_dai);
                if (ret < 0)
-                       goto error;
+                       goto error_dais;
        }
 
        mutex_lock(&client_mutex);
@@ -3262,12 +3486,9 @@ int snd_soc_register_codec(struct device *dev,
        pr_debug("Registered codec '%s'\n", codec->name);
        return 0;
 
-error:
-       for (i--; i >= 0; i--)
-               snd_soc_unregister_dai(dev);
-
-       if (codec->reg_cache)
-               kfree(codec->reg_cache);
+error_dais:
+       snd_soc_cache_exit(codec);
+error_cache:
        kfree(codec->name);
        kfree(codec);
        return ret;
@@ -3301,8 +3522,7 @@ found:
 
        pr_debug("Unregistered codec '%s'\n", codec->name);
 
-       if (codec->reg_cache)
-               kfree(codec->reg_cache);
+       snd_soc_cache_exit(codec);
        kfree(codec->name);
        kfree(codec);
 }