Linux device driver for Zedboard audio (1/2)

Yuhei Horibe
7 min readMay 10, 2020

--

ALSA (Advanced Linux Sound Architecture)

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.

Linux kernel menuconfig

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)

Booting Linux with ramdisk

Booting Linux from network

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.

--

--

No responses yet