linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 1/2] dt-bindings: tas2764: Add the TAS2764 binding doc
@ 2020-10-07 15:53 Dan Murphy
  2020-10-07 15:53 ` [PATCH v3 2/2] ASoC: tas2764: Add the driver for the TAS2764 Dan Murphy
  2020-10-08 22:01 ` [PATCH v3 1/2] dt-bindings: tas2764: Add the TAS2764 binding doc Mark Brown
  0 siblings, 2 replies; 3+ messages in thread
From: Dan Murphy @ 2020-10-07 15:53 UTC (permalink / raw)
  To: lgirdwood, broonie, tiwai, robh+dt
  Cc: devicetree, alsa-devel, linux-kernel, Dan Murphy

Add the binding for the TAS2764 Smart Amplifier.

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
 .../devicetree/bindings/sound/tas2764.yaml    | 76 +++++++++++++++++++
 1 file changed, 76 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/tas2764.yaml

diff --git a/Documentation/devicetree/bindings/sound/tas2764.yaml b/Documentation/devicetree/bindings/sound/tas2764.yaml
new file mode 100644
index 000000000000..5bf8c76ecda1
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/tas2764.yaml
@@ -0,0 +1,76 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2020 Texas Instruments Incorporated
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/sound/tas2764.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Texas Instruments TAS2764 Smart PA
+
+maintainers:
+  - Dan Murphy <dmurphy@ti.com>
+
+description: |
+  The TAS2764 is a mono, digital input Class-D audio amplifier optimized for
+  efficiently driving high peak power into small loudspeakers.
+  Integrated speaker voltage and current sense provides for
+  real time monitoring of loudspeaker behavior.
+
+properties:
+  compatible:
+    enum:
+      - ti,tas2764
+
+  reg:
+    maxItems: 1
+    description: |
+       I2C address of the device can be between 0x38 to 0x45.
+
+  reset-gpios:
+    maxItems: 1
+    description: GPIO used to reset the device.
+
+  shutdown-gpios:
+    maxItems: 1
+    description: GPIO used to control the state of the device.
+
+  interrupts:
+    maxItems: 1
+
+  ti,imon-slot-no:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: TDM TX current sense time slot.
+
+  ti,vmon-slot-no:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: TDM TX voltage sense time slot.
+
+  '#sound-dai-cells':
+    const: 1
+
+required:
+  - compatible
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+   #include <dt-bindings/gpio/gpio.h>
+   i2c0 {
+     #address-cells = <1>;
+     #size-cells = <0>;
+     codec: codec@38 {
+       compatible = "ti,tas2764";
+       reg = <0x38>;
+       #sound-dai-cells = <1>;
+       interrupt-parent = <&gpio1>;
+       interrupts = <14>;
+       reset-gpios = <&gpio1 15 0>;
+       shutdown-gpios = <&gpio1 15 0>;
+       ti,imon-slot-no = <0>;
+       ti,vmon-slot-no = <2>;
+     };
+   };
+
+...
-- 
2.28.0.585.ge1cfff676549


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH v3 2/2] ASoC: tas2764: Add the driver for the TAS2764
  2020-10-07 15:53 [PATCH v3 1/2] dt-bindings: tas2764: Add the TAS2764 binding doc Dan Murphy
@ 2020-10-07 15:53 ` Dan Murphy
  2020-10-08 22:01 ` [PATCH v3 1/2] dt-bindings: tas2764: Add the TAS2764 binding doc Mark Brown
  1 sibling, 0 replies; 3+ messages in thread
From: Dan Murphy @ 2020-10-07 15:53 UTC (permalink / raw)
  To: lgirdwood, broonie, tiwai, robh+dt
  Cc: devicetree, alsa-devel, linux-kernel, Dan Murphy

Introduce the Texas Instruments TAS2764 amplifier driver
with I/V sense for loud speaker applications.

Signed-off-by: Dan Murphy <dmurphy@ti.com>
---
 sound/soc/codecs/Kconfig   |   5 +
 sound/soc/codecs/Makefile  |   2 +
 sound/soc/codecs/tas2764.c | 688 +++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/tas2764.h |  92 +++++
 4 files changed, 787 insertions(+)
 create mode 100644 sound/soc/codecs/tas2764.c
 create mode 100644 sound/soc/codecs/tas2764.h

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index a62e0fb467d9..34c6dd04b85a 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -195,6 +195,7 @@ config SND_SOC_ALL_CODECS
 	imply SND_SOC_STI_SAS
 	imply SND_SOC_TAS2552
 	imply SND_SOC_TAS2562
+	imply SND_SOC_TAS2764
 	imply SND_SOC_TAS2770
 	imply SND_SOC_TAS5086
 	imply SND_SOC_TAS571X
@@ -1303,6 +1304,10 @@ config SND_SOC_TAS2562
 	tristate "Texas Instruments TAS2562 Mono Audio amplifier"
 	depends on I2C
 
+config SND_SOC_TAS2764
+	tristate "Texas Instruments TAS2764 Mono Audio amplifier"
+	depends on I2C
+
 config SND_SOC_TAS2770
 	tristate "Texas Instruments TAS2770 speaker amplifier"
 	depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 0404bc1ddcfb..11ce98c25d6c 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -304,6 +304,7 @@ snd-soc-simple-amplifier-objs := simple-amplifier.o
 snd-soc-tpa6130a2-objs := tpa6130a2.o
 snd-soc-tas2552-objs := tas2552.o
 snd-soc-tas2562-objs := tas2562.o
+snd-soc-tas2764-objs := tas2764.o
 
 obj-$(CONFIG_SND_SOC_88PM860X)	+= snd-soc-88pm860x.o
 obj-$(CONFIG_SND_SOC_AB8500_CODEC)	+= snd-soc-ab8500-codec.o
@@ -517,6 +518,7 @@ obj-$(CONFIG_SND_SOC_STAC9766)	+= snd-soc-stac9766.o
 obj-$(CONFIG_SND_SOC_STI_SAS)	+= snd-soc-sti-sas.o
 obj-$(CONFIG_SND_SOC_TAS2552)	+= snd-soc-tas2552.o
 obj-$(CONFIG_SND_SOC_TAS2562)	+= snd-soc-tas2562.o
+obj-$(CONFIG_SND_SOC_TAS2764)	+= snd-soc-tas2764.o
 obj-$(CONFIG_SND_SOC_TAS5086)	+= snd-soc-tas5086.o
 obj-$(CONFIG_SND_SOC_TAS571X)	+= snd-soc-tas571x.o
 obj-$(CONFIG_SND_SOC_TAS5720)	+= snd-soc-tas5720.o
diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c
new file mode 100644
index 000000000000..14a193e48dc7
--- /dev/null
+++ b/sound/soc/codecs/tas2764.c
@@ -0,0 +1,688 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for the Texas Instruments TAS2764 CODEC
+// Copyright (C) 2020 Texas Instruments Inc.
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "tas2764.h"
+
+struct tas2764_priv {
+	struct snd_soc_component *component;
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *sdz_gpio;
+	struct regmap *regmap;
+	struct device *dev;
+	
+	int v_sense_slot;
+	int i_sense_slot;
+};
+
+static void tas2764_reset(struct tas2764_priv *tas2764)
+{
+	if (tas2764->reset_gpio) {
+		gpiod_set_value_cansleep(tas2764->reset_gpio, 0);
+		msleep(20);
+		gpiod_set_value_cansleep(tas2764->reset_gpio, 1);
+	}
+
+	snd_soc_component_write(tas2764->component, TAS2764_SW_RST,
+				TAS2764_RST);
+}
+
+static int tas2764_set_bias_level(struct snd_soc_component *component,
+				 enum snd_soc_bias_level level)
+{
+	struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
+					      TAS2764_PWR_CTRL_MASK,
+					      TAS2764_PWR_CTRL_ACTIVE);
+		break;
+	case SND_SOC_BIAS_STANDBY:
+	case SND_SOC_BIAS_PREPARE:
+		snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
+					      TAS2764_PWR_CTRL_MASK,
+					      TAS2764_PWR_CTRL_MUTE);
+		break;
+	case SND_SOC_BIAS_OFF:
+		snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
+					      TAS2764_PWR_CTRL_MASK,
+					      TAS2764_PWR_CTRL_SHUTDOWN);
+		break;
+
+	default:
+		dev_err(tas2764->dev,
+				"wrong power level setting %d\n", level);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int tas2764_codec_suspend(struct snd_soc_component *component)
+{
+	struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
+	int ret;
+
+	ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
+					    TAS2764_PWR_CTRL_MASK,
+					    TAS2764_PWR_CTRL_SHUTDOWN);
+
+	if (ret < 0)
+		return ret;
+
+	if (tas2764->sdz_gpio)
+		gpiod_set_value_cansleep(tas2764->sdz_gpio, 0);
+
+	regcache_cache_only(tas2764->regmap, true);
+	regcache_mark_dirty(tas2764->regmap);
+
+	return 0;
+}
+
+static int tas2764_codec_resume(struct snd_soc_component *component)
+{
+	struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
+	int ret;
+
+	if (tas2764->sdz_gpio)
+		gpiod_set_value_cansleep(tas2764->sdz_gpio, 1);
+
+	ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
+					    TAS2764_PWR_CTRL_MASK,
+					    TAS2764_PWR_CTRL_ACTIVE);
+
+	if (ret < 0)
+		return ret;
+
+	regcache_cache_only(tas2764->regmap, false);
+
+	return regcache_sync(tas2764->regmap);
+}
+#else
+#define tas2764_codec_suspend NULL
+#define tas2764_codec_resume NULL
+#endif
+
+static const char * const tas2764_ASI1_src[] = {
+	"I2C offset", "Left", "Right", "LeftRightDiv2",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+	tas2764_ASI1_src_enum, TAS2764_TDM_CFG2, 4, tas2764_ASI1_src);
+
+static const struct snd_kcontrol_new tas2764_asi1_mux =
+	SOC_DAPM_ENUM("ASI1 Source", tas2764_ASI1_src_enum);
+
+static int tas2764_dac_event(struct snd_soc_dapm_widget *w,
+			     struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+	struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
+	int ret;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
+						    TAS2764_PWR_CTRL_MASK,
+						    TAS2764_PWR_CTRL_MUTE);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
+						    TAS2764_PWR_CTRL_MASK,
+						    TAS2764_PWR_CTRL_SHUTDOWN);
+		break;
+	default:
+		dev_err(tas2764->dev, "Unsupported event\n");
+		return -EINVAL;
+	}
+
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new isense_switch =
+	SOC_DAPM_SINGLE("Switch", TAS2764_PWR_CTRL, TAS2764_ISENSE_POWER_EN, 1, 1);
+static const struct snd_kcontrol_new vsense_switch =
+	SOC_DAPM_SINGLE("Switch", TAS2764_PWR_CTRL, TAS2764_VSENSE_POWER_EN, 1, 1);
+
+static const struct snd_soc_dapm_widget tas2764_dapm_widgets[] = {
+	SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2764_asi1_mux),
+	SND_SOC_DAPM_SWITCH("ISENSE", TAS2764_PWR_CTRL, TAS2764_ISENSE_POWER_EN,
+			    1, &isense_switch),
+	SND_SOC_DAPM_SWITCH("VSENSE", TAS2764_PWR_CTRL, TAS2764_VSENSE_POWER_EN,
+			    1, &vsense_switch),
+	SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2764_dac_event,
+			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_OUTPUT("OUT"),
+	SND_SOC_DAPM_SIGGEN("VMON"),
+	SND_SOC_DAPM_SIGGEN("IMON")
+};
+
+static const struct snd_soc_dapm_route tas2764_audio_map[] = {
+	{"ASI1 Sel", "I2C offset", "ASI1"},
+	{"ASI1 Sel", "Left", "ASI1"},
+	{"ASI1 Sel", "Right", "ASI1"},
+	{"ASI1 Sel", "LeftRightDiv2", "ASI1"},
+	{"DAC", NULL, "ASI1 Sel"},
+	{"OUT", NULL, "DAC"},
+	{"ISENSE", "Switch", "IMON"},
+	{"VSENSE", "Switch", "VMON"},
+};
+
+static int tas2764_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+	struct snd_soc_component *component = dai->component;
+	int ret;
+
+	ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
+					    TAS2764_PWR_CTRL_MASK,
+					    mute ? TAS2764_PWR_CTRL_MUTE : 0);
+
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int tas2764_set_bitwidth(struct tas2764_priv *tas2764, int bitwidth)
+{
+	struct snd_soc_component *component = tas2764->component;
+	int sense_en;
+	int val;
+	int ret;
+
+	switch (bitwidth) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		ret = snd_soc_component_update_bits(component,
+						    TAS2764_TDM_CFG2,
+						    TAS2764_TDM_CFG2_RXW_MASK,
+						    TAS2764_TDM_CFG2_RXW_16BITS);
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		ret = snd_soc_component_update_bits(component,
+						    TAS2764_TDM_CFG2,
+						    TAS2764_TDM_CFG2_RXW_MASK,
+						    TAS2764_TDM_CFG2_RXW_24BITS);
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		ret = snd_soc_component_update_bits(component,
+						    TAS2764_TDM_CFG2,
+						    TAS2764_TDM_CFG2_RXW_MASK,
+						    TAS2764_TDM_CFG2_RXW_32BITS);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	if (ret < 0)
+		return ret;
+
+	val = snd_soc_component_read(tas2764->component, TAS2764_PWR_CTRL);
+	if (val < 0)
+		return val;
+
+	if (val & (1 << TAS2764_VSENSE_POWER_EN))
+		sense_en = 0;
+	else
+		sense_en = TAS2764_TDM_CFG5_VSNS_ENABLE;
+
+	ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG5,
+					    TAS2764_TDM_CFG5_VSNS_ENABLE,
+					    sense_en);
+	if (ret < 0)
+		return ret;
+
+	if (val & (1 << TAS2764_ISENSE_POWER_EN))
+		sense_en = 0;
+	else
+		sense_en = TAS2764_TDM_CFG6_ISNS_ENABLE;
+
+	ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG6,
+					    TAS2764_TDM_CFG6_ISNS_ENABLE,
+					    sense_en);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int tas2764_set_samplerate(struct tas2764_priv *tas2764, int samplerate)
+{
+	struct snd_soc_component *component = tas2764->component;
+	int ramp_rate_val;
+	int ret;
+
+	switch (samplerate) {
+	case 48000:
+		ramp_rate_val = TAS2764_TDM_CFG0_SMP_48KHZ |
+				TAS2764_TDM_CFG0_44_1_48KHZ;
+		break;
+	case 44100:
+		ramp_rate_val = TAS2764_TDM_CFG0_SMP_44_1KHZ |
+				TAS2764_TDM_CFG0_44_1_48KHZ;
+		break;
+	case 96000:
+		ramp_rate_val = TAS2764_TDM_CFG0_SMP_48KHZ |
+				TAS2764_TDM_CFG0_88_2_96KHZ;
+		break;
+	case 88200:
+		ramp_rate_val = TAS2764_TDM_CFG0_SMP_44_1KHZ |
+				TAS2764_TDM_CFG0_88_2_96KHZ;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG0,
+					    TAS2764_TDM_CFG0_SMP_MASK |
+					    TAS2764_TDM_CFG0_MASK,
+					    ramp_rate_val);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int tas2764_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params,
+			     struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
+	int ret;
+
+	ret = tas2764_set_bitwidth(tas2764, params_format(params));
+	if (ret < 0)
+		return ret;
+
+	return tas2764_set_samplerate(tas2764, params_rate(params));
+}
+
+static int tas2764_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_component *component = dai->component;
+	struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
+	u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0;
+	int iface;
+	int ret;
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		asi_cfg_1 = TAS2764_TDM_CFG1_RX_RISING;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		asi_cfg_1 = TAS2764_TDM_CFG1_RX_FALLING;
+		break;
+	default:
+		dev_err(tas2764->dev, "ASI format Inverse is not found\n");
+		return -EINVAL;
+	}
+
+	ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG1,
+					    TAS2764_TDM_CFG1_RX_MASK,
+					    asi_cfg_1);
+	if (ret < 0)
+		return ret;
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+	case SND_SOC_DAIFMT_DSP_A:
+		iface = TAS2764_TDM_CFG2_SCFG_I2S;
+		tdm_rx_start_slot = 1;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+	case SND_SOC_DAIFMT_LEFT_J:
+		iface = TAS2764_TDM_CFG2_SCFG_LEFT_J;
+		tdm_rx_start_slot = 0;
+		break;
+	default:
+		dev_err(tas2764->dev,
+			"DAI Format is not found, fmt=0x%x\n", fmt);
+		return -EINVAL;
+	}
+
+	ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG1,
+					    TAS2764_TDM_CFG1_MASK,
+					    (tdm_rx_start_slot << TAS2764_TDM_CFG1_51_SHIFT));
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG2,
+					    TAS2764_TDM_CFG2_SCFG_MASK, iface);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int tas2764_set_dai_tdm_slot(struct snd_soc_dai *dai,
+				unsigned int tx_mask,
+				unsigned int rx_mask,
+				int slots, int slot_width)
+{
+	struct snd_soc_component *component = dai->component;
+	struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
+	int left_slot, right_slot;
+	int slots_cfg;
+	int slot_size;
+	int ret;
+
+	if (tx_mask == 0 || rx_mask != 0)
+		return -EINVAL;
+
+	if (slots == 1) {
+		if (tx_mask != 1)
+			return -EINVAL;
+		left_slot = 0;
+		right_slot = 0;
+	} else {
+		left_slot = __ffs(tx_mask);
+		tx_mask &= ~(1 << left_slot);
+		if (tx_mask == 0) {
+			right_slot = left_slot;
+		} else {
+			right_slot = __ffs(tx_mask);
+			tx_mask &= ~(1 << right_slot);
+		}
+	}
+
+	if (tx_mask != 0 || left_slot >= slots || right_slot >= slots)
+		return -EINVAL;
+
+	slots_cfg = (right_slot << TAS2764_TDM_CFG3_RXS_SHIFT) | left_slot;
+
+	ret = snd_soc_component_write(component, TAS2764_TDM_CFG3, slots_cfg);
+	if (ret)
+		return ret;
+
+	switch (slot_width) {
+	case 16:
+		slot_size = TAS2764_TDM_CFG2_RXS_16BITS;
+		break;
+	case 24:
+		slot_size = TAS2764_TDM_CFG2_RXS_24BITS;
+		break;
+	case 32:
+		slot_size = TAS2764_TDM_CFG2_RXS_32BITS;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG2,
+					    TAS2764_TDM_CFG2_RXS_MASK,
+					    slot_size);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG5,
+					    TAS2764_TDM_CFG5_50_MASK,
+					    tas2764->v_sense_slot);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG6,
+					    TAS2764_TDM_CFG6_50_MASK,
+					    tas2764->i_sense_slot);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops tas2764_dai_ops = {
+	.mute_stream = tas2764_mute,
+	.hw_params  = tas2764_hw_params,
+	.set_fmt    = tas2764_set_fmt,
+	.set_tdm_slot = tas2764_set_dai_tdm_slot,
+	.no_capture_mute = 1,
+};
+
+#define TAS2764_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+			 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define TAS2764_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+		       SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_88200)
+
+static struct snd_soc_dai_driver tas2764_dai_driver[] = {
+	{
+		.name = "tas2764 ASI1",
+		.id = 0,
+		.playback = {
+			.stream_name    = "ASI1 Playback",
+			.channels_min   = 2,
+			.channels_max   = 2,
+			.rates      = TAS2764_RATES,
+			.formats    = TAS2764_FORMATS,
+		},
+		.capture = {
+			.stream_name    = "ASI1 Capture",
+			.channels_min   = 0,
+			.channels_max   = 2,
+			.rates = TAS2764_RATES,
+			.formats = TAS2764_FORMATS,
+		},
+		.ops = &tas2764_dai_ops,
+		.symmetric_rates = 1,
+	},
+};
+
+static int tas2764_codec_probe(struct snd_soc_component *component)
+{
+	struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
+	int ret;
+
+	tas2764->component = component;
+
+	if (tas2764->sdz_gpio)
+		gpiod_set_value_cansleep(tas2764->sdz_gpio, 1);
+
+	tas2764_reset(tas2764);
+
+	ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG5,
+					    TAS2764_TDM_CFG5_VSNS_ENABLE, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG6,
+					    TAS2764_TDM_CFG6_ISNS_ENABLE, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
+					    TAS2764_PWR_CTRL_MASK,
+					    TAS2764_PWR_CTRL_MUTE);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(tas2764_digital_tlv, 1100, 50, 0);
+static DECLARE_TLV_DB_SCALE(tas2764_playback_volume, -10000, 50, 0);
+
+static const struct snd_kcontrol_new tas2764_snd_controls[] = {
+	SOC_SINGLE_TLV("Speaker Volume", TAS2764_DVC, 0,
+		       TAS2764_DVC_MAX, 1, tas2764_playback_volume),
+	SOC_SINGLE_TLV("Amp Gain Volume", TAS2764_CHNL_0, 0, 0x14, 0,
+		       tas2764_digital_tlv),
+};
+
+static const struct snd_soc_component_driver soc_component_driver_tas2764 = {
+	.probe			= tas2764_codec_probe,
+	.suspend		= tas2764_codec_suspend,
+	.resume			= tas2764_codec_resume,
+	.set_bias_level		= tas2764_set_bias_level,
+	.controls		= tas2764_snd_controls,
+	.num_controls		= ARRAY_SIZE(tas2764_snd_controls),
+	.dapm_widgets		= tas2764_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(tas2764_dapm_widgets),
+	.dapm_routes		= tas2764_audio_map,
+	.num_dapm_routes	= ARRAY_SIZE(tas2764_audio_map),
+	.idle_bias_on		= 1,
+	.endianness		= 1,
+	.non_legacy_dai_naming	= 1,
+};
+
+static const struct reg_default tas2764_reg_defaults[] = {
+	{ TAS2764_PAGE, 0x00 },
+	{ TAS2764_SW_RST, 0x00 },
+	{ TAS2764_PWR_CTRL, 0x1a },
+	{ TAS2764_DVC, 0x00 },
+	{ TAS2764_CHNL_0, 0x00 },
+	{ TAS2764_TDM_CFG0, 0x09 },
+	{ TAS2764_TDM_CFG1, 0x02 },
+	{ TAS2764_TDM_CFG2, 0x0a },
+	{ TAS2764_TDM_CFG3, 0x10 },
+	{ TAS2764_TDM_CFG5, 0x42 },
+};
+
+static const struct regmap_range_cfg tas2764_regmap_ranges[] = {
+	{
+		.range_min = 0,
+		.range_max = 1 * 128,
+		.selector_reg = TAS2764_PAGE,
+		.selector_mask = 0xff,
+		.selector_shift = 0,
+		.window_start = 0,
+		.window_len = 128,
+	},
+};
+
+static const struct regmap_config tas2764_i2c_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.reg_defaults = tas2764_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(tas2764_reg_defaults),
+	.cache_type = REGCACHE_RBTREE,
+	.ranges = tas2764_regmap_ranges,
+	.num_ranges = ARRAY_SIZE(tas2764_regmap_ranges),
+	.max_register = 1 * 128,
+};
+
+static int tas2764_parse_dt(struct device *dev, struct tas2764_priv *tas2764)
+{
+	int ret = 0;
+
+	tas2764->reset_gpio = devm_gpiod_get_optional(tas2764->dev, "reset",
+						      GPIOD_OUT_HIGH);
+	if (IS_ERR(tas2764->reset_gpio)) {
+		if (PTR_ERR(tas2764->reset_gpio) == -EPROBE_DEFER) {
+			tas2764->reset_gpio = NULL;
+			return -EPROBE_DEFER;
+		}
+	}
+
+	tas2764->sdz_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH);
+	if (IS_ERR(tas2764->sdz_gpio)) {
+		if (PTR_ERR(tas2764->sdz_gpio) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+
+		tas2764->sdz_gpio = NULL;
+	}
+
+	ret = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no",
+				       &tas2764->i_sense_slot);
+	if (ret)
+		tas2764->i_sense_slot = 0;
+
+	ret = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no",
+				       &tas2764->v_sense_slot);
+	if (ret)
+		tas2764->v_sense_slot = 2;
+
+	return 0;
+}
+
+static int tas2764_i2c_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct tas2764_priv *tas2764;
+	int result;
+
+	tas2764 = devm_kzalloc(&client->dev, sizeof(struct tas2764_priv),
+			       GFP_KERNEL);
+	if (!tas2764)
+		return -ENOMEM;
+
+	tas2764->dev = &client->dev;
+	i2c_set_clientdata(client, tas2764);
+	dev_set_drvdata(&client->dev, tas2764);
+
+	tas2764->regmap = devm_regmap_init_i2c(client, &tas2764_i2c_regmap);
+	if (IS_ERR(tas2764->regmap)) {
+		result = PTR_ERR(tas2764->regmap);
+		dev_err(&client->dev, "Failed to allocate register map: %d\n",
+					result);
+		return result;
+	}
+
+	if (client->dev.of_node) {
+		result = tas2764_parse_dt(&client->dev, tas2764);
+		if (result) {
+			dev_err(tas2764->dev, "%s: Failed to parse devicetree\n",
+				__func__);
+			return result;
+		}
+	}
+
+	return devm_snd_soc_register_component(tas2764->dev,
+					       &soc_component_driver_tas2764,
+					       tas2764_dai_driver,
+					       ARRAY_SIZE(tas2764_dai_driver));
+}
+
+static const struct i2c_device_id tas2764_i2c_id[] = {
+	{ "tas2764", 0},
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, tas2764_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id tas2764_of_match[] = {
+	{ .compatible = "ti,tas2764" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, tas2764_of_match);
+#endif
+
+static struct i2c_driver tas2764_i2c_driver = {
+	.driver = {
+		.name   = "tas2764",
+		.of_match_table = of_match_ptr(tas2764_of_match),
+	},
+	.probe      = tas2764_i2c_probe,
+	.id_table   = tas2764_i2c_id,
+};
+module_i2c_driver(tas2764_i2c_driver);
+
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
+MODULE_DESCRIPTION("TAS2764 I2C Smart Amplifier driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/tas2764.h b/sound/soc/codecs/tas2764.h
new file mode 100644
index 000000000000..67d6fd903c42
--- /dev/null
+++ b/sound/soc/codecs/tas2764.h
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * tas2764.h - ALSA SoC Texas Instruments TAS2764 Mono Audio Amplifier
+ *
+ * Copyright (C) 2020 Texas Instruments Incorporated -  https://www.ti.com
+ *
+ * Author: Dan Murphy <dmurphy@ti.com>
+ */
+
+#ifndef __TAS2764__
+#define __TAS2764__
+
+/* Book Control Register */
+#define TAS2764_BOOKCTL_PAGE	0
+#define TAS2764_BOOKCTL_REG	127
+#define TAS2764_REG(page, reg)	((page * 128) + reg)
+
+/* Page */
+#define TAS2764_PAGE		TAS2764_REG(0X0, 0x00)
+#define TAS2764_PAGE_PAGE_MASK	255
+
+/* Software Reset */
+#define TAS2764_SW_RST	TAS2764_REG(0X0, 0x01)
+#define TAS2764_RST	BIT(0)
+
+/* Power Control */
+#define TAS2764_PWR_CTRL		TAS2764_REG(0X0, 0x02)
+#define TAS2764_PWR_CTRL_MASK		GENMASK(1, 0)
+#define TAS2764_PWR_CTRL_ACTIVE		0x0
+#define TAS2764_PWR_CTRL_MUTE		BIT(0)
+#define TAS2764_PWR_CTRL_SHUTDOWN	BIT(1)
+
+#define TAS2764_VSENSE_POWER_EN		3
+#define TAS2764_ISENSE_POWER_EN		4
+
+/* Digital Volume Control */
+#define TAS2764_DVC	TAS2764_REG(0X0, 0x1a)
+#define TAS2764_DVC_MAX	0xc9
+
+#define TAS2764_CHNL_0  TAS2764_REG(0X0, 0x03)
+
+/* TDM Configuration Reg0 */
+#define TAS2764_TDM_CFG0		TAS2764_REG(0X0, 0x08)
+#define TAS2764_TDM_CFG0_SMP_MASK	BIT(5)
+#define TAS2764_TDM_CFG0_SMP_48KHZ	0x0
+#define TAS2764_TDM_CFG0_SMP_44_1KHZ	BIT(5)
+#define TAS2764_TDM_CFG0_MASK		GENMASK(3, 1)
+#define TAS2764_TDM_CFG0_44_1_48KHZ	BIT(3)
+#define TAS2764_TDM_CFG0_88_2_96KHZ	(BIT(3) | BIT(1))
+
+/* TDM Configuration Reg1 */
+#define TAS2764_TDM_CFG1		TAS2764_REG(0X0, 0x09)
+#define TAS2764_TDM_CFG1_MASK		GENMASK(5, 1)
+#define TAS2764_TDM_CFG1_51_SHIFT	1
+#define TAS2764_TDM_CFG1_RX_MASK	BIT(0)
+#define TAS2764_TDM_CFG1_RX_RISING	0x0
+#define TAS2764_TDM_CFG1_RX_FALLING	BIT(0)
+
+/* TDM Configuration Reg2 */
+#define TAS2764_TDM_CFG2		TAS2764_REG(0X0, 0x0a)
+#define TAS2764_TDM_CFG2_RXW_MASK	GENMASK(3, 2)
+#define TAS2764_TDM_CFG2_RXW_16BITS	0x0
+#define TAS2764_TDM_CFG2_RXW_24BITS	BIT(3)
+#define TAS2764_TDM_CFG2_RXW_32BITS	(BIT(3) | BIT(2))
+#define TAS2764_TDM_CFG2_RXS_MASK	GENMASK(1, 0)
+#define TAS2764_TDM_CFG2_RXS_16BITS	0x0
+#define TAS2764_TDM_CFG2_RXS_24BITS	BIT(0)
+#define TAS2764_TDM_CFG2_RXS_32BITS	BIT(1)
+#define TAS2764_TDM_CFG2_SCFG_MASK	GENMASK(5, 4)
+#define TAS2764_TDM_CFG2_SCFG_I2S	0x0
+#define TAS2764_TDM_CFG2_SCFG_LEFT_J	BIT(4)
+#define TAS2764_TDM_CFG2_SCFG_RIGHT_J	BIT(5)
+
+/* TDM Configuration Reg3 */
+#define TAS2764_TDM_CFG3		TAS2764_REG(0X0, 0x0c)
+#define TAS2764_TDM_CFG3_RXS_MASK	GENMASK(7, 4)
+#define TAS2764_TDM_CFG3_RXS_SHIFT	0x4
+#define TAS2764_TDM_CFG3_MASK		GENMASK(3, 0)
+
+/* TDM Configuration Reg5 */
+#define TAS2764_TDM_CFG5		TAS2764_REG(0X0, 0x0e)
+#define TAS2764_TDM_CFG5_VSNS_MASK	BIT(6)
+#define TAS2764_TDM_CFG5_VSNS_ENABLE	BIT(6)
+#define TAS2764_TDM_CFG5_50_MASK	GENMASK(5, 0)
+
+/* TDM Configuration Reg6 */
+#define TAS2764_TDM_CFG6		TAS2764_REG(0X0, 0x0f)
+#define TAS2764_TDM_CFG6_ISNS_MASK	BIT(6)
+#define TAS2764_TDM_CFG6_ISNS_ENABLE	BIT(6)
+#define TAS2764_TDM_CFG6_50_MASK	GENMASK(5, 0)
+
+#endif /* __TAS2764__ */
-- 
2.28.0.585.ge1cfff676549


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH v3 1/2] dt-bindings: tas2764: Add the TAS2764 binding doc
  2020-10-07 15:53 [PATCH v3 1/2] dt-bindings: tas2764: Add the TAS2764 binding doc Dan Murphy
  2020-10-07 15:53 ` [PATCH v3 2/2] ASoC: tas2764: Add the driver for the TAS2764 Dan Murphy
@ 2020-10-08 22:01 ` Mark Brown
  1 sibling, 0 replies; 3+ messages in thread
From: Mark Brown @ 2020-10-08 22:01 UTC (permalink / raw)
  To: tiwai, lgirdwood, Dan Murphy, robh+dt
  Cc: linux-kernel, devicetree, alsa-devel

On Wed, 7 Oct 2020 10:53:40 -0500, Dan Murphy wrote:
> Add the binding for the TAS2764 Smart Amplifier.

Applied to

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next

Thanks!

[1/2] dt-bindings: tas2764: Add the TAS2764 binding doc
      commit: 696bef70438359c1be333b62b2d879953768450d
[2/2] ASoC: tas2764: Add the driver for the TAS2764
      commit: 827ed8a0fa50bdd365c9f4c9f6ef561ca7032e49

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2020-10-08 22:01 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-07 15:53 [PATCH v3 1/2] dt-bindings: tas2764: Add the TAS2764 binding doc Dan Murphy
2020-10-07 15:53 ` [PATCH v3 2/2] ASoC: tas2764: Add the driver for the TAS2764 Dan Murphy
2020-10-08 22:01 ` [PATCH v3 1/2] dt-bindings: tas2764: Add the TAS2764 binding doc Mark Brown

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).