aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZidan Wang <zidan.wang@freescale.com>2015-05-12 02:58:08 -0400
committerMark Brown <broonie@kernel.org>2015-05-12 15:05:27 -0400
commit0e50b51aa22fea0b6762f9d932541ec6f922928f (patch)
tree0e6abe2db040eff12bd4537eb7d0e7ad092c149c
parentb787f68c36d49bb1d9236f403813641efa74a031 (diff)
ASoC: wm8960: Let wm8960 driver configure its bit clock and frame clock
wm8960 codec driver missing configure its bit clock and frame clock for codec master mode, so add support for it. It will calculate a appropriate frequency dividing ratio according to the system clock, bit clock and frame clock, then set the corresponding registers. Signed-off-by: Zidan Wang <zidan.wang@freescale.com> Acked-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--sound/soc/codecs/wm8960.c101
1 files changed, 101 insertions, 0 deletions
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index 3035d9856415..c02ed1f1959a 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -127,6 +127,8 @@ struct wm8960_priv {
127 struct snd_soc_dapm_widget *out3; 127 struct snd_soc_dapm_widget *out3;
128 bool deemph; 128 bool deemph;
129 int playback_fs; 129 int playback_fs;
130 int bclk;
131 int sysclk;
130 struct wm8960_data pdata; 132 struct wm8960_data pdata;
131}; 133};
132 134
@@ -563,6 +565,72 @@ static struct {
563 { 8000, 5 }, 565 { 8000, 5 },
564}; 566};
565 567
568/* Multiply 256 for internal 256 div */
569static const int dac_divs[] = { 256, 384, 512, 768, 1024, 1408, 1536 };
570
571/* Multiply 10 to eliminate decimials */
572static const int bclk_divs[] = {
573 10, 15, 20, 30, 40, 55, 60, 80, 110,
574 120, 160, 220, 240, 320, 320, 320
575};
576
577static void wm8960_configure_clocking(struct snd_soc_codec *codec,
578 bool tx, int lrclk)
579{
580 struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
581 u16 iface1 = snd_soc_read(codec, WM8960_IFACE1);
582 u16 iface2 = snd_soc_read(codec, WM8960_IFACE2);
583 u32 sysclk;
584 int i, j;
585
586 if (!(iface1 & (1<<6))) {
587 dev_dbg(codec->dev,
588 "Codec is slave mode, no need to configure clock\n");
589 return;
590 }
591
592 if (!wm8960->sysclk) {
593 dev_dbg(codec->dev, "No SYSCLK configured\n");
594 return;
595 }
596
597 if (!wm8960->bclk || !lrclk) {
598 dev_dbg(codec->dev, "No audio clocks configured\n");
599 return;
600 }
601
602 for (i = 0; i < ARRAY_SIZE(dac_divs); ++i) {
603 if (wm8960->sysclk == lrclk * dac_divs[i]) {
604 for (j = 0; j < ARRAY_SIZE(bclk_divs); ++j) {
605 sysclk = wm8960->bclk * bclk_divs[j] / 10;
606 if (wm8960->sysclk == sysclk)
607 break;
608 }
609 if(j != ARRAY_SIZE(bclk_divs))
610 break;
611 }
612 }
613
614 if (i == ARRAY_SIZE(dac_divs)) {
615 dev_err(codec->dev, "Unsupported sysclk %d\n", wm8960->sysclk);
616 return;
617 }
618
619 /*
620 * configure frame clock. If ADCLRC configure as GPIO pin, DACLRC
621 * pin is used as a frame clock for ADCs and DACs.
622 */
623 if (iface2 & (1<<6))
624 snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3);
625 else if (tx)
626 snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3);
627 else if (!tx)
628 snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, i << 6);
629
630 /* configure bit clock */
631 snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, j);
632}
633
566static int wm8960_hw_params(struct snd_pcm_substream *substream, 634static int wm8960_hw_params(struct snd_pcm_substream *substream,
567 struct snd_pcm_hw_params *params, 635 struct snd_pcm_hw_params *params,
568 struct snd_soc_dai *dai) 636 struct snd_soc_dai *dai)
@@ -570,8 +638,13 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
570 struct snd_soc_codec *codec = dai->codec; 638 struct snd_soc_codec *codec = dai->codec;
571 struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); 639 struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
572 u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3; 640 u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3;
641 bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
573 int i; 642 int i;
574 643
644 wm8960->bclk = snd_soc_params_to_bclk(params);
645 if (params_channels(params) == 1)
646 wm8960->bclk *= 2;
647
575 /* bit size */ 648 /* bit size */
576 switch (params_width(params)) { 649 switch (params_width(params)) {
577 case 16: 650 case 16:
@@ -602,6 +675,9 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream,
602 675
603 /* set iface */ 676 /* set iface */
604 snd_soc_write(codec, WM8960_IFACE1, iface); 677 snd_soc_write(codec, WM8960_IFACE1, iface);
678
679 wm8960_configure_clocking(codec, tx, params_rate(params));
680
605 return 0; 681 return 0;
606} 682}
607 683
@@ -950,6 +1026,30 @@ static int wm8960_set_bias_level(struct snd_soc_codec *codec,
950 return wm8960->set_bias_level(codec, level); 1026 return wm8960->set_bias_level(codec, level);
951} 1027}
952 1028
1029static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
1030 unsigned int freq, int dir)
1031{
1032 struct snd_soc_codec *codec = dai->codec;
1033 struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
1034
1035 switch (clk_id) {
1036 case WM8960_SYSCLK_MCLK:
1037 snd_soc_update_bits(codec, WM8960_CLOCK1,
1038 0x1, WM8960_SYSCLK_MCLK);
1039 break;
1040 case WM8960_SYSCLK_PLL:
1041 snd_soc_update_bits(codec, WM8960_CLOCK1,
1042 0x1, WM8960_SYSCLK_PLL);
1043 break;
1044 default:
1045 return -EINVAL;
1046 }
1047
1048 wm8960->sysclk = freq;
1049
1050 return 0;
1051}
1052
953#define WM8960_RATES SNDRV_PCM_RATE_8000_48000 1053#define WM8960_RATES SNDRV_PCM_RATE_8000_48000
954 1054
955#define WM8960_FORMATS \ 1055#define WM8960_FORMATS \
@@ -962,6 +1062,7 @@ static const struct snd_soc_dai_ops wm8960_dai_ops = {
962 .set_fmt = wm8960_set_dai_fmt, 1062 .set_fmt = wm8960_set_dai_fmt,
963 .set_clkdiv = wm8960_set_dai_clkdiv, 1063 .set_clkdiv = wm8960_set_dai_clkdiv,
964 .set_pll = wm8960_set_dai_pll, 1064 .set_pll = wm8960_set_dai_pll,
1065 .set_sysclk = wm8960_set_dai_sysclk,
965}; 1066};
966 1067
967static struct snd_soc_dai_driver wm8960_dai = { 1068static struct snd_soc_dai_driver wm8960_dai = {