1

I am trying to combine multiple streams (8 streams) of audio (PCM -48Kz, 16‑bit, 2ch), in iMX8. The test pattern in user space is similar to running 8 gst_launch/aplay instances simultaneously playing a wav file containing RAW PCM.
The problem I have at hand is that there are no audio HW devices (Cards) on the board connected to the iMX8 where the audio can be terminated. Instead, the audio (PCM) needs to be routed to the SAI interface and samples from 8 streams muxed into TDM frame and sent out via SAI interface on iMX8. The SAI lines are connected to an Audio FPGA that demuxes the TDM output and sends it to eight different ports on a different board that's connected to the main CPU board. I need to deliver the Kernel driver solution for iMX8 processor. We are using the iMX8M Mini app processor and Linux 4.14.98 on Yocto.
I am completely new to ALSA; my entire background is in video/display/PCI subsystems in kernel.

Digging into ALSA, my understanding is that the whole ALSA kernel/HW architecture is built around audio HW termination with the following architecture as given in Porting custom audio codec in Linux BSP by Gopinath Srinivasan:
Alsa kernel driver ⟺ [CODEC ⟺ Machine Driver ⟺ platform driver] ⟺ processorHW (audio).

I did try reading as much as I could and here's what I understand so far:

Since there are no HW devices.

  1. Can you enable snd_dummy or snd_aloop (for enabling dummy/aloop audio drivers) to pass audio to the kernel? I tested enabling these and ran speaker_test and aplay and these apps seem to work without any crashes.
  2. Read the samples in kernel and combine them and write to the SAI FIFO (TDR/TFR) and set the corresponding BCLK/MCLK.  Perhaps asynchronous mode of SAI can be used for this.  I am not sure how this path can be enabled in the current Alsa driver model.

I have several questions regarding the above method.  Also, I don't know if this is the right way of doing things with audio, so please correct me if I am wrong. Another issue is that I have no way of testing this other than probing WCLK/MCLK/BCLK/TX0 lines coming from the SAI interface. That's a whole different problem. I'll worry about that once I get there.

The following are my questions:

  1. Is this the right approach to begin with?
  2. I read about plugins (Dmix).  How does the DMIX plugin work? Can this plugin be used in this case to configure all 8 ports and samples written to TDM fifo somehow, via some configuration written in conf files (asoundrc, alsa.conf, etc.)? I have no idea how this works, just throwing some questions. Don't know if this makes any sense or not.
  3. Since there are no HW devices, how do I configure SAI in the DTS? What codec needs to be enabled when using snd_aloop/snd_dummy (assuming that's the way to pass audio to kernel)? like this here. This is based on the freescale iMX8M Mini eval board.
    sound-ak4458 {                                          
           compatible = "fsl,imx-audio-ak4458";            
           model = "ak4458-audio";                         
           audio-cpu = <&sai1>;                      <-------      
           audio-codec = <&ak4458_1>, <&ak4458_2>;         <--------
           ak4458,pdn-gpio = <&pca6416 4 GPIO_ACTIVE_HIGH>;
    };                                                      
    
  4. How is the audio passed to the alsa driver in kernel? Are the samples passed as buffers through ioclts/sysfs or is it written to memory (DMA ?) that's accessed by audioHW/codec diretly through memory (DMA) or by some other means?
  5. Does ALSA support accessing the SAI interface (fsl_sai.c/h) directly from aloop.c/dummy.c drivers in case that's how the data needs to be sent?
  6. There were 8 subdevices created when I enabled the snd_dummy driver. Can the userspace apps use these 8 subdevices as 8 different hw ports (hw:0,0; hw:0,1; hw:0,2, etc.) or do you need 8 independent dev nodes (hw:0,0; hw:1,0; hw:2,0, etc.) for the apps to send the data to? If the latter is true, can we create separate dev nodes in dummy/aloop drivers?

Any help in pointing me in the right direction is greatly appreciated. Please do let me know if there are any questions or details you need added.

1
  • Have you tried looking in official kernel documentation - kernel.org/doc/html/latest/sound/index.html. can't comment on most of this as i do not understand a lot of your nomenclature. ALSA dmix plugin won't do Time Division Multiplexing and I do not think ALSA is correct for this task if no devices available
    – nik gnomic
    Aug 28, 2019 at 19:06

1 Answer 1

1

I was able to create a TDM frame using the dshare plugin and it works for 8 channels. But when I tried to bump it to 16 channels, I ran into the issue of playback getting stuck at poll function. See my other post here.

I tried to use the share "share" plugin which uses the aserver instead of using the /dev/snd/timer that the dshare/dmix plugin uses.

But when trying to do simultaneous playback of multiple clients, I get audio device busy error after playing one client. Is there anything else in the configuration file that I need to enable playing the multiple sources?

See the asound.conf:

# Crestron DNA_AUDIO x specific configurations
# 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
}

# src1 shared pcm device and corresponding virtual playback device
pcm.src1_share {
    type share
    slave tdmshare
    bindings.0 0
    bindings.1 4
}
pcm.src1 {
    type plug
    slave.pcm "src1_share"
 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_share {
    type share
    slave tdmshare
    bindings.0 8
    bindings.1 12
}
pcm.src2 {
    type plug
    slave.pcm "src2_share"
 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_share {
    type share
    slave tdmshare
    bindings.0 1
    bindings.1 5
}
pcm.src3 {
    type plug
    slave.pcm "src3_share"
 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_share {
    type share
    slave tdmshare
    bindings.0 9
    bindings.1 13
}
pcm.src4 {
    type plug
    slave.pcm "src4_share"
 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_share {
    type share
    slave tdmshare
    bindings.0 2
    bindings.1 6
}
pcm.src5 {
    type plug
    slave.pcm "src5_share"
 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_share {
    type share
    slave tdmshare
    bindings.0 10
    bindings.1 14
}
pcm.src6 {
    type plug
    slave.pcm "src6_share"
 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_share {
    type share
    slave tdmshare
    bindings.0 3
    bindings.1 7
}
pcm.src7 {
    type plug
    slave.pcm "src7_share"
 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_share {
    type share
    slave tdmshare
    bindings.0 11
    bindings.1 15
}
pcm.src8 {
    type plug
    slave.pcm "src8_share"
 hint {
                show {
                        @func refer
                        name defaults.namehint.basic
                }
                description "TDM 7 channel 14/15 for audio playback"
        }
}
1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.