数字mic系列,注册machine
dts配置如下
digital_mic: digital-mic {
status = "okay";
compatible = "simple-audio-card";
simple-audio-card,format = "i2s";
simple-audio-card,mclk-fs = <256>;
simple-audio-card,name = "digital-mic";
simple-audio-card,cpu {
sound-dai = <&i2s0_8ch>;
};
simple-audio-card,codec {
sound-dai = <&dummy_codec>;
};
};
驱动代码位置 sound/soc/generic/simple-card.c
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 device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct snd_soc_card *card;
int num, ret;
/* Get the number of DAI links */
if (np && of_get_child_by_name(np, PREFIX "dai-link"))
num = of_get_child_count(np);
else
/* 对于dai-link*/
num = 1;
/* 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)
return -ENOMEM;
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->probe = asoc_simple_soc_card_probe;
if (np && of_device_is_available(np)) {
ret = asoc_simple_card_parse_of(priv);
if (ret < 0) {
if (ret != -EPROBE_DEFER)
dev_err(dev, "parse error %d\n", ret);
goto err;
}
} else {
...
}
snd_soc_card_set_drvdata(card, priv);
asoc_simple_card_probe() -> asoc_simple_is_available()
static int asoc_simple_card_parse_of(struct simple_card_data *priv)
{
struct device *dev = simple_priv_to_dev(priv);
struct snd_soc_card *card = simple_priv_to_card(priv);
struct device_node *dai_link;
struct device_node *node = dev->of_node;
int ret;
if (!node)
return -EINVAL;
/* 这里会匹配 simple-audio-card,dai-link 子节点 */
dai_link = of_get_child_by_name(node, PREFIX "dai-link");
/* 解析 simple-audio-card,widgets 属性 */
ret = asoc_simple_card_of_parse_widgets(card, PREFIX);
if (ret < 0)
goto card_parse_end;
/* 解析 simple-audio-card,routing 属性 */
ret = asoc_simple_card_of_parse_routing(card, PREFIX, 1);
if (ret < 0)
goto card_parse_end;
/* Factor to mclk, used in hw_params() */
/* 解析 simple-audio-card,mclk-fs 属性 */
of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs);
/* 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);
if (ret < 0)
goto card_parse_end;
}
/* label属性没有
* card->dai_link->name
*/
ret = asoc_simple_card_parse_card_name(card, PREFIX);
if (ret < 0)
goto card_parse_end;
/* 没有aux-devs */
ret = asoc_simple_card_parse_aux_devs(node, priv);
card_parse_end:
of_node_put(dai_link);
return ret;
}
asoc_simple_card_probe() -> asoc_simple_is_available() -> asoc_simple_card_dai_link_of()
static int asoc_simple_card_dai_link_of(struct device_node *node,
struct simple_card_data *priv,
int idx,
bool is_top_level_node)
{
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;
char prop[128];
char *prefix = "";
int ret, single_cpu;
/* For single DAI link & old style of DT node */
if (is_top_level_node)
prefix = PREFIX;
snprintf(prop, sizeof(prop), "%scpu", prefix);
/* simple-audio-card,cpu 节点 */
cpu = of_get_child_by_name(node, prop);
if (!cpu) {
ret = -EINVAL;
dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
goto dai_link_of_err;
}
snprintf(prop, sizeof(prop), "%splat", prefix);
/* simple-audio-card,plat 节点 ,可以没有 */
plat = of_get_child_by_name(node, prop);
snprintf(prop, sizeof(prop), "%scodec", prefix);
/* simple-audio-card,codec 节点 */
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;
}
/* 获取dai_fmt 主要通过machine和codec进行判断
* 对于当前dts配置来说, dai_fmt = SND_SOC_DAIFMT_I2S
*/
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);
/* 这里会找到platform
* dai_link->cpu_of_node 会填充platform节点
* 其中宏定义如下
* #define DAI "sound-dai"
* #define CELL "#sound-dai-cells"
* #define PREFIX "simple-audio-card,"
* 注意这里查找dai_name的时候
* 优先从dai->driver->name
* 如果没有则使用节点的名字
* 如果没有找到cpu的节点,则会返回-EPROBE_DEFER
*/
ret = asoc_simple_card_parse_cpu(cpu, dai_link,
DAI, CELL, &single_cpu);
if (ret < 0)
goto dai_link_of_err;
/* 同上
* dai_link->codec_of_node 会填充codec节点
*/
ret = asoc_simple_card_parse_codec(codec, dai_link, DAI, CELL);
if (ret < 0)
goto dai_link_of_err;
/* 没有platform code */
ret = asoc_simple_card_parse_platform(plat, dai_link, DAI, CELL);
if (ret < 0)
goto dai_link_of_err;
/* 不支持 */
ret = asoc_simple_card_of_parse_tdm(cpu, cpu_dai);
if (ret < 0)
goto dai_link_of_err;
/* 不支持 */
ret = asoc_simple_card_of_parse_tdm(codec, codec_dai);
if (ret < 0)
goto dai_link_of_err;
/* platform clk节点
* 查找顺序
* 1. platform (simple-audio-card,cpu)节点的 clock
* 2. platform (simple-audio-card,cpu)节点的 system-clock-frequency
* 3. dai_link->cpu_of_node (i2s0_8ch) 节点的 clock
* 这里是不是会到rcu ? 且只拿第一个时钟
*/
ret = asoc_simple_card_parse_clk_cpu(dev, cpu, dai_link, cpu_dai);
if (ret < 0)
goto dai_link_of_err;
/* codec clk节点
* 查找顺序
* 1. codec (simple-audio-card,codec)节点的 clock
* 2. codec (simple-audio-card,codec)节点的 system-clock-frequency
* 3. dai_link->codec_of_node (dummy_codec) 节点的 clock
*/
ret = asoc_simple_card_parse_clk_codec(dev, codec, dai_link, codec_dai);
if (ret < 0)
goto dai_link_of_err;
/* 没有platform_of_node
* platform_of_node = cpu_of_node
*/
ret = asoc_simple_card_canonicalize_dailink(dai_link);
if (ret < 0)
goto dai_link_of_err;
/* "ff800000.i2s-tdm-dummy_codec"
* 名字保存在 dai_link->name 和 dai_link->stream_name
*/
ret = asoc_simple_card_set_dailink_name(dev, dai_link,
"%s-%s",
dai_link->cpu_dai_name,
dai_link->codec_dai_name);
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->cpu_dai_name = NULL */
asoc_simple_card_canonicalize_cpu(dai_link, single_cpu);
dai_link_of_err:
of_node_put(cpu);
of_node_put(codec);
return ret;
}