MT6737 Android N Platform--ALSA Driver
1. A Brief Introduction to ALSA
ALSA is the abbreviation of Advanced Linux Sound Architecture. It has become the mainstream Audio Architecture of linux. At the core device driver layer, ALSA provides alsa-driver. At the application layer, ALSA provides alsa-lib for us. As long as the application program calls the API provided by alsa-lib, it can complete the control of the underlying audio hardware.
Kernel-3.10/sound/core This directory contains the middle layer of ALSA driver, which is the core part of the ALSA driver.
Kernel-3.10/sound/soc is for the middle-level code of system-on-chip system, and soc/codecs is for all kinds of codec code of SOC system, which is independent of platform.
Main functions of sound card
Three main functions:
(1) playback
(2) capture
(3) Sound control
Running ADB shell ls-l/dev/snd, we can see the sound card driver registered on the current platform.
It can be divided into the following categories:
pcmC0D0p —— Playback
pcmC0D0c —— Capture
Control C0 - Control, such as various audio control switches, volume gain, etc.
ASoC also divides the audio system into three parts: Machine, Platform and Code.
Platform generally refers to a SoC platform, such as MT6582, MT6595, MT6752 and so on. Audio-related SoC usually includes Clock, AFE, I2S, DMA and so on.
Codec literally means codec. Codec contains I2S interface, DAC, ADC, Mixer, PA (power amplifier). It usually contains multiple inputs (Mic, Line-in, I2S, PCM) and multiple outputs (earphone, speaker, receiver, Line-out). Codec, like Platform, is a reusable component.
Machine binds platform driver and codec driver.
Next we will analyze these three modules.
2,Platform
As can be seen from the above figure, ASOC contains several platforms, each of which corresponds to A. c code. The following analysis is made of these platforms.
2.1. capture
kernel-3.18/sound/soc/mediatek/mt_soc_audio_v3/mt_soc_pcm_capture.c
snd_soc_register_platform() This function is used to register a snd_soc_platform, which can only be used by Machine after registration. Its code has clearly expressed its implementation process:
Request memory for snd_soc_platform instance;
Get its name from platform_device for Machine-driven matching;
Initialize the fields of snd_soc_platform;
Connect the snd_soc_platform instance to the global linked list platform_list;
Call snd_soc_instantiate_cards to trigger the matching of machine, platform, codec, dai and so on.
From the above figure, we can see that capture is registered in the structure of mtk_soc_platform. Next, we analyze mtk_afe_capture_ops.
static struct snd_pcm_ops mtk_afe_capture_ops = {
.open = mtk_capture_pcm_open,
.close = mtk_capture_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = mtk_capture_pcm_hw_params,
.hw_free = mtk_capture_pcm_hw_free,
.prepare = mtk_capture_pcm_prepare,
.trigger = mtk_capture_pcm_trigger,
.pointer = mtk_capture_pcm_pointer,
.copy = mtk_capture_pcm_copy,
.silence = mtk_capture_pcm_silence,
.page = mtk_capture_pcm_page,
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
2.1.1,mtk_capture_pcm_open
static int mtk_capture_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
int ret = 0;
AudDrv_Clk_On();
AudDrv_ADC_Clk_On();//Enabling analog clock
VUL_Control_context = Get_Mem_ControlT(Soc_Aud_Digital_Block_MEM_VUL);
/* can allocate sram_dbg */
AfeControlSramLock();
#ifndef CAPTURE_FORCE_USE_DRAM
if (GetSramState() == SRAM_STATE_FREE) {
pr_warn("mtk_capture_pcm_open use sram\n");
mtk_capture_hardware.buffer_bytes_max = GetCaptureSramSize();
SetSramState(SRAM_STATE_CAPTURE);
mCaptureUseSram = true;
} else {
pr_warn("mtk_capture_pcm_open use dram\n");
mtk_capture_hardware.buffer_bytes_max = UL1_MAX_BUFFER_SIZE;
mCaptureUseSram = false;
}
#else
pr_warn("mtk_capture_pcm_open use dram\n");
mtk_capture_hardware.buffer_bytes_max = UL1_MAX_BUFFER_SIZE;
#endif
AfeControlSramUnLock();
runtime->hw = mtk_capture_hardware;
memcpy((void *)(&(runtime->hw)), (void *)&mtk_capture_hardware , sizeof(struct snd_pcm_hardware));
ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&constraints_sample_rates);
ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0)
pr_warn("snd_pcm_hw_constraint_integer failed\n");
if (ret < 0) {
pr_err("mtk_capture_pcm_close\n");
mtk_capture_pcm_close(substream);
return ret;
}
if (mCaptureUseSram == false)
AudDrv_Emi_Clk_On();
pr_warn("mtk_capture_pcm_open return\n");
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
2.1.2,mtk_capture_pcm_trigger
static int mtk_capture_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
pr_warn("mtk_capture_pcm_trigger cmd = %d\n", cmd);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
//awaken
return mtk_capture_alsa_start(substream);
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
//sleep
return mtk_capture_alsa_stop(substream);
}
return -EINVAL;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
static int mtk_capture_alsa_start(struct snd_pcm_substream *substream)
{
pr_warn("mtk_capture_alsa_start\n");
SetMemifSubStream(Soc_Aud_Digital_Block_MEM_VUL, substream);
StartAudioCaptureHardware(substream);
#ifdef DENALI_FPGA_EARLYPORTING /* ccc early porting, copy from TurnOnDacPower() and ADC_LOOP_DAC_Func() */
/* Afe_Set_Reg(AFE_SGEN_CON0, 0x24862862, 0xffffffff); */
/* Ana_Set_Reg(PMIC_AFE_TOP_CON0, 0x0002, 0x0002); //UL from sinetable */
/* Ana_Set_Reg(PMIC_AFE_TOP_CON0, 0x0001, 0x0001); //DL from sinetable */
/* Ana_Set_Reg(AFE_SGEN_CFG0 , 0x0080 , 0xffff); */
/* Ana_Set_Reg(AFE_SGEN_CFG1 , 0x0101 , 0xffff); */
Ana_Get_Reg(AFE_AUDIO_TOP_CON0); /* power on clock */
Ana_Get_Reg(AFUNC_AUD_CON2);
Ana_Get_Reg(AFUNC_AUD_CON0); /* sdm audio fifo clock power on */
Ana_Get_Reg(AFUNC_AUD_CON2); /* sdm power on */
Ana_Get_Reg(AFUNC_AUD_CON2); /* sdm fifo enable */
Ana_Get_Reg(AFE_DL_SDM_CON1); /* set attenuation gain */
Ana_Get_Reg(AFE_UL_DL_CON0); /* [0] afe enable */
Ana_Get_Reg(AFE_PMIC_NEWIF_CFG0); /* 8k sample rate */
Ana_Get_Reg(AFE_DL_SRC2_CON0_H);/* 8k sample rate */
Ana_Get_Reg(AFE_DL_SRC2_CON0_L); /* turn off mute function and turn on dl */
Ana_Get_Reg(PMIC_AFE_TOP_CON0); /* set DL in normal path, not from sine gen table */
Ana_Get_Reg(AFE_SGEN_CFG0); /* set DL in normal path, not from sine gen table */
Ana_Get_Reg(AFE_SGEN_CFG1); /* set DL in normal path, not from sine gen table */
Ana_Get_Reg(TOP_CLKSQ); /* Enable CLKSQ 26MHz */
Ana_Get_Reg(TOP_CLKSQ_SET); /* Turn on 26MHz source clock */
Ana_Get_Reg(AFE_AUDIO_TOP_CON0); /* power on clock */
Ana_Get_Reg(FPGA_CFG1); /* must set in FPGA platform for PMIC digital loopback */
#endif
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
2.1.3,snd_pcm_lib_ioctl
General PCM ioctl callback function
/**
* snd_pcm_lib_ioctl - a generic PCM ioctl callback
* @substream: the pcm substream instance
* @cmd: ioctl command
* @arg: ioctl argument
*
* Processes the generic ioctl commands for PCM.
* Can be passed as the ioctl callback for PCM ops.
*
* Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
{
switch (cmd) {
case SNDRV_PCM_IOCTL1_INFO:
return 0;
case SNDRV_PCM_IOCTL1_RESET:
return snd_pcm_lib_ioctl_reset(substream, arg);
case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
return snd_pcm_lib_ioctl_channel_info(substream, arg);
case SNDRV_PCM_IOCTL1_FIFO_SIZE:
return snd_pcm_lib_ioctl_fifo_size(substream, arg);
}
return -ENXIO;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
2.2,FM
kernel-3.18/sound/soc/mediatek/mt_soc_audio_v3/mt_soc_pcm_fm_i2s.c
static struct snd_pcm_ops mtk_fm_i2s_ops = {
.open = mtk_pcm_fm_i2s_open,
.close = mtk_pcm_fm_i2s_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = mtk_pcm_fm_i2s_hw_params,
.hw_free = mtk_pcm_fm_i2s_hw_free,
.prepare = mtk_pcm_fm_i2s_prepare,
.trigger = mtk_pcm_fm_i2s_trigger,
.pointer = mtk_pcm_fm_i2s_pointer,
.copy = mtk_pcm_fm_i2s_copy,
.silence = mtk_pcm_fm_i2s_silence,
.page = mtk_fm_i2s_pcm_page,
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
Next, analyze 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();//Turn on the I2S clock
/*
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
};
Supported sampling rate
*/
ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&fm_i2s_constraints_sample_rates);
ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0)
pr_warn("snd_pcm_hw_constraint_integer failed\n");
if (ret < 0) {
pr_err("mtk_pcm_fm_i2s_close\n");
mtk_pcm_fm_i2s_close(substream);
return ret;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
2.2,mtk_pcm_fm_i2s_prepare
static int mtk_pcm_fm_i2s_prepare(struct snd_pcm_substream *substream)
{
AudioDigtalI2S m2ndI2SInAttribute;
struct snd_pcm_runtime *runtime = substream->runtime;
pr_warn("%s rate = %d\n", __func__, runtime->rate);
if (mPrepareDone == false) {
/* mtk_wcn_cmb_stub_audio_ctrl((CMB_STUB_AIF_X)CMB_STUB_AIF_3);//temp mark for early porting */
/*
External smart pa, FM cut to exterior playback silent
1.The default behavior of the public version, headset and speaker are output by PMIC, that is, O03,O04 corresponding to interconnection.
2.When the Smart PA is plugged in, the I2S of Smart PA connection is I2S0,I2S3, and the interconnection of output is O00,O01.
3.FM After switching from headset to speaker, headset still goes to PMIC(O03,O04), while speaker goes to Smart PA, so the interconnection in FM driver file needs to add O00,O01 settings.
4.If there are other application scenarios, there will be no voice after switching smart PA, please first confirm whether the interconnection is set correctly. The specific modification is to add O00,O01 interconnection to the prepare function in the corresponding driver file according to which driver file the application scenario is going.
After adding O00 and O01 interconnection to the start function of mt_soc_pcm_dl1_i2s0Dl1.c, the fm switch speaker can be used.
The default behavior of our public version is that FM cuts headset to speaker and closes it.
Only let speaker output, if you want to output together, find the place to close the headset and open it.
*/
/* interconnection setting */
SetConnection(Soc_Aud_InterCon_Connection, Soc_Aud_InterConnectionInput_I00,
Soc_Aud_InterConnectionOutput_O13);
SetConnection(Soc_Aud_InterCon_Connection, Soc_Aud_InterConnectionInput_I01,
Soc_Aud_InterConnectionOutput_O14);
SetConnection(Soc_Aud_InterCon_Connection, Soc_Aud_InterConnectionInput_I10,
Soc_Aud_InterConnectionOutput_O03);
SetConnection(Soc_Aud_InterCon_Connection, Soc_Aud_InterConnectionInput_I11,
Soc_Aud_InterConnectionOutput_O04);
/* Set HW_GAIN */
SetHwDigitalGainMode(Soc_Aud_Hw_Digital_Gain_HW_DIGITAL_GAIN1, runtime->rate,
0x40);
SetHwDigitalGainEnable(Soc_Aud_Hw_Digital_Gain_HW_DIGITAL_GAIN1, true);
SetHwDigitalGain(mfm_i2s_Volume, Soc_Aud_Hw_Digital_Gain_HW_DIGITAL_GAIN1);
/* start I2S DAC out */
if (GetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_DAC) == false) {
SetI2SDacOut(runtime->rate, false, Soc_Aud_I2S_WLEN_WLEN_16BITS);
SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_DAC, true);
SetI2SDacEnable(true);
} else
SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_DAC, true);
if (GetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_IN_2) == false) {
/* set merge interface */
SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_IN_2, true);
/* Config 2nd I2S IN */
memset_io((void *)&m2ndI2SInAttribute, 0, sizeof(m2ndI2SInAttribute));
m2ndI2SInAttribute.mLR_SWAP = Soc_Aud_LR_SWAP_NO_SWAP;
m2ndI2SInAttribute.mI2S_IN_PAD_SEL = false; /* I2S_IN_FROM_CONNSYS */
m2ndI2SInAttribute.mI2S_SLAVE = Soc_Aud_I2S_SRC_SLAVE_MODE;
m2ndI2SInAttribute.mI2S_SAMPLERATE = 32000;
m2ndI2SInAttribute.mINV_LRCK = Soc_Aud_INV_LRCK_NO_INVERSE;
m2ndI2SInAttribute.mI2S_FMT = Soc_Aud_I2S_FORMAT_I2S;
m2ndI2SInAttribute.mI2S_WLEN = Soc_Aud_I2S_WLEN_WLEN_16BITS;
Set2ndI2SIn(&m2ndI2SInAttribute);
if (runtime->rate == 48000)
SetI2SASRCConfig(true, 48000); /* Covert from 32000 Hz to 48000 Hz */
else
SetI2SASRCConfig(true, 44100); /* Covert from 32000 Hz to 44100 Hz */
SetI2SASRCEnable(true);
Set2ndI2SInEnable(true);
} else
SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_IN_2, true);
EnableAfe(true);
if (GetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_DAC) == true)
SetI2SADDAEnable(true);
mPrepareDone = true;
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
2.3,mtk_pcm_fm_i2s_close
static int mtk_pcm_fm_i2s_close(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
pr_warn("%s rate = %d\n", __func__, runtime->rate);
/* mtk_wcn_cmb_stub_audio_ctrl((CMB_STUB_AIF_X)CMB_STUB_AIF_0);//temp mark for early porting */
SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_IN_2, false);
if (GetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_IN_2) == false) {
SetI2SASRCEnable(false);
SetI2SASRCConfig(false, 0); /* Setting to bypass ASRC */
Set2ndI2SInEnable(false);
}
SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_DAC, false);
if (GetI2SDacEnable() == false) {
SetI2SADDAEnable(false);
SetI2SDacEnable(false);
}
/* interconnection setting */
SetConnection(Soc_Aud_InterCon_DisConnect, Soc_Aud_InterConnectionInput_I00,
Soc_Aud_InterConnectionOutput_O13);
SetConnection(Soc_Aud_InterCon_DisConnect, Soc_Aud_InterConnectionInput_I01,
Soc_Aud_InterConnectionOutput_O14);
SetConnection(Soc_Aud_InterCon_DisConnect, Soc_Aud_InterConnectionInput_I10,
Soc_Aud_InterConnectionOutput_O03);
SetConnection(Soc_Aud_InterCon_DisConnect, Soc_Aud_InterConnectionInput_I11,
Soc_Aud_InterConnectionOutput_O04);
EnableAfe(false);
AudDrv_I2S_Clk_Off();
AudDrv_Clk_Off();
mPrepareDone = false;
SetFMEnableFlag(false);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
2.3,mtk_pcm_fm_i2s_close
static int mtk_pcm_fm_i2s_close(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
pr_warn("%s rate = %d\n", __func__, runtime->rate);
/* mtk_wcn_cmb_stub_audio_ctrl((CMB_STUB_AIF_X)CMB_STUB_AIF_0);//temp mark for early porting */
SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_IN_2, false);
if (GetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_IN_2) == false) {
SetI2SASRCEnable(false);
SetI2SASRCConfig(false, 0); /* Setting to bypass ASRC */
Set2ndI2SInEnable(false);
}
SetMemoryPathEnable(Soc_Aud_Digital_Block_I2S_OUT_DAC, false);
if (GetI2SDacEnable() == false) {
SetI2SADDAEnable(false);
SetI2SDacEnable(false);
}
/* interconnection setting */
SetConnection(Soc_Aud_InterCon_DisConnect, Soc_Aud_InterConnectionInput_I00,
Soc_Aud_InterConnectionOutput_O13);
SetConnection(Soc_Aud_InterCon_DisConnect, Soc_Aud_InterConnectionInput_I01,
Soc_Aud_InterConnectionOutput_O14);
SetConnection(Soc_Aud_InterCon_DisConnect, Soc_Aud_InterConnectionInput_I10,
Soc_Aud_InterConnectionOutput_O03);
SetConnection(Soc_Aud_InterCon_DisConnect, Soc_Aud_InterConnectionInput_I11,
Soc_Aud_InterConnectionOutput_O04);
EnableAfe(false);
AudDrv_I2S_Clk_Off();
AudDrv_Clk_Off();
mPrepareDone = false;
SetFMEnableFlag(false);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
Asoc also has many platform s, which are consistent with the above registration methods, but different in ops, so we will not analyze them one by one.
3,Platform Dai
kernel-3.18/sound/soc/mediatek/mt_soc_audio_v3/mt_soc_dai_stub.c
snd_soc_register_component(&pdev->dev, &mt_dai_component,
mtk_dai_stub_dai, ARRAY_SIZE(mtk_dai_stub_dai));
- 1
- 2
int snd_soc_register_component(struct device *dev,
const struct snd_soc_component_driver *cmpnt_drv,
struct snd_soc_dai_driver *dai_drv,
int num_dai)
{
...
cmpnt->ignore_pmdown_time = true;
cmpnt->registered_as_component = true;
ret = snd_soc_register_dais(cmpnt, dai_drv, num_dai, true);
...
snd_soc_component_add(cmpnt);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
static int snd_soc_register_dais(struct snd_soc_component *component,
struct snd_soc_dai_driver *dai_drv, size_t count,
bool legacy_dai_naming)
{
...
for (i = 0; i < count; i++) {
if (count == 1 && legacy_dai_naming) {
dai->name = fmt_single_name(dev, &dai->id);
} else {
dai->name = fmt_multiple_name(dev, &dai_drv[i]);
if (dai_drv[i].id)
dai->id = dai_drv[i].id;
else
dai->id = i;
}
}
dai->component = component;
dai->dev = dev;
dai->driver = &dai_drv[i]; //dai->driver = &dai_drv[i] = mtk_dai_stub_dai
list_add(&dai->list, &component->dai_list);//list_add(&cmpnt->list, &component_list)
}
...
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
Platform Dai registers with snd_soc_register_component, passes in the array mtk_dai_stub_dai[], and then adds all PCM s (platforms) (such as playback, recording, calling, etc.) to the dai list through snd_soc_register_dais.
Let's look at the mtk_dai_stub_dai array.
static struct snd_soc_dai_driver mtk_dai_stub_dai[] = {
{
.playback = {
.stream_name = MT_SOC_DL1_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 192000,
},
.name = MT_SOC_DL1DAI_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.capture = {
.stream_name = MT_SOC_UL1_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 192000,
},
.name = MT_SOC_UL1DAI_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.capture = {
.stream_name = MT_SOC_TDM_CAPTURE_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
.name = MT_SOC_TDMRX_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.playback = {
.stream_name = MT_SOC_HDMI_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 192000,
},
.capture = {
.stream_name = MT_SOC_HDMI_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 192000,
},
.name = MT_SOC_HDMI_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.playback = {
.stream_name = MT_SOC_VOICE_MD1_BT_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 32000,
},
.name = MT_SOC_VOICE_MD1_BT_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.playback = {
.stream_name = MT_SOC_VOICE_MD2_BT_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 32000,
},
.name = MT_SOC_VOICE_MD2_BT_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.playback = {
.stream_name = MT_SOC_VOIP_BT_OUT_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.name = MT_SOC_VOIP_CALL_BT_OUT_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.capture = {
.stream_name = MT_SOC_VOIP_BT_IN_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.name = MT_SOC_VOIP_CALL_BT_IN_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.playback = {
.stream_name = MT_SOC_FM_I2S2_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000 ,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.name = "FM_I2S2_OUT",
.ops = &mtk_dai_stub_ops,
},
{
.capture = {
.stream_name = MT_SOC_FM_I2S2_RECORD_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.name = "FM_I2S2_IN",
.ops = &mtk_dai_stub_ops,
},
{
.playback = {
.stream_name = MT_SOC_VOICE_MD1_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_STD_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 32000,
},
.capture = {
.stream_name = MT_SOC_VOICE_MD1_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_STD_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 32000,
},
.name = MT_SOC_VOICE_MD1_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.playback = {
.stream_name = MT_SOC_VOICE_MD2_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_STD_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 32000,
},
.capture = {
.stream_name = MT_SOC_VOICE_MD2_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_STD_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 32000,
},
.name = MT_SOC_VOICE_MD2_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.playback = {
.stream_name = MT_SOC_ULDLLOOPBACK_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.capture = {
.stream_name = MT_SOC_ULDLLOOPBACK_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.name = MT_SOC_ULDLLOOPBACK_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.playback = {
.stream_name = MT_SOC_I2S0_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 192000,
},
.capture = {
.stream_name = MT_SOC_I2S0_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 192000,
},
.name = MT_SOC_I2S0_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.playback = {
.stream_name = MT_SOC_I2SDL1_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 192000,
},
.name = MT_SOC_I2S0DL1_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.capture = {
.stream_name = MT_SOC_DL1_AWB_RECORD_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.name = MT_SOC_DL1AWB_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.playback = {
.stream_name = MT_SOC_MRGRX_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.capture = {
.stream_name = MT_SOC_MRGRX_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.name = MT_SOC_MRGRX_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.playback = {
.stream_name = MT_SOC_MRGRX_CAPTURE_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.capture = {
.stream_name = MT_SOC_MRGRX_CAPTURE_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.name = MT_SOC_MRGRXCAPTURE_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.playback = {
.stream_name = MT_SOC_FM_MRGTX_STREAM_NAME,
.rates = SNDRV_PCM_RATE_44100,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 44100,
.rate_max = 44100,
},
.name = MT_SOC_FM_MRGTX_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.capture = {
.stream_name = MT_SOC_UL1DATA2_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.name = MT_SOC_UL2DAI_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.capture = {
.stream_name = MT_SOC_I2S0AWB_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.name = MT_SOC_I2S0AWBDAI_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.capture = {
.stream_name = MT_SOC_MODADCI2S_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.name = MT_SOC_MODADCI2SDAI_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.capture = {
.stream_name = MT_SOC_ADC2AWB_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 192000,
},
.name = MT_SOC_ADC2AWBDAI_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.capture = {
.stream_name = MT_SOC_IO2DAI_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.name = MT_SOC_IO2DAIDAI_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.playback = {
.stream_name = MT_SOC_HP_IMPEDANCE_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 8,
.rate_min = 8000,
.rate_max = 48000,
},
.name = MT_SOC_HP_IMPEDANCE_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.playback = {
.stream_name = MT_SOC_FM_I2S_PLAYBACK_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.capture = {
.stream_name = MT_SOC_FM_I2S_PLAYBACK_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.name = MT_SOC_FM_I2S_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.playback = {
.stream_name = MT_SOC_FM_I2S_CAPTURE_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.capture = {
.stream_name = MT_SOC_FM_I2S_CAPTURE_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.name = MT_SOC_FM_I2S_CAPTURE_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.playback = {
.stream_name = MT_SOC_OFFLOAD_GDMA_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 48000,
},
.compress_dai = 1,
.name = MT_SOC_OFFLOAD_GDMA_NAME,
.ops = &mtk_dai_stub_ops,
},
{
.playback = {
.stream_name = MT_SOC_DL2_STREAM_NAME,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SND_SOC_ADV_MT_FMTS,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 192000,
},
.name = MT_SOC_DL2DAI_NAME,
.ops = &mtk_dai_stub_ops,
},
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334
- 335
- 336
- 337
- 338
- 339
- 340
- 341
- 342
- 343
- 344
- 345
- 346
- 347
- 348
- 349
- 350
- 351
- 352
- 353
- 354
- 355
- 356
- 357
- 358
- 359
- 360
- 361
- 362
- 363
- 364
- 365
- 366
- 367
- 368
- 369
- 370
- 371
- 372
- 373
- 374
- 375
- 376
- 377
- 378
- 379
- 380
- 381
- 382
- 383
- 384
- 385
- 386
- 387
- 388
- 389
- 390
- 391
- 392
- 393
- 394
- 395
- 396
- 397
- 398
- 399
- 400
- 401
- 402
- 403
- 404
- 405
- 406
- 407
- 408
- 409
- 410
- 411
- 412
- 413
- 414
- 415
- 416
- 417
- 418
- 419
- 420
- 421
- 422
- 423
- 424
- 425
- 426
- 427
- 428
- 429
- 430
- 431
- 432
- 433
- 434
- 435
- 436
- 437
- 438
- 439
- 440
- 441
- 442
- 443
- 444
- 445
- 446
- 447
- 448
- 449
- 450
- 451
- 452
- 453
- 454
- 455
- 456
- 457
- 458
- 459
- 460
- 461
- 462
- 463
- 464
4. Codec and Codc Dai
kernel-3.18/sound/soc/mediatek/mt_soc_audio_v3/mt_soc_codec_63xx.c
Codec and Codcdai are all registered in one file. The mt_soc_c odec_63xx.c file is analyzed below.
From the above figure, we can see that snd_soc_register_codec has registered codec and codec dai respectively. The following two aspects are analyzed.
4.1,Codec(soc_mtk_codec)
static struct snd_soc_codec_driver soc_mtk_codec = {
.probe = mt6331_codec_probe,
.remove = mt6331_codec_remove,
.read = mt6331_read,
.write = mt6331_write,
/* use add control to replace */
/* .controls = mt6331_snd_controls, */
/* .num_controls = ARRAY_SIZE(mt6331_snd_controls), */
.dapm_widgets = mt6331_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(mt6331_dapm_widgets),
.dapm_routes = mtk_audio_map,
.num_dapm_routes = ARRAY_SIZE(mtk_audio_map),
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
The soc_mtk_codec structure is analyzed below.
4.1.1,mt6331_codec_probe
static int mt6331_codec_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
pr_warn("%s()\n", __func__);
if (mInitCodec == true)
return 0;
pin_extspkamp = pin_extspkamp_2 = pin_vowclk = pin_audmiso = pin_rcvspkswitch = 0;
pin_mode_extspkamp = pin_mode_extspkamp_2 = pin_mode_vowclk = pin_mode_audmiso =
pin_mode_rcvspkswitch = 0;
snd_soc_dapm_new_controls(dapm, mt6331_dapm_widgets, ARRAY_SIZE(mt6331_dapm_widgets));
snd_soc_dapm_add_routes(dapm, mtk_audio_map, ARRAY_SIZE(mtk_audio_map));
/* add codec controls */
snd_soc_add_codec_controls(codec, mt6331_snd_controls, ARRAY_SIZE(mt6331_snd_controls));
snd_soc_add_codec_controls(codec, mt6331_UL_Codec_controls,
ARRAY_SIZE(mt6331_UL_Codec_controls));
snd_soc_add_codec_controls(codec, mt6331_Voice_Switch, ARRAY_SIZE(mt6331_Voice_Switch));
snd_soc_add_codec_controls(codec, mt6331_pmic_Test_controls,
ARRAY_SIZE(mt6331_pmic_Test_controls));
#ifdef CONFIG_MTK_SPEAKER
snd_soc_add_codec_controls(codec, mt6331_snd_Speaker_controls,
ARRAY_SIZE(mt6331_snd_Speaker_controls));
#endif
snd_soc_add_codec_controls(codec, Audio_snd_auxadc_controls,
ARRAY_SIZE(Audio_snd_auxadc_controls));
/* here to set private data */
mCodec_data = kzalloc(sizeof(mt6331_Codec_Data_Priv), GFP_KERNEL);
if (!mCodec_data) {
pr_warn("Failed to allocate private data\n");
return -ENOMEM;
}
snd_soc_codec_set_drvdata(codec, mCodec_data);
memset((void *)mCodec_data, 0, sizeof(mt6331_Codec_Data_Priv));
mt6331_codec_init_reg(codec);
InitCodecDefault();
mInitCodec = true;
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
The above code is the probe function, which contains a lot of important knowledge, the following analysis.
4.1.1.1, snd_soc_dapm_new_controls and snd_soc_dapm_add_routes
DAMP (Dynamic Audio Power Management) is dynamic audio power management, which is designed to enable mobile devices to work at the minimum power consumption at any time. DAPM is transparent to user-space applications, and all power-related switches are implemented in ASoc core. User-space applications do not need to modify the code or recompile. DAPM determines whether the power switches of those audio controls are turned on or off based on the configuration of the currently activated audio stream (playback/capture) and mixer in the sound card.
snd_soc_dapm_new_controls: snd_soc_dapm_new_controls will call snd_soc_dapm_new_controls function. snd_soc_dapm_new_controls is only the first step to create widgets. It allocates memory for each widget, initializes the necessary fields, and then hangs these widgets in the widgets list field representing snd_soc_card.
Snd_soc_dapm_route: The various widgets registered in the system need to be interconnected to coordinate their work. The connection relationship is defined by the snd_soc_dapm_route structure. Usually, all path information is defined with an array of snd_soc_dapm_route structures. Like widgets, path information exists in codec driver, machine driver and platform driver respectively. We have two ways to register audio path information:
(1) Through the dapm_routes and num_dapm_routes fields in the snd_soc_codec_driver/snd_soc_platform_driver/snd_soc_card structure;
(2) Actively register the audio path in the probe callback of codec and platform, while in machine driver, the init callback function of snd_soc_dai_link structure is used to register the audio path.
Finally, both methods complete the registration of audio paths by calling the snd_soc_dapm_add_routes function.
snd_soc_dapm_new_controls(dapm, mt6331_dapm_widgets, ARRAY_SIZE(mt6331_dapm_widgets))
//The mt6331_dapm_widgets array is defined as follows:
static const struct snd_soc_dapm_widget mt6331_dapm_widgets[] = {
/* Outputs */
SND_SOC_DAPM_OUTPUT("EARPIECE"),//Receiver
SND_SOC_DAPM_OUTPUT("HEADSET"),//Headset
SND_SOC_DAPM_OUTPUT("SPEAKER"),//horn
/*
SND_SOC_DAPM_MUX_E("VOICE_Mux_E", SND_SOC_NOPM, 0, 0 , &mt6331_Voice_Switch, codec_enable_rx_bias,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
SND_SOC_DAPM_PRE_REG | SND_SOC_DAPM_POST_REG),
*/
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
snd_soc_dapm_add_routes(dapm, mtk_audio_map, ARRAY_SIZE(mtk_audio_map))
//The mtk_audio_map array is defined as follows
static const struct snd_soc_dapm_route mtk_audio_map[] = {
{"VOICE_Mux_E", "Voice Mux", "SPEAKER PGA"},
};
- 1
- 2
- 3
- 4
- 5
4.1.1.2,snd_soc_add_codec_controls
snd_soc_add_codec_controls: This function first organizes the members from snd_ctl_new1 into the newly allocated members of snd_kcontrol structure through the snd_soc_cnew (creating a new control) function, then calls the snd_ctl_add function to add these audio controls to the control list of the struct snd_card (card - > controls), and assigns one control to the kcontrol. A unique id number.
For each control, we need to define a snd_kcontrol_new structure corresponding to it. These snd_kcontrol_new structures will be registered in the initial stage of the sound card through the snd_soc_add_codec_controls function. User space can view and set the status of these controls through tools such as amixer or alsamixer.
4.1.1.2.1,mt6331_snd_controls
snd_soc_add_codec_controls(codec, mt6331_snd_controls, ARRAY_SIZE(mt6331_snd_controls));
//The mt6331_snd_controls array is defined as follows
/////////////////////////////////////////////////////////
/*
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];//Selection of Power Supply Type for Equipment
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))//Judging the right type of Headphone Power Supply
{
Audio_Amp_Change(AUDIO_ANALOG_CHANNELS_RIGHT1, true);//The main functions of this function are: 1. Power on 2. Set up pmic analog control register 3. Set up the volume of headphones.
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),//Headphone Right Channel Control Switch
//Left channel settings are similar to the right channel settings above. Headphone power supply types are different.
SOC_ENUM_EXT("Audio_Amp_L_Switch", Audio_DL_Enum[1], Audio_AmpL_Get, Audio_AmpL_Set),//Headphone left channel control switch
/////////////////////////////////////////////////////////
/*
//Receiver
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];//Choosing the Power Type of the Receiver
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,Power on 2. Set up pmic register 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),//Hearing control switch
/////////////////////////////////////////////////////////
/*
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];//Selection of Horn Power Supply Type
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,Power on 2, set pmic 3, AB and D mode selection 4, set the speaker gain value (Note: 3 and 4 are only set when using built-in 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),//Horn Control Switch
/////////////////////////////////////////////////////////
/*
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];//Choose the type of power supply for simultaneous operation of headphones and speakers
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. Turn on power supply 2, set pmic register 3, select AB, D and receive mode 4, set headphone volume 5, set speaker gain value (Note: 3 and 5 are built-in PA settings only)
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),//Headphone speaker control switch
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];//Selection of External PA Power Supply Type
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);//External PA enabled foot gpio control
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),//External PA Control Switch
/////////////////////////////////////////////////////////
/*
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];//Select the type of power supply for handset horn switching
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);//According to the parameters passed in, ture, enable the receiver, turn off the speaker false, enable the speaker, turn off the receiver. For example, when talking, switch speaker operation
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),//Hearing horn switch control switch
SOC_SINGLE_EXT("Audio HP Impedance", SND_SOC_NOPM, 0, 512, 0, Audio_Hp_Impedance_Get,
Audio_Hp_Impedance_Set),
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
4.1.1.2.2,mt6331_UL_Codec_controls
snd_soc_add_codec_controls(codec, mt6331_UL_Codec_controls,
ARRAY_SIZE(mt6331_UL_Codec_controls));
//The mt6331_UL_Codec_controls array is defined as follows
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];//Selection of ADC1 Power Supply Type
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]) {
//From the figure above, we can see that MIC code defines six modes. There are four modes in this code: ACC, DCC, DMIC and DCCECMDIFF. Define mobile mic and headphone mic mode, according to the hardware design. Define mic schema code in 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),//Main Microphone Control Switch
SOC_ENUM_EXT("Audio_ADC_2_Switch", Audio_UL_Enum[1], Audio_ADC2_Get, Audio_ADC2_Set),//Headphone microphone control switch, and adc 1 are basically the same, but the adc channel is different.
SOC_ENUM_EXT("Audio_ADC_3_Switch", Audio_UL_Enum[2], Audio_ADC3_Get, Audio_ADC3_Set),//I won't support it
SOC_ENUM_EXT("Audio_ADC_4_Switch", Audio_UL_Enum[3], Audio_ADC4_Get, Audio_ADC4_Set),//I won't support it
/////////////////////////////////////////////////////////
//Mixer can mix multiple inputs to output
//Mux can only select one of multiple inputs as output
//Mixer, of course, can also select only one input from multiple inputs as output.
/*
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];//Selection of preamplifier 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;
}
//It can be seen from the above that this function sets differential input AIN0, AIN1, AIN2, and the microphone preamplifier sets AIN. The so-called differential input form.
//Differential amplifier circuit utilizes symmetry and negative feedback of circuit parameters to effectively stabilize the static operating point. It is widely used in the input stage of direct coupling circuit and measurement circuit because of its remarkable feature of amplifying differential mode signal and suppressing common mode signal.
//Here is a brief introduction to single-ended input and differential input.
//When working in single-ended mode, ADC converts the voltage value of single input pin to ground. When gain is1The measured value is the input voltage value; the range is0V reach VREF;When the gain increases, the input range decreases correspondingly.
//When working in differential mode, ADC converts the difference between AIN + and AIN - pins; when the gain is 0.1When the measured value is equal to(AIN+)-(AIN-),The scope is-VREF reach+VREF;When the gain increases, the input range decreases accordingly.
//Note: In differential mode, the negative voltage refers to that the voltage of AIN-pin is greater than that of AIN+pin, and the actual voltage input to both pins must be positive to the ground; for example, if the voltage input from AIN+pin is:0V,AIN-The input voltage of the pin is1/2VREF When the differential input voltage is(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),//A control switch for a preamplifier. A preamplifier is a circuit or electronic device placed between the source and the amplifier stage. It is designed to receive weak voltage signals from the source.
/////////////////////////////////////////////////////////
/*
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];//Select 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 Differential Input Control Switch
SOC_ENUM_EXT("Audio_ADC_2_Sel", Audio_UL_Enum[6], Audio_ADC2_Sel_Get, Audio_ADC2_Sel_Set),//mic2 Differential Input Control Switch
SOC_ENUM_EXT("Audio_ADC_3_Sel", Audio_UL_Enum[7], Audio_ADC3_Sel_Get, Audio_ADC3_Sel_Set),//I won't support it
SOC_ENUM_EXT("Audio_ADC_4_Sel", Audio_UL_Enum[8], Audio_ADC4_Sel_Get, Audio_ADC4_Sel_Set),//I won't support it
SOC_ENUM_EXT("Audio_PGA1_Setting", Audio_UL_Enum[9], Audio_PGA1_Get, Audio_PGA1_Set),//mic left PGA control switch, from Programmable Gain Amplifier (PGA), is a versatile amplifier. Its magnification can be controlled by program according to the need. With this amplifier, the full range signal of A/D converter can be homogenized by program adjustment, thus greatly improving the measurement accuracy.
SOC_ENUM_EXT("Audio_PGA2_Setting", Audio_UL_Enum[10], Audio_PGA2_Get, Audio_PGA2_Set),//mic right PGA control switch
SOC_ENUM_EXT("Audio_PGA3_Setting", Audio_UL_Enum[11], Audio_PGA3_Get, Audio_PGA3_Set),//I won't support it
SOC_ENUM_EXT("Audio_PGA4_Setting", Audio_UL_Enum[12], Audio_PGA4_Get, Audio_PGA4_Set),//I won't support it
/////////////////////////////////////////////////////////
/*
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),//Acquisition Source mic control settings, whether from phone mic or from earphone mic, mt6737 only collects from ADC1
SOC_ENUM_EXT("Audio_MicSource2_Setting", Audio_UL_Enum[14], Audio_MicSource2_Get,
Audio_MicSource2_Set),//I won't support it
SOC_ENUM_EXT("Audio_MicSource3_Setting", Audio_UL_Enum[15], Audio_MicSource3_Get,
Audio_MicSource3_Set),//I won't support it
SOC_ENUM_EXT("Audio_MicSource4_Setting", Audio_UL_Enum[16], Audio_MicSource4_Get,
Audio_MicSource4_Set),//I won't support it
/////////////////////////////////////////////////////////
/*
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];//Setting up mic mode 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 mode setting control switch
SOC_ENUM_EXT("Audio_MIC2_Mode_Select", Audio_UL_Enum[18], Audio_Mic2_Mode_Select_Get,
Audio_Mic2_Mode_Select_Set),//mic2 mode setting control switch
SOC_ENUM_EXT("Audio_MIC3_Mode_Select", Audio_UL_Enum[19], Audio_Mic3_Mode_Select_Get,
Audio_Mic3_Mode_Select_Set),//mic3 mode setting control switch
SOC_ENUM_EXT("Audio_MIC4_Mode_Select", Audio_UL_Enum[20], Audio_Mic4_Mode_Select_Get,
Audio_Mic4_Mode_Select_Set),//mic4 mode setting control switch
/////////////////////////////////////////////////////////
/*
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];//Setting up power supply mode of 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 power supply mode setting control switch
/////////////////////////////////////////////////////////
/*
//VOW It's a headset that can be connected via 3G/4G and 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 headset control switch
SOC_ENUM_EXT("Audio_Preamp2_Switch", Audio_UL_Enum[23], Audio_PreAmp2_Get,
Audio_PreAmp2_Set),//mic preamplifier 2 control switch
SOC_ENUM_EXT("Audio_Vow_Digital_Func_Switch", Audio_UL_Enum[24],
Audio_Vow_Digital_Func_Switch_Get, Audio_Vow_Digital_Func_Switch_Set),
//Below are all vow headphones, not for the moment.
SOC_ENUM_EXT("Audio_Vow_MIC_Type_Select", Audio_UL_Enum[25], Audio_Vow_MIC_Type_Select_Get,
Audio_Vow_MIC_Type_Select_Set),
SOC_SINGLE_EXT("Audio VOWCFG0 Data", SND_SOC_NOPM, 0, 0x80000, 0, Audio_Vow_Cfg0_Get,
Audio_Vow_Cfg0_Set),
SOC_SINGLE_EXT("Audio VOWCFG1 Data", SND_SOC_NOPM, 0, 0x80000, 0, Audio_Vow_Cfg1_Get,
Audio_Vow_Cfg1_Set),
SOC_SINGLE_EXT("Audio VOWCFG2 Data", SND_SOC_NOPM, 0, 0x80000, 0, Audio_Vow_Cfg2_Get,
Audio_Vow_Cfg2_Set),
SOC_SINGLE_EXT("Audio VOWCFG3 Data", SND_SOC_NOPM, 0, 0x80000, 0, Audio_Vow_Cfg3_Get,
Audio_Vow_Cfg3_Set),
SOC_SINGLE_EXT("Audio VOWCFG4 Data", SND_SOC_NOPM, 0, 0x80000, 0, Audio_Vow_Cfg4_Get,
Audio_Vow_Cfg4_Set),
SOC_SINGLE_EXT("Audio VOWCFG5 Data", SND_SOC_NOPM, 0, 0x80000, 0, Audio_Vow_Cfg5_Get,
Audio_Vow_Cfg5_Set),
SOC_SINGLE_EXT("Audio_VOW_State", SND_SOC_NOPM, 0, 0x80000, 0, Audio_Vow_State_Get,
Audio_Vow_State_Set),
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
4.1.1.2.3,mt6331_Voice_Switch
No substantive function
snd_soc_add_codec_controls(codec, mt6331_Voice_Switch, ARRAY_SIZE(mt6331_Voice_Switch));
static const struct snd_kcontrol_new mt6331_Voice_Switch[] = {
/* SOC_DAPM_ENUM_EXT("Voice Mux", Audio_DL_Enum[10], Voice_Mux_Get, Voice_Mux_Set), */
};
- 1
- 2
- 3
- 4
- 5
4.1.1.2.4,mt6331_pmic_Test_controls
pmic test control
snd_soc_add_codec_controls(codec, mt6331_pmic_Test_controls,
ARRAY_SIZE(mt6331_pmic_Test_controls));
static const struct snd_kcontrol_new mt6331_pmic_Test_controls[] = {
SOC_ENUM_EXT("SineTable_DAC_HP", Pmic_Test_Enum[0], SineTable_DAC_HP_Get,
SineTable_DAC_HP_Set),
SOC_ENUM_EXT("DAC_LOOP_DAC_HS", Pmic_Test_Enum[1], ADC_LOOP_DAC_HS_Get,
ADC_LOOP_DAC_HS_Set),
SOC_ENUM_EXT("DAC_LOOP_DAC_HP", Pmic_Test_Enum[2], ADC_LOOP_DAC_HP_Get,
ADC_LOOP_DAC_HP_Set),
SOC_ENUM_EXT("Voice_Call_DAC_DAC_HS", Pmic_Test_Enum[3], Voice_Call_DAC_DAC_HS_Get,
Voice_Call_DAC_DAC_HS_Set),
SOC_ENUM_EXT("SineTable_UL2", Pmic_Test_Enum[4], SineTable_UL2_Get, SineTable_UL2_Set),
SOC_ENUM_EXT("Pmic_Loopback", Pmic_Test_Enum[5], Pmic_Loopback_Get, Pmic_Loopback_Set),
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
4.1.1.2.5, mt6331_snd_Speaker_controls (built-in PA related controls)
#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,Built-in PA is class AB power amplifier
return 0;
}
*/
/////////////////////////////////////////////////////////
SOC_ENUM_EXT("Audio_Speaker_class_Switch", Audio_Speaker_Enum[0], Audio_Speaker_Class_Get,
Audio_Speaker_Class_Set),//Set PA to Class AB Amplifier
SOC_ENUM_EXT("Audio_Speaker_PGA_gain", Audio_Speaker_Enum[1], Audio_Speaker_Pga_Gain_Get,
Audio_Speaker_Pga_Gain_Set),//Set gain to 1db, which is used to adjust the magnification of the input acoustic signal.
/////////////////////////////////////////////////////////
/*
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;
}
//Configuration according to the following figure
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),//Class AB/D Amplifier Selection Flag Control
//PA Current Detection
SOC_ENUM_EXT("Audio_Speaker_CurrentSensing", Audio_Speaker_Enum[3],
Audio_Speaker_Current_Sensing_Get, Audio_Speaker_Current_Sensing_Set),
SOC_ENUM_EXT("Audio_Speaker_CurrentPeakDetector", Audio_Speaker_Enum[4],
Audio_Speaker_Current_Sensing_Peak_Detector_Get,
Audio_Speaker_Current_Sensing_Peak_Detector_Set),
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
4.1.1.2.6,Audio_snd_auxadc_controls
snd_soc_add_codec_controls(codec, Audio_snd_auxadc_controls,
ARRAY_SIZE(Audio_snd_auxadc_controls));
static const struct snd_kcontrol_new Audio_snd_auxadc_controls[] = {
SOC_SINGLE_EXT("Audio AUXADC Data", SND_SOC_NOPM, 0, 0x80000, 0, Audio_AuxAdcData_Get,
Audio_AuxAdcData_Set),//audio adc data control
};
static int Audio_AuxAdcData_Get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
#ifdef CONFIG_MTK_SPEAKER
ucontrol->value.integer.value[0] = Audio_AuxAdcData_Get_ext();
//PMIC_IMM_GetOneChannelValue(AUX_ICLASSAB_AP, 1, 0);
#else
ucontrol->value.integer.value[0] = 0;
#endif
pr_warn("%s dMax = 0x%lx\n", __func__, ucontrol->value.integer.value[0]);
return 0;
}
static int Audio_AuxAdcData_Set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
dAuxAdcChannel = ucontrol->value.integer.value[0];
pr_warn("%s dAuxAdcChannel = 0x%x\n", __func__, dAuxAdcChannel);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
4.1.1.2.7,mt6331_codec_init_reg(codec)
//Codc initialization
static void mt6331_codec_init_reg(struct snd_soc_codec *codec)
{
pr_warn("%s\n", __func__);
Ana_Set_Reg(TOP_CLKSQ, 0x0, 0x0001);
/* Disable CLKSQ 26MHz */
Ana_Set_Reg(AUDDEC_ANA_CON8, 0x0002, 0x0002);
/* disable AUDGLB */
Ana_Set_Reg(TOP_CKPDN_CON0_SET, 0x3800, 0x3800);
/* Turn off AUDNCP_CLKDIV engine clock,Turn off AUD 26M */
Ana_Set_Reg(AUDDEC_ANA_CON0, 0xe000, 0xe000);
/* Disable HeadphoneL/HeadphoneR/voice short circuit protection */
Ana_Set_Reg(AUDENC_ANA_CON9, 0x0000, 0x0010);
/* power off mic bias1 */
Ana_Set_Reg(AFE_PMIC_NEWIF_CFG2, 0x8000, 0x8000);
/* Reverse the PMIC clock*/
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
4.1.1.2.8,InitCodecDefault()
void InitCodecDefault(void)
{
pr_warn("%s\n", __func__);
//Setting mic1 volume Max
mCodec_data->mAudio_Ana_Volume[AUDIO_ANALOG_VOLUME_MICAMP1] = 3;
//Setting mic2 volume Max
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;
//Maximum right channel output of headphone volume
mCodec_data->mAudio_Ana_Volume[AUDIO_ANALOG_VOLUME_HPOUTR] = 8;
mCodec_data->mAudio_Ana_Volume[AUDIO_ANALOG_VOLUME_HPOUTR] = 8;
mCodec_data->mAudio_Ana_Mux[AUDIO_ANALOG_MUX_IN_MIC1] =
AUDIO_ANALOG_AUDIOANALOG_INPUT_PREAMP;
mCodec_data->mAudio_Ana_Mux[AUDIO_ANALOG_MUX_IN_MIC2] =
AUDIO_ANALOG_AUDIOANALOG_INPUT_PREAMP;
mCodec_data->mAudio_Ana_Mux[AUDIO_ANALOG_MUX_IN_MIC3] =
AUDIO_ANALOG_AUDIOANALOG_INPUT_PREAMP;
mCodec_data->mAudio_Ana_Mux[AUDIO_ANALOG_MUX_IN_MIC4] =
AUDIO_ANALOG_AUDIOANALOG_INPUT_PREAMP;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
4.2,Codec Dai(mtk_6331_dai_codecs)
static struct snd_soc_dai_driver mtk_6331_dai_codecs[] = {
{
.name = MT_SOC_CODEC_TXDAI_NAME,
.ops = &mt6323_aif1_dai_ops,
.playback = {
.stream_name = MT_SOC_DL1_STREAM_NAME,
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SND_SOC_ADV_MT_FMTS,
},
},
{
.name = MT_SOC_CODEC_RXDAI_NAME,
.ops = &mt6323_aif1_dai_ops,
.capture = {
.stream_name = MT_SOC_UL1_STREAM_NAME,
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SND_SOC_ADV_MT_FMTS,
},
},
{
.name = MT_SOC_CODEC_TDMRX_DAI_NAME,
.ops = &mt6323_aif1_dai_ops,
.capture = {
.stream_name = MT_SOC_TDM_CAPTURE_STREAM_NAME,
.channels_min = 2,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE |
SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_S24_3LE |
SNDRV_PCM_FMTBIT_U24_3BE | SNDRV_PCM_FMTBIT_S24_3BE |
SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE),
},
},
{
.name = MT_SOC_CODEC_I2S0TXDAI_NAME,
.ops = &mt6323_aif1_dai_ops,
.playback = {
.stream_name = MT_SOC_I2SDL1_STREAM_NAME,
.channels_min = 1,
.channels_max = 2,
.rate_min = 8000,
.rate_max = 192000,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SND_SOC_ADV_MT_FMTS,
}
},
{
.name = MT_SOC_CODEC_VOICE_MD1DAI_NAME,
.ops = &mt6323_aif1_dai_ops,
.playback = {
.stream_name = MT_SOC_VOICE_MD1_STREAM_NAME,
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
},
.capture = {
.stream_name = MT_SOC_VOICE_MD1_STREAM_NAME,
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
},
},
{
.name = MT_SOC_CODEC_VOICE_MD2DAI_NAME,
.ops = &mt6323_aif1_dai_ops,
.playback = {
.stream_name = MT_SOC_VOICE_MD2_STREAM_NAME,
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
},
.capture = {
.stream_name = MT_SOC_VOICE_MD2_STREAM_NAME,
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
},
},
{
.name = MT_SOC_CODEC_FMI2S2RXDAI_NAME,
.ops = &mt6323_aif1_dai_ops,
.playback = {
.stream_name = MT_SOC_FM_I2S2_STREAM_NAME,
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
},
.capture = {
.stream_name = MT_SOC_FM_I2S2_RECORD_STREAM_NAME,
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
},
},
{
.name = MT_SOC_CODEC_FMMRGTXDAI_DUMMY_DAI_NAME,
.ops = &mt6323_aif1_dai_ops,
.playback = {
.stream_name = MT_SOC_FM_MRGTX_STREAM_NAME,
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
},
},
{
.name = MT_SOC_CODEC_ULDLLOOPBACK_NAME,
.ops = &mt6323_aif1_dai_ops,
.playback = {
.stream_name = MT_SOC_ULDLLOOPBACK_STREAM_NAME,
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
},
.capture = {
.stream_name = MT_SOC_ULDLLOOPBACK_STREAM_NAME,
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SND_SOC_ADV_MT_FMTS,
},
},
{
.name = MT_SOC_CODEC_STUB_NAME,
.ops = &mt6323_aif1_dai_ops,
.playback = {
.stream_name = MT_SOC_ROUTING_STREAM_NAME,
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SND_SOC_ADV_MT_FMTS,
},
},
{
.name = MT_SOC_CODEC_RXDAI2_NAME,
.capture = {
.stream_name = MT_SOC_UL1DATA2_STREAM_NAME,
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SND_SOC_ADV_MT_FMTS,
},
},
{
.name = MT_SOC_CODEC_MRGRX_DAI_NAME,
.ops = &mt6323_aif1_dai_ops,
.playback = {
.stream_name = MT_SOC_MRGRX_STREAM_NAME,
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SND_SOC_ADV_MT_FMTS,
},
.capture = {
.stream_name = MT_SOC_MRGRX_STREAM_NAME,
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SND_SOC_ADV_MT_FMTS,
},
},
{
.name = MT_SOC_CODEC_HP_IMPEDANCE_NAME,
.ops = &mt6323_aif1_dai_ops,
.playback = {
.stream_name = MT_SOC_HP_IMPEDANCE_STREAM_NAME,
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SND_SOC_ADV_MT_FMTS,
},
},
{
.name = MT_SOC_CODEC_FM_I2S_DAI_NAME,
.ops = &mt6323_aif1_dai_ops,
.playback = {
.stream_name = MT_SOC_FM_I2S_PLAYBACK_STREAM_NAME,
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SND_SOC_ADV_MT_FMTS,
},
},
{
.name = MT_SOC_CODEC_TXDAI2_NAME,
.ops = &mt6323_aif1_dai_ops,
.playback = {
.stream_name = MT_SOC_DL2_STREAM_NAME,
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SND_SOC_ADV_MT_FMTS,
},
},
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
5,Machine
kernel-3.18/sound/soc/mediatek/mt_soc_audio_v3/mt_soc_machine.c
module_init(mt_soc_snd_init)->mt_soc_snd_init(void)
static int __init mt_soc_snd_init(void)
{
int ret;
struct snd_soc_card *card = &snd_soc_card_mt;
pr_debug("mt_soc_snd_init card addr = %p\n", card);
mt_snd_device = platform_device_alloc("soc-audio", -1);
if (!mt_snd_device) {
pr_err("mt6589_probe platform_device_alloc fail\n");
return -ENOMEM;
}
platform_set_drvdata(mt_snd_device, &snd_soc_card_mt);
ret = platform_device_add(mt_snd_device);
if (ret != 0) {
pr_err("mt_soc_snd_init goto put_device fail\n");
goto put_device;
}
pr_debug("mt_soc_snd_init dai_link = %p\n", snd_soc_card_mt.dai_link);
/* create debug file */
mt_sco_audio_debugfs = debugfs_create_file(DEBUG_FS_NAME,
S_IFREG | S_IRUGO, NULL, (void *)DEBUG_FS_NAME,
&mtaudio_debug_ops);
/* create analog debug file */
mt_sco_audio_debugfs = debugfs_create_file(DEBUG_ANA_FS_NAME,
S_IFREG | S_IRUGO, NULL,
(void *)DEBUG_ANA_FS_NAME,
&mtaudio_ana_debug_ops);
return 0;
put_device:
platform_device_put(mt_snd_device);
return ret;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
Next, the mt_soc_snd_init function is analyzed.
5.1,snd_soc_card_mt
snd_soc_card_mt is passed into the soc_probe function through the mt_soc_snd_init function.
dai_link = mt_soc_dai_common is the core of struct snd_soc_card data structure.
static struct snd_soc_card snd_soc_card_mt = {
.name = "mt-snd-card",
.dai_link = mt_soc_dai_common,
.num_links = ARRAY_SIZE(mt_soc_dai_common),
.controls = mt_soc_controls,
.num_controls = ARRAY_SIZE(mt_soc_controls),
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
5.1.1,.dai_link = mt_soc_dai_common
mt_soc_dai_common is bound together by stream_name, cpu_dai_name, platform_name, codec_dai_name, codec_name
/* Digital audio interface glue - connects codec <---> CPU */
static struct snd_soc_dai_link mt_soc_dai_common[] = {
/* FrontEnd DAI Links */
{
.name = "MultiMedia1",
.stream_name = MT_SOC_DL1_STREAM_NAME,
.cpu_dai_name = MT_SOC_DL1DAI_NAME,
.platform_name = MT_SOC_DL1_PCM,
.codec_dai_name = MT_SOC_CODEC_TXDAI_NAME,
.codec_name = MT_SOC_CODEC_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "MultiMedia2",
.stream_name = MT_SOC_UL1_STREAM_NAME,
.cpu_dai_name = MT_SOC_UL1DAI_NAME,
.platform_name = MT_SOC_UL1_PCM,
.codec_dai_name = MT_SOC_CODEC_RXDAI_NAME,
.codec_name = MT_SOC_CODEC_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "Voice_MD1",
.stream_name = MT_SOC_VOICE_MD1_STREAM_NAME,
.cpu_dai_name = MT_SOC_VOICE_MD1_NAME,
.platform_name = MT_SOC_VOICE_MD1,
.codec_dai_name = MT_SOC_CODEC_VOICE_MD1DAI_NAME,
.codec_name = MT_SOC_CODEC_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "HDMI_OUT",
.stream_name = MT_SOC_HDMI_STREAM_NAME,
.cpu_dai_name = MT_SOC_HDMI_NAME,
.platform_name = MT_SOC_HDMI_PCM,
.codec_dai_name = MT_SOC_CODEC_HDMI_DUMMY_DAI_NAME,
.codec_name = MT_SOC_CODEC_DUMMY_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "ULDLOOPBACK",
.stream_name = MT_SOC_ULDLLOOPBACK_STREAM_NAME,
.cpu_dai_name = MT_SOC_ULDLLOOPBACK_NAME,
.platform_name = MT_SOC_ULDLLOOPBACK_PCM,
.codec_dai_name = MT_SOC_CODEC_ULDLLOOPBACK_NAME,
.codec_name = MT_SOC_CODEC_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "I2S0OUTPUT",
.stream_name = MT_SOC_I2S0_STREAM_NAME,
.cpu_dai_name = MT_SOC_I2S0_NAME,
.platform_name = MT_SOC_I2S0_PCM,
.codec_dai_name = MT_SOC_CODEC_I2S0_DUMMY_DAI_NAME,
.codec_name = MT_SOC_CODEC_DUMMY_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "MRGRX",
.stream_name = MT_SOC_MRGRX_STREAM_NAME,
.cpu_dai_name = MT_SOC_MRGRX_NAME,
.platform_name = MT_SOC_MRGRX_PCM,
.codec_dai_name = MT_SOC_CODEC_MRGRX_DAI_NAME,
.codec_name = MT_SOC_CODEC_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "MRGRXCAPTURE",
.stream_name = MT_SOC_MRGRX_CAPTURE_STREAM_NAME,
.cpu_dai_name = MT_SOC_MRGRX_NAME,
.platform_name = MT_SOC_MRGRX_AWB_PCM,
.codec_dai_name = MT_SOC_CODEC_MRGRX_DUMMY_DAI_NAME,
.codec_name = MT_SOC_CODEC_DUMMY_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "I2S0DL1OUTPUT",
.stream_name = MT_SOC_I2SDL1_STREAM_NAME,
.cpu_dai_name = MT_SOC_I2S0DL1_NAME,
.platform_name = MT_SOC_I2S0DL1_PCM,
.codec_dai_name = MT_SOC_CODEC_I2S0TXDAI_NAME,
.codec_name = MT_SOC_CODEC_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "DL1AWBCAPTURE",
.stream_name = MT_SOC_DL1_AWB_RECORD_STREAM_NAME,
.cpu_dai_name = MT_SOC_DL1AWB_NAME,
.platform_name = MT_SOC_DL1_AWB_PCM,
.codec_dai_name = MT_SOC_CODEC_DL1AWBDAI_NAME,
.codec_name = MT_SOC_CODEC_DUMMY_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "Voice_MD1_BT",
.stream_name = MT_SOC_VOICE_MD1_BT_STREAM_NAME,
.cpu_dai_name = MT_SOC_VOICE_MD1_BT_NAME,
.platform_name = MT_SOC_VOICE_MD1_BT,
.codec_dai_name = MT_SOC_CODEC_VOICE_MD1_BTDAI_NAME,
.codec_name = MT_SOC_CODEC_DUMMY_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "VOIP_CALL_BT_PLAYBACK",
.stream_name = MT_SOC_VOIP_BT_OUT_STREAM_NAME,
.cpu_dai_name = MT_SOC_VOIP_CALL_BT_OUT_NAME,
.platform_name = MT_SOC_VOIP_BT_OUT,
.codec_dai_name = MT_SOC_CODEC_VOIPCALLBTOUTDAI_NAME,
.codec_name = MT_SOC_CODEC_DUMMY_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "VOIP_CALL_BT_CAPTURE",
.stream_name = MT_SOC_VOIP_BT_IN_STREAM_NAME,
.cpu_dai_name = MT_SOC_VOIP_CALL_BT_IN_NAME,
.platform_name = MT_SOC_VOIP_BT_IN,
.codec_dai_name = MT_SOC_CODEC_VOIPCALLBTINDAI_NAME,
.codec_name = MT_SOC_CODEC_DUMMY_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "TDM_Debug_CAPTURE",
.stream_name = MT_SOC_TDM_CAPTURE_STREAM_NAME,
.cpu_dai_name = MT_SOC_TDMRX_NAME,
.platform_name = MT_SOC_TDMRX_PCM,
.codec_dai_name = MT_SOC_CODEC_TDMRX_DAI_NAME,
.codec_name = MT_SOC_CODEC_DUMMY_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "FM_MRG_TX",
.stream_name = MT_SOC_FM_MRGTX_STREAM_NAME,
.cpu_dai_name = MT_SOC_FM_MRGTX_NAME,
.platform_name = MT_SOC_FM_MRGTX_PCM,
.codec_dai_name = MT_SOC_CODEC_FMMRGTXDAI_DUMMY_DAI_NAME,
.codec_name = MT_SOC_CODEC_DUMMY_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "MultiMedia3",
.stream_name = MT_SOC_UL1DATA2_STREAM_NAME,
.cpu_dai_name = MT_SOC_UL2DAI_NAME,
.platform_name = MT_SOC_UL2_PCM,
.codec_dai_name = MT_SOC_CODEC_RXDAI2_NAME,
.codec_name = MT_SOC_CODEC_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "I2S0_AWB_CAPTURE",
.stream_name = MT_SOC_I2S0AWB_STREAM_NAME,
.cpu_dai_name = MT_SOC_I2S0AWBDAI_NAME,
.platform_name = MT_SOC_I2S0_AWB_PCM,
.codec_dai_name = MT_SOC_CODEC_I2S0AWB_NAME,
.codec_name = MT_SOC_CODEC_DUMMY_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "Voice_MD2",
.stream_name = MT_SOC_VOICE_MD2_STREAM_NAME,
.cpu_dai_name = MT_SOC_VOICE_MD2_NAME,
.platform_name = MT_SOC_VOICE_MD2,
.codec_dai_name = MT_SOC_CODEC_VOICE_MD2DAI_NAME,
.codec_name = MT_SOC_CODEC_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "PLATOFRM_CONTROL",
.stream_name = MT_SOC_ROUTING_STREAM_NAME,
.cpu_dai_name = MT_SOC_ROUTING_DAI_NAME,
.platform_name = MT_SOC_ROUTING_PCM,
.codec_dai_name = MT_SOC_CODEC_DUMMY_DAI_NAME,
.codec_name = MT_SOC_CODEC_DUMMY_NAME,
.init = mt_soc_audio_init2,
.ops = &mtmachine_audio_ops2,
},
{
.name = "Voice_MD2_BT",
.stream_name = MT_SOC_VOICE_MD2_BT_STREAM_NAME,
.cpu_dai_name = MT_SOC_VOICE_MD2_BT_NAME,
.platform_name = MT_SOC_VOICE_MD2_BT,
.codec_dai_name = MT_SOC_CODEC_VOICE_MD2_BTDAI_NAME,
.codec_name = MT_SOC_CODEC_DUMMY_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "HP_IMPEDANCE",
.stream_name = MT_SOC_HP_IMPEDANCE_STREAM_NAME,
.cpu_dai_name = MT_SOC_HP_IMPEDANCE_NAME,
.platform_name = MT_SOC_HP_IMPEDANCE_PCM,
.codec_dai_name = MT_SOC_CODEC_HP_IMPEDANCE_NAME,
.codec_name = MT_SOC_CODEC_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "FM_I2S_RX_Playback",
.stream_name = MT_SOC_FM_I2S_PLAYBACK_STREAM_NAME,
.cpu_dai_name = MT_SOC_FM_I2S_NAME,
.platform_name = MT_SOC_FM_I2S_PCM,
.codec_dai_name = MT_SOC_CODEC_FM_I2S_DAI_NAME,
.codec_name = MT_SOC_CODEC_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "FM_I2S_RX_Capture",
.stream_name = MT_SOC_FM_I2S_CAPTURE_STREAM_NAME,
.cpu_dai_name = MT_SOC_FM_I2S_NAME,
.platform_name = MT_SOC_FM_I2S_AWB_PCM,
.codec_dai_name = MT_SOC_CODEC_FM_I2S_DUMMY_DAI_NAME,
.codec_name = MT_SOC_CODEC_DUMMY_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
{
.name = "OFFLOAD_GDMA_OUT",
.stream_name = MT_SOC_OFFLOAD_GDMA_STREAM_NAME,
.cpu_dai_name = MT_SOC_OFFLOAD_GDMA_NAME,
.platform_name = MT_SOC_OFFLOAD_GDMA_PCM,
.codec_dai_name = MT_SOC_CODEC_OFFLOAD_GDMA_DAI_NAME,
.codec_name = MT_SOC_CODEC_DUMMY_NAME,
.init = mt_soc_audio_init,
/* .ops = &mt_machine_audio_ops, */
.compr_ops = &mt_machine_audio_compr_ops,
},
{
.name = "MultiMedia_DL2",
.stream_name = MT_SOC_DL2_STREAM_NAME,
.cpu_dai_name = MT_SOC_DL2DAI_NAME,
.platform_name = MT_SOC_DL2_PCM,
.codec_dai_name = MT_SOC_CODEC_TXDAI2_NAME,
.codec_name = MT_SOC_CODEC_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
5.1.2,mt_soc_controls
I2S Low Jitter Function Control
static const struct snd_kcontrol_new mt_soc_controls[] = {
SOC_ENUM_EXT("I2S low Jitter function", mt_soc_machine_enum[0], mt6735_get_lowjitter,
mt6735_set_lowjitter),
};
static int mt6735_get_lowjitter(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
pr_debug("%s: mt_soc_lowjitter_control = %d\n", __func__, mt_soc_lowjitter_control);
ucontrol->value.integer.value[0] = mt_soc_lowjitter_control;
return 0;
}
static int mt6735_set_lowjitter(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
pr_debug("%s()\n", __func__);
mt_soc_lowjitter_control = ucontrol->value.integer.value[0];
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
5.2,platform_device_add(mt_snd_device)
platform_set_drvdata(mt_snd_device, &snd_soc_card_mt);//Store the snd_soc_card_mt structure in the platform device mt_snd_device
ret = platform_device_add(mt_snd_device);//Mount device mt_snd_device onto platform bus
- 1
- 2
Next to the kernel-3.18/sound/soc/soc-core.c file
The soc_probe function itself is very simple. The Snd_soc_card structure is extracted from the platform device data structure through the function platform_get_drvdata. Then call snd_soc_register_card, apply memory for snd_soc_pcm_runtime array through snd_soc_register_card. Each dai_link corresponds to a unit of snd_soc_pcm_runtime array. Then copy the dai_link configuration in snd_soc_card to the corresponding snd_soc_pcm_runtime. Finally, most of the work is implemented in snd_soc_instantiate_card.
module_init(snd_soc_init)->platform_driver_register(&soc_driver)->soc_probe
/* ASoC platform driver */
static struct platform_driver soc_driver = {
.driver = {
.name = "soc-audio",
.owner = THIS_MODULE,
.pm = &snd_soc_pm_ops,
},
.probe = soc_probe,
.remove = soc_remove,
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
/* probes a new socdev */
static int soc_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
//The Snd_soc_card structure is extracted from the platform device data structure through the function 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);//Pass the extracted value structure Snd_soc_card to snd_soc_register_card
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
Next, enter snd_soc_register_card(card)
/**
* snd_soc_register_card - Register a card with the ASoC core
*
* @card: Card to register
*
*/
int snd_soc_register_card(struct snd_soc_card *card)
{
int i, j, ret;
if (!card->name || !card->dev)
return -EINVAL;
for (i = 0; i < card->num_links; i++) {
struct snd_soc_dai_link *link = &card->dai_link[i];
ret = snd_soc_init_multicodec(card, link);
if (ret) {
dev_err(card->dev, "ASoC: failed to init multicodec\n");
return ret;
}
for (j = 0; j < link->num_codecs; j++) {
/*
* Codec must be specified by 1 of name or OF node,
* not both or neither.
*/
if (!!link->codecs[j].name ==
!!link->codecs[j].of_node) {
dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",
link->name);
return -EINVAL;
}
/* Codec DAI name must be specified */
if (!link->codecs[j].dai_name) {
dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",
link->name);
return -EINVAL;
}
}
/*
* Platform may be specified by either name or OF node, but
* can be left unspecified, and a dummy platform will be used.
*/
if (link->platform_name && link->platform_of_node) {
dev_err(card->dev,
"ASoC: Both platform name/of_node are set for %s\n",
link->name);
return -EINVAL;
}
/*
* CPU device may be specified by either name or OF node, but
* can be left unspecified, and will be matched based on DAI
* name alone..
*/
if (link->cpu_name && link->cpu_of_node) {
dev_err(card->dev,
"ASoC: Neither/both cpu name/of_node are set for %s\n",
link->name);
return -EINVAL;
}
/*
* At least one of CPU DAI name or CPU device name/node must be
* specified
*/
if (!link->cpu_dai_name &&
!(link->cpu_name || link->cpu_of_node)) {
dev_err(card->dev,
"ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",
link->name);
return -EINVAL;
}
}
dev_set_drvdata(card->dev, card);
snd_soc_initialize_card_lists(card);
soc_init_card_debugfs(card);
card->rtd = devm_kzalloc(card->dev,
sizeof(struct snd_soc_pcm_runtime) *
(card->num_links + card->num_aux_devs),
GFP_KERNEL);
if (card->rtd == NULL)
return -ENOMEM;
card->num_rtd = 0;
card->rtd_aux = &card->rtd[card->num_links];
for (i = 0; i < card->num_links; i++) {
card->rtd[i].card = card;
> card->rtd[i].dai_link = &card->dai_link[i];
card->rtd[i].codec_dais = devm_kzalloc(card->dev,
sizeof(struct snd_soc_dai *) *
(card->rtd[i].dai_link->num_codecs),
> GFP_KERNEL);
if (card->rtd[i].codec_dais == NULL)
return -ENOMEM;
}
for (i = 0; i < card->num_aux_devs; i++)
card->rtd_aux[i].card = card;
INIT_LIST_HEAD(&card->dapm_dirty);
card->instantiated = 0;
mutex_init(&card->mutex);
mutex_init(&card->dapm_mutex);
ret = snd_soc_instantiate_card(card);
if (ret != 0)
soc_cleanup_card_debugfs(card);
/* deactivate pins to sleep state */
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int j;
for (j = 0; j < rtd->num_codecs; j++) {
struct snd_soc_dai *codec_dai = rtd->codec_dais[j];
if (!codec_dai->active)
pinctrl_pm_select_sleep_state(codec_dai->dev);
}
if (!cpu_dai->active)
pinctrl_pm_select_sleep_state(cpu_dai->dev);
}
return ret;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
Next, snd_soc_register_card is analyzed.
5.2.1,snd_soc_init_multicodec(card, link)
static struct snd_soc_card snd_soc_card_mt = {
.name = "mt-snd-card",
.dai_link = mt_soc_dai_common,
.num_links = ARRAY_SIZE(mt_soc_dai_common),
.controls = mt_soc_controls,
.num_controls = ARRAY_SIZE(mt_soc_controls),
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
static struct snd_soc_dai_link mt_soc_dai_common[] = {
/* FrontEnd DAI Links */
{
.name = "MultiMedia1",
.stream_name = MT_SOC_DL1_STREAM_NAME,
.cpu_dai_name = MT_SOC_DL1DAI_NAME,
.platform_name = MT_SOC_DL1_PCM,
.codec_dai_name = MT_SOC_CODEC_TXDAI_NAME,
.codec_name = MT_SOC_CODEC_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
....
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
for (i = 0; i < card->num_links; i++) {
struct snd_soc_dai_link *link = &card->dai_link[i];
ret = snd_soc_init_multicodec(card, link);
if (ret) {
dev_err(card->dev, "ASoC: failed to init multicodec\n");
return ret;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
The elements of the snd_soc_card_mt structure member mt_soc_dai_common array are passed into the snd_soc_init_multicodec function one by one through the for loop. This function is analyzed below.
static struct snd_soc_dai_link mt_soc_dai_common[] = {
/* FrontEnd DAI Links */
{
.name = "MultiMedia1",
.stream_name = MT_SOC_DL1_STREAM_NAME,
.cpu_dai_name = MT_SOC_DL1DAI_NAME,
.platform_name = MT_SOC_DL1_PCM,
.codec_dai_name = MT_SOC_CODEC_TXDAI_NAME,
.codec_name = MT_SOC_CODEC_NAME,
.init = mt_soc_audio_init,
.ops = &mt_machine_audio_ops,
},
.........
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
static int snd_soc_init_multicodec(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_link)
{
/* Legacy codec/codec_dai link is a single entry in multicodec */
if (dai_link->codec_name || dai_link->codec_of_node ||
dai_link->codec_dai_name) {
dai_link->num_codecs = 1;
dai_link->codecs = devm_kzalloc(card->dev,
sizeof(struct snd_soc_dai_link_component),
GFP_KERNEL);
if (!dai_link->codecs)
return -ENOMEM;
dai_link->codecs[0].name = dai_link->codec_name;
dai_link->codecs[0].of_node = dai_link->codec_of_node;
dai_link->codecs[0].dai_name = dai_link->codec_dai_name;
}
if (!dai_link->codecs) {
dev_err(card->dev, "ASoC: DAI link has no CODECs\n");
return -EINVAL;
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
This is a linked list, which takes out codec_name, codec_of_node, codec_dai_name to store in the snd_soc_dai_link structure element codecs[0]
for (i = 0; i < card->num_links; i++) {
struct snd_soc_dai_link *link = &card->dai_link[i];
ret = snd_soc_init_multicodec(card, link);
if (ret) {
dev_err(card->dev, "ASoC: failed to init multicodec\n");
return ret;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
This is to extract the codec_name, codec_of_node, codec_dai_name of all members of the snd_soc_card_mt structure member mt_soc_dai_common array to store the snd_soc_dai_link structure element codecs[0].
5.2.2,snd_soc_initialize_card_lists(card)
dev_set_drvdata(card->dev, card);//Private data card used to store drivers
snd_soc_initialize_card_lists(card);
static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card)
{
INIT_LIST_HEAD(&card->codec_dev_list);
INIT_LIST_HEAD(&card->widgets);
INIT_LIST_HEAD(&card->paths);
INIT_LIST_HEAD(&card->dapm_list);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
5.2.3,devm_kzalloc()
card->rtd = devm_kzalloc(card->dev,
sizeof(struct snd_soc_pcm_runtime) *
(card->num_links + card->num_aux_devs),
GFP_KERNEL);
if (card->rtd == NULL)
return -ENOMEM;
card->num_rtd = 0;
card->rtd_aux = &card->rtd[card->num_links];
for (i = 0; i < card->num_links; i++) {
card->rtd[i].card = card;
card->rtd[i].dai_link = &card->dai_link[i];
card->rtd[i].codec_dais = devm_kzalloc(card->dev,
sizeof(struct snd_soc_dai *) *
(card->rtd[i].dai_link->num_codecs),
GFP_KERNEL);
if (card->rtd[i].codec_dais == NULL)
return -ENOMEM;
}
for (i = 0; i < card->num_aux_devs; i++)
card->rtd_aux[i].card = card;
INIT_LIST_HEAD(&card->dapm_dirty);
card->instantiated = 0;
mutex_init(&card->mutex);
mutex_init(&card->dapm_mutex);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
To apply for memory for snd_soc_pcm_runtime structure array, each dai_link corresponds to a unit of snd_soc_pcm_runtime array, and then copies the dai_link configuration in snd_soc_card to the corresponding snd_soc_pcm_runtime.
struct snd_soc_card {
…
struct snd_soc_pcm_runtime *rtd;
//Point to struct snd_soc_pcm_runtime entity, common
ARRAY_SIZE(mt_soc_dai_common)=23.
…
}
5.2.4, snd_soc_instantiate_card(card)
static int snd_soc_instantiate_card(struct snd_soc_card *card)
{
struct snd_soc_codec *codec;
struct snd_soc_dai_link *dai_link;
int ret, i, order, dai_fmt;
mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT);
/* bind DAIs */
for (i = 0; i < card->num_links; i++) {
ret = soc_bind_dai_link(card, i);
if (ret != 0)
goto base_error;
}
/* bind aux_devs too */
for (i = 0; i < card->num_aux_devs; i++) {
ret = soc_bind_aux_dev(card, i);
if (ret != 0)
goto base_error;
}
/* initialize the register cache for each available codec */
list_for_each_entry(codec, &codec_list, list) {
if (codec->cache_init)
continue;
ret = snd_soc_init_codec_cache(codec);
if (ret < 0)
goto base_error;
}
/* card bind complete so register a sound card */
ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
card->owner, 0, &card->snd_card);
if (ret < 0) {
dev_err(card->dev,
"ASoC: can't create sound card for card %s: %d\n",
card->name, ret);
goto base_error;
}
card->dapm.bias_level = SND_SOC_BIAS_OFF;
card->dapm.dev = card->dev;
card->dapm.card = card;
list_add(&card->dapm.list, &card->dapm_list);
#ifdef CONFIG_DEBUG_FS
snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root);
#endif
#ifdef CONFIG_PM_SLEEP
/* deferred resume work */
INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
#endif
if (card->dapm_widgets)
snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
card->num_dapm_widgets);
/* initialise the sound card only once */
if (card->probe) {
ret = card->probe(card);
if (ret < 0)
goto card_probe_error;
}
/* probe all components used by DAI links on this card */
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
order++) {
for (i = 0; i < card->num_links; i++) {
ret = soc_probe_link_components(card, i, order);
if (ret < 0) {
dev_err(card->dev,
"ASoC: failed to instantiate card %d\n",
ret);
goto probe_dai_err;
}
}
}
/* probe all DAI links on this card */
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
order++) {
for (i = 0; i < card->num_links; i++) {
ret = soc_probe_link_dais(card, i, order);
if (ret < 0) {
dev_err(card->dev,
"ASoC: failed to instantiate card %d\n",
ret);
goto probe_dai_err;
}
}
}
for (i = 0; i < card->num_aux_devs; i++) {
ret = soc_probe_aux_dev(card, i);
if (ret < 0) {
dev_err(card->dev,
"ASoC: failed to add auxiliary devices %d\n",
ret);
goto probe_aux_dev_err;
}
}
snd_soc_dapm_link_dai_widgets(card);
snd_soc_dapm_connect_dai_link_widgets(card);
if (card->controls)
snd_soc_add_card_controls(card, card->controls, card->num_controls);
if (card->dapm_routes)
snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
card->num_dapm_routes);
for (i = 0; i < card->num_links; i++) {
struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
dai_link = &card->dai_link[i];
dai_fmt = dai_link->dai_fmt;
if (dai_fmt) {
struct snd_soc_dai **codec_dais = rtd->codec_dais;
int j;
for (j = 0; j < rtd->num_codecs; j++) {
struct snd_soc_dai *codec_dai = codec_dais[j];
ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);
if (ret != 0 && ret != -ENOTSUPP)
dev_warn(codec_dai->dev,
"ASoC: Failed to set DAI format: %d\n",
ret);
}
}
/* If this is a regular CPU link there will be a platform */
if (dai_fmt &&
(dai_link->platform_name || dai_link->platform_of_node)) {
ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,
dai_fmt);
if (ret != 0 && ret != -ENOTSUPP)
dev_warn(card->rtd[i].cpu_dai->dev,
"ASoC: Failed to set DAI format: %d\n",
ret);
} else if (dai_fmt) {
/* Flip the polarity for the "CPU" end */
dai_fmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
switch (dai_link->dai_fmt &
SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
break;
case SND_SOC_DAIFMT_CBM_CFS:
dai_fmt |= SND_SOC_DAIFMT_CBS_CFM;
break;
case SND_SOC_DAIFMT_CBS_CFM:
dai_fmt |= SND_SOC_DAIFMT_CBM_CFS;
break;
case SND_SOC_DAIFMT_CBS_CFS:
dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
break;
}
ret = snd_soc_dai_set_fmt(card->rtd[i].cpu_dai,
dai_fmt);
if (ret != 0 && ret != -ENOTSUPP)
dev_warn(card->rtd[i].cpu_dai->dev,
"ASoC: Failed to set DAI format: %d\n",
ret);
}
}
snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),
"%s", card->name);
snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),
"%s", card->long_name ? card->long_name : card->name);
snprintf(card->snd_card->driver, sizeof(card->snd_card->driver),
"%s", card->driver_name ? card->driver_name : card->name);
for (i = 0; i < ARRAY_SIZE(card->snd_card->driver); i++) {
switch (card->snd_card->driver[i]) {
case '_':
case '-':
case '\0':
break;
default:
if (!isalnum(card->snd_card->driver[i]))
card->snd_card->driver[i] = '_';
break;
}
}
if (card->late_probe) {
ret = card->late_probe(card);
if (ret < 0) {
dev_err(card->dev, "ASoC: %s late_probe() failed: %d\n",
card->name, ret);
goto probe_aux_dev_err;
}
}
if (card->fully_routed)
snd_soc_dapm_auto_nc_pins(card);
snd_soc_dapm_new_widgets(card);
ret = snd_card_register(card->snd_card);
if (ret < 0) {
dev_err(card->dev, "ASoC: failed to register soundcard %d\n",
ret);
goto probe_aux_dev_err;
}
#ifdef CONFIG_SND_SOC_AC97_BUS
/* register any AC97 codecs */
for (i = 0; i < card->num_rtd; i++) {
ret = soc_register_ac97_dai_link(&card->rtd[i]);
if (ret < 0) {
dev_err(card->dev,
"ASoC: failed to register AC97: %d\n", ret);
while (--i >= 0)
soc_unregister_ac97_dai_link(&card->rtd[i]);
goto probe_aux_dev_err;
}
}
#endif
card->instantiated = 1;
snd_soc_dapm_sync(&card->dapm);
mutex_unlock(&card->mutex);
return 0;
probe_aux_dev_err:
for (i = 0; i < card->num_aux_devs; i++)
soc_remove_aux_dev(card, i);
probe_dai_err:
soc_remove_dai_links(card);
card_probe_error:
if (card->remove)
card->remove(card);
snd_card_free(card->snd_card);
base_error:
mutex_unlock(&card->mutex);
return ret;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
This function is analyzed below.
5.2.4.1,soc_bind_dai_link(card, i)
ASoC defines three global list header variables: codec_list, dai_list and platform_list. All codeec, DAI and Platform in the system are connected to these three global lists at the time of registration. The soc_bind_dai_link function scans the three linked lists one by one and matches them according to the names in card-> dai_link []. After matching, the corresponding codec, Dai and platform instances are assigned to card-> RTD [] (snd_soc_pcm_runtime). After this process, snd_soc_pcm_runtime: (card - > rtd) saves the information of Code, DAI and Platform drivers used in this Machine.
/* bind DAIs */
for (i = 0; i < card->num_links; i++) {
ret = soc_bind_dai_link(card, i);//Suppose card - > num_links =23,therefore for The loop will execute23second soc_bind_dai_link Function, and through i Change brings all mt_soc_dai_common Medium dai_link Traversed.
if (ret != 0)
goto base_error;
}
static int soc_bind_dai_link(struct snd_soc_card *card, int num)
{
/*
struct snd_soc_pcm_runtime {// Yes23Five such data structure objects, listing important members
…
struct snd_soc_dai_link *dai_link;//Refers to struct snd_soc_dai_link mt_soc_dai_common[i](i=0~22)
struct snd_soc_codec *codec;// rtd->codec = codec; from codec_list
struct snd_soc_platform *platform;// rtd->platform = platform; from platform_list
struct snd_soc_dai *codec_dai;// rtd->codec_dai = codec_dai; from dai_list
struct snd_soc_dai *cpu_dai;// rtd->cpu_dai = cpu_dai; from dai_list
…
}
*/
struct snd_soc_dai_link *dai_link = &card->dai_link[num];
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
struct snd_soc_dai_link_component *codecs = dai_link->codecs;
struct snd_soc_dai_link_component cpu_dai_component;
struct snd_soc_dai **codec_dais = rtd->codec_dais;
struct snd_soc_platform *platform;
const char *platform_name;
int i;
dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num);
cpu_dai_component.name = dai_link->cpu_name;
cpu_dai_component.of_node = dai_link->cpu_of_node;
cpu_dai_component.dai_name = dai_link->cpu_dai_name;
/* snd_soc_find_dai
// Find CPU DAI from registered DAIs
list_for_each_entry(component, &component_list, list) {
if (dlc->of_node && component->dev->of_node != dlc->of_node)
continue;
if (dlc->name && strcmp(component->name, dlc->name))
continue;
list_for_each_entry(dai, &component->dai_list, list) {
if (dlc->dai_name && strcmp(dai->name, dlc->dai_name))
continue;
*/
rtd->cpu_dai = snd_soc_find_dai(&cpu_dai_component);
if (!rtd->cpu_dai) {
dev_err(card->dev, "ASoC: CPU DAI %s not registered\n",
dai_link->cpu_dai_name);
return -EPROBE_DEFER;
}
rtd->num_codecs = dai_link->num_codecs;
/* Find CODEC from registered CODECs */
for (i = 0; i < rtd->num_codecs; i++) {
codec_dais[i] = snd_soc_find_dai(&codecs[i]);
if (!codec_dais[i]) {
dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n",
codecs[i].dai_name);
return -EPROBE_DEFER;
}
}
/* Single codec links expect codec and codec_dai in runtime data */
rtd->codec_dai = codec_dais[0];
rtd->codec = rtd->codec_dai->codec;
/* if there's no platform we match on the empty platform */
platform_name = dai_link->platform_name;
if (!platform_name && !dai_link->platform_of_node)
platform_name = "snd-soc-dummy";
/* find one from the set of registered platforms */
list_for_each_entry(platform, &platform_list, list) {
if (dai_link->platform_of_node) {
if (platform->dev->of_node !=
dai_link->platform_of_node)
continue;
} else {
if (strcmp(platform->component.name, platform_name))
continue;
}
rtd->platform = platform;
}
if (!rtd->platform) {
dev_err(card->dev, "ASoC: platform %s not registered\n",
dai_link->platform_name);
return -EPROBE_DEFER;
}
card->num_rtd++;
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
So far, machine binds the required codec and platform
5.2.4.2, snd_card_new create sound card
There are many device s under the sound card, the most important of which are pcm and control, snd_card_new.
Automatically call snd_ctl_create to create control logic device (control C0)
/* 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);//Register control Device
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 is linked to snd_card through a linked list. Later we will see that when snd_card is registered, the registration function of each device hanging on it will be called. For snd_control, it is snd_ctl_dev_register.
snd_ctl_create Establish control Logic device
a)call snd_device_new,transmit snd_device_ops ops,this ops Very critical i.snd_device_new Interface handle control Logic devices are in place card->devices in
b)ops Medium snd_ctl_dev_register Interface, which will actually be the following snd_card_register Interface calls to, snd_ctl_dev_register Interface call snd_register_device,transmit snd_ctl_f_ops,this ops It's actually used. control File operation of equipment.
/*
* create control core:
* called from init.c
*/
int snd_ctl_create(struct snd_card *card)
{
static struct snd_device_ops ops = {
.dev_free = snd_ctl_dev_free,
.dev_register = snd_ctl_dev_register,
.dev_disconnect = snd_ctl_dev_disconnect,
};
if (snd_BUG_ON(!card))
return -ENXIO;
return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
}
static int snd_ctl_dev_register(struct snd_device *device)
{
struct snd_card *card = device->device_data;
int err, cardnum;
char name[16];
if (snd_BUG_ON(!card))
return -ENXIO;
cardnum = card->number;
if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))
return -ENXIO;
sprintf(name, "controlC%i", cardnum);
if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
&snd_ctl_f_ops, card, name)) < 0)
return err;
return 0;
}
/**
* snd_device_new - create an ALSA device component
* @card: the card instance
* @type: the device type, SNDRV_DEV_XXX
* @device_data: the data pointer of this device
* @ops: the operator table
*
* Creates a new device component for the given data pointer.
* The device will be assigned to the card and managed together
* by the card.
*
* The data pointer plays a role as the identifier, too, so the
* pointer address must be unique and unchanged.
*
* Return: Zero if successful, or a negative error code on failure.
*/
int snd_device_new(struct snd_card *card, enum snd_device_type type,
void *device_data, struct snd_device_ops *ops)
{
struct snd_device *dev;
struct list_head *p;
if (snd_BUG_ON(!card || !device_data || !ops))
return -ENXIO;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
dev_err(card->dev, "Cannot allocate device, type=%d\n", type);
return -ENOMEM;
}
INIT_LIST_HEAD(&dev->list);
dev->card = card;
dev->type = type;
dev->state = SNDRV_DEV_BUILD;
dev->device_data = device_data;
dev->ops = ops;
/* insert the entry in an incrementally sorted list */
list_for_each_prev(p, &card->devices) {
struct snd_device *pdev = list_entry(p, struct snd_device, list);
if ((unsigned int)pdev->type <= (unsigned int)type)
break;
}
list_add(&dev->list, p);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
5.2.4.3,snd_soc_dapm_new_controls
Create damp controls
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++) {
/*
There are many kinds of controls for selective creation
case snd_soc_dapm_regulator_supply
case snd_soc_dapm_clock_supply
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
case snd_soc_dapm_mux:
case snd_soc_dapm_dai_out:
case snd_soc_dapm_dai_in:
case snd_soc_dapm_adc:
case snd_soc_dapm_aif_out:
case snd_soc_dapm_dac:
case snd_soc_dapm_aif_in:
case snd_soc_dapm_pga:
case snd_soc_dapm_out_drv:
case snd_soc_dapm_input:
case snd_soc_dapm_output:
case snd_soc_dapm_micbias:
case snd_soc_dapm_spk:
case snd_soc_dapm_hp:
case snd_soc_dapm_mic:
case snd_soc_dapm_line:
case snd_soc_dapm_dai_link:
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
case snd_soc_dapm_clock_supply:
case snd_soc_dapm_kcontrol:
*/
w = snd_soc_dapm_new_control(dapm, widget);
if (!w) {
dev_err(dapm->dev,
"ASoC: Failed to create DAPM control %s\n",
widget->name);
ret = -ENOMEM;
break;
}
widget++;
}
mutex_unlock(&dapm->card->dapm_mutex);
return ret;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
5.2.4.4,soc_probe_link_components
soc_probe_link_components->soc_probe_codec->soc_probe_platform
/* initialise the sound card only once */
if (card->probe) {//Call the probe function of each substructure in turn
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, so the for loop executes the soc_probe_link_components function 23 times
,This function is called one by one. codec,dai and platform Driven probe function
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)First look at 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)this probe(codec)That's what I analyzed earlier..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;
}
}
//Call platform probe
/* probe the ==platform== */
if (platform->component.driver->probe_order == order) {
ret = soc_probe_component(card, &platform->component);
if (ret < 0)
return ret;
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
soc_probe_component is to add control, widgets, etc. in the probe function that calls codec and platform before.
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);//Add a new 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);//Add new 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);//Add component controls
if (component->dapm_routes)
snd_soc_dapm_add_routes(dapm, component->dapm_routes,
component->num_dapm_routes);//Add dapm path
component->probed = 1;
list_add(&dapm->list, &card->dapm_list);
/* This is a HACK and will be removed soon */
if (component->codec)
list_add(&component->codec->card_list, &card->codec_dev_list);
return 0;
err_probe:
soc_cleanup_component_debugfs(component);
module_put(component->dev->driver->owner);
return ret;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
2) Take a look at 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;
//Call 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;
}
//Call 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);//Create PCM devices
if (ret < 0) {
dev_err(card->dev, "ASoC: can't create pcm %s :%d\n",
dai_link->stream_name, ret);
return ret;
}
} else {
INIT_DELAYED_WORK(&rtd->delayed_work,
codec2codec_close_delayed_work);
/* link the DAI widgets */
ret = soc_link_dai_widgets(card, dai_link, rtd);
if (ret)
return ret;
}
}
/* add platform data for AC97 devices */
for (i = 0; i < rtd->num_codecs; i++) {
if (rtd->codec_dais[i]->driver->ac97_control)
snd_ac97_dev_add_pdata(rtd->codec_dais[i]->codec->ac97,
rtd->cpu_dai->ac97_pdata);
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
The control device was created earlier, and now soc_probe_link_dais calls soc_new_pcm to create the PCM device.
1) Set up pcm operation functions to be used in pcm native, which are used to operate audio physical devices, including machine, codec_dai, cpu_dai, platform;
2) Call snd_pcm_new() to create a PCM device, where playback and recording sub-stream instances are created;
3) Callback pcm_new() driven by platform to initialize audio dma device and allocate dma buffer memory;
/* 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);//Create an instance of 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 {
//The snd_pcm instance finally passes through its substreamSubstream->ops Direct or indirect ( pcm Intermediate Layer Function) Calls Specific Drivers
//Substream->ops =rtd->ops
//Substream->ops.open = soc_pcm_open = platform->driver->ops->open = mtk_capture_pcm_open
rtd->ops.open = soc_pcm_open;
rtd->ops.hw_params = soc_pcm_hw_params;
rtd->ops.prepare = soc_pcm_prepare;
rtd->ops.trigger = soc_pcm_trigger;
rtd->ops.hw_free = soc_pcm_hw_free;
rtd->ops.close = soc_pcm_close;
rtd->ops.pointer = soc_pcm_pointer;
rtd->ops.ioctl = soc_pcm_ioctl;
}
if (platform->driver->ops) {
rtd->ops.ack = platform->driver->ops->ack;
rtd->ops.copy = platform->driver->ops->copy;
rtd->ops.silence = platform->driver->ops->silence;
rtd->ops.page = platform->driver->ops->page;
rtd->ops.mmap = platform->driver->ops->mmap;
}
if (playback)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);
if (capture)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);
if (platform->driver->pcm_new) {
ret = platform->driver->pcm_new(rtd);
if (ret < 0) {
dev_err(platform->dev,
"ASoC: pcm constructor failed: %d\n",
ret);
return ret;
}
}
pcm->private_free = platform->driver->pcm_free;
out:
dev_info(rtd->card->dev, "%s <-> %s mapping ok\n",
(rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name,
cpu_dai->name);
return ret;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
snd_pcm_new
_snd_pcm_new
snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)
snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)
snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)
rtd->ops.open = soc_pcm_open;
First look at _snd_pcm_new
ALSA has implemented a powerful PCM mid-tier for us. In our driver, we only need to implement some underlying functions that need to access the hardware. A PCM instance consists of a playback stream and a capture stream, which are composed of one or more substreams, respectively.
static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
int playback_count, int capture_count, bool internal,
struct snd_pcm **rpcm)
{
struct snd_pcm *pcm;
int err;
static struct snd_device_ops ops = {
.dev_free = snd_pcm_dev_free,
.dev_register = snd_pcm_dev_register,
.dev_disconnect = snd_pcm_dev_disconnect,
};
if (snd_BUG_ON(!card))
return -ENXIO;
if (rpcm)
*rpcm = NULL;
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
if (pcm == NULL) {
dev_err(card->dev, "Cannot allocate PCM\n");
return -ENOMEM;
}
pcm->card = card;
pcm->device = device;
pcm->internal = internal;
if (id)
strlcpy(pcm->id, id, sizeof(pcm->id));
if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {
snd_pcm_free(pcm);
return err;
}
if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) {
snd_pcm_free(pcm);
return err;
}
mutex_init(&pcm->open_mutex);
init_waitqueue_head(&pcm->open_wait);
if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) {
snd_pcm_free(pcm);
return err;
}
if (rpcm)
*rpcm = pcm;
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
static int snd_pcm_dev_register(struct snd_device *device)
{
int cidx, err;
struct snd_pcm_substream *substream;
struct snd_pcm_notify *notify;
char str[16];
struct snd_pcm *pcm;
struct device *dev;
if (snd_BUG_ON(!device || !device->device_data))
return -ENXIO;
pcm = device->device_data;
mutex_lock(®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;
}
//The end p and c of pcmC%iD%ip pcmC%iD%ic represent playback and recording respectively. You can see all sound card devices under 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);//Register pcm devices
/*
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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
From some APIs of the PCM in the HAL layer, the sound card device that operates the kernel can be invoked.
5.2.4.5,snd_soc_dapm_link_dai_widgets(card)
By default, the driver does not actively define the connection between dai widget and stream widget by snd_soc_route. In fact, the connection between them is assumed by ASoc. In the initialization function of sound card, snd_soc_dapm_link_dai_widgets function is used to establish the connection between them. Then, all widgets in sound card are traversed from scratch again to find out the energy. The first prerequisite for a stream widget connected to a dai widget is that the two widgets must be in the same dapm context
snd_soc_dapm_link_dai_widgets(card);//Establish the connection between dai widget and stream widget
snd_soc_dapm_connect_dai_link_widgets(card);
//Let's analyze the snd_soc_dapm_link_dai_widgets function again and see how it connects the two widgets. First, it traverses all the widgets in the sound card and finds out the widgets of snd_soc_dapm_dai_in and snd_soc_dapm_dai_out. Through the priv field of the widget, the snd_soc_dai structure pointer corresponding to the widget is extracted.
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;
//Let's analyze the snd_soc_dapm_link_dai_widgets function again and see how it connects the two widgets. First, it traverses all the widgets in the sound card and finds out the widgets of snd_soc_dapm_dai_in and snd_soc_dapm_dai_out. Through the priv field of the widget, the snd_soc_dai structure pointer corresponding to the widget is extracted.
/* 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;
//Next, traverse all the widgets in the sound card from scratch again to find the stream widget s that can be connected to the dai widget. The first prerequisite is that the two widgets must be in the same 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 widgets are not connected to Dai widgets, so skip them
switch (w->id) {
case snd_soc_dapm_dai_in:
case snd_soc_dapm_dai_out:
continue;
default:
break;
}
//The name of the dai widget does not appear in the stream name of the widget to be connected, skip the widget
if (!w->sname || !strstr(w->sname, dai_w->name))
continue;
if (dai_w->id == snd_soc_dapm_dai_in) {
src = dai_w;
sink = w;
} else {
src = w;
sink = dai_w;
}
dev_dbg(dai->dev, "%s -> %s\n", src->name, sink->name);
snd_soc_dapm_add_path(w->dapm, src, sink, NULL, NULL);
}
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
Let's see below. 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);
}
}
//If the stream name of the widget contains the stream name of the dai, the match succeeds and the widgets are connected
static void dapm_connect_dai_link_widgets(struct snd_soc_card *card,
struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dapm_widget *sink, *source;
int i;
for (i = 0; i < rtd->num_codecs; i++) {
struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
/* there is no point in connecting BE DAI links with dummies */
if (snd_soc_dai_is_dummy(codec_dai) ||
snd_soc_dai_is_dummy(cpu_dai))
continue;
/* connect BE DAI playback if widgets are valid */
if (codec_dai->playback_widget && cpu_dai->playback_widget) {
source = cpu_dai->playback_widget;
sink = codec_dai->playback_widget;
dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n",
cpu_dai->component->name, source->name,
codec_dai->component->name, sink->name);
snd_soc_dapm_add_path(&card->dapm, source, sink,
NULL, NULL);
}
/* connect BE DAI capture if widgets are valid */
if (codec_dai->capture_widget && cpu_dai->capture_widget) {
source = codec_dai->capture_widget;
sink = cpu_dai->capture_widget;
dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n",
codec_dai->component->name, source->name,
cpu_dai->component->name, sink->name);
snd_soc_dapm_add_path(&card->dapm, source, sink,
NULL, NULL);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
Thus, the dai widget and stream widget are matched by stream name, so when we define the stream widget of codec,
Their stream name must contain the stream name of the dai so that ASoc can automatically connect the two widget s together, only by connecting them.
Together, events such as playback, recording and stopping in ASoc can be transmitted to codec through dai widget, which enables widgets in codec to be based on the current situation.
Play state, dynamically turn on or off the power of all widget s on the audio path.
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));//Instantiate the created control into the sound card
if (err < 0) {
dev_err(dev, "ASoC: Failed to add %s: %d\n",
control->name, err);
return err;
}
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
5.2.4.7,snd_soc_dapm_add_routes
Register machine level path connection information
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;
}
//This function is just a loop, calling snd_soc_dapm_add_route to the array passed in by the parameter in turn. The main work is done by snd_soc_dapm_add_route. Let's go into the snd_soc_dapm_add_route function and see:
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;
}
}
//The above code compares widgets by their names, traverses the widgets list of sound cards, and finds out the pointers to the source widgets and the destination widgets.
//If it is not found in this dapm context, use the widget s found in other dapm context
/* 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 Function is the key in the whole call chain
*/
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);
//At the beginning of the function, a snd_soc_path structure is allocated for this connection. The source and sink fields of the path point to the source widget and the destination, respectively.
widget,connected Field save connected Callback function, initialize several snd_soc_path Several linked lists in the structure.
/* 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;
}
//This code is used to determine whether there is an external connection relationship, and if so, to position the widget's ext field. Judgment can be easily seen from the code:
//The destination widget is an input foot. If the source widget is mic, line, micbias or output, the destination widget is considered to have an external connection.
//The source widget is an output foot. If the destination widget is spk, hp, line or input, the source widget is considered to have an external connection relationship.
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;
}
//The source widget and the destination widget are added to the dapm_dirty list because of the addition of join relationships. If there is no kcontrol to control the connection,
//Then this is a static connection, which is connected directly by path. Then look down:
/* 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;
//Judging by the purpose widget, if you belong to the above types, just connect them directly. This feeling is a little redundant, because usually this is the case.
//Some types of widget s don't have kcontrol, so just use a piece of code. Maybe the authors of dapm think that they might be expanded in the future.
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;
//If the widget is of mixer and MUX type, the widget is connected by dapm_connect_mixer and dapm_connect_mux functions, respectively.
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 and spk widget s belong to external devices and are simply connected together, but the connect field defaults to an unconnected state.
return 0;
err:
kfree(path);
return ret;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
So far, we have created and initialized the required widgets for the sound card. Each widget is connected together through the path. Next, the dapm waits for the user's instructions. Once a dapm kcontrol is changed by user space, using these connections, the dapm will recreate the audio path, the widget that is separated from the audio path will be powered down, and the widget that joins the audio path will be uploaded. Electricity, all up and down electric actions will be automatically completed, user space applications do not need to pay attention to these changes, it is only necessary to change a dapm kcontrol.
5.2.4.8,snd_card_register(card->snd_card)
Registered sound card, at this stage will traverse all logical devices under the sound card, and call the registration callback function of each device. For pcm, the snd_pcm_dev_register function mentioned above establishes the device file nodes used to communicate with user space application (alsa-lib): / dev/snd/pcmCxDxxxxp and / dev/snd/pcmCxDxxxxc.
/**
* 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;
//Sound card classes will appear below / sys/class/sound / in the file system, and device_add (& card - > card_dev) also determines that the corresponding device nodes will appear below / 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)//By registering all logical devices under the sound card with snd_device_register_all(), snd_device_register_all() actually traverses all snd_devices through the devices list of snd_card, and calls Ops - > dev_register () of snd_device to register their devices.
return err;
mutex_lock(&snd_card_mutex);
if (snd_cards[card->number]) {
/* already registered */
mutex_unlock(&snd_card_mutex);
return 0;
}
if (*card->id) {
/* make a unique id name from the given string */
char tmpid[sizeof(card->id)];
memcpy(tmpid, card->id, sizeof(card->id));
snd_card_set_id_no_lock(card, tmpid, tmpid);
} else {
/* create an id from either shortname or longname */
const char *src;
src = *card->shortname ? card->shortname : card->longname;
snd_card_set_id_no_lock(card, src,
retrieve_id_from_card_name(src));
}
snd_cards[card->number] = card;
mutex_unlock(&snd_card_mutex);
init_info_for_card(card);
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
if (snd_mixer_oss_notify_callback)
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
#endif
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
So far, the whole ALSA Driver has been analyzed. The flow chart is used to summarize the overall invocation process of the alsa driver.