How to add a Speaker via i2s and enable it in Yocto
Base for this project is the Variscite VAR-SOM-MX7 together with its development board.
Goal of this project is to get a speaker running over i2s on an embedded Linux system, and get a deeper understanding of the linux environment in general.
First steps
This guide assumes you already successfully compiled Yocto and have basic knowledge of navigating through an linux environment.
There are 2 things needed for this whole thing to work. First, Linux needs the driver for the Speaker/Microphone installed and loaded, so it can communicate with it. Second it needs to know where the Hardware is mapped to.
1. Driver
1.1 Find the Driver
First we check if the driver is already installed on the system. lsmod
shows the currently loaded modules and is just a pretty view of the /proc/modules directory. If the driver isn't listed there, there is still the possibility that the driver isn't loaded. To see all modules, list the contents of the /sys/module directory with ls /sys/module
. You can load the driver with modprobe <module-name>
.
To see if the driver is statically built into the kernel we use cat /lib/modules/$(uname -r)/modules.builtin
. This will give us a list of all the modules that statically built into the kernel and are not removable without switching the kernel.
If the required driver isn't listed anywhere, it's not in the kernel, and so we have to compile it. There are 2 ways to do that. We either bake it directly into the kernel, or we set it up as a Loadable Kernel Module (KVM), which we can load and unload whenever we want. This is recommended, since it doesn't necessarily crash your kernel when a driver stops working, and you can load an updated module instead of recompiling the kernel, or unload it when you don't need it.
1.2 Compile the Driver
To compile the kernel module or to bake the driver into the kernel we need the source files of our Yocto build. We assume you have set that up already.
Now run the setup environment:
cd ~/var-fslc-yocto
MACHINE=imx7-var-som DISTRO=fslc-x11 . setup-environment build_x11
The above command is only mandatory for the very first build setup: whenever restarting a newer build session (from a different terminal or in a different time), you can skip the full setup and just run:
cd ~/var-fslc-yocto
source setup-environment build_x11
After that, run the menuconfig, this is used to configure the Linux kernel:
bitbake -c menuconfig virtual/kernel
This command will show a TUI (Terminal User Interface). In this we can configure our Linux kernel.
Now to set our driver as a module or built-in, we have to go into the driver settings. First, got into Device Drivers.
Next go to Sound card support.
Go to Advanced Linux Sound Architecture (ALSA).
Finally, open ALSA for SoC audio support.
In this menu, search for your driver. By pressing Y you include the driver in the kernel, N excludes it, and M sets it as a module.
Note: You can also do these steps by editing the config file, which is located
~/var-fslc-yocto/build_x11/tmp/work-shared/imx7-var-som/kernel-source/arch/arm/configs/imx_v7_var_defconfig
.Note: If your driver isn't in the menuconfig, it is possible that is is just not shown. Check
~/var-fslc-yocto/build_x11/tmp/work-shared/imx7-var-som/kernel-source/driver/
or in my case~/var-fslc-yocto/build_x11/tmp/work-shared/imx7-var-som/kernel-source/sound/soc/codecs/
. If you find it under there, edit the Kconfig file in the same directory. Scroll to you specific driver, and add a description after tristate just like this:
Now the driver will be compiled as a Kernel module. The Kernel will usually load the driver itself, if it finds it in the device tree. That will be the next thing to do, editing the device tree.
2. Device tree
The device tree is located under ~/var-fslc-yocto/build_x11/tmp/work-shared/imx7-var-som/kernel-source/arch/arm/boot/dts/
. You can inspect the config to see which device tree files are used: ~/dev/var-fslc-yocto/sources/meta-variscite-fslc/conf/machine/imx7-var-som.conf
. In our case it's the imx7d-var-som.dtsi
:
vim ~/var-fslc-yocto/build_x11/tmp/work-shared/imx7-var-som/kernel-source/arch/arm/boot/dts/imx7d-var-som.dtsi
We want to add another sound node, as well as a codec node for our max98357a driver. Just add another sound node like this:
sound0:sound@0 {
compatible = "simple-audio-card";
simple-audio-card,name = "max98357aaudio";
simple-audio-card,format = "i2s";
simple-audio-card,cpu {
sound-dai = <&sai2 0>;
};
simple-audio-card,codec {
sound-dai = <&max98357a>;
};
};
Note: The sound has to be placed under the root node /, That means in between the curly brackets {}.
Our new sound node has the name sound, with the unit adress 0. We also set the label sound0, but since we dont refer to it, it is optional. Since now 2 nodes have the name sound, we need to change the existing sound node to sound@1.
sound1: sound@1 {
...
Now we add the the codec node, also under the root node:
max98357a: max98357a {
#sound-dai-cells = <0>;
compatible = "maxim,max98357a";
};
Finally, we need to activate our i2s node, since it's disabled by default. This device tree names them sai (Serial Audio Interface). So search for the i2s interface you connected your driver to, in this case sai2, and change it's status from disabled to okay.
&sai2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sai2>;
assigned-clocks = <&clks IMX7D_SAI2_ROOT_SRC>,
<&clks IMX7D_SAI2_ROOT_CLK>;
assigned-clock-parents = <&clks IMX7D_PLL_AUDIO_POST_DIV>;
assigned-clock-rates = <0>, <36864000>;
status = "okay"; //Set this to from disabled to okay
};
This should be enough to get it working.
3. Build Yocto
Just compile the kernel and rebuild the project, and you're done.
bitbake virtual/kernel
bitbake fsl-image-gui
Note: With the -f option you can force a build.
4. Check if everything works as intended
After you successfully built your Yocto image, load the image onto your Board, and start it up. Open the terminal and check if the driver is loaded.
lsmod
lsmod
shows the currently loaded modules and is just a pretty view of the /proc/modules directory. If the driver isn't listed there, there is still the possibility that the driver isn't loaded. To see all modules, list the contents of the /sys/module directory with ls /sys/module
. You can load the driver with modprobe <module-name>
. If your kernel module doesn't show up anywhere, check again if you did your kernel configuration correctly, and if you compiled your kernel correctly.
Now if that works, we need to check if ALSA detects our soundcard. we can check with aplay -l
if our soundcard is detected. If not, check with dmesg for possible clues why it wasn't detected.
5. Play Music
If everything is alright, then test your driver with speaker-test
. Or play some music with a music player, like mplayer or mpv.
The End.