Hello, here Jorge. On this post I will explain how to enable MQS1 on i.MX8ULP.
As background about how to setup the environment to build the image using Yocto, please take a look on our i.MX Yocto Project User's Guide:
Requirements:
i.MX 8ULP EVK.
Serial console emulator (Tera Term, Putty, etc.).
USB Type-C cable.
Micro USB cable.
Headphones/speakers.
Linux PC.
Build done in Linux 6.6.23_2.0.0.
i.MX8ULP audio subsystem.
i.MX 8ULP extends audio capabilities on i.MX 7ULP by adding dedicated DSP cores for voice trigger and audio processing, enabling lower latency and power efficiency to support variety of audio applications.
Some of hardware blocks implemented on 8ULP to support audio use cases are the next:
Cadence Fusion F1 DSP processor.
Cadence HiFi4 DSP processor.
PowerQuad hardware accelerator with fixed and floating + FFT.
Digital Microphone interface with support of up-to 8 PDM channels.
Up-to 8 independent SAI instances.
Up-to 2 Medium Quality Sound (MQS).
Sony/Philips Digital interface (SPDIF).
As is described before, MQS0 and MQS1 are part of real time domain and application domain respectively. I’m going to focus this post on how to enable MQS1 on application domain.
Medium Quality Sound (MQS)
This module is basically generates a PWM from PCM audio data. For the major part of typical audio applications will require an external CODEC to deliver the audio quality but, sometimes where the application does not demand this quality, MQS can provide a medium quality audio via GPIO pin that can directly drive the audio output to a speaker or headphone via inexpensive external amplifier/buffer instead of CODEC.
The design of the MQS can be described as follows:
Input the PCM audio data (from SAI) into a 16-bit register.
Up-sample data to match PWM switching frequency.
Perform a simple 2nd order Sigma-Delta smooth on the current data versus previous data.
Convert the PCM register into a 6-bit PWM width register and output through a GPIO pin.
How to enable it?
By default, our BSP does not enable clock for MQS1. This clock is controlled on CGC1 (AD), specifically on MQS1CLK (Multiplexer to select the audio clock connected to the MQS clock input). So, it is needed to modify imx8ulp-clock.h and clk-imx8ulp.c. Please take a look on patch attached at the end of this post to see the modification in drivers easily.
These drivers have the definition/configuration for MQS1_SEL in CGC1 and needs to be added as follows:
MQS1_SEL definition needs to bed added in imx8ulp-clock.h:
#define IMX8ULP_CLK_MQS1_SEL 56
#define IMX8ULP_CLK_CGC1_END 57
MQS1_SEL configuration needs to be added in imx8ulp_clk_cgc1_init of clk-imx8ulp.c:
clks[IMX8ULP_CLK_MQS1_SEL] = imx_clk_hw_mux2("mqs1_sel", base + 0x90c, 0, 2, sai45_sels, ARRAY_SIZE(sai45_sels));
Also, it is necessary to configure MQS1 on device tree of i.MX8ULP.
Add this in soc: soc@0 of imx8ulp.dtsi:
mqs1: mqs@0x29290064 {
reg = <0x29290064 0x4>;
compatible = "fsl,imx8qm-mqs";
assigned-clocks = <&cgc1 IMX8ULP_CLK_MQS1_SEL>;
assigned-clock-parents = <&cgc1 IMX8ULP_CLK_SPLL3_PFD1_DIV1>;
clocks = <&cgc1 IMX8ULP_CLK_MQS1_SEL>, <&cgc1 IMX8ULP_CLK_MQS1_SEL>;
clock-names = "core", "mclk";
status = "disabled";
};
And create a new device tree, in this case is going to be named imx8ulp-evk-mqs.dts and is as follows:
#include "imx8ulp-evk.dts"
/ {
sound-simple-mqs {
compatible = "simple-audio-card";
simple-audio-card,name = "imx-simple-mqs";
simple-audio-card,frame-master = <&sndcpu>;
simple-audio-card,bitclock-master = <&sndcpu>;
simple-audio-card,dai-link@0 {
format = "left_j";
sndcpu: cpu {
sound-dai = <&sai4>;
};
codec {
sound-dai = <&mqs1>;
};
};
};
};
&cgc1 {
assigned-clock-rates = <24576000>;
};
&iomuxc1 {
pinctrl_mqs1: mqs1grp {
fsl,pins = <
MX8ULP_PAD_PTF7__MQS1_LEFT 0x43
>;
};
};
&mqs1 {
#sound-dai-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_mqs1>;
status = "okay";
};
&sai4 {
#sound-dai-cells = <0>;
assigned-clocks = <&cgc1 IMX8ULP_CLK_SAI4_SEL>;
assigned-clock-parents = <&cgc1 IMX8ULP_CLK_SPLL3_PFD1_DIV1>;
status = "okay";
};
Let’s apply these changes on our BSP, in my case I’m going to create a new layer in Yocto to add these modifications with a patch that can be found at the end on this post, here the steps:
Install essential Yocto Project host packages:
$ sudo apt install gawk wget git diffstat unzip texinfo gcc build-essential chrpath socat cpio python3 python3-pip python3-pexpect xz-utils debianutils iputils-ping python3-git python3-jinja2 python3-subunit zstd liblz4-tool file locales libacl1
Install the “repo” utility:
$ mkdir ~/bin
$ curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
$ chmod a+x ~/bin/repo
$ export PATH=~/bin:$PATH
Set up Git:
$ git config --global user.name "Your Name"
$ git config --global user.email "Your Email"
$ git config –list
Download the i.MX Yocto Project Community BSP recipe layers and create build folder:
$ mkdir imx-yocto-bsp
$ cd imx-yocto-bsp
$ repo init -u https://github.com/nxp-imx/imx-manifest -b imx-linux-scarthgap -m imx-6.6.23-2.0.0.xml
$ repo sync
$ DISTRO=fsl-imx-wayland MACHINE=imx8ulp-lpddr4-evk source imx-setup-release.sh -b 8ulp_build
Create the new layer:
$ cd ~/imx-yocto-bsp/sources
$ bibake-layers create-layer meta-mqs
$ cd meta-mqs
conf/layer.conf should be as follows:
BBPATH .= ":${LAYERDIR}"
BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \
${LAYERDIR}/recipes-*/*/*.bbappend"
BBFILE_COLLECTIONS += "meta-mqs"
BBFILE_PATTERN_meta-mqs = "^${LAYERDIR}/"
BBFILE_PRIORITY_meta-mqs = "6"
LAYERSERIES_COMPAT_meta-mqs = "nanbield"
Let’s change the recipe:
$ sudo rm -r recipes-example
$ mkdir -p recipes-kernel/linux/files
0001-8ULP-MQS-Enable.patch should be copied to ~/imx-yocto-bsp/sources/meta-mqs/recipes-kernel/linux/files
Add an append (on this case is called “linux-imx_%.bbappend”)to change the recipe with next content:
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
SRC_URI += "file:// 0001-8ULP-MQS-Enable.patch "
addtask copy_dts after do_unpack before do_prepare_recipe_sysroot
do_copy_dts () {
if [ -n "${DTS_FILE}" ]; then
if [ -f ${DTS_FILE} ]; then
echo "do_copy_dts: copying ${DTS_FILE} in ${S}/arch/arm64/boot/dts/freescale"
cp ${DTS_FILE} ${S}/arch/arm64/boot/dts/freescale/
fi
fi
}
The next step is add the layer and build the image:
$ cd ~/imx-yocto-bsp/8ulp_build
$ bitbake-layers add-layer ~/imx-yocto-bsp/sources/meta-mqs
Confirm that the layer has been added:
$ bitbake-layers show-layers
Build the image:
$ bitbake imx-image-multimedia
i.MX8ULP EVK limitations
The i.MX8ULP has the next MQS1 pins available:
But, in the EVK board, the mayor part of these pins are used for other functions such as:
- Push button:
- MIPI DSI:
- Etc…
So, take the output signal of MQS1 pins of EVK board is difficult, in this article, I’m going to configure PTF7 only (MQS1_left) for practicality. If you are working with this board and you need to use these pins for MQS function you will need to manipulate the traces and take the required signals. If you are designing a custom board, planning is essential to avoid this issue.
Flash the board.
One the build has been finished, we will have the necessary files to flash the board and test it. If you are not too familiarized with this process I suggest you take a look on this post.
First, put the board in serial download mode changing the boot configuration switches on the board:
The next step is connecting the power cable, micro-USB cable on the debug port and USB-C type cable to USB0 connector on the board.
Then, turn-on the board and run the next command in terminal of build directory:
uuu -b emmc_all imx-boot-imx8ulpevk-sd.bin-flash_singleboot_m33 imx-image-multimedia-imx8ulpevk.wic
Now, power-off the board, change the boot mode to single boot-eMMC and power it on to test it.
Test MQS1 in i.MX8ULP.
To test MQS1 it is needed to change the device tree we created, we can do it with the next commands in U-boot:
u-boot=> setenv fdtfile imx8ulp-evk-mqs.dtb
u-boot=> saveenv
u-boot=> boot
Now we can test MQS1 on i.MX8ULP EVK, let's confirm that the clock is active in MQS module with the next command:
$ cat /sys/kernel/debug/clk/clk_summary -n
As you can see mqs1_sel is active and running at 24576000 Hz:
And the card appears if we run the next command:
$ aplay -l
To play audio through MQS we can do it as any sound card:
$ speaker-test -D sysdefault:CARD=imxsimplemqs -c 2 -f 48000 -F S16_LE -t
pink -P 3
The signal should look like this in the pin output:
And like this after a filter, for example the filter used in i.MX93 EVK.
With this post we have been able check the general operation of MQS, configure and compile the image with the required changes to enable MQS1 on EVK board and measure the output on the board. There is a considerable limitation on EVK board since we cannot test left and right outputs without intervene the base board, but this can be helpful as a reference to who would like to use this audio output on i.MX8ULP processor.
Best regards.
References.
Yocto Project customization guide - NXP Community
How to add a new layer and a new recipe in Yocto - NXP Community
Flashing Linux BSP using UUU - NXP.
i.MX8ULP reference manual.
Embedded Linux Projects Using Yocto Project Cookbook.
View full article