[alsa-devel] [PATCH v2 0/7] simple card
This patch series has been test on VF610 Tower board.
Changed in V2: - Revise the problem from Jean-Francois Moine. - Add binding documentation patch.
Xiubo Li (7): ASoC: simple-card: fix __asoc_simple_card_dai_init ASoC: simple-card: simplify the daifmt code ASoC: simple-card: Add snd_card's name parsing from DT node support ASoC: simple-card: add tdm slot supports ASoC: add snd_soc_of_parse_audio_simple_widgets for DT ASoC: simple-card: add off-codec widgets supports. ASoC: binding: for new properties documenting and usage
.../devicetree/bindings/sound/simple-card.txt | 17 +++- include/sound/simple_card.h | 8 ++ include/sound/soc.h | 2 + sound/soc/generic/simple-card.c | 112 +++++++++++++++++---- sound/soc/soc-core.c | 87 ++++++++++++++++ 5 files changed, 204 insertions(+), 22 deletions(-)
If the CPU/CODEC DAI set_sysclk() is not support, the -ENOTSUPP will returnd. Here do the check like set_fmt().
Signed-off-by: Xiubo Li Li.Xiubo@freescale.com --- sound/soc/generic/simple-card.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-)
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 6443c87..6366f3f 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -27,21 +27,29 @@ static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, struct asoc_simple_dai *set, unsigned int daifmt) { - int ret = 0; + int ret;
daifmt |= set->fmt;
- if (daifmt) + if (daifmt) { ret = snd_soc_dai_set_fmt(dai, daifmt); - - if (ret == -ENOTSUPP) { - dev_dbg(dai->dev, "ASoC: set_fmt is not supported\n"); - ret = 0; + if (ret && ret != -ENOTSUPP) { + dev_err(dai->dev, "simple-card: set_fmt error\n"); + goto err; + } }
- if (!ret && set->sysclk) + if (set->sysclk) { ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0); + if (ret && ret != -ENOTSUPP) { + dev_err(dai->dev, "simple-card: set_sysclk error\n"); + goto err; + } + } + + ret = 0;
+err: return ret; }
On Fri, Jan 24, 2014 at 03:43:00PM +0800, Xiubo Li wrote:
If the CPU/CODEC DAI set_sysclk() is not support, the -ENOTSUPP will returnd. Here do the check like set_fmt().
Applied, thanks.
In the asoc_simple_card_parse_of() will parse the device node's CPU/CODEC DAI commone fmts, and then in asoc_simple_card_sub_parse_of() will parse the CPU/CODEC DAI's sub-node fmts, so we can combine the info->daifmt and info->set.fmt in asoc_simple_card_sub_parse_of() not while just before _set_fmt().
And this will be more easy to add new functions, such as supporting _set_tdm_slot(), etc.
Signed-off-by: Xiubo Li Li.Xiubo@freescale.com --- sound/soc/generic/simple-card.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-)
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 6366f3f..65833fe 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -24,15 +24,12 @@ struct simple_card_data { };
static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, - struct asoc_simple_dai *set, - unsigned int daifmt) + struct asoc_simple_dai *set) { int ret;
- daifmt |= set->fmt; - - if (daifmt) { - ret = snd_soc_dai_set_fmt(dai, daifmt); + if (set->fmt) { + ret = snd_soc_dai_set_fmt(dai, set->fmt); if (ret && ret != -ENOTSUPP) { dev_err(dai->dev, "simple-card: set_fmt error\n"); goto err; @@ -59,14 +56,13 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *codec = rtd->codec_dai; struct snd_soc_dai *cpu = rtd->cpu_dai; - unsigned int daifmt = priv->daifmt; int ret;
- ret = __asoc_simple_card_dai_init(codec, &priv->codec_dai, daifmt); + ret = __asoc_simple_card_dai_init(codec, &priv->codec_dai); if (ret < 0) return ret;
- ret = __asoc_simple_card_dai_init(cpu, &priv->cpu_dai, daifmt); + ret = __asoc_simple_card_dai_init(cpu, &priv->cpu_dai); if (ret < 0) return ret;
@@ -75,6 +71,7 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
static int asoc_simple_card_sub_parse_of(struct device_node *np, + unsigned int daifmt, struct asoc_simple_dai *dai, const struct device_node **p_node, const char **name) @@ -103,6 +100,7 @@ asoc_simple_card_sub_parse_of(struct device_node *np, * and specific "format" if it has */ dai->fmt = snd_soc_of_parse_daifmt(np, NULL); + dai->fmt |= daifmt;
/* * dai->sysclk come from @@ -161,7 +159,7 @@ static int asoc_simple_card_parse_of(struct device_node *node, ret = -EINVAL; np = of_get_child_by_name(node, "simple-audio-card,cpu"); if (np) - ret = asoc_simple_card_sub_parse_of(np, + ret = asoc_simple_card_sub_parse_of(np, priv->daifmt, &priv->cpu_dai, &dai_link->cpu_of_node, &dai_link->cpu_dai_name); @@ -172,7 +170,7 @@ static int asoc_simple_card_parse_of(struct device_node *node, ret = -EINVAL; np = of_get_child_by_name(node, "simple-audio-card,codec"); if (np) - ret = asoc_simple_card_sub_parse_of(np, + ret = asoc_simple_card_sub_parse_of(np, priv->daifmt, &priv->codec_dai, &dai_link->codec_of_node, &dai_link->codec_dai_name);
On Fri, Jan 24, 2014 at 03:43:01PM +0800, Xiubo Li wrote:
In the asoc_simple_card_parse_of() will parse the device node's CPU/CODEC DAI commone fmts, and then in asoc_simple_card_sub_parse_of() will parse the CPU/CODEC DAI's sub-node fmts, so we can combine the info->daifmt and info->set.fmt in asoc_simple_card_sub_parse_of() not while just before _set_fmt().
Applied, thanks.
If the DT is used and the CPU DAI device has only one DAI, the card name will be like :
ALSA device list: 0: 40031000.sai-sgtl5000
And this name maybe a little ugly to some customers, so here the card name parsing from DT node is supported.
Signed-off-by: Xiubo Li Li.Xiubo@freescale.com --- sound/soc/generic/simple-card.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 65833fe..0890fcd 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -143,6 +143,9 @@ static int asoc_simple_card_parse_of(struct device_node *node, char *name; int ret;
+ /* parsing the card name from DT */ + snd_soc_of_parse_card_name(&priv->snd_card, "simple-audio-card,name"); + /* get CPU/CODEC common format via simple-audio-card,format */ priv->daifmt = snd_soc_of_parse_daifmt(node, "simple-audio-card,") & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK); @@ -187,7 +190,8 @@ static int asoc_simple_card_parse_of(struct device_node *node, GFP_KERNEL); sprintf(name, "%s-%s", dai_link->cpu_dai_name, dai_link->codec_dai_name); - priv->snd_card.name = name; + if (!priv->snd_card.name) + priv->snd_card.name = name; dai_link->name = dai_link->stream_name = name;
/* simple-card assumes platform == cpu */
On Fri, Jan 24, 2014 at 03:43:02PM +0800, Xiubo Li wrote:
If the DT is used and the CPU DAI device has only one DAI, the card name will be like :
Applied, thanks.
For some CPU/CODEC DAI devices the tdm slot maybe needed. This patch adds the tdm slot supporting for simple-card driver.
The style of the tdm slot in DT:
For instance:
simple-tdm-slot = <0xffffffc 0xffffffc 2 0>;
Signed-off-by: Xiubo Li Li.Xiubo@freescale.com --- include/sound/simple_card.h | 8 ++++++ sound/soc/generic/simple-card.c | 60 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 65 insertions(+), 3 deletions(-)
diff --git a/include/sound/simple_card.h b/include/sound/simple_card.h index e1ac996..cfc5b66 100644 --- a/include/sound/simple_card.h +++ b/include/sound/simple_card.h @@ -14,10 +14,18 @@
#include <sound/soc.h>
+struct asoc_simple_tdm_slot { + unsigned int tx_mask; + unsigned int rx_mask; + int slots; + int slot_width; +}; + struct asoc_simple_dai { const char *name; unsigned int fmt; unsigned int sysclk; + struct asoc_simple_tdm_slot *tdm; };
struct asoc_simple_card_info { diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 0890fcd..3177aa8 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -9,11 +9,14 @@ * published by the Free Software Foundation. */ #include <linux/clk.h> +#include <linux/device.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/string.h> #include <sound/simple_card.h> +#include <sound/soc-dai.h> +#include <sound/soc.h>
struct simple_card_data { struct snd_soc_card snd_card; @@ -44,6 +47,17 @@ static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, } }
+ if (set->tdm) { + ret = snd_soc_dai_set_tdm_slot(dai, set->tdm->tx_mask, + set->tdm->rx_mask, + set->tdm->slots, + set->tdm->slot_width); + if (ret && ret != -ENOTSUPP) { + dev_err(dai->dev, "simple-card: set_tdm_slot error\n"); + goto err; + } + } + ret = 0;
err: @@ -70,11 +84,43 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) }
static int +asoc_simple_card_of_parse_tdm_slot(struct device_node *np, + struct device *dev, + struct asoc_simple_dai *dai, + const char *propname) +{ + struct asoc_simple_tdm_slot *tdm; + u32 out_value[4]; + int ret; + + if (!of_property_read_bool(np, propname)) + return 0; + + tdm = devm_kzalloc(dev, sizeof(*tdm), GFP_KERNEL); + if (!tdm) + return -ENOMEM; + + ret = of_property_read_u32_array(np, propname, out_value, 4); + if (ret) + return ret; + + tdm->tx_mask = out_value[0]; + tdm->rx_mask = out_value[1]; + tdm->slots = out_value[2]; + tdm->slot_width = out_value[3]; + + dai->tdm = tdm; + + return 0; +} + +static int asoc_simple_card_sub_parse_of(struct device_node *np, unsigned int daifmt, struct asoc_simple_dai *dai, const struct device_node **p_node, - const char **name) + const char **name, + struct device *dev) { struct device_node *node; struct clk *clk; @@ -94,6 +140,12 @@ asoc_simple_card_sub_parse_of(struct device_node *np, if (ret < 0) goto parse_error;
+ /* parse tdm_slot */ + ret = asoc_simple_card_of_parse_tdm_slot(np, dev, dai, + "simple-audio-card,tdm-slot"); + if (ret < 0) + goto parse_error; + /* * bitclock-inversion, frame-inversion * bitclock-master, frame-master @@ -165,7 +217,8 @@ static int asoc_simple_card_parse_of(struct device_node *node, ret = asoc_simple_card_sub_parse_of(np, priv->daifmt, &priv->cpu_dai, &dai_link->cpu_of_node, - &dai_link->cpu_dai_name); + &dai_link->cpu_dai_name, + dev); if (ret < 0) return ret;
@@ -176,7 +229,8 @@ static int asoc_simple_card_parse_of(struct device_node *node, ret = asoc_simple_card_sub_parse_of(np, priv->daifmt, &priv->codec_dai, &dai_link->codec_of_node, - &dai_link->codec_dai_name); + &dai_link->codec_dai_name, + dev); if (ret < 0) return ret;
On Fri, Jan 24, 2014 at 03:43:03PM +0800, Xiubo Li wrote:
For some CPU/CODEC DAI devices the tdm slot maybe needed. This patch adds the tdm slot supporting for simple-card driver.
The style of the tdm slot in DT:
For instance:
simple-tdm-slot = <0xffffffc 0xffffffc 2 0>;
It's not clear to me what such a definition for the TDM slots means - I looked at the binding document as well and that didn't clarify. We would need some definition of what the above means, though I think most likely the current definition is too specific and we need something more generic.
This patch adds snd_soc_of_parse_audio_simple_widgets() and supports below style of widgets name on DT:
"template-wname", "user supplied wname"
The "template-wname" includes: "Mic", "Line", "Hp", "Spk"...
For instance: simple-audio-widgets = "Mic", "Microphone Jack", "Line", "Line In Jack", "Line", "Line Out Jack", "Hp", "Headphone Jack", "Spk", "Speaker External";
Signed-off-by: Xiubo Li Li.Xiubo@freescale.com --- include/sound/soc.h | 2 ++ sound/soc/soc-core.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+)
diff --git a/include/sound/soc.h b/include/sound/soc.h index 9a00147..465dc6e 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1173,6 +1173,8 @@ void snd_soc_util_exit(void);
int snd_soc_of_parse_card_name(struct snd_soc_card *card, const char *propname); +int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, + const char *propname); int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname); unsigned int snd_soc_of_parse_daifmt(struct device_node *np, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index fe1df50..73fdf18 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4417,6 +4417,93 @@ int snd_soc_of_parse_card_name(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_card_name);
+static const struct snd_soc_dapm_widget simple_widgets[] = { + SND_SOC_DAPM_MIC("Mic", NULL), + SND_SOC_DAPM_LINE("Line", NULL), + SND_SOC_DAPM_HP("Hp", NULL), + SND_SOC_DAPM_SPK("Spk", NULL), +}; + +int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, + const char *propname) +{ + struct device_node *np = card->dev->of_node; + struct snd_soc_dapm_widget *widgets; + const char *template, *wname; + int i, j, num_widgets, ret; + + num_widgets = of_property_count_strings(np, propname); + if (num_widgets < 0) { + dev_err(card->dev, + "ASoC: Property '%s' does not exist\n", propname); + return -EINVAL; + } + if (num_widgets & 1) { + dev_err(card->dev, + "ASoC: Property '%s' length is not even\n", propname); + return -EINVAL; + } + + num_widgets /= 2; + if (!num_widgets) { + dev_err(card->dev, "ASoC: Property '%s's length is zero\n", + propname); + return -EINVAL; + } + + widgets = devm_kcalloc(card->dev, num_widgets, sizeof(*widgets), + GFP_KERNEL); + if (!widgets) { + dev_err(card->dev, + "ASoC: Could not allocate memory for widgets\n"); + return -ENOMEM; + } + + for (i = 0; i < num_widgets; i++) { + ret = of_property_read_string_index(np, propname, + 2 * i, &template); + if (ret) { + dev_err(card->dev, + "ASoC: Property '%s' index %d read error:%d\n", + propname, 2 * i, ret); + return -EINVAL; + } + + for (j = 0; j < ARRAY_SIZE(simple_widgets); j++) { + if (!strncmp(template, simple_widgets[j].name, + strlen(simple_widgets[j].name))) { + widgets[i] = simple_widgets[j]; + break; + } + } + + if (j >= ARRAY_SIZE(simple_widgets)) { + dev_err(card->dev, + "ASoC: DAPM widget '%s' is not supported\n", + template); + return -EINVAL; + } + + ret = of_property_read_string_index(np, propname, + (2 * i) + 1, + &wname); + if (ret) { + dev_err(card->dev, + "ASoC: Property '%s' index %d read error:%d\n", + propname, (2 * i) + 1, ret); + return -EINVAL; + } + + widgets[i].name = wname; + } + + card->dapm_widgets = widgets; + card->num_dapm_widgets = num_widgets; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets); + int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname) {
On Fri, Jan 24, 2014 at 03:43:04PM +0800, Xiubo Li wrote:
The "template-wname" includes: "Mic", "Line", "Hp", "Spk"...
Sorry, this is nitpicking but can we have "HP" or even "Headphone" instead of "Hp" - otherwise it looks like a typo and people might correct it. Similarly "Speaker" might be better as "Spk".
The "template-wname" includes: "Mic", "Line", "Hp", "Spk"...
Sorry, this is nitpicking but can we have "HP" or even "Headphone" instead of "Hp" - otherwise it looks like a typo and people might correct it. Similarly "Speaker" might be better as "Spk".
Yes, I will use them.
Please see the next version days later.
Thanks,
-- Best Regards, Xiubo
Signed-off-by: Xiubo Li Li.Xiubo@freescale.com --- sound/soc/generic/simple-card.c | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 3177aa8..ec2a65d 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -202,6 +202,14 @@ static int asoc_simple_card_parse_of(struct device_node *node, priv->daifmt = snd_soc_of_parse_daifmt(node, "simple-audio-card,") & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK);
+ /* off-codec widgets */ + if (of_property_read_bool(node, "simple-audio-card,widgets")) { + ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card, + "simple-audio-card,widgets"); + if (ret) + return ret; + } + /* DAPM routes */ if (of_property_read_bool(node, "simple-audio-card,routing")) { ret = snd_soc_of_parse_audio_routing(&priv->snd_card,
On Fri, Jan 24, 2014 at 03:43:05PM +0800, Xiubo Li wrote:
Signed-off-by: Xiubo Li Li.Xiubo@freescale.com
This is OK.
This add the following three new properties documenting and usage for simple card:
"simple-audio-card,name", "simple-audio-card,widgets", "simple-audio-card,tdm-slot".
Signed-off-by: Xiubo Li Li.Xiubo@freescale.com --- Documentation/devicetree/bindings/sound/simple-card.txt | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/simple-card.txt b/Documentation/devicetree/bindings/sound/simple-card.txt index 19c84df..dea1229 100644 --- a/Documentation/devicetree/bindings/sound/simple-card.txt +++ b/Documentation/devicetree/bindings/sound/simple-card.txt @@ -8,13 +8,19 @@ Required properties:
Optional properties:
+- simple-audio-card,name : User specified audio sound card name, one string + property. - simple-audio-card,format : CPU/CODEC common audio format. "i2s", "right_j", "left_j" , "dsp_a" "dsp_b", "ac97", "pdm", "msb", "lsb" +- simple-audio-card,widgets : A list of the off-CODEC widgets, each entry is a pair + of strings, the first being the template widget name, + the second being the user specified widget name. - simple-audio-card,routing : A list of the connections between audio components. Each entry is a pair of strings, the first being the connection's sink, the second being the connection's source. +- simple-audio-card,tdm-slot : The configuration of TDM operation for CPU/CODEC DAI.
Required subnodes:
@@ -42,11 +48,18 @@ Example:
sound { compatible = "simple-audio-card"; + simple-audio-card,name = "VF610-Tower-Sound-Card"; simple-audio-card,format = "left_j"; + simple-audio-card,widgets = + "Mic", "Microphone Jack", + "Hp", "Headphone Jack", + "Spk", "External Speaker"; simple-audio-card,routing = - "MIC_IN", "Mic Jack", + "MIC_IN", "Microphone Jack", "Headphone Jack", "HP_OUT", - "Ext Spk", "LINE_OUT"; + "External Speaker", "LINE_OUT"; + + simple-audio-card,tdm-slot = <0xffffffc 0xffffffc 2 0>;
simple-audio-card,cpu { sound-dai = <&sh_fsi2 0>;
On Fri, Jan 24, 2014 at 03:43:06PM +0800, Xiubo Li wrote:
This add the following three new properties documenting and usage for simple card:
"simple-audio-card,name", "simple-audio-card,widgets", "simple-audio-card,tdm-slot".
name and widgets are basically fine modulo the comment about "Hp". tdm-slot needs further study, when resubmitting can you please split that into a second patch so that the name and widgets properties can be merged even if that needs more work?
For widgets it might be good to create a separate document widgets.txt and refer to that - since the code in the kernel can be shared and extended we should share the binding document too.
This add the following three new properties documenting and usage for simple card:
"simple-audio-card,name", "simple-audio-card,widgets", "simple-audio-card,tdm-slot".
name and widgets are basically fine modulo the comment about "Hp". tdm-slot needs further study, when resubmitting can you please split that into a second patch so that the name and widgets properties can be merged even if that needs more work?
Yes, I will.
For widgets it might be good to create a separate document widgets.txt and refer to that - since the code in the kernel can be shared and extended we should share the binding document too.
I will send another patch series about this.
Thanks,
-- Best Regards, Xiubo
participants (3)
-
Li.Xiubo@freescale.com
-
Mark Brown
-
Xiubo Li