that are handled as follows.
fd = open("/dev/fb0", O_RDWR);
ioctl(fd, 0, address)
int reg32; ioctl(fd, 1, ®32)
ioctl(fd, 2, reg32)
They contain one video plane, two graphics planes, two alpha planes, and two hardware cursor planes. See SM501_MMCC_Databook pp.(1-12)-(1-13) for details of the video layers. Primary devices with display resolutions of VGA, SVGA, XGA, and SXGA (experimental) are considered in this document.
Each node is treated as a character device. See Documentation/fb/framebuffer.txt for details of the frame buffer. Just like MEM(4) device, a frame buffer is opened by OPEN(2):
fd = open("/dev/fb0", O_RDWR);
and mapped into memory by MMAP(2) to access:
p = (char *) mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
See SM501_MMCC_Databook pp.(1-26)-(1-28) for details of the memory map and register space.
Linuxにおいてビデオメモリは/dev/fb*というデバイスによってアクセスできます。
fbは通常のメモリデバイスで,memmapして読み書きすることができます。
/dev/fb*のさまざまな情報の取得や設定はioctlで行います。
詳しくはinclude/linux/fb.hをみてくださいだそうです。
CeLinuxではビデオを取り扱う枠組みとして伝統的なFrame bufferと,その上に構築されたDirectFBを考えているようです。
最終的にはどちらか一方のみのサポートになるかもしれないそうです。
SM501は
シリコンモーション社の誇る高性能省電力グラフィックコントローラVoyage GXファミリーです。
Linux用のドライバが供給されています。
それとは別にCeLinuxにはkernel組み込みのフレームバッファドライバが提供されているようです。
SM501は7枚のフレームバッファを通じてアクセスされます。
それぞれのフレームバッファは起動時に
によって初期化されます。
IRQなどの低いレイヤーの初期は
によって行われます。
USBについては
です。
SM501固有の機能はメモリにマップされたレジスタを操作することによって利用します。
各レジスタのMMIOは
で定義されています。
レジスタの操作はioctlによって行います。 ioctlを用いたコードをコンパイルするためにはターゲットアーキテクチャのasm以下のヘッダファイルが必要です。
# cp -R celinux/include/asm-sh /usr/local/sh4-linux/include/asm
始めに/dev/fb/0をopenします。
fd = open("/dev/fb/0", O_RDWR);
次に操作対象のレジスタがマップされたメモリアドレスを設定します。
ioctl(fd, 0, address)
レジスタの内容を読み込むには
int reg32; ioctl(fd, 1, ®32)
とします。書き込むには
ioctl(fd, 2, reg32)
とします。
SM501のグラフィック機能はLinux Frame bufferを通じて利用します。
デバイススペシャルファイルは伝統的なスタイルの場合/dev/fb0-dev/fb6,
devfsを用いた場合は/dev/fb/0-/dev/fb/6です。
フレームバッファ番号と対応するレイヤは以下の通りです。
0 | Graphics Layer |
1 | Video Layer |
2 | Video Alpha Layer |
3 | Alpha Layer |
4 | Hardware Cursor |
5 | CRT Graphics Layer |
6 | Hardware Cursor |
7枚のレイヤのうち,5-6とそれ以外は排他です。
CRT display control registaerの第10bitがONのとき5および6が,OFFのときは0-4がそれぞれCRTに表示されます。
Video FBとVideo Alphaだけが拡大機能をもっています。
拡大率はVideo Scale, Vide Initial Scale, およびVideo Alpha Scale Registerによって制御します。
Frame bufferのプログラムのためにはターゲットアーキテクチャのlinux以下のヘッダファイルが必要です。
# cp -R celinux/include/linux /usr/local/sh4-linux/include
SM501のフレームバッファに画像を描画する手順は以下の通りです。
操作するフレームバッファデバイスを開きます。
int fd; fd = open("/dev/fb/0, O_RDWR);
固定情報を取得します。
struct fb_fix_screeninfo finfo; ioctl(fd, FBIOGET_FSCREENINFO, &finfo);
可変情報を取得します。
struct fb_var_screeninfo vinfo; ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);
高さ,幅,色深度を取得します。
int width = vinfo.xres; int height = vinfo.yres; int depth = vinfo.bits_per_pixel;
バイト単位の画像サイズを計算します。
int size = width * height * depth / 8;
フレームバッファのVRAMをメモリにマッピングします。
char *p = (char *) mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
原点座標を取得します。
int ox = vinfo.xoffset; int oy = vinfo.yoffset;
画面上で1ライン分メモリアドレス増分を取得します。
len = finfo.line_length;
画像を描画します。
r:g:b = 5:6:5の16bit direct colorの場合の例です。
for (int j = 0; j < height; ++j) for (int i = 0; i < width; ++i) { int index = (ox + i) * (depth / 8) + (oy + j) * len; *((unsigned short int *)(p * idx)) = (31 << 11) | (127 << 5) | 31; }
マッピングを解除します。
munmap(p, size);
フレームバッファデバイスを閉じます。
close(fd);
RTS7751R2D Hundling Manualページにて配布されているvc.zipを入手。
% sh4-linux-gcc vc.c -o vc
コンパイル成功。
# ./vc IOCTL Address set error
へ? orz
普通にfbにアクセスするコードを書いてみます。
コードはLinux Frame Buffer HOWTOのやつほとんどそのままです。
% sh4-linux-gcc test.c -o test
コンパイル成功。
~ # ./test len 1280 width 640 height 480 depth 16 ox 0 oy 0
正常に実行できました。
video_fbのサイズと色深度は取得できていようです。
が,何も表示されません…orz
まずは何かCRTに表示させてみようということで,consoleをCRTにしてみます。
で起動。シリアルコンソールから起動メッセージが消えました。が
fH 28.7kHz fV 54.8Hz
サポート外…orz
なんの,RTS7751R → ダウンコンバータ → ビデオキャプチャカード → CRTで接続。
おおお。SuperHなペンギンとともに黄色い文字で起動メッセージがCRTに表示されました。
USBキーボードか使用可能です。
オリジナルviewに似たようなインターフェースを持つview.cを実装してみました。
# view 1 1
で/dev/fb1がONになります。
緑色の画面になりました。
consoleのON/OFFもできますねえ。
ただし,相変わらずダウンコーバータ経由ですorz
フレームバッファをopenして,memmap,豆腐を描いてunmemapしみました。
/dev/fb/0に対して実行するとコンソール画面にかさなって豆腐が,
/dev/fb/1に対して実行するとビデオフレームバッファに豆腐が,
それぞれ表示されました。ただし,後者はYUYV形式なので色がへんです。
水平同期周波数が足りない問題はとりあえずキニシナイ。
Power mode 0/1 clock関係のレジスタを操作できるようになりました。
Second PLL frequency: 336 MHz Power mode 0 clock V2XCLK frequency input select: 288 MHz V2XCLK fequencey divider: 6 Dot clock 24.000000 MHz Power mode 1 clock V2XCLK frequency input select: 288 MHz V2XCLK fequencey divider: 6 Dot clock 24.000000 MHz
が,操作してもまったく変化なし…orz
Panel display control: 0000 0111 0000 0001 0011 0011 0000 0101 Panel horizontal total: 831 pixels Panel horizontal display end: 639 pixels Panel horizontal sync width: 74 pixels Panel horizontal sync start: 651 pixels
水平同期周波数が低いのは水平画素数が多すぎるのではないでしょうか。
# ./mod2 0x80024 [0xb3e80024] 0x33f027f : 0000 0011 0011 1111 0000 0010 0111 1111
0x33f = 831を784 = 0x310に変更
~ # ./mod 0x80024 0x310027f [0xb3e80024] 0x310027f : 0000 0011 0001 0000 0000 0010 0111 1111
すると同じドットクロックでも水平同期周波数が向上。
fH: 30.4kHz fV: 58.0Hz
になり,無事CRTに画面が表示されました。ヽ(´ー`)ノ
ふと気づく(←遅い)。
Power Mode 0 Clock (+0x40)ってCRT display controler(+0x80200)の10bitをONにしたときだけ効果があるんじゃないだろうか!
10bit ONでCRT Graphics Layer表示
./mod 0x80200 0x10305
Power Mode 1 Clockの20bit ONでsecond PLLに切り替え
./mod 0x44 0x9091801
その通りでしたヽ(´ー`)ノ。
じゃあPower Mode 1 Clockって何でしょう?
変更しても意味ないようです。
それと,パネル出力のドットクロックはどこで制御しているのでしょう?
あとは気合で計算すれば任意の解像度が実現できる…かも。
CRT display parameters for XGA is set as follows.
Let CRT monitor display CRT buffers.
./mod 0x80200 0x10305
Set window width and offset to 128 bytes = 0x80
./mod 0x80208 0x8000800
Set horizontal total to 1327, and horizontal display end to 1023
./mod 8020c 0x52f03ff
Set horizontal sync width to 135, and horizontal sync start 1047
./mod 0x80210 0x870417
Set vertical total to 805, and vertical display end to 767
./mod 0x80214 0x32502ff
Set vertical sync height to 5, and vertical sync start to 770
./mod 0x80218 0x50302
Set V2XCLK input select to 288MHz, and frequeyncy divider to 2
./mod 0x44 0x29011801
Unfortunately, it can not be accessed as XGA since Frame Buffer driver uses fixed VGA values... org
So the next step is kernel recompile after modification of voyager.h file.
#define VOY_VRAM_TOP0 0xb0000000 //PANEL PLANE 1.5MB #define VOY_VRAM_TOP1 0xb0180000 //VIDEO PLANE 0 1.5MB #define VOY_VRAM_TOP2 0xb0300000 //VIDEO PLANE 1 1.5MB #define VOY_VRAM_TOP3 0xb0480000 //VIDEO ALPHA PLANE 1.5MB #define VOY_VRAM_TOP4 0xb0600000 //ALPHA PLANE 1.5MB #define VOY_VRAM_TOP5 0xb0610000 //PANEL CURSOR PLANE 64KB #define VOY_VRAM_TOP6 0xb0620000 //CRT PLANE 640KB #define VOY_VRAM_TOP7 0xb06c0000 //CRT CURSOR PLANE rest
#define XRES 1024 #define YRES 768
Then, I could successfully show a 1024x768-sized image on my CRT monitor ヽ(´ι _` )ノ
To back to VGA mode, use parameters as follows.
~ # ./mod 0x80208 0x5000500 ~ # ./mod 0x8020c 0x33f027f ~ # ./mod 0x80210 0x4a028b ~ # ./mod 0x80214 0x20c01df ~ # ./mod 0x80218 0x201e9 ~ # ./mod 0x44 0x9191801
Note that you can dynamically change the CRT resolution by setting SM501 registers properly,
though linux frame buffer-related information such as vinfo and finfo is no longer reliable.
You must get necessary information by means of memory mapped registers.
Set the bottom right location of the video plane to (319, 239).
[0xb3e80054] 0x00ef013f : 0000 0000 1110 1111 0000 0001 0011 1111
Then only a rectangle region (0, 0) - (319, 239) is shown.
Set vertical and horizontal scaling factors to two.
# ./mod 0x80058 0x04000400
Then the video content is expanded by a factor of two.
To disable scaling,
# ./mod 0x80058 0x0