[alsa-devel] [RFC PATCH 2/2] ASoC: codec: wm8960: Relax bit clock computation
Daniel Baluta
daniel.baluta at gmail.com
Thu Mar 16 10:34:30 CET 2017
On Wed, Mar 15, 2017 at 7:17 PM, Charles Keepax
<ckeepax at opensource.wolfsonmicro.com> wrote:
> On Wed, Mar 15, 2017 at 05:33:06PM +0200, Daniel Baluta wrote:
>> WM8960 derives bit clock from sysclock using BCLKDIV[3:0] of R8
>> clocking register (See WM8960 datasheet, page 71).
>>
>> There are use cases, like this:
>> aplay -Dhw:0,0 -r 48000 -c 1 -f S20_3LE -t raw audio48k20b_3LE1c.pcm
>>
>> where no BCLKDIV applied to sysclock can give us the exact requested
>> bitclk, so driver fails to configure clocking and aplay fails to run.
>>
>> Fix this by relaxing bitclk computation, so that when no exact value
>> can be derived from sysclk pick the closest value greater than
>> expected bitclk.
>>
>> Suggested-by: Charles Keepax <ckeepax at opensource.wolfsonmicro.com>
>> Signed-off-by: Daniel Baluta <daniel.baluta at nxp.com>
>> ---
>> sound/soc/codecs/wm8960.c | 39 +++++++++++++++++++++++++++++++++------
>> 1 file changed, 33 insertions(+), 6 deletions(-)
>>
>> diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
>> index cb2ff2d..1669b45 100644
>> --- a/sound/soc/codecs/wm8960.c
>> +++ b/sound/soc/codecs/wm8960.c
>> @@ -611,6 +611,10 @@ static const int bclk_divs[] = {
>> * - lrclk = sysclk / dac_divs
>> * - 10 * bclk = sysclk / bclk_divs
>> *
>> + * If we cannot find an exact match for (sysclk, lrclk, bclk)
>> + * triplet, we relax the bclk such that bclk is chosen as the
>> + * closest available frequency greater than expected bclk.
>> + *
>> * @wm8960_priv: wm8960 codec private data
>> * @mclk: MCLK used to derive sysclk
>> * @_i: sysclk_divs index for found sysclk
>> @@ -620,14 +624,14 @@ static const int bclk_divs[] = {
>> * Returns:
>> * -1, in case no sysclk frequency available found
>> * 0, in case an exact match is found. See @_i, @_j, @_k
>> - *
>> + * >0, in case a relaxed match is found. See @_i, @_j, @_k
>> */
>> int wm8960_configure_sysclk(struct wm8960_priv *wm8960, int mclk,
>> int *_i, int *_j, int *_k)
>> {
>> int sysclk, bclk, lrclk;
>> int i, j, k;
>> - int diff;
>> + int diff, closest = mclk;
>
> Don't you need to initialise diff here too?
It shouldn't be necessary. Diff is always initialized before used.
I added diff variable to avoid always writing: sysclk - bclk *
bclk_divs[k] / 10;
>
>>
>> bclk = wm8960->bclk;
>> lrclk = wm8960->lrclk;
>> @@ -648,6 +652,12 @@ int wm8960_configure_sysclk(struct wm8960_priv *wm8960, int mclk,
>> *_k = k;
>> break;
>> }
>> + if (diff > 0 && closest > diff) {
>> + *_i = i;
>> + *_j = j;
>> + *_k = k;
>> + closest = diff;
>> + }
>> }
>> if (k != ARRAY_SIZE(bclk_divs))
>> break;
>> @@ -656,10 +666,16 @@ int wm8960_configure_sysclk(struct wm8960_priv *wm8960, int mclk,
>> break;
>> }
>>
>> + /* exact match */
>> if (i != ARRAY_SIZE(sysclk_divs))
>> return 0;
>>
>> - return -1;
>> + /* no match */
>> + if (closest == mclk)
>> + return -1;
>> +
>> + /* relaxed match */
>> + return 1;
>
> Do we need to differenciate between relaxed and an actual match?
In my implementation yes. Because if an actual match happens we go directly
to "configure_clock" label. If a relaxed match happens we keep this in mind
for later and for now we try to see if a PLL out freq is available.
Finally, if no PLL out freq is available we go and use the "relaxed" match.
>> }
>>
>> static int wm8960_configure_clocking(struct snd_soc_codec *codec)
>> @@ -668,6 +684,7 @@ static int wm8960_configure_clocking(struct snd_soc_codec *codec)
>> int sysclk, bclk, lrclk, freq_out, freq_in;
>> u16 iface1 = snd_soc_read(codec, WM8960_IFACE1);
>> int i, j, k;
>> + int best_sysclk_div, best_dac_div, best_bclk_div = -1;
>> int ret;
>>
>> if (!(iface1 & (1<<6))) {
>> @@ -705,10 +722,14 @@ static int wm8960_configure_clocking(struct snd_soc_codec *codec)
>> ret = wm8960_configure_sysclk(wm8960, freq_out, &i, &j, &k);
>> if (ret == 0)
>> goto configure_clock;
>> - else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) {
>> + else if (ret < 0 && wm8960->clk_id != WM8960_SYSCLK_AUTO) {
>> dev_err(codec->dev, "failed to configure clock\n");
>> return -EINVAL;
>> }
>> + /* there is still hope, keep this if no PLL out available */
>> + best_sysclk_div = i;
>> + best_dac_div = j;
>> + best_bclk_div = k;
>
> Enabling the PLL just to avoid running the BCLK a little fast
> doesn't really seem worth it and it would make the code much more
> obvious.
That's a nice suggestion. With this we could remove the pll part. Not
sure if there
are any negative side effects.
I will do some tests.
thanks,
Daniel.
More information about the Alsa-devel
mailing list