-
ALSA和ASoC简介
ALSA,即 Advanced Linux Sound Architecture,高级 Linux 声音架构的简称,它在 Linux操作系统上提供了音频和 MIDI(Musical Instrument Digital Interface,音乐设备数字化接口)的支持。ASoC,即 ALSA System on Chip ,是建立在标准 ALSA 驱动层上,为了更好地支持嵌入式处理器和移动设备中的音频系统的一套软件架构。
-
ASoC软件系统组成部分
ASoC 软件系统是基于 ALSA 架构的。采用 ASoC 架理开发的音频驱动会包含三个部分,分别对应于下面三个基本概念。Intel 音频驱动也是采用 ASoC 架构开发。
- Machine
是指某一款机器,可以是某款设备,某款开发板,又或者是某款智能手机,由此可以看出 Machine 几乎是不可重用的,每个 Machine 上的硬件实现可能都不一样,CPU 不一样,Codec 不一样,音频的输入、输出设备也不一样,Machine 为CPU、Codec、输入输出设备提供了一个载体。
- Platform
一般是指某一个 SoC 平台,比如 Intel,s3cxxxx,omapxxx 等等,与音频相关的通常包含该 SoC 中的时钟、DMA、I2S、PCM 等等,只要指定了 SoC,那么我们可以认为它会有一个对应的 Platform,它只与 SoC 相关,与 Machine 无关,这样我们就可以把 Platform 抽象出来,使得同一款 SoC 不用做任何的改动,就可以用在不同的 Machine 中。实际上,把 Platform 认为是某个 SoC 更好理解。
- Codec
字面上的意思就是编解码器,Codec 里面包含了 I2S 接口、D/A、A/D、Mixer、PA(功放),通常包含多种输入(Mic、Line-in、I2S、PCM)和多个输出
(耳机、喇叭、听筒,Line-out),Codec 和 Platform 一样,是可重用的部件,同一个 Codec 可以被不同的 Machine 使用。嵌入式 Codec 通常通过 I2C 对内部的寄存器进行控制。
-
Machine部分注册流程
- 文件位置:kernel/sound/soc/intel/board/byt_cr_rt5642.c
static const struct acpi_device_id byt_mc_acpi_ids[] = {
{ "AMCR0F28", 0 },
{},
};
MODULE_DEVICE_TABLE(acpi, byt_mc_acpi_ids);
static struct platform_driver snd_byt_mc_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "byt_rt5642",
.pm = &snd_byt_mc_pm_ops,
.acpi_match_table = ACPI_PTR(byt_mc_acpi_ids), // intel平台的probe需要bios的设置支持
},
.probe = snd_byt_mc_probe,
.remove = snd_byt_mc_remove,
.shutdown = snd_byt_mc_shutdown,
};
static int __init snd_byt_driver_init(void)
{
pr_info("Baytrail Machine Driver byt_rt5642 registerd\n");
return platform_driver_register(&snd_byt_mc_driver); // 使用linux的普通device-》driver结构
}
late_initcall(snd_byt_driver_init);
static void __exit snd_byt_driver_exit(void)
{
pr_debug("In %s\n", __func__);
platform_driver_unregister(&snd_byt_mc_driver);
}
module_exit(snd_byt_driver_exit);
// 进入probe函数。
/* SoC card */
static struct snd_soc_card snd_soc_card_byt = { // snd_soc_card 结构体
.name = "baytrailaudio",
.dai_link = byt_dailink, // 很重要的结构体
.num_links = ARRAY_SIZE(byt_dailink), // 重要
.set_bias_level = byt_set_bias_level,
.dapm_widgets = byt_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(byt_dapm_widgets),
.dapm_routes = byt_audio_map,
.num_dapm_routes = ARRAY_SIZE(byt_audio_map),
};
static int snd_byt_mc_probe(struct platform_device *pdev)
{
int ret_val = 0;
struct byt_mc_private *drv;
int codec_gpio;
pr_debug("Entry %s\n", __func__);
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
if (!drv) {
pr_err("allocation failed\n");
return -ENOMEM;
}
... ...
/* register the soc card */
snd_soc_card_byt.dev = &pdev->dev;
snd_soc_card_set_drvdata(&snd_soc_card_byt, drv);
ret_val = snd_soc_register_card(&snd_soc_card_byt); // 注册soc card
if (ret_val) {
pr_err("snd_soc_register_card failed %d\n", ret_val);
return ret_val;
}
platform_set_drvdata(pdev, &snd_soc_card_byt);
pr_info("%s successful\n", __func__);
return ret_val;
}
// 上面的 byt_dailink 结构体内容如下:
static struct snd_soc_dai_link byt_dailink[] = { // snd_soc_dai_link 结构体
[BYT_AUD_AIF1] = {
.name = "Baytrail Audio",
.stream_name = "Audio",
.cpu_dai_name = "Headset-cpu-dai", // 需要匹配的platform的dai名字
.codec_dai_name = "rt5640-aif1", // 需要匹配的codec的dai名字
.codec_name = "rt5640.2-001c",
.platform_name = "sst-platform",
.init = byt_init,
.ignore_suspend = 1,
.ops = &byt_aif1_ops,
.playback_count = 2,
},
[BYT_AUD_AIF2] = {
.name = "Baytrail Voice",
.stream_name = "Voice",
.cpu_dai_name = "Voice-cpu-dai", // 需要匹配的platform的dai名字
.codec_dai_name = "rt5640-aif2", // 需要匹配的codec的dai名字
.codec_name = "rt5640.2-001c",
.platform_name = "sst-platform",
.init = NULL,
.ignore_suspend = 1,
.ops = &byt_aif2_ops,
},
[BYT_AUD_COMPR_DEV] = {
.name = "Baytrail Compressed Audio",
.stream_name = "Compress",
.cpu_dai_name = "Compress-cpu-dai", // 需要匹配的platform的dai名字
.codec_dai_name = "rt5640-aif1", // 需要匹配的codec的dai名字
.codec_name = "rt5640.2-001c",
.platform_name = "sst-platform",
.init = NULL,
.ignore_suspend = 1,
.compr_ops = &byt_compr_ops,
},
[BYT_COMMS_BT] = {
.name = "Baytrail Comms BT SCO",
.stream_name = "BYT_BTSCO",
.cpu_dai_name = SSP_BT_DAI_NAME, // 需要匹配的platform的dai名字
.codec_dai_name = "snd-soc-dummy-dai", // 需要匹配的codec的dai名字
.codec_name = "snd-soc-dummy",
.platform_name = "mid-ssp-dai",
.init = NULL,
.ops = &byt_comms_dai_link_ops,
},
[BYT_COMMS_MODEM] = {
.name = "Baytrail Comms MODEM",
.stream_name = "BYT_MODEM_MIXING",
.cpu_dai_name = SSP_MODEM_DAI_NAME, // 需要匹配的platform的dai名字
.codec_dai_name = "snd-soc-dummy-dai", // 需要匹配的codec的dai名字
.codec_name = "snd-soc-dummy",
.platform_name = "mid-ssp-dai",
.init = NULL,
.ops = &byt_comms_dai_link_ops,
},
};
-
Platform部分注册流程
这部分注册分成了两部分,一是和codec相连接的pcm.c文件部分,另外是关于蓝牙和Modem的mid_ssp.c部分。
首先介绍pcm.c部分;代码文件位于: kernel/sound/soc/intel/pcm.c
static struct platform_driver sst_platform_driver = {
.driver = {
.name = "sst-platform", // 模块名字,对应前面Machine里的cpu_name部分。
.owner = THIS_MODULE,
},
.probe = sst_platform_probe,
.remove = sst_platform_remove,
};
module_platform_driver(sst_platform_driver);
// 进入probe函数
static struct snd_soc_platform_driver sst_soc_platform_drv = {
.probe = sst_soc_probe,
.remove = sst_soc_remove,
.ops = &sst_platform_ops,
.compr_ops = &sst_platform_compr_ops,
.pcm_new = sst_pcm_new,
.pcm_free = sst_pcm_free,
.read = sst_soc_read,
.write = sst_soc_write,
};
static const struct snd_soc_component_driver pcm_component = {
.name = "pcm",
};
static int sst_platform_probe(struct platform_device *pdev)
{
struct sst_data *sst;
int ret;
struct sst_platform_data *pdata = pdev->dev.platform_data;
pr_debug("sst_platform_probe called\n");
sst = devm_kzalloc(&pdev->dev, sizeof(*sst), GFP_KERNEL);
if (sst == NULL) {
pr_err("kzalloc failed\n");
return -ENOMEM;
}
sst_pdev = &pdev->dev;
sst->pdata = pdata;
mutex_init(&sst->lock);
dev_set_drvdata(&pdev->dev, sst);
ret = snd_soc_register_platform(&pdev->dev, // 注册platform平台函数。
&sst_soc_platform_drv); // platform平台注册,会进一步触发probe函数。
if (ret) {
pr_err("registering soc platform failed\n");
return ret;
}
ret = snd_soc_register_component(&pdev->dev, &pcm_component, // platform的dai的注册。
sst_platform_dai, ARRAY_SIZE(sst_platform_dai));
if (ret) {
pr_err("registering cpu dais failed\n");
snd_soc_unregister_platform(&pdev->dev);
}
return ret;
}
// 对platform平台的注册会进一步触发probe函数;
static int sst_soc_probe(struct snd_soc_platform *platform)
{
struct sst_data *ctx = snd_soc_platform_get_drvdata(platform);
struct soft_platform_id spid;
int ret = 0;
memcpy(&spid, ctx->pdata->spid, sizeof(spid));
pr_debug("Enter:%s\n", __func__);
if (INTEL_MID_BOARD(1, PHONE, CLVTP) ||
INTEL_MID_BOARD(1, TABLET, CLVT) ||
INTEL_MID_BOARD(1, TABLET, BYT))
return sst_platform_clv_init(platform);
if (INTEL_MID_BOARD(1, PHONE, MRFL) ||
INTEL_MID_BOARD(1, TABLET, MRFL) ||
INTEL_MID_BOARD(1, PHONE, MOFD) ||
INTEL_MID_BOARD(1, TABLET, MOFD)) {
#if IS_BUILTIN(CONFIG_SST_MRFLD_DPCM)
ret = sst_dsp_init_v2_dpcm(platform);
#else
ret = sst_dsp_init(platform);
#endif
if (ret)
return ret;
ret = snd_soc_register_effect(platform->card, &effects_ops);
}
if (INTEL_MID_BOARD(1, TABLET, CHT)) {
ret = sst_dsp_init(platform);
if (ret)
pr_err("Dsp init failed: %d\n", ret);
}
return ret;
}
// 对platform的dai的注册,会把相应的dai结构注册进platform. 用来和Machine匹配。结构体如下:
从列表看,platform里的pcm.c部分支持的cpu_dai_name还是很多的,但针对某一产品,需要支持什么,则在Machine里定义。
static struct snd_soc_dai_driver sst_platform_dai[] = {
{
.name = SST_HEADSET_DAI, // 对应Machine里的cpu_dai_name
.ops = &sst_media_dai_ops,
.playback = {
.stream_name = "Headset Playback",
.channels_min = SST_STEREO,
.channels_max = SST_STEREO,
.rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
.stream_name = "Headset Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
},
{
.name = SST_DEEPBUFFER_DAI, // 对应Machine里的cpu_dai_name
.ops = &sst_media_dai_ops,
.playback = {
.stream_name = "Deepbuffer Playback",
.channels_min = SST_STEREO,
.channels_max = SST_STEREO,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
},
{
.name = SST_LOWLATENCY_DAI, // 对应Machine里的cpu_dai_name
.ops = &sst_media_dai_ops,
.playback = {
.stream_name = "Low Latency Playback",
.channels_min = SST_STEREO,
.channels_max = SST_STEREO,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
},
{
.name = SST_SPEAKER_DAI, // 对应Machine里的cpu_dai_name
.ops = &sst_media_dai_ops,
.playback = {
.stream_name = "Speaker Playback",
.channels_min = SST_MONO,
.channels_max = SST_STEREO,
.rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
},
{
.name = SST_VOICE_DAI, // 对应Machine里的cpu_dai_name
.playback = {
.stream_name = "Voice Downlink",
.channels_min = SST_MONO,
.channels_max = SST_STEREO,
.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
.stream_name = "Voice Uplink",
.channels_min = SST_MONO,
.channels_max = SST_STEREO,
.rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
},
{
.name = SST_COMPRESS_DAI, // 对应Machine里的cpu_dai_name
.compress_dai = 1,
.ops = &sst_compr_dai_ops,
.playback = {
.stream_name = "Compress Playback",
.channels_min = SST_STEREO,
.channels_max = SST_STEREO,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
},
{
.name = SST_VIRTUAL_DAI, // 对应Machine里的cpu_dai_name
.playback = {
.stream_name = "Virtual Playback",
.channels_min = SST_STEREO,
.channels_max = SST_STEREO,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
},
{
.name = SST_POWER_DAI, // 对应Machine里的cpu_dai_name
.ops = &sst_media_dai_ops,
.playback = {
.stream_name = "Dummy Power Stream",
.channels_min = SST_MONO,
.channels_max = SST_STEREO,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
},
},
{
.name = SST_PROBE_DAI, // 对应Machine里的cpu_dai_name
.ops = &sst_probe_dai_ops,
.playback = {
.stream_name = "Probe Playback",
.channels_min = SST_MONO,
.channels_max = SST_STEREO,
.rates = SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
},
.capture = {
.stream_name = "Probe Capture",
.channels_min = SST_MONO,
.channels_max = SST_STEREO,
.rates = SNDRV_PCM_RATE_8000 |
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
},
},
{
.name = SST_VOIP_DAI, // 对应Machine里的cpu_dai_name
.ops = &sst_media_dai_ops,
.playback = {
.stream_name = "VOIP Playback",
.channels_min = SST_MONO,
.channels_max = SST_STEREO,
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
.stream_name = "VOIP Capture",
.channels_min = SST_MONO,
.channels_max = SST_STEREO,
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
},
{
.name = SST_LOOPBACK_DAI, // 对应Machine里的cpu_dai_name
.ops = &sst_loopback_dai_ops,
.capture = {
.stream_name = "Loopback Capture",
.channels_min = SST_MONO,
.channels_max = SST_MONO,
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
},
/*BE CPU Dais */
{
.name = "ssp2-codec", // 对应Machine里的cpu_dai_name
.playback = {
.stream_name = "ssp2 playback",
.channels_min = SST_STEREO,
.channels_max = SST_STEREO,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
.stream_name = "ssp2 Capture",
.channels_min = SST_STEREO,
.channels_max = SST_STEREO,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
},
};
宏定义如下: kernel/sound/soc/intel/sst_platform_pvt.h
#define SST_HEADSET_DAI "Headset-cpu-dai"
#define SST_SPEAKER_DAI "Speaker-cpu-dai"
#define SST_VOICE_DAI "Voice-cpu-dai"
#define SST_VIRTUAL_DAI "Virtual-cpu-dai"
#define SST_LOOPBACK_DAI "Loopback-cpu-dai"
#define SST_POWER_DAI "Power-cpu-dai"
#define SST_COMPRESS_DAI "Compress-cpu-dai"
#define SST_PROBE_DAI "Probe-cpu-dai"
#define SST_VOIP_DAI "Voip-cpu-dai"
#define SST_DEEPBUFFER_DAI "Deepbuffer-cpu-dai"
#define SST_LOWLATENCY_DAI "Lowlatency-cpu-dai"
- 再介绍蓝牙和Modem的mid_ssp.c部分,代码位于:kernel/sound/soc/intel/ssp/mid_ssp.c
static struct platform_driver intel_ssp_dai_driver = {
.driver = {
.name = "mid-ssp-dai", // cpu_platform的name
.owner = THIS_MODULE,
},
.probe = ssp_dai_probe,
.remove = ssp_dai_remove,
};
static int __init ssp_soc_dai_init(void)
{
pr_info("SSP DAI: FCT %s called\n",
__func__);
return platform_driver_register(&intel_ssp_dai_driver); // 普通的device->driver结构
}
module_init(ssp_soc_dai_init);
static void __exit ssp_soc_dai_exit(void)
{
pr_debug("SSP DAI: FCT %s called\n",
__func__);
platform_driver_unregister(&intel_ssp_dai_driver);
}
module_exit(ssp_soc_dai_exit);
// 注册的platform结构体。
static struct snd_pcm_ops ssp_platform_ops = {
.open = NULL,
.close = NULL,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = ssp_platform_hw_params,
.hw_free = NULL,
.prepare = NULL,
.trigger = NULL,
.pointer = ssp_platform_pointer,
};
struct snd_soc_platform_driver soc_ssp_platform_drv = {
.ops = &ssp_platform_ops,
.probe = NULL,
.pcm_new = ssp_platform_pcm_new,
.pcm_free = ssp_platform_pcm_free,
};
// 进入probe函数
/* BT/FM */
static struct snd_soc_dai_ops ssp_dai_ops = {
.startup = ssp_dai_startup,
.shutdown = ssp_dai_shutdown,
.trigger = ssp_dai_trigger,
.hw_params = ssp_dai_hw_params,
.prepare = ssp_dai_prepare,
.set_sysclk = ssp_set_dai_sysclk,
.set_pll = NULL,
.set_fmt = ssp_set_dai_fmt,
.set_tdm_slot = ssp_set_dai_tdm_slot,
.set_tristate = ssp_set_dai_tristate,
};
struct snd_soc_dai_driver intel_ssp_platform_dai[] = {
{
.name = SSP_MODEM_DAI_NAME,
.id = 0,
.playback = {
.channels_min = 1,
.channels_max = 8,
.rates = SSP_SUPPORTED_RATES,
.formats = SSP_SUPPORTED_FORMATS,
},
.capture = {
.channels_min = 1,
.channels_max = 8,
.rates = SSP_SUPPORTED_RATES,
.formats = SSP_SUPPORTED_FORMATS,
},
.ops = &ssp_dai_ops,
.probe = ssp_probe,
.remove = ssp_remove,
},
{
.name = SSP_BT_DAI_NAME,
.id = 1,
.playback = {
.channels_min = 1,
.channels_max = 8,
.rates = SSP_SUPPORTED_RATES,
.formats = SSP_SUPPORTED_FORMATS,
},
.capture = {
.channels_min = 1,
.channels_max = 8,
.rates = SSP_SUPPORTED_RATES,
.formats = SSP_SUPPORTED_FORMATS,
},
.ops = &ssp_dai_ops,
.probe = ssp_probe,
.remove = ssp_remove,
},
};
static const struct snd_soc_component_driver ssp_component = {
.name = "ssp",
};
static int ssp_dai_probe(struct platform_device *pdev)
{
int ret;
struct intel_ssp_info *ssp_info;
pr_info("SSP DAI: FCT %s enters\n",
__func__);
ssp_info = kzalloc(sizeof(struct intel_ssp_info), GFP_KERNEL);
if (ssp_info == NULL) {
pr_err("Unable to allocate ssp_info\n");
return -ENOMEM;
}
pr_info("ssp_info address %p", ssp_info);
ret = snd_soc_register_platform(&pdev->dev, // 注册platform函数,在注册的结构体中,probe为空。
&soc_ssp_platform_drv);
if (ret) {
pr_err("registering SSP PLATFORM failed\n");
snd_soc_unregister_component(&pdev->dev);
kfree(ssp_info);
return -EBUSY;
}
ret = snd_soc_register_component(&pdev->dev, &ssp_component,
intel_ssp_platform_dai,
ARRAY_SIZE(intel_ssp_platform_dai)); // 注册platform_dai函数,在这个结构体中,有probe函数
if (ret) {
pr_err("registering cpu DAIs failed\n");
snd_soc_unregister_component(&pdev->dev);
kfree(ssp_info);
return -EBUSY;
}
ssp_info->ssp_dai_wq = create_workqueue("ssp_transfer_data");
if (!ssp_info->ssp_dai_wq) {
pr_err("work queue failed\n");
snd_soc_unregister_component(&pdev->dev);
kfree(ssp_info);
return -ENOMEM;
}
platform_set_drvdata(pdev, ssp_info);
pr_info("SSP DAI: FCT %s leaves %d\n",
__func__, ret);
return ret;
}
-
Codec部分注册流程
代码位置在:kernel/sound/soc/codecs/Rt5640.c
// 此codec首先注册为一个I2c设备。
static const struct i2c_device_id rt5640_i2c_id[] = {
{"rt5640", 0},
{"10EC5640:00", 0},
{"10EC5640", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, rt5640_i2c_id); // i2c设备的table结构。
struct i2c_driver rt5640_i2c_driver = {
.driver = {
.name = "rt5640",
.owner = THIS_MODULE,
},
.probe = rt5640_i2c_probe,
.remove = rt5640_i2c_remove,
.shutdown = rt5640_i2c_shutdown,
.id_table = rt5640_i2c_id,
};
static int __init rt5640_modinit(void)
{
return i2c_add_driver(&rt5640_i2c_driver);
}
module_init(rt5640_modinit);
static void __exit rt5640_modexit(void)
{
i2c_del_driver(&rt5640_i2c_driver);
}
module_exit(rt5640_modexit);
// 然后进入I2C的probe函数。在probe函数里注册codec设备。
struct snd_soc_dai_ops rt5640_aif_dai_ops = {
.hw_params = rt5640_hw_params,
.prepare = rt5640_prepare,
.set_fmt = rt5640_set_dai_fmt,
.set_sysclk = rt5640_set_dai_sysclk,
.set_pll = rt5640_set_dai_pll,
};
struct snd_soc_dai_driver rt5640_dai[] = {
{
.name = "rt5640-aif1", // codec_dai的name,对应Machine里的codec_dai_name
.id = RT5640_AIF1,
.playback = {
.stream_name = "AIF1 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = RT5640_STEREO_RATES,
.formats = RT5640_FORMATS,
},
.capture = {
.stream_name = "AIF1 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = RT5640_STEREO_RATES,
.formats = RT5640_FORMATS,
},
.ops = &rt5640_aif_dai_ops,
},
{
.name = "rt5640-aif2", // codec_dai的name,对应Machine里的codec_dai_name
.id = RT5640_AIF2,
.playback = {
.stream_name = "AIF2 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = RT5640_STEREO_RATES,
.formats = RT5640_FORMATS,
},
.capture = {
.stream_name = "AIF2 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = RT5640_STEREO_RATES,
.formats = RT5640_FORMATS,
},
.ops = &rt5640_aif_dai_ops,
.symmetric_rates = 1,
},
#if IS_ENABLED(CONFIG_SND_SOC_RT5643) || IS_ENABLED(CONFIG_SND_SOC_RT5646)
{
.name = "rt5640-aif3", // codec_dai的name,对应Machine里的codec_dai_name
.id = RT5640_AIF3,
.playback = {
.stream_name = "AIF3 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = RT5640_STEREO_RATES,
.formats = RT5640_FORMATS,
},
.capture = {
.stream_name = "AIF3 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = RT5640_STEREO_RATES,
.formats = RT5640_FORMATS,
},
.ops = &rt5640_aif_dai_ops,
},
#endif
};
static struct snd_soc_codec_driver soc_codec_dev_rt5640 = {
.probe = rt5640_probe,
.remove = rt5640_remove,
.suspend = rt5640_suspend,
.resume = rt5640_resume,
.set_bias_level = rt5640_set_bias_level,
.idle_bias_off = true,
.reg_cache_size = RT5640_VENDOR_ID2 + 1,
.reg_word_size = sizeof(u16),
.reg_cache_default = rt5640_reg,
.volatile_register = rt5640_volatile_register,
.readable_register = rt5640_readable_register,
.reg_cache_step = 1,
.controls = rt5640_snd_controls,
.num_controls = ARRAY_SIZE(rt5640_snd_controls),
.dapm_widgets = rt5640_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(rt5640_dapm_widgets),
.dapm_routes = rt5640_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(rt5640_dapm_routes),
.set_sysclk = rt5640_set_sysclk,
};
static int rt5640_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct rt5640_priv *rt5640;
int ret;
pr_debug("%s enter", __func__);
rt5640 = kzalloc(sizeof(struct rt5640_priv), GFP_KERNEL);
if (NULL == rt5640)
return -ENOMEM;
i2c_set_clientdata(i2c, rt5640);
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640,
rt5640_dai, ARRAY_SIZE(rt5640_dai)); // 注册codec设备。触发probe函数。同时注册dai结构体
if (ret < 0)
kfree(rt5640);
return ret;
}