[alsa-devel] [PATCH 18/22] ASoC: tas2552: Configure the WCLK frequency based on the stream
Peter Ujfalusi
peter.ujfalusi at ti.com
Thu Jun 4 14:22:42 CEST 2015
Instead of hard wiring the WCLK frequency at probe time do it runtime.
The hard wired 88_96KHz was not even setting the correct bits since it was
defined as (1 << 6) which will change the I2S_OUT_SEL bit and will leave
the amplifier configured for 8KHz.
At the same time clean up and fix the CFG3 register bits.
Signed-off-by: Peter Ujfalusi <peter.ujfalusi at ti.com>
---
sound/soc/codecs/tas2552.c | 178 +++++++++++++++++++++++++++++++++------------
sound/soc/codecs/tas2552.h | 44 +++++------
2 files changed, 152 insertions(+), 70 deletions(-)
diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c
index 13b435f9a9b1..01230395b61d 100644
--- a/sound/soc/codecs/tas2552.c
+++ b/sound/soc/codecs/tas2552.c
@@ -77,7 +77,9 @@ struct tas2552_data {
struct gpio_desc *enable_gpio;
unsigned char regs[TAS2552_VBAT_DATA];
unsigned int pll_clkin;
+ int pll_clk_id;
unsigned int pdm_clk;
+ int pdm_clk_id;
unsigned int dai_fmt;
unsigned int tdm_delay;
@@ -158,17 +160,94 @@ static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown)
}
#endif
+static int tas2552_setup_pll(struct snd_soc_codec *codec,
+ struct snd_pcm_hw_params *params)
+{
+ struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
+ bool bypass_pll = false;
+ unsigned int pll_clk = params_rate(params) * 512;
+ unsigned int pll_clkin = tas2552->pll_clkin;
+ u8 pll_enable;
+
+ if (!pll_clkin) {
+ if (tas2552->pll_clk_id != TAS2552_PLL_CLKIN_BCLK)
+ return -EINVAL;
+
+ pll_clkin = snd_soc_params_to_bclk(params);
+ pll_clkin += tas2552->tdm_delay;
+ }
+
+ pll_enable = snd_soc_read(codec, TAS2552_CFG_2) & TAS2552_PLL_ENABLE;
+ snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
+
+ if (pll_clkin == pll_clk)
+ bypass_pll = true;
+
+ if (bypass_pll) {
+ /* By pass the PLL configuration */
+ snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2,
+ TAS2552_PLL_BYPASS, TAS2552_PLL_BYPASS);
+ } else {
+ /* Fill in the PLL control registers for J & D
+ * pll_clk = (.5 * pll_clkin * J.D) / 2^p
+ * Need to fill in J and D here based on incoming freq
+ */
+ unsigned int d;
+ u8 j;
+ u8 pll_sel = (tas2552->pll_clk_id << 3) & TAS2552_PLL_SRC_MASK;
+ u8 p = snd_soc_read(codec, TAS2552_PLL_CTRL_1);
+
+ p = (p >> 7);
+
+recalc:
+ j = (pll_clk * 2 * (1 << p)) / pll_clkin;
+ d = (pll_clk * 2 * (1 << p)) % pll_clkin;
+ d /= (pll_clkin / 10000);
+
+ if (d && (pll_clkin < 512000 || pll_clkin > 9200000)) {
+ if (tas2552->pll_clk_id == TAS2552_PLL_CLKIN_BCLK) {
+ pll_clkin = 1800000;
+ pll_sel = (TAS2552_PLL_CLKIN_1_8_FIXED << 3) &
+ TAS2552_PLL_SRC_MASK;
+ } else {
+ pll_clkin = snd_soc_params_to_bclk(params);
+ pll_clkin += tas2552->tdm_delay;
+ pll_sel = (TAS2552_PLL_CLKIN_BCLK << 3) &
+ TAS2552_PLL_SRC_MASK;
+ }
+ goto recalc;
+ }
+
+ snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_PLL_SRC_MASK,
+ pll_sel);
+
+ snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1,
+ TAS2552_PLL_J_MASK, j);
+ snd_soc_write(codec, TAS2552_PLL_CTRL_2,
+ (d >> 7) & TAS2552_PLL_D_UPPER_MASK);
+ snd_soc_write(codec, TAS2552_PLL_CTRL_3,
+ d & TAS2552_PLL_D_LOWER_MASK);
+
+ /* PLL in use */
+ snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2,
+ TAS2552_PLL_BYPASS, 0);
+ }
+
+ /* Restore PLL status */
+ snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE,
+ pll_enable);
+
+ return 0;
+}
+
static int tas2552_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
- int sample_rate, pll_clk;
- int d;
int cpf;
- u8 p, j;
- u8 ser_ctrl1_reg;
+ u8 ser_ctrl1_reg, wclk_rate;
switch (params_width(params)) {
case 16:
@@ -206,49 +285,46 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream,
TAS2552_WORDLENGTH_MASK | TAS2552_CLKSPERFRAME_MASK,
ser_ctrl1_reg);
- if (!tas2552->pll_clkin)
+ switch (params_rate(params)) {
+ case 8000:
+ wclk_rate = TAS2552_WCLK_FREQ_8KHZ;
+ break;
+ case 11025:
+ case 12000:
+ wclk_rate = TAS2552_WCLK_FREQ_11_12KHZ;
+ break;
+ case 16000:
+ wclk_rate = TAS2552_WCLK_FREQ_16KHZ;
+ break;
+ case 22050:
+ case 24000:
+ wclk_rate = TAS2552_WCLK_FREQ_22_24KHZ;
+ break;
+ case 32000:
+ wclk_rate = TAS2552_WCLK_FREQ_32KHZ;
+ break;
+ case 44100:
+ case 48000:
+ wclk_rate = TAS2552_WCLK_FREQ_44_48KHZ;
+ break;
+ case 88200:
+ case 96000:
+ wclk_rate = TAS2552_WCLK_FREQ_88_96KHZ;
+ break;
+ case 176400:
+ case 192000:
+ wclk_rate = TAS2552_WCLK_FREQ_176_192KHZ;
+ break;
+ default:
+ dev_err(codec->dev, "Not supported sample rate: %d\n",
+ params_rate(params));
return -EINVAL;
-
- snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
-
- if (tas2552->pll_clkin == TAS2552_245MHZ_CLK ||
- tas2552->pll_clkin == TAS2552_225MHZ_CLK) {
- /* By pass the PLL configuration */
- snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2,
- TAS2552_PLL_BYPASS_MASK,
- TAS2552_PLL_BYPASS);
- } else {
- /* Fill in the PLL control registers for J & D
- * PLL_CLK = (.5 * freq * J.D) / 2^p
- * Need to fill in J and D here based on incoming freq
- */
- p = snd_soc_read(codec, TAS2552_PLL_CTRL_1);
- p = (p >> 7);
- sample_rate = params_rate(params);
-
- if (sample_rate == 48000)
- pll_clk = TAS2552_245MHZ_CLK;
- else if (sample_rate == 44100)
- pll_clk = TAS2552_225MHZ_CLK;
- else {
- dev_vdbg(codec->dev, "Substream sample rate is not found %i\n",
- params_rate(params));
- return -EINVAL;
- }
-
- j = (pll_clk * 2 * (1 << p)) / tas2552->pll_clkin;
- d = (pll_clk * 2 * (1 << p)) % tas2552->pll_clkin;
-
- snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1,
- TAS2552_PLL_J_MASK, j);
- snd_soc_write(codec, TAS2552_PLL_CTRL_2,
- (d >> 7) & TAS2552_PLL_D_UPPER_MASK);
- snd_soc_write(codec, TAS2552_PLL_CTRL_3,
- d & TAS2552_PLL_D_LOWER_MASK);
-
}
- return 0;
+ snd_soc_update_bits(codec, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK,
+ wclk_rate);
+
+ return tas2552_setup_pll(codec, params);
}
#define TAS2552_DAI_FMT_MASK (TAS2552_BCLKDIR | \
@@ -331,12 +407,21 @@ static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
switch (clk_id) {
case TAS2552_PLL_CLKIN_MCLK:
- case TAS2552_PLL_CLKIN_BCLK:
case TAS2552_PLL_CLKIN_IVCLKIN:
+ if (freq < 512000 || freq > 24576000) {
+ /* out of range PLL_CLKIN, fall back to use BCLK */
+ dev_warn(codec->dev, "Out of range PLL_CLKIN: %u\n",
+ freq);
+ clk_id = TAS2552_PLL_CLKIN_BCLK;
+ freq = 0;
+ }
+ /* fall through */
+ case TAS2552_PLL_CLKIN_BCLK:
case TAS2552_PLL_CLKIN_1_8_FIXED:
mask = TAS2552_PLL_SRC_MASK;
val = (clk_id << 3) & mask; /* bit 4:5 in the register */
reg = TAS2552_CFG_1;
+ tas2552->pll_clk_id = clk_id;
tas2552->pll_clkin = freq;
break;
case TAS2552_PDM_CLK_PLL:
@@ -346,6 +431,7 @@ static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
mask = TAS2552_PDM_CLK_SEL_MASK;
val = (clk_id >> 1) & mask; /* bit 0:1 in the register */
reg = TAS2552_PDM_CFG;
+ tas2552->pdm_clk_id = clk_id;
tas2552->pdm_clk = freq;
break;
default:
@@ -503,7 +589,7 @@ static int tas2552_codec_probe(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE, TAS2552_MUTE);
snd_soc_write(codec, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL |
- TAS2552_DIN_SRC_SEL_AVG_L_R | TAS2552_88_96KHZ);
+ TAS2552_DIN_SRC_SEL_AVG_L_R);
snd_soc_write(codec, TAS2552_DOUT, TAS2552_PDM_DATA_I);
snd_soc_write(codec, TAS2552_OUTPUT_DATA, TAS2552_PDM_DATA_V_I | 0x8);
snd_soc_write(codec, TAS2552_BOOST_PT_CTRL, TAS2552_APT_DELAY_200 |
diff --git a/sound/soc/codecs/tas2552.h b/sound/soc/codecs/tas2552.h
index de0ab0d27520..f04d9e6db0aa 100644
--- a/sound/soc/codecs/tas2552.h
+++ b/sound/soc/codecs/tas2552.h
@@ -62,6 +62,24 @@
#define TAS2552_LIM_EN (1 << 2)
#define TAS2552_IVSENSE_EN (1 << 1)
+/* CFG3 Register Masks */
+#define TAS2552_WCLK_FREQ_8KHZ (0x0 << 0)
+#define TAS2552_WCLK_FREQ_11_12KHZ (0x1 << 0)
+#define TAS2552_WCLK_FREQ_16KHZ (0x2 << 0)
+#define TAS2552_WCLK_FREQ_22_24KHZ (0x3 << 0)
+#define TAS2552_WCLK_FREQ_32KHZ (0x4 << 0)
+#define TAS2552_WCLK_FREQ_44_48KHZ (0x5 << 0)
+#define TAS2552_WCLK_FREQ_88_96KHZ (0x6 << 0)
+#define TAS2552_WCLK_FREQ_176_192KHZ (0x7 << 0)
+#define TAS2552_WCLK_FREQ_MASK TAS2552_WCLK_FREQ_176_192KHZ
+#define TAS2552_DIN_SRC_SEL_MUTED (0x0 << 3)
+#define TAS2552_DIN_SRC_SEL_LEFT (0x1 << 3)
+#define TAS2552_DIN_SRC_SEL_RIGHT (0x2 << 3)
+#define TAS2552_DIN_SRC_SEL_AVG_L_R (0x3 << 3)
+#define TAS2552_PDM_IN_SEL (1 << 5)
+#define TAS2552_I2S_OUT_SEL (1 << 6)
+#define TAS2552_ANALOG_IN_SEL (1 << 7)
+
/* DOUT Register Masks */
#define TAS2552_SDOUT_TRISTATE (1 << 2)
@@ -84,25 +102,6 @@
#define TAS2552_BCLKDIR (1 << 6)
#define TAS2552_WCLKDIR (1 << 7)
-#define TAS2552_DIN_SRC_SEL_MUTED 0x00
-#define TAS2552_DIN_SRC_SEL_LEFT (1 << 4)
-#define TAS2552_DIN_SRC_SEL_RIGHT (1 << 5)
-#define TAS2552_DIN_SRC_SEL_AVG_L_R (0x11 << 4)
-
-#define TAS2552_PDM_IN_SEL (1 << 5)
-#define TAS2552_I2S_OUT_SEL (1 << 6)
-#define TAS2552_ANALOG_IN_SEL (1 << 7)
-
-/* CFG3 WCLK Dividers */
-#define TAS2552_8KHZ 0x00
-#define TAS2552_11_12KHZ (1 << 1)
-#define TAS2552_16KHZ (1 << 2)
-#define TAS2552_22_24KHZ (1 << 3)
-#define TAS2552_32KHZ (1 << 4)
-#define TAS2552_44_48KHZ (1 << 5)
-#define TAS2552_88_96KHZ (1 << 6)
-#define TAS2552_176_192KHZ (1 << 7)
-
/* OUTPUT_DATA register */
#define TAS2552_PDM_DATA_I 0x00
#define TAS2552_PDM_DATA_V (1 << 6)
@@ -129,12 +128,9 @@
#define TAS2552_APT_THRESH_2_1_7 (0x11 << 2)
/* PLL Control Register */
-#define TAS2552_245MHZ_CLK 24576000
-#define TAS2552_225MHZ_CLK 22579200
-#define TAS2552_PLL_J_MASK 0x7f
+#define TAS2552_PLL_J_MASK 0x7f
#define TAS2552_PLL_D_UPPER_MASK 0x3f
#define TAS2552_PLL_D_LOWER_MASK 0xff
-#define TAS2552_PLL_BYPASS_MASK 0x80
-#define TAS2552_PLL_BYPASS 0x80
+#define TAS2552_PLL_BYPASS (1 << 7)
#endif
--
2.4.2
More information about the Alsa-devel
mailing list