ALSA驱动asoc框架之machine(一)

从前面内容我们知道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_namecodec_dai_namecpu_dai_nameplatform_name、驱动会利用这些名字去匹配已经在系统中注册的platformcodecdai(后面介绍匹配代码)。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 */

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值