alsa-devel.alsa-project.org archive mirror
 help / color / mirror / Atom feed
* [alsa-devel] [PATCH]Add tas2770 driver code
@ 2019-09-05  5:35 shifu0704
  2019-09-05 17:00 ` Mark Brown
  0 siblings, 1 reply; 2+ messages in thread
From: shifu0704 @ 2019-09-05  5:35 UTC (permalink / raw)
  To: lgirdwood, broonie, perex, tiwai, linux-kernel, alsa-devel
  Cc: Dan?Murphy, Navada Kanyana, Mukund

From 5b539e7dbebebf7e58c38a6b54f222bc116d63f3 Mon Sep 17 00:00:00 2001
From: Shi Fu <shifu0704@thundersoft.com>
Date: Mon, 1 Jul 2019 11:00:13 +0800
Signed-off-by: Shi Fu <shifu0704@thundersoft.com>
Subject: [PATCH v5] dt: tas2770: Add tas2770 smart PA dt bindings.

Add tas2770 smart PA dt bindings.
---
 Documentation/devicetree/bindings/tas2770.txt | 38 +++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/tas2770.txt

diff --git a/Documentation/devicetree/bindings/tas2770.txt b/Documentation/devicetree/bindings/tas2770.txt
new file mode 100644
index 0000000..f70b310
--- /dev/null
+++ b/Documentation/devicetree/bindings/tas2770.txt
@@ -0,0 +1,38 @@
+Texas Instruments TAS2770 Smart PA
+
+The TAS2770 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.
+
+Required properties:
+
+ - compatible:   - Should contain "ti,tas2770".
+ - reg:       - The i2c address. Should contain <0x4c>, <0x4d>,<0x4e>, or <0x4f>.
+ - #address-cells  - Should be <1>.
+ - #size-cells     - Should be <0>.
+ - ti,asi-format:  - Sets TDM RX capture edge. 0->Rising; 1->Falling.
+ - ti,left-slot:   - Sets TDM RX left time slots.
+ - ti,right-slot:  - Sets TDM RX right time slots.
+ - ti,imon-slot-no:- TDM TX current sense time slot.
+ - ti,vmon-slot-no:- TDM TX voltage sense time slot.
+
+Optional properties:
+
+ - reset-gpio: Reset GPIO number of left device.
+ - irq-gpio:  IRQ GPIO number of left device.
+
+Examples:
+
+    tas2770@4c {
+                compatible = "ti,tas2770";
+                reg = <0x4c>;
+                reset-gpio = <&gpio15 1 GPIO_ACTIVE_LOW>;
+                irq-gpio = <&gpio16 1 GPIO_ACTIVE_LOW>;
+                ti,asi-format = <0>;
+                ti,left-slot = <0>;
+                ti,right-slot = <1>;
+                ti,imon-slot-no = <0>;
+                ti,vmon-slot-no = <2>;
+        };
+
-- 
2.7.4





From 167a85aa30a41471ccddd9912b192d92800a4212 Mon Sep 17 00:00:00 2001
From: TS Shi Fu <shifu0704@thundersoft.com>
Date: Mon, 24 Jun 2019 18:26:09 +0800
Subject: [PATCH v5] Add tas2770 driver code

Add tas2770 driver code
Date: Wed, 15 May 2019 12:08:42 +0800
From: Shi Fu <shifu0704@thundersoft.com>
Signed-off-by: Shi Fu <shifu0704@thundersoft.com>

Change-Id: Ibd5b04b4c0d0021a8057023aa6b43f97bba372b1
---
 sound/soc/codecs/Kconfig   |    5 +
 sound/soc/codecs/Makefile  |    2 +
 sound/soc/codecs/tas2770.c | 1103 ++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/tas2770.h |  173 +++++++
 4 files changed, 1283 insertions(+)
 create mode 100644 sound/soc/codecs/tas2770.c
 create mode 100644 sound/soc/codecs/tas2770.h

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 8f3e787..cc92da3 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -111,6 +111,7 @@ config SND_SOC_ALL_CODECS
  select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
  select SND_SOC_STI_SAS
  select SND_SOC_TAS2552 if I2C
+ select SND_SOC_TAS2770 if I2C
  select SND_SOC_TAS5086 if I2C
  select SND_SOC_TAS571X if I2C
  select SND_SOC_TFA9879 if I2C
@@ -652,6 +653,10 @@ config SND_SOC_TAS2552
  tristate "Texas Instruments TAS2552 Mono Audio amplifier"
  depends on I2C
 
+config SND_SOC_TAS2770
+ tristate "Texas Instruments TAS2770 speaker amplifier"
+ depends on I2C
+
 config SND_SOC_TAS5086
  tristate "Texas Instruments TAS5086 speaker amplifier"
  depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 5305cc6..1b58d08 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -116,6 +116,7 @@ snd-soc-stac9766-objs := stac9766.o
 snd-soc-sti-sas-objs := sti-sas.o
 snd-soc-tas5086-objs := tas5086.o
 snd-soc-tas571x-objs := tas571x.o
+snd-soc-tas2770-objs :=  tas2770.o
 snd-soc-tfa9879-objs := tfa9879.o
 snd-soc-tlv320aic23-objs := tlv320aic23.o
 snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
@@ -332,6 +333,7 @@ obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o
 obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o
 obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
 obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o
+obj-$(CONFIG_SND_SOC_TAS2770) += snd-soc-tas2770.o
 obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o
diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c
new file mode 100644
index 0000000..9fc0c11
--- /dev/null
+++ b/sound/soc/codecs/tas2770.c
@@ -0,0 +1,1103 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ALSA SoC Texas Instruments TAS2770 20-W Digital Input Mono Class-D
+ * Audio Amplifier with Speaker I/V Sense
+ *
+ * Copyright (C) 2016-2017 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Tracy Yi <tracy-yi@ti.com>
+ * Shi Fu <shifu0704@thundersoft.com>
+ */
+
+#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/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.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 "tas2770.h"
+
+#define TAS2770_MDELAY 0xFFFFFFFE
+#define TAS2770_CHECK_PERIOD 5000 /* 5 second */
+
+static void tas2770_hw_reset(struct tas2770_priv *p_tas2770)
+{
+ if (p_tas2770->mn_reset_gpio) {
+ gpiod_set_value_cansleep(p_tas2770->mn_reset_gpio, 0);
+ msleep(20);
+ gpiod_set_value_cansleep(p_tas2770->mn_reset_gpio, 1);
+ }
+
+ p_tas2770->mn_current_book = -1;
+ p_tas2770->mn_current_page = -1;
+}
+
+static void tas2770_enable_irq(struct tas2770_priv *p_tas2770, bool enable)
+{
+ if (enable) {
+ if (p_tas2770->mb_irq_enable)
+ return;
+
+ if (gpio_is_valid(p_tas2770->mn_irq))
+ enable_irq(p_tas2770->mn_irq);
+ p_tas2770->mb_irq_enable = true;
+ } else {
+ if (gpio_is_valid(p_tas2770->mn_irq))
+ disable_irq_nosync(p_tas2770->mn_irq);
+ p_tas2770->mb_irq_enable = false;
+ }
+}
+
+static int tas2770_runtime_suspend(struct tas2770_priv *p_tas2770)
+{
+ p_tas2770->mb_runtime_suspend = true;
+
+ return 0;
+}
+
+static int tas2770_runtime_resume(struct tas2770_priv *p_tas2770)
+{
+
+ p_tas2770->mb_runtime_suspend = false;
+
+ return 0;
+}
+
+static int tas2770_regmap_write(struct tas2770_priv *p_tas2770,
+ unsigned int reg, unsigned int value)
+{
+ int nResult = 0;
+ int retry_count = TAS2770_I2C_RETRY_COUNT;
+
+ while (retry_count--) {
+ nResult = snd_soc_component_write(p_tas2770->component, reg,
+ value);
+ if (!nResult)
+ break;
+ msleep(20);
+ }
+ if (retry_count == -1)
+ return ERROR_I2C_FAILED;
+ else
+ return 0;
+}
+
+static int tas2770_regmap_read(struct tas2770_priv *p_tas2770,
+ unsigned int reg, unsigned int *value)
+{
+ int nResult = 0;
+ int retry_count = TAS2770_I2C_RETRY_COUNT;
+
+ while (retry_count--) {
+ nResult = snd_soc_component_read(p_tas2770->component, reg,
+ value);
+ if (!nResult)
+ break;
+
+ msleep(20);
+ }
+ if (!retry_count)
+ return ERROR_I2C_FAILED;
+
+ return 0;
+}
+
+static int tas2770_codec_suspend(struct snd_soc_component *component)
+{
+ struct tas2770_priv *p_tas2770 =
+ snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&p_tas2770->codec_lock);
+ tas2770_runtime_suspend(p_tas2770);
+ mutex_unlock(&p_tas2770->codec_lock);
+
+ return 0;
+}
+
+static int tas2770_codec_resume(struct snd_soc_component *component)
+{
+ struct tas2770_priv *p_tas2770 =
+ snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&p_tas2770->codec_lock);
+ tas2770_runtime_resume(p_tas2770);
+ mutex_unlock(&p_tas2770->codec_lock);
+
+ return 0;
+}
+
+static const char * const tas2770_ASI1_src[] = {
+ "I2C offset", "Left", "Right", "LeftRightDiv2",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ tas2770_ASI1_src_enum, TAS2770_TDM_CFG_REG2,
+ 4, tas2770_ASI1_src);
+
+static const struct snd_kcontrol_new tas2770_asi1_mux =
+ SOC_DAPM_ENUM("ASI1 Source", tas2770_ASI1_src_enum);
+
+static int tas2770_set_power_state(struct tas2770_priv *p_tas2770, int state)
+{
+ struct snd_soc_component *component = p_tas2770->component;
+
+ switch (state) {
+ case TAS2770_POWER_ACTIVE:
+ snd_soc_component_update_bits(component, TAS2770_PWR_CTRL,
+ TAS2770_PWR_CTRL_MASK,
+ TAS2770_PWR_CTRL_ACTIVE);
+ p_tas2770->mn_power_state = TAS2770_POWER_ACTIVE;
+ tas2770_enable_irq(p_tas2770, true);
+ break;
+
+ case TAS2770_POWER_MUTE:
+ snd_soc_component_update_bits(component, TAS2770_PWR_CTRL,
+ TAS2770_PWR_CTRL_MASK,
+ TAS2770_PWR_CTRL_MUTE);
+ p_tas2770->mn_power_state = TAS2770_POWER_MUTE;
+ break;
+
+ case TAS2770_POWER_SHUTDOWN:
+ snd_soc_component_update_bits(component, TAS2770_PWR_CTRL,
+ TAS2770_PWR_CTRL_MASK,
+ TAS2770_PWR_CTRL_SHUTDOWN);
+ tas2770_enable_irq(p_tas2770, false);
+ p_tas2770->mn_power_state = TAS2770_POWER_SHUTDOWN;
+ break;
+
+ default:
+ dev_err(p_tas2770->dev,
+ "wrong power state setting %d\n", state);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tas2770_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 tas2770_priv *p_tas2770 =
+ snd_soc_component_get_drvdata(component);
+ int ret;
+
+ mutex_lock(&p_tas2770->codec_lock);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ ret = tas2770_set_power_state(p_tas2770,
+ TAS2770_POWER_MUTE);
+ if (ret)
+ goto end;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ ret = tas2770_set_power_state(p_tas2770,
+ TAS2770_POWER_SHUTDOWN);
+ if (ret)
+ goto end;
+ break;
+ default:
+ dev_err(p_tas2770->dev, "Not supported evevt\n");
+ ret = -EINVAL;
+ }
+
+end:
+ mutex_unlock(&p_tas2770->codec_lock);
+ return ret;
+}
+
+static const struct snd_kcontrol_new isense_switch =
+ SOC_DAPM_SINGLE("Switch", TAS2770_PWR_CTRL, 3, 1, 1);
+static const struct snd_kcontrol_new vsense_switch =
+ SOC_DAPM_SINGLE("Switch", TAS2770_PWR_CTRL, 2, 1, 1);
+
+static const struct snd_soc_dapm_widget tas2770_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,
+ &tas2770_asi1_mux),
+ SND_SOC_DAPM_SWITCH("ISENSE", TAS2770_PWR_CTRL, 3, 1,
+ &isense_switch),
+ SND_SOC_DAPM_SWITCH("VSENSE", TAS2770_PWR_CTRL, 2, 1,
+ &vsense_switch),
+ SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2770_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 tas2770_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 tas2770_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2770_priv *p_tas2770 =
+ snd_soc_component_get_drvdata(component);
+ int ret;
+
+
+ mutex_lock(&p_tas2770->codec_lock);
+ if (mute)
+ ret = tas2770_set_power_state(p_tas2770, TAS2770_POWER_MUTE);
+ else
+ ret = tas2770_set_power_state(p_tas2770, TAS2770_POWER_ACTIVE);
+
+ mutex_unlock(&p_tas2770->codec_lock);
+ return ret;
+}
+
+static int tas2770_set_slot(struct snd_soc_component *component, int slot_width)
+{
+ int ret;
+ struct tas2770_priv *p_tas2770 =
+ snd_soc_component_get_drvdata(component);
+
+ switch (slot_width) {
+ case 16:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG2,
+ TAS2770_TDM_CFG_REG2_RXS_MASK,
+ TAS2770_TDM_CFG_REG2_RXS_16BITS);
+ break;
+
+ case 24:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG2,
+ TAS2770_TDM_CFG_REG2_RXS_MASK,
+ TAS2770_TDM_CFG_REG2_RXS_24BITS);
+ break;
+
+ case 32:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG2,
+ TAS2770_TDM_CFG_REG2_RXS_MASK,
+ TAS2770_TDM_CFG_REG2_RXS_32BITS);
+ break;
+
+ case 0:
+ /* Do not change slot width */
+ ret = 0;
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ if (!ret)
+ p_tas2770->mn_slot_width = slot_width;
+
+ return ret;
+}
+
+static int tas2770_set_bitwidth(struct tas2770_priv *p_tas2770, int bitwidth)
+{
+ int ret;
+ struct snd_soc_component *component = p_tas2770->component;
+
+ switch (bitwidth) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG2,
+ TAS2770_TDM_CFG_REG2_RXW_MASK,
+ TAS2770_TDM_CFG_REG2_RXW_16BITS);
+ /* If machine driver did not call set slot width */
+ if (p_tas2770->mn_slot_width == 0)
+ tas2770_set_slot(component, TAS2770_SLOT_16BIT);
+ p_tas2770->mn_v_mon_slot_no = p_tas2770->mn_i_mon_slot_no + 2;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG2,
+ TAS2770_TDM_CFG_REG2_RXW_MASK,
+ TAS2770_TDM_CFG_REG2_RXW_24BITS);
+ if (p_tas2770->mn_slot_width == 0)
+ tas2770_set_slot(component, TAS2770_SLOT_32BIT);
+ p_tas2770->mn_v_mon_slot_no = p_tas2770->mn_i_mon_slot_no + 4;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG2,
+ TAS2770_TDM_CFG_REG2_RXW_MASK,
+ TAS2770_TDM_CFG_REG2_RXW_32BITS);
+ if (p_tas2770->mn_slot_width == 0)
+ tas2770_set_slot(component, TAS2770_SLOT_32BIT);
+ p_tas2770->mn_v_mon_slot_no = p_tas2770->mn_i_mon_slot_no + 4;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ p_tas2770->mn_ch_size = bitwidth;
+
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG5,
+ TAS2770_TDM_CFG_REG5_VSNS_MASK |
+ TAS2770_TDM_CFG_REG5_50_MASK,
+ TAS2770_TDM_CFG_REG5_VSNS_ENABLE |
+ p_tas2770->mn_v_mon_slot_no);
+ if (ret)
+ goto end;
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG6,
+ TAS2770_TDM_CFG_REG6_ISNS_MASK |
+ TAS2770_TDM_CFG_REG6_50_MASK,
+ TAS2770_TDM_CFG_REG6_ISNS_ENABLE |
+ p_tas2770->mn_i_mon_slot_no);
+
+end:
+ return ret;
+}
+
+static int tas2770_set_samplerate(struct tas2770_priv *p_tas2770,
+ int samplerate)
+{
+ int ret;
+ struct snd_soc_component *component = p_tas2770->component;
+
+ switch (samplerate) {
+ case 48000:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_SMP_MASK,
+ TAS2770_TDM_CFG_REG0_SMP_48KHZ);
+ if (ret)
+ goto end;
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_31_MASK,
+ TAS2770_TDM_CFG_REG0_31_44_1_48KHZ);
+ if (ret)
+ goto end;
+ break;
+ case 44100:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_SMP_MASK,
+ TAS2770_TDM_CFG_REG0_SMP_44_1KHZ);
+ if (ret)
+ goto end;
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_31_MASK,
+ TAS2770_TDM_CFG_REG0_31_44_1_48KHZ);
+ if (ret)
+ goto end;
+ break;
+ case 96000:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_SMP_MASK,
+ TAS2770_TDM_CFG_REG0_SMP_48KHZ);
+ if (ret)
+ goto end;
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_31_MASK,
+ TAS2770_TDM_CFG_REG0_31_88_2_96KHZ);
+ break;
+ case 88200:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_SMP_MASK,
+ TAS2770_TDM_CFG_REG0_SMP_44_1KHZ);
+ if (ret)
+ goto end;
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_31_MASK,
+ TAS2770_TDM_CFG_REG0_31_88_2_96KHZ);
+ break;
+ case 19200:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_SMP_MASK,
+ TAS2770_TDM_CFG_REG0_SMP_48KHZ);
+ if (ret)
+ goto end;
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_31_MASK,
+ TAS2770_TDM_CFG_REG0_31_176_4_192KHZ);
+ if (ret)
+ goto end;
+ break;
+ case 17640:
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_SMP_MASK,
+ TAS2770_TDM_CFG_REG0_SMP_44_1KHZ);
+ if (ret)
+ goto end;
+ ret = snd_soc_component_update_bits(component,
+ TAS2770_TDM_CFG_REG0,
+ TAS2770_TDM_CFG_REG0_31_MASK,
+ TAS2770_TDM_CFG_REG0_31_176_4_192KHZ);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+end:
+ if (!ret)
+ p_tas2770->mn_sampling_rate = samplerate;
+ return ret;
+}
+
+static int tas2770_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 tas2770_priv *p_tas2770 =
+ snd_soc_component_get_drvdata(component);
+ int ret;
+
+ mutex_lock(&p_tas2770->codec_lock);
+
+ ret = tas2770_set_bitwidth(p_tas2770, params_format(params));
+ if (ret < 0)
+ goto end;
+
+
+ ret = tas2770_set_samplerate(p_tas2770, params_rate(params));
+
+end:
+ mutex_unlock(&p_tas2770->codec_lock);
+ return ret;
+}
+
+static int tas2770_set_fmt(struct tas2770_priv *p_tas2770, unsigned int fmt)
+{
+ u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0;
+ int ret;
+ int value = 0;
+ struct snd_soc_component *component = p_tas2770->component;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ dev_err(p_tas2770->dev, "ASI format master is not found\n");
+ ret = -EINVAL;
+ return ret;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ asi_cfg_1 |= TAS2770_TDM_CFG_REG1_RX_RSING;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ asi_cfg_1 |= TAS2770_TDM_CFG_REG1_RX_FALING;
+ break;
+ default:
+ dev_err(p_tas2770->dev, "ASI format Inverse is not found\n");
+ ret = -EINVAL;
+ return ret;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG1,
+ TAS2770_TDM_CFG_REG1_RX_MASK,
+ asi_cfg_1);
+ if (ret)
+ return ret;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case (SND_SOC_DAIFMT_I2S):
+ tdm_rx_start_slot = 1;
+ break;
+ case (SND_SOC_DAIFMT_DSP_A):
+ case (SND_SOC_DAIFMT_DSP_B):
+ tdm_rx_start_slot = 1;
+ break;
+ case (SND_SOC_DAIFMT_LEFT_J):
+ tdm_rx_start_slot = 0;
+ break;
+ default:
+ dev_err(p_tas2770->dev,
+ "DAI Format is not found, fmt=0x%x\n", fmt);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG1,
+ TAS2770_TDM_CFG_REG1_MASK,
+ (tdm_rx_start_slot << TAS2770_TDM_CFG_REG1_51_SHIFT));
+ if (ret)
+ return ret;
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG3,
+ TAS2770_TDM_CFG_REG3_30_MASK,
+ (p_tas2770->mn_left_slot <<
+ TAS2770_TDM_CFG_REG3_30_SHIFT));
+ if (ret)
+ return ret;
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG3,
+ TAS2770_TDM_CFG_REG3_RXS_MASK,
+ (p_tas2770->mn_right_slot <<
+ TAS2770_TDM_CFG_REG3_RXS_SHIFT));
+ if (ret)
+ return ret;
+
+ value = snd_soc_component_read32(component, TAS2770_TDM_CFG_REG3);
+
+ p_tas2770->mn_asi_format = fmt;
+
+ return 0;
+}
+
+static int tas2770_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2770_priv *p_tas2770 =
+ snd_soc_component_get_drvdata(component);
+ int ret;
+
+ mutex_lock(&p_tas2770->codec_lock);
+
+ ret = tas2770_set_fmt(p_tas2770, fmt);
+
+ mutex_unlock(&p_tas2770->codec_lock);
+ return ret;
+}
+
+static int tas2770_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ int ret;
+ struct snd_soc_component *component = dai->component;
+ struct tas2770_priv *p_tas2770 =
+ snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&p_tas2770->codec_lock);
+ ret = tas2770_set_slot(component, slot_width);
+ mutex_unlock(&p_tas2770->codec_lock);
+
+ return ret;
+}
+
+static struct snd_soc_dai_ops tas2770_dai_ops = {
+ .digital_mute = tas2770_mute,
+ .hw_params  = tas2770_hw_params,
+ .set_fmt    = tas2770_set_dai_fmt,
+ .set_tdm_slot = tas2770_set_dai_tdm_slot,
+};
+
+#define TAS2770_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define TAS2770_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+   SNDRV_PCM_RATE_96000 |\
+    SNDRV_PCM_RATE_192000\
+  )
+
+static struct snd_soc_dai_driver tas2770_dai_driver[] = {
+ {
+ .name = "tas2770 ASI1",
+ .id = 0,
+ .playback = {
+ .stream_name    = "ASI1 Playback",
+ .channels_min   = 2,
+ .channels_max   = 2,
+ .rates      = TAS2770_RATES,
+ .formats    = TAS2770_FORMATS,
+ },
+ .capture = {
+ .stream_name    = "ASI1 Capture",
+ .channels_min   = 0,
+ .channels_max   = 2,
+ .rates          = TAS2770_RATES,
+ .formats    = TAS2770_FORMATS,
+ },
+ .ops = &tas2770_dai_ops,
+ .symmetric_rates = 1,
+ },
+};
+
+static int tas2770_codec_probe(struct snd_soc_component *component)
+{
+ struct tas2770_priv *p_tas2770 =
+ snd_soc_component_get_drvdata(component);
+
+ p_tas2770->component = component;
+
+ return 0;
+}
+
+static void tas2770_codec_remove(struct snd_soc_component *component)
+{
+ pm_runtime_put(component->dev);
+}
+
+static DECLARE_TLV_DB_SCALE(tas2770_digital_tlv, 1100, 50, 0);
+static DECLARE_TLV_DB_SCALE(tas2770_playback_volume, -12750, 50, 0);
+
+static const struct snd_kcontrol_new tas2770_snd_controls[] = {
+ SOC_SINGLE_TLV("Amp Output Level", TAS2770_PLAY_CFG_REG0,
+ 0, 0x14, 0,
+ tas2770_digital_tlv),
+ SOC_SINGLE_TLV("Playback Volume", TAS2770_PLAY_CFG_REG2,
+ 0, TAS2770_PLAY_CFG_REG2_VMAX, 1,
+ tas2770_playback_volume),
+};
+
+static const struct snd_soc_component_driver soc_component_driver_tas2770 = {
+ .probe = tas2770_codec_probe,
+ .remove = tas2770_codec_remove,
+ .suspend = tas2770_codec_suspend,
+ .resume = tas2770_codec_resume,
+ .controls = tas2770_snd_controls,
+ .num_controls = ARRAY_SIZE(tas2770_snd_controls),
+ .dapm_widgets = tas2770_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas2770_dapm_widgets),
+ .dapm_routes = tas2770_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas2770_audio_map),
+ .idle_bias_on = 1,
+ .endianness = 1,
+ .non_legacy_dai_naming = 1,
+};
+
+static int tas2770_register_codec(struct tas2770_priv *p_tas2770)
+{
+ return devm_snd_soc_register_component(p_tas2770->dev,
+ &soc_component_driver_tas2770,
+ tas2770_dai_driver, ARRAY_SIZE(tas2770_dai_driver));
+}
+
+static void tas2770_load_config(struct tas2770_priv *p_tas2770)
+{
+ int ret;
+
+ tas2770_hw_reset(p_tas2770);
+ tas2770_regmap_write(p_tas2770, TAS2770_SW_RST,
+ TAS2770_RST);
+
+ ret = tas2770_set_slot(p_tas2770->component, p_tas2770->mn_slot_width);
+ if (ret)
+ goto error_out;
+
+ ret = tas2770_set_fmt(p_tas2770, p_tas2770->mn_asi_format);
+ if (ret)
+ goto error_out;
+
+ ret = tas2770_set_bitwidth(p_tas2770, p_tas2770->mn_ch_size);
+ if (ret)
+ goto error_out;
+
+ ret = tas2770_set_samplerate(p_tas2770, p_tas2770->mn_sampling_rate);
+ if (ret)
+ goto error_out;
+
+ ret = tas2770_set_power_state(p_tas2770, p_tas2770->mn_power_state);
+ if (ret)
+ goto error_out;
+
+error_out:
+ return;
+}
+
+static const struct reg_default tas2770_reg_defaults[] = {
+ { TAS2770_PAGE, 0x00 },
+ { TAS2770_SW_RST, 0x00 },
+ { TAS2770_PWR_CTRL, 0x0e },
+ { TAS2770_PLAY_CFG_REG0, 0x10 },
+ { TAS2770_PLAY_CFG_REG1, 0x01 },
+ { TAS2770_PLAY_CFG_REG2, 0x00 },
+ { TAS2770_MSC_CFG_REG0, 0x07 },
+ { TAS2770_TDM_CFG_REG1, 0x02 },
+ { TAS2770_TDM_CFG_REG2, 0x0a },
+ { TAS2770_TDM_CFG_REG3, 0x10 },
+ { TAS2770_INT_MASK_REG0, 0xfc },
+ { TAS2770_INT_MASK_REG1, 0xb1 },
+ { TAS2770_INT_CFG, 0x05 },
+ { TAS2770_MISC_IRQ, 0x81 },
+ { TAS2770_CLK_CGF, 0x0c },
+
+};
+
+static bool tas2770_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS2770_PAGE: /* regmap implementation requires this */
+ case TAS2770_SW_RST: /* always clears after write */
+ case TAS2770_BO_PRV_REG0:/* has a self clearing bit */
+ case TAS2770_LVE_INT_REG0:
+ case TAS2770_LVE_INT_REG1:
+ case TAS2770_LAT_INT_REG0:/* Sticky interrupt flags */
+ case TAS2770_LAT_INT_REG1:/* Sticky interrupt flags */
+ case TAS2770_VBAT_MSB:
+ case TAS2770_VBAT_LSB:
+ case TAS2770_TEMP_MSB:
+ case TAS2770_TEMP_LSB:
+ return true;
+ }
+ return false;
+}
+
+static bool tas2770_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS2770_LVE_INT_REG0:
+ case TAS2770_LVE_INT_REG1:
+ case TAS2770_LAT_INT_REG0:
+ case TAS2770_LAT_INT_REG1:
+ case TAS2770_VBAT_MSB:
+ case TAS2770_VBAT_LSB:
+ case TAS2770_TEMP_MSB:
+ case TAS2770_TEMP_LSB:
+ case TAS2770_TDM_CLK_DETC:
+ case TAS2770_REV_AND_GPID:
+ return false;
+ }
+ return true;
+}
+
+static const struct regmap_range_cfg tas2770_regmap_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 1 * 128,
+ .selector_reg = TAS2770_PAGE,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 128,
+ },
+};
+
+static const struct regmap_config tas2770_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = tas2770_writeable,
+ .volatile_reg = tas2770_volatile,
+ .reg_defaults = tas2770_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas2770_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .ranges = tas2770_regmap_ranges,
+ .num_ranges = ARRAY_SIZE(tas2770_regmap_ranges),
+ .max_register = 1 * 128,
+};
+
+static irqreturn_t tas2770_irq_handler(int irq, void *dev_id)
+{
+ struct tas2770_priv *p_tas2770 = (struct tas2770_priv *)dev_id;
+
+ unsigned int nDevInt1Status = 0, nDevInt2Status = 0;
+ int n_counter = 2;
+ int n_result;
+
+ mutex_lock(&p_tas2770->codec_lock);
+
+ tas2770_enable_irq(p_tas2770, false);
+
+ if (p_tas2770->mb_runtime_suspend)
+ goto end;
+
+ if (p_tas2770->mn_power_state == TAS2770_POWER_SHUTDOWN)
+ goto end;
+
+ n_result = tas2770_regmap_write(p_tas2770, TAS2770_INT_MASK_REG0,
+ TAS2770_INT_MASK_REG0_DISABLE);
+ if (n_result)
+ goto reload;
+ n_result = tas2770_regmap_write(p_tas2770, TAS2770_INT_MASK_REG1,
+ TAS2770_INT_MASK_REG1_DISABLE);
+ if (n_result)
+ goto reload;
+
+ n_result = tas2770_regmap_read(p_tas2770,
+ TAS2770_LAT_INT_REG0, &nDevInt1Status);
+ if (n_result >= 0)
+ n_result = tas2770_regmap_read(p_tas2770,
+ TAS2770_LAT_INT_REG1, &nDevInt2Status);
+ else
+ goto reload;
+
+ if (((nDevInt1Status & 0x3) != 0) || ((nDevInt2Status & 0x0f) != 0)) {
+ /* in case of INT_OC, INT_OT, INT_OVLT, INT_UVLT, INT_BO */
+
+ if (nDevInt1Status &
+ TAS2770_LAT_INT_REG0_OCE_FLG) {
+ p_tas2770->mn_err_code |= ERROR_OVER_CURRENT;
+ dev_err(p_tas2770->dev, "SPK over current!\n");
+ } else {
+ p_tas2770->mn_err_code &= ~ERROR_OVER_CURRENT;
+ }
+
+ if (nDevInt1Status &
+ TAS2770_LAT_INT_REG0_OTE_FLG) {
+ p_tas2770->mn_err_code |= ERROR_DIE_OVERTEMP;
+ dev_err(p_tas2770->dev, "die over temperature!\n");
+ } else {
+ p_tas2770->mn_err_code &= ~ERROR_DIE_OVERTEMP;
+ }
+
+ if (nDevInt2Status &
+ TAS2770_LAT_INT_REG1_VBA_TOV) {
+ p_tas2770->mn_err_code |= ERROR_OVER_VOLTAGE;
+ dev_err(p_tas2770->dev, "SPK over voltage!\n");
+ } else {
+ p_tas2770->mn_err_code &= ~ERROR_OVER_VOLTAGE;
+ }
+
+ if (nDevInt2Status &
+ TAS2770_LAT_INT_REG1_VBA_TUV) {
+ p_tas2770->mn_err_code |= ERROR_UNDER_VOLTAGE;
+ dev_err(p_tas2770->dev, "SPK under voltage!\n");
+ } else {
+ p_tas2770->mn_err_code &= ~ERROR_UNDER_VOLTAGE;
+ }
+
+ if (nDevInt2Status &
+ TAS2770_LAT_INT_REG1_BOUT_FLG) {
+ p_tas2770->mn_err_code |= ERROR_BROWNOUT;
+ dev_err(p_tas2770->dev, "brownout!\n");
+ } else {
+ p_tas2770->mn_err_code &= ~ERROR_BROWNOUT;
+ }
+
+ goto reload;
+ } else {
+ n_counter = 2;
+
+ while (n_counter > 0) {
+ n_result = tas2770_regmap_read(p_tas2770,
+ TAS2770_PWR_CTRL, &nDevInt1Status);
+ if (n_result)
+ goto reload;
+
+ if ((nDevInt1Status &
+ TAS2770_PWR_CTRL_MASK)
+ != TAS2770_PWR_CTRL_SHUTDOWN)
+ break;
+
+ n_counter--;
+ if (n_counter > 0)
+ msleep(20);
+ }
+
+ if ((nDevInt1Status &
+ TAS2770_PWR_CTRL_MASK)
+ == TAS2770_PWR_CTRL_SHUTDOWN) {
+ dev_err(p_tas2770->dev, "%s, Critical ERROR REG[0x%x] = 0x%x\n",
+ __func__,
+ TAS2770_PWR_CTRL,
+ nDevInt1Status);
+ p_tas2770->mn_err_code |= ERROR_CLASSD_PWR;
+ goto reload;
+ }
+ p_tas2770->mn_err_code &= ~ERROR_CLASSD_PWR;
+ }
+
+ n_result = tas2770_regmap_write(p_tas2770,
+ TAS2770_INT_MASK_REG0, 0xfc);
+ if (n_result)
+ goto reload;
+
+ n_result = tas2770_regmap_write(p_tas2770,
+ TAS2770_INT_MASK_REG1, 0xb1);
+ if (n_result)
+ goto reload;
+
+ goto end;
+
+reload:
+ /* hardware reset and reload */
+ tas2770_load_config(p_tas2770);
+
+end:
+ mutex_unlock(&p_tas2770->codec_lock);
+ tas2770_enable_irq(p_tas2770, true);
+
+ return IRQ_HANDLED;
+}
+
+static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *p_tas2770)
+{
+ int rc = 0;
+
+ rc = fwnode_property_read_u32(dev->fwnode, "ti,asi-format",
+ &p_tas2770->mn_asi_format);
+ if (rc) {
+ dev_err(p_tas2770->dev, "Looking up %s property failed %d\n",
+ "ti,asi-format", rc);
+ goto end;
+ }
+
+ rc = fwnode_property_read_u32(dev->fwnode, "ti,left-slot",
+ &p_tas2770->mn_left_slot);
+ if (rc) {
+ dev_err(p_tas2770->dev, "Looking up %s property failed %d\n",
+ "ti,left-slot", rc);
+ goto end;
+ }
+
+ rc = fwnode_property_read_u32(dev->fwnode, "ti,right-slot",
+ &p_tas2770->mn_right_slot);
+ if (rc) {
+ dev_err(p_tas2770->dev, "Looking up %s property failed %d\n",
+ "ti,right-slot", rc);
+ goto end;
+ }
+
+ rc = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no",
+ &p_tas2770->mn_i_mon_slot_no);
+ if (rc) {
+ dev_err(p_tas2770->dev, "Looking up %s property failed %d\n",
+ "ti,imon-slot-no", rc);
+ goto end;
+ }
+
+ rc = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no",
+ &p_tas2770->mn_v_mon_slot_no);
+ if (rc) {
+ dev_err(p_tas2770->dev, "Looking up %s property failed %d\n",
+ "ti,vmon-slot-no", rc);
+ goto end;
+ }
+
+end:
+ return rc;
+}
+
+static int tas2770_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tas2770_priv *p_tas2770;
+ int n_result;
+
+ p_tas2770 = devm_kzalloc(&client->dev,
+ sizeof(struct tas2770_priv), GFP_KERNEL);
+ if (p_tas2770 == NULL)
+ return -ENOMEM;
+ p_tas2770->dev = &client->dev;
+ p_tas2770->mn_irq = client->irq;
+
+ i2c_set_clientdata(client, p_tas2770);
+ dev_set_drvdata(&client->dev, p_tas2770);
+ p_tas2770->mn_power_state = TAS2770_POWER_SHUTDOWN;
+
+ p_tas2770->regmap = devm_regmap_init_i2c(client, &tas2770_i2c_regmap);
+ if (IS_ERR(p_tas2770->regmap)) {
+ n_result = PTR_ERR(p_tas2770->regmap);
+ dev_err(&client->dev, "Failed to allocate register map: %d\n",
+ n_result);
+ goto end;
+ }
+
+ if (client->dev.of_node) {
+ n_result = tas2770_parse_dt(&client->dev, p_tas2770);
+ if (n_result) {
+ dev_err(p_tas2770->dev, "%s: Failed to parse devicetree\n",
+ __func__);
+ goto end;
+ }
+ }
+
+ p_tas2770->mn_reset_gpio = devm_gpiod_get_optional(p_tas2770->dev,
+  "reset-gpio",
+      GPIOD_OUT_HIGH);
+ if (IS_ERR(p_tas2770->mn_reset_gpio)) {
+ if (PTR_ERR(p_tas2770->mn_reset_gpio) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ p_tas2770->mn_reset_gpio = NULL;
+ }
+
+ if (p_tas2770->mn_irq > 0) {
+ n_result = devm_request_threaded_irq(p_tas2770->dev,
+   p_tas2770->mn_irq,
+   NULL, tas2770_irq_handler,
+   IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name,
+   p_tas2770);
+ if (n_result) {
+ dev_err(p_tas2770->dev, "Unable to request IRQ\n");
+ return n_result;
+ }
+ }
+
+ p_tas2770->mn_ch_size = 0;
+ p_tas2770->mn_slot_width = 0;
+
+ tas2770_hw_reset(p_tas2770);
+ tas2770_regmap_write(p_tas2770, TAS2770_SW_RST,
+ TAS2770_RST);
+
+ mutex_init(&p_tas2770->dev_lock);
+
+ mutex_init(&p_tas2770->codec_lock);
+ n_result = tas2770_register_codec(p_tas2770);
+ if (n_result)
+ dev_err(p_tas2770->dev, "Register codec failed.\n");
+
+end:
+ return n_result;
+}
+
+static int tas2770_i2c_remove(struct i2c_client *client)
+{
+ struct tas2770_priv *p_tas2770 = i2c_get_clientdata(client);
+
+ mutex_destroy(&p_tas2770->dev_lock);
+
+ return 0;
+}
+
+
+static const struct i2c_device_id tas2770_i2c_id[] = {
+ { "tas2770", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas2770_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id tas2770_of_match[] = {
+ { .compatible = "ti,tas2770" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tas2770_of_match);
+#endif
+
+static struct i2c_driver tas2770_i2c_driver = {
+ .driver = {
+ .name   = "tas2770",
+ .owner  = THIS_MODULE,
+#if defined(CONFIG_OF)
+ .of_match_table = of_match_ptr(tas2770_of_match),
+#endif
+ },
+ .probe      = tas2770_i2c_probe,
+ .remove     = tas2770_i2c_remove,
+ .id_table   = tas2770_i2c_id,
+};
+
+module_i2c_driver(tas2770_i2c_driver);
+
+MODULE_AUTHOR("Shi Fu <shifu0704@thundersoft.com>");
+MODULE_DESCRIPTION("TAS2770 I2C Smart Amplifier driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/tas2770.h b/sound/soc/codecs/tas2770.h
new file mode 100644
index 0000000..f08e208
--- /dev/null
+++ b/sound/soc/codecs/tas2770.h
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * ALSA SoC TAS2770 codec driver
+ *
+ *  Copyright (C) 2016-2017 Texas Instruments Incorporated - http://www.ti.com/
+ */
+#ifndef __TAS2770__
+#define __TAS2770__
+
+/* Book Control Register (available in page0 of each book) */
+#define TAS2770_BOOKCTL_PAGE            0
+#define TAS2770_BOOKCTL_REG         127
+#define TAS2770_REG(page, reg)        ((page * 128) + reg)
+    /* Page */
+#define TAS2770_PAGE  TAS2770_REG(0X0, 0x00)
+#define TAS2770_PAGE_PAGE_MASK  255
+    /* Software Reset */
+#define TAS2770_SW_RST  TAS2770_REG(0X0, 0x01)
+#define TAS2770_RST  BIT(0)
+    /* Power Control */
+#define TAS2770_PWR_CTRL  TAS2770_REG(0X0, 0x02)
+#define TAS2770_PWR_CTRL_MASK  0x3
+#define TAS2770_PWR_CTRL_ACTIVE  0x0
+#define TAS2770_PWR_CTRL_MUTE  BIT(0)
+#define TAS2770_PWR_CTRL_SHUTDOWN  0x2
+    /* Playback Configuration Reg0 */
+#define TAS2770_PLAY_CFG_REG0  TAS2770_REG(0X0, 0x03)
+    /* Playback Configuration Reg1 */
+#define TAS2770_PLAY_CFG_REG1  TAS2770_REG(0X0, 0x04)
+    /* Playback Configuration Reg2 */
+#define TAS2770_PLAY_CFG_REG2  TAS2770_REG(0X0, 0x05)
+#define TAS2770_PLAY_CFG_REG2_VMAX 0xc9
+    /* Misc Configuration Reg0 */
+#define TAS2770_MSC_CFG_REG0  TAS2770_REG(0X0, 0x07)
+    /* TDM Configuration Reg0 */
+#define TAS2770_TDM_CFG_REG0  TAS2770_REG(0X0, 0x0A)
+#define TAS2770_TDM_CFG_REG0_SMP_MASK  BIT(5)
+#define TAS2770_TDM_CFG_REG0_SMP_48KHZ  0x0
+#define TAS2770_TDM_CFG_REG0_SMP_44_1KHZ  BIT(5)
+#define TAS2770_TDM_CFG_REG0_31_MASK  0xe
+#define TAS2770_TDM_CFG_REG0_31_44_1_48KHZ  0x6
+#define TAS2770_TDM_CFG_REG0_31_88_2_96KHZ  0x8
+#define TAS2770_TDM_CFG_REG0_31_176_4_192KHZ  0xa
+    /* TDM Configuration Reg1 */
+#define TAS2770_TDM_CFG_REG1  TAS2770_REG(0X0, 0x0B)
+#define TAS2770_TDM_CFG_REG1_MASK 0x3e
+#define TAS2770_TDM_CFG_REG1_51_SHIFT  1
+#define TAS2770_TDM_CFG_REG1_RX_MASK  BIT(0)
+#define TAS2770_TDM_CFG_REG1_RX_RSING  0x0
+#define TAS2770_TDM_CFG_REG1_RX_FALING  BIT(0)
+    /* TDM Configuration Reg2 */
+#define TAS2770_TDM_CFG_REG2  TAS2770_REG(0X0, 0x0C)
+#define TAS2770_TDM_CFG_REG2_RXW_MASK  0xc
+#define TAS2770_TDM_CFG_REG2_RXW_16BITS  0x0
+#define TAS2770_TDM_CFG_REG2_RXW_24BITS  0x8
+#define TAS2770_TDM_CFG_REG2_RXW_32BITS  0xc
+#define TAS2770_TDM_CFG_REG2_RXS_MASK    0x3
+#define TAS2770_TDM_CFG_REG2_RXS_16BITS  0x0
+#define TAS2770_TDM_CFG_REG2_RXS_24BITS  BIT(0)
+#define TAS2770_TDM_CFG_REG2_RXS_32BITS  0x2
+    /* TDM Configuration Reg3 */
+#define TAS2770_TDM_CFG_REG3  TAS2770_REG(0X0, 0x0D)
+#define TAS2770_TDM_CFG_REG3_RXS_MASK  0xf0
+#define TAS2770_TDM_CFG_REG3_RXS_SHIFT 0x4
+#define TAS2770_TDM_CFG_REG3_30_MASK  0xf
+#define TAS2770_TDM_CFG_REG3_30_SHIFT 0
+    /* TDM Configuration Reg5 */
+#define TAS2770_TDM_CFG_REG5  TAS2770_REG(0X0, 0x0F)
+#define TAS2770_TDM_CFG_REG5_VSNS_MASK  BIT(6)
+#define TAS2770_TDM_CFG_REG5_VSNS_ENABLE  BIT(6)
+#define TAS2770_TDM_CFG_REG5_50_MASK  0x3f
+    /* TDM Configuration Reg6 */
+#define TAS2770_TDM_CFG_REG6  TAS2770_REG(0X0, 0x10)
+#define TAS2770_TDM_CFG_REG6_ISNS_MASK  BIT(6)
+#define TAS2770_TDM_CFG_REG6_ISNS_ENABLE  BIT(6)
+#define TAS2770_TDM_CFG_REG6_50_MASK  0x3f
+    /* Brown Out Prevention Reg0 */
+#define TAS2770_BO_PRV_REG0  TAS2770_REG(0X0, 0x1B)
+    /* Interrupt MASK Reg0 */
+#define TAS2770_INT_MASK_REG0  TAS2770_REG(0X0, 0x20)
+#define TAS2770_INT_MASK_REG0_DISABLE 0xff
+    /* Interrupt MASK Reg1 */
+#define TAS2770_INT_MASK_REG1  TAS2770_REG(0X0, 0x21)
+#define TAS2770_INT_MASK_REG1_DISABLE 0xff
+    /* Live-Interrupt Reg0 */
+#define TAS2770_LVE_INT_REG0  TAS2770_REG(0X0, 0x22)
+    /* Live-Interrupt Reg1 */
+#define TAS2770_LVE_INT_REG1  TAS2770_REG(0X0, 0x23)
+    /* Latched-Interrupt Reg0 */
+#define TAS2770_LAT_INT_REG0  TAS2770_REG(0X0, 0x24)
+#define TAS2770_LAT_INT_REG0_OCE_FLG  BIT(1)
+#define TAS2770_LAT_INT_REG0_OTE_FLG  BIT(0)
+    /* Latched-Interrupt Reg1 */
+#define TAS2770_LAT_INT_REG1  TAS2770_REG(0X0, 0x25)
+#define TAS2770_LAT_INT_REG1_VBA_TOV  BIT(3)
+#define TAS2770_LAT_INT_REG1_VBA_TUV  BIT(2)
+#define TAS2770_LAT_INT_REG1_BOUT_FLG  BIT(1)
+    /* VBAT MSB */
+#define TAS2770_VBAT_MSB  TAS2770_REG(0X0, 0x27)
+    /* VBAT LSB */
+#define TAS2770_VBAT_LSB  TAS2770_REG(0X0, 0x28)
+    /* TEMP MSB */
+#define TAS2770_TEMP_MSB  TAS2770_REG(0X0, 0x29)
+    /* TEMP LSB */
+#define TAS2770_TEMP_LSB  TAS2770_REG(0X0, 0x2A)
+    /* Interrupt Configuration */
+#define TAS2770_INT_CFG  TAS2770_REG(0X0, 0x30)
+    /* Misc IRQ */
+#define TAS2770_MISC_IRQ  TAS2770_REG(0X0, 0x32)
+    /* Clock Configuration */
+#define TAS2770_CLK_CGF  TAS2770_REG(0X0, 0x3C)
+    /* TDM Clock detection monitor */
+#define TAS2770_TDM_CLK_DETC  TAS2770_REG(0X0, 0x77)
+    /* Revision and PG ID */
+#define TAS2770_REV_AND_GPID  TAS2770_REG(0X0, 0x7D)
+
+#define TAS2770_POWER_ACTIVE 0
+#define TAS2770_POWER_MUTE 1
+#define TAS2770_POWER_SHUTDOWN 2
+#define ERROR_OVER_CURRENT  0x0000001
+#define ERROR_DIE_OVERTEMP  0x0000002
+#define ERROR_OVER_VOLTAGE  0x0000004
+#define ERROR_UNDER_VOLTAGE 0x0000008
+#define ERROR_BROWNOUT      0x0000010
+#define ERROR_CLASSD_PWR    0x0000020
+#define TAS2770_SLOT_16BIT  16
+#define TAS2770_SLOT_32BIT  32
+#define TAS2770_I2C_RETRY_COUNT      3
+#define ERROR_I2C_FAILED            -2
+
+struct tas2770_register {
+ int book;
+ int page;
+ int reg;
+};
+
+struct tas2770_dai_cfg {
+ unsigned int dai_fmt;
+ unsigned int tdm_delay;
+};
+
+struct tas2770_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
+ struct mutex dev_lock;
+ struct hrtimer mtimer;
+ int mn_clkin;
+ int mn_clkid;
+ int mn_power_state;
+ int mn_current_book;
+ int mn_current_page;
+ int mn_load;
+ int mn_asi_format;
+ struct gpio_desc *mn_reset_gpio;
+ int mn_irq;
+ bool mb_irq_enable;
+ int mn_sampling_rate;
+ int mn_frame_size;
+ int mn_pll;
+ int mn_left_slot;
+ int mn_right_slot;
+ int mn_ch_size;
+ int mn_slot_width;
+ int mn_v_mon_slot_no;
+ int mn_i_mon_slot_no;
+ bool mb_runtime_suspend;
+ unsigned int mn_err_code;
+ struct mutex codec_lock;
+};
+
+#endif /* __TAS2770__ */
-- 
2.7.4





Frank Shi 石扶 
Thunder Software Technology Co., Ltd. 中科创达软件股份有限公司 
Mobile: +86-13002957678 | Email: shifu0704@thundersoft.com 
Add: 10F, #1, Digital China Technology Park, No.20, Zhangba Rd. 4th, High-tech Zone, Xi'an City, Shaanxi Province, 710075, P.R. China 
西安市高新区丈八四路20号神州数码科技园1号楼10层,710075 
www.thundersoft.com 


CONFIDENTIALITY. This e-mail and its attachments are intended only for the person or entity whose address is listed above and may contain confidential and/or privileged material. Any retransmission, dissemination or other use of this information by persons or entities other than the intended recipient is prohibited. If you received this e-mail in error, please do not disclose the contents to anyone, but notify the sender by email immediately, and delete this e-mail and any attachments from your system. Thank you. 

保密提示。本电子邮件及其附件仅适用于上述地址的个人或实体,并可能包含机密和/或特许资料。禁止对目标收件人以外的个人或实体进行此类信息 的任何转播,传播或其他使用。如果您误收了此电子邮件,请不要向任何人透露这些内容,而应立即以邮件形式通知发件人,并从您的系统中删除本邮 件和任何附件。谢谢。 

_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
https://mailman.alsa-project.org/mailman/listinfo/alsa-devel

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

* Re: [alsa-devel] [PATCH]Add tas2770 driver code
  2019-09-05  5:35 [alsa-devel] [PATCH]Add tas2770 driver code shifu0704
@ 2019-09-05 17:00 ` Mark Brown
  0 siblings, 0 replies; 2+ messages in thread
From: Mark Brown @ 2019-09-05 17:00 UTC (permalink / raw)
  To: shifu0704
  Cc: alsa-devel, lgirdwood, linux-kernel, Navada Kanyana, Mukund,
	tiwai, Dan?Murphy


[-- Attachment #1.1: Type: text/plain, Size: 952 bytes --]

On Thu, Sep 05, 2019 at 01:35:04PM +0800, shifu0704@thundersoft.com wrote:
> From 5b539e7dbebebf7e58c38a6b54f222bc116d63f3 Mon Sep 17 00:00:00 2001
> From: Shi Fu <shifu0704@thundersoft.com>
> Date: Mon, 1 Jul 2019 11:00:13 +0800
> Signed-off-by: Shi Fu <shifu0704@thundersoft.com>
> Subject: [PATCH v5] dt: tas2770: Add tas2770 smart PA dt bindings.
> 
> Add tas2770 smart PA dt bindings.
> ---

This looks like it's a whole patch series sent as a single mail, and
there's some confusing with things like Signed-off-by and general
formatting.  Please see submitting-patches.rst in the kernel source for
how to send patches, it's worth taking a look at other posts on the list
and trying to ensure that your mails look the same as them.  As things
stand it's really hard to read this since whatever happened with the way
this has been sent seems to have mangled the indention so it doesn't
look like normal kernel code unfortunately.

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

[-- Attachment #2: Type: text/plain, Size: 161 bytes --]

_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
https://mailman.alsa-project.org/mailman/listinfo/alsa-devel

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

end of thread, other threads:[~2019-09-05 17:48 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-05  5:35 [alsa-devel] [PATCH]Add tas2770 driver code shifu0704
2019-09-05 17:00 ` 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).