On 11/6/18 10:37 AM, Dimitris Papavasiliou wrote:
Hi,
A (commercial) DAC board, I use with a Raspberry Pi is plagued by clicks and pops and I'm trying to fix it at the driver level. I have mostly fixed it, but I could use some guidance on the problem described below. I apologize in advance for the long read, but the situation is a bit convoluted (or my understanding of it is).
The board (a HifiBerry DAC+ Pro) uses a PCM5122 chip which is driven by the pcm512x codec driver, but the Pro version also includes a set of external clocks on the board (one each for 48Khz and 44.1Khz sample rates and multiples thereof).
There are two kinds of clicks and pops: on the one hand the board clicks when the device is opened/closed, mostly because the pcm512x codec driver doesn't support the digital_mute operation. These are relatively mild clicks, which are an annoyance at most and I have a separate patch for the pcm512x, that addresses them, which I'm currently testing and will (hopefully) submit soon.
A separate case is a pop that's generated when the clock source is changed. This pop is independent of the digital volume setting and very loud. As far as I could determine, when the hw_params callback specified in the snd_soc_dai_link struct is called, it figures out what clock is needed for the giver sample rate and powers the appropriate clock through a GPIO on the PCM5122. This essentially means that the external clock source fed into the 5122 is stopped and restarted at a different rate, potentially while the DAC chip is operating. Later the hw_params callback defined in the pcm512x driver is called and it reconfigure the clock dividers for the new rate. It is at this point that the pop seems to be generated, provided that the card is not powered down or muted. If I comment out either the clock power-up/down in the dai_link hw_params, or the clock divider reconfiguration in the codec's hw_params, no pop is generated.
Now, the digital_mute patch described before, also happens to fix this loud pop. I'm not sure whether that's a happy coincidence that might cease to be the case in the future though and was considering fixing the pop problem separately, at its source: the clock change. One way to fix the problem is to switch the 5122 into standby before changing the clock source in the dai_link hw_params callback and switch it back into normal operation afterwards. This seems to work regardless of whether digital_mute is implemented or not, but has the following problem: The register that's used to put the chip into/out of standby, is also used by the pcm512x driver, in the set_bias_level operation. Since the hw_params operation is, as far as I can tell, not atomic, I'm concerned that race conditions may arise, causing the card to be left powered up, when it should be off and vice-versa.
Is there some way to synchronize the use of common chip registers in the DAI and codec drivers? Is the digital_mute somehow guaranteed to fix the pop originating at the DAI/machine driver level (whatever the hw_params operation in the dai_link struct is supposed to be)? Am I perhaps missing something entirely?
Any help or guidance is welcome.
Thanks for starting this thread. You are not the only one fighting to make those Hifiberry DAC+ PRO boards work without convoluted hacks. Our team uses them for validation of the Sound Open Firmware on the UP^2 boards and we are not quite happy with the clock definitions and don't really have a line of sight to upstream our changes.
The current implementation we have is ripped from the Raspberry kernel:
The PCM512x codec uses the clock framework to request an "sclk", and if it's not available will fall back to slave mode (SoC master). The codec driver uses this clock handle to request the current rates with clk_get_rate() and program the various dividers.
In the Raspberry kernel there is a pretend clock which does nothing but store a value on clk_set_rate() and provides the stored value with clock_get_rate().
The machine driver hw_params calls clk_set_rate(), but the silly part is that it then directly plays with the codec GPIO registers to change the clock sources without any sort of stabilization time/mute.
My view is that it's a broken model, and your point about the clock transitions not taking into account the codec state reinforces my diagnosis. Ideally we'd need a clk API implementation which is closely tied with codec driver implementation for the cases where the codec GPIOs are used to select clock sources.
For now our code uses a hack which bypasses the clock API completely. I shared it on this list [1] but was told the clock API was the way to go. I don't have a clear vision though on how to deal with a case where the codec driver is both client and provider of a clock.
I am told that Suse supports the Raspberry Pi now, maybe Takashi will chime in :-)?
-Pierre