diff options
-rw-r--r-- | sound/soc/codecs/wm8960.c | 101 |
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 */ | ||
569 | static const int dac_divs[] = { 256, 384, 512, 768, 1024, 1408, 1536 }; | ||
570 | |||
571 | /* Multiply 10 to eliminate decimials */ | ||
572 | static const int bclk_divs[] = { | ||
573 | 10, 15, 20, 30, 40, 55, 60, 80, 110, | ||
574 | 120, 160, 220, 240, 320, 320, 320 | ||
575 | }; | ||
576 | |||
577 | static 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 | |||
566 | static int wm8960_hw_params(struct snd_pcm_substream *substream, | 634 | static 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 | ||
1029 | static 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 | ||
967 | static struct snd_soc_dai_driver wm8960_dai = { | 1068 | static struct snd_soc_dai_driver wm8960_dai = { |