mbox series

[00/25] Introduce support of audio for Amlogic A1 SoC family

Message ID 20240314232201.2102178-1-jan.dakinevich@salutedevices.com
Headers show
Series Introduce support of audio for Amlogic A1 SoC family | expand

Message

Jan Dakinevich March 14, 2024, 11:21 p.m. UTC
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

Comments

Jerome Brunet March 15, 2024, 8:58 a.m. UTC | #1
On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <jan.dakinevich@salutedevices.com> wrote:

> Existing values were insufficient to produce accurate clock for audio
> devices. New values are safe and most suitable to produce 48000Hz sample
> rate.

The hifi pll is not about 48k only. I see no reason to restrict the PLL
to a single setting.

You've provided no justification why the PLL driver can't reach the same
setting for 48k. The setting below is just the crude part. the fine
tuning is done done with the frac parameter so I doubt this provides a
more accurate rate.

>
> Signed-off-by: Jan Dakinevich <jan.dakinevich@salutedevices.com>
> ---
>  drivers/clk/meson/a1-pll.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/clk/meson/a1-pll.c b/drivers/clk/meson/a1-pll.c
> index 4325e8a6a3ef..00e06d03445b 100644
> --- a/drivers/clk/meson/a1-pll.c
> +++ b/drivers/clk/meson/a1-pll.c
> @@ -74,9 +74,9 @@ static struct clk_regmap fixed_pll = {
>  	},
>  };
>  
> -static const struct pll_mult_range hifi_pll_mult_range = {
> -	.min = 32,
> -	.max = 64,
> +static const struct pll_params_table hifi_pll_params_table[] = {
> +	PLL_PARAMS(128, 5),
> +	{ },
>  };
>  
>  static const struct reg_sequence hifi_init_regs[] = {
> @@ -124,7 +124,7 @@ static struct clk_regmap hifi_pll = {
>  			.shift   = 6,
>  			.width   = 1,
>  		},
> -		.range = &hifi_pll_mult_range,
> +		.table = hifi_pll_params_table,
>  		.init_regs = hifi_init_regs,
>  		.init_count = ARRAY_SIZE(hifi_init_regs),
>  	},
Jerome Brunet March 15, 2024, 9:20 a.m. UTC | #2
On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <jan.dakinevich@salutedevices.com> 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 <jan.dakinevich@salutedevices.com>
> ---
>  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 <jan.dakinevich@salutedevices.com>
> + */
> +
> +#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 <jan.dakinevich@salutedevices.com>");
> +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 <jan.dakinevich@salutedevices.com>
> + */
> +
> +#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 Brunet March 15, 2024, 10 a.m. UTC | #3
On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <jan.dakinevich@salutedevices.com> 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 <jan.dakinevich@salutedevices.com>

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 Brunet March 15, 2024, 10:01 a.m. UTC | #4
On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <jan.dakinevich@salutedevices.com> 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
Dan Carpenter March 15, 2024, 1:17 p.m. UTC | #5
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 <jan.dakinevich@salutedevices.com>

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
Mark Brown March 15, 2024, 1:33 p.m. UTC | #6
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?
Mark Brown March 15, 2024, 1:36 p.m. UTC | #7
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?
Rob Herring (Arm) March 15, 2024, 3:50 p.m. UTC | #8
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 20240314232201.2102178-1-jan.dakinevich@salutedevices.com:

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#
Neil Armstrong March 15, 2024, 4:53 p.m. UTC | #9
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
>
Jan Dakinevich March 17, 2024, 2:17 p.m. UTC | #10
On 3/15/24 11:58, Jerome Brunet wrote:
> 
> On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <jan.dakinevich@salutedevices.com> wrote:
> 
>> Existing values were insufficient to produce accurate clock for audio
>> devices. New values are safe and most suitable to produce 48000Hz sample
>> rate.
> 
> The hifi pll is not about 48k only. I see no reason to restrict the PLL
> to a single setting.
> > You've provided no justification why the PLL driver can't reach the same
> setting for 48k. The setting below is just the crude part. the fine
> tuning is done done with the frac parameter so I doubt this provides a
> more accurate rate.
> 

You are right, it is not about 48k only. However, there are two issues.

First, indeed, I could just extend the range of multipliers to 1..255.
But I am unsure if hifi_pll is able to handle whole range of
mulptipliers. The value 128 is taken from Amlogic's branch, and I am
pretty sure that it works.

Second, unfortunately frac parameter currently doesn't work. When frac
is used enabling of hifi_pll fails in meson_clk_pll_wait_lock(). I see
it when try to use 44100Hz and multipliers' range is set to 1..255. So,
support of other rates than 48k requires extra effort.

>>
>> Signed-off-by: Jan Dakinevich <jan.dakinevich@salutedevices.com>
>> ---
>>  drivers/clk/meson/a1-pll.c | 8 ++++----
>>  1 file changed, 4 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/clk/meson/a1-pll.c b/drivers/clk/meson/a1-pll.c
>> index 4325e8a6a3ef..00e06d03445b 100644
>> --- a/drivers/clk/meson/a1-pll.c
>> +++ b/drivers/clk/meson/a1-pll.c
>> @@ -74,9 +74,9 @@ static struct clk_regmap fixed_pll = {
>>  	},
>>  };
>>  
>> -static const struct pll_mult_range hifi_pll_mult_range = {
>> -	.min = 32,
>> -	.max = 64,
>> +static const struct pll_params_table hifi_pll_params_table[] = {
>> +	PLL_PARAMS(128, 5),
>> +	{ },
>>  };
>>  
>>  static const struct reg_sequence hifi_init_regs[] = {
>> @@ -124,7 +124,7 @@ static struct clk_regmap hifi_pll = {
>>  			.shift   = 6,
>>  			.width   = 1,
>>  		},
>> -		.range = &hifi_pll_mult_range,
>> +		.table = hifi_pll_params_table,
>>  		.init_regs = hifi_init_regs,
>>  		.init_count = ARRAY_SIZE(hifi_init_regs),
>>  	},
> 
>
Jan Dakinevich March 17, 2024, 3:19 p.m. UTC | #11
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?
Jan Dakinevich March 17, 2024, 4:27 p.m. UTC | #12
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.
Dmitry Rokosov March 18, 2024, 7:30 a.m. UTC | #13
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
> > 
>
Jerome Brunet March 18, 2024, 10:17 a.m. UTC | #14
On Sun 17 Mar 2024 at 17:17, Jan Dakinevich <jan.dakinevich@salutedevices.com> wrote:

> On 3/15/24 11:58, Jerome Brunet wrote:
>> 
>> On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <jan.dakinevich@salutedevices.com> wrote:
>> 
>>> Existing values were insufficient to produce accurate clock for audio
>>> devices. New values are safe and most suitable to produce 48000Hz sample
>>> rate.
>> 
>> The hifi pll is not about 48k only. I see no reason to restrict the PLL
>> to a single setting.
>> > You've provided no justification why the PLL driver can't reach the same
>> setting for 48k. The setting below is just the crude part. the fine
>> tuning is done done with the frac parameter so I doubt this provides a
>> more accurate rate.
>> 
>
> You are right, it is not about 48k only. However, there are two issues.
>
> First, indeed, I could just extend the range of multipliers to 1..255.

Why 1..255 ? This is not what I'm pointing out

According to the datasheet - the range is 32 - 64, as currently
set in the driver.

The change you have provided request a multipler of 128/5 = 25,6
If you put assigned-rate = 614400000 in DT, I see no reason can find the
same solution on its own.

> But I am unsure if hifi_pll is able to handle whole range of
> mulptipliers. The value 128 is taken from Amlogic's branch, and I am
> pretty sure that it works.

>
> Second, unfortunately frac parameter currently doesn't work. When frac
> is used enabling of hifi_pll fails in meson_clk_pll_wait_lock(). I see
> it when try to use 44100Hz and multipliers' range is set to 1..255. So,
> support of other rates than 48k requires extra effort.

Then your change is even more problematic because it certainly does not
disable frac ... which you say is broken.

That parameter should be removed with a proper comment explaining why
you are disabling it. That type a limitation / known issue should be
mentionned in your change.

>
>>>
>>> Signed-off-by: Jan Dakinevich <jan.dakinevich@salutedevices.com>
>>> ---
>>>  drivers/clk/meson/a1-pll.c | 8 ++++----
>>>  1 file changed, 4 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/drivers/clk/meson/a1-pll.c b/drivers/clk/meson/a1-pll.c
>>> index 4325e8a6a3ef..00e06d03445b 100644
>>> --- a/drivers/clk/meson/a1-pll.c
>>> +++ b/drivers/clk/meson/a1-pll.c
>>> @@ -74,9 +74,9 @@ static struct clk_regmap fixed_pll = {
>>>  	},
>>>  };
>>>  
>>> -static const struct pll_mult_range hifi_pll_mult_range = {
>>> -	.min = 32,
>>> -	.max = 64,
>>> +static const struct pll_params_table hifi_pll_params_table[] = {
>>> +	PLL_PARAMS(128, 5),
>>> +	{ },
>>>  };
>>>  
>>>  static const struct reg_sequence hifi_init_regs[] = {
>>> @@ -124,7 +124,7 @@ static struct clk_regmap hifi_pll = {
>>>  			.shift   = 6,
>>>  			.width   = 1,
>>>  		},
>>> -		.range = &hifi_pll_mult_range,
>>> +		.table = hifi_pll_params_table,
>>>  		.init_regs = hifi_init_regs,
>>>  		.init_count = ARRAY_SIZE(hifi_init_regs),
>>>  	},
>> 
>>
Jerome Brunet March 18, 2024, 10:42 a.m. UTC | #15
On Sun 17 Mar 2024 at 18:19, Jan Dakinevich <jan.dakinevich@salutedevices.com> 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 Brunet March 18, 2024, 10:46 a.m. UTC | #16
On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <jan.dakinevich@salutedevices.com> 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 <jan.dakinevich@salutedevices.com>
> ---
>  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);
Mark Brown March 18, 2024, 1:30 p.m. UTC | #17
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.
Mark Brown March 18, 2024, 1:48 p.m. UTC | #18
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.
Jan Dakinevich March 18, 2024, 10:35 p.m. UTC | #19
On 3/18/24 13:17, Jerome Brunet wrote:
> 
> On Sun 17 Mar 2024 at 17:17, Jan Dakinevich <jan.dakinevich@salutedevices.com> wrote:
> 
>> On 3/15/24 11:58, Jerome Brunet wrote:
>>>
>>> On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <jan.dakinevich@salutedevices.com> wrote:
>>>
>>>> Existing values were insufficient to produce accurate clock for audio
>>>> devices. New values are safe and most suitable to produce 48000Hz sample
>>>> rate.
>>>
>>> The hifi pll is not about 48k only. I see no reason to restrict the PLL
>>> to a single setting.
>>>> You've provided no justification why the PLL driver can't reach the same
>>> setting for 48k. The setting below is just the crude part. the fine
>>> tuning is done done with the frac parameter so I doubt this provides a
>>> more accurate rate.
>>>
>>
>> You are right, it is not about 48k only. However, there are two issues.
>>
>> First, indeed, I could just extend the range of multipliers to 1..255.
> 
> Why 1..255 ? This is not what I'm pointing out
> 
> According to the datasheet - the range is 32 - 64, as currently
> set in the driver.
> 

Could you point where in the doc the range 32..64 is documented?
Documentation that I have may be not so complete, but I don't see there
any mention about it.

Anyway, range 32..64 of multipliers is not enough to produce accurate
clock, and a need 128 for 48kHz.

> The change you have provided request a multipler of 128/5 = 25,6
> If you put assigned-rate = 614400000 in DT, I see no reason can find the
> same solution on its own.
> 

The reasoning is following. I don't know why 32..64 range was declared
for this clock, and whether it would be safe to extend it and include
128, which is required for 48kHz. But I know, that multiplier=128 is
safe and works fine (together divider=5).

>> But I am unsure if hifi_pll is able to handle whole range of
>> mulptipliers. The value 128 is taken from Amlogic's branch, and I am
>> pretty sure that it works.
> 
>>
>> Second, unfortunately frac parameter currently doesn't work. When frac
>> is used enabling of hifi_pll fails in meson_clk_pll_wait_lock(). I see
>> it when try to use 44100Hz and multipliers' range is set to 1..255. So,
>> support of other rates than 48k requires extra effort.
> 
> Then your change is even more problematic because it certainly does not
> disable frac ... which you say is broken.
> 
> That parameter should be removed with a proper comment explaining why
> you are disabling it. That type a limitation / known issue should be
> mentionned in your change.
> 

Handling of frac should not be removed, it should be fixed to achieve
another rates. But that is not the goal of this commit.


>>
>>>>
>>>> Signed-off-by: Jan Dakinevich <jan.dakinevich@salutedevices.com>
>>>> ---
>>>>  drivers/clk/meson/a1-pll.c | 8 ++++----
>>>>  1 file changed, 4 insertions(+), 4 deletions(-)
>>>>
>>>> diff --git a/drivers/clk/meson/a1-pll.c b/drivers/clk/meson/a1-pll.c
>>>> index 4325e8a6a3ef..00e06d03445b 100644
>>>> --- a/drivers/clk/meson/a1-pll.c
>>>> +++ b/drivers/clk/meson/a1-pll.c
>>>> @@ -74,9 +74,9 @@ static struct clk_regmap fixed_pll = {
>>>>  	},
>>>>  };
>>>>  
>>>> -static const struct pll_mult_range hifi_pll_mult_range = {
>>>> -	.min = 32,
>>>> -	.max = 64,
>>>> +static const struct pll_params_table hifi_pll_params_table[] = {
>>>> +	PLL_PARAMS(128, 5),
>>>> +	{ },
>>>>  };
>>>>  
>>>>  static const struct reg_sequence hifi_init_regs[] = {
>>>> @@ -124,7 +124,7 @@ static struct clk_regmap hifi_pll = {
>>>>  			.shift   = 6,
>>>>  			.width   = 1,
>>>>  		},
>>>> -		.range = &hifi_pll_mult_range,
>>>> +		.table = hifi_pll_params_table,
>>>>  		.init_regs = hifi_init_regs,
>>>>  		.init_count = ARRAY_SIZE(hifi_init_regs),
>>>>  	},
>>>
>>>
> 
>
Jan Dakinevich March 18, 2024, 10:43 p.m. UTC | #20
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.
Jan Dakinevich March 19, 2024, 12:17 a.m. UTC | #21
On 3/18/24 13:46, Jerome Brunet wrote:
> 
> On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <jan.dakinevich@salutedevices.com> 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 <jan.dakinevich@salutedevices.com>
>> ---
>>  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);
> 
>
Jan Dakinevich March 19, 2024, 1:47 a.m. UTC | #22
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 <jan.dakinevich@salutedevices.com> 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 <jan.dakinevich@salutedevices.com>
>> ---
>>  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 <jan.dakinevich@salutedevices.com>
>> + */
>> +
>> +#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 <jan.dakinevich@salutedevices.com>");
>> +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 <jan.dakinevich@salutedevices.com>
>> + */
>> +
>> +#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 Brunet March 19, 2024, 8:21 a.m. UTC | #23
On Tue 19 Mar 2024 at 01:35, Jan Dakinevich <jan.dakinevich@salutedevices.com> wrote:

> On 3/18/24 13:17, Jerome Brunet wrote:
>> 
>> On Sun 17 Mar 2024 at 17:17, Jan Dakinevich <jan.dakinevich@salutedevices.com> wrote:
>> 
>>> On 3/15/24 11:58, Jerome Brunet wrote:
>>>>
>>>> On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <jan.dakinevich@salutedevices.com> wrote:
>>>>
>>>>> Existing values were insufficient to produce accurate clock for audio
>>>>> devices. New values are safe and most suitable to produce 48000Hz sample
>>>>> rate.
>>>>
>>>> The hifi pll is not about 48k only. I see no reason to restrict the PLL
>>>> to a single setting.
>>>>> You've provided no justification why the PLL driver can't reach the same
>>>> setting for 48k. The setting below is just the crude part. the fine
>>>> tuning is done done with the frac parameter so I doubt this provides a
>>>> more accurate rate.
>>>>
>>>
>>> You are right, it is not about 48k only. However, there are two issues.
>>>
>>> First, indeed, I could just extend the range of multipliers to 1..255.
>> 
>> Why 1..255 ? This is not what I'm pointing out
>> 
>> According to the datasheet - the range is 32 - 64, as currently
>> set in the driver.
>> 
>
> Could you point where in the doc the range 32..64 is documented?
> Documentation that I have may be not so complete, but I don't see there
> any mention about it.
>
> Anyway, range 32..64 of multipliers is not enough to produce accurate
> clock, and a need 128 for 48kHz.

A1 datasheet v0.4 - Section 7.6.3.2

>
>> The change you have provided request a multipler of 128/5 = 25,6
>> If you put assigned-rate = 614400000 in DT, I see no reason can find the
>> same solution on its own.
>> 
>
> The reasoning is following. I don't know why 32..64 range was declared
> for this clock, and whether it would be safe to extend it and include
> 128, which is required for 48kHz. But I know, that multiplier=128 is
> safe and works fine (together divider=5).

You have not answer my remark.
Mainline does not do everything like the AML SDK does. Saying you are
copying it because you know it works (in your opinion) is not good
enough.

I'm telling you that your hack is not necessary and so far, you have not
demonstrated that it is.

Also the multiplier range in m/n, not m alone.

>
>>> But I am unsure if hifi_pll is able to handle whole range of
>>> mulptipliers. The value 128 is taken from Amlogic's branch, and I am
>>> pretty sure that it works.
>> 
>>>
>>> Second, unfortunately frac parameter currently doesn't work. When frac
>>> is used enabling of hifi_pll fails in meson_clk_pll_wait_lock(). I see
>>> it when try to use 44100Hz and multipliers' range is set to 1..255. So,
>>> support of other rates than 48k requires extra effort.
>> 
>> Then your change is even more problematic because it certainly does not
>> disable frac ... which you say is broken.
>> 
>> That parameter should be removed with a proper comment explaining why
>> you are disabling it. That type a limitation / known issue should be
>> mentionned in your change.
>> 
>
> Handling of frac should not be removed, it should be fixed to achieve
> another rates. But that is not the goal of this commit.

You argued that frac was broken and that was partly why you introduced
this work around. I'm telling you this approach is incorrect.

So either :
* Remove frac for now, until it is fixed, because it is broken and add
  comment clearly explaining that quirk.
* Or fix it now.

Your choice.

>
>
>>>
>>>>>
>>>>> Signed-off-by: Jan Dakinevich <jan.dakinevich@salutedevices.com>
>>>>> ---
>>>>>  drivers/clk/meson/a1-pll.c | 8 ++++----
>>>>>  1 file changed, 4 insertions(+), 4 deletions(-)
>>>>>
>>>>> diff --git a/drivers/clk/meson/a1-pll.c b/drivers/clk/meson/a1-pll.c
>>>>> index 4325e8a6a3ef..00e06d03445b 100644
>>>>> --- a/drivers/clk/meson/a1-pll.c
>>>>> +++ b/drivers/clk/meson/a1-pll.c
>>>>> @@ -74,9 +74,9 @@ static struct clk_regmap fixed_pll = {
>>>>>  	},
>>>>>  };
>>>>>  
>>>>> -static const struct pll_mult_range hifi_pll_mult_range = {
>>>>> -	.min = 32,
>>>>> -	.max = 64,
>>>>> +static const struct pll_params_table hifi_pll_params_table[] = {
>>>>> +	PLL_PARAMS(128, 5),
>>>>> +	{ },
>>>>>  };
>>>>>  
>>>>>  static const struct reg_sequence hifi_init_regs[] = {
>>>>> @@ -124,7 +124,7 @@ static struct clk_regmap hifi_pll = {
>>>>>  			.shift   = 6,
>>>>>  			.width   = 1,
>>>>>  		},
>>>>> -		.range = &hifi_pll_mult_range,
>>>>> +		.table = hifi_pll_params_table,
>>>>>  		.init_regs = hifi_init_regs,
>>>>>  		.init_count = ARRAY_SIZE(hifi_init_regs),
>>>>>  	},
>>>>
>>>>
>> 
>>
Jerome Brunet March 19, 2024, 8:30 a.m. UTC | #24
On Tue 19 Mar 2024 at 04:47, Jan Dakinevich <jan.dakinevich@salutedevices.com> 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 <jan.dakinevich@salutedevices.com> 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 <jan.dakinevich@salutedevices.com>
>>> ---
>>>  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 <jan.dakinevich@salutedevices.com>
>>> + */
>>> +
>>> +#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 <jan.dakinevich@salutedevices.com>");
>>> +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 <jan.dakinevich@salutedevices.com>
>>> + */
>>> +
>>> +#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 */
>> 
>>
Dmitry Rokosov March 19, 2024, 1:53 p.m. UTC | #25
On Tue, Mar 19, 2024 at 09:21:27AM +0100, Jerome Brunet wrote:
> 
> On Tue 19 Mar 2024 at 01:35, Jan Dakinevich <jan.dakinevich@salutedevices.com> wrote:
> 
> > On 3/18/24 13:17, Jerome Brunet wrote:
> >> 
> >> On Sun 17 Mar 2024 at 17:17, Jan Dakinevich <jan.dakinevich@salutedevices.com> wrote:
> >> 
> >>> On 3/15/24 11:58, Jerome Brunet wrote:
> >>>>
> >>>> On Fri 15 Mar 2024 at 02:21, Jan Dakinevich <jan.dakinevich@salutedevices.com> wrote:
> >>>>
> >>>>> Existing values were insufficient to produce accurate clock for audio
> >>>>> devices. New values are safe and most suitable to produce 48000Hz sample
> >>>>> rate.
> >>>>
> >>>> The hifi pll is not about 48k only. I see no reason to restrict the PLL
> >>>> to a single setting.
> >>>>> You've provided no justification why the PLL driver can't reach the same
> >>>> setting for 48k. The setting below is just the crude part. the fine
> >>>> tuning is done done with the frac parameter so I doubt this provides a
> >>>> more accurate rate.
> >>>>
> >>>
> >>> You are right, it is not about 48k only. However, there are two issues.
> >>>
> >>> First, indeed, I could just extend the range of multipliers to 1..255.
> >> 
> >> Why 1..255 ? This is not what I'm pointing out
> >> 
> >> According to the datasheet - the range is 32 - 64, as currently
> >> set in the driver.
> >> 
> >
> > Could you point where in the doc the range 32..64 is documented?
> > Documentation that I have may be not so complete, but I don't see there
> > any mention about it.
> >
> > Anyway, range 32..64 of multipliers is not enough to produce accurate
> > clock, and a need 128 for 48kHz.
> 
> A1 datasheet v0.4 - Section 7.6.3.2
> 
> >
> >> The change you have provided request a multipler of 128/5 = 25,6
> >> If you put assigned-rate = 614400000 in DT, I see no reason can find the
> >> same solution on its own.
> >> 
> >
> > The reasoning is following. I don't know why 32..64 range was declared
> > for this clock, and whether it would be safe to extend it and include
> > 128, which is required for 48kHz. But I know, that multiplier=128 is
> > safe and works fine (together divider=5).
> 
> You have not answer my remark.
> Mainline does not do everything like the AML SDK does. Saying you are
> copying it because you know it works (in your opinion) is not good
> enough.
> 
> I'm telling you that your hack is not necessary and so far, you have not
> demonstrated that it is.
> 
> Also the multiplier range in m/n, not m alone.
> 
> >
> >>> But I am unsure if hifi_pll is able to handle whole range of
> >>> mulptipliers. The value 128 is taken from Amlogic's branch, and I am
> >>> pretty sure that it works.
> >> 
> >>>
> >>> Second, unfortunately frac parameter currently doesn't work. When frac
> >>> is used enabling of hifi_pll fails in meson_clk_pll_wait_lock(). I see
> >>> it when try to use 44100Hz and multipliers' range is set to 1..255. So,
> >>> support of other rates than 48k requires extra effort.
> >> 
> >> Then your change is even more problematic because it certainly does not
> >> disable frac ... which you say is broken.
> >> 
> >> That parameter should be removed with a proper comment explaining why
> >> you are disabling it. That type a limitation / known issue should be
> >> mentionned in your change.
> >> 
> >
> > Handling of frac should not be removed, it should be fixed to achieve
> > another rates. But that is not the goal of this commit.
> 
> You argued that frac was broken and that was partly why you introduced
> this work around. I'm telling you this approach is incorrect.
> 
> So either :
> * Remove frac for now, until it is fixed, because it is broken and add
>   comment clearly explaining that quirk.
> * Or fix it now.
> 
> Your choice.
> 
> >
> >
> >>>
> >>>>>
> >>>>> Signed-off-by: Jan Dakinevich <jan.dakinevich@salutedevices.com>
> >>>>> ---
> >>>>>  drivers/clk/meson/a1-pll.c | 8 ++++----
> >>>>>  1 file changed, 4 insertions(+), 4 deletions(-)
> >>>>>
> >>>>> diff --git a/drivers/clk/meson/a1-pll.c b/drivers/clk/meson/a1-pll.c
> >>>>> index 4325e8a6a3ef..00e06d03445b 100644
> >>>>> --- a/drivers/clk/meson/a1-pll.c
> >>>>> +++ b/drivers/clk/meson/a1-pll.c
> >>>>> @@ -74,9 +74,9 @@ static struct clk_regmap fixed_pll = {
> >>>>>  	},
> >>>>>  };
> >>>>>  
> >>>>> -static const struct pll_mult_range hifi_pll_mult_range = {
> >>>>> -	.min = 32,
> >>>>> -	.max = 64,
> >>>>> +static const struct pll_params_table hifi_pll_params_table[] = {
> >>>>> +	PLL_PARAMS(128, 5),
> >>>>> +	{ },
> >>>>>  };
> >>>>>  
> >>>>>  static const struct reg_sequence hifi_init_regs[] = {
> >>>>> @@ -124,7 +124,7 @@ static struct clk_regmap hifi_pll = {
> >>>>>  			.shift   = 6,
> >>>>>  			.width   = 1,
> >>>>>  		},
> >>>>> -		.range = &hifi_pll_mult_range,
> >>>>> +		.table = hifi_pll_params_table,
> >>>>>  		.init_regs = hifi_init_regs,
> >>>>>  		.init_count = ARRAY_SIZE(hifi_init_regs),
> >>>>>  	},
> >>>>
> >>>>
> >> 
> >> 
> 
> 
> -- 
> Jerome

BTW, here Amlogic already mentioned all possible output audio rates for
which hifipll can be used:

https://lore.kernel.org/all/1569411888-98116-1-git-send-email-jian.hu@amlogic.com/T/#md7083b4f851ab97dfce43f8f6a3b266eb49ed329

```
The audio working frequency are 44.1khz, 48khz and 192khz.

614.4M can meet the three frequency.

after the hifi pll, there are two dividers in Audio clock.

614.4M/3200 = 192khz

614.4M/12800 = 48khz

614,4M/13932 = 44.0999khz
```
Jan Dakinevich March 23, 2024, 6:02 p.m. UTC | #26
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 <jan.dakinevich@salutedevices.com> 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 <jan.dakinevich@salutedevices.com> 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 <jan.dakinevich@salutedevices.com>
>>>> ---
>>>>  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 <jan.dakinevich@salutedevices.com>
>>>> + */
>>>> +
>>>> +#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 <jan.dakinevich@salutedevices.com>");
>>>> +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 <jan.dakinevich@salutedevices.com>
>>>> + */
>>>> +
>>>> +#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 */
>>>
>>>
> 
>
Jan Dakinevich March 24, 2024, 5:51 p.m. UTC | #27
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 <jan.dakinevich@salutedevices.com>
> ---
>  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 Brunet March 26, 2024, 3:26 p.m. UTC | #28
On Sat 23 Mar 2024 at 21:02, Jan Dakinevich <jan.dakinevich@salutedevices.com> 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 <jan.dakinevich@salutedevices.com> 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 <jan.dakinevich@salutedevices.com> 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 <jan.dakinevich@salutedevices.com>
>>>>> ---
>>>>>  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 <jan.dakinevich@salutedevices.com>
>>>>> + */
>>>>> +
>>>>> +#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 <jan.dakinevich@salutedevices.com>");
>>>>> +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 <jan.dakinevich@salutedevices.com>
>>>>> + */
>>>>> +
>>>>> +#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 */
>>>>
>>>>
>> 
>>
Jan Dakinevich March 26, 2024, 6:44 p.m. UTC | #29
On 3/26/24 18:26, Jerome Brunet wrote:
> 
> On Sat 23 Mar 2024 at 21:02, Jan Dakinevich <jan.dakinevich@salutedevices.com> 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 <jan.dakinevich@salutedevices.com> 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 <jan.dakinevich@salutedevices.com> 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 <jan.dakinevich@salutedevices.com>
>>>>>> ---
>>>>>>  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 <jan.dakinevich@salutedevices.com>
>>>>>> + */
>>>>>> +
>>>>>> +#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 <jan.dakinevich@salutedevices.com>");
>>>>>> +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 <jan.dakinevich@salutedevices.com>
>>>>>> + */
>>>>>> +
>>>>>> +#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 Brunet March 27, 2024, 12:57 p.m. UTC | #30
On Tue 26 Mar 2024 at 21:44, Jan Dakinevich <jan.dakinevich@salutedevices.com> wrote:

> On 3/26/24 18:26, Jerome Brunet wrote:
>> 
>> On Sat 23 Mar 2024 at 21:02, Jan Dakinevich <jan.dakinevich@salutedevices.com> 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 <jan.dakinevich@salutedevices.com> 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 <jan.dakinevich@salutedevices.com> 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 <jan.dakinevich@salutedevices.com>
>>>>>>> ---
>>>>>>>  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 <jan.dakinevich@salutedevices.com>
>>>>>>> + */
>>>>>>> +
>>>>>>> +#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 <jan.dakinevich@salutedevices.com>");
>>>>>>> +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 <jan.dakinevich@salutedevices.com>
>>>>>>> + */
>>>>>>> +
>>>>>>> +#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 */
>>>>>>
>>>>>>
>>>>
>>>>
>> 
>>