linux ALSA & ASOC(1)—— framwork

一、ALSA framwork


1.涉及文件、函数

sound/core/sound.c        snd_register_device_for_dev            创建次设备
sound/core/init.c           snd_card_create                         创建contorl设备
sound/core/pcm.c            snd_pcm_new                             创建 pcm 设备

snd_card_create 和 snd_pcm_new 通过调用 snd_register_device_for_dev 函数创建次设备

主要思路:通过次设备号获取新的fops对不同次设备进行操作


2.框架图

这里写图片描述
☆ SNDRV_CTL_IOCTL_ELEM_WRITE ☆ // 调用put回调,设置音量值
SNDRV_CTL_IOCTL_ELEM_READ // 调用get回调,获取音量值
SNDRV_CTL_IOCTL_ELEM_INFO // 调用info回调



二、ASOC framwork


1.重要文件、函数、结构

涉及文件

 sound\sound_core.c
 ......
 sound\soc\mxs\mxs-sgtl5000.c
 sound\soc\codecs\ sgtl5000.c
 ......
 sound\soc\msm\msm8x10.c
 sound\soc\codecs\msm8x10-wcd.c
 ......

涉及函数

snd_soc_register_card
snd_soc_register_codec
snd_soc_register_dai
snd_soc_register_platform

重要结构

 (card)       snd_soc_card                                                           
 (dai_link)   snd_soc_dai_link  (snd_soc_card->dai_link)        
 (codec )     snd_soc_codec_driver                                          
 (dai)        snd_soc_dai_driver                                             
 (dma)        snd_soc_platform_driver       



----------
cat /sys/class/sound/card0/id  的字符串和 snd_soc_card->name 名字相同



2.ASOC组成

—— ASOC 就是对 ALSA 驱动框架的进一步封装,驱动框架分为了三个部分


2.1 machine

 指定了 cpu 端的DAI、 codec端的 DAI、 板子的platform名字 、 codec名字 (snd_soc_card->dai_link 结构中指定)

snd_soc_register_card(struct snd_soc_card *card)     // 注册一个card   

通常有两种方法注册一个card

—— 在machine驱动中注册一个名为 “soc-audio” 的 platform device,并将 struct snd_soc_card 结构变量作为这个device的私有数据,在 soc-core.c 调用 名为 “soc-audio” 的 platform drv的probe函数,probe中调用 snd_soc_register_card 注册这个card

—— 直接在machine驱动中调用 snd_soc_register_card 函数注册card

☆ snd_soc_register_card 函数解析

1.snd_soc_register_card 
   2. snd_soc_instantiate_card(card);  // ☆ 核心函数 ☆

      3.1/* soc_bind_dai_link ,就是将machine的 《dai_link》 结构中的
            codec、codec端的dai、cpu端的dai依次从已经注册的三个链表中找到并且赋值给《runtime》结构

            以下三个函数的作用就是将 dai、dma、codec  注册到三个链表中    
            snd_soc_register_dai
            snd_soc_register_platform
            snd_soc_register_codec

            */
                  for (i = 0; i < card->num_links; i++)
                    soc_bind_dai_link(card, i);
                        3.1.1 /* find CPU DAI */
                              rtd->cpu_dai = cpu_dai; 

                        3.1.2 /* find_codec */
                              rtd->codec = codec; 

                        3.1.3 /* find CODEC DAI */      
                              rtd->codec_dai = codec_dai;  

                        3.1.4 /* find_platform */
                              rtd->platform = platform;  

            3.2 /* initialize the register cache for each available codec */
                ret = snd_soc_init_codec_cache(codec, compress_type);

            3.3 snd_card_create     // ☆ ALSA 创建contorl设备节点


            3.4 /* ☆ 此处依次调用以下三个结构模块的probe函数,注意不是驱动的probe ☆  

                    snd_soc_platform_driver 
                    snd_soc_codec_driver
                    snd_soc_dai_driver  
                */
                soc_probe_dai_link   

                        /* probe the cpu_dai */
                        /* probe the CODEC */
                        /* probe the platform */
                        /* probe the CODEC DAI */
                        /* create the pcm */
                                    ret = soc_new_pcm(rtd, num);
                                                        struct snd_pcm_ops *soc_pcm_ops = &rtd->ops;
                                                                        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;

                                                        snd_pcm_new      // ☆ ALSA 创建pcm设备节点    

            3.5 snd_card_register                                        //  ☆ ALSA 创建sys节点 /sys/class/sound/cardx

2.2 platform

注册了cpu端的 DAI 和 DMA 驱动
 

snd_soc_register_platform(struct device *dev,struct snd_soc_platform_driver *platform_drv)  // 注册 DMA

snd_soc_register_dai(struct device *dev,struct snd_soc_dai_driver *dai_drv) // 注册 dai

重要结构

struct snd_soc_platform_driver

重要回调函数


当应用程序打开一个pcm设备时,该函数会被调用,通常,该函数会使用snd_soc_set_runtime_hwparams()设置substream中的snd_pcm_runtime结构里面的hw_params相关字段,然后为snd_pcm_runtime的private_data字段申请一个私有结构,用于保存该平台的dma参数。

snd_soc_platform_driver->ops->open 

驱动的hw_params阶段,该函数会被调用。通常,该函数会通过snd_soc_dai_get_dma_data函数获得对应的dai的dma参数,获得的参数一般都会保存在snd_pcm_runtime结构的private_data字段。然后通过snd_pcm_set_runtime_buffer函数设置snd_pcm_runtime结构中的dma buffer的地址和大小等参数。要注意的是,该回调可能会被多次调用,具体实现时要小心处理多次申请资源的问题。

snd_soc_platform_driver->ops->hw_params 

正式开始数据传送之前会调用该函数,该函数通常会完成dma操作的必要准备工作。

snd_soc_platform_driver->ops->prepare

数据传送的开始,暂停,恢复和停止时,该函数会被调用。

snd_soc_platform_driver->ops->trigger

该函数返回传送数据的当前位置。

snd_soc_platform_driver->ops->pointer


2.3 codec

注册了codec和codec端的dai,指定了control 、dapm_widgets、dapm_route

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)   

// 注册codec和 codec 端的dai,下面两个结构作为这个函数的参数
struct snd_soc_codec_driver
struct snd_soc_dai_driver

// 结构成员
snd_soc_codec_driver->controls       // ☆ 音量调节等Control设备 ☆           snd_kcontrol_new结构数组
snd_soc_codec_driver->dapm_widgets   // ☆ dapm_widgets control集合 ☆       snd_soc_dapm_widget 结构数组
snd_soc_codec_driver->dapm_route     // ☆ 路由 ☆                           snd_soc_dapm_route 结构数组

// 重要回调函数
snd_soc_dai_driver->ops->startup
snd_soc_dai_driver->ops->hw_params

snd_soc_codec_driver->ops->probe         //dai驱动的probe函数,由snd_soc_instantiate_card回调 


三、open、ioctl操作设备节点的函数调用过程


1.播放过程

这里写图片描述


snd_pcm_open

☆ 依次调用cpu_dai、 dma,、codec_dai,、machine 的open或startup函数 ☆
           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     startup           
           ret = rtd->dai_link->ops->startup(substream);                  // machine       startup

snd_pcm_playback_ioctl1

☆ SNDRV_PCM_IOCTL_HW_PARAMS

snd_pcm_hw_params_user
       soc_pcm_hw_params
              ☆  依次调用 machine 、codec_dai、cpu_dai、platform(dma) 的hw_params函数 ☆




SNDRV_PCM_IOCTL_PREPARE  :
                 snd_pcm_prepare(substream, file);
                               snd_power_wait // 电源管理相关 
                               .... 调用到platform里的prepare




☆ SNDRV_PCM_IOCTL_WRITEI_FRAMES :    // 循环数据传输
                                snd_pcm_lib_write
                                                snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_write_transfer)
                                                                snd_pcm_lib_write_transfer
                                                                                copy_from_user
                                                                                snd_pcm_start(substream);  // 启动传输

2.音量控制过程


见最开始时序图

这里写图片描述

codec驱动中搜索 “.put” 关键字直接找到音量控制相关操作


重要结构

/*
    定义一个kcontrol主要就是定义一个snd_kcontrol_new结构
    一个kcontrol代表着一个mixer(混音器),或者是一个mux(多路开关),又或者是一个音量控制器等等。
*/

struct snd_kcontrol_new

四、重要结构解析

snd_soc_codec

* SoC Audio Codec device */  
struct snd_soc_codec {  
    const char *name;  /* Codec的名字*/  
    struct device *dev; /* 指向Codec设备的指针 */  
    const struct snd_soc_codec_driver *driver; /* 指向该codec的驱动的指针 */  
    struct snd_soc_card *card;    /* 指向Machine驱动的card实例 */  
    int num_dai; /* 该Codec数字接口的个数,目前越来越多的Codec带有多个I2S或者是PCM接口 */  
    int (*volatile_register)(...);  /* 用于判定某一寄存器是否是volatile */  
    int (*readable_register)(...);  /* 用于判定某一寄存器是否可读 */  
    int (*writable_register)(...);  /* 用于判定某一寄存器是否可写 */  

    /* runtime */  
    ......  
    /* codec IO */  
    void *control_data; /* 该指针指向的结构用于对codec的控制,通常和read,write字段联合使用 */  
    enum snd_soc_control_type control_type;/* 可以是SND_SOC_SPI,SND_SOC_I2C,SND_SOC_REGMAP中的一种 */  
    unsigned int (*read)(struct snd_soc_codec *, unsigned int);  /* 读取Codec寄存器的函数 */  
    int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);  /* 写入Codec寄存器的函数 */  
    /* dapm */  
    struct snd_soc_dapm_context dapm;  /* 用于DAPM控件 */  
};  

snd_soc_codec_driver

/* codec driver */  
struct snd_soc_codec_driver {  
    /* driver ops */  
    int (*probe)(struct snd_soc_codec *);  /* codec驱动的probe函数,由snd_soc_instantiate_card回调 */  
    int (*remove)(struct snd_soc_codec *);    
    int (*suspend)(struct snd_soc_codec *);  /* 电源管理 */  
    int (*resume)(struct snd_soc_codec *);   /* 电源管理 */  

    /* Default control and setup, added after probe() is run */  
    const struct snd_kcontrol_new *controls;          /* ☆ 音频控件指针 */  
    const struct snd_soc_dapm_widget *dapm_widgets;   /* ☆ dapm部件指针 */  
    const struct snd_soc_dapm_route *dapm_routes;     /* ☆ dapm路由指针 */  

    /* codec wide operations */  
    int (*set_sysclk)(...);  /* 时钟配置函数 */  
    int (*set_pll)(...);     /* 锁相环配置函数 */  

    /* codec IO */  
    unsigned int (*read)(...);  /* 读取codec寄存器函数 */  
    int (*write)(...);          /* 写入codec寄存器函数 */  
    int (*volatile_register)(...);   /* 用于判定某一寄存器是否是volatile */  
    int (*readable_register)(...);   /* 用于判定某一寄存器是否可读 */  
    int (*writable_register)(...);   /* 用于判定某一寄存器是否可写 */  

    /* codec bias level */  
    int (*set_bias_level)(...);  /* 偏置电压配置函数 */  

};  

snd_soc_dai


  /*  
 * digital Audio Interface runtime data.  
 * Holds runtime data for a DAI.  
 */  
struct snd_soc_dai {  
    const char *name;    /* dai的名字 */  
    struct device *dev;  /* 设备指针 */  
    /* driver ops */  
    struct snd_soc_dai_driver *driver;  /* 指向dai驱动结构的指针 */
    /* DAI runtime info */  
    unsigned int capture_active:1;      /* stream is in use */  
    unsigned int playback_active:1;     /* stream is in use */  
    /* DAI DMA data */  
    void *playback_dma_data;           /* 用于管理playback dma */  
    void *capture_dma_data;            /* 用于管理capture dma */  

    /* parent platform/codec */  
    union {  
        struct snd_soc_platform *platform;  /* 如果是cpu dai,指向所绑定的平台 */  
        struct snd_soc_codec *codec;        /* 如果是codec dai指向所绑定的codec */  
    };  
    struct snd_soc_card *card;              /* 指向Machine驱动中的crad实例 */  
};  

snd_soc_dai_driver


struct snd_soc_dai_driver {  
    /* DAI description */  
    const char *name;  /* dai驱动名字 */  

    /* DAI driver callbacks */  
    int (*probe)(struct snd_soc_dai *dai);  /* dai驱动的probe函数,由snd_soc_instantiate_card回调 */  
    int (*remove)(struct snd_soc_dai *dai);    
    int (*suspend)(struct snd_soc_dai *dai);  /* 电源管理 */  
    int (*resume)(struct snd_soc_dai *dai);    

    /* ops */  
    const struct snd_soc_dai_ops *ops;  /* 指向本dai的snd_soc_dai_ops结构 */  

    /* DAI capabilities */  
    struct snd_soc_pcm_stream capture;   /* ☆ 描述capture的能力  ☆ */  
    struct snd_soc_pcm_stream playback;  /* ☆ 描述playback的能力 ☆ */  
};  

snd_soc_dai_driver->ops

struct snd_soc_dai_ops {  
    /*  
     * 工作时钟配置函数  通常由machine驱动调用
     *  
     */  
    int (*set_sysclk)(...);     // 设置dai的主时钟;
    int (*set_pll)(...);  
    int (*set_clkdiv)(...);  
    /*  
     * DAI format configuration  
     * Called by soc_card drivers, normally in their hw_params.  
     */  
    int (*set_fmt)(...);            // dai 的格式配置函数  通常由machine驱动调用
    int (*set_tdm_slot)(...);       // 如果dai支持时分复用,用于设置时分复用的slot;
    int (*set_channel_map)(...);    // 声道的时分复用映射设置
    int (*set_tristate)(...);       // 设置dai引脚的状态,当与其他dai并联使用同一引脚时需要使用该回调
    /*  
     * DAI digital mute - optional.  
     * 抗pop,pop声  由soc-core调用:
     */  
    int (*digital_mute)(...);  

    /*  
     * 标准的snd_soc_ops回调             ☆
     * 通常由soc-core在进行PCM操作时调用   ☆ 
     */  
    int (*startup)(...);  
    void (*shutdown)(...);  
    int (*hw_params)(...);  
    int (*hw_free)(...);  
    int (*prepare)(...);  
    int (*trigger)(...);  
    /*  
     * For hardware based FIFO caused delay reporting.  
     * Optional.  
     */  
    snd_pcm_sframes_t (*delay)(...);  
};  


四、framwork 框图

http://download.csdn.net/detail/u012719256/9592488

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值