Voxia OS v0.0.1
Hobby Project Operating System Targeting x86-64
Loading...
Searching...
No Matches
intel_hda.c
Go to the documentation of this file.
1// #include "./intel_hda.h"
2
3// #include <firmw/pci/pci.h>
4// #include <libk/debug/debug.h>
5// #include <libk/serial.h>
6// #include <str.h>
7// #include <libk/timer.h>
8// #include <type.h>
9// #include <memory/memory_utils.h>
10// #include <memory/phys_base_allocator.h>
11// #include <vfs/vfs.h>
12
13// /* Variabel global HDA */
14// pci_device_t hda_device;
15
16// static volatile uint32_t *hda_corr = 0; // CORB: Command Output Ring Buffer
17// static volatile uint32_t *hda_rirrb = 0; // RIRB: Response Input Ring Buffer
18// static uint8_t hda_corb_pointer = 1;
19// static uint8_t hda_rirb_pointer = 1;
20// static int ticks = 0;
21// static uint8_t pin_speaker_node = 0, pin_alt_speaker_node = 0;
22
23// /* Fungsi-fungsi MMIO dasar */
24// uint8_t
25// mmio_readb(uint16_t offset)
26// {
27// return *((volatile uint8_t *)(hda_device.bar[0] + offset));
28// }
29
30// void
31// mmio_writeb(uint16_t offset, uint8_t data)
32// {
33// *((volatile uint8_t *)(hda_device.bar[0] + offset)) = data;
34// }
35
36// uint16_t
37// mmio_readw(uint16_t offset)
38// {
39// return *((volatile uint16_t *)(hda_device.bar[0] + offset));
40// }
41
42// void
43// mmio_writew(uint16_t offset, uint16_t data)
44// {
45// *((volatile uint16_t *)(hda_device.bar[0] + offset)) = data;
46// }
47
48// uint32_t
49// mmio_readl(uint16_t offset)
50// {
51// return *((volatile uint32_t *)(hda_device.bar[0] + offset));
52// }
53
54// void
55// mmio_writel(uint16_t offset, uint32_t data)
56// {
57// *((volatile uint32_t *)(hda_device.bar[0] + offset)) = data;
58// }
59
60// /* Prototipe fungsi */
61// void parse_codec(int codec);
62// void audio_function_group_process(int codec, int node);
63
64// /*
65// * Kirim perintah (verb) melalui PIO.
66// * Metode ini mengirim perintah secara langsung dan menunggu respons.
67// */
68// uint32_t
69// send_verb_pio(uint32_t codec, uint32_t node, uint32_t verb, uint32_t cmd)
70// {
71// uint32_t data = ((codec << 28) | (node << 20) | (verb << 8) | cmd);
72// // Mulai PIO: set bit (misal: offset 0x68) sebelum menulis data
73// mmio_writew(0x68, 0x2);
74// mmio_writel(0x60, data);
75// mmio_writew(0x68, 0x1);
76
77// ticks = 0;
78// while (ticks < 4)
79// {
80// ticks++;
81// usleep(100);
82// // Cek status PIO (offset 0x68)
83// if ((mmio_readw(0x68) & 0x3) == 0x2)
84// {
85// mmio_writew(0x68, 0x2);
86// return mmio_readl(0x64);
87// }
88// }
89// return 0;
90// }
91
92// /*
93// * Kirim perintah melalui mekanisme CORB/RIRB.
94// * Perhatikan bahwa implementasi ini harus disesuaikan dengan
95// * spesifikasi dan pengaturan sinkronisasi hardware.
96// */
97// uint32_t
98// send_verb(uint32_t codec, uint32_t node, uint32_t command, uint32_t verb)
99// {
100// serial_trace("HDA CORB base: 0x%x\n", (uintptr_t)hda_corr);
101// uint32_t data = ((codec << 28) | (node << 20) | (command << 8) | verb);
102// serial_trace("Sending verb: 0x%x\n", data);
103// hda_corr[hda_corb_pointer] = data;
104// serial_trace("CORB pointer: %d -> 0x%x\n", hda_corb_pointer, hda_corr[hda_corb_pointer]);
105
106// mmio_writew(0x48, hda_corb_pointer);
107// int c = 0;
108// while (c < 3)
109// {
110// usleep(500);
111// uint8_t a = mmio_readb(0x58);
112// if (a == hda_corb_pointer)
113// {
114// break;
115// }
116// c++;
117// }
118
119// data = hda_rirrb[hda_rirb_pointer * 2];
120// hda_rirb_pointer = (hda_rirb_pointer + 1) % 256;
121// hda_corb_pointer = (hda_corb_pointer + 1) % 256;
122
123// serial_trace("Verb response: 0x%x\n", data);
124// return data;
125// }
126
127// /*
128// * Inisialisasi Intel HDA:
129// * - Mencari perangkat PCI dengan class 0x04 (audio) dan subclass 0x03 (HDA)
130// * - Reset dan konfigurasi awal register
131// * - Alokasi dan inisialisasi buffer CORB dan RIRB
132// */
133// void
134// intel_hda_init(void)
135// {
136// /* Cari perangkat HDA pada daftar PCI */
137// for (int i = 0; i < 32; i++)
138// {
139// if (pci_devices[i].class == 0x4 && pci_devices[i].subclass == 0x3)
140// {
141// serial_trace("\nFound Intel HDA device BAR at: 0x%x\n", pci_devices[i].bar[0]);
142// hda_device = pci_devices[i];
143// enable_bus_mastering(hda_device);
144// break;
145// }
146// }
147
148// /* Reset HDA controller */
149// mmio_writel(INTEL_HDA_GCTL_OFFSET, 0);
150// usleep(100);
151// if ((mmio_readb(INTEL_HDA_GCTL_OFFSET) & 0x1) == 0)
152// serial_trace("Intel HDA reset success\n");
153
154// mmio_writel(INTEL_HDA_GCTL_OFFSET, 1);
155// usleep(100);
156// if ((mmio_readb(INTEL_HDA_GCTL_OFFSET) & 0x1) == 1)
157// serial_trace("Intel HDA exited reset state successfully\n");
158
159// /* Baca versi spesifikasi */
160// uint8_t major_version = mmio_readb(INTEL_HDA_VMAJ_OFFSET);
161// uint8_t minor_version = mmio_readb(INTEL_HDA_VMIN_OFFSET);
162// serial_trace("Intel HDA version: %d.%d\n", major_version, minor_version);
163
164// /* Nonaktifkan interrupt */
165// mmio_writel(INTEL_HDA_INT_OFFSET, 0);
166
167// /* Konfigurasi DMA Position Buffer */
168// mmio_writel(0x70, 0);
169// mmio_writel(0x74, 0);
170
171// /* Nonaktifkan stream synchronisation dan CORB/RIRB */
172// mmio_writel(0x38, 0);
173// mmio_writeb(0x4C, 0);
174// mmio_writeb(0x5C, 0);
175
176// /* Alokasikan dan inisialisasi CORB */
177// uint8_t corb_size_reg = mmio_readb(0x4E);
178// serial_trace("CORB size register: 0x%x\n", corb_size_reg);
179
180// /* Alokasikan setidaknya satu halaman dan pastikan alignment 128-byte */
181// hda_corr = (volatile uint32_t *)VIRT2PHYS(phys_base_alloc(1));
182// hda_corr = (volatile uint32_t *)((((uint32_t)hda_corr + 0x80 - 1) / 0x80) * 0x80);
183// serial_trace("CORB allocated at: 0x%x\n", (uint32_t)hda_corr);
184// memset((void *)hda_corr, 0xFF, 256 * 8);
185// mmio_writel(0x40, (uint32_t)hda_corr);
186// mmio_writel(0x44, 0);
187// /* Set CORB ring size ke 256 entri (0b10) */
188// mmio_writeb(0x4E, 0x2);
189
190// /* Reset pointer CORB */
191// mmio_writew(0x4A, 0x8000);
192// while ((mmio_readw(0x4A) & 0x8000) != 0x8000)
193// usleep(100);
194// mmio_writew(0x4A, 0);
195// while ((mmio_readw(0x4A) & 0x8000) != 0)
196// usleep(100);
197// if ((mmio_readw(0x4A) & 0x8000) == 0x8000)
198// serial_trace("CORB reset failed\n");
199
200// mmio_writel(0x48, 0);
201
202// /* Alokasikan dan inisialisasi RIRB */
203// uint8_t rirb_size_reg = mmio_readb(0x5E);
204// serial_trace("RIRB size register: 0x%x\n", rirb_size_reg);
205// hda_rirrb = (volatile uint32_t *)VIRT2PHYS(phys_base_alloc(1));
206// memset((void *)hda_rirrb, 0, 256 * 8);
207// mmio_writel(0x50, (uint32_t)hda_rirrb);
208// mmio_writel(0x54, 0);
209// /* Set RIRB ring size ke 256 entri */
210// mmio_writeb(0x5E, 0x2);
211
212// /* Reset pointer RIRB */
213// mmio_writew(0x58, 0x8000);
214// while ((mmio_readw(0x58) & 0x8000) != 0)
215// ;
216// usleep(500);
217// mmio_writew(0x5A, 0xFF); // Set response interrupt count
218
219// /* Konfigurasi DMA Position Buffer */
220// mmio_writel(0x70, 1);
221// mmio_writel(0x74, 0x0);
222
223// /* Nyalakan CORB dan RIRB (DMA mode), namun jika ada masalah pada QEMU,
224// * gunakan PIO */
225// mmio_writeb(0x4C, 0x2);
226// mmio_writeb(0x5C, 0x2);
227// // Jika DMA bermasalah, nonaktifkan DMA:
228// mmio_writeb(0x4C, 0);
229// mmio_writeb(0x5C, 0);
230
231// /* Pindai CODEC menggunakan mode PIO */
232// for (int i = 0; i < 16; i++)
233// {
234// uint32_t codecid = send_verb_pio(i, 0, 0xF00, 0);
235// if (codecid != 0)
236// {
237// serial_trace("\nFound CODEC (PIO) id: 0x%x\n", codecid);
238// parse_codec(i);
239// }
240// }
241// serial_trace("Intel HDA init done\n\n");
242// }
243
244// /*
245// * Fungsi parse_codec mengambil informasi CODEC, seperti vendor/device,
246// * revision, dan data subordinate untuk menentukan function group.
247// */
248// void
249// parse_codec(int codec)
250// {
251// uint32_t info = send_verb_pio(codec, 0, 0xF00, 0);
252// uint16_t device_id = info & 0xFFFF;
253// uint16_t vendor_id = (info >> 16) & 0xFFFF;
254
255// info = send_verb_pio(codec, 0, 0xF00, 0x2);
256// uint16_t revision_id = info >> 16;
257// serial_trace("Codec %d: Vendor 0x%x, Device 0x%x, Revision 0x%x\n", codec, vendor_id,
258// device_id,
259// revision_id);
260
261// uint32_t subordinate_info = send_verb_pio(codec, 0, 0xF00, 0x4);
262// serial_trace("Subordinate info: 0x%x\n", subordinate_info);
263// uint8_t total_nodes = subordinate_info & 0xFF;
264// uint8_t start_node = (subordinate_info >> 16) & 0xFF;
265// serial_trace("Total nodes: %d, Start node: %d\n", total_nodes, start_node);
266
267// for (uint8_t i = start_node; i < start_node + total_nodes; i++)
268// {
269// uint32_t node_info = send_verb_pio(codec, i, 0xF00, 0x5);
270// if ((node_info & 0x7F) == 0x1)
271// {
272// serial_trace("Node 0x%x is an Audio Function Group\n", i);
273// audio_function_group_process(codec, i);
274// return;
275// }
276// }
277// }
278
279// /*
280// * Fungsi get_connection_list mengambil entri daftar koneksi dari suatu node.
281// * Memilih format pendek atau panjang sesuai bit paling atas dari response.
282// */
283// uint16_t
284// get_connection_list(int codec, int node, uint32_t connection_number)
285// {
286// uint32_t connection_lst_cap = send_verb_pio(codec, node, 0xF00, 0x0E);
287// if (connection_number >= (connection_lst_cap & 0x7F))
288// return 0;
289
290// if ((connection_lst_cap & 0x80) == 0x00)
291// { // short form
292// return (send_verb_pio(codec, node, 0xF02, ((connection_number / 4) * 4)) >>
293// ((connection_number % 4) * 8)) &
294// 0xFF;
295// }
296// else
297// { // long form
298// return (send_verb_pio(codec, node, 0xF02, ((connection_number / 2) * 2)) >>
299// ((connection_number % 2) * 16)) &
300// 0xFFFF;
301// }
302// }
303
304// /*
305// * Fungsi set_gain_node mengatur gain (volume) untuk sebuah node.
306// */
307// void
308// set_gain_node(int codec, int node, int type, uint16_t cap, uint16_t gain)
309// {
310// uint16_t payload = 0x3000;
311// if (type == 0x1)
312// payload |= 0x8000;
313// if (gain == 0 && (cap & 0x80000000))
314// payload |= 0x80; // mute
315// else
316// payload |= (((cap >> 8) & 0x7F) * gain / 100); // konversi 0-100 ke range node
317// send_verb_pio(codec, node, 0x3, payload);
318// }
319
320// /*
321// * Fungsi audio_function_group_process melakukan inisialisasi untuk Audio
322// * Function Group:
323// * - Reset, power on, aktifkan unsolicited response,
324// * - Baca capability widget, cari widget output,
325// * - Jika ditemukan, konfigurasi node output speaker.
326// */
327// void
328// audio_function_group_process(int codec, int node)
329// {
330// uint32_t node_info = send_verb_pio(codec, node, 0xF00, 0x5);
331// uint8_t node_type = node_info & 0xFF;
332// uint8_t unsol = (node_info >> 8) & 0xFF;
333// serial_trace("Node 0x%x type: %d, unsol: %d\n", node, node_type, unsol);
334
335// /* Inisialisasi AFG */
336// send_verb_pio(codec, node, 0x7FF, 0); // Reset function group
337// send_verb_pio(codec, node, 0x705, 0); // Power on
338// send_verb_pio(codec, node, 0x708, 0); // Enable unsolicited response
339
340// uint32_t sample_capability = send_verb_pio(codec, node, 0xF00, 0x0A);
341// uint32_t supported_format = send_verb_pio(codec, node, 0xF00, 0x0B);
342// uint32_t pin_capability = send_verb_pio(codec, node, 0xF00, 0x0C);
343// uint32_t input_amp_capability = send_verb_pio(codec, node, 0xF00, 0x0D);
344// uint32_t output_amp_capability = send_verb_pio(codec, node, 0xF00, 0x0E);
345
346// serial_trace("Sample capability: 0x%x\n", sample_capability);
347// serial_trace("Supported format: AC3: %d, Float32: %d, PCM: %d\n",
348// (supported_format & 0b100) != 0, (supported_format & 0b010) != 0,
349// (supported_format & 0b001) != 0);
350// serial_trace("Pin capability: 0x%x\n", pin_capability);
351
352// uint32_t subordinate_info = send_verb_pio(codec, node, 0xF00, 0x4);
353// uint8_t total_nodes = subordinate_info & 0xFF;
354// uint8_t start_node = (subordinate_info >> 16) & 0xFF;
355// serial_trace("Total nodes: %d, Start node: %d\n", total_nodes, start_node);
356
357// for (uint8_t i = start_node; i < start_node + total_nodes; i++)
358// {
359// uint32_t widget_cap = send_verb_pio(codec, i, 0xF00, 0x9);
360// uint8_t widget_type = (widget_cap >> 20) & 0xF;
361// if (widget_type == 0)
362// {
363// serial_trace("Widget 0x%x is an audio output\n", i);
364// send_verb_pio(codec, i, 0x706, 0x0);
365// // pin_speaker_node = i;
366// }
367// else if (widget_type == 1)
368// serial_trace("Widget 0x%x is an audio input\n", i);
369// else if (widget_type == 0x4)
370// {
371// serial_trace("Widget 0x%x is a complex widget\n", i);
372// widget_type = (send_verb_pio(codec, i, 0xF1C, 0x00) >> 20) & 0xF;
373// if (widget_type == 0x0)
374// {
375// serial_trace("Widget 0x%x is a Line Out widget\n", i);
376// pin_alt_speaker_node = i;
377// }
378// else if (widget_type == 0x1)
379// serial_trace("Widget 0x%x is a Speaker widget\n", i);
380// else if (widget_type == 0x2)
381// serial_trace("Widget 0x%x is a HP Out widget\n", i);
382// else if (widget_type == 0x3)
383// serial_trace("Widget 0x%x is a CD widget\n", i);
384// else if (widget_type == 0x4)
385// serial_trace("Widget 0x%x is a SPDIF Out widget\n", i);
386// else if (widget_type == 0x5)
387// serial_trace("Widget 0x%x is a Digital Other Out widget\n", i);
388// else if (widget_type == 0x6)
389// serial_trace("Widget 0x%x is a Modem Line Side widget\n", i);
390// else if (widget_type == 0x7)
391// serial_trace("Widget 0x%x is a Modem Handset Side widget\n", i);
392// else if (widget_type == 0x8)
393// serial_trace("Widget 0x%x is a Line In widget\n", i);
394// else if (widget_type == 0x9)
395// serial_trace("Widget 0x%x is a Aux widget\n", i);
396// else if (widget_type == 0xA)
397// serial_trace("Widget 0x%x is a Mic In widget\n", i);
398// else if (widget_type == 0xB)
399// serial_trace("Widget 0x%x is a Telephony widget\n", i);
400// else if (widget_type == 0xC)
401// serial_trace("Widget 0x%x is a SPDIF In widget\n", i);
402// else if (widget_type == 0xD)
403// serial_trace("Widget 0x%x is a Digital Other In widget\n", i);
404// else if (widget_type == 0xE)
405// serial_trace("Widget 0x%x is a Analog Other widget\n", i);
406// else if (widget_type == 0xF)
407// serial_trace("Widget 0x%x is a Reserved widget\n", i);
408// }
409// else if (widget_type == 0x6)
410// serial_trace("Widget 0x%x is a volume knob\n", i);
411// else if (widget_type == 0x7)
412// serial_trace("Widget 0x%x is a beep generator\n", i);
413// else if (widget_type == 0x8)
414// serial_trace("Widget 0x%x is a vendor defined\n", i);
415// else
416// serial_trace("Widget 0x%x is a reserved\n", i);
417
418// /* Coba cari output node yang terhubung */
419// uint8_t out_amp_present = widget_cap & 0x4;
420// if (out_amp_present)
421// {
422// serial_trace("Output amp present on widget 0x%x\n", i);
423// if ((send_verb_pio(codec, i, 0xF1C, 0) >> 30) != 0x1)
424// {
425// if ((send_verb_pio(codec, i, 0xF00, 0x0C) & 0x10) == 0x10)
426// {
427// serial_trace("Codec %d, Node 0x%x connected to "
428// "output device\n",
429// codec, i);
430// pin_speaker_node = i;
431// }
432// else
433// {
434// serial_trace("no output device\n");
435// }
436// }
437// }
438
439// /* Tampilkan daftar koneksi (jika ada) */
440// uint8_t con_entry_number = 0;
441// uint16_t con_list = get_connection_list(codec, i, con_entry_number);
442// while (con_list != 0)
443// {
444// serial_trace("Node 0x%x Connection list entry: %d\n", i, con_list);
445// con_entry_number++;
446// con_list = get_connection_list(codec, i, con_entry_number);
447// }
448// }
449
450// /* Jika ditemukan output node untuk speaker, lakukan inisialisasi lebih
451// * lanjut */
452// if (pin_speaker_node != 0)
453// {
454// serial_trace("Found speaker node: 0x%x\n", pin_speaker_node);
455// send_verb_pio(codec, pin_speaker_node, 0x705,
456// 0); // Power on speaker node
457// send_verb_pio(codec, pin_speaker_node, 0x708,
458// 0); // Enable unsolicited response
459// send_verb_pio(codec, pin_speaker_node, 0x703,
460// 0); // Stop processing (jika diperlukan)
461// send_verb_pio(codec, pin_speaker_node, 0x706,
462// 0x10); // Set stream/channel
463// uint16_t pin_out_cap = send_verb_pio(codec, pin_speaker_node, 0xF00, 0x012);
464// set_gain_node(codec, pin_speaker_node, 1, pin_out_cap, 100);
465// serial_trace("Audio output node initialized\n");
466// }
467// else if (pin_alt_speaker_node != 0)
468// {
469// serial_trace("Found alternate speaker node: 0x%x\n", pin_alt_speaker_node);
470// send_verb_pio(codec, pin_alt_speaker_node, 0x705,
471// 0); // Power on speaker node
472// send_verb_pio(codec, pin_alt_speaker_node, 0x708,
473// 0); // Enable unsolicited response
474// send_verb_pio(codec, pin_alt_speaker_node, 0x703,
475// 0); // Stop processing (jika diperlukan)
476// send_verb_pio(codec, pin_alt_speaker_node, 0x706,
477// 0x10); // Set stream/channel
478// uint16_t pin_out_cap = send_verb_pio(codec, pin_alt_speaker_node, 0xF00, 0x012);
479// set_gain_node(codec, pin_alt_speaker_node, 1, pin_out_cap, 100);
480// serial_trace("Alternate audio output node initialized\n");
481// pin_speaker_node = pin_alt_speaker_node;
482// }
483// else
484// {
485// serial_trace("No speaker node found\n");
486// }
487// }
488
489// /*
490// * Fungsi intel_hda_play:
491// * - Membaca file audio dari sistem file
492// * - Mengatur stream descriptor untuk output stream
493// * - Memulai pemutaran
494// */
495// struct stream_buffer
496// {
497// uint64_t buffer;
498// uint32_t length;
499// uint32_t ioc;
500// } __attribute__((packed));
501
502// uint16_t
503// hda_return_sound_data_format(uint32_t sample_rate, uint32_t channels, uint32_t bits_per_sample)
504// {
505// uint16_t data_format = 0;
506
507// // channels
508// data_format = (channels - 1);
509
510// // bits per sample
511// if (bits_per_sample == 16)
512// {
513// data_format |= ((0b001) << 4);
514// }
515// else if (bits_per_sample == 20)
516// {
517// data_format |= ((0b010) << 4);
518// }
519// else if (bits_per_sample == 24)
520// {
521// data_format |= ((0b011) << 4);
522// }
523// else if (bits_per_sample == 32)
524// {
525// data_format |= ((0b100) << 4);
526// }
527
528// // sample rate
529// if (sample_rate == 48000)
530// {
531// data_format |= ((0b0000000) << 8);
532// }
533// else if (sample_rate == 44100)
534// {
535// data_format |= ((0b1000000) << 8);
536// }
537// else if (sample_rate == 32000)
538// {
539// data_format |= ((0b0001010) << 8);
540// }
541// else if (sample_rate == 22050)
542// {
543// data_format |= ((0b1000001) << 8);
544// }
545// else if (sample_rate == 16000)
546// {
547// data_format |= ((0b0000010) << 8);
548// }
549// else if (sample_rate == 11025)
550// {
551// data_format |= ((0b1000011) << 8);
552// }
553// else if (sample_rate == 8000)
554// {
555// data_format |= ((0b0000101) << 8);
556// }
557// else if (sample_rate == 88200)
558// {
559// data_format |= ((0b1001000) << 8);
560// }
561// else if (sample_rate == 96000)
562// {
563// data_format |= ((0b0001000) << 8);
564// }
565// else if (sample_rate == 176400)
566// {
567// data_format |= ((0b1011000) << 8);
568// }
569// else if (sample_rate == 192000)
570// {
571// data_format |= ((0b0011000) << 8);
572// }
573
574// return data_format;
575// }
576
577// void
578// test_beep(void)
579// {
580// int beep_node = -1;
581// // Asumsikan codec 0; iterasi node 0 sampai 15 (sesuaikan dengan rentang
582// // node yang valid)
583// for (int node = 0; node < 16; node++)
584// {
585// uint32_t widget_cap = send_verb_pio(0, node, 0xF00, 0x9);
586// uint8_t widget_type = (widget_cap >> 20) & 0xF;
587// if (widget_type == 0x7)
588// {
589// beep_node = node;
590// serial_trace("Found beep generator at node 0x%x\n", node);
591// break;
592// }
593// }
594// if (beep_node == -1)
595// {
596// serial_trace("No beep generator found\n");
597// return;
598// }
599
600// /* Memicu beep:
601// Contoh: mengirimkan verb 0x70 dengan payload 0x1 untuk memulai beep,
602// kemudian 0x70 dengan payload 0x0 untuk menghentikannya.
603// (Nilai-nilai ini hanya contoh; sesuaikan dengan dokumentasi codec Anda)
604// */
605// serial_trace("Triggering beep...\n");
606// send_verb_pio(0, beep_node, 0x70, 0x1);
607// usleep(500000); // Beep selama 0.5 detik
608// send_verb_pio(0, beep_node, 0x70, 0x0);
609// serial_trace("Beep finished.\n");
610// }
611
612// void
613// intel_hda_play(const char *file_path)
614// {
615// int fd = vfs_open(file_path, 0);
616// struct vfs_file_stats stats;
617// int fs_resp = vfs_fstat(fd, &stats);
618// serial_trace("File size: %d bytes\n", stats.size);
619
620// /* Alokasikan buffer untuk file audio dan pastikan alamat fisiknya benar */
621// uint8_t *file = (uint8_t *)VIRT2PHYS(phys_base_alloc((stats.size / 4096) + 1));
622// file = (uint8_t *)(((uint32_t)file + 0x7F) & ~0x7F);
623// serial_trace("File buffer: 0x%x\n", (uint32_t)file);
624// int o = vfs_read(fd, file, stats.size);
625// if (o != 0)
626// {
627// serial_trace("Error reading file\n");
628// return;
629// }
630
631// uint32_t pos = ((mmio_readw(0x00) >> 8) & 0xF);
632// serial_trace("HDA stream input stream number %d\n", pos);
633
634// uint32_t output_pos = ((mmio_readw(0x00) >> 12) & 0xF);
635// serial_trace("HDA stream output stream number %d\n", output_pos);
636
637// /* Atur alamat stream output dari register stream descriptor.
638// Perhitungan offset disesuaikan dengan jumlah stream input/output yang
639// tersedia. */
640// volatile uint32_t *hda_stream_output_base =
641// (volatile uint32_t *)(hda_device.bar[0] + 0x80 + (0x20 * ((mmio_readw(0x00) >> 8) &
642// 0xF)));
643// serial_trace("HDA stream output base: 0x%x\n", (uint32_t)hda_stream_output_base);
644
645// /* Reset stream: update control register untuk memulai stream */
646// uint32_t stream_desc = *((volatile uint32_t *)hda_stream_output_base);
647// stream_desc &= ~0x2; // Clear flag tertentu (sesuaikan dengan spesifikasi)
648// stream_desc |= 0x1; // Set stream run flag
649// *((volatile uint32_t *)hda_stream_output_base) = stream_desc;
650
651// int counter = 0;
652// *((volatile uint32_t *)(hda_stream_output_base)) = 0;
653
654// while ((*((volatile uint32_t *)(hda_stream_output_base + 0)) & 0x1) != 0)
655// {
656// usleep(100);
657// counter++;
658// if (counter > 100)
659// {
660// serial_trace("\nHDA: can not reset stream");
661// break;
662// }
663// }
664
665// *((volatile uint32_t *)(hda_stream_output_base + 0x03)) = 0x1C;
666
667// /* Buat stream buffer descriptor */
668// struct stream_buffer *stream_list = (struct stream_buffer *)((uint64_t)VIRT2PHYS(
669// phys_base_alloc(1 + (sizeof(struct stream_buffer) * 2))));
670// stream_list = (struct stream_buffer *)(((uint32_t)stream_list + 0x7F) & ~0x7F);
671// memset(stream_list, 0, sizeof(struct stream_buffer) * 2);
672// asm("wbinvd");
673
674// for (int i = 0; i < 1; i++)
675// {
676// stream_list[i].buffer = (uint64_t)(file);
677// stream_list[i].length = stats.size;
678// stream_list[i].ioc = 1;
679// }
680
681// serial_trace("Stream buffer descriptor: 0x%x\n", (uint32_t)stream_list);
682// // asm("wbinvd"); // Flush cache (jika diperlukan)
683
684// asm("wbinvd");
685// mmio_writel(0x80 + pos * 0x20 + 0x18, (uint32_t)(uintptr_t)stream_list);
686// mmio_writel(0x80 + pos * 0x20 + 0x1C, (uint32_t)((uintptr_t)stream_list >> 32));
687
688// mmio_writel(0x80 + pos * 0x20 + 0x08, stats.size);
689// serial_trace("Stream size: %d\n", sizeof(struct stream_buffer));
690// mmio_writel(0x80 + pos * 0x20 + 0x0C, 1);
691
692// /* Kirim perintah untuk memulai pemutaran */
693
694// uint32_t payload = hda_return_sound_data_format(44100, 2, 16);
695// mmio_writel(0x80 + pos * 0x20 + 0x12, payload);
696
697// send_verb_pio(0, pin_speaker_node, 0x200, payload);
698// usleep(100);
699
700// // mmio_writel (0x20, 0xFFFF);
701
702// mmio_writel(0x80 + pos * 0x20 + 0x02, 0x14);
703// mmio_writel(0x80 + pos * 0x20 + 0x0, mmio_readl(0x80 + pos * 0x20 + 0x0) | 0x2);
704// KDEBUG(1, "HDA: playing %s with size %d\n", file_path, stats.size);
705// }