* [PATCH 0/2] ASoC: Add driver for Iron Device MA5311
@ 2026-10-15 6:04 Hyunuk Son
2026-10-15 6:04 ` [PATCH 1/2] ASoC: dt-bindings: Add " Hyunuk Son
2026-10-15 6:04 ` [PATCH 2/2] ASoC: codecs: Add driver for " Hyunuk Son
0 siblings, 2 replies; 5+ messages in thread
From: Hyunuk Son @ 2026-10-15 6:04 UTC (permalink / raw)
To: broonie, lgirdwood
Cc: robh, krzk+dt, conor+dt, perex, tiwai, kiseok.jo, linux-sound,
devicetree, linux-kernel, Hyunuk Son
This patch series adds support for the Iron Device MA5311
audio codec to the Linux ALSA SoC framework.
The MA5311 provides audio playback and capture functionality
through I2S and TDM interfaces.
The codec driver implements ALSA mixer controls for audio
configuration and uses regmap for register access.
The driver has been tested on an NVIDIA Jetson Orin Nano
platform using a 48 kHz sample rate, S32_LE format,
and 8-channel TDM playback and capture mode.
Hyunuk Son (2):
ASoC: dt-bindings: Add Iron Device MA5311
ASoC: codecs: Add driver for Iron Device MA5311
.../bindings/sound/irondevice,ma5311.yaml | 69 +
MAINTAINERS | 2 +
sound/soc/codecs/Kconfig | 9 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/ma5311.c | 2189 +++++++++++++++++
sound/soc/codecs/ma5311.h | 520 ++++
6 files changed, 2791 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/irondevice,ma5311.yaml
create mode 100644 sound/soc/codecs/ma5311.c
create mode 100644 sound/soc/codecs/ma5311.h
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/2] ASoC: dt-bindings: Add Iron Device MA5311
2026-10-15 6:04 [PATCH 0/2] ASoC: Add driver for Iron Device MA5311 Hyunuk Son
@ 2026-10-15 6:04 ` Hyunuk Son
2026-10-15 6:09 ` sashiko-bot
2026-10-15 6:04 ` [PATCH 2/2] ASoC: codecs: Add driver for " Hyunuk Son
1 sibling, 1 reply; 5+ messages in thread
From: Hyunuk Son @ 2026-10-15 6:04 UTC (permalink / raw)
To: broonie, lgirdwood
Cc: robh, krzk+dt, conor+dt, perex, tiwai, kiseok.jo, linux-sound,
devicetree, linux-kernel, Hyunuk Son
Add a Device Tree binding for the Iron Device MA5311
audio codec.
The binding describes the properties required to identify
the codec and configure its connections to the host audio
controller.
Signed-off-by: Hyunuk Son <hyunuk.son@irondevice.com>
---
.../bindings/sound/irondevice,ma5311.yaml | 69 +++++++++++++++++++
1 file changed, 69 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/irondevice,ma5311.yaml
diff --git a/Documentation/devicetree/bindings/sound/irondevice,ma5311.yaml b/Documentation/devicetree/bindings/sound/irondevice,ma5311.yaml
new file mode 100644
index 000000000..ae4a13c29
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/irondevice,ma5311.yaml
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/irondevice,ma5311.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Iron Device MA5311 Audio Codec
+
+maintainers:
+ - Hyunuk Son <hyunuk.son@irondevice.com>
+
+description:
+ The MA5311 is an audio device providing digital speaker amplification,
+ analog input conversion, and audio DSP functionality.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - irondevice,ma5311
+ description:
+ Requires two I2C Channel.
+
+ reg:
+ items:
+ - description: I2C address for control registers
+ - description: I2C address for ADC registers
+
+ reg-names:
+ items:
+ - const: control
+ - const: adc
+
+ reset-gpios:
+ maxItems: 1
+ description:
+ GPIO connected to the active-low RSTN pin.
+
+ interrupts:
+ maxItems: 1
+ description:
+ Interrupt from the active-low INTN fault and thermal warning output.
+
+ '#sound-dai-cells':
+ const: 1
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - '#sound-dai-cells'
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ audio-codec@3e {
+ compatible = "irondevice,ma5311";
+ reg = <0x3e>, <0x3f>;
+ #sound-dai-cells = <1>;
+ reg-names = "control", "adc";
+ };
+ };
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/2] ASoC: codecs: Add driver for Iron Device MA5311
2026-10-15 6:04 [PATCH 0/2] ASoC: Add driver for Iron Device MA5311 Hyunuk Son
2026-10-15 6:04 ` [PATCH 1/2] ASoC: dt-bindings: Add " Hyunuk Son
@ 2026-10-15 6:04 ` Hyunuk Son
2026-10-15 6:14 ` sashiko-bot
1 sibling, 1 reply; 5+ messages in thread
From: Hyunuk Son @ 2026-10-15 6:04 UTC (permalink / raw)
To: broonie, lgirdwood
Cc: robh, krzk+dt, conor+dt, perex, tiwai, kiseok.jo, linux-sound,
devicetree, linux-kernel, Hyunuk Son
Add an ASoC codec driver for the Iron Device MA5311.
The driver supports audio playback and capture through I2S
and TDM interfaces. It provides ALSA mixer controls for audio
configuration and uses regmap for register access.
Signed-off-by: Hyunuk Son <hyunuk.son@irondevice.com>
---
MAINTAINERS | 2 +
sound/soc/codecs/Kconfig | 9 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/ma5311.c | 2189 +++++++++++++++++++++++++++++++++++++
sound/soc/codecs/ma5311.h | 520 +++++++++
5 files changed, 2722 insertions(+)
create mode 100644 sound/soc/codecs/ma5311.c
create mode 100644 sound/soc/codecs/ma5311.h
diff --git a/MAINTAINERS b/MAINTAINERS
index c59a38f66..7ce0dfd5c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13812,9 +13812,11 @@ F: net/netfilter/ipvs/
IRON DEVICE AUDIO CODEC DRIVERS
M: Kiseok Jo <kiseok.jo@irondevice.com>
+M: Hyunuk Son <hyunuk.son@irondevice.com>
L: linux-sound@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/sound/irondevice,*
+F: sound/soc/codecs/ma5311.*
F: sound/soc/codecs/sma*
IRQ DOMAINS (IRQ NUMBER MAPPING LIBRARY)
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index ba59dabee..d11530b16 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -143,6 +143,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_LM4857
imply SND_SOC_LM49453
imply SND_SOC_LOCHNAGAR_SC
+ imply SND_SOC_MA5311
imply SND_SOC_MAX98088
imply SND_SOC_MAX98090
imply SND_SOC_MAX98095
@@ -1423,6 +1424,14 @@ config SND_SOC_LOCHNAGAR_SC
This driver support the sound card functionality of the Cirrus
Logic Lochnagar audio development board.
+config SND_SOC_MA5311
+ tristate "Iron Device MA5311 Audio Codec"
+ depends on I2C && GPIOLIB
+ select REGMAP_I2C
+ help
+ Enable support for the Iron Device MA5311 audio codec with
+ playback and capture over an I2S/TDM interface.
+
config SND_SOC_MADERA
tristate
select SND_SOC_WM_ADSP
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 3826f9e4f..0b774afbe 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -165,6 +165,7 @@ snd-soc-lpass-rx-macro-y := lpass-rx-macro.o
snd-soc-lpass-tx-macro-y := lpass-tx-macro.o
snd-soc-lpass-wsa-macro-y := lpass-wsa-macro.o
snd-soc-lpass-va-macro-y := lpass-va-macro.o
+snd-soc-ma5311-y := ma5311.o
snd-soc-madera-y := madera.o
snd-soc-max9759-y := max9759.o
snd-soc-max9768-y := max9768.o
@@ -608,6 +609,7 @@ obj-$(CONFIG_SND_SOC_JZ4770_CODEC) += snd-soc-jz4770-codec.o
obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o
obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o
obj-$(CONFIG_SND_SOC_LOCHNAGAR_SC) += snd-soc-lochnagar-sc.o
+obj-$(CONFIG_SND_SOC_MA5311) += snd-soc-ma5311.o
obj-$(CONFIG_SND_SOC_MADERA) += snd-soc-madera.o
obj-$(CONFIG_SND_SOC_MAX9759) += snd-soc-max9759.o
obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o
diff --git a/sound/soc/codecs/ma5311.c b/sound/soc/codecs/ma5311.c
new file mode 100644
index 000000000..ea6d6793b
--- /dev/null
+++ b/sound/soc/codecs/ma5311.c
@@ -0,0 +1,2189 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// ma5311.c -- ma5311 ALSA SoC Audio driver
+//
+// Copyright 2026 Iron Device Corporation
+//
+// Author: Gyuhwa Park <gyuwha.park@irondevice.com>
+// Author: Hyunuk Son <hyunuk.son@irondevice.com>
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "ma5311.h"
+
+/* Board-selected delays; RSTN timing has not been hardware-validated. */
+#define MA5311_RESET_ASSERT_MS 50
+#define MA5311_RESET_RECOVERY_MS 50
+
+#define MA5311_PLL_MATCH(_input_clk, _post_n, _n, _f1, _f2, _f3_p_cp) \
+ { \
+ .input_clk = (_input_clk), \
+ .post_n = (_post_n), \
+ .n = (_n), \
+ .f1 = (_f1), \
+ .f2 = (_f2), \
+ .f3_p_cp = (_f3_p_cp), \
+ }
+
+enum ma5311_playback_mode {
+ I2S,
+ TDM01,
+ TDM23,
+ TDM45,
+ TDM67,
+};
+
+enum ma5311_capture_mode {
+ I2S_ADC1,
+ I2S_ADC2,
+ I2S_ADC3,
+ I2S_ADC4,
+ TDM_8CH,
+};
+
+struct ma5311_pll_match {
+ unsigned int input_clk;
+ unsigned int post_n;
+ unsigned int n;
+ unsigned int f1;
+ unsigned int f2;
+ unsigned int f3_p_cp;
+};
+
+struct ma5311_priv {
+ bool amp_power_status;
+ bool adc_power_status;
+ bool force_mute_status;
+ enum ma5311_mode amp_mode;
+ unsigned int dapm_aif_in;
+ unsigned int num_of_pll_matches;
+ struct device *dev;
+ struct regmap *ctrl_regmap;
+ struct i2c_client *adc_client;
+ struct regmap *adc_regmap;
+ struct gpio_desc *reset_gpio;
+ const struct ma5311_pll_match *pll_matches;
+ unsigned int format;
+ unsigned int frame_size;
+ unsigned int last_bclk;
+ unsigned int sys_clk_id;
+ unsigned int adc1_vol_left;
+ unsigned int adc1_vol_right;
+ unsigned int adc2_vol_left;
+ unsigned int adc2_vol_right;
+ unsigned int adc3_vol_left;
+ unsigned int adc3_vol_right;
+ unsigned int adc4_vol_left;
+ unsigned int adc4_vol_right;
+ unsigned int adc1_pga_left;
+ unsigned int adc1_pga_right;
+ unsigned int adc2_pga_left;
+ unsigned int adc2_pga_right;
+ unsigned int adc3_pga_left;
+ unsigned int adc3_pga_right;
+ unsigned int adc4_pga_left;
+ unsigned int adc4_pga_right;
+
+ enum ma5311_playback_mode playback_mode;
+ enum ma5311_capture_mode capture_mode;
+};
+
+static const struct ma5311_pll_match ma5311_pll_matches[] = {
+ /* Input clock in Hz; register values produce a 24.576 MHz PLL clock. */
+ MA5311_PLL_MATCH(1536000, 0x07, 0xE0, 0x00, 0x00, 0x03),
+ MA5311_PLL_MATCH(3072000, 0x07, 0x70, 0x00, 0x00, 0x03),
+ MA5311_PLL_MATCH(6144000, 0x07, 0x70, 0x00, 0x00, 0x07),
+ MA5311_PLL_MATCH(12288000, 0x07, 0x70, 0x00, 0x00, 0x0B),
+};
+
+static const struct reg_default ma5311_ctrl_reg_def[] = {
+ { 0x00, 0x88 },
+ { 0x01, 0x00 },
+ { 0x02, 0x00 },
+ { 0x03, 0x01 },
+ { 0x04, 0x17 },
+ { 0x05, 0x01 },
+ { 0x09, 0x00 },
+ { 0x0A, 0x30 },
+ { 0x0B, 0x30 },
+ { 0x0E, 0xAF },
+ { 0x0F, 0x0F },
+ { 0x10, 0x0C },
+ { 0x11, 0x00 },
+ { 0x12, 0x01 },
+ { 0x14, 0x5B },
+ { 0x15, 0x01 },
+ { 0x16, 0x0F },
+ { 0x17, 0x0F },
+ { 0x18, 0x0F },
+ { 0x19, 0x00 },
+ { 0x1A, 0x00 },
+ { 0x1B, 0x00 },
+ { 0x23, 0x01 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x00 },
+ { 0x2B, 0x07 },
+ { 0x2C, 0x0C },
+ { 0x2D, 0x0C },
+ { 0x2E, 0x0C },
+ { 0x2F, 0x0C },
+ { 0x30, 0x0C },
+ { 0x33, 0x00 },
+ { 0x34, 0x00 },
+ { 0x35, 0x00 },
+ { 0x36, 0x92 },
+ { 0x37, 0x40 },
+ { 0x38, 0xFF },
+ { 0x39, 0x00 },
+ { 0x3A, 0x00 },
+ { 0x3B, 0x00 },
+ { 0x3C, 0x00 },
+ { 0x3D, 0x00 },
+ { 0x40, 0x00 },
+ { 0x41, 0x00 },
+ { 0x42, 0x00 },
+ { 0x43, 0x00 },
+ { 0x44, 0x00 },
+ { 0x45, 0x00 },
+ { 0x46, 0x20 },
+ { 0x47, 0x00 },
+ { 0x48, 0x00 },
+ { 0x49, 0x00 },
+ { 0x4A, 0x00 },
+ { 0x4B, 0x00 },
+ { 0x4C, 0x00 },
+ { 0x4D, 0x00 },
+ { 0x4E, 0x00 },
+ { 0x4F, 0x00 },
+ { 0x50, 0x00 },
+ { 0x51, 0x00 },
+ { 0x52, 0x00 },
+ { 0x53, 0x00 },
+ { 0x54, 0x00 },
+ { 0x55, 0x20 },
+ { 0x56, 0x00 },
+ { 0x57, 0x00 },
+ { 0x58, 0x00 },
+ { 0x59, 0x00 },
+ { 0x5A, 0x00 },
+ { 0x5B, 0x00 },
+ { 0x5C, 0x00 },
+ { 0x5D, 0x00 },
+ { 0x5E, 0x00 },
+ { 0x5F, 0x00 },
+ { 0x60, 0x00 },
+ { 0x61, 0x00 },
+ { 0x62, 0x00 },
+ { 0x63, 0x00 },
+ { 0x64, 0x20 },
+ { 0x65, 0x00 },
+ { 0x66, 0x00 },
+ { 0x67, 0x00 },
+ { 0x68, 0x00 },
+ { 0x69, 0x00 },
+ { 0x6A, 0x00 },
+ { 0x6B, 0x00 },
+ { 0x6C, 0x00 },
+ { 0x6D, 0x00 },
+ { 0x6E, 0x00 },
+ { 0x6F, 0x00 },
+ { 0x70, 0x00 },
+ { 0x71, 0x00 },
+ { 0x72, 0x00 },
+ { 0x73, 0x20 },
+ { 0x74, 0x00 },
+ { 0x75, 0x00 },
+ { 0x76, 0x00 },
+ { 0x77, 0x00 },
+ { 0x78, 0x00 },
+ { 0x79, 0x00 },
+ { 0x7A, 0x00 },
+ { 0x7B, 0x00 },
+ { 0x7C, 0x00 },
+ { 0x7D, 0x00 },
+ { 0x7E, 0x00 },
+ { 0x7F, 0x00 },
+ { 0x80, 0x00 },
+ { 0x81, 0x00 },
+ { 0x82, 0x20 },
+ { 0x83, 0x00 },
+ { 0x84, 0x00 },
+ { 0x85, 0x00 },
+ { 0x86, 0x00 },
+ { 0x87, 0x00 },
+ { 0x88, 0x00 },
+ { 0x89, 0x00 },
+ { 0x91, 0x00 },
+ { 0x8A, 0x00 },
+ { 0x8B, 0x07 },
+ { 0x8C, 0x70 },
+ { 0x8D, 0x00 },
+ { 0x8E, 0x00 },
+ { 0x8F, 0x02 },
+ { 0xA1, 0x40 },
+ { 0xA2, 0x08 },
+ { 0xA3, 0x20 },
+ { 0xA4, 0x00 },
+ { 0xA5, 0x1A },
+ { 0xA6, 0x52 },
+ { 0xA7, 0x00 },
+ { 0xA8, 0x28 },
+ { 0xA9, 0x25 },
+ { 0xAA, 0x44 },
+ { 0xAB, 0x62 },
+ { 0xAC, 0x04 },
+ { 0xAD, 0x00 },
+ { 0xAE, 0x00 },
+ { 0xAF, 0x00 },
+ { 0xB0, 0x00 },
+ { 0xB1, 0x00 },
+ { 0xB2, 0x00 },
+ { 0xB3, 0x04 },
+ { 0xB4, 0x00 },
+ { 0xB5, 0x00 },
+ { 0xB7, 0x20 },
+ { 0xB8, 0x7C },
+ { 0xB9, 0x0B },
+ { 0xBB, 0x42 },
+ { 0xBC, 0x42 },
+ { 0xBD, 0x61 },
+ { 0xBE, 0x67 },
+ { 0xBF, 0x02 },
+};
+
+static const struct reg_default ma5311_adc_reg_def[] = {
+ { 0x00, 0xA5 },
+ { 0x01, 0x6C },
+ { 0x02, 0x07 },
+ { 0x03, 0x00 },
+ { 0x04, 0xFF },
+ { 0x05, 0xFF },
+ { 0x06, 0x00 },
+ { 0x07, 0xC0 },
+ { 0x08, 0xC0 },
+ { 0x09, 0x10 },
+ { 0x0A, 0x10 },
+ { 0x0B, 0xFF },
+ { 0x0C, 0x6C },
+ { 0x0D, 0x6C },
+ { 0x0E, 0x00 },
+ { 0x0F, 0x02 },
+ { 0x10, 0xA5 },
+ { 0x11, 0x6C },
+ { 0x12, 0x07 },
+ { 0x13, 0x00 },
+ { 0x14, 0xFF },
+ { 0x15, 0xFF },
+ { 0x16, 0x00 },
+ { 0x17, 0xC0 },
+ { 0x18, 0xC0 },
+ { 0x19, 0x10 },
+ { 0x1A, 0x10 },
+ { 0x1B, 0xFF },
+ { 0x1C, 0x6C },
+ { 0x1D, 0x6C },
+ { 0x1E, 0x00 },
+ { 0x1F, 0x02 },
+ { 0x20, 0xA5 },
+ { 0x21, 0x6C },
+ { 0x22, 0x07 },
+ { 0x23, 0x00 },
+ { 0x24, 0xFF },
+ { 0x25, 0xFF },
+ { 0x26, 0x00 },
+ { 0x27, 0xC0 },
+ { 0x28, 0xC0 },
+ { 0x29, 0x10 },
+ { 0x2A, 0x10 },
+ { 0x2B, 0xFF },
+ { 0x2C, 0x6C },
+ { 0x2D, 0x6C },
+ { 0x2E, 0x00 },
+ { 0x2F, 0x02 },
+ { 0x30, 0xA5 },
+ { 0x31, 0x6C },
+ { 0x32, 0x07 },
+ { 0x33, 0x00 },
+ { 0x34, 0xFF },
+ { 0x35, 0xFF },
+ { 0x36, 0x00 },
+ { 0x37, 0xC0 },
+ { 0x38, 0xC0 },
+ { 0x39, 0x10 },
+ { 0x3A, 0x10 },
+ { 0x3B, 0xFF },
+ { 0x3C, 0x6C },
+ { 0x3D, 0x6C },
+ { 0x3E, 0x00 },
+ { 0x3F, 0x02 },
+};
+
+static bool ma5311_ctrl_readable_register(struct device *dev, unsigned int reg)
+{
+ if (reg > MA5311_DAC_FF_VERSION)
+ return false;
+
+ switch (reg) {
+ case MA5311_DAC_00_SYSTEM_CTRL1 ... MA5311_DAC_05_ADC_DAC_MIX2:
+ case MA5311_DAC_09_OUTPUT_CTRL ... MA5311_DAC_0B_SPK_VOL_R:
+ case MA5311_DAC_0E_MUTE_VOL_CTRL ... MA5311_DAC_12_SYSTEM_CTRL5:
+ case MA5311_DAC_14_MODULATOR2 ... MA5311_DAC_1B_BASS_SPK7:
+ case MA5311_DAC_23_COMP_LIM1 ... MA5311_DAC_26_COMP_LIM4:
+ case MA5311_DAC_2B_EQ_MODE ... MA5311_DAC_30_EQ_GRAPHIC5:
+ case MA5311_DAC_33_FLL_CTRL3 ... MA5311_DAC_3D_TEST3:
+ case MA5311_DAC_40_EQ_CTRL1 ... MA5311_DAC_8F_PLL_F3_P_CP:
+ case MA5311_DAC_91_ADC_DAC_MIX:
+ case MA5311_DAC_A1_TOP_MAN1_FOR_SDO ... MA5311_DAC_B5_MCLKO_TEST:
+ case MA5311_DAC_B7_TOP_MAN4 ... MA5311_DAC_B9_PLL_A_SETTING:
+ case MA5311_DAC_BB_CLASS_G_LEVEL1 ... MA5311_DAC_BF_POWER_METER:
+ case MA5311_DAC_EA_DAC_STATUS ... MA5311_DAC_EC_DAC_SCP_STATUS:
+ case MA5311_DAC_FB_STATUS2 ... MA5311_DAC_FD_POWER_METER_DISPLAY:
+ case MA5311_DAC_FF_VERSION:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static bool ma5311_ctrl_writeable_register(struct device *dev, unsigned int reg)
+{
+ if (reg > MA5311_DAC_FF_VERSION)
+ return false;
+
+ switch (reg) {
+ case MA5311_DAC_00_SYSTEM_CTRL1 ... MA5311_DAC_05_ADC_DAC_MIX2:
+ case MA5311_DAC_09_OUTPUT_CTRL ... MA5311_DAC_0B_SPK_VOL_R:
+ case MA5311_DAC_0E_MUTE_VOL_CTRL ... MA5311_DAC_12_SYSTEM_CTRL5:
+ case MA5311_DAC_14_MODULATOR2 ... MA5311_DAC_1B_BASS_SPK7:
+ case MA5311_DAC_23_COMP_LIM1 ... MA5311_DAC_26_COMP_LIM4:
+ case MA5311_DAC_2B_EQ_MODE ... MA5311_DAC_30_EQ_GRAPHIC5:
+ case MA5311_DAC_33_FLL_CTRL3 ... MA5311_DAC_3D_TEST3:
+ case MA5311_DAC_40_EQ_CTRL1 ... MA5311_DAC_8F_PLL_F3_P_CP:
+ case MA5311_DAC_91_ADC_DAC_MIX:
+ case MA5311_DAC_A1_TOP_MAN1_FOR_SDO ... MA5311_DAC_B5_MCLKO_TEST:
+ case MA5311_DAC_B7_TOP_MAN4 ... MA5311_DAC_B9_PLL_A_SETTING:
+ case MA5311_DAC_BB_CLASS_G_LEVEL1 ... MA5311_DAC_BF_POWER_METER:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static bool ma5311_ctrl_volatile_register(struct device *dev, unsigned int reg)
+{
+ if (reg > MA5311_DAC_FF_VERSION)
+ return false;
+
+ switch (reg) {
+ case MA5311_DAC_EA_DAC_STATUS ... MA5311_DAC_EC_DAC_SCP_STATUS:
+ case MA5311_DAC_FB_STATUS2 ... MA5311_DAC_FD_POWER_METER_DISPLAY:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static bool ma5311_adc_readable_register(struct device *dev, unsigned int reg)
+{
+ if (reg > MA5311_ADC_CH4_3F_OUTPUT_FORMAT)
+ return false;
+
+ switch (reg) {
+ case MA5311_ADC_CH1_00_GENERAL_SETTING ... MA5311_ADC_CH4_3F_OUTPUT_FORMAT:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static bool ma5311_adc_writeable_register(struct device *dev, unsigned int reg)
+{
+ if (reg > MA5311_ADC_CH4_3F_OUTPUT_FORMAT)
+ return false;
+
+ switch (reg) {
+ case MA5311_ADC_CH1_00_GENERAL_SETTING ... MA5311_ADC_CH4_3F_OUTPUT_FORMAT:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static bool ma5311_adc_volatile_register(struct device *dev, unsigned int reg)
+{
+ return false;
+}
+
+static const DECLARE_TLV_DB_RANGE(ma5311_adc_vol_tlv,
+ 0x00, 0xef, TLV_DB_MINMAX_ITEM(-7200, 1763),
+ 0xf0, 0xff, TLV_DB_SCALE_ITEM(1763, 0, 0));
+
+static const DECLARE_TLV_DB_RANGE(ma5311_adc_pga_tlv,
+ 0x00, 0x3b, TLV_DB_MINMAX_ITEM(-1200, 3225),
+ 0x3c, 0x3f, TLV_DB_SCALE_ITEM(3225, 0, 0));
+
+static const DECLARE_TLV_DB_RANGE(ma5311_spk_tlv,
+ 0x00, 0xa8, TLV_DB_MINMAX_ITEM(-6000, 2400),
+ 0xa9, 0xff, TLV_DB_SCALE_ITEM(-6000, 0, 0));
+
+static const char *const ma5311_aif_in_source_text[] = {
+ "Mono Both", "Stereo",
+};
+
+static const char *const ma5311_playback_mode_text[] = {
+ "I2S", "TDM01", "TDM23", "TDM45", "TDM67",
+};
+
+static const char *const ma5311_capture_mode_text[] = {
+ "I2S_ADC1", "I2S_ADC2", "I2S_ADC3", "I2S_ADC4", "TDM_8CH",
+};
+
+static const char *const ma5311_reset_text[] = {
+ "idle", "reset",
+};
+
+static const struct soc_enum ma5311_reset_enum =
+SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ma5311_reset_text), ma5311_reset_text);
+
+static const struct soc_enum ma5311_capture_mode_enum =
+SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ma5311_capture_mode_text),
+ ma5311_capture_mode_text);
+
+static SOC_ENUM_SINGLE_VIRT_DECL(ma5311_aif_in_source_enum,
+ ma5311_aif_in_source_text);
+
+static const struct soc_enum ma5311_playback_mode_enum =
+SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ma5311_playback_mode_text),
+ ma5311_playback_mode_text);
+
+static int ma5311_reset(struct snd_soc_component *component)
+{
+ struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+ unsigned int status, revision, i;
+ int ret;
+
+ ret = regmap_read(ma5311->ctrl_regmap, MA5311_DAC_FF_VERSION, &status);
+
+ if (ret) {
+ dev_err(component->dev, "Register read operation failed\n");
+ return ret;
+ }
+
+ revision = status & MA5311_REV_NUM_STATUS;
+ dev_dbg(component->dev, "MA5311 revision: %u\n", revision);
+
+ /*
+ * Register initialization invalidates the previous PLL configuration.
+ */
+ ma5311->last_bclk = 0;
+
+ for (i = 0; i < ARRAY_SIZE(ma5311_ctrl_reg_def); i++) {
+ regmap_write(ma5311->ctrl_regmap,
+ ma5311_ctrl_reg_def[i].reg,
+ ma5311_ctrl_reg_def[i].def);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(ma5311_adc_reg_def); i++) {
+ regmap_write(ma5311->adc_regmap,
+ ma5311_adc_reg_def[i].reg,
+ ma5311_adc_reg_def[i].def);
+ }
+
+ ma5311->adc1_vol_left = 0xc0;
+ ma5311->adc1_vol_right = 0xc0;
+ ma5311->adc2_vol_left = 0xc0;
+ ma5311->adc2_vol_right = 0xc0;
+ ma5311->adc3_vol_left = 0xc0;
+ ma5311->adc3_vol_right = 0xc0;
+ ma5311->adc4_vol_left = 0xc0;
+ ma5311->adc4_vol_right = 0xc0;
+ ma5311->adc1_pga_left = 0x10;
+ ma5311->adc1_pga_right = 0x10;
+ ma5311->adc2_pga_left = 0x10;
+ ma5311->adc2_pga_right = 0x10;
+ ma5311->adc3_pga_left = 0x10;
+ ma5311->adc3_pga_right = 0x10;
+ ma5311->adc4_pga_left = 0x10;
+ ma5311->adc4_pga_right = 0x10;
+ ma5311->capture_mode = I2S_ADC1;
+ return 0;
+}
+
+static unsigned int *ma5311_adc_volume_control_cache(struct ma5311_priv *ma5311,
+ unsigned int reg)
+{
+ switch (reg) {
+ case MA5311_ADC_CH1_07_VOLUME_LEFT:
+ return &ma5311->adc1_vol_left;
+ case MA5311_ADC_CH1_08_VOLUME_RIGHT:
+ return &ma5311->adc1_vol_right;
+ case MA5311_ADC_CH2_17_VOLUME_LEFT:
+ return &ma5311->adc2_vol_left;
+ case MA5311_ADC_CH2_18_VOLUME_RIGHT:
+ return &ma5311->adc2_vol_right;
+ case MA5311_ADC_CH3_27_VOLUME_LEFT:
+ return &ma5311->adc3_vol_left;
+ case MA5311_ADC_CH3_28_VOLUME_RIGHT:
+ return &ma5311->adc3_vol_right;
+ case MA5311_ADC_CH4_37_VOLUME_LEFT:
+ return &ma5311->adc4_vol_left;
+ case MA5311_ADC_CH4_38_VOLUME_RIGHT:
+ return &ma5311->adc4_vol_right;
+ default:
+ return NULL;
+ }
+}
+
+static int ma5311_adc_volume_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+ const struct soc_mixer_control *mc =
+ (const struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int *cache = ma5311_adc_volume_control_cache(ma5311, mc->reg);
+ unsigned int val;
+
+ if (!cache)
+ return -EINVAL;
+
+ regmap_read(ma5311->adc_regmap, mc->reg, &val);
+
+ ucontrol->value.integer.value[0] = val & MA5311_ADC_VOL_CTRL_MASK;
+ *cache = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int ma5311_adc_volume_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+ const struct soc_mixer_control *mc =
+ (const struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int val = ucontrol->value.integer.value[0];
+ unsigned int *cache;
+
+ if (val < 0 || val > MA5311_ADC_VOL_CTRL_MASK)
+ return -EINVAL;
+
+ cache = ma5311_adc_volume_control_cache(ma5311, mc->reg);
+
+ if (!cache)
+ return -EINVAL;
+
+ if (*cache == val)
+ return 0;
+
+ regmap_update_bits(ma5311->adc_regmap, mc->reg,
+ MA5311_ADC_VOL_CTRL_MASK, val);
+
+ *cache = val;
+
+ return 1;
+}
+
+static unsigned int *ma5311_adc_pga_control_cache(struct ma5311_priv *ma5311,
+ unsigned int reg)
+{
+ switch (reg) {
+ case MA5311_ADC_CH1_09_PGA_LEFT:
+ return &ma5311->adc1_pga_left;
+ case MA5311_ADC_CH1_0A_PGA_RIGHT:
+ return &ma5311->adc1_pga_right;
+ case MA5311_ADC_CH2_19_PGA_LEFT:
+ return &ma5311->adc2_pga_left;
+ case MA5311_ADC_CH2_1A_PGA_RIGHT:
+ return &ma5311->adc2_pga_right;
+ case MA5311_ADC_CH3_29_PGA_LEFT:
+ return &ma5311->adc3_pga_left;
+ case MA5311_ADC_CH3_2A_PGA_RIGHT:
+ return &ma5311->adc3_pga_right;
+ case MA5311_ADC_CH4_39_PGA_LEFT:
+ return &ma5311->adc4_pga_left;
+ case MA5311_ADC_CH4_3A_PGA_RIGHT:
+ return &ma5311->adc4_pga_right;
+ default:
+ return NULL;
+ }
+}
+
+static int ma5311_adc_pga_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+ const struct soc_mixer_control *mc =
+ (const struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int *cache = ma5311_adc_pga_control_cache(ma5311, mc->reg);
+ unsigned int val;
+
+ if (!cache)
+ return -EINVAL;
+
+ regmap_read(ma5311->adc_regmap, mc->reg, &val);
+
+ ucontrol->value.integer.value[0] = val & MA5311_ADC_PGA_CTRL_MASK;
+ *cache = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int ma5311_adc_pga_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+ const struct soc_mixer_control *mc =
+ (const struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int *cache;
+ unsigned int val = ucontrol->value.integer.value[0];
+
+ if (val < 0 || val > MA5311_ADC_PGA_CTRL_MASK)
+ return -EINVAL;
+
+ cache = ma5311_adc_pga_control_cache(ma5311, mc->reg);
+
+ if (!cache) {
+ dev_err(ma5311->dev, "%s: Invalid Control ID - %s\n", __func__,
+ kcontrol->id.name);
+ return -EINVAL;
+ }
+
+ if (*cache == val)
+ return 0;
+
+ regmap_update_bits(ma5311->adc_regmap, mc->reg,
+ MA5311_ADC_PGA_CTRL_MASK, val);
+
+ *cache = val;
+
+ return 1;
+}
+
+static int ma5311_startup(struct snd_soc_component *component)
+{
+ struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_A2_TOP_MAN2,
+ MA5311_PLL_MASK, MA5311_PLL_ON);
+
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_00_SYSTEM_CTRL1,
+ MA5311_POWER_MASK, MA5311_POWER_ON);
+
+ if (ma5311->amp_mode == MA5311_MONO_BOTH_MODE)
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_10_SYSTEM_CTRL3,
+ MA5311_SPK_MODE_MASK, MA5311_SPK_MONO_BOTH);
+ else
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_10_SYSTEM_CTRL3,
+ MA5311_SPK_MODE_MASK, MA5311_SPK_STEREO);
+
+ ma5311->amp_power_status = true;
+ msleep(55);
+
+ return 0;
+}
+
+static int ma5311_shutdown(struct snd_soc_component *component)
+{
+ struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+
+ regmap_update_bits(ma5311->ctrl_regmap, MA5311_DAC_0E_MUTE_VOL_CTRL,
+ MA5311_DAC_MUTE_VOL_MASK, MA5311_SPK_MUTE);
+
+ msleep(55);
+
+ regmap_update_bits(ma5311->ctrl_regmap, MA5311_DAC_10_SYSTEM_CTRL3,
+ MA5311_SPK_MODE_MASK, MA5311_SPK_OFF);
+
+ ma5311->amp_power_status = false;
+
+ /* Capture shares the PLL and global power with playback. */
+ if (!ma5311->adc_power_status) {
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_A2_TOP_MAN2,
+ MA5311_PLL_MASK, MA5311_PLL_OFF);
+
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_00_SYSTEM_CTRL1,
+ MA5311_POWER_MASK, MA5311_POWER_OFF);
+ }
+
+ return 0;
+}
+
+static int ma5311_capture_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.enumerated.item[0] = ma5311->capture_mode;
+
+ return 0;
+}
+
+static int ma5311_capture_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+ unsigned int val = ucontrol->value.enumerated.item[0];
+
+ if (val >= ARRAY_SIZE(ma5311_capture_mode_text)) {
+ dev_err(ma5311->dev, "%s: Out of range\n", __func__);
+ return -EINVAL;
+ }
+
+ if (ma5311->capture_mode == val)
+ return 0;
+
+ if (ma5311->adc_power_status) {
+ dev_err(ma5311->dev, "Try after capture is complete.\n");
+ return -EBUSY;
+ }
+
+ switch (val) {
+ case I2S_ADC1:
+ regmap_write(ma5311->ctrl_regmap,
+ MA5311_DAC_39_DATA_PATH,
+ MA5311_DATA_PATH_I2S_ADC1);
+
+ break;
+ case I2S_ADC2:
+ regmap_write(ma5311->ctrl_regmap,
+ MA5311_DAC_39_DATA_PATH,
+ MA5311_DATA_PATH_I2S_ADC2);
+
+ break;
+ case I2S_ADC3:
+ regmap_write(ma5311->ctrl_regmap,
+ MA5311_DAC_39_DATA_PATH,
+ MA5311_DATA_PATH_I2S_ADC3);
+
+ break;
+ case I2S_ADC4:
+ regmap_write(ma5311->ctrl_regmap,
+ MA5311_DAC_39_DATA_PATH,
+ MA5311_DATA_PATH_I2S_ADC4);
+ break;
+
+ case TDM_8CH:
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_38_TDM2,
+ MA5311_ADC_TDM2_SETTING_MASK,
+ MA5311_DAC_38_CAPTURE_8CH_CFG);
+ regmap_write(ma5311->ctrl_regmap,
+ MA5311_DAC_39_DATA_PATH, MA5311_DATA_PATH_TDM_8CH);
+ regmap_write(ma5311->ctrl_regmap,
+ MA5311_DAC_3A_TDM_SLOT_ENABLE,
+ MA5311_TDM_SLOT_ENABLE_ALL);
+ break;
+ }
+
+ regmap_update_bits(ma5311->ctrl_regmap, MA5311_DAC_39_DATA_PATH,
+ MA5311_DAC_SDO_DATA_PATH_MASK, val << 2);
+
+ regmap_update_bits(ma5311->ctrl_regmap, MA5311_DAC_91_ADC_DAC_MIX,
+ MA5311_DAC_ADC_DAC_MIX_CH_MASK, val);
+
+ ma5311->capture_mode = val;
+ return 1;
+}
+
+static int ma5311_playback_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.enumerated.item[0] = ma5311->playback_mode;
+
+ return 0;
+}
+
+static int ma5311_playback_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+ unsigned int val = ucontrol->value.enumerated.item[0];
+
+ if (val >= ARRAY_SIZE(ma5311_playback_mode_text)) {
+ dev_err(ma5311->dev, "%s: Out of range\n", __func__);
+ return -EINVAL;
+ }
+
+ if (ma5311->playback_mode == val)
+ return 0;
+
+ switch (val) {
+ case TDM01:
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_37_TDM1,
+ MA5311_TDM_SLOT0_RX_POS_MASK,
+ MA5311_TDM_SLOT0_RX_POS_0);
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_37_TDM1,
+ MA5311_TDM_SLOT1_RX_POS_MASK,
+ MA5311_TDM_SLOT1_RX_POS_1);
+ break;
+
+ case TDM23:
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_37_TDM1,
+ MA5311_TDM_SLOT0_RX_POS_MASK,
+ MA5311_TDM_SLOT0_RX_POS_2);
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_37_TDM1,
+ MA5311_TDM_SLOT1_RX_POS_MASK,
+ MA5311_TDM_SLOT1_RX_POS_3);
+ break;
+
+ case TDM45:
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_37_TDM1,
+ MA5311_TDM_SLOT0_RX_POS_MASK,
+ MA5311_TDM_SLOT0_RX_POS_4);
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_37_TDM1,
+ MA5311_TDM_SLOT1_RX_POS_MASK,
+ MA5311_TDM_SLOT1_RX_POS_5);
+ break;
+
+ case TDM67:
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_37_TDM1,
+ MA5311_TDM_SLOT0_RX_POS_MASK,
+ MA5311_TDM_SLOT0_RX_POS_6);
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_37_TDM1,
+ MA5311_TDM_SLOT1_RX_POS_MASK,
+ MA5311_TDM_SLOT1_RX_POS_7);
+ break;
+ }
+
+ ma5311->playback_mode = val;
+
+ return 1;
+}
+
+static int ma5311_reset_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+
+ if (!ma5311->reset_gpio)
+ return -ENODEV;
+
+ /* Do not persist a reset request when ALSA saves the mixer state. */
+ ucontrol->value.enumerated.item[0] = 0;
+
+ return 0;
+}
+
+static int ma5311_reset_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+ unsigned int val = ucontrol->value.enumerated.item[0];
+ int ret;
+
+ if (!val)
+ return 0;
+
+ if (!ma5311->reset_gpio)
+ return -ENODEV;
+
+ ma5311->last_bclk = 0;
+
+ gpiod_set_value_cansleep(ma5311->reset_gpio, 1);
+ msleep(MA5311_RESET_ASSERT_MS);
+ gpiod_set_value_cansleep(ma5311->reset_gpio, 0);
+ msleep(MA5311_RESET_RECOVERY_MS);
+
+ ret = ma5311_reset(component);
+ dev_dbg(component->dev, "Hardware Reset Done\n");
+ return ret;
+}
+
+static const struct snd_kcontrol_new ma5311_snd_controls[] = {
+ SOC_ENUM_EXT(MA5311_RESET_CTRL_NAME, ma5311_reset_enum,
+ ma5311_reset_control_get, ma5311_reset_control_put),
+
+ SOC_SINGLE_TLV(MA5311_LEFT_VOL_CTRL_NAME, MA5311_DAC_0A_SPK_VOL_L, 0,
+ 168, 1, ma5311_spk_tlv),
+ SOC_SINGLE_TLV(MA5311_RIGHT_VOL_CTRL_NAME, MA5311_DAC_0B_SPK_VOL_R, 0,
+ 168, 1, ma5311_spk_tlv),
+
+ SOC_SINGLE_EXT_TLV(MA5311_ADC1_LEFT_VOL_CTRL_NAME,
+ MA5311_ADC_CH1_07_VOLUME_LEFT, 0,
+ MA5311_ADC_VOL_CTRL_MASK, 0,
+ ma5311_adc_volume_control_get,
+ ma5311_adc_volume_control_put, ma5311_adc_vol_tlv),
+
+ SOC_SINGLE_EXT_TLV(MA5311_ADC1_RIGHT_VOL_CTRL_NAME,
+ MA5311_ADC_CH1_08_VOLUME_RIGHT, 0,
+ MA5311_ADC_VOL_CTRL_MASK, 0,
+ ma5311_adc_volume_control_get,
+ ma5311_adc_volume_control_put, ma5311_adc_vol_tlv),
+
+ SOC_SINGLE_EXT_TLV(MA5311_ADC2_LEFT_VOL_CTRL_NAME,
+ MA5311_ADC_CH2_17_VOLUME_LEFT, 0,
+ MA5311_ADC_VOL_CTRL_MASK, 0,
+ ma5311_adc_volume_control_get,
+ ma5311_adc_volume_control_put, ma5311_adc_vol_tlv),
+
+ SOC_SINGLE_EXT_TLV(MA5311_ADC2_RIGHT_VOL_CTRL_NAME,
+ MA5311_ADC_CH2_18_VOLUME_RIGHT, 0,
+ MA5311_ADC_VOL_CTRL_MASK, 0,
+ ma5311_adc_volume_control_get,
+ ma5311_adc_volume_control_put, ma5311_adc_vol_tlv),
+
+ SOC_SINGLE_EXT_TLV(MA5311_ADC3_LEFT_VOL_CTRL_NAME,
+ MA5311_ADC_CH3_27_VOLUME_LEFT, 0,
+ MA5311_ADC_VOL_CTRL_MASK, 0,
+ ma5311_adc_volume_control_get,
+ ma5311_adc_volume_control_put, ma5311_adc_vol_tlv),
+
+ SOC_SINGLE_EXT_TLV(MA5311_ADC3_RIGHT_VOL_CTRL_NAME,
+ MA5311_ADC_CH3_28_VOLUME_RIGHT, 0,
+ MA5311_ADC_VOL_CTRL_MASK, 0,
+ ma5311_adc_volume_control_get,
+ ma5311_adc_volume_control_put, ma5311_adc_vol_tlv),
+
+ SOC_SINGLE_EXT_TLV(MA5311_ADC4_LEFT_VOL_CTRL_NAME,
+ MA5311_ADC_CH4_37_VOLUME_LEFT, 0,
+ MA5311_ADC_VOL_CTRL_MASK, 0,
+ ma5311_adc_volume_control_get,
+ ma5311_adc_volume_control_put, ma5311_adc_vol_tlv),
+
+ SOC_SINGLE_EXT_TLV(MA5311_ADC4_RIGHT_VOL_CTRL_NAME,
+ MA5311_ADC_CH4_38_VOLUME_RIGHT, 0,
+ MA5311_ADC_VOL_CTRL_MASK, 0,
+ ma5311_adc_volume_control_get,
+ ma5311_adc_volume_control_put, ma5311_adc_vol_tlv),
+
+ SOC_SINGLE_EXT_TLV(MA5311_ADC1_LEFT_PGA_CTRL_NAME,
+ MA5311_ADC_CH1_09_PGA_LEFT, 0,
+ MA5311_ADC_PGA_CTRL_MASK, 0,
+ ma5311_adc_pga_control_get,
+ ma5311_adc_pga_control_put, ma5311_adc_pga_tlv),
+
+ SOC_SINGLE_EXT_TLV(MA5311_ADC1_RIGHT_PGA_CTRL_NAME,
+ MA5311_ADC_CH1_0A_PGA_RIGHT, 0,
+ MA5311_ADC_PGA_CTRL_MASK, 0,
+ ma5311_adc_pga_control_get,
+ ma5311_adc_pga_control_put, ma5311_adc_pga_tlv),
+
+ SOC_SINGLE_EXT_TLV(MA5311_ADC2_LEFT_PGA_CTRL_NAME,
+ MA5311_ADC_CH2_19_PGA_LEFT, 0,
+ MA5311_ADC_PGA_CTRL_MASK, 0,
+ ma5311_adc_pga_control_get,
+ ma5311_adc_pga_control_put, ma5311_adc_pga_tlv),
+
+ SOC_SINGLE_EXT_TLV(MA5311_ADC2_RIGHT_PGA_CTRL_NAME,
+ MA5311_ADC_CH2_1A_PGA_RIGHT, 0,
+ MA5311_ADC_PGA_CTRL_MASK, 0,
+ ma5311_adc_pga_control_get,
+ ma5311_adc_pga_control_put, ma5311_adc_pga_tlv),
+
+ SOC_SINGLE_EXT_TLV(MA5311_ADC3_LEFT_PGA_CTRL_NAME,
+ MA5311_ADC_CH3_29_PGA_LEFT, 0,
+ MA5311_ADC_PGA_CTRL_MASK, 0,
+ ma5311_adc_pga_control_get,
+ ma5311_adc_pga_control_put, ma5311_adc_pga_tlv),
+
+ SOC_SINGLE_EXT_TLV(MA5311_ADC3_RIGHT_PGA_CTRL_NAME,
+ MA5311_ADC_CH3_2A_PGA_RIGHT, 0,
+ MA5311_ADC_PGA_CTRL_MASK, 0,
+ ma5311_adc_pga_control_get,
+ ma5311_adc_pga_control_put, ma5311_adc_pga_tlv),
+
+ SOC_SINGLE_EXT_TLV(MA5311_ADC4_LEFT_PGA_CTRL_NAME,
+ MA5311_ADC_CH4_39_PGA_LEFT, 0,
+ MA5311_ADC_PGA_CTRL_MASK, 0,
+ ma5311_adc_pga_control_get,
+ ma5311_adc_pga_control_put, ma5311_adc_pga_tlv),
+
+ SOC_SINGLE_EXT_TLV(MA5311_ADC4_RIGHT_PGA_CTRL_NAME,
+ MA5311_ADC_CH4_3A_PGA_RIGHT, 0,
+ MA5311_ADC_PGA_CTRL_MASK, 0,
+ ma5311_adc_pga_control_get,
+ ma5311_adc_pga_control_put, ma5311_adc_pga_tlv),
+
+ SOC_ENUM_EXT(MA5311_PLAYBACK_MODE, ma5311_playback_mode_enum,
+ ma5311_playback_mode_get, ma5311_playback_mode_put),
+ SOC_ENUM_EXT(MA5311_CAPTURE_NAME, ma5311_capture_mode_enum,
+ ma5311_capture_mode_get, ma5311_capture_mode_put),
+};
+
+static int ma5311_dapm_aif_in_get(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 ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.enumerated.item[0] = ma5311->dapm_aif_in;
+
+ return 0;
+}
+
+static int ma5311_dapm_aif_in_put(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 ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+ unsigned int val = ucontrol->value.enumerated.item[0];
+ unsigned int old_val = ma5311->dapm_aif_in;
+ int ret;
+
+ if (val >= ARRAY_SIZE(ma5311_aif_in_source_text)) {
+ dev_err(ma5311->dev, "%s: Out of range\n", __func__);
+ return -EINVAL;
+ }
+
+ if (ma5311->dapm_aif_in == val)
+ return 0;
+
+ ma5311->dapm_aif_in = val;
+
+ ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ if (ret < 0) {
+ ma5311->dapm_aif_in = old_val;
+ return ret;
+ }
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new ma5311_aif_in_source_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = MA5311_AIF_IN_NAME,
+ .info = snd_soc_info_enum_double,
+ .get = ma5311_dapm_aif_in_get,
+ .put = ma5311_dapm_aif_in_put,
+ .private_value = (unsigned long)&ma5311_aif_in_source_enum,
+};
+
+static int ma5311_aif_in_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+ unsigned int mux = ma5311->dapm_aif_in;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mux) {
+ case MA5311_MONO_BOTH_MODE:
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_10_SYSTEM_CTRL3,
+ MA5311_SPK_MODE_MASK,
+ MA5311_SPK_MONO_BOTH);
+ break;
+ case MA5311_STEREO_MODE:
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_10_SYSTEM_CTRL3,
+ MA5311_SPK_MODE_MASK,
+ MA5311_SPK_STEREO);
+ break;
+ default:
+ dev_err(ma5311->dev, "%s: Invalid value (%u)\n",
+ __func__, mux);
+ return -EINVAL;
+ }
+
+ ma5311->amp_mode = mux;
+ break;
+ }
+ return 0;
+}
+
+static int ma5311_power_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);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ return ma5311_startup(component);
+ case SND_SOC_DAPM_PRE_PMD:
+ return ma5311_shutdown(component);
+ }
+
+ return 0;
+}
+
+static int ma5311_adc_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+ unsigned int mux = ma5311->capture_mode;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(ma5311->ctrl_regmap, MA5311_DAC_A2_TOP_MAN2,
+ MA5311_PLL_MASK, MA5311_PLL_ON);
+
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_00_SYSTEM_CTRL1,
+ MA5311_POWER_MASK, MA5311_POWER_ON);
+
+ switch (mux) {
+ case I2S_ADC1:
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH1_00_GENERAL_SETTING,
+ MA5311_ADC_POWER_MASK,
+ MA5311_ADC_POWER_ON);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH1_0E_MUTE_DATA_FLOW,
+ MA5311_ADC_MUTE_BOTH_MASK,
+ MA5311_ADC_BOTH_DEMUTE);
+
+ break;
+ case I2S_ADC2:
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH1_00_GENERAL_SETTING,
+ MA5311_ADC_POWER_MASK,
+ MA5311_ADC_POWER_ON);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH1_0E_MUTE_DATA_FLOW,
+ MA5311_ADC_MUTE_BOTH_MASK,
+ MA5311_ADC_BOTH_DEMUTE);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH2_10_GENERAL_SETTING,
+ MA5311_ADC_POWER_MASK,
+ MA5311_ADC_POWER_ON);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH2_1E_MUTE_DATA_FLOW,
+ MA5311_ADC_MUTE_BOTH_MASK,
+ MA5311_ADC_BOTH_DEMUTE);
+ break;
+ case I2S_ADC3:
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH3_20_GENERAL_SETTING,
+ MA5311_ADC_POWER_MASK,
+ MA5311_ADC_POWER_ON);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH3_2E_MUTE_DATA_FLOW,
+ MA5311_ADC_MUTE_BOTH_MASK,
+ MA5311_ADC_BOTH_DEMUTE);
+ break;
+ case I2S_ADC4:
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH3_20_GENERAL_SETTING,
+ MA5311_ADC_POWER_MASK,
+ MA5311_ADC_POWER_ON);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH3_2E_MUTE_DATA_FLOW,
+ MA5311_ADC_MUTE_BOTH_MASK,
+ MA5311_ADC_BOTH_DEMUTE);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH4_30_GENERAL_SETTING,
+ MA5311_ADC_POWER_MASK,
+ MA5311_ADC_POWER_ON);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH4_3E_MUTE_DATA_FLOW,
+ MA5311_ADC_MUTE_BOTH_MASK,
+ MA5311_ADC_BOTH_DEMUTE);
+ break;
+ case TDM_8CH:
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH1_00_GENERAL_SETTING,
+ MA5311_ADC_POWER_MASK,
+ MA5311_ADC_POWER_ON);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH1_0E_MUTE_DATA_FLOW,
+ MA5311_ADC_MUTE_BOTH_MASK,
+ MA5311_ADC_BOTH_DEMUTE);
+
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH2_10_GENERAL_SETTING,
+ MA5311_ADC_POWER_MASK,
+ MA5311_ADC_POWER_ON);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH2_1E_MUTE_DATA_FLOW,
+ MA5311_ADC_MUTE_BOTH_MASK,
+ MA5311_ADC_BOTH_DEMUTE);
+
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH3_20_GENERAL_SETTING,
+ MA5311_ADC_POWER_MASK,
+ MA5311_ADC_POWER_ON);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH3_2E_MUTE_DATA_FLOW,
+ MA5311_ADC_MUTE_BOTH_MASK,
+ MA5311_ADC_BOTH_DEMUTE);
+
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH4_30_GENERAL_SETTING,
+ MA5311_ADC_POWER_MASK,
+ MA5311_ADC_POWER_ON);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH4_3E_MUTE_DATA_FLOW,
+ MA5311_ADC_MUTE_BOTH_MASK,
+ MA5311_ADC_BOTH_DEMUTE);
+ break;
+ default:
+ dev_err(ma5311->dev, "%s: Invalid value (%u)\n",
+ __func__, mux);
+ return -EINVAL;
+ }
+ ma5311->adc_power_status = true;
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ switch (mux) {
+ case I2S_ADC1:
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH1_0E_MUTE_DATA_FLOW,
+ MA5311_ADC_MUTE_BOTH_MASK,
+ MA5311_ADC_BOTH_MUTE);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH1_00_GENERAL_SETTING,
+ MA5311_ADC_POWER_MASK,
+ MA5311_ADC_POWER_DOWN);
+ break;
+ case I2S_ADC2:
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH1_0E_MUTE_DATA_FLOW,
+ MA5311_ADC_MUTE_BOTH_MASK,
+ MA5311_ADC_BOTH_MUTE);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH1_00_GENERAL_SETTING,
+ MA5311_ADC_POWER_MASK,
+ MA5311_ADC_POWER_DOWN);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH2_1E_MUTE_DATA_FLOW,
+ MA5311_ADC_MUTE_BOTH_MASK,
+ MA5311_ADC_BOTH_MUTE);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH2_10_GENERAL_SETTING,
+ MA5311_ADC_POWER_MASK,
+ MA5311_ADC_POWER_DOWN);
+ break;
+ case I2S_ADC3:
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH3_2E_MUTE_DATA_FLOW,
+ MA5311_ADC_MUTE_BOTH_MASK,
+ MA5311_ADC_BOTH_MUTE);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH3_20_GENERAL_SETTING,
+ MA5311_ADC_POWER_MASK,
+ MA5311_ADC_POWER_DOWN);
+ break;
+ case I2S_ADC4:
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH3_2E_MUTE_DATA_FLOW,
+ MA5311_ADC_MUTE_BOTH_MASK,
+ MA5311_ADC_BOTH_MUTE);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH3_20_GENERAL_SETTING,
+ MA5311_ADC_POWER_MASK,
+ MA5311_ADC_POWER_DOWN);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH4_3E_MUTE_DATA_FLOW,
+ MA5311_ADC_MUTE_BOTH_MASK,
+ MA5311_ADC_BOTH_MUTE);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH4_30_GENERAL_SETTING,
+ MA5311_ADC_POWER_MASK,
+ MA5311_ADC_POWER_DOWN);
+ break;
+ case TDM_8CH:
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH1_0E_MUTE_DATA_FLOW,
+ MA5311_ADC_MUTE_BOTH_MASK,
+ MA5311_ADC_BOTH_MUTE);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH1_00_GENERAL_SETTING,
+ MA5311_ADC_POWER_MASK,
+ MA5311_ADC_POWER_DOWN);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH2_1E_MUTE_DATA_FLOW,
+ MA5311_ADC_MUTE_BOTH_MASK,
+ MA5311_ADC_BOTH_MUTE);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH2_10_GENERAL_SETTING,
+ MA5311_ADC_POWER_MASK,
+ MA5311_ADC_POWER_DOWN);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH3_2E_MUTE_DATA_FLOW,
+ MA5311_ADC_MUTE_BOTH_MASK,
+ MA5311_ADC_BOTH_MUTE);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH3_20_GENERAL_SETTING,
+ MA5311_ADC_POWER_MASK,
+ MA5311_ADC_POWER_DOWN);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH4_3E_MUTE_DATA_FLOW,
+ MA5311_ADC_MUTE_BOTH_MASK,
+ MA5311_ADC_BOTH_MUTE);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH4_30_GENERAL_SETTING,
+ MA5311_ADC_POWER_MASK,
+ MA5311_ADC_POWER_DOWN);
+ break;
+ default:
+ dev_err(ma5311->dev, "%s: Invalid value (%u)\n",
+ __func__, mux);
+ return -EINVAL;
+ }
+
+ ma5311->adc_power_status = false;
+
+ if (!ma5311->amp_power_status) {
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_A2_TOP_MAN2,
+ MA5311_PLL_MASK, MA5311_PLL_OFF);
+
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_00_SYSTEM_CTRL1,
+ MA5311_POWER_MASK, MA5311_POWER_OFF);
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget ma5311_dapm_widgets[] = {
+ /* Playback */
+ SND_SOC_DAPM_AIF_IN("AIF IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MUX_E("AIF IN Source", SND_SOC_NOPM, 0, 0,
+ &ma5311_aif_in_source_control, ma5311_aif_in_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MIXER("Entry", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV_E("AMP Power", SND_SOC_NOPM, 0, 0, NULL, 0,
+ ma5311_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUTPUT("AOUT1L"),
+ SND_SOC_DAPM_OUTPUT("AOUT1R"),
+
+ /* Capture */
+ SND_SOC_DAPM_INPUT("ADC IN"),
+ SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0, ma5311_adc_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT("AIF OUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route ma5311_dapm_routes[] = {
+ /* Playback */
+ { "AIF IN Source", "Mono Both", "AIF IN" },
+ { "AIF IN Source", "Stereo", "AIF IN" },
+ { "Entry", NULL, "AIF IN Source" },
+ { "AMP Power", NULL, "Entry" },
+ { "AOUT1L", NULL, "AMP Power" },
+ { "AOUT1R", NULL, "AMP Power" },
+
+ /* Capture */
+ { "ADC", NULL, "ADC IN" },
+ { "AIF OUT", NULL, "ADC" },
+};
+
+static int ma5311_probe(struct snd_soc_component *component)
+{
+ return ma5311_reset(component);
+}
+
+static const struct snd_soc_component_driver ma5311_component = {
+ .probe = ma5311_probe,
+ .controls = ma5311_snd_controls,
+ .num_controls = ARRAY_SIZE(ma5311_snd_controls),
+ .dapm_widgets = ma5311_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ma5311_dapm_widgets),
+ .dapm_routes = ma5311_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ma5311_dapm_routes),
+};
+
+static const struct regmap_config ma5311_ctrl_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = MA5311_DAC_FF_VERSION,
+ .readable_reg = ma5311_ctrl_readable_register,
+ .writeable_reg = ma5311_ctrl_writeable_register,
+ .volatile_reg = ma5311_ctrl_volatile_register,
+
+ .cache_type = REGCACHE_NONE,
+ .reg_defaults = ma5311_ctrl_reg_def,
+ .num_reg_defaults = ARRAY_SIZE(ma5311_ctrl_reg_def),
+};
+
+static const struct regmap_config ma5311_adc_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = MA5311_ADC_CH4_3F_OUTPUT_FORMAT,
+ .readable_reg = ma5311_adc_readable_register,
+ .writeable_reg = ma5311_adc_writeable_register,
+ .volatile_reg = ma5311_adc_volatile_register,
+
+ .cache_type = REGCACHE_NONE,
+ .reg_defaults = ma5311_adc_reg_def,
+ .num_reg_defaults = ARRAY_SIZE(ma5311_adc_reg_def),
+};
+
+static int ma5311_setup_pll(struct snd_soc_component *component,
+ unsigned int bclk)
+{
+ struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+ unsigned int i = 0;
+
+ dev_dbg(component->dev, "PLL input BCLK: %u Hz\n", bclk);
+
+ if (ma5311->sys_clk_id != MA5311_PLL_CLKIN_BCLK)
+ return 0;
+
+ for (i = 0; i < ma5311->num_of_pll_matches; i++) {
+ if (ma5311->pll_matches[i].input_clk == bclk)
+ break;
+ }
+
+ if (i == ma5311->num_of_pll_matches) {
+ dev_warn(component->dev,
+ "%s: No matching value between pll table and SCK\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_A2_TOP_MAN2,
+ MA5311_PLL_MASK, MA5311_PLL_ON);
+
+ regmap_write(ma5311->ctrl_regmap, MA5311_DAC_8B_PLL_POST_N,
+ ma5311->pll_matches[i].post_n);
+
+ regmap_write(ma5311->ctrl_regmap, MA5311_DAC_8C_PLL_N,
+ ma5311->pll_matches[i].n);
+
+ regmap_write(ma5311->ctrl_regmap, MA5311_DAC_8D_PLL_F1,
+ ma5311->pll_matches[i].f1);
+
+ regmap_write(ma5311->ctrl_regmap, MA5311_DAC_8E_PLL_F2,
+ ma5311->pll_matches[i].f2);
+
+ regmap_write(ma5311->ctrl_regmap, MA5311_DAC_8F_PLL_F3_P_CP,
+ ma5311->pll_matches[i].f3_p_cp);
+
+ return 0;
+}
+
+static int ma5311_dai_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 ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+ unsigned int bclk = 0;
+ int ret;
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 12000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 44100:
+ case 48000:
+ case 96000:
+ break;
+
+ default:
+ dev_err(component->dev, "%s: unsupported sample rate: %u\n",
+ __func__, params_rate(params));
+
+ return -EINVAL;
+ }
+
+ if (params_channels(params) > 8) {
+ dev_err(component->dev, "%s: unsupported channel count: %u\n",
+ __func__, params_channels(params));
+
+ return -EINVAL;
+ }
+
+ if (ma5311->format == SND_SOC_DAIFMT_DSP_A) {
+ if (!ma5311->frame_size) {
+ dev_err(component->dev,
+ "TDM frame size is not configured\n");
+ return -EINVAL;
+ }
+
+ bclk = params_rate(params) * ma5311->frame_size;
+ dev_dbg(component->dev, "Estimated TDM BCLK: %u Hz\n", bclk);
+ } else if (ma5311->format == SND_SOC_DAIFMT_I2S) {
+ bclk = params_rate(params) * params_physical_width(params) *
+ params_channels(params);
+ dev_dbg(component->dev, "Estimated I2S BCLK: %u Hz\n", bclk);
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (ma5311->sys_clk_id == MA5311_PLL_CLKIN_BCLK &&
+ ma5311->last_bclk != bclk) {
+ dev_dbg(component->dev, "Playback BCLK: %u Hz\n", bclk);
+ ret = ma5311_setup_pll(component, bclk);
+ if (ret) {
+ ma5311->last_bclk = 0;
+ dev_err(component->dev,
+ "Failed to configure PLL: %d\n", ret);
+ return ret;
+ }
+ ma5311->last_bclk = bclk;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_A1_TOP_MAN1_FOR_SDO,
+ MA5311_TOP_MAN1_SOO_LENGTH_MASK,
+ MA5311_TOP_MAN1_SDO_16B);
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_A1_TOP_MAN1_FOR_SDO,
+ MA5311_DAC_A1_SCK_RATE_MASK,
+ MA5311_DAC_A1_SCK_RATE_32FS);
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_38_TDM2,
+ MA5311_DAC_38_DATA_LEN_MASK,
+ MA5311_DAC_38_DATA_LEN_16);
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_A1_TOP_MAN1_FOR_SDO,
+ MA5311_TOP_MAN1_SOO_LENGTH_MASK,
+ MA5311_TOP_MAN1_SDO_24B);
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_A1_TOP_MAN1_FOR_SDO,
+ MA5311_DAC_A1_SCK_RATE_MASK,
+ MA5311_DAC_A1_SCK_RATE_64FS);
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_38_TDM2,
+ MA5311_DAC_38_DATA_LEN_MASK,
+ MA5311_DAC_38_DATA_LEN_32);
+ break;
+
+ default:
+ dev_err(component->dev,
+ "%s: unsupported PCM format: %d\n", __func__,
+ params_format(params));
+ return -EINVAL;
+ }
+
+ switch (ma5311->format) {
+ case SND_SOC_DAIFMT_I2S:
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_A1_TOP_MAN1_FOR_SDO,
+ MA5311_TOP_MAN1_O_FORMAT_MASK,
+ MA5311_TOP_MAN1_O_FORMAT_I2S);
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_A1_TOP_MAN1_FOR_SDO,
+ MA5311_TOP_MAN1_O_FORMAT_MASK,
+ MA5311_TOP_MAN1_O_FORMAT_TDM);
+ break;
+ }
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (ma5311->sys_clk_id == MA5311_PLL_CLKIN_BCLK &&
+ ma5311->last_bclk != bclk) {
+ dev_dbg(component->dev, "Capture BCLK: %u Hz\n", bclk);
+ ret = ma5311_setup_pll(component, bclk);
+
+ if (ret) {
+ ma5311->last_bclk = 0;
+ dev_err(component->dev,
+ "Failed to configure PLL: %d\n", ret);
+ return ret;
+ }
+
+ ma5311->last_bclk = bclk;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_38_TDM2,
+ MA5311_TDM_DL_MASK,
+ MA5311_TDM_DL_16);
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_A1_TOP_MAN1_FOR_SDO,
+ MA5311_TOP_MAN1_SOO_LENGTH_MASK,
+ MA5311_TOP_MAN1_SDO_16B);
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_A1_TOP_MAN1_FOR_SDO,
+ MA5311_DAC_A1_SCK_RATE_MASK,
+ MA5311_DAC_A1_SCK_RATE_32FS);
+
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH1_0F_OUTPUT_FORMAT,
+ MA5311_ADC_OUTPUT_WORD_LENGTH_MASK,
+ MA5311_ADC_WORD_LENGTH_16);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH2_1F_OUTPUT_FORMAT,
+ MA5311_ADC_OUTPUT_WORD_LENGTH_MASK,
+ MA5311_ADC_WORD_LENGTH_16);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH3_2F_OUTPUT_FORMAT,
+ MA5311_ADC_OUTPUT_WORD_LENGTH_MASK,
+ MA5311_ADC_WORD_LENGTH_16);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH4_3F_OUTPUT_FORMAT,
+ MA5311_ADC_OUTPUT_WORD_LENGTH_MASK,
+ MA5311_ADC_WORD_LENGTH_16);
+
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH1_0F_OUTPUT_FORMAT,
+ MA5311_ADC_OUTPUT_SCK_RATE_MASK,
+ MA5311_ADC_SCK_RATE_16BIT_CFG);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH2_1F_OUTPUT_FORMAT,
+ MA5311_ADC_OUTPUT_SCK_RATE_MASK,
+ MA5311_ADC_SCK_RATE_16BIT_CFG);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH3_2F_OUTPUT_FORMAT,
+ MA5311_ADC_OUTPUT_SCK_RATE_MASK,
+ MA5311_ADC_SCK_RATE_16BIT_CFG);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH4_3F_OUTPUT_FORMAT,
+ MA5311_ADC_OUTPUT_SCK_RATE_MASK,
+ MA5311_ADC_SCK_RATE_16BIT_CFG);
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_38_TDM2,
+ MA5311_TDM_DL_MASK,
+ MA5311_TDM_DL_32);
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_A1_TOP_MAN1_FOR_SDO,
+ MA5311_TOP_MAN1_SOO_LENGTH_MASK,
+ MA5311_TOP_MAN1_SDO_24B);
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_A1_TOP_MAN1_FOR_SDO,
+ MA5311_DAC_A1_SCK_RATE_MASK,
+ MA5311_DAC_A1_SCK_RATE_64FS);
+
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH1_0F_OUTPUT_FORMAT,
+ MA5311_ADC_OUTPUT_WORD_LENGTH_MASK,
+ MA5311_ADC_WORD_LENGTH_24);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH2_1F_OUTPUT_FORMAT,
+ MA5311_ADC_OUTPUT_WORD_LENGTH_MASK,
+ MA5311_ADC_WORD_LENGTH_24);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH3_2F_OUTPUT_FORMAT,
+ MA5311_ADC_OUTPUT_WORD_LENGTH_MASK,
+ MA5311_ADC_WORD_LENGTH_24);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH4_3F_OUTPUT_FORMAT,
+ MA5311_ADC_OUTPUT_WORD_LENGTH_MASK,
+ MA5311_ADC_WORD_LENGTH_24);
+
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH1_0F_OUTPUT_FORMAT,
+ MA5311_ADC_OUTPUT_SCK_RATE_MASK,
+ MA5311_ADC_SCK_RATE_24BIT_CFG);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH2_1F_OUTPUT_FORMAT,
+ MA5311_ADC_OUTPUT_SCK_RATE_MASK,
+ MA5311_ADC_SCK_RATE_24BIT_CFG);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH3_2F_OUTPUT_FORMAT,
+ MA5311_ADC_OUTPUT_SCK_RATE_MASK,
+ MA5311_ADC_SCK_RATE_24BIT_CFG);
+ regmap_update_bits(ma5311->adc_regmap,
+ MA5311_ADC_CH4_3F_OUTPUT_FORMAT,
+ MA5311_ADC_OUTPUT_SCK_RATE_MASK,
+ MA5311_ADC_SCK_RATE_24BIT_CFG);
+ break;
+ default:
+ dev_err(component->dev,
+ "%s: unsupported PCM format: %d\n", __func__,
+ params_format(params));
+ return -EINVAL;
+ }
+
+ switch (ma5311->format) {
+ case SND_SOC_DAIFMT_I2S:
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_A1_TOP_MAN1_FOR_SDO,
+ MA5311_TOP_MAN1_O_FORMAT_MASK,
+ MA5311_TOP_MAN1_O_FORMAT_I2S);
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_A1_TOP_MAN1_FOR_SDO,
+ MA5311_TOP_MAN1_O_FORMAT_MASK,
+ MA5311_TOP_MAN1_O_FORMAT_TDM);
+ break;
+ }
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ case 24:
+ case 32:
+ break;
+ default:
+ dev_err(component->dev, "%s: unsupported PCM format: %d\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ma5311_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ dev_dbg(component->dev, "BCLK and frame clock consumer\n");
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_01_INPUT1_CTRL1,
+ MA5311_CONTROLLER_DEVICE_MASK,
+ MA5311_DEVICE_MODE);
+
+ break;
+
+ case SND_SOC_DAIFMT_CBP_CFP:
+ dev_dbg(component->dev, "BCLK and frame clock provider\n");
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_01_INPUT1_CTRL1,
+ MA5311_CONTROLLER_DEVICE_MASK,
+ MA5311_CONTROLLER_MODE);
+
+ break;
+
+ default:
+ dev_err(component->dev,
+ "%s: Unsupported Controller/Device : 0x%x\n", __func__,
+ fmt);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_DSP_A:
+ break;
+
+ default:
+ dev_err(component->dev,
+ "%s: Unsupported Audio Interface Format : 0x%x\n",
+ __func__, fmt);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ dev_dbg(component->dev, "Normal BCLK, normal frame clock\n");
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_01_INPUT1_CTRL1,
+ MA5311_LEFTPOL_MASK |
+ MA5311_SCK_RISING_MASK,
+ MA5311_LOW_FIRST_CH |
+ MA5311_SCK_FALLING_EDGE);
+
+ break;
+
+ case SND_SOC_DAIFMT_NB_IF:
+ dev_dbg(component->dev, "Normal BCLK, inverted frame clock\n");
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_01_INPUT1_CTRL1,
+ MA5311_LEFTPOL_MASK |
+ MA5311_SCK_RISING_MASK,
+ MA5311_HIGH_FIRST_CH |
+ MA5311_SCK_FALLING_EDGE);
+
+ break;
+
+ case SND_SOC_DAIFMT_IB_NF:
+ dev_dbg(component->dev, "Inverted BCLK, normal frame clock\n");
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_01_INPUT1_CTRL1,
+ MA5311_LEFTPOL_MASK |
+ MA5311_SCK_RISING_MASK,
+ MA5311_LOW_FIRST_CH |
+ MA5311_SCK_RISING_EDGE);
+
+ break;
+
+ case SND_SOC_DAIFMT_IB_IF:
+ dev_dbg(component->dev,
+ "Inverted BCLK, inverted frame clock\n");
+ regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_01_INPUT1_CTRL1,
+ MA5311_LEFTPOL_MASK | MA5311_SCK_RISING_MASK,
+ MA5311_HIGH_FIRST_CH |
+ MA5311_SCK_RISING_EDGE);
+ break;
+
+ default:
+ dev_err(component->dev,
+ "%s: Unsupported Bit & Frameclock : 0x%x\n", __func__,
+ fmt);
+ return -EINVAL;
+ }
+
+ ma5311->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+ return 0;
+}
+
+static int ma5311_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+
+ switch (clk_id) {
+ case MA5311_EXTERNAL_CLOCK_19_2:
+ case MA5311_EXTERNAL_CLOCK_24_576:
+ case MA5311_PLL_CLKIN_MCLK:
+ case MA5311_PLL_CLKIN_BCLK:
+ if (clk_id == MA5311_PLL_CLKIN_BCLK)
+ dev_dbg(component->dev, "PLL input: BCLK\n");
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid clk id: %d\n", __func__,
+ clk_id);
+ return -EINVAL;
+ }
+
+ ma5311->sys_clk_id = clk_id;
+
+ return 0;
+}
+
+static int ma5311_dai_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 *component = dai->component;
+ struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+
+ /* Reject invalid frame geometry before changing the hardware. */
+ switch (slot_width) {
+ case 16:
+ case 32:
+ break;
+
+ default:
+ dev_err(component->dev, "%s: unsupported TDM slot width: %d\n",
+ __func__, slot_width);
+ return -EINVAL;
+ }
+
+ switch (slots) {
+ case 4:
+ case 8:
+ break;
+
+ default:
+ dev_err(component->dev, "%s: unsupported TDM slot count: %d\n",
+ __func__, slots);
+ return -EINVAL;
+ }
+
+ ma5311->frame_size = slot_width * slots;
+ dev_dbg(component->dev, "TDM slots=%d width=%d\n", slots, slot_width);
+
+ return 0;
+}
+
+static int ma5311_dai_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+
+ if (stream == SNDRV_PCM_STREAM_CAPTURE)
+ return 0;
+
+ if (mute || ma5311->force_mute_status) {
+ dev_dbg(component->dev, "Request playback mute\n");
+ val = MA5311_SPK_MUTE;
+ } else {
+ dev_dbg(component->dev, "Request playback unmute\n");
+ val = MA5311_SPK_UNMUTE;
+ }
+
+ return regmap_update_bits(ma5311->ctrl_regmap,
+ MA5311_DAC_0E_MUTE_VOL_CTRL,
+ MA5311_DAC_MUTE_VOL_MASK, val);
+}
+
+static const struct snd_soc_dai_ops ma5311_dai_ops_amp = {
+ .hw_params = ma5311_dai_hw_params,
+ .set_fmt = ma5311_dai_set_fmt,
+ .set_sysclk = ma5311_dai_set_sysclk,
+ .set_tdm_slot = ma5311_dai_set_tdm_slot,
+ .mute_stream = ma5311_dai_mute_stream,
+};
+
+#define MA5311_RATES_PLAYBACK SNDRV_PCM_RATE_8000_96000
+#define MA5311_RATES_CAPTURE SNDRV_PCM_RATE_8000_96000
+#define MA5311_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver ma5311_dai[] = {
+ {
+ .name = "ma5311-amplifier",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MA5311_RATES_PLAYBACK,
+ .formats = MA5311_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MA5311_RATES_CAPTURE,
+ .formats = MA5311_FORMATS,
+ },
+ .ops = &ma5311_dai_ops_amp,
+ },
+};
+
+static void ma5311_unregister_ancillary_client(void *data)
+{
+ struct i2c_client *client = data;
+
+ i2c_unregister_device(client);
+}
+
+static irqreturn_t ma5311_irq_thread(int irq, void *data)
+{
+ struct ma5311_priv *ma5311 = data;
+ unsigned int dac_status;
+ unsigned int dac_ocp_status;
+ unsigned int dac_scp_status;
+ int ret = 0;
+
+ ret |= regmap_read(ma5311->ctrl_regmap,
+ MA5311_DAC_EA_DAC_STATUS, &dac_status);
+
+ ret |= regmap_read(ma5311->ctrl_regmap,
+ MA5311_DAC_EB_DAC_OCP_STATUS, &dac_ocp_status);
+
+ ret |= regmap_read(ma5311->ctrl_regmap,
+ MA5311_DAC_EC_DAC_SCP_STATUS, &dac_scp_status);
+
+ if (ret) {
+ dev_crit(ma5311->dev,
+ "Error Occurred During Interrupt Handling\n");
+ return ret;
+ }
+ if (dac_status & MA5311_DAC_EA_FAULT_MASK)
+ dev_crit(ma5311->dev, "FAULT asserted\n");
+
+ if (!(dac_ocp_status & MA5311_DAC_EB_OCP_1A_MASK))
+ dev_crit(ma5311->dev, "CH1A OCP\n");
+
+ if (~dac_status & MA5311_DAC_EA_UVLON_CH1_MASK)
+ dev_crit(ma5311->dev, "%s: CH1 UVLO\n", __func__);
+
+ if (~dac_status & MA5311_DAC_EA_UVLON_CH2_MASK)
+ dev_crit(ma5311->dev, "%s: CH2 UVLO\n", __func__);
+
+ if (dac_status & MA5311_DAC_EA_TSDW_MASK)
+ dev_crit(ma5311->dev, "%s: TSD Warning\n", __func__);
+
+ if (dac_status & MA5311_DAC_EA_TSD_MASK)
+ dev_crit(ma5311->dev, "%s: TSD Fault\n", __func__);
+
+ if (~dac_ocp_status & MA5311_DAC_EB_IPSC_2U_MASK)
+ dev_crit(ma5311->dev,
+ "%s: IPSC CH2 Highside Fault\n", __func__);
+
+ if (~dac_ocp_status & MA5311_DAC_EB_IPSC_2L_MASK)
+ dev_crit(ma5311->dev, "%s: IPSC CH2 Lowside Fault\n", __func__);
+
+ if (~dac_ocp_status & MA5311_DAC_EB_IPSC_1U_MASK)
+ dev_crit(ma5311->dev,
+ "%s: IPSC CH1 Highside Fault\n", __func__);
+
+ if (~dac_ocp_status & MA5311_DAC_EB_IPSC_1L_MASK)
+ dev_crit(ma5311->dev, "%s: IPSC CH1 Lowside Fault\n", __func__);
+
+ if (~dac_scp_status & MA5311_DAC_EC_SCP_2B_MASK)
+ dev_crit(ma5311->dev, "%s: CH2B SCP\n", __func__);
+
+ if (~dac_scp_status & MA5311_DAC_EC_SCP_2A_MASK)
+ dev_crit(ma5311->dev, "%s: CH2A SCP\n", __func__);
+
+ if (~dac_scp_status & MA5311_DAC_EC_SCP_1B_MASK)
+ dev_crit(ma5311->dev, "%s: CH1B SCP\n", __func__);
+
+ if (~dac_scp_status & MA5311_DAC_EC_SCP_1A_MASK)
+ dev_crit(ma5311->dev, "%s: CH1A SCP\n", __func__);
+
+ dev_crit(ma5311->dev, "MA5311 INTN fault detected: irq=%d\n", irq);
+
+ return IRQ_HANDLED;
+}
+
+static int ma5311_i2c_probe(struct i2c_client *client)
+{
+ struct ma5311_priv *ma5311;
+ int ret;
+
+ ma5311 = devm_kzalloc(&client->dev, sizeof(*ma5311), GFP_KERNEL);
+
+ if (!ma5311)
+ return -ENOMEM;
+
+ ma5311->ctrl_regmap =
+ devm_regmap_init_i2c(client, &ma5311_ctrl_regmap_config);
+
+ if (IS_ERR(ma5311->ctrl_regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(ma5311->ctrl_regmap),
+ "%s: Failed to allocate control regmap\n",
+ __func__);
+
+ ma5311->adc_client = i2c_new_ancillary_device(client, "adc", 0x3f);
+
+ if (IS_ERR(ma5311->adc_client))
+ return dev_err_probe(&client->dev, PTR_ERR(ma5311->adc_client),
+ "Failed to create ADC ancillary client\n");
+
+ ret = devm_add_action_or_reset(&client->dev,
+ ma5311_unregister_ancillary_client,
+ ma5311->adc_client);
+ if (ret)
+ return ret;
+
+ ma5311->adc_regmap = devm_regmap_init_i2c(ma5311->adc_client,
+ &ma5311_adc_regmap_config);
+
+ if (IS_ERR(ma5311->adc_regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(ma5311->adc_regmap),
+ "Failed to allocate ADC regmap\n");
+
+ /* Logical 1 asserts reset, using the polarity from reset-gpios. */
+ ma5311->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ma5311->reset_gpio))
+ return dev_err_probe(&client->dev, PTR_ERR(ma5311->reset_gpio),
+ "Failed to acquire reset GPIO\n");
+
+ if (ma5311->reset_gpio)
+ msleep(MA5311_RESET_RECOVERY_MS);
+
+ ma5311->amp_mode = MA5311_MONO_BOTH_MODE;
+ ma5311->num_of_pll_matches = ARRAY_SIZE(ma5311_pll_matches);
+ ma5311->pll_matches = ma5311_pll_matches;
+ ma5311->format = SND_SOC_DAIFMT_I2S;
+ ma5311->sys_clk_id = MA5311_PLL_CLKIN_BCLK;
+ ma5311->dev = &client->dev;
+
+ i2c_set_clientdata(client, ma5311);
+
+ ret = devm_snd_soc_register_component(&client->dev, &ma5311_component,
+ ma5311_dai, 1);
+
+ if (ret) {
+ dev_err(&client->dev, "Failed to register component: %d\n",
+ ret);
+
+ return ret;
+ }
+
+ dev_dbg(&client->dev, "MA5311 client->irq = %d\n", client->irq);
+
+ if (client->irq > 0) {
+ ret = devm_request_threaded_irq(&client->dev,
+ client->irq,
+ NULL,
+ ma5311_irq_thread,
+ IRQF_ONESHOT, "ma5311", ma5311);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "Failed to request IRQ %d\n",
+ client->irq);
+
+ dev_dbg(&client->dev, "MA5311 IRQ %d registered\n",
+ client->irq);
+ } else {
+ dev_warn(&client->dev, "No IRQ configured\n");
+ }
+
+ dev_dbg(&client->dev, "Codec component registered\n");
+
+ return ret;
+}
+
+static const struct i2c_device_id ma5311_i2c_id[] = { { "ma5311", 0 }, { } };
+
+MODULE_DEVICE_TABLE(i2c, ma5311_i2c_id);
+
+static const struct of_device_id ma5311_of_match[] = {
+ {
+ .compatible = "irondevice,ma5311",
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, ma5311_of_match);
+
+static struct i2c_driver ma5311_i2c_driver = {
+ .driver = {
+ .name = "ma5311",
+ .of_match_table = ma5311_of_match,
+ },
+ .probe = ma5311_i2c_probe,
+ .id_table = ma5311_i2c_id,
+};
+
+module_i2c_driver(ma5311_i2c_driver);
+
+MODULE_DESCRIPTION("ALSA SoC MA5311 driver");
+MODULE_AUTHOR("Hyunuk Son, <hyunuk.son@irondevice.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ma5311.h b/sound/soc/codecs/ma5311.h
new file mode 100644
index 000000000..8ffb1492f
--- /dev/null
+++ b/sound/soc/codecs/ma5311.h
@@ -0,0 +1,520 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * ma5311.h -- MA5311 ALSA SoC codec register definitions
+ */
+
+#ifndef _MA5311_H
+#define _MA5311_H
+
+#include <linux/bits.h>
+
+/* DAC/control and ADC registers use separate I2C banks. */
+
+/* I2C addresses */
+#define MA5311_ADC_I2C_ADDR_00 0x3f
+#define MA5311_ADC_I2C_ADDR_01 0x4f
+#define MA5311_ADC_I2C_ADDR_10 0x5f
+#define MA5311_ADC_I2C_ADDR_11 0x6f
+
+#define MA5311_I2C_RETRY_COUNT 3
+
+/* DAC and control register addresses */
+#define MA5311_DAC_00_SYSTEM_CTRL1 0x00
+#define MA5311_DAC_01_INPUT1_CTRL1 0x01
+#define MA5311_DAC_02_INPUT1_CTRL2 0x02
+#define MA5311_DAC_03_INPUT1_CTRL3 0x03
+#define MA5311_DAC_04_INPUT1_CTRL4 0x04
+#define MA5311_DAC_05_ADC_DAC_MIX2 0x05
+#define MA5311_DAC_09_OUTPUT_CTRL 0x09
+#define MA5311_DAC_0A_SPK_VOL_L 0x0a
+#define MA5311_DAC_0B_SPK_VOL_R 0x0b
+#define MA5311_DAC_0E_MUTE_VOL_CTRL 0x0e
+#define MA5311_DAC_0F_SYSTEM_CTRL2 0x0f
+#define MA5311_DAC_10_SYSTEM_CTRL3 0x10
+#define MA5311_DAC_11_SYSTEM_CTRL4 0x11
+#define MA5311_DAC_12_SYSTEM_CTRL5 0x12
+#define MA5311_DAC_14_MODULATOR2 0x14
+#define MA5311_DAC_15_BASS_SPK1 0x15
+#define MA5311_DAC_16_BASS_SPK2 0x16
+#define MA5311_DAC_17_BASS_SPK3 0x17
+#define MA5311_DAC_18_BASS_SPK4 0x18
+#define MA5311_DAC_19_BASS_SPK5 0x19
+#define MA5311_DAC_1A_BASS_SPK6 0x1a
+#define MA5311_DAC_1B_BASS_SPK7 0x1b
+#define MA5311_DAC_23_COMP_LIM1 0x23
+#define MA5311_DAC_24_COMP_LIM2 0x24
+#define MA5311_DAC_25_COMP_LIM3 0x25
+#define MA5311_DAC_26_COMP_LIM4 0x26
+#define MA5311_DAC_2B_EQ_MODE 0x2b
+#define MA5311_DAC_2C_EQ_GRAPHIC1 0x2c
+#define MA5311_DAC_2D_EQ_GRAPHIC2 0x2d
+#define MA5311_DAC_2E_EQ_GRAPHIC3 0x2e
+#define MA5311_DAC_2F_EQ_GRAPHIC4 0x2f
+#define MA5311_DAC_30_EQ_GRAPHIC5 0x30
+#define MA5311_DAC_33_FLL_CTRL3 0x33
+#define MA5311_DAC_34_EQ_CTRL 0x34
+#define MA5311_DAC_35_POST_DRC_CTRL 0x35
+#define MA5311_DAC_36_PROTECTION 0x36
+#define MA5311_DAC_37_TDM1 0x37
+#define MA5311_DAC_38_TDM2 0x38
+#define MA5311_DAC_39_DATA_PATH 0x39
+#define MA5311_DAC_3A_TDM_SLOT_ENABLE 0x3a
+#define MA5311_DAC_3B_TEST1 0x3b
+#define MA5311_DAC_3C_TEST2 0x3c
+#define MA5311_DAC_3D_TEST3 0x3d
+#define MA5311_DAC_40_EQ_CTRL1 0x40
+#define MA5311_DAC_41_EQ_CTRL2 0x41
+#define MA5311_DAC_42_EQ_CTRL3 0x42
+#define MA5311_DAC_43_EQ_CTRL4 0x43
+#define MA5311_DAC_44_EQ_CTRL5 0x44
+#define MA5311_DAC_45_EQ_CTRL6 0x45
+#define MA5311_DAC_46_EQ_CTRL7 0x46
+#define MA5311_DAC_47_EQ_CTRL8 0x47
+#define MA5311_DAC_48_EQ_CTRL9 0x48
+#define MA5311_DAC_49_EQ_CTRL10 0x49
+#define MA5311_DAC_4A_EQ_CTRL11 0x4a
+#define MA5311_DAC_4B_EQ_CTRL12 0x4b
+#define MA5311_DAC_4C_EQ_CTRL13 0x4c
+#define MA5311_DAC_4D_EQ_CTRL14 0x4d
+#define MA5311_DAC_4E_EQ_CTRL15 0x4e
+#define MA5311_DAC_4F_EQ_CTRL16 0x4f
+#define MA5311_DAC_50_EQ_CTRL17 0x50
+#define MA5311_DAC_51_EQ_CTRL18 0x51
+#define MA5311_DAC_52_EQ_CTRL19 0x52
+#define MA5311_DAC_53_EQ_CTRL20 0x53
+#define MA5311_DAC_54_EQ_CTRL21 0x54
+#define MA5311_DAC_55_EQ_CTRL22 0x55
+#define MA5311_DAC_56_EQ_CTRL23 0x56
+#define MA5311_DAC_57_EQ_CTRL24 0x57
+#define MA5311_DAC_58_EQ_CTRL25 0x58
+#define MA5311_DAC_59_EQ_CTRL26 0x59
+#define MA5311_DAC_5A_EQ_CTRL27 0x5a
+#define MA5311_DAC_5B_EQ_CTRL28 0x5b
+#define MA5311_DAC_5C_EQ_CTRL29 0x5c
+#define MA5311_DAC_5D_EQ_CTRL30 0x5d
+#define MA5311_DAC_5E_EQ_CTRL31 0x5e
+#define MA5311_DAC_5F_EQ_CTRL32 0x5f
+#define MA5311_DAC_60_EQ_CTRL33 0x60
+#define MA5311_DAC_61_EQ_CTRL34 0x61
+#define MA5311_DAC_62_EQ_CTRL35 0x62
+#define MA5311_DAC_63_EQ_CTRL36 0x63
+#define MA5311_DAC_64_EQ_CTRL37 0x64
+#define MA5311_DAC_65_EQ_CTRL38 0x65
+#define MA5311_DAC_66_EQ_CTRL39 0x66
+#define MA5311_DAC_67_EQ_CTRL40 0x67
+#define MA5311_DAC_68_EQ_CTRL41 0x68
+#define MA5311_DAC_69_EQ_CTRL42 0x69
+#define MA5311_DAC_6A_EQ_CTRL43 0x6a
+#define MA5311_DAC_6B_EQ_CTRL44 0x6b
+#define MA5311_DAC_6C_EQ_CTRL45 0x6c
+#define MA5311_DAC_6D_EQ_CTRL46 0x6d
+#define MA5311_DAC_6E_EQ_CTRL47 0x6e
+#define MA5311_DAC_6F_EQ_CTRL48 0x6f
+#define MA5311_DAC_70_EQ_CTRL49 0x70
+#define MA5311_DAC_71_EQ_CTRL50 0x71
+#define MA5311_DAC_72_EQ_CTRL51 0x72
+#define MA5311_DAC_73_EQ_CTRL52 0x73
+#define MA5311_DAC_74_EQ_CTRL53 0x74
+#define MA5311_DAC_75_EQ_CTRL54 0x75
+#define MA5311_DAC_76_EQ_CTRL55 0x76
+#define MA5311_DAC_77_EQ_CTRL56 0x77
+#define MA5311_DAC_78_EQ_CTRL57 0x78
+#define MA5311_DAC_79_EQ_CTRL58 0x79
+#define MA5311_DAC_7A_EQ_CTRL59 0x7a
+#define MA5311_DAC_7B_EQ_CTRL60 0x7b
+#define MA5311_DAC_7C_EQ_CTRL61 0x7c
+#define MA5311_DAC_7D_EQ_CTRL62 0x7d
+#define MA5311_DAC_7E_EQ_CTRL63 0x7e
+#define MA5311_DAC_7F_EQ_CTRL64 0x7f
+#define MA5311_DAC_80_EQ_CTRL65 0x80
+#define MA5311_DAC_81_EQ_CTRL66 0x81
+#define MA5311_DAC_82_EQ_CTRL67 0x82
+#define MA5311_DAC_83_EQ_CTRL68 0x83
+#define MA5311_DAC_84_EQ_CTRL69 0x84
+#define MA5311_DAC_85_EQ_CTRL70 0x85
+#define MA5311_DAC_86_EQ_CTRL71 0x86
+#define MA5311_DAC_87_EQ_CTRL72 0x87
+#define MA5311_DAC_88_EQ_CTRL73 0x88
+#define MA5311_DAC_89_EQ_CTRL74 0x89
+#define MA5311_DAC_8A_EQ_CTRL75 0x8a
+#define MA5311_DAC_8B_PLL_POST_N 0x8b
+#define MA5311_DAC_8C_PLL_N 0x8c
+#define MA5311_DAC_8D_PLL_F1 0x8d
+#define MA5311_DAC_8E_PLL_F2 0x8e
+#define MA5311_DAC_8F_PLL_F3_P_CP 0x8f
+#define MA5311_DAC_91_ADC_DAC_MIX 0x91
+#define MA5311_DAC_A1_TOP_MAN1_FOR_SDO 0xa1
+#define MA5311_DAC_A2_TOP_MAN2 0xa2
+#define MA5311_DAC_A3_TOP_MAN3 0xa3
+#define MA5311_DAC_A4_BLOCK_CONTROL 0xa4
+#define MA5311_DAC_A5_VALUE_CONTROL 0xa5
+#define MA5311_DAC_A6_VALUE_CONTROL2 0xa6
+#define MA5311_DAC_A7_DT_CONTROL 0xa7
+#define MA5311_DAC_A8_SLOPE_CONTROL 0xa8
+#define MA5311_DAC_A9_FDPEC_CONTROL 0xa9
+#define MA5311_DAC_AA_LDO_CONTROL 0xaa
+#define MA5311_DAC_AB_PRESCALER 0xab
+#define MA5311_DAC_AC_MBD_COMP_LIM1 0xac
+#define MA5311_DAC_AD_MBD_COMP_LIM2 0xad
+#define MA5311_DAC_AE_MBD_COMP_LIM3 0xae
+#define MA5311_DAC_AF_MBD_COMP_LIM4 0xaf
+#define MA5311_DAC_B0_BPD_COMP_LIM1 0xb0
+#define MA5311_DAC_B1_BPD_COMP_LIM2 0xb1
+#define MA5311_DAC_B2_BPD_COMP_LIM3 0xb2
+#define MA5311_DAC_B3_BPD_COMP_LIM4 0xb3
+#define MA5311_DAC_B4_DSP_SPK_CTRL 0xb4
+#define MA5311_DAC_B5_MCLKO_TEST 0xb5
+#define MA5311_DAC_B7_TOP_MAN4 0xb7
+#define MA5311_DAC_B8_TOP_MAN5 0xb8
+#define MA5311_DAC_B9_PLL_A_SETTING 0xb9
+#define MA5311_DAC_BB_CLASS_G_LEVEL1 0xbb
+#define MA5311_DAC_BC_CLASS_G_LEVEL2 0xbc
+#define MA5311_DAC_BD_TONE_GENERATOR 0xbd
+#define MA5311_DAC_BE_TONE_FINE_VOLUME 0xbe
+#define MA5311_DAC_BF_POWER_METER 0xbf
+#define MA5311_DAC_EA_DAC_STATUS 0xea
+#define MA5311_DAC_EB_DAC_OCP_STATUS 0xeb
+#define MA5311_DAC_EC_DAC_SCP_STATUS 0xec
+#define MA5311_DAC_FB_STATUS2 0xfb
+#define MA5311_DAC_FC_STATUS3 0xfc
+#define MA5311_DAC_FD_POWER_METER_DISPLAY 0xfd
+#define MA5311_DAC_FF_VERSION 0xff
+
+/* DAC/control register masks and values */
+
+#define MA5311_POWER_MASK BIT(0)
+#define MA5311_POWER_OFF 0
+#define MA5311_POWER_ON BIT(0)
+
+#define MA5311_DAC_MUTE_VOL_MASK GENMASK(1, 0)
+#define MA5311_SPK_MUTE 0x03
+#define MA5311_SPK_UNMUTE 0x00
+
+/* 0x10: Speaker mode */
+#define MA5311_SPK_MODE_MASK GENMASK(4, 2)
+#define MA5311_SPK_MONO_LEFT 0x02
+#define MA5311_SPK_MONO_RIGHT 0x04
+#define MA5311_SPK_MONO_BOTH 0x0c
+#define MA5311_SPK_STEREO 0x10
+#define MA5311_SPK_OFF 0x00
+
+/* 0x37: TDM receive slot positions */
+#define MA5311_TDM_SLOT0_RX_POS_MASK GENMASK(5, 3)
+#define MA5311_TDM_SLOT0_RX_POS_0 0x00
+#define MA5311_TDM_SLOT0_RX_POS_1 0x08
+#define MA5311_TDM_SLOT0_RX_POS_2 0x10
+#define MA5311_TDM_SLOT0_RX_POS_3 0x18
+#define MA5311_TDM_SLOT0_RX_POS_4 0x20
+#define MA5311_TDM_SLOT0_RX_POS_5 0x28
+#define MA5311_TDM_SLOT0_RX_POS_6 0x30
+#define MA5311_TDM_SLOT0_RX_POS_7 0x38
+
+#define MA5311_TDM_SLOT1_RX_POS_MASK GENMASK(2, 0)
+#define MA5311_TDM_SLOT1_RX_POS_0 0x00
+#define MA5311_TDM_SLOT1_RX_POS_1 0x01
+#define MA5311_TDM_SLOT1_RX_POS_2 0x02
+#define MA5311_TDM_SLOT1_RX_POS_3 0x03
+#define MA5311_TDM_SLOT1_RX_POS_4 0x04
+#define MA5311_TDM_SLOT1_RX_POS_5 0x05
+#define MA5311_TDM_SLOT1_RX_POS_6 0x06
+#define MA5311_TDM_SLOT1_RX_POS_7 0x07
+
+#define MA5311_TDM_DL_MASK BIT(7)
+#define MA5311_TDM_DL_16 0
+#define MA5311_TDM_DL_32 BIT(7)
+
+#define MA5311_TDM_N_SLOT_MASK BIT(6)
+#define MA5311_TDM_N_SLOT_4 0
+#define MA5311_TDM_N_SLOT_8 BIT(6)
+
+#define MA5311_TDM_SLOT0_TX_POS_MASK GENMASK(5, 3)
+#define MA5311_TDM_SLOT0_TX_POS_0 0x00
+#define MA5311_TDM_SLOT0_TX_POS_1 0x08
+#define MA5311_TDM_SLOT0_TX_POS_2 0x10
+#define MA5311_TDM_SLOT0_TX_POS_3 0x18
+#define MA5311_TDM_SLOT0_TX_POS_4 0x20
+#define MA5311_TDM_SLOT0_TX_POS_5 0x28
+#define MA5311_TDM_SLOT0_TX_POS_6 0x30
+#define MA5311_TDM_SLOT0_TX_POS_7 0x38
+
+#define MA5311_TDM_SLOT1_TX_POS_MASK GENMASK(2, 0)
+#define MA5311_TDM_SLOT1_TX_POS_0 0x00
+#define MA5311_TDM_SLOT1_TX_POS_1 0x01
+#define MA5311_TDM_SLOT1_TX_POS_2 0x02
+#define MA5311_TDM_SLOT1_TX_POS_3 0x03
+#define MA5311_TDM_SLOT1_TX_POS_4 0x04
+#define MA5311_TDM_SLOT1_TX_POS_5 0x05
+#define MA5311_TDM_SLOT1_TX_POS_6 0x06
+#define MA5311_TDM_SLOT1_TX_POS_7 0x07
+
+#define MA5311_ADC_TDM2_SETTING_MASK GENMASK(6, 0)
+
+/* 0x39: SDO data path */
+#define MA5311_DAC_SDO_DATA_PATH_MASK GENMASK(3, 2)
+
+/* 0x91: ADC-to-DAC mix */
+#define MA5311_DAC_ADC_DAC_MIX_CH_MASK GENMASK(1, 0)
+
+/* 0xA1: SDO output format and word length */
+#define MA5311_TOP_MAN1_O_FORMAT_MASK GENMASK(7, 5)
+#define MA5311_TOP_MAN1_O_FORMAT_I2S 0x40
+#define MA5311_TOP_MAN1_O_FORMAT_TDM 0x80
+#define MA5311_TOP_MAN1_SOO_LENGTH_MASK GENMASK(2, 1)
+#define MA5311_TOP_MAN1_SDO_16B 0x04
+#define MA5311_TOP_MAN1_SDO_24B 0x00
+
+/* 0xA2: PLL power */
+#define MA5311_PLL_MASK BIT(6)
+#define MA5311_PLL_ON 0
+#define MA5311_PLL_OFF BIT(6)
+
+/* 0xFF: Device identification and revision */
+#define MA5311_DAC_VERSION_MASK GENMASK(7, 0)
+#define MA5311_DEVICE_ID 0x0bU
+#define MA5311_REV_NUM_STATUS GENMASK(7, 0)
+
+/* ADC register addresses */
+/* ADC CH1(L/R) */
+/* General Settings */
+#define MA5311_ADC_CH1_00_GENERAL_SETTING 0x00
+#define MA5311_ADC_CH1_01_DMIC_ALC1 0x01
+#define MA5311_ADC_CH1_02_ALC2 0x02
+#define MA5311_ADC_CH1_03_ALC3 0x03
+#define MA5311_ADC_CH1_04_ALC4 0x04
+#define MA5311_ADC_CH1_05_ALC5 0x05
+#define MA5311_ADC_CH1_06_ALC6 0x06
+#define MA5311_ADC_CH1_07_VOLUME_LEFT 0x07
+#define MA5311_ADC_CH1_08_VOLUME_RIGHT 0x08
+#define MA5311_ADC_CH1_09_PGA_LEFT 0x09
+#define MA5311_ADC_CH1_0A_PGA_RIGHT 0x0a
+#define MA5311_ADC_CH1_0B_AFE 0x0b
+#define MA5311_ADC_CH1_0C_VOLUME_BOOST_LINE1 0x0c
+#define MA5311_ADC_CH1_0D_VOLUME_BOOST_LINE2 0x0d
+#define MA5311_ADC_CH1_0E_MUTE_DATA_FLOW 0x0e
+#define MA5311_ADC_CH1_0F_OUTPUT_FORMAT 0x0f
+
+/* ADC CH2(L/R) */
+#define MA5311_ADC_CH2_10_GENERAL_SETTING 0x10
+#define MA5311_ADC_CH2_11_DMIC_ALC1 0x11
+#define MA5311_ADC_CH2_12_ALC2 0x12
+#define MA5311_ADC_CH2_13_ALC3 0x13
+#define MA5311_ADC_CH2_14_ALC4 0x14
+#define MA5311_ADC_CH2_15_ALC5 0x15
+#define MA5311_ADC_CH2_16_ALC6 0x16
+#define MA5311_ADC_CH2_17_VOLUME_LEFT 0x17
+#define MA5311_ADC_CH2_18_VOLUME_RIGHT 0x18
+#define MA5311_ADC_CH2_19_PGA_LEFT 0x19
+#define MA5311_ADC_CH2_1A_PGA_RIGHT 0x1a
+#define MA5311_ADC_CH2_1B_AFE 0x1b
+#define MA5311_ADC_CH2_1C_VOLUME_BOOST_LINE1 0x1c
+#define MA5311_ADC_CH2_1D_VOLUME_BOOST_LINE2 0x1d
+#define MA5311_ADC_CH2_1E_MUTE_DATA_FLOW 0x1e
+#define MA5311_ADC_CH2_1F_OUTPUT_FORMAT 0x1f
+
+/* ADC CH3(L/R) */
+#define MA5311_ADC_CH3_20_GENERAL_SETTING 0x20
+#define MA5311_ADC_CH3_21_DMIC_ALC1 0x21
+#define MA5311_ADC_CH3_22_ALC2 0x22
+#define MA5311_ADC_CH3_23_ALC3 0x23
+#define MA5311_ADC_CH3_24_ALC4 0x24
+#define MA5311_ADC_CH3_25_ALC5 0x25
+#define MA5311_ADC_CH3_26_ALC6 0x26
+#define MA5311_ADC_CH3_27_VOLUME_LEFT 0x27
+#define MA5311_ADC_CH3_28_VOLUME_RIGHT 0x28
+#define MA5311_ADC_CH3_29_PGA_LEFT 0x29
+#define MA5311_ADC_CH3_2A_PGA_RIGHT 0x2a
+#define MA5311_ADC_CH3_2B_AFE 0x2b
+#define MA5311_ADC_CH3_2C_VOLUME_BOOST_LINE1 0x2c
+#define MA5311_ADC_CH3_2D_VOLUME_BOOST_LINE2 0x2d
+#define MA5311_ADC_CH3_2E_MUTE_DATA_FLOW 0x2e
+#define MA5311_ADC_CH3_2F_OUTPUT_FORMAT 0x2f
+
+/* ADC CH4(L/R) */
+#define MA5311_ADC_CH4_30_GENERAL_SETTING 0x30
+#define MA5311_ADC_CH4_31_DMIC_ALC1 0x31
+#define MA5311_ADC_CH4_32_ALC2 0x32
+#define MA5311_ADC_CH4_33_ALC3 0x33
+#define MA5311_ADC_CH4_34_ALC4 0x34
+#define MA5311_ADC_CH4_35_ALC5 0x35
+#define MA5311_ADC_CH4_36_ALC6 0x36
+#define MA5311_ADC_CH4_37_VOLUME_LEFT 0x37
+#define MA5311_ADC_CH4_38_VOLUME_RIGHT 0x38
+#define MA5311_ADC_CH4_39_PGA_LEFT 0x39
+#define MA5311_ADC_CH4_3A_PGA_RIGHT 0x3a
+#define MA5311_ADC_CH4_3B_AFE 0x3b
+#define MA5311_ADC_CH4_3C_VOLUME_BOOST_LINE1 0x3c
+#define MA5311_ADC_CH4_3D_VOLUME_BOOST_LINE2 0x3d
+#define MA5311_ADC_CH4_3E_MUTE_DATA_FLOW 0x3e
+#define MA5311_ADC_CH4_3F_OUTPUT_FORMAT 0x3f
+
+/* ADC register masks and values */
+#define MA5311_ADC_HPF_ENABLE_MASK BIT(7)
+#define MA5311_ADC_OSR_SDM_MASK BIT(6)
+#define MA5311_ADC_SAMPLE_RATE_MASK GENMASK(4, 2)
+#define MA5311_ADC_SAMPLE_RATE_192K 0x00U
+#define MA5311_ADC_SAMPLE_RATE_96K 0x04U
+#define MA5311_ADC_SAMPLE_RATE_48K 0x08U
+#define MA5311_ADC_SAMPLE_RATE_24K 0x10U
+#define MA5311_ADC_SAMPLE_RATE_12K 0x18U
+#define MA5311_ADC_OSR_DEC_MASK BIT(1)
+#define MA5311_ADC_POWER_MASK BIT(0)
+#define MA5311_ADC_POWER_ON 0U
+#define MA5311_ADC_POWER_DOWN BIT(0)
+
+#define MA5311_ADC_SYSCLK_MASK GENMASK(7, 6)
+#define MA5311_ADC_DMIC_CLOCK_MASK BIT(5)
+#define MA5311_ADC_DMIC_MODE_MASK BIT(4)
+#define MA5311_ADC_ALC_SELECT_MASK GENMASK(1, 0)
+
+#define MA5311_ADC_VOL_CTRL_MASK GENMASK(7, 0)
+#define MA5311_ADC_PGA_CTRL_MASK GENMASK(5, 0)
+
+#define MA5311_ADC_LRCK_POL_MASK BIT(7)
+#define MA5311_ADC_SCK_POL_MASK BIT(6)
+#define MA5311_ADC_SWAP_LR_MASK BIT(5)
+#define MA5311_ADC_MUTE_SLOPE_MASK GENMASK(4, 3)
+#define MA5311_ADC_MUTE_RIGHT_MASK BIT(2)
+#define MA5311_ADC_MUTE_LEFT_MASK BIT(1)
+
+#define MA5311_ADC_LEFT_MUTE BIT(1)
+#define MA5311_ADC_LEFT_DEMUTE 0
+#define MA5311_ADC_RIGHT_MUTE BIT(2)
+#define MA5311_ADC_RIGHT_DEMUTE 0
+
+#define MA5311_ADC_MUTE_BOTH_MASK GENMASK(2, 1)
+#define MA5311_ADC_BOTH_MUTE 0x06
+#define MA5311_ADC_BOTH_DEMUTE 0x00
+
+#define MA5311_ADC_OUTPUT_MASTER_MASK BIT(7)
+#define MA5311_ADC_OUTPUT_SCK_RATE_MASK GENMASK(6, 5)
+#define MA5311_ADC_OUTPUT_WORD_LENGTH_MASK GENMASK(4, 3)
+#define MA5311_ADC_OUTPUT_FORMAT_MASK GENMASK(2, 0)
+#define MA5311_ADC_OUTPUT_FORMAT_RJ 0x00U
+#define MA5311_ADC_OUTPUT_FORMAT_LJ 0x01U
+#define MA5311_ADC_OUTPUT_FORMAT_I2S 0x02U
+#define MA5311_ADC_OUTPUT_FORMAT_SPI 0x03U
+#define MA5311_ADC_OUTPUT_FORMAT_PCM_SHORT 0x04U
+
+#define MA5311_ADC_WORD_LENGTH_24 0x00
+#define MA5311_ADC_WORD_LENGTH_16 0x18
+
+#define MA5311_EXTERNAL_CLOCK_19_2 0x00
+#define MA5311_EXTERNAL_CLOCK_24_576 0x01
+#define MA5311_PLL_CLKIN_MCLK 0x02
+#define MA5311_PLL_CLKIN_BCLK 0x03
+
+#define MA5311_RESET_CTRL_NAME "RSTN Switch"
+#define MA5311_LEFT_VOL_CTRL_NAME "Left Speaker Volume"
+#define MA5311_RIGHT_VOL_CTRL_NAME "Right Speaker Volume"
+
+#define MA5311_ADC1_LEFT_VOL_CTRL_NAME "ADC1 Left Volume"
+#define MA5311_ADC1_RIGHT_VOL_CTRL_NAME "ADC1 Right Volume"
+#define MA5311_ADC1_LEFT_PGA_CTRL_NAME "ADC1 Left PGA"
+#define MA5311_ADC1_RIGHT_PGA_CTRL_NAME "ADC1 Right PGA"
+
+#define MA5311_ADC2_LEFT_VOL_CTRL_NAME "ADC2 Left Volume"
+#define MA5311_ADC2_RIGHT_VOL_CTRL_NAME "ADC2 Right Volume"
+#define MA5311_ADC2_LEFT_PGA_CTRL_NAME "ADC2 Left PGA"
+#define MA5311_ADC2_RIGHT_PGA_CTRL_NAME "ADC2 Right PGA"
+
+#define MA5311_ADC3_LEFT_VOL_CTRL_NAME "ADC3 Left Volume"
+#define MA5311_ADC3_RIGHT_VOL_CTRL_NAME "ADC3 Right Volume"
+#define MA5311_ADC3_LEFT_PGA_CTRL_NAME "ADC3 Left PGA"
+#define MA5311_ADC3_RIGHT_PGA_CTRL_NAME "ADC3 Right PGA"
+
+#define MA5311_ADC4_LEFT_VOL_CTRL_NAME "ADC4 Left Volume"
+#define MA5311_ADC4_RIGHT_VOL_CTRL_NAME "ADC4 Right Volume"
+#define MA5311_ADC4_LEFT_PGA_CTRL_NAME "ADC4 Left PGA"
+#define MA5311_ADC4_RIGHT_PGA_CTRL_NAME "ADC4 Right PGA"
+
+#define MA5311_CAPTURE_NAME "Capture Mode"
+#define MA5311_PLAYBACK_MODE "Playback Mode"
+
+#define MA5311_AIF_IN_NAME "AIF IN Source"
+#define MA5311_AIF_OUT0_NAME "AIF OUT0 Source"
+#define MA5311_AIF_OUT1_NAME "AIF OUT1 Source"
+
+#define MA5311_CONTROLLER_DEVICE_MASK BIT(7)
+#define MA5311_DEVICE_MODE 0
+#define MA5311_CONTROLLER_MODE BIT(7)
+
+#define MA5311_SCK_RISING_MASK BIT(2)
+#define MA5311_SCK_RISING_EDGE BIT(2)
+#define MA5311_SCK_FALLING_EDGE 0
+
+#define MA5311_LEFTPOL_MASK BIT(3)
+#define MA5311_LOW_FIRST_CH 0
+#define MA5311_HIGH_FIRST_CH BIT(3)
+
+enum ma5311_mode {
+ MA5311_MONO_BOTH_MODE,
+ MA5311_STEREO_MODE,
+ MA5311_MONO_LEFT_MODE,
+ MA5311_MONO_RIGHT_MODE,
+};
+
+enum ma5311_setting {
+ MA5311_SPEAKER_MODE,
+ MA5311_RECEIVER_MODE,
+};
+
+enum ma5311_adc_mode {
+ MA5311_I2S_MODE_ADC1,
+ MA5311_I2S_MODE_ADC2,
+ MA5311_I2S_MODE_ADC3,
+ MA5311_I2S_MODE_ADC4,
+ MA5311_TDM_MODE,
+};
+
+#define MA5311_TSD_FAULT_STATUS BIT(2)
+
+/* Field encodings used by the existing serial audio configuration. */
+#define MA5311_DAC_A1_SCK_RATE_MASK GENMASK(4, 3)
+#define MA5311_DAC_A1_SCK_RATE_32FS 0x18
+#define MA5311_DAC_A1_SCK_RATE_64FS 0x00
+
+/* MA5311_DAC_38_TDM2 data length field. */
+#define MA5311_DAC_38_DATA_LEN_MASK BIT(7)
+#define MA5311_DAC_38_DATA_LEN_16 0
+#define MA5311_DAC_38_DATA_LEN_32 BIT(7)
+#define MA5311_DAC_38_CAPTURE_8CH_CFG 0xc1
+
+/* Full-register values: keep the other DATA_PATH bits cleared. */
+#define MA5311_DATA_PATH_I2S_ADC1 0x00
+#define MA5311_DATA_PATH_I2S_ADC2 0x04
+#define MA5311_DATA_PATH_I2S_ADC3 0x08
+#define MA5311_DATA_PATH_I2S_ADC4 0x0c
+#define MA5311_DATA_PATH_TDM_8CH 0x01
+#define MA5311_TDM_SLOT_ENABLE_ALL 0xff
+
+/* Preserve the SCK_RATE encoding used for 16-bit ADC capture. */
+#define MA5311_ADC_SCK_RATE_16BIT_CFG 0x40
+#define MA5311_ADC_SCK_RATE_24BIT_CFG 0x00
+
+/* 0xEA: DAC fault status */
+#define MA5311_DAC_EA_FAULT_MASK BIT(7)
+#define MA5311_DAC_EA_UVLON_CH1_MASK BIT(6)
+#define MA5311_DAC_EA_UVLON_CH2_MASK BIT(5)
+#define MA5311_DAC_EA_TSDW_MASK BIT(3)
+#define MA5311_DAC_EA_TSD_MASK BIT(2)
+
+/* 0xEB: DAC OCP status */
+#define MA5311_DAC_EB_IPSC_2U_MASK BIT(7)
+#define MA5311_DAC_EB_IPSC_2L_MASK BIT(6)
+#define MA5311_DAC_EB_IPSC_1U_MASK BIT(5)
+#define MA5311_DAC_EB_IPSC_1L_MASK BIT(4)
+#define MA5311_DAC_EB_OCP_2B_MASK BIT(3)
+#define MA5311_DAC_EB_OCP_2A_MASK BIT(2)
+#define MA5311_DAC_EB_OCP_1B_MASK BIT(1)
+#define MA5311_DAC_EB_OCP_1A_MASK BIT(0)
+
+/* 0xEC: DAC SCP status */
+#define MA5311_DAC_EC_SCP_2B_MASK BIT(3)
+#define MA5311_DAC_EC_SCP_2A_MASK BIT(2)
+#define MA5311_DAC_EC_SCP_1B_MASK BIT(1)
+#define MA5311_DAC_EC_SCP_1A_MASK BIT(0)
+
+/* 0xFB: Clock fault status */
+#define MA5311_DAC_FB_CLOCK_MON_MASK BIT(0)
+
+#endif /* _MA5311_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 1/2] ASoC: dt-bindings: Add Iron Device MA5311
2026-10-15 6:04 ` [PATCH 1/2] ASoC: dt-bindings: Add " Hyunuk Son
@ 2026-10-15 6:09 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-10-15 6:09 UTC (permalink / raw)
To: Hyunuk Son; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] dt-bindings: additionalProperties: false with allOf breaks inherited properties
--
commit 28fede9028d5369d7e76bfe368a2b05ad5391a52
Author: Hyunuk Son <hyunuk.son@irondevice.com>
ASoC: dt-bindings: Add Iron Device MA5311
Add a Device Tree binding for the Iron Device MA5311 audio codec.
> diff --git a/Documentation/devicetree/bindings/sound/irondevice,ma5311.yaml b/Documentation/devicetree/bindings/sound/irondevice,ma5311.yaml
> new file mode 100644
> index 0000000000000..ae4a13c2981ab
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/irondevice,ma5311.yaml
> @@ -0,0 +1,69 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/sound/irondevice,ma5311.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
[ ... ]
> +allOf:
> + - $ref: dai-common.yaml#
> +
> +properties:
[ ... ]
> +required:
> + - compatible
> + - reg
> + - reg-names
> + - '#sound-dai-cells'
> +
> +additionalProperties: false
[Severity: Low]
Because this schema includes dai-common.yaml# in its allOf block, will
additionalProperties: false incorrectly reject standard DAI properties?
In JSON schema, additionalProperties: false only evaluates explicitly defined
properties in the local properties dictionary, ignoring properties brought in
via a $ref. This causes make dt_binding_check to fail with "additional
property is not allowed" errors when valid common DAI properties are used
in the device tree.
Should this use unevaluatedProperties: false instead, as documented in
writing-schema.rst?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261015060408.128711-1-hyunuk.son@irondevice.com?part=1
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] ASoC: codecs: Add driver for Iron Device MA5311
2026-10-15 6:04 ` [PATCH 2/2] ASoC: codecs: Add driver for " Hyunuk Son
@ 2026-10-15 6:14 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-10-15 6:14 UTC (permalink / raw)
To: Hyunuk Son; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] asoc: missing symmetric constraints allows concurrent PLL reconfiguration
- [Medium] irq: thread handler incorrectly returns negative error codes
- [Medium] asoc: uninitialized kernel stack memory leak in ALSA control getters
- [Medium] asoc: hardware reset ALSA control irrevocably desynchronizes DAPM state
- [Medium] asoc: falsely advertised 44.1kHz capability fails unconditionally
- [Medium] asoc: ma5311: TOCTOU race between capture_mode_put and DAPM events
- [Low] i2c: missing named initializers for i2c_device_id array
--
commit cea868f0ad866edece054bdcc99ee119173a63e4
Author: Hyunuk Son <hyunuk.son@irondevice.com>
Date: Thu Oct 15 06:04:07 2026 +0000
ASoC: codecs: Add driver for Iron Device MA5311
Add an ASoC codec driver for the Iron Device MA5311.
The driver supports audio playback and capture through I2S
and TDM interfaces. It provides ALSA mixer controls for audio
configuration and uses regmap for register access.
> diff --git a/sound/soc/codecs/ma5311.c b/sound/soc/codecs/ma5311.c
> new file mode 100644
> index 0000000000000..ea6d6793bfb21
> --- /dev/null
> +++ b/sound/soc/codecs/ma5311.c
[ ... ]
> +static int ma5311_adc_volume_control_get(struct snd_kcontrol *kcontrol,
> + struct snd_ctl_elem_value *ucontrol)
> +{
> + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
> + struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
> + const struct soc_mixer_control *mc =
> + (const struct soc_mixer_control *)kcontrol->private_value;
> + unsigned int *cache = ma5311_adc_volume_control_cache(ma5311, mc->reg);
> + unsigned int val;
> +
> + if (!cache)
> + return -EINVAL;
> +
> + regmap_read(ma5311->adc_regmap, mc->reg, &val);
> +
> + ucontrol->value.integer.value[0] = val & MA5311_ADC_VOL_CTRL_MASK;
[Severity: Medium]
Does this code leak uninitialized kernel stack memory to userspace if the I2C
transaction fails? The driver uses REGCACHE_NONE, so every read triggers a
physical I2C transaction. If regmap_read() fails and returns a negative error
code, val remains uninitialized, but it is masked and copied to the ucontrol
value anyway.
> + *cache = ucontrol->value.integer.value[0];
> +
> + return 0;
> +}
> +
> +static int ma5311_adc_volume_control_put(struct snd_kcontrol *kcontrol,
> + struct snd_ctl_elem_value *ucontrol)
> +{
> + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
> + struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
> + const struct soc_mixer_control *mc =
> + (const struct soc_mixer_control *)kcontrol->private_value;
> + unsigned int val = ucontrol->value.integer.value[0];
> + unsigned int *cache;
> +
> + if (val < 0 || val > MA5311_ADC_VOL_CTRL_MASK)
> + return -EINVAL;
> +
> + cache = ma5311_adc_volume_control_cache(ma5311, mc->reg);
> +
> + if (!cache)
> + return -EINVAL;
> +
> + if (*cache == val)
> + return 0;
> +
> + regmap_update_bits(ma5311->adc_regmap, mc->reg,
> + MA5311_ADC_VOL_CTRL_MASK, val);
[Severity: Medium]
Should the software cache update be skipped if the regmap update fails? The
return value of regmap_update_bits() is ignored here, which silently updates
the software cache even when the hardware I2C write fails.
> +
> + *cache = val;
> +
> + return 1;
> +}
[ ... ]
> +static int ma5311_capture_mode_put(struct snd_kcontrol *kcontrol,
> + struct snd_ctl_elem_value *ucontrol)
> +{
> + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
> + struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
> + unsigned int val = ucontrol->value.enumerated.item[0];
> +
> + if (val >= ARRAY_SIZE(ma5311_capture_mode_text)) {
> + dev_err(ma5311->dev, "%s: Out of range\n", __func__);
> + return -EINVAL;
> + }
> +
> + if (ma5311->capture_mode == val)
> + return 0;
> +
> + if (ma5311->adc_power_status) {
[Severity: Medium]
Could this lockless check of adc_power_status lead to a TOCTOU race window?
If a user-space application writes to this mixer control concurrently with
the start or stop of a stream, ma5311_adc_event() (which holds the
dapm_mutex) might update the hardware state right after this check passes,
leading to corrupted audio capture and an inconsistent hardware state.
> + dev_err(ma5311->dev, "Try after capture is complete.\n");
> + return -EBUSY;
> + }
[ ... ]
> +static int ma5311_reset_control_put(struct snd_kcontrol *kcontrol,
> + struct snd_ctl_elem_value *ucontrol)
> +{
> + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
> + struct ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
> + unsigned int val = ucontrol->value.enumerated.item[0];
> + int ret;
> +
> + if (!val)
> + return 0;
> +
> + if (!ma5311->reset_gpio)
> + return -ENODEV;
> +
> + ma5311->last_bclk = 0;
> +
> + gpiod_set_value_cansleep(ma5311->reset_gpio, 1);
> + msleep(MA5311_RESET_ASSERT_MS);
> + gpiod_set_value_cansleep(ma5311->reset_gpio, 0);
> + msleep(MA5311_RESET_RECOVERY_MS);
> +
> + ret = ma5311_reset(component);
[Severity: Medium]
Will this hardware reset irrevocably desynchronize the DAPM state if
triggered during an active stream? Because the regmaps use REGCACHE_NONE and
this operation holds no stream locks, the ALSA framework cannot automatically
restore the previously configured dynamic state (PLL settings, DAPM routing,
etc) after the reset.
> + dev_dbg(component->dev, "Hardware Reset Done\n");
> + return ret;
> +}
[ ... ]
> +static int ma5311_dai_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 ma5311_priv *ma5311 = snd_soc_component_get_drvdata(component);
> + unsigned int bclk = 0;
> + int ret;
> +
> + switch (params_rate(params)) {
> + case 8000:
> + case 12000:
> + case 16000:
> + case 24000:
> + case 32000:
> + case 44100:
[Severity: Medium]
Does the advertised support for 44.1kHz streams unconditionally fail in
ma5311_setup_pll()? The hardware parameters explicitly accept 44100 here,
but the ma5311_pll_matches table only contains exact multiples of 48kHz,
which will result in no matching BCLK entry and a failure to configure
the PLL.
> + case 48000:
> + case 96000:
> + break;
[ ... ]
> +static struct snd_soc_dai_driver ma5311_dai[] = {
> + {
> + .name = "ma5311-amplifier",
> + .id = 0,
> + .playback = {
> + .stream_name = "Playback",
> + .channels_min = 1,
> + .channels_max = 2,
> + .rates = MA5311_RATES_PLAYBACK,
> + .formats = MA5311_FORMATS,
> + },
> + .capture = {
> + .stream_name = "Capture",
> + .channels_min = 1,
> + .channels_max = 8,
> + .rates = MA5311_RATES_CAPTURE,
> + .formats = MA5311_FORMATS,
> + },
> + .ops = &ma5311_dai_ops_amp,
[Severity: High]
Is it possible for concurrent playback and capture streams with different
rates to silently overwrite the shared PLL settings? Without the
.symmetric_rates = 1, .symmetric_channels = 1, and .symmetric_samplebits = 1
flags, ALSA won't enforce symmetry. This means a second stream could
reconfigure the PLL via ma5311_setup_pll() and instantly break the clock and
format for the first stream.
> + },
> +};
[ ... ]
> +static irqreturn_t ma5311_irq_thread(int irq, void *data)
> +{
> + struct ma5311_priv *ma5311 = data;
> + unsigned int dac_status;
> + unsigned int dac_ocp_status;
> + unsigned int dac_scp_status;
> + int ret = 0;
> +
> + ret |= regmap_read(ma5311->ctrl_regmap,
> + MA5311_DAC_EA_DAC_STATUS, &dac_status);
> +
> + ret |= regmap_read(ma5311->ctrl_regmap,
> + MA5311_DAC_EB_DAC_OCP_STATUS, &dac_ocp_status);
> +
> + ret |= regmap_read(ma5311->ctrl_regmap,
> + MA5311_DAC_EC_DAC_SCP_STATUS, &dac_scp_status);
> +
> + if (ret) {
> + dev_crit(ma5311->dev,
> + "Error Occurred During Interrupt Handling\n");
> + return ret;
[Severity: Medium]
Should this return IRQ_NONE instead of the negative error code from
regmap_read()? Returning a negative errno value from an IRQ thread handler
causes the core IRQ subsystem to misinterpret the state, which can trigger
spurious interrupt warnings and permanently disable the interrupt line.
> + }
[ ... ]
> +static const struct i2c_device_id ma5311_i2c_id[] = { { "ma5311", 0 }, { } };
[Severity: Low]
Could you use named initializers for this array element? The global I2C
subsystem guidelines require arrays of type struct i2c_device_id to use named
initializers (e.g., { .name = "ma5311", .driver_data = 0 }).
> +
> +MODULE_DEVICE_TABLE(i2c, ma5311_i2c_id);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261015060408.128711-1-hyunuk.son@irondevice.com?part=2
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-15 6:14 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-15 6:04 [PATCH 0/2] ASoC: Add driver for Iron Device MA5311 Hyunuk Son
2026-10-15 6:04 ` [PATCH 1/2] ASoC: dt-bindings: Add " Hyunuk Son
2026-10-15 6:09 ` sashiko-bot
2026-10-15 6:04 ` [PATCH 2/2] ASoC: codecs: Add driver for " Hyunuk Son
2026-10-15 6:14 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox