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