Codec drivers and alsa support for custom cape with sta321mp

Dear all,

I have created a custom cape around the STA321MP chip from ST Microelectronics. It has 6 MEMS microphones and outputs them on an I2S bus with 3 data lines. I am trying to use mcasp0 on the boneblack side to receive the data. I am now to the point where the codec configuration happens correctly (I can see the i2s signals with a logic analyzer) and a soundcard is detected. However, when I try to record sound with arecord, I get the following error:

root@beaglebone:~# arecord -l
**** List of CAPTURE Hardware Devices ****
card 0: EVM [STA321MP EVM], device 0: STA321MP sta321mp-audio-0
Subdevices: 1/1
Subdevice #0: subdevice #0
root@beaglebone:~# arecord -D hw:0,0 -f S24_LE -c 6 -r 44100 -t wav test.wav
Recording WAVE ‘test.wav’ : Signed 24 bit Little Endian, Rate 44100 Hz, Channels 6
arecord: pcm_read:1801: read error: Input/output error

Let me know provide more details.

  • The codec is frame and bit clocks master

  • The connection to mcasp0 is the following:

  • Bit clock <=> P9_12 (mcasp0_aclkr_mux3)

  • Frame sync clock (L/R) <=> P9_27 (mcasp0_fsr)

  • Data <=> P9_28 (mcasp0_axr2)

  • Data <=> P9_30 (mcasp0_axr0)

  • Data <=> P9_25 (mcasp0_axr3)- I am using kernel 3.14.41 (because 3.8 doesn’t have support for mcasp with >2 channels)

  • I am using dtb-builder to add my device to the device tree

All the code I have added to the kernel (codec and machine code) and to the device tree is on github. Here are the links to the specific changes I have made.

kernel: Added support for sta321mp to beaglebone black. · fakufaku/linux@3a66809 · GitHub

device tree: Added device tree for beaglebone black with sta321mp cape. · fakufaku/dtb-rebuilder@127dbf3 · GitHub

It is my first time trying to code audio driver so please be indulgent :slight_smile: I have spent a lot of time on this but it seems I am now completely stuck. I’d really appreciate some help. I’d be happy to provide more clarifications on anything.

One thing I do not understand is if I need to add mixer bindings. And also the audio routing (jack vs codec pins, etc).

Thank you in advance!
Robin

Hey Robin,
I first must say I don't have an answer for you. The reason I am writing is I have in mind to self-publish a free online open-source and organic BBB hacking book, working title "hackin' black" (think AC/DC!). Your topic is broadly interesting, really cool, and relevant as heck. So if you are willing, I would like to include the solution in "hackin' black" when you get it sorted?

Think about it, and think of how helpful a free book like that would have been to you right now!

Respectfully,

JF

BTW,

Your link is broken, here is the page I found -
http://www.st.com/web/en/catalog/sense_power/SC1756
Which chip is it, I see no 321mp?

Jack

Hi Jack,

Thanks for the interest. Sure, I’d be happy to share the solution. I’m planning to also release the schematics and PCB when things are working out.

The data sheet for the STA321MP is here: http://www.st.com/st-web-ui/static/active/en/resource/technical/document/datasheet/DM00046002.pdf
Thanks for catching that.

Cheers,
Robin

After a lot of effort, I have finally solved this.

The problem was that the mcasp module can operate with synchronous or asynchronous transmit and receive clocks. The default setting hardcoded in the linux mcasp driver is synchronous. That means that both receive and transmit use the transmit clock signals FSX and ACLKX.

My board was sending the frame sync and bit clock only to the receive clock. I could solve the problem by rewiring the circuit.

Robin, do you think it would be similarly easy to get a TI tas5731m chip to work? http://www.ti.com/lit/ds/symlink/tas5731m.pdf

Hi Rick,

I think it should not be too difficult. I have mainly followed this
tutorial to get the driver going:

http://processors.wiki.ti.com/index.php/Sitara_Linux_Audio_DAC_Example

In my case things were a bit tricky because I needed to enable 6
channels in mcasp0 and the current mainstream BBB kernel (~3.8.67) had
only support for 2 channels. I had to move to 3.14 but then lost
support for the device tree overlay. In your case since you only want
two channels, it should be no problem. Just follow the guide :slight_smile:

Cheers,
Robin

Hi Robin:

I’m doing a similar project, but with a PCM3168 codec… I also followed the guide http://processors.wiki.ti.com/index.php/Sitara_Linux_Audio_DAC_Example
Anyway there are lots of things that are not very clear for me…

In my case, i use codec as clock slave, and synch, so the problem you had should not affect to me… But when i try to record it says:

arecord -D hw:0,0 -f S24_LE -c 1 -r 96000 -t wav test.wav
Recording WAVE ‘test.wav’ : Signed 24 bit Little Endian, Rate 96000 Hz, Mono
arecord: set_params:1239: Channels count non available

Could you help me?

Hi Pablo,

With McASP it is not possible to capture less than 2 channels. Try with -c 2 argument.

Best,
Robin

Thanks very much, that was a problem. But now another error appears: “davinci_evm sound: ASoC: machine hw_params failed: -524”. Do you know what it means, or where can i find the meaning? I assume this means there is something wrong when i created the pcm3168_hw_params in the alsa machine layer… but i don’t know the problem… where did you look for info? only in that TI guide? I attach my files, and if is not much trouble for you i would be grateful if you help me…

i’ve noticed that some functions you used in davinci_evm.c where not explained in that guide… and it’s very difficult for a noob like me in this subject for understainding what to do… I’ve just based my pcm3168.c in another driver, and i really don’t quite understand much of what is going on, nor found any simple documentation about it. If you could give me some hints it would be great!

Thanks very much…

pcm3168.c (9.57 KB)

davinci-evm.c (14.9 KB)

sorry, the message also states:

arecord: set_params:1297: Unable to install hw params:
ACCESS: RW_INTERLEAVED
FORMAT: S24_LE
SUBFORMAT: STD
SAMPLE_BITS: 32
FRAME_BITS: 128
CHANNELS: 4
RATE: 96000
PERIOD_TIME: (21333 21334)
PERIOD_SIZE: 2048
PERIOD_BYTES: 32768
PERIODS: 4
BUFFER_TIME: (85333 85334)
BUFFER_SIZE: 8192
BUFFER_BYTES: 131072
TICK_TIME: 0

aplay -f S24_LE -r 96000 …/Yam*wav

Warning: format is changed to S16_LE
Playing WAVE ‘…/Yamaha-TG100-Ocarina-C5.wav’ : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
[ 102.291787] davinci_evm sound: ASoC: machine hw_params failed: -524
aplay: set_params:1297: Unable to install hw params:
ACCESS: RW_INTERLEAVED
FORMAT: S16_LE
SUBFORMAT: STD
SAMPLE_BITS: 16
FRAME_BITS: 32
CHANNELS: 2
RATE: 44100
PERIOD_TIME: (125011 125012)
PERIOD_SIZE: 5513
PERIOD_BYTES: 22052
PERIODS: 4
BUFFER_TIME: (500045 500046)
BUFFER_SIZE: 22052
BUFFER_BYTES: 88208
TICK_TIME: 0

Hi Pablo,

What is the kernel version you are using ? I have finally used kernel 3.14 because in 3.8 it seems that the mcasp driver implementation could not support more than two channels.

I was in the same position as you. I mostly dug as much as possible from TI documentation and also read the drivers for other chipsets in the kernel code. Then, I also used
the sitara am355xx data sheet to understand how McASP works. It took me quite some time to piece together how things work. Still now I do not fully understand. The problem is that there is no proper documentation for the ALSA SoC wrt embedded platforms such as the BBB.

Note that the TI example code might not be for exactly the same kernel version you are using. There might be discrepancies.

I suggest you run a search on the source code to find where the error “ASoC: machine hw_params failed” is generated. You can add printk statements in your code so that you can later check in the kernel log that things are happening as you expect.

Cheers,
Robin

Thanks Robin,

Im using latest stable kernel, think is 4.2, i can check when i get home… i will add printk statements, as you suggested…

Another question. I inted to use one mcasp serializer for 4 channels… in the am335x datasheet it seems possible, so i configured 4 tdm slots in the device tree instead of 2, but if i try to record with -c 6 it says again the “channels count non available”. Do you know if its possible? its quite frustrating to work without propper documentation, but in need this project to get my title…

And last thing, where you able to get alsamixer working?

Thanks very much

Hi Pablo,

I don’t know for 4.2. Try to read the sound/alsa/soc/davinci/davinci-mcasp.c file to understand which functionalities are implemented. I used the kernel from Robert C Nelson’s repo on github.

After a quick search on google, I saw you are not the first person to attempt to connect this codec to the BBB.
http://www.spinics.net/lists/alsa-devel/msg41291.html
Maybe you can try to contact this Wendelin Klimann.

BTW, this PCM3168 seems like a very neat chip. What exactly are you building ?

Cheers,
Robin

Maybe also this can help:
http://mailman.alsa-project.org/pipermail/alsa-devel/2013-February/059433.html

R

Hi Robin:

I intend to implement a portable multitrack recorder, that’s why i choose this codec, it has many inputs and outputs, good sample rate and bit resolution, and all in a single chip.

I can’t find wendelin’s mail direction, so i think i’ll just write to that posts, but they are a bit old i think. I have already looked at them, but i was more confused by that time, perhaps now i can understand a bit more.

If i find out anything i will continue posting here, so if someone is interested in this project can look at ti…

Please if you find any documentation tell me!

Thanks

Hi Robin:

with printk statements, i’ve isolated the error to this instruction:

ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, SND_SOC_CLOCK_OUT);

but i don’t know what is wrong… the sysclk variable is:

unsigned int sysclk = ((struct snd_soc_card_drvdata_davinci *)
snd_soc_card_get_drvdata(soc_card))->sysclk;

Just linke in the evm_hw_params

I’ve noticed that you used:

ret = snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_IN);

If i’m getting it right, this is to set the cpu_dai sysclock, but i dont get why you used 0 in the frequency parameter. Also you never used the function to set the codec_dai sysclk… Is it not necessary? I’ll try commenting this line

Hi Pablo,

Hi Robin:

with printk statements, i’ve isolated the error to this instruction:

ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, SND_SOC_CLOCK_OUT);

but i don’t know what is wrong… the sysclk variable is:

unsigned int sysclk = ((struct snd_soc_card_drvdata_davinci *)
snd_soc_card_get_drvdata(soc_card))->sysclk;

Just linke in the evm_hw_params

What value of sysclk are you providing ?

I’ve noticed that you used:

ret = snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_IN);

If i’m getting it right, this is to set the cpu_dai sysclock, but i dont get why you used 0 in the frequency parameter. Also you never used the function to set the codec_dai sysclk… Is it not necessary? I’ll try commenting this line

In my case, MCLK is provided to the chipset by an external crystal. Thus there is no sysclk for me. BCLK and LRCLK are also provided by the chipset to McASP.

Best,
Robin

Hi: im using 24Mhz, that is provided by de beaglebone… i used printk to see that sysclk value and it is correct… i get the value as is stated in the evm_hw_params function.
I dont see why the function returns error… but is there a list of error codes to see what -524 means?
Thanks very much

Hi Pablo,

I took a look at your code.

static int pcm3168_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_card *soc_card = rtd->card;
struct platform_device *pdev = to_platform_device(soc_card->dev);
int ret = 0;
unsigned sysclk = ((struct snd_soc_card_drvdata_davinci *)
snd_soc_card_get_drvdata(soc_card))->sysclk;

/* set the codec system clock */
ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, SND_SOC_CLOCK_OUT);
if (ret < 0)
return ret;

/* set the CPU system clock */
ret = snd_soc_dai_set_sysclk(cpu_dai, 0, sysclk, SND_SOC_CLOCK_OUT);
if (ret < 0)
return ret;

return 0;
}

I see that you are calling snd_soc_dai_set_sysclk for both codec_dai and cpu_dai. I think that is the error. In your case codec_dai is the PCM3168 chip and cpu_dai is the processor McASP module. Sysclk only makes sense for McASP as far as I understand. Try commenting out

/* set the codec system clock */
ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, SND_SOC_CLOCK_OUT);
if (ret < 0)
return ret;

I would also suggest you read up about the McASP clock generation structure in the sitara documentation (~7000 pages document available from TI). This helped me understand how the McASP module works.

Another resource I used is the linux cross-reference http://lxr.free-electrons.com/
I used it to look up where functions are defined and what they do.

Cheers,
Robin