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(-)
--
1.8.4
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 <[email protected]>
---
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;
}
--
1.8.4
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 <[email protected]>
---
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);
--
1.8.4
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 <[email protected]>
---
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;
--
1.8.4
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 <[email protected]>
---
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)
{
--
1.8.4
Signed-off-by: Xiubo Li <[email protected]>
---
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,
--
1.8.4
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 <[email protected]>
---
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>;
--
1.8.4
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 <[email protected]>
---
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 */
--
1.8.4
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.
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.
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.
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.
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".
On Fri, Jan 24, 2014 at 03:43:05PM +0800, Xiubo Li wrote:
> Signed-off-by: Xiubo Li <[email protected]>
This is OK.
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.
> > 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-
> > 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