リネオブログ

デバイスツリーとサウンド設定(widgets, routing)について学ぶ:後編

2023 年 03 月 31 日   Linux 技術ネタ

目次

  1. はじめに
  2. 設定事例:サウンドの widgets, routing プロパティ
  3. まとめ
  4. 参考文献

はじめに

Linux には、デバイスツリーというハードウェア固有設定をコンフィグファイルとしてソースコードから切り離す便利な機能がありますが、日本語、英語ともに情報が少なく、見よう見まねで対応されている方が多いのではないでしょうか?

リネオ社内で聞いてみたところ、ネットで検索してもなかなか有力な情報が出てこないため、例えば、『サウンドの widgets と routing プロパティの設定方法 (ルール) が理解できていない。』などコメントをもらいました。
OSS の Linux は、ソースコードを誰でも読むことができる反面、情報が溢れて何が正しいのかわかりづらいケース、公式ドキュメントの説明不足でソースコードを読まないと理解できないケースが多々あります。一方で、ソースコードから機能を読み解くことは、ハードルが高く、容易なことではありません。

本ブログエントリでは、デバイスツリーとはどのようなものかと 1 事例としてサウンドの widgets と routing プロパティの設定方法について紹介します。
本ブログは全2部構成となっており、後編では「サウンドの widgets, routing プロパティの設定事例」を解説していきます。

前編はこちら。

設定事例:サウンドの widgets, routing プロパティ

これまで デバイスツリー がどのようなものかを説明してきました。つづいて、リネオ社内で有力な情報なく未だに理解しきれていないと情報を得た、サウンドの widgets,routing プロパティを設定の具体事例として、本ブログエントリを締めくくりたいと思います。

ご注意

今回調査した結果、widgets と routing プロパティは使われなくなってきているとわかりました。arm64 SoC 搭載ボードのサウンドドライバの場合、widgets は全てハードコーディングされています。また、routing は Qualcomm の SoC のみが使用している状況です。将来使用されなくなる可能性が高いです。

現代のサウンドカードの事情

現代は、ハードウェアの進化スピードが目覚ましく、少し前まで最新だった技術がすぐに当たり前となり、組込み機器でも速やかに利用可能になる時代です。サウンドカードや組込みボードに搭載されるサウンド機能は、独立した多数のオーディオコンポートから構成され、複雑化しており、オーディオコンポート全体を管理しないと必要以上に電力を無駄遣いしてしまう状況です。Linux ではオーディオサブシステム内で常に最小限の電力を使用するように設計された DAPM(Dynamic Audio Power Management)という仕組みを導入し、この問題に対応しています。

anatomy of a modern sound card

Dynamic Audio Power Management より抜粋

widgets と routing の概要

widgets とは、オーディオコンポートのことです。また、routing とは、オーディオコンポートと外部の入出力の接続のことです。例えば、以下は NXP 社の SGTL5000 という Codec です。widgets は、黄色の部分を指します。routing は、SGTL5000 に IN/OUT される矢印を指します。
widgets と routing で機能と外部入出力の ON/OFF を設定することで、必要最小限のオーディオコンポートを利用可能になります。

Figure1.SGTL5000 Simplified Application Diagram

NXP Semiconductors Data Sheet: Technical Data Document Number: SGTL5000 Rev. 7, 1/2022 より抜粋

dtb で定義可能な widgets と routing

Linux カーネルには、DAPM を使用したサウンドドライバを手軽に開発するテンプレートとして、simple-card-driver が用意されています。

simple-card.txt

widgets, routing などシステム固有情報を dtb から設定する実装になっており、DAPM に対応するため、多くのドライバがこのテンプレートをベースに開発されたと推測します。しかし、このテンプレートでサポートしているのは、後で述べますが 4 種類のオーディオコンポートのみです。オーディオコンポーネントの多様化に伴い、このテンプレートではカバーできなくなり、使用されなくなったと推測します。Linux 6.1.8 の時点で DAPM がサポートするオーディオコンポーネントは、38 種類です。また、後で述べますが、widgets も routing もハードウェアで定義された正確な名前を使用しなければならないため、設定ミス含め無用な混乱を避けるためにソースコードにハードコーディングする方向に傾いたと推測します。

widgets の書式

DAPM widgets は、様々なオーディオコンポーネントをサポートしていますが、このうち dtb の widgets でサポートしているのは、ユーザからの IN/OUT に関わる以下 4 種類のみです。

dtb でサポートする widgets(templeate-wname) 対応する DAPM widgets 概要
Microphone Mic マイクからの入力ジャック
Line Line ライン入力・出力ジャック
Headphone Headphone ヘッドフォンの出力ジャック
Speaker Speaker スピーカーへの出力ジャック

書式は、以下の通りです。Linux カーネルは、奇数番目の文字列を template-wname として、偶数番目の文字列を user-supplied-wnameと認識します。

"template-wname", "user-supplied-wname"

● template-wname

上記 4 つのみが有効な文字列です。

● user-supplied-wname

ハードウェア毎に定義されている有効な名前を指定する必要があります。

For instance:
	simple-audio-widgets =
		"Microphone", "Microphone Jack",
		"Line", "Line In Jack",
		"Line", "Line Out Jack",
		"Headphone", "Headphone Jack",
		"Speaker", "Speaker External"

widgets.txt

widgets を dtb から DAPM に登録する関数
static const struct snd_soc_dapm_widget simple_widgets[] = {
	SND_SOC_DAPM_MIC("Microphone", NULL),
	SND_SOC_DAPM_LINE("Line", NULL),
	SND_SOC_DAPM_HP("Headphone", NULL),
	SND_SOC_DAPM_SPK("Speaker", NULL),
};
int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card,
					  const char *propname)
{
	struct device_node *np = card->dev->of_node;
	struct snd_soc_dapm_widget *widgets;
	const char *template, *wname;
	int i, j, num_widgets;

	num_widgets = of_property_count_strings(np, propname);
★widgets プロパティの総数を dtb から取得
・・・
	num_widgets /= 2;    ★2つずつで意味を持つので 2 で割る

・・・

	for (i = 0; i < num_widgets; i++) {
		int ret = of_property_read_string_index(np, propname,
							2 * i, &template);
★ templeate-wname を dtb から取得
・・・
		for (j = 0; j < ARRAY_SIZE(simple_widgets); j++) {
			if (!strncmp(template, simple_widgets[j].name,
				     strlen(simple_widgets[j].name))) {
				widgets[i] = simple_widgets[j];
				break;
★有効な templeate-wname か否かを確認
			}
		}

・・・
		ret = of_property_read_string_index(np, propname,
						    (2 * i) + 1,
						    &wname);
★user-supplied-wname を dtb から取得
・・・
		widgets[i].name = wname;
	}

	card->of_dapm_widgets = widgets; ★ dapm に登録
	card->num_of_dapm_widgets = num_widgets;

	return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets);
具体的な設定例

Linux 6.1.8 の時点で widgets をdtb から DAPM に登録する関数を使用している HardKernel 社の ODROID ボードが搭載する MAX98090 という Codec のカーネルドキュメントを見てみましょう。

samsung Codroid.txt

Samsung Exynos Odroid XU3/XU4 audio complex with MAX98090 codec

Required properties:
・・・
 - samsung,audio-widgets - this property specifies off-codec audio elements
   like headphones or speakers, for details see widgets.txt
 - samsung,audio-routing - a list of the connections between audio
   components;  each entry is a pair of strings, the first being the
   connection's sink, the second being the connection's source;
   valid names for sources and sinks are the MAX98090's pins (as
   documented in its binding), and the jacks on the board

   For Odroid X2:
     "Headphone Jack", "Mic Jack", "DMIC"

   For Odroid U3, XU3:
     "Headphone Jack", "Speakers"

   For Odroid XU4:
     no entries

widgets に指定可能な有効な名前がボード毎に記載されています。

ボード名 widgets に指定可能な有効な名前
Odroid X2 "Headphone Jack", "Mic Jack", "DMIC"
Odroid U3, XU3 "Headphone Jack", "Speakers"
Odroid XU4 なし

ハードウェアマニュアルにて widgets に指定可能な有効な名前を検索してみましたが、見つかりませんでした。ドライバに付属ドキュメントがない場合は、SoC ベンダへの確認が必要になります。

MAX98090

MAX98090 Ultra-Low Power Stereo Audio Codec 19-6492; Rev 4; 11/18 より抜粋

routing の書式

routing は、オーディオコンポーネント間の接続リストで、書式は、以下の通りです。Linux カーネルは、奇数番目の文字列を connection's sink として、偶数番目の文字列を connection's source と認識します。一方が widgets 名、もう一方が Codec のピン名になります。Codec に対する INPUT/OUTPUT によって順番が変わるため注意が必要です。また、有効な名前を指定する必要があります。

" connection's sink", " connection's source"

● connection's sink

source から信号を受信する側です。

● connection's source

信号を sink へ送信する側です。

routing を dtb から DAPM に登録する関数
int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
				   const char *propname)
{
	struct device_node *np = card->dev->of_node;
	int num_routes;
	struct snd_soc_dapm_route *routes;
	int i;

	num_routes = of_property_count_strings(np, propname);
★widgets プロパティの総数を dtb から取得
・・・
	num_routes /= 2;
★2つずつで意味を持つので 2 で割る
・・・
	for (i = 0; i < num_routes; i++) {
		int ret = of_property_read_string_index(np, propname,
						2 * i, &routes[i].sink);
★ connection's sink を dtb から取得
・・・
		ret = of_property_read_string_index(np, propname,
			(2 * i) + 1, &routes[i].source);
★ connection's source を dtb から取得
・・・
	}

	card->num_of_dapm_routes = num_routes; 
	card->of_dapm_routes = routes; ★ dapm に登録

	return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing);
具体的な設定例

上記で例に挙げた NXP 社の SGTL5000 のカーネルドキュメントを見てみましょう。 有効な名前に対してのみ電源が供給されると記述されているとともに、有効な名前がリストアップされています。

imx-audio-sgtl5000.txt

Freescale i.MX audio complex with SGTL5000 codec

Required properties:

・・・
- audio-routing	: A list of the connections between audio components.
		Each entry is a pair of strings, the first being the
		connection's sink, the second being the connection's
		source. Valid names could be power supplies, SGTL5000
		pins, and the jacks on the board:

		Power supplies:
		* Mic Bias

		SGTL5000 pins:
		* MIC_IN
		* LINE_IN
		* HP_OUT
		* LINE_OUT

		Board connectors:
		* Mic Jack
		* Line In Jack
		* Headphone Jack
		* Line Out Jack
		* Ext Spk

Example:

sound {
	compatible = "fsl,imx51-babbage-sgtl5000",
		     "fsl,imx-audio-sgtl5000";
	model = "imx51-babbage-sgtl5000";
	ssi-controller = <&ssi1>;
	audio-codec = <&sgtl5000>;
	audio-routing =
		"MIC_IN", "Mic Jack",
		"Mic Jack", "Mic Bias",
		"Headphone Jack", "HP_OUT";
	mux-int-port = <1>;
	mux-ext-port = <3>;
};

Example では以下のマイクとヘッドフォンをサポートする設定になっています。

connection's sink connection's source 信号の流れる方向
MIC_IN Mic Jack
Mic Jack Mic Bias
Headphone Jack HP_OUT

ハードウェアマニュアルに記載の構成図と比較すると、INPUT/OUTPUT の方向と一致することが確認できます。

Figure1.SGTL5000 Simplified Application Diagramその2

NXP Semiconductors Data Sheet: Technical Data Document Number: SGTL5000 Rev. 7, 1/2022 より抜粋

また、ハードウェアマニュアルの Pin 定義を確認すると、上記構成図の通り、HP_R と HP_L に分かれていますが、routing の source には 2 つをまとめた HP_OUT が有効な名前のようです。Pin 定義と一致しない場合がありますので、注意が必要です。ドライバに付属ドキュメントがない場合は、SoC ベンダへの確認が必要になります。

Table1.SGTL5000 Pin Definitions

NXP Semiconductors Data Sheet: Technical Data Document Number: SGTL5000 Rev. 7, 1/2022 より抜粋

なお、SGTL5000 の widgets の設定は、以下の通りソースコードにハードコーディングされています。関数名が dts で指定する templeate-wname に対応し、第一引数が dts で指定する user-supplied-wname に対応します。

static const struct snd_soc_dapm_widget imx_sgtl5000_dapm_widgets[] = {
	SND_SOC_DAPM_MIC("Mic Jack", NULL),
	SND_SOC_DAPM_LINE("Line In Jack", NULL),
	SND_SOC_DAPM_HP("Headphone Jack", NULL),
	SND_SOC_DAPM_SPK("Line Out Jack", NULL),
	SND_SOC_DAPM_SPK("Ext Spk", NULL),
};

まとめ

本ブログエントリでは、デバイスツリーとはどのようなものかと 1 事例としてサウンドカードの widgets と routing プロパティの設定方法について紹介しました。

  • デバイスツリー は元々 OpenFirmware と呼ばれるファームウェアにおいて OS にシステム情報を受け渡す手段として作られた仕組み。
  • u-boot や Linux カーネルでは OpenFirmware から デバイスツリーのデータフォーマットのみを取り入れ、これを fdt(Flattened デバイスツリー) と呼んでいる。
  • 組込みシステムでは、デバイスツリーを採用することによって以下のメリットがある。
    • ファームウェアにおけるハードウェアの表現が統一されプラットフォーム向けの実装がしやすくなる。
    • マルチプラットフォームに対応した Linux カーネルイメージを構築しやすくなる。
    • カーネルやドライバからプラットフォームやボード依存のコードを減らすことができる。
    • OS は、PCI や USB のような自動認識するデバイスでなくても、デバイスツリーの定義でシステムに必要なデバイスを特定することができる。
    • ハードウェアリヴィジョンの更新によるデバイスドライバのソースコード肥大化を抑制することができる。
  • サウンドの widget, routing プロパティは、将来なくなる可能性がある。ハードウェアに強く依存するため、ソースコードにハードコーディングするようになってきている。
  • widget, routing プロパティに指定する名前は、有効なものでなければならい。ドライバ付属ドキュメントに記載がない場合は、SoC ベンダに確認が必要。

Linux カーネルは、現在 6.x 系まで進化し、搭載機能は、ユーザのニーズなどにより、バージョンアップとともに淘汰されてきています。今回のブログ原稿執筆によって、サウンドの widget, routing プロパティがレガシーな設定になりつつあることを知り、最終的にはソースコードを読まないと内容把握できない Linux カーネル含めた OSS の難しさを痛感しました。

弊社には、Linux カーネルに熟達したエンジニアが多数おります。お困りごとがありましたら、お力になれるかもしれませんので、遠慮なくセールスの方へお問い合わせください。

参考文献

組込みLinuxセキュリティ基礎講座
Vigiles サポート
Yocto Project よもやま話
Yocto よもやま話 第 14 回 「Yocto 4.3 Nanbield リリース」
Yocto よもやま話 第 14 回 「Yocto 4.3 Nanbield リリース」

2024 年 03 月 26 日 Yocto Project よもやま話

Yocto よもやま話 第 13 回 「Yocto Project の最新動向 2023 夏」
Yocto よもやま話 第 13 回 「Yocto Project の最新動向 2023 夏」

2023 年 07 月 25 日 Yocto Project よもやま話

Yocto よもやま話 第 12 回 「Yocto Project 始めます その 2」
Yocto よもやま話 第 12 回 「Yocto Project 始めます その 2」

2023 年 06 月 20 日 Yocto Project よもやま話

Linux 技術ネタ
RISC-Vについて学ぶ-後編
RISC-Vについて学ぶ-後編

2024 年 01 月 10 日 Linux 技術ネタ

RISC-Vについて学ぶ-前編
RISC-Vについて学ぶ-前編

2023 年 12 月 12 日 Linux 技術ネタ

イベントレポート
EdgeTech+ West 2024 出展レポート
EdgeTech+ West 2024 出展レポート

2024 年 07 月 26 日 イベントレポート

Advanced Technology Forum 2024 Summer 参加レポート
Advanced Technology Forum 2024 Summer 参加レポート

2024 年 07 月 09 日 イベントレポート

リクルート
新卒採用、絶賛募集中!
新卒採用、絶賛募集中!

2023 年 05 月 30 日 リクルート

国立大学オンライン研修レポート 2022
国立大学オンライン研修レポート 2022

2022 年 09 月 27 日 リクルート

信州リネオ便り
リネオに雪が積もりました
リネオに雪が積もりました

2024 年 11 月 29 日 信州リネオ便り

社内BBQを開催しました!
社内BBQを開催しました!

2024 年 09 月 25 日 信州リネオ便り

夏季休業明け、スイカを頂きました!
夏季休業明け、スイカを頂きました!

2024 年 08 月 20 日 信州リネオ便り

ソリューション統括部
シリコンバレー探検記 2019 ~番外編~
シリコンバレー探検記 2019 ~番外編~

2019 年 12 月 10 日 ソリューション統括部

シリコンバレー探検記 2019 ~後編~
シリコンバレー探検記 2019 ~後編~

2019 年 12 月 10 日 ソリューション統括部

シリコンバレー探検記 2019 ~前編~
シリコンバレー探検記 2019 ~前編~

2019 年 12 月 10 日 ソリューション統括部

マーケティング統括部
大成功決起大会!!(ET2019)
大成功決起大会!!(ET2019)

2019 年 12 月 13 日 マーケティング統括部

ESEC 2019 決起大会
ESEC 2019 決起大会

2019 年 04 月 25 日 マーケティング統括部

シリコンバレー探検記 その 2
シリコンバレー探検記 その 2

2018 年 12 月 18 日 マーケティング統括部