How to play sound Intel HD Audio?

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
MishaTheOsmaker
Posts: 1
Joined: Wed Feb 17, 2021 10:44 am
Freenode IRC: MishaTheOsmaker

Re: How to play sound Intel HD Audio?

Post by MishaTheOsmaker »

Any update in 2021?
Klakap
Member
Member
Posts: 297
Joined: Sat Mar 10, 2018 10:16 am

Re: How to play sound Intel HD Audio?

Post by Klakap »

Hey guys, today I got first sound from my hda driver!

https://github.com/Klaykap/BleskOS/blob ... nd_hda.asm

Main feature is that I use immediate interface instead CORB/RIRB interface. So there is still a lot of work, but I am happy that I reached some point.
Klakap
Member
Member
Posts: 297
Joined: Sat Mar 10, 2018 10:16 am

Re: How to play sound Intel HD Audio?

Post by Klakap »

Well, I finally have it. I can send verb through CORB/RIRB interface.

Initalizing CORB:
  • Turn CORB off - outb(CORBCTL, 0x0);
  • Set CORB memory - outd(CORB_LBA, low_base_address_of_corb); outd(CORB_HBA, high_base_address_of_corb);
  • Set CORB size to 256 entries - outb(CORBSIZE, 0x2);
  • Set write pointer - outw(CORB_WP, 0);
  • Reset read pointer - outw(CORB_RP, 0x8000); here wait until bit 15 is set outw(CORB_RP, 0x0000); here wait until bit 15 is clear
Initalizing RIRB:
  • Turn RIRB off - outb(RIRBCTL, 0x0);
  • Set RIRB memory - outd(RIRB_LBA, low_base_address_of_rirb); outd(RIRB_HBA, high_base_address_of_rirb);
  • Set RIRB size to 256 entries - outb(RIRBSIZE, 0x2);
  • Reset write pointer - outw(RIRB_WP, 0x8000); here wait until bit 15 is set outw(RIRB_WP, 0x0000); here wait until bit 15 is clear
  • Set interrupt control - outw(RIRB_INTCTL, 0x0);
Here we can turn on CORB and RIRB by writing value 0x2 to their CONTROL registers.

And now you can send verb by this:
  • Write verb to CORB memory (important: you have to start writing verbs from entry 1, not entry 0 in your memory)
  • Update CORB write pointer - outw(CORB_WP, entry_number); So for first entry this will be 1
  • Wait until RIRB write pointer is the same value as CORB write pointer
  • Read response from RIRB memory
rdos
Member
Member
Posts: 3198
Joined: Wed Oct 01, 2008 1:55 pm

Re: How to play sound Intel HD Audio?

Post by rdos »

After CORB & RIRB you need to setup the codec so the sound flows on the correct path. This is a bit complicated. First you need to discover what is connected, and then you need to enable paths so sound flows to the enabled device. You probably also want to be able to change volume.
Klakap
Member
Member
Posts: 297
Joined: Sat Mar 10, 2018 10:16 am

Re: How to play sound Intel HD Audio?

Post by Klakap »

Here is my understanding of playing sound through nodes.

Stream -> Audio Output -> optional Mixer/Selector -> Out Pin -> Device

Stream:
Is controlled by Output Stream set of ports.

Audio Output:
Set format of stream by verb 0x2
Set stream and channel number by verb 0x706
Set volume by verb 0x3

Mixer/Selector:
Set volume by verb 0x3
In selector also select node by verb 0x701

Out Pin:
Enable pin by set bit 6 by verb 0x707
Enable external speaker by verb 0x70C
Set volume by verb 0x3

Now I can load sound data, describe it in Buffer Descriptor List, run stream and I will hear sound.

Please is this right? Or am I missing something? Thank you for responses.
rdos
Member
Member
Posts: 3198
Joined: Wed Oct 01, 2008 1:55 pm

Re: How to play sound Intel HD Audio?

Post by rdos »

Klakap wrote:Here is my understanding of playing sound through nodes.

Stream -> Audio Output -> optional Mixer/Selector -> Out Pin -> Device

Stream:
Is controlled by Output Stream set of ports.

Audio Output:
Set format of stream by verb 0x2
Set stream and channel number by verb 0x706
Set volume by verb 0x3

Mixer/Selector:
Set volume by verb 0x3
In selector also select node by verb 0x701

Out Pin:
Enable pin by set bit 6 by verb 0x707
Enable external speaker by verb 0x70C
Set volume by verb 0x3

Now I can load sound data, describe it in Buffer Descriptor List, run stream and I will hear sound.

Please is this right? Or am I missing something? Thank you for responses.
I think the above is a bit simplistic. First, you can have several output PINs, and you should check if they are connected or not before assuming you should enable them. Based on which connected output PINs you have, you then need to find a path from the source to the connected output PINs. You then need to send the correct commands to mixers and selectors so this path is properly enabled. You also might need to set volume controls on the path to reasonable values, and/or set the global volume control. The values set should depend on the volume the user has selected.
Klakap
Member
Member
Posts: 297
Joined: Sat Mar 10, 2018 10:16 am

Re: How to play sound Intel HD Audio?

Post by Klakap »

My driver is already able to find output pins nodes. So now I should check every pin if is some device connected to it. If yes, I can enable this pin and start going on his connection list. By connection lists I must find path to some audio output node. When I have this, I can enable all nodes I found. Am I understand it right?

And I also found that on real computer there are many speaker output pins. It is little confusing to me. Should I enable only one pin, if is some device connected to it, or should I enable all pins to whose are connected some devices?
rdos
Member
Member
Posts: 3198
Joined: Wed Oct 01, 2008 1:55 pm

Re: How to play sound Intel HD Audio?

Post by rdos »

Klakap wrote:My driver is already able to find output pins nodes. So now I should check every pin if is some device connected to it. If yes, I can enable this pin and start going on his connection list. By connection lists I must find path to some audio output node. When I have this, I can enable all nodes I found. Am I understand it right?
I think so.
Klakap wrote: And I also found that on real computer there are many speaker output pins. It is little confusing to me. Should I enable only one pin, if is some device connected to it, or should I enable all pins to whose are connected some devices?
It's a bit complicated. If I remember it correctly, there is also some output (speaker?) that doesn't have connection checking support. I think I prioritize if something is plugged into an output, and then I only enable that output & path. If nothing is connected, then I route to the "default speaker" which has no support for connection status. This should work on real computers.
Morehab
Posts: 1
Joined: Sun Jun 19, 2022 7:42 am

Re: How to play sound Intel HD Audio?

Post by Morehab »

Hi, I'm from 2022. I want to play audio with HD Audio on a small operating system (for teaching purposes), and I am just wondering, what should be done for the PCI configuration and Memory mapped configuration part? (or, what should I do with all the registers listed in Chapter 6?)
Klakap
Member
Member
Posts: 297
Joined: Sat Mar 10, 2018 10:16 am

Re: How to play sound Intel HD Audio?

Post by Klakap »

Finally, now I can say that I have fully functioning driver. Last and very tricky part for me was figuring out how to properly update buffer. It was issue because sound card read buffer entries directly from RAM memory, but because it is such small part of data, many computers wrote it in fact only to processor cache. So then sound card was playing previous content of buffer what was of course extremely weird. But today I finally found that flushing processor cache fixes this. I tested that my actual driver works with 6 different codecs, and on multiple real computers. Feel free to check it out: https://github.com/VendelinSlezak/Blesk ... ound/hda.c
rustykrab
Posts: 1
Joined: Sun Mar 10, 2024 5:04 am

Re: How to play sound Intel HD Audio?

Post by rustykrab »

Hey everyone,

First of all, thanks to all posters in this thread! I am writing an Intel HD Audio driver for a research operating system in Rust and this thread was a real good help so far in addition to the specification and online resources like https://wiki.osdev.org/Intel_High_Definition_Audio. I get the impression that through all the years since this thread's creation, people still run into the same problems and I am no exception.

I am developing in a self compiled QEMU 8.2 on Ubuntu 22.02, adding a virtual sound card to my machine via the flag "-audio alsa,model=hda". I already set up my CORB and RIRB in the way that @Klakap summarised in his Post from Mon Aug 30, 2021. Now, I place commands in the the CORB and increase the CORBWP accordingly and expect my CORBRP to catch up with the CORBWP as well as to find responses from the sound card in the RIRB. But this never happened so far and I am a little stuck on finding a promising direction for debugging. I know that the sound card already can communicate with the OS and that my registers are mapped correctly. This gets proven by the fact that I can read reasonable time stamps from my Wall Clock Counter and Wall Clock Counter Alias registers. Moreover, I already can use the Immediate Command Interface to send Commands to the sound card and the sound card writes back reasonable responses to the Immediate Response Input Interface register. The MMIO mapping of the base address for all the registers, which gets provided by the BAR register of the PCI configuration space, therefore seems to be setup and working properly.

I can't exclude that I made a stupid mistake somewhere, but I checked my CORB and RIRB setup several times and all the registers that I think to be relevant (GCTL, WAKEEN, INTCTL, CORBCTL, CORBSIZE, CORBLBASE, CORBUBASE, CORBWP, CORBRP, RIRBCTL, RIRBLBASE, RIRBUBASE, RIRBWP) and also the way that I reset my CORBRP look like they should according to the specification.
In contrast to the registers, CORB and RIRB each get mapped to their individual MMIO spaces, which get allocated to addresses chosen by the OS. One explanation for the observed behavior might be that the mapping is not setup correctly and the sound card doesn't understand the addresses that I placed in the CORBLBASE, CORBUBASE, RIRBLBASE and RIRBUBASE registers. I would then expect the CMEI bit in the CORBSTS register to be set in order to indicate a CORB Memory Error Indication. But the bit stays clear all the time, so I assume that the mapping was successful.

But was it really successful? I was told that QEMU can be pretty forgiving in comparison to real hardware. The example that I was given was the Bus Master Enable bit in the PCI configuration space. While a real PCI device won't be able to transfer data at all if you forget to set this bit during device initialisation, QEMU just lets you get throug with it and a virtual PCI device will work even if you don't set the bit. Yet another example is the fact that you don't need to consider the time it takes for real hardware to set or clear a bit and therefore can completely leave out any waiting loops for asserting a bit after setting it. The same code would probably fail on real harware as you would continue your program before the bit is set. Although, this forgiveness seems to be pretty advantageous at first glance for someone like me, who develops in a virtual environment, it makes debugging more opaque as I can never be sure, if setting a bit really does something or if QEMU just ignores it...

If necessary, I can share my code, but I think that some general advices of someone with more experience in driver development on virtual hardware might already help a lot. I am already using a GDB debugger, but it can't look inside the sound card. So I think that it would be especially helpful to find a way to get any kind of debugging information on my virtual sound card in order to take a look inside the black box that at the moment just doesn't feedback anything at all. As I am running out of ideas on how to continue my debugging journey, I am also very grateful for any fingerpoint to some place that didn't cross to my mind, yet. In the meantime, I will try to test my code on real hardware and report back if I observe the same behavior or something different.

Maybe, my post will be able to wake up this thread from its slumber and I am looking forward to any responses. Please don't behave like my RIRB... ;)
rdos
Member
Member
Posts: 3198
Joined: Wed Oct 01, 2008 1:55 pm

Re: How to play sound Intel HD Audio?

Post by rdos »

Sounds like you are using the wrong page attributes on the sound buffer. You should set it to non-cachable or something to make sure the CPU writes it to physical memory.
Octocontrabass
Member
Member
Posts: 5218
Joined: Mon Mar 25, 2013 7:01 pm

Re: How to play sound Intel HD Audio?

Post by Octocontrabass »

Wait a minute, PCI DMA is supposed to be cache-coherent by default. You shouldn't need to flush the caches unless you've intentionally told the HDA controller to use non-snooped reads.
Klakap
Member
Member
Posts: 297
Joined: Sat Mar 10, 2018 10:16 am

Re: How to play sound Intel HD Audio?

Post by Klakap »

Thank you for pointing this out. I digged deeper and I found out that in there is PCI register 0x78 in which bit 11 enables non snoop. When I cleared it, problem is fixed even without flushing processor cache. However I really do not understand why it is even set as default. It makes no sense for me, because it is great way to mess things up with cache / RAM data. But anyway, it is nice solution for this problem.
Octocontrabass
Member
Member
Posts: 5218
Joined: Mon Mar 25, 2013 7:01 pm

Re: How to play sound Intel HD Audio?

Post by Octocontrabass »

Huh. I took a look at the HDA specification and it says HDA controllers are allowed to force non-snoop DMA. So, the correct fix is what rdos suggested: change the buffer memory type to prevent caching.

I also checked a random Windows PC, and the Intel HDA driver leaves the Enable No Snoop bit set.

Oh, and that bit is set by default because it's part of the PCI Express Capability Structure, and the PCIe specification says that bit should be set by default.
Post Reply