2024-03-14 23:23:57

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 00/25] Introduce support of audio for Amlogic A1 SoC family

This series includes the following:

- new audio clock and reset controller data and adaptation for it of existing
code (patches 0001..0004);

- adaptation of existing audio components for A1 Soc (patches 0005..0021);

- handy cosmetics for dai-link naming (patches 0022..0023);

- integration of audio devices into common trees (patch 0024);

- audio support bring up on Amlogic ad402 reference board (patch 0025). This
patch is not actually checked on real hardware (because all ad402 that we had
were burned out). This patch is based on ad402's schematics and on experience
with our own hardware (which is very close to reference board);

Dmitry Rokosov (2):
ASoC: dt-bindings: meson: introduce link-name optional property
ASoC: meson: implement link-name optional property in meson card utils

Jan Dakinevich (23):
clk: meson: a1: restrict an amount of 'hifi_pll' params
clk: meson: axg: move reset controller's code to separate module
dt-bindings: clock: meson: add A1 audio clock and reset controller
bindings
clk: meson: a1: add the audio clock controller driver
ASoC: meson: codec-glue: add support for capture stream
ASoC: meson: g12a-toacodec: fix "Lane Select" width
ASoC: meson: g12a-toacodec: rework the definition of bits
ASoC: dt-bindings: meson: g12a-toacodec: add support for A1 SoC family
ASoC: meson: g12a-toacodec: add support for A1 SoC family
ASoC: meson: t9015: prepare to adding new platforms
ASoC: dt-bindings: meson: t9015: add support for A1 SoC family
ASoC: meson: t9015: add support for A1 SoC family
ASoC: dt-bindings: meson: axg-pdm: document 'sysrate' property
ASoC: meson: axg-pdm: introduce 'sysrate' property
pinctrl/meson: fix typo in PDM's pin name
ASoC: dt-bindings: meson: meson-axg-audio-arb: claim support of A1 SoC
family
ASoC: dt-bindings: meson: axg-fifo: claim support of A1 SoC family
ASoC: dt-bindings: meson: axg-pdm: claim support of A1 SoC family
ASoC: dt-bindings: meson: axg-sound-card: claim support of A1 SoC
family
ASoC: dt-bindings: meson: axg-tdm-formatters: claim support of A1 SoC
family
ASoC: dt-bindings: meson: axg-tdm-iface: claim support of A1 SoC
family
arm64: dts: meson: a1: add audio devices
arm64: dts: ad402: enable audio

.../bindings/clock/amlogic,a1-audio-clkc.yaml | 83 +++
.../reset/amlogic,meson-axg-audio-arb.yaml | 10 +-
.../bindings/sound/amlogic,axg-fifo.yaml | 8 +
.../bindings/sound/amlogic,axg-pdm.yaml | 5 +
.../sound/amlogic,axg-sound-card.yaml | 12 +-
.../sound/amlogic,axg-tdm-formatters.yaml | 22 +-
.../bindings/sound/amlogic,axg-tdm-iface.yaml | 6 +-
.../bindings/sound/amlogic,g12a-toacodec.yaml | 1 +
.../bindings/sound/amlogic,gx-sound-card.yaml | 6 +
.../bindings/sound/amlogic,t9015.yaml | 4 +-
.../arm64/boot/dts/amlogic/meson-a1-ad402.dts | 126 ++++
arch/arm64/boot/dts/amlogic/meson-a1.dtsi | 471 +++++++++++++++
drivers/clk/meson/Kconfig | 18 +
drivers/clk/meson/Makefile | 2 +
drivers/clk/meson/a1-audio.c | 556 ++++++++++++++++++
drivers/clk/meson/a1-audio.h | 58 ++
drivers/clk/meson/a1-pll.c | 8 +-
drivers/clk/meson/axg-audio.c | 95 +--
drivers/clk/meson/meson-audio-rstc.c | 109 ++++
drivers/clk/meson/meson-audio-rstc.h | 12 +
drivers/pinctrl/meson/pinctrl-meson-a1.c | 6 +-
.../dt-bindings/clock/amlogic,a1-audio-clkc.h | 122 ++++
.../reset/amlogic,meson-a1-audio-reset.h | 29 +
.../dt-bindings/sound/meson-g12a-toacodec.h | 5 +
sound/soc/meson/axg-pdm.c | 10 +-
sound/soc/meson/g12a-toacodec.c | 298 ++++++++--
sound/soc/meson/meson-card-utils.c | 12 +-
sound/soc/meson/meson-codec-glue.c | 174 ++++--
sound/soc/meson/meson-codec-glue.h | 23 +
sound/soc/meson/t9015.c | 326 +++++++++-
30 files changed, 2394 insertions(+), 223 deletions(-)
create mode 100644 Documentation/devicetree/bindings/clock/amlogic,a1-audio-clkc.yaml
create mode 100644 drivers/clk/meson/a1-audio.c
create mode 100644 drivers/clk/meson/a1-audio.h
create mode 100644 drivers/clk/meson/meson-audio-rstc.c
create mode 100644 drivers/clk/meson/meson-audio-rstc.h
create mode 100644 include/dt-bindings/clock/amlogic,a1-audio-clkc.h
create mode 100644 include/dt-bindings/reset/amlogic,meson-a1-audio-reset.h

--
2.34.1



2024-03-14 23:24:29

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 04/25] clk: meson: a1: add the audio clock controller driver

This controller provides clocks and reset functionality for audio
peripherals on Amlogic A1 SoC family.

The driver is almost identical to 'axg-audio', however it would be better
to keep it separate due to following reasons:

- significant amount of bits has another definition. I will bring there
a mess of new defines with A1_ suffixes.

- registers of this controller are located in two separate regions. It
will give a lot of complications for 'axg-audio' to support this.

Signed-off-by: Jan Dakinevich <[email protected]>
---
drivers/clk/meson/Kconfig | 13 +
drivers/clk/meson/Makefile | 1 +
drivers/clk/meson/a1-audio.c | 556 +++++++++++++++++++++++++++++++++++
drivers/clk/meson/a1-audio.h | 58 ++++
4 files changed, 628 insertions(+)
create mode 100644 drivers/clk/meson/a1-audio.c
create mode 100644 drivers/clk/meson/a1-audio.h

diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
index d6a2fa5f7e88..80c4a18c83d2 100644
--- a/drivers/clk/meson/Kconfig
+++ b/drivers/clk/meson/Kconfig
@@ -133,6 +133,19 @@ config COMMON_CLK_A1_PERIPHERALS
device, A1 SoC Family. Say Y if you want A1 Peripherals clock
controller to work.

+config COMMON_CLK_A1_AUDIO
+ tristate "Amlogic A1 SoC Audio clock controller support"
+ depends on ARM64
+ select COMMON_CLK_MESON_REGMAP
+ select COMMON_CLK_MESON_CLKC_UTILS
+ select COMMON_CLK_MESON_PHASE
+ select COMMON_CLK_MESON_SCLK_DIV
+ select COMMON_CLK_MESON_AUDIO_RSTC
+ help
+ Support for the Audio clock controller on Amlogic A113L based
+ device, A1 SoC Family. Say Y if you want A1 Audio clock controller
+ to work.
+
config COMMON_CLK_G12A
tristate "G12 and SM1 SoC clock controllers support"
depends on ARM64
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index 88d94921a4dc..4968fc7ad555 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
obj-$(CONFIG_COMMON_CLK_A1_PLL) += a1-pll.o
obj-$(CONFIG_COMMON_CLK_A1_PERIPHERALS) += a1-peripherals.o
+obj-$(CONFIG_COMMON_CLK_A1_AUDIO) += a1-audio.o
obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o
obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o
diff --git a/drivers/clk/meson/a1-audio.c b/drivers/clk/meson/a1-audio.c
new file mode 100644
index 000000000000..6039116c93ba
--- /dev/null
+++ b/drivers/clk/meson/a1-audio.c
@@ -0,0 +1,556 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
+ *
+ * Author: Jan Dakinevich <[email protected]>
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/init.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/reset-controller.h>
+#include <linux/slab.h>
+
+#include "meson-clkc-utils.h"
+#include "meson-audio-rstc.h"
+#include "clk-regmap.h"
+#include "clk-phase.h"
+#include "sclk-div.h"
+#include "a1-audio.h"
+
+#define AUDIO_PDATA(_name) \
+ ((const struct clk_parent_data[]) { { .hw = &(_name).hw } })
+
+#define AUDIO_MUX(_name, _reg, _mask, _shift, _pdata) \
+static struct clk_regmap _name = { \
+ .map = AUDIO_REG_MAP(_reg), \
+ .data = &(struct clk_regmap_mux_data){ \
+ .offset = AUDIO_REG_OFFSET(_reg), \
+ .mask = (_mask), \
+ .shift = (_shift), \
+ }, \
+ .hw.init = &(struct clk_init_data) { \
+ .name = #_name, \
+ .ops = &clk_regmap_mux_ops, \
+ .parent_data = (_pdata), \
+ .num_parents = ARRAY_SIZE(_pdata), \
+ .flags = CLK_SET_RATE_PARENT, \
+ }, \
+}
+
+#define AUDIO_DIV(_name, _reg, _shift, _width, _pdata) \
+static struct clk_regmap _name = { \
+ .map = AUDIO_REG_MAP(_reg), \
+ .data = &(struct clk_regmap_div_data){ \
+ .offset = AUDIO_REG_OFFSET(_reg), \
+ .shift = (_shift), \
+ .width = (_width), \
+ }, \
+ .hw.init = &(struct clk_init_data) { \
+ .name = #_name, \
+ .ops = &clk_regmap_divider_ops, \
+ .parent_data = (_pdata), \
+ .num_parents = 1, \
+ .flags = CLK_SET_RATE_PARENT, \
+ }, \
+}
+
+#define AUDIO_GATE(_name, _reg, _bit, _pdata) \
+static struct clk_regmap _name = { \
+ .map = AUDIO_REG_MAP(_reg), \
+ .data = &(struct clk_regmap_gate_data){ \
+ .offset = AUDIO_REG_OFFSET(_reg), \
+ .bit_idx = (_bit), \
+ }, \
+ .hw.init = &(struct clk_init_data) { \
+ .name = #_name, \
+ .ops = &clk_regmap_gate_ops, \
+ .parent_data = (_pdata), \
+ .num_parents = 1, \
+ .flags = CLK_SET_RATE_PARENT, \
+ }, \
+}
+
+#define AUDIO_SCLK_DIV(_name, _reg, _div_shift, _div_width, \
+ _hi_shift, _hi_width, _pdata, _set_rate_parent) \
+static struct clk_regmap _name = { \
+ .map = AUDIO_REG_MAP(_reg), \
+ .data = &(struct meson_sclk_div_data) { \
+ .div = { \
+ .reg_off = AUDIO_REG_OFFSET(_reg), \
+ .shift = (_div_shift), \
+ .width = (_div_width), \
+ }, \
+ .hi = { \
+ .reg_off = AUDIO_REG_OFFSET(_reg), \
+ .shift = (_hi_shift), \
+ .width = (_hi_width), \
+ }, \
+ }, \
+ .hw.init = &(struct clk_init_data) { \
+ .name = #_name, \
+ .ops = &meson_sclk_div_ops, \
+ .parent_data = (_pdata), \
+ .num_parents = 1, \
+ .flags = (_set_rate_parent) ? CLK_SET_RATE_PARENT : 0, \
+ }, \
+}
+
+#define AUDIO_TRIPHASE(_name, _reg, _width, _shift0, _shift1, _shift2, \
+ _pdata) \
+static struct clk_regmap _name = { \
+ .map = AUDIO_REG_MAP(_reg), \
+ .data = &(struct meson_clk_triphase_data) { \
+ .ph0 = { \
+ .reg_off = AUDIO_REG_OFFSET(_reg), \
+ .shift = (_shift0), \
+ .width = (_width), \
+ }, \
+ .ph1 = { \
+ .reg_off = AUDIO_REG_OFFSET(_reg), \
+ .shift = (_shift1), \
+ .width = (_width), \
+ }, \
+ .ph2 = { \
+ .reg_off = AUDIO_REG_OFFSET(_reg), \
+ .shift = (_shift2), \
+ .width = (_width), \
+ }, \
+ }, \
+ .hw.init = &(struct clk_init_data) { \
+ .name = #_name, \
+ .ops = &meson_clk_triphase_ops, \
+ .parent_data = (_pdata), \
+ .num_parents = 1, \
+ .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \
+ }, \
+}
+
+#define AUDIO_SCLK_WS(_name, _reg, _width, _shift_ph, _shift_ws, \
+ _pdata) \
+static struct clk_regmap _name = { \
+ .map = AUDIO_REG_MAP(_reg), \
+ .data = &(struct meson_sclk_ws_inv_data) { \
+ .ph = { \
+ .reg_off = AUDIO_REG_OFFSET(_reg), \
+ .shift = (_shift_ph), \
+ .width = (_width), \
+ }, \
+ .ws = { \
+ .reg_off = AUDIO_REG_OFFSET(_reg), \
+ .shift = (_shift_ws), \
+ .width = (_width), \
+ }, \
+ }, \
+ .hw.init = &(struct clk_init_data) { \
+ .name = #_name, \
+ .ops = &meson_sclk_ws_inv_ops, \
+ .parent_data = (_pdata), \
+ .num_parents = 1, \
+ .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \
+ }, \
+}
+
+static const struct clk_parent_data a1_pclk_pdata[] = {
+ { .fw_name = "pclk", },
+};
+
+AUDIO_GATE(audio_ddr_arb, AUDIO_CLK_GATE_EN0, 0, a1_pclk_pdata);
+AUDIO_GATE(audio_tdmin_a, AUDIO_CLK_GATE_EN0, 1, a1_pclk_pdata);
+AUDIO_GATE(audio_tdmin_b, AUDIO_CLK_GATE_EN0, 2, a1_pclk_pdata);
+AUDIO_GATE(audio_tdmin_lb, AUDIO_CLK_GATE_EN0, 3, a1_pclk_pdata);
+AUDIO_GATE(audio_loopback, AUDIO_CLK_GATE_EN0, 4, a1_pclk_pdata);
+AUDIO_GATE(audio_tdmout_a, AUDIO_CLK_GATE_EN0, 5, a1_pclk_pdata);
+AUDIO_GATE(audio_tdmout_b, AUDIO_CLK_GATE_EN0, 6, a1_pclk_pdata);
+AUDIO_GATE(audio_frddr_a, AUDIO_CLK_GATE_EN0, 7, a1_pclk_pdata);
+AUDIO_GATE(audio_frddr_b, AUDIO_CLK_GATE_EN0, 8, a1_pclk_pdata);
+AUDIO_GATE(audio_toddr_a, AUDIO_CLK_GATE_EN0, 9, a1_pclk_pdata);
+AUDIO_GATE(audio_toddr_b, AUDIO_CLK_GATE_EN0, 10, a1_pclk_pdata);
+AUDIO_GATE(audio_spdifin, AUDIO_CLK_GATE_EN0, 11, a1_pclk_pdata);
+AUDIO_GATE(audio_resample, AUDIO_CLK_GATE_EN0, 12, a1_pclk_pdata);
+AUDIO_GATE(audio_eqdrc, AUDIO_CLK_GATE_EN0, 13, a1_pclk_pdata);
+AUDIO_GATE(audio_audiolocker, AUDIO_CLK_GATE_EN0, 14, a1_pclk_pdata);
+
+AUDIO_GATE(audio2_ddr_arb, AUDIO2_CLK_GATE_EN0, 0, a1_pclk_pdata);
+AUDIO_GATE(audio2_pdm, AUDIO2_CLK_GATE_EN0, 1, a1_pclk_pdata);
+AUDIO_GATE(audio2_tdmin_vad, AUDIO2_CLK_GATE_EN0, 2, a1_pclk_pdata);
+AUDIO_GATE(audio2_toddr_vad, AUDIO2_CLK_GATE_EN0, 3, a1_pclk_pdata);
+AUDIO_GATE(audio2_vad, AUDIO2_CLK_GATE_EN0, 4, a1_pclk_pdata);
+AUDIO_GATE(audio2_audiotop, AUDIO2_CLK_GATE_EN0, 7, a1_pclk_pdata);
+
+static const struct clk_parent_data a1_mst_pdata[] = {
+ { .fw_name = "dds_in" },
+ { .fw_name = "fclk_div2" },
+ { .fw_name = "fclk_div3" },
+ { .fw_name = "hifi_pll" },
+ { .fw_name = "xtal" },
+};
+
+#define AUDIO_MST_MCLK(_name, _reg) \
+ AUDIO_MUX(_name##_mux, (_reg), 0x7, 24, a1_mst_pdata); \
+ AUDIO_DIV(_name##_div, (_reg), 0, 16, \
+ AUDIO_PDATA(_name##_mux)); \
+ AUDIO_GATE(_name, (_reg), 31, AUDIO_PDATA(_name##_div))
+
+AUDIO_MST_MCLK(audio_mst_a_mclk, AUDIO_MCLK_A_CTRL);
+AUDIO_MST_MCLK(audio_mst_b_mclk, AUDIO_MCLK_B_CTRL);
+AUDIO_MST_MCLK(audio_mst_c_mclk, AUDIO_MCLK_C_CTRL);
+AUDIO_MST_MCLK(audio_mst_d_mclk, AUDIO_MCLK_D_CTRL);
+AUDIO_MST_MCLK(audio_spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL);
+AUDIO_MST_MCLK(audio_eqdrc_clk, AUDIO_CLK_EQDRC_CTRL);
+
+AUDIO_MUX(audio_resample_clk_mux, AUDIO_CLK_RESAMPLE_CTRL, 0xf, 24,
+ a1_mst_pdata);
+AUDIO_DIV(audio_resample_clk_div, AUDIO_CLK_RESAMPLE_CTRL, 0, 8,
+ AUDIO_PDATA(audio_resample_clk_mux));
+AUDIO_GATE(audio_resample_clk, AUDIO_CLK_RESAMPLE_CTRL, 31,
+ AUDIO_PDATA(audio_resample_clk_div));
+
+AUDIO_MUX(audio_locker_in_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 8,
+ a1_mst_pdata);
+AUDIO_DIV(audio_locker_in_clk_div, AUDIO_CLK_LOCKER_CTRL, 0, 8,
+ AUDIO_PDATA(audio_locker_in_clk_mux));
+AUDIO_GATE(audio_locker_in_clk, AUDIO_CLK_LOCKER_CTRL, 15,
+ AUDIO_PDATA(audio_locker_in_clk_div));
+
+AUDIO_MUX(audio_locker_out_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 24,
+ a1_mst_pdata);
+AUDIO_DIV(audio_locker_out_clk_div, AUDIO_CLK_LOCKER_CTRL, 16, 8,
+ AUDIO_PDATA(audio_locker_out_clk_mux));
+AUDIO_GATE(audio_locker_out_clk, AUDIO_CLK_LOCKER_CTRL, 31,
+ AUDIO_PDATA(audio_locker_out_clk_div));
+
+AUDIO_MST_MCLK(audio2_vad_mclk, AUDIO2_MCLK_VAD_CTRL);
+AUDIO_MST_MCLK(audio2_vad_clk, AUDIO2_CLK_VAD_CTRL);
+AUDIO_MST_MCLK(audio2_pdm_dclk, AUDIO2_CLK_PDMIN_CTRL0);
+AUDIO_MST_MCLK(audio2_pdm_sysclk, AUDIO2_CLK_PDMIN_CTRL1);
+
+#define AUDIO_MST_SCLK(_name, _reg0, _reg1, _pdata) \
+ AUDIO_GATE(_name##_pre_en, (_reg0), 31, (_pdata)); \
+ AUDIO_SCLK_DIV(_name##_div, (_reg0), 20, 10, 0, 0, \
+ AUDIO_PDATA(_name##_pre_en), true); \
+ AUDIO_GATE(_name##_post_en, (_reg0), 30, \
+ AUDIO_PDATA(_name##_div)); \
+ AUDIO_TRIPHASE(_name, (_reg1), 1, 0, 2, 4, \
+ AUDIO_PDATA(_name##_post_en))
+
+#define AUDIO_MST_LRCLK(_name, _reg0, _reg1, _pdata) \
+ AUDIO_SCLK_DIV(_name##_div, (_reg0), 0, 10, 10, 10, \
+ (_pdata), false); \
+ AUDIO_TRIPHASE(_name, (_reg1), 1, 1, 3, 5, \
+ AUDIO_PDATA(_name##_div))
+
+AUDIO_MST_SCLK(audio_mst_a_sclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1,
+ AUDIO_PDATA(audio_mst_a_mclk));
+AUDIO_MST_SCLK(audio_mst_b_sclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1,
+ AUDIO_PDATA(audio_mst_b_mclk));
+AUDIO_MST_SCLK(audio_mst_c_sclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1,
+ AUDIO_PDATA(audio_mst_c_mclk));
+AUDIO_MST_SCLK(audio_mst_d_sclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1,
+ AUDIO_PDATA(audio_mst_d_mclk));
+
+AUDIO_MST_LRCLK(audio_mst_a_lrclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1,
+ AUDIO_PDATA(audio_mst_a_sclk_post_en));
+AUDIO_MST_LRCLK(audio_mst_b_lrclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1,
+ AUDIO_PDATA(audio_mst_b_sclk_post_en));
+AUDIO_MST_LRCLK(audio_mst_c_lrclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1,
+ AUDIO_PDATA(audio_mst_c_sclk_post_en));
+AUDIO_MST_LRCLK(audio_mst_d_lrclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1,
+ AUDIO_PDATA(audio_mst_d_sclk_post_en));
+
+static const struct clk_parent_data a1_mst_sclk_pdata[] = {
+ { .hw = &audio_mst_a_sclk.hw },
+ { .hw = &audio_mst_b_sclk.hw },
+ { .hw = &audio_mst_c_sclk.hw },
+ { .hw = &audio_mst_d_sclk.hw },
+ { .fw_name = "slv_sclk0" },
+ { .fw_name = "slv_sclk1" },
+ { .fw_name = "slv_sclk2" },
+ { .fw_name = "slv_sclk3" },
+ { .fw_name = "slv_sclk4" },
+ { .fw_name = "slv_sclk5" },
+ { .fw_name = "slv_sclk6" },
+ { .fw_name = "slv_sclk7" },
+ { .fw_name = "slv_sclk8" },
+ { .fw_name = "slv_sclk9" },
+};
+
+static const struct clk_parent_data a1_mst_lrclk_pdata[] = {
+ { .hw = &audio_mst_a_lrclk.hw },
+ { .hw = &audio_mst_b_lrclk.hw },
+ { .hw = &audio_mst_c_lrclk.hw },
+ { .hw = &audio_mst_d_lrclk.hw },
+ { .fw_name = "slv_lrclk0" },
+ { .fw_name = "slv_lrclk1" },
+ { .fw_name = "slv_lrclk2" },
+ { .fw_name = "slv_lrclk3" },
+ { .fw_name = "slv_lrclk4" },
+ { .fw_name = "slv_lrclk5" },
+ { .fw_name = "slv_lrclk6" },
+ { .fw_name = "slv_lrclk7" },
+ { .fw_name = "slv_lrclk8" },
+ { .fw_name = "slv_lrclk9" },
+};
+
+#define AUDIO_TDM_SCLK(_name, _reg) \
+ AUDIO_MUX(_name##_mux, (_reg), 0xf, 24, a1_mst_sclk_pdata); \
+ AUDIO_GATE(_name##_pre_en, (_reg), 31, \
+ AUDIO_PDATA(_name##_mux)); \
+ AUDIO_GATE(_name##_post_en, (_reg), 30, \
+ AUDIO_PDATA(_name##_pre_en)); \
+ AUDIO_SCLK_WS(_name, (_reg), 1, 29, 28, \
+ AUDIO_PDATA(_name##_post_en))
+
+#define AUDIO_TDM_LRCLK(_name, _reg) \
+ AUDIO_MUX(_name, (_reg), 0xf, 20, a1_mst_lrclk_pdata)
+
+AUDIO_TDM_SCLK(audio_tdmin_a_sclk, AUDIO_CLK_TDMIN_A_CTRL);
+AUDIO_TDM_SCLK(audio_tdmin_b_sclk, AUDIO_CLK_TDMIN_B_CTRL);
+AUDIO_TDM_SCLK(audio_tdmin_lb_sclk, AUDIO_CLK_TDMIN_LB_CTRL);
+AUDIO_TDM_SCLK(audio_tdmout_a_sclk, AUDIO_CLK_TDMOUT_A_CTRL);
+AUDIO_TDM_SCLK(audio_tdmout_b_sclk, AUDIO_CLK_TDMOUT_B_CTRL);
+
+AUDIO_TDM_LRCLK(audio_tdmin_a_lrclk, AUDIO_CLK_TDMIN_A_CTRL);
+AUDIO_TDM_LRCLK(audio_tdmin_b_lrclk, AUDIO_CLK_TDMIN_B_CTRL);
+AUDIO_TDM_LRCLK(audio_tdmin_lb_lrclk, AUDIO_CLK_TDMIN_LB_CTRL);
+AUDIO_TDM_LRCLK(audio_tdmout_a_lrclk, AUDIO_CLK_TDMOUT_A_CTRL);
+AUDIO_TDM_LRCLK(audio_tdmout_b_lrclk, AUDIO_CLK_TDMOUT_B_CTRL);
+
+static struct clk_hw *a1_audio_hw_clks[] = {
+ [AUD_CLKID_DDR_ARB] = &audio_ddr_arb.hw,
+ [AUD_CLKID_TDMIN_A] = &audio_tdmin_a.hw,
+ [AUD_CLKID_TDMIN_B] = &audio_tdmin_b.hw,
+ [AUD_CLKID_TDMIN_LB] = &audio_tdmin_lb.hw,
+ [AUD_CLKID_LOOPBACK] = &audio_loopback.hw,
+ [AUD_CLKID_TDMOUT_A] = &audio_tdmout_a.hw,
+ [AUD_CLKID_TDMOUT_B] = &audio_tdmout_b.hw,
+ [AUD_CLKID_FRDDR_A] = &audio_frddr_a.hw,
+ [AUD_CLKID_FRDDR_B] = &audio_frddr_b.hw,
+ [AUD_CLKID_TODDR_A] = &audio_toddr_a.hw,
+ [AUD_CLKID_TODDR_B] = &audio_toddr_b.hw,
+ [AUD_CLKID_SPDIFIN] = &audio_spdifin.hw,
+ [AUD_CLKID_RESAMPLE] = &audio_resample.hw,
+ [AUD_CLKID_EQDRC] = &audio_eqdrc.hw,
+ [AUD_CLKID_LOCKER] = &audio_audiolocker.hw,
+ [AUD_CLKID_MST_A_MCLK_SEL] = &audio_mst_a_mclk_mux.hw,
+ [AUD_CLKID_MST_A_MCLK_DIV] = &audio_mst_a_mclk_div.hw,
+ [AUD_CLKID_MST_A_MCLK] = &audio_mst_a_mclk.hw,
+ [AUD_CLKID_MST_B_MCLK_SEL] = &audio_mst_b_mclk_mux.hw,
+ [AUD_CLKID_MST_B_MCLK_DIV] = &audio_mst_b_mclk_div.hw,
+ [AUD_CLKID_MST_B_MCLK] = &audio_mst_b_mclk.hw,
+ [AUD_CLKID_MST_C_MCLK_SEL] = &audio_mst_c_mclk_mux.hw,
+ [AUD_CLKID_MST_C_MCLK_DIV] = &audio_mst_c_mclk_div.hw,
+ [AUD_CLKID_MST_C_MCLK] = &audio_mst_c_mclk.hw,
+ [AUD_CLKID_MST_D_MCLK_SEL] = &audio_mst_d_mclk_mux.hw,
+ [AUD_CLKID_MST_D_MCLK_DIV] = &audio_mst_d_mclk_div.hw,
+ [AUD_CLKID_MST_D_MCLK] = &audio_mst_d_mclk.hw,
+ [AUD_CLKID_RESAMPLE_CLK_SEL] = &audio_resample_clk_mux.hw,
+ [AUD_CLKID_RESAMPLE_CLK_DIV] = &audio_resample_clk_div.hw,
+ [AUD_CLKID_RESAMPLE_CLK] = &audio_resample_clk.hw,
+ [AUD_CLKID_LOCKER_IN_CLK_SEL] = &audio_locker_in_clk_mux.hw,
+ [AUD_CLKID_LOCKER_IN_CLK_DIV] = &audio_locker_in_clk_div.hw,
+ [AUD_CLKID_LOCKER_IN_CLK] = &audio_locker_in_clk.hw,
+ [AUD_CLKID_LOCKER_OUT_CLK_SEL] = &audio_locker_out_clk_mux.hw,
+ [AUD_CLKID_LOCKER_OUT_CLK_DIV] = &audio_locker_out_clk_div.hw,
+ [AUD_CLKID_LOCKER_OUT_CLK] = &audio_locker_out_clk.hw,
+ [AUD_CLKID_SPDIFIN_CLK_SEL] = &audio_spdifin_clk_mux.hw,
+ [AUD_CLKID_SPDIFIN_CLK_DIV] = &audio_spdifin_clk_div.hw,
+ [AUD_CLKID_SPDIFIN_CLK] = &audio_spdifin_clk.hw,
+ [AUD_CLKID_EQDRC_CLK_SEL] = &audio_eqdrc_clk_mux.hw,
+ [AUD_CLKID_EQDRC_CLK_DIV] = &audio_eqdrc_clk_div.hw,
+ [AUD_CLKID_EQDRC_CLK] = &audio_eqdrc_clk.hw,
+ [AUD_CLKID_MST_A_SCLK_PRE_EN] = &audio_mst_a_sclk_pre_en.hw,
+ [AUD_CLKID_MST_A_SCLK_DIV] = &audio_mst_a_sclk_div.hw,
+ [AUD_CLKID_MST_A_SCLK_POST_EN] = &audio_mst_a_sclk_post_en.hw,
+ [AUD_CLKID_MST_A_SCLK] = &audio_mst_a_sclk.hw,
+ [AUD_CLKID_MST_B_SCLK_PRE_EN] = &audio_mst_b_sclk_pre_en.hw,
+ [AUD_CLKID_MST_B_SCLK_DIV] = &audio_mst_b_sclk_div.hw,
+ [AUD_CLKID_MST_B_SCLK_POST_EN] = &audio_mst_b_sclk_post_en.hw,
+ [AUD_CLKID_MST_B_SCLK] = &audio_mst_b_sclk.hw,
+ [AUD_CLKID_MST_C_SCLK_PRE_EN] = &audio_mst_c_sclk_pre_en.hw,
+ [AUD_CLKID_MST_C_SCLK_DIV] = &audio_mst_c_sclk_div.hw,
+ [AUD_CLKID_MST_C_SCLK_POST_EN] = &audio_mst_c_sclk_post_en.hw,
+ [AUD_CLKID_MST_C_SCLK] = &audio_mst_c_sclk.hw,
+ [AUD_CLKID_MST_D_SCLK_PRE_EN] = &audio_mst_d_sclk_pre_en.hw,
+ [AUD_CLKID_MST_D_SCLK_DIV] = &audio_mst_d_sclk_div.hw,
+ [AUD_CLKID_MST_D_SCLK_POST_EN] = &audio_mst_d_sclk_post_en.hw,
+ [AUD_CLKID_MST_D_SCLK] = &audio_mst_d_sclk.hw,
+ [AUD_CLKID_MST_A_LRCLK_DIV] = &audio_mst_a_lrclk_div.hw,
+ [AUD_CLKID_MST_A_LRCLK] = &audio_mst_a_lrclk.hw,
+ [AUD_CLKID_MST_B_LRCLK_DIV] = &audio_mst_b_lrclk_div.hw,
+ [AUD_CLKID_MST_B_LRCLK] = &audio_mst_b_lrclk.hw,
+ [AUD_CLKID_MST_C_LRCLK_DIV] = &audio_mst_c_lrclk_div.hw,
+ [AUD_CLKID_MST_C_LRCLK] = &audio_mst_c_lrclk.hw,
+ [AUD_CLKID_MST_D_LRCLK_DIV] = &audio_mst_d_lrclk_div.hw,
+ [AUD_CLKID_MST_D_LRCLK] = &audio_mst_d_lrclk.hw,
+ [AUD_CLKID_TDMIN_A_SCLK_SEL] = &audio_tdmin_a_sclk_mux.hw,
+ [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &audio_tdmin_a_sclk_pre_en.hw,
+ [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &audio_tdmin_a_sclk_post_en.hw,
+ [AUD_CLKID_TDMIN_A_SCLK] = &audio_tdmin_a_sclk.hw,
+ [AUD_CLKID_TDMIN_A_LRCLK] = &audio_tdmin_a_lrclk.hw,
+ [AUD_CLKID_TDMIN_B_SCLK_SEL] = &audio_tdmin_b_sclk_mux.hw,
+ [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &audio_tdmin_b_sclk_pre_en.hw,
+ [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &audio_tdmin_b_sclk_post_en.hw,
+ [AUD_CLKID_TDMIN_B_SCLK] = &audio_tdmin_b_sclk.hw,
+ [AUD_CLKID_TDMIN_B_LRCLK] = &audio_tdmin_b_lrclk.hw,
+ [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &audio_tdmin_lb_sclk_mux.hw,
+ [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &audio_tdmin_lb_sclk_pre_en.hw,
+ [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &audio_tdmin_lb_sclk_post_en.hw,
+ [AUD_CLKID_TDMIN_LB_SCLK] = &audio_tdmin_lb_sclk.hw,
+ [AUD_CLKID_TDMIN_LB_LRCLK] = &audio_tdmin_lb_lrclk.hw,
+ [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &audio_tdmout_a_sclk_mux.hw,
+ [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &audio_tdmout_a_sclk_pre_en.hw,
+ [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &audio_tdmout_a_sclk_post_en.hw,
+ [AUD_CLKID_TDMOUT_A_SCLK] = &audio_tdmout_a_sclk.hw,
+ [AUD_CLKID_TDMOUT_A_LRCLK] = &audio_tdmout_a_lrclk.hw,
+ [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &audio_tdmout_b_sclk_mux.hw,
+ [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &audio_tdmout_b_sclk_pre_en.hw,
+ [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &audio_tdmout_b_sclk_post_en.hw,
+ [AUD_CLKID_TDMOUT_B_SCLK] = &audio_tdmout_b_sclk.hw,
+ [AUD_CLKID_TDMOUT_B_LRCLK] = &audio_tdmout_b_lrclk.hw,
+
+ [AUD2_CLKID_DDR_ARB] = &audio2_ddr_arb.hw,
+ [AUD2_CLKID_PDM] = &audio2_pdm.hw,
+ [AUD2_CLKID_TDMIN_VAD] = &audio2_tdmin_vad.hw,
+ [AUD2_CLKID_TODDR_VAD] = &audio2_toddr_vad.hw,
+ [AUD2_CLKID_VAD] = &audio2_vad.hw,
+ [AUD2_CLKID_AUDIOTOP] = &audio2_audiotop.hw,
+ [AUD2_CLKID_VAD_MCLK_SEL] = &audio2_vad_mclk_mux.hw,
+ [AUD2_CLKID_VAD_MCLK_DIV] = &audio2_vad_mclk_div.hw,
+ [AUD2_CLKID_VAD_MCLK] = &audio2_vad_mclk.hw,
+ [AUD2_CLKID_VAD_CLK_SEL] = &audio2_vad_clk_mux.hw,
+ [AUD2_CLKID_VAD_CLK_DIV] = &audio2_vad_clk_div.hw,
+ [AUD2_CLKID_VAD_CLK] = &audio2_vad_clk.hw,
+ [AUD2_CLKID_PDM_DCLK_SEL] = &audio2_pdm_dclk_mux.hw,
+ [AUD2_CLKID_PDM_DCLK_DIV] = &audio2_pdm_dclk_div.hw,
+ [AUD2_CLKID_PDM_DCLK] = &audio2_pdm_dclk.hw,
+ [AUD2_CLKID_PDM_SYSCLK_SEL] = &audio2_pdm_sysclk_mux.hw,
+ [AUD2_CLKID_PDM_SYSCLK_DIV] = &audio2_pdm_sysclk_div.hw,
+ [AUD2_CLKID_PDM_SYSCLK] = &audio2_pdm_sysclk.hw,
+};
+
+static struct meson_clk_hw_data a1_audio_clks = {
+ .hws = a1_audio_hw_clks,
+ .num = ARRAY_SIZE(a1_audio_hw_clks),
+};
+
+static struct regmap *a1_audio_map(struct platform_device *pdev,
+ unsigned int index)
+{
+ char name[32];
+ const struct regmap_config cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .name = name,
+ };
+ void __iomem *base;
+
+ base = devm_platform_ioremap_resource(pdev, index);
+ if (IS_ERR(base))
+ return base;
+
+ scnprintf(name, sizeof(name), "%d", index);
+ return devm_regmap_init_mmio(&pdev->dev, base, &cfg);
+}
+
+static int a1_register_clk(struct platform_device *pdev,
+ struct regmap *map0, struct regmap *map1,
+ struct clk_hw *hw)
+{
+ struct clk_regmap *clk = container_of(hw, struct clk_regmap, hw);
+
+ if (!hw)
+ return 0;
+
+ switch ((unsigned long)clk->map) {
+ case AUDIO_RANGE_0:
+ clk->map = map0;
+ break;
+ case AUDIO_RANGE_1:
+ clk->map = map1;
+ break;
+ default:
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ return devm_clk_hw_register(&pdev->dev, hw);
+}
+
+static int a1_audio_clkc_probe(struct platform_device *pdev)
+{
+ struct regmap *map0, *map1;
+ struct clk *clk;
+ unsigned int i;
+ int ret;
+
+ clk = devm_clk_get_enabled(&pdev->dev, "pclk");
+ if (WARN_ON(IS_ERR(clk)))
+ return PTR_ERR(clk);
+
+ map0 = a1_audio_map(pdev, 0);
+ if (IS_ERR(map0))
+ return PTR_ERR(map0);
+
+ map1 = a1_audio_map(pdev, 1);
+ if (IS_ERR(map1))
+ return PTR_ERR(map1);
+
+ /*
+ * Register and enable AUD2_CLKID_AUDIOTOP clock first. Unless
+ * it is enabled any read/write to 'map0' hangs the CPU.
+ */
+
+ ret = a1_register_clk(pdev, map0, map1,
+ a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]->clk);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < a1_audio_clks.num; i++) {
+ if (i == AUD2_CLKID_AUDIOTOP)
+ continue;
+
+ ret = a1_register_clk(pdev, map0, map1, a1_audio_clks.hws[i]);
+ if (ret)
+ return ret;
+ }
+
+ ret = devm_of_clk_add_hw_provider(&pdev->dev, meson_clk_hw_get,
+ &a1_audio_clks);
+ if (ret)
+ return ret;
+
+ BUILD_BUG_ON((unsigned long)AUDIO_REG_MAP(AUDIO_SW_RESET0) !=
+ AUDIO_RANGE_0);
+ return meson_audio_rstc_register(&pdev->dev, map0,
+ AUDIO_REG_OFFSET(AUDIO_SW_RESET0), 32);
+}
+
+static const struct of_device_id a1_audio_clkc_match_table[] = {
+ { .compatible = "amlogic,a1-audio-clkc", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, a1_audio_clkc_match_table);
+
+static struct platform_driver a1_audio_clkc_driver = {
+ .probe = a1_audio_clkc_probe,
+ .driver = {
+ .name = "a1-audio-clkc",
+ .of_match_table = a1_audio_clkc_match_table,
+ },
+};
+module_platform_driver(a1_audio_clkc_driver);
+
+MODULE_DESCRIPTION("Amlogic A1 Audio Clock driver");
+MODULE_AUTHOR("Jan Dakinevich <[email protected]>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/meson/a1-audio.h b/drivers/clk/meson/a1-audio.h
new file mode 100644
index 000000000000..f994e87276cd
--- /dev/null
+++ b/drivers/clk/meson/a1-audio.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
+ *
+ * Author: Jan Dakinevich <[email protected]>
+ */
+
+#ifndef __A1_AUDIO_H
+#define __A1_AUDIO_H
+
+#define AUDIO_RANGE_0 0xa
+#define AUDIO_RANGE_1 0xb
+#define AUDIO_RANGE_SHIFT 16
+
+#define AUDIO_REG(_range, _offset) \
+ (((_range) << AUDIO_RANGE_SHIFT) + (_offset))
+
+#define AUDIO_REG_OFFSET(_reg) \
+ ((_reg) & ((1 << AUDIO_RANGE_SHIFT) - 1))
+
+#define AUDIO_REG_MAP(_reg) \
+ ((void *)((_reg) >> AUDIO_RANGE_SHIFT))
+
+#define AUDIO_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_0, 0x000)
+#define AUDIO_MCLK_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x008)
+#define AUDIO_MCLK_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x00c)
+#define AUDIO_MCLK_C_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x010)
+#define AUDIO_MCLK_D_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x014)
+#define AUDIO_MCLK_E_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x018)
+#define AUDIO_MCLK_F_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x01c)
+#define AUDIO_SW_RESET0 AUDIO_REG(AUDIO_RANGE_0, 0x028)
+#define AUDIO_MST_A_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x040)
+#define AUDIO_MST_A_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x044)
+#define AUDIO_MST_B_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x048)
+#define AUDIO_MST_B_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x04c)
+#define AUDIO_MST_C_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x050)
+#define AUDIO_MST_C_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x054)
+#define AUDIO_MST_D_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x058)
+#define AUDIO_MST_D_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x05c)
+#define AUDIO_CLK_TDMIN_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x080)
+#define AUDIO_CLK_TDMIN_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x084)
+#define AUDIO_CLK_TDMIN_LB_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x08c)
+#define AUDIO_CLK_TDMOUT_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x090)
+#define AUDIO_CLK_TDMOUT_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x094)
+#define AUDIO_CLK_SPDIFIN_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x09c)
+#define AUDIO_CLK_RESAMPLE_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a4)
+#define AUDIO_CLK_LOCKER_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a8)
+#define AUDIO_CLK_EQDRC_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0c0)
+
+#define AUDIO2_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_1, 0x00c)
+#define AUDIO2_MCLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x040)
+#define AUDIO2_CLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x044)
+#define AUDIO2_CLK_PDMIN_CTRL0 AUDIO_REG(AUDIO_RANGE_1, 0x058)
+#define AUDIO2_CLK_PDMIN_CTRL1 AUDIO_REG(AUDIO_RANGE_1, 0x05c)
+
+#include <dt-bindings/clock/amlogic,a1-audio-clkc.h>
+
+#endif /* __A1_AUDIO_H */
--
2.34.1


2024-03-14 23:25:16

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 06/25] ASoC: meson: g12a-toacodec: fix "Lane Select" width

For both G12A and SM1 the width of "Lane Select" should be 2, not 3.
Otherwise, it overlaps with "Source".

Signed-off-by: Jan Dakinevich <[email protected]>
---
sound/soc/meson/g12a-toacodec.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/sound/soc/meson/g12a-toacodec.c b/sound/soc/meson/g12a-toacodec.c
index 531bb8707a3e..b92de2235627 100644
--- a/sound/soc/meson/g12a-toacodec.c
+++ b/sound/soc/meson/g12a-toacodec.c
@@ -229,11 +229,11 @@ static const struct snd_soc_dapm_route g12a_toacodec_routes[] = {
};

static const struct snd_kcontrol_new g12a_toacodec_controls[] = {
- SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL, 3, 0),
+ SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL, 2, 0),
};

static const struct snd_kcontrol_new sm1_toacodec_controls[] = {
- SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL_SM1, 3, 0),
+ SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL_SM1, 2, 0),
};

static const struct snd_soc_component_driver g12a_toacodec_component_drv = {
--
2.34.1


2024-03-14 23:25:27

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 02/25] clk: meson: axg: move reset controller's code to separate module

This code will by reused by A1 SoC.

Signed-off-by: Jan Dakinevich <[email protected]>
---
drivers/clk/meson/Kconfig | 5 ++
drivers/clk/meson/Makefile | 1 +
drivers/clk/meson/axg-audio.c | 95 +----------------------
drivers/clk/meson/meson-audio-rstc.c | 109 +++++++++++++++++++++++++++
drivers/clk/meson/meson-audio-rstc.h | 12 +++
5 files changed, 130 insertions(+), 92 deletions(-)
create mode 100644 drivers/clk/meson/meson-audio-rstc.c
create mode 100644 drivers/clk/meson/meson-audio-rstc.h

diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
index 29ffd14d267b..d6a2fa5f7e88 100644
--- a/drivers/clk/meson/Kconfig
+++ b/drivers/clk/meson/Kconfig
@@ -48,6 +48,10 @@ config COMMON_CLK_MESON_CPU_DYNDIV
tristate
select COMMON_CLK_MESON_REGMAP

+config COMMON_CLK_MESON_AUDIO_RSTC
+ tristate
+ select RESET_CONTROLLER
+
config COMMON_CLK_MESON8B
bool "Meson8 SoC Clock controller support"
depends on ARM
@@ -101,6 +105,7 @@ config COMMON_CLK_AXG_AUDIO
select COMMON_CLK_MESON_PHASE
select COMMON_CLK_MESON_SCLK_DIV
select COMMON_CLK_MESON_CLKC_UTILS
+ select COMMON_CLK_MESON_AUDIO_RSTC
select REGMAP_MMIO
help
Support for the audio clock controller on AmLogic A113D devices,
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index 9ee4b954c896..88d94921a4dc 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_COMMON_CLK_MESON_PLL) += clk-pll.o
obj-$(CONFIG_COMMON_CLK_MESON_REGMAP) += clk-regmap.o
obj-$(CONFIG_COMMON_CLK_MESON_SCLK_DIV) += sclk-div.o
obj-$(CONFIG_COMMON_CLK_MESON_VID_PLL_DIV) += vid-pll-div.o
+obj-$(CONFIG_COMMON_CLK_MESON_AUDIO_RSTC) += meson-audio-rstc.o

# Amlogic Clock controllers

diff --git a/drivers/clk/meson/axg-audio.c b/drivers/clk/meson/axg-audio.c
index ac3482960903..990203a7ad5c 100644
--- a/drivers/clk/meson/axg-audio.c
+++ b/drivers/clk/meson/axg-audio.c
@@ -12,10 +12,10 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
-#include <linux/reset-controller.h>
#include <linux/slab.h>

#include "meson-clkc-utils.h"
+#include "meson-audio-rstc.h"
#include "axg-audio.h"
#include "clk-regmap.h"
#include "clk-phase.h"
@@ -1648,84 +1648,6 @@ static struct clk_regmap *const sm1_clk_regmaps[] = {
&sm1_sysclk_b_en,
};

-struct axg_audio_reset_data {
- struct reset_controller_dev rstc;
- struct regmap *map;
- unsigned int offset;
-};
-
-static void axg_audio_reset_reg_and_bit(struct axg_audio_reset_data *rst,
- unsigned long id,
- unsigned int *reg,
- unsigned int *bit)
-{
- unsigned int stride = regmap_get_reg_stride(rst->map);
-
- *reg = (id / (stride * BITS_PER_BYTE)) * stride;
- *reg += rst->offset;
- *bit = id % (stride * BITS_PER_BYTE);
-}
-
-static int axg_audio_reset_update(struct reset_controller_dev *rcdev,
- unsigned long id, bool assert)
-{
- struct axg_audio_reset_data *rst =
- container_of(rcdev, struct axg_audio_reset_data, rstc);
- unsigned int offset, bit;
-
- axg_audio_reset_reg_and_bit(rst, id, &offset, &bit);
-
- regmap_update_bits(rst->map, offset, BIT(bit),
- assert ? BIT(bit) : 0);
-
- return 0;
-}
-
-static int axg_audio_reset_status(struct reset_controller_dev *rcdev,
- unsigned long id)
-{
- struct axg_audio_reset_data *rst =
- container_of(rcdev, struct axg_audio_reset_data, rstc);
- unsigned int val, offset, bit;
-
- axg_audio_reset_reg_and_bit(rst, id, &offset, &bit);
-
- regmap_read(rst->map, offset, &val);
-
- return !!(val & BIT(bit));
-}
-
-static int axg_audio_reset_assert(struct reset_controller_dev *rcdev,
- unsigned long id)
-{
- return axg_audio_reset_update(rcdev, id, true);
-}
-
-static int axg_audio_reset_deassert(struct reset_controller_dev *rcdev,
- unsigned long id)
-{
- return axg_audio_reset_update(rcdev, id, false);
-}
-
-static int axg_audio_reset_toggle(struct reset_controller_dev *rcdev,
- unsigned long id)
-{
- int ret;
-
- ret = axg_audio_reset_assert(rcdev, id);
- if (ret)
- return ret;
-
- return axg_audio_reset_deassert(rcdev, id);
-}
-
-static const struct reset_control_ops axg_audio_rstc_ops = {
- .assert = axg_audio_reset_assert,
- .deassert = axg_audio_reset_deassert,
- .reset = axg_audio_reset_toggle,
- .status = axg_audio_reset_status,
-};
-
static const struct regmap_config axg_audio_regmap_cfg = {
.reg_bits = 32,
.val_bits = 32,
@@ -1745,7 +1667,6 @@ static int axg_audio_clkc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct audioclk_data *data;
- struct axg_audio_reset_data *rst;
struct regmap *map;
void __iomem *regs;
struct clk_hw *hw;
@@ -1807,18 +1728,8 @@ static int axg_audio_clkc_probe(struct platform_device *pdev)
if (!data->reset_num)
return 0;

- rst = devm_kzalloc(dev, sizeof(*rst), GFP_KERNEL);
- if (!rst)
- return -ENOMEM;
-
- rst->map = map;
- rst->offset = data->reset_offset;
- rst->rstc.nr_resets = data->reset_num;
- rst->rstc.ops = &axg_audio_rstc_ops;
- rst->rstc.of_node = dev->of_node;
- rst->rstc.owner = THIS_MODULE;
-
- return devm_reset_controller_register(dev, &rst->rstc);
+ return meson_audio_rstc_register(dev, map, data->reset_offset,
+ data->reset_num);
}

static const struct audioclk_data axg_audioclk_data = {
diff --git a/drivers/clk/meson/meson-audio-rstc.c b/drivers/clk/meson/meson-audio-rstc.c
new file mode 100644
index 000000000000..2079d24c40f4
--- /dev/null
+++ b/drivers/clk/meson/meson-audio-rstc.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Copyright (c) 2018 BayLibre, SAS.
+ * Author: Jerome Brunet <[email protected]>
+ */
+
+#include <linux/reset-controller.h>
+
+#include "meson-audio-rstc.h"
+
+struct meson_audio_reset_data {
+ struct reset_controller_dev rstc;
+ struct regmap *map;
+ unsigned int offset;
+};
+
+static void meson_audio_reset_reg_and_bit(struct meson_audio_reset_data *rst,
+ unsigned long id,
+ unsigned int *reg,
+ unsigned int *bit)
+{
+ unsigned int stride = regmap_get_reg_stride(rst->map);
+
+ *reg = (id / (stride * BITS_PER_BYTE)) * stride;
+ *reg += rst->offset;
+ *bit = id % (stride * BITS_PER_BYTE);
+}
+
+static int meson_audio_reset_update(struct reset_controller_dev *rcdev,
+ unsigned long id, bool assert)
+{
+ struct meson_audio_reset_data *rst =
+ container_of(rcdev, struct meson_audio_reset_data, rstc);
+ unsigned int offset, bit;
+
+ meson_audio_reset_reg_and_bit(rst, id, &offset, &bit);
+
+ regmap_update_bits(rst->map, offset, BIT(bit),
+ assert ? BIT(bit) : 0);
+
+ return 0;
+}
+
+static int meson_audio_reset_status(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct meson_audio_reset_data *rst =
+ container_of(rcdev, struct meson_audio_reset_data, rstc);
+ unsigned int val, offset, bit;
+
+ meson_audio_reset_reg_and_bit(rst, id, &offset, &bit);
+
+ regmap_read(rst->map, offset, &val);
+
+ return !!(val & BIT(bit));
+}
+
+static int meson_audio_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return meson_audio_reset_update(rcdev, id, true);
+}
+
+static int meson_audio_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ return meson_audio_reset_update(rcdev, id, false);
+}
+
+static int meson_audio_reset_toggle(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ int ret;
+
+ ret = meson_audio_reset_assert(rcdev, id);
+ if (ret)
+ return ret;
+
+ return meson_audio_reset_deassert(rcdev, id);
+}
+
+static const struct reset_control_ops meson_audio_rstc_ops = {
+ .assert = meson_audio_reset_assert,
+ .deassert = meson_audio_reset_deassert,
+ .reset = meson_audio_reset_toggle,
+ .status = meson_audio_reset_status,
+};
+
+int meson_audio_rstc_register(struct device *dev, struct regmap *map,
+ unsigned int offset, unsigned int num)
+{
+ struct meson_audio_reset_data *rst;
+
+ rst = devm_kzalloc(dev, sizeof(*rst), GFP_KERNEL);
+ if (!rst)
+ return -ENOMEM;
+
+ rst->map = map;
+ rst->offset = offset;
+ rst->rstc.nr_resets = num;
+ rst->rstc.ops = &meson_audio_rstc_ops;
+ rst->rstc.of_node = dev->of_node;
+ rst->rstc.owner = THIS_MODULE;
+
+ return devm_reset_controller_register(dev, &rst->rstc);
+}
+EXPORT_SYMBOL_GPL(meson_audio_rstc_register);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/meson/meson-audio-rstc.h b/drivers/clk/meson/meson-audio-rstc.h
new file mode 100644
index 000000000000..6b441549de03
--- /dev/null
+++ b/drivers/clk/meson/meson-audio-rstc.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+
+#ifndef __MESON_AUDIO_RSTC_H
+#define __MESON_AUDIO_RSTC_H
+
+#include <linux/device.h>
+#include <linux/regmap.h>
+
+int meson_audio_rstc_register(struct device *dev, struct regmap *map,
+ unsigned int offset, unsigned int num);
+
+#endif /* __MESON_AUDIO_RSTC_H */
--
2.34.1


2024-03-14 23:25:44

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 05/25] ASoC: meson: codec-glue: add support for capture stream

---- >8 ----
Hope, I haven't misinterpreted the terminology and codec-glue's behavior
too much.
---- >8 ----

The glue saves stream's private data in front-end dai and then backend
dai can reach them searching backward from sink to source. For capture
stream everything left the same, but searching should be performed from
source to sink.

Signed-off-by: Jan Dakinevich <[email protected]>
---
sound/soc/meson/meson-codec-glue.c | 174 ++++++++++++++++++++++-------
sound/soc/meson/meson-codec-glue.h | 23 ++++
2 files changed, 158 insertions(+), 39 deletions(-)

diff --git a/sound/soc/meson/meson-codec-glue.c b/sound/soc/meson/meson-codec-glue.c
index f8c5643f3cfe..da6d65e58d90 100644
--- a/sound/soc/meson/meson-codec-glue.c
+++ b/sound/soc/meson/meson-codec-glue.c
@@ -11,65 +11,94 @@
#include "meson-codec-glue.h"

static struct snd_soc_dapm_widget *
-meson_codec_glue_get_input(struct snd_soc_dapm_widget *w)
+meson_codec_glue_get_data_widget(struct snd_soc_dapm_widget *w, bool playback)
{
struct snd_soc_dapm_path *p;
- struct snd_soc_dapm_widget *in;
-
- snd_soc_dapm_widget_for_each_source_path(w, p) {
+ struct snd_soc_dapm_widget *node;
+ enum snd_soc_dapm_type id = playback ? snd_soc_dapm_dai_in
+ : snd_soc_dapm_dai_out;
+ enum snd_soc_dapm_direction dir = playback ? SND_SOC_DAPM_DIR_IN
+ : SND_SOC_DAPM_DIR_OUT;
+ enum snd_soc_dapm_direction rdir = playback ? SND_SOC_DAPM_DIR_OUT
+ : SND_SOC_DAPM_DIR_IN;
+
+ snd_soc_dapm_widget_for_each_path(w, rdir, p) {
if (!p->connect)
continue;

/* Check that we still are in the same component */
if (snd_soc_dapm_to_component(w->dapm) !=
- snd_soc_dapm_to_component(p->source->dapm))
+ snd_soc_dapm_to_component(p->node[dir]->dapm))
continue;

- if (p->source->id == snd_soc_dapm_dai_in)
- return p->source;
+ if (p->node[dir]->id == id)
+ return p->node[dir];

- in = meson_codec_glue_get_input(p->source);
- if (in)
- return in;
+ node = meson_codec_glue_get_data_widget(p->node[dir], playback);
+ if (node)
+ return node;
}

return NULL;
}

-static void meson_codec_glue_input_set_data(struct snd_soc_dai *dai,
- struct meson_codec_glue_input *data)
+static void meson_codec_glue_set_data(struct snd_soc_dai *dai,
+ struct meson_codec_glue_input *data,
+ bool playback)
+{
+ int stream = playback ? SNDRV_PCM_STREAM_PLAYBACK
+ : SNDRV_PCM_STREAM_CAPTURE;
+
+ snd_soc_dai_dma_data_set(dai, stream, data);
+}
+
+static struct meson_codec_glue_input *
+meson_codec_glue_get_data(struct snd_soc_dai *dai, bool playback)
{
- snd_soc_dai_dma_data_set_playback(dai, data);
+ int stream = playback ? SNDRV_PCM_STREAM_PLAYBACK
+ : SNDRV_PCM_STREAM_CAPTURE;
+
+ return snd_soc_dai_dma_data_get(dai, stream);
}

struct meson_codec_glue_input *
meson_codec_glue_input_get_data(struct snd_soc_dai *dai)
{
- return snd_soc_dai_dma_data_get_playback(dai);
+ return meson_codec_glue_get_data(dai, true);
}
EXPORT_SYMBOL_GPL(meson_codec_glue_input_get_data);

+struct meson_codec_glue_input *
+meson_codec_glue_capture_output_get_data(struct snd_soc_dai *dai)
+{
+ return meson_codec_glue_get_data(dai, false);
+}
+EXPORT_SYMBOL_GPL(meson_codec_glue_capture_output_get_data);
+
static struct meson_codec_glue_input *
-meson_codec_glue_output_get_input_data(struct snd_soc_dapm_widget *w)
+meson_codec_glue_data(struct snd_soc_dapm_widget *w, bool playback)
{
- struct snd_soc_dapm_widget *in =
- meson_codec_glue_get_input(w);
+ struct snd_soc_dapm_widget *node =
+ meson_codec_glue_get_data_widget(w, playback);
struct snd_soc_dai *dai;

- if (WARN_ON(!in))
+ if (WARN_ON(!node))
return NULL;

- dai = in->priv;
+ dai = node->priv;

- return meson_codec_glue_input_get_data(dai);
+ return meson_codec_glue_get_data(dai, playback);
}

-int meson_codec_glue_input_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+static int meson_codec_glue_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai,
+ bool playback)
{
struct meson_codec_glue_input *data =
- meson_codec_glue_input_get_data(dai);
+ meson_codec_glue_get_data(dai, playback);
+ struct snd_soc_pcm_stream *stream = playback ? &dai->driver->playback
+ : &dai->driver->capture;

data->params.rates = snd_pcm_rate_to_rate_bit(params_rate(params));
data->params.rate_min = params_rate(params);
@@ -77,32 +106,64 @@ int meson_codec_glue_input_hw_params(struct snd_pcm_substream *substream,
data->params.formats = 1ULL << (__force int) params_format(params);
data->params.channels_min = params_channels(params);
data->params.channels_max = params_channels(params);
- data->params.sig_bits = dai->driver->playback.sig_bits;
+ data->params.sig_bits = stream->sig_bits;

return 0;
}
+
+int meson_codec_glue_input_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ return meson_codec_glue_hw_params(substream, params, dai, true);
+}
EXPORT_SYMBOL_GPL(meson_codec_glue_input_hw_params);

-int meson_codec_glue_input_set_fmt(struct snd_soc_dai *dai,
- unsigned int fmt)
+int meson_codec_glue_capture_output_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ return meson_codec_glue_hw_params(substream, params, dai, false);
+}
+EXPORT_SYMBOL_GPL(meson_codec_glue_capture_output_hw_params);
+
+static int meson_codec_glue_set_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt,
+ bool playback)
{
struct meson_codec_glue_input *data =
- meson_codec_glue_input_get_data(dai);
+ meson_codec_glue_get_data(dai, playback);

/* Save the source stream format for the downstream link */
data->fmt = fmt;
return 0;
}
+
+int meson_codec_glue_input_set_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ return meson_codec_glue_set_fmt(dai, fmt, true);
+}
EXPORT_SYMBOL_GPL(meson_codec_glue_input_set_fmt);

-int meson_codec_glue_output_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+int meson_codec_glue_capture_output_set_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ return meson_codec_glue_set_fmt(dai, fmt, false);
+}
+EXPORT_SYMBOL_GPL(meson_codec_glue_capture_output_set_fmt);
+
+static int meson_codec_glue_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai,
+ bool playback)
{
+ int stream = playback ? SNDRV_PCM_STREAM_CAPTURE
+ : SNDRV_PCM_STREAM_PLAYBACK;
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
- struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget_capture(dai);
- struct meson_codec_glue_input *in_data = meson_codec_glue_output_get_input_data(w);
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, stream);
+ struct meson_codec_glue_input *data = meson_codec_glue_data(w, playback);

- if (!in_data)
+ if (!data)
return -ENODEV;

if (WARN_ON(!rtd->dai_link->c2c_params)) {
@@ -111,14 +172,27 @@ int meson_codec_glue_output_startup(struct snd_pcm_substream *substream,
}

/* Replace link params with the input params */
- rtd->dai_link->c2c_params = &in_data->params;
+ rtd->dai_link->c2c_params = &data->params;
rtd->dai_link->num_c2c_params = 1;

- return snd_soc_runtime_set_dai_fmt(rtd, in_data->fmt);
+ return snd_soc_runtime_set_dai_fmt(rtd, data->fmt);
+}
+
+int meson_codec_glue_output_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return meson_codec_glue_startup(substream, dai, true);
}
EXPORT_SYMBOL_GPL(meson_codec_glue_output_startup);

-int meson_codec_glue_input_dai_probe(struct snd_soc_dai *dai)
+int meson_codec_glue_capture_input_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return meson_codec_glue_startup(substream, dai, false);
+}
+EXPORT_SYMBOL_GPL(meson_codec_glue_capture_input_startup);
+
+static int meson_codec_glue_dai_probe(struct snd_soc_dai *dai, bool playback)
{
struct meson_codec_glue_input *data;

@@ -126,21 +200,43 @@ int meson_codec_glue_input_dai_probe(struct snd_soc_dai *dai)
if (!data)
return -ENOMEM;

- meson_codec_glue_input_set_data(dai, data);
+ meson_codec_glue_set_data(dai, data, playback);
return 0;
}
+
+int meson_codec_glue_input_dai_probe(struct snd_soc_dai *dai)
+{
+ return meson_codec_glue_dai_probe(dai, true);
+}
EXPORT_SYMBOL_GPL(meson_codec_glue_input_dai_probe);

-int meson_codec_glue_input_dai_remove(struct snd_soc_dai *dai)
+int meson_codec_glue_capture_output_dai_probe(struct snd_soc_dai *dai)
+{
+ return meson_codec_glue_dai_probe(dai, false);
+}
+EXPORT_SYMBOL_GPL(meson_codec_glue_capture_output_dai_probe);
+
+static int meson_codec_glue_dai_remove(struct snd_soc_dai *dai, bool playback)
{
struct meson_codec_glue_input *data =
- meson_codec_glue_input_get_data(dai);
+ meson_codec_glue_get_data(dai, playback);

kfree(data);
return 0;
}
+
+int meson_codec_glue_input_dai_remove(struct snd_soc_dai *dai)
+{
+ return meson_codec_glue_dai_remove(dai, true);
+}
EXPORT_SYMBOL_GPL(meson_codec_glue_input_dai_remove);

+int meson_codec_glue_capture_output_dai_remove(struct snd_soc_dai *dai)
+{
+ return meson_codec_glue_dai_remove(dai, false);
+}
+EXPORT_SYMBOL_GPL(meson_codec_glue_capture_output_dai_remove);
+
MODULE_AUTHOR("Jerome Brunet <[email protected]>");
MODULE_DESCRIPTION("Amlogic Codec Glue Helpers");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/meson-codec-glue.h b/sound/soc/meson/meson-codec-glue.h
index 07f99446c0c6..75d20aa75638 100644
--- a/sound/soc/meson/meson-codec-glue.h
+++ b/sound/soc/meson/meson-codec-glue.h
@@ -14,6 +14,10 @@ struct meson_codec_glue_input {
unsigned int fmt;
};

+/*
+ * Playback stream
+ */
+
/* Input helpers */
struct meson_codec_glue_input *
meson_codec_glue_input_get_data(struct snd_soc_dai *dai);
@@ -29,4 +33,23 @@ int meson_codec_glue_input_dai_remove(struct snd_soc_dai *dai);
int meson_codec_glue_output_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai);

+/*
+ * Capture stream
+ */
+
+/* Output helpers */
+struct meson_codec_glue_input *
+meson_codec_glue_capture_output_get_data(struct snd_soc_dai *dai);
+int meson_codec_glue_capture_output_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai);
+int meson_codec_glue_capture_output_set_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt);
+int meson_codec_glue_capture_output_dai_probe(struct snd_soc_dai *dai);
+int meson_codec_glue_capture_output_dai_remove(struct snd_soc_dai *dai);
+
+/* Input helpers */
+int meson_codec_glue_capture_input_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+
#endif /* _MESON_CODEC_GLUE_H */
--
2.34.1


2024-03-14 23:26:05

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 13/25] ASoC: dt-bindings: meson: axg-pdm: document 'sysrate' property

This option allow to redefine the rate of DSP system clock.

Signed-off-by: Jan Dakinevich <[email protected]>
---
Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
index df21dd72fc65..d2f23a59a6b6 100644
--- a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
+++ b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
@@ -40,6 +40,10 @@ properties:
resets:
maxItems: 1

+ sysrate:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: redefine rate of DSP system clock
+
required:
- compatible
- reg
--
2.34.1


2024-03-14 23:26:08

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 14/25] ASoC: meson: axg-pdm: introduce 'sysrate' property

This driver unconditionally set the rate of DSP system clock to 250MHz,
that on A1 SoC family causes reconfiguring of 'hifi_pll' clock to some
rate, that is multiple to 250MHz.

Further, when playback is activating 'hifi_pll' would be reconfigured
to another rate to produce audio clock like 12288000Hz. Both these rates
can't coexist on same parent.

To avoid the fight for 'hifi_pll' clock allow PDM controller to
configure its maximum sysrate through device tree. It will allow to
inherit 'sysclk' from another clock (i.e. 'fclk_div2') and isolate
'hifi_pll' from PDM influence.

Signed-off-by: Jan Dakinevich <[email protected]>
---
sound/soc/meson/axg-pdm.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/sound/soc/meson/axg-pdm.c b/sound/soc/meson/axg-pdm.c
index d59050914d3c..a132444a51fb 100644
--- a/sound/soc/meson/axg-pdm.c
+++ b/sound/soc/meson/axg-pdm.c
@@ -94,6 +94,7 @@ struct axg_pdm {
struct clk *dclk;
struct clk *sysclk;
struct clk *pclk;
+ u32 sys_rate;
};

static void axg_pdm_enable(struct regmap *map)
@@ -172,10 +173,10 @@ static int axg_pdm_set_sysclk(struct axg_pdm *priv, unsigned int os,
* the requested sample rate. In this case, the sample pointer
* counter could overflow so set a lower system clock rate
*/
- if (sys_rate < priv->cfg->sys_rate)
+ if (sys_rate < priv->sys_rate)
return clk_set_rate(priv->sysclk, sys_rate);

- return clk_set_rate(priv->sysclk, priv->cfg->sys_rate);
+ return clk_set_rate(priv->sysclk, priv->sys_rate);
}

static int axg_pdm_set_sample_pointer(struct axg_pdm *priv)
@@ -386,7 +387,7 @@ static int axg_pdm_dai_probe(struct snd_soc_dai *dai)
* sysclk must be set and enabled as well to access the pdm registers
* Accessing the register w/o it will give a bus error.
*/
- ret = clk_set_rate(priv->sysclk, priv->cfg->sys_rate);
+ ret = clk_set_rate(priv->sysclk, priv->sys_rate);
if (ret) {
dev_err(dai->dev, "setting sysclk failed\n");
goto err_pclk;
@@ -623,6 +624,9 @@ static int axg_pdm_probe(struct platform_device *pdev)
if (IS_ERR(priv->sysclk))
return dev_err_probe(dev, PTR_ERR(priv->sysclk), "failed to get dclk\n");

+ if (device_property_read_u32(dev, "sysrate", &priv->sys_rate))
+ priv->sys_rate = priv->cfg->sys_rate;
+
return devm_snd_soc_register_component(dev, &axg_pdm_component_drv,
&axg_pdm_dai_drv, 1);
}
--
2.34.1


2024-03-14 23:26:11

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 11/25] ASoC: dt-bindings: meson: t9015: add support for A1 SoC family

Add "amlogic,t9015-a1" compatible string.

Signed-off-by: Jan Dakinevich <[email protected]>
---
Documentation/devicetree/bindings/sound/amlogic,t9015.yaml | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml b/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml
index 5f5cccdbeb34..ee8bd57dbcf9 100644
--- a/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml
+++ b/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml
@@ -21,7 +21,9 @@ properties:

compatible:
items:
- - const: amlogic,t9015
+ - enum:
+ - amlogic,t9015
+ - amlogic,t9015-a1

clocks:
items:
--
2.34.1


2024-03-14 23:26:13

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 18/25] ASoC: dt-bindings: meson: axg-pdm: claim support of A1 SoC family

Add "amlogic,a1-pdm" compatible string alias to "amlogic,axg-pdm".

Signed-off-by: Jan Dakinevich <[email protected]>
---
Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml | 1 +
1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
index d2f23a59a6b6..b129b8053352 100644
--- a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
+++ b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
@@ -16,6 +16,7 @@ properties:
- enum:
- amlogic,g12a-pdm
- amlogic,sm1-pdm
+ - amlogic,a1-pdm
- const: amlogic,axg-pdm
- const: amlogic,axg-pdm

--
2.34.1


2024-03-14 23:26:25

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 19/25] ASoC: dt-bindings: meson: axg-sound-card: claim support of A1 SoC family

Add "amlogic,a1-sound-card" compatible string alias to
"amlogic,axg-sound-card".

Signed-off-by: Jan Dakinevich <[email protected]>
---
.../devicetree/bindings/sound/amlogic,axg-sound-card.yaml | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml
index 5db718e4d0e7..492b41cc8ccd 100644
--- a/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml
+++ b/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml
@@ -14,7 +14,11 @@ allOf:

properties:
compatible:
- const: amlogic,axg-sound-card
+ oneOf:
+ - const: amlogic,axg-sound-card
+ - items:
+ - const: amlogic,a1-sound-card
+ - const: amlogic,axg-sound-card

audio-aux-devs:
$ref: /schemas/types.yaml#/definitions/phandle-array
--
2.34.1


2024-03-14 23:26:25

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 03/25] dt-bindings: clock: meson: add A1 audio clock and reset controller bindings

Signed-off-by: Jan Dakinevich <[email protected]>
---
.../bindings/clock/amlogic,a1-audio-clkc.yaml | 83 ++++++++++++
.../dt-bindings/clock/amlogic,a1-audio-clkc.h | 122 ++++++++++++++++++
.../reset/amlogic,meson-a1-audio-reset.h | 29 +++++
3 files changed, 234 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/amlogic,a1-audio-clkc.yaml
create mode 100644 include/dt-bindings/clock/amlogic,a1-audio-clkc.h
create mode 100644 include/dt-bindings/reset/amlogic,meson-a1-audio-reset.h

diff --git a/Documentation/devicetree/bindings/clock/amlogic,a1-audio-clkc.yaml b/Documentation/devicetree/bindings/clock/amlogic,a1-audio-clkc.yaml
new file mode 100644
index 000000000000..c76cad4da493
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/amlogic,a1-audio-clkc.yaml
@@ -0,0 +1,83 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/amlogic,a1-audio-clkc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Amlogic A1 Audio Clock Control Unit and Reset Controller
+
+maintainers:
+ - Neil Armstrong <[email protected]>
+ - Jerome Brunet <[email protected]>
+ - Jan Dakinevich <[email protected]>
+
+properties:
+ compatible:
+ const: amlogic,a1-audio-clkc
+
+ '#clock-cells':
+ const: 1
+
+ '#reset-cells':
+ const: 1
+
+ reg:
+ minItems: 2
+ maxItems: 2
+
+ clocks:
+ items:
+ - description: input main peripheral bus clock
+ - description: input dds_in
+ - description: input fixed pll div2
+ - description: input fixed pll div3
+ - description: input hifi_pll
+ - description: input oscillator (usually at 24MHz)
+
+ clock-names:
+ items:
+ - const: pclk
+ - const: dds_in
+ - const: fclk_div2
+ - const: fclk_div3
+ - const: hifi_pll
+ - const: xtal
+
+required:
+ - compatible
+ - '#clock-cells'
+ - '#reset-cells'
+ - reg
+ - clocks
+ - clock-names
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/amlogic,a1-pll-clkc.h>
+ #include <dt-bindings/clock/amlogic,a1-peripherals-clkc.h>
+ audio {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ clkc_audio: audio-clock-controller@0 {
+ compatible = "amlogic,a1-audio-clkc";
+ reg = <0x0 0xfe050000 0x0 0xb0>,
+ <0x0 0xfe054800 0x0 0x20>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ clocks = <&clkc_periphs CLKID_AUDIO>,
+ <&clkc_periphs CLKID_DDS_IN>,
+ <&clkc_pll CLKID_FCLK_DIV2>,
+ <&clkc_pll CLKID_FCLK_DIV3>,
+ <&clkc_pll CLKID_HIFI_PLL>,
+ <&xtal>;
+ clock-names = "pclk",
+ "dds_in",
+ "fclk_div2",
+ "fclk_div3",
+ "hifi_pll",
+ "xtal";
+ };
+ };
diff --git a/include/dt-bindings/clock/amlogic,a1-audio-clkc.h b/include/dt-bindings/clock/amlogic,a1-audio-clkc.h
new file mode 100644
index 000000000000..3392974784e7
--- /dev/null
+++ b/include/dt-bindings/clock/amlogic,a1-audio-clkc.h
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
+ *
+ * Author: Jan Dakinevich <[email protected]>
+ */
+
+#ifndef __A1_AUDIO_CLKC_BINDINGS_H
+#define __A1_AUDIO_CLKC_BINDINGS_H
+
+#define AUD_CLKID_DDR_ARB 1
+#define AUD_CLKID_TDMIN_A 2
+#define AUD_CLKID_TDMIN_B 3
+#define AUD_CLKID_TDMIN_LB 4
+#define AUD_CLKID_LOOPBACK 5
+#define AUD_CLKID_TDMOUT_A 6
+#define AUD_CLKID_TDMOUT_B 7
+#define AUD_CLKID_FRDDR_A 8
+#define AUD_CLKID_FRDDR_B 9
+#define AUD_CLKID_TODDR_A 10
+#define AUD_CLKID_TODDR_B 11
+#define AUD_CLKID_SPDIFIN 12
+#define AUD_CLKID_RESAMPLE 13
+#define AUD_CLKID_EQDRC 14
+#define AUD_CLKID_LOCKER 15
+#define AUD_CLKID_MST_A_MCLK_SEL 16
+#define AUD_CLKID_MST_A_MCLK_DIV 17
+#define AUD_CLKID_MST_A_MCLK 18
+#define AUD_CLKID_MST_B_MCLK_SEL 19
+#define AUD_CLKID_MST_B_MCLK_DIV 20
+#define AUD_CLKID_MST_B_MCLK 21
+#define AUD_CLKID_MST_C_MCLK_SEL 22
+#define AUD_CLKID_MST_C_MCLK_DIV 23
+#define AUD_CLKID_MST_C_MCLK 24
+#define AUD_CLKID_MST_D_MCLK_SEL 25
+#define AUD_CLKID_MST_D_MCLK_DIV 26
+#define AUD_CLKID_MST_D_MCLK 27
+#define AUD_CLKID_SPDIFIN_CLK_SEL 28
+#define AUD_CLKID_SPDIFIN_CLK_DIV 29
+#define AUD_CLKID_SPDIFIN_CLK 30
+#define AUD_CLKID_RESAMPLE_CLK_SEL 31
+#define AUD_CLKID_RESAMPLE_CLK_DIV 32
+#define AUD_CLKID_RESAMPLE_CLK 33
+#define AUD_CLKID_LOCKER_IN_CLK_SEL 34
+#define AUD_CLKID_LOCKER_IN_CLK_DIV 35
+#define AUD_CLKID_LOCKER_IN_CLK 36
+#define AUD_CLKID_LOCKER_OUT_CLK_SEL 37
+#define AUD_CLKID_LOCKER_OUT_CLK_DIV 38
+#define AUD_CLKID_LOCKER_OUT_CLK 39
+#define AUD_CLKID_EQDRC_CLK_SEL 40
+#define AUD_CLKID_EQDRC_CLK_DIV 41
+#define AUD_CLKID_EQDRC_CLK 42
+#define AUD_CLKID_MST_A_SCLK_PRE_EN 43
+#define AUD_CLKID_MST_A_SCLK_DIV 44
+#define AUD_CLKID_MST_A_SCLK_POST_EN 45
+#define AUD_CLKID_MST_A_SCLK 46
+#define AUD_CLKID_MST_B_SCLK_PRE_EN 47
+#define AUD_CLKID_MST_B_SCLK_DIV 48
+#define AUD_CLKID_MST_B_SCLK_POST_EN 49
+#define AUD_CLKID_MST_B_SCLK 50
+#define AUD_CLKID_MST_C_SCLK_PRE_EN 51
+#define AUD_CLKID_MST_C_SCLK_DIV 52
+#define AUD_CLKID_MST_C_SCLK_POST_EN 53
+#define AUD_CLKID_MST_C_SCLK 54
+#define AUD_CLKID_MST_D_SCLK_PRE_EN 55
+#define AUD_CLKID_MST_D_SCLK_DIV 56
+#define AUD_CLKID_MST_D_SCLK_POST_EN 57
+#define AUD_CLKID_MST_D_SCLK 58
+#define AUD_CLKID_MST_A_LRCLK_DIV 59
+#define AUD_CLKID_MST_A_LRCLK 60
+#define AUD_CLKID_MST_B_LRCLK_DIV 61
+#define AUD_CLKID_MST_B_LRCLK 62
+#define AUD_CLKID_MST_C_LRCLK_DIV 63
+#define AUD_CLKID_MST_C_LRCLK 64
+#define AUD_CLKID_MST_D_LRCLK_DIV 65
+#define AUD_CLKID_MST_D_LRCLK 66
+#define AUD_CLKID_TDMIN_A_SCLK_SEL 67
+#define AUD_CLKID_TDMIN_A_SCLK_PRE_EN 68
+#define AUD_CLKID_TDMIN_A_SCLK_POST_EN 69
+#define AUD_CLKID_TDMIN_A_SCLK 70
+#define AUD_CLKID_TDMIN_A_LRCLK 71
+#define AUD_CLKID_TDMIN_B_SCLK_SEL 72
+#define AUD_CLKID_TDMIN_B_SCLK_PRE_EN 73
+#define AUD_CLKID_TDMIN_B_SCLK_POST_EN 74
+#define AUD_CLKID_TDMIN_B_SCLK 75
+#define AUD_CLKID_TDMIN_B_LRCLK 76
+#define AUD_CLKID_TDMIN_LB_SCLK_SEL 77
+#define AUD_CLKID_TDMIN_LB_SCLK_PRE_EN 78
+#define AUD_CLKID_TDMIN_LB_SCLK_POST_EN 79
+#define AUD_CLKID_TDMIN_LB_SCLK 80
+#define AUD_CLKID_TDMIN_LB_LRCLK 81
+#define AUD_CLKID_TDMOUT_A_SCLK_SEL 82
+#define AUD_CLKID_TDMOUT_A_SCLK_PRE_EN 83
+#define AUD_CLKID_TDMOUT_A_SCLK_POST_EN 84
+#define AUD_CLKID_TDMOUT_A_SCLK 85
+#define AUD_CLKID_TDMOUT_A_LRCLK 86
+#define AUD_CLKID_TDMOUT_B_SCLK_SEL 87
+#define AUD_CLKID_TDMOUT_B_SCLK_PRE_EN 88
+#define AUD_CLKID_TDMOUT_B_SCLK_POST_EN 89
+#define AUD_CLKID_TDMOUT_B_SCLK 90
+#define AUD_CLKID_TDMOUT_B_LRCLK 91
+
+#define AUD2_CLKID_DDR_ARB 100
+#define AUD2_CLKID_PDM 101
+#define AUD2_CLKID_TDMIN_VAD 102
+#define AUD2_CLKID_TODDR_VAD 103
+#define AUD2_CLKID_VAD 104
+#define AUD2_CLKID_AUDIOTOP 105
+#define AUD2_CLKID_VAD_MCLK_SEL 106
+#define AUD2_CLKID_VAD_MCLK_DIV 107
+#define AUD2_CLKID_VAD_MCLK 108
+#define AUD2_CLKID_VAD_CLK_SEL 109
+#define AUD2_CLKID_VAD_CLK_DIV 110
+#define AUD2_CLKID_VAD_CLK 111
+#define AUD2_CLKID_PDM_DCLK_SEL 112
+#define AUD2_CLKID_PDM_DCLK_DIV 113
+#define AUD2_CLKID_PDM_DCLK 114
+#define AUD2_CLKID_PDM_SYSCLK_SEL 115
+#define AUD2_CLKID_PDM_SYSCLK_DIV 116
+#define AUD2_CLKID_PDM_SYSCLK 117
+
+#endif /* __A1_AUDIO_CLKC_BINDINGS_H */
diff --git a/include/dt-bindings/reset/amlogic,meson-a1-audio-reset.h b/include/dt-bindings/reset/amlogic,meson-a1-audio-reset.h
new file mode 100644
index 000000000000..6b7ad8ea3da2
--- /dev/null
+++ b/include/dt-bindings/reset/amlogic,meson-a1-audio-reset.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
+ *
+ * Author: Jan Dakinevich <[email protected]>
+ */
+
+#ifndef _DT_BINDINGS_AMLOGIC_MESON_A1_AUDIO_RESET_H
+#define _DT_BINDINGS_AMLOGIC_MESON_A1_AUDIO_RESET_H
+
+#define AUD_RESET_DDRARB 0
+#define AUD_RESET_TDMIN_A 1
+#define AUD_RESET_TDMIN_B 2
+#define AUD_RESET_TDMIN_LB 3
+#define AUD_RESET_LOOPBACK 4
+#define AUD_RESET_TDMOUT_A 5
+#define AUD_RESET_TDMOUT_B 6
+#define AUD_RESET_FRDDR_A 7
+#define AUD_RESET_FRDDR_B 8
+#define AUD_RESET_TODDR_A 9
+#define AUD_RESET_TODDR_B 10
+#define AUD_RESET_SPDIFIN 11
+#define AUD_RESET_RESAMPLE 12
+#define AUD_RESET_EQDRC 13
+#define AUD_RESET_LOCKER 14
+#define AUD_RESET_TOACODEC 30
+#define AUD_RESET_CLKTREE 31
+
+#endif
--
2.34.1


2024-03-14 23:26:27

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 12/25] ASoC: meson: t9015: add support for A1 SoC family

A1's internal codec is very close to t9015. The main difference, that it
has ADC. This commit introduces support for capturing from it.

Signed-off-by: Jan Dakinevich <[email protected]>
---
sound/soc/meson/t9015.c | 259 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 259 insertions(+)

diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c
index 48f6767bd858..365955bfeb78 100644
--- a/sound/soc/meson/t9015.c
+++ b/sound/soc/meson/t9015.c
@@ -19,16 +19,33 @@
#define LOLP_EN 3
#define DACR_EN 4
#define DACL_EN 5
+#define ADCR_EN 6
+#define ADCL_EN 7
+#define PGAR_ZCD_EN 8
+#define PGAL_ZCD_EN 9
+#define PGAR_EN 10
+#define PGAL_EN 11
+#define ADCR_INV 16
+#define ADCL_INV 17
+#define ADCR_SRC 18
+#define ADCL_SRC 19
#define DACR_INV 20
#define DACL_INV 21
#define DACR_SRC 22
#define DACL_SRC 23
+#define ADC_DEM_EN 26
+#define ADC_FILTER_MODE 28
+#define ADC_FILTER_EN 29
#define REFP_BUF_EN BIT(12)
#define BIAS_CURRENT_EN BIT(13)
#define VMID_GEN_FAST BIT(14)
#define VMID_GEN_EN BIT(15)
#define I2S_MODE BIT(30)
#define VOL_CTRL0 0x04
+#define PGAR_VC 0
+#define PGAL_VC 8
+#define ADCR_VC 16
+#define ADCL_VC 24
#define GAIN_H 31
#define GAIN_L 23
#define VOL_CTRL1 0x08
@@ -46,6 +63,28 @@
#define LOLN_POL 8
#define LOLP_POL 12
#define POWER_CFG 0x10
+#define LINEIN_CFG 0x14
+#define MICBIAS_LEVEL 0
+#define MICBIAS_EN 3
+#define PGAR_CTVMN 8
+#define PGAR_CTVMP 9
+#define PGAL_CTVMN 10
+#define PGAL_CTVMP 11
+#define PGAR_CTVIN 12
+#define PGAR_CTVIP 13
+#define PGAL_CTVIN 14
+#define PGAL_CTVIP 15
+
+#define PGAR_MASK (BIT(PGAR_CTVMP) | BIT(PGAR_CTVMN) | \
+ BIT(PGAR_CTVIP) | BIT(PGAR_CTVIN))
+#define PGAR_DIFF (BIT(PGAR_CTVIP) | BIT(PGAR_CTVIN))
+#define PGAR_POSITIVE (BIT(PGAR_CTVIP) | BIT(PGAR_CTVMN))
+#define PGAR_NEGATIVE (BIT(PGAR_CTVIN) | BIT(PGAR_CTVMP))
+#define PGAL_MASK (BIT(PGAL_CTVMP) | BIT(PGAL_CTVMN) | \
+ BIT(PGAL_CTVIP) | BIT(PGAL_CTVIN))
+#define PGAL_DIFF (BIT(PGAL_CTVIP) | BIT(PGAL_CTVIN))
+#define PGAL_POSITIVE (BIT(PGAL_CTVIP) | BIT(PGAL_CTVMN))
+#define PGAL_NEGATIVE (BIT(PGAL_CTVIN) | BIT(PGAL_CTVMP))

struct t9015 {
struct regulator *avdd;
@@ -103,6 +142,31 @@ static struct snd_soc_dai_driver t9015_dai = {
.ops = &t9015_dai_ops,
};

+static struct snd_soc_dai_driver a1_t9015_dai = {
+ .name = "t9015-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = (SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = (SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ },
+ .ops = &t9015_dai_ops,
+};
+
static const DECLARE_TLV_DB_MINMAX_MUTE(dac_vol_tlv, -9525, 0);

static const char * const ramp_rate_txt[] = { "Fast", "Slow" };
@@ -179,6 +243,166 @@ static const struct snd_soc_dapm_route t9015_dapm_routes[] = {
{ "LOLP", NULL, "Left+ Driver", },
};

+static const char * const a1_right_driver_txt[] = { "None", "Right DAC",
+ "Left DAC Inverted" };
+static const unsigned int a1_right_driver_values[] = { 0, 2, 4 };
+
+static const char * const a1_left_driver_txt[] = { "None", "Left DAC",
+ "Right DAC Inverted" };
+static const unsigned int a1_left_driver_values[] = { 0, 2, 4 };
+
+static SOC_VALUE_ENUM_SINGLE_DECL(a1_right_driver, LINEOUT_CFG, 12, 0x7,
+ a1_right_driver_txt, a1_right_driver_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(a1_left_driver, LINEOUT_CFG, 4, 0x7,
+ a1_left_driver_txt, a1_left_driver_values);
+
+static const struct snd_kcontrol_new a1_right_driver_mux =
+ SOC_DAPM_ENUM("Right Driver+ Source", a1_right_driver);
+static const struct snd_kcontrol_new a1_left_driver_mux =
+ SOC_DAPM_ENUM("Left Driver+ Source", a1_left_driver);
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(a1_adc_vol_tlv, -29625, 0);
+static const DECLARE_TLV_DB_MINMAX_MUTE(a1_adc_pga_vol_tlv, -1200, 0);
+
+static const char * const a1_adc_right_txt[] = { "Right", "Left" };
+static SOC_ENUM_SINGLE_DECL(a1_adc_right, BLOCK_EN, ADCR_SRC, a1_adc_right_txt);
+
+static const char * const a1_adc_left_txt[] = { "Left", "Right" };
+static SOC_ENUM_SINGLE_DECL(a1_adc_left, BLOCK_EN, ADCL_SRC, a1_adc_left_txt);
+
+static const struct snd_kcontrol_new a1_adc_right_mux =
+ SOC_DAPM_ENUM("ADC Right Source", a1_adc_right);
+static const struct snd_kcontrol_new a1_adc_left_mux =
+ SOC_DAPM_ENUM("ADC Left Source", a1_adc_left);
+
+static const char * const a1_adc_filter_mode_txt[] = { "Voice", "HiFi"};
+static SOC_ENUM_SINGLE_DECL(a1_adc_filter_mode, BLOCK_EN, ADC_FILTER_MODE,
+ a1_adc_filter_mode_txt);
+
+static const char * const a1_adc_mic_bias_level_txt[] = { "2.0V", "2.1V",
+ "2.3V", "2.5V", "2.8V" };
+static const unsigned int a1_adc_mic_bias_level_values[] = { 0, 1, 2, 3, 7 };
+static SOC_VALUE_ENUM_SINGLE_DECL(a1_adc_mic_bias_level,
+ LINEIN_CFG, MICBIAS_LEVEL, 0x7,
+ a1_adc_mic_bias_level_txt,
+ a1_adc_mic_bias_level_values);
+
+static const char * const a1_adc_pga_txt[] = { "None", "Differential",
+ "Positive", "Negative" };
+static const unsigned int a1_adc_pga_right_values[] = { 0, PGAR_DIFF,
+ PGAR_POSITIVE, PGAR_NEGATIVE };
+static const unsigned int a1_adc_pga_left_values[] = { 0, PGAL_DIFF,
+ PGAL_POSITIVE, PGAL_NEGATIVE };
+
+static SOC_VALUE_ENUM_SINGLE_DECL(a1_adc_pga_right, LINEIN_CFG, 0, PGAR_MASK,
+ a1_adc_pga_txt, a1_adc_pga_right_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(a1_adc_pga_left, LINEIN_CFG, 0, PGAL_MASK,
+ a1_adc_pga_txt, a1_adc_pga_left_values);
+
+static const struct snd_kcontrol_new a1_adc_pga_right_mux =
+ SOC_DAPM_ENUM("ADC PGA Right Source", a1_adc_pga_right);
+static const struct snd_kcontrol_new a1_adc_pga_left_mux =
+ SOC_DAPM_ENUM("ADC PGA Left Source", a1_adc_pga_left);
+
+static const struct snd_kcontrol_new a1_t9015_snd_controls[] = {
+ /* Volume Controls */
+ SOC_ENUM("Playback Channel Mode", mono_enum),
+ SOC_SINGLE("Playback Switch", VOL_CTRL1, DAC_SOFT_MUTE, 1, 1),
+ SOC_DOUBLE_TLV("Playback Volume", VOL_CTRL1, DACL_VC, DACR_VC,
+ 0xff, 0, dac_vol_tlv),
+
+ /* Ramp Controls */
+ SOC_ENUM("Ramp Rate", ramp_rate_enum),
+ SOC_SINGLE("Volume Ramp Switch", VOL_CTRL1, VC_RAMP_MODE, 1, 0),
+ SOC_SINGLE("Mute Ramp Switch", VOL_CTRL1, MUTE_MODE, 1, 0),
+ SOC_SINGLE("Unmute Ramp Switch", VOL_CTRL1, UNMUTE_MODE, 1, 0),
+
+ /* ADC Controls */
+ SOC_DOUBLE_TLV("ADC Volume", VOL_CTRL0, ADCL_VC, ADCR_VC,
+ 0x7f, 0, a1_adc_vol_tlv),
+ SOC_SINGLE("ADC Filter Switch", BLOCK_EN, ADC_FILTER_EN, 1, 0),
+ SOC_ENUM("ADC Filter Mode", a1_adc_filter_mode),
+ SOC_SINGLE("ADC Mic Bias Switch", LINEIN_CFG, MICBIAS_EN, 1, 0),
+ SOC_ENUM("ADC Mic Bias Level", a1_adc_mic_bias_level),
+ SOC_SINGLE("ADC DEM Switch", BLOCK_EN, ADC_DEM_EN, 1, 0),
+ SOC_DOUBLE_TLV("ADC PGA Volume", VOL_CTRL0, PGAR_VC, PGAL_VC,
+ 0x1f, 0, a1_adc_pga_vol_tlv),
+ SOC_DOUBLE("ADC PGA Zero Cross-detection Switch", BLOCK_EN,
+ PGAL_ZCD_EN, PGAR_ZCD_EN, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget a1_t9015_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("Right IN", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Left IN", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MUX("Right DAC Sel", SND_SOC_NOPM, 0, 0,
+ &t9015_right_dac_mux),
+ SND_SOC_DAPM_MUX("Left DAC Sel", SND_SOC_NOPM, 0, 0,
+ &t9015_left_dac_mux),
+ SND_SOC_DAPM_DAC("Right DAC", NULL, BLOCK_EN, DACR_EN, 0),
+ SND_SOC_DAPM_DAC("Left DAC", NULL, BLOCK_EN, DACL_EN, 0),
+ SND_SOC_DAPM_MUX("Right+ Driver Sel", SND_SOC_NOPM, 0, 0,
+ &a1_right_driver_mux),
+ SND_SOC_DAPM_MUX("Left+ Driver Sel", SND_SOC_NOPM, 0, 0,
+ &a1_left_driver_mux),
+ SND_SOC_DAPM_OUT_DRV("Right+ Driver", BLOCK_EN, LORP_EN, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("Left+ Driver", BLOCK_EN, LOLP_EN, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("LORP"),
+ SND_SOC_DAPM_OUTPUT("LOLP"),
+
+ SND_SOC_DAPM_INPUT("ADC IN Right"),
+ SND_SOC_DAPM_INPUT("ADC IN Left"),
+ SND_SOC_DAPM_MUX("ADC PGA Right Sel", SND_SOC_NOPM, 0, 0,
+ &a1_adc_pga_right_mux),
+ SND_SOC_DAPM_MUX("ADC PGA Left Sel", SND_SOC_NOPM, 0, 0,
+ &a1_adc_pga_left_mux),
+ SND_SOC_DAPM_PGA("ADC PGA Right", BLOCK_EN, PGAR_EN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ADC PGA Left", BLOCK_EN, PGAL_EN, 0, NULL, 0),
+ SND_SOC_DAPM_ADC("ADC Right", NULL, BLOCK_EN, ADCR_EN, 0),
+ SND_SOC_DAPM_ADC("ADC Left", NULL, BLOCK_EN, ADCL_EN, 0),
+ SND_SOC_DAPM_MUX("ADC Right Sel", SND_SOC_NOPM, 0, 0, &a1_adc_right_mux),
+ SND_SOC_DAPM_MUX("ADC Left Sel", SND_SOC_NOPM, 0, 0, &a1_adc_left_mux),
+ SND_SOC_DAPM_AIF_OUT("ADC OUT Right", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("ADC OUT Left", NULL, 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route a1_t9015_dapm_routes[] = {
+ { "Right IN", NULL, "Playback" },
+ { "Left IN", NULL, "Playback" },
+ { "Right DAC Sel", "Right", "Right IN" },
+ { "Right DAC Sel", "Left", "Left IN" },
+ { "Left DAC Sel", "Right", "Right IN" },
+ { "Left DAC Sel", "Left", "Left IN" },
+ { "Right DAC", NULL, "Right DAC Sel" },
+ { "Left DAC", NULL, "Left DAC Sel" },
+ { "Right+ Driver Sel", "Right DAC", "Right DAC" },
+ { "Right+ Driver Sel", "Left DAC Inverted", "Right DAC" },
+ { "Left+ Driver Sel", "Left DAC", "Left DAC" },
+ { "Left+ Driver Sel", "Right DAC Inverted", "Left DAC" },
+ { "Right+ Driver", NULL, "Right+ Driver Sel" },
+ { "Left+ Driver", NULL, "Left+ Driver Sel" },
+ { "LORP", NULL, "Right+ Driver", },
+ { "LOLP", NULL, "Left+ Driver", },
+
+ { "ADC PGA Right Sel", "Differential", "ADC IN Right" },
+ { "ADC PGA Right Sel", "Positive", "ADC IN Right" },
+ { "ADC PGA Right Sel", "Negative", "ADC IN Right" },
+ { "ADC PGA Left Sel", "Differential", "ADC IN Left" },
+ { "ADC PGA Left Sel", "Positive", "ADC IN Left" },
+ { "ADC PGA Left Sel", "Negative", "ADC IN Left" },
+ { "ADC PGA Right", NULL, "ADC PGA Right Sel" },
+ { "ADC PGA Left", NULL, "ADC PGA Left Sel" },
+ { "ADC Right", NULL, "ADC PGA Right" },
+ { "ADC Left", NULL, "ADC PGA Left" },
+ { "ADC Right Sel", "Right", "ADC Right" },
+ { "ADC Right Sel", "Left", "ADC Left" },
+ { "ADC Left Sel", "Right", "ADC Right" },
+ { "ADC Left Sel", "Left", "ADC Left" },
+ { "ADC OUT Right", NULL, "ADC Right Sel" },
+ { "ADC OUT Left", NULL, "ADC Left Sel" },
+ { "Capture", NULL, "ADC OUT Right" },
+ { "Capture", NULL, "ADC OUT Left" },
+};
+
static int t9015_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
@@ -241,6 +465,18 @@ static int t9015_component_probe(struct snd_soc_component *component)
return 0;
}

+static int a1_t9015_component_probe(struct snd_soc_component *component)
+{
+ /*
+ * This configuration was stealed from original Amlogic's driver to
+ * reproduce the behavior of the driver more accurately. However, it is
+ * not known for certain what it actually affects.
+ */
+ snd_soc_component_write(component, POWER_CFG, 0x00010000);
+
+ return 0;
+}
+
static const struct snd_soc_component_driver t9015_codec_driver = {
.probe = t9015_component_probe,
.set_bias_level = t9015_set_bias_level,
@@ -254,6 +490,19 @@ static const struct snd_soc_component_driver t9015_codec_driver = {
.endianness = 1,
};

+static const struct snd_soc_component_driver a1_t9015_codec_driver = {
+ .probe = a1_t9015_component_probe,
+ .set_bias_level = t9015_set_bias_level,
+ .controls = a1_t9015_snd_controls,
+ .num_controls = ARRAY_SIZE(a1_t9015_snd_controls),
+ .dapm_widgets = a1_t9015_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(a1_t9015_dapm_widgets),
+ .dapm_routes = a1_t9015_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(a1_t9015_dapm_routes),
+ .suspend_bias_off = 1,
+ .endianness = 1,
+};
+
static int t9015_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -315,11 +564,21 @@ static const struct t9015_match_data t9015_match_data = {
.max_register = POWER_CFG,
};

+static const struct t9015_match_data a1_t9015_match_data = {
+ .component_drv = &a1_t9015_codec_driver,
+ .dai_drv = &a1_t9015_dai,
+ .max_register = LINEIN_CFG,
+};
+
static const struct of_device_id t9015_ids[] __maybe_unused = {
{
.compatible = "amlogic,t9015",
.data = &t9015_match_data,
},
+ {
+ .compatible = "amlogic,t9015-a1",
+ .data = &a1_t9015_match_data,
+ },
{ }
};
MODULE_DEVICE_TABLE(of, t9015_ids);
--
2.34.1


2024-03-14 23:26:57

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 16/25] ASoC: dt-bindings: meson: meson-axg-audio-arb: claim support of A1 SoC family

Add "amlogic,meson-a1-audio-arb" compatible string alias to
"amlogic,meson-sm1-audio-arb".

Signed-off-by: Jan Dakinevich <[email protected]>
---
.../bindings/reset/amlogic,meson-axg-audio-arb.yaml | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/reset/amlogic,meson-axg-audio-arb.yaml b/Documentation/devicetree/bindings/reset/amlogic,meson-axg-audio-arb.yaml
index bc1d284785e1..57e977db029f 100644
--- a/Documentation/devicetree/bindings/reset/amlogic,meson-axg-audio-arb.yaml
+++ b/Documentation/devicetree/bindings/reset/amlogic,meson-axg-audio-arb.yaml
@@ -15,9 +15,13 @@ description: The Amlogic Audio ARB is a simple device which enables or disables

properties:
compatible:
- enum:
- - amlogic,meson-axg-audio-arb
- - amlogic,meson-sm1-audio-arb
+ oneOf:
+ - enum:
+ - amlogic,meson-axg-audio-arb
+ - amlogic,meson-sm1-audio-arb
+ - items:
+ - const: amlogic,meson-a1-audio-arb
+ - const: amlogic,meson-sm1-audio-arb

reg:
maxItems: 1
--
2.34.1


2024-03-14 23:27:03

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 10/25] ASoC: meson: t9015: prepare to adding new platforms

Move platform specific code to make the reading of upcoming commit
easier.

Signed-off-by: Jan Dakinevich <[email protected]>
---
sound/soc/meson/t9015.c | 71 ++++++++++++++++++++++++++++-------------
1 file changed, 48 insertions(+), 23 deletions(-)

diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c
index 571f65788c59..48f6767bd858 100644
--- a/sound/soc/meson/t9015.c
+++ b/sound/soc/meson/t9015.c
@@ -51,6 +51,12 @@ struct t9015 {
struct regulator *avdd;
};

+struct t9015_match_data {
+ const struct snd_soc_component_driver *component_drv;
+ struct snd_soc_dai_driver *dai_drv;
+ unsigned int max_register;
+};
+
static int t9015_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_component *component = dai->component;
@@ -112,6 +118,11 @@ static SOC_ENUM_SINGLE_DECL(dacl_in_enum, BLOCK_EN, DACL_SRC, dacl_in_txt);
static const char * const mono_txt[] = { "Stereo", "Mono"};
static SOC_ENUM_SINGLE_DECL(mono_enum, VOL_CTRL1, DAC_MONO, mono_txt);

+static const struct snd_kcontrol_new t9015_right_dac_mux =
+ SOC_DAPM_ENUM("Right DAC Source", dacr_in_enum);
+static const struct snd_kcontrol_new t9015_left_dac_mux =
+ SOC_DAPM_ENUM("Left DAC Source", dacl_in_enum);
+
static const struct snd_kcontrol_new t9015_snd_controls[] = {
/* Volume Controls */
SOC_ENUM("Playback Channel Mode", mono_enum),
@@ -126,11 +137,6 @@ static const struct snd_kcontrol_new t9015_snd_controls[] = {
SOC_SINGLE("Unmute Ramp Switch", VOL_CTRL1, UNMUTE_MODE, 1, 0),
};

-static const struct snd_kcontrol_new t9015_right_dac_mux =
- SOC_DAPM_ENUM("Right DAC Source", dacr_in_enum);
-static const struct snd_kcontrol_new t9015_left_dac_mux =
- SOC_DAPM_ENUM("Left DAC Source", dacl_in_enum);
-
static const struct snd_soc_dapm_widget t9015_dapm_widgets[] = {
SND_SOC_DAPM_AIF_IN("Right IN", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("Left IN", NULL, 0, SND_SOC_NOPM, 0, 0),
@@ -223,7 +229,20 @@ static int t9015_set_bias_level(struct snd_soc_component *component,
return 0;
}

+static int t9015_component_probe(struct snd_soc_component *component)
+{
+ /*
+ * Initialize output polarity:
+ * ATM the output polarity is fixed but in the future it might useful
+ * to add DT property to set this depending on the platform needs
+ */
+ snd_soc_component_write(component, LINEOUT_CFG, 0x1111);
+
+ return 0;
+}
+
static const struct snd_soc_component_driver t9015_codec_driver = {
+ .probe = t9015_component_probe,
.set_bias_level = t9015_set_bias_level,
.controls = t9015_snd_controls,
.num_controls = ARRAY_SIZE(t9015_snd_controls),
@@ -235,22 +254,25 @@ static const struct snd_soc_component_driver t9015_codec_driver = {
.endianness = 1,
};

-static const struct regmap_config t9015_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = POWER_CFG,
-};
-
static int t9015_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ const struct t9015_match_data *data;
struct t9015 *priv;
void __iomem *regs;
+ struct regmap_config config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ };
struct regmap *regmap;
struct clk *pclk;
int ret;

+ data = device_get_match_data(dev);
+ if (!data)
+ dev_err_probe(dev, -ENODEV, "failed to match device\n");
+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -276,25 +298,28 @@ static int t9015_probe(struct platform_device *pdev)
return PTR_ERR(regs);
}

- regmap = devm_regmap_init_mmio(dev, regs, &t9015_regmap_config);
+ config.max_register = data->max_register;
+ regmap = devm_regmap_init_mmio(dev, regs, &config);
if (IS_ERR(regmap)) {
dev_err(dev, "regmap init failed\n");
return PTR_ERR(regmap);
}

- /*
- * Initialize output polarity:
- * ATM the output polarity is fixed but in the future it might useful
- * to add DT property to set this depending on the platform needs
- */
- regmap_write(regmap, LINEOUT_CFG, 0x1111);
-
- return devm_snd_soc_register_component(dev, &t9015_codec_driver,
- &t9015_dai, 1);
+ return devm_snd_soc_register_component(dev, data->component_drv,
+ data->dai_drv, 1);
}

+static const struct t9015_match_data t9015_match_data = {
+ .component_drv = &t9015_codec_driver,
+ .dai_drv = &t9015_dai,
+ .max_register = POWER_CFG,
+};
+
static const struct of_device_id t9015_ids[] __maybe_unused = {
- { .compatible = "amlogic,t9015", },
+ {
+ .compatible = "amlogic,t9015",
+ .data = &t9015_match_data,
+ },
{ }
};
MODULE_DEVICE_TABLE(of, t9015_ids);
--
2.34.1


2024-03-14 23:28:42

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 17/25] ASoC: dt-bindings: meson: axg-fifo: claim support of A1 SoC family

Add "amlogic,a1-toddr" and "amlogic,a1-frddr" compatible string aliases
to "amlogic,sm1-toddr" and "amlogic,sm1-frddr" respectevely.

Signed-off-by: Jan Dakinevich <[email protected]>
---
.../devicetree/bindings/sound/amlogic,axg-fifo.yaml | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-fifo.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-fifo.yaml
index b1b48d683101..1e809c7c2179 100644
--- a/Documentation/devicetree/bindings/sound/amlogic,axg-fifo.yaml
+++ b/Documentation/devicetree/bindings/sound/amlogic,axg-fifo.yaml
@@ -25,6 +25,14 @@ properties:
- amlogic,g12a-frddr
- amlogic,sm1-frddr
- const: amlogic,axg-frddr
+ - items:
+ - const: amlogic,a1-toddr
+ - const: amlogic,sm1-toddr
+ - const: amlogic,axg-toddr
+ - items:
+ - const: amlogic,a1-frddr
+ - const: amlogic,sm1-frddr
+ - const: amlogic,axg-frddr

reg:
maxItems: 1
--
2.34.1


2024-03-14 23:28:46

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 15/25] pinctrl/meson: fix typo in PDM's pin name

Other pins have _a or _x suffix, but this one doesn't have any.

Signed-off-by: Jan Dakinevich <[email protected]>
---
drivers/pinctrl/meson/pinctrl-meson-a1.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/pinctrl/meson/pinctrl-meson-a1.c b/drivers/pinctrl/meson/pinctrl-meson-a1.c
index 79f5d753d7e1..50a87d9618a8 100644
--- a/drivers/pinctrl/meson/pinctrl-meson-a1.c
+++ b/drivers/pinctrl/meson/pinctrl-meson-a1.c
@@ -250,7 +250,7 @@ static const unsigned int pdm_dclk_x_pins[] = { GPIOX_10 };
static const unsigned int pdm_din2_a_pins[] = { GPIOA_6 };
static const unsigned int pdm_din1_a_pins[] = { GPIOA_7 };
static const unsigned int pdm_din0_a_pins[] = { GPIOA_8 };
-static const unsigned int pdm_dclk_pins[] = { GPIOA_9 };
+static const unsigned int pdm_dclk_a_pins[] = { GPIOA_9 };

/* gen_clk */
static const unsigned int gen_clk_x_pins[] = { GPIOX_7 };
@@ -591,7 +591,7 @@ static struct meson_pmx_group meson_a1_periphs_groups[] = {
GROUP(pdm_din2_a, 3),
GROUP(pdm_din1_a, 3),
GROUP(pdm_din0_a, 3),
- GROUP(pdm_dclk, 3),
+ GROUP(pdm_dclk_a, 3),
GROUP(pwm_c_a, 3),
GROUP(pwm_b_a, 3),

@@ -755,7 +755,7 @@ static const char * const spi_a_groups[] = {

static const char * const pdm_groups[] = {
"pdm_din0_x", "pdm_din1_x", "pdm_din2_x", "pdm_dclk_x", "pdm_din2_a",
- "pdm_din1_a", "pdm_din0_a", "pdm_dclk",
+ "pdm_din1_a", "pdm_din0_a", "pdm_dclk_a",
};

static const char * const gen_clk_groups[] = {
--
2.34.1


2024-03-14 23:29:14

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 20/25] ASoC: dt-bindings: meson: axg-tdm-formatters: claim support of A1 SoC family

Add "amlogic,a1-tdmout" and "amlogic,a1-tdmin" compatible string aliases
to "amlogic,sm1-tdmout" and "amlogic,sm1-tdmin" respectevely.

Signed-off-by: Jan Dakinevich <[email protected]>
---
.../sound/amlogic,axg-tdm-formatters.yaml | 22 ++++++++++++-------
1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-formatters.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-formatters.yaml
index 719ca8fc98c7..154522488020 100644
--- a/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-formatters.yaml
+++ b/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-formatters.yaml
@@ -11,14 +11,20 @@ maintainers:

properties:
compatible:
- enum:
- - amlogic,g12a-tdmout
- - amlogic,sm1-tdmout
- - amlogic,axg-tdmout
- - amlogic,g12a-tdmin
- - amlogic,sm1-tdmin
- - amlogic,axg-tdmin
-
+ oneOf:
+ - enum:
+ - amlogic,g12a-tdmout
+ - amlogic,sm1-tdmout
+ - amlogic,axg-tdmout
+ - amlogic,g12a-tdmin
+ - amlogic,sm1-tdmin
+ - amlogic,axg-tdmin
+ - items:
+ - const: amlogic,a1-tdmout
+ - const: amlogic,sm1-tdmout
+ - items:
+ - const: amlogic,a1-tdmin
+ - const: amlogic,sm1-tdmin
clocks:
items:
- description: Peripheral clock
--
2.34.1


2024-03-14 23:29:24

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 25/25] arm64: dts: ad402: enable audio

* playback to speaker

- setup:
$ amixer set "FRDDR_A SINK 1 SEL" "OUT 0"
$ amixer set "FRDDR_A SRC 1 EN" "on"
$ amixer set "TDMOUT_A SRC SEL" "IN 0"
$ amixer set "TOACODEC Source" "I2S A"
$ amixer set "TOACODEC Lane Select" "0"
$ amixer set "TOACODEC EN" "on"
$ amixer set "ACODEC" "70%"
$ amixer set "ACODEC Playback Channel Mode" "Mono"
$ amixer set "ACODEC Right+ Driver Sel" "Right DAC"
$ amixer set "ACODEC Left+ Driver Sel" "Right DAC Inverted"

- usage:
$ aplay -D hw:0,0 -f S16_LE -r 48000 /path/to/sample.wav

* capture from digital mics

- setup:
$ amixer set "TODDR_A SRC SEL" "IN 4"

- usage:
$ arecord -D hw:0,1 -f S32_LE -r 48000 -c 2 -t wav /path/to/sample.wav

* capture from analog mics

- setup:
$ amixer set "TDMIN_A SRC SEL" "IN 3"
$ amixer set "TODDR_B SRC SEL" "IN 0"
$ amixer set "TOACODEC Source" "I2S A"
$ amixer set "TOACODEC Lane Select" "0"
$ amixer set "TOACODEC EN" "on"
$ amixer set "ACODEC ADC" "70%"
$ amixer set "ACODEC ADC Filter" "on"
$ amixer set "ACODEC ADC Filter Mode" "HiFi"
$ amixer set "ACODEC ADC Mic Bias" "on"
$ amixer set "ACODEC ADC Mic Bias Level" "2.5V"
$ amixer set "ACODEC ADC PGA" "50%"
$ amixer set "ACODEC ADC PGA Right Sel" "Differential"
$ amixer set "ACODEC ADC PGA Left Sel" "Differential"

- usage:
$ arecord -D hw:0,2 -f S16_LE -r 48000 -c 2 -t wav /path/to/sample.wav

* capture from TDM loopback

- setup:
$ amixer set "TDMIN_LB SRC SEL" "IN 0"
$ amixer set "TODDR_A SRC SEL" "IN 6"

- usage:
$ arecord -D hw:0,1 -f S16_LE -r 48000 -c 2 -t wav /path/to/sample.wav

or

- setup:
$ amixer set "TDMIN_LB SRC SEL" "IN 0"
$ amixer set "TODDR_B SRC SEL" "IN 6"

- usage:
$ arecord -D hw:0,2 -f S16_LE -r 48000 -c 2 -t wav /path/to/sample.wav

Signed-off-by: Jan Dakinevich <[email protected]>
Signed-off-by: Dmitry Rokosov <[email protected]>
---
.../arm64/boot/dts/amlogic/meson-a1-ad402.dts | 126 ++++++++++++++++++
1 file changed, 126 insertions(+)

diff --git a/arch/arm64/boot/dts/amlogic/meson-a1-ad402.dts b/arch/arm64/boot/dts/amlogic/meson-a1-ad402.dts
index 4bc30af05848..4e0865a4b44e 100644
--- a/arch/arm64/boot/dts/amlogic/meson-a1-ad402.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-a1-ad402.dts
@@ -8,6 +8,8 @@

#include "meson-a1.dtsi"

+#include <dt-bindings/gpio/gpio.h>
+
/ {
compatible = "amlogic,ad402", "amlogic,a1";
model = "Amlogic Meson A1 AD402 Development Board";
@@ -83,6 +85,100 @@ vddio_1v8: regulator-vddio-1v8 {
vin-supply = <&vddao_3v3>;
regulator-always-on;
};
+
+ amplifier: amplifier {
+ compatible = "simple-audio-amplifier";
+ sound-name-prefix = "AMPLIFIER";
+ enable-gpios = <&gpio GPIOF_4 GPIO_ACTIVE_HIGH>;
+ VCC-supply = <&battery_4v2>;
+ };
+
+ dmics: dmics {
+ compatible = "dmic-codec";
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "MIC";
+ num-channels = <4>;
+ wakeup-delay-ms = <50>;
+ };
+
+ sound {
+ compatible = "amlogic,a1-sound-card",
+ "amlogic,axg-sound-card";
+ model = "AD402";
+ audio-aux-devs = <&tdmout_a>,
+ <&amplifier>,
+ <&tdmin_lb>,
+ <&tdmin_a>;
+ audio-routing = "TDMOUT_A IN 0", "FRDDR_A OUT 0",
+ "TDM_A Playback", "TDMOUT_A OUT",
+ "AMPLIFIER INL", "ACODEC LOLP",
+ "AMPLIFIER INR", "ACODEC LORP",
+
+ "TODDR_A IN 4", "PDM Capture",
+
+ "TDMIN_A IN 3", "TDM_A Capture",
+ "TODDR_B IN 0", "TDMIN_A OUT",
+
+ "TDMIN_LB IN 0", "TDM_A Loopback",
+ "TODDR_A IN 6", "TDMIN_LB OUT",
+ "TDMIN_LB IN 0", "TDM_A Loopback",
+ "TODDR_B IN 6", "TDMIN_LB OUT";
+
+ dai-link-0 {
+ link-name = "speaker";
+ sound-dai = <&frddr_a>;
+ };
+
+ dai-link-1 {
+ link-name = "dmics";
+ sound-dai = <&toddr_a>;
+ };
+
+ dai-link-2 {
+ link-name = "amics";
+ sound-dai = <&toddr_b>;
+ };
+
+ dai-link-3 {
+ sound-dai = <&tdmif_a>;
+ dai-format = "i2s";
+ dai-tdm-slot-tx-mask-0 = <1 1>;
+ dai-tdm-slot-rx-mask-0 = <1 1>;
+ mclk-fs = <256>;
+
+ codec-0 {
+ sound-dai = <&toacodec TOACODEC_IN_A>;
+ };
+
+ codec-1 {
+ sound-dai = <&toacodec TOACODEC_CAPTURE_OUT_A>;
+ };
+ };
+
+ dai-link-4 {
+ sound-dai = <&toacodec TOACODEC_OUT>;
+
+ codec {
+ sound-dai = <&acodec>;
+ };
+ };
+
+ dai-link-5 {
+ sound-dai = <&toacodec TOACODEC_CAPTURE_IN>;
+
+ codec {
+ sound-dai = <&acodec>;
+ };
+ };
+
+ dai-link-6 {
+ sound-dai = <&pdm>;
+
+ codec {
+ sound-dai = <&dmics>;
+ };
+ };
+ };
};

/* Bluetooth HCI H4 */
@@ -145,3 +241,33 @@ &sd_emmc {
vmmc-supply = <&vddao_3v3>;
vqmmc-supply = <&vddio_1v8>;
};
+
+&clkc_audio {
+ assigned-clocks = <&clkc_pll CLKID_HIFI_PLL>,
+ <&clkc_audio AUD_CLKID_MST_A_MCLK_SEL>,
+ <&clkc_audio AUD_CLKID_MST_A_MCLK>,
+ <&clkc_audio AUD2_CLKID_PDM_SYSCLK_SEL>,
+ <&clkc_audio AUD2_CLKID_PDM_DCLK_SEL>;
+ assigned-clock-parents = <0>,
+ <&clkc_pll CLKID_HIFI_PLL>,
+ <0>,
+ <&clkc_pll CLKID_FCLK_DIV3>,
+ <&clkc_pll CLKID_FCLK_DIV2>;
+ assigned-clock-rates = <614400000>,
+ <0>,
+ <12288000>,
+ <512000000>,
+ <768000000>;
+};
+
+&acodec {
+ AVDD-supply = <&vddio_1v8>;
+};
+
+&pdm {
+ sysrate = <256000000>;
+ pinctrl-0 = <&pdm_din0_a_pins>,
+ <&pdm_din1_a_pins>,
+ <&pdm_dclk_a_pins>;
+ pinctrl-names = "default";
+};
--
2.34.1


2024-03-14 23:29:39

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 24/25] arm64: dts: meson: a1: add audio devices

This commit adds the declarartion of currently supported audio devices
and their pins found Amlog A1 SoC family. SPDIF declaration is missing
here becase there are no publicly available boards with it.

Signed-off-by: Jan Dakinevich <[email protected]>
---
arch/arm64/boot/dts/amlogic/meson-a1.dtsi | 471 ++++++++++++++++++++++
1 file changed, 471 insertions(+)

diff --git a/arch/arm64/boot/dts/amlogic/meson-a1.dtsi b/arch/arm64/boot/dts/amlogic/meson-a1.dtsi
index c03e207ea6c5..eaedc2d849db 100644
--- a/arch/arm64/boot/dts/amlogic/meson-a1.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-a1.dtsi
@@ -5,11 +5,15 @@

#include <dt-bindings/clock/amlogic,a1-pll-clkc.h>
#include <dt-bindings/clock/amlogic,a1-peripherals-clkc.h>
+#include <dt-bindings/clock/amlogic,a1-audio-clkc.h>
#include <dt-bindings/gpio/meson-a1-gpio.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/power/meson-a1-power.h>
#include <dt-bindings/reset/amlogic,meson-a1-reset.h>
+#include <dt-bindings/reset/amlogic,meson-a1-audio-reset.h>
+#include <dt-bindings/reset/amlogic,meson-axg-audio-arb.h>
+#include <dt-bindings/sound/meson-g12a-toacodec.h>

/ {
compatible = "amlogic,a1";
@@ -280,6 +284,250 @@ mux {
function = "spif";
};
};
+
+ tdm_a_din0_pins: tdm-a-din0 {
+ mux {
+ groups = "tdm_a_din0";
+ function = "tdm_a";
+ bias-disable;
+ };
+ };
+
+ tdm_a_din1_pins: tdm-a-din1 {
+ mux {
+ groups = "tdm_a_din1";
+ function = "tdm_a";
+ bias-disable;
+ };
+ };
+
+ tdm_a_dout0_pins: tdm-a-dout0 {
+ mux {
+ groups = "tdm_a_dout0";
+ function = "tdm_a";
+ bias-disable;
+ drive-strength-microamp = <3000>;
+ };
+ };
+
+ tdm_a_dout1_pins: tdm-a-dout1 {
+ mux {
+ groups = "tdm_a_dout1";
+ function = "tdm_a";
+ bias-disable;
+ drive-strength-microamp = <3000>;
+ };
+ };
+
+ tdm_a_fs_pins: tdm-a-fs {
+ mux {
+ groups = "tdm_a_fs";
+ function = "tdm_a";
+ bias-disable;
+ drive-strength-microamp = <3000>;
+ };
+ };
+
+ tdm_a_sclk_pins: tdm-a-sclk {
+ mux {
+ groups = "tdm_a_sclk";
+ function = "tdm_a";
+ bias-disable;
+ drive-strength-microamp = <3000>;
+ };
+ };
+
+ tdm_a_slv_fs_pins: tdm-a-slv-fs {
+ mux {
+ groups = "tdm_a_slv_fs";
+ function = "tdm_a";
+ bias-disable;
+ };
+ };
+
+ tdm_a_slv_sclk_pins: tdm-a-slv-sclk {
+ mux {
+ groups = "tdm_a_slv_sclk";
+ function = "tdm_a";
+ bias-disable;
+ };
+ };
+
+ tdm_b_din0_pins: tdm-b-din0 {
+ mux {
+ groups = "tdm_b_din0";
+ function = "tdm_b";
+ bias-disable;
+ };
+ };
+
+ tdm_b_din1_pins: tdm-b-din1 {
+ mux {
+ groups = "tdm_b_din1";
+ function = "tdm_b";
+ bias-disable;
+ };
+ };
+
+ tdm_b_din2_pins: tdm-b-din2 {
+ mux {
+ groups = "tdm_b_din2";
+ function = "tdm_b";
+ bias-disable;
+ };
+ };
+
+ tdm_b_dout0_pins: tdm-b-dout0 {
+ mux {
+ groups = "tdm_b_dout0";
+ function = "tdm_b";
+ bias-disable;
+ drive-strength-microamp = <3000>;
+ };
+ };
+
+ tdm_b_dout1_pins: tdm-b-dout1 {
+ mux {
+ groups = "tdm_b_dout1";
+ function = "tdm_b";
+ bias-disable;
+ drive-strength-microamp = <3000>;
+ };
+ };
+
+ tdm_b_dout2_pins: tdm-b-dout2 {
+ mux {
+ groups = "tdm_b_dout2";
+ function = "tdm_b";
+ bias-disable;
+ drive-strength-microamp = <3000>;
+ };
+ };
+
+ tdm_b_dout3_pins: tdm-b-dout3 {
+ mux {
+ groups = "tdm_b_dout3";
+ function = "tdm_b";
+ bias-disable;
+ drive-strength-microamp = <3000>;
+ };
+ };
+
+ tdm_b_dout4_pins: tdm-b-dout4 {
+ mux {
+ groups = "tdm_b_dout4";
+ function = "tdm_b";
+ bias-disable;
+ drive-strength-microamp = <3000>;
+ };
+ };
+
+ tdm_b_dout5_pins: tdm-b-dout5 {
+ mux {
+ groups = "tdm_b_dout5";
+ function = "tdm_b";
+ bias-disable;
+ drive-strength-microamp = <3000>;
+ };
+ };
+
+ tdm_b_fs_pins: tdm-b-fs {
+ mux {
+ groups = "tdm_b_fs";
+ function = "tdm_b";
+ bias-disable;
+ drive-strength-microamp = <3000>;
+ };
+ };
+
+ tdm_b_sclk_pins: tdm-b-sclk {
+ mux {
+ groups = "tdm_b_sclk";
+ function = "tdm_b";
+ bias-disable;
+ drive-strength-microamp = <3000>;
+ };
+ };
+
+ tdm_b_slv_fs_pins: tdm-b-slv-fs {
+ mux {
+ groups = "tdm_b_slv_fs";
+ function = "tdm_b";
+ bias-disable;
+ };
+ };
+
+ tdm_b_slv_sclk_pins: tdm-b-slv-sclk {
+ mux {
+ groups = "tdm_b_slv_sclk";
+ function = "tdm_b";
+ bias-disable;
+ };
+ };
+
+ pdm_din0_a_pins: pdm-din0-a {
+ mux {
+ groups = "pdm_din0_a";
+ function = "pdm";
+ bias-disable;
+ };
+ };
+
+ pdm_din0_x_pins: pdm-din0-x {
+ mux {
+ groups = "pdm_din0_x";
+ function = "pdm";
+ bias-disable;
+ };
+ };
+
+ pdm_din1_a_pins: pdm-din1-a {
+ mux {
+ groups = "pdm_din1_a";
+ function = "pdm";
+ bias-disable;
+ };
+ };
+
+ pdm_din1_x_pins: pdm-din1-x {
+ mux {
+ groups = "pdm_din1_x";
+ function = "pdm";
+ bias-disable;
+ };
+ };
+
+ pdm_din2_a_pins: pdm-din2-a {
+ mux {
+ groups = "pdm_din2_a";
+ function = "pdm";
+ bias-disable;
+ };
+ };
+
+ pdm_din2_x_pins: pdm-din2-x {
+ mux {
+ groups = "pdm_din2_x";
+ function = "pdm";
+ bias-disable;
+ };
+ };
+
+ pdm_dclk_a_pins: pdm-dclk-a {
+ mux {
+ groups = "pdm_dclk_a";
+ function = "pdm";
+ bias-disable;
+ };
+ };
+
+ pdm_dclk_x_pins: pdm-dclk-x {
+ mux {
+ groups = "pdm_dclk_x";
+ function = "pdm";
+ bias-disable;
+ };
+ };
};

gpio_intc: interrupt-controller@440 {
@@ -435,6 +683,207 @@ sd_emmc: sd@10000 {
power-domains = <&pwrc PWRC_SD_EMMC_ID>;
status = "disabled";
};
+
+ acodec: audio-controller@4800 {
+ compatible = "amlogic,t9015-a1";
+ reg = <0x0 0x4800 0x0 0x14>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "ACODEC";
+ clocks = <&clkc_periphs CLKID_AUDIO>;
+ clock-names = "pclk";
+ resets = <&reset RESET_ACODEC>;
+ power-domains = <&pwrc PWRC_ACODEC_ID>;
+ };
+
+ audio: bus@50000 {
+ compatible = "simple-bus";
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges = <0x0 0x0 0x0 0x50000 0 0x4980>;
+ power-domains = <&pwrc PWRC_AUDIO_ID>;
+
+ clkc_audio: audio-clock-controller@0 {
+ compatible = "amlogic,a1-audio-clkc";
+ reg = <0x0 0x0 0x0 0xb0>,
+ <0x0 0x4800 0x0 0x20>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ clocks = <&clkc_periphs CLKID_AUDIO>,
+ <&clkc_periphs CLKID_DDS_IN>,
+ <&clkc_pll CLKID_FCLK_DIV2>,
+ <&clkc_pll CLKID_FCLK_DIV3>,
+ <&clkc_pll CLKID_HIFI_PLL>,
+ <&xtal>;
+ clock-names = "pclk", "dds_in",
+ "fclk_div2", "fclk_div3",
+ "hifi_pll", "xtal";
+ };
+
+ toddr_a: audio-controller@100 {
+ compatible = "amlogic,a1-toddr",
+ "amlogic,sm1-toddr",
+ "amlogic,axg-toddr";
+ reg = <0x0 0x100 0x0 0x2c>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "TODDR_A";
+ interrupts = <GIC_SPI 37 IRQ_TYPE_EDGE_RISING>;
+ clocks = <&clkc_audio AUD_CLKID_TODDR_A>;
+ resets = <&arb AXG_ARB_TODDR_A>,
+ <&clkc_audio AUD_RESET_TODDR_A>;
+ reset-names = "arb", "rst";
+ amlogic,fifo-depth = <128>;
+ };
+
+ toddr_b: audio-controller@140 {
+ compatible = "amlogic,a1-toddr",
+ "amlogic,sm1-toddr",
+ "amlogic,axg-toddr";
+ reg = <0x0 0x140 0x0 0x2c>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "TODDR_B";
+ interrupts = <GIC_SPI 38 IRQ_TYPE_EDGE_RISING>;
+ clocks = <&clkc_audio AUD_CLKID_TODDR_B>;
+ resets = <&arb AXG_ARB_TODDR_B>,
+ <&clkc_audio AUD_RESET_TODDR_B>;
+ reset-names = "arb", "rst";
+ amlogic,fifo-depth = <128>;
+ };
+
+ frddr_a: audio-controller@1c0 {
+ compatible = "amlogic,a1-frddr",
+ "amlogic,sm1-frddr",
+ "amlogic,axg-frddr";
+ reg = <0x0 0x1c0 0x0 0x2c>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "FRDDR_A";
+ interrupts = <GIC_SPI 40 IRQ_TYPE_EDGE_RISING>;
+ clocks = <&clkc_audio AUD_CLKID_FRDDR_A>;
+ resets = <&arb AXG_ARB_FRDDR_A>,
+ <&clkc_audio AUD_RESET_FRDDR_A>;
+ reset-names = "arb", "rst";
+ amlogic,fifo-depth = <128>;
+ };
+
+ frddr_b: audio-controller@200 {
+ compatible = "amlogic,a1-frddr",
+ "amlogic,sm1-frddr",
+ "amlogic,axg-frddr";
+ reg = <0x0 0x200 0x0 0x2c>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "FRDDR_B";
+ interrupts = <GIC_SPI 41 IRQ_TYPE_EDGE_RISING>;
+ clocks = <&clkc_audio AUD_CLKID_FRDDR_B>;
+ resets = <&arb AXG_ARB_FRDDR_B>,
+ <&clkc_audio AUD_RESET_FRDDR_B>;
+ reset-names = "arb", "rst";
+ amlogic,fifo-depth = <128>;
+ };
+
+ arb: reset-controller@280 {
+ compatible = "amlogic,meson-a1-audio-arb",
+ "amlogic,meson-sm1-audio-arb";
+ reg = <0x0 0x280 0x0 0x4>;
+ #reset-cells = <1>;
+ clocks = <&clkc_audio AUD_CLKID_DDR_ARB>;
+ };
+
+ tdmin_a: audio-controller@300 {
+ compatible = "amlogic,a1-tdmin",
+ "amlogic,sm1-tdmin";
+ reg = <0x0 0x300 0x0 0x40>;
+ sound-name-prefix = "TDMIN_A";
+ clocks = <&clkc_audio AUD_CLKID_TDMIN_A>,
+ <&clkc_audio AUD_CLKID_TDMIN_A_SCLK>,
+ <&clkc_audio AUD_CLKID_TDMIN_A_SCLK_SEL>,
+ <&clkc_audio AUD_CLKID_TDMIN_A_LRCLK>,
+ <&clkc_audio AUD_CLKID_TDMIN_A_LRCLK>;
+ clock-names = "pclk", "sclk", "sclk_sel",
+ "lrclk", "lrclk_sel";
+ resets = <&clkc_audio AUD_RESET_TDMIN_A>;
+ };
+
+ tdmin_b: audio-controller@340 {
+ compatible = "amlogic,a1-tdmin",
+ "amlogic,sm1-tdmin";
+ reg = <0x0 0x340 0x0 0x40>;
+ sound-name-prefix = "TDMIN_B";
+ clocks = <&clkc_audio AUD_CLKID_TDMIN_B>,
+ <&clkc_audio AUD_CLKID_TDMIN_B_SCLK>,
+ <&clkc_audio AUD_CLKID_TDMIN_B_SCLK_SEL>,
+ <&clkc_audio AUD_CLKID_TDMIN_B_LRCLK>,
+ <&clkc_audio AUD_CLKID_TDMIN_B_LRCLK>;
+ clock-names = "pclk", "sclk", "sclk_sel",
+ "lrclk", "lrclk_sel";
+ resets = <&clkc_audio AUD_RESET_TDMIN_B>;
+ };
+
+ tdmin_lb: audio-controller@3c0 {
+ compatible = "amlogic,a1-tdmin",
+ "amlogic,sm1-tdmin";
+ reg = <0x0 0x3c0 0x0 0x40>;
+ sound-name-prefix = "TDMIN_LB";
+ clocks = <&clkc_audio AUD_CLKID_TDMIN_LB>,
+ <&clkc_audio AUD_CLKID_TDMIN_LB_SCLK>,
+ <&clkc_audio AUD_CLKID_TDMIN_LB_SCLK_SEL>,
+ <&clkc_audio AUD_CLKID_TDMIN_LB_LRCLK>,
+ <&clkc_audio AUD_CLKID_TDMIN_LB_LRCLK>;
+ clock-names = "pclk", "sclk", "sclk_sel",
+ "lrclk", "lrclk_sel";
+ resets = <&clkc_audio AUD_RESET_TDMIN_LB>;
+ };
+
+ tdmout_a: audio-controller@500 {
+ compatible = "amlogic,a1-tdmout",
+ "amlogic,sm1-tdmout";
+ reg = <0x0 0x500 0x0 0x40>;
+ sound-name-prefix = "TDMOUT_A";
+ clocks = <&clkc_audio AUD_CLKID_TDMOUT_A>,
+ <&clkc_audio AUD_CLKID_TDMOUT_A_SCLK>,
+ <&clkc_audio AUD_CLKID_TDMOUT_A_SCLK_SEL>,
+ <&clkc_audio AUD_CLKID_TDMOUT_A_LRCLK>,
+ <&clkc_audio AUD_CLKID_TDMOUT_A_LRCLK>;
+ clock-names = "pclk", "sclk", "sclk_sel",
+ "lrclk", "lrclk_sel";
+ resets = <&clkc_audio AUD_RESET_TDMOUT_A>;
+ };
+
+ tdmout_b: audio-controller@540 {
+ compatible = "amlogic,a1-tdmout",
+ "amlogic,sm1-tdmout";
+ reg = <0x0 0x540 0x0 0x40>;
+ sound-name-prefix = "TDMOUT_B";
+ clocks = <&clkc_audio AUD_CLKID_TDMOUT_B>,
+ <&clkc_audio AUD_CLKID_TDMOUT_B_SCLK>,
+ <&clkc_audio AUD_CLKID_TDMOUT_B_SCLK_SEL>,
+ <&clkc_audio AUD_CLKID_TDMOUT_B_LRCLK>,
+ <&clkc_audio AUD_CLKID_TDMOUT_B_LRCLK>;
+ clock-names = "pclk", "sclk", "sclk_sel",
+ "lrclk", "lrclk_sel";
+ resets = <&clkc_audio AUD_RESET_TDMOUT_B>;
+ };
+
+ toacodec: audio-controller@740 {
+ compatible = "amlogic,a1-toacodec",
+ "amlogic,g12a-toacodec";
+ reg = <0x0 0x740 0x0 0x4>;
+ #sound-dai-cells = <1>;
+ sound-name-prefix = "TOACODEC";
+ resets = <&clkc_audio AUD_RESET_TOACODEC>;
+ };
+
+ pdm: audio-controller@1000 {
+ compatible = "amlogic,a1-pdm",
+ "amlogic,axg-pdm";
+ reg = <0x0 0x1000 0x0 0x34>;
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "PDM";
+ clocks = <&clkc_audio AUD2_CLKID_PDM>,
+ <&clkc_audio AUD2_CLKID_PDM_DCLK>,
+ <&clkc_audio AUD2_CLKID_PDM_SYSCLK>;
+ clock-names = "pclk", "dclk", "sysclk";
+ power-domains = <&pwrc PWRC_PDMIN_ID>;
+ };
+ };
};

usb: usb@fe004400 {
@@ -517,4 +966,26 @@ xtal: xtal-clk {
clock-output-names = "xtal";
#clock-cells = <0>;
};
+
+ tdmif_a: audio-controller-0 {
+ compatible = "amlogic,a1-tdm-iface",
+ "amlogic,axg-tdm-iface";
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "TDM_A";
+ clocks = <&clkc_audio AUD_CLKID_MST_A_SCLK>,
+ <&clkc_audio AUD_CLKID_MST_A_LRCLK>,
+ <&clkc_audio AUD_CLKID_MST_A_MCLK>;
+ clock-names = "sclk", "lrclk", "mclk";
+ };
+
+ tdmif_b: audio-controller-1 {
+ compatible = "amlogic,a1-tdm-iface",
+ "amlogic,axg-tdm-iface";
+ #sound-dai-cells = <0>;
+ sound-name-prefix = "TDM_B";
+ clocks = <&clkc_audio AUD_CLKID_MST_B_SCLK>,
+ <&clkc_audio AUD_CLKID_MST_B_LRCLK>,
+ <&clkc_audio AUD_CLKID_MST_B_MCLK>;
+ clock-names = "sclk", "lrclk", "mclk";
+ };
};
--
2.34.1


2024-03-14 23:31:07

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 23/25] ASoC: meson: implement link-name optional property in meson card utils

From: Dmitry Rokosov <[email protected]>

The 'link-name' property presents an optional DT feature that empowers
users to customize the name associated with the DAI link and PCM stream.
This functionality reflects the approach often employed in Qualcomm
audio cards, providing enhanced flexibility in DAI naming conventions
for improved system integration and userspace experience.

It allows userspace program to easy determine PCM stream purpose, e.g.:
~ # cat /proc/asound/pcm
00-00: speaker (*) : : playback 1
00-01: mics (*) : : capture 1
00-02: loopback (*) : : capture 1

The previous naming approach using auto-generated fe or be strings
continues to be utilized as a fallback.

Signed-off-by: Dmitry Rokosov <[email protected]>
---
sound/soc/meson/meson-card-utils.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c
index ed6c7e2f609c..7bae72905a9b 100644
--- a/sound/soc/meson/meson-card-utils.c
+++ b/sound/soc/meson/meson-card-utils.c
@@ -94,10 +94,14 @@ static int meson_card_set_link_name(struct snd_soc_card *card,
struct device_node *node,
const char *prefix)
{
- char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s",
- prefix, node->full_name);
- if (!name)
- return -ENOMEM;
+ const char *name;
+
+ if (of_property_read_string(node, "link-name", &name)) {
+ name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s",
+ prefix, node->full_name);
+ if (!name)
+ return -ENOMEM;
+ }

link->name = name;
link->stream_name = name;
--
2.34.1


2024-03-14 23:31:08

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 22/25] ASoC: dt-bindings: meson: introduce link-name optional property

From: Dmitry Rokosov <[email protected]>

The 'link-name' property is an optional DT property that allows for the
customization of the name associated with the DAI link and PCM stream.
This functionality mirrors the approach commonly utilized in Qualcomm
audio cards, providing flexibility in DAI naming conventions for
improved system integration and userspace experience.

It allows userspace program to easy determine PCM stream purpose, e.g.:
~ # cat /proc/asound/pcm
00-00: speaker (*) : : playback 1
00-01: mics (*) : : capture 1
00-02: loopback (*) : : capture 1

Signed-off-by: Dmitry Rokosov <[email protected]>
---
.../devicetree/bindings/sound/amlogic,axg-sound-card.yaml | 6 ++++++
.../devicetree/bindings/sound/amlogic,gx-sound-card.yaml | 6 ++++++
2 files changed, 12 insertions(+)

diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml
index 492b41cc8ccd..46774a3e4b1d 100644
--- a/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml
+++ b/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml
@@ -66,6 +66,11 @@ patternProperties:
maxItems: 1
description: phandle of the CPU DAI

+ link-name:
+ description: Indicates dai-link name and PCM stream name.
+ $ref: /schemas/types.yaml#/definitions/string
+ maxItems: 1
+
patternProperties:
"^dai-tdm-slot-(t|r)x-mask-[0-3]$":
$ref: /schemas/types.yaml#/definitions/uint32-array
@@ -137,6 +142,7 @@ examples:

dai-link-0 {
sound-dai = <&frddr_a>;
+ link-name = "speaker";
};

dai-link-1 {
diff --git a/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml
index d4277d342e69..975c148f9712 100644
--- a/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml
+++ b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml
@@ -52,6 +52,11 @@ patternProperties:
maxItems: 1
description: phandle of the CPU DAI

+ link-name:
+ description: Indicates dai-link name and PCM stream name.
+ $ref: /schemas/types.yaml#/definitions/string
+ maxItems: 1
+
patternProperties:
"^codec(-[0-9]+)?$":
type: object
@@ -89,6 +94,7 @@ examples:

dai-link-0 {
sound-dai = <&i2s_fifo>;
+ link-name = "speaker";
};

dai-link-1 {
--
2.34.1


2024-03-14 23:32:01

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 09/25] ASoC: meson: g12a-toacodec: add support for A1 SoC family

Internal codec on A1 SoC has ADC and this commit adds support for
capturing from it.

Also, regmap now uses caching for all platforms. Values that are written
to toacodec's register on A1 SoC can not be read back, that breaks
regmap_update_bits() functionality. Let's hope the caching will not
break anything on other platforms.

Signed-off-by: Jan Dakinevich <[email protected]>
---
sound/soc/meson/g12a-toacodec.c | 221 ++++++++++++++++++++++++++++++--
1 file changed, 208 insertions(+), 13 deletions(-)

diff --git a/sound/soc/meson/g12a-toacodec.c b/sound/soc/meson/g12a-toacodec.c
index 325cf420fd69..21e569e72157 100644
--- a/sound/soc/meson/g12a-toacodec.c
+++ b/sound/soc/meson/g12a-toacodec.c
@@ -50,6 +50,23 @@
#define CTRL0_BCLK_SEL_SM1_MSB 6
#define CTRL0_BCLK_SEL_SM1_LSB 4

+/* A1 bits */
+#define CTRL0_DAT_CAPTURE_SEL_A1_MSB 23
+#define CTRL0_DAT_CAPTURE_SEL_A1_LSB 23
+#define CTRL0_LANE_CAPTURE_SEL_A1_MSB 22
+#define CTRL0_LANE_CAPTURE_SEL_A1_LSB 20
+#define CTRL0_DAT_SEL_A1_MSB 19
+#define CTRL0_DAT_SEL_A1_LSB 19
+#define CTRL0_LANE_SEL_A1_MSB 18
+#define CTRL0_LANE_SEL_A1_LSB 16
+#define CTRL0_LRCLK_SEL_A1_MSB 14
+#define CTRL0_LRCLK_SEL_A1_LSB 12
+#define CTRL0_LRCLK_INV_A1 BIT(10)
+#define CTRL0_BLK_CAP_INV_A1 BIT(9)
+#define CTRL0_BCLK_O_INV_A1 BIT(8)
+#define CTRL0_BCLK_SEL_A1_MSB 6
+#define CTRL0_BCLK_SEL_A1_LSB 4
+
#define TOACODEC_OUT_CHMAX 2

struct g12a_toacodec {
@@ -60,6 +77,8 @@ struct g12a_toacodec {

struct g12a_toacodec_match_data {
const struct snd_soc_component_driver *component_drv;
+ struct snd_soc_dai_driver *dai_drv;
+ int num_dai_drv;
struct reg_field field_dat_sel;
struct reg_field field_lrclk_sel;
struct reg_field field_bclk_sel;
@@ -69,6 +88,10 @@ static const char * const g12a_toacodec_mux_texts[] = {
"I2S A", "I2S B", "I2S C",
};

+static const char * const a1_toacodec_mux_texts[] = {
+ "I2S A", "I2S B",
+};
+
static int g12a_toacodec_mux_put_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -124,8 +147,12 @@ static SOC_ENUM_SINGLE_DECL(sm1_toacodec_mux_enum, TOACODEC_CTRL0,
CTRL0_DAT_SEL_SM1_LSB,
g12a_toacodec_mux_texts);

-static const struct snd_kcontrol_new g12a_toacodec_mux =
- SOC_DAPM_ENUM_EXT("Source", g12a_toacodec_mux_enum,
+static SOC_ENUM_SINGLE_DECL(a1_toacodec_mux_enum, TOACODEC_CTRL0,
+ CTRL0_DAT_SEL_A1_LSB,
+ a1_toacodec_mux_texts);
+
+static const struct snd_kcontrol_new a1_toacodec_mux =
+ SOC_DAPM_ENUM_EXT("Source", a1_toacodec_mux_enum,
snd_soc_dapm_get_enum_double,
g12a_toacodec_mux_put_enum);

@@ -134,10 +161,21 @@ static const struct snd_kcontrol_new sm1_toacodec_mux =
snd_soc_dapm_get_enum_double,
g12a_toacodec_mux_put_enum);

+static const struct snd_kcontrol_new g12a_toacodec_mux =
+ SOC_DAPM_ENUM_EXT("Source", g12a_toacodec_mux_enum,
+ snd_soc_dapm_get_enum_double,
+ g12a_toacodec_mux_put_enum);
+
static const struct snd_kcontrol_new g12a_toacodec_out_enable =
SOC_DAPM_SINGLE_AUTODISABLE("Switch", TOACODEC_CTRL0,
CTRL0_ENABLE_SHIFT, 1, 0);

+/* Don't use AUTODISABLE unlike G12A. On A1 it causes noise after playback
+ * is stopped.
+ */
+static const struct snd_kcontrol_new a1_toacodec_enable =
+ SOC_DAPM_SINGLE("Switch", TOACODEC_CTRL0, CTRL0_ENABLE_SHIFT, 1, 0);
+
static const struct snd_soc_dapm_widget g12a_toacodec_widgets[] = {
SND_SOC_DAPM_MUX("SRC", SND_SOC_NOPM, 0, 0,
&g12a_toacodec_mux),
@@ -152,19 +190,34 @@ static const struct snd_soc_dapm_widget sm1_toacodec_widgets[] = {
&g12a_toacodec_out_enable),
};

-static int g12a_toacodec_input_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+static const struct snd_soc_dapm_widget a1_toacodec_widgets[] = {
+ SND_SOC_DAPM_SWITCH("EN", SND_SOC_NOPM, 0, 0,
+ &a1_toacodec_enable),
+ SND_SOC_DAPM_MUX("Playback SRC", SND_SOC_NOPM, 0, 0,
+ &a1_toacodec_mux),
+ SND_SOC_DAPM_MUX("Capture SRC A", SND_SOC_NOPM, 0, 0,
+ &a1_toacodec_mux),
+ SND_SOC_DAPM_MUX("Capture SRC B", SND_SOC_NOPM, 0, 0,
+ &a1_toacodec_mux),
+};
+
+static int g12a_toacodec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai,
+ bool playback)
{
struct meson_codec_glue_input *data;
int ret;

- ret = meson_codec_glue_input_hw_params(substream, params, dai);
+ ret = playback ? meson_codec_glue_input_hw_params(substream, params, dai)
+ : meson_codec_glue_capture_output_hw_params(substream,
+ params, dai);
if (ret)
return ret;

/* The glue will provide 1 lane out of the 4 to the output */
- data = meson_codec_glue_input_get_data(dai);
+ data = playback ? meson_codec_glue_input_get_data(dai)
+ : meson_codec_glue_capture_output_get_data(dai);
data->params.channels_min = min_t(unsigned int, TOACODEC_OUT_CHMAX,
data->params.channels_min);
data->params.channels_max = min_t(unsigned int, TOACODEC_OUT_CHMAX,
@@ -173,6 +226,21 @@ static int g12a_toacodec_input_hw_params(struct snd_pcm_substream *substream,
return 0;
}

+static int g12a_toacodec_input_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ return g12a_toacodec_hw_params(substream, params, dai, true);
+}
+
+static int
+g12a_toacodec_capture_output_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ return g12a_toacodec_hw_params(substream, params, dai, false);
+}
+
static const struct snd_soc_dai_ops g12a_toacodec_input_ops = {
.probe = meson_codec_glue_input_dai_probe,
.remove = meson_codec_glue_input_dai_remove,
@@ -184,6 +252,17 @@ static const struct snd_soc_dai_ops g12a_toacodec_output_ops = {
.startup = meson_codec_glue_output_startup,
};

+static const struct snd_soc_dai_ops g12a_toacodec_capture_output_ops = {
+ .probe = meson_codec_glue_capture_output_dai_probe,
+ .remove = meson_codec_glue_capture_output_dai_remove,
+ .hw_params = g12a_toacodec_capture_output_hw_params,
+ .set_fmt = meson_codec_glue_capture_output_set_fmt,
+};
+
+static const struct snd_soc_dai_ops g12a_toacodec_capture_input_ops = {
+ .startup = meson_codec_glue_capture_input_startup,
+};
+
#define TOACODEC_STREAM(xname, xsuffix, xchmax) \
{ \
.stream_name = xname " " xsuffix, \
@@ -208,6 +287,20 @@ static const struct snd_soc_dai_ops g12a_toacodec_output_ops = {
.ops = &g12a_toacodec_output_ops, \
}

+#define TOACODEC_CAPTURE_INPUT(xname, xid) { \
+ .name = xname, \
+ .id = (xid), \
+ .playback = TOACODEC_STREAM(xname, "Playback", 2), \
+ .ops = &g12a_toacodec_capture_input_ops, \
+}
+
+#define TOACODEC_CAPTURE_OUTPUT(xname, xid) { \
+ .name = xname, \
+ .id = (xid), \
+ .capture = TOACODEC_STREAM(xname, "Capture", 2), \
+ .ops = &g12a_toacodec_capture_output_ops, \
+}
+
static struct snd_soc_dai_driver g12a_toacodec_dai_drv[] = {
TOACODEC_INPUT("IN A", TOACODEC_IN_A),
TOACODEC_INPUT("IN B", TOACODEC_IN_B),
@@ -215,6 +308,16 @@ static struct snd_soc_dai_driver g12a_toacodec_dai_drv[] = {
TOACODEC_OUTPUT("OUT", TOACODEC_OUT),
};

+static struct snd_soc_dai_driver a1_toacodec_dai_drv[] = {
+ TOACODEC_INPUT("IN A", TOACODEC_IN_A),
+ TOACODEC_INPUT("IN B", TOACODEC_IN_B),
+ TOACODEC_OUTPUT("OUT", TOACODEC_OUT),
+
+ TOACODEC_CAPTURE_OUTPUT("OUT A", TOACODEC_CAPTURE_OUT_A),
+ TOACODEC_CAPTURE_OUTPUT("OUT B", TOACODEC_CAPTURE_OUT_B),
+ TOACODEC_CAPTURE_INPUT("IN", TOACODEC_CAPTURE_IN),
+};
+
static int g12a_toacodec_component_probe(struct snd_soc_component *c)
{
/* Initialize the static clock parameters */
@@ -229,6 +332,32 @@ static int sm1_toacodec_component_probe(struct snd_soc_component *c)
CTRL0_BLK_CAP_INV_SM1);
}

+static int a1_toacodec_component_probe(struct snd_soc_component *c)
+{
+ /* Initialize the static clock parameters */
+ return snd_soc_component_write(c, TOACODEC_CTRL0,
+ CTRL0_BLK_CAP_INV_A1);
+}
+
+static int a1_toacodec_of_xlate_dai_name(struct snd_soc_component *component,
+ const struct of_phandle_args *args,
+ const char **dai_name)
+{
+ struct snd_soc_dai *dai;
+
+ if (args->args_count != 1)
+ return -EINVAL;
+
+ for_each_component_dais(component, dai) {
+ if (dai->id == args->args[0]) {
+ *dai_name = dai->driver->name;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
static const struct snd_soc_dapm_route g12a_toacodec_routes[] = {
{ "SRC", "I2S A", "IN A Playback" },
{ "SRC", "I2S B", "IN B Playback" },
@@ -237,6 +366,19 @@ static const struct snd_soc_dapm_route g12a_toacodec_routes[] = {
{ "OUT Capture", NULL, "OUT EN" },
};

+static const struct snd_soc_dapm_route a1_toacodec_routes[] = {
+ { "Playback SRC", "I2S A", "IN A Playback" },
+ { "Playback SRC", "I2S B", "IN B Playback" },
+ { "EN", "Switch", "Playback SRC" },
+ { "OUT Capture", NULL, "Playback SRC" },
+
+ { "EN", "Switch", "IN Playback" },
+ { "Capture SRC A", "I2S A", "EN" },
+ { "Capture SRC B", "I2S B", "EN" },
+ { "OUT A Capture", NULL, "Capture SRC A" },
+ { "OUT B Capture", NULL, "Capture SRC B" },
+};
+
static const struct snd_kcontrol_new g12a_toacodec_controls[] = {
SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL_G12A_LSB,
CTRL0_LANE_SEL_G12A_MSB - CTRL0_LANE_SEL_G12A_LSB + 1, 0),
@@ -247,6 +389,11 @@ static const struct snd_kcontrol_new sm1_toacodec_controls[] = {
CTRL0_LANE_SEL_SM1_MSB - CTRL0_LANE_SEL_SM1_LSB + 1, 0),
};

+static const struct snd_kcontrol_new a1_toacodec_controls[] = {
+ SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL_A1_LSB,
+ CTRL0_LANE_SEL_A1_MSB - CTRL0_LANE_SEL_A1_LSB + 1, 0),
+};
+
static const struct snd_soc_component_driver g12a_toacodec_component_drv = {
.probe = g12a_toacodec_component_probe,
.controls = g12a_toacodec_controls,
@@ -269,14 +416,44 @@ static const struct snd_soc_component_driver sm1_toacodec_component_drv = {
.endianness = 1,
};

+static const struct snd_soc_component_driver a1_toacodec_component_drv = {
+ .probe = a1_toacodec_component_probe,
+ .of_xlate_dai_name = a1_toacodec_of_xlate_dai_name,
+ .controls = a1_toacodec_controls,
+ .num_controls = ARRAY_SIZE(a1_toacodec_controls),
+ .dapm_widgets = a1_toacodec_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(a1_toacodec_widgets),
+ .dapm_routes = a1_toacodec_routes,
+ .num_dapm_routes = ARRAY_SIZE(a1_toacodec_routes),
+ .endianness = 1,
+};
+
+static const struct reg_default g12a_toacodec_reg_default[] = {
+ { TOACODEC_CTRL0, 0x0 },
+};
+
+static bool g12_toacodec_readable_reg(struct device *dev, unsigned int reg)
+{
+ /* There are no readable registers */
+ return false;
+}
+
static const struct regmap_config g12a_toacodec_regmap_cfg = {
- .reg_bits = 32,
- .val_bits = 32,
- .reg_stride = 4,
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = TOACODEC_CTRL0,
+ .max_register_is_0 = true,
+ .cache_type = REGCACHE_FLAT,
+ .reg_defaults = g12a_toacodec_reg_default,
+ .num_reg_defaults = ARRAY_SIZE(g12a_toacodec_reg_default),
+ .readable_reg = g12_toacodec_readable_reg,
};

static const struct g12a_toacodec_match_data g12a_toacodec_match_data = {
.component_drv = &g12a_toacodec_component_drv,
+ .dai_drv = g12a_toacodec_dai_drv,
+ .num_dai_drv = ARRAY_SIZE(g12a_toacodec_dai_drv),
.field_dat_sel = REG_FIELD(TOACODEC_CTRL0, CTRL0_DAT_SEL_G12A_LSB,
CTRL0_DAT_SEL_G12A_MSB),
.field_lrclk_sel = REG_FIELD(TOACODEC_CTRL0, CTRL0_LRCLK_SEL_G12A_LSB,
@@ -287,6 +464,8 @@ static const struct g12a_toacodec_match_data g12a_toacodec_match_data = {

static const struct g12a_toacodec_match_data sm1_toacodec_match_data = {
.component_drv = &sm1_toacodec_component_drv,
+ .dai_drv = g12a_toacodec_dai_drv,
+ .num_dai_drv = ARRAY_SIZE(g12a_toacodec_dai_drv),
.field_dat_sel = REG_FIELD(TOACODEC_CTRL0, CTRL0_DAT_SEL_SM1_LSB,
CTRL0_DAT_SEL_SM1_MSB),
.field_lrclk_sel = REG_FIELD(TOACODEC_CTRL0, CTRL0_LRCLK_SEL_SM1_LSB,
@@ -295,6 +474,18 @@ static const struct g12a_toacodec_match_data sm1_toacodec_match_data = {
CTRL0_BCLK_SEL_SM1_MSB),
};

+static const struct g12a_toacodec_match_data a1_toacodec_match_data = {
+ .component_drv = &a1_toacodec_component_drv,
+ .dai_drv = a1_toacodec_dai_drv,
+ .num_dai_drv = ARRAY_SIZE(a1_toacodec_dai_drv),
+ .field_dat_sel = REG_FIELD(TOACODEC_CTRL0, CTRL0_DAT_SEL_A1_LSB,
+ CTRL0_DAT_SEL_A1_MSB),
+ .field_lrclk_sel = REG_FIELD(TOACODEC_CTRL0, CTRL0_LRCLK_SEL_A1_LSB,
+ CTRL0_LRCLK_SEL_A1_MSB),
+ .field_bclk_sel = REG_FIELD(TOACODEC_CTRL0, CTRL0_BCLK_SEL_A1_LSB,
+ CTRL0_BCLK_SEL_A1_MSB),
+};
+
static const struct of_device_id g12a_toacodec_of_match[] = {
{
.compatible = "amlogic,g12a-toacodec",
@@ -304,6 +495,10 @@ static const struct of_device_id g12a_toacodec_of_match[] = {
.compatible = "amlogic,sm1-toacodec",
.data = &sm1_toacodec_match_data,
},
+ {
+ .compatible = "amlogic,a1-toacodec",
+ .data = &a1_toacodec_match_data,
+ },
{}
};
MODULE_DEVICE_TABLE(of, g12a_toacodec_of_match);
@@ -356,9 +551,9 @@ static int g12a_toacodec_probe(struct platform_device *pdev)
if (IS_ERR(priv->field_bclk_sel))
return PTR_ERR(priv->field_bclk_sel);

- return devm_snd_soc_register_component(dev,
- data->component_drv, g12a_toacodec_dai_drv,
- ARRAY_SIZE(g12a_toacodec_dai_drv));
+ return devm_snd_soc_register_component(dev, data->component_drv,
+ data->dai_drv,
+ data->num_dai_drv);
}

static struct platform_driver g12a_toacodec_pdrv = {
--
2.34.1


2024-03-14 23:37:13

by Jan Dakinevich

[permalink] [raw]
Subject: [PATCH 21/25] ASoC: dt-bindings: meson: axg-tdm-iface: claim support of A1 SoC family

Add "amlogic,a1-tdm-iface" compatible string alias to
"amlogic,axg-tdm-iface".

Signed-off-by: Jan Dakinevich <[email protected]>
---
.../devicetree/bindings/sound/amlogic,axg-tdm-iface.yaml | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.yaml
index 45955d8a26d1..7c1af85b52b4 100644
--- a/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.yaml
+++ b/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.yaml
@@ -14,7 +14,11 @@ allOf:

properties:
compatible:
- const: amlogic,axg-tdm-iface
+ oneOf:
+ - const: amlogic,axg-tdm-iface
+ - items:
+ - const: amlogic,a1-tdm-iface
+ - const: amlogic,axg-tdm-iface

"#sound-dai-cells":
const: 0
--
2.34.1


2024-03-15 09:54:01

by Jerome Brunet

[permalink] [raw]
Subject: Re: [PATCH 04/25] clk: meson: a1: add the audio clock controller driver


On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <[email protected]> wrote:

> This controller provides clocks and reset functionality for audio
> peripherals on Amlogic A1 SoC family.
>
> The driver is almost identical to 'axg-audio', however it would be better
> to keep it separate due to following reasons:
>
> - significant amount of bits has another definition. I will bring there
> a mess of new defines with A1_ suffixes.
>
> - registers of this controller are located in two separate regions. It
> will give a lot of complications for 'axg-audio' to support this.
>
> Signed-off-by: Jan Dakinevich <[email protected]>
> ---
> drivers/clk/meson/Kconfig | 13 +
> drivers/clk/meson/Makefile | 1 +
> drivers/clk/meson/a1-audio.c | 556 +++++++++++++++++++++++++++++++++++
> drivers/clk/meson/a1-audio.h | 58 ++++
> 4 files changed, 628 insertions(+)
> create mode 100644 drivers/clk/meson/a1-audio.c
> create mode 100644 drivers/clk/meson/a1-audio.h
>
> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
> index d6a2fa5f7e88..80c4a18c83d2 100644
> --- a/drivers/clk/meson/Kconfig
> +++ b/drivers/clk/meson/Kconfig
> @@ -133,6 +133,19 @@ config COMMON_CLK_A1_PERIPHERALS
> device, A1 SoC Family. Say Y if you want A1 Peripherals clock
> controller to work.
>
> +config COMMON_CLK_A1_AUDIO
> + tristate "Amlogic A1 SoC Audio clock controller support"
> + depends on ARM64
> + select COMMON_CLK_MESON_REGMAP
> + select COMMON_CLK_MESON_CLKC_UTILS
> + select COMMON_CLK_MESON_PHASE
> + select COMMON_CLK_MESON_SCLK_DIV
> + select COMMON_CLK_MESON_AUDIO_RSTC
> + help
> + Support for the Audio clock controller on Amlogic A113L based
> + device, A1 SoC Family. Say Y if you want A1 Audio clock controller
> + to work.
> +
> config COMMON_CLK_G12A
> tristate "G12 and SM1 SoC clock controllers support"
> depends on ARM64
> diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
> index 88d94921a4dc..4968fc7ad555 100644
> --- a/drivers/clk/meson/Makefile
> +++ b/drivers/clk/meson/Makefile
> @@ -20,6 +20,7 @@ obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
> obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
> obj-$(CONFIG_COMMON_CLK_A1_PLL) += a1-pll.o
> obj-$(CONFIG_COMMON_CLK_A1_PERIPHERALS) += a1-peripherals.o
> +obj-$(CONFIG_COMMON_CLK_A1_AUDIO) += a1-audio.o
> obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
> obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o
> obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o
> diff --git a/drivers/clk/meson/a1-audio.c b/drivers/clk/meson/a1-audio.c
> new file mode 100644
> index 000000000000..6039116c93ba
> --- /dev/null
> +++ b/drivers/clk/meson/a1-audio.c
> @@ -0,0 +1,556 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
> +/*
> + * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
> + *
> + * Author: Jan Dakinevich <[email protected]>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/init.h>
> +#include <linux/of_device.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <linux/reset-controller.h>
> +#include <linux/slab.h>
> +
> +#include "meson-clkc-utils.h"
> +#include "meson-audio-rstc.h"
> +#include "clk-regmap.h"
> +#include "clk-phase.h"
> +#include "sclk-div.h"
> +#include "a1-audio.h"
> +
> +#define AUDIO_PDATA(_name) \
> + ((const struct clk_parent_data[]) { { .hw = &(_name).hw } })

Not a fan - yet another level of macro.

> +
> +#define AUDIO_MUX(_name, _reg, _mask, _shift, _pdata) \
> +static struct clk_regmap _name = { \
> + .map = AUDIO_REG_MAP(_reg), \
> + .data = &(struct clk_regmap_mux_data){ \
> + .offset = AUDIO_REG_OFFSET(_reg), \
> + .mask = (_mask), \
> + .shift = (_shift), \
> + }, \
> + .hw.init = &(struct clk_init_data) { \
> + .name = #_name, \
> + .ops = &clk_regmap_mux_ops, \
> + .parent_data = (_pdata), \
> + .num_parents = ARRAY_SIZE(_pdata), \
> + .flags = CLK_SET_RATE_PARENT, \
> + }, \
> +}
> +
> +#define AUDIO_DIV(_name, _reg, _shift, _width, _pdata) \
> +static struct clk_regmap _name = { \
> + .map = AUDIO_REG_MAP(_reg), \
> + .data = &(struct clk_regmap_div_data){ \
> + .offset = AUDIO_REG_OFFSET(_reg), \
> + .shift = (_shift), \
> + .width = (_width), \
> + }, \
> + .hw.init = &(struct clk_init_data) { \
> + .name = #_name, \
> + .ops = &clk_regmap_divider_ops, \
> + .parent_data = (_pdata), \
> + .num_parents = 1, \
> + .flags = CLK_SET_RATE_PARENT, \
> + }, \
> +}
> +
> +#define AUDIO_GATE(_name, _reg, _bit, _pdata) \
> +static struct clk_regmap _name = { \
> + .map = AUDIO_REG_MAP(_reg), \
> + .data = &(struct clk_regmap_gate_data){ \
> + .offset = AUDIO_REG_OFFSET(_reg), \
> + .bit_idx = (_bit), \
> + }, \
> + .hw.init = &(struct clk_init_data) { \
> + .name = #_name, \
> + .ops = &clk_regmap_gate_ops, \
> + .parent_data = (_pdata), \
> + .num_parents = 1, \
> + .flags = CLK_SET_RATE_PARENT, \
> + }, \
> +}
> +
> +#define AUDIO_SCLK_DIV(_name, _reg, _div_shift, _div_width, \
> + _hi_shift, _hi_width, _pdata, _set_rate_parent) \
> +static struct clk_regmap _name = { \
> + .map = AUDIO_REG_MAP(_reg), \
> + .data = &(struct meson_sclk_div_data) { \
> + .div = { \
> + .reg_off = AUDIO_REG_OFFSET(_reg), \
> + .shift = (_div_shift), \
> + .width = (_div_width), \
> + }, \
> + .hi = { \
> + .reg_off = AUDIO_REG_OFFSET(_reg), \
> + .shift = (_hi_shift), \
> + .width = (_hi_width), \
> + }, \
> + }, \
> + .hw.init = &(struct clk_init_data) { \
> + .name = #_name, \
> + .ops = &meson_sclk_div_ops, \
> + .parent_data = (_pdata), \
> + .num_parents = 1, \
> + .flags = (_set_rate_parent) ? CLK_SET_RATE_PARENT : 0, \

Does not help readeability. Just pass the flag as axg-audio does.

> + }, \
> +}
> +
> +#define AUDIO_TRIPHASE(_name, _reg, _width, _shift0, _shift1, _shift2, \
> + _pdata) \
> +static struct clk_regmap _name = { \
> + .map = AUDIO_REG_MAP(_reg), \
> + .data = &(struct meson_clk_triphase_data) { \
> + .ph0 = { \
> + .reg_off = AUDIO_REG_OFFSET(_reg), \
> + .shift = (_shift0), \
> + .width = (_width), \
> + }, \
> + .ph1 = { \
> + .reg_off = AUDIO_REG_OFFSET(_reg), \
> + .shift = (_shift1), \
> + .width = (_width), \
> + }, \
> + .ph2 = { \
> + .reg_off = AUDIO_REG_OFFSET(_reg), \
> + .shift = (_shift2), \
> + .width = (_width), \
> + }, \
> + }, \
> + .hw.init = &(struct clk_init_data) { \
> + .name = #_name, \
> + .ops = &meson_clk_triphase_ops, \
> + .parent_data = (_pdata), \
> + .num_parents = 1, \
> + .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \
> + }, \
> +}
> +
> +#define AUDIO_SCLK_WS(_name, _reg, _width, _shift_ph, _shift_ws, \
> + _pdata) \
> +static struct clk_regmap _name = { \
> + .map = AUDIO_REG_MAP(_reg), \
> + .data = &(struct meson_sclk_ws_inv_data) { \
> + .ph = { \
> + .reg_off = AUDIO_REG_OFFSET(_reg), \
> + .shift = (_shift_ph), \
> + .width = (_width), \
> + }, \
> + .ws = { \
> + .reg_off = AUDIO_REG_OFFSET(_reg), \
> + .shift = (_shift_ws), \
> + .width = (_width), \
> + }, \
> + }, \
> + .hw.init = &(struct clk_init_data) { \
> + .name = #_name, \
> + .ops = &meson_sclk_ws_inv_ops, \
> + .parent_data = (_pdata), \
> + .num_parents = 1, \
> + .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \
> + }, \
> +}

All the above does essentially the same things as the macro of
axg-audio, to some minor differences. Yet it is another set to maintain.

I'd much prefer if you put the axg-audio macro in a header a re-used
those. There would a single set to maintain. You may then specialize the
included in the driver C file, to avoid redundant parameters

Rework axg-audio to use clk_parent_data if you must, but not in the same
series please.

> +
> +static const struct clk_parent_data a1_pclk_pdata[] = {
> + { .fw_name = "pclk", },
> +};
> +
> +AUDIO_GATE(audio_ddr_arb, AUDIO_CLK_GATE_EN0, 0, a1_pclk_pdata);
> +AUDIO_GATE(audio_tdmin_a, AUDIO_CLK_GATE_EN0, 1, a1_pclk_pdata);
> +AUDIO_GATE(audio_tdmin_b, AUDIO_CLK_GATE_EN0, 2, a1_pclk_pdata);
> +AUDIO_GATE(audio_tdmin_lb, AUDIO_CLK_GATE_EN0, 3, a1_pclk_pdata);
> +AUDIO_GATE(audio_loopback, AUDIO_CLK_GATE_EN0, 4, a1_pclk_pdata);
> +AUDIO_GATE(audio_tdmout_a, AUDIO_CLK_GATE_EN0, 5, a1_pclk_pdata);
> +AUDIO_GATE(audio_tdmout_b, AUDIO_CLK_GATE_EN0, 6, a1_pclk_pdata);
> +AUDIO_GATE(audio_frddr_a, AUDIO_CLK_GATE_EN0, 7, a1_pclk_pdata);
> +AUDIO_GATE(audio_frddr_b, AUDIO_CLK_GATE_EN0, 8, a1_pclk_pdata);
> +AUDIO_GATE(audio_toddr_a, AUDIO_CLK_GATE_EN0, 9, a1_pclk_pdata);
> +AUDIO_GATE(audio_toddr_b, AUDIO_CLK_GATE_EN0, 10, a1_pclk_pdata);
> +AUDIO_GATE(audio_spdifin, AUDIO_CLK_GATE_EN0, 11, a1_pclk_pdata);
> +AUDIO_GATE(audio_resample, AUDIO_CLK_GATE_EN0, 12, a1_pclk_pdata);
> +AUDIO_GATE(audio_eqdrc, AUDIO_CLK_GATE_EN0, 13, a1_pclk_pdata);
> +AUDIO_GATE(audio_audiolocker, AUDIO_CLK_GATE_EN0, 14, a1_pclk_pdata);
This is what I mean by redundant parameter ^

> +
> +AUDIO_GATE(audio2_ddr_arb, AUDIO2_CLK_GATE_EN0, 0, a1_pclk_pdata);
> +AUDIO_GATE(audio2_pdm, AUDIO2_CLK_GATE_EN0, 1, a1_pclk_pdata);
> +AUDIO_GATE(audio2_tdmin_vad, AUDIO2_CLK_GATE_EN0, 2, a1_pclk_pdata);
> +AUDIO_GATE(audio2_toddr_vad, AUDIO2_CLK_GATE_EN0, 3, a1_pclk_pdata);
> +AUDIO_GATE(audio2_vad, AUDIO2_CLK_GATE_EN0, 4, a1_pclk_pdata);
> +AUDIO_GATE(audio2_audiotop, AUDIO2_CLK_GATE_EN0, 7, a1_pclk_pdata);
> +
> +static const struct clk_parent_data a1_mst_pdata[] = {
> + { .fw_name = "dds_in" },
> + { .fw_name = "fclk_div2" },
> + { .fw_name = "fclk_div3" },
> + { .fw_name = "hifi_pll" },
> + { .fw_name = "xtal" },
> +};
> +
> +#define AUDIO_MST_MCLK(_name, _reg) \
> + AUDIO_MUX(_name##_mux, (_reg), 0x7, 24, a1_mst_pdata); \
> + AUDIO_DIV(_name##_div, (_reg), 0, 16, \
> + AUDIO_PDATA(_name##_mux)); \
> + AUDIO_GATE(_name, (_reg), 31, AUDIO_PDATA(_name##_div))
> +
> +AUDIO_MST_MCLK(audio_mst_a_mclk, AUDIO_MCLK_A_CTRL);
> +AUDIO_MST_MCLK(audio_mst_b_mclk, AUDIO_MCLK_B_CTRL);
> +AUDIO_MST_MCLK(audio_mst_c_mclk, AUDIO_MCLK_C_CTRL);
> +AUDIO_MST_MCLK(audio_mst_d_mclk, AUDIO_MCLK_D_CTRL);
> +AUDIO_MST_MCLK(audio_spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL);
> +AUDIO_MST_MCLK(audio_eqdrc_clk, AUDIO_CLK_EQDRC_CTRL);
> +
> +AUDIO_MUX(audio_resample_clk_mux, AUDIO_CLK_RESAMPLE_CTRL, 0xf, 24,
> + a1_mst_pdata);
> +AUDIO_DIV(audio_resample_clk_div, AUDIO_CLK_RESAMPLE_CTRL, 0, 8,
> + AUDIO_PDATA(audio_resample_clk_mux));
> +AUDIO_GATE(audio_resample_clk, AUDIO_CLK_RESAMPLE_CTRL, 31,
> + AUDIO_PDATA(audio_resample_clk_div));
> +
> +AUDIO_MUX(audio_locker_in_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 8,
> + a1_mst_pdata);
> +AUDIO_DIV(audio_locker_in_clk_div, AUDIO_CLK_LOCKER_CTRL, 0, 8,
> + AUDIO_PDATA(audio_locker_in_clk_mux));
> +AUDIO_GATE(audio_locker_in_clk, AUDIO_CLK_LOCKER_CTRL, 15,
> + AUDIO_PDATA(audio_locker_in_clk_div));
> +
> +AUDIO_MUX(audio_locker_out_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 24,
> + a1_mst_pdata);
> +AUDIO_DIV(audio_locker_out_clk_div, AUDIO_CLK_LOCKER_CTRL, 16, 8,
> + AUDIO_PDATA(audio_locker_out_clk_mux));
> +AUDIO_GATE(audio_locker_out_clk, AUDIO_CLK_LOCKER_CTRL, 31,
> + AUDIO_PDATA(audio_locker_out_clk_div));
> +
> +AUDIO_MST_MCLK(audio2_vad_mclk, AUDIO2_MCLK_VAD_CTRL);
> +AUDIO_MST_MCLK(audio2_vad_clk, AUDIO2_CLK_VAD_CTRL);
> +AUDIO_MST_MCLK(audio2_pdm_dclk, AUDIO2_CLK_PDMIN_CTRL0);
> +AUDIO_MST_MCLK(audio2_pdm_sysclk, AUDIO2_CLK_PDMIN_CTRL1);
> +
> +#define AUDIO_MST_SCLK(_name, _reg0, _reg1, _pdata) \
> + AUDIO_GATE(_name##_pre_en, (_reg0), 31, (_pdata)); \
> + AUDIO_SCLK_DIV(_name##_div, (_reg0), 20, 10, 0, 0, \
> + AUDIO_PDATA(_name##_pre_en), true); \
> + AUDIO_GATE(_name##_post_en, (_reg0), 30, \
> + AUDIO_PDATA(_name##_div)); \
> + AUDIO_TRIPHASE(_name, (_reg1), 1, 0, 2, 4, \
> + AUDIO_PDATA(_name##_post_en))
> +

Again, I'm not a fan of this many levels of macro. I can live with it
but certainly don't want the burden of reviewing and maintaining for
clock driver. AXG / G12 and A1 are obviously closely related, so make it common.

> +#define AUDIO_MST_LRCLK(_name, _reg0, _reg1, _pdata) \
> + AUDIO_SCLK_DIV(_name##_div, (_reg0), 0, 10, 10, 10, \
> + (_pdata), false); \
> + AUDIO_TRIPHASE(_name, (_reg1), 1, 1, 3, 5, \
> + AUDIO_PDATA(_name##_div))
> +
> +AUDIO_MST_SCLK(audio_mst_a_sclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1,
> + AUDIO_PDATA(audio_mst_a_mclk));
> +AUDIO_MST_SCLK(audio_mst_b_sclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1,
> + AUDIO_PDATA(audio_mst_b_mclk));
> +AUDIO_MST_SCLK(audio_mst_c_sclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1,
> + AUDIO_PDATA(audio_mst_c_mclk));
> +AUDIO_MST_SCLK(audio_mst_d_sclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1,
> + AUDIO_PDATA(audio_mst_d_mclk));
> +
> +AUDIO_MST_LRCLK(audio_mst_a_lrclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1,
> + AUDIO_PDATA(audio_mst_a_sclk_post_en));
> +AUDIO_MST_LRCLK(audio_mst_b_lrclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1,
> + AUDIO_PDATA(audio_mst_b_sclk_post_en));
> +AUDIO_MST_LRCLK(audio_mst_c_lrclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1,
> + AUDIO_PDATA(audio_mst_c_sclk_post_en));
> +AUDIO_MST_LRCLK(audio_mst_d_lrclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1,
> + AUDIO_PDATA(audio_mst_d_sclk_post_en));
> +
> +static const struct clk_parent_data a1_mst_sclk_pdata[] = {
> + { .hw = &audio_mst_a_sclk.hw },
> + { .hw = &audio_mst_b_sclk.hw },
> + { .hw = &audio_mst_c_sclk.hw },
> + { .hw = &audio_mst_d_sclk.hw },
> + { .fw_name = "slv_sclk0" },
> + { .fw_name = "slv_sclk1" },
> + { .fw_name = "slv_sclk2" },
> + { .fw_name = "slv_sclk3" },
> + { .fw_name = "slv_sclk4" },
> + { .fw_name = "slv_sclk5" },
> + { .fw_name = "slv_sclk6" },
> + { .fw_name = "slv_sclk7" },
> + { .fw_name = "slv_sclk8" },
> + { .fw_name = "slv_sclk9" },
> +};
> +
> +static const struct clk_parent_data a1_mst_lrclk_pdata[] = {
> + { .hw = &audio_mst_a_lrclk.hw },
> + { .hw = &audio_mst_b_lrclk.hw },
> + { .hw = &audio_mst_c_lrclk.hw },
> + { .hw = &audio_mst_d_lrclk.hw },
> + { .fw_name = "slv_lrclk0" },
> + { .fw_name = "slv_lrclk1" },
> + { .fw_name = "slv_lrclk2" },
> + { .fw_name = "slv_lrclk3" },
> + { .fw_name = "slv_lrclk4" },
> + { .fw_name = "slv_lrclk5" },
> + { .fw_name = "slv_lrclk6" },
> + { .fw_name = "slv_lrclk7" },
> + { .fw_name = "slv_lrclk8" },
> + { .fw_name = "slv_lrclk9" },
> +};
> +
> +#define AUDIO_TDM_SCLK(_name, _reg) \
> + AUDIO_MUX(_name##_mux, (_reg), 0xf, 24, a1_mst_sclk_pdata); \
> + AUDIO_GATE(_name##_pre_en, (_reg), 31, \
> + AUDIO_PDATA(_name##_mux)); \
> + AUDIO_GATE(_name##_post_en, (_reg), 30, \
> + AUDIO_PDATA(_name##_pre_en)); \
> + AUDIO_SCLK_WS(_name, (_reg), 1, 29, 28, \
> + AUDIO_PDATA(_name##_post_en))
> +
> +#define AUDIO_TDM_LRCLK(_name, _reg) \
> + AUDIO_MUX(_name, (_reg), 0xf, 20, a1_mst_lrclk_pdata)
> +
> +AUDIO_TDM_SCLK(audio_tdmin_a_sclk, AUDIO_CLK_TDMIN_A_CTRL);
> +AUDIO_TDM_SCLK(audio_tdmin_b_sclk, AUDIO_CLK_TDMIN_B_CTRL);
> +AUDIO_TDM_SCLK(audio_tdmin_lb_sclk, AUDIO_CLK_TDMIN_LB_CTRL);
> +AUDIO_TDM_SCLK(audio_tdmout_a_sclk, AUDIO_CLK_TDMOUT_A_CTRL);
> +AUDIO_TDM_SCLK(audio_tdmout_b_sclk, AUDIO_CLK_TDMOUT_B_CTRL);
> +
> +AUDIO_TDM_LRCLK(audio_tdmin_a_lrclk, AUDIO_CLK_TDMIN_A_CTRL);
> +AUDIO_TDM_LRCLK(audio_tdmin_b_lrclk, AUDIO_CLK_TDMIN_B_CTRL);
> +AUDIO_TDM_LRCLK(audio_tdmin_lb_lrclk, AUDIO_CLK_TDMIN_LB_CTRL);
> +AUDIO_TDM_LRCLK(audio_tdmout_a_lrclk, AUDIO_CLK_TDMOUT_A_CTRL);
> +AUDIO_TDM_LRCLK(audio_tdmout_b_lrclk, AUDIO_CLK_TDMOUT_B_CTRL);
> +
> +static struct clk_hw *a1_audio_hw_clks[] = {
> + [AUD_CLKID_DDR_ARB] = &audio_ddr_arb.hw,
> + [AUD_CLKID_TDMIN_A] = &audio_tdmin_a.hw,
> + [AUD_CLKID_TDMIN_B] = &audio_tdmin_b.hw,
> + [AUD_CLKID_TDMIN_LB] = &audio_tdmin_lb.hw,
> + [AUD_CLKID_LOOPBACK] = &audio_loopback.hw,
> + [AUD_CLKID_TDMOUT_A] = &audio_tdmout_a.hw,
> + [AUD_CLKID_TDMOUT_B] = &audio_tdmout_b.hw,
> + [AUD_CLKID_FRDDR_A] = &audio_frddr_a.hw,
> + [AUD_CLKID_FRDDR_B] = &audio_frddr_b.hw,
> + [AUD_CLKID_TODDR_A] = &audio_toddr_a.hw,
> + [AUD_CLKID_TODDR_B] = &audio_toddr_b.hw,
> + [AUD_CLKID_SPDIFIN] = &audio_spdifin.hw,
> + [AUD_CLKID_RESAMPLE] = &audio_resample.hw,
> + [AUD_CLKID_EQDRC] = &audio_eqdrc.hw,
> + [AUD_CLKID_LOCKER] = &audio_audiolocker.hw,
> + [AUD_CLKID_MST_A_MCLK_SEL] = &audio_mst_a_mclk_mux.hw,
> + [AUD_CLKID_MST_A_MCLK_DIV] = &audio_mst_a_mclk_div.hw,
> + [AUD_CLKID_MST_A_MCLK] = &audio_mst_a_mclk.hw,
> + [AUD_CLKID_MST_B_MCLK_SEL] = &audio_mst_b_mclk_mux.hw,
> + [AUD_CLKID_MST_B_MCLK_DIV] = &audio_mst_b_mclk_div.hw,
> + [AUD_CLKID_MST_B_MCLK] = &audio_mst_b_mclk.hw,
> + [AUD_CLKID_MST_C_MCLK_SEL] = &audio_mst_c_mclk_mux.hw,
> + [AUD_CLKID_MST_C_MCLK_DIV] = &audio_mst_c_mclk_div.hw,
> + [AUD_CLKID_MST_C_MCLK] = &audio_mst_c_mclk.hw,
> + [AUD_CLKID_MST_D_MCLK_SEL] = &audio_mst_d_mclk_mux.hw,
> + [AUD_CLKID_MST_D_MCLK_DIV] = &audio_mst_d_mclk_div.hw,
> + [AUD_CLKID_MST_D_MCLK] = &audio_mst_d_mclk.hw,
> + [AUD_CLKID_RESAMPLE_CLK_SEL] = &audio_resample_clk_mux.hw,
> + [AUD_CLKID_RESAMPLE_CLK_DIV] = &audio_resample_clk_div.hw,
> + [AUD_CLKID_RESAMPLE_CLK] = &audio_resample_clk.hw,
> + [AUD_CLKID_LOCKER_IN_CLK_SEL] = &audio_locker_in_clk_mux.hw,
> + [AUD_CLKID_LOCKER_IN_CLK_DIV] = &audio_locker_in_clk_div.hw,
> + [AUD_CLKID_LOCKER_IN_CLK] = &audio_locker_in_clk.hw,
> + [AUD_CLKID_LOCKER_OUT_CLK_SEL] = &audio_locker_out_clk_mux.hw,
> + [AUD_CLKID_LOCKER_OUT_CLK_DIV] = &audio_locker_out_clk_div.hw,
> + [AUD_CLKID_LOCKER_OUT_CLK] = &audio_locker_out_clk.hw,
> + [AUD_CLKID_SPDIFIN_CLK_SEL] = &audio_spdifin_clk_mux.hw,
> + [AUD_CLKID_SPDIFIN_CLK_DIV] = &audio_spdifin_clk_div.hw,
> + [AUD_CLKID_SPDIFIN_CLK] = &audio_spdifin_clk.hw,
> + [AUD_CLKID_EQDRC_CLK_SEL] = &audio_eqdrc_clk_mux.hw,
> + [AUD_CLKID_EQDRC_CLK_DIV] = &audio_eqdrc_clk_div.hw,
> + [AUD_CLKID_EQDRC_CLK] = &audio_eqdrc_clk.hw,
> + [AUD_CLKID_MST_A_SCLK_PRE_EN] = &audio_mst_a_sclk_pre_en.hw,
> + [AUD_CLKID_MST_A_SCLK_DIV] = &audio_mst_a_sclk_div.hw,
> + [AUD_CLKID_MST_A_SCLK_POST_EN] = &audio_mst_a_sclk_post_en.hw,
> + [AUD_CLKID_MST_A_SCLK] = &audio_mst_a_sclk.hw,
> + [AUD_CLKID_MST_B_SCLK_PRE_EN] = &audio_mst_b_sclk_pre_en.hw,
> + [AUD_CLKID_MST_B_SCLK_DIV] = &audio_mst_b_sclk_div.hw,
> + [AUD_CLKID_MST_B_SCLK_POST_EN] = &audio_mst_b_sclk_post_en.hw,
> + [AUD_CLKID_MST_B_SCLK] = &audio_mst_b_sclk.hw,
> + [AUD_CLKID_MST_C_SCLK_PRE_EN] = &audio_mst_c_sclk_pre_en.hw,
> + [AUD_CLKID_MST_C_SCLK_DIV] = &audio_mst_c_sclk_div.hw,
> + [AUD_CLKID_MST_C_SCLK_POST_EN] = &audio_mst_c_sclk_post_en.hw,
> + [AUD_CLKID_MST_C_SCLK] = &audio_mst_c_sclk.hw,
> + [AUD_CLKID_MST_D_SCLK_PRE_EN] = &audio_mst_d_sclk_pre_en.hw,
> + [AUD_CLKID_MST_D_SCLK_DIV] = &audio_mst_d_sclk_div.hw,
> + [AUD_CLKID_MST_D_SCLK_POST_EN] = &audio_mst_d_sclk_post_en.hw,
> + [AUD_CLKID_MST_D_SCLK] = &audio_mst_d_sclk.hw,
> + [AUD_CLKID_MST_A_LRCLK_DIV] = &audio_mst_a_lrclk_div.hw,
> + [AUD_CLKID_MST_A_LRCLK] = &audio_mst_a_lrclk.hw,
> + [AUD_CLKID_MST_B_LRCLK_DIV] = &audio_mst_b_lrclk_div.hw,
> + [AUD_CLKID_MST_B_LRCLK] = &audio_mst_b_lrclk.hw,
> + [AUD_CLKID_MST_C_LRCLK_DIV] = &audio_mst_c_lrclk_div.hw,
> + [AUD_CLKID_MST_C_LRCLK] = &audio_mst_c_lrclk.hw,
> + [AUD_CLKID_MST_D_LRCLK_DIV] = &audio_mst_d_lrclk_div.hw,
> + [AUD_CLKID_MST_D_LRCLK] = &audio_mst_d_lrclk.hw,
> + [AUD_CLKID_TDMIN_A_SCLK_SEL] = &audio_tdmin_a_sclk_mux.hw,
> + [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &audio_tdmin_a_sclk_pre_en.hw,
> + [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &audio_tdmin_a_sclk_post_en.hw,
> + [AUD_CLKID_TDMIN_A_SCLK] = &audio_tdmin_a_sclk.hw,
> + [AUD_CLKID_TDMIN_A_LRCLK] = &audio_tdmin_a_lrclk.hw,
> + [AUD_CLKID_TDMIN_B_SCLK_SEL] = &audio_tdmin_b_sclk_mux.hw,
> + [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &audio_tdmin_b_sclk_pre_en.hw,
> + [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &audio_tdmin_b_sclk_post_en.hw,
> + [AUD_CLKID_TDMIN_B_SCLK] = &audio_tdmin_b_sclk.hw,
> + [AUD_CLKID_TDMIN_B_LRCLK] = &audio_tdmin_b_lrclk.hw,
> + [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &audio_tdmin_lb_sclk_mux.hw,
> + [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &audio_tdmin_lb_sclk_pre_en.hw,
> + [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &audio_tdmin_lb_sclk_post_en.hw,
> + [AUD_CLKID_TDMIN_LB_SCLK] = &audio_tdmin_lb_sclk.hw,
> + [AUD_CLKID_TDMIN_LB_LRCLK] = &audio_tdmin_lb_lrclk.hw,
> + [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &audio_tdmout_a_sclk_mux.hw,
> + [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &audio_tdmout_a_sclk_pre_en.hw,
> + [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &audio_tdmout_a_sclk_post_en.hw,
> + [AUD_CLKID_TDMOUT_A_SCLK] = &audio_tdmout_a_sclk.hw,
> + [AUD_CLKID_TDMOUT_A_LRCLK] = &audio_tdmout_a_lrclk.hw,
> + [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &audio_tdmout_b_sclk_mux.hw,
> + [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &audio_tdmout_b_sclk_pre_en.hw,
> + [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &audio_tdmout_b_sclk_post_en.hw,
> + [AUD_CLKID_TDMOUT_B_SCLK] = &audio_tdmout_b_sclk.hw,
> + [AUD_CLKID_TDMOUT_B_LRCLK] = &audio_tdmout_b_lrclk.hw,
> +
> + [AUD2_CLKID_DDR_ARB] = &audio2_ddr_arb.hw,
> + [AUD2_CLKID_PDM] = &audio2_pdm.hw,
> + [AUD2_CLKID_TDMIN_VAD] = &audio2_tdmin_vad.hw,
> + [AUD2_CLKID_TODDR_VAD] = &audio2_toddr_vad.hw,
> + [AUD2_CLKID_VAD] = &audio2_vad.hw,
> + [AUD2_CLKID_AUDIOTOP] = &audio2_audiotop.hw,
> + [AUD2_CLKID_VAD_MCLK_SEL] = &audio2_vad_mclk_mux.hw,
> + [AUD2_CLKID_VAD_MCLK_DIV] = &audio2_vad_mclk_div.hw,
> + [AUD2_CLKID_VAD_MCLK] = &audio2_vad_mclk.hw,
> + [AUD2_CLKID_VAD_CLK_SEL] = &audio2_vad_clk_mux.hw,
> + [AUD2_CLKID_VAD_CLK_DIV] = &audio2_vad_clk_div.hw,
> + [AUD2_CLKID_VAD_CLK] = &audio2_vad_clk.hw,
> + [AUD2_CLKID_PDM_DCLK_SEL] = &audio2_pdm_dclk_mux.hw,
> + [AUD2_CLKID_PDM_DCLK_DIV] = &audio2_pdm_dclk_div.hw,
> + [AUD2_CLKID_PDM_DCLK] = &audio2_pdm_dclk.hw,
> + [AUD2_CLKID_PDM_SYSCLK_SEL] = &audio2_pdm_sysclk_mux.hw,
> + [AUD2_CLKID_PDM_SYSCLK_DIV] = &audio2_pdm_sysclk_div.hw,
> + [AUD2_CLKID_PDM_SYSCLK] = &audio2_pdm_sysclk.hw,
> +};
> +
> +static struct meson_clk_hw_data a1_audio_clks = {
> + .hws = a1_audio_hw_clks,
> + .num = ARRAY_SIZE(a1_audio_hw_clks),
> +};
> +
> +static struct regmap *a1_audio_map(struct platform_device *pdev,
> + unsigned int index)
> +{
> + char name[32];
> + const struct regmap_config cfg = {
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = 4,
> + .name = name,

Not necessary

> + };
> + void __iomem *base;
> +
> + base = devm_platform_ioremap_resource(pdev, index);
> + if (IS_ERR(base))
> + return base;
> +
> + scnprintf(name, sizeof(name), "%d", index);
> + return devm_regmap_init_mmio(&pdev->dev, base, &cfg);
> +}

That is overengineered. Please keep it simple. Declare the regmap_config
as static const global, and do it like axg-audio please.

> +
> +static int a1_register_clk(struct platform_device *pdev,
> + struct regmap *map0, struct regmap *map1,
> + struct clk_hw *hw)
> +{
> + struct clk_regmap *clk = container_of(hw, struct clk_regmap, hw);
> +
> + if (!hw)
> + return 0;
> +
> + switch ((unsigned long)clk->map) {
> + case AUDIO_RANGE_0:
> + clk->map = map0;
> + break;
> + case AUDIO_RANGE_1:
> + clk->map = map1;
> + break;

.. fishy

> + default:
> + WARN_ON(1);
> + return -EINVAL;
> + }
> +
> + return devm_clk_hw_register(&pdev->dev, hw);
> +}
> +
> +static int a1_audio_clkc_probe(struct platform_device *pdev)
> +{
> + struct regmap *map0, *map1;
> + struct clk *clk;
> + unsigned int i;
> + int ret;
> +
> + clk = devm_clk_get_enabled(&pdev->dev, "pclk");
> + if (WARN_ON(IS_ERR(clk)))
> + return PTR_ERR(clk);
> +
> + map0 = a1_audio_map(pdev, 0);
> + if (IS_ERR(map0))
> + return PTR_ERR(map0);
> +
> + map1 = a1_audio_map(pdev, 1);
> + if (IS_ERR(map1))
> + return PTR_ERR(map1);

No - Looks to me you just have two clock controllers you are trying
force into one.

> +
> + /*
> + * Register and enable AUD2_CLKID_AUDIOTOP clock first. Unless
> + * it is enabled any read/write to 'map0' hangs the CPU.
> + */
> +
> + ret = a1_register_clk(pdev, map0, map1,
> + a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]);
> + if (ret)
> + return ret;
> +
> + ret = clk_prepare_enable(a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]->clk);
> + if (ret)
> + return ret;

Again, this shows 2 devices. The one related to your 'map0' should
request AUD2_CLKID_AUDIOTOP as input and enable it right away.

> +
> + for (i = 0; i < a1_audio_clks.num; i++) {
> + if (i == AUD2_CLKID_AUDIOTOP)
> + continue;
> +
> + ret = a1_register_clk(pdev, map0, map1, a1_audio_clks.hws[i]);
> + if (ret)
> + return ret;
> + }
> +
> + ret = devm_of_clk_add_hw_provider(&pdev->dev, meson_clk_hw_get,
> + &a1_audio_clks);
> + if (ret)
> + return ret;
> +
> + BUILD_BUG_ON((unsigned long)AUDIO_REG_MAP(AUDIO_SW_RESET0) !=
> + AUDIO_RANGE_0);

Why is that necessary ?

> + return meson_audio_rstc_register(&pdev->dev, map0,
> + AUDIO_REG_OFFSET(AUDIO_SW_RESET0), 32);
> +}
> +
> +static const struct of_device_id a1_audio_clkc_match_table[] = {
> + { .compatible = "amlogic,a1-audio-clkc", },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, a1_audio_clkc_match_table);
> +
> +static struct platform_driver a1_audio_clkc_driver = {
> + .probe = a1_audio_clkc_probe,
> + .driver = {
> + .name = "a1-audio-clkc",
> + .of_match_table = a1_audio_clkc_match_table,
> + },
> +};
> +module_platform_driver(a1_audio_clkc_driver);
> +
> +MODULE_DESCRIPTION("Amlogic A1 Audio Clock driver");
> +MODULE_AUTHOR("Jan Dakinevich <[email protected]>");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/clk/meson/a1-audio.h b/drivers/clk/meson/a1-audio.h
> new file mode 100644
> index 000000000000..f994e87276cd
> --- /dev/null
> +++ b/drivers/clk/meson/a1-audio.h
> @@ -0,0 +1,58 @@
> +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
> +/*
> + * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
> + *
> + * Author: Jan Dakinevich <[email protected]>
> + */
> +
> +#ifndef __A1_AUDIO_H
> +#define __A1_AUDIO_H
> +
> +#define AUDIO_RANGE_0 0xa
> +#define AUDIO_RANGE_1 0xb
> +#define AUDIO_RANGE_SHIFT 16
> +
> +#define AUDIO_REG(_range, _offset) \
> + (((_range) << AUDIO_RANGE_SHIFT) + (_offset))
> +
> +#define AUDIO_REG_OFFSET(_reg) \
> + ((_reg) & ((1 << AUDIO_RANGE_SHIFT) - 1))
> +
> +#define AUDIO_REG_MAP(_reg) \
> + ((void *)((_reg) >> AUDIO_RANGE_SHIFT))

That is seriouly overengineered.
The following are offset. Just write what they are.

There is not reason to put that into a header. It is only going to be
used by a single driver.

> +
> +#define AUDIO_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_0, 0x000)
> +#define AUDIO_MCLK_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x008)
> +#define AUDIO_MCLK_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x00c)
> +#define AUDIO_MCLK_C_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x010)
> +#define AUDIO_MCLK_D_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x014)
> +#define AUDIO_MCLK_E_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x018)
> +#define AUDIO_MCLK_F_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x01c)
> +#define AUDIO_SW_RESET0 AUDIO_REG(AUDIO_RANGE_0, 0x028)
> +#define AUDIO_MST_A_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x040)
> +#define AUDIO_MST_A_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x044)
> +#define AUDIO_MST_B_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x048)
> +#define AUDIO_MST_B_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x04c)
> +#define AUDIO_MST_C_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x050)
> +#define AUDIO_MST_C_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x054)
> +#define AUDIO_MST_D_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x058)
> +#define AUDIO_MST_D_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x05c)
> +#define AUDIO_CLK_TDMIN_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x080)
> +#define AUDIO_CLK_TDMIN_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x084)
> +#define AUDIO_CLK_TDMIN_LB_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x08c)
> +#define AUDIO_CLK_TDMOUT_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x090)
> +#define AUDIO_CLK_TDMOUT_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x094)
> +#define AUDIO_CLK_SPDIFIN_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x09c)
> +#define AUDIO_CLK_RESAMPLE_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a4)
> +#define AUDIO_CLK_LOCKER_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a8)
> +#define AUDIO_CLK_EQDRC_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0c0)
> +
> +#define AUDIO2_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_1, 0x00c)
> +#define AUDIO2_MCLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x040)
> +#define AUDIO2_CLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x044)
> +#define AUDIO2_CLK_PDMIN_CTRL0 AUDIO_REG(AUDIO_RANGE_1, 0x058)
> +#define AUDIO2_CLK_PDMIN_CTRL1 AUDIO_REG(AUDIO_RANGE_1, 0x05c)
> +
> +#include <dt-bindings/clock/amlogic,a1-audio-clkc.h>
> +
> +#endif /* __A1_AUDIO_H */


--
Jerome

2024-03-15 09:58:19

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH 03/25] dt-bindings: clock: meson: add A1 audio clock and reset controller bindings

On 15/03/2024 00:21, Jan Dakinevich wrote:
> Signed-off-by: Jan Dakinevich <[email protected]>

You must provide commit messages.

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


> ---
> .../bindings/clock/amlogic,a1-audio-clkc.yaml | 83 ++++++++++++
> .../dt-bindings/clock/amlogic,a1-audio-clkc.h | 122 ++++++++++++++++++
> .../reset/amlogic,meson-a1-audio-reset.h | 29 +++++
> 3 files changed, 234 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/clock/amlogic,a1-audio-clkc.yaml
> create mode 100644 include/dt-bindings/clock/amlogic,a1-audio-clkc.h
> create mode 100644 include/dt-bindings/reset/amlogic,meson-a1-audio-reset.h
>
> diff --git a/Documentation/devicetree/bindings/clock/amlogic,a1-audio-clkc.yaml b/Documentation/devicetree/bindings/clock/amlogic,a1-audio-clkc.yaml
> new file mode 100644
> index 000000000000..c76cad4da493
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/amlogic,a1-audio-clkc.yaml
> @@ -0,0 +1,83 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/clock/amlogic,a1-audio-clkc.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Amlogic A1 Audio Clock Control Unit and Reset Controller
> +
> +maintainers:
> + - Neil Armstrong <[email protected]>
> + - Jerome Brunet <[email protected]>
> + - Jan Dakinevich <[email protected]>
> +
> +properties:
> + compatible:
> + const: amlogic,a1-audio-clkc
> +
> + '#clock-cells':
> + const: 1
> +
> + '#reset-cells':
> + const: 1
> +
> + reg:
> + minItems: 2

Drop

> + maxItems: 2
> +
> + clocks:
> + items:
> + - description: input main peripheral bus clock
> + - description: input dds_in
> + - description: input fixed pll div2
> + - description: input fixed pll div3
> + - description: input hifi_pll
> + - description: input oscillator (usually at 24MHz)
> +
> + clock-names:
> + items:
> + - const: pclk
> + - const: dds_in
> + - const: fclk_div2
> + - const: fclk_div3
> + - const: hifi_pll
> + - const: xtal
> +
> +required:
> + - compatible
> + - '#clock-cells'
> + - '#reset-cells'
> + - reg
> + - clocks
> + - clock-names
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/clock/amlogic,a1-pll-clkc.h>
> + #include <dt-bindings/clock/amlogic,a1-peripherals-clkc.h>
> + audio {
> + #address-cells = <2>;
> + #size-cells = <2>;
> +
> + clkc_audio: audio-clock-controller@0 {
> + compatible = "amlogic,a1-audio-clkc";
> + reg = <0x0 0xfe050000 0x0 0xb0>,

Messed indentayion. Use 4 spaces for example indentation.

> + <0x0 0xfe054800 0x0 0x20>;
> + #clock-cells = <1>;
> + #reset-cells = <1>;
> + clocks = <&clkc_periphs CLKID_AUDIO>,
> + <&clkc_periphs CLKID_DDS_IN>,
> + <&clkc_pll CLKID_FCLK_DIV2>,
> + <&clkc_pll CLKID_FCLK_DIV3>,
> + <&clkc_pll CLKID_HIFI_PLL>,


2024-03-15 10:00:35

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH 13/25] ASoC: dt-bindings: meson: axg-pdm: document 'sysrate' property

On 15/03/2024 00:21, Jan Dakinevich wrote:
> This option allow to redefine the rate of DSP system clock.

And why is it suitable for bindings? Describe the hardware, not what you
want to do in the driver.

>
> Signed-off-by: Jan Dakinevich <[email protected]>
> ---
> Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
> index df21dd72fc65..d2f23a59a6b6 100644
> --- a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
> +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
> @@ -40,6 +40,10 @@ properties:
> resets:
> maxItems: 1
>
> + sysrate:
> + $ref: /schemas/types.yaml#/definitions/uint32
> + description: redefine rate of DSP system clock

No vendor prefix, so is it a generic property? Also, missing unit
suffix, but more importantly I don't understand why this is a property
of hardware.

Best regards,
Krzysztof


2024-03-15 10:01:36

by Jerome Brunet

[permalink] [raw]
Subject: Re: [PATCH 06/25] ASoC: meson: g12a-toacodec: fix "Lane Select" width


On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <[email protected]> wrote:

> For both G12A and SM1 the width of "Lane Select" should be 2, not 3.
> Otherwise, it overlaps with "Source".
>
> Signed-off-by: Jan Dakinevich <[email protected]>

Missing a Fixes tag.

Ideally fixes should be sent first in a series - or even separately in
this case

> ---
> sound/soc/meson/g12a-toacodec.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/sound/soc/meson/g12a-toacodec.c b/sound/soc/meson/g12a-toacodec.c
> index 531bb8707a3e..b92de2235627 100644
> --- a/sound/soc/meson/g12a-toacodec.c
> +++ b/sound/soc/meson/g12a-toacodec.c
> @@ -229,11 +229,11 @@ static const struct snd_soc_dapm_route g12a_toacodec_routes[] = {
> };
>
> static const struct snd_kcontrol_new g12a_toacodec_controls[] = {
> - SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL, 3, 0),
> + SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL, 2, 0),
> };
>
> static const struct snd_kcontrol_new sm1_toacodec_controls[] = {
> - SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL_SM1, 3, 0),
> + SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL_SM1, 2, 0),
> };
>
> static const struct snd_soc_component_driver g12a_toacodec_component_drv = {


--
Jerome

2024-03-15 10:07:10

by Jerome Brunet

[permalink] [raw]
Subject: Re: [PATCH 00/25] Introduce support of audio for Amlogic A1 SoC family


On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <[email protected]> wrote:

> This series includes the following:
>
> - new audio clock and reset controller data and adaptation for it of existing
> code (patches 0001..0004);
>
> - adaptation of existing audio components for A1 Soc (patches 0005..0021);
>
> - handy cosmetics for dai-link naming (patches 0022..0023);
>
> - integration of audio devices into common trees (patch 0024);
>
> - audio support bring up on Amlogic ad402 reference board (patch 0025). This
> patch is not actually checked on real hardware (because all ad402 that we had
> were burned out). This patch is based on ad402's schematics and on experience
> with our own hardware (which is very close to reference board);
>
> Dmitry Rokosov (2):
> ASoC: dt-bindings: meson: introduce link-name optional property
> ASoC: meson: implement link-name optional property in meson card utils
>
> Jan Dakinevich (23):
> clk: meson: a1: restrict an amount of 'hifi_pll' params
> clk: meson: axg: move reset controller's code to separate module
> dt-bindings: clock: meson: add A1 audio clock and reset controller
> bindings
> clk: meson: a1: add the audio clock controller driver
> ASoC: meson: codec-glue: add support for capture stream
> ASoC: meson: g12a-toacodec: fix "Lane Select" width
> ASoC: meson: g12a-toacodec: rework the definition of bits
> ASoC: dt-bindings: meson: g12a-toacodec: add support for A1 SoC family
> ASoC: meson: g12a-toacodec: add support for A1 SoC family
> ASoC: meson: t9015: prepare to adding new platforms
> ASoC: dt-bindings: meson: t9015: add support for A1 SoC family
> ASoC: meson: t9015: add support for A1 SoC family
> ASoC: dt-bindings: meson: axg-pdm: document 'sysrate' property
> ASoC: meson: axg-pdm: introduce 'sysrate' property
> pinctrl/meson: fix typo in PDM's pin name
> ASoC: dt-bindings: meson: meson-axg-audio-arb: claim support of A1 SoC
> family
> ASoC: dt-bindings: meson: axg-fifo: claim support of A1 SoC family
> ASoC: dt-bindings: meson: axg-pdm: claim support of A1 SoC family
> ASoC: dt-bindings: meson: axg-sound-card: claim support of A1 SoC
> family
> ASoC: dt-bindings: meson: axg-tdm-formatters: claim support of A1 SoC
> family
> ASoC: dt-bindings: meson: axg-tdm-iface: claim support of A1 SoC
> family
> arm64: dts: meson: a1: add audio devices
> arm64: dts: ad402: enable audio

I'm sorry but a 25 patches series is just way too big, especially when
spamming multiple sub systems.

Please try to make one series per sub systems and general topic, I see
at least
* A1 audio clocks
* G12 acodec fix
* Acodec rework
* PDM
* pinctrl
* arm64

I did not review all but I think I've made enough comments to keep you
busy for a while

>
> .../bindings/clock/amlogic,a1-audio-clkc.yaml | 83 +++
> .../reset/amlogic,meson-axg-audio-arb.yaml | 10 +-
> .../bindings/sound/amlogic,axg-fifo.yaml | 8 +
> .../bindings/sound/amlogic,axg-pdm.yaml | 5 +
> .../sound/amlogic,axg-sound-card.yaml | 12 +-
> .../sound/amlogic,axg-tdm-formatters.yaml | 22 +-
> .../bindings/sound/amlogic,axg-tdm-iface.yaml | 6 +-
> .../bindings/sound/amlogic,g12a-toacodec.yaml | 1 +
> .../bindings/sound/amlogic,gx-sound-card.yaml | 6 +
> .../bindings/sound/amlogic,t9015.yaml | 4 +-
> .../arm64/boot/dts/amlogic/meson-a1-ad402.dts | 126 ++++
> arch/arm64/boot/dts/amlogic/meson-a1.dtsi | 471 +++++++++++++++
> drivers/clk/meson/Kconfig | 18 +
> drivers/clk/meson/Makefile | 2 +
> drivers/clk/meson/a1-audio.c | 556 ++++++++++++++++++
> drivers/clk/meson/a1-audio.h | 58 ++
> drivers/clk/meson/a1-pll.c | 8 +-
> drivers/clk/meson/axg-audio.c | 95 +--
> drivers/clk/meson/meson-audio-rstc.c | 109 ++++
> drivers/clk/meson/meson-audio-rstc.h | 12 +
> drivers/pinctrl/meson/pinctrl-meson-a1.c | 6 +-
> .../dt-bindings/clock/amlogic,a1-audio-clkc.h | 122 ++++
> .../reset/amlogic,meson-a1-audio-reset.h | 29 +
> .../dt-bindings/sound/meson-g12a-toacodec.h | 5 +
> sound/soc/meson/axg-pdm.c | 10 +-
> sound/soc/meson/g12a-toacodec.c | 298 ++++++++--
> sound/soc/meson/meson-card-utils.c | 12 +-
> sound/soc/meson/meson-codec-glue.c | 174 ++++--
> sound/soc/meson/meson-codec-glue.h | 23 +
> sound/soc/meson/t9015.c | 326 +++++++++-
> 30 files changed, 2394 insertions(+), 223 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/clock/amlogic,a1-audio-clkc.yaml
> create mode 100644 drivers/clk/meson/a1-audio.c
> create mode 100644 drivers/clk/meson/a1-audio.h
> create mode 100644 drivers/clk/meson/meson-audio-rstc.c
> create mode 100644 drivers/clk/meson/meson-audio-rstc.h
> create mode 100644 include/dt-bindings/clock/amlogic,a1-audio-clkc.h
> create mode 100644 include/dt-bindings/reset/amlogic,meson-a1-audio-reset.h


--
Jerome

2024-03-15 10:13:42

by Jerome Brunet

[permalink] [raw]
Subject: Re: [PATCH 19/25] ASoC: dt-bindings: meson: axg-sound-card: claim support of A1 SoC family


On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <[email protected]> wrote:

> Add "amlogic,a1-sound-card" compatible string alias to
> "amlogic,axg-sound-card".
>
> Signed-off-by: Jan Dakinevich <[email protected]>
> ---
> .../devicetree/bindings/sound/amlogic,axg-sound-card.yaml | 6 +++++-
> 1 file changed, 5 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml
> index 5db718e4d0e7..492b41cc8ccd 100644
> --- a/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml
> +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml
> @@ -14,7 +14,11 @@ allOf:
>
> properties:
> compatible:
> - const: amlogic,axg-sound-card
> + oneOf:
> + - const: amlogic,axg-sound-card
> + - items:
> + - const: amlogic,a1-sound-card
> + - const: amlogic,axg-sound-card

I know the rule about SoC related name but it is different here.
This does not describe HW in the SoC.

The axg sound card is just a name, much like simple-card or
audio-graph-card. I could have named it "amlogic,my-awesome-card"

We would not add "amlogic,a1-simple-card", would we ?

It is purely a software component, which aggregate HW ones.


> audio-aux-devs:
> $ref: /schemas/types.yaml#/definitions/phandle-array


--
Jerome

2024-03-15 10:21:05

by Jerome Brunet

[permalink] [raw]
Subject: Re: [PATCH 21/25] ASoC: dt-bindings: meson: axg-tdm-iface: claim support of A1 SoC family


On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <[email protected]> wrote:

> Add "amlogic,a1-tdm-iface" compatible string alias to
> "amlogic,axg-tdm-iface".
>
> Signed-off-by: Jan Dakinevich <[email protected]>
> ---
> .../devicetree/bindings/sound/amlogic,axg-tdm-iface.yaml | 6 +++++-
> 1 file changed, 5 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.yaml
> index 45955d8a26d1..7c1af85b52b4 100644
> --- a/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.yaml
> +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.yaml
> @@ -14,7 +14,11 @@ allOf:
>
> properties:
> compatible:
> - const: amlogic,axg-tdm-iface
> + oneOf:
> + - const: amlogic,axg-tdm-iface
> + - items:
> + - const: amlogic,a1-tdm-iface
> + - const: amlogic,axg-tdm-iface

Same as the card driver. I could have named it "amlogic,tdm-iface"

This is purely a SW component, which help agregate clocks and
tdm-formatters. It is analog to a "gpio-leds" or a "pwm-clock"
driver. We would add a compatible for every SoC for these, would we ?

I don't think it makes a lot of sense to add this. It is not going to
hurt but this is just adding useless compatible to the doc that will
never be used

>
> "#sound-dai-cells":
> const: 0


--
Jerome

2024-03-15 10:25:17

by Jerome Brunet

[permalink] [raw]
Subject: Re: [PATCH 13/25] ASoC: dt-bindings: meson: axg-pdm: document 'sysrate' property


On Fri 15 Mar 2024 at 11:00, Krzysztof Kozlowski <[email protected]> wrote:

> On 15/03/2024 00:21, Jan Dakinevich wrote:
>> This option allow to redefine the rate of DSP system clock.
>
> And why is it suitable for bindings? Describe the hardware, not what you
> want to do in the driver.
>
>>
>> Signed-off-by: Jan Dakinevich <[email protected]>
>> ---
>> Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml | 4 ++++
>> 1 file changed, 4 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>> index df21dd72fc65..d2f23a59a6b6 100644
>> --- a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>> +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>> @@ -40,6 +40,10 @@ properties:
>> resets:
>> maxItems: 1
>>
>> + sysrate:
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> + description: redefine rate of DSP system clock
>
> No vendor prefix, so is it a generic property? Also, missing unit
> suffix, but more importantly I don't understand why this is a property
> of hardware.

+1.

The appropriate way to set rate of the clock before the driver take over
is 'assigned-rate', if you need to customize this for different
platform.

Then you don't have to deal with it in the device driver.

>
> Best regards,
> Krzysztof


--
Jerome

2024-03-15 13:18:16

by Dan Carpenter

[permalink] [raw]
Subject: Re: [PATCH 06/25] ASoC: meson: g12a-toacodec: fix "Lane Select" width

On Fri, Mar 15, 2024 at 02:21:42AM +0300, Jan Dakinevich wrote:
> For both G12A and SM1 the width of "Lane Select" should be 2, not 3.
> Otherwise, it overlaps with "Source".
>
> Signed-off-by: Jan Dakinevich <[email protected]>

When you resend this, could you describe how this bug might look to a
user? If a user hits this bug and they want to see if it has been
fixed by reading the commit messages, I don't think this description
is sufficient for that.

regards,
dan carpenter


2024-03-15 13:33:47

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH 09/25] ASoC: meson: g12a-toacodec: add support for A1 SoC family

On Fri, Mar 15, 2024 at 02:21:45AM +0300, Jan Dakinevich wrote:

> static const struct regmap_config g12a_toacodec_regmap_cfg = {
> - .reg_bits = 32,
> - .val_bits = 32,
> - .reg_stride = 4,
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = 4,
> + .max_register = TOACODEC_CTRL0,
> + .max_register_is_0 = true,

If the maximum register is 0 how does the regmap have a stride?


Attachments:
(No filename) (402.00 B)
signature.asc (499.00 B)
Download all attachments

2024-03-15 13:37:05

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH 12/25] ASoC: meson: t9015: add support for A1 SoC family

On Fri, Mar 15, 2024 at 02:21:48AM +0300, Jan Dakinevich wrote:

> +static const char * const a1_adc_mic_bias_level_txt[] = { "2.0V", "2.1V",
> + "2.3V", "2.5V", "2.8V" };
> +static const unsigned int a1_adc_mic_bias_level_values[] = { 0, 1, 2, 3, 7 };

Why would this be varied at runtime rather than being something fixed
when the system is designed?

> +static const char * const a1_adc_pga_txt[] = { "None", "Differential",
> + "Positive", "Negative" };
> +static const unsigned int a1_adc_pga_right_values[] = { 0, PGAR_DIFF,
> + PGAR_POSITIVE, PGAR_NEGATIVE };
> +static const unsigned int a1_adc_pga_left_values[] = { 0, PGAL_DIFF,
> + PGAL_POSITIVE, PGAL_NEGATIVE };

Similarly here.

> + SOC_SINGLE("ADC Mic Bias Switch", LINEIN_CFG, MICBIAS_EN, 1, 0),
> + SOC_ENUM("ADC Mic Bias Level", a1_adc_mic_bias_level),

Why would micbias be user controlled rather than a DAPM widget as
normal?


Attachments:
(No filename) (919.00 B)
signature.asc (499.00 B)
Download all attachments

2024-03-15 15:51:55

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH 00/25] Introduce support of audio for Amlogic A1 SoC family


On Fri, 15 Mar 2024 02:21:36 +0300, Jan Dakinevich wrote:
> This series includes the following:
>
> - new audio clock and reset controller data and adaptation for it of existing
> code (patches 0001..0004);
>
> - adaptation of existing audio components for A1 Soc (patches 0005..0021);
>
> - handy cosmetics for dai-link naming (patches 0022..0023);
>
> - integration of audio devices into common trees (patch 0024);
>
> - audio support bring up on Amlogic ad402 reference board (patch 0025). This
> patch is not actually checked on real hardware (because all ad402 that we had
> were burned out). This patch is based on ad402's schematics and on experience
> with our own hardware (which is very close to reference board);
>
> Dmitry Rokosov (2):
> ASoC: dt-bindings: meson: introduce link-name optional property
> ASoC: meson: implement link-name optional property in meson card utils
>
> Jan Dakinevich (23):
> clk: meson: a1: restrict an amount of 'hifi_pll' params
> clk: meson: axg: move reset controller's code to separate module
> dt-bindings: clock: meson: add A1 audio clock and reset controller
> bindings
> clk: meson: a1: add the audio clock controller driver
> ASoC: meson: codec-glue: add support for capture stream
> ASoC: meson: g12a-toacodec: fix "Lane Select" width
> ASoC: meson: g12a-toacodec: rework the definition of bits
> ASoC: dt-bindings: meson: g12a-toacodec: add support for A1 SoC family
> ASoC: meson: g12a-toacodec: add support for A1 SoC family
> ASoC: meson: t9015: prepare to adding new platforms
> ASoC: dt-bindings: meson: t9015: add support for A1 SoC family
> ASoC: meson: t9015: add support for A1 SoC family
> ASoC: dt-bindings: meson: axg-pdm: document 'sysrate' property
> ASoC: meson: axg-pdm: introduce 'sysrate' property
> pinctrl/meson: fix typo in PDM's pin name
> ASoC: dt-bindings: meson: meson-axg-audio-arb: claim support of A1 SoC
> family
> ASoC: dt-bindings: meson: axg-fifo: claim support of A1 SoC family
> ASoC: dt-bindings: meson: axg-pdm: claim support of A1 SoC family
> ASoC: dt-bindings: meson: axg-sound-card: claim support of A1 SoC
> family
> ASoC: dt-bindings: meson: axg-tdm-formatters: claim support of A1 SoC
> family
> ASoC: dt-bindings: meson: axg-tdm-iface: claim support of A1 SoC
> family
> arm64: dts: meson: a1: add audio devices
> arm64: dts: ad402: enable audio
>
> .../bindings/clock/amlogic,a1-audio-clkc.yaml | 83 +++
> .../reset/amlogic,meson-axg-audio-arb.yaml | 10 +-
> .../bindings/sound/amlogic,axg-fifo.yaml | 8 +
> .../bindings/sound/amlogic,axg-pdm.yaml | 5 +
> .../sound/amlogic,axg-sound-card.yaml | 12 +-
> .../sound/amlogic,axg-tdm-formatters.yaml | 22 +-
> .../bindings/sound/amlogic,axg-tdm-iface.yaml | 6 +-
> .../bindings/sound/amlogic,g12a-toacodec.yaml | 1 +
> .../bindings/sound/amlogic,gx-sound-card.yaml | 6 +
> .../bindings/sound/amlogic,t9015.yaml | 4 +-
> .../arm64/boot/dts/amlogic/meson-a1-ad402.dts | 126 ++++
> arch/arm64/boot/dts/amlogic/meson-a1.dtsi | 471 +++++++++++++++
> drivers/clk/meson/Kconfig | 18 +
> drivers/clk/meson/Makefile | 2 +
> drivers/clk/meson/a1-audio.c | 556 ++++++++++++++++++
> drivers/clk/meson/a1-audio.h | 58 ++
> drivers/clk/meson/a1-pll.c | 8 +-
> drivers/clk/meson/axg-audio.c | 95 +--
> drivers/clk/meson/meson-audio-rstc.c | 109 ++++
> drivers/clk/meson/meson-audio-rstc.h | 12 +
> drivers/pinctrl/meson/pinctrl-meson-a1.c | 6 +-
> .../dt-bindings/clock/amlogic,a1-audio-clkc.h | 122 ++++
> .../reset/amlogic,meson-a1-audio-reset.h | 29 +
> .../dt-bindings/sound/meson-g12a-toacodec.h | 5 +
> sound/soc/meson/axg-pdm.c | 10 +-
> sound/soc/meson/g12a-toacodec.c | 298 ++++++++--
> sound/soc/meson/meson-card-utils.c | 12 +-
> sound/soc/meson/meson-codec-glue.c | 174 ++++--
> sound/soc/meson/meson-codec-glue.h | 23 +
> sound/soc/meson/t9015.c | 326 +++++++++-
> 30 files changed, 2394 insertions(+), 223 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/clock/amlogic,a1-audio-clkc.yaml
> create mode 100644 drivers/clk/meson/a1-audio.c
> create mode 100644 drivers/clk/meson/a1-audio.h
> create mode 100644 drivers/clk/meson/meson-audio-rstc.c
> create mode 100644 drivers/clk/meson/meson-audio-rstc.h
> create mode 100644 include/dt-bindings/clock/amlogic,a1-audio-clkc.h
> create mode 100644 include/dt-bindings/reset/amlogic,meson-a1-audio-reset.h
>
> --
> 2.34.1
>
>
>


My bot found new DTB warnings on the .dts files added or changed in this
series.

Some warnings may be from an existing SoC .dtsi. Or perhaps the warnings
are fixed by another series. Ultimately, it is up to the platform
maintainer whether these warnings are acceptable or not. No need to reply
unless the platform maintainer has comments.

If you already ran DT checks and didn't see these error(s), then
make sure dt-schema is up to date:

pip3 install dtschema --upgrade


New warnings running 'make CHECK_DTBS=y amlogic/meson-a1-ad402.dtb' for [email protected]:

arch/arm64/boot/dts/amlogic/meson-a1-ad402.dtb: audio-controller@4800: 'power-domains' does not match any of the regexes: 'pinctrl-[0-9]+'
from schema $id: http://devicetree.org/schemas/sound/amlogic,t9015.yaml#
arch/arm64/boot/dts/amlogic/meson-a1-ad402.dtb: audio-controller@1000: Unevaluated properties are not allowed ('power-domains' was unexpected)
from schema $id: http://devicetree.org/schemas/sound/amlogic,axg-pdm.yaml#






2024-03-15 16:54:44

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH 00/25] Introduce support of audio for Amlogic A1 SoC family

Hi Jan!

On 15/03/2024 00:21, Jan Dakinevich wrote:
> This series includes the following:
>
> - new audio clock and reset controller data and adaptation for it of existing
> code (patches 0001..0004);
>
> - adaptation of existing audio components for A1 Soc (patches 0005..0021);
>
> - handy cosmetics for dai-link naming (patches 0022..0023);
>
> - integration of audio devices into common trees (patch 0024);
>
> - audio support bring up on Amlogic ad402 reference board (patch 0025). This
> patch is not actually checked on real hardware (because all ad402 that we had
> were burned out). This patch is based on ad402's schematics and on experience
> with our own hardware (which is very close to reference board);

Thanks for your serie, it's nice you're working on upstreaming this feature.

In my opinion it's fine to have a "big" initial serie if you're not sure
if your changes are ok, but next time add the RFC tag so we know it's not
a final changeset and you seek advices.

Overall the code is clean and your patch order makes sense if they were meant
to be applied by a single maintainer, but in this case it will be split
into multiple subsystems so it's better to split them as Jerome explained
to ease review and the maintainers process.

Don't hesitate discussing with us in the #linux-amlogic IRC channel
on Libera.Chat, the goal is to reduce the number of patch version and
ease the review and maintainance process.

Concerning the link-name property, I think it should be done afterwards
since it's not necessary to support audio on A1, and I think it could
be extended to other SoC boards (which would be a great feature).

Neil

>
> Dmitry Rokosov (2):
> ASoC: dt-bindings: meson: introduce link-name optional property
> ASoC: meson: implement link-name optional property in meson card utils
>
> Jan Dakinevich (23):
> clk: meson: a1: restrict an amount of 'hifi_pll' params
> clk: meson: axg: move reset controller's code to separate module
> dt-bindings: clock: meson: add A1 audio clock and reset controller
> bindings
> clk: meson: a1: add the audio clock controller driver
> ASoC: meson: codec-glue: add support for capture stream
> ASoC: meson: g12a-toacodec: fix "Lane Select" width
> ASoC: meson: g12a-toacodec: rework the definition of bits
> ASoC: dt-bindings: meson: g12a-toacodec: add support for A1 SoC family
> ASoC: meson: g12a-toacodec: add support for A1 SoC family
> ASoC: meson: t9015: prepare to adding new platforms
> ASoC: dt-bindings: meson: t9015: add support for A1 SoC family
> ASoC: meson: t9015: add support for A1 SoC family
> ASoC: dt-bindings: meson: axg-pdm: document 'sysrate' property
> ASoC: meson: axg-pdm: introduce 'sysrate' property
> pinctrl/meson: fix typo in PDM's pin name
> ASoC: dt-bindings: meson: meson-axg-audio-arb: claim support of A1 SoC
> family
> ASoC: dt-bindings: meson: axg-fifo: claim support of A1 SoC family
> ASoC: dt-bindings: meson: axg-pdm: claim support of A1 SoC family
> ASoC: dt-bindings: meson: axg-sound-card: claim support of A1 SoC
> family
> ASoC: dt-bindings: meson: axg-tdm-formatters: claim support of A1 SoC
> family
> ASoC: dt-bindings: meson: axg-tdm-iface: claim support of A1 SoC
> family
> arm64: dts: meson: a1: add audio devices
> arm64: dts: ad402: enable audio
>
> .../bindings/clock/amlogic,a1-audio-clkc.yaml | 83 +++
> .../reset/amlogic,meson-axg-audio-arb.yaml | 10 +-
> .../bindings/sound/amlogic,axg-fifo.yaml | 8 +
> .../bindings/sound/amlogic,axg-pdm.yaml | 5 +
> .../sound/amlogic,axg-sound-card.yaml | 12 +-
> .../sound/amlogic,axg-tdm-formatters.yaml | 22 +-
> .../bindings/sound/amlogic,axg-tdm-iface.yaml | 6 +-
> .../bindings/sound/amlogic,g12a-toacodec.yaml | 1 +
> .../bindings/sound/amlogic,gx-sound-card.yaml | 6 +
> .../bindings/sound/amlogic,t9015.yaml | 4 +-
> .../arm64/boot/dts/amlogic/meson-a1-ad402.dts | 126 ++++
> arch/arm64/boot/dts/amlogic/meson-a1.dtsi | 471 +++++++++++++++
> drivers/clk/meson/Kconfig | 18 +
> drivers/clk/meson/Makefile | 2 +
> drivers/clk/meson/a1-audio.c | 556 ++++++++++++++++++
> drivers/clk/meson/a1-audio.h | 58 ++
> drivers/clk/meson/a1-pll.c | 8 +-
> drivers/clk/meson/axg-audio.c | 95 +--
> drivers/clk/meson/meson-audio-rstc.c | 109 ++++
> drivers/clk/meson/meson-audio-rstc.h | 12 +
> drivers/pinctrl/meson/pinctrl-meson-a1.c | 6 +-
> .../dt-bindings/clock/amlogic,a1-audio-clkc.h | 122 ++++
> .../reset/amlogic,meson-a1-audio-reset.h | 29 +
> .../dt-bindings/sound/meson-g12a-toacodec.h | 5 +
> sound/soc/meson/axg-pdm.c | 10 +-
> sound/soc/meson/g12a-toacodec.c | 298 ++++++++--
> sound/soc/meson/meson-card-utils.c | 12 +-
> sound/soc/meson/meson-codec-glue.c | 174 ++++--
> sound/soc/meson/meson-codec-glue.h | 23 +
> sound/soc/meson/t9015.c | 326 +++++++++-
> 30 files changed, 2394 insertions(+), 223 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/clock/amlogic,a1-audio-clkc.yaml
> create mode 100644 drivers/clk/meson/a1-audio.c
> create mode 100644 drivers/clk/meson/a1-audio.h
> create mode 100644 drivers/clk/meson/meson-audio-rstc.c
> create mode 100644 drivers/clk/meson/meson-audio-rstc.h
> create mode 100644 include/dt-bindings/clock/amlogic,a1-audio-clkc.h
> create mode 100644 include/dt-bindings/reset/amlogic,meson-a1-audio-reset.h
>


2024-03-17 15:20:36

by Jan Dakinevich

[permalink] [raw]
Subject: Re: [PATCH 09/25] ASoC: meson: g12a-toacodec: add support for A1 SoC family



On 3/15/24 16:33, Mark Brown wrote:
> On Fri, Mar 15, 2024 at 02:21:45AM +0300, Jan Dakinevich wrote:
>
>> static const struct regmap_config g12a_toacodec_regmap_cfg = {
>> - .reg_bits = 32,
>> - .val_bits = 32,
>> - .reg_stride = 4,
>> + .reg_bits = 32,
>> + .val_bits = 32,
>> + .reg_stride = 4,
>> + .max_register = TOACODEC_CTRL0,
>> + .max_register_is_0 = true,
>
> If the maximum register is 0 how does the regmap have a stride?

reg_stride inherited from existing code. Apparently, it was meaningless
even before my modifications (the hardware has single register
regardless of max_register declaration) and it should be dropped. But,
is it okay to remove it in the same commit?

--
Best regards
Jan Dakinevich

2024-03-17 15:53:15

by Jan Dakinevich

[permalink] [raw]
Subject: Re: [PATCH 13/25] ASoC: dt-bindings: meson: axg-pdm: document 'sysrate' property



On 3/15/24 13:22, Jerome Brunet wrote:
>
> On Fri 15 Mar 2024 at 11:00, Krzysztof Kozlowski <[email protected]> wrote:
>
>> On 15/03/2024 00:21, Jan Dakinevich wrote:
>>> This option allow to redefine the rate of DSP system clock.
>>
>> And why is it suitable for bindings? Describe the hardware, not what you
>> want to do in the driver.
>>
>>>
>>> Signed-off-by: Jan Dakinevich <[email protected]>
>>> ---
>>> Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml | 4 ++++
>>> 1 file changed, 4 insertions(+)
>>>
>>> diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>>> index df21dd72fc65..d2f23a59a6b6 100644
>>> --- a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>>> +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>>> @@ -40,6 +40,10 @@ properties:
>>> resets:
>>> maxItems: 1
>>>
>>> + sysrate:
>>> + $ref: /schemas/types.yaml#/definitions/uint32
>>> + description: redefine rate of DSP system clock
>>
>> No vendor prefix, so is it a generic property? Also, missing unit
>> suffix, but more importantly I don't understand why this is a property
>> of hardware.
>
> +1.
>
> The appropriate way to set rate of the clock before the driver take over
> is 'assigned-rate', if you need to customize this for different
> platform.
>

It would be great, but it doesn't work. Below, is what I want to see:

assigned-clocks =
<&clkc_audio AUD2_CLKID_PDM_SYSCLK_SEL>,
<&clkc_audio AUD2_CLKID_PDM_SYSCLK_DIV>;
assigned-clock-parents =
<&clkc_pll CLKID_FCLK_DIV3>,
<0>;
assigned-clock-rates =
<0>,
<256000000>;

But regardles of this declaration, PDM's driver unconditionally sets
sysclk'rate to 250MHz and throws away everything that was configured
before, reparents audio2_pdm_sysclk_mux to hifi_pll and changes
hifi_pll's rate.

This value 250MHz is declared here:

static const struct axg_pdm_cfg axg_pdm_config = {
.filters = &axg_default_filters,
.sys_rate = 250000000,
};

The property 'sysrate' is intended to redefine hardcoded 'sys_rate'
value in 'axg_pdm_config'.

> Then you don't have to deal with it in the device driver.
>
>>
>> Best regards,
>> Krzysztof
>
>

--
Best regards
Jan Dakinevich

2024-03-17 15:56:59

by Jan Dakinevich

[permalink] [raw]
Subject: Re: [PATCH 13/25] ASoC: dt-bindings: meson: axg-pdm: document 'sysrate' property



On 3/15/24 13:00, Krzysztof Kozlowski wrote:
> On 15/03/2024 00:21, Jan Dakinevich wrote:
>> This option allow to redefine the rate of DSP system clock.
>
> And why is it suitable for bindings? Describe the hardware, not what you
> want to do in the driver.
>

What do you mean? I am adding some new property and should describe it
in dt-bindinds. Isn't it?

>>
>> Signed-off-by: Jan Dakinevich <[email protected]>
>> ---
>> Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml | 4 ++++
>> 1 file changed, 4 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>> index df21dd72fc65..d2f23a59a6b6 100644
>> --- a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>> +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>> @@ -40,6 +40,10 @@ properties:
>> resets:
>> maxItems: 1
>>
>> + sysrate:
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> + description: redefine rate of DSP system clock
>
> No vendor prefix, so is it a generic property? Also, missing unit
> suffix, but more importantly I don't understand why this is a property
> of hardware.
>

Answered in next message.

> Best regards,
> Krzysztof
>

--
Best regards
Jan Dakinevich

2024-03-17 16:28:43

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH 13/25] ASoC: dt-bindings: meson: axg-pdm: document 'sysrate' property

On 17/03/2024 16:55, Jan Dakinevich wrote:
>
>
> On 3/15/24 13:00, Krzysztof Kozlowski wrote:
>> On 15/03/2024 00:21, Jan Dakinevich wrote:
>>> This option allow to redefine the rate of DSP system clock.
>>
>> And why is it suitable for bindings? Describe the hardware, not what you
>> want to do in the driver.
>>
>
> What do you mean? I am adding some new property and should describe it
> in dt-bindinds. Isn't it?

No, if the property is not suitable for bindings, you should not add it
in the first place. So again: explain what sort of hardware, not driver,
problem you are solving here, so we can understand why do you need new
property. Otherwise use existing properties or no properties, because we
do not define all possible clocks in the bindings.

Let's be clear: with such commit msg explanation as you have, my answer
is: no, driver should set clock frequency and you do not need this
property at all.

Best regards,
Krzysztof


2024-03-17 16:29:08

by Jan Dakinevich

[permalink] [raw]
Subject: Re: [PATCH 12/25] ASoC: meson: t9015: add support for A1 SoC family



On 3/15/24 16:36, Mark Brown wrote:
> On Fri, Mar 15, 2024 at 02:21:48AM +0300, Jan Dakinevich wrote:
>
>> +static const char * const a1_adc_mic_bias_level_txt[] = { "2.0V", "2.1V",
>> + "2.3V", "2.5V", "2.8V" };
>> +static const unsigned int a1_adc_mic_bias_level_values[] = { 0, 1, 2, 3, 7 };
>
> Why would this be varied at runtime rather than being something fixed
> when the system is designed?
>
>> +static const char * const a1_adc_pga_txt[] = { "None", "Differential",
>> + "Positive", "Negative" };
>> +static const unsigned int a1_adc_pga_right_values[] = { 0, PGAR_DIFF,
>> + PGAR_POSITIVE, PGAR_NEGATIVE };
>> +static const unsigned int a1_adc_pga_left_values[] = { 0, PGAL_DIFF,
>> + PGAL_POSITIVE, PGAL_NEGATIVE };
>
> Similarly here.
>

Both mic bias and ADC's input mode depends on schematics and should be
configurable. What is the better way to give access to these parameters?
Device tree?

>> + SOC_SINGLE("ADC Mic Bias Switch", LINEIN_CFG, MICBIAS_EN, 1, 0),
>> + SOC_ENUM("ADC Mic Bias Level", a1_adc_mic_bias_level),
>
> Why would micbias be user controlled rather than a DAPM widget as
> normal?

Yes, I could use SND_SOC_DAPM_SUPPLY, but it supports only raw values,
and doesn't supports enums. Here, I want to use enum to restrict
possible values, because only these values mentioned in the
documentation that I have.

--
Best regards
Jan Dakinevich

2024-03-17 16:36:04

by Jan Dakinevich

[permalink] [raw]
Subject: Re: [PATCH 13/25] ASoC: dt-bindings: meson: axg-pdm: document 'sysrate' property



On 3/17/24 19:27, Krzysztof Kozlowski wrote:
> On 17/03/2024 16:55, Jan Dakinevich wrote:
>>
>>
>> On 3/15/24 13:00, Krzysztof Kozlowski wrote:
>>> On 15/03/2024 00:21, Jan Dakinevich wrote:
>>>> This option allow to redefine the rate of DSP system clock.
>>>
>>> And why is it suitable for bindings? Describe the hardware, not what you
>>> want to do in the driver.
>>>
>>
>> What do you mean? I am adding some new property and should describe it
>> in dt-bindinds. Isn't it?
>
> No, if the property is not suitable for bindings, you should not add it
> in the first place. So again: explain what sort of hardware, not driver,
> problem you are solving here, so we can understand why do you need new
> property. Otherwise use existing properties or no properties, because we
> do not define all possible clocks in the bindings.
>
> Let's be clear: with such commit msg explanation as you have, my answer
> is: no, driver should set clock frequency and you do not need this
> property at all.
>

Could you please take a look on answer to "Jerome Brunet
<[email protected]>"'s message on the same thread. There, I am trying
to explain what I am solving by this commit.

I would be happy to avoid this w/a, but currently this is the best I
came up with.

> Best regards,
> Krzysztof
>

--
Best regards
Jan Dakinevich

2024-03-17 16:40:48

by Jan Dakinevich

[permalink] [raw]
Subject: Re: [PATCH 19/25] ASoC: dt-bindings: meson: axg-sound-card: claim support of A1 SoC family



On 3/15/24 13:06, Jerome Brunet wrote:
>
> On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <[email protected]> wrote:
>
>> Add "amlogic,a1-sound-card" compatible string alias to
>> "amlogic,axg-sound-card".
>>
>> Signed-off-by: Jan Dakinevich <[email protected]>
>> ---
>> .../devicetree/bindings/sound/amlogic,axg-sound-card.yaml | 6 +++++-
>> 1 file changed, 5 insertions(+), 1 deletion(-)
>>
>> diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml
>> index 5db718e4d0e7..492b41cc8ccd 100644
>> --- a/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml
>> +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml
>> @@ -14,7 +14,11 @@ allOf:
>>
>> properties:
>> compatible:
>> - const: amlogic,axg-sound-card
>> + oneOf:
>> + - const: amlogic,axg-sound-card
>> + - items:
>> + - const: amlogic,a1-sound-card
>> + - const: amlogic,axg-sound-card
>
> I know the rule about SoC related name but it is different here.
> This does not describe HW in the SoC.
>
> The axg sound card is just a name, much like simple-card or
> audio-graph-card. I could have named it "amlogic,my-awesome-card"
>
> We would not add "amlogic,a1-simple-card", would we ?
>
> It is purely a software component, which aggregate HW ones.
>
>
>> audio-aux-devs:
>> $ref: /schemas/types.yaml#/definitions/phandle-array
>
>

So, I can drop a couple of commits like this. Right?

--
Best regards
Jan Dakinevich

2024-03-17 19:03:29

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH 11/25] ASoC: dt-bindings: meson: t9015: add support for A1 SoC family

On Fri, Mar 15, 2024 at 02:21:47AM +0300, Jan Dakinevich wrote:
> Add "amlogic,t9015-a1" compatible string.

That's obvious from the diff. Perhaps what does 'a1' mean? What the
difference from the existing compatible?

>
> Signed-off-by: Jan Dakinevich <[email protected]>
> ---
> Documentation/devicetree/bindings/sound/amlogic,t9015.yaml | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml b/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml
> index 5f5cccdbeb34..ee8bd57dbcf9 100644
> --- a/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml
> +++ b/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml
> @@ -21,7 +21,9 @@ properties:
>
> compatible:
> items:
> - - const: amlogic,t9015
> + - enum:
> + - amlogic,t9015
> + - amlogic,t9015-a1
>
> clocks:
> items:
> --
> 2.34.1
>

2024-03-17 19:10:23

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH 16/25] ASoC: dt-bindings: meson: meson-axg-audio-arb: claim support of A1 SoC family


On Fri, 15 Mar 2024 02:21:52 +0300, Jan Dakinevich wrote:
> Add "amlogic,meson-a1-audio-arb" compatible string alias to
> "amlogic,meson-sm1-audio-arb".
>
> Signed-off-by: Jan Dakinevich <[email protected]>
> ---
> .../bindings/reset/amlogic,meson-axg-audio-arb.yaml | 10 +++++++---
> 1 file changed, 7 insertions(+), 3 deletions(-)
>

Acked-by: Rob Herring <[email protected]>


2024-03-17 19:13:25

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH 17/25] ASoC: dt-bindings: meson: axg-fifo: claim support of A1 SoC family


On Fri, 15 Mar 2024 02:21:53 +0300, Jan Dakinevich wrote:
> Add "amlogic,a1-toddr" and "amlogic,a1-frddr" compatible string aliases
> to "amlogic,sm1-toddr" and "amlogic,sm1-frddr" respectevely.
>
> Signed-off-by: Jan Dakinevich <[email protected]>
> ---
> .../devicetree/bindings/sound/amlogic,axg-fifo.yaml | 8 ++++++++
> 1 file changed, 8 insertions(+)
>

Acked-by: Rob Herring <[email protected]>


2024-03-17 19:14:36

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH 18/25] ASoC: dt-bindings: meson: axg-pdm: claim support of A1 SoC family


On Fri, 15 Mar 2024 02:21:54 +0300, Jan Dakinevich wrote:
> Add "amlogic,a1-pdm" compatible string alias to "amlogic,axg-pdm".
>
> Signed-off-by: Jan Dakinevich <[email protected]>
> ---
> Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml | 1 +
> 1 file changed, 1 insertion(+)
>

Acked-by: Rob Herring <[email protected]>


2024-03-17 19:17:02

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH 20/25] ASoC: dt-bindings: meson: axg-tdm-formatters: claim support of A1 SoC family


On Fri, 15 Mar 2024 02:21:56 +0300, Jan Dakinevich wrote:
> Add "amlogic,a1-tdmout" and "amlogic,a1-tdmin" compatible string aliases
> to "amlogic,sm1-tdmout" and "amlogic,sm1-tdmin" respectevely.
>
> Signed-off-by: Jan Dakinevich <[email protected]>
> ---
> .../sound/amlogic,axg-tdm-formatters.yaml | 22 ++++++++++++-------
> 1 file changed, 14 insertions(+), 8 deletions(-)
>

Acked-by: Rob Herring <[email protected]>


2024-03-17 19:45:50

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH 22/25] ASoC: dt-bindings: meson: introduce link-name optional property

On Fri, Mar 15, 2024 at 02:21:58AM +0300, Jan Dakinevich wrote:
> From: Dmitry Rokosov <[email protected]>
>
> The 'link-name' property is an optional DT property that allows for the
> customization of the name associated with the DAI link and PCM stream.
> This functionality mirrors the approach commonly utilized in Qualcomm
> audio cards, providing flexibility in DAI naming conventions for
> improved system integration and userspace experience.
>
> It allows userspace program to easy determine PCM stream purpose, e.g.:
> ~ # cat /proc/asound/pcm
> 00-00: speaker (*) : : playback 1
> 00-01: mics (*) : : capture 1
> 00-02: loopback (*) : : capture 1
>
> Signed-off-by: Dmitry Rokosov <[email protected]>

This needs your S-o-b as well.

2024-03-17 23:40:06

by Jan Dakinevich

[permalink] [raw]
Subject: Re: [PATCH 11/25] ASoC: dt-bindings: meson: t9015: add support for A1 SoC family



On 3/17/24 22:03, Rob Herring wrote:
> On Fri, Mar 15, 2024 at 02:21:47AM +0300, Jan Dakinevich wrote:
>> Add "amlogic,t9015-a1" compatible string.
>
> That's obvious from the diff. Perhaps what does 'a1' mean? What the
> difference from the existing compatible?
>

a1 means A1 SoC family. The difference is described in next commit where
this compatible string is added to driver. Should I duplicate here the
explanation from that commit?

>>
>> Signed-off-by: Jan Dakinevich <[email protected]>
>> ---
>> Documentation/devicetree/bindings/sound/amlogic,t9015.yaml | 4 +++-
>> 1 file changed, 3 insertions(+), 1 deletion(-)
>>
>> diff --git a/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml b/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml
>> index 5f5cccdbeb34..ee8bd57dbcf9 100644
>> --- a/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml
>> +++ b/Documentation/devicetree/bindings/sound/amlogic,t9015.yaml
>> @@ -21,7 +21,9 @@ properties:
>>
>> compatible:
>> items:
>> - - const: amlogic,t9015
>> + - enum:
>> + - amlogic,t9015
>> + - amlogic,t9015-a1
>>
>> clocks:
>> items:
>> --
>> 2.34.1
>>

--
Best regards
Jan Dakinevich

2024-03-18 07:27:48

by Dmitry Rokosov

[permalink] [raw]
Subject: Re: [PATCH 22/25] ASoC: dt-bindings: meson: introduce link-name optional property

Hello Rob,

On Sun, Mar 17, 2024 at 01:45:34PM -0600, Rob Herring wrote:
> On Fri, Mar 15, 2024 at 02:21:58AM +0300, Jan Dakinevich wrote:
> > From: Dmitry Rokosov <[email protected]>
> >
> > The 'link-name' property is an optional DT property that allows for the
> > customization of the name associated with the DAI link and PCM stream.
> > This functionality mirrors the approach commonly utilized in Qualcomm
> > audio cards, providing flexibility in DAI naming conventions for
> > improved system integration and userspace experience.
> >
> > It allows userspace program to easy determine PCM stream purpose, e.g.:
> > ~ # cat /proc/asound/pcm
> > 00-00: speaker (*) : : playback 1
> > 00-01: mics (*) : : capture 1
> > 00-02: loopback (*) : : capture 1
> >
> > Signed-off-by: Dmitry Rokosov <[email protected]>
>
> This needs your S-o-b as well.

I will send this change in the separate patch series, as Neil suggested
in the cover letter reply.

--
Thank you,
Dmitry

2024-03-18 07:30:43

by Dmitry Rokosov

[permalink] [raw]
Subject: Re: [PATCH 00/25] Introduce support of audio for Amlogic A1 SoC family

Hello Neil,

On Fri, Mar 15, 2024 at 05:53:05PM +0100, Neil Armstrong wrote:
> Hi Jan!
>
> On 15/03/2024 00:21, Jan Dakinevich wrote:
> > This series includes the following:
> >
> > - new audio clock and reset controller data and adaptation for it of existing
> > code (patches 0001..0004);
> >
> > - adaptation of existing audio components for A1 Soc (patches 0005..0021);
> >
> > - handy cosmetics for dai-link naming (patches 0022..0023);
> >
> > - integration of audio devices into common trees (patch 0024);
> >
> > - audio support bring up on Amlogic ad402 reference board (patch 0025). This
> > patch is not actually checked on real hardware (because all ad402 that we had
> > were burned out). This patch is based on ad402's schematics and on experience
> > with our own hardware (which is very close to reference board);
>
> Thanks for your serie, it's nice you're working on upstreaming this feature.
>
> In my opinion it's fine to have a "big" initial serie if you're not sure
> if your changes are ok, but next time add the RFC tag so we know it's not
> a final changeset and you seek advices.
>
> Overall the code is clean and your patch order makes sense if they were meant
> to be applied by a single maintainer, but in this case it will be split
> into multiple subsystems so it's better to split them as Jerome explained
> to ease review and the maintainers process.
>
> Don't hesitate discussing with us in the #linux-amlogic IRC channel
> on Libera.Chat, the goal is to reduce the number of patch version and
> ease the review and maintainance process.
>
> Concerning the link-name property, I think it should be done afterwards
> since it's not necessary to support audio on A1, and I think it could
> be extended to other SoC boards (which would be a great feature).

If you don't mind, I will send this change in a separate patch series.
Although I don't have support for all boards in the linux-amlogic, I can
test it on some Khadas and Odroid boards on my side. I will prepare link
names for them.

> >
> > Dmitry Rokosov (2):
> > ASoC: dt-bindings: meson: introduce link-name optional property
> > ASoC: meson: implement link-name optional property in meson card utils
> >
> > Jan Dakinevich (23):
> > clk: meson: a1: restrict an amount of 'hifi_pll' params
> > clk: meson: axg: move reset controller's code to separate module
> > dt-bindings: clock: meson: add A1 audio clock and reset controller
> > bindings
> > clk: meson: a1: add the audio clock controller driver
> > ASoC: meson: codec-glue: add support for capture stream
> > ASoC: meson: g12a-toacodec: fix "Lane Select" width
> > ASoC: meson: g12a-toacodec: rework the definition of bits
> > ASoC: dt-bindings: meson: g12a-toacodec: add support for A1 SoC family
> > ASoC: meson: g12a-toacodec: add support for A1 SoC family
> > ASoC: meson: t9015: prepare to adding new platforms
> > ASoC: dt-bindings: meson: t9015: add support for A1 SoC family
> > ASoC: meson: t9015: add support for A1 SoC family
> > ASoC: dt-bindings: meson: axg-pdm: document 'sysrate' property
> > ASoC: meson: axg-pdm: introduce 'sysrate' property
> > pinctrl/meson: fix typo in PDM's pin name
> > ASoC: dt-bindings: meson: meson-axg-audio-arb: claim support of A1 SoC
> > family
> > ASoC: dt-bindings: meson: axg-fifo: claim support of A1 SoC family
> > ASoC: dt-bindings: meson: axg-pdm: claim support of A1 SoC family
> > ASoC: dt-bindings: meson: axg-sound-card: claim support of A1 SoC
> > family
> > ASoC: dt-bindings: meson: axg-tdm-formatters: claim support of A1 SoC
> > family
> > ASoC: dt-bindings: meson: axg-tdm-iface: claim support of A1 SoC
> > family
> > arm64: dts: meson: a1: add audio devices
> > arm64: dts: ad402: enable audio
> >
> > .../bindings/clock/amlogic,a1-audio-clkc.yaml | 83 +++
> > .../reset/amlogic,meson-axg-audio-arb.yaml | 10 +-
> > .../bindings/sound/amlogic,axg-fifo.yaml | 8 +
> > .../bindings/sound/amlogic,axg-pdm.yaml | 5 +
> > .../sound/amlogic,axg-sound-card.yaml | 12 +-
> > .../sound/amlogic,axg-tdm-formatters.yaml | 22 +-
> > .../bindings/sound/amlogic,axg-tdm-iface.yaml | 6 +-
> > .../bindings/sound/amlogic,g12a-toacodec.yaml | 1 +
> > .../bindings/sound/amlogic,gx-sound-card.yaml | 6 +
> > .../bindings/sound/amlogic,t9015.yaml | 4 +-
> > .../arm64/boot/dts/amlogic/meson-a1-ad402.dts | 126 ++++
> > arch/arm64/boot/dts/amlogic/meson-a1.dtsi | 471 +++++++++++++++
> > drivers/clk/meson/Kconfig | 18 +
> > drivers/clk/meson/Makefile | 2 +
> > drivers/clk/meson/a1-audio.c | 556 ++++++++++++++++++
> > drivers/clk/meson/a1-audio.h | 58 ++
> > drivers/clk/meson/a1-pll.c | 8 +-
> > drivers/clk/meson/axg-audio.c | 95 +--
> > drivers/clk/meson/meson-audio-rstc.c | 109 ++++
> > drivers/clk/meson/meson-audio-rstc.h | 12 +
> > drivers/pinctrl/meson/pinctrl-meson-a1.c | 6 +-
> > .../dt-bindings/clock/amlogic,a1-audio-clkc.h | 122 ++++
> > .../reset/amlogic,meson-a1-audio-reset.h | 29 +
> > .../dt-bindings/sound/meson-g12a-toacodec.h | 5 +
> > sound/soc/meson/axg-pdm.c | 10 +-
> > sound/soc/meson/g12a-toacodec.c | 298 ++++++++--
> > sound/soc/meson/meson-card-utils.c | 12 +-
> > sound/soc/meson/meson-codec-glue.c | 174 ++++--
> > sound/soc/meson/meson-codec-glue.h | 23 +
> > sound/soc/meson/t9015.c | 326 +++++++++-
> > 30 files changed, 2394 insertions(+), 223 deletions(-)
> > create mode 100644 Documentation/devicetree/bindings/clock/amlogic,a1-audio-clkc.yaml
> > create mode 100644 drivers/clk/meson/a1-audio.c
> > create mode 100644 drivers/clk/meson/a1-audio.h
> > create mode 100644 drivers/clk/meson/meson-audio-rstc.c
> > create mode 100644 drivers/clk/meson/meson-audio-rstc.h
> > create mode 100644 include/dt-bindings/clock/amlogic,a1-audio-clkc.h
> > create mode 100644 include/dt-bindings/reset/amlogic,meson-a1-audio-reset.h
> >
>

--
Thank you,
Dmitry

2024-03-18 07:46:20

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH 11/25] ASoC: dt-bindings: meson: t9015: add support for A1 SoC family

On 18/03/2024 00:39, Jan Dakinevich wrote:
>
>
> On 3/17/24 22:03, Rob Herring wrote:
>> On Fri, Mar 15, 2024 at 02:21:47AM +0300, Jan Dakinevich wrote:
>>> Add "amlogic,t9015-a1" compatible string.
>>
>> That's obvious from the diff. Perhaps what does 'a1' mean? What the
>> difference from the existing compatible?
>>
>
> a1 means A1 SoC family. The difference is described in next commit where
> this compatible string is added to driver. Should I duplicate here the
> explanation from that commit?
>

Bindings, not the driver, describe the hardware.

Best regards,
Krzysztof


2024-03-18 10:44:46

by Jerome Brunet

[permalink] [raw]
Subject: Re: [PATCH 09/25] ASoC: meson: g12a-toacodec: add support for A1 SoC family


On Sun 17 Mar 2024 at 18:19, Jan Dakinevich <[email protected]> wrote:

> On 3/15/24 16:33, Mark Brown wrote:
>> On Fri, Mar 15, 2024 at 02:21:45AM +0300, Jan Dakinevich wrote:
>>
>>> static const struct regmap_config g12a_toacodec_regmap_cfg = {
>>> - .reg_bits = 32,
>>> - .val_bits = 32,
>>> - .reg_stride = 4,
>>> + .reg_bits = 32,
>>> + .val_bits = 32,
>>> + .reg_stride = 4,
>>> + .max_register = TOACODEC_CTRL0,
>>> + .max_register_is_0 = true,
>>
>> If the maximum register is 0 how does the regmap have a stride?
>
> reg_stride inherited from existing code. Apparently, it was meaningless
> even before my modifications (the hardware has single register
> regardless of max_register declaration) and it should be dropped. But,
> is it okay to remove it in the same commit?

Yes it has a single register, for now. Still the stride is 4.
And assuming the mmio region passed from DT is correct, I'm not sure the
hunk is useful at all.

--
Jerome

2024-03-18 10:56:37

by Jerome Brunet

[permalink] [raw]
Subject: Re: [PATCH 13/25] ASoC: dt-bindings: meson: axg-pdm: document 'sysrate' property


On Sun 17 Mar 2024 at 18:52, Jan Dakinevich <[email protected]> wrote:

> On 3/15/24 13:22, Jerome Brunet wrote:
>>
>> On Fri 15 Mar 2024 at 11:00, Krzysztof Kozlowski <[email protected]> wrote:
>>
>>> On 15/03/2024 00:21, Jan Dakinevich wrote:
>>>> This option allow to redefine the rate of DSP system clock.
>>>
>>> And why is it suitable for bindings? Describe the hardware, not what you
>>> want to do in the driver.
>>>
>>>>
>>>> Signed-off-by: Jan Dakinevich <[email protected]>
>>>> ---
>>>> Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml | 4 ++++
>>>> 1 file changed, 4 insertions(+)
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>>>> index df21dd72fc65..d2f23a59a6b6 100644
>>>> --- a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>>>> +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>>>> @@ -40,6 +40,10 @@ properties:
>>>> resets:
>>>> maxItems: 1
>>>>
>>>> + sysrate:
>>>> + $ref: /schemas/types.yaml#/definitions/uint32
>>>> + description: redefine rate of DSP system clock
>>>
>>> No vendor prefix, so is it a generic property? Also, missing unit
>>> suffix, but more importantly I don't understand why this is a property
>>> of hardware.
>>
>> +1.
>>
>> The appropriate way to set rate of the clock before the driver take over
>> is 'assigned-rate', if you need to customize this for different
>> platform.
>>
>
> It would be great, but it doesn't work. Below, is what I want to see:
>
> assigned-clocks =
> <&clkc_audio AUD2_CLKID_PDM_SYSCLK_SEL>,
> <&clkc_audio AUD2_CLKID_PDM_SYSCLK_DIV>;
> assigned-clock-parents =
> <&clkc_pll CLKID_FCLK_DIV3>,
> <0>;
> assigned-clock-rates =
> <0>,
> <256000000>;
>
> But regardles of this declaration, PDM's driver unconditionally sets
> sysclk'rate to 250MHz and throws away everything that was configured
> before, reparents audio2_pdm_sysclk_mux to hifi_pll and changes
> hifi_pll's rate.
>
> This value 250MHz is declared here:
>
> static const struct axg_pdm_cfg axg_pdm_config = {
> .filters = &axg_default_filters,
> .sys_rate = 250000000,
> };
>
> The property 'sysrate' is intended to redefine hardcoded 'sys_rate'
> value in 'axg_pdm_config'.

What is stopping you from removing that from the driver and adding
assigned-rate to 250M is the existing platform ?

>
>> Then you don't have to deal with it in the device driver.
>>
>>>
>>> Best regards,
>>> Krzysztof
>>
>>


--
Jerome

2024-03-18 11:01:41

by Jerome Brunet

[permalink] [raw]
Subject: Re: [PATCH 12/25] ASoC: meson: t9015: add support for A1 SoC family


On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <[email protected]> wrote:

> A1's internal codec is very close to t9015. The main difference, that it
> has ADC. This commit introduces support for capturing from it.

This is mis-leading.

It does not look like the change is A1 specific but rather a extension
of the support for t9015. It also mixes several different topics like line
configuration, capture support, etc ...

Again, the t9015 changes should be a separated series from the rest, and
there should be one patch per topic.

As Mark, if something is meant to be configured based on the HW layout,
then there a good change a kcontrol is not appropriate, and this should
rather be part of the platform description, like DT.

It was also suggested here:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/sound/soc/meson/t9015.c?h=v6.8#n298

>
> Signed-off-by: Jan Dakinevich <[email protected]>
> ---
> sound/soc/meson/t9015.c | 259 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 259 insertions(+)
>
> diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c
> index 48f6767bd858..365955bfeb78 100644
> --- a/sound/soc/meson/t9015.c
> +++ b/sound/soc/meson/t9015.c
> @@ -19,16 +19,33 @@
> #define LOLP_EN 3
> #define DACR_EN 4
> #define DACL_EN 5
> +#define ADCR_EN 6
> +#define ADCL_EN 7
> +#define PGAR_ZCD_EN 8
> +#define PGAL_ZCD_EN 9
> +#define PGAR_EN 10
> +#define PGAL_EN 11
> +#define ADCR_INV 16
> +#define ADCL_INV 17
> +#define ADCR_SRC 18
> +#define ADCL_SRC 19
> #define DACR_INV 20
> #define DACL_INV 21
> #define DACR_SRC 22
> #define DACL_SRC 23
> +#define ADC_DEM_EN 26
> +#define ADC_FILTER_MODE 28
> +#define ADC_FILTER_EN 29
> #define REFP_BUF_EN BIT(12)
> #define BIAS_CURRENT_EN BIT(13)
> #define VMID_GEN_FAST BIT(14)
> #define VMID_GEN_EN BIT(15)
> #define I2S_MODE BIT(30)
> #define VOL_CTRL0 0x04
> +#define PGAR_VC 0
> +#define PGAL_VC 8
> +#define ADCR_VC 16
> +#define ADCL_VC 24
> #define GAIN_H 31
> #define GAIN_L 23
> #define VOL_CTRL1 0x08
> @@ -46,6 +63,28 @@
> #define LOLN_POL 8
> #define LOLP_POL 12
> #define POWER_CFG 0x10
> +#define LINEIN_CFG 0x14
> +#define MICBIAS_LEVEL 0
> +#define MICBIAS_EN 3
> +#define PGAR_CTVMN 8
> +#define PGAR_CTVMP 9
> +#define PGAL_CTVMN 10
> +#define PGAL_CTVMP 11
> +#define PGAR_CTVIN 12
> +#define PGAR_CTVIP 13
> +#define PGAL_CTVIN 14
> +#define PGAL_CTVIP 15
> +
> +#define PGAR_MASK (BIT(PGAR_CTVMP) | BIT(PGAR_CTVMN) | \
> + BIT(PGAR_CTVIP) | BIT(PGAR_CTVIN))
> +#define PGAR_DIFF (BIT(PGAR_CTVIP) | BIT(PGAR_CTVIN))
> +#define PGAR_POSITIVE (BIT(PGAR_CTVIP) | BIT(PGAR_CTVMN))
> +#define PGAR_NEGATIVE (BIT(PGAR_CTVIN) | BIT(PGAR_CTVMP))
> +#define PGAL_MASK (BIT(PGAL_CTVMP) | BIT(PGAL_CTVMN) | \
> + BIT(PGAL_CTVIP) | BIT(PGAL_CTVIN))
> +#define PGAL_DIFF (BIT(PGAL_CTVIP) | BIT(PGAL_CTVIN))
> +#define PGAL_POSITIVE (BIT(PGAL_CTVIP) | BIT(PGAL_CTVMN))
> +#define PGAL_NEGATIVE (BIT(PGAL_CTVIN) | BIT(PGAL_CTVMP))
>
> struct t9015 {
> struct regulator *avdd;
> @@ -103,6 +142,31 @@ static struct snd_soc_dai_driver t9015_dai = {
> .ops = &t9015_dai_ops,
> };
>
> +static struct snd_soc_dai_driver a1_t9015_dai = {
> + .name = "t9015-hifi",
> + .playback = {
> + .stream_name = "Playback",
> + .channels_min = 1,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_96000,
> + .formats = (SNDRV_PCM_FMTBIT_S8 |
> + SNDRV_PCM_FMTBIT_S16_LE |
> + SNDRV_PCM_FMTBIT_S20_LE |
> + SNDRV_PCM_FMTBIT_S24_LE),
> + },
> + .capture = {
> + .stream_name = "Capture",
> + .channels_min = 1,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_96000,
> + .formats = (SNDRV_PCM_FMTBIT_S8 |
> + SNDRV_PCM_FMTBIT_S16_LE |
> + SNDRV_PCM_FMTBIT_S20_LE |
> + SNDRV_PCM_FMTBIT_S24_LE),
> + },
> + .ops = &t9015_dai_ops,
> +};
> +
> static const DECLARE_TLV_DB_MINMAX_MUTE(dac_vol_tlv, -9525, 0);
>
> static const char * const ramp_rate_txt[] = { "Fast", "Slow" };
> @@ -179,6 +243,166 @@ static const struct snd_soc_dapm_route t9015_dapm_routes[] = {
> { "LOLP", NULL, "Left+ Driver", },
> };
>
> +static const char * const a1_right_driver_txt[] = { "None", "Right DAC",
> + "Left DAC Inverted" };
> +static const unsigned int a1_right_driver_values[] = { 0, 2, 4 };
> +
> +static const char * const a1_left_driver_txt[] = { "None", "Left DAC",
> + "Right DAC Inverted" };
> +static const unsigned int a1_left_driver_values[] = { 0, 2, 4 };
> +
> +static SOC_VALUE_ENUM_SINGLE_DECL(a1_right_driver, LINEOUT_CFG, 12, 0x7,
> + a1_right_driver_txt, a1_right_driver_values);
> +static SOC_VALUE_ENUM_SINGLE_DECL(a1_left_driver, LINEOUT_CFG, 4, 0x7,
> + a1_left_driver_txt, a1_left_driver_values);
> +
> +static const struct snd_kcontrol_new a1_right_driver_mux =
> + SOC_DAPM_ENUM("Right Driver+ Source", a1_right_driver);
> +static const struct snd_kcontrol_new a1_left_driver_mux =
> + SOC_DAPM_ENUM("Left Driver+ Source", a1_left_driver);
> +
> +static const DECLARE_TLV_DB_MINMAX_MUTE(a1_adc_vol_tlv, -29625, 0);
> +static const DECLARE_TLV_DB_MINMAX_MUTE(a1_adc_pga_vol_tlv, -1200, 0);
> +
> +static const char * const a1_adc_right_txt[] = { "Right", "Left" };
> +static SOC_ENUM_SINGLE_DECL(a1_adc_right, BLOCK_EN, ADCR_SRC, a1_adc_right_txt);
> +
> +static const char * const a1_adc_left_txt[] = { "Left", "Right" };
> +static SOC_ENUM_SINGLE_DECL(a1_adc_left, BLOCK_EN, ADCL_SRC, a1_adc_left_txt);
> +
> +static const struct snd_kcontrol_new a1_adc_right_mux =
> + SOC_DAPM_ENUM("ADC Right Source", a1_adc_right);
> +static const struct snd_kcontrol_new a1_adc_left_mux =
> + SOC_DAPM_ENUM("ADC Left Source", a1_adc_left);
> +
> +static const char * const a1_adc_filter_mode_txt[] = { "Voice", "HiFi"};
> +static SOC_ENUM_SINGLE_DECL(a1_adc_filter_mode, BLOCK_EN, ADC_FILTER_MODE,
> + a1_adc_filter_mode_txt);
> +
> +static const char * const a1_adc_mic_bias_level_txt[] = { "2.0V", "2.1V",
> + "2.3V", "2.5V", "2.8V" };
> +static const unsigned int a1_adc_mic_bias_level_values[] = { 0, 1, 2, 3, 7 };
> +static SOC_VALUE_ENUM_SINGLE_DECL(a1_adc_mic_bias_level,
> + LINEIN_CFG, MICBIAS_LEVEL, 0x7,
> + a1_adc_mic_bias_level_txt,
> + a1_adc_mic_bias_level_values);
> +
> +static const char * const a1_adc_pga_txt[] = { "None", "Differential",
> + "Positive", "Negative" };
> +static const unsigned int a1_adc_pga_right_values[] = { 0, PGAR_DIFF,
> + PGAR_POSITIVE, PGAR_NEGATIVE };
> +static const unsigned int a1_adc_pga_left_values[] = { 0, PGAL_DIFF,
> + PGAL_POSITIVE, PGAL_NEGATIVE };
> +
> +static SOC_VALUE_ENUM_SINGLE_DECL(a1_adc_pga_right, LINEIN_CFG, 0, PGAR_MASK,
> + a1_adc_pga_txt, a1_adc_pga_right_values);
> +static SOC_VALUE_ENUM_SINGLE_DECL(a1_adc_pga_left, LINEIN_CFG, 0, PGAL_MASK,
> + a1_adc_pga_txt, a1_adc_pga_left_values);
> +
> +static const struct snd_kcontrol_new a1_adc_pga_right_mux =
> + SOC_DAPM_ENUM("ADC PGA Right Source", a1_adc_pga_right);
> +static const struct snd_kcontrol_new a1_adc_pga_left_mux =
> + SOC_DAPM_ENUM("ADC PGA Left Source", a1_adc_pga_left);
> +
> +static const struct snd_kcontrol_new a1_t9015_snd_controls[] = {
> + /* Volume Controls */
> + SOC_ENUM("Playback Channel Mode", mono_enum),
> + SOC_SINGLE("Playback Switch", VOL_CTRL1, DAC_SOFT_MUTE, 1, 1),
> + SOC_DOUBLE_TLV("Playback Volume", VOL_CTRL1, DACL_VC, DACR_VC,
> + 0xff, 0, dac_vol_tlv),
> +
> + /* Ramp Controls */
> + SOC_ENUM("Ramp Rate", ramp_rate_enum),
> + SOC_SINGLE("Volume Ramp Switch", VOL_CTRL1, VC_RAMP_MODE, 1, 0),
> + SOC_SINGLE("Mute Ramp Switch", VOL_CTRL1, MUTE_MODE, 1, 0),
> + SOC_SINGLE("Unmute Ramp Switch", VOL_CTRL1, UNMUTE_MODE, 1, 0),
> +
> + /* ADC Controls */
> + SOC_DOUBLE_TLV("ADC Volume", VOL_CTRL0, ADCL_VC, ADCR_VC,
> + 0x7f, 0, a1_adc_vol_tlv),
> + SOC_SINGLE("ADC Filter Switch", BLOCK_EN, ADC_FILTER_EN, 1, 0),
> + SOC_ENUM("ADC Filter Mode", a1_adc_filter_mode),
> + SOC_SINGLE("ADC Mic Bias Switch", LINEIN_CFG, MICBIAS_EN, 1, 0),
> + SOC_ENUM("ADC Mic Bias Level", a1_adc_mic_bias_level),
> + SOC_SINGLE("ADC DEM Switch", BLOCK_EN, ADC_DEM_EN, 1, 0),
> + SOC_DOUBLE_TLV("ADC PGA Volume", VOL_CTRL0, PGAR_VC, PGAL_VC,
> + 0x1f, 0, a1_adc_pga_vol_tlv),
> + SOC_DOUBLE("ADC PGA Zero Cross-detection Switch", BLOCK_EN,
> + PGAL_ZCD_EN, PGAR_ZCD_EN, 1, 0),
> +};
> +
> +static const struct snd_soc_dapm_widget a1_t9015_dapm_widgets[] = {
> + SND_SOC_DAPM_AIF_IN("Right IN", NULL, 0, SND_SOC_NOPM, 0, 0),
> + SND_SOC_DAPM_AIF_IN("Left IN", NULL, 0, SND_SOC_NOPM, 0, 0),
> + SND_SOC_DAPM_MUX("Right DAC Sel", SND_SOC_NOPM, 0, 0,
> + &t9015_right_dac_mux),
> + SND_SOC_DAPM_MUX("Left DAC Sel", SND_SOC_NOPM, 0, 0,
> + &t9015_left_dac_mux),
> + SND_SOC_DAPM_DAC("Right DAC", NULL, BLOCK_EN, DACR_EN, 0),
> + SND_SOC_DAPM_DAC("Left DAC", NULL, BLOCK_EN, DACL_EN, 0),
> + SND_SOC_DAPM_MUX("Right+ Driver Sel", SND_SOC_NOPM, 0, 0,
> + &a1_right_driver_mux),
> + SND_SOC_DAPM_MUX("Left+ Driver Sel", SND_SOC_NOPM, 0, 0,
> + &a1_left_driver_mux),
> + SND_SOC_DAPM_OUT_DRV("Right+ Driver", BLOCK_EN, LORP_EN, 0, NULL, 0),
> + SND_SOC_DAPM_OUT_DRV("Left+ Driver", BLOCK_EN, LOLP_EN, 0, NULL, 0),
> + SND_SOC_DAPM_OUTPUT("LORP"),
> + SND_SOC_DAPM_OUTPUT("LOLP"),
> +
> + SND_SOC_DAPM_INPUT("ADC IN Right"),
> + SND_SOC_DAPM_INPUT("ADC IN Left"),
> + SND_SOC_DAPM_MUX("ADC PGA Right Sel", SND_SOC_NOPM, 0, 0,
> + &a1_adc_pga_right_mux),
> + SND_SOC_DAPM_MUX("ADC PGA Left Sel", SND_SOC_NOPM, 0, 0,
> + &a1_adc_pga_left_mux),
> + SND_SOC_DAPM_PGA("ADC PGA Right", BLOCK_EN, PGAR_EN, 0, NULL, 0),
> + SND_SOC_DAPM_PGA("ADC PGA Left", BLOCK_EN, PGAL_EN, 0, NULL, 0),
> + SND_SOC_DAPM_ADC("ADC Right", NULL, BLOCK_EN, ADCR_EN, 0),
> + SND_SOC_DAPM_ADC("ADC Left", NULL, BLOCK_EN, ADCL_EN, 0),
> + SND_SOC_DAPM_MUX("ADC Right Sel", SND_SOC_NOPM, 0, 0, &a1_adc_right_mux),
> + SND_SOC_DAPM_MUX("ADC Left Sel", SND_SOC_NOPM, 0, 0, &a1_adc_left_mux),
> + SND_SOC_DAPM_AIF_OUT("ADC OUT Right", NULL, 0, SND_SOC_NOPM, 0, 0),
> + SND_SOC_DAPM_AIF_OUT("ADC OUT Left", NULL, 0, SND_SOC_NOPM, 0, 0),
> +};
> +
> +static const struct snd_soc_dapm_route a1_t9015_dapm_routes[] = {
> + { "Right IN", NULL, "Playback" },
> + { "Left IN", NULL, "Playback" },
> + { "Right DAC Sel", "Right", "Right IN" },
> + { "Right DAC Sel", "Left", "Left IN" },
> + { "Left DAC Sel", "Right", "Right IN" },
> + { "Left DAC Sel", "Left", "Left IN" },
> + { "Right DAC", NULL, "Right DAC Sel" },
> + { "Left DAC", NULL, "Left DAC Sel" },
> + { "Right+ Driver Sel", "Right DAC", "Right DAC" },
> + { "Right+ Driver Sel", "Left DAC Inverted", "Right DAC" },
> + { "Left+ Driver Sel", "Left DAC", "Left DAC" },
> + { "Left+ Driver Sel", "Right DAC Inverted", "Left DAC" },
> + { "Right+ Driver", NULL, "Right+ Driver Sel" },
> + { "Left+ Driver", NULL, "Left+ Driver Sel" },
> + { "LORP", NULL, "Right+ Driver", },
> + { "LOLP", NULL, "Left+ Driver", },
> +
> + { "ADC PGA Right Sel", "Differential", "ADC IN Right" },
> + { "ADC PGA Right Sel", "Positive", "ADC IN Right" },
> + { "ADC PGA Right Sel", "Negative", "ADC IN Right" },
> + { "ADC PGA Left Sel", "Differential", "ADC IN Left" },
> + { "ADC PGA Left Sel", "Positive", "ADC IN Left" },
> + { "ADC PGA Left Sel", "Negative", "ADC IN Left" },
> + { "ADC PGA Right", NULL, "ADC PGA Right Sel" },
> + { "ADC PGA Left", NULL, "ADC PGA Left Sel" },
> + { "ADC Right", NULL, "ADC PGA Right" },
> + { "ADC Left", NULL, "ADC PGA Left" },
> + { "ADC Right Sel", "Right", "ADC Right" },
> + { "ADC Right Sel", "Left", "ADC Left" },
> + { "ADC Left Sel", "Right", "ADC Right" },
> + { "ADC Left Sel", "Left", "ADC Left" },
> + { "ADC OUT Right", NULL, "ADC Right Sel" },
> + { "ADC OUT Left", NULL, "ADC Left Sel" },
> + { "Capture", NULL, "ADC OUT Right" },
> + { "Capture", NULL, "ADC OUT Left" },
> +};
> +
> static int t9015_set_bias_level(struct snd_soc_component *component,
> enum snd_soc_bias_level level)
> {
> @@ -241,6 +465,18 @@ static int t9015_component_probe(struct snd_soc_component *component)
> return 0;
> }
>
> +static int a1_t9015_component_probe(struct snd_soc_component *component)
> +{
> + /*
> + * This configuration was stealed from original Amlogic's driver to
> + * reproduce the behavior of the driver more accurately. However, it is
> + * not known for certain what it actually affects.
> + */
> + snd_soc_component_write(component, POWER_CFG, 0x00010000);
> +
> + return 0;
> +}
> +
> static const struct snd_soc_component_driver t9015_codec_driver = {
> .probe = t9015_component_probe,
> .set_bias_level = t9015_set_bias_level,
> @@ -254,6 +490,19 @@ static const struct snd_soc_component_driver t9015_codec_driver = {
> .endianness = 1,
> };
>
> +static const struct snd_soc_component_driver a1_t9015_codec_driver = {
> + .probe = a1_t9015_component_probe,
> + .set_bias_level = t9015_set_bias_level,
> + .controls = a1_t9015_snd_controls,
> + .num_controls = ARRAY_SIZE(a1_t9015_snd_controls),
> + .dapm_widgets = a1_t9015_dapm_widgets,
> + .num_dapm_widgets = ARRAY_SIZE(a1_t9015_dapm_widgets),
> + .dapm_routes = a1_t9015_dapm_routes,
> + .num_dapm_routes = ARRAY_SIZE(a1_t9015_dapm_routes),
> + .suspend_bias_off = 1,
> + .endianness = 1,
> +};
> +
> static int t9015_probe(struct platform_device *pdev)
> {
> struct device *dev = &pdev->dev;
> @@ -315,11 +564,21 @@ static const struct t9015_match_data t9015_match_data = {
> .max_register = POWER_CFG,
> };
>
> +static const struct t9015_match_data a1_t9015_match_data = {
> + .component_drv = &a1_t9015_codec_driver,
> + .dai_drv = &a1_t9015_dai,
> + .max_register = LINEIN_CFG,
> +};
> +
> static const struct of_device_id t9015_ids[] __maybe_unused = {
> {
> .compatible = "amlogic,t9015",
> .data = &t9015_match_data,
> },
> + {
> + .compatible = "amlogic,t9015-a1",
> + .data = &a1_t9015_match_data,
> + },
> { }
> };
> MODULE_DEVICE_TABLE(of, t9015_ids);


--
Jerome

2024-03-18 12:37:35

by Jerome Brunet

[permalink] [raw]
Subject: Re: [PATCH 13/25] ASoC: dt-bindings: meson: axg-pdm: document 'sysrate' property


On Mon 18 Mar 2024 at 11:55, Jerome Brunet <[email protected]> wrote:

> On Sun 17 Mar 2024 at 18:52, Jan Dakinevich <[email protected]> wrote:
>
>> On 3/15/24 13:22, Jerome Brunet wrote:
>>>
>>> On Fri 15 Mar 2024 at 11:00, Krzysztof Kozlowski <[email protected]> wrote:
>>>
>>>> On 15/03/2024 00:21, Jan Dakinevich wrote:
>>>>> This option allow to redefine the rate of DSP system clock.
>>>>
>>>> And why is it suitable for bindings? Describe the hardware, not what you
>>>> want to do in the driver.
>>>>
>>>>>
>>>>> Signed-off-by: Jan Dakinevich <[email protected]>
>>>>> ---
>>>>> Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml | 4 ++++
>>>>> 1 file changed, 4 insertions(+)
>>>>>
>>>>> diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>>>>> index df21dd72fc65..d2f23a59a6b6 100644
>>>>> --- a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>>>>> +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>>>>> @@ -40,6 +40,10 @@ properties:
>>>>> resets:
>>>>> maxItems: 1
>>>>>
>>>>> + sysrate:
>>>>> + $ref: /schemas/types.yaml#/definitions/uint32
>>>>> + description: redefine rate of DSP system clock
>>>>
>>>> No vendor prefix, so is it a generic property? Also, missing unit
>>>> suffix, but more importantly I don't understand why this is a property
>>>> of hardware.
>>>
>>> +1.
>>>
>>> The appropriate way to set rate of the clock before the driver take over
>>> is 'assigned-rate', if you need to customize this for different
>>> platform.
>>>
>>
>> It would be great, but it doesn't work. Below, is what I want to see:
>>
>> assigned-clocks =
>> <&clkc_audio AUD2_CLKID_PDM_SYSCLK_SEL>,
>> <&clkc_audio AUD2_CLKID_PDM_SYSCLK_DIV>;
>> assigned-clock-parents =
>> <&clkc_pll CLKID_FCLK_DIV3>,
>> <0>;
>> assigned-clock-rates =
>> <0>,
>> <256000000>;
>>
>> But regardles of this declaration, PDM's driver unconditionally sets
>> sysclk'rate to 250MHz and throws away everything that was configured
>> before, reparents audio2_pdm_sysclk_mux to hifi_pll and changes
>> hifi_pll's rate.
>>
>> This value 250MHz is declared here:
>>
>> static const struct axg_pdm_cfg axg_pdm_config = {
>> .filters = &axg_default_filters,
>> .sys_rate = 250000000,
>> };
>>
>> The property 'sysrate' is intended to redefine hardcoded 'sys_rate'
>> value in 'axg_pdm_config'.
>
> What is stopping you from removing that from the driver and adding
> assigned-rate to 250M is the existing platform ?

.. Also, considering how PDM does work, I'm not sure I get the point of
the doing all this to go from 250MHz to 256Mhz.

PDM value is sampled at ~75% of the half period. That clock basically
feeds a counter and the threshold is adjusted based on the clock rate.

So there is no need to change the rate. Changing it is only necessary
when the captured audio rate is extremely slow (<8kHz) and the counter
may overflow. The driver already adjust this automatically.

So changing the input rate from 250MHz to 256MHz should not make any
difference.

>
>>
>>> Then you don't have to deal with it in the device driver.
>>>
>>>>
>>>> Best regards,
>>>> Krzysztof
>>>
>>>


--
Jerome

2024-03-18 13:31:08

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH 09/25] ASoC: meson: g12a-toacodec: add support for A1 SoC family

On Sun, Mar 17, 2024 at 06:19:36PM +0300, Jan Dakinevich wrote:
> On 3/15/24 16:33, Mark Brown wrote:

> > If the maximum register is 0 how does the regmap have a stride?

> reg_stride inherited from existing code. Apparently, it was meaningless
> even before my modifications (the hardware has single register
> regardless of max_register declaration) and it should be dropped. But,
> is it okay to remove it in the same commit?

Sure.


Attachments:
(No filename) (448.00 B)
signature.asc (499.00 B)
Download all attachments

2024-03-18 13:48:48

by Mark Brown

[permalink] [raw]
Subject: Re: [PATCH 12/25] ASoC: meson: t9015: add support for A1 SoC family

On Sun, Mar 17, 2024 at 07:27:14PM +0300, Jan Dakinevich wrote:

> Both mic bias and ADC's input mode depends on schematics and should be
> configurable. What is the better way to give access to these parameters?
> Device tree?

Yes.

> >> + SOC_SINGLE("ADC Mic Bias Switch", LINEIN_CFG, MICBIAS_EN, 1, 0),
> >> + SOC_ENUM("ADC Mic Bias Level", a1_adc_mic_bias_level),

> > Why would micbias be user controlled rather than a DAPM widget as
> > normal?

> Yes, I could use SND_SOC_DAPM_SUPPLY, but it supports only raw values,
> and doesn't supports enums. Here, I want to use enum to restrict
> possible values, because only these values mentioned in the
> documentation that I have.

A supply is an on/off switch not an enum. Users should not be selecting
values at all.


Attachments:
(No filename) (794.00 B)
signature.asc (499.00 B)
Download all attachments

2024-03-18 22:44:27

by Jan Dakinevich

[permalink] [raw]
Subject: Re: [PATCH 12/25] ASoC: meson: t9015: add support for A1 SoC family



On 3/18/24 16:48, Mark Brown wrote:
> On Sun, Mar 17, 2024 at 07:27:14PM +0300, Jan Dakinevich wrote:
>
>> Both mic bias and ADC's input mode depends on schematics and should be
>> configurable. What is the better way to give access to these parameters?
>> Device tree?
>
> Yes.
>
>>>> + SOC_SINGLE("ADC Mic Bias Switch", LINEIN_CFG, MICBIAS_EN, 1, 0),
>>>> + SOC_ENUM("ADC Mic Bias Level", a1_adc_mic_bias_level),
>
>>> Why would micbias be user controlled rather than a DAPM widget as
>>> normal?
>
>> Yes, I could use SND_SOC_DAPM_SUPPLY, but it supports only raw values,
>> and doesn't supports enums. Here, I want to use enum to restrict
>> possible values, because only these values mentioned in the
>> documentation that I have.
>
> A supply is an on/off switch not an enum. Users should not be selecting
> values at all.

Ok. For me it is great if I am free to move these kcontrols to device tree.

--
Best regards
Jan Dakinevich

2024-03-19 00:18:31

by Jan Dakinevich

[permalink] [raw]
Subject: Re: [PATCH 12/25] ASoC: meson: t9015: add support for A1 SoC family



On 3/18/24 13:46, Jerome Brunet wrote:
>
> On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <[email protected]> wrote:
>
>> A1's internal codec is very close to t9015. The main difference, that it
>> has ADC. This commit introduces support for capturing from it.
>
> This is mis-leading.
>
> It does not look like the change is A1 specific but rather a extension
> of the support for t9015. It also mixes several different topics like line
> configuration, capture support, etc ...
>
First, it is not only extentsion. Some bits are changed comparing to
existing t9015, so new compatible string is still required.

Second, I don't know anything about about ADC in t9015 on other SoCs and
even don't sure that it exist there (may be I am inattentive, but I'm
unable to find audio input pin on sm1/g12a's pinout).

> Again, the t9015 changes should be a separated series from the rest, and
> there should be one patch per topic.
>
> As Mark, if something is meant to be configured based on the HW layout,
> then there a good change a kcontrol is not appropriate, and this should
> rather be part of the platform description, like DT.
>
> It was also suggested here:
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/sound/soc/meson/t9015.c?h=v6.8#n298
>

Ok. By the way, on a1 LINEOUT_CFG would have another value.

>>
>> Signed-off-by: Jan Dakinevich <[email protected]>
>> ---
>> sound/soc/meson/t9015.c | 259 ++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 259 insertions(+)
>>
>> diff --git a/sound/soc/meson/t9015.c b/sound/soc/meson/t9015.c
>> index 48f6767bd858..365955bfeb78 100644
>> --- a/sound/soc/meson/t9015.c
>> +++ b/sound/soc/meson/t9015.c
>> @@ -19,16 +19,33 @@
>> #define LOLP_EN 3
>> #define DACR_EN 4
>> #define DACL_EN 5
>> +#define ADCR_EN 6
>> +#define ADCL_EN 7
>> +#define PGAR_ZCD_EN 8
>> +#define PGAL_ZCD_EN 9
>> +#define PGAR_EN 10
>> +#define PGAL_EN 11
>> +#define ADCR_INV 16
>> +#define ADCL_INV 17
>> +#define ADCR_SRC 18
>> +#define ADCL_SRC 19
>> #define DACR_INV 20
>> #define DACL_INV 21
>> #define DACR_SRC 22
>> #define DACL_SRC 23
>> +#define ADC_DEM_EN 26
>> +#define ADC_FILTER_MODE 28
>> +#define ADC_FILTER_EN 29
>> #define REFP_BUF_EN BIT(12)
>> #define BIAS_CURRENT_EN BIT(13)
>> #define VMID_GEN_FAST BIT(14)
>> #define VMID_GEN_EN BIT(15)
>> #define I2S_MODE BIT(30)
>> #define VOL_CTRL0 0x04
>> +#define PGAR_VC 0
>> +#define PGAL_VC 8
>> +#define ADCR_VC 16
>> +#define ADCL_VC 24
>> #define GAIN_H 31
>> #define GAIN_L 23
>> #define VOL_CTRL1 0x08
>> @@ -46,6 +63,28 @@
>> #define LOLN_POL 8
>> #define LOLP_POL 12
>> #define POWER_CFG 0x10
>> +#define LINEIN_CFG 0x14
>> +#define MICBIAS_LEVEL 0
>> +#define MICBIAS_EN 3
>> +#define PGAR_CTVMN 8
>> +#define PGAR_CTVMP 9
>> +#define PGAL_CTVMN 10
>> +#define PGAL_CTVMP 11
>> +#define PGAR_CTVIN 12
>> +#define PGAR_CTVIP 13
>> +#define PGAL_CTVIN 14
>> +#define PGAL_CTVIP 15
>> +
>> +#define PGAR_MASK (BIT(PGAR_CTVMP) | BIT(PGAR_CTVMN) | \
>> + BIT(PGAR_CTVIP) | BIT(PGAR_CTVIN))
>> +#define PGAR_DIFF (BIT(PGAR_CTVIP) | BIT(PGAR_CTVIN))
>> +#define PGAR_POSITIVE (BIT(PGAR_CTVIP) | BIT(PGAR_CTVMN))
>> +#define PGAR_NEGATIVE (BIT(PGAR_CTVIN) | BIT(PGAR_CTVMP))
>> +#define PGAL_MASK (BIT(PGAL_CTVMP) | BIT(PGAL_CTVMN) | \
>> + BIT(PGAL_CTVIP) | BIT(PGAL_CTVIN))
>> +#define PGAL_DIFF (BIT(PGAL_CTVIP) | BIT(PGAL_CTVIN))
>> +#define PGAL_POSITIVE (BIT(PGAL_CTVIP) | BIT(PGAL_CTVMN))
>> +#define PGAL_NEGATIVE (BIT(PGAL_CTVIN) | BIT(PGAL_CTVMP))
>>
>> struct t9015 {
>> struct regulator *avdd;
>> @@ -103,6 +142,31 @@ static struct snd_soc_dai_driver t9015_dai = {
>> .ops = &t9015_dai_ops,
>> };
>>
>> +static struct snd_soc_dai_driver a1_t9015_dai = {
>> + .name = "t9015-hifi",
>> + .playback = {
>> + .stream_name = "Playback",
>> + .channels_min = 1,
>> + .channels_max = 2,
>> + .rates = SNDRV_PCM_RATE_8000_96000,
>> + .formats = (SNDRV_PCM_FMTBIT_S8 |
>> + SNDRV_PCM_FMTBIT_S16_LE |
>> + SNDRV_PCM_FMTBIT_S20_LE |
>> + SNDRV_PCM_FMTBIT_S24_LE),
>> + },
>> + .capture = {
>> + .stream_name = "Capture",
>> + .channels_min = 1,
>> + .channels_max = 2,
>> + .rates = SNDRV_PCM_RATE_8000_96000,
>> + .formats = (SNDRV_PCM_FMTBIT_S8 |
>> + SNDRV_PCM_FMTBIT_S16_LE |
>> + SNDRV_PCM_FMTBIT_S20_LE |
>> + SNDRV_PCM_FMTBIT_S24_LE),
>> + },
>> + .ops = &t9015_dai_ops,
>> +};
>> +
>> static const DECLARE_TLV_DB_MINMAX_MUTE(dac_vol_tlv, -9525, 0);
>>
>> static const char * const ramp_rate_txt[] = { "Fast", "Slow" };
>> @@ -179,6 +243,166 @@ static const struct snd_soc_dapm_route t9015_dapm_routes[] = {
>> { "LOLP", NULL, "Left+ Driver", },
>> };
>>
>> +static const char * const a1_right_driver_txt[] = { "None", "Right DAC",
>> + "Left DAC Inverted" };
>> +static const unsigned int a1_right_driver_values[] = { 0, 2, 4 };
>> +
>> +static const char * const a1_left_driver_txt[] = { "None", "Left DAC",
>> + "Right DAC Inverted" };
>> +static const unsigned int a1_left_driver_values[] = { 0, 2, 4 };
>> +
>> +static SOC_VALUE_ENUM_SINGLE_DECL(a1_right_driver, LINEOUT_CFG, 12, 0x7,
>> + a1_right_driver_txt, a1_right_driver_values);
>> +static SOC_VALUE_ENUM_SINGLE_DECL(a1_left_driver, LINEOUT_CFG, 4, 0x7,
>> + a1_left_driver_txt, a1_left_driver_values);
>> +
>> +static const struct snd_kcontrol_new a1_right_driver_mux =
>> + SOC_DAPM_ENUM("Right Driver+ Source", a1_right_driver);
>> +static const struct snd_kcontrol_new a1_left_driver_mux =
>> + SOC_DAPM_ENUM("Left Driver+ Source", a1_left_driver);
>> +
>> +static const DECLARE_TLV_DB_MINMAX_MUTE(a1_adc_vol_tlv, -29625, 0);
>> +static const DECLARE_TLV_DB_MINMAX_MUTE(a1_adc_pga_vol_tlv, -1200, 0);
>> +
>> +static const char * const a1_adc_right_txt[] = { "Right", "Left" };
>> +static SOC_ENUM_SINGLE_DECL(a1_adc_right, BLOCK_EN, ADCR_SRC, a1_adc_right_txt);
>> +
>> +static const char * const a1_adc_left_txt[] = { "Left", "Right" };
>> +static SOC_ENUM_SINGLE_DECL(a1_adc_left, BLOCK_EN, ADCL_SRC, a1_adc_left_txt);
>> +
>> +static const struct snd_kcontrol_new a1_adc_right_mux =
>> + SOC_DAPM_ENUM("ADC Right Source", a1_adc_right);
>> +static const struct snd_kcontrol_new a1_adc_left_mux =
>> + SOC_DAPM_ENUM("ADC Left Source", a1_adc_left);
>> +
>> +static const char * const a1_adc_filter_mode_txt[] = { "Voice", "HiFi"};
>> +static SOC_ENUM_SINGLE_DECL(a1_adc_filter_mode, BLOCK_EN, ADC_FILTER_MODE,
>> + a1_adc_filter_mode_txt);
>> +
>> +static const char * const a1_adc_mic_bias_level_txt[] = { "2.0V", "2.1V",
>> + "2.3V", "2.5V", "2.8V" };
>> +static const unsigned int a1_adc_mic_bias_level_values[] = { 0, 1, 2, 3, 7 };
>> +static SOC_VALUE_ENUM_SINGLE_DECL(a1_adc_mic_bias_level,
>> + LINEIN_CFG, MICBIAS_LEVEL, 0x7,
>> + a1_adc_mic_bias_level_txt,
>> + a1_adc_mic_bias_level_values);
>> +
>> +static const char * const a1_adc_pga_txt[] = { "None", "Differential",
>> + "Positive", "Negative" };
>> +static const unsigned int a1_adc_pga_right_values[] = { 0, PGAR_DIFF,
>> + PGAR_POSITIVE, PGAR_NEGATIVE };
>> +static const unsigned int a1_adc_pga_left_values[] = { 0, PGAL_DIFF,
>> + PGAL_POSITIVE, PGAL_NEGATIVE };
>> +
>> +static SOC_VALUE_ENUM_SINGLE_DECL(a1_adc_pga_right, LINEIN_CFG, 0, PGAR_MASK,
>> + a1_adc_pga_txt, a1_adc_pga_right_values);
>> +static SOC_VALUE_ENUM_SINGLE_DECL(a1_adc_pga_left, LINEIN_CFG, 0, PGAL_MASK,
>> + a1_adc_pga_txt, a1_adc_pga_left_values);
>> +
>> +static const struct snd_kcontrol_new a1_adc_pga_right_mux =
>> + SOC_DAPM_ENUM("ADC PGA Right Source", a1_adc_pga_right);
>> +static const struct snd_kcontrol_new a1_adc_pga_left_mux =
>> + SOC_DAPM_ENUM("ADC PGA Left Source", a1_adc_pga_left);
>> +
>> +static const struct snd_kcontrol_new a1_t9015_snd_controls[] = {
>> + /* Volume Controls */
>> + SOC_ENUM("Playback Channel Mode", mono_enum),
>> + SOC_SINGLE("Playback Switch", VOL_CTRL1, DAC_SOFT_MUTE, 1, 1),
>> + SOC_DOUBLE_TLV("Playback Volume", VOL_CTRL1, DACL_VC, DACR_VC,
>> + 0xff, 0, dac_vol_tlv),
>> +
>> + /* Ramp Controls */
>> + SOC_ENUM("Ramp Rate", ramp_rate_enum),
>> + SOC_SINGLE("Volume Ramp Switch", VOL_CTRL1, VC_RAMP_MODE, 1, 0),
>> + SOC_SINGLE("Mute Ramp Switch", VOL_CTRL1, MUTE_MODE, 1, 0),
>> + SOC_SINGLE("Unmute Ramp Switch", VOL_CTRL1, UNMUTE_MODE, 1, 0),
>> +
>> + /* ADC Controls */
>> + SOC_DOUBLE_TLV("ADC Volume", VOL_CTRL0, ADCL_VC, ADCR_VC,
>> + 0x7f, 0, a1_adc_vol_tlv),
>> + SOC_SINGLE("ADC Filter Switch", BLOCK_EN, ADC_FILTER_EN, 1, 0),
>> + SOC_ENUM("ADC Filter Mode", a1_adc_filter_mode),
>> + SOC_SINGLE("ADC Mic Bias Switch", LINEIN_CFG, MICBIAS_EN, 1, 0),
>> + SOC_ENUM("ADC Mic Bias Level", a1_adc_mic_bias_level),
>> + SOC_SINGLE("ADC DEM Switch", BLOCK_EN, ADC_DEM_EN, 1, 0),
>> + SOC_DOUBLE_TLV("ADC PGA Volume", VOL_CTRL0, PGAR_VC, PGAL_VC,
>> + 0x1f, 0, a1_adc_pga_vol_tlv),
>> + SOC_DOUBLE("ADC PGA Zero Cross-detection Switch", BLOCK_EN,
>> + PGAL_ZCD_EN, PGAR_ZCD_EN, 1, 0),
>> +};
>> +
>> +static const struct snd_soc_dapm_widget a1_t9015_dapm_widgets[] = {
>> + SND_SOC_DAPM_AIF_IN("Right IN", NULL, 0, SND_SOC_NOPM, 0, 0),
>> + SND_SOC_DAPM_AIF_IN("Left IN", NULL, 0, SND_SOC_NOPM, 0, 0),
>> + SND_SOC_DAPM_MUX("Right DAC Sel", SND_SOC_NOPM, 0, 0,
>> + &t9015_right_dac_mux),
>> + SND_SOC_DAPM_MUX("Left DAC Sel", SND_SOC_NOPM, 0, 0,
>> + &t9015_left_dac_mux),
>> + SND_SOC_DAPM_DAC("Right DAC", NULL, BLOCK_EN, DACR_EN, 0),
>> + SND_SOC_DAPM_DAC("Left DAC", NULL, BLOCK_EN, DACL_EN, 0),
>> + SND_SOC_DAPM_MUX("Right+ Driver Sel", SND_SOC_NOPM, 0, 0,
>> + &a1_right_driver_mux),
>> + SND_SOC_DAPM_MUX("Left+ Driver Sel", SND_SOC_NOPM, 0, 0,
>> + &a1_left_driver_mux),
>> + SND_SOC_DAPM_OUT_DRV("Right+ Driver", BLOCK_EN, LORP_EN, 0, NULL, 0),
>> + SND_SOC_DAPM_OUT_DRV("Left+ Driver", BLOCK_EN, LOLP_EN, 0, NULL, 0),
>> + SND_SOC_DAPM_OUTPUT("LORP"),
>> + SND_SOC_DAPM_OUTPUT("LOLP"),
>> +
>> + SND_SOC_DAPM_INPUT("ADC IN Right"),
>> + SND_SOC_DAPM_INPUT("ADC IN Left"),
>> + SND_SOC_DAPM_MUX("ADC PGA Right Sel", SND_SOC_NOPM, 0, 0,
>> + &a1_adc_pga_right_mux),
>> + SND_SOC_DAPM_MUX("ADC PGA Left Sel", SND_SOC_NOPM, 0, 0,
>> + &a1_adc_pga_left_mux),
>> + SND_SOC_DAPM_PGA("ADC PGA Right", BLOCK_EN, PGAR_EN, 0, NULL, 0),
>> + SND_SOC_DAPM_PGA("ADC PGA Left", BLOCK_EN, PGAL_EN, 0, NULL, 0),
>> + SND_SOC_DAPM_ADC("ADC Right", NULL, BLOCK_EN, ADCR_EN, 0),
>> + SND_SOC_DAPM_ADC("ADC Left", NULL, BLOCK_EN, ADCL_EN, 0),
>> + SND_SOC_DAPM_MUX("ADC Right Sel", SND_SOC_NOPM, 0, 0, &a1_adc_right_mux),
>> + SND_SOC_DAPM_MUX("ADC Left Sel", SND_SOC_NOPM, 0, 0, &a1_adc_left_mux),
>> + SND_SOC_DAPM_AIF_OUT("ADC OUT Right", NULL, 0, SND_SOC_NOPM, 0, 0),
>> + SND_SOC_DAPM_AIF_OUT("ADC OUT Left", NULL, 0, SND_SOC_NOPM, 0, 0),
>> +};
>> +
>> +static const struct snd_soc_dapm_route a1_t9015_dapm_routes[] = {
>> + { "Right IN", NULL, "Playback" },
>> + { "Left IN", NULL, "Playback" },
>> + { "Right DAC Sel", "Right", "Right IN" },
>> + { "Right DAC Sel", "Left", "Left IN" },
>> + { "Left DAC Sel", "Right", "Right IN" },
>> + { "Left DAC Sel", "Left", "Left IN" },
>> + { "Right DAC", NULL, "Right DAC Sel" },
>> + { "Left DAC", NULL, "Left DAC Sel" },
>> + { "Right+ Driver Sel", "Right DAC", "Right DAC" },
>> + { "Right+ Driver Sel", "Left DAC Inverted", "Right DAC" },
>> + { "Left+ Driver Sel", "Left DAC", "Left DAC" },
>> + { "Left+ Driver Sel", "Right DAC Inverted", "Left DAC" },
>> + { "Right+ Driver", NULL, "Right+ Driver Sel" },
>> + { "Left+ Driver", NULL, "Left+ Driver Sel" },
>> + { "LORP", NULL, "Right+ Driver", },
>> + { "LOLP", NULL, "Left+ Driver", },
>> +
>> + { "ADC PGA Right Sel", "Differential", "ADC IN Right" },
>> + { "ADC PGA Right Sel", "Positive", "ADC IN Right" },
>> + { "ADC PGA Right Sel", "Negative", "ADC IN Right" },
>> + { "ADC PGA Left Sel", "Differential", "ADC IN Left" },
>> + { "ADC PGA Left Sel", "Positive", "ADC IN Left" },
>> + { "ADC PGA Left Sel", "Negative", "ADC IN Left" },
>> + { "ADC PGA Right", NULL, "ADC PGA Right Sel" },
>> + { "ADC PGA Left", NULL, "ADC PGA Left Sel" },
>> + { "ADC Right", NULL, "ADC PGA Right" },
>> + { "ADC Left", NULL, "ADC PGA Left" },
>> + { "ADC Right Sel", "Right", "ADC Right" },
>> + { "ADC Right Sel", "Left", "ADC Left" },
>> + { "ADC Left Sel", "Right", "ADC Right" },
>> + { "ADC Left Sel", "Left", "ADC Left" },
>> + { "ADC OUT Right", NULL, "ADC Right Sel" },
>> + { "ADC OUT Left", NULL, "ADC Left Sel" },
>> + { "Capture", NULL, "ADC OUT Right" },
>> + { "Capture", NULL, "ADC OUT Left" },
>> +};
>> +
>> static int t9015_set_bias_level(struct snd_soc_component *component,
>> enum snd_soc_bias_level level)
>> {
>> @@ -241,6 +465,18 @@ static int t9015_component_probe(struct snd_soc_component *component)
>> return 0;
>> }
>>
>> +static int a1_t9015_component_probe(struct snd_soc_component *component)
>> +{
>> + /*
>> + * This configuration was stealed from original Amlogic's driver to
>> + * reproduce the behavior of the driver more accurately. However, it is
>> + * not known for certain what it actually affects.
>> + */
>> + snd_soc_component_write(component, POWER_CFG, 0x00010000);
>> +
>> + return 0;
>> +}
>> +
>> static const struct snd_soc_component_driver t9015_codec_driver = {
>> .probe = t9015_component_probe,
>> .set_bias_level = t9015_set_bias_level,
>> @@ -254,6 +490,19 @@ static const struct snd_soc_component_driver t9015_codec_driver = {
>> .endianness = 1,
>> };
>>
>> +static const struct snd_soc_component_driver a1_t9015_codec_driver = {
>> + .probe = a1_t9015_component_probe,
>> + .set_bias_level = t9015_set_bias_level,
>> + .controls = a1_t9015_snd_controls,
>> + .num_controls = ARRAY_SIZE(a1_t9015_snd_controls),
>> + .dapm_widgets = a1_t9015_dapm_widgets,
>> + .num_dapm_widgets = ARRAY_SIZE(a1_t9015_dapm_widgets),
>> + .dapm_routes = a1_t9015_dapm_routes,
>> + .num_dapm_routes = ARRAY_SIZE(a1_t9015_dapm_routes),
>> + .suspend_bias_off = 1,
>> + .endianness = 1,
>> +};
>> +
>> static int t9015_probe(struct platform_device *pdev)
>> {
>> struct device *dev = &pdev->dev;
>> @@ -315,11 +564,21 @@ static const struct t9015_match_data t9015_match_data = {
>> .max_register = POWER_CFG,
>> };
>>
>> +static const struct t9015_match_data a1_t9015_match_data = {
>> + .component_drv = &a1_t9015_codec_driver,
>> + .dai_drv = &a1_t9015_dai,
>> + .max_register = LINEIN_CFG,
>> +};
>> +
>> static const struct of_device_id t9015_ids[] __maybe_unused = {
>> {
>> .compatible = "amlogic,t9015",
>> .data = &t9015_match_data,
>> },
>> + {
>> + .compatible = "amlogic,t9015-a1",
>> + .data = &a1_t9015_match_data,
>> + },
>> { }
>> };
>> MODULE_DEVICE_TABLE(of, t9015_ids);
>
>

--
Best regards
Jan Dakinevich

2024-03-19 00:31:53

by Jan Dakinevich

[permalink] [raw]
Subject: Re: [PATCH 13/25] ASoC: dt-bindings: meson: axg-pdm: document 'sysrate' property



On 3/18/24 15:19, Jerome Brunet wrote:
>
> On Mon 18 Mar 2024 at 11:55, Jerome Brunet <[email protected]> wrote:
>
>> On Sun 17 Mar 2024 at 18:52, Jan Dakinevich <[email protected]> wrote:
>>
>>> On 3/15/24 13:22, Jerome Brunet wrote:
>>>>
>>>> On Fri 15 Mar 2024 at 11:00, Krzysztof Kozlowski <[email protected]> wrote:
>>>>
>>>>> On 15/03/2024 00:21, Jan Dakinevich wrote:
>>>>>> This option allow to redefine the rate of DSP system clock.
>>>>>
>>>>> And why is it suitable for bindings? Describe the hardware, not what you
>>>>> want to do in the driver.
>>>>>
>>>>>>
>>>>>> Signed-off-by: Jan Dakinevich <[email protected]>
>>>>>> ---
>>>>>> Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml | 4 ++++
>>>>>> 1 file changed, 4 insertions(+)
>>>>>>
>>>>>> diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>>>>>> index df21dd72fc65..d2f23a59a6b6 100644
>>>>>> --- a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>>>>>> +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>>>>>> @@ -40,6 +40,10 @@ properties:
>>>>>> resets:
>>>>>> maxItems: 1
>>>>>>
>>>>>> + sysrate:
>>>>>> + $ref: /schemas/types.yaml#/definitions/uint32
>>>>>> + description: redefine rate of DSP system clock
>>>>>
>>>>> No vendor prefix, so is it a generic property? Also, missing unit
>>>>> suffix, but more importantly I don't understand why this is a property
>>>>> of hardware.
>>>>
>>>> +1.
>>>>
>>>> The appropriate way to set rate of the clock before the driver take over
>>>> is 'assigned-rate', if you need to customize this for different
>>>> platform.
>>>>
>>>
>>> It would be great, but it doesn't work. Below, is what I want to see:
>>>
>>> assigned-clocks =
>>> <&clkc_audio AUD2_CLKID_PDM_SYSCLK_SEL>,
>>> <&clkc_audio AUD2_CLKID_PDM_SYSCLK_DIV>;
>>> assigned-clock-parents =
>>> <&clkc_pll CLKID_FCLK_DIV3>,
>>> <0>;
>>> assigned-clock-rates =
>>> <0>,
>>> <256000000>;
>>>
>>> But regardles of this declaration, PDM's driver unconditionally sets
>>> sysclk'rate to 250MHz and throws away everything that was configured
>>> before, reparents audio2_pdm_sysclk_mux to hifi_pll and changes
>>> hifi_pll's rate.
>>>
>>> This value 250MHz is declared here:
>>>
>>> static const struct axg_pdm_cfg axg_pdm_config = {
>>> .filters = &axg_default_filters,
>>> .sys_rate = 250000000,
>>> };
>>>
>>> The property 'sysrate' is intended to redefine hardcoded 'sys_rate'
>>> value in 'axg_pdm_config'.
>>
>> What is stopping you from removing that from the driver and adding
>> assigned-rate to 250M is the existing platform ?
>
> ... Also, considering how PDM does work, I'm not sure I get the point of
> the doing all this to go from 250MHz to 256Mhz.
>

The point is to use fclk_div3 clock as source for PDM's sysclock and
keep hiff_pll clock free for TDM. Because, I can get 256MHz from any
hifi_pll and fclk_div3, but only hifi_pll is able to provide accurate
48kHz (after several divider).

> PDM value is sampled at ~75% of the half period. That clock basically
> feeds a counter and the threshold is adjusted based on the clock rate.
>
> So there is no need to change the rate. Changing it is only necessary
> when the captured audio rate is extremely slow (<8kHz) and the counter
> may overflow. The driver already adjust this automatically.
>
> So changing the input rate from 250MHz to 256MHz should not make any
> difference.
>

Thank you for the explanation.

>>
>>>
>>>> Then you don't have to deal with it in the device driver.
>>>>
>>>>>
>>>>> Best regards,
>>>>> Krzysztof
>>>>
>>>>
>
>

--
Best regards
Jan Dakinevich

2024-03-19 00:36:21

by Jan Dakinevich

[permalink] [raw]
Subject: Re: [PATCH 13/25] ASoC: dt-bindings: meson: axg-pdm: document 'sysrate' property



On 3/18/24 13:55, Jerome Brunet wrote:
>
> On Sun 17 Mar 2024 at 18:52, Jan Dakinevich <[email protected]> wrote:
>
>> On 3/15/24 13:22, Jerome Brunet wrote:
>>>
>>> On Fri 15 Mar 2024 at 11:00, Krzysztof Kozlowski <[email protected]> wrote:
>>>
>>>> On 15/03/2024 00:21, Jan Dakinevich wrote:
>>>>> This option allow to redefine the rate of DSP system clock.
>>>>
>>>> And why is it suitable for bindings? Describe the hardware, not what you
>>>> want to do in the driver.
>>>>
>>>>>
>>>>> Signed-off-by: Jan Dakinevich <[email protected]>
>>>>> ---
>>>>> Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml | 4 ++++
>>>>> 1 file changed, 4 insertions(+)
>>>>>
>>>>> diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>>>>> index df21dd72fc65..d2f23a59a6b6 100644
>>>>> --- a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>>>>> +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>>>>> @@ -40,6 +40,10 @@ properties:
>>>>> resets:
>>>>> maxItems: 1
>>>>>
>>>>> + sysrate:
>>>>> + $ref: /schemas/types.yaml#/definitions/uint32
>>>>> + description: redefine rate of DSP system clock
>>>>
>>>> No vendor prefix, so is it a generic property? Also, missing unit
>>>> suffix, but more importantly I don't understand why this is a property
>>>> of hardware.
>>>
>>> +1.
>>>
>>> The appropriate way to set rate of the clock before the driver take over
>>> is 'assigned-rate', if you need to customize this for different
>>> platform.
>>>
>>
>> It would be great, but it doesn't work. Below, is what I want to see:
>>
>> assigned-clocks =
>> <&clkc_audio AUD2_CLKID_PDM_SYSCLK_SEL>,
>> <&clkc_audio AUD2_CLKID_PDM_SYSCLK_DIV>;
>> assigned-clock-parents =
>> <&clkc_pll CLKID_FCLK_DIV3>,
>> <0>;
>> assigned-clock-rates =
>> <0>,
>> <256000000>;
>>
>> But regardles of this declaration, PDM's driver unconditionally sets
>> sysclk'rate to 250MHz and throws away everything that was configured
>> before, reparents audio2_pdm_sysclk_mux to hifi_pll and changes
>> hifi_pll's rate.
>>
>> This value 250MHz is declared here:
>>
>> static const struct axg_pdm_cfg axg_pdm_config = {
>> .filters = &axg_default_filters,
>> .sys_rate = 250000000,
>> };
>>
>> The property 'sysrate' is intended to redefine hardcoded 'sys_rate'
>> value in 'axg_pdm_config'.
>
> What is stopping you from removing that from the driver and adding
> assigned-rate to 250M is the existing platform ?
>

Ok, in next version I will try to remove this unconditional setting of
rate that spoils my clock hierarchy.

>>
>>> Then you don't have to deal with it in the device driver.
>>>
>>>>
>>>> Best regards,
>>>> Krzysztof
>>>
>>>
>
>

--
Best regards
Jan Dakinevich

2024-03-19 01:48:49

by Jan Dakinevich

[permalink] [raw]
Subject: Re: [PATCH 04/25] clk: meson: a1: add the audio clock controller driver

Let's start from the end:

> No - Looks to me you just have two clock controllers you are trying
force into one.

> Again, this shows 2 devices. The one related to your 'map0' should
request AUD2_CLKID_AUDIOTOP as input and enable it right away.

Most of fishy workarounds that you commented is caused the fact the mmio
of this clock controller is divided into two parts. Compare it with
axg-audio driver, things that was part of contigous memory region (like
pdm) here are moved to second region. Is this enough to make a guess
that these are two devices?

Concerning AUD2_CLKID_AUDIOTOP clock, as it turned out, it must be
enabled before enabling of clocks from second region too. That is
AUD2_CLKID_AUDIOTOP clock feeds both parts of this clock controller.


On 3/15/24 12:20, Jerome Brunet wrote:
>
> On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <[email protected]> wrote:
>
>> This controller provides clocks and reset functionality for audio
>> peripherals on Amlogic A1 SoC family.
>>
>> The driver is almost identical to 'axg-audio', however it would be better
>> to keep it separate due to following reasons:
>>
>> - significant amount of bits has another definition. I will bring there
>> a mess of new defines with A1_ suffixes.
>>
>> - registers of this controller are located in two separate regions. It
>> will give a lot of complications for 'axg-audio' to support this.
>>
>> Signed-off-by: Jan Dakinevich <[email protected]>
>> ---
>> drivers/clk/meson/Kconfig | 13 +
>> drivers/clk/meson/Makefile | 1 +
>> drivers/clk/meson/a1-audio.c | 556 +++++++++++++++++++++++++++++++++++
>> drivers/clk/meson/a1-audio.h | 58 ++++
>> 4 files changed, 628 insertions(+)
>> create mode 100644 drivers/clk/meson/a1-audio.c
>> create mode 100644 drivers/clk/meson/a1-audio.h
>>
>> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
>> index d6a2fa5f7e88..80c4a18c83d2 100644
>> --- a/drivers/clk/meson/Kconfig
>> +++ b/drivers/clk/meson/Kconfig
>> @@ -133,6 +133,19 @@ config COMMON_CLK_A1_PERIPHERALS
>> device, A1 SoC Family. Say Y if you want A1 Peripherals clock
>> controller to work.
>>
>> +config COMMON_CLK_A1_AUDIO
>> + tristate "Amlogic A1 SoC Audio clock controller support"
>> + depends on ARM64
>> + select COMMON_CLK_MESON_REGMAP
>> + select COMMON_CLK_MESON_CLKC_UTILS
>> + select COMMON_CLK_MESON_PHASE
>> + select COMMON_CLK_MESON_SCLK_DIV
>> + select COMMON_CLK_MESON_AUDIO_RSTC
>> + help
>> + Support for the Audio clock controller on Amlogic A113L based
>> + device, A1 SoC Family. Say Y if you want A1 Audio clock controller
>> + to work.
>> +
>> config COMMON_CLK_G12A
>> tristate "G12 and SM1 SoC clock controllers support"
>> depends on ARM64
>> diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
>> index 88d94921a4dc..4968fc7ad555 100644
>> --- a/drivers/clk/meson/Makefile
>> +++ b/drivers/clk/meson/Makefile
>> @@ -20,6 +20,7 @@ obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
>> obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
>> obj-$(CONFIG_COMMON_CLK_A1_PLL) += a1-pll.o
>> obj-$(CONFIG_COMMON_CLK_A1_PERIPHERALS) += a1-peripherals.o
>> +obj-$(CONFIG_COMMON_CLK_A1_AUDIO) += a1-audio.o
>> obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
>> obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o
>> obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o
>> diff --git a/drivers/clk/meson/a1-audio.c b/drivers/clk/meson/a1-audio.c
>> new file mode 100644
>> index 000000000000..6039116c93ba
>> --- /dev/null
>> +++ b/drivers/clk/meson/a1-audio.c
>> @@ -0,0 +1,556 @@
>> +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
>> +/*
>> + * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
>> + *
>> + * Author: Jan Dakinevich <[email protected]>
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/clk-provider.h>
>> +#include <linux/init.h>
>> +#include <linux/of_device.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regmap.h>
>> +#include <linux/reset.h>
>> +#include <linux/reset-controller.h>
>> +#include <linux/slab.h>
>> +
>> +#include "meson-clkc-utils.h"
>> +#include "meson-audio-rstc.h"
>> +#include "clk-regmap.h"
>> +#include "clk-phase.h"
>> +#include "sclk-div.h"
>> +#include "a1-audio.h"
>> +
>> +#define AUDIO_PDATA(_name) \
>> + ((const struct clk_parent_data[]) { { .hw = &(_name).hw } })
>
> Not a fan - yet another level of macro.
>
>> +
>> +#define AUDIO_MUX(_name, _reg, _mask, _shift, _pdata) \
>> +static struct clk_regmap _name = { \
>> + .map = AUDIO_REG_MAP(_reg), \
>> + .data = &(struct clk_regmap_mux_data){ \
>> + .offset = AUDIO_REG_OFFSET(_reg), \
>> + .mask = (_mask), \
>> + .shift = (_shift), \
>> + }, \
>> + .hw.init = &(struct clk_init_data) { \
>> + .name = #_name, \
>> + .ops = &clk_regmap_mux_ops, \
>> + .parent_data = (_pdata), \
>> + .num_parents = ARRAY_SIZE(_pdata), \
>> + .flags = CLK_SET_RATE_PARENT, \
>> + }, \
>> +}
>> +
>> +#define AUDIO_DIV(_name, _reg, _shift, _width, _pdata) \
>> +static struct clk_regmap _name = { \
>> + .map = AUDIO_REG_MAP(_reg), \
>> + .data = &(struct clk_regmap_div_data){ \
>> + .offset = AUDIO_REG_OFFSET(_reg), \
>> + .shift = (_shift), \
>> + .width = (_width), \
>> + }, \
>> + .hw.init = &(struct clk_init_data) { \
>> + .name = #_name, \
>> + .ops = &clk_regmap_divider_ops, \
>> + .parent_data = (_pdata), \
>> + .num_parents = 1, \
>> + .flags = CLK_SET_RATE_PARENT, \
>> + }, \
>> +}
>> +
>> +#define AUDIO_GATE(_name, _reg, _bit, _pdata) \
>> +static struct clk_regmap _name = { \
>> + .map = AUDIO_REG_MAP(_reg), \
>> + .data = &(struct clk_regmap_gate_data){ \
>> + .offset = AUDIO_REG_OFFSET(_reg), \
>> + .bit_idx = (_bit), \
>> + }, \
>> + .hw.init = &(struct clk_init_data) { \
>> + .name = #_name, \
>> + .ops = &clk_regmap_gate_ops, \
>> + .parent_data = (_pdata), \
>> + .num_parents = 1, \
>> + .flags = CLK_SET_RATE_PARENT, \
>> + }, \
>> +}
>> +
>> +#define AUDIO_SCLK_DIV(_name, _reg, _div_shift, _div_width, \
>> + _hi_shift, _hi_width, _pdata, _set_rate_parent) \
>> +static struct clk_regmap _name = { \
>> + .map = AUDIO_REG_MAP(_reg), \
>> + .data = &(struct meson_sclk_div_data) { \
>> + .div = { \
>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>> + .shift = (_div_shift), \
>> + .width = (_div_width), \
>> + }, \
>> + .hi = { \
>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>> + .shift = (_hi_shift), \
>> + .width = (_hi_width), \
>> + }, \
>> + }, \
>> + .hw.init = &(struct clk_init_data) { \
>> + .name = #_name, \
>> + .ops = &meson_sclk_div_ops, \
>> + .parent_data = (_pdata), \
>> + .num_parents = 1, \
>> + .flags = (_set_rate_parent) ? CLK_SET_RATE_PARENT : 0, \
>
> Does not help readeability. Just pass the flag as axg-audio does.
>
>> + }, \
>> +}
>> +
>> +#define AUDIO_TRIPHASE(_name, _reg, _width, _shift0, _shift1, _shift2, \
>> + _pdata) \
>> +static struct clk_regmap _name = { \
>> + .map = AUDIO_REG_MAP(_reg), \
>> + .data = &(struct meson_clk_triphase_data) { \
>> + .ph0 = { \
>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>> + .shift = (_shift0), \
>> + .width = (_width), \
>> + }, \
>> + .ph1 = { \
>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>> + .shift = (_shift1), \
>> + .width = (_width), \
>> + }, \
>> + .ph2 = { \
>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>> + .shift = (_shift2), \
>> + .width = (_width), \
>> + }, \
>> + }, \
>> + .hw.init = &(struct clk_init_data) { \
>> + .name = #_name, \
>> + .ops = &meson_clk_triphase_ops, \
>> + .parent_data = (_pdata), \
>> + .num_parents = 1, \
>> + .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \
>> + }, \
>> +}
>> +
>> +#define AUDIO_SCLK_WS(_name, _reg, _width, _shift_ph, _shift_ws, \
>> + _pdata) \
>> +static struct clk_regmap _name = { \
>> + .map = AUDIO_REG_MAP(_reg), \
>> + .data = &(struct meson_sclk_ws_inv_data) { \
>> + .ph = { \
>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>> + .shift = (_shift_ph), \
>> + .width = (_width), \
>> + }, \
>> + .ws = { \
>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>> + .shift = (_shift_ws), \
>> + .width = (_width), \
>> + }, \
>> + }, \
>> + .hw.init = &(struct clk_init_data) { \
>> + .name = #_name, \
>> + .ops = &meson_sclk_ws_inv_ops, \
>> + .parent_data = (_pdata), \
>> + .num_parents = 1, \
>> + .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \
>> + }, \
>> +}
>
> All the above does essentially the same things as the macro of
> axg-audio, to some minor differences. Yet it is another set to maintain.
>

Except one thing... Here I keep memory identifier to which this clock
belongs:

.map = AUDIO_REG_MAP(_reg),

It is workaround, but ->map the only common field in clk_regmap that
could be used for this purpose.


> I'd much prefer if you put the axg-audio macro in a header a re-used
> those. There would a single set to maintain. You may then specialize the
> included in the driver C file, to avoid redundant parameters
>
> Rework axg-audio to use clk_parent_data if you must, but not in the same
> series please.
>
>> +
>> +static const struct clk_parent_data a1_pclk_pdata[] = {
>> + { .fw_name = "pclk", },
>> +};
>> +
>> +AUDIO_GATE(audio_ddr_arb, AUDIO_CLK_GATE_EN0, 0, a1_pclk_pdata);
>> +AUDIO_GATE(audio_tdmin_a, AUDIO_CLK_GATE_EN0, 1, a1_pclk_pdata);
>> +AUDIO_GATE(audio_tdmin_b, AUDIO_CLK_GATE_EN0, 2, a1_pclk_pdata);
>> +AUDIO_GATE(audio_tdmin_lb, AUDIO_CLK_GATE_EN0, 3, a1_pclk_pdata);
>> +AUDIO_GATE(audio_loopback, AUDIO_CLK_GATE_EN0, 4, a1_pclk_pdata);
>> +AUDIO_GATE(audio_tdmout_a, AUDIO_CLK_GATE_EN0, 5, a1_pclk_pdata);
>> +AUDIO_GATE(audio_tdmout_b, AUDIO_CLK_GATE_EN0, 6, a1_pclk_pdata);
>> +AUDIO_GATE(audio_frddr_a, AUDIO_CLK_GATE_EN0, 7, a1_pclk_pdata);
>> +AUDIO_GATE(audio_frddr_b, AUDIO_CLK_GATE_EN0, 8, a1_pclk_pdata);
>> +AUDIO_GATE(audio_toddr_a, AUDIO_CLK_GATE_EN0, 9, a1_pclk_pdata);
>> +AUDIO_GATE(audio_toddr_b, AUDIO_CLK_GATE_EN0, 10, a1_pclk_pdata);
>> +AUDIO_GATE(audio_spdifin, AUDIO_CLK_GATE_EN0, 11, a1_pclk_pdata);
>> +AUDIO_GATE(audio_resample, AUDIO_CLK_GATE_EN0, 12, a1_pclk_pdata);
>> +AUDIO_GATE(audio_eqdrc, AUDIO_CLK_GATE_EN0, 13, a1_pclk_pdata);
>> +AUDIO_GATE(audio_audiolocker, AUDIO_CLK_GATE_EN0, 14, a1_pclk_pdata);
> This is what I mean by redundant parameter ^
>

Yep. I could define something like AUDIO_PCLK_GATE().

>> +
>> +AUDIO_GATE(audio2_ddr_arb, AUDIO2_CLK_GATE_EN0, 0, a1_pclk_pdata);
>> +AUDIO_GATE(audio2_pdm, AUDIO2_CLK_GATE_EN0, 1, a1_pclk_pdata);
>> +AUDIO_GATE(audio2_tdmin_vad, AUDIO2_CLK_GATE_EN0, 2, a1_pclk_pdata);
>> +AUDIO_GATE(audio2_toddr_vad, AUDIO2_CLK_GATE_EN0, 3, a1_pclk_pdata);
>> +AUDIO_GATE(audio2_vad, AUDIO2_CLK_GATE_EN0, 4, a1_pclk_pdata);
>> +AUDIO_GATE(audio2_audiotop, AUDIO2_CLK_GATE_EN0, 7, a1_pclk_pdata);
>> +
>> +static const struct clk_parent_data a1_mst_pdata[] = {
>> + { .fw_name = "dds_in" },
>> + { .fw_name = "fclk_div2" },
>> + { .fw_name = "fclk_div3" },
>> + { .fw_name = "hifi_pll" },
>> + { .fw_name = "xtal" },
>> +};
>> +
>> +#define AUDIO_MST_MCLK(_name, _reg) \
>> + AUDIO_MUX(_name##_mux, (_reg), 0x7, 24, a1_mst_pdata); \
>> + AUDIO_DIV(_name##_div, (_reg), 0, 16, \
>> + AUDIO_PDATA(_name##_mux)); \
>> + AUDIO_GATE(_name, (_reg), 31, AUDIO_PDATA(_name##_div))
>> +
>> +AUDIO_MST_MCLK(audio_mst_a_mclk, AUDIO_MCLK_A_CTRL);
>> +AUDIO_MST_MCLK(audio_mst_b_mclk, AUDIO_MCLK_B_CTRL);
>> +AUDIO_MST_MCLK(audio_mst_c_mclk, AUDIO_MCLK_C_CTRL);
>> +AUDIO_MST_MCLK(audio_mst_d_mclk, AUDIO_MCLK_D_CTRL);
>> +AUDIO_MST_MCLK(audio_spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL);
>> +AUDIO_MST_MCLK(audio_eqdrc_clk, AUDIO_CLK_EQDRC_CTRL);
>> +
>> +AUDIO_MUX(audio_resample_clk_mux, AUDIO_CLK_RESAMPLE_CTRL, 0xf, 24,
>> + a1_mst_pdata);
>> +AUDIO_DIV(audio_resample_clk_div, AUDIO_CLK_RESAMPLE_CTRL, 0, 8,
>> + AUDIO_PDATA(audio_resample_clk_mux));
>> +AUDIO_GATE(audio_resample_clk, AUDIO_CLK_RESAMPLE_CTRL, 31,
>> + AUDIO_PDATA(audio_resample_clk_div));
>> +
>> +AUDIO_MUX(audio_locker_in_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 8,
>> + a1_mst_pdata);
>> +AUDIO_DIV(audio_locker_in_clk_div, AUDIO_CLK_LOCKER_CTRL, 0, 8,
>> + AUDIO_PDATA(audio_locker_in_clk_mux));
>> +AUDIO_GATE(audio_locker_in_clk, AUDIO_CLK_LOCKER_CTRL, 15,
>> + AUDIO_PDATA(audio_locker_in_clk_div));
>> +
>> +AUDIO_MUX(audio_locker_out_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 24,
>> + a1_mst_pdata);
>> +AUDIO_DIV(audio_locker_out_clk_div, AUDIO_CLK_LOCKER_CTRL, 16, 8,
>> + AUDIO_PDATA(audio_locker_out_clk_mux));
>> +AUDIO_GATE(audio_locker_out_clk, AUDIO_CLK_LOCKER_CTRL, 31,
>> + AUDIO_PDATA(audio_locker_out_clk_div));
>> +
>> +AUDIO_MST_MCLK(audio2_vad_mclk, AUDIO2_MCLK_VAD_CTRL);
>> +AUDIO_MST_MCLK(audio2_vad_clk, AUDIO2_CLK_VAD_CTRL);
>> +AUDIO_MST_MCLK(audio2_pdm_dclk, AUDIO2_CLK_PDMIN_CTRL0);
>> +AUDIO_MST_MCLK(audio2_pdm_sysclk, AUDIO2_CLK_PDMIN_CTRL1);
>> +
>> +#define AUDIO_MST_SCLK(_name, _reg0, _reg1, _pdata) \
>> + AUDIO_GATE(_name##_pre_en, (_reg0), 31, (_pdata)); \
>> + AUDIO_SCLK_DIV(_name##_div, (_reg0), 20, 10, 0, 0, \
>> + AUDIO_PDATA(_name##_pre_en), true); \
>> + AUDIO_GATE(_name##_post_en, (_reg0), 30, \
>> + AUDIO_PDATA(_name##_div)); \
>> + AUDIO_TRIPHASE(_name, (_reg1), 1, 0, 2, 4, \
>> + AUDIO_PDATA(_name##_post_en))
>> +
>
> Again, I'm not a fan of this many levels of macro. I can live with it
> but certainly don't want the burden of reviewing and maintaining for
> clock driver. AXG / G12 and A1 are obviously closely related, so make it common.
>
>> +#define AUDIO_MST_LRCLK(_name, _reg0, _reg1, _pdata) \
>> + AUDIO_SCLK_DIV(_name##_div, (_reg0), 0, 10, 10, 10, \
>> + (_pdata), false); \
>> + AUDIO_TRIPHASE(_name, (_reg1), 1, 1, 3, 5, \
>> + AUDIO_PDATA(_name##_div))
>> +
>> +AUDIO_MST_SCLK(audio_mst_a_sclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1,
>> + AUDIO_PDATA(audio_mst_a_mclk));
>> +AUDIO_MST_SCLK(audio_mst_b_sclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1,
>> + AUDIO_PDATA(audio_mst_b_mclk));
>> +AUDIO_MST_SCLK(audio_mst_c_sclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1,
>> + AUDIO_PDATA(audio_mst_c_mclk));
>> +AUDIO_MST_SCLK(audio_mst_d_sclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1,
>> + AUDIO_PDATA(audio_mst_d_mclk));
>> +
>> +AUDIO_MST_LRCLK(audio_mst_a_lrclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1,
>> + AUDIO_PDATA(audio_mst_a_sclk_post_en));
>> +AUDIO_MST_LRCLK(audio_mst_b_lrclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1,
>> + AUDIO_PDATA(audio_mst_b_sclk_post_en));
>> +AUDIO_MST_LRCLK(audio_mst_c_lrclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1,
>> + AUDIO_PDATA(audio_mst_c_sclk_post_en));
>> +AUDIO_MST_LRCLK(audio_mst_d_lrclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1,
>> + AUDIO_PDATA(audio_mst_d_sclk_post_en));
>> +
>> +static const struct clk_parent_data a1_mst_sclk_pdata[] = {
>> + { .hw = &audio_mst_a_sclk.hw },
>> + { .hw = &audio_mst_b_sclk.hw },
>> + { .hw = &audio_mst_c_sclk.hw },
>> + { .hw = &audio_mst_d_sclk.hw },
>> + { .fw_name = "slv_sclk0" },
>> + { .fw_name = "slv_sclk1" },
>> + { .fw_name = "slv_sclk2" },
>> + { .fw_name = "slv_sclk3" },
>> + { .fw_name = "slv_sclk4" },
>> + { .fw_name = "slv_sclk5" },
>> + { .fw_name = "slv_sclk6" },
>> + { .fw_name = "slv_sclk7" },
>> + { .fw_name = "slv_sclk8" },
>> + { .fw_name = "slv_sclk9" },
>> +};
>> +
>> +static const struct clk_parent_data a1_mst_lrclk_pdata[] = {
>> + { .hw = &audio_mst_a_lrclk.hw },
>> + { .hw = &audio_mst_b_lrclk.hw },
>> + { .hw = &audio_mst_c_lrclk.hw },
>> + { .hw = &audio_mst_d_lrclk.hw },
>> + { .fw_name = "slv_lrclk0" },
>> + { .fw_name = "slv_lrclk1" },
>> + { .fw_name = "slv_lrclk2" },
>> + { .fw_name = "slv_lrclk3" },
>> + { .fw_name = "slv_lrclk4" },
>> + { .fw_name = "slv_lrclk5" },
>> + { .fw_name = "slv_lrclk6" },
>> + { .fw_name = "slv_lrclk7" },
>> + { .fw_name = "slv_lrclk8" },
>> + { .fw_name = "slv_lrclk9" },
>> +};
>> +
>> +#define AUDIO_TDM_SCLK(_name, _reg) \
>> + AUDIO_MUX(_name##_mux, (_reg), 0xf, 24, a1_mst_sclk_pdata); \
>> + AUDIO_GATE(_name##_pre_en, (_reg), 31, \
>> + AUDIO_PDATA(_name##_mux)); \
>> + AUDIO_GATE(_name##_post_en, (_reg), 30, \
>> + AUDIO_PDATA(_name##_pre_en)); \
>> + AUDIO_SCLK_WS(_name, (_reg), 1, 29, 28, \
>> + AUDIO_PDATA(_name##_post_en))
>> +
>> +#define AUDIO_TDM_LRCLK(_name, _reg) \
>> + AUDIO_MUX(_name, (_reg), 0xf, 20, a1_mst_lrclk_pdata)
>> +
>> +AUDIO_TDM_SCLK(audio_tdmin_a_sclk, AUDIO_CLK_TDMIN_A_CTRL);
>> +AUDIO_TDM_SCLK(audio_tdmin_b_sclk, AUDIO_CLK_TDMIN_B_CTRL);
>> +AUDIO_TDM_SCLK(audio_tdmin_lb_sclk, AUDIO_CLK_TDMIN_LB_CTRL);
>> +AUDIO_TDM_SCLK(audio_tdmout_a_sclk, AUDIO_CLK_TDMOUT_A_CTRL);
>> +AUDIO_TDM_SCLK(audio_tdmout_b_sclk, AUDIO_CLK_TDMOUT_B_CTRL);
>> +
>> +AUDIO_TDM_LRCLK(audio_tdmin_a_lrclk, AUDIO_CLK_TDMIN_A_CTRL);
>> +AUDIO_TDM_LRCLK(audio_tdmin_b_lrclk, AUDIO_CLK_TDMIN_B_CTRL);
>> +AUDIO_TDM_LRCLK(audio_tdmin_lb_lrclk, AUDIO_CLK_TDMIN_LB_CTRL);
>> +AUDIO_TDM_LRCLK(audio_tdmout_a_lrclk, AUDIO_CLK_TDMOUT_A_CTRL);
>> +AUDIO_TDM_LRCLK(audio_tdmout_b_lrclk, AUDIO_CLK_TDMOUT_B_CTRL);
>> +
>> +static struct clk_hw *a1_audio_hw_clks[] = {
>> + [AUD_CLKID_DDR_ARB] = &audio_ddr_arb.hw,
>> + [AUD_CLKID_TDMIN_A] = &audio_tdmin_a.hw,
>> + [AUD_CLKID_TDMIN_B] = &audio_tdmin_b.hw,
>> + [AUD_CLKID_TDMIN_LB] = &audio_tdmin_lb.hw,
>> + [AUD_CLKID_LOOPBACK] = &audio_loopback.hw,
>> + [AUD_CLKID_TDMOUT_A] = &audio_tdmout_a.hw,
>> + [AUD_CLKID_TDMOUT_B] = &audio_tdmout_b.hw,
>> + [AUD_CLKID_FRDDR_A] = &audio_frddr_a.hw,
>> + [AUD_CLKID_FRDDR_B] = &audio_frddr_b.hw,
>> + [AUD_CLKID_TODDR_A] = &audio_toddr_a.hw,
>> + [AUD_CLKID_TODDR_B] = &audio_toddr_b.hw,
>> + [AUD_CLKID_SPDIFIN] = &audio_spdifin.hw,
>> + [AUD_CLKID_RESAMPLE] = &audio_resample.hw,
>> + [AUD_CLKID_EQDRC] = &audio_eqdrc.hw,
>> + [AUD_CLKID_LOCKER] = &audio_audiolocker.hw,
>> + [AUD_CLKID_MST_A_MCLK_SEL] = &audio_mst_a_mclk_mux.hw,
>> + [AUD_CLKID_MST_A_MCLK_DIV] = &audio_mst_a_mclk_div.hw,
>> + [AUD_CLKID_MST_A_MCLK] = &audio_mst_a_mclk.hw,
>> + [AUD_CLKID_MST_B_MCLK_SEL] = &audio_mst_b_mclk_mux.hw,
>> + [AUD_CLKID_MST_B_MCLK_DIV] = &audio_mst_b_mclk_div.hw,
>> + [AUD_CLKID_MST_B_MCLK] = &audio_mst_b_mclk.hw,
>> + [AUD_CLKID_MST_C_MCLK_SEL] = &audio_mst_c_mclk_mux.hw,
>> + [AUD_CLKID_MST_C_MCLK_DIV] = &audio_mst_c_mclk_div.hw,
>> + [AUD_CLKID_MST_C_MCLK] = &audio_mst_c_mclk.hw,
>> + [AUD_CLKID_MST_D_MCLK_SEL] = &audio_mst_d_mclk_mux.hw,
>> + [AUD_CLKID_MST_D_MCLK_DIV] = &audio_mst_d_mclk_div.hw,
>> + [AUD_CLKID_MST_D_MCLK] = &audio_mst_d_mclk.hw,
>> + [AUD_CLKID_RESAMPLE_CLK_SEL] = &audio_resample_clk_mux.hw,
>> + [AUD_CLKID_RESAMPLE_CLK_DIV] = &audio_resample_clk_div.hw,
>> + [AUD_CLKID_RESAMPLE_CLK] = &audio_resample_clk.hw,
>> + [AUD_CLKID_LOCKER_IN_CLK_SEL] = &audio_locker_in_clk_mux.hw,
>> + [AUD_CLKID_LOCKER_IN_CLK_DIV] = &audio_locker_in_clk_div.hw,
>> + [AUD_CLKID_LOCKER_IN_CLK] = &audio_locker_in_clk.hw,
>> + [AUD_CLKID_LOCKER_OUT_CLK_SEL] = &audio_locker_out_clk_mux.hw,
>> + [AUD_CLKID_LOCKER_OUT_CLK_DIV] = &audio_locker_out_clk_div.hw,
>> + [AUD_CLKID_LOCKER_OUT_CLK] = &audio_locker_out_clk.hw,
>> + [AUD_CLKID_SPDIFIN_CLK_SEL] = &audio_spdifin_clk_mux.hw,
>> + [AUD_CLKID_SPDIFIN_CLK_DIV] = &audio_spdifin_clk_div.hw,
>> + [AUD_CLKID_SPDIFIN_CLK] = &audio_spdifin_clk.hw,
>> + [AUD_CLKID_EQDRC_CLK_SEL] = &audio_eqdrc_clk_mux.hw,
>> + [AUD_CLKID_EQDRC_CLK_DIV] = &audio_eqdrc_clk_div.hw,
>> + [AUD_CLKID_EQDRC_CLK] = &audio_eqdrc_clk.hw,
>> + [AUD_CLKID_MST_A_SCLK_PRE_EN] = &audio_mst_a_sclk_pre_en.hw,
>> + [AUD_CLKID_MST_A_SCLK_DIV] = &audio_mst_a_sclk_div.hw,
>> + [AUD_CLKID_MST_A_SCLK_POST_EN] = &audio_mst_a_sclk_post_en.hw,
>> + [AUD_CLKID_MST_A_SCLK] = &audio_mst_a_sclk.hw,
>> + [AUD_CLKID_MST_B_SCLK_PRE_EN] = &audio_mst_b_sclk_pre_en.hw,
>> + [AUD_CLKID_MST_B_SCLK_DIV] = &audio_mst_b_sclk_div.hw,
>> + [AUD_CLKID_MST_B_SCLK_POST_EN] = &audio_mst_b_sclk_post_en.hw,
>> + [AUD_CLKID_MST_B_SCLK] = &audio_mst_b_sclk.hw,
>> + [AUD_CLKID_MST_C_SCLK_PRE_EN] = &audio_mst_c_sclk_pre_en.hw,
>> + [AUD_CLKID_MST_C_SCLK_DIV] = &audio_mst_c_sclk_div.hw,
>> + [AUD_CLKID_MST_C_SCLK_POST_EN] = &audio_mst_c_sclk_post_en.hw,
>> + [AUD_CLKID_MST_C_SCLK] = &audio_mst_c_sclk.hw,
>> + [AUD_CLKID_MST_D_SCLK_PRE_EN] = &audio_mst_d_sclk_pre_en.hw,
>> + [AUD_CLKID_MST_D_SCLK_DIV] = &audio_mst_d_sclk_div.hw,
>> + [AUD_CLKID_MST_D_SCLK_POST_EN] = &audio_mst_d_sclk_post_en.hw,
>> + [AUD_CLKID_MST_D_SCLK] = &audio_mst_d_sclk.hw,
>> + [AUD_CLKID_MST_A_LRCLK_DIV] = &audio_mst_a_lrclk_div.hw,
>> + [AUD_CLKID_MST_A_LRCLK] = &audio_mst_a_lrclk.hw,
>> + [AUD_CLKID_MST_B_LRCLK_DIV] = &audio_mst_b_lrclk_div.hw,
>> + [AUD_CLKID_MST_B_LRCLK] = &audio_mst_b_lrclk.hw,
>> + [AUD_CLKID_MST_C_LRCLK_DIV] = &audio_mst_c_lrclk_div.hw,
>> + [AUD_CLKID_MST_C_LRCLK] = &audio_mst_c_lrclk.hw,
>> + [AUD_CLKID_MST_D_LRCLK_DIV] = &audio_mst_d_lrclk_div.hw,
>> + [AUD_CLKID_MST_D_LRCLK] = &audio_mst_d_lrclk.hw,
>> + [AUD_CLKID_TDMIN_A_SCLK_SEL] = &audio_tdmin_a_sclk_mux.hw,
>> + [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &audio_tdmin_a_sclk_pre_en.hw,
>> + [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &audio_tdmin_a_sclk_post_en.hw,
>> + [AUD_CLKID_TDMIN_A_SCLK] = &audio_tdmin_a_sclk.hw,
>> + [AUD_CLKID_TDMIN_A_LRCLK] = &audio_tdmin_a_lrclk.hw,
>> + [AUD_CLKID_TDMIN_B_SCLK_SEL] = &audio_tdmin_b_sclk_mux.hw,
>> + [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &audio_tdmin_b_sclk_pre_en.hw,
>> + [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &audio_tdmin_b_sclk_post_en.hw,
>> + [AUD_CLKID_TDMIN_B_SCLK] = &audio_tdmin_b_sclk.hw,
>> + [AUD_CLKID_TDMIN_B_LRCLK] = &audio_tdmin_b_lrclk.hw,
>> + [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &audio_tdmin_lb_sclk_mux.hw,
>> + [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &audio_tdmin_lb_sclk_pre_en.hw,
>> + [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &audio_tdmin_lb_sclk_post_en.hw,
>> + [AUD_CLKID_TDMIN_LB_SCLK] = &audio_tdmin_lb_sclk.hw,
>> + [AUD_CLKID_TDMIN_LB_LRCLK] = &audio_tdmin_lb_lrclk.hw,
>> + [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &audio_tdmout_a_sclk_mux.hw,
>> + [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &audio_tdmout_a_sclk_pre_en.hw,
>> + [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &audio_tdmout_a_sclk_post_en.hw,
>> + [AUD_CLKID_TDMOUT_A_SCLK] = &audio_tdmout_a_sclk.hw,
>> + [AUD_CLKID_TDMOUT_A_LRCLK] = &audio_tdmout_a_lrclk.hw,
>> + [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &audio_tdmout_b_sclk_mux.hw,
>> + [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &audio_tdmout_b_sclk_pre_en.hw,
>> + [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &audio_tdmout_b_sclk_post_en.hw,
>> + [AUD_CLKID_TDMOUT_B_SCLK] = &audio_tdmout_b_sclk.hw,
>> + [AUD_CLKID_TDMOUT_B_LRCLK] = &audio_tdmout_b_lrclk.hw,
>> +
>> + [AUD2_CLKID_DDR_ARB] = &audio2_ddr_arb.hw,
>> + [AUD2_CLKID_PDM] = &audio2_pdm.hw,
>> + [AUD2_CLKID_TDMIN_VAD] = &audio2_tdmin_vad.hw,
>> + [AUD2_CLKID_TODDR_VAD] = &audio2_toddr_vad.hw,
>> + [AUD2_CLKID_VAD] = &audio2_vad.hw,
>> + [AUD2_CLKID_AUDIOTOP] = &audio2_audiotop.hw,
>> + [AUD2_CLKID_VAD_MCLK_SEL] = &audio2_vad_mclk_mux.hw,
>> + [AUD2_CLKID_VAD_MCLK_DIV] = &audio2_vad_mclk_div.hw,
>> + [AUD2_CLKID_VAD_MCLK] = &audio2_vad_mclk.hw,
>> + [AUD2_CLKID_VAD_CLK_SEL] = &audio2_vad_clk_mux.hw,
>> + [AUD2_CLKID_VAD_CLK_DIV] = &audio2_vad_clk_div.hw,
>> + [AUD2_CLKID_VAD_CLK] = &audio2_vad_clk.hw,
>> + [AUD2_CLKID_PDM_DCLK_SEL] = &audio2_pdm_dclk_mux.hw,
>> + [AUD2_CLKID_PDM_DCLK_DIV] = &audio2_pdm_dclk_div.hw,
>> + [AUD2_CLKID_PDM_DCLK] = &audio2_pdm_dclk.hw,
>> + [AUD2_CLKID_PDM_SYSCLK_SEL] = &audio2_pdm_sysclk_mux.hw,
>> + [AUD2_CLKID_PDM_SYSCLK_DIV] = &audio2_pdm_sysclk_div.hw,
>> + [AUD2_CLKID_PDM_SYSCLK] = &audio2_pdm_sysclk.hw,
>> +};
>> +
>> +static struct meson_clk_hw_data a1_audio_clks = {
>> + .hws = a1_audio_hw_clks,
>> + .num = ARRAY_SIZE(a1_audio_hw_clks),
>> +};
>> +
>> +static struct regmap *a1_audio_map(struct platform_device *pdev,
>> + unsigned int index)
>> +{
>> + char name[32];
>> + const struct regmap_config cfg = {
>> + .reg_bits = 32,
>> + .val_bits = 32,
>> + .reg_stride = 4,
>> + .name = name,
>
> Not necessary
>

This implementation uses two regmaps, and this field allow to avoid
errors like this:

[ 0.145530] debugfs: Directory 'fe050000.audio-clock-controller' with
parent 'regmap' already present!

>> + };
>> + void __iomem *base;
>> +
>> + base = devm_platform_ioremap_resource(pdev, index);
>> + if (IS_ERR(base))
>> + return base;
>> +
>> + scnprintf(name, sizeof(name), "%d", index);
>> + return devm_regmap_init_mmio(&pdev->dev, base, &cfg);
>> +}
>
> That is overengineered. Please keep it simple. Declare the regmap_config
> as static const global, and do it like axg-audio please.
>

This only reason why it is not "static const" because I need to set
unique name for each regmap.

>> +
>> +static int a1_register_clk(struct platform_device *pdev,
>> + struct regmap *map0, struct regmap *map1,
>> + struct clk_hw *hw)
>> +{
>> + struct clk_regmap *clk = container_of(hw, struct clk_regmap, hw);
>> +
>> + if (!hw)
>> + return 0;
>> +
>> + switch ((unsigned long)clk->map) {
>> + case AUDIO_RANGE_0:
>> + clk->map = map0;
>> + break;
>> + case AUDIO_RANGE_1:
>> + clk->map = map1;
>> + break;
>
> ... fishy
>
>> + default:
>> + WARN_ON(1);
>> + return -EINVAL;
>> + }
>> +
>> + return devm_clk_hw_register(&pdev->dev, hw);
>> +}
>> +
>> +static int a1_audio_clkc_probe(struct platform_device *pdev)
>> +{
>> + struct regmap *map0, *map1;
>> + struct clk *clk;
>> + unsigned int i;
>> + int ret;
>> +
>> + clk = devm_clk_get_enabled(&pdev->dev, "pclk");
>> + if (WARN_ON(IS_ERR(clk)))
>> + return PTR_ERR(clk);
>> +
>> + map0 = a1_audio_map(pdev, 0);
>> + if (IS_ERR(map0))
>> + return PTR_ERR(map0);
>> +
>> + map1 = a1_audio_map(pdev, 1);
>> + if (IS_ERR(map1))
>> + return PTR_ERR(map1);
>
> No - Looks to me you just have two clock controllers you are trying
> force into one.
>

See the begining.

>> +
>> + /*
>> + * Register and enable AUD2_CLKID_AUDIOTOP clock first. Unless
>> + * it is enabled any read/write to 'map0' hangs the CPU.
>> + */
>> +
>> + ret = a1_register_clk(pdev, map0, map1,
>> + a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]);
>> + if (ret)
>> + return ret;
>> +
>> + ret = clk_prepare_enable(a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]->clk);
>> + if (ret)
>> + return ret;
>
> Again, this shows 2 devices. The one related to your 'map0' should
> request AUD2_CLKID_AUDIOTOP as input and enable it right away.
>

See the begining.

>> +
>> + for (i = 0; i < a1_audio_clks.num; i++) {
>> + if (i == AUD2_CLKID_AUDIOTOP)
>> + continue;
>> +
>> + ret = a1_register_clk(pdev, map0, map1, a1_audio_clks.hws[i]);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + ret = devm_of_clk_add_hw_provider(&pdev->dev, meson_clk_hw_get,
>> + &a1_audio_clks);
>> + if (ret)
>> + return ret;
>> +
>> + BUILD_BUG_ON((unsigned long)AUDIO_REG_MAP(AUDIO_SW_RESET0) !=
>> + AUDIO_RANGE_0);
>
> Why is that necessary ?
>

A little paranoia. Here AUDIO_SW_RESET0 is handled as map0's register,
and I want to assert it.

>> + return meson_audio_rstc_register(&pdev->dev, map0,
>> + AUDIO_REG_OFFSET(AUDIO_SW_RESET0), 32);
>> +}
>> +
>> +static const struct of_device_id a1_audio_clkc_match_table[] = {
>> + { .compatible = "amlogic,a1-audio-clkc", },
>> + {}
>> +};
>> +MODULE_DEVICE_TABLE(of, a1_audio_clkc_match_table);
>> +
>> +static struct platform_driver a1_audio_clkc_driver = {
>> + .probe = a1_audio_clkc_probe,
>> + .driver = {
>> + .name = "a1-audio-clkc",
>> + .of_match_table = a1_audio_clkc_match_table,
>> + },
>> +};
>> +module_platform_driver(a1_audio_clkc_driver);
>> +
>> +MODULE_DESCRIPTION("Amlogic A1 Audio Clock driver");
>> +MODULE_AUTHOR("Jan Dakinevich <[email protected]>");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/clk/meson/a1-audio.h b/drivers/clk/meson/a1-audio.h
>> new file mode 100644
>> index 000000000000..f994e87276cd
>> --- /dev/null
>> +++ b/drivers/clk/meson/a1-audio.h
>> @@ -0,0 +1,58 @@
>> +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
>> +/*
>> + * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
>> + *
>> + * Author: Jan Dakinevich <[email protected]>
>> + */
>> +
>> +#ifndef __A1_AUDIO_H
>> +#define __A1_AUDIO_H
>> +
>> +#define AUDIO_RANGE_0 0xa
>> +#define AUDIO_RANGE_1 0xb
>> +#define AUDIO_RANGE_SHIFT 16
>> +
>> +#define AUDIO_REG(_range, _offset) \
>> + (((_range) << AUDIO_RANGE_SHIFT) + (_offset))
>> +
>> +#define AUDIO_REG_OFFSET(_reg) \
>> + ((_reg) & ((1 << AUDIO_RANGE_SHIFT) - 1))
>> +
>> +#define AUDIO_REG_MAP(_reg) \
>> + ((void *)((_reg) >> AUDIO_RANGE_SHIFT))
>
> That is seriouly overengineered.
> The following are offset. Just write what they are.
>

This is all in order to keep range's identifier together with offset and
then use it to store the identifier in clk_regmaps.

> There is not reason to put that into a header. It is only going to be
> used by a single driver.
> >> +
>> +#define AUDIO_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_0, 0x000)
>> +#define AUDIO_MCLK_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x008)
>> +#define AUDIO_MCLK_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x00c)
>> +#define AUDIO_MCLK_C_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x010)
>> +#define AUDIO_MCLK_D_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x014)
>> +#define AUDIO_MCLK_E_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x018)
>> +#define AUDIO_MCLK_F_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x01c)
>> +#define AUDIO_SW_RESET0 AUDIO_REG(AUDIO_RANGE_0, 0x028)
>> +#define AUDIO_MST_A_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x040)
>> +#define AUDIO_MST_A_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x044)
>> +#define AUDIO_MST_B_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x048)
>> +#define AUDIO_MST_B_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x04c)
>> +#define AUDIO_MST_C_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x050)
>> +#define AUDIO_MST_C_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x054)
>> +#define AUDIO_MST_D_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x058)
>> +#define AUDIO_MST_D_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x05c)
>> +#define AUDIO_CLK_TDMIN_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x080)
>> +#define AUDIO_CLK_TDMIN_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x084)
>> +#define AUDIO_CLK_TDMIN_LB_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x08c)
>> +#define AUDIO_CLK_TDMOUT_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x090)
>> +#define AUDIO_CLK_TDMOUT_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x094)
>> +#define AUDIO_CLK_SPDIFIN_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x09c)
>> +#define AUDIO_CLK_RESAMPLE_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a4)
>> +#define AUDIO_CLK_LOCKER_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a8)
>> +#define AUDIO_CLK_EQDRC_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0c0)
>> +
>> +#define AUDIO2_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_1, 0x00c)
>> +#define AUDIO2_MCLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x040)
>> +#define AUDIO2_CLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x044)
>> +#define AUDIO2_CLK_PDMIN_CTRL0 AUDIO_REG(AUDIO_RANGE_1, 0x058)
>> +#define AUDIO2_CLK_PDMIN_CTRL1 AUDIO_REG(AUDIO_RANGE_1, 0x05c)
>> +
>> +#include <dt-bindings/clock/amlogic,a1-audio-clkc.h>
>> +
>> +#endif /* __A1_AUDIO_H */
>
>

--
Best regards
Jan Dakinevich

2024-03-19 05:17:21

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH 13/25] ASoC: dt-bindings: meson: axg-pdm: document 'sysrate' property

On 17/03/2024 17:35, Jan Dakinevich wrote:
>
>
> On 3/17/24 19:27, Krzysztof Kozlowski wrote:
>> On 17/03/2024 16:55, Jan Dakinevich wrote:
>>>
>>>
>>> On 3/15/24 13:00, Krzysztof Kozlowski wrote:
>>>> On 15/03/2024 00:21, Jan Dakinevich wrote:
>>>>> This option allow to redefine the rate of DSP system clock.
>>>>
>>>> And why is it suitable for bindings? Describe the hardware, not what you
>>>> want to do in the driver.
>>>>
>>>
>>> What do you mean? I am adding some new property and should describe it
>>> in dt-bindinds. Isn't it?
>>
>> No, if the property is not suitable for bindings, you should not add it
>> in the first place. So again: explain what sort of hardware, not driver,
>> problem you are solving here, so we can understand why do you need new
>> property. Otherwise use existing properties or no properties, because we
>> do not define all possible clocks in the bindings.
>>
>> Let's be clear: with such commit msg explanation as you have, my answer
>> is: no, driver should set clock frequency and you do not need this
>> property at all.
>>
>
> Could you please take a look on answer to "Jerome Brunet
> <[email protected]>"'s message on the same thread. There, I am trying
> to explain what I am solving by this commit.

How is this answer here? You asked "What do you mean", so apparently you
did not understand why I am responding and why you cannot just document
whatever you wish, because that "whatever you wish" is not correct. I
explained that but now you respond that I should read other part of
emails. Really?

So again, do you understand that commit msg should provide rationale why
you think this describes hardware and why this is suitable for bindings?


Best regards,
Krzysztof


2024-03-19 05:18:11

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH 13/25] ASoC: dt-bindings: meson: axg-pdm: document 'sysrate' property

On 17/03/2024 16:52, Jan Dakinevich wrote:
>
>
> On 3/15/24 13:22, Jerome Brunet wrote:
>>
>> On Fri 15 Mar 2024 at 11:00, Krzysztof Kozlowski <[email protected]> wrote:
>>
>>> On 15/03/2024 00:21, Jan Dakinevich wrote:
>>>> This option allow to redefine the rate of DSP system clock.
>>>
>>> And why is it suitable for bindings? Describe the hardware, not what you
>>> want to do in the driver.
>>>
>>>>
>>>> Signed-off-by: Jan Dakinevich <[email protected]>
>>>> ---
>>>> Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml | 4 ++++
>>>> 1 file changed, 4 insertions(+)
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>>>> index df21dd72fc65..d2f23a59a6b6 100644
>>>> --- a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>>>> +++ b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
>>>> @@ -40,6 +40,10 @@ properties:
>>>> resets:
>>>> maxItems: 1
>>>>
>>>> + sysrate:
>>>> + $ref: /schemas/types.yaml#/definitions/uint32
>>>> + description: redefine rate of DSP system clock
>>>
>>> No vendor prefix, so is it a generic property? Also, missing unit
>>> suffix, but more importantly I don't understand why this is a property
>>> of hardware.
>>
>> +1.
>>
>> The appropriate way to set rate of the clock before the driver take over
>> is 'assigned-rate', if you need to customize this for different
>> platform.
>>
>
> It would be great, but it doesn't work. Below, is what I want to see:
>
> assigned-clocks =
> <&clkc_audio AUD2_CLKID_PDM_SYSCLK_SEL>,
> <&clkc_audio AUD2_CLKID_PDM_SYSCLK_DIV>;
> assigned-clock-parents =
> <&clkc_pll CLKID_FCLK_DIV3>,
> <0>;
> assigned-clock-rates =
> <0>,
> <256000000>;
>
> But regardles of this declaration, PDM's driver unconditionally sets

That's driver's problem. You do not change bindings, just because your
driver behaves differently. Just fix driver.

> sysclk'rate to 250MHz and throws away everything that was configured
> before, reparents audio2_pdm_sysclk_mux to hifi_pll and changes
> hifi_pll's rate.
>
> This value 250MHz is declared here:
>
> static const struct axg_pdm_cfg axg_pdm_config = {
> .filters = &axg_default_filters,
> .sys_rate = 250000000,
> };
>
> The property 'sysrate' is intended to redefine hardcoded 'sys_rate'
> value in 'axg_pdm_config'.

What does it have to do with bindings? Change driver if you are not
happy how it operates.

Best regards,
Krzysztof


2024-03-19 09:04:27

by Jerome Brunet

[permalink] [raw]
Subject: Re: [PATCH 04/25] clk: meson: a1: add the audio clock controller driver


On Tue 19 Mar 2024 at 04:47, Jan Dakinevich <[email protected]> wrote:

> Let's start from the end:
>
>> No - Looks to me you just have two clock controllers you are trying
> force into one.
>
>> Again, this shows 2 devices. The one related to your 'map0' should
> request AUD2_CLKID_AUDIOTOP as input and enable it right away.
>
> Most of fishy workarounds that you commented is caused the fact the mmio
> of this clock controller is divided into two parts. Compare it with
> axg-audio driver, things that was part of contigous memory region (like
> pdm) here are moved to second region. Is this enough to make a guess
> that these are two devices?

I see obsolutely no reason to think it is a single device nor to add all the quirks
you have the way you did. So yes, in that case, 2 zones, 2 devices.

>
> Concerning AUD2_CLKID_AUDIOTOP clock, as it turned out, it must be
> enabled before enabling of clocks from second region too. That is
> AUD2_CLKID_AUDIOTOP clock feeds both parts of this clock controller.
>

Yes. I understood the first time around and already commented on that.

>
> On 3/15/24 12:20, Jerome Brunet wrote:
>>
>> On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <[email protected]> wrote:
>>
>>> This controller provides clocks and reset functionality for audio
>>> peripherals on Amlogic A1 SoC family.
>>>
>>> The driver is almost identical to 'axg-audio', however it would be better
>>> to keep it separate due to following reasons:
>>>
>>> - significant amount of bits has another definition. I will bring there
>>> a mess of new defines with A1_ suffixes.
>>>
>>> - registers of this controller are located in two separate regions. It
>>> will give a lot of complications for 'axg-audio' to support this.
>>>
>>> Signed-off-by: Jan Dakinevich <[email protected]>
>>> ---
>>> drivers/clk/meson/Kconfig | 13 +
>>> drivers/clk/meson/Makefile | 1 +
>>> drivers/clk/meson/a1-audio.c | 556 +++++++++++++++++++++++++++++++++++
>>> drivers/clk/meson/a1-audio.h | 58 ++++
>>> 4 files changed, 628 insertions(+)
>>> create mode 100644 drivers/clk/meson/a1-audio.c
>>> create mode 100644 drivers/clk/meson/a1-audio.h
>>>
>>> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
>>> index d6a2fa5f7e88..80c4a18c83d2 100644
>>> --- a/drivers/clk/meson/Kconfig
>>> +++ b/drivers/clk/meson/Kconfig
>>> @@ -133,6 +133,19 @@ config COMMON_CLK_A1_PERIPHERALS
>>> device, A1 SoC Family. Say Y if you want A1 Peripherals clock
>>> controller to work.
>>>
>>> +config COMMON_CLK_A1_AUDIO
>>> + tristate "Amlogic A1 SoC Audio clock controller support"
>>> + depends on ARM64
>>> + select COMMON_CLK_MESON_REGMAP
>>> + select COMMON_CLK_MESON_CLKC_UTILS
>>> + select COMMON_CLK_MESON_PHASE
>>> + select COMMON_CLK_MESON_SCLK_DIV
>>> + select COMMON_CLK_MESON_AUDIO_RSTC
>>> + help
>>> + Support for the Audio clock controller on Amlogic A113L based
>>> + device, A1 SoC Family. Say Y if you want A1 Audio clock controller
>>> + to work.
>>> +
>>> config COMMON_CLK_G12A
>>> tristate "G12 and SM1 SoC clock controllers support"
>>> depends on ARM64
>>> diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
>>> index 88d94921a4dc..4968fc7ad555 100644
>>> --- a/drivers/clk/meson/Makefile
>>> +++ b/drivers/clk/meson/Makefile
>>> @@ -20,6 +20,7 @@ obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
>>> obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
>>> obj-$(CONFIG_COMMON_CLK_A1_PLL) += a1-pll.o
>>> obj-$(CONFIG_COMMON_CLK_A1_PERIPHERALS) += a1-peripherals.o
>>> +obj-$(CONFIG_COMMON_CLK_A1_AUDIO) += a1-audio.o
>>> obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
>>> obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o
>>> obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o
>>> diff --git a/drivers/clk/meson/a1-audio.c b/drivers/clk/meson/a1-audio.c
>>> new file mode 100644
>>> index 000000000000..6039116c93ba
>>> --- /dev/null
>>> +++ b/drivers/clk/meson/a1-audio.c
>>> @@ -0,0 +1,556 @@
>>> +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
>>> +/*
>>> + * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
>>> + *
>>> + * Author: Jan Dakinevich <[email protected]>
>>> + */
>>> +
>>> +#include <linux/clk.h>
>>> +#include <linux/clk-provider.h>
>>> +#include <linux/init.h>
>>> +#include <linux/of_device.h>
>>> +#include <linux/module.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/reset.h>
>>> +#include <linux/reset-controller.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#include "meson-clkc-utils.h"
>>> +#include "meson-audio-rstc.h"
>>> +#include "clk-regmap.h"
>>> +#include "clk-phase.h"
>>> +#include "sclk-div.h"
>>> +#include "a1-audio.h"
>>> +
>>> +#define AUDIO_PDATA(_name) \
>>> + ((const struct clk_parent_data[]) { { .hw = &(_name).hw } })
>>
>> Not a fan - yet another level of macro.
>>
>>> +
>>> +#define AUDIO_MUX(_name, _reg, _mask, _shift, _pdata) \
>>> +static struct clk_regmap _name = { \
>>> + .map = AUDIO_REG_MAP(_reg), \
>>> + .data = &(struct clk_regmap_mux_data){ \
>>> + .offset = AUDIO_REG_OFFSET(_reg), \
>>> + .mask = (_mask), \
>>> + .shift = (_shift), \
>>> + }, \
>>> + .hw.init = &(struct clk_init_data) { \
>>> + .name = #_name, \
>>> + .ops = &clk_regmap_mux_ops, \
>>> + .parent_data = (_pdata), \
>>> + .num_parents = ARRAY_SIZE(_pdata), \
>>> + .flags = CLK_SET_RATE_PARENT, \
>>> + }, \
>>> +}
>>> +
>>> +#define AUDIO_DIV(_name, _reg, _shift, _width, _pdata) \
>>> +static struct clk_regmap _name = { \
>>> + .map = AUDIO_REG_MAP(_reg), \
>>> + .data = &(struct clk_regmap_div_data){ \
>>> + .offset = AUDIO_REG_OFFSET(_reg), \
>>> + .shift = (_shift), \
>>> + .width = (_width), \
>>> + }, \
>>> + .hw.init = &(struct clk_init_data) { \
>>> + .name = #_name, \
>>> + .ops = &clk_regmap_divider_ops, \
>>> + .parent_data = (_pdata), \
>>> + .num_parents = 1, \
>>> + .flags = CLK_SET_RATE_PARENT, \
>>> + }, \
>>> +}
>>> +
>>> +#define AUDIO_GATE(_name, _reg, _bit, _pdata) \
>>> +static struct clk_regmap _name = { \
>>> + .map = AUDIO_REG_MAP(_reg), \
>>> + .data = &(struct clk_regmap_gate_data){ \
>>> + .offset = AUDIO_REG_OFFSET(_reg), \
>>> + .bit_idx = (_bit), \
>>> + }, \
>>> + .hw.init = &(struct clk_init_data) { \
>>> + .name = #_name, \
>>> + .ops = &clk_regmap_gate_ops, \
>>> + .parent_data = (_pdata), \
>>> + .num_parents = 1, \
>>> + .flags = CLK_SET_RATE_PARENT, \
>>> + }, \
>>> +}
>>> +
>>> +#define AUDIO_SCLK_DIV(_name, _reg, _div_shift, _div_width, \
>>> + _hi_shift, _hi_width, _pdata, _set_rate_parent) \
>>> +static struct clk_regmap _name = { \
>>> + .map = AUDIO_REG_MAP(_reg), \
>>> + .data = &(struct meson_sclk_div_data) { \
>>> + .div = { \
>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>> + .shift = (_div_shift), \
>>> + .width = (_div_width), \
>>> + }, \
>>> + .hi = { \
>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>> + .shift = (_hi_shift), \
>>> + .width = (_hi_width), \
>>> + }, \
>>> + }, \
>>> + .hw.init = &(struct clk_init_data) { \
>>> + .name = #_name, \
>>> + .ops = &meson_sclk_div_ops, \
>>> + .parent_data = (_pdata), \
>>> + .num_parents = 1, \
>>> + .flags = (_set_rate_parent) ? CLK_SET_RATE_PARENT : 0, \
>>
>> Does not help readeability. Just pass the flag as axg-audio does.
>>
>>> + }, \
>>> +}
>>> +
>>> +#define AUDIO_TRIPHASE(_name, _reg, _width, _shift0, _shift1, _shift2, \
>>> + _pdata) \
>>> +static struct clk_regmap _name = { \
>>> + .map = AUDIO_REG_MAP(_reg), \
>>> + .data = &(struct meson_clk_triphase_data) { \
>>> + .ph0 = { \
>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>> + .shift = (_shift0), \
>>> + .width = (_width), \
>>> + }, \
>>> + .ph1 = { \
>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>> + .shift = (_shift1), \
>>> + .width = (_width), \
>>> + }, \
>>> + .ph2 = { \
>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>> + .shift = (_shift2), \
>>> + .width = (_width), \
>>> + }, \
>>> + }, \
>>> + .hw.init = &(struct clk_init_data) { \
>>> + .name = #_name, \
>>> + .ops = &meson_clk_triphase_ops, \
>>> + .parent_data = (_pdata), \
>>> + .num_parents = 1, \
>>> + .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \
>>> + }, \
>>> +}
>>> +
>>> +#define AUDIO_SCLK_WS(_name, _reg, _width, _shift_ph, _shift_ws, \
>>> + _pdata) \
>>> +static struct clk_regmap _name = { \
>>> + .map = AUDIO_REG_MAP(_reg), \
>>> + .data = &(struct meson_sclk_ws_inv_data) { \
>>> + .ph = { \
>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>> + .shift = (_shift_ph), \
>>> + .width = (_width), \
>>> + }, \
>>> + .ws = { \
>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>> + .shift = (_shift_ws), \
>>> + .width = (_width), \
>>> + }, \
>>> + }, \
>>> + .hw.init = &(struct clk_init_data) { \
>>> + .name = #_name, \
>>> + .ops = &meson_sclk_ws_inv_ops, \
>>> + .parent_data = (_pdata), \
>>> + .num_parents = 1, \
>>> + .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \
>>> + }, \
>>> +}
>>
>> All the above does essentially the same things as the macro of
>> axg-audio, to some minor differences. Yet it is another set to maintain.
>>
>
> Except one thing... Here I keep memory identifier to which this clock
> belongs:
>
> .map = AUDIO_REG_MAP(_reg),
>
> It is workaround, but ->map the only common field in clk_regmap that
> could be used for this purpose.
>
>
>> I'd much prefer if you put the axg-audio macro in a header a re-used
>> those. There would a single set to maintain. You may then specialize the
>> included in the driver C file, to avoid redundant parameters
>>
>> Rework axg-audio to use clk_parent_data if you must, but not in the same
>> series please.
>>
>>> +
>>> +static const struct clk_parent_data a1_pclk_pdata[] = {
>>> + { .fw_name = "pclk", },
>>> +};
>>> +
>>> +AUDIO_GATE(audio_ddr_arb, AUDIO_CLK_GATE_EN0, 0, a1_pclk_pdata);
>>> +AUDIO_GATE(audio_tdmin_a, AUDIO_CLK_GATE_EN0, 1, a1_pclk_pdata);
>>> +AUDIO_GATE(audio_tdmin_b, AUDIO_CLK_GATE_EN0, 2, a1_pclk_pdata);
>>> +AUDIO_GATE(audio_tdmin_lb, AUDIO_CLK_GATE_EN0, 3, a1_pclk_pdata);
>>> +AUDIO_GATE(audio_loopback, AUDIO_CLK_GATE_EN0, 4, a1_pclk_pdata);
>>> +AUDIO_GATE(audio_tdmout_a, AUDIO_CLK_GATE_EN0, 5, a1_pclk_pdata);
>>> +AUDIO_GATE(audio_tdmout_b, AUDIO_CLK_GATE_EN0, 6, a1_pclk_pdata);
>>> +AUDIO_GATE(audio_frddr_a, AUDIO_CLK_GATE_EN0, 7, a1_pclk_pdata);
>>> +AUDIO_GATE(audio_frddr_b, AUDIO_CLK_GATE_EN0, 8, a1_pclk_pdata);
>>> +AUDIO_GATE(audio_toddr_a, AUDIO_CLK_GATE_EN0, 9, a1_pclk_pdata);
>>> +AUDIO_GATE(audio_toddr_b, AUDIO_CLK_GATE_EN0, 10, a1_pclk_pdata);
>>> +AUDIO_GATE(audio_spdifin, AUDIO_CLK_GATE_EN0, 11, a1_pclk_pdata);
>>> +AUDIO_GATE(audio_resample, AUDIO_CLK_GATE_EN0, 12, a1_pclk_pdata);
>>> +AUDIO_GATE(audio_eqdrc, AUDIO_CLK_GATE_EN0, 13, a1_pclk_pdata);
>>> +AUDIO_GATE(audio_audiolocker, AUDIO_CLK_GATE_EN0, 14, a1_pclk_pdata);
>> This is what I mean by redundant parameter ^
>>
>
> Yep. I could define something like AUDIO_PCLK_GATE().
>
>>> +
>>> +AUDIO_GATE(audio2_ddr_arb, AUDIO2_CLK_GATE_EN0, 0, a1_pclk_pdata);
>>> +AUDIO_GATE(audio2_pdm, AUDIO2_CLK_GATE_EN0, 1, a1_pclk_pdata);
>>> +AUDIO_GATE(audio2_tdmin_vad, AUDIO2_CLK_GATE_EN0, 2, a1_pclk_pdata);
>>> +AUDIO_GATE(audio2_toddr_vad, AUDIO2_CLK_GATE_EN0, 3, a1_pclk_pdata);
>>> +AUDIO_GATE(audio2_vad, AUDIO2_CLK_GATE_EN0, 4, a1_pclk_pdata);
>>> +AUDIO_GATE(audio2_audiotop, AUDIO2_CLK_GATE_EN0, 7, a1_pclk_pdata);
>>> +
>>> +static const struct clk_parent_data a1_mst_pdata[] = {
>>> + { .fw_name = "dds_in" },
>>> + { .fw_name = "fclk_div2" },
>>> + { .fw_name = "fclk_div3" },
>>> + { .fw_name = "hifi_pll" },
>>> + { .fw_name = "xtal" },
>>> +};
>>> +
>>> +#define AUDIO_MST_MCLK(_name, _reg) \
>>> + AUDIO_MUX(_name##_mux, (_reg), 0x7, 24, a1_mst_pdata); \
>>> + AUDIO_DIV(_name##_div, (_reg), 0, 16, \
>>> + AUDIO_PDATA(_name##_mux)); \
>>> + AUDIO_GATE(_name, (_reg), 31, AUDIO_PDATA(_name##_div))
>>> +
>>> +AUDIO_MST_MCLK(audio_mst_a_mclk, AUDIO_MCLK_A_CTRL);
>>> +AUDIO_MST_MCLK(audio_mst_b_mclk, AUDIO_MCLK_B_CTRL);
>>> +AUDIO_MST_MCLK(audio_mst_c_mclk, AUDIO_MCLK_C_CTRL);
>>> +AUDIO_MST_MCLK(audio_mst_d_mclk, AUDIO_MCLK_D_CTRL);
>>> +AUDIO_MST_MCLK(audio_spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL);
>>> +AUDIO_MST_MCLK(audio_eqdrc_clk, AUDIO_CLK_EQDRC_CTRL);
>>> +
>>> +AUDIO_MUX(audio_resample_clk_mux, AUDIO_CLK_RESAMPLE_CTRL, 0xf, 24,
>>> + a1_mst_pdata);
>>> +AUDIO_DIV(audio_resample_clk_div, AUDIO_CLK_RESAMPLE_CTRL, 0, 8,
>>> + AUDIO_PDATA(audio_resample_clk_mux));
>>> +AUDIO_GATE(audio_resample_clk, AUDIO_CLK_RESAMPLE_CTRL, 31,
>>> + AUDIO_PDATA(audio_resample_clk_div));
>>> +
>>> +AUDIO_MUX(audio_locker_in_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 8,
>>> + a1_mst_pdata);
>>> +AUDIO_DIV(audio_locker_in_clk_div, AUDIO_CLK_LOCKER_CTRL, 0, 8,
>>> + AUDIO_PDATA(audio_locker_in_clk_mux));
>>> +AUDIO_GATE(audio_locker_in_clk, AUDIO_CLK_LOCKER_CTRL, 15,
>>> + AUDIO_PDATA(audio_locker_in_clk_div));
>>> +
>>> +AUDIO_MUX(audio_locker_out_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 24,
>>> + a1_mst_pdata);
>>> +AUDIO_DIV(audio_locker_out_clk_div, AUDIO_CLK_LOCKER_CTRL, 16, 8,
>>> + AUDIO_PDATA(audio_locker_out_clk_mux));
>>> +AUDIO_GATE(audio_locker_out_clk, AUDIO_CLK_LOCKER_CTRL, 31,
>>> + AUDIO_PDATA(audio_locker_out_clk_div));
>>> +
>>> +AUDIO_MST_MCLK(audio2_vad_mclk, AUDIO2_MCLK_VAD_CTRL);
>>> +AUDIO_MST_MCLK(audio2_vad_clk, AUDIO2_CLK_VAD_CTRL);
>>> +AUDIO_MST_MCLK(audio2_pdm_dclk, AUDIO2_CLK_PDMIN_CTRL0);
>>> +AUDIO_MST_MCLK(audio2_pdm_sysclk, AUDIO2_CLK_PDMIN_CTRL1);
>>> +
>>> +#define AUDIO_MST_SCLK(_name, _reg0, _reg1, _pdata) \
>>> + AUDIO_GATE(_name##_pre_en, (_reg0), 31, (_pdata)); \
>>> + AUDIO_SCLK_DIV(_name##_div, (_reg0), 20, 10, 0, 0, \
>>> + AUDIO_PDATA(_name##_pre_en), true); \
>>> + AUDIO_GATE(_name##_post_en, (_reg0), 30, \
>>> + AUDIO_PDATA(_name##_div)); \
>>> + AUDIO_TRIPHASE(_name, (_reg1), 1, 0, 2, 4, \
>>> + AUDIO_PDATA(_name##_post_en))
>>> +
>>
>> Again, I'm not a fan of this many levels of macro. I can live with it
>> but certainly don't want the burden of reviewing and maintaining for
>> clock driver. AXG / G12 and A1 are obviously closely related, so make it common.
>>
>>> +#define AUDIO_MST_LRCLK(_name, _reg0, _reg1, _pdata) \
>>> + AUDIO_SCLK_DIV(_name##_div, (_reg0), 0, 10, 10, 10, \
>>> + (_pdata), false); \
>>> + AUDIO_TRIPHASE(_name, (_reg1), 1, 1, 3, 5, \
>>> + AUDIO_PDATA(_name##_div))
>>> +
>>> +AUDIO_MST_SCLK(audio_mst_a_sclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1,
>>> + AUDIO_PDATA(audio_mst_a_mclk));
>>> +AUDIO_MST_SCLK(audio_mst_b_sclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1,
>>> + AUDIO_PDATA(audio_mst_b_mclk));
>>> +AUDIO_MST_SCLK(audio_mst_c_sclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1,
>>> + AUDIO_PDATA(audio_mst_c_mclk));
>>> +AUDIO_MST_SCLK(audio_mst_d_sclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1,
>>> + AUDIO_PDATA(audio_mst_d_mclk));
>>> +
>>> +AUDIO_MST_LRCLK(audio_mst_a_lrclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1,
>>> + AUDIO_PDATA(audio_mst_a_sclk_post_en));
>>> +AUDIO_MST_LRCLK(audio_mst_b_lrclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1,
>>> + AUDIO_PDATA(audio_mst_b_sclk_post_en));
>>> +AUDIO_MST_LRCLK(audio_mst_c_lrclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1,
>>> + AUDIO_PDATA(audio_mst_c_sclk_post_en));
>>> +AUDIO_MST_LRCLK(audio_mst_d_lrclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1,
>>> + AUDIO_PDATA(audio_mst_d_sclk_post_en));
>>> +
>>> +static const struct clk_parent_data a1_mst_sclk_pdata[] = {
>>> + { .hw = &audio_mst_a_sclk.hw },
>>> + { .hw = &audio_mst_b_sclk.hw },
>>> + { .hw = &audio_mst_c_sclk.hw },
>>> + { .hw = &audio_mst_d_sclk.hw },
>>> + { .fw_name = "slv_sclk0" },
>>> + { .fw_name = "slv_sclk1" },
>>> + { .fw_name = "slv_sclk2" },
>>> + { .fw_name = "slv_sclk3" },
>>> + { .fw_name = "slv_sclk4" },
>>> + { .fw_name = "slv_sclk5" },
>>> + { .fw_name = "slv_sclk6" },
>>> + { .fw_name = "slv_sclk7" },
>>> + { .fw_name = "slv_sclk8" },
>>> + { .fw_name = "slv_sclk9" },
>>> +};
>>> +
>>> +static const struct clk_parent_data a1_mst_lrclk_pdata[] = {
>>> + { .hw = &audio_mst_a_lrclk.hw },
>>> + { .hw = &audio_mst_b_lrclk.hw },
>>> + { .hw = &audio_mst_c_lrclk.hw },
>>> + { .hw = &audio_mst_d_lrclk.hw },
>>> + { .fw_name = "slv_lrclk0" },
>>> + { .fw_name = "slv_lrclk1" },
>>> + { .fw_name = "slv_lrclk2" },
>>> + { .fw_name = "slv_lrclk3" },
>>> + { .fw_name = "slv_lrclk4" },
>>> + { .fw_name = "slv_lrclk5" },
>>> + { .fw_name = "slv_lrclk6" },
>>> + { .fw_name = "slv_lrclk7" },
>>> + { .fw_name = "slv_lrclk8" },
>>> + { .fw_name = "slv_lrclk9" },
>>> +};
>>> +
>>> +#define AUDIO_TDM_SCLK(_name, _reg) \
>>> + AUDIO_MUX(_name##_mux, (_reg), 0xf, 24, a1_mst_sclk_pdata); \
>>> + AUDIO_GATE(_name##_pre_en, (_reg), 31, \
>>> + AUDIO_PDATA(_name##_mux)); \
>>> + AUDIO_GATE(_name##_post_en, (_reg), 30, \
>>> + AUDIO_PDATA(_name##_pre_en)); \
>>> + AUDIO_SCLK_WS(_name, (_reg), 1, 29, 28, \
>>> + AUDIO_PDATA(_name##_post_en))
>>> +
>>> +#define AUDIO_TDM_LRCLK(_name, _reg) \
>>> + AUDIO_MUX(_name, (_reg), 0xf, 20, a1_mst_lrclk_pdata)
>>> +
>>> +AUDIO_TDM_SCLK(audio_tdmin_a_sclk, AUDIO_CLK_TDMIN_A_CTRL);
>>> +AUDIO_TDM_SCLK(audio_tdmin_b_sclk, AUDIO_CLK_TDMIN_B_CTRL);
>>> +AUDIO_TDM_SCLK(audio_tdmin_lb_sclk, AUDIO_CLK_TDMIN_LB_CTRL);
>>> +AUDIO_TDM_SCLK(audio_tdmout_a_sclk, AUDIO_CLK_TDMOUT_A_CTRL);
>>> +AUDIO_TDM_SCLK(audio_tdmout_b_sclk, AUDIO_CLK_TDMOUT_B_CTRL);
>>> +
>>> +AUDIO_TDM_LRCLK(audio_tdmin_a_lrclk, AUDIO_CLK_TDMIN_A_CTRL);
>>> +AUDIO_TDM_LRCLK(audio_tdmin_b_lrclk, AUDIO_CLK_TDMIN_B_CTRL);
>>> +AUDIO_TDM_LRCLK(audio_tdmin_lb_lrclk, AUDIO_CLK_TDMIN_LB_CTRL);
>>> +AUDIO_TDM_LRCLK(audio_tdmout_a_lrclk, AUDIO_CLK_TDMOUT_A_CTRL);
>>> +AUDIO_TDM_LRCLK(audio_tdmout_b_lrclk, AUDIO_CLK_TDMOUT_B_CTRL);
>>> +
>>> +static struct clk_hw *a1_audio_hw_clks[] = {
>>> + [AUD_CLKID_DDR_ARB] = &audio_ddr_arb.hw,
>>> + [AUD_CLKID_TDMIN_A] = &audio_tdmin_a.hw,
>>> + [AUD_CLKID_TDMIN_B] = &audio_tdmin_b.hw,
>>> + [AUD_CLKID_TDMIN_LB] = &audio_tdmin_lb.hw,
>>> + [AUD_CLKID_LOOPBACK] = &audio_loopback.hw,
>>> + [AUD_CLKID_TDMOUT_A] = &audio_tdmout_a.hw,
>>> + [AUD_CLKID_TDMOUT_B] = &audio_tdmout_b.hw,
>>> + [AUD_CLKID_FRDDR_A] = &audio_frddr_a.hw,
>>> + [AUD_CLKID_FRDDR_B] = &audio_frddr_b.hw,
>>> + [AUD_CLKID_TODDR_A] = &audio_toddr_a.hw,
>>> + [AUD_CLKID_TODDR_B] = &audio_toddr_b.hw,
>>> + [AUD_CLKID_SPDIFIN] = &audio_spdifin.hw,
>>> + [AUD_CLKID_RESAMPLE] = &audio_resample.hw,
>>> + [AUD_CLKID_EQDRC] = &audio_eqdrc.hw,
>>> + [AUD_CLKID_LOCKER] = &audio_audiolocker.hw,
>>> + [AUD_CLKID_MST_A_MCLK_SEL] = &audio_mst_a_mclk_mux.hw,
>>> + [AUD_CLKID_MST_A_MCLK_DIV] = &audio_mst_a_mclk_div.hw,
>>> + [AUD_CLKID_MST_A_MCLK] = &audio_mst_a_mclk.hw,
>>> + [AUD_CLKID_MST_B_MCLK_SEL] = &audio_mst_b_mclk_mux.hw,
>>> + [AUD_CLKID_MST_B_MCLK_DIV] = &audio_mst_b_mclk_div.hw,
>>> + [AUD_CLKID_MST_B_MCLK] = &audio_mst_b_mclk.hw,
>>> + [AUD_CLKID_MST_C_MCLK_SEL] = &audio_mst_c_mclk_mux.hw,
>>> + [AUD_CLKID_MST_C_MCLK_DIV] = &audio_mst_c_mclk_div.hw,
>>> + [AUD_CLKID_MST_C_MCLK] = &audio_mst_c_mclk.hw,
>>> + [AUD_CLKID_MST_D_MCLK_SEL] = &audio_mst_d_mclk_mux.hw,
>>> + [AUD_CLKID_MST_D_MCLK_DIV] = &audio_mst_d_mclk_div.hw,
>>> + [AUD_CLKID_MST_D_MCLK] = &audio_mst_d_mclk.hw,
>>> + [AUD_CLKID_RESAMPLE_CLK_SEL] = &audio_resample_clk_mux.hw,
>>> + [AUD_CLKID_RESAMPLE_CLK_DIV] = &audio_resample_clk_div.hw,
>>> + [AUD_CLKID_RESAMPLE_CLK] = &audio_resample_clk.hw,
>>> + [AUD_CLKID_LOCKER_IN_CLK_SEL] = &audio_locker_in_clk_mux.hw,
>>> + [AUD_CLKID_LOCKER_IN_CLK_DIV] = &audio_locker_in_clk_div.hw,
>>> + [AUD_CLKID_LOCKER_IN_CLK] = &audio_locker_in_clk.hw,
>>> + [AUD_CLKID_LOCKER_OUT_CLK_SEL] = &audio_locker_out_clk_mux.hw,
>>> + [AUD_CLKID_LOCKER_OUT_CLK_DIV] = &audio_locker_out_clk_div.hw,
>>> + [AUD_CLKID_LOCKER_OUT_CLK] = &audio_locker_out_clk.hw,
>>> + [AUD_CLKID_SPDIFIN_CLK_SEL] = &audio_spdifin_clk_mux.hw,
>>> + [AUD_CLKID_SPDIFIN_CLK_DIV] = &audio_spdifin_clk_div.hw,
>>> + [AUD_CLKID_SPDIFIN_CLK] = &audio_spdifin_clk.hw,
>>> + [AUD_CLKID_EQDRC_CLK_SEL] = &audio_eqdrc_clk_mux.hw,
>>> + [AUD_CLKID_EQDRC_CLK_DIV] = &audio_eqdrc_clk_div.hw,
>>> + [AUD_CLKID_EQDRC_CLK] = &audio_eqdrc_clk.hw,
>>> + [AUD_CLKID_MST_A_SCLK_PRE_EN] = &audio_mst_a_sclk_pre_en.hw,
>>> + [AUD_CLKID_MST_A_SCLK_DIV] = &audio_mst_a_sclk_div.hw,
>>> + [AUD_CLKID_MST_A_SCLK_POST_EN] = &audio_mst_a_sclk_post_en.hw,
>>> + [AUD_CLKID_MST_A_SCLK] = &audio_mst_a_sclk.hw,
>>> + [AUD_CLKID_MST_B_SCLK_PRE_EN] = &audio_mst_b_sclk_pre_en.hw,
>>> + [AUD_CLKID_MST_B_SCLK_DIV] = &audio_mst_b_sclk_div.hw,
>>> + [AUD_CLKID_MST_B_SCLK_POST_EN] = &audio_mst_b_sclk_post_en.hw,
>>> + [AUD_CLKID_MST_B_SCLK] = &audio_mst_b_sclk.hw,
>>> + [AUD_CLKID_MST_C_SCLK_PRE_EN] = &audio_mst_c_sclk_pre_en.hw,
>>> + [AUD_CLKID_MST_C_SCLK_DIV] = &audio_mst_c_sclk_div.hw,
>>> + [AUD_CLKID_MST_C_SCLK_POST_EN] = &audio_mst_c_sclk_post_en.hw,
>>> + [AUD_CLKID_MST_C_SCLK] = &audio_mst_c_sclk.hw,
>>> + [AUD_CLKID_MST_D_SCLK_PRE_EN] = &audio_mst_d_sclk_pre_en.hw,
>>> + [AUD_CLKID_MST_D_SCLK_DIV] = &audio_mst_d_sclk_div.hw,
>>> + [AUD_CLKID_MST_D_SCLK_POST_EN] = &audio_mst_d_sclk_post_en.hw,
>>> + [AUD_CLKID_MST_D_SCLK] = &audio_mst_d_sclk.hw,
>>> + [AUD_CLKID_MST_A_LRCLK_DIV] = &audio_mst_a_lrclk_div.hw,
>>> + [AUD_CLKID_MST_A_LRCLK] = &audio_mst_a_lrclk.hw,
>>> + [AUD_CLKID_MST_B_LRCLK_DIV] = &audio_mst_b_lrclk_div.hw,
>>> + [AUD_CLKID_MST_B_LRCLK] = &audio_mst_b_lrclk.hw,
>>> + [AUD_CLKID_MST_C_LRCLK_DIV] = &audio_mst_c_lrclk_div.hw,
>>> + [AUD_CLKID_MST_C_LRCLK] = &audio_mst_c_lrclk.hw,
>>> + [AUD_CLKID_MST_D_LRCLK_DIV] = &audio_mst_d_lrclk_div.hw,
>>> + [AUD_CLKID_MST_D_LRCLK] = &audio_mst_d_lrclk.hw,
>>> + [AUD_CLKID_TDMIN_A_SCLK_SEL] = &audio_tdmin_a_sclk_mux.hw,
>>> + [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &audio_tdmin_a_sclk_pre_en.hw,
>>> + [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &audio_tdmin_a_sclk_post_en.hw,
>>> + [AUD_CLKID_TDMIN_A_SCLK] = &audio_tdmin_a_sclk.hw,
>>> + [AUD_CLKID_TDMIN_A_LRCLK] = &audio_tdmin_a_lrclk.hw,
>>> + [AUD_CLKID_TDMIN_B_SCLK_SEL] = &audio_tdmin_b_sclk_mux.hw,
>>> + [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &audio_tdmin_b_sclk_pre_en.hw,
>>> + [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &audio_tdmin_b_sclk_post_en.hw,
>>> + [AUD_CLKID_TDMIN_B_SCLK] = &audio_tdmin_b_sclk.hw,
>>> + [AUD_CLKID_TDMIN_B_LRCLK] = &audio_tdmin_b_lrclk.hw,
>>> + [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &audio_tdmin_lb_sclk_mux.hw,
>>> + [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &audio_tdmin_lb_sclk_pre_en.hw,
>>> + [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &audio_tdmin_lb_sclk_post_en.hw,
>>> + [AUD_CLKID_TDMIN_LB_SCLK] = &audio_tdmin_lb_sclk.hw,
>>> + [AUD_CLKID_TDMIN_LB_LRCLK] = &audio_tdmin_lb_lrclk.hw,
>>> + [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &audio_tdmout_a_sclk_mux.hw,
>>> + [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &audio_tdmout_a_sclk_pre_en.hw,
>>> + [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &audio_tdmout_a_sclk_post_en.hw,
>>> + [AUD_CLKID_TDMOUT_A_SCLK] = &audio_tdmout_a_sclk.hw,
>>> + [AUD_CLKID_TDMOUT_A_LRCLK] = &audio_tdmout_a_lrclk.hw,
>>> + [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &audio_tdmout_b_sclk_mux.hw,
>>> + [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &audio_tdmout_b_sclk_pre_en.hw,
>>> + [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &audio_tdmout_b_sclk_post_en.hw,
>>> + [AUD_CLKID_TDMOUT_B_SCLK] = &audio_tdmout_b_sclk.hw,
>>> + [AUD_CLKID_TDMOUT_B_LRCLK] = &audio_tdmout_b_lrclk.hw,
>>> +
>>> + [AUD2_CLKID_DDR_ARB] = &audio2_ddr_arb.hw,
>>> + [AUD2_CLKID_PDM] = &audio2_pdm.hw,
>>> + [AUD2_CLKID_TDMIN_VAD] = &audio2_tdmin_vad.hw,
>>> + [AUD2_CLKID_TODDR_VAD] = &audio2_toddr_vad.hw,
>>> + [AUD2_CLKID_VAD] = &audio2_vad.hw,
>>> + [AUD2_CLKID_AUDIOTOP] = &audio2_audiotop.hw,
>>> + [AUD2_CLKID_VAD_MCLK_SEL] = &audio2_vad_mclk_mux.hw,
>>> + [AUD2_CLKID_VAD_MCLK_DIV] = &audio2_vad_mclk_div.hw,
>>> + [AUD2_CLKID_VAD_MCLK] = &audio2_vad_mclk.hw,
>>> + [AUD2_CLKID_VAD_CLK_SEL] = &audio2_vad_clk_mux.hw,
>>> + [AUD2_CLKID_VAD_CLK_DIV] = &audio2_vad_clk_div.hw,
>>> + [AUD2_CLKID_VAD_CLK] = &audio2_vad_clk.hw,
>>> + [AUD2_CLKID_PDM_DCLK_SEL] = &audio2_pdm_dclk_mux.hw,
>>> + [AUD2_CLKID_PDM_DCLK_DIV] = &audio2_pdm_dclk_div.hw,
>>> + [AUD2_CLKID_PDM_DCLK] = &audio2_pdm_dclk.hw,
>>> + [AUD2_CLKID_PDM_SYSCLK_SEL] = &audio2_pdm_sysclk_mux.hw,
>>> + [AUD2_CLKID_PDM_SYSCLK_DIV] = &audio2_pdm_sysclk_div.hw,
>>> + [AUD2_CLKID_PDM_SYSCLK] = &audio2_pdm_sysclk.hw,
>>> +};
>>> +
>>> +static struct meson_clk_hw_data a1_audio_clks = {
>>> + .hws = a1_audio_hw_clks,
>>> + .num = ARRAY_SIZE(a1_audio_hw_clks),
>>> +};
>>> +
>>> +static struct regmap *a1_audio_map(struct platform_device *pdev,
>>> + unsigned int index)
>>> +{
>>> + char name[32];
>>> + const struct regmap_config cfg = {
>>> + .reg_bits = 32,
>>> + .val_bits = 32,
>>> + .reg_stride = 4,
>>> + .name = name,
>>
>> Not necessary
>>
>
> This implementation uses two regmaps, and this field allow to avoid
> errors like this:
>
> [ 0.145530] debugfs: Directory 'fe050000.audio-clock-controller' with
> parent 'regmap' already present!
>
>>> + };
>>> + void __iomem *base;
>>> +
>>> + base = devm_platform_ioremap_resource(pdev, index);
>>> + if (IS_ERR(base))
>>> + return base;
>>> +
>>> + scnprintf(name, sizeof(name), "%d", index);
>>> + return devm_regmap_init_mmio(&pdev->dev, base, &cfg);
>>> +}
>>
>> That is overengineered. Please keep it simple. Declare the regmap_config
>> as static const global, and do it like axg-audio please.
>>
>
> This only reason why it is not "static const" because I need to set
> unique name for each regmap.
>
>>> +
>>> +static int a1_register_clk(struct platform_device *pdev,
>>> + struct regmap *map0, struct regmap *map1,
>>> + struct clk_hw *hw)
>>> +{
>>> + struct clk_regmap *clk = container_of(hw, struct clk_regmap, hw);
>>> +
>>> + if (!hw)
>>> + return 0;
>>> +
>>> + switch ((unsigned long)clk->map) {
>>> + case AUDIO_RANGE_0:
>>> + clk->map = map0;
>>> + break;
>>> + case AUDIO_RANGE_1:
>>> + clk->map = map1;
>>> + break;
>>
>> ... fishy
>>
>>> + default:
>>> + WARN_ON(1);
>>> + return -EINVAL;
>>> + }
>>> +
>>> + return devm_clk_hw_register(&pdev->dev, hw);
>>> +}
>>> +
>>> +static int a1_audio_clkc_probe(struct platform_device *pdev)
>>> +{
>>> + struct regmap *map0, *map1;
>>> + struct clk *clk;
>>> + unsigned int i;
>>> + int ret;
>>> +
>>> + clk = devm_clk_get_enabled(&pdev->dev, "pclk");
>>> + if (WARN_ON(IS_ERR(clk)))
>>> + return PTR_ERR(clk);
>>> +
>>> + map0 = a1_audio_map(pdev, 0);
>>> + if (IS_ERR(map0))
>>> + return PTR_ERR(map0);
>>> +
>>> + map1 = a1_audio_map(pdev, 1);
>>> + if (IS_ERR(map1))
>>> + return PTR_ERR(map1);
>>
>> No - Looks to me you just have two clock controllers you are trying
>> force into one.
>>
>
> See the begining.
>
>>> +
>>> + /*
>>> + * Register and enable AUD2_CLKID_AUDIOTOP clock first. Unless
>>> + * it is enabled any read/write to 'map0' hangs the CPU.
>>> + */
>>> +
>>> + ret = a1_register_clk(pdev, map0, map1,
>>> + a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + ret = clk_prepare_enable(a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]->clk);
>>> + if (ret)
>>> + return ret;
>>
>> Again, this shows 2 devices. The one related to your 'map0' should
>> request AUD2_CLKID_AUDIOTOP as input and enable it right away.
>>
>
> See the begining.
>
>>> +
>>> + for (i = 0; i < a1_audio_clks.num; i++) {
>>> + if (i == AUD2_CLKID_AUDIOTOP)
>>> + continue;
>>> +
>>> + ret = a1_register_clk(pdev, map0, map1, a1_audio_clks.hws[i]);
>>> + if (ret)
>>> + return ret;
>>> + }
>>> +
>>> + ret = devm_of_clk_add_hw_provider(&pdev->dev, meson_clk_hw_get,
>>> + &a1_audio_clks);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + BUILD_BUG_ON((unsigned long)AUDIO_REG_MAP(AUDIO_SW_RESET0) !=
>>> + AUDIO_RANGE_0);
>>
>> Why is that necessary ?
>>
>
> A little paranoia. Here AUDIO_SW_RESET0 is handled as map0's register,
> and I want to assert it.
>
>>> + return meson_audio_rstc_register(&pdev->dev, map0,
>>> + AUDIO_REG_OFFSET(AUDIO_SW_RESET0), 32);
>>> +}
>>> +
>>> +static const struct of_device_id a1_audio_clkc_match_table[] = {
>>> + { .compatible = "amlogic,a1-audio-clkc", },
>>> + {}
>>> +};
>>> +MODULE_DEVICE_TABLE(of, a1_audio_clkc_match_table);
>>> +
>>> +static struct platform_driver a1_audio_clkc_driver = {
>>> + .probe = a1_audio_clkc_probe,
>>> + .driver = {
>>> + .name = "a1-audio-clkc",
>>> + .of_match_table = a1_audio_clkc_match_table,
>>> + },
>>> +};
>>> +module_platform_driver(a1_audio_clkc_driver);
>>> +
>>> +MODULE_DESCRIPTION("Amlogic A1 Audio Clock driver");
>>> +MODULE_AUTHOR("Jan Dakinevich <[email protected]>");
>>> +MODULE_LICENSE("GPL");
>>> diff --git a/drivers/clk/meson/a1-audio.h b/drivers/clk/meson/a1-audio.h
>>> new file mode 100644
>>> index 000000000000..f994e87276cd
>>> --- /dev/null
>>> +++ b/drivers/clk/meson/a1-audio.h
>>> @@ -0,0 +1,58 @@
>>> +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
>>> +/*
>>> + * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
>>> + *
>>> + * Author: Jan Dakinevich <[email protected]>
>>> + */
>>> +
>>> +#ifndef __A1_AUDIO_H
>>> +#define __A1_AUDIO_H
>>> +
>>> +#define AUDIO_RANGE_0 0xa
>>> +#define AUDIO_RANGE_1 0xb
>>> +#define AUDIO_RANGE_SHIFT 16
>>> +
>>> +#define AUDIO_REG(_range, _offset) \
>>> + (((_range) << AUDIO_RANGE_SHIFT) + (_offset))
>>> +
>>> +#define AUDIO_REG_OFFSET(_reg) \
>>> + ((_reg) & ((1 << AUDIO_RANGE_SHIFT) - 1))
>>> +
>>> +#define AUDIO_REG_MAP(_reg) \
>>> + ((void *)((_reg) >> AUDIO_RANGE_SHIFT))
>>
>> That is seriouly overengineered.
>> The following are offset. Just write what they are.
>>
>
> This is all in order to keep range's identifier together with offset and
> then use it to store the identifier in clk_regmaps.
>
>> There is not reason to put that into a header. It is only going to be
>> used by a single driver.
>> >> +
>>> +#define AUDIO_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_0, 0x000)
>>> +#define AUDIO_MCLK_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x008)
>>> +#define AUDIO_MCLK_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x00c)
>>> +#define AUDIO_MCLK_C_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x010)
>>> +#define AUDIO_MCLK_D_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x014)
>>> +#define AUDIO_MCLK_E_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x018)
>>> +#define AUDIO_MCLK_F_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x01c)
>>> +#define AUDIO_SW_RESET0 AUDIO_REG(AUDIO_RANGE_0, 0x028)
>>> +#define AUDIO_MST_A_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x040)
>>> +#define AUDIO_MST_A_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x044)
>>> +#define AUDIO_MST_B_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x048)
>>> +#define AUDIO_MST_B_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x04c)
>>> +#define AUDIO_MST_C_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x050)
>>> +#define AUDIO_MST_C_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x054)
>>> +#define AUDIO_MST_D_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x058)
>>> +#define AUDIO_MST_D_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x05c)
>>> +#define AUDIO_CLK_TDMIN_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x080)
>>> +#define AUDIO_CLK_TDMIN_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x084)
>>> +#define AUDIO_CLK_TDMIN_LB_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x08c)
>>> +#define AUDIO_CLK_TDMOUT_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x090)
>>> +#define AUDIO_CLK_TDMOUT_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x094)
>>> +#define AUDIO_CLK_SPDIFIN_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x09c)
>>> +#define AUDIO_CLK_RESAMPLE_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a4)
>>> +#define AUDIO_CLK_LOCKER_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a8)
>>> +#define AUDIO_CLK_EQDRC_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0c0)
>>> +
>>> +#define AUDIO2_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_1, 0x00c)
>>> +#define AUDIO2_MCLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x040)
>>> +#define AUDIO2_CLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x044)
>>> +#define AUDIO2_CLK_PDMIN_CTRL0 AUDIO_REG(AUDIO_RANGE_1, 0x058)
>>> +#define AUDIO2_CLK_PDMIN_CTRL1 AUDIO_REG(AUDIO_RANGE_1, 0x05c)
>>> +
>>> +#include <dt-bindings/clock/amlogic,a1-audio-clkc.h>
>>> +
>>> +#endif /* __A1_AUDIO_H */
>>
>>


--
Jerome

2024-03-23 18:04:11

by Jan Dakinevich

[permalink] [raw]
Subject: Re: [PATCH 04/25] clk: meson: a1: add the audio clock controller driver

Jerome, I have reworked my driver reusing axg-audio code as most as I
could and now I have one more question. Lets see on this definition from
axg-audio:

#define AUD_MST_MUX(_name, _reg, _flag) \
AUD_MUX(_name##_sel, _reg, 0x7, 24, _flag, \
mst_mux_parent_data, 0)

#define AUD_MST_MCLK_MUX(_name, _reg) \
AUD_MST_MUX(_name, _reg, CLK_MUX_ROUND_CLOSEST)

CLK_SET_RATE_PARENT is not set here. But why? It means, that topmost pll
clock will not be reconfigured at runtime to satisfy the rate that was
requested from axg-tdm.


On 3/19/24 11:30, Jerome Brunet wrote:
>
> On Tue 19 Mar 2024 at 04:47, Jan Dakinevich <[email protected]> wrote:
>
>> Let's start from the end:
>>
>>> No - Looks to me you just have two clock controllers you are trying
>> force into one.
>>
>>> Again, this shows 2 devices. The one related to your 'map0' should
>> request AUD2_CLKID_AUDIOTOP as input and enable it right away.
>>
>> Most of fishy workarounds that you commented is caused the fact the mmio
>> of this clock controller is divided into two parts. Compare it with
>> axg-audio driver, things that was part of contigous memory region (like
>> pdm) here are moved to second region. Is this enough to make a guess
>> that these are two devices?
>
> I see obsolutely no reason to think it is a single device nor to add all the quirks
> you have the way you did. So yes, in that case, 2 zones, 2 devices.
>
>>
>> Concerning AUD2_CLKID_AUDIOTOP clock, as it turned out, it must be
>> enabled before enabling of clocks from second region too. That is
>> AUD2_CLKID_AUDIOTOP clock feeds both parts of this clock controller.
>>
>
> Yes. I understood the first time around and already commented on that.
>
>>
>> On 3/15/24 12:20, Jerome Brunet wrote:
>>>
>>> On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <[email protected]> wrote:
>>>
>>>> This controller provides clocks and reset functionality for audio
>>>> peripherals on Amlogic A1 SoC family.
>>>>
>>>> The driver is almost identical to 'axg-audio', however it would be better
>>>> to keep it separate due to following reasons:
>>>>
>>>> - significant amount of bits has another definition. I will bring there
>>>> a mess of new defines with A1_ suffixes.
>>>>
>>>> - registers of this controller are located in two separate regions. It
>>>> will give a lot of complications for 'axg-audio' to support this.
>>>>
>>>> Signed-off-by: Jan Dakinevich <[email protected]>
>>>> ---
>>>> drivers/clk/meson/Kconfig | 13 +
>>>> drivers/clk/meson/Makefile | 1 +
>>>> drivers/clk/meson/a1-audio.c | 556 +++++++++++++++++++++++++++++++++++
>>>> drivers/clk/meson/a1-audio.h | 58 ++++
>>>> 4 files changed, 628 insertions(+)
>>>> create mode 100644 drivers/clk/meson/a1-audio.c
>>>> create mode 100644 drivers/clk/meson/a1-audio.h
>>>>
>>>> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
>>>> index d6a2fa5f7e88..80c4a18c83d2 100644
>>>> --- a/drivers/clk/meson/Kconfig
>>>> +++ b/drivers/clk/meson/Kconfig
>>>> @@ -133,6 +133,19 @@ config COMMON_CLK_A1_PERIPHERALS
>>>> device, A1 SoC Family. Say Y if you want A1 Peripherals clock
>>>> controller to work.
>>>>
>>>> +config COMMON_CLK_A1_AUDIO
>>>> + tristate "Amlogic A1 SoC Audio clock controller support"
>>>> + depends on ARM64
>>>> + select COMMON_CLK_MESON_REGMAP
>>>> + select COMMON_CLK_MESON_CLKC_UTILS
>>>> + select COMMON_CLK_MESON_PHASE
>>>> + select COMMON_CLK_MESON_SCLK_DIV
>>>> + select COMMON_CLK_MESON_AUDIO_RSTC
>>>> + help
>>>> + Support for the Audio clock controller on Amlogic A113L based
>>>> + device, A1 SoC Family. Say Y if you want A1 Audio clock controller
>>>> + to work.
>>>> +
>>>> config COMMON_CLK_G12A
>>>> tristate "G12 and SM1 SoC clock controllers support"
>>>> depends on ARM64
>>>> diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
>>>> index 88d94921a4dc..4968fc7ad555 100644
>>>> --- a/drivers/clk/meson/Makefile
>>>> +++ b/drivers/clk/meson/Makefile
>>>> @@ -20,6 +20,7 @@ obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
>>>> obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
>>>> obj-$(CONFIG_COMMON_CLK_A1_PLL) += a1-pll.o
>>>> obj-$(CONFIG_COMMON_CLK_A1_PERIPHERALS) += a1-peripherals.o
>>>> +obj-$(CONFIG_COMMON_CLK_A1_AUDIO) += a1-audio.o
>>>> obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
>>>> obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o
>>>> obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o
>>>> diff --git a/drivers/clk/meson/a1-audio.c b/drivers/clk/meson/a1-audio.c
>>>> new file mode 100644
>>>> index 000000000000..6039116c93ba
>>>> --- /dev/null
>>>> +++ b/drivers/clk/meson/a1-audio.c
>>>> @@ -0,0 +1,556 @@
>>>> +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
>>>> +/*
>>>> + * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
>>>> + *
>>>> + * Author: Jan Dakinevich <[email protected]>
>>>> + */
>>>> +
>>>> +#include <linux/clk.h>
>>>> +#include <linux/clk-provider.h>
>>>> +#include <linux/init.h>
>>>> +#include <linux/of_device.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/platform_device.h>
>>>> +#include <linux/regmap.h>
>>>> +#include <linux/reset.h>
>>>> +#include <linux/reset-controller.h>
>>>> +#include <linux/slab.h>
>>>> +
>>>> +#include "meson-clkc-utils.h"
>>>> +#include "meson-audio-rstc.h"
>>>> +#include "clk-regmap.h"
>>>> +#include "clk-phase.h"
>>>> +#include "sclk-div.h"
>>>> +#include "a1-audio.h"
>>>> +
>>>> +#define AUDIO_PDATA(_name) \
>>>> + ((const struct clk_parent_data[]) { { .hw = &(_name).hw } })
>>>
>>> Not a fan - yet another level of macro.
>>>
>>>> +
>>>> +#define AUDIO_MUX(_name, _reg, _mask, _shift, _pdata) \
>>>> +static struct clk_regmap _name = { \
>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>> + .data = &(struct clk_regmap_mux_data){ \
>>>> + .offset = AUDIO_REG_OFFSET(_reg), \
>>>> + .mask = (_mask), \
>>>> + .shift = (_shift), \
>>>> + }, \
>>>> + .hw.init = &(struct clk_init_data) { \
>>>> + .name = #_name, \
>>>> + .ops = &clk_regmap_mux_ops, \
>>>> + .parent_data = (_pdata), \
>>>> + .num_parents = ARRAY_SIZE(_pdata), \
>>>> + .flags = CLK_SET_RATE_PARENT, \
>>>> + }, \
>>>> +}
>>>> +
>>>> +#define AUDIO_DIV(_name, _reg, _shift, _width, _pdata) \
>>>> +static struct clk_regmap _name = { \
>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>> + .data = &(struct clk_regmap_div_data){ \
>>>> + .offset = AUDIO_REG_OFFSET(_reg), \
>>>> + .shift = (_shift), \
>>>> + .width = (_width), \
>>>> + }, \
>>>> + .hw.init = &(struct clk_init_data) { \
>>>> + .name = #_name, \
>>>> + .ops = &clk_regmap_divider_ops, \
>>>> + .parent_data = (_pdata), \
>>>> + .num_parents = 1, \
>>>> + .flags = CLK_SET_RATE_PARENT, \
>>>> + }, \
>>>> +}
>>>> +
>>>> +#define AUDIO_GATE(_name, _reg, _bit, _pdata) \
>>>> +static struct clk_regmap _name = { \
>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>> + .data = &(struct clk_regmap_gate_data){ \
>>>> + .offset = AUDIO_REG_OFFSET(_reg), \
>>>> + .bit_idx = (_bit), \
>>>> + }, \
>>>> + .hw.init = &(struct clk_init_data) { \
>>>> + .name = #_name, \
>>>> + .ops = &clk_regmap_gate_ops, \
>>>> + .parent_data = (_pdata), \
>>>> + .num_parents = 1, \
>>>> + .flags = CLK_SET_RATE_PARENT, \
>>>> + }, \
>>>> +}
>>>> +
>>>> +#define AUDIO_SCLK_DIV(_name, _reg, _div_shift, _div_width, \
>>>> + _hi_shift, _hi_width, _pdata, _set_rate_parent) \
>>>> +static struct clk_regmap _name = { \
>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>> + .data = &(struct meson_sclk_div_data) { \
>>>> + .div = { \
>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>> + .shift = (_div_shift), \
>>>> + .width = (_div_width), \
>>>> + }, \
>>>> + .hi = { \
>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>> + .shift = (_hi_shift), \
>>>> + .width = (_hi_width), \
>>>> + }, \
>>>> + }, \
>>>> + .hw.init = &(struct clk_init_data) { \
>>>> + .name = #_name, \
>>>> + .ops = &meson_sclk_div_ops, \
>>>> + .parent_data = (_pdata), \
>>>> + .num_parents = 1, \
>>>> + .flags = (_set_rate_parent) ? CLK_SET_RATE_PARENT : 0, \
>>>
>>> Does not help readeability. Just pass the flag as axg-audio does.
>>>
>>>> + }, \
>>>> +}
>>>> +
>>>> +#define AUDIO_TRIPHASE(_name, _reg, _width, _shift0, _shift1, _shift2, \
>>>> + _pdata) \
>>>> +static struct clk_regmap _name = { \
>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>> + .data = &(struct meson_clk_triphase_data) { \
>>>> + .ph0 = { \
>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>> + .shift = (_shift0), \
>>>> + .width = (_width), \
>>>> + }, \
>>>> + .ph1 = { \
>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>> + .shift = (_shift1), \
>>>> + .width = (_width), \
>>>> + }, \
>>>> + .ph2 = { \
>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>> + .shift = (_shift2), \
>>>> + .width = (_width), \
>>>> + }, \
>>>> + }, \
>>>> + .hw.init = &(struct clk_init_data) { \
>>>> + .name = #_name, \
>>>> + .ops = &meson_clk_triphase_ops, \
>>>> + .parent_data = (_pdata), \
>>>> + .num_parents = 1, \
>>>> + .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \
>>>> + }, \
>>>> +}
>>>> +
>>>> +#define AUDIO_SCLK_WS(_name, _reg, _width, _shift_ph, _shift_ws, \
>>>> + _pdata) \
>>>> +static struct clk_regmap _name = { \
>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>> + .data = &(struct meson_sclk_ws_inv_data) { \
>>>> + .ph = { \
>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>> + .shift = (_shift_ph), \
>>>> + .width = (_width), \
>>>> + }, \
>>>> + .ws = { \
>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>> + .shift = (_shift_ws), \
>>>> + .width = (_width), \
>>>> + }, \
>>>> + }, \
>>>> + .hw.init = &(struct clk_init_data) { \
>>>> + .name = #_name, \
>>>> + .ops = &meson_sclk_ws_inv_ops, \
>>>> + .parent_data = (_pdata), \
>>>> + .num_parents = 1, \
>>>> + .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \
>>>> + }, \
>>>> +}
>>>
>>> All the above does essentially the same things as the macro of
>>> axg-audio, to some minor differences. Yet it is another set to maintain.
>>>
>>
>> Except one thing... Here I keep memory identifier to which this clock
>> belongs:
>>
>> .map = AUDIO_REG_MAP(_reg),
>>
>> It is workaround, but ->map the only common field in clk_regmap that
>> could be used for this purpose.
>>
>>
>>> I'd much prefer if you put the axg-audio macro in a header a re-used
>>> those. There would a single set to maintain. You may then specialize the
>>> included in the driver C file, to avoid redundant parameters
>>>
>>> Rework axg-audio to use clk_parent_data if you must, but not in the same
>>> series please.
>>>
>>>> +
>>>> +static const struct clk_parent_data a1_pclk_pdata[] = {
>>>> + { .fw_name = "pclk", },
>>>> +};
>>>> +
>>>> +AUDIO_GATE(audio_ddr_arb, AUDIO_CLK_GATE_EN0, 0, a1_pclk_pdata);
>>>> +AUDIO_GATE(audio_tdmin_a, AUDIO_CLK_GATE_EN0, 1, a1_pclk_pdata);
>>>> +AUDIO_GATE(audio_tdmin_b, AUDIO_CLK_GATE_EN0, 2, a1_pclk_pdata);
>>>> +AUDIO_GATE(audio_tdmin_lb, AUDIO_CLK_GATE_EN0, 3, a1_pclk_pdata);
>>>> +AUDIO_GATE(audio_loopback, AUDIO_CLK_GATE_EN0, 4, a1_pclk_pdata);
>>>> +AUDIO_GATE(audio_tdmout_a, AUDIO_CLK_GATE_EN0, 5, a1_pclk_pdata);
>>>> +AUDIO_GATE(audio_tdmout_b, AUDIO_CLK_GATE_EN0, 6, a1_pclk_pdata);
>>>> +AUDIO_GATE(audio_frddr_a, AUDIO_CLK_GATE_EN0, 7, a1_pclk_pdata);
>>>> +AUDIO_GATE(audio_frddr_b, AUDIO_CLK_GATE_EN0, 8, a1_pclk_pdata);
>>>> +AUDIO_GATE(audio_toddr_a, AUDIO_CLK_GATE_EN0, 9, a1_pclk_pdata);
>>>> +AUDIO_GATE(audio_toddr_b, AUDIO_CLK_GATE_EN0, 10, a1_pclk_pdata);
>>>> +AUDIO_GATE(audio_spdifin, AUDIO_CLK_GATE_EN0, 11, a1_pclk_pdata);
>>>> +AUDIO_GATE(audio_resample, AUDIO_CLK_GATE_EN0, 12, a1_pclk_pdata);
>>>> +AUDIO_GATE(audio_eqdrc, AUDIO_CLK_GATE_EN0, 13, a1_pclk_pdata);
>>>> +AUDIO_GATE(audio_audiolocker, AUDIO_CLK_GATE_EN0, 14, a1_pclk_pdata);
>>> This is what I mean by redundant parameter ^
>>>
>>
>> Yep. I could define something like AUDIO_PCLK_GATE().
>>
>>>> +
>>>> +AUDIO_GATE(audio2_ddr_arb, AUDIO2_CLK_GATE_EN0, 0, a1_pclk_pdata);
>>>> +AUDIO_GATE(audio2_pdm, AUDIO2_CLK_GATE_EN0, 1, a1_pclk_pdata);
>>>> +AUDIO_GATE(audio2_tdmin_vad, AUDIO2_CLK_GATE_EN0, 2, a1_pclk_pdata);
>>>> +AUDIO_GATE(audio2_toddr_vad, AUDIO2_CLK_GATE_EN0, 3, a1_pclk_pdata);
>>>> +AUDIO_GATE(audio2_vad, AUDIO2_CLK_GATE_EN0, 4, a1_pclk_pdata);
>>>> +AUDIO_GATE(audio2_audiotop, AUDIO2_CLK_GATE_EN0, 7, a1_pclk_pdata);
>>>> +
>>>> +static const struct clk_parent_data a1_mst_pdata[] = {
>>>> + { .fw_name = "dds_in" },
>>>> + { .fw_name = "fclk_div2" },
>>>> + { .fw_name = "fclk_div3" },
>>>> + { .fw_name = "hifi_pll" },
>>>> + { .fw_name = "xtal" },
>>>> +};
>>>> +
>>>> +#define AUDIO_MST_MCLK(_name, _reg) \
>>>> + AUDIO_MUX(_name##_mux, (_reg), 0x7, 24, a1_mst_pdata); \
>>>> + AUDIO_DIV(_name##_div, (_reg), 0, 16, \
>>>> + AUDIO_PDATA(_name##_mux)); \
>>>> + AUDIO_GATE(_name, (_reg), 31, AUDIO_PDATA(_name##_div))
>>>> +
>>>> +AUDIO_MST_MCLK(audio_mst_a_mclk, AUDIO_MCLK_A_CTRL);
>>>> +AUDIO_MST_MCLK(audio_mst_b_mclk, AUDIO_MCLK_B_CTRL);
>>>> +AUDIO_MST_MCLK(audio_mst_c_mclk, AUDIO_MCLK_C_CTRL);
>>>> +AUDIO_MST_MCLK(audio_mst_d_mclk, AUDIO_MCLK_D_CTRL);
>>>> +AUDIO_MST_MCLK(audio_spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL);
>>>> +AUDIO_MST_MCLK(audio_eqdrc_clk, AUDIO_CLK_EQDRC_CTRL);
>>>> +
>>>> +AUDIO_MUX(audio_resample_clk_mux, AUDIO_CLK_RESAMPLE_CTRL, 0xf, 24,
>>>> + a1_mst_pdata);
>>>> +AUDIO_DIV(audio_resample_clk_div, AUDIO_CLK_RESAMPLE_CTRL, 0, 8,
>>>> + AUDIO_PDATA(audio_resample_clk_mux));
>>>> +AUDIO_GATE(audio_resample_clk, AUDIO_CLK_RESAMPLE_CTRL, 31,
>>>> + AUDIO_PDATA(audio_resample_clk_div));
>>>> +
>>>> +AUDIO_MUX(audio_locker_in_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 8,
>>>> + a1_mst_pdata);
>>>> +AUDIO_DIV(audio_locker_in_clk_div, AUDIO_CLK_LOCKER_CTRL, 0, 8,
>>>> + AUDIO_PDATA(audio_locker_in_clk_mux));
>>>> +AUDIO_GATE(audio_locker_in_clk, AUDIO_CLK_LOCKER_CTRL, 15,
>>>> + AUDIO_PDATA(audio_locker_in_clk_div));
>>>> +
>>>> +AUDIO_MUX(audio_locker_out_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 24,
>>>> + a1_mst_pdata);
>>>> +AUDIO_DIV(audio_locker_out_clk_div, AUDIO_CLK_LOCKER_CTRL, 16, 8,
>>>> + AUDIO_PDATA(audio_locker_out_clk_mux));
>>>> +AUDIO_GATE(audio_locker_out_clk, AUDIO_CLK_LOCKER_CTRL, 31,
>>>> + AUDIO_PDATA(audio_locker_out_clk_div));
>>>> +
>>>> +AUDIO_MST_MCLK(audio2_vad_mclk, AUDIO2_MCLK_VAD_CTRL);
>>>> +AUDIO_MST_MCLK(audio2_vad_clk, AUDIO2_CLK_VAD_CTRL);
>>>> +AUDIO_MST_MCLK(audio2_pdm_dclk, AUDIO2_CLK_PDMIN_CTRL0);
>>>> +AUDIO_MST_MCLK(audio2_pdm_sysclk, AUDIO2_CLK_PDMIN_CTRL1);
>>>> +
>>>> +#define AUDIO_MST_SCLK(_name, _reg0, _reg1, _pdata) \
>>>> + AUDIO_GATE(_name##_pre_en, (_reg0), 31, (_pdata)); \
>>>> + AUDIO_SCLK_DIV(_name##_div, (_reg0), 20, 10, 0, 0, \
>>>> + AUDIO_PDATA(_name##_pre_en), true); \
>>>> + AUDIO_GATE(_name##_post_en, (_reg0), 30, \
>>>> + AUDIO_PDATA(_name##_div)); \
>>>> + AUDIO_TRIPHASE(_name, (_reg1), 1, 0, 2, 4, \
>>>> + AUDIO_PDATA(_name##_post_en))
>>>> +
>>>
>>> Again, I'm not a fan of this many levels of macro. I can live with it
>>> but certainly don't want the burden of reviewing and maintaining for
>>> clock driver. AXG / G12 and A1 are obviously closely related, so make it common.
>>>
>>>> +#define AUDIO_MST_LRCLK(_name, _reg0, _reg1, _pdata) \
>>>> + AUDIO_SCLK_DIV(_name##_div, (_reg0), 0, 10, 10, 10, \
>>>> + (_pdata), false); \
>>>> + AUDIO_TRIPHASE(_name, (_reg1), 1, 1, 3, 5, \
>>>> + AUDIO_PDATA(_name##_div))
>>>> +
>>>> +AUDIO_MST_SCLK(audio_mst_a_sclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1,
>>>> + AUDIO_PDATA(audio_mst_a_mclk));
>>>> +AUDIO_MST_SCLK(audio_mst_b_sclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1,
>>>> + AUDIO_PDATA(audio_mst_b_mclk));
>>>> +AUDIO_MST_SCLK(audio_mst_c_sclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1,
>>>> + AUDIO_PDATA(audio_mst_c_mclk));
>>>> +AUDIO_MST_SCLK(audio_mst_d_sclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1,
>>>> + AUDIO_PDATA(audio_mst_d_mclk));
>>>> +
>>>> +AUDIO_MST_LRCLK(audio_mst_a_lrclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1,
>>>> + AUDIO_PDATA(audio_mst_a_sclk_post_en));
>>>> +AUDIO_MST_LRCLK(audio_mst_b_lrclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1,
>>>> + AUDIO_PDATA(audio_mst_b_sclk_post_en));
>>>> +AUDIO_MST_LRCLK(audio_mst_c_lrclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1,
>>>> + AUDIO_PDATA(audio_mst_c_sclk_post_en));
>>>> +AUDIO_MST_LRCLK(audio_mst_d_lrclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1,
>>>> + AUDIO_PDATA(audio_mst_d_sclk_post_en));
>>>> +
>>>> +static const struct clk_parent_data a1_mst_sclk_pdata[] = {
>>>> + { .hw = &audio_mst_a_sclk.hw },
>>>> + { .hw = &audio_mst_b_sclk.hw },
>>>> + { .hw = &audio_mst_c_sclk.hw },
>>>> + { .hw = &audio_mst_d_sclk.hw },
>>>> + { .fw_name = "slv_sclk0" },
>>>> + { .fw_name = "slv_sclk1" },
>>>> + { .fw_name = "slv_sclk2" },
>>>> + { .fw_name = "slv_sclk3" },
>>>> + { .fw_name = "slv_sclk4" },
>>>> + { .fw_name = "slv_sclk5" },
>>>> + { .fw_name = "slv_sclk6" },
>>>> + { .fw_name = "slv_sclk7" },
>>>> + { .fw_name = "slv_sclk8" },
>>>> + { .fw_name = "slv_sclk9" },
>>>> +};
>>>> +
>>>> +static const struct clk_parent_data a1_mst_lrclk_pdata[] = {
>>>> + { .hw = &audio_mst_a_lrclk.hw },
>>>> + { .hw = &audio_mst_b_lrclk.hw },
>>>> + { .hw = &audio_mst_c_lrclk.hw },
>>>> + { .hw = &audio_mst_d_lrclk.hw },
>>>> + { .fw_name = "slv_lrclk0" },
>>>> + { .fw_name = "slv_lrclk1" },
>>>> + { .fw_name = "slv_lrclk2" },
>>>> + { .fw_name = "slv_lrclk3" },
>>>> + { .fw_name = "slv_lrclk4" },
>>>> + { .fw_name = "slv_lrclk5" },
>>>> + { .fw_name = "slv_lrclk6" },
>>>> + { .fw_name = "slv_lrclk7" },
>>>> + { .fw_name = "slv_lrclk8" },
>>>> + { .fw_name = "slv_lrclk9" },
>>>> +};
>>>> +
>>>> +#define AUDIO_TDM_SCLK(_name, _reg) \
>>>> + AUDIO_MUX(_name##_mux, (_reg), 0xf, 24, a1_mst_sclk_pdata); \
>>>> + AUDIO_GATE(_name##_pre_en, (_reg), 31, \
>>>> + AUDIO_PDATA(_name##_mux)); \
>>>> + AUDIO_GATE(_name##_post_en, (_reg), 30, \
>>>> + AUDIO_PDATA(_name##_pre_en)); \
>>>> + AUDIO_SCLK_WS(_name, (_reg), 1, 29, 28, \
>>>> + AUDIO_PDATA(_name##_post_en))
>>>> +
>>>> +#define AUDIO_TDM_LRCLK(_name, _reg) \
>>>> + AUDIO_MUX(_name, (_reg), 0xf, 20, a1_mst_lrclk_pdata)
>>>> +
>>>> +AUDIO_TDM_SCLK(audio_tdmin_a_sclk, AUDIO_CLK_TDMIN_A_CTRL);
>>>> +AUDIO_TDM_SCLK(audio_tdmin_b_sclk, AUDIO_CLK_TDMIN_B_CTRL);
>>>> +AUDIO_TDM_SCLK(audio_tdmin_lb_sclk, AUDIO_CLK_TDMIN_LB_CTRL);
>>>> +AUDIO_TDM_SCLK(audio_tdmout_a_sclk, AUDIO_CLK_TDMOUT_A_CTRL);
>>>> +AUDIO_TDM_SCLK(audio_tdmout_b_sclk, AUDIO_CLK_TDMOUT_B_CTRL);
>>>> +
>>>> +AUDIO_TDM_LRCLK(audio_tdmin_a_lrclk, AUDIO_CLK_TDMIN_A_CTRL);
>>>> +AUDIO_TDM_LRCLK(audio_tdmin_b_lrclk, AUDIO_CLK_TDMIN_B_CTRL);
>>>> +AUDIO_TDM_LRCLK(audio_tdmin_lb_lrclk, AUDIO_CLK_TDMIN_LB_CTRL);
>>>> +AUDIO_TDM_LRCLK(audio_tdmout_a_lrclk, AUDIO_CLK_TDMOUT_A_CTRL);
>>>> +AUDIO_TDM_LRCLK(audio_tdmout_b_lrclk, AUDIO_CLK_TDMOUT_B_CTRL);
>>>> +
>>>> +static struct clk_hw *a1_audio_hw_clks[] = {
>>>> + [AUD_CLKID_DDR_ARB] = &audio_ddr_arb.hw,
>>>> + [AUD_CLKID_TDMIN_A] = &audio_tdmin_a.hw,
>>>> + [AUD_CLKID_TDMIN_B] = &audio_tdmin_b.hw,
>>>> + [AUD_CLKID_TDMIN_LB] = &audio_tdmin_lb.hw,
>>>> + [AUD_CLKID_LOOPBACK] = &audio_loopback.hw,
>>>> + [AUD_CLKID_TDMOUT_A] = &audio_tdmout_a.hw,
>>>> + [AUD_CLKID_TDMOUT_B] = &audio_tdmout_b.hw,
>>>> + [AUD_CLKID_FRDDR_A] = &audio_frddr_a.hw,
>>>> + [AUD_CLKID_FRDDR_B] = &audio_frddr_b.hw,
>>>> + [AUD_CLKID_TODDR_A] = &audio_toddr_a.hw,
>>>> + [AUD_CLKID_TODDR_B] = &audio_toddr_b.hw,
>>>> + [AUD_CLKID_SPDIFIN] = &audio_spdifin.hw,
>>>> + [AUD_CLKID_RESAMPLE] = &audio_resample.hw,
>>>> + [AUD_CLKID_EQDRC] = &audio_eqdrc.hw,
>>>> + [AUD_CLKID_LOCKER] = &audio_audiolocker.hw,
>>>> + [AUD_CLKID_MST_A_MCLK_SEL] = &audio_mst_a_mclk_mux.hw,
>>>> + [AUD_CLKID_MST_A_MCLK_DIV] = &audio_mst_a_mclk_div.hw,
>>>> + [AUD_CLKID_MST_A_MCLK] = &audio_mst_a_mclk.hw,
>>>> + [AUD_CLKID_MST_B_MCLK_SEL] = &audio_mst_b_mclk_mux.hw,
>>>> + [AUD_CLKID_MST_B_MCLK_DIV] = &audio_mst_b_mclk_div.hw,
>>>> + [AUD_CLKID_MST_B_MCLK] = &audio_mst_b_mclk.hw,
>>>> + [AUD_CLKID_MST_C_MCLK_SEL] = &audio_mst_c_mclk_mux.hw,
>>>> + [AUD_CLKID_MST_C_MCLK_DIV] = &audio_mst_c_mclk_div.hw,
>>>> + [AUD_CLKID_MST_C_MCLK] = &audio_mst_c_mclk.hw,
>>>> + [AUD_CLKID_MST_D_MCLK_SEL] = &audio_mst_d_mclk_mux.hw,
>>>> + [AUD_CLKID_MST_D_MCLK_DIV] = &audio_mst_d_mclk_div.hw,
>>>> + [AUD_CLKID_MST_D_MCLK] = &audio_mst_d_mclk.hw,
>>>> + [AUD_CLKID_RESAMPLE_CLK_SEL] = &audio_resample_clk_mux.hw,
>>>> + [AUD_CLKID_RESAMPLE_CLK_DIV] = &audio_resample_clk_div.hw,
>>>> + [AUD_CLKID_RESAMPLE_CLK] = &audio_resample_clk.hw,
>>>> + [AUD_CLKID_LOCKER_IN_CLK_SEL] = &audio_locker_in_clk_mux.hw,
>>>> + [AUD_CLKID_LOCKER_IN_CLK_DIV] = &audio_locker_in_clk_div.hw,
>>>> + [AUD_CLKID_LOCKER_IN_CLK] = &audio_locker_in_clk.hw,
>>>> + [AUD_CLKID_LOCKER_OUT_CLK_SEL] = &audio_locker_out_clk_mux.hw,
>>>> + [AUD_CLKID_LOCKER_OUT_CLK_DIV] = &audio_locker_out_clk_div.hw,
>>>> + [AUD_CLKID_LOCKER_OUT_CLK] = &audio_locker_out_clk.hw,
>>>> + [AUD_CLKID_SPDIFIN_CLK_SEL] = &audio_spdifin_clk_mux.hw,
>>>> + [AUD_CLKID_SPDIFIN_CLK_DIV] = &audio_spdifin_clk_div.hw,
>>>> + [AUD_CLKID_SPDIFIN_CLK] = &audio_spdifin_clk.hw,
>>>> + [AUD_CLKID_EQDRC_CLK_SEL] = &audio_eqdrc_clk_mux.hw,
>>>> + [AUD_CLKID_EQDRC_CLK_DIV] = &audio_eqdrc_clk_div.hw,
>>>> + [AUD_CLKID_EQDRC_CLK] = &audio_eqdrc_clk.hw,
>>>> + [AUD_CLKID_MST_A_SCLK_PRE_EN] = &audio_mst_a_sclk_pre_en.hw,
>>>> + [AUD_CLKID_MST_A_SCLK_DIV] = &audio_mst_a_sclk_div.hw,
>>>> + [AUD_CLKID_MST_A_SCLK_POST_EN] = &audio_mst_a_sclk_post_en.hw,
>>>> + [AUD_CLKID_MST_A_SCLK] = &audio_mst_a_sclk.hw,
>>>> + [AUD_CLKID_MST_B_SCLK_PRE_EN] = &audio_mst_b_sclk_pre_en.hw,
>>>> + [AUD_CLKID_MST_B_SCLK_DIV] = &audio_mst_b_sclk_div.hw,
>>>> + [AUD_CLKID_MST_B_SCLK_POST_EN] = &audio_mst_b_sclk_post_en.hw,
>>>> + [AUD_CLKID_MST_B_SCLK] = &audio_mst_b_sclk.hw,
>>>> + [AUD_CLKID_MST_C_SCLK_PRE_EN] = &audio_mst_c_sclk_pre_en.hw,
>>>> + [AUD_CLKID_MST_C_SCLK_DIV] = &audio_mst_c_sclk_div.hw,
>>>> + [AUD_CLKID_MST_C_SCLK_POST_EN] = &audio_mst_c_sclk_post_en.hw,
>>>> + [AUD_CLKID_MST_C_SCLK] = &audio_mst_c_sclk.hw,
>>>> + [AUD_CLKID_MST_D_SCLK_PRE_EN] = &audio_mst_d_sclk_pre_en.hw,
>>>> + [AUD_CLKID_MST_D_SCLK_DIV] = &audio_mst_d_sclk_div.hw,
>>>> + [AUD_CLKID_MST_D_SCLK_POST_EN] = &audio_mst_d_sclk_post_en.hw,
>>>> + [AUD_CLKID_MST_D_SCLK] = &audio_mst_d_sclk.hw,
>>>> + [AUD_CLKID_MST_A_LRCLK_DIV] = &audio_mst_a_lrclk_div.hw,
>>>> + [AUD_CLKID_MST_A_LRCLK] = &audio_mst_a_lrclk.hw,
>>>> + [AUD_CLKID_MST_B_LRCLK_DIV] = &audio_mst_b_lrclk_div.hw,
>>>> + [AUD_CLKID_MST_B_LRCLK] = &audio_mst_b_lrclk.hw,
>>>> + [AUD_CLKID_MST_C_LRCLK_DIV] = &audio_mst_c_lrclk_div.hw,
>>>> + [AUD_CLKID_MST_C_LRCLK] = &audio_mst_c_lrclk.hw,
>>>> + [AUD_CLKID_MST_D_LRCLK_DIV] = &audio_mst_d_lrclk_div.hw,
>>>> + [AUD_CLKID_MST_D_LRCLK] = &audio_mst_d_lrclk.hw,
>>>> + [AUD_CLKID_TDMIN_A_SCLK_SEL] = &audio_tdmin_a_sclk_mux.hw,
>>>> + [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &audio_tdmin_a_sclk_pre_en.hw,
>>>> + [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &audio_tdmin_a_sclk_post_en.hw,
>>>> + [AUD_CLKID_TDMIN_A_SCLK] = &audio_tdmin_a_sclk.hw,
>>>> + [AUD_CLKID_TDMIN_A_LRCLK] = &audio_tdmin_a_lrclk.hw,
>>>> + [AUD_CLKID_TDMIN_B_SCLK_SEL] = &audio_tdmin_b_sclk_mux.hw,
>>>> + [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &audio_tdmin_b_sclk_pre_en.hw,
>>>> + [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &audio_tdmin_b_sclk_post_en.hw,
>>>> + [AUD_CLKID_TDMIN_B_SCLK] = &audio_tdmin_b_sclk.hw,
>>>> + [AUD_CLKID_TDMIN_B_LRCLK] = &audio_tdmin_b_lrclk.hw,
>>>> + [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &audio_tdmin_lb_sclk_mux.hw,
>>>> + [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &audio_tdmin_lb_sclk_pre_en.hw,
>>>> + [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &audio_tdmin_lb_sclk_post_en.hw,
>>>> + [AUD_CLKID_TDMIN_LB_SCLK] = &audio_tdmin_lb_sclk.hw,
>>>> + [AUD_CLKID_TDMIN_LB_LRCLK] = &audio_tdmin_lb_lrclk.hw,
>>>> + [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &audio_tdmout_a_sclk_mux.hw,
>>>> + [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &audio_tdmout_a_sclk_pre_en.hw,
>>>> + [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &audio_tdmout_a_sclk_post_en.hw,
>>>> + [AUD_CLKID_TDMOUT_A_SCLK] = &audio_tdmout_a_sclk.hw,
>>>> + [AUD_CLKID_TDMOUT_A_LRCLK] = &audio_tdmout_a_lrclk.hw,
>>>> + [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &audio_tdmout_b_sclk_mux.hw,
>>>> + [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &audio_tdmout_b_sclk_pre_en.hw,
>>>> + [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &audio_tdmout_b_sclk_post_en.hw,
>>>> + [AUD_CLKID_TDMOUT_B_SCLK] = &audio_tdmout_b_sclk.hw,
>>>> + [AUD_CLKID_TDMOUT_B_LRCLK] = &audio_tdmout_b_lrclk.hw,
>>>> +
>>>> + [AUD2_CLKID_DDR_ARB] = &audio2_ddr_arb.hw,
>>>> + [AUD2_CLKID_PDM] = &audio2_pdm.hw,
>>>> + [AUD2_CLKID_TDMIN_VAD] = &audio2_tdmin_vad.hw,
>>>> + [AUD2_CLKID_TODDR_VAD] = &audio2_toddr_vad.hw,
>>>> + [AUD2_CLKID_VAD] = &audio2_vad.hw,
>>>> + [AUD2_CLKID_AUDIOTOP] = &audio2_audiotop.hw,
>>>> + [AUD2_CLKID_VAD_MCLK_SEL] = &audio2_vad_mclk_mux.hw,
>>>> + [AUD2_CLKID_VAD_MCLK_DIV] = &audio2_vad_mclk_div.hw,
>>>> + [AUD2_CLKID_VAD_MCLK] = &audio2_vad_mclk.hw,
>>>> + [AUD2_CLKID_VAD_CLK_SEL] = &audio2_vad_clk_mux.hw,
>>>> + [AUD2_CLKID_VAD_CLK_DIV] = &audio2_vad_clk_div.hw,
>>>> + [AUD2_CLKID_VAD_CLK] = &audio2_vad_clk.hw,
>>>> + [AUD2_CLKID_PDM_DCLK_SEL] = &audio2_pdm_dclk_mux.hw,
>>>> + [AUD2_CLKID_PDM_DCLK_DIV] = &audio2_pdm_dclk_div.hw,
>>>> + [AUD2_CLKID_PDM_DCLK] = &audio2_pdm_dclk.hw,
>>>> + [AUD2_CLKID_PDM_SYSCLK_SEL] = &audio2_pdm_sysclk_mux.hw,
>>>> + [AUD2_CLKID_PDM_SYSCLK_DIV] = &audio2_pdm_sysclk_div.hw,
>>>> + [AUD2_CLKID_PDM_SYSCLK] = &audio2_pdm_sysclk.hw,
>>>> +};
>>>> +
>>>> +static struct meson_clk_hw_data a1_audio_clks = {
>>>> + .hws = a1_audio_hw_clks,
>>>> + .num = ARRAY_SIZE(a1_audio_hw_clks),
>>>> +};
>>>> +
>>>> +static struct regmap *a1_audio_map(struct platform_device *pdev,
>>>> + unsigned int index)
>>>> +{
>>>> + char name[32];
>>>> + const struct regmap_config cfg = {
>>>> + .reg_bits = 32,
>>>> + .val_bits = 32,
>>>> + .reg_stride = 4,
>>>> + .name = name,
>>>
>>> Not necessary
>>>
>>
>> This implementation uses two regmaps, and this field allow to avoid
>> errors like this:
>>
>> [ 0.145530] debugfs: Directory 'fe050000.audio-clock-controller' with
>> parent 'regmap' already present!
>>
>>>> + };
>>>> + void __iomem *base;
>>>> +
>>>> + base = devm_platform_ioremap_resource(pdev, index);
>>>> + if (IS_ERR(base))
>>>> + return base;
>>>> +
>>>> + scnprintf(name, sizeof(name), "%d", index);
>>>> + return devm_regmap_init_mmio(&pdev->dev, base, &cfg);
>>>> +}
>>>
>>> That is overengineered. Please keep it simple. Declare the regmap_config
>>> as static const global, and do it like axg-audio please.
>>>
>>
>> This only reason why it is not "static const" because I need to set
>> unique name for each regmap.
>>
>>>> +
>>>> +static int a1_register_clk(struct platform_device *pdev,
>>>> + struct regmap *map0, struct regmap *map1,
>>>> + struct clk_hw *hw)
>>>> +{
>>>> + struct clk_regmap *clk = container_of(hw, struct clk_regmap, hw);
>>>> +
>>>> + if (!hw)
>>>> + return 0;
>>>> +
>>>> + switch ((unsigned long)clk->map) {
>>>> + case AUDIO_RANGE_0:
>>>> + clk->map = map0;
>>>> + break;
>>>> + case AUDIO_RANGE_1:
>>>> + clk->map = map1;
>>>> + break;
>>>
>>> ... fishy
>>>
>>>> + default:
>>>> + WARN_ON(1);
>>>> + return -EINVAL;
>>>> + }
>>>> +
>>>> + return devm_clk_hw_register(&pdev->dev, hw);
>>>> +}
>>>> +
>>>> +static int a1_audio_clkc_probe(struct platform_device *pdev)
>>>> +{
>>>> + struct regmap *map0, *map1;
>>>> + struct clk *clk;
>>>> + unsigned int i;
>>>> + int ret;
>>>> +
>>>> + clk = devm_clk_get_enabled(&pdev->dev, "pclk");
>>>> + if (WARN_ON(IS_ERR(clk)))
>>>> + return PTR_ERR(clk);
>>>> +
>>>> + map0 = a1_audio_map(pdev, 0);
>>>> + if (IS_ERR(map0))
>>>> + return PTR_ERR(map0);
>>>> +
>>>> + map1 = a1_audio_map(pdev, 1);
>>>> + if (IS_ERR(map1))
>>>> + return PTR_ERR(map1);
>>>
>>> No - Looks to me you just have two clock controllers you are trying
>>> force into one.
>>>
>>
>> See the begining.
>>
>>>> +
>>>> + /*
>>>> + * Register and enable AUD2_CLKID_AUDIOTOP clock first. Unless
>>>> + * it is enabled any read/write to 'map0' hangs the CPU.
>>>> + */
>>>> +
>>>> + ret = a1_register_clk(pdev, map0, map1,
>>>> + a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + ret = clk_prepare_enable(a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]->clk);
>>>> + if (ret)
>>>> + return ret;
>>>
>>> Again, this shows 2 devices. The one related to your 'map0' should
>>> request AUD2_CLKID_AUDIOTOP as input and enable it right away.
>>>
>>
>> See the begining.
>>
>>>> +
>>>> + for (i = 0; i < a1_audio_clks.num; i++) {
>>>> + if (i == AUD2_CLKID_AUDIOTOP)
>>>> + continue;
>>>> +
>>>> + ret = a1_register_clk(pdev, map0, map1, a1_audio_clks.hws[i]);
>>>> + if (ret)
>>>> + return ret;
>>>> + }
>>>> +
>>>> + ret = devm_of_clk_add_hw_provider(&pdev->dev, meson_clk_hw_get,
>>>> + &a1_audio_clks);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + BUILD_BUG_ON((unsigned long)AUDIO_REG_MAP(AUDIO_SW_RESET0) !=
>>>> + AUDIO_RANGE_0);
>>>
>>> Why is that necessary ?
>>>
>>
>> A little paranoia. Here AUDIO_SW_RESET0 is handled as map0's register,
>> and I want to assert it.
>>
>>>> + return meson_audio_rstc_register(&pdev->dev, map0,
>>>> + AUDIO_REG_OFFSET(AUDIO_SW_RESET0), 32);
>>>> +}
>>>> +
>>>> +static const struct of_device_id a1_audio_clkc_match_table[] = {
>>>> + { .compatible = "amlogic,a1-audio-clkc", },
>>>> + {}
>>>> +};
>>>> +MODULE_DEVICE_TABLE(of, a1_audio_clkc_match_table);
>>>> +
>>>> +static struct platform_driver a1_audio_clkc_driver = {
>>>> + .probe = a1_audio_clkc_probe,
>>>> + .driver = {
>>>> + .name = "a1-audio-clkc",
>>>> + .of_match_table = a1_audio_clkc_match_table,
>>>> + },
>>>> +};
>>>> +module_platform_driver(a1_audio_clkc_driver);
>>>> +
>>>> +MODULE_DESCRIPTION("Amlogic A1 Audio Clock driver");
>>>> +MODULE_AUTHOR("Jan Dakinevich <[email protected]>");
>>>> +MODULE_LICENSE("GPL");
>>>> diff --git a/drivers/clk/meson/a1-audio.h b/drivers/clk/meson/a1-audio.h
>>>> new file mode 100644
>>>> index 000000000000..f994e87276cd
>>>> --- /dev/null
>>>> +++ b/drivers/clk/meson/a1-audio.h
>>>> @@ -0,0 +1,58 @@
>>>> +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
>>>> +/*
>>>> + * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
>>>> + *
>>>> + * Author: Jan Dakinevich <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef __A1_AUDIO_H
>>>> +#define __A1_AUDIO_H
>>>> +
>>>> +#define AUDIO_RANGE_0 0xa
>>>> +#define AUDIO_RANGE_1 0xb
>>>> +#define AUDIO_RANGE_SHIFT 16
>>>> +
>>>> +#define AUDIO_REG(_range, _offset) \
>>>> + (((_range) << AUDIO_RANGE_SHIFT) + (_offset))
>>>> +
>>>> +#define AUDIO_REG_OFFSET(_reg) \
>>>> + ((_reg) & ((1 << AUDIO_RANGE_SHIFT) - 1))
>>>> +
>>>> +#define AUDIO_REG_MAP(_reg) \
>>>> + ((void *)((_reg) >> AUDIO_RANGE_SHIFT))
>>>
>>> That is seriouly overengineered.
>>> The following are offset. Just write what they are.
>>>
>>
>> This is all in order to keep range's identifier together with offset and
>> then use it to store the identifier in clk_regmaps.
>>
>>> There is not reason to put that into a header. It is only going to be
>>> used by a single driver.
>>>>> +
>>>> +#define AUDIO_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_0, 0x000)
>>>> +#define AUDIO_MCLK_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x008)
>>>> +#define AUDIO_MCLK_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x00c)
>>>> +#define AUDIO_MCLK_C_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x010)
>>>> +#define AUDIO_MCLK_D_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x014)
>>>> +#define AUDIO_MCLK_E_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x018)
>>>> +#define AUDIO_MCLK_F_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x01c)
>>>> +#define AUDIO_SW_RESET0 AUDIO_REG(AUDIO_RANGE_0, 0x028)
>>>> +#define AUDIO_MST_A_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x040)
>>>> +#define AUDIO_MST_A_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x044)
>>>> +#define AUDIO_MST_B_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x048)
>>>> +#define AUDIO_MST_B_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x04c)
>>>> +#define AUDIO_MST_C_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x050)
>>>> +#define AUDIO_MST_C_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x054)
>>>> +#define AUDIO_MST_D_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x058)
>>>> +#define AUDIO_MST_D_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x05c)
>>>> +#define AUDIO_CLK_TDMIN_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x080)
>>>> +#define AUDIO_CLK_TDMIN_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x084)
>>>> +#define AUDIO_CLK_TDMIN_LB_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x08c)
>>>> +#define AUDIO_CLK_TDMOUT_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x090)
>>>> +#define AUDIO_CLK_TDMOUT_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x094)
>>>> +#define AUDIO_CLK_SPDIFIN_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x09c)
>>>> +#define AUDIO_CLK_RESAMPLE_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a4)
>>>> +#define AUDIO_CLK_LOCKER_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a8)
>>>> +#define AUDIO_CLK_EQDRC_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0c0)
>>>> +
>>>> +#define AUDIO2_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_1, 0x00c)
>>>> +#define AUDIO2_MCLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x040)
>>>> +#define AUDIO2_CLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x044)
>>>> +#define AUDIO2_CLK_PDMIN_CTRL0 AUDIO_REG(AUDIO_RANGE_1, 0x058)
>>>> +#define AUDIO2_CLK_PDMIN_CTRL1 AUDIO_REG(AUDIO_RANGE_1, 0x05c)
>>>> +
>>>> +#include <dt-bindings/clock/amlogic,a1-audio-clkc.h>
>>>> +
>>>> +#endif /* __A1_AUDIO_H */
>>>
>>>
>
>

--
Best regards
Jan Dakinevich

2024-03-24 17:59:42

by Jan Dakinevich

[permalink] [raw]
Subject: Re: [PATCH 06/25] ASoC: meson: g12a-toacodec: fix "Lane Select" width

Sorry, I screwed up. Original code is correct. I mixed up the maximum
value and bit offset.

On 3/15/24 02:21, Jan Dakinevich wrote:
> For both G12A and SM1 the width of "Lane Select" should be 2, not 3.
> Otherwise, it overlaps with "Source".
>
> Signed-off-by: Jan Dakinevich <[email protected]>
> ---
> sound/soc/meson/g12a-toacodec.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/sound/soc/meson/g12a-toacodec.c b/sound/soc/meson/g12a-toacodec.c
> index 531bb8707a3e..b92de2235627 100644
> --- a/sound/soc/meson/g12a-toacodec.c
> +++ b/sound/soc/meson/g12a-toacodec.c
> @@ -229,11 +229,11 @@ static const struct snd_soc_dapm_route g12a_toacodec_routes[] = {
> };
>
> static const struct snd_kcontrol_new g12a_toacodec_controls[] = {
> - SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL, 3, 0),
> + SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL, 2, 0),
> };
>
> static const struct snd_kcontrol_new sm1_toacodec_controls[] = {
> - SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL_SM1, 3, 0),
> + SOC_SINGLE("Lane Select", TOACODEC_CTRL0, CTRL0_LANE_SEL_SM1, 2, 0),
> };
>
> static const struct snd_soc_component_driver g12a_toacodec_component_drv = {

--
Best regards
Jan Dakinevich

2024-03-26 15:53:06

by Jerome Brunet

[permalink] [raw]
Subject: Re: [PATCH 04/25] clk: meson: a1: add the audio clock controller driver


On Sat 23 Mar 2024 at 21:02, Jan Dakinevich <[email protected]> wrote:

> Jerome, I have reworked my driver reusing axg-audio code as most as I
> could and now I have one more question. Lets see on this definition from
> axg-audio:
>
> #define AUD_MST_MUX(_name, _reg, _flag) \
> AUD_MUX(_name##_sel, _reg, 0x7, 24, _flag, \
> mst_mux_parent_data, 0)
>
> #define AUD_MST_MCLK_MUX(_name, _reg) \
> AUD_MST_MUX(_name, _reg, CLK_MUX_ROUND_CLOSEST)
>
> CLK_SET_RATE_PARENT is not set here. But why? It means, that topmost pll
> clock will not be reconfigured at runtime to satisfy the rate that was
> requested from axg-tdm.
>

Yes, that is by design. It is another area where mainline audio differs
greatly from AML vendor code. The PLLs are expected be to fixed and the
audio master clock will reparent to the most adequate PLL source
depending on the use case.

This is how we manage to satisfy all audio interfaces with a very
limited number of PLLs

On AXG/G12 there is at most 6 concurrent interfaces (3 FRDDR/TODDR) - 8
on sm1 - and we can satisfy on that with 3 PLLs. That would not be
possible if interfaces were having their way with the PLLs, reseting it
everytime a stream is started.

The PLL rate should be carefully chosen so it can be derived easily. On
AXG/G12/SM1 that is:
* one PLL per rate family, to maximize clock precision
* x24 x32: to handle different sample sizes
* x2 until we reach the PLL limits to allow higher rates such as 384kHz
or even higher

If you have less PLLs on A1, you'll have to make compromises, like a less
precise clock to support multiple family with one PLL.
This is why the PLLs are set for each platform in DT because that choice
may depend on the platform use case.

>
> On 3/19/24 11:30, Jerome Brunet wrote:
>>
>> On Tue 19 Mar 2024 at 04:47, Jan Dakinevich <[email protected]> wrote:
>>
>>> Let's start from the end:
>>>
>>>> No - Looks to me you just have two clock controllers you are trying
>>> force into one.
>>>
>>>> Again, this shows 2 devices. The one related to your 'map0' should
>>> request AUD2_CLKID_AUDIOTOP as input and enable it right away.
>>>
>>> Most of fishy workarounds that you commented is caused the fact the mmio
>>> of this clock controller is divided into two parts. Compare it with
>>> axg-audio driver, things that was part of contigous memory region (like
>>> pdm) here are moved to second region. Is this enough to make a guess
>>> that these are two devices?
>>
>> I see obsolutely no reason to think it is a single device nor to add all the quirks
>> you have the way you did. So yes, in that case, 2 zones, 2 devices.
>>
>>>
>>> Concerning AUD2_CLKID_AUDIOTOP clock, as it turned out, it must be
>>> enabled before enabling of clocks from second region too. That is
>>> AUD2_CLKID_AUDIOTOP clock feeds both parts of this clock controller.
>>>
>>
>> Yes. I understood the first time around and already commented on that.
>>
>>>
>>> On 3/15/24 12:20, Jerome Brunet wrote:
>>>>
>>>> On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <[email protected]> wrote:
>>>>
>>>>> This controller provides clocks and reset functionality for audio
>>>>> peripherals on Amlogic A1 SoC family.
>>>>>
>>>>> The driver is almost identical to 'axg-audio', however it would be better
>>>>> to keep it separate due to following reasons:
>>>>>
>>>>> - significant amount of bits has another definition. I will bring there
>>>>> a mess of new defines with A1_ suffixes.
>>>>>
>>>>> - registers of this controller are located in two separate regions. It
>>>>> will give a lot of complications for 'axg-audio' to support this.
>>>>>
>>>>> Signed-off-by: Jan Dakinevich <[email protected]>
>>>>> ---
>>>>> drivers/clk/meson/Kconfig | 13 +
>>>>> drivers/clk/meson/Makefile | 1 +
>>>>> drivers/clk/meson/a1-audio.c | 556 +++++++++++++++++++++++++++++++++++
>>>>> drivers/clk/meson/a1-audio.h | 58 ++++
>>>>> 4 files changed, 628 insertions(+)
>>>>> create mode 100644 drivers/clk/meson/a1-audio.c
>>>>> create mode 100644 drivers/clk/meson/a1-audio.h
>>>>>
>>>>> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
>>>>> index d6a2fa5f7e88..80c4a18c83d2 100644
>>>>> --- a/drivers/clk/meson/Kconfig
>>>>> +++ b/drivers/clk/meson/Kconfig
>>>>> @@ -133,6 +133,19 @@ config COMMON_CLK_A1_PERIPHERALS
>>>>> device, A1 SoC Family. Say Y if you want A1 Peripherals clock
>>>>> controller to work.
>>>>>
>>>>> +config COMMON_CLK_A1_AUDIO
>>>>> + tristate "Amlogic A1 SoC Audio clock controller support"
>>>>> + depends on ARM64
>>>>> + select COMMON_CLK_MESON_REGMAP
>>>>> + select COMMON_CLK_MESON_CLKC_UTILS
>>>>> + select COMMON_CLK_MESON_PHASE
>>>>> + select COMMON_CLK_MESON_SCLK_DIV
>>>>> + select COMMON_CLK_MESON_AUDIO_RSTC
>>>>> + help
>>>>> + Support for the Audio clock controller on Amlogic A113L based
>>>>> + device, A1 SoC Family. Say Y if you want A1 Audio clock controller
>>>>> + to work.
>>>>> +
>>>>> config COMMON_CLK_G12A
>>>>> tristate "G12 and SM1 SoC clock controllers support"
>>>>> depends on ARM64
>>>>> diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
>>>>> index 88d94921a4dc..4968fc7ad555 100644
>>>>> --- a/drivers/clk/meson/Makefile
>>>>> +++ b/drivers/clk/meson/Makefile
>>>>> @@ -20,6 +20,7 @@ obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
>>>>> obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
>>>>> obj-$(CONFIG_COMMON_CLK_A1_PLL) += a1-pll.o
>>>>> obj-$(CONFIG_COMMON_CLK_A1_PERIPHERALS) += a1-peripherals.o
>>>>> +obj-$(CONFIG_COMMON_CLK_A1_AUDIO) += a1-audio.o
>>>>> obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
>>>>> obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o
>>>>> obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o
>>>>> diff --git a/drivers/clk/meson/a1-audio.c b/drivers/clk/meson/a1-audio.c
>>>>> new file mode 100644
>>>>> index 000000000000..6039116c93ba
>>>>> --- /dev/null
>>>>> +++ b/drivers/clk/meson/a1-audio.c
>>>>> @@ -0,0 +1,556 @@
>>>>> +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
>>>>> +/*
>>>>> + * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
>>>>> + *
>>>>> + * Author: Jan Dakinevich <[email protected]>
>>>>> + */
>>>>> +
>>>>> +#include <linux/clk.h>
>>>>> +#include <linux/clk-provider.h>
>>>>> +#include <linux/init.h>
>>>>> +#include <linux/of_device.h>
>>>>> +#include <linux/module.h>
>>>>> +#include <linux/platform_device.h>
>>>>> +#include <linux/regmap.h>
>>>>> +#include <linux/reset.h>
>>>>> +#include <linux/reset-controller.h>
>>>>> +#include <linux/slab.h>
>>>>> +
>>>>> +#include "meson-clkc-utils.h"
>>>>> +#include "meson-audio-rstc.h"
>>>>> +#include "clk-regmap.h"
>>>>> +#include "clk-phase.h"
>>>>> +#include "sclk-div.h"
>>>>> +#include "a1-audio.h"
>>>>> +
>>>>> +#define AUDIO_PDATA(_name) \
>>>>> + ((const struct clk_parent_data[]) { { .hw = &(_name).hw } })
>>>>
>>>> Not a fan - yet another level of macro.
>>>>
>>>>> +
>>>>> +#define AUDIO_MUX(_name, _reg, _mask, _shift, _pdata) \
>>>>> +static struct clk_regmap _name = { \
>>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>>> + .data = &(struct clk_regmap_mux_data){ \
>>>>> + .offset = AUDIO_REG_OFFSET(_reg), \
>>>>> + .mask = (_mask), \
>>>>> + .shift = (_shift), \
>>>>> + }, \
>>>>> + .hw.init = &(struct clk_init_data) { \
>>>>> + .name = #_name, \
>>>>> + .ops = &clk_regmap_mux_ops, \
>>>>> + .parent_data = (_pdata), \
>>>>> + .num_parents = ARRAY_SIZE(_pdata), \
>>>>> + .flags = CLK_SET_RATE_PARENT, \
>>>>> + }, \
>>>>> +}
>>>>> +
>>>>> +#define AUDIO_DIV(_name, _reg, _shift, _width, _pdata) \
>>>>> +static struct clk_regmap _name = { \
>>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>>> + .data = &(struct clk_regmap_div_data){ \
>>>>> + .offset = AUDIO_REG_OFFSET(_reg), \
>>>>> + .shift = (_shift), \
>>>>> + .width = (_width), \
>>>>> + }, \
>>>>> + .hw.init = &(struct clk_init_data) { \
>>>>> + .name = #_name, \
>>>>> + .ops = &clk_regmap_divider_ops, \
>>>>> + .parent_data = (_pdata), \
>>>>> + .num_parents = 1, \
>>>>> + .flags = CLK_SET_RATE_PARENT, \
>>>>> + }, \
>>>>> +}
>>>>> +
>>>>> +#define AUDIO_GATE(_name, _reg, _bit, _pdata) \
>>>>> +static struct clk_regmap _name = { \
>>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>>> + .data = &(struct clk_regmap_gate_data){ \
>>>>> + .offset = AUDIO_REG_OFFSET(_reg), \
>>>>> + .bit_idx = (_bit), \
>>>>> + }, \
>>>>> + .hw.init = &(struct clk_init_data) { \
>>>>> + .name = #_name, \
>>>>> + .ops = &clk_regmap_gate_ops, \
>>>>> + .parent_data = (_pdata), \
>>>>> + .num_parents = 1, \
>>>>> + .flags = CLK_SET_RATE_PARENT, \
>>>>> + }, \
>>>>> +}
>>>>> +
>>>>> +#define AUDIO_SCLK_DIV(_name, _reg, _div_shift, _div_width, \
>>>>> + _hi_shift, _hi_width, _pdata, _set_rate_parent) \
>>>>> +static struct clk_regmap _name = { \
>>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>>> + .data = &(struct meson_sclk_div_data) { \
>>>>> + .div = { \
>>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>>> + .shift = (_div_shift), \
>>>>> + .width = (_div_width), \
>>>>> + }, \
>>>>> + .hi = { \
>>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>>> + .shift = (_hi_shift), \
>>>>> + .width = (_hi_width), \
>>>>> + }, \
>>>>> + }, \
>>>>> + .hw.init = &(struct clk_init_data) { \
>>>>> + .name = #_name, \
>>>>> + .ops = &meson_sclk_div_ops, \
>>>>> + .parent_data = (_pdata), \
>>>>> + .num_parents = 1, \
>>>>> + .flags = (_set_rate_parent) ? CLK_SET_RATE_PARENT : 0, \
>>>>
>>>> Does not help readeability. Just pass the flag as axg-audio does.
>>>>
>>>>> + }, \
>>>>> +}
>>>>> +
>>>>> +#define AUDIO_TRIPHASE(_name, _reg, _width, _shift0, _shift1, _shift2, \
>>>>> + _pdata) \
>>>>> +static struct clk_regmap _name = { \
>>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>>> + .data = &(struct meson_clk_triphase_data) { \
>>>>> + .ph0 = { \
>>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>>> + .shift = (_shift0), \
>>>>> + .width = (_width), \
>>>>> + }, \
>>>>> + .ph1 = { \
>>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>>> + .shift = (_shift1), \
>>>>> + .width = (_width), \
>>>>> + }, \
>>>>> + .ph2 = { \
>>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>>> + .shift = (_shift2), \
>>>>> + .width = (_width), \
>>>>> + }, \
>>>>> + }, \
>>>>> + .hw.init = &(struct clk_init_data) { \
>>>>> + .name = #_name, \
>>>>> + .ops = &meson_clk_triphase_ops, \
>>>>> + .parent_data = (_pdata), \
>>>>> + .num_parents = 1, \
>>>>> + .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \
>>>>> + }, \
>>>>> +}
>>>>> +
>>>>> +#define AUDIO_SCLK_WS(_name, _reg, _width, _shift_ph, _shift_ws, \
>>>>> + _pdata) \
>>>>> +static struct clk_regmap _name = { \
>>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>>> + .data = &(struct meson_sclk_ws_inv_data) { \
>>>>> + .ph = { \
>>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>>> + .shift = (_shift_ph), \
>>>>> + .width = (_width), \
>>>>> + }, \
>>>>> + .ws = { \
>>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>>> + .shift = (_shift_ws), \
>>>>> + .width = (_width), \
>>>>> + }, \
>>>>> + }, \
>>>>> + .hw.init = &(struct clk_init_data) { \
>>>>> + .name = #_name, \
>>>>> + .ops = &meson_sclk_ws_inv_ops, \
>>>>> + .parent_data = (_pdata), \
>>>>> + .num_parents = 1, \
>>>>> + .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \
>>>>> + }, \
>>>>> +}
>>>>
>>>> All the above does essentially the same things as the macro of
>>>> axg-audio, to some minor differences. Yet it is another set to maintain.
>>>>
>>>
>>> Except one thing... Here I keep memory identifier to which this clock
>>> belongs:
>>>
>>> .map = AUDIO_REG_MAP(_reg),
>>>
>>> It is workaround, but ->map the only common field in clk_regmap that
>>> could be used for this purpose.
>>>
>>>
>>>> I'd much prefer if you put the axg-audio macro in a header a re-used
>>>> those. There would a single set to maintain. You may then specialize the
>>>> included in the driver C file, to avoid redundant parameters
>>>>
>>>> Rework axg-audio to use clk_parent_data if you must, but not in the same
>>>> series please.
>>>>
>>>>> +
>>>>> +static const struct clk_parent_data a1_pclk_pdata[] = {
>>>>> + { .fw_name = "pclk", },
>>>>> +};
>>>>> +
>>>>> +AUDIO_GATE(audio_ddr_arb, AUDIO_CLK_GATE_EN0, 0, a1_pclk_pdata);
>>>>> +AUDIO_GATE(audio_tdmin_a, AUDIO_CLK_GATE_EN0, 1, a1_pclk_pdata);
>>>>> +AUDIO_GATE(audio_tdmin_b, AUDIO_CLK_GATE_EN0, 2, a1_pclk_pdata);
>>>>> +AUDIO_GATE(audio_tdmin_lb, AUDIO_CLK_GATE_EN0, 3, a1_pclk_pdata);
>>>>> +AUDIO_GATE(audio_loopback, AUDIO_CLK_GATE_EN0, 4, a1_pclk_pdata);
>>>>> +AUDIO_GATE(audio_tdmout_a, AUDIO_CLK_GATE_EN0, 5, a1_pclk_pdata);
>>>>> +AUDIO_GATE(audio_tdmout_b, AUDIO_CLK_GATE_EN0, 6, a1_pclk_pdata);
>>>>> +AUDIO_GATE(audio_frddr_a, AUDIO_CLK_GATE_EN0, 7, a1_pclk_pdata);
>>>>> +AUDIO_GATE(audio_frddr_b, AUDIO_CLK_GATE_EN0, 8, a1_pclk_pdata);
>>>>> +AUDIO_GATE(audio_toddr_a, AUDIO_CLK_GATE_EN0, 9, a1_pclk_pdata);
>>>>> +AUDIO_GATE(audio_toddr_b, AUDIO_CLK_GATE_EN0, 10, a1_pclk_pdata);
>>>>> +AUDIO_GATE(audio_spdifin, AUDIO_CLK_GATE_EN0, 11, a1_pclk_pdata);
>>>>> +AUDIO_GATE(audio_resample, AUDIO_CLK_GATE_EN0, 12, a1_pclk_pdata);
>>>>> +AUDIO_GATE(audio_eqdrc, AUDIO_CLK_GATE_EN0, 13, a1_pclk_pdata);
>>>>> +AUDIO_GATE(audio_audiolocker, AUDIO_CLK_GATE_EN0, 14, a1_pclk_pdata);
>>>> This is what I mean by redundant parameter ^
>>>>
>>>
>>> Yep. I could define something like AUDIO_PCLK_GATE().
>>>
>>>>> +
>>>>> +AUDIO_GATE(audio2_ddr_arb, AUDIO2_CLK_GATE_EN0, 0, a1_pclk_pdata);
>>>>> +AUDIO_GATE(audio2_pdm, AUDIO2_CLK_GATE_EN0, 1, a1_pclk_pdata);
>>>>> +AUDIO_GATE(audio2_tdmin_vad, AUDIO2_CLK_GATE_EN0, 2, a1_pclk_pdata);
>>>>> +AUDIO_GATE(audio2_toddr_vad, AUDIO2_CLK_GATE_EN0, 3, a1_pclk_pdata);
>>>>> +AUDIO_GATE(audio2_vad, AUDIO2_CLK_GATE_EN0, 4, a1_pclk_pdata);
>>>>> +AUDIO_GATE(audio2_audiotop, AUDIO2_CLK_GATE_EN0, 7, a1_pclk_pdata);
>>>>> +
>>>>> +static const struct clk_parent_data a1_mst_pdata[] = {
>>>>> + { .fw_name = "dds_in" },
>>>>> + { .fw_name = "fclk_div2" },
>>>>> + { .fw_name = "fclk_div3" },
>>>>> + { .fw_name = "hifi_pll" },
>>>>> + { .fw_name = "xtal" },
>>>>> +};
>>>>> +
>>>>> +#define AUDIO_MST_MCLK(_name, _reg) \
>>>>> + AUDIO_MUX(_name##_mux, (_reg), 0x7, 24, a1_mst_pdata); \
>>>>> + AUDIO_DIV(_name##_div, (_reg), 0, 16, \
>>>>> + AUDIO_PDATA(_name##_mux)); \
>>>>> + AUDIO_GATE(_name, (_reg), 31, AUDIO_PDATA(_name##_div))
>>>>> +
>>>>> +AUDIO_MST_MCLK(audio_mst_a_mclk, AUDIO_MCLK_A_CTRL);
>>>>> +AUDIO_MST_MCLK(audio_mst_b_mclk, AUDIO_MCLK_B_CTRL);
>>>>> +AUDIO_MST_MCLK(audio_mst_c_mclk, AUDIO_MCLK_C_CTRL);
>>>>> +AUDIO_MST_MCLK(audio_mst_d_mclk, AUDIO_MCLK_D_CTRL);
>>>>> +AUDIO_MST_MCLK(audio_spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL);
>>>>> +AUDIO_MST_MCLK(audio_eqdrc_clk, AUDIO_CLK_EQDRC_CTRL);
>>>>> +
>>>>> +AUDIO_MUX(audio_resample_clk_mux, AUDIO_CLK_RESAMPLE_CTRL, 0xf, 24,
>>>>> + a1_mst_pdata);
>>>>> +AUDIO_DIV(audio_resample_clk_div, AUDIO_CLK_RESAMPLE_CTRL, 0, 8,
>>>>> + AUDIO_PDATA(audio_resample_clk_mux));
>>>>> +AUDIO_GATE(audio_resample_clk, AUDIO_CLK_RESAMPLE_CTRL, 31,
>>>>> + AUDIO_PDATA(audio_resample_clk_div));
>>>>> +
>>>>> +AUDIO_MUX(audio_locker_in_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 8,
>>>>> + a1_mst_pdata);
>>>>> +AUDIO_DIV(audio_locker_in_clk_div, AUDIO_CLK_LOCKER_CTRL, 0, 8,
>>>>> + AUDIO_PDATA(audio_locker_in_clk_mux));
>>>>> +AUDIO_GATE(audio_locker_in_clk, AUDIO_CLK_LOCKER_CTRL, 15,
>>>>> + AUDIO_PDATA(audio_locker_in_clk_div));
>>>>> +
>>>>> +AUDIO_MUX(audio_locker_out_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 24,
>>>>> + a1_mst_pdata);
>>>>> +AUDIO_DIV(audio_locker_out_clk_div, AUDIO_CLK_LOCKER_CTRL, 16, 8,
>>>>> + AUDIO_PDATA(audio_locker_out_clk_mux));
>>>>> +AUDIO_GATE(audio_locker_out_clk, AUDIO_CLK_LOCKER_CTRL, 31,
>>>>> + AUDIO_PDATA(audio_locker_out_clk_div));
>>>>> +
>>>>> +AUDIO_MST_MCLK(audio2_vad_mclk, AUDIO2_MCLK_VAD_CTRL);
>>>>> +AUDIO_MST_MCLK(audio2_vad_clk, AUDIO2_CLK_VAD_CTRL);
>>>>> +AUDIO_MST_MCLK(audio2_pdm_dclk, AUDIO2_CLK_PDMIN_CTRL0);
>>>>> +AUDIO_MST_MCLK(audio2_pdm_sysclk, AUDIO2_CLK_PDMIN_CTRL1);
>>>>> +
>>>>> +#define AUDIO_MST_SCLK(_name, _reg0, _reg1, _pdata) \
>>>>> + AUDIO_GATE(_name##_pre_en, (_reg0), 31, (_pdata)); \
>>>>> + AUDIO_SCLK_DIV(_name##_div, (_reg0), 20, 10, 0, 0, \
>>>>> + AUDIO_PDATA(_name##_pre_en), true); \
>>>>> + AUDIO_GATE(_name##_post_en, (_reg0), 30, \
>>>>> + AUDIO_PDATA(_name##_div)); \
>>>>> + AUDIO_TRIPHASE(_name, (_reg1), 1, 0, 2, 4, \
>>>>> + AUDIO_PDATA(_name##_post_en))
>>>>> +
>>>>
>>>> Again, I'm not a fan of this many levels of macro. I can live with it
>>>> but certainly don't want the burden of reviewing and maintaining for
>>>> clock driver. AXG / G12 and A1 are obviously closely related, so make it common.
>>>>
>>>>> +#define AUDIO_MST_LRCLK(_name, _reg0, _reg1, _pdata) \
>>>>> + AUDIO_SCLK_DIV(_name##_div, (_reg0), 0, 10, 10, 10, \
>>>>> + (_pdata), false); \
>>>>> + AUDIO_TRIPHASE(_name, (_reg1), 1, 1, 3, 5, \
>>>>> + AUDIO_PDATA(_name##_div))
>>>>> +
>>>>> +AUDIO_MST_SCLK(audio_mst_a_sclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1,
>>>>> + AUDIO_PDATA(audio_mst_a_mclk));
>>>>> +AUDIO_MST_SCLK(audio_mst_b_sclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1,
>>>>> + AUDIO_PDATA(audio_mst_b_mclk));
>>>>> +AUDIO_MST_SCLK(audio_mst_c_sclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1,
>>>>> + AUDIO_PDATA(audio_mst_c_mclk));
>>>>> +AUDIO_MST_SCLK(audio_mst_d_sclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1,
>>>>> + AUDIO_PDATA(audio_mst_d_mclk));
>>>>> +
>>>>> +AUDIO_MST_LRCLK(audio_mst_a_lrclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1,
>>>>> + AUDIO_PDATA(audio_mst_a_sclk_post_en));
>>>>> +AUDIO_MST_LRCLK(audio_mst_b_lrclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1,
>>>>> + AUDIO_PDATA(audio_mst_b_sclk_post_en));
>>>>> +AUDIO_MST_LRCLK(audio_mst_c_lrclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1,
>>>>> + AUDIO_PDATA(audio_mst_c_sclk_post_en));
>>>>> +AUDIO_MST_LRCLK(audio_mst_d_lrclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1,
>>>>> + AUDIO_PDATA(audio_mst_d_sclk_post_en));
>>>>> +
>>>>> +static const struct clk_parent_data a1_mst_sclk_pdata[] = {
>>>>> + { .hw = &audio_mst_a_sclk.hw },
>>>>> + { .hw = &audio_mst_b_sclk.hw },
>>>>> + { .hw = &audio_mst_c_sclk.hw },
>>>>> + { .hw = &audio_mst_d_sclk.hw },
>>>>> + { .fw_name = "slv_sclk0" },
>>>>> + { .fw_name = "slv_sclk1" },
>>>>> + { .fw_name = "slv_sclk2" },
>>>>> + { .fw_name = "slv_sclk3" },
>>>>> + { .fw_name = "slv_sclk4" },
>>>>> + { .fw_name = "slv_sclk5" },
>>>>> + { .fw_name = "slv_sclk6" },
>>>>> + { .fw_name = "slv_sclk7" },
>>>>> + { .fw_name = "slv_sclk8" },
>>>>> + { .fw_name = "slv_sclk9" },
>>>>> +};
>>>>> +
>>>>> +static const struct clk_parent_data a1_mst_lrclk_pdata[] = {
>>>>> + { .hw = &audio_mst_a_lrclk.hw },
>>>>> + { .hw = &audio_mst_b_lrclk.hw },
>>>>> + { .hw = &audio_mst_c_lrclk.hw },
>>>>> + { .hw = &audio_mst_d_lrclk.hw },
>>>>> + { .fw_name = "slv_lrclk0" },
>>>>> + { .fw_name = "slv_lrclk1" },
>>>>> + { .fw_name = "slv_lrclk2" },
>>>>> + { .fw_name = "slv_lrclk3" },
>>>>> + { .fw_name = "slv_lrclk4" },
>>>>> + { .fw_name = "slv_lrclk5" },
>>>>> + { .fw_name = "slv_lrclk6" },
>>>>> + { .fw_name = "slv_lrclk7" },
>>>>> + { .fw_name = "slv_lrclk8" },
>>>>> + { .fw_name = "slv_lrclk9" },
>>>>> +};
>>>>> +
>>>>> +#define AUDIO_TDM_SCLK(_name, _reg) \
>>>>> + AUDIO_MUX(_name##_mux, (_reg), 0xf, 24, a1_mst_sclk_pdata); \
>>>>> + AUDIO_GATE(_name##_pre_en, (_reg), 31, \
>>>>> + AUDIO_PDATA(_name##_mux)); \
>>>>> + AUDIO_GATE(_name##_post_en, (_reg), 30, \
>>>>> + AUDIO_PDATA(_name##_pre_en)); \
>>>>> + AUDIO_SCLK_WS(_name, (_reg), 1, 29, 28, \
>>>>> + AUDIO_PDATA(_name##_post_en))
>>>>> +
>>>>> +#define AUDIO_TDM_LRCLK(_name, _reg) \
>>>>> + AUDIO_MUX(_name, (_reg), 0xf, 20, a1_mst_lrclk_pdata)
>>>>> +
>>>>> +AUDIO_TDM_SCLK(audio_tdmin_a_sclk, AUDIO_CLK_TDMIN_A_CTRL);
>>>>> +AUDIO_TDM_SCLK(audio_tdmin_b_sclk, AUDIO_CLK_TDMIN_B_CTRL);
>>>>> +AUDIO_TDM_SCLK(audio_tdmin_lb_sclk, AUDIO_CLK_TDMIN_LB_CTRL);
>>>>> +AUDIO_TDM_SCLK(audio_tdmout_a_sclk, AUDIO_CLK_TDMOUT_A_CTRL);
>>>>> +AUDIO_TDM_SCLK(audio_tdmout_b_sclk, AUDIO_CLK_TDMOUT_B_CTRL);
>>>>> +
>>>>> +AUDIO_TDM_LRCLK(audio_tdmin_a_lrclk, AUDIO_CLK_TDMIN_A_CTRL);
>>>>> +AUDIO_TDM_LRCLK(audio_tdmin_b_lrclk, AUDIO_CLK_TDMIN_B_CTRL);
>>>>> +AUDIO_TDM_LRCLK(audio_tdmin_lb_lrclk, AUDIO_CLK_TDMIN_LB_CTRL);
>>>>> +AUDIO_TDM_LRCLK(audio_tdmout_a_lrclk, AUDIO_CLK_TDMOUT_A_CTRL);
>>>>> +AUDIO_TDM_LRCLK(audio_tdmout_b_lrclk, AUDIO_CLK_TDMOUT_B_CTRL);
>>>>> +
>>>>> +static struct clk_hw *a1_audio_hw_clks[] = {
>>>>> + [AUD_CLKID_DDR_ARB] = &audio_ddr_arb.hw,
>>>>> + [AUD_CLKID_TDMIN_A] = &audio_tdmin_a.hw,
>>>>> + [AUD_CLKID_TDMIN_B] = &audio_tdmin_b.hw,
>>>>> + [AUD_CLKID_TDMIN_LB] = &audio_tdmin_lb.hw,
>>>>> + [AUD_CLKID_LOOPBACK] = &audio_loopback.hw,
>>>>> + [AUD_CLKID_TDMOUT_A] = &audio_tdmout_a.hw,
>>>>> + [AUD_CLKID_TDMOUT_B] = &audio_tdmout_b.hw,
>>>>> + [AUD_CLKID_FRDDR_A] = &audio_frddr_a.hw,
>>>>> + [AUD_CLKID_FRDDR_B] = &audio_frddr_b.hw,
>>>>> + [AUD_CLKID_TODDR_A] = &audio_toddr_a.hw,
>>>>> + [AUD_CLKID_TODDR_B] = &audio_toddr_b.hw,
>>>>> + [AUD_CLKID_SPDIFIN] = &audio_spdifin.hw,
>>>>> + [AUD_CLKID_RESAMPLE] = &audio_resample.hw,
>>>>> + [AUD_CLKID_EQDRC] = &audio_eqdrc.hw,
>>>>> + [AUD_CLKID_LOCKER] = &audio_audiolocker.hw,
>>>>> + [AUD_CLKID_MST_A_MCLK_SEL] = &audio_mst_a_mclk_mux.hw,
>>>>> + [AUD_CLKID_MST_A_MCLK_DIV] = &audio_mst_a_mclk_div.hw,
>>>>> + [AUD_CLKID_MST_A_MCLK] = &audio_mst_a_mclk.hw,
>>>>> + [AUD_CLKID_MST_B_MCLK_SEL] = &audio_mst_b_mclk_mux.hw,
>>>>> + [AUD_CLKID_MST_B_MCLK_DIV] = &audio_mst_b_mclk_div.hw,
>>>>> + [AUD_CLKID_MST_B_MCLK] = &audio_mst_b_mclk.hw,
>>>>> + [AUD_CLKID_MST_C_MCLK_SEL] = &audio_mst_c_mclk_mux.hw,
>>>>> + [AUD_CLKID_MST_C_MCLK_DIV] = &audio_mst_c_mclk_div.hw,
>>>>> + [AUD_CLKID_MST_C_MCLK] = &audio_mst_c_mclk.hw,
>>>>> + [AUD_CLKID_MST_D_MCLK_SEL] = &audio_mst_d_mclk_mux.hw,
>>>>> + [AUD_CLKID_MST_D_MCLK_DIV] = &audio_mst_d_mclk_div.hw,
>>>>> + [AUD_CLKID_MST_D_MCLK] = &audio_mst_d_mclk.hw,
>>>>> + [AUD_CLKID_RESAMPLE_CLK_SEL] = &audio_resample_clk_mux.hw,
>>>>> + [AUD_CLKID_RESAMPLE_CLK_DIV] = &audio_resample_clk_div.hw,
>>>>> + [AUD_CLKID_RESAMPLE_CLK] = &audio_resample_clk.hw,
>>>>> + [AUD_CLKID_LOCKER_IN_CLK_SEL] = &audio_locker_in_clk_mux.hw,
>>>>> + [AUD_CLKID_LOCKER_IN_CLK_DIV] = &audio_locker_in_clk_div.hw,
>>>>> + [AUD_CLKID_LOCKER_IN_CLK] = &audio_locker_in_clk.hw,
>>>>> + [AUD_CLKID_LOCKER_OUT_CLK_SEL] = &audio_locker_out_clk_mux.hw,
>>>>> + [AUD_CLKID_LOCKER_OUT_CLK_DIV] = &audio_locker_out_clk_div.hw,
>>>>> + [AUD_CLKID_LOCKER_OUT_CLK] = &audio_locker_out_clk.hw,
>>>>> + [AUD_CLKID_SPDIFIN_CLK_SEL] = &audio_spdifin_clk_mux.hw,
>>>>> + [AUD_CLKID_SPDIFIN_CLK_DIV] = &audio_spdifin_clk_div.hw,
>>>>> + [AUD_CLKID_SPDIFIN_CLK] = &audio_spdifin_clk.hw,
>>>>> + [AUD_CLKID_EQDRC_CLK_SEL] = &audio_eqdrc_clk_mux.hw,
>>>>> + [AUD_CLKID_EQDRC_CLK_DIV] = &audio_eqdrc_clk_div.hw,
>>>>> + [AUD_CLKID_EQDRC_CLK] = &audio_eqdrc_clk.hw,
>>>>> + [AUD_CLKID_MST_A_SCLK_PRE_EN] = &audio_mst_a_sclk_pre_en.hw,
>>>>> + [AUD_CLKID_MST_A_SCLK_DIV] = &audio_mst_a_sclk_div.hw,
>>>>> + [AUD_CLKID_MST_A_SCLK_POST_EN] = &audio_mst_a_sclk_post_en.hw,
>>>>> + [AUD_CLKID_MST_A_SCLK] = &audio_mst_a_sclk.hw,
>>>>> + [AUD_CLKID_MST_B_SCLK_PRE_EN] = &audio_mst_b_sclk_pre_en.hw,
>>>>> + [AUD_CLKID_MST_B_SCLK_DIV] = &audio_mst_b_sclk_div.hw,
>>>>> + [AUD_CLKID_MST_B_SCLK_POST_EN] = &audio_mst_b_sclk_post_en.hw,
>>>>> + [AUD_CLKID_MST_B_SCLK] = &audio_mst_b_sclk.hw,
>>>>> + [AUD_CLKID_MST_C_SCLK_PRE_EN] = &audio_mst_c_sclk_pre_en.hw,
>>>>> + [AUD_CLKID_MST_C_SCLK_DIV] = &audio_mst_c_sclk_div.hw,
>>>>> + [AUD_CLKID_MST_C_SCLK_POST_EN] = &audio_mst_c_sclk_post_en.hw,
>>>>> + [AUD_CLKID_MST_C_SCLK] = &audio_mst_c_sclk.hw,
>>>>> + [AUD_CLKID_MST_D_SCLK_PRE_EN] = &audio_mst_d_sclk_pre_en.hw,
>>>>> + [AUD_CLKID_MST_D_SCLK_DIV] = &audio_mst_d_sclk_div.hw,
>>>>> + [AUD_CLKID_MST_D_SCLK_POST_EN] = &audio_mst_d_sclk_post_en.hw,
>>>>> + [AUD_CLKID_MST_D_SCLK] = &audio_mst_d_sclk.hw,
>>>>> + [AUD_CLKID_MST_A_LRCLK_DIV] = &audio_mst_a_lrclk_div.hw,
>>>>> + [AUD_CLKID_MST_A_LRCLK] = &audio_mst_a_lrclk.hw,
>>>>> + [AUD_CLKID_MST_B_LRCLK_DIV] = &audio_mst_b_lrclk_div.hw,
>>>>> + [AUD_CLKID_MST_B_LRCLK] = &audio_mst_b_lrclk.hw,
>>>>> + [AUD_CLKID_MST_C_LRCLK_DIV] = &audio_mst_c_lrclk_div.hw,
>>>>> + [AUD_CLKID_MST_C_LRCLK] = &audio_mst_c_lrclk.hw,
>>>>> + [AUD_CLKID_MST_D_LRCLK_DIV] = &audio_mst_d_lrclk_div.hw,
>>>>> + [AUD_CLKID_MST_D_LRCLK] = &audio_mst_d_lrclk.hw,
>>>>> + [AUD_CLKID_TDMIN_A_SCLK_SEL] = &audio_tdmin_a_sclk_mux.hw,
>>>>> + [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &audio_tdmin_a_sclk_pre_en.hw,
>>>>> + [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &audio_tdmin_a_sclk_post_en.hw,
>>>>> + [AUD_CLKID_TDMIN_A_SCLK] = &audio_tdmin_a_sclk.hw,
>>>>> + [AUD_CLKID_TDMIN_A_LRCLK] = &audio_tdmin_a_lrclk.hw,
>>>>> + [AUD_CLKID_TDMIN_B_SCLK_SEL] = &audio_tdmin_b_sclk_mux.hw,
>>>>> + [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &audio_tdmin_b_sclk_pre_en.hw,
>>>>> + [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &audio_tdmin_b_sclk_post_en.hw,
>>>>> + [AUD_CLKID_TDMIN_B_SCLK] = &audio_tdmin_b_sclk.hw,
>>>>> + [AUD_CLKID_TDMIN_B_LRCLK] = &audio_tdmin_b_lrclk.hw,
>>>>> + [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &audio_tdmin_lb_sclk_mux.hw,
>>>>> + [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &audio_tdmin_lb_sclk_pre_en.hw,
>>>>> + [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &audio_tdmin_lb_sclk_post_en.hw,
>>>>> + [AUD_CLKID_TDMIN_LB_SCLK] = &audio_tdmin_lb_sclk.hw,
>>>>> + [AUD_CLKID_TDMIN_LB_LRCLK] = &audio_tdmin_lb_lrclk.hw,
>>>>> + [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &audio_tdmout_a_sclk_mux.hw,
>>>>> + [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &audio_tdmout_a_sclk_pre_en.hw,
>>>>> + [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &audio_tdmout_a_sclk_post_en.hw,
>>>>> + [AUD_CLKID_TDMOUT_A_SCLK] = &audio_tdmout_a_sclk.hw,
>>>>> + [AUD_CLKID_TDMOUT_A_LRCLK] = &audio_tdmout_a_lrclk.hw,
>>>>> + [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &audio_tdmout_b_sclk_mux.hw,
>>>>> + [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &audio_tdmout_b_sclk_pre_en.hw,
>>>>> + [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &audio_tdmout_b_sclk_post_en.hw,
>>>>> + [AUD_CLKID_TDMOUT_B_SCLK] = &audio_tdmout_b_sclk.hw,
>>>>> + [AUD_CLKID_TDMOUT_B_LRCLK] = &audio_tdmout_b_lrclk.hw,
>>>>> +
>>>>> + [AUD2_CLKID_DDR_ARB] = &audio2_ddr_arb.hw,
>>>>> + [AUD2_CLKID_PDM] = &audio2_pdm.hw,
>>>>> + [AUD2_CLKID_TDMIN_VAD] = &audio2_tdmin_vad.hw,
>>>>> + [AUD2_CLKID_TODDR_VAD] = &audio2_toddr_vad.hw,
>>>>> + [AUD2_CLKID_VAD] = &audio2_vad.hw,
>>>>> + [AUD2_CLKID_AUDIOTOP] = &audio2_audiotop.hw,
>>>>> + [AUD2_CLKID_VAD_MCLK_SEL] = &audio2_vad_mclk_mux.hw,
>>>>> + [AUD2_CLKID_VAD_MCLK_DIV] = &audio2_vad_mclk_div.hw,
>>>>> + [AUD2_CLKID_VAD_MCLK] = &audio2_vad_mclk.hw,
>>>>> + [AUD2_CLKID_VAD_CLK_SEL] = &audio2_vad_clk_mux.hw,
>>>>> + [AUD2_CLKID_VAD_CLK_DIV] = &audio2_vad_clk_div.hw,
>>>>> + [AUD2_CLKID_VAD_CLK] = &audio2_vad_clk.hw,
>>>>> + [AUD2_CLKID_PDM_DCLK_SEL] = &audio2_pdm_dclk_mux.hw,
>>>>> + [AUD2_CLKID_PDM_DCLK_DIV] = &audio2_pdm_dclk_div.hw,
>>>>> + [AUD2_CLKID_PDM_DCLK] = &audio2_pdm_dclk.hw,
>>>>> + [AUD2_CLKID_PDM_SYSCLK_SEL] = &audio2_pdm_sysclk_mux.hw,
>>>>> + [AUD2_CLKID_PDM_SYSCLK_DIV] = &audio2_pdm_sysclk_div.hw,
>>>>> + [AUD2_CLKID_PDM_SYSCLK] = &audio2_pdm_sysclk.hw,
>>>>> +};
>>>>> +
>>>>> +static struct meson_clk_hw_data a1_audio_clks = {
>>>>> + .hws = a1_audio_hw_clks,
>>>>> + .num = ARRAY_SIZE(a1_audio_hw_clks),
>>>>> +};
>>>>> +
>>>>> +static struct regmap *a1_audio_map(struct platform_device *pdev,
>>>>> + unsigned int index)
>>>>> +{
>>>>> + char name[32];
>>>>> + const struct regmap_config cfg = {
>>>>> + .reg_bits = 32,
>>>>> + .val_bits = 32,
>>>>> + .reg_stride = 4,
>>>>> + .name = name,
>>>>
>>>> Not necessary
>>>>
>>>
>>> This implementation uses two regmaps, and this field allow to avoid
>>> errors like this:
>>>
>>> [ 0.145530] debugfs: Directory 'fe050000.audio-clock-controller' with
>>> parent 'regmap' already present!
>>>
>>>>> + };
>>>>> + void __iomem *base;
>>>>> +
>>>>> + base = devm_platform_ioremap_resource(pdev, index);
>>>>> + if (IS_ERR(base))
>>>>> + return base;
>>>>> +
>>>>> + scnprintf(name, sizeof(name), "%d", index);
>>>>> + return devm_regmap_init_mmio(&pdev->dev, base, &cfg);
>>>>> +}
>>>>
>>>> That is overengineered. Please keep it simple. Declare the regmap_config
>>>> as static const global, and do it like axg-audio please.
>>>>
>>>
>>> This only reason why it is not "static const" because I need to set
>>> unique name for each regmap.
>>>
>>>>> +
>>>>> +static int a1_register_clk(struct platform_device *pdev,
>>>>> + struct regmap *map0, struct regmap *map1,
>>>>> + struct clk_hw *hw)
>>>>> +{
>>>>> + struct clk_regmap *clk = container_of(hw, struct clk_regmap, hw);
>>>>> +
>>>>> + if (!hw)
>>>>> + return 0;
>>>>> +
>>>>> + switch ((unsigned long)clk->map) {
>>>>> + case AUDIO_RANGE_0:
>>>>> + clk->map = map0;
>>>>> + break;
>>>>> + case AUDIO_RANGE_1:
>>>>> + clk->map = map1;
>>>>> + break;
>>>>
>>>> ... fishy
>>>>
>>>>> + default:
>>>>> + WARN_ON(1);
>>>>> + return -EINVAL;
>>>>> + }
>>>>> +
>>>>> + return devm_clk_hw_register(&pdev->dev, hw);
>>>>> +}
>>>>> +
>>>>> +static int a1_audio_clkc_probe(struct platform_device *pdev)
>>>>> +{
>>>>> + struct regmap *map0, *map1;
>>>>> + struct clk *clk;
>>>>> + unsigned int i;
>>>>> + int ret;
>>>>> +
>>>>> + clk = devm_clk_get_enabled(&pdev->dev, "pclk");
>>>>> + if (WARN_ON(IS_ERR(clk)))
>>>>> + return PTR_ERR(clk);
>>>>> +
>>>>> + map0 = a1_audio_map(pdev, 0);
>>>>> + if (IS_ERR(map0))
>>>>> + return PTR_ERR(map0);
>>>>> +
>>>>> + map1 = a1_audio_map(pdev, 1);
>>>>> + if (IS_ERR(map1))
>>>>> + return PTR_ERR(map1);
>>>>
>>>> No - Looks to me you just have two clock controllers you are trying
>>>> force into one.
>>>>
>>>
>>> See the begining.
>>>
>>>>> +
>>>>> + /*
>>>>> + * Register and enable AUD2_CLKID_AUDIOTOP clock first. Unless
>>>>> + * it is enabled any read/write to 'map0' hangs the CPU.
>>>>> + */
>>>>> +
>>>>> + ret = a1_register_clk(pdev, map0, map1,
>>>>> + a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + ret = clk_prepare_enable(a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]->clk);
>>>>> + if (ret)
>>>>> + return ret;
>>>>
>>>> Again, this shows 2 devices. The one related to your 'map0' should
>>>> request AUD2_CLKID_AUDIOTOP as input and enable it right away.
>>>>
>>>
>>> See the begining.
>>>
>>>>> +
>>>>> + for (i = 0; i < a1_audio_clks.num; i++) {
>>>>> + if (i == AUD2_CLKID_AUDIOTOP)
>>>>> + continue;
>>>>> +
>>>>> + ret = a1_register_clk(pdev, map0, map1, a1_audio_clks.hws[i]);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + ret = devm_of_clk_add_hw_provider(&pdev->dev, meson_clk_hw_get,
>>>>> + &a1_audio_clks);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + BUILD_BUG_ON((unsigned long)AUDIO_REG_MAP(AUDIO_SW_RESET0) !=
>>>>> + AUDIO_RANGE_0);
>>>>
>>>> Why is that necessary ?
>>>>
>>>
>>> A little paranoia. Here AUDIO_SW_RESET0 is handled as map0's register,
>>> and I want to assert it.
>>>
>>>>> + return meson_audio_rstc_register(&pdev->dev, map0,
>>>>> + AUDIO_REG_OFFSET(AUDIO_SW_RESET0), 32);
>>>>> +}
>>>>> +
>>>>> +static const struct of_device_id a1_audio_clkc_match_table[] = {
>>>>> + { .compatible = "amlogic,a1-audio-clkc", },
>>>>> + {}
>>>>> +};
>>>>> +MODULE_DEVICE_TABLE(of, a1_audio_clkc_match_table);
>>>>> +
>>>>> +static struct platform_driver a1_audio_clkc_driver = {
>>>>> + .probe = a1_audio_clkc_probe,
>>>>> + .driver = {
>>>>> + .name = "a1-audio-clkc",
>>>>> + .of_match_table = a1_audio_clkc_match_table,
>>>>> + },
>>>>> +};
>>>>> +module_platform_driver(a1_audio_clkc_driver);
>>>>> +
>>>>> +MODULE_DESCRIPTION("Amlogic A1 Audio Clock driver");
>>>>> +MODULE_AUTHOR("Jan Dakinevich <[email protected]>");
>>>>> +MODULE_LICENSE("GPL");
>>>>> diff --git a/drivers/clk/meson/a1-audio.h b/drivers/clk/meson/a1-audio.h
>>>>> new file mode 100644
>>>>> index 000000000000..f994e87276cd
>>>>> --- /dev/null
>>>>> +++ b/drivers/clk/meson/a1-audio.h
>>>>> @@ -0,0 +1,58 @@
>>>>> +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
>>>>> +/*
>>>>> + * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
>>>>> + *
>>>>> + * Author: Jan Dakinevich <[email protected]>
>>>>> + */
>>>>> +
>>>>> +#ifndef __A1_AUDIO_H
>>>>> +#define __A1_AUDIO_H
>>>>> +
>>>>> +#define AUDIO_RANGE_0 0xa
>>>>> +#define AUDIO_RANGE_1 0xb
>>>>> +#define AUDIO_RANGE_SHIFT 16
>>>>> +
>>>>> +#define AUDIO_REG(_range, _offset) \
>>>>> + (((_range) << AUDIO_RANGE_SHIFT) + (_offset))
>>>>> +
>>>>> +#define AUDIO_REG_OFFSET(_reg) \
>>>>> + ((_reg) & ((1 << AUDIO_RANGE_SHIFT) - 1))
>>>>> +
>>>>> +#define AUDIO_REG_MAP(_reg) \
>>>>> + ((void *)((_reg) >> AUDIO_RANGE_SHIFT))
>>>>
>>>> That is seriouly overengineered.
>>>> The following are offset. Just write what they are.
>>>>
>>>
>>> This is all in order to keep range's identifier together with offset and
>>> then use it to store the identifier in clk_regmaps.
>>>
>>>> There is not reason to put that into a header. It is only going to be
>>>> used by a single driver.
>>>>>> +
>>>>> +#define AUDIO_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_0, 0x000)
>>>>> +#define AUDIO_MCLK_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x008)
>>>>> +#define AUDIO_MCLK_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x00c)
>>>>> +#define AUDIO_MCLK_C_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x010)
>>>>> +#define AUDIO_MCLK_D_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x014)
>>>>> +#define AUDIO_MCLK_E_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x018)
>>>>> +#define AUDIO_MCLK_F_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x01c)
>>>>> +#define AUDIO_SW_RESET0 AUDIO_REG(AUDIO_RANGE_0, 0x028)
>>>>> +#define AUDIO_MST_A_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x040)
>>>>> +#define AUDIO_MST_A_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x044)
>>>>> +#define AUDIO_MST_B_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x048)
>>>>> +#define AUDIO_MST_B_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x04c)
>>>>> +#define AUDIO_MST_C_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x050)
>>>>> +#define AUDIO_MST_C_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x054)
>>>>> +#define AUDIO_MST_D_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x058)
>>>>> +#define AUDIO_MST_D_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x05c)
>>>>> +#define AUDIO_CLK_TDMIN_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x080)
>>>>> +#define AUDIO_CLK_TDMIN_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x084)
>>>>> +#define AUDIO_CLK_TDMIN_LB_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x08c)
>>>>> +#define AUDIO_CLK_TDMOUT_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x090)
>>>>> +#define AUDIO_CLK_TDMOUT_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x094)
>>>>> +#define AUDIO_CLK_SPDIFIN_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x09c)
>>>>> +#define AUDIO_CLK_RESAMPLE_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a4)
>>>>> +#define AUDIO_CLK_LOCKER_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a8)
>>>>> +#define AUDIO_CLK_EQDRC_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0c0)
>>>>> +
>>>>> +#define AUDIO2_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_1, 0x00c)
>>>>> +#define AUDIO2_MCLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x040)
>>>>> +#define AUDIO2_CLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x044)
>>>>> +#define AUDIO2_CLK_PDMIN_CTRL0 AUDIO_REG(AUDIO_RANGE_1, 0x058)
>>>>> +#define AUDIO2_CLK_PDMIN_CTRL1 AUDIO_REG(AUDIO_RANGE_1, 0x05c)
>>>>> +
>>>>> +#include <dt-bindings/clock/amlogic,a1-audio-clkc.h>
>>>>> +
>>>>> +#endif /* __A1_AUDIO_H */
>>>>
>>>>
>>
>>


--
Jerome

2024-03-26 18:46:21

by Jan Dakinevich

[permalink] [raw]
Subject: Re: [PATCH 04/25] clk: meson: a1: add the audio clock controller driver



On 3/26/24 18:26, Jerome Brunet wrote:
>
> On Sat 23 Mar 2024 at 21:02, Jan Dakinevich <[email protected]> wrote:
>
>> Jerome, I have reworked my driver reusing axg-audio code as most as I
>> could and now I have one more question. Lets see on this definition from
>> axg-audio:
>>
>> #define AUD_MST_MUX(_name, _reg, _flag) \
>> AUD_MUX(_name##_sel, _reg, 0x7, 24, _flag, \
>> mst_mux_parent_data, 0)
>>
>> #define AUD_MST_MCLK_MUX(_name, _reg) \
>> AUD_MST_MUX(_name, _reg, CLK_MUX_ROUND_CLOSEST)
>>
>> CLK_SET_RATE_PARENT is not set here. But why? It means, that topmost pll
>> clock will not be reconfigured at runtime to satisfy the rate that was
>> requested from axg-tdm.
>>
>
> Yes, that is by design. It is another area where mainline audio differs
> greatly from AML vendor code. The PLLs are expected be to fixed and the
> audio master clock will reparent to the most adequate PLL source
> depending on the use case.
>
> This is how we manage to satisfy all audio interfaces with a very
> limited number of PLLs
>
> On AXG/G12 there is at most 6 concurrent interfaces (3 FRDDR/TODDR) - 8
> on sm1 - and we can satisfy on that with 3 PLLs. That would not be
> possible if interfaces were having their way with the PLLs, reseting it
> everytime a stream is started.
> > The PLL rate should be carefully chosen so it can be derived easily. On
> AXG/G12/SM1 that is:
> * one PLL per rate family, to maximize clock precision
> * x24 x32: to handle different sample sizes
> * x2 until we reach the PLL limits to allow higher rates such as 384kHz
> or even higher
>

Thank you. Now it has become much clearer.

> If you have less PLLs on A1, you'll have to make compromises, like a less
> precise clock to support multiple family with one PLL.
> This is why the PLLs are set for each platform in DT because that choice
> may depend on the platform use case.
>

Unfortunately, on A1 we have only one PLL.

Yes, for us it would be better to have hifi_pll with predefined rate.
For instance it will allow to avoid that ugly workaround in PDM (sysrate
property, etc).

But what whould be preferred for upstream? I can imagine a scenario
where samples with different rate should be played, PDM attached to
fclk_divN and there are no conflicts with TDM. In this case
reconfiguration of hifi_pll on demand could better satisfy somebody's
requirements.

>>
>> On 3/19/24 11:30, Jerome Brunet wrote:
>>>
>>> On Tue 19 Mar 2024 at 04:47, Jan Dakinevich <[email protected]> wrote:
>>>
>>>> Let's start from the end:
>>>>
>>>>> No - Looks to me you just have two clock controllers you are trying
>>>> force into one.
>>>>
>>>>> Again, this shows 2 devices. The one related to your 'map0' should
>>>> request AUD2_CLKID_AUDIOTOP as input and enable it right away.
>>>>
>>>> Most of fishy workarounds that you commented is caused the fact the mmio
>>>> of this clock controller is divided into two parts. Compare it with
>>>> axg-audio driver, things that was part of contigous memory region (like
>>>> pdm) here are moved to second region. Is this enough to make a guess
>>>> that these are two devices?
>>>
>>> I see obsolutely no reason to think it is a single device nor to add all the quirks
>>> you have the way you did. So yes, in that case, 2 zones, 2 devices.
>>>
>>>>
>>>> Concerning AUD2_CLKID_AUDIOTOP clock, as it turned out, it must be
>>>> enabled before enabling of clocks from second region too. That is
>>>> AUD2_CLKID_AUDIOTOP clock feeds both parts of this clock controller.
>>>>
>>>
>>> Yes. I understood the first time around and already commented on that.
>>>
>>>>
>>>> On 3/15/24 12:20, Jerome Brunet wrote:
>>>>>
>>>>> On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <[email protected]> wrote:
>>>>>
>>>>>> This controller provides clocks and reset functionality for audio
>>>>>> peripherals on Amlogic A1 SoC family.
>>>>>>
>>>>>> The driver is almost identical to 'axg-audio', however it would be better
>>>>>> to keep it separate due to following reasons:
>>>>>>
>>>>>> - significant amount of bits has another definition. I will bring there
>>>>>> a mess of new defines with A1_ suffixes.
>>>>>>
>>>>>> - registers of this controller are located in two separate regions. It
>>>>>> will give a lot of complications for 'axg-audio' to support this.
>>>>>>
>>>>>> Signed-off-by: Jan Dakinevich <[email protected]>
>>>>>> ---
>>>>>> drivers/clk/meson/Kconfig | 13 +
>>>>>> drivers/clk/meson/Makefile | 1 +
>>>>>> drivers/clk/meson/a1-audio.c | 556 +++++++++++++++++++++++++++++++++++
>>>>>> drivers/clk/meson/a1-audio.h | 58 ++++
>>>>>> 4 files changed, 628 insertions(+)
>>>>>> create mode 100644 drivers/clk/meson/a1-audio.c
>>>>>> create mode 100644 drivers/clk/meson/a1-audio.h
>>>>>>
>>>>>> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
>>>>>> index d6a2fa5f7e88..80c4a18c83d2 100644
>>>>>> --- a/drivers/clk/meson/Kconfig
>>>>>> +++ b/drivers/clk/meson/Kconfig
>>>>>> @@ -133,6 +133,19 @@ config COMMON_CLK_A1_PERIPHERALS
>>>>>> device, A1 SoC Family. Say Y if you want A1 Peripherals clock
>>>>>> controller to work.
>>>>>>
>>>>>> +config COMMON_CLK_A1_AUDIO
>>>>>> + tristate "Amlogic A1 SoC Audio clock controller support"
>>>>>> + depends on ARM64
>>>>>> + select COMMON_CLK_MESON_REGMAP
>>>>>> + select COMMON_CLK_MESON_CLKC_UTILS
>>>>>> + select COMMON_CLK_MESON_PHASE
>>>>>> + select COMMON_CLK_MESON_SCLK_DIV
>>>>>> + select COMMON_CLK_MESON_AUDIO_RSTC
>>>>>> + help
>>>>>> + Support for the Audio clock controller on Amlogic A113L based
>>>>>> + device, A1 SoC Family. Say Y if you want A1 Audio clock controller
>>>>>> + to work.
>>>>>> +
>>>>>> config COMMON_CLK_G12A
>>>>>> tristate "G12 and SM1 SoC clock controllers support"
>>>>>> depends on ARM64
>>>>>> diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
>>>>>> index 88d94921a4dc..4968fc7ad555 100644
>>>>>> --- a/drivers/clk/meson/Makefile
>>>>>> +++ b/drivers/clk/meson/Makefile
>>>>>> @@ -20,6 +20,7 @@ obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
>>>>>> obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
>>>>>> obj-$(CONFIG_COMMON_CLK_A1_PLL) += a1-pll.o
>>>>>> obj-$(CONFIG_COMMON_CLK_A1_PERIPHERALS) += a1-peripherals.o
>>>>>> +obj-$(CONFIG_COMMON_CLK_A1_AUDIO) += a1-audio.o
>>>>>> obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
>>>>>> obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o
>>>>>> obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o
>>>>>> diff --git a/drivers/clk/meson/a1-audio.c b/drivers/clk/meson/a1-audio.c
>>>>>> new file mode 100644
>>>>>> index 000000000000..6039116c93ba
>>>>>> --- /dev/null
>>>>>> +++ b/drivers/clk/meson/a1-audio.c
>>>>>> @@ -0,0 +1,556 @@
>>>>>> +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
>>>>>> +/*
>>>>>> + * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
>>>>>> + *
>>>>>> + * Author: Jan Dakinevich <[email protected]>
>>>>>> + */
>>>>>> +
>>>>>> +#include <linux/clk.h>
>>>>>> +#include <linux/clk-provider.h>
>>>>>> +#include <linux/init.h>
>>>>>> +#include <linux/of_device.h>
>>>>>> +#include <linux/module.h>
>>>>>> +#include <linux/platform_device.h>
>>>>>> +#include <linux/regmap.h>
>>>>>> +#include <linux/reset.h>
>>>>>> +#include <linux/reset-controller.h>
>>>>>> +#include <linux/slab.h>
>>>>>> +
>>>>>> +#include "meson-clkc-utils.h"
>>>>>> +#include "meson-audio-rstc.h"
>>>>>> +#include "clk-regmap.h"
>>>>>> +#include "clk-phase.h"
>>>>>> +#include "sclk-div.h"
>>>>>> +#include "a1-audio.h"
>>>>>> +
>>>>>> +#define AUDIO_PDATA(_name) \
>>>>>> + ((const struct clk_parent_data[]) { { .hw = &(_name).hw } })
>>>>>
>>>>> Not a fan - yet another level of macro.
>>>>>
>>>>>> +
>>>>>> +#define AUDIO_MUX(_name, _reg, _mask, _shift, _pdata) \
>>>>>> +static struct clk_regmap _name = { \
>>>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>>>> + .data = &(struct clk_regmap_mux_data){ \
>>>>>> + .offset = AUDIO_REG_OFFSET(_reg), \
>>>>>> + .mask = (_mask), \
>>>>>> + .shift = (_shift), \
>>>>>> + }, \
>>>>>> + .hw.init = &(struct clk_init_data) { \
>>>>>> + .name = #_name, \
>>>>>> + .ops = &clk_regmap_mux_ops, \
>>>>>> + .parent_data = (_pdata), \
>>>>>> + .num_parents = ARRAY_SIZE(_pdata), \
>>>>>> + .flags = CLK_SET_RATE_PARENT, \
>>>>>> + }, \
>>>>>> +}
>>>>>> +
>>>>>> +#define AUDIO_DIV(_name, _reg, _shift, _width, _pdata) \
>>>>>> +static struct clk_regmap _name = { \
>>>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>>>> + .data = &(struct clk_regmap_div_data){ \
>>>>>> + .offset = AUDIO_REG_OFFSET(_reg), \
>>>>>> + .shift = (_shift), \
>>>>>> + .width = (_width), \
>>>>>> + }, \
>>>>>> + .hw.init = &(struct clk_init_data) { \
>>>>>> + .name = #_name, \
>>>>>> + .ops = &clk_regmap_divider_ops, \
>>>>>> + .parent_data = (_pdata), \
>>>>>> + .num_parents = 1, \
>>>>>> + .flags = CLK_SET_RATE_PARENT, \
>>>>>> + }, \
>>>>>> +}
>>>>>> +
>>>>>> +#define AUDIO_GATE(_name, _reg, _bit, _pdata) \
>>>>>> +static struct clk_regmap _name = { \
>>>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>>>> + .data = &(struct clk_regmap_gate_data){ \
>>>>>> + .offset = AUDIO_REG_OFFSET(_reg), \
>>>>>> + .bit_idx = (_bit), \
>>>>>> + }, \
>>>>>> + .hw.init = &(struct clk_init_data) { \
>>>>>> + .name = #_name, \
>>>>>> + .ops = &clk_regmap_gate_ops, \
>>>>>> + .parent_data = (_pdata), \
>>>>>> + .num_parents = 1, \
>>>>>> + .flags = CLK_SET_RATE_PARENT, \
>>>>>> + }, \
>>>>>> +}
>>>>>> +
>>>>>> +#define AUDIO_SCLK_DIV(_name, _reg, _div_shift, _div_width, \
>>>>>> + _hi_shift, _hi_width, _pdata, _set_rate_parent) \
>>>>>> +static struct clk_regmap _name = { \
>>>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>>>> + .data = &(struct meson_sclk_div_data) { \
>>>>>> + .div = { \
>>>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>>>> + .shift = (_div_shift), \
>>>>>> + .width = (_div_width), \
>>>>>> + }, \
>>>>>> + .hi = { \
>>>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>>>> + .shift = (_hi_shift), \
>>>>>> + .width = (_hi_width), \
>>>>>> + }, \
>>>>>> + }, \
>>>>>> + .hw.init = &(struct clk_init_data) { \
>>>>>> + .name = #_name, \
>>>>>> + .ops = &meson_sclk_div_ops, \
>>>>>> + .parent_data = (_pdata), \
>>>>>> + .num_parents = 1, \
>>>>>> + .flags = (_set_rate_parent) ? CLK_SET_RATE_PARENT : 0, \
>>>>>
>>>>> Does not help readeability. Just pass the flag as axg-audio does.
>>>>>
>>>>>> + }, \
>>>>>> +}
>>>>>> +
>>>>>> +#define AUDIO_TRIPHASE(_name, _reg, _width, _shift0, _shift1, _shift2, \
>>>>>> + _pdata) \
>>>>>> +static struct clk_regmap _name = { \
>>>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>>>> + .data = &(struct meson_clk_triphase_data) { \
>>>>>> + .ph0 = { \
>>>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>>>> + .shift = (_shift0), \
>>>>>> + .width = (_width), \
>>>>>> + }, \
>>>>>> + .ph1 = { \
>>>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>>>> + .shift = (_shift1), \
>>>>>> + .width = (_width), \
>>>>>> + }, \
>>>>>> + .ph2 = { \
>>>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>>>> + .shift = (_shift2), \
>>>>>> + .width = (_width), \
>>>>>> + }, \
>>>>>> + }, \
>>>>>> + .hw.init = &(struct clk_init_data) { \
>>>>>> + .name = #_name, \
>>>>>> + .ops = &meson_clk_triphase_ops, \
>>>>>> + .parent_data = (_pdata), \
>>>>>> + .num_parents = 1, \
>>>>>> + .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \
>>>>>> + }, \
>>>>>> +}
>>>>>> +
>>>>>> +#define AUDIO_SCLK_WS(_name, _reg, _width, _shift_ph, _shift_ws, \
>>>>>> + _pdata) \
>>>>>> +static struct clk_regmap _name = { \
>>>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>>>> + .data = &(struct meson_sclk_ws_inv_data) { \
>>>>>> + .ph = { \
>>>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>>>> + .shift = (_shift_ph), \
>>>>>> + .width = (_width), \
>>>>>> + }, \
>>>>>> + .ws = { \
>>>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>>>> + .shift = (_shift_ws), \
>>>>>> + .width = (_width), \
>>>>>> + }, \
>>>>>> + }, \
>>>>>> + .hw.init = &(struct clk_init_data) { \
>>>>>> + .name = #_name, \
>>>>>> + .ops = &meson_sclk_ws_inv_ops, \
>>>>>> + .parent_data = (_pdata), \
>>>>>> + .num_parents = 1, \
>>>>>> + .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \
>>>>>> + }, \
>>>>>> +}
>>>>>
>>>>> All the above does essentially the same things as the macro of
>>>>> axg-audio, to some minor differences. Yet it is another set to maintain.
>>>>>
>>>>
>>>> Except one thing... Here I keep memory identifier to which this clock
>>>> belongs:
>>>>
>>>> .map = AUDIO_REG_MAP(_reg),
>>>>
>>>> It is workaround, but ->map the only common field in clk_regmap that
>>>> could be used for this purpose.
>>>>
>>>>
>>>>> I'd much prefer if you put the axg-audio macro in a header a re-used
>>>>> those. There would a single set to maintain. You may then specialize the
>>>>> included in the driver C file, to avoid redundant parameters
>>>>>
>>>>> Rework axg-audio to use clk_parent_data if you must, but not in the same
>>>>> series please.
>>>>>
>>>>>> +
>>>>>> +static const struct clk_parent_data a1_pclk_pdata[] = {
>>>>>> + { .fw_name = "pclk", },
>>>>>> +};
>>>>>> +
>>>>>> +AUDIO_GATE(audio_ddr_arb, AUDIO_CLK_GATE_EN0, 0, a1_pclk_pdata);
>>>>>> +AUDIO_GATE(audio_tdmin_a, AUDIO_CLK_GATE_EN0, 1, a1_pclk_pdata);
>>>>>> +AUDIO_GATE(audio_tdmin_b, AUDIO_CLK_GATE_EN0, 2, a1_pclk_pdata);
>>>>>> +AUDIO_GATE(audio_tdmin_lb, AUDIO_CLK_GATE_EN0, 3, a1_pclk_pdata);
>>>>>> +AUDIO_GATE(audio_loopback, AUDIO_CLK_GATE_EN0, 4, a1_pclk_pdata);
>>>>>> +AUDIO_GATE(audio_tdmout_a, AUDIO_CLK_GATE_EN0, 5, a1_pclk_pdata);
>>>>>> +AUDIO_GATE(audio_tdmout_b, AUDIO_CLK_GATE_EN0, 6, a1_pclk_pdata);
>>>>>> +AUDIO_GATE(audio_frddr_a, AUDIO_CLK_GATE_EN0, 7, a1_pclk_pdata);
>>>>>> +AUDIO_GATE(audio_frddr_b, AUDIO_CLK_GATE_EN0, 8, a1_pclk_pdata);
>>>>>> +AUDIO_GATE(audio_toddr_a, AUDIO_CLK_GATE_EN0, 9, a1_pclk_pdata);
>>>>>> +AUDIO_GATE(audio_toddr_b, AUDIO_CLK_GATE_EN0, 10, a1_pclk_pdata);
>>>>>> +AUDIO_GATE(audio_spdifin, AUDIO_CLK_GATE_EN0, 11, a1_pclk_pdata);
>>>>>> +AUDIO_GATE(audio_resample, AUDIO_CLK_GATE_EN0, 12, a1_pclk_pdata);
>>>>>> +AUDIO_GATE(audio_eqdrc, AUDIO_CLK_GATE_EN0, 13, a1_pclk_pdata);
>>>>>> +AUDIO_GATE(audio_audiolocker, AUDIO_CLK_GATE_EN0, 14, a1_pclk_pdata);
>>>>> This is what I mean by redundant parameter ^
>>>>>
>>>>
>>>> Yep. I could define something like AUDIO_PCLK_GATE().
>>>>
>>>>>> +
>>>>>> +AUDIO_GATE(audio2_ddr_arb, AUDIO2_CLK_GATE_EN0, 0, a1_pclk_pdata);
>>>>>> +AUDIO_GATE(audio2_pdm, AUDIO2_CLK_GATE_EN0, 1, a1_pclk_pdata);
>>>>>> +AUDIO_GATE(audio2_tdmin_vad, AUDIO2_CLK_GATE_EN0, 2, a1_pclk_pdata);
>>>>>> +AUDIO_GATE(audio2_toddr_vad, AUDIO2_CLK_GATE_EN0, 3, a1_pclk_pdata);
>>>>>> +AUDIO_GATE(audio2_vad, AUDIO2_CLK_GATE_EN0, 4, a1_pclk_pdata);
>>>>>> +AUDIO_GATE(audio2_audiotop, AUDIO2_CLK_GATE_EN0, 7, a1_pclk_pdata);
>>>>>> +
>>>>>> +static const struct clk_parent_data a1_mst_pdata[] = {
>>>>>> + { .fw_name = "dds_in" },
>>>>>> + { .fw_name = "fclk_div2" },
>>>>>> + { .fw_name = "fclk_div3" },
>>>>>> + { .fw_name = "hifi_pll" },
>>>>>> + { .fw_name = "xtal" },
>>>>>> +};
>>>>>> +
>>>>>> +#define AUDIO_MST_MCLK(_name, _reg) \
>>>>>> + AUDIO_MUX(_name##_mux, (_reg), 0x7, 24, a1_mst_pdata); \
>>>>>> + AUDIO_DIV(_name##_div, (_reg), 0, 16, \
>>>>>> + AUDIO_PDATA(_name##_mux)); \
>>>>>> + AUDIO_GATE(_name, (_reg), 31, AUDIO_PDATA(_name##_div))
>>>>>> +
>>>>>> +AUDIO_MST_MCLK(audio_mst_a_mclk, AUDIO_MCLK_A_CTRL);
>>>>>> +AUDIO_MST_MCLK(audio_mst_b_mclk, AUDIO_MCLK_B_CTRL);
>>>>>> +AUDIO_MST_MCLK(audio_mst_c_mclk, AUDIO_MCLK_C_CTRL);
>>>>>> +AUDIO_MST_MCLK(audio_mst_d_mclk, AUDIO_MCLK_D_CTRL);
>>>>>> +AUDIO_MST_MCLK(audio_spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL);
>>>>>> +AUDIO_MST_MCLK(audio_eqdrc_clk, AUDIO_CLK_EQDRC_CTRL);
>>>>>> +
>>>>>> +AUDIO_MUX(audio_resample_clk_mux, AUDIO_CLK_RESAMPLE_CTRL, 0xf, 24,
>>>>>> + a1_mst_pdata);
>>>>>> +AUDIO_DIV(audio_resample_clk_div, AUDIO_CLK_RESAMPLE_CTRL, 0, 8,
>>>>>> + AUDIO_PDATA(audio_resample_clk_mux));
>>>>>> +AUDIO_GATE(audio_resample_clk, AUDIO_CLK_RESAMPLE_CTRL, 31,
>>>>>> + AUDIO_PDATA(audio_resample_clk_div));
>>>>>> +
>>>>>> +AUDIO_MUX(audio_locker_in_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 8,
>>>>>> + a1_mst_pdata);
>>>>>> +AUDIO_DIV(audio_locker_in_clk_div, AUDIO_CLK_LOCKER_CTRL, 0, 8,
>>>>>> + AUDIO_PDATA(audio_locker_in_clk_mux));
>>>>>> +AUDIO_GATE(audio_locker_in_clk, AUDIO_CLK_LOCKER_CTRL, 15,
>>>>>> + AUDIO_PDATA(audio_locker_in_clk_div));
>>>>>> +
>>>>>> +AUDIO_MUX(audio_locker_out_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 24,
>>>>>> + a1_mst_pdata);
>>>>>> +AUDIO_DIV(audio_locker_out_clk_div, AUDIO_CLK_LOCKER_CTRL, 16, 8,
>>>>>> + AUDIO_PDATA(audio_locker_out_clk_mux));
>>>>>> +AUDIO_GATE(audio_locker_out_clk, AUDIO_CLK_LOCKER_CTRL, 31,
>>>>>> + AUDIO_PDATA(audio_locker_out_clk_div));
>>>>>> +
>>>>>> +AUDIO_MST_MCLK(audio2_vad_mclk, AUDIO2_MCLK_VAD_CTRL);
>>>>>> +AUDIO_MST_MCLK(audio2_vad_clk, AUDIO2_CLK_VAD_CTRL);
>>>>>> +AUDIO_MST_MCLK(audio2_pdm_dclk, AUDIO2_CLK_PDMIN_CTRL0);
>>>>>> +AUDIO_MST_MCLK(audio2_pdm_sysclk, AUDIO2_CLK_PDMIN_CTRL1);
>>>>>> +
>>>>>> +#define AUDIO_MST_SCLK(_name, _reg0, _reg1, _pdata) \
>>>>>> + AUDIO_GATE(_name##_pre_en, (_reg0), 31, (_pdata)); \
>>>>>> + AUDIO_SCLK_DIV(_name##_div, (_reg0), 20, 10, 0, 0, \
>>>>>> + AUDIO_PDATA(_name##_pre_en), true); \
>>>>>> + AUDIO_GATE(_name##_post_en, (_reg0), 30, \
>>>>>> + AUDIO_PDATA(_name##_div)); \
>>>>>> + AUDIO_TRIPHASE(_name, (_reg1), 1, 0, 2, 4, \
>>>>>> + AUDIO_PDATA(_name##_post_en))
>>>>>> +
>>>>>
>>>>> Again, I'm not a fan of this many levels of macro. I can live with it
>>>>> but certainly don't want the burden of reviewing and maintaining for
>>>>> clock driver. AXG / G12 and A1 are obviously closely related, so make it common.
>>>>>
>>>>>> +#define AUDIO_MST_LRCLK(_name, _reg0, _reg1, _pdata) \
>>>>>> + AUDIO_SCLK_DIV(_name##_div, (_reg0), 0, 10, 10, 10, \
>>>>>> + (_pdata), false); \
>>>>>> + AUDIO_TRIPHASE(_name, (_reg1), 1, 1, 3, 5, \
>>>>>> + AUDIO_PDATA(_name##_div))
>>>>>> +
>>>>>> +AUDIO_MST_SCLK(audio_mst_a_sclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1,
>>>>>> + AUDIO_PDATA(audio_mst_a_mclk));
>>>>>> +AUDIO_MST_SCLK(audio_mst_b_sclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1,
>>>>>> + AUDIO_PDATA(audio_mst_b_mclk));
>>>>>> +AUDIO_MST_SCLK(audio_mst_c_sclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1,
>>>>>> + AUDIO_PDATA(audio_mst_c_mclk));
>>>>>> +AUDIO_MST_SCLK(audio_mst_d_sclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1,
>>>>>> + AUDIO_PDATA(audio_mst_d_mclk));
>>>>>> +
>>>>>> +AUDIO_MST_LRCLK(audio_mst_a_lrclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1,
>>>>>> + AUDIO_PDATA(audio_mst_a_sclk_post_en));
>>>>>> +AUDIO_MST_LRCLK(audio_mst_b_lrclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1,
>>>>>> + AUDIO_PDATA(audio_mst_b_sclk_post_en));
>>>>>> +AUDIO_MST_LRCLK(audio_mst_c_lrclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1,
>>>>>> + AUDIO_PDATA(audio_mst_c_sclk_post_en));
>>>>>> +AUDIO_MST_LRCLK(audio_mst_d_lrclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1,
>>>>>> + AUDIO_PDATA(audio_mst_d_sclk_post_en));
>>>>>> +
>>>>>> +static const struct clk_parent_data a1_mst_sclk_pdata[] = {
>>>>>> + { .hw = &audio_mst_a_sclk.hw },
>>>>>> + { .hw = &audio_mst_b_sclk.hw },
>>>>>> + { .hw = &audio_mst_c_sclk.hw },
>>>>>> + { .hw = &audio_mst_d_sclk.hw },
>>>>>> + { .fw_name = "slv_sclk0" },
>>>>>> + { .fw_name = "slv_sclk1" },
>>>>>> + { .fw_name = "slv_sclk2" },
>>>>>> + { .fw_name = "slv_sclk3" },
>>>>>> + { .fw_name = "slv_sclk4" },
>>>>>> + { .fw_name = "slv_sclk5" },
>>>>>> + { .fw_name = "slv_sclk6" },
>>>>>> + { .fw_name = "slv_sclk7" },
>>>>>> + { .fw_name = "slv_sclk8" },
>>>>>> + { .fw_name = "slv_sclk9" },
>>>>>> +};
>>>>>> +
>>>>>> +static const struct clk_parent_data a1_mst_lrclk_pdata[] = {
>>>>>> + { .hw = &audio_mst_a_lrclk.hw },
>>>>>> + { .hw = &audio_mst_b_lrclk.hw },
>>>>>> + { .hw = &audio_mst_c_lrclk.hw },
>>>>>> + { .hw = &audio_mst_d_lrclk.hw },
>>>>>> + { .fw_name = "slv_lrclk0" },
>>>>>> + { .fw_name = "slv_lrclk1" },
>>>>>> + { .fw_name = "slv_lrclk2" },
>>>>>> + { .fw_name = "slv_lrclk3" },
>>>>>> + { .fw_name = "slv_lrclk4" },
>>>>>> + { .fw_name = "slv_lrclk5" },
>>>>>> + { .fw_name = "slv_lrclk6" },
>>>>>> + { .fw_name = "slv_lrclk7" },
>>>>>> + { .fw_name = "slv_lrclk8" },
>>>>>> + { .fw_name = "slv_lrclk9" },
>>>>>> +};
>>>>>> +
>>>>>> +#define AUDIO_TDM_SCLK(_name, _reg) \
>>>>>> + AUDIO_MUX(_name##_mux, (_reg), 0xf, 24, a1_mst_sclk_pdata); \
>>>>>> + AUDIO_GATE(_name##_pre_en, (_reg), 31, \
>>>>>> + AUDIO_PDATA(_name##_mux)); \
>>>>>> + AUDIO_GATE(_name##_post_en, (_reg), 30, \
>>>>>> + AUDIO_PDATA(_name##_pre_en)); \
>>>>>> + AUDIO_SCLK_WS(_name, (_reg), 1, 29, 28, \
>>>>>> + AUDIO_PDATA(_name##_post_en))
>>>>>> +
>>>>>> +#define AUDIO_TDM_LRCLK(_name, _reg) \
>>>>>> + AUDIO_MUX(_name, (_reg), 0xf, 20, a1_mst_lrclk_pdata)
>>>>>> +
>>>>>> +AUDIO_TDM_SCLK(audio_tdmin_a_sclk, AUDIO_CLK_TDMIN_A_CTRL);
>>>>>> +AUDIO_TDM_SCLK(audio_tdmin_b_sclk, AUDIO_CLK_TDMIN_B_CTRL);
>>>>>> +AUDIO_TDM_SCLK(audio_tdmin_lb_sclk, AUDIO_CLK_TDMIN_LB_CTRL);
>>>>>> +AUDIO_TDM_SCLK(audio_tdmout_a_sclk, AUDIO_CLK_TDMOUT_A_CTRL);
>>>>>> +AUDIO_TDM_SCLK(audio_tdmout_b_sclk, AUDIO_CLK_TDMOUT_B_CTRL);
>>>>>> +
>>>>>> +AUDIO_TDM_LRCLK(audio_tdmin_a_lrclk, AUDIO_CLK_TDMIN_A_CTRL);
>>>>>> +AUDIO_TDM_LRCLK(audio_tdmin_b_lrclk, AUDIO_CLK_TDMIN_B_CTRL);
>>>>>> +AUDIO_TDM_LRCLK(audio_tdmin_lb_lrclk, AUDIO_CLK_TDMIN_LB_CTRL);
>>>>>> +AUDIO_TDM_LRCLK(audio_tdmout_a_lrclk, AUDIO_CLK_TDMOUT_A_CTRL);
>>>>>> +AUDIO_TDM_LRCLK(audio_tdmout_b_lrclk, AUDIO_CLK_TDMOUT_B_CTRL);
>>>>>> +
>>>>>> +static struct clk_hw *a1_audio_hw_clks[] = {
>>>>>> + [AUD_CLKID_DDR_ARB] = &audio_ddr_arb.hw,
>>>>>> + [AUD_CLKID_TDMIN_A] = &audio_tdmin_a.hw,
>>>>>> + [AUD_CLKID_TDMIN_B] = &audio_tdmin_b.hw,
>>>>>> + [AUD_CLKID_TDMIN_LB] = &audio_tdmin_lb.hw,
>>>>>> + [AUD_CLKID_LOOPBACK] = &audio_loopback.hw,
>>>>>> + [AUD_CLKID_TDMOUT_A] = &audio_tdmout_a.hw,
>>>>>> + [AUD_CLKID_TDMOUT_B] = &audio_tdmout_b.hw,
>>>>>> + [AUD_CLKID_FRDDR_A] = &audio_frddr_a.hw,
>>>>>> + [AUD_CLKID_FRDDR_B] = &audio_frddr_b.hw,
>>>>>> + [AUD_CLKID_TODDR_A] = &audio_toddr_a.hw,
>>>>>> + [AUD_CLKID_TODDR_B] = &audio_toddr_b.hw,
>>>>>> + [AUD_CLKID_SPDIFIN] = &audio_spdifin.hw,
>>>>>> + [AUD_CLKID_RESAMPLE] = &audio_resample.hw,
>>>>>> + [AUD_CLKID_EQDRC] = &audio_eqdrc.hw,
>>>>>> + [AUD_CLKID_LOCKER] = &audio_audiolocker.hw,
>>>>>> + [AUD_CLKID_MST_A_MCLK_SEL] = &audio_mst_a_mclk_mux.hw,
>>>>>> + [AUD_CLKID_MST_A_MCLK_DIV] = &audio_mst_a_mclk_div.hw,
>>>>>> + [AUD_CLKID_MST_A_MCLK] = &audio_mst_a_mclk.hw,
>>>>>> + [AUD_CLKID_MST_B_MCLK_SEL] = &audio_mst_b_mclk_mux.hw,
>>>>>> + [AUD_CLKID_MST_B_MCLK_DIV] = &audio_mst_b_mclk_div.hw,
>>>>>> + [AUD_CLKID_MST_B_MCLK] = &audio_mst_b_mclk.hw,
>>>>>> + [AUD_CLKID_MST_C_MCLK_SEL] = &audio_mst_c_mclk_mux.hw,
>>>>>> + [AUD_CLKID_MST_C_MCLK_DIV] = &audio_mst_c_mclk_div.hw,
>>>>>> + [AUD_CLKID_MST_C_MCLK] = &audio_mst_c_mclk.hw,
>>>>>> + [AUD_CLKID_MST_D_MCLK_SEL] = &audio_mst_d_mclk_mux.hw,
>>>>>> + [AUD_CLKID_MST_D_MCLK_DIV] = &audio_mst_d_mclk_div.hw,
>>>>>> + [AUD_CLKID_MST_D_MCLK] = &audio_mst_d_mclk.hw,
>>>>>> + [AUD_CLKID_RESAMPLE_CLK_SEL] = &audio_resample_clk_mux.hw,
>>>>>> + [AUD_CLKID_RESAMPLE_CLK_DIV] = &audio_resample_clk_div.hw,
>>>>>> + [AUD_CLKID_RESAMPLE_CLK] = &audio_resample_clk.hw,
>>>>>> + [AUD_CLKID_LOCKER_IN_CLK_SEL] = &audio_locker_in_clk_mux.hw,
>>>>>> + [AUD_CLKID_LOCKER_IN_CLK_DIV] = &audio_locker_in_clk_div.hw,
>>>>>> + [AUD_CLKID_LOCKER_IN_CLK] = &audio_locker_in_clk.hw,
>>>>>> + [AUD_CLKID_LOCKER_OUT_CLK_SEL] = &audio_locker_out_clk_mux.hw,
>>>>>> + [AUD_CLKID_LOCKER_OUT_CLK_DIV] = &audio_locker_out_clk_div.hw,
>>>>>> + [AUD_CLKID_LOCKER_OUT_CLK] = &audio_locker_out_clk.hw,
>>>>>> + [AUD_CLKID_SPDIFIN_CLK_SEL] = &audio_spdifin_clk_mux.hw,
>>>>>> + [AUD_CLKID_SPDIFIN_CLK_DIV] = &audio_spdifin_clk_div.hw,
>>>>>> + [AUD_CLKID_SPDIFIN_CLK] = &audio_spdifin_clk.hw,
>>>>>> + [AUD_CLKID_EQDRC_CLK_SEL] = &audio_eqdrc_clk_mux.hw,
>>>>>> + [AUD_CLKID_EQDRC_CLK_DIV] = &audio_eqdrc_clk_div.hw,
>>>>>> + [AUD_CLKID_EQDRC_CLK] = &audio_eqdrc_clk.hw,
>>>>>> + [AUD_CLKID_MST_A_SCLK_PRE_EN] = &audio_mst_a_sclk_pre_en.hw,
>>>>>> + [AUD_CLKID_MST_A_SCLK_DIV] = &audio_mst_a_sclk_div.hw,
>>>>>> + [AUD_CLKID_MST_A_SCLK_POST_EN] = &audio_mst_a_sclk_post_en.hw,
>>>>>> + [AUD_CLKID_MST_A_SCLK] = &audio_mst_a_sclk.hw,
>>>>>> + [AUD_CLKID_MST_B_SCLK_PRE_EN] = &audio_mst_b_sclk_pre_en.hw,
>>>>>> + [AUD_CLKID_MST_B_SCLK_DIV] = &audio_mst_b_sclk_div.hw,
>>>>>> + [AUD_CLKID_MST_B_SCLK_POST_EN] = &audio_mst_b_sclk_post_en.hw,
>>>>>> + [AUD_CLKID_MST_B_SCLK] = &audio_mst_b_sclk.hw,
>>>>>> + [AUD_CLKID_MST_C_SCLK_PRE_EN] = &audio_mst_c_sclk_pre_en.hw,
>>>>>> + [AUD_CLKID_MST_C_SCLK_DIV] = &audio_mst_c_sclk_div.hw,
>>>>>> + [AUD_CLKID_MST_C_SCLK_POST_EN] = &audio_mst_c_sclk_post_en.hw,
>>>>>> + [AUD_CLKID_MST_C_SCLK] = &audio_mst_c_sclk.hw,
>>>>>> + [AUD_CLKID_MST_D_SCLK_PRE_EN] = &audio_mst_d_sclk_pre_en.hw,
>>>>>> + [AUD_CLKID_MST_D_SCLK_DIV] = &audio_mst_d_sclk_div.hw,
>>>>>> + [AUD_CLKID_MST_D_SCLK_POST_EN] = &audio_mst_d_sclk_post_en.hw,
>>>>>> + [AUD_CLKID_MST_D_SCLK] = &audio_mst_d_sclk.hw,
>>>>>> + [AUD_CLKID_MST_A_LRCLK_DIV] = &audio_mst_a_lrclk_div.hw,
>>>>>> + [AUD_CLKID_MST_A_LRCLK] = &audio_mst_a_lrclk.hw,
>>>>>> + [AUD_CLKID_MST_B_LRCLK_DIV] = &audio_mst_b_lrclk_div.hw,
>>>>>> + [AUD_CLKID_MST_B_LRCLK] = &audio_mst_b_lrclk.hw,
>>>>>> + [AUD_CLKID_MST_C_LRCLK_DIV] = &audio_mst_c_lrclk_div.hw,
>>>>>> + [AUD_CLKID_MST_C_LRCLK] = &audio_mst_c_lrclk.hw,
>>>>>> + [AUD_CLKID_MST_D_LRCLK_DIV] = &audio_mst_d_lrclk_div.hw,
>>>>>> + [AUD_CLKID_MST_D_LRCLK] = &audio_mst_d_lrclk.hw,
>>>>>> + [AUD_CLKID_TDMIN_A_SCLK_SEL] = &audio_tdmin_a_sclk_mux.hw,
>>>>>> + [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &audio_tdmin_a_sclk_pre_en.hw,
>>>>>> + [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &audio_tdmin_a_sclk_post_en.hw,
>>>>>> + [AUD_CLKID_TDMIN_A_SCLK] = &audio_tdmin_a_sclk.hw,
>>>>>> + [AUD_CLKID_TDMIN_A_LRCLK] = &audio_tdmin_a_lrclk.hw,
>>>>>> + [AUD_CLKID_TDMIN_B_SCLK_SEL] = &audio_tdmin_b_sclk_mux.hw,
>>>>>> + [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &audio_tdmin_b_sclk_pre_en.hw,
>>>>>> + [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &audio_tdmin_b_sclk_post_en.hw,
>>>>>> + [AUD_CLKID_TDMIN_B_SCLK] = &audio_tdmin_b_sclk.hw,
>>>>>> + [AUD_CLKID_TDMIN_B_LRCLK] = &audio_tdmin_b_lrclk.hw,
>>>>>> + [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &audio_tdmin_lb_sclk_mux.hw,
>>>>>> + [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &audio_tdmin_lb_sclk_pre_en.hw,
>>>>>> + [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &audio_tdmin_lb_sclk_post_en.hw,
>>>>>> + [AUD_CLKID_TDMIN_LB_SCLK] = &audio_tdmin_lb_sclk.hw,
>>>>>> + [AUD_CLKID_TDMIN_LB_LRCLK] = &audio_tdmin_lb_lrclk.hw,
>>>>>> + [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &audio_tdmout_a_sclk_mux.hw,
>>>>>> + [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &audio_tdmout_a_sclk_pre_en.hw,
>>>>>> + [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &audio_tdmout_a_sclk_post_en.hw,
>>>>>> + [AUD_CLKID_TDMOUT_A_SCLK] = &audio_tdmout_a_sclk.hw,
>>>>>> + [AUD_CLKID_TDMOUT_A_LRCLK] = &audio_tdmout_a_lrclk.hw,
>>>>>> + [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &audio_tdmout_b_sclk_mux.hw,
>>>>>> + [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &audio_tdmout_b_sclk_pre_en.hw,
>>>>>> + [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &audio_tdmout_b_sclk_post_en.hw,
>>>>>> + [AUD_CLKID_TDMOUT_B_SCLK] = &audio_tdmout_b_sclk.hw,
>>>>>> + [AUD_CLKID_TDMOUT_B_LRCLK] = &audio_tdmout_b_lrclk.hw,
>>>>>> +
>>>>>> + [AUD2_CLKID_DDR_ARB] = &audio2_ddr_arb.hw,
>>>>>> + [AUD2_CLKID_PDM] = &audio2_pdm.hw,
>>>>>> + [AUD2_CLKID_TDMIN_VAD] = &audio2_tdmin_vad.hw,
>>>>>> + [AUD2_CLKID_TODDR_VAD] = &audio2_toddr_vad.hw,
>>>>>> + [AUD2_CLKID_VAD] = &audio2_vad.hw,
>>>>>> + [AUD2_CLKID_AUDIOTOP] = &audio2_audiotop.hw,
>>>>>> + [AUD2_CLKID_VAD_MCLK_SEL] = &audio2_vad_mclk_mux.hw,
>>>>>> + [AUD2_CLKID_VAD_MCLK_DIV] = &audio2_vad_mclk_div.hw,
>>>>>> + [AUD2_CLKID_VAD_MCLK] = &audio2_vad_mclk.hw,
>>>>>> + [AUD2_CLKID_VAD_CLK_SEL] = &audio2_vad_clk_mux.hw,
>>>>>> + [AUD2_CLKID_VAD_CLK_DIV] = &audio2_vad_clk_div.hw,
>>>>>> + [AUD2_CLKID_VAD_CLK] = &audio2_vad_clk.hw,
>>>>>> + [AUD2_CLKID_PDM_DCLK_SEL] = &audio2_pdm_dclk_mux.hw,
>>>>>> + [AUD2_CLKID_PDM_DCLK_DIV] = &audio2_pdm_dclk_div.hw,
>>>>>> + [AUD2_CLKID_PDM_DCLK] = &audio2_pdm_dclk.hw,
>>>>>> + [AUD2_CLKID_PDM_SYSCLK_SEL] = &audio2_pdm_sysclk_mux.hw,
>>>>>> + [AUD2_CLKID_PDM_SYSCLK_DIV] = &audio2_pdm_sysclk_div.hw,
>>>>>> + [AUD2_CLKID_PDM_SYSCLK] = &audio2_pdm_sysclk.hw,
>>>>>> +};
>>>>>> +
>>>>>> +static struct meson_clk_hw_data a1_audio_clks = {
>>>>>> + .hws = a1_audio_hw_clks,
>>>>>> + .num = ARRAY_SIZE(a1_audio_hw_clks),
>>>>>> +};
>>>>>> +
>>>>>> +static struct regmap *a1_audio_map(struct platform_device *pdev,
>>>>>> + unsigned int index)
>>>>>> +{
>>>>>> + char name[32];
>>>>>> + const struct regmap_config cfg = {
>>>>>> + .reg_bits = 32,
>>>>>> + .val_bits = 32,
>>>>>> + .reg_stride = 4,
>>>>>> + .name = name,
>>>>>
>>>>> Not necessary
>>>>>
>>>>
>>>> This implementation uses two regmaps, and this field allow to avoid
>>>> errors like this:
>>>>
>>>> [ 0.145530] debugfs: Directory 'fe050000.audio-clock-controller' with
>>>> parent 'regmap' already present!
>>>>
>>>>>> + };
>>>>>> + void __iomem *base;
>>>>>> +
>>>>>> + base = devm_platform_ioremap_resource(pdev, index);
>>>>>> + if (IS_ERR(base))
>>>>>> + return base;
>>>>>> +
>>>>>> + scnprintf(name, sizeof(name), "%d", index);
>>>>>> + return devm_regmap_init_mmio(&pdev->dev, base, &cfg);
>>>>>> +}
>>>>>
>>>>> That is overengineered. Please keep it simple. Declare the regmap_config
>>>>> as static const global, and do it like axg-audio please.
>>>>>
>>>>
>>>> This only reason why it is not "static const" because I need to set
>>>> unique name for each regmap.
>>>>
>>>>>> +
>>>>>> +static int a1_register_clk(struct platform_device *pdev,
>>>>>> + struct regmap *map0, struct regmap *map1,
>>>>>> + struct clk_hw *hw)
>>>>>> +{
>>>>>> + struct clk_regmap *clk = container_of(hw, struct clk_regmap, hw);
>>>>>> +
>>>>>> + if (!hw)
>>>>>> + return 0;
>>>>>> +
>>>>>> + switch ((unsigned long)clk->map) {
>>>>>> + case AUDIO_RANGE_0:
>>>>>> + clk->map = map0;
>>>>>> + break;
>>>>>> + case AUDIO_RANGE_1:
>>>>>> + clk->map = map1;
>>>>>> + break;
>>>>>
>>>>> ... fishy
>>>>>
>>>>>> + default:
>>>>>> + WARN_ON(1);
>>>>>> + return -EINVAL;
>>>>>> + }
>>>>>> +
>>>>>> + return devm_clk_hw_register(&pdev->dev, hw);
>>>>>> +}
>>>>>> +
>>>>>> +static int a1_audio_clkc_probe(struct platform_device *pdev)
>>>>>> +{
>>>>>> + struct regmap *map0, *map1;
>>>>>> + struct clk *clk;
>>>>>> + unsigned int i;
>>>>>> + int ret;
>>>>>> +
>>>>>> + clk = devm_clk_get_enabled(&pdev->dev, "pclk");
>>>>>> + if (WARN_ON(IS_ERR(clk)))
>>>>>> + return PTR_ERR(clk);
>>>>>> +
>>>>>> + map0 = a1_audio_map(pdev, 0);
>>>>>> + if (IS_ERR(map0))
>>>>>> + return PTR_ERR(map0);
>>>>>> +
>>>>>> + map1 = a1_audio_map(pdev, 1);
>>>>>> + if (IS_ERR(map1))
>>>>>> + return PTR_ERR(map1);
>>>>>
>>>>> No - Looks to me you just have two clock controllers you are trying
>>>>> force into one.
>>>>>
>>>>
>>>> See the begining.
>>>>
>>>>>> +
>>>>>> + /*
>>>>>> + * Register and enable AUD2_CLKID_AUDIOTOP clock first. Unless
>>>>>> + * it is enabled any read/write to 'map0' hangs the CPU.
>>>>>> + */
>>>>>> +
>>>>>> + ret = a1_register_clk(pdev, map0, map1,
>>>>>> + a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> + ret = clk_prepare_enable(a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]->clk);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>
>>>>> Again, this shows 2 devices. The one related to your 'map0' should
>>>>> request AUD2_CLKID_AUDIOTOP as input and enable it right away.
>>>>>
>>>>
>>>> See the begining.
>>>>
>>>>>> +
>>>>>> + for (i = 0; i < a1_audio_clks.num; i++) {
>>>>>> + if (i == AUD2_CLKID_AUDIOTOP)
>>>>>> + continue;
>>>>>> +
>>>>>> + ret = a1_register_clk(pdev, map0, map1, a1_audio_clks.hws[i]);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> + }
>>>>>> +
>>>>>> + ret = devm_of_clk_add_hw_provider(&pdev->dev, meson_clk_hw_get,
>>>>>> + &a1_audio_clks);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> + BUILD_BUG_ON((unsigned long)AUDIO_REG_MAP(AUDIO_SW_RESET0) !=
>>>>>> + AUDIO_RANGE_0);
>>>>>
>>>>> Why is that necessary ?
>>>>>
>>>>
>>>> A little paranoia. Here AUDIO_SW_RESET0 is handled as map0's register,
>>>> and I want to assert it.
>>>>
>>>>>> + return meson_audio_rstc_register(&pdev->dev, map0,
>>>>>> + AUDIO_REG_OFFSET(AUDIO_SW_RESET0), 32);
>>>>>> +}
>>>>>> +
>>>>>> +static const struct of_device_id a1_audio_clkc_match_table[] = {
>>>>>> + { .compatible = "amlogic,a1-audio-clkc", },
>>>>>> + {}
>>>>>> +};
>>>>>> +MODULE_DEVICE_TABLE(of, a1_audio_clkc_match_table);
>>>>>> +
>>>>>> +static struct platform_driver a1_audio_clkc_driver = {
>>>>>> + .probe = a1_audio_clkc_probe,
>>>>>> + .driver = {
>>>>>> + .name = "a1-audio-clkc",
>>>>>> + .of_match_table = a1_audio_clkc_match_table,
>>>>>> + },
>>>>>> +};
>>>>>> +module_platform_driver(a1_audio_clkc_driver);
>>>>>> +
>>>>>> +MODULE_DESCRIPTION("Amlogic A1 Audio Clock driver");
>>>>>> +MODULE_AUTHOR("Jan Dakinevich <[email protected]>");
>>>>>> +MODULE_LICENSE("GPL");
>>>>>> diff --git a/drivers/clk/meson/a1-audio.h b/drivers/clk/meson/a1-audio.h
>>>>>> new file mode 100644
>>>>>> index 000000000000..f994e87276cd
>>>>>> --- /dev/null
>>>>>> +++ b/drivers/clk/meson/a1-audio.h
>>>>>> @@ -0,0 +1,58 @@
>>>>>> +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
>>>>>> +/*
>>>>>> + * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
>>>>>> + *
>>>>>> + * Author: Jan Dakinevich <[email protected]>
>>>>>> + */
>>>>>> +
>>>>>> +#ifndef __A1_AUDIO_H
>>>>>> +#define __A1_AUDIO_H
>>>>>> +
>>>>>> +#define AUDIO_RANGE_0 0xa
>>>>>> +#define AUDIO_RANGE_1 0xb
>>>>>> +#define AUDIO_RANGE_SHIFT 16
>>>>>> +
>>>>>> +#define AUDIO_REG(_range, _offset) \
>>>>>> + (((_range) << AUDIO_RANGE_SHIFT) + (_offset))
>>>>>> +
>>>>>> +#define AUDIO_REG_OFFSET(_reg) \
>>>>>> + ((_reg) & ((1 << AUDIO_RANGE_SHIFT) - 1))
>>>>>> +
>>>>>> +#define AUDIO_REG_MAP(_reg) \
>>>>>> + ((void *)((_reg) >> AUDIO_RANGE_SHIFT))
>>>>>
>>>>> That is seriouly overengineered.
>>>>> The following are offset. Just write what they are.
>>>>>
>>>>
>>>> This is all in order to keep range's identifier together with offset and
>>>> then use it to store the identifier in clk_regmaps.
>>>>
>>>>> There is not reason to put that into a header. It is only going to be
>>>>> used by a single driver.
>>>>>>> +
>>>>>> +#define AUDIO_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_0, 0x000)
>>>>>> +#define AUDIO_MCLK_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x008)
>>>>>> +#define AUDIO_MCLK_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x00c)
>>>>>> +#define AUDIO_MCLK_C_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x010)
>>>>>> +#define AUDIO_MCLK_D_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x014)
>>>>>> +#define AUDIO_MCLK_E_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x018)
>>>>>> +#define AUDIO_MCLK_F_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x01c)
>>>>>> +#define AUDIO_SW_RESET0 AUDIO_REG(AUDIO_RANGE_0, 0x028)
>>>>>> +#define AUDIO_MST_A_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x040)
>>>>>> +#define AUDIO_MST_A_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x044)
>>>>>> +#define AUDIO_MST_B_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x048)
>>>>>> +#define AUDIO_MST_B_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x04c)
>>>>>> +#define AUDIO_MST_C_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x050)
>>>>>> +#define AUDIO_MST_C_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x054)
>>>>>> +#define AUDIO_MST_D_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x058)
>>>>>> +#define AUDIO_MST_D_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x05c)
>>>>>> +#define AUDIO_CLK_TDMIN_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x080)
>>>>>> +#define AUDIO_CLK_TDMIN_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x084)
>>>>>> +#define AUDIO_CLK_TDMIN_LB_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x08c)
>>>>>> +#define AUDIO_CLK_TDMOUT_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x090)
>>>>>> +#define AUDIO_CLK_TDMOUT_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x094)
>>>>>> +#define AUDIO_CLK_SPDIFIN_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x09c)
>>>>>> +#define AUDIO_CLK_RESAMPLE_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a4)
>>>>>> +#define AUDIO_CLK_LOCKER_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a8)
>>>>>> +#define AUDIO_CLK_EQDRC_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0c0)
>>>>>> +
>>>>>> +#define AUDIO2_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_1, 0x00c)
>>>>>> +#define AUDIO2_MCLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x040)
>>>>>> +#define AUDIO2_CLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x044)
>>>>>> +#define AUDIO2_CLK_PDMIN_CTRL0 AUDIO_REG(AUDIO_RANGE_1, 0x058)
>>>>>> +#define AUDIO2_CLK_PDMIN_CTRL1 AUDIO_REG(AUDIO_RANGE_1, 0x05c)
>>>>>> +
>>>>>> +#include <dt-bindings/clock/amlogic,a1-audio-clkc.h>
>>>>>> +
>>>>>> +#endif /* __A1_AUDIO_H */
>>>>>
>>>>>
>>>
>>>
>
>

--
Best regards
Jan Dakinevich

2024-03-27 15:16:43

by Jerome Brunet

[permalink] [raw]
Subject: Re: [PATCH 04/25] clk: meson: a1: add the audio clock controller driver


On Tue 26 Mar 2024 at 21:44, Jan Dakinevich <[email protected]> wrote:

> On 3/26/24 18:26, Jerome Brunet wrote:
>>
>> On Sat 23 Mar 2024 at 21:02, Jan Dakinevich <[email protected]> wrote:
>>
>>> Jerome, I have reworked my driver reusing axg-audio code as most as I
>>> could and now I have one more question. Lets see on this definition from
>>> axg-audio:
>>>
>>> #define AUD_MST_MUX(_name, _reg, _flag) \
>>> AUD_MUX(_name##_sel, _reg, 0x7, 24, _flag, \
>>> mst_mux_parent_data, 0)
>>>
>>> #define AUD_MST_MCLK_MUX(_name, _reg) \
>>> AUD_MST_MUX(_name, _reg, CLK_MUX_ROUND_CLOSEST)
>>>
>>> CLK_SET_RATE_PARENT is not set here. But why? It means, that topmost pll
>>> clock will not be reconfigured at runtime to satisfy the rate that was
>>> requested from axg-tdm.
>>>
>>
>> Yes, that is by design. It is another area where mainline audio differs
>> greatly from AML vendor code. The PLLs are expected be to fixed and the
>> audio master clock will reparent to the most adequate PLL source
>> depending on the use case.
>>
>> This is how we manage to satisfy all audio interfaces with a very
>> limited number of PLLs
>>
>> On AXG/G12 there is at most 6 concurrent interfaces (3 FRDDR/TODDR) - 8
>> on sm1 - and we can satisfy on that with 3 PLLs. That would not be
>> possible if interfaces were having their way with the PLLs, reseting it
>> everytime a stream is started.
>> > The PLL rate should be carefully chosen so it can be derived easily. On
>> AXG/G12/SM1 that is:
>> * one PLL per rate family, to maximize clock precision
>> * x24 x32: to handle different sample sizes
>> * x2 until we reach the PLL limits to allow higher rates such as 384kHz
>> or even higher
>>
>
> Thank you. Now it has become much clearer.
>
>> If you have less PLLs on A1, you'll have to make compromises, like a less
>> precise clock to support multiple family with one PLL.
>> This is why the PLLs are set for each platform in DT because that choice
>> may depend on the platform use case.
>>
>
> Unfortunately, on A1 we have only one PLL.
>

That where compromises comes in. Pick a rate known as 'audio friendly'
which match some rates and appromixate others, or use codec clock master.

> Yes, for us it would be better to have hifi_pll with predefined rate.
> For instance it will allow to avoid that ugly workaround in PDM (sysrate
> property, etc).

That is another problem entirely.
Krzysztof and I already covered what you should do for this.

The exact rate the PDM system clock does not matter at all because the
driver queries the actual rate of the clocks and adapts.

You have problems here only because you added CLK_SET_RATE_PARENT and
you are triggering the concurrent usage problem I explain below.

>
> But what whould be preferred for upstream? I can imagine a scenario
> where samples with different rate should be played, PDM attached to
> fclk_divN and there are no conflicts with TDM.

You are considering only PDM and 1 TDM. The SoC has 2 TDMs which could
be active concurrently a different rates.

> In this case
> reconfiguration of hifi_pll on demand could better satisfy somebody's
> requirements.

No this is not possible. Doing so does not allow all the interfaces to be
used concurrently.

* If the PLL is not protected, existing streams get broken by new
starting stream when the PLL is reconfigured
* If the PLL is protected, new stream effectively starve because no
clock may provide a 'good enough' rate for them

This is why the reference rate must carefully chosen, something CCF
cannot do for you.

For example, a source of 12.288MHz (and its powers of 2) allows to match
48 and 32kHz sample rates and approximate 44.1kHz rates with an
acceptable drift.

>
>>>
>>> On 3/19/24 11:30, Jerome Brunet wrote:
>>>>
>>>> On Tue 19 Mar 2024 at 04:47, Jan Dakinevich <[email protected]> wrote:
>>>>
>>>>> Let's start from the end:
>>>>>
>>>>>> No - Looks to me you just have two clock controllers you are trying
>>>>> force into one.
>>>>>
>>>>>> Again, this shows 2 devices. The one related to your 'map0' should
>>>>> request AUD2_CLKID_AUDIOTOP as input and enable it right away.
>>>>>
>>>>> Most of fishy workarounds that you commented is caused the fact the mmio
>>>>> of this clock controller is divided into two parts. Compare it with
>>>>> axg-audio driver, things that was part of contigous memory region (like
>>>>> pdm) here are moved to second region. Is this enough to make a guess
>>>>> that these are two devices?
>>>>
>>>> I see obsolutely no reason to think it is a single device nor to add all the quirks
>>>> you have the way you did. So yes, in that case, 2 zones, 2 devices.
>>>>
>>>>>
>>>>> Concerning AUD2_CLKID_AUDIOTOP clock, as it turned out, it must be
>>>>> enabled before enabling of clocks from second region too. That is
>>>>> AUD2_CLKID_AUDIOTOP clock feeds both parts of this clock controller.
>>>>>
>>>>
>>>> Yes. I understood the first time around and already commented on that.
>>>>
>>>>>
>>>>> On 3/15/24 12:20, Jerome Brunet wrote:
>>>>>>
>>>>>> On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <[email protected]> wrote:
>>>>>>
>>>>>>> This controller provides clocks and reset functionality for audio
>>>>>>> peripherals on Amlogic A1 SoC family.
>>>>>>>
>>>>>>> The driver is almost identical to 'axg-audio', however it would be better
>>>>>>> to keep it separate due to following reasons:
>>>>>>>
>>>>>>> - significant amount of bits has another definition. I will bring there
>>>>>>> a mess of new defines with A1_ suffixes.
>>>>>>>
>>>>>>> - registers of this controller are located in two separate regions. It
>>>>>>> will give a lot of complications for 'axg-audio' to support this.
>>>>>>>
>>>>>>> Signed-off-by: Jan Dakinevich <[email protected]>
>>>>>>> ---
>>>>>>> drivers/clk/meson/Kconfig | 13 +
>>>>>>> drivers/clk/meson/Makefile | 1 +
>>>>>>> drivers/clk/meson/a1-audio.c | 556 +++++++++++++++++++++++++++++++++++
>>>>>>> drivers/clk/meson/a1-audio.h | 58 ++++
>>>>>>> 4 files changed, 628 insertions(+)
>>>>>>> create mode 100644 drivers/clk/meson/a1-audio.c
>>>>>>> create mode 100644 drivers/clk/meson/a1-audio.h
>>>>>>>
>>>>>>> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
>>>>>>> index d6a2fa5f7e88..80c4a18c83d2 100644
>>>>>>> --- a/drivers/clk/meson/Kconfig
>>>>>>> +++ b/drivers/clk/meson/Kconfig
>>>>>>> @@ -133,6 +133,19 @@ config COMMON_CLK_A1_PERIPHERALS
>>>>>>> device, A1 SoC Family. Say Y if you want A1 Peripherals clock
>>>>>>> controller to work.
>>>>>>>
>>>>>>> +config COMMON_CLK_A1_AUDIO
>>>>>>> + tristate "Amlogic A1 SoC Audio clock controller support"
>>>>>>> + depends on ARM64
>>>>>>> + select COMMON_CLK_MESON_REGMAP
>>>>>>> + select COMMON_CLK_MESON_CLKC_UTILS
>>>>>>> + select COMMON_CLK_MESON_PHASE
>>>>>>> + select COMMON_CLK_MESON_SCLK_DIV
>>>>>>> + select COMMON_CLK_MESON_AUDIO_RSTC
>>>>>>> + help
>>>>>>> + Support for the Audio clock controller on Amlogic A113L based
>>>>>>> + device, A1 SoC Family. Say Y if you want A1 Audio clock controller
>>>>>>> + to work.
>>>>>>> +
>>>>>>> config COMMON_CLK_G12A
>>>>>>> tristate "G12 and SM1 SoC clock controllers support"
>>>>>>> depends on ARM64
>>>>>>> diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
>>>>>>> index 88d94921a4dc..4968fc7ad555 100644
>>>>>>> --- a/drivers/clk/meson/Makefile
>>>>>>> +++ b/drivers/clk/meson/Makefile
>>>>>>> @@ -20,6 +20,7 @@ obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
>>>>>>> obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
>>>>>>> obj-$(CONFIG_COMMON_CLK_A1_PLL) += a1-pll.o
>>>>>>> obj-$(CONFIG_COMMON_CLK_A1_PERIPHERALS) += a1-peripherals.o
>>>>>>> +obj-$(CONFIG_COMMON_CLK_A1_AUDIO) += a1-audio.o
>>>>>>> obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
>>>>>>> obj-$(CONFIG_COMMON_CLK_G12A) += g12a.o g12a-aoclk.o
>>>>>>> obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o meson8-ddr.o
>>>>>>> diff --git a/drivers/clk/meson/a1-audio.c b/drivers/clk/meson/a1-audio.c
>>>>>>> new file mode 100644
>>>>>>> index 000000000000..6039116c93ba
>>>>>>> --- /dev/null
>>>>>>> +++ b/drivers/clk/meson/a1-audio.c
>>>>>>> @@ -0,0 +1,556 @@
>>>>>>> +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
>>>>>>> +/*
>>>>>>> + * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
>>>>>>> + *
>>>>>>> + * Author: Jan Dakinevich <[email protected]>
>>>>>>> + */
>>>>>>> +
>>>>>>> +#include <linux/clk.h>
>>>>>>> +#include <linux/clk-provider.h>
>>>>>>> +#include <linux/init.h>
>>>>>>> +#include <linux/of_device.h>
>>>>>>> +#include <linux/module.h>
>>>>>>> +#include <linux/platform_device.h>
>>>>>>> +#include <linux/regmap.h>
>>>>>>> +#include <linux/reset.h>
>>>>>>> +#include <linux/reset-controller.h>
>>>>>>> +#include <linux/slab.h>
>>>>>>> +
>>>>>>> +#include "meson-clkc-utils.h"
>>>>>>> +#include "meson-audio-rstc.h"
>>>>>>> +#include "clk-regmap.h"
>>>>>>> +#include "clk-phase.h"
>>>>>>> +#include "sclk-div.h"
>>>>>>> +#include "a1-audio.h"
>>>>>>> +
>>>>>>> +#define AUDIO_PDATA(_name) \
>>>>>>> + ((const struct clk_parent_data[]) { { .hw = &(_name).hw } })
>>>>>>
>>>>>> Not a fan - yet another level of macro.
>>>>>>
>>>>>>> +
>>>>>>> +#define AUDIO_MUX(_name, _reg, _mask, _shift, _pdata) \
>>>>>>> +static struct clk_regmap _name = { \
>>>>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>>>>> + .data = &(struct clk_regmap_mux_data){ \
>>>>>>> + .offset = AUDIO_REG_OFFSET(_reg), \
>>>>>>> + .mask = (_mask), \
>>>>>>> + .shift = (_shift), \
>>>>>>> + }, \
>>>>>>> + .hw.init = &(struct clk_init_data) { \
>>>>>>> + .name = #_name, \
>>>>>>> + .ops = &clk_regmap_mux_ops, \
>>>>>>> + .parent_data = (_pdata), \
>>>>>>> + .num_parents = ARRAY_SIZE(_pdata), \
>>>>>>> + .flags = CLK_SET_RATE_PARENT, \
>>>>>>> + }, \
>>>>>>> +}
>>>>>>> +
>>>>>>> +#define AUDIO_DIV(_name, _reg, _shift, _width, _pdata) \
>>>>>>> +static struct clk_regmap _name = { \
>>>>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>>>>> + .data = &(struct clk_regmap_div_data){ \
>>>>>>> + .offset = AUDIO_REG_OFFSET(_reg), \
>>>>>>> + .shift = (_shift), \
>>>>>>> + .width = (_width), \
>>>>>>> + }, \
>>>>>>> + .hw.init = &(struct clk_init_data) { \
>>>>>>> + .name = #_name, \
>>>>>>> + .ops = &clk_regmap_divider_ops, \
>>>>>>> + .parent_data = (_pdata), \
>>>>>>> + .num_parents = 1, \
>>>>>>> + .flags = CLK_SET_RATE_PARENT, \
>>>>>>> + }, \
>>>>>>> +}
>>>>>>> +
>>>>>>> +#define AUDIO_GATE(_name, _reg, _bit, _pdata) \
>>>>>>> +static struct clk_regmap _name = { \
>>>>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>>>>> + .data = &(struct clk_regmap_gate_data){ \
>>>>>>> + .offset = AUDIO_REG_OFFSET(_reg), \
>>>>>>> + .bit_idx = (_bit), \
>>>>>>> + }, \
>>>>>>> + .hw.init = &(struct clk_init_data) { \
>>>>>>> + .name = #_name, \
>>>>>>> + .ops = &clk_regmap_gate_ops, \
>>>>>>> + .parent_data = (_pdata), \
>>>>>>> + .num_parents = 1, \
>>>>>>> + .flags = CLK_SET_RATE_PARENT, \
>>>>>>> + }, \
>>>>>>> +}
>>>>>>> +
>>>>>>> +#define AUDIO_SCLK_DIV(_name, _reg, _div_shift, _div_width, \
>>>>>>> + _hi_shift, _hi_width, _pdata, _set_rate_parent) \
>>>>>>> +static struct clk_regmap _name = { \
>>>>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>>>>> + .data = &(struct meson_sclk_div_data) { \
>>>>>>> + .div = { \
>>>>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>>>>> + .shift = (_div_shift), \
>>>>>>> + .width = (_div_width), \
>>>>>>> + }, \
>>>>>>> + .hi = { \
>>>>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>>>>> + .shift = (_hi_shift), \
>>>>>>> + .width = (_hi_width), \
>>>>>>> + }, \
>>>>>>> + }, \
>>>>>>> + .hw.init = &(struct clk_init_data) { \
>>>>>>> + .name = #_name, \
>>>>>>> + .ops = &meson_sclk_div_ops, \
>>>>>>> + .parent_data = (_pdata), \
>>>>>>> + .num_parents = 1, \
>>>>>>> + .flags = (_set_rate_parent) ? CLK_SET_RATE_PARENT : 0, \
>>>>>>
>>>>>> Does not help readeability. Just pass the flag as axg-audio does.
>>>>>>
>>>>>>> + }, \
>>>>>>> +}
>>>>>>> +
>>>>>>> +#define AUDIO_TRIPHASE(_name, _reg, _width, _shift0, _shift1, _shift2, \
>>>>>>> + _pdata) \
>>>>>>> +static struct clk_regmap _name = { \
>>>>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>>>>> + .data = &(struct meson_clk_triphase_data) { \
>>>>>>> + .ph0 = { \
>>>>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>>>>> + .shift = (_shift0), \
>>>>>>> + .width = (_width), \
>>>>>>> + }, \
>>>>>>> + .ph1 = { \
>>>>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>>>>> + .shift = (_shift1), \
>>>>>>> + .width = (_width), \
>>>>>>> + }, \
>>>>>>> + .ph2 = { \
>>>>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>>>>> + .shift = (_shift2), \
>>>>>>> + .width = (_width), \
>>>>>>> + }, \
>>>>>>> + }, \
>>>>>>> + .hw.init = &(struct clk_init_data) { \
>>>>>>> + .name = #_name, \
>>>>>>> + .ops = &meson_clk_triphase_ops, \
>>>>>>> + .parent_data = (_pdata), \
>>>>>>> + .num_parents = 1, \
>>>>>>> + .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \
>>>>>>> + }, \
>>>>>>> +}
>>>>>>> +
>>>>>>> +#define AUDIO_SCLK_WS(_name, _reg, _width, _shift_ph, _shift_ws, \
>>>>>>> + _pdata) \
>>>>>>> +static struct clk_regmap _name = { \
>>>>>>> + .map = AUDIO_REG_MAP(_reg), \
>>>>>>> + .data = &(struct meson_sclk_ws_inv_data) { \
>>>>>>> + .ph = { \
>>>>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>>>>> + .shift = (_shift_ph), \
>>>>>>> + .width = (_width), \
>>>>>>> + }, \
>>>>>>> + .ws = { \
>>>>>>> + .reg_off = AUDIO_REG_OFFSET(_reg), \
>>>>>>> + .shift = (_shift_ws), \
>>>>>>> + .width = (_width), \
>>>>>>> + }, \
>>>>>>> + }, \
>>>>>>> + .hw.init = &(struct clk_init_data) { \
>>>>>>> + .name = #_name, \
>>>>>>> + .ops = &meson_sclk_ws_inv_ops, \
>>>>>>> + .parent_data = (_pdata), \
>>>>>>> + .num_parents = 1, \
>>>>>>> + .flags = CLK_SET_RATE_PARENT | CLK_DUTY_CYCLE_PARENT, \
>>>>>>> + }, \
>>>>>>> +}
>>>>>>
>>>>>> All the above does essentially the same things as the macro of
>>>>>> axg-audio, to some minor differences. Yet it is another set to maintain.
>>>>>>
>>>>>
>>>>> Except one thing... Here I keep memory identifier to which this clock
>>>>> belongs:
>>>>>
>>>>> .map = AUDIO_REG_MAP(_reg),
>>>>>
>>>>> It is workaround, but ->map the only common field in clk_regmap that
>>>>> could be used for this purpose.
>>>>>
>>>>>
>>>>>> I'd much prefer if you put the axg-audio macro in a header a re-used
>>>>>> those. There would a single set to maintain. You may then specialize the
>>>>>> included in the driver C file, to avoid redundant parameters
>>>>>>
>>>>>> Rework axg-audio to use clk_parent_data if you must, but not in the same
>>>>>> series please.
>>>>>>
>>>>>>> +
>>>>>>> +static const struct clk_parent_data a1_pclk_pdata[] = {
>>>>>>> + { .fw_name = "pclk", },
>>>>>>> +};
>>>>>>> +
>>>>>>> +AUDIO_GATE(audio_ddr_arb, AUDIO_CLK_GATE_EN0, 0, a1_pclk_pdata);
>>>>>>> +AUDIO_GATE(audio_tdmin_a, AUDIO_CLK_GATE_EN0, 1, a1_pclk_pdata);
>>>>>>> +AUDIO_GATE(audio_tdmin_b, AUDIO_CLK_GATE_EN0, 2, a1_pclk_pdata);
>>>>>>> +AUDIO_GATE(audio_tdmin_lb, AUDIO_CLK_GATE_EN0, 3, a1_pclk_pdata);
>>>>>>> +AUDIO_GATE(audio_loopback, AUDIO_CLK_GATE_EN0, 4, a1_pclk_pdata);
>>>>>>> +AUDIO_GATE(audio_tdmout_a, AUDIO_CLK_GATE_EN0, 5, a1_pclk_pdata);
>>>>>>> +AUDIO_GATE(audio_tdmout_b, AUDIO_CLK_GATE_EN0, 6, a1_pclk_pdata);
>>>>>>> +AUDIO_GATE(audio_frddr_a, AUDIO_CLK_GATE_EN0, 7, a1_pclk_pdata);
>>>>>>> +AUDIO_GATE(audio_frddr_b, AUDIO_CLK_GATE_EN0, 8, a1_pclk_pdata);
>>>>>>> +AUDIO_GATE(audio_toddr_a, AUDIO_CLK_GATE_EN0, 9, a1_pclk_pdata);
>>>>>>> +AUDIO_GATE(audio_toddr_b, AUDIO_CLK_GATE_EN0, 10, a1_pclk_pdata);
>>>>>>> +AUDIO_GATE(audio_spdifin, AUDIO_CLK_GATE_EN0, 11, a1_pclk_pdata);
>>>>>>> +AUDIO_GATE(audio_resample, AUDIO_CLK_GATE_EN0, 12, a1_pclk_pdata);
>>>>>>> +AUDIO_GATE(audio_eqdrc, AUDIO_CLK_GATE_EN0, 13, a1_pclk_pdata);
>>>>>>> +AUDIO_GATE(audio_audiolocker, AUDIO_CLK_GATE_EN0, 14, a1_pclk_pdata);
>>>>>> This is what I mean by redundant parameter ^
>>>>>>
>>>>>
>>>>> Yep. I could define something like AUDIO_PCLK_GATE().
>>>>>
>>>>>>> +
>>>>>>> +AUDIO_GATE(audio2_ddr_arb, AUDIO2_CLK_GATE_EN0, 0, a1_pclk_pdata);
>>>>>>> +AUDIO_GATE(audio2_pdm, AUDIO2_CLK_GATE_EN0, 1, a1_pclk_pdata);
>>>>>>> +AUDIO_GATE(audio2_tdmin_vad, AUDIO2_CLK_GATE_EN0, 2, a1_pclk_pdata);
>>>>>>> +AUDIO_GATE(audio2_toddr_vad, AUDIO2_CLK_GATE_EN0, 3, a1_pclk_pdata);
>>>>>>> +AUDIO_GATE(audio2_vad, AUDIO2_CLK_GATE_EN0, 4, a1_pclk_pdata);
>>>>>>> +AUDIO_GATE(audio2_audiotop, AUDIO2_CLK_GATE_EN0, 7, a1_pclk_pdata);
>>>>>>> +
>>>>>>> +static const struct clk_parent_data a1_mst_pdata[] = {
>>>>>>> + { .fw_name = "dds_in" },
>>>>>>> + { .fw_name = "fclk_div2" },
>>>>>>> + { .fw_name = "fclk_div3" },
>>>>>>> + { .fw_name = "hifi_pll" },
>>>>>>> + { .fw_name = "xtal" },
>>>>>>> +};
>>>>>>> +
>>>>>>> +#define AUDIO_MST_MCLK(_name, _reg) \
>>>>>>> + AUDIO_MUX(_name##_mux, (_reg), 0x7, 24, a1_mst_pdata); \
>>>>>>> + AUDIO_DIV(_name##_div, (_reg), 0, 16, \
>>>>>>> + AUDIO_PDATA(_name##_mux)); \
>>>>>>> + AUDIO_GATE(_name, (_reg), 31, AUDIO_PDATA(_name##_div))
>>>>>>> +
>>>>>>> +AUDIO_MST_MCLK(audio_mst_a_mclk, AUDIO_MCLK_A_CTRL);
>>>>>>> +AUDIO_MST_MCLK(audio_mst_b_mclk, AUDIO_MCLK_B_CTRL);
>>>>>>> +AUDIO_MST_MCLK(audio_mst_c_mclk, AUDIO_MCLK_C_CTRL);
>>>>>>> +AUDIO_MST_MCLK(audio_mst_d_mclk, AUDIO_MCLK_D_CTRL);
>>>>>>> +AUDIO_MST_MCLK(audio_spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL);
>>>>>>> +AUDIO_MST_MCLK(audio_eqdrc_clk, AUDIO_CLK_EQDRC_CTRL);
>>>>>>> +
>>>>>>> +AUDIO_MUX(audio_resample_clk_mux, AUDIO_CLK_RESAMPLE_CTRL, 0xf, 24,
>>>>>>> + a1_mst_pdata);
>>>>>>> +AUDIO_DIV(audio_resample_clk_div, AUDIO_CLK_RESAMPLE_CTRL, 0, 8,
>>>>>>> + AUDIO_PDATA(audio_resample_clk_mux));
>>>>>>> +AUDIO_GATE(audio_resample_clk, AUDIO_CLK_RESAMPLE_CTRL, 31,
>>>>>>> + AUDIO_PDATA(audio_resample_clk_div));
>>>>>>> +
>>>>>>> +AUDIO_MUX(audio_locker_in_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 8,
>>>>>>> + a1_mst_pdata);
>>>>>>> +AUDIO_DIV(audio_locker_in_clk_div, AUDIO_CLK_LOCKER_CTRL, 0, 8,
>>>>>>> + AUDIO_PDATA(audio_locker_in_clk_mux));
>>>>>>> +AUDIO_GATE(audio_locker_in_clk, AUDIO_CLK_LOCKER_CTRL, 15,
>>>>>>> + AUDIO_PDATA(audio_locker_in_clk_div));
>>>>>>> +
>>>>>>> +AUDIO_MUX(audio_locker_out_clk_mux, AUDIO_CLK_LOCKER_CTRL, 0xf, 24,
>>>>>>> + a1_mst_pdata);
>>>>>>> +AUDIO_DIV(audio_locker_out_clk_div, AUDIO_CLK_LOCKER_CTRL, 16, 8,
>>>>>>> + AUDIO_PDATA(audio_locker_out_clk_mux));
>>>>>>> +AUDIO_GATE(audio_locker_out_clk, AUDIO_CLK_LOCKER_CTRL, 31,
>>>>>>> + AUDIO_PDATA(audio_locker_out_clk_div));
>>>>>>> +
>>>>>>> +AUDIO_MST_MCLK(audio2_vad_mclk, AUDIO2_MCLK_VAD_CTRL);
>>>>>>> +AUDIO_MST_MCLK(audio2_vad_clk, AUDIO2_CLK_VAD_CTRL);
>>>>>>> +AUDIO_MST_MCLK(audio2_pdm_dclk, AUDIO2_CLK_PDMIN_CTRL0);
>>>>>>> +AUDIO_MST_MCLK(audio2_pdm_sysclk, AUDIO2_CLK_PDMIN_CTRL1);
>>>>>>> +
>>>>>>> +#define AUDIO_MST_SCLK(_name, _reg0, _reg1, _pdata) \
>>>>>>> + AUDIO_GATE(_name##_pre_en, (_reg0), 31, (_pdata)); \
>>>>>>> + AUDIO_SCLK_DIV(_name##_div, (_reg0), 20, 10, 0, 0, \
>>>>>>> + AUDIO_PDATA(_name##_pre_en), true); \
>>>>>>> + AUDIO_GATE(_name##_post_en, (_reg0), 30, \
>>>>>>> + AUDIO_PDATA(_name##_div)); \
>>>>>>> + AUDIO_TRIPHASE(_name, (_reg1), 1, 0, 2, 4, \
>>>>>>> + AUDIO_PDATA(_name##_post_en))
>>>>>>> +
>>>>>>
>>>>>> Again, I'm not a fan of this many levels of macro. I can live with it
>>>>>> but certainly don't want the burden of reviewing and maintaining for
>>>>>> clock driver. AXG / G12 and A1 are obviously closely related, so make it common.
>>>>>>
>>>>>>> +#define AUDIO_MST_LRCLK(_name, _reg0, _reg1, _pdata) \
>>>>>>> + AUDIO_SCLK_DIV(_name##_div, (_reg0), 0, 10, 10, 10, \
>>>>>>> + (_pdata), false); \
>>>>>>> + AUDIO_TRIPHASE(_name, (_reg1), 1, 1, 3, 5, \
>>>>>>> + AUDIO_PDATA(_name##_div))
>>>>>>> +
>>>>>>> +AUDIO_MST_SCLK(audio_mst_a_sclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1,
>>>>>>> + AUDIO_PDATA(audio_mst_a_mclk));
>>>>>>> +AUDIO_MST_SCLK(audio_mst_b_sclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1,
>>>>>>> + AUDIO_PDATA(audio_mst_b_mclk));
>>>>>>> +AUDIO_MST_SCLK(audio_mst_c_sclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1,
>>>>>>> + AUDIO_PDATA(audio_mst_c_mclk));
>>>>>>> +AUDIO_MST_SCLK(audio_mst_d_sclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1,
>>>>>>> + AUDIO_PDATA(audio_mst_d_mclk));
>>>>>>> +
>>>>>>> +AUDIO_MST_LRCLK(audio_mst_a_lrclk, AUDIO_MST_A_SCLK_CTRL0, AUDIO_MST_A_SCLK_CTRL1,
>>>>>>> + AUDIO_PDATA(audio_mst_a_sclk_post_en));
>>>>>>> +AUDIO_MST_LRCLK(audio_mst_b_lrclk, AUDIO_MST_B_SCLK_CTRL0, AUDIO_MST_B_SCLK_CTRL1,
>>>>>>> + AUDIO_PDATA(audio_mst_b_sclk_post_en));
>>>>>>> +AUDIO_MST_LRCLK(audio_mst_c_lrclk, AUDIO_MST_C_SCLK_CTRL0, AUDIO_MST_C_SCLK_CTRL1,
>>>>>>> + AUDIO_PDATA(audio_mst_c_sclk_post_en));
>>>>>>> +AUDIO_MST_LRCLK(audio_mst_d_lrclk, AUDIO_MST_D_SCLK_CTRL0, AUDIO_MST_D_SCLK_CTRL1,
>>>>>>> + AUDIO_PDATA(audio_mst_d_sclk_post_en));
>>>>>>> +
>>>>>>> +static const struct clk_parent_data a1_mst_sclk_pdata[] = {
>>>>>>> + { .hw = &audio_mst_a_sclk.hw },
>>>>>>> + { .hw = &audio_mst_b_sclk.hw },
>>>>>>> + { .hw = &audio_mst_c_sclk.hw },
>>>>>>> + { .hw = &audio_mst_d_sclk.hw },
>>>>>>> + { .fw_name = "slv_sclk0" },
>>>>>>> + { .fw_name = "slv_sclk1" },
>>>>>>> + { .fw_name = "slv_sclk2" },
>>>>>>> + { .fw_name = "slv_sclk3" },
>>>>>>> + { .fw_name = "slv_sclk4" },
>>>>>>> + { .fw_name = "slv_sclk5" },
>>>>>>> + { .fw_name = "slv_sclk6" },
>>>>>>> + { .fw_name = "slv_sclk7" },
>>>>>>> + { .fw_name = "slv_sclk8" },
>>>>>>> + { .fw_name = "slv_sclk9" },
>>>>>>> +};
>>>>>>> +
>>>>>>> +static const struct clk_parent_data a1_mst_lrclk_pdata[] = {
>>>>>>> + { .hw = &audio_mst_a_lrclk.hw },
>>>>>>> + { .hw = &audio_mst_b_lrclk.hw },
>>>>>>> + { .hw = &audio_mst_c_lrclk.hw },
>>>>>>> + { .hw = &audio_mst_d_lrclk.hw },
>>>>>>> + { .fw_name = "slv_lrclk0" },
>>>>>>> + { .fw_name = "slv_lrclk1" },
>>>>>>> + { .fw_name = "slv_lrclk2" },
>>>>>>> + { .fw_name = "slv_lrclk3" },
>>>>>>> + { .fw_name = "slv_lrclk4" },
>>>>>>> + { .fw_name = "slv_lrclk5" },
>>>>>>> + { .fw_name = "slv_lrclk6" },
>>>>>>> + { .fw_name = "slv_lrclk7" },
>>>>>>> + { .fw_name = "slv_lrclk8" },
>>>>>>> + { .fw_name = "slv_lrclk9" },
>>>>>>> +};
>>>>>>> +
>>>>>>> +#define AUDIO_TDM_SCLK(_name, _reg) \
>>>>>>> + AUDIO_MUX(_name##_mux, (_reg), 0xf, 24, a1_mst_sclk_pdata); \
>>>>>>> + AUDIO_GATE(_name##_pre_en, (_reg), 31, \
>>>>>>> + AUDIO_PDATA(_name##_mux)); \
>>>>>>> + AUDIO_GATE(_name##_post_en, (_reg), 30, \
>>>>>>> + AUDIO_PDATA(_name##_pre_en)); \
>>>>>>> + AUDIO_SCLK_WS(_name, (_reg), 1, 29, 28, \
>>>>>>> + AUDIO_PDATA(_name##_post_en))
>>>>>>> +
>>>>>>> +#define AUDIO_TDM_LRCLK(_name, _reg) \
>>>>>>> + AUDIO_MUX(_name, (_reg), 0xf, 20, a1_mst_lrclk_pdata)
>>>>>>> +
>>>>>>> +AUDIO_TDM_SCLK(audio_tdmin_a_sclk, AUDIO_CLK_TDMIN_A_CTRL);
>>>>>>> +AUDIO_TDM_SCLK(audio_tdmin_b_sclk, AUDIO_CLK_TDMIN_B_CTRL);
>>>>>>> +AUDIO_TDM_SCLK(audio_tdmin_lb_sclk, AUDIO_CLK_TDMIN_LB_CTRL);
>>>>>>> +AUDIO_TDM_SCLK(audio_tdmout_a_sclk, AUDIO_CLK_TDMOUT_A_CTRL);
>>>>>>> +AUDIO_TDM_SCLK(audio_tdmout_b_sclk, AUDIO_CLK_TDMOUT_B_CTRL);
>>>>>>> +
>>>>>>> +AUDIO_TDM_LRCLK(audio_tdmin_a_lrclk, AUDIO_CLK_TDMIN_A_CTRL);
>>>>>>> +AUDIO_TDM_LRCLK(audio_tdmin_b_lrclk, AUDIO_CLK_TDMIN_B_CTRL);
>>>>>>> +AUDIO_TDM_LRCLK(audio_tdmin_lb_lrclk, AUDIO_CLK_TDMIN_LB_CTRL);
>>>>>>> +AUDIO_TDM_LRCLK(audio_tdmout_a_lrclk, AUDIO_CLK_TDMOUT_A_CTRL);
>>>>>>> +AUDIO_TDM_LRCLK(audio_tdmout_b_lrclk, AUDIO_CLK_TDMOUT_B_CTRL);
>>>>>>> +
>>>>>>> +static struct clk_hw *a1_audio_hw_clks[] = {
>>>>>>> + [AUD_CLKID_DDR_ARB] = &audio_ddr_arb.hw,
>>>>>>> + [AUD_CLKID_TDMIN_A] = &audio_tdmin_a.hw,
>>>>>>> + [AUD_CLKID_TDMIN_B] = &audio_tdmin_b.hw,
>>>>>>> + [AUD_CLKID_TDMIN_LB] = &audio_tdmin_lb.hw,
>>>>>>> + [AUD_CLKID_LOOPBACK] = &audio_loopback.hw,
>>>>>>> + [AUD_CLKID_TDMOUT_A] = &audio_tdmout_a.hw,
>>>>>>> + [AUD_CLKID_TDMOUT_B] = &audio_tdmout_b.hw,
>>>>>>> + [AUD_CLKID_FRDDR_A] = &audio_frddr_a.hw,
>>>>>>> + [AUD_CLKID_FRDDR_B] = &audio_frddr_b.hw,
>>>>>>> + [AUD_CLKID_TODDR_A] = &audio_toddr_a.hw,
>>>>>>> + [AUD_CLKID_TODDR_B] = &audio_toddr_b.hw,
>>>>>>> + [AUD_CLKID_SPDIFIN] = &audio_spdifin.hw,
>>>>>>> + [AUD_CLKID_RESAMPLE] = &audio_resample.hw,
>>>>>>> + [AUD_CLKID_EQDRC] = &audio_eqdrc.hw,
>>>>>>> + [AUD_CLKID_LOCKER] = &audio_audiolocker.hw,
>>>>>>> + [AUD_CLKID_MST_A_MCLK_SEL] = &audio_mst_a_mclk_mux.hw,
>>>>>>> + [AUD_CLKID_MST_A_MCLK_DIV] = &audio_mst_a_mclk_div.hw,
>>>>>>> + [AUD_CLKID_MST_A_MCLK] = &audio_mst_a_mclk.hw,
>>>>>>> + [AUD_CLKID_MST_B_MCLK_SEL] = &audio_mst_b_mclk_mux.hw,
>>>>>>> + [AUD_CLKID_MST_B_MCLK_DIV] = &audio_mst_b_mclk_div.hw,
>>>>>>> + [AUD_CLKID_MST_B_MCLK] = &audio_mst_b_mclk.hw,
>>>>>>> + [AUD_CLKID_MST_C_MCLK_SEL] = &audio_mst_c_mclk_mux.hw,
>>>>>>> + [AUD_CLKID_MST_C_MCLK_DIV] = &audio_mst_c_mclk_div.hw,
>>>>>>> + [AUD_CLKID_MST_C_MCLK] = &audio_mst_c_mclk.hw,
>>>>>>> + [AUD_CLKID_MST_D_MCLK_SEL] = &audio_mst_d_mclk_mux.hw,
>>>>>>> + [AUD_CLKID_MST_D_MCLK_DIV] = &audio_mst_d_mclk_div.hw,
>>>>>>> + [AUD_CLKID_MST_D_MCLK] = &audio_mst_d_mclk.hw,
>>>>>>> + [AUD_CLKID_RESAMPLE_CLK_SEL] = &audio_resample_clk_mux.hw,
>>>>>>> + [AUD_CLKID_RESAMPLE_CLK_DIV] = &audio_resample_clk_div.hw,
>>>>>>> + [AUD_CLKID_RESAMPLE_CLK] = &audio_resample_clk.hw,
>>>>>>> + [AUD_CLKID_LOCKER_IN_CLK_SEL] = &audio_locker_in_clk_mux.hw,
>>>>>>> + [AUD_CLKID_LOCKER_IN_CLK_DIV] = &audio_locker_in_clk_div.hw,
>>>>>>> + [AUD_CLKID_LOCKER_IN_CLK] = &audio_locker_in_clk.hw,
>>>>>>> + [AUD_CLKID_LOCKER_OUT_CLK_SEL] = &audio_locker_out_clk_mux.hw,
>>>>>>> + [AUD_CLKID_LOCKER_OUT_CLK_DIV] = &audio_locker_out_clk_div.hw,
>>>>>>> + [AUD_CLKID_LOCKER_OUT_CLK] = &audio_locker_out_clk.hw,
>>>>>>> + [AUD_CLKID_SPDIFIN_CLK_SEL] = &audio_spdifin_clk_mux.hw,
>>>>>>> + [AUD_CLKID_SPDIFIN_CLK_DIV] = &audio_spdifin_clk_div.hw,
>>>>>>> + [AUD_CLKID_SPDIFIN_CLK] = &audio_spdifin_clk.hw,
>>>>>>> + [AUD_CLKID_EQDRC_CLK_SEL] = &audio_eqdrc_clk_mux.hw,
>>>>>>> + [AUD_CLKID_EQDRC_CLK_DIV] = &audio_eqdrc_clk_div.hw,
>>>>>>> + [AUD_CLKID_EQDRC_CLK] = &audio_eqdrc_clk.hw,
>>>>>>> + [AUD_CLKID_MST_A_SCLK_PRE_EN] = &audio_mst_a_sclk_pre_en.hw,
>>>>>>> + [AUD_CLKID_MST_A_SCLK_DIV] = &audio_mst_a_sclk_div.hw,
>>>>>>> + [AUD_CLKID_MST_A_SCLK_POST_EN] = &audio_mst_a_sclk_post_en.hw,
>>>>>>> + [AUD_CLKID_MST_A_SCLK] = &audio_mst_a_sclk.hw,
>>>>>>> + [AUD_CLKID_MST_B_SCLK_PRE_EN] = &audio_mst_b_sclk_pre_en.hw,
>>>>>>> + [AUD_CLKID_MST_B_SCLK_DIV] = &audio_mst_b_sclk_div.hw,
>>>>>>> + [AUD_CLKID_MST_B_SCLK_POST_EN] = &audio_mst_b_sclk_post_en.hw,
>>>>>>> + [AUD_CLKID_MST_B_SCLK] = &audio_mst_b_sclk.hw,
>>>>>>> + [AUD_CLKID_MST_C_SCLK_PRE_EN] = &audio_mst_c_sclk_pre_en.hw,
>>>>>>> + [AUD_CLKID_MST_C_SCLK_DIV] = &audio_mst_c_sclk_div.hw,
>>>>>>> + [AUD_CLKID_MST_C_SCLK_POST_EN] = &audio_mst_c_sclk_post_en.hw,
>>>>>>> + [AUD_CLKID_MST_C_SCLK] = &audio_mst_c_sclk.hw,
>>>>>>> + [AUD_CLKID_MST_D_SCLK_PRE_EN] = &audio_mst_d_sclk_pre_en.hw,
>>>>>>> + [AUD_CLKID_MST_D_SCLK_DIV] = &audio_mst_d_sclk_div.hw,
>>>>>>> + [AUD_CLKID_MST_D_SCLK_POST_EN] = &audio_mst_d_sclk_post_en.hw,
>>>>>>> + [AUD_CLKID_MST_D_SCLK] = &audio_mst_d_sclk.hw,
>>>>>>> + [AUD_CLKID_MST_A_LRCLK_DIV] = &audio_mst_a_lrclk_div.hw,
>>>>>>> + [AUD_CLKID_MST_A_LRCLK] = &audio_mst_a_lrclk.hw,
>>>>>>> + [AUD_CLKID_MST_B_LRCLK_DIV] = &audio_mst_b_lrclk_div.hw,
>>>>>>> + [AUD_CLKID_MST_B_LRCLK] = &audio_mst_b_lrclk.hw,
>>>>>>> + [AUD_CLKID_MST_C_LRCLK_DIV] = &audio_mst_c_lrclk_div.hw,
>>>>>>> + [AUD_CLKID_MST_C_LRCLK] = &audio_mst_c_lrclk.hw,
>>>>>>> + [AUD_CLKID_MST_D_LRCLK_DIV] = &audio_mst_d_lrclk_div.hw,
>>>>>>> + [AUD_CLKID_MST_D_LRCLK] = &audio_mst_d_lrclk.hw,
>>>>>>> + [AUD_CLKID_TDMIN_A_SCLK_SEL] = &audio_tdmin_a_sclk_mux.hw,
>>>>>>> + [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &audio_tdmin_a_sclk_pre_en.hw,
>>>>>>> + [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &audio_tdmin_a_sclk_post_en.hw,
>>>>>>> + [AUD_CLKID_TDMIN_A_SCLK] = &audio_tdmin_a_sclk.hw,
>>>>>>> + [AUD_CLKID_TDMIN_A_LRCLK] = &audio_tdmin_a_lrclk.hw,
>>>>>>> + [AUD_CLKID_TDMIN_B_SCLK_SEL] = &audio_tdmin_b_sclk_mux.hw,
>>>>>>> + [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &audio_tdmin_b_sclk_pre_en.hw,
>>>>>>> + [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &audio_tdmin_b_sclk_post_en.hw,
>>>>>>> + [AUD_CLKID_TDMIN_B_SCLK] = &audio_tdmin_b_sclk.hw,
>>>>>>> + [AUD_CLKID_TDMIN_B_LRCLK] = &audio_tdmin_b_lrclk.hw,
>>>>>>> + [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &audio_tdmin_lb_sclk_mux.hw,
>>>>>>> + [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &audio_tdmin_lb_sclk_pre_en.hw,
>>>>>>> + [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &audio_tdmin_lb_sclk_post_en.hw,
>>>>>>> + [AUD_CLKID_TDMIN_LB_SCLK] = &audio_tdmin_lb_sclk.hw,
>>>>>>> + [AUD_CLKID_TDMIN_LB_LRCLK] = &audio_tdmin_lb_lrclk.hw,
>>>>>>> + [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &audio_tdmout_a_sclk_mux.hw,
>>>>>>> + [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &audio_tdmout_a_sclk_pre_en.hw,
>>>>>>> + [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &audio_tdmout_a_sclk_post_en.hw,
>>>>>>> + [AUD_CLKID_TDMOUT_A_SCLK] = &audio_tdmout_a_sclk.hw,
>>>>>>> + [AUD_CLKID_TDMOUT_A_LRCLK] = &audio_tdmout_a_lrclk.hw,
>>>>>>> + [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &audio_tdmout_b_sclk_mux.hw,
>>>>>>> + [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &audio_tdmout_b_sclk_pre_en.hw,
>>>>>>> + [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &audio_tdmout_b_sclk_post_en.hw,
>>>>>>> + [AUD_CLKID_TDMOUT_B_SCLK] = &audio_tdmout_b_sclk.hw,
>>>>>>> + [AUD_CLKID_TDMOUT_B_LRCLK] = &audio_tdmout_b_lrclk.hw,
>>>>>>> +
>>>>>>> + [AUD2_CLKID_DDR_ARB] = &audio2_ddr_arb.hw,
>>>>>>> + [AUD2_CLKID_PDM] = &audio2_pdm.hw,
>>>>>>> + [AUD2_CLKID_TDMIN_VAD] = &audio2_tdmin_vad.hw,
>>>>>>> + [AUD2_CLKID_TODDR_VAD] = &audio2_toddr_vad.hw,
>>>>>>> + [AUD2_CLKID_VAD] = &audio2_vad.hw,
>>>>>>> + [AUD2_CLKID_AUDIOTOP] = &audio2_audiotop.hw,
>>>>>>> + [AUD2_CLKID_VAD_MCLK_SEL] = &audio2_vad_mclk_mux.hw,
>>>>>>> + [AUD2_CLKID_VAD_MCLK_DIV] = &audio2_vad_mclk_div.hw,
>>>>>>> + [AUD2_CLKID_VAD_MCLK] = &audio2_vad_mclk.hw,
>>>>>>> + [AUD2_CLKID_VAD_CLK_SEL] = &audio2_vad_clk_mux.hw,
>>>>>>> + [AUD2_CLKID_VAD_CLK_DIV] = &audio2_vad_clk_div.hw,
>>>>>>> + [AUD2_CLKID_VAD_CLK] = &audio2_vad_clk.hw,
>>>>>>> + [AUD2_CLKID_PDM_DCLK_SEL] = &audio2_pdm_dclk_mux.hw,
>>>>>>> + [AUD2_CLKID_PDM_DCLK_DIV] = &audio2_pdm_dclk_div.hw,
>>>>>>> + [AUD2_CLKID_PDM_DCLK] = &audio2_pdm_dclk.hw,
>>>>>>> + [AUD2_CLKID_PDM_SYSCLK_SEL] = &audio2_pdm_sysclk_mux.hw,
>>>>>>> + [AUD2_CLKID_PDM_SYSCLK_DIV] = &audio2_pdm_sysclk_div.hw,
>>>>>>> + [AUD2_CLKID_PDM_SYSCLK] = &audio2_pdm_sysclk.hw,
>>>>>>> +};
>>>>>>> +
>>>>>>> +static struct meson_clk_hw_data a1_audio_clks = {
>>>>>>> + .hws = a1_audio_hw_clks,
>>>>>>> + .num = ARRAY_SIZE(a1_audio_hw_clks),
>>>>>>> +};
>>>>>>> +
>>>>>>> +static struct regmap *a1_audio_map(struct platform_device *pdev,
>>>>>>> + unsigned int index)
>>>>>>> +{
>>>>>>> + char name[32];
>>>>>>> + const struct regmap_config cfg = {
>>>>>>> + .reg_bits = 32,
>>>>>>> + .val_bits = 32,
>>>>>>> + .reg_stride = 4,
>>>>>>> + .name = name,
>>>>>>
>>>>>> Not necessary
>>>>>>
>>>>>
>>>>> This implementation uses two regmaps, and this field allow to avoid
>>>>> errors like this:
>>>>>
>>>>> [ 0.145530] debugfs: Directory 'fe050000.audio-clock-controller' with
>>>>> parent 'regmap' already present!
>>>>>
>>>>>>> + };
>>>>>>> + void __iomem *base;
>>>>>>> +
>>>>>>> + base = devm_platform_ioremap_resource(pdev, index);
>>>>>>> + if (IS_ERR(base))
>>>>>>> + return base;
>>>>>>> +
>>>>>>> + scnprintf(name, sizeof(name), "%d", index);
>>>>>>> + return devm_regmap_init_mmio(&pdev->dev, base, &cfg);
>>>>>>> +}
>>>>>>
>>>>>> That is overengineered. Please keep it simple. Declare the regmap_config
>>>>>> as static const global, and do it like axg-audio please.
>>>>>>
>>>>>
>>>>> This only reason why it is not "static const" because I need to set
>>>>> unique name for each regmap.
>>>>>
>>>>>>> +
>>>>>>> +static int a1_register_clk(struct platform_device *pdev,
>>>>>>> + struct regmap *map0, struct regmap *map1,
>>>>>>> + struct clk_hw *hw)
>>>>>>> +{
>>>>>>> + struct clk_regmap *clk = container_of(hw, struct clk_regmap, hw);
>>>>>>> +
>>>>>>> + if (!hw)
>>>>>>> + return 0;
>>>>>>> +
>>>>>>> + switch ((unsigned long)clk->map) {
>>>>>>> + case AUDIO_RANGE_0:
>>>>>>> + clk->map = map0;
>>>>>>> + break;
>>>>>>> + case AUDIO_RANGE_1:
>>>>>>> + clk->map = map1;
>>>>>>> + break;
>>>>>>
>>>>>> ... fishy
>>>>>>
>>>>>>> + default:
>>>>>>> + WARN_ON(1);
>>>>>>> + return -EINVAL;
>>>>>>> + }
>>>>>>> +
>>>>>>> + return devm_clk_hw_register(&pdev->dev, hw);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int a1_audio_clkc_probe(struct platform_device *pdev)
>>>>>>> +{
>>>>>>> + struct regmap *map0, *map1;
>>>>>>> + struct clk *clk;
>>>>>>> + unsigned int i;
>>>>>>> + int ret;
>>>>>>> +
>>>>>>> + clk = devm_clk_get_enabled(&pdev->dev, "pclk");
>>>>>>> + if (WARN_ON(IS_ERR(clk)))
>>>>>>> + return PTR_ERR(clk);
>>>>>>> +
>>>>>>> + map0 = a1_audio_map(pdev, 0);
>>>>>>> + if (IS_ERR(map0))
>>>>>>> + return PTR_ERR(map0);
>>>>>>> +
>>>>>>> + map1 = a1_audio_map(pdev, 1);
>>>>>>> + if (IS_ERR(map1))
>>>>>>> + return PTR_ERR(map1);
>>>>>>
>>>>>> No - Looks to me you just have two clock controllers you are trying
>>>>>> force into one.
>>>>>>
>>>>>
>>>>> See the begining.
>>>>>
>>>>>>> +
>>>>>>> + /*
>>>>>>> + * Register and enable AUD2_CLKID_AUDIOTOP clock first. Unless
>>>>>>> + * it is enabled any read/write to 'map0' hangs the CPU.
>>>>>>> + */
>>>>>>> +
>>>>>>> + ret = a1_register_clk(pdev, map0, map1,
>>>>>>> + a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> +
>>>>>>> + ret = clk_prepare_enable(a1_audio_clks.hws[AUD2_CLKID_AUDIOTOP]->clk);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>
>>>>>> Again, this shows 2 devices. The one related to your 'map0' should
>>>>>> request AUD2_CLKID_AUDIOTOP as input and enable it right away.
>>>>>>
>>>>>
>>>>> See the begining.
>>>>>
>>>>>>> +
>>>>>>> + for (i = 0; i < a1_audio_clks.num; i++) {
>>>>>>> + if (i == AUD2_CLKID_AUDIOTOP)
>>>>>>> + continue;
>>>>>>> +
>>>>>>> + ret = a1_register_clk(pdev, map0, map1, a1_audio_clks.hws[i]);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> + }
>>>>>>> +
>>>>>>> + ret = devm_of_clk_add_hw_provider(&pdev->dev, meson_clk_hw_get,
>>>>>>> + &a1_audio_clks);
>>>>>>> + if (ret)
>>>>>>> + return ret;
>>>>>>> +
>>>>>>> + BUILD_BUG_ON((unsigned long)AUDIO_REG_MAP(AUDIO_SW_RESET0) !=
>>>>>>> + AUDIO_RANGE_0);
>>>>>>
>>>>>> Why is that necessary ?
>>>>>>
>>>>>
>>>>> A little paranoia. Here AUDIO_SW_RESET0 is handled as map0's register,
>>>>> and I want to assert it.
>>>>>
>>>>>>> + return meson_audio_rstc_register(&pdev->dev, map0,
>>>>>>> + AUDIO_REG_OFFSET(AUDIO_SW_RESET0), 32);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static const struct of_device_id a1_audio_clkc_match_table[] = {
>>>>>>> + { .compatible = "amlogic,a1-audio-clkc", },
>>>>>>> + {}
>>>>>>> +};
>>>>>>> +MODULE_DEVICE_TABLE(of, a1_audio_clkc_match_table);
>>>>>>> +
>>>>>>> +static struct platform_driver a1_audio_clkc_driver = {
>>>>>>> + .probe = a1_audio_clkc_probe,
>>>>>>> + .driver = {
>>>>>>> + .name = "a1-audio-clkc",
>>>>>>> + .of_match_table = a1_audio_clkc_match_table,
>>>>>>> + },
>>>>>>> +};
>>>>>>> +module_platform_driver(a1_audio_clkc_driver);
>>>>>>> +
>>>>>>> +MODULE_DESCRIPTION("Amlogic A1 Audio Clock driver");
>>>>>>> +MODULE_AUTHOR("Jan Dakinevich <[email protected]>");
>>>>>>> +MODULE_LICENSE("GPL");
>>>>>>> diff --git a/drivers/clk/meson/a1-audio.h b/drivers/clk/meson/a1-audio.h
>>>>>>> new file mode 100644
>>>>>>> index 000000000000..f994e87276cd
>>>>>>> --- /dev/null
>>>>>>> +++ b/drivers/clk/meson/a1-audio.h
>>>>>>> @@ -0,0 +1,58 @@
>>>>>>> +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
>>>>>>> +/*
>>>>>>> + * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
>>>>>>> + *
>>>>>>> + * Author: Jan Dakinevich <[email protected]>
>>>>>>> + */
>>>>>>> +
>>>>>>> +#ifndef __A1_AUDIO_H
>>>>>>> +#define __A1_AUDIO_H
>>>>>>> +
>>>>>>> +#define AUDIO_RANGE_0 0xa
>>>>>>> +#define AUDIO_RANGE_1 0xb
>>>>>>> +#define AUDIO_RANGE_SHIFT 16
>>>>>>> +
>>>>>>> +#define AUDIO_REG(_range, _offset) \
>>>>>>> + (((_range) << AUDIO_RANGE_SHIFT) + (_offset))
>>>>>>> +
>>>>>>> +#define AUDIO_REG_OFFSET(_reg) \
>>>>>>> + ((_reg) & ((1 << AUDIO_RANGE_SHIFT) - 1))
>>>>>>> +
>>>>>>> +#define AUDIO_REG_MAP(_reg) \
>>>>>>> + ((void *)((_reg) >> AUDIO_RANGE_SHIFT))
>>>>>>
>>>>>> That is seriouly overengineered.
>>>>>> The following are offset. Just write what they are.
>>>>>>
>>>>>
>>>>> This is all in order to keep range's identifier together with offset and
>>>>> then use it to store the identifier in clk_regmaps.
>>>>>
>>>>>> There is not reason to put that into a header. It is only going to be
>>>>>> used by a single driver.
>>>>>>>> +
>>>>>>> +#define AUDIO_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_0, 0x000)
>>>>>>> +#define AUDIO_MCLK_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x008)
>>>>>>> +#define AUDIO_MCLK_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x00c)
>>>>>>> +#define AUDIO_MCLK_C_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x010)
>>>>>>> +#define AUDIO_MCLK_D_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x014)
>>>>>>> +#define AUDIO_MCLK_E_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x018)
>>>>>>> +#define AUDIO_MCLK_F_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x01c)
>>>>>>> +#define AUDIO_SW_RESET0 AUDIO_REG(AUDIO_RANGE_0, 0x028)
>>>>>>> +#define AUDIO_MST_A_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x040)
>>>>>>> +#define AUDIO_MST_A_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x044)
>>>>>>> +#define AUDIO_MST_B_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x048)
>>>>>>> +#define AUDIO_MST_B_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x04c)
>>>>>>> +#define AUDIO_MST_C_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x050)
>>>>>>> +#define AUDIO_MST_C_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x054)
>>>>>>> +#define AUDIO_MST_D_SCLK_CTRL0 AUDIO_REG(AUDIO_RANGE_0, 0x058)
>>>>>>> +#define AUDIO_MST_D_SCLK_CTRL1 AUDIO_REG(AUDIO_RANGE_0, 0x05c)
>>>>>>> +#define AUDIO_CLK_TDMIN_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x080)
>>>>>>> +#define AUDIO_CLK_TDMIN_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x084)
>>>>>>> +#define AUDIO_CLK_TDMIN_LB_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x08c)
>>>>>>> +#define AUDIO_CLK_TDMOUT_A_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x090)
>>>>>>> +#define AUDIO_CLK_TDMOUT_B_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x094)
>>>>>>> +#define AUDIO_CLK_SPDIFIN_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x09c)
>>>>>>> +#define AUDIO_CLK_RESAMPLE_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a4)
>>>>>>> +#define AUDIO_CLK_LOCKER_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0a8)
>>>>>>> +#define AUDIO_CLK_EQDRC_CTRL AUDIO_REG(AUDIO_RANGE_0, 0x0c0)
>>>>>>> +
>>>>>>> +#define AUDIO2_CLK_GATE_EN0 AUDIO_REG(AUDIO_RANGE_1, 0x00c)
>>>>>>> +#define AUDIO2_MCLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x040)
>>>>>>> +#define AUDIO2_CLK_VAD_CTRL AUDIO_REG(AUDIO_RANGE_1, 0x044)
>>>>>>> +#define AUDIO2_CLK_PDMIN_CTRL0 AUDIO_REG(AUDIO_RANGE_1, 0x058)
>>>>>>> +#define AUDIO2_CLK_PDMIN_CTRL1 AUDIO_REG(AUDIO_RANGE_1, 0x05c)
>>>>>>> +
>>>>>>> +#include <dt-bindings/clock/amlogic,a1-audio-clkc.h>
>>>>>>> +
>>>>>>> +#endif /* __A1_AUDIO_H */
>>>>>>
>>>>>>
>>>>
>>>>
>>
>>


--
Jerome