ASoC: Refresh WM8974 PLL configuration

Move away from a fixed table to runtime calculation.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
This commit is contained in:
Mark Brown 2009-06-30 19:02:32 +01:00
Родитель 33d81af4d1
Коммит 91d0c3ecba
1 изменённых файлов: 60 добавлений и 27 удалений

Просмотреть файл

@ -325,51 +325,84 @@ static int wm8974_add_widgets(struct snd_soc_codec *codec)
} }
struct pll_ { struct pll_ {
unsigned int in_hz, out_hz; unsigned int pre_div:4; /* prescale - 1 */
unsigned int pre:4; /* prescale - 1 */
unsigned int n:4; unsigned int n:4;
unsigned int k; unsigned int k;
}; };
static struct pll_ pll[] = { static struct pll_ pll_div;
{ 12000000, 11289600, 0, 7, 0x86c220 },
{ 12000000, 12288000, 0, 8, 0x3126e8 }, /* The size in bits of the pll divide multiplied by 10
{ 13000000, 11289600, 0, 6, 0xf28bd4 }, * to allow rounding later */
{ 13000000, 12288000, 0, 7, 0x8fd525 }, #define FIXED_PLL_SIZE ((1 << 24) * 10)
{ 12288000, 11289600, 0, 7, 0x59999a },
{ 11289600, 12288000, 0, 8, 0x80dee9 }, static void pll_factors(unsigned int target, unsigned int source)
{ 25000000, 11289600, 1, 7, 0x39B024 }, {
{ 25000000, 24576000, 1, 7, 0xdd4413 } unsigned long long Kpart;
}; unsigned int K, Ndiv, Nmod;
Ndiv = target / source;
if (Ndiv < 6) {
source >>= 1;
pll_div.pre_div = 1;
Ndiv = target / source;
} else
pll_div.pre_div = 0;
if ((Ndiv < 6) || (Ndiv > 12))
printk(KERN_WARNING
"WM8974 N value %u outwith recommended range!d\n",
Ndiv);
pll_div.n = Ndiv;
Nmod = target % source;
Kpart = FIXED_PLL_SIZE * (long long)Nmod;
do_div(Kpart, source);
K = Kpart & 0xFFFFFFFF;
/* Check if we need to round */
if ((K % 10) >= 5)
K += 5;
/* Move down to proper range now rounding is done */
K /= 10;
pll_div.k = K;
}
static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
int pll_id, unsigned int freq_in, unsigned int freq_out) int pll_id, unsigned int freq_in, unsigned int freq_out)
{ {
struct snd_soc_codec *codec = codec_dai->codec; struct snd_soc_codec *codec = codec_dai->codec;
int i;
u16 reg; u16 reg;
if (freq_in == 0 || freq_out == 0) { if (freq_in == 0 || freq_out == 0) {
/* Clock CODEC directly from MCLK */
reg = wm8974_read_reg_cache(codec, WM8974_CLOCK);
wm8974_write(codec, WM8974_CLOCK, reg & 0x0ff);
/* Turn off PLL */
reg = wm8974_read_reg_cache(codec, WM8974_POWER1); reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
wm8974_write(codec, WM8974_POWER1, reg & 0x1df); wm8974_write(codec, WM8974_POWER1, reg & 0x1df);
return 0; return 0;
} }
for (i = 0; i < ARRAY_SIZE(pll); i++) { pll_factors(freq_out*4, freq_in);
if (freq_in == pll[i].in_hz && freq_out == pll[i].out_hz) {
wm8974_write(codec, WM8974_PLLN,
(pll[i].pre << 4) | pll[i].n);
wm8974_write(codec, WM8974_PLLK1, pll[i].k >> 18);
wm8974_write(codec, WM8974_PLLK2,
(pll[i].k >> 9) & 0x1ff);
wm8974_write(codec, WM8974_PLLK3, pll[i].k & 0x1ff);
reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
wm8974_write(codec, WM8974_POWER1, reg | 0x020);
return 0;
}
}
return -EINVAL; wm8974_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
wm8974_write(codec, WM8974_PLLK1, pll_div.k >> 18);
wm8974_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
wm8974_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
wm8974_write(codec, WM8974_POWER1, reg | 0x020);
/* Run CODEC from PLL instead of MCLK */
reg = wm8974_read_reg_cache(codec, WM8974_CLOCK);
wm8974_write(codec, WM8974_CLOCK, reg | 0x100);
return 0;
} }
/* /*