aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJarkko Nikula <jhnikula@gmail.com>2010-11-25 10:47:38 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2010-11-30 09:39:00 -0500
commit2eea392d0a28a0a07d36a9da544eb40f81bf4cb6 (patch)
tree6eebdb530c1d73fdf60cb0408f1f1808d47a6b20
parent09c74a9d0b0bedff16d0881db8cc0054a5e34f47 (diff)
ASoC: Add support for optional auxiliary dailess codecs
This makes possible to register auxiliary dailess codecs in a machine driver. Term dailess is used here for amplifiers and codecs without DAI or DAI being unused. Dailess auxiliary codecs are kept in struct snd_soc_aux_dev and those codecs are probed after initializing the DAI links. There are no major differences between DAI link codecs and dailess codecs in ASoC core point of view. DAPM handles them equally and sysfs and debugfs directories for dailess codecs are similar except the pmdown_time node is not created. Only suspend and resume functions are modified to traverse all probed codecs instead of DAI link codecs. Example below shows a dailess codec registration. struct snd_soc_aux_dev foo_aux_dev[] = { { .name = "Amp", .codec_name = "codec.2", .init = foo_init2, }, }; static struct snd_soc_card card = { ... .aux_dev = foo_aux_dev, .num_aux_devs = ARRAY_SIZE(foo_aux_dev), }; Signed-off-by: Jarkko Nikula <jhnikula@gmail.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-rw-r--r--include/sound/soc.h17
-rw-r--r--sound/soc/soc-core.c155
2 files changed, 166 insertions, 6 deletions
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 9e593cf1440b..4a9195c5ef2d 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -583,6 +583,14 @@ struct snd_soc_prefix_map {
583 const char *name_prefix; 583 const char *name_prefix;
584}; 584};
585 585
586struct snd_soc_aux_dev {
587 const char *name; /* Codec name */
588 const char *codec_name; /* for multi-codec */
589
590 /* codec/machine specific init - e.g. add machine controls */
591 int (*init)(struct snd_soc_dapm_context *dapm);
592};
593
586/* SoC card */ 594/* SoC card */
587struct snd_soc_card { 595struct snd_soc_card {
588 const char *name; 596 const char *name;
@@ -624,6 +632,15 @@ struct snd_soc_card {
624 struct snd_soc_prefix_map *prefix_map; 632 struct snd_soc_prefix_map *prefix_map;
625 int num_prefixes; 633 int num_prefixes;
626 634
635 /*
636 * optional auxiliary devices such as amplifiers or codecs with DAI
637 * link unused
638 */
639 struct snd_soc_aux_dev *aux_dev;
640 int num_aux_devs;
641 struct snd_soc_pcm_runtime *rtd_aux;
642 int num_aux_rtd;
643
627 struct work_struct deferred_resume_work; 644 struct work_struct deferred_resume_work;
628 645
629 /* lists of probed devices belonging to this card */ 646 /* lists of probed devices belonging to this card */
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index c8d8dde7f4dd..a7670d5ac13d 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -986,6 +986,7 @@ static int soc_suspend(struct device *dev)
986{ 986{
987 struct platform_device *pdev = to_platform_device(dev); 987 struct platform_device *pdev = to_platform_device(dev);
988 struct snd_soc_card *card = platform_get_drvdata(pdev); 988 struct snd_soc_card *card = platform_get_drvdata(pdev);
989 struct snd_soc_codec *codec;
989 int i; 990 int i;
990 991
991 /* If the initialization of this soc device failed, there is no codec 992 /* If the initialization of this soc device failed, there is no codec
@@ -1064,8 +1065,7 @@ static int soc_suspend(struct device *dev)
1064 } 1065 }
1065 1066
1066 /* suspend all CODECs */ 1067 /* suspend all CODECs */
1067 for (i = 0; i < card->num_rtd; i++) { 1068 list_for_each_entry(codec, &card->codec_dev_list, card_list) {
1068 struct snd_soc_codec *codec = card->rtd[i].codec;
1069 /* If there are paths active then the CODEC will be held with 1069 /* If there are paths active then the CODEC will be held with
1070 * bias _ON and should not be suspended. */ 1070 * bias _ON and should not be suspended. */
1071 if (!codec->suspended && codec->driver->suspend) { 1071 if (!codec->suspended && codec->driver->suspend) {
@@ -1106,6 +1106,7 @@ static void soc_resume_deferred(struct work_struct *work)
1106 struct snd_soc_card *card = 1106 struct snd_soc_card *card =
1107 container_of(work, struct snd_soc_card, deferred_resume_work); 1107 container_of(work, struct snd_soc_card, deferred_resume_work);
1108 struct platform_device *pdev = to_platform_device(card->dev); 1108 struct platform_device *pdev = to_platform_device(card->dev);
1109 struct snd_soc_codec *codec;
1109 int i; 1110 int i;
1110 1111
1111 /* our power state is still SNDRV_CTL_POWER_D3hot from suspend time, 1112 /* our power state is still SNDRV_CTL_POWER_D3hot from suspend time,
@@ -1131,8 +1132,7 @@ static void soc_resume_deferred(struct work_struct *work)
1131 cpu_dai->driver->resume(cpu_dai); 1132 cpu_dai->driver->resume(cpu_dai);
1132 } 1133 }
1133 1134
1134 for (i = 0; i < card->num_rtd; i++) { 1135 list_for_each_entry(codec, &card->codec_dev_list, card_list) {
1135 struct snd_soc_codec *codec = card->rtd[i].codec;
1136 /* If the CODEC was idle over suspend then it will have been 1136 /* If the CODEC was idle over suspend then it will have been
1137 * left with bias OFF or STANDBY and suspended so we must now 1137 * left with bias OFF or STANDBY and suspended so we must now
1138 * resume. Otherwise the suspend was suppressed. 1138 * resume. Otherwise the suspend was suppressed.
@@ -1603,6 +1603,130 @@ static void soc_unregister_ac97_dai_link(struct snd_soc_codec *codec)
1603} 1603}
1604#endif 1604#endif
1605 1605
1606static int soc_probe_aux_dev(struct snd_soc_card *card, int num)
1607{
1608 struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
1609 struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
1610 struct snd_soc_codec *codec;
1611 const char *temp;
1612 int ret = 0;
1613
1614 /* find CODEC from registered CODECs*/
1615 list_for_each_entry(codec, &codec_list, list) {
1616 if (!strcmp(codec->name, aux_dev->codec_name)) {
1617 if (codec->probed) {
1618 dev_err(codec->dev,
1619 "asoc: codec already probed");
1620 ret = -EBUSY;
1621 goto out;
1622 }
1623 break;
1624 }
1625 }
1626
1627 if (!try_module_get(codec->dev->driver->owner))
1628 return -ENODEV;
1629
1630 codec->card = card;
1631 codec->dapm.card = card;
1632
1633 soc_set_name_prefix(card, codec);
1634 if (codec->driver->probe) {
1635 ret = codec->driver->probe(codec);
1636 if (ret < 0) {
1637 dev_err(codec->dev, "asoc: failed to probe CODEC");
1638 return ret;
1639 }
1640 }
1641
1642 soc_init_codec_debugfs(codec);
1643
1644 /* mark codec as probed and add to card codec list */
1645 codec->probed = 1;
1646 list_add(&codec->card_list, &card->codec_dev_list);
1647 list_add(&codec->dapm.list, &card->dapm_list);
1648
1649 /* now that all clients have probed, initialise the DAI link */
1650 if (aux_dev->init) {
1651 /* machine controls, routes and widgets are not prefixed */
1652 temp = codec->name_prefix;
1653 codec->name_prefix = NULL;
1654 ret = aux_dev->init(&codec->dapm);
1655 if (ret < 0) {
1656 dev_err(codec->dev,
1657 "asoc: failed to init %s\n", aux_dev->name);
1658 return ret;
1659 }
1660 codec->name_prefix = temp;
1661 }
1662
1663 /* Make sure all DAPM widgets are instantiated */
1664 snd_soc_dapm_new_widgets(&codec->dapm);
1665 snd_soc_dapm_sync(&codec->dapm);
1666
1667 /* register the rtd device */
1668 rtd->codec = codec;
1669 rtd->card = card;
1670 rtd->dev.parent = card->dev;
1671 rtd->dev.release = rtd_release;
1672 rtd->dev.init_name = aux_dev->name;
1673 ret = device_register(&rtd->dev);
1674 if (ret < 0) {
1675 dev_err(codec->dev,
1676 "asoc: failed to register aux runtime device %d\n",
1677 ret);
1678 return ret;
1679 }
1680 rtd->dev_registered = 1;
1681
1682 /* add DAPM sysfs entries for this codec */
1683 ret = snd_soc_dapm_sys_add(&rtd->dev);
1684 if (ret < 0)
1685 dev_err(codec->dev,
1686 "asoc: failed to add codec dapm sysfs entries\n");
1687
1688 /* add codec sysfs entries */
1689 ret = device_create_file(&rtd->dev, &dev_attr_codec_reg);
1690 if (ret < 0)
1691 dev_err(codec->dev, "asoc: failed to add codec sysfs files\n");
1692
1693out:
1694 return ret;
1695}
1696
1697static void soc_remove_aux_dev(struct snd_soc_card *card, int num)
1698{
1699 struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
1700 struct snd_soc_codec *codec = rtd->codec;
1701 int err;
1702
1703 /* unregister the rtd device */
1704 if (rtd->dev_registered) {
1705 device_unregister(&rtd->dev);
1706 rtd->dev_registered = 0;
1707 }
1708
1709 /* remove the CODEC */
1710 if (codec && codec->probed) {
1711 if (codec->driver->remove) {
1712 err = codec->driver->remove(codec);
1713 if (err < 0)
1714 dev_err(codec->dev,
1715 "asoc: failed to remove %s\n",
1716 codec->name);
1717 }
1718
1719 /* Make sure all DAPM widgets are freed */
1720 snd_soc_dapm_free(&codec->dapm);
1721
1722 soc_cleanup_codec_debugfs(codec);
1723 device_remove_file(&rtd->dev, &dev_attr_codec_reg);
1724 codec->probed = 0;
1725 list_del(&codec->card_list);
1726 module_put(codec->dev->driver->owner);
1727 }
1728}
1729
1606static void snd_soc_instantiate_card(struct snd_soc_card *card) 1730static void snd_soc_instantiate_card(struct snd_soc_card *card)
1607{ 1731{
1608 struct platform_device *pdev = to_platform_device(card->dev); 1732 struct platform_device *pdev = to_platform_device(card->dev);
@@ -1657,6 +1781,15 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
1657 } 1781 }
1658 } 1782 }
1659 1783
1784 for (i = 0; i < card->num_aux_devs; i++) {
1785 ret = soc_probe_aux_dev(card, i);
1786 if (ret < 0) {
1787 pr_err("asoc: failed to add auxiliary devices %s: %d\n",
1788 card->name, ret);
1789 goto probe_aux_dev_err;
1790 }
1791 }
1792
1660 snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname), 1793 snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),
1661 "%s", card->name); 1794 "%s", card->name);
1662 snprintf(card->snd_card->longname, sizeof(card->snd_card->longname), 1795 snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),
@@ -1683,6 +1816,10 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
1683 mutex_unlock(&card->mutex); 1816 mutex_unlock(&card->mutex);
1684 return; 1817 return;
1685 1818
1819probe_aux_dev_err:
1820 for (i = 0; i < card->num_aux_devs; i++)
1821 soc_remove_aux_dev(card, i);
1822
1686probe_dai_err: 1823probe_dai_err:
1687 for (i = 0; i < card->num_links; i++) 1824 for (i = 0; i < card->num_links; i++)
1688 soc_remove_dai_link(card, i); 1825 soc_remove_dai_link(card, i);
@@ -1744,6 +1881,10 @@ static int soc_remove(struct platform_device *pdev)
1744 run_delayed_work(&rtd->delayed_work); 1881 run_delayed_work(&rtd->delayed_work);
1745 } 1882 }
1746 1883
1884 /* remove auxiliary devices */
1885 for (i = 0; i < card->num_aux_devs; i++)
1886 soc_remove_aux_dev(card, i);
1887
1747 /* remove and free each DAI */ 1888 /* remove and free each DAI */
1748 for (i = 0; i < card->num_rtd; i++) 1889 for (i = 0; i < card->num_rtd; i++)
1749 soc_remove_dai_link(card, i); 1890 soc_remove_dai_link(card, i);
@@ -2946,10 +3087,12 @@ static int snd_soc_register_card(struct snd_soc_card *card)
2946 if (!card->name || !card->dev) 3087 if (!card->name || !card->dev)
2947 return -EINVAL; 3088 return -EINVAL;
2948 3089
2949 card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) * card->num_links, 3090 card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) *
2950 GFP_KERNEL); 3091 (card->num_links + card->num_aux_devs),
3092 GFP_KERNEL);
2951 if (card->rtd == NULL) 3093 if (card->rtd == NULL)
2952 return -ENOMEM; 3094 return -ENOMEM;
3095 card->rtd_aux = &card->rtd[card->num_links];
2953 3096
2954 for (i = 0; i < card->num_links; i++) 3097 for (i = 0; i < card->num_links; i++)
2955 card->rtd[i].dai_link = &card->dai_link[i]; 3098 card->rtd[i].dai_link = &card->dai_link[i];