mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-14 15:54:15 +08:00
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: Mark Brown <broonie@opensource.wolfsonmicro.com> Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
This commit is contained in:
parent
8ef339df25
commit
c5607d8e7a
@ -192,6 +192,7 @@ struct wm8580_priv {
|
||||
u16 reg_cache[WM8580_MAX_REGISTER + 1];
|
||||
struct pll_state a;
|
||||
struct pll_state b;
|
||||
int sysclk[2];
|
||||
};
|
||||
|
||||
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,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const int wm8580_sysclk_ratios[] = {
|
||||
128, 192, 256, 384, 512, 768, 1152,
|
||||
};
|
||||
|
||||
/*
|
||||
* Set PCM DAI bit size and sample rate.
|
||||
*/
|
||||
@ -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_codec *codec = rtd->codec;
|
||||
struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
|
||||
u16 paifa = 0;
|
||||
u16 paifb = 0;
|
||||
int i, ratio;
|
||||
|
||||
/* bit size */
|
||||
switch (params_format(params)) {
|
||||
@ -492,6 +500,22 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
|
||||
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,
|
||||
WM8580_AIF_LENGTH_MASK, paifb);
|
||||
return 0;
|
||||
@ -501,9 +525,11 @@ static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
|
||||
unsigned int aifa;
|
||||
unsigned int aifb;
|
||||
int can_invert_lrclk;
|
||||
int sysclk;
|
||||
|
||||
aifa = snd_soc_read(codec, WM8580_PAIF1 + 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,
|
||||
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_PAIF3 + codec_dai->driver->id, aifb);
|
||||
|
||||
@ -611,28 +639,6 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
|
||||
snd_soc_write(codec, WM8580_PLLB4, reg);
|
||||
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:
|
||||
reg = snd_soc_read(codec, WM8580_PLLB4);
|
||||
reg &= ~WM8580_PLLB4_CLKOUTSRC_MASK;
|
||||
@ -666,6 +672,55 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
|
||||
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)
|
||||
{
|
||||
struct snd_soc_codec *codec = codec_dai->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)
|
||||
|
||||
static struct snd_soc_dai_ops wm8580_dai_ops_playback = {
|
||||
.set_sysclk = wm8580_set_sysclk,
|
||||
.hw_params = wm8580_paif_hw_params,
|
||||
.set_fmt = wm8580_set_paif_dai_fmt,
|
||||
.set_clkdiv = wm8580_set_dai_clkdiv,
|
||||
@ -727,6 +783,7 @@ static struct snd_soc_dai_ops wm8580_dai_ops_playback = {
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_ops wm8580_dai_ops_capture = {
|
||||
.set_sysclk = wm8580_set_sysclk,
|
||||
.hw_params = wm8580_paif_hw_params,
|
||||
.set_fmt = wm8580_set_paif_dai_fmt,
|
||||
.set_clkdiv = wm8580_set_dai_clkdiv,
|
||||
|
@ -19,14 +19,14 @@
|
||||
#define WM8580_PLLB 2
|
||||
|
||||
#define WM8580_MCLK 1
|
||||
#define WM8580_DAC_CLKSEL 2
|
||||
#define WM8580_CLKOUTSRC 3
|
||||
#define WM8580_CLKOUTSRC 2
|
||||
|
||||
#define WM8580_CLKSRC_MCLK 1
|
||||
#define WM8580_CLKSRC_PLLA 2
|
||||
#define WM8580_CLKSRC_PLLB 3
|
||||
#define WM8580_CLKSRC_OSC 4
|
||||
#define WM8580_CLKSRC_NONE 5
|
||||
#define WM8580_CLKSRC_MCLK 1
|
||||
#define WM8580_CLKSRC_PLLA 2
|
||||
#define WM8580_CLKSRC_PLLB 3
|
||||
#define WM8580_CLKSRC_OSC 4
|
||||
#define WM8580_CLKSRC_NONE 5
|
||||
#define WM8580_CLKSRC_ADCMCLK 6
|
||||
|
||||
#define WM8580_DAI_PAIFRX 0
|
||||
#define WM8580_DAI_PAIFTX 1
|
||||
|
@ -113,14 +113,13 @@ static int smdk64xx_hw_params(struct snd_pcm_substream *substream,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Explicitly set WM8580-DAC to source from MCLK */
|
||||
ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_DAC_CLKSEL,
|
||||
WM8580_CLKSRC_MCLK);
|
||||
ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0,
|
||||
SMDK64XX_WM8580_FREQ, pll_out);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0,
|
||||
SMDK64XX_WM8580_FREQ, pll_out);
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_PLLA,
|
||||
pll_out, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user