HiassofT
Posts: 21
Joined: Fri Jun 30, 2017 10:07 pm

Re: I2S clocks, GPCLK0

Thu Oct 05, 2017 9:26 am

You mentioned that you modified the upstream cs42xx8 driver - I'd start by dropping these modifications, quite certainly this is interfering with proper operation.

As for clocking, there are a few things to keep in mind:

Like most codecs the cs42448 requiires BCLK and LRCLK to be synchronously derived from MCLK. As you are supplying MCLK via a different clock source than the bcm2835's PCM clock this means you have to configure the cs42448 to be the clock master - it'll then generate BCLK and LRCLK from MCLK.

So, in your sound card driver (in hwparams) you just have to configure GPCLK0 to 256..1024 * params_rate(params) - see datasheet and/or driver source code for allowed/needed values - and then tell the cs42448 driver about the frequency it's set up to - using snd_soc_codec_set_sysclk. hwparams in the cs42448 driver uses that value to configure the various ratio and single/double/quad-clocked register settings.

Your sound card driver will also need to set up a fixed bclk ratio of 64 - that's a requirement of using I2S in master mode with the cs42448.

See pages 31 and 32 of the datasheet.

so long,

Hias

PhilE
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 1311
Joined: Mon Sep 29, 2014 1:07 pm
Location: Cambridge

Re: I2S clocks, GPCLK0

Thu Oct 05, 2017 9:36 am

As you are supplying MCLK via a different clock source than the bcm2835's PCM clock this means you have to configure the cs42448 to be the clock master - it'll then generate BCLK and LRCLK from MCLK.
But on page 31 it also says:
In the TDM format the ADC and DAC serial ports will only operate as a slave.
Which leaves us with a problem.

HiassofT
Posts: 21
Joined: Fri Jun 30, 2017 10:07 pm

Re: I2S clocks, GPCLK0

Thu Oct 05, 2017 9:50 am

Yes, this combination won't work. If that is a problem it's best to use a different codec, there's not much we can do about it.

so long,

Hias

czyskows
Posts: 28
Joined: Sun Oct 07, 2012 5:42 am

Re: I2S clocks, GPCLK0

Thu Oct 05, 2017 4:38 pm

Hi, and thanks again.

Is there really no way to have the cpu in CBM/CFM and have this work? I can't believe it is impossible to drive the CS42448 with one of the RPI's clocks and make it work.

I'm still baffled as to why I get proper clock signals when both cpu and Codec are in slave mode, but still incorrect audio rates.

PhilE
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 1311
Joined: Mon Sep 29, 2014 1:07 pm
Location: Cambridge

Re: I2S clocks, GPCLK0

Thu Oct 05, 2017 4:46 pm

Can you carefully describe the samples, channels and clocking in your scenario, with a description or diagram of where the BCLKs and LRCLKs fall in relation to the sample data for each channel? Also confirm that your understanding matches what you see on the wire, assuming you have enough scope channels etc.

czyskows
Posts: 28
Joined: Sun Oct 07, 2012 5:42 am

Re: I2S clocks, GPCLK0

Thu Oct 05, 2017 5:04 pm

Yes, I will do this tonight when I get home from work.

Thanks!

One thing that occurs to me -
I'm setting the clock rate in the cs42xx8.c driver in the probe function:

Code: Select all

cs42xx8->clk = devm_clk_get(dev, "mclk");
	if (IS_ERR(cs42xx8->clk)) {
		dev_err(dev, "failed to get the clock: %ld\n",
				PTR_ERR(cs42xx8->clk));
		return -EINVAL;
	}

	clk_set_rate(cs42xx8->clk, 11289600);

	cs42xx8->sysclk = clk_get_rate(cs42xx8->clk);


Is this perhaps the wrong place to set the clk rate? Should this be done in the soundcard driver instead, in hw_params as Hias suggests?

PhilE
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 1311
Joined: Mon Sep 29, 2014 1:07 pm
Location: Cambridge

Re: I2S clocks, GPCLK0

Thu Oct 05, 2017 6:40 pm

I'm not sure it will make any difference to the behaviour, but modifying the codec is definitely not the right thing to do. As I've been trying to explain, codec drivers are supposed to be application neutral - all customisation should go in device tree declarations or the soundcard driver.

I wouldn't accept codec changes into the raspberrypi/linux repo, so for any chance of getting support for your card into a standard distribution you must put all your code into a card driver.

czyskows
Posts: 28
Joined: Sun Oct 07, 2012 5:42 am

Re: I2S clocks, GPCLK0

Fri Oct 06, 2017 6:32 am

Hi Phil,

Here are my clocks:

Image
Image

Sorry - my digital scope only has two channels and photos of my analog scope probably aren't helpful. The top image shows MCLK in yellow and BCLK in cyan. The second image has LRCK in yellow and BCLK in cyan. These show that the BCLK ratio is at 256XFS (11.289MHz = 44100*256) just as it should be. The other image shows the MCLK at 512XFS (22.5792 = 44100*512). This should be fine, as the CS42448 says the MCLK can be 256-1024*FS.

Image

As the data sheet says, the SCLK (or BCLK) must operate at 256Fs.

In my setup, I have

Code: Select all

static int raspberry_beret_dai_init(struct snd_soc_pcm_runtime *rtd)
{
	return snd_soc_dai_set_bclk_ratio(rtd->cpu_dai, 256);
}
As that seems to be the only way to get the BCLK to 256Fs. This is also still with both the cpu and codec as CBS/CFS, as that is the only way I get BCLK and LRCK. I'm also still (just for now) calling clk_set_rate in the cs42xx8.c driver, as I couldn't quickly (yet) figure out how to call that function from the soundcard driver. That is now the only change I have in the cs42xx8.c file. I've attached the relevant files if you care to have a look.

Rasp-beret.zip
(7.62 KiB) Downloaded 29 times
I can't thank you enough for giving this the time and energy that you have so far.

HiassofT
Posts: 21
Joined: Fri Jun 30, 2017 10:07 pm

Re: I2S clocks, GPCLK0

Fri Oct 06, 2017 12:48 pm

Looking through your code I noticed several important parts (startup, shutdown, hwparams) are still based on the octo driver. The hardest part in getting your driver right will be to forget most things you think you learned from the driver - several crucial parts are utterly wrong and lead you to wrong tracks with dead ends. Last year I spent quite some time to explain to Matt how he could do it properly, but unfortunately he didn't follow my suggestions and now we have a rather bad example in the tree... Better use any other card driver as a reference.

The first important thing to know is that bcm2835 i2s hardware doesn't support more than 2 channels so there's no easy way to use it for multichannel audio.

The hardware is prefectly capable of supporting TDM mode (DSP mode A and B) and a few months ago I extended the i2s driver to support these modes. The 2 channel limit still applies though, which means you can configure eg an 8-slot TDM setup but only 2 of these slots may be active.

To use TDM mode you configure bcm2835-i2s to SND_SOC_DAIFMT_DSP_A and then setup the slot configuration with snd_soc_dai_set_tdm_slot (instead of using snd_soc_dai_set_bclk_ratio). For example:

snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x03, 0x0c, 8, 32);

This configures bcm2835-i2s to 8 tdm slots of 32 cycles each, transmits data in slots 0 and 1 (the "0x03") and recevies data in slots 2 and 3 ("0x0c" parameter). The transmit and receive slot parameters are bitmasks (bit0=slot0, bit1=slot1, ... bit7=slot7 ) and you can choose them freely, but only exactly 2 set bits are accepted.

With this setup bcm2835-i2s will caculate the bclk frequency correctly, in the example above it will setup bclk = 8*32*samplerate = 256*samplerate.

Now to the things you need to forget about:

Don't change rtd->cpu_dai->driver->playback.channels_min. This is modifying a private datastructure of bcm2835-i2s. These constraints are setup by the driver for a reason, they reflect what the hardware and driver can do. Quite unsurprisingly this also doesn't work as expected. Back then the i2s driver checked for the number of channels (which had to be set to 2) and failed if one tried to play a multi-channel stream. Another hack was needed to make this working, but as a consequence the i2s driver still calculated the clocks wrong.

Setting both the cpu and codec to slave mode (CBS/CFS) in hw_params doesn't make much sense. In a dai link you always have one master and one slave. Just configure the mode of the codec in the snd_soc_dai_link array. Likewise setting the CPU modie to I2S and the codec mode to DSP_A is nonsense. Very obviously the part (codec) that links the CPU with the codec, drives the clocks and converts between I2S and DSP_A mode is missing.

So, how could you do multichannel via a 2-channel-only device? One method would be the tunneling approach. If you want 8 channely you send a stereo stream with 4 times the rate and make sure the receiver interprets 4 consecutive stereo samples as a single 8-channel sample. You can implement the "packing" in a userspace alsa plugin (in a very easy approach keep the data unchanged and simply set HW channels to 2 and sample rate to 4x original rate) so you'll have an 8 channel PCM in userspace alsa. if bcm2835-i2s is configured for 32 clocks per sample (bclk ratio 64 or 2 tdm slots of 32bit each) the data stream and bclk will already be correct. The only thing that's missing is adapting LRCLK. On the driver side this adaption is modeled as another "unpacking" codec. Behind that is your real codec and in your soundcard driver you simply configure the "unpacking codec"->"real codec" link to 8 channels with 1/4th of the rate (standard codec-codec link setup).

One limitation of any such approach is that you could loose channel sync - if you only "look" at the stereo stream you can't tell which of the stereo sample pairs is channel 0/1 of your 8-channel stream. On the bcm2835-i2s hardware side there's nothing you can do about that, but you can try to work around this by controlling LRCLK and BCLK manually and starting/stopping these at just the right moment. This is what the octo card seems to be doing (in trigger), using a dedicated clock generator - the missing "unpacking / translation" codec in it's driver model.

Coming back to your driver:

It might be best to start from scratch with a very simple soundcard driver and drop all changes to the cs42xx8. Use eg rpi-dac as a base and start with a setup that's known to work (eg I2S mode) and slowly work up your way.

As a first step just setup the clock to a fixed rate and start it in .probe and call snd_soc_codec_set_sysclk and snd_soc_dai_set_bclk_ratio (or snd_soc_dai_set_tdm_slot) in .init. Later you can try changing the clock rate in hwparams and start/stop it eg in trigger. Do all of this in your soundcard driver and only use the official function calls (like snd_soc_codec_set_sysclk) when you need to interact with another component. Whenever you might feel the need to change one of the existing (i2s or codec) drivers or modify their datastructures directly you know you are on the wrong way.

so long,

Hias

czyskows
Posts: 28
Joined: Sun Oct 07, 2012 5:42 am

Re: I2S clocks, GPCLK0

Fri Oct 06, 2017 7:32 pm

Thanks for this, Hias. This is a big help. My initial thinking was that it made sense to use the Octo driver as a template since that was so close to what I wanted to do, but I can see now that what you're suggesting makes more sense. I'll get started on reworking the driver now. I'm sure I'll have more questions as I go.

Cheers and gratitude!

czyskows
Posts: 28
Joined: Sun Oct 07, 2012 5:42 am

Re: I2S clocks, GPCLK0

Sun Oct 08, 2017 10:37 pm

Hi. I took your suggestion and started over with the rpi-dac driver as a model. I now have stereo audio sounding great! I'm still confused as to the multichannel parts, though. I'm setting the TDM slots as you said, and I'm setting the DSP_A dai_fmt. with those settings, I'm getting perfect audio, but only 2 channels. I tried using Matt's "anyChannelCount" alsa plugin, but no effect.

Code: Select all

pcm.!default {
#       type hw
#       card 0
        type plug
        slave.pcm "anyChannelCount"
}

ctl.!default {
        type hw
        card 0
}

pcm.anyChannelCount {
    type route
    slave.pcm "hw:0"
    slave.channels 8;
    ttable {
           0.0 1
           1.1 1
           2.2 1
           3.3 1
           4.4 1
           5.5 1
           6.6 1
           7.7 1
    }
}

ctl.anyChannelCount {
    type hw;
    card 0;
}
When I use this in my asound.conf, I get ":(snd1_pcm_hw_refine_slave) Slave PCM not usable
aplay: set_params:1204: Broken configuration for this PCM: no configurations available" As far as I can tell, this is the type of routing that will need to be done in the end, but I'm obviously still missing a middle step or two. Can you explain the "unpacking codec"->"real codec" link that you mentioned, or point to an example that accomplishes this? I looked at the cygnus-ssp driver, as it seems to do something similar, but it didn't make all that much sense to me. I've attached my new soundcard driver code if you care to have a look. Thank you as always!
Rasp-beret.zip
(2.13 KiB) Downloaded 12 times

HiassofT
Posts: 21
Joined: Fri Jun 30, 2017 10:07 pm

Re: I2S clocks, GPCLK0

Mon Oct 09, 2017 1:21 pm

No idea what this allChannelCount plugin is doing but the "slave.channels 8;" setting looks like it might be trying to configure the pcm hw with 8 channels - which won't work of course, it should open it with 2 channels.

so long,

Hias

czyskows
Posts: 28
Joined: Sun Oct 07, 2012 5:42 am

Re: I2S clocks, GPCLK0

Mon Oct 09, 2017 9:14 pm

I'm sorry, but I'm still trying to clarify exactly what you're describing. My understanding is that I need an alsa plugin that will do automatic sample rate conversion to 4Fs over 2 channels. Is this correct?

The only sample rate conversion plugins I've been able to find are static, e.g. ones that will take any sample rate and convert it to, say 44.1K for example, when what I need is something that takes any sample rate and multiplies it by 4. Something like this would work if I were always at 44.1K, no?

Code: Select all

pcm_slave.sl3 {
        pcm "hw:0,0"
        channels 2
        rate 176400
}

ctl.!default {
        type hw
        card 0
}

pcm.tdm {
        type plug
        slave sl3
}
But when I tried this (and various iterations), I kept getting :"Slave PCM not usable" / "Broken configuration for this PCM: no configurations available."

Can you let me know if I'm on the right track and/or where I might be going wrong?

Thank you yet again!

HiassofT
Posts: 21
Joined: Fri Jun 30, 2017 10:07 pm

Re: I2S clocks, GPCLK0

Tue Oct 10, 2017 9:47 am

The existing alsa plugins won't help you much, you will have to write one on your own. Plus you'll also need to build the clock generation hardware and write a codec driver for it. So all this isn't going to be an easy task.

The basic idea of the plugin is that it exposes an 8-channel endpoint but configures the backend (slave) to 2 channels, running at 4-times the requested samplerate. You don't actually change the sample data (what the existing alsa plugins do on down-mixing or sample rate conversion) but pass it through unaltered. In standard terms you can see this as implementing a time-division multiplexer in software.

So, in the plugin implementation you have to intercept the hw_params / hw_refine calls and rewrite sample rate (multiply by 4) and channels (divide by 4). Furthermore you'll also have to do this for all calls and parameters that are specified in frames (and 8-channel frame consists of 8 samples, but a 2-channel frame only of 2 - so eg if you get a request to transfer 1 8-channel frame you need to adapt that to transfer 4 2-channel frames to the backend/slave).

so long,

Hias

HiassofT
Posts: 21
Joined: Fri Jun 30, 2017 10:07 pm

Re: I2S clocks, GPCLK0

Tue Oct 10, 2017 12:15 pm

Forgot to add:

if you don't want to tackle the alsa plugin yet but rather start with the hardware side you can do the conversion manually, eg using sox.

For example: if you have an 8-channel wav file using S32_LE format with 48kHz sample rate like this:

Code: Select all

mediainfo 8-channel.wav
General
Complete name                            : 8-channel.wav
Format                                   : Wave
File size                                : 1.00 MiB
Duration                                 : 682 ms
Overall bit rate mode                    : Constant
Overall bit rate                         : 12.3 Mb/s

Audio
Format                                   : PCM
Format settings, Endianness              : Little
Format settings, Sign                    : Signed
Codec ID                                 : 00001000-0000-0100-8000-00AA00389B71
Duration                                 : 682 ms
Bit rate mode                            : Constant
Bit rate                                 : 12.3 Mb/s
Channel(s)                               : 8 channels
Channel positions                        : Front: L C R, Side: L R, Back: L R, LFE
Sampling rate                            : 48.0 kHz
Bit depth                                : 32 bits
Stream size                              : 1.00 MiB (100%)
Then first extract the raw sample data with sox:

Code: Select all

sox 8-channel.wav 8-channel.raw
Now convert the raw data to a 2 channel wav with 4 times the rate. Note that the sample format must match the one of your original 8 channel file. For signed, 32 bit data in little endian (S32_LE)) do this:

Code: Select all

sox --bits 32 --encoding signed-integer --endian little --channels 2 --rate 192000 8-channel.raw 2-channel.wav
so long,

Hias

czyskows
Posts: 28
Joined: Sun Oct 07, 2012 5:42 am

Re: I2S clocks, GPCLK0

Mon Oct 16, 2017 2:41 am

Hi,

I tried implementing the 8-channel conversion with sox, but I'm still getting output out of only 2 channels. My process was:

Code: Select all

sox MultOut.wav MultOut.raw
sox --bits 32 --endian little --channels 2 --rate 176400 MultOut.raw MultOut.wav
The codec setup that I have is still the same as I had before:

Code: Select all

static int raspberry_beret_init(struct snd_soc_pcm_runtime *rtd)
{
	return snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x03, 0x0c, 8, 32);
}
....
.dai_fmt	= SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF |
				SND_SOC_DAIFMT_CBS_CFS,
Is there something else I'm missing in the codec setup that allows the multi-channel functionality? I'm still confused as to how increasing the sample rate by a multiple of 4 is going to separate the file into 8 channels from a 2 channel file. The output of sox, with " play -r 44100 -c 8 MultOut.wav" gives me the first 2 channels as I would expect to hear them, but the rest of the audio is missing. Is there a channels output setting that I need to make on the codec side of things?

Also, the last posts mention "you'll also need to build the clock generation hardware and write a codec driver for it." Just clarify - the clock/codec hardware is built. The clocks are derived from GPCLK0, running to GPIO4 pin, which runs to the MCLK.
Image
The codec driver is what I posted previously. Short of writing an Alsa plugin to handle the sample rate conversion, is there a critical step I'm not comprehending?

Thank you, as always, for dealing with this mess.

HiassofT
Posts: 21
Joined: Fri Jun 30, 2017 10:07 pm

Re: I2S clocks, GPCLK0

Mon Oct 16, 2017 11:10 am

The thing that's missing is the hardware and software (codec driver) to generate proper LRCLK signals for the CPU and DAC. The Alsa plugin (or manual conversion with sox) is only one of the part that's needed.

I'll start from scratch and try a different approach to explaining what we have to do.

Data and LRCK (in DSP A mode) signals of an 8-channel frame need to look like this:

Code: Select all

LRCLK  _-_______________________________________________________________-_
DATA   -||-CH 0-||-CH 1-||-CH 2-||-CH 3-||-CH 4-||-CH 6-||-CH 6-||-CH 7-||

Each of the 8 channel slots is 32-bits wide, so a full frame consists of 256 bits.

bcm2835 can't generate these signals natively, if you configure it to 8 32-bit slots it can only transmit data on 2 of these slots.

What we can do (via the alsa plugin / sox) is chop up each 8-channel frame into 4 2-channel frames. The signals of such a chopped up 8-channel frame then looks like this (again using DSP A format for LRCLK, but now 2 slots of 32 bits per frame):

Code: Select all

LRCLK _-_______________-_______________-_______________-_______________-_
DATA  -||-CH 0-||-CH 1-||-CH 2-||-CH 3-||-CH 4-||-CH 6-||-CH 6-||-CH 7-||
"Chopping up" means transmitting 4 stereo frames instead of a single 1-channel frame. The number of bits per slot is identical, but as we need to transfer 4 times the number of frames the samplerate (frequency of LRCLK) is effectively quadrupled compared to the original 8-channel-frame rate. OTOH we only transfer 1/4th of the channels per frame (2 instead of 8) so BCLK frequency is the same as in the 8-channel-frame case.

If we leave LRCLK aside for a moment and only look at BCLK and DATA we'll see that they are identical for both setups.

What's missing now is generating proper LRCLK signals. The end result should look like this:

Code: Select all

LRCLK CPU  _-_______________-_______________-_______________-_______________-_
LRCLK DAC  _-_______________________________________________________________-_
DATA       -||-CH 0-||-CH 1-||-CH 2-||-CH 3-||-CH 4-||-CH 6-||-CH 6-||-CH 7-||
To do this we need additional hardware. This is also necessary because all clocks have to be started and stopped at precise moments. If we start the clocks before bcm2835-i2s is ready it won't transmit actual audio data and our data stream will be out of sync - channels will be shifted (eg you could get CH0 data on CH4, CH1 data on CH5 etc).

In ASoC we model this as a codec with 2 audio interfaces. One interface is connected to bcm2835 and configured to 2 TDM slots of 32 bits each. The other interface is connected to the DAC and configured to 8 TDM slots of 32 bits each.

The codec is clock master on both interfaces and generates the different LRCLK signals and the (identical) BCLK signal.

Logically the data stream passes through this codec. The codec receives the chopped-up signal from bcm2835-i2s, merges the chopped frames into real 8-channel frames and sends them on to the DAC.

Physically the data stream won't be altered by the codec, as we already took care that it's in the right format and running at the right bclk frequency.

What we have to do in the machine driver is to adapt for the different sample rates. The chopped-up signal from the bcm2835 is running at 4 times the rate, so we have to configure the DAC-side audio interface of the codec to 1/4th the sample rate of the bcm2835 rate.

so long,

Hias

czyskows
Posts: 28
Joined: Sun Oct 07, 2012 5:42 am

Re: I2S clocks, GPCLK0

Mon Oct 16, 2017 6:33 pm

Hi Hias,

First, I can't thank you enough for your patience and your willingness to help with this. Most of this is pretty far over my head and I'd be even more lost if it weren't for yours and Phil's direction.

Second, What I'm understanding now is that, with the current hardware setup, i.e. getting the clock from the RPi and not dividing it with an FPGA, there is no way to make this work. This is because the bcm2835 only allows for 2 channels of audio output at a time and I need to have different clock outputs for the RPi I2S and the codec TDM. Is this correct? In short, if I want this soundcard to work, I have to add an FPGA on the hardware side of things to divide the clock signals appropriately. Or would a clock divider, such as https://www.digikey.com/product-detail/ ... ND/1915353 work?

Again, thanks!

HiassofT
Posts: 21
Joined: Fri Jun 30, 2017 10:07 pm

Re: I2S clocks, GPCLK0

Wed Oct 18, 2017 8:21 am

Yes, your summary is correct.

Using a CPLD or a small FPGA (like for example a Lattice MachXO2) for clock generation would be the best approach. This'll allow you to generate all 4 clocks (MCLK, BCLK, LRCLK for BCM and LRCLK for DAC) in a single device and make sure all clocks are synchronized.

Discrete clock distribution / clock dividers aren't the best solution. They are usually designed to output 50% duty-cycle clock signals (but DSP mode usually wants a LRCLK pulse of 1 BCLK cycle width) and if you cascade several of them to divide down all clocks each will introduce a slight delay - so the clock edges are no longer synchronous. All of this may or may not be a problem, but it surely isn't ideal.

I'd also drop the idea of using GPCLK0 as a main clock source, DACs usually need a clean master clock (which GPCLK0 isn't due to the use of fractional dividers) otherwise performance may be severely degraded.

Using 2 dedicated crystal oscillators for the 44.1kHz and 48kHz rate families as clock sources is the better approach. Choose frequencies so that you can derive MCLK via a pure integer divider from these - 45.158 and 49.152MHz oscillators would be typical choices.

so long,

Hias

czyskows
Posts: 28
Joined: Sun Oct 07, 2012 5:42 am

Re: I2S clocks, GPCLK0

Wed Oct 18, 2017 6:25 pm

Hi Hias,

Thanks again. While this is frustrating, since I already designed and built another board :( I guess that's the route I have to take. I'll start making the changes to my board layout and figuring out the FPGA programming now. I'm sure I'll be back with more questions once the hardware side is complete.

Cheers!

Return to “Device Tree”

Who is online

Users browsing this forum: No registered users and 2 guests