Skip to main content

atmos/kernel/
v3d.rs

1// v3d.rs - VideoCore IV 3D Engine Driver (Base Initialization)
2#![allow(dead_code)]
3
4use crate::kernel::mailbox;
5use core::ptr::{read_volatile, write_volatile};
6
7const V3D_BASE: usize = 0x3FC00000;
8const V3D_IDENT0: *const u32 = V3D_BASE as *const u32;
9const V3D_IDENT1: *const u32 = (V3D_BASE + 0x004) as *const u32;
10const V3D_IDENT2: *const u32 = (V3D_BASE + 0x008) as *const u32;
11const V3D_CT1CS: *mut u32 = (V3D_BASE + 0x110) as *mut u32; // Control Thread 1 Control and Status
12const V3D_CT1EA: *mut u32 = (V3D_BASE + 0x114) as *mut u32; // Control Thread 1 End Address
13const V3D_CT1CA: *mut u32 = (V3D_BASE + 0x118) as *mut u32; // Control Thread 1 Current Address
14
15/// V3Dエンジンの初期化とハードウェア確認
16pub fn init() -> Result<(), &'static str> {
17    // 1. Mailbox経由でV3Dの電源とクロックを有効化
18    if mailbox::enable_v3d().is_err() {
19        crate::kernel::dma::set_qemu_env(true);
20        return Err("Failed to enable V3D via Mailbox.");
21    }
22
23    // 2. V3Dレジスタが読めるか確認 (IDENT0)
24    let ident0 = unsafe { read_volatile(V3D_IDENT0) };
25
26    if ident0 == 0xDEADBEEF || ident0 == 0 {
27        crate::kernel::dma::set_qemu_env(true);
28        return Err("V3D registers are not accessible (power/clock may be off).");
29    }
30
31    // IDENT0の期待値は 0x02443356 ("V3D" という文字列が含まれている)
32    if ident0 != 0x02443356 {
33        // RPi3ではバージョンが異なる可能性があるため、単に警告するか無視するか
34        println!(
35            "Warning: V3D IDENT0 is 0x{:08X}, expected 0x02443356",
36            ident0
37        );
38    } else {
39        println!(
40            "V3D Engine (VideoCore IV) successfully initialized! (IDENT0: 0x{:08X})",
41            ident0
42        );
43    }
44
45    let ident1 = unsafe { read_volatile(V3D_IDENT1) };
46    let ident2 = unsafe { read_volatile(V3D_IDENT2) };
47    println!("V3D IDENT1: 0x{:08X}, IDENT2: 0x{:08X}", ident1, ident2);
48
49    Ok(())
50}
51
52/// VideoCore IV V3D レンダリングパイプラインを用いた高速ハードウェアクリア
53pub fn v3d_clear(vram_phys: u32, width: u32, height: u32, color: u32) -> Result<(), &'static str> {
54    // 1. V3Dが有効か確認
55    let ident0 = unsafe { read_volatile(V3D_IDENT0) };
56    if ident0 == 0xDEADBEEF || ident0 == 0 {
57        return Err("V3D is not initialized or power domain is off.");
58    }
59
60    // 32バイトアラインされたコントロールリスト用 static バッファ
61    #[repr(C, align(32))]
62    struct AlignBuffer {
63        data: [u8; 4096], // 十分なバッファサイズ
64    }
65    static mut RENDER_CL: AlignBuffer = AlignBuffer { data: [0; 4096] };
66
67    unsafe {
68        let cl = &mut RENDER_CL.data;
69        let mut idx = 0;
70
71        // Command: Clear Colors (Id: 114 / 0x72) (12 bytes)
72        cl[idx] = 114;
73        idx += 1;
74        cl[idx..idx + 4].copy_from_slice(&color.to_le_bytes());
75        idx += 4; // Color 0
76        cl[idx..idx + 4].copy_from_slice(&color.to_le_bytes());
77        idx += 4; // Color 1
78        cl[idx] = 0xFF;
79        cl[idx + 1] = 0xFF;
80        cl[idx + 2] = 0xFF;
81        cl[idx + 3] = 0;
82        idx += 4; // Depth/Stencil
83
84        // Command: Tile Rendering Mode Configuration (Id: 113 / 0x71) (11 bytes)
85        cl[idx] = 113;
86        idx += 1;
87        let vram_bus = crate::kernel::dma::to_bus_address(vram_phys);
88        cl[idx..idx + 4].copy_from_slice(&vram_bus.to_le_bytes());
89        idx += 4;
90        cl[idx..idx + 2].copy_from_slice(&(width as u16).to_le_bytes());
91        idx += 2;
92        cl[idx..idx + 2].copy_from_slice(&(height as u16).to_le_bytes());
93        idx += 2;
94        let flags: u16 = 1 << 2; // RGBA8888 (1 << 2) + Linear Mode (0 << 6)
95        cl[idx..idx + 2].copy_from_slice(&flags.to_le_bytes());
96        idx += 2;
97
98        // Command: Tile Coordinates (Id: 115) & Store Tile Buffer General (Id: 28)
99        // 画面を 64x64 タイル単位に分割して処理
100        let tiles_x = width.div_ceil(64);
101        let tiles_y = height.div_ceil(64);
102
103        for ty in 0..tiles_y {
104            for tx in 0..tiles_x {
105                // Tile Coordinates (0x73) : [tx, ty] (2 bytes)
106                cl[idx] = 115;
107                idx += 1;
108                cl[idx] = tx as u8;
109                idx += 1;
110                cl[idx] = ty as u8;
111                idx += 1;
112
113                // Store Tile Buffer General (Id: 28 / 0x1C) (7 bytes: Opcode 1B + Flags 2B + Addr 4B)
114                cl[idx] = 28;
115                idx += 1;
116                let store_flags: u16 = 1 << 6; // Disable Z/Stencil Buffer Write (Only Color Write)
117                cl[idx..idx + 2].copy_from_slice(&store_flags.to_le_bytes());
118                idx += 2;
119
120                // タイルごとのVRAMオフセット
121                let tile_offset = (ty * 64 * width + tx * 64) * 4;
122                let tile_vram_bus = crate::kernel::dma::to_bus_address(vram_phys + tile_offset);
123                cl[idx..idx + 4].copy_from_slice(&tile_vram_bus.to_le_bytes());
124                idx += 4;
125            }
126        }
127
128        // EOF / Flush (Id: 19 / 0x13)
129        cl[idx] = 19;
130        idx += 1;
131
132        // 3. コントロールリストのキャッシュクリーン
133        let cl_ptr = cl.as_ptr() as usize;
134        crate::kernel::mmu::clean_dcache_range(cl_ptr, idx);
135
136        // 4. V3D Thread 1 がアイドルになるのを待ってから投入
137        while (read_volatile(V3D_CT1CS) & (1 << 5)) != 0 {
138            core::hint::spin_loop();
139        }
140
141        let cl_bus = crate::kernel::dma::to_bus_address(cl_ptr as u32);
142        let cl_end_bus = cl_bus + idx as u32;
143
144        write_volatile(V3D_CT1CA, cl_bus);
145        write_volatile(V3D_CT1EA, cl_end_bus);
146
147        // 5. 完了待ち (ACTIVEビットが0になるまで)
148        let mut timeout = 0;
149        while (read_volatile(V3D_CT1CS) & (1 << 5)) != 0 {
150            core::hint::spin_loop();
151            timeout += 1;
152            if timeout > 10_000_000 {
153                return Err("V3D rendering timeout.");
154            }
155        }
156    }
157
158    Ok(())
159}