How to make I2S0(on LF connector) a PCM slave?

I want to feed external clock and sync signal to Hikey960 I2S0(brought out on LF connector) from my external BT controller. I am setting up my controller as PCM master. Please let me know what needs to be changed in device tree and kernel for this.

Thanking you,

Regards,
Arnab Dey

It’s really not in shape to do that at this point. The driver hisi-i2s.c doesn’t have any code for setting it in slave mode. Your best bet is to report this issue on the big tracker.

I wouldn’t expect too rapid of a response to your issue, unfortunately, so in the mean time, you might want to see if it might be an easier solution to put your external device into slave mode instead of the soc.

1 Like

Dear Mr. doitright,

Thank you very much for your input on this. I will check this. In the meantime, as you told, if I keep I2S0 as master, I need to change clock frequency of I2S0 in almost similar way you did for I2S2(HFP Client SCO audio routing?).
Can you please guide me how to do the same in case of I2S0?

Thanking you,

Regards,
Arnab Dey

In, sound/soc/hisilicon/hisi-i2s-plat.c, I changed snd_soc_dapm_widget and snd_soc_dapm_route accordingly for DMA <-> DAI routes and in sound/soc/hisilicon/hisi-i2s.c, I am configuring HI_ASP_CFG_R_CLK3_DIV_REG instead of HI_ASP_CFG_R_CLK4_DIV_REG(meant for I2S2) as you did in case of I2S2.

I just found that registering process is OK but somehow it selects 48kHz always. Need to find out why.

EDITED: If I select HI_ASP_CFG_R_CLK3_DIV_REG to be 1M forcibly(i.e. whatever the param rate is, select 1M always => PCM_CLK = 1MHz, SYNC=16kHz) I get proper BT WBS audio out of I2S0.

Mr. doitright, can you please help me in debugging what can be possible reasons for which the kernel always opts for 48kHz? It will be really helpful as I have to get NBS(8kHz) working also.

Thanking you,

Regards,
Arnab Dey

If you’re able to “force” it, then sounds like you almost have it. 3 MHz → 48 kHz, 1 MHz → 16 kHz, 512 kHz → 8 kHz.

Just make the clock divider change in the proper location in response to the parameter received from the alsa client.

And don’t forget to only apply it to the correct i2s device. You will need something like this: sound/soc/hisilicon/hisi-i2s.c · android-hikey-linaro-4.9_apr2018_CAR96 · HiKey960-Car / android_kernel_linaro_hikey_OLD · GitLab

Dear Mr. doitright,

I followed your link only(you shared with me earlier in another thread). This is exactly what I have in my code. However, I have completely disabled TI controller(removed “ti,wl1837-st” from device tree so that it will not be probed) and also disabled UART4. Instead I am using my own driver under UART3. I have only one HCI which is my BT controller=hci0.
Whenever, I am getting a call over BT, I can see the switch case under ‘hisi_i2s_hw_params’ in hisi-i2s.c, always hitting 48000 condition.
Please let me know if you see any problem in my implementation.

Thanking you,

Regards,
Arnab Dey

What is sending the 48000 then? Android HAL? What HAL are you using? Did you adapt it to your hardware?

Dear Mr. doitright,

I think that might be the problem. I am using default HAL and have not explicitly modified anything. But one doubt, when I was using TI controller that time also did not change anything explicitly in HAL but that worked fine. Can you please guide here?

Thanking you,

Regards,
Arnab Dey

Default HAL will be trying to play sounds via HDMI, which would definitely cause the 48000.

I have a HAL here that is “ready to go” for HFP:

BUT, the sound card detection routines and setups are all specific to my hardware. You’ll have to adjust things to match yours. In particular, see functions “find_cards_devices”, and “runsco”.

HOWEVER, you probably would be best off doing some simple tests using tinyplay and tinycap first, setting the “-r” parameter appropriately, and seeing what is passed into the alsa driver.

1 Like

Dear Mr. doitright,

Thank you very much for sharing the link. I am trying to interface with my USB audio adapter. I have this with me(https://store.google.com/us/product/usb_c_headphone_adapter). Currently, by default, this device gets detected and I am getting all sounds EXCEPT BT HFP audio out of this. Will update my status soon.

Thanking you,

Regards,
Arnab Dey

Do those things actually have a microphone contact in them? I was under the impression that they’re only output (3 contact).

Make sure that its picking up the correct devices in these 4 locations;

— otherwise it will bail at one of the “return NULL;” lines.

Also make sure that the headphone adapter supports 48 kHz stereo, both for input AND output (otherwise it will bail at a return NULL), and that your bluetooth sound driver supports stereo for both playback and capture.

Note: Yes, phone calls are MONO, but there are (were?) issues getting the i2s2 working in mono, and many USB devices simply do not support mono, which is why the alsa devices are all configured for stereo. Receiving data from the BT, we simply discard all channel 2 data (which is null anyway), then duplicate channel 1 over to channel 2 to play on speakers in stereo, and similarly in the opposite direction.

1 Like

Dear Mr. doitright,

Thank you very much for your help. Your input is really valuable and helped me a lot.
‘ls -l /sys/class/sound’ produces following on my device -

card0 → …/…/devices/platform/soc/soc:sound/sound/card0
card1 → …/…/devices/platform/soc/ff200000.hisi_usb/ff100000.dwc3/xhci-hcd.0.auto/usb1/1-1/1-1:1.0/sound/card1
controlC0 → …/…/devices/platform/soc/soc:sound/sound/card0/controlC0
controlC1 → …/…/devices/platform/soc/ff200000.hisi_usb/ff100000.dwc3/xhci-hcd.0.auto/usb1/1-1/1-1:1.0/sound/card1/controlC1
pcmC0D0p → …/…/devices/platform/soc/soc:sound/sound/card0/pcmC0D0p
pcmC0D1c → …/…/devices/platform/soc/soc:sound/sound/card0/pcmC0D1c
pcmC0D1p → …/…/devices/platform/soc/soc:sound/sound/card0/pcmC0D1p
pcmC1D0c → …/…/devices/platform/soc/ff200000.hisi_usb/ff100000.dwc3/xhci-hcd.0.auto/usb1/1-1/1-1:1.0/sound/card1/pcmC1D0c
pcmC1D0p → …/…/devices/platform/soc/ff200000.hisi_usb/ff100000.dwc3/xhci-hcd.0.auto/usb1/1-1/1-1:1.0/sound/card1/pcmC1D0p
timer → …/…/devices/virtual/sound/timer

and ls -l ‘/dev/snd’ produces following -

controlC0
controlC1
pcmC0D0p
pcmC0D1c
pcmC0D1p
pcmC1D0c
pcmC1D0p
timer

So, in my case,
USB = C1D0
BT = C0D1

I have modified ‘find_cards_devices()’ accordingly and logcat shows detection is proper -

modules.car96audio_hal.hikey: runsco: USBCARD: 1, BTCARD: 0

However, because of usb_config mismatch, ‘runsco()’ did not go through.
For USB adapter, tinypcminfo produces following -

Info for card 1, device 0:

PCM out:
Access: 0x000009
Format[0]: 00000000
Format[1]: 0x000001
Format Name: S24_3LE
Subformat: 0x000001
Rate: min=48000Hz max=48000Hz
Channels: min=2 max=2
Sample bits: min=24 max=24
Period size: min=48 max=87381
Period count: min=2 max=1024

PCM in:
Access: 0x000009
Format[0]: 00000000
Format[1]: 0x000001
Format Name: S24_3LE
Subformat: 0x000001
Rate: min=48000Hz max=48000Hz
Channels: min=2 max=2
Sample bits: min=24 max=24
Period size: min=48 max=87381
Period count: min=2 max=1024

tinypcminfo for BT PCM produces following -

Info for card 0, device 1:

PCM out:
Access: 0x000009
Format[0]: 0x000014
Format[1]: 00000000
Format Name: S16_LE, U16_LE
Subformat: 0x000001
Rate: min=8000Hz max=16000Hz
Channels: min=2 max=2
Sample bits: min=16 max=16
Period size: min=256 max=1024
Period count: min=4 max=64

PCM in:
Access: 0x000009
Format[0]: 0x000014
Format[1]: 00000000
Format Name: S16_LE, U16_LE
Subformat: 0x000001
Rate: min=8000Hz max=16000Hz
Channels: min=2 max=2
Sample bits: min=16 max=16
Period size: min=256 max=1024
Period count: min=4 max=64

simply, changing ‘usb_config.format’ in ‘runsco()’ will work?
Trying to figure out what to do…However, tinycap on BT card works fine, I could get audio.

Please help.

Thanking you,

Regards,
Arnab Dey

S24_3LE? Wow. Ok, that complicates things somewhat, because it looks like that USB device ONLY supports 24 bit audio samples. Dealing with this will be quite a bit more involved than just changing the format, because you will also have to deal with data that is in a different format, and actually convert between 24 and 16 bit data.

So before THIS line, you will need to convert the data from 16 to 24 bit;

And after THIS line, you will need to convert the data from 24 to 16 bit;

My recommendation would be that you get yourself a more flexible audio card. While I was still working with USB audio (I’m now working with a mezzanine board I’ve designed and will shortly be offering for sale, which includes an i2s “sound card” and an automotive amplifier), I was using a “Vantec NBA-200U”, which is MUCH better suited for the application than what you’re trying to use.

1 Like

Dear Mr. doitright,

Thank you very much for your help. I have started understanding the audio driver stuffs more and more now.
I will try bit shifting first(16<->24). Actually I chose that USB-C audio adapter because that comes with Google Pixel phones(https://store.google.com/product/pixel_2_specs) and reportedly used for CTS Audio tests(USB Audio CTS Verifier Tests  |  Android Open Source Project).

Will try as you have pointed and update.

Just wow. Would be of great use!!!

Thanking you,

Regards,
Arnab Dey

CTS isn’t relevant, because we aren’t using the USB sound card in the same way as it would be used by a USB audio HAL.

Dear Mr. doitright,

I will search for other USB audio adapter(the one you used) while in the meantime I am trying to use whatever I have.
Now, I am getting some noise out of the headphone, but encountered buffer overrun problem. My USB throws buffer overrun error sometimes and then stops working. I think it is because of slow pcm_read.

What I have changed :

in case of 48kHz@stereo@S24_3LE -

sample size = 3 bytes
frame size = 3x2 = 6 bytes.
in our case, period size=1024, period count = 4, therefore, USB audio buffer size should be = 1024x4 = 4096 frames = 4096x6 bytes
Based on this, I have changed here (replaced 4 by 6)

changed here (replaced 4 by 6) (It would be great if you explain why (4096 - f_write_avail) as what I understood is f_write_avail is available frames for appllications to write to pcm buffer, then why subtracting from total buffer size?)-

and changed here(replaced 4 by 6) -

It would be great if you help me here as I am very new to this ALSA sound driver implementation.

Thanking you,

Regards,
Arnab Dey

(4096 - f_write_avail)…
This is the commit I implemented that in;

You know, I wrote that code 6 months ago, and I don’t exactly remember what was going through my mind at the time. Its very possible that that is a bug in my code, or, it might be working around a bug in tinyalsa, or I’m too tired right now to understand it.

The changes you have detailed are NOT adequate to make this work. Somewhere before writing to your USB device, you need to add bytes to expand your samples from 2 to 3 bytes each, and also after you read from your USB device, you need to remove bytes to reduce your samples from 3 to 2 bytes.

Also uncomment this to see what is actually going on with the buffers;

And something else you may want to try out; instead of copying the data between the two sound devices (bluetooth and usb), start by implementing a loopback on the USB. That should be a simple matter of commenting out these 4 lines; 1551, 1552, 1569, 1570. Just don’t forget to adjust your lengths on the read and write. Once you can hear the microphone echoed to the speakers, then it becomes a manipulation of the sample length.

1 Like

I’ve got a weird feeling that f_write_avail actually ended up being the how much data was in the buffer rather than how much free space was in the buffer. But again, its been 6 months since I’ve looked at that.

Dear Mr. doitright,

I have following doubt regarding S16_LE to S24_3LE conversion -

If,
s16_le[0] = 0x1234;
s16_le[1] = 0x5678;
s16_le[2] = 0xdead;
s16_le[3] = 0xbeef;

then,
s24_3le[0] = 0x12340056;
s24_3le[1] = 0x7800dead;
s24_3le[2] = 0x00beef00;

Should this be the conversion(lsb padding) so that after this I can pass pointer to s24_3le to pcm_write()?
or, we can have another possiblity(msb padding) -

s24_3le[0] = 0x00123400;
s24_3le[1] = 0x567800de;
s24_3le[2] = 0xad00beef;

I am not getting buffer problem now. But none of the above conversion is producing proper sound output. Please help.

Thanking you,

Regards,
Arnab Dey

You would only pad the MSB if you were using s24_le, which fits 24 bit samples into 32 bit fields.

To clarify things in your mind, it would be better to step through by sample, rather than trying to translate the 16 bit values into 32 bit blocks. Like this;

s16_le[0] = 0x123400;
s16_le[1] = 0x567800;
s16_le[2] = 0xdeadff;
s16_le[3] = 0xbeefff;

Be careful with the value you are padding onto it. You’re dealing with signed integers. If the value you are converting is NEGATIVE, you will want to pad it with 0xff. If the value you are converting is POSITIVE, you will want to pad it with 0x00.

Now the data arrays (buffers) you are dealing with, are going to be 1D arrays, of length samples*bytes_per_sample.

You have the s16 with length 2xlength, and you have the s24 with length 3xlength, so you will convert using something like this;

for (i=0; i<length; i++){
    s24[3*i] = s16[2*i];
    s24[(3*i)+1] = s16[(2*i)+1];
    s24[(3*i)+2] = s16[2*i] & 0x80 ? 0xff : 0x00;
}

And then you write out 3xlength bytes from s24.