es8388 驱动详解
详细介绍codec的各个部件及功能,及他们与驱动概念的对应关系。进而讲述应用层命令amixer与驱动的对应关系。
音频的基本框架
分为三个部分:
1) 整体的绿框,为machine,例如你的PC等,负责图中 “线”的部分的关联,及黄色连接器的管理。也就是说不属于 paltform和codec管理的部分,都划归machine管理。
对应的驱动文件有: sound\soc\generic\simple-card.c
2) paltform,例如基于intel 或者arm等平台的soc,提供数据传输的通道I2S及配置通道I2C等。
3) codec 芯片,负责A/D ,D/A的转换及控制。
es8388 即为codec
ES8388驱动
对于所有的驱动,我们先理解其硬件结构,再去阅读代码,就很容易理解。
es8388的框图
为什么我们要说框图,框图是驱动编写的一个基础。
由此框图,我们主要了解音频数据输入输出的路径,进而了解后续将要介绍的概念。
1) LIN1 LIN2 RIN1 RIN2作为输入端,其对外与mic 等输入设备的连接器连接。
对内首先经过
a. mux 多路选择 (也就是选择 LIN1 LIN2 LIN1-RIN1 LIN2-RIN2,四选一)即输入的左声道支持两路(LIN1 LIN2)分别作为一个输入源;或者LIN1-RIN1 ,LIN2-RIN2作为立体输入源。这个需要硬件连线配合。
b. 接下来经过mic amp ,即前置放大器。将采集的声音(当然也包括噪音)进行放大处理。
c-1. mux 多路选择到ADC,完成模数转换,进而到serial audio data DSDIN-->i2s-->soc
c-2.或者经过此mux,输出为LIN作为mixL --》OUT,也就是耳机,此路径为循环测试用,即音频输入直接到音频输出,不经过ADC--DAC等。
2)输出的路径则比较简单
直接从ASDOUT -->DAC-->OUT
widget
根据第一章的描述,linux驱动分为三大块,而每块都有自己的widget管理,例如machine的驱动simple-card管理自己的widget(主要音频输入输出的连接器),而8388 作为codec则主要框图中提及的各个部件。
下面结合代码理解,针对如下代码,
1)前面四行 widget 和图上对应比较明确。
2)left PGA Mux 为框图中INPUT连接的第一个mux
3)mic bias 偏置电压,在框图中未画出,对是否可以录音也有影响。
4) differential mux 差分选择,图中也未画出,在输入为LIN1-RIN1 or LIN2-RIN2时才会启用。
5)ADC mux,即选择哪路输入到ADC
6) ADC
static const struct snd_soc_dapm_widget es8323_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("LINPUT1"),
SND_SOC_DAPM_INPUT("LINPUT2"),
SND_SOC_DAPM_INPUT("RINPUT1"),
SND_SOC_DAPM_INPUT("RINPUT2"),
SND_SOC_DAPM_MUX("Left PGA Mux", SND_SOC_NOPM, 0, 0,
&es8323_left_dac_mux_controls),
SND_SOC_DAPM_MUX("Right PGA Mux", SND_SOC_NOPM, 0, 0,
&es8323_right_dac_mux_controls),
SND_SOC_DAPM_MICBIAS("Mic Bias", ES8323_ADCPOWER, 3, 1),
SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
&es8323_diffmux_controls),
SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
&es8323_monomux_controls),
SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
&es8323_monomux_controls),
SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
&es8323_left_line_controls),
SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
&es8323_right_line_controls),
SND_SOC_DAPM_ADC("Right ADC", "Right Capture", ES8323_ADCPOWER, 4, 1),
SND_SOC_DAPM_ADC("Left ADC", "Left Capture", ES8323_ADCPOWER, 5, 1),
/* gModify.Cmmt Implement when suspend/startup */
SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8323_DACPOWER, 6, 1),
SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8323_DACPOWER, 7, 1),
SND_SOC_DAPM_AIF_OUT("I2S OUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("I2S IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
&es8323_left_mixer_controls[0],
ARRAY_SIZE(es8323_left_mixer_controls)),
SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
&es8323_right_mixer_controls[0],
ARRAY_SIZE(es8323_right_mixer_controls)),
SND_SOC_DAPM_PGA("Right ADC Power", ES8323_ADCPOWER, 6, 1, NULL, 0),
SND_SOC_DAPM_PGA("Left ADC Power", ES8323_ADCPOWER, 7, 1, NULL, 0),
SND_SOC_DAPM_PGA("Right Out 2", ES8323_DACPOWER, 2, 0, NULL, 0),
SND_SOC_DAPM_PGA("Left Out 2", ES8323_DACPOWER, 3, 0, NULL, 0),
SND_SOC_DAPM_PGA("Right Out 1", ES8323_DACPOWER, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA("Left Out 1", ES8323_DACPOWER, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA("LAMP", ES8323_ADCCONTROL1, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA("RAMP", ES8323_ADCCONTROL1, 0, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("LOUT1"),
SND_SOC_DAPM_OUTPUT("ROUT1"),
SND_SOC_DAPM_OUTPUT("LOUT2"),
SND_SOC_DAPM_OUTPUT("ROUT2"),
SND_SOC_DAPM_OUTPUT("VREF"),
};
通过上述代码,我们可以到widget和controls关联或者和寄存器的某些位关联,进而可以实现某个小器件的上下电等控制
kcontrols
/* Left Mixer */
static const struct snd_kcontrol_new es8323_left_mixer_controls[] = {
SOC_DAPM_SINGLE("Left Playback Switch", ES8323_DACCONTROL17, 7, 1, 0),
SOC_DAPM_SINGLE("Left Bypass Switch", ES8323_DACCONTROL17, 6, 1, 0),
};
/* Right Mixer */
static const struct snd_kcontrol_new es8323_right_mixer_controls[] = {
SOC_DAPM_SINGLE("Right Playback Switch", ES8323_DACCONTROL20, 7, 1, 0),
SOC_DAPM_SINGLE("Right Bypass Switch", ES8323_DACCONTROL20, 6, 1, 0),
};
即对 widget的控制
例如上面right mixer controls,
路由
其实就是决定了一个音频的流向,source和sink均为一个widget。例如下面代码,
LINPUT1作为source,由Line 1L 进行控制,最终流向了sink 即Left PGA mux,也就是框图中的左侧第一个mux。
根据下面的代码,一个路由如下:
LINPUT1-->Left PGA Mux-->Left ADC mux-->left ADC POWER-->LEFT ADC-->i2s OUT
static const struct snd_soc_dapm_route audio_map[] = {
{"Left PGA Mux", "Line 1L", "LINPUT1"},
{"Left PGA Mux", "Line 2L", "LINPUT2"},
{"Left PGA Mux", "DifferentialL", "Differential Mux"},
{"Right PGA Mux", "Line 1R", "RINPUT1"},
{"Right PGA Mux", "Line 2R", "RINPUT2"},
{"Right PGA Mux", "DifferentialR", "Differential Mux"},
{"Differential Mux", "Line 1", "LINPUT1"},
{"Differential Mux", "Line 1", "RINPUT1"},
{"Differential Mux", "Line 2", "LINPUT2"},
{"Differential Mux", "Line 2", "RINPUT2"},
{"Left ADC Mux", "Stereo", "Right PGA Mux"},
{"Left ADC Mux", "Stereo", "Left PGA Mux"},
{"Left ADC Mux", "Mono (Left)", "Left PGA Mux"},
{"Right ADC Mux", "Stereo", "Left PGA Mux"},
{"Right ADC Mux", "Stereo", "Right PGA Mux"},
{"Right ADC Mux", "Mono (Right)", "Right PGA Mux"},
{"Left ADC Power", NULL, "Left ADC Mux"},
{"Right ADC Power", NULL, "Right ADC Mux"},
{"Left ADC", NULL, "Left ADC Power"},
{"Right ADC", NULL, "Right ADC Power"},
{"I2S OUT", NULL, "Left ADC"},
{"I2S OUT", NULL, "Right ADC"},
{"Left Line Mux", "Line 1L", "LINPUT1"},
{"Left Line Mux", "Line 2L", "LINPUT2"},
{"Left Line Mux", "MicL", "Left PGA Mux"},
{"Right Line Mux", "Line 1R", "RINPUT1"},
{"Right Line Mux", "Line 2R", "RINPUT2"},
{"Right Line Mux", "MicR", "Right PGA Mux"},
{"Right DAC", NULL, "I2S IN"},
{"Left DAC", NULL, "I2S IN"},
{"Left Mixer", "Left Playback Switch", "Left DAC"},
{"Left Mixer", "Left Bypass Switch", "Left Line Mux"},
{"Right Mixer", "Right Playback Switch", "Right DAC"},
{"Right Mixer", "Right Bypass Switch", "Right Line Mux"},
{"Left Out 1", NULL, "Left Mixer"},
{"LOUT1", NULL, "Left Out 1"},
{"Right Out 1", NULL, "Right Mixer"},
{"ROUT1", NULL, "Right Out 1"},
{"Left Out 2", NULL, "Left Mixer"},
{"LOUT2", NULL, "Left Out 2"},
{"Right Out 2", NULL, "Right Mixer"},
{"ROUT2", NULL, "Right Out 2"},
};
那么中间一列的control,例如第一行代码的LINE 1L 等又是什么呢?
通过上面widget, kcontrol,route的分析,我们知晓:
当需要打通一条路,需要知晓这条路上涉及的widget,这就是route的功能;
而 对这些widget的控制则需要kcontrols对寄存器的配置。
至此,codec涉及到的widget及作用我们基本了解,但是codec与machine及soc连接的widget,我们还没有分析,后续以具体machine驱动再进行分析。
amixer命令和驱动的对应关系
通过命令amixer -c X controls 这里X表示card编号,一定注意此参数,否则查出来不对。
然后回头再看代码中:
通过代码与命令的输出对比,我们发现两者一致,进而就可以通过寄存器手册了解其功能。
通过命令amixer -c 2 contents 查看每个控制的具体配置值,例如如下面PGA Mux默认的输入是line 1L
numid=26,iface=MIXER,name='Left PGA Mux'
; type=ENUMERATED,access=rw------,values=1,items=3
; Item #0 'Line 1L'
; Item #1 'Line 2L'
; Item #2 'DifferentialL'
: values=0
numid=27,iface=MIXER,name='Right PGA Mux'
; type=ENUMERATED,access=rw------,values=1,items=3
; Item #0 'Line 1R'
; Item #1 'Line 2R'
; Item #2 'DifferentialR'
: values=0
选项和路由的配置一致
此时就可以知晓通过此两个controls可以对输入源进行控制,例如将输入改成LINE 2L。
amixer -c 2 cset numid=26,iface=MIXER,name='Left PGA Mux' 1
numid=26,iface=MIXER,name='Left PGA Mux'
; type=ENUMERATED,access=rw------,values=1,items=3
; Item #0 'Line 1L'
; Item #1 'Line 2L'
; Item #2 'DifferentialL'
: values=1
系统下查看widget状态的命令
cat /sys/devices/platform/es8388-sound/
综述
通过上面的驱动代码与命令的分析,可以得知:
amixer 与驱动密切相关,amixer查询到的即为驱动提供的路由、control及widget信息。通过这些信息可以完成对音频输入输出及流向的控制。
至此,我们对codec驱动有了比较清晰的认识,但是耳机、mic插座与codec的连接路由关系如何管理?何时对widget进行上下电管理?对于两个耳机、mic的连接器,是分别输出输入还是作为立体输出输入,这些由什么决定的?
对于这些问题,在后续machine驱动进行介绍。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)