一. ALSA 架构:
D:\kernel_code\linux-3.4.2\linux-3.4.2\sound\core
SNDRV_DEFAULT_IDX1 -1
SNDRV_DEFAULT_STR1 NULL
extra_size private_data 成员数据大小 可以通过 snd_dev = (struct sagitta_snd_dev *) card->private_data; 获取card上的私有数据
int snd_card_create(int idx, const char *xid, struct module *module, int extra_size, struct snd_card **card_ret) //sound/core/init.c
a. idx 为 -1, 则任意申请一个free的id,最大声卡数目为32/8
b. 申请(card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);) 并初始化 struct snd_card 的成员变量,包括设置获得的id
c. snd_ctl_create(card); //控制接口? //sound/core/control.c
snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops); //sound/core/device.c
struct snd_device *dev;
dev->type = type; ==SNDRV_DEV_CONTROL
list_add(&dev->list, &card->devices); 添加到card->devices链表
d. snd_info_card_create(card); //sound/core/info.c
d.1 在proc目录下创建card%d节点
snd_card_set_dev(card, dev); ?
int device: device number
snd_pcm_new(struct snd_card *card, const char *id, int device,int playback_count, int capture_count, struct snd_pcm **rpcm) //sound/core/pcm.c
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
//创建回放以及录制的流 stream
snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)
创建 "pcm%i%c" proc 文件
for (idx = 0, prev = NULL; idx < substream_count; idx++)
struct snd_pcm_substream *substream = kzalloc(sizeof(*substream), GFP_KERNEL); //创建 stream
snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)
snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops) //sound/core/device.c
void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, struct snd_pcm_ops *ops)
struct snd_pcm_str *stream = &pcm->streams[direction];
struct snd_pcm_substream *substream;
for (substream = stream->substream; substream != NULL; substream = substream->next)
substream->ops = ops;
int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) ?????
snd_card_register(card);
int snd_card_register(struct snd_card *card) //sound/core/init.c
card->card_dev = device_create(sound_class, card->dev, MKDEV(0, 0), card, "card%i", card->number);
snd_device_register_all(card); //比如在dev目录下创建/dev/card%i 节点 //sound/core/device.c
list_for_each_entry(dev, &card->devices, list)
dev->ops->dev_register(dev) //遍历所有card上的devices节点,并调用节点的dev_register()接口注册
list_add_tail(&substream->link_list, &substream->self_group.substreams);
snd_cards[card->number] = card; //snd_cards[] 全局数组
init_info_for_card(card); //proc 文件相关
device_create_file(card->card_dev, &card_id_attrs); //在sys目录下创建相关属性文件
device_create_file(card->card_dev, &card_number_attrs); //在sys目录下创建相关属性文件
//具体 pcm 的注册 (音频数据接口)
int snd_pcm_dev_register(struct snd_device *device)
list_add_tail(&newpcm->list, &snd_pcm_devices);
sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device); //playback 方式
sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device); //capture 方式
/* register pcm */ //snd_pcm_f_ops[2] 提供给用户态的读写
err = snd_register_device_for_dev(devtype, pcm->card, pcm->device, &snd_pcm_f_ops[cidx], pcm, str, dev); //sound/core/sound.c
实际创建 "pcmC%iD%ip" 和 "pcmC%iD%ic" 节点
snd_minors[minor] = preg;
为pcm回放与录像创建 sys/目录下的文件
snd_pcm_timer_init() //为每个流创建定时器相关 //sound/core/pcm_timer.c
sprintf(timer->name, "PCM %s %i-%i-%i", substream->stream == SNDRV_PCM_STREAM_CAPTURE ? "capture" : "playback", tid.card, tid.device, tid.subdevice);
snd_device_register(timer->card, timer)
//通知机制 snd_pcm_notify() //对应于 oss 架构
list_for_each_entry(notify, &snd_pcm_notify_list, list)
notify->n_register(pcm);
//具体 control 的注册 控制接口(比如音量等)
int snd_ctl_dev_register(struct snd_device *device)
sprintf(name, "controlC%i", cardnum);
snd_ctl_f_ops 提供给用户态的读写
snd_register_device_for_dev(SNDRV_DEVICE_TYPE_CONTROL, card, -1, &snd_ctl_f_ops, card, name)
实际创建 "controlC%i" 节点
补充:
涉及其余模块
PCM ---- snd_pcm_new()
RAWMIDI -- snd_rawmidi_new()
CONTROL -- snd_ctl_create()
TIMER -- snd_timer_new()
INFO -- snd_card_proc_new()
JACK -- snd_jack_new()
ALSA 内核态基本是使用流程:
//a. 创建 snd_card 结构体, 内部会创建 control 相关设备
snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, 0, &card); // 旧版 snd_card_new()
//b. 创建 pcm 相关设备
for (i = 0; i < alsa_setup_info->pcm_count; i++)
snd_pcm_new(alsa_cxt->card, pcm_info->name, device_idx, 0, pcm_info->capture_count, &pcm);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &alsa_model_pcm_ops);
//c. 注册音频设备
snd_card_set_dev(card, dev);
snd_card_register(card);
二. ASOC 架构:
D:\kernel_code\linux-3.4.2\linux-3.4.2\sound\soc
machine + codec + platform
machine: 指一块开发板
platform: 某个SOC 如 s3cxxxx,属于某块核心板 iis
音频的DMA
音频的接口 iis
codec: 音频芯片 包含iis接口,D/A、A/D、Mixer、PA(功放),通常包含多种输入(Mic、Line-in、I2S、PCM)和多个输出(耳机、喇叭、听筒,Line-out)
音频的控件(controls)
PCM 接口
DAPM(动态音频电源管理)
所有的Codec驱动都要提供以下特性:
Codec DAI 和 PCM的配置信息;
Codec的IO控制方式(I2C,SPI等);
Mixer和其他的音频控件;
Codec的ALSA音频操作接口;
必要时,也可以提供以下功能:
DAPM描述信息;
DAPM事件处理程序;
DAC数字静音控制
D:\kernel_code\linux-3.4.2\linux-3.4.2\sound\soc\samsung //samsung的ASOC架构
涉及的接口:
cpu端:iis,iic
uda134x: iis,l3接口 (int l3_clk; int l3_mode; int l3_data;)
int s3c24xx_uda134x_probe(struct platform_device *pdev) //machine 子模块相关
//设置l3口的gpio口属性
s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk, "clk")
s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode, "mode")
s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data, "data")
创建平台设备"soc-audio"
static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
.name = "UDA134X",
.stream_name = "UDA134X",
.codec_name = "uda134x-codec", //codec
.codec_dai_name = "uda134x-hifi", //codec dai
.cpu_dai_name = "s3c24xx-iis", //cpu dai
.ops = &s3c24xx_uda134x_ops,
.platform_name = "samsung-audio", //dma
};
static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
.name = "S3C24XX_UDA134X",
.owner = THIS_MODULE,
.dai_link = &s3c24xx_uda134x_dai_link,
.num_links = 1,
};
D:\kernel_code\linux-3.4.2\linux-3.4.2\sound\soc\soc-core.c
//ASOC 架构
int soc_probe(struct platform_device *pdev)
struct snd_soc_card *card = platform_get_drvdata(pdev);
snd_soc_register_card(card);
解析出每一项 snd_soc_dai_link 对象
创建 struct snd_soc_pcm_runtime
struct snd_soc_pcm_runtime* card->rtd = devm_kzalloc(card->dev, sizeof(struct snd_soc_pcm_runtime) * (card->num_links + card->num_aux_devs), GFP_KERNEL);
card->rtd[i].dai_link = &card->dai_link[i]; //struct snd_soc_dai_link
list_add(&card->list, &card_list); //全局变量 card_list
snd_soc_instantiate_cards();
snd_soc_instantiate_card(card); //从 card_list 链表中,依次取出 struct snd_soc_card* card
1. soc_bind_dai_link(card, i);
1. struct snd_soc_dai *cpu_dai;
从 dai_list 中查找 cpu_dai, 若与snd_soc_dai_link指定的cpu_dai_name名字相同,则赋值
rtd->cpu_dai = cpu_dai;
2. struct snd_soc_codec *codec;
从 codec_list 中查找 codec, 若与snd_soc_dai_link指定的codec_name名字相同,则赋值
rtd->codec = codec;
3. struct snd_soc_dai *codec_dai;
从 dai_list 中查找 codec_dai, 若与snd_soc_dai_link指定的codec_dai_name名字相同,则赋值
rtd->codec_dai = codec_dai;
4. struct snd_soc_platform *platform;
从 platform_list 中查找 platform, 若与snd_soc_dai_link指定的platform_name名字相同,则赋值
rtd->platform = platform;
2. list_for_each_entry(codec, &codec_list, list) //遍历 codec_list 主要功能:codec的cache相关操作
判断是否有 card->num_configs
snd_soc_cache_init(codec); //sound/soc/soc-cache.c
codec->cache_ops = &cache_types[i]; //全局变量 cache_types
codec->cache_ops->init(codec);
3. snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, card->owner, 0, &card->snd_card); //创建card节点以及control节点
4. list_add(&card->dapm.list, &card->dapm_list); //DAPM (动态音频电源管理)
5. snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets, card->num_dapm_widgets); //如果指定 card->dapm_widgets sound/soc/soc-dapm.c
6. card->probe(card);
7. soc_probe_dai_link(card, i, order); //order: -2 ~ 2
7.1 struct snd_soc_dai* cpu_dai; /* probe the cpu_dai */
cpu_dai->driver->probe(cpu_dai);
7.2 struct snd_soc_codec *codec; /* probe the CODEC */
soc_probe_codec(card, codec);
snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets, driver->num_dapm_widgets);
snd_soc_dapm_new_dai_widgets(&codec->dapm, dai);
driver->probe(codec);
snd_soc_add_codec_controls(codec, driver->controls, driver->num_controls);
snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes, driver->num_dapm_routes);
7.3 struct snd_soc_dai *codec_dai; /* probe the CODEC DAI */
codec_dai->driver->probe(codec_dai);
7.4 struct snd_soc_platform *platform /* probe the platform */
soc_probe_platform(card, platform);
snd_soc_dapm_new_controls(&platform->dapm, driver->dapm_widgets, driver->num_dapm_widgets);
driver->probe(platform);
snd_soc_add_platform_controls(platform, driver->controls, driver->num_controls);
snd_soc_dapm_add_routes(&platform->dapm, driver->dapm_routes, driver->num_dapm_routes);
7.5 soc_post_component_init(card, codec, num, 0); //DAPM 与 widgets
7.6 soc_new_pcm(rtd, num); //创建PCM //sound/soc/soc-pcm.c
soc_pcm_ops->open = soc_pcm_open; .....
snd_pcm_new(rtd->card->snd_card, new_name, num, playback, capture, &pcm); //具体值根据 codec_dai->driver 的设定
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, soc_pcm_ops); //soc_pcm_ops 有 ASOC 提供
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, soc_pcm_ops);
platform->driver->pcm_new(rtd); //******重点
preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); //申请 DMA 内存
size_t size = dma_hardware.buffer_bytes_max; //全局变量 dma_hardware
buf->area = dma_alloc_writecombine(pcm->card->dev, size, &buf->addr, GFP_KERNEL);
8. for 循环 card->num_aux_devs
9. snd_soc_dapm_link_dai_widgets(card); //sound/soc/soc-dapm.c
snd_soc_dapm_add_route(w->dapm, &r);
10. if (card->controls) snd_soc_add_card_controls(card, card->controls, card->num_controls);
snd_soc_add_controls();
snd_ctl_add(card, snd_soc_cnew(control, data, control->name, prefix));
11. if (card->dapm_routes) snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, card->num_dapm_routes);
12. snd_soc_dapm_new_widgets(&card->dapm);
13. snd_soc_dai_set_fmt(card->rtd[i].codec_dai, dai_link->dai_fmt); //设置 codec_dai 格式
snd_soc_dai_set_fmt(card->rtd[i].cpu_dai, dai_link->dai_fmt); //设置 cpu_dai 格式
dai->driver->ops->set_fmt(dai, fmt);
14. card->late_probe(card);
15. snd_soc_dapm_new_widgets(&card->dapm);
16. snd_card_register(card->snd_card);
17. snd_soc_dapm_sync(&card->dapm);
//codec 子模块相关 (sound/soc/codecs)
static struct snd_soc_dai_driver uda134x_dai = {
.name = "uda134x-hifi",
/* playback capabilities */
/* **********struct snd_soc_pcm_stream playback & capture */
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = UDA134X_RATES,
.formats = UDA134X_FORMATS,
},
/* capture capabilities */
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = UDA134X_RATES,
.formats = UDA134X_FORMATS,
},
/* pcm operations */
.ops = &uda134x_dai_ops,
};
snd_soc_register_codec(&pdev->dev, &soc_codec_dev_uda134x, &uda134x_dai, 1);
//涉及 codec 以及 codec_dai
int snd_soc_register_codec(struct device *dev, const struct snd_soc_codec_driver *codec_drv, struct snd_soc_dai_driver *dai_drv, int num_dai)
struct snd_soc_codec *codec; //处理 codec
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
设置 codec 的成员变量
if (codec_drv->reg_cache_size && codec_drv->reg_word_size)
codec->reg_def_copy = kmemdup(codec_drv->reg_cache_default, reg_size, GFP_KERNEL);
if (codec_drv->reg_access_size && codec_drv->reg_access_default)
fixup_codec_formats(&dai_drv[i].playback);
fixup_codec_formats(&dai_drv[i].capture);
snd_soc_register_dais(dev, dai_drv, num_dai);
struct snd_soc_dai *dai; //处理 codec_dai
dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
设置 dai 的成员变量,即 codec_dai
list_add(&dai->list, &dai_list);
snd_soc_instantiate_cards();
list_add(&codec->list, &codec_list);
snd_soc_instantiate_cards();
//cpu dai 子模块 "s3c24xx-iis" (sound/soc/samsung) ----- platform iis
static struct snd_soc_dai_driver s3c24xx_i2s_dai = {
.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,
};
snd_soc_register_dai(&pdev->dev, &s3c24xx_i2s_dai);
int snd_soc_register_dai(struct device *dev, struct snd_soc_dai_driver *dai_drv)
struct snd_soc_dai *dai;
dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
list_add(&dai->list, &dai_list);
snd_soc_instantiate_cards();
//platform dai 子模块 "samsung-audio" (sound/soc/samsung) ----- platform dma
static struct snd_soc_platform_driver samsung_asoc_platform = {
.ops = &dma_ops,
.pcm_new = dma_new,
.pcm_free = dma_free_dma_buffers,
};
snd_soc_register_platform(&pdev->dev, &samsung_asoc_platform);
int snd_soc_register_platform(struct device *dev, struct snd_soc_platform_driver *platform_drv)
struct snd_soc_platform *platform;
platform = kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL);
list_add(&platform->list, &platform_list);
snd_soc_instantiate_cards();
三. 音频数据流分析
frame_size ?????
sample_size 比如一个sample为左右声道两个采样点,一个采样点16位,则1个sample为4bytes
period_size 4096 1024个sample
buffer period_size * period_num
struct snd_pcm_ops {
int (*open)(struct snd_pcm_substream *substream);
int (*close)(struct snd_pcm_substream *substream);
int (*ioctl)(struct snd_pcm_substream * substream,
unsigned int cmd, void *arg);
int (*hw_params)(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params);
int (*hw_free)(struct snd_pcm_substream *substream);
int (*prepare)(struct snd_pcm_substream *substream);
int (*trigger)(struct snd_pcm_substream *substream, int cmd);
snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream);
int (*copy)(struct snd_pcm_substream *substream, int channel,
snd_pcm_uframes_t pos,
void __user *buf, snd_pcm_uframes_t count);
int (*silence)(struct snd_pcm_substream *substream, int channel,
snd_pcm_uframes_t pos, snd_pcm_uframes_t count);
struct page *(*page)(struct snd_pcm_substream *substream,
unsigned long offset);
int (*mmap)(struct snd_pcm_substream *substream, struct vm_area_struct *vma);
int (*ack)(struct snd_pcm_substream *substream);
};
录音过程:
static struct snd_pcm_ops alsa_model_pcm_ops = {
.open = alsa_model_pcm_open,
.close = alsa_model_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = alsa_model_hw_params,
.hw_free = alsa_model_hw_free,
.prepare = alsa_model_prepare,
.trigger = alsa_model_card_trigger,
.pointer = alsa_model_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
};
struct snd_pcm_runtime *runtime;
struct snd_pcm_substream *substream; 这两个结构体会在中断回调处理函数中使用
//用户态调用 open()函数时触发
int alsa_model_pcm_open(struct snd_pcm_substream *substream)
alsa_model_t *alsa_cxt = snd_pcm_substream_chip(substream); //获取设置时的私有数据
struct snd_pcm_runtime *runtime = substream->runtime; //pcm运行时结构体
runtime->hw = alsa_cxt->hw_parm; //struct snd_pcm_hardware hw_parm; (音频芯片的支持参数)
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); ?????
//用户态调用 close()函数时触发
int alsa_model_pcm_close(struct snd_pcm_substream *substream)
snd_pcm_lib_ioctl() //alsa 架构提供, 方便用户态通过 ioctl() 接口对codec进行配置.
//用户态进行codec属性配置 比如:申请虚拟地址空间, period 大小,以及 frame 大小, 供用户态使用runtime->dma_area
int alsa_model_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params)
snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); //申请虚拟地址空间, period 大小,以及 frame 大小
int alsa_model_hw_free(struct snd_pcm_substream *substream)
snd_pcm_lib_free_vmalloc_buffer(substream);
//启动采集操作时的准备工作,用户初始化一些私有数据
int alsa_model_prepare(struct snd_pcm_substream *substream)
//用户态通过 cmd 值使能或停止采集操作。基本是开启/关闭 dma 中断
int alsa_model_card_trigger(struct snd_pcm_substream *substream, int cmd)
//内核态调用该函数获得当前 hw_ptr 的值
snd_pcm_uframes_t alsa_model_pointer(struct snd_pcm_substream *substream)
spin_lock_irqsave(&alsa_cxt ->slock, flags);
hwptr = alsa_cxt ->hwptr;
spin_unlock_irqrestore(&alsa_cxt ->slock, flags);
.page = snd_pcm_lib_get_vmalloc_page,
s3c24xx-uda134x 过程:
ASOC 架构只使用如下的接口:
soc_pcm_ops->open = soc_pcm_open;
soc_pcm_ops->close = soc_pcm_close;
soc_pcm_ops->hw_params = soc_pcm_hw_params;
soc_pcm_ops->hw_free = soc_pcm_hw_free;
soc_pcm_ops->prepare = soc_pcm_prepare;
soc_pcm_ops->trigger = soc_pcm_trigger;
soc_pcm_ops->pointer = soc_pcm_pointer;
以上调用顺序:
一种情况:
alsa_model_pcm_open()
alsa_model_hw_params()
alsa_model_hw_free()
alsa_model_hw_params()
alsa_model_hw_free()
alsa_model_hw_params()
alsa_model_hw_free()
alsa_model_pcm_close()
另一种情况:
alsa_model_pcm_open()
alsa_model_hw_params()
alsa_model_prepare()
alsa_model_prepare()
alsa_model_card_trigger() //start
alsa_model_pointer()
alsa_model_pointer()
alsa_model_pointer()
alsa_model_pointer()
alsa_model_pointer()
.
.
alsa_model_card_trigger() //stop
alsa_model_hw_free()
alsa_model_pcm_close()
int soc_pcm_open(struct snd_pcm_substream *substream)
codec_dai->driver->ops->startup(substream, codec_dai); //codec_dai
cpu_dai->driver->ops->startup(substream, cpu_dai); //cpu_dai(platform)
platform->driver->ops->open(substream); //dma_open(platform)
dma_open(struct snd_pcm_substream *substream)
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
snd_soc_set_runtime_hwparams(substream, &dma_hardware); substream->hw (format, samplerate 等)
runtime->private_data = prtd; //设置 runtime 的私有数据, runtime 会在中断回调处理函数中使用
rtd->dai_link->ops->startup(substream); //mechine
//设置支持的参数
runtime->hw.rate_min = max(codec_dai_drv->capture.rate_min, cpu_dai_drv->capture.rate_min);
runtime->hw.channels_min = max(codec_dai_drv->capture.channels_min, cpu_dai_drv->capture.channels_min);
runtime->hw.formats = codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats;
runtime->hw.rates = codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates;
snd_pcm_limit_hw_rates(runtime);
//????? dai->driver->playback.sig_bits
soc_pcm_apply_msb(substream, codec_dai);
soc_pcm_apply_msb(substream, cpu_dai);
snd_pcm_hw_constraint_msbits();
/* Symmetry only applies if we've already got an active stream. */
snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_RATE, soc_dai->rate, soc_dai->rate); //?????
int soc_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
rtd->dai_link->ops->hw_params(substream, params); //machine
codec_dai->driver->ops->hw_params(substream, params, codec_dai); //codec_dai
cpu_dai->driver->ops->hw_params(substream, params, cpu_dai); //cpu_dai (platform)
platform->driver->ops->hw_params(substream, params); //dma (platform)
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
prtd->params->ch = prtd->params->ops->request(prtd->params->channel, &dma_info);
int soc_pcm_prepare(struct snd_pcm_substream *substream)
rtd->dai_link->ops->prepare(substream);
platform->driver->ops->prepare(substream);
codec_dai->driver->ops->prepare(substream, codec_dai);
cpu_dai->driver->ops->prepare(substream, cpu_dai);
snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
codec_dai->driver->ops->trigger(substream, cmd, codec_dai);
platform->driver->ops->trigger(substream, cmd);
cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai);
/**********************************************************************************************/
1. platform 相关
1.1 dma
static struct snd_pcm_ops dma_ops = {
.open = dma_open,
.close = dma_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = dma_hw_params,
.hw_free = dma_hw_free,
.prepare = dma_prepare,
.trigger = dma_trigger,
.pointer = dma_pointer,
.mmap = dma_mmap,
};
static struct snd_soc_platform_driver samsung_asoc_platform = {
.ops = &dma_ops,
.pcm_new = dma_new,
.pcm_free = dma_free_dma_buffers,
};
1.2 iis
static const struct snd_soc_dai_ops s3c24xx_i2s_dai_ops = {
.trigger = s3c24xx_i2s_trigger,
.hw_params = s3c24xx_i2s_hw_params,
.set_fmt = s3c24xx_i2s_set_fmt,
.set_clkdiv = s3c24xx_i2s_set_clkdiv,
.set_sysclk = s3c24xx_i2s_set_sysclk,
};
static struct snd_soc_dai_driver s3c24xx_i2s_dai = {
.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,
};
2. codec 相关
static struct snd_soc_codec_driver soc_codec_dev_uda134x = {
.probe = uda134x_soc_probe,
.remove = uda134x_soc_remove,
.suspend = uda134x_soc_suspend,
.resume = uda134x_soc_resume,
.reg_cache_size = sizeof(uda134x_reg),
.reg_word_size = sizeof(u8),
.reg_cache_default = uda134x_reg,
.reg_cache_step = 1,
.read = uda134x_read_reg_cache,
.write = uda134x_write,
.set_bias_level = uda134x_set_bias_level,
};
static const struct snd_soc_dai_ops uda134x_dai_ops = {
.startup = uda134x_startup,
.shutdown = uda134x_shutdown,
.hw_params = uda134x_hw_params,
.digital_mute = uda134x_mute,
.set_sysclk = uda134x_set_dai_sysclk,
.set_fmt = uda134x_set_dai_fmt,
};
static struct snd_soc_dai_driver uda134x_dai = {
.name = "uda134x-hifi",
/* playback capabilities */
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = UDA134X_RATES,
.formats = UDA134X_FORMATS,
},
/* capture capabilities */
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = UDA134X_RATES,
.formats = UDA134X_FORMATS,
},
/* pcm operations */
.ops = &uda134x_dai_ops,
};
3. machine 相关
static struct uda134x_platform_data s3c24xx_uda134x = {
.l3 = {
.setdat = setdat,
.setclk = setclk,
.setmode = setmode,
.data_hold = 1,
.data_setup = 1,
.clock_high = 1,
.mode_hold = 1,
.mode = 1,
.mode_setup = 1,
},
};
static struct snd_soc_ops s3c24xx_uda134x_ops = {
.startup = s3c24xx_uda134x_startup,
.shutdown = s3c24xx_uda134x_shutdown,
.hw_params = s3c24xx_uda134x_hw_params,
};
static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {
.name = "UDA134X",
.stream_name = "UDA134X",
.codec_name = "uda134x-codec",
.codec_dai_name = "uda134x-hifi",
.cpu_dai_name = "s3c24xx-iis",
.ops = &s3c24xx_uda134x_ops,
.platform_name = "samsung-audio",
};
static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
.name = "S3C24XX_UDA134X",
.owner = THIS_MODULE,
.dai_link = &s3c24xx_uda134x_dai_link,
.num_links = 1,
};
/**********************************************************************************************/
1. 创建架构以及生成节点
2. 初始化时
注册音频的中断
硬件设置
申请一块buffer,涉及dma。将映射的物理地址写到dma寄存器.首地址以及大小写入寄存器. 写dma寄存器只涉及一次
dma_pool_alloc(); //大小为aver311_cxt->audio_info.audio_buf_size,映射出物理地址和虚拟地址
3. 在中断处理函数
产生中断后,通知处理音频数据的tasklet任务
void board_alsa_recv_data(void *board_alas_cxt, unsigned char *buffer, U32_T size)
board_alsa_recv_data(aver311_cxt->audio_info.audio_cb_cxt, (U8_T *)aver311_cxt->audio_info.dma_buffer[index]->vaddr, aver311_cxt->audio_info.audio_buf_size);
中断到来后,dma寄存器有值,此时对应映射的虚拟地址也有数据,并处理
alsa_model_feed_data(board_alsa->alsa_handle, buffer, size);
{
substream = alsa_cxt->substream;
runtime = alsa_cxt->substream->runtime;
stride = runtime->frame_bits >> 3; //runtime->frame_bits = 32, stride = 4
oldptr = alsa_cxt->hwptr;
copy_frames = length / stride;
if (oldptr + copy_frames >= runtime->buffer_size)
{
cnt = runtime->buffer_size - oldptr;
memcpy(runtime->dma_area + oldptr * stride, buf, cnt * stride);
memcpy(runtime->dma_area, buf + cnt * stride, copy_frames * stride - cnt * stride);
}
else
{
//runtime->dma_area大小是buffer_size,在hw_param()函数里进行申请
memcpy(runtime->dma_area + oldptr * stride, buf, copy_frames * stride); //将音频数据拷贝到dma_area
}
snd_pcm_stream_lock(substream);
alsa_cxt->hwptr += copy_frames; //更新hwptr指针
if (alsa_cxt->hwptr >= runtime->buffer_size)
alsa_cxt->hwptr -= runtime->buffer_size;
alsa_cxt->capture_transfer_done += copy_frames; //capture_transfer_done记录处理的数据量有没有大于period_size
if (alsa_cxt->capture_transfer_done >= runtime->period_size)
{
alsa_cxt->capture_transfer_done -= runtime->period_size;
period_elapsed = true;
}
snd_pcm_stream_unlock(substream);
if (period_elapsed)
snd_pcm_period_elapsed(substream); //推送给用户空间
}
四. 音频控制分析
控制音量等
int uda134x_soc_probe(struct snd_soc_codec *codec)
uda134x_reset(codec);
snd_soc_add_codec_controls(codec, uda1340_snd_controls, ARRAY_SIZE(uda1340_snd_controls));
snd_soc_add_controls(card, codec->dev, controls, num_controls, codec->name_prefix, codec);
for (i = 0; i < num_controls; i++)
{
const struct snd_kcontrol_new *control = &controls[i];
err = snd_ctl_add(card, snd_soc_cnew(control, data, control->name, prefix));
}
五. 问题点 ?????
数据的传输过程
control
DAPM (动态音频电源管理)
调试方式
用户态 tinyalsa, alsa-lib, android audio framework
snd_pcm_period_elapsed() 具体作用?
pointer通常在buffer-update 过程中调用,由中断函数中的snd_pcm_period_elapsed触发。即每次硬件中断,就会调用snd_pcm_period_elapsed函数来通知alsa-core来读取当前的hardware position,计算buffer中空余空间,唤醒sleep的polling thread.
1. 未播放视频时为何有音频数据?
2. 为何需要多个period, 只使用1个?
3. runtime->dma_area 作用?
4. 杂音?
6. 音频卡顿? alsa_cxt->hwptr 数据处理问题
正常时声音间隔 0.04s
卡顿时声音间隔 0.06s
*************
snd_pcm_lib_malloc_pages() 返回-22,error 替换 snd_pcm_lib_alloc_vmalloc_buffer() 返回0,ok?
解决方法:必须设置申请内存类型. 比如 SNDRV_DMA_TYPE_DEV
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 64*1024, 64*1024);
snd_pcm_lib_preallocate_pages();
cn311h:
//设置 struct snd_pcm_runtime 结构体
.open = aaci_pcm_open,
struct snd_pcm_runtime *runtime = substream->runtime;
runtime->private_data = aacirun;
runtime->hw = aaci_hw_info;
/* enable DMA bits */
.close = aaci_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
//申请dmabuffer, buffer_size
.hw_params = aaci_pcm_hw_params,
runtime->dma_buffer_p = bufp;
runtime->dma_area = bufp->area;
runtime->dma_addr = bufp->addr;
runtime->dma_bytes = bufp->bytes;
//snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params));
runtime->dma_area = __vmalloc(size, gfp_flags, PAGE_KERNEL);
runtime->dma_bytes = size;
snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
.hw_free = aaci_pcm_hw_free,
//设置私有变量的值
.prepare = aaci_pcm_capture_prepare,
.trigger = aaci_pcm_capture_trigger,
.pointer = aaci_pcm_pointer,