Blog:
【三菱ディスプレイ】第3「Linux デバイスツリー」
この5回にわたるブログ記事連載では、LVDSディスプレイの基礎的な概念について説明します。前回の記事では、Apalis iMX8が、LVDS信号を扱う仕組みを説明しました。次に、Linux Device Treeでこのディスプレイを設定する方法について見ていきます。
Device treeについてまだご存知でない場合は、次のドキュメントを読むことを強くお勧めします。
https://developer.toradex.com/device-tree-customization
Device treeとは、簡単に言えばハードウェア機器を定義するソフトウェアアーティファクトにほかなりません。CPUコア、速度、電圧からGPIOピンのpinmuxまであらゆるものを含みます。
- ツールチェーンを準備(まだ準備していなかった場合)
- カーネルソースをダウンロード
- Device treeにディスプレイのタイミング要件を追加(device treeはカーネルソースに含まれています)
- ソースを再コンパイル
- 新しいバイナリをフラッシュ(バイナリは複数の場合あり)
最低限のコンパイル環境(必要なツールチェーンを含む)があること、この分野とコンパイルの経験が多少あること、最低なんらかのデフォルト構成が利用できるようにすることを推奨します。もしそれらが当てはまらない場合は、次のリンクを参照してください。
Apalis iMX6(または古い32ビットモジュール)の場合:
https://developer.toradex.com/knowledge-base/build-u-boot-and-linux-kernel-from-source-code
Apalis iMX8(64ビット、SECO、SCUなど)の場合:
https://developer.toradex.com/software/linux/linux-software/build-apalis-imx8-boot-imagelinux-from-scratch
ここで、注意すべきことが1つあります。これらのdevice treeの変更が必要なのは、古いEmbedded Linuxの標準バージョンのみです。Toradexの新しいTorizon OSでは、より簡単に動的なハードウェア変更が可能なDevice Tree Overlays(DTO)アーキテクチャを利用できます。これについては後で説明します。
メーカーの意図通りにディスプレイを使用するためには、お使いのディスプレイがどのようにピクセルを描き、フレームの同期を行うかを理解することが必要不可欠です。しかし、ソリューションが使用するソフトウェアAPIにこれらの値を反映させることも同様に重要です。今回使用するAPIはLinux display timing APIです。これらの値は、使用するハードウェア定義アーティファクト、device tree内に定義されます。
幸い、カーネルのドキュメントには各パラメータが非常に詳しく説明されており、組込みシステムを定義するdevice treeにディスプレイのタイミング値を反映させることは難しくはありません。
https://www.kernel.org/doc/Documentation/devicetree/bindings/display/panel/display-timing.txt
7インチの三菱のディスプレイを使って説明を続けます。上記のタイミング定義で「clock-frequency」、「hactive」、「vactive」は非常にわかりやすいものです。
「clock-frequency」は、通常の値でまったく問題ありません。このケースでは30.4MHzで、「clock-frequency = <30400000>;」という値になります。
「hactive」および「vactive」は、アクティブなディスプレイ期間(つまりディスプレイの解像度)を表すため、これらの値もそれぞれ「800」および「480」と簡単に定義できます。
さてここで、ブランク期間を扱わなくてはなりません。これは、Linuxでは「porch」および「len」という値で、これらの値にブランク期間を反映させることになります。しかし、これらの値は非常にオープンなため、何度も異なる値を使って試行することが可能であり、また、試行する必要があります。
これまでの経験上、アクティブ時間の前後の時間要件について特にディスプレイの製造元から特別な要件がない限り、通常はこれらの値をブランク期間の合計として設定すればよく、これについてはある程度の裁量が与えられています。
今回は、水平方向ブランクは160 pclkほどで、垂直方向は80 Hであり、双方とも上限はありません。ご存知のように、ブランク期間は、前後の非アクティブディスプレイ時間を合計したものです。
このため、水平方向ブランクの値には、hback-porch、hfront-porch、hsync-lenを合計します。垂直方向ブランクの値は、vback-porch、vfront-porch、vsync-lenとなります。
APIドキュメントを確認すると、水平値はピクセルでの測定、垂直ラインは水平ライン期間での測定で、データシートにあるディスプレイタイミング値とまったく同じです。
このため上のディスプレイを動作させるのに有効な定義は複数ありますが、試行錯誤を繰り返してもっともよく動作する設定を確認したところ、次の設定にたどりつきました。
/* Mitsubishi 7", WVGA" */ mitsubishi_7: 800x480 { clock-frequency = <30400000>; //30.4 MHZ hactive = <800>; vactive = <480>; hback-porch = <112>; hfront-porch = <32>; vback-porch = <3>; vfront-porch = <17>; hsync-len = <80>; vsync-len = <4>; hsync-active = <0>; vsync-active = <0>; pixelclk-active = <0>; };
タイミングについては、仕様の枠内で試行を繰り返してみてください。また、これまでの経験上、タイミングを切り替えた当初に画面がちらつくことがあっても、しばらくすると安定してきます。異なるタイミングをいくつも試す場合は、このことを念頭に置いてください。
さて、これを適用する場所ですが、Apalis iMX6 BSPには現在ブランチが2つあります。
2.8 (stable): toradex_4.9-2.3.x-imx 3.0 (dev): toradex_4.14-2.0.x-imx
Apalis iMX8用には、現在、開発ブランチしかありません。
3.0:toradex_imx_4.14.78_1.0.0_ga-bring_up
Apalis iMX6: arch/arm/boot/dts/imx6qdl-apalis.dtsi ... &ldb { status = "okay"; lvds-channel@0 { reg = <0>; fsl,data-mapping = "spwg"; /* "jeida"; */ fsl,data-width = <24>; //Selected by pin 19 of the LVDS connector crtc = "ipu2-di1"; primary; status = "okay"; display-timings { native-mode = <&mitsubishi_7>; /* Mitsubishi 7", Wide - VGA (800x480)" */ /* AA070ME01ADA11 */ mitsubishi_7: 800x480 { clock-frequency = <30400000>; //30.4 MHZ hactive = <800>; vactive = <480>; hback-porch = <112>; hfront-porch = <32>; vback-porch = <3>; vfront-porch = <17>; hsync-len = <80>; vsync-len = <4>; hsync-active = <0>; vsync-active = <0>; pixelclk-active = <0>; }; /* Mitsubishi 12.1", Wide - XGA (1280x800)" */ /* AA121TD11-DE1 */ mitsubishi_12_1: m_1280x800 { clock-frequency = <71000000>; //71 MHZ hactive = <1280>; vactive = <800>; hback-porch = <64>; hfront-porch = <64>; vback-porch = <5>; vfront-porch = <5>; hsync-len = <40>; vsync-len = <6>; hsync-active = <0>; vsync-active = <0>; pixelclk-active = <0>; }; /* Other display definitions */ ... }; }; }; }; ... Apalis iMX8: arch/arm64/boot/dts/freescale/fsl-imx8qm-apalis.dtb &ldb2 { status = "okay"; lvds-channel@0 { fsl,data-mapping = "spwg"; fsl,data-width = <24>; status = "okay"; primary; display-timings { native-mode = <&mitsubishi>; mitsubishi_7: 800x480 { clock-frequency = <30400000>; //30.4 MHZ hactive = <800>; vactive = <480>; hback-porch = <112>; hfront-porch = <32>; vback-porch = <3>; vfront-porch = <17>; hsync-len = <80>; vsync-len = <4>; hsync-active = <0>; vsync-active = <0>; pixelclk-active = <0>; }; }; port@1 { reg = <1>; lvds1_out: endpoint { remote-endpoint = <&panel_lvds1_in>; }; }; }; };
さらに、必要のないディスプレイ定義(BSPにデフォルトで追加された定義)を削除したり、native-mode変数を介してデフォルト定義を選択したりすることも可能です。
カラーマッピングについては、下記に異なるモードについての詳しい説明があります。この例においては、三菱のディスプレイではピン19をカラーマッピングのセレクタとして使用するため、ここでは、それを直接VCCに接続します(最後にピンアウトのドキュメント全体について説明します)。このピンでは、18ビットモードか24ビットモードかを選択します。
もっと高度な手法としては、このピンをGPIOに割り当てることも可能ですが、ある程度複雑な構成となるため、このドキュメントでは割愛します。
U-bootブートローダーが許可するビデオ因数についての情報とi.MXのフレームバッファの詳細については、次のドキュメントを参照してください。
https://developer.toradex.com/knowledge-base/display-output-resolution-and-timings-linux#iMX_6_based_modules
Ubootはデフォルトでは、メインのビデオ出力としてHDMIで出力するようビデオ因数を設定します。上のリファレンスガイドに従い、メインのフレームバッファ出力をLVDS(ldb)に設定しなければなりません。
まず、UART A シリアル接続を介していずれかのキーを押すことで、U-bootの自動起動を停止します。
すでにdevice tree内で三菱ディスプレイの解像度をデフォルトで選択した場合、ビデオデバイスとしてLVDSを定義すればこれが自動的に選択されるのですが、次のように直接選択することもできます。
setenv vidargs "video=mxcfb0:dev=ldb,lvds-channel@0,m_1280x800 video=mxcfb1:off video=mxcfb2:off video=mxcfb3:off fbmem=512M"
setenv vidargs "video=mxcfb0:dev=ldb,lvds-channel@0,m_1280x800 video=mxcfb1:off video=mxcfb2:off video=mxcfb3:off fbmem=512M"
最後に、変更を保存してモジュールをLinuxで起動し、変更内容が正常に適用されたことを確認します。
saveenv boot
本記事では、Linux Device TreeでLCDディスプレイを設定する方法について説明しました。次の記事では、Device Tree Overlaysについて見ていきます。これはdevice tree全体を再コンパイルせずに全体的なdevice treeを変更する方法です。