linux asoc和alsa驱动以及数据流分析

xiaoxiao2021-02-28  42

一. 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,

 

转载请注明原文地址: https://www.6miu.com/read-2632459.html

最新回复(0)