public inbox for devicetree@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] ASoC: codecs: Add Nuvoton NAU83G60 audio codec driver
@ 2026-04-24  3:39 Neo Chang
  2026-04-24  3:39 ` [PATCH 1/2] ASoC: dt-bindings: nuvoton,nau8360: Add bindings for NAU83G60 Neo Chang
  2026-04-24  3:39 ` [PATCH 2/2] ASoC: codecs: nau8360: Add support for NAU83G60 amplifier Neo Chang
  0 siblings, 2 replies; 7+ messages in thread
From: Neo Chang @ 2026-04-24  3:39 UTC (permalink / raw)
  To: broonie
  Cc: lgirdwood, perex, tiwai, robh, krzk+dt, linux-sound, devicetree,
	alsa-devel, neo.chang70, kchsu0, sjlin0, Neo Chang

This patch series adds support for the Nuvoton NAU83G60 audio codec.

The NAU83G60 is a stereo Class-D amplifier with an integrated DSP and
I/V-sense capabilities. The driver supports I2C register access through
regmap, DAPM widgets and routes, TDM interface configuration, and DSP
firmware loading.

The series is structured as follows:
- Patch 1: Adds the YAML device tree bindings documentation.
- Patch 2: Adds the ASoC codec driver implementationy.

Neo Chang (2):
  ASoC: dt-bindings: nuvoton,nau8360: Add bindings for NAU83G60
  ASoC: codecs: nau8360: Add support for NAU83G60 amplifier

 .../bindings/sound/nuvoton,nau8360.yaml       |  115 +
 sound/soc/codecs/nau8360-dsp.c                |  704 +++++
 sound/soc/codecs/nau8360-dsp.h                |  117 +
 sound/soc/codecs/nau8360.c                    | 2334 +++++++++++++++++
 sound/soc/codecs/nau8360.h                    |  900 +++++++
 5 files changed, 4170 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/nuvoton,nau8360.yaml
 create mode 100644 sound/soc/codecs/nau8360-dsp.c
 create mode 100644 sound/soc/codecs/nau8360-dsp.h
 create mode 100644 sound/soc/codecs/nau8360.c
 create mode 100644 sound/soc/codecs/nau8360.h

-- 
2.25.1


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

* [PATCH 1/2] ASoC: dt-bindings: nuvoton,nau8360: Add bindings for NAU83G60
  2026-04-24  3:39 [PATCH 0/2] ASoC: codecs: Add Nuvoton NAU83G60 audio codec driver Neo Chang
@ 2026-04-24  3:39 ` Neo Chang
  2026-04-25 10:13   ` Krzysztof Kozlowski
  2026-04-24  3:39 ` [PATCH 2/2] ASoC: codecs: nau8360: Add support for NAU83G60 amplifier Neo Chang
  1 sibling, 1 reply; 7+ messages in thread
From: Neo Chang @ 2026-04-24  3:39 UTC (permalink / raw)
  To: broonie
  Cc: lgirdwood, perex, tiwai, robh, krzk+dt, linux-sound, devicetree,
	alsa-devel, neo.chang70, kchsu0, sjlin0, Neo Chang

Add device tree bindings documentation for the Nuvoton NAU83G60
audio amplifier.

Signed-off-by: Neo Chang <YLCHANG2@nuvoton.com>
---
 .../bindings/sound/nuvoton,nau8360.yaml       | 115 ++++++++++++++++++
 1 file changed, 115 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/nuvoton,nau8360.yaml

diff --git a/Documentation/devicetree/bindings/sound/nuvoton,nau8360.yaml b/Documentation/devicetree/bindings/sound/nuvoton,nau8360.yaml
new file mode 100644
index 000000000000..29b10155c4f9
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nuvoton,nau8360.yaml
@@ -0,0 +1,115 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/nuvoton,nau8360.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton NAU83G60 Stereo Class-D Amplifier with DSP
+
+description: |
+  Stereo Class-D Amplifier with DSP and I/V-sense driver.
+  This device supports I2C.
+
+maintainers:
+  - Neo Chang <YLCHANG2@nuvoton.com>
+
+properties:
+  compatible:
+    enum:
+      - nuvoton,nau8360
+
+  reg:
+    maxItems: 1
+    description: |
+      The I2C address of the device. The address is determined by the external
+      hardware configuration of GPIO1 and GPIO2 pins:
+       - 0x1a (GPIO2=Low, GPIO1=Low)
+       - 0x1b (GPIO2=Low, GPIO1=High)
+       - 0x4a (GPIO2=High, GPIO1=Low)
+       - 0x4b (GPIO2=High, GPIO1=High)
+
+  "#sound-dai-cells":
+    const: 0
+
+  clocks:
+    maxItems: 1
+
+  clock-names:
+    const: mclk
+
+  nuvoton,dsp-bypass:
+    type: boolean
+    description: |
+      Forcibly disable/bypass DSP path.
+
+  nuvoton,low-latency:
+    type: boolean
+    description: |
+      Enable low latency mode.
+      Please note the feature helps sensing performance
+      but worsens power consumption.
+
+  nuvoton,anc-enable:
+    type: boolean
+    description: |
+      Enable ANC (Active Noise Cancellation) feature.
+      NAU83G60 provides configurable low latency ANC path to Advanced DSP through TDM-RX.
+      To reduce latency, the ANC path only supports 48 kHz sample rates.
+
+  nuvoton,aec-enable:
+    type: boolean
+    description: |
+      Enable AEC (Acoustic Echo Cancellation) feature.
+      NAU83G60 provides Advanced DSP processed audio data as AEC reference through TDM-TX.
+      The AEC path only supports 48 kHz sample rates.
+
+  nuvoton,pbtl-enable:
+    type: boolean
+    description: |
+      NAU83G60 supports PBTL mode for mono output.
+
+  nuvoton,vbat-microvolt:
+    minimum: 8000000 # 8V
+    maximum: 24000000 # 24V
+    description: |
+      VBAT supply voltage in microvolts.
+      This is the analog power supply, provided by an external power source
+      or battery, and must be between 8V and 24V.
+
+  nuvoton,tdm-channel-length:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    enum: [16, 24, 32]
+    description: |
+      Assign TDM channel length.
+      The length must be 16, 24, or 32.
+
+  nuvoton,dsp-fw-names:
+    $ref: /schemas/types.yaml#/definitions/string-array
+    minItems: 2
+    maxItems: 2
+    description: |
+      Assign firmware filenames for left and right DSP cores.
+
+required:
+  - compatible
+  - reg
+
+allOf:
+  - $ref: dai-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        codec@1a {
+            #sound-dai-cells = <0>;
+            compatible = "nuvoton,nau8360";
+            reg = <0x1a>;
+            nuvoton,dsp-bypass;
+            nuvoton,vbat-microvolt = <12000000>;
+        };
+    };
-- 
2.25.1


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

* [PATCH 2/2] ASoC: codecs: nau8360: Add support for NAU83G60 amplifier
  2026-04-24  3:39 [PATCH 0/2] ASoC: codecs: Add Nuvoton NAU83G60 audio codec driver Neo Chang
  2026-04-24  3:39 ` [PATCH 1/2] ASoC: dt-bindings: nuvoton,nau8360: Add bindings for NAU83G60 Neo Chang
@ 2026-04-24  3:39 ` Neo Chang
  2026-04-24 15:56   ` Mark Brown
  1 sibling, 1 reply; 7+ messages in thread
From: Neo Chang @ 2026-04-24  3:39 UTC (permalink / raw)
  To: broonie
  Cc: lgirdwood, perex, tiwai, robh, krzk+dt, linux-sound, devicetree,
	alsa-devel, neo.chang70, kchsu0, sjlin0, Neo Chang

Add support for the Nuvoton NAU83G60 audio codec. The NAU83G60 is a
stereo 30W+30W smart amplifier with an integrated low-latency
Advanced Audio DSP.

Signed-off-by: Neo Chang <YLCHANG2@nuvoton.com>
---
 sound/soc/codecs/nau8360-dsp.c |  704 ++++++++++
 sound/soc/codecs/nau8360-dsp.h |  117 ++
 sound/soc/codecs/nau8360.c     | 2334 ++++++++++++++++++++++++++++++++
 sound/soc/codecs/nau8360.h     |  900 ++++++++++++
 4 files changed, 4055 insertions(+)
 create mode 100644 sound/soc/codecs/nau8360-dsp.c
 create mode 100644 sound/soc/codecs/nau8360-dsp.h
 create mode 100644 sound/soc/codecs/nau8360.c
 create mode 100644 sound/soc/codecs/nau8360.h

diff --git a/sound/soc/codecs/nau8360-dsp.c b/sound/soc/codecs/nau8360-dsp.c
new file mode 100644
index 000000000000..400d80a2f559
--- /dev/null
+++ b/sound/soc/codecs/nau8360-dsp.c
@@ -0,0 +1,704 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * The NAU83G60 Stereo Class-D Amplifier with DSP and I/V-sense driver.
+ *
+ * Copyright (C) 2026 Nuvoton Technology Corp.
+ * Author: David Lin <ctlin0@nuvoton.com>
+ *         Seven Lee <wtli@nuvoton.com>
+ *         John Hsu <kchsu0@nuvoton.com>
+ *         Neo Chang <ylchang2@nuvoton.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "nau8360-dsp.h"
+#include "nau8360.h"
+
+#define NAU8360_DSP_IDLE_RETRY 10
+const char *nau8360_def_firmwares[NAU8360_DSP_FW_NUM] = {
+	NAU8360_DSP_FIRMWARE".l", NAU8360_DSP_FIRMWARE".r" };
+const unsigned short nau8360_dsp_addr[NAU8360_DSP_FW_NUM] = {
+	NAU8360_RF000_DSP_COMM, NAU8360_RF002_DSP_COMM };
+
+static int nau8360_dsp_chan_kcs_setup(struct snd_soc_component *component,
+	const char *fw_name, int dsp_addr);
+
+#define NAU_DSP_CMD(_id, _msg, _setup, _reply) \
+	[_id] = { \
+		.cmd_id = _id, \
+		.msg_param = _msg, \
+		.setup_data = _setup, \
+		.reply_data = _reply, \
+	}
+
+#define NAU_DSP_CMD_ID(_id) \
+	[_id] = { \
+		.cmd_id = _id, \
+	}
+
+#define __dsp_dbg_data(dev, prefix, val) \
+	dsp_dbg(dev, prefix " %02x %02x %02x %02x", \
+		(u8)((val) & 0xff), \
+		(u8)(((val) >> 8) & 0xff), \
+		(u8)(((val) >> 16) & 0xff), \
+		(u8)(((val) >> 24) & 0xff))
+
+#define payload_read(dev, val)  __dsp_dbg_data(dev, "[R]", val)
+#define payload_write(dev, val) __dsp_dbg_data(dev, "[W]", val)
+
+static const struct nau8360_cmd_info nau8360_dsp_cmd_table[] = {
+	NAU_DSP_CMD(NAU8360_DSP_CMD_GET_COUNTER,      0, 0, 1),
+	NAU_DSP_CMD(NAU8360_DSP_CMD_GET_FRAME_STATUS, 0, 0, 1),
+	NAU_DSP_CMD(NAU8360_DSP_CMD_GET_REVISION,     0, 0, 1),
+	NAU_DSP_CMD(NAU8360_DSP_CMD_GET_KCS_RSLTS,    1, 0, 1),
+	NAU_DSP_CMD(NAU8360_DSP_CMD_GET_KCS_SETUP,    1, 0, 1),
+	NAU_DSP_CMD(NAU8360_DSP_CMD_SET_KCS_SETUP,    1, 1, 1),
+	NAU_DSP_CMD_ID(NAU8360_DSP_CMD_CLK_STOP),
+	NAU_DSP_CMD_ID(NAU8360_DSP_CMD_CLK_RESTART),
+};
+
+static bool nau8360_dsp_commands(int cmd_id)
+{
+	switch (cmd_id) {
+	case NAU8360_DSP_CMD_GET_COUNTER:
+	case NAU8360_DSP_CMD_GET_FRAME_STATUS:
+	case NAU8360_DSP_CMD_GET_REVISION:
+	case NAU8360_DSP_CMD_GET_KCS_RSLTS:
+	case NAU8360_DSP_CMD_GET_KCS_SETUP:
+	case NAU8360_DSP_CMD_SET_KCS_SETUP:
+	case NAU8360_DSP_CMD_CLK_STOP:
+	case NAU8360_DSP_CMD_CLK_RESTART:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const char *const dsp_cmd_table[] = {
+	[NAU8360_DSP_CMD_GET_COUNTER] = "GET_COUNTER",
+	[NAU8360_DSP_CMD_GET_FRAME_STATUS] = "GET_FRAME_STATUS",
+	[NAU8360_DSP_CMD_GET_REVISION] = "GET_REVISION",
+	[NAU8360_DSP_CMD_GET_KCS_RSLTS] = "GET_KCS_RSLTS",
+	[NAU8360_DSP_CMD_GET_KCS_SETUP] = "GET_KCS_SETUP",
+	[NAU8360_DSP_CMD_SET_KCS_SETUP] = "SET_KCS_SETUP",
+	[NAU8360_DSP_CMD_CLK_STOP] = "CLK_STOP",
+	[NAU8360_DSP_CMD_CLK_RESTART] = "CLK_RESTART",
+};
+
+/* checking for DSP IDLE pattern */
+static int nau8360_dsp_idle(struct snd_soc_component *component, unsigned short dsp_addr)
+{
+	struct nau8360 *nau8360 = snd_soc_component_get_drvdata(component);
+	unsigned int idle_pattern, timeout = NAU8360_DSP_IDLE_RETRY * USEC_PER_MSEC;
+	int ret;
+
+	ret = regmap_read_poll_timeout_atomic(nau8360->regmap, dsp_addr, idle_pattern,
+		idle_pattern == NAU8360_DSP_COMM_IDLE_WORD, USEC_PER_MSEC, timeout);
+	if (ret) {
+		/* The driver can't establish a connection to DSP. Maybe it is not clocked,
+		 * or previous synchronization issue.
+		 */
+		if (ret == -ETIMEDOUT)
+			dev_err(nau8360->dev, "timeout waiting for DSP idle pattern");
+		else
+			dev_err(nau8360->dev, "failed to read dsp status: %d", ret);
+
+		return ret;
+	}
+
+	dsp_dbg(component->dev, "idle pattern found");
+	payload_read(component->dev, idle_pattern);
+	return 0;
+}
+
+/**
+ * nau8360_pack_preamble - Pack DSP preamble fragment
+ * @cmd_id: Command ID for the DSP message
+ * @frag_len: Total length of the message fragments
+ *
+ * Return: 32-bit packed payload in Little Endian format.
+ */
+static inline u32 nau8360_pack_preamble(u8 cmd_id, u16 frag_len)
+{
+	return (NAU8360_DSP_COMM_PREAMBLE & 0xffff) |
+		(((cmd_id << 2) | (frag_len & 0x3)) << 16) |
+		((frag_len >> 2) << 24);
+}
+
+/**
+ * nau8360_pack_param - Pack DSP parameter fragment
+ * @param_offset: Starting offset of the parameter data
+ * @param_size: Size of the parameter data in bytes
+ *
+ * Return: 32-bit packed payload in Little Endian format.
+ */
+static inline u32 nau8360_pack_param(u16 param_offset, u16 param_size)
+{
+	return (param_offset & 0xffff) | ((param_size & 0xffff) << 16);
+}
+
+/**
+ * nau8360_pack_trailing - Pack DSP trailing fragment
+ * @frag_cnt: Current fragment count
+ * @padding: Number of padding bytes added to the final data fragment
+ *
+ * Return: 32-bit packed payload in Little Endian format.
+ */
+static inline u32 nau8360_pack_trailing(u16 frag_cnt, u8 padding)
+{
+	return (frag_cnt & 0xff) |
+		((((frag_cnt >> 8) << 6) | (padding << 4)) << 8);
+}
+static int nau8360_message_to_dsp(struct snd_soc_component *component,
+	const struct nau8360_cmd_info *cmd_info, int frag_len, int param_offset,
+	int param_size, void *param_data, unsigned short dsp_addr)
+{
+	struct device *dev = component->dev;
+	u8 *b_data;
+	unsigned int payload;
+	int ret, i, data_size, padding = 0, frag_cnt = 0;
+
+	ret = nau8360_dsp_idle(component, dsp_addr);
+	if (ret)
+		goto err;
+
+	/* sending preamble fragment */
+	payload = nau8360_pack_preamble(cmd_info->cmd_id, frag_len);
+	snd_soc_component_write(component, dsp_addr, payload);
+	dsp_dbg(dev, "sending preamble fragment (CMD_ID 0x%x, LEN 0x%x)",
+		cmd_info->cmd_id, frag_len);
+	payload_write(dev, payload);
+
+	if (!cmd_info->msg_param)
+		goto done;
+
+	/* sending payload + padding */
+	payload = nau8360_pack_param(param_offset, param_size);
+	snd_soc_component_write(component, dsp_addr, payload);
+	frag_cnt++;
+	dsp_dbg(dev, "send fragment (offset 0x%x, size 0x%x)", param_offset, param_size);
+	payload_write(dev, payload);
+
+	if (cmd_info->setup_data) {
+		b_data = (u8 *)param_data;
+		payload = 0;
+		for (data_size = 0, i = 0; i < param_size; i++) {
+			payload |= b_data[i] << (data_size * 8);
+			data_size++;
+			if (data_size == NAU8360_DSP_DATA_BYTE) {
+				snd_soc_component_write(component, dsp_addr, payload);
+				payload_write(dev, payload);
+				data_size = 0;
+				payload = 0;
+				frag_cnt++;
+			}
+		}
+
+		if (data_size > 0 && data_size < NAU8360_DSP_DATA_BYTE) {
+			/* sending the data fragments with padding bytes */
+			padding = NAU8360_DSP_DATA_BYTE - data_size;
+			snd_soc_component_write(component, dsp_addr, payload);
+			payload_write(dev, payload);
+			payload = 0;
+			frag_cnt++;
+
+		}
+		dsp_dbg(dev, "\n");
+	}
+
+	/* sending trailing fragment */
+	frag_cnt++;
+	payload = nau8360_pack_trailing(frag_cnt, padding);
+	snd_soc_component_write(component, dsp_addr, payload);
+	dsp_dbg(dev, "send trailing fragment (LEN 0x%x, PAD 0x%x)", frag_cnt, padding);
+	payload_write(dev, payload);
+	if (frag_cnt != frag_len) {
+		dev_err(dev, "message error (CMD_ID 0x%x, LEN 0x%x) !!!",
+			cmd_info->cmd_id, frag_cnt);
+		ret = -EPROTO;
+		goto err;
+	}
+
+done:
+	return 0;
+err:
+	return ret;
+}
+
+static int nau8360_dsp_replied(struct nau8360 *nau8360, int *length,
+	unsigned short dsp_addr)
+{
+	struct device *dev = nau8360->dev;
+	unsigned int reply_preamble, timeout = NAU8360_DSP_IDLE_RETRY * USEC_PER_MSEC;
+	int ret, reply_id;
+
+	ret = regmap_read_poll_timeout_atomic(nau8360->regmap, dsp_addr, reply_preamble,
+		(reply_preamble & 0xffff) == NAU8360_DSP_COMM_PREAMBLE,
+		USEC_PER_MSEC, timeout);
+	if (ret) {
+		dev_err(dev, "timeout for reply preamble: %d", ret);
+		return ret;
+	}
+
+	*length = (reply_preamble >> 16) & 0x3;
+	*length |= ((reply_preamble >> 24) & 0xff) << 2;
+	reply_id = (reply_preamble >> 18) & 0x3f;
+	dsp_dbg(dev, "receive preamble fragment (REPLY_ID 0x%x, LEN 0x%x)",
+		reply_id, *length);
+	payload_read(dev, reply_preamble);
+
+	if (reply_id == NAU8360_DSP_REPLY_OK)
+		return 0;
+	else
+		return -reply_id;
+}
+
+static int nau8360_reply_from_dsp(struct snd_soc_component *component,
+	const struct nau8360_cmd_info *cmd_info, int data_size,
+	void *data, unsigned short dsp_addr)
+{
+	struct nau8360 *nau8360 = snd_soc_component_get_drvdata(component);
+	struct device *dev = component->dev;
+	unsigned int payload, *data_buf;
+	int i, j, ret, frag_len, frag_payload_len, data_count, len_pos, pad_len,
+		pad_len_exp;
+
+	if (!cmd_info->reply_data) {
+		dsp_dbg(dev, "The cmd without reply data!!");
+		ret = nau8360_dsp_replied(nau8360, &frag_len, dsp_addr);
+		if (ret)
+			goto err;
+		else if (!frag_len)
+			goto done;
+	}
+
+	if (!data) {
+		ret = -EINVAL;
+		goto err;
+	}
+	data_buf = (unsigned int *)data;
+
+	ret = nau8360_dsp_replied(nau8360, &frag_len, dsp_addr);
+	if (ret)
+		goto err;
+	else if (!frag_len)
+		goto done;
+
+	frag_payload_len = frag_len - 1;
+	if (cmd_info->msg_param)
+		data_count = data_size;
+
+	for (i = 0; i < frag_payload_len; i++) {
+		ret = regmap_read(nau8360->regmap, dsp_addr, &payload);
+		if (ret) {
+			dev_err(dev, "failed to read payload of dsp");
+			goto err;
+		}
+
+		if (cmd_info->msg_param) {
+			if (data_count >= NAU8360_DSP_DATA_BYTE) {
+				*data_buf++ = payload;
+				data_count -= NAU8360_DSP_DATA_BYTE;
+				payload_read(dev, payload);
+			} else {
+				for (j = 0; j < NAU8360_DSP_DATA_BYTE; j++) {
+					((u8 *)data_buf)[j] = (payload >> (j * 8)) & 0xff;
+					data_count--;
+					if (data_count <= 0)
+						break;
+				}
+				payload_read(dev, payload);
+				break;
+			}
+		} else {
+			*data_buf = payload;
+			payload_read(dev, payload);
+		}
+	}
+
+	/* check the reply length same as request */
+	if (data_count && (cmd_info->cmd_id == NAU8360_DSP_CMD_GET_KCS_RSLTS ||
+			cmd_info->cmd_id == NAU8360_DSP_CMD_GET_KCS_SETUP)) {
+		dev_warn(dev, "payload_len %d, expected %d",
+			data_size - data_count, data_size);
+	}
+	dsp_dbg(dev, "reading trailing fragment");
+	ret = regmap_read(nau8360->regmap, dsp_addr, &payload);
+	if (ret) {
+		dev_err(dev, "failed to read trailing fragment");
+		goto err;
+	}
+
+	len_pos = payload & 0xff;
+	len_pos |= ((payload >> 8) & 0xc0) << 2;
+	if (len_pos != frag_len) {
+		dev_err(dev, "LEN_POST %02X, expect %02X", len_pos, frag_len);
+		ret = -EPROTO;
+		goto err;
+	}
+
+	pad_len = ((payload >> 8) & 0x30) >> 4;
+	if (cmd_info->msg_param)
+		pad_len_exp = frag_payload_len * NAU8360_DSP_DATA_BYTE -
+			(data_size - data_count);
+	else
+		pad_len_exp = 0;
+	if (pad_len != pad_len_exp) {
+		dev_err(dev, "PAD_LEN %02X, expect %02X", pad_len, pad_len_exp);
+		ret = -EPROTO;
+		goto err;
+	}
+	dsp_dbg(dev, "LEN_POST 0x%x, PAD_LEN 0x%x", len_pos, pad_len);
+	payload_read(dev, payload);
+done:
+	return 0;
+err:
+	dev_err(dev, "DSP reply error %d !!!", ret);
+	return ret;
+}
+
+/**
+ * nau8360_send_dsp_command - Send command to DSP
+ *
+ * @component:  component to register
+ * @cmd_id:  DSP supported command ID
+ * @kcs_setup: KCS setup structure
+ * @dsp_addr: DSP address
+ *
+ * The communication protocol is a Master-Slave type protocol
+ * where the host processor is the master and DSP is the slave.
+ * The Master initiates the communication and can either write or
+ * read back from the slave.
+ * Transactions from the Master are called "Messages",
+ * and read-back data from the Slave is called a "Reply".
+ *
+ * The function sends command to DSP according to the command ID.
+ * These commands include getting the information of DSP,
+ * getting or setting KCS configuration, or making DSP control.
+ */
+static int nau8360_send_dsp_command(struct snd_soc_component *component, int cmd_id,
+	struct nau8360_kcs_setup *kcs_setup, unsigned short dsp_addr)
+{
+	const struct nau8360_cmd_info *cmd_info;
+	int ret, frag_len = 0;
+
+	if (!component || !kcs_setup) {
+		ret = -EINVAL;
+		goto msg_fail;
+	}
+	if (!nau8360_dsp_commands(cmd_id)) {
+		dev_err(component->dev, "command not support!");
+		ret = -EINVAL;
+		goto msg_fail;
+	}
+
+	cmd_info = &nau8360_dsp_cmd_table[cmd_id];
+	if ((cmd_info->msg_param && !kcs_setup->set_len) ||
+		(cmd_info->setup_data && !kcs_setup->set_kcs_data) ||
+		(cmd_info->reply_data && !kcs_setup->get_data)) {
+		ret = -EFAULT;
+		goto msg_fail;
+	}
+
+	/* Read up to 1kB data because the LEN field to request data is 10-bits
+	 * long; and not beyond 3kB offset.
+	 */
+	if (cmd_id == NAU8360_DSP_CMD_GET_KCS_SETUP &&
+		(kcs_setup->set_len > NAU8360_DSP_KCS_DAT_LEN_MAX ||
+			kcs_setup->set_kcs_offset > NAU8360_DSP_KCS_OFFSET_MAX)) {
+		ret = -ERANGE;
+		goto msg_fail;
+	}
+
+	if (cmd_info->msg_param) {
+		/* one fragment for offset and size parameters */
+		frag_len++;
+		/* one fragment for a postamble fragment */
+		frag_len++;
+	}
+
+	/* fragments for KCS setup writen */
+	if (cmd_info->setup_data)
+		frag_len += (kcs_setup->set_len +
+				NAU8360_DSP_DATA_BYTE - 1) / NAU8360_DSP_DATA_BYTE;
+
+	ret = nau8360_message_to_dsp(component, cmd_info, frag_len,
+			kcs_setup->set_kcs_offset, kcs_setup->set_len,
+			kcs_setup->set_kcs_data, dsp_addr);
+	if (ret)
+		goto msg_fail;
+
+	ret = nau8360_reply_from_dsp(component, cmd_info, kcs_setup->get_len,
+			kcs_setup->get_data, dsp_addr);
+	if (ret)
+		goto reply_fail;
+
+	return 0;
+
+msg_fail:
+	dev_err(component->dev, "fail to send a message %d to DSP (%d)", cmd_id, ret);
+	return ret;
+reply_fail:
+	dev_err(component->dev, "reply fail (%d) from DSP.", ret);
+	return ret;
+}
+
+static inline int nau8360_dsp_exec_command(struct snd_soc_component *cp, int cmd_id,
+	int offset, int set_len, void *set_data, int get_len, void *get_data,
+	int dsp_addr)
+{
+	struct nau8360_kcs_setup kcs_setup = {
+		.set_kcs_offset = offset,
+		.set_len = set_len,
+		.set_kcs_data = set_data,
+		.get_len = get_len,
+		.get_data = get_data,
+	};
+
+	return nau8360_send_dsp_command(cp, cmd_id, &kcs_setup, dsp_addr);
+}
+
+static inline int nau8360_send_dsp_broadcast(struct snd_soc_component *cp, int cmd_id)
+{
+	int i, ret;
+
+	for (i = 0; i < NAU8360_DSP_FW_NUM; i++) {
+		ret = nau8360_dsp_exec_command(cp, cmd_id, 0, 0, NULL, 0, NULL,
+			nau8360_dsp_addr[i]);
+		if (ret) {
+			dev_err(cp->dev, "DSP %x fail (%d)", nau8360_dsp_addr[i], ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * nau8360_dsp_kcs_setup - Send KCS setup command to DSP
+ *
+ * @component:  component to register
+ * @offset: address offset relative to KCS start
+ * @size: size of data writen to KCS
+ * @data: data writen to KCS setup
+ * @dsp_addr : DSP address
+ *
+ * The function sends KCS setup command to DSP for
+ * setting KCS configuration. The maximum size that you can transfer into
+ * the DSP is 96 bytes. Therefore, the driver has to split the data into
+ * 96 bytes chucks, if the setup configuration over the threshold.
+ */
+static int nau8360_dsp_kcs_setup(struct snd_soc_component *component, int offset, int size,
+	const void *data, unsigned short dsp_addr)
+{
+	u8 *data_buf;
+	unsigned int kcs_rst;
+	int cmd_id = NAU8360_DSP_CMD_SET_KCS_SETUP, retries, ret, data_len, data_rem,
+		addr_offset;
+
+	/* Limit full load of KCS_SETUP data and not beyond 3kB offset. */
+	if (!data || size > NAU8360_DSP_KCS_DAT_LEN_MAX ||
+		offset > NAU8360_DSP_KCS_OFFSET_MAX) {
+		ret = -EINVAL;
+		goto msg_fail;
+	}
+
+	/* sending fragments for KCS setup */
+	data_buf = (u8 *)data;
+	addr_offset = offset;
+	data_rem = size;
+	retries = 0;
+	while (data_rem) {
+		if (data_rem > NAU8360_DSP_KCS_TX_MAX)
+			data_len = NAU8360_DSP_KCS_TX_MAX;
+		else
+			data_len = data_rem;
+
+		ret = nau8360_dsp_exec_command(component, NAU8360_DSP_CMD_SET_KCS_SETUP,
+			addr_offset, data_len, (void *)data_buf, 0, &kcs_rst, dsp_addr);
+		if (ret) {
+			if (retries++ < NAU8360_DSP_RETRY_MAX)
+				continue;
+			else
+				goto msg_fail;
+		} else {
+			data_buf += (u8)data_len;
+			addr_offset += data_len;
+			data_rem -= data_len;
+		}
+		/* checking KCS result */
+		ret = nau8360_dsp_exec_command(component, NAU8360_DSP_CMD_GET_KCS_RSLTS,
+			0, NAU8360_DSP_DATA_BYTE, NULL,
+			NAU8360_DSP_DATA_BYTE, &kcs_rst, dsp_addr);
+		if (ret)
+			goto msg_fail;
+	}
+
+	return 0;
+
+msg_fail:
+	dev_err(component->dev, "send a kcs setup message %d fail (%d)", cmd_id, ret);
+	return ret;
+}
+
+static int nau8360_dsp_get_cmd_put(struct snd_soc_component *component,
+	int dsp_addr, int cmd, int *value)
+{
+	int ret;
+
+	dev_info(component->dev, "send DSP %x command %s", dsp_addr, dsp_cmd_table[cmd]);
+
+	ret = nau8360_dsp_exec_command(component, cmd, 0, sizeof(int), NULL,
+		sizeof(int), value, dsp_addr);
+	if (ret) {
+		dev_err(component->dev, "do command fail (%d)", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int nau8360_dsp_clock_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);
+	int ret, cmd;
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		cmd = NAU8360_DSP_CMD_CLK_RESTART;
+	else if (SND_SOC_DAPM_EVENT_OFF(event))
+		cmd = NAU8360_DSP_CMD_CLK_STOP;
+
+	dsp_dbg(component->dev, "send DSP command %s", dsp_cmd_table[cmd]);
+	ret = nau8360_send_dsp_broadcast(component, cmd);
+	if (ret) {
+		dev_err(component->dev, "send DSP command %s fail (%d)",
+			dsp_cmd_table[cmd], ret);
+		goto err;
+	}
+
+	return 0;
+err:
+	return ret;
+}
+
+static const struct snd_soc_dapm_widget nau8360_dsp_dapm_widgets[] = {
+	SND_SOC_DAPM_SUPPLY("DSP Clock", SND_SOC_NOPM, 0, 0, nau8360_dsp_clock_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route nau8360_dsp_dapm_routes[] = {
+	{ "DSP", NULL, "HW3 Engine" },
+	{ "DSP", NULL, "DSP Clock" },
+};
+
+static int nau8360_dsp_chan_kcs_setup(struct snd_soc_component *cp,
+	const char *fw_name, int dsp_addr)
+{
+	struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+	const struct firmware *fw;
+	int ret, status, buf_off, buf_len;
+
+	ret = nau8360_dsp_get_cmd_put(cp, dsp_addr,
+			NAU8360_DSP_CMD_GET_FRAME_STATUS, &status);
+	if (ret || !(status & NAU8360_DSP_ALGO_OK)) {
+		dev_err(cp->dev, "DSP %x is not ready", dsp_addr);
+		ret = -EIO;
+		goto err;
+	}
+
+	dev_info(cp->dev, "DSP %x is ready to load firmware %s, status %x",
+		dsp_addr, fw_name, status);
+
+	ret = request_firmware(&fw, fw_name, cp->dev);
+	if (ret) {
+		dev_err(cp->dev, "failed to load firmware (%d)", ret);
+		goto err;
+	}
+
+	buf_off = 0;
+	buf_len = nau8360->kcs_setup_size = fw->size;
+	ret = nau8360_dsp_kcs_setup(cp, buf_off, buf_len, fw->data, dsp_addr);
+	if (ret) {
+		dev_err(cp->dev, "send DSP command %s fail (%d)",
+			dsp_cmd_table[NAU8360_DSP_CMD_SET_KCS_SETUP], ret);
+		goto err_loaded;
+	}
+	release_firmware(fw);
+
+	return 0;
+
+err_loaded:
+	if (fw)
+		release_firmware(fw);
+err:
+	return ret;
+}
+
+static int nau8360_dsp_set_kcs_setup(struct snd_soc_component *cp)
+{
+	struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+	char firmware[NAU8360_DSP_FW_NAMELEN];
+	const char *fw_name;
+	int i, ret;
+
+	for (i = 0; i < NAU8360_DSP_FW_NUM; i++) {
+		if (nau8360->dsp_fws_num) {
+			snprintf(firmware, sizeof(firmware), NAU8360_DSP_FIRMDIR"%s",
+				nau8360->dsp_firmware[i]);
+			fw_name = firmware;
+		} else
+			fw_name = nau8360_def_firmwares[i];
+
+		ret = nau8360_dsp_chan_kcs_setup(cp, fw_name, nau8360_dsp_addr[i]);
+		if (ret)
+			return ret;
+
+		msleep(100);
+	}
+
+	return 0;
+}
+
+int nau8360_dsp_init(struct snd_soc_component *component)
+{
+	struct nau8360 *nau8360 = snd_soc_component_get_drvdata(component);
+	struct snd_soc_dapm_context *dapm = nau8360->dapm;
+	int ret;
+
+	dev_info(component->dev, "DSP initializing...");
+	ret = nau8360_dsp_set_kcs_setup(component);
+	if (ret)
+		goto err;
+
+	ret = snd_soc_dapm_new_controls(dapm, nau8360_dsp_dapm_widgets,
+			ARRAY_SIZE(nau8360_dsp_dapm_widgets));
+	if (ret) {
+		dev_err(component->dev, "add DSP widget fail (%d)", ret);
+		goto err;
+	}
+	ret = snd_soc_dapm_add_routes(dapm, nau8360_dsp_dapm_routes,
+			ARRAY_SIZE(nau8360_dsp_dapm_routes));
+	if (ret) {
+		dev_err(component->dev, "add DSP route fail (%d)", ret);
+		goto err;
+	}
+	nau8360->dsp_created = true;
+
+	return 0;
+
+err:
+	return ret;
+}
+
+int nau8360_dsp_reinit(struct snd_soc_component *component)
+{
+	dev_info(component->dev, "DSP initializing...");
+	return nau8360_dsp_set_kcs_setup(component);
+}
diff --git a/sound/soc/codecs/nau8360-dsp.h b/sound/soc/codecs/nau8360-dsp.h
new file mode 100644
index 000000000000..13c178bdef4e
--- /dev/null
+++ b/sound/soc/codecs/nau8360-dsp.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * The NAU83G60 Stereo Class-D Amplifier with DSP and I/V-sense driver.
+ *
+ * Copyright (C) 2026 Nuvoton Technology Corp.
+ * Author: David Lin <ctlin0@nuvoton.com>
+ *         Seven Lee <wtli@nuvoton.com>
+ *         John Hsu <kchsu0@nuvoton.com>
+ *         Neo Chang <ylchang2@nuvoton.com>
+ */
+
+#ifndef __NAU8360_DSP_H__
+#define __NAU8360_DSP_H__
+
+#define NAU8360_DSP_COMM_IDLE_WORD		0xf4f3f2f1
+#define NAU8360_DSP_COMM_PREAMBLE		0xb2a1
+#define NAU8360_DSP_DATA_BYTE			4
+#define NAU8360_DSP_DATA_LEN			(NAU8360_DSP_DATA_BYTE << 3)
+/* max bytes of data to transfer into DSP each time during the KCS setup */
+#define NAU8360_DSP_KCS_TX_MAX			96
+#define NAU8360_DSP_RETRY_MAX			3
+#define NAU8360_DSP_KCS_DAT_LEN_MAX		1024
+#define NAU8360_DSP_KCS_OFFSET_MAX		3072
+
+#ifdef DSP_DBG
+#define dsp_dbg(dev, fmt, ...) dev_dbg(dev, fmt, ##__VA_ARGS__)
+#else
+#define dsp_dbg(dev, fmt, ...) \
+	do { (void)(dev); no_printk(fmt, ##__VA_ARGS__); } while (0)
+#endif
+
+/* FRAME_STATUS (0x9) */
+#define NAU8360_DSP_SNS_OVF_SFT			31
+#define NAU8360_DSP_SNS_OVF			(0x1 << NAU8360_DSP_SNS_OVF_SFT)
+#define NAU8360_DSP_AUD_UVF_SFT			30
+#define NAU8360_DSP_AUD_UVF			(0x1 << NAU8360_DSP_AUD_UVF_SFT)
+#define NAU8360_DSP_AUD_OVF_SFT			29
+#define NAU8360_DSP_AUD_OVF			(0x1 << NAU8360_DSP_AUD_OVF_SFT)
+#define NAU8360_DSP_ALC_STS_SFT			28
+#define NAU8360_DSP_ALC_STS			(0x1 << NAU8360_DSP_ALC_STS_SFT)
+#define NAU8360_DSP_CLK_STOP_SFT		25
+#define NAU8360_DSP_CLK_STOP			(0x1 << NAU8360_DSP_CLK_STOP_SFT)
+#define NAU8360_DSP_OCP_OTP_SFT			24
+#define NAU8360_DSP_OCP_OTP			(0x1 << NAU8360_DSP_OCP_OTP_SFT)
+#define NAU8360_DSP_UVLO_SFT			22
+#define NAU8360_DSP_UVLO			(0x1 << NAU8360_DSP_UVLO_SFT)
+#define NAU8360_DSP_OVP_SFT			21
+#define NAU8360_DSP_OVP				(0x1 << NAU8360_DSP_OVP_SFT)
+#define NAU8360_DSP_APWR_DWN_SFT		20
+#define NAU8360_DSP_APWR_DWN			(0x1 << NAU8360_DSP_APWR_DWN_SFT)
+#define NAU8360_DSP_SNSR_RATE_SFT		12
+#define NAU8360_DSP_SNSR_RATE_MASK		(0xff << NAU8360_DSP_SNSR_RATE_SFT)
+#define NAU8360_DSP_AUD_RATE_SFT		4
+#define NAU8360_DSP_AUD_RATE_MASK		(0xff << NAU8360_DSP_AUD_RATE_SFT)
+#define NAU8360_DSP_FEED_TRU_SFT		1
+#define NAU8360_DSP_FEED_TRU			(0x1 << NAU8360_DSP_FEED_TRU_SFT)
+#define NAU8360_DSP_ALGO_OK			0x1
+
+#define NAU8360_DSP_FIRMDIR			"Nuvoton/"
+#define NAU8360_DSP_FIRMWARE			NAU8360_DSP_FIRMDIR"NAU83G60.kcs.bin"
+#define NAU8360_DSP_FW_NUM			NAU8360_DSP_CORE_NUM
+#define NAU8360_DSP_FW_NAMELEN			64
+
+enum {
+	NAU8360_DSP_REPLY_OK,
+	NAU8360_DSP_REPLY_MSG_INTEGRETY_ERR,
+	NAU8360_DSP_REPLY_EXECUTION_ERR,
+	NAU8360_DSP_REPLY_COMMAND_DOESNT_EXISTS_ERR,
+	NAU8360_DSP_REPLY_UNKNOWN_ERR,
+	NAU8360_DSP_REPLY_MSG_TOO_LONG,
+};
+
+enum {
+	NAU8360_DSP_CMD_GET_COUNTER	= 0x1,
+	NAU8360_DSP_CMD_GET_FRAME_STATUS	= 0x9,
+	NAU8360_DSP_CMD_GET_REVISION	= 0xa,
+	NAU8360_DSP_CMD_GET_KCS_RSLTS	= 0x4,
+	NAU8360_DSP_CMD_GET_KCS_SETUP	= 0x6,
+	NAU8360_DSP_CMD_SET_KCS_SETUP	= 0x7,
+	NAU8360_DSP_CMD_CLK_STOP	= 0xb,
+	NAU8360_DSP_CMD_CLK_RESTART	= 0xc,
+};
+
+/**
+ * struct nau8360_cmd_info - DSP command information structure
+ * @cmd_id:     The command identification number
+ * @msg_param:  Message parameters including offset, size, and data
+ * @setup_data: Setup data for write-only operations
+ * @reply_data: Reply data received from the DSP
+ */
+struct nau8360_cmd_info {
+	int cmd_id;
+	bool msg_param;
+	bool setup_data;
+	bool reply_data;
+};
+
+/**
+ * struct nau8360_kcs_setup - KCS setup configuration structure
+ * @set_len:        Length of data written in bytes
+ * @set_kcs_offset: Address offset relative to the start of KCS
+ * @set_kcs_data:   Pointer to the data written to KCS
+ * @get_len:        Length of data from the reply in bytes
+ * @get_data:       Pointer to the buffer for reply data
+ */
+struct nau8360_kcs_setup {
+	int set_len;
+	int set_kcs_offset;
+	void *set_kcs_data;
+	int get_len;
+	void *get_data;
+};
+
+int nau8360_dsp_init(struct snd_soc_component *component);
+int nau8360_dsp_reinit(struct snd_soc_component *component);
+
+#endif /* __NAU8360_DSP_H__ */
diff --git a/sound/soc/codecs/nau8360.c b/sound/soc/codecs/nau8360.c
new file mode 100644
index 000000000000..e87a4a8b1ccd
--- /dev/null
+++ b/sound/soc/codecs/nau8360.c
@@ -0,0 +1,2334 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * The NAU83G60 Stereo Class-D Amplifier with DSP and I/V-sense driver.
+ *
+ * Copyright (C) 2026 Nuvoton Technology Corp.
+ * Author: David Lin <ctlin0@nuvoton.com>
+ *         Seven Lee <wtli@nuvoton.com>
+ *         John Hsu <kchsu0@nuvoton.com>
+ *         Neo Chang <ylchang2@nuvoton.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/units.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "nau8360-dsp.h"
+#include "nau8360.h"
+
+static inline void nau8360_dsp_software_reset(struct snd_soc_component *component);
+static inline void nau8360_dsp_enable(struct regmap *regmap, bool enable);
+
+/* range of Master Clock MCLK (Hz) */
+#define MASTER_CLK_MIN 11025000
+#define MASTER_CLK_MAX 24576000
+/* DSP Optimal Clock Range 120MHz~126M(Hz) */
+#define DSP_OP_CLK48 122880000
+#define DSP_OP_CLK44 112896000
+/* the maximum frequency of DAC and IV sense clock */
+#define CLK_DA_IVSNS_MAX 6144000
+#define ADSP_SR_48000 48000
+#define ADSP_SR_44100 44100
+
+static const int ivsns_clk_div[] = { 1, 2, 4, 5, 8, 10 };
+
+static const int dac_clk_div[] = { 1, 2, 4, 8 };
+
+/* PLL threshold */
+#define PLL_FREQ_MIN 1000000
+#define PLL_FREQ_MAX 32000000
+#define PLL_FOUT_MIN 12500000
+#define PLL_FOUT_MAX 125000000
+#define PLL_FREF_MAX 8000000
+#define PLL_FVCO_MIN 50000000
+#define MSEL_MAX 32
+#define RSEL_MAX 4
+
+static const struct reg_default nau8360_reg_defaults[] = {
+	{ NAU8360_R02_I2C_ADDR, 0x0000 },
+	{ NAU8360_R03_CLK_CTRL0, 0x0000 },
+	{ NAU8360_R04_CLK_CTRL1, 0x0000 },
+	{ NAU8360_R05_INTERRUPT_CTRL, 0x40ff },
+	{ NAU8360_R07_GP_CTRL, 0xaa30 },
+	{ NAU8360_R08_GP_CTRL0, 0x1e1e },
+	{ NAU8360_R09_GP_CTRL1, 0x1e1e },
+	{ NAU8360_R0A_GP_CTRL2, 0x0000 },
+	{ NAU8360_R0B_I2S_PCM_CTRL1, 0x0702 },
+	{ NAU8360_R0C_I2S_PCM_CTRL2, 0x0a10 },
+	{ NAU8360_R0D_I2S_PCM_CTRL3, 0x1300 },
+	{ NAU8360_R0E_I2S_DATA_CTRL1, 0x0304 },
+	{ NAU8360_R0F_I2S_DATA_CTRL2, 0x080b },
+	{ NAU8360_R10_I2S_DATA_CTRL3, 0x0014 },
+	{ NAU8360_R11_I2S_DATA_CTRL4, 0x0014 },
+	{ NAU8360_R12_PATH_CTRL, 0x0400 },
+	{ NAU8360_R17_I2S0_DATA_CTRL5, 0x0014 },
+	{ NAU8360_R1A_DSP_CORE_CTRL2, 0x0010 },
+	{ NAU8360_R2C_ALC_CTRL1, 0x2000 },
+	{ NAU8360_R2D_ALC_CTRL2, 0x8400 },
+	{ NAU8360_R2E_ALC_CTRL3, 0x2083 },
+	{ NAU8360_R31_UVLOP_CTRL1, 0x0000 },
+	{ NAU8360_R32_UVLOP_CTRL2, 0x8400 },
+	{ NAU8360_R33_UVLOP_CTRL3, 0x0000 },
+	{ NAU8360_R40_CLK_DET_CTRL, 0xca60 },
+	{ NAU8360_R41_CLK_CTL2, 0xc400 },
+	{ NAU8360_R5D_SINC_CFG, 0x0010 },
+	{ NAU8360_R5F_ANA_TRIM_CFG1, 0x8400 },
+	{ NAU8360_R60_RST, 0x0010 },
+	{ NAU8360_R67_ANALOG_CONTROL_0, 0x0160 },
+	{ NAU8360_R68_ANALOG_CONTROL_1, 0x00d4 },
+	{ NAU8360_R6A_SARADC_CFG0, 0x5c09 },
+	{ NAU8360_R6B_SARADC_CFG1, 0x0008 },
+	{ NAU8360_R6C_IVSNS_CFG0, 0xf040 },
+	{ NAU8360_R6D_IVSNS_CFG1, 0x3555 },
+	{ NAU8360_R6E_DAC_CFG0, 0x0ed5 },
+	{ NAU8360_R71_CLK_DIV_CFG, 0x0211 },
+	{ NAU8360_R72_PLL_CFG0, 0xccc4 },
+	{ NAU8360_R73_PLL_CFG1, 0x0101 },
+	{ NAU8360_R74_PLL_CFG2, 0x0000 },
+	{ NAU8360_R7A_DAC_TRIM_CFG2, 0x0000 },
+	{ NAU8360_R7B_IVSNS_TRIM_CFG, 0x0000 },
+	{ NAU8360_R7C_MISC_TRIM_CFG, 0x72d5 },
+	{ NAU8360_R86_HW3_CTL0, 0x2000 },
+	{ NAU8360_R88_ALC_CTRL6, 0x0000 },
+	{ NAU8360_R8A_HW3_VL_CTL7, 0xc000 },
+	{ NAU8360_R8B_HW3_VR_CTL8, 0xc000 },
+	{ NAU8360_R8C_HW3_CTL6, 0x0000 },
+	{ NAU8360_R8D_HW3_IL_CTL7, 0xc000 },
+	{ NAU8360_R8E_HW3_IR_CTL8, 0xc000 },
+	{ NAU8360_R8F_HW3_CTL9, 0x0000 },
+	{ NAU8360_R90_HW2_CTL0, 0x2000 },
+	{ NAU8360_R96_HW2_CTL6, 0x0000 },
+	{ NAU8360_R97_HW2_CTL7, 0xc000 },
+	{ NAU8360_R98_HW2_CTL8, 0xc000 },
+	{ NAU8360_R99_HW2_CTL9, 0x0000 },
+	{ NAU8360_R9A_HW1_CTL0, 0xc000 },
+	{ NAU8360_R9B_HW1_CTL1, 0xc000 },
+	{ NAU8360_R9C_HW1_CTL2, 0x0800 },
+	{ NAU8360_R9D_PEQ_CTL, 0x0001 },
+	{ NAU8360_RA0_LEFT_XODRC_CTRL, 0x0000 },
+	{ NAU8360_RA2_RIGHT_XODRC_CTRL, 0x0000 },
+	{ NAU8360_RA4_ANA_REG_0, 0x7f86 },
+	{ NAU8360_RA5_ANA_REG_1, 0x276e },
+};
+
+static bool nau8360_readable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case NAU8360_R00_SOFTWARE_RST ... NAU8360_R12_PATH_CTRL:
+	case NAU8360_R17_I2S0_DATA_CTRL5:
+	case NAU8360_R1A_DSP_CORE_CTRL2:
+	case NAU8360_R21_VBAT_READOUT ... NAU8360_R22_TEMP_READOUT:
+	case NAU8360_R2C_ALC_CTRL1 ... NAU8360_R2E_ALC_CTRL3:
+	case NAU8360_R31_UVLOP_CTRL1 ... NAU8360_R33_UVLOP_CTRL3:
+	case NAU8360_R40_CLK_DET_CTRL ... NAU8360_R41_CLK_CTL2:
+	case NAU8360_R46_I2C_DEVICE_ID:
+	case NAU8360_R5D_SINC_CFG:
+	case NAU8360_R5F_ANA_TRIM_CFG1 ... NAU8360_R60_RST:
+	case NAU8360_R67_ANALOG_CONTROL_0 ... NAU8360_R6E_DAC_CFG0:
+	case NAU8360_R71_CLK_DIV_CFG ... NAU8360_R74_PLL_CFG2:
+	case NAU8360_R77_SOFT_SD ... NAU8360_R7C_MISC_TRIM_CFG:
+	case NAU8360_R7E_CLK_GATED_EN:
+	case NAU8360_R86_HW3_CTL0:
+	case NAU8360_R88_ALC_CTRL6:
+	case NAU8360_R8A_HW3_VL_CTL7 ... NAU8360_R90_HW2_CTL0:
+	case NAU8360_R96_HW2_CTL6 ... NAU8360_R9D_PEQ_CTL:
+	case NAU8360_RA0_LEFT_XODRC_CTRL:
+	case NAU8360_RA2_RIGHT_XODRC_CTRL:
+	case NAU8360_RA4_ANA_REG_0 ... NAU8360_RA5_ANA_REG_1:
+	case NAU8360_R100_LEFT_BIQ0_COE ... (NAU8360_R100_LEFT_BIQ0_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R10C_LEFT_BIQ1_COE ... (NAU8360_R10C_LEFT_BIQ1_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R118_LEFT_BIQ2_COE ... (NAU8360_R118_LEFT_BIQ2_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R124_LEFT_BIQ3_COE ... (NAU8360_R124_LEFT_BIQ3_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R130_LEFT_BIQ4_COE ... (NAU8360_R130_LEFT_BIQ4_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R13C_LEFT_BIQ5_COE ... (NAU8360_R13C_LEFT_BIQ5_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R148_LEFT_BIQ6_COE ... (NAU8360_R148_LEFT_BIQ6_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R154_LEFT_BIQ7_COE ... (NAU8360_R154_LEFT_BIQ7_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R160_LEFT_BIQ8_COE ... (NAU8360_R160_LEFT_BIQ8_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R16C_LEFT_BIQ9_COE ... (NAU8360_R16C_LEFT_BIQ9_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R178_LEFT_BIQ10_COE ... (NAU8360_R178_LEFT_BIQ10_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R184_LEFT_BIQ11_COE ... (NAU8360_R184_LEFT_BIQ11_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R190_LEFT_BIQ12_COE ... (NAU8360_R190_LEFT_BIQ12_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R19C_LEFT_BIQ13_COE ... (NAU8360_R19C_LEFT_BIQ13_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R1A8_LEFT_BIQ14_COE ... (NAU8360_R1A8_LEFT_BIQ14_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R200_RIGHT_BIQ0_COE ... (NAU8360_R200_RIGHT_BIQ0_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R20C_RIGHT_BIQ1_COE ... (NAU8360_R20C_RIGHT_BIQ1_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R218_RIGHT_BIQ2_COE ... (NAU8360_R218_RIGHT_BIQ2_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R224_RIGHT_BIQ3_COE ... (NAU8360_R224_RIGHT_BIQ3_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R230_RIGHT_BIQ4_COE ... (NAU8360_R230_RIGHT_BIQ4_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R23C_RIGHT_BIQ5_COE ... (NAU8360_R23C_RIGHT_BIQ5_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R248_RIGHT_BIQ6_COE ... (NAU8360_R248_RIGHT_BIQ6_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R254_RIGHT_BIQ7_COE ... (NAU8360_R254_RIGHT_BIQ7_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R260_RIGHT_BIQ8_COE ... (NAU8360_R260_RIGHT_BIQ8_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R26C_RIGHT_BIQ9_COE ... (NAU8360_R26C_RIGHT_BIQ9_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R278_RIGHT_BIQ10_COE ... (NAU8360_R278_RIGHT_BIQ10_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R284_RIGHT_BIQ11_COE ... (NAU8360_R284_RIGHT_BIQ11_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R290_RIGHT_BIQ12_COE ... (NAU8360_R290_RIGHT_BIQ12_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R29C_RIGHT_BIQ13_COE ... (NAU8360_R29C_RIGHT_BIQ13_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R2A8_RIGHT_BIQ14_COE ... (NAU8360_R2A8_RIGHT_BIQ14_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_RF000_DSP_COMM:
+	case NAU8360_RF002_DSP_COMM:
+		return true;
+	default:
+		return false;
+	}
+
+}
+
+static bool nau8360_writeable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case NAU8360_R00_SOFTWARE_RST ... NAU8360_R12_PATH_CTRL:
+	case NAU8360_R17_I2S0_DATA_CTRL5:
+	case NAU8360_R1A_DSP_CORE_CTRL2:
+	case NAU8360_R2C_ALC_CTRL1 ... NAU8360_R2E_ALC_CTRL3:
+	case NAU8360_R31_UVLOP_CTRL1 ... NAU8360_R33_UVLOP_CTRL3:
+	case NAU8360_R40_CLK_DET_CTRL ... NAU8360_R41_CLK_CTL2:
+	case NAU8360_R5D_SINC_CFG:
+	case NAU8360_R5F_ANA_TRIM_CFG1:
+	case NAU8360_R60_RST:
+	case NAU8360_R67_ANALOG_CONTROL_0 ... NAU8360_R68_ANALOG_CONTROL_1:
+	case NAU8360_R6A_SARADC_CFG0 ... NAU8360_R6E_DAC_CFG0:
+	case NAU8360_R71_CLK_DIV_CFG ... NAU8360_R74_PLL_CFG2:
+	case NAU8360_R77_SOFT_SD ... NAU8360_R7C_MISC_TRIM_CFG:
+	case NAU8360_R7E_CLK_GATED_EN:
+	case NAU8360_R86_HW3_CTL0:
+	case NAU8360_R88_ALC_CTRL6:
+	case NAU8360_R8A_HW3_VL_CTL7 ... NAU8360_R90_HW2_CTL0:
+	case NAU8360_R96_HW2_CTL6 ... NAU8360_R9D_PEQ_CTL:
+	case NAU8360_RA4_ANA_REG_0 ... NAU8360_RA5_ANA_REG_1:
+	case NAU8360_R100_LEFT_BIQ0_COE ... (NAU8360_R100_LEFT_BIQ0_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R10C_LEFT_BIQ1_COE ... (NAU8360_R10C_LEFT_BIQ1_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R118_LEFT_BIQ2_COE ... (NAU8360_R118_LEFT_BIQ2_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R124_LEFT_BIQ3_COE ... (NAU8360_R124_LEFT_BIQ3_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R130_LEFT_BIQ4_COE ... (NAU8360_R130_LEFT_BIQ4_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R13C_LEFT_BIQ5_COE ... (NAU8360_R13C_LEFT_BIQ5_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R148_LEFT_BIQ6_COE ... (NAU8360_R148_LEFT_BIQ6_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R154_LEFT_BIQ7_COE ... (NAU8360_R154_LEFT_BIQ7_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R160_LEFT_BIQ8_COE ... (NAU8360_R160_LEFT_BIQ8_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R16C_LEFT_BIQ9_COE ... (NAU8360_R16C_LEFT_BIQ9_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R178_LEFT_BIQ10_COE ... (NAU8360_R178_LEFT_BIQ10_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R184_LEFT_BIQ11_COE ... (NAU8360_R184_LEFT_BIQ11_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R190_LEFT_BIQ12_COE ... (NAU8360_R190_LEFT_BIQ12_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R19C_LEFT_BIQ13_COE ... (NAU8360_R19C_LEFT_BIQ13_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R1A8_LEFT_BIQ14_COE ... (NAU8360_R1A8_LEFT_BIQ14_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R200_RIGHT_BIQ0_COE ... (NAU8360_R200_RIGHT_BIQ0_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R20C_RIGHT_BIQ1_COE ... (NAU8360_R20C_RIGHT_BIQ1_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R218_RIGHT_BIQ2_COE ... (NAU8360_R218_RIGHT_BIQ2_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R224_RIGHT_BIQ3_COE ... (NAU8360_R224_RIGHT_BIQ3_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R230_RIGHT_BIQ4_COE ... (NAU8360_R230_RIGHT_BIQ4_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R23C_RIGHT_BIQ5_COE ... (NAU8360_R23C_RIGHT_BIQ5_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R248_RIGHT_BIQ6_COE ... (NAU8360_R248_RIGHT_BIQ6_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R254_RIGHT_BIQ7_COE ... (NAU8360_R254_RIGHT_BIQ7_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R260_RIGHT_BIQ8_COE ... (NAU8360_R260_RIGHT_BIQ8_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R26C_RIGHT_BIQ9_COE ... (NAU8360_R26C_RIGHT_BIQ9_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R278_RIGHT_BIQ10_COE ... (NAU8360_R278_RIGHT_BIQ10_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R284_RIGHT_BIQ11_COE ... (NAU8360_R284_RIGHT_BIQ11_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R290_RIGHT_BIQ12_COE ... (NAU8360_R290_RIGHT_BIQ12_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R29C_RIGHT_BIQ13_COE ... (NAU8360_R29C_RIGHT_BIQ13_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_R2A8_RIGHT_BIQ14_COE ... (NAU8360_R2A8_RIGHT_BIQ14_COE +
+					NAU8360_PEQ_REG_WIDTH):
+	case NAU8360_RF000_DSP_COMM:
+	case NAU8360_RF002_DSP_COMM:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool nau8360_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case NAU8360_R00_SOFTWARE_RST ... NAU8360_R02_I2C_ADDR:
+	case NAU8360_R06_INT_CLR_STATUS:
+	case NAU8360_R21_VBAT_READOUT ... NAU8360_R22_TEMP_READOUT:
+	case NAU8360_R41_CLK_CTL2:
+	case NAU8360_R46_I2C_DEVICE_ID:
+	case NAU8360_R69_ANALOG_CONTROL_3:
+	case NAU8360_R77_SOFT_SD ... NAU8360_R79_EN_HIRC48M:
+	case NAU8360_R7E_CLK_GATED_EN:
+	case NAU8360_R9D_PEQ_CTL:
+	case NAU8360_RF000_DSP_COMM:
+	case NAU8360_RF002_DSP_COMM:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const char *const tdm_chan_length[] = { "16", "24", "32" };
+
+static const struct soc_enum nau8360_tdm_chan_len_enum =
+	SOC_ENUM_SINGLE(NAU8360_R0C_I2S_PCM_CTRL2, NAU8360_TDM_CLEN_SFT,
+		ARRAY_SIZE(tdm_chan_length), tdm_chan_length);
+
+static const char *const tdm_data_length[] = { "16", "32" };
+
+static const char *const tdm_pdm_length[] = { "16", "24" };
+
+static const char *const tdm_data_n_length[] = { "8", "16" };
+
+static const char *const tdm_slot_text[] = { "Slot 0", "Slot 1", "Slot 2",
+	"Slot 3", "Slot 4", "Slot 5", "Slot 6", "Slot 7" };
+
+static const struct soc_enum nau8360_tdm_dacl_slot_enum =
+	SOC_ENUM_SINGLE(NAU8360_R0C_I2S_PCM_CTRL2, 0,
+		ARRAY_SIZE(tdm_slot_text), tdm_slot_text);
+
+static const struct soc_enum nau8360_tdm_dacr_slot_enum =
+	SOC_ENUM_SINGLE(NAU8360_R0C_I2S_PCM_CTRL2, NAU8360_RX_DACR_SFT,
+		ARRAY_SIZE(tdm_slot_text), tdm_slot_text);
+
+static const struct soc_enum nau8360_tdm_ancl_slot_enum =
+	SOC_ENUM_SINGLE(NAU8360_R10_I2S_DATA_CTRL3, NAU8360_RX_ANC_L_SFT,
+		ARRAY_SIZE(tdm_slot_text), tdm_slot_text);
+
+static const struct soc_enum nau8360_tdm_ancr_slot_enum =
+	SOC_ENUM_SINGLE(NAU8360_R10_I2S_DATA_CTRL3, NAU8360_RX_ANC_R_SFT,
+		ARRAY_SIZE(tdm_slot_text), tdm_slot_text);
+
+static const struct soc_enum nau8360_aec_data_len_enum =
+	SOC_ENUM_DOUBLE(NAU8360_R17_I2S0_DATA_CTRL5, NAU8360_AEC_L_SLEN_SFT,
+		NAU8360_AEC_R_SLEN_SFT, ARRAY_SIZE(tdm_data_length), tdm_data_length);
+
+static const struct soc_enum nau8360_aecl_slot_enum =
+	SOC_VALUE_ENUM_SINGLE(NAU8360_R17_I2S0_DATA_CTRL5, NAU8360_AEC_L_SLOT_SFT, 0x3f,
+		ARRAY_SIZE(tdm_slot_text), tdm_slot_text, NULL);
+
+static const struct soc_enum nau8360_aecr_slot_enum =
+	SOC_VALUE_ENUM_SINGLE(NAU8360_R17_I2S0_DATA_CTRL5, 0, 0x3f,
+		ARRAY_SIZE(tdm_slot_text), tdm_slot_text, NULL);
+
+static const struct soc_enum nau8360_isnsl_data_len_enum =
+	SOC_ENUM_SINGLE(NAU8360_R0E_I2S_DATA_CTRL1, NAU8360_ISNS_L_SLEN_SFT,
+		ARRAY_SIZE(tdm_data_n_length), tdm_data_n_length);
+
+static const struct soc_enum nau8360_isnsl_slot_enum =
+	SOC_VALUE_ENUM_SINGLE(NAU8360_R0E_I2S_DATA_CTRL1, NAU8360_ISNS_L_SLOT_SFT, 0x3f,
+		ARRAY_SIZE(tdm_slot_text), tdm_slot_text, NULL);
+
+static const struct soc_enum nau8360_isnsr_data_len_enum =
+	SOC_ENUM_SINGLE(NAU8360_R11_I2S_DATA_CTRL4, NAU8360_ISNS_R_SLEN_SFT,
+		ARRAY_SIZE(tdm_data_n_length), tdm_data_n_length);
+
+static const struct soc_enum nau8360_isnsr_slot_enum =
+	SOC_VALUE_ENUM_SINGLE(NAU8360_R11_I2S_DATA_CTRL4, 0, 0x3f,
+		ARRAY_SIZE(tdm_slot_text), tdm_slot_text, NULL);
+
+static const struct soc_enum nau8360_vsnsl_data_len_enum =
+	SOC_ENUM_SINGLE(NAU8360_R0D_I2S_PCM_CTRL3, NAU8360_VSNS_L_SLEN_SFT,
+		ARRAY_SIZE(tdm_data_n_length), tdm_data_n_length);
+
+static const struct soc_enum nau8360_vsnsl_slot_enum =
+	SOC_VALUE_ENUM_SINGLE(NAU8360_R0D_I2S_PCM_CTRL3, 0, 0x3f,
+		ARRAY_SIZE(tdm_slot_text), tdm_slot_text, NULL);
+
+static const struct soc_enum nau8360_vsnsr_data_len_enum =
+	SOC_ENUM_SINGLE(NAU8360_R11_I2S_DATA_CTRL4, NAU8360_VSNS_R_SLEN_SFT,
+		ARRAY_SIZE(tdm_data_n_length), tdm_data_n_length);
+
+static const struct soc_enum nau8360_vsnsr_slot_enum =
+	SOC_VALUE_ENUM_SINGLE(NAU8360_R11_I2S_DATA_CTRL4, NAU8360_VSNS_R_SLOT_SFT, 0x3f,
+		ARRAY_SIZE(tdm_slot_text), tdm_slot_text, NULL);
+
+static const struct soc_enum nau8360_temp_slot_enum =
+	SOC_VALUE_ENUM_SINGLE(NAU8360_R10_I2S_DATA_CTRL3, 0, 0x3f,
+		ARRAY_SIZE(tdm_slot_text), tdm_slot_text, NULL);
+
+static const struct soc_enum nau8360_vbat_slot_enum =
+	SOC_VALUE_ENUM_SINGLE(NAU8360_R0F_I2S_DATA_CTRL2, NAU8360_VBAT_SLOT_SFT, 0x3f,
+		ARRAY_SIZE(tdm_slot_text), tdm_slot_text, NULL);
+
+static const struct soc_enum nau8360_pdml_data_len_enum =
+	SOC_ENUM_SINGLE(NAU8360_R0E_I2S_DATA_CTRL1, NAU8360_PDM_L_SLEN_SFT,
+		ARRAY_SIZE(tdm_pdm_length), tdm_pdm_length);
+
+static const struct soc_enum nau8360_pdml_slot_enum =
+	SOC_VALUE_ENUM_SINGLE(NAU8360_R0E_I2S_DATA_CTRL1, 0, 0x3f,
+		ARRAY_SIZE(tdm_slot_text), tdm_slot_text, NULL);
+
+static const struct soc_enum nau8360_pdmr_data_len_enum =
+	SOC_ENUM_SINGLE(NAU8360_R0F_I2S_DATA_CTRL2, NAU8360_PDM_R_SLEN_SFT,
+		ARRAY_SIZE(tdm_pdm_length), tdm_pdm_length);
+
+static const struct soc_enum nau8360_pdmr_slot_enum =
+	SOC_VALUE_ENUM_SINGLE(NAU8360_R0F_I2S_DATA_CTRL2, 0, 0x3f,
+		ARRAY_SIZE(tdm_slot_text), tdm_slot_text, NULL);
+
+static const char *const tdm_pdm_mode[] = { "SDO=SDI", "HW2 path" };
+
+static const struct soc_enum nau8360_pdm_mode_enum =
+	SOC_ENUM_SINGLE(NAU8360_R10_I2S_DATA_CTRL3, NAU8360_TDM_LOOPBACK_SFT,
+		ARRAY_SIZE(tdm_pdm_mode), tdm_pdm_mode);
+
+static int nau8360_tdm_clen_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *cp = snd_kcontrol_chip(kcontrol);
+	struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+	unsigned int *item = ucontrol->value.enumerated.item;
+	int ret;
+
+	ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+	if (ret)
+		return ret;
+	nau8360->tdm_chan_len = 16 + item[0] * 8;
+
+	return 0;
+}
+
+static int nau8360_anc_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *cp = snd_kcontrol_chip(kcontrol);
+	struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+	int ret, value = 8;
+
+	ret = snd_soc_put_volsw(kcontrol, ucontrol);
+	/* update anc flag if return value 1 and register value changed */
+	if (ret != 1)
+		goto done;
+
+	nau8360->anc_enable = ucontrol->value.integer.value[0];
+	if (nau8360->dsp_enable)
+		value = nau8360->anc_enable ? 0xf : 0xc;
+	regmap_update_bits(nau8360->regmap, NAU8360_R9D_PEQ_CTL, NAU8360_PEQ_BAND_MASK,
+		value << NAU8360_PEQ_BAND_SFT);
+
+done:
+	return ret;
+}
+
+/* TDM TX slot maps according to TDM channel length */
+static inline void compute_slotx_scale(struct snd_soc_component *cp, int *scale)
+{
+	int value = snd_soc_component_read(cp, NAU8360_R0C_I2S_PCM_CTRL2);
+
+	value = (value & NAU8360_TDM_CLEN_MASK) >> NAU8360_TDM_CLEN_SFT;
+	*scale = (16 + value * 8) >> 3;
+}
+
+static int nau8360_tdm_slotx_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *cp = snd_kcontrol_chip(kcontrol);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	unsigned int *item = ucontrol->value.enumerated.item;
+	unsigned int mask = e->mask << e->shift_l;
+	int scale, slot;
+
+	if (item[0] >= e->items)
+		return -EINVAL;
+
+	compute_slotx_scale(cp, &scale);
+	slot = item[0] * scale;
+
+	return snd_soc_component_update_bits(cp, e->reg, mask, slot << e->shift_l);
+}
+
+static int nau8360_tdm_slotx_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *cp = snd_kcontrol_chip(kcontrol);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	int scale, slot = snd_soc_component_read(cp, e->reg);
+
+	compute_slotx_scale(cp, &scale);
+	slot = ((slot >> e->shift_l) & e->mask) / scale;
+	ucontrol->value.enumerated.item[0] = slot;
+
+	return 0;
+}
+
+static inline int nau8360_peq_regaddr(const char *id_name)
+{
+	int reg, band_num, dsp_addr = NAU8360_DSP_ADDR_BYNAME(id_name);
+	char *band = strstr(id_name, "BIQ");
+
+	if (kstrtoint((band + 3), 10, &band_num))
+		return -EINPROGRESS;
+	reg = dsp_addr == NAU8360_RF000_DSP_COMM ? NAU8360_R100_LEFT_BIQ0_COE :
+		NAU8360_R200_RIGHT_BIQ0_COE;
+	reg += band_num * NAU8360_TOT_BAND_COE_RANGE;
+
+	return reg;
+}
+
+static int nau8360_peq_coeff_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *cp = snd_kcontrol_chip(kcontrol);
+	struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+	int i, value, reg = nau8360_peq_regaddr(kcontrol->id.name);
+	u16 *val = (u16 *)ucontrol->value.bytes.data;
+
+	if (reg < 0)
+		return -EIO;
+
+	snd_soc_component_update_bits(cp, NAU8360_R9D_PEQ_CTL, NAU8360_HW1_MEM_TEST,
+		NAU8360_HW1_MEM_TEST);
+	for (i = 0; i < params->max / sizeof(u16); i++) {
+		value = snd_soc_component_read(cp, reg + i);
+		*(val + i) = cpu_to_be16(value);
+	}
+	snd_soc_component_update_bits(cp, NAU8360_R9D_PEQ_CTL, NAU8360_HW1_MEM_TEST, 0);
+
+	return 0;
+}
+
+static int nau8360_peq_coeff_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *cp = snd_kcontrol_chip(kcontrol);
+	struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+	int i, ret, reg = nau8360_peq_regaddr(kcontrol->id.name);
+	__be16 *data;
+
+	if (reg < 0) {
+		ret = -EIO;
+		goto err;
+	}
+	data = kmemdup(ucontrol->value.bytes.data, params->max, GFP_KERNEL | GFP_DMA);
+	if (!data) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	snd_soc_component_update_bits(cp, NAU8360_R9D_PEQ_CTL, NAU8360_HW1_MEM_TEST,
+		NAU8360_HW1_MEM_TEST);
+	for (i = 0; i < params->max / sizeof(u16); i++)
+		snd_soc_component_write(cp, reg + i, be16_to_cpu(*(data + i)));
+	snd_soc_component_update_bits(cp, NAU8360_R9D_PEQ_CTL, NAU8360_HW1_MEM_TEST, 0);
+
+	kfree(data);
+
+	return 0;
+err:
+	return ret;
+}
+
+#define NAU8360_PEQ_COEF_BYTES_EXT(ch, band) \
+	SND_SOC_BYTES_EXT(ch " PEQ Coefficients " band, 20, nau8360_peq_coeff_get, \
+		nau8360_peq_coeff_put)
+
+static const struct snd_kcontrol_new nau8360_snd_controls[] = {
+	NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ0"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ1"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ2"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ3"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ4"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ5"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ6"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ7"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ8"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ9"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ10"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ11"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ12"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ13"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ14"),
+
+	NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ0"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ1"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ2"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ3"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ4"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ5"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ6"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ7"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ8"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ9"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ10"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ11"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ12"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ13"),
+	NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ14"),
+
+	SOC_ENUM_EXT("TDM Channel Length", nau8360_tdm_chan_len_enum,
+		snd_soc_get_enum_double, nau8360_tdm_clen_put),
+	SOC_ENUM("DACL TDM RX Slot", nau8360_tdm_dacl_slot_enum),
+	SOC_ENUM("DACR TDM RX Slot", nau8360_tdm_dacr_slot_enum),
+	SOC_SINGLE_EXT("ANC Path Switch", NAU8360_R96_HW2_CTL6, NAU8360_HW1_ANC_EN_SFT,
+		1, 0, snd_soc_get_volsw, nau8360_anc_put),
+	SOC_ENUM("ANCL TDM RX Slot", nau8360_tdm_ancl_slot_enum),
+	SOC_ENUM("ANCR TDM RX Slot", nau8360_tdm_ancr_slot_enum),
+
+	SOC_DOUBLE("AEC REF Switch", NAU8360_R17_I2S0_DATA_CTRL5,
+		NAU8360_AEC_L_EN_SFT, NAU8360_AEC_R_EN_SFT, 1, 0),
+	SOC_ENUM("AEC REF Data Length", nau8360_aec_data_len_enum),
+	SOC_ENUM_EXT("AEC Left REF Slot", nau8360_aecl_slot_enum,
+		nau8360_tdm_slotx_get, nau8360_tdm_slotx_put),
+	SOC_ENUM_EXT("AEC Right REF Slot", nau8360_aecr_slot_enum,
+		nau8360_tdm_slotx_get, nau8360_tdm_slotx_put),
+
+	SOC_SINGLE("ISNSL TDM TX Switch", NAU8360_R0E_I2S_DATA_CTRL1,
+		NAU8360_ISNS_L_TX_EN_SFT, 1, 0),
+	SOC_ENUM("ISNSL TDM Data Length", nau8360_isnsl_data_len_enum),
+	SOC_ENUM_EXT("ISNSL TDM TX Slot", nau8360_isnsl_slot_enum,
+		nau8360_tdm_slotx_get, nau8360_tdm_slotx_put),
+	SOC_SINGLE("ISNSR TDM TX Switch", NAU8360_R11_I2S_DATA_CTRL4,
+		NAU8360_ISNS_R_TX_EN_SFT, 1, 0),
+	SOC_ENUM("ISNSR TDM Data Length", nau8360_isnsr_data_len_enum),
+	SOC_ENUM_EXT("ISNSR TDM TX Slot", nau8360_isnsr_slot_enum,
+		nau8360_tdm_slotx_get, nau8360_tdm_slotx_put),
+
+	SOC_SINGLE("VSNSL TDM TX Switch", NAU8360_R0D_I2S_PCM_CTRL3,
+		NAU8360_VSNS_L_TX_EN_SFT, 1, 0),
+	SOC_ENUM("VSNSL TDM Data Length", nau8360_vsnsl_data_len_enum),
+	SOC_ENUM_EXT("VSNSL TDM TX Slot", nau8360_vsnsl_slot_enum,
+		nau8360_tdm_slotx_get, nau8360_tdm_slotx_put),
+	SOC_SINGLE("VSNSR TDM TX Switch", NAU8360_R11_I2S_DATA_CTRL4,
+		NAU8360_VSNS_R_TX_EN_SFT, 1, 0),
+	SOC_ENUM("VSNSR TDM Data Length", nau8360_vsnsr_data_len_enum),
+	SOC_ENUM_EXT("VSNSR TDM TX Slot", nau8360_vsnsr_slot_enum,
+		nau8360_tdm_slotx_get, nau8360_tdm_slotx_put),
+
+	SOC_SINGLE("Junction Temperature TDM TX Switch", NAU8360_R10_I2S_DATA_CTRL3,
+		NAU8360_TEMP_TX_EN_SFT, 1, 0),
+	SOC_ENUM_EXT("Junction Temperature TDM TX Slot", nau8360_temp_slot_enum,
+		nau8360_tdm_slotx_get, nau8360_tdm_slotx_put),
+	SOC_SINGLE("VBAT Measured TDM TX Switch", NAU8360_R0F_I2S_DATA_CTRL2,
+		NAU8360_VBAT_TX_EN_SFT, 1, 0),
+	SOC_ENUM_EXT("VBAT Measured TDM TX Slot", nau8360_vbat_slot_enum,
+		nau8360_tdm_slotx_get, nau8360_tdm_slotx_put),
+
+	SOC_DOUBLE_R("TDM Loopback Switch", NAU8360_R0E_I2S_DATA_CTRL1,
+		NAU8360_R0F_I2S_DATA_CTRL2, NAU8360_PDM_L_TX_EN_SFT, 1, 0),
+	SOC_ENUM("TDM Loopback Left Data Length", nau8360_pdml_data_len_enum),
+	SOC_ENUM_EXT("TDM Loopback Left Slot", nau8360_pdml_slot_enum,
+		nau8360_tdm_slotx_get, nau8360_tdm_slotx_put),
+	SOC_ENUM("TDM Loopback Right Data Length", nau8360_pdmr_data_len_enum),
+	SOC_ENUM_EXT("TDM Loopback Right Slot", nau8360_pdmr_slot_enum,
+		nau8360_tdm_slotx_get, nau8360_tdm_slotx_put),
+	SOC_ENUM("TDM Loopback Mode", nau8360_pdm_mode_enum),
+};
+
+static int nau8360_adci_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);
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		snd_soc_component_update_bits(component, NAU8360_R6C_IVSNS_CFG0,
+			NAU8360_PD_ISNS_R_PMD | NAU8360_PD_ISNS_L_PMD, 0);
+	else if (SND_SOC_DAPM_EVENT_OFF(event))
+		snd_soc_component_update_bits(component, NAU8360_R6C_IVSNS_CFG0,
+			NAU8360_PD_ISNS_R_PMD | NAU8360_PD_ISNS_L_PMD,
+			NAU8360_PD_ISNS_R_PMD | NAU8360_PD_ISNS_L_PMD);
+
+	return 0;
+}
+
+static int nau8360_adcv_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);
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		snd_soc_component_update_bits(component, NAU8360_R6C_IVSNS_CFG0,
+			NAU8360_PD_VSNS_R_PMD | NAU8360_PD_VSNS_L_PMD, 0);
+	else if (SND_SOC_DAPM_EVENT_OFF(event))
+		snd_soc_component_update_bits(component, NAU8360_R6C_IVSNS_CFG0,
+			NAU8360_PD_VSNS_R_PMD | NAU8360_PD_VSNS_L_PMD,
+			NAU8360_PD_VSNS_R_PMD | NAU8360_PD_VSNS_L_PMD);
+
+	return 0;
+}
+
+static int nau8360_aif_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);
+
+	if (SND_SOC_DAPM_EVENT_OFF(event))
+		snd_soc_component_update_bits(component, NAU8360_R67_ANALOG_CONTROL_0,
+			NAU8360_HV_EN, 0);
+
+	return 0;
+}
+
+static int nau8360_hw1_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);
+
+	if (SND_SOC_DAPM_EVENT_OFF(event))
+		snd_soc_component_update_bits(component, NAU8360_R67_ANALOG_CONTROL_0,
+			NAU8360_ANA_MUTE, NAU8360_ANA_MUTE);
+
+	return 0;
+}
+
+static int nau8360_hw1_mux_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);
+
+	if (SND_SOC_DAPM_EVENT_OFF(event)) {
+		snd_soc_component_update_bits(component, NAU8360_R68_ANALOG_CONTROL_1,
+			NAU8360_DRVCTL_SEGL_FULL | NAU8360_DRVCTL_SEGR_FULL, 0);
+		snd_soc_component_update_bits(component, NAU8360_RA5_ANA_REG_1,
+			NAU8360_CLASSD_SHT_IN | NAU8360_HVEN_SYNC_SAW,
+			NAU8360_CLASSD_SHT_IN | NAU8360_HVEN_SYNC_SAW);
+		msleep(20);
+	}
+
+	return 0;
+}
+
+static int nau8360_hw2_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);
+
+	if (SND_SOC_DAPM_EVENT_ON(event)) {
+		snd_soc_component_update_bits(component, NAU8360_R99_HW2_CTL9,
+			NAU8360_HW2_CH_MUTE, 0);
+		snd_soc_component_update_bits(component, NAU8360_R9C_HW1_CTL2,
+			NAU8360_HW1_CH_MUTE, 0);
+	}
+
+	return 0;
+}
+
+static int nau8360_adacl_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);
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		snd_soc_component_update_bits(component, NAU8360_R6E_DAC_CFG0,
+			NAU8360_PD_DACL_DIS, 0);
+
+	return 0;
+}
+
+static int nau8360_adacr_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);
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		snd_soc_component_update_bits(component, NAU8360_R6E_DAC_CFG0,
+			NAU8360_PD_DACR_DIS, 0);
+
+	return 0;
+}
+
+static int nau8360_dacl_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);
+
+	if (SND_SOC_DAPM_EVENT_OFF(event))
+		snd_soc_component_update_bits(component, NAU8360_R6E_DAC_CFG0,
+			NAU8360_PD_DACL_DIS, NAU8360_PD_DACL_DIS);
+
+	return 0;
+}
+
+static int nau8360_dacr_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);
+
+	if (SND_SOC_DAPM_EVENT_OFF(event))
+		snd_soc_component_update_bits(component, NAU8360_R6E_DAC_CFG0,
+			NAU8360_PD_DACR_DIS, NAU8360_PD_DACR_DIS);
+
+	return 0;
+}
+
+static int nau8360_hv_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);
+
+	if (SND_SOC_DAPM_EVENT_ON(event)) {
+		/* enable Class D HV power after DAC power is stable */
+		snd_soc_component_update_bits(component, NAU8360_R67_ANALOG_CONTROL_0,
+			NAU8360_HV_EN, NAU8360_HV_EN);
+		msleep(50);
+		/* Class D modulator input short setting for mute and de-pop purpose.
+		 * Restore normal after initiation. Set segment driver as full driving
+		 * strength.
+		 */
+		snd_soc_component_update_bits(component, NAU8360_RA5_ANA_REG_1,
+			NAU8360_CLASSD_SHT_IN | NAU8360_HVEN_SYNC_SAW, 0);
+		snd_soc_component_update_bits(component, NAU8360_R68_ANALOG_CONTROL_1,
+			NAU8360_DRVCTL_SEGL_FULL | NAU8360_DRVCTL_SEGR_FULL,
+			NAU8360_DRVCTL_SEGL_FULL | NAU8360_DRVCTL_SEGR_FULL);
+	}
+
+	return 0;
+}
+
+static int nau8360_hv_pre_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);
+
+	if (SND_SOC_DAPM_EVENT_OFF(event)) {
+		snd_soc_component_update_bits(component, NAU8360_R9C_HW1_CTL2,
+			NAU8360_HW1_CH_MUTE, NAU8360_HW1_CH_MUTE);
+		snd_soc_component_update_bits(component, NAU8360_R99_HW2_CTL9,
+			NAU8360_HW2_CH_MUTE, NAU8360_HW2_CH_MUTE);
+	}
+
+	return 0;
+}
+
+static int nau8360_hv_post_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);
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		snd_soc_component_update_bits(component, NAU8360_R67_ANALOG_CONTROL_0,
+			NAU8360_ANA_MUTE, 0);
+
+	return 0;
+}
+
+static int nau8360_dsp_switch(struct snd_soc_component *component, bool enable)
+{
+	struct nau8360 *nau8360 = snd_soc_component_get_drvdata(component);
+	struct regmap *regmap = nau8360->regmap;
+	int ret, value = 8;
+
+	/* If DSP is enabled, unstall HW3 engine and DSP, loading DSP firmware,
+	 * and configure PEQ after dsp reset.
+	 */
+	if (enable) {
+		nau8360_dsp_software_reset(component);
+		value = nau8360->anc_enable ? 0xf : 0xc;
+		nau8360_dsp_enable(regmap, true);
+		if (!nau8360->dsp_created)
+			ret = nau8360_dsp_init(component);
+		else
+			ret = nau8360_dsp_reinit(component);
+		if (ret) {
+			nau8360_dsp_enable(regmap, false);
+			dev_err(nau8360->dev, "can't enable DSP (%d)", ret);
+			return ret;
+		}
+	} else {
+		dev_warn(nau8360->dev, "Bypass DSP !!");
+		nau8360_dsp_enable(regmap, false);
+	}
+	regmap_update_bits(regmap, NAU8360_R9D_PEQ_CTL, NAU8360_PEQ_BAND_MASK,
+		value << NAU8360_PEQ_BAND_SFT);
+
+	return 0;
+}
+
+static int nau8360_dac_mux_put_enum(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+	struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+	struct nau8360 *nau8360 = snd_soc_component_get_drvdata(component);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	unsigned int *item = ucontrol->value.enumerated.item;
+	bool dsp_en = snd_soc_enum_item_to_val(e, item[0]);
+	int ret = 0;
+
+	if (nau8360->dsp_enable == dsp_en)
+		goto done;
+
+	if (snd_soc_dapm_get_bias_level(dapm) > SND_SOC_BIAS_STANDBY) {
+		dev_err(nau8360->dev, "changing path is not allowed during playback");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	ret = nau8360_dsp_switch(component, dsp_en);
+	if (ret)
+		goto err;
+
+	ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+	if (ret < 0)
+		goto err;
+
+	nau8360->dsp_enable = dsp_en;
+
+done:
+	return ret;
+err:
+	nau8360_dsp_switch(component, !dsp_en);
+	return ret;
+}
+
+/* Select HW1 output source (enables PEQ bypass) */
+static const char *const nau8360_hw1out_src[] = { "Audio", "PEQ" };
+
+static SOC_ENUM_SINGLE_DECL(nau8360_hw1out_enum, NAU8360_R12_PATH_CTRL,
+	NAU8360_SEL_HW1_SFT, nau8360_hw1out_src);
+
+static const struct snd_kcontrol_new nau8360_hw1out_mux =
+	SOC_DAPM_ENUM("HW1 Output Source", nau8360_hw1out_enum);
+
+/* Select DAC input source (enables DSP bypass) */
+static const char *const nau8360_dac_src[] = { "HW1", "DSP" };
+
+static SOC_ENUM_SINGLE_DECL(nau8360_dac_enum, NAU8360_R12_PATH_CTRL,
+	NAU8360_DAC_SEL_SFT, nau8360_dac_src);
+
+static const struct snd_kcontrol_new nau8360_dac_mux =
+	SOC_DAPM_ENUM_EXT("DAC Source", nau8360_dac_enum,
+		snd_soc_dapm_get_enum_double, nau8360_dac_mux_put_enum);
+
+static const struct snd_soc_dapm_widget nau8360_dapm_widgets[] = {
+	SND_SOC_DAPM_SIGGEN("Sense"),
+	SND_SOC_DAPM_ADC_E("ADC_I", NULL, SND_SOC_NOPM, 0, 0, nau8360_adci_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_ADC_E("ADC_V", NULL, SND_SOC_NOPM, 0, 0, nau8360_adcv_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_PGA("DSP", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("HW3 Engine", NAU8360_R8C_HW3_CTL6, 3, 0, NULL, 0),
+
+	SND_SOC_DAPM_AIF_IN_E("AIFRX", "Playback", 0, SND_SOC_NOPM, 0, 0,
+		nau8360_aif_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_PGA_S("HW1 Engine", 0, NAU8360_R9D_PEQ_CTL, 0, 1,
+		nau8360_hw1_event, SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("HW1 Mux", SND_SOC_NOPM, 0, 0, &nau8360_hw1out_mux,
+		nau8360_hw1_mux_event, SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("DAC Mux", SND_SOC_NOPM, 0, 0, &nau8360_dac_mux),
+
+	SND_SOC_DAPM_PGA_S("HW2 Engine", 1, NAU8360_R96_HW2_CTL6, 5, 0,
+		nau8360_hw2_event, SND_SOC_DAPM_POST_PMU),
+
+	SND_SOC_DAPM_SUPPLY("DAC Clock", NAU8360_R71_CLK_DIV_CFG, 9, 1, NULL, 0),
+	SND_SOC_DAPM_DAC_E("DACL", NULL, SND_SOC_NOPM, 0, 0,
+		nau8360_dacl_event, SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_DAC_E("DACR", NULL, SND_SOC_NOPM, 0, 0,
+		nau8360_dacr_event, SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_S("ADACL", 2, SND_SOC_NOPM, 0, 0,
+		nau8360_adacl_event, SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_PGA_S("ADACR", 2, SND_SOC_NOPM, 0, 0,
+		nau8360_adacr_event, SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_PGA_S("Class D", 3, SND_SOC_NOPM, 0, 0,
+		nau8360_hv_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_PRE("Class D Pre", nau8360_hv_pre_event),
+	SND_SOC_DAPM_POST("Class D Post", nau8360_hv_post_event),
+
+	SND_SOC_DAPM_OUTPUT("OUTL"),
+	SND_SOC_DAPM_OUTPUT("OUTR"),
+};
+
+static const struct snd_soc_dapm_route nau8360_dapm_routes[] = {
+	{ "ADC_I", NULL, "Sense" },
+	{ "ADC_V", NULL, "Sense" },
+	{ "HW3 Engine", NULL, "ADC_I" },
+	{ "HW3 Engine", NULL, "ADC_V" },
+	{ "Capture", NULL, "HW3 Engine" },
+
+	{ "HW1 Engine", NULL, "AIFRX" },
+	{ "HW1 Mux", "Audio", "AIFRX" },
+	{ "HW1 Mux", "PEQ", "HW1 Engine" },
+
+	{ "DSP", NULL, "HW1 Mux" },
+	{ "DAC Mux", "HW1", "HW1 Mux" },
+	{ "DAC Mux", "DSP", "DSP" },
+
+	{ "HW2 Engine", NULL, "DAC Mux" },
+	{ "DACL", NULL, "HW2 Engine" },
+	{ "DACR", NULL, "HW2 Engine" },
+	{ "DACL", NULL, "DAC Clock" },
+	{ "DACR", NULL, "DAC Clock" },
+
+	{ "ADACL", NULL, "DACL" },
+	{ "ADACR", NULL, "DACR" },
+	{ "Class D", NULL, "ADACL" },
+	{ "Class D", NULL, "ADACR" },
+
+	{ "OUTL", NULL, "Class D" },
+	{ "OUTR", NULL, "Class D" },
+};
+
+static int nau8360_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct nau8360 *nau8360 = snd_soc_component_get_drvdata(component);
+
+	regmap_update_bits(nau8360->regmap, NAU8360_R0B_I2S_PCM_CTRL1,
+		NAU8360_EN_TDM_TX | NAU8360_EN_TDM_RX,
+		NAU8360_EN_TDM_TX | NAU8360_EN_TDM_RX);
+	if (nau8360->dsp_enable && substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		snd_soc_dapm_enable_pin(nau8360->dapm, "Sense");
+
+	return 0;
+}
+
+static void nau8360_shutdown(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct nau8360 *nau8360 = snd_soc_component_get_drvdata(component);
+
+	regmap_update_bits(nau8360->regmap, NAU8360_R0B_I2S_PCM_CTRL1,
+		NAU8360_EN_TDM_TX | NAU8360_EN_TDM_RX, 0);
+	if (nau8360->dsp_enable && substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		snd_soc_dapm_disable_pin(nau8360->dapm, "Sense");
+}
+
+static int nau8360_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 nau8360 *nau8360 = snd_soc_component_get_drvdata(component);
+	unsigned int val_len, val_srate;
+	int ret, dlen = params_width(params);
+
+	if (dlen > nau8360->tdm_chan_len) {
+		dev_err(nau8360->dev, "Invalid data length");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	switch (dlen) {
+	case 16:
+		val_len = NAU8360_TDM_DLEN_16;
+		break;
+	case 20:
+		val_len = NAU8360_TDM_DLEN_20;
+		break;
+	case 24:
+		val_len = NAU8360_TDM_DLEN_24;
+		break;
+	case 32:
+		val_len = NAU8360_TDM_DLEN_32;
+		break;
+	default:
+		ret = -EINVAL;
+		goto err;
+	}
+
+	switch (params_rate(params)) {
+	case 16000:
+		val_srate = NAU8360_SRATE_16000;
+		break;
+	case 32000:
+		val_srate = NAU8360_SRATE_32000;
+		break;
+	case 44100:
+	case 48000:
+		val_srate = NAU8360_SRATE_48000;
+		break;
+	case 88200:
+	case 96000:
+		val_srate = NAU8360_SRATE_96000;
+		break;
+	case 176400:
+	case 192000:
+		val_srate = NAU8360_SRATE_192000;
+		break;
+	default:
+		ret = -EINVAL;
+		goto err;
+	}
+
+	regmap_update_bits(nau8360->regmap, NAU8360_R0C_I2S_PCM_CTRL2,
+		NAU8360_TDM_DLEN_MASK, val_len);
+	regmap_update_bits(nau8360->regmap, NAU8360_R40_CLK_DET_CTRL,
+		NAU8360_SRATE_MASK, val_srate);
+
+	return 0;
+err:
+	return ret;
+}
+
+static int nau8360_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_component *component = dai->component;
+	unsigned int ctrl_val, ctrl1_val;
+	int ret;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBC_CFC:
+		break;
+	default:
+		ret = -EINVAL;
+		goto err;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	default:
+		ret = -EINVAL;
+		goto err;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		ctrl_val = NAU8360_FRAME_START_H2L | NAU8360_RX_OFFSET_I2S;
+		ctrl1_val = NAU8360_TX_OFFSET_I2S;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		ctrl_val = NAU8360_FRAME_START_H2L | NAU8360_RX_OFFSET_LEFT;
+		ctrl1_val = NAU8360_TX_OFFSET_LEFT;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		ctrl_val = NAU8360_FRAME_START_H2L | NAU8360_RX_OFFSET_RIGHT;
+		ctrl1_val = NAU8360_TX_OFFSET_RIGHT;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		ctrl_val = NAU8360_RX_OFFSET_PCM_A;
+		ctrl1_val = NAU8360_TX_OFFSET_PCM_A;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		ctrl_val = NAU8360_RX_OFFSET_PCM_B;
+		ctrl1_val = NAU8360_TX_OFFSET_PCM_B;
+		break;
+	default:
+		ret = -EINVAL;
+		goto err;
+	}
+
+	snd_soc_component_update_bits(component, NAU8360_R0B_I2S_PCM_CTRL1,
+		NAU8360_FRAME_START_MASK | NAU8360_RX_OFFSET_MASK, ctrl_val);
+	snd_soc_component_update_bits(component, NAU8360_R0D_I2S_PCM_CTRL3,
+		NAU8360_TX_OFFSET_MASK, ctrl1_val);
+
+	return 0;
+err:
+	return ret;
+}
+
+static int nau8360_set_tdm_tx_slot(struct snd_soc_component *cp, int type, int slot)
+{
+	switch (type) {
+	case NAU8360_TDM_AECL:
+		snd_soc_component_update_bits(cp, NAU8360_R17_I2S0_DATA_CTRL5,
+			NAU8360_AEC_L_SLOT_MASK, slot << NAU8360_AEC_L_SLOT_SFT);
+		break;
+
+	case NAU8360_TDM_AECR:
+		snd_soc_component_update_bits(cp, NAU8360_R17_I2S0_DATA_CTRL5,
+			NAU8360_AEC_R_SLOT_MASK, slot);
+		break;
+
+	case NAU8360_TDM_ISNSL:
+		snd_soc_component_update_bits(cp, NAU8360_R0E_I2S_DATA_CTRL1,
+			NAU8360_ISNS_L_SLOT_MASK, slot << NAU8360_ISNS_L_SLOT_SFT);
+		break;
+
+	case NAU8360_TDM_ISNSR:
+		snd_soc_component_update_bits(cp, NAU8360_R11_I2S_DATA_CTRL4,
+			NAU8360_ISNS_R_SLOT_MASK, slot);
+		break;
+
+	case NAU8360_TDM_VSNSL:
+		snd_soc_component_update_bits(cp, NAU8360_R0D_I2S_PCM_CTRL3,
+			NAU8360_VSNS_L_SLOT_MASK, slot);
+		break;
+
+	case NAU8360_TDM_VSNSR:
+		snd_soc_component_update_bits(cp, NAU8360_R11_I2S_DATA_CTRL4,
+			NAU8360_VSNS_R_SLOT_MASK, slot << NAU8360_VSNS_R_SLOT_SFT);
+		break;
+
+	case NAU8360_TDM_TJ:
+		snd_soc_component_update_bits(cp, NAU8360_R10_I2S_DATA_CTRL3,
+			NAU8360_TEMP_SLOT_MASK, slot);
+		break;
+
+	case NAU8360_TDM_VBAT:
+		snd_soc_component_update_bits(cp, NAU8360_R0F_I2S_DATA_CTRL2,
+			NAU8360_VBAT_SLOT_MASK, slot << NAU8360_VBAT_SLOT_SFT);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * nau8360_set_tdm_slot - configure DAI TDM.
+ * @tx_mask: 4-bits value representing each active TX slots. Range: 0 (skip), 1~8. Ex.
+ *	bit 0-3 for left AEC output channel selection
+ *	bit 4-7 for right AEC output channel selection
+ *	bit 8-11 for left Isense output channel selection
+ *	bit 12-15 for right Isense output channel selection
+ *	bit 16-19 for left Vsense output channel selection
+ *	bit 20-23 for right Vsense output channel selection
+ *	bit 24-27 for Junction Temperature (Tj) data output channel selection
+ *	bit 28-31 for VBAT measured data output channel selection
+ * @rx_mask: Bitmask representing active RX slots. Ex.
+ *	bit 0-7 for left DAC channel source selection
+ *	bit 8-15 for right DAC channel source selection
+ *	bit 16-23 for left ANC channel source selection
+ *	bit 24-31 for right ANC channel source selection
+ *
+ * Configures a DAI for TDM operation. Only support 8 slots TDM.
+ */
+static int nau8360_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+	unsigned int rx_mask, int slots, int slot_width)
+{
+	struct snd_soc_component *cp = dai->component;
+	struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+	int ret, i, slot_scale, chan[NAU8360_TDM_RXN], chan_tx;
+	unsigned int mask;
+
+	slot_scale = nau8360->tdm_chan_len >> 3;
+	for (i = 0; i < NAU8360_TDM_TXN; i++) {
+		mask = (tx_mask >> (i * 4)) & 0xf;
+		if (!mask)
+			continue;
+		else if (mask > NAU8360_TDM_MAX_CHAN) {
+			dev_err(cp->dev, "Invalid channel on TDM TX %d.", i);
+			ret = -EINVAL;
+			goto err;
+		}
+		/* compute the slot location in bytes according to slot/chan width */
+		chan_tx = slot_scale * (mask - 1);
+		ret = nau8360_set_tdm_tx_slot(cp, i, chan_tx);
+		if (ret)
+			goto err;
+	}
+
+	for (i = 0; i < NAU8360_TDM_RXN; i++) {
+		mask = (rx_mask >> (i * 8)) & 0xff;
+		if (hweight_long(mask) != 1) {
+			dev_err(cp->dev, "Invalid channel on TDM RX %d.", i);
+			ret = -EINVAL;
+			goto err;
+		}
+		chan[i] = ffs(mask) - 1;
+	}
+	snd_soc_component_update_bits(cp, NAU8360_R10_I2S_DATA_CTRL3,
+		NAU8360_RX_ANC_R_MASK | NAU8360_RX_ANC_L_MASK,
+		(chan[NAU8360_TDM_ANCR] << NAU8360_RX_ANC_R_SFT) |
+		(chan[NAU8360_TDM_ANCL] << NAU8360_RX_ANC_L_SFT));
+	snd_soc_component_update_bits(cp, NAU8360_R0C_I2S_PCM_CTRL2,
+		NAU8360_RX_DACL_MASK | NAU8360_RX_DACR_MASK, chan[NAU8360_TDM_DACL] |
+		(chan[NAU8360_TDM_DACR] << NAU8360_RX_DACR_SFT));
+
+	dev_dbg(cp->dev, "TDM: tx_mask 0x%08X, rx_mask 0x%08X", tx_mask, rx_mask);
+
+	return 0;
+err:
+	return ret;
+}
+
+static inline int dyn_clk_div(int div_max, int fin, int fout)
+{
+	int clk_div;
+
+	if (!fin || !fout)
+		return -EINVAL;
+
+	for (clk_div = 0; clk_div <= div_max; clk_div++)
+		if (fin / (clk_div + 1) <= fout)
+			break;
+	if (clk_div > div_max)
+		return -EINVAL;
+
+	return clk_div;
+}
+
+static inline int tab_clk_div(const int clk_div[], int num_div, int fin)
+{
+	int i;
+
+	if (!fin)
+		return -EINVAL;
+
+	for (i = 0; i < num_div; i++)
+		if ((fin / clk_div[i]) <= CLK_DA_IVSNS_MAX)
+			break;
+	if (i == num_div)
+		return -EINVAL;
+
+	return i;
+}
+
+static int nau8360_set_sysclk_output(struct nau8360 *nau8360, unsigned int freq)
+{
+	int ratio = 0, mclk_rate;
+
+	if (freq < MASTER_CLK_MIN || freq > MASTER_CLK_MAX) {
+		dev_err(nau8360->dev, "system clock %d Hz exceed range", freq);
+		goto err;
+	}
+
+	if (freq % ADSP_SR_48000 == 0)
+		ratio = freq / ADSP_SR_48000;
+	else if (freq % ADSP_SR_44100 == 0)
+		ratio = freq / ADSP_SR_44100;
+	else
+		goto err;
+
+	switch (ratio) {
+	case NAU8360_MCLK_FS_RATIO_250:
+		mclk_rate = NAU8360_MCLK_RATE_12000;
+		break;
+	case NAU8360_MCLK_FS_RATIO_256:
+		mclk_rate = NAU8360_MCLK_RATE_12288;
+		break;
+	case NAU8360_MCLK_FS_RATIO_400:
+		mclk_rate = NAU8360_MCLK_RATE_19200;
+		break;
+	case NAU8360_MCLK_FS_RATIO_500:
+		mclk_rate = NAU8360_MCLK_RATE_24000;
+		break;
+	case NAU8360_MCLK_FS_RATIO_512:
+		mclk_rate = NAU8360_MCLK_RATE_24576;
+		break;
+	default:
+		dev_err(nau8360->dev, "invalid sysclk %d", freq);
+		goto err;
+	}
+
+	dev_dbg(nau8360->dev, "sysclk %d Hz, ratio %d", freq, ratio);
+
+	nau8360->sys_clk = freq;
+	regmap_update_bits(nau8360->regmap, NAU8360_R40_CLK_DET_CTRL,
+		NAU8360_MCLK_RATE_MASK, mclk_rate);
+
+	return 0;
+err:
+	return -EINVAL;
+}
+
+static int nau8360_dig_sys_clk(struct nau8360 *nau8360, int source, unsigned int freq)
+{
+	struct device *dev = nau8360->dev;
+	struct regmap *regmap = nau8360->regmap;
+	int value, mclk_div;
+
+	if (source == NAU8360_CLK_SRC_ICLK) {
+		/* switch HIRC and clock down to 12MHz  */
+		regmap_update_bits(regmap, NAU8360_R04_CLK_CTRL1, NAU8360_MCLK_SEL_MASK,
+			NAU8360_MCLK_SEL_HIRC48M);
+		regmap_update_bits(regmap, NAU8360_R03_CLK_CTRL0,
+			NAU8360_MCLK_DIV_MASK, 0x3 << NAU8360_MCLK_DIV_SFT);
+		return 0;
+	}
+
+	if (source == NAU8360_CLK_SRC_MCLK)
+		value = NAU8360_MCLK_SEL_MCLK;
+	else if (source == NAU8360_CLK_SRC_PLL)
+		value = NAU8360_MCLK_SEL_PLL;
+	else
+		goto err;
+	regmap_update_bits(regmap, NAU8360_R04_CLK_CTRL1, NAU8360_MCLK_SEL_MASK, value);
+
+	mclk_div = dyn_clk_div(NAU8360_MCLK_DIV_MAX, freq, nau8360->sys_clk);
+	if (mclk_div < 0) {
+		dev_err(dev, "mclk_div (%d -> %d):  error", freq, nau8360->sys_clk);
+		goto err;
+	}
+	regmap_update_bits(regmap, NAU8360_R03_CLK_CTRL0, NAU8360_MCLK_DIV_MASK,
+		mclk_div << NAU8360_MCLK_DIV_SFT);
+
+	dev_dbg(dev, " mclk_div (%d -> %d): %d", freq, nau8360->sys_clk, mclk_div + 1);
+
+	return 0;
+err:
+	return -EINVAL;
+}
+
+static int nau8360_ana_sys_clk(struct nau8360 *nau8360, int source, unsigned int freq)
+{
+	struct device *dev = nau8360->dev;
+	struct regmap *regmap = nau8360->regmap;
+	struct nau8360_pll *pll = &nau8360->pll;
+	int value, pclk_div, ivdiv_sel, ddiv_sel;
+
+	if (source == NAU8360_CLK_SRC_PLL)
+		value = NAU8360_CLK_ANA_SEL_PLL;
+	else if (source == NAU8360_CLK_SRC_MCLK)
+		value = NAU8360_CLK_ANA_SEL_MCLK;
+	else if (source == NAU8360_CLK_SRC_BCLK)
+		value = NAU8360_CLK_ANA_SEL_BCLK;
+	else
+		goto err;
+
+	if (source == NAU8360_CLK_SRC_PLL) {
+		freq = pll->output;
+		pclk_div = dyn_clk_div(NAU8360_PLLOUT_DIV_MAX, freq, nau8360->sys_clk);
+		if (pclk_div < 0) {
+			dev_err(dev, "pll_div (%d -> %d): error", freq, nau8360->sys_clk);
+			goto err;
+		}
+
+		dev_dbg(dev, " pll_div (%d -> %d): %d", freq, nau8360->sys_clk, pclk_div + 1);
+
+		regmap_update_bits(regmap, NAU8360_R72_PLL_CFG0, NAU8360_PLLOUT_DIV_MASK,
+			pclk_div << NAU8360_PLLOUT_DIV_SFT);
+		freq /= (pclk_div + 1);
+	}
+
+	ivdiv_sel = tab_clk_div(ivsns_clk_div, ARRAY_SIZE(ivsns_clk_div), freq);
+	if (ivdiv_sel < 0) {
+		dev_err(dev, "iv_div (%d -> %d): error", freq, CLK_DA_IVSNS_MAX);
+		goto err;
+	}
+	value |= ivdiv_sel << NAU8360_IVSNS_CLK_DIV_SFT;
+
+	ddiv_sel = tab_clk_div(dac_clk_div, ARRAY_SIZE(dac_clk_div), freq);
+	if (ddiv_sel < 0) {
+		dev_err(dev, "dac_div (%d -> %d): error", freq, CLK_DA_IVSNS_MAX);
+		goto err;
+	}
+	value |= ddiv_sel << NAU8360_DAC_CLK_DIV_SFT;
+
+	dev_dbg(dev, " clk_div (%d -> %d): ivsns %d, dac %d", freq, CLK_DA_IVSNS_MAX,
+		ivsns_clk_div[ivdiv_sel], dac_clk_div[ddiv_sel]);
+
+	regmap_update_bits(regmap, NAU8360_R71_CLK_DIV_CFG, NAU8360_CLK_ANA_SEL_MASK |
+		NAU8360_IVSNS_CLK_DIV_MASK | NAU8360_DAC_CLK_DIV_MASK, value);
+
+	return 0;
+err:
+	return -EINVAL;
+}
+
+static int nau8360_dsp_hw_clk(struct nau8360 *nau8360, int source, unsigned int freq)
+{
+	struct device *dev = nau8360->dev;
+	struct regmap *regmap = nau8360->regmap;
+	int val_dsp, val_hw, clk_div, dsp_clk;
+
+	if (source == NAU8360_CLK_SRC_ICLK) {
+		/* switch HIRC and clock down to 12MHz  */
+		regmap_update_bits(regmap, NAU8360_R03_CLK_CTRL0,
+			NAU8360_DSP_CLK_SEL_MASK | NAU8360_DSP_CLK_DIV_MASK,
+			NAU8360_DSP_CLK_SEL_HIRC48M | 0x3 << NAU8360_DSP_CLK_DIV_SFT);
+		regmap_update_bits(regmap, NAU8360_R04_CLK_CTRL1,
+			NAU8360_HW_CLK_SEL_MASK | NAU8360_HW_CLK_DIV_MASK,
+			NAU8360_HW_CLK_SEL_HIRC48M | 0x3 << NAU8360_HW_CLK_DIV_SFT);
+		return 0;
+	}
+
+	if (freq % ADSP_SR_48000 == 0)
+		dsp_clk = DSP_OP_CLK48;
+	else if (freq % ADSP_SR_44100 == 0)
+		dsp_clk = DSP_OP_CLK44;
+	else
+		goto err;
+	clk_div = dyn_clk_div(NAU8360_DSP_CLK_DIV_MAX, freq, dsp_clk);
+	if (clk_div < 0) {
+		dev_err(dev, "dsp/hw clk_div (%d -> %d): error", freq, dsp_clk);
+		goto err;
+	}
+	val_dsp = clk_div << NAU8360_DSP_CLK_DIV_SFT;
+	val_hw = clk_div << NAU8360_HW_CLK_DIV_SFT;
+	if (source == NAU8360_CLK_SRC_MCLK) {
+		val_dsp |= NAU8360_DSP_CLK_SEL_MCLK;
+		val_hw |= NAU8360_HW_CLK_SEL_MCLK;
+	} else if (source == NAU8360_CLK_SRC_PLL) {
+		val_dsp |= NAU8360_DSP_CLK_SEL_PLL;
+		val_hw |= NAU8360_HW_CLK_SEL_PLL;
+	} else
+		goto err;
+
+	dev_dbg(dev, " dsp/hw clk_div (%d -> %d): %d", freq, dsp_clk, clk_div + 1);
+
+	regmap_update_bits(regmap, NAU8360_R03_CLK_CTRL0, NAU8360_DSP_CLK_SEL_MASK |
+		NAU8360_DSP_CLK_DIV_MASK, val_dsp);
+	regmap_update_bits(regmap, NAU8360_R04_CLK_CTRL1, NAU8360_HW_CLK_SEL_MASK |
+		NAU8360_HW_CLK_DIV_MASK, val_hw);
+
+	return 0;
+err:
+	return -EINVAL;
+}
+
+static int nau8360_set_sysclk(struct snd_soc_component *component,
+	int clk_id, int source, unsigned int freq, int dir)
+{
+	struct nau8360 *nau8360 = snd_soc_component_get_drvdata(component);
+	static const char * const idtab[] = { "DIG", "ANA", "Internal" };
+	static const char * const srctab[] = { "MCLK", "PLL", "HIRC48M", "BCLK" };
+	int ret;
+
+	if (dir == SND_SOC_CLOCK_OUT) {
+		dev_dbg(nau8360->dev, "sysclk: freq %d (out)", freq);
+		return nau8360_set_sysclk_output(nau8360, freq);
+	}
+
+	switch (clk_id) {
+	case NAU8360_CLK_ID_INT:
+		source = NAU8360_CLK_SRC_ICLK;
+		dev_dbg(nau8360->dev, "sysclk: id %d (%s), src %d (%s) (in)",
+			clk_id, idtab[clk_id], source, srctab[source]);
+
+		nau8360_dsp_hw_clk(nau8360, source, 0);
+		nau8360_dig_sys_clk(nau8360, source, 0);
+
+		regmap_update_bits(nau8360->regmap, NAU8360_R72_PLL_CFG0,
+			NAU8360_PD_PLL_MASK, NAU8360_PD_PLL_DIS);
+		break;
+
+	case NAU8360_CLK_ID_DIG:
+		dev_dbg(nau8360->dev, "sysclk: id %d (%s), src %d (%s), freq %d (in)",
+			clk_id, idtab[clk_id], source, srctab[source], freq);
+
+		if (source == NAU8360_CLK_SRC_BCLK) {
+			ret = -EINVAL;
+			goto err;
+		}
+		if (source == NAU8360_CLK_SRC_MCLK)
+			regmap_update_bits(nau8360->regmap, NAU8360_R72_PLL_CFG0,
+				NAU8360_PD_PLL_MASK, NAU8360_PD_PLL_DIS);
+
+		ret = nau8360_dig_sys_clk(nau8360, source, freq);
+		if (ret)
+			goto err;
+		ret = nau8360_dsp_hw_clk(nau8360, source, freq);
+		if (ret)
+			goto err;
+		break;
+
+	case NAU8360_CLK_ID_ANA:
+		dev_dbg(nau8360->dev, "sysclk: id %d (%s), src %d (%s), freq %d (in)",
+			clk_id, idtab[clk_id], source, srctab[source], freq);
+
+		if (source == NAU8360_CLK_SRC_MCLK || source == NAU8360_CLK_SRC_BCLK)
+			regmap_update_bits(nau8360->regmap, NAU8360_R72_PLL_CFG0,
+				NAU8360_PD_PLL_MASK, NAU8360_PD_PLL_DIS);
+
+		ret = nau8360_ana_sys_clk(nau8360, source, freq);
+		if (ret)
+			goto err;
+		break;
+
+	default:
+		ret = -EINVAL;
+		goto err;
+	}
+
+	return 0;
+err:
+	return ret;
+}
+
+static int nau8360_calc_pll(struct nau8360 *nau8360)
+{
+	struct nau8360_pll *pll = &nau8360->pll;
+	u64 fref = 0ULL, fvco = 0ULL, ratio;
+	int ret, fr, fv;
+
+	if (pll->src != NAU8360_PLL_INTERNAL &&
+		(pll->input > PLL_FREQ_MAX || pll->input < PLL_FREQ_MIN)) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	if (pll->output > PLL_FOUT_MAX || pll->output < PLL_FOUT_MIN) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	for (pll->msel = 1; pll->msel <= MSEL_MAX; pll->msel++) {
+		fr = pll->input / pll->msel;
+		if (fr <= PLL_FREF_MAX) {
+			fref = fr;
+			break;
+		}
+	}
+	if (!fref) {
+		ret = -ERANGE;
+		goto err;
+	}
+
+	for (pll->rsel = 1; pll->rsel <= RSEL_MAX; pll->rsel++) {
+		fv = pll->output * pll->rsel;
+		if (fv >= PLL_FVCO_MIN) {
+			fvco = fv;
+			break;
+		}
+	}
+	if (!fvco) {
+		ret = -ERANGE;
+		goto err;
+	}
+
+	dev_dbg(nau8360->dev, "fref(1-8MHz): %lld, fvco(50-125MHz): %lld", fref, fvco);
+
+	/* Calculate the PLL 8-bit integer input and 12-bit fractional */
+	ratio = div_u64(fvco << 12, fref);
+	pll->nsel = (ratio >> 12) & 0xff;
+	pll->xsel = ratio & 0xfff;
+
+	return 0;
+err:
+	return ret;
+}
+
+static int nau8360_set_pll(struct snd_soc_component *cp, int pll_id, int source,
+	unsigned int freq_in, unsigned int freq_out)
+{
+	struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+	struct nau8360_pll *pll = &nau8360->pll;
+	int ctrl_val, ret;
+
+	switch (source) {
+	case NAU8360_PLL_MCLK:
+		ctrl_val = NAU8360_PLL_CLK_SEL_MCLK;
+		break;
+	case NAU8360_PLL_BCLK:
+		ctrl_val = NAU8360_PLL_CLK_SEL_BCLK;
+		break;
+	case NAU8360_PLL_INTERNAL:
+		ctrl_val = NAU8360_PLL_CLK_SEL_HIRC;
+		break;
+	default:
+		return -EINVAL;
+	}
+	pll->src = source;
+	pll->input = freq_in;
+	pll->output = freq_out;
+	ret = nau8360_calc_pll(nau8360);
+	if (ret) {
+		dev_err(nau8360->dev, "clock error input %d output %d", freq_in, freq_out);
+		return ret;
+	}
+	dev_dbg(nau8360->dev, "src:%d, input:%d, output:%d, M:%d, R:%d, N:%d, X:%d", pll->src,
+		pll->input, pll->output, pll->msel, pll->rsel, pll->nsel, pll->xsel);
+
+	regmap_update_bits(nau8360->regmap, NAU8360_R72_PLL_CFG0, NAU8360_PD_PLL_MASK |
+		NAU8360_PLL_CLK_SEL_MASK, NAU8360_PD_PLL_EN | ctrl_val);
+	regmap_write_bits(nau8360->regmap, NAU8360_R73_PLL_CFG1,
+		NAU8360_RSEL_MASK | NAU8360_MSEL_MASK | NAU8360_NSEL_MASK,
+		(pll->rsel - 1) << NAU8360_RSEL_SFT |
+		(pll->msel - 1) << NAU8360_MSEL_SFT | (pll->nsel - 1));
+	regmap_write_bits(nau8360->regmap, NAU8360_R74_PLL_CFG2, NAU8360_XSEL_MASK,
+		pll->xsel);
+
+	return 0;
+}
+
+static void nau8360_coeff_set_def(struct regmap *regmap)
+{
+	int i;
+
+	regmap_update_bits(regmap, NAU8360_R9D_PEQ_CTL, NAU8360_HW1_MEM_TEST,
+		NAU8360_HW1_MEM_TEST);
+	for (i = 0; i < NAU8360_TOT_BAND_PER_CH; i++) {
+		regmap_write(regmap, NAU8360_R100_LEFT_BIQ0_COE + 1 +
+			i * NAU8360_TOT_BAND_COE_RANGE, 0x20);
+		regmap_write(regmap, NAU8360_R200_RIGHT_BIQ0_COE + 1 +
+			i * NAU8360_TOT_BAND_COE_RANGE, 0x20);
+	}
+	regmap_update_bits(regmap, NAU8360_R9D_PEQ_CTL, NAU8360_HW1_MEM_TEST, 0);
+}
+
+static inline int nau8360_vbat_level(struct regmap *regmap)
+{
+	int value;
+
+	regmap_read(regmap, NAU8360_R21_VBAT_READOUT, &value);
+	/* multiple 100 on value scale */
+	return (value * 100 + NAU8360_VBAT_BASE) / NAU8360_VBAT_STEP;
+}
+
+static inline int nau8360_sawtooth_params(int vbat, int *vsaw_level, int *vsaw_slope)
+{
+	if (!vsaw_level || !vsaw_slope)
+		return -EINVAL;
+
+	if (vbat > NAU8360_VBAT_MAX || vbat < NAU8360_VBAT_MIN)
+		return -ERANGE;
+
+	if (vbat < NAU8360_VBAT_MID_THRES) {
+		*vsaw_level = 0x1;
+		*vsaw_slope = 0x0;
+	} else if (vbat < NAU8360_VBAT_HIGH_THRES) {
+		*vsaw_level = 0x2;
+		*vsaw_slope = 0x1;
+	} else {
+		*vsaw_level = 0x3;
+		*vsaw_slope = 0x2;
+	}
+
+	return 0;
+}
+
+static int nau8360_codec_probe(struct snd_soc_component *component)
+{
+	struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+	struct nau8360 *nau8360 = snd_soc_component_get_drvdata(component);
+	struct regmap *regmap = nau8360->regmap;
+	struct device *dev = nau8360->dev;
+	int ret, vbat, vsaw_level, vsaw_slope, value = 8;
+
+	nau8360->dapm = dapm;
+
+	nau8360_coeff_set_def(regmap);
+	regmap_update_bits(regmap, NAU8360_R90_HW2_CTL0, NAU8360_HW2_STALL, 0);
+	nau8360_dsp_software_reset(component);
+	if (nau8360->dsp_enable) {
+		value = nau8360->anc_enable ? 0xf : 0xc;
+		nau8360_dsp_enable(regmap, true);
+		ret = nau8360_dsp_init(component);
+		if (ret) {
+			nau8360_dsp_enable(regmap, false);
+			dev_err(dev, "can't enable DSP (%d)", ret);
+			goto err;
+		}
+	}
+	regmap_update_bits(regmap, NAU8360_R9D_PEQ_CTL, NAU8360_PEQ_BAND_MASK,
+		value << NAU8360_PEQ_BAND_SFT);
+
+	/* defalut disable Sense signal after booting */
+	snd_soc_dapm_disable_pin(nau8360->dapm, "Sense");
+	snd_soc_dapm_sync(nau8360->dapm);
+
+	/* VBAT is assigned by system or sensed by chip. */
+	if (nau8360->vbat_microvolt)
+		vbat = (nau8360->vbat_microvolt / MICRO);
+	else
+		vbat = nau8360_vbat_level(regmap);
+	dev_dbg(dev, "VBAT %dV for nau8360", vbat);
+
+	/* Config sawtooth clock according to VBAT. Class D modulator input short setting
+	 * for mute and de-pop purpose. Restore normal after initiation.
+	 */
+	ret = nau8360_sawtooth_params(vbat, &vsaw_level, &vsaw_slope);
+	if (ret) {
+		dev_err(dev, "can't get sawtooth clock parameters (%d)", ret);
+		goto err;
+	}
+	regmap_update_bits(regmap, NAU8360_RA5_ANA_REG_1, NAU8360_VSAW_LV_MASK |
+		NAU8360_KVCO_SAW_MASK, (vsaw_level << NAU8360_VSAW_LV_SFT) |
+		(vsaw_slope << NAU8360_KVCO_SAW_SFT));
+
+	return 0;
+err:
+	return ret;
+}
+
+static int __maybe_unused nau8360_suspend(struct snd_soc_component *component)
+{
+	struct nau8360 *nau8360 = snd_soc_component_get_drvdata(component);
+
+	regmap_update_bits(nau8360->regmap, NAU8360_R90_HW2_CTL0, NAU8360_HW2_STALL,
+		NAU8360_HW2_STALL);
+	if (nau8360->dsp_enable)
+		nau8360_dsp_enable(nau8360->regmap, false);
+
+	regcache_cache_only(nau8360->regmap, true);
+	regcache_mark_dirty(nau8360->regmap);
+
+	return 0;
+}
+
+static int __maybe_unused nau8360_resume(struct snd_soc_component *component)
+{
+	struct nau8360 *nau8360 = snd_soc_component_get_drvdata(component);
+	struct regmap *regmap = nau8360->regmap;
+	int ret;
+
+	regcache_cache_only(regmap, false);
+	regcache_sync(regmap);
+	/* disable Sense at standby */
+	snd_soc_dapm_disable_pin(nau8360->dapm, "Sense");
+	snd_soc_dapm_sync(nau8360->dapm);
+
+	regmap_update_bits(regmap, NAU8360_R90_HW2_CTL0, NAU8360_HW2_STALL, 0);
+	nau8360_dsp_software_reset(component);
+	if (nau8360->dsp_enable) {
+		nau8360_dsp_enable(regmap, true);
+		/* loading DSP firmware */
+		ret = nau8360_dsp_reinit(component);
+		if (ret) {
+			nau8360_dsp_enable(regmap, false);
+			dev_err(nau8360->dev, "can't enable DSP (%d)", ret);
+			goto err;
+		}
+	}
+
+	return 0;
+err:
+	return ret;
+}
+
+static const struct snd_soc_component_driver soc_comp_dev_nau8360 = {
+	.probe			= nau8360_codec_probe,
+	.set_sysclk		= nau8360_set_sysclk,
+	.set_pll		= nau8360_set_pll,
+	.suspend		= nau8360_suspend,
+	.resume			= nau8360_resume,
+	.controls		= nau8360_snd_controls,
+	.num_controls		= ARRAY_SIZE(nau8360_snd_controls),
+	.dapm_widgets		= nau8360_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(nau8360_dapm_widgets),
+	.dapm_routes		= nau8360_dapm_routes,
+	.num_dapm_routes	= ARRAY_SIZE(nau8360_dapm_routes),
+	.suspend_bias_off	= 1,
+	.idle_bias_on		= 1,
+	.use_pmdown_time	= 1,
+	.endianness		= 1,
+};
+
+static const struct snd_soc_dai_ops nau8360_dai_ops = {
+	.startup	= nau8360_startup,
+	.shutdown	= nau8360_shutdown,
+	.hw_params	= nau8360_hw_params,
+	.set_fmt	= nau8360_set_fmt,
+	.set_tdm_slot	= nau8360_set_tdm_slot,
+};
+
+#define NAU8360_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | \
+	SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
+	SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
+
+#define NAU8360_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+	SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver nau8360_dai = {
+	.name = NAU8360_CODEC_DAI,
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 4,
+		.rates = NAU8360_RATES,
+		.formats = NAU8360_FORMATS,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 8,
+		.rates = NAU8360_RATES,
+		.formats = NAU8360_FORMATS,
+	},
+	.ops = &nau8360_dai_ops,
+};
+
+static int nau8360_reg_write(void *context, unsigned int reg, unsigned int value)
+{
+	struct i2c_client *client = context;
+	int ret, count = 0;
+	u8 buf[6];
+
+	buf[count++] = reg >> 8;
+	buf[count++] = reg & 0xff;
+	if (reg != NAU8360_RF000_DSP_COMM && reg != NAU8360_RF002_DSP_COMM) {
+		/* format for Codec, 2 bytes value and endian big */
+		buf[count++] = value >> 8;
+		buf[count++] = value & 0xff;
+		dev_dbg(&client->dev, " %x <= %x", reg, value);
+	} else {
+		/* format for DSP, 4 bytes value and native */
+		buf[count++] = value & 0xff;
+		buf[count++] = (value >> 8) & 0xff;
+		buf[count++] = (value >> 16) & 0xff;
+		buf[count++] = (value >> 24) & 0xff;
+	}
+
+	ret = i2c_master_send(client, buf, count);
+	if (ret == count)
+		return 0;
+	else if (ret < 0)
+		return ret;
+	else
+		return -EIO;
+}
+
+static int nau8360_reg_read(void *context, unsigned int reg, unsigned int *value)
+{
+	struct i2c_client *client = context;
+	struct i2c_msg xfer[2];
+	u8 buf[4];
+	u16 reg_buf;
+	int ret;
+
+	reg_buf = cpu_to_be16(reg);
+	xfer[0].addr = client->addr;
+	xfer[0].len = sizeof(reg_buf);
+	xfer[0].buf = (u8 *)&reg_buf;
+	xfer[0].flags = 0;
+
+	xfer[1].addr = client->addr;
+	if (reg != NAU8360_RF000_DSP_COMM && reg != NAU8360_RF002_DSP_COMM)
+		xfer[1].len = 2;
+	else
+		xfer[1].len = 4;
+	xfer[1].buf = buf;
+	xfer[1].flags = I2C_M_RD;
+
+	ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer));
+	if (ret < 0)
+		return ret;
+	else if (ret != ARRAY_SIZE(xfer))
+		return -EIO;
+
+	if (reg != NAU8360_RF000_DSP_COMM && reg != NAU8360_RF002_DSP_COMM)
+		/* parse for Codec, 2 bytes value and endian big */
+		*value = (buf[0] << 8) | buf[1];
+	else
+		/* parse for DSP, 4 bytes value and native */
+		*value = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+
+	return 0;
+}
+
+static const struct regmap_config nau8360_regmap_config = {
+	.reg_bits = NAU8360_REG_ADDR_LEN,
+	.val_bits = NAU8360_REG_DATA_LEN,
+
+	.max_register = NAU8360_REG_MAX,
+	.readable_reg = nau8360_readable_reg,
+	.writeable_reg = nau8360_writeable_reg,
+	.volatile_reg = nau8360_volatile_reg,
+	.reg_read = nau8360_reg_read,
+	.reg_write = nau8360_reg_write,
+
+	.cache_type = REGCACHE_RBTREE,
+	.reg_defaults = nau8360_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(nau8360_reg_defaults),
+};
+
+static inline void nau8360_reset_chip(struct regmap *regmap)
+{
+	regmap_write(regmap, NAU8360_R00_SOFTWARE_RST, 0x5a5a);
+	regmap_write(regmap, NAU8360_R00_SOFTWARE_RST, 0xa5a5);
+}
+
+static inline void nau8360_dsp_software_reset(struct snd_soc_component *component)
+{
+	/* Enable PLL for successful DSP reset. After DSP is alive,
+	 * system clock switches to internal clock and disable PLL.
+	 */
+	snd_soc_component_update_bits(component, NAU8360_R72_PLL_CFG0,
+		NAU8360_PD_PLL_MASK, NAU8360_PD_PLL_EN);
+	msleep(50);
+	snd_soc_component_write(component, NAU8360_R01_DSP_SOFTWARE_RST, 0x5a5a);
+	snd_soc_component_write(component, NAU8360_R01_DSP_SOFTWARE_RST, 0xa5a5);
+	snd_soc_component_set_sysclk(component, NAU8360_CLK_ID_INT, 0, 0,
+		SND_SOC_CLOCK_IN);
+}
+
+static inline void nau8360_dsp_enable(struct regmap *regmap, bool enable)
+{
+	regmap_update_bits(regmap, NAU8360_R86_HW3_CTL0, NAU8360_HW3_STALL,
+		enable ? 0 : NAU8360_HW3_STALL);
+	regmap_update_bits(regmap, NAU8360_R1A_DSP_CORE_CTRL2, NAU8360_DSP_RUNSTALL,
+		enable ? 0 : NAU8360_DSP_RUNSTALL);
+}
+
+static void nau8360_init_regs(struct nau8360 *nau8360)
+{
+	struct regmap *regmap = nau8360->regmap;
+
+	/* Enable Digital LDO */
+	regmap_write(regmap, NAU8360_R78_PD_SW_DLDO, NAU8360_PD_SW_DLDO_EN);
+	/* Enable Software Shutdown Mode */
+	regmap_write(regmap, NAU8360_R77_SOFT_SD, NAU8360_SOFT_SD_EN);
+	/* Stall HW1 PEQ Engine and Clear DRAM to Zero */
+	regmap_update_bits(regmap, NAU8360_R9D_PEQ_CTL, NAU8360_PEQ_STALL |
+		NAU8360_HW1_MEM_CLEAR, NAU8360_PEQ_STALL | NAU8360_HW1_MEM_CLEAR);
+	regmap_update_bits(regmap, NAU8360_R9D_PEQ_CTL, NAU8360_HW1_MEM_CLEAR, 0);
+	/* Stall HW2 engine */
+	regmap_update_bits(regmap, NAU8360_R90_HW2_CTL0, NAU8360_HW2_STALL,
+		NAU8360_HW2_STALL);
+	/* Stall HW3 engine and DSP processor */
+	nau8360_dsp_enable(regmap, false);
+	/* Enable Clock Gate */
+	regmap_write(regmap, NAU8360_R7E_CLK_GATED_EN, NAU8360_CLK_GATED_EN);
+	/* Latch I2C LSB Value */
+	regmap_write(regmap, NAU8360_R02_I2C_ADDR, 0x0001);
+	/* ANC Left/Right Channel as Slot 2/3 and switch */
+	regmap_update_bits(regmap, NAU8360_R10_I2S_DATA_CTRL3,
+		NAU8360_RX_ANC_R_MASK | NAU8360_RX_ANC_L_MASK,
+		0x3 << NAU8360_RX_ANC_R_SFT | 0x2 << NAU8360_RX_ANC_L_SFT);
+	regmap_update_bits(regmap, NAU8360_R96_HW2_CTL6, NAU8360_HW1_ANC_EN,
+		nau8360->anc_enable);
+	/* Set DAC Clock Divider as 2 and Chopper Divider as 16 */
+	regmap_update_bits(regmap, NAU8360_R71_CLK_DIV_CFG,
+		NAU8360_DAC_CLK_DIV_MASK | NAU8360_DAC_CHOP_CLK_DIV_MASK,
+		NAU8360_DAC_CLK_DIV_2 | NAU8360_DAC_CHOP_CLK_DIV_16);
+	/* Set DAC SINC OSR as 128 and IVSENSE BS OSR as 32 */
+	regmap_update_bits(regmap, NAU8360_R5D_SINC_CFG,
+		NAU8360_DAC_SINC_OSR_MASK | NAU8360_IVSENSE_BS_OSR_MASK,
+		NAU8360_DAC_SINC_OSR_128 | NAU8360_IVSENSE_BS_OSR_32);
+
+	/* DAC gain setting 0dB by changing current cell current. */
+	regmap_update_bits(regmap, NAU8360_R6E_DAC_CFG0, NAU8360_DAC_CUR_MASK,
+		NAU8360_DAC_CUR_0DB);
+
+	/* Set Trim Bit Control of DLDO and GVDD to The Highest Voltage */
+	regmap_write(regmap, NAU8360_R5F_ANA_TRIM_CFG1, 0xf407);
+
+	/* Disable Data Det and Clock Detection Settings*/
+	regmap_update_bits(regmap, NAU8360_R40_CLK_DET_CTRL, NAU8360_APWRUPEN |
+		NAU8360_CLKPWRUPEN | NAU8360_FS_MCLK_DET | NAU8360_FS_BCLK_DET, 0);
+
+	/* Set HW3 Droop and Filter Sample as 192K */
+	regmap_update_bits(regmap, NAU8360_R8C_HW3_CTL6, NAU8360_HW3_DROOP_MASK |
+		NAU8360_HW3_FS_MASK, NAU8360_HW3_DROOP_192K | NAU8360_HW3_FS_192K);
+	/* Set HW1 Mute, Mute Interval as 699ms and HW1 Zero THD as 0xff */
+	regmap_update_bits(regmap, NAU8360_R9C_HW1_CTL2, NAU8360_MUTE_INTRVL_MASK |
+		NAU8360_HW1_CH_MUTE | NAU8360_HW1_ZERO_THD_MASK,
+		NAU8360_MUTE_INTRVL_699MS | NAU8360_HW1_CH_MUTE | 0xff);
+	/* set HW2 droop with 192K and normal/low latency */
+	regmap_update_bits(regmap, NAU8360_R96_HW2_CTL6, NAU8360_HW2_DROOP_SEL_MASK |
+		NAU8360_HW2_DROOP_EN | NAU8360_HW2_LATENCY_MASK | NAU8360_HW2_FS_MASK,
+		NAU8360_HW2_DROOP_SEL_LARGE | NAU8360_HW2_DROOP_EN |
+		(nau8360->low_latency ? NAU8360_HW2_LATENCY_LOW :
+			NAU8360_HW2_LATENCY_NOR) | NAU8360_HW2_FS_192K);
+	/* Set HW2 default volume */
+	regmap_write(regmap, NAU8360_R97_HW2_CTL7, 0xbf66);
+	regmap_write(regmap, NAU8360_R98_HW2_CTL8, 0xbf66);
+	/* Set HW2 Mute and Threshold of Zero Crossing */
+	regmap_update_bits(regmap, NAU8360_R99_HW2_CTL9, NAU8360_HW2_CH_MUTE |
+		NAU8360_HW2_ZERO_THD_MASK, NAU8360_HW2_CH_MUTE | 0xff);
+	/* According to DSP enabled or not, set stream path as
+	 * HW1->DSP->HW2->SINC->HW3 or HW1->HW2->SINC->HW3
+	 */
+	regmap_update_bits(regmap, NAU8360_R12_PATH_CTRL, NAU8360_SEL_HW1_MASK |
+		NAU8360_AUD_SEL_MASK | NAU8360_SEL_HW2_MASK | NAU8360_SEL_HW3_MASK |
+		NAU8360_DAC_SEL_MASK, NAU8360_SEL_HW1_OUT | NAU8360_AUD_SEL_SINCOUT |
+		NAU8360_SEL_HW2_OUT | NAU8360_SEL_HW3_OUT | (nau8360->dsp_enable ?
+			NAU8360_DAC_SEL_DSP : NAU8360_DAC_SEL_BYP));
+	/* Set Input Status as Low and Input/Output Mode Disable for All GPIO */
+	regmap_write(regmap, NAU8360_R07_GP_CTRL, 0x0000);
+	/* Set GPIO1 and GPIO2 MUX as Reserved */
+	regmap_write(regmap, NAU8360_R08_GP_CTRL0, 0x8181);
+	/* set GPIO3 MUX as Clock output */
+	regmap_write(regmap, NAU8360_R09_GP_CTRL1, 0x1e9e);
+	/* enable SARADC with extra average filter of VBAT */
+	regmap_update_bits(regmap, NAU8360_R6A_SARADC_CFG0, NAU8360_SARADC_EN |
+		NAU8360_VBAT_AVG_EN, NAU8360_SARADC_EN | NAU8360_VBAT_AVG_EN);
+	/* Enable Temperature Sensor, VBATDIV and VRF.
+	 * Set Average Filter Data as 512 for VTEMP and VBAT.
+	 */
+	regmap_update_bits(regmap, NAU8360_R6B_SARADC_CFG1, NAU8360_VTEMP_EN |
+		NAU8360_VBATDIV_EN | NAU8360_VREF_EN | NAU8360_PRELOAD_VREF_EN |
+		NAU8360_VTEMP_AVG_N_MASK | NAU8360_VBAT_AVG_N_MASK, NAU8360_VTEMP_EN |
+		NAU8360_VBATDIV_EN | NAU8360_VREF_EN | NAU8360_PRELOAD_VREF_EN |
+		NAU8360_VTEMP_AVG_N_512 | NAU8360_VBAT_AVG_N_512);
+	/* Enable IV sense internal LDO */
+	regmap_update_bits(regmap, NAU8360_R6C_IVSNS_CFG0,
+		NAU8360_PD_LDO_SDM_IVSNS_PMD, 0);
+	/* Enable Bias Current and Reference Voltage for IVSENSE. Set VSNS Gain
+	 * as 1/12 and ISNS Gain as x16(24dB). Set PGA Current as 2'b11 and SDN
+	 * Delay as 2'b00.
+	 */
+	regmap_write(regmap, NAU8360_R6D_IVSNS_CFG1, 0x0f5c);
+	/* sawtooth clock from ivsense clock, PWM frequency 432 Khz */
+	regmap_update_bits(regmap, NAU8360_RA4_ANA_REG_0, NAU8360_SEL_STCLK_MASK |
+		NAU8360_NSEL_SAW_MASK, NAU8360_SEL_STCLK_IVCLK | 0x8);
+	/* sawtooth PLL APR */
+	regmap_update_bits(regmap, NAU8360_RA5_ANA_REG_1, NAU8360_SAW_PLL_MASK,
+		NAU8360_SAW_PLL_NOR);
+
+	/* Set DAC gain (0dB/+3.2dB) by current cell adjustment */
+	regmap_update_bits(regmap, NAU8360_R6E_DAC_CFG0, NAU8360_DAC_CUR_MASK,
+		nau8360->dac_cur_enable ? NAU8360_DAC_CUR_3_2DB : NAU8360_DAC_CUR_0DB);
+	/* Config trim short current reference. Enable Non-overlap longer delay.
+	 * Set segment driver as half driving strength.
+	 */
+	regmap_update_bits(regmap, NAU8360_R68_ANALOG_CONTROL_1, NAU8360_DTX_EN |
+		NAU8360_TRIMSCR_MASK | NAU8360_DRVCTL_SEGL_FULL |
+		NAU8360_DRVCTL_SEGR_FULL, NAU8360_TRIMSCR_MLOW | NAU8360_DTX_EN);
+	/* Enable SCP Clear Mode and Class D Modulator to Common Mode */
+	regmap_update_bits(regmap, NAU8360_R67_ANALOG_CONTROL_0,
+		NAU8360_SCP_CLEAR_MODE_MASK | NAU8360_CM_COMP_EN,
+		NAU8360_SCP_CLEAR_MODE_AUTO | NAU8360_CM_COMP_EN);
+	/* Set Analog Mute and Class D Modulator Gain as 14dB */
+	regmap_update_bits(regmap, NAU8360_R67_ANALOG_CONTROL_0, NAU8360_ANA_MUTE |
+		NAU8360_MOD_GAIN_MASK, NAU8360_ANA_MUTE | NAU8360_MOD_GAIN_14DB);
+	/* Set Stereo or PBTL Mode */
+	regmap_update_bits(regmap, NAU8360_R67_ANALOG_CONTROL_0,
+		NAU8360_V_PBTL_EN, nau8360->pbtl_enable ? NAU8360_V_PBTL_EN : 0);
+	regmap_update_bits(regmap, NAU8360_R6C_IVSNS_CFG0,
+		NAU8360_PBTL_ISENE_LR_MASK, NAU8360_PBTL_ISENE_LR_ILR);
+	/* adjust HW3 default volume of voltage/current sense */
+	regmap_write(regmap, NAU8360_R8A_HW3_VL_CTL7, 0xbe90);
+	regmap_write(regmap, NAU8360_R8B_HW3_VR_CTL8, 0xbe90);
+	regmap_write(regmap, NAU8360_R8D_HW3_IL_CTL7, nau8360->pbtl_enable ? 0xc3aa : 0xc24c);
+	regmap_write(regmap, NAU8360_R8E_HW3_IR_CTL8, nau8360->pbtl_enable ? 0xc3aa : 0xc24c);
+	/* Set HW3 Zero THD as 0xff */
+	regmap_update_bits(regmap, NAU8360_R8F_HW3_CTL9,
+		NAU8360_HW3_ZERO_THD_MASK, 0xff);
+	/* Enable TX SDOUT and Data at BCLK Rising. */
+	regmap_update_bits(regmap, NAU8360_R0D_I2S_PCM_CTRL3, NAU8360_TX_FILL_MASK,
+		NAU8360_TX_FILL_ZERO);
+	/* Switch AEC. The configuration can change in TDM slot setting later. */
+	if (nau8360->aec_enable)
+		regmap_update_bits(regmap, NAU8360_R17_I2S0_DATA_CTRL5,
+			NAU8360_AEC_L_EN | NAU8360_AEC_R_EN,
+			NAU8360_AEC_L_EN | NAU8360_AEC_R_EN);
+	/* TDM channel length configuration and  TX data length as 16 bit */
+	regmap_update_bits(regmap, NAU8360_R0C_I2S_PCM_CTRL2, NAU8360_TDM_CLEN_MASK,
+		((nau8360->tdm_chan_len - 16) >> 3) << NAU8360_TDM_CLEN_SFT);
+	regmap_update_bits(regmap, NAU8360_R0D_I2S_PCM_CTRL3,
+		NAU8360_VSNS_L_SLEN_MASK, NAU8360_VSNS_L_SLEN_16);
+	regmap_update_bits(regmap, NAU8360_R0E_I2S_DATA_CTRL1,
+		NAU8360_ISNS_L_SLEN_MASK, NAU8360_ISNS_L_SLEN_16);
+	regmap_update_bits(regmap, NAU8360_R11_I2S_DATA_CTRL4,
+		NAU8360_VSNS_R_SLEN_MASK | NAU8360_ISNS_R_SLEN_MASK,
+		NAU8360_VSNS_R_SLEN_16 | NAU8360_ISNS_R_SLEN_16);
+	/* set DAC channel temperature trim code slope as 0x7D */
+	regmap_update_bits(regmap, NAU8360_R7A_DAC_TRIM_CFG2,
+		NAU8360_DAC_TEMP_SLOPE_MASK, 0x7D << NAU8360_DAC_TEMP_SLOPE_SFT);
+	/* set ISNS temperature trim code slope as 0x1a */
+	regmap_update_bits(regmap, NAU8360_R7B_IVSNS_TRIM_CFG,
+		NAU8360_ISNS_TEMP_SLOPE_MASK, 0x1a << NAU8360_ISNS_TEMP_SLOPE_SFT);
+	/* set misc trim config as 0x67F0 */
+	regmap_update_bits(regmap, NAU8360_R7C_MISC_TRIM_CFG, NAU8360_DAC_GAIN_SB_MASK |
+		NAU8360_DAC_TEMP_SB_MASK | NAU8360_ISNS_TEMP_SB_MASK |
+		NAU8360_VSNS_TEMP_SB_MASK | NAU8360_ISNS_GAIN_SB_MASK |
+		NAU8360_VSNS_GAIN_SB_MASK, (0x3 << NAU8360_DAC_TEMP_SB_SFT) |
+		(0x7 << NAU8360_ISNS_TEMP_SB_SFT) | (0x3 << NAU8360_VSNS_TEMP_SB_SFT));
+	/* Enable Efuse Initianllization */
+	regmap_update_bits(regmap, NAU8360_R60_RST, NAU8360_EFUSE_CTRL_EN,
+		NAU8360_EFUSE_CTRL_EN);
+	/* Clear HW2 DRAM */
+	regmap_update_bits(regmap, NAU8360_R90_HW2_CTL0,
+		NAU8360_HW2_DRAM_CLR, NAU8360_HW2_DRAM_CLR);
+	/* Finish HW DRAM Clear */
+	regmap_update_bits(regmap, NAU8360_R90_HW2_CTL0, NAU8360_HW2_DRAM_CLR, 0);
+	/* Clear HW3 DRAM */
+	regmap_update_bits(regmap, NAU8360_R86_HW3_CTL0,
+		NAU8360_HW3_DRAM_CLR, NAU8360_HW3_DRAM_CLR);
+	/* Finish HW DRAM Clear */
+	regmap_update_bits(regmap, NAU8360_R86_HW3_CTL0, NAU8360_HW3_DRAM_CLR, 0);
+}
+
+static void nau8360_print_device_properties(struct nau8360 *nau8360)
+{
+	int i;
+
+	dev_dbg(nau8360->dev, "dsp-bypass:          %d", !nau8360->dsp_enable);
+	dev_dbg(nau8360->dev, "low-latency:         %d", nau8360->low_latency);
+	dev_dbg(nau8360->dev, "anc-enable:          %d", nau8360->anc_enable);
+	dev_dbg(nau8360->dev, "aec-enable:          %d", nau8360->aec_enable);
+	dev_dbg(nau8360->dev, "pbtl-enable:         %d", nau8360->pbtl_enable);
+	dev_dbg(nau8360->dev, "dac-cur-enable:      %d", nau8360->dac_cur_enable);
+	dev_dbg(nau8360->dev, "vbat-microvolt:      %d", nau8360->vbat_microvolt);
+	dev_dbg(nau8360->dev, "tdm-channel-length:  %d", nau8360->tdm_chan_len);
+	for (i = 0; i < nau8360->dsp_fws_num; i++)
+		dev_dbg(nau8360->dev, "dsp-fw-names[%d]:     %s", i,
+			nau8360->dsp_firmware[i]);
+}
+
+static void nau8360_read_device_properties(struct nau8360 *nau8360)
+{
+	const struct device_node *np = nau8360->dev->of_node;
+	struct device *dev = nau8360->dev;
+	int i, ret;
+
+	nau8360->dsp_enable = !device_property_read_bool(dev, "nuvoton,dsp-bypass");
+	nau8360->low_latency = device_property_read_bool(dev, "nuvoton,low-latency");
+	nau8360->anc_enable = device_property_read_bool(dev, "nuvoton,anc-enable");
+	nau8360->aec_enable = device_property_read_bool(dev, "nuvoton,aec-enable");
+	nau8360->pbtl_enable = device_property_read_bool(dev, "nuvoton,pbtl-enable");
+	nau8360->dac_cur_enable = device_property_read_bool(dev, "nuvoton,dac-cur-enable");
+	ret = device_property_read_u32(dev, "nuvoton,vbat-microvolt",
+			&nau8360->vbat_microvolt);
+	if (ret)
+		nau8360->vbat_microvolt = 0;
+	ret = device_property_read_u32(dev, "nuvoton,tdm-channel-length",
+			&nau8360->tdm_chan_len);
+	if (ret || (nau8360->tdm_chan_len != 16 && nau8360->tdm_chan_len != 24 &&
+			nau8360->tdm_chan_len != 32)) {
+		dev_err(dev, "Invalid TDM channel length. Assign 32 bits.");
+		nau8360->tdm_chan_len = 32;
+	}
+	ret = of_property_count_strings(np, "nuvoton,dsp-fw-names");
+	if (ret == NAU8360_DSP_FW_NUM) {
+		nau8360->dsp_fws_num = ret;
+		for (i = 0; i < nau8360->dsp_fws_num; i++) {
+			ret = of_property_read_string_index(np, "nuvoton,dsp-fw-names",
+					i, &nau8360->dsp_firmware[i]);
+			if (ret) {
+				dev_err(dev, "Invalid dsp-fw-names[%d]", i);
+				nau8360->dsp_fws_num = 0;
+				break;
+			}
+		}
+	}
+}
+
+static struct reg_default *nau8360_alloc_defaults(struct device *dev, int *total_regs)
+{
+	struct reg_default *dyn_defaults;
+	int reg_num = ARRAY_SIZE(nau8360_reg_defaults);
+	int total = reg_num + (2 * NAU8360_TOT_BAND_PER_CH * NAU8360_TOT_BAND_COE);
+	int i, j, idx;
+
+	dyn_defaults = devm_kzalloc(dev, total * sizeof(*dyn_defaults), GFP_KERNEL);
+	if (!dyn_defaults)
+		return NULL;
+
+	memcpy(dyn_defaults, nau8360_reg_defaults, sizeof(*dyn_defaults) * reg_num);
+	idx = reg_num;
+
+	for (i = 0; i < NAU8360_TOT_BAND_PER_CH; i++) {
+		unsigned int l_base = NAU8360_R100_LEFT_BIQ0_COE +
+					(i * NAU8360_TOT_BAND_COE_RANGE);
+		unsigned int r_base = NAU8360_R200_RIGHT_BIQ0_COE +
+					(i * NAU8360_TOT_BAND_COE_RANGE);
+
+		for (j = 0; j < NAU8360_TOT_BAND_COE; j++) {
+			dyn_defaults[idx++].reg = l_base + j;
+			dyn_defaults[idx++].reg = r_base + j;
+		}
+	}
+
+	*total_regs = total;
+
+	return dyn_defaults;
+}
+
+static int nau8360_i2c_probe(struct i2c_client *i2c)
+{
+	struct device *dev = &i2c->dev;
+	struct nau8360 *nau8360 = dev_get_platdata(dev);
+	struct regmap_config regmap_cfg = nau8360_regmap_config;
+	struct reg_default *dyn_defaults;
+	int num_total_regs;
+	int ret, value;
+
+	if (!nau8360) {
+		nau8360 = devm_kzalloc(dev, sizeof(*nau8360), GFP_KERNEL);
+		if (!nau8360)
+			return -ENOMEM;
+	}
+	i2c_set_clientdata(i2c, nau8360);
+
+	dyn_defaults = nau8360_alloc_defaults(dev, &num_total_regs);
+	if (!dyn_defaults)
+		return -ENOMEM;
+
+	regmap_cfg.reg_defaults = dyn_defaults;
+	regmap_cfg.num_reg_defaults = num_total_regs;
+
+	nau8360->regmap = devm_regmap_init(dev, NULL, i2c, &regmap_cfg);
+	if (IS_ERR(nau8360->regmap))
+		return PTR_ERR(nau8360->regmap);
+	nau8360->dev = dev;
+
+	nau8360_reset_chip(nau8360->regmap);
+	ret = regmap_read(nau8360->regmap, NAU8360_R46_I2C_DEVICE_ID, &value);
+	if (ret) {
+		dev_err(dev, "Failed to read NAU83G60 device id %d",
+			ret);
+		return ret;
+	}
+
+	nau8360->dsp_created = false;
+	nau8360_read_device_properties(nau8360);
+	nau8360_print_device_properties(nau8360);
+	nau8360_init_regs(nau8360);
+
+	return snd_soc_register_component(dev, &soc_comp_dev_nau8360, &nau8360_dai, 1);
+}
+
+static const struct i2c_device_id nau8360_i2c_ids[] = {
+	{ "nau8360", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, nau8360_i2c_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8360_of_ids[] = {
+	{ .compatible = "nuvoton,nau8360", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, nau8360_of_ids);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id nau8360_acpi_match[] = {
+	{"NVTN2002", 0,},
+	{},
+};
+MODULE_DEVICE_TABLE(acpi, nau8360_acpi_match);
+#endif
+
+static struct i2c_driver nau8360_i2c_driver = {
+	.driver = {
+		.name = "nau8360",
+		.of_match_table = of_match_ptr(nau8360_of_ids),
+		.acpi_match_table = ACPI_PTR(nau8360_acpi_match),
+	},
+	.probe = nau8360_i2c_probe,
+	.id_table = nau8360_i2c_ids,
+};
+module_i2c_driver(nau8360_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC NAU83G60 Stereo Class-D Amplifier with DSP and I/V-sense driver");
+MODULE_AUTHOR("David Lin <ctlin0@nuvoton.com>");
+MODULE_AUTHOR("Seven Lee <wtli@nuvoton.com>");
+MODULE_AUTHOR("John Hsu <kchsu0@nuvoton.com>");
+MODULE_AUTHOR("Neo Chang <ylchang2@nuvoton.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/nau8360.h b/sound/soc/codecs/nau8360.h
new file mode 100644
index 000000000000..05fb4553c9e8
--- /dev/null
+++ b/sound/soc/codecs/nau8360.h
@@ -0,0 +1,900 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * The NAU83G60 Stereo Class-D Amplifier with DSP and I/V-sense driver.
+ *
+ * Copyright (C) 2026 Nuvoton Technology Corp.
+ * Author: David Lin <ctlin0@nuvoton.com>
+ *         Seven Lee <wtli@nuvoton.com>
+ *         John Hsu <kchsu0@nuvoton.com>
+ *         Neo Chang <ylchang2@nuvoton.com>
+ */
+
+#ifndef __NAU8360_H__
+#define __NAU8360_H__
+
+#define NAU8360_R00_SOFTWARE_RST		0x00
+#define NAU8360_R01_DSP_SOFTWARE_RST		0x01
+#define NAU8360_R02_I2C_ADDR			0x02
+#define NAU8360_R03_CLK_CTRL0			0x03
+#define NAU8360_R04_CLK_CTRL1			0x04
+#define NAU8360_R05_INTERRUPT_CTRL		0x05
+#define NAU8360_R06_INT_CLR_STATUS		0x06
+#define NAU8360_R07_GP_CTRL			0x07
+#define NAU8360_R08_GP_CTRL0			0x08
+#define NAU8360_R09_GP_CTRL1			0x09
+#define NAU8360_R0A_GP_CTRL2			0x0a
+#define NAU8360_R0B_I2S_PCM_CTRL1		0x0b
+#define NAU8360_R0C_I2S_PCM_CTRL2		0x0c
+#define NAU8360_R0D_I2S_PCM_CTRL3		0x0d
+#define NAU8360_R0E_I2S_DATA_CTRL1		0x0e
+#define NAU8360_R0F_I2S_DATA_CTRL2		0x0f
+#define NAU8360_R10_I2S_DATA_CTRL3		0x10
+#define NAU8360_R11_I2S_DATA_CTRL4		0x11
+#define NAU8360_R12_PATH_CTRL			0x12
+#define NAU8360_R16_NO_NAME			0x16
+#define NAU8360_R17_I2S0_DATA_CTRL5		0x17
+#define NAU8360_R1A_DSP_CORE_CTRL2		0x1a
+#define NAU8360_R21_VBAT_READOUT		0x21
+#define NAU8360_R22_TEMP_READOUT		0x22
+#define NAU8360_R2C_ALC_CTRL1			0x2c
+#define NAU8360_R2D_ALC_CTRL2			0x2d
+#define NAU8360_R2E_ALC_CTRL3			0x2e
+#define NAU8360_R31_UVLOP_CTRL1			0x31
+#define NAU8360_R32_UVLOP_CTRL2			0x32
+#define NAU8360_R33_UVLOP_CTRL3			0x33
+#define NAU8360_R40_CLK_DET_CTRL		0x40
+#define NAU8360_R41_CLK_CTL2			0x41
+#define NAU8360_R46_I2C_DEVICE_ID		0x46
+#define NAU8360_R5D_SINC_CFG			0x5d
+#define NAU8360_R5F_ANA_TRIM_CFG1		0x5f
+#define NAU8360_R60_RST				0x60
+#define NAU8360_R67_ANALOG_CONTROL_0		0x67
+#define NAU8360_R68_ANALOG_CONTROL_1		0x68
+#define NAU8360_R69_ANALOG_CONTROL_3		0x69
+#define NAU8360_R6A_SARADC_CFG0			0x6a
+#define NAU8360_R6B_SARADC_CFG1			0x6b
+#define NAU8360_R6C_IVSNS_CFG0			0x6c
+#define NAU8360_R6D_IVSNS_CFG1			0x6d
+#define NAU8360_R6E_DAC_CFG0			0x6e
+#define NAU8360_R71_CLK_DIV_CFG			0x71
+#define NAU8360_R72_PLL_CFG0			0x72
+#define NAU8360_R73_PLL_CFG1			0x73
+#define NAU8360_R74_PLL_CFG2			0x74
+#define NAU8360_R77_SOFT_SD			0x77
+#define NAU8360_R78_PD_SW_DLDO			0x78
+#define NAU8360_R79_EN_HIRC48M			0x79
+#define NAU8360_R7A_DAC_TRIM_CFG2		0x7a
+#define NAU8360_R7B_IVSNS_TRIM_CFG		0x7b
+#define NAU8360_R7C_MISC_TRIM_CFG		0x7c
+#define NAU8360_R7E_CLK_GATED_EN		0x7e
+#define NAU8360_R86_HW3_CTL0			0x86
+#define NAU8360_R88_ALC_CTRL6			0x88
+#define NAU8360_R8A_HW3_VL_CTL7			0x8a
+#define NAU8360_R8B_HW3_VR_CTL8			0x8b
+#define NAU8360_R8C_HW3_CTL6			0x8c
+#define NAU8360_R8D_HW3_IL_CTL7			0x8d
+#define NAU8360_R8E_HW3_IR_CTL8			0x8e
+#define NAU8360_R8F_HW3_CTL9			0x8f
+#define NAU8360_R90_HW2_CTL0			0x90
+#define NAU8360_R96_HW2_CTL6			0x96
+#define NAU8360_R97_HW2_CTL7			0x97
+#define NAU8360_R98_HW2_CTL8			0x98
+#define NAU8360_R99_HW2_CTL9			0x99
+#define NAU8360_R9A_HW1_CTL0			0x9a
+#define NAU8360_R9B_HW1_CTL1			0x9b
+#define NAU8360_R9C_HW1_CTL2			0x9c
+#define NAU8360_R9D_PEQ_CTL			0x9d
+#define NAU8360_RA0_LEFT_XODRC_CTRL		0xa0
+#define NAU8360_RA2_RIGHT_XODRC_CTRL		0xa2
+#define NAU8360_RA4_ANA_REG_0			0xa4
+#define NAU8360_RA5_ANA_REG_1			0xa5
+/* Left PEQ includes 15 band, 10 coeff per band, from start addr 0x100 */
+#define NAU8360_R100_LEFT_BIQ0_COE		0x100
+#define NAU8360_R10C_LEFT_BIQ1_COE		0x10c
+#define NAU8360_R118_LEFT_BIQ2_COE		0x118
+#define NAU8360_R124_LEFT_BIQ3_COE		0x124
+#define NAU8360_R130_LEFT_BIQ4_COE		0x130
+#define NAU8360_R13C_LEFT_BIQ5_COE		0x13c
+#define NAU8360_R148_LEFT_BIQ6_COE		0x148
+#define NAU8360_R154_LEFT_BIQ7_COE		0x154
+#define NAU8360_R160_LEFT_BIQ8_COE		0x160
+#define NAU8360_R16C_LEFT_BIQ9_COE		0x16c
+#define NAU8360_R178_LEFT_BIQ10_COE		0x178
+#define NAU8360_R184_LEFT_BIQ11_COE		0x184
+#define NAU8360_R190_LEFT_BIQ12_COE		0x190
+#define NAU8360_R19C_LEFT_BIQ13_COE		0x19c
+#define NAU8360_R1A8_LEFT_BIQ14_COE		0x1a8
+/* Right PEQ includes 15 band, 10 coeff per band, from start addr 0x200 */
+#define NAU8360_R200_RIGHT_BIQ0_COE		0x200
+#define NAU8360_R20C_RIGHT_BIQ1_COE		0x20c
+#define NAU8360_R218_RIGHT_BIQ2_COE		0x218
+#define NAU8360_R224_RIGHT_BIQ3_COE		0x224
+#define NAU8360_R230_RIGHT_BIQ4_COE		0x230
+#define NAU8360_R23C_RIGHT_BIQ5_COE		0x23c
+#define NAU8360_R248_RIGHT_BIQ6_COE		0x248
+#define NAU8360_R254_RIGHT_BIQ7_COE		0x254
+#define NAU8360_R260_RIGHT_BIQ8_COE		0x260
+#define NAU8360_R26C_RIGHT_BIQ9_COE		0x26c
+#define NAU8360_R278_RIGHT_BIQ10_COE		0x278
+#define NAU8360_R284_RIGHT_BIQ11_COE		0x284
+#define NAU8360_R290_RIGHT_BIQ12_COE		0x290
+#define NAU8360_R29C_RIGHT_BIQ13_COE		0x29c
+#define NAU8360_R2A8_RIGHT_BIQ14_COE		0x2a8
+#define NAU8360_RF000_DSP_COMM			0xf000
+#define NAU8360_RF002_DSP_COMM			0xf002
+#define NAU8360_REG_MAX				NAU8360_RF002_DSP_COMM
+
+/* 16-bit control register address, and 16-bits control register data */
+#define NAU8360_REG_ADDR_LEN			16
+#define NAU8360_REG_DATA_LEN			16
+
+/* NAU8360_R03_CLK_CTRL0 (0x03) */
+#define NAU8360_MCLK_DIV_SFT			10
+#define NAU8360_MCLK_DIV_MAX			0x3f
+#define NAU8360_MCLK_DIV_MASK			(0x3f << NAU8360_MCLK_DIV_SFT)
+#define NAU8360_DSP_CLK_DIV_SFT			7
+#define NAU8360_DSP_CLK_DIV_MAX			0x7
+#define NAU8360_DSP_CLK_DIV_MASK		(0x7 << NAU8360_DSP_CLK_DIV_SFT)
+#define NAU8360_DSP_CLK_SEL_SFT			5
+#define NAU8360_DSP_CLK_SEL_MASK		(0x3 << NAU8360_DSP_CLK_SEL_SFT)
+#define NAU8360_DSP_CLK_SEL_PLL			(0x2 << NAU8360_DSP_CLK_SEL_SFT)
+#define NAU8360_DSP_CLK_SEL_HIRC48M		(0x1 << NAU8360_DSP_CLK_SEL_SFT)
+#define NAU8360_DSP_CLK_SEL_MCLK		(0x0 << NAU8360_DSP_CLK_SEL_SFT)
+
+/* NAU8360_R04_CLK_CTRL1 (0x4) */
+#define NAU8360_MCLK_SEL_SFT			14
+#define NAU8360_MCLK_SEL_MASK			(0x3 << NAU8360_MCLK_SEL_SFT)
+#define NAU8360_MCLK_SEL_PLL			(0x2 << NAU8360_MCLK_SEL_SFT)
+#define NAU8360_MCLK_SEL_HIRC48M		(0x1 << NAU8360_MCLK_SEL_SFT)
+#define NAU8360_MCLK_SEL_MCLK			(0x0 << NAU8360_MCLK_SEL_SFT)
+#define NAU8360_HW_CLK_SEL_SFT			10
+#define NAU8360_HW_CLK_SEL_MASK			(0x3 << NAU8360_HW_CLK_SEL_SFT)
+#define NAU8360_HW_CLK_SEL_PLL			(0x2 << NAU8360_HW_CLK_SEL_SFT)
+#define NAU8360_HW_CLK_SEL_HIRC48M		(0x1 << NAU8360_HW_CLK_SEL_SFT)
+#define NAU8360_HW_CLK_SEL_MCLK			(0x0 << NAU8360_HW_CLK_SEL_SFT)
+#define NAU8360_HW_CLK_DIV_SFT			7
+#define NAU8360_HW_CLK_DIV_MAX			NAU8360_DSP_CLK_DIV_MAX
+#define NAU8360_HW_CLK_DIV_MASK			(0x7 << NAU8360_HW_CLK_DIV_SFT)
+
+/* NAU8360_R07_GP_CTRL (0x7) */
+#define NAU8360_GPIO1_IEOE_SFT			14
+#define NAU8360_GPIO1_IEOE_MASK			(0x3 << NAU8360_GPIO1_IEOE_SFT)
+#define NAU8360_GPIO2_IEOE_SFT			12
+#define NAU8360_GPIO2_IEOE_MASK			(0x3 << NAU8360_GPIO2_IEOE_SFT)
+#define NAU8360_GPIO3_IEOE_SFT			10
+#define NAU8360_GPIO3_IEOE_MASK			(0x3 << NAU8360_GPIO3_IEOE_SFT)
+#define NAU8360_GPIO4_IEOE_SFT			8
+#define NAU8360_GPIO4_IEOE_MASK			(0x3 << NAU8360_GPIO4_IEOE_SFT)
+#define NAU8360_GPIO1_IN_STAT_SFT		7
+#define NAU8360_GPIO1_IN_STAT_MASK		(0x1 << NAU8360_GPIO1_IN_STAT_SFT)
+#define NAU8360_GPIO2_IN_STAT_SFT		6
+#define NAU8360_GPIO2_IN_STAT_MASK		(0x1 << NAU8360_GPIO2_IN_STAT_SFT)
+#define NAU8360_GPIO3_IN_STAT_SFT		5
+#define NAU8360_GPIO3_IN_STAT_MASK		(0x1 << NAU8360_GPIO3_IN_STAT_SFT)
+#define NAU8360_GPIO4_IN_STAT_SFT		4
+#define NAU8360_GPIO4_IN_STAT_MASK		(0x1 << NAU8360_GPIO4_IN_STAT_SFT)
+
+/* NAU8360_R08_GP_CTRL0 (0x8) */
+#define NAU8360_GPIO2_MUX_SFT			14
+#define NAU8360_GPIO2_MUX_MASK			(0x3 << NAU8360_GPIO2_MUX_SFT)
+#define NAU8360_GPIO2_SR_SFT			13
+#define NAU8360_GPIO2_SR_MASK			(0x1 << NAU8360_GPIO2_SR_SFT)
+#define NAU8360_GPIO2_CDS_SFT			11
+#define NAU8360_GPIO2_CDS_MASK			(0x3 << NAU8360_GPIO2_CDS_SFT)
+#define NAU8360_GPIO2_PS_SFT			10
+#define NAU8360_GPIO2_PS_MASK			(0x1 << NAU8360_GPIO2_PS_SFT)
+#define NAU8360_GPIO2_PE_SFT			9
+#define NAU8360_GPIO2_PE_EN			(0x1 << NAU8360_GPIO2_PE_SFT)
+#define NAU8360_GPIO1_MUX_SFT			6
+#define NAU8360_GPIO1_MUX_MASK			(0x3 << NAU8360_GPIO1_MUX_SFT)
+#define NAU8360_GPIO1_SR_SFT			5
+#define NAU8360_GPIO1_SR_MASK			(0x1 << NAU8360_GPIO1_SR_SFT)
+#define NAU8360_GPIO1_CDS_SFT			3
+#define NAU8360_GPIO1_CDS_MASK			(0x3 << NAU8360_GPIO1_CDS_SFT)
+#define NAU8360_GPIO1_PS_SFT			2
+#define NAU8360_GPIO1_PS_MASK			(0x1 << NAU8360_GPIO1_PS_SFT)
+#define NAU8360_GPIO1_PE_SFT			1
+#define NAU8360_GPIO1_PE_EN			(0x1 << NAU8360_GPIO1_PE_SFT)
+
+/* NAU8360_R09_GP_CTRL1 (0x9) */
+#define NAU8360_GPIO4_MUX_SFT			14
+#define NAU8360_GPIO4_MUX_MASK			(0x3 << NAU8360_GPIO4_MUX_SFT)
+#define NAU8360_GPIO4_SR_SFT			13
+#define NAU8360_GPIO4_SR_MASK			(0x1 << NAU8360_GPIO4_SR_SFT)
+#define NAU8360_GPIO4_CDS_SFT			11
+#define NAU8360_GPIO4_CDS_MASK			(0x3 << NAU8360_GPIO4_CDS_SFT)
+#define NAU8360_GPIO4_PS_SFT			10
+#define NAU8360_GPIO4_PS_MASK			(0x1 << NAU8360_GPIO4_PS_SFT)
+#define NAU8360_GPIO4_PE_SFT			9
+#define NAU8360_GPIO4_PE_EN			(0x1 << NAU8360_GPIO4_PE_SFT)
+#define NAU8360_GPIO3_MUX_SFT			6
+#define NAU8360_GPIO3_MUX_MASK			(0x3 << NAU8360_GPIO3_MUX_SFT)
+#define NAU8360_GPIO3_SR_SFT			5
+#define NAU8360_GPIO3_SR_MASK			(0x1 << NAU8360_GPIO3_SR_SFT)
+#define NAU8360_GPIO3_CDS_SFT			3
+#define NAU8360_GPIO3_CDS_MASK			(0x3 << NAU8360_GPIO3_CDS_SFT)
+#define NAU8360_GPIO3_PS_SFT			2
+#define NAU8360_GPIO3_PS_MASK			(0x1 << NAU8360_GPIO3_PS_SFT)
+#define NAU8360_GPIO3_PE_SFT			1
+#define NAU8360_GPIO3_PE_EN			(0x1 << NAU8360_GPIO3_PE_SFT)
+
+/* NAU8360_R0B_I2S_PCM_CTRL1 (0x0b) */
+#define NAU8360_EN_TDM_TX_SFT			15
+#define NAU8360_EN_TDM_TX			(0x1 << NAU8360_EN_TDM_TX_SFT)
+#define NAU8360_EN_TDM_RX_SFT			14
+#define NAU8360_EN_TDM_RX			(0x1 << NAU8360_EN_TDM_RX_SFT)
+#define NAU8360_FRAME_START_SFT			8
+#define NAU8360_FRAME_START_MASK		(0x1 << NAU8360_FRAME_START_SFT)
+#define NAU8360_FRAME_START_H2L			(0x1 << NAU8360_FRAME_START_SFT)
+#define NAU8360_FRAME_START_L2H			(0x0 << NAU8360_FRAME_START_SFT)
+#define NAU8360_RX_JUSTIFY_SFT			7
+#define NAU8360_RX_JUSTIFY_MASK			(0x1 << NAU8360_RX_JUSTIFY_SFT)
+#define NAU8360_RX_OFFSET_SFT			2
+#define NAU8360_RX_OFFSET_MASK			(0x1f << NAU8360_RX_OFFSET_SFT)
+#define NAU8360_RX_OFFSET_I2S			(0x1 << NAU8360_RX_OFFSET_SFT)
+#define NAU8360_RX_OFFSET_LEFT			(0x0 << NAU8360_RX_OFFSET_SFT)
+#define NAU8360_RX_OFFSET_RIGHT			(0x0 << NAU8360_RX_OFFSET_SFT)
+#define NAU8360_RX_OFFSET_PCM_A			(0x1 << NAU8360_RX_OFFSET_SFT)
+#define NAU8360_RX_OFFSET_PCM_B			(0x0 << NAU8360_RX_OFFSET_SFT)
+
+/* NAU8360_R0C_I2S_PCM_CTRL2 (0xc) */
+#define NAU8360_TDM_DLEN_SFT			10
+#define NAU8360_TDM_DLEN_MASK			(0x3 << NAU8360_TDM_DLEN_SFT)
+#define NAU8360_TDM_DLEN_32			(0x3 << NAU8360_TDM_DLEN_SFT)
+#define NAU8360_TDM_DLEN_24			(0x2 << NAU8360_TDM_DLEN_SFT)
+#define NAU8360_TDM_DLEN_20			(0x1 << NAU8360_TDM_DLEN_SFT)
+#define NAU8360_TDM_DLEN_16			(0x0 << NAU8360_TDM_DLEN_SFT)
+#define NAU8360_TDM_CLEN_SFT			8
+#define NAU8360_TDM_CLEN_MASK			(0x3 << NAU8360_TDM_CLEN_SFT)
+#define NAU8360_TDM_CLEN_32			(0x2 << NAU8360_TDM_CLEN_SFT)
+#define NAU8360_TDM_CLEN_24			(0x1 << NAU8360_TDM_CLEN_SFT)
+#define NAU8360_TDM_CLEN_16			(0x0 << NAU8360_TDM_CLEN_SFT)
+#define NAU8360_RX_DACR_SFT			4
+#define NAU8360_RX_DACR_MASK			(0xf << NAU8360_RX_DACR_SFT)
+#define NAU8360_RX_DACL_MASK			0xf
+
+/* NAU8360_R0D_I2S_PCM_CTRL3 (0x0d) */
+#define NAU8360_TX_FILL_SFT			12
+#define NAU8360_TX_FILL_MASK			(0x1 << NAU8360_TX_FILL_SFT)
+#define NAU8360_TX_FILL_HIGHZ			(0x1 << NAU8360_TX_FILL_SFT)
+#define NAU8360_TX_FILL_ZERO			(0x0 << NAU8360_TX_FILL_SFT)
+#define NAU8360_TX_OFFSET_SFT			9
+#define NAU8360_TX_OFFSET_MASK			(0x7 << NAU8360_TX_OFFSET_SFT)
+#define NAU8360_TX_OFFSET_I2S			(0x1 << NAU8360_TX_OFFSET_SFT)
+#define NAU8360_TX_OFFSET_LEFT			(0x0 << NAU8360_TX_OFFSET_SFT)
+#define NAU8360_TX_OFFSET_RIGHT			(0x0 << NAU8360_TX_OFFSET_SFT)
+#define NAU8360_TX_OFFSET_PCM_A			(0x1 << NAU8360_TX_OFFSET_SFT)
+#define NAU8360_TX_OFFSET_PCM_B			(0x0 << NAU8360_TX_OFFSET_SFT)
+#define NAU8360_VSNS_L_SLEN_SFT			7
+#define NAU8360_VSNS_L_SLEN_MASK		(0x1 << NAU8360_VSNS_L_SLEN_SFT)
+#define NAU8360_VSNS_L_SLEN_16			(0x1 << NAU8360_VSNS_L_SLEN_SFT)
+#define NAU8360_VSNS_L_SLEN_8			(0x0 << NAU8360_VSNS_L_SLEN_SFT)
+#define NAU8360_VSNS_L_TX_EN_SFT		6
+#define NAU8360_VSNS_L_TX_EN			(0x1 << NAU8360_VSNS_L_TX_EN_SFT)
+#define NAU8360_VSNS_L_SLOT_MASK		0x3f
+
+/* NAU8360_R0E_I2S_DATA_CTRL1 (0x0e) */
+#define NAU8360_ISNS_L_SLEN_SFT			15
+#define NAU8360_ISNS_L_SLEN_MASK		(0x1 << NAU8360_ISNS_L_SLEN_SFT)
+#define NAU8360_ISNS_L_SLEN_16			(0x1 << NAU8360_ISNS_L_SLEN_SFT)
+#define NAU8360_ISNS_L_SLEN_8			(0x0 << NAU8360_ISNS_L_SLEN_SFT)
+#define NAU8360_ISNS_L_TX_EN_SFT		14
+#define NAU8360_ISNS_L_TX_EN			(0x1 << NAU8360_ISNS_L_TX_EN_SFT)
+#define NAU8360_ISNS_L_SLOT_SFT			8
+#define NAU8360_ISNS_L_SLOT_MASK		(0x3f << NAU8360_ISNS_L_SLOT_SFT)
+#define NAU8360_PDM_L_SLEN_SFT			7
+#define NAU8360_PDM_L_SLEN_MASK			(0x1 << NAU8360_PDM_L_SLEN_SFT)
+#define NAU8360_PDM_L_SLEN_24			(0x1 << NAU8360_PDM_L_SLEN_SFT)
+#define NAU8360_PDM_L_SLEN_16			(0x0 << NAU8360_PDM_L_SLEN_SFT)
+#define NAU8360_PDM_L_TX_EN_SFT			6
+#define NAU8360_PDM_L_TX_EN			(0x1 << NAU8360_PDM_L_TX_EN_SFT)
+#define NAU8360_PDM_L_SLOT_MASK			0x3f
+
+/* NAU8360_R0F_I2S_DATA_CTRL2 (0xf) */
+#define NAU8360_VBAT_TX_EN_SFT			14
+#define NAU8360_VBAT_TX_EN			(0x1 << NAU8360_VBAT_TX_EN_SFT)
+#define NAU8360_VBAT_SLOT_SFT			8
+#define NAU8360_VBAT_SLOT_MASK			(0x3f << NAU8360_VBAT_SLOT_SFT)
+#define NAU8360_PDM_R_SLEN_SFT			7
+#define NAU8360_PDM_R_SLEN_MASK			(0x1 << NAU8360_PDM_R_SLEN_SFT)
+#define NAU8360_PDM_R_SLEN_24			(0x1 << NAU8360_PDM_R_SLEN_SFT)
+#define NAU8360_PDM_R_SLEN_16			(0x0 << NAU8360_PDM_R_SLEN_SFT)
+#define NAU8360_PDM_R_TX_EN_SFT			6
+#define NAU8360_PDM_R_TX_EN			(0x1 << NAU8360_PDM_R_TX_EN_SFT)
+#define NAU8360_PDM_R_SLOT_MASK			0x3f
+
+/* NAU8360_R10_I2S_DATA_CTRL3 (0x10) */
+#define NAU8360_RX_ANC_R_SFT			12
+#define NAU8360_RX_ANC_R_MASK			(0xf << NAU8360_RX_ANC_R_SFT)
+#define NAU8360_RX_ANC_L_SFT			8
+#define NAU8360_RX_ANC_L_MASK			(0xf << NAU8360_RX_ANC_L_SFT)
+#define NAU8360_TDM_LOOPBACK_SFT		7
+#define NAU8360_TDM_LOOPBACK			(0x1 << NAU8360_TDM_LOOPBACK_SFT)
+#define NAU8360_TEMP_TX_EN_SFT			6
+#define NAU8360_TEMP_TX_EN			(0x1 << NAU8360_TEMP_TX_EN_SFT)
+#define NAU8360_TEMP_SLOT_MASK			0x3f
+
+/* NAU8360_R11_I2S_DATA_CTRL4 (0x11) */
+#define NAU8360_VSNS_R_SLEN_SFT			15
+#define NAU8360_VSNS_R_SLEN_MASK		(0x1 << NAU8360_VSNS_R_SLEN_SFT)
+#define NAU8360_VSNS_R_SLEN_16			(0x1 << NAU8360_VSNS_R_SLEN_SFT)
+#define NAU8360_VSNS_R_SLEN_8			(0x0 << NAU8360_VSNS_R_SLEN_SFT)
+#define NAU8360_VSNS_R_TX_EN_SFT		14
+#define NAU8360_VSNS_R_TX_EN			(0x1 << NAU8360_VSNS_R_TX_EN_SFT)
+#define NAU8360_VSNS_R_SLOT_SFT			8
+#define NAU8360_VSNS_R_SLOT_MASK		(0x3f << NAU8360_VSNS_R_SLOT_SFT)
+#define NAU8360_ISNS_R_SLEN_SFT			7
+#define NAU8360_ISNS_R_SLEN_MASK		(0x1 << NAU8360_ISNS_R_SLEN_SFT)
+#define NAU8360_ISNS_R_SLEN_16			(0x1 << NAU8360_ISNS_R_SLEN_SFT)
+#define NAU8360_ISNS_R_SLEN_8			(0x0 << NAU8360_ISNS_R_SLEN_SFT)
+#define NAU8360_ISNS_R_TX_EN_SFT		6
+#define NAU8360_ISNS_R_TX_EN			(0x1 << NAU8360_ISNS_R_TX_EN_SFT)
+#define NAU8360_ISNS_R_SLOT_MASK		0x3f
+
+/* NAU8360_R12_PATH_CTRL (0x12) */
+#define NAU8360_SEL_HW1_SFT			9
+#define NAU8360_SEL_HW1_MASK			(0x1 << NAU8360_SEL_HW1_SFT)
+#define NAU8360_SEL_HW1_OUT			(0x1 << NAU8360_SEL_HW1_SFT)
+#define NAU8360_SEL_HW1_BYP			(0x0 << NAU8360_SEL_HW1_SFT)
+#define NAU8360_AUD_SEL_SFT			7
+#define NAU8360_AUD_SEL_MASK			(0x1 << NAU8360_AUD_SEL_SFT)
+#define NAU8360_AUD_SEL_SINCOUT			(0x1 << NAU8360_AUD_SEL_SFT)
+#define NAU8360_AUD_SEL_SINCBYP			(0x0 << NAU8360_AUD_SEL_SFT)
+#define NAU8360_SEL_HW2_SFT			6
+#define NAU8360_SEL_HW2_MASK			(0x1 << NAU8360_SEL_HW2_SFT)
+#define NAU8360_SEL_HW2_OUT			(0x1 << NAU8360_SEL_HW2_SFT)
+#define NAU8360_SEL_HW2_BYP			(0x0 << NAU8360_SEL_HW2_SFT)
+#define NAU8360_DAC_SEL_SFT			5
+#define NAU8360_DAC_SEL_MASK			(0x1 << NAU8360_DAC_SEL_SFT)
+#define NAU8360_DAC_SEL_DSP			(0x1 << NAU8360_DAC_SEL_SFT)
+#define NAU8360_DAC_SEL_BYP			(0x0 << NAU8360_DAC_SEL_SFT)
+#define NAU8360_SEL_HW3_SFT			4
+#define NAU8360_SEL_HW3_MASK			(0x1 << NAU8360_SEL_HW3_SFT)
+#define NAU8360_SEL_HW3_OUT			(0x1 << NAU8360_SEL_HW3_SFT)
+#define NAU8360_SEL_HW3_BYP			(0x0 << NAU8360_SEL_HW3_SFT)
+
+/* NAU8360_R17_I2S0_DATA_CTRL5 (0x17)*/
+#define NAU8360_AEC_L_SLEN_SFT			15
+#define NAU8360_AEC_L_SLEN_MASK			(0x1 << NAU8360_AEC_L_SLEN_SFT)
+#define NAU8360_AEC_L_SLEN_32			(0x1 << NAU8360_AEC_L_SLEN_SFT)
+#define NAU8360_AEC_L_SLEN_16			(0x0 << NAU8360_AEC_L_SLEN_SFT)
+#define NAU8360_AEC_L_EN_SFT			14
+#define NAU8360_AEC_L_EN			(0x1 << NAU8360_AEC_L_EN_SFT)
+#define NAU8360_AEC_L_SLOT_SFT			8
+#define NAU8360_AEC_L_SLOT_MASK			(0x3f << NAU8360_AEC_L_SLOT_SFT)
+#define NAU8360_AEC_R_SLEN_SFT			7
+#define NAU8360_AEC_R_SLEN_MASK			(0x1 << NAU8360_AEC_R_SLEN_SFT)
+#define NAU8360_AEC_R_SLEN_32			(0x1 << NAU8360_AEC_R_SLEN_SFT)
+#define NAU8360_AEC_R_SLEN_16			(0x0 << NAU8360_AEC_R_SLEN_SFT)
+#define NAU8360_AEC_R_EN_SFT			6
+#define NAU8360_AEC_R_EN			(0x1 << NAU8360_AEC_R_EN_SFT)
+#define NAU8360_AEC_R_SLOT_MASK			0x3f
+
+/* NAU8360_R1A_DSP_CORE_CTRL2 (0x1a) */
+#define NAU8360_DSP_RUNSTALL_SFT		4
+#define NAU8360_DSP_RUNSTALL			(0x1 << NAU8360_DSP_RUNSTALL_SFT)
+
+/* NAU8360_R21_VBAT_READOUT (0x21) */
+#define NAU8360_VBAT_BASE 1108
+#define NAU8360_VBAT_STEP 12353
+
+/* NAU8360_R40_CLK_DET_CTRL (0x40) */
+#define NAU8360_APWRUPEN_SFT			15
+#define NAU8360_APWRUPEN			(0x1 << NAU8360_APWRUPEN_SFT)
+#define NAU8360_CLKPWRUPEN_SFT			14
+#define NAU8360_CLKPWRUPEN			(0x1 << NAU8360_CLKPWRUPEN_SFT)
+#define NAU8360_SRATE_SFT			10
+#define NAU8360_SRATE_MASK			(0x7 << NAU8360_SRATE_SFT)
+#define NAU8360_SRATE_192000			(0x4 << NAU8360_SRATE_SFT)
+#define NAU8360_SRATE_96000			(0x3 << NAU8360_SRATE_SFT)
+#define NAU8360_SRATE_48000			(0x2 << NAU8360_SRATE_SFT)
+#define NAU8360_SRATE_32000			(0x1 << NAU8360_SRATE_SFT)
+#define NAU8360_SRATE_16000			(0x0 << NAU8360_SRATE_SFT)
+#define NAU8360_MCLK_RATE_SFT			7
+#define NAU8360_MCLK_RATE_MASK			(0x7 << NAU8360_MCLK_RATE_SFT)
+#define NAU8360_MCLK_RATE_24576			(0x4 << NAU8360_MCLK_RATE_SFT)
+#define NAU8360_MCLK_RATE_24000			(0x3 << NAU8360_MCLK_RATE_SFT)
+#define NAU8360_MCLK_RATE_19200			(0x2 << NAU8360_MCLK_RATE_SFT)
+#define NAU8360_MCLK_RATE_12288			(0x1 << NAU8360_MCLK_RATE_SFT)
+#define NAU8360_MCLK_RATE_12000			(0x0 << NAU8360_MCLK_RATE_SFT)
+#define NAU8360_FS_MCLK_DET_SFT			6
+#define NAU8360_FS_MCLK_DET			(0x1 << NAU8360_FS_MCLK_DET_SFT)
+#define NAU8360_FS_BCLK_DET_SFT			5
+#define NAU8360_FS_BCLK_DET			(0x1 << NAU8360_FS_BCLK_DET_SFT)
+
+/* NAU8360_R41_CLK_CTL2 (0x41) */
+#define NAU8360_NOISE_FILTER_THD_SFT		12
+#define NAU8360_NOISE_FILTER_THDT_MASK		(0xf << NAU8360_NOISE_FILTER_THD_SFT)
+#define NAU8360_SEL_BLOCK_SIZE_SFT		6
+#define NAU8360_SEL_BLOCK_SIZE_MASK		(0x7 << NAU8360_SEL_BLOCK_SIZE_SFT)
+#define NAU8360_EN_ABCLKDET_SFT			3
+#define NAU8360_EN_ABCLKDET			(0x1 << NAU8360_EN_ABCLKDET_SFT)
+#define NAU8360_EN_DBCLKDET_SFT			2
+#define NAU8360_EN_DBCLKDET			(0x1 << NAU8360_EN_DBCLKDET_SFT)
+#define NAU8360_EN_AMCLKDET_SFT			1
+#define NAU8360_EN_AMCLKDET			(0x1 << NAU8360_EN_AMCLKDET_SFT)
+#define NAU8360_EN_DMCLKDET			0x1
+
+/* NAU8360_R46_I2C_DEVICE_ID (0x46) */
+#define NAU8360_REG_SI_REV_MASK			0xff
+#define NAU8360_REG_SI_REV_B			0xf2
+#define NAU8360_REG_SI_REV_C			0xf5
+
+/* NAU8360_R5D_SINC_CFG (0x5d) */
+#define NAU8360_DAC_SINC_OSR_SFT		4
+#define NAU8360_DAC_SINC_OSR_MASK		(0x3 << NAU8360_DAC_SINC_OSR_SFT)
+#define NAU8360_DAC_SINC_OSR_250		(0x3 << NAU8360_DAC_SINC_OSR_SFT)
+#define NAU8360_DAC_SINC_OSR_200		(0x2 << NAU8360_DAC_SINC_OSR_SFT)
+#define NAU8360_DAC_SINC_OSR_128		(0x1 << NAU8360_DAC_SINC_OSR_SFT)
+#define NAU8360_DAC_SINC_OSR_0			(0x0 << NAU8360_DAC_SINC_OSR_SFT)
+#define NAU8360_IVSENSE_BS_OSR_MASK		0x3
+#define NAU8360_IVSENSE_BS_OSR_25		0x3
+#define NAU8360_IVSENSE_BS_OSR_64		0x2
+#define NAU8360_IVSENSE_BS_OSR_32		0x1
+#define NAU8360_IVSENSE_BS_OSR_16		0x0
+
+/* NAU8360_R60_RST (0x60) */
+#define NAU8360_PD_POR5_SFT			4
+#define NAU8360_PD_POR5_PWD			(0x1 << NAU8360_PD_POR5_SFT)
+#define NAU8360_EFUSE_CTRL_SFT			3
+#define NAU8360_EFUSE_CTRL_EN			(0x1 << NAU8360_EFUSE_CTRL_SFT)
+
+/*  NAU8360_R67_ANALOG_CONTROL_0 (0x67) */
+#define NAU8360_SCP_CLEAR_MODE_SFT		15
+#define NAU8360_SCP_CLEAR_MODE_MASK		(0x1 << NAU8360_SCP_CLEAR_MODE_SFT)
+#define NAU8360_SCP_CLEAR_MODE_AUTO		(0x1 << NAU8360_SCP_CLEAR_MODE_SFT)
+#define NAU8360_SCP_CLEAR_MODE_MANU		(0x0 << NAU8360_SCP_CLEAR_MODE_SFT)
+#define NAU8360_SCP_CLEAR_SFT			14
+#define NAU8360_SCP_CLEAR			(0x1 << NAU8360_SCP_CLEAR_SFT)
+#define NAU8360_HV_EN_SFT			13
+#define NAU8360_HV_EN				(0x1 << NAU8360_HV_EN_SFT)
+#define NAU8360_ANA_MUTE_SFT			8
+#define NAU8360_ANA_MUTE			(0x1 << NAU8360_ANA_MUTE_SFT)
+#define NAU8360_MOD_GAIN_SFT			5
+#define NAU8360_MOD_GAIN_MASK			(0x7 << NAU8360_MOD_GAIN_SFT)
+#define NAU8360_MOD_GAIN_20DB			(0x7 << NAU8360_MOD_GAIN_SFT)
+#define NAU8360_MOD_GAIN_18DB			(0x6 << NAU8360_MOD_GAIN_SFT)
+#define NAU8360_MOD_GAIN_16DB			(0x5 << NAU8360_MOD_GAIN_SFT)
+#define NAU8360_MOD_GAIN_14DB			(0x4 << NAU8360_MOD_GAIN_SFT)
+#define NAU8360_MOD_GAIN_12DB			(0x3 << NAU8360_MOD_GAIN_SFT)
+#define NAU8360_MOD_GAIN_10DB			(0x2 << NAU8360_MOD_GAIN_SFT)
+#define NAU8360_MOD_GAIN_8DB			(0x1 << NAU8360_MOD_GAIN_SFT)
+#define NAU8360_MOD_GAIN_6DB			(0x0 << NAU8360_MOD_GAIN_SFT)
+#define NAU8360_CM_COMP_SFT			4
+#define NAU8360_CM_COMP_EN			(0x1 << NAU8360_CM_COMP_SFT)
+#define NAU8360_V_PBTL_SFT			3
+#define NAU8360_V_PBTL_EN			(0x1 << NAU8360_V_PBTL_SFT)
+
+/* NAU8360_R68_ANALOG_CONTROL_1 (0x68) */
+#define NAU8360_TRIMSCR_SFT			6
+#define NAU8360_TRIMSCR_MASK			(0x3 << NAU8360_TRIMSCR_SFT)
+#define NAU8360_TRIMSCR_HIGH			(0x3 << NAU8360_TRIMSCR_SFT)
+#define NAU8360_TRIMSCR_MHIGH			(0x2 << NAU8360_TRIMSCR_SFT)
+#define NAU8360_TRIMSCR_MLOW			(0x1 << NAU8360_TRIMSCR_SFT)
+#define NAU8360_TRIMSCR_LOW			(0x0 << NAU8360_TRIMSCR_SFT)
+#define NAU8360_DTX_SFT				5
+#define NAU8360_DTX_EN				(0x1 << NAU8360_DTX_SFT)
+#define NAU8360_DRVCTL_SEGL_SFT			4
+#define NAU8360_DRVCTL_SEGL_FULL		(0x1 << NAU8360_DRVCTL_SEGL_SFT)
+#define NAU8360_DRVCTL_SEGR_SFT			2
+#define NAU8360_DRVCTL_SEGR_FULL		(0x1 << NAU8360_DRVCTL_SEGR_SFT)
+#define NAU8360_SCP_TEST_SFT			1
+#define NAU8360_SCP_TEST			(0x1 << NAU8360_SCP_TEST_SFT)
+#define NAU8360_SEG_AUTO_CTRL_EN		0x1
+
+/* NAU8360_R69_ANALOG_CONTROL_3 (0x69) */
+#define NAU8360_ANA_STATUS_OTP_SFT		8
+#define NAU8360_ANA_STATUS_OTP_MASK		(0x1 << NAU8360_ANA_STATUS_OTP_SFT)
+#define NAU8360_ANA_STATUS_OVLO_SFT		7
+#define NAU8360_ANA_STATUS_OVLO_MASK		(0x1 << NAU8360_ANA_STATUS_OVLO_SFT)
+#define NAU8360_ANA_STATUS_UVLO_SFT		6
+#define NAU8360_ANA_STATUS_UVLO_MASK		(0x1 << NAU8360_ANA_STATUS_UVLO_SFT)
+#define NAU8360_ANA_STATUS_SCP_SFT		4
+#define NAU8360_ANA_STATUS_SCP_MASK		(0x1 << NAU8360_ANA_STATUS_SCP_SFT)
+#define NAU8360_ANA_STATUS_EN_V2I_SFT		3
+#define NAU8360_ANA_STATUS_EN_V2I_MASK		(0x1 << NAU8360_ANA_STATUS_EN_V2I_SFT)
+#define NAU8360_ANA_STATUS_FAULT_SFT		2
+#define NAU8360_ANA_STATUS_FAULT_MASK		(0x1 << NAU8360_ANA_STATUS_FAULT_SFT)
+#define NAU8360_ANA_STATUS_DC_DET_SFT		1
+#define NAU8360_ANA_STATUS_DC_DET_MASK		(0x1 << NAU8360_ANA_STATUS_DC_DET_SFT)
+#define NAU8360_ANA_STATUS_EN_ALL_MASK		0x1
+
+/* NAU8360_R6A_SARADC_CFG0 (0x6a) */
+#define NAU8360_SARADC_SFT			15
+#define NAU8360_SARADC_EN			(0x1 << NAU8360_SARADC_SFT)
+#define NAU8360_RESETB_SARADC_SFT		14
+#define NAU8360_RESETB_SARADC_MASK		(0x1 << NAU8360_RESETB_SARADC_SFT)
+#define NAU8360_RESETB_SARADC_NOR		(0x1 << NAU8360_RESETB_SARADC_SFT)
+#define NAU8360_RESETB_SARADC_RST		(0x0 << NAU8360_RESETB_SARADC_SFT)
+#define NAU8360_SARADC_CAL_SFT			13
+#define NAU8360_SARADC_CAL			(0x1 << NAU8360_SARADC_CAL_SFT)
+#define NAU8360_SARADC_DIV_SFT			12
+#define NAU8360_SARADC_DIV			(0x1 << NAU8360_SARADC_DIV_SFT)
+#define NAU8360_SARADC_CALSEL_SFT		9
+#define NAU8360_SARADC_CALSEL_MASK		(0x7 << NAU8360_SARADC_CALSEL_SFT)
+#define NAU8360_TRIMSMPL_SFT			3
+#define NAU8360_TRIMSMPL_MASK			(0x7 << NAU8360_TRIMSMPL_SFT)
+#define NAU8360_SAR_INPUT_CH_SEL_SFT		2
+#define NAU8360_SAR_INPUT_CH_SEL_MASK		(0x1 << NAU8360_SAR_INPUT_CH_SEL_SFT)
+#define NAU8360_VBAT_AVG_SFT			1
+#define NAU8360_VBAT_AVG_EN			(0x1 << NAU8360_VBAT_AVG_SFT)
+#define NAU8360_VTEMP_AVG_EN			0x1
+
+/* NAU8360_R6B_SARADC_CFG1 (0x6b) */
+#define NAU8360_VTEMP_SFT			15
+#define NAU8360_VTEMP_EN			(0x1 << NAU8360_VTEMP_SFT)
+#define NAU8360_VBATDIV_SFT			14
+#define NAU8360_VBATDIV_EN			(0x1 << NAU8360_VBATDIV_SFT)
+#define NAU8360_VREF_SFT			10
+#define NAU8360_VREF_EN				(0x1 << NAU8360_VREF_SFT)
+#define NAU8360_PRELOAD_VREF_SFT		9
+#define NAU8360_PRELOAD_VREF_EN			(0x1 << NAU8360_PRELOAD_VREF_SFT)
+#define NAU8360_VTEMP_AVG_N_SFT			2
+#define NAU8360_VTEMP_AVG_N_MASK		(0x3 << NAU8360_VTEMP_AVG_N_SFT)
+#define NAU8360_VTEMP_AVG_N_512			(0x3 << NAU8360_VTEMP_AVG_N_SFT)
+#define NAU8360_VTEMP_AVG_N_128			(0x2 << NAU8360_VTEMP_AVG_N_SFT)
+#define NAU8360_VTEMP_AVG_N_32			(0x1 << NAU8360_VTEMP_AVG_N_SFT)
+#define NAU8360_VTEMP_AVG_N_1			(0x0 << NAU8360_VTEMP_AVG_N_SFT)
+#define NAU8360_VBAT_AVG_N_MASK			0x3
+#define NAU8360_VBAT_AVG_N_512			0x3
+#define NAU8360_VBAT_AVG_N_128			0x2
+#define NAU8360_VBAT_AVG_N_32			0x1
+#define NAU8360_VBAT_AVG_N_1			0
+
+/* NAU8360_R6C_IVSNS_CFG0 (0x6c) */
+#define NAU8360_PD_VSNS_R_SFT			15
+#define NAU8360_PD_VSNS_R_PMD			(0x1 << NAU8360_PD_VSNS_R_SFT)
+#define NAU8360_PD_VSNS_L_SFT			14
+#define NAU8360_PD_VSNS_L_PMD			(0x1 << NAU8360_PD_VSNS_L_SFT)
+#define NAU8360_PD_ISNS_R_SFT			13
+#define NAU8360_PD_ISNS_R_PMD			(0x1 << NAU8360_PD_ISNS_R_SFT)
+#define NAU8360_PD_ISNS_L_SFT			12
+#define NAU8360_PD_ISNS_L_PMD			(0x1 << NAU8360_PD_ISNS_L_SFT)
+#define NAU8360_RESET_SC_VADC_R_SFT		11
+#define NAU8360_RESET_SC_VADC_R_RST		(0x1 << NAU8360_RESET_SC_VADC_R_SFT)
+#define NAU8360_RESET_SC_VADC_L_SFT		10
+#define NAU8360_RESET_SC_VADC_L_RST		(0x1 << NAU8360_RESET_SC_VADC_L_SFT)
+#define NAU8360_RESET_SC_IADC_R_SFT		9
+#define NAU8360_RESET_SC_IADC_R_RST		(0x1 << NAU8360_RESET_SC_IADC_R_SFT)
+#define NAU8360_RESET_SC_IADC_L_SFT		8
+#define NAU8360_RESET_SC_IADC_L_RST		(0x1 << NAU8360_RESET_SC_IADC_L_SFT)
+#define NAU8360_PBTL_ISENE_LR_SFT		7
+#define NAU8360_PBTL_ISENE_LR_MASK		(0x1 << NAU8360_PBTL_ISENE_LR_SFT)
+#define NAU8360_PBTL_ISENE_LR_ILR		(0x1 << NAU8360_PBTL_ISENE_LR_SFT)
+#define NAU8360_PBTL_ISENE_LR_I2R		(0x0 << NAU8360_PBTL_ISENE_LR_SFT)
+#define NAU8360_PD_LDO_SDM_IVSNS_SFT		6
+#define NAU8360_PD_LDO_SDM_IVSNS_PMD		(0x1 << NAU8360_PD_LDO_SDM_IVSNS_SFT)
+
+/* NAU8360_R6D_IVSNS_CFG1 (0x6d) */
+#define NAU8360_PD_V2I_IVSNS_SFT		13
+#define NAU8360_PD_V2I_IVSNS_MASK		(0x1 << NAU8360_PD_V2I_IVSNS_SFT)
+#define NAU8360_PD_VREF_SDM_IVSNS_SFT		12
+#define NAU8360_PD_VREF_SDM_IVSNS_MASK		(0x1 << NAU8360_PD_VREF_SDM_IVSNS_SFT)
+#define NAU8360_VSNS_PGA_CUR_SET_SFT		10
+#define NAU8360_VSNS_PGA_CUR_SET_MASK		(0x3 << NAU8360_VSNS_PGA_CUR_SET_SFT)
+#define NAU8360_VSNS_GAIN_SFT			8
+#define NAU8360_VSNS_GAIN_MASK			(0x3 << NAU8360_VSNS_GAIN_SFT)
+#define NAU8360_VREF_CUR_SFT			6
+#define NAU8360_VREF_CUR_MASK			(0x3 << NAU8360_VREF_CUR_SFT)
+#define NAU8360_ISNS_PGA_CUR_SFT		4
+#define NAU8360_ISNS_PGA_CUR_MASK		(0x3 << NAU8360_ISNS_PGA_CUR_SFT)
+#define NAU8360_ISNS_GAIN_SFT			2
+#define NAU8360_ISNS_GAIN_MASK			(0x3 << NAU8360_ISNS_GAIN_SFT)
+#define NAU8360_SDM_DELAY_TRIM_MASK		0x3
+
+/* NAU8360_R6E_DAC_CFG0 (0x6e) */
+#define NAU8360_PD_DACL_SFT			11
+#define NAU8360_PD_DACL_DIS			(0x1 << NAU8360_PD_DACL_SFT)
+#define NAU8360_PD_DACR_SFT			10
+#define NAU8360_PD_DACR_DIS			(0x1 << NAU8360_PD_DACR_SFT)
+#define NAU8360_PD_CHOP_DAC_SFT			9
+#define NAU8360_PD_CHOP_DAC_MASK		(0x1 << NAU8360_PD_CHOP_DAC_SFT)
+#define NAU8360_DAC_RES_SFT			7
+#define NAU8360_DAC_RES_MASK			(0x1 << NAU8360_DAC_RES_SFT)
+#define NAU8360_DAC_CUR_SFT			6
+#define NAU8360_DAC_CUR_MASK			(0x1 << NAU8360_DAC_CUR_SFT)
+#define NAU8360_DAC_CUR_3_2DB			(0x1 << NAU8360_DAC_CUR_SFT)
+#define NAU8360_DAC_CUR_0DB			(0x0 << NAU8360_DAC_CUR_SFT)
+#define NAU8360_DAC_BUFOPCUR_SFT		4
+#define NAU8360_DAC_BUFOPCUR_MASK		(0x3 << NAU8360_DAC_BUFOPCUR_SFT)
+#define NAU8360_DAC_IVOPCUR_SFT			2
+#define NAU8360_DAC_IVOPCUR_MASK		(0x3 << NAU8360_DAC_IVOPCUR_SFT)
+#define NAU8360_DAC_LPFOP_CUR_MASK		0x3
+
+/* NAU8360_R71_CLK_DIV_CFG (0x71) */
+#define NAU8360_IVSNS_CLK_INV_SEL_SFT		10
+#define NAU8360_IVSNS_CLK_INV_SEL_MASK		(0x1 << NAU8360_IVSNS_CLK_INV_SEL_SFT)
+#define NAU8360_CLK_ANA_SEL_SFT			7
+#define NAU8360_CLK_ANA_SEL_MASK		(0x3 << NAU8360_CLK_ANA_SEL_SFT)
+#define NAU8360_CLK_ANA_SEL_PLL			(0x2 << NAU8360_CLK_ANA_SEL_SFT)
+#define NAU8360_CLK_ANA_SEL_BCLK		(0x1 << NAU8360_CLK_ANA_SEL_SFT)
+#define NAU8360_CLK_ANA_SEL_MCLK		(0x0 << NAU8360_CLK_ANA_SEL_SFT)
+#define NAU8360_IVSNS_CLK_DIV_SFT		4
+#define NAU8360_IVSNS_CLK_DIV_MASK		(0x7 << NAU8360_IVSNS_CLK_DIV_SFT)
+#define NAU8360_DAC_OUTPUT_EDGE_SFT		3
+#define NAU8360_DAC_OUTPUT_EDGE_MASK		(0x1 << NAU8360_DAC_OUTPUT_EDGE_SFT)
+#define NAU8360_DAC_CLK_DIV_SFT			1
+#define NAU8360_DAC_CLK_DIV_MASK		(0x3 << NAU8360_DAC_CLK_DIV_SFT)
+#define NAU8360_DAC_CLK_DIV_8			(0x3 << NAU8360_DAC_CLK_DIV_SFT)
+#define NAU8360_DAC_CLK_DIV_4			(0x2 << NAU8360_DAC_CLK_DIV_SFT)
+#define NAU8360_DAC_CLK_DIV_2			(0x1 << NAU8360_DAC_CLK_DIV_SFT)
+#define NAU8360_DAC_CLK_DIV_1			(0x0 << NAU8360_DAC_CLK_DIV_SFT)
+#define NAU8360_DAC_CHOP_CLK_DIV_MASK		0x1
+#define NAU8360_DAC_CHOP_CLK_DIV_32		0x1
+#define NAU8360_DAC_CHOP_CLK_DIV_16		0x0
+
+/* NAU8360_R72_PLL_CFG0 (0x72) */
+#define NAU8360_PLLOUT_DIV_SFT			10
+#define NAU8360_PLLOUT_DIV_MAX			0x1f
+#define NAU8360_PLLOUT_DIV_MASK			(0x1f << NAU8360_PLLOUT_DIV_SFT)
+#define NAU8360_PLLOUT_DIV_BYPASS		(0x0 << NAU8360_PLLOUT_DIV_SFT)
+#define NAU8360_PLL_CLK_SEL_SFT			8
+#define NAU8360_PLL_CLK_SEL_MASK		(0x3 << NAU8360_PLL_CLK_SEL_SFT)
+#define NAU8360_PLL_CLK_SEL_HIRC		(0x3 << NAU8360_PLL_CLK_SEL_SFT)
+#define NAU8360_PLL_CLK_SEL_BCLK		(0x1 << NAU8360_PLL_CLK_SEL_SFT)
+#define NAU8360_PLL_CLK_SEL_MCLK		(0x0 << NAU8360_PLL_CLK_SEL_SFT)
+#define NAU8360_PD_PLL_SFT			7
+#define NAU8360_PD_PLL_MASK			(0x1 << NAU8360_PD_PLL_SFT)
+#define NAU8360_PD_PLL_DIS			(0x1 << NAU8360_PD_PLL_SFT)
+#define NAU8360_PD_PLL_EN			(0x0 << NAU8360_PD_PLL_SFT)
+
+/* NAU8360_R73_PLL_CFG1 (0x73) */
+#define NAU8360_RSEL_SFT			13
+#define NAU8360_RSEL_MASK			(0x3 << NAU8360_RSEL_SFT)
+#define NAU8360_MSEL_SFT			8
+#define NAU8360_MSEL_MASK			(0x1f << NAU8360_MSEL_SFT)
+#define NAU8360_NSEL_MASK			0xff
+
+/* NAU8360_R74_PLL_CFG2 (0x74) */
+#define NAU8360_XSEL_MASK			0xfff
+
+/* NAU8360_R77_SOFT_SD (0x77) */
+#define NAU8360_SOFT_SD				0x55AA
+#define NAU8360_SOFT_SD_EN			0
+
+/* NAU8360_R78_PD_SW_DLDO (0x78) */
+#define NAU8360_PD_SW_DLDO			0xAA55
+#define NAU8360_PD_SW_DLDO_EN			0
+
+/* NAU8360_R7A_DAC_TRIM_CFG2 (0x7a) */
+#define NAU8360_DAC_TEMP_SLOPE_SFT		8
+#define NAU8360_DAC_TEMP_SLOPE_MASK		(0x7f << NAU8360_DAC_TEMP_SLOPE_SFT)
+
+/* NAU8360_R7B_IVSNS_TRIM_CFG (0x7b) */
+#define NAU8360_ISNS_TEMP_SLOPE_SFT		8
+#define NAU8360_ISNS_TEMP_SLOPE_MASK		(0x7f << NAU8360_ISNS_TEMP_SLOPE_SFT)
+#define NAU8360_VSNS_TEMP_SLOPE_MASK		0x7f
+
+/* NAU8360_R7C_MISC_TRIM_CFG (0x7c) */
+#define NAU8360_DAC_OS_SB_SFT			13
+#define NAU8360_DAC_OS_SB_SFT_MASK		(0x7 << NAU8360_DAC_OS_SB_SFT)
+#define NAU8360_DAC_GAIN_SB_SFT			11
+#define NAU8360_DAC_GAIN_SB_MASK		(0x3 << NAU8360_DAC_GAIN_SB_SFT)
+#define NAU8360_DAC_TEMP_SB_SFT			9
+#define NAU8360_DAC_TEMP_SB_MASK		(0x3 << NAU8360_DAC_TEMP_SB_SFT)
+#define NAU8360_ISNS_TEMP_SB_SFT		6
+#define NAU8360_ISNS_TEMP_SB_MASK		(0x7 << NAU8360_ISNS_TEMP_SB_SFT)
+#define NAU8360_VSNS_TEMP_SB_SFT		4
+#define NAU8360_VSNS_TEMP_SB_MASK		(0x3 << NAU8360_VSNS_TEMP_SB_SFT)
+#define NAU8360_ISNS_GAIN_SB_SFT		2
+#define NAU8360_ISNS_GAIN_SB_MASK		(0x3 << NAU8360_ISNS_GAIN_SB_SFT)
+#define NAU8360_VSNS_GAIN_SB_MASK		0x3
+
+/* NAU8360_R7E_CLK_GATED_EN (0x7e)*/
+#define NAU8360_CLK_GATED_EN			0xa5a5
+#define NAU8360_CLK_GATED_DIS			0
+
+/* NAU8360_R86_HW3_CTL0 (0x86) */
+#define NAU8360_HW3_DRAM_CLR_SFT		14
+#define NAU8360_HW3_DRAM_CLR			(0x1 << NAU8360_HW3_DRAM_CLR_SFT)
+#define NAU8360_HW3_STALL_SFT			13
+#define NAU8360_HW3_STALL			(0x1 << NAU8360_HW3_STALL_SFT)
+
+/* NAU8360_R8C_HW3_CTL6 (0x8c)*/
+#define NAU8360_HW3_DROOP_SFT			2
+#define NAU8360_HW3_DROOP_MASK			(0x1 << NAU8360_HW3_DROOP_SFT)
+#define NAU8360_HW3_DROOP_192K			(0x1 << NAU8360_HW3_DROOP_SFT)
+#define NAU8360_HW3_DROOP_96K			(0x0 << NAU8360_HW3_DROOP_SFT)
+#define NAU8360_HW3_FS_MASK			0x1
+#define NAU8360_HW3_FS_192K			0x1
+#define NAU8360_HW3_FS_96K			0x0
+
+/* NAU8360_R8F_HW3_CTL9 (0x8f)*/
+#define NAU8360_HW3_CH_MUTE_SFT			8
+#define NAU8360_HW3_CH_MUTE			(0x1 << NAU8360_HW3_CH_MUTE_SFT)
+#define NAU8360_HW3_ZERO_THD_MASK		0xff
+
+/* NAU8360_R90_HW2_CTL0 (0x90) */
+#define NAU8360_HW2_DRAM_CLR_SFT		14
+#define NAU8360_HW2_DRAM_CLR			(0x1 << NAU8360_HW2_DRAM_CLR_SFT)
+#define NAU8360_HW2_STALL_SFT			13
+#define NAU8360_HW2_STALL			(0x1 << NAU8360_HW2_STALL_SFT)
+
+/* NAU8360_R96_HW2_CTL6 (0x96) */
+#define NAU8360_HW1_ANC_EN_SFT			15
+#define NAU8360_HW1_ANC_EN			(0x1 << NAU8360_HW1_ANC_EN_SFT)
+#define NAU8360_HW2_DROOP_SEL_SFT		3
+#define NAU8360_HW2_DROOP_SEL_MASK		(0x1 << NAU8360_HW2_DROOP_SEL_SFT)
+#define NAU8360_HW2_DROOP_SEL_LARGE		(0x1 << NAU8360_HW2_DROOP_SEL_SFT)
+#define NAU8360_HW2_DROOP_SEL_SMALL		(0x0 << NAU8360_HW2_DROOP_SEL_SFT)
+#define NAU8360_HW2_DROOP_EN_SFT		2
+#define NAU8360_HW2_DROOP_EN			(0x1 << NAU8360_HW2_DROOP_EN_SFT)
+#define NAU8360_HW2_LATENCY_SFT			1
+#define NAU8360_HW2_LATENCY_MASK		(0x1 << NAU8360_HW2_LATENCY_SFT)
+#define NAU8360_HW2_LATENCY_LOW			(0x1 << NAU8360_HW2_LATENCY_SFT)
+#define NAU8360_HW2_LATENCY_NOR			(0x0 << NAU8360_HW2_LATENCY_SFT)
+#define NAU8360_HW2_FS_MASK			0x1
+#define NAU8360_HW2_FS_192K			0x1
+#define NAU8360_HW2_FS_96K			0x0
+
+/* NAU8360_R99_HW2_CTL9 (0x99) */
+#define NAU8360_HW2_VSR_SFT			9
+#define NAU8360_HW2_VSR_MASK			(0x3 << NAU8360_HW2_VSR_SFT)
+#define NAU8360_HW2_CH_MUTE_SFT			8
+#define NAU8360_HW2_CH_MUTE			(0x1 << NAU8360_HW2_CH_MUTE_SFT)
+#define NAU8360_HW2_ZERO_THD_MASK		0xff
+
+/* NAU8360_R9C_HW1_CTL2 (0x9c) */
+#define NAU8360_MUTE_INTRVL_SFT			11
+#define NAU8360_MUTE_INTRVL_MASK		(0x3 << NAU8360_MUTE_INTRVL_SFT)
+#define NAU8360_MUTE_INTRVL_699MS		(0x3 << NAU8360_MUTE_INTRVL_SFT)
+#define NAU8360_MUTE_INTRVL_466MS		(0x2 << NAU8360_MUTE_INTRVL_SFT)
+#define NAU8360_MUTE_INTRVL_233MS		(0x1 << NAU8360_MUTE_INTRVL_SFT)
+#define NAU8360_HW1_CH_MUTE_SFT			8
+#define NAU8360_HW1_CH_MUTE			(0x1 << NAU8360_HW1_CH_MUTE_SFT)
+#define NAU8360_HW1_ZERO_THD_MASK		0xff
+
+/* NAU8360_R9D_PEQ_CTL (0x9d) */
+#define NAU8360_PEQ_BAND_SFT			12
+#define NAU8360_PEQ_BAND_MASK			(0xf << NAU8360_PEQ_BAND_SFT)
+#define NAU8360_HW1_MEM_TEST_SFT		2
+#define NAU8360_HW1_MEM_TEST			(0x1 << NAU8360_HW1_MEM_TEST_SFT)
+#define NAU8360_HW1_MEM_CLEAR_SFT		1
+#define NAU8360_HW1_MEM_CLEAR			(0x1 << NAU8360_HW1_MEM_CLEAR_SFT)
+#define NAU8360_PEQ_STALL			0x1
+
+/* NAU8360_RA4_ANA_REG_0 (0xa4) */
+#define NAU8360_MSEL_SAW_SFT			8
+#define NAU8360_MSEL_SAW_MASK			(0xff << NAU8360_MSEL_SAW_SFT)
+#define NAU8360_FTRIM_PWM_SFT			6
+#define NAU8360_FTRIM_PWM_MASK			(0x3 << NAU8360_MSEL_SAW_SFT)
+#define NAU8360_SEL_STCLK_SFT			5
+#define NAU8360_SEL_STCLK_MASK			(0x1 << NAU8360_SEL_STCLK_SFT)
+#define NAU8360_SEL_STCLK_IVCLK			(0x1 << NAU8360_SEL_STCLK_SFT)
+#define NAU8360_SEL_STCLK_FREERUN		(0x0 << NAU8360_SEL_STCLK_SFT)
+#define NAU8360_NSEL_SAW_MASK			0x1f
+
+/* NAU8360_RA5_ANA_REG_1 (0xa5) */
+#define NAU8360_ICP_SAW_SET_SFT			14
+#define NAU8360_ICP_SAW_SET_MASK		(0x3 << NAU8360_ICP_SAW_SET_SFT)
+#define NAU8360_VSAW_LV_SFT			12
+#define NAU8360_VSAW_LV_MASK			(0x3 << NAU8360_VSAW_LV_SFT)
+#define NAU8360_KVCO_SAW_SFT			10
+#define NAU8360_KVCO_SAW_MASK			(0x3 << NAU8360_KVCO_SAW_SFT)
+#define NAU8360_TRIM_LPF_SFT			8
+#define NAU8360_TRIM_LPF_MASK			(0x3 << NAU8360_TRIM_LPF_SFT)
+#define NAU8360_SAW_PLL_SFT			7
+#define NAU8360_SAW_PLL_MASK			(0x1 << NAU8360_SAW_PLL_SFT)
+#define NAU8360_SAW_PLL_NOR			(0x1 << NAU8360_SAW_PLL_SFT)
+#define NAU8360_SAW_PLL_RST			(0x0 << NAU8360_SAW_PLL_SFT)
+#define NAU8360_CLASSD_SHT_SFT			6
+#define NAU8360_CLASSD_SHT_IN			(0x1 << NAU8360_CLASSD_SHT_SFT)
+#define NAU8360_HVEN_SYNC_SFT			5
+#define NAU8360_HVEN_SYNC_SAW			(0x1 << NAU8360_HVEN_SYNC_SFT)
+#define NAU8360_SYNC_SAW_CLK_SFT		4
+#define NAU8360_SYNC_SAW_CLK_INVT		(0x1 << NAU8360_SYNC_SAW_CLK_SFT)
+#define NAU8360_DEPOP_CAP_SFT			2
+#define NAU8360_DEPOP_CAP_MASK			(0x3 << NAU8360_DEPOP_CAP_SFT)
+#define NAU8360_DEPOP_CAP_48PF			(0x3 << NAU8360_DEPOP_CAP_SFT)
+#define NAU8360_DEPOP_CAP_36PF			(0x2 << NAU8360_DEPOP_CAP_SFT)
+#define NAU8360_DEPOP_CAP_24PF			(0x1 << NAU8360_DEPOP_CAP_SFT)
+#define NAU8360_DEPOP_CAP_12PF			(0x0 << NAU8360_DEPOP_CAP_SFT)
+#define NAU8360_SW_BG_IO_SFT			1
+#define NAU8360_SW_BG_IO_CONN			(0x1 << NAU8360_SW_BG_IO_SFT)
+#define NAU8360_SW_VPTC_IO_EN			0x1
+
+/* NAU8360 VBAT Range Thresholds */
+#define NAU8360_VBAT_MIN		8
+#define NAU8360_VBAT_MID_THRES		13
+#define NAU8360_VBAT_HIGH_THRES		19
+#define NAU8360_VBAT_MAX		24
+
+/* NAU8360_R100_LEFT_BIQ0_COE (0x100)
+ * NAU8360_R200_RIGHT_BIQ0_COE (0x200)
+ */
+#define NAU8360_TOT_BAND_PER_CH 15
+#define NAU8360_TOT_BAND_COE 10
+#define NAU8360_TOT_BAND_COE_RANGE (NAU8360_TOT_BAND_COE + 2)
+#define NAU8360_PEQ_REG_WIDTH (NAU8360_TOT_BAND_COE - 1)
+
+/* DSP stereo core */
+#define NAU8360_DSP_CORE_NUM 2
+#define NAU8360_DSP_ADDR_BYNAME(x) \
+	(strstr((x), "Left") ? NAU8360_RF000_DSP_COMM : NAU8360_RF002_DSP_COMM)
+
+#define NAU8360_CODEC_DAI "nau8360-hifi"
+
+/* clock source */
+enum {
+	NAU8360_CLK_SRC_MCLK,
+	NAU8360_CLK_SRC_PLL,
+	NAU8360_CLK_SRC_ICLK,
+	NAU8360_CLK_SRC_BCLK,
+};
+
+/* clock target */
+enum {
+	NAU8360_CLK_ID_DIG, /* DIG_SYS + DSP + HW */
+	NAU8360_CLK_ID_ANA, /* IV + DAC */
+	NAU8360_CLK_ID_INT, /* HIRC48M standby  */
+};
+
+enum {
+	NAU8360_MCLK_FS_RATIO_250 = 250,
+	NAU8360_MCLK_FS_RATIO_256 = 256,
+	NAU8360_MCLK_FS_RATIO_400 = 400,
+	NAU8360_MCLK_FS_RATIO_500 = 500,
+	NAU8360_MCLK_FS_RATIO_512 = 512,
+};
+
+#define NAU8360_TDM_MAX_CHAN 8
+
+enum {
+	NAU8360_TDM_DACL,
+	NAU8360_TDM_DACR,
+	NAU8360_TDM_ANCL,
+	NAU8360_TDM_ANCR,
+	NAU8360_TDM_RXN,
+};
+
+enum {
+	NAU8360_TDM_AECL,
+	NAU8360_TDM_AECR,
+	NAU8360_TDM_ISNSL,
+	NAU8360_TDM_ISNSR,
+	NAU8360_TDM_VSNSL,
+	NAU8360_TDM_VSNSR,
+	NAU8360_TDM_TJ,
+	NAU8360_TDM_VBAT,
+	NAU8360_TDM_TXN,
+};
+
+/* PLL Source */
+enum {
+	NAU8360_PLL_MCLK,
+	NAU8360_PLL_BCLK,
+	NAU8360_PLL_INTERNAL,
+};
+
+struct nau8360_pll {
+	int src;
+	int input;
+	int output;
+	int msel;
+	int rsel;
+	int nsel;
+	int xsel;
+};
+
+struct nau8360 {
+	struct device *dev;
+	struct regmap *regmap;
+	struct snd_soc_dapm_context *dapm;
+	int sys_clk;
+	int alc_enable;
+	int anc_enable;
+	int aec_enable;
+	int pbtl_enable;
+	int dac_cur_enable;
+	int low_latency;
+	int vbat_microvolt;
+	bool dsp_enable;
+	bool dsp_created;
+	int dsp_fws_num;
+	const char *dsp_firmware[NAU8360_DSP_CORE_NUM];
+	int kcs_setup_size;
+	struct nau8360_pll pll;
+	int tdm_chan_len;
+};
+
+#endif /* __NAU8360_H__ */
-- 
2.25.1


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

* Re: [PATCH 2/2] ASoC: codecs: nau8360: Add support for NAU83G60 amplifier
  2026-04-24  3:39 ` [PATCH 2/2] ASoC: codecs: nau8360: Add support for NAU83G60 amplifier Neo Chang
@ 2026-04-24 15:56   ` Mark Brown
  2026-04-29  3:11     ` YLCHANG2
  0 siblings, 1 reply; 7+ messages in thread
From: Mark Brown @ 2026-04-24 15:56 UTC (permalink / raw)
  To: Neo Chang
  Cc: lgirdwood, perex, tiwai, robh, krzk+dt, linux-sound, devicetree,
	alsa-devel, neo.chang70, kchsu0, sjlin0

[-- Attachment #1: Type: text/plain, Size: 3653 bytes --]

On Fri, Apr 24, 2026 at 11:39:53AM +0800, Neo Chang wrote:

> Add support for the Nuvoton NAU83G60 audio codec. The NAU83G60 is a
> stereo 30W+30W smart amplifier with an integrated low-latency
> Advanced Audio DSP.

> +static int nau8360_peq_coeff_put(struct snd_kcontrol *kcontrol,
> +	struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct snd_soc_component *cp = snd_kcontrol_chip(kcontrol);
> +	struct soc_bytes_ext *params = (void *)kcontrol->private_value;
> +	int i, ret, reg = nau8360_peq_regaddr(kcontrol->id.name);
> +	__be16 *data;

> +	snd_soc_component_update_bits(cp, NAU8360_R9D_PEQ_CTL, NAU8360_HW1_MEM_TEST,
> +		NAU8360_HW1_MEM_TEST);
> +	for (i = 0; i < params->max / sizeof(u16); i++)
> +		snd_soc_component_write(cp, reg + i, be16_to_cpu(*(data + i)));
> +	snd_soc_component_update_bits(cp, NAU8360_R9D_PEQ_CTL, NAU8360_HW1_MEM_TEST, 0);
> +
> +	kfree(data);
> +
> +	return 0;

This should return 1 if the value was changed to generate notifications.

> +/**
> + * nau8360_set_tdm_slot - configure DAI TDM.
> + * @tx_mask: 4-bits value representing each active TX slots. Range: 0 (skip), 1~8. Ex.
> + *	bit 0-3 for left AEC output channel selection
> + *	bit 4-7 for right AEC output channel selection
> + *	bit 8-11 for left Isense output channel selection
> + *	bit 12-15 for right Isense output channel selection
> + *	bit 16-19 for left Vsense output channel selection
> + *	bit 20-23 for right Vsense output channel selection
> + *	bit 24-27 for Junction Temperature (Tj) data output channel selection
> + *	bit 28-31 for VBAT measured data output channel selection
> + * @rx_mask: Bitmask representing active RX slots. Ex.
> + *	bit 0-7 for left DAC channel source selection
> + *	bit 8-15 for right DAC channel source selection
> + *	bit 16-23 for left ANC channel source selection
> + *	bit 24-31 for right ANC channel source selection
> + *
> + * Configures a DAI for TDM operation. Only support 8 slots TDM.
> + */
> +static int nau8360_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
> +	unsigned int rx_mask, int slots, int slot_width)

That's not the way the API is supposed to work, the mask should be which
slots are active as a bitmask.  It looks like what you want here is a
series of muxes which control the routing to some AIF widgets
representing the TDM slots.

> +static const struct regmap_config nau8360_regmap_config = {
> +	.reg_bits = NAU8360_REG_ADDR_LEN,
> +	.val_bits = NAU8360_REG_DATA_LEN,
> +
> +	.max_register = NAU8360_REG_MAX,
> +	.readable_reg = nau8360_readable_reg,
> +	.writeable_reg = nau8360_writeable_reg,
> +	.volatile_reg = nau8360_volatile_reg,
> +	.reg_read = nau8360_reg_read,
> +	.reg_write = nau8360_reg_write,
> +
> +	.cache_type = REGCACHE_RBTREE,

Use REGCACHE_MAPLE unless you've got a particular reason to use
something else, it's a more modern data structure than _RBTREE and makes
choices more suited to current hardware.

> +	/* DAC gain setting 0dB by changing current cell current. */
> +	regmap_update_bits(regmap, NAU8360_R6E_DAC_CFG0, NAU8360_DAC_CUR_MASK,
> +		NAU8360_DAC_CUR_0DB);

Things like gains should normally be user visible and left at the chip
defaults, that way we're not making use case specific decisions.

> +static void nau8360_read_device_properties(struct nau8360 *nau8360)
> +{

> +	nau8360->pbtl_enable = device_property_read_bool(dev, "nuvoton,pbtl-enable");

This is missing from the binding documentation.

> +static int nau8360_i2c_probe(struct i2c_client *i2c)
> +{

> +	return snd_soc_register_component(dev, &soc_comp_dev_nau8360, &nau8360_dai, 1);
> +}

Nothing unregisters this, you should use devm_snd_soc_register_component()

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

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

* Re: [PATCH 1/2] ASoC: dt-bindings: nuvoton,nau8360: Add bindings for NAU83G60
  2026-04-24  3:39 ` [PATCH 1/2] ASoC: dt-bindings: nuvoton,nau8360: Add bindings for NAU83G60 Neo Chang
@ 2026-04-25 10:13   ` Krzysztof Kozlowski
  2026-04-29  3:03     ` YLCHANG2
  0 siblings, 1 reply; 7+ messages in thread
From: Krzysztof Kozlowski @ 2026-04-25 10:13 UTC (permalink / raw)
  To: Neo Chang
  Cc: broonie, lgirdwood, perex, tiwai, robh, krzk+dt, linux-sound,
	devicetree, alsa-devel, neo.chang70, kchsu0, sjlin0

On Fri, Apr 24, 2026 at 11:39:52AM +0800, Neo Chang wrote:
> Add device tree bindings documentation for the Nuvoton NAU83G60
> audio amplifier.
> 
> Signed-off-by: Neo Chang <YLCHANG2@nuvoton.com>

A nit, subject: drop second/last, redundant "bindings for". The
"dt-bindings" prefix is already stating that these are bindings.
See also:
https://elixir.bootlin.com/linux/v6.17-rc3/source/Documentation/devicetree/bindings/submitting-patches.rst#L18

> ---
>  .../bindings/sound/nuvoton,nau8360.yaml       | 115 ++++++++++++++++++
>  1 file changed, 115 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/sound/nuvoton,nau8360.yaml
> 
> diff --git a/Documentation/devicetree/bindings/sound/nuvoton,nau8360.yaml b/Documentation/devicetree/bindings/sound/nuvoton,nau8360.yaml
> new file mode 100644
> index 000000000000..29b10155c4f9
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/nuvoton,nau8360.yaml
> @@ -0,0 +1,115 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/sound/nuvoton,nau8360.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Nuvoton NAU83G60 Stereo Class-D Amplifier with DSP
> +
> +description: |
> +  Stereo Class-D Amplifier with DSP and I/V-sense driver.

If "driver" as in "Linux driver" then please drop the word.

> +  This device supports I2C.
> +
> +maintainers:
> +  - Neo Chang <YLCHANG2@nuvoton.com>
> +
> +properties:
> +  compatible:
> +    enum:
> +      - nuvoton,nau8360
> +
> +  reg:
> +    maxItems: 1
> +    description: |
> +      The I2C address of the device. The address is determined by the external
> +      hardware configuration of GPIO1 and GPIO2 pins:
> +       - 0x1a (GPIO2=Low, GPIO1=Low)
> +       - 0x1b (GPIO2=Low, GPIO1=High)
> +       - 0x4a (GPIO2=High, GPIO1=Low)
> +       - 0x4b (GPIO2=High, GPIO1=High)
> +
> +  "#sound-dai-cells":
> +    const: 0
> +
> +  clocks:
> +    maxItems: 1
> +
> +  clock-names:
> +    const: mclk
> +
> +  nuvoton,dsp-bypass:
> +    type: boolean
> +    description: |

Do not need '|' unless you need to preserve formatting.

> +      Forcibly disable/bypass DSP path.

Why would that be hard-coded for given board?

> +
> +  nuvoton,low-latency:
> +    type: boolean
> +    description: |
> +      Enable low latency mode.
> +      Please note the feature helps sensing performance
> +      but worsens power consumption.

I could imagine that low-latency needed for certain use-cases, like
realtime audio, thus not really board-level DT configuration, but
runtime.

> +
> +  nuvoton,anc-enable:
> +    type: boolean
> +    description: |
> +      Enable ANC (Active Noise Cancellation) feature.

This as well.

> +      NAU83G60 provides configurable low latency ANC path to Advanced DSP through TDM-RX.
> +      To reduce latency, the ANC path only supports 48 kHz sample rates.
> +
> +  nuvoton,aec-enable:
> +    type: boolean
> +    description: |
> +      Enable AEC (Acoustic Echo Cancellation) feature.

As well.

> +      NAU83G60 provides Advanced DSP processed audio data as AEC reference through TDM-TX.
> +      The AEC path only supports 48 kHz sample rates.
> +
> +  nuvoton,pbtl-enable:
> +    type: boolean
> +    description: |
> +      NAU83G60 supports PBTL mode for mono output.
> +
> +  nuvoton,vbat-microvolt:
> +    minimum: 8000000 # 8V
> +    maximum: 24000000 # 24V
> +    description: |
> +      VBAT supply voltage in microvolts.

This device does not have any supply, so how could you set its voltage?
Plus, regulator bindings define it, not per-device propeties. Drop the property.

Also, add missing supply.

> +      This is the analog power supply, provided by an external power source
> +      or battery, and must be between 8V and 24V.
> +
> +  nuvoton,tdm-channel-length:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    enum: [16, 24, 32]
> +    description: |
> +      Assign TDM channel length.
> +      The length must be 16, 24, or 32.
> +
> +  nuvoton,dsp-fw-names:
> +    $ref: /schemas/types.yaml#/definitions/string-array
> +    minItems: 2
> +    maxItems: 2
> +    description: |
> +      Assign firmware filenames for left and right DSP cores.

Please use standard properties. This is the second one you re-implement.
If there is one single thing which upstream kernel hates is the
reimplementation of standard things by each vendor.

firmware-name:
(and see other examples how to write it - items: with descriptions)

> +
> +required:
> +  - compatible
> +  - reg
> +
> +allOf:
> +  - $ref: dai-common.yaml#
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    i2c {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        codec@1a {
> +            #sound-dai-cells = <0>;
> +            compatible = "nuvoton,nau8360";
> +            reg = <0x1a>;
> +            nuvoton,dsp-bypass;
> +            nuvoton,vbat-microvolt = <12000000>;

Add the rest of properties o make this complete.

Best regards,
Krzysztof


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

* Re: [PATCH 1/2] ASoC: dt-bindings: nuvoton,nau8360: Add bindings for NAU83G60
  2026-04-25 10:13   ` Krzysztof Kozlowski
@ 2026-04-29  3:03     ` YLCHANG2
  0 siblings, 0 replies; 7+ messages in thread
From: YLCHANG2 @ 2026-04-29  3:03 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Neo Chang
  Cc: broonie, lgirdwood, perex, tiwai, robh, krzk+dt, linux-sound,
	devicetree, alsa-devel, kchsu0, sjlin0


On 4/25/26 18:13, Krzysztof Kozlowski wrote:
> On Fri, Apr 24, 2026 at 11:39:52AM +0800, Neo Chang wrote:
>> Add device tree bindings documentation for the Nuvoton NAU83G60
>> audio amplifier.
>>
>> Signed-off-by: Neo Chang <YLCHANG2@nuvoton.com>
> A nit, subject: drop second/last, redundant "bindings for". The
> "dt-bindings" prefix is already stating that these are bindings.
> See also:
> https://elixir.bootlin.com/linux/v6.17-rc3/source/Documentation/devicetree/bindings/submitting-patches.rst#L18
Got it. I will fix the commit subject in v2.
>
>> ---
>>   .../bindings/sound/nuvoton,nau8360.yaml       | 115 ++++++++++++++++++
>>   1 file changed, 115 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/sound/nuvoton,nau8360.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/sound/nuvoton,nau8360.yaml b/Documentation/devicetree/bindings/sound/nuvoton,nau8360.yaml
>> new file mode 100644
>> index 000000000000..29b10155c4f9
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/sound/nuvoton,nau8360.yaml
>> @@ -0,0 +1,115 @@
>> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/sound/nuvoton,nau8360.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Nuvoton NAU83G60 Stereo Class-D Amplifier with DSP
>> +
>> +description: |
>> +  Stereo Class-D Amplifier with DSP and I/V-sense driver.
> If "driver" as in "Linux driver" then please drop the word.
Got it. I will drop the word 'driver' in v2.
>
>> +  This device supports I2C.
>> +
>> +maintainers:
>> +  - Neo Chang <YLCHANG2@nuvoton.com>
>> +
>> +properties:
>> +  compatible:
>> +    enum:
>> +      - nuvoton,nau8360
>> +
>> +  reg:
>> +    maxItems: 1
>> +    description: |
>> +      The I2C address of the device. The address is determined by the external
>> +      hardware configuration of GPIO1 and GPIO2 pins:
>> +       - 0x1a (GPIO2=Low, GPIO1=Low)
>> +       - 0x1b (GPIO2=Low, GPIO1=High)
>> +       - 0x4a (GPIO2=High, GPIO1=Low)
>> +       - 0x4b (GPIO2=High, GPIO1=High)
>> +
>> +  "#sound-dai-cells":
>> +    const: 0
>> +
>> +  clocks:
>> +    maxItems: 1
>> +
>> +  clock-names:
>> +    const: mclk
>> +
>> +  nuvoton,dsp-bypass:
>> +    type: boolean
>> +    description: |
> Do not need '|' unless you need to preserve formatting.
Got it. I will drop the '|' in v2."
>
>> +      Forcibly disable/bypass DSP path.
> Why would that be hard-coded for given board?
Our intention was to use this property only to set the initial DSP 
enable/disable state during probe,
while still exposing an ALSA kcontrol so userspace can change it 
dynamically later.
>
>> +
>> +  nuvoton,low-latency:
>> +    type: boolean
>> +    description: |
>> +      Enable low latency mode.
>> +      Please note the feature helps sensing performance
>> +      but worsens power consumption.
> I could imagine that low-latency needed for certain use-cases, like
> realtime audio, thus not really board-level DT configuration, but
> runtime.
I will drop these properties from the v2 DT bindings.
>
>> +
>> +  nuvoton,anc-enable:
>> +    type: boolean
>> +    description: |
>> +      Enable ANC (Active Noise Cancellation) feature.
> This as well.
I will drop these properties from the v2 DT bindings and expose them as 
ALSA kcontrols instead.
>
>> +      NAU83G60 provides configurable low latency ANC path to Advanced DSP through TDM-RX.
>> +      To reduce latency, the ANC path only supports 48 kHz sample rates.
>> +
>> +  nuvoton,aec-enable:
>> +    type: boolean
>> +    description: |
>> +      Enable AEC (Acoustic Echo Cancellation) feature.
> As well.
I will drop these properties from the v2 DT bindings and expose them as 
ALSA kcontrols instead.
>
>> +      NAU83G60 provides Advanced DSP processed audio data as AEC reference through TDM-TX.
>> +      The AEC path only supports 48 kHz sample rates.
>> +
>> +  nuvoton,pbtl-enable:
>> +    type: boolean
>> +    description: |
>> +      NAU83G60 supports PBTL mode for mono output.
>> +
>> +  nuvoton,vbat-microvolt:
>> +    minimum: 8000000 # 8V
>> +    maximum: 24000000 # 24V
>> +    description: |
>> +      VBAT supply voltage in microvolts.
> This device does not have any supply, so how could you set its voltage?
> Plus, regulator bindings define it, not per-device propeties. Drop the property.
>
> Also, add missing supply.
Sorry, I misunderstood the standard regulator framework.
I will drop the nuvoton,vbat-microvolt custom property in v2.
Instead, I will add the standard vbat-supply property to the binding.
>
>> +      This is the analog power supply, provided by an external power source
>> +      or battery, and must be between 8V and 24V.
>> +
>> +  nuvoton,tdm-channel-length:
>> +    $ref: /schemas/types.yaml#/definitions/uint32
>> +    enum: [16, 24, 32]
>> +    description: |
>> +      Assign TDM channel length.
>> +      The length must be 16, 24, or 32.
>> +
>> +  nuvoton,dsp-fw-names:
>> +    $ref: /schemas/types.yaml#/definitions/string-array
>> +    minItems: 2
>> +    maxItems: 2
>> +    description: |
>> +      Assign firmware filenames for left and right DSP cores.
> Please use standard properties. This is the second one you re-implement.
> If there is one single thing which upstream kernel hates is the
> reimplementation of standard things by each vendor.
>
> firmware-name:
> (and see other examples how to write it - items: with descriptions)
Got it. I will use the standard firmware-name and dai-tdm-slot-width 
property instead and drop the vendor-specific one.
>
>> +
>> +required:
>> +  - compatible
>> +  - reg
>> +
>> +allOf:
>> +  - $ref: dai-common.yaml#
>> +
>> +unevaluatedProperties: false
>> +
>> +examples:
>> +  - |
>> +    i2c {
>> +        #address-cells = <1>;
>> +        #size-cells = <0>;
>> +
>> +        codec@1a {
>> +            #sound-dai-cells = <0>;
>> +            compatible = "nuvoton,nau8360";
>> +            reg = <0x1a>;
>> +            nuvoton,dsp-bypass;
>> +            nuvoton,vbat-microvolt = <12000000>;
> Add the rest of properties o make this complete.
Got it. I will complete the rest in v2
>
> Best regards,
> Krzysztof
>

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

* Re: [PATCH 2/2] ASoC: codecs: nau8360: Add support for NAU83G60 amplifier
  2026-04-24 15:56   ` Mark Brown
@ 2026-04-29  3:11     ` YLCHANG2
  0 siblings, 0 replies; 7+ messages in thread
From: YLCHANG2 @ 2026-04-29  3:11 UTC (permalink / raw)
  To: Mark Brown, Neo Chang
  Cc: lgirdwood, perex, tiwai, robh, krzk+dt, linux-sound, devicetree,
	alsa-devel, kchsu0, sjlin0


On 4/24/26 23:56, Mark Brown wrote:
> On Fri, Apr 24, 2026 at 11:39:53AM +0800, Neo Chang wrote:
>
>> Add support for the Nuvoton NAU83G60 audio codec. The NAU83G60 is a
>> stereo 30W+30W smart amplifier with an integrated low-latency
>> Advanced Audio DSP.
>> +static int nau8360_peq_coeff_put(struct snd_kcontrol *kcontrol,
>> +	struct snd_ctl_elem_value *ucontrol)
>> +{
>> +	struct snd_soc_component *cp = snd_kcontrol_chip(kcontrol);
>> +	struct soc_bytes_ext *params = (void *)kcontrol->private_value;
>> +	int i, ret, reg = nau8360_peq_regaddr(kcontrol->id.name);
>> +	__be16 *data;
>> +	snd_soc_component_update_bits(cp, NAU8360_R9D_PEQ_CTL, NAU8360_HW1_MEM_TEST,
>> +		NAU8360_HW1_MEM_TEST);
>> +	for (i = 0; i < params->max / sizeof(u16); i++)
>> +		snd_soc_component_write(cp, reg + i, be16_to_cpu(*(data + i)));
>> +	snd_soc_component_update_bits(cp, NAU8360_R9D_PEQ_CTL, NAU8360_HW1_MEM_TEST, 0);
>> +
>> +	kfree(data);
>> +
>> +	return 0;
> This should return 1 if the value was changed to generate notifications.
I will modify this and returns 1 when the value actually changes in v2..
>
>> +/**
>> + * nau8360_set_tdm_slot - configure DAI TDM.
>> + * @tx_mask: 4-bits value representing each active TX slots. Range: 0 (skip), 1~8. Ex.
>> + *	bit 0-3 for left AEC output channel selection
>> + *	bit 4-7 for right AEC output channel selection
>> + *	bit 8-11 for left Isense output channel selection
>> + *	bit 12-15 for right Isense output channel selection
>> + *	bit 16-19 for left Vsense output channel selection
>> + *	bit 20-23 for right Vsense output channel selection
>> + *	bit 24-27 for Junction Temperature (Tj) data output channel selection
>> + *	bit 28-31 for VBAT measured data output channel selection
>> + * @rx_mask: Bitmask representing active RX slots. Ex.
>> + *	bit 0-7 for left DAC channel source selection
>> + *	bit 8-15 for right DAC channel source selection
>> + *	bit 16-23 for left ANC channel source selection
>> + *	bit 24-31 for right ANC channel source selection
>> + *
>> + * Configures a DAI for TDM operation. Only support 8 slots TDM.
>> + */
>> +static int nau8360_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
>> +	unsigned int rx_mask, int slots, int slot_width)
> That's not the way the API is supposed to work, the mask should be which
> slots are active as a bitmask.  It looks like what you want here is a
> series of muxes which control the routing to some AIF widgets
> representing the TDM slots.
Got it. I will rework set_tdm_slot() so tx_mask and rx_mask are used 
only as active slot bitmasks.
The default routing will be TX slots for AEC and RX slots for DAC.
Slot-to-function assignment will be handled through ALSA kcontrols instead.
>
>> +static const struct regmap_config nau8360_regmap_config = {
>> +	.reg_bits = NAU8360_REG_ADDR_LEN,
>> +	.val_bits = NAU8360_REG_DATA_LEN,
>> +
>> +	.max_register = NAU8360_REG_MAX,
>> +	.readable_reg = nau8360_readable_reg,
>> +	.writeable_reg = nau8360_writeable_reg,
>> +	.volatile_reg = nau8360_volatile_reg,
>> +	.reg_read = nau8360_reg_read,
>> +	.reg_write = nau8360_reg_write,
>> +
>> +	.cache_type = REGCACHE_RBTREE,
> Use REGCACHE_MAPLE unless you've got a particular reason to use
> something else, it's a more modern data structure than _RBTREE and makes
> choices more suited to current hardware.
I will change this to REGCACHE_MAPLE in v2.
>
>> +	/* DAC gain setting 0dB by changing current cell current. */
>> +	regmap_update_bits(regmap, NAU8360_R6E_DAC_CFG0, NAU8360_DAC_CUR_MASK,
>> +		NAU8360_DAC_CUR_0DB);
> Things like gains should normally be user visible and left at the chip
> defaults, that way we're not making use case specific decisions.
Agreed. I will remove this setting and leave it at the default in v2.
>
>> +static void nau8360_read_device_properties(struct nau8360 *nau8360)
>> +{
>> +	nau8360->pbtl_enable = device_property_read_bool(dev, "nuvoton,pbtl-enable");
> This is missing from the binding documentation.
I double-checked the patch, and it seems this property is already 
documented in the YAML file.
>
>> +static int nau8360_i2c_probe(struct i2c_client *i2c)
>> +{
>> +	return snd_soc_register_component(dev, &soc_comp_dev_nau8360, &nau8360_dai, 1);
>> +}
> Nothing unregisters this, you should use devm_snd_soc_register_component()
Got it. I will switch to devm_snd_soc_register_component() in v2.

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

end of thread, other threads:[~2026-04-29  3:11 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-24  3:39 [PATCH 0/2] ASoC: codecs: Add Nuvoton NAU83G60 audio codec driver Neo Chang
2026-04-24  3:39 ` [PATCH 1/2] ASoC: dt-bindings: nuvoton,nau8360: Add bindings for NAU83G60 Neo Chang
2026-04-25 10:13   ` Krzysztof Kozlowski
2026-04-29  3:03     ` YLCHANG2
2026-04-24  3:39 ` [PATCH 2/2] ASoC: codecs: nau8360: Add support for NAU83G60 amplifier Neo Chang
2026-04-24 15:56   ` Mark Brown
2026-04-29  3:11     ` YLCHANG2

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