While working on a custom board featuring an AT91SAM9261 processor our system has met a strange problem of audio channel swapping. A search on the Internet confirmed that it is a known problem met also by others as you can read on this mailing list and this mailing list. Since changing the SoC on the board was not an option, another solution needed to be found.
The CPU is connected to two, WM8804 and WM8731, audio codecs over I2S (for audio) and I2C (for control) interfaces. The WM8731 is operated in master mode, thus the BCLK and LRC are provided by it to the WM8804 codec and the CPU. Audio is operated at 32kHz, 44.1kHz and 48kHz sampling frequencies in 8bps or 16bps modes. The software uses the ALSA OSS emulation layer on a 3.3.1 Linux kernel powered system. The audio bus was configured to work in I2S mode.
Audio recorded on any of the audio inputs sometimes swaps right and left
channels. The channel swap seemed to be random, but more bit likely to be
correct (around 65%/35% correct/incorrect ratio). The swap happened on every
/dev/dsp device open/reconfiguration event.
To localize the problem the following cases had been examined:
- Board/chip problem: There is a faulty chip on the board or electronic noise on the I2S bus.
- Software problem: The custom software handles sound somehow incorrectly or other memory handling bug.
- Timing error: The data reception starts on the falling edge of the LRC clocking, instead of the rising edge. This could happen, since LRC is provided by the codec, which is ready sooner then the CPU and the CPU will trigger on any edge of LRC. This would cause all later data shifted in the buffers, thus swapping the channels.
- DMA error: DMA is not ready in time, thus loosing one channel sample. This would also cause all later data shifted in the buffers, thus swapping the channels (as hinted on the mailing lists).
- ALSA: Some other sound system related problem, after the physical layer and the DMA, but before the user land application.
Most possible problem sources were eliminated through measurements with an oscilloscope, hardware design check, trying other boards of the same type and simple sound loop application.
The problem hinted by the mailing lists was confirmed, where the DMA controller handles incoming data improperly thus causing occasional channel swap.
For the LRC trigger problem first I tried the suggestions provided on one of the mailing lists. Waiting with a sleep or with GPIO in a busy loop is not elegant and also does not fix the problem. After debugging the code and trying out a few other solution candidates, I decided in the end to switch the system to DSP Mode transfer instead of I2S.
This way the sound data is transferred on a LRC pulse concatenated thus no ambiguity can be caused by which of the trigger events was sensed first by the CPU.
However the kernel did not have this configuration mode implemented, so it had to be done first. The resulting patch can be found in the LKML archive or in my Github repository It was tested with kernels versions 3.3.1 and 3.7.9, but it should be straightforward to apply it to other kernel releases. Update: this patch was merged into mainline kernel and is no longer needed as of version 3.10!
This has improved the situation quite a bit, since both channel data were transferred after edge detection on the LRC line. The channels were on the appropriate outputs most of the time, but still sometimes the swap happened. Although the transfer happened in a concatenated way on the bus, actually the above patch actually tells the CPU to have two 16 bit transfers after LRC pulse. It seems that the DMA is slow to initialize in time and although it triggers, sometimes the first 16bit transfer is lost. After all the tests I would say it is a silicon bug in the DMA or related FIFO peripheral on these Atmel devices.
The final solution for this system was to configure the CPU to transfer the two 16 bit channel data in a single 32 bit transfer after the LRC pulse. This way in the DMA/I2S bus race condition partial transfers will be thrown away. This incremental patch will change the code accordingly (apply the previous patch first). I’d like to it note here, that in normal circumstances this patch should not be required, but AT91SAM9261 will not work correctly otherwise.
The above solution not only eliminates the channel swap problem, but is also a clean solution on these devices.
WM8731 DSP Mode A confusion
Some additional problem was caused by the analogue codec chip. The reason was that the WM8731 has different BCLK clocking in DSP Mode than the CPU or WM8804.
DSP Mode A for the CPU and WM8804 is when data transfer starts n=1 BCLK after LRC pulse, while it is actually DSP Mode B for the WM8731 codec. For the WM8731 DSP Mode A is when transfer starts n=0 BLCK after LRC pulse. This is important when initializing the audio drivers in the kernel board configuration code. Improper settings will cause a one bit shift in the sound data, which will result in corrupted audio on the system (very noisy and distorted). In any case it is wise to check the timing diagrams twice!