Skip to main content

strat9_kernel/hardware/amdgpu/
mod.rs

1//! Minimal AMDGPU driver for boot display.
2//!
3//! Provides basic framebuffer output on AMD GPUs (GCN/RDNA) for
4//! real hardware bring-up. Supports only linear scanout (no 3D,
5//! no acceleration, no display pipeline management).
6
7pub mod regs;
8
9use alloc::vec::Vec;
10use core::sync::atomic::{AtomicBool, Ordering};
11use spin::Mutex;
12
13use crate::{
14    arch::x86_64::pci,
15    memory::{allocate_zeroed_frame, PhysFrame},
16};
17
18/// ATI/AMD display controller vendor ID.
19pub const AMD_VENDOR: u16 = 0x1002;
20
21/// PCI class code for display controllers.
22const CLASS_DISPLAY: u8 = 0x03;
23
24/// MMIO register accessor.
25struct Mmio {
26    base: usize,
27}
28
29impl Mmio {
30    /// # Safety
31    /// `base` must be a valid MMIO-mapped address.
32    unsafe fn new(base: usize) -> Self {
33        Self { base }
34    }
35
36    fn read32(&self, off: u32) -> u32 {
37        unsafe { core::ptr::read_volatile((self.base + off as usize) as *const u32) }
38    }
39
40    fn write32(&self, off: u32, val: u32) {
41        unsafe { core::ptr::write_volatile((self.base + off as usize) as *mut u32, val) }
42    }
43}
44
45/// AMD GPU state.
46pub struct AmdGpu {
47    mmio: Mmio,
48    fb_phys: u64,
49    fb_virt: usize,
50    fb_pitch: u32,
51    fb_width: u32,
52    fb_height: u32,
53    fb_bpp: u32,
54    fb_size: usize,
55    _frames: Vec<PhysFrame>,
56}
57
58static GPU: Mutex<Option<AmdGpu>> = Mutex::new(None);
59static AVAILABLE: AtomicBool = AtomicBool::new(false);
60
61pub fn is_available() -> bool {
62    AVAILABLE.load(Ordering::Relaxed)
63}
64
65pub fn get_gpu() -> Option<spin::MutexGuard<'static, Option<AmdGpu>>> {
66    if AVAILABLE.load(Ordering::Relaxed) {
67        Some(GPU.lock())
68    } else {
69        None
70    }
71}
72
73/// Probe PCI for AMD display devices and initialize.
74pub fn init() {
75    crate::serial_println!("[amdgpu] Probing PCI for AMD display...");
76
77    let devs = pci::probe_all(pci::ProbeCriteria {
78        vendor_id: Some(AMD_VENDOR),
79        class_code: Some(CLASS_DISPLAY),
80        ..pci::ProbeCriteria::any()
81    });
82
83    if devs.is_empty() {
84        crate::serial_println!("[amdgpu] No AMD display device found");
85        return;
86    }
87
88    let dev = &devs[0];
89    crate::serial_println!(
90        "[amdgpu] Found: {:04X}:{:04X} (class {:02X}:{:02X})",
91        dev.vendor_id,
92        dev.device_id,
93        dev.class_code,
94        dev.subclass
95    );
96
97    let bar0 = match dev.read_bar_raw(0) {
98        Some(addr) => addr,
99        None => {
100            crate::serial_println!("[amdgpu] BAR0 not available");
101            return;
102        }
103    };
104
105    let hhdm = crate::memory::hhdm_offset();
106    let mmio = unsafe { Mmio::new((bar0 + hhdm) as usize) };
107
108    let chip_id = mmio.read32(regs::CHIP_ID);
109    crate::serial_println!("[amdgpu] CHIP_ID={:#x}", chip_id);
110
111    let vram_mb = match (chip_id >> 24) & 0xFF {
112        0x26..=0x2D => 1024,
113        0x32..=0x3D => 2048,
114        0x4A..=0x4D => 4096,
115        0x50..=0x56 => 8192,
116        0x66..=0x6C => 8192,
117        0x73..=0x7A => 16384,
118        0x88..=0x8D => 16384,
119        _ => 4096,
120    };
121    crate::serial_println!("[amdgpu] VRAM: {} MB", vram_mb);
122
123    // Allocate framebuffer
124    let (w, h, bpp) = (1024u32, 768u32, 32u32);
125    let pitch = w * (bpp / 8);
126    let fb_size = (pitch * h) as usize;
127    let fb_pages = (fb_size + 4095) / 4096;
128
129    let mut frames = Vec::new();
130    for _ in 0..fb_pages {
131        match allocate_zeroed_frame() {
132            Some(f) => frames.push(f),
133            None => {
134                crate::serial_println!("[amdgpu] FB alloc failed");
135                return;
136            }
137        }
138    }
139
140    let fb_phys = frames[0].start_address.as_u64();
141    let hhdm = crate::memory::hhdm_offset();
142    let fb_virt = (fb_phys + hhdm) as usize;
143
144    let gpu = AmdGpu {
145        mmio,
146        fb_phys: fb_phys as u64,
147        fb_virt,
148        fb_pitch: pitch,
149        fb_width: w,
150        fb_height: h,
151        fb_bpp: bpp,
152        fb_size,
153        _frames: frames,
154    };
155
156    gpu.configure_crtc();
157
158    *GPU.lock() = Some(gpu);
159    AVAILABLE.store(true, Ordering::Relaxed);
160    crate::serial_println!("[amdgpu] Display: {}x{} @ {}bpp", w, h, bpp);
161}
162
163impl AmdGpu {
164    fn configure_crtc(&self) {
165        self.mmio.write32(regs::CRTC_CONTROL, 0);
166        // Brief delay for CRTC disable to take effect
167        for _ in 0..10000 {
168            core::hint::spin_loop();
169        }
170
171        self.mmio
172            .write32(regs::CRTC_FB_BASE_LO, self.fb_phys as u32);
173        self.mmio
174            .write32(regs::CRTC_FB_BASE_HI, (self.fb_phys >> 32) as u32);
175        self.mmio.write32(regs::CRTC_FB_PITCH, self.fb_pitch / 4);
176        self.mmio
177            .write32(regs::CRTC_FB_SIZE, self.fb_width | (self.fb_height << 16));
178        self.mmio.write32(
179            regs::CRTC_DIMENSIONS,
180            self.fb_width | (self.fb_height << 16),
181        );
182        self.mmio.write32(
183            regs::CRTC_H_TOTAL,
184            (self.fb_width + 160 - 1) | ((self.fb_width + 160 - 1) << 16),
185        );
186        self.mmio.write32(
187            regs::CRTC_V_TOTAL,
188            (self.fb_height + 30 - 1) | ((self.fb_height + 30 - 1) << 16),
189        );
190        self.mmio.write32(regs::CRTC_FORMAT, 0);
191        self.mmio.write32(regs::CRTC_CONTROL, 1 << 0);
192        // Brief delay for display pipeline to settle
193        for _ in 0..50000 {
194            core::hint::spin_loop();
195        }
196
197        crate::serial_println!("[amdgpu] CRTC configured");
198    }
199
200    pub fn present_region(&self, _x: u32, _y: u32, _w: u32, _h: u32) {}
201
202    pub fn framebuffer_info(&self) -> (u32, u32, u32, u32) {
203        (
204            self.fb_phys as u32,
205            self.fb_width,
206            self.fb_height,
207            self.fb_pitch,
208        )
209    }
210}