ALSA声卡驱动中的DAPM详解之三:如何定义各种widget

1.定义widget
  codec域widget的定义
  platform域widget的定义
  音频路径path域widget的定义
  音频数据流stream域widget的定义
2.定义dapm kcontrol
3.建立widget和route

DAPM框架中几个重要的数据结构:snd_soc_dapm_widget,snd_soc_dapm_path,snd_soc_dapm_route。其中snd_soc_dapm_path无需我们自己定义,它会在注册snd_soc_dapm_route时动态地生成,但是对于系统中的widget和route,我们是需要自己进行定义的,另外,widget所包含的kcontrol与普通的kcontrol有所不同,它们的定义方法与标准的kcontrol也有所不同。本节的内容我将会介绍如何使用DAPM系统提供的一些辅助宏定义来定义各种类型的widget和它所用到的kcontrol。

(1)定义widget
和普通的kcontrol一样,DAPM框架为我们提供了大量的辅助宏用来定义各种各样的widget控件,这些宏定义根据widget的类型,按照它们的电源所在的域,被分为了几个域,他们分别是:
codec域     比如VREF和VMID等提供参考电压的widget,这些widget通常在codec的probe/remove回调中进行控制,当然,在工作中如果没有音频流时,也可以适当地进行控制它们的开启与关闭。
platform域    位于该域上的widget通常是针对平台或板子的一些需要物理连接的输入/输出接口,例如耳机、扬声器、麦克风,因为这些接口在每块板子上都可能不一样,所以通常它们是在machine驱动中进行定义和控制,并且也可以由用户空间的应用程序通过某种方式来控制它们的打开和关闭。
音频路径域    一般是指codec内部的mixer、mux等控制音频路径的widget,这些widget可以根据用户空间的设定连接关系,自动设定他们的电源状态。
音频数据流域    是指那些需要处理音频数据流的widget,例如ADC、DAC等等。

(1.1)codec域widget的定义
sound/soc/ingenic/icodec/icdc_d3.c
目前,DAPM框架只提供了定义一个codec域widget的辅助宏:

static const struct snd_soc_dapm_widget icdc_d3_dapm_widgets[] = {
/* ADC */
    SND_SOC_DAPM_ADC("ADC", "Capture" , SCODA_REG_AICR_ADC, 4, 1),
    SND_SOC_DAPM_MUX("ADC Mux", SCODA_REG_CR_ADC, 4, 1, &icdc_d3_adc_controls),                        /*0*/
    SND_SOC_DAPM_MICBIAS("MICBIAS", SCODA_REG_CR_MIC1, 5, 1),
    SND_SOC_DAPM_PGA("AMIC", SCODA_REG_CR_MIC1, 4, 1, NULL, 0),
    SND_SOC_DAPM_PGA("DMIC", SCODA_REG_CR_DMIC, 7, 0, NULL, 0),

    SND_SOC_DAPM_DAC("DAC", "Playback", SCODA_REG_AICR_DAC, 4, 1),
    SND_SOC_DAPM_PGA("DAC_MERCURY", SCODA_REG_CR_DAC, 4, 1, NULL, 0),

    SND_SOC_DAPM_SWITCH_E("VDIGITAL BYPASS", SND_SOC_NOPM, 0, 0, &icdc_d3_vdigital_bypass_controls,
            icdc_d3_vdigital_bypass_controls_event,
            SND_SOC_DAPM_POST_PMD|SND_SOC_DAPM_PRE_PMU),

/* PINS */
    SND_SOC_DAPM_INPUT("AIP"),
    SND_SOC_DAPM_INPUT("AIN"),
    SND_SOC_DAPM_INPUT("DMIC IN"),
    SND_SOC_DAPM_OUTPUT("DO_LO_PWM"),
    SND_SOC_DAPM_OUTPUT("DO_BO_PWM"),
};

(1.2)platform域widget的定义
sound/soc/ingenic/asoc-board/phoenix_icdc.c
DAPM框架为我们提供了多种platform域widget的辅助定义宏:


static const struct snd_soc_dapm_widget phoenix_dapm_widgets[] = {
    SND_SOC_DAPM_HP("Headphone Jack", NULL),
    SND_SOC_DAPM_SPK("Speaker", phoenix_spk_power),
    SND_SOC_DAPM_MIC("Mic Buildin", NULL),
    SND_SOC_DAPM_MIC("DMic", NULL),
};

以上这些widget分别对应信号发生器,输入引脚,输出引脚,麦克风,耳机,扬声器,线路输入接口。其中的reg字段被设置为SND_SOC_NOPM(-1),表明这些widget是没有寄存器控制位来控制widget的电源状态的。麦克风,耳机,扬声器,线路输入接口这几种widget,还可以定义一个dapm事件回调函数wevent,从event_flags字段的设置可以看出,他们只会响应SND_SOC_DAPM_POST_PMU(上电后)和SND_SOC_DAPM_PMD(下电前)事件,这几个widget通常会在machine驱动中定义,而SND_SOC_DAPM_INPUT和SND_SOC_DAPM_OUTPUT则用来定义codec芯片的输出输入脚,通常在codec驱动中定义,最后,在machine驱动中增加相应的route,把麦克风和耳机等widget与相应的codec输入输出引脚的widget连接起来。

    SND_SOC_DAPM_INPUT("AIP"),                                                                                                          
    SND_SOC_DAPM_INPUT("AIN"),                                                                                                          
    SND_SOC_DAPM_INPUT("DMIC IN"),                                                                                                      
    SND_SOC_DAPM_OUTPUT("DO_LO_PWM"),                                                                                                   
    SND_SOC_DAPM_OUTPUT("DO_BO_PWM")

static const struct snd_soc_dapm_widget phoenix_dapm_widgets[] = {                                                                      
    SND_SOC_DAPM_HP("Headphone Jack", NULL),                                                                                            
    SND_SOC_DAPM_SPK("Speaker", phoenix_spk_power),                                                                                     
    SND_SOC_DAPM_MIC("Mic Buildin", NULL),                                                                                              
    SND_SOC_DAPM_MIC("DMic", NULL),                                                                                                     
};  
    


(1.3)音频路径(path)域widget的定义
这种widget通常是对普通kcontrols控件的再封装,增加音频路径和电源管理功能,所以这种widget会包含一个或多个kcontrol,普通kcontrol的定义方法我们在ALSA声卡驱动中的DAPM详解之一:kcontrol中已经介绍过,不过这些被包含的kcontrol不能使用这种方法定义,它们需要使用dapm框架提供的定义宏来定义,详细的讨论我们后面有介绍。这里先列出这些widget的定义宏:

static const struct snd_soc_dapm_widget icdc_d3_dapm_widgets[] = {                                                                      
/* ADC */
    SND_SOC_DAPM_ADC("ADC", "Capture" , SCODA_REG_AICR_ADC, 4, 1),
    SND_SOC_DAPM_MUX("ADC Mux", SCODA_REG_CR_ADC, 4, 1, &icdc_d3_adc_controls),                        /*0*/                            
    SND_SOC_DAPM_MICBIAS("MICBIAS", SCODA_REG_CR_MIC1, 5, 1),
    SND_SOC_DAPM_PGA("AMIC", SCODA_REG_CR_MIC1, 4, 1, NULL, 0),
    SND_SOC_DAPM_PGA("DMIC", SCODA_REG_CR_DMIC, 7, 0, NULL, 0),
    
    SND_SOC_DAPM_DAC("DAC", "Playback", SCODA_REG_AICR_DAC, 4, 1),
    SND_SOC_DAPM_PGA("DAC_MERCURY", SCODA_REG_CR_DAC, 4, 1, NULL, 0),
    
    SND_SOC_DAPM_SWITCH_E("VDIGITAL BYPASS", SND_SOC_NOPM, 0, 0, &icdc_d3_vdigital_bypass_controls,                                     
            icdc_d3_vdigital_bypass_controls_event,
            SND_SOC_DAPM_POST_PMD|SND_SOC_DAPM_PRE_PMU),


可以看出,这些widget的reg和shift字段是需要赋值的,说明这些widget是有相应的电源控制寄存器的,DAPM框架在扫描和更新音频路径时,会利用这些寄存器来控制widget的电源状态,使得它们的供电状态是按需分配的,需要的时候(在有效的音频路径上)上电,不需要的时候(不再有效的音频路径上)下电。这些widget需要完成和之前介绍的mixer、mux等控件同样的功能,实际上,这是通过它们包含的kcontrol控件来完成的,这些kcontrol我们需要在定义widget前先定义好,然后通过wcontrols和num_kcontrols参数传递给这些辅助定义宏。
如果需要自定义这些widget的dapm事件处理回调函数,也可以使用下面这些带“_E”后缀的版本:
SND_SOC_DAPM_PGA_E
SND_SOC_DAPM_OUT_DRV_E
SND_SOC_DAPM_MIXER_E
SND_SOC_DAPM_MIXER_NAMED_CTL_E
SND_SOC_DAPM_SWITCH_E
SND_SOC_DAPM_MUX_E
SND_SOC_DAPM_VIRT_MUX_E


(1.4)音频数据流(stream)域widget的定义
这些widget主要包含音频输入/输出接口,ADC/DAC等等:

#define SND_SOC_DAPM_AIF_IN(wname, stname, wslot, wreg, wshift, winvert) \
{       .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \
        .reg = wreg, .shift = wshift, .invert = winvert }
#define SND_SOC_DAPM_AIF_IN_E(wname, stname, wslot, wreg, wshift, winvert, \
                              wevent, wflags)                           \
{       .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \
        .reg = wreg, .shift = wshift, .invert = winvert, \
        .event = wevent, .event_flags = wflags }
#define SND_SOC_DAPM_AIF_OUT(wname, stname, wslot, wreg, wshift, winvert) \
{       .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \
        .reg = wreg, .shift = wshift, .invert = winvert }
#define SND_SOC_DAPM_AIF_OUT_E(wname, stname, wslot, wreg, wshift, winvert, \
                             wevent, wflags)                            \
{       .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \
        .reg = wreg, .shift = wshift, .invert = winvert, \
        .event = wevent, .event_flags = wflags }
#define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \
{       .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \
        .shift = wshift, .invert = winvert}
#define SND_SOC_DAPM_DAC_E(wname, stname, wreg, wshift, winvert, \
                           wevent, wflags)                              \
{       .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \
        .shift = wshift, .invert = winvert, \
        .event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_ADC(wname, stname, wreg, wshift, winvert) \
{       .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \
        .shift = wshift, .invert = winvert}
#define SND_SOC_DAPM_ADC_E(wname, stname, wreg, wshift, winvert, \
                           wevent, wflags)                              \
{       .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \
        .shift = wshift, .invert = winvert, \
        .event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_CLOCK_SUPPLY(wname) \
{       .id = snd_soc_dapm_clock_supply, .name = wname, \
        .reg = SND_SOC_NOPM, .event = dapm_clock_event, \
        .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD }


除了上面这些widget,还有另外三种widget没有提供显示的定义方法,它们的种类id分别是:
snd_soc_dapm_dai_in
snd_soc_dapm_dai_out
snd_soc_dapm_dai_link
还记得我们在Linux ALSA声卡驱动之七:ASoC架构中的Codec中的snd_soc_dai结构吗?每个codec有多个dai,而cpu(通常就是指某个soc cpu芯片)也会有多个dai,dai注册时,dapm系统会为每个dai创建一个snd_soc_dapm_dai_in或snd_soc_dapm_dai_out类型的widget,通常,这两种widget会和codec中具有相同的stream name的widget进行连接。另外一种情况,当系统中具有多个音频处理器(比如多个codec)时,他们之间可能会通过某两个dai进行连接,当machine驱动确认有这种配置时(通过判断dai_links结构中的param字段),会为他们建立一个dai link把他们绑定在一起,因为有连接关系,两个音频处理器之间的widget的电源状态就可以互相传递。
除了还有几个通用的widget,他们的定义方法如下:

#define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \
{       .id = wid, .name = wname, .kcontrol_news = NULL, .num_kcontrols = 0, \
        .reg = -((wreg) + 1), .shift = wshift, .mask = wmask, \
        .on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \
        .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
#define SND_SOC_DAPM_SUPPLY(wname, wreg, wshift, winvert, wevent, wflags) \
{       .id = snd_soc_dapm_supply, .name = wname, .reg = wreg,  \
        .shift = wshift, .invert = winvert, .event = wevent, \
        .event_flags = wflags}
#define SND_SOC_DAPM_REGULATOR_SUPPLY(wname, wdelay, wflags)        \
{       .id = snd_soc_dapm_regulator_supply, .name = wname, \
        .reg = SND_SOC_NOPM, .shift = wdelay, .event = dapm_regulator_event, \
        .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
        .invert = wflags}


2.定义dapm kcontrol

上一节提到,对于音频路径上的mixer或mux类型的widget,它们包含了若干个kcontrol,这些被包含的kcontrol实际上就是我们之前讨论的mixer和mux等,dapm利用这些kcontrol完成音频路径的控制。不过,对于widget来说,它的任务还不止这些,dapm还要动态地管理这些音频路径的连结关系,以便可以根据这些连接关系来控制这些widget的电源状态,如果按照普通的方法定义这些kcontrol,是无法达到这个目的的,因此,dapm为我们提供了另外一套定义宏,由它们完成这些被widget包含的kcontrol的定义


#define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \
{       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_volsw, \
        .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
        .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
#define SOC_DAPM_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
{       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_volsw, \
        .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\
        .tlv.p = (tlv_array), \
        .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
        .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
#define SOC_DAPM_ENUM(xname, xenum) \
{       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_enum_double, \
        .get = snd_soc_dapm_get_enum_double, \
        .put = snd_soc_dapm_put_enum_double, \
        .private_value = (unsigned long)&xenum }
#define SOC_DAPM_ENUM_VIRT(xname, xenum)                    \
{       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_enum_double, \
        .get = snd_soc_dapm_get_enum_virt, \
        .put = snd_soc_dapm_put_enum_virt, \
        .private_value = (unsigned long)&xenum }
#define SOC_DAPM_ENUM_EXT(xname, xenum, xget, xput) \
{       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_enum_double, \
        .get = xget, \
        .put = xput, \
        .private_value = (unsigned long)&xenum }
#define SOC_DAPM_VALUE_ENUM(xname, xenum) \
{       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_enum_double, \
        .get = snd_soc_dapm_get_value_enum_double, \
        .put = snd_soc_dapm_put_value_enum_double, \
        .private_value = (unsigned long)&xenum }
#define SOC_DAPM_PIN_SWITCH(xname) \
{       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname " Switch", \
        .info = snd_soc_dapm_info_pin_switch, \
        .get = snd_soc_dapm_get_pin_switch, \
        .put = snd_soc_dapm_put_pin_switch, \
        .private_value = (unsigned long)xname }

可以看出,SOC_DAPM_SINGLE对应与普通控件的SOC_SINGLE,SOC_DAPM_SINGLE_TLV对应SOC_SINGLE_TLV......,相比普通的kcontrol控件,dapm的kcontrol控件只是把info,get,put回调函数换掉了。dapm kcontrol的put回调函数不仅仅会更新控件本身的状态,他还会把这种变化传递到相邻的dapm kcontrol,相邻的dapm kcontrol又会传递这个变化到他自己相邻的dapm kcontrol,知道音频路径的末端,通过这种机制,只要改变其中一个widget的连接状态,与之相关的所有widget都会被扫描并测试一下自身是否还在有效的音频路径中,从而可以动态地改变自身的电源状态,这就是dapm的精髓所在。


3.建立widget和route

上面介绍了一大堆的辅助宏,那么,对于一个实际的系统,我们怎么定义我们需要的widget?怎样定义widget的连接关系?下面我们还是以Wolfson公司的codec芯片WM8993为例子来了解这个过程。
第一步,利用辅助宏定义widget所需要的dapm kcontrol:


static const struct snd_kcontrol_new icdc_d3_snd_controls[] = {
    /* Volume controls */
    SOC_DOUBLE_R_TLV("Master Playback Volume", SCODA_REG_GCR_DACL, SCODA_REG_GCR_DACR, 0, 63, 0, dac_tlv),
    SOC_DOUBLE_R_TLV("TITANIUM Playback Volume", SCODA_REG_GCR_DACL, SCODA_REG_GCR_DACR, 0, 63, 0, dac_tlv),
    SOC_DOUBLE_R_TLV("Playback Mixer Volume", SCODA_REG_GCR_MIXDACL, SCODA_REG_GCR_MIXDACR, 0, 31, 0, mix_tlv),
    SOC_DOUBLE_R_TLV("Digital Capture Volume", SCODA_REG_GCR_ADCL, SCODA_REG_GCR_ADCR, 0, 43, 0, adc_tlv),
    SOC_DOUBLE_R_TLV("Digital Capture Mixer Volume", SCODA_REG_GCR_MIXADCL, SCODA_REG_GCR_MIXADCR, 0, 31, 0, mix_tlv),
    SOC_SINGLE_TLV("Mic Volume", SCODA_REG_GCR_MIC1, 0, 4, 0, mic_tlv),
    /* ADC private controls */
    SOC_SINGLE("ADC High Pass Filter Switch", SCODA_REG_FCR_ADC, 6, 1, 0),
    /* mic private controls */
    SOC_SINGLE_EXT("Digital Playback mute", SCODA_REG_CR_DAC, 7, 1, 0, icdc_d3_dmute_controls_get, icdc_d3_dmute_controls_put),
    
    SOC_ENUM_EXT("Mix0", icdc_d3_enum[1], icdc_d3_Mix0_controls_get,icdc_d3_Mix0_controls_put),
    SOC_ENUM_EXT("Mix1", icdc_d3_enum[2], icdc_d3_Mix1_controls_get,icdc_d3_Mix1_controls_put),
    SOC_ENUM_EXT("Mix2", icdc_d3_enum[3], icdc_d3_Mix2_controls_get,icdc_d3_Mix2_controls_put), 
    SOC_ENUM_EXT("Mix3", icdc_d3_enum[4], icdc_d3_Mix3_controls_get,icdc_d3_Mix3_controls_put),
    SOC_ENUM_EXT("Mux0", icdc_d3_enum[5], icdc_d3_Mux0_controls_get,icdc_d3_Mux0_controls_put),
    SOC_ENUM_EXT("Mux2", icdc_d3_enum[6], icdc_d3_Mux2_controls_get,icdc_d3_Mux2_controls_put),
    
    SOC_SINGLE("ACG_enable", SCODA_REG_CR_DAC_AGC, 7, 1, 0),
    SOC_SINGLE("ACG0_threshold", SCODA_DAC_AGC0, 0, 0x1f, 0),
    SOC_SINGLE("ACG1_rate", SCODA_DAC_AGC1, 0, 5, 0),
};


static const struct snd_kcontrol_new icdc_d3_vdigital_bypass_controls =
    SOC_DAPM_SINGLE("Switch", 0x11, 7, 1, 0);


static const struct snd_kcontrol_new icdc_d3_adc_controls =
    SOC_DAPM_VALUE_ENUM("Route",  icdc_d3_enum[0]);

第二步,定义真正的widget,包含第一步定义好的dapm控件:

sound/soc/ingenic/icodec/icdc_d3.c
static const struct snd_soc_dapm_widget icdc_d3_dapm_widgets[] = {                                                                      
/* ADC */                                                                                                                               
    SND_SOC_DAPM_ADC("ADC", "Capture" , SCODA_REG_AICR_ADC, 4, 1),                                                                      
    SND_SOC_DAPM_MUX("ADC Mux", SCODA_REG_CR_ADC, 4, 1, &icdc_d3_adc_controls),                        /*0*/                            
    SND_SOC_DAPM_MICBIAS("MICBIAS", SCODA_REG_CR_MIC1, 5, 1),                                                                           
    SND_SOC_DAPM_PGA("AMIC", SCODA_REG_CR_MIC1, 4, 1, NULL, 0),                                                                         
    SND_SOC_DAPM_PGA("DMIC", SCODA_REG_CR_DMIC, 7, 0, NULL, 0),                                                                         
                                                                                                                                        
    SND_SOC_DAPM_DAC("DAC", "Playback", SCODA_REG_AICR_DAC, 4, 1),                                                                      
    SND_SOC_DAPM_PGA("DAC_MERCURY", SCODA_REG_CR_DAC, 4, 1, NULL, 0),                                                                   
                                                                                                                                        
    SND_SOC_DAPM_SWITCH_E("VDIGITAL BYPASS", SND_SOC_NOPM, 0, 0, &icdc_d3_vdigital_bypass_controls,                                     
            icdc_d3_vdigital_bypass_controls_event,                                                                                     
            SND_SOC_DAPM_POST_PMD|SND_SOC_DAPM_PRE_PMU),                                                                                
                                                                                                                                        
/* PINS */                                                                                                                              
    SND_SOC_DAPM_INPUT("AIP"),                                                                                                          
    SND_SOC_DAPM_INPUT("AIN"),                                                                                                          
    SND_SOC_DAPM_INPUT("DMIC IN"),                                                                                                      
    SND_SOC_DAPM_OUTPUT("DO_LO_PWM"),                                                                                                   
    SND_SOC_DAPM_OUTPUT("DO_BO_PWM"),                                                                                                   
}; 

sound/soc/ingenic/asoc-board/phoenix_icdc.c
static const struct snd_soc_dapm_widget phoenix_dapm_widgets[] = {                                                                      
    SND_SOC_DAPM_HP("Headphone Jack", NULL),                                                                                            
    SND_SOC_DAPM_SPK("Speaker", phoenix_spk_power),                                                                                     
    SND_SOC_DAPM_MIC("Mic Buildin", NULL),                                                                                              
    SND_SOC_DAPM_MIC("DMic", NULL),                                                                                                     
};  


第三步,定义这些widget的连接路径:

static const struct snd_soc_dapm_route intercon[] = {                                                                                   
    { "MICBIAS",  NULL,  "AIP" },
    { "MICBIAS",  NULL,  "AIN" },
    { "AMIC",  NULL,  "MICBIAS" },
    /*input*/ 
    { "ADC Mux", "AMIC ON", "AMIC" },
    { "ADC Mux", "DMIC ON", "DMIC IN" },

        
    { "ADC", NULL, "ADC Mux" },

    {"VDIGITAL BYPASS", "Switch", "ADC"},
    {"DAC", NULL, "VDIGITAL BYPASS"},

    /*output*/
    { "DAC_MERCURY"  , NULL, "DAC" },
    { "DO_LO_PWM", NULL, "DAC_MERCURY" },
};


MICBIAS连接到(--->)AIP / AIN的输入端
AMIC / DMIC IN 连接到(--->) ADC Mux
ADC---->ADC Mux

DAC---->DAC_MERCURY
DAC_MERCURY---->DO_LO_PWM

第四步,在codec驱动的probe回调中注册这些widget和路径:
static struct snd_soc_codec_driver soc_codec_dev_icdc_d3_codec = {
    .probe =    icdc_d3_probe,
    .num_controls = ARRAY_SIZE(icdc_d3_snd_controls),
    .dapm_widgets = icdc_d3_dapm_widgets,
    .num_dapm_widgets = ARRAY_SIZE(icdc_d3_dapm_widgets),
    .dapm_routes = intercon,
    .num_dapm_routes = ARRAY_SIZE(intercon),
    .ignore_pmdown_time = 1,
};


static int icdc_d3_platform_probe(struct platform_device *pdev)
 {   
ret = snd_soc_register_codec(&pdev->dev,&soc_codec_dev_icdc_d3_codec, &icdc_d3_codec_dai, 1);

}

在machine驱动中,我们可以用同样的方式定义和注册板子特有的widget和路径信息。


static const struct snd_soc_dapm_widget phoenix_dapm_widgets[] = {
    SND_SOC_DAPM_HP("Headphone Jack", NULL),
    SND_SOC_DAPM_SPK("Speaker", phoenix_spk_power),
    SND_SOC_DAPM_MIC("Mic Buildin", NULL),
    SND_SOC_DAPM_MIC("DMic", NULL),
};

/* phoenix machine audio_map */
static const struct snd_soc_dapm_route audio_map[] = {
    /* ext speaker connected to DO_LO_PWM  */
    {"Speaker", NULL, "DO_LO_PWM"},

    /* mic is connected to AIP/N1 */
    {"MICBIAS", NULL, "Mic Buildin"},
    {"DMIC", NULL, "DMic"},
    
};

sound/soc/ingenic/asoc-board/phoenix_icdc.c 
static int phoenix_dlv_dai_link_init(struct snd_soc_pcm_runtime *rtd)
{

     err = snd_soc_dapm_new_controls(dapm, phoenix_dapm_widgets,                                                                         
            ARRAY_SIZE(phoenix_dapm_widgets));                                                                                          
                                                                                                                               
    /* Set up rx1950 specific audio path audio_mapnects */                                                                              
    err = snd_soc_dapm_add_routes(dapm, audio_map,                                                                                      
            ARRAY_SIZE(audio_map));  


}

转至:http://blog.csdn.net/droidphone

 

你可能感兴趣的:(alsa,audio)