一、audiocodec
audiocodec是具有数模转换功能的内置模块,可将音频数字信号转换成模拟信号发送出去,通常接耳机、扬声器等
1、音频流通路
播放流:
Playback ──> DACL ──> LINEOUTL ──> SPK
Playback ──> DACR ──> LINEOUTR ──> SPK
录音流:
MIC1 ──> ADC1 ──> Capture
MIC2 ──> ADC2 ──> Capture
MIC3 ──> ADC3 ──> Capture
AudioCodec 模块由 3 个设备树节点构建
(1)ASoC 层 codec: codec
(2)ASoC 层 platform: codec_plat
(3)ASoC 层 machine: codec_mach
2、设备树
/* audio dirver module -> audio codec */
&codec {
tx-hub-en;
rx-sync-en;
dac-vol = <63>; /* default value:63 range:0->63 */
dacl-vol = <160>; /* default value:160 range:0->255 */
dacr-vol = <160>; /* default value:160 range:0->255 */
adc1-vol = <160>; /* default value:160 range:0->255 */
adc2-vol = <160>; /* default value:160 range:0->255 */
adc3-vol = <160>; /* default value:160 range:0->255 */
lineout-gain = <31>; /* default value:31 range:0->31 */
hpout-gain = <7>; /* default value:7 range:0->7 */
adc1-gain = <31>; /* default value:31 range:0->31 */
adc2-gain = <31>; /* default value:31 range:0->31 */
adc3-gain = <31>; /* default value:31 range:0->31 */
/* to do: avcc-1.8 vdd33-3.3 cpvin-1.8 */
avcc-external;
avcc-supply = <®_aldo4>;
avcc-vol = <1800000>;
vdd-external;
vdd-supply = <®_cldo3>;
vdd-vol = <3300000>;
cpvin-external;
cpvin-supply = <®_bldo3>;
cpvin-vol = <1800000>;
pa-pin-max = <1>;
pa-pin-0 = <&r_pio PL 7 GPIO_ACTIVE_HIGH>;
pa-pin-level-0 = <1>;
pa-pin-msleep-0 = <0>;
jack-det-level = <0>;
jack-det-threshold = <8>;
jack-det-debouce-time = <250>;
/* extcon = <&usb_power_supply>;
* jack-swpin-mic-sel = <&pio PH 8 GPIO_ACTIVE_HIGH>;
* jack-swpin-hp-en = <&pio PH 15 GPIO_ACTIVE_HIGH>;
* jack-swpin-hp-sel = <&pio PH 11 GPIO_ACTIVE_HIGH>;
* jack-swmode-hp-off = <0x00>;
* jack-swmode-hp-usb = <0x11>;
* jack-swmode-hp-audio = <0x10>;
* jack-det-level = <1>;
* jack-det-threshold = <8>;
* jack-det-debouce-time = <250>;
*/
status = "okay";
};
&codec_plat {
status = "okay";
};
&codec_mach {
soundcard-mach,jack-support = <1>;
status = "okay";
soundcard-mach,cpu {
sound-dai = <&codec_plat>;
};
soundcard-mach,codec {
sound-dai = <&codec>;
};
};
3、驱动
bsp/drivers/sound/platform/snd_sunxi_aaudio.c:21:
#define DRV_NAME "sunxi-snd-plat-aaudio"
bsp/drivers/sound/platform/snd_sunxi_mach.c:78:static int asoc_simple_hw_params
mach 的时钟设置
static int asoc_simple_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
switch (params_rate(params)) {
case 8000:
case 12000:
case 16000:
case 24000:
case 32000:
case 48000:
case 64000:
case 96000:
case 192000:
freq_point = 24576000;
bsp/drivers/sound/platform/snd_sun55iw3_codec.c:695: SOC_ENUM_EXT("ADC HPF0 Mode", sunxi_adchpf0_sta_enum, sunxi_codec_get_dap_status,
时钟设置 sunxi_codec_dai_set_pll
static int snd_sunxi_clk_rate(struct sunxi_codec_clk *clk, int stream,
unsigned int freq_in, unsigned int freq_out)
{
SND_LOG_DEBUG("\n");
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
if (freq_in % 24576000 == 0) {
/* If you want to use clk_pll_audio0_4x, must set it 1083801600Hz */
if (clk_set_parent(clk->clk_audio_dac, clk->clk_pll_audio0_4x)) {
SND_LOG_ERR("set dac parent clk failed\n");
return -EINVAL;
}
if (clk_set_rate(clk->clk_pll_audio0_4x, 1083801600)) {
SND_LOG_ERR("set clk_pll_audio0_4x rate failed\n");
return -EINVAL;
}
} else {
if (clk_set_parent(clk->clk_audio_dac, clk->clk_pll_audio1_div5)) {
SND_LOG_ERR("set dac parent clk failed\n");
return -EINVAL;
}
}
if (clk_set_rate(clk->clk_audio_dac, freq_out)) {
SND_LOG_ERR("set clk_audio_dac rate failed, rate: %u\n", freq_out);
return -EINVAL;
}
}
}
static int sunxi_codec_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
/* Set rate */
for (i = 0; i < ARRAY_SIZE(sunxi_sample_rate_conv); i++) {
if (sunxi_sample_rate_conv[i].samplerate == params_rate(params)) {
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
regmap_update_bits(regmap, SUNXI_DAC_FIFO_CTL, 0x7 << DAC_FS,
sunxi_sample_rate_conv[i].rate_bit << DAC_FS);
} else {
if (sunxi_sample_rate_conv[i].samplerate > 48000)
return -EINVAL;
regmap_update_bits(regmap, SUNXI_ADC_FIFO_CTL, 0x7 << ADC_FS,
sunxi_sample_rate_conv[i].rate_bit << ADC_FS);
}
}
}
}
static const struct snd_soc_dai_ops sunxi_codec_dai_ops = {
.startup = sunxi_codec_dai_startup,
.hw_params = sunxi_codec_dai_hw_params,
.set_pll = sunxi_codec_dai_set_pll,
.prepare = sunxi_codec_dai_prepare,
.trigger = sunxi_codec_dai_trigger,
.shutdown = sunxi_codec_dai_shutdown,
};
二、AUDIO 音频测试
音频流通路:
播放流:
Playback ──> DACL ──> LINEOUTL ──> SPK
Playback ──> DACR ──> LINEOUTR ──> SPK
录音流:
MIC1 ──> ADC1 ──> Capture
MIC2 ──> ADC2 ──> Capture
MIC3 ──> ADC3 ──> Capture
音频设备:
tinymix查看信息
如何使用 tinymix 更改设置
要更改某个控制项的值,可以使用以下命令格式:
tinymix [control id] [value to set]
例如,如果你想把 MIC1 Switch 打开,可以执行:
tinymix 23 1
同理,如果你想调整音量或增益,可以使用类似的方法:
tinymix 12 100 # 设置 DAC Volume 为 100
tinymix 18 40 # 设置 LINEOUT Gain 为 40
1.播放音乐
1.1 aplay 播放wav格式音乐
#打开SPK switch
tinymix 29 1
#默认声卡播放
aplay /usr/share/alsa/syfs-chenhuilin.wav
1.2 ffplay 播放MP3格式音乐
ffplay -nodisp /usr/share/alsa/wuyeutian-xingkong.mp3
1.3 music 音量控制
#amixer 控制music音量,范围0~255, 255声音最大
amixer set 'PCM music volume' 90
1.4 FM 音量控制
#x为音量值,范围 0~100
#ep: 设置音量为50
tinymix -D 4 'DSP2_FM_FaderLR02 Volume' 50
#查看当前音量值
tinymix -D 4 'DSP2_FM_FaderLR02 Volume'
2 、录音
#alsa指定声卡录音, dsnoop 录音,可多路同时录音, 录制为wav格式
2.1 打开 MIC 输入
tinymix -D 0 23 1
tinymix -D 0 24 1
tinymix -D 0 25 1
#ADC HPF0 Mode
tinymix -D 0 5 1
#ADC1 Volume
tinymix -D 0 15 150
arecord -r 44100 -f S16_LE -c2 /mnt/UDISK/record.wav
arecord -r 44100 -f S16_LE -c2 /mnt/UDISK/record.wav
2.2 播放录音
aplay /media/record.wav
注意录音时双声道, 播放时接左右声道的喇叭应该会同时出声!! 否则可能是mic或功放工作异常。
MIC录音无声音,录音时测量MBIAS电压为1.8V左右,正常
查看原理图,MIC经过了FM1388模块,要先调试FM1388。如果是直连到CPU,录音功能测试正常。FM1388模块调试内容后续更新。