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

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


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值