* [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 *)®_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, ®map_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