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.
Signed-off-by: Neil Armstrong <[email protected]>
---
Changes in v9:
- Colledte reviewed-bys
- Fixed patches 2 & 4, commit messages and bindings format
- Link to v8: https://lore.kernel.org/r/20231109-amlogic-v6-4-upstream-dsi-ccf-vim3-v8-0-81e4aeeda193@linaro.org
Changes in v8:
- Switch vclk clk driver to parm as requested by Jerome
- Added bindings fixes to amlogic,meson-axg-mipi-pcie-analog & amlogic,g12a-mipi-dphy-analog
- Fixed DT errors in vim3 example and MNT Reform DT
- Rebased on next-20231107, successfully tested on VIM3L
- Link to v7: https://lore.kernel.org/r/20230803-amlogic-v6-4-upstream-dsi-ccf-vim3-v7-0-762219fc5b28@linaro.org
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]
---
Neil Armstrong (12):
dt-bindings: clk: g12a-clkc: add CTS_ENCL clock ids
dt-bindings: soc: amlogic,meson-gx-hhi-sysctrl: add example covering meson-axg-hhi-sysctrl
dt-bindings: phy: amlogic,meson-axg-mipi-pcie-analog: drop text about parent syscon and drop example
dt-bindings: phy: amlogic,g12a-mipi-dphy-analog: drop unneeded reg property and example
dt-bindings: arm: amlogic: Document the MNT Reform 2 CM4 adapter with a BPI-CM4 Module
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
arm64: dts: amlogic: meson-g12b-bananapi-cm4: add support for MNT Reform2 with CM4 adaper
Documentation/devicetree/bindings/arm/amlogic.yaml | 1 +
.../phy/amlogic,g12a-mipi-dphy-analog.yaml | 12 -
.../phy/amlogic,meson-axg-mipi-pcie-analog.yaml | 17 -
.../soc/amlogic/amlogic,meson-gx-hhi-sysctrl.yaml | 33 ++
arch/arm64/boot/dts/amlogic/Makefile | 1 +
arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi | 70 ++++
.../meson-g12b-bananapi-cm4-mnt-reform2.dts | 384 +++++++++++++++++++++
.../boot/dts/amlogic/meson-g12b-khadas-vim3.dtsi | 2 +-
arch/arm64/boot/dts/amlogic/meson-khadas-vim3.dtsi | 74 ++++
.../boot/dts/amlogic/meson-sm1-khadas-vim3l.dts | 2 +-
drivers/clk/meson/Kconfig | 5 +
drivers/clk/meson/Makefile | 1 +
drivers/clk/meson/g12a.c | 106 ++++--
drivers/clk/meson/vclk.c | 141 ++++++++
drivers/clk/meson/vclk.h | 51 +++
drivers/gpu/drm/meson/meson_dw_mipi_dsi.c | 7 +
include/dt-bindings/clock/g12a-clkc.h | 2 +
17 files changed, 858 insertions(+), 51 deletions(-)
---
base-commit: b0b93834348aaf1a6e14693b4f1d17d3ec024257
change-id: 20230512-amlogic-v6-4-upstream-dsi-ccf-vim3-b8e5217e1f4a
Best regards,
--
Neil Armstrong <[email protected]>
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
Add a third example covering the meson-axg-hhi-sysctrl variant and more
importantly the phy subnode.
Acked-by: Conor Dooley <[email protected]>
Signed-off-by: Neil Armstrong <[email protected]>
---
.../soc/amlogic/amlogic,meson-gx-hhi-sysctrl.yaml | 33 ++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/Documentation/devicetree/bindings/soc/amlogic/amlogic,meson-gx-hhi-sysctrl.yaml b/Documentation/devicetree/bindings/soc/amlogic/amlogic,meson-gx-hhi-sysctrl.yaml
index 16977e4e4357..c6bce40946d4 100644
--- a/Documentation/devicetree/bindings/soc/amlogic/amlogic,meson-gx-hhi-sysctrl.yaml
+++ b/Documentation/devicetree/bindings/soc/amlogic/amlogic,meson-gx-hhi-sysctrl.yaml
@@ -158,3 +158,36 @@ examples:
};
};
};
+
+ - |
+ system-controller@ff63c000 {
+ compatible = "amlogic,meson-axg-hhi-sysctrl", "simple-mfd", "syscon";
+ reg = <0xff63c000 0x400>;
+
+ clock-controller {
+ compatible = "amlogic,axg-clkc";
+ #clock-cells = <1>;
+ clocks = <&xtal>;
+ clock-names = "xtal";
+ };
+
+ power-controller {
+ compatible = "amlogic,meson-axg-pwrc";
+ #power-domain-cells = <1>;
+ amlogic,ao-sysctrl = <&sysctrl_AO>;
+
+ resets = <&reset_viu>,
+ <&reset_venc>,
+ <&reset_vcbus>,
+ <&reset_vencl>,
+ <&reset_vid_lock>;
+ reset-names = "viu", "venc", "vcbus", "vencl", "vid_lock";
+ clocks = <&clk_vpu>, <&clk_vapb>;
+ clock-names = "vpu", "vapb";
+ };
+
+ phy {
+ compatible = "amlogic,axg-mipi-pcie-analog-phy";
+ #phy-cells = <0>;
+ };
+ };
--
2.34.1
Since this bindings is referred from amlogic,meson-gx-hhi-sysctrl.yaml, drop the now
useless description about the parent node and also drop the unnecessary example.
Acked-by: Conor Dooley <[email protected]>
Signed-off-by: Neil Armstrong <[email protected]>
---
.../phy/amlogic,meson-axg-mipi-pcie-analog.yaml | 17 -----------------
1 file changed, 17 deletions(-)
diff --git a/Documentation/devicetree/bindings/phy/amlogic,meson-axg-mipi-pcie-analog.yaml b/Documentation/devicetree/bindings/phy/amlogic,meson-axg-mipi-pcie-analog.yaml
index 009a39808318..70def36e5688 100644
--- a/Documentation/devicetree/bindings/phy/amlogic,meson-axg-mipi-pcie-analog.yaml
+++ b/Documentation/devicetree/bindings/phy/amlogic,meson-axg-mipi-pcie-analog.yaml
@@ -9,16 +9,6 @@ title: Amlogic AXG shared MIPI/PCIE analog PHY
maintainers:
- Remi Pommarel <[email protected]>
-description: |+
- The Everything-Else Power Domains node should be the child of a syscon
- node with the required property:
-
- - compatible: Should be the following:
- "amlogic,meson-gx-hhi-sysctrl", "simple-mfd", "syscon"
-
- Refer to the bindings described in
- Documentation/devicetree/bindings/mfd/syscon.yaml
-
properties:
compatible:
const: amlogic,axg-mipi-pcie-analog-phy
@@ -31,10 +21,3 @@ required:
- "#phy-cells"
additionalProperties: false
-
-examples:
- - |
- mpphy: phy {
- compatible = "amlogic,axg-mipi-pcie-analog-phy";
- #phy-cells = <0>;
- };
--
2.34.1
The amlogic,g12a-mipi-dphy-analog is a feature of the simple-mfd
amlogic,meson-axg-hhi-sysctrl system control register zone which is an
intermixed registers zone, thus it's very hard to define clear ranges for
each SoC controlled features even if possible.
The amlogic,g12a-mipi-dphy-analog was wrongly documented as an independent
register range, which is not the reality, thus fix the bindings by dropping
the reg property now it's referred from amlogic,meson-gx-hhi-sysctrl.yaml
and documented as a subnode of amlogic,meson-axg-hhi-sysctrl.
Also drop the unnecessary example, the top level bindings example should
be enough.
Fixes: 76ab79f9726c ("dt-bindings: phy: add Amlogic G12A Analog MIPI D-PHY bindings")
Signed-off-by: Neil Armstrong <[email protected]>
---
.../bindings/phy/amlogic,g12a-mipi-dphy-analog.yaml | 12 ------------
1 file changed, 12 deletions(-)
diff --git a/Documentation/devicetree/bindings/phy/amlogic,g12a-mipi-dphy-analog.yaml b/Documentation/devicetree/bindings/phy/amlogic,g12a-mipi-dphy-analog.yaml
index c8c83acfb871..81c2654b7e57 100644
--- a/Documentation/devicetree/bindings/phy/amlogic,g12a-mipi-dphy-analog.yaml
+++ b/Documentation/devicetree/bindings/phy/amlogic,g12a-mipi-dphy-analog.yaml
@@ -16,20 +16,8 @@ properties:
"#phy-cells":
const: 0
- reg:
- maxItems: 1
-
required:
- compatible
- - reg
- "#phy-cells"
additionalProperties: false
-
-examples:
- - |
- phy@0 {
- compatible = "amlogic,g12a-mipi-dphy-analog";
- reg = <0x0 0xc>;
- #phy-cells = <0>;
- };
--
2.34.1
In order to setup the DSI clock, let's make the unused VCLK2 clock path
configuration via CCF.
The nocache option is removed from following clocks:
- vclk2_sel
- vclk2_input
- vclk2_div
- vclk2
- vclk_div1
- vclk2_div2_en
- vclk2_div4_en
- vclk2_div6_en
- vclk2_div12_en
- vclk2_div2
- vclk2_div4
- vclk2_div6
- vclk2_div12
- cts_encl_sel
vclk2 and vclk2_div uses the newly introduced vclk regmap driver
to handle the enable and reset bits.
In order to set a rate on cts_encl via the vclk2 clock path,
the NO_REPARENT flag is set on cts_encl_sel & vclk2_sel in order
to keep CCF from selection a parent.
The parents of cts_encl_sel & vclk2_sel are expected to be defined
in DT.
The following clock scheme is to be used for DSI:
xtal
\_ gp0_pll_dco
\_ gp0_pll
|- vclk2_sel
| \_ vclk2_input
| \_ vclk2_div
| \_ vclk2
| \_ vclk2_div1
| \_ cts_encl_sel
| \_ cts_encl -> to VPU LCD Encoder
|- mipi_dsi_pxclk_sel
\_ mipi_dsi_pxclk_div
\_ mipi_dsi_pxclk -> to DSI controller
The mipi_dsi_pxclk_div is set as RO in order to use the same GP0
for mipi_dsi_pxclk and vclk2_input.
Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/clk/meson/g12a.c | 68 +++++++++++++++++++++++++++++++++---------------
1 file changed, 47 insertions(+), 21 deletions(-)
diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c
index cadd824336ad..fb3d9196a1fd 100644
--- a/drivers/clk/meson/g12a.c
+++ b/drivers/clk/meson/g12a.c
@@ -22,6 +22,7 @@
#include "clk-regmap.h"
#include "clk-cpu-dyndiv.h"
#include "vid-pll-div.h"
+#include "vclk.h"
#include "meson-eeclk.h"
#include "g12a.h"
@@ -3165,7 +3166,7 @@ static struct clk_regmap g12a_vclk2_sel = {
.ops = &clk_regmap_mux_ops,
.parent_hws = g12a_vclk_parent_hws,
.num_parents = ARRAY_SIZE(g12a_vclk_parent_hws),
- .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
+ .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
},
};
@@ -3193,7 +3194,7 @@ static struct clk_regmap g12a_vclk2_input = {
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) { &g12a_vclk2_sel.hw },
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -3215,19 +3216,32 @@ static struct clk_regmap g12a_vclk_div = {
};
static struct clk_regmap g12a_vclk2_div = {
- .data = &(struct clk_regmap_div_data){
- .offset = HHI_VIID_CLK_DIV,
- .shift = 0,
- .width = 8,
+ .data = &(struct clk_regmap_vclk_div_data){
+ .div = {
+ .reg_off = HHI_VIID_CLK_DIV,
+ .shift = 0,
+ .width = 8,
+ },
+ .enable = {
+ .reg_off = HHI_VIID_CLK_DIV,
+ .shift = 16,
+ .width = 1,
+ },
+ .reset = {
+ .reg_off = HHI_VIID_CLK_DIV,
+ .shift = 17,
+ .width = 1,
+ },
+ .flags = CLK_DIVIDER_ROUND_CLOSEST,
},
.hw.init = &(struct clk_init_data){
.name = "vclk2_div",
- .ops = &clk_regmap_divider_ops,
+ .ops = &clk_regmap_vclk_div_ops,
.parent_hws = (const struct clk_hw *[]) {
&g12a_vclk2_input.hw
},
.num_parents = 1,
- .flags = CLK_GET_RATE_NOCACHE,
+ .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
},
};
@@ -3246,16 +3260,24 @@ static struct clk_regmap g12a_vclk = {
};
static struct clk_regmap g12a_vclk2 = {
- .data = &(struct clk_regmap_gate_data){
- .offset = HHI_VIID_CLK_CNTL,
- .bit_idx = 19,
+ .data = &(struct clk_regmap_vclk_data){
+ .enable = {
+ .reg_off = HHI_VIID_CLK_CNTL,
+ .shift = 19,
+ .width = 1,
+ },
+ .reset = {
+ .reg_off = HHI_VIID_CLK_CNTL,
+ .shift = 15,
+ .width = 1,
+ },
},
.hw.init = &(struct clk_init_data) {
.name = "vclk2",
- .ops = &clk_regmap_gate_ops,
+ .ops = &clk_regmap_vclk_ops,
.parent_hws = (const struct clk_hw *[]) { &g12a_vclk2_div.hw },
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
},
};
@@ -3339,7 +3361,7 @@ static struct clk_regmap g12a_vclk2_div1 = {
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -3353,7 +3375,7 @@ static struct clk_regmap g12a_vclk2_div2_en = {
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -3367,7 +3389,7 @@ static struct clk_regmap g12a_vclk2_div4_en = {
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -3381,7 +3403,7 @@ static struct clk_regmap g12a_vclk2_div6_en = {
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -3395,7 +3417,7 @@ static struct clk_regmap g12a_vclk2_div12_en = {
.ops = &clk_regmap_gate_ops,
.parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
.num_parents = 1,
- .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -3461,6 +3483,7 @@ static struct clk_fixed_factor g12a_vclk2_div2 = {
&g12a_vclk2_div2_en.hw
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -3474,6 +3497,7 @@ static struct clk_fixed_factor g12a_vclk2_div4 = {
&g12a_vclk2_div4_en.hw
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -3487,6 +3511,7 @@ static struct clk_fixed_factor g12a_vclk2_div6 = {
&g12a_vclk2_div6_en.hw
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -3500,6 +3525,7 @@ static struct clk_fixed_factor g12a_vclk2_div12 = {
&g12a_vclk2_div12_en.hw
},
.num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
},
};
@@ -3561,7 +3587,7 @@ static struct clk_regmap g12a_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,
+ .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
},
};
@@ -3717,7 +3743,7 @@ static struct clk_regmap g12a_mipi_dsi_pxclk_sel = {
.ops = &clk_regmap_mux_ops,
.parent_hws = g12a_mipi_dsi_pxclk_parent_hws,
.num_parents = ARRAY_SIZE(g12a_mipi_dsi_pxclk_parent_hws),
- .flags = CLK_SET_RATE_NO_REPARENT,
+ .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
},
};
@@ -3729,7 +3755,7 @@ static struct clk_regmap g12a_mipi_dsi_pxclk_div = {
},
.hw.init = &(struct clk_init_data){
.name = "mipi_dsi_pxclk_div",
- .ops = &clk_regmap_divider_ops,
+ .ops = &clk_regmap_divider_ro_ops,
.parent_hws = (const struct clk_hw *[]) {
&g12a_mipi_dsi_pxclk_sel.hw
},
--
2.34.1
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 f373a8d48b1d..cadd824336ad 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 | 141 +++++++++++++++++++++++++++++++++++++++++++++
drivers/clk/meson/vclk.h | 51 ++++++++++++++++
4 files changed, 198 insertions(+)
diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
index 29ffd14d267b..59a40a49f8e1 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 9ee4b954c896..9ba43fe7a07a 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..47f08a52b49f
--- /dev/null
+++ b/drivers/clk/meson/vclk.c
@@ -0,0 +1,141 @@
+// 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 inline struct clk_regmap_vclk_data *
+clk_get_regmap_vclk_data(struct clk_regmap *clk)
+{
+ return (struct clk_regmap_vclk_data *)clk->data;
+}
+
+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);
+
+ meson_parm_write(clk->map, &vclk->enable, 1);
+
+ /* Do a reset pulse */
+ meson_parm_write(clk->map, &vclk->reset, 1);
+ meson_parm_write(clk->map, &vclk->reset, 0);
+
+ 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);
+
+ meson_parm_write(clk->map, &vclk->enable, 0);
+}
+
+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);
+
+ return meson_parm_read(clk->map, &vclk->enable);
+}
+
+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 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;
+}
+
+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);
+
+ return divider_recalc_rate(hw, prate, meson_parm_read(clk->map, &vclk->div),
+ vclk->table, vclk->flags, vclk->div.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->div.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);
+ int ret;
+
+ ret = divider_get_val(rate, parent_rate, vclk->table, vclk->div.width,
+ vclk->flags);
+ if (ret < 0)
+ return ret;
+
+ meson_parm_write(clk->map, &vclk->div, ret);
+
+ return 0;
+};
+
+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 */
+ meson_parm_write(clk->map, &vclk->reset, 0);
+ meson_parm_write(clk->map, &vclk->enable, 1);
+
+ return 0;
+}
+
+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 */
+ meson_parm_write(clk->map, &vclk->enable, 0);
+ meson_parm_write(clk->map, &vclk->reset, 1);
+}
+
+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);
+
+ return meson_parm_read(clk->map, &vclk->enable);
+}
+
+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..4f25d7ad2717
--- /dev/null
+++ b/drivers/clk/meson/vclk.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2023 Neil Armstrong <[email protected]>
+ */
+
+#ifndef __VCLK_H
+#define __VCLK_H
+
+#include "clk-regmap.h"
+#include "parm.h"
+
+/**
+ * struct clk_regmap_vclk_data - vclk regmap backed specific data
+ *
+ * @enable: vclk enable field
+ * @reset: vclk reset field
+ * @flags: hardware-specific flags
+ *
+ * Flags:
+ * Same as clk_gate except CLK_GATE_HIWORD_MASK which is ignored
+ */
+struct clk_regmap_vclk_data {
+ struct parm enable;
+ struct parm reset;
+ u8 flags;
+};
+
+extern const struct clk_ops clk_regmap_vclk_ops;
+
+/**
+ * struct clk_regmap_vclk_div_data - vclk_div regmap back specific data
+ *
+ * @div: divider field
+ * @enable: vclk divider enable field
+ * @reset: vclk divider reset field
+ * @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 {
+ struct parm div;
+ struct parm enable;
+ struct parm reset;
+ const struct clk_div_table *table;
+ u8 flags;
+};
+
+extern const struct clk_ops clk_regmap_vclk_div_ops;
+
+#endif /* __VCLK_H */
--
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 caab7ceeda45..2154a4614fda 100644
--- a/Documentation/devicetree/bindings/arm/amlogic.yaml
+++ b/Documentation/devicetree/bindings/arm/amlogic.yaml
@@ -164,6 +164,7 @@ properties:
items:
- enum:
- bananapi,bpi-cm4io
+ - mntre,reform2-cm4
- const: bananapi,bpi-cm4
- const: amlogic,a311d
- const: amlogic,g12b
--
2.34.1
Add the MIPI DSI Analog & Digital PHY nodes and the DSI control
nodes with proper port endpoint to the VPU.
Signed-off-by: Neil Armstrong <[email protected]>
---
arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi | 70 +++++++++++++++++++++++
1 file changed, 70 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi b/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi
index ff68b911b729..7300408262d5 100644
--- a/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi
@@ -1663,9 +1663,28 @@ pwrc: power-controller {
<250000000>,
<0>; /* Do Nothing */
};
+
+ mipi_analog_dphy: phy {
+ compatible = "amlogic,g12a-mipi-dphy-analog";
+ #phy-cells = <0>;
+ status = "disabled";
+ };
};
};
+ mipi_dphy: phy@44000 {
+ compatible = "amlogic,axg-mipi-dphy";
+ reg = <0x0 0x44000 0x0 0x2000>;
+ clocks = <&clkc CLKID_MIPI_DSI_PHY>;
+ clock-names = "pclk";
+ resets = <&reset RESET_MIPI_DSI_PHY>;
+ reset-names = "phy";
+ phys = <&mipi_analog_dphy>;
+ phy-names = "analog";
+ #phy-cells = <0>;
+ status = "disabled";
+ };
+
usb3_pcie_phy: phy@46000 {
compatible = "amlogic,g12a-usb3-pcie-phy";
reg = <0x0 0x46000 0x0 0x2000>;
@@ -2152,6 +2171,15 @@ hdmi_tx_out: endpoint {
remote-endpoint = <&hdmi_tx_in>;
};
};
+
+ /* DPI output port */
+ dpi_port: port@2 {
+ reg = <2>;
+
+ dpi_out: endpoint {
+ remote-endpoint = <&mipi_dsi_in>;
+ };
+ };
};
gic: interrupt-controller@ffc01000 {
@@ -2189,6 +2217,48 @@ gpio_intc: interrupt-controller@f080 {
amlogic,channel-interrupts = <64 65 66 67 68 69 70 71>;
};
+ mipi_dsi: dsi@7000 {
+ compatible = "amlogic,meson-g12a-dw-mipi-dsi";
+ reg = <0x0 0x7000 0x0 0x1000>;
+ resets = <&reset RESET_MIPI_DSI_HOST>;
+ reset-names = "top";
+ clocks = <&clkc CLKID_MIPI_DSI_HOST>,
+ <&clkc CLKID_MIPI_DSI_PXCLK>,
+ <&clkc CLKID_CTS_ENCL>;
+ clock-names = "pclk", "bit", "px";
+ phys = <&mipi_dphy>;
+ phy-names = "dphy";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+
+ assigned-clocks = <&clkc CLKID_MIPI_DSI_PXCLK_SEL>,
+ <&clkc CLKID_CTS_ENCL_SEL>,
+ <&clkc CLKID_VCLK2_SEL>;
+ assigned-clock-parents = <&clkc CLKID_GP0_PLL>,
+ <&clkc CLKID_VCLK2_DIV1>,
+ <&clkc CLKID_GP0_PLL>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* VPU VENC Input */
+ mipi_dsi_venc_port: port@0 {
+ reg = <0>;
+
+ mipi_dsi_in: endpoint {
+ remote-endpoint = <&dpi_out>;
+ };
+ };
+
+ /* DSI Output */
+ mipi_dsi_panel_port: port@1 {
+ reg = <1>;
+ };
+ };
+ };
+
watchdog: watchdog@f0d0 {
compatible = "amlogic,meson-gxbb-wdt";
reg = <0x0 0xf0d0 0x0 0x10>;
--
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 e5fe4e994f43..72abe2057ec3 100644
--- a/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c
+++ b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c
@@ -95,6 +95,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) {
@@ -103,6 +104,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
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 | 384 +++++++++++++++++++++
2 files changed, 385 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/Makefile b/arch/arm64/boot/dts/amlogic/Makefile
index cc8b34bd583d..58b5b332bdb7 100644
--- a/arch/arm64/boot/dts/amlogic/Makefile
+++ b/arch/arm64/boot/dts/amlogic/Makefile
@@ -15,6 +15,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..003efed529ba
--- /dev/null
+++ b/arch/arm64/boot/dts/amlogic/meson-g12b-bananapi-cm4-mnt-reform2.dts
@@ -0,0 +1,384 @@
+// 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>;
+ };
+
+ panel {
+ compatible = "innolux,n125hce-gn1";
+ power-supply = <®_main_3v3>;
+ backlight = <&backlight>;
+ no-hpd;
+
+ 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
This add nodes to support the Khadas TS050 panel on the
Khadas VIM3 & VIM3L boards.
Signed-off-by: Neil Armstrong <[email protected]>
---
.../boot/dts/amlogic/meson-g12b-khadas-vim3.dtsi | 2 +-
arch/arm64/boot/dts/amlogic/meson-khadas-vim3.dtsi | 74 ++++++++++++++++++++++
.../boot/dts/amlogic/meson-sm1-khadas-vim3l.dts | 2 +-
3 files changed, 76 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/boot/dts/amlogic/meson-g12b-khadas-vim3.dtsi b/arch/arm64/boot/dts/amlogic/meson-g12b-khadas-vim3.dtsi
index 16dd409051b4..81c3057143b4 100644
--- a/arch/arm64/boot/dts/amlogic/meson-g12b-khadas-vim3.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-g12b-khadas-vim3.dtsi
@@ -98,7 +98,7 @@ &pwm_ab {
};
&pwm_AO_cd {
- pinctrl-0 = <&pwm_ao_d_e_pins>;
+ pinctrl-0 = <&pwm_ao_c_6_pins>, <&pwm_ao_d_e_pins>;
pinctrl-names = "default";
clocks = <&xtal>;
clock-names = "clkin1";
diff --git a/arch/arm64/boot/dts/amlogic/meson-khadas-vim3.dtsi b/arch/arm64/boot/dts/amlogic/meson-khadas-vim3.dtsi
index 514a6dd4b124..aafc37863f2e 100644
--- a/arch/arm64/boot/dts/amlogic/meson-khadas-vim3.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-khadas-vim3.dtsi
@@ -40,6 +40,14 @@ button-function {
};
};
+ panel_backlight: backlight {
+ compatible = "pwm-backlight";
+ pwms = <&pwm_AO_cd 0 25000 0>;
+ brightness-levels = <0 255>;
+ num-interpolated-steps = <255>;
+ default-brightness-level = <200>;
+ };
+
leds {
compatible = "gpio-leds";
@@ -358,6 +366,23 @@ rtc: rtc@51 {
};
};
+&i2c3 {
+ status = "okay";
+ pinctrl-0 = <&i2c3_sda_a_pins>, <&i2c3_sck_a_pins>;
+ pinctrl-names = "default";
+
+ touch-controller@38 {
+ compatible = "edt,edt-ft5206";
+ reg = <0x38>;
+ interrupt-parent = <&gpio_intc>;
+ interrupts = <66 IRQ_TYPE_EDGE_FALLING>; /* GPIOA_5 */
+ reset-gpios = <&gpio_expander 6 GPIO_ACTIVE_LOW>;
+ touchscreen-size-x = <1080>;
+ touchscreen-size-y = <1920>;
+ status = "okay";
+ };
+};
+
&ir {
status = "okay";
pinctrl-0 = <&remote_input_ao_pins>;
@@ -365,6 +390,55 @@ &ir {
linux,rc-map-name = "rc-khadas";
};
+&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 = <960000000>,
+ <0>,
+ <960000000>,
+ <0>,
+ <0>;
+
+ panel@0 {
+ compatible = "khadas,ts050";
+ reset-gpios = <&gpio_expander 0 GPIO_ACTIVE_LOW>;
+ enable-gpios = <&gpio_expander 1 GPIO_ACTIVE_HIGH>;
+ power-supply = <&vcc_3v3>;
+ backlight = <&panel_backlight>;
+ reg = <0>;
+
+ port {
+ mipi_in_panel: endpoint {
+ remote-endpoint = <&mipi_out_panel>;
+ };
+ };
+ };
+};
+
+&mipi_analog_dphy {
+ status = "okay";
+};
+
+&mipi_dphy {
+ status = "okay";
+};
+
+&mipi_dsi_panel_port {
+ mipi_out_panel: endpoint {
+ remote-endpoint = <&mipi_in_panel>;
+ };
+};
+
&pcie {
reset-gpios = <&gpio GPIOA_8 GPIO_ACTIVE_LOW>;
};
diff --git a/arch/arm64/boot/dts/amlogic/meson-sm1-khadas-vim3l.dts b/arch/arm64/boot/dts/amlogic/meson-sm1-khadas-vim3l.dts
index 9c0b544e2209..cb52a55ab70a 100644
--- a/arch/arm64/boot/dts/amlogic/meson-sm1-khadas-vim3l.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-sm1-khadas-vim3l.dts
@@ -76,7 +76,7 @@ &cpu3 {
};
&pwm_AO_cd {
- pinctrl-0 = <&pwm_ao_d_e_pins>;
+ pinctrl-0 = <&pwm_ao_c_6_pins>, <&pwm_ao_d_e_pins>;
pinctrl-names = "default";
clocks = <&xtal>;
clock-names = "clkin1";
--
2.34.1
Hi,
On Fri, Nov 24, 2023 at 09:41:22AM +0100, Neil Armstrong wrote:
> This add nodes to support the Khadas TS050 panel on the
> Khadas VIM3 & VIM3L boards.
>
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> .../boot/dts/amlogic/meson-g12b-khadas-vim3.dtsi | 2 +-
> arch/arm64/boot/dts/amlogic/meson-khadas-vim3.dtsi | 74 ++++++++++++++++++++++
> .../boot/dts/amlogic/meson-sm1-khadas-vim3l.dts | 2 +-
> 3 files changed, 76 insertions(+), 2 deletions(-)
Generally, those kind of patches still have value. Now that we are
accepting overlays, could this be converted to one and merged maybe?
Maxime
On Fri, Nov 24, 2023 at 09:41:15AM +0100, Neil Armstrong wrote:
> The amlogic,g12a-mipi-dphy-analog is a feature of the simple-mfd
> amlogic,meson-axg-hhi-sysctrl system control register zone which is an
> intermixed registers zone, thus it's very hard to define clear ranges for
> each SoC controlled features even if possible.
>
> The amlogic,g12a-mipi-dphy-analog was wrongly documented as an independent
> register range, which is not the reality, thus fix the bindings by dropping
> the reg property now it's referred from amlogic,meson-gx-hhi-sysctrl.yaml
> and documented as a subnode of amlogic,meson-axg-hhi-sysctrl.
>
> Also drop the unnecessary example, the top level bindings example should
> be enough.
>
> Fixes: 76ab79f9726c ("dt-bindings: phy: add Amlogic G12A Analog MIPI D-PHY bindings")
> Signed-off-by: Neil Armstrong <[email protected]>
I feel like I left a tag on this one before, but I can't remember.
Perhaps I missed the conclusion to the discussion to the discussion with
Rob about whether having "reg" was desirable that lead to a tag being
dropped?
> ---
> .../bindings/phy/amlogic,g12a-mipi-dphy-analog.yaml | 12 ------------
> 1 file changed, 12 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/phy/amlogic,g12a-mipi-dphy-analog.yaml b/Documentation/devicetree/bindings/phy/amlogic,g12a-mipi-dphy-analog.yaml
> index c8c83acfb871..81c2654b7e57 100644
> --- a/Documentation/devicetree/bindings/phy/amlogic,g12a-mipi-dphy-analog.yaml
> +++ b/Documentation/devicetree/bindings/phy/amlogic,g12a-mipi-dphy-analog.yaml
> @@ -16,20 +16,8 @@ properties:
> "#phy-cells":
> const: 0
>
> - reg:
> - maxItems: 1
> -
> required:
> - compatible
> - - reg
> - "#phy-cells"
>
> additionalProperties: false
> -
> -examples:
> - - |
> - phy@0 {
> - compatible = "amlogic,g12a-mipi-dphy-analog";
> - reg = <0x0 0xc>;
> - #phy-cells = <0>;
> - };
>
> --
> 2.34.1
>
Hi Conor,
On 24/11/2023 13:36, Conor Dooley wrote:
> On Fri, Nov 24, 2023 at 09:41:15AM +0100, Neil Armstrong wrote:
>> The amlogic,g12a-mipi-dphy-analog is a feature of the simple-mfd
>> amlogic,meson-axg-hhi-sysctrl system control register zone which is an
>> intermixed registers zone, thus it's very hard to define clear ranges for
>> each SoC controlled features even if possible.
>>
>> The amlogic,g12a-mipi-dphy-analog was wrongly documented as an independent
>> register range, which is not the reality, thus fix the bindings by dropping
>> the reg property now it's referred from amlogic,meson-gx-hhi-sysctrl.yaml
>> and documented as a subnode of amlogic,meson-axg-hhi-sysctrl.
>>
>> Also drop the unnecessary example, the top level bindings example should
>> be enough.
>>
>> Fixes: 76ab79f9726c ("dt-bindings: phy: add Amlogic G12A Analog MIPI D-PHY bindings")
>> Signed-off-by: Neil Armstrong <[email protected]>
>
> I feel like I left a tag on this one before, but I can't remember.
> Perhaps I missed the conclusion to the discussion to the discussion with
> Rob about whether having "reg" was desirable that lead to a tag being
> dropped?
I checked again and nope, not tag, but Rob's question was legitimate and I reworded
and clarified the commit message following your reviews.
On the other side you suggested a Fixes tag, which I added.
The rewording is about why reg doesn't make sense on the nature of the memory
region and it doesn't make sense here like other similar nodes.
Neil
>
>> ---
>> .../bindings/phy/amlogic,g12a-mipi-dphy-analog.yaml | 12 ------------
>> 1 file changed, 12 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/phy/amlogic,g12a-mipi-dphy-analog.yaml b/Documentation/devicetree/bindings/phy/amlogic,g12a-mipi-dphy-analog.yaml
>> index c8c83acfb871..81c2654b7e57 100644
>> --- a/Documentation/devicetree/bindings/phy/amlogic,g12a-mipi-dphy-analog.yaml
>> +++ b/Documentation/devicetree/bindings/phy/amlogic,g12a-mipi-dphy-analog.yaml
>> @@ -16,20 +16,8 @@ properties:
>> "#phy-cells":
>> const: 0
>>
>> - reg:
>> - maxItems: 1
>> -
>> required:
>> - compatible
>> - - reg
>> - "#phy-cells"
>>
>> additionalProperties: false
>> -
>> -examples:
>> - - |
>> - phy@0 {
>> - compatible = "amlogic,g12a-mipi-dphy-analog";
>> - reg = <0x0 0xc>;
>> - #phy-cells = <0>;
>> - };
>>
>> --
>> 2.34.1
>>
On Fri 24 Nov 2023 at 09:41, Neil Armstrong <[email protected]> wrote:
> In order to setup the DSI clock, let's make the unused VCLK2 clock path
> configuration via CCF.
>
> The nocache option is removed from following clocks:
> - vclk2_sel
> - vclk2_input
> - vclk2_div
> - vclk2
> - vclk_div1
> - vclk2_div2_en
> - vclk2_div4_en
> - vclk2_div6_en
> - vclk2_div12_en
> - vclk2_div2
> - vclk2_div4
> - vclk2_div6
> - vclk2_div12
> - cts_encl_sel
>
> vclk2 and vclk2_div uses the newly introduced vclk regmap driver
> to handle the enable and reset bits.
>
> In order to set a rate on cts_encl via the vclk2 clock path,
> the NO_REPARENT flag is set on cts_encl_sel & vclk2_sel in order
> to keep CCF from selection a parent.
> The parents of cts_encl_sel & vclk2_sel are expected to be defined
> in DT.
>
> The following clock scheme is to be used for DSI:
>
> xtal
> \_ gp0_pll_dco
> \_ gp0_pll
> |- vclk2_sel
> | \_ vclk2_input
> | \_ vclk2_div
> | \_ vclk2
> | \_ vclk2_div1
> | \_ cts_encl_sel
> | \_ cts_encl -> to VPU LCD Encoder
> |- mipi_dsi_pxclk_sel
> \_ mipi_dsi_pxclk_div
> \_ mipi_dsi_pxclk -> to DSI controller
>
> The mipi_dsi_pxclk_div is set as RO in order to use the same GP0
> for mipi_dsi_pxclk and vclk2_input.
Could you explain a bit more this part of about the RO ops ?
Maybe I'm missing something.
You would be relying on the reset being always the way it. It is
probable but not safe.
A way to deal with the shared GP0 would be to:
* cut rate propagation at mipi_dsi_pxclk_sel (already done) and
(vclk2_sel - TBD) ...
* Set GP0 base rate through assigned-clock-rate (which you already in
patch 11)
With this, I'm not sure anything needs to be RO for the rates to be set
properly for each subtree.
Also, with the subtree above and your example in patch 11, it looks odd that
PXCLK is manually set through DT while ENCL is not. Both are input of
dsi driver.
>
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> drivers/clk/meson/g12a.c | 68 +++++++++++++++++++++++++++++++++---------------
> 1 file changed, 47 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c
> index cadd824336ad..fb3d9196a1fd 100644
> --- a/drivers/clk/meson/g12a.c
> +++ b/drivers/clk/meson/g12a.c
> @@ -22,6 +22,7 @@
> #include "clk-regmap.h"
> #include "clk-cpu-dyndiv.h"
> #include "vid-pll-div.h"
> +#include "vclk.h"
> #include "meson-eeclk.h"
> #include "g12a.h"
>
> @@ -3165,7 +3166,7 @@ static struct clk_regmap g12a_vclk2_sel = {
> .ops = &clk_regmap_mux_ops,
> .parent_hws = g12a_vclk_parent_hws,
> .num_parents = ARRAY_SIZE(g12a_vclk_parent_hws),
> - .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
> + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
No sure CLK_SET_RATE_PARENT is wise here.
What you manually set in DT for the GP0, is likely to change because of
this, isn't it ?
> },
> };
>
> @@ -3193,7 +3194,7 @@ static struct clk_regmap g12a_vclk2_input = {
> .ops = &clk_regmap_gate_ops,
> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2_sel.hw },
> .num_parents = 1,
> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
> + .flags = CLK_SET_RATE_PARENT,
> },
> };
>
> @@ -3215,19 +3216,32 @@ static struct clk_regmap g12a_vclk_div = {
> };
>
> static struct clk_regmap g12a_vclk2_div = {
> - .data = &(struct clk_regmap_div_data){
> - .offset = HHI_VIID_CLK_DIV,
> - .shift = 0,
> - .width = 8,
> + .data = &(struct clk_regmap_vclk_div_data){
> + .div = {
> + .reg_off = HHI_VIID_CLK_DIV,
> + .shift = 0,
> + .width = 8,
> + },
> + .enable = {
> + .reg_off = HHI_VIID_CLK_DIV,
> + .shift = 16,
> + .width = 1,
> + },
> + .reset = {
> + .reg_off = HHI_VIID_CLK_DIV,
> + .shift = 17,
> + .width = 1,
> + },
> + .flags = CLK_DIVIDER_ROUND_CLOSEST,
> },
> .hw.init = &(struct clk_init_data){
> .name = "vclk2_div",
> - .ops = &clk_regmap_divider_ops,
> + .ops = &clk_regmap_vclk_div_ops,
> .parent_hws = (const struct clk_hw *[]) {
> &g12a_vclk2_input.hw
> },
> .num_parents = 1,
> - .flags = CLK_GET_RATE_NOCACHE,
> + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
> },
> };
>
> @@ -3246,16 +3260,24 @@ static struct clk_regmap g12a_vclk = {
> };
>
> static struct clk_regmap g12a_vclk2 = {
> - .data = &(struct clk_regmap_gate_data){
> - .offset = HHI_VIID_CLK_CNTL,
> - .bit_idx = 19,
> + .data = &(struct clk_regmap_vclk_data){
> + .enable = {
> + .reg_off = HHI_VIID_CLK_CNTL,
> + .shift = 19,
> + .width = 1,
> + },
> + .reset = {
> + .reg_off = HHI_VIID_CLK_CNTL,
> + .shift = 15,
> + .width = 1,
> + },
> },
> .hw.init = &(struct clk_init_data) {
> .name = "vclk2",
> - .ops = &clk_regmap_gate_ops,
> + .ops = &clk_regmap_vclk_ops,
> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2_div.hw },
> .num_parents = 1,
> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
> + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
> },
> };
>
> @@ -3339,7 +3361,7 @@ static struct clk_regmap g12a_vclk2_div1 = {
> .ops = &clk_regmap_gate_ops,
> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
> .num_parents = 1,
> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
> + .flags = CLK_SET_RATE_PARENT,
> },
> };
>
> @@ -3353,7 +3375,7 @@ static struct clk_regmap g12a_vclk2_div2_en = {
> .ops = &clk_regmap_gate_ops,
> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
> .num_parents = 1,
> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
> + .flags = CLK_SET_RATE_PARENT,
> },
> };
>
> @@ -3367,7 +3389,7 @@ static struct clk_regmap g12a_vclk2_div4_en = {
> .ops = &clk_regmap_gate_ops,
> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
> .num_parents = 1,
> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
> + .flags = CLK_SET_RATE_PARENT,
> },
> };
>
> @@ -3381,7 +3403,7 @@ static struct clk_regmap g12a_vclk2_div6_en = {
> .ops = &clk_regmap_gate_ops,
> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
> .num_parents = 1,
> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
> + .flags = CLK_SET_RATE_PARENT,
> },
> };
>
> @@ -3395,7 +3417,7 @@ static struct clk_regmap g12a_vclk2_div12_en = {
> .ops = &clk_regmap_gate_ops,
> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
> .num_parents = 1,
> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
> + .flags = CLK_SET_RATE_PARENT,
> },
> };
>
> @@ -3461,6 +3483,7 @@ static struct clk_fixed_factor g12a_vclk2_div2 = {
> &g12a_vclk2_div2_en.hw
> },
> .num_parents = 1,
> + .flags = CLK_SET_RATE_PARENT,
> },
> };
>
> @@ -3474,6 +3497,7 @@ static struct clk_fixed_factor g12a_vclk2_div4 = {
> &g12a_vclk2_div4_en.hw
> },
> .num_parents = 1,
> + .flags = CLK_SET_RATE_PARENT,
> },
> };
>
> @@ -3487,6 +3511,7 @@ static struct clk_fixed_factor g12a_vclk2_div6 = {
> &g12a_vclk2_div6_en.hw
> },
> .num_parents = 1,
> + .flags = CLK_SET_RATE_PARENT,
> },
> };
>
> @@ -3500,6 +3525,7 @@ static struct clk_fixed_factor g12a_vclk2_div12 = {
> &g12a_vclk2_div12_en.hw
> },
> .num_parents = 1,
> + .flags = CLK_SET_RATE_PARENT,
> },
> };
>
> @@ -3561,7 +3587,7 @@ static struct clk_regmap g12a_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,
> + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
> },
> };
>
> @@ -3717,7 +3743,7 @@ static struct clk_regmap g12a_mipi_dsi_pxclk_sel = {
> .ops = &clk_regmap_mux_ops,
> .parent_hws = g12a_mipi_dsi_pxclk_parent_hws,
> .num_parents = ARRAY_SIZE(g12a_mipi_dsi_pxclk_parent_hws),
> - .flags = CLK_SET_RATE_NO_REPARENT,
> + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
> },
> };
>
> @@ -3729,7 +3755,7 @@ static struct clk_regmap g12a_mipi_dsi_pxclk_div = {
> },
> .hw.init = &(struct clk_init_data){
> .name = "mipi_dsi_pxclk_div",
> - .ops = &clk_regmap_divider_ops,
> + .ops = &clk_regmap_divider_ro_ops,
> .parent_hws = (const struct clk_hw *[]) {
> &g12a_mipi_dsi_pxclk_sel.hw
> },
On Fri, Nov 24, 2023 at 02:50:58PM +0100, Neil Armstrong wrote:
> Hi Conor,
>
> On 24/11/2023 13:36, Conor Dooley wrote:
> > On Fri, Nov 24, 2023 at 09:41:15AM +0100, Neil Armstrong wrote:
> > > The amlogic,g12a-mipi-dphy-analog is a feature of the simple-mfd
> > > amlogic,meson-axg-hhi-sysctrl system control register zone which is an
> > > intermixed registers zone, thus it's very hard to define clear ranges for
> > > each SoC controlled features even if possible.
> > >
> > > The amlogic,g12a-mipi-dphy-analog was wrongly documented as an independent
> > > register range, which is not the reality, thus fix the bindings by dropping
> > > the reg property now it's referred from amlogic,meson-gx-hhi-sysctrl.yaml
> > > and documented as a subnode of amlogic,meson-axg-hhi-sysctrl.
> > >
> > > Also drop the unnecessary example, the top level bindings example should
> > > be enough.
> > >
> > > Fixes: 76ab79f9726c ("dt-bindings: phy: add Amlogic G12A Analog MIPI D-PHY bindings")
> > > Signed-off-by: Neil Armstrong <[email protected]>
> >
> > I feel like I left a tag on this one before, but I can't remember.
> > Perhaps I missed the conclusion to the discussion to the discussion with
> > Rob about whether having "reg" was desirable that lead to a tag being
> > dropped?
>
> I checked again and nope, not tag, but Rob's question was legitimate and I reworded
> and clarified the commit message following your reviews.
> On the other side you suggested a Fixes tag, which I added.
>
> The rewording is about why reg doesn't make sense on the nature of the memory
> region and it doesn't make sense here like other similar nodes.
Okay, I thought that I had given you one. Perhaps I forgot to send, or
Rob's message came in between me asking about the Fixes tag & replying
with an Ack. Sorry about that,
Acked-by: Conor Dooley <[email protected]>
Cheers,
Conor.
On 24/11/2023 15:41, Conor Dooley wrote:
> On Fri, Nov 24, 2023 at 02:50:58PM +0100, Neil Armstrong wrote:
>> Hi Conor,
>>
>> On 24/11/2023 13:36, Conor Dooley wrote:
>>> On Fri, Nov 24, 2023 at 09:41:15AM +0100, Neil Armstrong wrote:
>>>> The amlogic,g12a-mipi-dphy-analog is a feature of the simple-mfd
>>>> amlogic,meson-axg-hhi-sysctrl system control register zone which is an
>>>> intermixed registers zone, thus it's very hard to define clear ranges for
>>>> each SoC controlled features even if possible.
>>>>
>>>> The amlogic,g12a-mipi-dphy-analog was wrongly documented as an independent
>>>> register range, which is not the reality, thus fix the bindings by dropping
>>>> the reg property now it's referred from amlogic,meson-gx-hhi-sysctrl.yaml
>>>> and documented as a subnode of amlogic,meson-axg-hhi-sysctrl.
>>>>
>>>> Also drop the unnecessary example, the top level bindings example should
>>>> be enough.
>>>>
>>>> Fixes: 76ab79f9726c ("dt-bindings: phy: add Amlogic G12A Analog MIPI D-PHY bindings")
>>>> Signed-off-by: Neil Armstrong <[email protected]>
>>>
>>> I feel like I left a tag on this one before, but I can't remember.
>>> Perhaps I missed the conclusion to the discussion to the discussion with
>>> Rob about whether having "reg" was desirable that lead to a tag being
>>> dropped?
>>
>> I checked again and nope, not tag, but Rob's question was legitimate and I reworded
>> and clarified the commit message following your reviews.
>> On the other side you suggested a Fixes tag, which I added.
>>
>> The rewording is about why reg doesn't make sense on the nature of the memory
>> region and it doesn't make sense here like other similar nodes.
>
> Okay, I thought that I had given you one. Perhaps I forgot to send, or
> Rob's message came in between me asking about the Fixes tag & replying
> with an Ack. Sorry about that,
> Acked-by: Conor Dooley <[email protected]>
No problem thanks for your review.
Neil
>
> Cheers,
> Conor.
Hi,
On 24/11/2023 11:52, Maxime Ripard wrote:
> Hi,
>
> On Fri, Nov 24, 2023 at 09:41:22AM +0100, Neil Armstrong wrote:
>> This add nodes to support the Khadas TS050 panel on the
>> Khadas VIM3 & VIM3L boards.
>>
>> Signed-off-by: Neil Armstrong <[email protected]>
>> ---
>> .../boot/dts/amlogic/meson-g12b-khadas-vim3.dtsi | 2 +-
>> arch/arm64/boot/dts/amlogic/meson-khadas-vim3.dtsi | 74 ++++++++++++++++++++++
>> .../boot/dts/amlogic/meson-sm1-khadas-vim3l.dts | 2 +-
>> 3 files changed, 76 insertions(+), 2 deletions(-)
>
> Generally, those kind of patches still have value. Now that we are
> accepting overlays, could this be converted to one and merged maybe?
Yep I was thinking about that, I'll probably do that,
some new boards will also need overlays for DSI panels.
I'll probably switch to an overlay on v10.
Neil
>
> Maxime
On Fri 24 Nov 2023 at 09:41, 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.
>
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> drivers/clk/meson/Kconfig | 5 ++
> drivers/clk/meson/Makefile | 1 +
> drivers/clk/meson/vclk.c | 141 +++++++++++++++++++++++++++++++++++++++++++++
> drivers/clk/meson/vclk.h | 51 ++++++++++++++++
> 4 files changed, 198 insertions(+)
>
> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
> index 29ffd14d267b..59a40a49f8e1 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
This particular line belong in the next patch
> 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 9ee4b954c896..9ba43fe7a07a 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..47f08a52b49f
> --- /dev/null
> +++ b/drivers/clk/meson/vclk.c
> @@ -0,0 +1,141 @@
> +// 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 inline struct clk_regmap_vclk_data *
> +clk_get_regmap_vclk_data(struct clk_regmap *clk)
> +{
> + return (struct clk_regmap_vclk_data *)clk->data;
> +}
> +
> +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);
> +
> + meson_parm_write(clk->map, &vclk->enable, 1);
> +
> + /* Do a reset pulse */
> + meson_parm_write(clk->map, &vclk->reset, 1);
> + meson_parm_write(clk->map, &vclk->reset, 0);
> +
> + 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);
> +
> + meson_parm_write(clk->map, &vclk->enable, 0);
> +}
> +
> +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);
> +
> + return meson_parm_read(clk->map, &vclk->enable);
> +}
> +
> +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);
s/clk_regmap_vclk/meson_vclk at least for what is exported, ideally most
all the code.
I get clk_regmap_ comes from code copied from clk_regmap.c.
The reason the this part is different (and not using parm) if that when
I converted amlogic to regmap, I hope we could make this generic,
possibly converging between aml and qcom (which was the only other
platform using regmap for clock at the time). This is why clk_regmap.c
is a bit different from the other driver.
For the aml specific drivers, best to look at the mpll or cpu-dyndiv one.
> +
> +/* The VCLK Divider has supplementary reset & enable bits */
> +
> +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;
> +}
> +
> +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);
> +
> + return divider_recalc_rate(hw, prate, meson_parm_read(clk->map, &vclk->div),
> + vclk->table, vclk->flags, vclk->div.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->div.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);
> + int ret;
> +
> + ret = divider_get_val(rate, parent_rate, vclk->table, vclk->div.width,
> + vclk->flags);
> + if (ret < 0)
> + return ret;
> +
> + meson_parm_write(clk->map, &vclk->div, ret);
> +
> + return 0;
> +};
> +
> +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 */
> + meson_parm_write(clk->map, &vclk->reset, 0);
> + meson_parm_write(clk->map, &vclk->enable, 1);
> +
> + return 0;
> +}
> +
> +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 */
> + meson_parm_write(clk->map, &vclk->enable, 0);
> + meson_parm_write(clk->map, &vclk->reset, 1);
> +}
> +
> +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);
> +
> + return meson_parm_read(clk->map, &vclk->enable);
> +}
> +
> +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..4f25d7ad2717
> --- /dev/null
> +++ b/drivers/clk/meson/vclk.h
> @@ -0,0 +1,51 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2023 Neil Armstrong <[email protected]>
> + */
> +
> +#ifndef __VCLK_H
> +#define __VCLK_H
This is too generic.
Please add the MESON prefix like the other clock driver please.
> +
> +#include "clk-regmap.h"
> +#include "parm.h"
> +
> +/**
> + * struct clk_regmap_vclk_data - vclk regmap backed specific data
> + *
> + * @enable: vclk enable field
> + * @reset: vclk reset field
> + * @flags: hardware-specific flags
> + *
> + * Flags:
> + * Same as clk_gate except CLK_GATE_HIWORD_MASK which is ignored
> + */
> +struct clk_regmap_vclk_data {
> + struct parm enable;
> + struct parm reset;
> + u8 flags;
> +};
> +
> +extern const struct clk_ops clk_regmap_vclk_ops;
> +
> +/**
> + * struct clk_regmap_vclk_div_data - vclk_div regmap back specific data
> + *
> + * @div: divider field
> + * @enable: vclk divider enable field
> + * @reset: vclk divider reset field
> + * @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 {
> + struct parm div;
> + struct parm enable;
> + struct parm reset;
> + const struct clk_div_table *table;
> + u8 flags;
> +};
> +
> +extern const struct clk_ops clk_regmap_vclk_div_ops;
> +
> +#endif /* __VCLK_H */
On 24/11/2023 15:12, Jerome Brunet wrote:
>
> On Fri 24 Nov 2023 at 09:41, Neil Armstrong <[email protected]> wrote:
>
>> In order to setup the DSI clock, let's make the unused VCLK2 clock path
>> configuration via CCF.
>>
>> The nocache option is removed from following clocks:
>> - vclk2_sel
>> - vclk2_input
>> - vclk2_div
>> - vclk2
>> - vclk_div1
>> - vclk2_div2_en
>> - vclk2_div4_en
>> - vclk2_div6_en
>> - vclk2_div12_en
>> - vclk2_div2
>> - vclk2_div4
>> - vclk2_div6
>> - vclk2_div12
>> - cts_encl_sel
>>
>> vclk2 and vclk2_div uses the newly introduced vclk regmap driver
>> to handle the enable and reset bits.
>>
>> In order to set a rate on cts_encl via the vclk2 clock path,
>> the NO_REPARENT flag is set on cts_encl_sel & vclk2_sel in order
>> to keep CCF from selection a parent.
>> The parents of cts_encl_sel & vclk2_sel are expected to be defined
>> in DT.
>>
>> The following clock scheme is to be used for DSI:
>>
>> xtal
>> \_ gp0_pll_dco
>> \_ gp0_pll
>> |- vclk2_sel
>> | \_ vclk2_input
>> | \_ vclk2_div
>> | \_ vclk2
>> | \_ vclk2_div1
>> | \_ cts_encl_sel
>> | \_ cts_encl -> to VPU LCD Encoder
>> |- mipi_dsi_pxclk_sel
>> \_ mipi_dsi_pxclk_div
>> \_ mipi_dsi_pxclk -> to DSI controller
>>
>> The mipi_dsi_pxclk_div is set as RO in order to use the same GP0
>> for mipi_dsi_pxclk and vclk2_input.
>
> Could you explain a bit more this part of about the RO ops ?
> Maybe I'm missing something.
>
> You would be relying on the reset being always the way it. It is
> probable but not safe.
>
> A way to deal with the shared GP0 would be to:
> * cut rate propagation at mipi_dsi_pxclk_sel (already done) and
> (vclk2_sel - TBD) ...
> * Set GP0 base rate through assigned-clock-rate (which you already in
> patch 11)
>
> With this, I'm not sure anything needs to be RO for the rates to be set
> properly for each subtree.
>
> Also, with the subtree above and your example in patch 11, it looks odd that
> PXCLK is manually set through DT while ENCL is not. Both are input of
> dsi driver.
So the deal is about dynamic setup of clocks for DSI bridges, not really
for panels where we can probably know in advance the clock setup.
In this particular case, we need to keep a ratio between the vclk and the
DSI bitclk, the DSI bitclk is taken from mipi_dsi_pxclk and vclk is derived
from gp0 via vclk2.
If we set the bitclk rate via mipi_dsi_pxclk, CCF will try to use mipi_dsi_pxclk_div
to achieve the rate, and it does it everytime I tried, breaking the vclk/bitclk ratio,
and we have no way to know the gp0 rate in this case.
I suspect mipi_dsi_pxclk_div was added to achieve fractional vclk/bitclk ratios,
since it doesn't exist on AXG. Not sure we would ever need it... and none
of the other upstream DSI drivers supports such setups.
The main reasons I set only mipi_dsi_pxclk in DT is because :
1) the DSI controller requires a bitclk to respond, pclk is not enough
2) GP0 is disabled with an invalid config at cold boot, thus we cannot
rely on a default/safe rate on an initial prepare_enable().
This permits setting initial valid state for the DSI controller, while
the actual bitclk and vclk are calculated dynamically with panel/bridge
runtime parameters.
For the record, the samsung-dsim used fixed rate set from DT, and they moved
from that in order to support more panel and bridges.
But they're quite lucky because usually the DSI PLL is included in the PHY,
this makes the Amlogic design quite unusual (like most multimedia stuf...).
Neil
>
>>
>> Signed-off-by: Neil Armstrong <[email protected]>
>> ---
>> drivers/clk/meson/g12a.c | 68 +++++++++++++++++++++++++++++++++---------------
>> 1 file changed, 47 insertions(+), 21 deletions(-)
>>
>> diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c
>> index cadd824336ad..fb3d9196a1fd 100644
>> --- a/drivers/clk/meson/g12a.c
>> +++ b/drivers/clk/meson/g12a.c
>> @@ -22,6 +22,7 @@
>> #include "clk-regmap.h"
>> #include "clk-cpu-dyndiv.h"
>> #include "vid-pll-div.h"
>> +#include "vclk.h"
>> #include "meson-eeclk.h"
>> #include "g12a.h"
>>
>> @@ -3165,7 +3166,7 @@ static struct clk_regmap g12a_vclk2_sel = {
>> .ops = &clk_regmap_mux_ops,
>> .parent_hws = g12a_vclk_parent_hws,
>> .num_parents = ARRAY_SIZE(g12a_vclk_parent_hws),
>> - .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
>> + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
>
> No sure CLK_SET_RATE_PARENT is wise here.
> What you manually set in DT for the GP0, is likely to change because of
> this, isn't it ?
>
>> },
>> };
>>
>> @@ -3193,7 +3194,7 @@ static struct clk_regmap g12a_vclk2_input = {
>> .ops = &clk_regmap_gate_ops,
>> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2_sel.hw },
>> .num_parents = 1,
>> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>> + .flags = CLK_SET_RATE_PARENT,
>> },
>> };
>>
>> @@ -3215,19 +3216,32 @@ static struct clk_regmap g12a_vclk_div = {
>> };
>>
>> static struct clk_regmap g12a_vclk2_div = {
>> - .data = &(struct clk_regmap_div_data){
>> - .offset = HHI_VIID_CLK_DIV,
>> - .shift = 0,
>> - .width = 8,
>> + .data = &(struct clk_regmap_vclk_div_data){
>> + .div = {
>> + .reg_off = HHI_VIID_CLK_DIV,
>> + .shift = 0,
>> + .width = 8,
>> + },
>> + .enable = {
>> + .reg_off = HHI_VIID_CLK_DIV,
>> + .shift = 16,
>> + .width = 1,
>> + },
>> + .reset = {
>> + .reg_off = HHI_VIID_CLK_DIV,
>> + .shift = 17,
>> + .width = 1,
>> + },
>> + .flags = CLK_DIVIDER_ROUND_CLOSEST,
>> },
>> .hw.init = &(struct clk_init_data){
>> .name = "vclk2_div",
>> - .ops = &clk_regmap_divider_ops,
>> + .ops = &clk_regmap_vclk_div_ops,
>> .parent_hws = (const struct clk_hw *[]) {
>> &g12a_vclk2_input.hw
>> },
>> .num_parents = 1,
>> - .flags = CLK_GET_RATE_NOCACHE,
>> + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
>> },
>> };
>>
>> @@ -3246,16 +3260,24 @@ static struct clk_regmap g12a_vclk = {
>> };
>>
>> static struct clk_regmap g12a_vclk2 = {
>> - .data = &(struct clk_regmap_gate_data){
>> - .offset = HHI_VIID_CLK_CNTL,
>> - .bit_idx = 19,
>> + .data = &(struct clk_regmap_vclk_data){
>> + .enable = {
>> + .reg_off = HHI_VIID_CLK_CNTL,
>> + .shift = 19,
>> + .width = 1,
>> + },
>> + .reset = {
>> + .reg_off = HHI_VIID_CLK_CNTL,
>> + .shift = 15,
>> + .width = 1,
>> + },
>> },
>> .hw.init = &(struct clk_init_data) {
>> .name = "vclk2",
>> - .ops = &clk_regmap_gate_ops,
>> + .ops = &clk_regmap_vclk_ops,
>> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2_div.hw },
>> .num_parents = 1,
>> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>> + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
>> },
>> };
>>
>> @@ -3339,7 +3361,7 @@ static struct clk_regmap g12a_vclk2_div1 = {
>> .ops = &clk_regmap_gate_ops,
>> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
>> .num_parents = 1,
>> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>> + .flags = CLK_SET_RATE_PARENT,
>> },
>> };
>>
>> @@ -3353,7 +3375,7 @@ static struct clk_regmap g12a_vclk2_div2_en = {
>> .ops = &clk_regmap_gate_ops,
>> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
>> .num_parents = 1,
>> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>> + .flags = CLK_SET_RATE_PARENT,
>> },
>> };
>>
>> @@ -3367,7 +3389,7 @@ static struct clk_regmap g12a_vclk2_div4_en = {
>> .ops = &clk_regmap_gate_ops,
>> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
>> .num_parents = 1,
>> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>> + .flags = CLK_SET_RATE_PARENT,
>> },
>> };
>>
>> @@ -3381,7 +3403,7 @@ static struct clk_regmap g12a_vclk2_div6_en = {
>> .ops = &clk_regmap_gate_ops,
>> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
>> .num_parents = 1,
>> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>> + .flags = CLK_SET_RATE_PARENT,
>> },
>> };
>>
>> @@ -3395,7 +3417,7 @@ static struct clk_regmap g12a_vclk2_div12_en = {
>> .ops = &clk_regmap_gate_ops,
>> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
>> .num_parents = 1,
>> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>> + .flags = CLK_SET_RATE_PARENT,
>> },
>> };
>>
>> @@ -3461,6 +3483,7 @@ static struct clk_fixed_factor g12a_vclk2_div2 = {
>> &g12a_vclk2_div2_en.hw
>> },
>> .num_parents = 1,
>> + .flags = CLK_SET_RATE_PARENT,
>> },
>> };
>>
>> @@ -3474,6 +3497,7 @@ static struct clk_fixed_factor g12a_vclk2_div4 = {
>> &g12a_vclk2_div4_en.hw
>> },
>> .num_parents = 1,
>> + .flags = CLK_SET_RATE_PARENT,
>> },
>> };
>>
>> @@ -3487,6 +3511,7 @@ static struct clk_fixed_factor g12a_vclk2_div6 = {
>> &g12a_vclk2_div6_en.hw
>> },
>> .num_parents = 1,
>> + .flags = CLK_SET_RATE_PARENT,
>> },
>> };
>>
>> @@ -3500,6 +3525,7 @@ static struct clk_fixed_factor g12a_vclk2_div12 = {
>> &g12a_vclk2_div12_en.hw
>> },
>> .num_parents = 1,
>> + .flags = CLK_SET_RATE_PARENT,
>> },
>> };
>>
>> @@ -3561,7 +3587,7 @@ static struct clk_regmap g12a_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,
>> + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
>> },
>> };
>>
>> @@ -3717,7 +3743,7 @@ static struct clk_regmap g12a_mipi_dsi_pxclk_sel = {
>> .ops = &clk_regmap_mux_ops,
>> .parent_hws = g12a_mipi_dsi_pxclk_parent_hws,
>> .num_parents = ARRAY_SIZE(g12a_mipi_dsi_pxclk_parent_hws),
>> - .flags = CLK_SET_RATE_NO_REPARENT,
>> + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
>> },
>> };
>>
>> @@ -3729,7 +3755,7 @@ static struct clk_regmap g12a_mipi_dsi_pxclk_div = {
>> },
>> .hw.init = &(struct clk_init_data){
>> .name = "mipi_dsi_pxclk_div",
>> - .ops = &clk_regmap_divider_ops,
>> + .ops = &clk_regmap_divider_ro_ops,
>> .parent_hws = (const struct clk_hw *[]) {
>> &g12a_mipi_dsi_pxclk_sel.hw
>> },
>
On Fri 24 Nov 2023 at 16:15, Neil Armstrong <[email protected]> wrote:
> On 24/11/2023 15:12, Jerome Brunet wrote:
>> On Fri 24 Nov 2023 at 09:41, Neil Armstrong <[email protected]>
>> wrote:
>>
>>> In order to setup the DSI clock, let's make the unused VCLK2 clock path
>>> configuration via CCF.
>>>
>>> The nocache option is removed from following clocks:
>>> - vclk2_sel
>>> - vclk2_input
>>> - vclk2_div
>>> - vclk2
>>> - vclk_div1
>>> - vclk2_div2_en
>>> - vclk2_div4_en
>>> - vclk2_div6_en
>>> - vclk2_div12_en
>>> - vclk2_div2
>>> - vclk2_div4
>>> - vclk2_div6
>>> - vclk2_div12
>>> - cts_encl_sel
>>>
>>> vclk2 and vclk2_div uses the newly introduced vclk regmap driver
>>> to handle the enable and reset bits.
>>>
>>> In order to set a rate on cts_encl via the vclk2 clock path,
>>> the NO_REPARENT flag is set on cts_encl_sel & vclk2_sel in order
>>> to keep CCF from selection a parent.
>>> The parents of cts_encl_sel & vclk2_sel are expected to be defined
>>> in DT.
>>>
>>> The following clock scheme is to be used for DSI:
>>>
>>> xtal
>>> \_ gp0_pll_dco
>>> \_ gp0_pll
>>> |- vclk2_sel
>>> | \_ vclk2_input
>>> | \_ vclk2_div
>>> | \_ vclk2
>>> | \_ vclk2_div1
>>> | \_ cts_encl_sel
>>> | \_ cts_encl -> to VPU LCD Encoder
>>> |- mipi_dsi_pxclk_sel
>>> \_ mipi_dsi_pxclk_div
>>> \_ mipi_dsi_pxclk -> to DSI controller
>>>
>>> The mipi_dsi_pxclk_div is set as RO in order to use the same GP0
>>> for mipi_dsi_pxclk and vclk2_input.
>> Could you explain a bit more this part of about the RO ops ?
>> Maybe I'm missing something.
>> You would be relying on the reset being always the way it. It is
>> probable but not safe.
>> A way to deal with the shared GP0 would be to:
>> * cut rate propagation at mipi_dsi_pxclk_sel (already done) and
>> (vclk2_sel - TBD) ...
>> * Set GP0 base rate through assigned-clock-rate (which you already in
>> patch 11)
>> With this, I'm not sure anything needs to be RO for the rates to be set
>> properly for each subtree.
>> Also, with the subtree above and your example in patch 11, it looks odd
>> that
>> PXCLK is manually set through DT while ENCL is not. Both are input of
>> dsi driver.
>
> So the deal is about dynamic setup of clocks for DSI bridges, not really
> for panels where we can probably know in advance the clock setup.
>
> In this particular case, we need to keep a ratio between the vclk and the
> DSI bitclk, the DSI bitclk is taken from mipi_dsi_pxclk and vclk is derived
> from gp0 via vclk2.
>
> If we set the bitclk rate via mipi_dsi_pxclk, CCF will try to use mipi_dsi_pxclk_div
> to achieve the rate,
If you have CLK_RATE_PARENT on mipi_dsi_pxclk_sel, I'm not surprised it
does that, but you don't :/ I'm quite surprised it would do that, or
even could.
From your example setting 96Mhz on both gp0 and mipi_dsi_pxclk, since
you've proposed RO-OPS, I suppose the divider is assumed to be 1 and
stay like that forever.
With rate propagation disabled mipi_dsi_pxclk_sel and GP0 is 96Mhz,
CCF would have no choice but picking 1 as divider, so I don't understand
how CCF would pick anything else and how RO-OPS help
> and it does it everytime I tried, breaking the vclk/bitclk ratio,
> and we have no way to know the gp0 rate in this case.
If you really want to ensure the divider value is always 1, why not use a
divider table allowing only 1 ? Adding a comment in the g12 clock driver
would nice because this not obvious. It would be safer than relying on
the reset value.
>
> I suspect mipi_dsi_pxclk_div was added to achieve fractional vclk/bitclk ratios,
> since it doesn't exist on AXG. Not sure we would ever need it... and none
> of the other upstream DSI drivers supports such setups.
>
> The main reasons I set only mipi_dsi_pxclk in DT is because :
> 1) the DSI controller requires a bitclk to respond, pclk is not enough
> 2) GP0 is disabled with an invalid config at cold boot, thus we cannot
> rely on a default/safe rate on an initial prepare_enable().
> This permits setting initial valid state for the DSI controller, while
> the actual bitclk and vclk are calculated dynamically with panel/bridge
> runtime parameters.
Nothing against setting rate in DT when it is static. Setting it then
overriding it is not easy to follow.
To work around GP0 not being set, assuming you want to keep rate
propagation as it is, you could call clk_set_rate() on cts_encl (possibly w/o
enabling it) to force a setup on gp0 then clk_prepare_enable() on
pxclk. You'd get a your safe rate on GP0 and the clock you need on pxclk.
It is a bit hackish. Might be better to claim gp0 in your driver to
manage it directly, cutting rate propagation above it to control each
branch of the subtree as you need. It seems you need to have control over
that anyway and it would be clear GP0 is expected to belong to DSI.
>
> For the record, the samsung-dsim used fixed rate set from DT, and they moved
> from that in order to support more panel and bridges.
>
> But they're quite lucky because usually the DSI PLL is included in the PHY,
> this makes the Amlogic design quite unusual (like most multimedia stuf...).
>
> Neil
>
>>
>>>
>>> Signed-off-by: Neil Armstrong <[email protected]>
>>> ---
>>> drivers/clk/meson/g12a.c | 68 +++++++++++++++++++++++++++++++++---------------
>>> 1 file changed, 47 insertions(+), 21 deletions(-)
>>>
>>> diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c
>>> index cadd824336ad..fb3d9196a1fd 100644
>>> --- a/drivers/clk/meson/g12a.c
>>> +++ b/drivers/clk/meson/g12a.c
>>> @@ -22,6 +22,7 @@
>>> #include "clk-regmap.h"
>>> #include "clk-cpu-dyndiv.h"
>>> #include "vid-pll-div.h"
>>> +#include "vclk.h"
>>> #include "meson-eeclk.h"
>>> #include "g12a.h"
>>> @@ -3165,7 +3166,7 @@ static struct clk_regmap g12a_vclk2_sel = {
>>> .ops = &clk_regmap_mux_ops,
>>> .parent_hws = g12a_vclk_parent_hws,
>>> .num_parents = ARRAY_SIZE(g12a_vclk_parent_hws),
>>> - .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
>>> + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
>> No sure CLK_SET_RATE_PARENT is wise here.
>> What you manually set in DT for the GP0, is likely to change because of
>> this, isn't it ?
>>
>>> },
>>> };
>>> @@ -3193,7 +3194,7 @@ static struct clk_regmap g12a_vclk2_input = {
>>> .ops = &clk_regmap_gate_ops,
>>> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2_sel.hw },
>>> .num_parents = 1,
>>> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>>> + .flags = CLK_SET_RATE_PARENT,
>>> },
>>> };
>>> @@ -3215,19 +3216,32 @@ static struct clk_regmap g12a_vclk_div = {
>>> };
>>> static struct clk_regmap g12a_vclk2_div = {
>>> - .data = &(struct clk_regmap_div_data){
>>> - .offset = HHI_VIID_CLK_DIV,
>>> - .shift = 0,
>>> - .width = 8,
>>> + .data = &(struct clk_regmap_vclk_div_data){
>>> + .div = {
>>> + .reg_off = HHI_VIID_CLK_DIV,
>>> + .shift = 0,
>>> + .width = 8,
>>> + },
>>> + .enable = {
>>> + .reg_off = HHI_VIID_CLK_DIV,
>>> + .shift = 16,
>>> + .width = 1,
>>> + },
>>> + .reset = {
>>> + .reg_off = HHI_VIID_CLK_DIV,
>>> + .shift = 17,
>>> + .width = 1,
>>> + },
>>> + .flags = CLK_DIVIDER_ROUND_CLOSEST,
>>> },
>>> .hw.init = &(struct clk_init_data){
>>> .name = "vclk2_div",
>>> - .ops = &clk_regmap_divider_ops,
>>> + .ops = &clk_regmap_vclk_div_ops,
>>> .parent_hws = (const struct clk_hw *[]) {
>>> &g12a_vclk2_input.hw
>>> },
>>> .num_parents = 1,
>>> - .flags = CLK_GET_RATE_NOCACHE,
>>> + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
>>> },
>>> };
>>> @@ -3246,16 +3260,24 @@ static struct clk_regmap g12a_vclk = {
>>> };
>>> static struct clk_regmap g12a_vclk2 = {
>>> - .data = &(struct clk_regmap_gate_data){
>>> - .offset = HHI_VIID_CLK_CNTL,
>>> - .bit_idx = 19,
>>> + .data = &(struct clk_regmap_vclk_data){
>>> + .enable = {
>>> + .reg_off = HHI_VIID_CLK_CNTL,
>>> + .shift = 19,
>>> + .width = 1,
>>> + },
>>> + .reset = {
>>> + .reg_off = HHI_VIID_CLK_CNTL,
>>> + .shift = 15,
>>> + .width = 1,
>>> + },
>>> },
>>> .hw.init = &(struct clk_init_data) {
>>> .name = "vclk2",
>>> - .ops = &clk_regmap_gate_ops,
>>> + .ops = &clk_regmap_vclk_ops,
>>> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2_div.hw },
>>> .num_parents = 1,
>>> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>>> + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
>>> },
>>> };
>>> @@ -3339,7 +3361,7 @@ static struct clk_regmap g12a_vclk2_div1 = {
>>> .ops = &clk_regmap_gate_ops,
>>> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
>>> .num_parents = 1,
>>> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>>> + .flags = CLK_SET_RATE_PARENT,
>>> },
>>> };
>>> @@ -3353,7 +3375,7 @@ static struct clk_regmap g12a_vclk2_div2_en = {
>>> .ops = &clk_regmap_gate_ops,
>>> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
>>> .num_parents = 1,
>>> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>>> + .flags = CLK_SET_RATE_PARENT,
>>> },
>>> };
>>> @@ -3367,7 +3389,7 @@ static struct clk_regmap g12a_vclk2_div4_en = {
>>> .ops = &clk_regmap_gate_ops,
>>> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
>>> .num_parents = 1,
>>> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>>> + .flags = CLK_SET_RATE_PARENT,
>>> },
>>> };
>>> @@ -3381,7 +3403,7 @@ static struct clk_regmap g12a_vclk2_div6_en = {
>>> .ops = &clk_regmap_gate_ops,
>>> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
>>> .num_parents = 1,
>>> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>>> + .flags = CLK_SET_RATE_PARENT,
>>> },
>>> };
>>> @@ -3395,7 +3417,7 @@ static struct clk_regmap g12a_vclk2_div12_en = {
>>> .ops = &clk_regmap_gate_ops,
>>> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
>>> .num_parents = 1,
>>> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>>> + .flags = CLK_SET_RATE_PARENT,
>>> },
>>> };
>>> @@ -3461,6 +3483,7 @@ static struct clk_fixed_factor g12a_vclk2_div2 =
>>> {
>>> &g12a_vclk2_div2_en.hw
>>> },
>>> .num_parents = 1,
>>> + .flags = CLK_SET_RATE_PARENT,
>>> },
>>> };
>>> @@ -3474,6 +3497,7 @@ static struct clk_fixed_factor g12a_vclk2_div4 =
>>> {
>>> &g12a_vclk2_div4_en.hw
>>> },
>>> .num_parents = 1,
>>> + .flags = CLK_SET_RATE_PARENT,
>>> },
>>> };
>>> @@ -3487,6 +3511,7 @@ static struct clk_fixed_factor g12a_vclk2_div6 =
>>> {
>>> &g12a_vclk2_div6_en.hw
>>> },
>>> .num_parents = 1,
>>> + .flags = CLK_SET_RATE_PARENT,
>>> },
>>> };
>>> @@ -3500,6 +3525,7 @@ static struct clk_fixed_factor g12a_vclk2_div12
>>> = {
>>> &g12a_vclk2_div12_en.hw
>>> },
>>> .num_parents = 1,
>>> + .flags = CLK_SET_RATE_PARENT,
>>> },
>>> };
>>> @@ -3561,7 +3587,7 @@ static struct clk_regmap g12a_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,
>>> + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
>>> },
>>> };
>>> @@ -3717,7 +3743,7 @@ static struct clk_regmap g12a_mipi_dsi_pxclk_sel
>>> = {
>>> .ops = &clk_regmap_mux_ops,
>>> .parent_hws = g12a_mipi_dsi_pxclk_parent_hws,
>>> .num_parents = ARRAY_SIZE(g12a_mipi_dsi_pxclk_parent_hws),
>>> - .flags = CLK_SET_RATE_NO_REPARENT,
>>> + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
>>> },
>>> };
>>> @@ -3729,7 +3755,7 @@ static struct clk_regmap g12a_mipi_dsi_pxclk_div
>>> = {
>>> },
>>> .hw.init = &(struct clk_init_data){
>>> .name = "mipi_dsi_pxclk_div",
>>> - .ops = &clk_regmap_divider_ops,
>>> + .ops = &clk_regmap_divider_ro_ops,
>>> .parent_hws = (const struct clk_hw *[]) {
>>> &g12a_mipi_dsi_pxclk_sel.hw
>>> },
>>
Applied to clk-meson (v6.8/drivers), thanks!
[01/12] dt-bindings: clk: g12a-clkc: add CTS_ENCL clock ids
https://github.com/BayLibre/clk-meson/commit/bd5ef3f21d17
[06/12] clk: meson: g12a: add CTS_ENCL & CTS_ENCL_SEL clocks
https://github.com/BayLibre/clk-meson/commit/5de4e8353e32
Best regards,
--
Jerome
On Fri, 24 Nov 2023 09:41:15 +0100, Neil Armstrong wrote:
> The amlogic,g12a-mipi-dphy-analog is a feature of the simple-mfd
> amlogic,meson-axg-hhi-sysctrl system control register zone which is an
> intermixed registers zone, thus it's very hard to define clear ranges for
> each SoC controlled features even if possible.
>
> The amlogic,g12a-mipi-dphy-analog was wrongly documented as an independent
> register range, which is not the reality, thus fix the bindings by dropping
> the reg property now it's referred from amlogic,meson-gx-hhi-sysctrl.yaml
> and documented as a subnode of amlogic,meson-axg-hhi-sysctrl.
>
> Also drop the unnecessary example, the top level bindings example should
> be enough.
>
> Fixes: 76ab79f9726c ("dt-bindings: phy: add Amlogic G12A Analog MIPI D-PHY bindings")
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> .../bindings/phy/amlogic,g12a-mipi-dphy-analog.yaml | 12 ------------
> 1 file changed, 12 deletions(-)
>
Reviewed-by: Rob Herring <[email protected]>
Hi,
On Fri, 24 Nov 2023 09:41:11 +0100, Neil Armstrong wrote:
> 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.
>
> [...]
Thanks, Applied to https://git.kernel.org/pub/scm/linux/kernel/git/amlogic/linux.git (v6.8/arm64-dt)
[02/12] dt-bindings: soc: amlogic,meson-gx-hhi-sysctrl: add example covering meson-axg-hhi-sysctrl
https://git.kernel.org/amlogic/c/beb9c30ba4188e481991d91124c554f61a7ec121
These changes has been applied on the intermediate git tree [1].
The v6.8/arm64-dt branch will then be sent via a formal Pull Request to the Linux SoC maintainers
for inclusion in their intermediate git branches in order to be sent to Linus during
the next merge window, or sooner if it's a set of fixes.
In the cases of fixes, those will be merged in the current release candidate
kernel and as soon they appear on the Linux master branch they will be
backported to the previous Stable and Long-Stable kernels [2].
The intermediate git branches are merged daily in the linux-next tree [3],
people are encouraged testing these pre-release kernels and report issues on the
relevant mailing-lists.
If problems are discovered on those changes, please submit a signed-off-by revert
patch followed by a corrective changeset.
[1] https://git.kernel.org/pub/scm/linux/kernel/git/amlogic/linux.git
[2] https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
[3] https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
--
Neil
Hi Vinod,
On 24/11/2023 09:41, Neil Armstrong wrote:
<snip>
>
> ---
> Neil Armstrong (12):
> dt-bindings: clk: g12a-clkc: add CTS_ENCL clock ids
> dt-bindings: soc: amlogic,meson-gx-hhi-sysctrl: add example covering meson-axg-hhi-sysctrl
> dt-bindings: phy: amlogic,meson-axg-mipi-pcie-analog: drop text about parent syscon and drop example
> dt-bindings: phy: amlogic,g12a-mipi-dphy-analog: drop unneeded reg property and example
Could you pick patches 3 and 4 ? they are both reviewed.
Thanks,
Neil
> dt-bindings: arm: amlogic: Document the MNT Reform 2 CM4 adapter with a BPI-CM4 Module
> 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
> arm64: dts: amlogic: meson-g12b-bananapi-cm4: add support for MNT Reform2 with CM4 adaper
>
> Documentation/devicetree/bindings/arm/amlogic.yaml | 1 +
> .../phy/amlogic,g12a-mipi-dphy-analog.yaml | 12 -
> .../phy/amlogic,meson-axg-mipi-pcie-analog.yaml | 17 -
> .../soc/amlogic/amlogic,meson-gx-hhi-sysctrl.yaml | 33 ++
> arch/arm64/boot/dts/amlogic/Makefile | 1 +
> arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi | 70 ++++
> .../meson-g12b-bananapi-cm4-mnt-reform2.dts | 384 +++++++++++++++++++++
> .../boot/dts/amlogic/meson-g12b-khadas-vim3.dtsi | 2 +-
> arch/arm64/boot/dts/amlogic/meson-khadas-vim3.dtsi | 74 ++++
> .../boot/dts/amlogic/meson-sm1-khadas-vim3l.dts | 2 +-
> drivers/clk/meson/Kconfig | 5 +
> drivers/clk/meson/Makefile | 1 +
> drivers/clk/meson/g12a.c | 106 ++++--
> drivers/clk/meson/vclk.c | 141 ++++++++
> drivers/clk/meson/vclk.h | 51 +++
> drivers/gpu/drm/meson/meson_dw_mipi_dsi.c | 7 +
> include/dt-bindings/clock/g12a-clkc.h | 2 +
> 17 files changed, 858 insertions(+), 51 deletions(-)
> ---
> base-commit: b0b93834348aaf1a6e14693b4f1d17d3ec024257
> change-id: 20230512-amlogic-v6-4-upstream-dsi-ccf-vim3-b8e5217e1f4a
>
> Best regards,
On 24/11/2023 15:41, Jerome Brunet wrote:
>
> On Fri 24 Nov 2023 at 09:41, 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.
>>
>> Signed-off-by: Neil Armstrong <[email protected]>
>> ---
>> drivers/clk/meson/Kconfig | 5 ++
>> drivers/clk/meson/Makefile | 1 +
>> drivers/clk/meson/vclk.c | 141 +++++++++++++++++++++++++++++++++++++++++++++
>> drivers/clk/meson/vclk.h | 51 ++++++++++++++++
>> 4 files changed, 198 insertions(+)
>>
>> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
>> index 29ffd14d267b..59a40a49f8e1 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
>
> This particular line belong in the next patch
Indeed
>
>> 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 9ee4b954c896..9ba43fe7a07a 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..47f08a52b49f
>> --- /dev/null
>> +++ b/drivers/clk/meson/vclk.c
>> @@ -0,0 +1,141 @@
>> +// 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 inline struct clk_regmap_vclk_data *
>> +clk_get_regmap_vclk_data(struct clk_regmap *clk)
>> +{
>> + return (struct clk_regmap_vclk_data *)clk->data;
>> +}
>> +
>> +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);
>> +
>> + meson_parm_write(clk->map, &vclk->enable, 1);
>> +
>> + /* Do a reset pulse */
>> + meson_parm_write(clk->map, &vclk->reset, 1);
>> + meson_parm_write(clk->map, &vclk->reset, 0);
>> +
>> + 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);
>> +
>> + meson_parm_write(clk->map, &vclk->enable, 0);
>> +}
>> +
>> +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);
>> +
>> + return meson_parm_read(clk->map, &vclk->enable);
>> +}
>> +
>> +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);
>
> s/clk_regmap_vclk/meson_vclk at least for what is exported, ideally most
> all the code.
>
> I get clk_regmap_ comes from code copied from clk_regmap.c.
> The reason the this part is different (and not using parm) if that when
> I converted amlogic to regmap, I hope we could make this generic,
> possibly converging between aml and qcom (which was the only other
> platform using regmap for clock at the time). This is why clk_regmap.c
> is a bit different from the other driver.
>
> For the aml specific drivers, best to look at the mpll or cpu-dyndiv one.
Ack
>
>> +
>> +/* The VCLK Divider has supplementary reset & enable bits */
>> +
>> +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;
>> +}
>> +
>> +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);
>> +
>> + return divider_recalc_rate(hw, prate, meson_parm_read(clk->map, &vclk->div),
>> + vclk->table, vclk->flags, vclk->div.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->div.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);
>> + int ret;
>> +
>> + ret = divider_get_val(rate, parent_rate, vclk->table, vclk->div.width,
>> + vclk->flags);
>> + if (ret < 0)
>> + return ret;
>> +
>> + meson_parm_write(clk->map, &vclk->div, ret);
>> +
>> + return 0;
>> +};
>> +
>> +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 */
>> + meson_parm_write(clk->map, &vclk->reset, 0);
>> + meson_parm_write(clk->map, &vclk->enable, 1);
>> +
>> + return 0;
>> +}
>> +
>> +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 */
>> + meson_parm_write(clk->map, &vclk->enable, 0);
>> + meson_parm_write(clk->map, &vclk->reset, 1);
>> +}
>> +
>> +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);
>> +
>> + return meson_parm_read(clk->map, &vclk->enable);
>> +}
>> +
>> +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..4f25d7ad2717
>> --- /dev/null
>> +++ b/drivers/clk/meson/vclk.h
>> @@ -0,0 +1,51 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (c) 2023 Neil Armstrong <[email protected]>
>> + */
>> +
>> +#ifndef __VCLK_H
>> +#define __VCLK_H
>
> This is too generic.
> Please add the MESON prefix like the other clock driver please.
Ack
>
>> +
>> +#include "clk-regmap.h"
>> +#include "parm.h"
>> +
>> +/**
>> + * struct clk_regmap_vclk_data - vclk regmap backed specific data
>> + *
>> + * @enable: vclk enable field
>> + * @reset: vclk reset field
>> + * @flags: hardware-specific flags
>> + *
>> + * Flags:
>> + * Same as clk_gate except CLK_GATE_HIWORD_MASK which is ignored
>> + */
>> +struct clk_regmap_vclk_data {
>> + struct parm enable;
>> + struct parm reset;
>> + u8 flags;
>> +};
>> +
>> +extern const struct clk_ops clk_regmap_vclk_ops;
>> +
>> +/**
>> + * struct clk_regmap_vclk_div_data - vclk_div regmap back specific data
>> + *
>> + * @div: divider field
>> + * @enable: vclk divider enable field
>> + * @reset: vclk divider reset field
>> + * @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 {
>> + struct parm div;
>> + struct parm enable;
>> + struct parm reset;
>> + const struct clk_div_table *table;
>> + u8 flags;
>> +};
>> +
>> +extern const struct clk_ops clk_regmap_vclk_div_ops;
>> +
>> +#endif /* __VCLK_H */
>
Thanks,
Neil
Hi,
On 24/11/2023 16:32, Jerome Brunet wrote:
>
> On Fri 24 Nov 2023 at 16:15, Neil Armstrong <[email protected]> wrote:
>
>> On 24/11/2023 15:12, Jerome Brunet wrote:
>>> On Fri 24 Nov 2023 at 09:41, Neil Armstrong <[email protected]>
>>> wrote:
>>>
>>>> In order to setup the DSI clock, let's make the unused VCLK2 clock path
>>>> configuration via CCF.
>>>>
>>>> The nocache option is removed from following clocks:
>>>> - vclk2_sel
>>>> - vclk2_input
>>>> - vclk2_div
>>>> - vclk2
>>>> - vclk_div1
>>>> - vclk2_div2_en
>>>> - vclk2_div4_en
>>>> - vclk2_div6_en
>>>> - vclk2_div12_en
>>>> - vclk2_div2
>>>> - vclk2_div4
>>>> - vclk2_div6
>>>> - vclk2_div12
>>>> - cts_encl_sel
>>>>
>>>> vclk2 and vclk2_div uses the newly introduced vclk regmap driver
>>>> to handle the enable and reset bits.
>>>>
>>>> In order to set a rate on cts_encl via the vclk2 clock path,
>>>> the NO_REPARENT flag is set on cts_encl_sel & vclk2_sel in order
>>>> to keep CCF from selection a parent.
>>>> The parents of cts_encl_sel & vclk2_sel are expected to be defined
>>>> in DT.
>>>>
>>>> The following clock scheme is to be used for DSI:
>>>>
>>>> xtal
>>>> \_ gp0_pll_dco
>>>> \_ gp0_pll
>>>> |- vclk2_sel
>>>> | \_ vclk2_input
>>>> | \_ vclk2_div
>>>> | \_ vclk2
>>>> | \_ vclk2_div1
>>>> | \_ cts_encl_sel
>>>> | \_ cts_encl -> to VPU LCD Encoder
>>>> |- mipi_dsi_pxclk_sel
>>>> \_ mipi_dsi_pxclk_div
>>>> \_ mipi_dsi_pxclk -> to DSI controller
>>>>
>>>> The mipi_dsi_pxclk_div is set as RO in order to use the same GP0
>>>> for mipi_dsi_pxclk and vclk2_input.
>>> Could you explain a bit more this part of about the RO ops ?
>>> Maybe I'm missing something.
>>> You would be relying on the reset being always the way it. It is
>>> probable but not safe.
>>> A way to deal with the shared GP0 would be to:
>>> * cut rate propagation at mipi_dsi_pxclk_sel (already done) and
>>> (vclk2_sel - TBD) ...
>>> * Set GP0 base rate through assigned-clock-rate (which you already in
>>> patch 11)
>>> With this, I'm not sure anything needs to be RO for the rates to be set
>>> properly for each subtree.
>>> Also, with the subtree above and your example in patch 11, it looks odd
>>> that
>>> PXCLK is manually set through DT while ENCL is not. Both are input of
>>> dsi driver.
>>
>> So the deal is about dynamic setup of clocks for DSI bridges, not really
>> for panels where we can probably know in advance the clock setup.
>>
>> In this particular case, we need to keep a ratio between the vclk and the
>> DSI bitclk, the DSI bitclk is taken from mipi_dsi_pxclk and vclk is derived
>> from gp0 via vclk2.
>>
>> If we set the bitclk rate via mipi_dsi_pxclk, CCF will try to use mipi_dsi_pxclk_div
>> to achieve the rate,
>
> If you have CLK_RATE_PARENT on mipi_dsi_pxclk_sel, I'm not surprised it
> does that, but you don't :/ I'm quite surprised it would do that, or
> even could.
Hmm, I need to recheck the clock tree again... seems I got lost in the
different revisions...
>
> From your example setting 96Mhz on both gp0 and mipi_dsi_pxclk, since
> you've proposed RO-OPS, I suppose the divider is assumed to be 1 and
> stay like that forever.
>
> With rate propagation disabled mipi_dsi_pxclk_sel and GP0 is 96Mhz,
> CCF would have no choice but picking 1 as divider, so I don't understand
> how CCF would pick anything else and how RO-OPS help
>
>> and it does it everytime I tried, breaking the vclk/bitclk ratio,
>> and we have no way to know the gp0 rate in this case.
>
> If you really want to ensure the divider value is always 1, why not use a
> divider table allowing only 1 ? Adding a comment in the g12 clock driver
> would nice because this not obvious. It would be safer than relying on
> the reset value.
Indeed, will switch to that
>
>>
>> I suspect mipi_dsi_pxclk_div was added to achieve fractional vclk/bitclk ratios,
>> since it doesn't exist on AXG. Not sure we would ever need it... and none
>> of the other upstream DSI drivers supports such setups.
>>
>> The main reasons I set only mipi_dsi_pxclk in DT is because :
>> 1) the DSI controller requires a bitclk to respond, pclk is not enough
>> 2) GP0 is disabled with an invalid config at cold boot, thus we cannot
>> rely on a default/safe rate on an initial prepare_enable().
>> This permits setting initial valid state for the DSI controller, while
>> the actual bitclk and vclk are calculated dynamically with panel/bridge
>> runtime parameters.
>
> Nothing against setting rate in DT when it is static. Setting it then
> overriding it is not easy to follow.
Yup, would be simpler to only have parenting set in DT, since it must
stay static, I'm fine trying to move rate setup to code.
>
> To work around GP0 not being set, assuming you want to keep rate
> propagation as it is, you could call clk_set_rate() on cts_encl (possibly w/o
> enabling it) to force a setup on gp0 then clk_prepare_enable() on
> pxclk. You'd get a your safe rate on GP0 and the clock you need on pxclk.
>
> It is a bit hackish. Might be better to claim gp0 in your driver to
> manage it directly, cutting rate propagation above it to control each
> branch of the subtree as you need. It seems you need to have control over
> that anyway and it would be clear GP0 is expected to belong to DSI.
Controlling the PLL from the DSI controller seems violating too much layers,
DSI controller driver is not feed directly by the PLL so it's a non-sense
regarding DT properties.
Setting a safe clock from the DSI controller probe is an idea, but again I
don't know which value I should use...
I'll review the clk parenting flags and try to hack something.
Thanks,
Neil
>
>>
>> For the record, the samsung-dsim used fixed rate set from DT, and they moved
>> from that in order to support more panel and bridges.
>>
>> But they're quite lucky because usually the DSI PLL is included in the PHY,
>> this makes the Amlogic design quite unusual (like most multimedia stuf...).
>>
>> Neil
>>
>>>
>>>>
>>>> Signed-off-by: Neil Armstrong <[email protected]>
>>>> ---
>>>> drivers/clk/meson/g12a.c | 68 +++++++++++++++++++++++++++++++++---------------
>>>> 1 file changed, 47 insertions(+), 21 deletions(-)
>>>>
>>>> diff --git a/drivers/clk/meson/g12a.c b/drivers/clk/meson/g12a.c
>>>> index cadd824336ad..fb3d9196a1fd 100644
>>>> --- a/drivers/clk/meson/g12a.c
>>>> +++ b/drivers/clk/meson/g12a.c
>>>> @@ -22,6 +22,7 @@
>>>> #include "clk-regmap.h"
>>>> #include "clk-cpu-dyndiv.h"
>>>> #include "vid-pll-div.h"
>>>> +#include "vclk.h"
>>>> #include "meson-eeclk.h"
>>>> #include "g12a.h"
>>>> @@ -3165,7 +3166,7 @@ static struct clk_regmap g12a_vclk2_sel = {
>>>> .ops = &clk_regmap_mux_ops,
>>>> .parent_hws = g12a_vclk_parent_hws,
>>>> .num_parents = ARRAY_SIZE(g12a_vclk_parent_hws),
>>>> - .flags = CLK_SET_RATE_NO_REPARENT | CLK_GET_RATE_NOCACHE,
>>>> + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
>>> No sure CLK_SET_RATE_PARENT is wise here.
>>> What you manually set in DT for the GP0, is likely to change because of
>>> this, isn't it ?
>>>
>>>> },
>>>> };
>>>> @@ -3193,7 +3194,7 @@ static struct clk_regmap g12a_vclk2_input = {
>>>> .ops = &clk_regmap_gate_ops,
>>>> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2_sel.hw },
>>>> .num_parents = 1,
>>>> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>>>> + .flags = CLK_SET_RATE_PARENT,
>>>> },
>>>> };
>>>> @@ -3215,19 +3216,32 @@ static struct clk_regmap g12a_vclk_div = {
>>>> };
>>>> static struct clk_regmap g12a_vclk2_div = {
>>>> - .data = &(struct clk_regmap_div_data){
>>>> - .offset = HHI_VIID_CLK_DIV,
>>>> - .shift = 0,
>>>> - .width = 8,
>>>> + .data = &(struct clk_regmap_vclk_div_data){
>>>> + .div = {
>>>> + .reg_off = HHI_VIID_CLK_DIV,
>>>> + .shift = 0,
>>>> + .width = 8,
>>>> + },
>>>> + .enable = {
>>>> + .reg_off = HHI_VIID_CLK_DIV,
>>>> + .shift = 16,
>>>> + .width = 1,
>>>> + },
>>>> + .reset = {
>>>> + .reg_off = HHI_VIID_CLK_DIV,
>>>> + .shift = 17,
>>>> + .width = 1,
>>>> + },
>>>> + .flags = CLK_DIVIDER_ROUND_CLOSEST,
>>>> },
>>>> .hw.init = &(struct clk_init_data){
>>>> .name = "vclk2_div",
>>>> - .ops = &clk_regmap_divider_ops,
>>>> + .ops = &clk_regmap_vclk_div_ops,
>>>> .parent_hws = (const struct clk_hw *[]) {
>>>> &g12a_vclk2_input.hw
>>>> },
>>>> .num_parents = 1,
>>>> - .flags = CLK_GET_RATE_NOCACHE,
>>>> + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
>>>> },
>>>> };
>>>> @@ -3246,16 +3260,24 @@ static struct clk_regmap g12a_vclk = {
>>>> };
>>>> static struct clk_regmap g12a_vclk2 = {
>>>> - .data = &(struct clk_regmap_gate_data){
>>>> - .offset = HHI_VIID_CLK_CNTL,
>>>> - .bit_idx = 19,
>>>> + .data = &(struct clk_regmap_vclk_data){
>>>> + .enable = {
>>>> + .reg_off = HHI_VIID_CLK_CNTL,
>>>> + .shift = 19,
>>>> + .width = 1,
>>>> + },
>>>> + .reset = {
>>>> + .reg_off = HHI_VIID_CLK_CNTL,
>>>> + .shift = 15,
>>>> + .width = 1,
>>>> + },
>>>> },
>>>> .hw.init = &(struct clk_init_data) {
>>>> .name = "vclk2",
>>>> - .ops = &clk_regmap_gate_ops,
>>>> + .ops = &clk_regmap_vclk_ops,
>>>> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2_div.hw },
>>>> .num_parents = 1,
>>>> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>>>> + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
>>>> },
>>>> };
>>>> @@ -3339,7 +3361,7 @@ static struct clk_regmap g12a_vclk2_div1 = {
>>>> .ops = &clk_regmap_gate_ops,
>>>> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
>>>> .num_parents = 1,
>>>> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>>>> + .flags = CLK_SET_RATE_PARENT,
>>>> },
>>>> };
>>>> @@ -3353,7 +3375,7 @@ static struct clk_regmap g12a_vclk2_div2_en = {
>>>> .ops = &clk_regmap_gate_ops,
>>>> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
>>>> .num_parents = 1,
>>>> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>>>> + .flags = CLK_SET_RATE_PARENT,
>>>> },
>>>> };
>>>> @@ -3367,7 +3389,7 @@ static struct clk_regmap g12a_vclk2_div4_en = {
>>>> .ops = &clk_regmap_gate_ops,
>>>> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
>>>> .num_parents = 1,
>>>> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>>>> + .flags = CLK_SET_RATE_PARENT,
>>>> },
>>>> };
>>>> @@ -3381,7 +3403,7 @@ static struct clk_regmap g12a_vclk2_div6_en = {
>>>> .ops = &clk_regmap_gate_ops,
>>>> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
>>>> .num_parents = 1,
>>>> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>>>> + .flags = CLK_SET_RATE_PARENT,
>>>> },
>>>> };
>>>> @@ -3395,7 +3417,7 @@ static struct clk_regmap g12a_vclk2_div12_en = {
>>>> .ops = &clk_regmap_gate_ops,
>>>> .parent_hws = (const struct clk_hw *[]) { &g12a_vclk2.hw },
>>>> .num_parents = 1,
>>>> - .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
>>>> + .flags = CLK_SET_RATE_PARENT,
>>>> },
>>>> };
>>>> @@ -3461,6 +3483,7 @@ static struct clk_fixed_factor g12a_vclk2_div2 =
>>>> {
>>>> &g12a_vclk2_div2_en.hw
>>>> },
>>>> .num_parents = 1,
>>>> + .flags = CLK_SET_RATE_PARENT,
>>>> },
>>>> };
>>>> @@ -3474,6 +3497,7 @@ static struct clk_fixed_factor g12a_vclk2_div4 =
>>>> {
>>>> &g12a_vclk2_div4_en.hw
>>>> },
>>>> .num_parents = 1,
>>>> + .flags = CLK_SET_RATE_PARENT,
>>>> },
>>>> };
>>>> @@ -3487,6 +3511,7 @@ static struct clk_fixed_factor g12a_vclk2_div6 =
>>>> {
>>>> &g12a_vclk2_div6_en.hw
>>>> },
>>>> .num_parents = 1,
>>>> + .flags = CLK_SET_RATE_PARENT,
>>>> },
>>>> };
>>>> @@ -3500,6 +3525,7 @@ static struct clk_fixed_factor g12a_vclk2_div12
>>>> = {
>>>> &g12a_vclk2_div12_en.hw
>>>> },
>>>> .num_parents = 1,
>>>> + .flags = CLK_SET_RATE_PARENT,
>>>> },
>>>> };
>>>> @@ -3561,7 +3587,7 @@ static struct clk_regmap g12a_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,
>>>> + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
>>>> },
>>>> };
>>>> @@ -3717,7 +3743,7 @@ static struct clk_regmap g12a_mipi_dsi_pxclk_sel
>>>> = {
>>>> .ops = &clk_regmap_mux_ops,
>>>> .parent_hws = g12a_mipi_dsi_pxclk_parent_hws,
>>>> .num_parents = ARRAY_SIZE(g12a_mipi_dsi_pxclk_parent_hws),
>>>> - .flags = CLK_SET_RATE_NO_REPARENT,
>>>> + .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
>>>> },
>>>> };
>>>> @@ -3729,7 +3755,7 @@ static struct clk_regmap g12a_mipi_dsi_pxclk_div
>>>> = {
>>>> },
>>>> .hw.init = &(struct clk_init_data){
>>>> .name = "mipi_dsi_pxclk_div",
>>>> - .ops = &clk_regmap_divider_ops,
>>>> + .ops = &clk_regmap_divider_ro_ops,
>>>> .parent_hws = (const struct clk_hw *[]) {
>>>> &g12a_mipi_dsi_pxclk_sel.hw
>>>> },
>>>
>
>>
>>>
>>> I suspect mipi_dsi_pxclk_div was added to achieve fractional vclk/bitclk ratios,
>>> since it doesn't exist on AXG. Not sure we would ever need it... and none
>>> of the other upstream DSI drivers supports such setups.
>>>
>>> The main reasons I set only mipi_dsi_pxclk in DT is because :
>>> 1) the DSI controller requires a bitclk to respond, pclk is not enough
>>> 2) GP0 is disabled with an invalid config at cold boot, thus we cannot
>>> rely on a default/safe rate on an initial prepare_enable().
>>> This permits setting initial valid state for the DSI controller, while
>>> the actual bitclk and vclk are calculated dynamically with panel/bridge
>>> runtime parameters.
>> Nothing against setting rate in DT when it is static. Setting it then
>> overriding it is not easy to follow.
>
> Yup, would be simpler to only have parenting set in DT, since it must
> stay static, I'm fine trying to move rate setup to code.
>
>> To work around GP0 not being set, assuming you want to keep rate
>> propagation as it is, you could call clk_set_rate() on cts_encl (possibly w/o
>> enabling it) to force a setup on gp0 then clk_prepare_enable() on
>> pxclk. You'd get a your safe rate on GP0 and the clock you need on pxclk.
>> It is a bit hackish. Might be better to claim gp0 in your driver to
>> manage it directly, cutting rate propagation above it to control each
>> branch of the subtree as you need. It seems you need to have control over
>> that anyway and it would be clear GP0 is expected to belong to DSI.
>
> Controlling the PLL from the DSI controller seems violating too much layers,
> DSI controller driver is not feed directly by the PLL so it's a non-sense
> regarding DT properties.
Not sure what you mean by this. You have shown in your the commit
message that the DSI clocks make significant subtree. I don't see a
problem if you need to manage the root of that subtree. I'd be great if
you didn't need to, but it is what it is ...
>
> Setting a safe clock from the DSI controller probe is an idea, but again I
> don't know which value I should use...
You mentionned that the problem comes DSI bridges that needs to change
at runtime. I don't know much about those TBH, but is there
anyway you can come up with a static GP0 rate that would then be able to
divide to serve all the rates bridge would need in your use case ?
GP0 can go a lot higher than ~100MHz and there are dividers unused in the
tree it seems.
I suppose there is a finite number of required rate for each use case ?
If there are not too many and there is a common divider that allows a
common rate GP0 can do, it would solve your problem. It's a lot of if
but It is worth checking.
This is how audio works and DT assigned rate is a good match in this case.
>
> I'll review the clk parenting flags and try to hack something.
>
> Thanks,
> Neil
>
>
On Fri, 24 Nov 2023 09:41:11 +0100, Neil Armstrong wrote:
> 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.
>
> [...]
Applied, thanks!
[03/12] dt-bindings: phy: amlogic,meson-axg-mipi-pcie-analog: drop text about parent syscon and drop example
commit: 130601d488fa06447283767e447909ce9e975e43
[04/12] dt-bindings: phy: amlogic,g12a-mipi-dphy-analog: drop unneeded reg property and example
commit: 5f4a9a66f8a7582e90311fa8251da33a8d2111d7
Best regards,
--
~Vinod
On 24/11/2023 15:41, Jerome Brunet wrote:
>
> On Fri 24 Nov 2023 at 09:41, 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.
>>
>> Signed-off-by: Neil Armstrong <[email protected]>
>> ---
>> drivers/clk/meson/Kconfig | 5 ++
>> drivers/clk/meson/Makefile | 1 +
>> drivers/clk/meson/vclk.c | 141 +++++++++++++++++++++++++++++++++++++++++++++
>> drivers/clk/meson/vclk.h | 51 ++++++++++++++++
>> 4 files changed, 198 insertions(+)
>>
>> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
>> index 29ffd14d267b..59a40a49f8e1 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
>
> This particular line belong in the next patch
>
>> 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 9ee4b954c896..9ba43fe7a07a 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..47f08a52b49f
>> --- /dev/null
>> +++ b/drivers/clk/meson/vclk.c
>> @@ -0,0 +1,141 @@
>> +// 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 inline struct clk_regmap_vclk_data *
>> +clk_get_regmap_vclk_data(struct clk_regmap *clk)
>> +{
>> + return (struct clk_regmap_vclk_data *)clk->data;
>> +}
>> +
>> +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);
>> +
>> + meson_parm_write(clk->map, &vclk->enable, 1);
>> +
>> + /* Do a reset pulse */
>> + meson_parm_write(clk->map, &vclk->reset, 1);
>> + meson_parm_write(clk->map, &vclk->reset, 0);
>> +
>> + 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);
>> +
>> + meson_parm_write(clk->map, &vclk->enable, 0);
>> +}
>> +
>> +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);
>> +
>> + return meson_parm_read(clk->map, &vclk->enable);
>> +}
>> +
>> +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);
>
> s/clk_regmap_vclk/meson_vclk at least for what is exported, ideally most
> all the code.
>
> I get clk_regmap_ comes from code copied from clk_regmap.c.
> The reason the this part is different (and not using parm) if that when
> I converted amlogic to regmap, I hope we could make this generic,
> possibly converging between aml and qcom (which was the only other
> platform using regmap for clock at the time). This is why clk_regmap.c
> is a bit different from the other driver.
>
> For the aml specific drivers, best to look at the mpll or cpu-dyndiv one.
>
>> +
>> +/* The VCLK Divider has supplementary reset & enable bits */
>> +
>> +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;
>> +}
>> +
>> +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);
>> +
>> + return divider_recalc_rate(hw, prate, meson_parm_read(clk->map, &vclk->div),
>> + vclk->table, vclk->flags, vclk->div.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->div.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);
>> + int ret;
>> +
>> + ret = divider_get_val(rate, parent_rate, vclk->table, vclk->div.width,
>> + vclk->flags);
>> + if (ret < 0)
>> + return ret;
>> +
>> + meson_parm_write(clk->map, &vclk->div, ret);
>> +
>> + return 0;
>> +};
>> +
>> +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 */
>> + meson_parm_write(clk->map, &vclk->reset, 0);
>> + meson_parm_write(clk->map, &vclk->enable, 1);
>> +
>> + return 0;
>> +}
>> +
>> +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 */
>> + meson_parm_write(clk->map, &vclk->enable, 0);
>> + meson_parm_write(clk->map, &vclk->reset, 1);
>> +}
>> +
>> +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);
>> +
>> + return meson_parm_read(clk->map, &vclk->enable);
>> +}
>> +
>> +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..4f25d7ad2717
>> --- /dev/null
>> +++ b/drivers/clk/meson/vclk.h
Is vclk.c/h ok ? clk-vclk doesn't look pretty, but I can switch to it to
keep files organized.
Neil
>> @@ -0,0 +1,51 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (c) 2023 Neil Armstrong <[email protected]>
>> + */
>> +
>> +#ifndef __VCLK_H
>> +#define __VCLK_H
>
> This is too generic.
> Please add the MESON prefix like the other clock driver please.
>
>> +
>> +#include "clk-regmap.h"
>> +#include "parm.h"
>> +
>> +/**
>> + * struct clk_regmap_vclk_data - vclk regmap backed specific data
>> + *
>> + * @enable: vclk enable field
>> + * @reset: vclk reset field
>> + * @flags: hardware-specific flags
>> + *
>> + * Flags:
>> + * Same as clk_gate except CLK_GATE_HIWORD_MASK which is ignored
>> + */
>> +struct clk_regmap_vclk_data {
>> + struct parm enable;
>> + struct parm reset;
>> + u8 flags;
>> +};
>> +
>> +extern const struct clk_ops clk_regmap_vclk_ops;
>> +
>> +/**
>> + * struct clk_regmap_vclk_div_data - vclk_div regmap back specific data
>> + *
>> + * @div: divider field
>> + * @enable: vclk divider enable field
>> + * @reset: vclk divider reset field
>> + * @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 {
>> + struct parm div;
>> + struct parm enable;
>> + struct parm reset;
>> + const struct clk_div_table *table;
>> + u8 flags;
>> +};
>> +
>> +extern const struct clk_ops clk_regmap_vclk_div_ops;
>> +
>> +#endif /* __VCLK_H */
>
On Mon 27 Nov 2023 at 17:14, Neil Armstrong <[email protected]> wrote:
> On 24/11/2023 15:41, Jerome Brunet wrote:
>> On Fri 24 Nov 2023 at 09:41, 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.
>>>
>>> Signed-off-by: Neil Armstrong <[email protected]>
>>> ---
>>> drivers/clk/meson/Kconfig | 5 ++
>>> drivers/clk/meson/Makefile | 1 +
>>> drivers/clk/meson/vclk.c | 141 +++++++++++++++++++++++++++++++++++++++++++++
>>> drivers/clk/meson/vclk.h | 51 ++++++++++++++++
>>> 4 files changed, 198 insertions(+)
>>>
>>> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
>>> index 29ffd14d267b..59a40a49f8e1 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
>> This particular line belong in the next patch
>>
>>> 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 9ee4b954c896..9ba43fe7a07a 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..47f08a52b49f
>>> --- /dev/null
>>> +++ b/drivers/clk/meson/vclk.c
>>> @@ -0,0 +1,141 @@
>>> +// 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 inline struct clk_regmap_vclk_data *
>>> +clk_get_regmap_vclk_data(struct clk_regmap *clk)
>>> +{
>>> + return (struct clk_regmap_vclk_data *)clk->data;
>>> +}
>>> +
>>> +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);
>>> +
>>> + meson_parm_write(clk->map, &vclk->enable, 1);
>>> +
>>> + /* Do a reset pulse */
>>> + meson_parm_write(clk->map, &vclk->reset, 1);
>>> + meson_parm_write(clk->map, &vclk->reset, 0);
>>> +
>>> + 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);
>>> +
>>> + meson_parm_write(clk->map, &vclk->enable, 0);
>>> +}
>>> +
>>> +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);
>>> +
>>> + return meson_parm_read(clk->map, &vclk->enable);
>>> +}
>>> +
>>> +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);
>> s/clk_regmap_vclk/meson_vclk at least for what is exported, ideally most
>> all the code.
>> I get clk_regmap_ comes from code copied from clk_regmap.c.
>> The reason the this part is different (and not using parm) if that when
>> I converted amlogic to regmap, I hope we could make this generic,
>> possibly converging between aml and qcom (which was the only other
>> platform using regmap for clock at the time). This is why clk_regmap.c
>> is a bit different from the other driver.
>> For the aml specific drivers, best to look at the mpll or cpu-dyndiv one.
>>
>>> +
>>> +/* The VCLK Divider has supplementary reset & enable bits */
>>> +
>>> +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;
>>> +}
>>> +
>>> +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);
>>> +
>>> + return divider_recalc_rate(hw, prate, meson_parm_read(clk->map, &vclk->div),
>>> + vclk->table, vclk->flags, vclk->div.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->div.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);
>>> + int ret;
>>> +
>>> + ret = divider_get_val(rate, parent_rate, vclk->table, vclk->div.width,
>>> + vclk->flags);
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + meson_parm_write(clk->map, &vclk->div, ret);
>>> +
>>> + return 0;
>>> +};
>>> +
>>> +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 */
>>> + meson_parm_write(clk->map, &vclk->reset, 0);
>>> + meson_parm_write(clk->map, &vclk->enable, 1);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +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 */
>>> + meson_parm_write(clk->map, &vclk->enable, 0);
>>> + meson_parm_write(clk->map, &vclk->reset, 1);
>>> +}
>>> +
>>> +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);
>>> +
>>> + return meson_parm_read(clk->map, &vclk->enable);
>>> +}
>>> +
>>> +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..4f25d7ad2717
>>> --- /dev/null
>>> +++ b/drivers/clk/meson/vclk.h
>
> Is vclk.c/h ok ? clk-vclk doesn't look pretty, but I can switch to it to
> keep files organized.
I don't have a strong opinion about it.
I would have suggested vclk-div.c/h - like sclk ... but you do have gate
ops in there, so ... :/
This made me realize that one does not really go without the other.
It is more a coherent block, isn't it ?
Would it make more sense to have these 2 merged in a single clk_ops ?
It's bit late to point this out, sorry about that.
I let you decide whether to merge the ops or not and which name to pick.
If you keep them separated, meson_vclk_gate_ops instead of just
meson_vclk_ops, to make things clear.
>
> Neil
>
>>> @@ -0,0 +1,51 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/*
>>> + * Copyright (c) 2023 Neil Armstrong <[email protected]>
>>> + */
>>> +
>>> +#ifndef __VCLK_H
>>> +#define __VCLK_H
>> This is too generic.
>> Please add the MESON prefix like the other clock driver please.
>>
>>> +
>>> +#include "clk-regmap.h"
>>> +#include "parm.h"
>>> +
>>> +/**
>>> + * struct clk_regmap_vclk_data - vclk regmap backed specific data
>>> + *
>>> + * @enable: vclk enable field
>>> + * @reset: vclk reset field
>>> + * @flags: hardware-specific flags
>>> + *
>>> + * Flags:
>>> + * Same as clk_gate except CLK_GATE_HIWORD_MASK which is ignored
>>> + */
>>> +struct clk_regmap_vclk_data {
>>> + struct parm enable;
>>> + struct parm reset;
>>> + u8 flags;
>>> +};
>>> +
>>> +extern const struct clk_ops clk_regmap_vclk_ops;
>>> +
>>> +/**
>>> + * struct clk_regmap_vclk_div_data - vclk_div regmap back specific data
>>> + *
>>> + * @div: divider field
>>> + * @enable: vclk divider enable field
>>> + * @reset: vclk divider reset field
>>> + * @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 {
>>> + struct parm div;
>>> + struct parm enable;
>>> + struct parm reset;
>>> + const struct clk_div_table *table;
>>> + u8 flags;
>>> +};
>>> +
>>> +extern const struct clk_ops clk_regmap_vclk_div_ops;
>>> +
>>> +#endif /* __VCLK_H */
>>
--
Jerome
Hi,
On 27/11/2023 09:38, Jerome Brunet wrote:
>
>>>
>>>>
>>>> I suspect mipi_dsi_pxclk_div was added to achieve fractional vclk/bitclk ratios,
>>>> since it doesn't exist on AXG. Not sure we would ever need it... and none
>>>> of the other upstream DSI drivers supports such setups.
>>>>
>>>> The main reasons I set only mipi_dsi_pxclk in DT is because :
>>>> 1) the DSI controller requires a bitclk to respond, pclk is not enough
>>>> 2) GP0 is disabled with an invalid config at cold boot, thus we cannot
>>>> rely on a default/safe rate on an initial prepare_enable().
>>>> This permits setting initial valid state for the DSI controller, while
>>>> the actual bitclk and vclk are calculated dynamically with panel/bridge
>>>> runtime parameters.
>>> Nothing against setting rate in DT when it is static. Setting it then
>>> overriding it is not easy to follow.
>>
>> Yup, would be simpler to only have parenting set in DT, since it must
>> stay static, I'm fine trying to move rate setup to code.
>>
>>> To work around GP0 not being set, assuming you want to keep rate
>>> propagation as it is, you could call clk_set_rate() on cts_encl (possibly w/o
>>> enabling it) to force a setup on gp0 then clk_prepare_enable() on
>>> pxclk. You'd get a your safe rate on GP0 and the clock you need on pxclk.
>>> It is a bit hackish. Might be better to claim gp0 in your driver to
>>> manage it directly, cutting rate propagation above it to control each
>>> branch of the subtree as you need. It seems you need to have control over
>>> that anyway and it would be clear GP0 is expected to belong to DSI.
>>
>> Controlling the PLL from the DSI controller seems violating too much layers,
>> DSI controller driver is not feed directly by the PLL so it's a non-sense
>> regarding DT properties.
>
> Not sure what you mean by this. You have shown in your the commit
> message that the DSI clocks make significant subtree. I don't see a
> problem if you need to manage the root of that subtree. I'd be great if
> you didn't need to, but it is what it is ...
I really think the choice of PLL should not be done by the DSI controller,
but by the Video pipeline driver or statically until we can do this.
My point is that we should only define the clocks that are related to each
hardware, for example the whole VCLK/VCLK2 clocks should be defined for the
VPU HW, then only the few endpoint clocks should be defined for the HDMI
or DSI controllers, PHY clock and ENCI/ENCP for HDMI, DSI and ENCL for DSI.
The big plan is to entirely switch to CCF for VPU, but first I want to have
DSI working, and since DSI needs GP0, we need CCF for that so the intermediate
plan is to have partial CCF handling only for DSI with fixed clock tree in DT,
then in the future the Meson DRM driver would set up the appropriate clock
tree for HDMI, DSI, Composite and perhaps DP for T7 SoCs then the controller
bridge will call the clk_set_rate() in the same design I did for DSI.
Here's the tracked item: https://gitlab.com/amlogic-foss/mainline-linux-issues-tracker/-/issues/9
CCF clock control is a mandatory item to solved dual-head display: https://gitlab.com/amlogic-foss/mainline-linux-issues-tracker/-/issues/6
>
>>
>> Setting a safe clock from the DSI controller probe is an idea, but again I
>> don't know which value I should use...
>
> You mentionned that the problem comes DSI bridges that needs to change
> at runtime. I don't know much about those TBH, but is there
> anyway you can come up with a static GP0 rate that would then be able to
> divide to serve all the rates bridge would need in your use case ?
No, there's no such things in the DSI world, MIPI only specifies the electrical
and transport layer, everything else is custom per vendor.
>
> GP0 can go a lot higher than ~100MHz and there are dividers unused in the
> tree it seems.
>
> I suppose there is a finite number of required rate for each use case ?
> If there are not too many and there is a common divider that allows a
> common rate GP0 can do, it would solve your problem. It's a lot of if
> but It is worth checking.
>
> This is how audio works and DT assigned rate is a good match in this case.
Yeah I know, but I would love it but no...
>
>>
>> I'll review the clk parenting flags and try to hack something.
>>
>> Thanks,
>> Neil
>>
>>
Thanks,
Neil
On 27/11/2023 17:24, Jerome Brunet wrote:
>
> On Mon 27 Nov 2023 at 17:14, Neil Armstrong <[email protected]> wrote:
>
>> On 24/11/2023 15:41, Jerome Brunet wrote:
>>> On Fri 24 Nov 2023 at 09:41, 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.
>>>>
>>>> Signed-off-by: Neil Armstrong <[email protected]>
>>>> ---
>>>> drivers/clk/meson/Kconfig | 5 ++
>>>> drivers/clk/meson/Makefile | 1 +
>>>> drivers/clk/meson/vclk.c | 141 +++++++++++++++++++++++++++++++++++++++++++++
>>>> drivers/clk/meson/vclk.h | 51 ++++++++++++++++
>>>> 4 files changed, 198 insertions(+)
>>>>
>>>> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
>>>> index 29ffd14d267b..59a40a49f8e1 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
>>> This particular line belong in the next patch
>>>
>>>> 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 9ee4b954c896..9ba43fe7a07a 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..47f08a52b49f
>>>> --- /dev/null
>>>> +++ b/drivers/clk/meson/vclk.c
>>>> @@ -0,0 +1,141 @@
>>>> +// 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 inline struct clk_regmap_vclk_data *
>>>> +clk_get_regmap_vclk_data(struct clk_regmap *clk)
>>>> +{
>>>> + return (struct clk_regmap_vclk_data *)clk->data;
>>>> +}
>>>> +
>>>> +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);
>>>> +
>>>> + meson_parm_write(clk->map, &vclk->enable, 1);
>>>> +
>>>> + /* Do a reset pulse */
>>>> + meson_parm_write(clk->map, &vclk->reset, 1);
>>>> + meson_parm_write(clk->map, &vclk->reset, 0);
>>>> +
>>>> + 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);
>>>> +
>>>> + meson_parm_write(clk->map, &vclk->enable, 0);
>>>> +}
>>>> +
>>>> +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);
>>>> +
>>>> + return meson_parm_read(clk->map, &vclk->enable);
>>>> +}
>>>> +
>>>> +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);
>>> s/clk_regmap_vclk/meson_vclk at least for what is exported, ideally most
>>> all the code.
>>> I get clk_regmap_ comes from code copied from clk_regmap.c.
>>> The reason the this part is different (and not using parm) if that when
>>> I converted amlogic to regmap, I hope we could make this generic,
>>> possibly converging between aml and qcom (which was the only other
>>> platform using regmap for clock at the time). This is why clk_regmap.c
>>> is a bit different from the other driver.
>>> For the aml specific drivers, best to look at the mpll or cpu-dyndiv one.
>>>
>>>> +
>>>> +/* The VCLK Divider has supplementary reset & enable bits */
>>>> +
>>>> +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;
>>>> +}
>>>> +
>>>> +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);
>>>> +
>>>> + return divider_recalc_rate(hw, prate, meson_parm_read(clk->map, &vclk->div),
>>>> + vclk->table, vclk->flags, vclk->div.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->div.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);
>>>> + int ret;
>>>> +
>>>> + ret = divider_get_val(rate, parent_rate, vclk->table, vclk->div.width,
>>>> + vclk->flags);
>>>> + if (ret < 0)
>>>> + return ret;
>>>> +
>>>> + meson_parm_write(clk->map, &vclk->div, ret);
>>>> +
>>>> + return 0;
>>>> +};
>>>> +
>>>> +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 */
>>>> + meson_parm_write(clk->map, &vclk->reset, 0);
>>>> + meson_parm_write(clk->map, &vclk->enable, 1);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +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 */
>>>> + meson_parm_write(clk->map, &vclk->enable, 0);
>>>> + meson_parm_write(clk->map, &vclk->reset, 1);
>>>> +}
>>>> +
>>>> +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);
>>>> +
>>>> + return meson_parm_read(clk->map, &vclk->enable);
>>>> +}
>>>> +
>>>> +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..4f25d7ad2717
>>>> --- /dev/null
>>>> +++ b/drivers/clk/meson/vclk.h
>>
>> Is vclk.c/h ok ? clk-vclk doesn't look pretty, but I can switch to it to
>> keep files organized.
>
> I don't have a strong opinion about it.
> I would have suggested vclk-div.c/h - like sclk ... but you do have gate
> ops in there, so ... :/
>
> This made me realize that one does not really go without the other.
> It is more a coherent block, isn't it ?
> Would it make more sense to have these 2 merged in a single clk_ops ?
No those a 2 separate blocks, one is for VCLK clock input and the other is for the DIV
block, and the reset isn't used in the same way so I can't merge them.
>
> It's bit late to point this out, sorry about that.
>
> I let you decide whether to merge the ops or not and which name to pick.
>
> If you keep them separated, meson_vclk_gate_ops instead of just
> meson_vclk_ops, to make things clear.
Ack
>
>>
>> Neil
>>
>>>> @@ -0,0 +1,51 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>>> +/*
>>>> + * Copyright (c) 2023 Neil Armstrong <[email protected]>
>>>> + */
>>>> +
>>>> +#ifndef __VCLK_H
>>>> +#define __VCLK_H
>>> This is too generic.
>>> Please add the MESON prefix like the other clock driver please.
>>>
>>>> +
>>>> +#include "clk-regmap.h"
>>>> +#include "parm.h"
>>>> +
>>>> +/**
>>>> + * struct clk_regmap_vclk_data - vclk regmap backed specific data
>>>> + *
>>>> + * @enable: vclk enable field
>>>> + * @reset: vclk reset field
>>>> + * @flags: hardware-specific flags
>>>> + *
>>>> + * Flags:
>>>> + * Same as clk_gate except CLK_GATE_HIWORD_MASK which is ignored
>>>> + */
>>>> +struct clk_regmap_vclk_data {
>>>> + struct parm enable;
>>>> + struct parm reset;
>>>> + u8 flags;
>>>> +};
>>>> +
>>>> +extern const struct clk_ops clk_regmap_vclk_ops;
>>>> +
>>>> +/**
>>>> + * struct clk_regmap_vclk_div_data - vclk_div regmap back specific data
>>>> + *
>>>> + * @div: divider field
>>>> + * @enable: vclk divider enable field
>>>> + * @reset: vclk divider reset field
>>>> + * @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 {
>>>> + struct parm div;
>>>> + struct parm enable;
>>>> + struct parm reset;
>>>> + const struct clk_div_table *table;
>>>> + u8 flags;
>>>> +};
>>>> +
>>>> +extern const struct clk_ops clk_regmap_vclk_div_ops;
>>>> +
>>>> +#endif /* __VCLK_H */
>>>
>
>