aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 = {