Linux device driver for Zedboard audio (1/2)
Precaution
This article is not an official documentation of Linux sound system architecture. If you need more details, or official documentation, please refer to the following link. Or, download Linux kernel source code, and generate documents using Doxygen.
Advanced Linux Sound Architecture (ALSA) project homepage
Summary
This is Linux device driver preparation part for the designed hardware in previous articles. In this article, I will talk about the device driver for the hardware described in articles below.
Zedboard audio system design (1/2)
Zedboard audio system design (2/2)
Goal
In this article, the goal is to configure the kernel properly to include all necessary drivers. This is not enough for audio processing (Can’t play/record/control audio). We just can check if all necessary components are configured properly. In next article, I’ll talk about ALSA in detail, and describe how to create machine driver (sound card driver).
Linux audio API and device drivers
Linux has ALSA (Advanced Linux Sound Architecture) to handle audio processing. ALSA is capable of doing various things like sample rate conversion, audio format conversion, mixing, and so on. This framework provides an API for the audio application.
Sound components
In PC, sound component usually is provided as “sound card”, which is connected via PCIe bus to the CPU. In most cases, “Sound card” has all the components necessary to process audio. But in embedded system, this might not apply.
In embedded system, there might be multiple components scattered on board (or on chip). Basically, sound components are broken down into 3 different categories.
- CPU DAI (CPU Digital Audio Interface)
- CODEC DAI (Audio CODEC Interface)
- Platform DAI (Audio DMA Interface etc.)
First one handles physical audio links, and audio protocols. For example, I2S, SPDIF, PCM, AC97, and so on. Usually, this component handles only audio signals (Except AC97. This interface carries both control signals and audio signals on the same bus).
Second one is Audio CODEC, which deals with audio controls like volume, mixing, routing, clocking and so on.
The last one is not mandatory in most cases. This category includes audio DMA engine driver and so on.
In ALSA framework, these components are called DAI component. DAI stands for Digital Audio Interface.
Zedboard specific on-board DAI components
To configure device drivers properly, we need to know what’s on board. Zedboard has DAI components listed below;
- I2S transmitter (CPU DAI)
- I2S receiver (CPU DAI)
- ADAU1761 Audio CODEC (CODEC DAI)
- Audio Formatter (Platform DAI)
The device drivers for those components are provided in official Xilinx Linux kernel repository.
Official Xilinx Linux Kernel repository
By default, all those drivers are not included in configuration. That means, we need to configure the kernel appropriately to include all those drivers.
Also, remember, Audio CODEC ADAU1761 is connected via I2C bus in our case, which means, we need Xilinx AXI I2C device driver as well. What we need to configure is listed below;
- ALSA (Advanced Linux Sound Architecture)
- Xilinx Audio Formatter device driver
- Xilinx I2S driver (For both I2S transmitter and receiver)
- Xilinx AXI I2C device driver (I2C bus driver in PL)
- Analog Devices ADAU1761-I2C CODEC driver
- Xilinx PL Sound Card machine driver (This has to be modified)
Because Audio CODEC LSI is not Xilinx IP core, and implemented outside the Zynq chip, Audio CODEC driver is not configured in Xilinx PL Sound Card driver. This means, we need to modify this source code, and add this specific Audio CODEC handling.
NOTE: I also tried “Simple Sound Card” machine driver. By using this, we can configure machine driver by describing DAI links in device tree. This is pretty convenient since we don’t have to modify device driver source code, but this didn’t work for me (I guess, it doesn’t take care of platform driver, and therefore, the DMA driver is not included nor configured properly).
Linux kernel configuration
Now we know what should be configured, let’s try it.
NOTE: I’m assuming, cross compiler, libraries etc… to build the Linux kernel are prepared already. If not, prepare tool-chain for ARMv7 first (If you have Vitis IDE, it’s included. Just add the path to tool-chain).
Clone the kernel from the link above, and in kernel source code directory. Before configuring kernel, we need to modify quite a few lines in “sound/soc/xilinx/Kconfig”.
config SND_SOC_XILINX_PL_SND_CARD
tristate "Audio support for the the Xilinx PL sound card"
depends on SND_SOC_XILINX_AUDIO_FORMATTER
depends on SND_SOC_XILINX_I2S
depends on SND_SOC_XILINX_SDI
select SND_SOC_HDMI_CODEC
help
Select this option to enable Xilinx PL sound card
support. This enables sound card using xilinx soft IPs
in audio pipeline.
Modify bold part like below.
config SND_SOC_XILINX_PL_SND_CARD
tristate "Audio support for the the Xilinx PL sound card"
depends on SND_SOC_XILINX_AUDIO_FORMATTER
depends on SND_SOC_XILINX_I2S || SND_SOC_XILINX_SDI
select SND_SOC_HDMI_CODEC
help
Select this option to enable Xilinx PL sound card
support. This enables sound card using xilinx soft IPs
in audio pipeline.
This makes “Xilinx PL sound card” configuration visible without adding unnecessary module.
Close Kconfig and type those command.
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- xilinx_zynq_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
menuconfig is a tool to configure the kernel interactively. You will see screen like below.
Let’s configure everything. I’ll show you where you can find all the device drivers. Press “y” on selected line to include those.
- ALSA driver
Device Drivers -> Sound card support -> Advanced Linux Sound Architecture
- Xilinx Audio Formatter device driver
Device Drivers -> Sound card support -> Advanced Linux Sound Architecture -> ALSA for SoC audio support -> Audio support for the the Xilinx audio formatter
- Xilinx I2S driver (For both I2S transmitter and receiver)
Device Drivers -> Sound card support -> Advanced Linux Sound Architecture -> ALSA for SoC audio support -> Audio support for the Xilinx I2S
- Analog Devices ADAU1761-I2C CODEC driver
Device Drivers -> Sound card support -> Advanced Linux Sound Architecture -> ALSA for SoC audio support -> CODEC drivers -> Analog Devices AU1761 CODEC — I2C
- Xilinx PL Sound Card machine driver (This has to be modified)
Device Drivers -> Sound card support -> Advanced Linux Sound Architecture -> ALSA for SoC audio support -> CODEC drivers -> Audio support for the the Xilinx PL sound card
- Xilinx AXI I2C device driver (I2C bus driver in PL)
Device Drivers -> I2C support -> I2C Hardware Bus support -> Xilinx I2C Controller
That’s everything. Select “<Save>” and then “<Exit>”.
Let’s build the kernel.
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8 zImage modules
“-j” option specifies number of parallel build jobs. Adjust that number to suit your environment.
Device Tree Blob
Device tree blob is necessary to install/configure platform device driver properly. I’ll skip the detail about device tree, but will explain how to generate device tree blob.
In last article, I explained how to generate Device Tree Source files (dts and dtsi). Go to generated directory and open “pl.dtsi”, and find “axi_iic_0” in the file.
axi_iic_0: i2c@41600000 {
#address-cells = <1>;
#size-cells = <0>;
clock-names = "s_axi_aclk";
clocks = <&misc_clk_0>;
compatible = "xlnx,axi-iic-2.0", "xlnx,xps-iic-2.00.a";
interrupt-names = "iic2intc_irpt";
interrupt-parent = <&intc>;
interrupts = <0 29 4>;
reg = <0x41600000 0x10000>;
};
Since Audio CODEC is outside of the Zynq chip, this device is not described in generated device tree. Add lines indicated bold like below.
axi_iic_0: i2c@41600000 {
#address-cells = <1>;
#size-cells = <0>;
clock-names = "s_axi_aclk";
clocks = <&misc_clk_0>;
compatible = "xlnx,axi-iic-2.0", "xlnx,xps-iic-2.00.a";
interrupt-names = "iic2intc_irpt";
interrupt-parent = <&intc>;
interrupts = <0 29 4>;
reg = <0x41600000 0x10000>;
adau1761: adau1761@38 {
#sound-dai-cells = <0>;
compatible = "adi,adau1761";
clock-names = "mclk";
clocks = <&misc_clk_1>;
reg = <0x38>;
};
};
By default, I2C address GPIO output is set to “0”, so the I2C address should be “0x38”. Also, clock name “misc_clk_1” should be correct, but double check.
Compile device tree source using the command below.
<path to Linux source>/scripts/dtc/dtc -I dts -O dtb -o zynq-zed.dtb system-top.dts
Booting Linux
Now the kernel is built and ready. To boot Linux on Zedboard, we need;
- FSBL (First Stage Bootloader)
- U-boot (Seconf stage Bootloader)
- Linux Kernel Image (zImage)
- Device Tree Blob (zynq-zed.dtb)
- Hardware bitstream (system.bit)
How to boot Linux on Zedboard is explained in articles below. So I’ll skip that in this article.
Bootloader: Booting device from SD
Second-Stage Bootloader (U-Boot)
I haven’t explained how to prepare “root filesystem” specifically for Zedboard, but in the end of the article below, how to prepare root filesystem is explained. One thing to note is that, rootfs for Raspberry pi was for AArch64 in the article, but it has to be for armv7 for Zedboard.
Building and booting fully customized OS on Raspberry Pi
If you just want to do it easily, you can go to Linaro’s official website, and download root filesystem image.
If the Linux is booted successfully, type the command below.
dmesg | grep xlnx
You should see something like below;
[ 1.701826] xlnx_formatter_pcm 43c00000.audio_formatter: sound card device will use DAI link: i2s_transmitter
[ 1.711522] xlnx_formatter_pcm 43c00000.audio_formatter: sound card device will use DAI link: i2s_receiver
[ 1.720082] xlnx_formatter_pcm 43c00000.audio_formatter: pcm platform device registered
[ 1.727168] xlnx_i2s 43c20000.i2s_receiver: xlnx_i2s_capture DAI registered
[ 1.733032] xlnx_i2s 43c10000.i2s_transmitter: xlnx_i2s_playback DAI registered
[ 1.746225] xlnx_snd_card xlnx_snd_card.0.auto: snd_soc_dummy <-> 43c10000.i2s_transmitter mapping ok
[ 1.755219] xlnx_snd_card xlnx_snd_card.0.auto: snd_soc_dummy <-> 43c20000.i2s_receiver mapping ok
[ 1.771863] xlnx_snd_card xlnx_snd_card.0.auto: xlnx-pl-snd-card-0 registered
If you see “mapping ok” messages for both I2S transmitter and I2S receiver, this step is successful. If you don’t see it, and get error messages, the kernel version might not be the latest. I had problems with old kernel.
But as you may see in the messages above, Audio CODEC is recognized as “snd_soc_dummy”. This is actually dummy device, and doesn’t do anything.
I’ll explain how to let the driver recognize Audio CODEC, and how to make it usable in next article.