diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2010-08-13 14:05:04 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2010-08-15 09:52:12 -0400 |
commit | c5607d8e7a4c30d2ff62b8eefe3f977d5c71d2fe (patch) | |
tree | 4bcb668243cacfabcb9d58076911093391dbd94b /sound | |
parent | 8ef339df25ed424e7430fd411a52840c6af368c6 (diff) |
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>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/codecs/wm8580.c | 101 | ||||
-rw-r--r-- | sound/soc/codecs/wm8580.h | 14 | ||||
-rw-r--r-- | sound/soc/s3c24xx/smdk64xx_wm8580.c | 9 |
3 files changed, 90 insertions, 34 deletions
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index 9964f02f84e2..7942f917d31e 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c | |||
@@ -192,6 +192,7 @@ struct wm8580_priv { | |||
192 | u16 reg_cache[WM8580_MAX_REGISTER + 1]; | 192 | u16 reg_cache[WM8580_MAX_REGISTER + 1]; |
193 | struct pll_state a; | 193 | struct pll_state a; |
194 | struct pll_state b; | 194 | struct pll_state b; |
195 | int sysclk[2]; | ||
195 | }; | 196 | }; |
196 | 197 | ||
197 | static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); | 198 | 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 | return 0; | 465 | return 0; |
465 | } | 466 | } |
466 | 467 | ||
468 | static const int wm8580_sysclk_ratios[] = { | ||
469 | 128, 192, 256, 384, 512, 768, 1152, | ||
470 | }; | ||
471 | |||
467 | /* | 472 | /* |
468 | * Set PCM DAI bit size and sample rate. | 473 | * Set PCM DAI bit size and sample rate. |
469 | */ | 474 | */ |
@@ -473,7 +478,10 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream, | |||
473 | { | 478 | { |
474 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 479 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
475 | struct snd_soc_codec *codec = rtd->codec; | 480 | struct snd_soc_codec *codec = rtd->codec; |
481 | struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); | ||
482 | u16 paifa = 0; | ||
476 | u16 paifb = 0; | 483 | u16 paifb = 0; |
484 | int i, ratio; | ||
477 | 485 | ||
478 | /* bit size */ | 486 | /* bit size */ |
479 | switch (params_format(params)) { | 487 | switch (params_format(params)) { |
@@ -492,6 +500,22 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream, | |||
492 | return -EINVAL; | 500 | return -EINVAL; |
493 | } | 501 | } |
494 | 502 | ||
503 | /* Look up the SYSCLK ratio; accept only exact matches */ | ||
504 | ratio = wm8580->sysclk[dai->id] / params_rate(params); | ||
505 | for (i = 0; i < ARRAY_SIZE(wm8580_sysclk_ratios); i++) | ||
506 | if (ratio == wm8580_sysclk_ratios[i]) | ||
507 | break; | ||
508 | if (i == ARRAY_SIZE(wm8580_sysclk_ratios)) { | ||
509 | dev_err(codec->dev, "Invalid clock ratio %d/%d\n", | ||
510 | wm8580->sysclk[dai->id], params_rate(params)); | ||
511 | return -EINVAL; | ||
512 | } | ||
513 | paifa |= i; | ||
514 | dev_dbg(codec->dev, "Running at %dfs with %dHz clock\n", | ||
515 | wm8580_sysclk_ratios[i], wm8580->sysclk[dai->driver->id]); | ||
516 | |||
517 | snd_soc_update_bits(codec, WM8580_PAIF1 + dai->driver->id, | ||
518 | WM8580_AIF_RATE_MASK, paifa); | ||
495 | snd_soc_update_bits(codec, WM8580_PAIF3 + dai->driver->id, | 519 | snd_soc_update_bits(codec, WM8580_PAIF3 + dai->driver->id, |
496 | WM8580_AIF_LENGTH_MASK, paifb); | 520 | WM8580_AIF_LENGTH_MASK, paifb); |
497 | return 0; | 521 | return 0; |
@@ -501,9 +525,11 @@ static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai, | |||
501 | unsigned int fmt) | 525 | unsigned int fmt) |
502 | { | 526 | { |
503 | struct snd_soc_codec *codec = codec_dai->codec; | 527 | struct snd_soc_codec *codec = codec_dai->codec; |
528 | struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); | ||
504 | unsigned int aifa; | 529 | unsigned int aifa; |
505 | unsigned int aifb; | 530 | unsigned int aifb; |
506 | int can_invert_lrclk; | 531 | int can_invert_lrclk; |
532 | int sysclk; | ||
507 | 533 | ||
508 | aifa = snd_soc_read(codec, WM8580_PAIF1 + codec_dai->driver->id); | 534 | aifa = snd_soc_read(codec, WM8580_PAIF1 + codec_dai->driver->id); |
509 | aifb = snd_soc_read(codec, WM8580_PAIF3 + codec_dai->driver->id); | 535 | 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 | return -EINVAL; | 598 | return -EINVAL; |
573 | } | 599 | } |
574 | 600 | ||
601 | sysclk = wm8580->sysclk[codec_dai->driver->id]; | ||
602 | |||
575 | snd_soc_write(codec, WM8580_PAIF1 + codec_dai->driver->id, aifa); | 603 | snd_soc_write(codec, WM8580_PAIF1 + codec_dai->driver->id, aifa); |
576 | snd_soc_write(codec, WM8580_PAIF3 + codec_dai->driver->id, aifb); | 604 | snd_soc_write(codec, WM8580_PAIF3 + codec_dai->driver->id, aifb); |
577 | 605 | ||
@@ -611,28 +639,6 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai, | |||
611 | snd_soc_write(codec, WM8580_PLLB4, reg); | 639 | snd_soc_write(codec, WM8580_PLLB4, reg); |
612 | break; | 640 | break; |
613 | 641 | ||
614 | case WM8580_DAC_CLKSEL: | ||
615 | reg = snd_soc_read(codec, WM8580_CLKSEL); | ||
616 | reg &= ~WM8580_CLKSEL_DAC_CLKSEL_MASK; | ||
617 | |||
618 | switch (div) { | ||
619 | case WM8580_CLKSRC_MCLK: | ||
620 | break; | ||
621 | |||
622 | case WM8580_CLKSRC_PLLA: | ||
623 | reg |= WM8580_CLKSEL_DAC_CLKSEL_PLLA; | ||
624 | break; | ||
625 | |||
626 | case WM8580_CLKSRC_PLLB: | ||
627 | reg |= WM8580_CLKSEL_DAC_CLKSEL_PLLB; | ||
628 | break; | ||
629 | |||
630 | default: | ||
631 | return -EINVAL; | ||
632 | } | ||
633 | snd_soc_write(codec, WM8580_CLKSEL, reg); | ||
634 | break; | ||
635 | |||
636 | case WM8580_CLKOUTSRC: | 642 | case WM8580_CLKOUTSRC: |
637 | reg = snd_soc_read(codec, WM8580_PLLB4); | 643 | reg = snd_soc_read(codec, WM8580_PLLB4); |
638 | reg &= ~WM8580_PLLB4_CLKOUTSRC_MASK; | 644 | reg &= ~WM8580_PLLB4_CLKOUTSRC_MASK; |
@@ -666,6 +672,55 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai, | |||
666 | return 0; | 672 | return 0; |
667 | } | 673 | } |
668 | 674 | ||
675 | static int wm8580_set_sysclk(struct snd_soc_dai *dai, int clk_id, | ||
676 | unsigned int freq, int dir) | ||
677 | { | ||
678 | struct snd_soc_codec *codec = dai->codec; | ||
679 | struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); | ||
680 | int sel, sel_mask, sel_shift; | ||
681 | |||
682 | switch (dai->driver->id) { | ||
683 | case WM8580_DAI_PAIFTX: | ||
684 | sel_mask = 0x3; | ||
685 | sel_shift = 0; | ||
686 | break; | ||
687 | |||
688 | case WM8580_DAI_PAIFRX: | ||
689 | sel_mask = 0xc; | ||
690 | sel_shift = 2; | ||
691 | break; | ||
692 | |||
693 | default: | ||
694 | BUG_ON("Unknown DAI driver ID\n"); | ||
695 | return -EINVAL; | ||
696 | } | ||
697 | |||
698 | switch (clk_id) { | ||
699 | case WM8580_CLKSRC_ADCMCLK: | ||
700 | if (dai->id != WM8580_DAI_PAIFTX) | ||
701 | return -EINVAL; | ||
702 | sel = 0 << sel_shift; | ||
703 | break; | ||
704 | case WM8580_CLKSRC_PLLA: | ||
705 | sel = 1 << sel_shift; | ||
706 | break; | ||
707 | case WM8580_CLKSRC_PLLB: | ||
708 | sel = 2 << sel_shift; | ||
709 | break; | ||
710 | case WM8580_CLKSRC_MCLK: | ||
711 | sel = 3 << sel_shift; | ||
712 | break; | ||
713 | default: | ||
714 | dev_err(codec->dev, "Unknown clock %d\n", clk_id); | ||
715 | return -EINVAL; | ||
716 | } | ||
717 | |||
718 | /* We really should validate PLL settings but not yet */ | ||
719 | wm8580->sysclk[dai->id] = freq; | ||
720 | |||
721 | return snd_soc_update_bits(codec, WM8580_CLKSEL, sel, sel_mask); | ||
722 | } | ||
723 | |||
669 | static int wm8580_digital_mute(struct snd_soc_dai *codec_dai, int mute) | 724 | static int wm8580_digital_mute(struct snd_soc_dai *codec_dai, int mute) |
670 | { | 725 | { |
671 | struct snd_soc_codec *codec = codec_dai->codec; | 726 | struct snd_soc_codec *codec = codec_dai->codec; |
@@ -719,6 +774,7 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec, | |||
719 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) | 774 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) |
720 | 775 | ||
721 | static struct snd_soc_dai_ops wm8580_dai_ops_playback = { | 776 | static struct snd_soc_dai_ops wm8580_dai_ops_playback = { |
777 | .set_sysclk = wm8580_set_sysclk, | ||
722 | .hw_params = wm8580_paif_hw_params, | 778 | .hw_params = wm8580_paif_hw_params, |
723 | .set_fmt = wm8580_set_paif_dai_fmt, | 779 | .set_fmt = wm8580_set_paif_dai_fmt, |
724 | .set_clkdiv = wm8580_set_dai_clkdiv, | 780 | .set_clkdiv = wm8580_set_dai_clkdiv, |
@@ -727,6 +783,7 @@ static struct snd_soc_dai_ops wm8580_dai_ops_playback = { | |||
727 | }; | 783 | }; |
728 | 784 | ||
729 | static struct snd_soc_dai_ops wm8580_dai_ops_capture = { | 785 | static struct snd_soc_dai_ops wm8580_dai_ops_capture = { |
786 | .set_sysclk = wm8580_set_sysclk, | ||
730 | .hw_params = wm8580_paif_hw_params, | 787 | .hw_params = wm8580_paif_hw_params, |
731 | .set_fmt = wm8580_set_paif_dai_fmt, | 788 | .set_fmt = wm8580_set_paif_dai_fmt, |
732 | .set_clkdiv = wm8580_set_dai_clkdiv, | 789 | .set_clkdiv = wm8580_set_dai_clkdiv, |
diff --git a/sound/soc/codecs/wm8580.h b/sound/soc/codecs/wm8580.h index 8328ef667593..1d34656d0dcb 100644 --- a/sound/soc/codecs/wm8580.h +++ b/sound/soc/codecs/wm8580.h | |||
@@ -19,14 +19,14 @@ | |||
19 | #define WM8580_PLLB 2 | 19 | #define WM8580_PLLB 2 |
20 | 20 | ||
21 | #define WM8580_MCLK 1 | 21 | #define WM8580_MCLK 1 |
22 | #define WM8580_DAC_CLKSEL 2 | 22 | #define WM8580_CLKOUTSRC 2 |
23 | #define WM8580_CLKOUTSRC 3 | ||
24 | 23 | ||
25 | #define WM8580_CLKSRC_MCLK 1 | 24 | #define WM8580_CLKSRC_MCLK 1 |
26 | #define WM8580_CLKSRC_PLLA 2 | 25 | #define WM8580_CLKSRC_PLLA 2 |
27 | #define WM8580_CLKSRC_PLLB 3 | 26 | #define WM8580_CLKSRC_PLLB 3 |
28 | #define WM8580_CLKSRC_OSC 4 | 27 | #define WM8580_CLKSRC_OSC 4 |
29 | #define WM8580_CLKSRC_NONE 5 | 28 | #define WM8580_CLKSRC_NONE 5 |
29 | #define WM8580_CLKSRC_ADCMCLK 6 | ||
30 | 30 | ||
31 | #define WM8580_DAI_PAIFRX 0 | 31 | #define WM8580_DAI_PAIFRX 0 |
32 | #define WM8580_DAI_PAIFTX 1 | 32 | #define WM8580_DAI_PAIFTX 1 |
diff --git a/sound/soc/s3c24xx/smdk64xx_wm8580.c b/sound/soc/s3c24xx/smdk64xx_wm8580.c index 5c21e26da075..91367f7bfec3 100644 --- a/sound/soc/s3c24xx/smdk64xx_wm8580.c +++ b/sound/soc/s3c24xx/smdk64xx_wm8580.c | |||
@@ -113,14 +113,13 @@ static int smdk64xx_hw_params(struct snd_pcm_substream *substream, | |||
113 | if (ret < 0) | 113 | if (ret < 0) |
114 | return ret; | 114 | return ret; |
115 | 115 | ||
116 | /* Explicitly set WM8580-DAC to source from MCLK */ | 116 | ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0, |
117 | ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_DAC_CLKSEL, | 117 | SMDK64XX_WM8580_FREQ, pll_out); |
118 | WM8580_CLKSRC_MCLK); | ||
119 | if (ret < 0) | 118 | if (ret < 0) |
120 | return ret; | 119 | return ret; |
121 | 120 | ||
122 | ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0, | 121 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_PLLA, |
123 | SMDK64XX_WM8580_FREQ, pll_out); | 122 | pll_out, SND_SOC_CLOCK_IN); |
124 | if (ret < 0) | 123 | if (ret < 0) |
125 | return ret; | 124 | return ret; |
126 | 125 | ||