@@ -1,6 +1,6 @@
Simple-Card:
-Simple-Card specifies audio DAI connection of SoC <-> codec.
+Simple-Card specifies audio DAI connections of SoC <-> codec.
Required properties:
@@ -10,26 +10,51 @@ 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 : Please refer to widgets.txt.
- 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.
-- dai-tdm-slot-num : Please refer to tdm-slot.txt.
-- dai-tdm-slot-width : Please refer to tdm-slot.txt.
+Optional subnodes:
+
+- simple-audio-card,dai-link : Container for dai-link level
+ properties and the CPU and CODEC
+ sub-nodes. This container may be
+ omitted when the card has only one
+ DAI link. See the examples and the
+ section bellow.
+
+Dai-link subnode properties and subnodes:
+
+If dai-link subnode is omitted and the subnode properties are directly
+under "sound"-node the subnode property and subnode names have to be
+prefixed with "simple-audio-card,"-prefix.
-Required subnodes:
+Required dai-link subnodes:
-- simple-audio-card,dai-link : container for the CPU and CODEC sub-nodes
- This container may be omitted when the
- card has only one DAI link.
- See the examples.
+- cpu : CPU sub-node
+- codec : CODEC sub-node
-- simple-audio-card,cpu : CPU sub-node
-- simple-audio-card,codec : CODEC sub-node
+Optional dai-link subnode properties:
+
+- format : CPU/CODEC common audio format.
+ "i2s", "right_j", "left_j" , "dsp_a"
+ "dsp_b", "ac97", "pdm", "msb", "lsb"
+- frame-master : Indicates dai-link frame master.
+ phandle to a cpu or codec subnode.
+- bitclock-master : Indicates dai-link bit clock master.
+ phandle to a cpu or codec subnode.
+- bitclock-inversion : bool property. Add this if the
+ dai-link uses bit clock inversion.
+- frame-inversion : bool property. Add this if the
+ dai-link uses frame clock inversion.
+
+For backward compatibility the frame-master and bitclock-master
+properties can be used as booleans in codec subnode to indicate if the
+codec is the dai-link frame or bit clock master. In this case there
+should be no dai-link node, the same properties should not be present
+at sound-node level, and the bitclock-inversion and frame-inversion
+properties should also be placed in the codec node if needed.
Required CPU/CODEC subnodes properties:
@@ -37,29 +62,21 @@ Required CPU/CODEC subnodes properties:
Optional CPU/CODEC subnodes properties:
-- format : CPU/CODEC specific audio format if needed.
- see simple-audio-card,format
-- frame-master : bool property. add this if subnode is frame master
-- bitclock-master : bool property. add this if subnode is bitclock master
-- bitclock-inversion : bool property. add this if subnode has clock inversion
-- frame-inversion : bool property. add this if subnode has frame inversion
+- dai-tdm-slot-num : Please refer to tdm-slot.txt.
+- dai-tdm-slot-width : Please refer to tdm-slot.txt.
- clocks / system-clock-frequency : specify subnode's clock if needed.
it can be specified via "clocks" if system has
clock node (= common clock), or "system-clock-frequency"
(if system doens't support common clock)
-Note:
- * For 'format', 'frame-master', 'bitclock-master', 'bitclock-inversion' and
- 'frame-inversion', the simple card will use the settings of CODEC for both
- CPU and CODEC sides as we need to keep the settings identical for both ends
- of the link.
-
Example 1 - single DAI link:
sound {
compatible = "simple-audio-card";
simple-audio-card,name = "VF610-Tower-Sound-Card";
simple-audio-card,format = "left_j";
+ simple-audio-card,bitclock-master = <&dailink0_master>;
+ simple-audio-card,frame-master = <&dailink0_master>;
simple-audio-card,widgets =
"Microphone", "Microphone Jack",
"Headphone", "Headphone Jack",
@@ -69,17 +86,12 @@ sound {
"Headphone Jack", "HP_OUT",
"External Speaker", "LINE_OUT";
- dai-tdm-slot-num = <2>;
- dai-tdm-slot-width = <8>;
-
simple-audio-card,cpu {
sound-dai = <&sh_fsi2 0>;
};
- simple-audio-card,codec {
+ dailink0_master: simple-audio-card,codec {
sound-dai = <&ak4648>;
- bitclock-master;
- frame-master;
clocks = <&osc>;
};
};
@@ -105,31 +117,31 @@ Example 2 - many DAI links:
sound {
compatible = "simple-audio-card";
simple-audio-card,name = "Cubox Audio";
- simple-audio-card,format = "i2s";
simple-audio-card,dai-link@0 { /* I2S - HDMI */
- simple-audio-card,cpu {
+ format = "i2s";
+ cpu {
sound-dai = <&audio1 0>;
};
- simple-audio-card,codec {
+ codec {
sound-dai = <&tda998x 0>;
};
};
simple-audio-card,dai-link@1 { /* S/PDIF - HDMI */
- simple-audio-card,cpu {
+ cpu {
sound-dai = <&audio1 1>;
};
- simple-audio-card,codec {
+ codec {
sound-dai = <&tda998x 1>;
};
};
simple-audio-card,dai-link@2 { /* S/PDIF - S/PDIF */
- simple-audio-card,cpu {
+ cpu {
sound-dai = <&audio1 1>;
};
- simple-audio-card,codec {
+ codec {
sound-dai = <&spdif_codec>;
};
};
@@ -88,7 +88,6 @@ 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)
@@ -117,14 +116,6 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
return ret;
/*
- * bitclock-inversion, frame-inversion
- * bitclock-master, frame-master
- * and specific "format" if it has
- */
- dai->fmt = snd_soc_of_parse_daifmt(np, NULL, NULL, NULL);
- dai->fmt |= daifmt;
-
- /*
* dai->sysclk come from
* "clocks = <&xxx>" (if system has common clock)
* or "system-clock-frequency = <xxx>"
@@ -151,37 +142,135 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
return 0;
}
-static int simple_card_cpu_codec_of(struct device_node *node,
- int daifmt,
- struct snd_soc_dai_link *dai_link,
- struct simple_dai_props *dai_props)
+static int simple_card_dai_link_of(struct device_node *node,
+ struct device *dev,
+ struct snd_soc_dai_link *dai_link,
+ struct simple_dai_props *dai_props)
{
- struct device_node *np;
+ struct device_node *np = NULL;
+ struct device_node *bitclkmaster = NULL;
+ struct device_node *framemaster = NULL;
+ unsigned int daifmt;
+ char *name;
+ char prop[128];
+ char *prefix = "";
int ret;
- /* CPU sub-node */
- ret = -EINVAL;
- np = of_get_child_by_name(node, "simple-audio-card,cpu");
- if (np) {
- ret = asoc_simple_card_sub_parse_of(np, daifmt,
- &dai_props->cpu_dai,
- &dai_link->cpu_of_node,
- &dai_link->cpu_dai_name);
- of_node_put(np);
+ if (!strcmp("sound", node->name))
+ prefix = "simple-audio-card,";
+
+ daifmt = snd_soc_of_parse_daifmt(node, prefix,
+ &bitclkmaster, &framemaster);
+ daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
+
+ snprintf(prop, sizeof(prop), "%scpu", prefix);
+ np = of_get_child_by_name(node, prop);
+ if (!np) {
+ ret = -EINVAL;
+ dev_err(dev, "%s: Can't find simple-audio-card,cpu DT node\n",
+ __func__);
+ goto dai_link_of_err;
}
+
+ ret = asoc_simple_card_sub_parse_of(np, &dai_props->cpu_dai,
+ &dai_link->cpu_of_node,
+ &dai_link->cpu_dai_name);
if (ret < 0)
- return ret;
+ goto dai_link_of_err;
+
+ dai_props->cpu_dai.fmt = daifmt;
+ switch (((np == bitclkmaster)<<4)|(np == framemaster)) {
+ case 0x11:
+ dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBS_CFS;
+ break;
+ case 0x10:
+ dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBS_CFM;
+ break;
+ case 0x01:
+ dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBM_CFS;
+ break;
+ default:
+ dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ break;
+ }
- /* CODEC sub-node */
- ret = -EINVAL;
- np = of_get_child_by_name(node, "simple-audio-card,codec");
- if (np) {
- ret = asoc_simple_card_sub_parse_of(np, daifmt,
- &dai_props->codec_dai,
- &dai_link->codec_of_node,
- &dai_link->codec_dai_name);
- of_node_put(np);
+ of_node_put(np);
+ snprintf(prop, sizeof(prop), "%scodec", prefix);
+ np = of_get_child_by_name(node, prop);
+ if (!np) {
+ ret = -EINVAL;
+ dev_err(dev, "%s: Can't find simple-audio-card,codec DT node\n",
+ __func__);
+ goto dai_link_of_err;
+ }
+
+ ret = asoc_simple_card_sub_parse_of(np, &dai_props->codec_dai,
+ &dai_link->codec_of_node,
+ &dai_link->codec_dai_name);
+ if (ret < 0)
+ goto dai_link_of_err;
+
+ if (strlen(prefix) && !bitclkmaster && !framemaster) {
+ /* No dai-link level and master setting was not found from
+ sound node level, revert back to legacy DT parsing and
+ take the settings from codec node. */
+ dev_dbg(dev, "%s: Revert to legacy daifmt parsing\n",
+ __func__);
+ dai_props->cpu_dai.fmt = dai_props->codec_dai.fmt =
+ snd_soc_of_parse_daifmt(np, NULL, NULL, NULL) |
+ (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK);
+ } else {
+ dai_props->codec_dai.fmt = daifmt;
+ switch (((np == bitclkmaster)<<4)|(np == framemaster)) {
+ case 0x11:
+ dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBM_CFM;
+ break;
+ case 0x10:
+ dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBM_CFS;
+ break;
+ case 0x01:
+ dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBS_CFM;
+ break;
+ default:
+ dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBS_CFS;
+ break;
+ }
+ }
+
+ if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
+ ret = -EINVAL;
+ goto dai_link_of_err;
}
+
+ /* simple-card assumes platform == cpu */
+ dai_link->platform_of_node = dai_link->cpu_of_node;
+
+ /* Link name is created from CPU/CODEC dai name */
+ name = devm_kzalloc(dev,
+ strlen(dai_link->cpu_dai_name) +
+ strlen(dai_link->codec_dai_name) + 2,
+ GFP_KERNEL);
+ sprintf(name, "%s-%s", dai_link->cpu_dai_name,
+ dai_link->codec_dai_name);
+ dai_link->name = dai_link->stream_name = name;
+
+ dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
+ dev_dbg(dev, "\tcpu : %s / %04x / %d\n",
+ dai_link->cpu_dai_name,
+ dai_props->cpu_dai.fmt,
+ dai_props->cpu_dai.sysclk);
+ dev_dbg(dev, "\tcodec : %s / %04x / %d\n",
+ dai_link->codec_dai_name,
+ dai_props->codec_dai.fmt,
+ dai_props->codec_dai.sysclk);
+
+dai_link_of_err:
+ if (np)
+ of_node_put(np);
+ if (bitclkmaster)
+ of_node_put(bitclkmaster);
+ if (framemaster)
+ of_node_put(framemaster);
return ret;
}
@@ -192,19 +281,11 @@ static int asoc_simple_card_parse_of(struct device_node *node,
{
struct snd_soc_dai_link *dai_link = priv->snd_card.dai_link;
struct simple_dai_props *dai_props = priv->dai_props;
- struct device_node *np;
- char *name;
- unsigned int daifmt;
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 */
- daifmt = snd_soc_of_parse_daifmt(node, "simple-audio-card,", NULL,
- NULL) &
- (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,
@@ -221,71 +302,31 @@ static int asoc_simple_card_parse_of(struct device_node *node,
return ret;
}
- /* loop on the DAI links */
- np = NULL;
- for (;;) {
- if (multi) {
- np = of_get_next_child(node, np);
- if (!np)
- break;
+ dev_dbg(dev, "New simple-card: %s\n", priv->snd_card.name ?
+ priv->snd_card.name : "");
+
+ if (multi) {
+ struct device_node *np = NULL;
+ int i;
+ for (i = 0; (np = of_get_next_child(node, np)); i++) {
+ dev_dbg(dev, "\tlink %d:\n", i);
+ ret = simple_card_dai_link_of(np, dev, dai_link + i,
+ dai_props + i);
+ if (ret < 0) {
+ of_node_put(np);
+ return ret;
+ }
}
-
- ret = simple_card_cpu_codec_of(multi ? np : node,
- daifmt, dai_link, dai_props);
+ } else {
+ ret = simple_card_dai_link_of(node, dev, dai_link, dai_props);
if (ret < 0)
- goto err;
-
- /*
- * overwrite cpu_dai->fmt as its DAIFMT_MASTER bit is based on CODEC
- * while the other bits should be identical unless buggy SW/HW design.
- */
- dai_props->cpu_dai.fmt = dai_props->codec_dai.fmt;
-
- if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
- ret = -EINVAL;
- goto err;
- }
-
- /* simple-card assumes platform == cpu */
- dai_link->platform_of_node = dai_link->cpu_of_node;
-
- name = devm_kzalloc(dev,
- strlen(dai_link->cpu_dai_name) +
- strlen(dai_link->codec_dai_name) + 2,
- GFP_KERNEL);
- sprintf(name, "%s-%s", dai_link->cpu_dai_name,
- dai_link->codec_dai_name);
- dai_link->name = dai_link->stream_name = name;
-
- if (!multi)
- break;
-
- dai_link++;
- dai_props++;
+ return ret;
}
- /* card name is created from CPU/CODEC dai name */
- dai_link = priv->snd_card.dai_link;
if (!priv->snd_card.name)
- priv->snd_card.name = dai_link->name;
-
- dev_dbg(dev, "card-name : %s\n", priv->snd_card.name);
- dev_dbg(dev, "platform : %04x\n", daifmt);
- dai_props = priv->dai_props;
- dev_dbg(dev, "cpu : %s / %04x / %d\n",
- dai_link->cpu_dai_name,
- dai_props->cpu_dai.fmt,
- dai_props->cpu_dai.sysclk);
- dev_dbg(dev, "codec : %s / %04x / %d\n",
- dai_link->codec_dai_name,
- dai_props->codec_dai.fmt,
- dai_props->codec_dai.sysclk);
+ priv->snd_card.name = priv->snd_card.dai_link->name;
return 0;
-
-err:
- of_node_put(np);
- return ret;
}
/* update the reference count of the devices nodes at end of probe */