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_DRAMif (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;}
#elsepr_warn("mtk_capture_pcm_open use dram\n");mtk_capture_hardware.buffer_bytes_max = UL1_MAX_BUFFER_SIZE;
#endifAfeControlSramUnLock();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 */
#endifreturn 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);} elseSetMemoryPathEnable(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 */elseSetI2SASRCConfig(true, 44100);  /* Covert from 32000 Hz to 44100 Hz */SetI2SASRCEnable(true);Set2ndI2SInEnable(true);} elseSetMemoryPathEnable(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;elsedai->id = i;}}dai->component = component;dai->dev = dev;dai->driver = &dai_drv[i]; //dai->driver = &dai_drv[i] = mtk_dai_stub_dailist_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_SPEAKERsnd_soc_add_codec_controls(codec, mt6331_snd_Speaker_controls,ARRAY_SIZE(mt6331_snd_Speaker_controls));
#endifsnd_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 MICif (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];//选择前置放大器Muxreturn 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 */elsepr_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 muxreturn 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 */elsepr_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模式ACCpr_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);elseTurnOnVOWADcPowerACC(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_SPEAKERsnd_soc_add_codec_controls(codec, mt6331_snd_Speaker_controls,ARRAY_SIZE(mt6331_snd_Speaker_controls));
#endifstatic const struct snd_kcontrol_new mt6331_snd_Speaker_controls[] = {
/////////////////////////////////////////////////////////
/*
static int Audio_Speaker_Class_Set(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol)
{mutex_lock(&Ana_Ctrl_Mutex);Speaker_mode = ucontrol->value.integer.value[0];mutex_unlock(&Ana_Ctrl_Mutex);return 0;
}
static int Audio_Speaker_Class_Get(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol)
{ucontrol->value.integer.value[0] = Speaker_mode;//Speaker_mode = AUDIO_SPEAKER_MODE_AB,内置PA为AB类功放return 0;
}
*/
/////////////////////////////////////////////////////////SOC_ENUM_EXT("Audio_Speaker_class_Switch", Audio_Speaker_Enum[0], Audio_Speaker_Class_Get,Audio_Speaker_Class_Set),//设置PA为AB类功放SOC_ENUM_EXT("Audio_Speaker_PGA_gain", Audio_Speaker_Enum[1], Audio_Speaker_Pga_Gain_Get,Audio_Speaker_Pga_Gain_Set),//设置gain(增益调节)为1db,它是用来调节输入声信号的放大量/////////////////////////////////////////////////////////
/*
static int Audio_Speaker_OcFlag_Get(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol)
{mSpeaker_Ocflag = GetSpeakerOcFlag();ucontrol->value.integer.value[0] = mSpeaker_Ocflag;return 0;
}static int Audio_Speaker_OcFlag_Set(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol)
{pr_warn("%s is not support setting\n", __func__);return 0;
}
//根据下图配置
bool GetSpeakerOcFlag(void)
{unsigned int OCregister = 0;unsigned int bitmask = 1;bool DmodeFlag = false;bool ABmodeFlag = false;bool OCFlag = false;Ana_Set_Reg(TOP_CKPDN_CON2_CLR, 0x3, 0xffff);OCregister = Ana_Get_Reg(SPK_CON6);DmodeFlag = OCregister & (bitmask << 14);   /* ; no.14 bit is SPK_D_OC_L_DEG */ABmodeFlag = OCregister & (bitmask << 15);  /* ; no.15 bit is SPK_AB_OC_L_DEG */pr_warn("OCregister = %d\n", OCregister);OCFlag = (DmodeFlag | ABmodeFlag);return OCFlag;
}
*/
/////////////////////////////////////////////////////////SOC_ENUM_EXT("Audio_Speaker_OC_Falg", Audio_Speaker_Enum[2], Audio_Speaker_OcFlag_Get,Audio_Speaker_OcFlag_Set),//AB/D类功放选择标志控件//PA电流检测SOC_ENUM_EXT("Audio_Speaker_CurrentSensing", Audio_Speaker_Enum[3],Audio_Speaker_Current_Sensing_Get, Audio_Speaker_Current_Sensing_Set),SOC_ENUM_EXT("Audio_Speaker_CurrentPeakDetector", Audio_Speaker_Enum[4],Audio_Speaker_Current_Sensing_Peak_Detector_Get,Audio_Speaker_Current_Sensing_Peak_Detector_Set),
};
  • 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

 

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_SPEAKERucontrol->value.integer.value[0] = Audio_AuxAdcData_Get_ext();//PMIC_IMM_GetOneChannelValue(AUX_ICLASSAB_AP, 1, 0);
#elseucontrol->value.integer.value[0] = 0;
#endifpr_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);//用来存储驱动中要用到的私有数据cardsnd_soc_initialize_card_lists(card);static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card)
{INIT_LIST_HEAD(&card->codec_dev_list);INIT_LIST_HEAD(&card->widgets);INIT_LIST_HEAD(&card->paths);INIT_LIST_HEAD(&card->dapm_list);
}
  • 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_FSsnd_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);
#endifif (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;}}
#endifcard->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_liststruct snd_soc_platform *platform;// rtd->platform = platform; from platform_liststruct snd_soc_dai *codec_dai;// rtd->codec_dai = codec_dai; from dai_liststruct 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 DAIslist_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_PMmutex_init(&card->power_lock);init_waitqueue_head(&card->power_sleep);
#endifdevice_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);//添加新的controlif (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);//添加新的widgetsif (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 controlsif (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;}}
#endifret = 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);elsesnprintf(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_openrtd->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中,跳过这个widgetif (!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);
#endifreturn 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

MTK 驱动(61)---MT6737 Android N 平台 ----ALSA Driver相关推荐

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

    1.ALSA简述   ALSA是Advanced Linux Sound Architecture 的缩写,目前已经成为了linux的主流音频体系结构,在内核设备驱动层,ALSA提供了alsa-dri ...

  2. MTK驱动(75)--- MT6739平台的thermal高温充电机制调整

    MTK MT6739平台的thermal高温充电机制调整 MTK平台的thermal机制是一种高温保护主板机制.主要用于手机在充电过程中,温度太高对手机主板造成损坏. thermal主要由therma ...

  3. MT6737 Android N 平台 Audio系统学习----录音到播放录音流程分析

    本文将从主mic录音到播放流程来进行学习mtk audio系统架构.  在AudioFlinger::RecordThread::threadLoop中会调用mInput->stream-> ...

  4. MTK 驱动(4)---MTK Android Driver知识大全

    MTK Android Driver知识大全 一.Display 1.lcm 相关概念 1.1) MIPI接口:一共有三种接口:DBI(也做CPU或MCU接口).DPI(也叫RGB接口).DSI. 在 ...

  5. MTK Android 13平台开关机动画铃声客制化

    MTK Android 13平台开关机动画铃声客制化 Android T和S的差异很大 主要是MtkShutdownThread.java和ShutdownThread.java差异 未完,待更新,填 ...

  6. 高通Android智能平台环境搭建_编译流程分析

    高通Android智能平台环境搭建_编译流程分析 高通平台环境搭建,编译,系统引导流程分析 TOC \o \h \z \u 1. 高通平台android开发总结. 7 1.1 搭建高通平台环境开发环境 ...

  7. 高通Android智能平台开发总结

    高通Android智能平台开发总结 1. 高通平台android开发总结. 7 1.1 搭建高通平台环境开发环境. 7 1.2 搭建高通平台环境开发环境. 7 1.2.1 高通android智能平台概 ...

  8. MTK 驱动(64)---Mtk touch panel驱动/TP驱动详解

    Mtk touch panel驱动/TP驱动详解 TP还算是比LCM好理解的多. 在启动过程中,先注册/mediatek/custom/command/kernel/touchpanel目录下的具体驱 ...

  9. MTK 驱动(63)---MTK TP驱动移植

    MTK TP驱动移植 对于MTK TP驱动移植一般分为六部分: 1.硬件IO口配置: 2.TP驱动移植: 3.I2C通信: 4.中断触发: 5.数据上报: 6.虚拟按键: 硬件电路: 1.GPIO配置 ...

  10. MTK 驱动(51)---TP 驱动移植

    对于MTK TP驱动移植一般分为六部分: 1.硬件IO口配置: 2.TP驱动移植: 3.I2C通信: 4.中断触发: 5.数据上报: 6.虚拟按键: 硬件电路: 1.GPIO配置 打开 mediate ...

最新文章

  1. jquery选中以什么开头的元素
  2. ap测试系统软件,符合AUTOSAR(APCP)的嵌入式系统和软件设计工具
  3. python 中split函数的应用_举例详解Python中的split()函数的使用方法
  4. python全栈学习--day8
  5. csgo手机上看demo_仪表上的车速和手机导航不一样,哪个更准?碰到测速了该看哪个?...
  6. U-Boot工作过程
  7. JS计算GridView里的TextBox
  8. 搜索工具推荐 Windows中的everyting 和 mac下的alfred
  9. 基于Python的视频解析器
  10. 太强了 GitHub中文开源项目榜单出炉,暴露了程序员的硬性需求
  11. js中this指向学习总结
  12. Python 基础变量声明
  13. 计算机桌面性能4.4怎么办,台式CPU性能怎么看?桌面CPU天梯图2018年1月更新版 (全文)...
  14. Uncle-黄,恍恍惚惚的面经 -- 1(初级Java)
  15. echarts 大数据可视化
  16. 前端基础三剑客之——HTML超文本标记语言(上篇)
  17. 数据挖掘入门学习心得
  18. Cutting Game
  19. Linux系统学习了解计算机
  20. 华三模拟器完成交换机堆叠IRF实验

热门文章

  1. 分享一款开源堡垒机-jumpserver
  2. 地理信息系统GIS--介绍
  3. 初来乍到,请多多指教
  4. 《黑客大曝光:移动应用安全揭秘及防护措施》一1.2 移动风险模型
  5. 离散数学 集合的运算
  6. 7款英文语法检查工具推荐
  7. 货币转换python代码_在Python中创建货币转换器
  8. 如何安装arm交叉工具链及问题解决
  9. react实现问卷调查
  10. 智能家居系统解决方案