Devicetree
 help / color / mirror / Atom feed
* [PATCH 0/2] ASoC: codecs: Add Everest ES7202 support
@ 2026-10-20 14:31 Jun Yan
  2026-10-20 14:31 ` [PATCH 1/2] dt-bindings: sound: add Everest ES7202 Jun Yan
  2026-10-20 14:31 ` [PATCH 2/2] ASoC: codecs: add support for Everest ES7202 PDM ADC Jun Yan
  0 siblings, 2 replies; 4+ messages in thread
From: Jun Yan @ 2026-10-20 14:31 UTC (permalink / raw)
  To: lgirdwood, broonie, robh, krzk+dt, conor+dt, perex, tiwai, heiko
  Cc: linux-sound, devicetree, linux-kernel, linux-arm-kernel,
	linux-rockchip, Jun Yan

This series adds support for the Everest ES7202, a 2-channel PDM
analog-to-digital converter controlled over I2C. Patch 1 adds the DT
bindings and patch 2 the ASoC codec driver.

The driver is based on the Rockchip BSP driver, reworked for mainline to
use regmap and devm_ helpers, probe the device ID, and report the actual
per-step PGA gain.

Jun Yan (2):
  dt-bindings: sound: add Everest ES7202
  ASoC: codecs: add support for Everest ES7202 PDM ADC

 .../bindings/sound/everest,es7202.yaml        |  56 +++
 sound/soc/codecs/Kconfig                      |  10 +
 sound/soc/codecs/Makefile                     |   2 +
 sound/soc/codecs/es7202.c                     | 372 ++++++++++++++++++
 sound/soc/codecs/es7202.h                     | 133 +++++++
 5 files changed, 573 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/everest,es7202.yaml
 create mode 100644 sound/soc/codecs/es7202.c
 create mode 100644 sound/soc/codecs/es7202.h

--
2.56.0


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

* [PATCH 1/2] dt-bindings: sound: add Everest ES7202
  2026-10-20 14:31 [PATCH 0/2] ASoC: codecs: Add Everest ES7202 support Jun Yan
@ 2026-10-20 14:31 ` Jun Yan
  2026-10-20 14:31 ` [PATCH 2/2] ASoC: codecs: add support for Everest ES7202 PDM ADC Jun Yan
  1 sibling, 0 replies; 4+ messages in thread
From: Jun Yan @ 2026-10-20 14:31 UTC (permalink / raw)
  To: lgirdwood, broonie, robh, krzk+dt, conor+dt, perex, tiwai, heiko,
	Jun Yan
  Cc: linux-sound, devicetree, linux-kernel, linux-arm-kernel,
	linux-rockchip

Add the device-tree bindings for the Everest ES7202, a 2-channel PDM
analog-to-digital converter controlled over I2C.

Signed-off-by: Jun Yan <jerrysteve1101@gmail.com>
---
 .../bindings/sound/everest,es7202.yaml        | 56 +++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/everest,es7202.yaml

diff --git a/Documentation/devicetree/bindings/sound/everest,es7202.yaml b/Documentation/devicetree/bindings/sound/everest,es7202.yaml
new file mode 100644
index 000000000000..ce98d2d600d4
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/everest,es7202.yaml
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/everest,es7202.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Everest ES7202 2 channel PDM analog to digital converter
+
+maintainers:
+  - Jun Yan <jerrysteve1101@gmail.com>
+
+properties:
+  compatible:
+    enum:
+      - everest,es7202
+
+  reg:
+    maxItems: 1
+
+  reset-gpios:
+    maxItems: 1
+    description: GPIO connected to the reset pin (active low)
+
+  vdd-supply:
+    description: Main power supply, 1.8 V or 3.3 V. Optional; defaults to 3.3 V
+
+  "#sound-dai-cells":
+    const: 0
+
+required:
+  - compatible
+  - reg
+  - "#sound-dai-cells"
+
+allOf:
+  - $ref: dai-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        es7202: codec@30 {
+            compatible = "everest,es7202";
+            reg = <0x30>;
+            #sound-dai-cells = <0>;
+            reset-gpios = <&gpio0 0 GPIO_ACTIVE_LOW>;
+            vdd-supply = <&vcc3v3_sys>;
+        };
+    };
+...
-- 
2.56.0


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

* [PATCH 2/2] ASoC: codecs: add support for Everest ES7202 PDM ADC
  2026-10-20 14:31 [PATCH 0/2] ASoC: codecs: Add Everest ES7202 support Jun Yan
  2026-10-20 14:31 ` [PATCH 1/2] dt-bindings: sound: add Everest ES7202 Jun Yan
@ 2026-10-20 14:31 ` Jun Yan
  2026-10-20 14:49   ` sashiko-bot
  1 sibling, 1 reply; 4+ messages in thread
From: Jun Yan @ 2026-10-20 14:31 UTC (permalink / raw)
  To: lgirdwood, broonie, robh, krzk+dt, conor+dt, perex, tiwai, heiko
  Cc: linux-sound, devicetree, linux-kernel, linux-arm-kernel,
	linux-rockchip, Jun Yan

Add support for the Everest ES7202, a 2-channel PDM analog-to-digital
converter controlled over I2C. The device outputs PDM data to the SoC
PDM controller and is configured through registers for supply voltage,
analog front-end and per-channel PGA gain.

Based on the Rockchip BSP driver[1] by Binyuan Lan <lby@rock-chips.com>
(committed by Tao Huang <huangtao@rock-chips.com>), originally written
by David Yang <yangxiaohua@everest-semi.com>.

[1] https://github.com/rockchip-linux/kernel/blob/develop-6.1/sound/soc/codecs/es7202.c

Link: https://archive.org/details/es-7202-ds
Signed-off-by: Jun Yan <jerrysteve1101@gmail.com>
---
 sound/soc/codecs/Kconfig  |  10 +
 sound/soc/codecs/Makefile |   2 +
 sound/soc/codecs/es7202.c | 372 ++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/es7202.h | 133 ++++++++++++++
 4 files changed, 517 insertions(+)
 create mode 100644 sound/soc/codecs/es7202.c
 create mode 100644 sound/soc/codecs/es7202.h

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index ba59dabeeccd..c1eeebd12cf0 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -1274,6 +1274,16 @@ config SND_SOC_HDMI_CODEC
 config SND_SOC_ES7134
 	tristate "Everest Semi ES7134 CODEC"
 
+config SND_SOC_ES7202
+	tristate "Everest Semi ES7202 CODEC"
+	depends on I2C
+	select REGMAP_I2C
+	help
+	  Enable support for the Everest Semiconductor ES7202, a 2-channel
+	  PDM analog-to-digital converter controlled over I2C. The device
+	  outputs PDM data to a SoC PDM controller and is configured through
+	  registers for supply voltage, analog front-end and PGA gain.
+
 config SND_SOC_ES7241
 	tristate "Everest Semi ES7241 CODEC"
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 3826f9e4fce8..2a56fad9073a 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -129,6 +129,7 @@ snd-soc-da732x-y := da732x.o
 snd-soc-da9055-y := da9055.o
 snd-soc-dmic-y := dmic.o
 snd-soc-es7134-y := es7134.o
+snd-soc-es7202-y := es7202.o
 snd-soc-es7241-y := es7241.o
 snd-soc-es83xx-dsm-common-y := es83xx-dsm-common.o
 snd-soc-es8311-y := es8311.o
@@ -577,6 +578,7 @@ obj-$(CONFIG_SND_SOC_DA732X)	+= snd-soc-da732x.o
 obj-$(CONFIG_SND_SOC_DA9055)	+= snd-soc-da9055.o
 obj-$(CONFIG_SND_SOC_DMIC)	+= snd-soc-dmic.o
 obj-$(CONFIG_SND_SOC_ES7134)	+= snd-soc-es7134.o
+obj-$(CONFIG_SND_SOC_ES7202)	+= snd-soc-es7202.o
 obj-$(CONFIG_SND_SOC_ES7241)	+= snd-soc-es7241.o
 obj-$(CONFIG_SND_SOC_ES83XX_DSM_COMMON)    += snd-soc-es83xx-dsm-common.o
 obj-$(CONFIG_SND_SOC_ES8311)    += snd-soc-es8311.o
diff --git a/sound/soc/codecs/es7202.c b/sound/soc/codecs/es7202.c
new file mode 100644
index 000000000000..c30b0123c91f
--- /dev/null
+++ b/sound/soc/codecs/es7202.c
@@ -0,0 +1,372 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright Everest Semiconductor Co., Ltd.
+ *
+ * Author: David Yang <yangxiaohua@everest-semi.com>
+ * Author: Jun Yan <jerrysteve1101@gmail.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include "es7202.h"
+
+/*
+ * The main supply voltage selects the analog front-end configuration:
+ * below this threshold the device is configured for 1.8 V, otherwise 3.3 V.
+ */
+#define ES7202_SUPPLY_1V8_MAX_UV	1980000
+#define ES7202_SUPPLY_3V3_DEFAULT_UV	3300000
+
+struct es7202_priv {
+	struct regmap *regmap;
+	struct regulator *supply;
+	struct gpio_desc *reset_gpio;
+	unsigned int supply_uv;
+};
+
+static bool es7202_volatile_reg(struct device *dev, unsigned int reg)
+{
+	/* status and device-id registers are read-only, never cached */
+	return reg == ES7202_CHIP_STA ||
+	       reg == ES7202_DEV_ID1 ||
+	       reg == ES7202_DEV_ID0;
+}
+
+static const struct reg_default es7202_reg_defaults[] = {
+	{ ES7202_RESET,		0x10 },
+	{ ES7202_SOFT_MODE,	0x00 },
+	{ ES7202_CLK_DIV,	0x04 },
+	{ ES7202_CLK_EN,	0x00 },
+	{ ES7202_T1_VMID,	0x01 },
+	{ ES7202_T2_VMID,	0x18 },
+	{ ES7202_PDM_INF_CTL,	0x30 },
+	{ ES7202_MISC_CTL,	0x02 },
+	{ ES7202_ANALOG_EN,	0xff },
+	{ ES7202_BIAS_VMID,	0x0c },
+	{ ES7202_PGA1_BIAS,	0x55 },
+	{ ES7202_PGA2_BIAS,	0x55 },
+	{ ES7202_MOD1_BIAS,	0x8c },
+	{ ES7202_MOD2_BIAS,	0x33 },
+	{ ES7202_VREFP_BIAS,	0x33 },
+	{ ES7202_VMMOD_BIAS,	0x33 },
+	{ ES7202_MODS_BIAS,	0x44 },
+	{ ES7202_ANALOG_LP1,	0x00 },
+	{ ES7202_ANALOG_LP2,	0x00 },
+	{ ES7202_ANALOG_MISC1,	0x00 },
+	{ ES7202_ANALOG_MISC2,	0xf8 },
+	{ ES7202_PGA1,		0x18 },
+	{ ES7202_PGA2,		0x18 },
+};
+
+static const struct regmap_config es7202_regmap = {
+	.reg_bits	= 8,
+	.val_bits	= 8,
+	.max_register	= ES7202_DEV_ID0,
+	.reg_defaults	= es7202_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(es7202_reg_defaults),
+	.volatile_reg	= es7202_volatile_reg,
+	.cache_type	= REGCACHE_MAPLE,
+};
+
+/*
+ * PGA gain per the datasheet: code 0 is 0 dB, codes 1..10 step +3 dB from
+ * 2.25 dB, and codes 11 and 12 are 30.75 dB and 32.25 dB respectively.  As
+ * the steps are not uniform, a linear TLV (the vendor driver's
+ * DECLARE_TLV_DB_SCALE(mic_boost_tlv, 0, 300, 0)) would misreport the gain,
+ * so use a piecewise range matching the datasheet instead.
+ */
+static const DECLARE_TLV_DB_RANGE(mic_boost_tlv,
+	0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+	1, 10, TLV_DB_SCALE_ITEM(225, 300, 0),
+	11, 11, TLV_DB_SCALE_ITEM(3075, 0, 0),
+	12, 12, TLV_DB_SCALE_ITEM(3225, 0, 0),
+);
+
+static const struct snd_kcontrol_new es7202_controls[] = {
+	SOC_SINGLE_TLV("PGA1 Capture Volume", ES7202_PGA1, 0, 0x0c, 0,
+		       mic_boost_tlv),
+	SOC_SINGLE_TLV("PGA2 Capture Volume", ES7202_PGA2, 0, 0x0c, 0,
+		       mic_boost_tlv),
+};
+
+static const struct snd_soc_dapm_widget es7202_dapm_widgets[] = {
+	SND_SOC_DAPM_INPUT("MIC1"),
+	SND_SOC_DAPM_INPUT("MIC2"),
+	SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route es7202_dapm_routes[] = {
+	{ "ADC", NULL, "MIC1" },
+	{ "ADC", NULL, "MIC2" },
+};
+
+static int es7202_mute_pdm(struct es7202_priv *es7202, bool mute)
+{
+	return regmap_update_bits(es7202->regmap, ES7202_PDM_INF_CTL,
+				  ES7202_PDM_INF_CTL_MUTE,
+				  mute ? ES7202_PDM_INF_CTL_MUTE : 0);
+}
+
+static int es7202_hw_init(struct es7202_priv *es7202)
+{
+	struct regmap *regmap = es7202->regmap;
+	bool is_1v8 = es7202->supply_uv > 0 &&
+		      es7202->supply_uv <= ES7202_SUPPLY_1V8_MAX_UV;
+	/*
+	 * Power-up sequence from the vendor driver.
+	 * The analog front-end is configured for the actual supply voltage
+	 * (1.8 V vs 3.3 V) and the analog blocks are ramped out of
+	 * power-down in stages.
+	 */
+	const struct reg_sequence init_seq[] = {
+		{ ES7202_SOFT_MODE,	ES7202_MODE_SOFT_MODE_SEL },
+		{ ES7202_ANALOG_MISC1,	ES7202_ANALOG_MISC1_OFFSETSEL1 |
+					(is_1v8 ? 0 : ES7202_ANALOG_MISC1_SUPPLYSEL) },
+
+		/* 30.75 dB gain */
+		{ ES7202_PGA1,		ES7202_PGA_SELMIC1 |
+					FIELD_PREP(ES7202_PGA_MIC1_GAIN, 11) },
+		{ ES7202_PGA2,		ES7202_PGA_SELMIC2 |
+					FIELD_PREP(ES7202_PGA_MIC2_GAIN, 11) },
+
+		/* analog on, rest powered down */
+		{ ES7202_ANALOG_EN,	ES7202_ANALOG_EN_PDN_ADCVREFGEN |
+					ES7202_ANALOG_EN_MODTOP1_RST |
+					ES7202_ANALOG_EN_MODTOP2_RST |
+					ES7202_ANALOG_EN_PDN_MOD1 |
+					ES7202_ANALOG_EN_PDN_MOD2 |
+					ES7202_ANALOG_EN_PDN_PGA1 |
+					ES7202_ANALOG_EN_PDN_PGA2 },
+
+		{ ES7202_BIAS_VMID,	FIELD_PREP(ES7202_BIAS_ADCBIAS_SWH1, 3) |
+					ES7202_BIAS_ENDITHER |
+					FIELD_PREP(ES7202_BIAS_VMIDSEL, 3) },
+
+		/* VREFGEN on; 3.3 V also drops MODTOP reset */
+		{ ES7202_ANALOG_EN,	is_1v8 ?
+					(ES7202_ANALOG_EN_MODTOP1_RST |
+					 ES7202_ANALOG_EN_MODTOP2_RST |
+					 ES7202_ANALOG_EN_PDN_MOD1 |
+					 ES7202_ANALOG_EN_PDN_MOD2 |
+					 ES7202_ANALOG_EN_PDN_PGA1 |
+					 ES7202_ANALOG_EN_PDN_PGA2) :
+					(ES7202_ANALOG_EN_PDN_MOD1 |
+					 ES7202_ANALOG_EN_PDN_MOD2 |
+					 ES7202_ANALOG_EN_PDN_PGA1 |
+					 ES7202_ANALOG_EN_PDN_PGA2) },
+
+		{ ES7202_ANALOG_EN,	0 },
+		{ ES7202_MOD1_BIAS,	FIELD_PREP(ES7202_MOD1_BIAS_SW0, 5) |
+					FIELD_PREP(ES7202_MOD1_BIAS_SW1, 8) },
+		{ ES7202_CLK_DIV,	FIELD_PREP(ES7202_CLK_DIV_MCLK_DIV, 1) },
+		{ ES7202_T2_VMID,	0x01 },
+		{ ES7202_MISC_CTL,	ES7202_MISC_DSM_DITHER_ON },
+		{ ES7202_RESET,		ES7202_RESET_CSM_ON },
+		{ ES7202_CLK_EN,	ES7202_CLK_ANA_CLK_ON | ES7202_CLK_MCLK_ON },
+		{ ES7202_BIAS_VMID,	FIELD_PREP(ES7202_BIAS_ADCBIAS_SWH1, 3) |
+					ES7202_BIAS_ENDITHER |
+					FIELD_PREP(ES7202_BIAS_VMIDSEL, 2) },
+	};
+	int ret;
+
+	ret = regmap_multi_reg_write(regmap, init_seq, ARRAY_SIZE(init_seq));
+	if (ret)
+		return ret;
+
+	return es7202_mute_pdm(es7202, false);
+}
+
+static int es7202_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+	struct snd_soc_component *component = dai->component;
+	struct es7202_priv *es7202 = snd_soc_component_get_drvdata(component);
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+		return 0;
+
+	return es7202_mute_pdm(es7202, mute);
+}
+
+static const struct snd_soc_dai_ops es7202_dai_ops = {
+	.mute_stream = es7202_mute_stream,
+};
+
+static struct snd_soc_dai_driver es7202_dai = {
+	.name = "es7202-hifi",
+	.capture = {
+		.stream_name	= "Capture",
+		.channels_min	= 1,
+		.channels_max	= 2,
+		.rates		= SNDRV_PCM_RATE_8000_96000,
+		.formats	= SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = &es7202_dai_ops,
+};
+
+static int es7202_suspend(struct snd_soc_component *component)
+{
+	struct es7202_priv *es7202 = snd_soc_component_get_drvdata(component);
+
+	return es7202_mute_pdm(es7202, true);
+}
+
+static int es7202_resume(struct snd_soc_component *component)
+{
+	struct es7202_priv *es7202 = snd_soc_component_get_drvdata(component);
+	int ret;
+
+	regcache_mark_dirty(es7202->regmap);
+	ret = regcache_sync(es7202->regmap);
+	if (ret)
+		return ret;
+
+	return es7202_mute_pdm(es7202, false);
+}
+
+static const struct snd_soc_component_driver es7202_component_driver = {
+	.controls		= es7202_controls,
+	.num_controls		= ARRAY_SIZE(es7202_controls),
+	.dapm_widgets		= es7202_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(es7202_dapm_widgets),
+	.dapm_routes		= es7202_dapm_routes,
+	.num_dapm_routes	= ARRAY_SIZE(es7202_dapm_routes),
+	.suspend		= es7202_suspend,
+	.resume			= es7202_resume,
+	.idle_bias_on		= 1,
+	.endianness		= 1,
+};
+
+static void es7202_disable_supply(void *data)
+{
+	regulator_disable(data);
+}
+
+static int es7202_i2c_probe(struct i2c_client *i2c)
+{
+	struct device *dev = &i2c->dev;
+	struct es7202_priv *es7202;
+	unsigned int id1, id0, device_id;
+	int ret;
+
+	es7202 = devm_kzalloc(dev, sizeof(*es7202), GFP_KERNEL);
+	if (!es7202)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, es7202);
+
+	es7202->regmap = devm_regmap_init_i2c(i2c, &es7202_regmap);
+	if (IS_ERR(es7202->regmap))
+		return dev_err_probe(dev, PTR_ERR(es7202->regmap),
+				     "failed to init regmap\n");
+
+	es7202->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+						     GPIOD_OUT_HIGH);
+	if (IS_ERR(es7202->reset_gpio))
+		return dev_err_probe(dev, PTR_ERR(es7202->reset_gpio),
+				     "failed to get reset gpio\n");
+
+	es7202->supply = devm_regulator_get_optional(dev, "vdd");
+	if (IS_ERR(es7202->supply)) {
+		if (PTR_ERR(es7202->supply) != -ENODEV)
+			return dev_err_probe(dev, PTR_ERR(es7202->supply),
+					     "failed to get supply\n");
+
+		es7202->supply = NULL;
+		es7202->supply_uv = ES7202_SUPPLY_3V3_DEFAULT_UV;
+	} else {
+		ret = regulator_enable(es7202->supply);
+		if (ret)
+			return dev_err_probe(dev, ret,
+					     "failed to enable supply\n");
+
+		ret = devm_add_action_or_reset(dev, es7202_disable_supply,
+					       es7202->supply);
+		if (ret)
+			return dev_err_probe(dev, ret,
+					     "failed to add supply disable action\n");
+
+		es7202->supply_uv = regulator_get_voltage(es7202->supply);
+		if (es7202->supply_uv < 0)
+			es7202->supply_uv = ES7202_SUPPLY_3V3_DEFAULT_UV;
+	}
+
+	/* take the device out of reset */
+	gpiod_set_value_cansleep(es7202->reset_gpio, 0);
+	usleep_range(1000, 2000);
+
+	/* verify the chip is present by reading its device id */
+	ret = regmap_read(es7202->regmap, ES7202_DEV_ID1, &id1);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to read device id\n");
+	ret = regmap_read(es7202->regmap, ES7202_DEV_ID0, &id0);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to read device id\n");
+
+	device_id = (id1 << 8) | id0;
+	if (device_id != ES7202_DEVICE_ID)
+		return dev_err_probe(dev, -ENODEV,
+				     "unexpected device id 0x%04x, expected 0x%04x\n",
+				     device_id, ES7202_DEVICE_ID);
+
+	ret = es7202_hw_init(es7202);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to init device\n");
+
+	return devm_snd_soc_register_component(dev, &es7202_component_driver,
+					       &es7202_dai, 1);
+}
+
+static void es7202_i2c_remove(struct i2c_client *i2c)
+{
+	struct es7202_priv *es7202 = i2c_get_clientdata(i2c);
+
+	es7202_mute_pdm(es7202, true);
+}
+
+static void es7202_i2c_shutdown(struct i2c_client *i2c)
+{
+	struct es7202_priv *es7202 = i2c_get_clientdata(i2c);
+
+	es7202_mute_pdm(es7202, true);
+}
+
+static const struct of_device_id es7202_of_match[] = {
+	{ .compatible = "everest,es7202" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, es7202_of_match);
+
+static const struct i2c_device_id es7202_i2c_id[] = {
+	{ "es7202" },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, es7202_i2c_id);
+
+static struct i2c_driver es7202_i2c_driver = {
+	.driver = {
+		.name		= "es7202",
+		.of_match_table	= of_match_ptr(es7202_of_match),
+	},
+	.probe		= es7202_i2c_probe,
+	.remove		= es7202_i2c_remove,
+	.shutdown	= es7202_i2c_shutdown,
+	.id_table	= es7202_i2c_id,
+};
+module_i2c_driver(es7202_i2c_driver);
+
+MODULE_DESCRIPTION("Everest Semi ES7202 PDM ADC ALSA SoC Codec Driver");
+MODULE_AUTHOR("David Yang <yangxiaohua@everest-semi.com>");
+MODULE_AUTHOR("Jun Yan <jerrysteve1101@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es7202.h b/sound/soc/codecs/es7202.h
new file mode 100644
index 000000000000..15811b73c20a
--- /dev/null
+++ b/sound/soc/codecs/es7202.h
@@ -0,0 +1,133 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright Everest Semiconductor Co., Ltd.
+ *
+ * Author: David Yang <yangxiaohua@everest-semi.com>
+ * Author: Jun Yan <jerrysteve1101@gmail.com>
+ */
+
+#ifndef _ES7202_H
+#define _ES7202_H
+
+#include <linux/bits.h>
+
+/* Register addresses */
+#define ES7202_RESET		0x00
+#define ES7202_SOFT_MODE	0x01
+#define ES7202_CLK_DIV		0x02
+#define ES7202_CLK_EN		0x03
+#define ES7202_T1_VMID		0x04
+#define ES7202_T2_VMID		0x05
+#define ES7202_CHIP_STA		0x06
+#define ES7202_PDM_INF_CTL	0x07
+#define ES7202_MISC_CTL		0x08
+#define ES7202_ANALOG_EN	0x10
+#define ES7202_BIAS_VMID	0x11
+#define ES7202_PGA1_BIAS	0x12
+#define ES7202_PGA2_BIAS	0x13
+#define ES7202_MOD1_BIAS	0x14
+#define ES7202_MOD2_BIAS	0x15
+#define ES7202_VREFP_BIAS	0x16
+#define ES7202_VMMOD_BIAS	0x17
+#define ES7202_MODS_BIAS	0x18
+#define ES7202_ANALOG_LP1	0x19
+#define ES7202_ANALOG_LP2	0x1a
+#define ES7202_ANALOG_MISC1	0x1b
+#define ES7202_ANALOG_MISC2	0x1c
+#define ES7202_PGA1		0x1d
+#define ES7202_PGA2		0x1e
+#define ES7202_DEV_ID1		0xfd
+#define ES7202_DEV_ID0		0xfe
+
+#define ES7202_DEVICE_ID		0x7201
+
+#define ES7202_RESET_RST_REGS		BIT(5)
+#define ES7202_RESET_RST_DIG		BIT(4)
+#define ES7202_RESET_SEQ_DIS		BIT(1)
+#define ES7202_RESET_CSM_ON		BIT(0)
+
+#define ES7202_MODE_SOFT_MODE_SEL	BIT(0)
+
+#define ES7202_CLK_DIV_MCLK_DIV		GENMASK(3, 0)	/* 0/1 = /16 ... 15 = /240 */
+
+#define ES7202_CLK_MCLK_INVERT		BIT(2)
+#define ES7202_CLK_ANA_CLK_ON		BIT(1)
+#define ES7202_CLK_MCLK_ON		BIT(0)
+
+#define ES7202_CHIP_STA_CSM_STATE	GENMASK(5, 4)
+#define ES7202_CHIP_STA_FORCE_CSM	GENMASK(2, 0)
+
+#define ES7202_PDM_NEG_DATA_VALID	BIT(5)
+#define ES7202_PDM_POS_DATA_VALID	BIT(4)
+#define ES7202_PDM_CHN_CROSS		BIT(3)
+#define ES7202_PDM_IF_DLY		BIT(2)
+#define ES7202_PDM_DATA2_MUTE		BIT(1)
+#define ES7202_PDM_DATA1_MUTE		BIT(0)
+#define ES7202_PDM_INF_CTL_MUTE		(ES7202_PDM_DATA2_MUTE | \
+					 ES7202_PDM_DATA1_MUTE)
+
+#define ES7202_MISC_CLKDET_RST_DIS	BIT(2)
+#define ES7202_MISC_DSM_DITHER_ON	BIT(1)
+#define ES7202_MISC_PDN_CLKDET		BIT(0)
+
+#define ES7202_ANALOG_EN_PDN_ANA	BIT(7)
+#define ES7202_ANALOG_EN_PDN_ADCVREFGEN	BIT(6)
+#define ES7202_ANALOG_EN_MODTOP1_RST	BIT(5)
+#define ES7202_ANALOG_EN_MODTOP2_RST	BIT(4)
+#define ES7202_ANALOG_EN_PDN_MOD1	BIT(3)
+#define ES7202_ANALOG_EN_PDN_MOD2	BIT(2)
+#define ES7202_ANALOG_EN_PDN_PGA1	BIT(1)
+#define ES7202_ANALOG_EN_PDN_PGA2	BIT(0)
+
+#define ES7202_BIAS_ADCBIAS_SWH0	GENMASK(7, 6)
+#define ES7202_BIAS_ENDITHER		BIT(5)
+#define ES7202_BIAS_ADCBIAS_SWH1	GENMASK(3, 2)
+#define ES7202_BIAS_VMIDSEL		GENMASK(1, 0)	/* 0 = off, 1 = 50k, 2 = 500k, 3 = 5k */
+
+/* bias levels 0x12-0x18 (SW0 for 1.8 V, SW1 for 3.3 V) */
+#define ES7202_PGA1_BIAS_SW0		GENMASK(7, 4)
+#define ES7202_PGA1_BIAS_SW1		GENMASK(3, 0)
+#define ES7202_PGA2_BIAS_SW0		GENMASK(7, 4)
+#define ES7202_PGA2_BIAS_SW1		GENMASK(3, 0)
+#define ES7202_MOD1_BIAS_SW0		GENMASK(7, 4)
+#define ES7202_MOD1_BIAS_SW1		GENMASK(3, 0)
+#define ES7202_MOD2_BIAS_SW0		GENMASK(7, 4)
+#define ES7202_MOD2_BIAS_SW1		GENMASK(3, 0)
+#define ES7202_VREFP_BIAS_SW0		GENMASK(7, 4)
+#define ES7202_VREFP_BIAS_SW1		GENMASK(3, 0)
+#define ES7202_VMMOD_BIAS_SW0		GENMASK(7, 4)
+#define ES7202_VMMOD_BIAS_SW1		GENMASK(3, 0)
+#define ES7202_MODS_BIAS_SW0		GENMASK(7, 4)
+#define ES7202_MODS_BIAS_SW1		GENMASK(3, 0)
+
+#define ES7202_LP1_LP_VRP		BIT(7)
+#define ES7202_LP1_LP_VRPOUT		BIT(6)
+#define ES7202_LP1_LP_VMMOD1		BIT(5)
+#define ES7202_LP1_LP_VMMOD2		BIT(4)
+#define ES7202_LP1_LP_MODS1		BIT(3)
+#define ES7202_LP1_LP_MODS2		BIT(2)
+#define ES7202_LP1_LP_MODI1		BIT(1)
+#define ES7202_LP1_LP_MODI2		BIT(0)
+
+#define ES7202_LP2_LP_PGA1		BIT(5)
+#define ES7202_LP2_LP_PGA2		BIT(4)
+#define ES7202_LP2_PDN_DETCT		BIT(3)
+
+#define ES7202_ANALOG_MISC1_OFFSETSEL1	BIT(6)
+#define ES7202_ANALOG_MISC1_OFFSETSEL0	BIT(5)
+#define ES7202_ANALOG_MISC1_SUPPLYSEL	BIT(4)	/* 0 = 1.8 V, 1 = 3.3 V */
+#define ES7202_ANALOG_MISC1_DITHERSEL	GENMASK(2, 0)
+
+#define ES7202_ANALOG_MISC2_VX2OFF_SW0	BIT(7)
+#define ES7202_ANALOG_MISC2_VX1SEL_SW0	BIT(6)
+#define ES7202_ANALOG_MISC2_VMIDLOW_SW0	GENMASK(5, 4)
+#define ES7202_ANALOG_MISC2_VX2OFF_SW1	BIT(3)
+#define ES7202_ANALOG_MISC2_VX1SEL_SW1	BIT(2)
+#define ES7202_ANALOG_MISC2_VMIDLOW_SW1	GENMASK(1, 0)
+
+#define ES7202_PGA_SELMIC1		BIT(4)
+#define ES7202_PGA_MIC1_GAIN		GENMASK(3, 0)
+#define ES7202_PGA_SELMIC2		BIT(4)
+#define ES7202_PGA_MIC2_GAIN		GENMASK(3, 0)
+
+#endif
-- 
2.56.0


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

* Re: [PATCH 2/2] ASoC: codecs: add support for Everest ES7202 PDM ADC
  2026-10-20 14:31 ` [PATCH 2/2] ASoC: codecs: add support for Everest ES7202 PDM ADC Jun Yan
@ 2026-10-20 14:49   ` sashiko-bot
  0 siblings, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-10-20 14:49 UTC (permalink / raw)
  To: Jun Yan; +Cc: devicetree, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Medium] ASoC: es7202: unsigned supply_uv breaks regulator_get_voltage() error check
- [Medium] ASoC: es7202: unmute in es7202_hw_init() skipped due to regmap cache default
- [Medium] ASoC: es7202: broken power-up sequence and mute state in es7202_resume()
- [Low] ASoC: es7202: unused es7202_of_match warning with of_match_ptr() when !CONFIG_OF
--

commit 4a58517803f32764e08604ccc9d3e4df540a41cb
Author: Jun Yan <jerrysteve1101@gmail.com>

ASoC: codecs: add support for Everest ES7202 PDM ADC

This commit adds an ASoC codec driver for the Everest Semiconductor ES7202
2-channel PDM analog-to-digital converter controlled over I2C.

Link: https://archive.org/details/es-7202-ds

> diff --git a/sound/soc/codecs/es7202.c b/sound/soc/codecs/es7202.c
> new file mode 100644
> index 0000000000000..c30b0123c91f8
> --- /dev/null
> +++ b/sound/soc/codecs/es7202.c
> @@ -0,0 +1,372 @@

[ ... ]

> +struct es7202_priv {
> +	struct regmap *regmap;
> +	struct regulator *supply;
> +	struct gpio_desc *reset_gpio;
> +	unsigned int supply_uv;
> +};
> +
> +static bool es7202_volatile_reg(struct device *dev, unsigned int reg)
> +{
> +	/* status and device-id registers are read-only, never cached */
> +	return reg == ES7202_CHIP_STA ||
> +	       reg == ES7202_DEV_ID1 ||
> +	       reg == ES7202_DEV_ID0;
> +}
> +
> +static const struct reg_default es7202_reg_defaults[] = {
> +	{ ES7202_RESET,		0x10 },
> +	{ ES7202_SOFT_MODE,	0x00 },
> +	{ ES7202_CLK_DIV,	0x04 },
> +	{ ES7202_CLK_EN,	0x00 },
> +	{ ES7202_T1_VMID,	0x01 },
> +	{ ES7202_T2_VMID,	0x18 },
> +	{ ES7202_PDM_INF_CTL,	0x30 },

[ ... ]

> +static int es7202_mute_pdm(struct es7202_priv *es7202, bool mute)
> +{
> +	return regmap_update_bits(es7202->regmap, ES7202_PDM_INF_CTL,
> +				  ES7202_PDM_INF_CTL_MUTE,
> +				  mute ? ES7202_PDM_INF_CTL_MUTE : 0);
> +}
> +
> +static int es7202_hw_init(struct es7202_priv *es7202)
> +{
> +	struct regmap *regmap = es7202->regmap;
> +	bool is_1v8 = es7202->supply_uv > 0 &&
> +		      es7202->supply_uv <= ES7202_SUPPLY_1V8_MAX_UV;
> +	/*
> +	 * Power-up sequence from the vendor driver.
> +	 * The analog front-end is configured for the actual supply voltage
> +	 * (1.8 V vs 3.3 V) and the analog blocks are ramped out of
> +	 * power-down in stages.
> +	 */
> +	const struct reg_sequence init_seq[] = {
> +		{ ES7202_SOFT_MODE,	ES7202_MODE_SOFT_MODE_SEL },
> +		{ ES7202_ANALOG_MISC1,	ES7202_ANALOG_MISC1_OFFSETSEL1 |
> +					(is_1v8 ? 0 : ES7202_ANALOG_MISC1_SUPPLYSEL) },
> +
> +		/* 30.75 dB gain */
> +		{ ES7202_PGA1,		ES7202_PGA_SELMIC1 |
> +					FIELD_PREP(ES7202_PGA_MIC1_GAIN, 11) },
> +		{ ES7202_PGA2,		ES7202_PGA_SELMIC2 |
> +					FIELD_PREP(ES7202_PGA_MIC2_GAIN, 11) },
> +
> +		/* analog on, rest powered down */
> +		{ ES7202_ANALOG_EN,	ES7202_ANALOG_EN_PDN_ADCVREFGEN |
> +					ES7202_ANALOG_EN_MODTOP1_RST |
> +					ES7202_ANALOG_EN_MODTOP2_RST |
> +					ES7202_ANALOG_EN_PDN_MOD1 |
> +					ES7202_ANALOG_EN_PDN_MOD2 |
> +					ES7202_ANALOG_EN_PDN_PGA1 |
> +					ES7202_ANALOG_EN_PDN_PGA2 },
> +
> +		{ ES7202_BIAS_VMID,	FIELD_PREP(ES7202_BIAS_ADCBIAS_SWH1, 3) |
> +					ES7202_BIAS_ENDITHER |
> +					FIELD_PREP(ES7202_BIAS_VMIDSEL, 3) },
> +
> +		/* VREFGEN on; 3.3 V also drops MODTOP reset */
> +		{ ES7202_ANALOG_EN,	is_1v8 ?
> +					(ES7202_ANALOG_EN_MODTOP1_RST |
> +					 ES7202_ANALOG_EN_MODTOP2_RST |
> +					 ES7202_ANALOG_EN_PDN_MOD1 |
> +					 ES7202_ANALOG_EN_PDN_MOD2 |
> +					 ES7202_ANALOG_EN_PDN_PGA1 |
> +					 ES7202_ANALOG_EN_PDN_PGA2) :
> +					(ES7202_ANALOG_EN_PDN_MOD1 |
> +					 ES7202_ANALOG_EN_PDN_MOD2 |
> +					 ES7202_ANALOG_EN_PDN_PGA1 |
> +					 ES7202_ANALOG_EN_PDN_PGA2) },
> +
> +		{ ES7202_ANALOG_EN,	0 },
> +		{ ES7202_MOD1_BIAS,	FIELD_PREP(ES7202_MOD1_BIAS_SW0, 5) |
> +					FIELD_PREP(ES7202_MOD1_BIAS_SW1, 8) },
> +		{ ES7202_CLK_DIV,	FIELD_PREP(ES7202_CLK_DIV_MCLK_DIV, 1) },
> +		{ ES7202_T2_VMID,	0x01 },
> +		{ ES7202_MISC_CTL,	ES7202_MISC_DSM_DITHER_ON },
> +		{ ES7202_RESET,		ES7202_RESET_CSM_ON },
> +		{ ES7202_CLK_EN,	ES7202_CLK_ANA_CLK_ON | ES7202_CLK_MCLK_ON },
> +		{ ES7202_BIAS_VMID,	FIELD_PREP(ES7202_BIAS_ADCBIAS_SWH1, 3) |
> +					ES7202_BIAS_ENDITHER |
> +					FIELD_PREP(ES7202_BIAS_VMIDSEL, 2) },
> +	};
> +	int ret;
> +
> +	ret = regmap_multi_reg_write(regmap, init_seq, ARRAY_SIZE(init_seq));
> +	if (ret)
> +		return ret;
> +
> +	return es7202_mute_pdm(es7202, false);
> +}

[Severity: Medium]
Will es7202_mute_pdm(es7202, false) at the end of es7202_hw_init() ever
write to the hardware?

In es7202_reg_defaults[], ES7202_PDM_INF_CTL is initialized in the
REGCACHE_MAPLE cache to 0x30, which already has the ES7202_PDM_INF_CTL_MUTE
bits [1:0] cleared. Because init_seq does not touch ES7202_PDM_INF_CTL or
reset the register file (ES7202_RESET_RST_REGS), regmap_update_bits() in
es7202_mute_pdm(es7202, false) sees that the cached value is already 0x30
and skips the I2C write.

If the driver was previously unbound via es7202_i2c_remove() or the system
went through es7202_i2c_shutdown() (both of which write 0x33 to mute
ES7202_PDM_INF_CTL in hardware) and is then rebound or warm-rebooted on a
board without the optional reset-gpios or a switched vdd-supply, both
es7202_hw_init() and the subsequent es7202_mute_stream() on capture startup
will be skipped by the regmap cache, leaving the hardware muted.

[ ... ]

> +static int es7202_suspend(struct snd_soc_component *component)
> +{
> +	struct es7202_priv *es7202 = snd_soc_component_get_drvdata(component);
> +
> +	return es7202_mute_pdm(es7202, true);
> +}
> +
> +static int es7202_resume(struct snd_soc_component *component)
> +{
> +	struct es7202_priv *es7202 = snd_soc_component_get_drvdata(component);
> +	int ret;
> +
> +	regcache_mark_dirty(es7202->regmap);
> +	ret = regcache_sync(es7202->regmap);
> +	if (ret)
> +		return ret;
> +
> +	return es7202_mute_pdm(es7202, false);
> +}

[Severity: Medium]
Does es7202_resume() properly restore the hardware state and mute status
after system suspend?

In es7202_hw_init(), the chip requires a staged power-up sequence that
enables ES7202_SOFT_MODE, configures ES7202_ANALOG_MISC1 for the supply
voltage, ramps ES7202_ANALOG_EN across three writes while fast-charging VMID
with VMIDSEL=3 in ES7202_BIAS_VMID, and finally switches ES7202_BIAS_VMID to
VMIDSEL=2.

In es7202_resume(), regcache_sync() writes the final cached register values
in ascending address order (0x00 to 0x1e). If the device lost power or was
reset during suspend, this writes ES7202_RESET (0x00) before
ES7202_SOFT_MODE (0x01) and powers on all analog blocks at once in
ES7202_ANALOG_EN (0x10 = 0x00) before ES7202_ANALOG_MISC1 (0x1b) is
configured for 3.3 V, skipping the analog ramp and VMID fast-charge stages.

Additionally, es7202_suspend() calls es7202_mute_pdm(es7202, true), which
updates the cached value of ES7202_PDM_INF_CTL to muted (0x33), and
es7202_resume() then unconditionally calls es7202_mute_pdm(es7202, false),
unmuting the PDM output even if no capture stream was active and the DAI was
muted via es7202_mute_stream() prior to suspend.

[ ... ]

> +static int es7202_i2c_probe(struct i2c_client *i2c)
> +{
> +	struct device *dev = &i2c->dev;
> +	struct es7202_priv *es7202;
> +	unsigned int id1, id0, device_id;
> +	int ret;

[ ... ]

> +	es7202->supply = devm_regulator_get_optional(dev, "vdd");
> +	if (IS_ERR(es7202->supply)) {
> +		if (PTR_ERR(es7202->supply) != -ENODEV)
> +			return dev_err_probe(dev, PTR_ERR(es7202->supply),
> +					     "failed to get supply\n");
> +
> +		es7202->supply = NULL;
> +		es7202->supply_uv = ES7202_SUPPLY_3V3_DEFAULT_UV;
> +	} else {
> +		ret = regulator_enable(es7202->supply);
> +		if (ret)
> +			return dev_err_probe(dev, ret,
> +					     "failed to enable supply\n");
> +
> +		ret = devm_add_action_or_reset(dev, es7202_disable_supply,
> +					       es7202->supply);
> +		if (ret)
> +			return dev_err_probe(dev, ret,
> +					     "failed to add supply disable action\n");
> +
> +		es7202->supply_uv = regulator_get_voltage(es7202->supply);
> +		if (es7202->supply_uv < 0)
> +			es7202->supply_uv = ES7202_SUPPLY_3V3_DEFAULT_UV;
> +	}

[Severity: Medium]
Since es7202->supply_uv is declared as unsigned int in struct es7202_priv,
won't the check:

	es7202->supply_uv = regulator_get_voltage(es7202->supply);
	if (es7202->supply_uv < 0)
		es7202->supply_uv = ES7202_SUPPLY_3V3_DEFAULT_UV;

always evaluate to false?

If regulator_get_voltage() returns a negative error code (such as -EINVAL
when a regulator does not support voltage queries or when CONFIG_REGULATOR is
disabled), storing it directly into unsigned int es7202->supply_uv wraps it
to a large positive value and skips the fallback to
ES7202_SUPPLY_3V3_DEFAULT_UV.

[ ... ]

> +static const struct of_device_id es7202_of_match[] = {
> +	{ .compatible = "everest,es7202" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, es7202_of_match);
> +
> +static const struct i2c_device_id es7202_i2c_id[] = {
> +	{ "es7202" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, es7202_i2c_id);
> +
> +static struct i2c_driver es7202_i2c_driver = {
> +	.driver = {
> +		.name		= "es7202",
> +		.of_match_table	= of_match_ptr(es7202_of_match),
> +	},

[Severity: Low]
When CONFIG_OF is disabled, of_match_ptr(es7202_of_match) expands to NULL
while es7202_of_match is defined unconditionally without __maybe_unused.
Could of_match_ptr() be dropped here to avoid an unused-const-variable
warning when building with !CONFIG_OF?

> +	.probe		= es7202_i2c_probe,
> +	.remove		= es7202_i2c_remove,
> +	.shutdown	= es7202_i2c_shutdown,
> +	.id_table	= es7202_i2c_id,
> +};

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261020143135.623897-1-jerrysteve1101@gmail.com?part=2

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

end of thread, other threads:[~2026-10-20 14:49 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-20 14:31 [PATCH 0/2] ASoC: codecs: Add Everest ES7202 support Jun Yan
2026-10-20 14:31 ` [PATCH 1/2] dt-bindings: sound: add Everest ES7202 Jun Yan
2026-10-20 14:31 ` [PATCH 2/2] ASoC: codecs: add support for Everest ES7202 PDM ADC Jun Yan
2026-10-20 14:49   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox