0

Problem: Send 16 channel audio TDM stream over SAI(5) datalines using freescale IMX8m. There's no codec termination on the HW. only the data is framed in a TDM fromat and sent over the I2S Datalines. There's an FPGA that receives the data on the other end of Imx8m datalines and de-multiplexes it to multiple audio out sources.

Issue: When playing 8 (4 stereo sources) channels, the TDM frame seems to be working without any issues. But when i increase the TDM frame size to 16 channels (8 stereo sources), once in a while the playback is stuck before even beginning the playback. it remains stuck in the poll() function and never returns. this poll function is called at the top level from snd_pcm_write_areas() in /alsa-lib/src/pcm/pcm.c

Here's what I have found during my debug between aplay app/alsa-lib <=> alsa-pcm-snd-driver <==> fsl_sai driver > HW

  1. when the playback is functioning properly i can see the snd_pcm_direct.spcm->hw.ptr getting incremented by frame size*1 (or more) for every snd_pcm_write_areas call. this pointer seems to keep track of the #of frames in time that should have been sent out. its an unsigned int value with a boundary of 0x40000000 and the ptr loops back again once it reaches the boundary. From alsa-lib/src/pcm/pcm.c file
 static int snd_pcm_dshare_start_timer(snd_pcm_direct_t *dshare)
 {
  int err;
  snd_pcm_hwsync(dshare->spcm);
    dshare->slave_appl_ptr= dshare->slave_hw_ptr = *dshare->spcm->hw.ptr;
  1. When the playback is stuck at poll function i see that this pointer never got updated and is stuck at period size (frame size * n) or at some multiple of frame size.
  2. I have seen several alsa posts online regarding the playback being stuck but no solution or direction to take in case of.
  3. Also read one post, mentioning this to be an existing alsa bug as of 4.14 kernel, when using Dshare/Dmix/Dsnoop plugins.
  4. I have tried moving from 1.1.2 to 1.1.5 or 1.1.7 version but no luck. Apparently this issue doesn't happen when using the HW cards, only when using the above mentioned plugins this issue is seen.

I am not sure which one leads to the other and which one is the bug that needs to be fixed.

The poll function getting stuck due to timer getting into bad state or the timer getting into bad state leading to the poll getting stuck.

Some details of how we creating the TDM frame we have a 16 channel audio (8 independent stereo pairs) that need to be sent as a frame to the HW to be written out on one of the SAI datalines (4 data lines). These 8 sources can be played independently and the sources will occupy the slots in the TDM frame according to the channel map as specified in the asound.conf configuration file.

The dshare plugin is being used to create the shared buffer that will hold the frame. I have created 8 virtual sources in the conf file, one for each playback process. each source can be used in a separate aplay process for playing a 8 different source. see the batch file that plays all the 8 sources.

output from aplay -L/l

null
    Discard all samples (playback) or generate zero samples (capture)
src1
    TDM 0 channel 0/1 for audio playback
src2
    TDM 1 channel 2/3 for audio playback
src3
    TDM 2 channel 4/5 for audio playback
src4
    TDM 3 channel 6/7 for audio playback
src5
    TDM 4 channel 8/9 for audio playback
src6
    TDM 5channel 10/11 for audio playback
src7
    TDM 6 channel 12/13 for audio playback
src8
    TDM 7 channel 14/15 for audio playback
default:CARD=<xxxxx>
    Default Audio Device
sysdefault:CARD=<xxxxxxxx>
    Default Audio Device

asound.conf file

# NOTE: this is for sharing multiple channels on a single (TDM) audio device with multple ALSA clients

# shared buffer for playback
pcm_slave.tdmshare {
    pcm "hw:0"
    channels 16
    rate 48000          # fixed, because all dshare devices must use the same samplerate.
    format S24_LE
    period_size 512
    buffer_size 1024
}

# src1 shared pcm device and corresponding virtual playback device
pcm.src1_dshare {
    type dshare
    ipc_key 43544553
    slave tdmshare
    bindings.0 0
    bindings.1 4
}
pcm.src1 {
    type plug
    slave.pcm "src1_dshare"
 hint {
                show {
                        @func refer
                        name defaults.namehint.basic
                }
                description "TDM 0 channel 0/1 for audio playback"
        }
}

# src2 shared pcm device and corresponding virtual playback device
pcm.src2_dshare {
    type dshare
    ipc_key 43544553
    slave tdmshare
    bindings.0 8
    bindings.1 12
}
pcm.src2 {
    type plug
    slave.pcm "src2_dshare"
 hint {
                show {
                        @func refer
                        name defaults.namehint.basic
                }
                description "TDM 1 channel 2/3 for audio playback"
        }
}

# src3 shared pcm device and corresponding virtual playback device
pcm.src3_dshare {
    type dshare
    ipc_key 43544553
    slave tdmshare
    bindings.0 1
    bindings.1 5
}
pcm.src3 {
    type plug
    slave.pcm "src3_dshare"
 hint {
                show {
                        @func refer
                        name defaults.namehint.basic
                }
                description "TDM 2 channel 4/5 for audio playback"
        }
}

# src4 shared pcm device and corresponding virtual playback device
pcm.src4_dshare {
    type dshare
    ipc_key 43544553
    slave tdmshare
    bindings.0 9
    bindings.1 13
}
pcm.src4 {
    type plug
    slave.pcm "src4_dshare"
 hint {
                show {
                        @func refer
                        name defaults.namehint.basic
                }
                description "TDM 3 channel 6/7 for audio playback"
        }
}

# src5 shared pcm device and corresponding virtual playback device
pcm.src5_dshare {
    type dshare
    ipc_key 43544553
    slave tdmshare
    bindings.0 2
    bindings.1 6
}
pcm.src5 {
    type plug
    slave.pcm "src5_dshare"
 hint {
                show {
                        @func refer
                        name defaults.namehint.basic
                }
                description "TDM 4 channel 8/9 for audio playback"
        }
}

# src6 shared pcm device and corresponding virtual playback device
pcm.src6_dshare {
    type dshare
    ipc_key 43544553
    slave tdmshare
    bindings.0 10
    bindings.1 14
}
pcm.src6 {
    type plug
    slave.pcm "src6_dshare"
 hint {
                show {
                        @func refer
                        name defaults.namehint.basic
                }
                description "TDM 5channel 10/11 for audio playback"
        }
}

# src7 shared pcm device and corresponding virtual playback device
pcm.src7_dshare {
    type dshare
    ipc_key 43544553
    slave tdmshare
    bindings.0 3
    bindings.1 7
}
pcm.src7 {
    type plug
    slave.pcm "src7_dshare"
 hint {
                show {
                        @func refer
                        name defaults.namehint.basic
                }
                description "TDM 6 channel 12/13 for audio playback"
        }
}

# src8 shared pcm device and corresponding virtual playback device
pcm.src8_dshare {
    type dshare
    ipc_key 43544553
    slave tdmshare
    bindings.0 11
    bindings.1 15
}
pcm.src8 {
    type plug
    slave.pcm "src8_dshare"
 hint {
                show {
                        @func refer
                        name defaults.namehint.basic
                }
                description "TDM 7 channel 14/15 for audio playback"
        }
}

batch file for playing all 8 sources

aplay -Dplug:src1 /data/PCMCH1-Ch2_1.wav&
aplay -Dplug:src2 /data/PCMCH3-Ch4_1.wav&
aplay -Dplug:src3 /data/PCMCH5-Ch6_1.wav&
aplay -Dplug:src4 /data/PCMCH7-Ch8_1.wav&
aplay -Dplug:src5 /data/PCMCH1-Ch2_1.wav&
aplay -Dplug:src6 /data/PCMCH3-Ch4_1.wav&
aplay -Dplug:src7 /data/PCMCH5-Ch6_1.wav&
aplay -Dplug:src8 /data/PCMCH7-Ch8_1.wav&

Please let me know if there's something that can be modified in asound.conf file or if there's any limitation that i am hitting when playing 16 vs 8 channels in the alsa-lib that's causing this issue.

The other links that i have seen regarding this issue are below. they are very old links and some say its fixed but i am not sure how it was fixed or what those patches were. our system is using alsa version 1.1.6 with kernel 4.14 and is present in this build. please point me to any patchset or link that shows how to resolve this issue. i have hit a dead end at this time regarding this issue and also due to my limited knowledge in this area.

[https://bugzilla.redhat.com/show_bug.cgi?id=534130][1]

[https://www.raspberrypi.org/forums/viewtopic.php?t=64936][2]

[https://sourceforge.net/p/alsa/mailman/message/26464680/][3]

2
  • i tried using "share" plugin and that seems to avoid the poll function issue, but I am not able to play multiple clients even though the plugin documentation says multiple clients are allowed. I simply replaced the "dshare" with "share" and it works except for multiple clients, issue. i did remove the "ipckey" and "period size" and "buffer_size" from teh conf file as the "share" plugin doesn't use those. Commented Feb 13, 2020 at 7:37
  • Apparently, shared memory does not work correctly on your architecture.
    – CL.
    Commented Feb 13, 2020 at 7:52

1 Answer 1

0

after some debugging into kernel sound core pcm drivers and the corresponding sdma controller that the pcm core dma engine uses, I found that the DMA transfer between the DMA controller (/drivers/dma/imx-sdma.c)and the SAI FIFO's (/sound/soc/fsl/fsl_sai.c) was getting stalled when the number of channels was 16 and 32bit width per channel. The DMA buffer size being specified in the asound.conf file is also making a difference.

The Sdma controller's interrupt handler is the one that increments the HW.ptr metioned in my question above. The SDMA interrupt handler triggers the timer that was created by the alsa user space and the user space waits on this timer in the poll function (this is where it was getting stuck) to be cleared so that it knows how many buffers to write to the dma memory mapped buffer.

Since the dma interrupt handler never got triggered, the HW.ptr never got updated and the timer interrupt handler never got triggered either which resulted in the user space waiting in the poll function and never return.

this may not be a final solution, but depending on individual situations the solution could either be a fix within the dma controller firmware/script, or the way peripheral FIFO's connected to the dma controller operate. either way it needs an understanding of the underlying HW IP implementation on a given processor.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .