summaryrefslogtreecommitdiffstats
path: root/sound/soc/soc-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/soc-core.c')
-rw-r--r--sound/soc/soc-core.c412
1 files changed, 156 insertions, 256 deletions
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 2403bec2fccf..fd6eaae6c0ed 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -58,6 +58,13 @@ static LIST_HEAD(unbind_card_list);
list_for_each_entry(component, &component_list, list)
/*
+ * This is used if driver don't need to have CPU/Codec/Platform
+ * dai_link. see soc.h
+ */
+struct snd_soc_dai_link_component null_dailink_component[0];
+EXPORT_SYMBOL_GPL(null_dailink_component);
+
+/*
* 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.
* between two audio tracks.
@@ -158,9 +165,10 @@ static void soc_init_component_debugfs(struct snd_soc_component *component)
component->card->debugfs_card_root);
}
- if (!component->debugfs_root) {
+ if (IS_ERR(component->debugfs_root)) {
dev_warn(component->dev,
- "ASoC: Failed to create component debugfs directory\n");
+ "ASoC: Failed to create component debugfs directory: %ld\n",
+ PTR_ERR(component->debugfs_root));
return;
}
@@ -212,23 +220,29 @@ static void soc_init_card_debugfs(struct snd_soc_card *card)
card->debugfs_card_root = debugfs_create_dir(card->name,
snd_soc_debugfs_root);
- if (!card->debugfs_card_root) {
+ if (IS_ERR(card->debugfs_card_root)) {
dev_warn(card->dev,
- "ASoC: Failed to create card debugfs directory\n");
+ "ASoC: Failed to create card debugfs directory: %ld\n",
+ PTR_ERR(card->debugfs_card_root));
+ card->debugfs_card_root = NULL;
return;
}
card->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0644,
card->debugfs_card_root,
&card->pop_time);
- if (!card->debugfs_pop_time)
+ if (IS_ERR(card->debugfs_pop_time))
dev_warn(card->dev,
- "ASoC: Failed to create pop time debugfs file\n");
+ "ASoC: Failed to create pop time debugfs file: %ld\n",
+ PTR_ERR(card->debugfs_pop_time));
}
static void soc_cleanup_card_debugfs(struct snd_soc_card *card)
{
+ if (!card->debugfs_card_root)
+ return;
debugfs_remove_recursive(card->debugfs_card_root);
+ card->debugfs_card_root = NULL;
}
static void snd_soc_debugfs_init(void)
@@ -687,6 +701,8 @@ int snd_soc_resume(struct device *dev)
struct snd_soc_card *card = dev_get_drvdata(dev);
bool bus_control = false;
struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *codec_dai;
+ int i;
/* If the card is not initialized yet there is nothing to do */
if (!card->instantiated)
@@ -694,14 +710,12 @@ int snd_soc_resume(struct device *dev)
/* activate pins from sleep state */
for_each_card_rtds(card, rtd) {
- struct snd_soc_dai *codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int j;
if (cpu_dai->active)
pinctrl_pm_select_default_state(cpu_dai->dev);
- for_each_rtd_codec_dai(rtd, j, codec_dai) {
+ for_each_rtd_codec_dai(rtd, i, codec_dai) {
if (codec_dai->active)
pinctrl_pm_select_default_state(codec_dai->dev);
}
@@ -738,28 +752,16 @@ EXPORT_SYMBOL_GPL(snd_soc_resume);
static const struct snd_soc_dai_ops null_dai_ops = {
};
-static struct snd_soc_component *soc_find_component(
- const struct device_node *of_node, const char *name)
+static struct device_node
+*soc_component_to_node(struct snd_soc_component *component)
{
- struct snd_soc_component *component;
- struct device_node *component_of_node;
-
- lockdep_assert_held(&client_mutex);
+ struct device_node *of_node;
- for_each_component(component) {
- if (of_node) {
- component_of_node = component->dev->of_node;
- if (!component_of_node && component->dev->parent)
- component_of_node = component->dev->parent->of_node;
-
- if (component_of_node == of_node)
- return component;
- } else if (name && strcmp(component->name, name) == 0) {
- return component;
- }
- }
+ of_node = component->dev->of_node;
+ if (!of_node && component->dev->parent)
+ of_node = component->dev->parent->of_node;
- return NULL;
+ return of_node;
}
static int snd_soc_is_matching_component(
@@ -768,9 +770,10 @@ static int snd_soc_is_matching_component(
{
struct device_node *component_of_node;
- component_of_node = component->dev->of_node;
- if (!component_of_node && component->dev->parent)
- component_of_node = component->dev->parent->of_node;
+ if (!dlc)
+ return 0;
+
+ component_of_node = soc_component_to_node(component);
if (dlc->of_node && component_of_node != dlc->of_node)
return 0;
@@ -780,6 +783,28 @@ static int snd_soc_is_matching_component(
return 1;
}
+static struct snd_soc_component *soc_find_component(
+ const struct snd_soc_dai_link_component *dlc)
+{
+ struct snd_soc_component *component;
+
+ lockdep_assert_held(&client_mutex);
+
+ /*
+ * NOTE
+ *
+ * It returns *1st* found component, but some driver
+ * has few components by same of_node/name
+ * ex)
+ * CPU component and generic DMAEngine component
+ */
+ for_each_component(component)
+ if (snd_soc_is_matching_component(dlc, component))
+ return component;
+
+ return NULL;
+}
+
/**
* snd_soc_find_dai - Find a registered DAI
*
@@ -875,10 +900,8 @@ static int soc_bind_dai_link(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_link)
{
struct snd_soc_pcm_runtime *rtd;
- struct snd_soc_dai_link_component *codecs;
- struct snd_soc_dai_link_component cpu_dai_component;
+ struct snd_soc_dai_link_component *codec, *platform;
struct snd_soc_component *component;
- struct snd_soc_dai **codec_dais;
int i;
if (dai_link->ignore)
@@ -896,41 +919,39 @@ static int soc_bind_dai_link(struct snd_soc_card *card,
if (!rtd)
return -ENOMEM;
- cpu_dai_component.name = dai_link->cpu_name;
- cpu_dai_component.of_node = dai_link->cpu_of_node;
- cpu_dai_component.dai_name = dai_link->cpu_dai_name;
- rtd->cpu_dai = snd_soc_find_dai(&cpu_dai_component);
+ /* FIXME: we need multi CPU support in the future */
+ rtd->cpu_dai = snd_soc_find_dai(dai_link->cpus);
if (!rtd->cpu_dai) {
dev_info(card->dev, "ASoC: CPU DAI %s not registered\n",
- dai_link->cpu_dai_name);
+ dai_link->cpus->dai_name);
goto _err_defer;
}
snd_soc_rtdcom_add(rtd, rtd->cpu_dai->component);
- rtd->num_codecs = dai_link->num_codecs;
-
/* Find CODEC from registered CODECs */
- codec_dais = rtd->codec_dais;
- for_each_link_codecs(dai_link, i, codecs) {
- codec_dais[i] = snd_soc_find_dai(codecs);
- if (!codec_dais[i]) {
+ rtd->num_codecs = dai_link->num_codecs;
+ for_each_link_codecs(dai_link, i, codec) {
+ rtd->codec_dais[i] = snd_soc_find_dai(codec);
+ if (!rtd->codec_dais[i]) {
dev_info(card->dev, "ASoC: CODEC DAI %s not registered\n",
- codecs->dai_name);
+ codec->dai_name);
goto _err_defer;
}
- snd_soc_rtdcom_add(rtd, codec_dais[i]->component);
+
+ snd_soc_rtdcom_add(rtd, rtd->codec_dais[i]->component);
}
/* Single codec links expect codec and codec_dai in runtime data */
- rtd->codec_dai = codec_dais[0];
+ rtd->codec_dai = rtd->codec_dais[0];
- /* find one from the set of registered platforms */
- for_each_component(component) {
- if (!snd_soc_is_matching_component(dai_link->platforms,
- component))
- continue;
+ /* Find PLATFORM from registered PLATFORMs */
+ for_each_link_platforms(dai_link, i, platform) {
+ for_each_component(component) {
+ if (!snd_soc_is_matching_component(platform, component))
+ continue;
- snd_soc_rtdcom_add(rtd, component);
+ snd_soc_rtdcom_add(rtd, component);
+ }
}
soc_add_pcm_runtime(card, rtd);
@@ -943,6 +964,7 @@ _err_defer:
static void soc_cleanup_component(struct snd_soc_component *component)
{
+ snd_soc_component_set_jack(component, NULL, NULL);
list_del(&component->card_list);
snd_soc_dapm_free(snd_soc_component_get_dapm(component));
soc_cleanup_component_debugfs(component);
@@ -1038,171 +1060,73 @@ static void soc_remove_dai_links(struct snd_soc_card *card)
}
}
-static int snd_soc_init_platform(struct snd_soc_card *card,
- struct snd_soc_dai_link *dai_link)
-{
- struct snd_soc_dai_link_component *platform = dai_link->platforms;
-
- /*
- * REMOVE ME
- *
- * This is glue code for Legacy vs Modern dai_link.
- * This function will be removed if all derivers are switched to
- * modern style dai_link.
- * Driver shouldn't use both legacy and modern style in the same time.
- * see
- * soc.h :: struct snd_soc_dai_link
- */
- /* convert Legacy platform link */
- if (!platform) {
- platform = devm_kzalloc(card->dev,
- sizeof(struct snd_soc_dai_link_component),
- GFP_KERNEL);
- if (!platform)
- return -ENOMEM;
-
- dai_link->platforms = platform;
- dai_link->num_platforms = 1;
- dai_link->legacy_platform = 1;
- platform->name = dai_link->platform_name;
- platform->of_node = dai_link->platform_of_node;
- platform->dai_name = NULL;
- }
-
- /* if there's no platform we match on the empty platform */
- if (!platform->name &&
- !platform->of_node)
- platform->name = "snd-soc-dummy";
-
- return 0;
-}
-
-static void soc_cleanup_platform(struct snd_soc_card *card)
-{
- struct snd_soc_dai_link *link;
- int i;
- /*
- * FIXME
- *
- * this function should be removed with snd_soc_init_platform
- */
-
- for_each_card_prelinks(card, i, link) {
- if (link->legacy_platform) {
- link->legacy_platform = 0;
- link->platforms = NULL;
- }
- }
-}
-
-static int snd_soc_init_multicodec(struct snd_soc_card *card,
- struct snd_soc_dai_link *dai_link)
-{
- /*
- * REMOVE ME
- *
- * This is glue code for Legacy vs Modern dai_link.
- * This function will be removed if all derivers are switched to
- * modern style dai_link.
- * Driver shouldn't use both legacy and modern style in the same time.
- * see
- * soc.h :: struct snd_soc_dai_link
- */
-
- /* Legacy codec/codec_dai link is a single entry in multicodec */
- if (dai_link->codec_name || dai_link->codec_of_node ||
- dai_link->codec_dai_name) {
- dai_link->num_codecs = 1;
-
- dai_link->codecs = devm_kzalloc(card->dev,
- sizeof(struct snd_soc_dai_link_component),
- GFP_KERNEL);
- if (!dai_link->codecs)
- return -ENOMEM;
-
- dai_link->codecs[0].name = dai_link->codec_name;
- dai_link->codecs[0].of_node = dai_link->codec_of_node;
- dai_link->codecs[0].dai_name = dai_link->codec_dai_name;
- }
-
- if (!dai_link->codecs) {
- dev_err(card->dev, "ASoC: DAI link has no CODECs\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
static int soc_init_dai_link(struct snd_soc_card *card,
struct snd_soc_dai_link *link)
{
- int i, ret;
- struct snd_soc_dai_link_component *codec;
-
- ret = snd_soc_init_platform(card, link);
- if (ret) {
- dev_err(card->dev, "ASoC: failed to init multiplatform\n");
- return ret;
- }
-
- ret = snd_soc_init_multicodec(card, link);
- if (ret) {
- dev_err(card->dev, "ASoC: failed to init multicodec\n");
- return ret;
- }
+ int i;
+ struct snd_soc_dai_link_component *codec, *platform;
for_each_link_codecs(link, i, codec) {
/*
* Codec must be specified by 1 of name or OF node,
* not both or neither.
*/
- if (!!codec->name ==
- !!codec->of_node) {
+ if (!!codec->name == !!codec->of_node) {
dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",
link->name);
return -EINVAL;
}
+
/* Codec DAI name must be specified */
if (!codec->dai_name) {
dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",
link->name);
return -EINVAL;
}
+
+ /*
+ * Defer card registration if codec component is not added to
+ * component list.
+ */
+ if (!soc_find_component(codec))
+ return -EPROBE_DEFER;
}
- /* FIXME */
- if (link->num_platforms > 1) {
- dev_err(card->dev,
- "ASoC: multi platform is not yet supported %s\n",
- link->name);
- return -EINVAL;
+ for_each_link_platforms(link, i, platform) {
+ /*
+ * Platform may be specified by either name or OF node, but it
+ * can be left unspecified, then no components will be inserted
+ * in the rtdcom list
+ */
+ if (!!platform->name == !!platform->of_node) {
+ dev_err(card->dev,
+ "ASoC: Neither/both platform name/of_node are set for %s\n",
+ link->name);
+ return -EINVAL;
+ }
+
+ /*
+ * Defer card registration if platform component is not added to
+ * component list.
+ */
+ if (!soc_find_component(platform))
+ return -EPROBE_DEFER;
}
- /*
- * Platform may be specified by either name or OF node, but
- * can be left unspecified, and a dummy platform will be used.
- */
- if (link->platforms->name && link->platforms->of_node) {
+ /* FIXME */
+ if (link->num_cpus > 1) {
dev_err(card->dev,
- "ASoC: Both platform name/of_node are set for %s\n",
+ "ASoC: multi cpu is not yet supported %s\n",
link->name);
return -EINVAL;
}
/*
- * Defer card registartion if platform dai component is not added to
- * component list.
- */
- if ((link->platforms->of_node || link->platforms->name) &&
- !soc_find_component(link->platforms->of_node, link->platforms->name))
- return -EPROBE_DEFER;
-
- /*
* CPU device may be specified by either name or OF node, but
* can be left unspecified, and will be matched based on DAI
* name alone..
*/
- if (link->cpu_name && link->cpu_of_node) {
+ if (link->cpus->name && link->cpus->of_node) {
dev_err(card->dev,
"ASoC: Neither/both cpu name/of_node are set for %s\n",
link->name);
@@ -1213,16 +1137,16 @@ static int soc_init_dai_link(struct snd_soc_card *card,
* Defer card registartion if cpu dai component is not added to
* component list.
*/
- if ((link->cpu_of_node || link->cpu_name) &&
- !soc_find_component(link->cpu_of_node, link->cpu_name))
+ if ((link->cpus->of_node || link->cpus->name) &&
+ !soc_find_component(link->cpus))
return -EPROBE_DEFER;
/*
* At least one of CPU DAI name or CPU device name/node must be
* specified
*/
- if (!link->cpu_dai_name &&
- !(link->cpu_name || link->cpu_of_node)) {
+ if (!link->cpus->dai_name &&
+ !(link->cpus->name || link->cpus->of_node)) {
dev_err(card->dev,
"ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",
link->name);
@@ -1320,13 +1244,10 @@ EXPORT_SYMBOL_GPL(snd_soc_remove_dai_link);
static void soc_set_of_name_prefix(struct snd_soc_component *component)
{
- struct device_node *component_of_node = component->dev->of_node;
+ struct device_node *component_of_node = soc_component_to_node(component);
const char *str;
int ret;
- if (!component_of_node && component->dev->parent)
- component_of_node = component->dev->parent->of_node;
-
ret = of_property_read_string(component_of_node, "sound-name-prefix",
&str);
if (!ret)
@@ -1340,10 +1261,7 @@ static void soc_set_name_prefix(struct snd_soc_card *card,
for (i = 0; i < card->num_configs && card->codec_conf; i++) {
struct snd_soc_codec_conf *map = &card->codec_conf[i];
- struct device_node *component_of_node = component->dev->of_node;
-
- if (!component_of_node && component->dev->parent)
- component_of_node = component->dev->parent->of_node;
+ struct device_node *component_of_node = soc_component_to_node(component);
if (map->of_node && component_of_node != map->of_node)
continue;
@@ -1421,12 +1339,11 @@ static int soc_probe_component(struct snd_soc_card *card,
"ASoC: failed to probe component %d\n", ret);
goto err_probe;
}
-
- WARN(dapm->idle_bias_off &&
- dapm->bias_level != SND_SOC_BIAS_OFF,
- "codec %s can not start from non-off bias with idle_bias_off==1\n",
- component->name);
}
+ WARN(dapm->idle_bias_off &&
+ dapm->bias_level != SND_SOC_BIAS_OFF,
+ "codec %s can not start from non-off bias with idle_bias_off==1\n",
+ component->name);
/* machine specific init */
if (component->init) {
@@ -1665,23 +1582,23 @@ static int soc_bind_aux_dev(struct snd_soc_card *card, int num)
{
struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
struct snd_soc_component *component;
- const char *name;
- struct device_node *codec_of_node;
+ struct snd_soc_dai_link_component dlc;
if (aux_dev->codec_of_node || aux_dev->codec_name) {
/* codecs, usually analog devices */
- name = aux_dev->codec_name;
- codec_of_node = aux_dev->codec_of_node;
- component = soc_find_component(codec_of_node, name);
+ dlc.name = aux_dev->codec_name;
+ dlc.of_node = aux_dev->codec_of_node;
+ component = soc_find_component(&dlc);
if (!component) {
- if (codec_of_node)
- name = of_node_full_name(codec_of_node);
+ if (dlc.of_node)
+ dlc.name = of_node_full_name(dlc.of_node);
goto err_defer;
}
} else if (aux_dev->name) {
/* generic components */
- name = aux_dev->name;
- component = soc_find_component(NULL, name);
+ dlc.name = aux_dev->name;
+ dlc.of_node = NULL;
+ component = soc_find_component(&dlc);
if (!component)
goto err_defer;
} else {
@@ -1695,7 +1612,7 @@ static int soc_bind_aux_dev(struct snd_soc_card *card, int num)
return 0;
err_defer:
- dev_err(card->dev, "ASoC: %s not registered\n", name);
+ dev_err(card->dev, "ASoC: %s not registered\n", dlc.name);
return -EPROBE_DEFER;
}
@@ -1994,7 +1911,7 @@ match:
card->dai_link[i].name);
/* override platform component */
- if (snd_soc_init_platform(card, dai_link) < 0) {
+ if (!dai_link->platforms) {
dev_err(card->dev, "init platform error");
continue;
}
@@ -2037,13 +1954,14 @@ match:
static int soc_cleanup_card_resources(struct snd_soc_card *card)
{
/* free the ALSA card at first; this syncs with pending operations */
- if (card->snd_card)
+ if (card->snd_card) {
snd_card_free(card->snd_card);
+ card->snd_card = NULL;
+ }
/* remove and free each DAI */
soc_remove_dai_links(card);
soc_remove_pcm_runtimes(card);
- soc_cleanup_platform(card);
/* remove auxiliary devices */
soc_remove_aux_devices(card);
@@ -2065,6 +1983,15 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
int ret, i, order;
mutex_lock(&client_mutex);
+ for_each_card_prelinks(card, i, dai_link) {
+ ret = soc_init_dai_link(card, dai_link);
+ if (ret) {
+ dev_err(card->dev, "ASoC: failed to init link %s: %d\n",
+ dai_link->name, ret);
+ mutex_unlock(&client_mutex);
+ return ret;
+ }
+ }
mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT);
card->dapm.bias_level = SND_SOC_BIAS_OFF;
@@ -2691,7 +2618,7 @@ int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
return dai->driver->ops->set_channel_map(dai, tx_num, tx_slot,
rx_num, rx_slot);
else
- return -EINVAL;
+ return -ENOTSUPP;
}
EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map);
@@ -2789,26 +2716,9 @@ static int snd_soc_bind_card(struct snd_soc_card *card)
*/
int snd_soc_register_card(struct snd_soc_card *card)
{
- int i, ret;
- struct snd_soc_dai_link *link;
-
if (!card->name || !card->dev)
return -EINVAL;
- mutex_lock(&client_mutex);
- for_each_card_prelinks(card, i, link) {
-
- ret = soc_init_dai_link(card, link);
- if (ret) {
- soc_cleanup_platform(card);
- dev_err(card->dev, "ASoC: failed to init link %s\n",
- link->name);
- mutex_unlock(&client_mutex);
- return ret;
- }
- }
- mutex_unlock(&client_mutex);
-
dev_set_drvdata(card->dev, card);
snd_soc_initialize_card_lists(card);
@@ -2863,7 +2773,9 @@ static void snd_soc_unbind_card(struct snd_soc_card *card, bool unregister)
*/
int snd_soc_unregister_card(struct snd_soc_card *card)
{
+ mutex_lock(&client_mutex);
snd_soc_unbind_card(card, true);
+ mutex_unlock(&client_mutex);
dev_dbg(card->dev, "ASoC: Unregistered card '%s'\n", card->name);
return 0;
@@ -3752,12 +3664,12 @@ EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt);
int snd_soc_get_dai_id(struct device_node *ep)
{
- struct snd_soc_component *pos;
- struct device_node *node;
+ struct snd_soc_component *component;
+ struct snd_soc_dai_link_component dlc;
int ret;
- node = of_graph_get_port_parent(ep);
-
+ dlc.of_node = of_graph_get_port_parent(ep);
+ dlc.name = NULL;
/*
* For example HDMI case, HDMI has video/sound port,
* but ALSA SoC needs sound port number only.
@@ -3766,23 +3678,13 @@ int snd_soc_get_dai_id(struct device_node *ep)
*/
ret = -ENOTSUPP;
mutex_lock(&client_mutex);
- for_each_component(pos) {
- struct device_node *component_of_node = pos->dev->of_node;
-
- if (!component_of_node && pos->dev->parent)
- component_of_node = pos->dev->parent->of_node;
-
- if (component_of_node != node)
- continue;
-
- if (pos->driver->of_xlate_dai_id)
- ret = pos->driver->of_xlate_dai_id(pos, ep);
-
- break;
- }
+ component = soc_find_component(&dlc);
+ if (component &&
+ component->driver->of_xlate_dai_id)
+ ret = component->driver->of_xlate_dai_id(component, ep);
mutex_unlock(&client_mutex);
- of_node_put(node);
+ of_node_put(dlc.of_node);
return ret;
}
@@ -3797,9 +3699,7 @@ int snd_soc_get_dai_name(struct of_phandle_args *args,
mutex_lock(&client_mutex);
for_each_component(pos) {
- component_of_node = pos->dev->of_node;
- if (!component_of_node && pos->dev->parent)
- component_of_node = pos->dev->parent->of_node;
+ component_of_node = soc_component_to_node(pos);
if (component_of_node != args->np)
continue;