summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/sound/tas2552.txt6
-rw-r--r--sound/soc/codecs/tas2552.c182
-rw-r--r--sound/soc/codecs/tas2552.h61
3 files changed, 158 insertions, 91 deletions
diff --git a/Documentation/devicetree/bindings/sound/tas2552.txt b/Documentation/devicetree/bindings/sound/tas2552.txt
index 55e2a0af5645..c49992c0b62a 100644
--- a/Documentation/devicetree/bindings/sound/tas2552.txt
+++ b/Documentation/devicetree/bindings/sound/tas2552.txt
@@ -14,6 +14,12 @@ Required properties:
 Optional properties:
 	- enable-gpio - gpio pin to enable/disable the device
 
+tas2552 can receive it's reference clock via MCLK, BCLK, IVCLKIN pin or use the
+internal 1.8MHz. This CLKIN is used by the PLL. In addition to PLL, the PDM
+reference clock is also selectable: PLL, IVCLKIN, BCLK or MCLK.
+For system integration the dt-bindings/sound/tas2552.h header file provides
+defined values to selct and configure the PLL and PDM reference clocks.
+
 Example:
 
 tas2552: tas2552@41 {
diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c
index 891e2c529df3..4f25a7d0efa2 100644
--- a/sound/soc/codecs/tas2552.c
+++ b/sound/soc/codecs/tas2552.c
@@ -45,7 +45,7 @@ static struct reg_default tas2552_reg_defs[] = {
 	{TAS2552_OUTPUT_DATA, 0xc0},
 	{TAS2552_PDM_CFG, 0x01},
 	{TAS2552_PGA_GAIN, 0x00},
-	{TAS2552_BOOST_PT_CTRL, 0x0f},
+	{TAS2552_BOOST_APT_CTRL, 0x0f},
 	{TAS2552_RESERVED_0D, 0xbe},
 	{TAS2552_LIMIT_RATE_HYS, 0x08},
 	{TAS2552_CFG_2, 0xef},
@@ -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;
@@ -143,31 +145,105 @@ static const struct snd_soc_dapm_route tas2552_audio_map[] = {
 };
 
 #ifdef CONFIG_PM
-static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown)
+static void tas2552_sw_shutdown(struct tas2552_data *tas2552, int sw_shutdown)
 {
 	u8 cfg1_reg = 0;
 
-	if (!tas_data->codec)
+	if (!tas2552->codec)
 		return;
 
 	if (sw_shutdown)
 		cfg1_reg = TAS2552_SWS;
 
-	snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1, TAS2552_SWS,
+	snd_soc_update_bits(tas2552->codec, TAS2552_CFG_1, TAS2552_SWS,
 			    cfg1_reg);
 }
 #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);
+		/* Will clear the PLL_BYPASS bit */
+		snd_soc_write(codec, TAS2552_PLL_CTRL_2,
+			      TAS2552_PLL_D_UPPER(d));
+		snd_soc_write(codec, TAS2552_PLL_CTRL_3,
+			      TAS2552_PLL_D_LOWER(d));
+	}
+
+	/* 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, wclk_rate;
 
 	switch (params_width(params)) {
@@ -245,49 +321,7 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream,
 	snd_soc_update_bits(codec, TAS2552_CFG_3, TAS2552_WCLK_FREQ_MASK,
 			    wclk_rate);
 
-	if (!tas2552->pll_clkin)
-		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;
+	return tas2552_setup_pll(codec, params);
 }
 
 #define TAS2552_DAI_FMT_MASK	(TAS2552_BCLKDIR | \
@@ -370,12 +404,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:
@@ -385,6 +428,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:
@@ -509,9 +553,20 @@ static struct snd_soc_dai_driver tas2552_dai[] = {
  */
 static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 0);
 
+static const char * const tas2552_din_source_select[] = {
+	"Muted",
+	"Left",
+	"Right",
+	"Left + Right average",
+};
+static SOC_ENUM_SINGLE_DECL(tas2552_din_source_enum,
+			    TAS2552_CFG_3, 3,
+			    tas2552_din_source_select);
+
 static const struct snd_kcontrol_new tas2552_snd_controls[] = {
 	SOC_SINGLE_TLV("Speaker Driver Playback Volume",
 			 TAS2552_PGA_GAIN, 0, 0x1f, 0, dac_tlv),
+	SOC_ENUM("DIN source", tas2552_din_source_enum),
 };
 
 static int tas2552_codec_probe(struct snd_soc_codec *codec)
@@ -543,13 +598,14 @@ 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);
-	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 |
-				TAS2552_APT_THRESH_2_1_7);
+	snd_soc_write(codec, TAS2552_OUTPUT_DATA,
+		      TAS2552_PDM_DATA_SEL_V_I |
+		      TAS2552_R_DATA_OUT(TAS2552_DATA_OUT_V_DATA));
+	snd_soc_write(codec, TAS2552_BOOST_APT_CTRL, TAS2552_APT_DELAY_200 |
+						     TAS2552_APT_THRESH_20_17);
 
-	snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN |
-				  TAS2552_APT_EN | TAS2552_LIM_EN);
+	snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN | TAS2552_APT_EN |
+					    TAS2552_LIM_EN);
 
 	return 0;
 
@@ -647,13 +703,10 @@ static int tas2552_probe(struct i2c_client *client,
 	if (data == NULL)
 		return -ENOMEM;
 
-	data->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
-	if (IS_ERR(data->enable_gpio)) {
-		if (PTR_ERR(data->enable_gpio) == -EPROBE_DEFER)
-			return -EPROBE_DEFER;
-
-		data->enable_gpio = NULL;;
-	}
+	data->enable_gpio = devm_gpiod_get_optional(dev, "enable",
+						    GPIOD_OUT_LOW);
+	if (IS_ERR(data->enable_gpio))
+		return PTR_ERR(data->enable_gpio);
 
 	data->tas2552_client = client;
 	data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config);
@@ -695,6 +748,7 @@ static int tas2552_probe(struct i2c_client *client,
 static int tas2552_i2c_remove(struct i2c_client *client)
 {
 	snd_soc_unregister_codec(&client->dev);
+	pm_runtime_disable(&client->dev);
 	return 0;
 }
 
diff --git a/sound/soc/codecs/tas2552.h b/sound/soc/codecs/tas2552.h
index bbb820495516..5746f8fd0afd 100644
--- a/sound/soc/codecs/tas2552.h
+++ b/sound/soc/codecs/tas2552.h
@@ -19,7 +19,7 @@
 #define __TAS2552_H__
 
 /* Register Address Map */
-#define TAS2552_DEVICE_STATUS	0x00
+#define TAS2552_DEVICE_STATUS		0x00
 #define TAS2552_CFG_1			0x01
 #define TAS2552_CFG_2			0x02
 #define TAS2552_CFG_3			0x03
@@ -33,13 +33,13 @@
 #define TAS2552_BTIP			0x0b
 #define TAS2552_BTS_CTRL		0x0c
 #define TAS2552_RESERVED_0D		0x0d
-#define TAS2552_LIMIT_RATE_HYS	0x0e
-#define TAS2552_LIMIT_RELEASE	0x0f
-#define TAS2552_LIMIT_INT_COUNT	0x10
+#define TAS2552_LIMIT_RATE_HYS		0x0e
+#define TAS2552_LIMIT_RELEASE		0x0f
+#define TAS2552_LIMIT_INT_COUNT		0x10
 #define TAS2552_PDM_CFG			0x11
 #define TAS2552_PGA_GAIN		0x12
-#define TAS2552_EDGE_RATE_CTRL	0x13
-#define TAS2552_BOOST_PT_CTRL	0x14
+#define TAS2552_EDGE_RATE_CTRL		0x13
+#define TAS2552_BOOST_APT_CTRL		0x14
 #define TAS2552_VER_NUM			0x16
 #define TAS2552_VBAT_DATA		0x19
 #define TAS2552_MAX_REG			0x20
@@ -103,10 +103,21 @@
 #define TAS2552_WCLKDIR			(1 << 7)
 
 /* OUTPUT_DATA register */
-#define TAS2552_PDM_DATA_I		0x00
-#define TAS2552_PDM_DATA_V		(1 << 6)
-#define TAS2552_PDM_DATA_I_V	(1 << 7)
-#define TAS2552_PDM_DATA_V_I	(0x11 << 6)
+#define TAS2552_DATA_OUT_I_DATA		(0x0)
+#define TAS2552_DATA_OUT_V_DATA		(0x1)
+#define TAS2552_DATA_OUT_VBAT_DATA	(0x2)
+#define TAS2552_DATA_OUT_VBOOST_DATA	(0x3)
+#define TAS2552_DATA_OUT_PGA_GAIN	(0x4)
+#define TAS2552_DATA_OUT_IV_DATA	(0x5)
+#define TAS2552_DATA_OUT_VBAT_VBOOST_GAIN	(0x6)
+#define TAS2552_DATA_OUT_DISABLED	(0x7)
+#define TAS2552_L_DATA_OUT(x)		((x) << 0)
+#define TAS2552_R_DATA_OUT(x)		((x) << 3)
+#define TAS2552_PDM_DATA_SEL_I		(0x0 << 6)
+#define TAS2552_PDM_DATA_SEL_V		(0x1 << 6)
+#define TAS2552_PDM_DATA_SEL_I_V	(0x2 << 6)
+#define TAS2552_PDM_DATA_SEL_V_I	(0x3 << 6)
+#define TAS2552_PDM_DATA_SEL_MASK	TAS2552_PDM_DATA_SEL_V_I
 
 /* PDM CFG Register */
 #define TAS2552_PDM_CLK_SEL_PLL		(0x0 << 0)
@@ -116,24 +127,20 @@
 #define TAS2552_PDM_CLK_SEL_MASK	TAS2552_PDM_CLK_SEL_MCLK
 #define TAS2552_PDM_DATA_ES	 	(1 << 2)
 
-/* Boost pass-through register */
-#define TAS2552_APT_DELAY_50	0x00
-#define TAS2552_APT_DELAY_75	(1 << 1)
-#define TAS2552_APT_DELAY_125	(1 << 2)
-#define TAS2552_APT_DELAY_200	(1 << 3)
-
-#define TAS2552_APT_THRESH_2_5		0x00
-#define TAS2552_APT_THRESH_1_7		(1 << 3)
-#define TAS2552_APT_THRESH_1_4_1_1	(1 << 4)
-#define TAS2552_APT_THRESH_2_1_7	(0x11 << 2)
+/* Boost Auto-pass through register */
+#define TAS2552_APT_DELAY_50		(0x0 << 0)
+#define TAS2552_APT_DELAY_75		(0x1 << 0)
+#define TAS2552_APT_DELAY_125		(0x2 << 0)
+#define TAS2552_APT_DELAY_200		(0x3 << 0)
+#define TAS2552_APT_THRESH_05_02	(0x0 << 2)
+#define TAS2552_APT_THRESH_10_07	(0x1 << 2)
+#define TAS2552_APT_THRESH_14_11	(0x2 << 2)
+#define TAS2552_APT_THRESH_20_17	(0x3 << 2)
 
 /* PLL Control Register */
-#define TAS2552_245MHZ_CLK			24576000
-#define TAS2552_225MHZ_CLK			22579200
-#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_J_MASK		0x7f
+#define TAS2552_PLL_D_UPPER(x)		(((x) >> 8) & 0x3f)
+#define TAS2552_PLL_D_LOWER(x)		((x) & 0xff)
+#define TAS2552_PLL_BYPASS		(1 << 7)
 
 #endif