diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index fb0318f9b10f38..ab5a2ba09c0779 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -75,7 +75,7 @@ void asoc_simple_card_clk_disable(struct asoc_simple_dai *dai); &dai_link->codec_dai_name, \ list_name, cells_name, NULL) #define asoc_simple_card_parse_platform(node, dai_link, list_name, cells_name) \ - asoc_simple_card_parse_dai(node, dai_link->platform, \ + asoc_simple_card_parse_dai(node, dai_link->platforms, \ &dai_link->platform_of_node, \ NULL, list_name, cells_name, NULL) int asoc_simple_card_parse_dai(struct device_node *node, @@ -116,12 +116,12 @@ int asoc_simple_card_clean_reference(struct snd_soc_card *card); void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data, struct snd_pcm_hw_params *params); -void asoc_simple_card_parse_convert(struct device *dev, char *prefix, +void asoc_simple_card_parse_convert(struct device *dev, + struct device_node *np, char *prefix, struct asoc_simple_card_data *data); int asoc_simple_card_of_parse_routing(struct snd_soc_card *card, - char *prefix, - int optional); + char *prefix); int asoc_simple_card_of_parse_widgets(struct snd_soc_card *card, char *prefix); diff --git a/include/sound/soc.h b/include/sound/soc.h index 632d3ae11d3d38..b71b05019c68c2 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -961,7 +961,8 @@ struct snd_soc_dai_link { */ const char *platform_name; struct device_node *platform_of_node; - struct snd_soc_dai_link_component *platform; + struct snd_soc_dai_link_component *platforms; + unsigned int num_platforms; int id; /* optional ID for machine driver link identification */ @@ -1021,6 +1022,12 @@ struct snd_soc_dai_link { /* Do not create a PCM for this DAI link (Backend link) */ unsigned int ignore:1; + /* + * This driver uses legacy platform naming. Set by the core, machine + * drivers should not modify this value. + */ + unsigned int legacy_platform:1; + struct list_head list; /* DAI link list of the soc card */ struct snd_soc_dobj dobj; /* For topology */ }; diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index bd45d0343913c0..9d5acd2d04abd4 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -1784,7 +1784,9 @@ static const struct snd_soc_dapm_route rt5682_dapm_routes[] = { {"ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc}, {"DAC Stereo1 Filter", NULL, "DAC STO1 ASRC", is_using_asrc}, {"ADC STO1 ASRC", NULL, "AD ASRC"}, + {"ADC STO1 ASRC", NULL, "DA ASRC"}, {"ADC STO1 ASRC", NULL, "CLKDET"}, + {"DAC STO1 ASRC", NULL, "AD ASRC"}, {"DAC STO1 ASRC", NULL, "DA ASRC"}, {"DAC STO1 ASRC", NULL, "CLKDET"}, diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 44433b20435cce..81f2fe2c6d2302 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -571,17 +571,17 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) } /* Common settings for corresponding Freescale CPU DAI driver */ - if (strstr(cpu_np->name, "ssi")) { + if (of_node_name_eq(cpu_np, "ssi")) { /* Only SSI needs to configure AUDMUX */ ret = fsl_asoc_card_audmux_init(np, priv); if (ret) { dev_err(&pdev->dev, "failed to init audmux\n"); goto asrc_fail; } - } else if (strstr(cpu_np->name, "esai")) { + } else if (of_node_name_eq(cpu_np, "esai")) { priv->cpu_priv.sysclk_id[1] = ESAI_HCKT_EXTAL; priv->cpu_priv.sysclk_id[0] = ESAI_HCKR_EXTAL; - } else if (strstr(cpu_np->name, "sai")) { + } else if (of_node_name_eq(cpu_np, "sai")) { priv->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1; priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1; } diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig index c954be0a0f96b0..83f1243145b00b 100644 --- a/sound/soc/generic/Kconfig +++ b/sound/soc/generic/Kconfig @@ -6,28 +6,13 @@ config SND_SIMPLE_CARD select SND_SIMPLE_CARD_UTILS help This option enables generic simple sound card support - -config SND_SIMPLE_SCU_CARD - tristate "ASoC Simple SCU sound card support" - depends on OF - select SND_SIMPLE_CARD_UTILS - help - This option enables generic simple SCU sound card support. - It supports DPCM of multi CPU single Codec system. + It also support DPCM of multi CPU single Codec ststem. config SND_AUDIO_GRAPH_CARD tristate "ASoC Audio Graph sound card support" depends on OF select SND_SIMPLE_CARD_UTILS help - This option enables generic simple simple sound card support - with OF-graph DT bindings. - -config SND_AUDIO_GRAPH_SCU_CARD - tristate "ASoC Audio Graph SCU sound card support" - depends on OF - select SND_SIMPLE_CARD_UTILS - help - This option enables generic simple SCU sound card support + This option enables generic simple sound card support with OF-graph DT bindings. - It supports DPCM of multi CPU single Codec ststem. + It also support DPCM of multi CPU single Codec ststem. diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile index 9dec293a4c4de9..21c29e5e0671e2 100644 --- a/sound/soc/generic/Makefile +++ b/sound/soc/generic/Makefile @@ -1,12 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 snd-soc-simple-card-utils-objs := simple-card-utils.o snd-soc-simple-card-objs := simple-card.o -snd-soc-simple-scu-card-objs := simple-scu-card.o snd-soc-audio-graph-card-objs := audio-graph-card.o -snd-soc-audio-graph-scu-card-objs := audio-graph-scu-card.o obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o -obj-$(CONFIG_SND_SIMPLE_SCU_CARD) += snd-soc-simple-scu-card.o obj-$(CONFIG_SND_AUDIO_GRAPH_CARD) += snd-soc-audio-graph-card.o -obj-$(CONFIG_SND_AUDIO_GRAPH_SCU_CARD) += snd-soc-audio-graph-scu-card.o diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 25c819e402e185..42b077c6be4c4a 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -20,28 +20,45 @@ #include #include -struct graph_card_data { +struct graph_priv { struct snd_soc_card snd_card; struct graph_dai_props { - struct asoc_simple_dai cpu_dai; - struct asoc_simple_dai codec_dai; + struct asoc_simple_dai *cpu_dai; + struct asoc_simple_dai *codec_dai; struct snd_soc_dai_link_component codecs; /* single codec */ - struct snd_soc_dai_link_component platform; + struct snd_soc_dai_link_component platforms; + struct asoc_simple_card_data adata; + struct snd_soc_codec_conf *codec_conf; unsigned int mclk_fs; } *dai_props; - unsigned int mclk_fs; struct asoc_simple_jack hp_jack; struct asoc_simple_jack mic_jack; struct snd_soc_dai_link *dai_link; + struct asoc_simple_dai *dais; + struct snd_soc_codec_conf *codec_conf; struct gpio_desc *pa_gpio; }; -static int asoc_graph_card_outdrv_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, - int event) +struct link_info { + int dais; /* number of dai */ + int link; /* number of link */ + int conf; /* number of codec_conf */ + int cpu; /* turn for CPU / Codec */ +}; + +#define graph_priv_to_card(priv) (&(priv)->snd_card) +#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i)) +#define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev) +#define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i)) + +#define PREFIX "audio-graph-card," + +static int graph_outdrv_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) { struct snd_soc_dapm_context *dapm = w->dapm; - struct graph_card_data *priv = snd_soc_card_get_drvdata(dapm->card); + struct graph_priv *priv = snd_soc_card_get_drvdata(dapm->card); switch (event) { case SND_SOC_DAPM_POST_PMU: @@ -57,60 +74,53 @@ static int asoc_graph_card_outdrv_event(struct snd_soc_dapm_widget *w, return 0; } -static const struct snd_soc_dapm_widget asoc_graph_card_dapm_widgets[] = { +static const struct snd_soc_dapm_widget graph_dapm_widgets[] = { SND_SOC_DAPM_OUT_DRV_E("Amplifier", SND_SOC_NOPM, - 0, 0, NULL, 0, asoc_graph_card_outdrv_event, + 0, 0, NULL, 0, graph_outdrv_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), }; -#define graph_priv_to_card(priv) (&(priv)->snd_card) -#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i)) -#define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev) -#define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i)) - -static int asoc_graph_card_startup(struct snd_pcm_substream *substream) +static int graph_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); int ret; - ret = asoc_simple_card_clk_enable(&dai_props->cpu_dai); + ret = asoc_simple_card_clk_enable(dai_props->cpu_dai); if (ret) return ret; - ret = asoc_simple_card_clk_enable(&dai_props->codec_dai); + ret = asoc_simple_card_clk_enable(dai_props->codec_dai); if (ret) - asoc_simple_card_clk_disable(&dai_props->cpu_dai); + asoc_simple_card_clk_disable(dai_props->cpu_dai); return ret; } -static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream) +static void graph_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); - asoc_simple_card_clk_disable(&dai_props->cpu_dai); + asoc_simple_card_clk_disable(dai_props->cpu_dai); - asoc_simple_card_clk_disable(&dai_props->codec_dai); + asoc_simple_card_clk_disable(dai_props->codec_dai); } -static int asoc_graph_card_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int graph_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); unsigned int mclk, mclk_fs = 0; int ret = 0; - if (priv->mclk_fs) - mclk_fs = priv->mclk_fs; - else if (dai_props->mclk_fs) + if (dai_props->mclk_fs) mclk_fs = dai_props->mclk_fs; if (mclk_fs) { @@ -130,158 +140,495 @@ static int asoc_graph_card_hw_params(struct snd_pcm_substream *substream, return ret; } -static const struct snd_soc_ops asoc_graph_card_ops = { - .startup = asoc_graph_card_startup, - .shutdown = asoc_graph_card_shutdown, - .hw_params = asoc_graph_card_hw_params, +static const struct snd_soc_ops graph_ops = { + .startup = graph_startup, + .shutdown = graph_shutdown, + .hw_params = graph_hw_params, }; -static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd) +static int graph_dai_init(struct snd_soc_pcm_runtime *rtd) { - struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *codec = rtd->codec_dai; - struct snd_soc_dai *cpu = rtd->cpu_dai; - struct graph_dai_props *dai_props = - graph_priv_to_props(priv, rtd->num); - int ret; + struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); + int ret = 0; - ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai); + ret = asoc_simple_card_init_dai(rtd->codec_dai, + dai_props->codec_dai); if (ret < 0) return ret; - ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai); + ret = asoc_simple_card_init_dai(rtd->cpu_dai, + dai_props->cpu_dai); if (ret < 0) return ret; return 0; } -static int asoc_graph_card_dai_link_of(struct device_node *cpu_port, - struct graph_card_data *priv, - int idx) +static int graph_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct graph_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); + + asoc_simple_card_convert_fixup(&dai_props->adata, params); + + return 0; +} + +static void graph_get_conversion(struct device *dev, + struct device_node *ep, + struct asoc_simple_card_data *adata) +{ + struct device_node *top = dev->of_node; + struct device_node *port = of_get_parent(ep); + struct device_node *ports = of_get_parent(port); + struct device_node *node = of_graph_get_port_parent(ep); + + asoc_simple_card_parse_convert(dev, top, NULL, adata); + asoc_simple_card_parse_convert(dev, node, PREFIX, adata); + asoc_simple_card_parse_convert(dev, ports, NULL, adata); + asoc_simple_card_parse_convert(dev, port, NULL, adata); + asoc_simple_card_parse_convert(dev, ep, NULL, adata); +} + +static int graph_dai_link_of_dpcm(struct graph_priv *priv, + struct device_node *cpu_ep, + struct device_node *codec_ep, + struct link_info *li, + int dup_codec) { struct device *dev = graph_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, idx); - struct graph_dai_props *dai_props = graph_priv_to_props(priv, idx); - struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai; - struct asoc_simple_dai *codec_dai = &dai_props->codec_dai; - struct device_node *cpu_ep = of_get_next_child(cpu_port, NULL); - struct device_node *codec_ep = of_graph_get_remote_endpoint(cpu_ep); - struct device_node *rcpu_ep = of_graph_get_remote_endpoint(codec_ep); + struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, li->link); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, li->link); + struct device_node *top = dev->of_node; + struct device_node *ep = li->cpu ? cpu_ep : codec_ep; + struct device_node *port; + struct device_node *ports; + struct device_node *node; + struct asoc_simple_dai *dai; + struct snd_soc_dai_link_component *codecs = dai_link->codecs; int ret; - if (rcpu_ep != cpu_ep) { - dev_err(dev, "remote-endpoint mismatch (%s/%s/%s)\n", - cpu_ep->name, codec_ep->name, rcpu_ep->name); - ret = -EINVAL; - goto dai_link_of_err; + /* Do it all CPU endpoint, and 1st Codec endpoint */ + if (!li->cpu && dup_codec) + return 0; + + port = of_get_parent(ep); + ports = of_get_parent(port); + node = of_graph_get_port_parent(ep); + + li->link++; + + dev_dbg(dev, "link_of DPCM (%pOF)\n", ep); + + of_property_read_u32(top, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ports, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(port, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ep, "mclk-fs", &dai_props->mclk_fs); + + graph_get_conversion(dev, ep, &dai_props->adata); + + of_node_put(ports); + of_node_put(port); + of_node_put(node); + + if (li->cpu) { + + /* BE is dummy */ + codecs->of_node = NULL; + codecs->dai_name = "snd-soc-dummy-dai"; + codecs->name = "snd-soc-dummy"; + + /* FE settings */ + dai_link->dynamic = 1; + dai_link->dpcm_merged_format = 1; + + dai = + dai_props->cpu_dai = &priv->dais[li->dais++]; + + ret = asoc_simple_card_parse_graph_cpu(ep, dai_link); + if (ret) + return ret; + + ret = asoc_simple_card_parse_clk_cpu(dev, ep, dai_link, dai); + if (ret < 0) + return ret; + + ret = asoc_simple_card_set_dailink_name(dev, dai_link, + "fe.%s", + dai_link->cpu_dai_name); + if (ret < 0) + return ret; + + /* card->num_links includes Codec */ + asoc_simple_card_canonicalize_cpu(dai_link, + of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1); + } else { + struct snd_soc_codec_conf *cconf; + + /* FE is dummy */ + dai_link->cpu_of_node = NULL; + dai_link->cpu_dai_name = "snd-soc-dummy-dai"; + dai_link->cpu_name = "snd-soc-dummy"; + + /* BE settings */ + dai_link->no_pcm = 1; + dai_link->be_hw_params_fixup = graph_be_hw_params_fixup; + + dai = + dai_props->codec_dai = &priv->dais[li->dais++]; + + cconf = + dai_props->codec_conf = &priv->codec_conf[li->conf++]; + + ret = asoc_simple_card_parse_graph_codec(ep, dai_link); + if (ret < 0) + return ret; + + ret = asoc_simple_card_parse_clk_codec(dev, ep, dai_link, dai); + if (ret < 0) + return ret; + + ret = asoc_simple_card_set_dailink_name(dev, dai_link, + "be.%s", + codecs->dai_name); + if (ret < 0) + return ret; + + /* check "prefix" from top node */ + snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node, + "prefix"); + snd_soc_of_parse_node_prefix(node, cconf, codecs->of_node, + PREFIX "prefix"); + snd_soc_of_parse_node_prefix(ports, cconf, codecs->of_node, + "prefix"); + snd_soc_of_parse_node_prefix(port, cconf, codecs->of_node, + "prefix"); } + ret = asoc_simple_card_of_parse_tdm(ep, dai); + if (ret) + return ret; + + ret = asoc_simple_card_canonicalize_dailink(dai_link); + if (ret < 0) + return ret; + ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, NULL, &dai_link->dai_fmt); if (ret < 0) - goto dai_link_of_err; + return ret; + + dai_link->dpcm_playback = 1; + dai_link->dpcm_capture = 1; + dai_link->ops = &graph_ops; + dai_link->init = graph_dai_init; + + return 0; +} - of_property_read_u32(cpu_ep, "mclk-fs", &dai_props->mclk_fs); - of_property_read_u32(codec_ep, "mclk-fs", &dai_props->mclk_fs); +static int graph_dai_link_of(struct graph_priv *priv, + struct device_node *cpu_ep, + struct device_node *codec_ep, + struct link_info *li) +{ + struct device *dev = graph_priv_to_dev(priv); + struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, li->link); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, li->link); + struct device_node *top = dev->of_node; + struct device_node *cpu_port; + struct device_node *cpu_ports; + struct device_node *codec_port; + struct device_node *codec_ports; + struct asoc_simple_dai *cpu_dai; + struct asoc_simple_dai *codec_dai; + int ret; + + /* Do it only CPU turn */ + if (!li->cpu) + return 0; + + cpu_port = of_get_parent(cpu_ep); + cpu_ports = of_get_parent(cpu_port); + codec_port = of_get_parent(codec_ep); + codec_ports = of_get_parent(codec_port); + + dev_dbg(dev, "link_of (%pOF)\n", cpu_ep); + + li->link++; + + cpu_dai = + dai_props->cpu_dai = &priv->dais[li->dais++]; + codec_dai = + dai_props->codec_dai = &priv->dais[li->dais++]; + + /* Factor to mclk, used in hw_params() */ + of_property_read_u32(top, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(cpu_ports, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(codec_ports, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(cpu_port, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(codec_port, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(cpu_ep, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(codec_ep, "mclk-fs", &dai_props->mclk_fs); + of_node_put(cpu_port); + of_node_put(cpu_ports); + of_node_put(codec_port); + of_node_put(codec_ports); + + ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, + NULL, &dai_link->dai_fmt); + if (ret < 0) + return ret; ret = asoc_simple_card_parse_graph_cpu(cpu_ep, dai_link); if (ret < 0) - goto dai_link_of_err; + return ret; ret = asoc_simple_card_parse_graph_codec(codec_ep, dai_link); if (ret < 0) - goto dai_link_of_err; + return ret; ret = asoc_simple_card_of_parse_tdm(cpu_ep, cpu_dai); if (ret < 0) - goto dai_link_of_err; + return ret; ret = asoc_simple_card_of_parse_tdm(codec_ep, codec_dai); if (ret < 0) - goto dai_link_of_err; + return ret; ret = asoc_simple_card_parse_clk_cpu(dev, cpu_ep, dai_link, cpu_dai); if (ret < 0) - goto dai_link_of_err; + return ret; ret = asoc_simple_card_parse_clk_codec(dev, codec_ep, dai_link, codec_dai); if (ret < 0) - goto dai_link_of_err; + return ret; ret = asoc_simple_card_canonicalize_dailink(dai_link); if (ret < 0) - goto dai_link_of_err; + return ret; ret = asoc_simple_card_set_dailink_name(dev, dai_link, "%s-%s", dai_link->cpu_dai_name, dai_link->codecs->dai_name); if (ret < 0) - goto dai_link_of_err; + return ret; - dai_link->ops = &asoc_graph_card_ops; - dai_link->init = asoc_graph_card_dai_init; + dai_link->ops = &graph_ops; + dai_link->init = graph_dai_init; asoc_simple_card_canonicalize_cpu(dai_link, of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1); -dai_link_of_err: - of_node_put(cpu_ep); - of_node_put(rcpu_ep); - of_node_put(codec_ep); - - return ret; + return 0; } -static int asoc_graph_card_parse_of(struct graph_card_data *priv) +static int graph_for_each_link(struct graph_priv *priv, + struct link_info *li, + int (*func_noml)(struct graph_priv *priv, + struct device_node *cpu_ep, + struct device_node *codec_ep, + struct link_info *li), + int (*func_dpcm)(struct graph_priv *priv, + struct device_node *cpu_ep, + struct device_node *codec_ep, + struct link_info *li, int dup_codec)) { struct of_phandle_iterator it; struct device *dev = graph_priv_to_dev(priv); - struct snd_soc_card *card = graph_priv_to_card(priv); struct device_node *node = dev->of_node; - int rc, idx = 0; + struct device_node *cpu_port; + struct device_node *cpu_ep; + struct device_node *codec_ep; + struct device_node *codec_port; + struct device_node *codec_port_old = NULL; + struct asoc_simple_card_data adata; + int rc, ret; + + /* loop for all listed CPU port */ + of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { + cpu_port = it.node; + cpu_ep = NULL; + + /* loop for all CPU endpoint */ + while (1) { + cpu_ep = of_get_next_child(cpu_port, cpu_ep); + if (!cpu_ep) + break; + + /* get codec */ + codec_ep = of_graph_get_remote_endpoint(cpu_ep); + codec_port = of_get_parent(codec_ep); + + of_node_put(codec_ep); + of_node_put(codec_port); + + /* get convert-xxx property */ + memset(&adata, 0, sizeof(adata)); + graph_get_conversion(dev, codec_ep, &adata); + graph_get_conversion(dev, cpu_ep, &adata); + + /* + * It is DPCM + * if Codec port has many endpoints, + * or has convert-xxx property + */ + if ((of_get_child_count(codec_port) > 1) || + adata.convert_rate || adata.convert_channels) + ret = func_dpcm(priv, cpu_ep, codec_ep, li, + (codec_port_old == codec_port)); + /* else normal sound */ + else + ret = func_noml(priv, cpu_ep, codec_ep, li); + + if (ret < 0) + return ret; + + codec_port_old = codec_port; + } + } + + return 0; +} + +static int graph_parse_of(struct graph_priv *priv) +{ + struct snd_soc_card *card = graph_priv_to_card(priv); + struct link_info li; int ret; ret = asoc_simple_card_of_parse_widgets(card, NULL); if (ret < 0) return ret; - ret = asoc_simple_card_of_parse_routing(card, NULL, 1); + ret = asoc_simple_card_of_parse_routing(card, NULL); if (ret < 0) return ret; - /* Factor to mclk, used in hw_params() */ - of_property_read_u32(node, "mclk-fs", &priv->mclk_fs); - - of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { - ret = asoc_graph_card_dai_link_of(it.node, priv, idx++); - if (ret < 0) { - of_node_put(it.node); - + memset(&li, 0, sizeof(li)); + for (li.cpu = 1; li.cpu >= 0; li.cpu--) { + /* + * Detect all CPU first, and Detect all Codec 2nd. + * + * In Normal sound case, all DAIs are detected + * as "CPU-Codec". + * + * In DPCM sound case, + * all CPUs are detected as "CPU-dummy", and + * all Codecs are detected as "dummy-Codec". + * To avoid random sub-device numbering, + * detect "dummy-Codec" in last; + */ + ret = graph_for_each_link(priv, &li, + graph_dai_link_of, + graph_dai_link_of_dpcm); + if (ret < 0) return ret; - } } return asoc_simple_card_parse_card_name(card, NULL); } -static int asoc_graph_get_dais_count(struct device *dev) +static int graph_count_noml(struct graph_priv *priv, + struct device_node *cpu_ep, + struct device_node *codec_ep, + struct link_info *li) { - struct of_phandle_iterator it; - struct device_node *node = dev->of_node; - int count = 0; - int rc; + struct device *dev = graph_priv_to_dev(priv); - of_for_each_phandle(&it, rc, node, "dais", NULL, 0) - count++; + li->link += 1; /* 1xCPU-Codec */ + li->dais += 2; /* 1xCPU + 1xCodec */ - return count; + dev_dbg(dev, "Count As Normal\n"); + + return 0; } -static int asoc_graph_soc_card_probe(struct snd_soc_card *card) +static int graph_count_dpcm(struct graph_priv *priv, + struct device_node *cpu_ep, + struct device_node *codec_ep, + struct link_info *li, + int dup_codec) { - struct graph_card_data *priv = snd_soc_card_get_drvdata(card); + struct device *dev = graph_priv_to_dev(priv); + + li->link++; /* 1xCPU-dummy */ + li->dais++; /* 1xCPU */ + + if (!dup_codec) { + li->link++; /* 1xdummy-Codec */ + li->conf++; /* 1xdummy-Codec */ + li->dais++; /* 1xCodec */ + } + + dev_dbg(dev, "Count As DPCM\n"); + + return 0; +} + +static void graph_get_dais_count(struct graph_priv *priv, + struct link_info *li) +{ + struct device *dev = graph_priv_to_dev(priv); + + /* + * link_num : number of links. + * CPU-Codec / CPU-dummy / dummy-Codec + * dais_num : number of DAIs + * ccnf_num : number of codec_conf + * same number for "dummy-Codec" + * + * ex1) + * CPU0 --- Codec0 link : 5 + * CPU1 --- Codec1 dais : 7 + * CPU2 -/ ccnf : 1 + * CPU3 --- Codec2 + * + * => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec + * => 7 DAIs = 4xCPU + 3xCodec + * => 1 ccnf = 1xdummy-Codec + * + * ex2) + * CPU0 --- Codec0 link : 5 + * CPU1 --- Codec1 dais : 6 + * CPU2 -/ ccnf : 1 + * CPU3 -/ + * + * => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec + * => 6 DAIs = 4xCPU + 2xCodec + * => 1 ccnf = 1xdummy-Codec + * + * ex3) + * CPU0 --- Codec0 link : 6 + * CPU1 -/ dais : 6 + * CPU2 --- Codec1 ccnf : 2 + * CPU3 -/ + * + * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec + * => 6 DAIs = 4xCPU + 2xCodec + * => 2 ccnf = 2xdummy-Codec + * + * ex4) + * CPU0 --- Codec0 (convert-rate) link : 3 + * CPU1 --- Codec1 dais : 4 + * ccnf : 1 + * + * => 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec + * => 4 DAIs = 2xCPU + 2xCodec + * => 1 ccnf = 1xdummy-Codec + */ + graph_for_each_link(priv, li, + graph_count_noml, + graph_count_dpcm); + dev_dbg(dev, "link %d, dais %d, ccnf %d\n", + li->link, li->dais, li->conf); +} + +static int graph_card_probe(struct snd_soc_card *card) +{ + struct graph_priv *priv = snd_soc_card_get_drvdata(card); int ret; ret = asoc_simple_card_init_hp(card, &priv->hp_jack, NULL); @@ -295,27 +642,40 @@ static int asoc_graph_soc_card_probe(struct snd_soc_card *card) return 0; } -static int asoc_graph_card_probe(struct platform_device *pdev) +static int graph_probe(struct platform_device *pdev) { - struct graph_card_data *priv; + struct graph_priv *priv; struct snd_soc_dai_link *dai_link; struct graph_dai_props *dai_props; + struct asoc_simple_dai *dais; struct device *dev = &pdev->dev; struct snd_soc_card *card; - int num, ret, i; + struct snd_soc_codec_conf *cconf; + struct link_info li; + int ret, i; /* Allocate the private data and the DAI link array */ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - num = asoc_graph_get_dais_count(dev); - if (num == 0) + card = graph_priv_to_card(priv); + card->owner = THIS_MODULE; + card->dev = dev; + card->dapm_widgets = graph_dapm_widgets; + card->num_dapm_widgets = ARRAY_SIZE(graph_dapm_widgets); + card->probe = graph_card_probe; + + memset(&li, 0, sizeof(li)); + graph_get_dais_count(priv, &li); + if (!li.link || !li.dais) return -EINVAL; - dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL); - dai_link = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL); - if (!dai_props || !dai_link) + dai_props = devm_kcalloc(dev, li.link, sizeof(*dai_props), GFP_KERNEL); + dai_link = devm_kcalloc(dev, li.link, sizeof(*dai_link), GFP_KERNEL); + dais = devm_kcalloc(dev, li.dais, sizeof(*dais), GFP_KERNEL); + cconf = devm_kcalloc(dev, li.conf, sizeof(*cconf), GFP_KERNEL); + if (!dai_props || !dai_link || !dais) return -ENOMEM; /* @@ -324,10 +684,11 @@ static int asoc_graph_card_probe(struct platform_device *pdev) * see * soc-core.c :: snd_soc_init_multicodec() */ - for (i = 0; i < num; i++) { + for (i = 0; i < li.link; i++) { dai_link[i].codecs = &dai_props[i].codecs; dai_link[i].num_codecs = 1; - dai_link[i].platform = &dai_props[i].platform; + dai_link[i].platforms = &dai_props[i].platforms; + dai_link[i].num_platforms = 1; } priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW); @@ -337,20 +698,17 @@ static int asoc_graph_card_probe(struct platform_device *pdev) return ret; } - priv->dai_props = dai_props; - priv->dai_link = dai_link; + priv->dai_props = dai_props; + priv->dai_link = dai_link; + priv->dais = dais; + priv->codec_conf = cconf; - /* Init snd_soc_card */ - card = graph_priv_to_card(priv); - card->owner = THIS_MODULE; - card->dev = dev; - card->dai_link = dai_link; - card->num_links = num; - card->dapm_widgets = asoc_graph_card_dapm_widgets; - card->num_dapm_widgets = ARRAY_SIZE(asoc_graph_card_dapm_widgets); - card->probe = asoc_graph_soc_card_probe; - - ret = asoc_graph_card_parse_of(priv); + card->dai_link = dai_link; + card->num_links = li.link; + card->codec_conf = cconf; + card->num_configs = li.conf; + + ret = graph_parse_of(priv); if (ret < 0) { if (ret != -EPROBE_DEFER) dev_err(dev, "parse error %d\n", ret); @@ -370,29 +728,30 @@ static int asoc_graph_card_probe(struct platform_device *pdev) return ret; } -static int asoc_graph_card_remove(struct platform_device *pdev) +static int graph_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); return asoc_simple_card_clean_reference(card); } -static const struct of_device_id asoc_graph_of_match[] = { +static const struct of_device_id graph_of_match[] = { { .compatible = "audio-graph-card", }, + { .compatible = "audio-graph-scu-card", }, {}, }; -MODULE_DEVICE_TABLE(of, asoc_graph_of_match); +MODULE_DEVICE_TABLE(of, graph_of_match); -static struct platform_driver asoc_graph_card = { +static struct platform_driver graph_card = { .driver = { .name = "asoc-audio-graph-card", .pm = &snd_soc_pm_ops, - .of_match_table = asoc_graph_of_match, + .of_match_table = graph_of_match, }, - .probe = asoc_graph_card_probe, - .remove = asoc_graph_card_remove, + .probe = graph_probe, + .remove = graph_remove, }; -module_platform_driver(asoc_graph_card); +module_platform_driver(graph_card); MODULE_ALIAS("platform:asoc-audio-graph-card"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/generic/audio-graph-scu-card.c b/sound/soc/generic/audio-graph-scu-card.c deleted file mode 100644 index b83bb31021a9fb..00000000000000 --- a/sound/soc/generic/audio-graph-scu-card.c +++ /dev/null @@ -1,431 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// ASoC audio graph SCU sound card support -// -// Copyright (C) 2017 Renesas Solutions Corp. -// Kuninori Morimoto -// -// based on -// ${LINUX}/sound/soc/generic/simple-scu-card.c -// ${LINUX}/sound/soc/generic/audio-graph-card.c - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct graph_card_data { - struct snd_soc_card snd_card; - struct snd_soc_codec_conf codec_conf; - struct graph_dai_props { - struct asoc_simple_dai dai; - struct snd_soc_dai_link_component codecs; - struct snd_soc_dai_link_component platform; - } *dai_props; - struct snd_soc_dai_link *dai_link; - struct asoc_simple_card_data adata; -}; - -#define graph_priv_to_card(priv) (&(priv)->snd_card) -#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i)) -#define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev) -#define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i)) - -static int asoc_graph_card_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); - - return asoc_simple_card_clk_enable(&dai_props->dai); -} - -static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); - - asoc_simple_card_clk_disable(&dai_props->dai); -} - -static const struct snd_soc_ops asoc_graph_card_ops = { - .startup = asoc_graph_card_startup, - .shutdown = asoc_graph_card_shutdown, -}; - -static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd) -{ - struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai; - struct snd_soc_dai_link *dai_link; - struct graph_dai_props *dai_props; - int num = rtd->num; - - dai_link = graph_priv_to_link(priv, num); - dai_props = graph_priv_to_props(priv, num); - dai = dai_link->dynamic ? - rtd->cpu_dai : - rtd->codec_dai; - - return asoc_simple_card_init_dai(dai, &dai_props->dai); -} - -static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) -{ - struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - - asoc_simple_card_convert_fixup(&priv->adata, params); - - return 0; -} - -static int asoc_graph_card_dai_link_of(struct device_node *ep, - struct graph_card_data *priv, - unsigned int daifmt, - int idx, int is_fe) -{ - struct device *dev = graph_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, idx); - struct graph_dai_props *dai_props = graph_priv_to_props(priv, idx); - struct snd_soc_card *card = graph_priv_to_card(priv); - int ret; - - if (is_fe) { - struct snd_soc_dai_link_component *codecs; - - /* BE is dummy */ - codecs = dai_link->codecs; - codecs->of_node = NULL; - codecs->dai_name = "snd-soc-dummy-dai"; - codecs->name = "snd-soc-dummy"; - - /* FE settings */ - dai_link->dynamic = 1; - dai_link->dpcm_merged_format = 1; - - ret = asoc_simple_card_parse_graph_cpu(ep, dai_link); - if (ret) - return ret; - - ret = asoc_simple_card_parse_clk_cpu(dev, ep, dai_link, &dai_props->dai); - if (ret < 0) - return ret; - - ret = asoc_simple_card_set_dailink_name(dev, dai_link, - "fe.%s", - dai_link->cpu_dai_name); - if (ret < 0) - return ret; - - /* card->num_links includes Codec */ - asoc_simple_card_canonicalize_cpu(dai_link, - of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1); - } else { - /* FE is dummy */ - dai_link->cpu_of_node = NULL; - dai_link->cpu_dai_name = "snd-soc-dummy-dai"; - dai_link->cpu_name = "snd-soc-dummy"; - - /* BE settings */ - dai_link->no_pcm = 1; - dai_link->be_hw_params_fixup = asoc_graph_card_be_hw_params_fixup; - - ret = asoc_simple_card_parse_graph_codec(ep, dai_link); - if (ret < 0) - return ret; - - ret = asoc_simple_card_parse_clk_codec(dev, ep, dai_link, &dai_props->dai); - if (ret < 0) - return ret; - - ret = asoc_simple_card_set_dailink_name(dev, dai_link, - "be.%s", - dai_link->codecs->dai_name); - if (ret < 0) - return ret; - - snd_soc_of_parse_audio_prefix(card, - &priv->codec_conf, - dai_link->codecs->of_node, - "prefix"); - } - - ret = asoc_simple_card_of_parse_tdm(ep, &dai_props->dai); - if (ret) - return ret; - - ret = asoc_simple_card_canonicalize_dailink(dai_link); - if (ret < 0) - return ret; - - dai_link->dai_fmt = daifmt; - dai_link->dpcm_playback = 1; - dai_link->dpcm_capture = 1; - dai_link->ops = &asoc_graph_card_ops; - dai_link->init = asoc_graph_card_dai_init; - - return 0; -} - -static int asoc_graph_card_parse_of(struct graph_card_data *priv) -{ - struct of_phandle_iterator it; - struct device *dev = graph_priv_to_dev(priv); - struct snd_soc_card *card = graph_priv_to_card(priv); - struct device_node *node = dev->of_node; - struct device_node *cpu_port; - struct device_node *cpu_ep; - struct device_node *codec_ep; - struct device_node *rcpu_ep; - struct device_node *codec_port; - struct device_node *codec_port_old; - unsigned int daifmt = 0; - int dai_idx, ret; - int rc, codec; - - if (!node) - return -EINVAL; - - /* - * we need to consider "widgets", "mclk-fs" around here - * see simple-card - */ - - ret = asoc_simple_card_of_parse_routing(card, NULL, 0); - if (ret < 0) - return ret; - - asoc_simple_card_parse_convert(dev, NULL, &priv->adata); - - /* - * it supports multi CPU, single CODEC only here - * see asoc_graph_get_dais_count - */ - - /* find 1st codec */ - of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { - cpu_port = it.node; - cpu_ep = of_get_next_child(cpu_port, NULL); - codec_ep = of_graph_get_remote_endpoint(cpu_ep); - rcpu_ep = of_graph_get_remote_endpoint(codec_ep); - - of_node_put(cpu_ep); - of_node_put(codec_ep); - of_node_put(rcpu_ep); - - if (!codec_ep) - continue; - - if (rcpu_ep != cpu_ep) { - dev_err(dev, "remote-endpoint missmatch (%s/%s/%s)\n", - cpu_ep->name, codec_ep->name, rcpu_ep->name); - ret = -EINVAL; - goto parse_of_err; - } - - ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, - NULL, &daifmt); - if (ret < 0) { - of_node_put(cpu_port); - goto parse_of_err; - } - } - - dai_idx = 0; - codec_port_old = NULL; - for (codec = 0; codec < 2; codec++) { - /* - * To listup valid sounds continuously, - * detect all CPU-dummy first, and - * detect all dummy-Codec second - */ - of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { - cpu_port = it.node; - cpu_ep = of_get_next_child(cpu_port, NULL); - codec_ep = of_graph_get_remote_endpoint(cpu_ep); - codec_port = of_graph_get_port_parent(codec_ep); - - of_node_put(cpu_ep); - of_node_put(codec_ep); - of_node_put(codec_port); - - if (codec) { - if (!codec_port) - continue; - - if (codec_port_old == codec_port) - continue; - - codec_port_old = codec_port; - - /* Back-End (= Codec) */ - ret = asoc_graph_card_dai_link_of(codec_ep, priv, daifmt, dai_idx++, 0); - if (ret < 0) { - of_node_put(cpu_port); - goto parse_of_err; - } - } else { - /* Front-End (= CPU) */ - ret = asoc_graph_card_dai_link_of(cpu_ep, priv, daifmt, dai_idx++, 1); - if (ret < 0) { - of_node_put(cpu_port); - goto parse_of_err; - } - } - } - } - - ret = asoc_simple_card_parse_card_name(card, NULL); - if (ret) - goto parse_of_err; - - ret = 0; - -parse_of_err: - return ret; -} - -static int asoc_graph_get_dais_count(struct device *dev) -{ - struct of_phandle_iterator it; - struct device_node *node = dev->of_node; - struct device_node *cpu_port; - struct device_node *cpu_ep; - struct device_node *codec_ep; - struct device_node *codec_port; - struct device_node *codec_port_old; - int count = 0; - int rc; - - codec_port_old = NULL; - of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { - cpu_port = it.node; - cpu_ep = of_get_next_child(cpu_port, NULL); - codec_ep = of_graph_get_remote_endpoint(cpu_ep); - codec_port = of_graph_get_port_parent(codec_ep); - - of_node_put(cpu_ep); - of_node_put(codec_ep); - of_node_put(codec_port); - - if (cpu_ep) - count++; - - if (!codec_port) - continue; - - if (codec_port_old == codec_port) - continue; - - count++; - codec_port_old = codec_port; - } - - return count; -} - -static int asoc_graph_card_probe(struct platform_device *pdev) -{ - struct graph_card_data *priv; - struct snd_soc_dai_link *dai_link; - struct graph_dai_props *dai_props; - struct device *dev = &pdev->dev; - struct snd_soc_card *card; - int num, ret, i; - - /* Allocate the private data and the DAI link array */ - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - num = asoc_graph_get_dais_count(dev); - if (num == 0) - return -EINVAL; - - dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL); - dai_link = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL); - if (!dai_props || !dai_link) - return -ENOMEM; - - /* - * Use snd_soc_dai_link_component instead of legacy style - * It is codec only. but cpu/platform will be supported in the future. - * see - * soc-core.c :: snd_soc_init_multicodec() - */ - for (i = 0; i < num; i++) { - dai_link[i].codecs = &dai_props[i].codecs; - dai_link[i].num_codecs = 1; - dai_link[i].platform = &dai_props[i].platform; - } - - priv->dai_props = dai_props; - priv->dai_link = dai_link; - - /* Init snd_soc_card */ - card = graph_priv_to_card(priv); - card->owner = THIS_MODULE; - card->dev = dev; - card->dai_link = priv->dai_link; - card->num_links = num; - card->codec_conf = &priv->codec_conf; - card->num_configs = 1; - - ret = asoc_graph_card_parse_of(priv); - if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "parse error %d\n", ret); - goto err; - } - - snd_soc_card_set_drvdata(card, priv); - - ret = devm_snd_soc_register_card(dev, card); - if (ret < 0) - goto err; - - return 0; -err: - asoc_simple_card_clean_reference(card); - - return ret; -} - -static int asoc_graph_card_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - return asoc_simple_card_clean_reference(card); -} - -static const struct of_device_id asoc_graph_of_match[] = { - { .compatible = "audio-graph-scu-card", }, - {}, -}; -MODULE_DEVICE_TABLE(of, asoc_graph_of_match); - -static struct platform_driver asoc_graph_card = { - .driver = { - .name = "asoc-audio-graph-scu-card", - .pm = &snd_soc_pm_ops, - .of_match_table = asoc_graph_of_match, - }, - .probe = asoc_graph_card_probe, - .remove = asoc_graph_card_remove, -}; -module_platform_driver(asoc_graph_card); - -MODULE_ALIAS("platform:asoc-audio-graph-scu-card"); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("ASoC Audio Graph SCU Sound Card"); -MODULE_AUTHOR("Kuninori Morimoto "); diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index f34cc6cddfa283..3c0901df57960a 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -32,10 +32,11 @@ void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data, } EXPORT_SYMBOL_GPL(asoc_simple_card_convert_fixup); -void asoc_simple_card_parse_convert(struct device *dev, char *prefix, +void asoc_simple_card_parse_convert(struct device *dev, + struct device_node *np, + char *prefix, struct asoc_simple_card_data *data) { - struct device_node *np = dev->of_node; char prop[128]; if (!prefix) @@ -151,21 +152,19 @@ int asoc_simple_card_parse_card_name(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name); -static void asoc_simple_card_clk_register(struct asoc_simple_dai *dai, - struct clk *clk) -{ - dai->clk = clk; -} - int asoc_simple_card_clk_enable(struct asoc_simple_dai *dai) { - return clk_prepare_enable(dai->clk); + if (dai) + return clk_prepare_enable(dai->clk); + + return 0; } EXPORT_SYMBOL_GPL(asoc_simple_card_clk_enable); void asoc_simple_card_clk_disable(struct asoc_simple_dai *dai) { - clk_disable_unprepare(dai->clk); + if (dai) + clk_disable_unprepare(dai->clk); } EXPORT_SYMBOL_GPL(asoc_simple_card_clk_disable); @@ -200,7 +199,7 @@ int asoc_simple_card_parse_clk(struct device *dev, if (!IS_ERR(clk)) { simple_dai->sysclk = clk_get_rate(clk); - asoc_simple_card_clk_register(simple_dai, clk); + simple_dai->clk = clk; } else if (!of_property_read_u32(node, "system-clock-frequency", &val)) { simple_dai->sysclk = val; } else { @@ -272,13 +271,32 @@ static int asoc_simple_card_get_dai_id(struct device_node *ep) { struct device_node *node; struct device_node *endpoint; + struct of_endpoint info; int i, id; int ret; + /* use driver specified DAI ID if exist */ ret = snd_soc_get_dai_id(ep); if (ret != -ENOTSUPP) return ret; + /* use endpoint/port reg if exist */ + ret = of_graph_parse_endpoint(ep, &info); + if (ret == 0) { + /* + * Because it will count port/endpoint if it doesn't have "reg". + * But, we can't judge whether it has "no reg", or "reg = <0>" + * only of_graph_parse_endpoint(). + * We need to check "reg" property + */ + if (of_get_property(ep, "reg", NULL)) + return info.id; + + node = of_get_parent(ep); + of_node_put(node); + if (of_get_property(node, "reg", NULL)) + return info.port; + } node = of_graph_get_port_parent(ep); /* @@ -348,6 +366,9 @@ int asoc_simple_card_init_dai(struct snd_soc_dai *dai, { int ret; + if (!simple_dai) + return 0; + if (simple_dai->sysclk) { ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk, simple_dai->clk_direction); @@ -376,8 +397,8 @@ EXPORT_SYMBOL_GPL(asoc_simple_card_init_dai); int asoc_simple_card_canonicalize_dailink(struct snd_soc_dai_link *dai_link) { /* Assumes platform == cpu */ - if (!dai_link->platform->of_node) - dai_link->platform->of_node = dai_link->cpu_of_node; + if (!dai_link->platforms->of_node) + dai_link->platforms->of_node = dai_link->cpu_of_node; return 0; @@ -415,8 +436,7 @@ int asoc_simple_card_clean_reference(struct snd_soc_card *card) EXPORT_SYMBOL_GPL(asoc_simple_card_clean_reference); int asoc_simple_card_of_parse_routing(struct snd_soc_card *card, - char *prefix, - int optional) + char *prefix) { struct device_node *node = card->dev->of_node; char prop[128]; @@ -426,11 +446,8 @@ int asoc_simple_card_of_parse_routing(struct snd_soc_card *card, snprintf(prop, sizeof(prop), "%s%s", prefix, "routing"); - if (!of_property_read_bool(node, prop)) { - if (optional) - return 0; - return -EINVAL; - } + if (!of_property_read_bool(node, prop)) + return 0; return snd_soc_of_parse_audio_routing(card, prop); } diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 5a3f59aa4ba55c..d8a0d1ec256e06 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -15,19 +15,29 @@ #include #include -struct simple_card_data { +struct simple_priv { struct snd_soc_card snd_card; struct simple_dai_props { - struct asoc_simple_dai cpu_dai; - struct asoc_simple_dai codec_dai; + struct asoc_simple_dai *cpu_dai; + struct asoc_simple_dai *codec_dai; struct snd_soc_dai_link_component codecs; /* single codec */ - struct snd_soc_dai_link_component platform; + struct snd_soc_dai_link_component platforms; + struct asoc_simple_card_data adata; + struct snd_soc_codec_conf *codec_conf; unsigned int mclk_fs; } *dai_props; - unsigned int mclk_fs; struct asoc_simple_jack hp_jack; struct asoc_simple_jack mic_jack; struct snd_soc_dai_link *dai_link; + struct asoc_simple_dai *dais; + struct snd_soc_codec_conf *codec_conf; +}; + +struct link_info { + int dais; /* number of dai */ + int link; /* number of link */ + int conf; /* number of codec_conf */ + int cpu; /* turn for CPU / Codec */ }; #define simple_priv_to_card(priv) (&(priv)->snd_card) @@ -39,40 +49,43 @@ struct simple_card_data { #define CELL "#sound-dai-cells" #define PREFIX "simple-audio-card," -static int asoc_simple_card_startup(struct snd_pcm_substream *substream) +static int simple_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); int ret; - ret = asoc_simple_card_clk_enable(&dai_props->cpu_dai); + ret = asoc_simple_card_clk_enable(dai_props->cpu_dai); if (ret) return ret; - ret = asoc_simple_card_clk_enable(&dai_props->codec_dai); + ret = asoc_simple_card_clk_enable(dai_props->codec_dai); if (ret) - asoc_simple_card_clk_disable(&dai_props->cpu_dai); + asoc_simple_card_clk_disable(dai_props->cpu_dai); return ret; } -static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) +static void simple_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); - asoc_simple_card_clk_disable(&dai_props->cpu_dai); + asoc_simple_card_clk_disable(dai_props->cpu_dai); - asoc_simple_card_clk_disable(&dai_props->codec_dai); + asoc_simple_card_clk_disable(dai_props->codec_dai); } -static int asoc_simple_set_clk_rate(struct asoc_simple_dai *simple_dai, - unsigned long rate) +static int simple_set_clk_rate(struct asoc_simple_dai *simple_dai, + unsigned long rate) { + if (!simple_dai) + return 0; + if (!simple_dai->clk) return 0; @@ -82,31 +95,29 @@ static int asoc_simple_set_clk_rate(struct asoc_simple_dai *simple_dai, return clk_set_rate(simple_dai->clk, rate); } -static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int simple_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); unsigned int mclk, mclk_fs = 0; int ret = 0; - if (priv->mclk_fs) - mclk_fs = priv->mclk_fs; - else if (dai_props->mclk_fs) + if (dai_props->mclk_fs) mclk_fs = dai_props->mclk_fs; if (mclk_fs) { mclk = params_rate(params) * mclk_fs; - ret = asoc_simple_set_clk_rate(&dai_props->codec_dai, mclk); + ret = simple_set_clk_rate(dai_props->codec_dai, mclk); if (ret < 0) return ret; - ret = asoc_simple_set_clk_rate(&dai_props->cpu_dai, mclk); + ret = simple_set_clk_rate(dai_props->cpu_dai, mclk); if (ret < 0) return ret; @@ -125,80 +136,249 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, return ret; } -static const struct snd_soc_ops asoc_simple_card_ops = { - .startup = asoc_simple_card_startup, - .shutdown = asoc_simple_card_shutdown, - .hw_params = asoc_simple_card_hw_params, +static const struct snd_soc_ops simple_ops = { + .startup = simple_startup, + .shutdown = simple_shutdown, + .hw_params = simple_hw_params, }; -static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) +static int simple_dai_init(struct snd_soc_pcm_runtime *rtd) { - struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *codec = rtd->codec_dai; - struct snd_soc_dai *cpu = rtd->cpu_dai; - struct simple_dai_props *dai_props = - simple_priv_to_props(priv, rtd->num); + struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); int ret; - ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai); + ret = asoc_simple_card_init_dai(rtd->codec_dai, + dai_props->codec_dai); if (ret < 0) return ret; - ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai); + ret = asoc_simple_card_init_dai(rtd->cpu_dai, + dai_props->cpu_dai); if (ret < 0) return ret; return 0; } -static int asoc_simple_card_dai_link_of(struct device_node *node, - struct simple_card_data *priv, - int idx, - bool is_top_level_node) +static int simple_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); + + asoc_simple_card_convert_fixup(&dai_props->adata, params); + + return 0; +} + +static void simple_get_conversion(struct device *dev, + struct device_node *np, + struct asoc_simple_card_data *adata) +{ + struct device_node *top = dev->of_node; + struct device_node *node = of_get_parent(np); + + asoc_simple_card_parse_convert(dev, top, PREFIX, adata); + asoc_simple_card_parse_convert(dev, node, PREFIX, adata); + asoc_simple_card_parse_convert(dev, node, NULL, adata); + asoc_simple_card_parse_convert(dev, np, NULL, adata); + + of_node_put(node); +} + +static int simple_dai_link_of_dpcm(struct simple_priv *priv, + struct device_node *np, + struct device_node *codec, + struct link_info *li, + bool is_top) { struct device *dev = simple_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx); - struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx); - struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai; - struct asoc_simple_dai *codec_dai = &dai_props->codec_dai; - struct device_node *cpu = NULL; - struct device_node *plat = NULL; - struct device_node *codec = NULL; + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); + struct asoc_simple_dai *dai; + struct snd_soc_dai_link_component *codecs = dai_link->codecs; + struct device_node *top = dev->of_node; + struct device_node *node = of_get_parent(np); char prop[128]; char *prefix = ""; - int ret, single_cpu; + int ret; + + /* + * |CPU |Codec : turn + * CPU |Pass |return + * Codec |return|Pass + * np + */ + if (li->cpu == (np == codec)) + return 0; + + dev_dbg(dev, "link_of DPCM (%pOF)\n", np); + + li->link++; + + of_node_put(node); /* For single DAI link & old style of DT node */ - if (is_top_level_node) + if (is_top) prefix = PREFIX; - snprintf(prop, sizeof(prop), "%scpu", prefix); - cpu = of_get_child_by_name(node, prop); + if (li->cpu) { + int is_single_links = 0; - if (!cpu) { - ret = -EINVAL; - dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); - goto dai_link_of_err; + /* BE is dummy */ + codecs->of_node = NULL; + codecs->dai_name = "snd-soc-dummy-dai"; + codecs->name = "snd-soc-dummy"; + + /* FE settings */ + dai_link->dynamic = 1; + dai_link->dpcm_merged_format = 1; + + dai = + dai_props->cpu_dai = &priv->dais[li->dais++]; + + ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL, + &is_single_links); + if (ret) + return ret; + + ret = asoc_simple_card_parse_clk_cpu(dev, np, dai_link, dai); + if (ret < 0) + return ret; + + ret = asoc_simple_card_set_dailink_name(dev, dai_link, + "fe.%s", + dai_link->cpu_dai_name); + if (ret < 0) + return ret; + + asoc_simple_card_canonicalize_cpu(dai_link, is_single_links); + } else { + struct snd_soc_codec_conf *cconf; + + /* FE is dummy */ + dai_link->cpu_of_node = NULL; + dai_link->cpu_dai_name = "snd-soc-dummy-dai"; + dai_link->cpu_name = "snd-soc-dummy"; + + /* BE settings */ + dai_link->no_pcm = 1; + dai_link->be_hw_params_fixup = simple_be_hw_params_fixup; + + dai = + dai_props->codec_dai = &priv->dais[li->dais++]; + + cconf = + dai_props->codec_conf = &priv->codec_conf[li->conf++]; + + ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL); + if (ret < 0) + return ret; + + ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, dai); + if (ret < 0) + return ret; + + ret = asoc_simple_card_set_dailink_name(dev, dai_link, + "be.%s", + codecs->dai_name); + if (ret < 0) + return ret; + + /* check "prefix" from top node */ + snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node, + PREFIX "prefix"); + snd_soc_of_parse_node_prefix(node, cconf, codecs->of_node, + "prefix"); + snd_soc_of_parse_node_prefix(np, cconf, codecs->of_node, + "prefix"); } + simple_get_conversion(dev, np, &dai_props->adata); + + ret = asoc_simple_card_of_parse_tdm(np, dai); + if (ret) + return ret; + + ret = asoc_simple_card_canonicalize_dailink(dai_link); + if (ret < 0) + return ret; + + snprintf(prop, sizeof(prop), "%smclk-fs", prefix); + of_property_read_u32(top, PREFIX "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(node, prop, &dai_props->mclk_fs); + of_property_read_u32(np, prop, &dai_props->mclk_fs); + + ret = asoc_simple_card_parse_daifmt(dev, node, codec, + prefix, &dai_link->dai_fmt); + if (ret < 0) + return ret; + + dai_link->dpcm_playback = 1; + dai_link->dpcm_capture = 1; + dai_link->ops = &simple_ops; + dai_link->init = simple_dai_init; + + return 0; +} + +static int simple_dai_link_of(struct simple_priv *priv, + struct device_node *np, + struct device_node *codec, + struct link_info *li, + bool is_top) +{ + struct device *dev = simple_priv_to_dev(priv); + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link); + struct asoc_simple_dai *cpu_dai; + struct asoc_simple_dai *codec_dai; + struct device_node *top = dev->of_node; + struct device_node *cpu = NULL; + struct device_node *node = NULL; + struct device_node *plat = NULL; + char prop[128]; + char *prefix = ""; + int ret, single_cpu; + + /* + * |CPU |Codec : turn + * CPU |Pass |return + * Codec |return|return + * np + */ + if (!li->cpu || np == codec) + return 0; + + cpu = np; + node = of_get_parent(np); + li->link++; + + dev_dbg(dev, "link_of (%pOF)\n", node); + + /* For single DAI link & old style of DT node */ + if (is_top) + prefix = PREFIX; + snprintf(prop, sizeof(prop), "%splat", prefix); plat = of_get_child_by_name(node, prop); - snprintf(prop, sizeof(prop), "%scodec", prefix); - codec = of_get_child_by_name(node, prop); - - if (!codec) { - ret = -EINVAL; - dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); - goto dai_link_of_err; - } + cpu_dai = + dai_props->cpu_dai = &priv->dais[li->dais++]; + codec_dai = + dai_props->codec_dai = &priv->dais[li->dais++]; ret = asoc_simple_card_parse_daifmt(dev, node, codec, prefix, &dai_link->dai_fmt); if (ret < 0) goto dai_link_of_err; - of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs); + snprintf(prop, sizeof(prop), "%smclk-fs", prefix); + of_property_read_u32(top, PREFIX "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(node, prop, &dai_props->mclk_fs); + of_property_read_u32(cpu, prop, &dai_props->mclk_fs); + of_property_read_u32(codec, prop, &dai_props->mclk_fs); ret = asoc_simple_card_parse_cpu(cpu, dai_link, DAI, CELL, &single_cpu); @@ -240,20 +420,87 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, if (ret < 0) goto dai_link_of_err; - dai_link->ops = &asoc_simple_card_ops; - dai_link->init = asoc_simple_card_dai_init; + dai_link->ops = &simple_ops; + dai_link->init = simple_dai_init; asoc_simple_card_canonicalize_cpu(dai_link, single_cpu); dai_link_of_err: - of_node_put(cpu); - of_node_put(codec); + of_node_put(node); return ret; } -static int asoc_simple_card_parse_aux_devs(struct device_node *node, - struct simple_card_data *priv) +static int simple_for_each_link(struct simple_priv *priv, + struct link_info *li, + int (*func_noml)(struct simple_priv *priv, + struct device_node *np, + struct device_node *codec, + struct link_info *li, bool is_top), + int (*func_dpcm)(struct simple_priv *priv, + struct device_node *np, + struct device_node *codec, + struct link_info *li, bool is_top)) +{ + struct device *dev = simple_priv_to_dev(priv); + struct device_node *top = dev->of_node; + struct device_node *node; + bool is_top = 0; + + /* Check if it has dai-link */ + node = of_get_child_by_name(top, PREFIX "dai-link"); + if (!node) { + node = top; + is_top = 1; + } + + /* loop for all dai-link */ + do { + struct asoc_simple_card_data adata; + struct device_node *codec; + struct device_node *np; + int num = of_get_child_count(node); + int ret; + + /* get codec */ + codec = of_get_child_by_name(node, is_top ? + PREFIX "codec" : "codec"); + if (!codec) + return -ENODEV; + + of_node_put(codec); + + /* get convert-xxx property */ + memset(&adata, 0, sizeof(adata)); + for_each_child_of_node(node, np) + simple_get_conversion(dev, np, &adata); + + /* loop for all CPU/Codec node */ + for_each_child_of_node(node, np) { + /* + * It is DPCM + * if it has many CPUs, + * or has convert-xxx property + */ + if (num > 2 || + adata.convert_rate || adata.convert_channels) + ret = func_dpcm(priv, np, codec, li, is_top); + /* else normal sound */ + else + ret = func_noml(priv, np, codec, li, is_top); + + if (ret < 0) + return ret; + } + + node = of_get_next_child(top, node); + } while (!is_top && node); + + return 0; +} + +static int simple_parse_aux_devs(struct device_node *node, + struct simple_priv *priv) { struct device *dev = simple_priv_to_dev(priv); struct device_node *aux_node; @@ -283,67 +530,151 @@ static int asoc_simple_card_parse_aux_devs(struct device_node *node, return 0; } -static int asoc_simple_card_parse_of(struct simple_card_data *priv) +static int simple_parse_of(struct simple_priv *priv) { struct device *dev = simple_priv_to_dev(priv); + struct device_node *top = dev->of_node; struct snd_soc_card *card = simple_priv_to_card(priv); - struct device_node *dai_link; - struct device_node *node = dev->of_node; + struct link_info li; int ret; - if (!node) + if (!top) return -EINVAL; - dai_link = of_get_child_by_name(node, PREFIX "dai-link"); - ret = asoc_simple_card_of_parse_widgets(card, PREFIX); if (ret < 0) - goto card_parse_end; + return ret; - ret = asoc_simple_card_of_parse_routing(card, PREFIX, 1); + ret = asoc_simple_card_of_parse_routing(card, PREFIX); if (ret < 0) - goto card_parse_end; - - /* Factor to mclk, used in hw_params() */ - of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs); + return ret; /* Single/Muti DAI link(s) & New style of DT node */ - if (dai_link) { - struct device_node *np = NULL; - int i = 0; - - for_each_child_of_node(node, np) { - dev_dbg(dev, "\tlink %d:\n", i); - ret = asoc_simple_card_dai_link_of(np, priv, - i, false); - if (ret < 0) { - of_node_put(np); - goto card_parse_end; - } - i++; - } - } else { - /* For single DAI link & old style of DT node */ - ret = asoc_simple_card_dai_link_of(node, priv, 0, true); + memset(&li, 0, sizeof(li)); + for (li.cpu = 1; li.cpu >= 0; li.cpu--) { + /* + * Detect all CPU first, and Detect all Codec 2nd. + * + * In Normal sound case, all DAIs are detected + * as "CPU-Codec". + * + * In DPCM sound case, + * all CPUs are detected as "CPU-dummy", and + * all Codecs are detected as "dummy-Codec". + * To avoid random sub-device numbering, + * detect "dummy-Codec" in last; + */ + ret = simple_for_each_link(priv, &li, + simple_dai_link_of, + simple_dai_link_of_dpcm); if (ret < 0) - goto card_parse_end; + return ret; } ret = asoc_simple_card_parse_card_name(card, PREFIX); if (ret < 0) - goto card_parse_end; - - ret = asoc_simple_card_parse_aux_devs(node, priv); + return ret; -card_parse_end: - of_node_put(dai_link); + ret = simple_parse_aux_devs(top, priv); return ret; } -static int asoc_simple_soc_card_probe(struct snd_soc_card *card) +static int simple_count_noml(struct simple_priv *priv, + struct device_node *np, + struct device_node *codec, + struct link_info *li, bool is_top) { - struct simple_card_data *priv = snd_soc_card_get_drvdata(card); + li->dais++; /* CPU or Codec */ + if (np != codec) + li->link++; /* CPU-Codec */ + + return 0; +} + +static int simple_count_dpcm(struct simple_priv *priv, + struct device_node *np, + struct device_node *codec, + struct link_info *li, bool is_top) +{ + li->dais++; /* CPU or Codec */ + li->link++; /* CPU-dummy or dummy-Codec */ + if (np == codec) + li->conf++; + + return 0; +} + +static void simple_get_dais_count(struct simple_priv *priv, + struct link_info *li) +{ + struct device *dev = simple_priv_to_dev(priv); + struct device_node *top = dev->of_node; + + /* + * link_num : number of links. + * CPU-Codec / CPU-dummy / dummy-Codec + * dais_num : number of DAIs + * ccnf_num : number of codec_conf + * same number for "dummy-Codec" + * + * ex1) + * CPU0 --- Codec0 link : 5 + * CPU1 --- Codec1 dais : 7 + * CPU2 -/ ccnf : 1 + * CPU3 --- Codec2 + * + * => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec + * => 7 DAIs = 4xCPU + 3xCodec + * => 1 ccnf = 1xdummy-Codec + * + * ex2) + * CPU0 --- Codec0 link : 5 + * CPU1 --- Codec1 dais : 6 + * CPU2 -/ ccnf : 1 + * CPU3 -/ + * + * => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec + * => 6 DAIs = 4xCPU + 2xCodec + * => 1 ccnf = 1xdummy-Codec + * + * ex3) + * CPU0 --- Codec0 link : 6 + * CPU1 -/ dais : 6 + * CPU2 --- Codec1 ccnf : 2 + * CPU3 -/ + * + * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec + * => 6 DAIs = 4xCPU + 2xCodec + * => 2 ccnf = 2xdummy-Codec + * + * ex4) + * CPU0 --- Codec0 (convert-rate) link : 3 + * CPU1 --- Codec1 dais : 4 + * ccnf : 1 + * + * => 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec + * => 4 DAIs = 2xCPU + 2xCodec + * => 1 ccnf = 1xdummy-Codec + */ + if (!top) { + li->link = 1; + li->dais = 2; + li->conf = 0; + return; + } + + simple_for_each_link(priv, li, + simple_count_noml, + simple_count_dpcm); + + dev_dbg(dev, "link %d, dais %d, ccnf %d\n", + li->link, li->dais, li->conf); +} + +static int simple_soc_probe(struct snd_soc_card *card) +{ + struct simple_priv *priv = snd_soc_card_get_drvdata(card); int ret; ret = asoc_simple_card_init_hp(card, &priv->hp_jack, PREFIX); @@ -357,30 +688,39 @@ static int asoc_simple_soc_card_probe(struct snd_soc_card *card) return 0; } -static int asoc_simple_card_probe(struct platform_device *pdev) +static int simple_probe(struct platform_device *pdev) { - struct simple_card_data *priv; + struct simple_priv *priv; struct snd_soc_dai_link *dai_link; struct simple_dai_props *dai_props; + struct asoc_simple_dai *dais; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct snd_soc_card *card; - int num, ret, i; - - /* Get the number of DAI links */ - if (np && of_get_child_by_name(np, PREFIX "dai-link")) - num = of_get_child_count(np); - else - num = 1; + struct snd_soc_codec_conf *cconf; + struct link_info li; + int ret, i; /* Allocate the private data and the DAI link array */ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL); - dai_link = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL); - if (!dai_props || !dai_link) + card = simple_priv_to_card(priv); + card->owner = THIS_MODULE; + card->dev = dev; + card->probe = simple_soc_probe; + + memset(&li, 0, sizeof(li)); + simple_get_dais_count(priv, &li); + if (!li.link || !li.dais) + return -EINVAL; + + dai_props = devm_kcalloc(dev, li.link, sizeof(*dai_props), GFP_KERNEL); + dai_link = devm_kcalloc(dev, li.link, sizeof(*dai_link), GFP_KERNEL); + dais = devm_kcalloc(dev, li.dais, sizeof(*dais), GFP_KERNEL); + cconf = devm_kcalloc(dev, li.conf, sizeof(*cconf), GFP_KERNEL); + if (!dai_props || !dai_link || !dais) return -ENOMEM; /* @@ -389,26 +729,26 @@ static int asoc_simple_card_probe(struct platform_device *pdev) * see * soc-core.c :: snd_soc_init_multicodec() */ - for (i = 0; i < num; i++) { + for (i = 0; i < li.link; i++) { dai_link[i].codecs = &dai_props[i].codecs; dai_link[i].num_codecs = 1; - dai_link[i].platform = &dai_props[i].platform; + dai_link[i].platforms = &dai_props[i].platforms; + dai_link[i].num_platforms = 1; } - priv->dai_props = dai_props; - priv->dai_link = dai_link; + priv->dai_props = dai_props; + priv->dai_link = dai_link; + priv->dais = dais; + priv->codec_conf = cconf; - /* Init snd_soc_card */ - card = simple_priv_to_card(priv); - card->owner = THIS_MODULE; - card->dev = dev; card->dai_link = priv->dai_link; - card->num_links = num; - card->probe = asoc_simple_soc_card_probe; + card->num_links = li.link; + card->codec_conf = cconf; + card->num_configs = li.conf; if (np && of_device_is_available(np)) { - ret = asoc_simple_card_parse_of(priv); + ret = simple_parse_of(priv); if (ret < 0) { if (ret != -EPROBE_DEFER) dev_err(dev, "parse error %d\n", ret); @@ -419,6 +759,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) struct asoc_simple_card_info *cinfo; struct snd_soc_dai_link_component *codecs; struct snd_soc_dai_link_component *platform; + int dai_idx = 0; cinfo = dev->platform_data; if (!cinfo) { @@ -435,11 +776,14 @@ static int asoc_simple_card_probe(struct platform_device *pdev) return -EINVAL; } + dai_props->cpu_dai = &priv->dais[dai_idx++]; + dai_props->codec_dai = &priv->dais[dai_idx++]; + codecs = dai_link->codecs; codecs->name = cinfo->codec; codecs->dai_name = cinfo->codec_dai.name; - platform = dai_link->platform; + platform = dai_link->platforms; platform->name = cinfo->platform; card->name = (cinfo->card) ? cinfo->card : cinfo->name; @@ -447,11 +791,11 @@ static int asoc_simple_card_probe(struct platform_device *pdev) dai_link->stream_name = cinfo->name; dai_link->cpu_dai_name = cinfo->cpu_dai.name; dai_link->dai_fmt = cinfo->daifmt; - dai_link->init = asoc_simple_card_dai_init; - memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai, - sizeof(priv->dai_props->cpu_dai)); - memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai, - sizeof(priv->dai_props->codec_dai)); + dai_link->init = simple_dai_init; + memcpy(priv->dai_props->cpu_dai, &cinfo->cpu_dai, + sizeof(*priv->dai_props->cpu_dai)); + memcpy(priv->dai_props->codec_dai, &cinfo->codec_dai, + sizeof(*priv->dai_props->codec_dai)); } snd_soc_card_set_drvdata(card, priv); @@ -467,27 +811,28 @@ static int asoc_simple_card_probe(struct platform_device *pdev) return ret; } -static int asoc_simple_card_remove(struct platform_device *pdev) +static int simple_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); return asoc_simple_card_clean_reference(card); } -static const struct of_device_id asoc_simple_of_match[] = { +static const struct of_device_id simple_of_match[] = { { .compatible = "simple-audio-card", }, + { .compatible = "simple-scu-audio-card", }, {}, }; -MODULE_DEVICE_TABLE(of, asoc_simple_of_match); +MODULE_DEVICE_TABLE(of, simple_of_match); static struct platform_driver asoc_simple_card = { .driver = { .name = "asoc-simple-card", .pm = &snd_soc_pm_ops, - .of_match_table = asoc_simple_of_match, + .of_match_table = simple_of_match, }, - .probe = asoc_simple_card_probe, - .remove = asoc_simple_card_remove, + .probe = simple_probe, + .remove = simple_remove, }; module_platform_driver(asoc_simple_card); diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c deleted file mode 100644 index 85b46f0eae0ff2..00000000000000 --- a/sound/soc/generic/simple-scu-card.c +++ /dev/null @@ -1,330 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// ASoC simple SCU sound card support -// -// Copyright (C) 2015 Renesas Solutions Corp. -// Kuninori Morimoto -// -// based on ${LINUX}/sound/soc/generic/simple-card.c - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct simple_card_data { - struct snd_soc_card snd_card; - struct snd_soc_codec_conf codec_conf; - struct simple_dai_props { - struct asoc_simple_dai dai; - struct snd_soc_dai_link_component codecs; - struct snd_soc_dai_link_component platform; - } *dai_props; - struct snd_soc_dai_link *dai_link; - struct asoc_simple_card_data adata; -}; - -#define simple_priv_to_card(priv) (&(priv)->snd_card) -#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i)) -#define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev) -#define simple_priv_to_link(priv, i) (simple_priv_to_card(priv)->dai_link + (i)) - -#define DAI "sound-dai" -#define CELL "#sound-dai-cells" -#define PREFIX "simple-audio-card," - -static int asoc_simple_card_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *dai_props = - simple_priv_to_props(priv, rtd->num); - - return asoc_simple_card_clk_enable(&dai_props->dai); -} - -static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct simple_dai_props *dai_props = - simple_priv_to_props(priv, rtd->num); - - asoc_simple_card_clk_disable(&dai_props->dai); -} - -static const struct snd_soc_ops asoc_simple_card_ops = { - .startup = asoc_simple_card_startup, - .shutdown = asoc_simple_card_shutdown, -}; - -static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) -{ - struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai; - struct snd_soc_dai_link *dai_link; - struct simple_dai_props *dai_props; - int num = rtd->num; - - dai_link = simple_priv_to_link(priv, num); - dai_props = simple_priv_to_props(priv, num); - dai = dai_link->dynamic ? - rtd->cpu_dai : - rtd->codec_dai; - - return asoc_simple_card_init_dai(dai, &dai_props->dai); -} - -static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, - struct snd_pcm_hw_params *params) -{ - struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - - asoc_simple_card_convert_fixup(&priv->adata, params); - - return 0; -} - -static int asoc_simple_card_dai_link_of(struct device_node *np, - struct simple_card_data *priv, - unsigned int daifmt, - int idx, bool is_fe) -{ - struct device *dev = simple_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx); - struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx); - struct snd_soc_card *card = simple_priv_to_card(priv); - int ret; - - if (is_fe) { - int is_single_links = 0; - struct snd_soc_dai_link_component *codecs; - - /* BE is dummy */ - codecs = dai_link->codecs; - codecs->of_node = NULL; - codecs->dai_name = "snd-soc-dummy-dai"; - codecs->name = "snd-soc-dummy"; - - /* FE settings */ - dai_link->dynamic = 1; - dai_link->dpcm_merged_format = 1; - - ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL, - &is_single_links); - if (ret) - return ret; - - ret = asoc_simple_card_parse_clk_cpu(dev, np, dai_link, &dai_props->dai); - if (ret < 0) - return ret; - - ret = asoc_simple_card_set_dailink_name(dev, dai_link, - "fe.%s", - dai_link->cpu_dai_name); - if (ret < 0) - return ret; - - asoc_simple_card_canonicalize_cpu(dai_link, is_single_links); - } else { - /* FE is dummy */ - dai_link->cpu_of_node = NULL; - dai_link->cpu_dai_name = "snd-soc-dummy-dai"; - dai_link->cpu_name = "snd-soc-dummy"; - - /* BE settings */ - dai_link->no_pcm = 1; - dai_link->be_hw_params_fixup = asoc_simple_card_be_hw_params_fixup; - - ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL); - if (ret < 0) - return ret; - - ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, &dai_props->dai); - if (ret < 0) - return ret; - - ret = asoc_simple_card_set_dailink_name(dev, dai_link, - "be.%s", - dai_link->codecs->dai_name); - if (ret < 0) - return ret; - - snd_soc_of_parse_audio_prefix(card, - &priv->codec_conf, - dai_link->codecs->of_node, - PREFIX "prefix"); - } - - ret = asoc_simple_card_of_parse_tdm(np, &dai_props->dai); - if (ret) - return ret; - - ret = asoc_simple_card_canonicalize_dailink(dai_link); - if (ret < 0) - return ret; - - dai_link->dai_fmt = daifmt; - dai_link->dpcm_playback = 1; - dai_link->dpcm_capture = 1; - dai_link->ops = &asoc_simple_card_ops; - dai_link->init = asoc_simple_card_dai_init; - - return 0; -} - -static int asoc_simple_card_parse_of(struct simple_card_data *priv) - -{ - struct device *dev = simple_priv_to_dev(priv); - struct device_node *np; - struct snd_soc_card *card = simple_priv_to_card(priv); - struct device_node *node = dev->of_node; - unsigned int daifmt = 0; - bool is_fe; - int ret, i; - - if (!node) - return -EINVAL; - - ret = asoc_simple_card_of_parse_widgets(card, PREFIX); - if (ret < 0) - return ret; - - ret = asoc_simple_card_of_parse_routing(card, PREFIX, 0); - if (ret < 0) - return ret; - - asoc_simple_card_parse_convert(dev, PREFIX, &priv->adata); - - /* find 1st codec */ - np = of_get_child_by_name(node, PREFIX "codec"); - if (!np) - return -ENODEV; - - ret = asoc_simple_card_parse_daifmt(dev, node, np, PREFIX, &daifmt); - if (ret < 0) - return ret; - - i = 0; - for_each_child_of_node(node, np) { - is_fe = false; - if (strcmp(np->name, PREFIX "cpu") == 0) - is_fe = true; - - ret = asoc_simple_card_dai_link_of(np, priv, daifmt, i, is_fe); - if (ret < 0) - return ret; - i++; - } - - ret = asoc_simple_card_parse_card_name(card, PREFIX); - if (ret < 0) - return ret; - - return 0; -} - -static int asoc_simple_card_probe(struct platform_device *pdev) -{ - struct simple_card_data *priv; - struct snd_soc_dai_link *dai_link; - struct simple_dai_props *dai_props; - struct snd_soc_card *card; - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - int num, ret, i; - - /* Allocate the private data */ - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - num = of_get_child_count(np); - - dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL); - dai_link = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL); - if (!dai_props || !dai_link) - return -ENOMEM; - - /* - * Use snd_soc_dai_link_component instead of legacy style - * It is codec only. but cpu/platform will be supported in the future. - * see - * soc-core.c :: snd_soc_init_multicodec() - */ - for (i = 0; i < num; i++) { - dai_link[i].codecs = &dai_props[i].codecs; - dai_link[i].num_codecs = 1; - dai_link[i].platform = &dai_props[i].platform; - } - - priv->dai_props = dai_props; - priv->dai_link = dai_link; - - /* Init snd_soc_card */ - card = simple_priv_to_card(priv); - card->owner = THIS_MODULE; - card->dev = dev; - card->dai_link = priv->dai_link; - card->num_links = num; - card->codec_conf = &priv->codec_conf; - card->num_configs = 1; - - ret = asoc_simple_card_parse_of(priv); - if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "parse error %d\n", ret); - goto err; - } - - snd_soc_card_set_drvdata(card, priv); - - ret = devm_snd_soc_register_card(dev, card); - if (ret < 0) - goto err; - - return 0; -err: - asoc_simple_card_clean_reference(card); - - return ret; -} - -static int asoc_simple_card_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - return asoc_simple_card_clean_reference(card); -} - -static const struct of_device_id asoc_simple_of_match[] = { - { .compatible = "renesas,rsrc-card", }, - { .compatible = "simple-scu-audio-card", }, - {}, -}; -MODULE_DEVICE_TABLE(of, asoc_simple_of_match); - -static struct platform_driver asoc_simple_card = { - .driver = { - .name = "simple-scu-audio-card", - .pm = &snd_soc_pm_ops, - .of_match_table = asoc_simple_of_match, - }, - .probe = asoc_simple_card_probe, - .remove = asoc_simple_card_remove, -}; - -module_platform_driver(asoc_simple_card); - -MODULE_ALIAS("platform:asoc-simple-scu-card"); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("ASoC Simple SCU Sound Card"); -MODULE_AUTHOR("Kuninori Morimoto "); diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c index fa9c4cf97686d9..1364e4e601d837 100644 --- a/sound/soc/intel/boards/bytcht_es8316.c +++ b/sound/soc/intel/boards/bytcht_es8316.c @@ -437,7 +437,7 @@ static const struct acpi_gpio_mapping byt_cht_es8316_gpios[] = { static int snd_byt_cht_es8316_mc_probe(struct platform_device *pdev) { - const char * const mic_name[] = { "in1", "in2" }; + static const char * const mic_name[] = { "in1", "in2" }; struct byt_cht_es8316_private *priv; struct device *dev = &pdev->dev; struct snd_soc_acpi_mach *mach; diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index ca8b4d5ff70f9f..a79466c8fb2961 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -1149,7 +1149,7 @@ struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) { - const char * const map_name[] = { "dmic1", "dmic2", "in1", "in3" }; + static const char * const map_name[] = { "dmic1", "dmic2", "in1", "in3" }; const struct dmi_system_id *dmi_id; struct byt_rt5640_private *priv; struct snd_soc_acpi_mach *mach; diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index b618d984e2d566..e6945d11c8abde 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -919,7 +919,7 @@ struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) { - const char * const mic_name[] = { "dmic", "in1", "in2", "in12" }; + static const char * const mic_name[] = { "dmic", "in1", "in2", "in12" }; struct byt_rt5651_private *priv; struct snd_soc_acpi_mach *mach; struct device *codec_dev; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index fc7dadfbc10fa9..cfc5f3bbc57a3b 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -425,6 +425,14 @@ struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime); +static void snd_soc_flush_all_delayed_work(struct snd_soc_card *card) +{ + struct snd_soc_pcm_runtime *rtd; + + for_each_card_rtds(card, rtd) + flush_delayed_work(&rtd->delayed_work); +} + static void codec2codec_close_delayed_work(struct work_struct *work) { /* @@ -494,8 +502,7 @@ int snd_soc_suspend(struct device *dev) } /* close any waiting streams */ - for_each_card_rtds(card, rtd) - flush_delayed_work(&rtd->delayed_work); + snd_soc_flush_all_delayed_work(card); for_each_card_rtds(card, rtd) { @@ -735,14 +742,19 @@ static struct snd_soc_component *soc_find_component( const struct device_node *of_node, const char *name) { struct snd_soc_component *component; + struct device_node *component_of_node; lockdep_assert_held(&client_mutex); for_each_component(component) { if (of_node) { - if (component->dev->of_node == 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 (strcmp(component->name, name) == 0) { + } else if (name && strcmp(component->name, name) == 0) { return component; } } @@ -903,8 +915,8 @@ static int soc_bind_dai_link(struct snd_soc_card *card, for (i = 0; i < rtd->num_codecs; i++) { codec_dais[i] = snd_soc_find_dai(&codecs[i]); if (!codec_dais[i]) { - dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n", - codecs[i].dai_name); + dev_info(card->dev, "ASoC: CODEC DAI %s not registered\n", + codecs[i].dai_name); goto _err_defer; } snd_soc_rtdcom_add(rtd, codec_dais[i]->component); @@ -915,7 +927,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card, /* find one from the set of registered platforms */ for_each_component(component) { - if (!snd_soc_is_matching_component(dai_link->platform, + if (!snd_soc_is_matching_component(dai_link->platforms, component)) continue; @@ -930,21 +942,24 @@ static int soc_bind_dai_link(struct snd_soc_card *card, return -EPROBE_DEFER; } +static void soc_cleanup_component(struct snd_soc_component *component) +{ + list_del(&component->card_list); + snd_soc_dapm_free(snd_soc_component_get_dapm(component)); + soc_cleanup_component_debugfs(component); + component->card = NULL; + module_put(component->dev->driver->owner); +} + static void soc_remove_component(struct snd_soc_component *component) { if (!component->card) return; - list_del(&component->card_list); - if (component->driver->remove) component->driver->remove(component); - snd_soc_dapm_free(snd_soc_component_get_dapm(component)); - - soc_cleanup_component_debugfs(component); - component->card = NULL; - module_put(component->dev->driver->owner); + soc_cleanup_component(component); } static void soc_remove_dai(struct snd_soc_dai *dai, int order) @@ -1026,7 +1041,7 @@ 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->platform; + struct snd_soc_dai_link_component *platform = dai_link->platforms; /* * REMOVE ME @@ -1039,17 +1054,19 @@ static int snd_soc_init_platform(struct snd_soc_card *card, * soc.h :: struct snd_soc_dai_link */ /* convert Legacy platform link */ - if (!platform) { + if (!platform || dai_link->legacy_platform) { platform = devm_kzalloc(card->dev, sizeof(struct snd_soc_dai_link_component), GFP_KERNEL); if (!platform) return -ENOMEM; - dai_link->platform = platform; - platform->name = dai_link->platform_name; - platform->of_node = dai_link->platform_of_node; - platform->dai_name = NULL; + 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 */ @@ -1135,16 +1152,33 @@ static int soc_init_dai_link(struct snd_soc_card *card, } } + /* FIXME */ + if (link->num_platforms > 1) { + dev_err(card->dev, + "ASoC: multi platform is not yet supported %s\n", + link->name); + return -EINVAL; + } + /* * Platform may be specified by either name or OF node, but * can be left unspecified, and a dummy platform will be used. */ - if (link->platform->name && link->platform->of_node) { + if (link->platforms->name && link->platforms->of_node) { dev_err(card->dev, "ASoC: Both platform name/of_node are set for %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 @@ -1156,6 +1190,15 @@ static int soc_init_dai_link(struct snd_soc_card *card, link->name); return -EINVAL; } + + /* + * 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)) + return -EPROBE_DEFER; + /* * At least one of CPU DAI name or CPU device name/node must be * specified @@ -1325,6 +1368,8 @@ static int soc_probe_component(struct snd_soc_card *card, component->card = card; dapm->card = card; + INIT_LIST_HEAD(&component->card_list); + INIT_LIST_HEAD(&dapm->list); soc_set_name_prefix(card, component); soc_init_component_debugfs(component); @@ -1387,12 +1432,9 @@ static int soc_probe_component(struct snd_soc_card *card, /* see for_each_card_components */ list_add(&component->card_list, &card->component_dev_list); - return 0; - err_probe: - soc_cleanup_component_debugfs(component); - component->card = NULL; - module_put(component->dev->driver->owner); + if (ret < 0) + soc_cleanup_component(component); return ret; } @@ -1577,27 +1619,24 @@ static int soc_probe_link_dais(struct snd_soc_card *card, dai_link->stream_name); return ret; } - } else { - - if (!dai_link->params) { - /* create the pcm */ - ret = soc_new_pcm(rtd, num); - if (ret < 0) { - dev_err(card->dev, "ASoC: can't create pcm %s :%d\n", - dai_link->stream_name, ret); - return ret; - } - ret = soc_link_dai_pcm_new(&cpu_dai, 1, rtd); - if (ret < 0) - return ret; - ret = soc_link_dai_pcm_new(rtd->codec_dais, - rtd->num_codecs, rtd); - if (ret < 0) - return ret; - } else { - INIT_DELAYED_WORK(&rtd->delayed_work, - codec2codec_close_delayed_work); + } else if (!dai_link->params) { + /* create the pcm */ + ret = soc_new_pcm(rtd, num); + if (ret < 0) { + dev_err(card->dev, "ASoC: can't create pcm %s :%d\n", + dai_link->stream_name, ret); + return ret; } + ret = soc_link_dai_pcm_new(&cpu_dai, 1, rtd); + if (ret < 0) + return ret; + ret = soc_link_dai_pcm_new(rtd->codec_dais, + rtd->num_codecs, rtd); + if (ret < 0) + return ret; + } else { + INIT_DELAYED_WORK(&rtd->delayed_work, + codec2codec_close_delayed_work); } return 0; @@ -1940,7 +1979,7 @@ static void soc_check_tplg_fes(struct snd_soc_card *card) dev_err(card->dev, "init platform error"); continue; } - dai_link->platform->name = component->name; + dai_link->platforms->name = component->name; /* convert non BE into BE */ dai_link->no_pcm = 1; @@ -1976,6 +2015,29 @@ static void soc_check_tplg_fes(struct snd_soc_card *card) } } +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) + snd_card_free(card->snd_card); + + /* remove and free each DAI */ + soc_remove_dai_links(card); + soc_remove_pcm_runtimes(card); + + /* remove auxiliary devices */ + soc_remove_aux_devices(card); + + snd_soc_dapm_free(&card->dapm); + soc_cleanup_card_debugfs(card); + + /* remove the card */ + if (card->remove) + card->remove(card); + + return 0; +} + static int snd_soc_instantiate_card(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd; @@ -1985,6 +2047,11 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) mutex_lock(&client_mutex); mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT); + card->dapm.bias_level = SND_SOC_BIAS_OFF; + card->dapm.dev = card->dev; + card->dapm.card = card; + list_add(&card->dapm.list, &card->dapm_list); + /* check whether any platform is ignore machine FE and using topology */ soc_check_tplg_fes(card); @@ -1992,14 +2059,14 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) for_each_card_prelinks(card, i, dai_link) { ret = soc_bind_dai_link(card, dai_link); if (ret != 0) - goto base_error; + goto probe_end; } /* bind aux_devs too */ for (i = 0; i < card->num_aux_devs; i++) { ret = soc_bind_aux_dev(card, i); if (ret != 0) - goto base_error; + goto probe_end; } /* add predefined DAI links to the list */ @@ -2013,16 +2080,11 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) dev_err(card->dev, "ASoC: can't create sound card for card %s: %d\n", card->name, ret); - goto base_error; + goto probe_end; } soc_init_card_debugfs(card); - card->dapm.bias_level = SND_SOC_BIAS_OFF; - card->dapm.dev = card->dev; - card->dapm.card = card; - list_add(&card->dapm.list, &card->dapm_list); - #ifdef CONFIG_DEBUG_FS snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root); #endif @@ -2044,7 +2106,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) if (card->probe) { ret = card->probe(card); if (ret < 0) - goto card_probe_error; + goto probe_end; } /* probe all components used by DAI links on this card */ @@ -2055,7 +2117,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) dev_err(card->dev, "ASoC: failed to instantiate card %d\n", ret); - goto probe_dai_err; + goto probe_end; } } } @@ -2063,7 +2125,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) /* probe auxiliary components */ ret = soc_probe_aux_devices(card); if (ret < 0) - goto probe_dai_err; + goto probe_end; /* * Find new DAI links added during probing components and bind them. @@ -2075,10 +2137,10 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) ret = soc_init_dai_link(card, dai_link); if (ret) - goto probe_dai_err; + goto probe_end; ret = soc_bind_dai_link(card, dai_link); if (ret) - goto probe_dai_err; + goto probe_end; } /* probe all DAI links on this card */ @@ -2089,7 +2151,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) dev_err(card->dev, "ASoC: failed to instantiate card %d\n", ret); - goto probe_dai_err; + goto probe_end; } } } @@ -2136,7 +2198,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) if (ret < 0) { dev_err(card->dev, "ASoC: %s late_probe() failed: %d\n", card->name, ret); - goto probe_aux_dev_err; + goto probe_end; } } @@ -2146,33 +2208,17 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) if (ret < 0) { dev_err(card->dev, "ASoC: failed to register soundcard %d\n", ret); - goto probe_aux_dev_err; + goto probe_end; } card->instantiated = 1; dapm_mark_endpoints_dirty(card); snd_soc_dapm_sync(&card->dapm); - mutex_unlock(&card->mutex); - mutex_unlock(&client_mutex); - - return 0; - -probe_aux_dev_err: - soc_remove_aux_devices(card); - -probe_dai_err: - soc_remove_dai_links(card); - -card_probe_error: - if (card->remove) - card->remove(card); - snd_soc_dapm_free(&card->dapm); - soc_cleanup_card_debugfs(card); - snd_card_free(card->snd_card); +probe_end: + if (ret < 0) + soc_cleanup_card_resources(card); -base_error: - soc_remove_pcm_runtimes(card); mutex_unlock(&card->mutex); mutex_unlock(&client_mutex); @@ -2201,34 +2247,6 @@ static int soc_probe(struct platform_device *pdev) return snd_soc_register_card(card); } -static int soc_cleanup_card_resources(struct snd_soc_card *card) -{ - struct snd_soc_pcm_runtime *rtd; - - /* make sure any delayed work runs */ - for_each_card_rtds(card, rtd) - flush_delayed_work(&rtd->delayed_work); - - /* free the ALSA card at first; this syncs with pending operations */ - snd_card_free(card->snd_card); - - /* remove and free each DAI */ - soc_remove_dai_links(card); - soc_remove_pcm_runtimes(card); - - /* remove auxiliary devices */ - soc_remove_aux_devices(card); - - snd_soc_dapm_free(&card->dapm); - soc_cleanup_card_debugfs(card); - - /* remove the card */ - if (card->remove) - card->remove(card); - - return 0; -} - /* removes a socdev */ static int soc_remove(struct platform_device *pdev) { @@ -2250,8 +2268,7 @@ int snd_soc_poweroff(struct device *dev) * Flush out pmdown_time work - we actually do want to run it * now, we're shutting down so no imminent restart. */ - for_each_card_rtds(card, rtd) - flush_delayed_work(&rtd->delayed_work); + snd_soc_flush_all_delayed_work(card); snd_soc_dapm_shutdown(card); @@ -2795,6 +2812,7 @@ static void snd_soc_unbind_card(struct snd_soc_card *card, bool unregister) if (card->instantiated) { card->instantiated = false; snd_soc_dapm_shutdown(card); + snd_soc_flush_all_delayed_work(card); soc_cleanup_card_resources(card); if (!unregister) list_add(&card->list, &unbind_card_list);