HiFiBerry on Debian ARM64

Long are gone the days of the ten thousands songs Winamp playlist, the modern way of listening to music consists solely of spotify blasting its (not-so-randomly) set of tracks and ads on random variants of laptop/phones/speakers.

But I will forever say no to that.
Spotify is bad for the artists, for you, our planet and it has as much a good impact on musical culture as Facebook has on our social life. Today, the common physical storage available locally (if you don’t depend uniquely on the cloud) has more than enough room to host all the music you’d ever want to listen to. Of course this means that you have to handle your music playlist manually. But that effort will refine your tastes as to what is genuinely good music to your ears, and what is just effective product placement.

To handle my music media center, I always had a RPi stucked to my HiFi system. This RPi would only handle playing music (mpd) and remote control (Cantata, MPDroid, ). This music would be stored remotely on my NAS and accessible in read-only from NFS (yup NFS not SAMBA/CIFS).

The music is played through a RPi HAT, the HiFiBerry DAC+ based on Texas Instruments PCM5102A DAC chip. This was done on a common Raspbian (the only viable solution back then), may be you could do the same easily on the new RaspberryPi OS. I did try to replicate the same setup on FreeBSD but neither the HiFiBerry nor the PCM5102 codec were supported. Attempts to play the music through a USB audio device instead of the HiFiBerry HAT were ultimately unsuccessful probably because of the USB DWC2 host driver. The same attempt on a FreeBSD running RPi4 with PCIe XHCI USB host ran the same USB audio devices perfectly well.

[Debian Diversity Logo -- https://gitlab.com/valessiobrito/artwork (GPLv3 -- https://gitlab.com/valessiobrito/artwork)]

But since I only had a single RPi4 that was already in use, and the fact that RPi4 are currently way overpriced and out-of-stock, I resolved to try a Linux based setup on a RPi3B. But you see, Raspbian is rather restricted by the fact that the RPi platforms ranges from ARMv6 to ARMv8 SoC. That’s 32 to 64-bit, so as a compromise Raspbian runs everything in 32-bit even on ARM64 capable SoC like the RPi3 or better (the new Raspberry Pi OS has some ARM64 images although it’s not widely advertised). The challenge was that it should run Debian ARM64 and play audio through the HiFiBerry DAC+.

The main problem is that the Debian ARM64 image runs a mainline/vanilla Linux kernel. Among other things this version of the kernel does not have support for the HiFiBerry. Instead I resolved to compile my own kernel with the appropriate driver included. Also to avoid the hurdle of porting everything HiFiBerry related and other RPi/BCM goodies to the mainline kernel, I used the Raspberry Pi Foundation kernel source tree.

As a starting point, I used the configuration of the RPi3B kernel from the Debian ARM64 image, which you can find in /boot/config-5.10.0-8-arm64. Crosscompiling the kernel for ARM64 was pretty straight forward:

# Mount the RPi image
mount /dev/sdf1 /mnt/rpi/fat32
mount /dev/sdf2 /mnt/rpi/ext4

# Install and clone the kernel
apt-get install crossbuild-essential-arm64
git clone git://github.com/raspberrypi/linux.git

# Setup some variable for cross-compiling.
cd linux
export KERNEL=kernel8
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-

# Kernel configuration
# (be sure to select the appropriate platform or the Device Tree Blob won't be compiled)
cp /mnt/rpi/ext4/boot/config-5.10.0-8-arm64 .config
make oldconfig
make menuconfig

# Compile!
make Image modules dtbs -j32

# Install
# (you might have to create the 'overlays' directory in the fat32 partition)
VMLINUZ=vmlinuz-5.10.74+
make INSTALL_MOD_PATH=/mnt/rpi/ext4 modules_install
cp arch/arm64/boot/dts/broadcom/*.dtb  /mnt/rpi/fat32
cp arch/arm64/boot/dts/overlays/*.dtb* /mnt/rpi/fat32/overlays/
cp arch/arm64/boot/Image "/mnt/rpi/fat32/${VMLINUZ}"
cp arch/arm64/boot/Image "/mnt/rpi/ext4/boot/${VMLINUZ}"

# Umount the RPi image and boot it still on the old working kernel.
umount /dev/sdf1
umount /dev/sdf2

# Connect to the RPi and create the initrd.
# This should also adapt the boot config. 
update-initramfs -c -k 5.10.74+

If you want to play with Device Tree overlays for example to use a RPi HAT like the HiFiBerry, you will have to compile the dtoverlay manipulation command from the RPi userland repository. Install libfdt-dev, compile and install libdtovl in helpers/dtoverlay, compile dtoverlay in host_applications/linux/apps/dtoverlay.

You need ConfigFS and the following kernel options for the dtoverlay command to work with dynamic DT:

CONFIG_DTC=y
CONFIG_OF=y
CONFIG_OF_UNITTEST=y
CONFIG_OF_DYNAMIC=y
CONFIG_OF_OVERLAY=y
CONFIG_OF_CONFIGFS=y

Also mount ConfigFS:

mkdir /config
mount -t configfs none /config

The command expects to find the overlays in /boot/overlays but on the Debian ARM64 image they will probably be in /boot/firmware/overlays. I fixed this with a symlink.

Next I had to fiddle a bit with the Device Tree sources. If you want some nice documentation about them, here’s a nice pdf from Freescale that explains a lot.

When I first tried to add the HiFiBerry overlay with dtoverlay hifiberry-dacplus, it could not apply because of “incompatible devices”. This was caused by discrepancies between the ARM and ARM64 DT sources for the RPi. This driver was made for the ARM arch than runs Raspbian, not ARM64. And the ARM arch DTS/DTSI exposes devices differently than ARM64. I added the missing devices on ARM64, recompiled and reinstalled the DTB on the RPi image. After that the DT overlay applied like a charm.

The HiFiBerry DAC+ is now happily playing music on Debian ARM64. The RPi3B runs at ~1.14W on idle and ~1.35W when playing music. I don’t how know much the fat-free low-power kernel config contributes to that, but it seems to run at ~1.25W on Debian’s vanilla kernel and I have some ideas to reduce the consumption even further.