MT6737 Android N 平台 Audio系统学习----ALSA Driver

来源:互联网 发布:网络出版服务规定 编辑:程序博客网 时间:2024/06/05 01:58

1、ALSA简述

这里写图片描述
ALSA是Advanced Linux Sound Architecture 的缩写,目前已经成为了linux的主流音频体系结构,在内核设备驱动层,ALSA提供了alsa-driver,同时在应用层,ALSA为我们提供了alsa-lib,应用程序只要调用alsa-lib提供的API,即可以完成对底层音频硬件的控制。
Kernel-3.10/sound/core该目录包含了ALSA驱动的中间层,它是整个ALSA驱动的核心部分。
Kernel-3.10/sound/soc 针对system-on-chip体系的中间层代码 ,soc/codecs 针对soc体系的各种codec的代码,与平台无关 。
这里写图片描述
声卡的主要功能
三个主要功能:
(1)播放声音(playback)
(2)录音(capture)
(3)声音控制(control)
运行adb shell ls -l /dev/snd,我们可以看到当前平台注册的声卡驱动设备。
主要分为以下几类:
pcmC0D0p —— Playback
pcmC0D0c —— Capture
controlC0 —— Control,比如各种音频控件开关、音量增益等
这里写图片描述

ASoC把音频系统同样分为3大部分:Machine,Platform和Codec。
Platform 一般是指某一个SoC平台,比如MT6582, MT6595, MT6752等等,与音频相关的通常包含该SoC中的Clock、AFE、I2S、DMA等等。
Codec 字面上的意思就是编解码器,Codec里面包含了I2S接口、DAC、ADC、Mixer、PA(功放),通常包含多种输入(Mic、Line-in、I2S、PCM)和多个输出(耳机、喇叭、听筒,Line-out),Codec和Platform一样,是可重用的部件。
Machine 绑定platform driver和codec driver 。
下面我们来对这三个模块进行分析。

2、Platform

这里写图片描述
从上图可以看出ASOC中包含了多个platform,它们每种platform对应一个.c代码,下面针对这些platform进行分析。

2.1、录音(capture)

kernel-3.18/sound/soc/mediatek/mt_soc_audio_v3/mt_soc_pcm_capture.c
这里写图片描述

snd_soc_register_platform() 该函数用于注册一个snd_soc_platform,只有注册以后,它才可以被Machine驱动使用。它的代码已经清晰地表达了它的实现过程:
为snd_soc_platform实例申请内存;
从platform_device中获得它的名字,用于Machine驱动的匹配工作;
初始化snd_soc_platform的字段;
把snd_soc_platform实例连接到全局链表platform_list中;
调用snd_soc_instantiate_cards,触发声卡的machine、platform、codec、dai等的匹配工作;

由上图可知capture被注册到mtk_soc_platform结构体中,接下来分析mtk_afe_capture_ops

static struct snd_pcm_ops mtk_afe_capture_ops = {    .open =     mtk_capture_pcm_open,    .close =    mtk_capture_pcm_close,    .ioctl =    snd_pcm_lib_ioctl,    .hw_params =    mtk_capture_pcm_hw_params,    .hw_free =  mtk_capture_pcm_hw_free,    .prepare =  mtk_capture_pcm_prepare,    .trigger =  mtk_capture_pcm_trigger,    .pointer =  mtk_capture_pcm_pointer,    .copy =     mtk_capture_pcm_copy,    .silence =  mtk_capture_pcm_silence,    .page =     mtk_capture_pcm_page,};

2.1.1、mtk_capture_pcm_open

static int mtk_capture_pcm_open(struct snd_pcm_substream *substream){    struct snd_pcm_runtime *runtime = substream->runtime;    int ret = 0;    AudDrv_Clk_On();    AudDrv_ADC_Clk_On();//使能模拟时钟    VUL_Control_context = Get_Mem_ControlT(Soc_Aud_Digital_Block_MEM_VUL);    /* can allocate sram_dbg */    AfeControlSramLock();#ifndef CAPTURE_FORCE_USE_DRAM    if (GetSramState() ==  SRAM_STATE_FREE) {        pr_warn("mtk_capture_pcm_open use sram\n");        mtk_capture_hardware.buffer_bytes_max = GetCaptureSramSize();        SetSramState(SRAM_STATE_CAPTURE);        mCaptureUseSram = true;    } else {        pr_warn("mtk_capture_pcm_open use dram\n");        mtk_capture_hardware.buffer_bytes_max = UL1_MAX_BUFFER_SIZE;        mCaptureUseSram = false;    }#else    pr_warn("mtk_capture_pcm_open use dram\n");    mtk_capture_hardware.buffer_bytes_max = UL1_MAX_BUFFER_SIZE;#endif    AfeControlSramUnLock();    runtime->hw = mtk_capture_hardware;    memcpy((void *)(&(runtime->hw)), (void *)&mtk_capture_hardware , sizeof(struct snd_pcm_hardware));    ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,                     &constraints_sample_rates);    ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);    if (ret < 0)        pr_warn("snd_pcm_hw_constraint_integer failed\n");    if (ret < 0) {        pr_err("mtk_capture_pcm_close\n");        mtk_capture_pcm_close(substream);        return ret;    }    if (mCaptureUseSram == false)        AudDrv_Emi_Clk_On();    pr_warn("mtk_capture_pcm_open return\n");    return 0;}

2.1.2、mtk_capture_pcm_trigger

static int mtk_capture_pcm_trigger(struct snd_pcm_substream *substream, int cmd){    pr_warn("mtk_capture_pcm_trigger cmd = %d\n", cmd);    switch (cmd) {    case SNDRV_PCM_TRIGGER_START:    case SNDRV_PCM_TRIGGER_RESUME:        //唤醒        return mtk_capture_alsa_start(substream);    case SNDRV_PCM_TRIGGER_STOP:    case SNDRV_PCM_TRIGGER_SUSPEND:        //睡眠        return mtk_capture_alsa_stop(substream);    }    return -EINVAL;}
static int mtk_capture_alsa_start(struct snd_pcm_substream *substream){    pr_warn("mtk_capture_alsa_start\n");    SetMemifSubStream(Soc_Aud_Digital_Block_MEM_VUL, substream);    StartAudioCaptureHardware(substream);#ifdef DENALI_FPGA_EARLYPORTING /* ccc early porting, copy from TurnOnDacPower() and ADC_LOOP_DAC_Func() */    /* Afe_Set_Reg(AFE_SGEN_CON0, 0x24862862, 0xffffffff); */    /* Ana_Set_Reg(PMIC_AFE_TOP_CON0, 0x0002, 0x0002);   //UL from sinetable */    /* Ana_Set_Reg(PMIC_AFE_TOP_CON0, 0x0001, 0x0001);   //DL from sinetable */    /* Ana_Set_Reg(AFE_SGEN_CFG0 , 0x0080 , 0xffff); */    /* Ana_Set_Reg(AFE_SGEN_CFG1 , 0x0101 , 0xffff); */    Ana_Get_Reg(AFE_AUDIO_TOP_CON0);   /* power on clock */    Ana_Get_Reg(AFUNC_AUD_CON2);    Ana_Get_Reg(AFUNC_AUD_CON0); /* sdm audio fifo clock power on */    Ana_Get_Reg(AFUNC_AUD_CON2); /* sdm power on */    Ana_Get_Reg(AFUNC_AUD_CON2); /* sdm fifo enable */    Ana_Get_Reg(AFE_DL_SDM_CON1); /* set attenuation gain */    Ana_Get_Reg(AFE_UL_DL_CON0); /* [0] afe enable */    Ana_Get_Reg(AFE_PMIC_NEWIF_CFG0); /* 8k sample rate */    Ana_Get_Reg(AFE_DL_SRC2_CON0_H);/* 8k sample rate */    Ana_Get_Reg(AFE_DL_SRC2_CON0_L); /* turn off mute function and turn on dl */    Ana_Get_Reg(PMIC_AFE_TOP_CON0); /* set DL in normal path, not from sine gen table */    Ana_Get_Reg(AFE_SGEN_CFG0); /* set DL in normal path, not from sine gen table */    Ana_Get_Reg(AFE_SGEN_CFG1); /* set DL in normal path, not from sine gen table */    Ana_Get_Reg(TOP_CLKSQ); /* Enable CLKSQ 26MHz */    Ana_Get_Reg(TOP_CLKSQ_SET); /* Turn on 26MHz source clock */    Ana_Get_Reg(AFE_AUDIO_TOP_CON0);   /* power on clock */    Ana_Get_Reg(FPGA_CFG1); /* must set in FPGA platform for PMIC digital loopback */#endif    return 0;}

2.1.3、snd_pcm_lib_ioctl

通用的PCM ioctl回调函数

/** * snd_pcm_lib_ioctl - a generic PCM ioctl callback * @substream: the pcm substream instance * @cmd: ioctl command * @arg: ioctl argument * * Processes the generic ioctl commands for PCM. * Can be passed as the ioctl callback for PCM ops. * * Return: Zero if successful, or a negative error code on failure. */int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,              unsigned int cmd, void *arg){    switch (cmd) {    case SNDRV_PCM_IOCTL1_INFO:        return 0;    case SNDRV_PCM_IOCTL1_RESET:        return snd_pcm_lib_ioctl_reset(substream, arg);    case SNDRV_PCM_IOCTL1_CHANNEL_INFO:        return snd_pcm_lib_ioctl_channel_info(substream, arg);    case SNDRV_PCM_IOCTL1_FIFO_SIZE:        return snd_pcm_lib_ioctl_fifo_size(substream, arg);    }    return -ENXIO;}

2.2、FM

kernel-3.18/sound/soc/mediatek/mt_soc_audio_v3/mt_soc_pcm_fm_i2s.c
这里写图片描述

static struct snd_pcm_ops mtk_fm_i2s_ops = {    .open =     mtk_pcm_fm_i2s_open,    .close =    mtk_pcm_fm_i2s_close,    .ioctl =    snd_pcm_lib_ioctl,    .hw_params =    mtk_pcm_fm_i2s_hw_params,    .hw_free =  mtk_pcm_fm_i2s_hw_free,    .prepare =  mtk_pcm_fm_i2s_prepare,    .trigger =  mtk_pcm_fm_i2s_trigger,    .pointer =  mtk_pcm_fm_i2s_pointer,    .copy =     mtk_pcm_fm_i2s_copy,    .silence =  mtk_pcm_fm_i2s_silence,    .page =     mtk_fm_i2s_pcm_page,};

接下来分析mtk_fm_i2s_ops

2.1、mtk_pcm_fm_i2s_open

static int mtk_pcm_fm_i2s_open(struct snd_pcm_substream *substream){    struct snd_pcm_runtime *runtime = substream->runtime;    int ret = 0;    AudDrv_Clk_On();    AudDrv_I2S_Clk_On();//打开I2S时钟/*static struct snd_pcm_hardware mtk_fm_i2s_hardware = {    .info = (SNDRV_PCM_INFO_MMAP |    SNDRV_PCM_INFO_INTERLEAVED |    SNDRV_PCM_INFO_RESUME |    SNDRV_PCM_INFO_MMAP_VALID),    .formats =      SND_SOC_STD_MT_FMTS,    .rates =        SOC_HIGH_USE_RATE,    .rate_min =     SOC_NORMAL_USE_RATE_MIN,    .rate_max =     SOC_NORMAL_USE_RATE_MAX,    .channels_min =     SOC_NORMAL_USE_CHANNELS_MIN,    .channels_max =     SOC_NORMAL_USE_CHANNELS_MAX,    .buffer_bytes_max = FM_I2S_MAX_BUFFER_SIZE,    .period_bytes_max = FM_I2S_MAX_PERIOD_SIZE,    .periods_min =      FM_I2S_MIN_PERIOD_SIZE,    .periods_max =      FM_I2S_MAX_PERIOD_SIZE,    .fifo_size =        0,};#define SOC_HIGH_USE_RATE        (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000)#define SOC_HIGH_USE_RATE_MIN        8000#define SOC_HIGH_USE_RATE_MAX       192000#define SOC_HIGH_USE_CHANNELS_MIN    1#define SOC_HIGH_USE_CHANNELS_MAX    8*/    /*pr_warn("mtk_pcm_fm_i2s_open\n");*/    runtime->hw = mtk_fm_i2s_hardware;    memcpy((void *)(&(runtime->hw)), (void *)&mtk_fm_i2s_hardware ,           sizeof(struct snd_pcm_hardware));/*static struct snd_pcm_hw_constraint_list fm_i2s_constraints_sample_rates = {    .count = ARRAY_SIZE(soc_fm_supported_sample_rates),    .list = soc_fm_supported_sample_rates,    .mask = 0,};const unsigned int soc_fm_supported_sample_rates[3] = {    32000, 44100, 48000};支持的采样率*/    ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,                     &fm_i2s_constraints_sample_rates);    ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);    if (ret < 0)        pr_warn("snd_pcm_hw_constraint_integer failed\n");    if (ret < 0) {        pr_err("mtk_pcm_fm_i2s_close\n");        mtk_pcm_fm_i2s_close(substream);        return ret;    }

2.2、mtk_pcm_fm_i2s_prepare

static int mtk_pcm_fm_i2s_prepare(struct snd_pcm_substream *substream){    AudioDigtalI2S m2ndI2SInAttribute;    struct snd_pcm_runtime *runtime = substream->runtime;    pr_warn("%s rate = %d\n", __func__, runtime->rate);    if (mPrepareDone == false) {        /* mtk_wcn_cmb_stub_audio_ctrl((CMB_STUB_AIF_X)CMB_STUB_AIF_3);//temp mark for early porting *//*外挂smart pa,FM切到外放播放无声1.公版默认的行为,headset和speaker 都是走PMIC这边输出,也就是interconnection对应的O03,O04.2.当外挂Smart PA后,Smart PA连接的I2S为I2S0,I2S3,output 的interconnection 为O00,O01;3.FM 从headset 切换到speaker 后,headset走的还是PMIC(O03,O04),而speaker 走的是Smart PA,所以FM driver文件里面的interconnection 需要增加O00,O01的设置4.如果还有其他应用场景,同样会出现切换smart PA后,没有声音,请首先确认interconnection是否有正确的设置,具体的修改是根据应用场景走的是哪一只driver 文件,在对应的driver 文件里面prepare函数添加O00,O01的interconnection。在mt_soc_pcm_dl1_i2s0Dl1.c的start函数中添加O00,O01的interconnection后,fm切换speaker可以使用了我们公版默认的行为,,FM 从headset 切到speaker 后,把headset 关闭只让speaker 输出,如果贵司想让一起输出,找到关闭headset地方打开即可*/        /* interconnection setting */        SetConnection(Soc_Aud_InterCon_Connection, Soc_Aud_InterConnectionInput_I00,                  Soc_Aud_InterConnectionOutput_O13);        SetConnection(Soc_Aud_InterCon_Connection, Soc_Aud_InterConnectionInput_I01,                  Soc_Aud_InterConnectionOutput_O14);        SetConnection(Soc_Aud_InterCon_Connection, Soc_Aud_InterConnectionInput_I10,                  Soc_Aud_InterConnectionOutput_O03);        SetConnection(Soc_Aud_InterCon_Connection, Soc_Aud_InterConnectionInput_I11,                  Soc_Aud_InterConnectionOutput_O04);        /* Set HW_GAIN */        SetHwDigitalGainMode(Soc_Aud_Hw_Digital_Gain_HW_DIGITAL_GAIN1, runtime->rate,                     0x40);        SetHwDigitalGainEnable(Soc_Aud_Hw_Digital_Gain_HW_DIGITAL_GAIN1, true);        SetHwDigitalGain(mfm_i2s_Volume, Soc_Aud_Hw_Digital_Gain_HW_DIGITAL_GAIN1);        /* start I2S DAC out */        if (GetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_DAC) == false) {            SetI2SDacOut(runtime->rate, false, Soc_Aud_I2S_WLEN_WLEN_16BITS);            SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_DAC, true);            SetI2SDacEnable(true);        } else            SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_DAC, true);        if (GetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_IN_2) == false) {            /* set merge interface */            SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_IN_2, true);            /* Config 2nd I2S IN */            memset_io((void *)&m2ndI2SInAttribute, 0, sizeof(m2ndI2SInAttribute));            m2ndI2SInAttribute.mLR_SWAP = Soc_Aud_LR_SWAP_NO_SWAP;            m2ndI2SInAttribute.mI2S_IN_PAD_SEL = false; /* I2S_IN_FROM_CONNSYS */            m2ndI2SInAttribute.mI2S_SLAVE = Soc_Aud_I2S_SRC_SLAVE_MODE;            m2ndI2SInAttribute.mI2S_SAMPLERATE = 32000;            m2ndI2SInAttribute.mINV_LRCK = Soc_Aud_INV_LRCK_NO_INVERSE;            m2ndI2SInAttribute.mI2S_FMT = Soc_Aud_I2S_FORMAT_I2S;            m2ndI2SInAttribute.mI2S_WLEN = Soc_Aud_I2S_WLEN_WLEN_16BITS;            Set2ndI2SIn(&m2ndI2SInAttribute);            if (runtime->rate == 48000)                SetI2SASRCConfig(true, 48000);  /* Covert from 32000 Hz to 48000 Hz */            else                SetI2SASRCConfig(true, 44100);  /* Covert from 32000 Hz to 44100 Hz */            SetI2SASRCEnable(true);            Set2ndI2SInEnable(true);        } else            SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_IN_2, true);        EnableAfe(true);        if (GetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_DAC) == true)            SetI2SADDAEnable(true);        mPrepareDone = true;    }    return 0;}

2.3、mtk_pcm_fm_i2s_close

static int mtk_pcm_fm_i2s_close(struct snd_pcm_substream *substream){    struct snd_pcm_runtime *runtime = substream->runtime;    pr_warn("%s rate = %d\n", __func__, runtime->rate);    /* mtk_wcn_cmb_stub_audio_ctrl((CMB_STUB_AIF_X)CMB_STUB_AIF_0);//temp mark for early porting */    SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_IN_2, false);    if (GetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_IN_2) == false) {        SetI2SASRCEnable(false);        SetI2SASRCConfig(false, 0); /* Setting to bypass ASRC */        Set2ndI2SInEnable(false);    }    SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_DAC, false);    if (GetI2SDacEnable() == false) {        SetI2SADDAEnable(false);        SetI2SDacEnable(false);    }    /* interconnection setting */    SetConnection(Soc_Aud_InterCon_DisConnect, Soc_Aud_InterConnectionInput_I00,              Soc_Aud_InterConnectionOutput_O13);    SetConnection(Soc_Aud_InterCon_DisConnect, Soc_Aud_InterConnectionInput_I01,              Soc_Aud_InterConnectionOutput_O14);    SetConnection(Soc_Aud_InterCon_DisConnect, Soc_Aud_InterConnectionInput_I10,              Soc_Aud_InterConnectionOutput_O03);    SetConnection(Soc_Aud_InterCon_DisConnect, Soc_Aud_InterConnectionInput_I11,              Soc_Aud_InterConnectionOutput_O04);    EnableAfe(false);    AudDrv_I2S_Clk_Off();    AudDrv_Clk_Off();    mPrepareDone = false;    SetFMEnableFlag(false);    return 0;}

2.3、mtk_pcm_fm_i2s_close

static int mtk_pcm_fm_i2s_close(struct snd_pcm_substream *substream){    struct snd_pcm_runtime *runtime = substream->runtime;    pr_warn("%s rate = %d\n", __func__, runtime->rate);    /* mtk_wcn_cmb_stub_audio_ctrl((CMB_STUB_AIF_X)CMB_STUB_AIF_0);//temp mark for early porting */    SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_IN_2, false);    if (GetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_IN_2) == false) {        SetI2SASRCEnable(false);        SetI2SASRCConfig(false, 0); /* Setting to bypass ASRC */        Set2ndI2SInEnable(false);    }    SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_DAC, false);    if (GetI2SDacEnable() == false) {        SetI2SADDAEnable(false);        SetI2SDacEnable(false);    }    /* interconnection setting */    SetConnection(Soc_Aud_InterCon_DisConnect, Soc_Aud_InterConnectionInput_I00,              Soc_Aud_InterConnectionOutput_O13);    SetConnection(Soc_Aud_InterCon_DisConnect, Soc_Aud_InterConnectionInput_I01,              Soc_Aud_InterConnectionOutput_O14);    SetConnection(Soc_Aud_InterCon_DisConnect, Soc_Aud_InterConnectionInput_I10,              Soc_Aud_InterConnectionOutput_O03);    SetConnection(Soc_Aud_InterCon_DisConnect, Soc_Aud_InterConnectionInput_I11,              Soc_Aud_InterConnectionOutput_O04);    EnableAfe(false);    AudDrv_I2S_Clk_Off();    AudDrv_Clk_Off();    mPrepareDone = false;    SetFMEnableFlag(false);    return 0;}

Asoc 还有很多platform,和上面注册方式都一致,只是在ops方面不同,在此就不一一分析了。

3、Platform Dai

kernel-3.18/sound/soc/mediatek/mt_soc_audio_v3/mt_soc_dai_stub.c
这里写图片描述

snd_soc_register_component(&pdev->dev, &mt_dai_component,                    mtk_dai_stub_dai, ARRAY_SIZE(mtk_dai_stub_dai));
int snd_soc_register_component(struct device *dev,                   const struct snd_soc_component_driver *cmpnt_drv,                   struct snd_soc_dai_driver *dai_drv,                   int num_dai){...    cmpnt->ignore_pmdown_time = true;    cmpnt->registered_as_component = true;    ret = snd_soc_register_dais(cmpnt, dai_drv, num_dai, true);...    snd_soc_component_add(cmpnt);    return 0;}
static int snd_soc_register_dais(struct snd_soc_component *component,    struct snd_soc_dai_driver *dai_drv, size_t count,    bool legacy_dai_naming){...    for (i = 0; i < count; i++) {        if (count == 1 && legacy_dai_naming) {            dai->name = fmt_single_name(dev, &dai->id);        } else {            dai->name = fmt_multiple_name(dev, &dai_drv[i]);            if (dai_drv[i].id)                dai->id = dai_drv[i].id;            else                dai->id = i;        }        }        dai->component = component;        dai->dev = dev;        dai->driver = &dai_drv[i]; //dai->driver = &dai_drv[i] = mtk_dai_stub_dai        list_add(&dai->list, &component->dai_list);//list_add(&cmpnt->list, &component_list)    }...    return 0;}

Platform Dai 通过snd_soc_register_component进行注册,将数组mtk_dai_stub_dai[]传入,再通过snd_soc_register_dais将所有的PCM(platform)(如播放、录音、通话等)循环加入dai list里面。

下面看下mtk_dai_stub_dai数组。

static struct snd_soc_dai_driver mtk_dai_stub_dai[] = {    {        .playback = {            .stream_name = MT_SOC_DL1_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_192000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 192000,        },        .name = MT_SOC_DL1DAI_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .capture = {            .stream_name = MT_SOC_UL1_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_192000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 192000,        },        .name = MT_SOC_UL1DAI_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .capture = {            .stream_name = MT_SOC_TDM_CAPTURE_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SNDRV_PCM_FMTBIT_S16_LE,            .channels_min = 1,            .channels_max = 8,            .rate_min = 8000,            .rate_max = 48000,        },        .name = MT_SOC_TDMRX_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .playback = {            .stream_name = MT_SOC_HDMI_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_192000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 8,            .rate_min = 8000,            .rate_max = 192000,        },        .capture = {            .stream_name = MT_SOC_HDMI_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_192000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 8,            .rate_min = 8000,            .rate_max = 192000,        },        .name = MT_SOC_HDMI_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .playback = {            .stream_name = MT_SOC_VOICE_MD1_BT_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 32000,        },        .name = MT_SOC_VOICE_MD1_BT_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .playback = {            .stream_name = MT_SOC_VOICE_MD2_BT_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 32000,        },        .name = MT_SOC_VOICE_MD2_BT_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .playback = {            .stream_name = MT_SOC_VOIP_BT_OUT_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 48000,        },        .name = MT_SOC_VOIP_CALL_BT_OUT_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .capture = {            .stream_name = MT_SOC_VOIP_BT_IN_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 48000,        },        .name = MT_SOC_VOIP_CALL_BT_IN_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .playback = {            .stream_name = MT_SOC_FM_I2S2_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000 ,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 48000,        },        .name = "FM_I2S2_OUT",        .ops = &mtk_dai_stub_ops,    },    {        .capture = {            .stream_name = MT_SOC_FM_I2S2_RECORD_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 48000,        },        .name = "FM_I2S2_IN",        .ops = &mtk_dai_stub_ops,    },    {        .playback = {            .stream_name = MT_SOC_VOICE_MD1_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SND_SOC_STD_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 32000,        },        .capture = {            .stream_name = MT_SOC_VOICE_MD1_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SND_SOC_STD_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 32000,        },        .name = MT_SOC_VOICE_MD1_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .playback = {            .stream_name = MT_SOC_VOICE_MD2_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SND_SOC_STD_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 32000,        },        .capture = {            .stream_name = MT_SOC_VOICE_MD2_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SND_SOC_STD_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 32000,        },        .name = MT_SOC_VOICE_MD2_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .playback = {            .stream_name = MT_SOC_ULDLLOOPBACK_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 48000,        },        .capture = {            .stream_name = MT_SOC_ULDLLOOPBACK_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 48000,        },        .name = MT_SOC_ULDLLOOPBACK_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .playback = {            .stream_name = MT_SOC_I2S0_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_192000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 192000,        },        .capture = {            .stream_name = MT_SOC_I2S0_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_192000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 192000,        },        .name = MT_SOC_I2S0_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .playback = {            .stream_name = MT_SOC_I2SDL1_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_192000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 192000,        },        .name = MT_SOC_I2S0DL1_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .capture = {            .stream_name = MT_SOC_DL1_AWB_RECORD_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_192000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 48000,        },        .name = MT_SOC_DL1AWB_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .playback = {            .stream_name = MT_SOC_MRGRX_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 48000,        },        .capture = {            .stream_name = MT_SOC_MRGRX_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 48000,        },        .name = MT_SOC_MRGRX_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .playback = {            .stream_name = MT_SOC_MRGRX_CAPTURE_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 48000,        },        .capture = {            .stream_name = MT_SOC_MRGRX_CAPTURE_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 48000,        },        .name = MT_SOC_MRGRXCAPTURE_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .playback = {            .stream_name = MT_SOC_FM_MRGTX_STREAM_NAME,            .rates = SNDRV_PCM_RATE_44100,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 44100,            .rate_max = 44100,        },        .name = MT_SOC_FM_MRGTX_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .capture = {            .stream_name = MT_SOC_UL1DATA2_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SNDRV_PCM_FMTBIT_S16_LE,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 48000,        },        .name = MT_SOC_UL2DAI_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .capture = {            .stream_name = MT_SOC_I2S0AWB_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SNDRV_PCM_FMTBIT_S16_LE,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 48000,        },        .name = MT_SOC_I2S0AWBDAI_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .capture = {            .stream_name = MT_SOC_MODADCI2S_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SNDRV_PCM_FMTBIT_S16_LE,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 48000,        },        .name = MT_SOC_MODADCI2SDAI_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .capture = {            .stream_name = MT_SOC_ADC2AWB_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_192000,            .formats = SNDRV_PCM_FMTBIT_S16_LE,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 192000,        },        .name = MT_SOC_ADC2AWBDAI_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .capture = {            .stream_name = MT_SOC_IO2DAI_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SNDRV_PCM_FMTBIT_S16_LE,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 48000,        },        .name = MT_SOC_IO2DAIDAI_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .playback = {            .stream_name = MT_SOC_HP_IMPEDANCE_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 8,            .rate_min = 8000,            .rate_max = 48000,        },        .name = MT_SOC_HP_IMPEDANCE_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .playback = {            .stream_name = MT_SOC_FM_I2S_PLAYBACK_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 48000,        },        .capture = {            .stream_name = MT_SOC_FM_I2S_PLAYBACK_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 48000,        },        .name = MT_SOC_FM_I2S_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .playback = {            .stream_name = MT_SOC_FM_I2S_CAPTURE_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 48000,        },        .capture = {            .stream_name = MT_SOC_FM_I2S_CAPTURE_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 48000,        },        .name = MT_SOC_FM_I2S_CAPTURE_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .playback = {            .stream_name = MT_SOC_OFFLOAD_GDMA_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_48000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 48000,        },        .compress_dai = 1,        .name = MT_SOC_OFFLOAD_GDMA_NAME,        .ops = &mtk_dai_stub_ops,    },    {        .playback = {            .stream_name = MT_SOC_DL2_STREAM_NAME,            .rates = SNDRV_PCM_RATE_8000_192000,            .formats = SND_SOC_ADV_MT_FMTS,            .channels_min = 1,            .channels_max = 2,            .rate_min = 8000,            .rate_max = 192000,        },        .name = MT_SOC_DL2DAI_NAME,        .ops = &mtk_dai_stub_ops,    },};

4、Codec和Codec dai

kernel-3.18/sound/soc/mediatek/mt_soc_audio_v3/mt_soc_codec_63xx.c
Codec和Codec dai都是在一个文件注册完成的,下面进行分析mt_soc_codec_63xx.c文件。

这里写图片描述

从上图可知snd_soc_register_codec分别注册了codec和codec dai,下面从这两个方面进行分析。

4.1、Codec(soc_mtk_codec)

static struct snd_soc_codec_driver soc_mtk_codec = {    .probe = mt6331_codec_probe,    .remove = mt6331_codec_remove,    .read = mt6331_read,    .write = mt6331_write,      /* use add control to replace */      /* .controls = mt6331_snd_controls,  */      /* .num_controls = ARRAY_SIZE(mt6331_snd_controls),  */    .dapm_widgets = mt6331_dapm_widgets,    .num_dapm_widgets = ARRAY_SIZE(mt6331_dapm_widgets),    .dapm_routes = mtk_audio_map,    .num_dapm_routes = ARRAY_SIZE(mtk_audio_map),};

下面进行分析soc_mtk_codec结构体

4.1.1、mt6331_codec_probe

static int mt6331_codec_probe(struct snd_soc_codec *codec){    struct snd_soc_dapm_context *dapm = &codec->dapm;    pr_warn("%s()\n", __func__);    if (mInitCodec == true)        return 0;    pin_extspkamp = pin_extspkamp_2 = pin_vowclk = pin_audmiso = pin_rcvspkswitch = 0;    pin_mode_extspkamp = pin_mode_extspkamp_2 = pin_mode_vowclk = pin_mode_audmiso =        pin_mode_rcvspkswitch = 0;    snd_soc_dapm_new_controls(dapm, mt6331_dapm_widgets, ARRAY_SIZE(mt6331_dapm_widgets));    snd_soc_dapm_add_routes(dapm, mtk_audio_map, ARRAY_SIZE(mtk_audio_map));    /* add codec controls */    snd_soc_add_codec_controls(codec, mt6331_snd_controls, ARRAY_SIZE(mt6331_snd_controls));    snd_soc_add_codec_controls(codec, mt6331_UL_Codec_controls,                   ARRAY_SIZE(mt6331_UL_Codec_controls));    snd_soc_add_codec_controls(codec, mt6331_Voice_Switch, ARRAY_SIZE(mt6331_Voice_Switch));    snd_soc_add_codec_controls(codec, mt6331_pmic_Test_controls,                   ARRAY_SIZE(mt6331_pmic_Test_controls));#ifdef CONFIG_MTK_SPEAKER    snd_soc_add_codec_controls(codec, mt6331_snd_Speaker_controls,                   ARRAY_SIZE(mt6331_snd_Speaker_controls));#endif    snd_soc_add_codec_controls(codec, Audio_snd_auxadc_controls,                   ARRAY_SIZE(Audio_snd_auxadc_controls));    /* here to set  private data */    mCodec_data = kzalloc(sizeof(mt6331_Codec_Data_Priv), GFP_KERNEL);    if (!mCodec_data) {        pr_warn("Failed to allocate private data\n");        return -ENOMEM;    }    snd_soc_codec_set_drvdata(codec, mCodec_data);    memset((void *)mCodec_data, 0, sizeof(mt6331_Codec_Data_Priv));    mt6331_codec_init_reg(codec);    InitCodecDefault();    mInitCodec = true;    return 0;}

上面代码为probe函数,里面有很多重要知识,下面进行一一分析。

4.1.1.1、snd_soc_dapm_new_controls和snd_soc_dapm_add_routes

DAMP(Dynamic Audio Power Management)是动态音频电源管理,是为了让移动设备在任何时候都工作在最小功耗下。DAPM对用户空间的应用程序来说是透明的,所有与电源相关的开关都在ASoc core中完成。用户空间的应用程序无需对代码做出修改,也无需重新编译,DAPM根据当前激活的音频流(playback/capture)和声卡中的mixer等的配置来决定那些音频控件的电源开关被打开或关闭。
snd_soc_dapm_new_controls: snd_soc_dapm_new_controls会调用snd_soc_dapm_new_controls函数,snd_soc_dapm_new_control只是创建widget的第一步,它为每个widget分配内存,初始化必要的字段,然后把这些widget挂在代表声卡的snd_soc_card的widgets链表字段中。
snd_soc_dapm_route: 系统中注册的各种widget需要互相连接在一起才能协调工作,连接关系通过snd_soc_dapm_route结构来定义。通常,所有的路径信息会用一个snd_soc_dapm_route结构数组来定义。和widget一样,路径信息也分别存在与codec驱动,machine驱动和platform驱动中,我们一样有两种方式来注册音频路径信息:
(1)通过snd_soc_codec_driver/snd_soc_platform_driver/snd_soc_card结构中的dapm_routes和num_dapm_routes字段;
(2)在codec、platform的的probe回调中主动注册音频路径,machine驱动中则通过snd_soc_dai_link结构的init回调函数来注册音频路径;
两种方法最终都是通过调用snd_soc_dapm_add_routes函数来完成音频路径的注册工作的。

snd_soc_dapm_new_controls(dapm, mt6331_dapm_widgets, ARRAY_SIZE(mt6331_dapm_widgets))//mt6331_dapm_widgets数组定义如下:static const struct snd_soc_dapm_widget mt6331_dapm_widgets[] = {    /* Outputs */    SND_SOC_DAPM_OUTPUT("EARPIECE"),//听筒    SND_SOC_DAPM_OUTPUT("HEADSET"),//耳机    SND_SOC_DAPM_OUTPUT("SPEAKER"),//喇叭    /*       SND_SOC_DAPM_MUX_E("VOICE_Mux_E", SND_SOC_NOPM, 0, 0  , &mt6331_Voice_Switch, codec_enable_rx_bias,       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |       SND_SOC_DAPM_PRE_REG | SND_SOC_DAPM_POST_REG),     */};
snd_soc_dapm_add_routes(dapm, mtk_audio_map, ARRAY_SIZE(mtk_audio_map))//mtk_audio_map数组定义如下static const struct snd_soc_dapm_route mtk_audio_map[] = {    {"VOICE_Mux_E", "Voice Mux", "SPEAKER PGA"},};

4.1.1.2、snd_soc_add_codec_controls

snd_soc_add_codec_controls:该函数首先通过 snd_soc_cnew(创建新的control) 函数将这些来自snd_ctl_new1的成员组织到新分配的snd_kcontrol结构体成员中,然后调用 snd_ctl_add 函数,将这些音频控件添加到声卡对象(struct snd_card)的控件列表中(card->controls),同时为这个kcontrol分配一个唯一的id号。
对于每个控件,我们需要定义一个和它对应的snd_kcontrol_new结构,这些snd_kcontrol_new结构会在声卡的初始化阶段,通过snd_soc_add_codec_controls函数注册到系统中,用户空间就可以通过amixer或alsamixer等工具查看和设定这些控件的状态。

4.1.1.2.1、mt6331_snd_controls
snd_soc_add_codec_controls(codec, mt6331_snd_controls, ARRAY_SIZE(mt6331_snd_controls));//mt6331_snd_controls数组定义如下//////////////////////////////////////////////////////////*static int Audio_AmpR_Get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){    pr_warn("Audio_AmpR_Get = %d\n",mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_HEADSETR]);    ucontrol->value.integer.value[0] = mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_HEADSETR];//选择设备电源类型    return 0;}static int Audio_AmpR_Set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){    mutex_lock(&Ana_Ctrl_Mutex);    pr_warn("%s()\n", __func__);    if ((ucontrol->value.integer.value[0] == true)        && (mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_HEADSETR] == false))//判断选择了正确的耳机设备电源类型         {        Audio_Amp_Change(AUDIO_ANALOG_CHANNELS_RIGHT1, true);//此函数主要功能:1、上电 2、设置pmic模拟控制寄存器 3、耳机音量设置        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_HEADSETR] =            ucontrol->value.integer.value[0];    } else if ((ucontrol->value.integer.value[0] == false)           && (mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_HEADSETR] ==               true)) {        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_HEADSETR] =            ucontrol->value.integer.value[0];        Audio_Amp_Change(AUDIO_ANALOG_CHANNELS_RIGHT1, false);    }    mutex_unlock(&Ana_Ctrl_Mutex);    return 0;}*//////////////////////////////////////////////////////////    SOC_ENUM_EXT("Audio_Amp_R_Switch", Audio_DL_Enum[0], Audio_AmpR_Get, Audio_AmpR_Set),//耳机右声道控件开关    //左声道设置与上面右声道设置大致一样,耳机电源类型选择不同    SOC_ENUM_EXT("Audio_Amp_L_Switch", Audio_DL_Enum[1], Audio_AmpL_Get, Audio_AmpL_Set),//耳机左声道控件开关//////////////////////////////////////////////////////////*//听筒static int Voice_Amp_Get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){    pr_warn("Voice_Amp_Get = %d\n",           mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_EARPIECEL]);    ucontrol->value.integer.value[0] =        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_EARPIECEL];//选择听筒电源类型    return 0;}static int Voice_Amp_Set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){    mutex_lock(&Ana_Ctrl_Mutex);    pr_warn("%s()\n", __func__);    if ((ucontrol->value.integer.value[0] == true)        && (mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_EARPIECEL] == false)) {        Voice_Amp_Change(true);//1、上电 2、设置pmic寄存器 3、        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_EARPIECEL] =            ucontrol->value.integer.value[0];    } else if ((ucontrol->value.integer.value[0] == false)           && (mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_EARPIECEL] ==               true)) {        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_EARPIECEL] =            ucontrol->value.integer.value[0];        Voice_Amp_Change(false);    }    mutex_unlock(&Ana_Ctrl_Mutex);    return 0;}*//////////////////////////////////////////////////////////       SOC_ENUM_EXT("Voice_Amp_Switch", Audio_DL_Enum[2], Voice_Amp_Get, Voice_Amp_Set),//听筒控件开关//////////////////////////////////////////////////////////*static int Speaker_Amp_Get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){    pr_warn("%s()\n", __func__);    ucontrol->value.integer.value[0] =        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_SPEAKERL];//选择喇叭电源类型    return 0;}static int Speaker_Amp_Set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){    pr_warn("%s() value = %ld\n ", __func__, ucontrol->value.integer.value[0]);    if ((ucontrol->value.integer.value[0] == true)        && (mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_SPEAKERL] == false)) {        Speaker_Amp_Change(true);//1、上电 2、设置pmic 3、AB类和D类模式选择 4、设置喇叭gain值  (注:3和4只在用内置PA时设置)        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_SPEAKERL] =            ucontrol->value.integer.value[0];    } else if ((ucontrol->value.integer.value[0] == false)           && (mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_SPEAKERL] ==               true)) {        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_SPEAKERL] =            ucontrol->value.integer.value[0];        Speaker_Amp_Change(false);    }    return 0;}*//////////////////////////////////////////////////////////    SOC_ENUM_EXT("Speaker_Amp_Switch", Audio_DL_Enum[3], Speaker_Amp_Get, Speaker_Amp_Set),//喇叭控件开关//////////////////////////////////////////////////////////*static int Headset_Speaker_Amp_Get(struct snd_kcontrol *kcontrol,                   struct snd_ctl_elem_value *ucontrol){    pr_warn("%s()\n", __func__);    ucontrol->value.integer.value[0] =        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_SPEAKER_HEADSET_R];//选择耳机和喇叭同时工作电源类型    return 0;}static int Headset_Speaker_Amp_Set(struct snd_kcontrol *kcontrol,                   struct snd_ctl_elem_value *ucontrol){    /* struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); */    pr_warn("%s() gain = %lu\n ", __func__, ucontrol->value.integer.value[0]);    if ((ucontrol->value.integer.value[0] == true)        && (mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_SPEAKER_HEADSET_R] ==        false)) {        Headset_Speaker_Amp_Change(true);//1、打开电源 2、设置pmic寄存器 3、选择AB类 、D类和receiver模式 4、耳机音量设置 5、喇叭gain值设置 (注:3和5只在内置PA设置)        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_SPEAKER_HEADSET_R] =            ucontrol->value.integer.value[0];    } else if ((ucontrol->value.integer.value[0] == false)           && (mCodec_data->               mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_SPEAKER_HEADSET_R] == true)) {        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_SPEAKER_HEADSET_R] =            ucontrol->value.integer.value[0];        Headset_Speaker_Amp_Change(false);    }    return 0;}*//////////////////////////////////////////////////////////    SOC_ENUM_EXT("Headset_Speaker_Amp_Switch", Audio_DL_Enum[4], Headset_Speaker_Amp_Get,             Headset_Speaker_Amp_Set),//耳机喇叭控件开关    SOC_ENUM_EXT("Headset_PGAL_GAIN", Audio_DL_Enum[5], Headset_PGAL_Get, Headset_PGAL_Set),    SOC_ENUM_EXT("Headset_PGAR_GAIN", Audio_DL_Enum[6], Headset_PGAR_Get, Headset_PGAR_Set),    SOC_ENUM_EXT("Handset_PGA_GAIN", Audio_DL_Enum[7], Handset_PGA_Get, Handset_PGA_Set),    SOC_ENUM_EXT("Lineout_PGAR_GAIN", Audio_DL_Enum[8], Lineout_PGAR_Get, Lineout_PGAR_Set),    SOC_ENUM_EXT("AUD_CLK_BUF_Switch", Audio_DL_Enum[10], Aud_Clk_Buf_Get, Aud_Clk_Buf_Set),//////////////////////////////////////////////////////////*static int Ext_Speaker_Amp_Get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){    pr_debug("%s()\n", __func__);    ucontrol->value.integer.value[0] =        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_EXTSPKAMP];//选择外置PA电源类型    return 0;}static int Ext_Speaker_Amp_Set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){    pr_debug("%s() gain = %ld\n ", __func__, ucontrol->value.integer.value[0]);    if (ucontrol->value.integer.value[0]) {        Ext_Speaker_Amp_Change(true);//外置PA使能脚gpio控制        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_EXTSPKAMP] =            ucontrol->value.integer.value[0];    } else {        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_OUT_EXTSPKAMP] =            ucontrol->value.integer.value[0];        Ext_Speaker_Amp_Change(false);    }    return 0;}*//////////////////////////////////////////////////////////    SOC_ENUM_EXT("Ext_Speaker_Amp_Switch", Audio_DL_Enum[11], Ext_Speaker_Amp_Get,             Ext_Speaker_Amp_Set),//外置PA控件开关//////////////////////////////////////////////////////////*static int Receiver_Speaker_Switch_Get(struct snd_kcontrol *kcontrol,                       struct snd_ctl_elem_value *ucontrol){    pr_debug("%s() : %d\n", __func__,           mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_RECEIVER_SPEAKER_SWITCH]);    ucontrol->value.integer.value[0] =        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_RECEIVER_SPEAKER_SWITCH];//选择听筒喇叭切换电源类型    return 0;}static int Receiver_Speaker_Switch_Set(struct snd_kcontrol *kcontrol,                       struct snd_ctl_elem_value *ucontrol){    pr_debug("%s()\n", __func__);    if ((ucontrol->value.integer.value[0] == true)        && (mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_RECEIVER_SPEAKER_SWITCH] ==        false)) {        Receiver_Speaker_Switch_Change(true);//根据传进去的参数判断,ture,使能听筒,关喇叭  false,使能喇叭,关听筒。 例如通话时开关扬声器操作        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_RECEIVER_SPEAKER_SWITCH] =            ucontrol->value.integer.value[0];    } else if ((ucontrol->value.integer.value[0] == false)           && (mCodec_data->               mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_RECEIVER_SPEAKER_SWITCH] ==               true)) {        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_RECEIVER_SPEAKER_SWITCH] =            ucontrol->value.integer.value[0];        Receiver_Speaker_Switch_Change(false);    }    return 0;}*//////////////////////////////////////////////////////////          SOC_ENUM_EXT("Receiver_Speaker_Switch", Audio_DL_Enum[11], Receiver_Speaker_Switch_Get,             Receiver_Speaker_Switch_Set),//听筒喇叭切换控件开关    SOC_SINGLE_EXT("Audio HP Impedance", SND_SOC_NOPM, 0, 512, 0, Audio_Hp_Impedance_Get,               Audio_Hp_Impedance_Set),};
4.1.1.2.2、mt6331_UL_Codec_controls

这里写图片描述
这里写图片描述

snd_soc_add_codec_controls(codec, mt6331_UL_Codec_controls,                   ARRAY_SIZE(mt6331_UL_Codec_controls));//mt6331_UL_Codec_controls数组定义如下static const struct snd_kcontrol_new mt6331_UL_Codec_controls[] = {/////////////////////////////////////////////////////////   /*static int Audio_ADC1_Get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){    pr_warn("Audio_ADC1_Get = %d\n",           mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_IN_ADC1]);    ucontrol->value.integer.value[0] =        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_IN_ADC1];//选择ADC1电源类型    return 0;}static int Audio_ADC1_Set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){    pr_warn("%s()\n", __func__);    mutex_lock(&Ana_Power_Mutex);    if (ucontrol->value.integer.value[0]) {    //从上图可知MIC代码定义六中模式,本代码有四种模式ACC、DCC、DMIC、DCCECMDIFF可选。定义手机mic和耳机mic模式,具体根据硬件设计有关。定义mic模式代码在vendor/mediatek/proprietary/custom/project_name/hal/audioflinger/audio/audio_custom_exp.h里//1 : ACC mode (Traditional)//2 : DCC mode without internal bias circuit (MEMS Mic) (default) //3 : Digital MIC        if (mAudio_Analog_Mic1_mode == AUDIO_ANALOGUL_MODE_ACC)            TurnOnADcPowerACC(AUDIO_ANALOG_DEVICE_IN_ADC1, true);        else if (mAudio_Analog_Mic1_mode == AUDIO_ANALOGUL_MODE_DCC)            TurnOnADcPowerDCC(AUDIO_ANALOG_DEVICE_IN_ADC1, true, 0);        else if (mAudio_Analog_Mic1_mode == AUDIO_ANALOGUL_MODE_DMIC)            TurnOnADcPowerDmic(AUDIO_ANALOG_DEVICE_IN_ADC1, true);        else if (mAudio_Analog_Mic1_mode == AUDIO_ANALOGUL_MODE_DCCECMDIFF)            TurnOnADcPowerDCC(AUDIO_ANALOG_DEVICE_IN_ADC1, true, 1);        else if (mAudio_Analog_Mic1_mode == AUDIO_ANALOGUL_MODE_DCCECMSINGLE)            TurnOnADcPowerDCC(AUDIO_ANALOG_DEVICE_IN_ADC1, true, 2);        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_IN_ADC1] =            ucontrol->value.integer.value[0];    } else {        mCodec_data->mAudio_Ana_DevicePower[AUDIO_ANALOG_DEVICE_IN_ADC1] =            ucontrol->value.integer.value[0];        if (mAudio_Analog_Mic1_mode == AUDIO_ANALOGUL_MODE_ACC)            TurnOnADcPowerACC(AUDIO_ANALOG_DEVICE_IN_ADC1, false);        else if (mAudio_Analog_Mic1_mode == AUDIO_ANALOGUL_MODE_DCC)            TurnOnADcPowerDCC(AUDIO_ANALOG_DEVICE_IN_ADC1, false, 0);        else if (mAudio_Analog_Mic1_mode == AUDIO_ANALOGUL_MODE_DMIC)            TurnOnADcPowerDmic(AUDIO_ANALOG_DEVICE_IN_ADC1, false);        else if (mAudio_Analog_Mic1_mode == AUDIO_ANALOGUL_MODE_DCCECMDIFF)            TurnOnADcPowerDCC(AUDIO_ANALOG_DEVICE_IN_ADC1, false, 1);        else if (mAudio_Analog_Mic1_mode == AUDIO_ANALOGUL_MODE_DCCECMSINGLE)            TurnOnADcPowerDCC(AUDIO_ANALOG_DEVICE_IN_ADC1, false, 2);    }    mutex_unlock(&Ana_Power_Mutex);    return 0;}*//////////////////////////////////////////////////////////       SOC_ENUM_EXT("Audio_ADC_1_Switch", Audio_UL_Enum[0], Audio_ADC1_Get, Audio_ADC1_Set),//主麦克风控件开关    SOC_ENUM_EXT("Audio_ADC_2_Switch", Audio_UL_Enum[1], Audio_ADC2_Get, Audio_ADC2_Set),//耳机麦克风控件开关,和ADC1基本相同,只是adc通道不同    SOC_ENUM_EXT("Audio_ADC_3_Switch", Audio_UL_Enum[2], Audio_ADC3_Get, Audio_ADC3_Set),//不支持    SOC_ENUM_EXT("Audio_ADC_4_Switch", Audio_UL_Enum[3], Audio_ADC4_Get, Audio_ADC4_Set),//不支持/////////////////////////////////////////////////////////   //Mixer可以混合多个输入到输出//Mux只能从多个输入里选择一个作为输出//Mixer当然也可以只从多个输入里选择一个作为输出/*static int Audio_PreAmp1_Get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){    pr_warn("%s() mCodec_data->mAudio_Ana_Mux[AUDIO_ANALOG_MUX_IN_PREAMP_1]; = %d\n", __func__,           mCodec_data->mAudio_Ana_Mux[AUDIO_ANALOG_MUX_IN_PREAMP_1]);    ucontrol->value.integer.value[0] =        mCodec_data->mAudio_Ana_Mux[AUDIO_ANALOG_MUX_IN_PREAMP_1];//选择前置放大器Mux    return 0;}static int Audio_PreAmp1_Set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){    pr_warn("%s()\n", __func__);    if (ucontrol->value.enumerated.item[0] > ARRAY_SIZE(PreAmp_Mux_function)) {        pr_err("return -EINVAL\n");        return -EINVAL;    }    mCodec_data->mAudio_Ana_Mux[AUDIO_ANALOG_MUX_IN_PREAMP_1] =        ucontrol->value.integer.value[0];/////static bool AudioPreAmp1_Sel(int Mul_Sel){    pr_warn("%s Mul_Sel = %d ", __func__, Mul_Sel);    if (Mul_Sel == 0)        Ana_Set_Reg(AUDENC_ANA_CON0, 0x0000, 0x00C0);   /* pinumx open */    else if (Mul_Sel == 1)        Ana_Set_Reg(AUDENC_ANA_CON0, 0x0040, 0x00C0);   /* AIN0 */    else if (Mul_Sel == 2)        Ana_Set_Reg(AUDENC_ANA_CON0, 0x0080, 0x00C0);   /* AIN1 */    else if (Mul_Sel == 3)        Ana_Set_Reg(AUDENC_ANA_CON0, 0x00C0, 0x00C0);   /* AIN2 */    else        pr_warn("AudioPreAmp1_Sel warning");    return true;}由上可知此函数设置差分输入AIN0、AIN1、AIN2,由上上图知是麦克风前置放大器设置AIN。所谓的差分输入形式。差分放大电路利用电路参数的对称性和负反馈作用,有效地稳定静态工作点,以放大差模信号抑制共模信号为显著特征,广泛应用于直接耦合电路和测量电路的输入级。下面来简单的介绍一下单端输入与差分输入。    在单端方式工作时,ADC转换的是单输入引脚对地的电压值。当增益为1时,测量的值就是输入的电压值;范围是0V到VREF;当增益增加时,输入的范围要相应的减小;    在差分方式工作时;ADC转换的是AIN+与AIN-两个引脚的差值;在增益为1时,测量的值等于(AIN+)-(AIN-),范围是-VREF到+VREF;当增益增加时,输入的范围要相应的减小。注意:在差分方式时所提的负压是指AIN-引脚的电压大于AIN+引脚的电压,实际输入到两个引脚的电压对地都必需是正的;例如:如果AIN+引脚输入的电压为0V,AIN-引脚的输入电压为1/2VREF时,差分的输入电压为(0V-1/2VREF) = -1/2VREF/////////AudioPreAmp1_Sel(mCodec_data->mAudio_Ana_Mux[AUDIO_ANALOG_MUX_IN_PREAMP_1]);    pr_warn("%s() done\n", __func__);    return 0;}*//////////////////////////////////////////////////////////       SOC_ENUM_EXT("Audio_Preamp1_Switch", Audio_UL_Enum[4], Audio_PreAmp1_Get,             Audio_PreAmp1_Set),//前置放大器控件开关,前置放大器是指置于信源与放大器级之间的电路或电子设备,是专为接受来自信源的微弱电压信号而设计的//////////////////////////////////////////////////////////*static int Audio_ADC1_Sel_Get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){    pr_warn("%s() = %d\n", __func__, mCodec_data->mAudio_Ana_Mux[AUDIO_ANALOG_MUX_IN_MIC1]);    ucontrol->value.integer.value[0] = mCodec_data->mAudio_Ana_Mux[AUDIO_ANALOG_MUX_IN_MIC1];//选择mic1 mux    return 0;}static int Audio_ADC1_Sel_Set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){    pr_warn("%s()\n", __func__);    if (ucontrol->value.enumerated.item[0] > ARRAY_SIZE(Adc_Input_Sel)) {        pr_err("return -EINVAL\n");        return -EINVAL;    }    if (ucontrol->value.integer.value[0] == 0)        Ana_Set_Reg(AUDENC_ANA_CON0, (0x0000 << 9), 0x0600);    /* pinumx sel */    else if (ucontrol->value.integer.value[0] == 1)        Ana_Set_Reg(AUDENC_ANA_CON0, (0x0001 << 9), 0x0600);    /* AIN0 */    else if (ucontrol->value.integer.value[0] == 2)        Ana_Set_Reg(AUDENC_ANA_CON0, (0x0002 << 9), 0x0600);    /* Left preamp */    else        pr_warn("%s() warning\n ", __func__);    pr_warn("%s() done\n", __func__);    mCodec_data->mAudio_Ana_Mux[AUDIO_ANALOG_MUX_IN_MIC1] = ucontrol->value.integer.value[0];    return 0;}*//////////////////////////////////////////////////////////    SOC_ENUM_EXT("Audio_ADC_1_Sel", Audio_UL_Enum[5], Audio_ADC1_Sel_Get, Audio_ADC1_Sel_Set),//mic1差分输入控件开关    SOC_ENUM_EXT("Audio_ADC_2_Sel", Audio_UL_Enum[6], Audio_ADC2_Sel_Get, Audio_ADC2_Sel_Set),//mic2差分输入控件开关    SOC_ENUM_EXT("Audio_ADC_3_Sel", Audio_UL_Enum[7], Audio_ADC3_Sel_Get, Audio_ADC3_Sel_Set),//不支持    SOC_ENUM_EXT("Audio_ADC_4_Sel", Audio_UL_Enum[8], Audio_ADC4_Sel_Get, Audio_ADC4_Sel_Set),//不支持    SOC_ENUM_EXT("Audio_PGA1_Setting", Audio_UL_Enum[9], Audio_PGA1_Get, Audio_PGA1_Set),//mic左PGA控件开关,从可编程增益放大器(PGA),是一种通用性很强的放大器,其放大倍数可以根据需要用程序进行控制,采用这种放大器,可通过程序调节放大倍数,使A/D转换器满量程信号达到均一化,因而大大提高测量精度。    SOC_ENUM_EXT("Audio_PGA2_Setting", Audio_UL_Enum[10], Audio_PGA2_Get, Audio_PGA2_Set),//mic右PGA控件开关    SOC_ENUM_EXT("Audio_PGA3_Setting", Audio_UL_Enum[11], Audio_PGA3_Get, Audio_PGA3_Set),//不支持    SOC_ENUM_EXT("Audio_PGA4_Setting", Audio_UL_Enum[12], Audio_PGA4_Get, Audio_PGA4_Set),//不支持//////////////////////////////////////////////////////////*static int Audio_MicSource1_Get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){    pr_warn("Audio_MicSource1_Get = %d\n",           mCodec_data->mAudio_Ana_Mux[AUDIO_MICSOURCE_MUX_IN_1]);    ucontrol->value.integer.value[0] = mCodec_data->mAudio_Ana_Mux[AUDIO_MICSOURCE_MUX_IN_1];    return 0;}static int Audio_MicSource1_Set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){    /* 6752 used for ADC1 Mic source selection, "ADC1" is main_mic, "ADC2" is headset_mic */    int index = 0;    pr_warn("%s()\n", __func__);    if (ucontrol->value.enumerated.item[0] > ARRAY_SIZE(Pmic_Digital_Mux)) {// Pmic_Digital_Mux[] = { "ADC1", "ADC2", "ADC3", "ADC4" };        pr_err("return -EINVAL\n");        return -EINVAL;    }    index = ucontrol->value.integer.value[0];    pr_warn("%s() index = %d done\n", __func__, index);    mCodec_data->mAudio_Ana_Mux[AUDIO_MICSOURCE_MUX_IN_1] = ucontrol->value.integer.value[0];    return 0;}*//////////////////////////////////////////////////////////    SOC_ENUM_EXT("Audio_MicSource1_Setting", Audio_UL_Enum[13], Audio_MicSource1_Get,             Audio_MicSource1_Set),//采集音源mic控件设置,无论从phone mic还是从earphone mic传来的音源,mt6737都只从ADC1进行采集    SOC_ENUM_EXT("Audio_MicSource2_Setting", Audio_UL_Enum[14], Audio_MicSource2_Get,             Audio_MicSource2_Set),//不支持    SOC_ENUM_EXT("Audio_MicSource3_Setting", Audio_UL_Enum[15], Audio_MicSource3_Get,             Audio_MicSource3_Set),//不支持    SOC_ENUM_EXT("Audio_MicSource4_Setting", Audio_UL_Enum[16], Audio_MicSource4_Get,             Audio_MicSource4_Set),//不支持//////////////////////////////////////////////////////////*Mic ACC/DCC Mode Settingstatic int Audio_Mic1_Mode_Select_Get(struct snd_kcontrol *kcontrol,                      struct snd_ctl_elem_value *ucontrol){    pr_warn("%s() mAudio_Analog_Mic1_mode = %d\n", __func__, mAudio_Analog_Mic1_mode);    ucontrol->value.integer.value[0] = mAudio_Analog_Mic1_mode;    return 0;}static int Audio_Mic1_Mode_Select_Set(struct snd_kcontrol *kcontrol,                      struct snd_ctl_elem_value *ucontrol){    pr_warn("%s()\n", __func__);    if (ucontrol->value.enumerated.item[0] > ARRAY_SIZE(Audio_AnalogMic_Mode)) {//Audio_AnalogMic_Mode[] = {"ACCMODE", "DCCMODE", "DMIC", "DCCECMDIFFMODE", "DCCECMSINGLEMODE" };        pr_err("return -EINVAL\n");        return -EINVAL;    }    mAudio_Analog_Mic1_mode = ucontrol->value.integer.value[0];//设置mic模式ACC    pr_warn("%s() mAudio_Analog_Mic1_mode = %d\n", __func__, mAudio_Analog_Mic1_mode);    return 0;}*//////////////////////////////////////////////////////////    SOC_ENUM_EXT("Audio_MIC1_Mode_Select", Audio_UL_Enum[17], Audio_Mic1_Mode_Select_Get,             Audio_Mic1_Mode_Select_Set),//mic1模式设置控件开关    SOC_ENUM_EXT("Audio_MIC2_Mode_Select", Audio_UL_Enum[18], Audio_Mic2_Mode_Select_Get,             Audio_Mic2_Mode_Select_Set),//mic2模式设置控件开关    SOC_ENUM_EXT("Audio_MIC3_Mode_Select", Audio_UL_Enum[19], Audio_Mic3_Mode_Select_Get,             Audio_Mic3_Mode_Select_Set),//mic3模式设置控件开关    SOC_ENUM_EXT("Audio_MIC4_Mode_Select", Audio_UL_Enum[20], Audio_Mic4_Mode_Select_Get,             Audio_Mic4_Mode_Select_Set),//mic4模式设置控件开关//////////////////////////////////////////////////////////*static int Audio_Adc_Power_Mode_Get(struct snd_kcontrol *kcontrol,                    struct snd_ctl_elem_value *ucontrol){    pr_warn("%s()  = %d\n", __func__, mAdc_Power_Mode);    ucontrol->value.integer.value[0] = mAdc_Power_Mode;    return 0;}static int Audio_Adc_Power_Mode_Set(struct snd_kcontrol *kcontrol,                    struct snd_ctl_elem_value *ucontrol){    pr_warn("%s()\n", __func__);    if (ucontrol->value.enumerated.item[0] > ARRAY_SIZE(ADC_power_mode)) {//static const char * const ADC_power_mode[] = { "normal", "lowpower" };        pr_err("return -EINVAL\n");        return -EINVAL;    }    mAdc_Power_Mode = ucontrol->value.integer.value[0];//设置mic adc供电模式    pr_warn("%s() mAdc_Power_Mode = %d\n", __func__, mAdc_Power_Mode);    return 0;}*//////////////////////////////////////////////////////////    SOC_ENUM_EXT("Audio_Mic_Power_Mode", Audio_UL_Enum[21], Audio_Adc_Power_Mode_Get,             Audio_Adc_Power_Mode_Set),//mic adc供电模式设置控件开关//////////////////////////////////////////////////////////*//VOW是一个可以通过3G/4G和WIFI联网的头戴式耳机static int Audio_Vow_ADC_Func_Switch_Get(struct snd_kcontrol *kcontrol,                     struct snd_ctl_elem_value *ucontrol){    pr_warn("%s()  = %d\n", __func__, mAudio_Vow_Analog_Func_Enable);    ucontrol->value.integer.value[0] = mAudio_Vow_Analog_Func_Enable;    return 0;}static int Audio_Vow_ADC_Func_Switch_Set(struct snd_kcontrol *kcontrol,                     struct snd_ctl_elem_value *ucontrol){    pr_warn("%s()\n", __func__);    if (ucontrol->value.enumerated.item[0] > ARRAY_SIZE(Audio_VOW_ADC_Function)) {//Audio_VOW_ADC_Function[] = { "Off", "On" }        pr_err("return -EINVAL\n");        return -EINVAL;    }    if (ucontrol->value.integer.value[0])        TurnOnVOWADcPowerACC(mAudio_VOW_Mic_type, true);    else        TurnOnVOWADcPowerACC(mAudio_VOW_Mic_type, false);    mAudio_Vow_Analog_Func_Enable = ucontrol->value.integer.value[0];    pr_warn("%s() mAudio_Vow_Analog_Func_Enable = %d\n", __func__,           mAudio_Vow_Analog_Func_Enable);    return 0;}*//////////////////////////////////////////////////////////    SOC_ENUM_EXT("Audio_Vow_ADC_Func_Switch", Audio_UL_Enum[22], Audio_Vow_ADC_Func_Switch_Get,             Audio_Vow_ADC_Func_Switch_Set),//vow耳机控件开关    SOC_ENUM_EXT("Audio_Preamp2_Switch", Audio_UL_Enum[23], Audio_PreAmp2_Get,             Audio_PreAmp2_Set),//mic前置差分放大器2控制开关    SOC_ENUM_EXT("Audio_Vow_Digital_Func_Switch", Audio_UL_Enum[24],             Audio_Vow_Digital_Func_Switch_Get, Audio_Vow_Digital_Func_Switch_Set),//下面都是vow耳机,暂不分析    SOC_ENUM_EXT("Audio_Vow_MIC_Type_Select", Audio_UL_Enum[25], Audio_Vow_MIC_Type_Select_Get,             Audio_Vow_MIC_Type_Select_Set),    SOC_SINGLE_EXT("Audio VOWCFG0 Data", SND_SOC_NOPM, 0, 0x80000, 0, Audio_Vow_Cfg0_Get,               Audio_Vow_Cfg0_Set),    SOC_SINGLE_EXT("Audio VOWCFG1 Data", SND_SOC_NOPM, 0, 0x80000, 0, Audio_Vow_Cfg1_Get,               Audio_Vow_Cfg1_Set),    SOC_SINGLE_EXT("Audio VOWCFG2 Data", SND_SOC_NOPM, 0, 0x80000, 0, Audio_Vow_Cfg2_Get,               Audio_Vow_Cfg2_Set),    SOC_SINGLE_EXT("Audio VOWCFG3 Data", SND_SOC_NOPM, 0, 0x80000, 0, Audio_Vow_Cfg3_Get,               Audio_Vow_Cfg3_Set),    SOC_SINGLE_EXT("Audio VOWCFG4 Data", SND_SOC_NOPM, 0, 0x80000, 0, Audio_Vow_Cfg4_Get,               Audio_Vow_Cfg4_Set),    SOC_SINGLE_EXT("Audio VOWCFG5 Data", SND_SOC_NOPM, 0, 0x80000, 0, Audio_Vow_Cfg5_Get,               Audio_Vow_Cfg5_Set),    SOC_SINGLE_EXT("Audio_VOW_State", SND_SOC_NOPM, 0, 0x80000, 0, Audio_Vow_State_Get,               Audio_Vow_State_Set),};
4.1.1.2.3、mt6331_Voice_Switch

没有实质功能

snd_soc_add_codec_controls(codec, mt6331_Voice_Switch, ARRAY_SIZE(mt6331_Voice_Switch));static const struct snd_kcontrol_new mt6331_Voice_Switch[] = {    /* SOC_DAPM_ENUM_EXT("Voice Mux", Audio_DL_Enum[10], Voice_Mux_Get, Voice_Mux_Set), */};
4.1.1.2.4、mt6331_pmic_Test_controls

pmic测试控件

snd_soc_add_codec_controls(codec, mt6331_pmic_Test_controls,                   ARRAY_SIZE(mt6331_pmic_Test_controls));static const struct snd_kcontrol_new mt6331_pmic_Test_controls[] = {    SOC_ENUM_EXT("SineTable_DAC_HP", Pmic_Test_Enum[0], SineTable_DAC_HP_Get,             SineTable_DAC_HP_Set),    SOC_ENUM_EXT("DAC_LOOP_DAC_HS", Pmic_Test_Enum[1], ADC_LOOP_DAC_HS_Get,             ADC_LOOP_DAC_HS_Set),    SOC_ENUM_EXT("DAC_LOOP_DAC_HP", Pmic_Test_Enum[2], ADC_LOOP_DAC_HP_Get,             ADC_LOOP_DAC_HP_Set),    SOC_ENUM_EXT("Voice_Call_DAC_DAC_HS", Pmic_Test_Enum[3], Voice_Call_DAC_DAC_HS_Get,             Voice_Call_DAC_DAC_HS_Set),    SOC_ENUM_EXT("SineTable_UL2", Pmic_Test_Enum[4], SineTable_UL2_Get, SineTable_UL2_Set),    SOC_ENUM_EXT("Pmic_Loopback", Pmic_Test_Enum[5], Pmic_Loopback_Get, Pmic_Loopback_Set),};
4.1.1.2.5、mt6331_snd_Speaker_controls(内置PA相关控件)

这里写图片描述
这里写图片描述

#ifdef CONFIG_MTK_SPEAKER    snd_soc_add_codec_controls(codec, mt6331_snd_Speaker_controls,           ARRAY_SIZE(mt6331_snd_Speaker_controls));#endifstatic const struct snd_kcontrol_new mt6331_snd_Speaker_controls[] = {//////////////////////////////////////////////////////////*static int Audio_Speaker_Class_Set(struct snd_kcontrol *kcontrol,                   struct snd_ctl_elem_value *ucontrol){    mutex_lock(&Ana_Ctrl_Mutex);    Speaker_mode = ucontrol->value.integer.value[0];    mutex_unlock(&Ana_Ctrl_Mutex);    return 0;}static int Audio_Speaker_Class_Get(struct snd_kcontrol *kcontrol,                   struct snd_ctl_elem_value *ucontrol){    ucontrol->value.integer.value[0] = Speaker_mode;//Speaker_mode = AUDIO_SPEAKER_MODE_AB,内置PA为AB类功放    return 0;}*//////////////////////////////////////////////////////////    SOC_ENUM_EXT("Audio_Speaker_class_Switch", Audio_Speaker_Enum[0], Audio_Speaker_Class_Get,             Audio_Speaker_Class_Set),//设置PA为AB类功放    SOC_ENUM_EXT("Audio_Speaker_PGA_gain", Audio_Speaker_Enum[1], Audio_Speaker_Pga_Gain_Get,             Audio_Speaker_Pga_Gain_Set),//设置gain(增益调节)为1db,它是用来调节输入声信号的放大量//////////////////////////////////////////////////////////*static int Audio_Speaker_OcFlag_Get(struct snd_kcontrol *kcontrol,                    struct snd_ctl_elem_value *ucontrol){    mSpeaker_Ocflag = GetSpeakerOcFlag();    ucontrol->value.integer.value[0] = mSpeaker_Ocflag;    return 0;}static int Audio_Speaker_OcFlag_Set(struct snd_kcontrol *kcontrol,                    struct snd_ctl_elem_value *ucontrol){    pr_warn("%s is not support setting\n", __func__);    return 0;}//根据下图配置bool GetSpeakerOcFlag(void){    unsigned int OCregister = 0;    unsigned int bitmask = 1;    bool DmodeFlag = false;    bool ABmodeFlag = false;    bool OCFlag = false;    Ana_Set_Reg(TOP_CKPDN_CON2_CLR, 0x3, 0xffff);    OCregister = Ana_Get_Reg(SPK_CON6);    DmodeFlag = OCregister & (bitmask << 14);   /* ; no.14 bit is SPK_D_OC_L_DEG */    ABmodeFlag = OCregister & (bitmask << 15);  /* ; no.15 bit is SPK_AB_OC_L_DEG */    pr_warn("OCregister = %d\n", OCregister);    OCFlag = (DmodeFlag | ABmodeFlag);    return OCFlag;}*//////////////////////////////////////////////////////////    SOC_ENUM_EXT("Audio_Speaker_OC_Falg", Audio_Speaker_Enum[2], Audio_Speaker_OcFlag_Get,             Audio_Speaker_OcFlag_Set),//AB/D类功放选择标志控件    //PA电流检测    SOC_ENUM_EXT("Audio_Speaker_CurrentSensing", Audio_Speaker_Enum[3],             Audio_Speaker_Current_Sensing_Get, Audio_Speaker_Current_Sensing_Set),    SOC_ENUM_EXT("Audio_Speaker_CurrentPeakDetector", Audio_Speaker_Enum[4],             Audio_Speaker_Current_Sensing_Peak_Detector_Get,             Audio_Speaker_Current_Sensing_Peak_Detector_Set),};

SpeakerOcFlag
Speaker_Current_Sensing

4.1.1.2.6、Audio_snd_auxadc_controls
snd_soc_add_codec_controls(codec, Audio_snd_auxadc_controls,                   ARRAY_SIZE(Audio_snd_auxadc_controls));static const struct snd_kcontrol_new Audio_snd_auxadc_controls[] = {    SOC_SINGLE_EXT("Audio AUXADC Data", SND_SOC_NOPM, 0, 0x80000, 0, Audio_AuxAdcData_Get,               Audio_AuxAdcData_Set),//audio adc数据控件};static int Audio_AuxAdcData_Get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){#ifdef CONFIG_MTK_SPEAKER    ucontrol->value.integer.value[0] = Audio_AuxAdcData_Get_ext();    //PMIC_IMM_GetOneChannelValue(AUX_ICLASSAB_AP, 1, 0);#else    ucontrol->value.integer.value[0] = 0;#endif    pr_warn("%s dMax = 0x%lx\n", __func__, ucontrol->value.integer.value[0]);    return 0;}static int Audio_AuxAdcData_Set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){    dAuxAdcChannel = ucontrol->value.integer.value[0];    pr_warn("%s dAuxAdcChannel = 0x%x\n", __func__, dAuxAdcChannel);    return 0;}
4.1.1.2.7、mt6331_codec_init_reg(codec)
//codec初始化static void mt6331_codec_init_reg(struct snd_soc_codec *codec){    pr_warn("%s\n", __func__);    Ana_Set_Reg(TOP_CLKSQ, 0x0, 0x0001);    /* Disable CLKSQ 26MHz */    Ana_Set_Reg(AUDDEC_ANA_CON8, 0x0002, 0x0002);    /* disable AUDGLB */    Ana_Set_Reg(TOP_CKPDN_CON0_SET, 0x3800, 0x3800);    /* Turn off AUDNCP_CLKDIV engine clock,Turn off AUD 26M */    Ana_Set_Reg(AUDDEC_ANA_CON0, 0xe000, 0xe000);    /* Disable HeadphoneL/HeadphoneR/voice short circuit protection */    Ana_Set_Reg(AUDENC_ANA_CON9, 0x0000, 0x0010);    /* power off mic bias1 */    Ana_Set_Reg(AFE_PMIC_NEWIF_CFG2, 0x8000, 0x8000);    /* Reverse the PMIC clock*/}
4.1.1.2.8、InitCodecDefault()
void InitCodecDefault(void){    pr_warn("%s\n", __func__);//设置mic1音量最大值mCodec_data->mAudio_Ana_Volume[AUDIO_ANALOG_VOLUME_MICAMP1] = 3;//设置mic2音量最大值mCodec_data->mAudio_Ana_Volume[AUDIO_ANALOG_VOLUME_MICAMP2] = 3;    mCodec_data->mAudio_Ana_Volume[AUDIO_ANALOG_VOLUME_MICAMP3] = 3;    mCodec_data->mAudio_Ana_Volume[AUDIO_ANALOG_VOLUME_MICAMP4] = 3;//耳机音量右声道输出最大值mCodec_data->mAudio_Ana_Volume[AUDIO_ANALOG_VOLUME_HPOUTR] = 8;    mCodec_data->mAudio_Ana_Volume[AUDIO_ANALOG_VOLUME_HPOUTR] = 8;    mCodec_data->mAudio_Ana_Mux[AUDIO_ANALOG_MUX_IN_MIC1] =        AUDIO_ANALOG_AUDIOANALOG_INPUT_PREAMP;    mCodec_data->mAudio_Ana_Mux[AUDIO_ANALOG_MUX_IN_MIC2] =        AUDIO_ANALOG_AUDIOANALOG_INPUT_PREAMP;    mCodec_data->mAudio_Ana_Mux[AUDIO_ANALOG_MUX_IN_MIC3] =        AUDIO_ANALOG_AUDIOANALOG_INPUT_PREAMP;    mCodec_data->mAudio_Ana_Mux[AUDIO_ANALOG_MUX_IN_MIC4] =        AUDIO_ANALOG_AUDIOANALOG_INPUT_PREAMP;}

4.2、Codec Dai(mtk_6331_dai_codecs)

static struct snd_soc_dai_driver mtk_6331_dai_codecs[] = {    {     .name = MT_SOC_CODEC_TXDAI_NAME,     .ops = &mt6323_aif1_dai_ops,     .playback = {              .stream_name = MT_SOC_DL1_STREAM_NAME,              .channels_min = 1,              .channels_max = 2,              .rates = SNDRV_PCM_RATE_8000_192000,              .formats = SND_SOC_ADV_MT_FMTS,              },     },    {     .name = MT_SOC_CODEC_RXDAI_NAME,     .ops = &mt6323_aif1_dai_ops,     .capture = {             .stream_name = MT_SOC_UL1_STREAM_NAME,             .channels_min = 1,             .channels_max = 2,             .rates = SNDRV_PCM_RATE_8000_192000,             .formats = SND_SOC_ADV_MT_FMTS,             },     },    {     .name = MT_SOC_CODEC_TDMRX_DAI_NAME,     .ops = &mt6323_aif1_dai_ops,     .capture = {             .stream_name = MT_SOC_TDM_CAPTURE_STREAM_NAME,             .channels_min = 2,             .channels_max = 8,             .rates = SNDRV_PCM_RATE_8000_192000,             .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |                 SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |                 SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |                 SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE |                 SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE |                 SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_S24_3LE |                 SNDRV_PCM_FMTBIT_U24_3BE | SNDRV_PCM_FMTBIT_S24_3BE |                 SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |                 SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE),             },     },    {     .name = MT_SOC_CODEC_I2S0TXDAI_NAME,     .ops = &mt6323_aif1_dai_ops,     .playback = {              .stream_name = MT_SOC_I2SDL1_STREAM_NAME,              .channels_min = 1,              .channels_max = 2,              .rate_min = 8000,              .rate_max = 192000,              .rates = SNDRV_PCM_RATE_8000_192000,              .formats = SND_SOC_ADV_MT_FMTS,              }     },    {     .name = MT_SOC_CODEC_VOICE_MD1DAI_NAME,     .ops = &mt6323_aif1_dai_ops,     .playback = {              .stream_name = MT_SOC_VOICE_MD1_STREAM_NAME,              .channels_min = 1,              .channels_max = 2,              .rates = SNDRV_PCM_RATE_8000_48000,              .formats = SND_SOC_ADV_MT_FMTS,              },     .capture = {             .stream_name = MT_SOC_VOICE_MD1_STREAM_NAME,             .channels_min = 1,             .channels_max = 2,             .rates = SNDRV_PCM_RATE_8000_48000,             .formats = SND_SOC_ADV_MT_FMTS,             },     },    {     .name = MT_SOC_CODEC_VOICE_MD2DAI_NAME,     .ops = &mt6323_aif1_dai_ops,     .playback = {              .stream_name = MT_SOC_VOICE_MD2_STREAM_NAME,              .channels_min = 1,              .channels_max = 2,              .rates = SNDRV_PCM_RATE_8000_48000,              .formats = SND_SOC_ADV_MT_FMTS,              },     .capture = {             .stream_name = MT_SOC_VOICE_MD2_STREAM_NAME,             .channels_min = 1,             .channels_max = 2,             .rates = SNDRV_PCM_RATE_8000_48000,             .formats = SND_SOC_ADV_MT_FMTS,             },     },    {     .name = MT_SOC_CODEC_FMI2S2RXDAI_NAME,     .ops = &mt6323_aif1_dai_ops,     .playback = {              .stream_name = MT_SOC_FM_I2S2_STREAM_NAME,              .channels_min = 1,              .channels_max = 2,              .rates = SNDRV_PCM_RATE_8000_48000,              .formats = SND_SOC_ADV_MT_FMTS,              },     .capture = {             .stream_name = MT_SOC_FM_I2S2_RECORD_STREAM_NAME,             .channels_min = 1,             .channels_max = 2,             .rates = SNDRV_PCM_RATE_8000_48000,             .formats = SND_SOC_ADV_MT_FMTS,             },     },    {     .name = MT_SOC_CODEC_FMMRGTXDAI_DUMMY_DAI_NAME,     .ops = &mt6323_aif1_dai_ops,     .playback = {              .stream_name = MT_SOC_FM_MRGTX_STREAM_NAME,              .channels_min = 1,              .channels_max = 2,              .rates = SNDRV_PCM_RATE_8000_48000,              .formats = SND_SOC_ADV_MT_FMTS,              },     },    {     .name = MT_SOC_CODEC_ULDLLOOPBACK_NAME,     .ops = &mt6323_aif1_dai_ops,     .playback = {              .stream_name = MT_SOC_ULDLLOOPBACK_STREAM_NAME,              .channels_min = 1,              .channels_max = 2,              .rates = SNDRV_PCM_RATE_8000_48000,              .formats = SND_SOC_ADV_MT_FMTS,              },     .capture = {             .stream_name = MT_SOC_ULDLLOOPBACK_STREAM_NAME,             .channels_min = 1,             .channels_max = 2,             .rates = SNDRV_PCM_RATE_8000_48000,             .formats = SND_SOC_ADV_MT_FMTS,             },     },    {     .name = MT_SOC_CODEC_STUB_NAME,     .ops = &mt6323_aif1_dai_ops,     .playback = {              .stream_name = MT_SOC_ROUTING_STREAM_NAME,              .channels_min = 1,              .channels_max = 2,              .rates = SNDRV_PCM_RATE_8000_192000,              .formats = SND_SOC_ADV_MT_FMTS,              },     },    {     .name = MT_SOC_CODEC_RXDAI2_NAME,     .capture = {             .stream_name = MT_SOC_UL1DATA2_STREAM_NAME,             .channels_min = 1,             .channels_max = 2,             .rates = SNDRV_PCM_RATE_8000_192000,             .formats = SND_SOC_ADV_MT_FMTS,             },     },    {     .name = MT_SOC_CODEC_MRGRX_DAI_NAME,     .ops = &mt6323_aif1_dai_ops,     .playback = {              .stream_name = MT_SOC_MRGRX_STREAM_NAME,              .channels_min = 1,              .channels_max = 8,              .rates = SNDRV_PCM_RATE_8000_192000,              .formats = SND_SOC_ADV_MT_FMTS,              },     .capture = {             .stream_name = MT_SOC_MRGRX_STREAM_NAME,             .channels_min = 1,             .channels_max = 8,             .rates = SNDRV_PCM_RATE_8000_192000,             .formats = SND_SOC_ADV_MT_FMTS,             },     },    {     .name = MT_SOC_CODEC_HP_IMPEDANCE_NAME,     .ops = &mt6323_aif1_dai_ops,     .playback = {              .stream_name = MT_SOC_HP_IMPEDANCE_STREAM_NAME,              .channels_min = 1,              .channels_max = 2,              .rates = SNDRV_PCM_RATE_8000_192000,              .formats = SND_SOC_ADV_MT_FMTS,              },     },    {     .name = MT_SOC_CODEC_FM_I2S_DAI_NAME,     .ops = &mt6323_aif1_dai_ops,     .playback = {              .stream_name = MT_SOC_FM_I2S_PLAYBACK_STREAM_NAME,              .channels_min = 1,              .channels_max = 8,              .rates = SNDRV_PCM_RATE_8000_192000,              .formats = SND_SOC_ADV_MT_FMTS,              },     },    {     .name = MT_SOC_CODEC_TXDAI2_NAME,     .ops = &mt6323_aif1_dai_ops,     .playback = {              .stream_name = MT_SOC_DL2_STREAM_NAME,              .channels_min = 1,              .channels_max = 2,              .rates = SNDRV_PCM_RATE_8000_192000,              .formats = SND_SOC_ADV_MT_FMTS,              },     },};

5、Machine

kernel-3.18/sound/soc/mediatek/mt_soc_audio_v3/mt_soc_machine.c
module_init(mt_soc_snd_init)->mt_soc_snd_init(void)

static int __init mt_soc_snd_init(void){    int ret;    struct snd_soc_card *card = &snd_soc_card_mt;    pr_debug("mt_soc_snd_init card addr = %p\n", card);    mt_snd_device = platform_device_alloc("soc-audio", -1);    if (!mt_snd_device) {        pr_err("mt6589_probe  platform_device_alloc fail\n");        return -ENOMEM;    }    platform_set_drvdata(mt_snd_device, &snd_soc_card_mt);    ret = platform_device_add(mt_snd_device);    if (ret != 0) {        pr_err("mt_soc_snd_init goto put_device fail\n");        goto put_device;    }    pr_debug("mt_soc_snd_init dai_link = %p\n", snd_soc_card_mt.dai_link);    /* create debug file */    mt_sco_audio_debugfs = debugfs_create_file(DEBUG_FS_NAME,                           S_IFREG | S_IRUGO, NULL, (void *)DEBUG_FS_NAME,                           &mtaudio_debug_ops);    /* create analog debug file */    mt_sco_audio_debugfs = debugfs_create_file(DEBUG_ANA_FS_NAME,                           S_IFREG | S_IRUGO, NULL,                           (void *)DEBUG_ANA_FS_NAME,                           &mtaudio_ana_debug_ops);    return 0;put_device:    platform_device_put(mt_snd_device);    return ret;}

下面开始对mt_soc_snd_init函数进行分析。

5.1、snd_soc_card_mt

snd_soc_card_mt通过mt_soc_snd_init函数传入soc_probe函数。
.dai_link = mt_soc_dai_common是struct snd_soc_card数据结构的核心。

static struct snd_soc_card snd_soc_card_mt = {    .name = "mt-snd-card",    .dai_link = mt_soc_dai_common,    .num_links = ARRAY_SIZE(mt_soc_dai_common),    .controls = mt_soc_controls,    .num_controls = ARRAY_SIZE(mt_soc_controls),};

mt_soc_dai_common通过stream_name 、cpu_dai_name 、platform_name 、codec_dai_name 、codec_name名字绑定在一起

/* Digital audio interface glue - connects codec <---> CPU */static struct snd_soc_dai_link mt_soc_dai_common[] = {    /* FrontEnd DAI Links */    {     .name = "MultiMedia1",     .stream_name = MT_SOC_DL1_STREAM_NAME,     .cpu_dai_name = MT_SOC_DL1DAI_NAME,     .platform_name = MT_SOC_DL1_PCM,     .codec_dai_name = MT_SOC_CODEC_TXDAI_NAME,     .codec_name = MT_SOC_CODEC_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "MultiMedia2",     .stream_name = MT_SOC_UL1_STREAM_NAME,     .cpu_dai_name = MT_SOC_UL1DAI_NAME,     .platform_name = MT_SOC_UL1_PCM,     .codec_dai_name = MT_SOC_CODEC_RXDAI_NAME,     .codec_name = MT_SOC_CODEC_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "Voice_MD1",     .stream_name = MT_SOC_VOICE_MD1_STREAM_NAME,     .cpu_dai_name = MT_SOC_VOICE_MD1_NAME,     .platform_name = MT_SOC_VOICE_MD1,     .codec_dai_name = MT_SOC_CODEC_VOICE_MD1DAI_NAME,     .codec_name = MT_SOC_CODEC_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "HDMI_OUT",     .stream_name = MT_SOC_HDMI_STREAM_NAME,     .cpu_dai_name = MT_SOC_HDMI_NAME,     .platform_name = MT_SOC_HDMI_PCM,     .codec_dai_name = MT_SOC_CODEC_HDMI_DUMMY_DAI_NAME,     .codec_name = MT_SOC_CODEC_DUMMY_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "ULDLOOPBACK",     .stream_name = MT_SOC_ULDLLOOPBACK_STREAM_NAME,     .cpu_dai_name = MT_SOC_ULDLLOOPBACK_NAME,     .platform_name = MT_SOC_ULDLLOOPBACK_PCM,     .codec_dai_name = MT_SOC_CODEC_ULDLLOOPBACK_NAME,     .codec_name = MT_SOC_CODEC_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "I2S0OUTPUT",     .stream_name = MT_SOC_I2S0_STREAM_NAME,     .cpu_dai_name = MT_SOC_I2S0_NAME,     .platform_name = MT_SOC_I2S0_PCM,     .codec_dai_name = MT_SOC_CODEC_I2S0_DUMMY_DAI_NAME,     .codec_name = MT_SOC_CODEC_DUMMY_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "MRGRX",     .stream_name = MT_SOC_MRGRX_STREAM_NAME,     .cpu_dai_name = MT_SOC_MRGRX_NAME,     .platform_name = MT_SOC_MRGRX_PCM,     .codec_dai_name = MT_SOC_CODEC_MRGRX_DAI_NAME,     .codec_name = MT_SOC_CODEC_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "MRGRXCAPTURE",     .stream_name = MT_SOC_MRGRX_CAPTURE_STREAM_NAME,     .cpu_dai_name = MT_SOC_MRGRX_NAME,     .platform_name = MT_SOC_MRGRX_AWB_PCM,     .codec_dai_name = MT_SOC_CODEC_MRGRX_DUMMY_DAI_NAME,     .codec_name = MT_SOC_CODEC_DUMMY_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "I2S0DL1OUTPUT",     .stream_name = MT_SOC_I2SDL1_STREAM_NAME,     .cpu_dai_name = MT_SOC_I2S0DL1_NAME,     .platform_name = MT_SOC_I2S0DL1_PCM,     .codec_dai_name = MT_SOC_CODEC_I2S0TXDAI_NAME,     .codec_name = MT_SOC_CODEC_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "DL1AWBCAPTURE",     .stream_name = MT_SOC_DL1_AWB_RECORD_STREAM_NAME,     .cpu_dai_name = MT_SOC_DL1AWB_NAME,     .platform_name = MT_SOC_DL1_AWB_PCM,     .codec_dai_name = MT_SOC_CODEC_DL1AWBDAI_NAME,     .codec_name = MT_SOC_CODEC_DUMMY_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "Voice_MD1_BT",     .stream_name = MT_SOC_VOICE_MD1_BT_STREAM_NAME,     .cpu_dai_name = MT_SOC_VOICE_MD1_BT_NAME,     .platform_name = MT_SOC_VOICE_MD1_BT,     .codec_dai_name = MT_SOC_CODEC_VOICE_MD1_BTDAI_NAME,     .codec_name = MT_SOC_CODEC_DUMMY_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "VOIP_CALL_BT_PLAYBACK",     .stream_name = MT_SOC_VOIP_BT_OUT_STREAM_NAME,     .cpu_dai_name = MT_SOC_VOIP_CALL_BT_OUT_NAME,     .platform_name = MT_SOC_VOIP_BT_OUT,     .codec_dai_name = MT_SOC_CODEC_VOIPCALLBTOUTDAI_NAME,     .codec_name = MT_SOC_CODEC_DUMMY_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "VOIP_CALL_BT_CAPTURE",     .stream_name = MT_SOC_VOIP_BT_IN_STREAM_NAME,     .cpu_dai_name = MT_SOC_VOIP_CALL_BT_IN_NAME,     .platform_name = MT_SOC_VOIP_BT_IN,     .codec_dai_name = MT_SOC_CODEC_VOIPCALLBTINDAI_NAME,     .codec_name = MT_SOC_CODEC_DUMMY_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "TDM_Debug_CAPTURE",     .stream_name = MT_SOC_TDM_CAPTURE_STREAM_NAME,     .cpu_dai_name = MT_SOC_TDMRX_NAME,     .platform_name = MT_SOC_TDMRX_PCM,     .codec_dai_name = MT_SOC_CODEC_TDMRX_DAI_NAME,     .codec_name = MT_SOC_CODEC_DUMMY_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "FM_MRG_TX",     .stream_name = MT_SOC_FM_MRGTX_STREAM_NAME,     .cpu_dai_name = MT_SOC_FM_MRGTX_NAME,     .platform_name = MT_SOC_FM_MRGTX_PCM,     .codec_dai_name = MT_SOC_CODEC_FMMRGTXDAI_DUMMY_DAI_NAME,     .codec_name = MT_SOC_CODEC_DUMMY_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "MultiMedia3",     .stream_name = MT_SOC_UL1DATA2_STREAM_NAME,     .cpu_dai_name = MT_SOC_UL2DAI_NAME,     .platform_name = MT_SOC_UL2_PCM,     .codec_dai_name = MT_SOC_CODEC_RXDAI2_NAME,     .codec_name = MT_SOC_CODEC_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "I2S0_AWB_CAPTURE",     .stream_name = MT_SOC_I2S0AWB_STREAM_NAME,     .cpu_dai_name = MT_SOC_I2S0AWBDAI_NAME,     .platform_name = MT_SOC_I2S0_AWB_PCM,     .codec_dai_name = MT_SOC_CODEC_I2S0AWB_NAME,     .codec_name = MT_SOC_CODEC_DUMMY_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "Voice_MD2",     .stream_name = MT_SOC_VOICE_MD2_STREAM_NAME,     .cpu_dai_name = MT_SOC_VOICE_MD2_NAME,     .platform_name = MT_SOC_VOICE_MD2,     .codec_dai_name = MT_SOC_CODEC_VOICE_MD2DAI_NAME,     .codec_name = MT_SOC_CODEC_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "PLATOFRM_CONTROL",     .stream_name = MT_SOC_ROUTING_STREAM_NAME,     .cpu_dai_name = MT_SOC_ROUTING_DAI_NAME,     .platform_name = MT_SOC_ROUTING_PCM,     .codec_dai_name = MT_SOC_CODEC_DUMMY_DAI_NAME,     .codec_name = MT_SOC_CODEC_DUMMY_NAME,     .init = mt_soc_audio_init2,     .ops = &mtmachine_audio_ops2,     },    {     .name = "Voice_MD2_BT",     .stream_name = MT_SOC_VOICE_MD2_BT_STREAM_NAME,     .cpu_dai_name = MT_SOC_VOICE_MD2_BT_NAME,     .platform_name = MT_SOC_VOICE_MD2_BT,     .codec_dai_name = MT_SOC_CODEC_VOICE_MD2_BTDAI_NAME,     .codec_name = MT_SOC_CODEC_DUMMY_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "HP_IMPEDANCE",     .stream_name = MT_SOC_HP_IMPEDANCE_STREAM_NAME,     .cpu_dai_name = MT_SOC_HP_IMPEDANCE_NAME,     .platform_name = MT_SOC_HP_IMPEDANCE_PCM,     .codec_dai_name = MT_SOC_CODEC_HP_IMPEDANCE_NAME,     .codec_name = MT_SOC_CODEC_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "FM_I2S_RX_Playback",     .stream_name = MT_SOC_FM_I2S_PLAYBACK_STREAM_NAME,     .cpu_dai_name = MT_SOC_FM_I2S_NAME,     .platform_name = MT_SOC_FM_I2S_PCM,     .codec_dai_name = MT_SOC_CODEC_FM_I2S_DAI_NAME,     .codec_name = MT_SOC_CODEC_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "FM_I2S_RX_Capture",     .stream_name = MT_SOC_FM_I2S_CAPTURE_STREAM_NAME,     .cpu_dai_name = MT_SOC_FM_I2S_NAME,     .platform_name = MT_SOC_FM_I2S_AWB_PCM,     .codec_dai_name = MT_SOC_CODEC_FM_I2S_DUMMY_DAI_NAME,     .codec_name = MT_SOC_CODEC_DUMMY_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },    {     .name = "OFFLOAD_GDMA_OUT",     .stream_name = MT_SOC_OFFLOAD_GDMA_STREAM_NAME,     .cpu_dai_name = MT_SOC_OFFLOAD_GDMA_NAME,     .platform_name = MT_SOC_OFFLOAD_GDMA_PCM,     .codec_dai_name = MT_SOC_CODEC_OFFLOAD_GDMA_DAI_NAME,     .codec_name = MT_SOC_CODEC_DUMMY_NAME,     .init = mt_soc_audio_init,     /* .ops = &mt_machine_audio_ops, */     .compr_ops = &mt_machine_audio_compr_ops,     },    {     .name = "MultiMedia_DL2",     .stream_name = MT_SOC_DL2_STREAM_NAME,     .cpu_dai_name   = MT_SOC_DL2DAI_NAME,     .platform_name  = MT_SOC_DL2_PCM,     .codec_dai_name = MT_SOC_CODEC_TXDAI2_NAME,     .codec_name = MT_SOC_CODEC_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },};

5.1.2、mt_soc_controls

I2S低抖动功能控件

static const struct snd_kcontrol_new mt_soc_controls[] = {    SOC_ENUM_EXT("I2S low Jitter function", mt_soc_machine_enum[0], mt6735_get_lowjitter,             mt6735_set_lowjitter),};static int mt6735_get_lowjitter(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){    pr_debug("%s:  mt_soc_lowjitter_control = %d\n", __func__, mt_soc_lowjitter_control);    ucontrol->value.integer.value[0] = mt_soc_lowjitter_control;    return 0;}static int mt6735_set_lowjitter(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){    pr_debug("%s()\n", __func__);    mt_soc_lowjitter_control = ucontrol->value.integer.value[0];    return 0;}

5.2、platform_device_add(mt_snd_device)

platform_set_drvdata(mt_snd_device, &snd_soc_card_mt);//将snd_soc_card_mt结构体保存在平台设备mt_snd_device中ret = platform_device_add(mt_snd_device);//将设备mt_snd_device挂载到platform总线上

下面到kernel-3.18/sound/soc/soc-core.c文件
soc_probe函数本身很简单,Snd_soc_card这个结构体通过函数platform_get_drvdata从平台设备数据结构取取出来。然后调用snd_soc_register_card,通过snd_soc_register_card,为snd_soc_pcm_runtime数组申请内存,每一个dai_link对应snd_soc_pcm_runtime数组的一个单元,然后把snd_soc_card中的dai_link配置复制到相应的snd_soc_pcm_runtime中,最后,大部分的工作都在snd_soc_instantiate_card中实现

module_init(snd_soc_init)->platform_driver_register(&soc_driver)->soc_probe

/* ASoC platform driver */static struct platform_driver soc_driver = {    .driver     = {        .name       = "soc-audio",        .owner      = THIS_MODULE,        .pm     = &snd_soc_pm_ops,    },    .probe      = soc_probe,    .remove     = soc_remove,};
/* probes a new socdev */static int soc_probe(struct platform_device *pdev){    struct snd_soc_card *card = platform_get_drvdata(pdev);//Snd_soc_card这个结构体通过函数platform_get_drvdata从平台设备数据结构取取出来。    /*     * no card, so machine driver should be registering card     * we should not be here in that case so ret error     */    if (!card)        return -EINVAL;    dev_warn(&pdev->dev,         "ASoC: machine %s should use snd_soc_register_card()\n",         card->name);    /* Bodge while we unpick instantiation */    card->dev = &pdev->dev;    return snd_soc_register_card(card);//将取出值结构体Snd_soc_card传给snd_soc_register_card}

下面进入snd_soc_register_card(card)

/** * snd_soc_register_card - Register a card with the ASoC core * * @card: Card to register * */int snd_soc_register_card(struct snd_soc_card *card){    int i, j, ret;    if (!card->name || !card->dev)    return -EINVAL;    for (i = 0; i < card->num_links; i++) {        struct snd_soc_dai_link *link = &card->dai_link[i];        ret = snd_soc_init_multicodec(card, link);        if (ret) {        dev_err(card->dev, "ASoC: failed to init multicodec\n");        return ret;    }    for (j = 0; j < link->num_codecs; j++) {    /*     * Codec must be specified by 1 of name or OF node,     * not both or neither.     */    if (!!link->codecs[j].name ==        !!link->codecs[j].of_node) {    dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",    link->name);    return -EINVAL;    }    /* Codec DAI name must be specified */    if (!link->codecs[j].dai_name) {    dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",    link->name);    return -EINVAL;    }    }    /*     * Platform may be specified by either name or OF node, but     * can be left unspecified, and a dummy platform will be used.     */    if (link->platform_name && link->platform_of_node) {    dev_err(card->dev,    "ASoC: Both platform name/of_node are set for %s\n",    link->name);    return -EINVAL;    }    /*     * CPU device may be specified by either name or OF node, but     * can be left unspecified, and will be matched based on DAI     * name alone..     */    if (link->cpu_name && link->cpu_of_node) {    dev_err(card->dev,    "ASoC: Neither/both cpu name/of_node are set for %s\n",    link->name);    return -EINVAL;    }    /*     * At least one of CPU DAI name or CPU device name/node must be     * specified     */    if (!link->cpu_dai_name &&        !(link->cpu_name || link->cpu_of_node)) {    dev_err(card->dev,    "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",    link->name);    return -EINVAL;    }    }    dev_set_drvdata(card->dev, card);    snd_soc_initialize_card_lists(card);    soc_init_card_debugfs(card);    card->rtd = devm_kzalloc(card->dev,     sizeof(struct snd_soc_pcm_runtime) *     (card->num_links + card->num_aux_devs),     GFP_KERNEL);    if (card->rtd == NULL)    return -ENOMEM;    card->num_rtd = 0;    card->rtd_aux = &card->rtd[card->num_links];    for (i = 0; i < card->num_links; i++) {    card->rtd[i].card = card;    > card->rtd[i].dai_link = &card->dai_link[i];    card->rtd[i].codec_dais = devm_kzalloc(card->dev,    sizeof(struct snd_soc_dai *) *    (card->rtd[i].dai_link->num_codecs),    > GFP_KERNEL);    if (card->rtd[i].codec_dais == NULL)    return -ENOMEM;    }    for (i = 0; i < card->num_aux_devs; i++)    card->rtd_aux[i].card = card;    INIT_LIST_HEAD(&card->dapm_dirty);    card->instantiated = 0;    mutex_init(&card->mutex);    mutex_init(&card->dapm_mutex);    ret = snd_soc_instantiate_card(card);    if (ret != 0)    soc_cleanup_card_debugfs(card);    /* deactivate pins to sleep state */    for (i = 0; i < card->num_rtd; i++) {    struct snd_soc_pcm_runtime *rtd = &card->rtd[i];    struct snd_soc_dai *cpu_dai = rtd->cpu_dai;    int j;    for (j = 0; j < rtd->num_codecs; j++) {    struct snd_soc_dai *codec_dai = rtd->codec_dais[j];    if (!codec_dai->active)    pinctrl_pm_select_sleep_state(codec_dai->dev);    }    if (!cpu_dai->active)    pinctrl_pm_select_sleep_state(cpu_dai->dev);    }    return ret;}

下面来进行分析snd_soc_register_card.

static struct snd_soc_card snd_soc_card_mt = {    .name = "mt-snd-card",    .dai_link = mt_soc_dai_common,    .num_links = ARRAY_SIZE(mt_soc_dai_common),    .controls = mt_soc_controls,    .num_controls = ARRAY_SIZE(mt_soc_controls),};
static struct snd_soc_dai_link mt_soc_dai_common[] = {    /* FrontEnd DAI Links */    {     .name = "MultiMedia1",     .stream_name = MT_SOC_DL1_STREAM_NAME,     .cpu_dai_name = MT_SOC_DL1DAI_NAME,     .platform_name = MT_SOC_DL1_PCM,     .codec_dai_name = MT_SOC_CODEC_TXDAI_NAME,     .codec_name = MT_SOC_CODEC_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },     ....}
for (i = 0; i < card->num_links; i++) {        struct snd_soc_dai_link *link = &card->dai_link[i];        ret = snd_soc_init_multicodec(card, link);        if (ret) {            dev_err(card->dev, "ASoC: failed to init multicodec\n");            return ret;        }

上面通过for循环将 snd_soc_card_mt 结构体成员mt_soc_dai_common数组元素一一传入 snd_soc_init_multicodec函数里。下面进行分析这个函数。

static struct snd_soc_dai_link mt_soc_dai_common[] = {    /* FrontEnd DAI Links */    {     .name = "MultiMedia1",     .stream_name = MT_SOC_DL1_STREAM_NAME,     .cpu_dai_name = MT_SOC_DL1DAI_NAME,     .platform_name = MT_SOC_DL1_PCM,     .codec_dai_name = MT_SOC_CODEC_TXDAI_NAME,     .codec_name = MT_SOC_CODEC_NAME,     .init = mt_soc_audio_init,     .ops = &mt_machine_audio_ops,     },     .........}
static int snd_soc_init_multicodec(struct snd_soc_card *card,                   struct snd_soc_dai_link *dai_link){    /* Legacy codec/codec_dai link is a single entry in multicodec */    if (dai_link->codec_name || dai_link->codec_of_node ||        dai_link->codec_dai_name) {        dai_link->num_codecs = 1;        dai_link->codecs = devm_kzalloc(card->dev,                sizeof(struct snd_soc_dai_link_component),                GFP_KERNEL);        if (!dai_link->codecs)            return -ENOMEM;        dai_link->codecs[0].name = dai_link->codec_name;        dai_link->codecs[0].of_node = dai_link->codec_of_node;        dai_link->codecs[0].dai_name = dai_link->codec_dai_name;    }    if (!dai_link->codecs) {        dev_err(card->dev, "ASoC: DAI link has no CODECs\n");        return -EINVAL;    }    return 0;}

这是一个链表,此函数将codec_name、codec_of_node、codec_dai_name取出来保存在snd_soc_dai_link结构体元素codecs[0]中

for (i = 0; i < card->num_links; i++) {        struct snd_soc_dai_link *link = &card->dai_link[i];        ret = snd_soc_init_multicodec(card, link);        if (ret) {            dev_err(card->dev, "ASoC: failed to init multicodec\n");            return ret;        }

这里就是要将snd_soc_card_mt结构体成员mt_soc_dai_common数组所有成员的codec_name、codec_of_node、codec_dai_name取出来保存在snd_soc_dai_link结构体元素codecs[0]中

5.2.2、snd_soc_initialize_card_lists(card)

dev_set_drvdata(card->dev, card);//用来存储驱动中要用到的私有数据cardsnd_soc_initialize_card_lists(card);static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card){    INIT_LIST_HEAD(&card->codec_dev_list);    INIT_LIST_HEAD(&card->widgets);    INIT_LIST_HEAD(&card->paths);    INIT_LIST_HEAD(&card->dapm_list);}

5.2.3、devm_kzalloc()

card->rtd = devm_kzalloc(card->dev,                 sizeof(struct snd_soc_pcm_runtime) *                 (card->num_links + card->num_aux_devs),                 GFP_KERNEL);    if (card->rtd == NULL)        return -ENOMEM;    card->num_rtd = 0;    card->rtd_aux = &card->rtd[card->num_links];    for (i = 0; i < card->num_links; i++) {        card->rtd[i].card = card;        card->rtd[i].dai_link = &card->dai_link[i];        card->rtd[i].codec_dais = devm_kzalloc(card->dev,                    sizeof(struct snd_soc_dai *) *                    (card->rtd[i].dai_link->num_codecs),                    GFP_KERNEL);        if (card->rtd[i].codec_dais == NULL)            return -ENOMEM;    }    for (i = 0; i < card->num_aux_devs; i++)        card->rtd_aux[i].card = card;    INIT_LIST_HEAD(&card->dapm_dirty);    card->instantiated = 0;    mutex_init(&card->mutex);    mutex_init(&card->dapm_mutex);

为snd_soc_pcm_runtime结构体数组申请内存,每一个dai_link对应snd_soc_pcm_runtime数组的一个单元,然后把snd_soc_card中的dai_link配置复制到相应的snd_soc_pcm_runtime中.
struct snd_soc_card {

struct snd_soc_pcm_runtime *rtd;
//指向struct snd_soc_pcm_runtime实体,共有
ARRAY_SIZE(mt_soc_dai_common)=23个.

}

5.2.4、 snd_soc_instantiate_card(card)

static int snd_soc_instantiate_card(struct snd_soc_card *card){    struct snd_soc_codec *codec;    struct snd_soc_dai_link *dai_link;    int ret, i, order, dai_fmt;    mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT);    /* bind DAIs */    for (i = 0; i < card->num_links; i++) {        ret = soc_bind_dai_link(card, i);        if (ret != 0)            goto base_error;    }    /* bind aux_devs too */    for (i = 0; i < card->num_aux_devs; i++) {        ret = soc_bind_aux_dev(card, i);        if (ret != 0)            goto base_error;    }    /* initialize the register cache for each available codec */    list_for_each_entry(codec, &codec_list, list) {        if (codec->cache_init)            continue;        ret = snd_soc_init_codec_cache(codec);        if (ret < 0)            goto base_error;    }    /* card bind complete so register a sound card */    ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,            card->owner, 0, &card->snd_card);    if (ret < 0) {        dev_err(card->dev,            "ASoC: can't create sound card for card %s: %d\n",            card->name, ret);        goto base_error;    }    card->dapm.bias_level = SND_SOC_BIAS_OFF;    card->dapm.dev = card->dev;    card->dapm.card = card;    list_add(&card->dapm.list, &card->dapm_list);#ifdef CONFIG_DEBUG_FS    snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root);#endif#ifdef CONFIG_PM_SLEEP    /* deferred resume work */    INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);#endif    if (card->dapm_widgets)        snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,                      card->num_dapm_widgets);    /* initialise the sound card only once */    if (card->probe) {        ret = card->probe(card);        if (ret < 0)            goto card_probe_error;    }    /* probe all components used by DAI links on this card */    for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;            order++) {        for (i = 0; i < card->num_links; i++) {            ret = soc_probe_link_components(card, i, order);            if (ret < 0) {                dev_err(card->dev,                    "ASoC: failed to instantiate card %d\n",                    ret);                goto probe_dai_err;            }        }    }    /* probe all DAI links on this card */    for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;            order++) {        for (i = 0; i < card->num_links; i++) {            ret = soc_probe_link_dais(card, i, order);            if (ret < 0) {                dev_err(card->dev,                    "ASoC: failed to instantiate card %d\n",                    ret);                goto probe_dai_err;            }        }    }    for (i = 0; i < card->num_aux_devs; i++) {        ret = soc_probe_aux_dev(card, i);        if (ret < 0) {            dev_err(card->dev,                "ASoC: failed to add auxiliary devices %d\n",                ret);            goto probe_aux_dev_err;        }    }    snd_soc_dapm_link_dai_widgets(card);    snd_soc_dapm_connect_dai_link_widgets(card);    if (card->controls)        snd_soc_add_card_controls(card, card->controls, card->num_controls);    if (card->dapm_routes)        snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,                    card->num_dapm_routes);    for (i = 0; i < card->num_links; i++) {        struct snd_soc_pcm_runtime *rtd = &card->rtd[i];        dai_link = &card->dai_link[i];        dai_fmt = dai_link->dai_fmt;        if (dai_fmt) {            struct snd_soc_dai **codec_dais = rtd->codec_dais;            int j;            for (j = 0; j < rtd->num_codecs; j++) {                struct snd_soc_dai *codec_dai = codec_dais[j];                ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);                if (ret != 0 && ret != -ENOTSUPP)                    dev_warn(codec_dai->dev,                         "ASoC: Failed to set DAI format: %d\n",                         ret);            }        }        /* If this is a regular CPU link there will be a platform */        if (dai_fmt &&            (dai_link->platform_name || dai_link->platform_of_node)) {            ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,                          dai_fmt);            if (ret != 0 && ret != -ENOTSUPP)                dev_warn(card->rtd[i].cpu_dai->dev,                     "ASoC: Failed to set DAI format: %d\n",                     ret);        } else if (dai_fmt) {            /* Flip the polarity for the "CPU" end */            dai_fmt &= ~SND_SOC_DAIFMT_MASTER_MASK;            switch (dai_link->dai_fmt &                SND_SOC_DAIFMT_MASTER_MASK) {            case SND_SOC_DAIFMT_CBM_CFM:                dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;                break;            case SND_SOC_DAIFMT_CBM_CFS:                dai_fmt |= SND_SOC_DAIFMT_CBS_CFM;                break;            case SND_SOC_DAIFMT_CBS_CFM:                dai_fmt |= SND_SOC_DAIFMT_CBM_CFS;                break;            case SND_SOC_DAIFMT_CBS_CFS:                dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;                break;            }            ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,                          dai_fmt);            if (ret != 0 && ret != -ENOTSUPP)                dev_warn(card->rtd[i].cpu_dai->dev,                     "ASoC: Failed to set DAI format: %d\n",                     ret);        }    }    snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),         "%s", card->name);    snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),         "%s", card->long_name ? card->long_name : card->name);    snprintf(card->snd_card->driver, sizeof(card->snd_card->driver),         "%s", card->driver_name ? card->driver_name : card->name);    for (i = 0; i < ARRAY_SIZE(card->snd_card->driver); i++) {        switch (card->snd_card->driver[i]) {        case '_':        case '-':        case '\0':            break;        default:            if (!isalnum(card->snd_card->driver[i]))                card->snd_card->driver[i] = '_';            break;        }    }    if (card->late_probe) {        ret = card->late_probe(card);        if (ret < 0) {            dev_err(card->dev, "ASoC: %s late_probe() failed: %d\n",                card->name, ret);            goto probe_aux_dev_err;        }    }    if (card->fully_routed)        snd_soc_dapm_auto_nc_pins(card);    snd_soc_dapm_new_widgets(card);    ret = snd_card_register(card->snd_card);    if (ret < 0) {        dev_err(card->dev, "ASoC: failed to register soundcard %d\n",                ret);        goto probe_aux_dev_err;    }#ifdef CONFIG_SND_SOC_AC97_BUS    /* register any AC97 codecs */    for (i = 0; i < card->num_rtd; i++) {        ret = soc_register_ac97_dai_link(&card->rtd[i]);        if (ret < 0) {            dev_err(card->dev,                "ASoC: failed to register AC97: %d\n", ret);            while (--i >= 0)                soc_unregister_ac97_dai_link(&card->rtd[i]);            goto probe_aux_dev_err;        }    }#endif    card->instantiated = 1;    snd_soc_dapm_sync(&card->dapm);    mutex_unlock(&card->mutex);    return 0;probe_aux_dev_err:    for (i = 0; i < card->num_aux_devs; i++)        soc_remove_aux_dev(card, i);probe_dai_err:    soc_remove_dai_links(card);card_probe_error:    if (card->remove)        card->remove(card);    snd_card_free(card->snd_card);base_error:    mutex_unlock(&card->mutex);    return ret;}

下面进行分析此函数

5.2.4.1、soc_bind_dai_link(card, i)

ASoC定义了三个全局的链表头变量:codec_list、dai_list、platform_list,系统中所有的Codec、DAI、Platform都在注册时连接到这三个全局链表上。soc_bind_dai_link函数逐个扫描这三个链表,根据card->dai_link[]中的名称进行匹配,匹配后把相应的codec,dai和platform实例赋值到card->rtd[]中(snd_soc_pcm_runtime)。经过这个过程后,snd_soc_pcm_runtime:(card->rtd)中保存了本Machine中使用的Codec,DAI和Platform驱动的信息。

/* bind DAIs */    for (i = 0; i < card->num_links; i++) {        ret = soc_bind_dai_link(card, i);//假设card->num_links=23,所以for循环会执行23次soc_bind_dai_link函数,而且通过i的变化把所有mt_soc_dai_common中的dai_link遍历到。        if (ret != 0)            goto base_error;    }static int soc_bind_dai_link(struct snd_soc_card *card, int num){/*struct snd_soc_pcm_runtime {// 有23个这样的数据结构对象,列出重要的成员       …       struct snd_soc_dai_link *dai_link;//指struct snd_soc_dai_link mt_soc_dai_common[i](i=0~22)       struct snd_soc_codec *codec;// rtd->codec = codec; from codec_list       struct snd_soc_platform *platform;// rtd->platform = platform; from platform_list       struct snd_soc_dai *codec_dai;// rtd->codec_dai = codec_dai; from dai_list       struct snd_soc_dai *cpu_dai;// rtd->cpu_dai = cpu_dai; from dai_list       …}*/    struct snd_soc_dai_link *dai_link = &card->dai_link[num];    struct snd_soc_pcm_runtime *rtd = &card->rtd[num];    struct snd_soc_dai_link_component *codecs = dai_link->codecs;    struct snd_soc_dai_link_component cpu_dai_component;    struct snd_soc_dai **codec_dais = rtd->codec_dais;    struct snd_soc_platform *platform;    const char *platform_name;    int i;    dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num);    cpu_dai_component.name = dai_link->cpu_name;    cpu_dai_component.of_node = dai_link->cpu_of_node;    cpu_dai_component.dai_name = dai_link->cpu_dai_name;    /*          snd_soc_find_dai    // Find CPU DAI from registered DAIs    list_for_each_entry(component, &component_list, list) {        if (dlc->of_node && component->dev->of_node != dlc->of_node)            continue;        if (dlc->name && strcmp(component->name, dlc->name))            continue;        list_for_each_entry(dai, &component->dai_list, list) {            if (dlc->dai_name && strcmp(dai->name, dlc->dai_name))                continue;    */    rtd->cpu_dai = snd_soc_find_dai(&cpu_dai_component);    if (!rtd->cpu_dai) {        dev_err(card->dev, "ASoC: CPU DAI %s not registered\n",            dai_link->cpu_dai_name);        return -EPROBE_DEFER;    }    rtd->num_codecs = dai_link->num_codecs;    /* Find CODEC from registered CODECs */    for (i = 0; i < rtd->num_codecs; i++) {        codec_dais[i] = snd_soc_find_dai(&codecs[i]);        if (!codec_dais[i]) {            dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n",                codecs[i].dai_name);            return -EPROBE_DEFER;        }    }    /* Single codec links expect codec and codec_dai in runtime data */    rtd->codec_dai = codec_dais[0];    rtd->codec = rtd->codec_dai->codec;    /* if there's no platform we match on the empty platform */    platform_name = dai_link->platform_name;    if (!platform_name && !dai_link->platform_of_node)        platform_name = "snd-soc-dummy";    /* find one from the set of registered platforms */    list_for_each_entry(platform, &platform_list, list) {        if (dai_link->platform_of_node) {            if (platform->dev->of_node !=                dai_link->platform_of_node)                continue;        } else {            if (strcmp(platform->component.name, platform_name))                continue;        }        rtd->platform = platform;    }    if (!rtd->platform) {        dev_err(card->dev, "ASoC: platform %s not registered\n",            dai_link->platform_name);        return -EPROBE_DEFER;    }    card->num_rtd++;    return 0;}

到这里为止,machine就把所需要的codec和platform绑定

5.2.4.2、snd_card_new创建声卡

这里写图片描述
声卡下面有多种device,最重要的是pcm和control两类,snd_card_new 会
自动调用snd_ctl_create创建control逻辑设备(controlC0)

/* card bind complete so register a sound card */    ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,            card->owner, 0, &card->snd_card);/** *  snd_card_new - create and initialize a soundcard structure *  @parent: the parent device object *  @idx: card index (address) [0 ... (SNDRV_CARDS-1)] *  @xid: card identification (ASCII string) *  @module: top level module for locking *  @extra_size: allocate this extra size after the main soundcard structure *  @card_ret: the pointer to store the created card instance * *  Creates and initializes a soundcard structure. * *  The function allocates snd_card instance via kzalloc with the given *  space for the driver to use freely.  The allocated struct is stored *  in the given card_ret pointer. * *  Return: Zero if successful or a negative error code. */int snd_card_new(struct device *parent, int idx, const char *xid,            struct module *module, int extra_size,            struct snd_card **card_ret){    struct snd_card *card;    int err;    if (snd_BUG_ON(!card_ret))        return -EINVAL;    *card_ret = NULL;    if (extra_size < 0)        extra_size = 0;    card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);    if (!card)        return -ENOMEM;    if (extra_size > 0)        card->private_data = (char *)card + sizeof(struct snd_card);    if (xid)        strlcpy(card->id, xid, sizeof(card->id));    err = 0;    mutex_lock(&snd_card_mutex);    if (idx < 0) /* first check the matching module-name slot */        idx = get_slot_from_bitmask(idx, module_slot_match, module);    if (idx < 0) /* if not matched, assign an empty slot */        idx = get_slot_from_bitmask(idx, check_empty_slot, module);    if (idx < 0)        err = -ENODEV;    else if (idx < snd_ecards_limit) {        if (test_bit(idx, snd_cards_lock))            err = -EBUSY;   /* invalid */    } else if (idx >= SNDRV_CARDS)        err = -ENODEV;    if (err < 0) {        mutex_unlock(&snd_card_mutex);        dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n",             idx, snd_ecards_limit - 1, err);        kfree(card);        return err;    }    set_bit(idx, snd_cards_lock);       /* lock it */    if (idx >= snd_ecards_limit)        snd_ecards_limit = idx + 1; /* increase the limit */    mutex_unlock(&snd_card_mutex);    card->dev = parent;    card->number = idx;    card->module = module;    INIT_LIST_HEAD(&card->devices);    init_rwsem(&card->controls_rwsem);    rwlock_init(&card->ctl_files_rwlock);    mutex_init(&card->user_ctl_lock);    INIT_LIST_HEAD(&card->controls);    INIT_LIST_HEAD(&card->ctl_files);    spin_lock_init(&card->files_lock);    INIT_LIST_HEAD(&card->files_list);#ifdef CONFIG_PM    mutex_init(&card->power_lock);    init_waitqueue_head(&card->power_sleep);#endif    device_initialize(&card->card_dev);    card->card_dev.parent = parent;    card->card_dev.class = sound_class;    card->card_dev.release = release_card_device;    card->card_dev.groups = card_dev_attr_groups;    err = kobject_set_name(&card->card_dev.kobj, "card%d", idx);    if (err < 0)        goto __error;    /* the control interface cannot be accessed from the user space until */    /* snd_cards_bitmask and snd_cards are set with snd_card_register */    /////////////////////////////////////////////////////////////////////                /*             * create control core:             * called from init.c             */            int snd_ctl_create(struct snd_card *card)            {                static struct snd_device_ops ops = {                    .dev_free = snd_ctl_dev_free,                    .dev_register = snd_ctl_dev_register,                    .dev_disconnect = snd_ctl_dev_disconnect,                };                if (snd_BUG_ON(!card))                    return -ENXIO;                return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);            }    /////////////////////////////////////////////////////////////////////    err = snd_ctl_create(card);//注册control设备    if (err < 0) {        dev_err(parent, "unable to register control minors\n");        goto __error;    }    err = snd_info_card_create(card);    if (err < 0) {        dev_err(parent, "unable to create card info\n");        goto __error_ctl;    }    *card_ret = card;    return 0;      __error_ctl:    snd_device_free_all(card);      __error:    put_device(&card->card_dev);    return err;}//snd_control通过链表挂到snd_card上面。到后面我们会看到snd_card注册的时候会调用每一挂在上面的设备的注册函数,对用snd_control来说,是snd_ctl_dev_register。snd_ctl_create 创建control逻辑设备a)调用snd_device_new,传递snd_device_ops ops,这个ops很关键                                                i.snd_device_new接口把control逻辑设备放在了card->devices里b)ops中的snd_ctl_dev_register接口,实际会被下边的snd_card_register接口调用到,snd_ctl_dev_register接口调用snd_register_device,传递snd_ctl_f_ops,这个ops就是实际使用到的control设备的文件操作./* * create control core: * called from init.c */int snd_ctl_create(struct snd_card *card){    static struct snd_device_ops ops = {        .dev_free = snd_ctl_dev_free,        .dev_register = snd_ctl_dev_register,        .dev_disconnect = snd_ctl_dev_disconnect,    };    if (snd_BUG_ON(!card))        return -ENXIO;    return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);}static int snd_ctl_dev_register(struct snd_device *device){    struct snd_card *card = device->device_data;    int err, cardnum;    char name[16];    if (snd_BUG_ON(!card))        return -ENXIO;    cardnum = card->number;    if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))        return -ENXIO;    sprintf(name, "controlC%i", cardnum);    if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,                       &snd_ctl_f_ops, card, name)) < 0)        return err;    return 0;}/** * snd_device_new - create an ALSA device component * @card: the card instance * @type: the device type, SNDRV_DEV_XXX * @device_data: the data pointer of this device * @ops: the operator table * * Creates a new device component for the given data pointer. * The device will be assigned to the card and managed together * by the card. * * The data pointer plays a role as the identifier, too, so the * pointer address must be unique and unchanged. * * Return: Zero if successful, or a negative error code on failure. */int snd_device_new(struct snd_card *card, enum snd_device_type type,           void *device_data, struct snd_device_ops *ops){    struct snd_device *dev;    struct list_head *p;    if (snd_BUG_ON(!card || !device_data || !ops))        return -ENXIO;    dev = kzalloc(sizeof(*dev), GFP_KERNEL);    if (dev == NULL) {        dev_err(card->dev, "Cannot allocate device, type=%d\n", type);        return -ENOMEM;    }    INIT_LIST_HEAD(&dev->list);    dev->card = card;    dev->type = type;    dev->state = SNDRV_DEV_BUILD;    dev->device_data = device_data;    dev->ops = ops;    /* insert the entry in an incrementally sorted list */    list_for_each_prev(p, &card->devices) {        struct snd_device *pdev = list_entry(p, struct snd_device, list);        if ((unsigned int)pdev->type <= (unsigned int)type)            break;    }    list_add(&dev->list, p);    return 0;}

5.2.4.3、snd_soc_dapm_new_controls

创建damp控件

if (card->dapm_widgets)        snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,                      card->num_dapm_widgets);/** * snd_soc_dapm_new_controls - create new dapm controls * @dapm: DAPM context * @widget: widget array * @num: number of widgets * * Creates new DAPM controls based upon the templates. * * Returns 0 for success else error. */int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,    const struct snd_soc_dapm_widget *widget,    int num){    struct snd_soc_dapm_widget *w;    int i;    int ret = 0;    mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);    for (i = 0; i < num; i++) {/*有很多种控件进行选择创建case snd_soc_dapm_regulator_supplycase snd_soc_dapm_clock_supplycase snd_soc_dapm_switch:case snd_soc_dapm_mixer:case snd_soc_dapm_mixer_named_ctl:case snd_soc_dapm_mux:case snd_soc_dapm_dai_out:case snd_soc_dapm_dai_in:case snd_soc_dapm_adc:case snd_soc_dapm_aif_out:case snd_soc_dapm_dac:case snd_soc_dapm_aif_in:case snd_soc_dapm_pga:case snd_soc_dapm_out_drv:case snd_soc_dapm_input:case snd_soc_dapm_output:case snd_soc_dapm_micbias:case snd_soc_dapm_spk:case snd_soc_dapm_hp:case snd_soc_dapm_mic:case snd_soc_dapm_line:case snd_soc_dapm_dai_link:case snd_soc_dapm_supply:case snd_soc_dapm_regulator_supply:case snd_soc_dapm_clock_supply:case snd_soc_dapm_kcontrol:*/        w = snd_soc_dapm_new_control(dapm, widget);        if (!w) {            dev_err(dapm->dev,                "ASoC: Failed to create DAPM control %s\n",                widget->name);            ret = -ENOMEM;            break;        }        widget++;    }    mutex_unlock(&dapm->card->dapm_mutex);    return ret;}

5.2.4.4、soc_probe_link_components

soc_probe_link_components->soc_probe_codec->soc_probe_platform

/* initialise the sound card only once */    if (card->probe) {//依次调用各个子结构的probe函数        ret = card->probe(card);        if (ret < 0)            goto card_probe_error;    }    /* probe all components used by DAI links on this card */    for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;            order++) {        for (i = 0; i < card->num_links; i++) {            ret = soc_probe_link_components(card, i, order);//card->num_links=23,所以for循环会执行23次soc_probe_link_components函数,该函数出了挨个调用了codec,dai和platform驱动的probe函数            if (ret < 0) {                dev_err(card->dev,                    "ASoC: failed to instantiate card %d\n",                    ret);                goto probe_dai_err;            }        }    }/* probe all DAI links on this card */    for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;            order++) {        for (i = 0; i < card->num_links; i++) {            ret = soc_probe_link_dais(card, i, order);            if (ret < 0) {                dev_err(card->dev,                    "ASoC: failed to instantiate card %d\n",                    ret);                goto probe_dai_err;            }        }    }1)先看下   soc_probe_link_componentsstatic int soc_probe_link_components(struct snd_soc_card *card, int num,                     int order){    struct snd_soc_pcm_runtime *rtd = &card->rtd[num];    struct snd_soc_platform *platform = rtd->platform;    struct snd_soc_component *component;    int i, ret;    /* probe the ==CPU-side== component, if it is a CODEC */    component = rtd->cpu_dai->component;    if (component->driver->probe_order == order) {        ret = soc_probe_component(card, component);        if (ret < 0)            return ret;    }codec->driver-> probe(codec)这个probe(codec)就是前面分析的.probe = mt6331_codec_probe    /* probe the ==CODEC-side== components */    for (i = 0; i < rtd->num_codecs; i++) {        component = rtd->codec_dais[i]->component;        if (component->driver->probe_order == order) {            ret = soc_probe_component(card, component);            if (ret < 0)                return ret;        }    }//调用platform probe    /* probe the ==platform== */    if (platform->component.driver->probe_order == order) {        ret = soc_probe_component(card, &platform->component);        if (ret < 0)            return ret;    }    return 0;}

soc_probe_component就是添加前面调用codec、platform的probe函数里的control、widgets等。

static int soc_probe_component(struct snd_soc_card *card,    struct snd_soc_component *component){    struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);    struct snd_soc_dai *dai;    int ret;    if (component->probed)        return 0;    component->card = card;    dapm->card = card;    soc_set_name_prefix(card, component);    if (!try_module_get(component->dev->driver->owner))        return -ENODEV;    soc_init_component_debugfs(component);    if (component->dapm_widgets) {        ret = snd_soc_dapm_new_controls(dapm, component->dapm_widgets,            component->num_dapm_widgets);//添加新的control        if (ret != 0) {            dev_err(component->dev,                "Failed to create new controls %d\n", ret);            goto err_probe;        }    }    list_for_each_entry(dai, &component->dai_list, list) {        ret = snd_soc_dapm_new_dai_widgets(dapm, dai);//添加新的widgets        if (ret != 0) {            dev_err(component->dev,                "Failed to create DAI widgets %d\n", ret);            goto err_probe;        }    }    if (component->probe) {        ret = component->probe(component);        if (ret < 0) {            dev_err(component->dev,                "ASoC: failed to probe component %d\n", ret);            goto err_probe;        }        WARN(dapm->idle_bias_off &&            dapm->bias_level != SND_SOC_BIAS_OFF,            "codec %s can not start from non-off bias with idle_bias_off==1\n",            component->name);    }    if (component->controls)        snd_soc_add_component_controls(component, component->controls,                     component->num_controls);//添加component controls    if (component->dapm_routes)        snd_soc_dapm_add_routes(dapm, component->dapm_routes,                    component->num_dapm_routes);//添加dapm路径    component->probed = 1;    list_add(&dapm->list, &card->dapm_list);    /* This is a HACK and will be removed soon */    if (component->codec)        list_add(&component->codec->card_list, &card->codec_dev_list);    return 0;err_probe:    soc_cleanup_component_debugfs(component);    module_put(component->dev->driver->owner);    return ret;}

2)下面看下soc_probe_link_dais

static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order){    struct snd_soc_dai_link *dai_link = &card->dai_link[num];    struct snd_soc_pcm_runtime *rtd = &card->rtd[num];    struct snd_soc_platform *platform = rtd->platform;    struct snd_soc_dai *cpu_dai = rtd->cpu_dai;    int i, ret;    dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n",            card->name, num, order);    /* config components */    cpu_dai->platform = platform;    cpu_dai->card = card;    for (i = 0; i < rtd->num_codecs; i++)        rtd->codec_dais[i]->card = card;    /* set default power off timeout */    rtd->pmdown_time = pmdown_time;//调用cpu_dai probe    /* probe the cpu_dai */    if (!cpu_dai->probed &&            cpu_dai->driver->probe_order == order) {        if (cpu_dai->driver->probe) {            ret = cpu_dai->driver->probe(cpu_dai);            if (ret < 0) {                dev_err(cpu_dai->dev,                    "ASoC: failed to probe CPU DAI %s: %d\n",                    cpu_dai->name, ret);                return ret;            }        }        cpu_dai->probed = 1;    }//调用codec_dai probe    /* probe the CODEC DAI */    for (i = 0; i < rtd->num_codecs; i++) {        ret = soc_probe_codec_dai(card, rtd->codec_dais[i], order);        if (ret)            return ret;    }    /* complete DAI probe during last probe */    if (order != SND_SOC_COMP_ORDER_LAST)        return 0;    /* do machine specific initialization */    if (dai_link->init) {        ret = dai_link->init(rtd);        if (ret < 0) {            dev_err(card->dev, "ASoC: failed to init %s: %d\n",                dai_link->name, ret);            return ret;        }    }    ret = soc_post_component_init(rtd, dai_link->name);    if (ret)        return ret;#ifdef CONFIG_DEBUG_FS    /* add DPCM sysfs entries */    if (dai_link->dynamic) {        ret = soc_dpcm_debugfs_add(rtd);        if (ret < 0) {            dev_err(rtd->dev,                "ASoC: failed to add dpcm sysfs entries: %d\n",                ret);            return ret;        }    }#endif    ret = device_create_file(rtd->dev, &dev_attr_pmdown_time);    if (ret < 0)        dev_warn(rtd->dev, "ASoC: failed to add pmdown_time sysfs: %d\n",            ret);    if (cpu_dai->driver->compress_dai) {        /*create compress_device"*/        ret = soc_new_compress(rtd, num);        if (ret < 0) {            dev_err(card->dev, "ASoC: can't create compress %s\n",                     dai_link->stream_name);            return ret;        }    } else {        if (!dai_link->params) {            /* create the pcm */            ret = soc_new_pcm(rtd, num);//创建PCM设备            if (ret < 0) {                dev_err(card->dev, "ASoC: can't create pcm %s :%d\n",                       dai_link->stream_name, ret);                return ret;            }        } else {            INIT_DELAYED_WORK(&rtd->delayed_work,                        codec2codec_close_delayed_work);            /* link the DAI widgets */            ret = soc_link_dai_widgets(card, dai_link, rtd);            if (ret)                return ret;        }    }    /* add platform data for AC97 devices */    for (i = 0; i < rtd->num_codecs; i++) {        if (rtd->codec_dais[i]->driver->ac97_control)            snd_ac97_dev_add_pdata(rtd->codec_dais[i]->codec->ac97,                           rtd->cpu_dai->ac97_pdata);    }    return 0;}

这里写图片描述

前面已经创建了control设备,现在soc_probe_link_dais调用soc_new_pcm创建pcm设备。
1)设置pcm native中要使用的pcm操作函数,这些函数用于操作音频物理设备,包括machine、codec_dai、cpu_dai、platform;
2)调用snd_pcm_new()创建pcm设备,回放子流实例和录制子流实例都在这里创建;
3)回调platform驱动的pcm_new(),完成音频dma设备初始化和dma buffer内存分配;

/* create a new pcm */int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num){    struct snd_soc_platform *platform = rtd->platform;    struct snd_soc_dai *codec_dai;    struct snd_soc_dai *cpu_dai = rtd->cpu_dai;    struct snd_pcm *pcm;    char new_name[64];    int ret = 0, playback = 0, capture = 0;    int i;    if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {        playback = rtd->dai_link->dpcm_playback;        capture = rtd->dai_link->dpcm_capture;    } else {        for (i = 0; i < rtd->num_codecs; i++) {            codec_dai = rtd->codec_dais[i];            if (codec_dai->driver->playback.channels_min)                playback = 1;            if (codec_dai->driver->capture.channels_min)                capture = 1;        }        capture = capture && cpu_dai->driver->capture.channels_min;        playback = playback && cpu_dai->driver->playback.channels_min;    }    if (rtd->dai_link->playback_only) {        playback = 1;        capture = 0;    }    if (rtd->dai_link->capture_only) {        playback = 0;        capture = 1;    }    /* create the PCM */    if (rtd->dai_link->no_pcm) {        snprintf(new_name, sizeof(new_name), "(%s)",            rtd->dai_link->stream_name);        ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num,                playback, capture, &pcm);    } else {        if (rtd->dai_link->dynamic)            snprintf(new_name, sizeof(new_name), "%s (*)",                rtd->dai_link->stream_name);        else            snprintf(new_name, sizeof(new_name), "%s %s-%d",                rtd->dai_link->stream_name,                (rtd->num_codecs > 1) ?                "multicodec" : rtd->codec_dai->name, num);/*snd_pcm_new->_snd_pcm_new*/        ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback,            capture, &pcm);//创建pcm实例    }    if (ret < 0) {        dev_err(rtd->card->dev, "ASoC: can't create pcm for %s\n",            rtd->dai_link->name);        return ret;    }    dev_dbg(rtd->card->dev, "ASoC: registered pcm #%d %s\n",num, new_name);    /* DAPM dai link stream work */    INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);    rtd->pcm = pcm;    pcm->private_data = rtd;    if (rtd->dai_link->no_pcm) {        if (playback)            pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd;        if (capture)            pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd;        goto out;    }    /* ASoC PCM operations */    if (rtd->dai_link->dynamic) {        rtd->ops.open       = dpcm_fe_dai_open;        rtd->ops.hw_params  = dpcm_fe_dai_hw_params;        rtd->ops.prepare    = dpcm_fe_dai_prepare;        rtd->ops.trigger    = dpcm_fe_dai_trigger;        rtd->ops.hw_free    = dpcm_fe_dai_hw_free;        rtd->ops.close      = dpcm_fe_dai_close;        rtd->ops.pointer    = soc_pcm_pointer;        rtd->ops.ioctl      = soc_pcm_ioctl;    } else {    //snd_pcm实例最后是通过它的子流Substream->ops直接或者间接(pcm中间层函数)对具体驱动进行调用    //Substream->ops =rtd->ops     //Substream->ops.open = soc_pcm_open = platform->driver->ops->open = mtk_capture_pcm_open        rtd->ops.open       = soc_pcm_open;        rtd->ops.hw_params  = soc_pcm_hw_params;        rtd->ops.prepare    = soc_pcm_prepare;        rtd->ops.trigger    = soc_pcm_trigger;        rtd->ops.hw_free    = soc_pcm_hw_free;        rtd->ops.close      = soc_pcm_close;        rtd->ops.pointer    = soc_pcm_pointer;        rtd->ops.ioctl      = soc_pcm_ioctl;    }    if (platform->driver->ops) {        rtd->ops.ack        = platform->driver->ops->ack;        rtd->ops.copy       = platform->driver->ops->copy;        rtd->ops.silence    = platform->driver->ops->silence;        rtd->ops.page       = platform->driver->ops->page;        rtd->ops.mmap       = platform->driver->ops->mmap;    }    if (playback)        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);    if (capture)        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);    if (platform->driver->pcm_new) {        ret = platform->driver->pcm_new(rtd);        if (ret < 0) {            dev_err(platform->dev,                "ASoC: pcm constructor failed: %d\n",                ret);            return ret;        }    }    pcm->private_free = platform->driver->pcm_free;out:    dev_info(rtd->card->dev, "%s <-> %s mapping ok\n",         (rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name,         cpu_dai->name);    return ret;}

snd_pcm_new
_snd_pcm_new
snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)
snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)
snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)
rtd->ops.open = soc_pcm_open;
首先看下_snd_pcm_new

ALSA已经为我们实现了功能强劲的PCM中间层,自己的驱动中只要实现一些底层的需要访问硬件的函数即可。一个pcm实例由一个playback stream和一个capture stream组成,这两个stream又分别有一个或多个substreams组成。

static int _snd_pcm_new(struct snd_card *card, const char *id, int device,        int playback_count, int capture_count, bool internal,        struct snd_pcm **rpcm){    struct snd_pcm *pcm;    int err;    static struct snd_device_ops ops = {        .dev_free = snd_pcm_dev_free,        .dev_register = snd_pcm_dev_register,        .dev_disconnect = snd_pcm_dev_disconnect,    };    if (snd_BUG_ON(!card))        return -ENXIO;    if (rpcm)        *rpcm = NULL;    pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);    if (pcm == NULL) {        dev_err(card->dev, "Cannot allocate PCM\n");        return -ENOMEM;    }    pcm->card = card;    pcm->device = device;    pcm->internal = internal;    if (id)        strlcpy(pcm->id, id, sizeof(pcm->id));    if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {        snd_pcm_free(pcm);        return err;    }    if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) {        snd_pcm_free(pcm);        return err;    }    mutex_init(&pcm->open_mutex);    init_waitqueue_head(&pcm->open_wait);    if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) {        snd_pcm_free(pcm);        return err;    }    if (rpcm)        *rpcm = pcm;    return 0;}
static int snd_pcm_dev_register(struct snd_device *device){    int cidx, err;    struct snd_pcm_substream *substream;    struct snd_pcm_notify *notify;    char str[16];    struct snd_pcm *pcm;    struct device *dev;    if (snd_BUG_ON(!device || !device->device_data))        return -ENXIO;    pcm = device->device_data;    mutex_lock(&register_mutex);    err = snd_pcm_add(pcm);    if (err) {        mutex_unlock(&register_mutex);        return err;    }    for (cidx = 0; cidx < 2; cidx++) {        int devtype = -1;        if (pcm->streams[cidx].substream == NULL || pcm->internal)            continue;        switch (cidx) {        case SNDRV_PCM_STREAM_PLAYBACK:            sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);            devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;            break;        case SNDRV_PCM_STREAM_CAPTURE:            sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);            devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;            break;        }        //pcmC%iD%ip  pcmC%iD%ic末尾p和c分别代表播放和录音,可以再ls dev/snd下看到所有声卡设备        /* device pointer to use, pcm->dev takes precedence if         * it is assigned, otherwise fall back to card's device         * if possible */        dev = pcm->dev;        if (!dev)            dev = snd_card_get_device_link(pcm->card);        /* register pcm */        err = snd_register_device_for_dev(devtype, pcm->card,                          pcm->device,                          &snd_pcm_f_ops[cidx],                          pcm, str, dev);//注册pcm设备    /*    const struct file_operations snd_pcm_f_ops[2] = {    {        .owner =        THIS_MODULE,        .read =         snd_pcm_read,        .aio_read =     snd_pcm_aio_read,        .open =         snd_pcm_capture_open,        .release =      snd_pcm_release,        .llseek =       no_llseek,        .poll =         snd_pcm_capture_poll,        .unlocked_ioctl =   snd_pcm_capture_ioctl,        .compat_ioctl =     snd_pcm_ioctl_compat,        .mmap =         snd_pcm_mmap,        .fasync =       snd_pcm_fasync,        .get_unmapped_area =    snd_pcm_get_unmapped_area,    }};    */            if (err < 0) {            list_del(&pcm->list);            mutex_unlock(&register_mutex);            return err;        }        dev = snd_get_device(devtype, pcm->card, pcm->device);        if (dev) {            err = sysfs_create_groups(&dev->kobj,                          pcm_dev_attr_groups);            if (err < 0)                dev_warn(dev,                     "pcm %d:%d: cannot create sysfs groups\n",                     pcm->card->number, pcm->device);            put_device(dev);        }        for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)            snd_pcm_timer_init(substream);    }    list_for_each_entry(notify, &snd_pcm_notify_list, list)        notify->n_register(pcm);    mutex_unlock(&register_mutex);    return 0;}

这里写图片描述
这里写图片描述
从这HAL层的PCM的一些api就可以调用操作kernel的声卡设备。

5.2.4.5、snd_soc_dapm_link_dai_widgets(card)

默认情况下,驱动不会通过snd_soc_route来主动定义dai widget和stream widget之间的连接关系,实际上,他们之间的连接关系是由ASoc负责的,在声卡的初始化函数中,使用snd_soc_dapm_link_dai_widgets函数来建立他们之间的连接关系,接着,再次从头遍历声卡中所有的widget,找出能与dai widget相连接的stream widget,第一个前提条件是这两个widget必须位于同一个dapm context中

snd_soc_dapm_link_dai_widgets(card);//建立dai widget和stream widget之间的连接关系snd_soc_dapm_connect_dai_link_widgets(card);我们再来分析一下snd_soc_dapm_link_dai_widgets函数,看看它是如何连接这两种widget的,它先是遍历声卡中所有的widget,找出类型为snd_soc_dapm_dai_in和snd_soc_dapm_dai_out的widget,通过widget的priv字段,取出widget对应的snd_soc_dai结构指针int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card){    struct snd_soc_dapm_widget *dai_w, *w;    struct snd_soc_dapm_widget *src, *sink;    struct snd_soc_dai *dai;//我们再来分析一下snd_soc_dapm_link_dai_widgets函数,看看它是如何连接这两种widget的,它先是遍历声卡中所有的widget,找出类型为snd_soc_dapm_dai_in和snd_soc_dapm_dai_out的widget,通过widget的priv字段,取出widget对应的snd_soc_dai结构指针    /* For each DAI widget... */    list_for_each_entry(dai_w, &card->widgets, list) {        switch (dai_w->id) {        case snd_soc_dapm_dai_in:        case snd_soc_dapm_dai_out:            break;        default:            continue;        }        dai = dai_w->priv;//接着,再次从头遍历声卡中所有的widget,找出能与dai widget相连接的stream widget,第一个前提条件是这两个widget必须位于同一个dapm context中        /* ...find all widgets with the same stream and link them */        list_for_each_entry(w, &card->widgets, list) {            if (w->dapm != dai_w->dapm)                continue;//dai widget不会与dai widget相连,所以跳过它们            switch (w->id) {            case snd_soc_dapm_dai_in:            case snd_soc_dapm_dai_out:                continue;            default:                break;            }//dai widget的名字没有出现在要连接的widget的stream name中,跳过这个widget            if (!w->sname || !strstr(w->sname, dai_w->name))                continue;            if (dai_w->id == snd_soc_dapm_dai_in) {                src = dai_w;                sink = w;            } else {                src = w;                sink = dai_w;            }            dev_dbg(dai->dev, "%s -> %s\n", src->name, sink->name);            snd_soc_dapm_add_path(w->dapm, src, sink, NULL, NULL);        }    }    return 0;}
下面看下snd_soc_dapm_connect_dai_link_widgets(card)void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card){    struct snd_soc_pcm_runtime *rtd = card->rtd;    int i;    /* for each BE DAI link... */    for (i = 0; i < card->num_rtd; i++) {        rtd = &card->rtd[i];        /*         * dynamic FE links have no fixed DAI mapping.         * CODEC<->CODEC links have no direct connection.         */        if (rtd->dai_link->dynamic || rtd->dai_link->params)            continue;        dapm_connect_dai_link_widgets(card, rtd);    }}//如果widget的stream name包含了dai的stream name,则匹配成功,连接这两个widgetstatic void dapm_connect_dai_link_widgets(struct snd_soc_card *card,                      struct snd_soc_pcm_runtime *rtd){    struct snd_soc_dai *cpu_dai = rtd->cpu_dai;    struct snd_soc_dapm_widget *sink, *source;    int i;    for (i = 0; i < rtd->num_codecs; i++) {        struct snd_soc_dai *codec_dai = rtd->codec_dais[i];        /* there is no point in connecting BE DAI links with dummies */        if (snd_soc_dai_is_dummy(codec_dai) ||            snd_soc_dai_is_dummy(cpu_dai))            continue;        /* connect BE DAI playback if widgets are valid */        if (codec_dai->playback_widget && cpu_dai->playback_widget) {            source = cpu_dai->playback_widget;            sink = codec_dai->playback_widget;            dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n",                cpu_dai->component->name, source->name,                codec_dai->component->name, sink->name);            snd_soc_dapm_add_path(&card->dapm, source, sink,                NULL, NULL);        }        /* connect BE DAI capture if widgets are valid */        if (codec_dai->capture_widget && cpu_dai->capture_widget) {            source = codec_dai->capture_widget;            sink = cpu_dai->capture_widget;            dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n",                codec_dai->component->name, source->name,                cpu_dai->component->name, sink->name);            snd_soc_dapm_add_path(&card->dapm, source, sink,                NULL, NULL);        }    }}

由此可见,dai widget和stream widget是通过stream name进行匹配的,所以,我们在定义codec的stream widget时,
它们的stream name必须要包含dai的stream name,这样才能让ASoc自动把这两种widget连接在一起,只有把它们连接
在一起,ASoc中的播放、录音和停止等事件,才能通过dai widget传递到codec中,使得codec中的widget能根据目前的
播放状态,动态地开启或关闭音频路径上所有widget的电源。

5.2.4.6、snd_soc_add_card_controls

snd_soc_add_card_controls(card, card->controls, card->num_controls)/** * snd_soc_add_card_controls - add an array of controls to a SoC card. * Convenience function to add a list of controls. * * @soc_card: SoC card to add controls to * @controls: array of controls to add * @num_controls: number of elements in the array * * Return 0 for success, else error. */int snd_soc_add_card_controls(struct snd_soc_card *soc_card,    const struct snd_kcontrol_new *controls, int num_controls){    struct snd_card *card = soc_card->snd_card;    return snd_soc_add_controls(card, soc_card->dev, controls, num_controls,            NULL, soc_card);}static int snd_soc_add_controls(struct snd_card *card, struct device *dev,    const struct snd_kcontrol_new *controls, int num_controls,    const char *prefix, void *data){    int err, i;    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进行添加实例化到声卡中        if (err < 0) {            dev_err(dev, "ASoC: Failed to add %s: %d\n",                control->name, err);            return err;        }    }    return 0;}

5.2.4.7、snd_soc_dapm_add_routes

注册machine级别的路径连接信息

snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,                    card->num_dapm_routes);/** * snd_soc_dapm_add_routes - Add routes between DAPM widgets * @dapm: DAPM context * @route: audio routes * @num: number of routes * * Connects 2 dapm widgets together via a named audio path. The sink is * the widget receiving the audio signal, whilst the source is the sender * of the audio signal. * * Returns 0 for success else error. On error all resources can be freed * with a call to snd_soc_card_free(). */int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,                const struct snd_soc_dapm_route *route, int num){    int i, r, ret = 0;    mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);    for (i = 0; i < num; i++) {        r = snd_soc_dapm_add_route(dapm, route);        if (r < 0) {            dev_err(dapm->dev, "ASoC: Failed to add route %s -> %s -> %s\n",                route->source,                route->control ? route->control : "direct",                route->sink);            ret = r;        }        route++;    }    mutex_unlock(&dapm->card->dapm_mutex);    return ret;}该函数只是一个循环,依次对参数传入的数组调用snd_soc_dapm_add_route,主要的工作由snd_soc_dapm_add_route完成。我们进入snd_soc_dapm_add_route函数看看:static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,                  const struct snd_soc_dapm_route *route){    struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;    struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL;    const char *sink;    const char *source;    char prefixed_sink[80];    char prefixed_source[80];    const char *prefix;    int ret;    prefix = soc_dapm_prefix(dapm);    if (prefix) {        snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",             prefix, route->sink);        sink = prefixed_sink;        snprintf(prefixed_source, sizeof(prefixed_source), "%s %s",             prefix, route->source);        source = prefixed_source;    } else {        sink = route->sink;        source = route->source;    }    /*     * find src and dest widgets over all widgets but favor a widget from     * current DAPM context     */    list_for_each_entry(w, &dapm->card->widgets, list) {        if (!wsink && !(strcmp(w->name, sink))) {            wtsink = w;            if (w->dapm == dapm)                wsink = w;            continue;        }        if (!wsource && !(strcmp(w->name, source))) {            wtsource = w;            if (w->dapm == dapm)                wsource = w;        }    }//上面代码用widget的名字来比较,遍历声卡的widgets链表,找出源widget和目的widget的指针//如果在本dapm context中没有找到,则使用别的dapm context中找到的widget    /* use widget from another DAPM context if not found from this */    if (!wsink)        wsink = wtsink;    if (!wsource)        wsource = wtsource;    if (wsource == NULL) {        dev_err(dapm->dev, "ASoC: no source widget found for %s\n",            route->source);        return -ENODEV;    }    if (wsink == NULL) {        dev_err(dapm->dev, "ASoC: no sink widget found for %s\n",            route->sink);        return -ENODEV;    }/*snd_soc_dapm_add_path函数是整个调用链条中的关键*/    ret = snd_soc_dapm_add_path(dapm, wsource, wsink, route->control,        route->connected);    if (ret)        goto err;    return 0;err:    dev_warn(dapm->dev, "ASoC: no dapm match for %s --> %s --> %s\n",         source, route->control, sink);    return ret;}static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,    struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,    const char *control,    int (*connected)(struct snd_soc_dapm_widget *source,             struct snd_soc_dapm_widget *sink)){    struct snd_soc_dapm_path *path;    int ret;    path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);    if (!path)        return -ENOMEM;    path->source = wsource;    path->sink = wsink;    path->connected = connected;    INIT_LIST_HEAD(&path->list);    INIT_LIST_HEAD(&path->list_kcontrol);    INIT_LIST_HEAD(&path->list_source);    INIT_LIST_HEAD(&path->list_sink);//函数的一开始,首先为这个连接分配了一个snd_soc_path结构,path的source和sink字段分别指向源widget和目的widget,connected字段保存connected回调函数,初始化几个snd_soc_path结构中的几个链表。    /* check for external widgets */    if (wsink->id == snd_soc_dapm_input) {        if (wsource->id == snd_soc_dapm_micbias ||            wsource->id == snd_soc_dapm_mic ||            wsource->id == snd_soc_dapm_line ||            wsource->id == snd_soc_dapm_output)            wsink->ext = 1;    }    if (wsource->id == snd_soc_dapm_output) {        if (wsink->id == snd_soc_dapm_spk ||            wsink->id == snd_soc_dapm_hp ||            wsink->id == snd_soc_dapm_line ||            wsink->id == snd_soc_dapm_input)            wsource->ext = 1;    }//这段代码用于判断是否有外部连接关系,如果有,置位widget的ext字段。判断方法从代码中可以方便地看出:    目的widget是一个输入脚,如果源widget是mic、line、micbias或output,则认为目的widget具有外部连接关系。    源widget是一个输出脚,如果目的widget是spk、hp、line或input,则认为源widget具有外部连接关系。    dapm_mark_dirty(wsource, "Route added");    dapm_mark_dirty(wsink, "Route added");    /* connect static paths */    if (control == NULL) {        list_add(&path->list, &dapm->card->paths);        list_add(&path->list_sink, &wsink->sources);        list_add(&path->list_source, &wsource->sinks);        path->connect = 1;        return 0;    }//因为增加了连结关系,所以把源widget和目的widget加入到dapm_dirty链表中。如果没有kcontrol来控制该连接关系,则这是一个静态连接,直接用path把它们连接在一起。在接着往下看:    /* connect dynamic paths */    switch (wsink->id) {    case snd_soc_dapm_adc:    case snd_soc_dapm_dac:    case snd_soc_dapm_pga:    case snd_soc_dapm_out_drv:    case snd_soc_dapm_input:    case snd_soc_dapm_output:    case snd_soc_dapm_siggen:    case snd_soc_dapm_micbias:    case snd_soc_dapm_vmid:    case snd_soc_dapm_pre:    case snd_soc_dapm_post:    case snd_soc_dapm_supply:    case snd_soc_dapm_regulator_supply:    case snd_soc_dapm_clock_supply:    case snd_soc_dapm_aif_in:    case snd_soc_dapm_aif_out:    case snd_soc_dapm_dai_in:    case snd_soc_dapm_dai_out:    case snd_soc_dapm_dai_link:    case snd_soc_dapm_kcontrol:        list_add(&path->list, &dapm->card->paths);        list_add(&path->list_sink, &wsink->sources);        list_add(&path->list_source, &wsource->sinks);        path->connect = 1;        return 0;//按照目的widget来判断,如果属于以上这些类型,直接把它们连接在一起即可,这段感觉有点多余,因为通常以上这些类型的widget本来也没有kcontrol,直接用上一段代码就可以了,也许是dapm的作者们想着以后可能会有所扩展吧。    case snd_soc_dapm_mux:        ret = dapm_connect_mux(dapm, wsource, wsink, path, control,            &wsink->kcontrol_news[0]);        if (ret != 0)            goto err;        break;    case snd_soc_dapm_switch:    case snd_soc_dapm_mixer:    case snd_soc_dapm_mixer_named_ctl:        ret = dapm_connect_mixer(dapm, wsource, wsink, path, control);        if (ret != 0)            goto err;        break;//目的widget如果是mixer和mux类型,分别用dapm_connect_mixer和dapm_connect_mux函数完成连接工作    case snd_soc_dapm_hp:    case snd_soc_dapm_mic:    case snd_soc_dapm_line:    case snd_soc_dapm_spk:        list_add(&path->list, &dapm->card->paths);        list_add(&path->list_sink, &wsink->sources);        list_add(&path->list_source, &wsource->sinks);        path->connect = 0;        return 0;    }//hp、mic、line和spk这几种widget属于外部器件,也只是简单地连接在一起,不过connect字段默认为是未连接状态。    return 0;err:    kfree(path);    return ret;}

到这里为止,我们为声卡创建并初始化好了所需的widget,各个widget也通过path连接在了一起,接下来,dapm等待用户的指令,一旦某个dapm kcontrol被用户空间改变,利用这些连接关系,dapm会重新创建音频路径,脱离音频路径的widget会被下电,加入音频路径的widget会被上电,所有的上下电动作都会自动完成,用户空间的应用程序无需关注这些变化,它只管按需要改变某个dapm kcontrol即可。

5.2.4.8、snd_card_register(card->snd_card)

注册声卡,在这个阶段会遍历声卡下的所有逻辑设备,并且调用各设备的注册回调函数,对于pcm,前面提到的snd_pcm_dev_register函数,该回调函数建立了和用户空间应用程序(alsa-lib)通信所用的设备文件节点:/dev/snd/pcmCxxDxxp和/dev/snd/pcmCxxDxxc

/** *  snd_card_register - register the soundcard *  @card: soundcard structure * *  This function registers all the devices assigned to the soundcard. *  Until calling this, the ALSA control interface is blocked from the *  external accesses.  Thus, you should call this function at the end *  of the initialization of the card. * *  Return: Zero otherwise a negative error code if the registration failed. */int snd_card_register(struct snd_card *card){    int err;    if (snd_BUG_ON(!card))        return -EINVAL;//声卡的class将会出现在文件系统的/sys/class/sound/下面,并且,device_add(&card->card_dev)也决定了相应的设备节点也将会出现在/dev/snd/下面    if (!card->registered) {        err = device_add(&card->card_dev);        if (err < 0)            return err;        card->registered = true;    }    if ((err = snd_device_register_all(card)) < 0)//通过snd_device_register_all()注册所有挂在该声卡下的逻辑设备,snd_device_register_all()实际上是通过snd_card的devices链表,遍历所有的snd_device,并且调用snd_device的ops->dev_register()来实现各自设备的注册的        return err;    mutex_lock(&snd_card_mutex);    if (snd_cards[card->number]) {        /* already registered */        mutex_unlock(&snd_card_mutex);        return 0;    }    if (*card->id) {        /* make a unique id name from the given string */        char tmpid[sizeof(card->id)];        memcpy(tmpid, card->id, sizeof(card->id));        snd_card_set_id_no_lock(card, tmpid, tmpid);    } else {        /* create an id from either shortname or longname */        const char *src;        src = *card->shortname ? card->shortname : card->longname;        snd_card_set_id_no_lock(card, src,                    retrieve_id_from_card_name(src));    }    snd_cards[card->number] = card;    mutex_unlock(&snd_card_mutex);    init_info_for_card(card);#if IS_ENABLED(CONFIG_SND_MIXER_OSS)    if (snd_mixer_oss_notify_callback)        snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);#endif    return 0;}

至此,整个ALSA Driver就分析完了。下面使用流程图来总结下alsa驱动整体调用流程。
这里写图片描述

0 0
原创粉丝点击