2018-04-25 16:34:36

by Jerome Brunet

[permalink] [raw]
Subject: [PATCH 0/7] clk: meson: axg: add audio clock controller support

The purpose of this patchset is to add support for the clock controller
dedicated to the audio subsystem found on A113 based SoCs

The series depends on the CLK_MUX_ROUND_CLOSEST fixes [0] and the duty
cycle support [1] making their way into CCF.

First patch is a clean-up of the meson clk Kconfig.
Then, there is 3 clock provider drivers for clocks found in the audio
The last 3 are for the clock controller itself.

[0]: https://lkml.kernel.org/r/152389589448.51482.15489333464741262913@swboyd.mtv.corp.google.com
[1]: https://lkml.kernel.org/r/[email protected]

Jerome Brunet (7):
clk: meson: clean-up meson clock configuration
clk: meson: add clk-phase clock driver
clk: meson: add triple phase clock driver
clk: meson: add axg audio sclk divider driver
clk: meson: axg: export audio clock controller id bindings
clk: meson: axg: document bindings for the audio clock controller
clk: meson: axg: add the audio clock controller driver

.../bindings/clock/amlogic,axg-audio-clkc.txt | 56 ++
drivers/clk/meson/Kconfig | 25 +-
drivers/clk/meson/Makefile | 3 +
drivers/clk/meson/axg-audio.c | 845 +++++++++++++++++++++
drivers/clk/meson/axg-audio.h | 127 ++++
drivers/clk/meson/clk-phase.c | 63 ++
drivers/clk/meson/clk-triphase.c | 68 ++
drivers/clk/meson/clkc-audio.h | 28 +
drivers/clk/meson/clkc.h | 8 +
drivers/clk/meson/sclk-div.c | 243 ++++++
include/dt-bindings/clock/axg-audio-clkc.h | 94 +++
11 files changed, 1553 insertions(+), 7 deletions(-)
create mode 100644 Documentation/devicetree/bindings/clock/amlogic,axg-audio-clkc.txt
create mode 100644 drivers/clk/meson/axg-audio.c
create mode 100644 drivers/clk/meson/axg-audio.h
create mode 100644 drivers/clk/meson/clk-phase.c
create mode 100644 drivers/clk/meson/clk-triphase.c
create mode 100644 drivers/clk/meson/clkc-audio.h
create mode 100644 drivers/clk/meson/sclk-div.c
create mode 100644 include/dt-bindings/clock/axg-audio-clkc.h

--
2.14.3



2018-04-25 16:35:03

by Jerome Brunet

[permalink] [raw]
Subject: [PATCH 2/7] clk: meson: add clk-phase clock driver

Add a driver based meson clk-regmap to control clock phase on
amlogic SoCs

Signed-off-by: Jerome Brunet <[email protected]>
---
drivers/clk/meson/Makefile | 1 +
drivers/clk/meson/clk-phase.c | 63 +++++++++++++++++++++++++++++++++++++++++++
drivers/clk/meson/clkc.h | 8 ++++++
3 files changed, 72 insertions(+)
create mode 100644 drivers/clk/meson/clk-phase.c

diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index ffee82e60b7a..352fb848c406 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -3,6 +3,7 @@
#

obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-audio-divider.o
+obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-phase.o
obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o
obj-$(CONFIG_COMMON_CLK_AXG) += axg.o
diff --git a/drivers/clk/meson/clk-phase.c b/drivers/clk/meson/clk-phase.c
new file mode 100644
index 000000000000..96e70497ef1b
--- /dev/null
+++ b/drivers/clk/meson/clk-phase.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2018 BayLibre, SAS.
+ * Author: Jerome Brunet <[email protected]>
+ */
+
+#include <linux/clk-provider.h>
+#include "clkc.h"
+
+#define phase_step(_width) (360 / (1 << (_width)))
+
+static inline struct meson_clk_phase_data *
+meson_clk_phase_data(struct clk_regmap *clk)
+{
+ return (struct meson_clk_phase_data *)clk->data;
+}
+
+int meson_clk_degrees_from_val(unsigned int val, unsigned int width)
+{
+ return phase_step(width) * val;
+}
+EXPORT_SYMBOL_GPL(meson_clk_degrees_from_val);
+
+unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width)
+{
+ unsigned int val = DIV_ROUND_CLOSEST(degrees, phase_step(width));
+
+ /*
+ * This last calculation is here for cases when degrees is rounded
+ * to 360, in which case val == (1 << width).
+ */
+ return val % (1 << width);
+}
+EXPORT_SYMBOL_GPL(meson_clk_degrees_to_val);
+
+static int meson_clk_phase_get_phase(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_phase_data *phase = meson_clk_phase_data(clk);
+ unsigned int val;
+
+ val = meson_parm_read(clk->map, &phase->ph);
+
+ return meson_clk_degrees_from_val(val, phase->ph.width);
+}
+
+static int meson_clk_phase_set_phase(struct clk_hw *hw, int degrees)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_phase_data *phase = meson_clk_phase_data(clk);
+ unsigned int val;
+
+ val = meson_clk_degrees_to_val(degrees, phase->ph.width);
+ meson_parm_write(clk->map, &phase->ph, val);
+
+ return 0;
+}
+
+const struct clk_ops meson_clk_phase_ops = {
+ .get_phase = meson_clk_phase_get_phase,
+ .set_phase = meson_clk_phase_set_phase,
+};
+EXPORT_SYMBOL_GPL(meson_clk_phase_ops);
diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h
index 8fe73c4edca8..9a17d6705e0a 100644
--- a/drivers/clk/meson/clkc.h
+++ b/drivers/clk/meson/clkc.h
@@ -104,6 +104,13 @@ struct meson_clk_audio_div_data {
u8 flags;
};

+struct meson_clk_phase_data {
+ struct parm ph;
+};
+
+int meson_clk_degrees_from_val(unsigned int val, unsigned int width);
+unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width);
+
#define MESON_GATE(_name, _reg, _bit) \
struct clk_regmap _name = { \
.data = &(struct clk_regmap_gate_data){ \
@@ -127,5 +134,6 @@ extern const struct clk_ops meson_clk_mpll_ro_ops;
extern const struct clk_ops meson_clk_mpll_ops;
extern const struct clk_ops meson_clk_audio_divider_ro_ops;
extern const struct clk_ops meson_clk_audio_divider_ops;
+extern const struct clk_ops meson_clk_phase_ops;

#endif /* __CLKC_H */
--
2.14.3


2018-04-25 16:35:09

by Jerome Brunet

[permalink] [raw]
Subject: [PATCH 7/7] clk: meson: axg: add the audio clock controller driver

The axg audio clock controller is the clock generation unit for the
amlogic audio subsystem of A113 based SoCs. It may be clocked by 8
different plls provided by the primary clock controller and also by
10 slave bit clocks and 10 slave sample clocks which may be provided
by external components, such as audio codecs, through the SoC pads.

It contains several muxes, dividers and gates which are fed into the
the different devices of the audio subsystem.

Signed-off-by: Jerome Brunet <[email protected]>
---
drivers/clk/meson/Kconfig | 9 +
drivers/clk/meson/Makefile | 1 +
drivers/clk/meson/axg-audio.c | 845 ++++++++++++++++++++++++++++++++++++++++++
drivers/clk/meson/axg-audio.h | 127 +++++++
4 files changed, 982 insertions(+)
create mode 100644 drivers/clk/meson/axg-audio.c
create mode 100644 drivers/clk/meson/axg-audio.h

diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
index 7f7fd6fb3809..006fa1204e5b 100644
--- a/drivers/clk/meson/Kconfig
+++ b/drivers/clk/meson/Kconfig
@@ -38,3 +38,12 @@ config COMMON_CLK_AXG
help
Support for the clock controller on AmLogic A113D devices, aka axg.
Say Y if you want peripherals and CPU frequency scaling to work.
+
+config COMMON_CLK_AXG_AUDIO
+ tristate "Meson AXG Audio Clock Controller Driver"
+ depends on COMMON_CLK_AXG
+ select COMMON_CLK_AMLOGIC_AUDIO
+ select MFD_SYSCON
+ help
+ Support for the audio clock controller on AmLogic A113D devices,
+ aka axg, Say Y if you want audio subsystem to work.
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index f51b4754c31b..1ab89b627586 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -8,4 +8,5 @@ obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o sclk-div.o
obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o
obj-$(CONFIG_COMMON_CLK_AXG) += axg.o
+obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
obj-$(CONFIG_COMMON_CLK_REGMAP_MESON) += clk-regmap.o
diff --git a/drivers/clk/meson/axg-audio.c b/drivers/clk/meson/axg-audio.c
new file mode 100644
index 000000000000..830c13ccd819
--- /dev/null
+++ b/drivers/clk/meson/axg-audio.c
@@ -0,0 +1,845 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2018 BayLibre, SAS.
+ * Author: Jerome Brunet <[email protected]>
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/init.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#include "clkc-audio.h"
+#include "axg-audio.h"
+
+#define AXG_MST_IN_COUNT 8
+#define AXG_SLV_SCLK_COUNT 10
+#define AXG_SLV_LRCLK_COUNT 10
+
+#define AXG_AUD_GATE(_name, _reg, _bit, _pname, _iflags) \
+struct clk_regmap axg_##_name = { \
+ .data = &(struct clk_regmap_gate_data){ \
+ .offset = (_reg), \
+ .bit_idx = (_bit), \
+ }, \
+ .hw.init = &(struct clk_init_data) { \
+ .name = "axg_"#_name, \
+ .ops = &clk_regmap_gate_ops, \
+ .parent_names = (const char *[]){ _pname }, \
+ .num_parents = 1, \
+ .flags = CLK_DUTY_CYCLE_PARENT | (_iflags), \
+ }, \
+}
+
+#define AXG_AUD_MUX(_name, _reg, _mask, _shift, _dflags, _pnames, _iflags) \
+struct clk_regmap axg_##_name = { \
+ .data = &(struct clk_regmap_mux_data){ \
+ .offset = (_reg), \
+ .mask = (_mask), \
+ .shift = (_shift), \
+ .flags = (_dflags), \
+ }, \
+ .hw.init = &(struct clk_init_data){ \
+ .name = "axg_"#_name, \
+ .ops = &clk_regmap_mux_ops, \
+ .parent_names = (_pnames), \
+ .num_parents = ARRAY_SIZE(_pnames), \
+ .flags = CLK_DUTY_CYCLE_PARENT | (_iflags), \
+ }, \
+}
+
+#define AXG_AUD_DIV(_name, _reg, _shift, _width, _dflags, _pname, _iflags) \
+struct clk_regmap axg_##_name = { \
+ .data = &(struct clk_regmap_div_data){ \
+ .offset = (_reg), \
+ .shift = (_shift), \
+ .width = (_width), \
+ .flags = (_dflags), \
+ }, \
+ .hw.init = &(struct clk_init_data){ \
+ .name = "axg_"#_name, \
+ .ops = &clk_regmap_divider_ops, \
+ .parent_names = (const char *[]) { _pname }, \
+ .num_parents = 1, \
+ .flags = (_iflags), \
+ }, \
+}
+
+#define AXG_PCLK_GATE(_name, _bit) \
+ AXG_AUD_GATE(_name, AUDIO_CLK_GATE_EN, _bit, "axg_audio_pclk", 0)
+
+/* Audio peripheral clocks */
+static AXG_PCLK_GATE(ddr_arb, 0);
+static AXG_PCLK_GATE(pdm, 1);
+static AXG_PCLK_GATE(tdmin_a, 2);
+static AXG_PCLK_GATE(tdmin_b, 3);
+static AXG_PCLK_GATE(tdmin_c, 4);
+static AXG_PCLK_GATE(tdmin_lb, 5);
+static AXG_PCLK_GATE(tdmout_a, 6);
+static AXG_PCLK_GATE(tdmout_b, 7);
+static AXG_PCLK_GATE(tdmout_c, 8);
+static AXG_PCLK_GATE(frddr_a, 9);
+static AXG_PCLK_GATE(frddr_b, 10);
+static AXG_PCLK_GATE(frddr_c, 11);
+static AXG_PCLK_GATE(toddr_a, 12);
+static AXG_PCLK_GATE(toddr_b, 13);
+static AXG_PCLK_GATE(toddr_c, 14);
+static AXG_PCLK_GATE(loopback, 15);
+static AXG_PCLK_GATE(spdifin, 16);
+static AXG_PCLK_GATE(spdifout, 17);
+static AXG_PCLK_GATE(resample, 18);
+static AXG_PCLK_GATE(power_detect, 19);
+
+/* Audio Master Clocks */
+static const char * const mst_mux_parent_names[] = {
+ "axg_mst_in0", "axg_mst_in1", "axg_mst_in2", "axg_mst_in3",
+ "axg_mst_in4", "axg_mst_in5", "axg_mst_in6", "axg_mst_in7",
+};
+
+#define AXG_MST_MCLK_MUX(_name, _reg) \
+ AXG_AUD_MUX(_name##_sel, _reg, 0x7, 24, CLK_MUX_ROUND_CLOSEST, \
+ mst_mux_parent_names, CLK_SET_RATE_PARENT)
+
+static AXG_MST_MCLK_MUX(mst_a_mclk, AUDIO_MCLK_A_CTRL);
+static AXG_MST_MCLK_MUX(mst_b_mclk, AUDIO_MCLK_B_CTRL);
+static AXG_MST_MCLK_MUX(mst_c_mclk, AUDIO_MCLK_C_CTRL);
+static AXG_MST_MCLK_MUX(mst_d_mclk, AUDIO_MCLK_D_CTRL);
+static AXG_MST_MCLK_MUX(mst_e_mclk, AUDIO_MCLK_E_CTRL);
+static AXG_MST_MCLK_MUX(mst_f_mclk, AUDIO_MCLK_F_CTRL);
+static AXG_MST_MCLK_MUX(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL);
+static AXG_MST_MCLK_MUX(spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL);
+static AXG_MST_MCLK_MUX(pdm_dclk, AUDIO_CLK_PDMIN_CTRL0);
+static AXG_MST_MCLK_MUX(pdm_sysclk, AUDIO_CLK_PDMIN_CTRL1);
+
+#define AXG_MST_MCLK_DIV(_name, _reg) \
+ AXG_AUD_DIV(_name##_div, _reg, 0, 16, CLK_DIVIDER_ROUND_CLOSEST, \
+ "axg_"#_name"_sel", CLK_SET_RATE_PARENT) \
+
+static AXG_MST_MCLK_DIV(mst_a_mclk, AUDIO_MCLK_A_CTRL);
+static AXG_MST_MCLK_DIV(mst_b_mclk, AUDIO_MCLK_B_CTRL);
+static AXG_MST_MCLK_DIV(mst_c_mclk, AUDIO_MCLK_C_CTRL);
+static AXG_MST_MCLK_DIV(mst_d_mclk, AUDIO_MCLK_D_CTRL);
+static AXG_MST_MCLK_DIV(mst_e_mclk, AUDIO_MCLK_E_CTRL);
+static AXG_MST_MCLK_DIV(mst_f_mclk, AUDIO_MCLK_F_CTRL);
+static AXG_MST_MCLK_DIV(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL);
+static AXG_MST_MCLK_DIV(spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL);
+static AXG_MST_MCLK_DIV(pdm_dclk, AUDIO_CLK_PDMIN_CTRL0);
+static AXG_MST_MCLK_DIV(pdm_sysclk, AUDIO_CLK_PDMIN_CTRL1);
+
+#define AXG_MST_MCLK_GATE(_name, _reg) \
+ AXG_AUD_GATE(_name, _reg, 31, "axg_"#_name"_div", \
+ CLK_SET_RATE_PARENT)
+
+static AXG_MST_MCLK_GATE(mst_a_mclk, AUDIO_MCLK_A_CTRL);
+static AXG_MST_MCLK_GATE(mst_b_mclk, AUDIO_MCLK_B_CTRL);
+static AXG_MST_MCLK_GATE(mst_c_mclk, AUDIO_MCLK_C_CTRL);
+static AXG_MST_MCLK_GATE(mst_d_mclk, AUDIO_MCLK_D_CTRL);
+static AXG_MST_MCLK_GATE(mst_e_mclk, AUDIO_MCLK_E_CTRL);
+static AXG_MST_MCLK_GATE(mst_f_mclk, AUDIO_MCLK_F_CTRL);
+static AXG_MST_MCLK_GATE(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL);
+static AXG_MST_MCLK_GATE(spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL);
+static AXG_MST_MCLK_GATE(pdm_dclk, AUDIO_CLK_PDMIN_CTRL0);
+static AXG_MST_MCLK_GATE(pdm_sysclk, AUDIO_CLK_PDMIN_CTRL1);
+
+/* Sample Clocks */
+#define AXG_MST_SCLK_PRE_EN(_name, _reg) \
+ AXG_AUD_GATE(mst_##_name##_sclk_pre_en, _reg, 31, \
+ "axg_mst_"#_name"_mclk", 0)
+
+static AXG_MST_SCLK_PRE_EN(a, AUDIO_MST_A_SCLK_CTRL0);
+static AXG_MST_SCLK_PRE_EN(b, AUDIO_MST_B_SCLK_CTRL0);
+static AXG_MST_SCLK_PRE_EN(c, AUDIO_MST_C_SCLK_CTRL0);
+static AXG_MST_SCLK_PRE_EN(d, AUDIO_MST_D_SCLK_CTRL0);
+static AXG_MST_SCLK_PRE_EN(e, AUDIO_MST_E_SCLK_CTRL0);
+static AXG_MST_SCLK_PRE_EN(f, AUDIO_MST_F_SCLK_CTRL0);
+
+#define AXG_AUD_SCLK_DIV(_name, _reg, _div_shift, _div_width, \
+ _hi_shift, _hi_width, _pname, _iflags) \
+struct clk_regmap axg_##_name = { \
+ .data = &(struct meson_sclk_div_data) { \
+ .div = { \
+ .reg_off = (_reg), \
+ .shift = (_div_shift), \
+ .width = (_div_width), \
+ }, \
+ .hi = { \
+ .reg_off = (_reg), \
+ .shift = (_hi_shift), \
+ .width = (_hi_width), \
+ }, \
+ }, \
+ .hw.init = &(struct clk_init_data) { \
+ .name = "axg_"#_name, \
+ .ops = &meson_sclk_div_ops, \
+ .parent_names = (const char *[]) { _pname }, \
+ .num_parents = 1, \
+ .flags = (_iflags), \
+ }, \
+}
+
+#define AXG_MST_SCLK_DIV(_name, _reg) \
+ AXG_AUD_SCLK_DIV(mst_##_name##_sclk_div, _reg, 20, 10, 0, 0, \
+ "axg_mst_"#_name"_sclk_pre_en", \
+ CLK_SET_RATE_PARENT)
+
+static AXG_MST_SCLK_DIV(a, AUDIO_MST_A_SCLK_CTRL0);
+static AXG_MST_SCLK_DIV(b, AUDIO_MST_B_SCLK_CTRL0);
+static AXG_MST_SCLK_DIV(c, AUDIO_MST_C_SCLK_CTRL0);
+static AXG_MST_SCLK_DIV(d, AUDIO_MST_D_SCLK_CTRL0);
+static AXG_MST_SCLK_DIV(e, AUDIO_MST_E_SCLK_CTRL0);
+static AXG_MST_SCLK_DIV(f, AUDIO_MST_F_SCLK_CTRL0);
+
+#define AXG_MST_SCLK_POST_EN(_name, _reg) \
+ AXG_AUD_GATE(mst_##_name##_sclk_post_en, _reg, 30, \
+ "axg_mst_"#_name"_sclk_div", CLK_SET_RATE_PARENT)
+
+static AXG_MST_SCLK_POST_EN(a, AUDIO_MST_A_SCLK_CTRL0);
+static AXG_MST_SCLK_POST_EN(b, AUDIO_MST_B_SCLK_CTRL0);
+static AXG_MST_SCLK_POST_EN(c, AUDIO_MST_C_SCLK_CTRL0);
+static AXG_MST_SCLK_POST_EN(d, AUDIO_MST_D_SCLK_CTRL0);
+static AXG_MST_SCLK_POST_EN(e, AUDIO_MST_E_SCLK_CTRL0);
+static AXG_MST_SCLK_POST_EN(f, AUDIO_MST_F_SCLK_CTRL0);
+
+#define AXG_AUD_TRIPHASE(_name, _reg, _width, _shift0, _shift1, _shift2, \
+ _pname, _iflags) \
+struct clk_regmap axg_##_name = { \
+ .data = &(struct meson_clk_triphase_data) { \
+ .ph0 = { \
+ .reg_off = (_reg), \
+ .shift = (_shift0), \
+ .width = (_width), \
+ }, \
+ .ph1 = { \
+ .reg_off = (_reg), \
+ .shift = (_shift1), \
+ .width = (_width), \
+ }, \
+ .ph2 = { \
+ .reg_off = (_reg), \
+ .shift = (_shift2), \
+ .width = (_width), \
+ }, \
+ }, \
+ .hw.init = &(struct clk_init_data) { \
+ .name = "axg_"#_name, \
+ .ops = &meson_clk_triphase_ops, \
+ .parent_names = (const char *[]) { _pname }, \
+ .num_parents = 1, \
+ .flags = CLK_DUTY_CYCLE_PARENT | (_iflags), \
+ }, \
+}
+
+#define AXG_MST_SCLK(_name, _reg) \
+ AXG_AUD_TRIPHASE(mst_##_name##_sclk, _reg, 1, 0, 2, 4, \
+ "axg_mst_"#_name"_sclk_post_en", CLK_SET_RATE_PARENT)
+
+static AXG_MST_SCLK(a, AUDIO_MST_A_SCLK_CTRL1);
+static AXG_MST_SCLK(b, AUDIO_MST_B_SCLK_CTRL1);
+static AXG_MST_SCLK(c, AUDIO_MST_C_SCLK_CTRL1);
+static AXG_MST_SCLK(d, AUDIO_MST_D_SCLK_CTRL1);
+static AXG_MST_SCLK(e, AUDIO_MST_E_SCLK_CTRL1);
+static AXG_MST_SCLK(f, AUDIO_MST_F_SCLK_CTRL1);
+
+#define AXG_MST_LRCLK_DIV(_name, _reg) \
+ AXG_AUD_SCLK_DIV(mst_##_name##_lrclk_div, _reg, 0, 10, 10, 10, \
+ "axg_mst_"#_name"_sclk_post_en", 0) \
+
+static AXG_MST_LRCLK_DIV(a, AUDIO_MST_A_SCLK_CTRL0);
+static AXG_MST_LRCLK_DIV(b, AUDIO_MST_B_SCLK_CTRL0);
+static AXG_MST_LRCLK_DIV(c, AUDIO_MST_C_SCLK_CTRL0);
+static AXG_MST_LRCLK_DIV(d, AUDIO_MST_D_SCLK_CTRL0);
+static AXG_MST_LRCLK_DIV(e, AUDIO_MST_E_SCLK_CTRL0);
+static AXG_MST_LRCLK_DIV(f, AUDIO_MST_F_SCLK_CTRL0);
+
+#define AXG_MST_LRCLK(_name, _reg) \
+ AXG_AUD_TRIPHASE(mst_##_name##_lrclk, _reg, 1, 1, 3, 5, \
+ "axg_mst_"#_name"_lrclk_div", CLK_SET_RATE_PARENT)
+
+static AXG_MST_LRCLK(a, AUDIO_MST_A_SCLK_CTRL1);
+static AXG_MST_LRCLK(b, AUDIO_MST_B_SCLK_CTRL1);
+static AXG_MST_LRCLK(c, AUDIO_MST_C_SCLK_CTRL1);
+static AXG_MST_LRCLK(d, AUDIO_MST_D_SCLK_CTRL1);
+static AXG_MST_LRCLK(e, AUDIO_MST_E_SCLK_CTRL1);
+static AXG_MST_LRCLK(f, AUDIO_MST_F_SCLK_CTRL1);
+
+static const char * const tdm_sclk_parent_names[] = {
+ "axg_mst_a_sclk", "axg_mst_b_sclk", "axg_mst_c_sclk",
+ "axg_mst_d_sclk", "axg_mst_e_sclk", "axg_mst_f_sclk",
+ "axg_slv_sclk0", "axg_slv_sclk1", "axg_slv_sclk2",
+ "axg_slv_sclk3", "axg_slv_sclk4", "axg_slv_sclk5",
+ "axg_slv_sclk6", "axg_slv_sclk7", "axg_slv_sclk8",
+ "axg_slv_sclk9"
+};
+
+#define AXG_TDM_SCLK_MUX(_name, _reg) \
+ AXG_AUD_MUX(tdm##_name##_sclk_sel, _reg, 0xf, 24, \
+ CLK_MUX_ROUND_CLOSEST, \
+ tdm_sclk_parent_names, 0)
+
+static AXG_TDM_SCLK_MUX(in_a, AUDIO_CLK_TDMIN_A_CTRL);
+static AXG_TDM_SCLK_MUX(in_b, AUDIO_CLK_TDMIN_B_CTRL);
+static AXG_TDM_SCLK_MUX(in_c, AUDIO_CLK_TDMIN_C_CTRL);
+static AXG_TDM_SCLK_MUX(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
+static AXG_TDM_SCLK_MUX(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
+static AXG_TDM_SCLK_MUX(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
+static AXG_TDM_SCLK_MUX(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
+
+#define AXG_TDM_SCLK_PRE_EN(_name, _reg) \
+ AXG_AUD_GATE(tdm##_name##_sclk_pre_en, _reg, 31, \
+ "axg_tdm"#_name"_sclk_sel", CLK_SET_RATE_PARENT)
+
+static AXG_TDM_SCLK_PRE_EN(in_a, AUDIO_CLK_TDMIN_A_CTRL);
+static AXG_TDM_SCLK_PRE_EN(in_b, AUDIO_CLK_TDMIN_B_CTRL);
+static AXG_TDM_SCLK_PRE_EN(in_c, AUDIO_CLK_TDMIN_C_CTRL);
+static AXG_TDM_SCLK_PRE_EN(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
+static AXG_TDM_SCLK_PRE_EN(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
+static AXG_TDM_SCLK_PRE_EN(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
+static AXG_TDM_SCLK_PRE_EN(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
+
+#define AXG_TDM_SCLK_POST_EN(_name, _reg) \
+ AXG_AUD_GATE(tdm##_name##_sclk_post_en, _reg, 30, \
+ "axg_tdm"#_name"_sclk_pre_en", CLK_SET_RATE_PARENT)
+
+static AXG_TDM_SCLK_POST_EN(in_a, AUDIO_CLK_TDMIN_A_CTRL);
+static AXG_TDM_SCLK_POST_EN(in_b, AUDIO_CLK_TDMIN_B_CTRL);
+static AXG_TDM_SCLK_POST_EN(in_c, AUDIO_CLK_TDMIN_C_CTRL);
+static AXG_TDM_SCLK_POST_EN(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
+static AXG_TDM_SCLK_POST_EN(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
+static AXG_TDM_SCLK_POST_EN(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
+static AXG_TDM_SCLK_POST_EN(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
+
+#define AXG_TDM_SCLK(_name, _reg) \
+ struct clk_regmap axg_tdm##_name##_sclk = { \
+ .data = &(struct meson_clk_phase_data) { \
+ .ph = { \
+ .reg_off = (_reg), \
+ .shift = 29, \
+ .width = 1, \
+ }, \
+ }, \
+ .hw.init = &(struct clk_init_data) { \
+ .name = "axg_tdm"#_name"_sclk", \
+ .ops = &meson_clk_phase_ops, \
+ .parent_names = (const char *[]) \
+ { "axg_tdm"#_name"_sclk_post_en" }, \
+ .num_parents = 1, \
+ .flags = CLK_DUTY_CYCLE_PARENT | CLK_SET_RATE_PARENT, \
+ }, \
+}
+
+static AXG_TDM_SCLK(in_a, AUDIO_CLK_TDMIN_A_CTRL);
+static AXG_TDM_SCLK(in_b, AUDIO_CLK_TDMIN_B_CTRL);
+static AXG_TDM_SCLK(in_c, AUDIO_CLK_TDMIN_C_CTRL);
+static AXG_TDM_SCLK(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
+static AXG_TDM_SCLK(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
+static AXG_TDM_SCLK(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
+static AXG_TDM_SCLK(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
+
+static const char * const tdm_lrclk_parent_names[] = {
+ "axg_mst_a_lrclk", "axg_mst_b_lrclk", "axg_mst_c_lrclk",
+ "axg_mst_d_lrclk", "axg_mst_e_lrclk", "axg_mst_f_lrclk",
+ "axg_slv_lrclk0", "axg_slv_lrclk1", "axg_slv_lrclk2",
+ "axg_slv_lrclk3", "axg_slv_lrclk4", "axg_slv_lrclk5",
+ "axg_slv_lrclk6", "axg_slv_lrclk7", "axg_slv_lrclk8",
+ "axg_slv_lrclk9"
+};
+
+#define AXG_TDM_LRLCK(_name, _reg) \
+ AXG_AUD_MUX(tdm##_name##_lrclk, _reg, 0xf, 20, \
+ CLK_MUX_ROUND_CLOSEST, \
+ tdm_lrclk_parent_names, 0)
+
+static AXG_TDM_LRLCK(in_a, AUDIO_CLK_TDMIN_A_CTRL);
+static AXG_TDM_LRLCK(in_b, AUDIO_CLK_TDMIN_B_CTRL);
+static AXG_TDM_LRLCK(in_c, AUDIO_CLK_TDMIN_C_CTRL);
+static AXG_TDM_LRLCK(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
+static AXG_TDM_LRLCK(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
+static AXG_TDM_LRLCK(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
+static AXG_TDM_LRLCK(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
+
+/*
+ * Array of all clocks provided by this provider
+ * The input clocks of the controller will be populated at runtime
+ */
+static struct clk_hw_onecell_data axg_audio_hw_onecell_data = {
+ .hws = {
+ [AUD_CLKID_DDR_ARB] = &axg_ddr_arb.hw,
+ [AUD_CLKID_PDM] = &axg_pdm.hw,
+ [AUD_CLKID_TDMIN_A] = &axg_tdmin_a.hw,
+ [AUD_CLKID_TDMIN_B] = &axg_tdmin_b.hw,
+ [AUD_CLKID_TDMIN_C] = &axg_tdmin_c.hw,
+ [AUD_CLKID_TDMIN_LB] = &axg_tdmin_lb.hw,
+ [AUD_CLKID_TDMOUT_A] = &axg_tdmout_a.hw,
+ [AUD_CLKID_TDMOUT_B] = &axg_tdmout_b.hw,
+ [AUD_CLKID_TDMOUT_C] = &axg_tdmout_c.hw,
+ [AUD_CLKID_FRDDR_A] = &axg_frddr_a.hw,
+ [AUD_CLKID_FRDDR_B] = &axg_frddr_b.hw,
+ [AUD_CLKID_FRDDR_C] = &axg_frddr_c.hw,
+ [AUD_CLKID_TODDR_A] = &axg_toddr_a.hw,
+ [AUD_CLKID_TODDR_B] = &axg_toddr_b.hw,
+ [AUD_CLKID_TODDR_C] = &axg_toddr_c.hw,
+ [AUD_CLKID_LOOPBACK] = &axg_loopback.hw,
+ [AUD_CLKID_SPDIFIN] = &axg_spdifin.hw,
+ [AUD_CLKID_SPDIFOUT] = &axg_spdifout.hw,
+ [AUD_CLKID_RESAMPLE] = &axg_resample.hw,
+ [AUD_CLKID_POWER_DETECT] = &axg_power_detect.hw,
+ [AUD_CLKID_MST_A_MCLK_SEL] = &axg_mst_a_mclk_sel.hw,
+ [AUD_CLKID_MST_B_MCLK_SEL] = &axg_mst_b_mclk_sel.hw,
+ [AUD_CLKID_MST_C_MCLK_SEL] = &axg_mst_c_mclk_sel.hw,
+ [AUD_CLKID_MST_D_MCLK_SEL] = &axg_mst_d_mclk_sel.hw,
+ [AUD_CLKID_MST_E_MCLK_SEL] = &axg_mst_e_mclk_sel.hw,
+ [AUD_CLKID_MST_F_MCLK_SEL] = &axg_mst_f_mclk_sel.hw,
+ [AUD_CLKID_MST_A_MCLK_DIV] = &axg_mst_a_mclk_div.hw,
+ [AUD_CLKID_MST_B_MCLK_DIV] = &axg_mst_b_mclk_div.hw,
+ [AUD_CLKID_MST_C_MCLK_DIV] = &axg_mst_c_mclk_div.hw,
+ [AUD_CLKID_MST_D_MCLK_DIV] = &axg_mst_d_mclk_div.hw,
+ [AUD_CLKID_MST_E_MCLK_DIV] = &axg_mst_e_mclk_div.hw,
+ [AUD_CLKID_MST_F_MCLK_DIV] = &axg_mst_f_mclk_div.hw,
+ [AUD_CLKID_MST_A_MCLK] = &axg_mst_a_mclk.hw,
+ [AUD_CLKID_MST_B_MCLK] = &axg_mst_b_mclk.hw,
+ [AUD_CLKID_MST_C_MCLK] = &axg_mst_c_mclk.hw,
+ [AUD_CLKID_MST_D_MCLK] = &axg_mst_d_mclk.hw,
+ [AUD_CLKID_MST_E_MCLK] = &axg_mst_e_mclk.hw,
+ [AUD_CLKID_MST_F_MCLK] = &axg_mst_f_mclk.hw,
+ [AUD_CLKID_SPDIFOUT_CLK_SEL] = &axg_spdifout_clk_sel.hw,
+ [AUD_CLKID_SPDIFOUT_CLK_DIV] = &axg_spdifout_clk_div.hw,
+ [AUD_CLKID_SPDIFOUT_CLK] = &axg_spdifout_clk.hw,
+ [AUD_CLKID_SPDIFIN_CLK_SEL] = &axg_spdifin_clk_sel.hw,
+ [AUD_CLKID_SPDIFIN_CLK_DIV] = &axg_spdifin_clk_div.hw,
+ [AUD_CLKID_SPDIFIN_CLK] = &axg_spdifin_clk.hw,
+ [AUD_CLKID_PDM_DCLK_SEL] = &axg_pdm_dclk_sel.hw,
+ [AUD_CLKID_PDM_DCLK_DIV] = &axg_pdm_dclk_div.hw,
+ [AUD_CLKID_PDM_DCLK] = &axg_pdm_dclk.hw,
+ [AUD_CLKID_PDM_SYSCLK_SEL] = &axg_pdm_sysclk_sel.hw,
+ [AUD_CLKID_PDM_SYSCLK_DIV] = &axg_pdm_sysclk_div.hw,
+ [AUD_CLKID_PDM_SYSCLK] = &axg_pdm_sysclk.hw,
+ [AUD_CLKID_MST_A_SCLK_PRE_EN] = &axg_mst_a_sclk_pre_en.hw,
+ [AUD_CLKID_MST_B_SCLK_PRE_EN] = &axg_mst_b_sclk_pre_en.hw,
+ [AUD_CLKID_MST_C_SCLK_PRE_EN] = &axg_mst_c_sclk_pre_en.hw,
+ [AUD_CLKID_MST_D_SCLK_PRE_EN] = &axg_mst_d_sclk_pre_en.hw,
+ [AUD_CLKID_MST_E_SCLK_PRE_EN] = &axg_mst_e_sclk_pre_en.hw,
+ [AUD_CLKID_MST_F_SCLK_PRE_EN] = &axg_mst_f_sclk_pre_en.hw,
+ [AUD_CLKID_MST_A_SCLK_DIV] = &axg_mst_a_sclk_div.hw,
+ [AUD_CLKID_MST_B_SCLK_DIV] = &axg_mst_b_sclk_div.hw,
+ [AUD_CLKID_MST_C_SCLK_DIV] = &axg_mst_c_sclk_div.hw,
+ [AUD_CLKID_MST_D_SCLK_DIV] = &axg_mst_d_sclk_div.hw,
+ [AUD_CLKID_MST_E_SCLK_DIV] = &axg_mst_e_sclk_div.hw,
+ [AUD_CLKID_MST_F_SCLK_DIV] = &axg_mst_f_sclk_div.hw,
+ [AUD_CLKID_MST_A_SCLK_POST_EN] = &axg_mst_a_sclk_post_en.hw,
+ [AUD_CLKID_MST_B_SCLK_POST_EN] = &axg_mst_b_sclk_post_en.hw,
+ [AUD_CLKID_MST_C_SCLK_POST_EN] = &axg_mst_c_sclk_post_en.hw,
+ [AUD_CLKID_MST_D_SCLK_POST_EN] = &axg_mst_d_sclk_post_en.hw,
+ [AUD_CLKID_MST_E_SCLK_POST_EN] = &axg_mst_e_sclk_post_en.hw,
+ [AUD_CLKID_MST_F_SCLK_POST_EN] = &axg_mst_f_sclk_post_en.hw,
+ [AUD_CLKID_MST_A_SCLK] = &axg_mst_a_sclk.hw,
+ [AUD_CLKID_MST_B_SCLK] = &axg_mst_b_sclk.hw,
+ [AUD_CLKID_MST_C_SCLK] = &axg_mst_c_sclk.hw,
+ [AUD_CLKID_MST_D_SCLK] = &axg_mst_d_sclk.hw,
+ [AUD_CLKID_MST_E_SCLK] = &axg_mst_e_sclk.hw,
+ [AUD_CLKID_MST_F_SCLK] = &axg_mst_f_sclk.hw,
+ [AUD_CLKID_MST_A_LRCLK_DIV] = &axg_mst_a_lrclk_div.hw,
+ [AUD_CLKID_MST_B_LRCLK_DIV] = &axg_mst_b_lrclk_div.hw,
+ [AUD_CLKID_MST_C_LRCLK_DIV] = &axg_mst_c_lrclk_div.hw,
+ [AUD_CLKID_MST_D_LRCLK_DIV] = &axg_mst_d_lrclk_div.hw,
+ [AUD_CLKID_MST_E_LRCLK_DIV] = &axg_mst_e_lrclk_div.hw,
+ [AUD_CLKID_MST_F_LRCLK_DIV] = &axg_mst_f_lrclk_div.hw,
+ [AUD_CLKID_MST_A_LRCLK] = &axg_mst_a_lrclk.hw,
+ [AUD_CLKID_MST_B_LRCLK] = &axg_mst_b_lrclk.hw,
+ [AUD_CLKID_MST_C_LRCLK] = &axg_mst_c_lrclk.hw,
+ [AUD_CLKID_MST_D_LRCLK] = &axg_mst_d_lrclk.hw,
+ [AUD_CLKID_MST_E_LRCLK] = &axg_mst_e_lrclk.hw,
+ [AUD_CLKID_MST_F_LRCLK] = &axg_mst_f_lrclk.hw,
+ [AUD_CLKID_TDMIN_A_SCLK_SEL] = &axg_tdmin_a_sclk_sel.hw,
+ [AUD_CLKID_TDMIN_B_SCLK_SEL] = &axg_tdmin_b_sclk_sel.hw,
+ [AUD_CLKID_TDMIN_C_SCLK_SEL] = &axg_tdmin_c_sclk_sel.hw,
+ [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &axg_tdmin_lb_sclk_sel.hw,
+ [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &axg_tdmout_a_sclk_sel.hw,
+ [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &axg_tdmout_b_sclk_sel.hw,
+ [AUD_CLKID_TDMOUT_C_SCLK_SEL] = &axg_tdmout_c_sclk_sel.hw,
+ [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &axg_tdmin_a_sclk_pre_en.hw,
+ [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &axg_tdmin_b_sclk_pre_en.hw,
+ [AUD_CLKID_TDMIN_C_SCLK_PRE_EN] = &axg_tdmin_c_sclk_pre_en.hw,
+ [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &axg_tdmin_lb_sclk_pre_en.hw,
+ [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &axg_tdmout_a_sclk_pre_en.hw,
+ [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &axg_tdmout_b_sclk_pre_en.hw,
+ [AUD_CLKID_TDMOUT_C_SCLK_PRE_EN] = &axg_tdmout_c_sclk_pre_en.hw,
+ [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &axg_tdmin_a_sclk_post_en.hw,
+ [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &axg_tdmin_b_sclk_post_en.hw,
+ [AUD_CLKID_TDMIN_C_SCLK_POST_EN] = &axg_tdmin_c_sclk_post_en.hw,
+ [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &axg_tdmin_lb_sclk_post_en.hw,
+ [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &axg_tdmout_a_sclk_post_en.hw,
+ [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &axg_tdmout_b_sclk_post_en.hw,
+ [AUD_CLKID_TDMOUT_C_SCLK_POST_EN] = &axg_tdmout_c_sclk_post_en.hw,
+ [AUD_CLKID_TDMIN_A_SCLK] = &axg_tdmin_a_sclk.hw,
+ [AUD_CLKID_TDMIN_B_SCLK] = &axg_tdmin_b_sclk.hw,
+ [AUD_CLKID_TDMIN_C_SCLK] = &axg_tdmin_c_sclk.hw,
+ [AUD_CLKID_TDMIN_LB_SCLK] = &axg_tdmin_lb_sclk.hw,
+ [AUD_CLKID_TDMOUT_A_SCLK] = &axg_tdmout_a_sclk.hw,
+ [AUD_CLKID_TDMOUT_B_SCLK] = &axg_tdmout_b_sclk.hw,
+ [AUD_CLKID_TDMOUT_C_SCLK] = &axg_tdmout_c_sclk.hw,
+ [AUD_CLKID_TDMIN_A_LRCLK] = &axg_tdmin_a_lrclk.hw,
+ [AUD_CLKID_TDMIN_B_LRCLK] = &axg_tdmin_b_lrclk.hw,
+ [AUD_CLKID_TDMIN_C_LRCLK] = &axg_tdmin_c_lrclk.hw,
+ [AUD_CLKID_TDMIN_LB_LRCLK] = &axg_tdmin_lb_lrclk.hw,
+ [AUD_CLKID_TDMOUT_A_LRCLK] = &axg_tdmout_a_lrclk.hw,
+ [AUD_CLKID_TDMOUT_B_LRCLK] = &axg_tdmout_b_lrclk.hw,
+ [AUD_CLKID_TDMOUT_C_LRCLK] = &axg_tdmout_c_lrclk.hw,
+ [NR_CLKS] = NULL,
+ },
+ .num = NR_CLKS,
+};
+
+/* Convenience table to populate regmap in .probe() */
+static struct clk_regmap *const axg_audio_clk_regmaps[] = {
+ &axg_ddr_arb,
+ &axg_pdm,
+ &axg_tdmin_a,
+ &axg_tdmin_b,
+ &axg_tdmin_c,
+ &axg_tdmin_lb,
+ &axg_tdmout_a,
+ &axg_tdmout_b,
+ &axg_tdmout_c,
+ &axg_frddr_a,
+ &axg_frddr_b,
+ &axg_frddr_c,
+ &axg_toddr_a,
+ &axg_toddr_b,
+ &axg_toddr_c,
+ &axg_loopback,
+ &axg_spdifin,
+ &axg_spdifout,
+ &axg_resample,
+ &axg_power_detect,
+ &axg_mst_a_mclk_sel,
+ &axg_mst_b_mclk_sel,
+ &axg_mst_c_mclk_sel,
+ &axg_mst_d_mclk_sel,
+ &axg_mst_e_mclk_sel,
+ &axg_mst_f_mclk_sel,
+ &axg_mst_a_mclk_div,
+ &axg_mst_b_mclk_div,
+ &axg_mst_c_mclk_div,
+ &axg_mst_d_mclk_div,
+ &axg_mst_e_mclk_div,
+ &axg_mst_f_mclk_div,
+ &axg_mst_a_mclk,
+ &axg_mst_b_mclk,
+ &axg_mst_c_mclk,
+ &axg_mst_d_mclk,
+ &axg_mst_e_mclk,
+ &axg_mst_f_mclk,
+ &axg_spdifout_clk_sel,
+ &axg_spdifout_clk_div,
+ &axg_spdifout_clk,
+ &axg_spdifin_clk_sel,
+ &axg_spdifin_clk_div,
+ &axg_spdifin_clk,
+ &axg_pdm_dclk_sel,
+ &axg_pdm_dclk_div,
+ &axg_pdm_dclk,
+ &axg_pdm_sysclk_sel,
+ &axg_pdm_sysclk_div,
+ &axg_pdm_sysclk,
+ &axg_mst_a_sclk_pre_en,
+ &axg_mst_b_sclk_pre_en,
+ &axg_mst_c_sclk_pre_en,
+ &axg_mst_d_sclk_pre_en,
+ &axg_mst_e_sclk_pre_en,
+ &axg_mst_f_sclk_pre_en,
+ &axg_mst_a_sclk_div,
+ &axg_mst_b_sclk_div,
+ &axg_mst_c_sclk_div,
+ &axg_mst_d_sclk_div,
+ &axg_mst_e_sclk_div,
+ &axg_mst_f_sclk_div,
+ &axg_mst_a_sclk_post_en,
+ &axg_mst_b_sclk_post_en,
+ &axg_mst_c_sclk_post_en,
+ &axg_mst_d_sclk_post_en,
+ &axg_mst_e_sclk_post_en,
+ &axg_mst_f_sclk_post_en,
+ &axg_mst_a_sclk,
+ &axg_mst_b_sclk,
+ &axg_mst_c_sclk,
+ &axg_mst_d_sclk,
+ &axg_mst_e_sclk,
+ &axg_mst_f_sclk,
+ &axg_mst_a_lrclk_div,
+ &axg_mst_b_lrclk_div,
+ &axg_mst_c_lrclk_div,
+ &axg_mst_d_lrclk_div,
+ &axg_mst_e_lrclk_div,
+ &axg_mst_f_lrclk_div,
+ &axg_mst_a_lrclk,
+ &axg_mst_b_lrclk,
+ &axg_mst_c_lrclk,
+ &axg_mst_d_lrclk,
+ &axg_mst_e_lrclk,
+ &axg_mst_f_lrclk,
+ &axg_tdmin_a_sclk_sel,
+ &axg_tdmin_b_sclk_sel,
+ &axg_tdmin_c_sclk_sel,
+ &axg_tdmin_lb_sclk_sel,
+ &axg_tdmout_a_sclk_sel,
+ &axg_tdmout_b_sclk_sel,
+ &axg_tdmout_c_sclk_sel,
+ &axg_tdmin_a_sclk_pre_en,
+ &axg_tdmin_b_sclk_pre_en,
+ &axg_tdmin_c_sclk_pre_en,
+ &axg_tdmin_lb_sclk_pre_en,
+ &axg_tdmout_a_sclk_pre_en,
+ &axg_tdmout_b_sclk_pre_en,
+ &axg_tdmout_c_sclk_pre_en,
+ &axg_tdmin_a_sclk_post_en,
+ &axg_tdmin_b_sclk_post_en,
+ &axg_tdmin_c_sclk_post_en,
+ &axg_tdmin_lb_sclk_post_en,
+ &axg_tdmout_a_sclk_post_en,
+ &axg_tdmout_b_sclk_post_en,
+ &axg_tdmout_c_sclk_post_en,
+ &axg_tdmin_a_sclk,
+ &axg_tdmin_b_sclk,
+ &axg_tdmin_c_sclk,
+ &axg_tdmin_lb_sclk,
+ &axg_tdmout_a_sclk,
+ &axg_tdmout_b_sclk,
+ &axg_tdmout_c_sclk,
+ &axg_tdmin_a_lrclk,
+ &axg_tdmin_b_lrclk,
+ &axg_tdmin_c_lrclk,
+ &axg_tdmin_lb_lrclk,
+ &axg_tdmout_a_lrclk,
+ &axg_tdmout_b_lrclk,
+ &axg_tdmout_c_lrclk,
+};
+
+static struct clk *devm_clk_get_enable(struct device *dev, char *id)
+{
+ struct clk *clk;
+ int ret;
+
+ clk = devm_clk_get(dev, id);
+ if (IS_ERR(clk)) {
+ if (PTR_ERR(clk) != -EPROBE_DEFER)
+ dev_err(dev, "failed to get %s", id);
+ return clk;
+ }
+
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_err(dev, "failed to enable %s", id);
+ return ERR_PTR(ret);
+ }
+
+ ret = devm_add_action_or_reset(dev,
+ (void(*)(void *))clk_disable_unprepare,
+ clk);
+ if (ret) {
+ dev_err(dev, "failed to add reset action on %s", id);
+ return ERR_PTR(ret);
+ }
+
+ return clk;
+}
+
+static const struct clk_ops axg_clk_no_ops = {};
+
+static struct clk_hw *axg_clk_hw_register_bypass(struct device *dev,
+ const char *name,
+ const char *parent_name)
+{
+ struct clk_hw *hw;
+ struct clk_init_data init;
+ char *clk_name;
+ int ret;
+
+ hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
+ if (!hw)
+ return ERR_PTR(-ENOMEM);
+
+ clk_name = kasprintf(GFP_KERNEL, "axg_%s", name);
+ if (!clk_name)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = clk_name;
+ init.ops = &axg_clk_no_ops;
+ init.flags = 0;
+ init.parent_names = parent_name ? &parent_name : NULL;
+ init.num_parents = parent_name ? 1 : 0;
+ hw->init = &init;
+
+ ret = devm_clk_hw_register(dev, hw);
+ kfree(clk_name);
+
+ return ret ? ERR_PTR(ret) : hw;
+}
+
+static int axg_register_clk_hw_input(struct device *dev,
+ const char *name,
+ unsigned int clkid)
+{
+ struct clk *parent_clk = devm_clk_get(dev, name);
+ struct clk_hw *hw = NULL;
+
+ if (IS_ERR(parent_clk)) {
+ int err = PTR_ERR(parent_clk);
+
+ /* It is ok if an input clock is missing */
+ if (err == -ENOENT) {
+ dev_dbg(dev, "%s not provided", name);
+ } else {
+ if (err != -EPROBE_DEFER)
+ dev_err(dev, "failed to get %s clock", name);
+ return err;
+ }
+ } else {
+ hw = axg_clk_hw_register_bypass(dev, name,
+ __clk_get_name(parent_clk));
+ }
+
+ if (IS_ERR(hw)) {
+ dev_err(dev, "failed to register %s clock", name);
+ return PTR_ERR(hw);
+ }
+
+ axg_audio_hw_onecell_data.hws[clkid] = hw;
+ return 0;
+}
+
+static int axg_register_clk_hw_inputs(struct device *dev,
+ const char *basename,
+ unsigned int count,
+ unsigned int clkid)
+{
+ char *name;
+ int i, ret;
+
+ for (i = 0; i < count; i++) {
+ name = kasprintf(GFP_KERNEL, "%s%d", basename, i);
+ if (!name)
+ return -ENOMEM;
+
+ ret = axg_register_clk_hw_input(dev, name, clkid + i);
+ kfree(name);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct regmap_config axg_audio_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = AUDIO_CLK_PDMIN_CTRL1,
+};
+
+static int axg_audio_clkc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct regmap *map;
+ struct resource *res;
+ void __iomem *regs;
+ struct clk *clk;
+ struct clk_hw *hw;
+ int ret, i;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ map = devm_regmap_init_mmio(dev, regs, &axg_audio_regmap_cfg);
+ if (IS_ERR(map)) {
+ dev_err(dev, "failed to init regmap: %ld\n", PTR_ERR(map));
+ return PTR_ERR(map);
+ }
+
+ /* Get the mandatory peripheral clock */
+ clk = devm_clk_get_enable(dev, "pclk");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ ret = device_reset(dev);
+ if (ret) {
+ dev_err(dev, "failed to reset device\n");
+ return ret;
+ }
+
+ /* Register the peripheral input clock */
+ hw = axg_clk_hw_register_bypass(dev, "audio_pclk",
+ __clk_get_name(clk));
+ if (IS_ERR(hw))
+ return PTR_ERR(hw);
+
+ axg_audio_hw_onecell_data.hws[AUD_CLKID_PCLK] = hw;
+
+ /* Register optional input master clocks */
+ ret = axg_register_clk_hw_inputs(dev, "mst_in",
+ AXG_MST_IN_COUNT,
+ AUD_CLKID_MST0);
+ if (ret)
+ return ret;
+
+ /* Register optional input slave sclks */
+ ret = axg_register_clk_hw_inputs(dev, "slv_sclk",
+ AXG_SLV_SCLK_COUNT,
+ AUD_CLKID_SLV_SCLK0);
+ if (ret)
+ return ret;
+
+ /* Register optional input slave lrclks */
+ ret = axg_register_clk_hw_inputs(dev, "slv_lrclk",
+ AXG_SLV_LRCLK_COUNT,
+ AUD_CLKID_SLV_LRCLK0);
+ if (ret)
+ return ret;
+
+ /* Populate regmap for the regmap backed clocks */
+ for (i = 0; i < ARRAY_SIZE(axg_audio_clk_regmaps); i++)
+ axg_audio_clk_regmaps[i]->map = map;
+
+ /* Take care to skip the registered input clocks */
+ for (i = AUD_CLKID_DDR_ARB; i < axg_audio_hw_onecell_data.num; i++) {
+ hw = axg_audio_hw_onecell_data.hws[i];
+ /* array might be sparse */
+ if (!hw)
+ continue;
+
+ ret = devm_clk_hw_register(dev, hw);
+ if (ret) {
+ dev_err(dev, "failed to register clock %s\n",
+ hw->init->name);
+ return ret;
+ }
+ }
+
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+ &axg_audio_hw_onecell_data);
+}
+
+static const struct of_device_id clkc_match_table[] = {
+ { .compatible = "amlogic,axg-audio-clkc" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, clkc_match_table);
+
+static struct platform_driver axg_audio_driver = {
+ .probe = axg_audio_clkc_probe,
+ .driver = {
+ .name = "axg-audio-clkc",
+ .of_match_table = clkc_match_table,
+ },
+};
+module_platform_driver(axg_audio_driver);
+
+MODULE_DESCRIPTION("Amlogic A113x Audio Clock driver");
+MODULE_AUTHOR("Jerome Brunet <[email protected]>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/meson/axg-audio.h b/drivers/clk/meson/axg-audio.h
new file mode 100644
index 000000000000..e7a0efd12429
--- /dev/null
+++ b/drivers/clk/meson/axg-audio.h
@@ -0,0 +1,127 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
+/*
+ * Copyright (c) 2018 BayLibre, SAS.
+ * Author: Jerome Brunet <[email protected]>
+ */
+
+#ifndef __AXG_AUDIO_CLKC_H
+#define __AXG_AUDIO_CLKC_H
+
+/*
+ * Audio Clock register offsets
+ *
+ * Register offsets from the datasheet must be multiplied by 4 before
+ * to get the right offset
+ */
+#define AUDIO_CLK_GATE_EN 0x000
+#define AUDIO_MCLK_A_CTRL 0x004
+#define AUDIO_MCLK_B_CTRL 0x008
+#define AUDIO_MCLK_C_CTRL 0x00C
+#define AUDIO_MCLK_D_CTRL 0x010
+#define AUDIO_MCLK_E_CTRL 0x014
+#define AUDIO_MCLK_F_CTRL 0x018
+#define AUDIO_MST_A_SCLK_CTRL0 0x040
+#define AUDIO_MST_A_SCLK_CTRL1 0x044
+#define AUDIO_MST_B_SCLK_CTRL0 0x048
+#define AUDIO_MST_B_SCLK_CTRL1 0x04C
+#define AUDIO_MST_C_SCLK_CTRL0 0x050
+#define AUDIO_MST_C_SCLK_CTRL1 0x054
+#define AUDIO_MST_D_SCLK_CTRL0 0x058
+#define AUDIO_MST_D_SCLK_CTRL1 0x05C
+#define AUDIO_MST_E_SCLK_CTRL0 0x060
+#define AUDIO_MST_E_SCLK_CTRL1 0x064
+#define AUDIO_MST_F_SCLK_CTRL0 0x068
+#define AUDIO_MST_F_SCLK_CTRL1 0x06C
+#define AUDIO_CLK_TDMIN_A_CTRL 0x080
+#define AUDIO_CLK_TDMIN_B_CTRL 0x084
+#define AUDIO_CLK_TDMIN_C_CTRL 0x088
+#define AUDIO_CLK_TDMIN_LB_CTRL 0x08C
+#define AUDIO_CLK_TDMOUT_A_CTRL 0x090
+#define AUDIO_CLK_TDMOUT_B_CTRL 0x094
+#define AUDIO_CLK_TDMOUT_C_CTRL 0x098
+#define AUDIO_CLK_SPDIFIN_CTRL 0x09C
+#define AUDIO_CLK_SPDIFOUT_CTRL 0x0A0
+#define AUDIO_CLK_RESAMPLE_CTRL 0x0A4
+#define AUDIO_CLK_LOCKER_CTRL 0x0A8
+#define AUDIO_CLK_PDMIN_CTRL0 0x0AC
+#define AUDIO_CLK_PDMIN_CTRL1 0x0B0
+
+/*
+ * CLKID index values
+ * These indices are entirely contrived and do not map onto the hardware.
+ */
+
+#define AUD_CLKID_PCLK 0
+#define AUD_CLKID_MST0 1
+#define AUD_CLKID_MST1 2
+#define AUD_CLKID_MST2 3
+#define AUD_CLKID_MST3 4
+#define AUD_CLKID_MST4 5
+#define AUD_CLKID_MST5 6
+#define AUD_CLKID_MST6 7
+#define AUD_CLKID_MST7 8
+#define AUD_CLKID_MST_A_MCLK_SEL 59
+#define AUD_CLKID_MST_B_MCLK_SEL 60
+#define AUD_CLKID_MST_C_MCLK_SEL 61
+#define AUD_CLKID_MST_D_MCLK_SEL 62
+#define AUD_CLKID_MST_E_MCLK_SEL 63
+#define AUD_CLKID_MST_F_MCLK_SEL 64
+#define AUD_CLKID_MST_A_MCLK_DIV 65
+#define AUD_CLKID_MST_B_MCLK_DIV 66
+#define AUD_CLKID_MST_C_MCLK_DIV 67
+#define AUD_CLKID_MST_D_MCLK_DIV 68
+#define AUD_CLKID_MST_E_MCLK_DIV 69
+#define AUD_CLKID_MST_F_MCLK_DIV 70
+#define AUD_CLKID_SPDIFOUT_CLK_SEL 71
+#define AUD_CLKID_SPDIFOUT_CLK_DIV 72
+#define AUD_CLKID_SPDIFIN_CLK_SEL 73
+#define AUD_CLKID_SPDIFIN_CLK_DIV 74
+#define AUD_CLKID_PDM_DCLK_SEL 75
+#define AUD_CLKID_PDM_DCLK_DIV 76
+#define AUD_CLKID_PDM_SYSCLK_SEL 77
+#define AUD_CLKID_PDM_SYSCLK_DIV 78
+#define AUD_CLKID_MST_A_SCLK_PRE_EN 92
+#define AUD_CLKID_MST_B_SCLK_PRE_EN 93
+#define AUD_CLKID_MST_C_SCLK_PRE_EN 94
+#define AUD_CLKID_MST_D_SCLK_PRE_EN 95
+#define AUD_CLKID_MST_E_SCLK_PRE_EN 96
+#define AUD_CLKID_MST_F_SCLK_PRE_EN 97
+#define AUD_CLKID_MST_A_SCLK_DIV 98
+#define AUD_CLKID_MST_B_SCLK_DIV 99
+#define AUD_CLKID_MST_C_SCLK_DIV 100
+#define AUD_CLKID_MST_D_SCLK_DIV 101
+#define AUD_CLKID_MST_E_SCLK_DIV 102
+#define AUD_CLKID_MST_F_SCLK_DIV 103
+#define AUD_CLKID_MST_A_SCLK_POST_EN 104
+#define AUD_CLKID_MST_B_SCLK_POST_EN 105
+#define AUD_CLKID_MST_C_SCLK_POST_EN 106
+#define AUD_CLKID_MST_D_SCLK_POST_EN 107
+#define AUD_CLKID_MST_E_SCLK_POST_EN 108
+#define AUD_CLKID_MST_F_SCLK_POST_EN 109
+#define AUD_CLKID_MST_A_LRCLK_DIV 110
+#define AUD_CLKID_MST_B_LRCLK_DIV 111
+#define AUD_CLKID_MST_C_LRCLK_DIV 112
+#define AUD_CLKID_MST_D_LRCLK_DIV 113
+#define AUD_CLKID_MST_E_LRCLK_DIV 114
+#define AUD_CLKID_MST_F_LRCLK_DIV 115
+#define AUD_CLKID_TDMIN_A_SCLK_PRE_EN 137
+#define AUD_CLKID_TDMIN_B_SCLK_PRE_EN 138
+#define AUD_CLKID_TDMIN_C_SCLK_PRE_EN 139
+#define AUD_CLKID_TDMIN_LB_SCLK_PRE_EN 140
+#define AUD_CLKID_TDMOUT_A_SCLK_PRE_EN 141
+#define AUD_CLKID_TDMOUT_B_SCLK_PRE_EN 142
+#define AUD_CLKID_TDMOUT_C_SCLK_PRE_EN 143
+#define AUD_CLKID_TDMIN_A_SCLK_POST_EN 144
+#define AUD_CLKID_TDMIN_B_SCLK_POST_EN 145
+#define AUD_CLKID_TDMIN_C_SCLK_POST_EN 146
+#define AUD_CLKID_TDMIN_LB_SCLK_POST_EN 147
+#define AUD_CLKID_TDMOUT_A_SCLK_POST_EN 148
+#define AUD_CLKID_TDMOUT_B_SCLK_POST_EN 149
+#define AUD_CLKID_TDMOUT_C_SCLK_POST_EN 150
+
+/* include the CLKIDs which are part of the DT bindings */
+#include <dt-bindings/clock/axg-audio-clkc.h>
+
+#define NR_CLKS 151
+
+#endif /*__AXG_AUDIO_CLKC_H */
--
2.14.3


2018-04-25 16:36:40

by Jerome Brunet

[permalink] [raw]
Subject: [PATCH 6/7] clk: meson: axg: document bindings for the audio clock controller

Add documentation for the device tree bindings of the audio clock
controller of the A113 based SoCs

Signed-off-by: Jerome Brunet <[email protected]>
---
.../bindings/clock/amlogic,axg-audio-clkc.txt | 56 ++++++++++++++++++++++
1 file changed, 56 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/amlogic,axg-audio-clkc.txt

diff --git a/Documentation/devicetree/bindings/clock/amlogic,axg-audio-clkc.txt b/Documentation/devicetree/bindings/clock/amlogic,axg-audio-clkc.txt
new file mode 100644
index 000000000000..1b989ceda567
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/amlogic,axg-audio-clkc.txt
@@ -0,0 +1,56 @@
+* Amlogic AXG Audio Clock Controllers
+
+The Amlogic AXG audio clock controller generates and supplies clock to the
+other elements of the audio subsystem, such as fifos, i2s, spdif and pdm
+devices.
+
+Required Properties:
+
+- compatible : should be "amlogic,axg-audio-clkc" for the A113X and A113D
+- reg : physical base address of the clock controller and length of
+ memory mapped region.
+- clocks : a list of phandle + clock-specifier pairs for the clocks listed
+ in clock-names.
+- clock-names : must contain the following:
+ * "pclk" - Main peripheral bus clock
+ may contain the following:
+ * "mst_in[0-7]" - 8 input plls to generate clock signals
+ * "slv_sclk[0-9]" - 10 slave bit clocks provided by external
+ components.
+ * "slv_lrclk[0-9]" - 10 slave sample clocks provided by external
+ components.
+- reset : phandle of the internal reset line
+- #clock-cells : should be 1.
+
+Each clock is assigned an identifier and client nodes can use this identifier
+to specify the clock which they consume. All available clocks are defined as
+preprocessor macros in the dt-bindings/clock/axg-audio-clkc.h header and can be
+used in device tree sources.
+
+Example:
+
+clkc_audio: clock-controller {
+ compatible = "amlogic,axg-audio-clkc";
+ reg = <0x0 0x0 0x0 0xb4>;
+ #clock-cells = <1>;
+
+ clocks = <&clkc CLKID_AUDIO>,
+ <&clkc CLKID_MPLL0>,
+ <&clkc CLKID_MPLL1>,
+ <&clkc CLKID_MPLL2>,
+ <&clkc CLKID_MPLL3>,
+ <&clkc CLKID_HIFI_PLL>,
+ <&clkc CLKID_FCLK_DIV3>,
+ <&clkc CLKID_FCLK_DIV4>,
+ <&clkc CLKID_GP0_PLL>;
+ clock-names = "pclk",
+ "mst_in0",
+ "mst_in1",
+ "mst_in2",
+ "mst_in3",
+ "mst_in4",
+ "mst_in5",
+ "mst_in6",
+ "mst_in7";
+ resets = <&reset RESET_AUDIO>;
+};
--
2.14.3


2018-04-25 16:36:40

by Jerome Brunet

[permalink] [raw]
Subject: [PATCH 4/7] clk: meson: add axg audio sclk divider driver

Add a driver to control the clock divider found in the sample clock
generator of the axg audio clock controller.

The sclk divider accumulates specific features which make the generic
divider unsuitable to control it:
- zero based divider (div = val + 1), but zero value gates the clock,
so minimum divider value is 2.
- lrclk variant may adjust the duty cycle depending the divider value
and the 'hi' value.

Signed-off-by: Jerome Brunet <[email protected]>
---
drivers/clk/meson/Makefile | 2 +-
drivers/clk/meson/clkc-audio.h | 8 ++
drivers/clk/meson/sclk-div.c | 243 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 252 insertions(+), 1 deletion(-)
create mode 100644 drivers/clk/meson/sclk-div.c

diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index 64bb917fe1f0..f51b4754c31b 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -4,7 +4,7 @@

obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-audio-divider.o
obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-phase.o
-obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o
+obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o sclk-div.o
obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o
obj-$(CONFIG_COMMON_CLK_AXG) += axg.o
diff --git a/drivers/clk/meson/clkc-audio.h b/drivers/clk/meson/clkc-audio.h
index 286ff1201258..0a7c157ebf81 100644
--- a/drivers/clk/meson/clkc-audio.h
+++ b/drivers/clk/meson/clkc-audio.h
@@ -15,6 +15,14 @@ struct meson_clk_triphase_data {
struct parm ph2;
};

+struct meson_sclk_div_data {
+ struct parm div;
+ struct parm hi;
+ unsigned int cached_div;
+ struct clk_duty cached_duty;
+};
+
extern const struct clk_ops meson_clk_triphase_ops;
+extern const struct clk_ops meson_sclk_div_ops;

#endif /* __MESON_CLKC_AUDIO_H */
diff --git a/drivers/clk/meson/sclk-div.c b/drivers/clk/meson/sclk-div.c
new file mode 100644
index 000000000000..8c0bc914a6d7
--- /dev/null
+++ b/drivers/clk/meson/sclk-div.c
@@ -0,0 +1,243 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2018 BayLibre, SAS.
+ * Author: Jerome Brunet <[email protected]>
+ *
+ * Sample clock generator divider:
+ * This HW divider gates with value 0 but is otherwise a zero based divider:
+ *
+ * val >= 1
+ * divider = val + 1
+ *
+ * The duty cycle may also be set for the LR clock variant. The duty cycle
+ * ratio is:
+ *
+ * hi = [0 - val]
+ * duty_cycle = (1 + hi) / (1 + val)
+ */
+
+#include "clkc-audio.h"
+
+static inline struct meson_sclk_div_data *
+meson_sclk_div_data(struct clk_regmap *clk)
+{
+ return (struct meson_sclk_div_data *)clk->data;
+}
+
+static int sclk_div_maxval(struct meson_sclk_div_data *sclk)
+{
+ return (1 << sclk->div.width) - 1;
+}
+
+static int sclk_div_maxdiv(struct meson_sclk_div_data *sclk)
+{
+ return sclk_div_maxval(sclk) + 1;
+}
+
+static int sclk_div_getdiv(struct clk_hw *hw, unsigned long rate,
+ unsigned long prate, int maxdiv)
+{
+ int div = DIV_ROUND_CLOSEST_ULL((u64)prate, rate);
+
+ return clamp(div, 2, maxdiv);
+}
+
+static int sclk_div_bestdiv(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate,
+ struct meson_sclk_div_data *sclk)
+{
+ struct clk_hw *parent = clk_hw_get_parent(hw);
+ int bestdiv = 0, i;
+ unsigned long maxdiv, now, parent_now;
+ unsigned long best = 0, best_parent = 0;
+
+ if (!rate)
+ rate = 1;
+
+ maxdiv = sclk_div_maxdiv(sclk);
+
+ if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT))
+ return sclk_div_getdiv(hw, rate, *prate, maxdiv);
+
+ /*
+ * The maximum divider we can use without overflowing
+ * unsigned long in rate * i below
+ */
+ maxdiv = min(ULONG_MAX / rate, maxdiv);
+
+ for (i = 2; i <= maxdiv; i++) {
+ /*
+ * It's the most ideal case if the requested rate can be
+ * divided from parent clock without needing to change
+ * parent rate, so return the divider immediately.
+ */
+ if (rate * i == *prate)
+ return i;
+
+ parent_now = clk_hw_round_rate(parent, rate * i);
+ now = DIV_ROUND_UP_ULL((u64)parent_now, i);
+
+ if (abs(rate - now) < abs(rate - best)) {
+ bestdiv = i;
+ best = now;
+ best_parent = parent_now;
+ }
+ }
+
+ if (!bestdiv)
+ bestdiv = sclk_div_maxdiv(sclk);
+ else
+ *prate = best_parent;
+
+ return bestdiv;
+}
+
+static long sclk_div_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
+ int div;
+
+ div = sclk_div_bestdiv(hw, rate, prate, sclk);
+
+ return DIV_ROUND_UP_ULL((u64)*prate, div);
+}
+
+static void sclk_apply_ratio(struct clk_regmap *clk,
+ struct meson_sclk_div_data *sclk)
+{
+ unsigned int hi = DIV_ROUND_CLOSEST(sclk->cached_div *
+ sclk->cached_duty.num,
+ sclk->cached_duty.den);
+
+ if (hi)
+ hi -= 1;
+
+ meson_parm_write(clk->map, &sclk->hi, hi);
+}
+
+static int sclk_div_set_duty_cycle(struct clk_hw *hw,
+ struct clk_duty *duty)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
+
+ if (MESON_PARM_APPLICABLE(&sclk->hi)) {
+ memcpy(&sclk->cached_duty, duty, sizeof(*duty));
+ sclk_apply_ratio(clk, sclk);
+ }
+
+ return 0;
+}
+
+static int sclk_div_get_duty_cycle(struct clk_hw *hw,
+ struct clk_duty *duty)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
+ int hi;
+
+ if (!MESON_PARM_APPLICABLE(&sclk->hi)) {
+ duty->num = 1;
+ duty->den = 2;
+ return 0;
+ }
+
+ hi = meson_parm_read(clk->map, &sclk->hi);
+ duty->num = hi + 1;
+ duty->den = sclk->cached_div;
+ return 0;
+}
+
+static void sclk_apply_divider(struct clk_regmap *clk,
+ struct meson_sclk_div_data *sclk)
+{
+ if (MESON_PARM_APPLICABLE(&sclk->hi))
+ sclk_apply_ratio(clk, sclk);
+
+ meson_parm_write(clk->map, &sclk->div, sclk->cached_div - 1);
+}
+
+static int sclk_div_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long prate)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
+ unsigned long maxdiv = sclk_div_maxdiv(sclk);
+
+ sclk->cached_div = sclk_div_getdiv(hw, rate, prate, maxdiv);
+
+ if (clk_hw_is_enabled(hw))
+ sclk_apply_divider(clk, sclk);
+
+ return 0;
+}
+
+static unsigned long sclk_div_recalc_rate(struct clk_hw *hw,
+ unsigned long prate)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
+
+ return DIV_ROUND_UP_ULL((u64)prate, sclk->cached_div);
+}
+
+static int sclk_div_enable(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
+
+ sclk_apply_divider(clk, sclk);
+
+ return 0;
+}
+
+static void sclk_div_disable(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
+
+ meson_parm_write(clk->map, &sclk->div, 0);
+}
+
+static int sclk_div_is_enabled(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
+
+ if (meson_parm_read(clk->map, &sclk->div))
+ return 1;
+
+ return 0;
+}
+
+static void sclk_div_init(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
+ unsigned int val;
+
+ val = meson_parm_read(clk->map, &sclk->div);
+
+ /* if the divider is initially disabled, assume max */
+ if (!val)
+ sclk->cached_div = sclk_div_maxdiv(sclk);
+ else
+ sclk->cached_div = val + 1;
+
+ sclk_div_get_duty_cycle(hw, &sclk->cached_duty);
+}
+
+const struct clk_ops meson_sclk_div_ops = {
+ .recalc_rate = sclk_div_recalc_rate,
+ .round_rate = sclk_div_round_rate,
+ .set_rate = sclk_div_set_rate,
+ .enable = sclk_div_enable,
+ .disable = sclk_div_disable,
+ .is_enabled = sclk_div_is_enabled,
+ .get_duty_cycle = sclk_div_get_duty_cycle,
+ .set_duty_cycle = sclk_div_set_duty_cycle,
+ .init = sclk_div_init,
+};
+EXPORT_SYMBOL_GPL(meson_sclk_div_ops);
--
2.14.3


2018-04-25 16:36:51

by Jerome Brunet

[permalink] [raw]
Subject: [PATCH 5/7] clk: meson: axg: export audio clock controller id bindings

export the clock ids dt-bindings usable by the consumers of the axg
audio clock controller

Signed-off-by: Jerome Brunet <[email protected]>
---
include/dt-bindings/clock/axg-audio-clkc.h | 94 ++++++++++++++++++++++++++++++
1 file changed, 94 insertions(+)
create mode 100644 include/dt-bindings/clock/axg-audio-clkc.h

diff --git a/include/dt-bindings/clock/axg-audio-clkc.h b/include/dt-bindings/clock/axg-audio-clkc.h
new file mode 100644
index 000000000000..4426ae655858
--- /dev/null
+++ b/include/dt-bindings/clock/axg-audio-clkc.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
+/*
+ * Copyright (c) 2018 Baylibre SAS.
+ * Author: Jerome Brunet <[email protected]>
+ */
+
+#ifndef __AXG_AUDIO_CLKC_BINDINGS_H
+#define __AXG_AUDIO_CLKC_BINDINGS_H
+
+#define AUD_CLKID_SLV_SCLK0 9
+#define AUD_CLKID_SLV_SCLK1 10
+#define AUD_CLKID_SLV_SCLK2 11
+#define AUD_CLKID_SLV_SCLK3 12
+#define AUD_CLKID_SLV_SCLK4 13
+#define AUD_CLKID_SLV_SCLK5 14
+#define AUD_CLKID_SLV_SCLK6 15
+#define AUD_CLKID_SLV_SCLK7 16
+#define AUD_CLKID_SLV_SCLK8 17
+#define AUD_CLKID_SLV_SCLK9 18
+#define AUD_CLKID_SLV_LRCLK0 19
+#define AUD_CLKID_SLV_LRCLK1 20
+#define AUD_CLKID_SLV_LRCLK2 21
+#define AUD_CLKID_SLV_LRCLK3 22
+#define AUD_CLKID_SLV_LRCLK4 23
+#define AUD_CLKID_SLV_LRCLK5 24
+#define AUD_CLKID_SLV_LRCLK6 25
+#define AUD_CLKID_SLV_LRCLK7 26
+#define AUD_CLKID_SLV_LRCLK8 27
+#define AUD_CLKID_SLV_LRCLK9 28
+#define AUD_CLKID_DDR_ARB 29
+#define AUD_CLKID_PDM 30
+#define AUD_CLKID_TDMIN_A 31
+#define AUD_CLKID_TDMIN_B 32
+#define AUD_CLKID_TDMIN_C 33
+#define AUD_CLKID_TDMIN_LB 34
+#define AUD_CLKID_TDMOUT_A 35
+#define AUD_CLKID_TDMOUT_B 36
+#define AUD_CLKID_TDMOUT_C 37
+#define AUD_CLKID_FRDDR_A 38
+#define AUD_CLKID_FRDDR_B 39
+#define AUD_CLKID_FRDDR_C 40
+#define AUD_CLKID_TODDR_A 41
+#define AUD_CLKID_TODDR_B 42
+#define AUD_CLKID_TODDR_C 43
+#define AUD_CLKID_LOOPBACK 44
+#define AUD_CLKID_SPDIFIN 45
+#define AUD_CLKID_SPDIFOUT 46
+#define AUD_CLKID_RESAMPLE 47
+#define AUD_CLKID_POWER_DETECT 48
+#define AUD_CLKID_MST_A_MCLK 49
+#define AUD_CLKID_MST_B_MCLK 50
+#define AUD_CLKID_MST_C_MCLK 51
+#define AUD_CLKID_MST_D_MCLK 52
+#define AUD_CLKID_MST_E_MCLK 53
+#define AUD_CLKID_MST_F_MCLK 54
+#define AUD_CLKID_SPDIFOUT_CLK 55
+#define AUD_CLKID_SPDIFIN_CLK 56
+#define AUD_CLKID_PDM_DCLK 57
+#define AUD_CLKID_PDM_SYSCLK 58
+#define AUD_CLKID_MST_A_SCLK 79
+#define AUD_CLKID_MST_B_SCLK 80
+#define AUD_CLKID_MST_C_SCLK 81
+#define AUD_CLKID_MST_D_SCLK 82
+#define AUD_CLKID_MST_E_SCLK 83
+#define AUD_CLKID_MST_F_SCLK 84
+#define AUD_CLKID_MST_A_LRCLK 86
+#define AUD_CLKID_MST_B_LRCLK 87
+#define AUD_CLKID_MST_C_LRCLK 88
+#define AUD_CLKID_MST_D_LRCLK 89
+#define AUD_CLKID_MST_E_LRCLK 90
+#define AUD_CLKID_MST_F_LRCLK 91
+#define AUD_CLKID_TDMIN_A_SCLK_SEL 116
+#define AUD_CLKID_TDMIN_B_SCLK_SEL 117
+#define AUD_CLKID_TDMIN_C_SCLK_SEL 118
+#define AUD_CLKID_TDMIN_LB_SCLK_SEL 119
+#define AUD_CLKID_TDMOUT_A_SCLK_SEL 120
+#define AUD_CLKID_TDMOUT_B_SCLK_SEL 121
+#define AUD_CLKID_TDMOUT_C_SCLK_SEL 122
+#define AUD_CLKID_TDMIN_A_SCLK 123
+#define AUD_CLKID_TDMIN_B_SCLK 124
+#define AUD_CLKID_TDMIN_C_SCLK 125
+#define AUD_CLKID_TDMIN_LB_SCLK 126
+#define AUD_CLKID_TDMOUT_A_SCLK 127
+#define AUD_CLKID_TDMOUT_B_SCLK 128
+#define AUD_CLKID_TDMOUT_C_SCLK 129
+#define AUD_CLKID_TDMIN_A_LRCLK 130
+#define AUD_CLKID_TDMIN_B_LRCLK 131
+#define AUD_CLKID_TDMIN_C_LRCLK 132
+#define AUD_CLKID_TDMIN_LB_LRCLK 133
+#define AUD_CLKID_TDMOUT_A_LRCLK 134
+#define AUD_CLKID_TDMOUT_B_LRCLK 135
+#define AUD_CLKID_TDMOUT_C_LRCLK 136
+
+#endif /* __AXG_AUDIO_CLKC_BINDINGS_H */
--
2.14.3


2018-04-25 16:36:55

by Jerome Brunet

[permalink] [raw]
Subject: [PATCH 1/7] clk: meson: clean-up meson clock configuration

Clean the dependencies in meson clock Kconfig.
CLK_AMLOGIC should actually select CLK_REGMAP_MESON which it uses. Also,
each platform should select CLK_AMLOGIC, so everything is properly turned
on when the platform Kconfig enable each configuration flag

Signed-off-by: Jerome Brunet <[email protected]>
---
drivers/clk/meson/Kconfig | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
index d5cbec522aec..87d69573e172 100644
--- a/drivers/clk/meson/Kconfig
+++ b/drivers/clk/meson/Kconfig
@@ -1,7 +1,7 @@
config COMMON_CLK_AMLOGIC
bool
- depends on OF
depends on ARCH_MESON || COMPILE_TEST
+ select COMMON_CLK_REGMAP_MESON

config COMMON_CLK_REGMAP_MESON
bool
@@ -9,9 +9,8 @@ config COMMON_CLK_REGMAP_MESON

config COMMON_CLK_MESON8B
bool
- depends on COMMON_CLK_AMLOGIC
+ select COMMON_CLK_AMLOGIC
select RESET_CONTROLLER
- select COMMON_CLK_REGMAP_MESON
help
Support for the clock controller on AmLogic S802 (Meson8),
S805 (Meson8b) and S812 (Meson8m2) devices. Say Y if you
@@ -19,9 +18,8 @@ config COMMON_CLK_MESON8B

config COMMON_CLK_GXBB
bool
- depends on COMMON_CLK_AMLOGIC
+ select COMMON_CLK_AMLOGIC
select RESET_CONTROLLER
- select COMMON_CLK_REGMAP_MESON
select MFD_SYSCON
help
Support for the clock controller on AmLogic S905 devices, aka gxbb.
@@ -29,9 +27,8 @@ config COMMON_CLK_GXBB

config COMMON_CLK_AXG
bool
- depends on COMMON_CLK_AMLOGIC
+ select COMMON_CLK_AMLOGIC
select RESET_CONTROLLER
- select COMMON_CLK_REGMAP_MESON
select MFD_SYSCON
help
Support for the clock controller on AmLogic A113D devices, aka axg.
--
2.14.3


2018-04-25 16:37:08

by Jerome Brunet

[permalink] [raw]
Subject: [PATCH 3/7] clk: meson: add triple phase clock driver

Add a driver to control the output of the sample clock generator found
in the axg audio clock controller.

The goal of this driver is to coherently control the phase provided to
the different element using the sample clock generator. This simplify
the usage of the sample clock generator a lot, without comprising the
ability of the SoC.

Signed-off-by: Jerome Brunet <[email protected]>
---
drivers/clk/meson/Kconfig | 5 +++
drivers/clk/meson/Makefile | 1 +
drivers/clk/meson/clk-triphase.c | 68 ++++++++++++++++++++++++++++++++++++++++
drivers/clk/meson/clkc-audio.h | 20 ++++++++++++
4 files changed, 94 insertions(+)
create mode 100644 drivers/clk/meson/clk-triphase.c
create mode 100644 drivers/clk/meson/clkc-audio.h

diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
index 87d69573e172..7f7fd6fb3809 100644
--- a/drivers/clk/meson/Kconfig
+++ b/drivers/clk/meson/Kconfig
@@ -3,6 +3,11 @@ config COMMON_CLK_AMLOGIC
depends on ARCH_MESON || COMPILE_TEST
select COMMON_CLK_REGMAP_MESON

+config COMMON_CLK_AMLOGIC_AUDIO
+ bool
+ depends on ARCH_MESON || COMPILE_TEST
+ select COMMON_CLK_AMLOGIC
+
config COMMON_CLK_REGMAP_MESON
bool
select REGMAP
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index 352fb848c406..64bb917fe1f0 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -4,6 +4,7 @@

obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-audio-divider.o
obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-phase.o
+obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o
obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o
obj-$(CONFIG_COMMON_CLK_AXG) += axg.o
diff --git a/drivers/clk/meson/clk-triphase.c b/drivers/clk/meson/clk-triphase.c
new file mode 100644
index 000000000000..9508c03c73c1
--- /dev/null
+++ b/drivers/clk/meson/clk-triphase.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2018 BayLibre, SAS.
+ * Author: Jerome Brunet <[email protected]>
+ */
+
+#include <linux/clk-provider.h>
+#include "clkc-audio.h"
+
+/*
+ * This is a special clock for the audio controller.
+ * The phase of mst_sclk clock output can be controlled independently
+ * for the outside world (ph0), the tdmout (ph1) and tdmin (ph2).
+ * Controlling these 3 phases as just one makes things simpler and
+ * give the same clock view to all the element on the i2s bus.
+ * If necessary, we can still control the phase in the tdm block
+ * which makes these independent control redundant.
+ */
+static inline struct meson_clk_triphase_data *
+meson_clk_triphase_data(struct clk_regmap *clk)
+{
+ return (struct meson_clk_triphase_data *)clk->data;
+}
+
+static void meson_clk_triphase_sync(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
+ unsigned int val;
+
+ /* Get phase 0 and sync it to phase 1 and 2 */
+ val = meson_parm_read(clk->map, &tph->ph0);
+ meson_parm_write(clk->map, &tph->ph1, val);
+ meson_parm_write(clk->map, &tph->ph2, val);
+}
+
+static int meson_clk_triphase_get_phase(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
+ unsigned int val;
+
+ /* Phase are in sync, reading phase 0 is enough */
+ val = meson_parm_read(clk->map, &tph->ph0);
+
+ return meson_clk_degrees_from_val(val, tph->ph0.width);
+}
+
+static int meson_clk_triphase_set_phase(struct clk_hw *hw, int degrees)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
+ unsigned int val;
+
+ val = meson_clk_degrees_to_val(degrees, tph->ph0.width);
+ meson_parm_write(clk->map, &tph->ph0, val);
+ meson_parm_write(clk->map, &tph->ph1, val);
+ meson_parm_write(clk->map, &tph->ph2, val);
+
+ return 0;
+}
+
+const struct clk_ops meson_clk_triphase_ops = {
+ .init = meson_clk_triphase_sync,
+ .get_phase = meson_clk_triphase_get_phase,
+ .set_phase = meson_clk_triphase_set_phase,
+};
+EXPORT_SYMBOL_GPL(meson_clk_triphase_ops);
diff --git a/drivers/clk/meson/clkc-audio.h b/drivers/clk/meson/clkc-audio.h
new file mode 100644
index 000000000000..286ff1201258
--- /dev/null
+++ b/drivers/clk/meson/clkc-audio.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 BayLibre, SAS.
+ * Author: Jerome Brunet <[email protected]>
+ */
+
+#ifndef __MESON_CLKC_AUDIO_H
+#define __MESON_CLKC_AUDIO_H
+
+#include "clkc.h"
+
+struct meson_clk_triphase_data {
+ struct parm ph0;
+ struct parm ph1;
+ struct parm ph2;
+};
+
+extern const struct clk_ops meson_clk_triphase_ops;
+
+#endif /* __MESON_CLKC_AUDIO_H */
--
2.14.3


2018-04-26 08:47:55

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH 1/7] clk: meson: clean-up meson clock configuration

On 25/04/2018 18:32, Jerome Brunet wrote:
> Clean the dependencies in meson clock Kconfig.
> CLK_AMLOGIC should actually select CLK_REGMAP_MESON which it uses. Also,
> each platform should select CLK_AMLOGIC, so everything is properly turned
> on when the platform Kconfig enable each configuration flag
>
> Signed-off-by: Jerome Brunet <[email protected]>
> ---
> drivers/clk/meson/Kconfig | 11 ++++-------
> 1 file changed, 4 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
> index d5cbec522aec..87d69573e172 100644
> --- a/drivers/clk/meson/Kconfig
> +++ b/drivers/clk/meson/Kconfig
> @@ -1,7 +1,7 @@
> config COMMON_CLK_AMLOGIC
> bool
> - depends on OF
> depends on ARCH_MESON || COMPILE_TEST
> + select COMMON_CLK_REGMAP_MESON
>
> config COMMON_CLK_REGMAP_MESON
> bool
> @@ -9,9 +9,8 @@ config COMMON_CLK_REGMAP_MESON
>
> config COMMON_CLK_MESON8B
> bool
> - depends on COMMON_CLK_AMLOGIC
> + select COMMON_CLK_AMLOGIC
> select RESET_CONTROLLER
> - select COMMON_CLK_REGMAP_MESON
> help
> Support for the clock controller on AmLogic S802 (Meson8),
> S805 (Meson8b) and S812 (Meson8m2) devices. Say Y if you
> @@ -19,9 +18,8 @@ config COMMON_CLK_MESON8B
>
> config COMMON_CLK_GXBB
> bool
> - depends on COMMON_CLK_AMLOGIC
> + select COMMON_CLK_AMLOGIC
> select RESET_CONTROLLER
> - select COMMON_CLK_REGMAP_MESON
> select MFD_SYSCON
> help
> Support for the clock controller on AmLogic S905 devices, aka gxbb.
> @@ -29,9 +27,8 @@ config COMMON_CLK_GXBB
>
> config COMMON_CLK_AXG
> bool
> - depends on COMMON_CLK_AMLOGIC
> + select COMMON_CLK_AMLOGIC
> select RESET_CONTROLLER
> - select COMMON_CLK_REGMAP_MESON
> select MFD_SYSCON
> help
> Support for the clock controller on AmLogic A113D devices, aka axg.
>

Acked-by: Neil Armstrong <[email protected]>

2018-04-26 08:48:02

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH 2/7] clk: meson: add clk-phase clock driver

On 25/04/2018 18:32, Jerome Brunet wrote:
> Add a driver based meson clk-regmap to control clock phase on
> amlogic SoCs
>
> Signed-off-by: Jerome Brunet <[email protected]>
> ---
> drivers/clk/meson/Makefile | 1 +
> drivers/clk/meson/clk-phase.c | 63 +++++++++++++++++++++++++++++++++++++++++++
> drivers/clk/meson/clkc.h | 8 ++++++
> 3 files changed, 72 insertions(+)
> create mode 100644 drivers/clk/meson/clk-phase.c
>
> diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
> index ffee82e60b7a..352fb848c406 100644
> --- a/drivers/clk/meson/Makefile
> +++ b/drivers/clk/meson/Makefile
> @@ -3,6 +3,7 @@
> #
>
> obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-audio-divider.o
> +obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-phase.o
> obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
> obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o
> obj-$(CONFIG_COMMON_CLK_AXG) += axg.o
> diff --git a/drivers/clk/meson/clk-phase.c b/drivers/clk/meson/clk-phase.c
> new file mode 100644
> index 000000000000..96e70497ef1b
> --- /dev/null
> +++ b/drivers/clk/meson/clk-phase.c
> @@ -0,0 +1,63 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Copyright (c) 2018 BayLibre, SAS.
> + * Author: Jerome Brunet <[email protected]>
> + */
> +
> +#include <linux/clk-provider.h>
> +#include "clkc.h"
> +
> +#define phase_step(_width) (360 / (1 << (_width)))
> +
> +static inline struct meson_clk_phase_data *
> +meson_clk_phase_data(struct clk_regmap *clk)
> +{
> + return (struct meson_clk_phase_data *)clk->data;
> +}
> +
> +int meson_clk_degrees_from_val(unsigned int val, unsigned int width)
> +{
> + return phase_step(width) * val;
> +}
> +EXPORT_SYMBOL_GPL(meson_clk_degrees_from_val);
> +
> +unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width)
> +{
> + unsigned int val = DIV_ROUND_CLOSEST(degrees, phase_step(width));
> +
> + /*
> + * This last calculation is here for cases when degrees is rounded
> + * to 360, in which case val == (1 << width).
> + */
> + return val % (1 << width);
> +}
> +EXPORT_SYMBOL_GPL(meson_clk_degrees_to_val);
> +
> +static int meson_clk_phase_get_phase(struct clk_hw *hw)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct meson_clk_phase_data *phase = meson_clk_phase_data(clk);
> + unsigned int val;
> +
> + val = meson_parm_read(clk->map, &phase->ph);
> +
> + return meson_clk_degrees_from_val(val, phase->ph.width);
> +}
> +
> +static int meson_clk_phase_set_phase(struct clk_hw *hw, int degrees)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct meson_clk_phase_data *phase = meson_clk_phase_data(clk);
> + unsigned int val;
> +
> + val = meson_clk_degrees_to_val(degrees, phase->ph.width);
> + meson_parm_write(clk->map, &phase->ph, val);
> +
> + return 0;
> +}
> +
> +const struct clk_ops meson_clk_phase_ops = {
> + .get_phase = meson_clk_phase_get_phase,
> + .set_phase = meson_clk_phase_set_phase,
> +};
> +EXPORT_SYMBOL_GPL(meson_clk_phase_ops);
> diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h
> index 8fe73c4edca8..9a17d6705e0a 100644
> --- a/drivers/clk/meson/clkc.h
> +++ b/drivers/clk/meson/clkc.h
> @@ -104,6 +104,13 @@ struct meson_clk_audio_div_data {
> u8 flags;
> };
>
> +struct meson_clk_phase_data {
> + struct parm ph;
> +};
> +
> +int meson_clk_degrees_from_val(unsigned int val, unsigned int width);
> +unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width);
> +
> #define MESON_GATE(_name, _reg, _bit) \
> struct clk_regmap _name = { \
> .data = &(struct clk_regmap_gate_data){ \
> @@ -127,5 +134,6 @@ extern const struct clk_ops meson_clk_mpll_ro_ops;
> extern const struct clk_ops meson_clk_mpll_ops;
> extern const struct clk_ops meson_clk_audio_divider_ro_ops;
> extern const struct clk_ops meson_clk_audio_divider_ops;
> +extern const struct clk_ops meson_clk_phase_ops;
>
> #endif /* __CLKC_H */
>

Acked-by: Neil Armstrong <[email protected]>

2018-04-26 08:49:43

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH 3/7] clk: meson: add triple phase clock driver

On 25/04/2018 18:33, Jerome Brunet wrote:
> Add a driver to control the output of the sample clock generator found
> in the axg audio clock controller.
>
> The goal of this driver is to coherently control the phase provided to
> the different element using the sample clock generator. This simplify
> the usage of the sample clock generator a lot, without comprising the
> ability of the SoC.
>
> Signed-off-by: Jerome Brunet <[email protected]>
> ---
> drivers/clk/meson/Kconfig | 5 +++
> drivers/clk/meson/Makefile | 1 +
> drivers/clk/meson/clk-triphase.c | 68 ++++++++++++++++++++++++++++++++++++++++
> drivers/clk/meson/clkc-audio.h | 20 ++++++++++++
> 4 files changed, 94 insertions(+)
> create mode 100644 drivers/clk/meson/clk-triphase.c
> create mode 100644 drivers/clk/meson/clkc-audio.h
>
> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
> index 87d69573e172..7f7fd6fb3809 100644
> --- a/drivers/clk/meson/Kconfig
> +++ b/drivers/clk/meson/Kconfig
> @@ -3,6 +3,11 @@ config COMMON_CLK_AMLOGIC
> depends on ARCH_MESON || COMPILE_TEST
> select COMMON_CLK_REGMAP_MESON
>
> +config COMMON_CLK_AMLOGIC_AUDIO
> + bool
> + depends on ARCH_MESON || COMPILE_TEST
> + select COMMON_CLK_AMLOGIC
> +
> config COMMON_CLK_REGMAP_MESON
> bool
> select REGMAP
> diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
> index 352fb848c406..64bb917fe1f0 100644
> --- a/drivers/clk/meson/Makefile
> +++ b/drivers/clk/meson/Makefile
> @@ -4,6 +4,7 @@
>
> obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-audio-divider.o
> obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-phase.o
> +obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o
> obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
> obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o
> obj-$(CONFIG_COMMON_CLK_AXG) += axg.o
> diff --git a/drivers/clk/meson/clk-triphase.c b/drivers/clk/meson/clk-triphase.c
> new file mode 100644
> index 000000000000..9508c03c73c1
> --- /dev/null
> +++ b/drivers/clk/meson/clk-triphase.c
> @@ -0,0 +1,68 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Copyright (c) 2018 BayLibre, SAS.
> + * Author: Jerome Brunet <[email protected]>
> + */
> +
> +#include <linux/clk-provider.h>
> +#include "clkc-audio.h"
> +
> +/*
> + * This is a special clock for the audio controller.
> + * The phase of mst_sclk clock output can be controlled independently
> + * for the outside world (ph0), the tdmout (ph1) and tdmin (ph2).
> + * Controlling these 3 phases as just one makes things simpler and
> + * give the same clock view to all the element on the i2s bus.
> + * If necessary, we can still control the phase in the tdm block
> + * which makes these independent control redundant.
> + */
> +static inline struct meson_clk_triphase_data *
> +meson_clk_triphase_data(struct clk_regmap *clk)
> +{
> + return (struct meson_clk_triphase_data *)clk->data;
> +}
> +
> +static void meson_clk_triphase_sync(struct clk_hw *hw)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
> + unsigned int val;
> +
> + /* Get phase 0 and sync it to phase 1 and 2 */
> + val = meson_parm_read(clk->map, &tph->ph0);
> + meson_parm_write(clk->map, &tph->ph1, val);
> + meson_parm_write(clk->map, &tph->ph2, val);
> +}
> +
> +static int meson_clk_triphase_get_phase(struct clk_hw *hw)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
> + unsigned int val;
> +
> + /* Phase are in sync, reading phase 0 is enough */
> + val = meson_parm_read(clk->map, &tph->ph0);
> +
> + return meson_clk_degrees_from_val(val, tph->ph0.width);
> +}
> +
> +static int meson_clk_triphase_set_phase(struct clk_hw *hw, int degrees)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
> + unsigned int val;
> +
> + val = meson_clk_degrees_to_val(degrees, tph->ph0.width);
> + meson_parm_write(clk->map, &tph->ph0, val);
> + meson_parm_write(clk->map, &tph->ph1, val);
> + meson_parm_write(clk->map, &tph->ph2, val);
> +
> + return 0;
> +}
> +
> +const struct clk_ops meson_clk_triphase_ops = {
> + .init = meson_clk_triphase_sync,
> + .get_phase = meson_clk_triphase_get_phase,
> + .set_phase = meson_clk_triphase_set_phase,
> +};
> +EXPORT_SYMBOL_GPL(meson_clk_triphase_ops);
> diff --git a/drivers/clk/meson/clkc-audio.h b/drivers/clk/meson/clkc-audio.h
> new file mode 100644
> index 000000000000..286ff1201258
> --- /dev/null
> +++ b/drivers/clk/meson/clkc-audio.h
> @@ -0,0 +1,20 @@
> +/* SPDX-License-Identifier: GPL-2.0 */

// SPDX-License-Identifier: GPL-2.0

Checkpatch should have warned about this !

> +/*
> + * Copyright (c) 2018 BayLibre, SAS.
> + * Author: Jerome Brunet <[email protected]>
> + */
> +
> +#ifndef __MESON_CLKC_AUDIO_H
> +#define __MESON_CLKC_AUDIO_H
> +
> +#include "clkc.h"
> +
> +struct meson_clk_triphase_data {
> + struct parm ph0;
> + struct parm ph1;
> + struct parm ph2;
> +};
> +
> +extern const struct clk_ops meson_clk_triphase_ops;
> +
> +#endif /* __MESON_CLKC_AUDIO_H */
>

Apart that :

Acked-by: Neil Armstrong <[email protected]>

2018-04-26 08:49:43

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH 4/7] clk: meson: add axg audio sclk divider driver

On 25/04/2018 18:33, Jerome Brunet wrote:
> Add a driver to control the clock divider found in the sample clock
> generator of the axg audio clock controller.
>
> The sclk divider accumulates specific features which make the generic
> divider unsuitable to control it:
> - zero based divider (div = val + 1), but zero value gates the clock,
> so minimum divider value is 2.
> - lrclk variant may adjust the duty cycle depending the divider value
> and the 'hi' value.
>
> Signed-off-by: Jerome Brunet <[email protected]>
> ---
> drivers/clk/meson/Makefile | 2 +-
> drivers/clk/meson/clkc-audio.h | 8 ++
> drivers/clk/meson/sclk-div.c | 243 +++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 252 insertions(+), 1 deletion(-)
> create mode 100644 drivers/clk/meson/sclk-div.c
>
> diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
> index 64bb917fe1f0..f51b4754c31b 100644
> --- a/drivers/clk/meson/Makefile
> +++ b/drivers/clk/meson/Makefile
> @@ -4,7 +4,7 @@
>
> obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-audio-divider.o
> obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-phase.o
> -obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o
> +obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o sclk-div.o
> obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
> obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o
> obj-$(CONFIG_COMMON_CLK_AXG) += axg.o
> diff --git a/drivers/clk/meson/clkc-audio.h b/drivers/clk/meson/clkc-audio.h
> index 286ff1201258..0a7c157ebf81 100644
> --- a/drivers/clk/meson/clkc-audio.h
> +++ b/drivers/clk/meson/clkc-audio.h
> @@ -15,6 +15,14 @@ struct meson_clk_triphase_data {
> struct parm ph2;
> };
>
> +struct meson_sclk_div_data {
> + struct parm div;
> + struct parm hi;
> + unsigned int cached_div;
> + struct clk_duty cached_duty;
> +};
> +
> extern const struct clk_ops meson_clk_triphase_ops;
> +extern const struct clk_ops meson_sclk_div_ops;
>
> #endif /* __MESON_CLKC_AUDIO_H */
> diff --git a/drivers/clk/meson/sclk-div.c b/drivers/clk/meson/sclk-div.c
> new file mode 100644
> index 000000000000..8c0bc914a6d7
> --- /dev/null
> +++ b/drivers/clk/meson/sclk-div.c
> @@ -0,0 +1,243 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Copyright (c) 2018 BayLibre, SAS.
> + * Author: Jerome Brunet <[email protected]>
> + *
> + * Sample clock generator divider:
> + * This HW divider gates with value 0 but is otherwise a zero based divider:
> + *
> + * val >= 1
> + * divider = val + 1
> + *
> + * The duty cycle may also be set for the LR clock variant. The duty cycle
> + * ratio is:
> + *
> + * hi = [0 - val]
> + * duty_cycle = (1 + hi) / (1 + val)
> + */
> +
> +#include "clkc-audio.h"
> +
> +static inline struct meson_sclk_div_data *
> +meson_sclk_div_data(struct clk_regmap *clk)
> +{
> + return (struct meson_sclk_div_data *)clk->data;
> +}
> +
> +static int sclk_div_maxval(struct meson_sclk_div_data *sclk)
> +{
> + return (1 << sclk->div.width) - 1;
> +}
> +
> +static int sclk_div_maxdiv(struct meson_sclk_div_data *sclk)
> +{
> + return sclk_div_maxval(sclk) + 1;
> +}
> +
> +static int sclk_div_getdiv(struct clk_hw *hw, unsigned long rate,
> + unsigned long prate, int maxdiv)
> +{
> + int div = DIV_ROUND_CLOSEST_ULL((u64)prate, rate);
> +
> + return clamp(div, 2, maxdiv);
> +}
> +
> +static int sclk_div_bestdiv(struct clk_hw *hw, unsigned long rate,
> + unsigned long *prate,
> + struct meson_sclk_div_data *sclk)
> +{
> + struct clk_hw *parent = clk_hw_get_parent(hw);
> + int bestdiv = 0, i;
> + unsigned long maxdiv, now, parent_now;
> + unsigned long best = 0, best_parent = 0;
> +
> + if (!rate)
> + rate = 1;
> +
> + maxdiv = sclk_div_maxdiv(sclk);
> +
> + if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT))
> + return sclk_div_getdiv(hw, rate, *prate, maxdiv);
> +
> + /*
> + * The maximum divider we can use without overflowing
> + * unsigned long in rate * i below
> + */
> + maxdiv = min(ULONG_MAX / rate, maxdiv);
> +
> + for (i = 2; i <= maxdiv; i++) {
> + /*
> + * It's the most ideal case if the requested rate can be
> + * divided from parent clock without needing to change
> + * parent rate, so return the divider immediately.
> + */
> + if (rate * i == *prate)
> + return i;
> +
> + parent_now = clk_hw_round_rate(parent, rate * i);
> + now = DIV_ROUND_UP_ULL((u64)parent_now, i);
> +
> + if (abs(rate - now) < abs(rate - best)) {
> + bestdiv = i;
> + best = now;
> + best_parent = parent_now;
> + }
> + }
> +
> + if (!bestdiv)
> + bestdiv = sclk_div_maxdiv(sclk);
> + else
> + *prate = best_parent;
> +
> + return bestdiv;
> +}
> +
> +static long sclk_div_round_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long *prate)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
> + int div;
> +
> + div = sclk_div_bestdiv(hw, rate, prate, sclk);
> +
> + return DIV_ROUND_UP_ULL((u64)*prate, div);
> +}
> +
> +static void sclk_apply_ratio(struct clk_regmap *clk,
> + struct meson_sclk_div_data *sclk)
> +{
> + unsigned int hi = DIV_ROUND_CLOSEST(sclk->cached_div *
> + sclk->cached_duty.num,
> + sclk->cached_duty.den);
> +
> + if (hi)
> + hi -= 1;
> +
> + meson_parm_write(clk->map, &sclk->hi, hi);
> +}
> +
> +static int sclk_div_set_duty_cycle(struct clk_hw *hw,
> + struct clk_duty *duty)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
> +
> + if (MESON_PARM_APPLICABLE(&sclk->hi)) {
> + memcpy(&sclk->cached_duty, duty, sizeof(*duty));
> + sclk_apply_ratio(clk, sclk);
> + }
> +
> + return 0;
> +}
> +
> +static int sclk_div_get_duty_cycle(struct clk_hw *hw,
> + struct clk_duty *duty)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
> + int hi;
> +
> + if (!MESON_PARM_APPLICABLE(&sclk->hi)) {
> + duty->num = 1;
> + duty->den = 2;
> + return 0;
> + }
> +
> + hi = meson_parm_read(clk->map, &sclk->hi);
> + duty->num = hi + 1;
> + duty->den = sclk->cached_div;
> + return 0;
> +}
> +
> +static void sclk_apply_divider(struct clk_regmap *clk,
> + struct meson_sclk_div_data *sclk)
> +{
> + if (MESON_PARM_APPLICABLE(&sclk->hi))
> + sclk_apply_ratio(clk, sclk);
> +
> + meson_parm_write(clk->map, &sclk->div, sclk->cached_div - 1);
> +}
> +
> +static int sclk_div_set_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long prate)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
> + unsigned long maxdiv = sclk_div_maxdiv(sclk);
> +
> + sclk->cached_div = sclk_div_getdiv(hw, rate, prate, maxdiv);
> +
> + if (clk_hw_is_enabled(hw))
> + sclk_apply_divider(clk, sclk);
> +
> + return 0;
> +}
> +
> +static unsigned long sclk_div_recalc_rate(struct clk_hw *hw,
> + unsigned long prate)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
> +
> + return DIV_ROUND_UP_ULL((u64)prate, sclk->cached_div);
> +}
> +
> +static int sclk_div_enable(struct clk_hw *hw)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
> +
> + sclk_apply_divider(clk, sclk);
> +
> + return 0;
> +}
> +
> +static void sclk_div_disable(struct clk_hw *hw)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
> +
> + meson_parm_write(clk->map, &sclk->div, 0);
> +}
> +
> +static int sclk_div_is_enabled(struct clk_hw *hw)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
> +
> + if (meson_parm_read(clk->map, &sclk->div))
> + return 1;
> +
> + return 0;
> +}
> +
> +static void sclk_div_init(struct clk_hw *hw)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
> + unsigned int val;
> +
> + val = meson_parm_read(clk->map, &sclk->div);
> +
> + /* if the divider is initially disabled, assume max */
> + if (!val)
> + sclk->cached_div = sclk_div_maxdiv(sclk);
> + else
> + sclk->cached_div = val + 1;
> +
> + sclk_div_get_duty_cycle(hw, &sclk->cached_duty);
> +}
> +
> +const struct clk_ops meson_sclk_div_ops = {
> + .recalc_rate = sclk_div_recalc_rate,
> + .round_rate = sclk_div_round_rate,
> + .set_rate = sclk_div_set_rate,
> + .enable = sclk_div_enable,
> + .disable = sclk_div_disable,
> + .is_enabled = sclk_div_is_enabled,
> + .get_duty_cycle = sclk_div_get_duty_cycle,
> + .set_duty_cycle = sclk_div_set_duty_cycle,
> + .init = sclk_div_init,
> +};
> +EXPORT_SYMBOL_GPL(meson_sclk_div_ops);
>

Acked-by: Neil Armstrong <[email protected]>

2018-04-26 08:50:51

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH 5/7] clk: meson: axg: export audio clock controller id bindings

On 25/04/2018 18:33, Jerome Brunet wrote:
> export the clock ids dt-bindings usable by the consumers of the axg
> audio clock controller
>
> Signed-off-by: Jerome Brunet <[email protected]>
> ---
> include/dt-bindings/clock/axg-audio-clkc.h | 94 ++++++++++++++++++++++++++++++
> 1 file changed, 94 insertions(+)
> create mode 100644 include/dt-bindings/clock/axg-audio-clkc.h
>
> diff --git a/include/dt-bindings/clock/axg-audio-clkc.h b/include/dt-bindings/clock/axg-audio-clkc.h
> new file mode 100644
> index 000000000000..4426ae655858
> --- /dev/null
> +++ b/include/dt-bindings/clock/axg-audio-clkc.h
> @@ -0,0 +1,94 @@
> +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
> +/*
> + * Copyright (c) 2018 Baylibre SAS.
> + * Author: Jerome Brunet <[email protected]>
> + */
> +
> +#ifndef __AXG_AUDIO_CLKC_BINDINGS_H
> +#define __AXG_AUDIO_CLKC_BINDINGS_H
> +
> +#define AUD_CLKID_SLV_SCLK0 9
> +#define AUD_CLKID_SLV_SCLK1 10
> +#define AUD_CLKID_SLV_SCLK2 11
> +#define AUD_CLKID_SLV_SCLK3 12
> +#define AUD_CLKID_SLV_SCLK4 13
> +#define AUD_CLKID_SLV_SCLK5 14
> +#define AUD_CLKID_SLV_SCLK6 15
> +#define AUD_CLKID_SLV_SCLK7 16
> +#define AUD_CLKID_SLV_SCLK8 17
> +#define AUD_CLKID_SLV_SCLK9 18
> +#define AUD_CLKID_SLV_LRCLK0 19
> +#define AUD_CLKID_SLV_LRCLK1 20
> +#define AUD_CLKID_SLV_LRCLK2 21
> +#define AUD_CLKID_SLV_LRCLK3 22
> +#define AUD_CLKID_SLV_LRCLK4 23
> +#define AUD_CLKID_SLV_LRCLK5 24
> +#define AUD_CLKID_SLV_LRCLK6 25
> +#define AUD_CLKID_SLV_LRCLK7 26
> +#define AUD_CLKID_SLV_LRCLK8 27
> +#define AUD_CLKID_SLV_LRCLK9 28
> +#define AUD_CLKID_DDR_ARB 29
> +#define AUD_CLKID_PDM 30
> +#define AUD_CLKID_TDMIN_A 31
> +#define AUD_CLKID_TDMIN_B 32
> +#define AUD_CLKID_TDMIN_C 33
> +#define AUD_CLKID_TDMIN_LB 34
> +#define AUD_CLKID_TDMOUT_A 35
> +#define AUD_CLKID_TDMOUT_B 36
> +#define AUD_CLKID_TDMOUT_C 37
> +#define AUD_CLKID_FRDDR_A 38
> +#define AUD_CLKID_FRDDR_B 39
> +#define AUD_CLKID_FRDDR_C 40
> +#define AUD_CLKID_TODDR_A 41
> +#define AUD_CLKID_TODDR_B 42
> +#define AUD_CLKID_TODDR_C 43
> +#define AUD_CLKID_LOOPBACK 44
> +#define AUD_CLKID_SPDIFIN 45
> +#define AUD_CLKID_SPDIFOUT 46
> +#define AUD_CLKID_RESAMPLE 47
> +#define AUD_CLKID_POWER_DETECT 48
> +#define AUD_CLKID_MST_A_MCLK 49
> +#define AUD_CLKID_MST_B_MCLK 50
> +#define AUD_CLKID_MST_C_MCLK 51
> +#define AUD_CLKID_MST_D_MCLK 52
> +#define AUD_CLKID_MST_E_MCLK 53
> +#define AUD_CLKID_MST_F_MCLK 54
> +#define AUD_CLKID_SPDIFOUT_CLK 55
> +#define AUD_CLKID_SPDIFIN_CLK 56
> +#define AUD_CLKID_PDM_DCLK 57
> +#define AUD_CLKID_PDM_SYSCLK 58
> +#define AUD_CLKID_MST_A_SCLK 79
> +#define AUD_CLKID_MST_B_SCLK 80
> +#define AUD_CLKID_MST_C_SCLK 81
> +#define AUD_CLKID_MST_D_SCLK 82
> +#define AUD_CLKID_MST_E_SCLK 83
> +#define AUD_CLKID_MST_F_SCLK 84
> +#define AUD_CLKID_MST_A_LRCLK 86
> +#define AUD_CLKID_MST_B_LRCLK 87
> +#define AUD_CLKID_MST_C_LRCLK 88
> +#define AUD_CLKID_MST_D_LRCLK 89
> +#define AUD_CLKID_MST_E_LRCLK 90
> +#define AUD_CLKID_MST_F_LRCLK 91
> +#define AUD_CLKID_TDMIN_A_SCLK_SEL 116
> +#define AUD_CLKID_TDMIN_B_SCLK_SEL 117
> +#define AUD_CLKID_TDMIN_C_SCLK_SEL 118
> +#define AUD_CLKID_TDMIN_LB_SCLK_SEL 119
> +#define AUD_CLKID_TDMOUT_A_SCLK_SEL 120
> +#define AUD_CLKID_TDMOUT_B_SCLK_SEL 121
> +#define AUD_CLKID_TDMOUT_C_SCLK_SEL 122
> +#define AUD_CLKID_TDMIN_A_SCLK 123
> +#define AUD_CLKID_TDMIN_B_SCLK 124
> +#define AUD_CLKID_TDMIN_C_SCLK 125
> +#define AUD_CLKID_TDMIN_LB_SCLK 126
> +#define AUD_CLKID_TDMOUT_A_SCLK 127
> +#define AUD_CLKID_TDMOUT_B_SCLK 128
> +#define AUD_CLKID_TDMOUT_C_SCLK 129
> +#define AUD_CLKID_TDMIN_A_LRCLK 130
> +#define AUD_CLKID_TDMIN_B_LRCLK 131
> +#define AUD_CLKID_TDMIN_C_LRCLK 132
> +#define AUD_CLKID_TDMIN_LB_LRCLK 133
> +#define AUD_CLKID_TDMOUT_A_LRCLK 134
> +#define AUD_CLKID_TDMOUT_B_LRCLK 135
> +#define AUD_CLKID_TDMOUT_C_LRCLK 136
> +
> +#endif /* __AXG_AUDIO_CLKC_BINDINGS_H */
>

Acked-by: Neil Armstrong <[email protected]>

2018-04-26 08:51:31

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH 7/7] clk: meson: axg: add the audio clock controller driver

On 25/04/2018 18:33, Jerome Brunet wrote:
> The axg audio clock controller is the clock generation unit for the
> amlogic audio subsystem of A113 based SoCs. It may be clocked by 8
> different plls provided by the primary clock controller and also by
> 10 slave bit clocks and 10 slave sample clocks which may be provided
> by external components, such as audio codecs, through the SoC pads.
>
> It contains several muxes, dividers and gates which are fed into the
> the different devices of the audio subsystem.
>
> Signed-off-by: Jerome Brunet <[email protected]>
> ---
> drivers/clk/meson/Kconfig | 9 +
> drivers/clk/meson/Makefile | 1 +
> drivers/clk/meson/axg-audio.c | 845 ++++++++++++++++++++++++++++++++++++++++++
> drivers/clk/meson/axg-audio.h | 127 +++++++
> 4 files changed, 982 insertions(+)
> create mode 100644 drivers/clk/meson/axg-audio.c
> create mode 100644 drivers/clk/meson/axg-audio.h
>
> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
> index 7f7fd6fb3809..006fa1204e5b 100644
> --- a/drivers/clk/meson/Kconfig
> +++ b/drivers/clk/meson/Kconfig
> @@ -38,3 +38,12 @@ config COMMON_CLK_AXG
> help
> Support for the clock controller on AmLogic A113D devices, aka axg.
> Say Y if you want peripherals and CPU frequency scaling to work.
> +
> +config COMMON_CLK_AXG_AUDIO
> + tristate "Meson AXG Audio Clock Controller Driver"
> + depends on COMMON_CLK_AXG
> + select COMMON_CLK_AMLOGIC_AUDIO
> + select MFD_SYSCON
> + help
> + Support for the audio clock controller on AmLogic A113D devices,
> + aka axg, Say Y if you want audio subsystem to work.
> diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
> index f51b4754c31b..1ab89b627586 100644
> --- a/drivers/clk/meson/Makefile
> +++ b/drivers/clk/meson/Makefile
> @@ -8,4 +8,5 @@ obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o sclk-div.o
> obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
> obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o
> obj-$(CONFIG_COMMON_CLK_AXG) += axg.o
> +obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
> obj-$(CONFIG_COMMON_CLK_REGMAP_MESON) += clk-regmap.o
> diff --git a/drivers/clk/meson/axg-audio.c b/drivers/clk/meson/axg-audio.c
> new file mode 100644
> index 000000000000..830c13ccd819
> --- /dev/null
> +++ b/drivers/clk/meson/axg-audio.c
> @@ -0,0 +1,845 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Copyright (c) 2018 BayLibre, SAS.
> + * Author: Jerome Brunet <[email protected]>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/init.h>
> +#include <linux/of_device.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <linux/slab.h>
> +
> +#include "clkc-audio.h"
> +#include "axg-audio.h"
> +
> +#define AXG_MST_IN_COUNT 8
> +#define AXG_SLV_SCLK_COUNT 10
> +#define AXG_SLV_LRCLK_COUNT 10
> +
> +#define AXG_AUD_GATE(_name, _reg, _bit, _pname, _iflags) \
> +struct clk_regmap axg_##_name = { \
> + .data = &(struct clk_regmap_gate_data){ \
> + .offset = (_reg), \
> + .bit_idx = (_bit), \
> + }, \
> + .hw.init = &(struct clk_init_data) { \
> + .name = "axg_"#_name, \
> + .ops = &clk_regmap_gate_ops, \
> + .parent_names = (const char *[]){ _pname }, \
> + .num_parents = 1, \
> + .flags = CLK_DUTY_CYCLE_PARENT | (_iflags), \
> + }, \
> +}
> +
> +#define AXG_AUD_MUX(_name, _reg, _mask, _shift, _dflags, _pnames, _iflags) \
> +struct clk_regmap axg_##_name = { \
> + .data = &(struct clk_regmap_mux_data){ \
> + .offset = (_reg), \
> + .mask = (_mask), \
> + .shift = (_shift), \
> + .flags = (_dflags), \
> + }, \
> + .hw.init = &(struct clk_init_data){ \
> + .name = "axg_"#_name, \
> + .ops = &clk_regmap_mux_ops, \
> + .parent_names = (_pnames), \
> + .num_parents = ARRAY_SIZE(_pnames), \
> + .flags = CLK_DUTY_CYCLE_PARENT | (_iflags), \
> + }, \
> +}
> +
> +#define AXG_AUD_DIV(_name, _reg, _shift, _width, _dflags, _pname, _iflags) \
> +struct clk_regmap axg_##_name = { \
> + .data = &(struct clk_regmap_div_data){ \
> + .offset = (_reg), \
> + .shift = (_shift), \
> + .width = (_width), \
> + .flags = (_dflags), \
> + }, \
> + .hw.init = &(struct clk_init_data){ \
> + .name = "axg_"#_name, \
> + .ops = &clk_regmap_divider_ops, \
> + .parent_names = (const char *[]) { _pname }, \
> + .num_parents = 1, \
> + .flags = (_iflags), \
> + }, \
> +}
> +
> +#define AXG_PCLK_GATE(_name, _bit) \
> + AXG_AUD_GATE(_name, AUDIO_CLK_GATE_EN, _bit, "axg_audio_pclk", 0)
> +
> +/* Audio peripheral clocks */
> +static AXG_PCLK_GATE(ddr_arb, 0);
> +static AXG_PCLK_GATE(pdm, 1);
> +static AXG_PCLK_GATE(tdmin_a, 2);
> +static AXG_PCLK_GATE(tdmin_b, 3);
> +static AXG_PCLK_GATE(tdmin_c, 4);
> +static AXG_PCLK_GATE(tdmin_lb, 5);
> +static AXG_PCLK_GATE(tdmout_a, 6);
> +static AXG_PCLK_GATE(tdmout_b, 7);
> +static AXG_PCLK_GATE(tdmout_c, 8);
> +static AXG_PCLK_GATE(frddr_a, 9);
> +static AXG_PCLK_GATE(frddr_b, 10);
> +static AXG_PCLK_GATE(frddr_c, 11);
> +static AXG_PCLK_GATE(toddr_a, 12);
> +static AXG_PCLK_GATE(toddr_b, 13);
> +static AXG_PCLK_GATE(toddr_c, 14);
> +static AXG_PCLK_GATE(loopback, 15);
> +static AXG_PCLK_GATE(spdifin, 16);
> +static AXG_PCLK_GATE(spdifout, 17);
> +static AXG_PCLK_GATE(resample, 18);
> +static AXG_PCLK_GATE(power_detect, 19);
> +
> +/* Audio Master Clocks */
> +static const char * const mst_mux_parent_names[] = {
> + "axg_mst_in0", "axg_mst_in1", "axg_mst_in2", "axg_mst_in3",
> + "axg_mst_in4", "axg_mst_in5", "axg_mst_in6", "axg_mst_in7",
> +};
> +
> +#define AXG_MST_MCLK_MUX(_name, _reg) \
> + AXG_AUD_MUX(_name##_sel, _reg, 0x7, 24, CLK_MUX_ROUND_CLOSEST, \
> + mst_mux_parent_names, CLK_SET_RATE_PARENT)
> +
> +static AXG_MST_MCLK_MUX(mst_a_mclk, AUDIO_MCLK_A_CTRL);
> +static AXG_MST_MCLK_MUX(mst_b_mclk, AUDIO_MCLK_B_CTRL);
> +static AXG_MST_MCLK_MUX(mst_c_mclk, AUDIO_MCLK_C_CTRL);
> +static AXG_MST_MCLK_MUX(mst_d_mclk, AUDIO_MCLK_D_CTRL);
> +static AXG_MST_MCLK_MUX(mst_e_mclk, AUDIO_MCLK_E_CTRL);
> +static AXG_MST_MCLK_MUX(mst_f_mclk, AUDIO_MCLK_F_CTRL);
> +static AXG_MST_MCLK_MUX(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL);
> +static AXG_MST_MCLK_MUX(spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL);
> +static AXG_MST_MCLK_MUX(pdm_dclk, AUDIO_CLK_PDMIN_CTRL0);
> +static AXG_MST_MCLK_MUX(pdm_sysclk, AUDIO_CLK_PDMIN_CTRL1);
> +
> +#define AXG_MST_MCLK_DIV(_name, _reg) \
> + AXG_AUD_DIV(_name##_div, _reg, 0, 16, CLK_DIVIDER_ROUND_CLOSEST, \
> + "axg_"#_name"_sel", CLK_SET_RATE_PARENT) \
> +
> +static AXG_MST_MCLK_DIV(mst_a_mclk, AUDIO_MCLK_A_CTRL);
> +static AXG_MST_MCLK_DIV(mst_b_mclk, AUDIO_MCLK_B_CTRL);
> +static AXG_MST_MCLK_DIV(mst_c_mclk, AUDIO_MCLK_C_CTRL);
> +static AXG_MST_MCLK_DIV(mst_d_mclk, AUDIO_MCLK_D_CTRL);
> +static AXG_MST_MCLK_DIV(mst_e_mclk, AUDIO_MCLK_E_CTRL);
> +static AXG_MST_MCLK_DIV(mst_f_mclk, AUDIO_MCLK_F_CTRL);
> +static AXG_MST_MCLK_DIV(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL);
> +static AXG_MST_MCLK_DIV(spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL);
> +static AXG_MST_MCLK_DIV(pdm_dclk, AUDIO_CLK_PDMIN_CTRL0);
> +static AXG_MST_MCLK_DIV(pdm_sysclk, AUDIO_CLK_PDMIN_CTRL1);
> +
> +#define AXG_MST_MCLK_GATE(_name, _reg) \
> + AXG_AUD_GATE(_name, _reg, 31, "axg_"#_name"_div", \
> + CLK_SET_RATE_PARENT)
> +
> +static AXG_MST_MCLK_GATE(mst_a_mclk, AUDIO_MCLK_A_CTRL);
> +static AXG_MST_MCLK_GATE(mst_b_mclk, AUDIO_MCLK_B_CTRL);
> +static AXG_MST_MCLK_GATE(mst_c_mclk, AUDIO_MCLK_C_CTRL);
> +static AXG_MST_MCLK_GATE(mst_d_mclk, AUDIO_MCLK_D_CTRL);
> +static AXG_MST_MCLK_GATE(mst_e_mclk, AUDIO_MCLK_E_CTRL);
> +static AXG_MST_MCLK_GATE(mst_f_mclk, AUDIO_MCLK_F_CTRL);
> +static AXG_MST_MCLK_GATE(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL);
> +static AXG_MST_MCLK_GATE(spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL);
> +static AXG_MST_MCLK_GATE(pdm_dclk, AUDIO_CLK_PDMIN_CTRL0);
> +static AXG_MST_MCLK_GATE(pdm_sysclk, AUDIO_CLK_PDMIN_CTRL1);
> +
> +/* Sample Clocks */
> +#define AXG_MST_SCLK_PRE_EN(_name, _reg) \
> + AXG_AUD_GATE(mst_##_name##_sclk_pre_en, _reg, 31, \
> + "axg_mst_"#_name"_mclk", 0)
> +
> +static AXG_MST_SCLK_PRE_EN(a, AUDIO_MST_A_SCLK_CTRL0);
> +static AXG_MST_SCLK_PRE_EN(b, AUDIO_MST_B_SCLK_CTRL0);
> +static AXG_MST_SCLK_PRE_EN(c, AUDIO_MST_C_SCLK_CTRL0);
> +static AXG_MST_SCLK_PRE_EN(d, AUDIO_MST_D_SCLK_CTRL0);
> +static AXG_MST_SCLK_PRE_EN(e, AUDIO_MST_E_SCLK_CTRL0);
> +static AXG_MST_SCLK_PRE_EN(f, AUDIO_MST_F_SCLK_CTRL0);
> +
> +#define AXG_AUD_SCLK_DIV(_name, _reg, _div_shift, _div_width, \
> + _hi_shift, _hi_width, _pname, _iflags) \
> +struct clk_regmap axg_##_name = { \
> + .data = &(struct meson_sclk_div_data) { \
> + .div = { \
> + .reg_off = (_reg), \
> + .shift = (_div_shift), \
> + .width = (_div_width), \
> + }, \
> + .hi = { \
> + .reg_off = (_reg), \
> + .shift = (_hi_shift), \
> + .width = (_hi_width), \
> + }, \
> + }, \
> + .hw.init = &(struct clk_init_data) { \
> + .name = "axg_"#_name, \
> + .ops = &meson_sclk_div_ops, \
> + .parent_names = (const char *[]) { _pname }, \
> + .num_parents = 1, \
> + .flags = (_iflags), \
> + }, \
> +}
> +
> +#define AXG_MST_SCLK_DIV(_name, _reg) \
> + AXG_AUD_SCLK_DIV(mst_##_name##_sclk_div, _reg, 20, 10, 0, 0, \
> + "axg_mst_"#_name"_sclk_pre_en", \
> + CLK_SET_RATE_PARENT)
> +
> +static AXG_MST_SCLK_DIV(a, AUDIO_MST_A_SCLK_CTRL0);
> +static AXG_MST_SCLK_DIV(b, AUDIO_MST_B_SCLK_CTRL0);
> +static AXG_MST_SCLK_DIV(c, AUDIO_MST_C_SCLK_CTRL0);
> +static AXG_MST_SCLK_DIV(d, AUDIO_MST_D_SCLK_CTRL0);
> +static AXG_MST_SCLK_DIV(e, AUDIO_MST_E_SCLK_CTRL0);
> +static AXG_MST_SCLK_DIV(f, AUDIO_MST_F_SCLK_CTRL0);
> +
> +#define AXG_MST_SCLK_POST_EN(_name, _reg) \
> + AXG_AUD_GATE(mst_##_name##_sclk_post_en, _reg, 30, \
> + "axg_mst_"#_name"_sclk_div", CLK_SET_RATE_PARENT)
> +
> +static AXG_MST_SCLK_POST_EN(a, AUDIO_MST_A_SCLK_CTRL0);
> +static AXG_MST_SCLK_POST_EN(b, AUDIO_MST_B_SCLK_CTRL0);
> +static AXG_MST_SCLK_POST_EN(c, AUDIO_MST_C_SCLK_CTRL0);
> +static AXG_MST_SCLK_POST_EN(d, AUDIO_MST_D_SCLK_CTRL0);
> +static AXG_MST_SCLK_POST_EN(e, AUDIO_MST_E_SCLK_CTRL0);
> +static AXG_MST_SCLK_POST_EN(f, AUDIO_MST_F_SCLK_CTRL0);
> +
> +#define AXG_AUD_TRIPHASE(_name, _reg, _width, _shift0, _shift1, _shift2, \
> + _pname, _iflags) \
> +struct clk_regmap axg_##_name = { \
> + .data = &(struct meson_clk_triphase_data) { \
> + .ph0 = { \
> + .reg_off = (_reg), \
> + .shift = (_shift0), \
> + .width = (_width), \
> + }, \
> + .ph1 = { \
> + .reg_off = (_reg), \
> + .shift = (_shift1), \
> + .width = (_width), \
> + }, \
> + .ph2 = { \
> + .reg_off = (_reg), \
> + .shift = (_shift2), \
> + .width = (_width), \
> + }, \
> + }, \
> + .hw.init = &(struct clk_init_data) { \
> + .name = "axg_"#_name, \
> + .ops = &meson_clk_triphase_ops, \
> + .parent_names = (const char *[]) { _pname }, \
> + .num_parents = 1, \
> + .flags = CLK_DUTY_CYCLE_PARENT | (_iflags), \
> + }, \
> +}
> +
> +#define AXG_MST_SCLK(_name, _reg) \
> + AXG_AUD_TRIPHASE(mst_##_name##_sclk, _reg, 1, 0, 2, 4, \
> + "axg_mst_"#_name"_sclk_post_en", CLK_SET_RATE_PARENT)
> +
> +static AXG_MST_SCLK(a, AUDIO_MST_A_SCLK_CTRL1);
> +static AXG_MST_SCLK(b, AUDIO_MST_B_SCLK_CTRL1);
> +static AXG_MST_SCLK(c, AUDIO_MST_C_SCLK_CTRL1);
> +static AXG_MST_SCLK(d, AUDIO_MST_D_SCLK_CTRL1);
> +static AXG_MST_SCLK(e, AUDIO_MST_E_SCLK_CTRL1);
> +static AXG_MST_SCLK(f, AUDIO_MST_F_SCLK_CTRL1);
> +
> +#define AXG_MST_LRCLK_DIV(_name, _reg) \
> + AXG_AUD_SCLK_DIV(mst_##_name##_lrclk_div, _reg, 0, 10, 10, 10, \
> + "axg_mst_"#_name"_sclk_post_en", 0) \
> +
> +static AXG_MST_LRCLK_DIV(a, AUDIO_MST_A_SCLK_CTRL0);
> +static AXG_MST_LRCLK_DIV(b, AUDIO_MST_B_SCLK_CTRL0);
> +static AXG_MST_LRCLK_DIV(c, AUDIO_MST_C_SCLK_CTRL0);
> +static AXG_MST_LRCLK_DIV(d, AUDIO_MST_D_SCLK_CTRL0);
> +static AXG_MST_LRCLK_DIV(e, AUDIO_MST_E_SCLK_CTRL0);
> +static AXG_MST_LRCLK_DIV(f, AUDIO_MST_F_SCLK_CTRL0);
> +
> +#define AXG_MST_LRCLK(_name, _reg) \
> + AXG_AUD_TRIPHASE(mst_##_name##_lrclk, _reg, 1, 1, 3, 5, \
> + "axg_mst_"#_name"_lrclk_div", CLK_SET_RATE_PARENT)
> +
> +static AXG_MST_LRCLK(a, AUDIO_MST_A_SCLK_CTRL1);
> +static AXG_MST_LRCLK(b, AUDIO_MST_B_SCLK_CTRL1);
> +static AXG_MST_LRCLK(c, AUDIO_MST_C_SCLK_CTRL1);
> +static AXG_MST_LRCLK(d, AUDIO_MST_D_SCLK_CTRL1);
> +static AXG_MST_LRCLK(e, AUDIO_MST_E_SCLK_CTRL1);
> +static AXG_MST_LRCLK(f, AUDIO_MST_F_SCLK_CTRL1);
> +
> +static const char * const tdm_sclk_parent_names[] = {
> + "axg_mst_a_sclk", "axg_mst_b_sclk", "axg_mst_c_sclk",
> + "axg_mst_d_sclk", "axg_mst_e_sclk", "axg_mst_f_sclk",
> + "axg_slv_sclk0", "axg_slv_sclk1", "axg_slv_sclk2",
> + "axg_slv_sclk3", "axg_slv_sclk4", "axg_slv_sclk5",
> + "axg_slv_sclk6", "axg_slv_sclk7", "axg_slv_sclk8",
> + "axg_slv_sclk9"
> +};
> +
> +#define AXG_TDM_SCLK_MUX(_name, _reg) \
> + AXG_AUD_MUX(tdm##_name##_sclk_sel, _reg, 0xf, 24, \
> + CLK_MUX_ROUND_CLOSEST, \
> + tdm_sclk_parent_names, 0)
> +
> +static AXG_TDM_SCLK_MUX(in_a, AUDIO_CLK_TDMIN_A_CTRL);
> +static AXG_TDM_SCLK_MUX(in_b, AUDIO_CLK_TDMIN_B_CTRL);
> +static AXG_TDM_SCLK_MUX(in_c, AUDIO_CLK_TDMIN_C_CTRL);
> +static AXG_TDM_SCLK_MUX(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
> +static AXG_TDM_SCLK_MUX(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
> +static AXG_TDM_SCLK_MUX(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
> +static AXG_TDM_SCLK_MUX(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
> +
> +#define AXG_TDM_SCLK_PRE_EN(_name, _reg) \
> + AXG_AUD_GATE(tdm##_name##_sclk_pre_en, _reg, 31, \
> + "axg_tdm"#_name"_sclk_sel", CLK_SET_RATE_PARENT)
> +
> +static AXG_TDM_SCLK_PRE_EN(in_a, AUDIO_CLK_TDMIN_A_CTRL);
> +static AXG_TDM_SCLK_PRE_EN(in_b, AUDIO_CLK_TDMIN_B_CTRL);
> +static AXG_TDM_SCLK_PRE_EN(in_c, AUDIO_CLK_TDMIN_C_CTRL);
> +static AXG_TDM_SCLK_PRE_EN(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
> +static AXG_TDM_SCLK_PRE_EN(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
> +static AXG_TDM_SCLK_PRE_EN(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
> +static AXG_TDM_SCLK_PRE_EN(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
> +
> +#define AXG_TDM_SCLK_POST_EN(_name, _reg) \
> + AXG_AUD_GATE(tdm##_name##_sclk_post_en, _reg, 30, \
> + "axg_tdm"#_name"_sclk_pre_en", CLK_SET_RATE_PARENT)
> +
> +static AXG_TDM_SCLK_POST_EN(in_a, AUDIO_CLK_TDMIN_A_CTRL);
> +static AXG_TDM_SCLK_POST_EN(in_b, AUDIO_CLK_TDMIN_B_CTRL);
> +static AXG_TDM_SCLK_POST_EN(in_c, AUDIO_CLK_TDMIN_C_CTRL);
> +static AXG_TDM_SCLK_POST_EN(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
> +static AXG_TDM_SCLK_POST_EN(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
> +static AXG_TDM_SCLK_POST_EN(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
> +static AXG_TDM_SCLK_POST_EN(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
> +
> +#define AXG_TDM_SCLK(_name, _reg) \
> + struct clk_regmap axg_tdm##_name##_sclk = { \
> + .data = &(struct meson_clk_phase_data) { \
> + .ph = { \
> + .reg_off = (_reg), \
> + .shift = 29, \
> + .width = 1, \
> + }, \
> + }, \
> + .hw.init = &(struct clk_init_data) { \
> + .name = "axg_tdm"#_name"_sclk", \
> + .ops = &meson_clk_phase_ops, \
> + .parent_names = (const char *[]) \
> + { "axg_tdm"#_name"_sclk_post_en" }, \
> + .num_parents = 1, \
> + .flags = CLK_DUTY_CYCLE_PARENT | CLK_SET_RATE_PARENT, \
> + }, \
> +}
> +
> +static AXG_TDM_SCLK(in_a, AUDIO_CLK_TDMIN_A_CTRL);
> +static AXG_TDM_SCLK(in_b, AUDIO_CLK_TDMIN_B_CTRL);
> +static AXG_TDM_SCLK(in_c, AUDIO_CLK_TDMIN_C_CTRL);
> +static AXG_TDM_SCLK(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
> +static AXG_TDM_SCLK(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
> +static AXG_TDM_SCLK(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
> +static AXG_TDM_SCLK(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
> +
> +static const char * const tdm_lrclk_parent_names[] = {
> + "axg_mst_a_lrclk", "axg_mst_b_lrclk", "axg_mst_c_lrclk",
> + "axg_mst_d_lrclk", "axg_mst_e_lrclk", "axg_mst_f_lrclk",
> + "axg_slv_lrclk0", "axg_slv_lrclk1", "axg_slv_lrclk2",
> + "axg_slv_lrclk3", "axg_slv_lrclk4", "axg_slv_lrclk5",
> + "axg_slv_lrclk6", "axg_slv_lrclk7", "axg_slv_lrclk8",
> + "axg_slv_lrclk9"
> +};
> +
> +#define AXG_TDM_LRLCK(_name, _reg) \
> + AXG_AUD_MUX(tdm##_name##_lrclk, _reg, 0xf, 20, \
> + CLK_MUX_ROUND_CLOSEST, \
> + tdm_lrclk_parent_names, 0)
> +
> +static AXG_TDM_LRLCK(in_a, AUDIO_CLK_TDMIN_A_CTRL);
> +static AXG_TDM_LRLCK(in_b, AUDIO_CLK_TDMIN_B_CTRL);
> +static AXG_TDM_LRLCK(in_c, AUDIO_CLK_TDMIN_C_CTRL);
> +static AXG_TDM_LRLCK(in_lb, AUDIO_CLK_TDMIN_LB_CTRL);
> +static AXG_TDM_LRLCK(out_a, AUDIO_CLK_TDMOUT_A_CTRL);
> +static AXG_TDM_LRLCK(out_b, AUDIO_CLK_TDMOUT_B_CTRL);
> +static AXG_TDM_LRLCK(out_c, AUDIO_CLK_TDMOUT_C_CTRL);
> +
> +/*
> + * Array of all clocks provided by this provider
> + * The input clocks of the controller will be populated at runtime
> + */
> +static struct clk_hw_onecell_data axg_audio_hw_onecell_data = {
> + .hws = {
> + [AUD_CLKID_DDR_ARB] = &axg_ddr_arb.hw,
> + [AUD_CLKID_PDM] = &axg_pdm.hw,
> + [AUD_CLKID_TDMIN_A] = &axg_tdmin_a.hw,
> + [AUD_CLKID_TDMIN_B] = &axg_tdmin_b.hw,
> + [AUD_CLKID_TDMIN_C] = &axg_tdmin_c.hw,
> + [AUD_CLKID_TDMIN_LB] = &axg_tdmin_lb.hw,
> + [AUD_CLKID_TDMOUT_A] = &axg_tdmout_a.hw,
> + [AUD_CLKID_TDMOUT_B] = &axg_tdmout_b.hw,
> + [AUD_CLKID_TDMOUT_C] = &axg_tdmout_c.hw,
> + [AUD_CLKID_FRDDR_A] = &axg_frddr_a.hw,
> + [AUD_CLKID_FRDDR_B] = &axg_frddr_b.hw,
> + [AUD_CLKID_FRDDR_C] = &axg_frddr_c.hw,
> + [AUD_CLKID_TODDR_A] = &axg_toddr_a.hw,
> + [AUD_CLKID_TODDR_B] = &axg_toddr_b.hw,
> + [AUD_CLKID_TODDR_C] = &axg_toddr_c.hw,
> + [AUD_CLKID_LOOPBACK] = &axg_loopback.hw,
> + [AUD_CLKID_SPDIFIN] = &axg_spdifin.hw,
> + [AUD_CLKID_SPDIFOUT] = &axg_spdifout.hw,
> + [AUD_CLKID_RESAMPLE] = &axg_resample.hw,
> + [AUD_CLKID_POWER_DETECT] = &axg_power_detect.hw,
> + [AUD_CLKID_MST_A_MCLK_SEL] = &axg_mst_a_mclk_sel.hw,
> + [AUD_CLKID_MST_B_MCLK_SEL] = &axg_mst_b_mclk_sel.hw,
> + [AUD_CLKID_MST_C_MCLK_SEL] = &axg_mst_c_mclk_sel.hw,
> + [AUD_CLKID_MST_D_MCLK_SEL] = &axg_mst_d_mclk_sel.hw,
> + [AUD_CLKID_MST_E_MCLK_SEL] = &axg_mst_e_mclk_sel.hw,
> + [AUD_CLKID_MST_F_MCLK_SEL] = &axg_mst_f_mclk_sel.hw,
> + [AUD_CLKID_MST_A_MCLK_DIV] = &axg_mst_a_mclk_div.hw,
> + [AUD_CLKID_MST_B_MCLK_DIV] = &axg_mst_b_mclk_div.hw,
> + [AUD_CLKID_MST_C_MCLK_DIV] = &axg_mst_c_mclk_div.hw,
> + [AUD_CLKID_MST_D_MCLK_DIV] = &axg_mst_d_mclk_div.hw,
> + [AUD_CLKID_MST_E_MCLK_DIV] = &axg_mst_e_mclk_div.hw,
> + [AUD_CLKID_MST_F_MCLK_DIV] = &axg_mst_f_mclk_div.hw,
> + [AUD_CLKID_MST_A_MCLK] = &axg_mst_a_mclk.hw,
> + [AUD_CLKID_MST_B_MCLK] = &axg_mst_b_mclk.hw,
> + [AUD_CLKID_MST_C_MCLK] = &axg_mst_c_mclk.hw,
> + [AUD_CLKID_MST_D_MCLK] = &axg_mst_d_mclk.hw,
> + [AUD_CLKID_MST_E_MCLK] = &axg_mst_e_mclk.hw,
> + [AUD_CLKID_MST_F_MCLK] = &axg_mst_f_mclk.hw,
> + [AUD_CLKID_SPDIFOUT_CLK_SEL] = &axg_spdifout_clk_sel.hw,
> + [AUD_CLKID_SPDIFOUT_CLK_DIV] = &axg_spdifout_clk_div.hw,
> + [AUD_CLKID_SPDIFOUT_CLK] = &axg_spdifout_clk.hw,
> + [AUD_CLKID_SPDIFIN_CLK_SEL] = &axg_spdifin_clk_sel.hw,
> + [AUD_CLKID_SPDIFIN_CLK_DIV] = &axg_spdifin_clk_div.hw,
> + [AUD_CLKID_SPDIFIN_CLK] = &axg_spdifin_clk.hw,
> + [AUD_CLKID_PDM_DCLK_SEL] = &axg_pdm_dclk_sel.hw,
> + [AUD_CLKID_PDM_DCLK_DIV] = &axg_pdm_dclk_div.hw,
> + [AUD_CLKID_PDM_DCLK] = &axg_pdm_dclk.hw,
> + [AUD_CLKID_PDM_SYSCLK_SEL] = &axg_pdm_sysclk_sel.hw,
> + [AUD_CLKID_PDM_SYSCLK_DIV] = &axg_pdm_sysclk_div.hw,
> + [AUD_CLKID_PDM_SYSCLK] = &axg_pdm_sysclk.hw,
> + [AUD_CLKID_MST_A_SCLK_PRE_EN] = &axg_mst_a_sclk_pre_en.hw,
> + [AUD_CLKID_MST_B_SCLK_PRE_EN] = &axg_mst_b_sclk_pre_en.hw,
> + [AUD_CLKID_MST_C_SCLK_PRE_EN] = &axg_mst_c_sclk_pre_en.hw,
> + [AUD_CLKID_MST_D_SCLK_PRE_EN] = &axg_mst_d_sclk_pre_en.hw,
> + [AUD_CLKID_MST_E_SCLK_PRE_EN] = &axg_mst_e_sclk_pre_en.hw,
> + [AUD_CLKID_MST_F_SCLK_PRE_EN] = &axg_mst_f_sclk_pre_en.hw,
> + [AUD_CLKID_MST_A_SCLK_DIV] = &axg_mst_a_sclk_div.hw,
> + [AUD_CLKID_MST_B_SCLK_DIV] = &axg_mst_b_sclk_div.hw,
> + [AUD_CLKID_MST_C_SCLK_DIV] = &axg_mst_c_sclk_div.hw,
> + [AUD_CLKID_MST_D_SCLK_DIV] = &axg_mst_d_sclk_div.hw,
> + [AUD_CLKID_MST_E_SCLK_DIV] = &axg_mst_e_sclk_div.hw,
> + [AUD_CLKID_MST_F_SCLK_DIV] = &axg_mst_f_sclk_div.hw,
> + [AUD_CLKID_MST_A_SCLK_POST_EN] = &axg_mst_a_sclk_post_en.hw,
> + [AUD_CLKID_MST_B_SCLK_POST_EN] = &axg_mst_b_sclk_post_en.hw,
> + [AUD_CLKID_MST_C_SCLK_POST_EN] = &axg_mst_c_sclk_post_en.hw,
> + [AUD_CLKID_MST_D_SCLK_POST_EN] = &axg_mst_d_sclk_post_en.hw,
> + [AUD_CLKID_MST_E_SCLK_POST_EN] = &axg_mst_e_sclk_post_en.hw,
> + [AUD_CLKID_MST_F_SCLK_POST_EN] = &axg_mst_f_sclk_post_en.hw,
> + [AUD_CLKID_MST_A_SCLK] = &axg_mst_a_sclk.hw,
> + [AUD_CLKID_MST_B_SCLK] = &axg_mst_b_sclk.hw,
> + [AUD_CLKID_MST_C_SCLK] = &axg_mst_c_sclk.hw,
> + [AUD_CLKID_MST_D_SCLK] = &axg_mst_d_sclk.hw,
> + [AUD_CLKID_MST_E_SCLK] = &axg_mst_e_sclk.hw,
> + [AUD_CLKID_MST_F_SCLK] = &axg_mst_f_sclk.hw,
> + [AUD_CLKID_MST_A_LRCLK_DIV] = &axg_mst_a_lrclk_div.hw,
> + [AUD_CLKID_MST_B_LRCLK_DIV] = &axg_mst_b_lrclk_div.hw,
> + [AUD_CLKID_MST_C_LRCLK_DIV] = &axg_mst_c_lrclk_div.hw,
> + [AUD_CLKID_MST_D_LRCLK_DIV] = &axg_mst_d_lrclk_div.hw,
> + [AUD_CLKID_MST_E_LRCLK_DIV] = &axg_mst_e_lrclk_div.hw,
> + [AUD_CLKID_MST_F_LRCLK_DIV] = &axg_mst_f_lrclk_div.hw,
> + [AUD_CLKID_MST_A_LRCLK] = &axg_mst_a_lrclk.hw,
> + [AUD_CLKID_MST_B_LRCLK] = &axg_mst_b_lrclk.hw,
> + [AUD_CLKID_MST_C_LRCLK] = &axg_mst_c_lrclk.hw,
> + [AUD_CLKID_MST_D_LRCLK] = &axg_mst_d_lrclk.hw,
> + [AUD_CLKID_MST_E_LRCLK] = &axg_mst_e_lrclk.hw,
> + [AUD_CLKID_MST_F_LRCLK] = &axg_mst_f_lrclk.hw,
> + [AUD_CLKID_TDMIN_A_SCLK_SEL] = &axg_tdmin_a_sclk_sel.hw,
> + [AUD_CLKID_TDMIN_B_SCLK_SEL] = &axg_tdmin_b_sclk_sel.hw,
> + [AUD_CLKID_TDMIN_C_SCLK_SEL] = &axg_tdmin_c_sclk_sel.hw,
> + [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &axg_tdmin_lb_sclk_sel.hw,
> + [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &axg_tdmout_a_sclk_sel.hw,
> + [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &axg_tdmout_b_sclk_sel.hw,
> + [AUD_CLKID_TDMOUT_C_SCLK_SEL] = &axg_tdmout_c_sclk_sel.hw,
> + [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &axg_tdmin_a_sclk_pre_en.hw,
> + [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &axg_tdmin_b_sclk_pre_en.hw,
> + [AUD_CLKID_TDMIN_C_SCLK_PRE_EN] = &axg_tdmin_c_sclk_pre_en.hw,
> + [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &axg_tdmin_lb_sclk_pre_en.hw,
> + [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &axg_tdmout_a_sclk_pre_en.hw,
> + [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &axg_tdmout_b_sclk_pre_en.hw,
> + [AUD_CLKID_TDMOUT_C_SCLK_PRE_EN] = &axg_tdmout_c_sclk_pre_en.hw,
> + [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &axg_tdmin_a_sclk_post_en.hw,
> + [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &axg_tdmin_b_sclk_post_en.hw,
> + [AUD_CLKID_TDMIN_C_SCLK_POST_EN] = &axg_tdmin_c_sclk_post_en.hw,
> + [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &axg_tdmin_lb_sclk_post_en.hw,
> + [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &axg_tdmout_a_sclk_post_en.hw,
> + [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &axg_tdmout_b_sclk_post_en.hw,
> + [AUD_CLKID_TDMOUT_C_SCLK_POST_EN] = &axg_tdmout_c_sclk_post_en.hw,
> + [AUD_CLKID_TDMIN_A_SCLK] = &axg_tdmin_a_sclk.hw,
> + [AUD_CLKID_TDMIN_B_SCLK] = &axg_tdmin_b_sclk.hw,
> + [AUD_CLKID_TDMIN_C_SCLK] = &axg_tdmin_c_sclk.hw,
> + [AUD_CLKID_TDMIN_LB_SCLK] = &axg_tdmin_lb_sclk.hw,
> + [AUD_CLKID_TDMOUT_A_SCLK] = &axg_tdmout_a_sclk.hw,
> + [AUD_CLKID_TDMOUT_B_SCLK] = &axg_tdmout_b_sclk.hw,
> + [AUD_CLKID_TDMOUT_C_SCLK] = &axg_tdmout_c_sclk.hw,
> + [AUD_CLKID_TDMIN_A_LRCLK] = &axg_tdmin_a_lrclk.hw,
> + [AUD_CLKID_TDMIN_B_LRCLK] = &axg_tdmin_b_lrclk.hw,
> + [AUD_CLKID_TDMIN_C_LRCLK] = &axg_tdmin_c_lrclk.hw,
> + [AUD_CLKID_TDMIN_LB_LRCLK] = &axg_tdmin_lb_lrclk.hw,
> + [AUD_CLKID_TDMOUT_A_LRCLK] = &axg_tdmout_a_lrclk.hw,
> + [AUD_CLKID_TDMOUT_B_LRCLK] = &axg_tdmout_b_lrclk.hw,
> + [AUD_CLKID_TDMOUT_C_LRCLK] = &axg_tdmout_c_lrclk.hw,
> + [NR_CLKS] = NULL,
> + },
> + .num = NR_CLKS,
> +};
> +
> +/* Convenience table to populate regmap in .probe() */
> +static struct clk_regmap *const axg_audio_clk_regmaps[] = {
> + &axg_ddr_arb,
> + &axg_pdm,
> + &axg_tdmin_a,
> + &axg_tdmin_b,
> + &axg_tdmin_c,
> + &axg_tdmin_lb,
> + &axg_tdmout_a,
> + &axg_tdmout_b,
> + &axg_tdmout_c,
> + &axg_frddr_a,
> + &axg_frddr_b,
> + &axg_frddr_c,
> + &axg_toddr_a,
> + &axg_toddr_b,
> + &axg_toddr_c,
> + &axg_loopback,
> + &axg_spdifin,
> + &axg_spdifout,
> + &axg_resample,
> + &axg_power_detect,
> + &axg_mst_a_mclk_sel,
> + &axg_mst_b_mclk_sel,
> + &axg_mst_c_mclk_sel,
> + &axg_mst_d_mclk_sel,
> + &axg_mst_e_mclk_sel,
> + &axg_mst_f_mclk_sel,
> + &axg_mst_a_mclk_div,
> + &axg_mst_b_mclk_div,
> + &axg_mst_c_mclk_div,
> + &axg_mst_d_mclk_div,
> + &axg_mst_e_mclk_div,
> + &axg_mst_f_mclk_div,
> + &axg_mst_a_mclk,
> + &axg_mst_b_mclk,
> + &axg_mst_c_mclk,
> + &axg_mst_d_mclk,
> + &axg_mst_e_mclk,
> + &axg_mst_f_mclk,
> + &axg_spdifout_clk_sel,
> + &axg_spdifout_clk_div,
> + &axg_spdifout_clk,
> + &axg_spdifin_clk_sel,
> + &axg_spdifin_clk_div,
> + &axg_spdifin_clk,
> + &axg_pdm_dclk_sel,
> + &axg_pdm_dclk_div,
> + &axg_pdm_dclk,
> + &axg_pdm_sysclk_sel,
> + &axg_pdm_sysclk_div,
> + &axg_pdm_sysclk,
> + &axg_mst_a_sclk_pre_en,
> + &axg_mst_b_sclk_pre_en,
> + &axg_mst_c_sclk_pre_en,
> + &axg_mst_d_sclk_pre_en,
> + &axg_mst_e_sclk_pre_en,
> + &axg_mst_f_sclk_pre_en,
> + &axg_mst_a_sclk_div,
> + &axg_mst_b_sclk_div,
> + &axg_mst_c_sclk_div,
> + &axg_mst_d_sclk_div,
> + &axg_mst_e_sclk_div,
> + &axg_mst_f_sclk_div,
> + &axg_mst_a_sclk_post_en,
> + &axg_mst_b_sclk_post_en,
> + &axg_mst_c_sclk_post_en,
> + &axg_mst_d_sclk_post_en,
> + &axg_mst_e_sclk_post_en,
> + &axg_mst_f_sclk_post_en,
> + &axg_mst_a_sclk,
> + &axg_mst_b_sclk,
> + &axg_mst_c_sclk,
> + &axg_mst_d_sclk,
> + &axg_mst_e_sclk,
> + &axg_mst_f_sclk,
> + &axg_mst_a_lrclk_div,
> + &axg_mst_b_lrclk_div,
> + &axg_mst_c_lrclk_div,
> + &axg_mst_d_lrclk_div,
> + &axg_mst_e_lrclk_div,
> + &axg_mst_f_lrclk_div,
> + &axg_mst_a_lrclk,
> + &axg_mst_b_lrclk,
> + &axg_mst_c_lrclk,
> + &axg_mst_d_lrclk,
> + &axg_mst_e_lrclk,
> + &axg_mst_f_lrclk,
> + &axg_tdmin_a_sclk_sel,
> + &axg_tdmin_b_sclk_sel,
> + &axg_tdmin_c_sclk_sel,
> + &axg_tdmin_lb_sclk_sel,
> + &axg_tdmout_a_sclk_sel,
> + &axg_tdmout_b_sclk_sel,
> + &axg_tdmout_c_sclk_sel,
> + &axg_tdmin_a_sclk_pre_en,
> + &axg_tdmin_b_sclk_pre_en,
> + &axg_tdmin_c_sclk_pre_en,
> + &axg_tdmin_lb_sclk_pre_en,
> + &axg_tdmout_a_sclk_pre_en,
> + &axg_tdmout_b_sclk_pre_en,
> + &axg_tdmout_c_sclk_pre_en,
> + &axg_tdmin_a_sclk_post_en,
> + &axg_tdmin_b_sclk_post_en,
> + &axg_tdmin_c_sclk_post_en,
> + &axg_tdmin_lb_sclk_post_en,
> + &axg_tdmout_a_sclk_post_en,
> + &axg_tdmout_b_sclk_post_en,
> + &axg_tdmout_c_sclk_post_en,
> + &axg_tdmin_a_sclk,
> + &axg_tdmin_b_sclk,
> + &axg_tdmin_c_sclk,
> + &axg_tdmin_lb_sclk,
> + &axg_tdmout_a_sclk,
> + &axg_tdmout_b_sclk,
> + &axg_tdmout_c_sclk,
> + &axg_tdmin_a_lrclk,
> + &axg_tdmin_b_lrclk,
> + &axg_tdmin_c_lrclk,
> + &axg_tdmin_lb_lrclk,
> + &axg_tdmout_a_lrclk,
> + &axg_tdmout_b_lrclk,
> + &axg_tdmout_c_lrclk,
> +};
> +
> +static struct clk *devm_clk_get_enable(struct device *dev, char *id)
> +{
> + struct clk *clk;
> + int ret;
> +
> + clk = devm_clk_get(dev, id);
> + if (IS_ERR(clk)) {
> + if (PTR_ERR(clk) != -EPROBE_DEFER)
> + dev_err(dev, "failed to get %s", id);
> + return clk;
> + }
> +
> + ret = clk_prepare_enable(clk);
> + if (ret) {
> + dev_err(dev, "failed to enable %s", id);
> + return ERR_PTR(ret);
> + }
> +
> + ret = devm_add_action_or_reset(dev,
> + (void(*)(void *))clk_disable_unprepare,
> + clk);
> + if (ret) {
> + dev_err(dev, "failed to add reset action on %s", id);
> + return ERR_PTR(ret);
> + }
> +
> + return clk;
> +}
> +
> +static const struct clk_ops axg_clk_no_ops = {};
> +
> +static struct clk_hw *axg_clk_hw_register_bypass(struct device *dev,
> + const char *name,
> + const char *parent_name)
> +{
> + struct clk_hw *hw;
> + struct clk_init_data init;
> + char *clk_name;
> + int ret;
> +
> + hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
> + if (!hw)
> + return ERR_PTR(-ENOMEM);
> +
> + clk_name = kasprintf(GFP_KERNEL, "axg_%s", name);
> + if (!clk_name)
> + return ERR_PTR(-ENOMEM);
> +
> + init.name = clk_name;
> + init.ops = &axg_clk_no_ops;
> + init.flags = 0;
> + init.parent_names = parent_name ? &parent_name : NULL;
> + init.num_parents = parent_name ? 1 : 0;
> + hw->init = &init;
> +
> + ret = devm_clk_hw_register(dev, hw);
> + kfree(clk_name);
> +
> + return ret ? ERR_PTR(ret) : hw;
> +}
> +
> +static int axg_register_clk_hw_input(struct device *dev,
> + const char *name,
> + unsigned int clkid)
> +{
> + struct clk *parent_clk = devm_clk_get(dev, name);
> + struct clk_hw *hw = NULL;
> +
> + if (IS_ERR(parent_clk)) {
> + int err = PTR_ERR(parent_clk);
> +
> + /* It is ok if an input clock is missing */
> + if (err == -ENOENT) {
> + dev_dbg(dev, "%s not provided", name);
> + } else {
> + if (err != -EPROBE_DEFER)
> + dev_err(dev, "failed to get %s clock", name);
> + return err;
> + }
> + } else {
> + hw = axg_clk_hw_register_bypass(dev, name,
> + __clk_get_name(parent_clk));
> + }
> +
> + if (IS_ERR(hw)) {
> + dev_err(dev, "failed to register %s clock", name);
> + return PTR_ERR(hw);
> + }
> +
> + axg_audio_hw_onecell_data.hws[clkid] = hw;
> + return 0;
> +}
> +
> +static int axg_register_clk_hw_inputs(struct device *dev,
> + const char *basename,
> + unsigned int count,
> + unsigned int clkid)
> +{
> + char *name;
> + int i, ret;
> +
> + for (i = 0; i < count; i++) {
> + name = kasprintf(GFP_KERNEL, "%s%d", basename, i);
> + if (!name)
> + return -ENOMEM;
> +
> + ret = axg_register_clk_hw_input(dev, name, clkid + i);
> + kfree(name);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static const struct regmap_config axg_audio_regmap_cfg = {
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = 4,
> + .max_register = AUDIO_CLK_PDMIN_CTRL1,
> +};
> +
> +static int axg_audio_clkc_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct regmap *map;
> + struct resource *res;
> + void __iomem *regs;
> + struct clk *clk;
> + struct clk_hw *hw;
> + int ret, i;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + regs = devm_ioremap_resource(dev, res);
> + if (IS_ERR(regs))
> + return PTR_ERR(regs);
> +
> + map = devm_regmap_init_mmio(dev, regs, &axg_audio_regmap_cfg);
> + if (IS_ERR(map)) {
> + dev_err(dev, "failed to init regmap: %ld\n", PTR_ERR(map));
> + return PTR_ERR(map);
> + }
> +
> + /* Get the mandatory peripheral clock */
> + clk = devm_clk_get_enable(dev, "pclk");
> + if (IS_ERR(clk))
> + return PTR_ERR(clk);
> +
> + ret = device_reset(dev);
> + if (ret) {
> + dev_err(dev, "failed to reset device\n");
> + return ret;
> + }
> +
> + /* Register the peripheral input clock */
> + hw = axg_clk_hw_register_bypass(dev, "audio_pclk",
> + __clk_get_name(clk));
> + if (IS_ERR(hw))
> + return PTR_ERR(hw);
> +
> + axg_audio_hw_onecell_data.hws[AUD_CLKID_PCLK] = hw;
> +
> + /* Register optional input master clocks */
> + ret = axg_register_clk_hw_inputs(dev, "mst_in",
> + AXG_MST_IN_COUNT,
> + AUD_CLKID_MST0);
> + if (ret)
> + return ret;
> +
> + /* Register optional input slave sclks */
> + ret = axg_register_clk_hw_inputs(dev, "slv_sclk",
> + AXG_SLV_SCLK_COUNT,
> + AUD_CLKID_SLV_SCLK0);
> + if (ret)
> + return ret;
> +
> + /* Register optional input slave lrclks */
> + ret = axg_register_clk_hw_inputs(dev, "slv_lrclk",
> + AXG_SLV_LRCLK_COUNT,
> + AUD_CLKID_SLV_LRCLK0);
> + if (ret)
> + return ret;
> +
> + /* Populate regmap for the regmap backed clocks */
> + for (i = 0; i < ARRAY_SIZE(axg_audio_clk_regmaps); i++)
> + axg_audio_clk_regmaps[i]->map = map;
> +
> + /* Take care to skip the registered input clocks */
> + for (i = AUD_CLKID_DDR_ARB; i < axg_audio_hw_onecell_data.num; i++) {
> + hw = axg_audio_hw_onecell_data.hws[i];
> + /* array might be sparse */
> + if (!hw)
> + continue;
> +
> + ret = devm_clk_hw_register(dev, hw);
> + if (ret) {
> + dev_err(dev, "failed to register clock %s\n",
> + hw->init->name);
> + return ret;
> + }
> + }
> +
> + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
> + &axg_audio_hw_onecell_data);
> +}
> +
> +static const struct of_device_id clkc_match_table[] = {
> + { .compatible = "amlogic,axg-audio-clkc" },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, clkc_match_table);
> +
> +static struct platform_driver axg_audio_driver = {
> + .probe = axg_audio_clkc_probe,
> + .driver = {
> + .name = "axg-audio-clkc",
> + .of_match_table = clkc_match_table,
> + },
> +};
> +module_platform_driver(axg_audio_driver);
> +
> +MODULE_DESCRIPTION("Amlogic A113x Audio Clock driver");
> +MODULE_AUTHOR("Jerome Brunet <[email protected]>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/clk/meson/axg-audio.h b/drivers/clk/meson/axg-audio.h
> new file mode 100644
> index 000000000000..e7a0efd12429
> --- /dev/null
> +++ b/drivers/clk/meson/axg-audio.h
> @@ -0,0 +1,127 @@
> +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
> +/*
> + * Copyright (c) 2018 BayLibre, SAS.
> + * Author: Jerome Brunet <[email protected]>
> + */
> +
> +#ifndef __AXG_AUDIO_CLKC_H
> +#define __AXG_AUDIO_CLKC_H
> +
> +/*
> + * Audio Clock register offsets
> + *
> + * Register offsets from the datasheet must be multiplied by 4 before
> + * to get the right offset
> + */
> +#define AUDIO_CLK_GATE_EN 0x000
> +#define AUDIO_MCLK_A_CTRL 0x004
> +#define AUDIO_MCLK_B_CTRL 0x008
> +#define AUDIO_MCLK_C_CTRL 0x00C
> +#define AUDIO_MCLK_D_CTRL 0x010
> +#define AUDIO_MCLK_E_CTRL 0x014
> +#define AUDIO_MCLK_F_CTRL 0x018
> +#define AUDIO_MST_A_SCLK_CTRL0 0x040
> +#define AUDIO_MST_A_SCLK_CTRL1 0x044
> +#define AUDIO_MST_B_SCLK_CTRL0 0x048
> +#define AUDIO_MST_B_SCLK_CTRL1 0x04C
> +#define AUDIO_MST_C_SCLK_CTRL0 0x050
> +#define AUDIO_MST_C_SCLK_CTRL1 0x054
> +#define AUDIO_MST_D_SCLK_CTRL0 0x058
> +#define AUDIO_MST_D_SCLK_CTRL1 0x05C
> +#define AUDIO_MST_E_SCLK_CTRL0 0x060
> +#define AUDIO_MST_E_SCLK_CTRL1 0x064
> +#define AUDIO_MST_F_SCLK_CTRL0 0x068
> +#define AUDIO_MST_F_SCLK_CTRL1 0x06C
> +#define AUDIO_CLK_TDMIN_A_CTRL 0x080
> +#define AUDIO_CLK_TDMIN_B_CTRL 0x084
> +#define AUDIO_CLK_TDMIN_C_CTRL 0x088
> +#define AUDIO_CLK_TDMIN_LB_CTRL 0x08C
> +#define AUDIO_CLK_TDMOUT_A_CTRL 0x090
> +#define AUDIO_CLK_TDMOUT_B_CTRL 0x094
> +#define AUDIO_CLK_TDMOUT_C_CTRL 0x098
> +#define AUDIO_CLK_SPDIFIN_CTRL 0x09C
> +#define AUDIO_CLK_SPDIFOUT_CTRL 0x0A0
> +#define AUDIO_CLK_RESAMPLE_CTRL 0x0A4
> +#define AUDIO_CLK_LOCKER_CTRL 0x0A8
> +#define AUDIO_CLK_PDMIN_CTRL0 0x0AC
> +#define AUDIO_CLK_PDMIN_CTRL1 0x0B0
> +
> +/*
> + * CLKID index values
> + * These indices are entirely contrived and do not map onto the hardware.
> + */
> +
> +#define AUD_CLKID_PCLK 0
> +#define AUD_CLKID_MST0 1
> +#define AUD_CLKID_MST1 2
> +#define AUD_CLKID_MST2 3
> +#define AUD_CLKID_MST3 4
> +#define AUD_CLKID_MST4 5
> +#define AUD_CLKID_MST5 6
> +#define AUD_CLKID_MST6 7
> +#define AUD_CLKID_MST7 8
> +#define AUD_CLKID_MST_A_MCLK_SEL 59
> +#define AUD_CLKID_MST_B_MCLK_SEL 60
> +#define AUD_CLKID_MST_C_MCLK_SEL 61
> +#define AUD_CLKID_MST_D_MCLK_SEL 62
> +#define AUD_CLKID_MST_E_MCLK_SEL 63
> +#define AUD_CLKID_MST_F_MCLK_SEL 64
> +#define AUD_CLKID_MST_A_MCLK_DIV 65
> +#define AUD_CLKID_MST_B_MCLK_DIV 66
> +#define AUD_CLKID_MST_C_MCLK_DIV 67
> +#define AUD_CLKID_MST_D_MCLK_DIV 68
> +#define AUD_CLKID_MST_E_MCLK_DIV 69
> +#define AUD_CLKID_MST_F_MCLK_DIV 70
> +#define AUD_CLKID_SPDIFOUT_CLK_SEL 71
> +#define AUD_CLKID_SPDIFOUT_CLK_DIV 72
> +#define AUD_CLKID_SPDIFIN_CLK_SEL 73
> +#define AUD_CLKID_SPDIFIN_CLK_DIV 74
> +#define AUD_CLKID_PDM_DCLK_SEL 75
> +#define AUD_CLKID_PDM_DCLK_DIV 76
> +#define AUD_CLKID_PDM_SYSCLK_SEL 77
> +#define AUD_CLKID_PDM_SYSCLK_DIV 78
> +#define AUD_CLKID_MST_A_SCLK_PRE_EN 92
> +#define AUD_CLKID_MST_B_SCLK_PRE_EN 93
> +#define AUD_CLKID_MST_C_SCLK_PRE_EN 94
> +#define AUD_CLKID_MST_D_SCLK_PRE_EN 95
> +#define AUD_CLKID_MST_E_SCLK_PRE_EN 96
> +#define AUD_CLKID_MST_F_SCLK_PRE_EN 97
> +#define AUD_CLKID_MST_A_SCLK_DIV 98
> +#define AUD_CLKID_MST_B_SCLK_DIV 99
> +#define AUD_CLKID_MST_C_SCLK_DIV 100
> +#define AUD_CLKID_MST_D_SCLK_DIV 101
> +#define AUD_CLKID_MST_E_SCLK_DIV 102
> +#define AUD_CLKID_MST_F_SCLK_DIV 103
> +#define AUD_CLKID_MST_A_SCLK_POST_EN 104
> +#define AUD_CLKID_MST_B_SCLK_POST_EN 105
> +#define AUD_CLKID_MST_C_SCLK_POST_EN 106
> +#define AUD_CLKID_MST_D_SCLK_POST_EN 107
> +#define AUD_CLKID_MST_E_SCLK_POST_EN 108
> +#define AUD_CLKID_MST_F_SCLK_POST_EN 109
> +#define AUD_CLKID_MST_A_LRCLK_DIV 110
> +#define AUD_CLKID_MST_B_LRCLK_DIV 111
> +#define AUD_CLKID_MST_C_LRCLK_DIV 112
> +#define AUD_CLKID_MST_D_LRCLK_DIV 113
> +#define AUD_CLKID_MST_E_LRCLK_DIV 114
> +#define AUD_CLKID_MST_F_LRCLK_DIV 115
> +#define AUD_CLKID_TDMIN_A_SCLK_PRE_EN 137
> +#define AUD_CLKID_TDMIN_B_SCLK_PRE_EN 138
> +#define AUD_CLKID_TDMIN_C_SCLK_PRE_EN 139
> +#define AUD_CLKID_TDMIN_LB_SCLK_PRE_EN 140
> +#define AUD_CLKID_TDMOUT_A_SCLK_PRE_EN 141
> +#define AUD_CLKID_TDMOUT_B_SCLK_PRE_EN 142
> +#define AUD_CLKID_TDMOUT_C_SCLK_PRE_EN 143
> +#define AUD_CLKID_TDMIN_A_SCLK_POST_EN 144
> +#define AUD_CLKID_TDMIN_B_SCLK_POST_EN 145
> +#define AUD_CLKID_TDMIN_C_SCLK_POST_EN 146
> +#define AUD_CLKID_TDMIN_LB_SCLK_POST_EN 147
> +#define AUD_CLKID_TDMOUT_A_SCLK_POST_EN 148
> +#define AUD_CLKID_TDMOUT_B_SCLK_POST_EN 149
> +#define AUD_CLKID_TDMOUT_C_SCLK_POST_EN 150
> +
> +/* include the CLKIDs which are part of the DT bindings */
> +#include <dt-bindings/clock/axg-audio-clkc.h>
> +
> +#define NR_CLKS 151
> +
> +#endif /*__AXG_AUDIO_CLKC_H */
>

Acked-by: Neil Armstrong <[email protected]>

2018-04-26 08:52:31

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH 3/7] clk: meson: add triple phase clock driver

On 26/04/2018 10:47, Neil Armstrong wrote:
> On 25/04/2018 18:33, Jerome Brunet wrote:
>> Add a driver to control the output of the sample clock generator found
>> in the axg audio clock controller.
>>
>> The goal of this driver is to coherently control the phase provided to
>> the different element using the sample clock generator. This simplify
>> the usage of the sample clock generator a lot, without comprising the
>> ability of the SoC.
>>
>> Signed-off-by: Jerome Brunet <[email protected]>
>> ---
>> drivers/clk/meson/Kconfig | 5 +++
>> drivers/clk/meson/Makefile | 1 +
>> drivers/clk/meson/clk-triphase.c | 68 ++++++++++++++++++++++++++++++++++++++++
>> drivers/clk/meson/clkc-audio.h | 20 ++++++++++++
>> 4 files changed, 94 insertions(+)
>> create mode 100644 drivers/clk/meson/clk-triphase.c
>> create mode 100644 drivers/clk/meson/clkc-audio.h
>>
>> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
>> index 87d69573e172..7f7fd6fb3809 100644
>> --- a/drivers/clk/meson/Kconfig
>> +++ b/drivers/clk/meson/Kconfig
>> @@ -3,6 +3,11 @@ config COMMON_CLK_AMLOGIC
>> depends on ARCH_MESON || COMPILE_TEST
>> select COMMON_CLK_REGMAP_MESON
>>
>> +config COMMON_CLK_AMLOGIC_AUDIO
>> + bool
>> + depends on ARCH_MESON || COMPILE_TEST
>> + select COMMON_CLK_AMLOGIC
>> +
>> config COMMON_CLK_REGMAP_MESON
>> bool
>> select REGMAP
>> diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
>> index 352fb848c406..64bb917fe1f0 100644
>> --- a/drivers/clk/meson/Makefile
>> +++ b/drivers/clk/meson/Makefile
>> @@ -4,6 +4,7 @@
>>
>> obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-mpll.o clk-audio-divider.o
>> obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-phase.o
>> +obj-$(CONFIG_COMMON_CLK_AMLOGIC_AUDIO) += clk-triphase.o
>> obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
>> obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o
>> obj-$(CONFIG_COMMON_CLK_AXG) += axg.o
>> diff --git a/drivers/clk/meson/clk-triphase.c b/drivers/clk/meson/clk-triphase.c
>> new file mode 100644
>> index 000000000000..9508c03c73c1
>> --- /dev/null
>> +++ b/drivers/clk/meson/clk-triphase.c
>> @@ -0,0 +1,68 @@
>> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
>> +/*
>> + * Copyright (c) 2018 BayLibre, SAS.
>> + * Author: Jerome Brunet <[email protected]>
>> + */
>> +
>> +#include <linux/clk-provider.h>
>> +#include "clkc-audio.h"
>> +
>> +/*
>> + * This is a special clock for the audio controller.
>> + * The phase of mst_sclk clock output can be controlled independently
>> + * for the outside world (ph0), the tdmout (ph1) and tdmin (ph2).
>> + * Controlling these 3 phases as just one makes things simpler and
>> + * give the same clock view to all the element on the i2s bus.
>> + * If necessary, we can still control the phase in the tdm block
>> + * which makes these independent control redundant.
>> + */
>> +static inline struct meson_clk_triphase_data *
>> +meson_clk_triphase_data(struct clk_regmap *clk)
>> +{
>> + return (struct meson_clk_triphase_data *)clk->data;
>> +}
>> +
>> +static void meson_clk_triphase_sync(struct clk_hw *hw)
>> +{
>> + struct clk_regmap *clk = to_clk_regmap(hw);
>> + struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
>> + unsigned int val;
>> +
>> + /* Get phase 0 and sync it to phase 1 and 2 */
>> + val = meson_parm_read(clk->map, &tph->ph0);
>> + meson_parm_write(clk->map, &tph->ph1, val);
>> + meson_parm_write(clk->map, &tph->ph2, val);
>> +}
>> +
>> +static int meson_clk_triphase_get_phase(struct clk_hw *hw)
>> +{
>> + struct clk_regmap *clk = to_clk_regmap(hw);
>> + struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
>> + unsigned int val;
>> +
>> + /* Phase are in sync, reading phase 0 is enough */
>> + val = meson_parm_read(clk->map, &tph->ph0);
>> +
>> + return meson_clk_degrees_from_val(val, tph->ph0.width);
>> +}
>> +
>> +static int meson_clk_triphase_set_phase(struct clk_hw *hw, int degrees)
>> +{
>> + struct clk_regmap *clk = to_clk_regmap(hw);
>> + struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
>> + unsigned int val;
>> +
>> + val = meson_clk_degrees_to_val(degrees, tph->ph0.width);
>> + meson_parm_write(clk->map, &tph->ph0, val);
>> + meson_parm_write(clk->map, &tph->ph1, val);
>> + meson_parm_write(clk->map, &tph->ph2, val);
>> +
>> + return 0;
>> +}
>> +
>> +const struct clk_ops meson_clk_triphase_ops = {
>> + .init = meson_clk_triphase_sync,
>> + .get_phase = meson_clk_triphase_get_phase,
>> + .set_phase = meson_clk_triphase_set_phase,
>> +};
>> +EXPORT_SYMBOL_GPL(meson_clk_triphase_ops);
>> diff --git a/drivers/clk/meson/clkc-audio.h b/drivers/clk/meson/clkc-audio.h
>> new file mode 100644
>> index 000000000000..286ff1201258
>> --- /dev/null
>> +++ b/drivers/clk/meson/clkc-audio.h
>> @@ -0,0 +1,20 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>
> // SPDX-License-Identifier: GPL-2.0
>
> Checkpatch should have warned about this !

My bad, I wasn't totally aware there is an exception in headers...

>
>> +/*
>> + * Copyright (c) 2018 BayLibre, SAS.
>> + * Author: Jerome Brunet <[email protected]>
>> + */
>> +
>> +#ifndef __MESON_CLKC_AUDIO_H
>> +#define __MESON_CLKC_AUDIO_H
>> +
>> +#include "clkc.h"
>> +
>> +struct meson_clk_triphase_data {
>> + struct parm ph0;
>> + struct parm ph1;
>> + struct parm ph2;
>> +};
>> +
>> +extern const struct clk_ops meson_clk_triphase_ops;
>> +
>> +#endif /* __MESON_CLKC_AUDIO_H */
>>
>
> Apart that :
>
> Acked-by: Neil Armstrong <[email protected]>
>


2018-04-27 01:15:45

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH 7/7] clk: meson: axg: add the audio clock controller driver

Hi Jerome,

I love your patch! Yet something to improve:

[auto build test ERROR on clk/clk-next]
[also build test ERROR on v4.17-rc2 next-20180426]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/Jerome-Brunet/clk-meson-axg-add-audio-clock-controller-support/20180427-035149
base: https://git.kernel.org/pub/scm/linux/kernel/git/clk/linux.git clk-next
config: arm64-allmodconfig (attached as .config)
compiler: aarch64-linux-gnu-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=arm64

All error/warnings (new ones prefixed by >>):

In file included from drivers/clk/meson/clk-triphase.c:8:0:
>> drivers/clk/meson/clkc-audio.h:22:18: error: field 'cached_duty' has incomplete type
struct clk_duty cached_duty;
^~~~~~~~~~~
--
In file included from drivers/clk/meson/sclk-div.c:19:0:
>> drivers/clk/meson/clkc-audio.h:22:18: error: field 'cached_duty' has incomplete type
struct clk_duty cached_duty;
^~~~~~~~~~~
drivers/clk/meson/sclk-div.c: In function 'sclk_div_set_duty_cycle':
>> drivers/clk/meson/sclk-div.c:127:43: error: dereferencing pointer to incomplete type 'struct clk_duty'
memcpy(&sclk->cached_duty, duty, sizeof(*duty));
^~~~~
drivers/clk/meson/sclk-div.c: At top level:
>> drivers/clk/meson/sclk-div.c:239:3: error: 'const struct clk_ops' has no member named 'get_duty_cycle'
.get_duty_cycle = sclk_div_get_duty_cycle,
^~~~~~~~~~~~~~
>> drivers/clk/meson/sclk-div.c:239:20: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
.get_duty_cycle = sclk_div_get_duty_cycle,
^~~~~~~~~~~~~~~~~~~~~~~
drivers/clk/meson/sclk-div.c:239:20: note: (near initialization for 'meson_sclk_div_ops.disable_unused')
>> drivers/clk/meson/sclk-div.c:240:3: error: 'const struct clk_ops' has no member named 'set_duty_cycle'
.set_duty_cycle = sclk_div_set_duty_cycle,
^~~~~~~~~~~~~~
drivers/clk/meson/sclk-div.c:240:20: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
.set_duty_cycle = sclk_div_set_duty_cycle,
^~~~~~~~~~~~~~~~~~~~~~~
drivers/clk/meson/sclk-div.c:240:20: note: (near initialization for 'meson_sclk_div_ops.recalc_rate')
cc1: some warnings being treated as errors
--
In file included from drivers/clk/meson/axg-audio.c:17:0:
>> drivers/clk/meson/clkc-audio.h:22:18: error: field 'cached_duty' has incomplete type
struct clk_duty cached_duty;
^~~~~~~~~~~
>> drivers/clk/meson/axg-audio.c:35:12: error: 'CLK_DUTY_CYCLE_PARENT' undeclared here (not in a function); did you mean 'CLK_SET_RATE_PARENT'?
.flags = CLK_DUTY_CYCLE_PARENT | (_iflags), \
^
>> drivers/clk/meson/axg-audio.c:74:2: note: in expansion of macro 'AXG_AUD_GATE'
AXG_AUD_GATE(_name, AUDIO_CLK_GATE_EN, _bit, "axg_audio_pclk", 0)
^~~~~~~~~~~~
>> drivers/clk/meson/axg-audio.c:77:8: note: in expansion of macro 'AXG_PCLK_GATE'
static AXG_PCLK_GATE(ddr_arb, 0);
^~~~~~~~~~~~~

vim +/cached_duty +22 drivers/clk/meson/clkc-audio.h

6f493fa86 Jerome Brunet 2018-04-25 17
e8e28d3aa Jerome Brunet 2018-04-25 18 struct meson_sclk_div_data {
e8e28d3aa Jerome Brunet 2018-04-25 19 struct parm div;
e8e28d3aa Jerome Brunet 2018-04-25 20 struct parm hi;
e8e28d3aa Jerome Brunet 2018-04-25 21 unsigned int cached_div;
e8e28d3aa Jerome Brunet 2018-04-25 @22 struct clk_duty cached_duty;
e8e28d3aa Jerome Brunet 2018-04-25 23 };
e8e28d3aa Jerome Brunet 2018-04-25 24

:::::: The code at line 22 was first introduced by commit
:::::: e8e28d3aaf526ae3af0d0fd0e8b4c8c8ad2041d8 clk: meson: add axg audio sclk divider driver

:::::: TO: Jerome Brunet <[email protected]>
:::::: CC: 0day robot <[email protected]>

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation


Attachments:
(No filename) (4.32 kB)
.config.gz (57.68 kB)
Download all attachments

2018-05-01 14:31:45

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH 5/7] clk: meson: axg: export audio clock controller id bindings

On Wed, Apr 25, 2018 at 06:33:02PM +0200, Jerome Brunet wrote:
> export the clock ids dt-bindings usable by the consumers of the axg
> audio clock controller

Capitalization and punctuation would be nice.

>
> Signed-off-by: Jerome Brunet <[email protected]>
> ---
> include/dt-bindings/clock/axg-audio-clkc.h | 94 ++++++++++++++++++++++++++++++
> 1 file changed, 94 insertions(+)
> create mode 100644 include/dt-bindings/clock/axg-audio-clkc.h

This can be combined with the next patch. Otherwise,

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

>
> diff --git a/include/dt-bindings/clock/axg-audio-clkc.h b/include/dt-bindings/clock/axg-audio-clkc.h
> new file mode 100644
> index 000000000000..4426ae655858
> --- /dev/null
> +++ b/include/dt-bindings/clock/axg-audio-clkc.h
> @@ -0,0 +1,94 @@
> +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
> +/*
> + * Copyright (c) 2018 Baylibre SAS.
> + * Author: Jerome Brunet <[email protected]>
> + */
> +
> +#ifndef __AXG_AUDIO_CLKC_BINDINGS_H
> +#define __AXG_AUDIO_CLKC_BINDINGS_H
> +
> +#define AUD_CLKID_SLV_SCLK0 9
> +#define AUD_CLKID_SLV_SCLK1 10
> +#define AUD_CLKID_SLV_SCLK2 11
> +#define AUD_CLKID_SLV_SCLK3 12
> +#define AUD_CLKID_SLV_SCLK4 13
> +#define AUD_CLKID_SLV_SCLK5 14
> +#define AUD_CLKID_SLV_SCLK6 15
> +#define AUD_CLKID_SLV_SCLK7 16
> +#define AUD_CLKID_SLV_SCLK8 17
> +#define AUD_CLKID_SLV_SCLK9 18
> +#define AUD_CLKID_SLV_LRCLK0 19
> +#define AUD_CLKID_SLV_LRCLK1 20
> +#define AUD_CLKID_SLV_LRCLK2 21
> +#define AUD_CLKID_SLV_LRCLK3 22
> +#define AUD_CLKID_SLV_LRCLK4 23
> +#define AUD_CLKID_SLV_LRCLK5 24
> +#define AUD_CLKID_SLV_LRCLK6 25
> +#define AUD_CLKID_SLV_LRCLK7 26
> +#define AUD_CLKID_SLV_LRCLK8 27
> +#define AUD_CLKID_SLV_LRCLK9 28
> +#define AUD_CLKID_DDR_ARB 29
> +#define AUD_CLKID_PDM 30
> +#define AUD_CLKID_TDMIN_A 31
> +#define AUD_CLKID_TDMIN_B 32
> +#define AUD_CLKID_TDMIN_C 33
> +#define AUD_CLKID_TDMIN_LB 34
> +#define AUD_CLKID_TDMOUT_A 35
> +#define AUD_CLKID_TDMOUT_B 36
> +#define AUD_CLKID_TDMOUT_C 37
> +#define AUD_CLKID_FRDDR_A 38
> +#define AUD_CLKID_FRDDR_B 39
> +#define AUD_CLKID_FRDDR_C 40
> +#define AUD_CLKID_TODDR_A 41
> +#define AUD_CLKID_TODDR_B 42
> +#define AUD_CLKID_TODDR_C 43
> +#define AUD_CLKID_LOOPBACK 44
> +#define AUD_CLKID_SPDIFIN 45
> +#define AUD_CLKID_SPDIFOUT 46
> +#define AUD_CLKID_RESAMPLE 47
> +#define AUD_CLKID_POWER_DETECT 48
> +#define AUD_CLKID_MST_A_MCLK 49
> +#define AUD_CLKID_MST_B_MCLK 50
> +#define AUD_CLKID_MST_C_MCLK 51
> +#define AUD_CLKID_MST_D_MCLK 52
> +#define AUD_CLKID_MST_E_MCLK 53
> +#define AUD_CLKID_MST_F_MCLK 54
> +#define AUD_CLKID_SPDIFOUT_CLK 55
> +#define AUD_CLKID_SPDIFIN_CLK 56
> +#define AUD_CLKID_PDM_DCLK 57
> +#define AUD_CLKID_PDM_SYSCLK 58
> +#define AUD_CLKID_MST_A_SCLK 79
> +#define AUD_CLKID_MST_B_SCLK 80
> +#define AUD_CLKID_MST_C_SCLK 81
> +#define AUD_CLKID_MST_D_SCLK 82
> +#define AUD_CLKID_MST_E_SCLK 83
> +#define AUD_CLKID_MST_F_SCLK 84
> +#define AUD_CLKID_MST_A_LRCLK 86
> +#define AUD_CLKID_MST_B_LRCLK 87
> +#define AUD_CLKID_MST_C_LRCLK 88
> +#define AUD_CLKID_MST_D_LRCLK 89
> +#define AUD_CLKID_MST_E_LRCLK 90
> +#define AUD_CLKID_MST_F_LRCLK 91
> +#define AUD_CLKID_TDMIN_A_SCLK_SEL 116
> +#define AUD_CLKID_TDMIN_B_SCLK_SEL 117
> +#define AUD_CLKID_TDMIN_C_SCLK_SEL 118
> +#define AUD_CLKID_TDMIN_LB_SCLK_SEL 119
> +#define AUD_CLKID_TDMOUT_A_SCLK_SEL 120
> +#define AUD_CLKID_TDMOUT_B_SCLK_SEL 121
> +#define AUD_CLKID_TDMOUT_C_SCLK_SEL 122
> +#define AUD_CLKID_TDMIN_A_SCLK 123
> +#define AUD_CLKID_TDMIN_B_SCLK 124
> +#define AUD_CLKID_TDMIN_C_SCLK 125
> +#define AUD_CLKID_TDMIN_LB_SCLK 126
> +#define AUD_CLKID_TDMOUT_A_SCLK 127
> +#define AUD_CLKID_TDMOUT_B_SCLK 128
> +#define AUD_CLKID_TDMOUT_C_SCLK 129
> +#define AUD_CLKID_TDMIN_A_LRCLK 130
> +#define AUD_CLKID_TDMIN_B_LRCLK 131
> +#define AUD_CLKID_TDMIN_C_LRCLK 132
> +#define AUD_CLKID_TDMIN_LB_LRCLK 133
> +#define AUD_CLKID_TDMOUT_A_LRCLK 134
> +#define AUD_CLKID_TDMOUT_B_LRCLK 135
> +#define AUD_CLKID_TDMOUT_C_LRCLK 136
> +
> +#endif /* __AXG_AUDIO_CLKC_BINDINGS_H */
> --
> 2.14.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe devicetree" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2018-05-01 14:38:16

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH 6/7] clk: meson: axg: document bindings for the audio clock controller

On Wed, Apr 25, 2018 at 06:33:03PM +0200, Jerome Brunet wrote:
> Add documentation for the device tree bindings of the audio clock
> controller of the A113 based SoCs
>

The preferred subject prefix for bindings is 'dt/bindings: clock: ...'

> Signed-off-by: Jerome Brunet <[email protected]>
> ---
> .../bindings/clock/amlogic,axg-audio-clkc.txt | 56 ++++++++++++++++++++++
> 1 file changed, 56 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/clock/amlogic,axg-audio-clkc.txt
>
> diff --git a/Documentation/devicetree/bindings/clock/amlogic,axg-audio-clkc.txt b/Documentation/devicetree/bindings/clock/amlogic,axg-audio-clkc.txt
> new file mode 100644
> index 000000000000..1b989ceda567
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/amlogic,axg-audio-clkc.txt
> @@ -0,0 +1,56 @@
> +* Amlogic AXG Audio Clock Controllers
> +
> +The Amlogic AXG audio clock controller generates and supplies clock to the
> +other elements of the audio subsystem, such as fifos, i2s, spdif and pdm
> +devices.
> +
> +Required Properties:
> +
> +- compatible : should be "amlogic,axg-audio-clkc" for the A113X and A113D
> +- reg : physical base address of the clock controller and length of
> + memory mapped region.
> +- clocks : a list of phandle + clock-specifier pairs for the clocks listed
> + in clock-names.
> +- clock-names : must contain the following:
> + * "pclk" - Main peripheral bus clock
> + may contain the following:
> + * "mst_in[0-7]" - 8 input plls to generate clock signals

Unless the sources don't exist, then you should always have the clocks.
It should not be based on whether you are using them or not.

> + * "slv_sclk[0-9]" - 10 slave bit clocks provided by external
> + components.
> + * "slv_lrclk[0-9]" - 10 slave sample clocks provided by external
> + components.

I'm guessing these may come from pins that aren't connected, so being
optional is fine.

> +- reset : phandle of the internal reset line

resets

> +- #clock-cells : should be 1.
> +
> +Each clock is assigned an identifier and client nodes can use this identifier
> +to specify the clock which they consume. All available clocks are defined as
> +preprocessor macros in the dt-bindings/clock/axg-audio-clkc.h header and can be
> +used in device tree sources.
> +
> +Example:
> +
> +clkc_audio: clock-controller {

Needs a unit address.

> + compatible = "amlogic,axg-audio-clkc";
> + reg = <0x0 0x0 0x0 0xb4>;
> + #clock-cells = <1>;
> +
> + clocks = <&clkc CLKID_AUDIO>,
> + <&clkc CLKID_MPLL0>,
> + <&clkc CLKID_MPLL1>,
> + <&clkc CLKID_MPLL2>,
> + <&clkc CLKID_MPLL3>,
> + <&clkc CLKID_HIFI_PLL>,
> + <&clkc CLKID_FCLK_DIV3>,
> + <&clkc CLKID_FCLK_DIV4>,
> + <&clkc CLKID_GP0_PLL>;
> + clock-names = "pclk",
> + "mst_in0",
> + "mst_in1",
> + "mst_in2",
> + "mst_in3",
> + "mst_in4",
> + "mst_in5",
> + "mst_in6",
> + "mst_in7";
> + resets = <&reset RESET_AUDIO>;
> +};
> --
> 2.14.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe devicetree" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2018-05-14 14:17:36

by Jerome Brunet

[permalink] [raw]
Subject: Re: [PATCH 6/7] clk: meson: axg: document bindings for the audio clock controller

On Tue, 2018-05-01 at 09:37 -0500, Rob Herring wrote:
> On Wed, Apr 25, 2018 at 06:33:03PM +0200, Jerome Brunet wrote:
> > Add documentation for the device tree bindings of the audio clock
> > controller of the A113 based SoCs
> >
>
> The preferred subject prefix for bindings is 'dt/bindings: clock: ...'

Noted, I suppose meant 'dt-bindings: clocks: ..." which seems to used half of
the times
The rest use 'clk: {platform}: ...'

>
> > Signed-off-by: Jerome Brunet <[email protected]>
> > ---
> > .../bindings/clock/amlogic,axg-audio-clkc.txt | 56 ++++++++++++++++++++++
> > 1 file changed, 56 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/clock/amlogic,axg-audio-clkc.txt
> >
> > diff --git a/Documentation/devicetree/bindings/clock/amlogic,axg-audio-clkc.txt b/Documentation/devicetree/bindings/clock/amlogic,axg-audio-clkc.txt
> > new file mode 100644
> > index 000000000000..1b989ceda567
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/clock/amlogic,axg-audio-clkc.txt
> > @@ -0,0 +1,56 @@
> > +* Amlogic AXG Audio Clock Controllers
> > +
> > +The Amlogic AXG audio clock controller generates and supplies clock to the
> > +other elements of the audio subsystem, such as fifos, i2s, spdif and pdm
> > +devices.
> > +
> > +Required Properties:
> > +
> > +- compatible : should be "amlogic,axg-audio-clkc" for the A113X and A113D
> > +- reg : physical base address of the clock controller and length of
> > + memory mapped region.
> > +- clocks : a list of phandle + clock-specifier pairs for the clocks listed
> > + in clock-names.
> > +- clock-names : must contain the following:
> > + * "pclk" - Main peripheral bus clock
> > + may contain the following:
> > + * "mst_in[0-7]" - 8 input plls to generate clock signals
>
> Unless the sources don't exist, then you should always have the clocks.
> It should not be based on whether you are using them or not.

This is not really the point. The parent controller may or may not provide some
of the PLL. This was the case during the development of the controller when the
HIFI pll was not available simply because there was no driver for it.

Whatever the reason for an input clock to be missing, this clock controller may
continue to serve its purpose even if some input plls are not provided/available
which is why these are not "mandatory"

>
> > + * "slv_sclk[0-9]" - 10 slave bit clocks provided by external
> > + components.
> > + * "slv_lrclk[0-9]" - 10 slave sample clocks provided by external
> > + components.
>
> I'm guessing these may come from pins that aren't connected, so being
> optional is fine.
>
> > +- reset : phandle of the internal reset line
>
> resets
>
> > +- #clock-cells : should be 1.
> > +
> > +Each clock is assigned an identifier and client nodes can use this identifier
> > +to specify the clock which they consume. All available clocks are defined as
> > +preprocessor macros in the dt-bindings/clock/axg-audio-clkc.h header and can be
> > +used in device tree sources.
> > +
> > +Example:
> > +
> > +clkc_audio: clock-controller {
>
> Needs a unit address.
>
> > + compatible = "amlogic,axg-audio-clkc";
> > + reg = <0x0 0x0 0x0 0xb4>;
> > + #clock-cells = <1>;
> > +
> > + clocks = <&clkc CLKID_AUDIO>,
> > + <&clkc CLKID_MPLL0>,
> > + <&clkc CLKID_MPLL1>,
> > + <&clkc CLKID_MPLL2>,
> > + <&clkc CLKID_MPLL3>,
> > + <&clkc CLKID_HIFI_PLL>,
> > + <&clkc CLKID_FCLK_DIV3>,
> > + <&clkc CLKID_FCLK_DIV4>,
> > + <&clkc CLKID_GP0_PLL>;
> > + clock-names = "pclk",
> > + "mst_in0",
> > + "mst_in1",
> > + "mst_in2",
> > + "mst_in3",
> > + "mst_in4",
> > + "mst_in5",
> > + "mst_in6",
> > + "mst_in7";
> > + resets = <&reset RESET_AUDIO>;
> > +};
> > --
> > 2.14.3
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe devicetree" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html


2018-05-15 23:42:08

by Stephen Boyd

[permalink] [raw]
Subject: Re: [PATCH 7/7] clk: meson: axg: add the audio clock controller driver

Quoting Neil Armstrong (2018-04-26 01:49:29)
> On 25/04/2018 18:33, Jerome Brunet wrote:
> > +#endif /*__AXG_AUDIO_CLKC_H */
> >
>
> Acked-by: Neil Armstrong <[email protected]>

Please trim replies.