1. 程式人生 > >MT6737 Android N 平臺 Audio系統學習----ALSA Driver

MT6737 Android N 平臺 Audio系統學習----ALSA Driver

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 = {