1const MBOX_BASE: usize = 0x3F00_B880;
2
3const MBOX_READ: *mut u32 = MBOX_BASE as *mut u32;
4const MBOX_STATUS: *mut u32 = (MBOX_BASE + 0x18) as *mut u32;
5const MBOX_WRITE: *mut u32 = (MBOX_BASE + 0x20) as *mut u32;
6
7const MBOX_FULL: u32 = 0x8000_0000;
8const MBOX_EMPTY: u32 = 0x4000_0000;
9const MBOX_RESPONSE: u32 = 0x8000_0000;
10
11pub const MBOX_CH_PROP: u32 = 8;
12
13pub struct Mailbox;
14
15impl Mailbox {
16 pub fn call(buffer: &mut [u32], channel: u32) -> Result<(), &'static str> {
17 let addr = buffer.as_ptr() as u32;
18
19 if (addr & 0xF) != 0 {
21 return Err("mailbox buffer not 16-byte aligned");
22 }
23
24 crate::kernel::mmu::clean_dcache_range(addr as usize, buffer.len() * 4);
27
28 let data = (addr & !0xF) | (channel & 0xF);
30
31 unsafe {
32 while (core::ptr::read_volatile(MBOX_STATUS) & MBOX_FULL) != 0 {}
34 core::ptr::write_volatile(MBOX_WRITE, data);
36
37 loop {
38 while (core::ptr::read_volatile(MBOX_STATUS) & MBOX_EMPTY) != 0 {}
40 let res = core::ptr::read_volatile(MBOX_READ);
42 if res == data {
44 crate::kernel::mmu::invalidate_dcache_range(addr as usize, buffer.len() * 4);
46 if buffer[1] == MBOX_RESPONSE {
48 return Ok(());
49 } else {
50 return Err("mailbox response code indicates failure");
51 }
52 }
53 }
54 }
55 }
56}
57
58#[repr(C, align(16))]
60pub struct FrameBufferRequest {
61 pub size: u32,
62 pub magic: u32,
63 pub tags: [u32; 30],
64}
65
66pub struct FrameBufferInfo {
67 pub pointer: *mut u32,
68 pub width: u32,
69 pub height: u32,
70 pub pitch: u32,
71 pub virtual_height: u32, pub size: u32, }
74
75impl FrameBufferRequest {
76 pub fn new(width: u32, height: u32) -> Self {
77 Self {
78 size: 128, magic: 0, tags: [
81 0x0004_8003,
83 8,
84 0,
85 width,
86 height,
87 0x0004_8004,
90 8,
91 0,
92 width,
93 height * 2,
94 0x0004_8005,
96 4,
97 0,
98 32,
99 0x0004_8006,
101 4,
102 0,
103 1,
104 0x0004_0001,
106 8,
107 0,
108 4096,
109 0,
110 0x0004_0008,
112 4,
113 0,
114 0,
115 0,
117 0,
118 0,
119 ],
120 }
121 }
122
123 pub fn get_info(&self) -> Option<FrameBufferInfo> {
124 let pointer = self.tags[21]; let size = self.tags[22];
133 let pitch = self.tags[26]; let width = self.tags[3];
135 let height = self.tags[4];
136 let virtual_height = self.tags[9]; if pointer != 0 {
139 let cpu_ptr = (pointer & 0x3FFF_FFFF) as *mut u32;
140 Some(FrameBufferInfo {
141 pointer: cpu_ptr,
142 width,
143 height,
144 pitch,
145 virtual_height,
146 size,
147 })
148 } else {
149 None
150 }
151 }
152}
153
154pub fn power_on_usb() -> Result<(), &'static str> {
157 #[repr(C, align(16))]
158 struct UsbPowerMsg {
159 size: u32,
160 req: u32,
161 tag: u32,
162 buf_size: u32,
163 req_code: u32,
164 device_id: u32,
165 state: u32,
166 end_tag: u32,
167 }
168
169 let mut msg = UsbPowerMsg {
170 size: core::mem::size_of::<UsbPowerMsg>() as u32,
171 req: 0,
172 tag: 0x0002_8001, buf_size: 8,
174 req_code: 8,
175 device_id: 3, state: 3, end_tag: 0,
178 };
179
180 let buffer = unsafe {
181 core::slice::from_raw_parts_mut(
182 &mut msg as *mut _ as *mut u32,
183 core::mem::size_of::<UsbPowerMsg>() / 4,
184 )
185 };
186 Mailbox::call(buffer, MBOX_CH_PROP)?;
187
188 if (buffer[6] & 1) == 0 {
190 return Err("usb power state did not turn on");
191 }
192 Ok(())
193}
194
195pub fn set_virtual_offset(x: u32, y: u32) -> Result<u32, &'static str> {
199 #[repr(C, align(16))]
200 struct OffsetMsg {
201 size: u32,
202 req: u32,
203 tag: u32,
204 buf_size: u32,
205 req_code: u32,
206 x: u32,
207 y: u32,
208 end_tag: u32,
209 }
210
211 let mut msg = OffsetMsg {
212 size: core::mem::size_of::<OffsetMsg>() as u32,
213 req: 0,
214 tag: 0x0004_8009, buf_size: 8,
216 req_code: 8,
217 x,
218 y,
219 end_tag: 0,
220 };
221
222 let buffer = unsafe {
223 core::slice::from_raw_parts_mut(
224 &mut msg as *mut _ as *mut u32,
225 core::mem::size_of::<OffsetMsg>() / 4,
226 )
227 };
228 crate::kernel::mmu::clean_dcache_range(
229 buffer.as_ptr() as usize,
230 core::mem::size_of::<OffsetMsg>(),
231 );
232 Mailbox::call(buffer, MBOX_CH_PROP)?;
233 Ok(msg.y)
234}
235
236pub fn enable_v3d() -> Result<(), &'static str> {
237 #[repr(C, align(16))]
238 struct V3dEnableMsg {
239 size: u32,
240 req: u32,
241
242 tag_enable: u32,
244 tag_enable_buf_size: u32,
245 tag_enable_req: u32,
246 enable_val: u32,
247
248 tag_clock: u32,
250 tag_clock_buf_size: u32,
251 tag_clock_req: u32,
252 clock_id: u32,
253 rate: u32,
254 skip_turbo: u32,
255
256 end_tag: u32,
257 }
258
259 let mut msg = V3dEnableMsg {
260 size: core::mem::size_of::<V3dEnableMsg>() as u32,
261 req: 0,
262
263 tag_enable: 0x0003_0012,
264 tag_enable_buf_size: 4,
265 tag_enable_req: 4,
266 enable_val: 1, tag_clock: 0x0003_8002,
269 tag_clock_buf_size: 12,
270 tag_clock_req: 12,
271 clock_id: 5, rate: 250_000_000, skip_turbo: 0,
274
275 end_tag: 0,
276 };
277
278 let buffer = unsafe {
279 core::slice::from_raw_parts_mut(
280 &mut msg as *mut _ as *mut u32,
281 core::mem::size_of::<V3dEnableMsg>() / 4,
282 )
283 };
284
285 crate::kernel::mmu::clean_dcache_range(
287 buffer.as_ptr() as usize,
288 core::mem::size_of::<V3dEnableMsg>(),
289 );
290
291 Mailbox::call(buffer, MBOX_CH_PROP)
292}
293
294pub fn wait_vsync() -> Result<(), &'static str> {
295 #[repr(C, align(16))]
296 struct VsyncMsg {
297 size: u32,
298 req: u32,
299 tag: u32,
300 buf_size: u32,
301 req_code: u32,
302 end_tag: u32,
303 }
304
305 let mut msg = VsyncMsg {
306 size: core::mem::size_of::<VsyncMsg>() as u32,
307 req: 0,
308 tag: 0x0004_0011, buf_size: 0,
310 req_code: 0,
311 end_tag: 0,
312 };
313
314 let buffer = unsafe {
315 core::slice::from_raw_parts_mut(
316 &mut msg as *mut _ as *mut u32,
317 core::mem::size_of::<VsyncMsg>() / 4,
318 )
319 };
320
321 let addr = buffer.as_ptr() as u32;
322
323 crate::kernel::mmu::clean_dcache_range(addr as usize, buffer.len() * 4);
325
326 let data = (addr & !0xF) | (MBOX_CH_PROP & 0xF);
327
328 unsafe {
329 let write_start = crate::kernel::timer::get_system_time_us();
331 while (core::ptr::read_volatile(MBOX_STATUS) & MBOX_FULL) != 0 {
332 if crate::kernel::timer::get_system_time_us().wrapping_sub(write_start) > 50000 {
333 return Err("wait_vsync: write timeout");
334 }
335 core::arch::asm!("dsb sy", "isb", options(nostack));
336 }
337
338 core::ptr::write_volatile(MBOX_WRITE, data);
340
341 let read_start = crate::kernel::timer::get_system_time_us();
343 loop {
344 if crate::kernel::timer::get_system_time_us().wrapping_sub(read_start) > 50000 {
346 return Err("wait_vsync: read timeout");
347 }
348
349 if (core::ptr::read_volatile(MBOX_STATUS) & MBOX_EMPTY) == 0 {
351 let res = core::ptr::read_volatile(MBOX_READ);
352 if res == data {
353 crate::kernel::mmu::invalidate_dcache_range(addr as usize, buffer.len() * 4);
355 if buffer[1] == MBOX_RESPONSE {
356 return Ok(());
357 } else {
358 return Err("wait_vsync: mailbox response code indicates failure");
359 }
360 }
361 }
362 core::arch::asm!("dsb sy", "isb", options(nostack));
363 }
364 }
365}