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

xiaoxiao2021-02-28  9

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

2.1.1、mtk_capture_pcm_open

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

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; } 12345678910111213141516 static int mtk_capture_alsa_start(struct snd_pcm_substream *substream) { pr_warn("mtk_capture_alsa_start\n"); SetMemifSubStream(Soc_Aud_Digital_Block_MEM_VUL, substream); StartAudioCaptureHardware(substream); #ifdef DENALI_FPGA_EARLYPORTING /* ccc early porting, copy from TurnOnDacPower() and ADC_LOOP_DAC_Func() */ /* Afe_Set_Reg(AFE_SGEN_CON0, 0x24862862, 0xffffffff); */ /* Ana_Set_Reg(PMIC_AFE_TOP_CON0, 0x0002, 0x0002); //UL from sinetable */ /* Ana_Set_Reg(PMIC_AFE_TOP_CON0, 0x0001, 0x0001); //DL from sinetable */ /* Ana_Set_Reg(AFE_SGEN_CFG0 , 0x0080 , 0xffff); */ /* Ana_Set_Reg(AFE_SGEN_CFG1 , 0x0101 , 0xffff); */ Ana_Get_Reg(AFE_AUDIO_TOP_CON0); /* power on clock */ Ana_Get_Reg(AFUNC_AUD_CON2); Ana_Get_Reg(AFUNC_AUD_CON0); /* sdm audio fifo clock power on */ Ana_Get_Reg(AFUNC_AUD_CON2); /* sdm power on */ Ana_Get_Reg(AFUNC_AUD_CON2); /* sdm fifo enable */ Ana_Get_Reg(AFE_DL_SDM_CON1); /* set attenuation gain */ Ana_Get_Reg(AFE_UL_DL_CON0); /* [0] afe enable */ Ana_Get_Reg(AFE_PMIC_NEWIF_CFG0); /* 8k sample rate */ Ana_Get_Reg(AFE_DL_SRC2_CON0_H);/* 8k sample rate */ Ana_Get_Reg(AFE_DL_SRC2_CON0_L); /* turn off mute function and turn on dl */ Ana_Get_Reg(PMIC_AFE_TOP_CON0); /* set DL in normal path, not from sine gen table */ Ana_Get_Reg(AFE_SGEN_CFG0); /* set DL in normal path, not from sine gen table */ Ana_Get_Reg(AFE_SGEN_CFG1); /* set DL in normal path, not from sine gen table */ Ana_Get_Reg(TOP_CLKSQ); /* Enable CLKSQ 26MHz */ Ana_Get_Reg(TOP_CLKSQ_SET); /* Turn on 26MHz source clock */ Ana_Get_Reg(AFE_AUDIO_TOP_CON0); /* power on clock */ Ana_Get_Reg(FPGA_CFG1); /* must set in FPGA platform for PMIC digital loopback */ #endif return 0; } 1234567891011121314151617181920212223242526272829303132333435363738

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

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

接下来分析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; } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859

2.2、mtk_pcm_fm_i2s_prepare

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

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

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

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)); 12 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; } 12345678910111213 static int snd_soc_register_dais(struct snd_soc_component *component, struct snd_soc_dai_driver *dai_drv, size_t count, bool legacy_dai_naming) { ... for (i = 0; i < count; i++) { if (count == 1 && legacy_dai_naming) { dai->name = fmt_single_name(dev, &dai->id); } else { dai->name = fmt_multiple_name(dev, &dai_drv[i]); if (dai_drv[i].id) dai->id = dai_drv[i].id; else dai->id = i; } } dai->component = component; dai->dev = dev; dai->driver = &dai_drv[i]; //dai->driver = &dai_drv[i] = mtk_dai_stub_dai list_add(&dai->list, &component->dai_list);//list_add(&cmpnt->list, &component_list) } ... return 0; } 12345678910111213141516171819202122232425

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

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), }; 12345678910111213

下面进行分析soc_mtk_codec结构体

4.1.1、mt6331_codec_probe

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

上面代码为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), */ }; 1234567891011121314 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"}, }; 12345

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), }; 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
4.1.1.2.2、mt6331_UL_Codec_controls

 

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

 

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

 

4.1.1.2.6、Audio_snd_auxadc_controls
snd_soc_add_codec_controls(codec, Audio_snd_auxadc_controls, ARRAY_SIZE(Audio_snd_auxadc_controls)); static const struct snd_kcontrol_new Audio_snd_auxadc_controls[] = { SOC_SINGLE_EXT("Audio AUXADC Data", SND_SOC_NOPM, 0, 0x80000, 0, Audio_AuxAdcData_Get, Audio_AuxAdcData_Set),//audio adc数据控件 }; static int Audio_AuxAdcData_Get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { #ifdef CONFIG_MTK_SPEAKER ucontrol->value.integer.value[0] = Audio_AuxAdcData_Get_ext(); //PMIC_IMM_GetOneChannelValue(AUX_ICLASSAB_AP, 1, 0); #else ucontrol->value.integer.value[0] = 0; #endif pr_warn("%s dMax = 0x%lx\n", __func__, ucontrol->value.integer.value[0]); return 0; } static int Audio_AuxAdcData_Set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { dAuxAdcChannel = ucontrol->value.integer.value[0]; pr_warn("%s dAuxAdcChannel = 0x%x\n", __func__, dAuxAdcChannel); return 0; } 1234567891011121314151617181920212223242526272829
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*/ } 1234567891011121314151617
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; } 12345678910111213141516171819202122

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

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

下面开始对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), }; 1234567

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

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

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总线上 12

下面到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, }; 12345678910 /* 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 } 123456789101112131415161718192021

下面进入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; } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131

下面来进行分析snd_soc_register_card.

static struct snd_soc_card snd_soc_card_mt = { .name = "mt-snd-card", .dai_link = mt_soc_dai_common, .num_links = ARRAY_SIZE(mt_soc_dai_common), .controls = mt_soc_controls, .num_controls = ARRAY_SIZE(mt_soc_controls), }; 1234567 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, }, .... } 1234567891011121314 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; } 12345678

上面通过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, }, ......... } 1234567891011121314 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; } 1234567891011121314151617181920212223242526

这是一个链表,此函数将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; } 12345678

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

5.2.2、snd_soc_initialize_card_lists(card)

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

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); 123456789101112131415161718192021222324252627

为snd_soc_pcm_runtime结构体数组申请内存,每一个dai_link对应snd_soc_pcm_runtime数组的一个单元,然后把snd_soc_card中的dai_link配置复制到相应的snd_soc_pcm_runtime中.  struct snd_soc_card {  …  struct snd_soc_pcm_runtime *rtd;  //指向struct snd_soc_pcm_runtime实体,共有  ARRAY_SIZE(mt_soc_dai_common)=23个.  …  }

5.2.4、 snd_soc_instantiate_card(card)

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

下面进行分析此函数

5.2.4.1、soc_bind_dai_link(card, i)

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

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

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

5.2.4.2、snd_card_new创建声卡

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

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

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

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

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

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

2)下面看下soc_probe_link_dais

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

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

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

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; } 1234567891011121314151617181920212223242526272829303132333435363738394041424344 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(®ister_mutex); err = snd_pcm_add(pcm); if (err) { mutex_unlock(®ister_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(®ister_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(®ister_mutex); return 0; } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091

    从这HAL层的PCM的一些api就可以调用操作kernel的声卡设备。

5.2.4.5、snd_soc_dapm_link_dai_widgets(card)

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

snd_soc_dapm_link_dai_widgets(card);//建立dai widget和stream widget之间的连接关系 snd_soc_dapm_connect_dai_link_widgets(card); 我们再来分析一下snd_soc_dapm_link_dai_widgets函数,看看它是如何连接这两种widget的,它先是遍历声卡中所有的widget,找出类型为snd_soc_dapm_dai_in和snd_soc_dapm_dai_out的widget,通过widget的priv字段,取出widget对应的snd_soc_dai结构指针 int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) { struct snd_soc_dapm_widget *dai_w, *w; struct snd_soc_dapm_widget *src, *sink; struct snd_soc_dai *dai; //我们再来分析一下snd_soc_dapm_link_dai_widgets函数,看看它是如何连接这两种widget的,它先是遍历声卡中所有的widget,找出类型为snd_soc_dapm_dai_in和snd_soc_dapm_dai_out的widget,通过widget的priv字段,取出widget对应的snd_soc_dai结构指针 /* For each DAI widget... */ list_for_each_entry(dai_w, &card->widgets, list) { switch (dai_w->id) { case snd_soc_dapm_dai_in: case snd_soc_dapm_dai_out: break; default: continue; } dai = dai_w->priv; //接着,再次从头遍历声卡中所有的widget,找出能与dai widget相连接的stream widget,第一个前提条件是这两个widget必须位于同一个dapm context中 /* ...find all widgets with the same stream and link them */ list_for_each_entry(w, &card->widgets, list) { if (w->dapm != dai_w->dapm) continue; //dai widget不会与dai widget相连,所以跳过它们 switch (w->id) { case snd_soc_dapm_dai_in: case snd_soc_dapm_dai_out: continue; default: break; } //dai widget的名字没有出现在要连接的widget的stream name中,跳过这个widget if (!w->sname || !strstr(w->sname, dai_w->name)) continue; if (dai_w->id == snd_soc_dapm_dai_in) { src = dai_w; sink = w; } else { src = w; sink = dai_w; } dev_dbg(dai->dev, "%s -> %s\n", src->name, sink->name); snd_soc_dapm_add_path(w->dapm, src, sink, NULL, NULL); } } return 0; } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152 下面看下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); } } } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162

由此可见,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; } 12345678910111213141516171819202122232425262728293031323334353637383940

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

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

5.2.4.8、snd_card_register(card->snd_card)

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

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

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

转载请注明原文地址: https://www.6miu.com/read-2050118.html

最新回复(0)