概要
前回の続きで、ある程度私なりの使い方が落ち着いてきて、github コードをにも上げましたので簡単に説明を書いておきます。
事前に断っておきますと、正しいやり方でも何でもなく、さしあたって「安直にPLから画像出力表示したい」という欲求から「力技で何とかしました」というメモとなります。
私の環境
- Ultra96-V2 Board がターゲットです
- ikwzm氏のUltraZed/Ultra96/Ultra96-V2 向け Debian GNU/Linux (v2019.2版) ブートイメージを使っております。
- 1920x1080 サイズのモニタ(LCD-MF222XBR)に DP-HDMI変換で接続
DP-HDMI変換はどんなものでもいいわけではなさそうです。私は@basaro_k氏のこちらの記事を参考に探しました。
このDebian環境は非常によくできており、起動した段階で DisplayPort には 1920x1080 サイズで映像出力がなされており、ログインプロンプトが出ております。 おそらくちゃんよプラグアンドプレイでモニタのEDIDが読み込まれたうえで適切な出力設定がなされていると思われます。
DisplayPort の仕組み
ZynqMP の DisplayPort 出力は
- ビデオ系(STREAM1)とグラフィックス系(STREAM2)の2つの入力をブレンドして出力する機能を有している
- ビデオ系はPLからの Live Graphics 入力かテストパターンジェネレータ入力かOFFかを選べる(AV_BUF_OUTPUT_AUDIO_VIDEO_SELECT)
- グラフィックス系はPLからの Live Graphics 入力か DisplayPort DMA 入力かOFFかを選べる(AV_BUF_OUTPUT_AUDIO_VIDEO_SELECT)
- ビデオ系とグラフィックス系とのブレンド率が設定できる(V_BLEND_SET_GLOBAL_ALPHA_REG)
- 映像の垂直同期/水平同期などのタイミングは PLからの入力か、PS側で生成したものかを選べる(AV_BUF_AUD_VID_CLK_SOURCE)
といった特徴があるようで、大まかな構成はUG1085から引用すると
の部分がわかりやすいです。
今回の方針
本来であればLinuxの表示ドライバにお願いしてPLからの出力を出してもらうのがスジです。しかし今回はスジを通さずに、Linux が気づかないうちにこっそり出力を借りて、使い終わったらばれないようにこっそり元に戻すという方針で行きます。
AV_BUF_AUD_VID_CLK_SOURCE を弄るとPL側のタイミングで映像を作れるのですが、どうやら切り替えには再同期が必要なようで、いきなり書き換えるという暴挙に出るとそのあと元に戻してももうコマンドプロンプトは表示されなくなります。
ですので、AV_BUF_AUD_VID_CLK_SOURCE はそのままに、既に出力されている映像フォーマットに合わせてPL側で映像を作るという方針を取っております。
実際の処理
作成したプロジェクト
github に置いております。
DP関連のレジスタにアクセスする
まず、Linux の起動時に DisplayPort がどのように設定されているのか調べます。まずは DisplayPort 周りのレジスタにアクセスするために Device Tree overlay にて UIO をマップしています(乱暴)。 ソースはこちらの
uio_dp { compatible = "generic-uio"; reg = <0xfd4a0000 0x00010000>; };
の部分となります。/dev/mem 経由でも良いのですが、アプロケーションを root で動かさないといけなくなるので、今回は一時的に overlay した UIO にユーザーアクセス権を付ける方法でごまかしました(あまりセキュアではないです)。
現在出ている出力フォーマットを知る
まず Linux起動時に DisplayPort に設定されているレジスタにアクセスします。 UIOへのメモリアクセス方法は私の旧ブログのこのあたりなどを参考にしてください。
下記のレジスタをざっと読みだせば、フォーマット情報をうかがい知ることができるようです。
DP_MAIN_STREAM_HTOTAL 0x00000180 DP_MAIN_STREAM_VTOTAL 0x00000184 DP_MAIN_STREAM_POLARITY 0x00000188 DP_MAIN_STREAM_HSWIDTH 0x0000018C DP_MAIN_STREAM_VSWIDTH 0x00000190 DP_MAIN_STREAM_HRES 0x00000194 DP_MAIN_STREAM_VRES 0x00000198 DP_MAIN_STREAM_HSTART 0x0000019C DP_MAIN_STREAM_VSTART 0x000001A0
現在の設定に合わせてde信号を作る
Zynq のコア設定で Live Video を有効にすると関連のポートが現れます。
ここで、現在モニタに出力中の内容をPLで取得できるのですが今回必要なのは
- dp_video_ref_clk
- dp_video_out_vsync
- dp_video_out_hsync
の3つです。
これは先に取得した内容で動作する出力タイミング時の波形です。 画像を合成するには、内部の合成処理のサイクル分少し早いタイミングで入力側の映像を合わせてやらないといけません。
ですので、ここで出力されるタイミングに同期する形で、少しだけ早いサイクルで de 信号を生成する回路を用意します。 それがこちらです
特に難しいことはやっておらず。WISHBONEバスからレジスタ設定した値でカウントしてdeを生成しております。
ですので、
- DPレジスタを読んでフォーマットを知る
- フォーマットに合わせて少し早いサイクルで de を出す設定をする 15~16サイクルぐらい?(ザ・目分量)
というプログラムをPS側ですれば、いい感じに表示に合う位置に映像を持ってくることができます。
映像表示
生成した映像は、PSに接続するわけですが、この時
- dp_video_in_clk
- dp_live_video_in_pixel1
の2つだけ繋げばいいようです。dp_video_in_clk には dp_video_ref_clk のクロックをそのまま入れています。
そしてこのままだと、ビデオ側の映像はまだブレンドされていませんので
の2つを書き換えます。
そして使い終わったらこの2つのレジスタを元の値に戻しておけば、Linux に気づかれることなく状態復帰できる模様です。
このあたりの一連のPS側のプログラムはこちらになります。
動作風景
少し発展させて、静止画表示にさらにカメラ画像も追加したプロジェクトの動作風景です。
コマンドプロンプトとアルファブレンドされているのがお分かりいただけるかと思います。静止画を転送した後にカメラを中央に取り込んでいます。
なお、カメラは拙作の変換基板を使って RaspberryPI 用のカメラを繋いでいます(周りに余計なものがいろいろ写ってますが無視してください(笑))