Skip to main content

atmos/kernel/
dma.rs

1// dma.rs - BCM2837 DMA Controller Driver
2#![allow(dead_code)]
3
4use core::ptr::{read_volatile, write_volatile};
5
6static mut IS_QEMU_ENV: bool = false;
7
8pub fn set_qemu_env(qemu: bool) {
9    unsafe {
10        IS_QEMU_ENV = qemu;
11    }
12}
13
14pub fn is_qemu_env() -> bool {
15    unsafe { IS_QEMU_ENV }
16}
17
18const DMA_BASE: usize = 0x3F007000;
19const DMA_ENABLE: usize = 0x3F007FF0;
20
21// BCM2837の物理アドレスをGPU側のアンキャッシュバスアドレスに変換する
22pub fn to_bus_address(phys_addr: u32) -> u32 {
23    phys_addr | 0xC0000000
24}
25
26#[repr(C, align(32))]
27#[derive(Debug, Clone, Copy)]
28pub struct DmaControlBlock {
29    pub ti: u32,
30    pub source_ad: u32,
31    pub dest_ad: u32,
32    pub txfr_len: u32,
33    pub stride: u32,
34    pub nextconbk: u32,
35    pub _reserved: [u32; 2],
36}
37
38/// 単純なメモリーコピー (CPU直接コピー版)
39/// 実機DMAバスのキャッシュコヒーレンシ問題を回避するため、恒久的にCPU転送を使用します。
40pub fn dma_memcpy(_channel: usize, src_phys: u32, dst_phys: u32, len_bytes: u32) {
41    if len_bytes == 0 {
42        return;
43    }
44
45    unsafe {
46        core::ptr::copy_nonoverlapping(
47            src_phys as *const u8,
48            dst_phys as *mut u8,
49            len_bytes as usize,
50        );
51    }
52}
53
54/// 矩形塗りつぶし (CPU直接塗りつぶし版)
55/// 実機DMAバスのキャッシュコヒーレンシ問題を回避するため、恒久的にCPU転送を使用します。
56pub fn dma_fill_rect(
57    _channel: usize,
58    color_phys: u32,
59    dst_phys: u32,
60    width: u32,
61    height: u32,
62    pitch: u32,
63) {
64    if width == 0 || height == 0 {
65        return;
66    }
67
68    unsafe {
69        let color_val = *(color_phys as *const u32);
70        let dst_ptr = dst_phys as *mut u32;
71        let stride = pitch / 4;
72        for y in 0..height {
73            let row = dst_ptr.add((y * stride) as usize);
74            let slice = core::slice::from_raw_parts_mut(row, width as usize);
75            slice.fill(color_val);
76        }
77    }
78}
79
80/// 矩形メモリコピー (CPU直接コピー版)
81/// 実機DMAバスのキャッシュコヒーレンシ問題を回避するため、恒久的にCPU転送を使用します。
82pub fn dma_memcpy_2d(
83    _channel: usize,
84    src_phys: u32,
85    dst_phys: u32,
86    width_bytes: u32,
87    height: u32,
88    src_pitch: u32,
89    dst_pitch: u32,
90) {
91    if width_bytes == 0 || height == 0 {
92        return;
93    }
94
95    unsafe {
96        let src_ptr = src_phys as *const u8;
97        let dst_ptr = dst_phys as *mut u8;
98        for y in 0..height {
99            let src_row = src_ptr.add((y * src_pitch) as usize);
100            let dst_row = dst_ptr.add((y * dst_pitch) as usize);
101            core::ptr::copy_nonoverlapping(src_row, dst_row, width_bytes as usize);
102        }
103    }
104}
105
106fn execute_dma(channel: usize, cb: &DmaControlBlock) {
107    let ch_base = DMA_BASE + channel * 0x100;
108    let cb_bus = to_bus_address(cb as *const _ as u32);
109
110    unsafe {
111        // 1. DMAチャネル有効化
112        let mut enable = read_volatile(DMA_ENABLE as *const u32);
113        enable |= 1 << channel;
114        write_volatile(DMA_ENABLE as *mut u32, enable);
115
116        // 2. 終了フラグのクリア
117        write_volatile(ch_base as *mut u32, 1 << 1); // clear END flag
118
119        // 3. CBアドレスをセット
120        write_volatile((ch_base + 0x04) as *mut u32, cb_bus);
121
122        // 4. ACTIVE を設定して開始
123        write_volatile(ch_base as *mut u32, 1);
124
125        // 5. 完了待ち (タイムアウト付き)
126        let mut wait_count = 0;
127        while read_volatile(ch_base as *const u32) & (1 << 1) == 0 {
128            core::hint::spin_loop();
129            wait_count += 1;
130            if wait_count > 10_000_000 {
131                // タイムアウト発生
132                // 強制リセット
133                write_volatile(ch_base as *mut u32, 1 << 31);
134                crate::println!(
135                    "DMA TIMEOUT on channel {}! wait_count={}",
136                    channel,
137                    wait_count
138                );
139                break;
140            }
141        }
142
143        // 終了時にフラグをクリア
144        write_volatile(ch_base as *mut u32, 1 << 1);
145
146        // DMAが DDR に書き込んだデータを CPU が見えるようにするためのメモリバリア
147        core::arch::asm!("dsb sy", options(nostack));
148    }
149}