aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Mack <daniel@caiaq.de>2008-04-30 10:20:19 -0400
committerJaroslav Kysela <perex@perex.cz>2008-05-19 07:19:13 -0400
commit4f9c16ccfa26691dbb9a5d9e7d5098eb934ccdbe (patch)
tree9d1d3124b3b6d9d8dd474d3642b492dc16403113
parentbce7f793daec3e65ec5c5705d2457b81fe7b5725 (diff)
[ALSA] soc - tlv320aic3x - revisit clock setup
This patch cleans up the clocking setup for aic3x codecs. It drops the dividers table and determines the PLL control values programatically. Under certain conditions, the PLL is disabled entirely which could save some power. Signed-off-by: Daniel Mack <daniel@caiaq.de> Acked-by: Jarkko Nikula <jarkko.nikula@nokia.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
-rw-r--r--sound/soc/codecs/tlv320aic3x.c225
-rw-r--r--sound/soc/codecs/tlv320aic3x.h4
2 files changed, 95 insertions, 134 deletions
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 09b1661b8a3a..738b3b634d74 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -49,7 +49,7 @@
49#include "tlv320aic3x.h" 49#include "tlv320aic3x.h"
50 50
51#define AUDIO_NAME "aic3x" 51#define AUDIO_NAME "aic3x"
52#define AIC3X_VERSION "0.1" 52#define AIC3X_VERSION "0.2"
53 53
54/* codec private data */ 54/* codec private data */
55struct aic3x_priv { 55struct aic3x_priv {
@@ -648,81 +648,6 @@ static int aic3x_add_widgets(struct snd_soc_codec *codec)
648 return 0; 648 return 0;
649} 649}
650 650
651struct aic3x_rate_divs {
652 u32 mclk;
653 u32 rate;
654 u32 fsref_reg;
655 u8 sr_reg:4;
656 u8 pllj_reg;
657 u16 plld_reg;
658};
659
660/* AIC3X codec mclk clock divider coefficients */
661static const struct aic3x_rate_divs aic3x_divs[] = {
662 /* 8k */
663 {12000000, 8000, 48000, 0xa, 16, 3840},
664 {19200000, 8000, 48000, 0xa, 10, 2400},
665 {22579200, 8000, 48000, 0xa, 8, 7075},
666 {33868800, 8000, 48000, 0xa, 5, 8049},
667 /* 11.025k */
668 {12000000, 11025, 44100, 0x6, 15, 528},
669 {19200000, 11025, 44100, 0x6, 9, 4080},
670 {22579200, 11025, 44100, 0x6, 8, 0},
671 {33868800, 11025, 44100, 0x6, 5, 3333},
672 /* 16k */
673 {12000000, 16000, 48000, 0x4, 16, 3840},
674 {19200000, 16000, 48000, 0x4, 10, 2400},
675 {22579200, 16000, 48000, 0x4, 8, 7075},
676 {33868800, 16000, 48000, 0x4, 5, 8049},
677 /* 22.05k */
678 {12000000, 22050, 44100, 0x2, 15, 528},
679 {19200000, 22050, 44100, 0x2, 9, 4080},
680 {22579200, 22050, 44100, 0x2, 8, 0},
681 {33868800, 22050, 44100, 0x2, 5, 3333},
682 /* 32k */
683 {12000000, 32000, 48000, 0x1, 16, 3840},
684 {19200000, 32000, 48000, 0x1, 10, 2400},
685 {22579200, 32000, 48000, 0x1, 8, 7075},
686 {33868800, 32000, 48000, 0x1, 5, 8049},
687 /* 44.1k */
688 {12000000, 44100, 44100, 0x0, 15, 528},
689 {19200000, 44100, 44100, 0x0, 9, 4080},
690 {22579200, 44100, 44100, 0x0, 8, 0},
691 {33868800, 44100, 44100, 0x0, 5, 3333},
692 /* 48k */
693 {12000000, 48000, 48000, 0x0, 16, 3840},
694 {19200000, 48000, 48000, 0x0, 10, 2400},
695 {22579200, 48000, 48000, 0x0, 8, 7075},
696 {33868800, 48000, 48000, 0x0, 5, 8049},
697 /* 64k */
698 {12000000, 64000, 96000, 0x1, 16, 3840},
699 {19200000, 64000, 96000, 0x1, 10, 2400},
700 {22579200, 64000, 96000, 0x1, 8, 7075},
701 {33868800, 64000, 96000, 0x1, 5, 8049},
702 /* 88.2k */
703 {12000000, 88200, 88200, 0x0, 15, 528},
704 {19200000, 88200, 88200, 0x0, 9, 4080},
705 {22579200, 88200, 88200, 0x0, 8, 0},
706 {33868800, 88200, 88200, 0x0, 5, 3333},
707 /* 96k */
708 {12000000, 96000, 96000, 0x0, 16, 3840},
709 {19200000, 96000, 96000, 0x0, 10, 2400},
710 {22579200, 96000, 96000, 0x0, 8, 7075},
711 {33868800, 96000, 96000, 0x0, 5, 8049},
712};
713
714static inline int aic3x_get_divs(int mclk, int rate)
715{
716 int i;
717
718 for (i = 0; i < ARRAY_SIZE(aic3x_divs); i++) {
719 if (aic3x_divs[i].rate == rate && aic3x_divs[i].mclk == mclk)
720 return i;
721 }
722
723 return 0;
724}
725
726static int aic3x_hw_params(struct snd_pcm_substream *substream, 651static int aic3x_hw_params(struct snd_pcm_substream *substream,
727 struct snd_pcm_hw_params *params) 652 struct snd_pcm_hw_params *params)
728{ 653{
@@ -730,49 +655,107 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
730 struct snd_soc_device *socdev = rtd->socdev; 655 struct snd_soc_device *socdev = rtd->socdev;
731 struct snd_soc_codec *codec = socdev->codec; 656 struct snd_soc_codec *codec = socdev->codec;
732 struct aic3x_priv *aic3x = codec->private_data; 657 struct aic3x_priv *aic3x = codec->private_data;
733 int i; 658 int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
734 u8 data, pll_p, pll_r, pll_j; 659 u8 data, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
735 u16 pll_d; 660 u16 pll_d = 1;
736
737 i = aic3x_get_divs(aic3x->sysclk, params_rate(params));
738 661
739 /* Route Left DAC to left channel input and 662 /* select data word length */
740 * right DAC to right channel input */ 663 data =
741 data = (LDAC2LCH | RDAC2RCH); 664 aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
742 switch (aic3x_divs[i].fsref_reg) { 665 switch (params_format(params)) {
743 case 44100: 666 case SNDRV_PCM_FORMAT_S16_LE:
744 data |= FSREF_44100;
745 break; 667 break;
746 case 48000: 668 case SNDRV_PCM_FORMAT_S20_3LE:
747 data |= FSREF_48000; 669 data |= (0x01 << 4);
748 break; 670 break;
749 case 88200: 671 case SNDRV_PCM_FORMAT_S24_LE:
750 data |= FSREF_44100 | DUAL_RATE_MODE; 672 data |= (0x02 << 4);
751 break; 673 break;
752 case 96000: 674 case SNDRV_PCM_FORMAT_S32_LE:
753 data |= FSREF_48000 | DUAL_RATE_MODE; 675 data |= (0x03 << 4);
754 break; 676 break;
755 } 677 }
678 aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, data);
679
680 /* Fsref can be 44100 or 48000 */
681 fsref = (params_rate(params) % 11025 == 0) ? 44100 : 48000;
682
683 /* Try to find a value for Q which allows us to bypass the PLL and
684 * generate CODEC_CLK directly. */
685 for (pll_q = 2; pll_q < 18; pll_q++)
686 if (aic3x->sysclk / (128 * pll_q) == fsref) {
687 bypass_pll = 1;
688 break;
689 }
690
691 if (bypass_pll) {
692 pll_q &= 0xf;
693 aic3x_write(codec, AIC3X_PLL_PROGA_REG, pll_q << PLLQ_SHIFT);
694 aic3x_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_CLKDIV);
695 } else
696 aic3x_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_PLLDIV);
697
698 /* Route Left DAC to left channel input and
699 * right DAC to right channel input */
700 data = (LDAC2LCH | RDAC2RCH);
701 data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000;
702 if (params_rate(params) >= 64000)
703 data |= DUAL_RATE_MODE;
756 aic3x_write(codec, AIC3X_CODEC_DATAPATH_REG, data); 704 aic3x_write(codec, AIC3X_CODEC_DATAPATH_REG, data);
757 705
758 /* codec sample rate select */ 706 /* codec sample rate select */
759 data = aic3x_divs[i].sr_reg; 707 data = (fsref * 20) / params_rate(params);
708 if (params_rate(params) < 64000)
709 data /= 2;
710 data /= 5;
711 data -= 2;
760 data |= (data << 4); 712 data |= (data << 4);
761 aic3x_write(codec, AIC3X_SAMPLE_RATE_SEL_REG, data); 713 aic3x_write(codec, AIC3X_SAMPLE_RATE_SEL_REG, data);
762 714
763 /* Use PLL for generation Fsref by equation: 715 if (bypass_pll)
764 * Fsref = (MCLK * K * R)/(2048 * P); 716 return 0;
765 * Fix P = 2 and R = 1 and calculate K, if 717
766 * K = J.D, i.e. J - an interger portion of K and D is the fractional 718 /* Use PLL
767 * one with 4 digits of precision; 719 * find an apropriate setup for j, d, r and p by iterating over
768 * Example: 720 * p and r - j and d are calculated for each fraction.
769 * For MCLK = 22.5792 MHz and Fsref = 48kHz: 721 * Up to 128 values are probed, the closest one wins the game.
770 * Select P = 2, R= 1, K = 8.7074, which results in J = 8, D = 7074 722 * The sysclk is divided by 1000 to prevent integer overflows.
771 */ 723 */
772 pll_p = 2; 724 codec_clk = (2048 * fsref) / (aic3x->sysclk / 1000);
773 pll_r = 1; 725
774 pll_j = aic3x_divs[i].pllj_reg; 726 for (r = 1; r <= 16; r++)
775 pll_d = aic3x_divs[i].plld_reg; 727 for (p = 1; p <= 8; p++) {
728 int clk, tmp = (codec_clk * pll_r * 10) / pll_p;
729 u8 j = tmp / 10000;
730 u16 d = tmp % 10000;
731
732 if (j > 63)
733 continue;
734
735 if (d != 0 && aic3x->sysclk < 10000000)
736 continue;
737
738 /* This is actually 1000 * ((j + (d/10000)) * r) / p
739 * The term had to be converted to get rid of the
740 * division by 10000 */
741 clk = ((10000 * j * r) + (d * r)) / (10 * p);
742
743 /* check whether this values get closer than the best
744 * ones we had before */
745 if (abs(codec_clk - clk) < abs(codec_clk - last_clk)) {
746 pll_j = j; pll_d = d; pll_r = r; pll_p = p;
747 last_clk = clk;
748 }
749
750 /* Early exit for exact matches */
751 if (clk == codec_clk)
752 break;
753 }
754
755 if (last_clk == 0) {
756 printk(KERN_ERR "%s(): unable to setup PLL\n", __func__);
757 return -EINVAL;
758 }
776 759
777 data = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG); 760 data = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
778 aic3x_write(codec, AIC3X_PLL_PROGA_REG, data | (pll_p << PLLP_SHIFT)); 761 aic3x_write(codec, AIC3X_PLL_PROGA_REG, data | (pll_p << PLLP_SHIFT));
@@ -782,24 +765,6 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
782 aic3x_write(codec, AIC3X_PLL_PROGD_REG, 765 aic3x_write(codec, AIC3X_PLL_PROGD_REG,
783 (pll_d & 0x3F) << PLLD_LSB_SHIFT); 766 (pll_d & 0x3F) << PLLD_LSB_SHIFT);
784 767
785 /* select data word length */
786 data =
787 aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
788 switch (params_format(params)) {
789 case SNDRV_PCM_FORMAT_S16_LE:
790 break;
791 case SNDRV_PCM_FORMAT_S20_3LE:
792 data |= (0x01 << 4);
793 break;
794 case SNDRV_PCM_FORMAT_S24_LE:
795 data |= (0x02 << 4);
796 break;
797 case SNDRV_PCM_FORMAT_S32_LE:
798 data |= (0x03 << 4);
799 break;
800 }
801 aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, data);
802
803 return 0; 768 return 0;
804} 769}
805 770
@@ -826,16 +791,8 @@ static int aic3x_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
826 struct snd_soc_codec *codec = codec_dai->codec; 791 struct snd_soc_codec *codec = codec_dai->codec;
827 struct aic3x_priv *aic3x = codec->private_data; 792 struct aic3x_priv *aic3x = codec->private_data;
828 793
829 switch (freq) { 794 aic3x->sysclk = freq;
830 case 12000000: 795 return 0;
831 case 19200000:
832 case 22579200:
833 case 33868800:
834 aic3x->sysclk = freq;
835 return 0;
836 }
837
838 return -EINVAL;
839} 796}
840 797
841static int aic3x_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, 798static int aic3x_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h
index d0cdeeb629de..d49d001e6e4c 100644
--- a/sound/soc/codecs/tlv320aic3x.h
+++ b/sound/soc/codecs/tlv320aic3x.h
@@ -109,6 +109,7 @@
109#define LLOPM_CTRL 86 109#define LLOPM_CTRL 86
110#define RLOPM_CTRL 93 110#define RLOPM_CTRL 93
111/* Clock generation control register */ 111/* Clock generation control register */
112#define AIC3X_GPIOB_REG 101
112#define AIC3X_CLKGEN_CTRL_REG 102 113#define AIC3X_CLKGEN_CTRL_REG 102
113 114
114/* Page select register bits */ 115/* Page select register bits */
@@ -128,12 +129,15 @@
128 129
129/* PLL registers bitfields */ 130/* PLL registers bitfields */
130#define PLLP_SHIFT 0 131#define PLLP_SHIFT 0
132#define PLLQ_SHIFT 3
131#define PLLR_SHIFT 0 133#define PLLR_SHIFT 0
132#define PLLJ_SHIFT 2 134#define PLLJ_SHIFT 2
133#define PLLD_MSB_SHIFT 0 135#define PLLD_MSB_SHIFT 0
134#define PLLD_LSB_SHIFT 2 136#define PLLD_LSB_SHIFT 2
135 137
136/* Clock generation register bits */ 138/* Clock generation register bits */
139#define CODEC_CLKIN_PLLDIV 0
140#define CODEC_CLKIN_CLKDIV 1
137#define PLL_CLKIN_SHIFT 4 141#define PLL_CLKIN_SHIFT 4
138#define MCLK_SOURCE 0x0 142#define MCLK_SOURCE 0x0
139#define PLL_CLKDIV_SHIFT 0 143#define PLL_CLKDIV_SHIFT 0