diff options
-rw-r--r-- | include/sound/soc.h | 17 | ||||
-rw-r--r-- | sound/soc/soc-core.c | 155 |
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 | ||
586 | struct 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 */ |
587 | struct snd_soc_card { | 595 | struct 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 | ||
1606 | static 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 | |||
1693 | out: | ||
1694 | return ret; | ||
1695 | } | ||
1696 | |||
1697 | static 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 | |||
1606 | static void snd_soc_instantiate_card(struct snd_soc_card *card) | 1730 | static 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 | ||
1819 | probe_aux_dev_err: | ||
1820 | for (i = 0; i < card->num_aux_devs; i++) | ||
1821 | soc_remove_aux_dev(card, i); | ||
1822 | |||
1686 | probe_dai_err: | 1823 | probe_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]; |