SayoriOS 0.3.3

hda.c
1 //
2 // Created by ndraey on 21.01.24.
3 //
4
5 #include "drv/pci.h"
6 #include "drv/audio/hda.h"
7 #include "io/ports.h"
8 #include "io/tty.h"
9 #include "mem/pmm.h"
10 #include "mem/vmm.h"
11 #include "sys/isr.h"
12 #include "../../lib/libvector/include/vector.h"
13
14 uint8_t hda_bus = 0,
15 hda_slot = 0,
16 hda_func = 0;
17
18 uint16_t hda_vendor = 0,
19 hda_device = 0;
20
21 uint32_t hda_addr = 0;
22
23 volatile uint32_t* hda_corb = 0;
24 size_t hda_corb_phys = 0;
25 volatile uint32_t* hda_rirb = 0;
26 size_t hda_rirb_phys = 0;
27
28 size_t hda_corb_entry_count = 0;
29 size_t hda_rirb_entry_count = 0;
30
31 size_t hda_corb_current = 0;
32 size_t hda_rirb_current = 0;
33
34 size_t hda_irq = 0;
35
36 volatile bool hda_fired = false;
37 volatile size_t hda_response = 0;
38
39 #define WRITE32(reg, value) *(volatile uint32_t*)(hda_addr + (reg)) = (value)
40 #define READ32(reg) (*(volatile uint32_t*)(hda_addr + (reg)))
41 #define WRITE16(reg, value) *(volatile uint16_t*)(hda_addr + (reg)) = (value)
42 #define READ16(reg) (*(volatile uint16_t*)(hda_addr + (reg)))
43 #define WRITE8(reg, value) *(volatile uint8_t*)(hda_addr + (reg)) = (value)
44 #define READ8(reg) (*(volatile uint8_t*)(hda_addr + (reg)))
45
46 #define VERB(codec, node, verb, command) ((codec << 28) | (node << 20) | (verb << 8) | (command))
47
48 #define REG_GCAP 0x00
49 #define REG_SSYNC 0x34
50 #define REG_DMA_LOW_POSITION_ADDR 0x70
51 #define REG_DMA_HIGH_POSITION_ADDR 0x74
52
53 size_t hda_afg_codec_id = 0;
54 size_t hda_afg_node_id = 0;
55
56 vector_t* hda_output_nodes;
57
58 void hda_init() {
59 // Find devce by its class and subclass numbers.
60 pci_find_device_by_class_and_subclass(4, 3, &hda_vendor, &hda_device, &hda_bus, &hda_slot, &hda_func);
61
62 if(hda_vendor && hda_device) {
63 qemu_ok("Found Intel HDA! (%x:%x)", hda_vendor, hda_device);
64 } else {
65 return;
66 }
67
68 // Read memory base address
69 hda_addr = pci_read32(hda_bus, hda_slot, hda_func, 0x10) & ~0b1111;
70
71 uint32_t word = pci_read_confspc_word(hda_bus, hda_slot, hda_func, 0x3C); // All 0xF PCI register
72 hda_irq = word & 0xff;
73
74 pci_enable_bus_mastering(hda_bus, hda_slot, hda_func);
75
76 qemu_ok("HDA address: %x", hda_addr);
77 tty_printf("HDA address: %x\n", hda_addr);
78
79 // Map the registers into our address space (without caching, because memory-mapped regs should not be cached).
80 map_pages(
81 get_kernel_page_directory(),
82 hda_addr,
83 hda_addr,
84 PAGE_SIZE,
85 PAGE_WRITEABLE | PAGE_CACHE_DISABLE // PAGE_PRESENT is set automatically
86 );
87
88 // Reset the entire controller!
89 hda_reset();
90
91 tty_printf("HDA RESET OKAY!\n");
92
93 // Read capabilities
94 size_t data = READ16(REG_GCAP);
95
96 size_t input_streams = (data >> 8) & 0b1111;
97 size_t output_streams = (data >> 12) & 0b1111;
98
99 tty_printf("HDA: I: %d; O: %d;\n", input_streams, output_streams);
100
101 hda_output_nodes = vector_new();
102
103 WRITE32(0x20, 0);
104
105 //turn off dma position transfer
106 WRITE32(REG_DMA_LOW_POSITION_ADDR, 0);
107 WRITE32(REG_DMA_HIGH_POSITION_ADDR, 0);
108
109 //disable synchronization
110 WRITE32(REG_SSYNC, 0);
111 // WRITE32(0x38, 0);
112
113 //stop CORB and RIRB
114 WRITE8(0x4C, 0x0);
115 WRITE8(0x5C, 0x0);
116
117 hda_corb = kmalloc_common(1024, PAGE_SIZE);
118 hda_rirb = kmalloc_common(2048, PAGE_SIZE);
119 // phys_set_flags(get_kernel_page_directory(), (virtual_addr_t)hda_corb, PAGE_PRESENT | PAGE_WRITEABLE | PAGE_CACHE_DISABLE);
120 // phys_set_flags(get_kernel_page_directory(), (virtual_addr_t)hda_rirb, PAGE_PRESENT | PAGE_WRITEABLE | PAGE_CACHE_DISABLE);
121 hda_corb_phys = virt2phys(get_kernel_page_directory(), (virtual_addr_t) hda_corb);
122 hda_rirb_phys = virt2phys(get_kernel_page_directory(), (virtual_addr_t) hda_rirb);
123
124 qemu_note("CORB: V%x => P%x", (size_t)hda_corb, hda_corb_phys);
125 qemu_note("RIRB: V%x => P%x", (size_t)hda_rirb, hda_rirb_phys);
126
127 memset((uint32_t*)hda_corb, 0, 1024);
128 memset((uint32_t*)hda_rirb, 0, 1024);
129
130 qemu_ok("Allocated memory for CORB and RIRB!");
131
132 // Write CORB address
133 WRITE32(0x40, (uint32_t)hda_corb_phys); // First 32 bits
134 WRITE32(0x44, 0); // Last 32 bits (we are 32-bit, so we don't need it)
135
136 hda_corb_entry_count = hda_calculate_entries(READ8(0x4E));
137
138 tty_printf("HDA: CORB: %d entries\n", hda_corb_entry_count);
139
140 // Reset read pointer
141 WRITE16(0x4A, (1 << 15));
142 while((READ16(0x4A) & (1 << 15)) != (1 << 15))
143 ;
144
145 WRITE16(0x4A, 0);
146 while((READ16(0x4A) & (1 << 15)) != 0)
147 ;
148
149 WRITE16(0x48, 0);
150
151 // RIRB
152
153 WRITE32(0x50, (uint32_t)hda_rirb_phys); // First 32 bits
154 WRITE32(0x54, 0); // Last 32 bits (we are 32-bit, so we don't need it)
155
156 hda_rirb_entry_count = hda_calculate_entries(READ8(0x5E));
157
158 tty_printf("HDA: RIRB: %d entries\n", hda_rirb_entry_count);
159
160 // Reset write pointer
161 WRITE16(0x58, (1 << 15));
162
163 // Implement loop to check is WP ready
164
165 sleep_ms(50);
166
167 // Enable interrupts
168 WRITE16(0x5A, READ16(0x5A) | 1);
169 // WRITE16(0x5A, 0xff);
170
171 qemu_log("Starting engines");
172 // Start!
173 WRITE8(0x4C, (1 << 1) | 1);
174 WRITE8(0x5C, (1 << 1) | 1);
175
176 qemu_note("IRQ LINE IS: %d", hda_irq);
177
178 register_interrupt_handler(32 + hda_irq, hda_interrupt_handler);
179
180 WRITE32(0x20, (1 << 30) | (1 << 31));
181
182 qemu_ok("Okay!");
183
184 size_t codec_bitmap = READ16(0x0E);
185
186 for(size_t codec = 0; codec < 16; codec++) {
187 if(~codec_bitmap & (1 << codec)) {
188 continue;
189 }
190
191 qemu_log("Probing codec: %d", codec);
192
193 size_t id = hda_send_verb_via_corb_rirb(VERB(codec, 0, 0xf00, 0));
194
195 if(id != 0) {
196 tty_printf("FOUND CODEC: %x\n", id);
197
198 hda_find_afg(id, codec);
199 }
200 }
201
202 tty_printf("Found %d suitable output nodes.\n", hda_output_nodes->size);
203 }
204
205 void hda_find_afg(size_t codec_response, size_t codec_id) {
206 hda_afg_codec_id = codec_id; // Save codec id
207
208 // Read vendor id
209 size_t vendor_id = (codec_response >> 16) & 0xffff;
210 size_t dev_id = codec_response & 0xffff;
211
212 tty_printf("|- Vendor: %x; Device: %x\n", vendor_id, dev_id);
213
214 size_t child_nodes_info = hda_send_verb_via_corb_rirb(VERB(codec_id, 0, 0xf00, 0x04));
215 size_t first_gnode = (child_nodes_info >> 16) & 0xff;
216 size_t node_count = child_nodes_info & 0xff;
217 size_t last_node = first_gnode + node_count;
218
219 tty_printf("|- First group node: %d; Node count: %d\n", first_gnode, node_count);
220
221 for(size_t node = first_gnode; node < last_node; node++) {
222 size_t function_group_type = hda_send_verb_via_corb_rirb(VERB(codec_id, node, 0xf00, 0x05));
223
224 if((function_group_type & 0x7f) == 0x01) {
225 tty_printf("|- AFG at node: %d\n", node);
226 tty_printf("|- FP: %d -> %d\n", codec_id, node);
227
228 hda_afg_node_id = node; // Save node
229 qemu_ok("UNBELIEVABLE! FOUND AFG!");
230 tty_printf("UNBELIEVABLE! FOUND AFG!\n");
231
232 hda_initialize_afg();
233 break;
234 }
235 }
236 }
237
238 void hda_initialize_afg() {
239 hda_send_verb_via_corb_rirb(VERB(hda_afg_codec_id, hda_afg_node_id, 0x7ff, 0));
240 hda_send_verb_via_corb_rirb(VERB(hda_afg_codec_id, hda_afg_node_id, 0x705, 0));
241 hda_send_verb_via_corb_rirb(VERB(hda_afg_codec_id, hda_afg_node_id, 0x708, 0));
242
243 size_t hda_afg_node_sample_capabilities = hda_send_verb_via_corb_rirb(VERB(hda_afg_codec_id, hda_afg_node_id, 0xF00, 0x0A));
244 size_t hda_afg_node_stream_format_capabilities = hda_send_verb_via_corb_rirb(VERB(hda_afg_codec_id, hda_afg_node_id, 0xF00, 0x0B));
245 size_t hda_afg_node_input_amp_capabilities = hda_send_verb_via_corb_rirb(VERB(hda_afg_codec_id, hda_afg_node_id, 0xF00, 0x0D));
246 size_t hda_afg_node_output_amp_capabilities = hda_send_verb_via_corb_rirb(VERB(hda_afg_codec_id, hda_afg_node_id, 0xF00, 0x12));
247
248 qemu_note("hda_afg_node_sample_capabilities: %x", hda_afg_node_sample_capabilities);
249 qemu_note("hda_afg_node_stream_format_capabilities: %x", hda_afg_node_stream_format_capabilities);
250 qemu_note("hda_afg_node_input_amp_capabilities: %x", hda_afg_node_input_amp_capabilities);
251 qemu_note("hda_afg_node_output_amp_capabilities: %x", hda_afg_node_output_amp_capabilities);
252
253
254 size_t node_data = hda_send_verb_via_corb_rirb(VERB(hda_afg_codec_id, hda_afg_node_id, 0xf00, 0x04));
255 size_t first_gnode = (node_data >> 16) & 0xff; // First group node
256 size_t node_count = node_data & 0xff; // Node count
257 size_t last_node = first_gnode + node_count;
258
259 char* node_types[] = {
260 "Output",
261 "Input",
262 "Mixer",
263 "Selector",
264 "Complex"
265 };
266
267 for(size_t node = first_gnode; node < last_node; node++) {
268 qemu_note("node: %d", node);
269 size_t type = (hda_send_verb_via_corb_rirb(VERB(hda_afg_codec_id, node, 0xF00, 0x09)) >> 20) & 0xF;
270
271 if(type >= 5) {
272 continue;
273 }
274
275 // qemu_note("type: %d = %s", type, node_types[type]);
276 // tty_printf("|- %s at node: %d\n", node_types[type], node);
277
278 if(type == 0x4) {
279 size_t type_of_node = ((hda_send_verb_via_corb_rirb(VERB(hda_afg_codec_id, node, 0xF1C, 0x00)) >> 20) & 0xF);
280
281 // qemu_note("COMPLEX PIN HAS TYPE: %d", type_of_node);
282 // tty_printf("|- COMPLEX PIN HAS TYPE: %d\n", type_of_node);
283
284 if(type_of_node == 0) {
285 tty_printf("Node %d is LINE OUT\n", node);
286 vector_push_back(hda_output_nodes, node);
287 } else if(type_of_node == 1) {
288 tty_printf("Node %d is SPEAKER\n", node);
289 vector_push_back(hda_output_nodes, node);
290 }
291 }
292 }
293 }
294
295 void hda_interrupt_handler(__attribute__((unused)) registers_t regs) {
296 // qemu_warn("HDA Interrupt!");
297
298 size_t interrupt_status = READ32(0x24);
299
300 if(~interrupt_status & (1 << 31)) {
301 return;
302 }
303
304 if(interrupt_status & (1 << 30)) {
305 size_t rirb_status = READ8(0x5D);
306
307 WRITE8(0x5D, rirb_status);
308
309 if(rirb_status & (1 << 0)) {
310 size_t rirb_wp = READ16(0x58);
311
312 // qemu_ok("RESPONSE!");
313
314 while(hda_rirb_current != rirb_wp) {
315 hda_rirb_current = (hda_rirb_current + 1) % hda_rirb_entry_count;
316
317 hda_fired = true;
318 hda_response = hda_rirb[hda_rirb_current * 2];
319
320 // qemu_ok("RIRB: %x", hda_rirb[hda_rirb_current * 2]);
321 }
322 }
323 }
324 }
325
326 uint32_t hda_send_verb_via_corb_rirb(uint32_t verb) {
327 // qemu_warn("CWP: %d; CRP: %d; RWP: %d", READ16(0x48), READ16(0x4A), READ16(0x58));
328
329 hda_corb_current = (hda_corb_current + 1) % hda_corb_entry_count;
330
331 // qemu_warn("CORB CUR: %d; RIRB CUR: %d", hda_corb_current, hda_rirb_current);
332 // SEND VERB
333 hda_corb[hda_corb_current] = verb;
334
335 WRITE16(0x48, hda_corb_current & 0xff);
336
337 while(!hda_fired)
338 ;
339
340 hda_fired = false;
341 qemu_log("VERB %x got response %x", verb, hda_response);
342
343 return hda_response;
344 }
345
346 void hda_reset() {
347 if(!hda_vendor)
348 return;
349
350 WRITE32(0x8, READ32(0x8) & ~1);
351
352 while((READ32(0x08) & 1) != 0);
353
354 WRITE32(0x8, READ32(0x8) | 1);
355
356 while ((READ32(0x08) & 1) != 1);
357
358 qemu_ok("Reset ok!");
359 }
360
361 size_t hda_calculate_entries(size_t word) {
362 if((word & 0x40) == 0x40) {
363 return 256;
364 } else if((word & 0x20) == 0x20) {
365 return 16;
366 } else if((word & 0x10) == 0x10) {
367 return 2;
368 } else {
369 return 0;
370 }
371 }
__attribute__
struct registers __attribute__((packed))
Структура данных пакета от мыши
Definition: psf.h:19
memset
void * memset(void *ptr, char value, size_t num)
Заполнение массива указанными символами
Definition: string.c:203
pci_read_confspc_word
uint16_t pci_read_confspc_word(uint8_t bus, uint8_t slot, uint8_t function, uint8_t offset)
[PCI] Чтение 16-битных полей из пространства механизма конфигураций 1
Definition: pci.c:32
pci_read32
uint32_t pci_read32(uint8_t bus, uint8_t slot, uint8_t function, uint8_t offset)
Чтение данных из шины PCI.
Definition: pci.c:54
registers
Definition: common.h:59
vector
Definition: vector.h:7
sleep_ms
void sleep_ms(uint32_t milliseconds)
Ожидание по миллисекундам
Definition: timer.c:68