xref: /aosp_15_r20/external/mesa3d/src/nouveau/headers/nv_push_rs/lib.rs (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 // Copyright © 2024 Collabora, Ltd.
2 // SPDX-License-Identifier: MIT
3 
4 use nvidia_headers::ArrayMthd;
5 use nvidia_headers::Mthd;
6 
7 pub const MAX_MTHD_LEN: u16 = 0x1fff;
8 pub const MAX_MTHD_ADDR: u16 = 0x7fff;
9 
class_to_subc(class: u16) -> u810 fn class_to_subc(class: u16) -> u8 {
11     match class & 0xff {
12         0x97 => 0,
13         0xc0 => 1,
14         0x39 => 2,
15         0x2d => 3,
16         0xb5 => 4,
17         _ => panic!("Invalid class: {class}"),
18     }
19 }
20 
21 #[repr(u8)]
22 enum MthdType {
23     /// Each dword increments the address by one
24     NInc = 1,
25     /// The first dword increments the address by one
26     OneInc = 3,
27     /// Instead of a length, stores 13 bits of immediate data
28     Immd = 4,
29     /// The address is not incremented
30     ZeroInc = 5,
31 }
32 
33 impl TryFrom<u8> for MthdType {
34     type Error = &'static str;
35 
try_from(value: u8) -> Result<Self, Self::Error>36     fn try_from(value: u8) -> Result<Self, Self::Error> {
37         match value {
38             1 => Ok(MthdType::NInc),
39             3 => Ok(MthdType::OneInc),
40             4 => Ok(MthdType::Immd),
41             5 => Ok(MthdType::ZeroInc),
42             _ => Err("Invalid method type"),
43         }
44     }
45 }
46 
47 /// A method header.
48 ///
49 /// Methods start with a header that can encode the `IncType`, the subclass,
50 /// an address and the size. Optionally, the header can contain an address
51 /// and an immediate instead.
52 #[repr(transparent)]
53 struct MthdHeader(u32);
54 
55 impl MthdHeader {
from_bits_mut(bits: &mut u32) -> &mut Self56     fn from_bits_mut(bits: &mut u32) -> &mut Self {
57         // This is always safe beause a reference is always safe to
58         // derefence.
59         unsafe { &mut *(bits as *mut u32 as *mut MthdHeader) }
60     }
61 
to_bits(self) -> u3262     fn to_bits(self) -> u32 {
63         self.0
64     }
65 
new(mthd_type: MthdType, subc: u8, addr: u16, data: u16) -> Self66     fn new(mthd_type: MthdType, subc: u8, addr: u16, data: u16) -> Self {
67         debug_assert!(subc <= 0x7);
68         debug_assert!(addr & 0x3 == 0 && addr <= MAX_MTHD_ADDR);
69         debug_assert!(data <= MAX_MTHD_LEN);
70 
71         let mthd_type: u32 = (mthd_type as u8).into();
72         let subc: u32 = subc.into();
73         let addr: u32 = addr.into();
74         let data: u32 = data.into();
75 
76         Self((mthd_type << 29) | (data << 16) | (subc << 13) | (addr >> 2))
77     }
78 
new_immd(immd: u16, subc: u8, addr: u16) -> Self79     fn new_immd(immd: u16, subc: u8, addr: u16) -> Self {
80         Self::new(MthdType::Immd, subc, addr, immd)
81     }
82 
mthd_type(&self) -> MthdType83     fn mthd_type(&self) -> MthdType {
84         ((self.0 >> 29) as u8).try_into().unwrap()
85     }
86 
set_mthd_type(&mut self, mthd_type: MthdType)87     fn set_mthd_type(&mut self, mthd_type: MthdType) {
88         self.0 &= 0x1fffffff;
89         self.0 |= (mthd_type as u8 as u32) << 29;
90     }
91 
subc(&self) -> u892     fn subc(&self) -> u8 {
93         (self.0 >> 13 & 0x7) as u8
94     }
95 
addr(&self) -> u1696     fn addr(&self) -> u16 {
97         ((self.0 & 0x1fff) << 2) as u16
98     }
99 
len(&self) -> u16100     fn len(&self) -> u16 {
101         debug_assert!(!matches!(self.mthd_type(), MthdType::Immd));
102         (self.0 >> 16 & 0x1fff) as u16
103     }
104 
set_len(&mut self, len: u16)105     fn set_len(&mut self, len: u16) {
106         debug_assert!(len <= MAX_MTHD_LEN);
107         self.0 &= 0xe000ffff;
108         self.0 |= u32::from(len) << 16;
109     }
110 
add_len(&mut self, count: u16)111     fn add_len(&mut self, count: u16) {
112         let new_len = self.len() + count;
113         self.set_len(new_len);
114     }
115 }
116 
117 pub struct Push {
118     /// The internal memory. Has to be uploaded to a BO through flush().
119     mem: Vec<u32>,
120     /// Last DW that is an incrementing type or usize::MAX
121     last_inc: usize,
122 }
123 
124 impl Push {
125     /// Instantiates a new push buffer.
new() -> Self126     pub fn new() -> Self {
127         Self {
128             mem: Vec::new(),
129             last_inc: usize::MAX,
130         }
131     }
132 
push_mthd_bits(&mut self, subc: u8, addr: u16, bits: u32)133     fn push_mthd_bits(&mut self, subc: u8, addr: u16, bits: u32) {
134         let current_len = self.mem.len();
135         if let Some(last) = self.mem.get_mut(self.last_inc) {
136             let last = MthdHeader::from_bits_mut(last);
137             debug_assert!(last.len() >= 1);
138             debug_assert!(
139                 self.last_inc + usize::from(last.len()) + 1 == current_len
140             );
141             if subc == last.subc() {
142                 match last.mthd_type() {
143                     MthdType::NInc => {
144                         if addr == last.addr() + last.len() * 4 {
145                             last.add_len(1);
146                             self.mem.push(bits);
147                             return;
148                         } else if last.len() == 1 && addr == last.addr() {
149                             last.set_mthd_type(MthdType::ZeroInc);
150                             last.add_len(1);
151                             self.mem.push(bits);
152                             return;
153                         } else if last.len() == 2 && addr == last.addr() + 4 {
154                             last.set_mthd_type(MthdType::OneInc);
155                             last.add_len(1);
156                             self.mem.push(bits);
157                             return;
158                         }
159                     }
160                     MthdType::ZeroInc => {
161                         if addr == last.addr() {
162                             last.add_len(1);
163                             self.mem.push(bits);
164                             return;
165                         }
166                     }
167                     MthdType::OneInc => {
168                         if addr == last.addr() + 4 {
169                             last.add_len(1);
170                             self.mem.push(bits);
171                             return;
172                         }
173                     }
174                     _ => (),
175                 }
176             }
177         }
178 
179         // Otherwise, we need a new method header.
180         //
181         // Methods that use 13bits or lower can be encoded as immediates
182         // directly.
183         if bits <= 0x1fff {
184             self.last_inc = usize::MAX;
185             let header = MthdHeader::new_immd(bits as u16, subc, addr);
186             self.mem.push(header.to_bits());
187         } else {
188             self.last_inc = self.mem.len();
189             let header = MthdHeader::new(MthdType::NInc, subc, addr, 1);
190             self.mem.push(header.to_bits());
191             self.mem.push(bits);
192         }
193     }
194 
push_method<M: Mthd>(&mut self, mthd: M)195     pub fn push_method<M: Mthd>(&mut self, mthd: M) {
196         self.push_mthd_bits(class_to_subc(M::CLASS), M::ADDR, mthd.to_bits());
197     }
198 
push_array_method<M: ArrayMthd>(&mut self, i: usize, mthd: M)199     pub fn push_array_method<M: ArrayMthd>(&mut self, i: usize, mthd: M) {
200         self.push_mthd_bits(
201             class_to_subc(M::CLASS),
202             M::addr(i),
203             mthd.to_bits(),
204         );
205     }
206 
207     /// Push an array of dwords into the push buffer
push_inline_data(&mut self, data: &[u32])208     pub fn push_inline_data(&mut self, data: &[u32]) {
209         if self.last_inc != usize::MAX {
210             panic!("Inline data must only be placed after a method header");
211         }
212         self.mem.extend_from_slice(data);
213     }
214 }
215 
216 impl std::ops::Deref for Push {
217     type Target = [u32];
218 
deref(&self) -> &[u32]219     fn deref(&self) -> &[u32] {
220         &self.mem
221     }
222 }
223