Commit c5607d8e authored by Mark Brown's avatar Mark Brown

ASoC: Automatically calculate clock ratio for WM8580

Implement set_sysclk() and then rather than assuming 256fs use the
supplied value to calculate and configure the clock ratio for the
currently used sample rate. As a side effect we also end up
implementing clock selection for the ADC path.

In order to avoid confusion remove the existing set_clkdiv() based
configuration of the clock source for the DAC and update the SMDK64xx
driver (which is the only in-tree user of the CODEC).
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: default avatarLiam Girdwood <lrg@slimlogic.co.uk>
parent 8ef339df
...@@ -192,6 +192,7 @@ struct wm8580_priv { ...@@ -192,6 +192,7 @@ struct wm8580_priv {
u16 reg_cache[WM8580_MAX_REGISTER + 1]; u16 reg_cache[WM8580_MAX_REGISTER + 1];
struct pll_state a; struct pll_state a;
struct pll_state b; struct pll_state b;
int sysclk[2];
}; };
static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
...@@ -464,6 +465,10 @@ static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, ...@@ -464,6 +465,10 @@ static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
return 0; return 0;
} }
static const int wm8580_sysclk_ratios[] = {
128, 192, 256, 384, 512, 768, 1152,
};
/* /*
* Set PCM DAI bit size and sample rate. * Set PCM DAI bit size and sample rate.
*/ */
...@@ -473,7 +478,10 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream, ...@@ -473,7 +478,10 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
{ {
struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec; struct snd_soc_codec *codec = rtd->codec;
struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
u16 paifa = 0;
u16 paifb = 0; u16 paifb = 0;
int i, ratio;
/* bit size */ /* bit size */
switch (params_format(params)) { switch (params_format(params)) {
...@@ -492,6 +500,22 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream, ...@@ -492,6 +500,22 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
return -EINVAL; return -EINVAL;
} }
/* Look up the SYSCLK ratio; accept only exact matches */
ratio = wm8580->sysclk[dai->id] / params_rate(params);
for (i = 0; i < ARRAY_SIZE(wm8580_sysclk_ratios); i++)
if (ratio == wm8580_sysclk_ratios[i])
break;
if (i == ARRAY_SIZE(wm8580_sysclk_ratios)) {
dev_err(codec->dev, "Invalid clock ratio %d/%d\n",
wm8580->sysclk[dai->id], params_rate(params));
return -EINVAL;
}
paifa |= i;
dev_dbg(codec->dev, "Running at %dfs with %dHz clock\n",
wm8580_sysclk_ratios[i], wm8580->sysclk[dai->driver->id]);
snd_soc_update_bits(codec, WM8580_PAIF1 + dai->driver->id,
WM8580_AIF_RATE_MASK, paifa);
snd_soc_update_bits(codec, WM8580_PAIF3 + dai->driver->id, snd_soc_update_bits(codec, WM8580_PAIF3 + dai->driver->id,
WM8580_AIF_LENGTH_MASK, paifb); WM8580_AIF_LENGTH_MASK, paifb);
return 0; return 0;
...@@ -501,9 +525,11 @@ static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai, ...@@ -501,9 +525,11 @@ static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt) unsigned int fmt)
{ {
struct snd_soc_codec *codec = codec_dai->codec; struct snd_soc_codec *codec = codec_dai->codec;
struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
unsigned int aifa; unsigned int aifa;
unsigned int aifb; unsigned int aifb;
int can_invert_lrclk; int can_invert_lrclk;
int sysclk;
aifa = snd_soc_read(codec, WM8580_PAIF1 + codec_dai->driver->id); aifa = snd_soc_read(codec, WM8580_PAIF1 + codec_dai->driver->id);
aifb = snd_soc_read(codec, WM8580_PAIF3 + codec_dai->driver->id); aifb = snd_soc_read(codec, WM8580_PAIF3 + codec_dai->driver->id);
...@@ -572,6 +598,8 @@ static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai, ...@@ -572,6 +598,8 @@ static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL; return -EINVAL;
} }
sysclk = wm8580->sysclk[codec_dai->driver->id];
snd_soc_write(codec, WM8580_PAIF1 + codec_dai->driver->id, aifa); snd_soc_write(codec, WM8580_PAIF1 + codec_dai->driver->id, aifa);
snd_soc_write(codec, WM8580_PAIF3 + codec_dai->driver->id, aifb); snd_soc_write(codec, WM8580_PAIF3 + codec_dai->driver->id, aifb);
...@@ -611,28 +639,6 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai, ...@@ -611,28 +639,6 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
snd_soc_write(codec, WM8580_PLLB4, reg); snd_soc_write(codec, WM8580_PLLB4, reg);
break; break;
case WM8580_DAC_CLKSEL:
reg = snd_soc_read(codec, WM8580_CLKSEL);
reg &= ~WM8580_CLKSEL_DAC_CLKSEL_MASK;
switch (div) {
case WM8580_CLKSRC_MCLK:
break;
case WM8580_CLKSRC_PLLA:
reg |= WM8580_CLKSEL_DAC_CLKSEL_PLLA;
break;
case WM8580_CLKSRC_PLLB:
reg |= WM8580_CLKSEL_DAC_CLKSEL_PLLB;
break;
default:
return -EINVAL;
}
snd_soc_write(codec, WM8580_CLKSEL, reg);
break;
case WM8580_CLKOUTSRC: case WM8580_CLKOUTSRC:
reg = snd_soc_read(codec, WM8580_PLLB4); reg = snd_soc_read(codec, WM8580_PLLB4);
reg &= ~WM8580_PLLB4_CLKOUTSRC_MASK; reg &= ~WM8580_PLLB4_CLKOUTSRC_MASK;
...@@ -666,6 +672,55 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai, ...@@ -666,6 +672,55 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
return 0; return 0;
} }
static int wm8580_set_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
struct snd_soc_codec *codec = dai->codec;
struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
int sel, sel_mask, sel_shift;
switch (dai->driver->id) {
case WM8580_DAI_PAIFTX:
sel_mask = 0x3;
sel_shift = 0;
break;
case WM8580_DAI_PAIFRX:
sel_mask = 0xc;
sel_shift = 2;
break;
default:
BUG_ON("Unknown DAI driver ID\n");
return -EINVAL;
}
switch (clk_id) {
case WM8580_CLKSRC_ADCMCLK:
if (dai->id != WM8580_DAI_PAIFTX)
return -EINVAL;
sel = 0 << sel_shift;
break;
case WM8580_CLKSRC_PLLA:
sel = 1 << sel_shift;
break;
case WM8580_CLKSRC_PLLB:
sel = 2 << sel_shift;
break;
case WM8580_CLKSRC_MCLK:
sel = 3 << sel_shift;
break;
default:
dev_err(codec->dev, "Unknown clock %d\n", clk_id);
return -EINVAL;
}
/* We really should validate PLL settings but not yet */
wm8580->sysclk[dai->id] = freq;
return snd_soc_update_bits(codec, WM8580_CLKSEL, sel, sel_mask);
}
static int wm8580_digital_mute(struct snd_soc_dai *codec_dai, int mute) static int wm8580_digital_mute(struct snd_soc_dai *codec_dai, int mute)
{ {
struct snd_soc_codec *codec = codec_dai->codec; struct snd_soc_codec *codec = codec_dai->codec;
...@@ -719,6 +774,7 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec, ...@@ -719,6 +774,7 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec,
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops wm8580_dai_ops_playback = { static struct snd_soc_dai_ops wm8580_dai_ops_playback = {
.set_sysclk = wm8580_set_sysclk,
.hw_params = wm8580_paif_hw_params, .hw_params = wm8580_paif_hw_params,
.set_fmt = wm8580_set_paif_dai_fmt, .set_fmt = wm8580_set_paif_dai_fmt,
.set_clkdiv = wm8580_set_dai_clkdiv, .set_clkdiv = wm8580_set_dai_clkdiv,
...@@ -727,6 +783,7 @@ static struct snd_soc_dai_ops wm8580_dai_ops_playback = { ...@@ -727,6 +783,7 @@ static struct snd_soc_dai_ops wm8580_dai_ops_playback = {
}; };
static struct snd_soc_dai_ops wm8580_dai_ops_capture = { static struct snd_soc_dai_ops wm8580_dai_ops_capture = {
.set_sysclk = wm8580_set_sysclk,
.hw_params = wm8580_paif_hw_params, .hw_params = wm8580_paif_hw_params,
.set_fmt = wm8580_set_paif_dai_fmt, .set_fmt = wm8580_set_paif_dai_fmt,
.set_clkdiv = wm8580_set_dai_clkdiv, .set_clkdiv = wm8580_set_dai_clkdiv,
......
...@@ -19,14 +19,14 @@ ...@@ -19,14 +19,14 @@
#define WM8580_PLLB 2 #define WM8580_PLLB 2
#define WM8580_MCLK 1 #define WM8580_MCLK 1
#define WM8580_DAC_CLKSEL 2 #define WM8580_CLKOUTSRC 2
#define WM8580_CLKOUTSRC 3
#define WM8580_CLKSRC_MCLK 1 #define WM8580_CLKSRC_MCLK 1
#define WM8580_CLKSRC_PLLA 2 #define WM8580_CLKSRC_PLLA 2
#define WM8580_CLKSRC_PLLB 3 #define WM8580_CLKSRC_PLLB 3
#define WM8580_CLKSRC_OSC 4 #define WM8580_CLKSRC_OSC 4
#define WM8580_CLKSRC_NONE 5 #define WM8580_CLKSRC_NONE 5
#define WM8580_CLKSRC_ADCMCLK 6
#define WM8580_DAI_PAIFRX 0 #define WM8580_DAI_PAIFRX 0
#define WM8580_DAI_PAIFTX 1 #define WM8580_DAI_PAIFTX 1
......
...@@ -113,14 +113,13 @@ static int smdk64xx_hw_params(struct snd_pcm_substream *substream, ...@@ -113,14 +113,13 @@ static int smdk64xx_hw_params(struct snd_pcm_substream *substream,
if (ret < 0) if (ret < 0)
return ret; return ret;
/* Explicitly set WM8580-DAC to source from MCLK */ ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0,
ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_DAC_CLKSEL, SMDK64XX_WM8580_FREQ, pll_out);
WM8580_CLKSRC_MCLK);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0, ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_PLLA,
SMDK64XX_WM8580_FREQ, pll_out); pll_out, SND_SOC_CLOCK_IN);
if (ret < 0) if (ret < 0)
return ret; return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment