赞
踩
硬件原理图
与mini2440连接示意图
(1)采样频率(sample rate):录音设备在一秒钟内对声音信号的采样次数。
(2)通道数(channel):分为单声道mono;立体声stereo。两个声道,说明只有左右两边有声音传过来。
(3)比特率(bitrate):也叫码率。针对编码格式,表示压缩编码后每秒的音频数据量大小。计算公式:比特率 = 采样率 x 采样精度 x 声道数。单位kbps。
(4)量化位数:指每个采样点里传输的数字信号次数,一般有8位、16位、32位等。S3C2440只支持8位和16位。
(1)IIS接口
MCLK:主机为编解码芯片提供的系统同步时钟 ( Master/system clock input ) ,在2440中,一般设置为256fs,或者384fs
BCLK:编解码芯片提供的串行时钟信号 ( Audio bit clock output ) ,也就是量化位深,比如I2SIRCK=44.1khz,量化位为32位,则BCLK=44.1khz322
I2SLRCK:采样频率信号,当为低电平时是采样的是左声道信号,为高电平是右声道信号,常见有44.1Khz(1fs)
I2SDI:ADC数据输入
I2SDO:DAC数据输出
(2)IIS时序
LRCK就是采样频率:
LRCK为低——传输的采样数据是左声道;
LRCK为高——传输的采样数据是右声道。
(3)L3接口
L3MODE:0——地址模式;1——数据模式。
L3DATA:地址/数据。
L3CLOCK:时钟。
地址模式:bit7-bit2为地址,bit1-bit0为模式。
bit7-bit2 | bit1-bit0 |
---|---|
000101 | 模式(控制音量、位宽等) |
ASoC(ALSA System on Chip)是 ALSA 在 SoC 方面的发展和演变,它在本质上仍然属于ALSA,但是在 ALSA 架构基础上对 CPU 相关的代码和 Codec 相关的代码进行了分离。其原因是,采用传统 ALSA 架构的情况下,同一型号的 Codec 工作于不同的 CPU 时,需要不同的驱动,这不符合代码重用的要求。
ASoC 主要由 3 部分组成。
(1)Machine
是指某一款机器,可以是某款设备,某款开发板,又或者是某款智能手机,由此可以看出Machine几乎是不可重用的,每个Machine上的硬件实现可能都不一样,CPU不一样,Codec不一样,音频的输入、输出设备也不一样,Machine为CPU、Codec、输入输出设备提供了一个载体。
(2)Platform
一般是指某一个SoC平台,比如pxaxxx,s3cxxxx,omapxxx等等,与音频相关的通常包含该SoC中的时钟、DMA、I2S、PCM等等,只要指定了SoC,那么我们可以认为它会有一个对应的Platform,它只与SoC相关,与Machine无关,这样我们就可以把Platform抽象出来,使得同一款SoC不用做任何的改动,就可以用在不同的Machine中。实际上,把Platform认为是某个SoC更好理解。
(3)Codec
字面上的意思就是编解码器,Codec里面包含了I2S接口、D/A、A/D、Mixer、PA(功放),通常包含多种输入(Mic、Line-in、I2S、PCM)和多个输出(耳机、喇叭、听筒,Line-out),Codec和Platform一样,是可重用的部件,同一个Codec可以被不同的Machine使用。嵌入式Codec通常通过I2C对内部的寄存器进行控制。
分成三个部分:
Machine:
S3c24xx_uda134x.c (sound\soc\s3c24xx)
Platform:
S3c24xx-i2s.c (sound\soc\s3c24xx)
Codec:
Uda134x.c (sound\soc\codecs)
snd_soc_card代表着Machine驱动,snd_soc_platform则代表着Platform驱动,snd_soc_codec则代表了Codec驱动,而snd_soc_dai_link则负责连接Platform和Codec。linux使用snd_soc_codec进行所有codec设备的抽象,而将codec的驱动抽象为snd_soc_codec_driver结构。
(1)设备
/arch/arm/mach-s3c2440/mach-mini2440.c
static struct s3c24xx_uda134x_platform_data mini2440_audio_pins __initdata = { .l3_clk = S3C2410_GPB(4), .l3_mode = S3C2410_GPB(2), .l3_data = S3C2410_GPB(3), .model = UDA134X_UDA1341 }; static struct platform_device mini2440_audio __initdata = { .name = "s3c24xx_uda134x", .id = 0, .dev = { .platform_data = &mini2440_audio_pins, }, }; static struct platform_device *mini2440_devices[] __initdata = { ... &s3c_device_iis, &mini2440_audio, ... };
(2)驱动
初始化,在/sound/soc/s3c24xx/s3c24xx_uda134x.c。
static struct platform_driver s3c24xx_uda134x_driver = {
.probe = s3c24xx_uda134x_probe,
.remove = s3c24xx_uda134x_remove,
.driver = {
.name = "s3c24xx_uda134x",
.owner = THIS_MODULE,
},
};
static int __init s3c24xx_uda134x_init(void)
{
return platform_driver_register(&s3c24xx_uda134x_driver);
}
匹配后调用probe。s3c24xx_uda134x_probe首先设置L3相关引脚,然后添加设备platform_device_add(s3c24xx_uda134x_snd_device)。
static int s3c24xx_uda134x_probe(struct platform_device *pdev)
{
...
s3c24xx_uda134x_snd_device = platform_device_alloc("soc-audio", -1);
...
platform_set_drvdata(s3c24xx_uda134x_snd_device,
&s3c24xx_uda134x_snd_devdata);
s3c24xx_uda134x_snd_devdata.dev = &s3c24xx_uda134x_snd_device->dev;
ret = platform_device_add(s3c24xx_uda134x_snd_device);
...
}
添加了名称是"soc-audio"的设备,找到相同名称的driver,在sound/soc/soc-core.c中,snd_soc_init注册驱动。
static struct platform_driver soc_driver = {
.driver = {
.name = "soc-audio",
.owner = THIS_MODULE,
.pm = &soc_pm_ops,
},
.probe = soc_probe,
.remove = soc_remove,
};
static int __init snd_soc_init(void)
{
...
return platform_driver_register(&soc_driver);
}
与驱动匹配后调用probe。
static int soc_probe(struct platform_device *pdev) { int ret = 0; struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_card *card = socdev->card; card->socdev = socdev; card->dev = &pdev->dev; ret = snd_soc_register_card(card); if (ret != 0) { dev_err(&pdev->dev, "Failed to register card\n"); return ret; } return 0; }
platform_get_drvdata与s3c24xx_uda134x_probe中的platform_set_drvdata相对应,即得到s3c24xx_uda134x_snd_devdata。
相关结构体。
static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = { .name = "UDA134X", .stream_name = "UDA134X", .codec_dai = &uda134x_dai, .cpu_dai = &s3c24xx_i2s_dai, .ops = &s3c24xx_uda134x_ops, }; //联系起声卡驱动各个模块 static struct snd_soc_card snd_soc_s3c24xx_uda134x = { .name = "S3C24XX_UDA134X", .platform = &s3c24xx_soc_platform, .dai_link = &s3c24xx_uda134x_dai_link, .num_links = 1, }; //对整个声卡结构体的封装 static struct snd_soc_device s3c24xx_uda134x_snd_devdata = { .card = &snd_soc_s3c24xx_uda134x, .codec_dev = &soc_codec_dev_uda134x, .codec_data = &s3c24xx_uda134x, };
添加"soc-audio"设备,soc_probe–>snd_soc_register_card。
static int snd_soc_register_card(struct snd_soc_card *card)
{
...
list_add(&card->list, &card_list);//添加snd_soc_s3c24xx_uda134x
snd_soc_instantiate_cards();
...
}
继续
static void snd_soc_instantiate_cards(void)
{
struct snd_soc_card *card;
list_for_each_entry(card, &card_list, list)
snd_soc_instantiate_card(card);
}
遍历card_list,调用snd_soc_instantiate_card。
static void snd_soc_instantiate_card(struct snd_soc_card *card) { list_for_each_entry(platform, &platform_list, list)//从platform_list中找与card->platform相同的 if (card->platform == platform) { found = 1; break; } ... if (!ac97) for (i = 0; i < card->num_links; i++) { found = 0; list_for_each_entry(dai, &dai_list, list)//从dai_list中找与card->dai_link[i].codec_dai相同的 if (card->dai_link[i].codec_dai == dai) { found = 1; break; } if (!found) { dev_dbg(card->dev, "DAI %s not registered\n", card->dai_link[i].codec_dai->name); return; } } for (i = 0; i < card->num_links; i++) { struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai; if (cpu_dai->probe) { ret = cpu_dai->probe(pdev, cpu_dai);//即s3c24xx_i2s_probe if (ret < 0) goto cpu_dai_err; } } if (codec_dev->probe) { ret = codec_dev->probe(pdev);//即uda134x_soc_probe if (ret < 0) goto cpu_dai_err; } if (platform->probe) { ret = platform->probe(pdev); //没有 if (ret < 0) goto platform_err; } ... }
在probe函数内部会把新增的声卡snd_soc_s3c24xx_uda134x加入card链表card_list,然后对声卡链表的每个节点,进行platform(码流传输模块),dai_list(数字音频接口)比对,如果发现相同,则调用各自的probe函数(这里card、platform没有probe函数)。
上面调用cpu_dai->probe(pdev, cpu_dai),.cpu_dai = &s3c24xx_i2s_dai。playback用于播放,capture用于录音,最大/最少所支持的通道channels_max和channels_min,采样频率rates,支持的格式formats。
struct snd_soc_dai s3c24xx_i2s_dai = { .name = "s3c24xx-i2s", .id = 0, .probe = s3c24xx_i2s_probe, .suspend = s3c24xx_i2s_suspend, .resume = s3c24xx_i2s_resume, .playback = { .channels_min = 2, .channels_max = 2, .rates = S3C24XX_I2S_RATES, .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,}, .capture = { .channels_min = 2, .channels_max = 2, .rates = S3C24XX_I2S_RATES, .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,}, .ops = &s3c24xx_i2s_dai_ops, }; static int __init s3c24xx_i2s_init(void) { return snd_soc_register_dai(&s3c24xx_i2s_dai); }
在s3c24xx_i2s_probe中初始化了i2s接口。
在snd_soc_instantiate_card函数中调用codec_dev->probe(pdev),即uda134x_soc_probe
uda134x_soc_probe --->
snd_soc_new_pcms ---> //注册pcm
snd_card_create --->
snd_ctl_create //用户空间开辟了一个控制音频的接口
soc_new_pcm //生成音频控制接口,录音/播音
(1)内核配置
-> Device Drivers
-> Sound card support
-> Advanced Linux Sound Architecture
-> ALSA for Soc audio support
<*> Soc Audio for the Samsung S3C24XX chips
<*> Soc I2S Audio support UDA134X wired to a S3C24XX
(2)播放
用madplay播放测试。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。