/* machine tiny_wm8960.c */
platform_set_drvdata(&asoc_dev, &myalsa_card); //设置平台设备的私有数据
static struct snd_soc_card myalsa_card = {
.name = "S3C2440_UDA1341",
.owner = THIS_MODULE,
.dai_link = &s3c2440_uda1341_dai_link,
static struct snd_soc_dai_link s3c2440_uda1341_dai_link = {
.name = "100ask_UDA1341",
.stream_name = "100ask_UDA1341",
.codec_name = "wm8960-codec.0-001a", // codec
.codec_dai_name = "wm8960-hifi", //codec dai
.cpu_dai_name = "samsung-i2s.0", //cpu dai
.ops = &s3c2440_uda1341_ops, //??
static struct snd_soc_ops s3c2440_uda1341_ops = {
.hw_params = smdk_hw_params,
};
.platform_name = "samsung-audio", //dma
.init = tiny4412_wm8960_machine_init, //??
};
.num_links = 1,
};
platform_device_register(&asoc_dev); //注册平台设备
static struct platform_device asoc_dev = {
.name = "soc-audio", //这名字是固定的,对应的平台驱动会被调用
.id = -1,
.dev = {
.release = asoc_release,
},
};
/* codec : wm8960.c */
ret = snd_soc_register_codec(&i2c->dev,&soc_codec_dev_wm8960, &wm8960_dai, 1);
static struct snd_soc_codec_driver soc_codec_dev_wm8960 = {
.probe = wm8960_probe, //根据machine的dai_link的codec_name匹配,匹配成功调用到此
.remove = wm8960_remove,
.suspend = wm8960_suspend,
.resume = wm8960_resume,
.set_bias_level = wm8960_set_bias_level,
.reg_cache_size = ARRAY_SIZE(wm8960_reg),
.reg_word_size = sizeof(u16),
.reg_cache_default = wm8960_reg,
};
/* codec dai : wm8960.c */
ret = snd_soc_register_codec(&i2c->dev,&soc_codec_dev_wm8960, &wm8960_dai, 1);
static struct snd_soc_dai_driver wm8960_dai = {
.name = "wm8960-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 2,
.rates = WM8960_RATES,
.formats = WM8960_FORMATS,},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM8960_RATES,
.formats = WM8960_FORMATS,},
.ops = &wm8960_dai_ops,
static struct snd_soc_dai_ops wm8960_dai_ops = {
.hw_params = wm8960_hw_params,
.digital_mute = wm8960_mute,
.set_fmt = wm8960_set_dai_fmt,
.set_clkdiv = wm8960_set_dai_clkdiv,
.set_pll = wm8960_set_dai_pll,
};
.symmetric_rates = 1,
};
/* cpu dai : i2s.c*/
pri_dai = i2s_alloc_dai(pdev, false);
if (!pri_dai) {
dev_err(&pdev->dev, "Unable to alloc I2S_pri\n");
ret = -ENOMEM;
goto err;
}
pri_dai->dma_playback.dma_addr = regs_base + I2STXD;
pri_dai->dma_capture.dma_addr = regs_base + I2SRXD;
pri_dai->dma_playback.client =
(struct s3c2410_dma_client *)&pri_dai->dma_playback;
pri_dai->dma_capture.client =
(struct s3c2410_dma_client *)&pri_dai->dma_capture;
pri_dai->dma_playback.channel = dma_pl_chan;
pri_dai->dma_capture.channel = dma_cp_chan;
pri_dai->src_clk = i2s_cfg->src_clk;
pri_dai->dma_playback.dma_size = 4;
pri_dai->dma_capture.dma_size = 4;
pri_dai->base = regs_base;
pri_dai->quirks = quirks;
if (pdev->id == 0) {
pri_dai->audss_clk_enable = audss_clk_enable;
pri_dai->audss_suspend = audss_suspend;
pri_dai->audss_resume = audss_resume;
}
if (quirks & QUIRK_PRI_6CHAN)
pri_dai->i2s_dai_drv.playback.channels_max = 6;
if (quirks & QUIRK_SEC_DAI) {
sec_dai = i2s_alloc_dai(pdev, true);
if (!sec_dai) {
dev_err(&pdev->dev, "Unable to alloc I2S_sec\n");
ret = -ENOMEM;
goto err;
}
sec_dai->dma_playback.dma_addr = regs_base + I2STXDS;
sec_dai->dma_playback.client =
(struct s3c2410_dma_client *)&sec_dai->dma_playback;
/* Use iDMA always if SysDMA not provided */
sec_dai->dma_playback.channel = dma_pl_sec_chan ? : -1;
sec_dai->src_clk = i2s_cfg->src_clk;
sec_dai->dma_playback.dma_size = 4;
sec_dai->base = regs_base;
sec_dai->quirks = quirks;
sec_dai->pri_dai = pri_dai;
pri_dai->sec_dai = sec_dai;
if (pdev->id == 0) {
sec_dai->audss_clk_enable = audss_clk_enable;
sec_dai->audss_suspend = audss_suspend;
sec_dai->audss_resume = audss_resume;
}
}
snd_soc_register_dai(&pri_dai->pdev->dev, &pri_dai->i2s_dai_drv);
/* dma : dma.c */
snd_soc_register_platform(&pdev->dev, &samsung_asoc_platform);
static struct snd_soc_platform_driver samsung_asoc_platform = {
.ops = &dma_ops,
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,
};
.pcm_new = dma_new,
.pcm_free = dma_free_dma_buffers,
};
/**************************************************************************************************************/
/* machine */
/* 注册平台设备后对应的平台驱动的probe会被调用 */
static int soc_probe(struct platform_device *pdev)
ret = snd_soc_register_card(card);
snd_soc_initialize_card_lists(card);
card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) *(card->num_links + card->num_aux_devs),GFP_KERNEL);
INIT_LIST_HEAD(&card->list);
list_add(&card->list, &card_list);
snd_soc_instantiate_cards();
snd_soc_instantiate_card(card);
soc_bind_dai_link(card, i); //绑定cpudai,codec,codec dai,platform
/* 1.find CPU DAI */
rtd->cpu_dai = cpu_dai; //&pri_dai->i2s_dai_drv
/* 2.find_codec */
rtd->codec = codec; //soc_codec_dev_wm8960
/* 3.find CODEC DAI */
rtd->codec_dai = codec_dai; //wm8960_dai
/* 4.find_platform */
rtd->platform = platform; //samsung_asoc_platform
/* initialize the register cache for each available codec */
ret = snd_soc_init_codec_cache(codec, compress_type);
/* 创建snd_card */
ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,card->owner, 0, &card->snd_card);
/* 注册widget */
if (card->dapm_widgets)
snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,card->num_dapm_widgets);
/* 调用cpu_dai,codec,codec_dai,platform probe函数 */
ret = soc_probe_dai_link(card, i, order) {
/* probe the cpu_dai */
snd_soc_dapm_new_dai_widgets(&cpu_dai->dapm, cpu_dai); //注册cpu_dai的widget
ret = cpu_dai->driver->probe(cpu_dai); //如果有probe函数调用probe函数
/* mark cpu_dai as probed and add to card dai list */
list_add(&cpu_dai->card_list, &card->dai_dev_list);
/* probe the CODEC */
ret = soc_probe_codec(card, codec);
snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets,driver->num_dapm_widgets); //如果codec的dapm_widgets不为空,注册它们
snd_soc_dapm_new_dai_widgets(&codec->dapm, dai); //在dai_list找到对应codec的codec_dai,注册codec_dai的widget,??问什么codec_dai的widget会在这里注册??
ret = driver->probe(codec); //probe函数不为空,调用它,就是wm8960_probe
snd_soc_add_codec_controls(codec, driver->controls,driver->num_controls); //controls不为空,注册它
snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes, driver->num_dapm_routes); //route不为空,注册它
list_add(&codec->card_list, &card->codec_dev_list);
list_add(&codec->dapm.list, &card->dapm_list);
/* probe the platform */
ret = soc_probe_platform(card, platform);
snd_soc_dapm_new_controls(&platform->dapm,driver->dapm_widgets, driver->num_dapm_widgets); //如果platform的dapm_widgets不为空,注册它们
snd_soc_dapm_new_dai_widgets(&platform->dapm, dai); //
ret = driver->probe(platform); //probe函数不为空,调用它,这里为NULL
snd_soc_add_platform_controls(platform, driver->controls,driver->num_controls); //controls不为空,注册它
snd_soc_dapm_add_routes(&platform->dapm, driver->dapm_routes,driver->num_dapm_routes);//route不为空,注册它
list_add(&platform->card_list, &card->platform_dev_list);
list_add(&platform->dapm.list, &card->dapm_list);
/* probe the CODEC DAI */
ret = codec_dai->driver->probe(codec_dai); //如果有probe函数调用probe函数
list_add(&codec_dai->card_list, &card->dai_dev_list);
ret = soc_post_component_init(card, codec, num, 0);
snd_soc_dapm_new_widgets(&codec->dapm); //遍历card的widgets链表,取出kcontrol_new重新创建kcontrol,注册到card的controls链表
ret = device_create_file(rtd->dev, &dev_attr_pmdown_time);
ret = soc_new_pcm(rtd, num);{
ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback,capture, &pcm);
rtd->ops.open = soc_pcm_open;
rtd->ops.hw_params = soc_pcm_hw_params;
rtd->ops.prepare = soc_pcm_prepare;
rtd->ops.trigger = soc_pcm_trigger;
rtd->ops.hw_free = soc_pcm_hw_free;
rtd->ops.close = soc_pcm_close;
rtd->ops.pointer = soc_pcm_pointer;
rtd->ops.ioctl = soc_pcm_ioctl;
/* 将ops复制给substream */
if (playback)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);
if (capture)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);
}
ret = snd_soc_dapm_new_pcm(card, dai_link->params,capture_w, play_w);
ret = snd_soc_dapm_new_pcm(card, dai_link->params,capture_w, play_w);
}
snd_soc_dapm_link_dai_widgets(card); //连接card中的dai widget和stream widget
if (dai->driver->playback.stream_name &&strstr(w->sname,dai->driver->playback.stream_name)) {
r.source = dai->playback_widget->name; //源
r.sink = w->name; //目的
snd_soc_dapm_add_route(w->dapm, &r); //添加路线
}
if (dai->driver->capture.stream_name && strstr(w->sname,dai->driver->capture.stream_name)) {
r.source = w->name; //源
r.sink = dai->capture_widget->name; //目的
snd_soc_dapm_add_route(w->dapm, &r); //添加路线
}
snd_soc_add_card_controls(card, card->controls, card->num_controls);
snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,card->num_dapm_routes);
snd_soc_dapm_new_widgets(&card->dapm){ //创建dapm control ??这里为什么会有两个snd_soc_dapm_new_widgets,难道codec->dapm->card不等于card->dapm->card吗??
/* 分配内存 */
w->kcontrols = kzalloc(w->num_kcontrols *sizeof(struct snd_kcontrol *),GFP_KERNEL);
/* 创建并初始化dapm control */
switch(w->id) {
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
dapm_new_mixer(w);{
}
break;
case snd_soc_dapm_mux:
case snd_soc_dapm_virt_mux:
case snd_soc_dapm_value_mux:
dapm_new_mux(w);
break;
case snd_soc_dapm_pga:
case snd_soc_dapm_out_drv:
dapm_new_pga(w);
break;
default:
break;
/* 根据widget寄存器是当前值,初始化widget的电源状态 */
/* 将widget加入到dapm_dirty链表,表示widget发生了变化 */
dapm_mark_dirty(w, "new widget"); //linux 3.0.86没有此函数
/* 统一处理dapm_dirty链表 */
dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
}
}
/* codec , codec dai */
snd_soc_register_codec(&i2c->dev,&soc_codec_dev_wm8960, &wm8960_dai, 1);
struct snd_soc_codec *codec;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); //分配一个codec
codec->name = fmt_single_name(dev, &codec->id); //codec 的名字用于匹配,[平台总线名字].[第几组i2c]-[i2c地址] , 如:wm8960-codec.0-001a
/* 将副本codec(soc_codec_dev_wm8960)赋值给刚分配的codec */
codec->write = codec_drv->write;
codec->read = codec_drv->read;
codec->volatile_register = codec_drv->volatile_register;
codec->readable_register = codec_drv->readable_register;
codec->writable_register = codec_drv->writable_register;
codec->ignore_pmdown_time = codec_drv->ignore_pmdown_time;
codec->dapm.bias_level = SND_SOC_BIAS_OFF;
codec->dapm.dev = dev;
codec->dapm.codec = codec;
codec->dapm.seq_notifier = codec_drv->seq_notifier;
codec->dapm.stream_event = codec_drv->stream_event;
codec->dev = dev;
codec->driver = codec_drv;
codec->num_dai = num_dai;
/* 将codec添加到codec->list */
list_add(&codec->list, &codec_list);
/* 注册codec dai */
ret = snd_soc_register_dais(dev, dai_drv, num_dai);
dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL); //分配dai
dai->name = fmt_multiple_name(dev, &dai_drv[i]); // wm8960-hifi
/* codec匹配成功,调用codec的probe函数 */
static int wm8960_probe(struct snd_soc_codec *codec)
snd_soc_add_controls(codec, wm8960_snd_controls,ARRAY_SIZE(wm8960_snd_controls)); //注册kcontrol
wm8960_add_widgets(codec);
snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets,ARRAY_SIZE(wm8960_dapm_widgets)); //注册codec的widget
for (i = 0; i < num; i++) { //为每个widget重新创建,注册
w = snd_soc_dapm_new_control(dapm, widget);
w = dapm_cnew_widget(widget)) //为widget重新分配内存
/* 根据widget类型,设置不同的power_check函数 */
switch (w->id) {
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_mux:
case snd_soc_dapm_virt_mux:
case snd_soc_dapm_value_mux:
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_adc:
case snd_soc_dapm_aif_out:
w->power_check = dapm_adc_check_power;
break;
case snd_soc_dapm_dac:
case snd_soc_dapm_aif_in:
w->power_check = dapm_dac_check_power;
break;
case snd_soc_dapm_pga:
case snd_soc_dapm_out_drv:
case snd_soc_dapm_input:
case snd_soc_dapm_output:
case snd_soc_dapm_micbias:
case snd_soc_dapm_spk:
case snd_soc_dapm_hp:
case snd_soc_dapm_mic:
case snd_soc_dapm_line:
case snd_soc_dapm_dai_link:
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
w->power_check = dapm_supply_check_power;
break;
case snd_soc_dapm_dai:
w->power_check = dapm_dai_check_power;
break;
default:
w->power_check = dapm_always_on_check_power;
break;
}
/* 初始化链表 */
INIT_LIST_HEAD(&w->sources); //所有widget输入端的snd_soc_path
INIT_LIST_HEAD(&w->sinks); //所有widget输出端的snd_soc_path
INIT_LIST_HEAD(&w->list); //链接到声卡的widgets链表
INIT_LIST_HEAD(&w->dirty); //链接到声卡的dapm_dirty链表
//将widget加入到snd_soc_card链表
list_add(&w->list, &dapm->card->widgets);
//设置connect状态
w->connected = 1;
snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths)); //注册codec的route
====================================================================================================================================
片段
/* 创建设备节点:control */
snd_card_create
snd_ctl_create(card);
snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
/*会掉到*/
snd_ctl_dev_register(struct snd_device *device)
sprintf(name, "controlC%i", cardnum); //设备名
snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,&snd_ctl_f_ops, card, name))
snd_register_device_for_dev(type, card, dev, f_ops,private_data, name,snd_card_get_device_link(card));
snd_minors[minor] = preg;
preg->dev = device_create(sound_class, device, MKDEV(major, minor),private_data, "%s", name); //创建设备节点
/* fops */
static const struct file_operations snd_ctl_f_ops =
{
.owner = THIS_MODULE,
.read = snd_ctl_read,
.open = snd_ctl_open,
.release = snd_ctl_release,
.llseek = no_llseek,
.poll = snd_ctl_poll,
.unlocked_ioctl = snd_ctl_ioctl,{
switch (cmd) {
case SNDRV_CTL_IOCTL_ELEM_READ:
return snd_ctl_elem_read_user(card, argp) {
result = snd_ctl_elem_read(card, control);
kctl = snd_ctl_find_id(card, &control->id); //找到对应的kcontrol
result = kctl->get(kctl, control); //调用get函数
}
case SNDRV_CTL_IOCTL_ELEM_WRITE:
return snd_ctl_elem_write_user(ctl, argp){
result = snd_ctl_elem_write(card, file, control);
kctl = snd_ctl_find_id(card, &control->id); //找到对应的kcontrol
result = kctl->put(kctl, control); //调用put函数
}
}
}
.compat_ioctl = snd_ctl_ioctl_compat,
.fasync = snd_ctl_fasync,
};
/* 创建设备节点:pcm */
int snd_pcm_new(struct snd_card *card, const char *id, int device,int playback_count, int capture_count, struct snd_pcm **rpcm)
_snd_pcm_new(card, id, device, playback_count, capture_count,false, rpcm);
/*会掉到*/
static int snd_pcm_dev_register(struct snd_device *device)
sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);
devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
或
sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);
devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
err = snd_register_device_for_dev(devtype, pcm->card,pcm->device,&snd_pcm_f_ops[cidx],pcm, str, dev);
snd_minors[minor] = preg;
preg->dev = device_create(sound_class, device, MKDEV(major, minor),private_data, "%s", name); //创建pcmC0D0p 或 pcmC0D0
/* fops */
const struct file_operations snd_pcm_f_ops[2] = {
{
.owner = THIS_MODULE,
.write = snd_pcm_write,
.aio_write = snd_pcm_aio_write,
.open = snd_pcm_playback_open,{
struct snd_pcm *pcm;
pcm = snd_lookup_minor_data(iminor(inode),SNDRV_DEVICE_TYPE_PCM_PLAYBACK); //在snd_minors数组中根据次设备号找到对应的snd_minor,这个snd_minor的私有数据就是snd_pcm对象
err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
err = snd_pcm_open_file(file, pcm, stream); //stream=SNDRV_PCM_STREAM_PLAYBACK
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream;
err = snd_pcm_open_substream(pcm, stream, file, &substream); //根据stream获取对应的snd_pcm_substream
err = snd_pcm_attach_substream(pcm, stream, file, &substream); //找到substream
err = snd_pcm_hw_constraints_init(substream);
err = substream->ops->open(substream) //substream->ops : snd_pcm_ops结构体
static int soc_pcm_open(struct snd_pcm_substream *substream)
ret = cpu_dai->driver->ops->startup(substream, cpu_dai); //cpu dai的startup
ret = platform->driver->ops->open(substream); //dma的open
ret = codec_dai->driver->ops->startup(substream, codec_dai); //codec dai的startup
ret = rtd->dai_link->ops->startup(substream); //machine的startup
/* 检查codec dai与cpu dai是参数否完全相同,比如采样频率,通道数 */
/* 设置格式和采样率 */
err = snd_pcm_hw_constraints_complete(substream);
pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);
pcm_file->substream = substream;
file->private_data = pcm_file; //这样就可以通过file的私有数据找到snd_pcm_substream
}
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_playback_poll,
.unlocked_ioctl = snd_pcm_playback_ioctl,{
return snd_pcm_playback_ioctl1(file, pcm_file->substream, cmd,(void __user *)arg);
case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);
snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock,snd_pcm_lib_write_transfer);
err = transfer(substream, appl_ofs, data, offset, frames); //就是snd_pcm_lib_write_transfer函数
copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))
err = snd_pcm_start(substream); //启动传输
return
snd_pcm_common_ioctl1(file, substream, cmd, arg);
case SNDRV_PCM_IOCTL_HW_PARAMS:
return snd_pcm_hw_params_user(substream, arg);
err = snd_pcm_hw_params(substream, params);
err = substream->ops->hw_params(substream, params);
static int soc_pcm_hw_params(struct snd_pcm_substream *substream,struct snd_pcm_hw_params *params)
ret = rtd->dai_link->ops->hw_params(substream, params); // machine的hw_params回调函数
ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai); // codec_dai的hw_params回调函数
ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai); // cpu_dai的hw_params回调函数
ret = platform->driver->ops->hw_params(substream, params); // platform的hw_params回调函数
cpu_dai->rate = params_rate(params); //保存
codec_dai->rate = params_rate(params); //保存
copy_to_user(_params, params, sizeof(*params)) //给用户层
}
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
},
{
.owner = THIS_MODULE,
.read = snd_pcm_read,
.aio_read = snd_pcm_aio_read,
.open = snd_pcm_capture_open,{
pcm = snd_lookup_minor_data(iminor(inode),SNDRV_DEVICE_TYPE_PCM_CAPTURE);
err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE); //同上
}
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_capture_poll,
.unlocked_ioctl = snd_pcm_capture_ioctl,{
snd_pcm_capture_ioctl1(file, pcm_file->substream, cmd,(void __user *)arg);
case SNDRV_PCM_IOCTL_READI_FRAMES:
result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames);
snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer);
case SNDRV_PCM_STATE_PREPARED:
err = snd_pcm_start(substream); //开始传输
err = transfer(substream, appl_ofs, data, offset, frames); //就是snd_pcm_lib_read_transfer
copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)) //数据返回给用户层
return
snd_pcm_common_ioctl1(file, substream, cmd, arg);
}
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
}
};
/* 注册kontrol */
static const struct snd_kcontrol_new wm8960_snd_controls[] = {
SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
0, 63, 0, adc_tlv),
SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL,
6, 1, 0),
...
};
snd_soc_add_codec_controls(codec, wm8960_snd_controls,ARRAY_SIZE(wm8960_snd_controls));
snd_soc_add_controls(card, codec->dev, controls, num_controls,codec->name_prefix, codec);
for (i = 0; i < num_controls; i++) { //for循环注册每一个control
const struct snd_kcontrol_new *control = &controls[i];
err = snd_ctl_add(card, snd_soc_cnew(control, data,control->name, prefix)); //将kcontrol注册到card的controls链表中,data就是codec,所有kcontrol的private_data=codec
//snd_soc_cnew(control, data,control->name, prefix) //将kcontrol_new转换成kcontrol
}
/* widget */
/* route ==> path */
static const struct snd_soc_dapm_route audio_paths[] = {
{ "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" },
{ "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" },
{ "Left Boost Mixer", "LINPUT3 Switch", "LINPUT3" },
...
};
snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths));
for (i = 0; i < num; i++) { //num=ARRAY_SIZE(audio_paths)
ret = snd_soc_dapm_add_route(dapm, route); //
list_for_each_entry(w, &dapm->card->widgets, list) { //遍历card的widgets链表
if (!wsink && !(strcmp(w->name, sink))) { //比较sink
wtsink = w;
if (w->dapm == dapm) //比较dapm
wsink = w;
continue;
}
if (!wsource && !(strcmp(w->name, source))) { //比较souce
wtsource = w;
if (w->dapm == dapm) //比较dapm
wsource = w;
}
}
/* 如果dapm不相同,使用wtsink,wtsource */
if (!wsink)
wsink = wtsink;
if (!wsource)
wsource = wtsource;
path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL); //分配一个snd_soc_dapm_path
/* 连接静态path:就是route的kcontrol为NULL */
if (control == NULL) {
list_add(&path->list, &dapm->card->paths); //将path加入到card的path链表
list_add(&path->list_sink, &wsink->sources); //path输出就是widget的输入
list_add(&path->list_source, &wsource->sinks); //path输入就是widget的输出
path->connect = 1; //设置path为已连接状态
return 0;
}
/* 连接动态path:就是route的kcontrol不为NULL */
switch (wsink->id) {
case snd_soc_dapm_adc:
case snd_soc_dapm_dac:
case snd_soc_dapm_pga:
case snd_soc_dapm_out_drv:
case snd_soc_dapm_input:
case snd_soc_dapm_output:
case snd_soc_dapm_siggen:
case snd_soc_dapm_micbias:
case snd_soc_dapm_vmid:
case snd_soc_dapm_pre:
case snd_soc_dapm_post:
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
case snd_soc_dapm_aif_in:
case snd_soc_dapm_aif_out:
case snd_soc_dapm_dai:
case snd_soc_dapm_dai_link:
list_add(&path->list, &dapm->card->paths);
list_add(&path->list_sink, &wsink->sources);
list_add(&path->list_source, &wsource->sinks);
path->connect = 1;
return 0;
case snd_soc_dapm_mux:
case snd_soc_dapm_virt_mux:
case snd_soc_dapm_value_mux:
ret = dapm_connect_mux(dapm, wsource, wsink, path, control,&wsink->kcontrol_news[0]); //连接mux根据寄存器值,设置connect状态
if (ret != 0)
goto err;
break;
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
ret = dapm_connect_mixer(dapm, wsource, wsink, path, control); //连接mix根据寄存器值,设置connect状态
if (ret != 0)
goto err;
break;
case snd_soc_dapm_hp:
case snd_soc_dapm_mic:
case snd_soc_dapm_line:
case snd_soc_dapm_spk:
list_add(&path->list, &dapm->card->paths);
list_add(&path->list_sink, &wsink->sources);
list_add(&path->list_source, &wsource->sinks);
path->connect = 0;
return 0;
}
/* dapm kcontrol */
int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) //遍历card的widgets链表,取出kcontrol_new重新创建kcontrol,注册到card的controls链表
list_for_each_entry(w, &dapm->card->widgets, list) //遍历card的widgets链表
w->kcontrols = kzalloc(w->num_kcontrols *sizeof(struct snd_kcontrol *),GFP_KERNEL); //给widget的kcontrols分配内存
/* 创建dapm kcontrol */
switch(w->id) {
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
dapm_new_mixer(w); //创建mix类的dapm kcontrol
for (i = 0; i < w->num_kcontrols; i++) { //for循环widget的kcontrol_new数组
list_for_each_entry(path, &w->sources, list_sink) { //遍历widget的输入path
if (path->name != (char *)w->kcontrol_news[i].name) //path->name 与kcontrol_news的name比较,意思是说:如果没path使用kcontrol_new就没必要创建dapm kcontrol了
continue;
if (w->kcontrols[i]) {
path->kcontrol = w->kcontrols[i]; //如果dapm kcontrol已经有了,就没必要继续了,直接给path->kcontrol
continue;
}
path->long_name = kmalloc(name_len, GFP_KERNEL); //给dapm kcontrol的name分配空间
/* 给dapm kcontrol命名 */
switch (w->id) {
default:
snprintf((char *)path->long_name, name_len,"%s %s", w->name + prefix_len,w->kcontrol_news[i].name); //widget的名字+kcontrol_new的名字
break;
case snd_soc_dapm_mixer_named_ctl:
snprintf((char *)path->long_name, name_len,"%s", w->kcontrol_news[i].name);
break;
}
path->kcontrol = snd_soc_cnew(&w->kcontrol_news[i],wlist, path->long_name,prefix); //将kcontrol_new转换成dapm kcontrol
ret = snd_ctl_add(card, path->kcontrol); //将dapm kcontrol添加到card的controls链表
w->kcontrols[i] = path->kcontrol; //把dapm kcontrol的指针给w->kcontrols[i],(path->kcontrol是个指针)
}
}
break;
case snd_soc_dapm_mux:
case snd_soc_dapm_virt_mux:
case snd_soc_dapm_value_mux:
dapm_new_mux(w); //创建mux的dapm kcontrol
shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[0],&kcontrol); //????
kcontrol = snd_soc_cnew(&w->kcontrol_news[0], wlist,name + prefix_len, prefix); //mux只有一个kcontrol,将kcontrol_new转换成dapm kcontrol
ret = snd_ctl_add(card, kcontrol); //将dapm kcontrol添加到card的controls链表
w->kcontrols[0] = kcontrol; //mux只有一个kcontrol,保存它
list_for_each_entry(path, &w->sources, list_sink) //设置所有path的kcontrol
path->kcontrol = kcontrol;
break;
case snd_soc_dapm_pga:
case snd_soc_dapm_out_drv:
dapm_new_pga(w);
break;
default:
break;
}
dapm_mark_dirty(w, "new widget"); //加入dirty链表
dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); //统一处理
/* widget上电循序 */
static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
list_for_each_entry(w, &card->dapm_dirty, dirty) { //遍历card的dapm_dirty链表
dapm_power_one_widget(w, &up_list, &down_list); //按照一定的顺序, 把需要上电的widget放到up_list链表,把需要下电的widget放到down_list
ret = dapm_widget_power_check(w); //调用每个widget的power_check函数
dapm_widget_set_power(w, power, up_list, down_list); //按照一定循序放到up_list或down_list
}
dapm_seq_run(dapm, &down_list, event, false); { //先下电
list_for_each_entry_safe(w, n, list, power_list) { //遍历list(即up_list或down_list),根据成员power_list找到挂到list上的每一个widget
list_move(&w->power_list, &pending); //加入到pending链表
}
if (!list_empty(&pending))
dapm_seq_run_coalesced(cur_dapm, &pending); //统一处理widget的电源
list_for_each_entry(w, pending, power_list) { //遍历pending链表,通过power_list成员找到widget
if (w->power != power)return;
dapm_seq_check_event(dapm, w, SND_SOC_DAPM_PRE_PMU);
ret = w->event(w, NULL, event); //调用w的event函数,event就是SND_SOC_DAPM_PRE_PMU
dapm_seq_check_event(dapm, w, SND_SOC_DAPM_PRE_PMD);
ret = w->event(w, NULL, event); //调用w的event函数,event就是SND_SOC_DAPM_PRE_PMD
}
pop_wait(card->pop_time); //等待一下
snd_soc_update_bits(dapm->codec, reg, mask, value); //寄存器操作,改变电源
list_for_each_entry(w, pending, power_list) {
if (w->power != power)return;
dapm_seq_check_event(dapm, w, SND_SOC_DAPM_POST_PMU);
ret = w->event(w, NULL, event); //调用w的event函数,event就是SND_SOC_DAPM_POST_PMU
dapm_seq_check_event(dapm, w, SND_SOC_DAPM_POST_PMD);
ret = w->event(w, NULL, event); //调用w的event函数,event就是SND_SOC_DAPM_POST_PMD
}
}
dapm_widget_update(dapm){ //更新dapm kcontrol
ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG); //发出SND_SOC_DAPM_PRE_REG事件
ret = snd_soc_update_bits(w->codec, update->reg, update->mask,update->val); //更新dapm kcontrol电源
ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG); //发出SND_SOC_DAPM_POST_REG
}
dapm_seq_run(dapm, &up_list, event, true); //再上电,同上
list_for_each_entry(d, &card->dapm_list, list)
d->stream_event(d, event); //linux3.0.86没有这个
pop_wait(card->pop_time); //等待一下
static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
in = is_connected_input_ep(w, NULL);
dapm_clear_walk_input(w->dapm, &w->sources);
out = is_connected_output_ep(w, NULL);
dapm_clear_walk_output(w->dapm, &w->sinks);