音频的基本框架

  

      分为三个部分:

      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驱动进行介绍。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐