目录:
1. 架构
整体框架
2. 文件组成
文件/文件夹 | 作用 |
---|---|
aoa | 苹果板载音频驱动 |
arm | arm音频设备支持 |
atmel | atmel ABDAC(音频字节流数模转换器)和ac97C(ac97控制器) |
core | alsa驱动中间层,是alsa驱动的核心 |
core/oss | 模拟旧OSS架构的PCM和Mixer模块 |
core/seq | 有关音序器相关代码 |
drivers | 与cpu、bus无关的公用代码 |
firewire | IEEE-1394/FireWire/iLink音频设备支持 |
hda | HD audio(高保真音频)支持 |
i2c | alsa的i2c控制代码 |
isa | 各种isa声卡的代码 |
mips | mips音频设备支持 |
oss | 对oss的兼容支持 |
parisc | 鸿蒙和PA-RISC架构的GSC音频设备支持 |
pci | pci音频设备支持 |
pcmcia | pcmcia音频设备支持 |
ppc | PowerPC音频设备支持 |
sh | SuperH架构音频设备支持 |
soc | system-on-chip体系的中间层代码 |
soc/codec | 针对asoc体系各种codec代码,与平台无关 |
sparc | SPARC架构音频设备支持 |
spi | spi音频设备支持 |
synth | 一些工具 |
usb | usb音频设备支持 |
ac97_bus.c | 实现ac97标准总线 |
last.c | 音频设备注册完成后打印”ALSA devices List” |
sound_core.c | 注册音频核心层子系统 |
sound_firmware.c | 加载音频驱动固件 |
3. ALSA核心层
3.1 alsa驱动的设备文件结构:
目前ALSA内核提供给用户空间的接口有:
-
信息接口(proc/asound)
-
控制接口(dev/snd/controlCX)
-
混音器接口(dev/snd/mixerCXDX)
-
PCM接口(dev/snd/pcmCXDX)
-
Raw迷笛接口(dev/snd/midiCXDX)
-
音序器接口(dev/snd/seq)
-
定时器接口(dev/snd/timer)
tree /proc/asound/
/proc/asound/
|-- Codec -> card0
|-- card0
| |-- id
| |-- oss_mixer
| |-- pcm0c
| | |-- info
| | |-- oss
| | |-- sub0
| | |-- hw_params
| | |-- info
| | |-- prealloc
| | |-- prealloc_max
| | |-- status
| | |-- sw_params
| |-- pcm0p
| |-- info
| |-- oss
| |-- sub0
| |-- hw_params
| |-- info
| |-- prealloc
| |-- prealloc_max
| |-- status
| |-- sw_params
|-- cards
|-- devices
|-- oss
| |-- devices
| |-- sndstat
|-- pcm
|-- timers
|-- version
7 directories, 25 files
# ls /dev/snd/ -l
total 0
crw------- 1 root root 116, 0 Jan 1 00:00 controlC0 // 用于声卡的控制,例如通道选择,混音,麦克风的控制等
crw------- 1 root root 116, 24 Jan 1 00:00 pcmC0D0c // 用于录音的pcm设备
crw------- 1 root root 116, 16 Jan 1 00:00 pcmC0D0p //用于播放的pcm设备
crw------- 1 root root 116, 33 Jan 1 00:00 timer //定时器
注:
部分设备未开启: midiC0D0(用于播放midi音频), 2. seq (音序器),等
设备文件注册代码, 以pcm设备为例:
int snd_register_device(int type, struct snd_card *card, int dev,
const struct file_operations *f_ops,
void *private_data, struct device *device)
static int snd_pcm_dev_register(struct snd_device *device)
static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
int playback_count, int capture_count, bool internal,
struct snd_pcm **rpcm)
int snd_pcm_new(struct snd_card *card, const char *id, int device,
int playback_count, int capture_count, struct snd_pcm **rpcm)
------------------------------------------------------------------------
int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
static int soc_link_init(struct snd_soc_card *card,
struct snd_soc_pcm_runtime *rtd)
static int soc_link_init(struct snd_soc_card *card,
struct snd_soc_pcm_runtime *rtd)
static int snd_soc_instantiate_card(struct snd_soc_card *card)
static int snd_soc_bind_card(struct snd_soc_card *card)
int snd_soc_register_card(struct snd_soc_card *card)
3.2 数据结构和操作函数
3.2.1 声卡
直接接口: int snd_soc_register_card(struct snd_soc_card *card)(soc\soc-core.c)
snd_soc_register_card -> snd_soc_bind_card -> snd_soc_instantiate_card -> snd_card_new
3.2.2 pcm设备
snd_pcm_new
snd_pcm_set_ops
3.2.2 控制接口
struct snd_kcontrol
struct snd_kcontrol_new
-> snd_pcm_new
3.3 alsa声卡设备
3.3.1 platform层
3.3.2 cpu-dai层
3.3.3 codec-dai层
4. ASoC框架
参考帖子 :ALSA-ASOC音频驱动框架简述
4.1 整体框架
4.2 主要接口
4.3 源码分析
machine/platform/codec代码分析从其他地方摘的图,链接:https://blog.csdn.net/baidu_36250852/article/details/120614976
注: 此图中应该未使用设备树,最新版本Linux应该会有细节区别
4.3.1 无外置codec芯片,直接输出模拟信号
4.3.1.1 配置
设备树:
&codec {
allwinner,audio-routing =
"Headphone", "HP",
"Headphone", "HPCOM",
"LINEIN", "Line In",
"FMINL", "Left FM In",
"FMINR", "Right FM In",
"MIC", "Mic";
status = "okay";
};
codec: codec@1c23c00 {
compatible = "allwinner,suniv-f1c100s-codec";
reg = <0x01c23c00 0x400>;
interrupts = <21>;
clocks = <&ccu CLK_BUS_CODEC>,
<&ccu CLK_CODEC>;
clock-names = "apb", "codec";
resets = <&ccu RST_BUS_CODEC>;
dmas = <&dma SUN4I_DMA_NORMAL 0x0c>,
<&dma SUN4I_DMA_NORMAL 0x0c>;
dma-names = "rx", "tx";
status = "disabled";
};
4.3.1.2 源码摘录
sound\soc\sunxi\sun4i-codec.c
static struct snd_soc_card *suniv_codec_create_card(struct device *dev)
{
struct snd_soc_card *card;
int ret;
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return ERR_PTR(-ENOMEM);
card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
if (!card->dai_link)
return ERR_PTR(-ENOMEM);
card->dev = dev;
card->name = "F1C100s Audio Codec";
card->dapm_widgets = suniv_codec_card_dapm_widgets;
card->num_dapm_widgets = ARRAY_SIZE(suniv_codec_card_dapm_widgets);
card->dapm_routes = suniv_codec_card_routes;
card->num_dapm_routes = ARRAY_SIZE(suniv_codec_card_routes);
card->fully_routed = true;
ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");
if (ret)
dev_warn(dev, "failed to parse audio-routing: %d\n", ret);
return card;
};
static const struct sun4i_codec_quirks suniv_f1c100s_codec_quirks = {
.regmap_config = &suniv_codec_regmap_config,
.codec = &suniv_codec_codec,
.create_card = suniv_codec_create_card,
.reg_adc_fifoc = REG_FIELD(SUNIV_CODEC_ADC_FIFOC, 0, 31),
.reg_dac_txdata = SUN4I_CODEC_DAC_TXDATA,
.reg_adc_rxdata = SUNIV_CODEC_ADC_RXDATA,
.has_reset = true,
.dma_max_burst = SUNIV_DMA_MAX_BURST,
};
static const struct of_device_id sun4i_codec_of_match[] = {
{
.compatible = "allwinner,suniv-f1c100s-codec",
.data = &suniv_f1c100s_codec_quirks,
},
};
static int sun4i_codec_probe(struct platform_device *pdev)
{
struct snd_soc_card *card;
struct sun4i_codec *scodec;
const struct sun4i_codec_quirks *quirks;
struct resource *res;
void __iomem *base;
int ret;
scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL);
if (!scodec)
return -ENOMEM;
scodec->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base)) {
dev_err(&pdev->dev, "Failed to map the registers\n");
return PTR_ERR(base);
}
quirks = of_device_get_match_data(&pdev->dev);
if (quirks == NULL) {
dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
return -ENODEV;
}
scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
quirks->regmap_config);
if (IS_ERR(scodec->regmap)) {
dev_err(&pdev->dev, "Failed to create our regmap\n");
return PTR_ERR(scodec->regmap);
}
/* Get the clocks from the DT */
scodec->clk_apb = devm_clk_get(&pdev->dev, "apb");
if (IS_ERR(scodec->clk_apb)) {
dev_err(&pdev->dev, "Failed to get the APB clock\n");
return PTR_ERR(scodec->clk_apb);
}
scodec->clk_module = devm_clk_get(&pdev->dev, "codec");
if (IS_ERR(scodec->clk_module)) {
dev_err(&pdev->dev, "Failed to get the module clock\n");
return PTR_ERR(scodec->clk_module);
}
if (quirks->has_reset) {
scodec->rst = devm_reset_control_get_exclusive(&pdev->dev,
NULL);
if (IS_ERR(scodec->rst)) {
dev_err(&pdev->dev, "Failed to get reset control\n");
return PTR_ERR(scodec->rst);
}
}
scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa",
GPIOD_OUT_LOW);
if (IS_ERR(scodec->gpio_pa)) {
ret = PTR_ERR(scodec->gpio_pa);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "Failed to get pa gpio: %d\n", ret);
return ret;
}
/* reg_field setup */
scodec->reg_adc_fifoc = devm_regmap_field_alloc(&pdev->dev,
scodec->regmap,
quirks->reg_adc_fifoc);
if (IS_ERR(scodec->reg_adc_fifoc)) {
ret = PTR_ERR(scodec->reg_adc_fifoc);
dev_err(&pdev->dev, "Failed to create regmap fields: %d\n",
ret);
return ret;
}
/* Enable the bus clock */
if (clk_prepare_enable(scodec->clk_apb)) {
dev_err(&pdev->dev, "Failed to enable the APB clock\n");
return -EINVAL;
}
/* Deassert the reset control */
if (scodec->rst) {
ret = reset_control_deassert(scodec->rst);
if (ret) {
dev_err(&pdev->dev,
"Failed to deassert the reset control\n");
goto err_clk_disable;
}
}
/* DMA configuration for TX FIFO */
scodec->playback_dma_data.addr = res->start + quirks->reg_dac_txdata;
scodec->playback_dma_data.maxburst = quirks->dma_max_burst;
scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
/* DMA configuration for RX FIFO */
scodec->capture_dma_data.addr = res->start + quirks->reg_adc_rxdata;
scodec->capture_dma_data.maxburst = quirks->dma_max_burst;
scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
ret = devm_snd_soc_register_component(&pdev->dev, quirks->codec,
&sun4i_codec_dai, 1);
if (ret) {
dev_err(&pdev->dev, "Failed to register our codec\n");
goto err_assert_reset;
}
ret = devm_snd_soc_register_component(&pdev->dev,
&sun4i_codec_component,
&dummy_cpu_dai, 1);
if (ret) {
dev_err(&pdev->dev, "Failed to register our DAI\n");
goto err_assert_reset;
}
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (ret) {
dev_err(&pdev->dev, "Failed to register against DMAEngine\n");
goto err_assert_reset;
}
card = quirks->create_card(&pdev->dev);
if (IS_ERR(card)) {
ret = PTR_ERR(card);
dev_err(&pdev->dev, "Failed to create our card\n");
goto err_assert_reset;
}
snd_soc_card_set_drvdata(card, scodec);
ret = snd_soc_register_card(card); //注册声卡
if (ret) {
dev_err(&pdev->dev, "Failed to register our card\n");
goto err_assert_reset;
}
return 0;
err_assert_reset:
if (scodec->rst)
reset_control_assert(scodec->rst);
err_clk_disable:
clk_disable_unprepare(scodec->clk_apb);
return ret;
}
static struct platform_driver sun4i_codec_driver = {
.driver = {
.name = "sun4i-codec",
.of_match_table = sun4i_codec_of_match,
},
.probe = sun4i_codec_probe,
.remove = sun4i_codec_remove,
};
module_platform_driver(sun4i_codec_driver);
5 测试
# tinymix contents
# tinymix set 2 1
# tinymix set 1 63
# tinymix set 13 1
# tinycap 1.wav
# tinyplay 1.wav
Number of controls: 25
ctl type num name value
0 INT 1 DAC Playback Volume 63 (range 0->63)
1 INT 1 Headphone Playback Volume 63 (range 0->63)
2 BOOL 2 Headphone Playback Switch On, On
3 INT 1 Line In Playback Volume 0 (range 0->7)
4 INT 1 FM In Playback Volume 0 (range 0->7)
5 INT 1 Mic In Playback Volume 3 (range 0->7)
6 INT 1 Mic Boost Volume 4 (range 0->7)
7 INT 1 ADC Capture Volume 3 (range 0->7)
8 BOOL 1 ADC Mixer Right Out Capture Switch Off
9 BOOL 1 ADC Mixer Left Out Capture Switch Off
10 BOOL 1 ADC Mixer Line In Capture Switch Off
11 BOOL 1 ADC Mixer Right FM In Capture Switch Off
12 BOOL 1 ADC Mixer Left FM In Capture Switch Off
13 BOOL 1 ADC Mixer Mic Capture Switch On
14 BOOL 1 Left Mixer Right DAC Playback Switch Off
15 BOOL 1 Left Mixer Left DAC Playback Switch Off
16 BOOL 1 Left Mixer FM In Playback Switch Off
17 BOOL 1 Left Mixer Line In Playback Switch Off
18 BOOL 1 Left Mixer Mic In Playback Switch Off
19 BOOL 1 Right Mixer Left DAC Playback Switch Off
20 BOOL 1 Right Mixer Right DAC Playback Switch Off
21 BOOL 1 Right Mixer FM In Playback Switch Off
22 BOOL 1 Right Mixer Line In Playback Switch Off
23 BOOL 1 Right Mixer Mic In Playback Switch Off
24 ENUM 2 Headphone Source Playback Route , DACMixer, , DACMixer
参考文章:
Linux ALSA 系统架构:
主要针对初始化,设备打开
架构: