toge's diary

コンピュータ関連の趣味をつらつらと。

LCD-8000UをLinuxで使う(遅まきながら)

お久しぶりです。

ちょっと仕事に没頭しているうちに、DisplayLinkまわりが偉いことになっていてビックリしました。

[Phoronix] DisplayLink Provides USB GPU Support On Linux
[Phoronix] DisplayLink's Frame-buffer and X.Org Drivers
[Phoronix] DisplayLink Linux Driver Continues To Mature

あっさりFrameBufferのモジュールが開発されて、それを使ってはいるもののX Windowのドライバまで提供されているんですね。

ちょっとX Windowは面倒臭そうだったので、FrameBufferを試してみることにしました。

試すのはFrameBufferのモジュールであるudlfb。
バージョンは6/10に出たばかりの0.2.3です。

まずはソースファイルの展開。今回欲しいのはudlfbのソースだけなので、そこだけ解凍します。

% tar xvf ~/down/udlfb-0.2.3_and_xf86-video-displaylink-0.3.tar.gz udlfb                                   
udlfb/
udlfb/README
udlfb/Makefile
udlfb/udlfb.c
udlfb/udlfb.h

で早速コンパイル。モジュールを作成することになるので、Linuxカーネルのヘッダが必要になります。Ubuntuだとlinux-headersとかそこらへんです。

% make
make -C /lib/modules/`uname -r`/build SUBDIRS=/tmp/udlfb modules
make[1]: ディレクトリ `/usr/src/linux-headers-2.6.28-13-generic' に入ります
  Building modules, stage 2.
  MODPOST 1 modules
make[1]: ディレクトリ `/usr/src/linux-headers-2.6.28-13-generic' から出ます

カレントディレクトリにudlfb.koができていればコンパイル成功。早速モジュールをカーネルに組み込みます。

% sudo insmod udlfb.ko                                                                          

まあ、何も言われませんがな。これでフレームバッファが新しくできたことになります。
では早速/dev/fb0をば・・・とすると、全然存在しないでやんの。

% ls /dev/fb*
ls: /dev/fb*にアクセスできません: No such file or directory

「なんでだろう?」と/var/log/messagesを見るとこんなメッセージが。

DisplayLink device attached
ret control msg 0: 4 1501fffffff0
EDID XRES 800 YRES 600
INIT VIDEO 0 800 480
INIT VIDEO 1 1024 768
INIT VIDEO 2 1280 1024
INIT VIDEO 3 1400 1050
udlfb: probe of 2-3.1.1.3:1.0 failed with error -12
usbcore: registered new interface driver udlfb
VMODES initialized

なんか解像度が未対応でエラーになっているっぽい。
解像度が800x480, 1024x768, 1280x1024, 1400x1050にしか対応していないのね。
これでは困るので800x600に対応させてみる。

解像度のキモになるのはudlfb.hで定義されているdlfb_init_modes()

void dlfb_init_modes(void) {
  dlfb_video_modes[0].col = 0;
  memcpy(&dlfb_video_modes[0].hclock, "\x20\x3C\x7A\xC9", 4);
  memcpy(&dlfb_video_modes[0].vclock, "\xF2\x6C\x48\xF9", 4);
  memcpy(&dlfb_video_modes[0].unknown1, "\x70\x53\xFF\xFF\x21\x27", 6);
  dlfb_video_modes[0].xres = 800;
  memcpy(&dlfb_video_modes[0].unknown2, "\x91\xF3\xFF\xFF\xFF\xF9", 6);
  dlfb_video_modes[0].yres = 480;
  memcpy(&dlfb_video_modes[0].unknown3, "\x01\x02\xC8\x19", 4);

  dlfb_video_modes[1].col = 0;
  memcpy(&dlfb_video_modes[1].hclock, "\x36\x18\xD5\x10", 4);
  memcpy(&dlfb_video_modes[1].vclock, "\x60\xA9\x7B\x33", 4);
  memcpy(&dlfb_video_modes[1].unknown1, "\xA1\x2B\x27\x32\xFF\xFF", 6);
  dlfb_video_modes[1].xres = 1024;
  memcpy(&dlfb_video_modes[1].unknown2, "\xD9\x9A\xFF\xCA\xFF\xFF", 6);
  dlfb_video_modes[1].yres = 768;
  memcpy(&dlfb_video_modes[1].unknown3, "\x04\x03\xC8\x32", 4);

  dlfb_video_modes[2].col = 0;
  memcpy(&dlfb_video_modes[2].hclock, "\x98\xF8\x0D\x57", 4);
  memcpy(&dlfb_video_modes[2].vclock, "\x2A\x55\x4D\x54", 4);
  memcpy(&dlfb_video_modes[2].unknown1, "\xCA\x0D\xFF\xFF\x94\x43", 6);
  dlfb_video_modes[2].xres = 1280;
  memcpy(&dlfb_video_modes[2].unknown2, "\x9A\xA8\xFF\xFF\xFF\xF9", 6);
  dlfb_video_modes[2].yres = 1024;
  memcpy(&dlfb_video_modes[2].unknown3, "\x04\x02\x60\x54", 4);

  dlfb_video_modes[3].col = 0;
  memcpy(&dlfb_video_modes[3].hclock, "\x42\x24\x38\x36", 4);
  memcpy(&dlfb_video_modes[3].vclock, "\xC1\x52\xD9\x29", 4);
  memcpy(&dlfb_video_modes[3].unknown1, "\xEA\xB8\x32\x60\xFF\xFF", 6);
  dlfb_video_modes[3].xres = 1400;
  memcpy(&dlfb_video_modes[3].unknown2, "\xC9\x4E\xFF\xFF\xFF\xF2", 6);
  dlfb_video_modes[3].yres = 1050;
  memcpy(&dlfb_video_modes[3].unknown3, "\x04\x02\x1E\x5F", 4);
}

うん、さっぱり分からん数値の羅列ですな。分かるのはxres, yresのところぐらいですが、試しに480のところを600にしてみましたが、画面が乱れて使い物にならん。

さすがに手詰まりかなと思ったのですが、次の貴重な情報が私を再び突き動かします。

もし標準で対応している解像度以外を使いたい場合、libdlo で対応している解像度であれば、ソースを書き換えて対応させることは可能です。

で、tubecable 0.1.4の中のソースコードを参考にして、800x600用の定義を作ることにしました。

% tar xvf ~/down/tubecable_0.1.4.tar.gz
tubecable/
tubecable/Makefile
tubecable/tubecable.h
tubecable/tubecable_demo.cpp
tubecable/tubecable_huffman.bin
tubecable/tubecable.cpp
tubecable/tubecable_decrypt.cpp
tubecable/tubecable_huffman.cpp

見るべきなのはtubecable.hのDL_REG_MODE_...となっている箇所。LCD-8OO0Uなら次の行ですね。

#define DL_REG_MODE_800x600_60   { 0x00,  0x20, 0x3c, 0x7a, 0xc9,  
0x93, 0x60, 0xc8, 0xc7,  0x70, 0x53, 0xff, 0xff, 0x21, 0x27,  0x03, 
0x20,  0x91, 0x8f, 0xff, 0xff, 0xff, 0xf2,  0x02, 0x58,  0x01, 0x02,  
0x40, 0x1f }

これを参考に、dlfb_init_modes()に一つモードを追加してみます。
直すのは2ヶ所、どちらもudlfb.hの中身です。

最初はモードの数を示す定数。今は4になっているので5にしてみます。

#define MAX_VMODES 4

次にdlfb_init_modes()の中身を次みたいに変更します。

void dlfb_init_modes(void) {
  dlfb_video_modes[0].col = 0;
  memcpy(&dlfb_video_modes[0].hclock, "\x20\x3C\x7A\xC9", 4);
  memcpy(&dlfb_video_modes[0].vclock, "\xF2\x6C\x48\xF9", 4);
  memcpy(&dlfb_video_modes[0].unknown1, "\x70\x53\xFF\xFF\x21\x27", 6);
  dlfb_video_modes[0].xres = 800;
  memcpy(&dlfb_video_modes[0].unknown2, "\x91\xF3\xFF\xFF\xFF\xF9", 6);
  dlfb_video_modes[0].yres = 480;
  memcpy(&dlfb_video_modes[0].unknown3, "\x01\x02\xC8\x19", 4);

  dlfb_video_modes[1].col = 0;
  memcpy(&dlfb_video_modes[1].hclock, "\x36\x18\xD5\x10", 4);
  memcpy(&dlfb_video_modes[1].vclock, "\x60\xA9\x7B\x33", 4);
  memcpy(&dlfb_video_modes[1].unknown1, "\xA1\x2B\x27\x32\xFF\xFF", 6);
  dlfb_video_modes[1].xres = 1024;
  memcpy(&dlfb_video_modes[1].unknown2, "\xD9\x9A\xFF\xCA\xFF\xFF", 6);
  dlfb_video_modes[1].yres = 768;
  memcpy(&dlfb_video_modes[1].unknown3, "\x04\x03\xC8\x32", 4);

  dlfb_video_modes[2].col = 0;
  memcpy(&dlfb_video_modes[2].hclock, "\x98\xF8\x0D\x57", 4);
  memcpy(&dlfb_video_modes[2].vclock, "\x2A\x55\x4D\x54", 4);
  memcpy(&dlfb_video_modes[2].unknown1, "\xCA\x0D\xFF\xFF\x94\x43", 6);
  dlfb_video_modes[2].xres = 1280;
  memcpy(&dlfb_video_modes[2].unknown2, "\x9A\xA8\xFF\xFF\xFF\xF9", 6);
  dlfb_video_modes[2].yres = 1024;
  memcpy(&dlfb_video_modes[2].unknown3, "\x04\x02\x60\x54", 4);

  dlfb_video_modes[3].col = 0;
  memcpy(&dlfb_video_modes[3].hclock, "\x42\x24\x38\x36", 4);
  memcpy(&dlfb_video_modes[3].vclock, "\xC1\x52\xD9\x29", 4);
  memcpy(&dlfb_video_modes[3].unknown1, "\xEA\xB8\x32\x60\xFF\xFF", 6);
  dlfb_video_modes[3].xres = 1400;
  memcpy(&dlfb_video_modes[3].unknown2, "\xC9\x4E\xFF\xFF\xFF\xF2", 6);
  dlfb_video_modes[3].yres = 1050;
  memcpy(&dlfb_video_modes[3].unknown3, "\x04\x02\x1E\x5F", 4);

  dlfb_video_modes[4].col = 0;
  memcpy(&dlfb_video_modes[4].hclock, "\x20\x3C\x7A\xC9", 4);
  memcpy(&dlfb_video_modes[4].vclock, "\x93\x60\xC8\xC7", 4);
  memcpy(&dlfb_video_modes[4].unknown1, "\x70\x53\xFF\xFF\x21\x27", 6);
  dlfb_video_modes[4].xres = 800;
  memcpy(&dlfb_video_modes[4].unknown2, "\x91\x8F\xFF\xFF\xFF\xF2", 6);
  dlfb_video_modes[4].yres = 600;
  memcpy(&dlfb_video_modes[4].unknown3, "\x01\x02\x40\x1F", 4);
}

変更したのは最後の10行ぐらいの部分。

後は再度コンパイルして、再度insmodすると・・・

DisplayLink device attached
ret control msg 0: 4 1501fffffff0
EDID XRES 800 YRES 600
INIT VIDEO 0 800 480
INIT VIDEO 1 1024 768
INIT VIDEO 2 1280 1024
INIT VIDEO 3 1400 1050
INIT VIDEO 4 800 600
ret control msg 1 (STD_CHANNEL): 16
ret bulk 2: 156 156
ret bulk 3: 0
found valid mode...20000
screen base allocated !!!
colormap allocated
Console: switching to colour frame buffer device 100x37
usbcore: registered new interface driver udlfb
VMODES initialized

おおっ、なんか動いた感じ。

% ls /dev/fb*
/dev/fb0

ちゃんとFrameBufferも作られてますね。
とりあえず今日はこれで満足します。

せっかくできたFrameBufferを何に使うかは明日以降ゆっくり考えよう・・・。