The Amlogic G12A, G12B & SM1 SoCs embeds a Synopsys DW-MIPI-DSI transceiver (ver 1.21a),
with a custom glue managing the IP resets, clock and data input similar to the DW-HDMI
glue on the same Amlogic SoCs.
This is a follow-up of v5 now the DRM patches are applied, the clk & DT changes
remains for a full DSI support on G12A & SM1 platforms.
The DW-MIPI-DSI transceiver + D-PHY are clocked by the GP0 PLL, and the ENCL encoder + VIU
pixel reader by the VCLK2 clock using the HDMI PLL.
The DW-MIPI-DSI transceiver gets this pixel stream as input clocked with the VCLK2 clock.
An optional "MEAS" clock can be enabled to measure the delay between each vsync feeding the
DW-MIPI-DSI transceiver.
The clock setup has been redesigned to use CCF, a common PLL (GP0) and the VCLK2 clock
path for DSI in preparation of full CCF support and possibly dual display with HDMI.
The change from v5 is that now we use a "VCLK" driver instea dof notifier and rely
on CLK_SET_RATE_GATE to ensure the VCLK gate operation are called.
Depends on clkid public migration at [4].
Signed-off-by: Neil Armstrong <[email protected]>
---
Changes in v7:
- Added review tags
- Fixed patch 5 thanks to George
- Link to v6: https://lore.kernel.org/r/20230512-amlogic-v6-4-upstream-dsi-ccf-vim3-v6-0-fd2ac9845472@linaro.org
Changes in v6:
- dropped applied DRM patches
- dropped clk private prefix patches
- rebased on top of 20230607-topic-amlogic-upstream-clkid-public-migration-v2-0-38172d17c27a@linaro.org
- re-ordered/cleaned ENCL patches to match clkid public migration
- Added new "vclk" driver
- uses vclk driver instead of notifier
- cleaned VCLK2 clk flags
- add px_clk gating from DSI driver
- Link to v5: https://lore.kernel.org/r/20230512-amlogic-v6-4-upstream-dsi-ccf-vim3-v5-0-56eb7a4d5b8e@linaro.org
Changes in v5:
- Aded PRIV all the G12 internal clk IDS to simplify public exposing
- Fixed the DSI bindings
- Fixed the DSI HSYNC/VSYNC polarity handling
- Fixed the DSI clock setup
- Fixed the DSI phy timings
- Dropped components for DSI, only keeping it for HDMI
- Added MNT Reform 2 CM4 DT
- Dropped already applied PHY fix
- Link to v4: https://lore.kernel.org/r/20230512-amlogic-v6-4-upstream-dsi-ccf-vim3-v4-0-2592c29ea263@linaro.org
Changes from v3 at [3]:
- switched all clk setup via CCF
- using single PLL for DSI controller & ENCL encoder
- added ENCL clocks to CCF
- make the VCLK2 clocks configuration by CCF
- fixed probe/bind of DSI controller to work with panels & bridges
- added bit_clk to controller to it can setup the BIT clock aswell
- added fix for components unbind
- added fix for analog phy setup value
- added TS050 timings fix
- dropped previous clk control patch
Changes from v2 at [2]:
- Fixed patch 3
- Added reviews from Jagan
- Rebased on v5.19-rc1
Changes from v1 at [1]:
- fixed DSI host bindings
- add reviewed-by tags for bindings
- moved magic values to defines thanks to Martin's searches
- added proper prefixes to defines
- moved phy_configure to phy_init() dw-mipi-dsi callback
- moved phy_on to a new phy_power_on() dw-mipi-dsi callback
- correctly return phy_init/configure errors to callback returns
[1] https://lore.kernel.org/r/[email protected]
[2] https://lore.kernel.org/r/[email protected]
[3] https://lore.kernel.org/r/[email protected]
[4] https://lore.kernel.org/all/20230607-topic-amlogic-upstream-clkid-public-migration-v2-0-38172d17c27a@linaro.org/
---
Neil Armstrong (9):
dt-bindings: clk: g12a-clkc: add CTS_ENCL clock ids
clk: meson: g12a: add CTS_ENCL & CTS_ENCL_SEL clocks
clk: meson: add vclk driver
clk: meson: g12a: make VCLK2 and ENCL clock path configurable by CCF
drm/meson: gate px_clk when setting rate
arm64: meson: g12-common: add the MIPI DSI nodes
DONOTMERGE: arm64: meson: khadas-vim3l: add DSI panel
dt-bindings: arm: amlogic: Document the MNT Reform 2 CM4 adapter with a BPI-CM4 Module
arm64: dts: amlogic: meson-g12b-bananapi-cm4: add support for MNT Reform2 with CM4 adaper
Documentation/devicetree/bindings/arm/amlogic.yaml | 1 +
arch/arm64/boot/dts/amlogic/Makefile | 1 +
arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi | 70 ++++
.../meson-g12b-bananapi-cm4-mnt-reform2.dts | 388 +++++++++++++++++++++
.../boot/dts/amlogic/meson-g12b-khadas-vim3.dtsi | 2 +-
arch/arm64/boot/dts/amlogic/meson-khadas-vim3.dtsi | 76 ++++
.../boot/dts/amlogic/meson-sm1-khadas-vim3l.dts | 2 +-
drivers/clk/meson/Kconfig | 5 +
drivers/clk/meson/Makefile | 1 +
drivers/clk/meson/g12a.c | 81 ++++-
drivers/clk/meson/vclk.c | 146 ++++++++
drivers/clk/meson/vclk.h | 68 ++++
drivers/gpu/drm/meson/meson_dw_mipi_dsi.c | 7 +
include/dt-bindings/clock/g12a-clkc.h | 2 +
14 files changed, 832 insertions(+), 18 deletions(-)
---
base-commit: 631ac6d680c1d3bff1a7f29825f11db6889fe958
change-id: 20230512-amlogic-v6-4-upstream-dsi-ccf-vim3-b8e5217e1f4a
Best regards,
--
Neil Armstrong <[email protected]>
Add new CTS_ENCL & CTS_ENCL_SEL clocks for the G12A compatible
SoCs, they are used to feed the VPU LCD Pixel encoder used for
DSI display purposes.
Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/clk/meson/g12a.c | 40 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c
index ceabd5f4b2ac..5d62134335c1 100644
--- a/drivers/clk/meson/g12a.c
+++ b/drivers/clk/meson/g12a.c
@@ -3549,6 +3549,22 @@ static struct clk_regmap g12a_cts_encp_sel = {
},
};
+static struct clk_regmap g12a_cts_encl_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = HHI_VIID_CLK_DIV,
+ .mask = 0xf,
+ .shift = 12,
+ .table = mux_table_cts_sel,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "cts_encl_sel",
+ .ops = &clk_regmap_mux_ops,
+ .parent_hws = g12a_cts_parent_hws,
+ .num_parents = ARRAY_SIZE(g12a_cts_parent_hws),
+ .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
+ },
+};
+
static struct clk_regmap g12a_cts_vdac_sel = {
.data = &(struct clk_regmap_mux_data){
.offset = HHI_VIID_CLK_DIV,
@@ -3628,6 +3644,22 @@ static struct clk_regmap g12a_cts_encp = {
},
};
+static struct clk_regmap g12a_cts_encl = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = HHI_VID_CLK_CNTL2,
+ .bit_idx = 3,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "cts_encl",
+ .ops = &clk_regmap_gate_ops,
+ .parent_hws = (const struct clk_hw *[]) {
+ &g12a_cts_encl_sel.hw
+ },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ },
+};
+
static struct clk_regmap g12a_cts_vdac = {
.data = &(struct clk_regmap_gate_data){
.offset = HHI_VID_CLK_CNTL2,
@@ -4407,10 +4439,12 @@ static struct clk_hw *g12a_hw_clks[] = {
[CLKID_VCLK2_DIV12] = &g12a_vclk2_div12.hw,
[CLKID_CTS_ENCI_SEL] = &g12a_cts_enci_sel.hw,
[CLKID_CTS_ENCP_SEL] = &g12a_cts_encp_sel.hw,
+ [CLKID_CTS_ENCL_SEL] = &g12a_cts_encl_sel.hw,
[CLKID_CTS_VDAC_SEL] = &g12a_cts_vdac_sel.hw,
[CLKID_HDMI_TX_SEL] = &g12a_hdmi_tx_sel.hw,
[CLKID_CTS_ENCI] = &g12a_cts_enci.hw,
[CLKID_CTS_ENCP] = &g12a_cts_encp.hw,
+ [CLKID_CTS_ENCL] = &g12a_cts_encl.hw,
[CLKID_CTS_VDAC] = &g12a_cts_vdac.hw,
[CLKID_HDMI_TX] = &g12a_hdmi_tx.hw,
[CLKID_HDMI_SEL] = &g12a_hdmi_sel.hw,
@@ -4632,10 +4666,12 @@ static struct clk_hw *g12b_hw_clks[] = {
[CLKID_VCLK2_DIV12] = &g12a_vclk2_div12.hw,
[CLKID_CTS_ENCI_SEL] = &g12a_cts_enci_sel.hw,
[CLKID_CTS_ENCP_SEL] = &g12a_cts_encp_sel.hw,
+ [CLKID_CTS_ENCL_SEL] = &g12a_cts_encl_sel.hw,
[CLKID_CTS_VDAC_SEL] = &g12a_cts_vdac_sel.hw,
[CLKID_HDMI_TX_SEL] = &g12a_hdmi_tx_sel.hw,
[CLKID_CTS_ENCI] = &g12a_cts_enci.hw,
[CLKID_CTS_ENCP] = &g12a_cts_encp.hw,
+ [CLKID_CTS_ENCL] = &g12a_cts_encl.hw,
[CLKID_CTS_VDAC] = &g12a_cts_vdac.hw,
[CLKID_HDMI_TX] = &g12a_hdmi_tx.hw,
[CLKID_HDMI_SEL] = &g12a_hdmi_sel.hw,
@@ -4892,10 +4928,12 @@ static struct clk_hw *sm1_hw_clks[] = {
[CLKID_VCLK2_DIV12] = &g12a_vclk2_div12.hw,
[CLKID_CTS_ENCI_SEL] = &g12a_cts_enci_sel.hw,
[CLKID_CTS_ENCP_SEL] = &g12a_cts_encp_sel.hw,
+ [CLKID_CTS_ENCL_SEL] = &g12a_cts_encl_sel.hw,
[CLKID_CTS_VDAC_SEL] = &g12a_cts_vdac_sel.hw,
[CLKID_HDMI_TX_SEL] = &g12a_hdmi_tx_sel.hw,
[CLKID_CTS_ENCI] = &g12a_cts_enci.hw,
[CLKID_CTS_ENCP] = &g12a_cts_encp.hw,
+ [CLKID_CTS_ENCL] = &g12a_cts_encl.hw,
[CLKID_CTS_VDAC] = &g12a_cts_vdac.hw,
[CLKID_HDMI_TX] = &g12a_hdmi_tx.hw,
[CLKID_HDMI_SEL] = &g12a_hdmi_sel.hw,
@@ -5123,10 +5161,12 @@ static struct clk_regmap *const g12a_clk_regmaps[] = {
&g12a_vclk2_div12_en,
&g12a_cts_enci_sel,
&g12a_cts_encp_sel,
+ &g12a_cts_encl_sel,
&g12a_cts_vdac_sel,
&g12a_hdmi_tx_sel,
&g12a_cts_enci,
&g12a_cts_encp,
+ &g12a_cts_encl,
&g12a_cts_vdac,
&g12a_hdmi_tx,
&g12a_hdmi_sel,
--
2.34.1
The VCLK and VCLK_DIV clocks have supplementary bits.
The VCLK has a "SOFT RESET" bit to toggle after the whole
VCLK sub-tree rate has been set, this is implemented in
the gate enable callback.
The VCLK_DIV clocks as enable and reset bits used to disable
and reset the divider, associated with CLK_SET_RATE_GATE it ensures
the rate is set while the divider is disabled and in reset mode.
The VCLK_DIV enable bit isn't implemented as a gate since it's part
of the divider logic and vendor does this exact sequence to ensure
the divider is correctly set.
Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/clk/meson/Kconfig | 5 ++
drivers/clk/meson/Makefile | 1 +
drivers/clk/meson/vclk.c | 146 +++++++++++++++++++++++++++++++++++++++++++++
drivers/clk/meson/vclk.h | 68 +++++++++++++++++++++
4 files changed, 220 insertions(+)
diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
index 135da8f2d0b1..83f629515e96 100644
--- a/drivers/clk/meson/Kconfig
+++ b/drivers/clk/meson/Kconfig
@@ -30,6 +30,10 @@ config COMMON_CLK_MESON_VID_PLL_DIV
tristate
select COMMON_CLK_MESON_REGMAP
+config COMMON_CLK_MESON_VCLK
+ tristate
+ select COMMON_CLK_MESON_REGMAP
+
config COMMON_CLK_MESON_CLKC_UTILS
tristate
@@ -140,6 +144,7 @@ config COMMON_CLK_G12A
select COMMON_CLK_MESON_EE_CLKC
select COMMON_CLK_MESON_CPU_DYNDIV
select COMMON_CLK_MESON_VID_PLL_DIV
+ select COMMON_CLK_MESON_VCLK
select MFD_SYSCON
help
Support for the clock controller on Amlogic S905D2, S905X2 and S905Y2
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index cd961cc4f4db..6efeb8c7bd2a 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_COMMON_CLK_MESON_PLL) += clk-pll.o
obj-$(CONFIG_COMMON_CLK_MESON_REGMAP) += clk-regmap.o
obj-$(CONFIG_COMMON_CLK_MESON_SCLK_DIV) += sclk-div.o
obj-$(CONFIG_COMMON_CLK_MESON_VID_PLL_DIV) += vid-pll-div.o
+obj-$(CONFIG_COMMON_CLK_MESON_VCLK) += vclk.o
# Amlogic Clock controllers
diff --git a/drivers/clk/meson/vclk.c b/drivers/clk/meson/vclk.c
new file mode 100644
index 000000000000..0df84403b17f
--- /dev/null
+++ b/drivers/clk/meson/vclk.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 Neil Armstrong <[email protected]>
+ */
+
+#include <linux/module.h>
+#include "vclk.h"
+
+/* The VCLK gate has a supplementary reset bit to pulse after ungating */
+
+static int clk_regmap_vclk_enable(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct clk_regmap_vclk_data *vclk = clk_get_regmap_vclk_data(clk);
+
+ regmap_set_bits(clk->map, vclk->offset, BIT(vclk->enable_bit_idx));
+
+ /* Do a reset pulse */
+ regmap_set_bits(clk->map, vclk->offset, BIT(vclk->reset_bit_idx));
+ regmap_clear_bits(clk->map, vclk->offset, BIT(vclk->reset_bit_idx));
+
+ return 0;
+}
+
+static void clk_regmap_vclk_disable(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct clk_regmap_vclk_data *vclk = clk_get_regmap_vclk_data(clk);
+
+ regmap_clear_bits(clk->map, vclk->offset, BIT(vclk->enable_bit_idx));
+}
+
+static int clk_regmap_vclk_is_enabled(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct clk_regmap_vclk_data *vclk = clk_get_regmap_vclk_data(clk);
+ unsigned int val;
+
+ regmap_read(clk->map, vclk->offset, &val);
+
+ return val & BIT(vclk->enable_bit_idx) ? 1 : 0;
+}
+
+const struct clk_ops clk_regmap_vclk_ops = {
+ .enable = clk_regmap_vclk_enable,
+ .disable = clk_regmap_vclk_disable,
+ .is_enabled = clk_regmap_vclk_is_enabled,
+};
+EXPORT_SYMBOL_GPL(clk_regmap_vclk_ops);
+
+/* The VCLK Divider has supplementary reset & enable bits */
+
+static unsigned long clk_regmap_vclk_div_recalc_rate(struct clk_hw *hw,
+ unsigned long prate)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(clk->map, vclk->offset, &val);
+ if (ret)
+ /* Gives a hint that something is wrong */
+ return 0;
+
+ val >>= vclk->shift;
+ val &= clk_div_mask(vclk->width);
+
+ return divider_recalc_rate(hw, prate, val, vclk->table, vclk->flags,
+ vclk->width);
+}
+
+static int clk_regmap_vclk_div_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
+
+ return divider_determine_rate(hw, req, vclk->table, vclk->width,
+ vclk->flags);
+}
+
+static int clk_regmap_vclk_div_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
+ unsigned int val;
+ int ret;
+
+ ret = divider_get_val(rate, parent_rate, vclk->table, vclk->width,
+ vclk->flags);
+ if (ret < 0)
+ return ret;
+
+ val = (unsigned int)ret << vclk->shift;
+ return regmap_update_bits(clk->map, vclk->offset,
+ clk_div_mask(vclk->width) << vclk->shift, val);
+};
+
+static int clk_regmap_vclk_div_enable(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
+
+ /* Unreset the divider when ungating */
+ regmap_clear_bits(clk->map, vclk->offset, BIT(vclk->reset_bit_idx));
+
+ return regmap_set_bits(clk->map, vclk->offset, BIT(vclk->enable_bit_idx));
+}
+
+static void clk_regmap_vclk_div_disable(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
+
+ /* Reset the divider when gating */
+ regmap_clear_bits(clk->map, vclk->offset, BIT(vclk->enable_bit_idx));
+
+ regmap_set_bits(clk->map, vclk->offset, BIT(vclk->reset_bit_idx));
+}
+
+static int clk_regmap_vclk_div_is_enabled(struct clk_hw *hw)
+{
+ struct clk_regmap *clk = to_clk_regmap(hw);
+ struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
+ unsigned int val;
+
+ regmap_read(clk->map, vclk->offset, &val);
+
+ return val & BIT(vclk->enable_bit_idx) ? 1 : 0;
+}
+
+const struct clk_ops clk_regmap_vclk_div_ops = {
+ .recalc_rate = clk_regmap_vclk_div_recalc_rate,
+ .determine_rate = clk_regmap_vclk_div_determine_rate,
+ .set_rate = clk_regmap_vclk_div_set_rate,
+ .enable = clk_regmap_vclk_div_enable,
+ .disable = clk_regmap_vclk_div_disable,
+ .is_enabled = clk_regmap_vclk_div_is_enabled,
+};
+EXPORT_SYMBOL_GPL(clk_regmap_vclk_div_ops);
+
+MODULE_DESCRIPTION("Amlogic vclk clock driver");
+MODULE_AUTHOR("Neil Armstrong <[email protected]>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/meson/vclk.h b/drivers/clk/meson/vclk.h
new file mode 100644
index 000000000000..90786552a7f3
--- /dev/null
+++ b/drivers/clk/meson/vclk.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2023 Neil Armstrong <[email protected]>
+ */
+
+#ifndef __VCLK_H
+#define __VCLK_H
+
+#include "clk-regmap.h"
+
+/**
+ * struct clk_regmap_vclk_data - vclk regmap backed specific data
+ *
+ * @offset: offset of the register controlling gate
+ * @enable_bit_idx: single bit controlling vclk enable
+ * @reset_bit_idx: single bit controlling vclk reset
+ * @flags: hardware-specific flags
+ *
+ * Flags:
+ * Same as clk_gate except CLK_GATE_HIWORD_MASK which is ignored
+ */
+struct clk_regmap_vclk_data {
+ unsigned int offset;
+ u8 enable_bit_idx;
+ u8 reset_bit_idx;
+ u8 flags;
+};
+
+static inline struct clk_regmap_vclk_data *
+clk_get_regmap_vclk_data(struct clk_regmap *clk)
+{
+ return (struct clk_regmap_vclk_data *)clk->data;
+}
+
+extern const struct clk_ops clk_regmap_vclk_ops;
+
+/**
+ * struct clk_regmap_vclk_div_data - vclk_div regmap back specific data
+ *
+ * @offset: offset of the register controlling the divider
+ * @shift: shift to the divider bit field
+ * @width: width of the divider bit field
+ * @enable_bit_idx: single bit controlling vclk divider enable
+ * @reset_bit_idx: single bit controlling vclk divider reset
+ * @table: array of value/divider pairs, last entry should have div = 0
+ *
+ * Flags:
+ * Same as clk_divider except CLK_DIVIDER_HIWORD_MASK which is ignored
+ */
+struct clk_regmap_vclk_div_data {
+ unsigned int offset;
+ u8 shift;
+ u8 width;
+ u8 enable_bit_idx;
+ u8 reset_bit_idx;
+ const struct clk_div_table *table;
+ u8 flags;
+};
+
+static inline struct clk_regmap_vclk_div_data *
+clk_get_regmap_vclk_div_data(struct clk_regmap *clk)
+{
+ return (struct clk_regmap_vclk_div_data *)clk->data;
+}
+
+extern const struct clk_ops clk_regmap_vclk_div_ops;
+
+#endif /* __VCLK_H */
--
2.34.1
Disable the px_clk when setting the rate to recover a fully
configured and correctly reset VCLK clock tree after the rate
is set.
Fixes: 77d9e1e6b846 ("drm/meson: add support for MIPI-DSI transceiver")
Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/gpu/drm/meson/meson_dw_mipi_dsi.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c
index 57447abf1a29..e995dff8c976 100644
--- a/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c
+++ b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c
@@ -94,6 +94,7 @@ static int dw_mipi_dsi_phy_init(void *priv_data)
return ret;
}
+ clk_disable_unprepare(mipi_dsi->px_clk);
ret = clk_set_rate(mipi_dsi->px_clk, mipi_dsi->mode->clock * 1000);
if (ret) {
@@ -102,6 +103,12 @@ static int dw_mipi_dsi_phy_init(void *priv_data)
return ret;
}
+ ret = clk_prepare_enable(mipi_dsi->px_clk);
+ if (ret) {
+ dev_err(mipi_dsi->dev, "Failed to enable DSI Pixel clock (ret %d)\n", ret);
+ return ret;
+ }
+
switch (mipi_dsi->dsi_device->format) {
case MIPI_DSI_FMT_RGB888:
dpi_data_format = DPI_COLOR_24BIT;
--
2.34.1
Add new CLK ids for the CTS_ENCL and CTS_ENCL_SEL clocks
on G12A compatible SoCs.
Acked-by: Conor Dooley <[email protected]>
Signed-off-by: Neil Armstrong <[email protected]>
---
include/dt-bindings/clock/g12a-clkc.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/include/dt-bindings/clock/g12a-clkc.h b/include/dt-bindings/clock/g12a-clkc.h
index 387767f4e298..636d713f95ff 100644
--- a/include/dt-bindings/clock/g12a-clkc.h
+++ b/include/dt-bindings/clock/g12a-clkc.h
@@ -279,5 +279,7 @@
#define CLKID_MIPI_DSI_PXCLK_DIV 268
#define CLKID_MIPI_DSI_PXCLK_SEL 269
#define CLKID_MIPI_DSI_PXCLK 270
+#define CLKID_CTS_ENCL 271
+#define CLKID_CTS_ENCL_SEL 272
#endif /* __G12A_CLKC_H */
--
2.34.1
This adds a basic devicetree for the MNT Reform2 DIY laptop when using a
CM4 adapter and a BPI-CM4 module.
Co-developed-by: Lukas F. Hartmann <[email protected]>
Signed-off-by: Neil Armstrong <[email protected]>
---
arch/arm64/boot/dts/amlogic/Makefile | 1 +
.../meson-g12b-bananapi-cm4-mnt-reform2.dts | 388 +++++++++++++++++++++
2 files changed, 389 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/Makefile b/arch/arm64/boot/dts/amlogic/Makefile
index 6f61798a109f..c94d9f514dc9 100644
--- a/arch/arm64/boot/dts/amlogic/Makefile
+++ b/arch/arm64/boot/dts/amlogic/Makefile
@@ -12,6 +12,7 @@ dtb-$(CONFIG_ARCH_MESON) += meson-g12a-x96-max.dtb
dtb-$(CONFIG_ARCH_MESON) += meson-g12b-a311d-bananapi-m2s.dtb
dtb-$(CONFIG_ARCH_MESON) += meson-g12b-a311d-khadas-vim3.dtb
dtb-$(CONFIG_ARCH_MESON) += meson-g12b-bananapi-cm4-cm4io.dtb
+dtb-$(CONFIG_ARCH_MESON) += meson-g12b-bananapi-cm4-mnt-reform2.dtb
dtb-$(CONFIG_ARCH_MESON) += meson-g12b-gsking-x.dtb
dtb-$(CONFIG_ARCH_MESON) += meson-g12b-gtking-pro.dtb
dtb-$(CONFIG_ARCH_MESON) += meson-g12b-gtking.dtb
diff --git a/arch/arm64/boot/dts/amlogic/meson-g12b-bananapi-cm4-mnt-reform2.dts b/arch/arm64/boot/dts/amlogic/meson-g12b-bananapi-cm4-mnt-reform2.dts
new file mode 100644
index 000000000000..12c4ff9c9372
--- /dev/null
+++ b/arch/arm64/boot/dts/amlogic/meson-g12b-bananapi-cm4-mnt-reform2.dts
@@ -0,0 +1,388 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2023 Neil Armstrong <[email protected]>
+ * Copyright 2023 MNT Research GmbH
+ */
+
+/dts-v1/;
+
+#include "meson-g12b-bananapi-cm4.dtsi"
+#include <dt-bindings/input/input.h>
+#include <dt-bindings/leds/common.h>
+#include <dt-bindings/sound/meson-g12a-tohdmitx.h>
+
+/ {
+ model = "MNT Reform 2 with BPI-CM4 Module";
+ compatible = "mntre,reform2-cm4", "bananapi,bpi-cm4", "amlogic,a311d", "amlogic,g12b";
+ chassis-type = "laptop";
+
+ aliases {
+ ethernet0 = ðmac;
+ i2c0 = &i2c1;
+ i2c1 = &i2c3;
+ };
+
+ hdmi_connector: hdmi-connector {
+ compatible = "hdmi-connector";
+ type = "a";
+
+ port {
+ hdmi_connector_in: endpoint {
+ remote-endpoint = <&hdmi_tx_tmds_out>;
+ };
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ led-blue {
+ color = <LED_COLOR_ID_BLUE>;
+ function = LED_FUNCTION_STATUS;
+ gpios = <&gpio_ao GPIOAO_7 GPIO_ACTIVE_HIGH>;
+ linux,default-trigger = "heartbeat";
+ };
+
+ led-green {
+ color = <LED_COLOR_ID_GREEN>;
+ function = LED_FUNCTION_STATUS;
+ gpios = <&gpio_ao GPIOAO_2 GPIO_ACTIVE_HIGH>;
+ };
+ };
+
+ sound {
+ compatible = "amlogic,axg-sound-card";
+ model = "MNT-REFORM2-BPI-CM4";
+ audio-widgets = "Headphone", "Headphone Jack",
+ "Speaker", "External Speaker",
+ "Microphone", "Mic Jack";
+ audio-aux-devs = <&tdmout_a>, <&tdmout_b>, <&tdmin_b>;
+ audio-routing = "TDMOUT_A IN 0", "FRDDR_A OUT 0",
+ "TDMOUT_A IN 1", "FRDDR_B OUT 0",
+ "TDMOUT_A IN 2", "FRDDR_C OUT 0",
+ "TDM_A Playback", "TDMOUT_A OUT",
+ "TDMOUT_B IN 0", "FRDDR_A OUT 1",
+ "TDMOUT_B IN 1", "FRDDR_B OUT 1",
+ "TDMOUT_B IN 2", "FRDDR_C OUT 1",
+ "TDM_B Playback", "TDMOUT_B OUT",
+ "TDMIN_B IN 1", "TDM_B Capture",
+ "TDMIN_B IN 4", "TDM_B Loopback",
+ "TODDR_A IN 1", "TDMIN_B OUT",
+ "TODDR_B IN 1", "TDMIN_B OUT",
+ "TODDR_C IN 1", "TDMIN_B OUT",
+ "Headphone Jack", "HP_L",
+ "Headphone Jack", "HP_R",
+ "External Speaker", "SPK_LP",
+ "External Speaker", "SPK_LN",
+ "External Speaker", "SPK_RP",
+ "External Speaker", "SPK_RN",
+ "LINPUT1", "Mic Jack",
+ "Mic Jack", "MICB";
+
+ assigned-clocks = <&clkc CLKID_MPLL2>,
+ <&clkc CLKID_MPLL0>,
+ <&clkc CLKID_MPLL1>;
+ assigned-clock-parents = <0>, <0>, <0>;
+ assigned-clock-rates = <294912000>,
+ <270950400>,
+ <393216000>;
+
+ dai-link-0 {
+ sound-dai = <&frddr_a>;
+ };
+
+ dai-link-1 {
+ sound-dai = <&frddr_b>;
+ };
+
+ dai-link-2 {
+ sound-dai = <&frddr_c>;
+ };
+
+ dai-link-3 {
+ sound-dai = <&toddr_a>;
+ };
+
+ dai-link-4 {
+ sound-dai = <&toddr_b>;
+ };
+
+ dai-link-5 {
+ sound-dai = <&toddr_c>;
+ };
+
+ /* 8ch hdmi interface */
+ dai-link-6 {
+ sound-dai = <&tdmif_a>;
+ dai-format = "i2s";
+ dai-tdm-slot-tx-mask-0 = <1 1>;
+ dai-tdm-slot-tx-mask-1 = <1 1>;
+ dai-tdm-slot-tx-mask-2 = <1 1>;
+ dai-tdm-slot-tx-mask-3 = <1 1>;
+ mclk-fs = <256>;
+
+ codec {
+ sound-dai = <&tohdmitx TOHDMITX_I2S_IN_A>;
+ };
+ };
+
+ /* Analog Audio */
+ dai-link-7 {
+ sound-dai = <&tdmif_b>;
+ dai-format = "i2s";
+ dai-tdm-slot-tx-mask-0 = <1 1>;
+ mclk-fs = <256>;
+
+ codec {
+ sound-dai = <&wm8960>;
+ };
+ };
+
+ /* hdmi glue */
+ dai-link-8 {
+ sound-dai = <&tohdmitx TOHDMITX_I2S_OUT>;
+
+ codec {
+ sound-dai = <&hdmi_tx>;
+ };
+ };
+ };
+
+ reg_main_1v8: regulator-main-1v8 {
+ compatible = "regulator-fixed";
+ regulator-name = "1V8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ vin-supply = <®_main_3v3>;
+ };
+
+ reg_main_1v2: regulator-main-1v2 {
+ compatible = "regulator-fixed";
+ regulator-name = "1V2";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ vin-supply = <®_main_5v>;
+ };
+
+ reg_main_3v3: regulator-main-3v3 {
+ compatible = "regulator-fixed";
+ regulator-name = "3V3";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ reg_main_5v: regulator-main-5v {
+ compatible = "regulator-fixed";
+ regulator-name = "5V";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ };
+
+ reg_main_usb: regulator-main-usb {
+ compatible = "regulator-fixed";
+ regulator-name = "USB_PWR";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ vin-supply = <®_main_5v>;
+ };
+
+ backlight: backlight {
+ compatible = "pwm-backlight";
+ pwms = <&pwm_AO_ab 0 10000 0>;
+ power-supply = <®_main_usb>;
+ enable-gpios = <&gpio 58 GPIO_ACTIVE_HIGH>;
+ brightness-levels = <0 32 64 128 160 200 255>;
+ default-brightness-level = <6>;
+
+ status = "okay";
+ };
+
+ panel {
+ compatible = "innolux,n125hce-gn1", "simple-panel";
+ power-supply = <®_main_3v3>;
+ backlight = <&backlight>;
+ no-hpd;
+
+ status = "okay";
+
+ port {
+ panel_in: endpoint {
+ remote-endpoint = <&edp_bridge_out>;
+ };
+ };
+ };
+
+ clock_12288: clock_12288 {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <12288000>;
+ };
+};
+
+&mipi_analog_dphy {
+ status = "okay";
+};
+
+&mipi_dphy {
+ status = "okay";
+};
+
+&mipi_dsi {
+ status = "okay";
+
+ assigned-clocks = <&clkc CLKID_GP0_PLL>,
+ <&clkc CLKID_MIPI_DSI_PXCLK_SEL>,
+ <&clkc CLKID_MIPI_DSI_PXCLK>,
+ <&clkc CLKID_CTS_ENCL_SEL>,
+ <&clkc CLKID_VCLK2_SEL>;
+ assigned-clock-parents = <0>,
+ <&clkc CLKID_GP0_PLL>,
+ <0>,
+ <&clkc CLKID_VCLK2_DIV1>,
+ <&clkc CLKID_GP0_PLL>;
+ assigned-clock-rates = <936000000>,
+ <0>,
+ <936000000>,
+ <0>,
+ <0>;
+};
+
+&mipi_dsi_panel_port {
+ mipi_dsi_out: endpoint {
+ remote-endpoint = <&edp_bridge_in>;
+ };
+};
+
+&cecb_AO {
+ status = "okay";
+};
+
+ðmac {
+ status = "okay";
+};
+
+&hdmi_tx {
+ status = "okay";
+};
+
+&hdmi_tx_tmds_port {
+ hdmi_tx_tmds_out: endpoint {
+ remote-endpoint = <&hdmi_connector_in>;
+ };
+};
+
+&pwm_AO_ab {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pwm_ao_a_pins>;
+ status = "okay";
+};
+
+&i2c0 {
+ status = "okay";
+};
+
+&i2c3 {
+ status = "okay";
+
+ edp_bridge: bridge@2c {
+ compatible = "ti,sn65dsi86";
+ reg = <0x2c>;
+ enable-gpios = <&gpio GPIOX_10 GPIO_ACTIVE_HIGH>; // PIN_24 / GPIO8
+ vccio-supply = <®_main_1v8>;
+ vpll-supply = <®_main_1v8>;
+ vcca-supply = <®_main_1v2>;
+ vcc-supply = <®_main_1v2>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ edp_bridge_in: endpoint {
+ remote-endpoint = <&mipi_dsi_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ edp_bridge_out: endpoint {
+ remote-endpoint = <&panel_in>;
+ };
+ };
+ };
+ };
+};
+
+&i2c2 {
+ status = "okay";
+
+ wm8960: codec@1a {
+ compatible = "wlf,wm8960";
+ reg = <0x1a>;
+ clocks = <&clock_12288>;
+ clock-names = "mclk";
+ #sound-dai-cells = <0>;
+ wlf,shared-lrclk;
+ };
+
+ rtc@68 {
+ compatible = "nxp,pcf8523";
+ reg = <0x68>;
+ };
+};
+
+&pcie {
+ status = "okay";
+};
+
+&sd_emmc_b {
+ status = "okay";
+};
+
+&tdmif_a {
+ status = "okay";
+};
+
+&tdmout_a {
+ status = "okay";
+};
+
+&tdmif_b {
+ pinctrl-0 = <&tdm_b_dout0_pins>, <&tdm_b_fs_pins>, <&tdm_b_sclk_pins>, <&tdm_b_din1_pins>;
+ pinctrl-names = "default";
+
+ assigned-clocks = <&clkc_audio AUD_CLKID_TDM_SCLK_PAD1>,
+ <&clkc_audio AUD_CLKID_TDM_LRCLK_PAD1>;
+ assigned-clock-parents = <&clkc_audio AUD_CLKID_MST_B_SCLK>,
+ <&clkc_audio AUD_CLKID_MST_B_LRCLK>;
+ assigned-clock-rates = <0>, <0>;
+};
+
+&tdmin_b {
+ status = "okay";
+};
+
+&toddr_a {
+ status = "okay";
+};
+
+&toddr_b {
+ status = "okay";
+};
+
+&toddr_c {
+ status = "okay";
+};
+
+&tohdmitx {
+ status = "okay";
+};
+
+&usb {
+ dr_mode = "host";
+
+ status = "okay";
+};
--
2.34.1
The MNT Reform 2 CM4 adapter can be populated with any Raspberry Pi CM4
compatible module such as a BPI-CM4 Module, document that.
Acked-by: Conor Dooley <[email protected]>
Signed-off-by: Neil Armstrong <[email protected]>
---
Documentation/devicetree/bindings/arm/amlogic.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/arm/amlogic.yaml b/Documentation/devicetree/bindings/arm/amlogic.yaml
index 08d59842655c..c237afef5093 100644
--- a/Documentation/devicetree/bindings/arm/amlogic.yaml
+++ b/Documentation/devicetree/bindings/arm/amlogic.yaml
@@ -163,6 +163,7 @@ properties:
items:
- enum:
- bananapi,bpi-cm4io
+ - mntre,reform2-cm4
- const: bananapi,bpi-cm4
- const: amlogic,a311d
- const: amlogic,g12b
--
2.34.1
On Thu 03 Aug 2023 at 14:03, Neil Armstrong <[email protected]> wrote:
> The VCLK and VCLK_DIV clocks have supplementary bits.
>
> The VCLK has a "SOFT RESET" bit to toggle after the whole
> VCLK sub-tree rate has been set, this is implemented in
> the gate enable callback.
>
> The VCLK_DIV clocks as enable and reset bits used to disable
> and reset the divider, associated with CLK_SET_RATE_GATE it ensures
> the rate is set while the divider is disabled and in reset mode.
>
> The VCLK_DIV enable bit isn't implemented as a gate since it's part
> of the divider logic and vendor does this exact sequence to ensure
> the divider is correctly set.
Unless there is reason, I'd prefer if this driver was using 'struct
parm', like the rest of amlogic custom clock drivers, for consistency.
>
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> drivers/clk/meson/Kconfig | 5 ++
> drivers/clk/meson/Makefile | 1 +
> drivers/clk/meson/vclk.c | 146 +++++++++++++++++++++++++++++++++++++++++++++
> drivers/clk/meson/vclk.h | 68 +++++++++++++++++++++
> 4 files changed, 220 insertions(+)
>
> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
> index 135da8f2d0b1..83f629515e96 100644
> --- a/drivers/clk/meson/Kconfig
> +++ b/drivers/clk/meson/Kconfig
> @@ -30,6 +30,10 @@ config COMMON_CLK_MESON_VID_PLL_DIV
> tristate
> select COMMON_CLK_MESON_REGMAP
>
> +config COMMON_CLK_MESON_VCLK
> + tristate
> + select COMMON_CLK_MESON_REGMAP
> +
> config COMMON_CLK_MESON_CLKC_UTILS
> tristate
>
> @@ -140,6 +144,7 @@ config COMMON_CLK_G12A
> select COMMON_CLK_MESON_EE_CLKC
> select COMMON_CLK_MESON_CPU_DYNDIV
> select COMMON_CLK_MESON_VID_PLL_DIV
> + select COMMON_CLK_MESON_VCLK
> select MFD_SYSCON
> help
> Support for the clock controller on Amlogic S905D2, S905X2 and S905Y2
> diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
> index cd961cc4f4db..6efeb8c7bd2a 100644
> --- a/drivers/clk/meson/Makefile
> +++ b/drivers/clk/meson/Makefile
> @@ -12,6 +12,7 @@ obj-$(CONFIG_COMMON_CLK_MESON_PLL) += clk-pll.o
> obj-$(CONFIG_COMMON_CLK_MESON_REGMAP) += clk-regmap.o
> obj-$(CONFIG_COMMON_CLK_MESON_SCLK_DIV) += sclk-div.o
> obj-$(CONFIG_COMMON_CLK_MESON_VID_PLL_DIV) += vid-pll-div.o
> +obj-$(CONFIG_COMMON_CLK_MESON_VCLK) += vclk.o
>
> # Amlogic Clock controllers
>
> diff --git a/drivers/clk/meson/vclk.c b/drivers/clk/meson/vclk.c
> new file mode 100644
> index 000000000000..0df84403b17f
> --- /dev/null
> +++ b/drivers/clk/meson/vclk.c
> @@ -0,0 +1,146 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2023 Neil Armstrong <[email protected]>
> + */
> +
> +#include <linux/module.h>
> +#include "vclk.h"
> +
> +/* The VCLK gate has a supplementary reset bit to pulse after ungating */
> +
> +static int clk_regmap_vclk_enable(struct clk_hw *hw)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct clk_regmap_vclk_data *vclk = clk_get_regmap_vclk_data(clk);
> +
> + regmap_set_bits(clk->map, vclk->offset, BIT(vclk->enable_bit_idx));
> +
> + /* Do a reset pulse */
> + regmap_set_bits(clk->map, vclk->offset, BIT(vclk->reset_bit_idx));
> + regmap_clear_bits(clk->map, vclk->offset, BIT(vclk->reset_bit_idx));
> +
> + return 0;
> +}
> +
> +static void clk_regmap_vclk_disable(struct clk_hw *hw)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct clk_regmap_vclk_data *vclk = clk_get_regmap_vclk_data(clk);
> +
> + regmap_clear_bits(clk->map, vclk->offset, BIT(vclk->enable_bit_idx));
> +}
> +
> +static int clk_regmap_vclk_is_enabled(struct clk_hw *hw)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct clk_regmap_vclk_data *vclk = clk_get_regmap_vclk_data(clk);
> + unsigned int val;
> +
> + regmap_read(clk->map, vclk->offset, &val);
> +
> + return val & BIT(vclk->enable_bit_idx) ? 1 : 0;
> +}
> +
> +const struct clk_ops clk_regmap_vclk_ops = {
> + .enable = clk_regmap_vclk_enable,
> + .disable = clk_regmap_vclk_disable,
> + .is_enabled = clk_regmap_vclk_is_enabled,
> +};
> +EXPORT_SYMBOL_GPL(clk_regmap_vclk_ops);
> +
> +/* The VCLK Divider has supplementary reset & enable bits */
> +
> +static unsigned long clk_regmap_vclk_div_recalc_rate(struct clk_hw *hw,
> + unsigned long prate)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
> + unsigned int val;
> + int ret;
> +
> + ret = regmap_read(clk->map, vclk->offset, &val);
> + if (ret)
> + /* Gives a hint that something is wrong */
> + return 0;
> +
> + val >>= vclk->shift;
> + val &= clk_div_mask(vclk->width);
> +
> + return divider_recalc_rate(hw, prate, val, vclk->table, vclk->flags,
> + vclk->width);
> +}
> +
> +static int clk_regmap_vclk_div_determine_rate(struct clk_hw *hw,
> + struct clk_rate_request *req)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
> +
> + return divider_determine_rate(hw, req, vclk->table, vclk->width,
> + vclk->flags);
> +}
> +
> +static int clk_regmap_vclk_div_set_rate(struct clk_hw *hw, unsigned long rate,
> + unsigned long parent_rate)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
> + unsigned int val;
> + int ret;
> +
> + ret = divider_get_val(rate, parent_rate, vclk->table, vclk->width,
> + vclk->flags);
> + if (ret < 0)
> + return ret;
> +
> + val = (unsigned int)ret << vclk->shift;
> + return regmap_update_bits(clk->map, vclk->offset,
> + clk_div_mask(vclk->width) << vclk->shift, val);
> +};
> +
> +static int clk_regmap_vclk_div_enable(struct clk_hw *hw)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
> +
> + /* Unreset the divider when ungating */
> + regmap_clear_bits(clk->map, vclk->offset, BIT(vclk->reset_bit_idx));
> +
> + return regmap_set_bits(clk->map, vclk->offset, BIT(vclk->enable_bit_idx));
> +}
> +
> +static void clk_regmap_vclk_div_disable(struct clk_hw *hw)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
> +
> + /* Reset the divider when gating */
> + regmap_clear_bits(clk->map, vclk->offset, BIT(vclk->enable_bit_idx));
> +
> + regmap_set_bits(clk->map, vclk->offset, BIT(vclk->reset_bit_idx));
> +}
> +
> +static int clk_regmap_vclk_div_is_enabled(struct clk_hw *hw)
> +{
> + struct clk_regmap *clk = to_clk_regmap(hw);
> + struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
> + unsigned int val;
> +
> + regmap_read(clk->map, vclk->offset, &val);
> +
> + return val & BIT(vclk->enable_bit_idx) ? 1 : 0;
> +}
> +
> +const struct clk_ops clk_regmap_vclk_div_ops = {
> + .recalc_rate = clk_regmap_vclk_div_recalc_rate,
> + .determine_rate = clk_regmap_vclk_div_determine_rate,
> + .set_rate = clk_regmap_vclk_div_set_rate,
> + .enable = clk_regmap_vclk_div_enable,
> + .disable = clk_regmap_vclk_div_disable,
> + .is_enabled = clk_regmap_vclk_div_is_enabled,
> +};
> +EXPORT_SYMBOL_GPL(clk_regmap_vclk_div_ops);
> +
> +MODULE_DESCRIPTION("Amlogic vclk clock driver");
> +MODULE_AUTHOR("Neil Armstrong <[email protected]>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/clk/meson/vclk.h b/drivers/clk/meson/vclk.h
> new file mode 100644
> index 000000000000..90786552a7f3
> --- /dev/null
> +++ b/drivers/clk/meson/vclk.h
> @@ -0,0 +1,68 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2023 Neil Armstrong <[email protected]>
> + */
> +
> +#ifndef __VCLK_H
> +#define __VCLK_H
> +
> +#include "clk-regmap.h"
> +
> +/**
> + * struct clk_regmap_vclk_data - vclk regmap backed specific data
> + *
> + * @offset: offset of the register controlling gate
> + * @enable_bit_idx: single bit controlling vclk enable
> + * @reset_bit_idx: single bit controlling vclk reset
> + * @flags: hardware-specific flags
> + *
> + * Flags:
> + * Same as clk_gate except CLK_GATE_HIWORD_MASK which is ignored
> + */
> +struct clk_regmap_vclk_data {
> + unsigned int offset;
> + u8 enable_bit_idx;
> + u8 reset_bit_idx;
> + u8 flags;
> +};
> +
> +static inline struct clk_regmap_vclk_data *
> +clk_get_regmap_vclk_data(struct clk_regmap *clk)
> +{
> + return (struct clk_regmap_vclk_data *)clk->data;
> +}
> +
> +extern const struct clk_ops clk_regmap_vclk_ops;
> +
> +/**
> + * struct clk_regmap_vclk_div_data - vclk_div regmap back specific data
> + *
> + * @offset: offset of the register controlling the divider
> + * @shift: shift to the divider bit field
> + * @width: width of the divider bit field
> + * @enable_bit_idx: single bit controlling vclk divider enable
> + * @reset_bit_idx: single bit controlling vclk divider reset
> + * @table: array of value/divider pairs, last entry should have div = 0
> + *
> + * Flags:
> + * Same as clk_divider except CLK_DIVIDER_HIWORD_MASK which is ignored
> + */
> +struct clk_regmap_vclk_div_data {
> + unsigned int offset;
> + u8 shift;
> + u8 width;
> + u8 enable_bit_idx;
> + u8 reset_bit_idx;
> + const struct clk_div_table *table;
> + u8 flags;
> +};
> +
> +static inline struct clk_regmap_vclk_div_data *
> +clk_get_regmap_vclk_div_data(struct clk_regmap *clk)
> +{
> + return (struct clk_regmap_vclk_div_data *)clk->data;
> +}
> +
> +extern const struct clk_ops clk_regmap_vclk_div_ops;
> +
> +#endif /* __VCLK_H */
On 04/08/2023 09:33, Jerome Brunet wrote:
>
> On Thu 03 Aug 2023 at 14:03, Neil Armstrong <[email protected]> wrote:
>
>> The VCLK and VCLK_DIV clocks have supplementary bits.
>>
>> The VCLK has a "SOFT RESET" bit to toggle after the whole
>> VCLK sub-tree rate has been set, this is implemented in
>> the gate enable callback.
>>
>> The VCLK_DIV clocks as enable and reset bits used to disable
>> and reset the divider, associated with CLK_SET_RATE_GATE it ensures
>> the rate is set while the divider is disabled and in reset mode.
>>
>> The VCLK_DIV enable bit isn't implemented as a gate since it's part
>> of the divider logic and vendor does this exact sequence to ensure
>> the divider is correctly set.
>
> Unless there is reason, I'd prefer if this driver was using 'struct
> parm', like the rest of amlogic custom clock drivers, for consistency.
Ack
thx,
Neil
>
>>
>> Signed-off-by: Neil Armstrong <[email protected]>
>> ---
>> drivers/clk/meson/Kconfig | 5 ++
>> drivers/clk/meson/Makefile | 1 +
>> drivers/clk/meson/vclk.c | 146 +++++++++++++++++++++++++++++++++++++++++++++
>> drivers/clk/meson/vclk.h | 68 +++++++++++++++++++++
>> 4 files changed, 220 insertions(+)
>>
>> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
>> index 135da8f2d0b1..83f629515e96 100644
>> --- a/drivers/clk/meson/Kconfig
>> +++ b/drivers/clk/meson/Kconfig
>> @@ -30,6 +30,10 @@ config COMMON_CLK_MESON_VID_PLL_DIV
>> tristate
>> select COMMON_CLK_MESON_REGMAP
>>
>> +config COMMON_CLK_MESON_VCLK
>> + tristate
>> + select COMMON_CLK_MESON_REGMAP
>> +
>> config COMMON_CLK_MESON_CLKC_UTILS
>> tristate
>>
>> @@ -140,6 +144,7 @@ config COMMON_CLK_G12A
>> select COMMON_CLK_MESON_EE_CLKC
>> select COMMON_CLK_MESON_CPU_DYNDIV
>> select COMMON_CLK_MESON_VID_PLL_DIV
>> + select COMMON_CLK_MESON_VCLK
>> select MFD_SYSCON
>> help
>> Support for the clock controller on Amlogic S905D2, S905X2 and S905Y2
>> diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
>> index cd961cc4f4db..6efeb8c7bd2a 100644
>> --- a/drivers/clk/meson/Makefile
>> +++ b/drivers/clk/meson/Makefile
>> @@ -12,6 +12,7 @@ obj-$(CONFIG_COMMON_CLK_MESON_PLL) += clk-pll.o
>> obj-$(CONFIG_COMMON_CLK_MESON_REGMAP) += clk-regmap.o
>> obj-$(CONFIG_COMMON_CLK_MESON_SCLK_DIV) += sclk-div.o
>> obj-$(CONFIG_COMMON_CLK_MESON_VID_PLL_DIV) += vid-pll-div.o
>> +obj-$(CONFIG_COMMON_CLK_MESON_VCLK) += vclk.o
>>
>> # Amlogic Clock controllers
>>
>> diff --git a/drivers/clk/meson/vclk.c b/drivers/clk/meson/vclk.c
>> new file mode 100644
>> index 000000000000..0df84403b17f
>> --- /dev/null
>> +++ b/drivers/clk/meson/vclk.c
>> @@ -0,0 +1,146 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (c) 2023 Neil Armstrong <[email protected]>
>> + */
>> +
>> +#include <linux/module.h>
>> +#include "vclk.h"
>> +
>> +/* The VCLK gate has a supplementary reset bit to pulse after ungating */
>> +
>> +static int clk_regmap_vclk_enable(struct clk_hw *hw)
>> +{
>> + struct clk_regmap *clk = to_clk_regmap(hw);
>> + struct clk_regmap_vclk_data *vclk = clk_get_regmap_vclk_data(clk);
>> +
>> + regmap_set_bits(clk->map, vclk->offset, BIT(vclk->enable_bit_idx));
>> +
>> + /* Do a reset pulse */
>> + regmap_set_bits(clk->map, vclk->offset, BIT(vclk->reset_bit_idx));
>> + regmap_clear_bits(clk->map, vclk->offset, BIT(vclk->reset_bit_idx));
>> +
>> + return 0;
>> +}
>> +
>> +static void clk_regmap_vclk_disable(struct clk_hw *hw)
>> +{
>> + struct clk_regmap *clk = to_clk_regmap(hw);
>> + struct clk_regmap_vclk_data *vclk = clk_get_regmap_vclk_data(clk);
>> +
>> + regmap_clear_bits(clk->map, vclk->offset, BIT(vclk->enable_bit_idx));
>> +}
>> +
>> +static int clk_regmap_vclk_is_enabled(struct clk_hw *hw)
>> +{
>> + struct clk_regmap *clk = to_clk_regmap(hw);
>> + struct clk_regmap_vclk_data *vclk = clk_get_regmap_vclk_data(clk);
>> + unsigned int val;
>> +
>> + regmap_read(clk->map, vclk->offset, &val);
>> +
>> + return val & BIT(vclk->enable_bit_idx) ? 1 : 0;
>> +}
>> +
>> +const struct clk_ops clk_regmap_vclk_ops = {
>> + .enable = clk_regmap_vclk_enable,
>> + .disable = clk_regmap_vclk_disable,
>> + .is_enabled = clk_regmap_vclk_is_enabled,
>> +};
>> +EXPORT_SYMBOL_GPL(clk_regmap_vclk_ops);
>> +
>> +/* The VCLK Divider has supplementary reset & enable bits */
>> +
>> +static unsigned long clk_regmap_vclk_div_recalc_rate(struct clk_hw *hw,
>> + unsigned long prate)
>> +{
>> + struct clk_regmap *clk = to_clk_regmap(hw);
>> + struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
>> + unsigned int val;
>> + int ret;
>> +
>> + ret = regmap_read(clk->map, vclk->offset, &val);
>> + if (ret)
>> + /* Gives a hint that something is wrong */
>> + return 0;
>> +
>> + val >>= vclk->shift;
>> + val &= clk_div_mask(vclk->width);
>> +
>> + return divider_recalc_rate(hw, prate, val, vclk->table, vclk->flags,
>> + vclk->width);
>> +}
>> +
>> +static int clk_regmap_vclk_div_determine_rate(struct clk_hw *hw,
>> + struct clk_rate_request *req)
>> +{
>> + struct clk_regmap *clk = to_clk_regmap(hw);
>> + struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
>> +
>> + return divider_determine_rate(hw, req, vclk->table, vclk->width,
>> + vclk->flags);
>> +}
>> +
>> +static int clk_regmap_vclk_div_set_rate(struct clk_hw *hw, unsigned long rate,
>> + unsigned long parent_rate)
>> +{
>> + struct clk_regmap *clk = to_clk_regmap(hw);
>> + struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
>> + unsigned int val;
>> + int ret;
>> +
>> + ret = divider_get_val(rate, parent_rate, vclk->table, vclk->width,
>> + vclk->flags);
>> + if (ret < 0)
>> + return ret;
>> +
>> + val = (unsigned int)ret << vclk->shift;
>> + return regmap_update_bits(clk->map, vclk->offset,
>> + clk_div_mask(vclk->width) << vclk->shift, val);
>> +};
>> +
>> +static int clk_regmap_vclk_div_enable(struct clk_hw *hw)
>> +{
>> + struct clk_regmap *clk = to_clk_regmap(hw);
>> + struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
>> +
>> + /* Unreset the divider when ungating */
>> + regmap_clear_bits(clk->map, vclk->offset, BIT(vclk->reset_bit_idx));
>> +
>> + return regmap_set_bits(clk->map, vclk->offset, BIT(vclk->enable_bit_idx));
>> +}
>> +
>> +static void clk_regmap_vclk_div_disable(struct clk_hw *hw)
>> +{
>> + struct clk_regmap *clk = to_clk_regmap(hw);
>> + struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
>> +
>> + /* Reset the divider when gating */
>> + regmap_clear_bits(clk->map, vclk->offset, BIT(vclk->enable_bit_idx));
>> +
>> + regmap_set_bits(clk->map, vclk->offset, BIT(vclk->reset_bit_idx));
>> +}
>> +
>> +static int clk_regmap_vclk_div_is_enabled(struct clk_hw *hw)
>> +{
>> + struct clk_regmap *clk = to_clk_regmap(hw);
>> + struct clk_regmap_vclk_div_data *vclk = clk_get_regmap_vclk_div_data(clk);
>> + unsigned int val;
>> +
>> + regmap_read(clk->map, vclk->offset, &val);
>> +
>> + return val & BIT(vclk->enable_bit_idx) ? 1 : 0;
>> +}
>> +
>> +const struct clk_ops clk_regmap_vclk_div_ops = {
>> + .recalc_rate = clk_regmap_vclk_div_recalc_rate,
>> + .determine_rate = clk_regmap_vclk_div_determine_rate,
>> + .set_rate = clk_regmap_vclk_div_set_rate,
>> + .enable = clk_regmap_vclk_div_enable,
>> + .disable = clk_regmap_vclk_div_disable,
>> + .is_enabled = clk_regmap_vclk_div_is_enabled,
>> +};
>> +EXPORT_SYMBOL_GPL(clk_regmap_vclk_div_ops);
>> +
>> +MODULE_DESCRIPTION("Amlogic vclk clock driver");
>> +MODULE_AUTHOR("Neil Armstrong <[email protected]>");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/clk/meson/vclk.h b/drivers/clk/meson/vclk.h
>> new file mode 100644
>> index 000000000000..90786552a7f3
>> --- /dev/null
>> +++ b/drivers/clk/meson/vclk.h
>> @@ -0,0 +1,68 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (c) 2023 Neil Armstrong <[email protected]>
>> + */
>> +
>> +#ifndef __VCLK_H
>> +#define __VCLK_H
>> +
>> +#include "clk-regmap.h"
>> +
>> +/**
>> + * struct clk_regmap_vclk_data - vclk regmap backed specific data
>> + *
>> + * @offset: offset of the register controlling gate
>> + * @enable_bit_idx: single bit controlling vclk enable
>> + * @reset_bit_idx: single bit controlling vclk reset
>> + * @flags: hardware-specific flags
>> + *
>> + * Flags:
>> + * Same as clk_gate except CLK_GATE_HIWORD_MASK which is ignored
>> + */
>> +struct clk_regmap_vclk_data {
>> + unsigned int offset;
>> + u8 enable_bit_idx;
>> + u8 reset_bit_idx;
>> + u8 flags;
>> +};
>> +
>> +static inline struct clk_regmap_vclk_data *
>> +clk_get_regmap_vclk_data(struct clk_regmap *clk)
>> +{
>> + return (struct clk_regmap_vclk_data *)clk->data;
>> +}
>> +
>> +extern const struct clk_ops clk_regmap_vclk_ops;
>> +
>> +/**
>> + * struct clk_regmap_vclk_div_data - vclk_div regmap back specific data
>> + *
>> + * @offset: offset of the register controlling the divider
>> + * @shift: shift to the divider bit field
>> + * @width: width of the divider bit field
>> + * @enable_bit_idx: single bit controlling vclk divider enable
>> + * @reset_bit_idx: single bit controlling vclk divider reset
>> + * @table: array of value/divider pairs, last entry should have div = 0
>> + *
>> + * Flags:
>> + * Same as clk_divider except CLK_DIVIDER_HIWORD_MASK which is ignored
>> + */
>> +struct clk_regmap_vclk_div_data {
>> + unsigned int offset;
>> + u8 shift;
>> + u8 width;
>> + u8 enable_bit_idx;
>> + u8 reset_bit_idx;
>> + const struct clk_div_table *table;
>> + u8 flags;
>> +};
>> +
>> +static inline struct clk_regmap_vclk_div_data *
>> +clk_get_regmap_vclk_div_data(struct clk_regmap *clk)
>> +{
>> + return (struct clk_regmap_vclk_div_data *)clk->data;
>> +}
>> +
>> +extern const struct clk_ops clk_regmap_vclk_div_ops;
>> +
>> +#endif /* __VCLK_H */
>
On 04/08/2023 11:59, Jerome Brunet wrote:
>
> On Thu 03 Aug 2023 at 14:03, Neil Armstrong <[email protected]> wrote:
>
>> Add new CTS_ENCL & CTS_ENCL_SEL clocks for the G12A compatible
>> SoCs, they are used to feed the VPU LCD Pixel encoder used for
>> DSI display purposes.
>>
>> Signed-off-by: Neil Armstrong <[email protected]>
>> ---
>> drivers/clk/meson/g12a.c | 40 ++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 40 insertions(+)
>>
>> diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c
>> index ceabd5f4b2ac..5d62134335c1 100644
>> --- a/drivers/clk/meson/g12a.c
>> +++ b/drivers/clk/meson/g12a.c
>> @@ -3549,6 +3549,22 @@ static struct clk_regmap g12a_cts_encp_sel = {
>> },
>> };
>>
>> +static struct clk_regmap g12a_cts_encl_sel = {
>> + .data = &(struct clk_regmap_mux_data){
>> + .offset = HHI_VIID_CLK_DIV,
>> + .mask = 0xf,
>> + .shift = 12,
>> + .table = mux_table_cts_sel,
>> + },
>> + .hw.init = &(struct clk_init_data){
>> + .name = "cts_encl_sel",
>> + .ops = &clk_regmap_mux_ops,
>> + .parent_hws = g12a_cts_parent_hws,
>> + .num_parents = ARRAY_SIZE(g12a_cts_parent_hws),
>> + .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
>
> Why nocache ?
> This is usually used when the consumer driver is poking around behind
> CCF back.
>
> Any chance this can use assigned-parent or CCF directly ?
>
>> + },
>> +};
>> +
>> static struct clk_regmap g12a_cts_vdac_sel = {
>> .data = &(struct clk_regmap_mux_data){
>> .offset = HHI_VIID_CLK_DIV,
>> @@ -3628,6 +3644,22 @@ static struct clk_regmap g12a_cts_encp = {
>> },
>> };
>>
>> +static struct clk_regmap g12a_cts_encl = {
>> + .data = &(struct clk_regmap_gate_data){
>> + .offset = HHI_VID_CLK_CNTL2,
>> + .bit_idx = 3,
>> + },
>> + .hw.init = &(struct clk_init_data) {
>> + .name = "cts_encl",
>> + .ops = &clk_regmap_gate_ops,
>> + .parent_hws = (const struct clk_hw *[]) {
>> + &g12a_cts_encl_sel.hw
>> + },
>> + .num_parents = 1,
>> + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>
> What is the reason for IGNORE_UNUSED ?
> If you need to keep the clock on while the driver comes up, please
> document it here.
The clocks are added like other video clock, unused & nocache, then
enabled correctly in patch 4 to be used by CCF.
Neil
>
>> + },
>> +};
>> +
>> static struct clk_regmap g12a_cts_vdac = {
>> .data = &(struct clk_regmap_gate_data){
>> .offset = HHI_VID_CLK_CNTL2,
>> @@ -4407,10 +4439,12 @@ static struct clk_hw *g12a_hw_clks[] = {
>> [CLKID_VCLK2_DIV12] = &g12a_vclk2_div12.hw,
>> [CLKID_CTS_ENCI_SEL] = &g12a_cts_enci_sel.hw,
>> [CLKID_CTS_ENCP_SEL] = &g12a_cts_encp_sel.hw,
>> + [CLKID_CTS_ENCL_SEL] = &g12a_cts_encl_sel.hw,
>> [CLKID_CTS_VDAC_SEL] = &g12a_cts_vdac_sel.hw,
>> [CLKID_HDMI_TX_SEL] = &g12a_hdmi_tx_sel.hw,
>> [CLKID_CTS_ENCI] = &g12a_cts_enci.hw,
>> [CLKID_CTS_ENCP] = &g12a_cts_encp.hw,
>> + [CLKID_CTS_ENCL] = &g12a_cts_encl.hw,
>> [CLKID_CTS_VDAC] = &g12a_cts_vdac.hw,
>> [CLKID_HDMI_TX] = &g12a_hdmi_tx.hw,
>> [CLKID_HDMI_SEL] = &g12a_hdmi_sel.hw,
>> @@ -4632,10 +4666,12 @@ static struct clk_hw *g12b_hw_clks[] = {
>> [CLKID_VCLK2_DIV12] = &g12a_vclk2_div12.hw,
>> [CLKID_CTS_ENCI_SEL] = &g12a_cts_enci_sel.hw,
>> [CLKID_CTS_ENCP_SEL] = &g12a_cts_encp_sel.hw,
>> + [CLKID_CTS_ENCL_SEL] = &g12a_cts_encl_sel.hw,
>> [CLKID_CTS_VDAC_SEL] = &g12a_cts_vdac_sel.hw,
>> [CLKID_HDMI_TX_SEL] = &g12a_hdmi_tx_sel.hw,
>> [CLKID_CTS_ENCI] = &g12a_cts_enci.hw,
>> [CLKID_CTS_ENCP] = &g12a_cts_encp.hw,
>> + [CLKID_CTS_ENCL] = &g12a_cts_encl.hw,
>> [CLKID_CTS_VDAC] = &g12a_cts_vdac.hw,
>> [CLKID_HDMI_TX] = &g12a_hdmi_tx.hw,
>> [CLKID_HDMI_SEL] = &g12a_hdmi_sel.hw,
>> @@ -4892,10 +4928,12 @@ static struct clk_hw *sm1_hw_clks[] = {
>> [CLKID_VCLK2_DIV12] = &g12a_vclk2_div12.hw,
>> [CLKID_CTS_ENCI_SEL] = &g12a_cts_enci_sel.hw,
>> [CLKID_CTS_ENCP_SEL] = &g12a_cts_encp_sel.hw,
>> + [CLKID_CTS_ENCL_SEL] = &g12a_cts_encl_sel.hw,
>> [CLKID_CTS_VDAC_SEL] = &g12a_cts_vdac_sel.hw,
>> [CLKID_HDMI_TX_SEL] = &g12a_hdmi_tx_sel.hw,
>> [CLKID_CTS_ENCI] = &g12a_cts_enci.hw,
>> [CLKID_CTS_ENCP] = &g12a_cts_encp.hw,
>> + [CLKID_CTS_ENCL] = &g12a_cts_encl.hw,
>> [CLKID_CTS_VDAC] = &g12a_cts_vdac.hw,
>> [CLKID_HDMI_TX] = &g12a_hdmi_tx.hw,
>> [CLKID_HDMI_SEL] = &g12a_hdmi_sel.hw,
>> @@ -5123,10 +5161,12 @@ static struct clk_regmap *const g12a_clk_regmaps[] = {
>> &g12a_vclk2_div12_en,
>> &g12a_cts_enci_sel,
>> &g12a_cts_encp_sel,
>> + &g12a_cts_encl_sel,
>> &g12a_cts_vdac_sel,
>> &g12a_hdmi_tx_sel,
>> &g12a_cts_enci,
>> &g12a_cts_encp,
>> + &g12a_cts_encl,
>> &g12a_cts_vdac,
>> &g12a_hdmi_tx,
>> &g12a_hdmi_sel,
>
On Thu 03 Aug 2023 at 14:03, Neil Armstrong <[email protected]> wrote:
> Add new CTS_ENCL & CTS_ENCL_SEL clocks for the G12A compatible
> SoCs, they are used to feed the VPU LCD Pixel encoder used for
> DSI display purposes.
>
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> drivers/clk/meson/g12a.c | 40 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 40 insertions(+)
>
> diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c
> index ceabd5f4b2ac..5d62134335c1 100644
> --- a/drivers/clk/meson/g12a.c
> +++ b/drivers/clk/meson/g12a.c
> @@ -3549,6 +3549,22 @@ static struct clk_regmap g12a_cts_encp_sel = {
> },
> };
>
> +static struct clk_regmap g12a_cts_encl_sel = {
> + .data = &(struct clk_regmap_mux_data){
> + .offset = HHI_VIID_CLK_DIV,
> + .mask = 0xf,
> + .shift = 12,
> + .table = mux_table_cts_sel,
> + },
> + .hw.init = &(struct clk_init_data){
> + .name = "cts_encl_sel",
> + .ops = &clk_regmap_mux_ops,
> + .parent_hws = g12a_cts_parent_hws,
> + .num_parents = ARRAY_SIZE(g12a_cts_parent_hws),
> + .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
Why nocache ?
This is usually used when the consumer driver is poking around behind
CCF back.
Any chance this can use assigned-parent or CCF directly ?
> + },
> +};
> +
> static struct clk_regmap g12a_cts_vdac_sel = {
> .data = &(struct clk_regmap_mux_data){
> .offset = HHI_VIID_CLK_DIV,
> @@ -3628,6 +3644,22 @@ static struct clk_regmap g12a_cts_encp = {
> },
> };
>
> +static struct clk_regmap g12a_cts_encl = {
> + .data = &(struct clk_regmap_gate_data){
> + .offset = HHI_VID_CLK_CNTL2,
> + .bit_idx = 3,
> + },
> + .hw.init = &(struct clk_init_data) {
> + .name = "cts_encl",
> + .ops = &clk_regmap_gate_ops,
> + .parent_hws = (const struct clk_hw *[]) {
> + &g12a_cts_encl_sel.hw
> + },
> + .num_parents = 1,
> + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
What is the reason for IGNORE_UNUSED ?
If you need to keep the clock on while the driver comes up, please
document it here.
> + },
> +};
> +
> static struct clk_regmap g12a_cts_vdac = {
> .data = &(struct clk_regmap_gate_data){
> .offset = HHI_VID_CLK_CNTL2,
> @@ -4407,10 +4439,12 @@ static struct clk_hw *g12a_hw_clks[] = {
> [CLKID_VCLK2_DIV12] = &g12a_vclk2_div12.hw,
> [CLKID_CTS_ENCI_SEL] = &g12a_cts_enci_sel.hw,
> [CLKID_CTS_ENCP_SEL] = &g12a_cts_encp_sel.hw,
> + [CLKID_CTS_ENCL_SEL] = &g12a_cts_encl_sel.hw,
> [CLKID_CTS_VDAC_SEL] = &g12a_cts_vdac_sel.hw,
> [CLKID_HDMI_TX_SEL] = &g12a_hdmi_tx_sel.hw,
> [CLKID_CTS_ENCI] = &g12a_cts_enci.hw,
> [CLKID_CTS_ENCP] = &g12a_cts_encp.hw,
> + [CLKID_CTS_ENCL] = &g12a_cts_encl.hw,
> [CLKID_CTS_VDAC] = &g12a_cts_vdac.hw,
> [CLKID_HDMI_TX] = &g12a_hdmi_tx.hw,
> [CLKID_HDMI_SEL] = &g12a_hdmi_sel.hw,
> @@ -4632,10 +4666,12 @@ static struct clk_hw *g12b_hw_clks[] = {
> [CLKID_VCLK2_DIV12] = &g12a_vclk2_div12.hw,
> [CLKID_CTS_ENCI_SEL] = &g12a_cts_enci_sel.hw,
> [CLKID_CTS_ENCP_SEL] = &g12a_cts_encp_sel.hw,
> + [CLKID_CTS_ENCL_SEL] = &g12a_cts_encl_sel.hw,
> [CLKID_CTS_VDAC_SEL] = &g12a_cts_vdac_sel.hw,
> [CLKID_HDMI_TX_SEL] = &g12a_hdmi_tx_sel.hw,
> [CLKID_CTS_ENCI] = &g12a_cts_enci.hw,
> [CLKID_CTS_ENCP] = &g12a_cts_encp.hw,
> + [CLKID_CTS_ENCL] = &g12a_cts_encl.hw,
> [CLKID_CTS_VDAC] = &g12a_cts_vdac.hw,
> [CLKID_HDMI_TX] = &g12a_hdmi_tx.hw,
> [CLKID_HDMI_SEL] = &g12a_hdmi_sel.hw,
> @@ -4892,10 +4928,12 @@ static struct clk_hw *sm1_hw_clks[] = {
> [CLKID_VCLK2_DIV12] = &g12a_vclk2_div12.hw,
> [CLKID_CTS_ENCI_SEL] = &g12a_cts_enci_sel.hw,
> [CLKID_CTS_ENCP_SEL] = &g12a_cts_encp_sel.hw,
> + [CLKID_CTS_ENCL_SEL] = &g12a_cts_encl_sel.hw,
> [CLKID_CTS_VDAC_SEL] = &g12a_cts_vdac_sel.hw,
> [CLKID_HDMI_TX_SEL] = &g12a_hdmi_tx_sel.hw,
> [CLKID_CTS_ENCI] = &g12a_cts_enci.hw,
> [CLKID_CTS_ENCP] = &g12a_cts_encp.hw,
> + [CLKID_CTS_ENCL] = &g12a_cts_encl.hw,
> [CLKID_CTS_VDAC] = &g12a_cts_vdac.hw,
> [CLKID_HDMI_TX] = &g12a_hdmi_tx.hw,
> [CLKID_HDMI_SEL] = &g12a_hdmi_sel.hw,
> @@ -5123,10 +5161,12 @@ static struct clk_regmap *const g12a_clk_regmaps[] = {
> &g12a_vclk2_div12_en,
> &g12a_cts_enci_sel,
> &g12a_cts_encp_sel,
> + &g12a_cts_encl_sel,
> &g12a_cts_vdac_sel,
> &g12a_hdmi_tx_sel,
> &g12a_cts_enci,
> &g12a_cts_encp,
> + &g12a_cts_encl,
> &g12a_cts_vdac,
> &g12a_hdmi_tx,
> &g12a_hdmi_sel,