wm8974.c (33d81af4d12fc8863247abba1c1d706b462e89d0) wm8974.c (91d0c3ecbaf6616c0723d7aad9b6dadad2dea43f)
1/*
2 * wm8974.c -- WM8974 ALSA Soc Audio driver
3 *
4 * Copyright 2006 Wolfson Microelectronics PLC.
5 *
6 * Author: Liam Girdwood <linux@wolfsonmicro.com>
7 *
8 * This program is free software; you can redistribute it and/or modify

--- 311 unchanged lines hidden (view full) ---

320
321 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
322
323 snd_soc_dapm_new_widgets(codec);
324 return 0;
325}
326
327struct pll_ {
1/*
2 * wm8974.c -- WM8974 ALSA Soc Audio driver
3 *
4 * Copyright 2006 Wolfson Microelectronics PLC.
5 *
6 * Author: Liam Girdwood <linux@wolfsonmicro.com>
7 *
8 * This program is free software; you can redistribute it and/or modify

--- 311 unchanged lines hidden (view full) ---

320
321 snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
322
323 snd_soc_dapm_new_widgets(codec);
324 return 0;
325}
326
327struct pll_ {
328 unsigned int in_hz, out_hz;
329 unsigned int pre:4; /* prescale - 1 */
328 unsigned int pre_div:4; /* prescale - 1 */
330 unsigned int n:4;
331 unsigned int k;
332};
333
329 unsigned int n:4;
330 unsigned int k;
331};
332
334static struct pll_ pll[] = {
335 { 12000000, 11289600, 0, 7, 0x86c220 },
336 { 12000000, 12288000, 0, 8, 0x3126e8 },
337 { 13000000, 11289600, 0, 6, 0xf28bd4 },
338 { 13000000, 12288000, 0, 7, 0x8fd525 },
339 { 12288000, 11289600, 0, 7, 0x59999a },
340 { 11289600, 12288000, 0, 8, 0x80dee9 },
341 { 25000000, 11289600, 1, 7, 0x39B024 },
342 { 25000000, 24576000, 1, 7, 0xdd4413 }
343};
333static struct pll_ pll_div;
344
334
335/* The size in bits of the pll divide multiplied by 10
336 * to allow rounding later */
337#define FIXED_PLL_SIZE ((1 << 24) * 10)
338
339static void pll_factors(unsigned int target, unsigned int source)
340{
341 unsigned long long Kpart;
342 unsigned int K, Ndiv, Nmod;
343
344 Ndiv = target / source;
345 if (Ndiv < 6) {
346 source >>= 1;
347 pll_div.pre_div = 1;
348 Ndiv = target / source;
349 } else
350 pll_div.pre_div = 0;
351
352 if ((Ndiv < 6) || (Ndiv > 12))
353 printk(KERN_WARNING
354 "WM8974 N value %u outwith recommended range!d\n",
355 Ndiv);
356
357 pll_div.n = Ndiv;
358 Nmod = target % source;
359 Kpart = FIXED_PLL_SIZE * (long long)Nmod;
360
361 do_div(Kpart, source);
362
363 K = Kpart & 0xFFFFFFFF;
364
365 /* Check if we need to round */
366 if ((K % 10) >= 5)
367 K += 5;
368
369 /* Move down to proper range now rounding is done */
370 K /= 10;
371
372 pll_div.k = K;
373}
374
345static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
346 int pll_id, unsigned int freq_in, unsigned int freq_out)
347{
348 struct snd_soc_codec *codec = codec_dai->codec;
375static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
376 int pll_id, unsigned int freq_in, unsigned int freq_out)
377{
378 struct snd_soc_codec *codec = codec_dai->codec;
349 int i;
350 u16 reg;
351
352 if (freq_in == 0 || freq_out == 0) {
379 u16 reg;
380
381 if (freq_in == 0 || freq_out == 0) {
382 /* Clock CODEC directly from MCLK */
383 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK);
384 wm8974_write(codec, WM8974_CLOCK, reg & 0x0ff);
385
386 /* Turn off PLL */
353 reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
354 wm8974_write(codec, WM8974_POWER1, reg & 0x1df);
355 return 0;
356 }
357
387 reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
388 wm8974_write(codec, WM8974_POWER1, reg & 0x1df);
389 return 0;
390 }
391
358 for (i = 0; i < ARRAY_SIZE(pll); i++) {
359 if (freq_in == pll[i].in_hz && freq_out == pll[i].out_hz) {
360 wm8974_write(codec, WM8974_PLLN,
361 (pll[i].pre << 4) | pll[i].n);
362 wm8974_write(codec, WM8974_PLLK1, pll[i].k >> 18);
363 wm8974_write(codec, WM8974_PLLK2,
364 (pll[i].k >> 9) & 0x1ff);
365 wm8974_write(codec, WM8974_PLLK3, pll[i].k & 0x1ff);
366 reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
367 wm8974_write(codec, WM8974_POWER1, reg | 0x020);
368 return 0;
369 }
370 }
392 pll_factors(freq_out*4, freq_in);
371
393
372 return -EINVAL;
394 wm8974_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
395 wm8974_write(codec, WM8974_PLLK1, pll_div.k >> 18);
396 wm8974_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
397 wm8974_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
398 reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
399 wm8974_write(codec, WM8974_POWER1, reg | 0x020);
400
401 /* Run CODEC from PLL instead of MCLK */
402 reg = wm8974_read_reg_cache(codec, WM8974_CLOCK);
403 wm8974_write(codec, WM8974_CLOCK, reg | 0x100);
404
405 return 0;
373}
374
375/*
376 * Configure WM8974 clock dividers.
377 */
378static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
379 int div_id, int div)
380{

--- 427 unchanged lines hidden ---
406}
407
408/*
409 * Configure WM8974 clock dividers.
410 */
411static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
412 int div_id, int div)
413{

--- 427 unchanged lines hidden ---