aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2010-08-13 14:05:04 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2010-08-15 09:52:12 -0400
commitc5607d8e7a4c30d2ff62b8eefe3f977d5c71d2fe (patch)
tree4bcb668243cacfabcb9d58076911093391dbd94b /sound
parent8ef339df25ed424e7430fd411a52840c6af368c6 (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.c101
-rw-r--r--sound/soc/codecs/wm8580.h14
-rw-r--r--sound/soc/s3c24xx/smdk64xx_wm8580.c9
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
197static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); 198static 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
468static 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
675static 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
669static int wm8580_digital_mute(struct snd_soc_dai *codec_dai, int mute) 724static 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
721static struct snd_soc_dai_ops wm8580_dai_ops_playback = { 776static 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
729static struct snd_soc_dai_ops wm8580_dai_ops_capture = { 785static 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