从前面内容我们知道ALSA 驱动 asoc 框架主要包括 codec driver、 platform driver、 machine driver,其中machine是连接codec driver及platform driver的桥梁,我们本节内容重点分析machine driver驱动。
asoc 框架中 codec driver分析可以参考:ALSA驱动asoc框架之Codec
asoc 框架中 platform driver分析可以参考:ALSA驱动asoc框架之Platform
本例Codec为nau88c10,platfrom为zx297520v3,machine driver路径linux-3.4.x\sound\soc\sanechips\zx297520v3_nau8810.c如下:
static int zx297520v3_nau8810_probe(struct platform_device *pdev)
{
int ret;
unsigned int armRegBit = 0;
zx297520v3_nau8810_pow_pins = pdev->dev.platform_data;
if (zx297520v3_nau8810_pow_pins == NULL) {
printk(KERN_ERR "zx297520v3_nau8810 SoC Audio: unable to find platform data\n");
return -ENODEV;
}
if (zx297520v3_nau8810_setup_pins(zx297520v3_nau8810_pow_pins, "codec") < 0)
return -EBUSY;
zx29_i2s_top_reg_cfg();
zx297520v3_nau8810_snd_device = platform_device_alloc("soc-audio", -1);
if (!zx297520v3_nau8810_snd_device) {
printk(KERN_ERR "zx297520v3_nau8810 SoC Audio: Unable to register\n");
return -ENOMEM;
}
platform_set_drvdata(zx297520v3_nau8810_snd_device,
&snd_soc_zx297520v3_nau8810);
ret = platform_device_add(zx297520v3_nau8810_snd_device);
if (ret) {
printk(KERN_ERR "zx297520v3_nau8810 SoC Audio: Unable to add\n");
platform_device_put(zx297520v3_nau8810_snd_device);
}
return ret;
}
... ...
/* by面朝大海0902 https://blog.csdn.net/lihuan680680/article/details/122289970 */
static struct platform_driver zx297520v3_nau8810_driver =
{
.probe = zx297520v3_nau8810_probe,
.remove = zx297520v3_nau8810_remove,
.driver = {
.name = "zx297520v3_nau8810",
.owner = THIS_MODULE,
},
};
module_platform_driver(zx297520v3_nau8810_driver);
MODULE_DESCRIPTION("ZX297520V3_NAU8810 ALSA SoC audio driver");
MODULE_LICENSE("GPL");
这里的platform_driver与platform_device(设备树含有name为“zx297520v3_nau8810”的平台设备节点)匹配之后probe函数被调用。
probe函数主要内容:
1、初始化一些配置信息,例如引脚配置。
2、分配一个名为soc-audio的平台设备,有平台设备,必定有平台驱动,以soc-audio搜索,在soc-core.c函数里面有对应的平台驱动(后面会重点分析)。
3、同时把snd_soc_card snd_soc_zx297520v3_nau8810设置到platform_device结构的dev.drvdata字段中,这里引出了第一个数据结构snd_soc_card 的实例(它非常重要下面会讲),同时也将zx297520v3_nau8810_snd_device实例添加到platform_device结构中。
/* by面朝大海0902 https://blog.csdn.net/lihuan680680/article/details/122289970 */
我们看下snd_soc_card snd_soc_zx297520v3_nau8810结构体:
static struct snd_soc_ops zx297520v3_nau8810_ops =
{
.hw_params = zx297520v3_nau8810_hw_params,
};
static struct snd_soc_ops zx297520v3_nau8810_ops1 =
{
.hw_params = zx297520v3_nau8810_hw_params1,
};
static struct snd_soc_dai_link zx297520v3_nau8810_dai_link[] =
{
{
.name = "NAU8810_Media",
.stream_name = "MultiMedia",
.codec_name = "nau8810.1-001a", //nau8810.1-001a
.codec_dai_name = "nau8810-hifi",
.cpu_dai_name = "MultiMedia", //"zx29_i2s.0"
.ops = &zx297520v3_nau8810_ops,
.init = zx297520v3_nau8810_init_paiftx,
.platform_name = "zx29-pcm-audio",
},
{
.name = "voice_call",
.stream_name = "voice",
.codec_name = "nau8810.1-001a",
.codec_dai_name = "nau8810-hifi",
.cpu_dai_name = "voice", //"snd-soc-dummy-dai",
.platform_name = "snd-soc-dummy",
.init = zx297520v3_nau8810_init_paiftx,
.ops = &zx297520v3_nau8810_ops1,
}
};
static struct snd_soc_card snd_soc_zx297520v3_nau8810 =
{
.name = "zx297520v3_nau8810",
.owner = THIS_MODULE,
.dai_link = &zx297520v3_nau8810_dai_link,
.num_links = ARRAY_SIZE(zx297520v3_nau8810_dai_link),
.late_probe = zx297520v3_nau8810_late_probe,
};
通过snd_soc_card结构,又引出了Machine驱动的另外两个个数据结构:
snd_soc_dai_link(实例:zx297520v3_nau8810_dai_link)—指定了Platform、Codec、codec_dai、cpu_dai的名字
snd_soc_ops(实例:zx297520v3_nau8810_ops)—硬件的操作
涉及数据结构snd_soc_card--->
snd_soc_dai_link
snd_soc_ops
/* SoC card */
struct snd_soc_card {
const char *name;
const char *long_name;
const char *driver_name;
struct device *dev;
struct snd_card *snd_card;
struct module *owner;
... ...
/* CPU <--> Codec DAI links */
struct snd_soc_dai_link *dai_link;
int num_links;
struct snd_soc_pcm_runtime *rtd;
int num_rtd;
... ...
};
struct snd_soc_dai_link {
/* config - must be set by machine driver */
const char *name; /* Codec name */
const char *stream_name; /* Stream name */
const char *codec_name; /* for multi-codec */
const struct device_node *codec_of_node;
const char *platform_name; /* for multi-platform */
const struct device_node *platform_of_node;
const char *cpu_dai_name;
const struct device_node *cpu_dai_of_node;
const char *codec_dai_name;
... ...
/* codec/machine specific init - e.g. add machine controls */
int (*init)(struct snd_soc_pcm_runtime *rtd);
/* machine stream operations */
struct snd_soc_ops *ops;
};
/* SoC audio ops */
struct snd_soc_ops {
int (*startup)(struct snd_pcm_substream *);
void (*shutdown)(struct snd_pcm_substream *);
int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *);
int (*hw_free)(struct snd_pcm_substream *);
int (*prepare)(struct snd_pcm_substream *);
int (*trigger)(struct snd_pcm_substream *, int);
};
从snd_soc_dai_link结构体中我们可以值codec_name、codec_dai_name、cpu_dai_name、platform_name、驱动会利用这些名字去匹配已经在系统中注册的platform,codec,dai(后面介绍匹配代码)。Machine驱动核心是确定合适的Platform和Codec以及dai,填充以上几个数据结构,然后注册Platform设备。当然还要实现连接Platform和Codec的dai_link对应的ops实现。
我们也可以直接在代码工程里面搜索对应name关键字。,以搜索codec_dai_name “nau8810-hifi”为例,我们全工程搜索,最终在nau8810.c Codec驱动中搜索到对应关键字,后面会有单独一讲Codec驱动:
grep "nau8810-hifi" -nR
ap/os/linux/linux-3.4.x/sound/soc/codecs/nau8810.c:865: .name = "nau8810-hifi",
nau8810.c文件部分代码如下:
static struct snd_soc_dai_driver nau8810_dai = {
.name = "nau8810-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2, /* Only 1 channel of data */
.rates = NAU8810_RATES,
.formats = NAU8810_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2, /* Only 1 channel of data */
.rates = NAU8810_RATES,
.formats = NAU8810_FORMATS,
},
.ops = &nau8810_ops,
.symmetric_rates = 1,
};
鉴于篇幅太长,后面继续往下说ALSA驱动asoc框架之machine(二)
/* by面朝大海0902 https://blog.csdn.net/lihuan680680/article/details/122289970 */