[v2,2/2] ASoC: wm8960: Let wm8960 driver configure its bit clock and frame clock
diff mbox

Message ID 1420615905-4078-2-git-send-email-zidan.wang@freescale.com
State New, archived
Headers show

Commit Message

Zidan Wang Jan. 7, 2015, 7:31 a.m. UTC
From: Zidan Wang <b50113@freescale.com>

wm8960 codec driver missing configure its bit clock and frame clock, so add
support for it. It will calculate a appropriate frequency dividing ratio
according to the system clock, bit clock and frame clock, then set the
corresponding registers.

Signed-off-by: Zidan Wang <b50113@freescale.com>
---
 sound/soc/codecs/wm8960.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 97 insertions(+)

Comments

Mark Brown Jan. 14, 2015, 7:27 p.m. UTC | #1
On Wed, Jan 07, 2015 at 03:31:45PM +0800, Zidan Wang wrote:

> +	for (i = 0; i < ARRAY_SIZE(dac_divs); ++i) {
> +		if (wm8960->sysclk == lrclk * dac_divs[i]) {
> +			for (j = 0; j < ARRAY_SIZE(bclk_divs); ++j) {
> +				if (wm8960->sysclk ==  wm8960->bclk *
> +						bclk_divs[j] / 10) {
> +					goto config_clock;
> +				}
> +			}
> +		}
> +	}
> +
> +	dev_err(codec->dev, "Unsupported sysclk %d\n", wm8960->sysclk);
> +	return;

It's a bit awkward using the goto like this.  A more common way of
writing this is to change the above block to be

	if (i == ARRAY_SIZE(dac_divs))
		/* return error */

rather than skipping over the error.  Otherwise this looks good.
Zidan Wang Jan. 15, 2015, 1:34 p.m. UTC | #2
On Wed, Jan 14, 2015 at 07:27:03PM +0000, Mark Brown wrote:
> On Wed, Jan 07, 2015 at 03:31:45PM +0800, Zidan Wang wrote:
> 
> > +	for (i = 0; i < ARRAY_SIZE(dac_divs); ++i) {
> > +		if (wm8960->sysclk == lrclk * dac_divs[i]) {
> > +			for (j = 0; j < ARRAY_SIZE(bclk_divs); ++j) {
> > +				if (wm8960->sysclk ==  wm8960->bclk *
> > +						bclk_divs[j] / 10) {
> > +					goto config_clock;
> > +				}
> > +			}
> > +		}
> > +	}
> > +
> > +	dev_err(codec->dev, "Unsupported sysclk %d\n", wm8960->sysclk);
> > +	return;
> 
> It's a bit awkward using the goto like this.  A more common way of
> writing this is to change the above block to be
> 
> 	if (i == ARRAY_SIZE(dac_divs))
> 		/* return error */
> 
> rather than skipping over the error.  Otherwise this looks good.

Hi Mark,

I found it can't generate bclk for S20_3LE data format.

For 2 channel S20_3LE data format:

bclk = fs * 20 * 2
Sysclk = BCLKDIV * bclk = BCLKDIV * fs * 40
Sysclk = DACDIV * fs * 256

BCLKDIV/DACDIV = 256/40 = 32/5

But BCLKDIV/DACDIV can't be 32/5. So I want to support tdm slot.

bclk = fs * slot_width * slots * channal.

Do you think it make sense, or any other ideas?

Best Regards,
Zidan
Daniel Baluta April 3, 2017, 1:16 p.m. UTC | #3
On Thu, Jan 15, 2015 at 3:34 PM, Zidan Wang <b50113@freescale.com> wrote:
> On Wed, Jan 14, 2015 at 07:27:03PM +0000, Mark Brown wrote:
>> On Wed, Jan 07, 2015 at 03:31:45PM +0800, Zidan Wang wrote:
>>
>> > +   for (i = 0; i < ARRAY_SIZE(dac_divs); ++i) {
>> > +           if (wm8960->sysclk == lrclk * dac_divs[i]) {
>> > +                   for (j = 0; j < ARRAY_SIZE(bclk_divs); ++j) {
>> > +                           if (wm8960->sysclk ==  wm8960->bclk *
>> > +                                           bclk_divs[j] / 10) {
>> > +                                   goto config_clock;
>> > +                           }
>> > +                   }
>> > +           }
>> > +   }
>> > +
>> > +   dev_err(codec->dev, "Unsupported sysclk %d\n", wm8960->sysclk);
>> > +   return;
>>
>> It's a bit awkward using the goto like this.  A more common way of
>> writing this is to change the above block to be
>>
>>       if (i == ARRAY_SIZE(dac_divs))
>>               /* return error */
>>
>> rather than skipping over the error.  Otherwise this looks good.
>
> Hi Mark,
>
> I found it can't generate bclk for S20_3LE data format.
>
> For 2 channel S20_3LE data format:
>
> bclk = fs * 20 * 2
> Sysclk = BCLKDIV * bclk = BCLKDIV * fs * 40
> Sysclk = DACDIV * fs * 256
>
> BCLKDIV/DACDIV = 256/40 = 32/5
>
> But BCLKDIV/DACDIV can't be 32/5. So I want to support tdm slot.
>
> bclk = fs * slot_width * slots * channal.
>
> Do you think it make sense, or any other ideas?

Reviving this question after two years :).

After "ASoC: codec: wm8960: Relax bit clock computation" patch

https://patchwork.kernel.org/patch/9636769/

we can now support S20_3LE for round rates like 8000, 16000,
32000 and 48000.

But not for 11025, 22050, 441000. Do you think it's worth exploring
"tdm slot" idea? I don't know exactly what it implies.

Another idea, is to completely remove support for S20_3LE since it
is not trivial to derive bitclk from sysclk.

What do you guys think?

Daniel.
Charles Keepax April 3, 2017, 1:34 p.m. UTC | #4
On Mon, Apr 03, 2017 at 04:16:23PM +0300, Daniel Baluta wrote:
> On Thu, Jan 15, 2015 at 3:34 PM, Zidan Wang <b50113@freescale.com> wrote:
> > On Wed, Jan 14, 2015 at 07:27:03PM +0000, Mark Brown wrote:
> >> On Wed, Jan 07, 2015 at 03:31:45PM +0800, Zidan Wang wrote:
> > I found it can't generate bclk for S20_3LE data format.
> >
> > For 2 channel S20_3LE data format:
> >
> > bclk = fs * 20 * 2
> > Sysclk = BCLKDIV * bclk = BCLKDIV * fs * 40
> > Sysclk = DACDIV * fs * 256
> >
> > BCLKDIV/DACDIV = 256/40 = 32/5
> >
> > But BCLKDIV/DACDIV can't be 32/5. So I want to support tdm slot.
> >
> > bclk = fs * slot_width * slots * channal.
> >
> > Do you think it make sense, or any other ideas?
> 
> Reviving this question after two years :).
> 
> After "ASoC: codec: wm8960: Relax bit clock computation" patch
> 
> https://patchwork.kernel.org/patch/9636769/
> 
> we can now support S20_3LE for round rates like 8000, 16000,
> 32000 and 48000.
> 
> But not for 11025, 22050, 441000. Do you think it's worth exploring
> "tdm slot" idea? I don't know exactly what it implies.
> 
> Another idea, is to completely remove support for S20_3LE since it
> is not trivial to derive bitclk from sysclk.
> 
> What do you guys think?

Does this problem still remain after the relaxed clock
computation? The maths you quote depends on the derived BCLK
being exactly the correct speed for the audio, that is no longer
the case anymore.

I would have thought the patch would cover both situations, as in
if we can produce a suitable LRCLK, then we just pick a BCLK we
can produce that is higher than we need. I don't see why that
depends on things being a 48k based rate there. Am I missing
something?

Thanks,
Charles
Daniel Baluta April 3, 2017, 1:39 p.m. UTC | #5
On Mon, Apr 3, 2017 at 4:34 PM, Charles Keepax
<ckeepax@opensource.wolfsonmicro.com> wrote:
> On Mon, Apr 03, 2017 at 04:16:23PM +0300, Daniel Baluta wrote:
>> On Thu, Jan 15, 2015 at 3:34 PM, Zidan Wang <b50113@freescale.com> wrote:
>> > On Wed, Jan 14, 2015 at 07:27:03PM +0000, Mark Brown wrote:
>> >> On Wed, Jan 07, 2015 at 03:31:45PM +0800, Zidan Wang wrote:
>> > I found it can't generate bclk for S20_3LE data format.
>> >
>> > For 2 channel S20_3LE data format:
>> >
>> > bclk = fs * 20 * 2
>> > Sysclk = BCLKDIV * bclk = BCLKDIV * fs * 40
>> > Sysclk = DACDIV * fs * 256
>> >
>> > BCLKDIV/DACDIV = 256/40 = 32/5
>> >
>> > But BCLKDIV/DACDIV can't be 32/5. So I want to support tdm slot.
>> >
>> > bclk = fs * slot_width * slots * channal.
>> >
>> > Do you think it make sense, or any other ideas?
>>
>> Reviving this question after two years :).
>>
>> After "ASoC: codec: wm8960: Relax bit clock computation" patch
>>
>> https://patchwork.kernel.org/patch/9636769/
>>
>> we can now support S20_3LE for round rates like 8000, 16000,
>> 32000 and 48000.
>>
>> But not for 11025, 22050, 441000. Do you think it's worth exploring
>> "tdm slot" idea? I don't know exactly what it implies.
>>
>> Another idea, is to completely remove support for S20_3LE since it
>> is not trivial to derive bitclk from sysclk.
>>
>> What do you guys think?
>
> Does this problem still remain after the relaxed clock
> computation? The maths you quote depends on the derived BCLK
> being exactly the correct speed for the audio, that is no longer
> the case anymore.
>
> I would have thought the patch would cover both situations, as in
> if we can produce a suitable LRCLK, then we just pick a BCLK we

That!

The problem for remaining rates is that we cannot derive the LRCLK

<snip>
+ for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
+ if (sysclk != dac_divs[j] * lrclk)
+ continue;
</snip>


> can produce that is higher than we need. I don't see why that
> depends on things being a 48k based rate there. Am I missing
> something?
Charles Keepax April 3, 2017, 1:54 p.m. UTC | #6
On Mon, Apr 03, 2017 at 04:39:40PM +0300, Daniel Baluta wrote:
> On Mon, Apr 3, 2017 at 4:34 PM, Charles Keepax
> <ckeepax@opensource.wolfsonmicro.com> wrote:
> > On Mon, Apr 03, 2017 at 04:16:23PM +0300, Daniel Baluta wrote:
> > Does this problem still remain after the relaxed clock
> > computation? The maths you quote depends on the derived BCLK
> > being exactly the correct speed for the audio, that is no longer
> > the case anymore.
> >
> > I would have thought the patch would cover both situations, as in
> > if we can produce a suitable LRCLK, then we just pick a BCLK we
> 
> That!
> 
> The problem for remaining rates is that we cannot derive the LRCLK
> 
> <snip>
> + for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
> + if (sysclk != dac_divs[j] * lrclk)
> + continue;
> </snip>
> 

If you can't generate the LRCLK you either need a different
source clock or to use the PLL. You don't want to be trying to
pull 44.1k audio over a link that is clocked on a 48k based
clock.

Is the problem here that the PLL part of the code is making the
same assumption as the direct part of the code was, that the bclk
should be exact?

Thanks,
Charles
Daniel Baluta April 4, 2017, 7:55 a.m. UTC | #7
<Removing Zidan from thread because the address no longer exists>

On Mon, Apr 3, 2017 at 4:54 PM, Charles Keepax
<ckeepax@opensource.wolfsonmicro.com> wrote:
> On Mon, Apr 03, 2017 at 04:39:40PM +0300, Daniel Baluta wrote:
>> On Mon, Apr 3, 2017 at 4:34 PM, Charles Keepax
>> <ckeepax@opensource.wolfsonmicro.com> wrote:
>> > On Mon, Apr 03, 2017 at 04:16:23PM +0300, Daniel Baluta wrote:
>> > Does this problem still remain after the relaxed clock
>> > computation? The maths you quote depends on the derived BCLK
>> > being exactly the correct speed for the audio, that is no longer
>> > the case anymore.
>> >
>> > I would have thought the patch would cover both situations, as in
>> > if we can produce a suitable LRCLK, then we just pick a BCLK we
>>
>> That!
>>
>> The problem for remaining rates is that we cannot derive the LRCLK
>>
>> <snip>
>> + for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
>> + if (sysclk != dac_divs[j] * lrclk)
>> + continue;
>> </snip>
>>
>
> If you can't generate the LRCLK you either need a different
> source clock or to use the PLL. You don't want to be trying to
> pull 44.1k audio over a link that is clocked on a 48k based
> clock.

Yup, this makes sense to me.

>
> Is the problem here that the PLL part of the code is making the
> same assumption as the direct part of the code was, that the bclk
> should be exact?

Yes.


After wm8960_configure_sysclk fails to find a LRCLK, we try to use the
PLL.

Anyhow, here we don't even reach to check if the PLL can be used because
there is no solution for the following system:

freq_out = sysclk * sysclk_divs[i];
sysclk = lrclk * dac_divs[j];
sysclk == bclk * bclk_divs[k]


Perhaps, we can also try here to relax bitclk computation like we did for when
sysclk was directly derived from mclk.

thanks,
Daniel.
Charles Keepax April 4, 2017, 8:55 a.m. UTC | #8
On Tue, Apr 04, 2017 at 10:55:00AM +0300, Daniel Baluta wrote:
> <Removing Zidan from thread because the address no longer exists>
> 
> On Mon, Apr 3, 2017 at 4:54 PM, Charles Keepax
> <ckeepax@opensource.wolfsonmicro.com> wrote:
> > On Mon, Apr 03, 2017 at 04:39:40PM +0300, Daniel Baluta wrote:
> >> On Mon, Apr 3, 2017 at 4:34 PM, Charles Keepax
> >> <ckeepax@opensource.wolfsonmicro.com> wrote:
> > Is the problem here that the PLL part of the code is making the
> > same assumption as the direct part of the code was, that the bclk
> > should be exact?
> 
> Yes.
> 
> 
> After wm8960_configure_sysclk fails to find a LRCLK, we try to use the
> PLL.
> 
> Anyhow, here we don't even reach to check if the PLL can be used because
> there is no solution for the following system:
> 
> freq_out = sysclk * sysclk_divs[i];
> sysclk = lrclk * dac_divs[j];
> sysclk == bclk * bclk_divs[k]
> 
> 
> Perhaps, we can also try here to relax bitclk computation like we did for when
> sysclk was directly derived from mclk.

Exactly that is what I am saying it looks like the PLL part
of the process still assumes it requires bclk to be an exact
frequency if we relax that, the same way we did for the direct
MCLK then we should be good.

Thanks,
Charles

Patch
diff mbox

diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index fd25973..aba6bbf 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -127,6 +127,8 @@  struct wm8960_priv {
 	struct snd_soc_dapm_widget *out3;
 	bool deemph;
 	int playback_fs;
+	int bclk;
+	int sysclk;
 	struct wm8960_data pdata;
 };
 
@@ -563,6 +565,68 @@  static struct {
 	{  8000, 5 },
 };
 
+/* Multiply 256 for internal 256 div */
+static const int dac_divs[] = { 256, 384, 512, 768, 1024, 1408, 1536 };
+
+/* Multiply 10 to eliminate decimials */
+static const int bclk_divs[] = {
+	10, 15, 20, 30, 40, 55, 60, 80, 110,
+	120, 160, 220, 240, 320, 320, 320
+};
+
+static void wm8960_configure_clocking(struct snd_soc_codec *codec,
+		int stream, int lrclk)
+{
+	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+	u16 iface1 = snd_soc_read(codec, WM8960_IFACE1);
+	u16 iface2 = snd_soc_read(codec, WM8960_IFACE2);
+	int i, j;
+
+	if (!(iface1 & (1<<6))) {
+		dev_dbg(codec->dev,
+			"Codec is slave mode, no need to configure clock\n");
+		return;
+	}
+
+	if (!wm8960->sysclk) {
+		dev_dbg(codec->dev, "No SYSCLK configured\n");
+		return;
+	}
+
+	if (!wm8960->bclk || !lrclk) {
+		dev_dbg(codec->dev, "No audio clocks configured\n");
+		return;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(dac_divs); ++i) {
+		if (wm8960->sysclk == lrclk * dac_divs[i]) {
+			for (j = 0; j < ARRAY_SIZE(bclk_divs); ++j) {
+				if (wm8960->sysclk ==  wm8960->bclk *
+						bclk_divs[j] / 10) {
+					goto config_clock;
+				}
+			}
+		}
+	}
+
+	dev_err(codec->dev, "Unsupported sysclk %d\n", wm8960->sysclk);
+	return;
+
+config_clock:
+	/* configure frame clock. If ADCLRC configure as GPIO pin, DACLRC
+	 * pin is used as a frame clock for ADCs and DACs.
+	 */
+	if (iface2 & (1<<6))
+		snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3);
+	else if (SNDRV_PCM_STREAM_PLAYBACK == stream)
+		snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3);
+	else if (SNDRV_PCM_STREAM_CAPTURE == stream)
+		snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, i << 6);
+
+	/* configure bit clock */
+	snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, j);
+}
+
 static int wm8960_hw_params(struct snd_pcm_substream *substream,
 			    struct snd_pcm_hw_params *params,
 			    struct snd_soc_dai *dai)
@@ -572,6 +636,10 @@  static int wm8960_hw_params(struct snd_pcm_substream *substream,
 	u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3;
 	int i;
 
+	wm8960->bclk = snd_soc_params_to_bclk(params);
+	if (params_channels(params) == 1)
+		wm8960->bclk *= 2;
+
 	/* bit size */
 	switch (params_width(params)) {
 	case 16:
@@ -602,6 +670,10 @@  static int wm8960_hw_params(struct snd_pcm_substream *substream,
 
 	/* set iface */
 	snd_soc_write(codec, WM8960_IFACE1, iface);
+
+	wm8960_configure_clocking(codec, substream->stream,
+			params_rate(params));
+
 	return 0;
 }
 
@@ -950,6 +1022,30 @@  static int wm8960_set_bias_level(struct snd_soc_codec *codec,
 	return wm8960->set_bias_level(codec, level);
 }
 
+static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+					unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+
+	switch (clk_id) {
+	case WM8960_SYSCLK_MCLK:
+		snd_soc_update_bits(codec, WM8960_CLOCK1,
+					0x1, WM8960_SYSCLK_MCLK);
+		break;
+	case WM8960_SYSCLK_PLL:
+		snd_soc_update_bits(codec, WM8960_CLOCK1,
+					0x1, WM8960_SYSCLK_PLL);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	wm8960->sysclk = freq;
+
+	return 0;
+}
+
 #define WM8960_RATES SNDRV_PCM_RATE_8000_48000
 
 #define WM8960_FORMATS \
@@ -962,6 +1058,7 @@  static const struct snd_soc_dai_ops wm8960_dai_ops = {
 	.set_fmt = wm8960_set_dai_fmt,
 	.set_clkdiv = wm8960_set_dai_clkdiv,
 	.set_pll = wm8960_set_dai_pll,
+	.set_sysclk = wm8960_set_dai_sysclk,
 };
 
 static struct snd_soc_dai_driver wm8960_dai = {