MTK 驱动(61)---MT6737 Android N 平台 ----ALSA Driver MT6737 Android N 平台 ----ALSA Driver

MT6737 Android N 平台 ----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,
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

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,
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

接下来分析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;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

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));
  • 1
  • 2
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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

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,
    },
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • 464

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),
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

下面进行分析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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

上面代码为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),
     */

};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
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"},
};
  • 1
  • 2
  • 3
  • 4
  • 5

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),
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
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 Setting
static 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),
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
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), */
};
  • 1
  • 2
  • 3
  • 4
  • 5
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),
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
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));
#endif

static 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),
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
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*/
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

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,
              },
     },
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211

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;

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

下面开始对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),
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

5.1.1、.dai_link = mt_soc_dai_common

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,
     },
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

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总线上
  • 1
  • 2

下面到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,
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
/* 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
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

下面进入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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131

下面来进行分析snd_soc_register_card.

5.2.1、snd_soc_init_multicodec(card, link)

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),
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
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,
     },
     ....
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
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;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

上面通过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,
     },
     .........
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

这是一个链表,此函数将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;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这里就是要将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);//用来存储驱动中要用到的私有数据card

snd_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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

为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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249

下面进行分析此函数

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98

到这里为止,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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215

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_supply
case snd_soc_dapm_clock_supply
case 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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

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_components
static 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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116

这里写图片描述

前面已经创建了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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91

这里写图片描述 
这里写图片描述 
从这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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
下面看下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,则匹配成功,连接这两个widget
static 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);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

由此可见,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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

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;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234

到这里为止,我们为声卡创建并初始化好了所需的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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u014310046/article/details/53671853

猜你喜欢

转载自blog.csdn.net/zhangbijun1230/article/details/80780691