LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
From: Peter Rosin <peda@lysator.liu.se>
To: alsa-devel@alsa-project.org
Cc: Peter Rosin <peda@axentia.se>,
	Liam Girdwood <lgirdwood@gmail.com>,
	Mark Brown <broonie@linaro.org>, Jaroslav Kysela <perex@perex.cz>,
	Takashi Iwai <tiwai@suse.de>,
	linux-kernel@vger.kernel.org
Subject: [PATCH 6/7] ASoC: pcm512x: Avoid the PLL for the DAC clock, if possible
Date: Wed, 14 Jan 2015 12:52:49 +0100	[thread overview]
Message-ID: <1421236370-19826-7-git-send-email-peda@lysator.liu.se> (raw)
In-Reply-To: <1421236370-19826-1-git-send-email-peda@lysator.liu.se>

From: Peter Rosin <peda@axentia.se>

The PLL introduces jitter, which in turn introduces noice if used
to clock the DAC. Thus, avoid the PLL output, and use the PLL input
to drive the DAC clock, if possible.

This is described for the PCM5142/PCM5242 chips in the answers to the
forum post "PCM5142/PCM5242 DAC clock source" at the TI E2E community
pages (1).

(1) http://e2e.ti.com/support/data_converters/audio_converters/f/64/t/389994

Signed-off-by: Peter Rosin <peda@axentia.se>
---
 sound/soc/codecs/pcm512x.c |  119 ++++++++++++++++++++++++++++++++++----------
 sound/soc/codecs/pcm512x.h |    4 +-
 2 files changed, 96 insertions(+), 27 deletions(-)

diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c
index df8948b4e848..2a50c58a2fb1 100644
--- a/sound/soc/codecs/pcm512x.c
+++ b/sound/soc/codecs/pcm512x.c
@@ -105,6 +105,7 @@ static const struct reg_default pcm512x_reg_defaults[] = {
 	{ PCM512x_VCOM_CTRL_2,       0x01 },
 	{ PCM512x_BCLK_LRCLK_CFG,    0x00 },
 	{ PCM512x_MASTER_MODE,       0x7c },
+	{ PCM512x_GPIO_DACIN,        0x00 },
 	{ PCM512x_GPIO_PLLIN,        0x00 },
 	{ PCM512x_SYNCHRONIZE,       0x10 },
 	{ PCM512x_PLL_COEFF_0,       0x00 },
@@ -138,6 +139,7 @@ static bool pcm512x_readable(struct device *dev, unsigned int reg)
 	case PCM512x_MASTER_MODE:
 	case PCM512x_PLL_REF:
 	case PCM512x_DAC_REF:
+	case PCM512x_GPIO_DACIN:
 	case PCM512x_GPIO_PLLIN:
 	case PCM512x_SYNCHRONIZE:
 	case PCM512x_PLL_COEFF_0:
@@ -681,6 +683,37 @@ done:
 	return 0;
 }
 
+static unsigned long pcm512x_pllin_dac_rate(struct snd_soc_dai *dai,
+					    unsigned long osr_rate,
+					    unsigned long pllin_rate)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+	unsigned long dac_rate;
+
+	if (!pcm512x->pll_out)
+		return 0; /* no PLL to bypass, force SCK as DAC input */
+
+	if (pllin_rate % osr_rate)
+		return 0; /* futile, quit early */
+
+	/* run DAC no faster than 6144000 Hz */
+	for (dac_rate = rounddown(6144000, osr_rate);
+	     dac_rate;
+	     dac_rate -= osr_rate) {
+
+		if (pllin_rate / dac_rate > 128)
+			return 0; /* DAC divider would be too big */
+
+		if (!(pllin_rate % dac_rate))
+			return dac_rate;
+
+		dac_rate -= osr_rate;
+	}
+
+	return 0;
+}
+
 static int pcm512x_set_dividers(struct snd_soc_dai *dai,
 				struct snd_pcm_hw_params *params)
 {
@@ -694,6 +727,7 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
 	unsigned long bclk_rate;
 	unsigned long sample_rate;
 	unsigned long osr_rate;
+	unsigned long dacsrc_rate;
 	int bclk_div;
 	int lrclk_div;
 	int dsp_div;
@@ -701,11 +735,10 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
 	unsigned long dac_rate;
 	int ncp_div;
 	int osr_div;
-	unsigned long dac_mul;
-	unsigned long sck_mul;
 	int ret;
 	int idac;
 	int fssp;
+	int gpio;
 
 	lrclk_div = pcm512x_params_to_frame_size(params);
 	if (lrclk_div == 0) {
@@ -794,31 +827,72 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
 	/* run DSP no faster than 50 MHz */
 	dsp_div = mck_rate > 50000000 ? 2 : 1;
 
-	/* run DAC no faster than 6144000 Hz */
-	dac_mul = 6144000 / osr_rate;
-	sck_mul = sck_rate / osr_rate;
-	for (; dac_mul; dac_mul--) {
-		if (!(sck_mul % dac_mul))
-			break;
-	}
-	if (!dac_mul) {
-		dev_err(dev, "Failed to find DAC rate\n");
-		return -EINVAL;
-	}
+	dac_rate = pcm512x_pllin_dac_rate(dai, osr_rate, pllin_rate);
+	if (dac_rate) {
+		/* the desired clock rate is "compatible" with the pll input
+		 * clock, so use that clock as dac input instead of the pll
+		 * output clock since the pll will introduce jitter and thus
+		 * noise.
+		 */
+		dev_dbg(dev, "using pll input as dac input\n");
+		ret = regmap_update_bits(pcm512x->regmap, PCM512x_DAC_REF,
+					 PCM512x_SDAC, PCM512x_SDAC_GPIO);
+		if (ret != 0) {
+			dev_err(codec->dev,
+				"Failed to set gpio as dacref: %d\n", ret);
+			return ret;
+		}
 
-	dac_rate = dac_mul * osr_rate;
-	dev_dbg(dev, "dac_rate %lu sample_rate %lu\n", dac_rate, sample_rate);
+		gpio = PCM512x_GREF_GPIO1 + pcm512x->pll_in - 1;
+		ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_DACIN,
+					 PCM512x_GREF, gpio);
+		if (ret != 0) {
+			dev_err(codec->dev,
+				"Failed to set gpio %d as dacin: %d\n",
+				pcm512x->pll_in, ret);
+			return ret;
+		}
+
+		dacsrc_rate = pllin_rate;
+	} else {
+		/* run DAC no faster than 6144000 Hz */
+		unsigned long dac_mul = 6144000 / osr_rate;
+		unsigned long sck_mul = sck_rate / osr_rate;
+
+		for (; dac_mul; dac_mul--) {
+			if (!(sck_mul % dac_mul))
+				break;
+		}
+		if (!dac_mul) {
+			dev_err(dev, "Failed to find DAC rate\n");
+			return -EINVAL;
+		}
 
-	dac_div = DIV_ROUND_CLOSEST(sck_rate, dac_rate);
+		dac_rate = dac_mul * osr_rate;
+		dev_dbg(dev, "dac_rate %lu sample_rate %lu\n",
+			dac_rate, sample_rate);
+
+		ret = regmap_update_bits(pcm512x->regmap, PCM512x_DAC_REF,
+					 PCM512x_SDAC, PCM512x_SDAC_SCK);
+		if (ret != 0) {
+			dev_err(codec->dev,
+				"Failed to set sck as dacref: %d\n", ret);
+			return ret;
+		}
+
+		dacsrc_rate = sck_rate;
+	}
+
+	dac_div = DIV_ROUND_CLOSEST(dacsrc_rate, dac_rate);
 	if (dac_div > 128) {
 		dev_err(dev, "Failed to find DAC divider\n");
 		return -EINVAL;
 	}
 
-	ncp_div = DIV_ROUND_CLOSEST(sck_rate / dac_div, 1536000);
-	if (ncp_div > 128 || sck_rate / dac_div / ncp_div > 2048000) {
+	ncp_div = DIV_ROUND_CLOSEST(dacsrc_rate / dac_div, 1536000);
+	if (ncp_div > 128 || dacsrc_rate / dac_div / ncp_div > 2048000) {
 		/* run NCP no faster than 2048000 Hz, but why? */
-		ncp_div = DIV_ROUND_UP(sck_rate / dac_div, 2048000);
+		ncp_div = DIV_ROUND_UP(dacsrc_rate / dac_div, 2048000);
 		if (ncp_div > 128) {
 			dev_err(dev, "Failed to find NCP divider\n");
 			return -EINVAL;
@@ -1033,13 +1107,6 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
 	if (ret != 0)
 		return ret;
 
-	ret = regmap_update_bits(pcm512x->regmap, PCM512x_DAC_REF,
-				 PCM512x_SDAC, PCM512x_SDAC_SCK);
-	if (ret != 0) {
-		dev_err(codec->dev, "Failed to set sck as dacref: %d\n", ret);
-		return ret;
-	}
-
 	if (pcm512x->pll_out) {
 		ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_REF,
 					 PCM512x_SREF, PCM512x_SREF_GPIO);
diff --git a/sound/soc/codecs/pcm512x.h b/sound/soc/codecs/pcm512x.h
index eba5adc2cdb1..b7c310207223 100644
--- a/sound/soc/codecs/pcm512x.h
+++ b/sound/soc/codecs/pcm512x.h
@@ -38,6 +38,7 @@
 #define PCM512x_MASTER_MODE       (PCM512x_PAGE_BASE(0) +  12)
 #define PCM512x_PLL_REF           (PCM512x_PAGE_BASE(0) +  13)
 #define PCM512x_DAC_REF           (PCM512x_PAGE_BASE(0) +  14)
+#define PCM512x_GPIO_DACIN        (PCM512x_PAGE_BASE(0) +  16)
 #define PCM512x_GPIO_PLLIN        (PCM512x_PAGE_BASE(0) +  18)
 #define PCM512x_SYNCHRONIZE       (PCM512x_PAGE_BASE(0) +  19)
 #define PCM512x_PLL_COEFF_0       (PCM512x_PAGE_BASE(0) +  20)
@@ -162,8 +163,9 @@
 #define PCM512x_SDAC_PLL    (1 << 4)
 #define PCM512x_SDAC_SCK    (3 << 4)
 #define PCM512x_SDAC_BCK    (4 << 4)
+#define PCM512x_SDAC_GPIO   (5 << 4)
 
-/* Page 0, Register 18 - GPIO source for PLL */
+/* Page 0, Register 16, 18 - GPIO source for DAC, PLL */
 #define PCM512x_GREF        (7 << 0)
 #define PCM512x_GREF_SHIFT  0
 #define PCM512x_GREF_GPIO1  (0 << 0)
-- 
1.7.10.4


  parent reply	other threads:[~2015-01-14 11:54 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-01-14 11:52 [PATCH 0/7] ASoC: pcm512x: Clock master modes Peter Rosin
2015-01-14 11:52 ` [PATCH 1/7] ALSA: pcm: Add snd_interval_ranges() and snd_pcm_hw_constraint_ranges() Peter Rosin
2015-01-26 22:10   ` [alsa-devel] " Lars-Peter Clausen
2015-01-14 11:52 ` [PATCH 2/7] ASoC: pcm512x: Fix spelling of register field names Peter Rosin
2015-01-14 11:52 ` [PATCH 3/7] ASoC: pcm512x: Change register default to match actual content after reset Peter Rosin
2015-01-14 11:52 ` [PATCH 4/7] ASoC: pcm512x: Support mastering BCLK/LRCLK without using the PLL Peter Rosin
2015-01-14 11:52 ` [PATCH 5/7] ASoC: pcm512x: Support mastering BCLK/LRCLK " Peter Rosin
2015-01-14 11:52 ` Peter Rosin [this message]
2015-01-14 11:52 ` [PATCH 7/7] ASoC: pcm512x: Support SND_SOC_DAIFMT_CBM_CFS Peter Rosin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1421236370-19826-7-git-send-email-peda@lysator.liu.se \
    --to=peda@lysator.liu.se \
    --cc=alsa-devel@alsa-project.org \
    --cc=broonie@linaro.org \
    --cc=lgirdwood@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=peda@axentia.se \
    --cc=perex@perex.cz \
    --cc=tiwai@suse.de \
    --subject='Re: [PATCH 6/7] ASoC: pcm512x: Avoid the PLL for the DAC clock, if possible' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).