Hello,
I wrote a codec driver and modified the "fsl-asoc-card.c" driver to work with my system, which went surprisingly well.
My system consists of the following:
iMX8M SAI2 connected to a TLV320ADC3100 with SAI/I2S interface plus I2C3 for register read/write.
The expectation is that the iMX8M provides MCLK out of its pin named:
And based on this, the TLV320ADC3100 will generate BCLK and WCLK (or BCLK and FS, as you wish).
I plan to have 44100Hz sampling rate, so that's what I set up in the I2C driver, which would result in a BCLK frequency of 705.6kHz (fs * bitdepth = 44100 Hz * 16bit)
After playing around with the device tree, I was finally able to get the output on the MCLK pin, but it looks "odd".
Here are the relevant entries:
With such a setting, the driver gets loaded, maps up to the SAI interface, everything looks sunny.
According to dmesg:
And I'm able to start a recording as a sound card gets registered:
Now it hangs here and my oscilloscope shows the following on pins MCLK and BCLK:
GREEN -> MCLK (from iMX8)
YELLOW -> BCLK (from TLV320ADC3100 codec)
(This is a zoomed oscilloscope view, the unzoomed "preview" is on top, the zoomed part is on the bottom)
As you can see MCLK is around the expected 11.26 MHz (close enough for now...)
BCLK, generated by the CODEC TLV320ADC3100 also seems correct with its 702kHz (again, not perfect, but good for now)
The "odd" behavior is in-between, when it looks like the MCLK goes away, then returns (upper view is unzoomed, in the red rectangle MCLK disappears, then comes back again)
Any idea why this happens and how to fix it would be greatly appreciated!
Hello Krisztian,
How long does MCLK keep a continuous clock from recording?
I mean, when the recording begins - > MCLK provides the clock, how long after the MCLK is discontinuous.
Have a nice day!
BR,
Weidong
Hello Weidong,
It looks like it's approximately 4.6us clock, then ~20us skip, then another ~2us clock, then it dies.
The screenshot you see above is triggered by arecord -d 1 --rate=44100 --channels=1 -f S16
That's the full event that happens. You press enter on the above command, the image you see shows up on the oscilloscope, then everything just hangs.
If that helps, the command actually hangs on a Poll() function (if you trace it with strace)
Hello Krisztian,
I checked i.MX8MQ source code and tlv320adc3100 datasheet, which is a codec with I2S/PCM interface, it's usage is very similar to WM8524 supported on EVK board. We only need to add a RX pin for record.
In addition, audio clock source is AUDIO_PLL1_OUT, which is an integral multiple of 24576000, but it's not divisible from 11289600, so you should set it to be 24576000, which is also in the input range of tlv320adc3100's MCLK (512K~50M).
Below is for your reference:
/*SAI2 Multiplexing for I2S interface--- You only need 5 pins for your audio codec*/
pinctrl_sai2: sai2grp {
fsl,pins = <
MX8MQ_IOMUXC_SAI2_TXFS_SAI2_TX_SYNC 0xd6
MX8MQ_IOMUXC_SAI2_TXC_SAI2_TX_BCLK 0xd6
MX8MQ_IOMUXC_SAI2_MCLK_SAI2_MCLK 0xd6
MX8MQ_IOMUXC_SAI2_TXD0_SAI2_TX_DATA0 0xd6
MX8MQ_IOMUXC_SAI2_RXD0_SAI2_RX_DATA0
/* MX8MQ_IOMUXC_GPIO1_IO08_GPIO1_IO8 0xd6 */
>;
};
... ...
/* codec and sound card */
tlv320adc3100: tlv320adc3100 {
compatible = "wlf,tlv320adc3100";
clocks = <&clk IMX8MQ_CLK_SAI2_ROOT>;
clock-names = "mclk";
/*wlf,mute-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>;*/
};
sound-tlv320adc3100 {
compatible = "fsl,imx-audio-tlv320adc3100";
model = "imx-tlv320adc3100";
audio-cpu = <&sai2>;
audio-codec = <&tlv320adc3100>;
audio-routing = /* routing should be added here according to codec driver */
"CPU-Capture", "Capture";
status = "okay";
};
... ...
&sai2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sai2>;
assigned-clocks = <&clk IMX8MQ_CLK_SAI2_SRC>,
<&clk IMX8MQ_CLK_SAI2_DIV>;
assigned-clock-parents = <&clk IMX8MQ_AUDIO_PLL1_OUT>;
assigned-clock-rates = <0>, <24576000>;
status = "okay";
};
Try it , please!
Have a nice day!
Weidong
Thank you for the mentioned points Weidong.
After a lot of tinkering around I figured out what the problem is.
I was able to get the clock and my driver to work "properly" with "simple-audio-card", but not with the fsl-asoc-card.
This led me to figure out that something is missing from either the fsl-asoc-card.c or my driver.
Sure enough I found this note in fsl-asoc-card.c:
So it turns out that the sound card is waiting for my driver to enable the clock output (which then brings up another question: why and what enabled the clock in the original post?!?)
Taking a look in wm8904.c I found the part where they enable the clock:
Where they get the clk struct wm8904->mclk while probing the driver:
This supposedly points to the same device tree entry you suggested I make:
Now I went ahead and modified my driver, so that it also does the same (my tlv320adc3100.c):
When I boot, the driver loads fine, it doesn't complain that it doesn't find "mclk" so I assume that part is OK?
However, the moment I run
root@b1sample:~# arecord --rate=48000 lofasz.wav --duration=1 -f s16 -c 1
I get a kernel panic, the board dies, everything goes up in flames...
Guess it's a NULL pointer based on the output while following dmesg, but I have no idea why...
All the other Codec drivers do the same call, I guess those don't die...
root@b1sample:~# dmesg -w
[ 114.752749] tlv320adc3100-codec 2-001b: Setting FLL
[ 114.756357] tlv320adc3100-codec 2-001b: Setting sysclk
[ 114.760387] tlv320adc3100-codec 2-001b: Setting HW Params
[ 114.764671] fsl-sai 308b0000.sai: SAI is set to slave mode
[ 114.769462] tlv320adc3100-codec 2-001b: Enabling this f%$%#$ing clock finally...
[ 114.775476] Unable to handle kernel NULL pointer dereference at virtual address 00000010
[ 114.782453] pgd = ffff000008a7e000
[ 114.784552] [00000010] *pgd=00000000ffffe003, *pud=00000000ffffd003, *pmd=0000000000000000
[ 114.791852] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[ 114.796120] Modules linked in: rfcomm bnep hci_uart qcserial usb_wwan qmi_wwan cdc_ether cdc_wdm usbserial fec brcmfmac cfg80211 brcmutil ax88179_178a usbnet mii bluetooth xhci_plat_hcd xhci
_hcd usbcore dwc3 udc_core snd_soc_tlv320adc3100 industrialio_triggered_buffer g
alcore(O) ipv6 autofs4
[ 114.830878] CPU: 3 PID: 445 Comm: kworker/u8:2 Tainted: G O 4.9.51-imx_4.9.51_imx8m_ga_var01+gfa349fc #24
[ 114.840266] Hardware name: Variscite DART-MX8M EMMC+WIFI+DCSS+LVDS (DT)
[ 114.845583] Workqueue: events_unbound async_run_entry_fn
[ 114.849600] task: ffff8000b9dd1800 task.stack: ffff8000b9e54000
[ 114.854220] PC is at tlv320adc3100_set_bias_level+0x30/0xb8 [snd_soc_tlv320adc3100]
[ 114.860573] LR is at tlv320adc3100_set_bias_level+0x30/0xb8 [snd_soc_tlv320adc3100]
[ 114.866923] pc : [<ffff0000007b60d8>] lr : [<ffff0000007b60d8>] pstate: 00000145
[ 114.873012] sp : ffff8000b9e57cd0
[ 114.875021] x29: ffff8000b9e57cd0 x28: 0000000000000000
[ 114.879049] x27: 0000000000000000 x26: 0000000000000000
[ 114.883076] x25: ffff000008a3348c x24: ffff800078109400
[ 114.887103] x23: ffff800078176100 x22: 0000000000000000
[ 114.891130] x21: 0000000000000003 x20: 0000000000000000
[ 114.895156] x19: 0000000000000000 x18: 0000000000000034
I think this will be the last piece that I'm missing, but this kernel panic really makes me wonder why would it be a NULL pointer...
Let me know if you have any ideas.
Hi
I am working on getting simple_audio_card work for imx8mq and pcm3060 codec, I am on kernel version 5.05 and it appears both the the platform driver (fsl_sai) and the codec driver (pcm3060) support the ASOC design and the simple audio card. Here is the snapshot of my dts for simple audio card
&ecspi2 {
fsl,spi-num-chipselects = <1>;
cs-gpios = <&gpio5 13 0>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ecspi2>;
status = "okay";
// As per the IMX8MDQLQRM applications processors reference manual PDF
// 7.1.4 SDMA event mapping
// NOTES:
// - our transfer sizes aren't big enough to justify DMA
// - we need a patched imx-smda.c unless we're using Linux 5.1 or newer
// dmas = <&sdma1 2 7 0>, <&sdma1 3 7 0>;
// dma-names = "rx", "tx";
pcm3060: pcm3060@0 {
#sound-dai-cells = <0>;
compatible = "pcm3060";
spi-max-frequency = <20000000>;
reg = <0>;
};
};
sai1: sai@30010000 {
#sound-dai-cells = <0>;
compatible = "fsl,imx8mq-sai",
"fsl,imx6sx-sai";
reg = <0x0 0x30010000 0x0 0x10000>;
interrupts = <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk IMX8MQ_CLK_SAI1_IPG>,
<&clk IMX8MQ_CLK_SAI1_ROOT>,
<&clk IMX8MQ_AUDIO_PLL1>,
<&clk IMX8MQ_CLK_DUMMY>;
clock-names = "bus", "mclk1", "mclk2", "mclk3";
dmas = <&sdma1 10 24 0>, <&sdma1 11 24 0>;
dma-names = "rx", "tx";
fsl,dataline = <0xff 0xff>;
status = "disabled";
};
&sai1 {
#sound-dai-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sai1>;
assigned-clocks = <&clk IMX8MQ_SAI1>;
assigned-clock-parents = <&clk IMX8MQ_AUDIO_PLL1_OUT>;
assigned-clock-rates = <49152000>;
status = "okay";
};
pinctrl_sai1: sai1grp {
fsl,pins = <
MX8MQ_IOMUXC_SAI1_MCLK_SAI1_MCLK 0xd6
MX8MQ_IOMUXC_SAI1_TXFS_SAI1_TX_SYNC 0xd6
MX8MQ_IOMUXC_SAI1_TXC_SAI1_TX_BCLK 0xd6
MX8MQ_IOMUXC_SAI1_TXD0_SAI1_TX_DATA0 0xd6
MX8MQ_IOMUXC_SAI1_RXD0_SAI1_RX_DATA0 0xd6
>;
};
sound {
compatible = "simple-audio-card";
simple-audio-card,name = "pcm3060-audio";
simple-audio-card,format = "i2s";
simple-audio-card,bitclock-master = <&dai_cpu>;
simple-audio-card,frame-master = <&dai_cpu>;
simple-audio-card,widgets =
"Line", "Balanced In",
"Line", "Balanced Out",
"Line", "Unbalanced Out",
"Line", "Unbalanced In";
simple-audio-card,routing =
"INL", "Balanced In",
"INR", "Unbalanced In",
"Balanced Out", "OUTL",
"Unbalanced Out", "OUTR";
dai_cpu: simple-audio-card,cpu {
sound-dai = <&sai1>;
};
dai0-codec: simple-audio-card,codec {
sound-dai = <&pcm3060>;
clocks = <&clk IMX8MQ_CLK_SAI1_ROOT>;
};
&clk {
assigned-clocks = <&clk IMX8MQ_AUDIO_PLL1>;
assigned-clock-rates = <49152000>;
};
The device tree specifically the clocks for sai. This is based on what is used on upstream (5.2.x) and NXP maintained 4.19.y for i.MX8mq branch. On upstream, they provide the bus clock and only one master clock (mclk1). This configuration is not fails when I am trying to playback an audio wav file. When the sai driver tries to set the bitclock for i2s. (sound/soc/fsl/fsl_sai.c :: fsl_sai_set_bclk), the m_clck provided to the driver [as specified in the dts] does not match the requirement of the sample rate of the audio being played. Following the code snippet of that function with my addition logging.
sai1: sai@30010000 {
#sound-dai-cells = <0>;
compatible = "fsl,imx8mq-sai",
"fsl,imx6sx-sai";
reg = <0x0 0x30010000 0x0 0x10000>;
interrupts = <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk IMX8MQ_CLK_SAI1_IPG>,
<&clk IMX8MQ_CLK_SAI1_ROOT>,
<&clk IMX8MQ_AUDIO_PLL1>,<-------------------------- On upstream it is IMX8MQ_CLK_DUMMY, I supplied AUDIO_PLL1 as one of the mclk and it does fit for the stream playback, but fails when it tries to prepare and enable that clock.
<&clk IMX8MQ_CLK_DUMMY>;
clock-names = "bus", "mclk1", "mclk2", "mclk3";
dmas = <&sdma1 10 24 0>, <&sdma1 11 24 0>;
dma-names = "rx", "tx";
fsl,dataline = <0xff 0xff>;
status = "disabled";
};
&sai1 {
#sound-dai-cells = <0>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sai1>;
assigned-clocks = <&clk IMX8MQ_SAI1>;
assigned-clock-parents = <&clk IMX8MQ_AUDIO_PLL1_OUT>;
assigned-clock-rates = <49152000>;
status = "okay";
};
&clk {
assigned-clocks = <&clk IMX8MQ_AUDIO_PLL1>;
assigned-clock-rates = <49152003>;
};
I get following logs
2018-03-09T12:39:49.515972+00:00 localhost kernel: [ 336.690525] 001: fsl-sai 30010000.sai: inside fsl_sai_hw_params channels=2 word_width=24 slots=2 slot_width=24
2018-03-09T12:39:49.516023+00:00 localhost kernel: [ 336.690545] 001: fsl-sai 30010000.sai: inside fsl_sai_set_bclk for freq: 768000Hz
2018-03-09T12:39:49.516028+00:00 localhost kernel: [ 336.690554] 001: fsl-sai 30010000.sai: failed to get the required clock rate 12500000Hz for frequency: 768000Hz ----> bus
2018-03-09T12:39:49.516032+00:00 localhost kernel: [ 336.690563] 001: fsl-sai 30010000.sai: failed to get the required clock rate 25000000Hz for frequency: 768000Hz -----> mclk1 --- > SAI1_ROOT
2018-03-09T12:39:49.516036+00:00 localhost kernel: [ 336.690571] 001: fsl-sai 30010000.sai: ratio 64 for freq 768000Hz based on clock 49152001Hz -----------------------------------------> mclk2 ---> AUDIO_PLL1 ----> it is configured to run at a desirable rate, but fails when the driver tries to prepare and enable the clock.
2018-03-09T12:39:49.516039+00:00 localhost kernel: [ 336.690579] 001: fsl-sai 30010000.sai: failed to get the clock 0Hz for frequency: 768000Hz
2018-03-09T12:39:49.516046+00:00 localhost kernel: [ 336.690602] 001: fsl-sai 30010000.sai: best fit: clock id=2, div=32, deviation =1
2018-03-09T12:39:49.516050+00:00 localhost kernel: [ 336.700617] 001: fsl-sai 30010000.sai: inside fsl_sai_hw_params could not prepare clk with clock id: 2
2018-03-09T12:39:49.526003+00:00 localhost kernel: [ 336.701229] 001: fsl-sai 30010000.sai: ASoC: can't set 30010000.sai hw params: -110
Can you please tell me that how were the clock arrangements in you device tree when you made it work with simple audio card
Cheers
Dear Krisztian,
MCLK is primary working clock of your audio codec, I think you shouldn't enable it in codec driver, because if the MCLK is not stable or doesn't exist before loading codec driver, codec driver can't normally start.
I think you should refer to soc/fsl/imx-wm8524.c (or imx-wm8960.c) to write a simple machine driver for your application, like imx-tlv320adc3100.c, which is sound card driver, and simple-audio-card.c is not recommended.
In imx-wm8524.c, MCLK's frequency is gotten from device tree, and set real frequency in wm8524_late_probe() function.
Hope above advice can help you!
Have a nice day!
BR,
Weidong
Well, that was my original thinking as well, but after seeing that almost _every_ codec driver does that, I thought... hey, why not?!
I don’t really understand however, how my codec driver would rely on MCLK. I configure my chip using I2C, that part works perfectly fine, to be honest I think it’s actually a good implementation not to clock the I2S when not needed. The datasheet of TLV320AD3100 doesn’t mention anything that it would need a continous clock.
For the machine driver part, I understood that all the necessary “glueing” happens in “fsl-asoc-card.c” or at least it seems to me that it’s a standalone implementation. For “simple-audio” I see why I should have a machine driver, but not for the asoc-card. Are you 100% sure I’m missing that and the codec driver+fsl-asoc-card is not enough to get this working?
In the iMX8M’s datasheet it actually refers to SAIn_ROOT_CLK as gateable out to the MCLK pad, would there be a setting to just do that instead of stopping it? (Given that the enable function starts to work?! :smileyhappy: )
I also think that I’m getting “mclk” from the device tree, or isnt’t that what “devm_get_clk(dev,”mclk”) supposed to do? Get the clock assigned to my i2c device, named “mclk”, which is supposedly SAI2_ROOT_CLK, which in this case turns out to be NULL?
Dear Krisztian,
>>Well, that was my original thinking as well, but after seeing that almost _every_ codec driver does that, I thought... hey, why not?!
Actually, SAI1_MCLK output a clock for audio codec, which can make customer cost donw, she doesn't need to buy a new OSC part for audio codec. if customer doesn't want to use SAI1_MCLK, she can use an independent OSC for the MCLK pin of audio codec.
>>I don’t really understand however, how my codec driver would rely on MCLK. I configure my chip using I2C, that part works perfectly fine, to be honest I think it’s actually a good implementation not to clock the I2S when not needed. The datasheet of TLV320AD3100 doesn’t mention anything that it would need a continous clock.
If there is no MCLK, probably audio codec can be configured by Host via I2C bus, but what we configured is those registers related on how codec I2S works, at this time, because MCLK doesn't exist, I2S can't begin to work according to configurations in registers.
so MCLK should exist after codec power on in order to ensure codec normally works.
>>For the machine driver part, I understood that all the necessary “glueing” happens in “fsl-asoc-card.c” or at least it seems to me that it’s a standalone implementation. For “simple-audio” I see why I should have a machine driver, but not for the asoc-card. Are you 100% sure I’m missing that and the codec driver+fsl-asoc-card is not enough to get this working?
Generally speaking, if we port a new codec to linux, we often write a new machine driver for sound card application. Actually, you can alos use simple-audio-card to be machine driver,but you should enable MCLK at first, I feel you can enable in fsl-asoc-card.c, or directly enable it in drivers/clk/imx/clk-imx8mm.c,which can make it continuous. Try it, please!
>>In the iMX8M’s datasheet it actually refers to SAIn_ROOT_CLK as gateable out to the MCLK pad, would there be a setting to just do that instead of stopping it? (Given that the enable function starts to work?! )
Gate function is mainly used for low power mode. if there is no audio playing, disable the clock, otherwise, enable it.
>>I also think that I’m getting “mclk” from the device tree, or isnt’t that what “devm_get_clk(dev,”mclk”) supposed to do? Get the clock assigned to my i2c device, named “mclk”, which is supposedly SAI2_ROOT_CLK, which in this case turns out to be NULL?
I think "devm_get_clk(dev,”mclk”)" is right, because in imx-wm8524.c, the same way has been used.
I feel before loading codec driver, MCLK doesn't exist( you will enable it in codec driver), and audio codec can't work, so errors occured.
Let me summarize it , IF you want to use simple audio card to be machine driver:
1. Keep MCLK continuous.
or
2. enable MCLK in platform driver , not codec driver.
This is my advice for you, Could you try it?
Have a nice day!
BR,
Weidong
Hello Weidong,
I am looking into a similar issue we are having on the imx6sl. I have a codec that requires MCLK to always be running. We have wired our system to use the IMX6SL_CLK_EXTERN_AUDIO signal to drive MCLK on the codec. The issue I'm having is this clock is currently only running if there is audio playing back, what I would like to do is have that clock always running.
In your message, you mention that it's possible to
"directly enable it in drivers/clk/imx/clk-imx8mm.c,which can make it continuous"
Could you provide some more detail on how to make this change? I have tried adding IMX6SL_CLK_EXTERN_AUDIO to the list of "clks_init_on" in clk-imx6sl.c, that seems like it would work, but this causes the system to hang on boot.
Is there a better way to set IMX6SL_CLK_EXTERN_AUDIO to be always enabled?
Thanks,
Mike
While reading my own reply I realized that, really my question in a bit more streamlined approach is:
How do I enable SAI2_ROOT_CLK on the MCLK pin of the device? With all the settings above, it is not coming out of the device, so I assume something is missing.
fsl-asoc-card -> no MCLK
simple-card -> MCLK good, with good frequency
Why? :smileyhappy: