Linux 核心音訊子系統偵錯

2023-08-28 21:01:06

debugfs 檔案系統

debugfs 可以為 Linux 核心各個模組的分析偵錯,提供許多資訊,如音訊子系統的 ASoC,以及 tracing 等。debugfs 檔案系統可以通過命令列工具掛載,如下所示:

root@apollo:~# ls /sys/kernel/debug/
root@apollo:~# mount -t debugfs nodev /sys/kernel/debug
root@apollo:~# ls /sys/kernel/debug/
asoc                device_component    fault_around_bytes  mtd                 regulator           virtio-ports
bdi                 devices_deferred    gpio                opp                 remoteproc          wakeup_sources
block               dma_buf             hid                 pinctrl             sleep_time
clear_warn_once     dma_pools           iio                 pm_genpd            split_huge_pages
clk                 dmaengine           interconnect        pwm                 suspend_stats
debug_enabled       energy_model        irq                 ras                 swiotlb
devfreq             extfrag             memblock            regmap              usb

debugfs 檔案系統也可以在系統啟動時自動掛載,這需要修改 /etc/fstab 檔案,需要在這個檔案中新增如下行:

debugfs  /sys/kernel/debug  debugfs  defaults  0  0

掛載了 debugfs 檔案系統之後,可以在 /sys/kernel/debug/asoc/ 目錄下看到註冊的所有 DAI、component 和 sound card,如:

root@apollo:~# ls /sys/kernel/debug/asoc/
components        dais              rt5651codec_card

在 sound card 對應的目錄下,可以看到其更詳細的組成,如:

root@apollo:~# ls /sys/kernel/debug/asoc/rt5651codec_card/
725000.i2s     dapm           dapm_pop_time  rt5651.0-001a

這裡的 sound card 主要由位於 0x725000 的 I2S 裝置,也就是 CPU DAI,幾個 DAPM 小部件,及 Audio Codec ALC5651,也就是 Codec DAI 組成。在 sound card 的 dapm 目錄下,可以看到關於它的 DAPM 小部件的更詳細的資訊,如:

root@apollo:~# ls /sys/kernel/debug/asoc/rt5651codec_card/dapm
HDMIIN       Headphones   Headset Mic  Int Mic      Lineout      bias_level

檢視 sound card 的 dapm 目錄下各個檔案的內容,可以瞭解對應 DAPM 小部件的狀態,如:

root@apollo:~# cat /sys/kernel/debug/asoc/rt5651codec_card/dapm/'Headset Mic'
Headset Mic: Off  in 1 out 0

這些小部件可能需要使用者專門通過工具來開關,也可能播放和錄製應用軟體會自動地開關。如 Headphones 小部件,它不需要使用者專門通過工具來開關。在不播放音訊檔時,它的狀態為 Off,如:

root@apollo:~# cat /sys/kernel/debug/asoc/rt5651codec_card/dapm/Headphones
Headphones: Off  in 0 out 1
 in  "static" "HPOR"
 in  "static" "HPOL"

當啟動 tinyplay 播放 WAV 音訊檔時,它的狀態變為 On,如:

root@apollo:~# cat /sys/kernel/debug/asoc/rt5651codec_card/dapm/Headphones
Headphones: On  in 16 out 1
 in  "static" "HPOR"
 in  "static" "HPOL"

在 sound card 的 rt5651.0-001a 目錄下,可以看到關於它的 Audio Codec ALC5651 的更詳細的資訊,如:

root@apollo:~# ls /sys/kernel/debug/asoc/rt5651codec_card/rt5651.0-001a/
dapm

ALC5651 的 Linux 核心驅動程式,主要提供了眾多 DAPM 小部件,來對其內部各部分進行控制,如音量,靜音,DAC,ADC,Mixer,耳機輸出,有線輸出等。

ALC5651 的 DAPM 小部件有如下這些:

root@apollo:~# ls /sys/kernel/debug/asoc/rt5651codec_card/rt5651.0-001a/dapm/
ADC ASRC                            HPO R Playback                      LOUT R Playback
ADC L                               HPO R Playback Switch Autodisable   LOUT R Playback Switch Autodisable
ADC L Power                         HPOL                                LOUTL
ADC R                               HPOL MIX                            LOUTR
ADC R Power                         HPOR                                MIC1
AIF1 Capture                        HPOR MIX                            MIC2
AIF1 Playback                       HPOVOL L                            MIC3
AIF1RX                              HPOVOL R                            OUT MIXL
AIF1TX                              I2S1                                OUT MIXR
AIF2 Capture                        I2S1 ASRC                           OUTVOL L
AIF2 Playback                       I2S2                                OUTVOL R
AIF2RX                              I2S2 ASRC                           PDM L Mux
AIF2TX                              IF1 ADC1                            PDM R Mux
Amp Power                           IF1 ADC2                            PDML
Audio DSP                           IF1 DAC                             PDMR
BST1                                IF1 DAC1 L                          RECMIXL
BST2                                IF1 DAC1 R                          RECMIXR
BST3                                IF1 DAC2 L                          STO1 DAC ASRC
DAC L1                              IF1 DAC2 R                          STO2 DAC ASRC
DAC L1 Power                        IF2 ADC                             Stereo DAC MIXL
DAC L2 Mux                          IF2 DAC                             Stereo DAC MIXR
DAC L2 Volume                       IF2 DAC L                           Stereo1 ADC L1 Mux
DAC MIXL                            IF2 DAC R                           Stereo1 ADC L2 Mux
DAC MIXR                            IN1P                                Stereo1 ADC MIXL
DAC R1                              IN2N                                Stereo1 ADC MIXR
DAC R1 Power                        IN2P                                Stereo1 ADC R1 Mux
DAC R2 Mux                          IN3P                                Stereo1 ADC R2 Mux
DAC R2 Volume                       INL1                                Stereo1 Filter
DD MIXL                             INL1 VOL                            Stereo2 ADC L1 Mux
DD MIXR                             INL2                                Stereo2 ADC L2 Mux
DMIC CLK                            INL2 VOL                            Stereo2 ADC MIXL
DMIC L1                             INR1                                Stereo2 ADC MIXR
DMIC R1                             INR1 VOL                            Stereo2 ADC R1 Mux
HP Amp                              INR2                                Stereo2 ADC R2 Mux
HP L Amp                            INR2 VOL                            Stereo2 Filter
HP Post                             LDO                                 Stero1 DAC Power
HP R Amp                            LOUT L Playback                     Stero2 DAC Power
HPO L Playback                      LOUT L Playback Switch Autodisable  bias_level
HPO L Playback Switch Autodisable   LOUT MIX                            micbias1

同樣,檢視這些檔案的內容,可以瞭解它們的狀態。其中的許多,由播放和錄製應用自動控制。如其中的耳機輸出開關 HPOVOL L,在播放音訊檔之前狀態如下:

root@apollo:~# cat /sys/kernel/debug/asoc/rt5651codec_card/rt5651.0-001a/dapm/'HPOVOL L'
HPOVOL L: Off  in 0 out 2 - R102(0x66) mask 0x800
 out  "HPO MIX HPVOL Switch" "HPOL MIX"
 in  "Switch" "OUT MIXL"

在播放音訊檔之後,其狀態則為:

root@apollo:~# cat /sys/kernel/debug/asoc/rt5651codec_card/rt5651.0-001a/dapm/'HPOVOL L'
HPOVOL L: On  in 2 out 2 - R102(0x66) mask 0x800
 out  "HPO MIX HPVOL Switch" "HPOL MIX"
 in  "Switch" "OUT MIXL"

這裡還會顯示各個 DAPM 小部件的音訊路由,即其輸入和輸出分別是其它哪個小部件。

在 debugfs 檔案系統中看到的 DAPM 小部件,由驅動程式中類似下面的程式碼定義:

static const struct snd_soc_dapm_widget rt5651_dapm_widgets[] = {
	/* ASRC */
	SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5651_PLL_MODE_2,
			      15, 0, NULL, 0),
	SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5651_PLL_MODE_2,
			      14, 0, NULL, 0),
	SND_SOC_DAPM_SUPPLY_S("STO1 DAC ASRC", 1, RT5651_PLL_MODE_2,
			      13, 0, NULL, 0),
	SND_SOC_DAPM_SUPPLY_S("STO2 DAC ASRC", 1, RT5651_PLL_MODE_2,
			      12, 0, NULL, 0),
	SND_SOC_DAPM_SUPPLY_S("ADC ASRC", 1, RT5651_PLL_MODE_2,
			      11, 0, NULL, 0),
 . . . . . .
 	/* Output Lines */
	SND_SOC_DAPM_OUTPUT("HPOL"),
	SND_SOC_DAPM_OUTPUT("HPOR"),
	SND_SOC_DAPM_OUTPUT("LOUTL"),
	SND_SOC_DAPM_OUTPUT("LOUTR"),
	SND_SOC_DAPM_OUTPUT("PDML"),
	SND_SOC_DAPM_OUTPUT("PDMR"),
};

Linux 核心音訊裝置驅動程式可以向用戶空間匯出控制介面,使用者可以通過這些控制介面控制音量大小,音量開關等,也可以通過這些控制介面控制兩個 DAPM 小部件等。這些控制介面在 Linux 核心音訊裝置驅動程式中,由 struct snd_kcontrol_new 描述。ALC5651 Linux 核心裝置驅動程式中定義的控制介面有如下這些:

static const struct snd_kcontrol_new rt5651_snd_controls[] = {
	/* Headphone Output Volume */
	SOC_DOUBLE_TLV("HP Playback Volume", RT5651_HP_VOL,
		RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, 39, 1, out_vol_tlv),
	/* OUTPUT Control */
	SOC_DOUBLE_TLV("OUT Playback Volume", RT5651_LOUT_CTRL1,
		RT5651_L_VOL_SFT, RT5651_R_VOL_SFT, 39, 1, out_vol_tlv),

	/* DAC Digital Volume */
	SOC_DOUBLE("DAC2 Playback Switch", RT5651_DAC2_CTRL,
		RT5651_M_DAC_L2_VOL_SFT, RT5651_M_DAC_R2_VOL_SFT, 1, 1),
	SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5651_DAC1_DIG_VOL,
			RT5651_L_VOL_SFT, RT5651_R_VOL_SFT,
			175, 0, dac_vol_tlv),
	SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5651_DAC2_DIG_VOL,
			RT5651_L_VOL_SFT, RT5651_R_VOL_SFT,
			175, 0, dac_vol_tlv),
	/* IN1/IN2/IN3 Control */
	SOC_SINGLE_TLV("IN1 Boost", RT5651_IN1_IN2,
		RT5651_BST_SFT1, 8, 0, bst_tlv),
	SOC_SINGLE_TLV("IN2 Boost", RT5651_IN1_IN2,
		RT5651_BST_SFT2, 8, 0, bst_tlv),
	SOC_SINGLE_TLV("IN3 Boost", RT5651_IN3,
		RT5651_BST_SFT1, 8, 0, bst_tlv),
	/* INL/INR Volume Control */
	SOC_DOUBLE_TLV("IN Capture Volume", RT5651_INL1_INR1_VOL,
			RT5651_INL_VOL_SFT, RT5651_INR_VOL_SFT,
			31, 1, in_vol_tlv),
	/* ADC Digital Volume Control */
	SOC_DOUBLE("ADC Capture Switch", RT5651_ADC_DIG_VOL,
		RT5651_L_MUTE_SFT, RT5651_R_MUTE_SFT, 1, 1),
	SOC_DOUBLE_TLV("ADC Capture Volume", RT5651_ADC_DIG_VOL,
			RT5651_L_VOL_SFT, RT5651_R_VOL_SFT,
			127, 0, adc_vol_tlv),
	SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5651_ADC_DATA,
			RT5651_L_VOL_SFT, RT5651_R_VOL_SFT,
			127, 0, adc_vol_tlv),
	/* ADC Boost Volume Control */
	SOC_DOUBLE_TLV("ADC Boost Gain", RT5651_ADC_BST_VOL,
			RT5651_ADC_L_BST_SFT, RT5651_ADC_R_BST_SFT,
			3, 0, adc_bst_tlv),

	/* ASRC */
	SOC_SINGLE("IF1 ASRC Switch", RT5651_PLL_MODE_1,
		RT5651_STO1_T_SFT, 1, 0),
	SOC_SINGLE("IF2 ASRC Switch", RT5651_PLL_MODE_1,
		RT5651_STO2_T_SFT, 1, 0),
	SOC_SINGLE("DMIC ASRC Switch", RT5651_PLL_MODE_1,
		RT5651_DMIC_1_M_SFT, 1, 0),

	SOC_ENUM("ADC IF2 Data Switch", rt5651_if2_adc_enum),
	SOC_ENUM("DAC IF2 Data Switch", rt5651_if2_dac_enum),
};
 . . . . . .
/* Digital Mixer */
static const struct snd_kcontrol_new rt5651_sto1_adc_l_mix[] = {
	SOC_DAPM_SINGLE("ADC1 Switch", RT5651_STO1_ADC_MIXER,
			RT5651_M_STO1_ADC_L1_SFT, 1, 1),
	SOC_DAPM_SINGLE("ADC2 Switch", RT5651_STO1_ADC_MIXER,
			RT5651_M_STO1_ADC_L2_SFT, 1, 1),
};

static const struct snd_kcontrol_new rt5651_sto1_adc_r_mix[] = {
	SOC_DAPM_SINGLE("ADC1 Switch", RT5651_STO1_ADC_MIXER,
			RT5651_M_STO1_ADC_R1_SFT, 1, 1),
	SOC_DAPM_SINGLE("ADC2 Switch", RT5651_STO1_ADC_MIXER,
			RT5651_M_STO1_ADC_R2_SFT, 1, 1),
};
 . . . . . .

DAPM 小部件可以繫結控制操作或事件處理程式,這種控制操作在 Linux 核心驅動程式中也用控制介面來描述。上面的 DAPM 小部件,參照的部分 struct snd_kcontrol_new 定義如下:

static const struct snd_kcontrol_new rt5651_dd_dac_l_mix[] = {
	SOC_DAPM_SINGLE("DAC L1 Switch", RT5651_DD_MIXER,
			RT5651_M_STO_DD_L1_SFT, 1, 1),
	SOC_DAPM_SINGLE("DAC L2 Switch", RT5651_DD_MIXER,
			RT5651_M_STO_DD_L2_SFT, 1, 1),
	SOC_DAPM_SINGLE("DAC R2 Switch", RT5651_DD_MIXER,
			RT5651_M_STO_DD_R2_L_SFT, 1, 1),
};

音訊路由描述不同的 DAPM 小部件之間的連線。上面看到的音訊路由,在 Linux 核心驅動程式中,由 struct snd_soc_dapm_route 描述。一些控制介面用於控制兩個 DAPM 小部件之間的連線,對於需要這種控制介面的音訊路由,在音訊路由表中,會參照控制介面。ALC5651 Linux 核心驅動程式中定義的音訊路由主要有如下這些:

static const struct snd_soc_dapm_route rt5651_dapm_routes[] = {
	{"Stero1 DAC Power", NULL, "STO1 DAC ASRC"},
	{"Stero2 DAC Power", NULL, "STO2 DAC ASRC"},
	{"I2S1", NULL, "I2S1 ASRC"},
	{"I2S2", NULL, "I2S2 ASRC"},
 . . . . . .
	{"RECMIXL", "INL1 Switch", "INL1 VOL"},
	{"RECMIXL", "BST3 Switch", "BST3"},
	{"RECMIXL", "BST2 Switch", "BST2"},
	{"RECMIXL", "BST1 Switch", "BST1"},

	{"RECMIXR", "INR1 Switch", "INR1 VOL"},
	{"RECMIXR", "BST3 Switch", "BST3"},
	{"RECMIXR", "BST2 Switch", "BST2"},
	{"RECMIXR", "BST1 Switch", "BST1"},
 . . . . . .
	{"PDML", NULL, "PDM L Mux"},
	{"PDMR", NULL, "PDM R Mux"},
};

上面的這些 DAPM 小部件,和音訊路由,作為 Codec DAI 驅動程式的一部分在註冊 Codec DAI 驅動程式時註冊進 ALSA 框架,如:

static const struct snd_soc_component_driver soc_component_dev_rt5651 = {
	.probe			= rt5651_probe,
	.suspend		= rt5651_suspend,
	.resume			= rt5651_resume,
	.set_bias_level		= rt5651_set_bias_level,
	.set_jack		= rt5651_set_jack,
	.controls		= rt5651_snd_controls,
	.num_controls		= ARRAY_SIZE(rt5651_snd_controls),
	.dapm_widgets		= rt5651_dapm_widgets,
	.num_dapm_widgets	= ARRAY_SIZE(rt5651_dapm_widgets),
	.dapm_routes		= rt5651_dapm_routes,
	.num_dapm_routes	= ARRAY_SIZE(rt5651_dapm_routes),
	.use_pmdown_time	= 1,
	.endianness		= 1,
	.non_legacy_dai_naming	= 1,
};

通過 debugfs 檔案系統瞭解音訊子系統的狀態比較方便,但控制各個 DAPM 小部件的開關則需要其它工具,如 tinymix 等。

ALSA 專案的使用者空間程式包括可以幫助使用者空間應用程式存取 ALSA 的 alsa-lib 庫,及眾多實用的工具,如 alsa-utils 中的 alsactlamixeraplayamidi等,這些工具可用於設定和測試 ALSA 的各項功能。如 aplay 呼叫 alsa-lib 庫來播放音訊檔,arecord 呼叫 alsa-lib 庫來錄製音訊檔,amixer 可用於設定各個控制介面的狀態等。更多詳細資訊可以參考 alsa-project 的 github

tinyalsa 可以看作是 ALSA 專案的使用者空間程式的簡化版本,它包含一個簡化版的庫,及一些實用的小工具。這些工具包括 tinyplaytinycaptinymix 等,這幾個工具的作用大體與 aplayarecordamixer 等價。相對於 ALSA 專案的使用者空間程式,tinyalsa 的程式碼更簡潔,因而移植和偵錯分析也就更簡單。本文中所做的測試和偵錯用 tinyalsa 的工具。

tinymix

tinymix 可用來獲取或設定各個控制介面的狀態,這個工具用法如下:

root@apollo:~# tinymix --help
usage: tinymix [options] <command>
options:
        -h, --help               : prints this help message and exits
        -v, --version            : prints this version of tinymix and exits
        -D, --card NUMBER        : specifies the card number of the mixer

commands:
        get NAME|ID              : prints the values of a control
        set NAME|ID VALUE(S) ... : sets the value of a control
                VALUE(S): integers, percents, and relative values
                        Integers: 0, 100, -100 ...
                        Percents: 0%, 100% ...
                        Relative values: 1+, 1-, 1%+, 2%+ ...
        controls                 : lists controls of the mixer
        contents                 : lists controls of the mixer and their contents

tinymix controls 命令可以列出系統中所有的控制介面,如:

root@apollo:~# tinymix controls
Number of controls: 91
ctl     type    num     name
0       INT     2       HP Playback Volume
1       INT     2       OUT Playback Volume
2       BOOL    2       DAC2 Playback Switch
3       INT     2       DAC1 Playback Volume
4       INT     2       Mono DAC Playback Volume
5       INT     1       IN1 Boost
6       INT     1       IN2 Boost
7       INT     1       IN3 Boost
8       INT     2       IN Capture Volume
9       BOOL    2       ADC Capture Switch
10      INT     2       ADC Capture Volume
11      INT     2       Mono ADC Capture Volume
12      INT     2       ADC Boost Gain
13      BOOL    1       IF1 ASRC Switch
14      BOOL    1       IF2 ASRC Switch
15      BOOL    1       DMIC ASRC Switch
16      ENUM    1       ADC IF2 Data Switch
17      ENUM    1       DAC IF2 Data Switch
18      BOOL    1       Headphones Switch
19      BOOL    1       Lineout Switch
20      BOOL    1       Headset Mic Switch
21      BOOL    1       Int Mic Switch
22      BOOL    1       RECMIXL INL1 Switch
23      BOOL    1       RECMIXL BST3 Switch
24      BOOL    1       RECMIXL BST2 Switch
25      BOOL    1       RECMIXL BST1 Switch
26      BOOL    1       RECMIXR INR1 Switch
27      BOOL    1       RECMIXR BST3 Switch
28      BOOL    1       RECMIXR BST2 Switch
29      BOOL    1       RECMIXR BST1 Switch
30      ENUM    1       Stereo1 ADC L2 Mux
31      ENUM    1       Stereo1 ADC R2 Mux
32      ENUM    1       Stereo1 ADC L1 Mux
33      ENUM    1       Stereo1 ADC R1 Mux
34      ENUM    1       Stereo2 ADC L2 Mux
35      ENUM    1       Stereo2 ADC L1 Mux
36      ENUM    1       Stereo2 ADC R1 Mux
37      ENUM    1       Stereo2 ADC R2 Mux
38      BOOL    1       Stereo1 ADC MIXL ADC1 Switch
39      BOOL    1       Stereo1 ADC MIXL ADC2 Switch
40      BOOL    1       Stereo1 ADC MIXR ADC1 Switch
41      BOOL    1       Stereo1 ADC MIXR ADC2 Switch
42      BOOL    1       Stereo2 ADC MIXL ADC1 Switch
43      BOOL    1       Stereo2 ADC MIXL ADC2 Switch
44      BOOL    1       Stereo2 ADC MIXR ADC1 Switch
45      BOOL    1       Stereo2 ADC MIXR ADC2 Switch
46      ENUM    1       IF2 ADC
47      ENUM    1       PDM L Mux
48      ENUM    1       PDM R Mux
49      BOOL    1       DAC MIXL Stereo ADC Switch
50      BOOL    1       DAC MIXL INF1 Switch
51      BOOL    1       DAC MIXR Stereo ADC Switch
52      BOOL    1       DAC MIXR INF1 Switch
53      ENUM    1       DAC L2 Mux
54      ENUM    1       DAC R2 Mux
55      BOOL    1       Stereo DAC MIXL DAC L1 Switch
56      BOOL    1       Stereo DAC MIXL DAC L2 Switch
57      BOOL    1       Stereo DAC MIXL DAC R1 Switch
58      BOOL    1       Stereo DAC MIXR DAC R1 Switch
59      BOOL    1       Stereo DAC MIXR DAC R2 Switch
60      BOOL    1       Stereo DAC MIXR DAC L1 Switch
61      BOOL    1       DD MIXL DAC L1 Switch
62      BOOL    1       DD MIXL DAC L2 Switch
63      BOOL    1       DD MIXL DAC R2 Switch
64      BOOL    1       DD MIXR DAC R1 Switch
65      BOOL    1       DD MIXR DAC R2 Switch
66      BOOL    1       DD MIXR DAC L2 Switch
67      BOOL    1       OUT MIXL BST1 Switch
68      BOOL    1       OUT MIXL BST2 Switch
69      BOOL    1       OUT MIXL INL1 Switch
70      BOOL    1       OUT MIXL REC MIXL Switch
71      BOOL    1       OUT MIXL DAC L1 Switch
72      BOOL    1       OUT MIXR BST2 Switch
73      BOOL    1       OUT MIXR BST1 Switch
74      BOOL    1       OUT MIXR INR1 Switch
75      BOOL    1       OUT MIXR REC MIXR Switch
76      BOOL    1       OUT MIXR DAC R1 Switch
77      BOOL    1       OUTVOL L Switch
78      BOOL    1       OUTVOL R Switch
79      BOOL    1       HPOVOL L Switch
80      BOOL    1       HPOVOL R Switch
81      BOOL    1       HPO MIX DAC1 Switch
82      BOOL    1       HPO MIX HPVOL Switch
83      BOOL    1       LOUT MIX DAC L1 Switch
84      BOOL    1       LOUT MIX DAC R1 Switch
85      BOOL    1       LOUT MIX OUTVOL L Switch
86      BOOL    1       LOUT MIX OUTVOL R Switch
87      BOOL    1       HPO L Playback Switch
88      BOOL    1       HPO R Playback Switch
89      BOOL    1       LOUT L Playback Switch
90      BOOL    1       LOUT R Playback Switch

tinymix controls 命令每行列出一個控制介面,其中包含控制 ID,可以用於其它命令,值的型別,及控制介面的名稱等。tinymix contents 命令在列出 tinymix controls 命令列出的內容之外,還會列出各個控制介面的狀態,如:

root@apollo:~# tinymix contents
Number of controls: 91
ctl     type    num     name                                    value
0       INT     2       HP Playback Volume                      31, 31 (range 0->39)
1       INT     2       OUT Playback Volume                     31, 31 (range 0->39)
2       BOOL    2       DAC2 Playback Switch                    On, On
3       INT     2       DAC1 Playback Volume                    175, 175 (range 0->175)
4       INT     2       Mono DAC Playback Volume                175, 175 (range 0->175)
5       INT     1       IN1 Boost                               0 (range 0->8)
6       INT     1       IN2 Boost                               0 (range 0->8)
7       INT     1       IN3 Boost                               0 (range 0->8)
8       INT     2       IN Capture Volume                       23, 23 (range 0->31)
9       BOOL    2       ADC Capture Switch                      On, On
10      INT     2       ADC Capture Volume                      47, 47 (range 0->127)
11      INT     2       Mono ADC Capture Volume                 47, 47 (range 0->127)
12      INT     2       ADC Boost Gain                          0, 0 (range 0->3)
13      BOOL    1       IF1 ASRC Switch                         Off
14      BOOL    1       IF2 ASRC Switch                         Off
15      BOOL    1       DMIC ASRC Switch                        Off
16      ENUM    1       ADC IF2 Data Switch                     > Normal, Swap, left copy to right, right copy to left,
17      ENUM    1       DAC IF2 Data Switch                     > Normal, Swap, left copy to right, right copy to left,
18      BOOL    1       Headphones Switch                       On
19      BOOL    1       Lineout Switch                          On
20      BOOL    1       Headset Mic Switch                      On
21      BOOL    1       Int Mic Switch                          On
22      BOOL    1       RECMIXL INL1 Switch                     Off
23      BOOL    1       RECMIXL BST3 Switch                     Off
24      BOOL    1       RECMIXL BST2 Switch                     Off
25      BOOL    1       RECMIXL BST1 Switch                     Off
26      BOOL    1       RECMIXR INR1 Switch                     Off
27      BOOL    1       RECMIXR BST3 Switch                     Off
28      BOOL    1       RECMIXR BST2 Switch                     Off
29      BOOL    1       RECMIXR BST1 Switch                     Off
30      ENUM    1       Stereo1 ADC L2 Mux                      DMIC, > DD MIX,
31      ENUM    1       Stereo1 ADC R2 Mux                      DMIC, > DD MIX,
32      ENUM    1       Stereo1 ADC L1 Mux                      DD MIX, > ADC,
33      ENUM    1       Stereo1 ADC R1 Mux                      DD MIX, > ADC,
34      ENUM    1       Stereo2 ADC L2 Mux                      DMIC L, > DD MIXL,
35      ENUM    1       Stereo2 ADC L1 Mux                      DD MIXL, > ADCL,
36      ENUM    1       Stereo2 ADC R1 Mux                      > DD MIXR, ADCR,
37      ENUM    1       Stereo2 ADC R2 Mux                      > DMIC R, DD MIXR,
38      BOOL    1       Stereo1 ADC MIXL ADC1 Switch            Off
39      BOOL    1       Stereo1 ADC MIXL ADC2 Switch            Off
40      BOOL    1       Stereo1 ADC MIXR ADC1 Switch            Off
41      BOOL    1       Stereo1 ADC MIXR ADC2 Switch            Off
42      BOOL    1       Stereo2 ADC MIXL ADC1 Switch            Off
43      BOOL    1       Stereo2 ADC MIXL ADC2 Switch            Off
44      BOOL    1       Stereo2 ADC MIXR ADC1 Switch            Off
45      BOOL    1       Stereo2 ADC MIXR ADC2 Switch            Off
46      ENUM    1       IF2 ADC                                 > IF1 ADC1, IF1 ADC2,
47      ENUM    1       PDM L Mux                               > DD MIX, Stereo DAC MIX,
48      ENUM    1       PDM R Mux                               > DD MIX, Stereo DAC MIX,
49      BOOL    1       DAC MIXL Stereo ADC Switch              Off
50      BOOL    1       DAC MIXL INF1 Switch                    On
51      BOOL    1       DAC MIXR Stereo ADC Switch              Off
52      BOOL    1       DAC MIXR INF1 Switch                    On
53      ENUM    1       DAC L2 Mux                              IF1, > IF2,
54      ENUM    1       DAC R2 Mux                              IF1, > IF2,
55      BOOL    1       Stereo DAC MIXL DAC L1 Switch           On
56      BOOL    1       Stereo DAC MIXL DAC L2 Switch           Off
57      BOOL    1       Stereo DAC MIXL DAC R1 Switch           Off
58      BOOL    1       Stereo DAC MIXR DAC R1 Switch           On
59      BOOL    1       Stereo DAC MIXR DAC R2 Switch           Off
60      BOOL    1       Stereo DAC MIXR DAC L1 Switch           Off
61      BOOL    1       DD MIXL DAC L1 Switch                   Off
62      BOOL    1       DD MIXL DAC L2 Switch                   Off
63      BOOL    1       DD MIXL DAC R2 Switch                   Off
64      BOOL    1       DD MIXR DAC R1 Switch                   Off
65      BOOL    1       DD MIXR DAC R2 Switch                   Off
66      BOOL    1       DD MIXR DAC L2 Switch                   Off
67      BOOL    1       OUT MIXL BST1 Switch                    Off
68      BOOL    1       OUT MIXL BST2 Switch                    Off
69      BOOL    1       OUT MIXL INL1 Switch                    Off
70      BOOL    1       OUT MIXL REC MIXL Switch                Off
71      BOOL    1       OUT MIXL DAC L1 Switch                  On
72      BOOL    1       OUT MIXR BST2 Switch                    Off
73      BOOL    1       OUT MIXR BST1 Switch                    Off
74      BOOL    1       OUT MIXR INR1 Switch                    Off
75      BOOL    1       OUT MIXR REC MIXR Switch                Off
76      BOOL    1       OUT MIXR DAC R1 Switch                  On
77      BOOL    1       OUTVOL L Switch                         Off
78      BOOL    1       OUTVOL R Switch                         Off
79      BOOL    1       HPOVOL L Switch                         On
80      BOOL    1       HPOVOL R Switch                         On
81      BOOL    1       HPO MIX DAC1 Switch                     On
82      BOOL    1       HPO MIX HPVOL Switch                    On
83      BOOL    1       LOUT MIX DAC L1 Switch                  Off
84      BOOL    1       LOUT MIX DAC R1 Switch                  Off
85      BOOL    1       LOUT MIX OUTVOL L Switch                Off
86      BOOL    1       LOUT MIX OUTVOL R Switch                Off
87      BOOL    1       HPO L Playback Switch                   On
88      BOOL    1       HPO R Playback Switch                   On
89      BOOL    1       LOUT L Playback Switch                  Off
90      BOOL    1       LOUT R Playback Switch                  Off

如前面看到的,tinymix controlstinymix contents 命令列出的所有控制介面,在 Linux 核心裝置驅動程式中,由 struct snd_kcontrol_new 結構體描述。

對於相同的 Audio Codec,由於 sound card 註冊的控制介面不同,而使其各個控制介面,在不同裝置中的控制 ID 可能不同,但名稱都是相同的。

要使播放或錄製正常運轉,通常需要開啟 sound card 及 Audio Codec 中相關各個控制介面的開關。如要播放音訊,對於 ALC5651,大概有如下這樣的音訊路由:

需要開啟如下這些控制介面:

18      BOOL    1       Headphones Switch                       On
50      BOOL    1       DAC MIXL INF1 Switch                    On
52      BOOL    1       DAC MIXR INF1 Switch                    On
55      BOOL    1       Stereo DAC MIXL DAC L1 Switch           On
58      BOOL    1       Stereo DAC MIXR DAC R1 Switch           On
71      BOOL    1       OUT MIXL DAC L1 Switch                  On
76      BOOL    1       OUT MIXR DAC R1 Switch                  On
79      BOOL    1       HPOVOL L Switch                         On
80      BOOL    1       HPOVOL R Switch                         On
81      BOOL    1       HPO MIX DAC1 Switch                     On
82      BOOL    1       HPO MIX HPVOL Switch                    On
87      BOOL    1       HPO L Playback Switch                   On
88      BOOL    1       HPO R Playback Switch                   On

tinymix get 命令可以用於獲取具體某個控制介面的狀態,其引數為上面看到的控制 ID,如:

root@apollo:~# tinymix get 18
On

tinymix set 命令可以用於設定具體某個控制介面的狀態,其引數為上面看到的控制 ID 和要設定的值,如:

root@apollo:~# tinymix get 18
On
root@apollo:~# tinymix set 18 0
root@apollo:~# tinymix get 18
Off
root@apollo:~# tinymix set 18 1
root@apollo:~# tinymix get 18
On

對於 ALC5651,如要播放音訊,需要像下面這樣開啟相關控制介面的開關:

root@apollo:~# tinymix set 18 1
root@apollo:~# tinymix set 50 1
root@apollo:~# tinymix set 52 1
root@apollo:~# tinymix set 55 1
root@apollo:~# tinymix set 58 1
root@apollo:~# tinymix set 71 1
root@apollo:~# tinymix set 76 1
root@apollo:~# tinymix set 79 1
root@apollo:~# tinymix set 80 1
root@apollo:~# tinymix set 81 1
root@apollo:~# tinymix set 82 1
root@apollo:~# tinymix set 87 1
root@apollo:~# tinymix set 88 1

在播放音訊資料的過程中,可以看到 Audio Codec 的 DAPM 小部件 AIF1 Playback 的狀態由 inactive 變為 active, sound card 的 DAPM 小部件 Headphones 的狀態由 Off 變為 On,如:

root@apollo:~# cat /sys/kernel/debug/asoc/rt5651codec_card/rt5651.0-001a/dapm/'AIF1 Playback'
AIF1 Playback: Off  in 0 out 18
 stream AIF1 Playback inactive
 out  "static" "AIF1RX"
 in  "static" "Playback"
root@apollo:~# cat /sys/kernel/debug/asoc/rt5651codec_card/rt5651.0-001a/dapm/'AIF1 Playback'
AIF1 Playback: On  in 1 out 18
 stream AIF1 Playback active
 out  "static" "AIF1RX"
 in  "static" "Playback"
root@apollo:~# cat /sys/kernel/debug/asoc/rt5651codec_card/dapm/Headphones
Headphones: On  in 16 out 1
 in  "static" "HPOR"
 in  "static" "HPOL"

如果要用 ALC5651 錄音,有如下的音訊路由:

則需要開啟如下這些控制介面的開關:

24      BOOL    1       RECMIXL BST2 Switch                     Off
28      BOOL    1       RECMIXR BST2 Switch                     Off
32      ENUM    1       Stereo1 ADC L1 Mux                      DD MIX, > ADC,
33      ENUM    1       Stereo1 ADC R1 Mux                      DD MIX, > ADC,
38      BOOL    1       Stereo1 ADC MIXL ADC1 Switch            Off
40      BOOL    1       Stereo1 ADC MIXR ADC1 Switch            Off

這需要執行如下這些命令:

root@apollo:~# tinymix set 24 1
root@apollo:~# tinymix set 28 1
root@apollo:~# tinymix set 38 1
root@apollo:~# tinymix set 40 1

i2cget/i2cset

偵錯 Linux 核心音訊子系統時,如果懷疑音訊裝置的暫存器設定不正確,則可以根據不同裝置的特性,採取不同的方法來確認。在 SoC 上,許多裝置的暫存器被對映到實體記憶體地址空間,存取裝置的暫存器就像存取實體記憶體一樣,如 I2S 匯流排。還有一些裝置的暫存器,需要通過 I2C 等其它匯流排存取,如 Audio Codec ALC5651。在 Linux 核心驅動程式的實現中,這些裝置的暫存器都可以用 regmap 機制來存取。

debugfs 檔案系統的 /sys/kernel/debug/regmap 目錄中,可以看到使用了 regmap 機制的裝置,如:

root@apollo:~# ls /sys/kernel/debug/regmap/
0-001a/                725000.i2s/            dummy-crg_vid@2c0000/

在上面各個具體裝置的目錄中,可以看到關於該裝置的 regmap 的更詳細資訊,如 Audio Codec ALC5651 的 0-001a 目錄包含如下內容:

root@apollo:~# ls /sys/kernel/debug/regmap/0-001a/
PR            cache_bypass  cache_only    range         registers
access        cache_dirty   name          rbtree

檢視目錄中 registers 檔案的內容,可以看到裝置的所有暫存器當前的值,如:

root@apollo:~# cat /sys/kernel/debug/regmap/0-001a/registers
000: 0000
002: 8888
003: c8c8
005: 0000
00d: 0000
00e: 0000
00f: 0808
010: 0808
019: afaf
01a: afaf
 . . . . . .
0fc: 0000
0fd: 0002
0fe: 10ec
0ff: 6281
100: aaaa
101: 4000
102: a280
 . . . . . .
1ad: 01f4
1ae: 1c10
1af: 01f4
1b0: 2000
1b1: 0000
1b2: 2000
1b3: 0800
1b4: 0800

播放音訊時檢視 ALC5651 電源控制暫存器 1 的值:

root@apollo:~# cat /sys/kernel/debug/regmap/0-001a/registers  | grep 061
061: 0000

可以用同樣的方法檢視 I2S 這種把裝置暫存器對映到實體記憶體地址空間的裝置的暫存器,如:

root@apollo:~# cat /sys/kernel/debug/regmap/725000.i2s/registers
000: 00000000
004: 00000000
008: 00000000
 . . . . . .

對於把裝置暫存器對映到實體記憶體地址空間的裝置,devmem 族是更好用的分析偵錯工具。devmemdevmset 分別可以檢視和設定特定實體記憶體地址處的值,因而也可以讀取和設定裝置暫存器的值,如:

root@apollo:~# devmem 0x9f4100000
0x00000000
root@apollo:~# devmset 0x9f4100000 0x1234 1
root@apollo:~# devmem 0x9f4100000
0x00001234

對於需要通過 I2C 來存取內部暫存器的裝置,i2cget/i2cset 族是更好用的工具。i2cdetect 命令可以檢測 I2C 匯流排的狀態,這個命令的用法如下:

root@apollo:~# i2cdetect --help
BusyBox v1.35.0 () multi-call binary.

Usage: i2cdetect -l | -F I2CBUS | [-ya] [-q|-r] I2CBUS [FIRST LAST]

i2cdetect -l 命令列出所有的 I2C 裝置:

root@apollo:~# i2cdetect -l
i2c-0   i2c            I2C adapter

i2cdetect -F [I2CBUS] 命令列出特定 I2C 匯流排支援的特性,如:

root@apollo:~# i2cdetect -F 0
Functionalities implemented by bus #0
I2C                              yes
SMBus quick command              yes
SMBus send byte                  yes
SMBus receive byte               yes
SMBus write byte                 yes
SMBus read byte                  yes
SMBus write word                 yes
SMBus read word                  yes
SMBus process call               no
SMBus block write                yes
SMBus block read                 yes
SMBus block process call         no
SMBus PEC                        no
I2C block write                  yes
I2C block read                   yes

i2cdetect [I2CBUS] 命令檢測特定 I2C 匯流排上,哪個從地址上有裝置。如下面的命令列出了 I2C 0 匯流排的情況:

root@apollo:~# i2cdetect -y 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- UU -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

在上面這個 I2C匯流排上,只有從地址 0x1A 上有裝置,也就是 ALC5651。

i2cdump 命令可以顯示特定 I2C 從裝置所有暫存器的值,如下面的命令列出了 ALC5651 的所有暫存器的值:

root@apollo:~# i2cdump -fy 0 0x1a w
     0,8  1,9  2,a  3,b  4,c  5,d  6,e  7,f
00: 0000 0000 8888 c8c8 0000 0000 0000 0000
08: 0000 0000 0000 0000 0000 0000 0000 0808
10: 0808 0000 0000 0000 0000 0000 0000 0000
18: 0000 afaf afaf 000c 2f2f 2f2f 0000 0000
20: 0000 0000 0000 0000 0000 0000 0000 0018
28: 1010 8080 1212 0000 0000 0000 0000 0000
30: 0050 0000 0000 0000 0000 0000 0000 0000
38: 0000 0000 0000 0000 4100 0000 4100 0000
40: 0000 0000 0000 0000 0000 0000 0000 0000
48: 0000 0000 0000 0000 0000 0000 0000 7802
50: 0000 0000 7802 00f0 0000 0000 0000 0000
58: 0000 0000 0000 0000 0000 0000 0000 0000
60: 0000 0000 0000 0200 0002 0000 0000 0000
68: 0000 0000 b400 0000 0008 0000 0000 0000
70: 0080 0080 0000 0411 000c 8014 0000 000c
78: 0040 2301 0000 0000 0000 0000 0000 0000
80: 0040 0102 0008 0008 0000 0800 0000 0000
88: 0000 0000 0000 0006 0000 0000 0500 4011
90: 3707 000e 0000 0020 0002 0000 0000 0000
98: 0000 0000 0000 0000 0000 0000 0000 0000
a0: 0000 0000 0000 0000 0000 0000 0000 0000
a8: 0000 0000 0000 0000 0000 0000 0000 0000
b0: 8020 0000 0000 0000 0622 001f 0000 0000
b8: 0000 0000 0000 0000 0000 0000 0000 0000
c0: 0000 0000 0000 0000 0000 0000 0000 0000
c8: 0000 0000 0000 0000 0000 0000 0000 1300
d0: 8006 171c 0000 20b3 0000 0000 0004 0000
d8: 0000 0908 0000 0000 0000 0000 0000 0000
e0: 0000 0000 0000 0000 0000 0000 0000 0000
e8: 0000 0000 0000 0000 0000 0000 0000 0000
f0: 0000 0000 0000 0000 0000 0000 0000 0000
f8: 0000 0000 1000 0000 0000 0200 ec10 8162

i2cgeti2cset分別用於讀取和寫入特定 I2C 從裝置的暫存器。如:

root@apollo:~# i2cget -fy 0 0x1a 0xff w
0x8162

i2c* 預設不能存取已經被某個驅動程式佔用的 I2C 從裝置,上面看到的 -f 標記表示強制存取,-y 標記表示以 Y 來回答工具給出的提示。這些工具是按大尾端來讀取和設定值的。如上面看到的,0xFF 地址處的 0x8162,其實際值為 0x6281。向 I2C 從裝置的暫存器寫入特定值的方法如下:

root@apollo:~# i2cset -fy 0 0x1a 0xff 0x8162 w

Done.