Linux音频调试示例
Posted xxccry
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux音频调试示例相关的知识,希望对你有一定的参考价值。
Linux音频调试示例
硬件设计
cpu | codec | peripheral interface |
---|---|---|
nvidia-jetson | max9867 | line-in×1, hp×1 |
codec与cpu之间通过i2c读写寄存器,i2s传输音频信号(i2s总线简介)。
cpu作为主设备,codec作为从设备,cpu提供主时钟(aud_mclk)。
codec连接1路line-in,1路headphone。
设备树
因为cpu是nvidia jetson系列,所以基于Nvidia官方释放的“L4T Sources”(Jetson下载中心)修改。
在i2c节点添加设备
/
i2c@c250000 // 硬件连通的i2c通道
status = "okay";
max9867: codec@18
compatible = "maxim,max9867"; // 与官方驱动匹配
reg = <0x18>; // i2c地址
;
;
;
使能i2s总线
// 在tegraxx_soc_audio.dtsi中查找
/
aconnect@2a41000
ahub
i2s@2901300 // 硬件连通的i2s通道
status = "okay";
;
;
;
;
添加dai_link
/
sound
status = "okay";
compatible = "nvidia,tegra-audio-t186ref-mobile"; // 修改,与rt565x解绑
nvidia,model = "tegra-snd-t19x-mobile"; // 修改,与rt565x解绑
nvidia,audio-routing = // 需要与codec驱动匹配,从右到左,代表数据流向
// x是前缀,与dai-link的name-prefix匹配
"x LINE_IN", "x Linein",
"x Headphone", "x HPOUT"
nvidia,dai-link-1 // dai-link,与codec和i2s匹配
// codec-dai-name与codec驱动匹配
link-name = "max9867";
cpu-dai = <&tegra_i2s1>;
codec-dai = <&max9867>;
cpu-dai-name = "I2S1";
codec-dai-name = "max9867-aif1";
name-prefix = "x";
;
// 只用到一路,其余nvidia,dai-link设置“disabled”
;
;
// max9867.c
static struct snd_soc_dai_driver max9867_dai[] =
.name = "max9867-aif1", // 匹配nvidia,dai-link中的codec-dai-name
... ...
;
修改pinmux
/
pinmux@2430000
pinmux_default: common
// audio mclk pin
aud_mclk_ps4
nvidia,pins = "aud_mclk_ps4";
nvidia,function = "aud";
nvidia,pull = <TEGRA_PIN_PULL_NONE>;
nvidia,tristate = <TEGRA_PIN_DISABLE>;
nvidia,enable-input = <TEGRA_PIN_DISABLE>;
nvidia,lpdr = <TEGRA_PIN_DISABLE>;
;
// i2s pins
dap5_sclk_pt5
nvidia,pins = "dap5_sclk_pt5";
nvidia,function = "rsvd2";
nvidia,pull = <TEGRA_PIN_PULL_NONE>;
nvidia,tristate = <TEGRA_PIN_DISABLE>;
nvidia,enable-input = <TEGRA_PIN_DISABLE>;
nvidia,lpdr = <TEGRA_PIN_DISABLE>;
;
dap5_dout_pt6
nvidia,pins = "dap5_dout_pt6";
nvidia,function = "rsvd2";
nvidia,pull = <TEGRA_PIN_PULL_NONE>;
nvidia,tristate = <TEGRA_PIN_DISABLE>;
nvidia,enable-input = <TEGRA_PIN_DISABLE>;
nvidia,lpdr = <TEGRA_PIN_DISABLE>;
;
dap5_din_pt7
nvidia,pins = "dap5_din_pt7";
nvidia,function = "rsvd3";
nvidia,pull = <TEGRA_PIN_PULL_DOWN>;
nvidia,tristate = <TEGRA_PIN_ENABLE>;
nvidia,enable-input = <TEGRA_PIN_ENABLE>;
nvidia,lpdr = <TEGRA_PIN_DISABLE>;
;
dap5_fs_pu0
nvidia,pins = "dap5_fs_pu0";
nvidia,function = "rsvd3";
nvidia,pull = <TEGRA_PIN_PULL_NONE>;
nvidia,tristate = <TEGRA_PIN_DISABLE>;
nvidia,enable-input = <TEGRA_PIN_DISABLE>;
nvidia,lpdr = <TEGRA_PIN_DISABLE>;
;
// i2c pins 未复用
;
;
;
驱动
code驱动
Linux内核源码支持Maxim的多款codec(包括max9867),代码路径kernel-4.9/sound/soc/codecs/
。
不需要修改。
platform驱动
Nvidia提供,音频代码目录结构如下:platform代码路径nvidia/sound/tegra-alt
:
nvidia/sound/
├── pci
├── soc
├── tegra-alt
├── include
├── machine_drivers
├── tegra210_admaif_alt.c
├── tegra210_i2s_alt.c
├── tegra210_mixer_alt.c
├── tegra210_xbar_alt.c
├── ... ...
├── utils
├── tegra-virt-alt
不需要修改。
machine驱动
代码路径nvidia/sound/tegra-alt/machine_drivers
:
nvidia/sound/
├── soc
├── tegra-alt
├── machine_drivers
├── tegra_machine_driver_mobile.c
├── tegra_t186ref_alt.c
├── tegra_t210ref_alt.c
├── ... ...
Nvidia默认适配codec芯片rt5659,需要修改为实际使用的max9867。
修改nvidia/sound/soc/tegra-alt/machine_drivers/tegra_machine_driver_mobile.c
- 添加codec头文件
#include "../codecs/max9867.h"
- 添加dapm_widgets
static const struct snd_soc_dapm_widget tegra_machine_dapm_widgets[] =
// 与设备树中nvidia,audio-routing匹配
SND_SOC_DAPM_HP("x Headphone", NULL),
SND_SOC_DAPM_LINE("x Linein", NULL),
;
- 添加codec的dai_link
static int tegra_machine_dai_init(struct snd_soc_pcm_runtime *runtime,
unsigned int rate, unsigned int channels,
u64 formats)
... ...
rtd = snd_soc_get_pcm_runtime(card, "max9867"); // 对应设备树中dai-link的link-name
if (rtd)
dai_params =
(struct snd_soc_pcm_stream *)rtd->dai_link->params;
dai_params->rate_min = srate;
dai_params->formats = (machine->fmt_via_kcontrol == 2) ?
(1ULL << SNDRV_PCM_FORMAT_S32_LE) : formats;
err = snd_soc_dai_set_sysclk(rtd->codec_dai, 0,
aud_mclk, SND_SOC_CLOCK_IN);
if (err < 0)
dev_err(card->dev, "codec_dai clock not set\\n");
return err;
... ...
- 添加codec初始化(非必要)
static int tegra_machine_max9867_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_card *card = rtd->card;
snd_soc_dapm_sync(&card->dapm);
return 0;
static int codec_init(struct tegra_machine *machine)
... ...
for (i = 0; i < num_links; i++)
if (!dai_links[i].name)
continue;
if (strstr(dai_links[i].name, "max9867"))
dai_links[i].init = tegra_machine_max9867_init;
... ...
return 0;
激活音频流
播放
amixer -c <sound_card> cset name='x Speaker Mode' 'Stereo Single' # codec内开启双声道
amixer -c <sound_card> cset name="I2S1 Mux" ADMAIF1 # i2s1(设备树指定)连接到cpu内aif1_rx
aplay -Dhw:1,0 <name>.wav # nvidia的card 0默认是hdmi的声卡,音频流参数根据音频文件自适应设置
录音
amixer -c <sound_card> cset name='x Input Mux' Line # codec输入mux连接line接
amixer -c <sound_card> cset name="ADMAIF1 Mux" I2S1 # cpu内aif1_tx(nvidia系统默认)连接到i2s1
arecord -Dhw:1,0 -r 44100 -c 2 -f S16_LE <name>.wav # nvidia的card 0默认是hdmi的声卡
问题定位
声卡未识别
执行aplay -l
orarecord -l
,可以查询声卡列表,Jetson平台的声卡如下:
如果未显示声卡:
- 检查cpu与codec之间的i2c通信
- 检查设备树与内核的dai-link是否匹配
以上Linux和Nvidia源码均可打印相关log。
录音/播放失败
如果aplay
orarecord
返回失败:
检查codec寄存器值
i2cdump -f -y <i2c_num> <i2c_addr>
可以返回对应地址的i2c设备的寄存器值:
检查音频流的trace
- 先打开trace开关:
echo 0 | sudo tee /sys/kernel/debug/tracing/trace
echo 0 | sudo tee /sys/kernel/debug/tracing/events/enable
echo 1 | sudo tee /sys/kernel/debug/tracing/tracing_on
echo 1 | sudo tee /sys/kernel/debug/tracing/events/asoc/snd_soc_dapm_path/enable
echo 1 | sudo tee /sys/kernel/debug/tracing/events/asoc/snd_soc_dapm_start/enable
echo 1 | sudo tee /sys/kernel/debug/tracing/events/asoc/snd_soc_dapm_walk_done/enable
echo 1 | sudo tee /sys/kernel/debug/tracing/events/asoc/snd_soc_dapm_widget_power/enable
- 然后激活音频流(
aplay
orarecord
),查看/sys/kernel/debug/tracing/trace
:
root@nvidia-desktop:/sys/kernel/debug/tracing# cat trace
# tracer: nop
#
# entries-in-buffer/entries-written: 197/197 #P:4
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / delay
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
alsa-sink-ADMAI-7714 [000] .... 1252.484903: snd_soc_dapm_start: card=tegra-snd-t19x-mobile
alsa-sink-ADMAI-7714 [000] .... 1252.484978: snd_soc_dapm_widget_power: widget=Playback 1 val=1
alsa-sink-ADMAI-7714 [000] .... 1252.484980: snd_soc_dapm_widget_power: widget=ADMAIF1 Receive val=1
alsa-sink-ADMAI-7714 [000] .... 1252.484983: snd_soc_dapm_path: *ADMAIF1 RX <- (direct) <- ADMAIF1 Receive
alsa-sink-ADMAI-7714 [000] .... 1252.485010: snd_soc_dapm_widget_power: widget=ADMAIF1 RX val=1
....
alsa-sink-ADMAI-7714 [000] .... 1252.485252: snd_soc_dapm_path: *I2S1 Mux <- ADMAIF1 <- ADMAIF1 RX
alsa-sink-ADMAI-7714 [000] .... 1252.485254: snd_soc_dapm_widget_power: widget=I2S1 Mux val=1
alsa-sink-ADMAI-7714 [000] .... 1252.485256: snd_soc_dapm_path: *I2S1 TX <- (direct) <- I2S1 Mux
alsa-sink-ADMAI-7714 [000] .... 1252.485258: snd_soc_dapm_widget_power: widget=I2S1 TX val=1
alsa-sink-ADMAI-7714 [000] .... 1252.485259: snd_soc_dapm_path: *I2S1 Transmit <- (direct) <- I2S1 TX
alsa-sink-ADMAI-7714 [000] .... 1252.485261: snd_soc_dapm_widget_power: widget=I2S1 Transmit val=1
alsa-sink-ADMAI-7714 [000] .... 1252.485262: snd_soc_dapm_path: *I2S1 Transmit-I2S1 CIF Receive <- (direct) <- I2S1 Transmit
alsa-sink-ADMAI-7714 [000] .... 1252.485264: snd_soc_dapm_widget_power: widget=I2S1 Transmit-I2S1 CIF Receive val=1
alsa-sink-ADMAI-7714 [000] .... 1252.485266: snd_soc_dapm_path: *I2S1 CIF Receive <- (direct) <- I2S1 Transmit-I2S1 CIF Receive
alsa-sink-ADMAI-7714 [000] .... 1252.485267: snd_soc_dapm_widget_power: widget=I2S1 CIF Receive val=1
alsa-sink-ADMAI-7714 [000] .... 1252.485269: snd_soc_dapm_path: *I2S1 CIF RX <- (direct) <- I2S1 CIF Receive
alsa-sink-ADMAI-7714 [000] .... 1252.485271: snd_soc_dapm_widget_power: widget=I2S1 CIF RX val=1
alsa-sink-ADMAI-7714 [000] .... 1252.485272: snd_soc_dapm_path: *I2S1 DAP TX <- (direct) <- I2S1 CIF RX
alsa-sink-ADMAI-7714 [000] .... 1252.485274: snd_soc_dapm_widget_power: widget=I2S1 DAP TX val=1
alsa-sink-ADMAI-7714 [000] .... 1252.485275: snd_soc_dapm_path: *I2S1 DAP Transmit <- (direct) <- I2S1 DAP TX
alsa-sink-ADMAI-7714 [000] .... 1252.485277: snd_soc_dapm_widget_power: widget=I2S1 DAP Transmit val=1
alsa-sink-ADMAI-7714 [000] .... 1252.485279: snd_soc_dapm_path: *I2S1 DAP Transmit-x HiFi Playback <- (direct) <- I2S1 DAP Transmit
alsa-sink-ADMAI-7714 [000] .... 1252.485280: snd_soc_dapm_widget_power: widget=I2S1 DAP Transmit-x HiFi Playback val=1
alsa-sink-ADMAI-7714 [000] .... 1252.485282: snd_soc_dapm_path: *x HiFi Playback <- (direct) <- I2S1 DAP Transmit-x HiFi Playback
alsa-sink-ADMAI-7714 [000] .... 1252.485284: snd_soc_dapm_widget_power: widget=x HiFi Playback val=1
alsa-sink-ADMAI-7714 [000] .... 1252.485285: snd_soc_dapm_path: *x DAI_OUT <- (direct) <- x HiFi Playback
alsa-sink-ADMAI-7714 [000] .... 1252.485287: snd_soc_dapm_widget_power: widget=x DAI_OUT val=1
alsa-sink-ADMAI-7714 [000] .... 1252.485289: snd_soc_dapm_path: *x Right DAC <- (direct) <- x DAI_OUT
alsa-sink-ADMAI-7714 [000] .... 1252.485290: snd_soc_dapm_widget_power: widget=x Right DAC val=1
alsa-sink-ADMAI-7714 [000] .... 1252.485292: snd_soc_dapm_path: *x Left DAC <- (direct) <- x DAI_OUT
alsa-sink-ADMAI-7714 [000] .... 1252.485294: snd_soc_dapm_widget_power: widget=x Left DAC val=1
alsa-sink-ADMAI-7714 [000] .... 1252.485296: snd_soc_dapm_widget_power: widget=x SHDN val=1
alsa-sink-ADMAI-7714 [000] .... 1252.485297: snd_soc_dapm_path: *x Output Mixer <- (direct) <- x Right DAC
alsa-sink-ADMAI-7714 [000] .... 1252.485299: snd_soc_dapm_path: *x Output Mixer <- (direct) <- x Left DAC
alsa-sink-ADMAI-7714 [000] .... 1252.485300: snd_soc_dapm_widget_power: widget=x Output Mixer val=1
alsa-sink-ADMAI-7714 [000] .... 1252.485302: snd_soc_dapm_path: *x HPOUT <- (direct) <- x Output Mixer
alsa-sink-ADMAI-7714 [000] .... 1252.485303: snd_soc_dapm_widget_power: widget=x HPOUT val=1
alsa-sink-ADMAI-7714 [000] .... 1252.485305: snd_soc_dapm_path: *x Headphone <- (direct) <- x HPOUT
alsa-sink-ADMAI-7714 [000] .... 1252.485306: snd_soc_dapm_widget_power: widget=x Headphone val=1
alsa-sink-ADMAI-7714 [000] .... 1252.485356: snd_soc_dapm_walk_done: tegra-snd-t19x-mobile: checks 23 power, 20 path, 179 neighbour
alsa在录音/播放前会先检查snd_soc_dapm_path(带"*")能否连成一路完整的routing,如果失败,录音/播放的进程会自动停止。
- 如果录音/播放的trace不完整,检查codec代码中的
snd_soc_dapm_route
,从右到左,能否连接成一路完整的routing:
static const struct snd_soc_dapm_route max9867_audio_map[] =
"Left DAC", NULL, "SHDN",
"Right DAC", NULL, "SHDN",
"Left DAC", NULL, "DAI_OUT",
"Right DAC", NULL, "DAI_OUT",
"Output Mixer", NULL, "Left DAC",
"Output Mixer", NULL, "Right DAC",
"HPOUT", NULL, "Output Mixer",
"DAI_IN", NULL, "SHDN",
"DAI_IN", NULL, "Left ADC",
"DAI_IN", NULL, "Right ADC",
"Left ADC", NULL, "Input Mux",
"Right ADC", NULL, "Input Mux",
"Input Mux", "Mic", "Line Input",
"Input Mux", "Line", "Line Input",
"Input Mux", "Mic_Line", "Line Input",
"Line Input", NULL, "LINE_IN",
;
RK3588平台开发系列讲解(AUDIO篇)Linux音频调试--alsa-utils 工具
平台 | 内核版本 | 安卓版本 |
---|---|---|
RK3588 |
Linux 5.10 |
Android 12 |
文章目录
沉淀、分享、成长,让自己和他人都能有所收获!
以上是关于Linux音频调试示例的主要内容,如果未能解决你的问题,请参考以下文章
RK3588平台开发系列讲解(AUDIO篇)Android音频调试--tiny-alsa 工具