summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sound/soc/codecs/pcm512x.c119
-rw-r--r--sound/soc/codecs/pcm512x.h4
2 files changed, 96 insertions, 27 deletions
diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c
index 66dd036f0141..8b474196d52a 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:
@@ -659,6 +661,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)
{
@@ -672,6 +705,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;
@@ -679,11 +713,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 = snd_soc_params_to_frame_size(params);
if (lrclk_div == 0) {
@@ -772,31 +805,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;
@@ -1011,13 +1085,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)